/* ===================================================================
   v2.css — 编辑体版本
   气质参考 belleduffner.com：暖棕墨色而非黑、强字阶落差、
   小号大写标签承担章节身份、sticky 分栏。
   配色与字阶为重新配置，字体用免费同气质替代（原站 Apercu / Exposure
   为商用授权）。
   =================================================================== */

:root {
  /* ---- 暖调基底：墨色是深棕，不是黑 ---- */
  --paper:      #fffefc;
  --paper-warm: #f7f3ec;
  --paper-glass: rgba(255, 254, 252, .72);   /* sticky 顶栏 / 回顶按钮的磨砂底，= --paper @72% */
  --end:        var(--paper-warm);           /* 页底 .v2end 色块 */
  --ink:        #2d1a0a;
  --ink-80:     rgba(45, 26, 10, .8);
  --ink-60:     rgba(45, 26, 10, .6);
  --ink-45:     rgba(45, 26, 10, .45);
  --ink-12:     rgba(45, 26, 10, .12);
  --warm-grey:  #8e847c;

  /* 冷色点缀，与暖棕对冲 */
  --mist:       #c1d5e2;
  --mist-deep:  #8fb2c6;
  /* 章节序号用的暖红 */
  --flag:       #c2452f;

  --f-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --f-ui:      'Manrope', -apple-system, BlinkMacSystemFont, sans-serif;
  --f-body:    'Inter', -apple-system, BlinkMacSystemFont, sans-serif;

  --rail-v2: 268px;
  --col:     1000px;
  --pad:     28px;
  --ease:    cubic-bezier(.22, .61, .36, 1);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; }
p { margin: 0; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 3px; }

.vh {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* 跳到正文 —— 平时推到屏幕外，键盘 Tab 到才滑进来。
   不能直接用 .vh：那个是 clip-path 永久隐藏，没有显形规则，
   焦点落上去屏幕上毫无反应，键盘用户不知道自己在哪。 */
.skip {
  /* 必须 fixed：absolute 是相对文档定位的，页面一滚这个提示丸就跟着
     滚出视口，键盘用户在页面中段 Shift+Tab 回来会什么都看不到。 */
  position: fixed; left: 12px; top: -64px; z-index: 100;
  font-family: var(--f-ui); font-size: 14px; color: var(--ink);
  background: var(--paper);
  padding: 10px 16px; border-radius: 999px;
  box-shadow: 0 6px 20px rgba(45, 26, 10, .16);
  transition: top .18s var(--ease);
}
.skip:focus { top: 12px; }

/* ===================================================================
   字阶 —— 落差是这套语言的核心：
   85px 展示标题 / 24px 章节标题 / 14px 大写标签
   =================================================================== */
.d-title {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(40px, 6.4vw, 85px);
  line-height: 1.02;
  letter-spacing: -.02em;
  color: var(--ink);
  text-wrap: balance;
  margin: 0;
}
.d-title em { font-style: italic; }

.label {
  font-family: var(--f-ui);
  font-size: 14px; font-weight: 400;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-80);
  margin: 0;
}
.label-flag { color: var(--flag); }

/* 章节标题。原来是 Manrope 600 / 24px，与 18px 的 .h-sub 同字族同字重、
   只差 6px，在长案例里两级读起来是平的。改用展示衬线的次级音量：
   字阶仍是五档（85 / 38 / 18 / 17 / 14），靠字族拉开层级而不是靠再加字号。 */
.h-sec {
  font-family: var(--f-display);
  font-size: clamp(27px, 3.2vw, 38px);
  font-weight: 400; line-height: 1.16;
  letter-spacing: -.015em;
  color: var(--ink); margin: 0;
}
.h-sec em { font-style: italic; }

/* 步骤/子项标题：比章节标题小一档，否则两者混在一起像同一层 */
.h-sub {
  font-family: var(--f-ui);
  font-size: 18px; font-weight: 600; line-height: 1.3;
  letter-spacing: -.01em; color: var(--ink); margin: 0 0 8px;
}

.lede {
  font-size: clamp(18px, 2vw, 22px);
  line-height: 1.55; color: var(--ink-80);
}
.lede em { font-style: italic; color: var(--ink); }
.body { font-size: 17px; line-height: 1.62; color: var(--ink-80); }
.body + .body { margin-top: 1.05em; }

/* ===================================================================
   壳层
   =================================================================== */
.wrap { width: 100%; max-width: var(--col); margin-inline: auto; padding-inline: var(--pad); }
.bleed { width: 100%; }

/* ===================================================================
   左侧固定导轨
   =================================================================== */
.v2rail {
  position: fixed; inset: 0 auto 0 0;
  width: var(--rail-v2);
  padding: 32px 24px;
  display: flex; flex-direction: column;
  background: var(--paper);
  z-index: 40;
  overflow-y: auto;
}
.v2rail-id { margin-bottom: 34px; }
.v2rail-id .nm {
  font-family: var(--f-display);
  font-size: 25px; line-height: 1.1; color: var(--ink);
}
.v2rail-id .rl {
  font-family: var(--f-ui);
  font-size: 13px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-45); margin-top: 5px;
}
.v2nav { display: flex; flex-direction: column; gap: 1px; margin-bottom: 30px; }
.v2nav a {
  font-family: var(--f-ui); font-size: 15px;
  color: var(--ink-60); padding: 5px 0;
  width: fit-content;
  border-bottom: 1px solid transparent;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}
.v2nav a:hover, .v2nav a[aria-current="page"] {
  color: var(--ink); border-bottom-color: var(--ink);
}

.v2idx-label {
  font-family: var(--f-ui); font-size: 12px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-45);
  padding-bottom: 12px; margin-bottom: 12px;
  border-top: 1px solid var(--ink-12); padding-top: 22px;
}
.v2idx { display: flex; flex-direction: column; gap: 2px; }
.v2idx a, .v2idx span {
  display: flex; align-items: baseline; gap: 10px;
  font-family: var(--f-ui); font-size: 14.5px;
  color: var(--ink-60); padding: 5px 0;
  transition: color .2s var(--ease);
}
.v2idx a:hover, .v2idx a.is-here { color: var(--ink); }
.v2idx a.is-here .n { color: var(--flag); }
.v2idx .n {
  font-size: 11.5px; color: var(--ink-45);
  font-variant-numeric: tabular-nums; min-width: 16px;
  transition: color .2s var(--ease);
}
.v2idx span { color: var(--ink-45); cursor: default; }

.v2rail-foot {
  margin-top: auto; padding-top: 26px;
  font-family: var(--f-ui); font-size: 13px; color: var(--ink-45);
}
.v2rail-foot a { border-bottom: 1px solid var(--ink-12); }
.v2rail-foot a:hover { color: var(--ink); border-bottom-color: var(--ink); }

.v2main { margin-left: var(--rail-v2); min-width: 0; }

/* ---- 顶部状态条 ---- */
.v2top {
  position: sticky; top: 0; z-index: 30;
  display: flex; justify-content: flex-end; align-items: center; gap: 10px;
  padding: 16px clamp(20px, 3vw, 40px);
  background: var(--paper-glass);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}
.v2pill, .v2btn {
  font-family: var(--f-ui); font-size: 13.5px;
  padding: 8px 17px; border-radius: 100px;
  display: inline-flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
.v2pill { border: 1px solid var(--ink-12); color: var(--ink-80); }
.v2pill::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: #21c46a; box-shadow: 0 0 0 3px rgba(33,196,106,.18);
}
.v2btn {
  background: var(--ink); color: var(--paper); font-weight: 500;
  transition: background .22s var(--ease), box-shadow .22s var(--ease), transform .22s var(--ease);
}
.v2btn:hover {
  background: #1a0f05;
  box-shadow: 0 8px 22px -8px rgba(45, 26, 10, .6);
  transform: translateY(-1px);
}

/* ===================================================================
   Hero
   =================================================================== */
.v2hero { padding-block: clamp(40px, 7vw, 92px) clamp(28px, 4vw, 48px); }
.v2hero .label { margin-bottom: 20px; }
.v2hero .d-title { margin-bottom: 26px; }
.v2hero-media { margin-top: clamp(28px, 4vw, 46px); }
.v2hero-media img, .v2hero-media video { width: 100%; height: auto; }

/* ===================================================================
   指标条 —— 原站在标题下用两三个大数字定调
   =================================================================== */
