/* ==========================================================================
   site.css —— 二次元日系美学：干净、明亮、有空气感
   图层顺序（务必保持）：#bg 氛围插画(0) → #danmaku 弹幕(1) → #particles 粒子(2)
                        → main 内容(5) → 光标(120) → .credit 署名(3, 在 #bg 内) → .nav 导航(40)
   ⚠️ 因为弹幕(1)在内容(5)之下，指针永远碰不到 .dm-item ——
      弹幕的悬停高亮只能由 JS 做几何命中检测（见 core.js 的 Auxia.danmakuAt）。
      任何给 .dm-item 写 :hover 的写法都是无效的。
   ========================================================================== */

/* 内容层必须显式抬起来，这一条不能删。
   原因：#bg 是 position:fixed + z-index:0，按 CSS 绘制顺序它排在
   「已定位元素」那一档，会盖住所有**未定位**的普通块级内容。
   .splash / .sec 各自带了 position:relative 所以一直没事，
   而 .proj-page / .nf 这类没有定位的容器会被整块盖住——
   DOM 里文字都在、自检也全过，但用户看到的是一片空白。
   与其逐个容器补定位，不如在 main 这一层统一兜住。 */
main { position: relative; z-index: 5; }

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: 1.9;
  letter-spacing: .01em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }

::selection { background: rgba(var(--accent-rgb), .22); }

/* 焦点可见性：无障碍硬要求，绝不删 */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.wrap { width: min(100% - 48px, var(--wrap)); margin-inline: auto; }
@media (max-width: 640px) { .wrap { width: min(100% - 32px, var(--wrap)); } }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--paper-2); padding: 10px 16px; border-radius: 0 0 var(--radius-sm) 0;
}
.skip:focus { left: 0; }

/* ==========================================================================
   1. 背景图系统（纯本地资源，无外部请求）
   ========================================================================== */
#bg {
  position: fixed; inset: 0; z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background: var(--sheen);
}

#bg .bg-img {
  position: absolute; inset: -4%;
  width: 108%; height: 108%;
  object-fit: cover;
  filter: blur(var(--bg-blur)) saturate(1.06);
  opacity: 0;
  transform: scale(1.06);
  transition: opacity 1.1s var(--ease), transform 1.4s var(--ease);
  will-change: opacity, transform;
}
#bg .bg-img.is-ready { opacity: 1; transform: scale(1); }

/* 白纱：把插画压成氛围，正文对比度由它保证。
   用一个 opacity 旋钮（--bg-veil）统一控制强度，内部渐变只负责形状，
   这样比在 rgba() 里塞 calc() 更可预测，也不会被某些渲染路径丢掉。 */
#bg .veil {
  position: absolute; inset: 0;
  opacity: var(--bg-veil);
  background:
    radial-gradient(135% 105% at 50% 6%, rgba(255,255,255,.5) 0%, rgba(255,255,255,0) 64%),
    linear-gradient(180deg, #ffffff 0%, #ffffff 100%);
}

/* 兜底状态：插画池取不到时用本地摄影，绝不开天窗 */
#bg .bg-fallback { opacity: 1; }

