/* Sober BW — 全站样式
   字体：HarmonyOS Sans SC（本地自托管，见 fonts/harmonyos/）
   色彩：深色首屏 + 纸白内容区，米金作唯一强调色 */

:root{
  --ground:#0A0A09;
  --paper:#F5F4F0;
  --ink-l:#F2F0EA;
  --ink-l2:#A5A196;
  --ink-d:#141412;
  --ink-d2:#5B5B55;
  --ink-d3:#74736B;   /* 原 #8E8D84：在 #F5F4F0 上对比只有 2.9:1，
                         10.5–12px 的小字（日期、图注、字段名）实际很难读。
                         压到 4.3:1 仍明显轻于 --ink-d2，层级没丢。 */
  --rule-d:#DCDAD2;
  --rule-l:#26251F;
  --gold:#C8B48A;
  --accent:#9E2B25;
  --sans:"HarmonyOS Sans SC","HarmonyOS Sans",system-ui,"PingFang SC","Microsoft YaHei",sans-serif;
  --m:clamp(20px,4vw,64px);
  --ease:cubic-bezier(.2,.7,.2,1);
}

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{background:var(--paper);color:var(--ink-d);font-family:var(--sans);line-height:1.7;font-weight:400;font-size:16px;-webkit-font-smoothing:antialiased}
img,svg{display:block;max-width:100%}
/* manipulation：允许滚动和双指缩放，但关掉「双击缩放」。
   手机上双击一个链接时，浏览器会先等 300ms 看是不是双击，才决定要不要跳转——
   那一下的迟疑就是「点了没反应」。链接和按钮都关掉它，不影响页面缩放。 */
a{color:inherit;text-decoration:none;touch-action:manipulation}
button{touch-action:manipulation}
.defs{position:absolute;width:0;height:0;overflow:hidden}
.wrap{max-width:1600px;margin:0 auto;padding:0 var(--m)}

/* ---------- 顶栏 ---------- */
.topbar{position:fixed;left:0;right:0;top:0;z-index:100;background:rgba(10,10,9,.62);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border-bottom:1px solid rgba(255,255,255,.07);transition:background .3s}
.topbar .in{max-width:1600px;margin:0 auto;padding:14px var(--m);display:flex;align-items:baseline;justify-content:space-between;gap:24px;color:var(--ink-l)}
.mark{font-size:16px;font-weight:600;letter-spacing:.14em}
.topbar nav{display:flex;gap:clamp(14px,2.4vw,30px);font-size:13.5px;color:var(--ink-l2)}
.topbar nav a{padding-bottom:2px;border-bottom:1px solid transparent;transition:color .2s,border-color .2s}
.topbar nav a:hover,.topbar nav a[aria-current]{color:var(--ink-l);border-color:var(--gold)}

/* ---------- 首屏（首页）---------- */
.media{position:absolute;inset:0}
.media svg{width:100%;height:100%}

/* ---------- 区块头 ---------- */
main{padding-top:clamp(48px,9vh,110px)}
.sec-head{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.85fr);gap:clamp(16px,4vw,64px);align-items:end;margin-bottom:clamp(24px,3.6vh,42px)}
.sec-head h1,.sec-head h2{font-weight:600;font-size:clamp(24px,2.6vw,40px);line-height:1.25;letter-spacing:-.01em}
.sec-head p{font-size:14px;line-height:1.9;color:var(--ink-d2);max-width:34em}
.count{font-size:12px;color:var(--ink-d3);letter-spacing:.08em;margin-bottom:6px}

/* ---------- 作品网格 ---------- */
.work{display:block}
.frame{position:relative;overflow:hidden;background:#1A1915}
.frame svg{width:100%;height:100%;transition:transform .8s var(--ease)}
.work .frame{aspect-ratio:2/1}
.work:hover .frame svg{transform:scale(1.03)}
.work .cap{display:flex;justify-content:space-between;gap:10px;align-items:baseline;margin-top:11px}
.work .cap b{font-weight:500;font-size:clamp(14px,1.15vw,16.5px);line-height:1.4}
.work .cap i{font-style:normal;font-size:11.5px;color:var(--ink-d3);white-space:nowrap}
.work:hover .cap b{color:var(--accent)}

/* ---------- 竖屏商业 ---------- */

/* ---------- 工具表 ---------- */
.tools{padding-top:clamp(56px,10vh,120px)}
table{width:100%;border-collapse:collapse;font-size:14px}
th{text-align:left;font-weight:400;font-size:11.5px;color:var(--ink-d3);letter-spacing:.06em;padding:0 16px 10px 0;border-bottom:1px solid var(--ink-d)}
td{padding:17px 16px 17px 0;border-bottom:1px solid var(--rule-d);vertical-align:top;line-height:1.7}
td.n{font-weight:500;white-space:nowrap;font-size:15px}
td.n em{font-style:normal;font-weight:400;font-size:12px;color:var(--ink-d3);margin-left:6px}
td.s{color:var(--ink-d2);white-space:nowrap;font-size:12.5px}

/* ---------- 数字行 ---------- */

footer{margin-top:clamp(16px,2.4vh,28px);padding:22px 0 48px;border-top:1px solid var(--rule-d);display:flex;justify-content:space-between;gap:18px;flex-wrap:wrap;font-size:12px;color:var(--ink-d3)}
footer a{border-bottom:1px solid var(--rule-d)}
footer a:hover{color:var(--accent);border-color:var(--accent)}

/* ---------- 作品详情页 ---------- */

.spec{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0 20px;margin-top:clamp(28px,4vh,44px);border-top:1px solid var(--rule-d);padding-top:16px}
.spec dt{font-size:11.5px;color:var(--ink-d3);margin-bottom:4px}
.spec dd{font-size:13.5px;line-height:1.6}

/* ---------- 作品列表页 ---------- */
.list-page{padding-top:clamp(96px,14vh,150px)}
.list-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,4.2vw,64px) clamp(16px,2.4vw,36px)}
.list-grid .frame{aspect-ratio:2/1}

/* 通栏那三张（第 1、4、9 部）：占满两列，标题也跟着提一档。
   编辑式排版里尺寸本身就是层级——图大一号，标题也该大一号，
   否则一张 1360px 宽的图下面挂着 19px 的字，看上去像忘了调。

   为什么只做「通栏」和「半栏」两种宽度、不玩 2/3 + 1/3：
   源图全是 2:1。宽度比例一变，要么裁掉构图（7:5 的两张并排，
   窄的那张得裁成 2.86:1），要么两张卡高度不一样、底下空一块。
   两个尺寸已经足够打断「六张一样大」的模板感，再多就是拿构图换花样。 */
.list-grid a.is-wide{grid-column:1/-1}
/* 只补宽度、不动字号：给「两列网格里最后一张孤卡」收口用。 */
.list-grid a.is-fill{grid-column:1/-1}
.list-grid a.is-wide .cap{margin-top:clamp(14px,1.9vh,22px)}
.list-grid a.is-wide .cap b{font-size:clamp(17px,1.72vw,26px);letter-spacing:-.012em}
.list-grid .cap{display:flex;justify-content:space-between;gap:12px;align-items:baseline;margin-top:12px}
.list-grid .cap b{font-weight:500;font-size:clamp(15px,1.3vw,19px)}
.list-grid .cap i{font-style:normal;font-size:12px;color:var(--ink-d3);white-space:nowrap}
.list-grid a:hover .cap b{color:var(--accent)}
/* 没有真实链接的条目不做成可点的样子——宁可不点，也不要点了没反应。 */

/* ---------- 关于页 ---------- */
.timeline{margin-top:clamp(48px,7vh,92px);border-top:1px solid var(--ink-d)}

/* ---------- 响应式 ---------- */
@media (max-width:1080px){
        .sec-head{grid-template-columns:1fr;gap:12px}
      .spec{grid-template-columns:1fr 1fr;gap:14px 20px}
        }
@media (max-width:640px){
  .topbar{position:static;background:var(--ground);border-bottom:0}
  /* 一级页首屏是满屏影像：顶栏浮在上面，别把 100svh 顶出视口 */
  body:not(.inner) .topbar{position:fixed;background:transparent;border-bottom:0}
  .topbar .in{padding:14px var(--m) 10px}
  .mark span{display:none}
  .topbar nav{gap:14px;font-size:12.5px}
                .list-grid{grid-template-columns:1fr}
  /* 单列之后「通栏」这个概念已经没了，标题必须回到同一档。
     不回的话，前三张 17px、后面六张 15px，读者只会觉得是漏改。 */
  .list-grid a.is-wide .cap b{font-size:clamp(15px,1.3vw,19px);letter-spacing:0}
            table,thead,tbody,tr,td,th{display:block}
  thead{display:none}
  tr{padding:15px 0;border-bottom:1px solid var(--rule-d)}
  td{border:0;padding:2px 0}
            footer{flex-direction:column;gap:6px}
}

/* ============ 一级页：简介入口 ============ */

/* ============ 二级页公共 ============ */
.list-page,.resume-page,.tools-page,.contact-page{padding-top:clamp(96px,13vh,146px);padding-bottom:clamp(48px,7vh,90px)}
/* 联系页内容少，把收尾那句压到底，空的那段就变成留白而不是断掉 */
.contact-page{display:flex;flex-direction:column}

/* 简历 */
.timeline{margin-top:clamp(32px,4.6vh,56px);border-top:1px solid var(--ink-d)}

/* 软件 */

/* 联系 */

/* 二级页浅色顶栏。
   0.88 的米白底下会透出正文的暗字，滚过去时顶栏里浮着一层能认出来的字，
   像没擦干净的玻璃。加厚到 0.94 再把模糊拉大，透出来的只剩一片色温，
   不再是字——磨砂该有的样子。 */
body.inner .topbar{
  background:rgba(245,244,240,.94);
  -webkit-backdrop-filter:blur(10px) saturate(1.15);
  backdrop-filter:blur(10px) saturate(1.15);
  border-bottom:1px solid var(--rule-d);
}
/* 内容短的二级页把页脚压到底部，别让它悬在半空 */
body.inner{display:flex;flex-direction:column;min-height:100vh;min-height:100svh}
body.inner>main{flex:1 0 auto}
/* flex 子项遇到 margin:auto 会退化成 shrink-to-fit，必须显式给宽度 */
body.inner>main,body.inner>footer{width:100%}
body.inner .topbar .in{color:var(--ink-d)}
body.inner .mark span{color:var(--ink-d2)}
body.inner .topbar nav{color:var(--ink-d2)}
body.inner .topbar nav a:hover,body.inner .topbar nav a[aria-current]{color:var(--ink-d);border-color:var(--accent)}
/* 时间线列宽 */

/* ============ 无障碍与降级 ============ */
/* 键盘用户看得见焦点，鼠标用户不受打扰 */
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:2px}
body:not(.inner) :focus-visible{outline-color:var(--gold)}
::selection{background:var(--gold);color:#0A0A09}

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;scroll-behavior:auto !important}
  .frame svg{transition:none}
  .work:hover .frame svg{transform:none}
}
/* 时间线最后一行的下边框会和下一块的顶边框叠成两条线，去掉一条 */

/* ============================================================
   一级页：署名
   照作品集第一页：纯黑、姓名最大、姓名下带倒影、一行身份、四个入口。
   不放域名、不放城市、不放"做了几年"、不放清单式副标。
   ============================================================ */

body.dark{background:#000;color:#FAFAF7}

/* 姓名 + 倒影。倒影用绝对定位，不占布局高度 */
/* 倒影：不用 mask（会被 scaleY 一起翻，方向容易搞反），
   改在倒影内部盖一层黑渐变——翻转后自然就是"贴身处清楚、往下淡出"。 */

/* 身份：名词并列，字距拉开，不解释 */

/* 入场：慢一点，只做一次 */
@keyframes gateIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}

/* 占位图统一压暗去饱和：八张各唱各的调，看着像廉价滤镜 */
.list-grid .frame svg{filter:saturate(.6) brightness(.92)}

/* 联系页内容少，四块撑高一点，别让页面空掉 */

/* ============================================================
   场景首页（sc- 前缀，与旧版深色首屏的 .hero / .stage 彻底隔开）
   结构照参考站：全屏场景 → 胶囊导航 → 巨型标题 + 手写签名 → 底部卡片带
   场景是三层叠出来的：底层一段悬浮岛短片（.sc-film）铺满全屏，
   中间 app.js 用 canvas 画云（.sc-sky），上面压一层渐变压暗（.sc-veil）。
   底部原来还有一道 SVG 山脊剪影，已按用户要求去掉——全屏只留这一个背景。
   ============================================================ */

:root{
  --sky-top:#37485A;
  --sky-mid:#7C8A8C;
  --sky-low:#EFB77C;
  --sky-bot:#CE8F59;
  --lime:#C6F04A;
}

