/* LOFE_SITE — v4（2026-08-25 全面美学重构）
   纯 CSS，无框架、无预处理器、无外部字体（保证离线 zip 打开也正常）。
   移动端优先：基础样式即手机样式，min-width 媒体查询往上加。

   v4 相对 v3 的核心改动，四条，都是从"这版看起来不对"倒推的：

   1. 色系重定。v3 直接照搬苹果的 #fbfbfd/#f5f5f7 + 蓝橙一对强调色，
      问题是这站的两个视觉锚点——LOFE logo（纯黑白、斜切面伪立体）和首屏
      那张模块合成器背影（黑白胶片调）——都是无彩色的，页面却在用饱和蓝橙
      标 M-A/M-B，两者互相打架。v4 把整套色降到无彩灰阶，只保留一对
      去饱和的墨蓝/暖褐用于区分两种映射模式（数据区必须可区分，但不该抢戏）。
   2. 首屏重做。v3 引用的 hero.jpg 根本不存在，首屏是一块空渐变。现在用真图，
      并把 logo 落点定在人物头顶那片浅墙上——那是整张图里唯一一块低密度区域，
      放在合成器架上会糊掉。
   3. logo 滚动动画补上。v3 的 HTML/CSS 都在等 main.js 的 initLogoAnim()，
      但那个函数压根没写，导致 .anim-logo 永久 fixed 在 (0,0) 盖住导航。
   4. 横滑面板等高。v3 三块面板内容量差一倍多（依据面板极长、结果中等），
      横滑时右边缘参差不齐。现在统一高度 + 面板内滚动。 */

:root{
  /* 无彩灰阶——跟 logo 和首屏照片同调 */
  --bg:#f7f7f8;
  --bg-alt:#efeff1;
  --card:#ffffff;
  --ink:#111113;
  --ink-soft:#5f5f66;
  --ink-faint:#8a8a92;
  --line:rgba(17,17,19,.10);
  --line-strong:rgba(17,17,19,.18);

  /* 唯一保留的两个有彩色：去饱和后仅用于区分两种映射模式。
     不再用于任何装饰性场合。 */
  --accent-a:#3d5a80;
  --accent-b:#9a6b3f;

  --glass-bg:rgba(255,255,255,.70);
  --glass-border:rgba(255,255,255,.85);

  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif;

  --nav-h:52px;
  --max-w:960px;
  --max-w-wide:1400px;
  --ease:cubic-bezier(.22,.61,.36,1);

  --sp-1:8px;  --sp-2:14px; --sp-3:20px; --sp-4:28px;
  --sp-5:40px; --sp-6:56px; --sp-7:80px;
}