/* 悬停浮出署名（摄影兜底时用它；插画池没有可考的作者，故保持隐藏） */
#bg .credit {
  position: fixed; left: 50%; bottom: 22px; transform: translate(-50%, 14px);
  z-index: 3;
  pointer-events: auto;
  font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: .02em;
  color: var(--ink-2);
  background: rgba(255,255,255,.9);
  backdrop-filter: blur(10px);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 7px 15px;
  opacity: 0;
  transition: opacity .35s var(--ease), transform .35s var(--ease);
  white-space: nowrap; max-width: calc(100vw - 40px); overflow: hidden; text-overflow: ellipsis;
}
#bg .credit a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
body:hover #bg .credit { opacity: 1; transform: translate(-50%, 0); }
@media (hover: none) { #bg .credit { opacity: 1; transform: translate(-50%, 0); } }

/* ==========================================================================
   2. 弹幕
   —— 每秒 3 条从右往左飘过。密度靠 JS 的投放器控制，这里只管长什么样。
   ========================================================================== */
#danmaku {
  position: fixed; inset: 0; z-index: 1;
  pointer-events: none;
  overflow: hidden;
  opacity: var(--danmaku-opacity);
  /* 这里**不能**用 mix-blend-mode:multiply。
     multiply 会把文字与它下面的白纱/插画相乘，彩色背景上直接糊成一片脏色，
     再叠上低透明度就成了「肉眼不可见」。
     现在是普通叠加 + 极淡的白色光晕保证可读性（不是药丸底，不要加回来）。 */
  mask-image: linear-gradient(180deg, transparent 0%, #000 9%, #000 93%, transparent 100%);
}
.dm-lane { position: absolute; left: 0; width: 100%; height: 0; }

.dm-item {
  position: absolute;
  top: 0;
  white-space: nowrap;
  font-family: var(--font-mono);
  /* 字号基准 + 逐条随机倍率（core.js 写 --dm-scale，0.85~1.22）。
     倍率加在 clamp 外面，所以窄屏的响应式下限仍然有效，
     只是在此基础上再放大/缩小一点 —— 让弹幕有大有小，不至于一排一样高。 */
  font-size: calc(clamp(14.5px, 1.55vw, 19px) * var(--dm-scale, 1));
  letter-spacing: .04em;
  color: var(--ink);
  padding: 3px 8px;
  /* 没有背景、没有边框。只留一层很淡的白色光晕，
     让它在花哨的插画上不至于糊掉——这是可读性的下限，不是「气泡」。 */
  text-shadow: 0 1px 3px rgba(255, 255, 255, .85);
  /* ⚠️ 必须只跑一圈（iteration-count: 1）。
     这里原本是 `animation: dm-scroll linear infinite`，后果是：
     动画无限循环 => animationend **永远不触发** => 回收只剩「dur+3 秒」的兜底定时器
     说了算。也就是说每条弹幕跑完一整圈后，会在原地**从右侧重新进场、再飞 3 秒**
     才被删掉。
     而命中检测（core.js 的 boxes()）是按 `p ∈ [0,1]` 算位置的，第二圈的元素
     在它眼里已经跑完、直接被跳过 —— 于是就出现「明明看得见，指针扫上去却不高亮」。
     实测 25 条里稳定有 3 条处于这个状态（约 12%），正好对得上站主反馈的
     「高亮效果没实现」。改成跑一圈后 animationend 准时触发、立刻回收，
     p ∈ [0,1] 才真正等价于「在屏幕上」。 */
  animation-name: dm-scroll;
  animation-timing-function: linear;
  /* 真正用的时长由 core.js 逐条随机写在内联 style 上。
     这里给个兜底值：万一内联那句没执行，duration 会退化成 0s，
     动画瞬间走完 -> animationend 立刻触发 -> 弹幕刚生成就被回收，
     页面上一条都看不见（比"速度快得离谱"难排查得多）。 */
  animation-duration: 14s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  will-change: transform;
  /* 命中检测由 JS 解析几何完成（弹幕层在 main 之下，靠 :hover 永远收不到），
     所以这里必须保持 none：让弹幕完全不挡正文的点击。 */
  pointer-events: none;
}

/* 指针悬停到「某一条」之上 -> 该条高亮并停住，方便看清写的什么。
   注意这个类是 JS 加的（Auxia.danmakuAt 命中谁就给谁加），不是 :hover。 */
.dm-item.is-hot {
  color: var(--accent);
  font-weight: 600;
  text-shadow: 0 1px 4px rgba(255, 255, 255, .95);
}

@keyframes dm-scroll {
  from { transform: translate3d(100vw, 0, 0); }
  to   { transform: translate3d(calc(-100% - 100vw), 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  #danmaku { display: none; }
}

/* ==========================================================================
   3. 点击粒子
   ========================================================================== */
#particles { position: fixed; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
.pt {
  position: absolute; border-radius: 50%;
  transform: translate(-50%, -50%);
  animation: pt-fly var(--dur, 900ms) cubic-bezier(.16,.7,.3,1) forwards;
}
@keyframes pt-fly {
  from { opacity: .95; }
  to   { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(.25); }
}
@media (prefers-reduced-motion: reduce) { #particles { display: none; } }

/* ==========================================================================
   3.5 跟随指针的矢量光标
   —— 用 SVG 画一枚「四角星 + 虚线圈」，两层各自用不同的阻尼系数跟随，
      所以外圈永远拖在内核后面（非线性：每一帧只吃掉剩余距离的一部分，
      而不是匀速直线追过去）。速度越快，外圈被拉得越长、方向也随之旋转。
   ========================================================================== */

/* 只有 JS 确认「精细指针 + 未开启减少动效」时才挂 .cursor-on。
   绝不能直接写 body{cursor:none}——一旦 JS 挂掉，用户就会面对一个没有指针的页面。 */
html.cursor-on, html.cursor-on * { cursor: none !important; }

#cursor {
  position: fixed; inset: 0; z-index: 120;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s var(--ease);
}
#cursor.is-on { opacity: 1; }

#cursor .cur {
  position: absolute; left: 0; top: 0;
  display: grid; place-items: center;
  transform-origin: 50% 50%;
  will-change: transform;
}
#cursor .cur-ring { width: 40px; height: 40px; margin: -20px 0 0 -20px; }
#cursor .cur-core { width: 17px; height: 17px; margin: -8.5px 0 0 -8.5px; }
#cursor svg { display: block; width: 100%; height: 100%; overflow: visible; }

#cursor .ring-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-dasharray: 4 6;
  opacity: .85;
  transform-box: fill-box;
  transform-origin: center;
  animation: cur-spin 10s linear infinite;
}
#cursor .core-mark {
  fill: var(--accent);
  stroke: rgba(255,255,255,.92);
  stroke-width: .9;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform .3s var(--ease);
}

