/* ===== 纯七壁纸 · 设计系统 =====
   令牌：间距 4/8/12/16/24/32 · 圆角 6/8/12/16/22/pill · 字号 11~22
   移动端：触控目标 ≥44px、卡片信息常驻、预览改底部面板

   ── 动效清单（作用范围 / 触发时机 / 时长）──────────────────────────
   全部只用 transform / translate / opacity 等合成属性，不触发布局重排。
   ① 卡片滚动渐显   范围 .card           触发：进入视口（IntersectionObserver）
                    时长 460ms + 批内 70ms×n 错峰（n≤6），位移 22px + 轻微缩放
   ② 卡片悬停抬起   范围 .card           触发：hover        时长 360ms，位移 4px
   ③ 缩略图悬停放大 范围 .card .media img 触发：hover        时长 360ms，scale 1.045
   ④ 顶栏滚动加深   范围 .topbar         触发：scrollY>8px  时长 360ms（背景/投影）
   ⑤ 侧栏滑块位移   范围 .ind            触发：切换选中项    时长 240ms
   ⑥ 悬停高光流动   范围 侧栏按钮/模式按钮 触发：hover        时长 900ms，单次扫过
   ⑦ 收藏心跳       范围 .card-fav/.beat  触发：点击收藏      时长 460ms，单次
   ⑧ 预览面板升起   范围 .ov-panel        触发：打开预览      时长 520ms，位移 18px
   ⑨ 沉浸模式进出场 范围 栅格/侧栏/顶栏/底栏 触发：切换按钮/Esc 时长 520ms
   ⑩ 背景光斑漂移   范围 .orb            触发：常驻          时长 32s/41s/53s 循环
   ⑪ 骨架屏流光     范围 .skel           触发：加载中        时长 1.35s 循环
   ⑫ 加载/提示/聚焦 范围 spinner/toast/:focus-visible 触发：对应状态  时长 160~360ms
   ⑬~⑳ 属于各扩展模块，清单就近写在该模块段落的注释里（心灵频道 / 滚动流动画廊）。
   ⚠️ ㉑（翻转入场）与 ㉒（翻牌）曾属于「随机一张翻牌卡」，该模块已按用户要求**整块移除**，
      编号**留空不重号**——编号是有历史的稳定 ID，重号会让旧日志与旧 commit 对不上。
   ── 高端作品集式动效（assets/motion.js）────────────────────────
   ㉓ 首屏揭幕      .intro           页面加载（仅顶层帧） 约 1900ms 单次
                    字标逐字遮罩升起 → 分隔线展开 → 幕布整体抽走；点击/按键/滚动即跳过
   ㉔ 模块大标题     .mod-en          滚动进入视口        900ms 单次
                    遮罩揭开（clip-path）+ 上推位移 + 压缩后归位（scale 1.06/.84 → 1）
   ㉕ 图片揭显      .card .media img  进入视口且已就绪    760ms 单次：scale 1.06 → 1 + 淡入
   ── 无障碍：prefers-reduced-motion 下 ①⑤⑥⑧⑩⑪ 与 ㉓㉔㉕ 全部瞬时化或移除，
      卡片与图片直接可见（不会停在 opacity:0）；
      未启用 JS（无 html.has-reveal）时不套用 ① 的隐藏初态，
      无 html.has-motion（降级 / 无 IO / motion.js 没加载）时不套用 ㉔㉕ 的隐藏初态。
   ────────────────────────────────────────────────────────────── */

:root{
  /* 颜色 */
  --bg:#0d0b16;
  --bg-2:#151225;
  --surface:rgba(255,255,255,.055);
  --surface-2:rgba(255,255,255,.09);
  --surface-3:rgba(255,255,255,.14);
  --line:rgba(255,255,255,.10);
  --line-strong:rgba(255,255,255,.18);
  --text:#f4f1fa;          /* 主文本 */
  --text-dim:#b3aacb;      /* 次要文本 对比度 ≈7:1 */
  --text-mute:#8f88a8;     /* 辅助文本 对比度 ≈5.7:1（原 #7a7295 仅 4.27:1） */
  --pink:#ec4899;
  --grad:linear-gradient(115deg,#a855f7 0%,#ec4899 52%,#f97316 100%);
  --grad-soft:linear-gradient(115deg,rgba(168,85,247,.20),rgba(236,72,153,.20));

  /* 间距（8px 基准） */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:40px;

  /* 圆角 */
  --r-xs:6px; --r-sm:8px; --r-md:12px; --r-lg:16px; --r-xl:22px; --r-pill:999px;

  /* 阴影 */
  --sh-1:0 2px 10px rgba(0,0,0,.30);
  --sh-2:0 10px 30px rgba(0,0,0,.42);
  --sh-3:0 22px 60px rgba(0,0,0,.55);

  /* 字号与行高 */
  --fs-xs:11px; --fs-sm:12px; --fs-md:13px; --fs-base:14px;
  --fs-lg:15px; --fs-xl:18px; --fs-2xl:22px;
  --lh-tight:1.35; --lh:1.6; --lh-loose:1.75;

  --topbar-h:64px;

  /* 动效令牌：全站统一时长 + 缓动曲线（合成属性优先：transform / translate / opacity） */
  --dur-1:160ms;      /* 微反馈：按压、hover 着色 */
  --dur-2:240ms;      /* 常规：按钮、指示器位移 */
  --dur-3:360ms;      /* 位移：卡片 hover、淡入淡出 */
  --dur-4:520ms;      /* 结构：栅格收放、沉浸进出场、面板升起 */
  --dur-reveal:460ms; /* 滚动渐显：卡片进入视口时的一次性出场 */
  --dur-5:900ms;      /* 氛围：高光扫过、光斑漂移 */
  --dur-6:1200ms;     /* 首屏揭幕：氛围段（字标升起、幕布抽走） */
  --dur-7:900ms;      /* 模块大标题：遮罩揭开 + 压缩归位 */
  --ease:cubic-bezier(.22,.9,.28,1);        /* 通用进出 */
  --ease-soft:cubic-bezier(.36,.12,.28,1);  /* 位移，收尾更柔 */
  --ease-out:cubic-bezier(.16,.84,.44,1);   /* 强调出场 */
  --ease-expo:cubic-bezier(.16,1,.3,1);     /* 猛进慢收：高端作品集站的标配曲线，无回弹 */
  --ease-in-expo:cubic-bezier(.7,0,.84,0);  /* 缓起急收：用于「甩出去」那一段 */
}

*{box-sizing:border-box}
/* 作者样式里的 display 会覆盖浏览器默认的 [hidden]{display:none}，这里统一兜底 */
[hidden]{display:none!important}

html{-webkit-text-size-adjust:100%}
html,body{margin:0;padding:0}
body{
  font:var(--fs-base)/var(--lh) "PingFang SC","Microsoft YaHei","Hiragino Sans GB",system-ui,-apple-system,"Segoe UI",sans-serif;
  background:var(--bg);
  color:var(--text);
  min-height:100vh;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -webkit-tap-highlight-color:transparent;
}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;touch-action:manipulation}
svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
::selection{background:rgba(236,72,153,.35)}

/* 统一焦点反馈（键盘可达） */
:where(a,button,input):focus-visible{
  outline:2px solid rgba(236,72,153,.75);
  outline-offset:2px;
  border-radius:var(--r-sm);
}

