/* ============================================================
   牛子旭 作品集 · 第二版
   art direction:「制图室 / DRAFTING」
     · 纸白到底 —— 全程不转暗，颜色只以「墨迹」的形式出现
     · 四支笔：群青 / 朱红 / 铜绿 / 信号黄，像绘图仪换笔，不混不叠
     · 一切可测量 —— 编号、尺寸线、坐标、卡尺读数
     · 字要细 —— 200–300 的中文大字在白纸上才有精度感
     · 动效语言是「描线 / 扫描 / 吸附」，不是「淡入上浮」
   ============================================================ */

/* ============================================================
   1. token
   ============================================================ */
:root {
  /* --- 纸 --- */
  --w:      #FFFFFF;
  --w-1:    #FAFAF9;   /* 面板 */
  --w-2:    #F1F1EF;   /* 凹槽 */
  --w-blue: #F3F5FF;   /* 蓝调纸 */
  --w-warm: #FCF8F2;   /* 暖调纸 */

  /* --- 墨 --- */
  --ink:    #111114;
  --ink-2:  #43434D;
  --ink-3:  #8A8A94;
  --ink-4:  #C4C4CC;
  --rule:   #E6E6E3;
  --rule-2: #F0F0EE;

  /* --- 四支笔 --- */
  --pen-1: #1B3BFF;   /* 群青：主结构线 */
  --pen-2: #FF3B1F;   /* 朱红：标注、关键值 */
  --pen-3: #00A98F;   /* 铜绿：状态、正向 */
  --pen-4: #FFC400;   /* 信号黄：网格、高亮 */

  /* --- 字 --- */
  --cjk:     'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC',
             'Microsoft YaHei', sans-serif;
  --display: 'Space Grotesk', 'Helvetica Neue', var(--cjk);
  --body:    'IBM Plex Sans', 'Helvetica Neue', var(--cjk);
  --mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* --- 8px 基线音阶：图纸上的一切都是这个数的整数倍 --- */
  --u:   8px;
  --u2: 16px;  --u3: 24px;  --u4:  32px;  --u5:  40px;
  --u6: 48px;  --u8: 64px;  --u10: 80px;  --u12: 96px;
  --u16:128px; --u20:160px; --u24: 192px;

  --gut:  clamp(16px, 3.4vw, 60px);
  --gap:  clamp(10px, 1.2vw, 22px);
  --band: clamp(64px, 9vw, 160px);   /* 区块上下留白 */

  /* --- 缓动：比第一版硬，机械感 --- */
  --snap: cubic-bezier(.2, .9, .18, 1);
  --draw: cubic-bezier(.65, 0, .35, 1);
  --out:  cubic-bezier(.33, 1, .68, 1);
}

/* ============================================================
   2. reset
   ============================================================ */
* { margin: 0; padding: 0; box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  background: var(--w);
  scroll-behavior: smooth;
}
body {
  font-family: var(--body);
  font-size: 16px;
  color: var(--ink);
  background: var(--w);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg, canvas, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
table { border-collapse: collapse; width: 100%; }
::selection { background: var(--pen-1); color: #fff; }
:focus-visible { outline: 2px solid var(--pen-1); outline-offset: 3px; }

/* 全站鼠标隐藏，交给跟随圆环；触屏和键盘用户不受影响 */
@media (hover: hover) and (pointer: fine) {
  html.js body, html.js body * { cursor: none; }
}

/* ============================================================
   3. 纸面 —— 每个区块自己带纸，不做整页换色
   ============================================================ */
.sheet { position: relative; padding-block: var(--band); }
.sheet[data-paper="off"]  { background: var(--w-1); }
.sheet[data-paper="slot"] { background: var(--w-2); }
.sheet[data-paper="blue"] { background: var(--w-blue); }
.sheet[data-paper="warm"] { background: var(--w-warm); }
.sheet + .sheet { border-top: 1px solid var(--rule); }
.wrap { padding-inline: var(--gut); }

/* 纸纹：极淡的纤维，只在白纸上，不用混合模式 */
body::after {
  content: ''; position: fixed; inset: 0; z-index: 9997; pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23f)'/%3E%3C/svg%3E");
}

/* ============================================================
   4. 构造网格 —— 按 G 或点导航里的按钮显示
   ============================================================ */
#grid {
  position: fixed; inset: 0; z-index: 8500; pointer-events: none;
  opacity: 0; transition: opacity .28s var(--snap);
}
html[data-grid="on"] #grid { opacity: 1; }
#grid .cols {
  position: absolute; inset: 0; padding-inline: var(--gut);
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
}
#grid .cols i { background: rgba(27, 59, 255, .055); }
#grid .base {
  position: absolute; inset: 0;
  background-image: repeating-linear-gradient(to bottom, rgba(255,196,0,.34) 0 1px, transparent 1px 8px);
  opacity: .42;
}
#grid .edge { position: absolute; inset: var(--gut); border: 1px solid rgba(255,59,31,.34); }
#grid .rd {
  position: absolute; left: var(--gut); bottom: calc(var(--gut) - 19px);
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em; color: var(--pen-2);
}
@media (max-width: 880px) { #grid .cols { grid-template-columns: repeat(6, 1fr); } }

/* ============================================================
   5. 光标：跟随的圆环
   环滞后，点跟得死 —— 差出来的那点距离就是「跟踪」的手感。
   环用 difference 混合：白纸上自动呈墨色，压到深色图版上自动转白，
   不必为每种底色单独配色。悬停时脱离混合，换成群青实色。
   ============================================================ */
.cur-ring, .cur-dot {
  position: fixed; top: 0; left: 0; z-index: 9600; pointer-events: none;
  border-radius: 50%;
  mix-blend-mode: difference;
}
.cur-ring {
  width: 30px; height: 30px; margin: -15px 0 0 -15px;
  border: 1px solid #fff;
  transition: width .3s var(--snap), height .3s var(--snap), margin .3s var(--snap),
              background-color .25s, border-color .25s, mix-blend-mode 0s .08s;
}
.cur-dot {
  width: 4px; height: 4px; margin: -2px 0 0 -2px; background: #fff;
  transition: opacity .2s;
}
.cur-read {
  position: fixed; top: 0; left: 0; z-index: 9602; pointer-events: none;
  font-family: var(--mono); font-size: 10px; letter-spacing: .04em;
  color: var(--ink-3); white-space: nowrap;
  transition: color .25s;
}
/* 带颜色时必须退出 difference，否则群青会被反色搅烂 */
html.hot .cur-ring {
  width: 62px; height: 62px; margin: -31px 0 0 -31px;
  mix-blend-mode: normal;
  border-color: var(--pen-1); background: rgba(27, 59, 255, .10);
}
html.hot .cur-dot { opacity: 0; }
html.hot .cur-read { color: var(--pen-1); }
@media (hover: none), (pointer: coarse) { .cur-ring, .cur-dot, .cur-read { display: none; } }

/* ============================================================
   6. 字
   中文大字压到 200–300，白纸上才有精度；标点开 palt。
   ============================================================ */
.t-hero, .t-1, .t-2, .t-3, .t-4, .lede {
  font-feature-settings: 'palt' 1; font-kerning: normal;
}
.t-hero {
  font-family: var(--display); font-weight: 300;
  font-size: clamp(3.4rem, 13vw, 13.5rem);
  line-height: .98; letter-spacing: -.045em;
}
.t-1 {
  font-family: var(--display); font-weight: 300;
  font-size: clamp(2rem, 5.6vw, 5rem);
  line-height: 1.1; letter-spacing: -.035em;
}
.t-2 {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.4rem, 3vw, 2.5rem);
  line-height: 1.24; letter-spacing: -.025em;
}
.t-3 {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(1.05rem, 1.6vw, 1.35rem);
  line-height: 1.4; letter-spacing: -.015em;
}
.t-4 { font-family: var(--body); font-weight: 500; font-size: .95rem; line-height: 1.5; }
.w-med  { font-weight: 500; }
.w-thin { font-weight: 200; }

.lede { font-size: clamp(1.02rem, 1.5vw, 1.32rem); line-height: 1.78; color: var(--ink-2); letter-spacing: .006em; }

/* 中文小字的三条规矩，跟拉丁不一样：
   一、字号不能照搬 —— 中文笔画密，13px 就开始糊，14px 是底线；
   二、行距要更松而不是更紧 —— 越小越需要行间的空气；
   三、层级靠字号和留白拉开，不靠把字调淡。
      「小 + 淡」会同时砍掉两个可读性维度，所以正文一律不淡于 --ink-2；
      --ink-3 只留给等宽的技术标签和第三级注释。 */
.p {
  font-size: 1rem; line-height: 1.85; color: var(--ink-2);
  letter-spacing: .008em; max-width: 36em;
}
.p-s {
  font-size: .9rem; line-height: 1.95; color: var(--ink-2);
  letter-spacing: .014em; max-width: 32em;
}
.p-xs {
  font-size: .82rem; line-height: 1.85; color: var(--ink-3);
  letter-spacing: .014em; max-width: 30em;
}
.measure { max-width: 34em; }
.measure-s { max-width: 22em; }

/* 技术标签 —— 全站所有小字都走这一个模子 */
.lbl {
  font-family: var(--mono); font-size: 11.5px; font-weight: 400;
  /* .1em 的字距配 RESERVE A ROOM 好看，配「房间是有属性的」就散了。
     折中到 .06em。uppercase 去掉 —— 该大写的地方标记里本来就是大写。 */
  letter-spacing: .06em; color: var(--ink-3);
}
.lbl.up { text-transform: uppercase; letter-spacing: .12em; }
.lbl-b { color: var(--pen-1); }
.lbl-r { color: var(--pen-2); }
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }

/* 编号牌：图版 / 图 / 表 都用它 */
.plate-no {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  color: var(--ink); border: 1px solid var(--ink); border-radius: 2px;
  padding: 3px 7px; white-space: nowrap;
}
.plate-no.b { color: var(--pen-1); border-color: var(--pen-1); }
.plate-no.r { color: var(--pen-2); border-color: var(--pen-2); }

/* 墨色强调 */
.ink-1 { color: var(--pen-1); }
.ink-2c { color: var(--pen-2); }
.ink-3c { color: var(--pen-3); }
.uline { box-shadow: inset 0 -1px 0 currentColor; }

/* ============================================================
   7. 版面
   ============================================================ */
.g {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
}
.c1{grid-column-end:span 1} .c2{grid-column-end:span 2} .c3{grid-column-end:span 3}
.c4{grid-column-end:span 4} .c5{grid-column-end:span 5} .c6{grid-column-end:span 6}
.c7{grid-column-end:span 7} .c8{grid-column-end:span 8} .c9{grid-column-end:span 9}
.c10{grid-column-end:span 10} .c11{grid-column-end:span 11} .c12{grid-column-end:span 12}
.s2{grid-column-start:2} .s3{grid-column-start:3} .s4{grid-column-start:4}
.s5{grid-column-start:5} .s6{grid-column-start:6} .s7{grid-column-start:7}
.s8{grid-column-start:8} .s9{grid-column-start:9} .s10{grid-column-start:10}
.s11{grid-column-start:11}

/* 区块抬头：编号 / 标题 / 说明，横向一条线串起来 */
.head { margin-bottom: var(--u10); }
.head .bar {
  display: flex; align-items: center; gap: var(--u3);
  padding-bottom: var(--u2); border-bottom: 1px solid var(--ink);
}
.head .bar .fill { flex: 1; height: 1px; background: var(--rule); }
.head h2 { margin-top: var(--u4); }
.head .sub { margin-top: var(--u2); }

@media (max-width: 880px) {
  .g { grid-template-columns: repeat(6, 1fr); }
  .c7,.c8,.c9,.c10,.c11,.c12 { grid-column-end: span 6; }
  .c4,.c5,.c6 { grid-column-end: span 6; }
  .s2,.s3,.s4,.s5,.s6,.s7,.s8,.s9,.s10,.s11 { grid-column-start: auto; }
}
@media (max-width: 600px) {
  .g { grid-template-columns: 1fr; }
  .g > * { grid-column: 1 / -1 !important; }
}

/* ============================================================
   8. 导航 —— 一条贴顶的仪表条
   ============================================================ */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 9000;
  display: flex; align-items: center; gap: var(--u3);
  padding: 0 var(--gut); height: 46px;
  background: rgba(255,255,255,.82);
  backdrop-filter: blur(12px) saturate(1.1); -webkit-backdrop-filter: blur(12px) saturate(1.1);
  border-bottom: 1px solid var(--rule);
}
.nav .id { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; }
.nav .id b { font-weight: 500; }
.nav .id span { color: var(--ink-3); }
.nav .sep { flex: 1; height: 1px; background: var(--rule); }
.nav ul { display: flex; gap: var(--u3); list-style: none; }
/* li 改 flex：否则 11px 的链接落在继承来的 16px 行框里，基线被往下带 2px，
   跟左边的名字、右边的「网格」按钮对不齐 */