/* 悬停可点元素 / 按下时给一点反馈 */
#cursor.is-hover .core-mark { transform: scale(1.5); }
#cursor.is-press .core-mark { transform: scale(.7); }
#cursor.is-hover .ring-arc { stroke-dasharray: 2 4; opacity: 1; }

@keyframes cur-spin { to { transform: rotate(360deg); } }

/* 触摸设备与「减少动态效果」下完全不上这层 */
@media (prefers-reduced-motion: reduce) { #cursor { display: none; } }

/* ==========================================================================
   4. 顶栏
   ========================================================================== */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  height: var(--nav-h);
  display: flex; align-items: center;
  transition: background .4s var(--ease), border-color .4s var(--ease), backdrop-filter .4s;
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(255,255,255,.72);
  backdrop-filter: blur(16px) saturate(1.2);
  border-bottom-color: var(--line);
}
.nav .wrap { display: flex; align-items: center; justify-content: space-between; gap: 18px; }
.brand { display: flex; align-items: center; gap: 9px; font-weight: 700; letter-spacing: .04em; font-size: 17px; }
.brand .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .16);
}
.nav-links { display: flex; gap: 4px; font-size: 15.5px; }
.nav-links a {
  padding: 8px 14px; border-radius: var(--radius-pill); color: var(--ink-2);
  transition: color .25s, background .25s;
}
.nav-links a:hover { color: var(--ink); background: rgba(255,255,255,.7); }
@media (max-width: 760px) { .nav-links { display: none; } }

/* ==========================================================================
   5. 开屏
   —— 左边打字机标题（站酷小薇 LOGO 体），右边一个布朗运动小演示。
   第五轮改版：原来是「一句大字居中」，现在改成左右两栏。
   ========================================================================== */
.splash {
  position: relative;
  min-height: 100dvh;
  display: flex; flex-direction: column; justify-content: center;
  padding: calc(var(--nav-h) + 40px) 0 88px;
}

.splash-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
  align-items: center;
  gap: clamp(32px, 5vw, 76px);
}

.splash-title {
  font-family: var(--font-logo);
  font-size: clamp(34px, 5.6vw, 78px);
  line-height: 1.16;
  font-weight: 400;          /* 站酷小薇只有一档字重，写 800 会触发浏览器加粗，糊掉笔画 */
  letter-spacing: .015em;
  margin: 0;
  color: var(--ink);
}

/* 每一行独立成块，空行也占一行高 —— 打字机逐字敲的时候
   第二行是空的，不预留高度的话整块会往上缩、右边画布跟着跳。 */
.st-row {
  display: block;
  min-height: 1.16em;
  white-space: pre-wrap;      /* 保留行尾空格；也顺手挡住意外的折行 */
}

/* 光标：一条跟着字走的竖条。
   ⚠️ 用行内元素，不能用绝对定位 —— 绝对定位要自己去量每行文字的宽度，
   字体一换就得重算。行内光标由排版引擎自动摆到最后一个字后面。 */
.st-caret {
  display: inline-block;
  width: .06em; min-width: 2px;
  height: 1em;
  margin-left: .06em;
  vertical-align: -.12em;
  background: var(--base);
  opacity: 0;
}
.st-caret.is-on { opacity: 1; animation: caretBlink .92s step-end infinite; }
@keyframes caretBlink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .st-caret { opacity: 0 !important; animation: none !important; }
}

/* ---- 右侧演示 ---- */
.splash-demo {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, .42);
  box-shadow: 0 26px 62px -40px rgba(42, 39, 36, .5);
  overflow: hidden;
}
#brownian {
  display: block;
  width: 100%;
  height: clamp(260px, 34vh, 400px);
  /* 指针要能在画布上「施力」，必须显式关掉命中穿透之外的一切默认行为：
     光标形状 + 禁止触摸滚动抢手势 */
  cursor: crosshair;
  touch-action: none;
}
.splash-demo .demo-hint {
  display: flex; align-items: center; gap: 8px;
  margin: 0;
  padding: 10px 14px;
  border-top: 1px solid var(--line-2);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .06em;
  color: var(--ink-3);
}
.splash-demo .demo-hint .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--base);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .18);
}

/* 窄屏：标题在上、演示在下。演示不隐藏 —— 它是开屏的趣味点，
   只是压矮一点。 */
@media (max-width: 900px) {
  .splash-grid { grid-template-columns: minmax(0, 1fr); gap: 34px; }
  #brownian { height: clamp(200px, 26vh, 280px); }
}

.splash .scroll-hint {
  position: absolute; left: 50%; bottom: 30px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 12px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--ink-3);
}
.splash .scroll-hint .bar {
  width: 1px; height: 42px;
  background: linear-gradient(180deg, var(--ink-3), transparent);
  animation: hint 2.2s ease-in-out infinite;
}
@keyframes hint { 0%,100% { opacity: .25; transform: scaleY(.6); } 50% { opacity: 1; transform: scaleY(1); } }

