/* ============================================================
   光影精灵 — 明暗双主题（默认浅色）
   ============================================================
   2026-10-01 第二轮改版：主色由「金 #d4af6a」整套换成「蓝 #49b1f5」，
   页面底色由纯色改为紫蓝渐变 —— 对齐哥另一个站 ludawei.top 的 Hexo Butterfly。
   取色依据（都是实测值，不是看着像）：
     · Butterfly 的 canvas_nest 粒子色 = '73,177,245' → #49b1f5
     · Butterfly 的 #web_bg 主题色   = #49B1F5
     · Butterfly 的背景渐变          = linear-gradient(-225deg,#dfbacd 0%,#B8DCFF 48%,#58abf3 100%)

   变量表分两组：:root 存深色 token，html[data-theme="light"] 存浅色 token；
   页面默认由 index.html 预置脚本打上 data-theme="light"（哥指定默认浅色），
   只有 localStorage 存了 sd_theme=dark 才回深色。

   语义色板：所有组件只允许引用变量，禁止写死颜色 ——
   任何新颜色（含 rgba 家族）都必须走 --*-rgb 三元组或独立 token，
   否则在另一套主题下必然出现「看不清字」。

   为什么浅色的 --accent 不是 #49b1f5 而是 #1f74c9：
   #49b1f5 压白底只有 2.4:1（正文要 4.5:1），链接和按钮会明显发虚 ——
   与上一版「浅金 #d4af6a 在白底上看不见、必须加深到 #8f6b2a」同一个道理。
   深色主题则相反：底色够深，#49b1f5 有 8.3:1，直接用原色最还原 Butterfly。
   --shine 只做「装饰高光」（渐变细线、粒子光斑），不承担文字对比度，可放开亮度。
   ============================================================ */
:root {
  --bg: #0c1420;
  --bg-soft: #151d2b;
  --bg-card: #1a2331;
  --border: #263245;
  --border-strong: #35455e;
  --text: #e9eef5;
  --text-dim: #9aa8bb;
  /* hero（满屏首屏）专用次要文字色：它压的是「全站渐变」而不是卡片底色，
     浅色主题那条渐变很亮，全站的 --text-dim 在上面只有 4.0:1（见浅色段注释）。
     深色主题下渐变本身就暗，这里只比 --text-dim 再亮一档兜底。 */
  --hero-dim: #a8b6c9;
  --accent: #49b1f5;            /* Butterfly 原色，深底上 8.3:1 */
  --accent-rgb: 73, 177, 245;
  --accent-contrast: #08131f;   /* 放在蓝底上的文字/圆点 */
  --accent-soft: rgba(73, 177, 245, 0.14);
  --shine: #8ecbff;             /* 高光色：比 accent 更亮。粒子光斑取它的 --shine-rgb */
  --shine-rgb: 142, 203, 255;
  /* 页面底色渐变（全站）：深色版把浅色那条紫粉→淡蓝→蓝整体压暗降饱和，
     色相顺序保持不变，切换主题时观感是「同一张底片换曝光」而不是换了个站。 */
  --page-grad: linear-gradient(-225deg, #241338 0%, #17273f 48%, #0a1a2e 100%);
  /* 光晕色：首屏那层柔光，也是渐变装饰的取色源 */
  --glow-rgb: 73, 177, 245;
  /* 光影粒子背景的透明度（js/particles.js 读取；线取 --accent-rgb、点取 --shine-rgb）。
     放在变量里是为了深浅两套主题能各配一份，调力度不用改 JS。 */
  --particle-line: 0.55;
  --particle-dot: 0.9;
  --danger: #e06c6c;
  --danger-rgb: 224, 108, 108;
  --danger-strong: #ff7070;     /* 强调红：深底上要亮 */
  --danger-dim: #ffd6d6;        /* 红色警示框正文：深底上要浅 */
  --ok: #5fbf7f;
  --ok-rgb: 95, 191, 127;
  --info: #86b6e0;
  --info-rgb: 127, 168, 208;
  --neutral: #4a4e57;           /* 中性圆点/描边 */
  --neutral-text: #7f848f;      /* 中性文字（忙/暂满） */
  --neutral-rgb: 127, 132, 143;
  --placeholder: #5f646e;
  --ghost: rgba(255, 255, 255, 0.03);   /* 幽灵底：暗底微亮 / 亮底微暗 */
  --header-bg: rgba(12, 20, 32, 0.85);  /* sticky 页头毛玻璃底（对齐 --bg 的蓝调） */
  --shadow: rgba(0, 0, 0, 0.45);        /* 面板投影 */
  --radius: 8px;
  --maxw: 1280px;
  /* sticky 页头实测高度：34px 控件 + 上下各 14px padding + 1px 下边框。
     首屏 hero 用 calc(100vh - var(--header-h)) 算高度，让底边正好落在首屏下沿。
     手机上页头里是 40px 的汉堡按钮，断点里单独覆盖。 */
  --header-h: 63px;
  color-scheme: dark;   /* 原生控件（日期选择器/滚动条/表单）按深色渲染 */
}

/* ---------------- 浅色主题（默认）----------------
   2026-10-01 起底色不再是纯白，而是 Butterfly 那条紫蓝渐变（--page-grad）。
   卡片仍是纯白 —— 白卡片浮在浅紫蓝渐变上，正是 Butterfly 的层次。
   对比度基准：正文 ≥ 4.5:1（在渐变最亮的 #B8DCFF 处实测 10.3:1，很宽裕），
   链接/按钮压白卡片算，故 --accent 用 #1f74c9（4.8:1）而非 #49b1f5（2.4:1）。
   签名板保持白底、验证码保持纸白（本来就是「纸」的隐喻），不随主题翻。 */
html[data-theme="light"] {
  --bg: #eef4fb;                /* 渐变撑不住时的兜底纯色（取渐变中间调） */
  --bg-soft: #f2f5fa;
  --bg-card: #ffffff;
  --border: #dfe6f0;
  --border-strong: #c2cddd;
  --text: #26282d;
  --text-dim: #48505e;          /* 别调回 #5c6675 —— 压在渐变亮端（#b8dcff）只有 3.8:1，
                                   实测「还没有文章」这类空状态文字读不清。现值最差 4.66:1 */
  /* hero 专用：首屏满屏压在渐变上，且首屏背景图切成「只用渐变」时没有照片遮罩
     兜底 —— 实测那一点背后是 rgb(123,189,247)，--text-dim(#48505e) 只有 4.05:1。
     这里再深一档 → 4.69:1（对更亮的 #b8dcff 也有 6.16:1）。2026-10-01 对比度审计抓到。 */
  --hero-dim: #3f4652;
  --accent: #1f74c9;
  --accent-rgb: 31, 116, 201;
  --accent-contrast: #ffffff;
  --accent-soft: rgba(31, 116, 201, 0.10);
  --shine: #2f86d6;             /* 亮底上的高光必须更深：浅蓝在浅底上等于看不见 */
  --shine-rgb: 47, 134, 214;
  /* 页面底色渐变：与 ludawei.top 的 #web_bg 逐字节相同（-225deg 紫粉→淡蓝→蓝） */
  --page-grad: linear-gradient(-225deg, #dfbacd 0%, #b8dcff 48%, #58abf3 100%);
  --glow-rgb: 120, 170, 235;
  /* 粒子：浅底上连线要压淡（满屏深线显脏），光点保留存在感。
     底比上一版更暗（紫蓝渐变 vs 纯白），两档各上调一档。 */
  --particle-line: 0.5;
  --particle-dot: 0.85;
  --danger: #b23f3f;
  --danger-rgb: 178, 63, 63;
  --danger-strong: #a12f2f;
  --danger-dim: #6e2222;
  --ok: #2f7d4f;
  --ok-rgb: 47, 125, 79;
  --info: #35618f;
  --info-rgb: 53, 97, 143;
  --neutral: #a9a9b0;
  --neutral-text: #6f7076;
  --neutral-rgb: 111, 112, 118;
  --placeholder: #a4a4ab;
  --ghost: rgba(0, 0, 0, 0.03);
  --header-bg: rgba(255, 255, 255, 0.88);
  --shadow: rgba(0, 0, 0, 0.12);
  color-scheme: light;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", "Segoe UI", sans-serif;
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ---------------- 全站底色渐变层（2026-10-01）----------------
   紫蓝渐变垫在整站底下，对齐 ludawei.top 的 Butterfly。

   为什么是伪元素 + position:fixed，而不是给 <html> 直接设
   background-attachment:fixed：后者在 iOS Safari 上滚动重绘代价高、
   历史兼容也差；fixed 定位的独立层在所有浏览器都是一次合成，稳。
   为什么不干脆省掉 body 的纯色底：伪元素若因任何原因没渲染（极老浏览器、
   打印样式覆盖），纯色底还在，深色主题不会变成白底黑字。两道保险。

   层序（都在根层叠上下文，从下往上）：
     根画布层（body 的 --bg，会传播给 <html>）
       → body::before  (z-index:-2)  渐变
         → #bg-particles (z-index:-1) 粒子
           → 正文 / 卡片
   卡片是 --bg-card 不透明，所以渐变只在留白与缝隙露出来，不会糊到照片上。 */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -2;
  background: var(--page-grad);
  pointer-events: none;
  /* 同样提升为合成层：fixed 的全屏渐变层若不提升，滚动时也要参与重绘。
     增益比首屏照片小（省约 6%），但一行、零风险，顺手做掉。 */
  will-change: transform;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
img { display: block; max-width: 100%; }
button { font-family: inherit; cursor: pointer; }
input, textarea, select { font-family: inherit; }

/* ---------------- 顶部导航 ---------------- */
#site-header {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 28px;
  background: var(--header-bg);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.brand {
  font-size: 20px; font-weight: 700; letter-spacing: 2px; color: var(--text);
  display: inline-flex; align-items: center; gap: 9px;
}
.brand:hover { text-decoration: none; color: var(--accent); }
/* 页头标识：与站点图标同源的光圈造型，去掉深色方章底板（源见 assets/icons/brand-mark.svg）
   26px 是实测配比 —— 与 20px 字标并置时最稳，且这个尺寸下用主图参数比小图简化参数更精微 */
.brand-mark { display: block; width: 26px; height: 26px; flex: 0 0 auto; }
#site-nav { display: flex; align-items: center; gap: 18px; font-size: 14px; }
#site-nav a { color: var(--text-dim); }
#site-nav a:hover { color: var(--accent); text-decoration: none; }
#site-nav a.active { color: var(--text); }

/* ---------------- 主题切换按钮（2026-09-30 明暗双主题）----------------
   挂在页头右侧容器（.header-right = 导航 + 主题 + 汉堡）：手机上不开
   汉堡菜单也能一键切换。两个 SVG 图标按当前主题二选一显示 ——
   深色时显示太阳（点去浅色），浅色时显示月亮（点回深色）。 */
.header-right { display: flex; align-items: center; gap: 12px; }
#theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; flex: 0 0 auto;
  background: transparent; border: 1px solid var(--border); border-radius: 8px;
  color: var(--text-dim); padding: 0;
}
#theme-toggle:hover { border-color: var(--accent); color: var(--accent); }
#theme-toggle svg { width: 16px; height: 16px; display: none; }
#theme-toggle .icon-sun { display: block; }
html[data-theme="light"] #theme-toggle .icon-sun { display: none; }
html[data-theme="light"] #theme-toggle .icon-moon { display: block; }
#site-nav .nav-btn {
  background: transparent; border: 1px solid var(--border); color: var(--text-dim);
  padding: 6px 16px; border-radius: 999px; font-size: 13px;
}
#site-nav .nav-btn:hover { border-color: var(--accent); color: var(--accent); }
/* 中文导航在空间不足时会逐字断行（一个字一行竖排），任何宽度下都必须禁止 */
.brand, #site-nav a { white-space: nowrap; }

/* 页头未读角标（2026-09-28）：微信式红点数字，挂在「约拍记录」「留言板」上 */
#site-nav .nav-badge {
  display: inline-block;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  margin-left: 5px;
  border-radius: 999px;
  background: #e5484d;
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  vertical-align: 2px;
}

/* 汉堡按钮：桌面端隐藏；≤768px 显示（见响应式段）。
   三道杠，菜单展开时动画变 ✕ */
#nav-toggle {
  display: none;
  flex-direction: column; align-items: center; justify-content: center;
  gap: 5px; width: 40px; height: 40px; flex: 0 0 auto;
  background: transparent; border: 1px solid var(--border); border-radius: 8px;
  padding: 0;
}
#nav-toggle:hover { border-color: var(--accent); }
#nav-toggle .bar {
  display: block; width: 18px; height: 2px; border-radius: 2px;
  background: var(--text); transition: transform .22s ease, opacity .18s ease;
}
#site-header.nav-open #nav-toggle .bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
#site-header.nav-open #nav-toggle .bar:nth-child(2) { opacity: 0; }
#site-header.nav-open #nav-toggle .bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------------- 通用布局 ---------------- */
main { flex: 1; width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 24px 28px 64px; }

.page-title { font-size: 26px; font-weight: 600; margin-bottom: 4px; }
.page-sub { color: var(--text-dim); font-size: 14px; margin-bottom: 24px; }

/* ---------------- 首页 Hero（2026-10-01 改为满屏）----------------
   对齐 Butterfly 首页的信息节奏：首屏整屏只放站名与打字机副标题，
   作品集网格退到第二屏，往下滚才出现。

   布局三要点：
     · 由 body.home-view 放开 main 的 1280px 宽度限制（规则在下方）——
       否则「全屏背景图」只铺得到中间一条，两侧露底色；
     · 高度 = 视口高 − 页头高（--header-h 实测值），底边正好落在首屏下沿；
     · 遮罩与文字色分两态：有背景图时固定「深遮罩 + 白字」，不随明暗主题翻 ——
       理由与签名板保持纸白、灯箱压黑一致：压在照片上的文字不该跟着主题换色。
       没背景图（纯渐变）时跟主题走，浅底上仍是深字。
   ================================================================ */