*, *::before, *::after{ box-sizing:border-box; }
html{ scroll-behavior:smooth; }
html,body{ margin:0; padding:0; }
body{
  font-family:var(--sans);
  color:var(--ink);
  background:var(--bg);
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img{ max-width:100%; display:block; }
a{ color:inherit; }
button{ font-family:inherit; }

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

/* ── 滚动进场 ─────────────────────────────────────────── */
.reveal{
  opacity:0; transform:translateY(18px);
  transition:opacity .8s var(--ease), transform .8s var(--ease);
}
.reveal.in-view{ opacity:1; transform:translateY(0); }
@media (prefers-reduced-motion: reduce){
  .reveal{ opacity:1; transform:none; transition:none; }
  html{ scroll-behavior:auto; }
}

/* ── 顶部导航 ─────────────────────────────────────────── */
.nav{
  position:fixed; top:0; left:0; right:0; z-index:100;
  height:var(--nav-h);
  display:flex; align-items:center; justify-content:space-between;
  padding:0 20px;
  background:rgba(247,247,248,.72);
  backdrop-filter:saturate(180%) blur(16px);
  -webkit-backdrop-filter:saturate(180%) blur(16px);
  border-bottom:1px solid transparent;
  transition:border-color .3s var(--ease);
}
.nav.scrolled{ border-bottom-color:var(--line); }

.logo-mark{ display:inline-flex; align-items:center; height:22px; text-decoration:none; }
.logo-text{ font-weight:600; letter-spacing:.10em; font-size:13px; color:var(--ink); }
.logo-img{
  display:none; width:64px; height:100%;
  background-image:url("../images/logo.png");
  background-size:contain; background-repeat:no-repeat; background-position:left center;
}
body.has-logo .logo-text{ display:none; }
body.has-logo .logo-img{ display:block; }

.nav-links{ display:none; gap:30px; list-style:none; margin:0; padding:0; font-size:13px; }
.nav-links a{ text-decoration:none; color:var(--ink-soft); font-weight:500; transition:color .18s; }
.nav-links a:hover{ color:var(--ink); }
.nav-right{ display:flex; align-items:center; gap:12px; }

.lang-btn{
  border:1px solid var(--line-strong); background:transparent;
  border-radius:999px; padding:5px 14px; font-size:12px; font-weight:500;
  cursor:pointer; color:var(--ink-soft);
  transition:border-color .18s, color .18s;
}
.lang-btn:hover{ border-color:var(--ink-soft); color:var(--ink); }

.nav-toggle{
  display:inline-flex; flex-direction:column; gap:4px;
  background:none; border:none; cursor:pointer; padding:6px;
}
.nav-toggle span{ width:19px; height:1.5px; background:var(--ink); display:block; }

.nav-drawer{
  display:none; position:fixed; top:var(--nav-h); left:0; right:0;
  background:rgba(247,247,248,.96);
  backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
  border-bottom:1px solid var(--line); z-index:99; padding:4px 24px 18px;
}
.nav-drawer.open{ display:block; }
.nav-drawer a{
  display:block; padding:12px 0; text-decoration:none; color:var(--ink);
  font-size:15px; font-weight:500; border-bottom:1px solid var(--line);
}
.nav-drawer a:last-child{ border-bottom:none; }

@media (min-width:760px){
  .nav-links{ display:flex; }
  .nav-toggle{ display:none; }
  .nav-drawer{ display:none !important; }
}

/* ── 首屏 ─────────────────────────────────────────────────
   v3 引用的 hero.jpg 不存在，首屏是块空渐变。现在用真图。
   照片本身构图对称、密度自上而下递减：顶部合成器架最密，
   中段人物背影为主体，底部桌面相对空。
   底部那道渐变把照片溶进页面底色，不留硬边——这是首屏和 Bio
   之间唯一的过渡，不额外加分隔线。 */
.hero{
  position:relative; height:76vh; min-height:420px; max-height:760px;
  overflow:hidden; background:var(--bg-alt);
}
.hero-bg{
  position:absolute; left:0; right:0; top:-6%; height:112%;
  background-image:url("../images/hero.jpg");
  background-size:cover; background-position:center 42%;
  will-change:transform;
}
/* 三层叠加。八稿反馈把 logo 改成纯白带光边之后，原来那层"顶部往亮里压"
   的渐变正好是反的：照片本身是浅墙 sepia 调，整体偏亮，白 logo 压在上面
   会糊。现在改成整体轻微压暗打底，让白色笔画全程有对比；顶部再补一道
   稍深的，是为了让 logo 起步那一段（首屏 44% 处横跨浅墙与合成器架）不
   挑背景；底部保持溶进页面底色。
   导航栏自带浅色半透明底加模糊，照片压暗不影响导航文字的可读性。 */
.hero-veil{
  position:absolute; inset:0;
  background:
    linear-gradient(180deg, rgba(17,17,19,.30) 0%, rgba(17,17,19,.10) 34%, rgba(17,17,19,0) 58%),
    linear-gradient(180deg, rgba(17,17,19,.16) 0%, rgba(17,17,19,.16) 55%, rgba(17,17,19,0) 78%),
    linear-gradient(180deg, rgba(247,247,248,0) 62%, var(--bg) 100%);
}

/* logo 的唯一可见实例：首屏落在人物头顶那片浅墙上（整张图里唯一的
   低密度区，放在合成器架上会被机箱纹理糊掉），随滚动连续插值到导航
   栏左上角。定位与缩放全部由 main.js 的 initLogoAnim() 逐帧写 inline
   style，这里只定义静态外观。 */
.nav .logo-mark{ visibility:hidden; }
/* 七稿反馈：首屏起步状态放大一倍（260→520），起步再往下挪一点；
   宽度用 min() 卡住避免窄屏溢出，配 aspect-ratio 让高度跟着走，
   不用再单独写一条窄屏媒体查询。颜色（首屏纯白→导航纯黑）交给
   main.js 用 filter:invert() 连续插值，见 initLogoAnim()。 */
.anim-logo{
  position:fixed; left:0; top:0; z-index:150;
  display:inline-flex; align-items:center; justify-content:center;
  width:min(660px, 88vw); aspect-ratio:520/188; height:auto;
  transform-origin:center center;
  text-decoration:none; pointer-events:auto;
  will-change:transform, filter;
}
.anim-logo .logo-text{
  color:#fff; font-weight:700; font-size:clamp(26px, 9vw, 56px);
  letter-spacing:.10em; white-space:nowrap;
}
.anim-logo .logo-img{ width:100%; height:100%; background-position:center; }


/* 照片压暗后这个提示还用深灰就看不见了，改成白色。
   它落在首屏底部那道"溶进页面底色"的渐变区里，所以配一层极轻的
   文字投影，防止它正好压在已经变白的那一段上时糊掉。 */
.hero-scroll{
  position:absolute; z-index:2; left:50%; bottom:22px; transform:translateX(-50%);
  color:rgba(255,255,255,.92); font-size:10px; letter-spacing:.18em; text-transform:uppercase;
  text-decoration:none; display:flex; flex-direction:column; align-items:center; gap:8px;
  opacity:.75; transition:opacity .3s;
  text-shadow:0 1px 10px rgba(17,17,19,.5);
}
.hero-scroll:hover{ opacity:1; }
.hero-scroll span{
  display:block; width:1px; height:22px; background:currentColor;
  box-shadow:0 0 8px rgba(17,17,19,.4);
}

/* ── 通用 section 与排版 ─────────────────────────────────
   v3 每个 section 之间都有一条 1px 全宽横线，八个 section 就是八条线，
   页面被切成一格一格。改成靠留白分段，只在必要处保留细线。 */
section{ padding:var(--sp-7) 0; }
section + section{ border-top:1px solid var(--line); }

.section-label{
  font-family:var(--mono); font-size:10.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--ink-faint); margin:0 0 var(--sp-2);
}
h2{
  font-size:29px; font-weight:600; letter-spacing:-.021em;
  margin:0 0 var(--sp-2); line-height:1.18;
}
p{ margin:0 0 var(--sp-2); color:var(--ink); }
.lead{ font-size:16.5px; color:var(--ink-soft); max-width:62ch; }