.v2facts {
  display: grid;
  grid-auto-flow: column;                  /* 几项就几列，一律一行排完 */
  grid-auto-columns: minmax(0, 1fr);
  gap: 28px 26px;
  padding-block: clamp(24px, 3vw, 34px);
}
@media (max-width: 900px) {
  .v2facts { grid-auto-flow: row; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}
.v2facts .label { margin-bottom: 8px; }
/* 只是元信息，不是数据故事 —— 用 UI 字，不上展示衬线，页面只留一个大字号时刻 */
.v2facts dd {
  font-family: var(--f-ui);
  font-size: 16px; line-height: 1.55;
  color: var(--ink);
}
/* 副标说明另起一行：原本是裸 <small>，会渲染成 "12Weeks · Jun – Sep" */
.v2facts dd small { display: block; font-size: 14px; color: var(--ink-60); margin-top: 4px; }

/* ===================================================================
   占位块 —— 结构需要一张图但素材还没有时，写清该放什么
   =================================================================== */
.v2ph {
  display: grid; place-items: center; text-align: center;
  aspect-ratio: 16 / 9;
  border-radius: 10px;
  background: var(--mist);
  color: var(--ink);
  padding: 24px;
}
.v2ph .label { color: var(--ink-60); margin-bottom: 8px; }
.v2ph p { font-family: var(--f-ui); font-size: 16px; font-weight: 600; max-width: 34ch; }
.v2ph.is-wide { aspect-ratio: 21 / 9; }

/* ===================================================================
   章节
   =================================================================== */
.v2sec { padding-block: clamp(56px, 8vw, 104px) 0; }
.v2sec-head { margin-bottom: clamp(22px, 3vw, 34px); }
.v2sec-head .label { margin-bottom: 12px; }

/* 章节分隔 —— 不用通栏细线：它与行内 1px 分隔线同粗同色，读不出层级。
   改成内容左端的一道 48x3 短粗刻度：长度、粗细、颜色都不同。
   盒子仍是 .wrap 宽度以保持居中，用渐变只在左端画出实体部分，
   再用 content-box 裁切令其与正文左边界对齐。 */
.v2rule {
  border: 0; height: 3px;
  background: linear-gradient(to right, var(--ink) 0 48px, transparent 48px);
  background-clip: content-box;
  margin-top: clamp(72px, 10vw, 124px);
  opacity: .85;
}

/* 图 */
.v2fig { margin-block: clamp(24px, 3.4vw, 40px); }
.v2fig img, .v2fig video { width: 100%; height: auto; border-radius: 10px; }
.v2fig.is-flat img { border-radius: 0; }
.v2fig figcaption {
  margin-top: 12px; font-family: var(--f-ui);
  font-size: 13.5px; color: var(--ink-45); line-height: 1.5;
}

/* 满幅图：冲出正文栏，贴到主区两边 */
.v2bleed { margin-block: clamp(36px, 5vw, 68px); }
.v2bleed img, .v2bleed video { width: 100%; height: auto; border-radius: 0; }

/* 图组 */
.v2grid { display: grid; gap: 14px; margin-block: clamp(24px, 3vw, 36px); }
.v2grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.v2grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.v2grid img { width: 100%; height: auto; border-radius: 10px; }

/* 逐张全宽 */
.v2stack { display: flex; flex-direction: column; gap: 16px; margin-block: clamp(24px, 3vw, 36px); }
.v2stack img { width: 100%; height: auto; border-radius: 10px; }

/* 并排的一对图：五五开、两格各自填满。
   名字不能叫 .v2pair —— 那个已经是 Brilliant 配对草图的组件，
   自带 margin-block，套进 .v2stack 会在 16px 的间距上再加 32px。 */
.v2duo { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.v2duo img { width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 10px; }
@media (max-width: 700px) { .v2duo { grid-template-columns: 1fr; } }

/* ===================================================================
   Sticky 分栏 —— 左文右图，图钉住随文滚动
   =================================================================== */
.v2split {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
  margin-block: clamp(28px, 4vw, 48px);
}
.v2split-text > * + * { margin-top: 16px; }
.v2split-media { position: sticky; top: 96px; }
.v2split-media img, .v2split-media video { width: 100%; height: auto; border-radius: 10px; }
.v2split.is-flip .v2split-text { order: 2; }

/* ===================================================================
   编号步骤
   =================================================================== */
.v2step { padding-top: clamp(30px, 4vw, 50px); }
.v2step-n {
  font-family: var(--f-ui); font-size: 14px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--flag); margin-bottom: 10px;
}

/* 引言 */
.v2quote {
  font-family: var(--f-display);
  font-size: clamp(24px, 3.2vw, 38px);
  line-height: 1.24; letter-spacing: -.015em;
  color: var(--ink);
  margin-block: clamp(34px, 5vw, 60px);
}

/* ===================================================================
   项目卡（首页 / 页尾更多作品）
   =================================================================== */
.v2works { display: flex; flex-direction: column; }
.v2work {
  display: grid; grid-template-columns: minmax(0, 1fr) 300px;
  gap: 30px; align-items: center;
  padding-block: 26px;
  border-top: 1px solid var(--ink-12);
  transition: opacity .25s var(--ease);
}
.v2work:last-child { border-bottom: 1px solid var(--ink-12); }
.v2works:hover .v2work { opacity: .45; }
.v2works:hover .v2work:hover { opacity: 1; }
.v2work-t { font-family: var(--f-display); font-size: clamp(24px, 2.8vw, 34px); line-height: 1.14; }
.v2work-m {
  font-family: var(--f-ui); font-size: 13.5px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-45); margin-top: 9px;
}
.v2work-shot { overflow: hidden; border-radius: 8px; aspect-ratio: 16 / 10; background: var(--paper-warm); }
.v2work-shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.v2work:hover .v2work-shot img { transform: scale(1.04); }

/* ===================================================================
   页尾
   =================================================================== */
.v2foot {
  margin-top: clamp(70px, 10vw, 130px);
  border-top: 1px solid var(--ink-12);
  padding-block: 34px 46px;
  font-family: var(--f-ui); font-size: 14px; color: var(--ink-45);
  display: flex; justify-content: space-between; gap: 18px; flex-wrap: wrap;
}
.v2foot a { border-bottom: 1px solid transparent; }
.v2foot a:hover { color: var(--ink); border-bottom-color: var(--ink); }
.v2foot-links { display: flex; gap: 22px; flex-wrap: wrap; }

/* 回到顶部 */
.totop {
  position: fixed; right: 26px; bottom: 26px; z-index: 60;
  width: 44px; height: 44px; display: grid; place-items: center;
  border-radius: 50%; border: 1px solid var(--ink-12);
  background: var(--paper-glass);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  color: var(--ink); cursor: pointer;
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity .28s var(--ease), transform .28s var(--ease),
              visibility .28s var(--ease), background .22s var(--ease), color .22s var(--ease);
}
.totop.is-on { opacity: 1; visibility: visible; transform: none; }
.totop svg { width: 16px; height: 16px; fill: currentColor; }
.totop:hover { background: var(--ink); color: var(--paper); transform: translateY(-3px); }

/* ===================================================================
   入场
   =================================================================== */
.rise { transition: opacity .7s var(--ease), transform .7s var(--ease); }
.js .rise { opacity: 0; transform: translateY(14px); }
.js .rise.seen { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .js .rise { opacity: 1; transform: none; } }

/* ===================================================================
   窄屏
   =================================================================== */
@media (max-width: 1080px) {
  .v2rail {
    position: static; width: auto; inset: auto;
    flex-direction: row; align-items: center; gap: 20px;
    padding: 14px var(--pad); overflow-x: auto;
    border-bottom: 1px solid var(--ink-12);
  }
  .v2rail-id { margin-bottom: 0; }
  .v2rail-id .rl { display: none; }
  .v2nav { flex-direction: row; gap: 16px; margin-bottom: 0; }
  .v2idx-label, .v2idx, .v2rail-foot { display: none; }
  /* 横排的 rail 里塞不下竖向子菜单；Work 本来就通向列全了项目的首页 */
  .v2nav-sub, .v2nav-toe { display: none; }
  .v2main { margin-left: 0; }
  .v2split { grid-template-columns: 1fr; }
  .v2split-media { position: static; }
  .v2split.is-flip .v2split-text { order: 0; }
}
@media (max-width: 680px) {
  .v2work { grid-template-columns: 1fr; gap: 16px; }
  .v2work-shot { order: -1; }
  .v2grid-3, .v2grid-2 { grid-template-columns: 1fr; }
}

/* ===================================================================
   首页瀑布流 —— 两列，各列独立堆叠，高差来自图片本身的比例
   =================================================================== */
.wf { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; align-items: start; }
.wf-col { display: flex; flex-direction: column; gap: 44px; }
.wf-card { display: block; cursor: none; }
.wf-card.is-soon { cursor: default; }
.wf-shot {
  overflow: hidden; border-radius: 12px;
  background: var(--paper-warm);
}
.wf-shot img {
  width: 100%; height: auto; display: block;
  transition: transform .7s var(--ease);
}
.wf-card:hover .wf-shot img { transform: scale(1.035); }
.wf-card.is-soon .wf-shot img { filter: saturate(.7); }
.wf-meta {
  display: flex; justify-content: space-between; align-items: baseline; gap: 20px;
  margin-top: 16px;
}
.wf-t {
  font-family: var(--f-display); font-size: clamp(22px, 2.2vw, 27px);
  line-height: 1.2; color: var(--ink); max-width: 22ch;
}
.wf-m {
  font-family: var(--f-ui); font-size: 14px; letter-spacing: .02em;
  color: var(--ink-45); white-space: nowrap; text-align: right;
}
.wf-m b { font-weight: 500; color: var(--ink-60); }

/* 跟随光标的标签 */
.wf-badge {
  position: fixed; left: 0; top: 0; z-index: 70;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(.9);
  display: inline-flex; align-items: center; gap: 9px;
  padding: 11px 18px; border-radius: 100px;
  background: var(--ink); color: var(--paper);
  font-family: var(--f-ui); font-size: 14px; font-weight: 500;
  white-space: nowrap;
  opacity: 0; transition: opacity .18s var(--ease), transform .18s var(--ease);
}
.wf-badge.is-on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.wf-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--mist); }
.wf-badge.is-soon { background: var(--paper-warm); color: var(--ink-60); }
.wf-badge.is-soon::before { background: var(--ink-45); }
@media (hover: none) { .wf-badge { display: none; } .wf-card { cursor: pointer; } }