/* 背景光斑：缓慢漂移（只用 transform，走合成层；reduce 降级为静止） */
.bg-orbs{position:fixed;inset:0;z-index:0;overflow:hidden;pointer-events:none}
.orb{
  position:absolute;border-radius:50%;opacity:.6;
  will-change:transform;
  animation:drift-a 32s var(--ease-soft) infinite;
}
.orb-1{width:640px;height:640px;background:radial-gradient(circle at 50% 50%,#7c3aed 0%,rgba(124,58,237,.5) 40%,rgba(124,58,237,0) 70%);top:-220px;left:-160px}
.orb-2{width:560px;height:560px;background:radial-gradient(circle at 50% 50%,#db2777 0%,rgba(219,39,119,.5) 40%,rgba(219,39,119,0) 70%);bottom:-240px;right:-140px;animation-name:drift-b;animation-duration:41s}
.orb-3{width:460px;height:460px;background:radial-gradient(circle at 50% 50%,#2563eb 0%,rgba(37,99,235,.45) 40%,rgba(37,99,235,0) 70%);top:36%;left:52%;opacity:.4;animation-name:drift-c;animation-duration:53s}

@keyframes drift-a{
  0%,100%{transform:translate3d(0,0,0) scale(1)}
  33%{transform:translate3d(62px,46px,0) scale(1.10)}
  66%{transform:translate3d(-40px,84px,0) scale(.96)}
}
@keyframes drift-b{
  0%,100%{transform:translate3d(0,0,0) scale(1)}
  50%{transform:translate3d(-74px,-58px,0) scale(1.12)}
}
@keyframes drift-c{
  0%,100%{transform:translate3d(0,0,0) scale(1)}
  40%{transform:translate3d(56px,-72px,0) scale(1.08)}
  75%{transform:translate3d(-48px,-28px,0) scale(.94)}
}

/* 毛玻璃 */
.glass{
  background:var(--surface);
  backdrop-filter:blur(20px);
  -webkit-backdrop-filter:blur(20px);
  border:1px solid var(--line);
}

/* ===== 顶栏 ===== */
.topbar{
  position:sticky;top:0;z-index:50;
  min-height:var(--topbar-h);
  display:flex;align-items:center;gap:var(--sp-4);
  padding:0 var(--sp-5);
  border-width:0 0 1px;
  border-radius:0;
  transition:min-height var(--dur-3) var(--ease),
             padding var(--dur-3) var(--ease),
             border-radius var(--dur-3) var(--ease),
             transform var(--dur-4) var(--ease),
             opacity var(--dur-3) var(--ease),
             background-color var(--dur-3) var(--ease),
             box-shadow var(--dur-3) var(--ease);
}
/* 滚动加深：跨过 8px 才切类，JS 只做一次布尔比较，不逐帧写样式 */
.topbar.scrolled{
  background-color:rgba(10,8,18,.66);
  box-shadow:0 8px 28px rgba(0,0,0,.36);
}
.brand{display:flex;align-items:center;gap:var(--sp-3);flex-shrink:0;cursor:pointer;user-select:none;color:inherit;text-decoration:none}
.brand:active{transform:translateY(1px)}
.brand-mark{
  width:36px;height:36px;border-radius:var(--r-md);
  background:var(--grad);
  display:grid;place-items:center;
  font-weight:700;font-size:17px;color:#fff;
  box-shadow:0 6px 20px rgba(168,85,247,.4);
}
.brand-text{display:flex;flex-direction:column;line-height:var(--lh-tight)}
.brand-text strong{font-size:var(--fs-lg);font-weight:650;letter-spacing:.3px}
.brand-text span{font-size:var(--fs-xs);color:var(--text-mute);margin-top:2px}

.search-wrap{
  position:relative;flex:1;max-width:460px;margin-left:auto;
  display:flex;align-items:center;
}
.search-wrap svg.search-icon{
  position:absolute;left:13px;width:16px;height:16px;color:var(--text-mute);pointer-events:none
}
#search{
  width:100%;height:40px;padding:0 38px 0 38px;
  background:var(--surface-2);
  border:1px solid var(--line);
  border-radius:var(--r-pill);
  color:var(--text);font-size:var(--fs-md);outline:none;
  transition:border-color .25s,box-shadow .25s,background .25s;
}
#search:hover{background:var(--surface-3)}
#search:focus{border-color:rgba(236,72,153,.55);box-shadow:0 0 0 3px rgba(236,72,153,.13)}
#search::placeholder{color:var(--text-mute)}
.search-clear{
  position:absolute;right:10px;width:24px;height:24px;border-radius:50%;
  display:grid;place-items:center;font-size:16px;color:var(--text-mute);
  background:var(--surface-3);line-height:1;
  transition:.2s var(--ease);
}
.search-clear:hover{color:#fff;background:rgba(255,255,255,.22)}
.search-clear:active{transform:scale(.92)}

.top-actions{display:flex;align-items:center;gap:var(--sp-2)}
.icon-btn{
  position:relative;width:40px;height:40px;border-radius:var(--r-md);
  display:grid;place-items:center;color:var(--text-dim);
  border:1px solid transparent;
  transition:.22s var(--ease);
}
.icon-btn:hover{color:#fff;background:var(--surface-2);border-color:var(--line)}
.icon-btn:active{transform:scale(.94);background:var(--surface-3)}
.icon-btn.active{color:#fff;background:var(--grad-soft);border-color:rgba(236,72,153,.4)}
/* 沉浸按钮：进入后图标由「展开」换成「收起」 */
#btnImmerse .ic-out{display:none}
body.immersive #btnImmerse .ic-in{display:none}
body.immersive #btnImmerse .ic-out{display:block}
.badge{
  position:absolute;top:2px;right:1px;min-width:17px;height:17px;padding:0 5px;
  border-radius:var(--r-pill);background:var(--pink);color:#fff;
  font-size:10px;line-height:17px;text-align:center;font-weight:600;
}
.layout-switch{
  display:flex;gap:2px;padding:3px;
  background:var(--surface-2);border-radius:var(--r-md);border:1px solid var(--line);
}

/* ===== 骨架 ===== */
.shell{
  position:relative;z-index:1;
  /* minmax(0,1fr)：防止子内容（如横向 chips 的 min-content）把轨道撑爆视口 */
  display:grid;grid-template-columns:224px minmax(0,1fr);gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5) var(--sp-6);
  max-width:1680px;margin:0 auto;
  align-items:start;
  /* 沉浸模式进出场：轨道/间距/内边距一起收放 */
  transition:grid-template-columns var(--dur-4) var(--ease),
             gap var(--dur-4) var(--ease),
             padding var(--dur-4) var(--ease),
             max-width var(--dur-4) var(--ease);
}

/* ===== 玻璃容器滚动条 =====
   细窄、圆角、半透明；静止淡出 → 滚动淡入 → 悬停加深。
   注意：Blink 121+ 起同时支持标准的 scrollbar-width/color，一旦声明就会
   顶掉 ::-webkit-scrollbar 的定制（圆角内缩、thumb:hover 全部失效）。
   所以这里按引擎分流：WebKit/Blink 走伪元素，Firefox 走标准属性。 */
.sidebar,.ov-panel,.ov-story{scrollbar-gutter:stable}  /* 预留滑槽，滚动条出现时内容不跳动 */

/* —— WebKit / Blink 路径 —— */
.sidebar::-webkit-scrollbar,
.ov-panel::-webkit-scrollbar,
.ov-story::-webkit-scrollbar{width:10px;height:10px}
.sidebar::-webkit-scrollbar-track,
.ov-panel::-webkit-scrollbar-track,
.ov-story::-webkit-scrollbar-track{background:transparent}
.sidebar::-webkit-scrollbar-thumb,
.ov-panel::-webkit-scrollbar-thumb,
.ov-story::-webkit-scrollbar-thumb{
  background-color:rgba(255,255,255,.09);   /* 静止：几乎隐形，只留一丝可滚动暗示 */
  background-clip:content-box;
  border:3px solid transparent;             /* 内容盒内缩 → 视觉宽度 4px */
  border-radius:var(--r-pill);
  transition:background-color var(--dur-3) var(--ease);
}
.sidebar.is-scrolling::-webkit-scrollbar-thumb,
.ov-panel.is-scrolling::-webkit-scrollbar-thumb,
.ov-story.is-scrolling::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,.30)}  /* 滚动中淡入 */
.sidebar::-webkit-scrollbar-thumb:hover,
.ov-panel::-webkit-scrollbar-thumb:hover,
.ov-story::-webkit-scrollbar-thumb:hover{background-color:rgba(236,72,153,.62)}          /* 悬停加深 */

/* —— Firefox 路径（Blink 解析不了 ::-webkit-scrollbar，故只在此生效） —— */
@supports not selector(::-webkit-scrollbar){
  .sidebar,.ov-panel,.ov-story{
    scrollbar-width:thin;
    scrollbar-color:rgba(255,255,255,.09) transparent;
  }
  .sidebar.is-scrolling,.ov-panel.is-scrolling,.ov-story.is-scrolling{
    scrollbar-color:rgba(255,255,255,.30) transparent;   /* 注：Firefox 无法单独给滑块做 hover 态 */
  }
  @media(max-width:1100px){ .side-list{scrollbar-width:none} }
}

/* 横向 chips 滚动条：直接隐藏，交给滑动手势 */
@media(max-width:1100px){
  .side-list{scrollbar-width:none}
  .side-list::-webkit-scrollbar{display:none}
}

/* ===== 选中项滑块指示器 ===== */
.mode-switch,.side-list{position:relative}
.ind{
  position:absolute;top:0;left:0;width:0;height:0;z-index:0;
  opacity:0;pointer-events:none;
  transition:transform var(--dur-2) var(--ease-soft),
             width var(--dur-2) var(--ease-soft),
             height var(--dur-2) var(--ease-soft),
             opacity var(--dur-2) linear;
}
.mode-switch .ind{
  border-radius:var(--r-sm);
  background:var(--grad);
  box-shadow:0 5px 16px rgba(168,85,247,.34);
}
.side-list .ind{
  border-radius:var(--r-md);
  background:var(--grad-soft);
  box-shadow:inset 0 0 0 1px rgba(236,72,153,.32);
}
/* JS 未接管时的兜底：保留原来的静态选中底色 */
.mode-switch:not(.has-ind) .mode-btn.active{background:var(--grad);box-shadow:0 5px 16px rgba(168,85,247,.34)}
.side-list:not(.has-ind) button.active{background:var(--grad-soft);box-shadow:inset 0 0 0 1px rgba(236,72,153,.32)}

/* ===== 悬停高光流动 ===== */
.mode-btn,.side-list button,.immerse-bar .ghost-btn{
  position:relative;z-index:1;overflow:hidden;isolation:isolate;
}
.mode-btn::after,
.side-list button::after,
.immerse-bar .ghost-btn::after{
  content:'';position:absolute;top:-2px;bottom:-2px;left:0;width:38%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.17) 45%,transparent);
  transform:translate3d(-170%,0,0);
  opacity:0;pointer-events:none;z-index:2;
  transition:transform var(--dur-5) var(--ease-out),opacity var(--dur-2) linear;
}
.mode-btn:hover::after,
.side-list button:hover::after,
.immerse-bar .ghost-btn:hover::after{
  transform:translate3d(370%,0,0);
  opacity:1;
}

/* ===== 侧栏 ===== */
.sidebar{
  position:sticky;top:calc(var(--topbar-h) + var(--sp-4));
  border-radius:var(--r-lg);
  padding:var(--sp-4) var(--sp-3);
  display:flex;flex-direction:column;gap:var(--sp-5);
  max-height:calc(100vh - var(--topbar-h) - var(--sp-6));
  overflow-y:auto;
  min-width:0;   /* 沉浸模式下让侧栏跟随 0 宽轨道一起收拢（否则 min-content 会顶住） */
  transition:opacity var(--dur-3) var(--ease),
             transform var(--dur-4) var(--ease),
             padding var(--dur-4) var(--ease),
             border-width var(--dur-4) var(--ease),
             visibility 0s;
}
.mode-switch{
  display:flex;gap:var(--sp-1);padding:4px;
  background:rgba(0,0,0,.25);border-radius:var(--r-md);border:1px solid var(--line);
}
.mode-btn{
  flex:1;padding:10px var(--sp-2);border-radius:var(--r-sm);
  display:flex;flex-direction:column;align-items:center;gap:2px;
  color:var(--text-dim);font-size:var(--fs-md);font-weight:500;
  transition:color var(--dur-2) var(--ease);
}
.mode-btn em{font-style:normal;font-size:10px;color:var(--text-mute);font-weight:400}
.mode-btn:hover{color:var(--text)}
.mode-btn:active{transform:scale(.97)}
.mode-btn.active{color:#fff;background:transparent;box-shadow:none}
.mode-btn.active em{color:rgba(255,255,255,.82)}

.side-block h3{
  margin:0 0 var(--sp-2);padding:0 var(--sp-2);
  font-size:var(--fs-xs);font-weight:600;letter-spacing:1.4px;
  color:var(--text-mute);text-transform:uppercase;
}
.side-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.side-list button{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  padding:9px var(--sp-3);border-radius:var(--r-md);
  font-size:var(--fs-md);color:var(--text-dim);text-align:left;
  transition:color var(--dur-2) var(--ease);
}
.side-list button:hover{color:var(--text);background:var(--surface-2)}
.side-list button:active{background:var(--surface-3);transform:scale(.985)}
.side-list button.active{
  color:#fff;background:transparent;box-shadow:none;
}
.side-list .cnt{font-size:var(--fs-xs);color:var(--text-mute);font-variant-numeric:tabular-nums}
.side-list button.active .cnt{color:rgba(255,255,255,.7)}
/* 「更多」里的跳转条目是链接不是筛选器，但观感要和上面那两条保持一致 */
.side-list a{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  padding:9px var(--sp-3);border-radius:var(--r-md);
  font-size:var(--fs-md);color:var(--text-dim);text-align:left;
  transition:color var(--dur-2) var(--ease);
}
.side-list a:hover{color:var(--text);background:var(--surface-2)}
.side-list a:active{background:var(--surface-3);transform:scale(.985)}

.side-foot{
  margin-top:auto;padding-top:var(--sp-3);border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:var(--sp-1);
  font-size:var(--fs-xs);color:var(--text-mute);line-height:var(--lh);
}
.side-foot a{color:var(--text-dim);font-weight:600}
.side-foot a:hover{color:var(--pink)}

/* ===== 内容区 ===== */
.content{min-width:0}
/* 全站唯一的页头：英文大标题 + 真标题 + 状态描述 + 主操作。
   ⚠️ 底下那条渐变细线是并入进来的「栅格区模块头」的线 —— 那一整块
   （.mod-head：Gallery / 全部壁纸）已经删了，它和上面的「发现」信息重复，
   两个 42px 渐变大字上下叠着还把首屏高度吃掉一截。
   ⚠️ 这个元素不能删：#pageTitle / #pageDesc 由 app.js 在切分类·收藏
   时改写，#btnShuffle 是首页主操作；.content-head 本身还是沉浸模式收起动画
   的载体（body.immersive 下 max-height→0）和 motion.js 认的 hero 锚点。 */
.content-head{
  position:relative;
  display:flex;align-items:flex-end;justify-content:space-between;gap:var(--sp-3);
  padding-bottom:var(--sp-3);
  margin-bottom:var(--sp-4);
  max-height:240px;overflow:hidden;   /* 沉浸模式收起时用来裁切 */
  transition:max-height var(--dur-4) var(--ease),
             margin-bottom var(--dur-4) var(--ease),
             padding-bottom var(--dur-4) var(--ease),
             opacity var(--dur-3) var(--ease),
             transform var(--dur-3) var(--ease);
}
/* 分隔线：绝对定位在 padding 里，不参与上面的 flex 排布（否则会挤走换一批按钮） */
.content-head::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:1px;
  background:linear-gradient(90deg,rgba(255,255,255,.22),rgba(255,255,255,0));
}
.content-head h1{
  margin:0;font-size:var(--fs-2xl);font-weight:650;letter-spacing:.3px;
  line-height:var(--lh-tight);text-wrap:balance;overflow-wrap:break-word;
}
.content-head p{
  margin:var(--sp-1) 0 0;font-size:var(--fs-sm);color:var(--text-mute);
  line-height:var(--lh);text-wrap:pretty;
}
.ghost-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  min-height:40px;padding:0 var(--sp-4);
  border-radius:var(--r-md);
  border:1px solid var(--line-strong);
  color:var(--text-dim);font-size:var(--fs-md);
  transition:.22s var(--ease);
  flex-shrink:0;
}
.ghost-btn:hover{color:#fff;border-color:rgba(236,72,153,.5);background:var(--grad-soft)}
.ghost-btn:active{transform:scale(.97)}
.ghost-btn svg{width:15px;height:15px}

/* ===== 网格 =====
   瀑布流改用「CSS Grid + 细行 + 每张卡按自身比例跨行(--span)」，
   不再用 column-count。
   原因（实测）：column-count 在追加内容时会重新平衡所有列，
   一次无限滚动就把 30 张已渲染卡片里的 23 张挪了位，最大位移 4043px，
   CLS 0.19 —— 这正是「滚到底加载下一页时整个页面跳一下」的根源。
   Grid 的隐式行是彼此独立的，追加只影响新卡片所在的行，老卡片零位移。 */
.grid{display:block}
.grid.waterfall{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  column-gap:var(--sp-4);
  grid-auto-rows:4px;   /* 细行粒度，与 app.js 的 SPAN_ROW 常量必须一致 */
  row-gap:0;            /* 行距由 span 向上取整产生，避免出现双份间距 */
  align-items:start;    /* 卡片按自然高度贴顶，留出的余量即行间距 */
}
.grid.waterfall .card,
.grid.waterfall .skel{grid-row-end:span var(--span,80);margin-bottom:0}
.grid.grid-mode{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
  gap:var(--sp-4);
}
@media(max-width:1400px){
  .grid.waterfall{grid-template-columns:repeat(3,minmax(0,1fr));column-gap:var(--sp-3)}
}
@media(max-width:1000px){
  .grid.waterfall{grid-template-columns:repeat(2,minmax(0,1fr))}
  .grid.grid-mode{gap:var(--sp-3)}
}

.card{
  position:relative;
  border-radius:var(--r-lg);
  overflow:hidden;
  background:var(--bg-2);
  border:1px solid var(--line);
  box-shadow:var(--sh-1);
  cursor:zoom-in;
  /* 屏外卡片跳过渲染/布局：无限滚动长列表时大幅降低主线程与合成开销。
     auto 会在首次渲染后记住真实高度，配合 contain-intrinsic-size 兜底，避免滚动抖动 */
  content-visibility:auto;
  contain-intrinsic-size:auto 320px;
  /* 渐显走独立属性 translate + opacity + scale（带 --d 错峰延迟）；
     hover 抬起走 transform（无延迟）。两者互不干扰，
     否则 hover 也会被套上最多 420ms 的错峰延迟，手感发黏。
     用 scale（独立变换属性）而不是 transform:scale()，正是为了不和 hover 的 transform 打架。 */
  transition:opacity var(--dur-reveal) var(--ease-out) var(--d,0ms),
             translate var(--dur-reveal) var(--ease-out) var(--d,0ms),
             scale var(--dur-reveal) var(--ease-expo) var(--d,0ms),
             transform var(--dur-3) var(--ease),
             box-shadow var(--dur-3) var(--ease),
             border-color var(--dur-3) var(--ease);
}
/* 隐藏初态只在 JS 就绪时生效：无 JS / 老浏览器下卡片直接可见，不会整页空白 */
html.has-reveal .card{opacity:0;translate:0 22px;scale:.965}
html.has-reveal .card.in{opacity:1;translate:none;scale:1}

.grid.grid-mode .card{margin-bottom:0}
.grid.grid-mode .card .media{aspect-ratio:16/10}
.card:hover{
  transform:translateY(-4px);
  border-color:rgba(236,72,153,.45);
  box-shadow:var(--sh-2),0 0 0 1px rgba(236,72,153,.16);
}

/* 媒体容器：比例由 JS 注入的 --ar 决定（用自定义属性而非内联 aspect-ratio，
   这样网格模式才能用 class 正常覆盖） */
.media{
  position:relative;width:100%;display:block;
  aspect-ratio:var(--ar,3/2);
  background:#191527;overflow:hidden;
}
.media img,.media video{
  position:absolute;inset:0;
  width:100%;height:100%;object-fit:cover;display:block;
  opacity:0;transition:opacity .5s var(--ease),transform .5s var(--ease);
}
/* 网格模式：统一比例，媒体撑满卡片 */
.grid.grid-mode .card{aspect-ratio:16/10}
.grid.grid-mode .card .media{aspect-ratio:auto;height:100%}
/* 竖屏专区卡（.pcard，JS 注入 --ar:9/16）：占位估算调高（竖卡约为横卡两倍高，
   避免 content-visibility 兜底高度过小造成滚动抖动）；网格模式下也保持 9:16 */
.card.pcard{contain-intrinsic-size:auto 480px}
.grid.grid-mode .card.pcard{aspect-ratio:9/16}
.media img.loaded,.media video.loaded{opacity:1}
.card:hover .media img{transform:scale(1.045)}

.card .shade{
  position:absolute;inset:0;
  background:linear-gradient(to top,rgba(10,8,18,.88) 0%,rgba(10,8,18,.26) 42%,transparent 68%);
  opacity:0;transition:opacity .3s;pointer-events:none;
}
.card:hover .shade{opacity:1}

.card-info{
  position:absolute;left:0;right:0;bottom:0;
  padding:var(--sp-3);
  transform:translateY(8px);opacity:0;
  transition:.3s var(--ease);
  pointer-events:none;
}
.card:hover .card-info{transform:none;opacity:1}
.card-info .t{
  font-size:var(--fs-md);font-weight:600;color:#fff;line-height:var(--lh-tight);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.card-info .s{
  margin-top:var(--sp-1);font-size:var(--fs-xs);color:rgba(255,255,255,.72);
  display:flex;align-items:center;gap:6px;flex-wrap:wrap;line-height:1.4;
}
.tag{
  padding:2px 7px;border-radius:var(--r-xs);font-size:10px;
  background:rgba(10,8,18,.62);color:#fff;
  border:1px solid rgba(255,255,255,.14);white-space:nowrap;
}
.tag.res{background:rgba(0,0,0,.5)}

.card-fav{
  position:absolute;top:var(--sp-2);right:var(--sp-2);
  width:36px;height:36px;border-radius:var(--r-md);
  display:grid;place-items:center;
  background:rgba(12,10,20,.72);
  color:#fff;opacity:0;transform:scale(.85);
  transition:.26s var(--ease);
  border:1px solid rgba(255,255,255,.14);
  /* 不可见时别让透明按钮挡住点击，避免误触收藏 */
  pointer-events:none;
}
.card:hover .card-fav{opacity:1;transform:none;pointer-events:auto}
.card-fav:hover{background:rgba(236,72,153,.78);border-color:transparent}
.card-fav:active{transform:scale(.9)}
.card-fav.on{opacity:1;transform:none;background:rgba(236,72,153,.85);border-color:transparent;pointer-events:auto}
.card-fav svg{width:17px;height:17px}
.card-fav.on svg{fill:currentColor}

/* 收藏心跳：点击收藏时一次性弹出（460ms，单次，不循环） */
.beat{animation:beat 460ms var(--ease-soft)}
@keyframes beat{
  0%{transform:scale(1)}
  30%{transform:scale(.84)}
  62%{transform:scale(1.14)}
  100%{transform:scale(1)}
}

.card.dead{display:none}

/* 骨架屏：加载中的低对比流光（循环，但亮度极低，不干扰阅读） */
.skel{
  border-radius:var(--r-lg);
  aspect-ratio:var(--ar,3/2);
  background:linear-gradient(100deg,rgba(255,255,255,.045) 30%,rgba(255,255,255,.10) 50%,rgba(255,255,255,.045) 70%);
  background-size:220% 100%;animation:sweep 1.35s infinite linear;
}
.grid.grid-mode .skel{margin-bottom:0;aspect-ratio:16/10}
@keyframes sweep{from{background-position:180% 0}to{background-position:-20% 0}}

/* ===== 加载哨兵 / 页脚 ===== */
.sentinel{
  display:flex;align-items:center;justify-content:center;gap:var(--sp-3);
  padding:var(--sp-6) 0 var(--sp-2);color:var(--text-mute);font-size:var(--fs-sm);min-height:52px;
}
.spinner{
  width:20px;height:20px;border-radius:50%;
  border:2.5px solid rgba(255,255,255,.14);
  border-top-color:var(--pink);
  animation:spin .75s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}

.site-foot{
  margin-top:var(--sp-5);padding-top:var(--sp-4);
  border-top:1px solid var(--line);
  text-align:center;color:var(--text-mute);font-size:var(--fs-xs);
  display:flex;flex-direction:column;gap:var(--sp-1);line-height:var(--lh);
  max-height:200px;overflow:hidden;
  transition:max-height var(--dur-4) var(--ease),
             margin-top var(--dur-4) var(--ease),
             padding-top var(--dur-4) var(--ease),
             opacity var(--dur-3) var(--ease);
}
.site-foot a{color:var(--text-dim);font-weight:600}
.site-foot a:hover{color:var(--pink)}

/* ===== 预览 ===== */
.overlay{
  position:fixed;inset:0;z-index:100;
  background:rgba(8,6,14,.92);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  display:flex;align-items:center;justify-content:center;gap:var(--sp-5);
  padding:var(--sp-5);
  overscroll-behavior:contain;
  animation:fade .28s var(--ease);
}
@keyframes fade{from{opacity:0}to{opacity:1}}
.ov-stage{
  position:relative;flex:1;min-width:0;height:100%;
  display:grid;place-items:center;
}
.ov-stage img,.ov-stage video{
  max-width:100%;max-height:100%;
  border-radius:var(--r-lg);object-fit:contain;
  box-shadow:var(--sh-3);
  animation:zoomin .34s var(--ease);
}
@keyframes zoomin{from{opacity:0;transform:scale(.965)}to{opacity:1;transform:none}}

/* 预览双层：缩略图秒出（多半已命中网格缓存），原图解码完再淡入。
   翻页只改 src / class，不重建节点 —— 既不会白闪，也不会重复请求大图。
   注意 .ov-full 必须关掉 zoomin：那条动画结束后会回落到 opacity:0，
   否则原图会「淡入又淡出」闪一下。 */
.ov-stage .ov-thumb,
.ov-stage .ov-full{
  position:absolute;inset:0;margin:auto;
  max-width:100%;max-height:100%;
  transition:opacity var(--dur-3) var(--ease);
}
.ov-stage .ov-full{opacity:0;animation:none}
.ov-stage .ov-full.ready{opacity:1}
.ov-stage .ov-thumb.done{opacity:0}

.ov-panel{
  width:326px;flex-shrink:0;
  border-radius:var(--r-lg);padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-4);
  max-height:100%;overflow-y:auto;
}
/* 面板升起：每次「打开预览」播一次（翻页不重播，避免高频跳闪）520ms / 18px */
.ov-panel.anim{animation:panelin var(--dur-4) var(--ease-out) both}
@keyframes panelin{from{opacity:0;transform:translate3d(0,18px,0)}to{opacity:1;transform:none}}
.ov-meta h2{
  margin:0 0 var(--sp-2);font-size:var(--fs-xl);font-weight:650;
  line-height:var(--lh-tight);text-wrap:balance;overflow-wrap:break-word;
}
.ov-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:var(--sp-3)}
.ov-story{
  margin:0 0 var(--sp-4);font-size:var(--fs-sm);line-height:var(--lh-loose);color:var(--text-dim);
  max-height:132px;overflow-y:auto;white-space:pre-wrap;overflow-wrap:break-word;
}
.ov-specs{
  margin:0;display:grid;grid-template-columns:auto 1fr;gap:var(--sp-2) var(--sp-3);
  font-size:var(--fs-sm);padding-top:var(--sp-3);border-top:1px solid var(--line);
}
.ov-specs dt{color:var(--text-mute)}
.ov-specs dd{margin:0;color:var(--text-dim);text-align:right;word-break:break-all}
.ov-actions{display:flex;flex-direction:column;gap:var(--sp-2)}
.ov-row{display:contents}          /* 桌面端：子项直接纵向铺满 */
.ov-row-nav{display:none}          /* 桌面端用左右箭头，这里隐藏 */
.ov-actions .primary-btn,.ov-actions .ghost-btn{justify-content:center;min-height:44px}
.primary-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  min-height:44px;padding:0 var(--sp-5);border-radius:var(--r-md);
  background:var(--grad);color:#fff;font-size:var(--fs-md);font-weight:600;
  box-shadow:0 6px 20px rgba(168,85,247,.36);
  transition:.24s var(--ease);
}
.primary-btn:hover{transform:translateY(-1px);box-shadow:0 9px 26px rgba(168,85,247,.5)}
.primary-btn:active{transform:translateY(0) scale(.98)}