/* ==========================================================================
   6. 区块通用
   ========================================================================== */
.sec { position: relative; padding: 104px 0; }
.sec + .sec { border-top: 1px solid var(--line-2); }
.sec-head { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; margin-bottom: 46px; }
.sec-num { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: .16em; color: var(--base); }
.sec-head h2 {
  font-family: var(--font-title);
  font-size: clamp(30px, 5vw, 48px);
  font-weight: 800; letter-spacing: .01em; margin: 0;
}
.sec-head .desc { color: var(--ink-3); font-size: var(--fs-sm); margin-left: auto; }

/* --------------------------------------------------------------------------
   卡片：全站统一的「玻璃」
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   ⚠️ 先纠正一个误解。站主第五轮说「液态玻璃只做进了 liar card，别处没有」。
      其实 backdrop-filter 一直在（上一版就有 blur(14px)）。
      真正的问题是**卡片背后没有东西可糊**：
        · --bg-veil 把背景插画压到 60% 白纱
        · --bg-blur 又把插画本身糊掉 9px
        · 卡片再叠 72% 白底
      三层白摞下来，卡片背后已经是接近纯白的一张纸 —— 再强的 blur 也糊不出
      任何可见的变化。所以这轮不是「加玻璃」，是**把玻璃做到看得见**：

        1. saturate + brightness —— 玻璃的关键从来不是模糊，是「增艳」。
           透过来的那点颜色被提纯提亮之后，才会有「隔着一层」的感觉。
        2. 顶边内高光 + 亮色描边 —— 玻璃棱边的折射。这一条最容易被忽略，
           但少了它，再好的模糊也只是一坨灰。
        3. 多层阴影 —— 把卡片从纸面上抬起来，而不是贴在上面。
        4. 跟着指针走的高光斑（下面 background 的第一层）—— 最像「液态」的一层。

   高光斑为什么做成 background 的一层、而不是 ::before：
       ::before 是 position:absolute，绘制顺序排在**未定位的子元素之后**，
       会盖到卡片里的文字上（即便加 mix-blend-mode 也还是糊字）。
       做成 background 的第一层就没有这个层叠问题 —— 它天然在内容之下，
       而且不用逐个去给子元素补 position/z-index。
   -------------------------------------------------------------------------- */
.card {
  position: relative;
  /* 第一层：指针高光。默认位置在卡片**上方外面**（--gy:-60%），
     所以不悬停时它根本不在框里，等于不存在 —— 不需要额外的 opacity 开关。 */
  background:
    radial-gradient(260px circle at var(--gx, 50%) var(--gy, -60%),
                     rgba(255,255,255,.62), rgba(255,255,255,.14) 45%, transparent 72%),
    linear-gradient(180deg, rgba(255,255,255,.82), rgba(255,255,255,.60));
  -webkit-backdrop-filter: blur(18px) saturate(1.85) brightness(1.045);
  backdrop-filter: blur(18px) saturate(1.85) brightness(1.045);
  border: 1px solid rgba(255,255,255,.62);
  border-radius: var(--radius);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,.92),
    inset 0 -1px 0 0 rgba(42,39,36,.045),
    0 18px 40px -26px rgba(42,39,36,.42),
    0 2px 8px -4px rgba(42,39,36,.10);
  transition: transform .38s var(--ease), box-shadow .38s var(--ease),
              border-color .38s;
}
.card:hover {
  transform: translateY(-4px);
  border-color: rgba(var(--accent-rgb), .40);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,.95),
    inset 0 -1px 0 0 rgba(42,39,36,.045),
    0 22px 50px -22px rgba(var(--accent-rgb), .48),
    0 2px 8px -4px rgba(42,39,36,.10);
}
/* 触摸设备没有指针，就别让它白算一个永远不动的高光 */
@media (hover: none) {
  .card {
    background: linear-gradient(180deg, rgba(255,255,255,.82), rgba(255,255,255,.60));
  }
}

/* --------------------------------------------------------------------------
   卡片逐个淡入
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   容器上已有 .reveal（滚动进入视口时整体淡入）。这里再加一层：
   **当容器亮起来时，里面的卡片依次浮上来**，而不是整块一起出现。

   ⚠️ animation-fill-mode 必须是 backwards，不能是 both / forwards：
      `.card:hover` 靠 transform 上浮，而 both/forwards 会把最后一帧的
      `transform: none` 永久保留在元素上，**压掉 hover 的位移** ——
      表现是「卡片进场后就不再有悬停反馈了」，而且很难联想到是这一行造成的。
      backwards 只在动画开始前生效，跑完就交还控制权，正好。 */