.hero {
  position: relative;
  min-height: calc(100vh - var(--header-h));
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  padding: 56px 28px 96px;
  overflow: hidden;   /* 背景图位移/放大时不外溢（曾用 108vw 出过横向滚动条） */
}
/* 背景图照片层。刻意做成独立 <div> 而不是 .hero 自己的 background-image：
   这样遮罩能单独叠一层，JS 也只改这一层、不碰其他样式。 */
.hero-media {
  position: absolute; inset: 0;
  z-index: 0;
  background-position: center 38%;   /* 焦点略上移：竖构图人像不会被裁掉脸 */
  background-size: cover;
  background-repeat: no-repeat;
  /* 提升为独立合成层。不提升的话，这一层（视口大小、内含一张 2560px 位图）
     会在滚动时被逐帧重绘 —— 实测给它 will-change 后，滚动期间主线程耗时
     1649ms → 763ms、布局 42ms → 15ms、稳态最长帧 33ms → 17ms。
     代价是常驻一张视口大小的层（桌面 1440×834 约 9.6MB），换来滚动不卡，值。 */
  will-change: transform;
}
/* 遮罩：只在真的有照片时叠加（纯渐变模式不加，否则会把渐变压脏） */
.hero[data-media="1"] .hero-media::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10, 16, 28, .58) 0%, rgba(10, 16, 28, .50) 44%, rgba(10, 16, 28, .74) 100%);
}
.hero-inner { position: relative; z-index: 2; max-width: 900px; }

.hero h1 {
  font-family: "Songti SC", "STSong", Georgia, serif;
  font-size: 60px; font-weight: 500; letter-spacing: 14px;
  margin-bottom: 0;
  text-indent: 14px; /* 抵消末字后的字距，保证视觉居中 */
}
/* 打字机副标题：定高，避免逐字打出时下方细线上下跳 */
.hero-typed {
  margin-top: 20px;
  font-size: 16px; letter-spacing: 4px;
  min-height: 1.7em;
  color: var(--hero-dim);   /* 不是 --text-dim：hero 压在渐变上，见 --hero-dim 定义处 */
}
.typed-caret {
  display: inline-block; width: 2px; height: 1.05em; margin-left: 3px;
  background: currentColor; vertical-align: -0.16em;
  animation: caret-blink 1.05s steps(1, end) infinite;
}
@keyframes caret-blink { 0%, 49.9% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* 标题下的细线装饰 */
.hero .hero-rule {
  width: 56px; height: 1px; margin: 28px auto 0;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
}

/* 有照片时的白字态。
   text-shadow 不是装饰：遮罩是固定透明度，遇到大面积高亮区域（雨后的湿路面、
   雪地、白墙）中段仍会偏亮，白字实测只剩 2:1 上下。加一层极轻的深色投影把
   字从亮背景里「拔」出来，比继续加深遮罩划算 —— 加深遮罩会把照片整体压死。 */
.hero[data-media="1"] { color: #fff; }
.hero[data-media="1"] h1 { text-shadow: 0 2px 24px rgba(6, 12, 22, .55), 0 1px 3px rgba(6, 12, 22, .45); }
.hero[data-media="1"] .hero-typed {
  color: rgba(255, 255, 255, .9);
  text-shadow: 0 1px 12px rgba(6, 12, 22, .6), 0 1px 2px rgba(6, 12, 22, .5);
}
.hero[data-media="1"] .hero-rule { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .8), transparent); }

/* 底部「向下浏览」指示：告诉访客下面还有内容（相册退到第二屏后必须有它，
   否则首屏看起来像个只有标题的落地页）。
   ⚠ background / border / color 必须显式写：<button> 有浏览器默认样式，
   不写就是一块系统灰白底 —— 实测在照片上是一块刺眼的方块。 */
.hero-scroll {
  position: absolute; left: 50%; bottom: 28px;
  z-index: 2;
  transform: translateX(-50%);   /* 兜底：动画被系统禁掉时也要保持居中 */
  display: inline-flex; flex-direction: column; align-items: center; gap: 7px;
  background: transparent; border: 0; padding: 0;
  font-size: 12px; letter-spacing: 3px; color: var(--hero-dim);
  animation: hero-scroll-bob 2.4s ease-in-out infinite;
}
/* hover 用 --text 而不是 --accent：无照片时 accent 是 #1f74c9，压在渐变的亮蓝上
   只有 2.4:1，比不 hover 还差。（有照片时下面的 data-media 白字态会接管。） */
.hero-scroll:hover { text-decoration: none; color: var(--text); }
.hero-scroll svg { width: 18px; height: 18px; }
.hero[data-media="1"] .hero-scroll {
  color: rgba(255, 255, 255, .88);
  text-shadow: 0 1px 10px rgba(6, 12, 22, .65);
}
.hero[data-media="1"] .hero-scroll:hover { color: #fff; }
@keyframes hero-scroll-bob {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 7px); }
}

/* 首页放开 main 的宽度限制，让 hero 铺满视口宽；
   但相册区仍要 1280px 居中，所以下面各容器自己把约束加回来。
   注意 padding 四个方向都要管：main 原本是 '24px 28px 64px'，
   只清左右的话，顶部那 24px 会在页头与 hero 之间露出一条底色渐变
   （首版就是漏了这一处，截图里是页头下面一条明显的紫蓝横条）。
   底部 64px 保留 —— 滚动到最底时留一段呼吸，不然最后一行卡片贴着屏幕边。 */
body.home-view #app { max-width: none; padding: 0 0 64px; }
body.home-view #home-galleries,
body.home-view #home-private,
body.home-view .home-note {
  max-width: var(--maxw); margin-left: auto; margin-right: auto;
  padding-left: 28px; padding-right: 28px;
}
/* 首页排序条（2026-10-01 哥反馈：紧贴首屏照片下沿、间距为 0，上挤下松显得拥挤）。
   现在它是一块白色工具条卡片，因此**不再**与上面那组共用 28px 内边距，
   单独一条规则：宽度取 calc(100% - 56px)，让它的**外沿**与下方作品集卡片的
   左右边缘对齐（1440 视口下同为 108px），而不是与 #home-galleries 的外框对齐。
   居中仍交给 auto —— max-width 配固定 margin 会让元素靠左，不会自己居中。 */
body.home-view .sort-bar {
  width: calc(100% - 56px);
  max-width: calc(var(--maxw) - 56px);
  margin: clamp(30px, 4vw, 46px) auto 22px;
  padding: 12px 16px;
}
/* scroll-margin-top：首屏「向下浏览」滚过来时，别让 sticky 页头压住排序栏。
   sticky 元素是浮在文档流上的，scrollIntoView 只认元素位置、不管遮挡。 */
body.home-view #home-galleries,
body.home-view .sort-bar { scroll-margin-top: calc(var(--header-h) + 20px); }

/* ---------------- 作品集卡片（首页） ---------------- */
.portfolio-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 28px; margin-top: 8px; }
/* ⚠ 卡片自身的 transition 与 :hover **只在这里定义一次**。
   2026-10-01 排查「滚动时相册抖动」时发现：下方动效段里曾经又写了一遍
   `.portfolio-card` / `.portfolio-card:hover`（位移 -7px），把这里的 -4px
   整条盖死 —— 于是按 -4px 去分析，实际渲染的却是 -7px（实测 -6.97px）。
   重复定义已删，别再写第二份。 */
.portfolio-card {
  position: relative;
  display: block; background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; color: var(--text);
  transition: transform .3s cubic-bezier(.22,.61,.36,1), border-color .3s ease, box-shadow .3s ease;
}
.portfolio-card:hover {
  text-decoration: none;
  transform: translateY(-7px);
  border-color: var(--accent);
  box-shadow: 0 20px 40px -20px rgba(var(--accent-rgb), .75), 0 8px 18px -12px var(--shadow);
}
/* 封面卡保持 3:2 横幅裁切；竖图把焦点上移到画面上部（人脸位置），避免只截到躯干 */
.portfolio-cover { width: 100%; aspect-ratio: 3/2; object-fit: cover; object-position: 50% 25%; background: var(--bg-soft); }
.portfolio-meta { padding: 16px 18px 18px; }
.portfolio-meta h3 { font-size: 17px; font-weight: 600; margin-bottom: 4px; }
.portfolio-meta p { color: var(--text-dim); font-size: 13px; }
.portfolio-meta .count { margin-top: 10px; font-size: 12px; color: var(--text-dim); letter-spacing: 1px; }

/* ---------------- 照片墙（公开作品集 / 管理员预览） ---------------- */
/* 瀑布流（CSS columns）：竖图按原始比例完整显示，不再统一裁成横幅。
 * columns 按列高自动均衡填充，不会出现空列；masonry 用列宽自适应，
 * 与原 auto-fill 网格的列数节奏保持一致。 */
.layout-masonry { columns: 320px; column-gap: 14px; }
.layout-grid2 { columns: 2; column-gap: 14px; }
.layout-grid3 { columns: 3; column-gap: 14px; }

.gallery-photo {
  break-inside: avoid; -webkit-column-break-inside: avoid;
  margin: 0 0 14px; position: relative;
  border-radius: var(--radius); overflow: hidden; cursor: zoom-in;
  background: var(--bg-soft);
}
.gallery-photo img { width: 100%; height: auto; display: block; transition: transform .25s ease; }
.gallery-photo:hover img { transform: scale(1.03); }
.gallery-photo .photo-overlay {
  position: absolute; inset: 0; display: flex; align-items: flex-end;
  background: linear-gradient(transparent 60%, rgba(0,0,0,.55));
  opacity: 0; transition: opacity .2s ease; padding: 12px;
  font-size: 13px;
}
.gallery-photo:hover .photo-overlay { opacity: 1; }

/* ---------------- 表单 ---------------- */
.auth-wrap { max-width: 380px; margin: 60px auto 0; }
.auth-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 32px; }
.auth-card h2 { font-size: 20px; margin-bottom: 20px; font-weight: 600; }
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 13px; color: var(--text-dim); margin-bottom: 6px; }
.form-group input, .form-group textarea, .form-group select {
  width: 100%; background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 6px; color: var(--text); padding: 10px 12px; font-size: 14px; outline: none;
}
.form-group input:focus, .form-group textarea:focus { border-color: var(--accent); }
.form-group textarea { resize: vertical; min-height: 72px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--accent); color: var(--accent-contrast); border: none; border-radius: 6px;
  padding: 10px 22px; font-size: 14px; font-weight: 600;
}
.btn:hover { filter: brightness(1.08); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-ghost {
  background: transparent; border: 1px solid var(--border); color: var(--text-dim);
}
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); filter: none; }
.btn-danger { background: transparent; border: 1px solid rgba(var(--danger-rgb),.5); color: var(--danger); }
.btn-danger:hover { background: rgba(var(--danger-rgb),.1); filter: none; }
.btn-sm { padding: 5px 12px; font-size: 12.5px; border-radius: 5px; }
.auth-switch { margin-top: 16px; font-size: 13px; color: var(--text-dim); text-align: center; }

/* ---------------- 管理后台 ---------------- */
.admin-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; gap: 12px; flex-wrap: wrap; }
.admin-toolbar .hint { color: var(--text-dim); font-size: 13px; }
/* 作品集详情页的操作行：分享（所有访客可见）+ 编辑（仅作者） */
.portfolio-actions { display: flex; align-items: center; gap: 10px; margin-bottom: 24px; flex-wrap: wrap; }

.portfolio-row {
  display: flex; align-items: center; gap: 16px; background: var(--bg-card);
  border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 18px; margin-bottom: 12px;
}
.portfolio-row .thumb { width: 88px; height: 60px; object-fit: cover; object-position: 50% 25%; border-radius: 5px; background: var(--bg-soft); flex-shrink: 0; }
.portfolio-row .info { flex: 1; min-width: 0; }
.portfolio-row .info h3 { font-size: 15px; font-weight: 600; }
.portfolio-row .info .meta-line { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.portfolio-row .ops { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }
.badge { display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); color: var(--text-dim); }
.badge.public { color: var(--accent); border-color: var(--accent); }

/* ---------------- 编辑器 ---------------- */
.editor-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 20px; }
.editor-head h2 { font-size: 20px; font-weight: 600; }

.dropzone {
  border: 2px dashed var(--border); border-radius: var(--radius);
  padding: 40px 20px; text-align: center; color: var(--text-dim);
  margin-bottom: 24px; transition: border-color .2s ease, background .2s ease; font-size: 14px;
}
.dropzone.dragover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }

.progressbar { height: 5px; background: var(--bg-soft); border-radius: 999px; overflow: hidden; margin: 10px 0 24px; }
.progressbar > div { height: 100%; background: var(--accent); width: 0; transition: width .2s ease; }

.editor-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }

/* ---------------- 上传队列 ---------------- */
.upload-queue { display: flex; flex-direction: column; gap: 6px; margin: -14px 0 4px; }
.uq-item {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 10px 6px 6px;
}
.uq-thumb { width: 42px; height: 42px; object-fit: cover; border-radius: 5px; background: var(--bg-soft); flex: none; }
.uq-info { flex: 1; min-width: 0; }
.uq-name { font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.uq-size { font-size: 11px; color: var(--text-dim); }
.uq-status { font-size: 12px; flex: none; color: var(--text-dim); white-space: nowrap; }
.uq-status.busy { color: var(--accent); }
.uq-status.ok { color: var(--ok); }
.uq-status.err { color: var(--danger); }
/* 内容重复被服务端挡下：用品牌金而非报错红 —— 这不是失败，只是跳过 */
.uq-status.dup { color: var(--accent); font-weight: 600; }
.uq-retry { flex: none; }
.uq-remove { flex: none; padding: 2px 8px; }
.uq-remove:hover { color: var(--danger); border-color: var(--danger); }
.upload-confirm {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 12px; margin: 4px 0 14px;
}
.confirm-hint { font-size: 13px; color: var(--text-dim); }
.upload-result {
  margin: 4px 0 14px; padding: 10px 14px; border-radius: 8px;
  font-size: 14px; border: 1px solid var(--border);
}
.upload-result.all-ok { background: rgba(var(--ok-rgb),.12); border-color: rgba(var(--ok-rgb),.45); color: var(--ok); }
.upload-result.has-err { background: rgba(220,80,80,.10); border-color: rgba(220,80,80,.45); color: var(--danger); }
.upload-result.has-dup { background: var(--accent-soft); border-color: rgba(var(--accent-rgb),.45); color: var(--accent); }
.photo-card {
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; display: flex; flex-direction: column;
}
.photo-card.dragging { opacity: .4; }
.photo-card.drag-over { border-color: var(--accent); }
.photo-card img { width: 100%; aspect-ratio: 4/3; object-fit: cover; object-position: 50% 25%; cursor: grab; }
.photo-card .body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 6px; }
.photo-card input, .photo-card textarea {
  width: 100%; background: var(--bg-soft); border: 1px solid var(--border); border-radius: 5px;
  color: var(--text); padding: 6px 9px; font-size: 13px; outline: none;
}
.photo-card textarea { resize: vertical; min-height: 44px; }
.photo-card .ops { display: flex; justify-content: space-between; align-items: center; }
.photo-card .ops .order-badge { font-size: 11px; color: var(--text-dim); }

/* ---------------- 灯箱 ---------------- */
#lightbox {
  position: fixed; inset: 0; z-index: 200; background: rgba(8, 9, 11, 0.96);
  display: flex; align-items: center; justify-content: center;
}
#lightbox[hidden] { display: none; }
.lb-figure { max-width: min(1400px, 92vw); max-height: 88vh; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.lb-figure img { max-width: 92vw; max-height: 78vh; object-fit: contain; border-radius: 4px; cursor: zoom-in; }
.lb-figure figcaption { text-align: center; font-size: 13.5px; color: var(--text-dim); max-width: 80vw; }
.lb-figure .lb-title { font-size: 16px; color: var(--text); margin-bottom: 4px; }
.lb-figure .lb-exif { font-size: 12.5px; letter-spacing: .5px; }
.lb-figure .lb-hint { font-size: 11.5px; opacity: .55; margin-top: 2px; }
.lb-download { display: inline-block; margin-top: 8px; font-size: 12.5px; }
.lb-close, .lb-fullscreen, .lb-prev, .lb-next {
  position: fixed; background: rgba(255,255,255,.06); border: none; color: var(--text);
  width: 46px; height: 46px; border-radius: 50%; font-size: 22px; line-height: 1;
  display: flex; align-items: center; justify-content: center; padding: 0;
}
.lb-close:hover, .lb-fullscreen:hover, .lb-prev:hover, .lb-next:hover { background: rgba(255,255,255,.14); }
.lb-close { top: 20px; right: 24px; }
.lb-fullscreen { top: 20px; right: 84px; }
.lb-fullscreen svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lb-fullscreen .lb-fs-exit { display: none; }
#lightbox.lb-fs-on .lb-fullscreen .lb-fs-enter { display: none; }
#lightbox.lb-fs-on .lb-fullscreen .lb-fs-exit { display: block; }

/* 切换按钮容器：宽屏时两个按钮脱离文档流浮在图片两侧（display:contents 让按钮回到 #lightbox 的固定定位层） */
.lb-nav { display: contents; }
.lb-counter { display: none; }
.lb-prev { left: 24px; top: 50%; transform: translateY(-50%); }
.lb-next { right: 24px; top: 50%; transform: translateY(-50%); }

/* 灯箱全屏放大模式：图片撑满屏幕（保持完整比例），隐藏文字，点击图片恢复 */
#lightbox.lb-full { overflow: hidden; }
#lightbox.lb-full .lb-figure {
  max-width: none; max-height: none; width: 100vw; height: 100vh;
  margin: 0; justify-content: center; gap: 0;
}
#lightbox.lb-full .lb-figure img { max-width: 100vw; max-height: 100vh; border-radius: 0; cursor: zoom-out; }
#lightbox.lb-full figcaption { display: none; }

/* ---------------- 提示条 ---------------- */
.toast {
  position: fixed; bottom: 32px; left: 50%; transform: translateX(-50%); z-index: 300;
  background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
  padding: 11px 22px; border-radius: 999px; font-size: 14px;
  box-shadow: 0 8px 30px rgba(0,0,0,.5); max-width: 86vw;
}
.toast.error { border-color: var(--danger); color: var(--danger); }
.toast.success { border-color: var(--accent); color: var(--accent); }

/* ---------------- 页脚（2026-10-01）----------------
   站点此前**没有页脚**（下面那段 #site-footer 是历史遗留的死代码，无对应 HTML）。
   本次按哥要求新建 + 站名版权；友情链接其后由 2 条逐轮扩充到 6 条，
   顺序按行政层级自上而下（中国摄协 → 大众摄影 → 《中国摄影》杂志社 → 省文联 → 省摄协 → 市摄协），详见 index.html。

   · 位置上在 #app 之外 —— 路由切换只重渲染 #app，页脚常驻不闪。
   · 宽度收在 --maxw 内并与 main 左右同边距（28px），与上方内容对齐。
   · 链接做成胶囊卡片底，而不是裸文字压在渐变上：浅色渐变的亮端是 #b8dcff，
     灰字压上去只有 4.66:1，擦着及格线；放进卡片底后与站内 .sort-btn 同口径，
     明暗两套主题都稳（实测浅色 8.1:1 / 深色 6.5:1）。
   · 6 条链接在桌面 1280 宽下**一行放得下**（整组约 939px，实测 Δcy=0）；窄屏靠 flex-wrap
     折行、justify-content:center 保持每行都居中 —— 折行是设计内的，不是溢出。
     ⚠ 再加链接前先量这行宽度：可用宽约 1224px（1280 − 28×2），逼近就得调 gap 或字号。
   · hover **只变色与投影，不做位移/缩放** —— 位移类 hover 会在滚动时抖动
     （见 body.is-scrolling 那节），所以这里不需要往抑制清单里加。 */
#site-footer {
  width: 100%; max-width: var(--maxw); margin: 0 auto;
  padding: 0 28px 46px;
  color: var(--text-dim); font-size: 12.5px;
}
/* 管理端与登录/注册页收起页脚（body 类由 js/app.js 的 route() 打）：
   站长操作界面、访客登录框上挂一排「友情链接」不合适。 */
body.is-admin-page #site-footer { display: none; }

.foot-inner { text-align: center; }

.foot-links { margin: 0 0 22px; }
.foot-links-title {
  margin: 0 0 14px;
  font-size: 12px; font-weight: 500;
  letter-spacing: 4px; text-indent: 4px;   /* text-indent 抵消末字右侧多出的字距，视觉才居中 */
  color: var(--text-dim);
}
.foot-links-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
}
.foot-link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 15px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text); text-decoration: none;
  font-size: 12.5px; line-height: 1.4;
  transition: border-color .2s ease, color .2s ease, box-shadow .2s ease;
}
.foot-link:hover {
  border-color: var(--accent); color: var(--accent);
  box-shadow: 0 8px 20px -14px rgba(var(--accent-rgb), .9);
}
/* 外链图标：表示「在新窗口打开」。站内没有图标字体，一律内联 SVG（与 blog.js 同做法）。 */
.foot-link-ico { width: 12px; height: 12px; flex: none; opacity: .65; }
.foot-link:hover .foot-link-ico { opacity: 1; }

.foot-meta {
  margin: 0; display: flex; flex-wrap: wrap;
  justify-content: center; align-items: center; gap: 7px;
}
.foot-brand { color: var(--text); letter-spacing: 1px; }
.foot-dot { opacity: .5; }
.foot-copy { letter-spacing: .5px; }

@media (max-width: 768px) {
  #site-footer { padding: 0 16px 36px; }
  /* 行距 > 列距：窄屏会折成两行，行间要留得比同行两胶囊之间更松，层级才不乱 */
  .foot-links-list { gap: 10px 8px; }
  .foot-link { padding: 6px 13px; font-size: 12px; }
  .foot-meta { font-size: 12px; }
}

/* ---------------- 空状态 ---------------- */
.empty { text-align: center; color: var(--text-dim); padding: 70px 0; font-size: 14px; }

/* ---------------- 加载态 ---------------- */
.loading { text-align: center; color: var(--text-dim); padding: 70px 0; font-size: 14px; letter-spacing: 2px; }

/* ---------------- 约拍预约（公开登记表单） ---------------- */
.bk-wrap { max-width: 720px; margin: 0 auto; }
.bk-head { text-align: center; margin: 44px 0 30px; }
.bk-head h1 {
  font-family: "Songti SC", "STSong", Georgia, serif;
  font-size: 34px; font-weight: 500; letter-spacing: 8px; text-indent: 8px;
  margin-bottom: 14px;
}
.bk-head p { color: var(--text-dim); font-size: 13.5px; line-height: 1.9; }
.bk-req { color: var(--accent); }

.bk-card {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 30px 32px 26px;
}
.bk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }

.bk-field { margin-bottom: 18px; }
.bk-field label { display: block; font-size: 13px; color: var(--text-dim); margin-bottom: 7px; }
.bk-field input, .bk-field select, .bk-field textarea {
  width: 100%; background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 6px; color: var(--text); padding: 11px 13px; font-size: 14px; outline: none;
  transition: border-color .15s ease;
}
.bk-field select { cursor: pointer; color-scheme: dark; }
.bk-field input:focus, .bk-field select:focus, .bk-field textarea:focus {
  border-color: var(--accent);
  /* 光晕要跟着 focus 一起给：深底上 1px 边框变色不够显眼，访客点了框
     却看不出「接住了」，会以为没反应（2026-09-21 访客反馈的教训） */
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .14);
}
.bk-field input:hover, .bk-field select:hover { border-color: rgba(var(--accent-rgb), .45); }
.bk-field input::placeholder, .bk-field textarea::placeholder { color: var(--placeholder); }
.bk-field textarea { resize: vertical; min-height: 112px; line-height: 1.75; }
.bk-counter { text-align: right; font-size: 11.5px; color: var(--text-dim); margin-top: 5px; }

/* 分组小标题（把「联系方式」与「约拍安排」分开） */
.bk-sect {
  font-size: 12.5px; letter-spacing: 3px; color: var(--accent);
  margin: 26px 0 16px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}

/* 字段下方的说明文字 */
.bk-tip { font-size: 12px; color: var(--text-dim); line-height: 1.8; margin-top: 7px; }

/* 职业身份：在职 / 学生单选。
   原生 radio 在深色底上几乎看不见，所以自绘圆点，选中时整块卡片一起变色 ——
   让「当前选的是哪个」一眼可辨，而不是靠一个 12px 的小圆点。 */
.bk-radios { display: flex; gap: 10px; flex-wrap: wrap; }
.bk-radio {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 9px 16px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft); font-size: 13.5px; color: var(--text-dim);
  transition: border-color .15s, color .15s, background .15s;
}
.bk-radio:hover { border-color: var(--accent); color: var(--text); }
.bk-radio input {
  appearance: none; -webkit-appearance: none; width: 15px; height: 15px;
  border: 1px solid var(--neutral); border-radius: 50%; margin: 0;
  position: relative; background: transparent; flex: none;
}
.bk-radio input:checked { border-color: var(--accent); background: var(--accent); }
.bk-radio input:checked::after {
  content: ''; position: absolute; left: 4px; top: 4px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent-contrast);
}
.bk-radio:has(input:checked) {
  border-color: var(--accent); background: var(--accent-soft); color: var(--text);
}

/* ── 格式条款提示（通讯与送达地址）─────────────────────────
   依《民法典》第496条第2款：提供格式条款的一方对免除或者减轻其责任等
   与对方有重大利害关系的条款，应当采取合理的方式提示对方注意；未履行
   提示或者说明义务，致使对方没有注意或者理解的，对方可以主张该条款
   不成为合同的内容。

   因此这里同时使用四种标识：放大字号、特别颜色、下划线、独立边框。
   四者叠加不是装饰 —— 司法实践审查的正是「是否足以引起对方注意」。 */
.bk-clause {
  border: 2px solid var(--danger-strong);
  background: rgba(var(--danger-rgb), .16);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin: 4px 0 20px;
}
.bk-clause-h {
  font-size: 18px; font-weight: 800; color: var(--danger-strong); letter-spacing: 1px;
  text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 6px;
  margin-bottom: 12px;
}
.bk-clause-p {
  font-size: 15px; line-height: 2; color: var(--danger-dim); font-weight: 600;
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; margin-bottom: 10px;
}
.bk-clause-agree {
  font-size: 15px; font-weight: 800; color: var(--danger-strong);
  text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; margin-top: 4px;
}

/* ── 已签署同意书的上传区 ───────────────────────────────── */
.bk-sign {
  border: 1px solid var(--border); background: var(--bg-card);
  border-radius: var(--radius); padding: 22px 24px; margin-top: 10px;
}
.bk-sign-h { font-size: 15px; font-weight: 600; color: var(--text); margin-bottom: 8px; }
.bk-sign-desc { font-size: 13px; color: var(--text-dim); line-height: 1.9; margin-bottom: 16px; }
.bk-sign-ops { display: flex; gap: 10px; flex-wrap: wrap; }
.bk-sign-state { font-size: 12.5px; color: var(--text-dim); margin-top: 14px; line-height: 1.8; }
.bk-sign-state.ok { color: var(--ok); }
.bk-sign-state.err { color: var(--danger); }
.bk-sign-link {
  font-size: 12.5px; color: var(--text-dim); margin-top: 10px;
  padding-top: 12px; border-top: 1px dashed var(--border); word-break: break-all;
}

/* ── 在线电子签名（2026-09-30）──────────────────────────
   签名区刻意做成白底深墨：像纸，笔迹才自然；也和深色主题形成反差，
   提示「这里要动手写字」。触摸设备上 touch-action:none 防止手写时页面滚动。 */