.ov-close{
  position:absolute;top:var(--sp-4);right:var(--sp-5);
  width:44px;height:44px;border-radius:var(--r-md);font-size:24px;line-height:1;
  display:grid;place-items:center;color:var(--text-dim);
  background:rgba(255,255,255,.07);border:1px solid var(--line);
  transition:.22s;
}
.ov-close:hover{color:#fff;background:rgba(255,255,255,.16)}
.ov-close:active{transform:scale(.93)}
.ov-nav{
  position:absolute;top:50%;transform:translateY(-50%);
  width:46px;height:66px;border-radius:var(--r-md);font-size:32px;line-height:1;
  display:grid;place-items:center;color:var(--text-dim);
  background:rgba(255,255,255,.06);border:1px solid var(--line);
  transition:.22s;
}
.ov-nav:hover{color:#fff;background:rgba(255,255,255,.16)}
.ov-nav:active{transform:translateY(-50%) scale(.94)}
.ov-nav.prev{left:var(--sp-3)}
.ov-nav.next{right:calc(326px + var(--sp-5) + var(--sp-2))}

/* ===== Toast ===== */
.toast{
  position:fixed;left:50%;bottom:var(--sp-6);transform:translateX(-50%);
  z-index:200;padding:var(--sp-3) var(--sp-5);border-radius:var(--r-md);
  background:rgba(20,16,32,.94);backdrop-filter:blur(14px);
  border:1px solid var(--line-strong);
  font-size:var(--fs-md);color:var(--text);
  box-shadow:var(--sh-2);
  animation:toastin .3s var(--ease);
  max-width:calc(100vw - 32px);text-align:center;
}
@keyframes toastin{from{opacity:0;transform:translate(-50%,12px)}to{opacity:1;transform:translate(-50%,0)}}

/* 空状态 */
.empty{
  grid-column:1/-1;column-span:all;
  padding:var(--sp-7) var(--sp-5);text-align:center;color:var(--text-mute);
  animation:fadein var(--dur-4) var(--ease-out);
}
@keyframes fadein{from{opacity:0;transform:translate3d(0,8px,0)}to{opacity:1;transform:none}}
.empty strong{
  display:block;font-size:var(--fs-lg);color:var(--text-dim);
  margin-bottom:var(--sp-2);font-weight:600;
}

/* ==================================================================
   沉浸模式（body.immersive）
   壁纸铺满视口，侧栏 / 标题 / 页脚等装饰 UI 淡出收起；
   顶栏自动隐藏（鼠标移动、触摸、聚焦时浮现），底部常驻一个极简操作条。
   ================================================================== */
body.immersive .shell{
  grid-template-columns:0px minmax(0,1fr);   /* 轨道动画：侧栏列平滑收到 0 */
  gap:0;
  max-width:2400px;                          /* 大屏尽量铺满，仍留边缘余量 */
  padding:calc(var(--sp-2) + env(safe-area-inset-top,0px))
          calc(var(--sp-3) + env(safe-area-inset-right,0px))
          calc(var(--sp-6) + env(safe-area-inset-bottom,0px))
          calc(var(--sp-3) + env(safe-area-inset-left,0px));
}
body.immersive .sidebar{
  opacity:0;visibility:hidden;pointer-events:none;
  transform:translateX(-14px);
  padding-inline:0;
  border-width:0;
  /* 退出时立即可见（delay 0），进入时等淡出完再隐藏 */
  transition:opacity var(--dur-3) var(--ease),
             transform var(--dur-4) var(--ease),
             padding var(--dur-4) var(--ease),
             border-width var(--dur-4) var(--ease),
             visibility 0s .34s;
}
body.immersive .content-head{
  /* padding 也要归零：border-box 下 max-height:0 压不掉 padding，
     留着会撑出一条 14px 的缝（跟 .site-foot 同一个坑） */
  max-height:0;margin-bottom:0;padding-bottom:0;
  opacity:0;transform:translateY(-10px);
  visibility:hidden;pointer-events:none;
}
body.immersive .site-foot{
  max-height:0;margin-top:0;padding-top:0;
  opacity:0;visibility:hidden;
}
body.immersive .sentinel{padding-top:var(--sp-5);min-height:40px}

/* 顶栏：脱离文档流，收成一条浮动玻璃胶囊，随鼠标/触摸自动显隐 */
body.immersive .topbar{
  position:fixed;
  top:calc(var(--sp-2) + env(safe-area-inset-top,0px));
  left:calc(var(--sp-3) + env(safe-area-inset-left,0px));
  right:calc(var(--sp-3) + env(safe-area-inset-right,0px));
  max-width:1400px;margin-inline:auto;
  min-height:56px;padding:0 var(--sp-4);
  border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh-2);
  transform:translateY(-150%);
  opacity:0;pointer-events:none;
}
body.immersive.ui-awake .topbar,
body.immersive .topbar:focus-within{
  transform:translateY(0);
  opacity:1;pointer-events:auto;
}

/* 底部常驻操作条：保证“退出沉浸”永远可点，入口清晰 */
.immerse-bar{
  position:fixed;left:50%;
  bottom:calc(var(--sp-4) + env(safe-area-inset-bottom,0px));
  z-index:60;
  display:flex;align-items:center;gap:var(--sp-2);
  padding:var(--sp-2);border-radius:var(--r-pill);
  box-shadow:var(--sh-2);
  transform:translate3d(-50%,calc(100% + 28px),0);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:transform var(--dur-4) var(--ease),
             opacity var(--dur-3) var(--ease),
             visibility 0s .44s;
}
body.immersive .immerse-bar{
  transform:translate3d(-50%,0,0);
  opacity:.62;visibility:visible;pointer-events:auto;
  transition:transform var(--dur-4) var(--ease),
             opacity var(--dur-3) var(--ease),
             visibility 0s;
}
body.immersive.ui-awake .immerse-bar,
body.immersive .immerse-bar:hover,
body.immersive .immerse-bar:focus-within{opacity:1}
.immerse-bar .ghost-btn{
  min-height:38px;padding:0 var(--sp-4);border-radius:var(--r-pill);
  background:rgba(255,255,255,.06);border-color:transparent;
}
.immerse-bar .ghost-btn#btnExitImmerse{
  color:#fff;background:var(--grad-soft);border-color:rgba(236,72,153,.4);
}