.reveal.is-on > .card { animation: cardIn .72s var(--ease) backwards; }
.reveal.is-on > .card:nth-child(1) { animation-delay: .00s; }
.reveal.is-on > .card:nth-child(2) { animation-delay: .09s; }
.reveal.is-on > .card:nth-child(3) { animation-delay: .18s; }
.reveal.is-on > .card:nth-child(4) { animation-delay: .27s; }
.reveal.is-on > .card:nth-child(5) { animation-delay: .36s; }
.reveal.is-on > .card:nth-child(6) { animation-delay: .45s; }
@keyframes cardIn {
  from { opacity: 0; transform: translateY(20px) scale(.986); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .reveal.is-on > .card { animation: none; }
}

/* ==========================================================================
   7. 自我介绍 —— 只有「人格模型 + 爱好」，不放任何自述文字
   —— 删掉自述之后，原来「左半栏爱好 / 右半栏人格卡」的两栏就空掉一半，
      两栏高度也难对齐。改成上下两个整宽区块：
      上面是横铺的人格卡（图在左、五条分值在右），下面跟一排爱好。
   ========================================================================== */
.intro-stack { display: grid; gap: 20px; }

/* ---- 大五人格 ---- */
.pentagon-card { padding: 26px 30px 28px; }
.penta-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.pentagon-card h3 {
  font-family: var(--font-title); font-size: var(--fs-xl); margin: 0; font-weight: 700;
}
.pentagon-card .sub {
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink-3);
  letter-spacing: .06em;
}
/* 「模型图横着放」就落在这一条：左图右条，整块横向铺满 */
.penta-body {
  display: grid;
  grid-template-columns: minmax(280px, 420px) minmax(260px, 1fr);
  gap: 22px 48px;
  align-items: center;
}
@media (max-width: 860px) { .penta-body { grid-template-columns: 1fr; gap: 22px; } }
.penta-figure { width: 100%; max-width: 420px; margin-inline: auto; }

.pentagon { width: 100%; height: auto; overflow: visible; }
.pentagon .grid-ring { fill: none; stroke: var(--line); stroke-width: 1; }
.pentagon .axis { stroke: var(--line); stroke-width: 1; stroke-dasharray: 3 4; }
.pentagon .shape {
  fill: rgba(var(--accent-rgb), .2);
  stroke: var(--accent); stroke-width: 2; stroke-linejoin: round;
  transform-origin: 50% 50%;
  transform: scale(.02); opacity: 0;
  transition: transform 1.15s var(--ease), opacity .7s ease;
}
.pentagon.is-in .shape { transform: scale(1); opacity: 1; }
.pentagon .pt-dot { fill: var(--paper-2); stroke: var(--accent); stroke-width: 2; }
.pentagon .dim-name { font-family: var(--font-body); font-size: 14.5px; fill: var(--ink); font-weight: 600; }
.pentagon .dim-val  { font-family: var(--font-mono); font-size: 13px; fill: var(--base); }
@media (prefers-reduced-motion: reduce) {
  .pentagon .shape { transition: none; transform: scale(1); opacity: 1; }
}

/* 图例：五条分值纵向铺满右半栏 */
.b5-legend { display: grid; gap: 15px; align-content: center; margin: 0; }
.b5-row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 6px 14px; font-size: 16.5px; }
.b5-row .track { grid-column: 1 / -1; height: 6px; border-radius: var(--radius-sm); background: var(--line); overflow: hidden; }
.b5-row .fill { height: 100%; width: 0; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--base), var(--accent)); transition: width 1.1s var(--ease); }
.b5-row .val { font-family: var(--font-mono); font-size: 14px; color: var(--ink-3); }

/* ---- 爱好：一排四张，均分整宽 ---- */
.hobbies { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 860px) { .hobbies { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hobby {
  display: flex; align-items: center; gap: 14px;
  padding: 18px 22px; border-radius: var(--radius);
  background: rgba(255,255,255,.78); border: 1px solid var(--line);
  backdrop-filter: blur(10px);
  transition: transform .3s var(--ease), border-color .3s, background .3s;
}
.hobby:hover { transform: translateY(-3px); border-color: rgba(var(--accent-rgb), .4); background: #fff; }
.hobby .ico { font-size: 26px; line-height: 1; }
.hobby .hb { display: flex; flex-direction: column; gap: 2px; line-height: 1.35; min-width: 0; }
.hobby .hb b { font-size: 19px; font-weight: 600; }
.hobby .hb small { font-family: var(--font-mono); font-size: 12.5px; color: var(--ink-3); letter-spacing: .06em; }

/* ==========================================================================
   8. 展墙橱窗
   ========================================================================== */
.walls-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 34px; }
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 22px; border-radius: var(--radius-pill);
  border: 1px solid var(--line); background: rgba(255,255,255,.78);
  backdrop-filter: blur(10px);
  font-size: 15.5px; color: var(--ink);
  transition: transform .28s var(--ease), border-color .28s, background .28s, box-shadow .28s;
}
.btn:hover { transform: translateY(-2px); border-color: rgba(var(--accent-rgb), .45); box-shadow: 0 10px 26px -12px rgba(var(--accent-rgb), .5); }
.btn.primary { background: var(--base); border-color: var(--base); color: #fff; }
.btn.primary:hover { background: var(--accent); border-color: var(--accent); }
.btn .arw { transition: transform .28s var(--ease); }
.btn:hover .arw { transform: translateX(3px); }

.wall-row { margin-bottom: 42px; }
.wall-row-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 16px; }
.wall-row-head h3 { font-family: var(--font-title); font-size: 22px; margin: 0; font-weight: 700; }
.wall-row-head .count { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink-3); letter-spacing: .06em; }
.wall-row-head a { margin-left: auto; font-size: 15px; color: var(--base); }
.wall-row-head a:hover { color: var(--accent); }