/* ── Bio ───────────────────────────────────────────────── */
.bio{ padding-top:var(--sp-6); }
.bio h1{ font-size:35px; font-weight:700; letter-spacing:-.028em; margin:0 0 8px; }
.bio h1 .cn{ font-weight:400; color:var(--ink-soft); letter-spacing:0; }
.bio-role{ font-size:17.5px; color:var(--ink-soft); margin:0 0 var(--sp-4); max-width:56ch; }
.bio-lines{ margin-bottom:var(--sp-3); }
.bio-lines p{ margin:0 0 7px; font-size:14.5px; color:var(--ink-soft); }
.bio-contact{
  margin-top:var(--sp-3); display:flex; flex-wrap:wrap;
  gap:10px 20px; font-size:14px; align-items:center;
}
.bio-contact a{
  text-decoration:none; color:var(--ink);
  border-bottom:1px solid var(--line-strong); padding-bottom:2px;
  transition:border-color .18s;
}
.bio-contact a:hover{ border-bottom-color:var(--ink); }
.bio-contact span{ color:var(--ink-faint); }

/* ── Research ─────────────────────────────────────────── */
/* research-sub 已合并进下方 .section-sub（九稿），此行保留占位说明该合并 */
.thesis-links{ margin:var(--sp-3) 0 0; display:flex; flex-wrap:wrap; gap:14px; align-items:center; }
.pdf-link{
  display:inline-flex; align-items:center; gap:8px;
  background:var(--ink); color:#fff;
  padding:11px 20px; border-radius:980px; text-decoration:none;
  font-size:13.5px; font-weight:500; transition:opacity .18s;
}
.pdf-link:hover{ opacity:.82; }
.swipe-hint{ font-size:12.5px; color:var(--ink-faint); margin-top:var(--sp-3); }