/* ==================================================================
   触屏 / 无 hover 设备：卡片信息与收藏按钮常驻（否则手机上永远看不到）
   ================================================================== */
@media (hover:none),(pointer:coarse){
  .card .shade{opacity:1}
  .card-info{opacity:1;transform:none}
  .card-fav{opacity:1;transform:none;width:40px;height:40px}
  .card:hover{transform:none}
  .card:hover .media img{transform:none}
  .card:active{transform:scale(.985);border-color:rgba(236,72,153,.5)}
}

/* ==================================================================
   平板 / 窄屏：侧栏转为三段式胶囊筛选
   ================================================================== */
@media(max-width:1100px){
  .shell{
    grid-template-columns:minmax(0,1fr);gap:var(--sp-3);
    padding:var(--sp-3) var(--sp-3) var(--sp-6);
  }
  .sidebar{
    min-width:0;
    position:static;
    max-height:1200px;   /* 给一个可插值的上限，沉浸模式下才能平滑收到 0 */
    overflow:visible;
    flex-direction:column;align-items:stretch;gap:var(--sp-3);
    padding:var(--sp-3);
    border-radius:var(--r-lg);
  }
  /* 单列布局：侧栏是一整行，用 max-height 收放（轨道动画对行不适用） */
  body.immersive .shell{grid-template-columns:minmax(0,1fr);gap:0}
  body.immersive .sidebar{
    max-height:0;padding-block:0;border-width:0;overflow:hidden;
    transform:translateY(-10px);
  }
  .side-foot{display:none}   /* 版权说明已由内容区页脚承担，避免重复 */
  .mode-switch{width:100%}
  .mode-btn{
    flex-direction:row;justify-content:center;align-items:center;gap:var(--sp-2);
    padding:var(--sp-2) var(--sp-2);min-height:44px;border-radius:var(--r-sm);
    font-size:var(--fs-md);
  }
  .mode-btn em{font-size:var(--fs-xs)}
  .side-block{display:block}
  .side-block h3{margin:0 0 var(--sp-2);padding:0 2px}
  .side-list{
    flex-direction:row;gap:var(--sp-2);
    overflow-x:auto;overflow-y:hidden;
    scrollbar-width:none;-ms-overflow-style:none;
    -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;
    overscroll-behavior-x:contain;   /* 胶囊滑到头不要把整页一起拽动（突兀感来源） */
    padding:2px;
  }
  .side-list::-webkit-scrollbar{display:none}
  .side-list li{flex:0 0 auto;scroll-snap-align:start}
  .side-list button{
    width:auto;justify-content:center;
    min-height:40px;padding:var(--sp-2) var(--sp-4);
    border-radius:var(--r-pill);
    background:var(--surface);
    border:1px solid var(--line);
    font-size:var(--fs-md);
  }
  /* 跳到独立页的那条也长成胶囊（它不是筛选项，所以不参与滑块定位） */
  .side-list a{
    width:auto;justify-content:center;
    min-height:40px;padding:var(--sp-2) var(--sp-4);
    border-radius:var(--r-pill);
    background:var(--surface);border:1px solid var(--line);
    font-size:var(--fs-md);
  }
  /* 滑块在胶囊形态下也跟着变圆，选中胶囊让出底色给滑块 */
  .side-list .ind{border-radius:var(--r-pill)}
  .side-list button.active{background:transparent;border-color:transparent}
  .side-list .cnt{display:none}

  /* 预览：图片在上、信息面板在下（底部面板式） */
  .overlay{
    flex-direction:column;align-items:stretch;
    gap:0;padding:0;justify-content:flex-end;
  }
  .ov-nav{display:none}                       /* 小屏隐藏悬浮箭头 */
  .ov-stage{flex:1 1 auto;height:auto;min-height:0;padding:60px var(--sp-3) var(--sp-2)}
  .ov-stage img,.ov-stage video{border-radius:var(--r-md);box-shadow:var(--sh-2)}
  .ov-panel{
    width:100%;max-height:48%;
    border-radius:var(--r-xl) var(--r-xl) 0 0;
    border-width:1px 0 0;
    padding:var(--sp-4) var(--sp-4) calc(var(--sp-4) + env(safe-area-inset-bottom,0px));
    gap:var(--sp-3);
  }
  .ov-meta h2{font-size:var(--fs-lg)}
  .ov-story{max-height:88px}
  .ov-actions{gap:var(--sp-2)}
  .ov-row{display:flex;gap:var(--sp-2)}
  .ov-row-nav{display:flex}
  .ov-row .primary-btn{flex:1 1 auto}
  .ov-row .ghost-btn{flex:0 0 auto;padding:0 var(--sp-4)}
  .ov-row-nav .ghost-btn{flex:1 1 0}
  .ov-close{top:var(--sp-3);right:var(--sp-3);background:rgba(12,10,20,.6)}
}