.bk-sign-read { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.bk-esign-agree {
  display: flex; gap: 8px; align-items: flex-start; cursor: pointer;
  font-size: 13px; color: var(--text); line-height: 1.7; margin-bottom: 10px;
}
.bk-esign-agree input { margin-top: 3px; accent-color: var(--accent); flex: none; }
.bk-esign-pad {
  position: relative; background: #fff; border: 1px solid #cfcfcf;
  border-radius: 10px; overflow: hidden;
}
.bk-esign-pad canvas {
  display: block; width: 100%; height: 180px;
  touch-action: none; cursor: crosshair;
}
.bk-esign-hint {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: #a5a5a5; font-size: 13px; letter-spacing: 1px; pointer-events: none;
}

/* ── 身份证照片上传（2026-09-30）────────────────────────
   两格并排（手机上也放得下），虚线框占位、选中后显示本图预览。 */
.bk-id-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.bk-id-slot {
  position: relative; aspect-ratio: 16 / 10; border: 1.5px dashed var(--border);
  border-radius: 10px; overflow: hidden; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: var(--ghost); transition: border-color .15s ease;
}
.bk-id-slot:hover { border-color: rgba(var(--accent-rgb), .55); }
.bk-id-ph {
  color: var(--text-dim); font-size: 12.5px; line-height: 1.8;
  text-align: center; pointer-events: none;
}
.bk-id-preview {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* ⚠ 上面那句 display:block 会压掉 UA 的 [hidden]{display:none} —— 未选图时
   <img> 带着 hidden，缺了这一行页面上就会露出「身份证人像面预览」的裂图
   alt 文字。同一个坑站内已踩过两次（排序条 .sort-bar、近照 .bk-shot-preview），
   凡给元素显式设 display 又用 hidden 控制显隐的，都要这样兜一句。 */
.bk-id-preview[hidden] { display: none; }
.bk-id-labels {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  font-size: 12px; color: var(--text-dim); text-align: center; margin-top: 6px;
}

/* ── 本人近照上传（2026-10-01）──────────────────────────
   登记项里唯一**必传**的材料。三格并排、竖构图（3:4）——
   手机 390 宽下每格约 96px，三列仍放得下，不必折行。
   与身份证那两格同族（虚线框 + 选中后显示本图预览），两点区别：
   · 预览用 object-position: 50% 25% 略偏上，竖图裁切时人不至于只剩半张脸
     （与站内其他需裁切的缩略位同一口径）；
   · 选中的格子改为实线描边，三格齐没齐一眼可辨。 */
.bk-shot-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.bk-shot-slot {
  position: relative; aspect-ratio: 3 / 4; border: 1.5px dashed var(--border);
  border-radius: 10px; overflow: hidden; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: var(--ghost); transition: border-color .15s ease;
}
.bk-shot-slot:hover { border-color: rgba(var(--accent-rgb), .55); }
.bk-shot-slot.is-picked { border-style: solid; border-color: rgba(var(--accent-rgb), .6); }
.bk-shot-ph {
  color: var(--text-dim); font-size: 12px; line-height: 1.7;
  text-align: center; pointer-events: none;
}
.bk-shot-preview {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 25%; display: block;
}
/* 同 .bk-id-preview：display:block 会压掉 [hidden]，未选图时必须显式兜住，
   否则三格里会露出「近照 N 预览」的裂图 alt 文字（2026-10-01 上线实拍发现）。 */
.bk-shot-preview[hidden] { display: none; }

/* 日期时间输入。原生日期选择器与日历图标的明暗由 :root 的 color-scheme
   统一控制（深色主题 dark / 浅色主题 light），这里不再单独声明 ——
   否则浅色主题下会强制渲染成深色控件。 */
.bk-field input[type="datetime-local"],
.bk-field input[type="date"] {
  cursor: pointer;
}
.bk-field input[type="datetime-local"]:hover,
.bk-field input[type="date"]:hover { border-color: rgba(var(--accent-rgb), .45); }
.bk-field input[type="datetime-local"]::-webkit-calendar-picker-indicator,
.bk-field input[type="date"]::-webkit-calendar-picker-indicator {
  cursor: pointer; opacity: .8; transition: opacity .15s ease;
}
.bk-field input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
.bk-field input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* 时段二选一（上午 / 下午）：分段控件，与职业身份的单选卡片同族。
   两个选项并排占满一行，选中的用品牌金描边加浅金底，当前时段一眼可辨。
   忙/满状态由 JS 直接写进按钮文字（「上午 9:00–11:00 · 已约满」）。 */
.bk-half { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.bk-half-opt { position: relative; display: block; cursor: pointer; }
.bk-half-opt input { position: absolute; opacity: 0; pointer-events: none; }
.bk-half-opt span {
  display: block; text-align: center; padding: 11px 8px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft); font-size: 13.5px; color: var(--text-dim);
  transition: border-color .15s, color .15s, background .15s;
  white-space: nowrap;
}
.bk-half-opt:hover span { border-color: var(--accent); color: var(--text); }
.bk-half-opt input:checked + span {
  border-color: var(--accent); color: var(--accent);
  background: rgba(var(--accent-rgb), .10); font-weight: 600;
}
.bk-half-opt input:focus-visible + span { box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .14); }

/* 权利告知：不设勾选框，故视觉上要足够醒目，确保是提交前最后被看到的内容 */
.bk-notice {
  margin: 22px 0 4px; padding: 16px 18px;
  background: var(--accent-soft); border: 1px solid rgba(var(--accent-rgb), .28);
  border-radius: 6px;
}
.bk-notice-h {
  font-size: 13px; font-weight: 600; color: var(--accent);
  letter-spacing: 1px; margin-bottom: 8px;
}
.bk-notice p { font-size: 12.5px; color: var(--text-dim); line-height: 1.9; }
.bk-notice p.bk-notice-agree { margin-top: 8px; color: var(--text); }

/* 验证码：图片由后端渲染为点阵 SVG，这里只负责排版 */
.bk-cap-row { display: flex; gap: 10px; align-items: stretch; }
.bk-cap-row input {
  flex: 1; min-width: 96px; max-width: 200px;
  letter-spacing: 5px; text-align: center; font-size: 16px; text-transform: uppercase;
}
.bk-cap-img {
  flex: none; width: 130px; height: 44px; padding: 0; line-height: 0;
  border: 1px solid var(--border); border-radius: 6px;
  background: #f7f3ec; overflow: hidden;
}
.bk-cap-img:hover { border-color: var(--accent); }
.bk-cap-img svg { display: block; width: 100%; height: 100%; }
.bk-cap-hint {
  display: flex; align-items: center; justify-content: center;
  height: 100%; font-size: 12px; color: #8a8375; letter-spacing: 0;
}
.bk-cap-refresh {
  flex: none; width: 44px; height: 44px;
  border: 1px solid var(--border); border-radius: 6px;
  background: transparent; color: var(--text-dim); font-size: 17px;
}
.bk-cap-refresh:hover { border-color: var(--accent); color: var(--accent); }
.bk-cap-tip { font-size: 11.5px; color: var(--text-dim); margin-top: 7px; }

/* 蜜罐字段：对真人不可见，仅用于拦截自动化脚本（用绝对定位而非 display:none，
   部分爬虫会跳过 display:none 的字段） */
.bk-honeypot { position: absolute; left: -9999px; top: -9999px; height: 0; overflow: hidden; }

.bk-actions { display: flex; align-items: center; gap: 14px; margin-top: 24px; flex-wrap: wrap; }
.bk-submit { padding: 12px 42px; font-size: 15px; letter-spacing: 1px; }
.bk-status { font-size: 13px; color: var(--text-dim); }
.bk-status.error { color: var(--danger); }
.bk-privacy { text-align: center; color: var(--text-dim); font-size: 12px; margin-top: 18px; opacity: .75; }

/* 提交成功 */
.bk-done { text-align: center; padding: 54px 32px; }
.bk-done-mark {
  width: 58px; height: 58px; margin: 0 auto 20px; border-radius: 50%;
  border: 1px solid var(--accent); color: var(--accent);
  display: flex; align-items: center; justify-content: center; font-size: 26px;
}
.bk-done h2 { font-size: 22px; font-weight: 600; margin-bottom: 12px; }
.bk-done p { color: var(--text-dim); font-size: 14px; max-width: 460px; margin: 0 auto; line-height: 1.9; }

/* 成功页回显的约拍时间与地点 */
.bk-done-sum {
  margin: 22px auto 0; max-width: 430px; text-align: left;
  border-top: 1px solid var(--border); padding-top: 18px;
}
.bk-done-sum > div { display: flex; gap: 12px; font-size: 13px; margin-bottom: 8px; }
.bk-done-sum > div:last-child { margin-bottom: 0; }
.bk-done-sum span { flex: none; width: 68px; color: var(--text-dim); }
.bk-done-sum b { font-weight: 500; color: var(--text); word-break: break-word; }
.bk-done-ops { margin-top: 28px; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* ---------------- 约拍记录（管理端） ---------------- */
.bk-toolbar-ops { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* 手工录入表单（2026-09-25）：给没走公开表单、线下约的人建档 */
.bk-newform {
  background: var(--bg-card); border: 1px solid rgba(var(--accent-rgb), .4);
  border-radius: var(--radius); padding: 18px 20px; margin-bottom: 18px;
}
.bk-newform-h { font-size: 13px; color: var(--accent); letter-spacing: 1px; margin-bottom: 14px; }
.bk-newform .frm-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 16px; }
.bk-newform label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12.5px; color: var(--text-dim); min-width: 0;
}
.bk-newform label.frm-full { grid-column: 1 / -1; }
.bk-newform input, .bk-newform select, .bk-newform textarea {
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  color: var(--text); font-size: 13.5px; padding: 8px 10px; width: 100%;
  font-family: inherit;
}
.bk-newform input:focus, .bk-newform select:focus, .bk-newform textarea:focus {
  outline: none; border-color: var(--accent);
}
.bk-newform textarea { resize: vertical; }
.bk-newform-ops { display: flex; gap: 10px; margin-top: 14px; }
@media (max-width: 900px) {
  .bk-newform .frm-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
  .bk-newform .frm-grid { grid-template-columns: 1fr; }
}

.bk-stats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin-bottom: 20px; }
@media (max-width: 700px) { .bk-stats { grid-template-columns: repeat(2, 1fr); } }
.bk-stat {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 15px 16px; text-align: center;
}
.bk-stat b { display: block; font-family: Georgia, serif; font-size: 25px; font-weight: 600; line-height: 1.2; }
.bk-stat span { font-size: 12px; color: var(--text-dim); letter-spacing: 1px; }
.bk-stat.is-new b { color: var(--accent); }
.bk-stat.is-black b { color: var(--danger); }