.nav ul li { display: flex; align-items: center; }
.nav ul a {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; color: var(--ink-3);
  position: relative; padding-block: 3px; transition: color .25s;
}
.nav ul a:hover { color: var(--ink); }
.nav ul a[aria-current] { color: var(--ink); }
.nav ul a[aria-current]::before {
  content: ''; position: absolute; left: -9px; top: 50%; width: 4px; height: 4px;
  margin-top: -2px; background: var(--pen-2);
}
.gtoggle {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em;
  border: 1px solid var(--rule); border-radius: 2px; padding: 4px 9px;
  color: var(--ink-3); display: inline-flex; align-items: center; gap: 6px;
  transition: color .25s, border-color .25s, background-color .25s;
}
.gtoggle i { width: 6px; height: 6px; background: var(--ink-4); display: block; transition: background-color .25s; }
.gtoggle:hover { color: var(--ink); border-color: var(--ink-4); }
html[data-grid="on"] .gtoggle { color: var(--pen-1); border-color: var(--pen-1); }
html[data-grid="on"] .gtoggle i { background: var(--pen-1); }
@media (max-width: 720px) {
  .nav { gap: var(--u2); }
  .nav ul { gap: var(--u2); }
  .nav .id span, .gtoggle span { display: none; }
}

/* 顶部出图进度：页面在载入时像绘图仪走纸 */
#feed {
  position: fixed; top: 0; left: 0; height: 2px; width: 0; z-index: 9500;
  background: var(--pen-1); transition: width .3s linear, opacity .4s .2s;
}
#feed.done { opacity: 0; }

/* 换页：一张新纸从左边推进来，前缘是一条蓝墨线 */
#sheet {
  position: fixed; inset: 0; z-index: 9400; background: var(--w);
  transform: translateX(-100%); pointer-events: none;
  border-right: 2px solid var(--pen-1);
  display: grid; place-items: center;
}
#sheet::after {
  content: attr(data-to); font-family: var(--mono); font-size: 12px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-3);
  opacity: 0; transition: opacity .25s .18s;
}
#sheet.in { animation: sheetIn .5s var(--draw) forwards; }
#sheet.in::after { opacity: 1; }
#sheet.out { animation: sheetOut .6s var(--draw) forwards; }
@keyframes sheetIn  { from { transform: translateX(-100%); } to { transform: translateX(0); } }
@keyframes sheetOut { from { transform: translateX(0); }    to { transform: translateX(100%); } }

/* ============================================================
   9. 首屏
   ============================================================ */
.hero {
  position: relative; min-height: 100svh;
  padding-top: calc(46px + var(--u12)); padding-bottom: var(--u8);
  display: flex; flex-direction: column; justify-content: space-between;
  overflow: hidden;
}
#plot { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
.hero > * { position: relative; z-index: 2; }

.hero-name { position: relative; display: inline-block; }
.hero-sub { margin-top: var(--u4); }

/* 卡尺：给首屏大字量一个宽度，是装饰也是这套语言的注脚 */
.caliper { position: relative; margin-top: var(--u3); height: 22px; }
.caliper .ln {
  position: absolute; top: 10px; left: 0; height: 1px; background: var(--pen-2);
  transform-origin: left; transform: scaleX(0);
}
.js body.set .caliper .ln { animation: drawX 1s var(--draw) .9s forwards; }
.caliper .t, .caliper .tr {
  position: absolute; top: 3px; width: 1px; height: 15px; background: var(--pen-2);
  opacity: 0; transition: opacity .3s 1.6s;
}
.caliper .t { left: 0; } .caliper .tr { right: 0; }
.js body.set .caliper .t, .js body.set .caliper .tr { opacity: 1; }
.caliper .v {
  position: absolute; top: -3px; left: 50%; transform: translateX(-50%);
  background: var(--w); padding: 0 8px;
  font-family: var(--mono); font-size: 10.5px; color: var(--pen-2);
  opacity: 0; transition: opacity .35s 1.7s;
}
.js body.set .caliper .v { opacity: 1; }
@keyframes drawX { to { transform: scaleX(1); } }

.hero-foot {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
  align-items: end; padding-top: var(--u3); border-top: 1px solid var(--ink);
}
.hero-foot > * { grid-column-end: span 3; }
.hero-foot .fk { display: block; margin-bottom: 6px; }
.live { display: inline-flex; align-items: center; gap: 7px; }
.live i {
  width: 6px; height: 6px; background: var(--pen-3); display: block;
  animation: blip 1.8s steps(1) infinite;
}
@keyframes blip { 0%, 60% { opacity: 1; } 61%, 100% { opacity: .25; } }
/* 末项吊在最右两栏；用类而不是行内样式写，窄屏才覆盖得掉 */
.hero-foot > .last { grid-column: 11 / 13; text-align: right; }
@media (max-width: 880px) { .hero-foot > * { grid-column-end: span 3; } }
@media (max-width: 600px) {
  .hero-foot { grid-template-columns: 1fr 1fr; gap: var(--u3); }
  .hero-foot > * { grid-column: auto / span 1; }
  .hero-foot > .last { grid-column: auto / span 1; text-align: left; }
}

/* ============================================================
   10. 尺寸标注 —— hover 时给元素量一下
   ============================================================ */
[data-dim] { position: relative; }
[data-dim]::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: -14px; height: 1px;
  background: var(--pen-2); transform: scaleX(0); transform-origin: left;
  transition: transform .4s var(--draw); pointer-events: none;
}
[data-dim]::after {
  content: attr(data-dim); position: absolute; right: 0; bottom: -30px;
  font-family: var(--mono); font-size: 10px; color: var(--pen-2);
  opacity: 0; transition: opacity .3s .18s; pointer-events: none;
}
[data-dim]:hover::before { transform: scaleX(1); }
[data-dim]:hover::after  { opacity: 1; }

/* ============================================================
   11. 动效原语
   ============================================================ */
/* 吸附入场：从网格外「咔」地就位，不是慢慢淡进来 */
.js [data-snap] { opacity: 0; transform: translate3d(0, 22px, 0); }
.js body.set [data-snap].in {
  opacity: 1; transform: none;
  transition: opacity .38s var(--out), transform .52s var(--snap);
  transition-delay: calc(var(--d, 0) * 1ms);
}
.js [data-snap="left"]  { transform: translate3d(-26px, 0, 0); }
.js [data-snap="right"] { transform: translate3d(26px, 0, 0); }

/* 扫描揭幕：一条蓝墨线扫过，内容留在它身后 */
.js [data-scan] { position: relative; clip-path: inset(0 100% 0 0); }
.js body.set [data-scan].in { clip-path: inset(0 0 0 0); transition: clip-path .82s var(--draw); }
.js [data-scan]::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 1.5px;
  background: var(--pen-1); opacity: 0; pointer-events: none;
}
.js body.set [data-scan].in::after { animation: scanline .82s var(--draw); }
@keyframes scanline {
  0%   { left: 0;    opacity: 1; }
  92%  { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}

/* 描线：SVG 路径自己画出来 */
.js [data-draw] path, .js [data-draw] line,
.js [data-draw] circle, .js [data-draw] rect, .js [data-draw] polyline {
  stroke-dasharray: var(--len, 400); stroke-dashoffset: var(--len, 400);
}
.js body.set [data-draw].in path, .js body.set [data-draw].in line,
.js body.set [data-draw].in circle, .js body.set [data-draw].in rect, .js body.set [data-draw].in polyline {
  animation: drawPath 1.1s var(--draw) forwards;
  animation-delay: calc(var(--i, 0) * 110ms);
}
@keyframes drawPath { to { stroke-dashoffset: 0; } }

/* 逐行推入 —— 每行自己包一个 span，JS 只套遮罩 */
.js [data-push] > * { display: block; overflow: hidden; }
.js [data-push] > * > span { display: block; transform: translateY(102%); }
.js body.set [data-push].in > * > span {
  transform: none; transition: transform .68s var(--snap);
}
.js body.set [data-push].in > *:nth-child(2) > span { transition-delay: .06s; }
.js body.set [data-push].in > *:nth-child(3) > span { transition-delay: .12s; }
.js body.set [data-push].in > *:nth-child(4) > span { transition-delay: .18s; }
.js body.set [data-push].in > *:nth-child(5) > span { transition-delay: .24s; }

/* ============================================================
   12. 索引表 —— 能做什么，用表格说
   ============================================================ */
.manifest th, .manifest td {
  text-align: left; padding: var(--u3) var(--u2) var(--u3) 0;
  border-bottom: 1px solid var(--rule);
  /* 基线对齐，不用 top：编号是等宽小字、名称是 t-3、说明是正文，三种行高不同，
     顶对齐时第一行的基线会错开 1–4px，编号看起来比名称高一截 */
  vertical-align: baseline;
}
.manifest thead th { padding-block: var(--u2); border-bottom: 1px solid var(--ink); }
.manifest tbody tr { transition: background-color .25s; }
.manifest tbody tr:hover { background: var(--w-1); }
.manifest .no { width: 56px; color: var(--ink-3); }
.manifest .nm { width: 26%; }
.manifest .yr { width: 92px; text-align: right; color: var(--ink-3); }
.manifest tbody tr:hover .no { color: var(--pen-2); }
@media (max-width: 720px) {
  .manifest .yr, .manifest thead { display: none; }
  .manifest .nm { width: auto; }
  .manifest th, .manifest td { display: block; padding: 0; border: 0; }
  .manifest tbody tr { display: block; padding: var(--u3) 0; border-bottom: 1px solid var(--rule); }
  .manifest .no { margin-bottom: 6px; }
  .manifest .ds { margin-top: 6px; }
}

/* ============================================================
   13. 图版 —— 跟着竖向滚动横着走
   ============================================================ */
.hs { position: relative; }
.hs .vp {
  position: sticky; top: 0; height: 100svh;
  display: flex; align-items: center; overflow: hidden;
}
.hs .rail {
  display: flex; align-items: stretch; gap: clamp(20px, 3vw, 56px);
  padding-inline: var(--gut); will-change: transform;
}
.hs .hint {
  position: absolute; left: var(--gut); bottom: var(--u6); z-index: 3;
  display: flex; align-items: center; gap: 10px;
}
.hs .hint .rail-bar { width: clamp(80px, 14vw, 200px); height: 1px; background: var(--rule); position: relative; }
.hs .hint .rail-bar i { position: absolute; inset: 0 auto 0 0; background: var(--pen-1); width: 0; }

.plate { flex: 0 0 auto; width: clamp(240px, 30vw, 440px); display: flex; flex-direction: column; }
.plate .frame {
  position: relative; overflow: hidden; border: 1px solid var(--rule);
  background: var(--w-1); aspect-ratio: 4 / 5;
}
.plate .frame .art { position: absolute; inset: 0; }
.plate .frame .rulemark {
  position: absolute; left: 10px; top: 10px; z-index: 2;
  font-family: var(--mono); font-size: 10px; color: var(--w);
  mix-blend-mode: difference;
}
.plate .cap { display: flex; justify-content: space-between; align-items: baseline; gap: var(--u2); margin-top: var(--u2); }
.plate .cap h3 { font-family: var(--display); font-weight: 400; font-size: 1.15rem; letter-spacing: -.02em; }
.plate .meta { margin-top: 4px; }
.plate .frame::after {
  content: ''; position: absolute; inset: 0; z-index: 3; border: 1px solid var(--pen-1);
  opacity: 0; transition: opacity .3s;
}
.plate:hover .frame::after { opacity: 1; }
.plate.tall  { width: clamp(240px, 24vw, 340px); }
.plate.tall .frame { aspect-ratio: 3 / 5; }
.plate.wide  { width: clamp(280px, 42vw, 620px); }
.plate.wide .frame { aspect-ratio: 16 / 10; }
@media (max-width: 880px) {
  .hs .vp { position: static; height: auto; display: block; }
  .hs .rail { flex-direction: column; transform: none !important; }
  .hs .hint { display: none; }
  /* 展台的 display:flex 写在内联样式里，class 规则盖不住 —— 必须 !important，
     否则导轨虽然竖排了，里面的机器还是横排，一半落在屏幕外。 */
  .hs .stage3d { display: block !important; }
  .hs .dvx-slot { margin-inline: auto; }
  .plate, .plate.tall, .plate.wide { width: 100%; }
}

/* ============================================================
   14. 方法 —— 四张会自己画出来的小图
   ============================================================ */
.steps { counter-reset: st; }
.step { padding-top: var(--u4); border-top: 1px solid var(--ink); }
.step .dia { margin: var(--u4) 0 var(--u3); }
.step .dia svg { width: 100%; height: auto; overflow: visible; }
.step .dia [stroke] { stroke-linecap: round; stroke-linejoin: round; fill: none; }
.step h3 { margin-bottom: var(--u2); }

/* ============================================================
   15. 实测 —— 卡尺读数
   ============================================================ */
.gauges { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); }
.gauge { grid-column-end: span 3; position: relative; padding-top: var(--u3); }
.gauge::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: var(--ink); }
.gauge .v {
  font-family: var(--display); font-weight: 200;
  font-size: clamp(3rem, 7vw, 6rem); line-height: 1;
  letter-spacing: -.05em; font-variant-numeric: tabular-nums;
}
.gauge .v sup { font-size: .3em; vertical-align: super; font-weight: 400; letter-spacing: 0; color: var(--pen-2); }
.gauge .k { margin-top: var(--u2); }
.gauge .d { margin-top: 6px; max-width: 15em; }
/* 刻度 */
.gauge .scale { display: flex; gap: 3px; margin-top: var(--u3); align-items: flex-end; height: 14px; }
.gauge .scale i { width: 1px; background: var(--ink-4); height: 6px; }
.gauge .scale i:nth-child(5n+1) { height: 14px; background: var(--ink-3); }
@media (max-width: 880px) { .gauge { grid-column-end: span 3; } }
@media (max-width: 600px) { .gauges { grid-template-columns: 1fr; } .gauge { grid-column-end: span 1; } }