/* ==================================================================
   手机：360–430px 主战场
   ================================================================== */
@media(max-width:720px){
  .topbar{
    flex-wrap:wrap;gap:var(--sp-2) var(--sp-3);
    padding:calc(env(safe-area-inset-top,0px) + var(--sp-2)) var(--sp-4) var(--sp-2);
    min-height:0;
  }
  .brand{gap:var(--sp-2)}
  .brand-mark{width:32px;height:32px;border-radius:var(--r-sm);font-size:15px}
  .brand-text strong{font-size:var(--fs-md)}
  .brand-text span{display:none}                 /* 副标题在手机上收起，减少噪音 */
  .top-actions{margin-left:auto}
  .icon-btn{width:40px;height:40px}   /* 触控目标 ≥40px */
  .search-wrap{
    order:3;flex-basis:100%;max-width:none;margin-left:0;
  }
  #search{
    height:44px;font-size:16px;                  /* ≥16px 防止 iOS 聚焦时整页放大 */
    padding:0 40px 0 40px;
  }
  .search-clear{width:26px;height:26px;right:9px}

  .shell{padding:var(--sp-3) var(--sp-3) calc(var(--sp-6) + env(safe-area-inset-bottom,0px))}
  .content-head{
    align-items:flex-start;flex-direction:column;gap:var(--sp-2);
    margin-bottom:var(--sp-3);
  }
  .content-head h1{font-size:var(--fs-xl)}
  .content-head p{font-size:var(--fs-xs)}
  #btnShuffle{align-self:flex-start;min-height:38px;padding:0 var(--sp-3)}

  /* 双列网格：间距收紧到 10px，视觉更连贯 */
  .grid.waterfall{column-count:2;column-gap:var(--sp-2)}
  .card{margin-bottom:var(--sp-2);border-radius:var(--r-md)}
  .grid.grid-mode{grid-template-columns:repeat(2,1fr);gap:var(--sp-2)}
  /* 网格模式：手机上换成 3:4，更贴合壁纸浏览 */
  .grid.grid-mode .card{aspect-ratio:3/4}
  .card-info{padding:var(--sp-2)}
  .card-info .t{font-size:var(--fs-sm)}
  .card-info .s{gap:4px}
  .card-fav{top:6px;right:6px;width:40px;height:40px;border-radius:var(--r-sm)}

  .sentinel{padding:var(--sp-5) 0 var(--sp-2);font-size:var(--fs-xs);min-height:44px}
  .toast{bottom:calc(var(--sp-5) + env(safe-area-inset-bottom,0px));font-size:var(--fs-sm)}

  /* 光斑缩小并降透明度：手机 GPU 更省，也更透气。
     ⚠️ 不要在这里重新加 filter:blur —— 基础层已改为零滤镜渐变柔光，
     移动端再叠模糊会让顶栏/侧栏的合成每帧重采，滚动直接掉帧（卡顿主因之一）。 */
  .orb{opacity:.34}
  .orb-1{width:320px;height:320px;top:-130px;left:-110px}
  .orb-2{width:300px;height:300px;bottom:-140px;right:-90px}
  .orb-3{display:none}
  /* 毛玻璃手机档：只保留模糊，不加 saturate（饱和度重采样在大面积面板上很贵） */
  .glass{backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}

  /* 侧栏（胶囊筛选区）移动端专属：去掉 backdrop-filter。
     手机上它是一整块大面积面板，实时模糊背后滚动的壁纸 = 持续全帧重采样，
     又费电又会让半透明层出现色带；换成高不透明度实底 + 细边 + 投影，
     视觉更「稳」不透底突兀，滚动时零滤镜开销。 */
  .sidebar.glass{
    backdrop-filter:none;-webkit-backdrop-filter:none;
    background:linear-gradient(180deg,#181428,#110e1d);
    border-color:rgba(255,255,255,.07);
    box-shadow:0 12px 32px rgba(0,0,0,.28);
  }

  /* 沉浸模式（手机）：顶栏压成一行小胶囊，收起搜索与副标题 */
  body.immersive .topbar{
    flex-wrap:nowrap;gap:var(--sp-2);
    padding:0 var(--sp-3);min-height:52px;
  }
  body.immersive .brand-text{display:none}
  body.immersive .search-wrap{display:none}
  body.immersive .shell{
    padding:calc(var(--sp-2) + env(safe-area-inset-top,0px))
            calc(var(--sp-2) + env(safe-area-inset-right,0px))
            calc(var(--sp-6) + env(safe-area-inset-bottom,0px))
            calc(var(--sp-2) + env(safe-area-inset-left,0px));
  }
  /* 沉浸模式（手机）：底部操作条贴安全区，横屏也不压内容 */
  .immerse-bar{bottom:calc(var(--sp-3) + env(safe-area-inset-bottom,0px));padding:6px}
  .immerse-bar .ghost-btn{min-height:40px;padding:0 var(--sp-3);font-size:var(--fs-sm)}
}

/* 超窄屏（<360px）回到单列，避免卡片被挤压 */
@media(max-width:359px){
  .grid.waterfall{column-count:1}
  .grid.grid-mode{grid-template-columns:1fr}
  .grid.grid-mode .card{aspect-ratio:16/10}
}