/* ── Research 横滑：等高三联 ────────────────────────────
   v3 的问题：三块面板内容量差一倍多（依据面板最长、结果次之），
   flex 默认拉伸后轨道右边缘参差不齐，横滑时视觉在跳。
   v4 统一面板高度、内容超出时面板内部纵向滚动——横滑轨道本身
   保持整齐，每块面板成为一个独立可读的"卡片"。 */
.swipe-shell{
  position:relative; margin:var(--sp-5) auto 0;
  max-width:var(--max-w-wide); padding:0 18px;
}
.swipe-track{
  display:flex; align-items:stretch;
  overflow-x:auto; scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch; scrollbar-width:none;
  gap:18px; padding:12px 5% 22px;
}
.swipe-track::-webkit-scrollbar{ display:none; }
.swipe-panel{
  position:relative;
  flex:0 0 84%; scroll-snap-align:center; min-width:0;
  height:min(74vh, 660px);
  overflow-y:auto; overscroll-behavior:contain;
  transform-origin:center center;
  transition:transform .12s linear, opacity .12s linear, filter .12s linear;
  will-change:transform, opacity, filter;
  scrollbar-width:thin;
}
.swipe-panel::-webkit-scrollbar{ width:5px; }
.swipe-panel::-webkit-scrollbar-thumb{ background:var(--line-strong); border-radius:99px; }

.peek-veil{
  position:absolute; inset:0; border-radius:inherit;
  background:rgba(0,0,0,0); pointer-events:none;
  transition:background-color .2s ease;
}
.swipe-panel:not(.is-center){ cursor:pointer; }
.swipe-panel:not(.is-center):hover .peek-veil{ background:rgba(17,17,19,.12); }

.glass{
  background:var(--glass-bg);
  border:1px solid var(--glass-border);
  border-radius:18px;
  backdrop-filter:blur(20px) saturate(150%);
  -webkit-backdrop-filter:blur(20px) saturate(150%);
  box-shadow:0 1px 2px rgba(17,17,19,.04), 0 14px 36px rgba(17,17,19,.07);
  padding:var(--sp-4) var(--sp-3);
}
/* 七稿反馈排查："依据/影响"两块左右面板即便滑到正中间、字面上该完全清晰
   的时候，看起来还是发虚——这是 backdrop-filter 叠加 transform（哪怕
   transform 只是 scale(1) 这种恒等变换）在部分浏览器上的已知渲染坑：
   只要元素同时挂着 backdrop-filter 和逐帧写的 transform，合成层的
   栅格化就可能把元素自己的内容也糊一层，不只是糊背景。解法是居中时
   直接摘掉 backdrop-filter——毛玻璃质感本来就只该在"偷看中的邻居面板"
   身上出现，居中判定为准焦点之后就该是纯净的实底，两者不冲突。 */
.glass.is-center{
  background:var(--card);
  backdrop-filter:none; -webkit-backdrop-filter:none;
}
/* 中间那块结果面板不用毛玻璃——它是主角，给纯白实底，
   跟两侧半透明的邻居拉开层次 */
.swipe-panel[data-panel="results"]{
  background:var(--card); border:1px solid var(--line);
  border-radius:18px; padding:var(--sp-4) var(--sp-3);
  box-shadow:0 1px 2px rgba(17,17,19,.04), 0 14px 36px rgba(17,17,19,.07);
}

