/* 知未言提示词 · 基础样式
 * 排版参考站酷：灰底白卡 · 横排分类 · 大卡片网格（每行五张）
 * 交互参考 Apple：毛玻璃吸顶 · 柔和缓动 · 微位移反馈 · 克制动效
 * 布局：全屏响应式（--wrap 之上不留死宽度，靠 clamp 与断点列数自适应）
 */
/* ---------- 字体：HarmonyOS Sans SC（自托管子集，文件在 assets/fonts/）----------
 * 为什么不用系统字体：Mac / 手机装不到这套字，只有自托管才能让所有访客看到同一个字体。
 * 为什么是子集：原 ttf 一个字重 8MB，三个就是 24MB；子集化后每个约 930KB。
 * 已收录：站点全部文案 + GB2312 全字集（6763 字），生僻字会回退到系统字体。
 */
@font-face {
  font-family: 'HarmonyOS Sans SC';
  src: url('../fonts/HarmonyOS-Sans-SC-Regular.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'HarmonyOS Sans SC';
  src: url('../fonts/HarmonyOS-Sans-SC-Medium.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'HarmonyOS Sans SC';
  src: url('../fonts/HarmonyOS-Sans-SC-Bold.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

:root {
  /* ⭐ 2026-09-23 第一轮：用户要求「界面颜色只留黑白灰」→ 彩色全部撤掉。
     ⭐ 2026-09-23 第二轮：用户要求「重要文字改 #000000，需要有深色的地方都改为 #000000」。
     追问确认过的边界（用户选「次要文字留灰」）——
       · **该深的一律纯黑 #000000**：主文字 / 主色 / 已通过 / 次级强调 / 待审字 / 已驳回字；
       · **次要文字保持灰**：--ink-2 / --ink-3（主次还得靠深浅分，全黑就没层级了）；
       · **图表两档保持灰**：--shade-mid / --shade-light（否则三张条形图只剩标题能区分）。
     所以本文件里凡是以 #111111 / #3C3C3C / #4C4C4C / rgba(17|30|60, …) 打底的深色，
     都已收成纯黑；**唯一还"深但不纯黑"的只剩 --ink-2 / --shade-mid**，那是故意留的。
     硬约束：任何界面颜色必须 R=G=B，pair-check 的 [H] 段会扫（含内联 SVG）。
     ⭐ 2026-09-23 第三轮：用户要求「整个后台换彩色主题」。
     做法是**只给后台开一个口子** —— 见 assets/css/admin-theme.css，本文件一行没改：
     公众页仍然全灰；admin.html 靠 `body.admin-page` 作用域重定义下面这几个语义变量来上色
     （界面规则本来就写 var(--brand) 这种引用，变量一换自动跟着变）。
     所以 [H] 段的扫描清单已排除 admin.html / admin.js / admin-theme.css，
     [H2] 段专门盯着「后台彩色不许扩散到公众页」。 */
  --ink: #000000;          /* 主文字（重要文字），也是「主色」—— 黑白灰体系里品牌色就是黑 */
  --ink-2: #4C4C4C;        /* 次级文字（作者名/时间/邮箱…）—— 用户要求保留这一档灰，别改黑 */
  --ink-3: #6B6B6B;        /* 弱化文字。⚠ 2026-09-25 从 #8C8C8C 提上来：
                               原值在白卡上只有 3.36:1、在页面底上 3.06:1，而 AA 要 4.5:1；
                               现值 5.33:1 / 4.85:1，**仍是灰阶（R=G=B）**，[H] 灰阶检查照旧过。
                               ⚠ 别调回 #8C8C8C 那一带 —— 灰的它看不出问题，只有对比度量尺抓得到。 */
  --line: #E5E5E5;         /* 分隔线 */
  --line-2: #F1F1F1;       /* 更浅的分隔线 / 徽章默认底 */
  --bg: #F4F4F4;           /* 页面底 */
  --panel: var(--ink-invert);        /* 卡面 */
  --brand: #000000;        /* 主色：选中项 / 主按钮底 → 黑底白字，对比最强 */
  --brand-dark: #000000;
  --brand-soft: #F0F0F0;   /* 选中 / 标签的浅底 */
  --accent: #000000;       /* 次级强调：置顶标记、图生图标签的字色（原来是深灰 #3C3C3C） */
  --accent-soft: #E5E5E5;
  --ok: #000000;           /* 已通过 = 纯黑实底 + 白字（三档里最重的一档） */
  --ok-soft: #EAEAEA;
  --warn: #000000;         /* 待审核 = 浅灰实底 + 纯黑字 + 细描边（靠"底色浅"跟已通过分开） */
  --warn-soft: #EEEEEE;
  --bad: #000000;          /* 已驳回 = 白底 + 描边 + 纯黑字（靠「没有填充」表达被退回） */
  --bad-soft: var(--ink-invert);
  /* 图表专供：后台数据看板那三个条形图原来靠「橙 / 绿 / 蓝」区分，
     去色后必须靠**明度阶梯**分开 —— 黑 → 中灰 → 浅灰，一眼看得出是三组。
     ⚠ 这两档**故意不纯黑**：三张图都黑掉后就只剩标题和数字能区分了。 */
  --shade-mid: #6E6E6E;
  --shade-light: #8A8A8A; /* ⚠ 2026-09-25 从 #ADADAD 提上来：与轨道 #F1F1F1 只有 2.0:1，图形要 3:1，
                               现值 3.06:1。与 --shade-mid 的间隔因此变窄，但三张图各有标题、
                               每根柱子右侧都有数字，不靠颜色唯一表意。 */
  /* ⭐ 2026-09-25 圆角收敛成 3 档 + 药丸 + 光学档（原来是 19 种即兴值）：
       xs = 细线与极小色块的光学圆角（浏览器会把超过半个高度的值夹住，细线上 4px 与 2px 看起来一样）
       sm = 输入框、小徽章、缩略图
       --radius = 卡片内的小面板、按钮组
       --radius-lg = 卡片、大面板、弹窗
       --radius-pill = 药丸（按钮、标签、头像环） */
  --radius-xs: 4px;
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 16px;
  --radius-pill: 980px;

  /* ⭐ 2026-09-25 字号收敛成 6 档 + 流体标题（原来 21 种，其中
     11.5 / 12 / 12.5 / 13 / 13.5 / 14 / 14.5 七档挤在 3px 区间 —— 典型的「一处一处调出来的」）。
     ⚠ 卡片标题桌面 17px / 窄屏 16px 有断言卡着，别动这两个。
       （指的是**首页**卡片 .card-title —— 用户 2026-09-24 明确要求「放大 2 号」。
        「模板卡」.t-tpl-t 是**另一档**（--fs-title-sm 16px / 500，2026-09-29 用户要求），
        两者别混：改 --fs-md 会连首页一起改，那就把用户 09-24 那条要求改回去了。） */
  --fs-2xs: 12px;                        /* 徽章、极小标记、次要注释 */
  --fs-xs: 13px;                         /* 卡片说明、次要文字、小控件 */
  --fs-sm: 14px;                         /* 导航、控件、标签 */
  --fs-base: 15px;                       /* 正文（body 也是这个） */
  --fs-title-sm: 16px;                   /* 卡片标题（≤600px 那一档） */
  --fs-md: 17px;                         /* 卡片标题（桌面） */
  --fs-lg: 20px;                         /* 中等区块标题 */
  --fs-stat: 25px;                       /* 后台看板的大数字 */
  --fs-stat-lg: clamp(24px, 2.4vw, 34px); /* 看板顶部那一排高亮数字（比 --fs-stat 更重一档） */

  /* ⭐ 文章**正文内**的标题（不是页标题）。原来 25/20/17 与窄屏 21/18 都是当场写死的。
     ⚠️ 这是"内容排版"，跟页标题是两回事 —— 别拿 --fs-h1 去套它（正文要的是阅读节奏，不是版面气势）。 */
  --fs-c-h1: 25px;
  --fs-c-h2: var(--fs-lg);               /* 20px */
  --fs-c-h3: var(--fs-md);               /* 17px */
  --fs-c-h1-sm: 21px;                    /* 窄屏那两档 */
  --fs-c-h2-sm: 18px;
  /* ⭐⭐ 2026-09-29 同日第十五轮：用户要求「所有页面…都做统一，避免各页面之间的不协调」。
     页标题原来有 **5 种档位**（这样板很难说是"一个站"）：
       · .page-head h1 / .t-h1 → --fs-h1-lg (clamp 24~32)
       · .set-head h1          → clamp(22, 2.6vw, 28)
       · .art-title            → clamp(23, 2.4vw, 32)
       · .scene-head h1 / .d-title → --fs-h1 (clamp 21~27)
     现在**只有一个页标题档**，值取第十一轮已经和用户敲定的那档（32px 上限）——
     往大里收，而不是往小里收：第十一轮用户明确把文章/投稿/模板标签三页的标题统一到了这一档。
     ⚠️ 所以 --fs-h1-lg 这个变量**已删**，所有页标题一律 var(--fs-h1)。
       别再新造第二档页标题 —— 那正是这一轮要治的病。 */
  --fs-h1: clamp(24px, 2.6vw, 32px);     /* 页面标题（全站唯一一档） */
  --fs-hero: clamp(21px, 2.5vw, 31px);   /* 首页轮播标题 */
  --fs-cover: clamp(32px, 3.6vw, 54px);  /* 无图卡片的兜底首字 */

  /* ⭐ 2026-09-25 表面 / 描边 / 反色文字收进变量（原来是 14 种硬编码颜色）。
     ⚠ 后台彩色主题（admin-theme.css）里另有 #F0D48A / #F3C6C6 / #F0BFBF，那是刻意的彩色，不在收敛范围。 */
  --surface-2: #F8F8F8;
  --surface-3: #F3F3F3;
  --surface-4: #FAFAFA;
  --surface-hover: #F5F5F5;
  --line-soft: #E6E6E6;                  /* 卡片极浅描边（理由见 .card 那一段） */
  --line-mid: #DDDDDD;
  --line-strong: #CBCBCB;
  --ink-invert: #FFFFFF;                 /* 深底上的文字（黑药丸白字、图上白字） */
  --tip-bg: #333333;                     /* 悬浮气泡底 */

  /* ⭐ 2026-09-25 间距 4px 阶梯（新代码从这里取；顶栏/导航等像素级调优区有意不套） */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;
  --ease: cubic-bezier(.32, .72, 0, 1);
  --shadow-0: 0 1px 2px rgba(0, 0, 0, .05);          /* 最轻一档：静止的小白块（按钮底）立起来一点 */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 2px 8px rgba(0, 0, 0, .045);
  --shadow-2: 0 2px 8px rgba(0, 0, 0, .06), 0 14px 36px rgba(0, 0, 0, .10);

  /* ⭐ 2026-09-29 同日第十五轮：用户要求「阴影 / 交互 / 颜色也做统一」。
     扫描发现阴影有 **12 处**是当场写死的（同一件事写成 12 个不同的值），这里收成一条阶梯。
     每条都要能回答"什么时候用我"，不然过两个月又会长出第 13 种。 */
  --shadow-3: 0 24px 70px rgba(0, 0, 0, .22);        /* 最重的一档：弹窗（要压住整页） */
  --shadow-focus: 0 0 0 3px rgba(0, 0, 0, .10);      /* 聚焦环：输入框 focus / 选中项 */
  --shadow-pop: 0 2px 8px rgba(0, 0, 0, .18);        /* 浮层小泡：下拉、气泡、浮动按钮 */
  --shadow-btn: 0 1px 2px rgba(0, 0, 0, .16);        /* 主按钮静止态：一点点"立起来" */
  --shadow-btn-hover: 0 8px 22px rgba(0, 0, 0, .20); /* 主按钮 hover：抬起来更多 */
  --shadow-up: 0 -2px 18px rgba(0, 0, 0, .10);       /* 底部吸附条（往上投） */
  --shadow-edge: -10px 0 10px -10px rgba(0, 0, 0, .18); /* 侧滑面板靠内容那侧的软边 */

  /* ⭐ 悬停 / 字段 / 遮罩 —— 全是"半透明黑在白色上"，但原来是 8 个不一样的 alpha
     （.04 .045 .05 .055 .07 .08 .09 .10）。同一件"鼠标划过去变暗一点"，
     在首页、后台、详情页各写各的 → 用户能感觉到"不同页面手感不一样"，但说不出哪里不一样。
     现在只留 2 档悬停 + 1 个字段底，语义写死，别再随手加。 */
  --hover-1: rgba(0, 0, 0, .045);   /* 轻微：导航项 / 幽灵按钮 hover */
  --hover-2: rgba(0, 0, 0, .07);    /* 稍重：已选中项 hover / 用户菜单 hover */
  --field-bg: rgba(0, 0, 0, .05);   /* 输入框与分段控件的静止底色 */
  --scrim: rgba(0, 0, 0, .38);      /* 弹窗背后的压暗层 */
  --overlay: rgba(0, 0, 0, .82);    /* 大图查看（灯箱）的底 —— 要几乎全黑才看得清图 */
  --toast-bg: rgba(0, 0, 0, .95);   /* 提示条底（全站同一档，原来 .9 / .95 两个值混用） */

  /* ⭐ 字间距：原来 3 处各写各的（.02em / -.2px / -.01em）。
     ⚠️ 大标题收紧、小标签撑开，是两个**方向相反**的用途，所以要两条而不是一条。 */
  --ls-tight: -.01em;   /* 大字号标题：字距略收，标题才成"一整块" */
  --ls-wide: .02em;     /* 小号全大写 / 英文副名：撑开一点才好认 */

  /* ⭐ 正圆：头像、圆点、加载环都是几何意义上的圆，不是"圆角风格"。
     给个令牌是为了让门禁能一眼看出"50% 是故意写的"，而不是第 11 种圆角。 */
  --radius-circle: 50%;

  /* ⭐ 页头下留白（pushing 下面内容的那一道）。
     扫描发现同一件事在五个页面上写了两个值：文章/收藏/投稿/模板标签 用 14px，
     场景页/账号设置 用 18px —— 同一站里两个数，用户就会觉得"有的页面挤有的松"。
     现在收成一个命名值（取原先 4 个页面用的 14px：那是第十一轮已经和用户敲定的）。 */
  --head-gap: 14px;
  --wrap: 100%;                              /* 不再限制宽度：内容铺满整个视口 */
  --pad: clamp(14px, 1.6vw, 24px);           /* 只留一点点安全边距 */
  --topbar-h: 56px;
  --font: 'HarmonyOS Sans SC', -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
}
* { box-sizing: border-box; }
/* input / textarea / select / button 不会自动继承 body 字体，这里统一收口 */
input, textarea, select, button, optgroup, option { font-family: inherit; }
/* 必须放在作者样式最前面：否则 display:flex/grid 的容器会盖掉 hidden 属性 */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  background: var(--bg); color: var(--ink);
  font: 15px/1.6 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
/* ⚠️⚠️ **不许给标题加负字距**（2026-09-29 轮次 AE 试过、被冒烟挡回来了）。
   有个很常见的排版建议是"大标题收紧字距（-.01em 之类）"—— 那是**拉丁文**的惯例：
   拉丁字母侧边距大、收紧显整。**中文是全角字，笔画本来就顶到字身框边界，收紧会让相邻字相碰。**
   项目早就把这条定成反向锁（"全站不再有负字距"），别再加回来。 */
h1, h2, h3, h4 { margin: 0; line-height: 1.3; }
p { margin: 0; }
button { font: inherit; cursor: pointer; }
img { max-width: 100%; display: block; }
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--pad); }
.muted { color: var(--ink-3); }
.small { font-size: var(--fs-xs); }
a:focus-visible, button:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid rgba(0, 0, 0, .45); outline-offset: 2px;
}
/* ---------- 屏幕阅读器专用 / 跳到主要内容（2026-09-25 补，P1-2 + P1-15）----------
   .sr-only：视觉上不占位、但读屏读得到 —— 首页与场景页原来**没有 h1**，
   而它们又把可见标题删掉了，所以只能这样补一个（别再删）。
   .skip-link：键盘用户 Tab 第一下就拿到它，回车直接跳到 <main>，
   不用先穿过 品牌链接 + 3 个导航 + 搜索框 + 随机按钮 + 用户按钮。
   ⚠ 七个页面都在 <body> 后第一行插了它，每个页面自己的 <main> 都带 id="main" tabindex="-1"。 */
.sr-only {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: 8px; z-index: 400;
  transform: translateY(-220%);
  background: var(--ink); color: var(--ink-invert);
  padding: 10px 16px; border-radius: var(--radius-pill);
  font-size: var(--fs-sm); text-decoration: none;
  transition: transform .2s var(--ease);
}
.skip-link:focus { transform: none; }
/* 跳转过去之后整页别出现一圈描边（main 是 tabindex="-1"，只为接住焦点） */
main:focus { outline: none; }

/* ---------- 首屏骨架（P0-3）----------
   首页卡片区在取到数据之前不再是一片空白。
   ⭐ 只在**取数超过 120ms** 时才画（app.js 用一个 setTimeout 控制）——
     本机模式下数据 10ms 就绪，一画一撤反而是一次闪烁。
   ⚠ 骨架**不许用 .card 类**：冒烟是按 `#grid .card` 数真卡片的，挂了就会多数出来。
   封面块复用已有的 sheen 微光动画，所以视觉语言和真卡片一致。 */
.skel-col { display: flex; flex-direction: column; gap: clamp(14px, 1.4vw, 22px); min-width: 0; }
.skel { background: var(--panel); border: 1px solid var(--line-soft); border-radius: var(--radius-lg); overflow: hidden; }
.skel-cover {
  background: linear-gradient(100deg, var(--line-2) 32%, var(--surface-2) 50%, var(--line-2) 68%);
  background-size: 220% 100%; animation: sheen 1.6s linear infinite;
}
.skel-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 8px; }
.skel-line { height: 12px; border-radius: var(--radius-xs); background: var(--line-2); }
.skel-line.is-short { width: 46%; }