/* ============================================================
   16. 按钮与链接
   ============================================================ */
.act {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em;
  padding: 11px 16px; border: 1px solid var(--ink); border-radius: 2px;
  position: relative; overflow: hidden; isolation: isolate;
  transition: color .3s var(--snap), border-color .3s;
}
.act > * { position: relative; z-index: 2; }
.act::before {
  content: ''; position: absolute; inset: 0; z-index: 1; background: var(--ink);
  transform: translateX(-101%); transition: transform .42s var(--draw);
}
.act:hover { color: var(--w); }
.act:hover::before { transform: translateX(0); }
.act.blue { border-color: var(--pen-1); color: var(--pen-1); }
.act.blue::before { background: var(--pen-1); }
/* 必须单独写一条：.act:hover 在上面，和 .act.blue 优先级相同，会被它盖掉 ——
   结果悬停时群青底上还是群青字，整行看不见 */
.act.blue:hover { color: var(--w); }
.act .arw { transition: transform .42s var(--snap); }
.act:hover .arw { transform: translateX(4px); }

.lk { position: relative; color: var(--ink-2); transition: color .25s; }
.lk::after {
  content: ''; position: absolute; left: 0; bottom: -2px; height: 1px; width: 100%;
  background: currentColor; transform: scaleX(0); transform-origin: right;
  transition: transform .38s var(--draw);
}
.lk:hover { color: var(--ink); }
.lk:hover::after { transform: scaleX(1); transform-origin: left; }

/* ============================================================
   17. 版权页
   ============================================================ */
.colophon { padding: var(--u12) var(--gut) var(--u6); border-top: 1px solid var(--ink); }
.colophon .big {
  font-family: var(--display); font-weight: 200;
  font-size: clamp(2.4rem, 9vw, 8rem); line-height: 1; letter-spacing: -.05em;
  display: inline-block;
}
.colophon .rows { margin-top: var(--u12); }
.crow {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
  padding: var(--u2) 0; border-top: 1px solid var(--rule); align-items: baseline;
}
.crow > .k { grid-column-end: span 2; }
.crow > .v { grid-column-end: span 6; }
.crow > .x { grid-column: 11 / 13; text-align: right; }
.keys { display: flex; gap: var(--u2); flex-wrap: wrap; align-items: center; }
kbd {
  font-family: var(--mono); font-size: 10px; border: 1px solid var(--ink-4);
  border-bottom-width: 2px; border-radius: 3px; padding: 2px 6px; color: var(--ink-2);
}
@media (max-width: 880px) {
  .crow { grid-template-columns: 1fr; gap: 4px; }
  .crow > *, .crow > .x { grid-column: 1 / -1; text-align: left; }
}

/* ============================================================
   18. 设计系统页的零件
   ============================================================ */
.swatch { border: 1px solid var(--rule); }
.swatch .chip { height: clamp(80px, 11vw, 140px); }
.swatch .info { padding: var(--u2); display: flex; justify-content: space-between; gap: var(--u2); }
.spec { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); padding: var(--u3) 0; border-top: 1px solid var(--rule); align-items: baseline; }
.spec .n { grid-column-end: span 2; }
.spec .b { grid-column-end: span 8; }
.spec .m { grid-column: 11 / 13; text-align: right; }
.bar-demo { height: 8px; background: var(--pen-1); }
.ease-box { border: 1px solid var(--rule); padding: var(--u3); }
.ease-box .track { position: relative; height: 2px; background: var(--rule); margin-top: var(--u4); }
.ease-box .track i { position: absolute; top: -4px; left: 0; width: 10px; height: 10px; background: var(--pen-1); }
@media (max-width: 880px) {
  .spec .n, .spec .b { grid-column-end: span 6; }
  .spec .m { grid-column: auto / span 6; text-align: left; }
}

/* ============================================================
   19. 减少动效
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; transition-duration: .001ms !important;
    animation-iteration-count: 1 !important;
  }
  .js [data-snap] { opacity: 1 !important; transform: none !important; }
  .js [data-scan] { clip-path: none !important; }
  .js [data-push] > * > span { transform: none !important; }
  .js [data-draw] path, .js [data-draw] line, .js [data-draw] circle,
  .js [data-draw] rect, .js [data-draw] polyline { stroke-dashoffset: 0 !important; }
  .cur-ring, .cur-dot, .cur-read { display: none !important; }
  html.js body, html.js body * { cursor: auto !important; }
}

/* ============================================================
   20. 目录页 —— 表格 + 悬停预览
   ============================================================ */
.fbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.fbtn {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em;
  padding: 7px 13px; border: 1px solid var(--rule); border-radius: 2px; color: var(--ink-3);
  transition: color .25s, border-color .25s, background-color .25s;
}
.fbtn:hover { color: var(--ink); border-color: var(--ink-4); }
.fbtn[aria-pressed="true"] { background: var(--ink); color: var(--w); border-color: var(--ink); }

.cat th, .cat td {
  text-align: left; padding: var(--u3) var(--u2) var(--u3) 0;
  border-bottom: 1px solid var(--rule); vertical-align: baseline;
}
.cat thead th { border-bottom: 1px solid var(--ink); padding-block: var(--u2); }
.cat tbody tr { transition: background-color .2s; }
.cat tbody tr:hover { background: var(--w-1); }
.cat tbody tr:hover .c-no { color: var(--pen-2); }
.cat tbody tr.off { display: none; }
.cat .c-no { width: 60px; color: var(--ink-3); }
.cat .c-ct { width: 24%; }
.cat .c-yr { width: 76px; color: var(--ink-3); }
.cat .c-st { width: 92px; text-align: right; }
.cat a { display: block; }
.dot { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.dot i { width: 6px; height: 6px; display: block; flex: none; }

.preview {
  position: sticky; top: calc(46px + var(--u5));
  display: block; border: 1px solid var(--rule); background: var(--w-1);
  aspect-ratio: 4 / 5; overflow: hidden;
}
.preview .slot { position: absolute; inset: 0; opacity: 0; transition: opacity .32s var(--out); }
.preview .slot.on { opacity: 1; }
.preview .slot svg { width: 100%; height: 100%; }
/* 真实截图是 1:2 的手机比例，放进 4:5 的框里只能裁 —— 从顶部裁，
   首屏的标题区正好留在画面里 */
.preview .slot img { width: 100%; height: 100%; object-fit: cover; object-position: center top; }
.plate .frame img.art { object-fit: cover; object-position: center top; }
.preview .idle {
  position: absolute; inset: 0; display: grid; place-items: center;
  transition: opacity .32s; text-align: center; padding: var(--u3);
}
.preview.armed .idle { opacity: 0; }
.preview .tagline {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  padding: var(--u2); border-top: 1px solid var(--rule); background: var(--w);
  display: flex; justify-content: space-between; gap: var(--u2);
}
@media (max-width: 880px) {
  .preview { display: none; }
  .cat .c-ct, .cat .c-st { display: none; }
}

/* 图版网格（目录页底部，跟首页的横向图版换个呈现） */
.pgrid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); }
.pgrid .plate { width: auto; }
@media (max-width: 600px) { .pgrid { grid-template-columns: 1fr; } .pgrid > * { grid-column: 1 / -1 !important; } }

/* ============================================================
   21. 关于页 —— 雷达图与技术时间轴
   ============================================================ */
.radar { width: 100%; height: auto; overflow: visible; }
.radar .web { stroke: var(--ink-4); fill: none; stroke-width: 1; }
.radar .axis { stroke: var(--rule); stroke-width: 1; }
.radar .shape { fill: rgba(27,59,255,.09); stroke: var(--pen-1); stroke-width: 1.4; }
.radar .node { fill: var(--pen-2); }
.radar text { font-family: var(--mono); font-size: 10px; fill: var(--ink-3); letter-spacing: .06em; }

.axisline { position: relative; padding-left: var(--u6); }
.axisline::before { content: ''; position: absolute; left: 12px; top: 6px; bottom: 6px; width: 1px; background: var(--ink); }
.era { position: relative; padding: var(--u4) 0; }
.era::before {
  content: ''; position: absolute; left: -42px; top: calc(var(--u4) + .45em);
  width: 13px; height: 1px; background: var(--ink);
}
.era::after {
  content: ''; position: absolute; left: -46px; top: calc(var(--u4) + .45em - 3px);
  width: 7px; height: 7px; border: 1px solid var(--ink); background: var(--w);
}
.era:first-child::after { background: var(--pen-2); border-color: var(--pen-2); }
.era .yr { display: block; margin-bottom: var(--u2); }
@media (max-width: 600px) {
  .axisline { padding-left: var(--u4); }
  .era::before { left: -26px; width: 9px; } .era::after { left: -30px; }
}

.kvrow {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
  padding: var(--u3) 0; border-top: 1px solid var(--rule); align-items: baseline;
}
.kvrow .k { grid-column-end: span 3; }
.kvrow .v { grid-column-end: span 9; }
@media (max-width: 600px) { .kvrow { grid-template-columns: 1fr; gap: 4px; } .kvrow .k, .kvrow .v { grid-column: 1 / -1; } }

/* ============================================================
   22. 案例页 —— 技术报告体例
   ============================================================ */
.figure { margin-block: var(--u8); }
.figure .box { border: 1px solid var(--rule); overflow: hidden; position: relative; background: var(--w-1); }
.figure .box svg { width: 100%; height: 100%; display: block; }
.figure figcaption {
  display: flex; gap: var(--u3); align-items: baseline;
  margin-top: var(--u2); padding-top: var(--u2); border-top: 1px solid var(--rule);
}
.abstract { border-left: 2px solid var(--pen-1); padding-left: var(--u3); }
.finding { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); padding: var(--u4) 0; border-top: 1px solid var(--rule); }
.finding .n { grid-column-end: span 1; }
.finding .h { grid-column-end: span 4; }
.finding .b { grid-column-end: span 6; }
@media (max-width: 880px) {
  .finding { grid-template-columns: 1fr; gap: var(--u2); }
  .finding > * { grid-column: 1 / -1 !important; }
}

/* ============================================================
   23. iPhone 三维展台
   不做照片级渲染 —— 那跟这一版的语言不搭。这里是「图纸里的机身」：
   发丝轮廓、看得见的机身厚度、底下一道尺寸线。
   真 3D：preserve-3d 撑起前后两面，左右两条侧壁把厚度接起来，
   转到一定角度就能看出这是个有体积的东西，不是贴图。
   ============================================================ */
.stage3d {
  perspective: 1500px;
  perspective-origin: 50% 42%;
}
.dev {
  /* --dev-w 不在这里声明：一旦声明就会盖掉祖先传下来的值，外面便调不动尺寸。
     只给 width 一个兜底，变量留给调用方设。 */
  --dev-r: calc(var(--dev-w, 240px) * .118);  /* 必须是长度：百分比圆角横竖半径不等，拐角会变椭圆 */
  --dev-t: 8px;            /* 机身半厚 */
  position: relative;
  width: var(--dev-w, clamp(190px, 20vw, 280px));
  aspect-ratio: 390 / 844;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 4deg)) rotateY(var(--ry, -17deg)) rotateZ(var(--rz, 0deg));
  will-change: transform;
}
.dev-face, .dev-back {
  position: absolute; inset: 0;
  border-radius: var(--dev-r);
}
.dev-face {
  transform: translateZ(var(--dev-t));
  background: var(--w);
  border: 1px solid var(--ink);
  overflow: hidden;
  display: flex;
}
.dev-back {
  transform: translateZ(calc(var(--dev-t) * -1));
  background: var(--w-2);
  border: 1px solid var(--ink-4);
}
/* 侧壁：绕自身边缘转 90° 立起来，再往回挪半个厚度，正好卡在前后面之间 */
.dev-edge {
  position: absolute;
  top: var(--dev-r); bottom: var(--dev-r);
  width: calc(var(--dev-t) * 2);
  background: linear-gradient(to right, var(--w-2), var(--ink-4) 55%, var(--w-2));
}
.dev-edge.l {
  left: 0; transform-origin: left center;
  transform: translateZ(calc(var(--dev-t) * -1)) rotateY(-90deg);
}
.dev-edge.r {
  right: 0; transform-origin: right center;
  transform: translateZ(calc(var(--dev-t) * -1)) rotateY(90deg);
}
/* 屏幕本体 */
.dev-screen {
  flex: 1; position: relative; overflow: hidden;
  border-radius: calc(var(--dev-r) - 3px);
  margin: 3px;
  background: var(--w-1);
}
.dev-screen img, .dev-screen svg { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 灵动岛 */
.dev-face::after {
  content: ''; position: absolute; z-index: 3;
  top: 2.4%; left: 50%; transform: translateX(-50%);
  width: 27%; height: 3.1%;
  background: var(--ink); border-radius: 999px;
}
/* 落地影：让它看起来是浮着的，而不是贴在纸上 */
.dev-shadow {
  position: absolute; z-index: -1;
  left: 6%; right: 6%; bottom: -5%; height: 7%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(17,17,20,.20), transparent 72%);
  filter: blur(6px);
  transform: rotateX(84deg) translateZ(-14px);
}
/* 机身尺寸标注 */
.dev-dim {
  position: absolute; left: 0; right: 0; bottom: -34px;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em; color: var(--pen-2);
}
.dev-dim::before, .dev-dim::after { content: ''; flex: 1; height: 1px; background: var(--pen-2); }