.panel-eyebrow{
  font-family:var(--mono); font-size:10.5px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--ink-faint); margin:0 0 10px;
}
.panel-title{ font-size:19px; font-weight:600; letter-spacing:-.014em; margin:0 0 var(--sp-2); }

@media (min-width:760px){
  .swipe-panel{ flex-basis:72%; }
  .swipe-track{ padding-left:9%; padding-right:9%; }
}
@media (min-width:1180px){
  .swipe-panel{ flex-basis:66%; }
  .swipe-track{ padding-left:12%; padding-right:12%; }
}

/* 卡片内的小轮播（Systems / Biker Buddy）：不跳出 .wrap，
   面板高度自适应内容，不套用上面那个固定高度 */
.swipe-shell.mini{ max-width:100%; margin:var(--sp-3) 0 0; padding:0; }
.swipe-shell.mini .swipe-track{ padding:6px 6% 16px; gap:12px; }
/* 七稿反馈：Systems 那 7 张图长宽比差很多，轨道 align-items:stretch 让
   每块面板都拉伸到最高那张的高度，但图片本身只在顶部对齐，导致矮图
   下面空一截、看起来"有的高有的低"。改成 flex 列 + 居中，让每张图
   （连同它下面的说明文字）作为一个整体在面板里垂直居中，水平中轴线
   自然就对齐了，不用再去改图片本身的比例。 */
.swipe-shell.mini .swipe-panel{
  display:flex; flex-direction:column; justify-content:center;
  flex-basis:88%; height:auto;
  border-radius:12px; overflow:hidden;
  border:1px solid var(--line); background:var(--card);
}
.swipe-shell.mini .swipe-panel img{ width:100%; display:block; transition:transform .35s ease; }
/* 十七稿：桌面端体积太小的真正原因不是这条规则本身，是它现在被用在
   两种截然不同的容器里——Systems 那个在 960px 的整栏宽度里，Biker
   Buddy 这个被塞进两栏网格更窄的那一栏，同一个百分比在窄栏里出来的
   绝对尺寸小了一大截，跟旁边 Sketchfab 那个吃满整栏宽度的嵌入摆在
   一起自然显小。66% 提到 92%，两个用例都会跟着变大——Systems 那边
   变大是顺带的好处，不是副作用。

   顺带做了 hover 放大：居中的那块鼠标悬停时图片轻微放大，两侧偷看
   的面板不加这个效果，因为它们已经有点击跳转的交互，两种悬停反馈
   叠在一起会打架。 */
.swipe-shell.mini .swipe-panel.is-center:hover img{ transform:scale(1.035); }
@media (min-width:640px){ .swipe-shell.mini .swipe-panel{ flex-basis:92%; } }

/* ── 论文数据组件 ──────────────────────────────────────── */
.spec-grid{ display:grid; grid-template-columns:1fr; gap:12px; margin:0 0 var(--sp-3); }
@media (min-width:520px){ .spec-grid{ grid-template-columns:1fr 1fr; } }
.spec-card{
  border:1px solid var(--line); border-radius:13px;
  padding:16px 18px; background:var(--card);
}
.spec-card .k{ font-family:var(--mono); font-size:11.5px; color:var(--ink-faint); margin-bottom:6px; }
.spec-card .name{ font-weight:600; margin-bottom:9px; font-size:14.5px; }
.spec-card code{
  display:block; font-family:var(--mono); font-size:12.5px; line-height:1.7;
  background:var(--bg-alt); padding:10px 12px; border-radius:8px;
  overflow-x:auto; color:var(--ink);
}
.spec-card.a{ border-left:3px solid var(--accent-a); }
.spec-card.b{ border-left:3px solid var(--accent-b); }