.bk-tabs { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.bk-tab {
  background: transparent; border: 1px solid var(--border); color: var(--text-dim);
  padding: 6px 15px; border-radius: 999px; font-size: 13px;
}
.bk-tab:hover { border-color: var(--accent); color: var(--accent); }
.bk-tab.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

.bk-row {
  display: grid; grid-template-columns: 1fr auto; gap: 12px 16px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 15px 18px; margin-bottom: 10px;
  transition: border-color .15s ease;
}
.bk-row:hover { border-color: var(--border-strong); }
.bk-row.open { border-color: var(--accent); }
.bk-row-head { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.bk-row-main { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bk-name { font-size: 15px; font-weight: 600; }
/* 微信式备注名（2026-09-28）：设了备注后，原始姓名弱化显示在旁边 */
.bk-orig { font-size: 12px; font-weight: 400; color: var(--text-dim); margin-left: 6px; }
.bk-row-sub {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--text-dim);
}
.bk-dot { opacity: .45; }
.bk-phone { color: var(--text); font-family: Menlo, Consolas, monospace; font-size: 13px; }
.bk-row-ops { display: flex; gap: 8px; flex-wrap: wrap; align-self: center; justify-content: flex-end; }

.bk-badge.new { color: var(--accent); border-color: rgba(var(--accent-rgb),.5); background: var(--accent-soft); }
.bk-badge.contacted { color: var(--info); border-color: rgba(127,168,208,.45); }
.bk-badge.done { color: var(--ok); border-color: rgba(var(--ok-rgb),.45); }
/* 同意书是否已回传。已收用绿、待收压暗，让站长扫一眼列表就知道还差谁。 */
.bk-badge.signed { color: var(--ok); border-color: rgba(var(--ok-rgb),.35); background: rgba(var(--ok-rgb),.08); }
.bk-badge.unsigned { color: var(--text-dim); border-color: var(--border); }
.bk-warn { color: var(--danger); border-color: rgba(var(--danger-rgb),.45); }
/* 黑名单（2026-09-25）：红底徽标 + 整行压一圈红边，扫一眼就能认出来 */
.bk-badge.black { color: var(--danger); border-color: rgba(var(--danger-rgb),.5); background: rgba(var(--danger-rgb),.10); }
.bk-row.is-black { border-color: rgba(var(--danger-rgb),.35); }
.bk-row.is-black:hover, .bk-row.is-black.open { border-color: rgba(var(--danger-rgb),.65); }
/* 列表行上的备注摘要：一行截断，悬停 title 看全文 */
.bk-row-note {
  font-size: 12.5px; color: var(--danger);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 详情里的站长标注编辑 */
.bk-note-edit { display: flex; gap: 8px; align-items: flex-start; }
.bk-note-edit textarea {
  flex: 1; min-height: 56px; resize: vertical;
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  color: var(--text); font-size: 13px; padding: 8px 10px; font-family: inherit;
}
.bk-note-edit textarea:focus { outline: none; border-color: var(--accent); }

.bk-detail { grid-column: 1 / -1; border-top: 1px solid var(--border); padding-top: 15px; }
.bk-detail dl { display: grid; grid-template-columns: 92px 1fr; gap: 9px 14px; }
.bk-detail dt { font-size: 12.5px; color: var(--text-dim); }
.bk-detail dd { font-size: 13.5px; word-break: break-word; }
.bk-detail dd.bk-msg { line-height: 1.85; white-space: pre-wrap; }
.bk-detail dd.bk-dim { color: var(--text-dim); font-size: 12.5px; }

/* 签署件预览。缩略图限高：手机竖着拍的 A4 扫描件如果原尺寸铺开，
   会把整条详情撑到屏幕外，反而什么都看不全。点图可看大图。 */
.bk-sign-box { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.bk-sign-thumb {
  max-height: 180px; width: auto; max-width: 100%;
  border: 1px solid var(--border); border-radius: 6px;
}
.bk-sign-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ---------------- 日程（公开档期一览） ----------------
   对外只呈现「半天 + 忙闲 + 剩余名额」，没有姓名、没有具体时刻，
   所以这里刻意不做时间轴式的排布 —— 时间轴本身就会暗示出别人的预约时间。 */
.sc-wrap { max-width: 720px; margin: 0 auto; }
.sc-head { text-align: center; margin: 44px 0 26px; }
.sc-head h1 {
  font-family: "Songti SC", "STSong", Georgia, serif;
  font-size: 34px; font-weight: 500; letter-spacing: 8px; text-indent: 8px;
  margin-bottom: 14px;
}
.sc-head p { color: var(--text-dim); font-size: 13.5px; line-height: 1.9; }

/* 预约须知：日程页与预约表单页共用同一份 DOM（见 js/schedule.js 的 ScheduleRules）。
   左缘一道金线把它和普通卡片区分开，位置又始终在表单之前，很难被忽略。 */
.bk-rules {
  background: var(--bg-card); border: 1px solid var(--border);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius); padding: 16px 20px; margin-bottom: 18px;
}
.bk-rules-h { font-size: 13px; color: var(--accent); letter-spacing: 2px; margin-bottom: 10px; }
.bk-rules ul { list-style: none; }
.bk-rules li {
  position: relative; padding-left: 16px;
  font-size: 12.5px; color: var(--text-dim); line-height: 1.95;
}
.bk-rules li::before {
  content: ''; position: absolute; left: 2px; top: .8em;
  width: 4px; height: 4px; border-radius: 50%; background: var(--accent); opacity: .7;
}
.bk-rules b { color: var(--text); font-weight: 600; }
.bk-rules-extra { margin-top: 10px; font-size: 12.5px; color: var(--text); line-height: 1.9; }

/* 一句话结论：最早能约哪天、未来一周还剩几个半天 */
.sc-summary {
  font-size: 13px; color: var(--text); line-height: 1.9;
  background: var(--accent-soft); border: 1px solid rgba(var(--accent-rgb), .28);
  border-radius: var(--radius); padding: 11px 16px; margin-bottom: 14px;
}
.sc-summary.is-full { background: rgba(var(--danger-rgb), .10); border-color: rgba(var(--danger-rgb), .3); }
.sc-summary b { color: var(--accent); font-weight: 600; }
.sc-summary.is-full b { color: var(--danger); }
.sc-ago { color: var(--text-dim); font-size: 12px; }

.sc-legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px;
  font-size: 12px; color: var(--text-dim); margin-bottom: 16px;
}
.sc-legend span { display: inline-flex; align-items: center; gap: 6px; }
.sc-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }
.sc-dot.st-free { background: var(--ok); }
.sc-dot.st-full { background: var(--danger); }
.sc-dot.st-busy { background: var(--neutral-text); }
.sc-dot.st-past { background: var(--neutral); }
.sc-dot.st-closed { background: transparent; border: 1px solid var(--neutral); }

.sc-list { display: flex; flex-direction: column; gap: 10px; }
.sc-day {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 16px;
}
.sc-day.is-today { border-color: rgba(var(--accent-rgb), .45); }
.sc-day-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.sc-date { font-size: 15px; font-weight: 600; letter-spacing: .5px; }
.sc-week { font-size: 12.5px; color: var(--text-dim); }
.sc-week.is-weekend { color: var(--accent); opacity: .85; }
.sc-today-tag {
  font-size: 11px; color: var(--accent);
  border: 1px solid rgba(var(--accent-rgb), .5); background: var(--accent-soft);
  border-radius: 999px; padding: 1px 8px;
}
.sc-day-op { margin-left: auto; }

/* 站长视角的当天总览：「忙」还是「闲」。
   「闲」是缺省态（表里没有记录即为闲），显式写出来是为了让站长一眼看出
   哪天还没占上，不必逐个半天去数。访客看不到这个标签。 */
.sc-day-state {
  font-size: 11px; border-radius: 999px; padding: 1px 8px;
  border: 1px solid var(--border); color: var(--text-dim);
}
.sc-day-state.is-busy { color: var(--accent); border-color: rgba(var(--accent-rgb), .5); background: var(--accent-soft); }
.sc-day-state.is-free { color: var(--ok); border-color: rgba(var(--ok-rgb), .35); }

.sc-halves { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.sc-half {
  border: 1px solid var(--border); border-radius: 6px;
  padding: 10px 12px; background: rgba(255, 255, 255, .012);
}
.sc-half-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.sc-half-name { font-size: 13px; color: var(--text); letter-spacing: 1px; }
.sc-half-state { font-size: 12px; font-weight: 600; }
.sc-half-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 3px; min-height: 17px; }
.sc-half-ops { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }
.sc-half-ops .btn { padding: 3px 12px; font-size: 12px; }
.sc-book { padding: 3px 16px; }

/* 状态着色：可约给绿、约满给红，忙/已过/未开放统一压暗 ——
   后三种对访客而言是同一件事（约不了），没必要用三种颜色去区分原因。 */
.sc-half.st-free { border-color: rgba(var(--ok-rgb), .35); }
.sc-half.st-free .sc-half-state { color: var(--ok); }
.sc-half.st-full { border-color: rgba(var(--danger-rgb), .35); }
.sc-half.st-full .sc-half-state { color: var(--danger); }
.sc-half.st-busy .sc-half-state { color: var(--text-dim); }
.sc-half.st-busy, .sc-half.st-past, .sc-half.st-closed { opacity: .6; }

.sc-foot { text-align: center; margin-top: 28px; }
.sc-foot .btn { padding: 11px 34px; font-size: 14px; }
.sc-privacy { text-align: center; color: var(--text-dim); font-size: 12px; margin-top: 16px; opacity: .75; }

/* 预约表单页：日程预选提示 + 时段额度提示 */
.bk-preset {
  font-size: 13px; color: var(--text); line-height: 1.9;
  background: var(--accent-soft); border: 1px solid rgba(var(--accent-rgb), .28);
  border-radius: var(--radius); padding: 10px 16px; margin-bottom: 16px;
}
.bk-preset b { color: var(--accent); }
.bk-slot { font-size: 12.5px; line-height: 1.8; margin: -6px 0 18px; min-height: 18px; }
.bk-slot.ok { color: var(--ok); }
.bk-slot.err { color: var(--danger); }

/* ---------------- 响应式（900 断点）· 灯箱 ---------------- */
/* 手机 / 平板窄屏：上一张、下一张按钮从图片两侧移到图片正下方，
   避免圆形按钮压住画面；同时收紧图片高度，为按钮行与文字说明留出空间 */
@media (max-width: 900px) {
  .lb-nav {
    display: flex; align-items: center; justify-content: center;
    gap: 14px; padding-top: 12px;
  }
  .lb-prev, .lb-next {
    position: static; left: auto; right: auto; top: auto; transform: none;
    width: 44px; height: 44px; font-size: 20px; background: rgba(255,255,255,.10);
  }
  .lb-counter {
    display: inline-block; min-width: 64px; text-align: center;
    font-size: 13px; letter-spacing: .5px; color: var(--text-dim);
  }
  .lb-figure { max-height: 94vh; gap: 10px; }
  .lb-figure img { max-height: 54vh; min-height: 0; }
  .lb-figure figcaption { max-width: 92vw; }
  .lb-close, .lb-fullscreen { width: 42px; height: 42px; top: 14px; }
  .lb-close { right: 14px; }
  .lb-fullscreen { right: 64px; }
  .lb-fullscreen svg { width: 18px; height: 18px; }
  /* 撑满模式：图片同样给下方的切换按钮留出空间 */
  #lightbox.lb-full .lb-figure img { max-height: calc(100vh - 62px); }
}

/* ---------------- 响应式（768 断点） ---------------- */
@media (max-width: 768px) {
  main { padding: 16px 16px 48px; }
  /* 首页排序条：左右内缩由 calc(100% - 56px) 自带（390 视口下同为 28px），
     这里只把卡片内边距收一档，把横向空间尽量留给标签与按钮。 */
  body.home-view .sort-bar { padding: 11px 14px; }
  #site-header { padding: 10px 14px; }

  /* 页头：手机端改汉堡菜单。导航项横排放不下会逐字断行竖排（2026-09-27 手机截图事故），
     收进 sticky 页头正下方的全宽下拉面板，纵向排列、大触控目标 */
  #nav-toggle { display: flex; }
  #site-nav {
    display: none;
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 2px 16px 14px;
    /* 不透明：半透明会让下层内容透出鬼影，菜单看起来「没盖住」 */
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 14px 28px var(--shadow);
  }
  #site-header.nav-open #site-nav { display: flex; }
  #site-nav a {
    padding: 13px 4px;
    border-bottom: 1px solid var(--border);
    font-size: 15px;
  }
  /* 退出 / 登录按钮：面板底部整行一块，别沿用胶囊小按钮的样式 */
  #site-nav .nav-btn {
    margin-top: 12px; padding: 11px;
    border: 1px solid var(--border); border-radius: 6px;
    font-size: 14px; text-align: center;
  }

  /* 手机端 hero 的字号；padding / 高度 / 打字机尺寸见样式表末尾的 hero 段 */
  .hero h1 { font-size: 34px; letter-spacing: 8px; text-indent: 8px; }
  .layout-masonry, .layout-grid2, .layout-grid3 { columns: 2; column-gap: 10px; }
  .gallery-photo { margin-bottom: 10px; }
  .portfolio-row { flex-wrap: wrap; }
  /* 作品集行的操作按钮：窄屏虽然换了行，但 .ops 是 flex-shrink:0，
   * 换行后宽度仍是内容宽度（三个按钮约 420px），在 390px 屏上照样溢出 ——
   * 表现为整个管理页能横向拖动。让它占满一行并允许收缩，按钮自行换行。
   * （2026-09-18 加改密码功能时顺带发现并修掉） */
  .portfolio-row .ops { flex-basis: 100%; flex-shrink: 1; min-width: 0; justify-content: flex-start; }
  .editor-photos { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

  /* 约拍预约 */
  .bk-head { margin: 26px 0 22px; }
  .bk-head h1 { font-size: 26px; letter-spacing: 5px; text-indent: 5px; }
  .bk-card { padding: 22px 18px; }
  .bk-grid { grid-template-columns: 1fr; }
  .bk-cap-row input { max-width: none; }
  .bk-cap-img { width: 116px; }
  .bk-stats { grid-template-columns: repeat(2, 1fr); }
  .bk-row { grid-template-columns: 1fr; }
  .bk-row-ops { justify-content: flex-start; }
  .bk-detail dl { grid-template-columns: 1fr; gap: 3px 0; }
  .bk-detail dt { margin-top: 9px; }

  /* 拍摄地点：窄屏改成两列，按钮独占一行 */
  .shot-place input { flex: 1 1 calc(50% - 5px); min-width: 0; }

  /* 日程：窄屏下仍保持「上午 / 下午」两列 —— 半天是这里的基本单位，
     拆成一列会让一天的档期散成两块，反而更难一眼看全 */
  .sc-head { margin: 26px 0 20px; }
  .sc-head h1 { font-size: 26px; letter-spacing: 5px; text-indent: 5px; }
  .sc-day { padding: 11px 13px; }
  .sc-halves { gap: 8px; }
  .sc-half { padding: 9px 10px; }
  .sc-half-name { font-size: 12.5px; letter-spacing: 0; }
  .sc-summary { font-size: 12.5px; }
  .bk-rules { padding: 14px 15px; }
  .bk-rules li { font-size: 12px; }
}

/* ============================================================
   拍摄时间 / 拍摄地点（2026-09-16 新增）
   ============================================================ */

/* 排序条：首页与后台列表共用同一套写法。
   2026-10-01 视觉重做 —— 原先它是「裸的一行」直接躺在页面底色上（浅色主题那层
   是紫蓝渐变），灰字压渐变对比度低、按钮也偏细小。现在包成一块**白色工具条
   卡片**，与站内其他模块同一层次，对比度问题一并解决（灰字改压白底）。

   ⚠ 必须显式写 [hidden]：下面这条 display:flex 的特异性高于 UA 样式表里的
     [hidden] { display: none }，不写这句，JS 里 bar.hidden = true 根本不起作用
     （首页一条公开作品集都没有时，会留下一个空工具条）。2026-10-01 实测确认。 */
.sort-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 22px;
  padding: 12px 16px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.sort-bar[hidden] { display: none; }

.sort-bar .sort-label { font-size: 13px; color: var(--text-dim); margin-right: 4px; }
.sort-bar .hint { font-size: 12.5px; color: var(--text-dim); opacity: .75; margin-left: 4px; }

.sort-btn {
  background: transparent; border: 1px solid var(--border-strong); color: var(--text-dim);
  padding: 7px 16px; border-radius: 999px; font-size: 13px; line-height: 1.2;
  transition: color .2s, border-color .2s, background .2s;
}
.sort-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
/* 选中态用实底。白卡片上，「淡蓝底 + 蓝字」跟灰边框的未选中态容易看混；
   实底一眼就能看出当前是哪个方向。字色取 --accent-contrast（两套主题各自算过：
   浅色 #fff on #1f74c9 = 4.8:1，深色 #08131f on #49b1f5 = 8.0:1）。 */