.wall-pair { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
@media (max-width: 700px) { .wall-pair { grid-template-columns: 1fr; } }

/* 文字墙的橱窗用「索引行」：一张小缩略图 + 标题，十件一屏看得完。
   刻意不再渲染「来源 / 题名卡」这类解释性小字。 */
.show-rows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 720px) { .show-rows { grid-template-columns: 1fr; } }

.show-row {
  display: flex; align-items: center; gap: 16px;
  padding: 13px; border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, .74);
  backdrop-filter: blur(12px);
  transition: transform .32s var(--ease), border-color .32s, box-shadow .32s;
}
.show-row:hover {
  transform: translateY(-3px);
  border-color: rgba(var(--accent-rgb), .42);
  box-shadow: 0 12px 30px -16px rgba(var(--accent-rgb), .55);
}
.show-row .thumb {
  flex: 0 0 auto; width: 68px; height: 98px;
  border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--line-2); background: var(--sheen);
}
.show-row .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.show-row .thumb.is-card {
  display: grid; place-items: center;
  font-family: var(--font-title); font-size: 24px; font-weight: 800;
  color: var(--base);
  background:
    radial-gradient(120% 100% at 50% 0%, var(--sheen) 0%, transparent 70%),
    var(--paper-2);
}
.show-row .meta { min-width: 0; flex: 1; }
.show-row .meta b {
  display: block; font-size: 16.5px; font-weight: 600; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.tile {
  display: block; position: relative; overflow: hidden;
  border-radius: var(--radius); border: 1px solid var(--line);
  background: var(--paper-2);
  aspect-ratio: 3 / 2;
  transition: transform .42s var(--ease), box-shadow .42s var(--ease), border-color .42s;
}
.tile:hover { transform: translateY(-5px); border-color: rgba(var(--accent-rgb), .4); box-shadow: 0 18px 44px -20px rgba(var(--accent-rgb), .55); }
.tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.tile:hover img { transform: scale(1.045); }
.tile .cap {
  position: absolute; inset: auto 0 0 0;
  padding: 30px 16px 13px;
  background: linear-gradient(180deg, transparent, rgba(20,16,14,.72));
  color: #fff; font-size: 16px; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tile .cap small { display: block; font-family: var(--font-mono); font-size: 12px; opacity: .78; letter-spacing: .06em; }

/* 题名卡的完整样式在下面 .card-tile 之后。
   ⚠️ 必须写在 .card-tile 之后，否则 display / background 会被 .card-tile 覆盖掉。 */

/* ==========================================================================
   9. 项目
   ========================================================================== */
.proj-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 22px; }
@media (max-width: 800px) { .proj-grid { grid-template-columns: 1fr; } }
.proj { padding: 32px 30px; display: flex; flex-direction: column; min-height: 100%; }
.proj .tag {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--base); margin-bottom: 16px;
}
.proj h3 { font-family: var(--font-title); font-size: 26px; margin: 0 0 12px; font-weight: 800; letter-spacing: .01em; }
.proj p { color: var(--ink-2); font-size: 16.5px; margin: 0 0 22px; flex: 1; }
.proj .foot { display: flex; align-items: center; gap: 10px; }
.status {
  font-size: var(--fs-xs); font-family: var(--font-mono);
  padding: 5px 12px; border-radius: var(--radius-pill);
  background: rgba(var(--accent-rgb), .12); color: var(--base);
  border: 1px solid rgba(var(--accent-rgb), .22);
}
.proj .go { margin-left: auto; font-size: 15px; color: var(--base); display: flex; align-items: center; gap: 6px; }
.proj .go .arw { transition: transform .3s var(--ease); }
.proj:hover .go .arw { transform: translateX(4px); }

/* ==========================================================================
   10. 联系方式（防爬：明文不进 DOM）
   ========================================================================== */
.contact-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
@media (max-width: 820px) { .contact-grid { grid-template-columns: 1fr; } }
.contact-card { padding: 28px 26px; text-align: left; width: 100%; }
.contact-card .label { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: .14em; text-transform: uppercase; color: var(--base); }
.contact-card .value {
  margin-top: 14px; min-height: 38px;
  font-family: var(--font-mono); font-size: clamp(16px, 2.2vw, 21px); font-weight: 600;
  letter-spacing: .02em; word-break: break-all;
  display: flex; align-items: center; gap: 10px;
}
.contact-card .value.masked { color: var(--ink-3); font-size: 15px; font-weight: 400; letter-spacing: .1em; }
.contact-card .hint { margin-top: 9px; font-size: 14px; color: var(--ink-3); }
.contact-card .copy {
  margin-top: 15px; font-size: 13.5px; color: var(--base); opacity: 0;
  transition: opacity .3s;
}
.contact-card.revealed .copy { opacity: 1; }
.contact-note { margin-top: 20px; font-size: 13.5px; color: var(--ink-3); }