.fig{ margin:var(--sp-3) 0; }
.fig img{ border-radius:11px; border:1px solid var(--line); background:#fff; }
.fig figcaption{
  font-size:12px; color:var(--ink-faint);
  margin-top:9px; line-height:1.6;
}
.stack-note{
  font-family:var(--mono); font-size:11.5px; color:var(--ink-faint);
  margin-top:var(--sp-3); line-height:1.85;
  padding-top:var(--sp-2); border-top:1px solid var(--line);
}

/* 三个大数字：v3 用了 30px 等宽粗体加彩色，配合灰阶新色系过重。
   改成更大但更轻的字重，靠字号和留白建立层级，不靠颜色。 */
.stat-hero{
  display:grid; grid-template-columns:1fr 1fr;
  gap:1px; margin:var(--sp-3) 0;
  background:var(--line); border:1px solid var(--line);
  border-radius:13px; overflow:hidden;
}
.stat-hero .stat{ background:var(--card); padding:18px 16px 16px; }
.stat-hero .stat .num{
  font-family:var(--mono); font-weight:600; font-size:27px;
  letter-spacing:-.03em; line-height:1.1;
}
.stat-hero .stat.a .num{ color:var(--accent-a); }
.stat-hero .stat.b .num{ color:var(--accent-b); }
.stat-hero .stat .lbl{ font-size:12px; color:var(--ink-soft); margin-top:6px; }

.stat-row{
  display:flex; flex-wrap:wrap; gap:var(--sp-3) var(--sp-5);
  margin:var(--sp-3) 0; padding:var(--sp-2) 0;
  border-top:1px solid var(--line); border-bottom:1px solid var(--line);
}
.stat{ min-width:120px; }
.stat-row .stat .num{ font-family:var(--mono); font-size:16px; font-weight:600; letter-spacing:-.02em; }
.stat-row .stat .lbl{ font-size:11.5px; color:var(--ink-soft); margin-top:3px; line-height:1.5; }

.impact-block p{ font-size:14px; color:var(--ink-soft); }
.impact-block strong{ color:var(--ink); font-weight:600; }
.impact-note{
  border-left:2px solid var(--line-strong);
  padding:4px 0 4px 16px; color:var(--ink-faint);
  font-size:13px; margin-top:var(--sp-4);
}

/* ── Experience ───────────────────────────────────────── */
.xp-list{ display:flex; flex-direction:column; margin-top:var(--sp-3); }
.xp-item{ padding:20px 0; border-bottom:1px solid var(--line); }
.xp-item:last-child{ border-bottom:none; padding-bottom:0; }
.xp-top{
  display:flex; justify-content:space-between; align-items:baseline;
  gap:12px; flex-wrap:wrap; margin-bottom:8px;
}
.xp-title{ font-weight:600; font-size:15px; }
.xp-org{ font-size:13px; color:var(--ink-soft); }
.xp-date{ font-family:var(--mono); font-size:11px; color:var(--ink-faint); white-space:nowrap; letter-spacing:.02em; }
.xp-bullets{ margin:0; padding-left:17px; }
.xp-bullets li{ margin:5px 0; font-size:13.5px; color:var(--ink-soft); line-height:1.65; }

.proj-grid{ display:grid; grid-template-columns:1fr; gap:16px; margin-top:var(--sp-3); }
@media (min-width:700px){ .proj-grid{ grid-template-columns:1fr 1fr; } }
.proj-grid .spec-card{ border-left:1px solid var(--line); }
.proj-shots{ display:flex; gap:8px; margin-top:14px; }
.proj-shots img{ border-radius:8px; flex:1 1 0; width:50%; border:1px solid var(--line); }
.embed-3d{
  position:relative; width:100%; aspect-ratio:16/10; margin-top:12px;
  border-radius:8px; overflow:hidden; border:1px solid var(--line); background:var(--bg-alt);
}
.embed-3d iframe{ position:absolute; inset:0; width:100%; height:100%; border:0; }

/* ── Audio（十一稿：从 .wrap 里挪出来做通栏，跟 .hero 同一种处理）──
   原来是白底卡片直接摆在页面背景上；现在 track 卡片是半透明玻璃，
   叠在 .audio-stage-bg 那张（待补的）打碟正面照上——这是本节
   "playlist 透明"的实现方式：玻璃卡片本身透，不是靠降低不透明度
   露出页面底色。

   .audio-stage 本身现在是通栏元素（HTML 里已经挪到 .wrap 外面，
   是 section 的直接子元素），跟 .hero 左右都到边，不再有圆角和边框——
   那两样是"卡片"才需要的语言，通栏元素不需要。

   十四稿：整段推理再改一次，这次是结构层面的。前两版的问题根子在于
   "照片只占 section 里的一块子区域"这件事本身——section 自己还套着
   通用规则 `section{padding:80px 0}`，label 之前是一段素色留白，
   照片区域结束后到下一个 section 之间又是一段素色留白。不管照片内部
   怎么调，section 整体看起来还是"素色—照片—素色"三明治，不是照片
   铺满整个 section。

   现在把 #audio 的 padding 清零（覆盖通用规则），bg/veil 直接是
   section 的背景层，label 和播放列表都是浮在照片上的独立元素——
   这跟 .hero 是同一套做法：hero 自己有独立高度，scroll-hint 之类
   的内容浮在上面，不再是"外面包一层留白"。 */
#audio{
  position:relative;
  height:80vh; min-height:480px; max-height:820px;
  padding:0; overflow:hidden;
}
.audio-stage-bg{
  position:absolute; inset:0; z-index:0;
  background-color:var(--ink);
  background-image:url("../images/dj-portrait.jpg");
  background-size:cover; background-position:center;
}
/* 顶部只压极轻的暗，把脸和氛围光留出来真的看得见；越往下越暗，
   撑住底部托盘里玻璃卡片的白字对比度。十四稿把暗部起点提前了
   （42%→26%起步收紧），因为托盘本身加高了，暗区要跟着往上跟。
   深浅这几个数值是我按肉眼判断定的——Logan 原话是"淡化的微调
   你自己确定"。 */