@media (max-width: 860px) {
  .wf { grid-template-columns: 1fr; }
  .wf-col { gap: 34px; }
  .wf-t { max-width: none; }
  /* 标题与元信息改为上下排，日期不再和标题抢同一行 */
  .wf-meta { flex-direction: column; gap: 6px; }
  .wf-m { text-align: left; white-space: normal; }
}

/* 首页 hero 收紧：更快到图 */
.v2hero.is-home { padding-block: clamp(36px, 6vw, 72px) clamp(20px, 3vw, 36px); }
.v2hero.is-home .d-title { max-width: 18ch; }

/* About 页的紧凑履历 */
.v2cv { display: flex; flex-direction: column; }
.v2cv-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 28px;
  padding: 16px 0; border-top: 1px solid var(--ink-12);
}
.v2cv-row:last-child { border-bottom: 1px solid var(--ink-12); }
.v2cv-r { font-family: var(--f-ui); font-size: 16px; font-weight: 600; color: var(--ink); }
.v2cv-o { font-size: 15px; color: var(--ink-60); margin-top: 2px; }
.v2cv-w { font-family: var(--f-ui); font-size: 14px; color: var(--ink-45); white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 560px) { .v2cv-row { grid-template-columns: 1fr; } }

/* 作品栅格比正文栏更宽 —— 图要占地方 */
.wrap-wide { width: 100%; max-width: 1320px; margin-inline: auto; padding-inline: var(--pad); }
@media (min-width: 1400px) { .wf { gap: 36px; } .wf-col { gap: 56px; } }

/* ===================================================================
   手风琴履历 —— 点开展开简历条目
   =================================================================== */
.acc { border-top: 1px solid var(--ink-12); }
.acc-item { border-bottom: 1px solid var(--ink-12); }

.acc-head {
  width: 100%; max-width: 100%; background: none; border: 0; padding: 26px 0;
  display: grid; grid-template-columns: minmax(0, 1fr) auto 40px;
  gap: 12px 28px; align-items: center;
  text-align: left; cursor: pointer;
  font: inherit; color: inherit;
}
.acc-head > span:first-child { min-width: 0; }
.acc-org {
  display: block; min-width: 0;
  font-family: var(--f-ui); font-size: 19px; font-weight: 600;
  letter-spacing: -.01em; color: var(--ink-45);
  transition: color .25s var(--ease);
}
.acc-role {
  display: block; min-width: 0;
  font-family: var(--f-ui); font-size: 15px; color: var(--ink-45);
  margin-top: 4px; transition: color .25s var(--ease);
}
.acc-when {
  font-family: var(--f-ui); font-size: 15px; color: var(--ink-45);
  white-space: nowrap; font-variant-numeric: tabular-nums;
  transition: color .25s var(--ease);
}
.acc-head:hover .acc-org,
.acc-item.is-open .acc-org { color: var(--ink); }
.acc-head:hover .acc-when,
.acc-head:hover .acc-role { color: var(--ink-60); }

/* + 转 × 并落进实心圆 */
.acc-ico {
  justify-self: end; position: relative;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: transparent;
  transition: background .3s var(--ease), transform .45s var(--ease);
}
.acc-ico::before, .acc-ico::after {
  content: ""; position: absolute;
  background: var(--ink); border-radius: 1px;
  transition: background .3s var(--ease);
}
.acc-ico::before { width: 16px; height: 2px; }
.acc-ico::after  { width: 2px; height: 16px; }
.acc-item.is-open .acc-ico {
  background: var(--warm-grey);
  transform: rotate(135deg);
}
.acc-item.is-open .acc-ico::before,
.acc-item.is-open .acc-ico::after { background: var(--paper); }

/* 展开区：高度由 JS 驱动。
   0fr→1fr 的写法在部分引擎里不过渡（实测停在 0px），故不采用。 */
.acc-body {
  height: 0; overflow: hidden;
  transition: height .42s var(--ease);
}
.acc-body ul { list-style: none; padding: 0 0 26px; margin: 0; max-width: 76ch; }
.acc-body li {
  font-size: 16px; line-height: 1.6; color: var(--ink-80);
  padding-left: 18px; position: relative;
}
.acc-body li + li { margin-top: 12px; }
.acc-body li::before {
  content: ""; position: absolute; left: 0; top: .68em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-45);
}
.acc-body b { font-weight: 600; color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .acc-body, .acc-ico { transition: none; }
}
@media (max-width: 620px) {
  /* 显式落位，否则自动排布会把 1fr 列挤塌 */
  .acc-head { grid-template-columns: minmax(0, 1fr) 34px; row-gap: 6px; }
  .acc-head > span:first-child { grid-column: 1; grid-row: 1; }
  .acc-ico { grid-column: 2; grid-row: 1; width: 30px; height: 30px; }
  .acc-when { grid-column: 1 / -1; grid-row: 2; font-size: 14px; }
  .acc-org { font-size: 17px; }
}

/* ===================================================================
   What I do —— 手风琴画廊，一张图对一项能力
   =================================================================== */
.gal-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 20px; margin-bottom: clamp(20px, 3vw, 30px); flex-wrap: wrap;
}
.gal-head a { border-bottom: 1px solid var(--ink-12); }
.gal-head a:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* 手风琴画廊（React Bits AccordionGallery 移植）。
   原版用 GSAP 补间 flex-grow / rotateY / 视差位移 / 灰度；这些属性 CSS 都能过渡，
   所以不引依赖。原版的 #060010 深紫叠色换成站内墨色，圆角换成站内的 10px。 */
.ag {
  --ag-gap: 10px;
  --ag-radius: 10px;
  --ag-media: 320px;                           /* JS 按容器宽度量出来覆盖 */
  --ag-dur: .6s;
  --ag-ease: cubic-bezier(.165, .84, .44, 1);  /* = GSAP power3.out */
  display: flex; gap: var(--ag-gap);
  width: 100%; height: 460px;
  perspective: 1400px; perspective-origin: 50% 50%;
}
.ag-panel {
  position: relative; display: block;
  flex: 1 1 0; min-width: 0; min-height: 0;
  overflow: hidden; border-radius: var(--ag-radius);
  background: var(--ink);
  transform: rotateY(var(--ag-rot, 0deg));
  transform-style: preserve-3d; transform-origin: center;
  box-shadow: 0 10px 30px -18px color-mix(in srgb, var(--ink) 80%, transparent);
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.ag-panel.is-on { flex-grow: var(--ag-grow, 5); }
.ag-panel:focus-visible {
  box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px var(--ink),
              0 10px 30px -18px color-mix(in srgb, var(--ink) 80%, transparent);
}

.ag-frame { position: absolute; inset: 0; overflow: hidden; border-radius: inherit; }
.ag-media {
  position: absolute; top: 50%; left: 50%;
  width: var(--ag-media); height: 100%;
  transform: translate(-50%, -50%) translateX(var(--ag-shift, 0px));
  filter: grayscale(1);
}
.ag-panel.is-on .ag-media { filter: grayscale(0); }
.ag-media img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  user-select: none; -webkit-user-drag: none;
}

/* 底部渐变常驻；未展开的面板再压一层 35% 墨色 */
.ag-overlay {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 45%, color-mix(in srgb, var(--ink) 78%, transparent) 100%);
}
.ag-overlay::before {
  content: ""; position: absolute; inset: 0;
  background: var(--ink); opacity: .35;
}
.ag-panel.is-on .ag-overlay::before { opacity: 0; }