/* ══════════════════════════════════════════════════════════════════
   滚动流动画廊（flowscroll.js）
   复刻 obsidianui「Flow Scroll」：三列壁纸随滚动不同速流动，电影感。
   用在哪：整块搬到了独立页 flow.html（用 data-percol 把每列从 5 张放大到 10 张，
   撑成一面墙）；首页 index.html 已经不再有它。默认高度 300px 留给「嵌进列表页
   当横幅」的用法，独立页在自己的样式里覆盖成 min(72vh,640px)。
   整块可摘：删掉 flow.html + assets/flowscroll.js + assets/flowapp.js 即可。
   ── 追加动效清单 ─────────────────────────────────────────────────
   ⑳ 滚动流动   .fs-col   滚动进入视口  随滚动实时（rAF，仅 3 个 transform）
                阻尼跟随 lerp=0.26（约 0.4s 滑到位，前 90ms 走完约八成）；收敛即停不空转。
                注意门控只决定「要不要开始跟手」，**不**决定「要不要滑到终点」——
                把「区块是否可见」写进可跑条件会让阻尼链在区块滚出视口时被掐断，
                三列停在行程中途（表现为「滚了但不跟手」，且矮列会露底）。
   ══════════════════════════════════════════════════════════════════ */
.flowscroll{
  position:relative;
  height:300px;
  margin:0 0 var(--sp-4);
  border-radius:var(--r-xl);
  border:1px solid var(--line);
  background:
    radial-gradient(120% 150% at 12% -12%, rgba(168,85,247,.16), transparent 55%),
    radial-gradient(120% 150% at 88% 112%, rgba(236,72,153,.14), transparent 55%),
    var(--bg-2);
  overflow:hidden;
  cursor:pointer;
  transition:height var(--dur-4) var(--ease),
             margin-bottom var(--dur-4) var(--ease),
             opacity var(--dur-3) var(--ease);
}
/* 顶部/底部渐变遮罩：让图片融进背景，别在容器边缘硬生生截断 */
.flowscroll::before,.flowscroll::after{
  content:'';position:absolute;left:0;right:0;z-index:2;pointer-events:none;
}
.flowscroll::before{top:0;height:72px;background:linear-gradient(180deg,var(--bg-2),rgba(21,18,37,0))}
.flowscroll::after{bottom:0;height:72px;background:linear-gradient(0deg,var(--bg-2),rgba(21,18,37,0))}

.fs-head{
  position:absolute;top:var(--sp-3);left:var(--sp-4);z-index:3;
  display:flex;align-items:center;gap:var(--sp-2);pointer-events:none;
}
.fs-tag{
  font-size:var(--fs-xs);font-weight:650;letter-spacing:.5px;color:#fff;
  padding:4px 10px;border-radius:var(--r-pill);background:var(--grad);
  box-shadow:0 4px 14px rgba(168,85,247,.42);
}
.fs-hint{font-size:var(--fs-xs);color:var(--text-mute)}

.fs-track{
  position:absolute;inset:0;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-3);
  padding:var(--sp-5);
}
/* 列内图片高度用 --h 逐列错开：三列溢出量不同 → 滚动时行程不同 → 更像水流。
   高度写在各列上而不是 .fs-item 上，窄屏只需覆盖一条 --h，不用跟特异性打架。
   position:relative 是给 JS 量内容高用的（子项 offsetTop 相对本列），本身不改变外观。 */
.fs-col{--h:150px;position:relative;display:flex;flex-direction:column;gap:var(--sp-3);will-change:transform}
.fs-col:nth-child(2){--h:176px}
.fs-col:nth-child(3){--h:130px}
.fs-item{
  margin:0;height:var(--h);flex:0 0 auto;
  border-radius:var(--r-md);overflow:hidden;
  border:1px solid var(--line);box-shadow:var(--sh-1);
}
.fs-item img{
  display:block;width:100%;height:100%;object-fit:cover;
  transition:transform var(--dur-3) var(--ease);
}
.fs-item:hover img{transform:scale(1.06)}

/* 沉浸模式让位 */
body.immersive .flowscroll{height:0;margin-bottom:0;opacity:0;border-width:0}

/* 移动端：三列太挤，降为两列；图片更矮更密 */
@media(max-width:720px){
  .flowscroll{height:240px}
  .fs-track{grid-template-columns:repeat(2,minmax(0,1fr));padding:var(--sp-4)}
  .fs-col{--h:104px}
  .fs-col:nth-child(2){--h:126px}
  .fs-col:last-child{display:none}
  .fs-head{top:var(--sp-2);left:var(--sp-3)}
  .fs-hint{display:none}
}

/* 动效降级：滚动流动关闭，图片静止 */
@media(prefers-reduced-motion:reduce){
  .fs-col{transform:none!important}
  .fs-item img{transition:none}
}

/* ══════════════════════════════════════════════════════════════════
   心灵频道（soul.js）
   一个 INFJ 做的角落，写给 ENFP 的你。整块可摘：
   删掉 index.html 里那段结构 + assets/soul.js，主站一切如常。
   ── 追加动效清单 ─────────────────────────────────────────────────
   ⑬ 光球呼吸      .orb-halo       常驻              5.2s 循环
   ⑭ 光球炸开      .soul-orb       按「惊喜」        900ms 单次
   ⑮ 纸条飘出      .soul-tease     当天首次进入      520ms 单次
   ⑯ 面板升起      .soul-panel     打开              520ms 单次（复用 panelin）
   ⑰ 心情卡点亮    .mood           悬停 / 选中       240ms
   ⑱ 悄悄话浮现    .soul-whisper   选中心情          520ms 单次
   ⑲ 匹配度条生长  .mbti-meter i   出结果            620ms 单次
   ── 无障碍：⑬⑭⑮⑯⑱⑲ 在 prefers-reduced-motion 下一律瞬时化，
      纸条与面板不会再「飘」。
   ══════════════════════════════════════════════════════════════════ */

/* —— 悬浮光球 ——
   用 grid 定心；光环是绝对定位的独立层，不参与布局，所以呼吸放大
   不会把按钮撑大（撑大就会带动点击热区抖动）。 */
.soul-orb{
  position:fixed;z-index:60;
  right:var(--sp-5);bottom:calc(var(--sp-5) + env(safe-area-inset-bottom,0px));
  width:54px;height:54px;border-radius:50%;
  display:grid;place-items:center;color:#fff;
  background:var(--grad);
  box-shadow:0 10px 30px rgba(168,85,247,.44);
  transition:transform var(--dur-2) var(--ease),
             box-shadow var(--dur-3) var(--ease),
             opacity var(--dur-3) var(--ease);
}
.soul-orb svg{position:relative;z-index:1;width:23px;height:23px;fill:currentColor;stroke:none}
.soul-orb:hover{transform:translateY(-2px) scale(1.04);box-shadow:0 14px 34px rgba(236,72,153,.5)}
.soul-orb:active{transform:scale(.94)}
.orb-halo{
  position:absolute;inset:0;border-radius:50%;pointer-events:none;
  border:1px solid rgba(255,255,255,.48);
  animation:halo 5.2s var(--ease-soft) infinite;
}
@keyframes halo{
  0%{transform:scale(1);opacity:.5}
  70%,100%{transform:scale(1.62);opacity:0}
}
.orb-dot{
  position:absolute;top:1px;right:1px;width:12px;height:12px;border-radius:50%;
  background:#fff;box-shadow:0 0 0 3px var(--pink);
}
/* 「惊喜」按下时先炸一圈光，再揭晓 —— 这点延迟本身就是乐趣的一部分 */
.soul-orb.is-sparking{animation:spark-burst var(--dur-5) var(--ease-out) both}
@keyframes spark-burst{
  0%{box-shadow:0 10px 30px rgba(168,85,247,.44),0 0 0 0 rgba(236,72,153,.62)}
  55%{box-shadow:0 10px 30px rgba(168,85,247,.44),0 0 0 20px rgba(236,72,153,0)}
  100%{box-shadow:0 10px 30px rgba(168,85,247,.44),0 0 0 0 rgba(236,72,153,0)}
}

/* —— 今日一签的纸条 ——
   不弹窗、不遮屏，只是从右下角飘出来。右边缘与光球对齐，看着像从它那里递出来的。 */
.soul-tease{
  position:fixed;z-index:59;
  right:var(--sp-5);bottom:calc(var(--sp-5) + env(safe-area-inset-bottom,0px) + 54px + var(--sp-3));
  width:274px;text-align:left;
  display:flex;flex-direction:column;gap:6px;
  padding:var(--sp-4);border-radius:var(--r-lg);
  background-color:rgba(17,13,28,.92);   /* 同面板：悬在壁纸上，必须自己吃得住对比度 */
  box-shadow:var(--sh-2);
  transition:background .24s var(--ease),border-color .24s var(--ease);
}
.soul-tease.anim{animation:tease-in var(--dur-4) var(--ease-out) both}
@keyframes tease-in{
  from{opacity:0;transform:translate3d(8px,16px,0) scale(.97)}
  to{opacity:1;transform:none}
}
.soul-tease:hover{background:var(--surface-2);border-color:rgba(236,72,153,.45)}
.soul-tease:active{transform:scale(.985)}
.tease-tag{font-size:var(--fs-xs);font-weight:650;color:var(--pink);letter-spacing:.4px}
.tease-text{display:block;font-size:var(--fs-md);line-height:var(--lh-loose);color:var(--text-dim)}
.tease-go{font-size:var(--fs-xs);color:var(--text-mute)}

/* —— 面板 —— */
.soul-panel{
  position:fixed;z-index:70;
  right:var(--sp-5);bottom:calc(var(--sp-5) + env(safe-area-inset-bottom,0px) + 54px + var(--sp-3));
  width:352px;max-height:min(72vh,648px);
  overflow-y:auto;overscroll-behavior:contain;
  border-radius:var(--r-xl);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-4);
  /* 侧栏那种 .glass 只铺在深色页面上，够用；这块面板是悬在壁纸上的，
     亮图会直接穿透把字冲灰。所以底色单独加实，再靠 blur 保留玻璃感。
     alpha 值有断言兜底（≥0.75），免得以后被「调透一点」改回去。 */
  background-color:rgba(17,13,28,.88);
  box-shadow:var(--sh-3),inset 0 1px 0 rgba(255,255,255,.06);
}
.soul-panel.anim{animation:panelin var(--dur-4) var(--ease-out) both}
/* 面板自身可滚动，但里面每一块都不许被压扁 ——
   flex 列容器 + overflow-y:auto 时，子项仍是 flex-shrink:1，
   内容超高就会一起收缩到 min-content：今日签卡片会直接塌成一条线。
   这个坑是在截图上看到的（自动化断言只查了行为，没量高度），所以这里显式钉死。 */
.soul-panel > *{flex-shrink:0}
.soul-sign > *{flex-shrink:0}
/* 玻璃滚动条：与侧栏同一套观感（滚动中淡入、悬停转粉） */
.soul-panel::-webkit-scrollbar{width:10px;height:10px}
.soul-panel::-webkit-scrollbar-track{background:transparent}
.soul-panel::-webkit-scrollbar-thumb{
  background-color:rgba(255,255,255,.09);
  background-clip:content-box;
  border:3px solid transparent;
  border-radius:var(--r-pill);
  transition:background-color var(--dur-3) var(--ease);
}
.soul-panel.is-scrolling::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,.30)}
.soul-panel::-webkit-scrollbar-thumb:hover{background-color:rgba(236,72,153,.62)}