.audio-stage-veil{
  position:absolute; inset:0; z-index:1;
  background:linear-gradient(180deg,
    rgba(17,17,19,.05) 0%,
    rgba(17,17,19,.11) 15%,
    rgba(17,17,19,.60) 26%,
    rgba(17,17,19,.82) 100%);
}
/* 三首歌高度的托盘，贴着舞台底部悬浮，不再决定外层 .audio-stage
   的高度——这两个尺寸现在互相独立了。main.js 的 initAudioLoop()
   把 .audio-scroll-track 的内容复制成三份并绑定滚动重定位，
   让下拉永远有"下一首"，视觉上不可见接缝。

   宽度卡在阅读栏宽度并居中——通栏的是背景照片，播放列表本身不用
   跟着拉到整个视口那么宽，宽屏下歌曲卡片横向拉到一千好几百像素会
   很难看。十四稿：托盘加高，顶到脸部眉毛附近——Logan 原话是"不要
   担心挡住我的脸"，之前刻意留出大片空当只为不碰到脸，这个顾虑是
   多余的，直接砍掉大半空当，让托盘顶部往上顶。具体顶到哪一点，
   在不同屏幕宽度下人脸在照片里的实际位置会变，这个数字是估的，
   看实际效果可能还要再抬高或压低几十像素。 */
.audio-scroll-mask{
  position:absolute; z-index:2;
  left:50%; bottom:44px; transform:translateX(-50%);
  width:calc(100% - 48px); max-width:var(--max-w);
  height:min(70vh, 600px);
  overflow-y:auto; overscroll-behavior:contain; scrollbar-width:none;
  padding:16px 24px;
}
.audio-scroll-mask::-webkit-scrollbar{ display:none; }
.audio-scroll-track{ display:flex; flex-direction:column; gap:12px; }

/* label 现在浮在照片上，不再是浅色页面底上的深灰字——套用默认
   .section-label 颜色会在暗照片上读不清，这里单独覆盖成近白。
   padding-top 补的是原来 section 通用留白（80px）的等效值，只是
   现在只加在 label 这一层，不影响下面的照片。 */