.sort-btn.active {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-contrast); font-weight: 500;
}
/* 键盘焦点环（鼠标点击不出现，只在 Tab 到时给）——与约拍那组 .bk-half-opt 同一手法 */
.sort-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .3); }

/* 卡片 / 列表里的「拍摄时间 · 拍摄地」行 */
.shot-line {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--text-dim); margin-top: 4px;
}
.shot-line .shot-date { color: var(--accent); }
/* 分隔符别用 --border（#262a32）—— 卡片底色上几乎看不见，日期和地点会粘成一片 */
.shot-line .shot-sep { color: var(--text-dim); opacity: .5; }
.shot-line .shot-empty { color: var(--text-dim); opacity: .55; }
.portfolio-row .info .shot-line { margin-top: 3px; }

/* 作品集详情页：标题下方的拍摄信息 */
.page-shot { color: var(--text-dim); font-size: 13.5px; margin: 0 0 16px; letter-spacing: .3px; }

/* 编辑器：拍摄时间（年 / 月 / 日 三级下拉，选到哪级就是哪级精度） */
.shot-block .shot-label { display: block; font-size: 13px; color: var(--text-dim); margin-bottom: 8px; }
.shot-block .shot-label .hint { font-size: 12px; opacity: .75; margin-left: 6px; }
.shot-date { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.shot-date .shot-sel {
  width: auto; min-width: 92px; background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 6px; color: var(--text); padding: 8px 10px; font-size: 13.5px; outline: none;
}
.shot-date .shot-sel:focus { border-color: var(--accent); }
/* 上级未选时下级禁用：避免出现「填了日却没填年月」这种拼不出值的状态 */
.shot-date .shot-sel:disabled { opacity: .45; cursor: not-allowed; }
.shot-date .shot-preview { font-size: 13px; color: var(--accent); }

/* 编辑器：拍摄地点（省 / 市 / 区 / 街道，可选可手填） */
.shot-place { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.shot-place input {
  flex: 1 1 160px; min-width: 120px; background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 6px; color: var(--text); padding: 10px 12px; font-size: 13.5px; outline: none;
}
.shot-place input:focus { border-color: var(--accent); }
.shot-place .btn { flex: 0 0 auto; }

/* ============================================================
   约拍交付分享（2026-09-18）
   两部分：管理端的「交付链接」设置面板，以及客户看到的交付页。
   客户页是陌生人最先接触这个站点的界面，配色沿用站内的深色 + 金色，
   但不放导航以外的任何入口 —— 客户的任务只有「看照片」与「下载照片」。
   ============================================================ */

/* ---------------- 徽标补充 ---------------- */
.badge.delivery { color: var(--info); border-color: rgba(var(--info-rgb), 0.5); }
.badge.warn { color: var(--danger); border-color: rgba(var(--danger-rgb), 0.5); }

/* ---------------- 管理端：交付链接设置面板 ---------------- */
.share-panel {
  margin: -8px 0 20px;
  padding: 16px 18px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.share-panel .sp-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.share-panel .sp-row:last-of-type { margin-bottom: 0; }
.share-panel .sp-row > label {
  width: 72px; flex-shrink: 0; font-size: 13px; color: var(--text-dim);
}
.share-panel .sp-row > input[type="text"],
.share-panel .sp-row > input[type="date"] {
  background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
  padding: 7px 10px; border-radius: 5px; font-size: 13px; min-width: 250px; outline: none;
}
.share-panel .sp-row > input:focus { border-color: var(--accent); }
.share-panel .sp-url {
  flex: 1;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--accent);
}
.share-panel .sp-pass { min-width: 0; max-width: 200px; }
.share-panel .sp-expire { min-width: 0; }
.share-panel .sp-row .hint { font-size: 12px; color: var(--text-dim); opacity: .85; }
.share-panel .sp-state {
  font-size: 12.5px; padding: 3px 12px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-dim);
}
.share-panel .sp-state.on { color: var(--ok); border-color: rgba(var(--ok-rgb), 0.5); }
.share-panel .sp-state.off { color: var(--danger); border-color: rgba(var(--danger-rgb), 0.5); }
/* 「本次设置的口令」备忘行：口令只存哈希，服务端无法回显，所以这一次
 * 显示必须做得足够显眼，让站长一眼看见、顺手复制走。 */
.share-panel .sp-memo { padding-top: 12px; border-top: 1px dashed var(--border); }
.share-panel .sp-memo-val {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 15px; letter-spacing: 2px;
  padding: 4px 12px; border-radius: 5px;
  background: var(--accent-soft); border: 1px solid var(--accent); color: var(--accent);
}
.share-panel .sp-note {
  margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--border);
  font-size: 12.5px; color: var(--text-dim); line-height: 1.8;
}

/* ---------------- 首页：未公开相册 ---------------- */
/* 与公开作品集分段显示，中间用一条实线隔开。目的很直接：
 * 让「这是只有我能看的东西」在视觉上一眼可辨，避免顺手把私密链接发出去。 */
.private-block {
  margin-top: 72px; padding-top: 34px;
  border-top: 1px solid var(--border);
}
.private-head { margin-bottom: 20px; }
.private-head h2 {
  font-size: 18px; font-weight: 600;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.private-head .hint {
  display: block; font-size: 12.5px; color: var(--text-dim); margin-top: 6px;
}
.private-block .portfolio-card { border-style: dashed; }

/* 卡片下方的操作条。刻意放在 <a> 之外：把按钮嵌进链接里，点按钮会顺带
 * 触发卡片跳转，得靠 preventDefault 到处堵，结构上分开更干净。 */
.priv-item { display: flex; flex-direction: column; gap: 10px; }
.priv-item .portfolio-card { flex: 1; }
.priv-state {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px dashed var(--border);
}
.priv-ops { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---------------- 交付页（客户视角） ---------------- */
.dl-wrap { max-width: 1100px; margin: 0 auto; }
.dl-head { margin-bottom: 22px; }
.dl-meta { color: var(--text-dim); font-size: 13px; margin-top: 8px; }

.dl-toolbar {
  position: sticky; top: 57px; z-index: 20;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 14px; margin-bottom: 20px;
  background: rgba(22, 24, 29, 0.94);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.dl-selcount { font-size: 13px; color: var(--text-dim); }
.dl-spacer { flex: 1; }

.dl-grid {
  /* 瀑布流：竖图完整显示（与公开照片墙同一套思路），列宽自适应 */
  columns: 240px; column-gap: 14px;
}
.dl-item {
  position: relative; overflow: hidden;
  break-inside: avoid; -webkit-column-break-inside: avoid;
  margin: 0 0 14px;
  background: var(--bg-soft);
  border: 2px solid transparent; border-radius: var(--radius);
  transition: border-color .15s;
}
.dl-item.picked { border-color: var(--accent); }
.dl-img {
  width: 100%; height: auto; display: block;
  cursor: zoom-in; background: var(--bg-card);
}
.dl-item-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 10px; font-size: 12px; color: var(--text-dim);
}
.dl-no { font-variant-numeric: tabular-nums; }
.dl-one { font-size: 12px; }
/* 复选框放在图片上层：点击它只切换选中，不触发灯箱 */
.dl-pick { position: absolute; top: 8px; left: 8px; z-index: 2; display: block; line-height: 0; }
.dl-pick input { width: 21px; height: 21px; accent-color: var(--accent); cursor: pointer; }
.dl-foot-tip {
  margin-top: 30px; font-size: 12.5px; color: var(--text-dim); line-height: 1.9;
}

/* 口令页 */
.dl-gate { max-width: 350px; margin: 72px auto; text-align: center; }
.dl-gate-mark { font-size: 21px; letter-spacing: 6px; color: var(--accent); margin-bottom: 34px; }
.dl-gate h2 { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
.dl-gate p { font-size: 13px; color: var(--text-dim); }
.dl-gate form { display: flex; gap: 8px; margin: 22px 0 12px; }
.dl-gate input {
  flex: 1; min-width: 0; background: var(--bg-card); border: 1px solid var(--border);
  color: var(--text); padding: 11px 14px; border-radius: 6px;
  font-size: 15px; text-align: center; letter-spacing: 3px; outline: none;
}
.dl-gate input:focus { border-color: var(--accent); }
.dl-gate-hint { font-size: 12px; }

/* 链接打不开时的说明页 */
.dl-error { max-width: 460px; margin: 72px auto; text-align: center; }
.dl-error-mark { font-size: 24px; color: var(--text-dim); margin-bottom: 20px; }
.dl-error h2 { font-size: 17px; font-weight: 600; margin-bottom: 14px; }
.dl-error p { font-size: 13px; color: var(--text-dim); line-height: 1.9; }

@media (max-width: 640px) {
  .dl-toolbar { top: 51px; padding: 10px; gap: 8px; }
  .dl-toolbar .btn { padding: 6px 11px; font-size: 12px; }
  .dl-selcount { width: 100%; }
  .dl-spacer { display: none; }
  .dl-grid { columns: 2; column-gap: 10px; }
  .dl-item { margin-bottom: 10px; }
  .dl-item-bar { padding: 5px 8px; }
  /* 设置面板在窄屏上改为上下堆叠，否则输入框会被挤成一条 */
  .share-panel { padding: 14px; }
  .share-panel .sp-row { align-items: stretch; }
  .share-panel .sp-row > label { width: 100%; }
  .share-panel .sp-row > input[type="text"],
  .share-panel .sp-row > input[type="date"] { min-width: 0; width: 100%; }
  .share-panel .sp-pass, .share-panel .sp-expire { max-width: none; }
}

/* ---------------- 账号安全 · 修改密码（2026-09-18）----------------
 * 收起状态的折叠块：改密码是低频操作，不该占着作品集列表上方的位置。 */
.acct-box {
  margin: 0 0 20px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.acct-box > summary {
  cursor: pointer; padding: 12px 18px; font-size: 13.5px;
  color: var(--text-dim); list-style: none;
}
.acct-box > summary::-webkit-details-marker { display: none; }
.acct-box > summary::before { content: '▸ '; color: var(--accent); }
.acct-box[open] > summary::before { content: '▾ '; }
.acct-box > summary:hover { color: var(--text); }
.acct-body {
  padding: 4px 18px 16px;
  border-top: 1px solid var(--border);
}
.acct-body .acct-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px; padding-top: 12px;
}
.acct-body .acct-row > label {
  width: 88px; flex-shrink: 0; font-size: 13px; color: var(--text-dim);
}
.acct-body .acct-row > input[type="password"],
.acct-body .acct-row > input[type="text"] {
  background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
  padding: 7px 10px; border-radius: 5px; font-size: 13px;
  min-width: 260px; outline: none;
}
.acct-body .acct-row > input:focus { border-color: var(--accent); }
.acct-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.acct-actions .acct-show {
  display: flex; align-items: center; gap: 5px;
  font-size: 12.5px; color: var(--text-dim); cursor: pointer;
}
.acct-actions .acct-memo { font-size: 12px; color: var(--text-dim); }
.acct-memo code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--accent); font-size: 13px;
}
.acct-note {
  margin-top: 12px; font-size: 12px; line-height: 1.75;
  color: var(--text-dim); opacity: .9;
}
.acct-note strong { color: var(--text); font-weight: 600; }

@media (max-width: 640px) {
  .acct-body .acct-row > label { width: 100%; }
  .acct-body .acct-row > input[type="password"],
  .acct-body .acct-row > input[type="text"] { min-width: 0; width: 100%; }
}

/* ── 留言板（2026-09-24）────────────────────────────── */
/* 管理端列表里的内容预览行（未展开时露一句，展开看全文） */
.msg-preview {
  margin-top: 6px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-dim);
}
/* 提交成功的确认卡 */
.gb-done { text-align: center; padding: 48px 24px; }
.gb-done-icon {
  width: 56px; height: 56px; margin: 0 auto 16px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 28px;
  color: var(--accent);
  border: 2px solid rgba(var(--accent-rgb), .5);
  background: var(--accent-soft, rgba(var(--accent-rgb), .1));
}
.gb-done h2 { margin: 0 0 8px; font-size: 20px; color: var(--text); }
.gb-done p { margin: 0 0 6px; color: var(--text-dim); font-size: 14px; }
.gb-done .gb-done-sub { margin-top: 14px; }
.gb-done .gb-done-sub a { color: var(--accent); }

/* ── 博客（2026-09-24；2026-10-01 改版：照 Hexo Butterfly）──────────
   列表卡由「竖排」改为**横向左右布局**，比例全部照 Butterfly 的 index.css 实量：
     .recent-post-item   { display:flex; align-items:center; height:16.8em }
     .post_cover         { width:42%; height:100%; overflow:hidden }
     .post_cover.right   { order:1 }        ← 封面按条目序号左右交替
     .recent-post-info   { width:58%; padding:0 40px }
     卡片 hover → .post-bg { transform:scale(1.1) }（transition .6s）
     ≤768px：flex-direction:column、封面 height:230px、封面 order 归 0
   卡片阴影用 Butterfly 那对值（0 3px 8px 6px rgba(7,17,27,.05/.09)），
   但**不做位移**——位移类 hover 会在滚动时抖动（见 body.is-scrolling 那节）。 */
.blog-wrap { max-width: 980px; margin: 0 auto; padding: 32px 16px 60px; }
/* 正文页要放右侧目录，比列表页更宽 */
.blog-wrap-post { max-width: 1180px; }
.editor-wrap { max-width: 900px; }

.post-card {
  position: relative; overflow: hidden;
  display: flex; flex-direction: row; align-items: center;
  height: 268px; margin-bottom: 20px;                 /* 268px ≈ Butterfly 的 16.8em */
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 3px 8px 6px rgba(7, 17, 27, .05);
  text-decoration: none; color: inherit;
  transition: box-shadow .3s ease, border-color .3s ease;
}
.post-card:hover {
  border-color: var(--accent);
  box-shadow: 0 14px 30px -16px rgba(var(--accent-rgb), .6), 0 3px 8px 6px rgba(7, 17, 27, .09);
}
/* 封面左右交替：由 JS 给「有封面」的卡按顺序编号、偶数张加 .flip（见 blog.js）。
   不用 :nth-child(even) —— 那样第 2 张没封面时，视觉上的第 2 张有图卡反而跑到右边。 */