/* ==========================================================================
   11. 页脚 / 404 / 通用
   —— 页脚只保留可折叠的版权说明，不再放签名与段子。
   ========================================================================== */
.foot {
  padding: 72px 0 84px; border-top: 1px solid var(--line-2);
  text-align: center;
}
.foot .legal {
  max-width: 72ch; margin-inline: auto;
  font-size: var(--fs-xs); line-height: 2; color: var(--ink-3); text-align: left;
}
.foot .legal summary {
  cursor: pointer; text-align: center; font-family: var(--font-mono); letter-spacing: .12em;
  color: var(--ink-2);
}
.foot .legal p { margin: 14px 0 0; }

.reveal { opacity: 0; transform: translateY(24px); transition: opacity .85s ease, transform .85s var(--ease); }
.reveal.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

.page-head { padding: calc(var(--nav-h) + 84px) 0 44px; }
.page-head h1 {
  font-family: var(--font-title);
  font-size: clamp(34px, 6.6vw, 68px); font-weight: 800; margin: 0 0 14px; letter-spacing: .01em;
}
/* 页头副行只放「有多少件」这种事实，不放解释性段落 */
.page-head .page-sub { color: var(--ink-3); margin: 0; font-size: var(--fs-sm); font-family: var(--font-mono); letter-spacing: .04em; }
.page-head .page-sub strong { color: var(--base); font-size: 18px; }
.crumb { font-family: var(--font-mono); font-size: 13px; letter-spacing: .12em; color: var(--ink-3); margin-bottom: 20px; display: inline-block; }
.crumb:hover { color: var(--base); }

/* 展墙总页 */
.wall-block { padding: 64px 0; }
.wall-block + .wall-block { border-top: 1px solid var(--line-2); }
.wall-block-head { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; margin-bottom: 26px; }
.wall-block-head h2 { font-family: var(--font-title); font-size: clamp(26px, 4.2vw, 40px); margin: 0; font-weight: 800; }
.wall-block-head .meta { font-family: var(--font-mono); font-size: 13.5px; color: var(--ink-3); letter-spacing: .08em; }

.grid-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(214px, 1fr)); gap: 18px; }
.grid-cards.masonry { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
@media (max-width: 520px) { .grid-cards, .grid-cards.masonry { grid-template-columns: repeat(2, 1fr); gap: 10px; } }

.card-tile {
  position: relative; overflow: hidden;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--paper-2);
  aspect-ratio: 2 / 3; display: block;
  transition: transform .38s var(--ease), box-shadow .38s var(--ease), border-color .38s;
}
.card-tile:hover { transform: translateY(-5px); border-color: rgba(var(--accent-rgb), .45); box-shadow: 0 16px 40px -20px rgba(var(--accent-rgb), .6); }
.card-tile.square { aspect-ratio: 1 / 1; }
.card-tile.wide { aspect-ratio: 16 / 9; }
.card-tile img { width: 100%; height: 100%; object-fit: cover; }
.card-tile .name {
  position: absolute; inset: auto 0 0 0; padding: 28px 13px 12px;
  background: linear-gradient(180deg, transparent, rgba(20,16,14,.74));
  color: #fff; font-size: 15.5px; font-weight: 500;
  line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.card-tile .name small { display: block; font-family: var(--font-mono); font-size: 11.5px; opacity: .75; }

/* ---- 题名卡：抓不到封面的展品（书籍 / 街机音游 / 同人曲）用日系排版兜底 ----
   只留名字 + 一道分隔线。墙名与「题名卡」字样属于解释性文字，一概不渲染。 */
.card-tile.titlecard {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 12px; padding: 22px 15px 18px; text-align: center;
  background:
    radial-gradient(130% 95% at 50% 0%, var(--sheen) 0%, transparent 64%),
    repeating-linear-gradient(45deg, rgba(0,0,0,.017) 0 8px, transparent 8px 16px),
    var(--paper-2);
}
.card-tile.titlecard .tc-name {
  font-family: var(--font-title);
  font-size: clamp(20px, 2.1vw, 28px);
  line-height: 1.35; font-weight: 800; color: var(--ink);
  word-break: break-word; max-width: 100%;
}
.card-tile.titlecard .tc-rule {
  width: 42px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--base), var(--accent));
}