.soul-head{display:flex;align-items:center;gap:var(--sp-3);flex-shrink:0}
.soul-ava{
  width:38px;height:38px;flex-shrink:0;border-radius:var(--r-md);
  display:grid;place-items:center;
  background:var(--grad);color:#fff;font-weight:700;font-size:16px;
  box-shadow:0 6px 18px rgba(168,85,247,.4);
}
.soul-id{flex:1;min-width:0;line-height:var(--lh-tight)}
.soul-id h2{margin:0;font-size:var(--fs-lg);font-weight:650}
.soul-id p{margin:3px 0 0;font-size:var(--fs-xs);color:var(--text-mute)}
.soul-x{
  width:32px;height:32px;flex-shrink:0;border-radius:var(--r-sm);
  display:grid;place-items:center;font-size:20px;line-height:1;color:var(--text-mute);
  transition:.2s var(--ease);
}
.soul-x:hover{color:#fff;background:var(--surface-2)}

.soul-sec{display:flex;flex-direction:column;gap:var(--sp-3)}
.soul-sec h3{
  margin:0;display:flex;align-items:center;gap:var(--sp-2);
  font-size:var(--fs-sm);font-weight:600;color:var(--text-dim);letter-spacing:.2px;
}
.soul-sec h3::before{
  content:'';width:3px;height:12px;border-radius:var(--r-pill);background:var(--grad);
}

/* —— 今日一签：图 + 字 + 两个按钮 —— */
.soul-sign{
  position:relative;border-radius:var(--r-lg);overflow:hidden;
  border:1px solid var(--line-strong);
  background:linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.02));
  display:flex;flex-direction:column;
}
.soul-sign-img{
  position:relative;display:block;width:100%;
  /* 按图片原始比例自适应，显示完整画面；极端比例用 max-height 兜底 */
  aspect-ratio:var(--ar, 16 / 9);
  max-height:min(52vh, 460px);
  background-color:var(--surface-3);
  background-size:contain;
  background-repeat:no-repeat;
  background-position:center;
  border-bottom:1px solid var(--line);
  transition:filter var(--dur-3) var(--ease);
}
/* 图底部压一条渐变，让签文区与图片接得更柔和，也把「这是张签」的仪式感立起来 */
.soul-sign-img::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:52px;
  background:linear-gradient(to top, rgba(15,12,26,.78), rgba(15,12,26,0));
  pointer-events:none;
}
.soul-sign-img:hover{filter:brightness(1.1) saturate(1.06)}
.soul-sign-img.is-empty{background-image:var(--grad-soft)}
.soul-sign-body{padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)}
.soul-sign-tag{font-size:var(--fs-xs);font-weight:650;color:var(--pink);letter-spacing:.5px}
.soul-sign-tag em{font-style:normal;font-weight:500;color:var(--text-mute)}
.soul-sign-body p{
  margin:0;font-size:var(--fs-lg);font-weight:500;line-height:var(--lh-loose);color:var(--text);
  overflow-wrap:break-word;text-wrap:balance;
}
.soul-sign-act{display:flex;gap:var(--sp-2)}

.soul-mini{
  min-height:34px;padding:0 var(--sp-4);border-radius:var(--r-sm);
  font-size:var(--fs-sm);font-weight:600;color:#fff;background:var(--grad);
  transition:.22s var(--ease);
}
.soul-mini:hover{transform:translateY(-1px)}
.soul-mini:active{transform:scale(.96)}
.soul-mini.is-ghost{background:var(--surface-2);border:1px solid var(--line);color:var(--text-dim)}
.soul-mini.is-ghost:hover{color:#fff;border-color:rgba(236,72,153,.5)}
.soul-mini[disabled]{opacity:.45;cursor:default}
.soul-mini[disabled]:hover{transform:none}

/* —— 心情电台 ——
   色雾用 inset box-shadow 做，避免依赖 color-mix；图标的软色圆盘则用 color-mix，
   前面留一行中性底做回退，老引擎照样有圆盘、只是没颜色。 */
.mood-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-2)}
.mood{
  display:flex;flex-direction:column;align-items:center;gap:8px;
  padding:var(--sp-4) 4px var(--sp-3);border-radius:var(--r-md);
  background:var(--surface);border:1px solid var(--line);color:var(--text-dim);
  transition:color var(--dur-2) var(--ease),
             border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             transform var(--dur-1) var(--ease);
}
.mood-ico{
  width:36px;height:36px;display:grid;place-items:center;color:var(--mh);
  border-radius:50%;
  background:var(--surface-2);
  background:color-mix(in srgb, var(--mh) 16%, transparent);
  transition:transform var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.mood-ico svg{width:19px;height:19px;stroke-width:1.6}
.mood-name{font-size:var(--fs-sm);font-weight:600}
.mood:hover{color:#fff;border-color:var(--line-strong);
  box-shadow:inset 0 -26px 32px -24px var(--mh),0 4px 14px rgba(0,0,0,.22)}
.mood:hover .mood-ico{transform:translateY(-1px) scale(1.05);
  background:color-mix(in srgb, var(--mh) 24%, transparent)}
.mood.is-on{color:#fff;border-color:var(--mh);
  box-shadow:inset 0 -42px 40px -26px var(--mh),0 0 0 1px var(--mh),0 6px 18px rgba(0,0,0,.28)}
.mood.is-on .mood-ico{background:color-mix(in srgb, var(--mh) 28%, transparent)}
.mood:active{transform:scale(.97)}

.soul-whisper{
  margin:0;padding:var(--sp-3) var(--sp-4);border-radius:var(--r-md);
  background:var(--grad-soft);border:1px solid rgba(236,72,153,.3);
  font-size:var(--fs-sm);line-height:var(--lh-loose);color:var(--text);
}
.soul-whisper.anim{animation:whisper-in var(--dur-4) var(--ease-out) both}
@keyframes whisper-in{from{opacity:0;transform:translate3d(0,6px,0)}to{opacity:1;transform:none}}

/* —— 惊喜 —— */
.soul-spark{
  display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:var(--sp-4);border-radius:var(--r-lg);
  background:var(--grad);color:#fff;font-size:var(--fs-base);font-weight:650;
  box-shadow:0 8px 24px rgba(168,85,247,.36),inset 0 1px 0 rgba(255,255,255,.28);
  transition:.24s var(--ease);
}
.soul-spark svg{width:18px;height:18px;fill:currentColor;stroke:none;margin-bottom:2px}
.soul-spark em{font-style:normal;font-size:var(--fs-xs);font-weight:500;opacity:.82}
.soul-spark:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(236,72,153,.46)}
.soul-spark:active{transform:translateY(0) scale(.985)}

/* —— 灵魂匹配度 —— */
.mbti-out{font-size:var(--fs-sm)}
.quiz{display:flex;flex-direction:column;gap:var(--sp-3)}
.quiz-bar{height:3px;border-radius:var(--r-pill);background:var(--surface-3);overflow:hidden}
.quiz-bar i{display:block;height:100%;border-radius:inherit;background:var(--grad);transition:width var(--dur-3) var(--ease)}
.quiz-q{margin:0;font-size:var(--fs-base);line-height:var(--lh);color:var(--text)}
.quiz-opt{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2)}
.quiz-opt button{
  display:flex;flex-direction:column;gap:5px;align-items:flex-start;text-align:left;
  padding:var(--sp-3);border-radius:var(--r-md);
  background:var(--surface);border:1px solid var(--line);
  color:var(--text);font-size:var(--fs-md);font-weight:600;
  transition:.22s var(--ease);
}
.quiz-opt button em{font-style:normal;font-size:var(--fs-xs);font-weight:400;color:var(--text-mute);line-height:var(--lh-tight)}
.quiz-opt button:hover{border-color:rgba(236,72,153,.5);background:var(--grad-soft);transform:translateY(-1px)}
.quiz-opt button:active{transform:scale(.97)}
.quiz-step{margin:0;font-size:var(--fs-xs);color:var(--text-mute);text-align:center}