.post-card.flip > .post-card-cover { order: 1; }

.post-card-cover { flex: none; width: 42%; height: 100%; overflow: hidden; background: var(--bg-soft); }
.post-card-cover img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 25%;       /* 保人脸，与全站一致 */
  display: block;
  transition: transform .6s cubic-bezier(.22, .61, .36, 1);
}
.post-card:hover .post-card-cover img { transform: scale(1.1); }

.post-card-body { width: 58%; padding: 0 40px; }
/* 无封面：文字区吃满整行（对应 Butterfly 的 .recent-post-info.no-cover） */
.post-card.no-cover .post-card-body { width: 100%; }

.post-card-title {
  margin: 0 0 10px; font-size: 21px; line-height: 1.4; color: var(--text);
  /* 两行截断（Butterfly 的 -webkit-line-clamp:2）—— 必须配 display:-webkit-box */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
  transition: color .2s ease-in-out;
}
.post-card:hover .post-card-title { color: var(--accent); }
.post-card-meta { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--text-dim); }
.post-card-summary {
  margin: 10px 0 0; font-size: 14px; line-height: 1.75; color: var(--text-dim);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
/* meta 前的小图标：站内没有图标字体，一律内联 SVG（见 blog.js 的 ICON_CAL） */
.card-ico { width: 13px; height: 13px; flex: none; opacity: .85; }

/* 文章正文页：顶部封面 hero + 右侧目录 + 上下篇 */
.post-hero {
  position: relative; overflow: hidden;
  height: min(44vh, 380px); min-height: 220px;
  margin-bottom: 22px;
  border-radius: var(--radius); background: var(--bg-soft);
}
.post-hero-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 30%;
  /* 与首页首屏同理：视口大小的大图提升为独立合成层，滚动时不再逐帧重绘 */
  will-change: transform;
}
/* 遮罩：压住照片以保证白字对比度（与首页 hero 同一套手法，刻意不随主题翻） */
.post-hero::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8, 12, 20, .16) 0%, rgba(8, 12, 20, .38) 46%, rgba(8, 12, 20, .74) 100%);
}
.post-hero-inner { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: 26px 32px; }
.post-hero-inner h1 {
  margin: 0 0 10px; font-size: 30px; line-height: 1.35; font-weight: 600;
  color: #fff; text-shadow: 0 2px 14px rgba(0, 0, 0, .45);
}

.post-meta { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-dim); }
.post-hero-inner .post-meta { color: rgba(255, 255, 255, .92); }

.post-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 22px; align-items: start; }
@media (min-width: 1100px) { .post-layout { grid-template-columns: minmax(0, 1fr) 236px; } }

.post-page { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 28px 32px 32px; }
.post-head h1 { margin: 0 0 8px; font-size: 26px; line-height: 1.35; color: var(--text); }
.post-head { margin-bottom: 22px; padding-bottom: 18px; border-bottom: 1px solid var(--border); }
.post-foot { margin-top: 30px; padding-top: 18px; border-top: 1px solid var(--border); font-size: 13.5px; }
.post-foot a { color: var(--accent); text-decoration: none; }

/* 右侧目录（≥1100px 才出现；窄屏没有空间，目录也别硬塞） */
.post-aside { display: none; }
@media (min-width: 1100px) { .post-aside { display: block; } }
.post-toc {
  position: sticky; top: calc(var(--header-h) + 20px);
  max-height: calc(100vh - var(--header-h) - 48px); overflow-y: auto;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 12px 16px;
}
.post-toc-title { font-size: 13px; letter-spacing: 2px; color: var(--text-dim); margin: 0 0 10px 8px; }
.post-toc-list { list-style: none; margin: 0; padding: 0; }
/* 目录项用 <button> 而不是 <a href="#xxx">：本站是 hash 路由，
   点普通锚点会把 location.hash 改成 #xxx，直接被路由当成一次跳转、页面被重渲染。 */
.post-toc-link {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; border-left: 2px solid transparent;
  padding: 5px 8px 5px 10px; border-radius: 0 6px 6px 0;
  font-family: inherit; font-size: 13px; line-height: 1.5;
  color: var(--text-dim);                       /* button 不继承文字色，必须显式给 */
  cursor: pointer;
  transition: color .2s, border-color .2s, background .2s;
}
.post-toc-link:hover { color: var(--text); background: var(--bg-soft); }
.post-toc-link.active { color: var(--accent); border-left-color: var(--accent); background: var(--accent-soft); }
.post-toc-list .lvl-3 > .post-toc-link { padding-left: 22px; font-size: 12.5px; }

/* 上一篇 / 下一篇（对应 Butterfly 的 post-nav） */
.post-nav { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 18px; }
.post-nav-item {
  display: block; padding: 10px 14px;
  background: var(--bg-soft); border: 1px solid var(--border); border-radius: var(--radius);
  text-decoration: none; color: var(--text);
  transition: border-color .2s ease;
}
.post-nav-item.next { text-align: right; }
a.post-nav-item:hover { border-color: var(--accent); }
.post-nav-label { display: block; font-size: 11.5px; letter-spacing: 1px; color: var(--text-dim); margin-bottom: 4px; }
.post-nav-title {
  font-size: 13.5px; line-height: 1.5;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden;
}
.post-nav-empty { border-style: dashed; opacity: .5; }

/* 正文里的标题要带锚点偏移：点目录跳过去时别被页头盖住 */
.post-body h2, .post-body h3 { scroll-margin-top: calc(var(--header-h) + 20px); }

/* Markdown 正文排版（.post-body 同时用作编辑器预览区） */
.post-body { font-size: 15.5px; line-height: 1.9; color: var(--text); word-break: break-word; }
.post-body h1, .post-body h2, .post-body h3,
.post-body h4, .post-body h5, .post-body h6 {
  margin: 1.4em 0 .6em; line-height: 1.4; color: var(--text);
}
.post-body h1 { font-size: 22px; } .post-body h2 { font-size: 19px; }
.post-body h3 { font-size: 17px; } .post-body h4 { font-size: 15.5px; }
.post-body p { margin: .8em 0; }
.post-body a { color: var(--accent); }
.post-body hr { border: 0; border-top: 1px solid var(--border); margin: 1.8em 0; }
.post-body ul, .post-body ol { margin: .8em 0; padding-left: 1.6em; }
.post-body li { margin: .3em 0; }
.post-body blockquote {
  margin: 1em 0; padding: 8px 14px;
  border-left: 3px solid var(--accent); background: var(--accent-soft);
  color: var(--text-dim); border-radius: 0 var(--radius) var(--radius) 0;
}
.post-body blockquote p { margin: .2em 0; }
.post-body .md-img {
  display: block; max-width: 100%; height: auto;
  margin: 1.2em auto; border-radius: var(--radius);
}
.post-body .md-code {
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 14px;
  overflow-x: auto; margin: 1em 0;
}
.post-body .md-code code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; color: var(--text);
}
.post-body .md-code-inline {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .88em; color: var(--accent);
  background: var(--bg-soft); padding: 2px 6px; border-radius: 4px;
}

/* 编辑器 */
.editor-card { padding: 18px; }
.ed-toolbar { display: flex; align-items: center; gap: 10px; margin: 4px 0 8px; flex-wrap: wrap; }
.ed-tip { font-size: 12px; color: var(--text-dim); }
.ed-textarea {
  width: 100%; min-height: 380px; resize: vertical;
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius); color: var(--text);
  padding: 14px; font-size: 14px; line-height: 1.8;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  box-sizing: border-box;
}
.ed-textarea:focus { outline: none; border-color: var(--accent); }
.ed-preview { margin-top: 16px; padding: 16px; background: var(--bg-soft); border: 1px dashed var(--border); border-radius: var(--radius); }
.ed-actions { margin-top: 16px; }

/* 插图面板（相册选图 / 上传新图） */
.bp-modal {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(0, 0, 0, .6);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.bp-panel {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); width: min(680px, 100%);
  max-height: 82vh; display: flex; flex-direction: column;
}
.bp-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.bp-tabs { display: flex; gap: 6px; }
.bp-tab {
  background: none; border: 1px solid transparent; color: var(--text-dim);
  padding: 6px 12px; border-radius: 999px; cursor: pointer; font-size: 13.5px;
}
.bp-tab.active { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.bp-close {
  background: none; border: 0; color: var(--text-dim);
  font-size: 22px; line-height: 1; cursor: pointer; padding: 4px 8px;
}
.bp-close:hover { color: var(--text); }
.bp-body { overflow-y: auto; padding: 16px; }
.bp-pane[hidden] { display: none; }
.bp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.bp-cell {
  position: relative; padding: 0; border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; cursor: pointer;
  background: var(--bg-soft); aspect-ratio: 1;
}
.bp-cell:hover { border-color: var(--accent); }
.bp-cell img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 25%; display: block; }
.bp-cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: 10.5px; padding: 3px 6px;
  background: rgba(0, 0, 0, .55); color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: left;
}
.bp-upload-btn { display: inline-block; }
.bp-up-status { margin-top: 10px; font-size: 13px; color: var(--text-dim); }
.bp-up-status:empty { display: none; }

@media (max-width: 768px) {
  /* 列表卡改竖排 —— 对应 Butterfly 在 ≤768px 的 flex-direction:column +
     封面 height:230px、封面 order 归 0 */
  .post-card { flex-direction: column; height: auto; }
  .post-card.flip > .post-card-cover { order: 0; }
  .post-card-cover { width: 100%; height: 210px; }
  .post-card-body, .post-card.no-cover .post-card-body { width: 100%; padding: 18px 18px 22px; }
  .post-card-title { font-size: 18px; margin-bottom: 8px; }
  .post-card-summary { margin-top: 8px; }

  /* 正文页 */
  .post-hero { height: 240px; min-height: 0; }
  .post-hero-inner { padding: 18px 18px; }
  .post-hero-inner h1 { font-size: 21px; }
  .post-page { padding: 20px 16px 24px; }
  .post-head h1 { font-size: 22px; }
  .post-nav { grid-template-columns: minmax(0, 1fr); }
  .post-nav-item.next { text-align: left; }
}

/* ============================================================
 * 月历（日程页 #/schedule，2026-09-30 改版）
 * ============================================================ */
.cal-head { display: flex; align-items: center; justify-content: space-between; margin: 18px 0 10px; }
.cal-nav {
  background: none; border: 1px solid var(--border); color: var(--text);
  width: 36px; height: 36px; border-radius: var(--radius); font-size: 20px;
  line-height: 1; cursor: pointer;
}
.cal-nav:hover { border-color: var(--accent); color: var(--accent); }
.cal-title { display: flex; align-items: center; gap: 12px; font-size: 16px; font-weight: 600; letter-spacing: 1px; }
.cal-today {
  background: none; border: 1px solid var(--border); color: var(--text-dim);
  font-size: 12px; padding: 3px 12px; border-radius: 999px; cursor: pointer;
}
.cal-today:hover { color: var(--accent); border-color: var(--accent); }
.cal-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-bottom: 6px; }
.cal-week span { text-align: center; font-size: 12px; color: var(--text-dim); padding: 4px 0; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-cell {
  display: flex; flex-direction: column; gap: 4px; min-height: 86px;
  /* min-width:0 —— grid 子项默认 min-width:auto，长的预约名会把格子撑破导致横向溢出 */
  min-width: 0;
  padding: 6px 6px 5px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--bg-card);
}
.cal-cell.is-out { opacity: .55; }
.cal-cell.is-today { border-color: rgba(var(--accent-rgb), .5); }
.cal-num { font-size: 13px; font-weight: 600; }
.cal-books { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cal-book {
  font-size: 10.5px; color: var(--text); background: var(--bg-soft);
  border-radius: 3px; padding: 1px 4px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.cal-loc {
  align-self: flex-start; background: none; border: 1px dashed var(--border);
  color: var(--text-dim); font-size: 10.5px; padding: 1px 6px; border-radius: 3px;
  cursor: pointer; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-loc:hover { color: var(--accent); border-color: var(--accent); }
.cal-loc.has-note { border-style: solid; color: var(--accent); border-color: rgba(var(--accent-rgb), .4); }
.cal-halfbar { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-top: auto; }
.cal-half {
  border: 1px solid var(--border); background: var(--bg-soft); color: var(--text);
  font-size: 11px; padding: 3px 0; border-radius: 4px; cursor: pointer; text-align: center;
  white-space: nowrap;
}
.cal-half:hover { border-color: var(--accent); }
.cal-half.st-free { border-color: rgba(var(--ok-rgb), .4); color: var(--ok); }
.cal-half.st-full { border-color: rgba(var(--danger-rgb), .4); color: var(--danger); }
.cal-half.st-busy { border-color: rgba(var(--neutral-rgb), .5); color: var(--neutral-text); }
.cal-half.st-past, .cal-half.st-closed { opacity: .45; color: var(--text-dim); }
.sc-dot.st-note { background: var(--accent); }

@media (max-width: 768px) {
  .cal-grid { gap: 4px; }
  .cal-cell { min-height: 62px; padding: 4px 3px 3px; gap: 3px; }
  .cal-num { font-size: 12px; }
  .cal-half { font-size: 10px; padding: 2px 0; }
  .cal-book { font-size: 9.5px; padding: 0 3px; }
  .cal-loc { font-size: 9.5px; padding: 0 4px; }
}

/* ============================================================
 * 约拍页月历选日期（#/booking，2026-09-30 新增）
 * ============================================================ */
.bk-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.bk-cal-nav {
  background: none; border: 1px solid var(--border); color: var(--text);
  width: 32px; height: 32px; border-radius: var(--radius); font-size: 18px;
  line-height: 1; cursor: pointer;
}
.bk-cal-nav:hover { border-color: var(--accent); color: var(--accent); }
.bk-cal-title { font-size: 14px; font-weight: 600; letter-spacing: .5px; }
.bk-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 6px; }
.bkcal-cell {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 6px 2px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-card); cursor: pointer;
  color: var(--text); /* button 元素默认文字是黑色，必须显式继承主题色 */
}
.bkcal-cell:hover { border-color: var(--accent); }
.bkcal-cell.is-out { opacity: .55; }
.bkcal-cell.is-out.is-dis { opacity: .3; }
.bkcal-cell.is-today { border-color: rgba(var(--accent-rgb), .5); }
.bkcal-cell.is-sel { border-color: var(--accent); background: var(--accent-soft); }
.bkcal-cell.is-dis { opacity: .4; cursor: not-allowed; }
.bkcal-num { font-size: 13px; font-weight: 600; }
.bkcal-dots { display: flex; gap: 3px; }
.bkcal-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--neutral); }
.bkcal-dot.st-free { background: var(--ok); }
.bkcal-dot.st-full { background: var(--danger); }
.bkcal-dot.st-busy { background: var(--neutral-text); }
.bkcal-dot.st-past, .bkcal-dot.st-closed { background: var(--neutral); }
.bk-cal-selected { font-size: 12.5px; color: var(--accent); min-height: 18px; margin-bottom: 4px; }