/* 展台上的排布 */
.devrow { display: flex; align-items: center; gap: clamp(24px, 4vw, 72px); }
.devrow .dev { flex: 0 0 auto; }
/* 交错抬升，一排机器才有纵深，不是一条直线 */
.devrow .dev:nth-child(even) { --rx: 7deg; --ry: -13deg; margin-top: clamp(20px, 4vw, 64px); }
.devrow .dev:nth-child(3n)   { --ry: -21deg; }

@media (prefers-reduced-motion: reduce) {
  .dev { transition: none !important; }
}
@media (max-width: 600px) {
  .stage3d { --dev-w: min(62vw, 240px); perspective: 900px; }
}

/* ============================================================
   24. 案例作用域配色 —— 衣橱 / OOTD
   色值不是我挑的，是从设计稿的像素里取出来的：
   天青 #05BAE0 是主色，暖黄 #F8B838 专门标 AI 功能，
   珊瑚 #D8503F 标商业推荐，深藏青 #10203A 是机身与正文。
   只覆盖 token，不改任何组件 —— 整页因此换了一身皮，版式一行没动。
   ============================================================ */
body[data-case="closet"] {
  /* 取自作品集第 16 页「产品色彩 · COLOR」的官方定义，不再靠像素取样：
     Ink #090909 核心文字/主按钮 · Cyan #05BAE0 主操作/选中态
     Ice #ECFAFF 页面底色/信息层 · Lime #B8FF00 高光提示/与生成相关的关键信息 */
  --ink:    #090909;
  --ink-2:  #3D4A55;
  --ink-3:  #7C8A96;
  --ink-4:  #BDCAD4;
  --rule:   #DCE8F0;
  --rule-2: #EDF5FA;
  --w-1:    #F7FCFF;
  --w-2:    #E4F4FB;
  --w-blue: #ECFAFF;   /* Ice */
  --w-warm: #F7FFE5;   /* 柠檬绿的极淡版，用于「生成相关」的信息底 */
  --pen-1:  #05BAE0;   /* Cyan：主操作、选中态、结构线 */
  --pen-2:  #D8503F;   /* 珊瑚：商业推荐、警示 */
  --pen-3:  #17B39A;   /* 青绿：状态、通过 */
  --pen-4:  #7A9E00;   /* 深柠檬：需要当文字读时的柠檬绿 */
  --lime:   #B8FF00;   /* Lime 本色：只做高光与荧光笔，不做文字 */
}
body[data-case="closet"] ::selection { background: var(--pen-1); color: #fff; }

/* ============================================================
   25. 三维展台 · 平面图版（保留）
   给「已经是成品渲染图」的素材用 —— 比如从 Blender / Spline / Rotato
   导出的机身渲染。那种图本身已有光影，不该再套 CSS 机身，只需要摆位。
   衣橱案例现在用的是下面第 26 节那套有材质的机身。
   设计稿自带机身外壳，而且四周是透明的 —— 那就别再套一层我的边框，
   直接把整张 PNG 当成机身来转。厚度靠背面那张压暗的同图撑出来：
   一转角度就从边缘露出来，读作机身侧壁。
   注意 filter 会打断 3D 渲染上下文，所以投影只能加在叶子节点的 img 上，
   不能加在带 preserve-3d 的容器上。
   ============================================================ */
.slab {
  position: relative;
  width: var(--slab-w, clamp(190px, 21vw, 290px));
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 4deg)) rotateY(var(--ry, -16deg));
  will-change: transform;
}
.slab img { width: 100%; height: auto; display: block; }
.slab .fore {
  position: relative; z-index: 2;
  transform: translateZ(9px);
  filter: drop-shadow(0 22px 26px rgba(16, 32, 58, .22));
}
.slab .aft {
  position: absolute; inset: 0; z-index: 1;
  transform: translateZ(-9px);
  filter: brightness(.38) saturate(.35);
}
.slab-cap {
  position: absolute; left: 0; right: 0; bottom: -30px;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em; color: var(--pen-2);
}
.slab-cap::after { content: ''; flex: 1; height: 1px; background: var(--pen-2); opacity: .5; }

/* ------------------------------------------------------------
   封面样机 —— 素材本身已经是渲染好的立体图。
   只做摆位：不套 CSS 机身，更不能再叠一层 rotateX/rotateY，
   否则渲染图自带的透视和 CSS 的透视会打架，出现两套灭点。
   这类图的机身在底边是被切断的，所以必须把图钉在区块分隔线上，
   那道切口才读作「立在桌沿」，而不是「图没切干净」。
   ------------------------------------------------------------ */
.cover-g   { align-items: end; }
.cover-txt { padding-bottom: var(--u10); }
.cover-rig { position: relative; align-self: end; }
.cover-rig img { width: 100%; height: auto; }

/* 图版号 + 图注，压在图的上沿，和 .phead 同一套语言 */
.cover-cap {
  display: flex; align-items: baseline; gap: 10px;
  margin-bottom: var(--u3);
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em;
  color: var(--ink-3);
}
.cover-cap b { font-weight: 400; color: var(--pen-1); }
.cover-cap::after { content: ''; flex: 1; height: 1px; background: var(--rule); }

@media (max-width: 880px) {
  .cover-txt { padding-bottom: var(--u8); }
}

/* ------------------------------------------------------------
   样机图版 —— 成品渲染图自带一块纯色底板，抠不掉：机身的铝合金
   中框和屏幕里的浅灰卡片，跟底色在数值上无法区分，一抠就穿帮
   （试过，中框会被削掉一条）。
   所以不抠，改成「图版」：给它细线框、图版号和图注，那块底
   就当成图版的纸。这本来就是制图室该有的语言。
   底色写在 --rig-bg 上，好跟素材自带的底对齐。
   ------------------------------------------------------------ */
.rig { position: relative; }
.rig .rig-plate {
  position: relative; overflow: hidden;
  background: var(--rig-bg, #F4F4F4);
  border: 1px solid var(--rule);
}
.rig .rig-plate img { width: 100%; height: auto; }

/* 左上角图版号，压在图上，和 .figslot 的 .no 同一套 */
.rig .rig-plate .no {
  position: absolute; left: 0; top: 0; z-index: 2;
  padding: 5px 9px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  color: var(--ink-3); background: var(--w-1);
  border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
}

/* 占位提示：换成真实渲染图后，把 data-todo 属性删掉，角标就没了 */
.rig[data-todo] .rig-plate::after {
  content: attr(data-todo);
  position: absolute; right: 0; bottom: 0; z-index: 2;
  padding: 5px 9px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em;
  color: var(--pen-2); background: var(--w-1);
  border-left: 1px solid var(--rule); border-top: 1px solid var(--rule);
}

.rig-cap {
  display: flex; align-items: baseline; gap: 10px; margin-top: 10px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em; color: var(--ink-3);
}
.rig-cap b { font-weight: 400; color: var(--pen-1); }
.rig-cap::after { content: ''; flex: 1; height: 1px; background: var(--rule); }

/* ------------------------------------------------------------
   .dvx —— 总览横滑条里的机器。要同时满足两件事：
   看起来是真机身，而且角度得跟着鼠标慢慢转。
   这两条把「把透视烘进图里」的做法排除掉了 —— 烘死了就转不动。

   所以：**正面机身用现成素材，转角交给 CSS**。
   机身是 webmobilefirst 的免费透明 PNG（可商用、无需署名），
   屏幕镂空，截图垫在下面保持原生分辨率，一点不糊。

   厚度不画几何，用一招：把同一张机身图复制一份推到 Z 轴后方并压暗。
   转起来时沿着一侧露出的那条就是机身侧面 —— 轮廓天然吻合，
   因为它本来就是同一个形状。手画侧壁那条路已经证明不好看。
   ------------------------------------------------------------ */
/* 灭点必须落在每台机器自己身上。
   之前 perspective 挂在 .stage3d 上 —— 那是整条 2500px 宽的导轨，
   灭点在导轨中心，于是离中心越远的机器，translateZ 投影出的横向位移越大，
   「厚度」直接变成一台完整的重影。所以外面套一层 .dvx-slot 自带 perspective。 */
.dvx-slot {
  position: relative;              /* z-index 要靠它才生效 */
  flex: 0 0 auto;
  /* 尺寸同时受宽度和高度约束：机身高是宽的 2.046 倍，鼠标经过时还要放大 1.24×，
     只按 vw 取值的话，矮窗口里会被 .vp（100svh）裁掉底部。
     所以取 min(按宽, 按高)，33svh 是留足放大余量后的上限。 */
  width: var(--dvx-w, min(clamp(190px, 21vw, 310px), 33svh));
  margin-bottom: 44px;
  perspective: 760px;
  perspective-origin: 50% 50%;
  /* 放大走 transform，不进布局 —— 邻居不会被推开，只会被盖住 */
  transform: scale(var(--k, 1));
  transform-origin: 50% 56%;
}
.dvx {
  position: relative; width: 100%;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 3deg)) rotateY(var(--ry, 0deg));
  will-change: transform;
}
.dvx-face, .dvx-back { width: 100%; height: auto; display: block; }
.dvx-face { position: relative; z-index: 3; }
/* 推到后方并压暗的同一张图 —— 转角时露出的那一条就是厚度 */
.dvx-back {
  position: absolute; inset: 0; z-index: 0;
  transform: translateZ(calc(var(--dvx-w, 200px) * -.072));
  filter: brightness(.42) saturate(.35);
}
/* 屏幕：inset 是从素材上量出来的（左 4.859% 右 4.604% 上下各 1.875%） */
.dvx-scr {
  position: absolute; z-index: 2;
  left: 4.859%; right: 4.604%; top: 1.875%; bottom: 1.875%;
  overflow: hidden;
  border-radius: calc(var(--dvx-w, 200px) * .105);
  background: var(--w-2);
}
.dvx-scr img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 投影只能加在叶子上，加在 preserve-3d 容器上会把 3D 压平 */
.dvx-shadow {
  position: absolute; z-index: -1;
  left: 8%; right: 8%; bottom: -4%; height: 8%;
  background: radial-gradient(ellipse at center, rgba(16,32,58,.30), transparent 70%);
  filter: blur(6px);
}
.dvx-cap {
  position: absolute; left: 5%; right: 0; bottom: -30px; z-index: 4;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em; color: var(--pen-2);
  white-space: nowrap;
}
.dvx-cap::after { content: ''; flex: 1; height: 1px; background: var(--pen-2); opacity: .5; }
@media (max-width: 600px) { .hs .rail { --dvx-w: min(74vw, 300px); } }

/* 雏菊用的「时刻窗」：同一套图版组件，改成竖裁。
   衣橱是 4:3 横图版、7 栏、左右交替；雏菊是 3:4 竖窗、贴边、
   沿时间轴一路排下去 —— 两个案例的图片语言就此分开。 */
.rig.win .rig-plate { aspect-ratio: 3 / 4; }
.rig.win .rig-plate img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
}

/* 一条决策链：三台机器 + 中间的流程箭头 */
.chain { display: flex; align-items: center; justify-content: center; gap: clamp(8px, 2vw, 34px); flex-wrap: wrap; }
.chain .arrow {
  flex: 0 0 auto; display: grid; place-items: center; gap: 6px;
  color: var(--pen-1); align-self: center; padding-bottom: 40px;
}
.chain .arrow svg { display: block; }
.chain .arrow span { font-family: var(--mono); font-size: 9.5px; letter-spacing: .08em; color: var(--ink-3); white-space: nowrap; }
@media (max-width: 880px) {
  .chain { flex-direction: column; }
  .chain .arrow { padding: 0; transform: rotate(90deg); margin-block: var(--u5); }
}

/* 取自设计稿的色板 */
.pal { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); }
.pal .sw { grid-column-end: span 2; }
.pal .sw .cap { height: clamp(64px, 8vw, 104px); border-radius: 3px; border: 1px solid rgba(16,32,58,.08); }
.pal .sw .nm { margin-top: var(--u2); display: block; }
.pal .sw .hx { display: block; margin-top: 2px; }
@media (max-width: 880px) { .pal { grid-template-columns: repeat(3, 1fr); } .pal .sw { grid-column-end: span 1; } }

/* 展台横向排布时给标注留出下方空间 */
.hs .rail .slab, .hs .rail .ip { margin-bottom: 44px; }

/* ============================================================
   26. iPhone 机身 · 有材质的那一版
   前一版是「把平面图斜过去」，怎么转都没有体积。这一版按真机
   的构造搭：前玻璃、中框、背板三层，中框是绕自身边缘立起来的
   实体面，上面挂着音量键和电源键。
   受光统一按「左上打光」处理：左侧中框暗、右侧中框亮，
   前脸沿外沿留一道倒角高光 —— 金属感全在那一道线上。

   设计稿自带一层扁平机身，所以屏幕要先裁出来：
   实测屏幕在 534×1048 的图里占 x 47–486 / y 31–980。
   ============================================================ */
.ip {
  /* --ip-w 不在这里声明，否则会盖掉祖先传下来的值，外面就调不动尺寸 */
  --ip-t: calc(var(--ip-w, 240px) * .058);    /* 半厚：真机 8.25/71.5 ≈ 11.5% 宽，取半 */
  --ip-r: calc(var(--ip-w, 240px) * .152);    /* 圆角必须是长度，百分比会把拐角拉成椭圆 */
  --ip-bz: calc(var(--ip-w, 240px) * .028);   /* 边框 13/466 */
  position: relative;
  width: var(--ip-w, clamp(180px, 19vw, 264px));
  aspect-ratio: 466 / 976;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 5deg)) rotateY(var(--ry, -20deg));
  will-change: transform;
}