.ag-label {
  position: absolute; left: 20px; right: 20px; bottom: 20px; z-index: 2;
  display: flex; align-items: flex-start; gap: 12px;
  pointer-events: none;
}
.ag-bar {
  flex: 0 0 auto; width: 3px; height: 26px; border-radius: 3px;
  background: var(--paper);
  box-shadow: 0 0 12px color-mix(in srgb, var(--paper) 60%, transparent);
  opacity: 0; transform: translateX(-14px);
}
.ag-copy {
  min-width: 0; display: flex; flex-direction: column; gap: 3px;
  opacity: 0; transform: translateX(-14px);
  text-shadow: 0 2px 14px color-mix(in srgb, var(--ink) 55%, transparent);
}
.ag-t {
  font-family: var(--f-ui); font-size: 18px; font-weight: 600; line-height: 26px;
  color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ag-d { font-size: 14px; line-height: 1.45; color: color-mix(in srgb, var(--paper) 78%, transparent); }
.ag-panel.is-on .ag-bar,
.ag-panel.is-on .ag-copy { opacity: 1; transform: none; }

/* 首帧直接落位，之后才开过渡 —— 否则一加载就看到从均分长到展开 */
.ag.is-ready .ag-panel { transition: flex-grow var(--ag-dur) var(--ag-ease), transform var(--ag-dur) var(--ag-ease), min-height var(--ag-dur) var(--ag-ease); }
.ag.is-ready .ag-media { transition: transform var(--ag-dur) var(--ag-ease), filter var(--ag-dur) var(--ag-ease); }
.ag.is-ready .ag-overlay::before { transition: opacity var(--ag-dur) var(--ag-ease); }
/* 收起比展开快（原版 duration × 0.6）；展开时文字比竖条晚 60ms（原版 stagger） */
.ag.is-ready .ag-bar,
.ag.is-ready .ag-copy { transition: opacity calc(var(--ag-dur) * .6) var(--ag-ease), transform calc(var(--ag-dur) * .6) var(--ag-ease); }
.ag.is-ready .ag-panel.is-on .ag-bar { transition-duration: var(--ag-dur); }
.ag.is-ready .ag-panel.is-on .ag-copy { transition-duration: var(--ag-dur); transition-delay: 60ms; }

/* 窄屏竖排。原版这里所有条都是 84px、展开的那条也不长高，只看得到一道缝；
   这里让展开的那条长到 300px，才看得清图。 */
@media (max-width: 520px) {
  .ag { flex-direction: column; height: auto; perspective: none; }
  .ag-panel { flex: 0 0 auto; min-height: 84px; transform: none; }
  .ag-panel.is-on { min-height: 300px; }
  .ag-media { width: 100%; height: 100%; transform: translate(-50%, -50%); }
}

@media (prefers-reduced-motion: reduce) {
  .ag.is-ready .ag-panel, .ag.is-ready .ag-media, .ag.is-ready .ag-overlay::before,
  .ag.is-ready .ag-bar, .ag.is-ready .ag-copy { transition: none; }
}

/* ===================================================================
   ScrollExpand Hero（React Bits ScrollExpand 移植，只有 index-hero.html 用）
   圆角小窗随滚动展开成满屏。几何参数放这里，JS 只读值算进度。
   =================================================================== */
.se {
  --se-start-w: 56;      /* 起始宽度，占舞台百分比 */
  --se-start-h: 74;
  /* 原版起始小窗恒在舞台正中；这张照片人脸偏左，正中会切在半张脸上。
     加一对锚点把小窗挪到人脸上，展开到满屏时锚点自然失效。 */
  --se-start-x: .34;
  --se-start-y: .5;
  --se-start-r: 24px;    /* 起始圆角 */
  --se-end-r: 0px;
  --se-zoom: 1.12;       /* 起始时图片放大倍数，展开过程中回到 1 */
  --se-scrim: .45;       /* 展开后压在图片上的最大暗度 */
  --se-distance: 1.2;    /* 展开过程占几屏 */
  --se-hold: .35;        /* 展开后停留几屏 */
  --se-smoothing: .1;
  position: relative; width: 100%;
  margin-bottom: clamp(40px, 6vw, 80px);
}
.se-track { position: relative; width: 100%; }
/* 顶栏是 sticky 的，舞台要落在它下面，满屏那一刻才真的是一屏 */
.se-stage { position: sticky; top: var(--v2top-h, 72px); width: 100%; overflow: hidden; }

.se-frame {
  position: absolute; inset: 0;
  clip-path: inset(21% 29% 21% 29% round var(--se-start-r));
  will-change: clip-path;
}
.se-media {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 32%;   /* 满屏时人和远景都在画面里 */
  transform: scale(var(--se-zoom));
  transform-origin: center; will-change: transform;
  user-select: none; -webkit-user-drag: none;
}
.se-scrim {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--ink) 88%, transparent),
    color-mix(in srgb, var(--ink) 10%, transparent) 52%,
    color-mix(in srgb, var(--ink) 35%, transparent));
}

/* 展开到最后才淡入的介绍。没有 JS 时不该整段消失，所以 opacity:0 只在 .js 下生效 */
.se-overlay {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: 14px; padding: clamp(28px, 5vw, 72px);
  will-change: opacity, transform;
}
.js .se-overlay { opacity: 0; }
.se-overlay .label { color: color-mix(in srgb, var(--paper) 88%, transparent); }
.se-overlay .d-title { color: var(--paper); max-width: 18ch; }
.se-overlay .d-title em { color: var(--paper); }

/* 小窗阶段压在舞台正中的名字，展开到一半开始淡出上移 */
.se-title {
  position: absolute; inset: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 0 6%; text-align: center;
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(28px, 7vw, 85px); line-height: 1.05; letter-spacing: -.02em;
  color: var(--paper);
  text-shadow: 0 2px 24px color-mix(in srgb, var(--ink) 45%, transparent);
  pointer-events: none; will-change: opacity, transform;
}
.se-title em { font-style: italic; }
.se-hint {
  position: absolute; left: 0; right: 0; bottom: 20px; margin: 0;
  text-align: center;
  font-family: var(--f-ui); font-size: 14px; letter-spacing: .12em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper) 60%, transparent);
  pointer-events: none; will-change: opacity, transform;
}

/* 窄屏：42% 宽的小窗只剩一条，起始画幅改成宽一点矮一点 */
@media (max-width: 640px) {
  .se { --se-start-w: 76; --se-start-h: 44; }
}
/* 不跑动画时直接给最终状态：满屏、无缩放、介绍可见 */
@media (prefers-reduced-motion: reduce) {
  .se-frame { clip-path: inset(0 round 0); }
  .se-media { transform: none; }
  .js .se-overlay { opacity: 1; }
  .se-title, .se-hint { opacity: 0; }
}

/* ===================================================================
   About —— 人像与小传
   =================================================================== */
.v2portrait { max-height: 62vh; overflow: hidden; }
.v2portrait img { width: 100%; height: 100%; object-fit: cover; }

/* 四则小传。
   窄屏与触屏是这一份：规整网格，说明常驻 —— 没有光标，没有别的办法。
   宽屏且有 hover 的那一份在下面覆盖成散落 + 光标文字框。 */
.facets {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 22px; margin-top: clamp(20px, 3vw, 30px);
}
.facet { display: block; }
.facet-shot {
  aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: 10px; background: var(--paper-warm);
}
.facet-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.facet-t { margin: 14px 0 6px; }
.facet-note { margin: 0; font-size: 15.5px; line-height: 1.55; color: var(--ink-60); }

@media (max-width: 1080px) { .facets { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px; } }
@media (max-width: 560px)  { .facets { grid-template-columns: 1fr; } }

/* 散落版。--l/--t/--w 都是舞台的百分比，所以整组随容器等比缩放，
   --k 这个放大倍数在任何视口下都成立。--org 让大图朝舞台内侧长，不出界。 */
@media (min-width: 1060px) and (hover: hover) {
  .facets {
    display: block; position: relative;
    height: clamp(380px, 34vw, 440px);
    margin-top: clamp(26px, 3.4vw, 44px);
  }
  .facet {
    position: absolute; left: var(--l); top: var(--t); width: var(--w);
    transform: rotate(var(--rot)); cursor: none;
    transition: transform .5s var(--ease), opacity .3s var(--ease), filter .3s var(--ease);
  }
  /* 小图按各自原生比例放，不裁 —— 「无序」是比例差自己给的 */
  .facet-shot {
    aspect-ratio: auto; border-radius: 8px;
    transform: scale(1); transform-origin: var(--org);
    box-shadow: 0 6px 20px rgba(45, 26, 10, .10);
    transition: transform .55s cubic-bezier(.2, .78, .3, 1), box-shadow .45s var(--ease);
  }
  .facet-shot img { height: auto; object-fit: fill; }
  .facet-t { transition: opacity .3s var(--ease); }
  .facet-note {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    clip-path: inset(50%); overflow: hidden;
  }

  /* 「展开」是靠兄弟压暗读出来的，不是靠尺寸猜 */
  .facets:hover .facet:not(:hover),
  .facets:focus-within .facet:not(:focus-visible) { opacity: .3; filter: saturate(.5); }
  .facet:hover, .facet:focus-visible { z-index: 5; transform: rotate(0deg); outline: none; }
  .facet:hover .facet-shot,
  .facet:focus-visible .facet-shot {
    transform: scale(var(--k));
    box-shadow: 0 26px 64px rgba(45, 26, 10, .22);
  }
  .facet:hover .facet-t { opacity: 0; }

  /* 键盘聚焦没有光标可挂，说明就地显示在图下 */
  .facet:focus-visible .facet-note {
    position: static; width: auto; height: auto; margin: 10px 0 0;
    clip-path: none; overflow: visible;
  }
  .facet:focus-visible .facet-shot { outline: 2px solid var(--ink); outline-offset: 4px; }

  @media (prefers-reduced-motion: reduce) {
    .facet, .facet-shot, .facet-t { transition: none; }
  }
}

/* 光标文字框：复用首页那颗徽标的跟随机制，换成能排整句的方盒。
   不居中在光标上，偏到右下，否则正好遮住刚放大的那张图。 */
.wf-badge.is-note {
  transform: none; transform-origin: 0 0;
  max-width: 300px; padding: 14px 17px; border-radius: 14px;
  white-space: normal; text-align: left; text-wrap: pretty;
  font-weight: 400; line-height: 1.5;
  background: var(--ink); color: var(--paper);
}
.wf-badge.is-note::before { display: none; }
/* 位移由 JS 每帧写 inline transform，这里不能再给 transform 上过渡 ——
   两套动画叠在一起会拖出残影。 */
.wf-badge.is-note { transition: opacity .2s var(--ease); }