.mbti-res{display:flex;flex-direction:column;gap:var(--sp-3)}
.mbti-hit{display:flex;align-items:baseline;gap:var(--sp-2)}
.mbti-you{
  font-size:var(--fs-2xl);font-weight:700;letter-spacing:1.2px;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.mbti-vs{font-size:var(--fs-xs);color:var(--text-mute)}
.mbti-score{margin-left:auto;font-size:var(--fs-xl);font-weight:700;color:#fff}
.mbti-score em{font-style:normal;font-size:var(--fs-xs);font-weight:500;color:var(--text-mute);margin-left:1px}
.mbti-meter{height:6px;border-radius:var(--r-pill);background:var(--surface-3);overflow:hidden}
.mbti-meter i{
  display:block;height:100%;border-radius:inherit;background:var(--grad);
  transform-origin:left center;animation:meter var(--dur-4) var(--ease-out) both;
}
@keyframes meter{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.mbti-band{margin:0;font-size:var(--fs-md);color:var(--text)}
.mbti-line{
  margin:0;padding:var(--sp-3) var(--sp-4);border-radius:var(--r-md);
  background:var(--surface);border-left:2px solid var(--pink);
  font-size:var(--fs-sm);line-height:var(--lh-loose);color:var(--text-dim);
}
.mbti-act{display:flex;align-items:center;gap:var(--sp-3)}
.mbti-hint{font-size:var(--fs-xs);color:var(--text-mute)}

/* —— 心情轨迹 —— */
.soul-trace{
  padding-top:var(--sp-3);border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:var(--sp-2);
}
.trace-dots{display:flex;gap:6px;align-items:center}
.trace-dot{
  flex:1 1 0;max-width:26px;height:8px;border-radius:var(--r-pill);
  background:var(--mh);box-shadow:0 0 10px -2px var(--mh);
}
.trace-text,.trace-empty{margin:0;font-size:var(--fs-xs);line-height:var(--lh);color:var(--text-mute)}

/* 沉浸模式：这里的一切都让位给壁纸本身 */
body.immersive .soul-orb,
body.immersive .soul-tease,
body.immersive .soul-panel{opacity:0;pointer-events:none}

/* 手机：光球缩小、面板改成贴底抽屉（飘在半空会压住卡片） */
@media(max-width:720px){
  .soul-orb{
    right:var(--sp-4);
    bottom:calc(var(--sp-4) + env(safe-area-inset-bottom,0px));
    width:50px;height:50px;
  }
  .soul-orb svg{width:21px;height:21px}
  .soul-tease{
    left:var(--sp-4);right:var(--sp-4);width:auto;
    bottom:calc(var(--sp-4) + env(safe-area-inset-bottom,0px) + 50px + var(--sp-2));
  }
  .soul-panel{
    left:0;right:0;width:auto;bottom:0;
    max-height:82vh;
    border-radius:var(--r-xl) var(--r-xl) 0 0;
    border-width:1px 0 0;
    padding:var(--sp-4) var(--sp-4) calc(var(--sp-5) + env(safe-area-inset-bottom,0px));
  }
  /* 抽屉展开时把光球收掉，别压在内容上 */
  .soul-orb.is-open{opacity:0;pointer-events:none}
  .soul-sign-img{max-height:min(46vh, 340px)}
}

/* —— 动效降级：所有动效瞬时化，避免无过渡时闪进闪出或内容卡在透明态 —— */
@media(prefers-reduced-motion:reduce){
  .orb-halo{animation:none!important;opacity:0!important}
  .soul-orb.is-sparking{animation:none!important}
  .soul-tease,
  .soul-panel,
  .soul-whisper,
  .mbti-meter i{animation:none!important;opacity:1!important;transform:none!important}
  .soul-orb:hover,.soul-spark:hover,.soul-mini:hover,
  .mood:hover,.quiz-opt button:hover{transform:none}
  .soul-tease:active,.mood:active{transform:none}
}

/* ==================================================================
   动效降级：prefers-reduced-motion
   位移 / 淡入淡出全部瞬时化，光斑静止，高光扫过直接移除
   ================================================================== */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;transition-delay:0s!important;
  }
  .orb{animation:none!important;will-change:auto}
  .mode-btn::after,
  .side-list button::after,
  .immerse-bar .ghost-btn::after{display:none}
  /* 渐显直接给终态：不做位移与淡入，也不会停在 opacity:0 */
  html.has-reveal .card{opacity:1!important;translate:none!important;scale:1!important}
  /* 顶栏不再自动显隐，避免无过渡时闪进闪出 */
  body.immersive .topbar{transform:none!important;opacity:1!important;pointer-events:auto!important}
  body.immersive .immerse-bar{opacity:1!important}
}

/* ══════════════════════════════════════════════════════════════════
   高端作品集式动效（assets/motion.js）
   要的不是「普通淡入」，而是设计师作品集 / 创意机构官网那种进场：
   遮罩揭开（clip-path）+ 大幅位移 + 压缩后归位（scale），节奏偏慢、
   缓动丝滑、全程无回弹（一律用 expo 系曲线，不用任何会过冲的 spring）。
   整块可摘：删掉 index.html 里 .intro 那段 + assets/motion.js 即可。
   ── 追加动效清单 ─────────────────────────────────────────────────
   ㉓ 首屏揭幕   .intro             页面加载（仅顶层帧）  约 1900ms 单次
   ㉔ 模块大标题 .mod-en            滚动进入视口          900ms 单次
   ㉕ 图片揭显   .card .media img   进入视口且已就绪      760ms 单次
   ══════════════════════════════════════════════════════════════════ */

/* —— ㉓ 首屏揭幕 ——
   幕布不是「盖住内容的黑块」，而是与页面同底色的一层帘子：
   底色一致，观众看到的是版面被「拉出来」，而不是先闪一下黑屏。
   默认 display:none，只有 html.intro-on 时才渲染 —— 无 JS / 降级 / 被 iframe 嵌入时，
   这个节点根本不会出现，也就谈不上影响。 */
.intro{display:none}
html.intro-on{overflow:hidden}          /* 揭幕期间不跟着滚动；intro-on 一摘就恢复 */
html.intro-on .intro{
  display:grid;place-items:center;
  position:fixed;inset:0;z-index:9999;
  background:var(--bg);overflow:hidden;
}
.intro-core{position:relative;text-align:center;padding:0 var(--sp-5)}

.intro-mark{
  display:flex;justify-content:center;margin:0;
  font-size:clamp(30px,5vw,54px);font-weight:800;
  letter-spacing:.06em;line-height:1.06;color:var(--text);
}
/* 逐字遮罩升起：每个字自带一层从下往上的帘子，靠 clip-path 揭开；
   -16% 的上下留白是给字形溢出部分（笔画、下延）留余量，不然会被切掉。
   这里的 108% 只受定时器驱动、不归 IO 管，所以能用「矩形翻负」的写法；
   若哪天改成用 IntersectionObserver 揭，必须收到 100%（见 .mod-en 那条注释）。 */
.intro-mark i{
  font-style:normal;display:block;
  clip-path:inset(-12% -12% 108% -12%);
  translate:0 46%;
  transition:clip-path var(--dur-6) var(--ease-expo) calc(140ms + var(--i,0) * 90ms),
             translate var(--dur-6) var(--ease-expo) calc(140ms + var(--i,0) * 90ms);
}
html.intro-on .intro.is-rise .intro-mark i{clip-path:inset(-16% -12% -16% -12%);translate:0 0}

.intro-rule{
  display:block;width:132px;height:1px;margin:18px auto 0;
  background:var(--grad);
  scale:0 1;                            /* 用 scaleX 展开而不是动 width：不触发布局 */
  transition:scale 900ms var(--ease-expo) 520ms;
}
html.intro-on .intro.is-rise .intro-rule{scale:1 1}

.intro-sub{
  display:inline-block;margin-top:16px;
  font-size:11px;letter-spacing:.42em;text-transform:uppercase;color:var(--text-mute);
  opacity:0;translate:0 12px;
  transition:opacity 680ms var(--ease-out) 560ms,
             translate 900ms var(--ease-expo) 560ms;
}
html.intro-on .intro.is-rise .intro-sub{opacity:1;translate:0 0}

/* 抽幕：整块帘子向上走掉，字标反向轻移淡出 —— 版面是被「拉出来」的 */
html.intro-on .intro.is-out{translate:0 -101%;transition:translate 1000ms var(--ease-expo)}
html.intro-on .intro.is-out .intro-core{
  opacity:0;translate:0 -46px;
  transition:opacity 420ms var(--ease),translate 760ms var(--ease-expo);
}

/* —— ㉔ 模块大标题 ——
   每个模块的「开场白」：先大幅进场，卡片再依次跟上。
   三种落位（首页页头 .ch-text 内真实占位一行 / flow 页 hero / flow 画廊里压在
   图上的绝对定位水印）。首页原本有两处「真实占位」的大字（Discover + Gallery），
   后者已并入页头删掉 —— 现在全站每页只剩一处「真标题」，其余都是水印。 */
.mod-en{
  margin:0;
  font-family:"SF Pro Display","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;
  font-weight:800;text-transform:uppercase;letter-spacing:.01em;line-height:.88;
  color:rgba(255,255,255,.055);
  -webkit-text-stroke:1px rgba(255,255,255,.14);
  pointer-events:none;user-select:none;
}
/* 隐藏初态只写在 html.has-motion 下（该类由 <head> 内联脚本在「未降级 + 有 IO」时加），
   所以降级 / 无 IO / motion.js 没加载时标题直接可见，不会永久隐身。
   压缩后归位 = scale:1.06 .84 → 1：先横向压扁，再随遮罩揭开一起放松。

   ⚠️ 下沿那 100% 是硬约束，别改回 101% 以上：Chrome 的 IntersectionObserver
   会把元素自身的 clip-path 算进相交矩形，下沿一旦超过 100%（矩形翻负、退化成空集），
   IO 就永远判定「不相交」—— 这张标题无论怎么滚都进不了 .in，永久隐身。
   100% 时裁切面正好落在元素上边缘（上面 -16% 的留白保证矩形非空），
   视觉上依然「一个像素都不露」，但 IO 认得出它。 */
html.has-motion .mod-en{
  opacity:0;
  clip-path:inset(-16% -8% 100% -8%);
  translate:0 46px;
  scale:1.06 .84;
  transition:clip-path var(--dur-7) var(--ease-expo) var(--mx-d,0ms),
             translate var(--dur-7) var(--ease-expo) var(--mx-d,0ms),
             scale var(--dur-7) var(--ease-expo) var(--mx-d,0ms),
             opacity var(--dur-4) var(--ease-out) var(--mx-d,0ms);
}
html.has-motion .mod-en.in{
  opacity:1;
  clip-path:inset(-18% -10% -18% -10%);
  translate:0 0;
  scale:1;
}

/* 首屏 Hero 的英文大标题：真实占位的一行，用品牌渐变填充（和 .mbti-you 同一套做法） */
.ch-text{min-width:0}
.ch-text .mod-en{
  display:block;margin-bottom:2px;
  font-size:clamp(26px,3.4vw,42px);
  color:transparent;
  background:var(--grad);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-stroke-width:0;
}

/* 压在图片上的这块用轮廓水印：不抢主体，但滚动到位时「大幅进场」依然看得见 */
.flowscroll .mod-en{
  position:absolute;z-index:3;
  font-size:clamp(24px,3vw,40px);
  color:rgba(255,255,255,.10);
  -webkit-text-stroke:1px rgba(255,255,255,.26);
  text-shadow:0 2px 18px rgba(0,0,0,.55);
  text-align:right;
}
.flowscroll .mod-en{right:var(--sp-5);bottom:calc(var(--sp-5) - 4px)}

/* —— 栅格区模块头已并入 .content-head（那条渐变细线留在页头底部）——
   原先这里还有一整块「Gallery / 全部壁纸」，与页头的「发现」重复且与
   42px 大英文标题撞车，已整块移除；.mod-zh 随之一起删。 */

/* —— ㉕ 图片揭显 ——
   app.js 的 .loaded 是「离视口还有 260px 就加上」（那是给视频预拉源用的），
   照它揭显的话用户在视口外就把动画跑完了，滚到跟前什么也看不到。
   所以这里单独挂 .img-in，由 motion.js 用「真的进视口」的观察器加。
   has-imgreveal 由 motion.js 自己加 —— 它没加载成功就整块不生效，
   .loaded 的老淡入照旧兜底，图片不会隐身。 */
html.has-imgreveal .media img,
html.has-imgreveal .media video{
  opacity:0;
  scale:1.05;
  transition:opacity 620ms var(--ease-out),
             scale 820ms var(--ease-expo),
             transform .5s var(--ease);
}
html.has-imgreveal .media img.img-in,
html.has-imgreveal .media video.img-in{opacity:1;scale:1}

/* 手机：装饰性的英文大标题一律收掉（水印挤在小图上会显脏），
   顺带保住移动端本就紧张的首屏高度预算 */
@media(max-width:720px){
  .ch-text .mod-en,
  .flowscroll .mod-en{display:none}
}