/* 前脸：黑边框 + 屏幕，外沿一道倒角高光 */
.ip-front {
  position: absolute; inset: 0; z-index: 3;
  transform: translateZ(var(--ip-t));
  border-radius: var(--ip-r);
  background: #080B10;
  padding: var(--ip-bz);
  box-shadow:
    inset 0 0 0 .5px rgba(255,255,255,.7),      /* 倒角亮边 */
    inset 0 1.5px 1px rgba(255,255,255,.42),    /* 顶部受光 */
    inset 0 -1.5px 1px rgba(255,255,255,.16),
    0 0 0 .5px rgba(10,16,26,.55);
}
.ip-screen {
  position: relative; width: 100%; height: 100%;
  border-radius: calc(var(--ip-r) - var(--ip-bz));
  overflow: hidden;
  background: #EAF3FB;
}
/* 喂进来的必须是「纯屏幕」的图（不含设计稿自带的机身外壳）。
   衣橱那批已用脚本裁好放在 assets/img/screen/，雏菊那批本身就没有外壳。
   所以这里直接铺满即可 —— 千万别再叠一层缩放偏移，那会二次裁切。 */
.ip-screen img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center top;
  max-width: none; display: block;
}
/* 玻璃反光：一道斜掠的高光，别太重，压住屏幕内容就假了 */
.ip-gloss {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border-radius: calc(var(--ip-r) - var(--ip-bz));
  background: linear-gradient(112deg,
    rgba(255,255,255,.26) 0%, rgba(255,255,255,.10) 16%,
    rgba(255,255,255,0) 34%, rgba(255,255,255,0) 72%,
    rgba(255,255,255,.07) 88%, rgba(255,255,255,.16) 100%);
}

/* 中框：绕自身边缘立起来的实体面。渐变横跨厚度方向，
   模拟中框的倒角 —— 前沿受光最亮，往机背方向压暗。 */
.ip-rail {
  position: absolute;
  top: var(--ip-r); bottom: var(--ip-r);
  width: calc(var(--ip-t) * 2);
  transform-style: preserve-3d;
  z-index: 2;
}
.ip-rail.l {
  left: 0; transform-origin: left center;
  transform: translateZ(calc(var(--ip-t) * -1)) rotateY(-90deg);
  /* 多加几档色阶 —— 金属感来自明暗的「带状跳变」，平滑过渡会像塑料 */
  background: linear-gradient(to right,
    #1C232C 0%, #39434F 9%, #6C7885 22%, #A6B2C0 38%,
    #E3EAF2 54%, #F8FBFE 64%, #C0CBD8 78%, #78838F 90%, #414B57 100%);
}
.ip-rail.r {
  right: 0; transform-origin: right center;
  transform: translateZ(calc(var(--ip-t) * -1)) rotateY(90deg);
  /* 右侧靠近光源，整体比左侧亮一档 */
  background: linear-gradient(to left,
    #232B35 0%, #46515E 9%, #8794A2 22%, #BFCAD7 38%,
    #EDF3F9 54%, #FFFFFF 64%, #D2DCE7 78%, #8A96A3 90%, #4C5764 100%);
}
/* 侧键：从中框面往外推一点点，才有实体感 */
/* 侧键：往外推一点点，上下各留一道暗缝，才看得出是独立零件 */
.ip-rail i {
  position: absolute; left: 6%; right: 6%;
  transform: translateZ(2px);
  border-radius: 1.5px;
  background: inherit;
  box-shadow:
    0 -1px 0 rgba(8,12,18,.5),
    0 1px 0 rgba(8,12,18,.5),
    inset 0 .5px 0 rgba(255,255,255,.55);
}
.ip-rail.l i.b1 { top: 15.5%; height: 3.2%; }   /* 静音 / 操作按钮 */
.ip-rail.l i.b2 { top: 24%;   height: 7.2%; }   /* 音量 + */
.ip-rail.l i.b3 { top: 33.5%; height: 7.2%; }   /* 音量 − */
.ip-rail.r i.b1 { top: 26%;   height: 9.5%; }   /* 电源 */

/* 背板 */
.ip-back {
  position: absolute; inset: 0; z-index: 1;
  transform: translateZ(calc(var(--ip-t) * -1));
  border-radius: var(--ip-r);
  background:
    radial-gradient(120% 80% at 22% 12%, #F2F6FA 0%, #D3DCE5 38%, #AFBAC7 72%, #8F9BA9 100%);
  box-shadow: inset 0 0 0 .5px rgba(255,255,255,.5);
}

/* 落影：贴地一道 + 底下一团散射，机器才像浮着 */
.ip-shadow {
  position: absolute; z-index: 0; pointer-events: none;
  left: -4%; right: -4%; bottom: -3%; height: 9%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(16,32,58,.30), transparent 70%);
  filter: blur(9px);
  transform: rotateX(82deg) translateZ(-18px);
}
.ip-cast {
  position: absolute; z-index: 0; pointer-events: none;
  left: 4%; right: 4%; top: 8%; bottom: 2%;
  border-radius: var(--ip-r);
  background: rgba(16,32,58,.20);
  filter: blur(20px);
  transform: translateZ(calc(var(--ip-t) * -2)) translate(6%, 5%);
}

.ip-cap {
  position: absolute; left: 0; right: 0; bottom: -32px;
  display: flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .05em; color: var(--pen-2);
}
.ip-cap::after { content: ''; flex: 1; height: 1px; background: var(--pen-2); opacity: .45; }

@media (max-width: 600px) { .stage3d { --ip-w: min(58vw, 230px); } }

/* ============================================================
   27. 长篇案例的版面零件
   参考「作品集 PDF」的叙事骨架，但表达仍走制图室这一套：
   编号、尺寸线、等宽小字、扫描揭幕。
   ============================================================ */

/* ---- 页眉：每一节固定的抬头，等同 PDF 每页的页眉 ---- */
.phead {
  display: flex; align-items: baseline; gap: var(--u3);
  padding-bottom: var(--u2); margin-bottom: var(--u6);
  border-bottom: 1px solid var(--ink);
}
.phead .no { flex: none; }
.phead .fill { flex: 1; height: 1px; background: var(--rule); }
.phead .cat { display: flex; gap: var(--u2); flex: none; }
.phead .cat span { color: var(--ink-4); }
.phead .cat span[aria-current] { color: var(--pen-1); }

/* ---- 标题组：中文大标题 + 英文副题 ---- */
.ttl-set { margin-bottom: var(--u6); }
.ttl-set .en {
  display: block; margin-top: var(--u2);
  font-family: var(--mono); font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
.ttl-set .en::before { content: '— '; color: var(--pen-1); }

/* ---- 用户原声：带表情的小胶囊 ---- */
.voices { display: flex; flex-wrap: wrap; gap: var(--u2); }
.voice {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 16px 10px 10px; border-radius: 999px;
  background: var(--w); border: 1px solid var(--rule);
  font-size: .9rem; line-height: 1.6; color: var(--ink-2); letter-spacing: .01em;
  transition: border-color .3s, transform .4s var(--snap);
}
.voice:hover { border-color: var(--pen-1); transform: translateY(-2px); }
.voice em {
  font-style: normal; width: 30px; height: 30px; flex: none;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--w-2); font-size: 15px;
}

/* ------------------------------------------------------------
   .xform —— 难点 → 目标 的转化行
   案例里最能体现产品思维的地方：把「用户有问题」翻译成
   「设计要解决什么、用什么手段」。四列强制走完这条链，
   缺一列就说明这个难点还没想透。
   ------------------------------------------------------------ */
.xform { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
         padding: var(--u5) 0; border-top: 1px solid var(--rule); align-items: start; }
.xform .n { grid-column-end: span 1; font-family: var(--mono); font-size: 11.5px;
            letter-spacing: .06em; color: var(--pen-2); padding-top: 3px; }
.xform .h { grid-column-end: span 3; }
.xform .h b { display: block; font-family: var(--body); font-weight: 500;
              font-size: 1.02rem; letter-spacing: .01em; color: var(--ink); }
.xform .h span { display: block; margin-top: 6px; }
.xform .arrow { grid-column-end: span 1; display: grid; place-items: center; padding-top: 4px;
                color: var(--pen-1); }
.xform .g1 { grid-column-end: span 3; }
.xform .g2 { grid-column-end: span 4; }
.xform .cap { display: block; font-family: var(--mono); font-size: 10.5px;
              letter-spacing: .08em; color: var(--ink-3); margin-bottom: 7px; }
@media (max-width: 880px) {
  .xform { grid-template-columns: 1fr; gap: var(--u2); padding: var(--u4) 0; }
  .xform > * { grid-column: 1 / -1 !important; }
  .xform .arrow { display: none; }
}

/* ------------------------------------------------------------
   .pers —— 用户画像。四维度是可扫读的条目，不是散文；
   每维底部挂一条「所以设计上」，让画像推得出决策，否则就是装饰。
   下面那条一天时间轴是这一节的关键：每个时刻直接点名它由哪一屏承接，
   画像因此和产品咬合，而不是各说各话。
   ------------------------------------------------------------ */
.pers { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); align-items: start; }

/* 左：人像 + 姓名徽章 */
.pface { grid-column-end: span 4; position: relative; }
.pface .shot {
  position: relative; overflow: hidden; border-radius: 10px;
  aspect-ratio: 3 / 4; background: var(--w-2);
  border: 1px solid var(--rule);
  display: grid; place-items: center; text-align: center; padding: var(--u5);
}
.pface .shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pface .badge {
  position: absolute; left: var(--u3); bottom: var(--u3); z-index: 2;
  display: inline-flex; align-items: baseline; gap: 9px;
  padding: 10px 16px; border-radius: 999px;
  background: var(--w); box-shadow: 0 6px 18px rgba(16,32,58,.14);
}
.pface .badge b { font-family: var(--body); font-weight: 500; font-size: 1.05rem; letter-spacing: .01em; }
.pface .badge span { font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; color: var(--ink-3); }

.pers .side { grid-column-end: span 8; }

/* 四维卡 */
.pdim { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap); margin-top: var(--u6); }
.pdim .d {
  border: 1px solid var(--rule); border-radius: 8px; padding: var(--u4);
  background: var(--w); display: flex; flex-direction: column;
}
.pdim .d .hd { display: flex; align-items: center; gap: 9px; margin-bottom: var(--u3); }
.pdim .d .hd i {
  width: 26px; height: 26px; flex: none; border-radius: 6px;
  display: grid; place-items: center; background: var(--w-blue); color: var(--pen-1);
}
.pdim .d .hd b { font-family: var(--body); font-weight: 500; font-size: 1rem; letter-spacing: .01em; }
.pdim .d ul { margin: 0; padding: 0; list-style: none; flex: 1; }
.pdim .d li {
  position: relative; padding-left: 15px; margin-bottom: 9px;
  font-size: .86rem; line-height: 1.75; color: var(--ink-2); letter-spacing: .012em;
}
.pdim .d li::before {
  content: ''; position: absolute; left: 0; top: .66em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--ink-4);
}
.pdim .d .so {
  margin-top: var(--u3); padding-top: var(--u3); border-top: 1px dashed var(--ink-4);
}
.pdim .d .so b {
  display: block; margin-bottom: 5px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em; font-weight: 400; color: var(--pen-2);
}
.pdim .d .so span { font-size: .84rem; line-height: 1.8; color: var(--ink-2); letter-spacing: .012em; }

/* 一天的时间轴 —— 每个时刻点名它由哪一屏承接 */
.ptl { position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
       gap: var(--gap); margin-top: var(--u8); padding-top: var(--u5); }
.ptl::before {
  content: ''; position: absolute; left: 4%; right: 4%; top: calc(var(--u5) + 31px);
  height: 1px; background: repeating-linear-gradient(90deg,
    var(--pen-1) 0 5px, transparent 5px 10px);
}
.ptl .n { position: relative; text-align: center; }
.ptl .n .t { font-family: var(--mono); font-size: 11.5px; letter-spacing: .06em; color: var(--ink-3); }
.ptl .n .dot {
  position: relative; z-index: 2; width: 30px; height: 30px; margin: 10px auto 0;
  border-radius: 50%; background: var(--w); border: 1px solid var(--rule);
  display: grid; place-items: center; color: var(--pen-1);
}
.ptl .n .a { display: block; margin-top: 11px; font-size: .88rem; letter-spacing: .012em; color: var(--ink); }
.ptl .n .scr {
  display: inline-block; margin-top: 6px; padding: 2px 8px; border-radius: 3px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .05em;
  color: var(--pen-1); background: var(--w-blue);
}
.ptl .n.off .dot { color: var(--ink-4); }
.ptl .n.off .scr { color: var(--ink-3); background: var(--w-2); }

/* 目标：三个 chip，颜色接回第 06 节的问题分类 */
.pchip { display: flex; flex-wrap: wrap; gap: var(--u3); margin-top: var(--u8);
         padding-top: var(--u5); border-top: 1px solid var(--rule); }
.pchip span { display: inline-flex; align-items: center; gap: 9px;
              font-size: .95rem; letter-spacing: .012em; color: var(--ink); }
.pchip i { width: 6px; height: 6px; border-radius: 50%; flex: none; }