/* ---------- 轮播的暂停按钮（2026-09-25 加，P1-6）----------
   自动轮播必须能停（WCAG 2.2.2）；「减少动效」下 app.js 直接不启动定时器，这个按钮也就不出现。
   ⚠ 纯文字，不用图标（全站去图标那条硬规矩）。 */
.hero-pause {
  position: absolute; right: 12px; top: 10px; z-index: 4;
  border: 1px solid rgba(0, 0, 0, .12); background: rgba(255, 255, 255, .82); color: var(--ink);
  border-radius: var(--radius-pill); padding: 9px 13px; font-size: var(--fs-2xs);
  cursor: pointer; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: background .2s var(--ease);
}
/* ⭐ 2026-09-29 轮次 AE：触达区量尺实测 39px → 补到 45（差 3px，肉眼完全看不出来，
   但手机上那 3px 就是点不准）。伪元素内衬，按钮自身盒子不变。 */
.hero-pause { position: relative; }
.hero-pause::before { content: ''; position: absolute; inset: -4px 0; }
.hero-pause:hover { background: rgba(255, 255, 255, .96); }
.hero-pause.is-paused { background: var(--ink); color: var(--ink-invert); border-color: var(--ink); }
.hero-card.has-img .hero-pause { border-color: rgba(255, 255, 255, .26); background: rgba(0, 0, 0, .40); color: var(--ink-invert); }
.hero-card.has-img .hero-pause:hover { background: rgba(0, 0, 0, .62); }
.hero-card.has-img .hero-pause.is-paused { background: var(--ink-invert); color: var(--ink); border-color: var(--ink-invert); }