.audio-label-wrap{
  position:relative; z-index:2;
  padding-top:var(--sp-7);
}
.audio-label-wrap .section-label{ color:rgba(255,255,255,.75); }

/* 玻璃卡片，压在 audio-stage-bg 的照片上。color-scheme:dark 是给
   Chromium 内核的原生 <audio controls> 一个提示，让它尽量渲染成深色
   系——但这只是提示，不是强制：不同浏览器的原生播放条这块可控性很有限，
   Safari 基本不受这个影响，卡片在它上面可能还是会看到一条偏浅色的
   原生播放条，这是原生控件本身的限制，CSS 改不动。 */
.track{
  background:rgba(255,255,255,.15); border:1px solid rgba(255,255,255,.24);
  border-radius:13px; padding:14px 16px; position:relative;
  backdrop-filter:blur(16px) saturate(150%);
  -webkit-backdrop-filter:blur(16px) saturate(150%);
}
@supports not (backdrop-filter: blur(1px)){
  .track{ background:rgba(20,20,22,.74); }
}
.track-top{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:10px; flex-wrap:wrap; margin-bottom:4px;
}
.track-title{ font-weight:600; font-size:14.5px; letter-spacing:-.01em; color:#fff; }
.track-dur{ font-family:var(--mono); font-size:11px; color:rgba(255,255,255,.62); }
.track audio{ width:100%; height:32px; color-scheme:dark; }

/* ── Footer ────────────────────────────────────────────── */
footer{ padding:var(--sp-5) 0 var(--sp-6); border-top:1px solid var(--line); }
footer .wrap{
  font-size:12.5px; color:var(--ink-faint);
  display:flex; flex-wrap:wrap; gap:10px 20px; justify-content:space-between;
}
footer a{
  text-decoration:none; color:var(--ink-faint);
  border-bottom:1px solid var(--line); padding-bottom:1px;
  transition:color .18s, border-color .18s;
}
footer a:hover{ color:var(--ink); border-bottom-color:var(--line-strong); }

/* ── 大屏 ──────────────────────────────────────────────── */
@media (min-width:760px){
  h2{ font-size:33px; }
  .bio h1{ font-size:44px; }
  .stat-hero{ grid-template-columns:repeat(3,1fr); }
  .swipe-shell{ padding:0 10px; }
}

/* 轮播里图片下方的说明文字（Systems 七张截图） */
.shot-cap{
  font-size:12.5px; color:var(--ink-soft);
  padding:12px 14px 14px; margin:0; line-height:1.6;
}

.spec-card .desc{ font-size:13px; margin-top:8px; margin-bottom:0; color:var(--ink-soft); }

/* ── 剩余行内样式归位（九稿，语意角色分类，非原子化工具类）── */
/* 大标题下紧跟的副标题行——原来 research-sub 只服务 Research 一处，
   现在 Systems 的年份行也是同一种角色，改成通用名并复用 */
.section-sub{ font-size:13.5px; color:var(--ink-faint); margin-top:-8px; margin-bottom:var(--sp-3); }

.evidence-note{ font-size:13.5px; color:var(--ink-soft); margin-top:var(--sp-2); line-height:1.65; }
.panel-sub{ font-size:13.5px; color:var(--ink-soft); margin-top:-8px; }
.panel-sub.faint{ font-size:12.5px; color:var(--ink-faint); }
.bonferroni-note{ font-size:13px; color:var(--ink-soft); }
.panel-title.spaced{ margin-top:var(--sp-4); }

.proj-meta{ font-size:13px; color:var(--ink-soft); margin:2px 0 10px; }
.proj-desc{ font-size:14px; margin-bottom:8px; }
.proj-desc.last{ margin-bottom:0; }
.embed-caption{ font-size:12px; color:var(--ink-faint); margin:8px 0 0; }
.sub-heading{ margin-top:44px; }
.muted-suffix{ font-weight:400; color:var(--ink-faint); }