body.dark{background:#0B0F12}

.sc-stage{
  position:relative;
  min-height:100vh;min-height:100svh;
  overflow:hidden;
  isolation:isolate;
  /* 右侧片带的宽度。左栏的字距、名字字号都按它让位 */
  --rail-w:clamp(288px,36.5vw,620px);
  background:
    radial-gradient(140% 40% at 50% 86%, rgba(255,206,146,.62) 0%, rgba(255,198,138,0) 68%),
    linear-gradient(180deg, #1E2C3A 0%, #2C3D4A 20%, #3E5159 38%, #5C6B6C 54%, #9A8B7A 68%, #C99A70 80%, #B87F50 100%);
}

/* 悬浮岛短片：垫在最底下当活背景。模糊只做一点点（约 4px），主体靠亮度和不透明度压住。
   取景特意放大并往右上推：让「人和电脑」落在标题右侧的空白区，草地压在文字下面当暗场，
   既看得见内容，又不跟标题抢。 */
.sc-film{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  width:100%;height:100%;object-fit:cover;object-position:44% 46%;
  transform:translate(2%,-8%) scale(1.5);
  /* brightness 从 .80 提到 .86：片子左边是悬浮岛的背光面，本身就是一大块近黑；
     再乘 .80 之后直接压死成纯黑，跟旁边的亮云之间只剩一条边界，看着像一条黑边。
     提回来一点，那块暗部里还能看出一点岩石和草的层次——有层次的暗才是「暗」，
     没有层次的黑就是「边」。 */
  filter:blur(3px) saturate(.84) brightness(.86) contrast(1.02);
  opacity:.79;
  /* 整幅铺满，下缘不再淡出。
     原来这里有一条 46% 往下渐隐的 mask，是为了让片子亮部别在卡片带后面糊成雾，
     底下的空当由山脊剪影（.sc-ridge）接住。剪影去掉之后，那条渐隐会把画面
     底部让给 .sc-stage 的橙褐渐变，屏幕上就多出一条横向的色带——
     索性整幅到底，全屏就是这一个背景。 */
}

.sc-sky{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;filter:contrast(1.14) saturate(1.06)}

/* 顶部压暗 + 左侧压暗 + 四角压暗，保证白字在任何云层上都读得出来

   左边这一条改过两处，都是为了消掉用户圈出来的那条「黑边」：

   1. 原来写的是 100deg，等值线是斜的（倾斜 10°）——1326px 高的屏上，
      暗带边界从上到下横向差了 234px。一个「左边压暗」用斜渐变是错的，
      边界看着就是一条斜着切过去的暗带。改成 90deg。
   2. 最关键的一条：渐变最左端直接落到页面底色 #0B0F12，再在 12% 内化开。
      片子左边本来就有一条亮云（悬浮岛左边、岛外的天），它正好贴着视口边缘，
      旁边紧挨着岛子的背光暗部——一亮一黑贴着边框并排，就成了「一条黑边」。
      把最外那几十像素直接换成页面底色之后，边框和页面是同一个颜色，
      不可能再有缝；亮条被压在化开的过渡里，看不见了。
      代价是左边缘比原来更暗一点，但它是「均匀地暗」，不是「有边界的黑」。
      过渡只做 12%（约 230px）：再宽会把左下角那片雾也一起压平，
      而那片雾是画面里唯一有空气感的地方。 */
.sc-veil{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:
    linear-gradient(180deg, rgba(6,10,14,.55) 0%, rgba(6,10,14,.05) 26%),
    linear-gradient(90deg, #0B0F12 0%, rgba(9,13,17,.74) 4%, rgba(8,12,16,.32) 12%,
                            rgba(6,10,14,.18) 28%, rgba(6,10,14,0) 52%),
    radial-gradient(130% 100% at 46% 34%, rgba(0,0,0,0) 34%, rgba(6,10,14,.52) 100%);
}

/* ---------- 胶囊导航 ---------- */
.sc-nav{
  position:absolute;left:0;right:0;top:clamp(16px,3.2vh,36px);z-index:10;
  display:flex;align-items:center;justify-content:space-between;gap:18px;
  padding:0 clamp(18px,4vw,64px);
}
.sc-pill{
  display:inline-flex;align-items:center;height:38px;padding:0 20px;
  border-radius:999px;font-size:13px;letter-spacing:.06em;
  transition:background .35s var(--ease),color .35s var(--ease),
             transform .34s var(--ease),box-shadow .34s var(--ease);
}
/* 按下去要有一点回弹，但别做成弹簧玩具 */
.sc-pill:hover{transform:translateY(-1px) scale(1.03)}
.sc-pill:active{transform:translateY(0) scale(.97)}
.sc-brand{background:var(--lime);color:#0B0F0A;font-weight:600;letter-spacing:.14em;
  box-shadow:0 10px 30px -12px rgba(198,240,74,.55),inset 0 1px 0 rgba(255,255,255,.55)}
.sc-brand:hover{transform:translateY(-1px) scale(1.03);box-shadow:0 16px 38px -14px rgba(198,240,74,.7),inset 0 1px 0 rgba(255,255,255,.7)}

/* ---------- 液态玻璃：曲面高光 + 内缘回光 ----------
   原来 backdrop-filter 里还挂了一个 SVG 位移滤镜（feTurbulence → feDisplacementMap），
   想让背后画面边缘被轻微扭一下、玻璃显得「厚」。问题是 Chrome 会把元素自己的
   文字一起喂进滤镜，字被噪声推歪十几像素，看着就是「发虚」——2026-10-26 去掉了。
   体块感现在全靠渐变和内阴影撑着，不靠折射。 */
.sc-cta{
  position:relative;overflow:hidden;
  color:#FAFAF7;border:0;
  background:linear-gradient(152deg,
    rgba(255,255,255,.30) 0%,
    rgba(255,255,255,.09) 42%,
    rgba(255,255,255,.20) 100%);
  -webkit-backdrop-filter:blur(11px) saturate(1.7) brightness(1.16);
  backdrop-filter:blur(11px) saturate(1.7) brightness(1.16);
  box-shadow:
    0 10px 34px -10px rgba(4,8,10,.6),
    inset 0 1px 0 rgba(255,255,255,.55),
    inset 0 -1px 0 rgba(255,255,255,.14),
    inset 0 0 0 1px rgba(255,255,255,.15);
}
.sc-cta::before{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:
    radial-gradient(120% 92% at 50% -34%,rgba(255,255,255,.6),rgba(255,255,255,0) 60%),
    radial-gradient(96% 76% at 50% 132%,rgba(198,240,74,.20),rgba(198,240,74,0) 72%);
  mix-blend-mode:screen;
  transition:opacity .3s linear;
}
.sc-cta:hover{
  color:#0B0F12;
  background:rgba(250,250,247,.94);
  box-shadow:0 16px 44px -12px rgba(4,8,10,.65),inset 0 1px 0 rgba(255,255,255,.95);
}
.sc-cta:hover::before{opacity:0}

.sc-links{display:flex;gap:clamp(16px,2.6vw,42px);font-size:13.5px;color:rgba(250,250,247,.8)}
.sc-links a{position:relative;padding:6px 2px;transition:color .3s}
.sc-links a::after{
  content:"";position:absolute;left:0;right:100%;bottom:0;height:1px;
  background:var(--lime);transition:right .42s var(--ease);
}
.sc-links a:hover{color:#FAFAF7}
.sc-links a:hover::after{right:0}

/* ---------- 标题组 ----------
   左边一块字，右边一条竖着走的片子。名字占的宽度是「屏幕宽 - 带子宽 - 留白」，
   所以字号跟着 vw 走而不是写死：13.9vw 在 1440 下会顶到带子边上。 */
.sc-hero{
  position:absolute;z-index:6;
  left:clamp(20px,8vw,150px);
  right:calc(var(--rail-w) + clamp(24px,3.2vw,64px));
  top:0;bottom:0;
  display:flex;flex-direction:column;justify-content:center;
}
/* 780 是「一行五十来字」的可读上限。简介那三行最长一句 51em，
   框给到 820 是为了让它在 15px 上还留 7% 余量——正文本身比框短，
   放宽框宽不会让字变宽，只是不让它被框卡着折。

   这个上限只给简介，不给整栏：名字是 inline-block，会被父级宽度卡住而折行。
   宋体的「尤博文」比行书宽 8%，1920 下 864px，超过 820 就断成「尤博」+「文」。
   简介自己带 max-width，父级放开即可。 */
.sc-hero-in{max-width:100%}
/* 文字背后单独压一小片暗场。
   全局那层 .sc-veil 是从左往右淡出的（0.62 → 62% 处归零），到正文右端只剩
   一成多一点——片子里那块亮雾一飘过来，后半行白字就只剩 3:1 左右，读得很吃力。
   这里按「正文实际占的那块」再补一层径向压暗：只补字后面，不碰右边的片带，
   也不把整张画面压灰。z-index:-1 落在 .sc-hero 自己的层叠上下文里，
   所以它在全局遮罩之上、文字之下。 */
.sc-hero::before{
  content:"";
  position:absolute;z-index:-1;pointer-events:none;
  left:-6%;right:-6%;top:18%;bottom:8%;
  background:radial-gradient(56% 50% at 32% 56%,
    rgba(7,10,13,.50) 0%,rgba(7,10,13,.25) 52%,rgba(7,10,13,0) 76%);
}
/* 名字上面压一行极小的小字：先说「我是干什么的」，再让名字出场。
   前面拖一根 1px 的绿线，把这一行和左边的留白接起来。 */
.sc-kicker{
  display:flex;align-items:center;gap:12px;
  margin-bottom:clamp(10px,1.9vh,22px);
  font-size:10.5px;line-height:1;letter-spacing:.4em;margin-right:-.4em;
  color:rgba(250,250,247,.56);
}
.sc-kicker::before{
  content:"";flex:0 0 auto;
  width:clamp(18px,2.4vw,34px);height:1px;
  background:rgba(198,240,74,.62);
}
/* 名字用思源黑体 700（Noto Sans SC）。只子集了「尤博文」三个字，4KB——
   中文展示字体动辄十几兆，但这里只排三个字，没必要把整副字库搬上来。

   这行字换过四次，留个记录：
   1. 手写行书（志莽行书）——问题不是「不够艺术」，是「不够可信」：三个字
      大小不齐、重心各异，放到 216px 之后第一眼像签名草稿而不是名字，
      「博」尤其难认。简历网站的第一屏就是这行字，认不出来就是失职。
   2. 思源宋体 800——高对比、有衬线，像电影片头。但字号一大，横细竖粗那套
      对比反而显得「瘦而尖」，笔画在照片背景上发飘，用户看下来觉得「怪」。
   3. 思源黑体 800：笔画粗细一致、重心方正，整块字压在明暗交错的云层上最扎实。
   4. 思源黑体 700（现在）：800 撑到 300px 之后，横竖笔画都偏厚，整块字看着
      「闷」——用户的原话是「有点粗了」。降一档到 700，笔画的粗细差立刻回来了，
      「博」里那些小横之间透出气来；再往下到 600 就发飘，在照片上压不住。

   字重写死 400（字体文件里已经是 700 的轮廓），并且 font-synthesis:none：
   浏览器一旦合成加粗，字形会被算法再描一遍，边缘发糊。 */
@font-face{
  font-family:'Site Display';
  src:url('../fonts/display/sans-name-700.woff2') format('woff2');
  font-weight:400;font-style:normal;font-display:swap;
}
.sc-hero h1{
  font-family:'Site Display','Noto Sans SC','Source Han Sans SC','PingFang SC','Microsoft YaHei',sans-serif;
  font-size:clamp(70px,15vw,300px);
  font-weight:400;font-synthesis:none;
  line-height:1.04;
  /* 黑体是满格的方块字，字距 0 就已经贴在一起；再负会撞。
     左缘不补负边距：行书那 -.04em 是为了让「尤」的撇尖顶到左边缘，
     黑体的「尤」四边留白均匀，补了反而缩进。 */
  letter-spacing:0;
  color:#FAFAF7;
  text-shadow:
    0 1px 0 rgba(8,11,14,.26),
    0 2px 12px rgba(8,11,14,.32),
    0 16px 52px rgba(8,11,14,.34);
}

/* ---------- 标题悬停：一层跟着光标走的柔光，把字里的金绿渐变擦出来 ----------
   同一个字排两遍：底层是原来的白字，上层是渐变字，用径向遮罩只在光标周围露出来。
   遮罩半径用 @property 注册成可过渡的长度，所以是「扩散」而不是「闪现」。 */
@property --sc-r{syntax:'<length>';inherits:false;initial-value:0px}

/* 三个字，永远不许折行。父级宽度不够时宁可溢出，也不能断成两行——
   名字断行在首页上等于把版面撕开。 */
.sc-title{position:relative;display:inline-block;white-space:nowrap}
.sc-t-b{display:block}
.sc-t-lit{
  position:absolute;inset:0;display:block;
  pointer-events:none;user-select:none;
  background:linear-gradient(96deg,#C6F04A 0%,#E8CE7C 20%,#FFF4D8 40%,#E8CE7C 60%,#C6F04A 82%);
  background-size:320px 100%;
  -webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;
  text-shadow:none;
  -webkit-mask-image:radial-gradient(circle var(--sc-r) at var(--sc-mx,50%) var(--sc-my,50%),#000 0%,rgba(0,0,0,.86) 30%,rgba(0,0,0,.38) 58%,transparent 80%);
  mask-image:radial-gradient(circle var(--sc-r) at var(--sc-mx,50%) var(--sc-my,50%),#000 0%,rgba(0,0,0,.86) 30%,rgba(0,0,0,.38) 58%,transparent 80%);
  transition:--sc-r .62s cubic-bezier(.22,1,.36,1),opacity .3s linear;
  opacity:0;
}
.sc-title.is-lit .sc-t-lit{opacity:1}
/* 万一浏览器不支持文字裁剪，宁可不要这层，也别让渐变方块盖在标题上 */
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .sc-t-lit{display:none}
}
/* 英文行不跟名字抢：Portfolio 压成一条小号宽字距的标签，右边接手写签名。
   字号差三倍是故意的——签名是「人」的痕迹，标签只是分类。 */
.sc-en{
  display:flex;align-items:baseline;gap:clamp(12px,1.8vw,26px);
  margin-top:clamp(4px,.9vh,14px);
}
.sc-en .en{
  font-size:clamp(9.5px,.8vw,11.5px);
  font-weight:500;line-height:1;
  /* 字距加在每个字后面，末字也会多出一截——用负右外边距收掉，
     不能拿 text-indent 顶，那会把整行往右推，左缘就和名字对不齐了 */
  letter-spacing:.5em;margin-right:-.5em;
  color:rgba(250,250,247,.52);
}
.sc-en .sig{
  font-family:'Caveat',cursive;
  font-size:clamp(21px,2.3vw,36px);
  line-height:1;color:var(--lime);
  /* 签名不要压到 Portfolio：右移一点、抬一点，落在名字右下角的空档里 */
  transform:rotate(-6deg) translate(.12em,-.08em);
  text-shadow:0 0 26px rgba(198,240,74,.3);
  white-space:nowrap;
}
/* 简介的最终样式在后面（搜索「首页简介」），这里不再重复定义——
   之前有两份，后一份带 text-shadow 的才生效，改前一份等于白改。 */

/* ---------- 右侧片带：竖着往上走的一列 2:1 画面 ----------
   为什么从横排改成竖排：作品八成是 2:1 的横片，塞进 9:16 的竖卡里，
   左右各切掉一大半，人物经常只剩半张脸。竖着排，卡就是 2:1，
   跟原片一比一，一个像素都不用裁。

   形状：一列匀速上移的胶片，上下用遮罩淡出。卡片的大小和倾角每帧由脚本算
   （见 app.js 的 TAPER / TILT）：中间最小、往上下两端长回来，同时上方逆时针、
   下方顺时针转一点——看上去像一条被弯起来的带子，中间那段退得最远。
   常态整条带子只有满幅的 84%，光标压住的那张才弹回满幅。 */
.sc-rail{
  position:absolute;right:0;top:0;bottom:0;z-index:7;
  /* 左边多留 120px 空跑（用 padding-left 补回去，卡片位置一点不动）。
     为什么：卡片的 box-shadow 一直铺到 rail 边框盒之外，而遮罩和
     overflow:hidden 都在边框盒上切一刀，切出来是一条笔直的竖边——
     悬停放大时阴影变长，这条边就露出来了。
     把盒子往左撑开 120px 之后，那一刀落在离卡片 120px 外的地方，
     阴影在那里已经很淡；再叠一道 100px 的水平淡入，连这刀都看不出来。
     用固定 px 而不是百分比：卡片悬停时会正好长到 rail 的内宽（HOVER = 1/QUIET），
     百分比淡入在窄屏上会把卡片自己的左边一起淡掉。 */
  width:calc(var(--rail-w) + 120px);
  padding-left:120px;
  padding-right:clamp(0px,2.2vw,34px);
  overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 12%,#000 88%,transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 12%,#000 88%,transparent 100%);
}
/* 左边再加一道水平淡入。两层遮罩必须用 mask-composite:intersect 取交集；
   不支持的浏览器会退回默认的 add（并集），那样上下淡出就整个失效了——
   所以放进 @supports：宁可没有左淡入，也不能丢上下淡出。 */
@supports (mask-composite:intersect){
  .sc-rail{
    -webkit-mask-image:
      linear-gradient(180deg,transparent 0,#000 12%,#000 88%,transparent 100%),
      linear-gradient(90deg,transparent 0,#000 100px);
    mask-image:
      linear-gradient(180deg,transparent 0,#000 12%,#000 88%,transparent 100%),
      linear-gradient(90deg,transparent 0,#000 100px);
    -webkit-mask-composite:source-in;
    mask-composite:intersect;
  }
}
.sc-track{
  position:absolute;inset:0;
  cursor:grab;touch-action:none;
}
.sc-track.dragging{cursor:grabbing}
.sc-card{
  position:absolute;left:0;top:0;
  display:block;
  background:#141813;
  overflow:hidden;
  /* 圆角从 4px 提到 12px。
     4px 在 393px 宽的卡上等于 1%——肉眼就是直角，卡片像直接从网页上抠下来的。
     12px 在常态（缩放 0.8–1.0）下看着是 10–12px，读得出「这是一张有厚度的卡」，
     又没到 20px 那种 App 卡片的程度——那样会把胶片感洗掉。 */
  border-radius:12px;
  /* 影子分四层，越远越淡——这是物理，也是暗底上不糊成一团的关键。
       · 1px 3px   —— 接触影。只负责「卡片落在东西上」，几乎看不见；
       · 8px 18px  —— 近影。物体自己的体积；
       · 22px 44px —— 中景，把卡片从背景上「托起来」，立体感主要来自这层；
       · 48px 90px —— 远影，铺开一圈很淡的暗，负责「这里有一块东西」。

     上一版是 .62 / .58 / .64 / .72，比现在高四倍。暗底上四层叠起来，
     卡片之间的间隙被压成死黑，整条带子像垫在一张黑纸上——用户说的
     「很深」就是这个。实测：间隙亮度 27.6，改后 44.7。

     第一层原来写的是 `4px 1px 2px`，模糊只有 2px 配 .62 的透明度，
     等于一条实心黑边，那是「硬」的来源。现在压到 .22 并给了 -1px 收束，
     它只在卡片下沿留一线，不再是一条线。

     颜色用 rgba(6,10,14) 而不是纯黑：背景是暖色云海，纯黑影子压在暖色上
     会发脏。深蓝黑更接近真实阴影里带的那点环境色。

     横向偏移去掉了：卡片在一条竖排的带子里，左右两侧的间隙一样宽，
     往右下偏 4px 会让每张卡看着都往右歪。 */
  box-shadow:
    0 1px 3px -1px rgba(6,10,14,.26),
    0 8px 18px -6px rgba(6,10,14,.30),
    0 22px 44px -16px rgba(6,10,14,.34),
    0 48px 90px -32px rgba(6,10,14,.26);
  will-change:transform;
  backface-visibility:hidden;
}
.sc-card .art{position:absolute;inset:0}
.sc-card .art img{width:100%;height:100%;object-fit:cover;display:block}
/* 压暗渐变 + 亮边，都挂在这一层上。
   为什么亮边必须写在 ::after 而不是父级的 inset box-shadow：inset 影画在
   背景之上、子元素之下，而 ::after 正盖在照片上——写父级会被 ::after 的
   压暗渐变吃掉，尤其是下沿那一段（那里渐变不透明度已经到 .92）。
   光从上方来，所以上沿最亮（1px 实边）、四周只留一圈很淡的轮廓。 */
.sc-card::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  border-radius:inherit;
  background:linear-gradient(180deg,rgba(8,12,10,0) 40%,rgba(8,12,10,.32) 66%,rgba(8,12,10,.92) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.22),
    inset 0 0 0 1px rgba(255,255,255,.09);
}
.sc-card .cap{
  position:absolute;left:16px;right:auto;bottom:13px;z-index:2;color:#F2F0EA;
  /* 宽度跟着文字走，不再横跨整张卡。
     上一版是 left:16px;right:16px，框宽 = 卡片宽 - 32px（1920 下 487px），
     悬停时那一片玻璃把照片下半截整个糊住了。这一页是给人看图的，
     字幕不该占这么大——收成 max-content 之后只剩包住文字那一小块。 */
  width:max-content;max-width:calc(100% - 32px);
  border-radius:10px;padding:0;
  transition:opacity .35s linear,background .42s var(--ease),
             box-shadow .42s var(--ease),padding .42s var(--ease);
}
/* 抽出来的那张卡，字幕变成一小片玻璃，只包住文字。

   这里不再挂 url(#sc-refract) 那个位移滤镜。Chrome 会把元素自己的文字
   也喂进 feDisplacementMap——噪声把字形推歪 14px，看起来就是「发虚」。
   放大 3 倍对比过：去掉之后文字立刻锐利，毛玻璃的质感一点没丢，
   而且背后照片不再被扭出波纹，更像「玻璃盖在图上」而不是「图被搅了一下」。

   背景透明度从 .24/.07 降到 .14/.045：原来那片白太厚，压在照片上像蒙了层雾。
   blur 也从 8px 收到 6px——糊得越狠，越挡图。 */
.sc-card:hover .cap{
  padding:6px 9px;
  background:linear-gradient(150deg,rgba(255,255,255,.14),rgba(255,255,255,.045));
  -webkit-backdrop-filter:blur(6px) saturate(1.5);
  backdrop-filter:blur(6px) saturate(1.5);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.3),
    inset 0 0 0 1px rgba(255,255,255,.1),
    0 6px 18px -6px rgba(0,0,0,.4);
}
.sc-card .cap b{display:block;font-weight:500;font-size:12.5px;line-height:1.35;letter-spacing:.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 片名底下那行（片种 / 场次）常态不显示。
   卡片常态缩到 84%，9.5px 乘下来只剩 8px——那不是「小」，是糊成一条灰线。
   抽出来的时候卡片回到满幅，这行字才有地方站，也才有意义（平时只需要认出是哪部）。
   用 opacity 不用 display：留着它的高度，悬停时字幕块不会突然长高、片名不会跳。 */
.sc-card .cap i{display:block;font-style:normal;font-size:9.5px;color:rgba(242,240,234,.6);margin-top:3px;opacity:0;transition:opacity .3s linear}
.sc-card:hover .cap i{opacity:1}

/* ---------- 入场 ---------- */
@keyframes sc-rise{from{opacity:0;transform:translateY(26px)}to{opacity:1;transform:none}}
.sc-kicker,.sc-hero h1,.sc-en,.sc-sub{animation:sc-rise 1.15s var(--ease) both}
.sc-en{animation-delay:.1s}
.sc-sub{animation-delay:.2s}
.sc-nav{animation:sc-rise .9s var(--ease) both}
.sc-rail{animation:sc-rise 1.2s .28s var(--ease) both}

/* 竖屏 / 方屏：16:9 的片子会被裁得很窄，取景改成偏中心偏上，
   让「人和电脑」落在正文和卡片带之间的空档里，亮度也压低一档免得中间发雾 */
@media (max-width:1024px){
  .sc-film{
    transform:scale(1.06);
    object-position:52% 40%;
    filter:blur(3px) saturate(.86) brightness(.72) contrast(1.0);
    opacity:.74;
  }
}

/* 窄到放不下两栏：字回到顶部，片带变成底部一整条，还是竖着往上走。
   2:1 的卡在手机上正好占满宽度，比原来横排的竖卡舒服得多。

   这里把 .sc-stage 改成竖排的 flex：片带不再写死高度、贴底放，
   而是吃掉文字下面剩下的全部空间。写死高度会在 834 这种竖屏平板上
   留出两百多像素的空档——正文底下悬着一条带子，中间什么也没有。 */
@media (max-width:900px){
  .sc-stage{--rail-w:auto;display:flex;flex-direction:column}
  .sc-hero{
    position:relative;
    left:auto;right:auto;top:auto;bottom:auto;
    /* 单栏时占满「带子以上」的整块，内容竖向居中。
       高屏（iPad 竖屏 820×1180）上原来是顶上对齐，结果名字和简介贴着顶、
       带子贴着底，中间空出三百多像素，像漏了一块。居中之后空白被摊到上下两边，
       同一个屏变成「上面留白 / 中间是字 / 下面留白 / 带子」的对称结构。
       矮屏（640/667）本来就没有富余，居中等于不居中，不影响。 */
    flex:1 1 auto;
    display:flex;flex-direction:column;justify-content:center;
    padding:clamp(78px,13vh,132px) 20px 0;
  }
  .sc-hero-in{max-width:none}
  .sc-hero h1{font-size:clamp(88px,23vw,150px);line-height:1.06}
  .sc-en{gap:clamp(11px,3vw,18px);margin-top:18px}
  .sc-en .en{font-size:clamp(10px,2.6vw,11.5px);letter-spacing:.4em;margin-right:-.4em}
  .sc-en .sig{font-size:clamp(19px,5vw,28px)}
  /* 简介的字号/上边距不在这里定：这条规则在文件里排在 .sc-sub 主定义之前，
     同权重、主定义在后，写在这里会被整个盖掉（13px 从来没生效过）。
     单栏的字号在主定义后面单独给了。 */
  .sc-rail{
    position:relative;
    left:auto;right:auto;top:auto;bottom:auto;
    width:auto;
    /* 高度定死，不再让它去吸剩下的空间：
       竖版是「一列很长的胶片」，横版是一条固定高度的带子。
       定高之后配 margin-top:auto，带子贴在屏底，正文和带子之间自然留白，
       短屏（360×640）上也不会把页面顶出视口。 */
    flex:0 0 auto;
    margin-top:auto;
    height:clamp(150px,20vh,208px);
    padding:0 20px;
    /* 横向走，遮罩就得跟着转 90°：左右淡出，不是上下 */
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 5%,#000 95%,transparent 100%);
    mask-image:linear-gradient(90deg,transparent 0,#000 5%,#000 95%,transparent 100%);
  }
  /* pan-y：竖着划让浏览器滚页面，横着划才交给带子。
     桌面那条是 touch-action:none（整条带子吃掉手势），手机上不能这么干——
     首页在短屏上是要滚一点点的，带子把竖划吃掉，手指按在带子上就卡住了。 */
  .sc-track{touch-action:pan-y}
  .sc-card .cap{left:12px;right:12px;bottom:10px}
  .sc-card .cap b{font-size:11.5px}
  .sc-card .cap i{font-size:9px}
}

@media (max-width:760px){
  .sc-nav{padding:0 16px;gap:8px}
  .sc-links{gap:13px;font-size:12.5px}
  .sc-pill{height:34px;padding:0 14px;font-size:12px;letter-spacing:.1em}
  /* 竖屏没地方放岛，取右侧云层当暖色氛围，别让草地糊在正文后面 */
  .sc-film{
    transform:scale(1.12);
    object-position:66% 42%;
    filter:blur(3px) saturate(.86) brightness(.82) contrast(1.0);
    opacity:.74;
  }
  }

/* 短屏：360×640（SE / 老安卓）、390×667（iPhone 8）这一档。
   首页本来是「一屏就是全部」的设计，但这两种屏幕竖着只有 640/667，
   名字 + 三行简介 + 片带加起来会顶出去六十来像素，页面能滚一小截——
   滚一下发现后面什么都没有，观感就是「没做完」。
   收三处：顶上留白、名字、简介与名字的间距。名字只收一档（88→70），
   再小就不像「一屏就这一行字」了。 */
@media (max-width:900px) and (max-height:720px){
  .sc-hero{padding-top:clamp(54px,8vh,76px)}
  .sc-hero h1{font-size:clamp(70px,19vw,110px)}
  .sc-en{margin-top:14px}
  .sc-sub{margin-top:clamp(12px,2vh,20px)}
}

/* 噪点 + 色彩分级：把程序画的云从"雾"拉向"胶片" */
.sc-grain{
  position:absolute;inset:0;z-index:4;pointer-events:none;
  opacity:.055;
  mix-blend-mode:overlay;
  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='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:160px 160px;
}
/* 暖高光 / 冷暗部，让画面有片子感 */
.sc-stage::after{
  content:"";position:absolute;inset:0;z-index:5;pointer-events:none;
  background:
    radial-gradient(90% 55% at 50% 76%, rgba(255,196,132,.16) 0%, rgba(255,196,132,0) 70%),
    linear-gradient(180deg, rgba(30,48,68,.28) 0%, rgba(30,48,68,0) 34%);
  mix-blend-mode:soft-light;
}

/* ============================================================
   单部作品页（work/<slug>.html）
   深色底 + 左侧信息 + 右侧受控尺寸的画面卡。
   不铺满屏：占位图本身是失焦的，放大到 1440 只会变成一团灰雾。
   ============================================================ */

body.dt-page{background:var(--ground);color:var(--ink-l)}
/* 二级页的 main 顶部留了 81px 给固定顶栏；详情页没有顶栏，必须清掉 */
body.dt-page>main{width:100%;padding-top:0}

.dt-hero{
  display:flex;flex-direction:column;
  min-height:78vh;min-height:78svh;
  padding-bottom:clamp(40px,7vh,84px);
  background:
    radial-gradient(90% 70% at 78% 42%,rgba(200,180,138,.075) 0%,rgba(200,180,138,0) 62%),
    var(--ground);
}

.dt-top{
  width:100%;
  display:flex;justify-content:space-between;align-items:baseline;gap:20px;
  padding-top:clamp(22px,3.4vh,40px);
  font-size:12.5px;letter-spacing:.06em;color:#7E7A6F;
}
.dt-top a{padding-bottom:3px;border-bottom:1px solid rgba(255,255,255,.16);transition:color .25s,border-color .25s}
.dt-top a:hover{color:var(--ink-l);border-color:var(--gold)}
.dt-mark{color:#A9A499;letter-spacing:.14em}

/* 主体：左文右图。图的尺寸受控，占位图才看得出内容 */
.dt-main{
  width:100%;flex:1 0 auto;
  display:grid;grid-template-columns:minmax(0,.95fr) minmax(0,1.1fr);
  gap:clamp(28px,5.5vw,96px);align-items:center;
  padding-top:clamp(32px,6vh,72px);
}

.dt-kind{font-size:12px;letter-spacing:.18em;color:var(--gold);margin-bottom:clamp(14px,2vh,24px)}
.dt-info h1{
  font-weight:600;font-size:clamp(34px,5.4vw,82px);
  line-height:1.12;letter-spacing:-.025em;color:var(--ink-l);
  /* 中文标题不能按字断行，「@智云官号 视频产出」会被切成两半 */
  word-break:keep-all;overflow-wrap:anywhere;
}
.dt-lead{
  margin-top:clamp(18px,2.6vh,30px);
  font-size:clamp(13.5px,1.06vw,15.5px);line-height:2;
  color:#A9A499;max-width:30em;
}

/* 身份 / 发布 / 年份 + 观看入口，拉成一条横贯首屏底部的 credits 带。
   原来这四样挤在标题下面一小坨 dl 里，跟标题抢同一块注意力；
   现在挪到首屏底部，像片尾字幕那样一行一件事，标题就彻底空出来了。

   为什么用 space-between 而不是均分：左边是「谁做的」，右边是「去哪看」，
   两件事性质不同，中间留白本身就是分隔线，比再画一条竖线干净。 */
.dt-credits{
  width:100%;
  /* 竖版作品（智云 / X100）的 9:16 画面高 501px，正好把 .dt-main 撑满，
     不留 margin 的话画面底边会贴着这条横线，看着像压上去了。 */
  margin-top:clamp(16px,2.6vh,32px);
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;
  gap:clamp(16px,2.6vw,40px);
  border-top:1px solid var(--rule-l);
  padding-top:clamp(16px,2.4vh,24px);
}
.dt-credits dl{display:flex;flex-wrap:wrap;gap:clamp(18px,3.4vw,56px);margin:0}
.dt-credits dl>div{min-width:0}
.dt-credits dt{font-size:11px;letter-spacing:.1em;color:#6E6A60;margin-bottom:7px}
.dt-credits dd{
  margin:0;font-size:13.5px;line-height:1.5;color:var(--ink-l);
  font-variant-numeric:tabular-nums;
}

/* 画面卡 */
.dt-shot{margin:0;min-width:0}
.dt-shot-in{
  position:relative;overflow:hidden;
  aspect-ratio:2/1;background:#141813;
  border:1px solid rgba(255,255,255,.09);
  box-shadow:0 40px 90px -40px rgba(0,0,0,.9),0 2px 0 rgba(255,255,255,.03) inset;
}
.dt-shot-in svg{width:100%;height:100%;filter:saturate(.72) contrast(1.02)}
.dt-hero.is-v .dt-shot{max-width:min(282px,46%);margin-left:auto}
.dt-hero.is-v .dt-shot-in{aspect-ratio:9/16}
.dt-shot figcaption{
  margin-top:11px;font-size:10.5px;letter-spacing:.04em;color:#5F5B53;
  text-align:right;
}
.dt-hero.is-v .dt-shot figcaption{text-align:right}

/* ---------- 正文 ---------- */
.dt-body{
  background:var(--paper);color:var(--ink-d);
  padding-top:clamp(48px,8vh,96px);padding-bottom:clamp(44px,7vh,80px);
  max-width:1600px;
}
/* 「关于」这一段原来是顶格一大坨：一个小标签 + 三行一样大的字，左边排到 700px
   就停，右边 600 多像素全是空的。改两件事：

   1. 标签挪到左边距里当书眉（像书里那种侧栏小标），正文另起一列。
      原来标签和正文同列，读起来是「关于 / 然后一堆字」，现在是「关于 │ 一堆字」，
      左边缘立刻立起来了。
   2. 第一段提一档当导语。三行一样大的字没有入口，读者不知道从哪儿开始看；
      第一行大一号，眼睛就有着落。

   正文宽度按中文的舒服区间卡在 700px 上下（一行 40 来字），不是按 44em 算的
   ——em 跟着元素自身字号走，字号一变宽度就跟着飘，之前首页简介折行折得莫名其妙
   就是这个原因。 */
.dt-text{
  display:grid;
  grid-template-columns:clamp(56px,6.4vw,96px) minmax(0,1fr);
  gap:clamp(14px,2.6vw,36px);
  max-width:54em;
}
.dt-text .label{
  grid-column:1;margin:0;padding-top:.55em;
  font-size:11.5px;letter-spacing:.1em;color:var(--ink-d3);
}
.dt-text p{grid-column:2;font-size:clamp(14.5px,1.12vw,16.5px);line-height:1.92;color:#3A3931;margin-bottom:.95em}
.dt-text p:first-of-type{
  font-size:clamp(16.5px,1.42vw,21px);line-height:1.8;
  color:#23221D;letter-spacing:-.004em;margin-bottom:1.05em;
}
.dt-text p:last-child{margin-bottom:0}

/* ---------- 上一部 / 下一部 ---------- */
.dt-nav{
  background:var(--paper);color:var(--ink-d);
  display:grid;grid-template-columns:1fr 1fr;gap:clamp(16px,3vw,40px);
  border-top:1px solid var(--rule-d);
  padding-top:clamp(22px,3.4vh,34px);padding-bottom:clamp(52px,9vh,110px);
  max-width:1600px;
}
.dt-nav a{display:flex;flex-direction:column}
/* 左边只有两行字，右边多了一张封面，两格高度就不一样了。
   让左边贴底对齐，两边的标题才会落在同一条基线上，看着是「并排」而不是「错位」。 */
.dt-prev{justify-content:flex-end}
.dt-nav span{display:block;font-size:11.5px;letter-spacing:.1em;color:var(--ink-d3);margin-bottom:9px;transition:color .25s}
.dt-nav b{display:block;font-weight:500;font-size:clamp(15px,1.35vw,20px);line-height:1.4;transition:color .25s}
.dt-next{text-align:right;align-items:flex-end}
/* 「下一部」带一张封面。原来两边都只是两行小字，读到页尾就没气了；
   这一页最后看到的东西应该是一张画面。 */
.dt-next figure{
  width:clamp(150px,15vw,224px);aspect-ratio:2/1;
  margin:0 0 clamp(13px,2vh,20px);
  overflow:hidden;background:#141813;
  border:1px solid var(--rule-d);
}
.dt-next figure img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .9s var(--ease)}
.dt-next:hover figure img{transform:scale(1.05)}
.dt-nav a:hover b{color:var(--accent)}
.dt-nav a:hover span{color:var(--ink-d2)}

@media (max-width:900px){
  .dt-main{grid-template-columns:1fr;gap:clamp(26px,5vh,44px);align-items:start;padding-top:clamp(28px,5vh,56px)}
  .dt-shot{order:-1}
  .dt-hero.is-v .dt-shot{max-width:min(300px,70%);margin-left:0}
  .dt-info h1{max-width:none}
  .dt-shot figcaption{text-align:left}
}
@media (max-width:560px){
  .dt-hero{min-height:auto;padding-bottom:clamp(34px,6vh,52px)}
  /* credits 带在窄屏改成竖着叠：三组字横排在 390px 上必然折成一团。 */
  .dt-credits{flex-direction:column;align-items:flex-start}
  .dt-credits dl{gap:14px 28px}
  /* 左边距只剩 390px，书眉挤不进边栏了，回到「标签在上、正文在下」。
     必须把 grid-column 一起重置回 1：留着 2 的话网格会多长出一条隐式列。 */
  .dt-text{grid-template-columns:1fr;gap:0}
  .dt-text .label,.dt-text p{grid-column:1}
  .dt-text .label{padding-top:0;margin-bottom:14px}
  /* 单列之后「通栏」已经没意义，竖版那两张也该跟着放大：
     64% 在 390px 上只剩 224px，一张竖版封面缩成小邮票，看不清标题字。 */
  .dt-gallery figure.is-tall{width:min(320px,86%)}
  .dt-nav{grid-template-columns:1fr;gap:22px}
  .dt-next{text-align:left;align-items:flex-start}
}

@media (max-width:760px){
}

/* 作品页原来在横屏列表下面还挂了一段「竖屏商业」三列网格（.sub-head + .vgrid3）。
   9:16 的卡和 16:9 的卡同页并排，横向尺寸对不齐、看着散，用户要求整段删掉；
   对应的 .sub-head / .vgrid3 / .list-grid a.is-v 规则也一并清掉了，不留死样式。
   那三部竖屏作品本身还在（首页片带能点到，作品详情页也都在）。 */

/* ============================================================
   真图替换占位图后的统一处理 + 抽牌交互
   ============================================================ */

/* 悬停：光标压住的那张微微放大，字幕化成一小片玻璃，其余整条带子暗下去。
   放大不用 CSS 的 transform——每帧的 transform 是脚本写的，写死的 :hover 会被
   行内样式盖掉，所以倍率在脚本里乘（见 app.js 的 hoverEl）。 */
/* 抽出来的那张再抬一层：近影收紧、远影铺开，等于「离背景更远」。
   光有放大没有影子变化，放大就只是变大，不是往前。
   transition 不写在这里——文件末尾那条 `.sc-card{transition:...}` 在后面，
   同权重会被它整个盖掉，两处都写只会让人以为这里生效。 */
.sc-card:hover{
  z-index:9;filter:none;
  /* 抽出来的那张是「抬起来」，所以影子更大、更淡。
     上一版反过来（.52 → .70，越抬越黑）——真实世界里物体离地面越远，
     影子越大越淡，反着做就是「不自然」的来源。
     抬起来之后还要保持边缘清楚，靠的是 ::after 那圈内描边，不是靠加深阴影。 */
  box-shadow:
    0 2px 6px -1px rgba(6,10,14,.22),
    0 14px 30px -8px rgba(6,10,14,.28),
    0 36px 64px -20px rgba(6,10,14,.32),
    0 70px 128px -42px rgba(6,10,14,.26);
}
.sc-card:hover::after{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.32),
    inset 0 0 0 1px rgba(255,255,255,.13);
}
.sc-card:hover .cap b{color:#fff}
.sc-card .cap b{transition:color .35s}
/* 被压住的那张亮起来，邻卡稍暗一点，视线才集中 */
.sc-rail:hover .sc-card:not(:hover){filter:brightness(.76) saturate(.82)}

/* ---------- 首页简介（作品集原文三行） ----------

   三句话就是三行，一行一句，谁也别折。

   折行的真凶不是字号，是 max-width。原来写的是 39em —— em 按自己的字号算，
   15.26px 下就是 595px，而最长那句（「4 年+视频制作经验……」）单行要 779px。
   于是第一句折成两行、第二句折成两行、第三句一行，右端参差成三级台阶，
   正是「显得廉价」的那种样子。

   现在宽度不再自己限，交给 .sc-hero-in 的 820px 兜底；字号反过来按
   「最长那一行刚好放得下」倒推：779px ÷ 15.264px = 51.03em 是最长行的长度，
   820px 的框给到 15px 还剩 7% 余量。往下按 1vw 缩，1280 时是 12.8px、
   最长行 645px、框 669px，余 3.6%。
   （0.523 是左栏占屏宽的比例：100% − 8vw 左边距 − 36.5vw 片带 − 3.2vw 间距。）

   为什么留余量而不是压到刚好：中文字是全角，一个字正好 1em，换字体不会变宽；
   但「4 年+」「3C 类」「Top」这些拉丁字符和空格，在 PingFang 和雅黑下宽度不同，
   差个 1–2% 是有的。刚好贴边的话，换个系统就折了。

   字距从 .012em 改成 0：中文正文字距本来就是 0，那点字距在这行长度上
   要吃掉 8px 的预算，换不来任何观感。 */
.sc-sub{
  margin-top:clamp(20px,3vh,34px);
  list-style:none;
  max-width:min(100%,820px);
  font-size:clamp(12.5px,1vw,15px);
  line-height:1.72;color:rgba(252,252,249,.9);
  text-shadow:0 1px 20px rgba(6,10,14,.62);
  /* 万一哪档宽度真的放不下（901–1267px 这一带：两栏但左栏不够宽），
     宁可折成两条一样长的，也别折成「一整行 + 两个字的尾巴」——
     前者像刻意断句，后者像排版失误。一行的时候这条不起作用。 */
  text-wrap:balance;
}
.sc-sub li{position:relative;padding-left:0}
/* 三句之间的间隙收到 3px 上下：line-height 1.72 已经给足了行距，
   再叠 8px 就变成「三个列表项」而不是「三行字」了。 */
.sc-sub li+li{margin-top:clamp(2px,.35vh,5px)}

/* 单栏（≤900）字号回到 13.5：这一档文字铺满整屏宽，一行五十来字放得下，
   没必要跟着 1vw 缩到 12.5——手机上小一号是实打实的难读。
   必须写在这里（主定义之后），否则会被上面那条盖掉。 */
@media (max-width:900px){
  .sc-sub{font-size:13.5px}
}

/* ---------- 列表页：真图 ---------- */
.list-grid .frame img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .9s var(--ease),filter .5s;
}
.list-grid a:hover .frame img{transform:scale(1.035)}
.list-grid .frame{background:#141813}

/* 列表页标题区只留标题 */
.sec-head{grid-template-columns:1fr;align-items:start;margin-bottom:clamp(22px,3.2vh,38px)}

/* ---------- 二级页的页头：标题要压得住场 ----------
   原来三个二级页的标题都是 clamp(24px,2.6vw,40px)，1440 下 37px。
   同一页里卡片标题 19px、正文 16px，37px 只比它们大一倍多一点——
   一进页面眼睛不知道该落在哪。详情页的标题是 78px，一对比就看出二级页太怯。

   标题提到 76px 上下，跟详情页同一个量级；简介在软件页挪到右栏贴底，
   左边空出来的那 850px 才有着落（详见 .tools-page .sec-head）。 */
.list-page .sec-head h1,
.contact-page .sec-head h1{font-size:clamp(34px,5vw,76px);line-height:1.06;letter-spacing:-.03em}

.tools-page .sec-head{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.62fr);
  gap:clamp(20px,4vw,64px);align-items:end;
}
.tools-page .sec-head h1{font-size:clamp(34px,5.4vw,84px);line-height:1.05;letter-spacing:-.03em}
/* 简介原来挤在标题正下方 476px 宽的窄栏里，14px，右边 850px 全空。
   挪到右栏、贴底、上面加一条细线——像杂志目录页右边那段导语。 */
.tools-page .sec-head p{
  max-width:30em;
  font-size:clamp(13.5px,1.05vw,15.5px);line-height:1.95;
  padding-top:clamp(12px,2vh,18px);
  border-top:1px solid var(--rule-d);
}

/* ---------- 详情页：剧照墙 + 外链按钮 ---------- */
.dt-shot-in img{width:100%;height:100%;object-fit:cover;display:block}
/* 带子的 margin-top 归零：它现在挂在 .dt-credits 里，跟左边那几行字底对齐，
   再挂一个 margin-top 就会被 flex 的 align-items:flex-end 顶上去，对不齐。 */
.dt-cta{
  display:inline-flex;align-items:center;gap:8px;
  padding:11px 20px;border-radius:999px;
  background:var(--gold);color:#141210;
  font-size:13px;font-weight:500;letter-spacing:.02em;
  white-space:nowrap;
  transition:background .3s,transform .3s,color .3s;
}
.dt-cta span{font-size:12px;opacity:.7}
.dt-cta:hover{background:#FAFAF7;transform:translateY(-1px)}

.dt-gallery{
  margin-top:clamp(38px,6vh,72px);
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(10px,1.3vw,18px);
  /* dense：通栏那张（跨两列）如果正好落在右半格，默认会被整体推到下一行，
     在它前面留一个 660×330 的空洞。dense 让后面的半栏图回头把这个洞补上。
     代价是视觉顺序可能和 DOM 差一位——画廊里的图没有编号，看不出差别。 */
  grid-auto-flow:row dense;
}
.dt-gallery figure{margin:0;overflow:hidden;background:#141813;aspect-ratio:2/1}
.dt-gallery img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .9s var(--ease)}
.dt-gallery figure:hover img{transform:scale(1.04)}

/* 通栏：只有源图 ≥1200px 的横向剧照才配。九部里只有两张够格。 */
.dt-gallery figure.is-wide{grid-column:1/-1}
/* 竖版（两张抖音封面）：保持原比例，压到 360px 居中。
   这两张自带大字标题，裁成 2:1 会把标题和脸一起切掉。 */
.dt-gallery figure.is-tall{grid-column:1/-1;justify-self:center;width:min(360px,64%)}
.dt-body{max-width:1600px}

/* ============================================================
   简历页
   ============================================================ */
.rs-head{
  display:grid;grid-template-columns:minmax(0,.72fr) minmax(0,1.35fr);
  gap:clamp(24px,5vw,80px);align-items:start;
  padding-bottom:clamp(32px,5vh,56px);
  border-bottom:1px solid var(--rule-d);
}
.rs-name h1{
  font-weight:600;font-size:clamp(34px,5vw,76px);
  line-height:1.08;letter-spacing:-.025em;
}
.rs-meta{
  margin-top:clamp(18px,2.6vh,28px);
  display:grid;grid-template-columns:1fr 1fr;gap:14px 20px;
}
.rs-meta div{border-top:1px solid var(--rule-d);padding-top:9px}
.rs-meta dt{font-size:10.5px;letter-spacing:.1em;color:var(--ink-d3);margin-bottom:3px}
.rs-meta dd{font-size:13px;color:var(--ink-d);line-height:1.45;word-break:break-all}

.rs-summary{list-style:none;display:grid;gap:clamp(12px,1.8vh,18px)}
.rs-summary li{
  position:relative;padding-left:18px;
  font-size:clamp(13.5px,1.02vw,15px);line-height:1.95;color:#3A3931;
}
.rs-summary li::before{
  content:"";position:absolute;left:0;top:.72em;
  width:6px;height:6px;border-radius:50%;background:var(--gold);
}

/* ---- 区块 ---- */
.rs-block{
  padding-top:clamp(40px,6.5vh,80px);
}
.rs-block>h2{
  font-weight:600;font-size:clamp(17px,1.5vw,21px);
  letter-spacing:-.005em;margin-bottom:clamp(20px,3vh,32px);
  display:flex;align-items:center;gap:14px;
}
.rs-block>h2::after{
  content:"";flex:1;height:1px;background:var(--rule-d);
}

/* ---- 工作经历：左侧切换，右侧详情（纯 CSS radio tab） ---- */
.exp{
  position:relative;
  display:grid;grid-template-columns:minmax(0,.66fr) minmax(0,1.6fr);
  gap:clamp(20px,4vw,64px);align-items:start;
}
.exp>input{position:absolute;opacity:0;pointer-events:none;width:0;height:0}
/* 左栏钉住：右边那栏动辄六七百像素高，读到一半想换一段经历，
   标签已经滚没了。sticky 让它跟着停在顶栏下面，随时能点。
   top 要避开 fixed 顶栏（64px）+ 一点呼吸。 */
.exp-list{
  display:grid;gap:2px;
  border-left:1px solid var(--rule-d);padding-left:0;
  position:sticky;top:clamp(80px,10vh,100px);
  align-self:start;
}
.exp-list label{
  display:grid;grid-template-columns:auto 1fr;gap:11px;align-items:start;
  padding:13px 14px;cursor:pointer;
  border-left:2px solid transparent;margin-left:-1px;
  transition:background .28s,border-color .28s;
}
.exp-list label:hover{background:rgba(0,0,0,.028)}
.exp-list .mark{
  width:26px;height:26px;border-radius:7px;flex:0 0 auto;
  display:grid;place-items:center;
  background:#E7E5DD;color:#6B675C;
  font-style:normal;font-size:12.5px;font-weight:600;
  transition:background .28s,color .28s;
}
.exp-list b{display:block;font-weight:500;font-size:13.5px;line-height:1.4}
.exp-list em{display:block;font-style:normal;font-size:11.5px;color:var(--ink-d3);margin-top:2px}
.exp-list u{
  grid-column:2;text-decoration:none;
  font-size:11px;color:var(--ink-d3);letter-spacing:.03em;
  font-variant-numeric:tabular-nums;margin-top:3px;
}

/* 选中态 */
#exp-0:checked~.exp-list label[for="exp-0"],
#exp-1:checked~.exp-list label[for="exp-1"],
#exp-2:checked~.exp-list label[for="exp-2"],
#exp-3:checked~.exp-list label[for="exp-3"]{
  background:rgba(0,0,0,.04);border-left-color:var(--ink-d);
}
#exp-0:checked~.exp-list label[for="exp-0"] .mark,
#exp-1:checked~.exp-list label[for="exp-1"] .mark,
#exp-2:checked~.exp-list label[for="exp-2"] .mark,
#exp-3:checked~.exp-list label[for="exp-3"] .mark{
  background:var(--ink-d);color:var(--paper);
}

/* ---------- 公司图标徽章 ----------
   26px 的字母方块换成真 logo。字母（拍/智/森）只是没有图时的替身，
   有 logo 就用 logo——一眼认出公司，比认一个自己起的简称有用。

   为什么徽章放大到 30px：绿联和智云的标都是「色块里带字」，
   26px 下那两个字只剩 4px 高，糊成一团；30px 能勉强看出是字。
   白底 + 1px 内描边是因为森鑫那个 X 是白底的，不描边会浮在米白页面上。

   选中态不再动徽章：字母块那种「整块反色」对图没用（图是满铺的，反色看不见），
   而加一圈描边又太像边框——一行「背景变浅 + 左侧一道深色竖线」已经说得很清楚了，
   再给图标套个框只是多一层噪音。 */
.exp-list .mark.is-logo{
  width:30px;height:30px;padding:0;
  background:#fff;overflow:hidden;
  box-shadow:inset 0 0 0 1px rgba(20,18,16,.12);
}
.exp-list .mark.is-logo img{width:100%;height:100%;object-fit:cover;display:block}

.exp-panes{min-width:0}
.exp-pane{display:none}
#exp-0:checked~.exp-panes .exp-pane:nth-child(1),
#exp-1:checked~.exp-panes .exp-pane:nth-child(2),
#exp-2:checked~.exp-panes .exp-pane:nth-child(3),
#exp-3:checked~.exp-panes .exp-pane:nth-child(4){display:block}
/* 切换时不要整块一起淡进来——那块内容一次出现，视线没有落点。
   标题、副行、正文分三拍依次落位，慢半拍的那两拍反而让人觉得「翻页了」。
   display:none → block 会重启动画，所以每次点标签都会重新播。 */
.exp-pane>*{animation:paneIn .52s var(--ease) both}
.exp-pane>*:nth-child(2){animation-delay:.05s}
.exp-pane>*:nth-child(3){animation-delay:.11s}
@keyframes paneIn{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}

.exp-pane h3{font-weight:600;font-size:clamp(18px,1.6vw,24px);line-height:1.3;letter-spacing:-.01em}
.exp-pane-meta{margin-top:6px;font-size:12px;color:var(--ink-d3);letter-spacing:.02em}

/* 内容还没给的经历，先占位。
   不能留白：右边那一栏空着，看上去跟渲染失败没区别。
   也不能做成「敬请期待」——那是一句营销话。就老老实实说整理中。 */
.exp-todo{
  margin-top:clamp(18px,2.6vh,26px);
  font-size:13px;line-height:1.7;color:var(--ink-d3);
  letter-spacing:.02em;
}

/* ---- 打勾列表 ---- */
.ticks{list-style:none;margin-top:clamp(18px,2.6vh,26px);display:grid;gap:clamp(12px,1.8vh,17px)}
.ticks li{
  display:grid;grid-template-columns:1fr auto;gap:14px;align-items:start;
  padding-left:22px;position:relative;
}
.ticks li::before{
  content:"";position:absolute;left:2px;top:.52em;
  width:9px;height:5px;
  /* 金色 #C8B48A 当装饰很好，当「勾」太淡——1.6px 的浅金在米白上几乎看不见，
     这条列表就丢了列表感。勾换成深一档的金，线也加粗到 2px。 */
  border-left:2px solid #9C8455;border-bottom:2px solid #9C8455;
  transform:rotate(-45deg);
}
.ticks b{display:block;font-weight:500;font-size:13.5px;line-height:1.5;margin-bottom:3px}
.ticks p{font-size:13px;line-height:1.85;color:#4A4941}
.kpi{
  font-size:11.5px;color:var(--ink-d);white-space:nowrap;
  background:#E9E6DB;border-radius:999px;padding:4px 11px;
  box-shadow:inset 0 0 0 1px rgba(20,20,18,.045);
  font-variant-numeric:tabular-nums;align-self:start;
  letter-spacing:.01em;
}

/* ---- 项目经历 ---- */
.proj-list{display:grid;gap:clamp(26px,4vh,44px)}
.proj{
  display:grid;grid-template-columns:minmax(0,.62fr) minmax(0,1.65fr);
  gap:clamp(20px,4vw,64px);align-items:start;
}
.proj-head h3{font-weight:500;font-size:clamp(14.5px,1.2vw,17px);line-height:1.45}
.proj-meta{margin-top:5px;font-size:11.5px;color:var(--ink-d3);line-height:1.6}
.proj .ticks{margin-top:0}

/* ---- 教育 ---- */
.edu-row{
  display:grid;grid-template-columns:1fr auto;gap:20px;align-items:baseline;
  border-top:1px solid var(--rule-d);padding-top:16px;
}
.edu-row b{font-weight:500;font-size:clamp(15px,1.3vw,18px)}
.edu-row span{display:block;font-size:13px;color:var(--ink-d2);margin-top:4px}
.edu-row u{text-decoration:none;font-size:12px;color:var(--ink-d3);font-variant-numeric:tabular-nums}

/* ---- 荣誉 ---- */
.award-list{list-style:none;display:grid;gap:0}
.award-list li{
  display:grid;grid-template-columns:minmax(0,.28fr) minmax(0,1.72fr);
  gap:clamp(16px,3vw,48px);
  border-top:1px solid var(--rule-d);padding:15px 0;
}
.award-list b{font-weight:500;font-size:13px;color:var(--ink-d)}
.award-list p{font-size:13px;line-height:1.85;color:#4A4941}

/* ---- 技能 ---- */
.skill-list{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0}
.skill-list div{border-top:1px solid var(--rule-d);padding:14px 20px 14px 0}
.skill-list dt{font-size:10.5px;letter-spacing:.1em;color:var(--ink-d3);margin-bottom:5px}
.skill-list dd{font-size:13px;line-height:1.6;color:var(--ink-d)}

/* ============================================================
   软件页
   左图右文。图区是「一张主图 + 一排细节图」，主图给整体印象，
   细节图带说明，负责讲清楚界面里到底有什么。
   ============================================================ */
.tool-list{display:grid;gap:clamp(54px,8.6vh,108px)}
.tool{
  display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);
  gap:clamp(14px,2vh,22px) clamp(24px,3.6vw,58px);
  align-items:start;
  padding-top:clamp(20px,3vh,34px);
  border-top:1px solid var(--rule-d);
}
/* 图永远占宽的那一栏；偶数条左右对调，长页面才有呼吸的节奏。
   用显式定位而不是 order：细节图横跨整行，order 会把它挤到图上面去。 */
.tool-visual{grid-column:1;grid-row:1;min-width:0}
.tool-info{grid-column:2;grid-row:1;min-width:0}
.tool.is-flip .tool-visual{grid-column:2}
.tool.is-flip .tool-info{grid-column:1}

.tool-main{
  display:block;overflow:hidden;background:#EDEBE3;
  aspect-ratio:16/10;
  border:1px solid var(--rule-d);border-radius:3px;
}
.tool-main img{
  width:100%;height:100%;object-fit:cover;object-position:top center;
  display:block;transition:transform .9s var(--ease);
}
.tool-main:hover img{transform:scale(1.02)}

/* 细节图：横跨整行，每张固定占三分之一宽。张数少的条目右端留白，
   比把它拉满更像「就这些」，也比塞进图区下面那窄条里看得清 */
.tool-shots{
  grid-column:1/-1;grid-row:2;
  display:flex;flex-wrap:wrap;gap:clamp(10px,1.3vw,18px);
}
.tool-shots figure{margin:0;min-width:0;flex:1 1 26%;max-width:50%}
.tool-shots img{
  width:100%;aspect-ratio:4/3;object-fit:cover;object-position:top center;
  display:block;background:#EDEBE3;
  border:1px solid var(--rule-d);border-radius:2px;
  transition:transform .7s var(--ease),border-color .3s;
}
/* 超宽的整条截图（比如转录列表）裁两边会把句子切断，改成完整放进浅色衬底 */
.tool-shots figure.is-fit img{object-fit:contain;background:#F1EFE8}
.tool-shots figure:hover img{transform:translateY(-2px);border-color:#CFCCC0}
.tool-shots figcaption{
  margin-top:8px;font-size:11px;line-height:1.65;color:var(--ink-d3);
  text-wrap:pretty;
}

.tool-info{min-width:0}
.tool-meta{
  display:flex;align-items:baseline;gap:10px;
  font-size:10.5px;letter-spacing:.13em;color:var(--ink-d3);
}
.tool-no{color:var(--gold);font-variant-numeric:tabular-nums;letter-spacing:.06em}
.tool-info h2{
  margin-top:10px;display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
  font-weight:600;font-size:clamp(22px,2.1vw,30px);line-height:1.2;letter-spacing:-.02em;
}
.tool-en{
  font-size:10.5px;font-weight:400;letter-spacing:.12em;
  color:var(--ink-d3);text-transform:uppercase;
}
/* 定位句比正文大一档：先让人知道这是什么，再读细节 */
.tool-lead{
  margin-top:clamp(12px,1.8vh,18px);
  font-size:clamp(14px,1.14vw,16.5px);line-height:1.62;
  font-weight:500;color:var(--ink-d);letter-spacing:-.004em;text-wrap:pretty;
}
.tool-desc{
  margin-top:10px;font-size:clamp(12.8px,.95vw,14px);line-height:1.9;
  color:#4A4941;text-wrap:pretty;
}
/* 要点：细横线分隔，标题加粗但不换行，保持一条一条读下去 */
.tool-points{
  list-style:none;margin-top:clamp(16px,2.4vh,24px);
  border-top:1px solid var(--rule-d);
  padding-top:clamp(12px,1.8vh,16px);
  display:grid;gap:clamp(8px,1.3vh,12px);
}
.tool-points li{
  position:relative;padding-left:15px;
  font-size:clamp(12.2px,.92vw,13.4px);line-height:1.74;color:#55544B;
}
.tool-points li::before{
  content:"";position:absolute;left:1px;top:.68em;
  width:5px;height:1px;background:var(--gold);
}
.tool-points b{font-weight:600;color:var(--ink-d);margin-right:.45em}

.tool-foot{
  margin-top:clamp(16px,2.4vh,24px);
  border-top:1px solid var(--rule-d);
  padding-top:clamp(11px,1.6vh,15px);
  display:flex;align-items:baseline;justify-content:space-between;
  gap:14px;flex-wrap:wrap;
}
.tool-stack{font-size:11px;color:var(--ink-d3);letter-spacing:.04em}
.tool-link{
  display:inline-flex;align-items:center;gap:6px;
  font-size:12.5px;color:var(--ink-d);
  border-bottom:1px solid var(--ink-d);padding-bottom:2px;
  transition:color .25s,border-color .25s,gap .25s;
}
.tool-link span{font-size:11px;opacity:.6}
.tool-link:hover{color:var(--accent);border-color:var(--accent);gap:9px}

/* ---- 两个 Windows 小工具：并排的小卡，不占整屏 ---- */
.tool-mini{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(14px,2vw,28px);
  margin-top:clamp(40px,7vh,78px);
  padding-top:clamp(26px,4vh,44px);
  border-top:1px solid var(--rule-d);
}
.mini{
  display:flex;flex-direction:column;
  padding:clamp(18px,2.2vw,30px);
  border:1px solid var(--rule-d);border-radius:3px;background:#FBFAF6;
}
.mini-top{display:flex;align-items:center;gap:14px}
.mini-icon{
  width:clamp(38px,3.4vw,46px);height:clamp(38px,3.4vw,46px);
  object-fit:contain;border-radius:10px;
  filter:drop-shadow(0 7px 16px rgba(40,36,26,.18));
}
.mini-head{flex:1;display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.mini-head h2{font-weight:600;font-size:clamp(17px,1.5vw,21px);letter-spacing:-.015em}
.mini-no{font-size:10.5px;letter-spacing:.13em;color:var(--gold);font-variant-numeric:tabular-nums}
.mini-desc{
  margin-top:14px;font-size:12.8px;line-height:1.86;color:#4A4941;text-wrap:pretty;
}
.mini-tags{list-style:none;margin-top:14px;display:flex;flex-wrap:wrap;gap:6px}
.mini-tags li{
  font-size:11px;line-height:1;padding:6px 9px;
  border:1px solid var(--rule-d);border-radius:2px;
  color:var(--ink-d2);background:#fff;
}
/* 小卡高度不等，把页脚压到底，两栏的链接才对得齐 */
.mini .tool-foot{margin-top:auto;padding-top:clamp(14px,2vh,20px);border-top:0}

.tool-note{
  margin-top:clamp(30px,5vh,54px);
  font-size:11.5px;line-height:1.8;color:var(--ink-d3);
}

/* 两栏撑不住了就上下排：1024 以下图占满宽，细节图反而更大更好看 */
@media (max-width:1024px){
  .tool{grid-template-columns:1fr;gap:clamp(20px,3vh,30px)}
  .tool-visual,.tool-info,
  .tool.is-flip .tool-visual,.tool.is-flip .tool-info{grid-column:1}
  .tool-visual{grid-row:1}
  .tool-info{grid-row:2}
  .tool-shots{grid-row:3}
  .tool-mini{grid-template-columns:1fr}
  .mini-desc{max-width:56ch}
}
/* 手机端细节图改成横滑：一屏三张会缩成色块，滑过去一张一张看更清楚 */
@media (max-width:640px){
  .tool-main{aspect-ratio:4/3}
  .tool-shots{
    display:flex;flex-wrap:nowrap;gap:10px;
    overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    margin-right:calc(var(--m) * -1);padding-right:var(--m);
  }
  .tool-shots::-webkit-scrollbar{display:none}
  .tool-shots figure{flex:0 0 74%;max-width:none;scroll-snap-align:start}
  .tool-shots figcaption{font-size:10.5px;margin-top:6px}
  .tool-info h2{gap:9px}
  .tool-lead{font-size:15px}
  .tool-foot{gap:10px}
}

/* ============================================================
   联系页
   ============================================================ */
.ct-grid{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(14px,2.2vw,30px);
}
.ct-main{
  display:flex;flex-direction:column;justify-content:center;text-align:left;
  min-height:clamp(146px,19vh,180px);
  padding:clamp(22px,3.4vh,34px) clamp(20px,2.4vw,32px);
  border:1px solid var(--rule-d);border-radius:3px;
  background:transparent;cursor:pointer;
  font-family:inherit;color:inherit;
  transition:border-color .3s,background .3s,transform .3s,box-shadow .3s;
}
.ct-main:hover{border-color:var(--ink-d);background:#FBFAF6;transform:translateY(-3px);box-shadow:0 18px 40px -30px rgba(20,20,18,.5)}
.ct-k{display:block;font-size:10.5px;letter-spacing:.14em;color:var(--ink-d3);margin-bottom:clamp(12px,2vh,18px)}
.ct-main b{
  display:block;font-weight:500;
  font-size:clamp(15px,1.42vw,20px);line-height:1.35;
  letter-spacing:-.005em;word-break:break-all;
}
.ct-main i{display:block;margin-top:clamp(10px,1.6vh,14px);font-style:normal;font-size:11.5px;color:var(--ink-d3);letter-spacing:.02em}
.ct-copy .ct-hint{transition:color .25s}
.ct-copy.copied{border-color:var(--gold);background:#FBF7EE}
.ct-copy.copied .ct-hint{color:#8A7448}
/* 复制失败。给个能看出来的红，但不用正红——那是报错的颜色，
   这里只是「没成，你手动选一下」，不是出事了。 */
.ct-copy.failed{border-color:#B4553F;background:#FDF6F3}
.ct-copy.failed .ct-hint{color:#B4553F}

/* 这一行原来放三条外链（GitHub / 新片场 / 抖音），三列网格，每格里
   「名字」靠左、「账号」靠右。后来新片场和抖音那两个地址是错的，删了，
   只剩 GitHub 一条——三列网格配一条内容，右边会空掉三分之二；
   改成两端对齐又会把「soberbw-hash」推到一千多像素之外，像排版坏了。

   所以改成普通的行内排列：名字和账号挨着，左对齐。以后再加链接，
   横向排开就行，不用回来改列数。 */
.ct-links{
  display:flex;flex-wrap:wrap;
  gap:10px clamp(28px,4.6vw,64px);
  margin-top:clamp(28px,4.4vh,52px);
  border-top:1px solid var(--rule-d);padding-top:clamp(20px,3vh,30px);
}
.ct-links a{
  display:flex;align-items:baseline;gap:10px;
  font-size:14px;padding:4px 0;transition:color .25s,gap .25s;
}
.ct-links a:hover{gap:14px}
.ct-links a span{font-size:11.5px;color:var(--ink-d3)}
.ct-links a:hover{color:var(--accent)}

/* ============================================================
   响应式
   ============================================================ */
@media (max-width:960px){
  .rs-head{grid-template-columns:1fr;gap:clamp(22px,4vh,34px)}
  .exp,.proj{grid-template-columns:1fr;gap:clamp(20px,3.4vh,30px)}
  .exp-list{grid-auto-flow:row;border-left:0;border-top:1px solid var(--rule-d)}
  .exp-list label{border-left:0;border-top:2px solid transparent;margin-left:0;padding:12px 4px}
  #exp-0:checked~.exp-list label[for="exp-0"],
  #exp-1:checked~.exp-list label[for="exp-1"],
  #exp-2:checked~.exp-list label[for="exp-2"],
  #exp-3:checked~.exp-list label[for="exp-3"]{border-left-color:transparent;border-top-color:var(--ink-d)}
  .skill-list{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ct-grid{grid-template-columns:1fr;gap:12px}
  .tools-page .sec-head{grid-template-columns:1fr;gap:clamp(14px,3vw,22px)}
  .dt-gallery{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:560px){
  .rs-meta{grid-template-columns:1fr 1fr}
  .award-list li{grid-template-columns:1fr;gap:5px}
  .skill-list{grid-template-columns:1fr}
  .ticks li{grid-template-columns:1fr;gap:6px}
  .kpi{justify-self:start}
  .dt-gallery{grid-template-columns:1fr}
}

/* ---------- 抽牌：带一点回弹，才像被抽出来 ----------
   这里原来挂着 `transition:transform .58s cubic-bezier(.34,1.5,.56,1)`。
   但卡片的位置是脚本每帧用 transform 写的——于是每一帧的新值都会重新起一次
   0.58 秒的弹性动画，卡片永远追不上目标位置，整条带子看起来「软、糊、掉帧」。
   （同时 `.sc-card:hover{transform:...}` 也是死的：行内样式优先级更高。）

   现在 transform 完全交给脚本：抬升、旋转、放大都在循环里按一个缓动的 hover
   进度算（见 app.js 的 hp）。CSS 这边只保留不会和它打架的属性。 */
.sc-card{
  transition:box-shadow .45s var(--ease),filter .4s ease;
}
.sc-card .art img{transition:transform .7s var(--ease)}
.sc-card:hover .art img{transform:scale(1.06)}
.sc-card .cap{transition:transform .45s var(--ease)}
.sc-card:hover .cap{transform:translateY(-3px)}

/* ---------- 联系页：内容少，整页垂直居中，别把 footer 甩到天边 ---------- */
body.inner .contact-page{
  display:flex;flex-direction:column;justify-content:center;
  padding-top:clamp(88px,13vh,132px);
  padding-bottom:clamp(56px,9vh,110px);
  min-height:100vh;min-height:100svh;
}
body.inner .contact-page .sec-head{margin-bottom:clamp(28px,4.4vh,48px)}
.ct-links a{justify-content:flex-start;gap:10px}
.ct-links a span{color:var(--ink-d3)}
.ct-links a:hover span{color:var(--ink-d2)}

/* 剧照墙的尺寸规则统一在上面的 `.dt-gallery` 那一段里，这里不再重复。
   原来这一块挂着一条 `figure:last-child:nth-child(odd){grid-column:span 2}`
   （外加 960px 断点里同样的一条），意图是「张数是奇数时最后一张跨两列收尾，
   不留豁口」。听着合理，实际是拿画质换的：它不看源图多大，《女书回响》的
   714px 剧照被拉到 1330px，放大 1.86 倍，糊得很明显。
   现在通栏权限改由构建脚本按真实像素宽度发（见 build-site.mjs 的 gallery），
   奇数张的尾巴就让它空着——一页读到末尾，右下角空一格是正常的，
   比放一张糊图强。 */

/* ============================================================
   滚动浏览页（browse.html）
   作品 / 简历 / 软件 / 联系四屏拼成一条竖着滚的流。
   为什么要一个文档：跨页做不了无缝——滚到底再触发跳转，中间必然白屏一次。
   所以四屏必须在同一个文档里，靠 scroll-snap 吸附，靠 --vis 驱动进入动画。
   ============================================================ */

/* 吸附放在 html 上（滚动容器就是它）。用 proximity 不用 mandatory：
   简历那一屏内容比视口高，mandatory 会把人死死按在屏顶，滚不到中间。 */
html.snap{
  scroll-snap-type:y proximity;
  /* 这里必须是 0，不能再让开一次顶栏。
     每屏自己有一段 padding-top（见 .bz-pane），那一段的作用就是让开 fixed 顶栏；
     如果这里再写 64–84px，两者会相加：从导航点进「简历」时，标题会被推到
     离顶栏 189px 的地方，而直接滚到同一屏时只有 108px——同一个位置两种间距。
     让 padding-top 一个人负责，两条路径就对齐了。 */
  scroll-padding-top:0;
}

.bz{position:relative}

.bz-pane{
  position:relative;
  min-height:100svh;
  display:flex;
  flex-direction:column;
  /* safe center：内容不满一屏时居中（联系页就不会顶在上面留一大片空），
     内容超过一屏时自动退化成顶对齐（作品 / 简历 / 软件三屏都超），
     上下内边距不会被居中吃掉。不支持 safe 的浏览器走下一行的 center，
     因为 section 高度总会被内容撑开，center 也不会溢出。 */
  justify-content:center;
  justify-content:safe center;
  padding-top:clamp(84px,12vh,136px);
  padding-bottom:clamp(48px,8vh,100px);
  scroll-snap-align:start;
  /* 进入视口的过程中浮现。透明度不从 0 起：这些屏动辄两三千像素高，
     从全透明开始的话，屏顶刚进视口那一段会是一片空底色，看着像卡住了。
     留 35% 起步就没有这个断层。
     位移不放在这里，见下面 .bz-pane>.wrap —— 原因也写在那儿。
     --vis 这条只在浏览器不支持 scroll timeline 时才起作用（由 scroll.js 写）；
     支持的时候下面 @supports 里的动画会盖掉它，而且动画跑在合成线程上。 */
  opacity:calc(.35 + var(--vis,1) * .65);
}

/* 老浏览器不认 safe 时的兜底：内容超一屏就顶对齐（由 scroll.js 打上这个类） */

/* 内层容器原来的上下内边距由 section 接管，不然两处相加会顶出一大截空白。
   width:100% 不能省：.bz-pane 是 flex 容器，.wrap 带着 margin:0 auto 就成了
   flex 子项，auto 外边距会把它的宽度退化成 shrink-to-fit——内容长的屏（作品、
   简历、软件）刚好被撑满看不出来，内容短的联系屏只有 1052px，整块内容比别的屏
   右移 194px，滚过去就是一次横向错位。

   位移为什么放在 wrap 上而不是 .bz-pane 上：scroll-snap 的吸附区是按元素
   「带变换后的边框盒」算的。位移写在 section 上，section 的吸附点就跟着
   --vis 一起飘，最多差 34px——于是点导航和手动滚到同一屏会落在两个不同的
   位置（实测 2418 和 2452 都出现过），标题离顶栏的距离忽远忽近。
   放在 wrap 上，section 的盒子纹丝不动，吸附点和 goTo() 算出来的目标就都准了。 */
.bz-pane>.wrap{
  width:100%;padding-top:0;padding-bottom:0;
  transform:translate3d(0,calc((1 - var(--vis,1)) * 34px),0);
}

/* 每屏一层极淡的暖光。四屏都是米白，靠色块区分会很脏；
   靠这种几乎看不见的光感反而连得上。
   位置原来是跟着 --vis 往上飘一点点的，现在固定了——那 14% 的位移肉眼看不出，
   却让这个伪元素每帧都要重算一次渐变。 */
.bz-pane::before{
  content:"";
  position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(58% 46% at 50% 26%,
    rgba(200,180,138,.11),rgba(200,180,138,0) 72%);
}

/* ---------- 进入动画交给合成器 ----------
   以前是 scroll.js 每帧往 section 上写 --vis，透明度跟着变。问题是写自定义属性
   会作废整棵子树的样式，而这几屏一屏就有几百个节点；再加上读几何和写样式交替，
   每帧都被迫同步布局一次。实测滚动 120 帧要重算 158 次样式、122 次布局，
   样式重算独占 0.66s（主线程总耗时 0.94s）。

   换成 CSS 的滚动驱动动画之后，opacity 和 transform 由合成线程按滚动进度直接插值，
   主线程一帧都不用醒。animation-range 的 entry 0%→55% 对应「元素顶边从视口底部
   升到视口 45% 处」，跟原来 --vis 的区间一致。

   为什么动画挂在 .wrap 上而不是 .bz-pane 上：scroll-snap 的吸附区按元素
   「带变换后的边框盒」算，section 一旦有 transform，吸附点就会跟着动画飘
   （点导航和手动滚到同一屏会落在两个位置，实测差 34px）。 */
@supports (animation-timeline: view()) {
  .bz-pane>.wrap{
    animation:pane-enter linear both;
    animation-timeline:view();
    animation-range:entry 0% entry 55%;
  }
  @keyframes pane-enter{
    from{opacity:.35;transform:translate3d(0,34px,0)}
    to{opacity:1;transform:translate3d(0,0,0)}
  }
}

/* 联系页原来在单独页面里靠 justify-content:center + min-height:100svh 撑满一屏，
   在滚动流里会把内容顶到屏幕下半截。这里连同外层那条更高优先级的规则一起覆盖。
   （body.inner .contact-page 是 (0,2,1)，所以这里必须写到 (0,3,1) 才压得住。） */
body.inner .bz-pane .contact-page{
  display:block;
  min-height:0;
  justify-content:initial;
  padding-top:0;
  padding-bottom:0;
}

/* 屏与屏之间一条极淡的分隔，给「换章节了」一个视觉信号。
   原来透明度跟着 --vis 变；现在固定成 .55 —— 一条本来就极淡的线，
   再让它渐显一遍，收益远不如省下的那次样式重算。 */
.bz-pane + .bz-pane::after{
  content:"";
  position:absolute;left:var(--m);right:var(--m);top:0;height:1px;
  background:linear-gradient(90deg,transparent,var(--rule-d) 12%,var(--rule-d) 88%,transparent);
  opacity:.55;
  pointer-events:none;
}

/* ---------- 右侧进度：四个点，当前那个亮起来 ----------
   点贴到屏幕最边上，标签平时收起来、鼠标移上去才出来——
   常显会压在作品图和简历的右栏上，很碍事。
   整组外面套一层「只在移上去时才出现」的磨砂胶囊：平时它只是四个小点，
   鼠标进来才知道这里能点。原来 5px、34% 不透明度，在米白底上就是几个脏点，
   既不像控件也不像装饰。

   标签必须绝对定位。它原来只是 opacity:0，仍占着文档流里的宽度——四个标签里
   最宽的那个（「01 作品」约 48px）把整列点往左顶，点就落在了正文右缘里面，
   在联系屏上正好压在第三张卡片边上。脱离文档流之后，这一列只有点本身那么宽。 */
.bz-dots{
  position:fixed;right:clamp(6px,1vw,16px);top:50%;
  transform:translateY(-50%);
  z-index:90;
  display:flex;flex-direction:column;gap:0;
  padding:9px 6px;
  border-radius:999px;
  transition:background .45s var(--ease),box-shadow .45s var(--ease);
}
.bz-dots:hover{
  background:rgba(248,247,243,.82);
  box-shadow:0 10px 34px -22px rgba(20,20,18,.55),0 0 0 1px rgba(220,218,210,.7);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
}
.bz-dots a{
  position:relative;
  display:flex;align-items:center;justify-content:flex-end;
  padding:7px 6px;
  font-size:10.5px;letter-spacing:.14em;color:var(--ink-d2);
  transition:color .35s var(--ease);
}
.bz-dots a i{
  flex:0 0 auto;
  width:6px;height:6px;border-radius:50%;
  background:currentColor;opacity:.42;
  transition:opacity .4s var(--ease),transform .4s var(--ease),background .4s var(--ease),box-shadow .4s var(--ease);
}
/* 标签从点的左边滑出来。自带一小片米白底，滑到正文上也还读得清。 */
.bz-dots a span{
  position:absolute;right:calc(100% + 4px);top:50%;
  padding:3px 9px;border-radius:999px;
  background:rgba(248,247,243,.92);
  box-shadow:0 4px 16px -10px rgba(20,20,18,.45);
  white-space:nowrap;pointer-events:none;
  opacity:0;transform:translate(7px,-50%);
  transition:opacity .32s var(--ease),transform .32s var(--ease);
}
.bz-dots a:hover{color:var(--ink-d)}
.bz-dots a:hover i{opacity:.9}
.bz-dots a:hover span{opacity:1;transform:translate(0,-50%)}
.bz-dots a.on{color:var(--ink-d)}
/* 当前那个：金色实心，外面一圈很淡的金晕。晕用 box-shadow 而不是伪元素，
   它跟着 i 的 scale 一起变，不用另算尺寸。
   晕的扩散半径要小：0 模糊 + 大扩散会变成一块实心色饼，比 6px 的点大四倍，
   看着像贴了张标签。2px 扩散、16% 不透明度，才只是「描了一圈」。 */
.bz-dots a.on i{
  opacity:1;transform:scale(1.35);background:var(--gold);
  box-shadow:0 0 0 2px rgba(200,180,138,.16);
}
.bz-dots:hover a.on i{box-shadow:0 0 0 3px rgba(200,180,138,.2)}

/* 顶栏导航：滚动到哪一屏，对应那一项就亮。
   原来 aria-current 是构建时写死的，滚动页里由脚本改写。 */
body.bz-page .topbar nav a[aria-current]{color:var(--ink-d);border-color:var(--gold)}

/* 窄屏下顶栏原本是 static（跟着页面滚走）——单页时无所谓，反正页面短。
   滚动页里不行：滚到简历屏中段就没有导航了，必须钉住。
   （这条要压过 @media 里那条 .topbar{position:static}，所以带上 body.bz-page。） */
body.bz-page .topbar{
  position:fixed;
  background:rgba(245,244,240,.94);
  -webkit-backdrop-filter:blur(10px) saturate(1.15);
  backdrop-filter:blur(10px) saturate(1.15);
  border-bottom:1px solid var(--rule-d);
}
/* 顶栏底下一条极细的金线，长度就是整页的滚动进度。
   长滚动页最难受的是「不知道还剩多少」，这条线给一个连续的答案。

   用 transform:scaleX() 而不是 width：width 是布局属性，每帧改一次浏览器就要
   重新排一次这一条线所在的盒子；scaleX 是合成器属性，改它不触发布局，
   还能交给合成线程去插值。 */
body.bz-page .topbar::after{
  content:"";
  position:absolute;left:0;bottom:-1px;height:1px;
  width:100%;
  background:var(--gold);
  transform-origin:0 50%;
  transform:scaleX(var(--prog,0));
}
/* 支持滚动时间线的话，连 --prog 都不用写：进度由浏览器按滚动位置直接驱动，
   全程零 JS、零样式重算。 */
@supports (animation-timeline: scroll()) {
  body.bz-page .topbar::after{
    animation:bar-progress linear both;
    animation-timeline:scroll(root block);
  }
  @keyframes bar-progress{
    from{transform:scaleX(0)}
    to{transform:scaleX(1)}
  }
}

/* 窄屏：进度点挪到底部，横着排，别挡正文。
   必须常显一层磨砂底：它是 fixed 的，底下滚过什么都有可能，而手机上的正文是
   满宽的单列——不加底的时候，四个点正好落在图注那一行上，像纸上的脏点。 */
@media (max-width:760px){
  .bz-dots{
    right:auto;left:50%;top:auto;bottom:12px;
    transform:translateX(-50%);
    flex-direction:row;gap:8px;
    padding:7px 13px;
    background:rgba(248,247,243,.86);
    box-shadow:0 8px 26px -18px rgba(20,20,18,.55),0 0 0 1px rgba(220,218,210,.75);
    -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  }
  .bz-dots a span{display:none}
  .bz-dots a{padding:6px 5px}
  .bz-pane{padding-top:clamp(72px,10vh,104px);padding-bottom:clamp(52px,8vh,72px)}
}

/* 关掉动效时不要位移，只保留可见 */
@media (prefers-reduced-motion:reduce){
  html.snap{scroll-snap-type:none}
  .bz-pane{opacity:1}
  /* animation:none 不能省：滚动驱动的入场动画会盖掉下面那条 transform:none，
     不显式关掉的话，「减少动态」在这几屏上等于没生效。 */
  .bz-pane>.wrap{animation:none;opacity:1;transform:none}
  /* 标签页的分拍入场也要停：只留淡入，不然「减少动态」形同虚设 */
  .exp-pane>*{animation-duration:.01s;animation-delay:0s}
  .exp-pane>*{transform:none}
}

/* ============================================================
   页面之间的过渡
   ============================================================ */
/* 跨文档视图过渡：从作品详情返回列表、从首页点进任何一页，浏览器会在两次文档
   之间做一次交叉淡入，而不是「白闪一下 → 新页直接出现」。
   之前那一下白闪是整个站点里最不丝滑的地方——它不在动画里，它在动画之外。

   只用 0.26s：过渡太长会变成「等页面」。慢的那一版里，返回列表要等半秒才看见
   内容，比白闪还难受。 */
@view-transition{navigation:auto}
::view-transition-old(root),
::view-transition-new(root){
  animation-duration:.26s;
  animation-timing-function:cubic-bezier(.2,.7,.2,1);
}
/* 旧页淡出得比新页快一点：两页同权重对拉会有一瞬间的「双影」 */
::view-transition-old(root){animation-duration:.18s}

@media (prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
}

/* ============================================================
   触摸目标：手指点不准 25×24 的东西
   ============================================================ */
/* 这一块只在「触屏」或「窄屏」时生效。
   为什么用 pointer:coarse 而不是只按宽度：iPad 竖屏是 820、横屏 1024，
   一样是手指，宽度断点抓不全。两条并着写，取并集。

   原来顶栏那四个链接实测 25×24（12.5px 的字，上下只有行高），
   拇指按下去十次有三次点空。Apple HIG / Material 的底线都是 44×44。

   为什么不直接把 padding 加到链接上：顶栏的「当前页」标记是一条 border-bottom
   下划线，加 padding-bottom 会把线推到字下面十几像素，看着像脱开了。
   所以下划线改成 ::after 自己定位，点击区随便撑。 */
@media (pointer:coarse), (max-width:900px){
  .topbar .in{align-items:center;padding-top:5px;padding-bottom:5px}
  .topbar nav{gap:0}
  .topbar nav a{
    position:relative;
    padding:14px 8px;
    border-bottom:0;
  }
  .topbar nav a::after{
    content:"";position:absolute;left:8px;right:8px;bottom:8px;height:1px;
    background:transparent;transition:background .2s;
  }
  .topbar nav a:hover::after,
  .topbar nav a[aria-current]::after{background:var(--gold)}
  /* 站名本身也是回首页的链接，一起撑到 44 */
  .mark{display:inline-flex;align-items:center;min-height:44px}

  /* 详情页顶部的「← 全部作品 / 尤博文」，下划线同样挪到 ::after */
  .dt-top a{padding:12px 0;border-bottom:0;position:relative}
  .dt-top a::after{
    content:"";position:absolute;left:0;right:0;bottom:7px;height:1px;
    background:rgba(255,255,255,.16);
  }
  .dt-top a:hover::after{background:var(--gold)}
  .dt-mark{padding-left:8px;padding-right:8px}
  .dt-mark::after{left:8px;right:8px}

  /* 软件页的「GitHub 与下载 / 在线试用」这类外链 */
  .tool-link{min-height:44px;display:inline-flex;align-items:center;justify-content:flex-end}
  /* 联系页 GitHub 那一条 */
  .ct-links a{min-height:44px}

  /* 首页胶囊导航：34px 的胶囊在手指上偏矮，抬到 40。视觉上还是那颗胶囊 */
  .sc-pill{height:40px}
  .sc-links a{padding:12px 6px}
}

/* 页脚的「返回首页」实测 48×17，也是手指点不准的尺寸。
   不能直接加 padding：footer 是 flex，加了下边距整条页脚会被顶高、跟上面的
   分隔线脱开。用一块透明的 ::after 把点击区往上下各撑 13px，版式一点不动。 */
@media (pointer:coarse), (max-width:900px){
  footer a{position:relative;display:inline-block;border-bottom:1px solid var(--rule-d)}
  footer a::after{content:"";position:absolute;left:-6px;right:-6px;top:-13px;bottom:-13px}
}
/* browse 的进度点：手机上一律不显示。
   原来它在窄屏挪到屏底正中，做成一颗浮着的胶囊。问题是它是 fixed 的，
   内容从它底下滚过去——实测在 390 上正好压住卡片字幕「武汉声音，过早了…」，
   把一句话从中间截断。加内边距只会把胶囊撑得更大、压得更多。
   而它本来就是个「跳章节 + 看进度」的控件，手机顶栏那四个链接（作品 / 简历 /
   软件 / 联系）跳的是同样四个位置，功能重复。所以窄屏直接收掉，
   把屏底还给内容。 */
@media (max-width:760px){
  .bz-dots{display:none}
}