@media (max-width: 1100px) { .pdim { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 900px) {
  .pface, .pers .side { grid-column-end: span 12; }
  .pface .shot { aspect-ratio: 16 / 9; }
  .ptl { grid-auto-flow: row; grid-auto-columns: auto; gap: var(--u4); }
  .ptl::before { display: none; }
  .ptl .n { display: grid; grid-template-columns: 54px 30px 1fr; align-items: center;
            gap: var(--u3); text-align: left; }
  .ptl .n .dot { margin: 0; }
  .ptl .n .a { margin-top: 0; }
}
@media (max-width: 560px) { .pdim { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------
   .phen —— 三个现象 → 机会
   把原声（散的证据）归纳成三个可命名的现象，再收到一个机会陈述上。
   顶部虚线把三张卡串起来并指向右侧，说明这三件事是同一个问题的三面，
   而不是并列的三条抱怨。
   ------------------------------------------------------------ */
.phen { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
        position: relative; padding-top: 34px; }

/* 串起三张卡的虚线，只跨前三列（末列是机会，不参与） */
.phen .tie {
  position: absolute; top: 0; height: 34px; pointer-events: none;
  /* 从首卡中心跨到「机会」中心：两者相距 9 个「列宽+gap」。
     左端 = 1.5 列 + 1 gap（首卡中心）。 */
  left:  calc((100% - 11 * var(--gap)) / 12 * 1.5 + var(--gap));
  width: calc((100% - 11 * var(--gap)) / 12 * 9 + var(--gap) * 9);
}
.phen .tie svg { width: 100%; height: 100%; overflow: visible; }
.phen .tie .ln { fill: none; stroke: var(--pen-1); stroke-width: 1.3;
                 stroke-dasharray: 5 5; vector-effect: non-scaling-stroke; }
.phen .tie .pt { fill: var(--pen-1); }

.phcard { grid-column-end: span 3; border-radius: 10px; background: var(--w-1);
          border: 1px solid var(--rule); padding: var(--u4); text-align: center;
          transition: border-color .3s var(--out), transform .4s var(--snap); }
.phcard:hover { border-color: var(--pen-1); transform: translateY(-3px); }
.phcard .ring { width: 62px; height: 62px; margin: 0 auto var(--u3); border-radius: 50%;
                background: var(--w); border: 1px solid var(--rule);
                display: grid; place-items: center; color: var(--pen-1); }
.phcard h4 { font-family: var(--body); font-weight: 500; font-size: 1.05rem;
             letter-spacing: .01em; margin-bottom: var(--u2); }
.phcard .p-s { text-align: left; }
.phcard .shot { margin-top: var(--u3); border-radius: 8px; overflow: hidden;
                border: 1px solid var(--rule); background: var(--w-2);
                display: grid; place-items: center; padding: var(--u2);
                aspect-ratio: 4 / 3; }
.phcard .shot img { width: 100%; height: 100%; object-fit: contain;
                    object-position: center; display: block; }

/* 机会：深底陈述 + 三节点闭环 */
.opp { grid-column-end: span 3; }
.oppchip { background: var(--ink); color: var(--w); border-radius: 10px;
           padding: var(--u4); display: flex; gap: 11px; align-items: flex-start; }
.oppchip i { color: var(--pen-1); flex: none; margin-top: 4.5px; }
.phen .oppchip i img { display: block; width: 18px; height: 18px; object-fit: contain;
                        filter: brightness(0) invert(1); }
.oppchip b { font-family: var(--body); font-weight: 500; font-size: 1rem;
             line-height: 1.7; letter-spacing: .01em; }
.oppchip b span { color: var(--pen-1); }

.loop { position: relative; margin-top: var(--u5); aspect-ratio: 1 / .86; }
.loop > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.loop > svg path { fill: none; stroke: var(--pen-1); stroke-width: 1.3;
                 stroke-dasharray: 4 5; opacity: .7; }
.loop .nd { position: absolute; width: 33%; aspect-ratio: 1; border-radius: 50%;
            background: var(--w); border: 1px solid var(--rule);
            display: grid; place-items: center; align-content: center; gap: 3px; text-align: center;
            color: var(--pen-1); }
.loop .nd span { font-family: var(--body); font-size: .78rem; letter-spacing: .01em;
                 color: var(--ink); line-height: 1.3; }
.phen .loop .nd .loop-icon { position: static; inset: auto; display: block;
                              width: 54px; height: 54px; object-fit: contain; }
.loop .n1 { top: 0;   left: 33.5%; }
.loop .n2 { bottom: 0; right: 0; }
.loop .n3 { bottom: 0; left: 0; }
.loop .core { position: absolute; left: 50%; top: 52%; transform: translate(-50%,-50%);
              color: var(--pen-4); }
.phen .loop .core .core-icon { position: static; inset: auto; display: block;
                                width: 26px; height: 26px; object-fit: contain; }

@media (max-width: 1080px) {
  .phcard { grid-column-end: span 6; }
  .opp { grid-column-end: span 12; }
  .phen { padding-top: 0; }
  .phen .tie { display: none; }
  .loop { max-width: 340px; margin-inline: auto; }
}
@media (max-width: 600px) { .phcard { grid-column-end: span 12; } }

/* ------------------------------------------------------------
   .jchain —— 旅程 → 痛点 → 设计应对
   五个环节横向排开，上方一条压力曲线标出峰值在哪一环。
   每列往下走完「痛点 → 应对」，让设计动作挂在具体环节上，
   而不是三条飘着的原则。
   曲线用 SVG 拉伸铺满，节点落在 10/30/50/70/90% —— 正好是五列的中心。
   ------------------------------------------------------------ */
.jchain { position: relative; padding-top: 118px; }
/* 曲线只跨「首列中心 → 末列中心」，两端各缩进半列。
   半列宽 = (100% - 4×gap) / 10 —— 这样五个节点正好落在 0/25/50/75/100%，
   与 gap 取值无关。按整条宽度猜 10/30/50/70/90% 会被 gap 带偏（gap 是
   clamp，随视口变），实测末列差 5px。 */
.jcurve {
  position: absolute; top: 26px; height: 92px; overflow: visible; pointer-events: none;
  /* SVG 是替换元素：width:auto 会取它的固有宽度（viewBox 推出的 1000px），
     right 会被忽略。所以必须显式给宽度 = 100% − 一整列。 */
  left:  calc((100% - 4 * var(--gap)) / 10);
  width: calc(100% - (100% - 4 * var(--gap)) / 5);
}
.jcurve .ln { fill: none; stroke: var(--pen-1); stroke-width: 1.4;
              stroke-dasharray: 5 5; opacity: .75; vector-effect: non-scaling-stroke; }
.jcurve .pt { fill: var(--pen-1); }
/* 圆点与峰值标签挂在列内部，因此永远对准列中心 ——
   靠百分比猜位置会被 gap 带偏（实测两端差 7px，gap 是 clamp，随视口变）。 */
.jchain .col .cdot {
  position: absolute; left: 50%; transform: translate(-50%,-50%);
  width: 9px; height: 9px; border-radius: 50%; background: var(--pen-1); z-index: 2;
}
.jchain .col .jpeak {
  position: absolute; left: 50%; bottom: calc(100% + 78px); transform: translateX(-50%); z-index: 3;
  padding: 6px 14px; border-radius: 999px; background: var(--pen-1); color: #fff;
  font-size: .82rem; letter-spacing: .02em; white-space: nowrap;
}

.jchain .cols { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--gap); }
.jchain .col { position: relative; text-align: center; }

/* 环节节点 */
.jnode { position: relative; }
.jnode .ring {
  width: 78px; height: 78px; margin: 0 auto; border-radius: 50%;
  border: 1px solid var(--rule); background: var(--w);
  display: grid; place-items: center; color: var(--pen-1);
  transition: border-color .3s var(--out), transform .4s var(--snap);
}
.jchain .col:hover .jnode .ring { border-color: var(--pen-1); transform: translateY(-3px); }
.jnode b { display: block; margin-top: var(--u2); font-family: var(--body);
           font-weight: 500; font-size: .96rem; letter-spacing: .01em; }
/* 环节之间的箭头 */
.jchain .col:not(:last-child) .jnode::after {
  content: ''; position: absolute; top: 39px; left: calc(50% + 47px); right: calc(-50% + 47px);
  height: 1px; background: repeating-linear-gradient(90deg,
    var(--ink-4) 0 4px, transparent 4px 8px);
}

/* 竖向连线 */
.jchain .col .drop { display: block; width: 1px; height: var(--u4); margin: var(--u3) auto 0;
                     background: repeating-linear-gradient(180deg,
                       var(--ink-4) 0 4px, transparent 4px 8px); }

/* 痛点：深底 */
.jpain {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 12px; border-radius: 8px;
  background: var(--ink); color: var(--w);
  font-size: .92rem; letter-spacing: .01em;
}
/* 中文行框含 ascender/descender，图标按盒子居中会比字面高约 1px，补回来 */
.jpain i, .jresp .t i { margin-top: 1px; }
.jpain i { color: var(--pen-1); display: grid; place-items: center; flex: none; }

/* 设计应对：描边 */
.jresp {
  padding: 12px; border-radius: 8px;
  border: 1px solid var(--pen-1); background: var(--w);
}
.jresp .t { display: flex; align-items: center; justify-content: center; gap: 8px;
            font-size: .92rem; letter-spacing: .01em; color: var(--ink); }
.jresp .t i { color: var(--pen-1); display: grid; place-items: center; flex: none; }
.jresp .d { display: block; margin-top: 7px; font-size: .8rem; line-height: 1.7;
            color: var(--ink-2); letter-spacing: .012em; }

@media (max-width: 1080px) {
  .jchain { padding-top: 0; }
  .jcurve { display: none; }
  .jchain .col .cdot, .jchain .col .jpeak { display: none; }
  .jchain .cols { grid-template-columns: repeat(2, 1fr); row-gap: var(--u6); }
  .jchain .col:not(:last-child) .jnode::after { display: none; }
}
@media (max-width: 560px) { .jchain .cols { grid-template-columns: 1fr; } }

/* .sixd —— 视觉体系六维（字色形构质动）。左边一个字，右边答案。 */
.sixd { border-top: 1px solid var(--rule); }
.sixd .row { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
             padding: var(--u4) 0; border-bottom: 1px solid var(--rule-2); align-items: baseline; }
.sixd .row .w { grid-column-end: span 1; font-family: var(--display); font-weight: 200;
                font-size: clamp(1.8rem, 3vw, 2.6rem); line-height: 1; color: var(--pen-1); }
.sixd .row .t { grid-column-end: span 4; }
.sixd .row .b { grid-column-end: span 7; }
@media (max-width: 880px) {
  .sixd .row { grid-template-columns: 1fr; gap: var(--u2); }
  .sixd .row > * { grid-column: 1 / -1 !important; }
}

/* .atoms —— 组件库三层（原子 / 分子 / 组织） */
.atoms { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); }
.atoms .lv { grid-column-end: span 4; }
.atoms .lv .hd { display: flex; align-items: baseline; gap: 10px; margin-bottom: var(--u3);
                 padding-bottom: var(--u2); border-bottom: 1px solid var(--ink); }
.atoms .lv .hd b { font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
                   font-weight: 400; color: var(--pen-1); }
.atoms .lv ul { margin: 0; padding-left: 1.1em; }
.atoms .lv li { font-size: .9rem; line-height: 1.95; color: var(--ink-2);
                letter-spacing: .012em; margin-bottom: 5px; }
@media (max-width: 880px) { .atoms .lv { grid-column-end: span 12; } }

/* ---- 问题三分类：颜色贯穿全篇，读者不用重新学 ---- */
.probs { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); }
.prob { grid-column-end: span 4; border-radius: 6px; padding: var(--u4); border: 1px solid var(--rule); }
.prob .hd { display: flex; align-items: center; gap: 10px; margin-bottom: var(--u3); }
.prob .hd b {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-family: var(--mono); font-size: 11px;
  color: #fff; font-weight: 400;
}
.prob ol { margin: 0; padding-left: 1.15em; }
.prob li { font-size: .9rem; line-height: 1.9; color: var(--ink-2); letter-spacing: .012em; margin-bottom: var(--u2); padding-left: 2px; }
.prob.p1 { background: rgba(5,186,224,.06);  border-color: rgba(5,186,224,.34); }
.prob.p1 .hd b { background: var(--pen-1); }
.prob.p2 { background: rgba(248,184,56,.09); border-color: rgba(248,184,56,.42); }
.prob.p2 .hd b { background: var(--pen-4); color: #4A3200; }
.prob.p3 { background: rgba(216,80,63,.06);  border-color: rgba(216,80,63,.30); }
.prob.p3 .hd b { background: var(--pen-2); }
@media (max-width: 880px) { .prob { grid-column-end: span 12; } }

/* ---- 设计目标：三圆交集 ---- */
.venn { width: 100%; height: auto; overflow: visible; }
.venn text { font-family: var(--body); }
.venn .lbl-c { font-size: 15px; font-weight: 500; fill: #fff; }
.venn .ring { fill: none; stroke: var(--ink-4); stroke-dasharray: 4 5; }

/* ---- 指标：虚拟项目里一律标注为「假设 · 待验证」 ---- */
.metrics { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); }
.metric { grid-column-end: span 3; padding-top: var(--u3); border-top: 1px solid var(--ink); }
.metric .v {
  font-family: var(--display); font-weight: 200; line-height: 1;
  font-size: clamp(2.4rem, 5.4vw, 4.2rem); letter-spacing: -.05em;
  font-variant-numeric: tabular-nums; color: var(--pen-1);
}
.metric .v sup { font-size: .32em; vertical-align: super; letter-spacing: 0; }
.metric .k { margin-top: var(--u3); color: var(--ink); font-family: var(--body); font-size: .86rem; font-weight: 500; letter-spacing: .02em; }
.metric .d { margin-top: 6px; font-size: .82rem; line-height: 1.8; color: var(--ink-2); max-width: 16em; }
.metric .hyp {
  display: inline-flex; align-items: center; gap: 6px; margin-top: var(--u2);
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
  color: var(--pen-2); border: 1px solid var(--pen-2); border-radius: 2px; padding: 2px 6px;
}
@media (max-width: 880px) { .metric { grid-column-end: span 6; } }
@media (max-width: 520px) { .metric { grid-column-end: span 12; } }

/* ---- 图位：还没补的图，留一个有编号的坑 ---- */
.figslot {
  position: relative; border: 1px dashed var(--ink-4); border-radius: 5px;
  background:
    repeating-linear-gradient(45deg, transparent 0 9px, rgba(5,186,224,.05) 9px 10px),
    var(--w-1);
  display: grid; place-items: center; text-align: center;
  padding: var(--u5) var(--u4); min-height: 150px;
  transition: border-color .35s, background-color .35s;
}
.figslot:hover { border-color: var(--pen-1); }
.figslot .no {
  display: inline-block; margin-bottom: var(--u2);
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  color: var(--pen-2); border: 1px solid var(--pen-2); border-radius: 2px; padding: 3px 8px;
}
.figslot .desc { font-size: .9rem; line-height: 1.9; color: var(--ink-2); letter-spacing: .014em; max-width: 25em; }
.figslot .hint { display: block; margin-top: var(--u2); font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; color: var(--ink-4); }
.figslot.tall { min-height: 300px; }
.figslot.phone { aspect-ratio: 440 / 950; min-height: 0; }

/* ---- Before / After ---- */
.ba { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); align-items: center; }
.ba .side { grid-column-end: span 5; position: relative; }
.ba .mid { grid-column-end: span 2; display: grid; place-items: center; gap: 8px; }
.ba .mid svg { color: var(--pen-1); }
.ba .mid span { font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em; color: var(--ink-3); }
.ba .tagba {
  position: absolute; left: 0; top: -26px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
}
.ba .side.b .tagba { color: var(--ink-3); }
.ba .side.a .tagba { color: var(--pen-1); }
@media (max-width: 880px) {
  .ba { grid-template-columns: 1fr; }
  .ba .side, .ba .mid { grid-column: 1 / -1; }
  .ba .mid { transform: rotate(90deg); margin-block: var(--u5); }
  .ba .tagba { position: static; display: block; margin-bottom: 8px; }
}