/* 照片墙用真图比例，避免裁切 */
.photo-grid { columns: 4 220px; column-gap: 14px; }
@media (max-width: 520px) { .photo-grid { columns: 2 140px; column-gap: 10px; } }
.photo-grid figure {
  margin: 0 0 14px; break-inside: avoid;
  border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--line); background: var(--paper-2);
  transition: transform .38s var(--ease), box-shadow .38s var(--ease);
  position: relative; display: block;
}
.photo-grid figure:hover { transform: translateY(-4px); box-shadow: 0 16px 40px -20px rgba(var(--accent-rgb), .6); }
.photo-grid img { width: 100%; height: auto; }
.photo-grid figcaption {
  position: absolute; inset: auto 0 0 0; padding: 26px 12px 10px;
  background: linear-gradient(180deg, transparent, rgba(20,16,14,.7));
  color: #fff; font-family: var(--font-mono); font-size: 12.5px; opacity: 0; transition: opacity .3s;
}
.photo-grid figure:hover figcaption { opacity: 1; }

/* 灯箱 */
.lightbox {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(250, 248, 245, .93);
  backdrop-filter: blur(20px);
  display: none; place-items: center; padding: 5vh 4vw;
}
.lightbox.is-open { display: grid; }
.lightbox img { max-width: 100%; max-height: 80dvh; border-radius: var(--radius); box-shadow: 0 30px 80px -30px rgba(0,0,0,.5); }
.lightbox .lb-cap { position: fixed; left: 50%; bottom: 4vh; transform: translateX(-50%); font-family: var(--font-mono); font-size: 13.5px; color: var(--ink-2); }
.lightbox .lb-close { position: fixed; top: 22px; right: 26px; font-size: 16px; color: var(--ink-2); }

/* 项目占位页 */
.proj-page { padding: calc(var(--nav-h) + 88px) 0 104px; max-width: 760px; }
.proj-page .tag { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: .18em; text-transform: uppercase; color: var(--base); }
.proj-page h1 { font-family: var(--font-title); font-size: clamp(38px, 7.4vw, 74px); margin: 16px 0 20px; font-weight: 800; }
.proj-page .lead { color: var(--ink-2); font-size: 18.5px; margin-bottom: 36px; }
.proj-page .slot {
  margin-top: 36px; padding: 36px 28px; text-align: center;
  border: 1px dashed rgba(var(--accent-rgb), .4);
  border-radius: var(--radius);
  background: rgba(255,255,255,.6);
  color: var(--ink-2); font-size: 16px;
}
.proj-page .slot b { display: block; font-family: var(--font-mono); font-size: 13px; color: var(--base); margin-bottom: 10px; letter-spacing: .1em; }
.proj-page .back { display: inline-flex; align-items: center; gap: 8px; margin-top: 36px; font-size: 15.5px; color: var(--base); }
/* 带内嵌 Demo 的项目页要放宽，760px 装不下一个能玩的界面 */
.proj-page.wide { max-width: 1020px; }
/* starcraft3 那个程序 #app 写死 min-width:1024px，需要整幅 --wrap 才不出横向滚动条 */
.proj-page.ultra { max-width: var(--wrap); }

/* --------------------------------------------------------------------------
   项目页内嵌的可玩 Demo
   —— 为什么用 iframe 而不是把游戏直接搬进本站：
      Liar Card 是个自带样式与状态的小程序（自己的深色主题、布局、字号），
      直接塞进来会和本站的样式表互相渗透 ——
      本站的 body / button / .card / .slot 这些规则会顺着层叠糊到它身上。
      iframe 给它一个干净的沙箱：本站的导航、背景、弹幕照旧，
      两边都不用为对方改一行代码。
   -------------------------------------------------------------------------- */
.embed { margin-top: 44px; }
.embed-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.embed-head b { font-size: var(--fs-sm); font-weight: 600; }
.embed-head .sub {
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink-3);
}
.embed-head .open {
  margin-left: auto; font-size: var(--fs-xs); color: var(--base);
  text-decoration: none; border-bottom: 1px solid rgba(var(--accent-rgb), .35);
}
.embed-head .open:hover { border-bottom-color: var(--accent); }
.embed-box {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  /* 和应用内底色一致：加载的那一瞬间不会闪一块白 */
  background: #0e0f15;
  box-shadow: 0 26px 64px -34px rgba(42, 39, 36, .55);
}
.embed-box iframe {
  display: block; width: 100%; border: 0;
  height: clamp(600px, 82vh, 880px);
  background: #0e0f15;
}
@media (max-width: 700px) {
  .embed-box iframe { height: 78vh; }
  .embed-head .open { margin-left: 0; }
}

/* 404 */
.nf { min-height: 100dvh; display: grid; place-items: center; text-align: center; padding: 40px 0; }
.nf .code { font-family: var(--font-title); font-size: clamp(76px, 19vw, 184px); font-weight: 800; line-height: 1; }
.nf .code span { background: linear-gradient(100deg, var(--base), var(--accent)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.nf p { color: var(--ink-2); margin: 20px 0 32px; }

.noscript-note {
  position: fixed; inset: auto 0 0 0; z-index: 100;
  background: var(--base); color: #fff; text-align: center;
  padding: 11px; font-size: 14px;
}