/* ---------- 重筛时不重播入场动画（2026-09-25 加，P1-4）----------
   卡片 DOM 是整块 innerHTML 重建的（app.js 会如实报告），而 `rise-in` 是无条件生效的 →
   每敲一个字整屏卡片都会重播一次淡入。首屏仍然保留错峰入场，只有"重筛"这一路关掉。
   场景页同理（.wall 那一路由 sub.js 挂同一个类）。 */
/* ⭐ 2026-09-29 同日第四轮：新纳入动效的卡片类也一并关掉重播 ——
   凡是"筛一下 / 搜一下就把整块列表 innerHTML 重建"的地方都要覆盖到，
   否则每敲一个字整屏卡片重播淡入，比没有动画还糟。
   ⚠️ 这里按"行为"加类名（谁会被重画就加谁），不要按页面加。
   ⚠️⚠️ 2026-09-29 同日第四轮（后半）：**去掉 .grid / .wall 前缀** ——
      原来只认这两个容器类，而提示词模板页的三个容器（#tTpls / #tCards / #tExs）
      既不叫 .grid 也不叫 .wall，于是挂了类也不生效（**而且不报错**，只是白挂）。
      现在改成"父容器带 .is-refilter 就够"，与下面 .my-item / .rs-row 那几条一致。
      新增会被整块重画的列表容器，直接把 .is-refilter 挂在容器上即可。 */
.is-refilter .card, .is-refilter .p-card,
.is-refilter .t-card, .is-refilter .t-ex,
.is-refilter .a-card, .is-refilter .art-card, .is-refilter .work-card,
.is-refilter .my-item, .is-refilter .rs-row, .is-refilter .model-row { animation: none; }
/* ⚠️ .t-tpl 单独一条：它的容器（#tTpls.t-tpls）本身就是卡片列表，
   但卡片是 .t-tpls **直接子元素** —— 上面那条 `.is-refilter .t-tpl`
   同样命中（后代选择器），所以这里不需要重复。
   保留这行注释是为了防止后人以为"漏了 t-tpl"又补一条。 */

/* ---------- 顶栏（Apple 毛玻璃吸顶） ---------- */
.topbar {
  position: sticky; top: 0; z-index: 60;
  background: rgba(255, 255, 255, .72);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.topbar.is-scrolled { background: rgba(255, 255, 255, .88); border-bottom-color: var(--line); }
.topbar-inner {
  max-width: var(--wrap); margin: 0 auto; height: var(--topbar-h);
  padding: 0 var(--pad);
  display: flex; align-items: center; gap: 20px; flex-wrap: nowrap; min-width: 0;
}
.brand { display: flex; align-items: center; gap: 10px; flex: none; position: relative; }
/* 站标：图桌面 30px、**窄屏只有 23px**。按 -11px 补，两种高度下都到 45 以上
   （第二轮量尺实测：-8px 在窄屏只有 39px，不够）。
   ⚠️ 横向只扩 6px：它右边紧挨着导航，横向扩多了会抢"首页"的点击。 */
.brand::before { content: ''; position: absolute; inset: -11px -6px; }
/* 站标 = 一张图（2026-09-24 第二轮）。用户说「网页这里的文字部分不要了」——
   他的 assets/logo.png 里已经把「知未言」画进去了，旁边再渲染一遍站名是重复。
   ⚠ 这里**没有** .brand-mark / .brand-text / .brand-long / .brand-short 了：
   换 logo 只需替换 assets/logo.png，品牌名保留在 <img alt> 里。
   只写死高度、宽度按原图比例（275:85）来，以后换别的比例的图也不会被压扁。
   （想跟整站黑白灰统一的话，在下面加一行 filter: grayscale(1) 就行 ——
     2026-09-24 用户给的是彩色版，如原样保留；他没要求转灰，别自作主张。） */
.brand-logo {
  display: block; height: 30px; width: auto;
  transition: transform .3s var(--ease);
}
.brand:hover .brand-logo { transform: scale(1.04); }
.nav { display: flex; gap: 2px; flex: 1; min-width: 0; }
.nav a {
  position: relative; padding: 8px 12px; border-radius: var(--radius-sm);
  color: var(--ink-2); font-size: var(--fs-sm); white-space: nowrap;
  transition: color .2s var(--ease), background .2s var(--ease);
}
/* ⭐ 命中区补到 ≥44px（2026-09-29 轮次 AE）。实测量到顶栏导航只有 38px。
   ⚠️ 用 `::before` 而**不是** `::after` —— .nav a 的 `::after` 已经被"选中下划线"占了，
      抢过来会把下划线弄没（这种冲突只有真跑一遍才看得出）。
   ⚠️ **只往上下扩**（inset: -4px 0）：往左右扩会和相邻导航项的命中区重叠，
      点"文章"却打开了"首页" —— 触屏上这种误点最难查。
   ⚠️ 元素自身的 rect 不变（量尺量到的还是 38），真可点区是 46 —— 和 .hero-arrow 同一个口径。 */
.nav a::before { content: ''; position: absolute; inset: -4px 0; }
.nav a:hover { color: var(--ink); background: var(--hover-1); }
.nav a.is-active { color: var(--ink); font-weight: 500; }
.nav a.is-active::after {
  content: ''; position: absolute; left: 12px; right: 12px; bottom: -1px;
  height: 2px; border-radius: var(--radius-xs); background: var(--brand);
}
.topbar-right { display: flex; align-items: center; gap: 10px; margin-left: auto; flex: none; }
/* ---------- 顶栏搜索区（2026-09-23：从首页那排整排挪进来，只在首页渲染） ---------- */
/* 位置：导航右侧、用户区左侧。.nav 原来是 flex:1 吃中间空间，有搜索区时把它让出来 ——
   只在 .has-search 下改，别的页面顶栏一个像素都不变。
   窄屏放不进一行时整个换行到第二行（见媒体查询），顶栏高度由 ui.js 的 syncTopbarH() 同步。 */
.topbar-search { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; max-width: 560px; }
.topbar-inner.has-search .nav { flex: none; }
.topbar-search .search { flex: 1; min-width: 0; }
.topbar-search .search input { padding: 11px 15px; font-size: var(--fs-xs); }
.topbar-search .btn { flex: none; white-space: nowrap; }
.mode-chip { font-size: var(--fs-2xs); color: var(--warn); background: var(--warn-soft); border: 1px solid var(--line-mid); padding: 3px 9px; border-radius: var(--radius-pill); white-space: nowrap; }
.user-wrap { position: relative; }
.user-btn {
  display: flex; align-items: center; gap: 8px; background: var(--hover-1);
  border: 1px solid transparent; border-radius: var(--radius-pill); padding: 5px 12px 5px 5px; color: var(--ink);
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.user-btn:hover { background: var(--hover-2); }
.avatar { width: 26px; height: 26px; border-radius: var(--radius-circle); background: var(--brand); color: var(--ink-invert); display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 600; flex: none; }
/* 头像里的真图（2026-09-28 加）。
   ⚠️ 两种头像（空 / 图片）都长在同一个 `.avatar` 里（预置色块 2026-09-28 第二轮已删）：
      有图时把圆形底色去掉 + overflow:hidden，否则图片四周会露一圈底色
      （透明 PNG 和圆角对不齐时最明显）。
   ⚠️ 图是 **object-fit: cover** 而不是 contain：头像是圆形的，留白会显得脏。 */
.avatar.has-img { background: none; overflow: hidden; }
.avatar-img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: var(--radius-circle); }
/* 尺寸变体：默认 26（顶栏 / 卡片作者位），20 由 `.a-by .avatar` 覆盖（文章卡），
   56 是「账号设置」页的预览（2026-09-28 加）。要再加尺寸就在这里加一条，别到处写 width。 */
.avatar-lg { width: 56px; height: 56px; font-size: var(--fs-lg); }
.role-dot { font-size: var(--fs-2xs); background: var(--accent-soft); color: var(--accent); border-radius: var(--radius-xs); padding: 1px 4px; }
.menu {
  position: absolute; right: 0; top: calc(var(--topbar-h) - 10px);
  background: rgba(255, 255, 255, .92);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-2);
  min-width: 200px; padding: 6px; z-index: 60; transform-origin: top right;
}
.menu-head { font-size: var(--fs-2xs); color: var(--ink-3); padding: 6px 10px 8px; border-bottom: 1px solid var(--line-2); margin-bottom: 6px; word-break: break-all; }
.menu a, .menu button { display: block; width: 100%; text-align: left; padding: 11px 10px; border: 0; background: none; border-radius: var(--radius-sm); font-size: var(--fs-sm); color: var(--ink); transition: background .16s var(--ease); }
.menu a:hover, .menu button:hover { background: var(--hover-1); }