/* ---- 决策/流程步骤条 ---- */
.steps-bar { display: flex; align-items: center; gap: var(--u2); flex-wrap: wrap; }
.steps-bar .st {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: 999px; border: 1px solid var(--rule);
  font-size: .84rem; color: var(--ink-2);
}
.steps-bar .st b {
  font-family: var(--mono); font-size: 10px; color: var(--pen-1); font-weight: 400;
}
.steps-bar .st.on { border-color: var(--pen-1); background: rgba(5,186,224,.07); color: var(--ink); }
.steps-bar .sep { color: var(--ink-4); }

/* ---- 侧边章节索引：PDF 有页码，网页可以有活的进度 ---- */
.secrail {
  position: fixed; right: calc(var(--gut) - 6px); top: 50%; transform: translateY(-50%);
  z-index: 8800; display: flex; flex-direction: column; gap: 3px;
  padding: 10px 6px; border-radius: 999px;
  background: var(--scrim); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--rule);
}
.secrail a {
  display: flex; align-items: center; gap: 8px; justify-content: flex-end;
  font-family: var(--mono); font-size: 9px; letter-spacing: .06em; color: var(--ink-4);
  padding: 3px 4px; transition: color .3s;
}
.secrail a i { width: 14px; height: 1px; background: currentColor; transition: width .35s var(--snap); }
.secrail a span { opacity: 0; width: 0; overflow: hidden; white-space: nowrap; transition: opacity .3s, width .35s var(--snap); }
.secrail:hover a span { opacity: 1; width: 5.2em; }
.secrail a:hover { color: var(--ink); }
.secrail a[aria-current] { color: var(--pen-1); }
.secrail a[aria-current] i { width: 22px; }
@media (max-width: 1180px) { .secrail { display: none; } }

/* 封面在场时把导轨收起来。两个理由：正文还没开始，进度指示没有意义；
   而且这条玻璃胶囊固定在右边距上，正好压在封面样机的第三台机器上。
   注意 .secrail 自己带了 translateY(-50%)，收起态必须把它重写回去。 */
.secrail { transition: opacity .45s var(--out), transform .45s var(--out); }
.secrail.tucked {
  opacity: 0; pointer-events: none;
  transform: translateY(-50%) translateX(16px);
}

/* ============================================================
   28. 案例作用域配色 —— 雏菊 / Daisy
   同样是从设计稿像素里统计出来的：宝蓝 #2068F8 是主色，
   紫 #9878F8 归协作与人，珊瑚 #F87068 归截止与警示，
   翠绿 #2FB672 只表示「通过 / 开放」。底是白到薰衣草的渐变。
   ============================================================ */