/* 履历分组之间的间距，替代内联 margin */
.v2cv-group + .v2cv-group { margin-top: clamp(34px, 5vw, 48px); }

/* 行内下划线链接 */
.ul-link { border-bottom: 1px solid currentColor; }

/* 引言下方的联系方式 */
.contact-line { margin-top: 18px; }

/* ===================================================================
   项目页：白底 + 原型浅框 + 横滑图组
   =================================================================== */
body.is-case {
  --paper:       #ffffff;
  --paper-glass: rgba(255, 255, 255, .72);
  --end:         #f6f4f1;
}

/* 原型/界面演示：浅灰描边，把屏幕从白底上托出来 */
/* 向内描边：box-sizing 为 border-box，3px 画在元素内部，不改变占位尺寸 */
.v2fig.is-proto img,
.v2fig.is-proto video { border: 3px solid #e4e0da; border-radius: 12px; }

/* 横滑图组 —— 原站 brainstorming 就是可翻页的 */
.v2scroll {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: min(52vw, 380px);
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--pad);
  margin-inline: calc(var(--pad) * -1);
  padding: 0 var(--pad) 16px;
  overscroll-behavior-x: contain;
  scrollbar-width: thin; scrollbar-color: var(--ink-12) transparent;
}
.v2scroll::-webkit-scrollbar { height: 6px; }
.v2scroll::-webkit-scrollbar-track { background: transparent; }
.v2scroll::-webkit-scrollbar-thumb { background: var(--ink-12); border-radius: 100px; }
.v2scroll img { scroll-snap-align: start; width: 100%; height: auto; border-radius: 10px; }
.v2scroll-hint {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--f-ui); font-size: 12px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-45); margin-bottom: 12px;
}
.v2scroll-hint::after { content: "→"; letter-spacing: 0; font-size: 14px; }

/* Hero：动图在最上，标题在其下，两者之间不再插入正文 */
.case-open { padding-block: 0 0; }
.case-open-media { width: 100%; }
/* 开屏动图吃满整屏时会把标题推到折线以下。压到 58vh，让 logo、
   标签和标题的头两行留在首屏。三页的开屏素材都是 ~16:9，裁切幅度温和。 */
.case-open-media img, .case-open-media video {
  width: 100%; height: auto; display: block;
  max-height: 58vh; object-fit: cover; object-position: center 42%;
}
.case-open-title { padding-block: clamp(32px, 5vw, 60px) clamp(8px, 1.5vw, 16px); }
.case-open-title .case-logo { height: clamp(34px, 4vw, 46px); width: auto; margin-bottom: 18px; }
.case-open-title .label { margin-bottom: 16px; }

/* ===================================================================
   结尾区 —— 全站唯一换地面色的地方，让页面真的“收住”
   =================================================================== */
.v2end {
  background: var(--end);
  margin-top: clamp(80px, 11vw, 140px);
  padding-block: clamp(56px, 8vw, 96px) 0;
}

.v2end .v2sec { padding-top: 0; }

/* 结尾区的大图卡片：复用 .wf-card，跟随光标的标签自动生效 */
.end-works {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 32px);
}
.end-works .wf-shot { aspect-ratio: 16 / 11; }
.end-works .wf-shot img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 760px) { .end-works { grid-template-columns: 1fr; } }

/* ===================================================================
   页脚 —— 三栏：身份 / 导航 / 社交，下方版权条
   =================================================================== */
.v2foot {
  margin-top: clamp(56px, 8vw, 88px);
  padding-block: clamp(40px, 6vw, 64px) 32px;
  border-top: 1px solid var(--ink-12);
  display: block;
}
.v2foot-grid {
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 36px 32px;
  align-items: start;
}
.v2foot-id .nm {
  font-family: var(--f-display); font-size: clamp(26px, 3vw, 34px);
  line-height: 1.1; color: var(--ink);
}
.v2foot-id .nm em { font-style: italic; }
.v2foot-id p {
  font-family: var(--f-ui); font-size: 14.5px; color: var(--ink-60);
  margin-top: 10px; max-width: 34ch; line-height: 1.5;
}
.v2foot-mail {
  display: inline-block; margin-top: 18px;
  font-family: var(--f-ui); font-size: 16px; color: var(--ink);
  border-bottom: 1px solid var(--ink-12);
  transition: border-color .2s var(--ease);
}
.v2foot-mail:hover { border-bottom-color: var(--ink); }

.v2foot-col h3 {
  font-family: var(--f-ui); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-45); margin: 0 0 14px;
}
.v2foot-col a {
  display: block; font-family: var(--f-ui); font-size: 15px;
  color: var(--ink-60); padding: 4px 0; width: fit-content;
  border-bottom: 1px solid transparent;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}
.v2foot-col a:hover { color: var(--ink); border-bottom-color: var(--ink); }

.v2foot-base {
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin-top: clamp(40px, 6vw, 64px); padding-top: 22px;
  border-top: 1px solid var(--ink-12);
  font-family: var(--f-ui); font-size: 13px; color: var(--ink-45);
}
@media (max-width: 760px) {
  .v2foot-grid { grid-template-columns: 1fr 1fr; }
  .v2foot-id { grid-column: 1 / -1; }
}