/* ---------- 按钮（Apple 药丸 + 按下微缩） ---------- */
/* ⭐ 2026-09-25 控件尺寸立档（P1-9）：默认高度按 44px 给（padding 13 + 行高 18 ≈ 44），
   拇指点得中；小号 36px。横向 padding 不变，宽度自然撑开。
   ⚠ 顶栏与分类条那两个像素级调优区**有意不套这一档**（topbar-metrics 盯着它们）。 */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  /* 描边从 --line(#E5E5E5，白卡上 1.24:1) 提到 --line-mid：原来「加载更多 / 取消 / 关闭」看着像
     纯文字，能不能点全靠猜。再加一层极淡投影，白药丸在灰底上才立得住。 */
  border: 1px solid var(--line-mid); background: var(--panel); color: var(--ink);
  box-shadow: var(--shadow-0);
  border-radius: var(--radius-pill); padding: 10px 18px; font-size: var(--fs-sm); white-space: nowrap;
  transition: transform .22s var(--ease), background .22s var(--ease), border-color .22s var(--ease), color .22s var(--ease), box-shadow .22s var(--ease);
}
.btn:hover { border-color: var(--line-strong); box-shadow: var(--shadow-1); transform: translateY(-1px); }
.btn:active { transform: scale(.97); }
/* ⭐ 2026-09-29 轮次 AE：原来只有 `opacity: .5` —— 黑色主按钮半透明之后
   文字对比度掉到 ~3.9:1（AA 要 4.5），而且 `cursor: not-allowed` 在触屏上根本不存在。
   现在改成**明确的浅灰实底 + 灰字**（对比度 5.3:1，灰阶内），并加 `pointer-events: none`
   从根上断掉点击（原来能点、只是业务层拒绝，用户会觉得"点了没反应"）。
   ⚠️ `opacity` 保留一点点（.92）是为了让禁用态在**任何底色**上都还看得出"淡了一档"。 */
.btn:disabled, .btn.is-disabled {
  opacity: .92; cursor: not-allowed; pointer-events: none; box-shadow: none;
  background: var(--surface-3); border-color: var(--line); color: var(--ink-3);
}
.btn-primary { background: var(--brand); border-color: var(--brand); color: var(--ink-invert); }
.btn-primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
.btn-ghost { background: transparent; box-shadow: none; }
.btn-ghost:hover { background: var(--hover-1); box-shadow: none; }
/* 黑白灰里没有「红色」可用：危险按钮改成**白底 + 深灰描边**（看着比主按钮轻），
   悬停才反转为深灰实底 —— 多一步确认感，也避免和黑色主按钮抢。 */
.btn-danger { background: var(--panel); border-color: var(--line-strong); color: var(--bad); }
.btn-danger:hover { background: var(--bad); border-color: var(--bad); color: var(--ink-invert); }
.btn-sm { padding: 7px 13px; font-size: var(--fs-xs); position: relative; }
/* ⭐ 小按钮实测 37px → 命中区补到 45（只上下扩，免得和同一行相邻按钮抢点击）。
   ⚠️ **不许把 padding/height 改成 44** —— .btn 44 / .btn-sm 37 是评审落地的视觉尺寸。 */
.btn-sm::before { content: ''; position: absolute; inset: -4px 0; }
.btn-block { width: 100%; }

/* ---------- 表单 ---------- */
.field { margin-bottom: 14px; }
.field > label { display: block; font-size: var(--fs-xs); color: var(--ink-2); margin-bottom: 6px; }
.input, .select, .textarea {
  width: 100%; border: 1px solid var(--line); border-radius: var(--radius); padding: 12px;
  font: inherit; color: var(--ink); background: var(--panel);
  /* ⭐ 2026-09-29 轮次 AE：`<select>` 的 line-height 默认是 `normal`（≈16px），
     而 input 是 24px —— 两者并排时**高度不一样**，下拉框的文字会偏上。
     显式给同一档，三件套永远一样高。（.textarea 下面另有 1.7，那是多行编辑要的松。） */
  line-height: 1.6;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease);
}
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: var(--shadow-focus);
}
.textarea { min-height: 150px; resize: vertical; line-height: 1.7; }
.hint { font-size: var(--fs-2xs); color: var(--ink-3); margin-top: 6px; }
.hint.is-ok { color: var(--ok); }
.hint.is-warn { color: var(--warn); }
.row-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }

/* ---------- 徽章 / 标签 ---------- */
.badge { display: inline-block; font-size: var(--fs-2xs); padding: 2px 9px; border-radius: var(--radius-pill); background: var(--line-2); color: var(--ink-2); }
/* 三档状态只靠「明度阶梯 + 文字」区分（2026-09-23 用户选定）：
   浅灰实底 → 纯黑实底 → 白底描边。灰度打印、色弱、缩到很小都分得清，
   而且徽章里本来就写着「待审核 / 已通过 / 已驳回」。
   ⚠ 别再加颜色回来 —— pair-check 的 [H] 段会红。 */
.badge-pending { background: var(--warn-soft); color: var(--warn); border: 1px solid var(--line-mid); }
.badge-approved { background: var(--ok); color: var(--ink-invert); }
.badge-rejected { background: var(--ink-invert); color: var(--bad); border: 1px solid var(--line-strong); }
/* 角色徽章：光靠文字区分不够（管理员 / 普通用户 两个胶囊长得一模一样，看截图才发现）。
   黑灰体系里用「底色 + 描边」分档，跟三档状态徽章同一套逻辑：
     管理员  = 白底 + 深色描边 + 加粗（强调，但不跟「正常」的黑实底抢）
     普通用户 = 浅灰实底 + 灰字（弱化，见 .badge）
   白底黑框 vs 白底浅灰框（.badge-rejected）也分得开：描边深浅差一档。 */
.badge-admin { background: var(--ink-invert); border: 1px solid var(--ink); color: var(--ink); font-weight: 600; }
.tag { display: inline-block; font-size: var(--fs-2xs); font-weight: 500; padding: 2px 8px; border-radius: var(--radius-sm); background: var(--brand-soft); color: var(--brand-dark); }
.tag-i2i { background: var(--accent-soft); color: var(--accent); }

/* ---------- 提示条 / 空态 / 加载 ---------- */
.notice { background: var(--warn-soft); border: 1px solid var(--line-mid); color: var(--ink-2); border-radius: var(--radius); padding: 11px 14px; font-size: var(--fs-xs); }
.notice a { text-decoration: underline; position: relative; }
/* ⭐ 门禁那句「去登录 / 注册」实测只有 **18px** 高 —— 全站最难点中的可点元素。
   补到 44（18 + 13×2）。 */
.notice a::before { content: ''; position: absolute; inset: -13px -8px; }
.empty, .loading { padding: 60px 16px; text-align: center; color: var(--ink-3); font-size: var(--fs-sm); }
.loading::after { content: ''; display: inline-block; width: 14px; height: 14px; margin-left: 8px; border: 2px solid var(--line); border-top-color: var(--brand); border-radius: var(--radius-circle); animation: spin .7s linear infinite; vertical-align: -2px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- toast / 弹层 ---------- */
.toast-box { position: fixed; left: 50%; top: 18px; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; }
.toast {
  background: var(--toast-bg);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  color: var(--ink-invert); padding: 9px 18px; border-radius: var(--radius-pill); font-size: var(--fs-sm); box-shadow: var(--shadow-2);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
/* 成功 = 深灰底，失败 = 纯黑底（更沉）。都是灰阶，靠明度分，文字内容本来就说清了结果。 */
/* ⚠️ 这两个变体在公众页与底态同色（灰阶体系里"成功/失败"靠文案说，不靠颜色）；
     留着是为了**后台**能覆盖成彩色（见 admin-theme.css 的 body.admin-page .toast.is-*）。
     所以别删 —— 删了后台那两条就没有落点了。 */
.toast.is-error { background: var(--toast-bg); }
.toast.is-ok { background: var(--toast-bg); }
.toast.is-out { opacity: 0; transform: translateY(-8px); }
.modal { position: fixed; inset: 0; background: var(--scrim); display: grid; place-items: center; z-index: 150; padding: 20px; -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.modal-box { background: var(--panel); border-radius: var(--radius-lg); width: 100%; max-width: 560px; max-height: 88vh; display: flex; flex-direction: column; box-shadow: var(--shadow-3); }
/* `UI.dialog({ wide: true })` —— 默认 560px 放不下两栏表单（后台「提示词详情 / 编辑」），
   窄屏回落用 `min(920px, 100%)`：小屏不会溢出，大屏也不至于拉得过宽。 */
.modal-box.is-wide { max-width: min(920px, 100%); }
.modal-box:focus { outline: none; }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px; border-bottom: 1px solid var(--line-2); }
.modal-head h3 { font-size: var(--fs-title-sm); font-weight: 600; }
.modal-body { padding: 18px; overflow: auto; }
.modal-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 14px 18px; border-top: 1px solid var(--line-2); }

/* ---------- 首页：轮播横幅（2026-09-23 改成站酷式大图） ----------
   高度 = 原来（约 100px）加高 50% → 150px，用户定的，写在这里的 --hero-h 上。
   · 有底图（.has-img）：图铺满 + 左侧压暗渐变 + 白字叠上去，指示器和箭头都压在图上；
   · 没底图：退回原来的「白色卡片 + 深色字 + 右下角品牌光晕」。
   ⚠ 底图是 object-fit: cover，上下一定会被裁 —— 换图时把重点放在垂直中间那一带。 */
.hero { padding: 30px 0 0; }
.hero-card {
  --hero-h: 150px;
  background: var(--panel); border-radius: var(--radius-lg); padding: 28px clamp(20px, 2.4vw, 34px);
  position: relative; overflow: hidden; box-shadow: var(--shadow-1);
}
.hero-card.has-img { height: var(--hero-h); padding: 0; background: var(--ink); }
.hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.hero-card.has-img::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, rgba(0, 0, 0, .82) 0%, rgba(0, 0, 0, .60) 38%, rgba(0, 0, 0, .18) 72%, rgba(0, 0, 0, .04) 100%);
}
/* 没底图时右下角那团品牌色光晕（有图时被上面的渐变取代，别一起显示） */
.hero-card:not(.has-img)::after {
  content: ''; position: absolute; right: -70px; top: -80px; width: 260px; height: 260px; border-radius: var(--radius-circle);
  background: radial-gradient(circle, rgba(0, 0, 0, .10), rgba(0, 0, 0, 0) 70%);
  pointer-events: none;
}
.hero-inner {
  position: relative; z-index: 2; height: 100%;
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 clamp(20px, 2.4vw, 34px);
}
/* ⚠ 左右各留 ≥ 52px：箭头是绝对定位压在图上（left 12 + 宽 34 = 46），
   文字 padding 给小了标题就会被箭头压住 —— 有断言卡着这条，别随手改小。 */