body[data-case="daisy"] {
  /* 取自作品集第 32 页「清透、克制的北欧设计语言」：
     品牌主色 #296BFF 专注·可信 · 背景色 #E2EBFF 干净·柔和
     文字主色 #0B1235 清晰·专注 · 警示红 #FF6B5E · 功能紫 #8A7DFF */
  --ink:    #0B1235;
  --ink-2:  #414B6B;
  --ink-3:  #838DA8;
  --ink-4:  #C0C8DC;
  --rule:   #DEE5F3;
  --rule-2: #EEF2FA;
  --w-1:    #F7F9FE;
  --w-2:    #EAF0FC;
  --w-blue: #E2EBFF;   /* 官方背景色 */
  --w-warm: #F1EEFD;   /* 功能紫的极淡版 */
  --pen-1:  #296BFF;   /* 品牌主色 */
  --pen-2:  #FF6B5E;   /* 警示红：提醒·重要 */
  --pen-3:  #2FB672;   /* 翠绿：通过·开放（界面里存在，官方色板未单列） */
  --pen-4:  #8A7DFF;   /* 功能紫：探索·创意 */
}
body[data-case="daisy"] ::selection { background: var(--pen-1); color: #fff; }
/* 雏菊那三张问题卡跟着换色 */
body[data-case="daisy"] .prob.p2 { background: rgba(152,120,248,.07); border-color: rgba(152,120,248,.34); }
body[data-case="daisy"] .prob.p2 .hd b { background: var(--pen-4); color: #fff; }

/* ------------------------------------------------------------
   .mk —— 荧光笔划线
   作品集里最醒目的一个动作：关键短句底下压一道柠檬绿马克笔。
   不是下划线 —— 它压在字身下三分之一处，像真的用笔划过。
   进场时从左往右扫出来，归到本版「描线 / 扫描」那一族动效里。
   ------------------------------------------------------------ */
.mk {
  /* 用 <i> 承载，但中文没有真斜体 —— 必须显式关掉，否则浏览器合成假斜 */
  font-style: normal;
  background-image: linear-gradient(
    transparent 54%,
    var(--mk-c, var(--lime, #B8FF00)) 54%,
    var(--mk-c, var(--lime, #B8FF00)) 93%,
    transparent 93%);
  background-repeat: no-repeat;
  background-size: 0 100%;
  padding-inline: .08em;
  transition: background-size .8s var(--out) .15s;
}
body.set [data-snap].in .mk,
body.set [data-push].in .mk,
body.set [data-scan].in .mk { background-size: 100% 100%; }
/* 没有入场包裹时（比如直接写在正文里）也要能显示 */
body.set .mk:not([data-snap] .mk):not([data-push] .mk):not([data-scan] .mk) { background-size: 100% 100%; }
/* 雏菊那版没有柠檬绿，用主色的淡版 */
body[data-case="daisy"] .mk { --mk-c: rgba(41,107,255,.22); }

/* ---- 三层音域的字体样本（只用在雏菊的设计语言那节） ---- */
.spec-serif { font-family: 'Playfair Display', Georgia, serif; font-weight: 500; letter-spacing: -.01em; }
.spec-hand  { font-family: 'Caveat', 'Bradley Hand', cursive; font-weight: 500; }
.voice-row {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
  padding: var(--u4) 0; border-top: 1px solid var(--rule); align-items: baseline;
}
.voice-row .k { grid-column-end: span 2; }
.voice-row .s { grid-column-end: span 6; }
.voice-row .n { grid-column: 10 / 13; text-align: right; }
@media (max-width: 880px) {
  .voice-row { grid-template-columns: 1fr; gap: var(--u2); }
  .voice-row > *, .voice-row .n { grid-column: 1 / -1; text-align: left; }
}

/* ============================================================
   29. 雏菊专用版面：以「一天」为脊
   跟衣橱那页刻意拉开：
     衣橱 = 编号章节 + 交替底色 + 居中机身 + 右侧索引
     雏菊 = 时刻轴 + 全程纸白 + 贴边机身 + 引线批注
   产品讲的是学生的一天，所以案例本身也按一天走。
   ============================================================ */

/* ---- 左侧固定时刻轴 ---- */
.hourrail {
  position: fixed; left: calc(var(--gut) - 4px); top: 50%; transform: translateY(-50%);
  z-index: 8800; display: flex; flex-direction: column; gap: 2px;
  padding-left: 10px; border-left: 1px solid var(--rule);
}
.hourrail a {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .04em;
  color: var(--ink-4); padding: 4px 0; transition: color .3s;
}
.hourrail a i {
  width: 6px; height: 1px; background: currentColor; flex: none;
  transition: width .35s var(--snap);
}
/* 默认只留刻度，免得压住正文；当前项常驻，悬停整条展开 */
.hourrail a span {
  opacity: 0; width: 0; overflow: hidden; white-space: nowrap;
  transition: opacity .28s, width .35s var(--snap);
}
.hourrail:hover a span, .hourrail a[aria-current] span { opacity: 1; width: 3.6em; }
.hourrail:hover {
  background: var(--scrim); border-radius: 3px;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.hourrail a:hover { color: var(--ink-2); }
.hourrail a[aria-current] { color: var(--pen-1); }
.hourrail a[aria-current] i { width: 20px; }
@media (max-width: 1240px) { .hourrail { display: none; } }

/* ---- 时刻抬头：大时间戳 + 细线 + 场景 ---- */
.hourhead { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); align-items: baseline; margin-bottom: var(--u8); }
.hourhead .t {
  grid-column: 1 / 4;
  font-family: var(--display); font-weight: 200;
  font-size: clamp(2.6rem, 6vw, 5rem); line-height: .9;
  letter-spacing: -.055em; font-variant-numeric: tabular-nums;
  color: var(--pen-1);
}
.hourhead .t small { display: block; margin-top: var(--u2); }
.hourhead .h { grid-column: 4 / 11; }
.hourhead .x { grid-column: 11 / 13; justify-self: end; text-align: right; }
.hourhead::after {
  content: ''; grid-column: 1 / -1; height: 1px; background: var(--ink);
  margin-top: var(--u4);
}
@media (max-width: 880px) {
  .hourhead { grid-template-columns: 1fr; gap: var(--u3); }
  .hourhead .t, .hourhead .h, .hourhead .x { grid-column: 1 / -1; justify-self: start; text-align: left; }
}

/* ---- 贴边机身 + 引线批注 ---- */
.edgefig { position: relative; display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap); align-items: center; }
.edgefig .dev-side { grid-column: 1 / 5; }
/* 一栏只用一个行宽。段落、批注共享同一条右边缘，批注靠缩进挂进去 ——
   三种不同宽度叠在一起，右边缘参差，是「排得不好看」最常见的原因。 */
.edgefig .txt-side { grid-column: 6 / 12; max-width: 34em; }
.edgefig .txt-side .p,
.edgefig .txt-side .p-s,
.edgefig .txt-side .callout .p-s { max-width: none; }
.edgefig.flip .dev-side { grid-column: 9 / 13; order: 2; }
.edgefig.flip .txt-side { grid-column: 2 / 8; order: 1; }
@media (max-width: 900px) {
  .edgefig, .edgefig.flip { grid-template-columns: 1fr; }
  .edgefig .dev-side, .edgefig .txt-side,
  .edgefig.flip .dev-side, .edgefig.flip .txt-side { grid-column: 1 / -1; order: 0; }
}

/* 引线批注：一条细线 + 一个圆点 + 一段小字，像图纸上的指引 */
/* 引线批注：靠「贴紧 / 拉开」建立分组 —— 标签与正文贴在一起（6px），
   批注与批注之间拉开（--u5）。中间那点距离就是读者判断「这两句是一组」的依据。 */
.callout { position: relative; padding-left: var(--u6); margin-top: var(--u5); }
.callout + .callout { margin-top: var(--u5); }
.callout::before {
  content: ''; position: absolute; left: 0; top: .68em;
  width: calc(var(--u6) - 16px); height: 1px; background: var(--pen-1);
}
.callout::after {
  content: ''; position: absolute; left: 0; top: .68em;
  width: 5px; height: 5px; margin: -2px 0 0 -2px; border-radius: 50%; background: var(--pen-1);
}
/* 批注标题不走等宽 —— 中文在等宽里被拉散，这里回到正文字体加字重 */
.callout .k {
  display: block; margin-bottom: 6px;
  font-family: var(--body); font-size: .84rem; font-weight: 500;
  letter-spacing: .02em; text-transform: none; color: var(--pen-1);
}
.callout.warm .k { color: var(--pen-2); }
.callout.violet .k { color: var(--pen-4); }
.callout.green .k { color: var(--pen-3); }
.callout .p-s { max-width: 29em; }
.callout.warm::before, .callout.warm::after { background: var(--pen-2); }
.callout.violet::before, .callout.violet::after { background: var(--pen-4); }
.callout.green::before, .callout.green::after { background: var(--pen-3); }

/* ---- 编号问题：竖排大号，不用三色卡（那是衣橱的写法） ---- */
.friction { border-top: 1px solid var(--ink); }
.frow {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gap);
  padding: var(--u5) 0; border-bottom: 1px solid var(--rule); align-items: start;
}
.frow .n {
  grid-column: 1 / 3;
  font-family: var(--display); font-weight: 200; line-height: 1;
  font-size: clamp(2rem, 4vw, 3.2rem); letter-spacing: -.05em; color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}
.frow .h { grid-column: 3 / 7; }
.frow .b { grid-column: 7 / 12; max-width: 30em; }
.frow:hover .n { color: var(--pen-1); transition: color .35s; }
@media (max-width: 880px) {
  .frow { grid-template-columns: 1fr; gap: var(--u2); }
  .frow > * { grid-column: 1 / -1 !important; }
}

/* ---- 一天的流程曲线 ---- */
.dayflow-svg { width: 100%; height: auto; overflow: visible; }
.dayflow-svg .line { fill: none; stroke: var(--pen-1); stroke-width: 1.6; }
.dayflow-svg .tick { stroke: var(--ink-4); stroke-width: 1; }
.dayflow-svg .node { fill: #fff; stroke: var(--pen-1); stroke-width: 1.6; }
.dayflow-svg .node.on { fill: var(--pen-1); }
.dayflow-svg text { font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em; fill: var(--ink-3); }
.dayflow-svg text.b { fill: var(--ink); font-family: var(--body); font-size: 12px; letter-spacing: 0; }

/* ---- 八屏总览：4 × 2，对应 Figma 里的原始排布 ---- */
.daygrid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(20px, 3vw, 56px) clamp(16px, 2.4vw, 44px);
  align-items: start;
}
.daygrid .cell { display: flex; flex-direction: column; align-items: center; }
.daygrid .cell .lbl { margin-top: var(--u6); text-align: center; }
@media (max-width: 1000px) { .daygrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .daygrid { grid-template-columns: 1fr; } }



/* ------------------------------------------------------------
   .manifesto / .loopband —— 决策链（衣橱）
   作品集里最核心的一句：情境 → 识别 → 推荐 → 调整 → 记录。
   深底 + 柠檬绿标签，是这个项目视觉上最强的一个组合。
   ------------------------------------------------------------ */
.manifesto {
  margin-top: var(--u8); padding: var(--u6) var(--u6) var(--u5);
  border-radius: 16px; position: relative; overflow: hidden;
  background: linear-gradient(135deg, #0B1A24 0%, #071018 55%, #0A1C26 100%);
  color: #fff;
}
.manifesto::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 88% 6%, rgba(5,186,224,.20), transparent 58%);
}
.manifesto .k {
  position: relative; z-index: 1; display: inline-block;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .16em;
  color: var(--lime, #B8FF00);
}
.manifesto .line {
  position: relative; z-index: 1; margin-top: var(--u4);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--u2) var(--u3);
  font-family: var(--display); font-weight: 300;
  font-size: clamp(1.3rem, 2.7vw, 2.1rem); letter-spacing: .02em;
}
.manifesto .line b { font-weight: 400; }
.manifesto .line s { text-decoration: none; color: var(--pen-1); font-size: .8em; }
.manifesto .tail {
  position: relative; z-index: 1; margin-top: var(--u5); padding-top: var(--u4);
  border-top: 1px solid rgba(255,255,255,.14);
  font-size: .92rem; line-height: 1.9; color: rgba(255,255,255,.62);
  letter-spacing: .012em;
}
.manifesto .tail em { font-style: normal; color: var(--lime, #B8FF00); }

/* 六个关键节点 */
.loopband { margin-top: var(--u8); display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--gap); }
.loopstep { position: relative; padding-top: var(--u5); }
.loopstep::before {
  content: ''; position: absolute; left: 0; right: 0; top: 13px; height: 1px; background: var(--rule);
}
.loopstep .n {
  position: relative; width: 27px; height: 27px; border-radius: 50%;
  display: grid; place-items: center; background: var(--w); border: 1px solid var(--ink);
  font: 10px var(--mono); color: var(--ink); margin-bottom: var(--u3);
}
.loopstep:last-child .n { background: var(--lime, #B8FF00); border-color: var(--lime, #B8FF00); }
.loopstep b { display: block; font-family: var(--body); font-weight: 500; font-size: .98rem;
              letter-spacing: .01em; }
.loopstep span { display: block; margin-top: 7px; font-size: .8rem; line-height: 1.75;
                 color: var(--ink-2); letter-spacing: .012em; }
.loopstep i { display: block; margin-top: var(--u2); font-style: normal;
              font-family: var(--mono); font-size: 9.5px; letter-spacing: .04em; color: var(--ink-4); }

@media (max-width: 1000px) { .loopband { grid-template-columns: repeat(3, 1fr); row-gap: var(--u6); } }
@media (max-width: 620px)  { .loopband { grid-template-columns: repeat(2, 1fr); } }

/* ------------------------------------------------------------
   .iamap —— 信息架构图（衣橱 18 页 / 雏菊 34 页）
   两张图都是横向铺开的树，缩到一栏宽就没法读了。
   所以给一个下限宽度让它横向滚，而不是整体缩小。
   两个案例页共用，所以留在 styles.css，不进 closet.css / daisy.css。
   ------------------------------------------------------------ */
.iamap { margin: var(--u6) 0 0; }
.iawrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--w); padding: var(--u3);
}
.iawrap img { display: block; width: 100%; min-width: 1180px; height: auto; }
.iamap figcaption {
  margin-top: var(--u3); display: flex; flex-wrap: wrap; gap: 6px var(--u4);
  font-size: 12.5px; line-height: 1.8; color: var(--ink-3);
}
.iamap figcaption b { font-weight: 400; color: var(--ink-2); }

/* 一级模块：图太密时，这排标签替读者把骨架说一遍 */
.iatabs { margin-top: var(--u5); display: flex; flex-wrap: wrap; gap: 8px; }
.iatabs span {
  padding: 9px 17px; border-radius: 999px;
  border: 1px solid var(--rule); background: var(--w);
  font-size: .86rem; letter-spacing: .012em;
}
.iatabs span:first-child { border-color: var(--pen-1); color: var(--pen-1); }

/* ------------------------------------------------------------
   .allui —— 全部界面速览
   对应作品集末尾那六页「快速预览 UI 设计」。原稿是六张
   5760×3240 的大拼图，网页上没必要照搬 —— 那二十张屏本来
   就是二十个单文件，直接排成网格，还能点进对应的案例页。
   ------------------------------------------------------------ */
.allui { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--gap); margin-top: var(--u6); }
.allui figure { margin: 0; }
.allui .sc {
  border-radius: 10px; overflow: hidden; background: var(--w-2);
  aspect-ratio: 680 / 1349;
}
.allui .sc img {
  display: block; width: 100%; height: 100%; object-fit: contain;
  transition: transform .7s var(--out);
}
.allui a:hover .sc img { transform: scale(1.03); }
.allui figcaption {
  margin-top: 12px; display: flex; align-items: baseline; gap: 8px;
  font-size: .8rem; color: var(--ink-2); letter-spacing: .012em;
}
.allui figcaption b { font-family: var(--mono); font-size: 10px; font-weight: 400; color: var(--ink-4); }
.allui a:hover figcaption { color: var(--pen-1); }
.allui a:hover figcaption b { color: var(--pen-1); }

.allui-head {
  display: flex; align-items: baseline; gap: var(--u3);
  margin-top: var(--u12); padding-bottom: var(--u2);
  border-bottom: 1px solid var(--ink);
}
.allui-head:first-of-type { margin-top: var(--u8); }
.allui-head .fill { flex: 1; height: 1px; background: var(--rule); }

@media (max-width: 1000px) { .allui { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 600px)  { .allui { grid-template-columns: repeat(2, 1fr); } }

/* ------------------------------------------------------------
   .ld —— 单张图还没到时的占位：一条群青扫描线来回走，中间一行等宽小字。
   写在标记里而不是 JS 里补：脚本自己在慢网速下也晚到。
   父元素加 .loaded 收起；加 .err 变成「加载失败 · 点击重试」。
   ------------------------------------------------------------ */
.ld {
  position: absolute; inset: 0; z-index: 1; overflow: hidden;
  display: none; place-items: center; pointer-events: none;
  background: var(--w-2);
}
.js .ld { display: grid; }
.ld i {
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent calc(100% - 1px), var(--pen-1) 0);
  opacity: .55; transform: translateX(-100%);
  animation: ldScan 1.9s var(--draw) infinite;
}
@keyframes ldScan { to { transform: translateX(0); } }
.ld b {
  position: relative; padding: 3px 8px; background: var(--w-2);
  font-family: var(--mono); font-size: 10.5px; font-weight: 400; letter-spacing: .08em; color: var(--ink-3);
}
.loaded > .ld { opacity: 0; visibility: hidden; transition: opacity .4s, visibility 0s .4s; }
.err > .ld { pointer-events: auto; }   /* 光标交给 main.js 的跟随圆环，不另设 pointer */
.err > .ld i { display: none; }
.err > .ld b { color: var(--pen-2); }

/* ------------------------------------------------------------
   .pdfdl —— 下载作品集：本站直接下载 / 百度网盘（附二维码）
   用原生 <dialog> + showModal()：焦点圈在弹窗里、Esc 关闭、背后页面不可点，都是浏览器给的。
   触发链接本身就是本站 PDF 的直链：没有 JS 或浏览器不支持 <dialog> 时照旧直接下载。
   维护页、在线查看页共用，所以放在这里。
   前缀用 pdfdl-：曾经只叫 dl，和维护页按钮的修饰类重名，按钮被套上了弹窗的 640px 宽和自动外边距。
   ------------------------------------------------------------ */
.pdfdl {
  margin: auto; padding: 0; border: 1px solid var(--ink); border-radius: 2px;
  width: min(640px, calc(100vw - 2 * var(--gut))); max-height: calc(100svh - 2 * var(--gut));
  background: var(--w); color: var(--ink);
  box-shadow: 0 30px 60px -30px rgba(17,17,20,.45);
}
.pdfdl::backdrop { background: rgba(255,255,255,.72); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.pdfdl[open] { animation: pdfdlIn .32s var(--snap); }
@keyframes pdfdlIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.pdfdl-hd {
  display: flex; align-items: center; gap: var(--u2);
  padding: 14px 18px; border-bottom: 1px solid var(--ink);
}
.pdfdl-hd .fill { flex: 1; }
.pdfdl-x { font-family: var(--mono); font-size: 11px; color: var(--ink-3); padding: 2px 4px; }
.pdfdl-x:hover { color: var(--pen-2); }
.pdfdl-bd { display: grid; grid-template-columns: 1fr 1fr; }
.pdfdl-opt { display: flex; flex-direction: column; gap: 10px; padding: var(--u4) var(--u3) var(--u3); }
.pdfdl-opt + .pdfdl-opt { border-left: 1px solid var(--rule); }
.pdfdl-opt .p-s { max-width: none; }
.pdfdl-opt .act { margin-top: auto; align-self: flex-start; }
.pdfdl-opt .act .arw { display: flex; }
.pdfdl-opt .act svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.pdfdl-opt .act.dn:hover .arw { transform: translateY(3px); }
.pdfdl-opt .act.out:hover .arw { transform: translate(2px, -2px); }
.pdfdl-qr { width: 148px; aspect-ratio: 1; background: #fff; border: 1px solid var(--rule); }
.pdfdl-spec { margin-top: 4px; }
.pdfdl-spec div {
  display: grid; grid-template-columns: 3.4em 1fr; gap: 8px; align-items: baseline;
  padding: 7px 0; border-top: 1px solid var(--rule-2);
  font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
}
.pdfdl-spec div:last-child { border-bottom: 1px solid var(--rule-2); }
.pdfdl-spec dt { color: var(--ink-3); }
.pdfdl-spec dd { color: var(--ink); word-break: break-all; }
.pdfdl-qr img { display: block; width: 100%; height: 100%; image-rendering: pixelated; }
.pdfdl-code { font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); }
.pdfdl-code b { font-weight: 500; color: var(--ink); letter-spacing: .08em; }
/* 弹窗在浏览器顶层，会盖住跟随光标 —— 打开期间把原生光标还回来 */
html.pdfdl-open, html.pdfdl-open * { cursor: auto !important; }
html.pdfdl-open a, html.pdfdl-open button { cursor: pointer !important; }
html.pdfdl-open .cur-ring, html.pdfdl-open .cur-dot, html.pdfdl-open .cur-read { opacity: 0; }
@media (max-width: 600px) {
  .pdfdl-bd { grid-template-columns: 1fr; }
  .pdfdl-opt + .pdfdl-opt { border-left: 0; border-top: 1px solid var(--rule); }
}