/* ============================================================
 * 用户管理（#/contacts，2026-09-30 新增）
 * ============================================================ */
.ct-search {
  background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
  padding: 7px 12px; border-radius: var(--radius); font-size: 13px; min-width: 200px;
}
.ct-search:focus { outline: none; border-color: var(--accent); }
.ct-tag {
  font-size: 11px; color: var(--accent); border: 1px solid rgba(var(--accent-rgb), .4);
  padding: 1px 6px; border-radius: 3px;
}
.bk-danger { color: var(--danger); }
@media (max-width: 640px) {
  .ct-search { min-width: 100%; }
}

/* ============================================================
 * 约拍权利义务告知书（#/notice，2026-09-30 新增）
 * ============================================================ */
.notice-body { font-size: 15px; line-height: 1.95; color: var(--text); word-break: break-word; }
.notice-body .notice-lead { color: var(--text-dim); font-size: 14px; }
.notice-body h2 {
  font-size: 18px; margin: 30px 0 14px; padding-bottom: 10px;
  border-bottom: 1px solid var(--border); letter-spacing: 1px;
}
.notice-body h2:first-of-type { margin-top: 4px; }
.notice-body p { margin: .7em 0; }
.notice-body ol, .notice-body ul { margin: .6em 0; padding-left: 1.7em; }
.notice-body li { margin: .5em 0; }
.notice-body strong { color: var(--text); }
.notice-law {
  background: var(--bg-soft); border: 1px solid var(--border);
  border-left: 3px solid var(--accent); border-radius: 6px;
  padding: 12px 16px; margin: 12px 0;
}
.notice-law .law-name { font-weight: 600; letter-spacing: .5px; }
.notice-law .law-art { color: var(--text-dim); font-size: 13.5px; margin-top: 6px; line-height: 1.8; }
.notice-tip {
  background: var(--accent-soft); border: 1px solid rgba(var(--accent-rgb), .35);
  border-radius: 6px; padding: 12px 16px; margin: 14px 0;
}
.notice-tip b { color: var(--accent); }

/* ============================================================
   光影动效（2026-10-01 三轮）
   第一轮「克制版」→ 第二轮「加强版」→ 同日第三轮：哥否掉了所有
   基于「光扫过 / 光晕呼吸」的做法（原话「我不喜欢这种光线的效果」），
   改指 Hexo Butterfly 的粒子背景。故本节的微光扫字、首屏光晕**全部删除**，
   背景改由 js/particles.js 的 canvas 粒子层承担（④）。
   保留的只有与「光」无关的两项：卡片悬停放大（②）、卡片入场（③）。
   统一受 prefers-reduced-motion 约束。
   ============================================================ */

/* ① 站名：保持纯文字色，不做任何动画。
   （微光扫字已删。曾有的坑留档：渐变若挂在整行 h1 上，background-size
   的百分比按 1100px 的块级宽度算，光带会比整行字还宽、变成「四个字
   一起变色」——万一将来要加回，必须把渐变挂在文字宽的 inline-block 上。）*/
.hero h1 > span { white-space: nowrap; }   /* 站名不折行 */
/* 其余 hero 尺寸（全屏高度、打字机、滚动指示、白字态）见上方「首页 Hero」主块，
   这里只留与动效有关的补充，避免同一条规则两处定义、改一处忘一处。 */

/* ② 封面悬停：图片轻微放大 + 金边 + 投影下沉 + 轻微提亮。
   初版是在照片上扫一道白光带 —— 哥实测「效果不好」：在照片上像糊了一层雾，
   与照片内容互相打架。改成放大 + 金边：指向明确、幅度看得见，
   也是摄影站更常见的做法。只动 transform / border-color / box-shadow / filter。
   卡片自身已 overflow:hidden（见第 210 行），所以图片放大不会溢出圆角。

   ⚠ 卡片自身的 transition / :hover 定义在「作品集卡片（首页）」那一段，
   这里**不要再写一遍** —— 曾经的重复定义见该段注释。 */
.portfolio-card .portfolio-cover {
  transition: transform .65s cubic-bezier(.22,.61,.36,1), filter .45s ease;
}
.portfolio-card:hover .portfolio-cover {
  transform: scale(1.07);
  filter: brightness(1.08) saturate(1.1);
}

/* ⚠⚠ 滚动期间：指针下的元素不做位移与缩放（配套 js/app.js 的 is-scrolling）。
   浏览器在滚动过程中会持续重判 :hover —— 鼠标一动不动，卡片却在指针下方
   进进出出，每张都完整播一遍「上浮 7px + 封面放大 7%」的过渡。这与滚动
   方向无关，所以向下滚、向上滚都抖；看上去就是整个相册连同它上方的文字
   一起在抖。只掐 transform 与 filter（位移、缩放、提亮，都要重新光栅化），
   边框与投影保留 —— 滚动中仍能看出指针指着哪一张。停下 700ms 后自动恢复
   （这个延迟值踩过两回坑，见 js/app.js 里 initScrollHoverGuard 的注释②）。 */
body.is-scrolling .portfolio-card:hover { transform: none; }
body.is-scrolling .portfolio-card:hover .portfolio-cover { transform: none; filter: none; }
body.is-scrolling .gallery-photo:hover img { transform: none; }
body.is-scrolling .post-card:hover .post-card-cover img { transform: none; }

/* ③ 卡片入场：滚进视口就淡入上浮，同一屏的按顺序错开。
   隐藏态（.reveal）必须由 JS 添加 —— 写死在这里的话，脚本一挂就是整屏空白。 */
.portfolio-card.reveal { opacity: 0; }
.portfolio-card.reveal.in { animation: card-in .78s cubic-bezier(.22,.61,.36,1) both; }
@keyframes card-in {
  /* 位移 14 → 26px、并加一点缩放：初版幅度太小，哥「感觉不出来」 */
  from { opacity: 0; transform: translateY(26px) scale(.97); }
  55%  { opacity: 1; }
  to   { opacity: 1; transform: none; }
}

/* 系统开启「减弱动态效果」时全部静止。
   注意：粒子层不在这里处理 —— 它由 js/particles.js 判断后改画「静态星图」，
   CSS 管不到 canvas 内部。 */
@media (prefers-reduced-motion: reduce) {
  .portfolio-card, .portfolio-card .portfolio-cover { transition: none !important; }
  .portfolio-card:hover { transform: none; }
  .portfolio-card:hover .portfolio-cover { transform: none; filter: none; }
  .portfolio-card.reveal { opacity: 1 !important; animation: none !important; }
}

/* ④ 光影粒子背景层（canvas，逻辑在 js/particles.js）。
   这是哥要的「Butterfly 那个效果」：浮动光点 + 邻近连线 + 鼠标星座。

   z-index:-1 是刻意的 —— 和 Butterfly 的 canvas_nest 一样：
     · 页面底色（body 的 background 会传播到根画布层）先画；
     · 粒子层压在其上；
     · 正文/卡片再压在粒子层之上。
   好处是照片卡片（--bg-card 不透明）会把粒子完全挡住，粒子只在首屏、
   留白、以及页头毛玻璃后面露出来 —— 背景再热闹也不会糊在照片上抢戏。

   position:fixed + inset:0：不随滚动走，滚动时像是「一层悬浮的光尘」，
   而不是贴在页面上的一张贴图。
   pointer-events:none：绝不挡点击（鼠标交互是在 window 上监听的）。 */
#bg-particles {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
  display: block;
}

/* 手机端：页头里换成 40px 的汉堡按钮，页头高 69px；字号也要收，
   否则「光影精灵」四个字加 14px 字距会超出 390px 的屏宽。 */
@media (max-width: 768px) {
  /* ⚠ 改页头内边距 / 控件尺寸后必须重测这个值（实测方法见 UI-VERIFICATION.md）——
     它同时是 hero 的高度基准和 scroll-margin-top 的偏移量。写小了会被页头压住，
     写大了首屏就短一截、下一页会从底部露出一条边（首版手机端写 69px，
     实测只有 61px，截图里底部那道蓝边就是它）。 */
  :root { --header-h: 61px; }
  .hero { padding: 40px 24px 88px; }
  .hero-typed { font-size: 14px; letter-spacing: 2px; margin-top: 16px; }
  .hero-scroll { font-size: 11px; letter-spacing: 2px; bottom: 22px; }
}

/* 系统开启「减弱动态效果」：滚动指示不再上下浮动、光标不再闪 */
@media (prefers-reduced-motion: reduce) {
  .hero-scroll { animation: none; }
  .typed-caret { animation: none; opacity: 1; }
}

/* ============================================================
   后台 · 站点设置（2026-10-01）
   结构见 js/admin.js 的 renderSettings()。
   只用现有 token，不引入新颜色 —— 两套主题下都得能看。
   ============================================================ */
.set-block {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 22px;
}
.set-block h2 { font-size: 18px; font-weight: 600; margin-bottom: 6px; }
.set-block > .hint { display: block; color: var(--text-dim); font-size: 13px; line-height: 1.7; margin-bottom: 16px; }
.set-block .hint b { color: var(--text); }

/* 模式单选：整块可点，选中态用主色描边 + 淡底，比默认小圆点好认 */
.set-modes { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; margin-bottom: 18px; }
.set-mode {
  display: flex; gap: 10px; align-items: flex-start;
  border: 1px solid var(--border); border-radius: 6px;
  padding: 12px 14px; cursor: pointer;
  background: var(--ghost);
  transition: border-color .18s ease, background .18s ease;
}
.set-mode:hover { border-color: var(--border-strong); }
.set-mode.on { border-color: var(--accent); background: var(--accent-soft); }
.set-mode input { margin-top: 3px; flex: 0 0 auto; accent-color: var(--accent); }
.set-mode-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.set-mode-body b { font-size: 14px; font-weight: 600; }
.set-mode-body i { font-style: normal; font-size: 12px; color: var(--text-dim); line-height: 1.55; }

.set-fixed { border-top: 1px dashed var(--border); padding-top: 16px; }
.set-preview {
  width: 100%; max-width: 460px; aspect-ratio: 16 / 9;
  border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
  background: var(--bg-soft);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 12px;
}
.set-preview img { width: 100%; height: 100%; object-fit: cover; }
.set-ops { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.set-field { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.set-field span { flex: 0 0 auto; font-size: 13px; color: var(--text-dim); }
.set-field input {
  flex: 1; min-width: 0;
  padding: 8px 12px; font-size: 13px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--text);
}
.set-field input:focus { outline: none; border-color: var(--accent); }
.set-actions { display: flex; align-items: center; gap: 12px; margin-top: 16px; }
.set-actions .hint { color: var(--text-dim); font-size: 13px; }
.set-textarea {
  width: 100%; padding: 12px 14px;
  font-size: 14px; line-height: 1.9; font-family: inherit; resize: vertical;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--text);
}
.set-textarea:focus { outline: none; border-color: var(--accent); }

/* 选图弹层（从作品集里挑一张当首屏背景） */
.picker-mask {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, .55);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.picker {
  width: 100%; max-width: 980px; max-height: 86vh;
  display: flex; flex-direction: column;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.picker-head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px; border-bottom: 1px solid var(--border);
}
.picker-head b { font-size: 15px; }
.picker-head .hint { flex: 1; color: var(--text-dim); font-size: 12px; }
.picker-grid {
  flex: 1; overflow-y: auto;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  /* ⚠⚠ 这一行不能删（2026-10-01 事故：哥报「点开后一堆图片乱七八糟地挤在一起」）。
     本容器是 flex:1 的子项 → 在「.picker 有 max-height」时拿到**确定高度**；
     而卡片的高完全来自 aspect-ratio（得先知道列宽才算得出来，属于循环依赖），
     隐式行的默认 auto 尺寸于是量不出卡片高度。最终效果是**行高被强行摊平**：
     用来铺满网格高度的每一行 = (网格高 − 全部 gap) ÷ 行数。
     120 张时算出 20 多像素一行、而卡片实高 114px → 137 张全部叠成一片。
     max-content 不属于可拉伸的 auto 轨道，行老老实实取卡片高度，多出来的
     部分交给 overflow-y 滚动。别改回 auto、也别删，改了先跑
     .workbuddy/tools/ui-check-picker.mjs。 */
  grid-auto-rows: max-content;
  gap: 8px; padding: 14px;
}
.picker-item {
  padding: 0; aspect-ratio: 4 / 3; overflow: hidden;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg-soft);
}
.picker-item:hover { border-color: var(--accent); }
.picker-item img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 768px) {
  .set-block { padding: 16px; }
  .set-field { flex-direction: column; align-items: stretch; gap: 6px; }
  .picker-mask { padding: 12px; }
  .picker-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
}