/* 两张图并排、等高铺满 —— 高度由较矮的一张决定，另一张裁切填充 */
.v2pair {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px; margin-block: clamp(20px, 3vw, 32px);
}
.v2pair figure { margin: 0; }
.v2pair .shot { aspect-ratio: 16 / 10; overflow: hidden; border-radius: 12px; }
.v2pair .shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.v2pair.is-proto .shot { border: 3px solid #e4e0da; }
.v2pair figcaption {
  margin-top: 10px; font-family: var(--f-ui);
  font-size: 13.5px; color: var(--ink-45);
}
@media (max-width: 680px) { .v2pair { grid-template-columns: 1fr; } }

/* ===================================================================
   开屏与加载
   遮罩和 loading 是同一块色块：加载时它是底，加载完它向上撤走，
   就是开屏的第一拍，中间没有接缝。
   色块每次进站都在（它就是开屏）；loading 的内容只有超过阈值才创建。
   =================================================================== */
.v2-intro::after {
  content: ""; position: fixed; inset: 0; z-index: 90;
  background: var(--paper-warm);
  transform: translateY(0);
  transition: transform 1s cubic-bezier(.76, 0, .24, 1);
  /* JS 挂掉时的自救：8s 后色块自己撤走，不把站点锁死 */
  animation: veilBail .9s cubic-bezier(.76, 0, .24, 1) 8s forwards;
}
.v2-intro.is-up::after { transform: translateY(-101%); animation: none; }
@keyframes veilBail { to { transform: translateY(-101%); } }

.v2ld {
  position: fixed; inset: 0; z-index: 91;
  display: grid; place-items: center;
  opacity: 0; pointer-events: none;
  transition: opacity .34s var(--ease);
}
.v2ld.is-on { opacity: 1; }
.v2ld-in { display: flex; flex-direction: column; align-items: center; gap: 22px; }

.v2ld-mark {
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(28px, 3.4vw, 42px); line-height: 1.1;
  color: var(--ink); white-space: nowrap;
}
.v2ld-mark em { font-style: italic; }

.v2ld-line { width: 232px; height: 3px; background: var(--ink-12); }
.v2ld-line i {
  display: block; width: 0; height: 100%;
  background: var(--flag);
  transition: width .38s cubic-bezier(.3, .7, .3, 1);
}

/* 逐词遮罩：同一行共用延时，看起来是整行推。
   不重排 DOM，嵌套的 <em> 原样保留。
   line-height 1.02 的盒子会切掉 Instrument Serif 的降部，
   所以动画结束后把 overflow 交还 visible。 */
.lw  { display: inline-block; overflow: hidden; vertical-align: bottom; }
.lwi { display: inline-block; transform: translateY(115%);
       transition: transform .92s cubic-bezier(.19, .84, .3, 1); }
.is-in .lwi { transform: none; }
.lw.done { overflow: visible; }
@media (prefers-reduced-motion: reduce) {
  .lwi { transform: none; }
  .v2-intro::after { display: none; }
}

/* ===================================================================
   手写签名 —— 复刻原站：Allison 56px / rgba(0,0,0,.5)，
   每个字母从 opacity .001 + blur(10px) 依次解除
   =================================================================== */
.signoff { margin-top: clamp(28px, 4vw, 44px); }
.signoff-regards { font-family: var(--f-ui); font-size: 14px; color: var(--ink-45); margin-bottom: 6px; }
.sign {
  font-family: 'Allison', cursive;
  font-size: 56px;
  line-height: 1;
  color: rgba(0, 0, 0, .5);
  white-space: nowrap;
}
.sign span { display: inline-block; }
.js .sign span { opacity: .001; filter: blur(10px); }
.js .sign.seen span {
  opacity: 1; filter: blur(0);
  transition: opacity .7s var(--ease), filter .7s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .js .sign span { opacity: 1; filter: none; }
}

/* ===================================================================
   Alteration —— 左栏原型钉住、占满一屏高，右栏改动逐条滚过。
   与 .v2split 的区别：那边是长文对着一张不变的图读，这边是
   一件不变的成品对着一串变化读，所以钉住的是左栏且吃满视口高。
   =================================================================== */
.v2alt {
  display: grid;
  grid-template-columns: minmax(260px, 400px) minmax(0, 1fr);
  gap: clamp(24px, 3.5vw, 56px);
  align-items: start;
}

.v2alt-stage {
  position: sticky; top: 0;
  height: 100vh;
  height: 100svh;
  display: grid; grid-template-rows: minmax(0, 1fr) auto;
  gap: 14px;
  padding-block: 84px 28px;          /* 让开顶部那条 sticky 状态条 */
}

.v2alt-device {
  height: 100%; width: auto;
  max-width: 100%; max-height: 100%;
  aspect-ratio: 9 / 19.5;
  justify-self: center;
  border: 3px solid #e4e0da;          /* 与 .v2fig.is-proto 同一道描边 */
  border-radius: 28px;
  overflow: hidden;
  background: var(--paper-warm);
}
.v2alt-device > iframe,
.v2alt-device > video,
.v2alt-device > img {
  width: 100%; height: 100%; display: block; border: 0; object-fit: cover;
}

/* ---- 嵌入原型的点击激活 ----
   一个钉住的、占满一屏高的 iframe 会把滚轮吃掉：鼠标停在上面时页面滚不动。
   所以默认让它不接收指针事件，点一下才交出控制权。 */
/* Figma 的嵌入自己会画一层设备外壳，我们的描边加上去就是双层框。
   放原型时把外框撤掉，让它自己的机身当边界。 */
.v2alt-device.has-proto {
  position: relative;
  border: 0; border-radius: 0; background: transparent;
}
.v2alt-device.has-proto > iframe { pointer-events: none; }
.v2alt-device.has-proto.is-armed > iframe { pointer-events: auto; }

.v2alt-arm {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  margin: 0; padding: 0; border: 0;
  background: rgba(247, 243, 236, .52);
  font: inherit; cursor: pointer;
  transition: opacity .25s var(--ease), background .25s var(--ease);
}
.v2alt-arm:hover { background: rgba(247, 243, 236, .34); }
.v2alt-arm span {
  font-family: var(--f-ui); font-size: 14px;
  letter-spacing: .04em; text-transform: uppercase; color: var(--ink);
  background: var(--paper); border-radius: 999px; padding: 10px 18px;
  box-shadow: 0 6px 20px rgba(45, 26, 10, .14);
}
.is-armed .v2alt-arm { opacity: 0; pointer-events: none; }

.v2alt-hint {
  position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
  white-space: nowrap;
  font-family: var(--f-ui); font-size: 12px; letter-spacing: .03em;
  color: var(--ink-60);
  background: var(--paper); border-radius: 999px; padding: 7px 14px;
  box-shadow: 0 6px 20px rgba(45, 26, 10, .14);
  opacity: 0; pointer-events: none;
  transition: opacity .25s var(--ease);
}
.is-armed .v2alt-hint { opacity: 1; }

.v2alt-slot {
  height: 100%;
  display: grid; align-content: center; justify-items: center; gap: 10px;
  padding: 24px; text-align: center;
}
.v2alt-slot p:last-child {
  font-family: var(--f-ui); font-size: 14px;
  color: var(--ink-45); line-height: 1.5; max-width: 22ch;
}

.v2alt-cap {
  font-family: var(--f-ui); font-size: 13.5px;
  color: var(--ink-45); line-height: 1.5; text-align: center;
}

.v2alt-flow {
  display: flex; flex-direction: column;
  gap: clamp(44px, 6vw, 84px);
  padding-block: clamp(84px, 11vw, 150px);
}
.v2alt-item img { width: 100%; height: auto; border-radius: 10px; }
.v2alt-item .v2step-n { margin-bottom: 12px; }
.v2alt-item figcaption {
  margin-top: 12px; font-family: var(--f-ui);
  font-size: 13.5px; color: var(--ink-45); line-height: 1.5;
}

/* 窄屏：钉不住也没意义，原型退回普通一张图排在最前 */
@media (max-width: 1080px) {
  .v2alt { grid-template-columns: minmax(0, 1fr); }
  .v2alt-stage { position: static; height: auto; padding-block: 0; }
  .v2alt-device { height: auto; width: min(300px, 86%); }   /* 68% 在 375 上只有 217px，嵌入的可交互原型点不准 */
  .v2alt-flow { padding-block: 40px 0; }
}

/* 奖状：IDA 那张自带白边，齐边的两张直接贴着纸，三张并排时边界读不出来。
   统一加一道浅灰描边把纸的范围交代清楚。 */
.v2grid.is-cert img { border: 1px solid var(--ink-12); }

/* ===================================================================
   Brilliant 用到的几个件
   =================================================================== */

/* 笔记本样机 —— 现在装在 .v2feat-media 里 */
/* 底座要比屏幕宽才像 MacBook。不能靠底座外扩——那样它会顶出容器，
   图位一吃满宽度就把整页撑出横向滚动。改成让屏幕内缩，底座占满。 */
.v2laptop-screen {
  width: 94%; margin-inline: auto;
  background: #1d1d1f;
  border-radius: 12px 12px 3px 3px;
  padding: 9px 9px 0;
}
.v2feat-media .v2laptop-screen video,
.v2laptop-screen video,
.v2laptop-screen img {
  width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  display: block; border-radius: 3px;
}
.v2laptop-base {
  height: 9px;
  border-radius: 0 0 9px 9px;
  background: linear-gradient(#d2d2d7, #a9a9ae);
}
/* 正在跑的东西，名字前面挂 .v2pill 那颗绿点 */
.h-sub.is-live { display: inline-flex; align-items: center; gap: 10px; }
.h-sub.is-live::before {
  content: ""; flex: 0 0 auto;
  width: 7px; height: 7px; border-radius: 50%;
  background: #21c46a; box-shadow: 0 0 0 3px rgba(33, 196, 106, .18);
}

/* 横滑变体 —— 基础 .v2scroll 的格宽给小图用，这两种放不下 */
/* 露出下一张的左侧：这批图的论点全在左边约 30%，格宽压到 72% 就能
   在未滑动时看到下一张的标题 */
.v2scroll.is-peek { grid-auto-columns: min(72%, 840px); scroll-snap-type: x mandatory; }
.v2scroll.is-peek img { width: 100%; height: auto; border-radius: 10px; scroll-snap-align: start; }

.v2rig { margin-top: clamp(28px, 4vw, 48px); }

/* 三个研究方法：切开的图各自带上选用理由 */
.v2methods {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2.4vw, 30px);
  margin-block: clamp(20px, 3vw, 32px);
}
.v2methods figure { margin: 0; }
.v2methods img { width: 100%; height: auto; display: block; }
.v2methods figcaption {
  margin-top: 14px; font-family: var(--f-ui);
  font-size: 14px; line-height: 1.5; color: var(--ink-60);
}
@media (max-width: 720px) { .v2methods { grid-template-columns: 1fr; } }

/* Scale / Goal 这类定值说明 */
.v2spec { display: grid; gap: 14px; margin-block: clamp(20px, 3vw, 30px); }
.v2spec > div { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 16px; align-items: baseline; }
.v2spec dd { font-size: 17px; line-height: 1.62; color: var(--ink-80); }
@media (max-width: 640px) { .v2spec > div { grid-template-columns: 1fr; gap: 4px; } }

/* ===================================================================
   侧栏 Work 下拉 —— 展开只列项目名，直接跳
   不做高度动画：grid-template-rows 0fr→1fr 在这个引擎里不 transition，
   而三行的菜单本来也不值得为它写一套 JS 高度动画，直接显隐更利落。
   =================================================================== */
.v2nav-work { display: flex; align-items: center; gap: 6px; }
.v2nav-work > a { flex: 0 0 auto; }

.v2nav-toe {
  width: 20px; height: 20px; padding: 0;
  border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
  color: var(--ink-45);
  transition: color .2s var(--ease), transform .2s var(--ease);
}
.v2nav-toe::before {
  content: ""; width: 6px; height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
.v2nav-toe:hover { color: var(--ink); }
.v2nav-work.open .v2nav-toe { transform: rotate(180deg); color: var(--ink); }

/* 作者样式里的 display 会压过 UA 表的 [hidden]{display:none}，
   所以 hidden 属性必须自己再声明一次，否则设了等于没设 */
.v2nav-sub[hidden] { display: none; }
.v2nav-sub {
  display: flex; flex-direction: column; gap: 1px;
  margin: 2px 0 4px 12px;
  border-left: 1px solid var(--ink-12);
  padding-left: 12px;
}
.v2nav-sub a {
  font-family: var(--f-ui); font-size: 14px;
  color: var(--ink-45); padding: 4px 0; width: fit-content;
  transition: color .2s var(--ease);
}
.v2nav-sub a:hover { color: var(--ink); }
.v2nav-sub a[aria-current="page"] { color: var(--ink); }

/* 首页：Work 下的两个领域常驻，滚到哪组高亮哪项 */
.v2nav-sub.is-domains { margin-top: 4px; }
.v2nav-sub a.is-on { color: var(--ink); }
.v2nav-sub a.is-on::before {
  content: ""; position: absolute; left: -13px; top: 50%;
  width: 1px; height: 14px; margin-top: -7px;
  background: var(--flag);
}
.v2nav-sub.is-domains a { position: relative; }
/* 窄屏交给胶囊切换条，侧栏这两项要收起来 —— 靠前那条 @media 里的
   display:none 会被后面的 .v2nav-sub{display:flex} 覆盖，必须在这里再写一次 */
@media (max-width: 1080px) { .v2nav-sub.is-domains { display: none; } }

/* 章节索引里需要主动被看见的一项：不靠 hover，静止状态就带旗色圆点 */
.v2idx a.is-live { color: var(--ink); }
.v2idx a.is-live::after {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--flag); margin-left: 7px; align-self: center;
}

/* ===================================================================
   Feature 行 —— 左边动图吃满，右边文字；向下滚磁吸到下一个
   =================================================================== */
/* 磁吸只在有这个组件的页面上开，不是全站行为。
   用 proximity 不用 mandatory：页面其余部分照常滚，只有靠近 feature
   时才被吸过去，不会把整页变成一格一格的翻页。 */
html:has(.v2feats) {
  scroll-snap-type: y proximity;
  /* 顶栏是 sticky 的，吸附区域要扣掉它，center 才是「可见区域的中间」 */
  scroll-padding-top: var(--v2top-h, 72px);
}

/* 左侧留一条窄轨给节点 bar，正文列在第二列 */
.v2feats {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  column-gap: clamp(14px, 2.2vw, 30px);
  align-items: start;
}

/* 节点 bar：钉在视口中线，告诉你一共几个、现在第几个，
   下面那个箭头提示还能往下滑，滑到最后一个就淡掉 */
.v2feats-bar {
  grid-column: 1; grid-row: 1 / -1;
  position: sticky; top: 50vh;
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  margin-top: 42vh;
}
.v2fb {
  width: 24px; height: 22px; padding: 0; border: 0; background: none;
  cursor: pointer; display: grid; place-items: center;
}
.v2fb::before {
  content: ""; width: 5px; height: 5px; border-radius: 100px;
  background: var(--ink-12);
  transition: height .32s var(--ease), background .32s var(--ease), width .32s var(--ease);
}
.v2fb:hover::before { background: var(--ink-45); }
.v2fb.is-on::before { height: 20px; width: 5px; background: var(--flag); }

.v2fb-cue {
  width: 7px; height: 7px; margin-top: 4px;
  border-right: 1.5px solid var(--ink-45);
  border-bottom: 1.5px solid var(--ink-45);
  transform: rotate(45deg);
  transition: opacity .3s var(--ease);
}
.v2feats.is-last .v2fb-cue { opacity: 0; }

.v2feat {
  grid-column: 2;
  display: grid;
  grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 72px);
  align-items: center;
  min-height: 100vh;
  min-height: 100svh;
  padding-block: clamp(32px, 5vw, 64px);
  /* 吸到中间而不是顶部：顶部有那条 sticky 状态条，start 会被压住 */
  scroll-snap-align: center;
}