.hero-card.has-img .hero-inner { padding: 0 clamp(56px, 4vw, 64px); }
.hero-card h2 { font-size: var(--fs-hero); font-weight: 600; margin-bottom: 8px; position: relative; }
.hero-card p { font-size: var(--fs-sm); color: var(--ink-2); position: relative; }
.hero-card.has-img h2 { color: var(--ink-invert); margin-bottom: 6px; text-shadow: 0 2px 16px rgba(0, 0, 0, .5); }
.hero-card.has-img p { color: rgba(255, 255, 255, .86); text-shadow: 0 1px 12px rgba(0, 0, 0, .5); }
/* ⭐ 2026-09-25 指示条改成「44×44 的透明按钮 + 内部 4px 细条」。
   原来按钮本身就是 24×4px，手机上根本点不中（低于 WCAG 2.2 AA 的 24×24 最低线）。
   视觉零变化：细条由 ::before 画，按钮本体完全透明。
   margin: 0 -7px 让相邻按钮叠掉 14px —— 细条的**视觉间距仍是 6px**（44 − 14 = 30 的节距）。 */
.hero-dots { display: flex; gap: 0; margin-top: 18px; position: relative; }
.hero-dots button {
  width: 44px; height: 44px; margin: 0 -7px; padding: 0; border: 0; background: transparent;
  display: grid; place-items: center; cursor: pointer; border-radius: var(--radius-pill);
}
.hero-dots button::before {
  content: ''; display: block; width: 24px; height: 4px; border-radius: var(--radius-xs);
  background: var(--line); transition: background .3s var(--ease), width .3s var(--ease);
}
.hero-dots button.is-on::before { background: var(--brand); width: 32px; }
/* 有图时：指示条挪到右下角、换成白色（压在图上） */
.hero-card.has-img .hero-dots { position: absolute; right: clamp(20px, 2.4vw, 34px); bottom: 6px; margin: 0; z-index: 3; }
.hero-card.has-img .hero-dots button::before { background: rgba(255, 255, 255, .42); }
.hero-card.has-img .hero-dots button.is-on::before { background: var(--ink-invert); }
/* 左右切换箭头：常显但半透明（手机上没有悬浮，藏起来就点不着了） */
.hero-arrow {
  position: absolute; top: 50%; z-index: 3; transform: translateY(-50%);
  width: 34px; height: 34px; padding: 0; border-radius: var(--radius-circle);
  display: grid; place-items: center; cursor: pointer; color: var(--ink-invert);
  background: rgba(0, 0, 0, .40); border: 1px solid rgba(255, 255, 255, .26);
  backdrop-filter: blur(6px);
  opacity: .86;
  transition: opacity .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
/* ⭐ 2026-09-25 命中区扩到 44×44：圆还是 34px，外面多一圈 -5px 的透明内衬。
   ⚠ 元素自身的 getBoundingClientRect 仍是 34×34（所以设计量尺量到的是 34），真可点区域是 44。
     别把 width/height 直接改成 44 —— 那会把文字区挤掉，冒烟的「标题不被箭头压住」会红。 */
.hero-arrow::before { content: ''; position: absolute; inset: -5px; border-radius: var(--radius-circle); }
.hero-arrow:hover { opacity: 1; background: rgba(0, 0, 0, .60); }
.hero-arrow:active { transform: translateY(-50%) scale(.94); }
.hero-arrow.prev { left: 12px; }
.hero-arrow.next { right: 12px; }
.hero-card.is-link { cursor: pointer; }

/* ---------- 首页：一级 + 二级菜单（两层共用一块吸顶玻璃） ---------- */
.cat-wrap {
  position: sticky; top: var(--topbar-h); z-index: 50;
  margin: 18px calc(-1 * var(--pad)) 0;
  padding: 0 var(--pad);
  background: rgba(246, 246, 246, .92);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--line);
}
.cat-scroll {
  display: flex; gap: 4px; overflow-x: auto; overflow-y: hidden;
  padding: 9px 0; scrollbar-width: none; -ms-overflow-style: none; scroll-behavior: smooth;
}
.cat-scroll::-webkit-scrollbar { display: none; }
.group-item {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  padding: 11px 14px; border: 0; background: none; border-radius: var(--radius-pill);
  color: var(--ink-2); font-size: var(--fs-sm); white-space: nowrap; text-align: left;
  transition: background .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
.group-item:hover { background: var(--hover-1); color: var(--ink); }
.group-item:active { transform: scale(.96); }
.group-item.is-active { background: var(--brand); color: var(--ink-invert); }
.g-name { font-size: var(--fs-sm); }

/* 二级场景：点了一级方向才出现 */
.sub-bar { border-top: 1px solid var(--line-2); }
.sub-scroll {
  display: flex; gap: 6px; overflow-x: auto; overflow-y: hidden;
  padding: 8px 0; scrollbar-width: none; -ms-overflow-style: none; scroll-behavior: smooth;
}
.sub-scroll::-webkit-scrollbar { display: none; }
.sub-item {
  flex: none; padding: 12px 12px; border: 0; border-radius: var(--radius-pill); white-space: nowrap;
  background: var(--hover-1); color: var(--ink-2); font-size: var(--fs-xs);
  font-family: inherit; cursor: pointer;
  transition: background .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
.sub-item:hover { background: var(--hover-2); color: var(--ink); }
.sub-item:active { transform: scale(.96); }
.sub-item.is-on { background: var(--brand); color: var(--ink-invert); }
.sub-item.is-on:hover { background: var(--brand-dark); color: var(--ink-invert); }
.empty-act { margin-top: 14px; }

/* ---------- 页头（文章 / 投稿 / 我的收藏 共用） ---------- */
/* .list-head / .list-tools 已删（2026-09-23）：搜索框 +「随机看一个」整排挪进顶栏，
   样式见上面的 .topbar-search。首页原来那一排整块没了，卡片区直接接在分类条下面
   （中间那点间距改由 .grid 的 margin-top 提供）。
   ⭐ 2026-09-29 同日第十一轮：用户要求「统一文章页 / 模板标签页 / 投稿页的标题大小与位置」。
   实测三页原本是**两套**：文章与投稿吃 var(--fs-h1)（27px）、模板标签吃 var(--fs-h1-lg)（32px），
   而投稿页的 <main> 又漏了 .page-top（顶部留白 0 而不是 24px）→ 三页看着"错位"。
   现在统一：字号一律 var(--fs-h1)，上下留白一律由这里 + .page-top 给。
   ⭐ 同日第十五轮：用户进一步要求**全站**统一 → 页标题从 5 档收成这一档，
      .set-head h1 / .art-title / .scene-head h1 / .d-title 全部改指 var(--fs-h1)，
      --fs-h1-lg 已删。
   ⚠️ 上下留白只留 padding-top 这一道（配合 .page-top 的 24px）；padding-bottom 交给
      下面那条 .page-head 与 .tabs.app 的 margin-top 对齐，别在这里再叠一层。 */
.page-head { min-width: 0; padding: 0 0 14px; }
/* ⭐⭐ 页标题的**完整规格**（2026-09-29 同日第十五轮定，六处必须一字不差）：
     字号 var(--fs-h1) · 字重 600 · 行高 1.3 · 字间距 var(--ls-tight)
   ⚠️ 之前这四项在六个类里各写各的：字重 600/650 两种、行高 1.3/1.32/1.4 三种、
      字间距 normal/-0.01em 两种 —— 单看一页没问题，两页并排就是"哪里怪怪的"。
   ⚠️ 要调这一档**只改 --fs-h1 令牌**，别去逐个类改数值。 */
.page-head h1 { font-size: var(--fs-h1); font-weight: 600; line-height: 1.3; letter-spacing: var(--ls-tight); }
.page-head p { color: var(--ink-3); font-size: var(--fs-xs); margin-top: 5px; }
.search { position: relative; }
.search input {
  width: 100%; border: 1px solid transparent; border-radius: var(--radius-pill);
  padding: 12px 16px; font: inherit; font-size: var(--fs-sm);
  background: var(--field-bg); color: var(--ink);
  transition: background .25s var(--ease), border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.search input::placeholder { color: var(--ink-3); }
.search input:focus {
  outline: none; background: var(--panel); border-color: var(--brand);
  box-shadow: var(--shadow-focus);
}

/* ---------- 首页：大卡片网格（桌面每行五张） ---------- */
/* 瀑布流：外层按断点定列数，每列一个 .m-col，卡片在列内竖排、高度随图片比例 */
.grid {
  /* 分类条到卡片之间的这一截：原来是 .list-head 的 padding 撑的，
     2026-09-23 那排挪进顶栏之后得自己补，不然卡片贴着分类条。 */
  margin-top: 20px;
  display: grid; gap: clamp(14px, 1.4vw, 22px);
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: start;
  padding-bottom: 60px;
}
.m-col {
  display: flex; flex-direction: column; min-width: 0;
  gap: clamp(14px, 1.4vw, 22px);
}
.card {
  background: var(--panel); border-radius: var(--radius-lg); overflow: hidden;
  /* ⚠ 2026-09-25 加 1px 极浅描边：白卡(#FFF,255) 与页面底(#F4F4F4,244) 只差 4%，卡片默认又无阴影
     （用户要求），于是在暗屏 / 强光 / 缩图下卡片边界会「消失」。描边不是阴影，不违反那条要求。
     ⚠ pages.css 的 .p-card 是同一套，两处要一起改，改一处会不一致。 */
  border: 1px solid var(--line-soft);
  display: flex; flex-direction: column;   /* 默认无阴影，悬浮时才浮起来 */
  transition: transform .32s var(--ease), box-shadow .32s var(--ease);
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }
.card:active { transform: translateY(-1px) scale(.995); }
/* 有图的卡片：封面按图片自己的比例撑开（不再裁成 16:10），卡片因此高低错落 */
.card-cover {
  display: grid; place-items: center; position: relative;
  background: var(--surface-3); overflow: hidden; min-height: 120px;
}
/* 没图的卡片（兜底场景卡、没传图的投稿）：给个固定比例，别塌成一条 */
.card-cover.is-word { aspect-ratio: 16 / 10; }
.card-cover img { width: 100%; height: auto; display: block; transition: transform .5s var(--ease); }
.card:hover .card-cover img { transform: scale(1.04); }
.card-cover.is-locked {
  display: grid; place-items: center; color: var(--ink-3); font-size: var(--fs-xs);
  background: repeating-linear-gradient(45deg, var(--surface-2), var(--surface-2) 8px, var(--line-2) 8px, var(--line-2) 16px);
}
.cover-word {
  font-size: var(--fs-cover); font-weight: 600; line-height: 1;
  color: rgba(0, 0, 0, .15); user-select: none;
  transition: transform .45s var(--ease), color .45s var(--ease);
}
.card:hover .cover-word { transform: scale(1.07); color: rgba(0, 0, 0, .22); }
.card-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.card-title {
  /* 2026-09-24 用户要求「主页面卡片上的标题字体放大 2 号」：15 → 17px（手机端 14 → 16px，
     在下面 ≤600px 段里）。**这里就是那个数字**，别再散落到别处。 */
  font-size: var(--fs-md); font-weight: 600;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.card-desc {
  font-size: var(--fs-xs); color: var(--ink-3); line-height: 1.55; flex: 1;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.card-foot:last-child { margin-top: auto; }
/* 卡片上的风格标签（浅蓝药丸）与收藏/复制计数（图标是内联 SVG，不是 emoji 字符） */
.card-tags { display: flex; flex-wrap: wrap; gap: 5px; }
.tag-style {
  font-size: var(--fs-2xs); color: var(--brand); background: var(--brand-soft);
  border-radius: var(--radius-pill); padding: 2px 9px; white-space: nowrap;
}
/* 第三行：左边风格标签、右边收藏/复制 —— 统计靠右贴边。
   这一行允许换行：卡片窄（手机两列）时统计会掉到第二行，但 margin-left:auto 保证它始终靠右。 */
.card-metas { display: flex; align-items: center; gap: 8px; row-gap: 6px; flex-wrap: wrap; }
.card-tags { flex: none; max-width: 100%; }
.card-stats { flex: none; margin-left: auto; display: flex; align-items: center; gap: 10px; }
/* ⚠ 卡片上的收藏 / 复制一律用 .stat-mini，**不要用 .stat**：
   后台数据看板里另有一个 .stat（padding:15px + 边框的方块卡片），同名会互相污染 ——
   卡上会莫名多出边框和 30px 内边距（宽高翻倍），把风格标签挤到下一行。 */
.stat-mini { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; color: var(--ink-3); }
.stat-mini .i { flex: none; opacity: .82; }
.stat-mini b { font-weight: 600; color: var(--ink-2); font-variant-numeric: tabular-nums; }
/* 卡上的收藏 / 复制要"缩小"：图标 14 → 12，数字 12.5 → 11.5 */
.card-stats .stat-mini, .p-stats .stat-mini { font-size: var(--fs-2xs); }
.card-stats .stat-mini b, .p-stats .stat-mini b { font-size: var(--fs-2xs); }
.card-stats .i, .p-stats .i { width: 12px; height: 12px; }
/* 最后一行：头像 + 昵称 + 已上传时长（贴到卡片底部） */
.card-by { display: flex; align-items: center; gap: 6px; min-width: 0; margin-top: auto; font-size: var(--fs-2xs); color: var(--ink-3); }
.card-by .avatar { width: 20px; height: 20px; font-size: var(--fs-2xs); }
.card-by .by-name { color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-by .by-time { flex: none; }

/* 投稿页：图片风格标签选择器（预设点选 + 自己写一个） */
.tag-pick { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 9px; }
.tag-pick-item {
  font: inherit; font-size: var(--fs-xs); color: var(--ink-2); background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 9px 12px; cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.tag-pick-item:hover { border-color: var(--brand); color: var(--brand); }
.tag-pick-item.is-on { background: var(--brand); border-color: var(--brand); color: var(--ink-invert); }
.tag-add { display: flex; gap: 8px; }
.tag-add .input { flex: 1; min-width: 0; }
.tag-chosen { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 9px; }
.chosen-title { font-size: var(--fs-xs); color: var(--ink-3); }
.tag-chosen-item {
  font: inherit; font-size: var(--fs-xs); color: var(--brand); background: var(--brand-soft);
  border: 0; border-radius: var(--radius-pill); padding: 9px 12px; cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.tag-chosen-item:hover { background: var(--line-2); color: var(--ink); text-decoration: line-through; }

/* 投稿页：参考图 / 生成图 两个上传位（并排，窄屏自动叠起来） */
.up-slots { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.up-slot { min-width: 0; }
.up-slot-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: var(--fs-xs); }
.up-slot-head b { font-weight: 600; }
.up-slot .drop { min-height: 52px; display: grid; place-items: center; padding: 12px; }
.up-slot .drop.is-full { cursor: not-allowed; color: var(--ink-3); border-color: var(--line-2); background: var(--surface-3); }
/* 缩略图横排；一张都没有时整块不占高度 */
.thumb-box { display: flex; flex-wrap: wrap; gap: 10px; }
.thumb-box:empty { display: none; }
.up-slot .thumb img { max-height: 96px; }
.pill { font-size: var(--fs-2xs); color: var(--ink-3); white-space: nowrap; }
.author { display: flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--ink-3); min-width: 0; }
.author .avatar { width: 20px; height: 20px; font-size: var(--fs-2xs); }
.author span, .author { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 搜索命中：黑白灰里没有黄色可用 → 反白（黑底白字），是这套配色里最抓眼的强调 */
mark { background: var(--ink); color: var(--ink-invert); border-radius: var(--radius-xs); padding: 0 2px; }
.foot { border-top: 1px solid var(--line); margin-top: 20px; padding: 22px 0 40px; color: var(--ink-3); font-size: var(--fs-xs); }
/* 页脚备案号（2026-09-30 加）：法规要求的合规信息，**单独一行**放在 AI 标识下面。
   ⚠️ 公众页只有黑白灰 → 链接跟随页脚颜色（不做成蓝色），悬停才加下划线提示可点。 */
.foot .icp-line { margin-top: 6px; }
.foot .icp-line a { color: inherit; text-decoration: none; }
.foot .icp-line a:hover { text-decoration: underline; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .card, .btn, .group-item, .sub-item, .brand-logo, .g-scene, .g-sib { transition: none; }
  .card:hover, .card:hover .cover-word, .card:hover .card-cover img { transform: none; }
  /* ⭐ 2026-09-29 同日第四轮：⑨ 段"全站卡片统一动效"新增的类名一并关掉。
     ⚠️ 加动效必须同步改这一段（站上硬规矩）—— 漏了的话，开了"减少动效"的用户
        照样会看到几十张卡一起上浮，而那正是这个系统设置要避免的事。
     ⚠️ 这里用 `:hover` 逐个列：不列的话上面 reduce 段里的 `transform: none`
        会被 ⑨ 段更靠后的规则盖掉（特异性相同、后者胜）。 */
  .p-card, .a-card, .art-card, .auth-card, .set-card, .work-card, .my-item,
  .t-card, .t-ex, .t-tpl, .g-tpl, .d-tpl, .rs-row, .model-row, .set-row,
  .panel, .stat, .gd-edit, .gd-side, .t-box, .t-tagwrap { transition: none; }
  .p-card:hover, .a-card:hover, .art-card:hover, .auth-card:hover, .set-card:hover,
  .work-card:hover, .my-item:hover, .t-card:hover, .t-ex:hover, .t-tpl:hover,
  .g-tpl:hover, .d-tpl:hover, .rs-row:hover, .model-row:hover, .set-row:hover,
  .panel:hover, .stat:hover, .gd-edit:hover, .gd-side:hover, .t-box:hover {
    transform: none; box-shadow: none;
  }
  .t-card, .t-ex, .t-tpl, .a-card, .art-card, .work-card { animation: none !important; }
  /* 动效层（2026-09-23）新增的一并关掉，并且**强制 opacity: 1** ——
     否则「等图时透明」的写法在动画禁用后会留下看不见的图。
     （顶部进度条不在此列：它是位置指示，不是装饰。） */
  .card, .p-card, .panel, .stat, main, .card-cover.is-wait, .p-media.is-wait,
  .skel-cover,
  .card-cover img, .p-media img, .thumb img, .d-media img, .hero-bg, .avatar-img {
    animation: none !important; transition: none !important; opacity: 1 !important; transform: none !important;
  }
}

/* ---------- 响应式（列数断点递减） ---------- */
@media (max-width: 1180px) { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* ⭐ 2026-09-25 加：超宽屏给第 6 列。原来 1800px 以上仍是 5 列，2560px 时卡片宽到 485px
   （17px 的标题在 485px 里显得空）。加列而不给 --wrap 设上限，是为了不破坏「全屏铺满」。 */
@media (min-width: 1800px) { .grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 940px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px) {
  :root { --topbar-h: 52px; }
  .topbar-inner { gap: 10px; }
  .mode-chip { display: none; }
  .u-name { display: none; }
  .row-2 { grid-template-columns: minmax(0, 1fr); }
  .hero { padding-top: 18px; }
  .hero-card { padding: 20px; }
  .hero-card.has-img { --hero-h: 132px; }
  .hero-card.has-img .hero-inner { padding: 0 46px; }
  .hero-card.has-img h2 { font-size: var(--fs-lg); }
  .hero-card.has-img p { font-size: var(--fs-xs); }
  .hero-arrow { width: 30px; height: 30px; }
  /* ⚠️ 窄屏箭头更小（30px），沿用桌面的 -5px 内衬只有 40px —— 差 4px 到 44。
     这里单独补到 -7px（30+14=44）。箭头在轮播图上、四周没有别的可点元素，横向也能一起扩。 */
  .hero-arrow::before { inset: -7px; }
  .hero-arrow.prev { left: 8px; }
  .hero-arrow.next { right: 8px; }
  .cat-wrap { margin-top: 14px; }
  .grid { margin-top: 16px; }
  /* 顶栏搜索区在窄屏放不进一行 → 整个换到第二行，顶栏变成两行高。
     ⚠ .topbar-inner 用 height: auto（不是 min-height: var(--topbar-h)）—— 否则会跟
     syncTopbarH() 写回来的高度互相喂，成环。分类条 sticky 靠 --topbar-h 对齐，改这里要一起想。 */
  .topbar-inner.has-search {
    height: auto; flex-wrap: wrap;
    padding-top: 10px; padding-bottom: 10px;
  }
  /* 搜索区 flex-basis 100% 会把「用户区」（本机模式 + 登录/注册）挤到第三行去 ——
     390px 下顶栏变三行高，白占近 1/5 屏（2026-09-23 看截图发现的）。
     用 order 把品牌 / 导航 / 用户区都排到搜索区前面：第一行「品牌 导航 … 用户区」，
     第二行搜索区，顶栏稳稳两行。只在首页（.has-search）且窄屏下生效，别的页面一个像素不变。 */
  .topbar-inner.has-search .brand { order: -2; }
  .topbar-inner.has-search .nav { order: -2; }
  .topbar-inner.has-search .topbar-right { order: -1; }
  /* max-width 不覆盖：继承上面的 560px —— 900px 断点换行时免得搜索框拉成一条长杠 */
  .topbar-search { flex: 1 0 100%; margin-top: 10px; }
  .toast-box { max-width: 92vw; }
}
@media (max-width: 700px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) {
  .card-body { padding: 11px 12px 13px; gap: 6px; }
  .card-title { font-size: var(--fs-title-sm); }
  .card-desc { font-size: var(--fs-2xs); }
  .btn-sm { padding: 7px 12px; }

  /* ---------- 顶栏四个导航项的窄屏补救（2026-09-24 起，2026-09-28 加第四项后重调）----------
     背景：顶栏从三项涨到四项「首页 / 文章 / 提示词怎么写 / 投稿」，第四项是 2026-09-28
     用户要求加的（原话「把文章移动到最上面，在首页标签的后面，提示词怎么写页面的前面」）。
     不处理的话：内页会把「投稿」压出视野（**不报错、不出滚动条**，只有量像素才看得见；
     375px 超 8px、360px 超 23px，topbar-metrics 实测）。
     下面几段是配套的，改任何一段都要重跑 `node tools/topbar-metrics.mjs`（三种手机宽度）：
       ① 字号与内边距收一档（导航 12px / 4px、间隙 0）—— 第四项就是靠这个塞进去的
       ② 站标图收一档（见下面的 .brand-logo）—— 这是新加的一条，理由写在它上面。
       ③ 首页顶栏重排：第一行「品牌 + 导航 + 用户区」，第二行搜索框整行。
          导航不再跟搜索框抢第一行，实测顶栏反而从 116px 降到 101px（第一行还余十几 px）。
       ④ 内页没有搜索区可让位 → 导航吃剩余宽度，放不下时可就地横滑（360px 级小屏的兜底）。 */
  .topbar-inner { gap: 5px; }
  /* ⭐ 2026-09-25 纵向 8 → 13px：导航项高度 35 → 45（拇指友好）；横向不动。
     ⚠ 非首页的 .topbar-inner 是 height: var(--topbar-h)(52px)，45 放得下。
     ⭐ 2026-09-28 横向 6 → 4px：四项了，390px 那档只剩 1px 余量（实测导航条内部溢出 1px），
        不收这一档 375px 就是一打开就挤出去。改这一条**必须**重跑 topbar-metrics。 */
  .nav a { padding: 13px 4px; font-size: var(--fs-2xs); }
  /* ⭐ 2026-09-28：四项之间不再留空隙（原来 2px × 3 ＝ 6px）——
     390px 那档的总余量本来就只有个位数，这 6px 直接决定 375px 过不过。 */
  .nav { gap: 0; }
  /* ⚠ 2026-09-24 第二轮加：站标换成整张 logo 图之后，窄屏反而变紧了 ——
     旧写法在窄屏显示「30px 方块 + 3 字简称」≈ 79px 宽，
     而这张图 30px 高就是 97px（图里自带「知未言」，换不掉也删不掉）。
     360px 那档本来只剩 7px 余量，多出这 18px 直接把「投稿」挤出容器（topbar-metrics 抓到）。
     收到 23px 高（宽约 74px）刚好把余量补回来。改这一条必须重跑 topbar-metrics。 */
  .brand-logo { height: 23px; }

  .topbar-inner.has-search { gap: 5px 8px; }
  /* ⚠ 这里**不要**再写 brand / nav / topbar-right 的 order —— 900px 断点那一套
     （品牌 -2 → 导航 -2 → 用户区 -1，用户区靠 margin-left:auto 顶到最右）已经是想要的顺序。
     600px 这层如果另写一份 order（曾写成 品牌 -3 / 用户区 -2 / 导航 -1），
     结果是把「登录 / 注册」「头像+管」夹在品牌和导航中间，看着像导航项 —— 实测过。 */
  /* 只改搜索框：900px 那层是 flex-basis:100%（一定独占一行），这里收成 140px ——
     第一行装不下就整行落到第二行（390px 的实际结果），装得下（600px 级宽屏）就还在一行。 */
  .topbar-inner.has-search .topbar-search {
    order: 0; flex: 1 1 140px; min-width: 0; margin-top: 0;
  }

  .topbar-inner:not(.has-search) .nav {
    flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; overflow-x: auto;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .topbar-inner:not(.has-search) .nav::-webkit-scrollbar { display: none; }
  /* 导航变成滚动容器后，溢出方向的下划线会被裁掉（overflow:auto 会把另一个方向也算成 auto）→ 收进容器内 */
  .topbar-inner:not(.has-search) .nav a.is-active::after { bottom: 0; }
  /* 管理员登录后导航会多出第 5 项「后台管理」—— 五项 + 搜索框在 390px 一行放不下，
     首页顶栏会被顶成**三行**（实测）。窄屏把它收起来：管理员在右上角头像菜单里一样能进后台，
     手机上顶栏就固定是四项、布局不随登录态变化。
     ⚠ 也就是说：窄屏所有人看到**可见**导航项都是 4 项（DOM 里有 5 个 a），别再按 5 项写断言。 */
  .topbar-inner .nav a[href="admin.html"] { display: none; }
}
@media (max-width: 380px) {
  /* 小屏再省一档，把第四条「投稿」也完整露出来（390px 及以上的手机用不到这一档）。
     ⚠ 2026-09-28：顶栏涨到四项后，这一档从「省 12px」变成「省 28px」才够 ——
        横向内边距 5 → 3px（四项省 16px）+ 站标 23 → 20px（省约 6px）。改这一条必须重跑 topbar-metrics。 */
  .nav a { padding: 13px 3px; }
  .brand-logo { height: 20px; }
}

/* ---------- 加载更多 ---------- */
/* 列表页一次最多 window.LIST_PAGE 张（25），还有没加载完的就在卡片下面给这个按钮 */
.more-box { display: flex; justify-content: center; padding: 26px 0 4px; }
.more-box:empty { display: none; }

/* ---------- 回到顶部（右下角浮标，由 UI.mountToTop 挂上去） ---------- */
.to-top {
  position: fixed; right: 26px; bottom: 26px; z-index: 70;
  width: 44px; height: 44px; border-radius: var(--radius-circle);
  display: flex; align-items: center; justify-content: center;
  background: var(--panel); color: var(--ink-2);
  border: 1px solid var(--line); box-shadow: var(--shadow-2);
  cursor: pointer;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), color .2s var(--ease);
}
.to-top:hover { color: var(--brand); transform: translateY(-2px); }
.to-top:active { transform: scale(.94); }
@media (max-width: 700px) { .to-top { right: 16px; bottom: 18px; width: 44px; height: 44px; } }

/* ==========================================================================
   动效层（2026-09-23 新增，用户选的「精致克制」档）
   三条自律：
   · 只动 transform / opacity / box-shadow —— 不碰 width/height/top，不触发重排；
   · 时长 220~600ms、缓动统一 var(--ease)，卡片错峰步长 32ms（前 14 张，往后归零）；
   · 全部在 prefers-reduced-motion: reduce 下关掉（见上面那段媒体查询）。
   ========================================================================== */

/* ---- ① 顶部滚动进度条：一条 2px 黑线，随阅读进度长出来 ---- */
.scroll-progress {
  position: fixed; left: 0; top: 0; z-index: 300;
  width: 100%; height: 2px; background: var(--ink);
  transform: scaleX(0); transform-origin: 0 50%;   /* 用 scaleX 而不是 width —— 不触发布局 */
  opacity: 0; pointer-events: none;
  transition: opacity .3s var(--ease);
}

/* ---- ② 卡片入场：逐张错峰淡入上浮（JS 给每张卡注入 --i = 序号） ---- */
@keyframes rise-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.card, .p-card { animation: rise-in .46s var(--ease) both; }
.card, .p-card { animation-delay: calc(var(--i, 0) * 32ms); }

/* ---- ③ 封面骨架微光 + 图片淡入 ----
   骨架只在「等图」期间闪（容器上由 JS 打 .is-wait），图到位换成 .has-pic 停掉。
   没图的那两种封面（.is-word 首字兜底 / .is-locked 游客态）自带样式，不会被卷进来。 */
@keyframes sheen { from { background-position: 140% 0; } to { background-position: -60% 0; } }
.card-cover.is-wait, .p-media.is-wait {
  background: linear-gradient(100deg, var(--line-2) 32%, var(--surface-2) 50%, var(--line-2) 68%);
  background-size: 220% 100%;
  animation: sheen 1.6s linear infinite;
}
.card-cover.has-pic, .p-media.has-pic { background: var(--line-2); animation: none; }
/* 图片淡入：基础态是「看得见」，等图时才透明 —— JS 挂了也不会整站没图（fail-safe） */
.card-cover img, .p-media img, .thumb img, .d-media img, .hero-bg, .avatar-img {
  transition: transform .5s var(--ease), opacity .5s var(--ease);
}
.card-cover img.is-loading, .p-media img.is-loading, .thumb img.is-loading,
.d-media img.is-loading, .hero-bg.is-loading, .avatar-img.is-loading { opacity: 0; }

/* ---- ④ 区块滚动渐显（纯 CSS，零 JS） ----
   animation-timeline: view() 是 Chrome 115+ 的能力，老浏览器整段不生效 → 元素静态显示，不影响可用性。
   所以用 @supports 包住：不支持就完全没有动画，而不是「动画失效后卡在透明态」。 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .panel, .stat {
      animation: rise-in .62s var(--ease) both;
      animation-timeline: view();
      animation-range: entry 0% entry 46%;
    }
  }
}

/* ---- ⑤ 页面进入淡入：别让整页硬闪一下 ---- */
@keyframes page-in { from { opacity: 0; } to { opacity: 1; } }
main { animation: page-in .34s var(--ease) both; }

/* ---- ⑥ 按钮悬浮微抬（配合已有的按下 scale） ---- */
.btn:hover { transform: translateY(-1px); }
.btn-primary { box-shadow: var(--shadow-btn); }
.btn-primary:hover { box-shadow: var(--shadow-btn-hover); }

/* ---- ⚠️ 这里原来是 ⑦ 后台条形图的 `bar-grow` 动画 + `.bar-fill` ----
   2026-09-28 轮次 Z 用户要求「分布内容以数字展示就行」，条形图整块换成了数字表格，
   这一整套动画跟着删了（连 `prefers-reduced-motion` 白名单里那一条 `.bar-fill` 一起）。
   **别再往回加** —— 要展示分布就用 `pages.css` 的 `.dist-*`。 */

/* ---- ⑨ 全站卡片统一交互动效（2026-09-29 同日第四轮）----
   用户原话：「网站所有的卡片都做首页卡片一样的交互动画效果」（追问确认范围 = 所有方框）。
   「首页卡片那套」拆开就是三件事，这里**原样复用同一套数值**：
     · 悬浮上浮 4px + 阴影浮起  .card:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }
     · 按下回弹  translateY(-1px) scale(.995)
     · 入场错峰淡入上浮 rise-in .46s + 每张 32ms 步长（--i 由 JS 注入）
   ⚠️ 为什么用**选择器清单**而不是挨个类写：清单一处，以后新增卡片把类名加进来就行；
      挨个写的话下次加卡片一定会漏（漏了不报错，只是"那张卡不动"）。
   ⚠️ 只动 transform / box-shadow / opacity —— 三条自律之一，不碰 width/height/top。
   ⚠️ 清单里**不许**放：输入框、按钮、页签、菜单项、行内小标签（.t-chip / .pill 这类）。
      它们自己已经有按下反馈，再加"上浮 4px"会让一屏里几十个小方块一起抖，非常吵。
   ⚠️ 后台面板 .panel 也在清单里：它面积大，上浮 4px 视觉上很晃 —— 所以后台那几条
      单独降成 2px（见下面 .admin-page 那一段）。 */
/* 清单：公众页 + 后台，凡是"一块方框装内容"的都收进来 */
.card, .p-card, .a-card, .art-card, .auth-card, .set-card, .work-card, .my-item,
.t-card, .t-ex, .t-tpl, .g-tpl, .d-tpl, .rs-row, .model-row, .set-row,
.panel, .stat, .skel, .gd-edit, .gd-side, .t-box {
  transition: transform .32s var(--ease), box-shadow .32s var(--ease);
}
.card:hover, .p-card:hover, .a-card:hover, .art-card:hover, .auth-card:hover, .set-card:hover,
.work-card:hover, .my-item:hover,
.t-card:hover, .t-ex:hover, .t-tpl:hover, .g-tpl:hover, .d-tpl:hover,
.rs-row:hover, .model-row:hover, .set-row:hover,
.panel:hover, .stat:hover, .gd-edit:hover, .gd-side:hover, .t-box:hover {
  transform: translateY(-4px); box-shadow: var(--shadow-2);
}
.card:active, .p-card:active, .a-card:active, .art-card:active, .auth-card:active, .set-card:active,
.work-card:active, .my-item:active,
.t-card:active, .t-ex:active, .t-tpl:active, .g-tpl:active, .d-tpl:active,
.rs-row:active, .model-row:active, .set-row:active,
.panel:active, .stat:active, .gd-edit:active, .gd-side:active, .t-box:active {
  transform: translateY(-1px) scale(.995);
}
/* 入场错峰：与 .card 同一套 rise-in（--i 由 JS 注入，前 14 张）。
   ⚠️ 只给"列表里会出现很多个"的卡片类挂；一页只有一个的方框不挂
      （一页一个还错峰入场，用户看到的就是"加载完某一块才慢慢浮上来"，很怪）。
   ⚠️⚠️ .panel / .stat **故意不在这个清单里**：它们已经由上面
      `@supports (animation-timeline: view())` 那段做**滚动渐显**了。
      再挂一条 rise-in 就是两条 animation 抢同一个属性 —— 后者胜，但那是隐式的；
      而且不支持 animation-timeline 的老浏览器里只剩 rise-in，行为随浏览器而变。 */
.card, .t-card, .t-ex, .t-tpl, .a-card, .art-card, .work-card, .my-item, .rs-row, .model-row {
  animation: rise-in .46s var(--ease) both;
  animation-delay: calc(var(--i, 0) * 32ms);
}
/* 后台：面板面积大，位移降到 2px（4px 在大方块上晃得厉害）；
   ⚠️ 后台的 hover 反馈本来就靠 admin-theme.css 的彩色，这里只补"抬起来"这一下。 */
body.admin-page .panel:hover, body.admin-page .stat:hover,
body.admin-page .gd-edit:hover, body.admin-page .gd-side:hover { transform: translateY(-2px); }

/* ---- ⑧ 卡片悬浮：封面图轻微推近 ----
   两个前提缺一不可：
   · hover: hover —— 触屏没有真 hover，否则点一下会一直保持放大态；
   · min-width: 901px —— 窄屏卡片本来就小，再推近显得挤；
   · prefers-reduced-motion: no-preference —— **必须写**：上面那段 reduced-motion 里
     已经有 `.card:hover .card-cover img { transform: none; }`，跟这条特异性相同，
     而这条在文件更后面 → 不写这一句就会把它盖掉，减少动效的用户照样会看到缩放。 */
@media (hover: hover) and (min-width: 901px) and (prefers-reduced-motion: no-preference) {
  .card:hover .card-cover img, .p-card:hover .p-media img { transform: scale(1.035); }
}