/* 动图不加边框，也不圆角 */
.v2feat-media video,
.v2feat-media img {
  width: 100%; height: auto; display: block;
  border: 0; border-radius: 0;
}

/* 竖屏手机录屏（SwiftHome，1080×1920）：按高度定尺寸，任何视口下都占满
   「视口 − 顶栏 − 上下留白」这一格，不会被顶栏压住。
   min() 的第二项是宽度上限换算回来的高度 —— 窗口又窄又高时退成撑满宽度，
   宁可不满高也不裁切。16/9 对应素材比例。 */
/* 这块放在 .wrap-wide 里（最宽 1264px），标题和导语用 .v2col 收回正文列宽。
   原因：满高的录屏在大屏上会很宽（2560×1440 下 734px），塞进 1000px 的正文列
   文字只剩 85px。 */
.v2col { max-width: calc(var(--col) - var(--pad) * 2); margin-inline: auto; }
.v2feats:not(.is-wide) {
  container-type: inline-size;
  --feat-pad: clamp(16px, 2.5vh, 32px);
  --feat-h: calc(100svh - var(--v2top-h, 72px) - var(--feat-pad) * 2);
}
.v2feats:not(.is-wide) .v2feat {
  grid-template-columns: max-content minmax(0, 1fr);
  min-height: calc(100svh - var(--v2top-h, 72px));
  padding-block: var(--feat-pad);
}
.v2feats:not(.is-wide) .v2feat-media video,
.v2feats:not(.is-wide) .v2feat-media img {
  /* 录屏最多占这一块宽度的 58%，保证文字列不被挤没 */
  height: min(var(--feat-h), 58cqw * 16 / 9);
  width: auto; aspect-ratio: 9 / 16;
  object-fit: contain;
}

.v2feat-text > * + * { margin-top: 12px; }
.v2feat-text .v2step-n { margin-bottom: 0; }

/* 横幅素材（笔记本样机）左右分栏会把两边都撑空：16:9 的机器挤在半栏里，
   右栏又只有一个名字。改成上下叠：说明在上，机器吃满整行宽度。 */
.v2feats.is-wide .v2feat {
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  gap: clamp(18px, 2.4vw, 30px);
}
.v2feats.is-wide .v2feat-text { order: -1; }
/* 编号贴着标题，正文才拉开：Brilliant 那边只有编号 + 名字，
   FitFido 这边名字下面还有一句说明 */
.v2feats.is-wide .v2feat-text > * + * { margin-top: 6px; }
.v2feats.is-wide .v2feat-text .h-sub + .body { margin-top: 10px; }
.v2feats.is-wide .v2feat-text .body { max-width: 62ch; }

@media (max-width: 900px) {
  html:has(.v2feats) { scroll-snap-type: none; }
  .v2feats { grid-template-columns: minmax(0, 1fr); }
  .v2feats-bar { display: none; }
  .v2feat, .v2feats.is-wide .v2feat {
    grid-column: 1;
    grid-template-columns: minmax(0, 1fr);
    min-height: 0; scroll-snap-align: none;
    padding-block: clamp(28px, 5vw, 48px);
  }
  .v2feat-media { max-width: 360px; }
  .v2feats.is-wide .v2feat-media { max-width: none; }
  /* 窄屏上下叠。选择器必须和上面桌面那条同样具体，否则两栏 max-content
     盖不掉，375 宽下页面会被撑到 499px。录屏仍按一屏高，放不下就按列宽。 */
  .v2feats:not(.is-wide) .v2feat {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    padding-block: clamp(28px, 5vw, 48px);
  }
  .v2feats:not(.is-wide) .v2feat-media { max-width: none; }
  .v2feats:not(.is-wide) .v2feat-media video,
  .v2feats:not(.is-wide) .v2feat-media img {
    height: min(var(--feat-h), 100cqw * 16 / 9);
    margin-inline: auto;
  }
}

/* ===================================================================
   CTA —— 通向可交互原型。整页只有这一个实心块，所以它跳得出来
   =================================================================== */
.v2cta {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  margin-top: clamp(32px, 4.5vw, 56px);
  padding: clamp(20px, 2.6vw, 28px) clamp(22px, 3vw, 34px);
  background: var(--ink); color: var(--paper);
  border-radius: 14px;
  transition: transform .22s var(--ease), box-shadow .22s var(--ease);
}
.v2cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 34px rgba(45, 26, 10, .22);
}
.v2cta-txt { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.v2cta-t {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--f-ui); font-size: 18px; font-weight: 600;
  letter-spacing: -.01em;
}
.v2cta-t::before {
  content: ""; flex: 0 0 auto;
  width: 7px; height: 7px; border-radius: 50%;
  background: #21c46a; box-shadow: 0 0 0 3px rgba(33, 196, 106, .22);
}
.v2cta-s {
  font-family: var(--f-ui); font-size: 14px;
  color: rgba(255, 254, 252, .6);
}
/* 目标在本页下方，所以箭头朝下 */
.v2cta-go {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255, 254, 252, .12);
  display: grid; place-items: center;
  transition: background .22s var(--ease), transform .22s var(--ease);
}
.v2cta-go::before {
  content: ""; width: 8px; height: 8px;
  border-right: 1.6px solid var(--paper);
  border-bottom: 1.6px solid var(--paper);
  transform: translateY(-2px) rotate(45deg);
}
.v2cta:hover .v2cta-go { background: rgba(255, 254, 252, .2); transform: translateY(2px); }

/* 锚点跳转要让开顶部那条 sticky 状态条 */
.v2sec[id], [id].v2sec { scroll-margin-top: 88px; }

/* ===================================================================
   About 开场 —— 整屏照片墙翻落、缓慢横滚，介绍板块再从下方滑上来
   =================================================================== */
.v2open {
  position: relative;
  /* 舞台上面还压着状态条（窄屏时还多一条横排 rail），高度必须减掉它们。
     --v2open-h 是 v2.js 量出的「视口高 − 舞台距文档顶的偏移」，
     两种布局都准；变量缺席时退回只减状态条。 */
  height: var(--v2open-h, calc(100svh - var(--v2top-h, 72px)));
  overflow: hidden;
  margin-bottom: clamp(40px, 6vw, 80px);
}

.v2open-wall {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; gap: 12px;
  perspective: 1500px;                 /* 翻转要有景深才立得住 */
}
.v2open-row { flex: 1 1 0; min-height: 0; overflow: hidden; }
.v2open-row .wall-track {
  display: flex; gap: 12px; height: 100%; width: max-content;
}
.v2open-row i {
  flex: 0 0 auto; height: 100%; aspect-ratio: 3 / 4;
  border-radius: 10px;
  background: var(--c, var(--paper-warm));
  transform-origin: top center;        /* 从上沿往下翻落 */
}

/* 起始态由文档头部的同步脚本挂上，赶得上第一帧 */
.v2-about .v2open-row i { transform: rotateX(-92deg); opacity: 0; }
.v2-about.is-open .v2open-row i {
  transform: none; opacity: 1;
  transition: transform .8s cubic-bezier(.2, .78, .3, 1), opacity .44s var(--ease);
}

/* 落定停顿后开始走。--roll 是一份副本的实际宽度，JS 逐行量出来写进去，
   两份首尾相接，循环处看不出接缝。中间一行反向，墙才不会整体平移。 */
.v2open-row.is-rolling .wall-track {
  animation: wallRoll var(--roll-dur, 80s) linear infinite;
}
.v2open-row.is-back.is-rolling .wall-track { animation-direction: reverse; }
@keyframes wallRoll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(var(--roll, 50%) * -1), 0, 0); }
}
/* 只有鼠标停在墙上才暂停，停在介绍卡片上不该停 */
.v2open-wall:hover .wall-track { animation-play-state: paused; }

/* 介绍板块：贴着视口下沿，开场末尾滑上来 */
.v2open-card {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: var(--paper);
  padding-block: clamp(26px, 4vw, 46px) clamp(30px, 4.5vw, 52px);
  box-shadow: 0 -20px 50px rgba(45, 26, 10, .1);
  transform: translateY(100%);
  transition: transform .9s cubic-bezier(.22, .8, .3, 1);
}
.v2open-card .v2hero { padding-block: 0; }
/* 必须把 :not() 限定在根元素上。写成裸的 :not(.v2-about) 会匹配任何
   不带该类的祖先（包括卡片自己的父级），transform: none 就永远生效，
   起始位移根本不会出现。 */
.v2-about.is-card .v2open-card,
html:not(.v2-about) .v2open-card { transform: none; }

@media (max-width: 720px) {
  .v2open-wall { gap: 8px; }
  .v2open-row .wall-track { gap: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .v2open-row i { transform: none; opacity: 1; }
  .v2open-row.is-rolling .wall-track { animation: none; }
  .v2open-card { transform: none; }
}

/* ===================================================================
   首页 Work 分组（实习 / 学校）+ 大纲页
   =================================================================== */
.wf-group + .wf-group { margin-top: clamp(64px, 8vw, 104px); }

/* Fabricare 整宽特写：一块公开实习，里面三件交付物。
   标题沿用 .wf-t 的字号（clamp 22–27），不另起一档 —— 整宽本身已经给足分量，
   一页只留首页大标题一个 display moment。 */
.wf-feat { display: block; margin-bottom: clamp(44px, 6vw, 72px); }
.wf-feat-shot { display: block; }
.wf-feat-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 24px; flex-wrap: wrap; margin-top: 18px;
}
.wf-feat-t {
  font-family: var(--f-display); font-size: clamp(22px, 2.2vw, 27px);
  line-height: 1.2; color: var(--ink); max-width: 30ch;
}
.wf-feat-t:hover { text-decoration: underline; text-underline-offset: 4px; }
.wf-feat-parts {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.4vw, 36px);
  margin-top: clamp(22px, 2.6vw, 32px);
  padding-top: clamp(20px, 2.4vw, 28px);
  border-top: 1px solid var(--ink-12);
}
.wf-part { display: block; }
.wf-part .v2step-n { display: block; margin-bottom: 8px; }
.wf-part-t {
  display: block; font-family: var(--f-ui); font-size: 16px; font-weight: 600;
  color: var(--ink); margin-bottom: 5px;
  transition: color .2s var(--ease);
}
.wf-part-d { display: block; font-size: 14.5px; line-height: 1.5; color: var(--ink-60); }
.wf-part:hover .wf-part-t { color: var(--flag); }

/* 锁住的两个 SCADpro 项目降到下一行，仍是两栏 */
.wf-locked { align-items: start; }

@media (max-width: 860px) {
  .wf-feat-parts { grid-template-columns: 1fr; gap: 22px; }
  .wf-feat-head { flex-direction: column; gap: 8px; }
}

/* 领域切换条：钉在状态条下面，只在 #work 这一段里跟着走（sticky 的父级就是 section）。
   点击是跳转不是筛选，作品照旧上下排。 */
/* 领域导航已经并进左侧栏，宽屏不再需要这条横条；
   但 ≤1080px 时 rail 变横排、子导航被隐藏，那里仍然靠它。 */
@media (min-width: 1081px) { .wf-switch { display: none; } }
.wf-switch {
  position: sticky; top: var(--v2top-h, 72px); z-index: 25;
  margin: 0 calc(var(--pad) * -1) clamp(22px, 3vw, 34px);
  padding: 10px var(--pad);
  background: var(--paper-glass);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
}
.wf-switch-in {
  display: inline-flex; gap: 4px; padding: 4px;
  border: 1px solid var(--ink-12); border-radius: 100px;
  background: var(--paper);
}
.wf-switch a {
  font-family: var(--f-ui); font-size: 14px; font-weight: 500;
  padding: 8px 20px; border-radius: 100px;
  color: var(--ink-60);
  transition: background .25s var(--ease), color .25s var(--ease);
}
.wf-switch a:hover { color: var(--ink); }
.wf-switch a.is-on { background: var(--ink); color: var(--paper); }
/* 跳过去时标题要露在状态条 + 切换条下面 */
.wf-group { scroll-margin-top: calc(var(--v2top-h, 72px) + 100px); }   /* 切换条 69px + 约 30px 呼吸 */
.wf-group-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 20px;
  margin-bottom: clamp(22px, 3vw, 34px);
}
.wf-group-head .label + .label { color: var(--ink-45); }
.wf-shot .v2ph { border-radius: 0; aspect-ratio: 4 / 3; }
.wf-shot .v2ph.is-wide { aspect-ratio: 16 / 9; }
.wf-shot .v2ph.is-tall { aspect-ratio: 3 / 4; }

/* 学校组：三张并排，统一裁切 */
.wf.is-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.wf.is-3 .wf-shot { aspect-ratio: 4 / 3; }
.wf.is-3 .wf-shot img { height: 100%; object-fit: cover; }
.wf.is-3 .wf-meta { flex-direction: column; gap: 6px; }
.wf.is-3 .wf-m { text-align: left; white-space: normal; }
@media (max-width: 1080px) { .wf.is-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 860px)  { .wf.is-3 { grid-template-columns: 1fr; } }

/* 次要项目：一行一条，不占大卡 */
.wf-more { list-style: none; margin-top: clamp(40px, 5vw, 56px); border-top: 1px solid var(--ink-12); }
.wf-more li { border-bottom: 1px solid var(--ink-12); }
.wf-more a {
  display: flex; justify-content: space-between; align-items: baseline; gap: 20px;
  padding: 18px 0;
}
.wf-more li:not(:has(a)) { display: flex; justify-content: space-between; align-items: baseline; gap: 20px; padding: 18px 0; color: var(--ink-60); }
.wf-more-t { font-family: var(--f-body); font-size: 17px; color: inherit; }
.wf-more a { color: var(--ink); }
.wf-more a:hover .wf-more-t { text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 680px) { .wf-more li, .wf-more a { flex-direction: column; gap: 4px; } }

/* 大纲：每节写该放什么，不是正式文案 */
.outline { list-style: none; max-width: 68ch; }
.outline li {
  position: relative; padding-left: 22px;
  font-size: 17px; line-height: 1.62; color: var(--ink-80);
}
.outline li + li { margin-top: 10px; }
.outline li::before {
  content: ""; position: absolute; left: 0; top: .78em;
  width: 10px; height: 1px; background: var(--ink-45);
}
.outline + .v2ph, .v2ph + .outline, .v2ph + .v2ph, .h-sub + .outline { margin-top: 22px; }
.outline + .v2facts { margin-top: 34px; }
.v2chap + .v2chap { margin-top: clamp(40px, 5vw, 64px); }
.v2chap .h-sub { margin-bottom: 4px; }
.v2chap .label { color: var(--ink-45); }

/* 受保护项目的锁 */
.v2lock {
  max-width: 520px; padding: clamp(28px, 4vw, 44px);
  border-radius: 12px; background: var(--paper-warm);
}
.v2lock .h-sub { margin: 10px 0 8px; }
.v2lock-form { display: flex; gap: 10px; margin: 22px 0 14px; }
.v2lock-form input {
  flex: 1; min-width: 0; padding: 8px 17px; border-radius: 100px;
  border: 1px solid var(--ink-12); background: var(--paper);
  font-family: var(--f-ui); font-size: 14px; color: var(--ink);
}
.v2lock-form input:focus { outline: none; border-color: var(--ink-45); }
.v2lock-msg { font-family: var(--f-ui); font-size: 14px; color: var(--flag); margin-bottom: 14px; }

/* 正文里的并列项目块（实习 hub 与三个子页共用 .wf-part） */
.wf-feat-parts.is-flat { border-top: 0; padding-top: 0; margin-top: 0; }
.v2sib-back { margin-top: 26px; }

