/* @import 必须排在所有其它规则之前（@charset / @layer 除外），否则整条被浏览器丢弃。
   这里曾经被下面那条 [hidden] 挡在后面，结果 Noto Serif SC 一直没加载成功——
   中文全部落到系统宋体，font-weight 改了也看不出变化。别再把它往下挪。 */
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&display=swap');

/* [hidden] 全局兜底。

   HTML 的 hidden 属性是靠浏览器默认样式表里的 [hidden]{display:none} 生效的，
   优先级极低——任何一条给元素设了 display 的作者样式都能把它架空，
   于是「明明 el.hidden = true 了，元素却还在页面上」。
   这个坑在本项目里已经踩过两次（高级筛选的把手、后台的登录页），
   而且症状很迷惑：元素没消失，只是被挤到别处，看着像布局错乱。
   一条全局规则永久解决。 */
[hidden] {
  display: none !important;
}


/* 卡片主标题专用字体：仅收录当前 53 件作品标题里出现过的字（子集化，
   原字库 26.5MB → 140KB），新作品若用到子集外的字会自动回退到青鸟/宋体。 */
/* 首页与页脚那行「天津师范大学新闻学系」用的粗衬线。
   自托管的 9 字子集，来历与重新生成的方法见 fonts/NotoSerifSC-来源说明.md。

   unicode-range 是关键：它让这份字体**只**接管这 9 个字，
   其余汉字照常落到后面的字体上——否则一份只有 9 个字形的字体
   会把整段中文都吞掉，缺字的地方全变成豆腐块。 */
@font-face {
  font-family: "Noto Serif SC";
  src: url("fonts/noto-serif-sc-600-subset.woff2") format("woff2");
  /* 字重跟着 .hero-dept 走。子集是按字重切的，两边必须对上——
     CSS 里写 600 而这里只有 700 的话，这 9 个字就不命中本地子集了，
     会掉回 Google 那份（国内多半拉不到，最后显示成系统宋体）。 */
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+5927, U+5929, U+5b66, U+5e08, U+65b0, U+6d25, U+7cfb, U+8303, U+95fb;
}

@font-face {
  font-family: "ZhaohuaTitleB";
  src: url("fonts/zhaohua-title-b-subset.woff2") format("woff2"),
       url("fonts/zhaohua-title-b-subset.ttf") format("truetype");
  font-display: swap;
}

/* 站点拉丁字体 —— 软宋（Ruan Song）。
   Text 光学尺寸系列（针对正文/UI 字号优化），
   覆盖 Light / Regular / Medium / Semibold / Bold / Extrabold + 对应斜体。
   unicode-range 锁在拉丁区，中文不受影响。 */
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-LightItalic.woff2") format("woff2");
  font-weight: 300;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-RegularItalic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-MediumItalic.woff2") format("woff2");
  font-weight: 500;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-Semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-SemiboldItalic.woff2") format("woff2");
  font-weight: 600;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-BoldItalic.woff2") format("woff2");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-Extrabold.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "SiteLatin";
  src: url("fonts/RuanSong-Text-ExtraboldItalic.woff2") format("woff2");
  font-weight: 800;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}


:root {
  color-scheme: light;
  --ink: #170f0b;
  --muted: #5f4c45;
  --paper: rgba(250, 245, 240, 0.68);
  --paper-soft: rgba(245, 232, 224, 0.65);
  --paper-strong: rgba(255, 252, 248, 0.82);
  --line: rgba(255, 255, 255, 0.42);
  --line-dark: rgba(67, 34, 20, 0.2);
  --red: #bb3f3c;
  --red-deep: #8c1f1e;
  --cream: #fff6ee;
  --radius: 25px;
  --glass-blur: blur(18px) saturate(132%);
  /* --paper-strong 压平后的实色版本。作品卡和卡片正文用它顶替毛玻璃，
     省掉侧边栏收展时每帧重算几十层模糊（见 .work-card / .work-body） */
  --paper-flat: #fdf9f5;
  --shadow: 0 14px 36px rgba(52, 18, 7, 0.26);
  --inset: inset 0 1px 0 rgba(255, 255, 255, 0.78), inset 0 -16px 28px rgba(135, 73, 48, 0.08);
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", "STSong", serif;
}

/* ===================== 主题切换的过渡 =====================
   两条路，能走哪条走哪条：

   1) View Transitions（Chrome 111+ / Safari 18+）：整页交叉淡入淡出，
      由合成器一次性完成，页面上有几十张卡也不影响，这是首选。

   2) 退化方案 html.theme-anim：给所有元素临时挂一条颜色过渡。
      用通配符 + !important 是因为站里几百条规则各自写了自己的 transition，
      不强制盖不住。代价是这 0.5 秒内别的过渡（hover 位移之类）会被顶掉——
      只在手动切主题的那一下发生，且切完立刻摘掉，可以接受。
      注意只过渡颜色和 filter，不碰 transform/opacity：
      那两个正在被别的动画用着，一起接管会打架。

   首次进页面不走这里——防闪烁脚本在 CSS 生效前就把主题定好了，
   没有"从亮变暗"这个过程可动。 */
@media (prefers-reduced-motion: no-preference) {
  /* 页面换色不和光同时起步：延到光的峰值之后（约 11% 处）才开始，
     用 62% 的时长走完。读起来才是「光把颜色带出来的」，
     而不是光和颜色各走各的两件事——这是调参台上最能去掉"生硬"的一处。

     这里用的是普通 CSS 过渡，不是 View Transitions。后者要把整页拍成快照，
     而快照里 backdrop-filter 不生效——站里十九处毛玻璃会在切换那一下集体失去模糊，
     最显眼的就是顶部那条磨砂带闪一下没了。用 transition 就没有这个问题。
     代价是这 0.6 秒内所有元素都挂着一条过渡（下面用了通配符 + !important，
     因为站里几百条规则各写各的 transition，不强制盖不住），切完立刻摘掉。
     只过渡颜色和 filter，不碰 transform/opacity——那两个正被别的动画用着。 */
  html.theme-anim *,
  html.theme-anim *::before,
  html.theme-anim *::after {
    transition:
      background-color 0.37s cubic-bezier(0.33, 0, 0.2, 1) 0.066s,
      background-image 0.37s cubic-bezier(0.33, 0, 0.2, 1) 0.066s,
      color 0.37s cubic-bezier(0.33, 0, 0.2, 1) 0.066s,
      border-color 0.37s cubic-bezier(0.33, 0, 0.2, 1) 0.066s,
      box-shadow 0.37s cubic-bezier(0.33, 0, 0.2, 1) 0.066s,
      filter 0.37s cubic-bezier(0.33, 0, 0.2, 1) 0.066s !important;
  }

  /* ── 明暗切换的那束顶光 ──
     视觉语言沿用站里已有的 .viewer-flash / topLightSweep：顶部中央一枚柔和的
     暖白椭圆锥，边缘靠 gradient 自己化开，不用任何硬边。
     （早先试过用 clip-path 把页面裁成楔形，硬边三角，跟手电筒不沾边，已废弃。）

     「集中 → 发散」用 scaleX 表达而不是改 gradient 尺寸：transform 走合成器，
     改 background-size 每帧都要重画整块渐变。transform-origin 钉在 50% 0，
     张开时光源不动、只有下方铺开——这是「光从上面打下来」和「从中间炸开」的区别。

     下面五个数值是在调参台上试出来的，别凭感觉改：
       时长 600ms · 起始集中 0.08 · 散开 2.15 · 强度 0.30 · 余晖 1.40×
     （强度只有 0.3，是一道很淡的光；起始 0.08 极窄，开灯那一下是真的"一束"） */
  .theme-beam {
    --beam-dur: 600ms;
    --beam-from: 0.08;
    --beam-to: 2.15;
    --beam-peak: 0.30;
    --beam-tail: 1.40;

    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 100vh;
    height: 100lvh;
    z-index: 9999;
    pointer-events: none;
    opacity: 0;
    overflow: hidden;
  }

  .theme-beam::before {
    content: "";
    position: absolute;
    inset: 0;
    transform-origin: 50% 0;
    background: radial-gradient(
      ellipse 52% 82% at 50% -6%,
      rgba(255, 247, 231, 0.95) 0%,
      rgba(255, 242, 219, 0.5) 20%,
      rgba(255, 237, 208, 0.16) 40%,
      transparent 62%
    );
  }

  /* 整屏一点点提亮，让"灯亮了"不只发生在顶部那一块 */
  .theme-beam::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(255, 250, 240, 0.06);
    opacity: 0;
  }

  /* 开灯：一束集中的光落下 → 散开铺满 → 长长地退去 */
  .theme-beam.beam-on { animation: beamFade calc(var(--beam-dur) * var(--beam-tail)) both; }
  .theme-beam.beam-on::before { animation: beamSpread var(--beam-dur) cubic-bezier(0.17, 0.84, 0.28, 1) both; }
  .theme-beam.beam-on::after { animation: beamLift calc(var(--beam-dur) * var(--beam-tail)) both; }

  /* 关灯：光先在场，收拢成一束，然后灭 */
  .theme-beam.beam-off { animation: beamFadeOff calc(var(--beam-dur) * var(--beam-tail) * 0.92) both; }
  .theme-beam.beam-off::before { animation: beamCollapse calc(var(--beam-dur) * 0.9) cubic-bezier(0.36, 0, 0.28, 1) both; }

  /* 张开到 74% 时略过冲再回落，光才像有惯性；blur 同步变大，
     因为光散开时边缘本来就该越来越糊 */
  @keyframes beamSpread {
    0%   { transform: scaleX(var(--beam-from)) scaleY(0.78); filter: blur(1px); }
    38%  { transform: scaleX(calc(var(--beam-to) * 0.80)) scaleY(1.04); filter: blur(5px); }
    74%  { transform: scaleX(calc(var(--beam-to) * 1.05)) scaleY(1.21); filter: blur(10px); }
    100% { transform: scaleX(var(--beam-to)) scaleY(1.16); filter: blur(13px); }
  }

  /* 包络逐帧指定缓动：点亮走快出曲线，熄灭走长尾曲线。
     整段比 beamSpread 长（余晖 1.35×）——两件事不在同一帧收尾，
     「同时结束」正是生硬的主因。 */
  @keyframes beamFade {
    0%   { opacity: 0;                              animation-timing-function: cubic-bezier(0.15, 0.85, 0.3, 1); }
    11%  { opacity: var(--beam-peak);               animation-timing-function: linear; }
    34%  { opacity: calc(var(--beam-peak) * 0.94);  animation-timing-function: cubic-bezier(0.45, 0, 0.7, 0.8); }
    72%  { opacity: calc(var(--beam-peak) * 0.34);  animation-timing-function: cubic-bezier(0.3, 0, 0.75, 1); }
    100% { opacity: 0; }
  }

  @keyframes beamLift {
    0%   { opacity: 0; animation-timing-function: cubic-bezier(0.15, 0.85, 0.3, 1); }
    16%  { opacity: 1; animation-timing-function: cubic-bezier(0.4, 0, 0.7, 0.9); }
    100% { opacity: 0; }
  }

  @keyframes beamCollapse {
    0%   { transform: scaleX(calc(var(--beam-to) * 0.88)) scaleY(1.14); filter: blur(12px); }
    46%  { transform: scaleX(calc(var(--beam-to) * 0.46)) scaleY(0.96); filter: blur(7px); }
    100% { transform: scaleX(0.13) scaleY(0.58); filter: blur(2px); }
  }

  @keyframes beamFadeOff {
    0%   { opacity: 0;                              animation-timing-function: cubic-bezier(0.15, 0.85, 0.3, 1); }
    13%  { opacity: calc(var(--beam-peak) * 0.92);  animation-timing-function: cubic-bezier(0.5, 0, 0.72, 0.75); }
    58%  { opacity: calc(var(--beam-peak) * 0.42);  animation-timing-function: cubic-bezier(0.3, 0, 0.7, 1); }
    100% { opacity: 0; }
  }
}

/* ===================== 暗色主题 =====================
   表面走中性黑灰（近似 Apple 的 systemGray 深色阶），不带棕；
   暖色只留给强调色（--red 系）和背景照本身。
   一层一层往上抬：#1c1c1e 底 → #2b2b2d 卡片 → #3a3a3c 悬浮/按钮。 */
[data-theme="dark"] {
  color-scheme: dark;
  --ink: #ececf0;
  --muted: #9a9aa0;
  --paper: rgba(44, 44, 46, 0.78);
  --paper-soft: rgba(50, 50, 52, 0.76);
  --paper-strong: rgba(36, 36, 38, 0.88);
  --paper-flat: #2b2b2d;
  --line: rgba(255, 255, 255, 0.07);
  --line-dark: rgba(255, 255, 255, 0.10);
  --cream: #2b2b2d;
  /* 暗色下的主红压深一档：#a04540 在深灰面板上偏亮、偏"粉"，
     红按钮反倒比亮色版本更抢眼，正好和暗色该有的克制相反 */
  --red: #8e3a36;
  --red-deep: #c46864;
  --shadow: 0 14px 36px rgba(0, 0, 0, 0.56);
  /* 只留顶边一道极淡的高光，不要底部内阴影——那道暗角会把整块面拆成上下两半 */
  --inset: inset 0 1px 0 rgba(255, 255, 255, 0.045);
}

/* 暗色下 body 背景 —— 中性深灰，不是纯黑 */
html[data-theme="dark"] {
  background-color: #1c1c1e;
}

[data-theme="dark"] body {
  background: #1c1c1e;
}

/* 背景图压暗。这里是两道叠加：filter 先压一次，::after 的黑罩再压一次，
   两道乘起来很容易把背景图干成纯黑（0.38 × 0.45 ≈ 0.17）。
   改动任意一边都要连着另一边一起看。 */
[data-theme="dark"] .page-bg {
  filter: brightness(0.62) saturate(0.72);
}

/* 暗色下顶部状态栏区域同步换成 #1c1c1e 实色带 */
@supports (-webkit-touch-callout: none) {
  [data-theme="dark"] .page-bg::before {
    background: linear-gradient(180deg,
      #1c1c1e 0,
      #1c1c1e env(safe-area-inset-top, 0px),
      rgba(28, 28, 30, 0.55) 65%,
      transparent 100%);
  }
}

/* 罩子用暖棕而不是纯黑：纯黑压在暖色照片上会把它拉灰，读起来像掉色而不是入夜 */
[data-theme="dark"] .page-bg::after {
  background-image:
    radial-gradient(circle at 50% 18%, rgba(60, 40, 26, 0.22), transparent 34%),
    linear-gradient(180deg, rgba(24, 16, 10, 0.28), rgba(24, 16, 10, 0.46));
}

/* --- 暗色下各组件微调 --- */

/* 顶部磨砂条（浅底半透明 → 深底半透明） */
[data-theme="dark"] body::after {
  background: linear-gradient(180deg, rgba(28, 28, 30, 0.85) 0%, rgba(28, 28, 30, 0.6) 25%, rgba(28, 28, 30, 0.2) 50%, transparent 75%);
  border-bottom-color: rgba(255, 255, 255, 0.06);
}

/* 搜索框 */
[data-theme="dark"] .search-box input {
  background: rgba(44, 44, 46, 0.78);
  border-color: rgba(255, 255, 255, 0.1);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  color: var(--ink);
}
[data-theme="dark"] .search-box input::placeholder {
  color: rgba(235, 235, 245, 0.45);
}

/* 联系行 */
[data-theme="dark"] .contact-row {
  background: #2b2b2d;
  border-color: rgba(255, 255, 255, 0.08);
}
[data-theme="dark"] .contact-row .c-ico {
  background: rgba(187, 63, 60, 0.2);
}

/* 通知公告页样式 */
[data-theme="dark"] .news-item,
[data-theme="dark"] .announcement-card {
  background: rgba(44, 44, 46, 0.75);
}

/* 筛选栏及其他浅色表面 */
[data-theme="dark"] .filter-bar,
[data-theme="dark"] .sort-toggle,
[data-theme="dark"] .award-tabs,
[data-theme="dark"] .award-chiprow,
[data-theme="dark"] .timeline-node {
  background: rgba(44, 44, 46, 0.78);
}

/* --- 暗色下的卡片：要一整块，不要"相框" ---
   .work-card 的 6px 边框在亮色下是有意的卡纸相框（米白），
   暗色里再怎么调半透明白都只会变成一圈灰边。这里把边框刷成和卡面同色，
   6px 就当作卡面自己的一圈留白消失掉了。
   （不直接改成 1px 是因为 .thumb 的内圆角写死了 calc(var(--radius) - 6px)，
   动边框宽度就得连着改圆角，多一处要对齐的地方。） */
[data-theme="dark"] .work-card {
  border-color: var(--paper-flat);
}
/* 正文区与卡面同色，只留一丝极淡的自上而下过渡 */
[data-theme="dark"] .work-body {
  background: linear-gradient(180deg, #2e2e30, #2b2b2d);
}
[data-theme="dark"] .work-card p {
  color: #a1a1a6;
}
[data-theme="dark"] .thumb {
  background: #242426;
}
/* 没有缩略图的作品走 .placeholder，它的底是米白 rgba(246,232,224,.72)——
   暗色下不盖掉就是一整块刺眼的白 */
[data-theme="dark"] .placeholder {
  background: #242426;
}
/* 卡片悬浮阴影原本是暖棕红，暗色下换成中性黑，免得卡片周围泛出一圈棕 */
[data-theme="dark"] .work-card:hover {
  box-shadow: 0 24px 56px rgba(0, 0, 0, 0.5), var(--inset);
}
/* 缩略图加蒙版降低亮度 */
[data-theme="dark"] .thumb img {
  filter: brightness(0.82) saturate(0.85);
}
[data-theme="dark"] .work-card:hover .thumb img {
  filter: brightness(0.92) saturate(0.9);
}

/* 标签 */
[data-theme="dark"] .tag {
  background: rgba(160, 69, 64, 0.22);
  border-color: rgba(196, 104, 100, 0.25);
  color: var(--red-deep);
}

/* 按钮 */
[data-theme="dark"] .button {
  background: #3a3a3c;
  color: var(--red-deep);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
[data-theme="dark"] .button.primary {
  background: var(--red);
  color: #fff0ec;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}
[data-theme="dark"] .button.primary:hover {
  background: #a3453f;
}
[data-theme="dark"] .icon-button:hover {
  background: #3a3a3c;
}

/* 获奖选中态 */
[data-theme="dark"] .award-chip.is-selected {
  border-color: var(--red-deep);
  background: rgba(160, 69, 64, 0.3);
  color: var(--red-deep);
}

/* --- 筛选药丸的文字与箭头 ---
   药丸的标签不在按钮自己身上，而在 .filters span 里，那条规则写死了 color:#120c09。
   只给 .filter-pill 换色管不到它——文字仍然是近黑压在深底上，等于看不见。
   下拉箭头同理：它是 ::after 里一张 stroke='%231c120d' 的内联 SVG，也得换一张浅色的。 */
[data-theme="dark"] .filters span {
  color: var(--ink);
}
[data-theme="dark"] .filter-pill::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ececf0' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* --- 二级筛选面板（媒介面板）---
   面板由 JS 挂到 body 下，整块底色是硬编码的米白毛玻璃，暗色下从来没被覆盖 */
[data-theme="dark"] .media-panel {
  background: rgba(36, 36, 38, 0.82);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
}
[data-theme="dark"] .mp-cats {
  background: rgba(28, 28, 30, 0.5);
}
[data-theme="dark"] .mp-cat:hover,
[data-theme="dark"] .mp-item:hover {
  background: rgba(255, 255, 255, 0.07);
}

/* --- 作品详情弹窗 ---
   面板整体是一张米色卡纸，里面还嵌一张预览卡，暗色下两层都要压下来：
   面板 #282828 一档，预览卡再深一档，才看得出"卡里嵌卡"的层次 */
[data-theme="dark"] .viewer-panel {
  background: rgba(40, 40, 42, 0.94);
}
[data-theme="dark"] .viewer-stage {
  background: rgba(28, 28, 30, 0.92);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
  filter: drop-shadow(0 10px 30px rgba(0, 0, 0, 0.45));
}
/* 亮色下压在模糊底衬上的是米色薄纱，暗色下要反过来压暗，
   否则一张亮底缩略图糊开之后整块预览卡会白得刺眼 */
[data-theme="dark"] .viewer-stage[data-mode="link"]::after {
  background: linear-gradient(180deg, rgba(18, 18, 20, 0.34), rgba(18, 18, 20, 0.62));
}
[data-theme="dark"] .viewer-stage[data-mode="link"]::before {
  filter: blur(36px) saturate(1.2) brightness(0.8);
}
/* 磨砂牌现在是 .tray-meta（奖牌已挪出去单独站着，不再共用这层玻璃） */
[data-theme="dark"] .viewer-stage[data-mode="doc"] .tray-meta {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(38, 38, 40, 0.72);
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
/* 磨砂操作条：暗色下同样只留一层意思，别在深底上现出一块矩形 */
[data-theme="dark"] .viewer-meta-footer {
  background: none;
}
[data-theme="dark"] .icon-button {
  background: rgba(58, 58, 60, 0.88);
  border-color: rgba(255, 255, 255, 0.14);
  color: var(--ink);
}
[data-theme="dark"] .share-button {
  background: rgba(58, 58, 60, 0.85);
  border-color: rgba(255, 255, 255, 0.14);
}
[data-theme="dark"] .share-button:hover {
  background: rgba(72, 72, 74, 0.9);
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.45);
}
/* 手机端整张卡片的环境色薄纱：亮色是米色，暗色要反过来压暗，
   否则一张亮底缩略图糊开之后整张上拉卡片会白得刺眼 */
@media (max-width: 820px) {
  [data-theme="dark"] .viewer-panel::after {
    background: linear-gradient(180deg,
      rgba(18, 18, 20, 0.58) 0%,
      rgba(18, 18, 20, 0.74) 46%,
      rgba(18, 18, 20, 0.86) 100%);
  }
  [data-theme="dark"] .viewer-panel::before {
    filter: blur(52px) saturate(1.3) brightness(0.75);
  }
  [data-theme="dark"] .viewer-stage[data-mode="doc"] {
    background: rgba(28, 28, 30, 0.92);
  }
}
[data-theme="dark"] .text-button {
  background: rgba(58, 58, 60, 0.78);
}

/* --- 其余几处同样漏掉的浅底面板 --- */
[data-theme="dark"] .gs-panel {
  background: rgba(36, 36, 38, 0.82);
}
[data-theme="dark"] .an-item {
  background: rgba(44, 44, 46, 0.94);
}
[data-theme="dark"] .an-item.is-important {
  background: rgba(52, 52, 54, 0.97);
}
[data-theme="dark"] .ac-chip {
  background: rgba(58, 58, 60, 0.78);
}
[data-theme="dark"] .ac-chip:hover {
  background: rgba(72, 72, 74, 0.9);
}
[data-theme="dark"] .adv-chip {
  background: rgba(58, 58, 60, 0.45);
}
[data-theme="dark"] .adv-seg {
  background: rgba(58, 58, 60, 0.6);
}
[data-theme="dark"] .adv-panel {
  background: rgba(40, 40, 42, 0.96);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
}

/* 侧边栏搜索 placeholder */
[data-theme="dark"] .sb-search-input::placeholder {
  color: rgba(235, 235, 245, 0.55);
}

/* 筛选药丸：去掉浅底，换深底浅字 */
[data-theme="dark"] .filter-pill {
  background: rgba(58, 58, 60, 0.82);
  border-color: rgba(255, 255, 255, 0.12);
  color: var(--ink);
}
[data-theme="dark"] .filter-pill:hover {
  background: rgba(72, 72, 74, 0.88);
}
[data-theme="dark"] .filter-pill[aria-expanded="true"] {
  background: rgba(84, 84, 88, 0.92);
}

/* 二级筛选胶囊 */
[data-theme="dark"] .tray-item {
  background: rgba(52, 52, 54, 0.82);
  border-color: rgba(255, 255, 255, 0.10);
  color: var(--ink);
}
[data-theme="dark"] .tray-item:hover {
  background: rgba(70, 70, 73, 0.88);
  color: #ececf0;
}

/* 排序切换 */
[data-theme="dark"] .sort-toggle {
  background: rgba(58, 58, 60, 0.82);
  border-color: rgba(255, 255, 255, 0.12);
  color: var(--ink);
}
[data-theme="dark"] .sort-toggle:hover {
  background: rgba(72, 72, 74, 0.88);
}

/* 最近浏览标题：去掉阴影、提高亮度 */
[data-theme="dark"] .recent-strip-title {
  color: rgba(255, 248, 240, 0.95);
  text-shadow: none;
}
[data-theme="dark"] .recent-clear {
  color: rgba(255, 248, 240, 0.78);
}
[data-theme="dark"] .recent-clear:hover {
  color: #e8a09a;
}

html {
  min-height: 100%;
  background-color: #9f4d25;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

* {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  margin: 0;
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
  color: var(--ink);
  background: #9f4d25;
  font-synthesis: none;
  -webkit-font-synthesis: none;
}

/* ===================== 键盘焦点指示 =====================
   全站原先一条 :focus-visible 都没有，几处 input 还写了 outline:0 且无替代，
   用键盘走一遍完全不知道自己在哪。统一给一圈高对比描边环。
   用 :focus-visible 而不是 :focus：鼠标点击不会触发，只在键盘导航时出现。 */
:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 3px;
  border-radius: 4px;
}

/* 深底上的控件（侧边栏、移动菜单、页脚）用浅色环，红色在深红底上看不出来 */
.sidebar :focus-visible,
.mobile-menu :focus-visible,
.mobile-topbar :focus-visible,
.site-footer :focus-visible,
.award-tabs :focus-visible,
.award-chiprow :focus-visible {
  outline-color: #fff6ee;
}

/* 这几个自己有圆角，焦点环跟着走一圈才不别扭 */
.button:focus-visible,
.text-button:focus-visible,
.search-submit:focus-visible,
.award-tab:focus-visible,
.award-chip:focus-visible,
.filter-pill:focus-visible,
.icon-button:focus-visible,
.gyro-ask-yes:focus-visible,
.gyro-ask-no:focus-visible {
  border-radius: 999px;
}

/* 输入框本来就把 outline 关了并用 box-shadow 画了聚焦态，别再叠一圈 */
.search-box input:focus-visible {
  outline: none;
}

/* 标题类：汉字收紧间距，数字/英文加宽。
   字重不在这里定——按钮和导航要粗体，各自的规则说了算。 */
/* .hero h1 原本也在这张单子里，一并摘掉：首页主标题的字距
   由 .hero-name 自己定（中文要松、英文要紧），不该被这条统一值管。 */
.section-heading h2,
.about-section h2,
.work-card h3,
.viewer-meta h3 {
  letter-spacing: -0.06em;
  word-spacing: 0.12em;
}

.nav-links,
.button,
.text-button,
.search-submit {
  letter-spacing: -0.06em;
  word-spacing: 0.12em;
}

/* 顶部渐变模糊带：仅在搜索工具栏吸顶后出现（JS 切换 body.has-top-blur） */
body::after {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 25;
  width: 100vw;
  height: calc(env(safe-area-inset-top, 0px) + 140px);
  content: "";
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.35s ease;
  background: linear-gradient(180deg, rgba(159, 77, 37, 0.18) 0%, rgba(159, 77, 37, 0.1) 25%, rgba(159, 77, 37, 0.04) 50%, transparent 75%);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0.7) 20%, rgba(0, 0, 0, 0.15) 50%, transparent 75%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0.7) 20%, rgba(0, 0, 0, 0.15) 50%, transparent 75%);
  border-bottom: 1px solid rgba(159, 77, 37, 0.15);
}

body.has-top-blur::after {
  opacity: 1;
}

/* 仅 iOS 的 Safari 本体（sidebar.js 按 UA 打标）：顶部模糊带与 Safari 自身的 chrome 磨砂叠加显乱，禁用；
   微信等其他 iOS 浏览器保持正常 */
html.ios-safari body::after {
  display: none;
}




.page-bg {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 0;
  width: 100vw;
  /* 锚定最大视口（Safari 工具栏收起时的全屏高度）：
     不随工具栏收展改变尺寸，背景图钉死全屏、盖住状态栏后方 */
  height: 100vh;
  height: 100lvh;
  pointer-events: none;
  background: url("index pictures/background.png") center center / cover no-repeat;
  transform: translateZ(0);
}

/* 灵动岛/刘海机型：把背景照最顶上那一条盖成实色，避免状态栏后面是一块
   忽明忽暗的报纸纹理。

   注意这条带子只管背景照——它在 .page-bg（z-index:0）里，而正文 main 是
   z-index:1，压在它上面。所以「状态栏区域透出正文」不是靠这条解决的，
   那件事只有顶栏（z-index:60）盖得住，见 .mobile-topbar 的第一段色标。
   两处各管一半，缺一不可：没有这条，顶栏没铺到的地方会露出纹理；
   没有顶栏那段，正文会直接透上来。

   仅针对 iOS/WebKit（-webkit-touch-callout 是 iOS 特有属性）。 */
@supports (-webkit-touch-callout: none) {
  .page-bg::before {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1;
    height: calc(env(safe-area-inset-top, 0px) + 90px);
    content: "";
    background: linear-gradient(180deg,
      #9f4d25 0,
      #9f4d25 env(safe-area-inset-top, 0px),
      rgba(159, 77, 37, 0.55) 65%,
      transparent 100%);
    pointer-events: none;
  }
}

.page-bg::after {
  position: absolute;
  inset: 0;
  content: "";
  background-image:
    radial-gradient(circle at 50% 18%, rgba(255, 236, 216, 0.18), transparent 34%),
    linear-gradient(180deg, rgba(58, 16, 5, 0.08), rgba(80, 24, 7, 0.28));
}

a {
  color: inherit;
  text-decoration: none;
}

button,
input,
select {
  font: inherit;
}

button,
select {
  cursor: pointer;
}

img {
  display: block;
  max-width: 100%;
}

.site-header {
  position: relative;
  z-index: 1;
  min-height: auto;
  padding-top: env(safe-area-inset-top, 0px);
  color: var(--cream);
}

.topbar {
  position: absolute;
  top: 0;
  left: 32px;
  right: 32px;
  z-index: 20;
  width: auto;
  padding: 50px 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.brand {
  color: rgba(255, 246, 238, 0.88);
  font-size: 18px;
  opacity: 0;
  pointer-events: none;
}

.nav-links {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: end;
  padding: 6px;
  border-radius: 999px;
  border: 1px solid rgba(255, 246, 238, 0.28);
  background: rgba(60, 22, 10, 0.26);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 6px 20px rgba(38, 12, 4, 0.28);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  color: #fff8ef;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 19px;
  font-weight: 700;
}

.nav-links a {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 9px 20px;
  border-radius: 999px;
  color: rgba(255, 248, 240, 0.92);
  letter-spacing: 0.02em;
  transition: color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.nav-links a:hover {
  color: #fffaf5;
  background: rgba(255, 248, 240, 0.16);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22);
  transform: translateY(-1px);
}

.nav-links a:active {
  transform: translateY(0) scale(0.97);
  background: rgba(255, 248, 240, 0.1);
}

.hero {
  width: min(1220px, calc(100% - 48px));
  margin: 0 auto;
  padding: 170px 0 0;
  display: grid;
  align-content: start;
  justify-items: center;
}

.hero-copy {
  text-align: center;
  text-shadow: 0 6px 22px rgba(50, 15, 4, 0.48);
}

.kicker {
  margin: 0 0 20px;
  font-size: clamp(30px, 3.2vw, 48px);
  line-height: 1.15;
}

/* 首页主标题的样式现在全在 .hero-dept / .hero-name 那两条里。
   原来这里还有一条 .hero h1：它的特异性（0,1,1）比 .hero-name（0,1,0）高，
   会把新规则整条压掉——留着就是个隐形陷阱，直接删。 */

/* 首页主标题：两行文字，不再是 title.png。
   字体按语言分工——中文两行是两副字（上行粗衬线、下行朝华），
   英文两行都用站点的拉丁字 SiteLatin，靠字重拉开层次。
   分工写在下面 html[lang="en"] 那组里，i18n.js 会把 lang 挂到 <html> 上。 */
/* 标题块定高，让中英两版占同样的垂直空间。
   不定高就会跳：中文下行是 78px 的一行（87px 高），英文是 58px 的两行（130px 高），
   上行的差（56 vs 35）只抵消一部分，净剩 22px——切一次语言，
   下面的检索栏就往下窜一次，很难看。

   为什么不靠调字号让英文也排成一行：那需要把英文压到 48px 上下，
   两行标题的体量就完全不对等了，而且窄屏一样会折。定高最直接，
   代价只是中文时多出一点上下留白——hero 本来就是留白区，看不出来。 */
.hero-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: clamp(132px, 16.5vw, 196px);
}

.hero-dept,
.hero-name {
  margin: 0 auto;
  text-align: center;
  color: #fff8ef;
  text-shadow: 0 6px 20px rgba(50, 15, 4, 0.42);
}

.hero-dept {
  font-family: "Noto Serif SC", "SiteLatin", serif;
  font-weight: 600;
  font-size: clamp(21px, 3.3vw, 39px);
  letter-spacing: 0.04em;
  /* 字距是往右加的，整行会因此右偏半个字距——补回来才真居中 */
  text-indent: 0.04em;
  opacity: 0.92;
}

.hero-name {
  margin-top: clamp(6px, 1vw, 14px);
  font-family: "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-weight: 400;
  font-size: clamp(38px, 7.4vw, 78px);
  line-height: 1.12;
  letter-spacing: 0.02em;
  text-indent: 0.02em;
}

/* 英文：两行同一副拉丁字，用字重和字号分主次——下面那行更粗。
   英文比中文长得多，字号要压下来，字距也不需要中文那么松。 */
html[lang="en"] .hero-dept {
  font-family: "SiteLatin", serif;
  font-weight: 400;
  font-size: clamp(17px, 2.2vw, 28px);
  letter-spacing: 0.02em;
  text-indent: 0.02em;
}

html[lang="en"] .hero-name {
  font-family: "SiteLatin", serif;
  /* 700 压到 600：RuanSong 的 Bold 在这个字号下太重，压住了整屏 */
  font-weight: 600;
  font-size: clamp(30px, 4.6vw, 58px);
  letter-spacing: 0.005em;
  text-indent: 0;
}

.toolbar-shell {
  position: sticky;
  top: calc(env(safe-area-inset-top, 0px) + 24px);
  z-index: 30;
  width: min(820px, 100%);
  margin: 56px auto 18px;
  transition: opacity 0.38s ease, transform 0.38s ease;
  /* 注意：这里不能加 will-change: opacity / filter 之类的属性，
     否则会成为子元素的 backdrop root，筛选框和按钮的高斯模糊会失效 */
}

.toolbar-shell::after {
  display: none;
}

.toolbar-shell.is-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-16px);
  visibility: hidden;
}

.scroll-hint {
  position: fixed;
  left: 50%;
  bottom: 80px;
  z-index: 15;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 246, 238, 0.24);
  background: rgba(60, 22, 10, 0.22);
  color: #fff8ef;
  opacity: 0.72;
  transform: translateX(-50%);
  transition: opacity 0.4s ease;
  animation: scrollHintBob 1.9s ease-in-out infinite;
}

.scroll-hint::after {
  content: "";
  width: 13px;
  height: 13px;
  margin-top: -5px;
  border: solid currentColor;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.scroll-hint:hover {
  opacity: 1;
}

@keyframes scrollHintBob {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 7px); }
}

body.is-scrolled .scroll-hint {
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .scroll-hint {
    animation: none;
  }
  .work-card {
    transition: opacity 0.2s ease;
    transform: none !important;
  }
  .work-card:hover {
    transform: none !important;
  }
  .fadeSlideIn {
    animation: none !important;
    opacity: 1 !important;
  }
}

/* ===================== 独立页面 ===================== */
.page-wrap {
  position: relative;
  z-index: 1;
  width: min(960px, calc(100% - 48px));
  margin: 0 auto;
  padding: 118px 0 96px;
}

.page-header {
  margin-bottom: 34px;
}

.page-header .eyebrow {
  color: #ffe1cc;
}

/* 页首大标题：中文在前、英文在后，同一行「新闻公告 /NEWS & NOTICE」。
   两者各自的字号字体都不动（h1 是 clamp 出来的 40–66px 朝华，
   .eyebrow 是 13px 的拉丁体），靠 baseline 对齐把小字坐到大字的基线上——
   用 center 会让 13px 浮在半空。窄屏放不下时英文整段换行，不硬挤。 */
.page-title-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12px;
}

.page-title-row h1,
.page-title-row .eyebrow {
  margin: 0;
}

.page-title-row .eyebrow::before {
  content: "/";
}

.page-header h1 {
  margin: 6px 0 0;
  color: #fff8ef;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: clamp(40px, 5vw, 66px);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.03em;
  text-shadow: 0 5px 18px rgba(46, 14, 5, 0.4);
}

.page-lead {
  max-width: 640px;
  margin: 18px 0 0;
  color: rgba(255, 246, 238, 0.9);
  font-size: 18px;
  line-height: 1.9;
}

.panel {
  /* 通条列表（.news-item）要靠负外边距顶到托盘两侧，得知道这里的内边距是多少；
     写成变量，窄屏改一处两边同时跟上，不会各改各的对不齐 */
  --panel-pad: 34px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  box-shadow: var(--shadow), var(--inset);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  padding: var(--panel-pad);
}

.panel + .panel,
.about-creator-card + .about-creator-card {
  margin-top: 22px;
}

.panel h2 {
  margin: 0 0 14px;
  color: var(--ink);
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 26px;
  font-weight: 400;
  letter-spacing: -0.03em;
}

.panel p {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 16px;
  line-height: 1.95;
}

.panel p:last-child {
  margin-bottom: 0;
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-top: 22px;
}

.feature-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  box-shadow: var(--shadow);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  padding: 24px;
}

.feature-card .f-ico {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  background: rgba(187, 63, 60, 0.12);
  color: var(--red-deep);
  margin-bottom: 14px;
}

.feature-card .f-ico svg {
  width: 22px;
  height: 22px;
}

.feature-card h3 {
  margin: 0 0 6px;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 18px;
  font-weight: 400;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.feature-card p {
  margin: 0;
  color: var(--muted);
  font-size: 14.5px;
  line-height: 1.75;
}

.page-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-top: 22px;
}

.page-stats div {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  box-shadow: var(--shadow);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  padding: 22px 24px;
}

.page-stats strong {
  display: block;
  color: var(--red-deep);
  font: 800 34px/1.05 "SiteLatin", serif;
}

.page-stats span {
  display: block;
  margin-top: 8px;
  color: var(--muted);
  font-size: 15px;
}

/* 联系方式 */
.contact-list {
  display: grid;
  gap: 14px;
  margin-top: 4px;
}

.contact-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 20px;
  border: 1px solid rgba(120, 60, 40, 0.12);
  border-radius: var(--radius);
  background: #fbf3ec;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

a.contact-row:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(53, 20, 8, 0.14);
}

.contact-row .c-ico {
  flex: none;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 13px;
  background: rgba(187, 63, 60, 0.12);
  color: var(--red-deep);
}

.contact-row .c-ico svg {
  width: 23px;
  height: 23px;
}

.contact-row .c-label {
  display: block;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.04em;
}

.contact-row .c-value {
  display: block;
  margin-top: 3px;
  color: var(--ink);
  font-size: 17px;
}

.contact-note {
  margin-top: 18px;
  color: rgba(255, 246, 238, 0.7);
  font-size: 14px;
  line-height: 1.8;
}

/* 版权声明 */
.site-footer {
  position: relative;
  z-index: 1;
  width: 100%;
  margin: 64px 0 0;
  padding: 44px 32px calc(48px + env(safe-area-inset-bottom, 0px));
  color: var(--cream);
  background: rgba(122, 44, 16, 0.5);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border-top: 1px solid rgba(255, 246, 238, 0.14);
}

.footer-inner {
  width: min(1320px, 100%);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.site-footer p {
  margin: 0;
}

/* 页脚这两行和首页主标题同一套字体分工，只是尺寸小一号 */
.footer-dept {
  font-family: "Noto Serif SC", "SiteLatin", serif;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: rgba(255, 246, 238, 0.72);
}

.footer-title {
  margin-top: 6px !important;
  font-family: "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-weight: 400;
  font-size: 26px;
  letter-spacing: 0.02em;
  color: #fff8ef;
}

/* 英文页脚：同首页——两行都用 SiteLatin，下面那行更粗 */
html[lang="en"] .footer-dept {
  font-family: "SiteLatin", serif;
  font-weight: 500;
  letter-spacing: 0.03em;
}

html[lang="en"] .footer-title {
  font-family: "SiteLatin", serif;
  font-weight: 600;
  font-size: 22px;
  letter-spacing: 0;
}

.footer-legal {
  text-align: right;
  font-size: 13px;
  line-height: 2;
  color: rgba(255, 246, 238, 0.7);
}

.footer-nav {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 16px;
  font-size: 15px;
  color: rgba(255, 246, 238, 0.92);
}

.footer-nav a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.footer-nav svg {
  width: 17px;
  height: 17px;
}

.footer-sep {
  color: rgba(255, 246, 238, 0.35);
}

@media (max-width: 640px) {
  .site-footer {
    margin-top: 48px;
    padding: 40px 24px calc(44px + env(safe-area-inset-bottom, 0px));
    text-align: center;
  }

  .footer-inner {
    flex-direction: column;
    gap: 0;
  }

  .footer-dept {
    display: none;
  }

  .footer-title {
    margin-top: 0 !important;
    font-size: 24px;
  }

  .footer-legal {
    margin-top: 14px;
    text-align: center;
  }

  .footer-nav {
    display: flex;
    margin-top: 22px;
  }
}



.copyright-author {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 16px;
  letter-spacing: -0.02em;
  color: rgba(255, 246, 238, 0.9);
}

.copyright-author + .copyright-author {
  margin-top: 4px;
}

@media (max-width: 720px) {
  .feature-grid,
  .page-stats {
    grid-template-columns: 1fr;
  }

  .page-wrap {
    padding-top: 96px;
  }

  .panel {
    --panel-pad: 24px;
  }
}

/* ===================== 侧边栏 ===================== */
:root {
  --sb-w: 238px;
  /* 71 是量出来的，不是拍的：71 − 左右各 12px 内边距 − 1px 右边框 = 46 的内容宽，
     收起键（34px）在里面居中，圆心落在 12 + 23 = 35px；
     导航图标（24px，左内边距 11px）圆心同样是 12 + 11 + 12 = 35px；
     展开态图标（内边距 11px）也是 35px。
     于是收起键、六个图标、展开态图标三者共用一条中线，收起时图标一步不动。 */
  --sb-w-collapsed: 71px;
}

body.has-sidebar .scroll-hint {
  left: calc(50% + var(--sb-w) / 2);
}
body.has-sidebar.sidebar-collapsed .scroll-hint {
  left: calc(50% + var(--sb-w-collapsed) / 2);
}

/* ===== 收起 / 展开 =====
   推开式侧边栏，正文宽度是真的在变，所以每帧都得重排——这躲不掉。
   之前之所以卡，不是因为重排本身，而是重排时还要连带重算 25 张作品卡的
   backdrop-filter（见 .work-card 那条注释）。把卡片的毛玻璃去掉之后，
   每帧就只剩普通的块级重排，动画才真的跑得动。

   曲线用前重后轻的 (0.32,0.72,0,1)：位移在前 40% 就走掉大半，
   最贵的那几帧集中在开头，收尾时已经几乎不动了。 */
body.has-sidebar {
  padding-left: var(--sb-w);
  transition: padding-left 0.26s cubic-bezier(0.32, 0.72, 0, 1);
}

/* 侧边栏由 JS 注入，首次挂载那一下不要动画，否则每次开页面都看见它推一遍 */
body.sb-no-anim,
body.sb-no-anim * {
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  body.has-sidebar {
    transition: none;
  }
}

body.has-sidebar.sidebar-collapsed {
  padding-left: var(--sb-w-collapsed);
}

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60;
  width: var(--sb-w);
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: 16px 12px;
  color: #fff5ec;
  border-right: 1px solid rgba(255, 246, 238, 0.16);
  box-shadow: 2px 0 22px rgba(30, 10, 3, 0.26);
  /* 和 body 的 padding-left 同一条曲线、同一个时长——两边必须是一个整体在动 */
  transition: width 0.26s cubic-bezier(0.32, 0.72, 0, 1), transform 0.3s ease;
  overflow: hidden;
}

/* 毛玻璃底单独拆成一层，宽度恒为展开宽度。
   backdrop-filter 的采样区域跟着元素自身几何走，让它随栏宽变化就得重算整块模糊；
   固定宽度之后，收起时父级只是把它裁窄，模糊结果可以直接复用。 */
.sidebar::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--sb-w);
  background: rgba(46, 17, 8, 0.42);
  backdrop-filter: blur(18px) saturate(120%);
  -webkit-backdrop-filter: blur(18px) saturate(120%);
  pointer-events: none;
}

/* backdrop-filter 会开新的层叠上下文，内容得显式抬到毛玻璃之上。
   往 .sidebar 里加新的直接子元素时，记得一并加进这个选择器，
   否则它会被 ::before 那层盖住——看不见，但因为 ::before 不吃事件，还点得到 */
.sidebar-head,
.sidebar-nav,
.sb-theme-row {
  position: relative;
  z-index: 1;
}

.sidebar.is-collapsed {
  width: var(--sb-w-collapsed);
}

.sidebar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 4px 12px;
  margin-bottom: 8px;
  border-bottom: 1px solid rgba(255, 246, 238, 0.12);
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  color: #fff8ef;
}

.sidebar-brand-mark {
  flex: none;
  width: 100%;
  max-width: 36px;
  display: block;
  background: transparent;
  box-shadow: none;
}

.sidebar-brand-mark img {
  width: 100%;
  height: auto;
  object-fit: contain;
  filter: brightness(1.15) drop-shadow(0 2px 8px rgba(50, 15, 4, 0.35));
}

.sidebar-toggle,
.sb-caret,
.sidebar-opener {
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.sidebar-toggle {
  flex: none;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 9px;
  color: rgba(255, 246, 238, 0.82);
  transition: background 0.18s ease;
}

.sidebar-toggle:hover {
  background: rgba(255, 248, 240, 0.14);
}

.sidebar-toggle svg {
  width: 20px;
  height: 20px;
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 3px;
  overflow-y: auto;
  overflow-x: hidden;
}

.sb-item,
.sb-parent-link,
.sb-sub-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 11px;
  border-radius: 11px;
  color: rgba(255, 246, 238, 0.86);
  /* 目录条目按标题待遇走朝华，和站内各处主标题同一副面孔；
     拉丁字仍由 SiteLatin 先接（朝华只做中文标题） */
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 16.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
  transition: background 0.18s ease, color 0.18s ease;
}

a.sb-item:hover,
.sb-parent-link:hover,
.sb-sub-item:hover {
  background: rgba(255, 248, 240, 0.12);
  color: #fff9f3;
}

.sb-ico {
  flex: none;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
}

.sb-ico svg {
  width: 21px;
  height: 21px;
}

.sb-ico-sm svg {
  width: 13px;
  height: 13px;
  opacity: 0.85;
}

/* 展开时文字滑入。
   收展本身（栏宽 + 正文左边距）是瞬间的——那是布局，一做过渡就得整页逐帧重排，
   之前几版卡就卡在这儿。但文字这一层是纯合成的：opacity + translateX 不碰布局，
   多少个都不掉帧。所以动效没有取消，只是从"整条栏慢慢推"换成了
   "栏一步到位、字跟着滑进来"——这一下就够交代状态变化了。 */
.sb-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity 0.22s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.sidebar:not(.is-collapsed) .sb-label {
  animation: sbLabelIn 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* 逐项错开一点，像依次落位，而不是整块一起闪出来 */
.sidebar:not(.is-collapsed) .sb-item:nth-child(2) .sb-label { animation-delay: 0.02s; }
.sidebar:not(.is-collapsed) .sb-item:nth-child(3) .sb-label { animation-delay: 0.04s; }
.sidebar:not(.is-collapsed) .sb-item:nth-child(4) .sb-label { animation-delay: 0.06s; }
.sidebar:not(.is-collapsed) .sb-item:nth-child(5) .sb-label { animation-delay: 0.08s; }
.sidebar:not(.is-collapsed) .sb-group .sb-label { animation-delay: 0.1s; }

@keyframes sbLabelIn {
  from {
    opacity: 0;
    transform: translateX(-10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar:not(.is-collapsed) .sb-label {
    animation: none;
  }
}

.sb-item.is-active,
.sb-parent.is-active .sb-parent-link,
.sb-sub-item.is-active {
  background: rgba(187, 63, 60, 0.92);
  color: #fffaf5;
  box-shadow: 0 4px 12px rgba(120, 25, 23, 0.34);
}

.sb-group {
  display: flex;
  flex-direction: column;
}

.sb-parent {
  display: flex;
  align-items: center;
  padding: 0;
  border-radius: 11px;
}

.sb-parent-link {
  flex: 1;
  min-width: 0;
}

.sb-caret {
  flex: none;
  width: 32px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 9px;
  color: rgba(255, 246, 238, 0.66);
}

.sb-caret svg {
  width: 16px;
  height: 16px;
  transition: transform 0.22s ease;
}

.sb-group.is-open .sb-caret svg {
  transform: rotate(90deg);
}

.sb-sub {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.28s ease;
}

.sb-group.is-open .sb-sub {
  max-height: 480px;
}

.sb-sub-item {
  padding: 8px 11px 8px 12px;
  margin: 2px 0 2px 15px;
  font-size: 15px;
  font-weight: 700;
  color: rgba(255, 246, 238, 0.74);
  border-left: 1px solid rgba(255, 246, 238, 0.16);
  border-radius: 0 10px 10px 0;
}

.sidebar.is-collapsed .sidebar-brand,
.sidebar.is-collapsed .sb-caret,
.sidebar.is-collapsed .sb-sub {
  display: none;
}

/* 收起时不用 justify-content:center 去挤图标——那样文字还在流里，
   会把图标顶偏，而且文字一旦 display:none 就是"啪"地消失。
   改成保持左对齐、给一个恰好让图标落在栏中线的左内边距，
   文字原地淡出、由侧栏的 overflow:hidden 顺势裁掉，图标一步不动。 */
.sidebar.is-collapsed .sb-item,
.sidebar.is-collapsed .sb-parent-link {
  align-items: center;
  height: 44px;
  padding: 0 0 0 11px;
  margin: 0;
}

.sidebar.is-collapsed .sb-label {
  opacity: 0;
}

/* 「作品库」是 .sb-item.sb-parent 里再套一个 .sb-parent-link，
   两层都会吃到上面那条 11px 左内边距，图标被推到 46px 去。外层清零。 */
.sidebar.is-collapsed .sb-parent {
  padding-left: 0;
}

/* 收起时收起键要和下面一列图标共用一条中线。
   原来对不齐有两个叠加原因：
   1) .sidebar-brand 只是把里面的 logo 设成 display:none，自己还带着 flex:1，
      仍然占着一整格，把按钮往右挤；
   2) .sidebar-head 左右各 4px 内边距，而导航项收起后 padding 归零。
   两处一起去掉，按钮中心才落回 34px（= 68px 栏宽的一半）。 */
.sidebar.is-collapsed .sidebar-head {
  justify-content: center;
  gap: 0;
  padding-left: 0;
  padding-right: 0;
}

/* 主题切换按钮 */
.sb-theme-row {
  margin-top: auto;
  padding: 8px 14px 6px;
  display: flex;
  justify-content: flex-end;
}
/* 规格对齐上面的 .sb-item：无边框、透明底、同一条 hover 曲线和同一个圆角，
   只是它是纯图标所以做成正方形 */
.sb-theme-btn {
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 11px;
  background: transparent;
  color: rgba(255, 246, 238, 0.86);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background 0.18s ease, color 0.18s ease;
}
.sb-theme-btn:hover {
  background: rgba(255, 248, 240, 0.12);
  color: #fff9f3;
}
.sb-theme-btn svg { width: 20px; height: 20px; }
/* 收起时内容宽只剩 47px，右对齐会把 38px 的按钮推到圆心 40px 处，
   偏离收起键和六个导航图标共用的 35px 中线；这里改成居中对齐归位 */
.sidebar.is-collapsed .sb-theme-row {
  padding: 6px 0;
  justify-content: center;
}

@media (max-width: 900px) {
  body.has-sidebar,
  body.has-sidebar.sidebar-collapsed {
    padding-left: 0;
  }

  .sidebar {
    display: none;
  }

  /* 箭头避开底部 tab 栏 */
  .scroll-hint {
    bottom: 130px;
  }
}

.toolbar {
  position: relative;
  z-index: 1;
  width: min(820px, 100%);
  margin: 0 auto;
  display: grid;
  /* 按钮列从 134 收到 120：这一列让出多少，1fr 的输入框就长多少，
     "按钮窄一点、输入框长一点"是同一处改动的两面。
     列间距一并从 14 收到 12，再多让 2px 给输入框。 */
  grid-template-columns: minmax(0, 1fr) 120px;
  row-gap: 16px;
  column-gap: 12px;
  align-items: start;
  padding: 0;
  transition: filter 0.22s ease;
}

.toolbar::before {
  display: none;
}

.search-box {
  position: relative;
  z-index: 5;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  align-self: stretch;
}

.search-box input,
.search-submit {
  width: 100%;
  /* 58 → 56.8（-2%） */
  height: 56.8px;
  min-height: 56.8px;
  border-radius: 999px;
}

.search-box input {
  border: 1px solid rgba(76, 46, 35, 0.22);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(12px) saturate(120%);
  -webkit-backdrop-filter: blur(12px) saturate(120%);
  color: var(--ink);
  padding: 0 24px;
  font-size: 18px;
  box-shadow: 0 2px 10px rgba(53, 20, 8, 0.10);
  outline: 0;
  transition: box-shadow 0.18s ease, border-color 0.18s ease;
}

.search-box input:focus {
  border-color: var(--red);
  box-shadow: 0 2px 10px rgba(53, 20, 8, 0.10), 0 0 0 3px rgba(187, 63, 60, 0.3);
}

.search-box input::placeholder {
  color: rgba(80, 68, 63, 0.45);
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fbf1e8;
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(53, 20, 8, 0.12);
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
  transform-origin: center;
}

.button.primary {
  min-height: 54px;
  border-color: transparent;
  background: var(--red);
  color: #fffaf5;
  font-size: 16px;
  box-shadow: 0 4px 14px rgba(140, 31, 30, 0.28);
}

.button.primary:hover {
  background: #c94b48;
}

.button:hover,
.text-button:hover,
.filter-pill:hover {
  transform: translateY(-2px);
}

.button:active,
.text-button:active,
.search-submit:active,
.tray-item:active {
  transform: translateY(0) scale(0.97);
  transition: transform 80ms ease-out;
}

.button.primary.search-submit {
  position: relative;
  z-index: 5;
  align-self: stretch;
  margin-left: 0;
  border-radius: 999px;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-weight: 700;
  /* 同上，跟着 -2% */
  min-height: 56.8px;
  height: 56.8px;
  box-shadow: 0 4px 14px rgba(140, 31, 30, 0.28);
}

.toolbar .search-submit {
  font-size: 22px;
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-weight: 600;
}

/* 筛选栏：固定四个药丸；媒介药丸点击弹出二级面板 */
.filters {
  position: relative;
  z-index: 1;
  grid-column: 1 / -1;
  margin: 0 -9px;
  display: flex;
}

.filters > .filter-pill {
  flex: 1 1 0;
}

/* ===================== 媒介二级面板 =====================
   面板由 JS 挂载到 body 下（脱离工具栏的层叠上下文），
   桌面端 fixed 定位、位置由 JS 按筛选栏实时计算 */
.media-panel {
  position: fixed;
  z-index: 200;
  width: min(520px, calc(100vw - 32px));
  max-height: 60vh;
  display: flex;
  border: 1px solid var(--line);
  border-radius: 22px;
  /* 毛玻璃：--paper-strong（0.82）几乎不透光，底下那张报纸底图全被挡住，
     面板看着就是一块糊在页面上的死板子。压到 0.72 让材质真的透出来，
     模糊只给 14px —— 再高就糊成一片灰、跟实色没区别了，
     saturate 补回被模糊冲淡的暖色。 */
  background: rgba(255, 252, 248, 0.72);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 18px 48px rgba(40, 12, 4, 0.28);
  overflow: hidden;
  opacity: 0;
  transform: translateX(-50%) translateY(-8px) scale(0.97);
  transform-origin: top center;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 0.22s ease,
    transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    visibility 0s linear 0.3s;
}

.media-panel.is-open {
  opacity: 1;
  transform: translateX(-50%);
  pointer-events: auto;
  visibility: visible;
  transition: opacity 0.22s ease,
    transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    visibility 0s;
}

.mp-cats {
  width: 150px;
  flex: none;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-right: 1px solid var(--line-dark);
  background: rgba(246, 234, 225, 0.5);
}

.mp-items {
  flex: 1;
  min-width: 0;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.mp-cat,
.mp-item {
  flex: none;
  border: 0;
  border-radius: 12px;
  background: transparent;
  color: var(--ink);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 17px;
  line-height: 1.4;
  letter-spacing: -0.03em;
  text-align: left;
  /* 左边 28px 给多选的勾留位（见 .mp-check） */
  padding: 11px 15px 11px 28px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.16s ease, color 0.16s ease;
}

.mp-cat {
  padding-left: 15px;
}

/* 右列具体媒体用 Noto 正文字体，与左列分类的标题字体区分 */
.mp-item {
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0;
}

/* 多选：选中项前面出一个勾。用 gap + 固定宽度的勾位，
   选中和未选中的文字左边缘对齐，勾出现时整列不会左右跳 */
.mp-item {
  display: flex;
  align-items: center;
  gap: 7px;
}

.mp-item > span {
  overflow: hidden;
  text-overflow: ellipsis;
}

.mp-check {
  flex: none;
  width: 14px;
  height: 14px;
  margin-left: -21px;   /* 勾挂到文字左侧的空白里，不占文字的位置 */
}

/* 左列大类上的小圆点：这一类里有选中项，但当前没在浏览它 */
.mp-dot {
  position: absolute;
  top: 50%;
  right: 11px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--red);
  transform: translateY(-50%);
}

.mp-cat {
  position: relative;
  padding-right: 24px;
}

.mp-cat.is-selected .mp-dot,
.mp-cat.is-active .mp-dot {
  background: currentColor;
}

/* 平铺形态（类型只有三项，不值得为它分左右两列） */
.media-panel.is-flat {
  width: min(260px, calc(100vw - 32px));
}

.media-panel.is-flat .mp-cats {
  display: none;
}

.mp-cat:hover,
.mp-item:hover {
  background: rgba(187, 63, 60, 0.1);
}

.mp-cat.is-active {
  background: rgba(187, 63, 60, 0.14);
  color: var(--red-deep);
}

.mp-cat.is-selected,
.mp-item.is-selected {
  background: var(--red);
  color: #fffaf5;
}

.mp-overlay {
  display: none;
}

/* 四个筛选器的触发药丸。原来年级/类型/主题是 <label><select>、媒介是 <label>，
   现在统一成 <button>，所以要自己抹掉按钮的默认外观 */
.filter-pill {
  position: relative;
  min-width: 0;
  margin: 0 9px;
  height: 48px;
  padding: 0;
  display: grid;
  place-items: center;
  border: 1px solid rgba(120, 60, 40, 0.12);
  border-radius: 999px;
  background: rgba(246, 234, 225, 0.72);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  color: #120c09;
  box-shadow: 0 2px 8px rgba(54, 19, 7, 0.10);
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}

.filter-pill:hover {
  background: rgba(251, 243, 236, 0.78);
}

.filter-pill.is-active {
  border-color: var(--red);
  box-shadow: 0 0 0 1px var(--red), 0 2px 8px rgba(54, 19, 7, 0.10);
}

/* 面板打开时药丸下压一点，和面板形成"这个按钮正开着"的连带感 */
.filter-pill[aria-expanded="true"] {
  background: rgba(251, 243, 236, 0.9);
}

.filter-pill[aria-expanded="true"]::after {
  transform: translateY(-50%) rotate(180deg);
}

.filter-pill:disabled {
  opacity: 0.45;
  cursor: default;
}

.medium-tray {
  display: none;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 0 0;
  grid-column: 1 / -1;
}

.medium-tray.is-open {
  display: flex;
}

.tray-item {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  border: 1px solid rgba(120, 60, 40, 0.15);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
  color: var(--ink);
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-size: 14px;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
  white-space: nowrap;
  line-height: 1.4;
}

.tray-item:hover {
  background: rgba(187, 63, 60, 0.08);
  border-color: var(--red);
  color: var(--red-deep);
}

.tray-item.is-selected {
  background: var(--red);
  color: #fff;
  border-color: var(--red);
}

@media (max-width: 640px) {
  .medium-tray {
    gap: 4px;
    padding: 6px 0 0;
  }

  .tray-item {
    font-size: 13px;
    padding: 4px 10px;
  }
}


.filters span {
  position: absolute;
  left: 18px;
  right: 40px;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
  color: #120c09;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.06em;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 下拉箭头：CSS 重绘为干净的 V 形雪佛龙 */
.filter-pill::after {
  position: absolute;
  top: 50%;
  right: 15px;
  width: 13px;
  height: 13px;
  content: "";
  transform: translateY(-50%);
  background: center / contain no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231c120d' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  pointer-events: none;
  transition: transform 0.2s ease;
}

.filter-pill:hover::after {
  transform: translateY(-50%) translateY(1px);
}

main {
  position: relative;
  z-index: 1;
  width: min(1320px, calc(100% - 48px));
  margin: 0 auto;
  padding-bottom: 72px;
}

.stats-band {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 16px 0 0;
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.stats-band.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.stats-band div,
.class-card,
.about-section,
/* 注意：真正生效的 .viewer-panel 尺寸/底色规则在本文件后面（搜 border-radius: 32px），
   同权重、更靠后，会把这条覆盖掉。改面板外观请去改那一条，别只改这里。
   这里同样不写 backdrop-filter，理由见那边的长注释。 */
.viewer-panel {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  box-shadow: var(--shadow), var(--inset);
}

/* 作品卡不用毛玻璃 —— 这是侧边栏收展能有动画的前提。
   卡片是全站唯一会同时出现二三十个的毛玻璃元素，而 backdrop-filter 的采样区
   跟着元素几何走：正文区一变宽，25 张卡就要同时重算 25 次高斯模糊，
   收展动画的每一帧都得付这笔钱，怎么调曲线和时长都救不回来。
   而且这层模糊本来也几乎看不见：底色 --paper-strong 已经 0.82 不透明，
   只剩 18% 能透出去，透出来的还是一张本来就模糊的背景图。
   换成不透明实色，静态下看不出区别，代价却整个消失了。 */
.work-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-flat);
  box-shadow: var(--shadow), var(--inset);
}

.stats-band div {
  min-height: 114px;
  padding: 22px 24px;
}

.stats-band strong {
  display: block;
  color: var(--red-deep);
  font: 800 36px/1.05 "SiteLatin", serif;
}

.stats-band span {
  display: block;
  margin-top: 10px;
  color: var(--muted);
  font-size: 17px;
}

.section {
  padding-top: 18px;
}

.section-heading {
  margin-bottom: 28px;
}

/* 字号跟着 h1 一起缩放：h1 是 clamp(40px, 5vw, 66px)，这里按它约 4 成走。
   原来固定 13px 是竖排时代的遗产——那时它独占一行，13px 够看；
   改成和中文标题同排后，旁边就是 40px 以上的大字，13px 一下子显得很小。

   字重 500：13px 时代用 700 是为了在小字号下压住笔画不发虚，
   放大到 17–26px 后 700 就显得又黑又壮，压过旁边的中文标题了。
   （字体是 Inter，可变字重 100–900，这里任何值都有真实字形，不会被伪粗体糊掉。） */
.eyebrow {
  margin: 0 0 10px;
  color: #ffe1cc;
  font: 500 clamp(17px, 2vw, 26px)/1 "SiteLatin", serif;
  text-transform: uppercase;
}

.section-heading h2,
.about-section h2 {
  margin: 0;
  color: #fff8ef;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: clamp(38px, 4vw, 58px);
  line-height: 1.1;
  text-shadow: 0 5px 18px rgba(46, 14, 5, 0.4);
}

.section-heading p:last-child,
.about-section p {
  margin: 10px 0 0;
  color: rgba(255, 246, 238, 0.86);
  font-size: 18px;
  line-height: 1.85;
}

.result-line {
  width: min(1120px, 100%);
  margin: 0 auto 24px;
  display: flex;
  align-items: center;
  gap: 20px;
  color: #fff9ef;
  font-size: 26px;
  font-weight: 700;
  text-shadow: 0 4px 14px rgba(50, 15, 4, 0.45);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.62s ease, transform 0.62s ease;
}

.result-line.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.text-button {
  min-height: 30px;
  border: 1px solid var(--line);
  border-radius: 15px;
  background: rgba(246, 225, 218, 0.78);
  color: var(--red-deep);
  padding: 2px 14px;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 17px;
  font-weight: 700;
  box-shadow: 0 2px 8px rgba(54, 19, 7, 0.12);
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}

/* 列数跟着「容器」宽度走，而不是视口宽度：侧边栏展开/收起、时间轴缩进
   都会改变可用宽度，纯视口断点看不见这些，会把卡片压成窄条。
   轨道下限取 max(250px, 三等分)：容器够宽时正好三列（与原版一致），
   不够宽时退到 250px 下限，auto-fill 自动减列。 */
.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(250px, (100% - 68px) / 3)), 1fr));
  gap: 34px;
}

.work-card {
  min-height: 100%;
  display: grid;
  grid-template-rows: auto 1fr;
  overflow: hidden;
  border-width: 6px;
  border-color: rgba(255, 246, 238, 0.9);
  isolation: isolate;
  opacity: 0;
  transform: translateY(36px);
  transition: opacity 0.68s ease, transform 0.68s ease, box-shadow 0.22s ease;
}

.thumb {
  aspect-ratio: 16/11;
}

.work-card.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.work-card:hover {
  transform: translateY(-8px) scale(1.015);
  box-shadow: 0 24px 56px rgba(52, 18, 7, 0.38), var(--inset);
}

.thumb {
  overflow: hidden;
  background: rgba(230, 213, 203, 0.9);
  border-radius: calc(var(--radius) - 6px) calc(var(--radius) - 6px) 0 0;
}

.thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transition: transform 0.36s ease;
}

.work-card:hover .thumb img {
  transform: scale(1.04);
}

.placeholder {
  height: 100%;
  padding: 26px;
  display: grid;
  align-content: space-between;
  background: rgba(246, 232, 224, 0.72);
}

.placeholder strong {
  color: var(--red-deep);
  font: 800 44px/1 "SiteLatin", serif;
}

.placeholder span {
  color: var(--muted);
  line-height: 1.7;
}

/* 同 .work-card：这里也不能留 backdrop-filter。
   它在每张卡里各有一个，25 张卡就是 25 层模糊，
   正文区一变宽全都要重算——侧边栏收展卡顿的另一半在这儿。
   渐变本来就压着 0.62～0.68 的不透明度，底下透出来的又是同一张模糊背景图，
   换成同色系的不透明渐变，肉眼分不出来。 */
/* 底边 18px 原本是给"查看作品"按钮留的呼吸位，按钮没了就收回来，
   和左右 22px 一起收成一圈匀称的内边距。 */
.work-body {
  padding: 20px 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: linear-gradient(180deg, #fffbf6, #f6ece4);
}

.meta-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* 同排卡片是等高的（.work-card 的 min-height:100%），摘要短的那张会多出一段空间。
   让标签行吸住底边，把这段空间顶到它上面去——
   否则空间全落在标签下面，就是一块空下巴（"查看作品"按钮撤掉后暴露出来的问题）。

   必须限定 :last-child：获奖页的卡片把 .meta-line 放在标题**之前**（第一个子元素），
   那里加 margin-top:auto 会把标签连同下面的标题一起压到卡片底部。
   首页的卡片 .meta-line 才是最后一个，只有它匹配。 */
.work-body > .meta-line:last-child {
  margin-top: auto;
}

/* 主/副标题是一个整体：副标题字号略小、行距贴紧主标题，
   与下方作者名的间距略大于标题内部间距 */
.work-card h3,
.card-title {
  margin: 0;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 25px;
  line-height: 1.28;
}

.work-card .card-subtitle {
  margin: -6px 0 0;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.62;
  color: var(--ink);
  opacity: 0.78;
  letter-spacing: -0.005em;
}

.work-card p {
  margin: 0;
  color: #40302a;
  font-size: 15px;
  line-height: 1.8;
}

.work-card .author-line {
  margin: -3px 0 0;
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.6;
  letter-spacing: 0.03em;
}

.work-card > .work-body > p:not(.author-line):not(.card-subtitle) {
  display: none;
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: flex-start;
}

.tag {
  display: inline-flex;
  min-width: 0;
  min-height: 26px;
  align-items: center;
  justify-content: center;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid rgba(140, 31, 30, 0.14);
  background: rgba(244, 214, 208, 0.68);
  color: var(--red-deep);
  font-size: 13px;
  line-height: 1.2;
  white-space: nowrap;
}

/* 整张卡片是入口（见 script.js renderCard），所以指针要表明可点。
   aria-disabled 的卡片没链接可开，保持默认指针。 */
.work-card[data-open] {
  cursor: pointer;
}

.work-card[data-open][aria-disabled="true"] {
  cursor: default;
}

/* 卡片自己有 overflow:hidden，outline 会被裁掉，用 box-shadow 画聚焦环。
   :focus-visible 只在键盘操作时出现，鼠标点击不会留下环。 */
.work-card[data-open]:focus {
  outline: none;
}

.work-card[data-open]:focus-visible {
  box-shadow: 0 0 0 3px var(--red), 0 24px 56px rgba(52, 18, 7, 0.38), var(--inset);
}

.button[aria-disabled="true"] {
  pointer-events: none;
  opacity: 0.55;
}

.class-section {
  padding-top: 86px;
}

.class-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}

.class-card {
  min-height: 166px;
  padding: 26px;
}

.class-card strong {
  display: block;
  color: var(--red-deep);
  font: 800 46px/1 "SiteLatin", serif;
}

.class-card span {
  display: block;
  margin-top: 0px;
  color: var(--muted);
  font-size: 17px;
}

.about-section {
  margin: 86px 0 0;
  padding: 34px;
  display: grid;
  grid-template-columns: minmax(220px, 0.42fr) minmax(0, 0.8fr);
  gap: 28px;
}

.about-section .eyebrow {
  color: var(--red-deep);
}

.about-section h2 {
  color: var(--ink);
  text-shadow: none;
}

.about-section p {
  color: var(--muted);
}

.viewer {
  width: min(1000px, calc(100% - 28px));
  max-height: 88vh;
  padding: 0;
  border: 0;
  background: transparent;
  /* 这条是四个圆角外那块方形阴影残边的真凶。
     <dialog> 的浏览器默认样式是 overflow: auto，那会建立一个**方角**裁剪盒；
     面板的 box-shadow 是 0 14px 36px，向下要铺开 50px，而面板到 dialog 边缘
     只有 18px——多出来的部分被方角切掉，于是圆角附近的阴影是直角断口。
     （上一轮删掉 backdrop-filter 只去掉了另一半原因，这半边还在。）

     dialog 不需要自己滚：面板高度是 min(600px, 88vh)，内部各区各自 overflow。
     手机端那条 .viewer{overflow:hidden} 是刻意用来裁圆角的，在媒体查询里覆盖本条。 */
  overflow: visible;
}

.viewer::backdrop {
  background: rgba(30, 12, 6, 0.62);
  backdrop-filter: blur(8px);
  /* 淡入走两条路，因为单靠哪一条都有浏览器不认：

     Chrome 走下面那条 animation: backdropIn。
     Safari 对「顶层元素的 ::backdrop 上跑 animation」历来不可靠，开窗那一下
     经常整条动画不执行，遮罩直接就是全黑——所以再补一条 transition +
     @starting-style：@starting-style 给出元素刚进入渲染树时的起始值，
     过渡便有了可插值的起点，这是规范给「从无到有」做过渡的正规写法。

     两条同时生效也不会打架：动画运行期间本来就压过过渡，且终点一致。 */
  opacity: 1;
  transition: opacity 0.46s cubic-bezier(0.22, 1, 0.36, 1);
}

@starting-style {
  .viewer[open]::backdrop {
    opacity: 0;
  }
}

/* ---- 下拉关闭时，暗底跟着卡片一起退 ----
   以前暗底全程不动，直到 dialog.close() 那一刻整块消失：卡片在手上柔顺地滑走，
   背景却是硬切的，两层不在一个节奏上，就是"卡吧一下"。

   ::backdrop 写不了内联样式，所以由 viewer.js 在 dialog 上设一个自定义属性，
   这里继承下来用。注意 ::backdrop 从原始元素继承是较新的行为
   （Safari 17.4+ / Chrome 122+）；拿不到的老浏览器 var() 回退成 1，
   拖动过程中暗底不跟着变淡——但下面那条关闭淡出是纯 opacity，不依赖自定义属性，
   照样生效。而"消失得太硬"本来就是要修的那件事。 */
.viewer[open].is-dragging::backdrop {
  animation: none;
  transition: none;
  opacity: var(--sheet-dim, 1);
}

.viewer[open].is-sheet-closing::backdrop {
  animation: none;
  opacity: 0;
  /* 0.24s 比 JS 里那个 260ms 的关闭定时略短，保证淡完了才真正 close，
     不会在半透明状态被硬切掉 */
  transition: opacity 0.24s cubic-bezier(0.32, 0, 0.36, 1);
}

/* 松手回弹时暗底平滑回到全黑，靠的是 .viewer::backdrop 上那条 transition
   （和开窗淡入共用同一条）。这里原来另写了一条 0.34s 的同名过渡，权重更高、
   把上面那条压掉了——两条并存只会让人分不清到底哪条在起作用，已删。 */

/* Safari < 15.4 的 polyfill 遮罩：没有 ::backdrop，JS 注入一个 div 代替 */
.viewer-backdrop-poly {
  position: fixed;
  inset: 0;
  z-index: 100000;
  background: rgba(30, 12, 6, 0.62);
  backdrop-filter: blur(8px);
  animation: backdropIn 0.46s cubic-bezier(0.22, 1, 0.36, 1);
}

/* 老 Safari 下 dialog 没有 top-layer 自动居中，需要手动定位（仅桌面端；
   移动端 bottom sheet 有自己的贴底逻辑，不套这套固定居中） */
@media (min-width: 821px) {
  .viewer.viewer-polyfill[open] {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 100001;
    margin: 0;
    width: auto;
    max-width: none;
  }
}

/* 老 Safari 把 dialog 当未知元素，默认 display: inline。
   无论桌面还是移动端，open 状态下都得是 block 才能让宽高 / margin 生效 */
.viewer.viewer-polyfill[open] {
  display: block;
}

@keyframes backdropIn {
  from {
    opacity: 0;
  }
}

/* 进场：带一点回弹的弹出感。
   曲线第二个控制点大于 1，会冲过终点再收回来——放大到 1 之后轻轻过一点点
   再落定，这一下"过冲"就是弹出的手感来源。

   原来是 0.44s / 过冲 1.4 / 从 scale(0.9)+18px 起跳，落地太急，看着"啪"一下。
   放缓成 0.52s / 过冲 1.12，起跳也收敛到 scale(0.94)+14px：
   位移小了、时间长了，同样有弹性但不再是被弹出来的。 */
.viewer[open] .viewer-panel {
  animation: viewerPanelIn 0.52s cubic-bezier(0.32, 1.12, 0.44, 1);
  transform-origin: center center;
}

.viewer.is-closing .viewer-panel {
  animation: viewerPanelOut 0.3s cubic-bezier(0.32, 0, 0.36, 1) forwards;
}

/* 关闭时 dialog 还挂着 [open]，所以这里必须带上 [open] 把权重压过下面那条
   进场规则——否则 backdropIn 会一直赢，遮罩根本不淡出，直到 close() 才啪地消失，
   看着就是"卡片没了、背景还黑着"。 */
.viewer[open].is-closing::backdrop {
  animation: backdropOut 0.3s cubic-bezier(0.32, 0, 0.36, 1) forwards;
}

@keyframes backdropOut {
  to {
    opacity: 0;
  }
}

/* 起点压到 0.9 + 下移 18px，落差够了才有东西"弹出来"；
   透明度在前 45% 就补满，避免面板还在半透明时就已经在回弹，看着发飘。 */
/* 透明度补满的时机从 45% 推到 62%：原来面板还在放大移动时就已经完全实心了，
   剩下那段纯几何位移没有淡入陪着，读起来就是"啪"地出现再动一下。
   让淡入陪着位移一起走完，进场才是一件事而不是两件。 */
@keyframes viewerPanelIn {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.94);
  }
  62% {
    opacity: 1;
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 遮罩与面板同步收尾（面板 0.52s）。原来 0.3s，遮罩先落定、面板还在动，
   两层各走各的，也是进场显得突兀的一部分 */
.viewer[open]::backdrop {
  animation: backdropIn 0.46s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  .viewer[open] .viewer-panel {
    animation: viewerPanelIn 0.2s ease;
  }
  @keyframes viewerPanelIn {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}

@keyframes viewerPanelOut {
  from {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  to {
    opacity: 0;
    transform: translateY(8px) scale(0.99);
  }
}

/* 面板本身是一整张米色卡纸，左边嵌一张"预览卡"，右边是信息与操作。
   高度写死（受 88vh 兜底）而不是由内容撑开：两栏一个要滚原件、一个要滚简介，
   高度随作品变来变去的话，连着开两件作品就是弹窗在原地忽大忽小。 */
/* 面板 1000×600、左右 1.42:1，窄一点高一点：
   预览框 ≈ 497×398（1.25），横图裁切约 19%，但右栏多了 60px 可用高度，
   简介堆叠更散。如需减少裁切，把面板加宽 40px 即可回到 12% 左右。 */
.viewer-panel {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.42fr) minmax(280px, 1fr);
  grid-template-rows: minmax(0, 1fr);
  gap: 26px;
  height: min(600px, 88vh);
  padding: 26px;
  border-radius: 32px;
  background: rgba(247, 238, 229, 0.94);
  /* 这里刻意不写 backdrop-filter —— 它就是四个圆角外那块矩形暗斑的来源。
     backdrop-filter 的采样区跟着元素的 border box 走，而在「圆角 +
     backdrop-filter + 子元素带 transform 进场动画」这个组合下，不少浏览器
     （Safari 尤其明显）不会把模糊输出按圆角裁掉，角外就多出一块方的。

     为什么是删掉，而不是像 .viewer-stage 那样加 clip-path 兜住：
     clip-path 会连 box-shadow 一起裁没，外阴影就得挪到 .viewer 上做
     drop-shadow；可 filter 会让 .viewer 变成子元素的 backdrop root，
     面板自己的 backdrop-filter 反而彻底失效（.toolbar-shell 那条注释记着
     同一个坑）——绕一圈还是没有玻璃，白搭一层滤镜。

     删掉的视觉代价几乎为零：这张面板底色本身就 0.94 不透明，
     底下又是已经模糊过、压暗 62% 的遮罩，这层玻璃本来就没什么可透的。
     实测把圆角临时放大到 90px 再开关这条属性，两张截图看不出区别。
     （.work-card 当年也是因为同类理由放弃毛玻璃，见上面那段注释。） */
  box-shadow: var(--shadow);
  /* 子元素 .viewer-stage 和 .viewer-meta 各自有 overflow:hidden，
     弹窗面板本身不必再设，否则 box-shadow 会在圆角处被裁成方形 */
}

/* 弹窗打开时锁定主页滚动：html/body 双锁 + 阻断内部滚动链外溢 */
html.viewer-lock,
html.viewer-lock body {
  overflow: hidden;
  overscroll-behavior: none;
}

/* body 侧改用 clip：同样截断滚动，但不生成滚动容器，
   不破坏检索工具栏的 position: sticky —— 弹窗开合时搜索框原地不动，
   不会出现"关闭弹窗搜索框突然蹦出来"（老浏览器回退到上面的 hidden） */
html.viewer-lock body {
  overflow: clip;
}

.viewer,
.viewer-scroll,
.viewer-frame,
.viewer-frame-content {
  overscroll-behavior: contain;
}

/* 关闭按钮：浅色磨砂底 + 描边阴影，深浅背景上都清晰可见 */
.icon-button {
  position: absolute;
  top: 18px;
  right: 18px;
  z-index: 10;
  width: 38px;
  height: 38px;
  border: 1px solid rgba(67, 34, 20, 0.16);
  border-radius: 50%;
  background: rgba(255, 250, 244, 0.88);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  color: var(--red-deep);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 0 2px 10px rgba(30, 10, 4, 0.28);
  transition: background 0.15s ease, transform 0.15s ease;
}

.icon-button:hover {
  background: #fffaf4;
  transform: scale(1.06);
}

/* 触控目标补到 44×44（HIG 下限），但视觉圆点保持原大小——
   按钮画成 44 会显得笨重，而热区本来就不需要看得见。
   下面 .mm-links a / .rel-link 用的是同一招。 */
.icon-button::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

/* 抓手只属于移动端的 bottom sheet 形态。
   桌面端 .viewer-panel 是两列网格，这个由 JS 注入的节点如果不藏起来，
   会当成第一个网格项把信息栏和预览区整体挤错位。 */
.viewer-grabber {
  display: none;
}

/* ===================== 顶光过渡 =====================
   切换作品时盖一层全屏光罩（pointer-events:none，不挡操作）：
   顶部中央一束锥形暖白渐变从上往下扫过（0.7s），
   同时整屏带一层很淡的提亮，然后一起淡出。
   暗色模式用同一束光但压成冷调、更淡——"关灯前最后一道光"。 */
.viewer-flash {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  opacity: 0;
}

.viewer-flash.flash-active {
  animation: topLightSweep 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

@keyframes topLightSweep {
  0% { opacity: 0; }
  18% { opacity: 1; }
  100% { opacity: 0; }
}

/* 锥形暖白光：从顶部中央往下扩散，渐隐到 55% 处 */
.viewer-flash::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 100%;
  background: radial-gradient(
    ellipse 55% 85% at 50% -8%,
    rgba(255, 245, 225, 0.62) 0%,
    rgba(255, 240, 220, 0.28) 28%,
    transparent 55%
  );
}

/* 整屏淡提亮 */
.viewer-flash::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 250, 240, 0.07);
}

/* 暗色模式：冷调、更淡 */
[data-theme="dark"] .viewer-flash::before {
  background: radial-gradient(
    ellipse 55% 85% at 50% -8%,
    rgba(190, 210, 240, 0.18) 0%,
    rgba(200, 220, 250, 0.08) 28%,
    transparent 55%
  );
}

[data-theme="dark"] .viewer-flash::after {
  background: rgba(200, 220, 255, 0.025);
}

/* 减少动态效果：不播放光罩，直接切 */
@media (prefers-reduced-motion: reduce) {
  .viewer-flash.flash-active {
    animation: none;
  }
}

/* 手机端固定头部：标题+作者从滚动区提出来，放在预览图和信息栏之间。
   桌面端不显示（标题仍在滚动区里，反正桌面简介很少长到要滚）。 */
.viewer-mobile-header {
  display: none;
}

/* ===================== 左：预览卡 =====================
   两种版式共用同一张卡，差别只在 data-mode：
   doc  —— PDF / 图片，原件铺满整张卡、可滚动，信息托盘浮在上层；
   link —— 网页外链，缩略图完整居中、四周留白等宽，托盘落在图的下方。
   clip-path 强制圆角：CSS 在 grid item 上的 border-radius 偶有失效
   （panel 是 grid 也算一种触发条件）；内/外 shadow 都跟着裁后形状走。 */
.viewer-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  border-radius: 20px;
  background: rgba(240, 228, 217, 0.9);
  -webkit-clip-path: inset(0 round 20px);
  clip-path: inset(0 round 20px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.4);
  filter: drop-shadow(0 10px 30px rgba(52, 18, 7, 0.16));
}

/* 外链版式的底衬：同一张缩略图放大后高斯模糊。
   inset 负值把图撑出卡外再模糊，边缘才不会因为采样不到而发虚发白。
   （--shot 由 viewer.js 写在面板上、继承下来；doc 版式不设这条规则，
   原件本来就铺满整张卡。） */
.viewer-stage[data-mode="link"]::before {
  content: "";
  position: absolute;
  inset: -14%;
  background-image: var(--shot, none);
  background-position: center;
  background-size: cover;
  filter: blur(36px) saturate(1.4) brightness(1.06);
  pointer-events: none;
}

/* 模糊底衬上压一层米色薄纱：底图深浅全看作品，不压一层的话
   托盘上的深色小字碰上深色缩略图就读不出来了 */
.viewer-stage[data-mode="link"]::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255, 250, 244, 0.22), rgba(255, 247, 239, 0.52));
  pointer-events: none;
}

/* 预览内容与托盘都要压在上面两层底衬之上 */
.viewer-frame,
.viewer-tray {
  position: relative;
  z-index: 1;
}

.viewer-frame {
  flex: 1;
  min-height: 0;
  overflow: auto;
}

/* --- 外链版式 --- */
.viewer-stage[data-mode="link"] {
  padding: 22px;
}

.viewer-stage[data-mode="link"] .viewer-frame {
  border-radius: 12px;
  /* 原来 overflow:hidden 用来裁剪子元素，但 stage-shot 自己就有
     border-radius:12px，img 的 object-fit:cover 配合 border-radius
     已经自裁剪了，不需要父元素再 clip 一次。
     去掉 overflow:hidden 之后，frame 自身的 box-shadow 才能跟着
     圆角走，不会在四个角上被裁成方形阴影。 */
  box-shadow: 0 6px 18px rgba(52, 18, 7, 0.22);
}

/* 缩略图铺满预览框。裁切量靠"把框的比例做得接近图的比例"来压——预览框现在
   是 3:2（534×356），常见的 1.55 横图只切掉约 3%，肉眼看不出来。

   不能用 contain 躲裁切：contain 之下圆角圆的是元素框、不是图真正画出的那块，
   看到的图边缘还是方的，阴影也会贴着方轮廓走，四个角就成了四段直角黑影。 */
.stage-shot {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  border-radius: 12px;
  /* 阴影已移到父元素 .viewer-frame 上；img 自己画 shadow 会被
     父的 overflow 裁剪出方形角 */
}

.viewer-stage[data-mode="link"] .viewer-tray {
  margin-top: 22px;
}

/* --- PDF / 图片版式 --- */
.viewer-frame-content {
  width: 100%;
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

.viewer-frame-content img {
  width: 100%;
  height: auto;
  display: block;
}

/* 长图滚到底时，末尾会被浮在上层的托盘盖住，垫出托盘那么高的一段。
   奖牌改到元数据右边并排之后托盘不再叠高，从 180 收回 130。 */
.viewer-frame-content.is-image {
  padding-bottom: 130px;
}

.viewer-frame-content iframe {
  flex: 1;
  width: 100%;
  min-height: 100%;
  border: 0;
}

/* doc 版式的托盘只是一条透明的定位行，压在原件下缘、四周留 14px。
   真正的两张卡是它的两个孩子：左边元数据的磨砂牌、右边奖牌，
   一左一右各自成块，互不相干（不是把奖牌塞进同一张玻璃里）。
   宽度按内容来——没有奖牌时只占左边那点宽度，不会一条通栏横在底下。 */
.viewer-stage[data-mode="doc"] .viewer-tray {
  position: absolute;
  left: 14px;
  right: auto;
  bottom: 14px;
  width: fit-content;
  max-width: calc(100% - 28px);
  /* stretch 而不是 flex-end：两块浮标一起被撑到同一高度，底边和顶边都齐。
     原来 flex-end 只对齐底边，元数据牌比奖牌高一截，上面参差不齐。 */
  align-items: stretch;
  gap: 18px;
}

/* 浮标里的字整体收一档，让这块的自然高度贴近奖牌。
   只作用于 doc 版式的浮标，不影响手机端滚动区里的同一批类名。 */
.viewer-stage[data-mode="doc"] .tray-meta .meta-label { font-size: 12px; }
.viewer-stage[data-mode="doc"] .tray-meta .meta-value { font-size: 15.5px; }
.viewer-stage[data-mode="doc"] .tray-meta .viewer-meta-row { gap: 2px; }
.viewer-stage[data-mode="doc"] .tray-meta .tray-line { gap: 3px 20px; }
.viewer-stage[data-mode="doc"] .tray-meta .viewer-entry-date { margin-top: 2px; }

/* 奖牌被 stretch 撑高后，内容要继续居中，别贴在顶上 */
.viewer-stage[data-mode="doc"] .viewer-tray .viewer-award {
  align-items: center;
}

/* 悬浮的磨砂牌是元数据自己，不是整条托盘 */
/* 元数据牌与奖牌等高：托盘改 align-items:stretch（见上），两块就一起被撑到
   同一高度；再把这块的字号整体收一档，让它自己的自然高度贴近奖牌，
   而不是把矮的那块硬拉长。 */
.viewer-stage[data-mode="doc"] .tray-meta {
  padding: 10px 14px;
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 14px;
  background: rgba(253, 247, 241, 0.72);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow:
    0 8px 24px rgba(30, 12, 4, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

/* 鼠标进到原件上时，两块浮标（元数据牌、奖牌）一起淡下去，别挡着看作品；
   但指到浮标自己身上时立刻恢复清楚——想看它就凑过去看，不必先把鼠标移开。
   opacity 不影响命中测试，淡到 0.12 也照样接得到 hover。 */
@media (hover: hover) {
  /* 托盘往外扩一圈隐形热区。托盘钉在 left:14px / bottom:14px，
     鼠标从下边或左边进来时，必然先穿过那条 14px 的"是 stage、不是托盘"的窄缝：
     那一瞬间触发淡出，再走一像素进了托盘又立刻恢复——来回一次就是闪烁。
     热区往外推 26px，盖住窄缝并越过 stage 边界，
     指针从任何方向进来都是直接落到托盘上，没有中间态。
     z-index:-1 让它躲在两张卡下面，只用来接 hover，不挡任何点击。 */
  .viewer-stage[data-mode="doc"] .viewer-tray::before {
    content: "";
    position: absolute;
    inset: -26px;
    z-index: -1;
  }

  .viewer-stage[data-mode="doc"] .tray-meta,
  .viewer-stage[data-mode="doc"] .viewer-award {
    /* 淡出留 0.14s 迟滞：擦着边过一下不算"要看作品"。
       恢复不延迟（见下），凑过去就立刻清楚——这一快一慢正是防抖的关键。 */
    transition: opacity 0.22s ease 0.14s;
  }
  .viewer-stage[data-mode="doc"]:hover .tray-meta,
  .viewer-stage[data-mode="doc"]:hover .viewer-award {
    opacity: 0.12;
  }
  /* 比上面那条多一个类，权重更高，所以能赢。
     判定挪到托盘容器上——热区是它的伪元素，卡片自己身上判不到。 */
  .viewer-stage[data-mode="doc"] .viewer-tray:hover .tray-meta,
  .viewer-stage[data-mode="doc"] .viewer-tray:hover .viewer-award {
    opacity: 1;
    transition-delay: 0s;
  }
}

/* 没有原件 / 外链没缩略图时的占位 */
.stage-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  height: 100%;
  padding: 40px;
  color: var(--muted);
  text-align: center;
}

.stage-empty p {
  max-width: 260px;
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
}

/* 占位图标：一个缺了右上角的方框 + 一支朝右上的箭头，即"外链"的通用符号。
   箭头用 ↑ 转 45°——U+2191 在本站字体的 unicode-range 里，不会掉字。 */
.stage-empty-icon {
  position: relative;
  width: 38px;
  height: 38px;
  opacity: 0.5;
}

.stage-empty-icon::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 28px;
  height: 28px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-right-color: transparent;
  border-radius: 6px;
}

.stage-empty-icon::after {
  content: "↑";
  position: absolute;
  right: 0;
  top: -2px;
  font-size: 26px;
  line-height: 1;
  transform: rotate(45deg);
}

/* === 托盘：左边元数据、右边奖牌，一行并排 ===
   外链版式下托盘是通栏的，space-between 把奖牌顶到预览卡右缘；
   doc 版式的托盘宽度贴内容，两者之间就只剩那道 18px 的间距。 */
.viewer-tray {
  flex: none;
  display: flex;
  flex-direction: row;
  /* 中线对齐。原来是 flex-end（底对齐）：奖牌比元数据矮一截时，
     两块的底边虽然齐了，视觉重心却一高一低，看着像没对上。 */
  align-items: center;
  justify-content: space-between;
  gap: 18px;
}

/* 托盘里两块的宽度分配：奖牌按自己的内容占位、绝不被压（赛事名一压就得
   截成「第四届中国新…」，那还不如不写），剩下多少都归元数据。
   「媒体」超长时就在这个剩余宽度里折行，不会去抢奖牌的地方。 */
.tray-meta {
  flex: 1 1 auto;
  min-width: 0;
}

.viewer-tray .viewer-award {
  flex: none;
}

.viewer-meta-row {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

/* 年级与形式同一行，媒体独占一行——媒体常常是三四家报社，
   和年级挤一行必然折行，折出来的第二行还会和下面的入库时间对不齐。

   类名叫 tray-line 不叫 meta-line：后者早就被首页和获奖页的卡片用着了
   （装那三枚红胶囊），重名会把卡片的 6px 间距顶成这里的 26px。 */
.tray-line {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 26px;
  align-items: baseline;
}

.meta-unit {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
}

.meta-label {
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-size: 13px;
  font-weight: 400;
  color: var(--muted);
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.meta-value {
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-size: 18px;
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 「媒体」常常是三四家报社，是这几项里唯一会超长的。
   它不走上面那套省略号——报刊名截一半（「大公报、四川日…」）就是错的信息。
   这里放开换行，装不下时由 viewer.js 的 fitMedium() 先降字号再折成两行。
   line-height 必须是可算的数值：那个函数靠它换算「几行」。 */
.meta-value-medium {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.viewer-entry-date {
  margin: 3px 0 0;
}

/* ===================== 右：信息栏 =====================
   面板已经是一整张米色卡纸，这一栏不再另铺底色，只负责排版留白。
   顶部空出 62px 是给右上角关闭按钮让位，免得标题顶到叉号底下。 */
.viewer-meta {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 62px 30px 0 6px;
  overflow: hidden;
}

/* 滚动区占满整栏（操作条是浮在它上面的，不占位）。
   末尾垫出操作条的高度 —— 那个高度由 viewer.js 量好写进 --footer-h，
   有没有奖牌、按钮换不换行都能对上；变量取不到时按 96px 兜底。 */
.viewer-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-right: 6px;
  padding-bottom: 16px;
  display: flex;
  flex-direction: column;
}

/* 末尾的垫高做成真实子盒，不用容器 padding-bottom：
   iOS 对 flex 滚动容器 block 端 padding 是否计入滚动范围一直有兼容坑——
   一旦没计入，内容不超就不生成滚动，用户看着下面还有字却怎么也划不动。
   真实元素在任何浏览器里都保证占住这段滚动高度。 */
.viewer-scroll::after {
  content: "";
  display: block;
  flex: none;
  height: calc(var(--footer-h, 96px));
}

/* 这是一个高度固定、内部滚动的列向 flex 容器：默认 flex-shrink:1 会让
   内容超出时**先把每个孩子压扁**，再才出现滚动条。滚动区里的块一律不参与收缩，
   超出就滚动，这才是本来该有的行为。 */
.viewer-scroll > * {
  flex: none;
}

/* 主标题 */
#viewerTitle {
  margin: 0 0 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.viewer-main-title {
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: clamp(22px, 2.0vw, 30px);
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
  letter-spacing: -0.03em;
}

.viewer-sub-title {
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: clamp(18px, 1.6vw, 23.8px);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  letter-spacing: -0.02em;
}

/* 作者 */
.viewer-author {
  margin: 0 0 22px;
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.6;
  letter-spacing: 0.03em;
}

/* 简介 */
#viewerDesc {
  margin: 0;
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.85;
  color: var(--muted);
  text-align: justify;
}

#viewerTags {
  margin: 0;
}

/* 操作条：分享 + 打开外链按钮。跟在滚动区后面，不浮在上面。
   原来绝对定位 + backdrop-filter(40px) blur + mask-image 是给"长简介
   滚过按钮"那种场景糊字用的；桌面端简介短时它就只剩右下角一个矩形，
   像"正方形边框"。改为静态：与手机端同一套布局，无模糊、无 mask，
   简洁无视觉残骸。
   桌面端只在右栏底部收齐右对齐；手机端 padding 改 12px 横排铺开。 */
.viewer-meta-footer {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 22px;
  padding: 28px 30px 22px 6px;
}

.viewer-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  width: 100%;
}

.viewer-actions .button.primary {
  min-height: 52px;
  padding: 0 30px;
  border-radius: 999px;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* --- 分享按钮：圆钮 + 纯 CSS 画的「箭头出方框」---
   四笔组成，四个伪元素各画一笔，不引 SVG：
     .share-button::before  托盘（缺了顶边的圆角方框）
     .share-glyph::before   箭杆（下端插进托盘里）
     .share-glyph::after    箭头左半
     .share-button::after   箭头右半
   四笔同为 1.6px、端头全部 999px 圆角，从箭杆顶端各转 ±42° 分出去——
   线宽、端头、圆角都是一套语言，才不会出现"头比杆粗、拐角糊成一坨"。
   （上一版箭头是"一个转 45° 的方块留两条边"，那是两回事。） */
.share-button {
  position: relative;
  flex: none;
  width: 52px;
  height: 52px;
  padding: 0;
  border: 1px solid rgba(67, 34, 20, 0.16);
  border-radius: 50%;
  background: rgba(255, 251, 246, 0.9);
  color: var(--red-deep);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(53, 20, 8, 0.12);
  transition: transform 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}

/* 托盘：下沿在按钮中心偏下 10px 处，箭杆的下端插进来 */
.share-button::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 12px;
  transform: translate(-50%, -2px);
  border: 1.6px solid currentColor;
  border-top: 0;
  border-radius: 0 0 4px 4px;
}

/* 箭杆与箭头共用的定位盒：顶端（y=0）就是箭头的尖 */
.share-glyph {
  position: absolute;
  left: 50%;
  top: calc(50% - 11px);
  width: 16px;
  height: 22px;
  transform: translateX(-50%);
}

/* 箭杆 */
.share-glyph::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 1.6px;
  height: 13px;
  margin-left: -0.8px;
  border-radius: 999px;
  background: currentColor;
}

/* 箭头左半：从尖上往左下甩 42° */
.share-glyph::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 1.6px;
  height: 8px;
  margin-left: -0.8px;
  border-radius: 999px;
  background: currentColor;
  transform-origin: 50% 0.8px;
  transform: rotate(-42deg);
}

/* 箭头右半：同一支笔镜像过去。它挂在按钮上（伪元素一个元素只有两个），
   所以位置得自己按按钮中心算，与 .share-glyph 的顶端对齐 */
.share-button::after {
  content: "";
  position: absolute;
  left: 50%;
  top: calc(50% - 11px);
  width: 1.6px;
  height: 8px;
  margin-left: -0.8px;
  border-radius: 999px;
  background: currentColor;
  transform-origin: 50% 0.8px;
  transform: rotate(42deg);
}

.share-button:hover {
  transform: translateY(-2px);
  background: #fffaf4;
  box-shadow: 0 8px 18px rgba(53, 20, 8, 0.18);
}

.share-button:active {
  transform: translateY(0) scale(0.95);
}

/* 复制成功的提示：挤在按钮左边，没内容时（:empty）整条淡出让位 */
.share-status {
  flex: 1;
  min-width: 0;
  margin: 0 4px 0 0;
  color: var(--red-deep);
  font-size: 13px;
  line-height: 1.4;
  text-align: right;
  opacity: 1;
  transition: opacity 0.25s ease;
}

.share-status:empty {
  opacity: 0;
}


/* 移动端：弹窗改为底部上拉卡片（bottom sheet），预览卡在上、信息在下 */
@media (max-width: 820px) {
  /* 贴底、圆顶角、上滑入场 */
  .viewer {
    width: 100vw;
    max-width: 100vw;
    max-height: none;
    margin: auto 0 0;
    /* 圆角和裁剪放在 dialog 这一层。
       下面的 .viewer-panel 是 overflow:visible + background:transparent——
       它那个 border-radius 画在一个透明元素上，真正看得见的背景是
       ::before/::after 两层 inset:0 的伪元素，方角、且不受面板裁剪。
       亮色下薄纱接近页面底色看不出来，暗色一对比就成了方角。
       在这里统一裁一次，整张抽屉才真是个圆角矩形。 */
    border-radius: 24px 24px 0 0;
    overflow: hidden;
  }

  .viewer[open] {
    animation: sheetUp 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .viewer.is-closing {
    animation: sheetDown 0.35s cubic-bezier(0.32, 0, 0.36, 1) forwards;
  }

  .viewer[open] .viewer-panel {
    animation: none;
  }

  .viewer[open]::backdrop {
    animation: backdropIn 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .viewer.is-closing::backdrop {
    animation: backdropOut 0.3s cubic-bezier(0.32, 0, 0.36, 1) forwards;
  }

  /* 手机端布局：面板纵向 flex，把手/按钮固定，其余全部滚动。
     顶：抓手（绝对定位）
     滚动区：缩略图 / 标题 / 作者 / 奖牌 / 简介 / 标签 / 元数据
     底：分享 + 打开原件（固定）
     高度 88dvh——每件作品掀起来一样高，不会因为简介长短而忽高忽低。

     左右 padding 归零，overflow 从 hidden 改 visible：
     - padding 让给子元素自己管，滚动区用负 margin 撑到面板边缘
     - overflow:visible 不再一刀切裁掉图片和奖项的 drop-shadow
     - 背景伪元素 z-index:-1 在面板背后，不会跟着溢出 */
  .viewer-panel {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
    grid-template-rows: none;
    gap: 0;
    height: 88dvh;
    max-height: 88dvh;
    padding: 30px 0 0;
    border-radius: 24px 24px 0 0;
    overflow: visible;
    background: transparent;
  }

  /* 整张卡片的背景就是这件作品的缩略图，放大、重度模糊后当环境色用
     （Apple Music 正在播放页的做法）。上面再压一层米色薄纱保证读得清字。
     --shot 由 viewer.js 写在面板上。 */
  /* 这两层自己也带上圆角，不全指望上面 .viewer 的 overflow:hidden：
     Safari 在「圆角 + overflow:hidden + 子元素带 transform/动画」这个组合上
     经常裁不干净，边角会漏出方的一点。

     光靠 border-radius 还不够：下面 ::before 带 filter: blur(52px)，
     而模糊的输出是画到 border box 之外的，border-radius 管不住 filter 溢出——
     那 52px 光晕会糊出圆角，把两个角填成浅色（看着像指甲盖）。
     clip-path 按规范在 filter 之后执行，能连模糊输出一起裁掉，
     而且不依赖任何祖先的 overflow，Safari 上也稳。 */
  .viewer-panel::before,
  .viewer-panel::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    border-radius: 24px 24px 0 0;
    clip-path: inset(0 round 24px 24px 0 0);
  }

  .viewer-panel::before {
    background-image: var(--shot, none);
    background-position: center top;
    /* 原来是 background-size:130% + transform:scale(1.25)，
       但 transform 会把这层整个放大到盒子外面，圆角跟着被推出去、等于没有。
       改成把放大量全部并进 background-size（130% × 1.25 ≈ 165%），
       元素本身老实待在盒子里，圆角才裁得住。观感不变。 */
    background-size: 165% auto;
    background-repeat: no-repeat;
    filter: blur(52px) saturate(1.8);
    opacity: 0.85;
  }

  .viewer-panel::after {
    background: linear-gradient(180deg,
      rgba(255, 250, 244, 0.42) 0%,
      rgba(255, 250, 244, 0.62) 46%,
      rgba(255, 250, 244, 0.78) 100%);
  }

  /* 预览卡在滚动区最前面：底衬交给整个面板，这里只剩作品本身浮在环境色上。
     顶边与抓手条之间留 28px 作为拖拽区。
     margin-bottom 18px 给 drop-shadow (30px blur) 留出底部展开空间。 */
  .viewer-stage {
    height: clamp(170px, 26dvh, 220px);
    border-radius: 16px;
    background: none;
    box-shadow: none;
    min-height: 0;
    flex: none;
    margin: 28px 0 18px;
  }

  .viewer-stage[data-mode="link"] {
    padding: 0;
    overflow: visible;
  }

  /* 面板已经铺了这张图的模糊底，预览卡不必再来一层 */
  .viewer-stage[data-mode="link"]::before,
  .viewer-stage[data-mode="link"]::after {
    display: none;
  }

  .stage-shot {
    border-radius: 16px;
    filter: drop-shadow(0 14px 30px rgba(30, 12, 4, 0.34));
  }

  /* doc 版式仍是一张实卡（里面是要读的原件，不能透） */
  .viewer-stage[data-mode="doc"] {
    background: rgba(240, 228, 217, 0.9);
    box-shadow: 0 14px 30px rgba(30, 12, 4, 0.28);
  }

  /* 手机端预览卡上没有悬浮说明牌——元数据整块搬进了下面的滚动区
     （见本媒体查询里的 .viewer-tray{display:none}），所以这里不必再为
     doc 版式的托盘写定位与卡片样式，写了也是一条永不生效的死规则。 */

  .viewer-frame-content.is-image {
    padding-bottom: 84px;
  }

  /* 抓手：整条通宽的透明拖拽带，中间画那颗药丸。
     做成真实元素（viewer.js 注入）而不是伪元素——伪元素接不到指针事件。
     touch-action:none 是关键：不写的话浏览器会先把手势当滚动，
     等 pointermove 到手时已经不可 preventDefault，拖拽就会和滚动打架。 */
  .viewer-grabber {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 11;
    height: 28px;
    touch-action: none;
    cursor: grab;
  }

  .viewer-grabber::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    width: 38px;
    height: 4px;
    border-radius: 999px;
    background: rgba(67, 34, 20, 0.22);
    transform: translateX(-50%);
    transition: background 0.16s ease;
  }

  /* 拖拽中：药丸加深，给一点"抓住了"的反馈 */
  .viewer.is-dragging .viewer-grabber::before {
    background: rgba(67, 34, 20, 0.4);
  }

  /* 松手回弹；拖拽跟手时必须关掉过渡，否则每帧都在追一个旧值，手感发黏 */
  .viewer {
    transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.28s ease;
  }

  .viewer.is-dragging {
    transition: none;
  }

  /* 信息栏：纵向 flex，header 和 footer 固定，中间 scroll 占满剩余空间滚动。
     左右 padding 18px 保持文字宽度与原来一致。 */
  .viewer-meta {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
    padding: 0 18px;
    position: static;
    overflow: visible;
  }

  /* 中间滚动区：负 margin 撑到面板边缘，腾出滚动条和阴影的呼吸空间；
     内 padding 18px 把文字推回原位，不跟着变宽。
     overflow-x:clip 只剪不生成滚动条，防止 drop-shadow 触发横向栏。 */
  .viewer-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: clip;
    /* 到顶后不要橡皮筋、也不要把滚动传给页面：
       那一下正好是「接管手势、开始拖走卡片」的时机，橡皮筋会跟它抢。
       用 none 不用 contain：contain 只掐断滚动链，本地那下回弹照样弹，
       看上去就是「拖不动卡片，只有内容在弹」。 */
    overscroll-behavior: none;
    scrollbar-gutter: stable;
    margin: 0 -18px;
    padding: 4px 18px 0;
    -webkit-mask-image: linear-gradient(180deg,
      transparent 0%,
      #000 8%,
      #000 92%,
      transparent 100%);
    mask-image: linear-gradient(180deg,
      transparent 0%,
      #000 8%,
      #000 92%,
      transparent 100%);
  }

  /* 托盘内容已搬进滚动区，隐藏空的托盘壳 */
  .viewer-tray {
    display: none;
  }

  /* 手机端滚动条瘦身：3px 宽、半透明，只在 hover 时加深，不打扰阅读 */
  .viewer-scroll::-webkit-scrollbar {
    width: 3px;
  }

  .viewer-scroll::-webkit-scrollbar-track {
    background: transparent;
  }

  .viewer-scroll::-webkit-scrollbar-thumb {
    background: rgba(67, 34, 20, 0.18);
    border-radius: 999px;
  }

  .viewer-scroll::-webkit-scrollbar-thumb:hover {
    background: rgba(67, 34, 20, 0.35);
  }

  /* 元数据在滚动区里的样式：加一条分隔线，和上面的简介区分开 */
  .viewer-scroll .tray-meta {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid rgba(67, 34, 20, 0.1);
  }

  [data-theme="dark"] .viewer-scroll .tray-meta {
    border-top-color: rgba(255, 255, 255, 0.08);
  }


  /* 手机端操作条：固定在面板底部，不再铺大渐变遮罩。
     上下内容淡出由 .viewer-scroll 的 mask-image 负责，footer 只放按钮。
     按钮本身有实色/半透明底，不需要额外背景；简介滚到底时已在 mask 中淡掉，
     不会直接糊在按钮上。 */
  .viewer-meta-footer {
    position: static;
    left: auto;
    right: auto;
    bottom: auto;
    z-index: 3;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 12px 0 calc(12px + env(safe-area-inset-bottom, 0px));
    pointer-events: auto;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .viewer-meta-footer .viewer-actions {
    pointer-events: auto;
  }

  [data-theme="dark"] .viewer-meta-footer {
    background: none;
  }

  /* 手机端不要关闭按钮：抓手本身就是关闭入口（下拉即关），
     两个功能重复的控件还挤在同一条 28px 高的带子上互相抢点击。
     去掉叉号，抓手独占整条顶部带，热区反而更大更好按。 */
  .icon-button {
    display: none;
  }

  .viewer-main-title {
    font-size: 24px;
  }

  .viewer-sub-title {
    font-size: 17.5px;
    font-weight: 600;
  }

  .viewer-author {
    margin-bottom: 14px;
    font-size: 13.5px;
  }

  #viewerDesc {
    font-size: 15px;
    line-height: 1.75;
  }

  /* 移动端反而更该给足：主按钮是弹窗里的主操作，别掉到触控下限以下 */
  .viewer-actions .button.primary {
    flex: 1;
    min-height: 48px;
    padding: 0 20px;
    font-size: 16px;
    font-weight: 700;
  }

  .share-button {
    width: 48px;
    height: 48px;
  }

  .share-status {
    flex: none;
  }

  .tray-line {
    gap: 2px 16px;
  }

  .meta-label {
    font-size: 12px;
  }

  .meta-value {
    font-size: 15px;
  }

  /* 手机端的奖牌：落在作者与简介之间，在滚动区里，压成通栏一长条——
     最左奖杯，中间赛事名，最右奖级。竖着堆三行的那种牌子在手机上太占地方。
     margin-bottom 给 drop-shadow 留出空间，防止阴影被下一个元素裁掉。 */
  .viewer-scroll > .viewer-award {
    width: 100%;
    max-width: none;
    margin: 0 0 14px;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 12px;
  }

  .viewer-scroll > .viewer-award svg {
    width: 26px;
    height: 26px;
  }

  /* 赛事名和奖级并排，不再上下两行 */
  .viewer-scroll > .viewer-award .award-text {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
  }

  /* 赛事名保持两行，断点还是 award-fx.js 算好的那个（「第X届中国新闻传播学科」
     ／「数据库建设及可视化大赛」）——所以这里不要动 .ac-line：它默认就是
     一段一行。把两段合成一行再让浏览器自由折，断点会落在「数据库建设」中间，
     读起来是断的。 */
  .viewer-scroll > .viewer-award .award-contest {
    flex: 1;
    min-width: 0;
    font-size: 12.5px;
    line-height: 1.35;
  }

  .viewer-scroll > .viewer-award .award-prize {
    flex: none;
    margin-top: 0;
    font-size: 17px;
    white-space: nowrap;
  }
}

/* 起跳从 48% 收到 36%：位移越大、在同样时长里速度越快，
   落地那一下就越像"甩"上来的。起始透明度也从 0.4 提到 0.55，
   一上来就有个形，不是从半透明里冲出来。 */
@keyframes sheetUp {
  from {
    transform: translateY(36%);
    opacity: 0.55;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

@media (max-width: 820px) and (min-width: 641px) {
  .work-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px;
  }
}


/* 按钮光追已移除（原 .glow-target / .glow-spot 整块）：
   光照只保留在奖牌上，见「获奖作品奖项卡」一节。
   这里原本给按钮统一加的 overflow:hidden 也一并去掉——它只是为了裁光斑，
   留着反而会把 focus ring 和溢出的下划线切掉。 */

/* 陀螺仪授权提示：真正的授权申请在点「开启」时才发起
   （iOS 要求必须在用户手势内调用） */
.gyro-ask {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 22px);
  z-index: 400;
  display: flex;
  align-items: center;
  gap: 14px;
  width: max-content;
  max-width: calc(100vw - 24px);
  padding: 11px 12px 11px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: 0 14px 40px rgba(40, 12, 4, 0.34);
  opacity: 0;
  transform: translateX(-50%) translateY(14px);
  transition: opacity 0.3s ease, transform 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

.gyro-ask.is-in {
  opacity: 1;
  transform: translateX(-50%);
}

/* 不能 nowrap：窄屏上文案 + 两个按钮会挤爆胶囊，把「不用」推出可视区 */
.gyro-ask-text {
  min-width: 0;
  margin: 0;
  color: var(--ink);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 14px;
  line-height: 1.4;
}

.gyro-ask-btns {
  display: flex;
  gap: 6px;
  flex: none;
}

.gyro-ask-yes,
.gyro-ask-no {
  padding: 7px 14px;
  border: 0;
  border-radius: 999px;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.gyro-ask-yes {
  background: var(--red);
  color: #fff6ee;
}

.gyro-ask-no {
  background: transparent;
  color: var(--muted);
}

/* ===================== 移动端顶部栏 + 全屏菜单 ===================== */
.mobile-topbar,
.mobile-menu {
  display: none;
}

@media (max-width: 900px) {
  /* 顶栏本身不再是一条通栏的渐变带。

     原来是「整屏宽的毛玻璃 + 向下渐隐的 mask」。在 Safari 里这条带子很别扭：
     Safari 自己的顶部是透明的，站点又在下面再压一条渐变，两层叠在一起，
     滑动时内容从渐变里钻出来，看着是一块糊住的污渍——首页和菜单上尤其明显。

     改成 iOS 的做法：顶栏退成一个透明的定位容器，只给左边的标志和右边的图标组
     各套一枚毛玻璃胶囊。内容干干净净地从胶囊旁边滑过去，
     该糊的只有胶囊底下那一小块。 */
  .mobile-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* 图标行比例对齐 apple.com.cn 移动版顶栏（图标约 20px、离顶 4px），
       但模糊带本身要比图标行高得多：靠 padding-bottom 向下延伸，
       图标位置不动，只是让渐隐过渡走得更长、更自然。
       检索栏（z-index 70）会压在这条带子上层，属预期。 */
    padding: calc(env(safe-area-inset-top, 0px) + 6px) 16px 44px;
    /* 色标一律用「安全区 + 固定像素」，不能用百分比。
       百分比是相对元素高度算的，而这条栏的高度里含着 env(safe-area-inset-top)——
       它随机型变：灵动岛机型上是 59px，总高就成了 145px，34% 只有 49px，
       比状态栏本身还短 10px，于是状态栏下缘就开始透内容了。
       锚到安全区之后，实色段永远盖满「状态栏 + 图标行」，跟机型无关。

       不透明度分两段给：状态栏那一条给满（后面没有该看的东西，纯色最干净），
       图标行那一段留一点通透，让 20px 的背景模糊继续起作用——
       整条都给满 1.0 的话模糊就白做了，吸顶也就没了质感。 */
    background: linear-gradient(180deg,
      /* 安全区这一段给满不透明。状态栏后面本来就没有该看的东西，
         这里是纯色反而干净——Apple 自己的页面顶端也是一块纯色。
         注意能盖住正文的只有顶栏这一层：.page-bg::before 那条带子在
         z-index:0 的背景层里，只盖得住背景照片，盖不住 z-index:1 的正文。 */
      #9f4d25 0,
      #9f4d25 env(safe-area-inset-top, 0px),
      /* 图标行这一段留一点通透，20px 的背景模糊才继续起作用，
         表面有材质厚度而不是一块涂料 */
      rgba(159, 77, 37, 0.93) calc(env(safe-area-inset-top, 0px) + 44px),
      rgba(150, 70, 32, 0.58) calc(env(safe-area-inset-top, 0px) + 62px),
      rgba(130, 58, 25, 0.20) calc(env(safe-area-inset-top, 0px) + 76px),
      transparent 100%);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    /* 遮罩负责让 backdrop-filter 的模糊跟着淡出（模糊没法直接用渐变淡出）。
       色标和上面的背景一一对齐，同样锚在安全区上——两条曲线必须同步，
       错开的话会出现「还在糊、但已经没底色」的一段，看着发脏。 */
    mask-image: linear-gradient(180deg,
      #000 0,
      #000 calc(env(safe-area-inset-top, 0px) + 44px),
      rgba(0, 0, 0, 0.62) calc(env(safe-area-inset-top, 0px) + 62px),
      rgba(0, 0, 0, 0.22) calc(env(safe-area-inset-top, 0px) + 76px),
      transparent 100%);
    -webkit-mask-image: linear-gradient(180deg,
      #000 0,
      #000 calc(env(safe-area-inset-top, 0px) + 44px),
      rgba(0, 0, 0, 0.62) calc(env(safe-area-inset-top, 0px) + 62px),
      rgba(0, 0, 0, 0.22) calc(env(safe-area-inset-top, 0px) + 76px),
      transparent 100%);
    pointer-events: none;
  }

  /* 暗色下换成中性深灰，和 html 的 #1c1c1e、theme-color 三处对齐。
     色标与亮色一一对应，同样锚在安全区上。 */
  [data-theme="dark"] .mobile-topbar {
    background: linear-gradient(180deg,
      #1c1c1e 0,
      #1c1c1e env(safe-area-inset-top, 0px),
      rgba(28, 28, 30, 0.93) calc(env(safe-area-inset-top, 0px) + 44px),
      rgba(28, 28, 30, 0.58) calc(env(safe-area-inset-top, 0px) + 62px),
      rgba(28, 28, 30, 0.20) calc(env(safe-area-inset-top, 0px) + 76px),
      transparent 100%);
  }


  /* 顶栏常驻：检索工具栏吸顶时停在顶栏下方，互不遮挡，
     浏览作品途中也能随时点汉堡切换页面。

     z-index 抬到顶栏(60)之上，搜索框永远画在模糊渐变上层、不被它糊住。

     这里的 44px 和上面顶栏的 padding-bottom:44px 是成对的：
     顶栏靠那段 padding 把模糊带往下延伸，图标行本身只占上面一小截，
     所以吸顶的检索条让 44px 就正好贴在图标行下面、压在渐隐带上层。
     改顶栏的 padding-bottom 必须同时改这里。 */
  .toolbar-shell {
    top: calc(env(safe-area-inset-top, 0px) + 44px);
    z-index: 70;
  }

  .mobile-topbar a,
  .mobile-topbar button {
    pointer-events: auto;
  }

  .mt-logo img {
    width: 25px;
    height: 25px;
    object-fit: contain;
  }

  .mt-actions {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .mt-btn {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 0;
    border-radius: 12px;
    background: transparent;
    color: #fff6ee;
    -webkit-tap-highlight-color: transparent;
  }

  .mt-btn svg {
    width: 23px;
    height: 23px;
  }

  .mt-btn:active {
    background: rgba(255, 246, 238, 0.14);
  }

  .mobile-menu {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: auto;
    /* 不能用 inset:0 / bottom:0：那样高度锚的是「小视口」（Safari 上下工具栏
       都在时的高度）。工具栏一收起可视区变高，菜单却没跟着长，底部就露出
       一条能看见后面页面的缝。改成锚定最大视口，和 .page-bg 用的是同一招。

       但只写 100lvh 还是漏。lvh 是个静态值，而工具栏收起/展开时 Safari 是
       *动画*地改可视区高度，那几帧里可视区能比 lvh 还高一点；再加上底部安全区
       和各家浏览器对 lvh 的口径本来就不完全一致——差几像素，底下就是一条缝。
       所以不再去赌哪个单位"对"：直接多铺 96px 探出屏幕底。菜单是 fixed，
       按规范不参与滚动溢出，多出来的这截既看不见也不会撑出滚动条。 */
    height: calc(100vh + 96px);
    height: calc(100lvh + 96px);
    z-index: 90;
    display: block;
    background:
      linear-gradient(rgba(88, 30, 9, 0.3), rgba(88, 30, 9, 0.3)),
      url("index pictures/background.png") center center / cover no-repeat;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.34s ease, visibility 0.34s ease;
  }

  .mobile-menu.is-open {
    opacity: 1;
    visibility: visible;
  }

  .mm-close {
    position: absolute;
    top: calc(env(safe-area-inset-top, 0px) + 16px);
    right: 14px;
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    padding: 0;
    border: 0;
    background: transparent;
    color: #fff6ee;
    -webkit-tap-highlight-color: transparent;
  }

  .mm-close svg {
    width: 30px;
    height: 30px;
  }

  .mm-links {
    display: flex;
    flex-direction: column;
    gap: 38px;
    /* 顶部安全区偏移已由上方的 .mm-search 承担，这里只留与它的间距 */
    padding: 34px 44px 0;
  }

  .mm-links a {
    display: inline-flex;
    align-items: center;
    /* 27px 的字撑出来只有 29px 高，够不着 44 的触控下限 */
    min-height: 44px;
    gap: 16px;
    color: #fff8ef;
    /* 同 .sb-item：目录条目走朝华 */
    font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
    font-size: 27px;
    font-weight: 700;
    letter-spacing: -0.02em;
    /* 关闭时立即复位（无延迟），打开时按行错峰下拉 */
    opacity: 0;
    transform: translateY(-22px);
    transition: opacity 0.2s ease, transform 0.2s ease;
  }

  .mm-links a svg {
    width: 27px;
    height: 27px;
    flex-shrink: 0;
  }

  .mobile-menu.is-open .mm-links a {
    opacity: 1;
    transform: none;
    transition: opacity 0.5s cubic-bezier(0.22, 1, 0.36, 1), transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .mobile-menu .mm-links a {
    transition: opacity 0.3s ease, transform 0.3s ease;
  }

  .mobile-menu .mm-links a:nth-child(1) { transition-delay: 0.30s; }
  .mobile-menu .mm-links a:nth-child(2) { transition-delay: 0.25s; }
  .mobile-menu .mm-links a:nth-child(3) { transition-delay: 0.2s; }
  .mobile-menu .mm-links a:nth-child(4) { transition-delay: 0.15s; }
  .mobile-menu .mm-links a:nth-child(5) { transition-delay: 0.1s; }
  .mobile-menu .mm-links a:nth-child(6) { transition-delay: 0.05s; }

  .mobile-menu.is-open .mm-links a:nth-child(1) { transition-delay: 0.06s; }
  .mobile-menu.is-open .mm-links a:nth-child(2) { transition-delay: 0.12s; }
  .mobile-menu.is-open .mm-links a:nth-child(3) { transition-delay: 0.18s; }
  .mobile-menu.is-open .mm-links a:nth-child(4) { transition-delay: 0.24s; }
  .mobile-menu.is-open .mm-links a:nth-child(5) { transition-delay: 0.30s; }
  .mobile-menu.is-open .mm-links a:nth-child(6) { transition-delay: 0.36s; }

  /* 主题切换按钮已经移到顶栏（.mt-theme-btn，复用 .mt-btn 的规格），
     菜单里不再重复放一个 */

  /* 菜单打开时必须把根元素一起锁死，只锁 body 是不够的：
     iOS 上文档滚动和橡皮筋归 html 管，只锁 body 拦不住——
     手指在菜单上一拖，底下的页面照样滚，而菜单是 position:fixed 不动，
     页面就从它底下滑出来，底部露出一条能看见后面的缝。
     （之前以为是菜单高度不够，改过 100lvh，没打中。）
     写法和作品弹窗的 .viewer-lock 保持一致。 */
  html.mm-lock,
  html.mm-lock body {
    overflow: hidden;
    overscroll-behavior: none;
  }

  /* 菜单自身的滚动也别往外传 */
  .mobile-menu {
    overscroll-behavior: contain;
  }
}

/* ===================== 获奖作品奖项卡 ===================== */
.viewer-award {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 12px;
  width: fit-content;
  max-width: 100%;
  margin: 0;
  padding: 12px 16px;
  border-radius: 14px;
}

/* hidden 属性会被 display:flex 覆盖，必须显式关掉，否则无奖作品露出空胶囊 */
.viewer-award[hidden] {
  display: none;
}

.viewer-award svg,
.viewer-award .award-text {
  position: relative;
  z-index: 1;
}

/* 允许文字块收缩到内容宽度以下，ac-line 才能测出溢出、触发缩字号 */
.viewer-award .award-text {
  min-width: 0;
}

/* 金属拉丝：feTurbulence 噪声横向拉伸成长短、明暗错落的丝纹，
   overlay 混合进金属底色；再叠一层极细的横线收拢方向感 */
.viewer-award::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='240'%20height='240'%3E%3Cfilter%20id='n'%20x='0'%20y='0'%20width='100%25'%20height='100%25'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.0125%200.5'%20numOctaves='3'%20seed='8'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='saturate'%20values='0'/%3E%3C/filter%3E%3Crect%20width='240'%20height='240'%20filter='url%28%23n%29'/%3E%3C/svg%3E"),
    repeating-linear-gradient(0deg,
      rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 3px,
      rgba(0, 0, 0, 0.03) 3px 4px, transparent 4px 6px);
  /* 平铺尺寸与噪声周期整倍对齐（240 × 0.0125 = 3 周期），
     stitchTiles 才能真正无缝，接缝竖线消失 */
  background-size: 240px 240px, auto;
  mix-blend-mode: overlay;
  opacity: 0.85;
  pointer-events: none;
}

/* 交互光照：超尺寸光层由 JS 写入 --tx/--ty 做 transform 位移。
   渐变只画一次、位移走 GPU 合成，Safari 上不会逐帧重绘。
   径向泛光 + 一条垂直光带（横向拉丝的各向异性反光） */
.viewer-award::after {
  content: "";
  position: absolute;
  left: -75%;
  top: -75%;
  width: 250%;
  height: 250%;
  background:
    radial-gradient(30% 34% at 50% 50%,
      rgba(255, 255, 255, 0.45), rgba(255, 255, 255, 0.12) 55%, transparent 76%),
    linear-gradient(90deg,
      transparent 41%, rgba(255, 255, 255, 0.2) 50%, transparent 59%);
  transform: translate3d(var(--tx, 0px), var(--ty, 0px), 0);
  will-change: transform;
  pointer-events: none;
}

.award-contest {
  /* 和奖名（.award-prize）同一副字：整块奖牌上不该有两种中文字面。
     换字体会改变文字度量，而 fitContest 是靠量宽度缩字号的——
     朝华是 font-display:swap 的网络字体，字体换上去之后必须重新 fit 一次，
     那一下由 award-fx.js 的 document.fonts.ready 钩子负责。 */
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 13px;
  line-height: 1.5;
  letter-spacing: -0.01em;
}

/* 每行整体不断字；放不下由 AwardFX.fitContest 缩字号，而不是硬折出「赛」这种孤字。
   缩到 12px 下限仍塞不下时用省略号收尾——截断可读，缩成 9px 不可读 */
.award-contest .ac-line {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.award-prize {
  margin-top: 4px;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 21px;
  letter-spacing: 0.02em;
}

.viewer-award svg {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
}

/* 四档奖级（Excel awardTier 列自由指定）：金 > 银 > 铜 > 红。
   质感参数集中在这四条规则里，奖项卡（.viewer-award）与卡片小奖牌
   （.tier-badge）共用同一套变量，改一处两边同时生效 */
.award-gold {
  --met-ink: #6a4c0d;
  --met-line: rgba(158, 117, 35, 0.6);
  --met-bg: linear-gradient(160deg, #f1dfa4 0%, #e0c169 38%, #d0a946 68%, #e6ca7c 100%);
  --met-top: rgba(255, 255, 255, 0.6);
  --met-glow: rgba(158, 117, 35, 0.3);
}

.award-silver {
  --met-ink: #414c58;
  --met-line: rgba(120, 132, 145, 0.55);
  --met-bg: linear-gradient(160deg, #eceff2 0%, #cbd1d8 38%, #b1bac4 68%, #dbe0e5 100%);
  --met-top: rgba(255, 255, 255, 0.8);
  --met-glow: rgba(110, 122, 135, 0.26);
}

.award-bronze {
  --met-ink: #57300f;
  --met-line: rgba(150, 90, 45, 0.6);
  --met-bg: linear-gradient(160deg, #e9c6a0 0%, #cd9761 38%, #b3773f 68%, #dcae81 100%);
  --met-top: rgba(255, 244, 232, 0.65);
  --met-glow: rgba(150, 90, 45, 0.28);
}

.award-red {
  --met-ink: #fff6ee;
  --met-line: rgba(140, 31, 30, 0.6);
  --met-bg: linear-gradient(160deg, var(--red) 0%, var(--red-deep) 100%);
  --met-top: rgba(255, 255, 255, 0.22);
  --met-glow: rgba(140, 31, 30, 0.3);
}

.viewer-award.award-gold,
.viewer-award.award-silver,
.viewer-award.award-bronze,
.viewer-award.award-red {
  color: var(--met-ink);
  border: 1px solid var(--met-line);
  background: var(--met-bg);
  /* 内凹高光还是 box-shadow 画，没问题；外发光改用 filter:drop-shadow，
     它跟着视觉轮廓（包括 border-radius 的圆角）走，不会被 overflow:hidden 裁成方形 */
  box-shadow: inset 0 1px 0 var(--met-top);
  filter: drop-shadow(0 6px 18px var(--met-glow));
}

/* 红档不是金属：丝纹与光照都压淡 */
.viewer-award.award-red::before {
  opacity: 0.45;
}

.viewer-award.award-red::after {
  background:
    radial-gradient(30% 34% at 50% 50%,
      rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.06) 55%, transparent 76%);
}

/* 衍生关系：同一批数据做出的两件作品互相指路（各有各的链接与奖项，
   故是两条独立记录，不合并）。母件一侧的关系由 viewer.js 反查得出。 */
.viewer-related {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 2px 4px;
}

.viewer-related[hidden] {
  display: none;
}

.rel-label {
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.02em;
}

.rel-link {
  position: relative;
  padding: 0;
  border: 0;
  background: none;
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 15px;
  letter-spacing: -0.01em;
  text-underline-offset: 3px;
  text-decoration: underline;
  text-decoration-color: rgba(140, 31, 30, 0.35);
  cursor: pointer;
}

.rel-link:hover {
  text-decoration-color: var(--red);
}

/* 这行链接文字本身只有 17px 高，纵向把热区撑到 44（宽度就用文字本身的宽度，
   不外扩，免得和相邻的"衍生作品/"标签抢点击） */
.rel-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

/* 卡片上的衍生标记：与普通 tag 同形，描边区分，避免被当成重复录入 */
.tag.tag-derived {
  border-color: rgba(140, 31, 30, 0.4);
  background: rgba(187, 63, 60, 0.08);
  color: var(--red-deep);
}

/* ===================== 全站搜索 =====================
   入口就是一个可直接输入的框：一个放大镜 + 一行淡提示词，不弹窗。
   结果面板挂在 body 下浮动定位（见 search.js），避免被侧边栏窄宽度裁掉。
   与首页工具栏的作品筛选是两回事，那个只筛作品网格，这个跨作品/页面/新闻。 */
.sb-item.sb-search {
  gap: 10px;
  margin-bottom: 4px;
  cursor: text;
}

.sb-search-input,
.mm-search-input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: none;
  color: #fff8ef;
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
}

.sb-search-input::-webkit-search-cancel-button,
.mm-search-input::-webkit-search-cancel-button {
  display: none;
}

.sb-search-input {
  font-size: 14px;
  letter-spacing: 0.01em;
}

.sb-search-input::placeholder,
.mm-search-input::placeholder {
  color: rgba(255, 246, 238, 0.45);
}

/* 侧边栏收起时只剩图标，输入框没有容身之处 */
.sidebar.is-collapsed .sb-search-input {
  display: none;
}

/* 收起后这个放大镜实际上是个按钮（点它展开并聚焦，见 sidebar.js），
   所以要有按钮该有的手型和反馈，不能还摆着文本输入的 I 形光标 */
.sidebar.is-collapsed .sb-item.sb-search {
  cursor: pointer;
}

.sidebar.is-collapsed .sb-item.sb-search:hover {
  background: rgba(255, 248, 240, 0.12);
  color: #fff9f3;
}

.mm-search {
  display: flex;
  align-items: center;
  gap: 10px;
  /* 让开绝对定位的关闭按钮（safe-area+16，高 46），并与下方链接的 44px 缩进对齐 */
  margin: calc(env(safe-area-inset-top, 0px) + 84px) 44px 0;
  padding: 0 0 14px;
  border-bottom: 1px solid rgba(255, 246, 238, 0.16);
  color: rgba(255, 246, 238, 0.55);
}

.mm-search svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.mm-search-input {
  font-size: 15px;
}

/* ---- 结果下拉面板 ---- */
.gs-panel {
  position: fixed;
  z-index: 300;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 16px;
  /* --paper-strong 有 0.82 不透明度，只剩 18% 能透出模糊、等于白加 backdrop-filter。
     这里单独调透并加大模糊半径，毛玻璃才真的看得出来 */
  background: rgba(255, 252, 248, 0.64);
  backdrop-filter: blur(26px) saturate(155%);
  -webkit-backdrop-filter: blur(26px) saturate(155%);
  box-shadow: 0 18px 48px rgba(40, 12, 4, 0.34);
  overflow: hidden;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.18s ease, transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.gs-panel[hidden] {
  display: none;
}

.gs-panel.is-open {
  opacity: 1;
  transform: none;
}

.gs-results {
  flex: 1;
  min-height: 0;
  padding: 8px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.gs-empty {
  margin: 0;
  padding: 26px 16px;
  color: var(--muted);
  font-size: 14px;
  text-align: center;
}

.gs-group + .gs-group {
  margin-top: 6px;
}

.gs-group-label {
  margin: 0;
  padding: 9px 12px 5px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
}

.gs-group-label span {
  opacity: 0.6;
}

.gs-item {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 8px 12px;
  border-radius: 10px;
  color: var(--ink);
  text-decoration: none;
}

.gs-item.is-cursor,
.gs-item:hover {
  background: rgba(187, 63, 60, 0.1);
}

.gs-item-main {
  flex: 1;
  min-width: 0;
}

.gs-item-title {
  display: block;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 15px;
  letter-spacing: -0.01em;
}

.gs-item-sub {
  display: block;
  margin-top: 1px;
  color: var(--muted);
  font-size: 12.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gs-item-meta {
  flex: none;
  color: var(--muted);
  font-size: 12px;
}

@media (max-width: 640px) {
  .gs-item-meta {
    display: none;
  }
}

/* ===================== 新闻报道页面 ===================== */
/* 列表就长在 .panel 这一层托盘上，条目本身不再是卡片：
   原来卡片套卡片（外层 panel 有底色+描边+阴影，每条 news-item 又来一遍），
   两层材质叠在一起，边框挨着边框，显得又闷又碎。
   改成苹果 inset grouped list 的做法——一张托盘，条目之间用一条发丝线分隔。 */
.news-list {
  display: flex;
  flex-direction: column;
  margin-top: 8px;
}

.news-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 4px;
  text-decoration: none;
  color: inherit;
  transition: background 0.18s ease;
}

.news-item::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(255, 236, 220, 0.18);
}

.news-item:last-child::after {
  display: none;
}

a.news-item:hover {
  background: rgba(255, 240, 226, 0.09);
}

a.news-item:active {
  background: rgba(255, 240, 226, 0.14);
}

/* hover 小动效：标题微微右移、箭头跟出去一点并变实 */
.news-item .n-body,
.news-item .n-arrow {
  transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1), color 0.18s ease;
}

a.news-item:hover .n-body {
  transform: translateX(4px);
}

a.news-item:hover .n-arrow {
  transform: translateX(6px);
  color: var(--red);
}

@media (prefers-reduced-motion: reduce) {
  .news-item .n-body,
  .news-item .n-arrow {
    transition: color 0.18s ease;
  }
  a.news-item:hover .n-body,
  a.news-item:hover .n-arrow {
    transform: none;
  }
}

.news-item .n-body {
  flex: 1;
  min-width: 0;
}

.news-item .n-title {
  margin: 0;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 18.5px;
  font-weight: 400;
  letter-spacing: -0.03em;
  color: #fff8ef;
  line-height: 1.45;
  transition: color 0.18s ease;
}

a.news-item:hover .n-title {
  color: #ffe1cc;
}

/* 来源与日期：中间加一个间隔点，两段信息才分得开；
   之前只靠 12px 间距，来源名一长就和日期粘成一句话 */
.news-item .n-meta {
  margin: 6px 0 0;
  color: rgba(255, 240, 226, 0.62);
  font-size: 13px;
  display: flex;
  flex-wrap: wrap;
  gap: 2px 9px;
  align-items: baseline;
}

.news-item .n-meta span::before {
  content: none;
}

.news-item .n-meta span + span::before {
  content: "·";
  display: inline-block;
  margin-right: 9px;
  color: rgba(255, 240, 226, 0.4);
}

/* 日期用等宽数字：几条并排时个位数对得齐，不会一条宽一条窄 */
.news-item .n-meta span:last-child {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

/* 自撰文章的小标记 */
.n-badge {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 9px;
  background: var(--red);
  color: #fff6ee;
  font-size: 11px;
  letter-spacing: 0.02em;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  vertical-align: middle;
  margin-left: 2px;
}

/* 覆盖 n-meta span:last-child 的等宽字体规则——badge 不需要 */
.news-item .n-meta span.n-badge {
  font-variant-numeric: normal;
  letter-spacing: 0.02em;
}

/* ═══════════════ 头版头条：与列表同一透明行风格，略大一号 ═══════════════ */
.news-hero {
  margin-bottom: 12px;
}

.nw-hero-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 4px;
  text-decoration: none;
  color: inherit;
  transition: background 0.18s ease;
  position: relative;
}

.nw-hero-card::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(255, 236, 220, 0.22);
}

.nw-hero-card:hover {
  background: rgba(255, 240, 226, 0.09);
}

.nw-hero-img {
  flex: none;
  width: 72px;
  height: 72px;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(120, 90, 78, 0.12);
}

.nw-hero-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.nw-hero-body {
  flex: 1;
  min-width: 0;
}

.nw-hero-title {
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 21px;
  font-weight: 500;
  line-height: 1.45;
  color: #fff8ef;
  margin: 0 0 8px;
  transition: color 0.18s ease;
}

.nw-hero-card:hover .nw-hero-title {
  color: #ffe1cc;
}

.nw-hero-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  color: rgba(255, 240, 226, 0.62);
}

.nw-hero-meta .n-badge {
  font-size: 11px;
  padding: 1px 7px;
  border-radius: 3px;
  background: var(--red);
  color: #fff6ee;
}

/* 列表缩略图 */
.nw-thumb {
  flex: none;
  width: 60px;
  height: 60px;
  border-radius: 6px;
  overflow: hidden;
  background: rgba(255, 240, 226, 0.08);
}

.nw-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ═══════════════ 新闻详情页（自撰文章）═══════════════ */

/* 新闻详情页标题缩小：不要在文章头上压 66px 的巨字 */
#ndTitle {
  font-size: clamp(26px, 3.5vw, 36px);
}

/* 页面容器与文章卡片 */
.nd-article {
  max-width: 860px;
  margin: 0 auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow), var(--inset);
  overflow: hidden;
}

/* 顶部配图 */
.nd-hero-img {
  margin: 0;
  width: 100%;
  max-height: 360px;
  overflow: hidden;
  background: rgba(80, 20, 18, 0.12);
}

.nd-hero-img img {
  width: 100%;
  height: auto;
  max-height: 360px;
  object-fit: cover;
  display: block;
}

/* 正文区域有独立内边距 */
.nd-body {
  padding: 32px 28px;
}

.nd-hero-img + .nd-body {
  padding-top: 24px;
}

/* 文章正文段落 */
.nd-article p {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 17px;
  line-height: 1.85;
  color: var(--text);
  margin: 0 0 1.2em;
  text-indent: 2em;
  letter-spacing: -0.01em;
}

.nd-article p:last-child {
  margin-bottom: 0;
}

/* 文章内嵌配图 */
.nd-fig {
  margin: 1.6em 0;
  text-indent: 0;
  border-radius: 6px;
  overflow: hidden;
}

.nd-fig img {
  max-width: 100%;
  height: auto;
  display: block;
  border-radius: 6px;
}

/* 底部返回导航 */
.nd-back {
  max-width: 860px;
  margin: 28px auto 0;
  text-align: center;
}

.nd-back .button {
  font-size: 15px;
  padding: 10px 28px;
}

/* 页首元数据：来源 · 日期 · 作者 */
.page-lead .nd-date::before,
.page-lead .nd-author::before {
  content: "·";
  display: inline-block;
  margin: 0 8px;
  color: var(--muted);
  opacity: 0.5;
}

.page-lead .nd-author {
  color: var(--muted);
}

/* 正文末尾编辑署名，右对齐 */
.nd-editor {
  font-family: "Noto Serif SC", "SiteLatin", serif !important;
  font-size: 14px !important;
  text-indent: 0 !important;
  text-align: right;
  color: var(--muted);
  margin: 2em 0 0 !important;
  padding-top: 1em;
  border-top: 1px solid var(--line);
}

@media (max-width: 640px) {
  .nd-article {
    margin: 0;
    border-radius: 12px;
    border-left: 0;
    border-right: 0;
  }

  .nd-body {
    padding: 22px 17px;
  }

  .nw-hero-img {
    width: 56px;
    height: 56px;
  }

  .nw-hero-title {
    font-size: 18px;
  }

  .nw-hero-body {
    padding: 16px 18px 18px;
  }

  .nw-thumb {
    width: 48px;
    height: 48px;
  }

  .nd-article p {
    font-size: 16px;
    text-indent: 1.6em;
  }
}

/* 箭头平时很淡，是个"可以点进去"的暗示而不是装饰；
   hover 时才实起来并跟着往前走（动效在上面 .n-arrow 的 transition 里） */
.news-item .n-arrow {
  flex: none;
  width: 18px;
  height: 18px;
  color: rgba(255, 225, 204, 0.45);
}

@media (max-width: 640px) {
  /* 一行一个作品 */
  .work-grid {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  .work-card {
    grid-template-rows: auto 1fr;
  }

  /* 标题图片放大到检索框宽度 */
  /* 手机上标题独占一屏顶部，字距收一点免得折行 */
  .hero-dept { letter-spacing: 0.03em; text-indent: 0.03em; }
  .hero-name { letter-spacing: 0.015em; text-indent: 0.015em; }

  .hero {
    padding-top: 180px;
  }

  /* 检索框和筛选框收窄，保留页边距 */
  .toolbar-shell {
    width: calc(100% - 38px);
    margin-top: 50px;
  }

  .toolbar {
    /* 同桌面：按钮列上限 110 → 98，让出来的宽度归输入框 */
    grid-template-columns: minmax(0, 1fr) minmax(74px, 98px);
    row-gap: 16px;
    column-gap: 9px;
  }

  .search-box input,
  .search-submit {
    /* 53 → 51.9（-2%） */
    min-height: 51.9px;
    height: 51.9px;
    font-size: 14px;
    border-radius: 999px;
  }

  .toolbar .search-submit {
    font-size: 19px;
    font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
    font-weight: 600;
  }

  .button.primary.search-submit {
    min-height: 51.9px;
    height: 51.9px;
    font-weight: 700;
  }

  .filters {
    margin: 0 -2.5px;
  }

  .filter-pill {
    margin: 0 2.5px;
    height: 42px;
    border-radius: 999px;
    padding: 0 12px;
  }

  /* 移动端：媒介面板改为底部弹出抽屉 + 遮罩。
     固定高度（取最高分类"报刊"所需高度），切换分类时抽屉不跳动，
     短列表内部留白、长列表内部滚动 */
  .media-panel {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 120;
    width: 100%;
    height: min(62vh, 460px);
    max-height: none;
    padding-top: 18px;
    border-radius: 22px 22px 0 0;
    border-bottom: 0;
    background: rgba(255, 250, 244, 0.94);
    overflow: hidden;
    transform: translateY(100%);
    padding-bottom: env(safe-area-inset-bottom, 0);
    transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1),
      visibility 0s linear 0.34s;
    opacity: 1;
  }

  /* 底部抽屉抓手 */
  .media-panel::before {
    content: "";
    position: absolute;
    top: 8px;
    left: 50%;
    width: 38px;
    height: 4px;
    border-radius: 999px;
    background: rgba(67, 34, 20, 0.22);
    transform: translateX(-50%);
  }

  .media-panel.is-open {
    transform: none;
    transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1),
      visibility 0s;
  }

  /* 平铺形态必须在这里显式覆盖一次宽度。
     它的选择器 .media-panel.is-flat 比上面那条 .media-panel 多一个类，
     而媒体查询是不加优先级的——不覆盖的话，桌面那个 260px 的小面板宽度
     会一路赢到移动端，把整条底部抽屉压成贴着左边的一根窄条。

     高度不再放开成 auto：和其他分类抽屉拉齐（min(62vh, 460px)），
     连续点开几个筛选时抽屉不忽高忽低。

     内容一律贴顶：曾经为了"只有三个选项时别显得空"让它垂直居中，
     但抽屉是定高的，居中会让列表悬在屏幕中间、和抓手之间空出一大截，
     看着像没加载完。底部留白是定高抽屉的正常样子，不用靠居中去掩饰。 */
  .media-panel.is-flat {
    width: 100%;
  }

  .media-panel.is-flat .mp-items {
    justify-content: flex-start;
  }

  .mp-cats {
    width: 124px;
    padding: 10px 8px;
  }

  .mp-items {
    padding: 10px 8px;
  }

  .mp-cat,
  .mp-item {
    font-size: 16px;
    padding: 13px 14px;
  }

  /* 左边仍要留 28px 给选中的勾：.mp-check 靠 margin-left:-21px 挂进这段留白，
     上面那条把 padding 压回 14px 的话，勾会被顶到按钮外面、被圆角裁掉半个。 */
  .mp-item {
    font-size: 15px;
    padding-left: 28px;
  }

  .mp-overlay {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 119;
    background: rgba(30, 8, 2, 0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
  }

  .mp-overlay.is-open {
    opacity: 1;
    pointer-events: auto;
  }

  .filters span {
    font-size: 17px;
    font-weight: 700;
    left: 4px;
    right: 4px;
    letter-spacing: -0.06em;
  }

  .filter-pill::after {
    display: none;
  }

  .news-item {
    /* 左右仍由 --panel-pad 决定（要顶到托盘边），只压上下行高 */
    padding-top: 13px;
    padding-bottom: 13px;
  }
  .news-item .n-title {
    font-size: 17px;
  }

  /* 窄屏放不下一行，来源和日期会各占一行——这时那个间隔点就孤零零挂在
     日期前面（"· 2026-07-14"），像个没写完的项目符号。换行就不要点了。 */
  .news-item .n-meta {
    flex-direction: column;
    gap: 2px;
  }

  .news-item .n-meta span + span::before {
    display: none;
  }

  .work-card h3,
  .card-title {
    font-size: 22px;
  }

  .work-card .card-subtitle {
    font-size: 16.5px;
    font-weight: 600;
  }

  .work-card p {
    font-size: 14px;
  }

  .work-card .author-line {
    font-size: 16px;
  }

  .work-body {
    padding: 20px 24px 20px;
  }


  .result-line {
    font-size: 20px;
  }

  .tag {
    font-size: 13px;
  }
}

/* 正文段落保持中等字重，标题/数字/按钮/标签保留各自的粗细层次 */
.section-heading p,
.about-section p,
.work-card p,
.stats-band span,
.class-card span,
.viewer-meta p {
  font-weight: 500;
}





/* 介绍/新闻页入场动画 */
.page-wrap {
  animation: fadeSlideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes fadeSlideUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .page-wrap {
    animation: none;
  }
}


/* 创作者卡片 */
.about-creator-card {
  display: flex !important;
  align-items: center;
  gap: 20px;
  margin-top: 32px;
  padding: 24px 32px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  box-shadow: var(--shadow), var(--inset);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

.creator-logo {
  width: 60px;
  height: 60px;
  object-fit: contain;
  flex-shrink: 0;
}

.creator-info {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.creator-name {
  margin: 0;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 17px;
  color: var(--ink);
  letter-spacing: -0.02em;
}

@media (max-width: 640px) {
  .about-creator-card {
    padding: 16px 20px;
    gap: 14px;
  }

  .creator-logo {
    width: 48px;
    height: 48px;
  }

  .creator-name {
    font-size: 15px;
  }

  .creator-logo {
    width: 48px;
    height: 48px;
  }

  .creator-name {
    font-size: 15px;
  }
}




@keyframes sheetDown {
  from {
    transform: translateY(0);
    opacity: 1;
  }
  to {
    transform: translateY(30%);
    opacity: 0;
  }
}


/* ---------- 获奖页：比赛分栏 + 届数时间轴 ----------
   一级轴是比赛（同一比赛内「二等奖」只有一个含义、一种质感），
   二级轴是届数（早的在上），等级降级成作品卡角上的小奖牌。
   分栏、届数、等级筛选项全部由 data.js 自动推导，新增比赛无需改版式。 */
.award-tabs {
  display: flex;
  gap: 6px;
  width: fit-content;
  max-width: 100%;
  margin: 0 0 16px;
  padding: 5px;
  border: 1px solid rgba(255, 246, 238, 0.16);
  border-radius: 999px;
  background: rgba(255, 246, 238, 0.1);
  backdrop-filter: blur(14px) saturate(130%);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  overflow-x: auto;
  scrollbar-width: none;
}

.award-tabs::-webkit-scrollbar {
  display: none;
}

.award-tab {
  flex: none;
  padding: 9px 20px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: rgba(255, 246, 238, 0.82);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  /* 与站内弹窗同款的减速曲线（quint-out），不是线性 */
  transition: background 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    color 0.28s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.award-tab:active {
  transform: scale(0.97);
}

.award-tab:hover {
  color: #fff6ee;
  background: rgba(255, 246, 238, 0.12);
}

.award-tab.is-active {
  background: var(--red);
  color: #fff6ee;
  box-shadow: 0 2px 12px rgba(140, 31, 30, 0.45);
}

/* 等级筛选胶囊：选项按当前比赛的奖级自动生成 */
.award-chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 34px;
}

.award-chip {
  padding: 6px 15px;
  border: 1px solid rgba(255, 246, 238, 0.26);
  border-radius: 999px;
  background: rgba(255, 246, 238, 0.08);
  color: rgba(255, 246, 238, 0.88);
  font-family: "SiteLatin", "Noto Serif SC", "Songti SC", serif;
  font-size: 14px;
  line-height: 1.4;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.award-chip:hover {
  background: rgba(255, 246, 238, 0.16);
}

.award-chip.is-selected {
  border-color: #fff6ee;
  background: #fff6ee;
  color: var(--red-deep);
}

/* 时间轴：左侧一条竖线串起各届，节点圆点对齐届标题 */
.award-timeline {
  position: relative;
}

/* 切换比赛/等级后各届依次浮入：quint-out 减速，越往下延迟越大。
   延迟由 JS 按序号写在行内，届数再多也能接上 */
@keyframes tlNodeIn {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.tl-node {
  position: relative;
  padding-left: 32px;
  padding-bottom: 44px;
  animation: tlNodeIn 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .tl-node {
    animation: none;
  }
}

.tl-node:last-child {
  padding-bottom: 0;
}

/* 竖线从本节点圆点画到下一个节点；末节点不画 */
.tl-node::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 22px;
  bottom: 0;
  width: 2px;
  background: linear-gradient(180deg, rgba(255, 246, 238, 0.34), rgba(255, 246, 238, 0.08));
}

.tl-node:last-child::before {
  display: none;
}

.tl-dot {
  position: absolute;
  left: 0;
  top: 6px;
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 246, 238, 0.55);
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 4px rgba(140, 31, 30, 0.22);
}

.tl-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-bottom: 18px;
}

.tl-session {
  margin: 0;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 26px;
  letter-spacing: -0.01em;
  color: #fff8ef;
  text-shadow: 0 3px 12px rgba(50, 15, 4, 0.4);
}

.tl-date {
  color: rgba(255, 246, 238, 0.62);
  font-size: 14px;
  letter-spacing: 0.02em;
}

/* 该届的奖牌小结：有几项就出现几枚，数量自动统计。
   原来 margin-left:auto 把它顶到最右，宽屏上离它所注解的「第三届」有七百多像素，
   看着像浮在页面边缘的孤立元素。改成紧跟在日期后面——注解就该贴着被注解的对象。 */
.tl-medals {
  display: flex;
  gap: 6px;
  margin-left: 4px;
}

.tl-medal {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border: 1px solid var(--met-line);
  border-radius: 999px;
  background: var(--met-bg);
  color: var(--met-ink);
  box-shadow: inset 0 1px 0 var(--met-top);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12px;
  font-weight: 600;
}

.tl-node .work-grid {
  margin-top: 0;
}

/* 作品卡角上的小奖牌：与大奖项卡同源的四档金属质感 */
.work-card .thumb {
  position: relative;
}

.tier-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px 5px 9px;
  border: 1px solid var(--met-line);
  border-radius: 999px;
  background: var(--met-bg);
  color: var(--met-ink);
  box-shadow: inset 0 1px 0 var(--met-top), 0 3px 10px rgba(40, 12, 4, 0.32);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
}

.tier-badge svg,
.tier-badge span {
  position: relative;
  z-index: 1;
}

.tier-badge svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* 同款金属拉丝，缩放到徽章尺寸 */
.tier-badge::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='240'%20height='240'%3E%3Cfilter%20id='n'%20x='0'%20y='0'%20width='100%25'%20height='100%25'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.0125%200.5'%20numOctaves='3'%20seed='8'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='saturate'%20values='0'/%3E%3C/filter%3E%3Crect%20width='240'%20height='240'%20filter='url%28%23n%29'/%3E%3C/svg%3E");
  background-size: 120px 120px;
  mix-blend-mode: overlay;
  opacity: 0.8;
  pointer-events: none;
}

.tier-badge.award-red::before {
  opacity: 0.4;
}

.awards-empty {
  padding: 60px 0;
  color: rgba(255, 246, 238, 0.8);
  text-align: center;
}

@media (max-width: 640px) {
  .tl-node {
    padding-left: 24px;
    padding-bottom: 34px;
  }

  .tl-dot {
    width: 14px;
    height: 14px;
    top: 7px;
  }

  .tl-node::before {
    left: 6px;
    top: 20px;
  }

  .tl-session {
    font-size: 21px;
  }

  /* 窄屏放不下时奖牌小结换行到标题下方，不挤压届号 */
  .tl-medals {
    margin-left: 0;
  }

  /* 窄屏上比赛名很长，横向滚动会把第二个比赛推出屏幕、根本发现不了。
     改成整块换行、每个比赛占满一行，两个比赛永远同时可见。 */
  .award-tabs {
    flex-wrap: wrap;
    width: 100%;
    overflow: visible;
    gap: 5px;
    border-radius: 20px;
  }

  .award-tab {
    flex: 1 1 100%;
    padding: 10px 14px;
    font-size: 14.5px;
    font-weight: 700;
    line-height: 1.35;
    white-space: normal;
    text-align: center;
  }

  .tier-badge {
    font-size: 12px;
    font-weight: 700;
    padding: 4px 9px 4px 7px;
  }
}


/* ===================== 时间图谱（timeline.html） =====================
   一条共用的年份轴：上面是各年代的作品密度，下面每件作品一条跨度带。
   横坐标全部用百分比，由 timeline.js 按实际年份范围现算——
   以后作品时段跨到更早或更晚，轴自己会长过去，这里不用改。 */
.tlx {
  --panel-pad: 30px;
  position: relative;
  padding: var(--panel-pad);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-strong);
  box-shadow: var(--shadow), var(--inset);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

/* —— 年代密度条 —— */
.tlx-density {
  display: flex;
  flex-direction: column;
  gap: 6px;
  height: 132px;
  margin-bottom: 6px;
}

/* 读数行：专门空出来的一条，柱子再高也顶不到它，指针也压不到它。
   这就是"件数放柱子上方、hover 才出现"里那个"上方"。 */
.tlx-readout {
  position: relative;
  flex: none;
  height: 19px;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
  line-height: 19px;
  white-space: nowrap;
}

.tlx-ro-dim {
  color: rgba(95, 76, 69, 0.62);
}

/* 指到某年时，读数横向跟着那根柱子走（--ro-x 由 timeline.js 写入） */
.tlx-readout.is-pinned {
  color: var(--red-deep);
}

.tlx-readout.is-pinned .tlx-ro-y,
.tlx-readout.is-pinned .tlx-ro-n {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
}

.tlx-ro-y {
  left: var(--ro-x, 50%);
  margin-left: -26px;
  font-weight: 700;
}

.tlx-ro-n {
  left: var(--ro-x, 50%);
  margin-left: 26px;
  color: var(--muted);
}

.tlx-bars {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: flex-end;
  gap: 1px;
}

/* 一年一根。柱子很窄，热区靠 padding 往两边扩，指针不用瞄得很准 */
.tlx-yr {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.tlx-yr-bar {
  width: 100%;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(180deg, rgba(187, 63, 60, 0.92), rgba(140, 31, 30, 0.72));
  transition: background 0.14s ease, transform 0.14s ease;
  transform-origin: bottom center;
}

.tlx-yr.is-void .tlx-yr-bar {
  background: rgba(120, 90, 78, 0.22);
}

.tlx-yr:hover .tlx-yr-bar,
.tlx-yr:focus-visible .tlx-yr-bar {
  background: linear-gradient(180deg, #d2504c, var(--red-deep));
  transform: scaleX(1.6);
}

.tlx-yr.is-on .tlx-yr-bar {
  background: linear-gradient(180deg, #8c1f1e, #6d1615);
  transform: scaleX(1.6);
}

.tlx-axis {
  position: relative;
  height: 22px;
  margin-bottom: 14px;
  border-top: 1px solid var(--line-dark);
}

.tlx-tick {
  position: absolute;
  top: 5px;
  transform: translateX(-50%);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 11.5px;
  color: var(--muted);
  white-space: nowrap;
}

.tlx-tick::before {
  content: "";
  position: absolute;
  top: -6px;
  left: 50%;
  width: 1px;
  height: 5px;
  background: var(--line-dark);
}

/* —— 作品跨度带 —— */
.tlx-lanes {
  --lane-h: 26px;
  position: relative;
  height: calc(var(--lanes, 1) * var(--lane-h));
  transition: height 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.tlx-bar {
  position: absolute;
  top: calc(var(--lane) * var(--lane-h));
  height: 21px;
  /* 只跨一两年的作品按比例算出来才几个像素，点不着；给一个下限 */
  min-width: 16px;
  padding: 0 9px;
  display: flex;
  align-items: center;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, rgba(187, 63, 60, 0.9), rgba(140, 31, 30, 0.85));
  color: #fff6ee;
  cursor: pointer;
  overflow: hidden;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(60, 20, 8, 0.18);
  transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.18s ease, filter 0.18s ease;
}

.tlx-bar-label {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
  letter-spacing: -0.02em;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 条身上放不下完整标题（timeline.js 量过像素宽后打的标记）：不显示标题。
   曾经把标题甩到条外面，结果是顶出托盘被裁、还和邻条的字互相叠——
   现在交给悬浮提示，划过就有完整标题、年份和作者。 */
.tlx-bar.is-mute .tlx-bar-label {
  display: none;
}

.tlx-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12px;
}

.tlx-legend li {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.tlx-key {
  width: 16px;
  height: 8px;
  border-radius: 999px;
}

.tlx-key-poster {
  background: linear-gradient(90deg, rgba(187, 63, 60, 0.9), rgba(140, 31, 30, 0.85));
}

.tlx-key-long {
  background: linear-gradient(90deg, rgba(190, 122, 45, 0.92), rgba(148, 88, 26, 0.86));
}

.tlx-key-web {
  background: linear-gradient(90deg, rgba(96, 118, 108, 0.92), rgba(62, 84, 76, 0.86));
}

/* 三种作品类型给三个色，竖着扫一眼能看出分布 */
.tlx-bar[data-genre="数据长图"] {
  background: linear-gradient(90deg, rgba(190, 122, 45, 0.92), rgba(148, 88, 26, 0.86));
}

.tlx-bar[data-genre="数据网页"] {
  background: linear-gradient(90deg, rgba(96, 118, 108, 0.92), rgba(62, 84, 76, 0.86));
}

.tlx-bar:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(60, 20, 8, 0.32);
  filter: saturate(1.15) brightness(1.06);
  z-index: 2;
}

.tlx-empty {
  padding: 30px 0;
  color: var(--muted);
  text-align: center;
}

.tlx-foot {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 16px 2px 0;
  color: rgba(255, 246, 238, 0.82);
}

.tlx-count {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 14px;
}

/* 悬停提示：跨度带太窄，标题和年份塞不进去，另外浮一条出来 */
/* hidden 属性必须显式关掉。
   下面那条 display:flex 是作者样式，优先级高于浏览器给 [hidden] 的 display:none——
   于是 timeline.js 里 els.tip.hidden = true 根本不起作用，提示条从头到尾都在屏幕上。
   前两轮我一直在补事件（离开容器、滚动、点面板…），但那些判定其实都跑到了，
   只是"藏"这个动作被这条规则吃掉了。 */
.tlx-tip[hidden] {
  display: none;
}

.tlx-tip {
  position: fixed;
  z-index: 300;
  max-width: 280px;
  padding: 9px 13px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  border-radius: 12px;
  background: rgba(40, 15, 6, 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff6ee;
  box-shadow: 0 12px 30px rgba(30, 10, 3, 0.4);
  pointer-events: none;
}

.tlx-tip strong {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 15px;
  font-weight: 400;
  letter-spacing: -0.02em;
}

.tlx-tip span {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12px;
  color: rgba(255, 246, 238, 0.7);
}

@media (max-width: 720px) {
  .tlx {
    --panel-pad: 16px;
  }

  .tlx-density {
    height: 104px;
  }

  .tlx-density {
    height: 108px;
  }

  .tlx-readout {
    font-size: 11.5px;
  }

  .tlx-ro-y { margin-left: -22px; }
  .tlx-ro-n { margin-left: 22px; }

  /* 窄屏一年只有两三像素，柱间那 1px 缝隙会把柱子吃掉一半，取消 */
  .tlx-bars {
    gap: 0;
  }

  .tlx-lanes {
    --lane-h: 24px;
  }

  /* 窄屏上条更短，字压小一号，能多留住几条标题 */
  .tlx-bar-label {
    font-size: 11px;
  }

  .tlx-bar {
    height: 18px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tlx-lanes,
  .tlx-yr-bar,
  .tlx-bar {
    transition: none;
  }
  .tlx-bar:hover,
  .tlx-yr:hover .tlx-yr-bar {
    transform: none;
  }
}

/* 时间图谱：两块图各自的小标题 —— 之前只有柱子和条，没有一句话说这是什么 */
.tlx-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-bottom: 12px;
}

.tlx-head-lanes {
  margin-top: 22px;
}

.tlx-h {
  margin: 0;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 17px;
  font-weight: 400;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.tlx-hint {
  margin: 0;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
  color: var(--muted);
}

/* 时间图谱页的筛选药丸：这页没有工具栏，自己起一行 */
.tlx-filters {
  margin: 0 0 18px;
}

.tlx-filters .filters {
  margin: 0 -6px;
  flex-wrap: wrap;
}

.tlx-filters .filter-pill {
  flex: 0 1 auto;
  min-width: 118px;
  margin: 0 6px 8px;
  height: 42px;
}

@media (max-width: 720px) {
  .tlx-h {
    font-size: 15.5px;
  }

  .tlx-hint {
    font-size: 11.5px;
  }

  .tlx-filters .filters {
    margin: 0 -3px;
  }

  /* 窄屏两个一行，四个筛选器排成两行，不横向挤成一条 */
  .tlx-filters .filter-pill {
    flex: 1 1 calc(50% - 6px);
    min-width: 0;
    margin: 0 3px 6px;
  }
}

/* ===================== 排序切换 ===================== */
.sort-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 28px;
  padding: 0 11px 0 8px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(246, 225, 218, 0.78);
  box-shadow: 0 2px 8px rgba(54, 19, 7, 0.12);
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}
.sort-toggle:hover { background: rgba(246, 215, 205, 0.92); }
.sort-toggle:active { transform: scale(0.96); }
.sort-toggle .st-ico {
  width: 14px; height: 14px; flex: none;
  transition: transform 0.2s ease;
}

/* ===================== 已选条件胶囊 ===================== */
.active-chips {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 4px;
}
.active-chips[hidden] { display: none; }

.ac-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 26px;
  padding: 0 8px 0 11px;
  border: 1px solid var(--line);
  border-radius: 13px;
  background: rgba(246, 225, 218, 0.78);
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.1s ease;
}
.ac-chip:hover { background: rgba(246, 210, 198, 0.92); }
.ac-chip:active { transform: scale(0.94); }
.ac-chip svg { opacity: 0.5; flex: none; }
.ac-chip:hover svg { opacity: 1; }

/* ===================== 视图切换 + 目录视图 ===================== */
/* 靠右：margin-left:auto 把这一组推到结果行最右端，
   左边的"共 N 件作品"占多宽都不影响 */
.result-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-left: auto;
}

/* 件数和「重置筛选」是一组：按钮撤销的就是这个数字，两者贴在一起 */
.result-count-wrap {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

/* 分段控件：两个按钮共用一个胶囊底，选中的那个抬起来 */
/* 尺寸完全对齐 .text-button：同样 30px 高、同样的描边圆角和底色，
   两个控件并排时是一条线上的，不会一高一矮 */
.view-switch {
  display: flex;
  gap: 2px;
  padding: 1px;
  border: 1px solid var(--line);
  border-radius: 15px;
  background: rgba(246, 225, 218, 0.78);
  box-shadow: 0 2px 8px rgba(54, 19, 7, 0.12);
}

.vs-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 14px;
  justify-content: center;
  border: 0;
  border-radius: 13px;
  background: transparent;
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, width 0.2s ease;
  white-space: nowrap;
}

/* 结果行浮出来之前（还在首屏、没往下看作品），这两个按钮先不出现 */
.result-actions {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.result-line.is-visible .result-actions {
  opacity: 1;
  pointer-events: auto;
}

.vs-btn svg {
  width: 15px;
  height: 15px;
  flex: none;
}

.vs-btn:hover {
  background: rgba(187, 63, 60, 0.12);
}

.vs-btn.is-on {
  background: var(--red);
  color: #fff6ee;
  box-shadow: 0 2px 8px rgba(120, 25, 23, 0.28);
}

/* 未选中时只显示图标，隐藏文字 */
.vs-btn:not(.is-on) .vs-label {
  display: none;
}
/* 未选中时只保留图标，但宽度稍宽一点，避免图标贴边 */
.vs-btn:not(.is-on) {
  width: 36px;
  padding: 0;
}

/* 目录视图：纯透明容器，每条单独一张卡片 */
.work-grid.is-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: transparent;
}

/* 回到页面顶部时整体淡出 */
.work-grid.is-list.is-off {
  opacity: 0;
  transition: opacity 0.62s ease;
}

.work-row {
  background: var(--paper-strong);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow), var(--inset);
  overflow: hidden;
  transition: opacity 0.62s ease, transform 0.62s ease;
}

.work-row:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 18px rgba(54, 19, 7, 0.13), var(--inset);
}

.wr-hit {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 22px;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: background 0.18s ease;
}

/* 悬停：底色 + 缩略图轻微放大 + 箭头右移，和卡片视图的抬起是同一种手感 */
.wr-hit:hover {
  background: rgba(120, 45, 20, 0.055);
}

.wr-hit:active {
  background: rgba(120, 45, 20, 0.1);
}

.wr-hit:hover .wr-thumb img {
  transform: scale(1.08);
}

.wr-hit:hover .wr-arrow {
  transform: translateX(3px);
  color: var(--red);
}

.wr-hit:hover .wr-title {
  color: var(--red-deep);
}

/* 缩略图：固定正方形区域，图片 contain 居中，保证左右边距一致 */
.wr-thumb {
  flex: none;
  width: 72px;
  height: 72px;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(120, 90, 78, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
}

.wr-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  transition: transform 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}

.wr-thumb .placeholder {
  width: 72px;
  height: 72px;
  font-size: 10px;
}

.wr-thumb .placeholder span {
  display: none;
}

.wr-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.wr-title {
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 20px;
  transition: color 0.18s ease;
  letter-spacing: -0.03em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wr-sub {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 14px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wr-meta {
  flex: 0 1 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.wr-author {
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 14px;
  color: var(--muted);
  white-space: nowrap;
}

.wr-tags {
  display: flex;
  gap: 6px;
}

.wr-arrow {
  flex: none;
  width: 18px;
  height: 18px;
  color: rgba(140, 31, 30, 0.45);
  transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1), color 0.18s ease;
}

/* 窄屏：元数据那一列放不下，收起作者和标签，只留题名 */
@media (max-width: 820px) {
  .result-actions {
    gap: 10px;
  }

  /* 手机端不给目录视图：一行要塞下缩略图、题名、副题、作者、三个标签，
     窄屏根本排不开，只能把元数据全藏掉——剩下的和卡片视图比毫无优势。
     直接不提供这个选项，比提供一个残缺的好。 */
  .view-switch {
    display: none;
  }

  .wr-hit {
    padding: 16px;
    gap: 12px;
  }

  .wr-thumb {
    width: 56px;
    height: 56px;
  }

  .wr-thumb img {
    width: 100%;
    height: 100%;
  }

  .wr-title {
    font-size: 17px;
  }

  .wr-meta {
    display: none;
  }
}

/* 时间图谱：横向滚动容器。
   上（逐年柱）下（作品区间条）共用同一条年份轴，必须锁在同一个画布里一起滚，
   否则一滚就对不上年份了。桌面上内容本来就放得下，滚不动，等于不存在。 */
.tlx-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /* 滚出去的内容贴着托盘内边缘走，不要在中间留白边 */
  margin: 0 calc(var(--panel-pad, 34px) * -1);
  padding: 0 var(--panel-pad, 34px);
  scrollbar-width: thin;
}

.tlx-canvas {
  min-width: 100%;
  /* 左右留出空间给年份刻度标签：刻度用 translateX(-50%) 居中，
     边缘年份（如 1840）的标签左半边会跑到画布外面被 overflow 裁掉，
     加上 padding 后刻度就有了呼吸空间 */
  padding-left: 26px;
  padding-right: 26px;
}

/* 中间那条小标题跟着画布滚会跑出视野，钉在左边 */
.tlx-scroll .tlx-head-lanes {
  position: sticky;
  left: 0;
  width: max-content;
  max-width: 100%;
}

@media (max-width: 820px) {
  /* 一百二十多年挤在 300 多像素里，一年不到 3px，柱子和条都糊成一片。
     给画布一个下限宽度，改成左右拖着看。 */
  .tlx-canvas {
    min-width: 720px;
  }

  /* 提示用户这里是可以拖的 */
  .tlx-scroll {
    scroll-snap-type: none;
  }

  .tlx-head-lanes .tlx-hint::after {
    content: " · 左右拖动看全部年份";
  }
}

/* ===================== 高级筛选 ===================== */
/* 入口收进搜索框右侧，只留一个图标 */
.search-box {
  position: relative;
}

.search-box input {
  /* 给右边的图标腾出位置，别让长关键词压到它下面 */
  padding-right: 58px;
}

/* 输入框高 58、按钮 36，上下各余 11px；右边也留 11px，四边看着才是一样宽的 */
.adv-toggle {
  position: absolute;
  right: 11px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: rgba(95, 76, 69, 0.7);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, transform 0.24s cubic-bezier(0.34, 1.4, 0.5, 1);
}

.adv-toggle svg {
  width: 17px;
  height: 17px;
}

.adv-toggle:hover {
  background: rgba(120, 45, 20, 0.09);
  color: var(--ink);
}

.adv-toggle.is-on {
  background: var(--red);
  color: #fff6ee;
  transform: translateY(-50%) rotate(90deg);
}

/* 进入高级模式：药丸那一排让位给面板，收起来的过程要能看见 */
.filters {
  transition: opacity 0.26s ease, transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              max-height 0.32s cubic-bezier(0.22, 1, 0.36, 1), margin 0.3s ease;
  max-height: 200px;
  transform-origin: top center;
}

.toolbar.is-adv .filters,
body.adv-mode .toolbar .filters {
  max-height: 0;
  margin: 0;
  opacity: 0;
  transform: translateY(-6px) scale(0.98);
  pointer-events: none;
  overflow: hidden;
}

/* 面板展开/收起：高度 + 透明度一起过渡 */
.adv-panel {
  max-height: 0;
  opacity: 0;
  transform: translateY(-8px);
  overflow: hidden;
  transition: max-height 0.36s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.26s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1),
              padding 0.3s ease;
  padding-top: 0;
  padding-bottom: 0;
}

/* 条件多的时候面板会很高，给个上限让它自己滚，别把整屏结果都顶掉。
   上限还要留出搜索框和把手的位置——工具栏吸顶之后，
   「面板 + 把手」得整个装进一屏，否则把手又得往下翻才看得见。 */
.adv-panel.is-open {
  max-height: min(60vh, 780px);
  opacity: 1;
  transform: none;
  padding-top: 14px;
  padding-bottom: 14px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ===== 高级检索：整屏专注模式 =====

   点开高级筛选后，屏幕上只留搜索框、筛选面板和把手，三者上下居中，
   其余内容退到蒙版后面。原来面板是挤在页面顶部一条里展开的，
   空间局促、还会把作品墙顶下去；这里空间管够，排布也松得开。

   另一件事：这里把 .adv-panel 的 transition 整个关掉。
   原先展开靠 max-height 从 0 过渡到 min(60vh,780px)，
   一旦过渡没能起步（面板刚从 hidden 出来那一帧），
   max-height / opacity / transform / padding 这四个被过渡接管的属性
   就会一直停在关闭态的值上——面板明明有 is-open、内容也在，
   高度却只有 20 多像素，就是那个"选完条件面板显示不出来"的 bug。
   专注模式下面板本来就该是完全展开的，不需要高度动画，关掉即无从卡住。 */
body.adv-mode {
  overflow: hidden;
}

body.adv-mode .toolbar-shell {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  /* 常态下它是 min(820px,100%) 的居中条，铺满整屏得把宽度限制解开 */
  width: 100%;
  max-width: none;
  padding: 24px;
  margin: 0;
  background: rgba(38, 11, 3, 0.72);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  animation: advFadeIn 0.28s ease both;
}

@keyframes advFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* 保持工具栏原本的两列网格：搜索框和「检索」自然并排在第一行，
   面板和把手各自 grid-column:1/-1 通栏。改成 flex 纵向反而要把按钮再拽回去。 */
body.adv-mode .toolbar {
  width: min(880px, 100%);
  margin: 0;
  overflow: visible;
  animation: advRise 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes advRise {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* 搜索框在专注模式下是主角，放大一档 */
body.adv-mode .search-box {
  width: 100%;
}

body.adv-mode .search-box input {
  font-size: 19px;
  padding-top: 14px;
  padding-bottom: 14px;
}

/* 面板在专注模式下完全展开，高度交给内容，超高才自己滚 */
body.adv-mode .adv-panel {
  transition: none;
  opacity: 1;
  transform: none;
  overflow-y: auto;
  padding-top: 18px;
  padding-bottom: 18px;
  /* 留出搜索框那一行和底下的已选托盘，剩下的高度都给面板，超了自己滚。
     已选条件多时托盘会换行变高，所以留得宽裕些，宁可面板矮一点，
     也不能把把手挤到屏幕外——那正是之前"把手要手动划到最下面"的老毛病。 */
  max-height: calc(100dvh - 310px);
}

/* 收起时只留那一条已选条件，面板真正收掉 */
body.adv-mode .adv-panel.is-rolled {
  display: none;
}

@media (max-width: 640px) {
  body.adv-mode .toolbar-shell { padding: 12px; }
  body.adv-mode .search-box input { font-size: 17px; }
  body.adv-mode .adv-panel { padding-top: 12px; padding-bottom: 12px; }
}

.adv-head {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 2px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

.adv-title {
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 15px;
  letter-spacing: 0.02em;
}

.adv-head-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.adv-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

.adv-close svg {
  width: 15px;
  height: 15px;
}

.adv-close:hover {
  background: rgba(187, 63, 60, 0.12);
  color: var(--red-deep);
}

/* 标签列：筛选器名在上、大类名在下，两行都靠右收齐，
   药丸那一列才有一条干净的起始线 */
.adv-label {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  line-height: 1.3;
}

.adv-label-main {
  color: var(--ink);
  font-size: 13.5px;
}

.adv-label-sub {
  color: var(--muted);
  font-size: 11.5px;
}

/* 每个筛选器的第一行上面拉一条细线，把几组条件断开 */
.adv-group.is-head > .adv-label,
.adv-group.is-head > .adv-chips {
  margin-top: 4px;
  padding-top: 9px;
  border-top: 1px solid var(--line);
}


/* 生效条件数亮在工具栏按钮上：面板收起时也一眼看得出筛没筛、筛了几条。
   角标绝对定位挂在按钮右上角，不占按钮内部空间。 */
.adv-toggle[data-count]:not([data-count=""])::after {
  content: attr(data-count);
  position: absolute;
  top: -5px;
  right: -5px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--red-deep, #8c1f1e);
  color: #fff6ee;
  font-size: 11px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px rgba(255, 250, 244, 0.95);
}

/* 按钮进入高级模式时会整体旋转 90°，角标得转回来，
   否则数字是躺着的——"8" 躺下来看着就是个 ∞ */
.adv-toggle.is-on[data-count]:not([data-count=""])::after {
  transform: rotate(-90deg);
}


/* #advPickers 只是个容器，让里面的 .adv-group 直接落到面板网格上 */
#advPickers {
  display: contents;
}

/* 两列网格：左边一列是标签，右边一列是控件。各组用 display:contents
   把自己拆成两个格子，这样五行的标签能对齐成一条竖线。 */
.adv-panel {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  align-items: center;
  gap: 10px 14px;
  margin-top: 4px;
  /* 上下内边距交给上面的展开动画控制，这里只定左右 */
  padding-left: 16px;
  padding-right: 16px;
  border: 1px solid var(--line);
  border-radius: 22px;
  /* 面板压在卡片墙上方，半透明会把下面的作品图透上来，糊到看不清标签 */
  background: rgba(255, 250, 244, 0.96);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 10px 28px rgba(52, 18, 7, 0.12);
}

/* hidden 属性得显式关掉，否则上面的 display:flex 会把它盖过去
   （和 .tlx-tip 踩过的是同一个坑） */
.adv-panel[hidden] {
  display: none;
}

/* 拆成两个格子交给 .adv-panel 的网格，标签才能列对齐 */
.adv-group {
  display: contents;
}

.adv-label {
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13.5px;
  white-space: nowrap;
}

/* 可多选的条件用药丸，和单选的分段控件在视觉上区分开 */
.adv-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.adv-chip {
  height: 28px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: rgba(246, 225, 218, 0.45);
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;
}

.adv-chip:hover {
  background: rgba(187, 63, 60, 0.12);
  border-color: rgba(187, 63, 60, 0.35);
}

.adv-chip[aria-pressed="true"] {
  background: var(--red);
  border-color: var(--red);
  color: #fff6ee;
}

.adv-foot {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 2px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.adv-clear[hidden] {
  display: none;
}

/* 分段控件：和视图切换同一套规格，站内两处分段选择长得一样 */
.adv-seg {
  display: flex;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: 13px;
  background: rgba(246, 225, 218, 0.6);
}

.adv-seg-btn {
  height: 26px;
  padding: 0 12px;
  border: 0;
  border-radius: 11px;
  background: transparent;
  color: var(--red-deep);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

.adv-seg-btn:hover {
  background: rgba(187, 63, 60, 0.12);
}

.adv-seg-btn.is-on {
  background: var(--red);
  color: #fff6ee;
}

.adv-note {
  margin: 0;
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
}

/* 多选模式下面板里给个提示：勾是可以叠加的 */
.media-panel.is-multi .mp-item.is-selected {
  color: var(--red-deep);
}

@media (max-width: 640px) {
  .adv-row {
    justify-content: flex-start;
  }

  /* 窄屏塞不下两列，标签改成压在控件上方的一行 */
  .adv-panel {
    grid-template-columns: minmax(0, 1fr);
    align-items: flex-start;
    gap: 4px;
    padding: 12px 13px;
  }

  /* 单列布局下标签压在药丸上方，再靠右就和下面那排对不上了 */
  .adv-label {
    align-items: flex-start;
  }

  .adv-group.is-head > .adv-chips {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }

  .adv-seg {
    flex-wrap: wrap;
  }

  .adv-seg-btn {
    padding: 0 10px;
    font-size: 13px;
    font-weight: 700;
  }

  .adv-chip {
    height: 26px;
    padding: 0 10px;
    font-size: 12.5px;
    font-weight: 700;
  }

  .adv-foot {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    margin-top: 6px;
  }

  .adv-note {
    font-size: 11.5px;
  }
}

/* ===================== 通知公告 ===================== */
.an-count {
  margin: 0 0 14px;
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13px;
}

.an-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.an-item {
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-left: 3px solid rgba(187, 63, 60, 0.35);
  border-radius: 14px;
  background: rgba(255, 250, 244, 0.94);
  box-shadow: 0 8px 24px rgba(52, 18, 7, 0.08);
}

.an-item.is-important {
  border-left-color: var(--red);
  background: rgba(255, 248, 245, 0.97);
}

/* 归档的压淡一档，能翻到但不跟在架公告抢注意力 */
.an-item.is-archived {
  border-left-color: rgba(140, 110, 98, 0.3);
  opacity: 0.72;
}

.an-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.an-date {
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
  letter-spacing: 0.02em;
}

.an-flag {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--red);
  color: #fff6ee;
  font-size: 11px;
}

.an-flag-new {
  background: rgba(63, 125, 85, 0.9);
}

.an-flag-old {
  background: rgba(140, 110, 98, 0.55);
}

.an-title {
  margin: 0 0 8px;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 19px;
  line-height: 1.35;
  color: var(--ink);
}

.an-body p {
  margin: 0 0 8px;
  color: #5a453d;
  font-size: 14.5px;
  line-height: 1.75;
}

.an-body p:last-child {
  margin-bottom: 0;
}

.an-empty {
  padding: 50px 0;
  text-align: center;
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
}

@media (max-width: 640px) {
  .an-item { padding: 15px 16px; }
  .an-title { font-size: 17px; }
  .an-body p { font-size: 14px; }
}

/* ===== 新闻公告：左右并排 ===== */
.nn-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  align-items: start;
}

/* 栏目标题直接压在深色页面底上，不在托盘里——
   用 --ink 那种深色等于隐形，这里必须走浅色系（同 .page-header） */
.nn-heading {
  margin: 0 0 16px;
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 17px;
  font-weight: 600;
  color: #fff8ef;
  letter-spacing: 0.04em;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255, 236, 220, 0.28);
  text-shadow: 0 2px 10px rgba(46, 14, 5, 0.35);
}

@media (max-width: 800px) {
  .nn-split {
    grid-template-columns: 1fr;
    gap: 28px;
  }
}

/* 空栏提示同样在托盘外，跟着走浅色 */
.nt-empty {
  padding: 46px 0;
  text-align: center;
  color: rgba(255, 246, 238, 0.72);
  font-family: "SiteLatin", "Noto Serif SC", serif;
}

/* 往期公告折叠起来 */
.an-archive {
  margin-top: 4px;
}

.an-archive > summary {
  padding: 11px 14px;
  border: 1px dashed var(--line);
  border-radius: 12px;
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 13.5px;
  cursor: pointer;
  list-style: none;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.an-archive > summary::-webkit-details-marker { display: none; }

.an-archive > summary::before {
  content: "▸ ";
  display: inline-block;
  transition: transform 0.18s ease;
}

.an-archive[open] > summary::before { content: "▾ "; }

.an-archive > summary:hover {
  border-color: #cdb8ac;
  color: var(--red-deep);
}

.an-archive .an-item {
  margin-top: 12px;
}

/* 发布人：只在后台勾了「署名」时才渲染，跟日期并排放在抬头里。
   前面那个间隔点把「时间」和「谁发的」分开，又不至于断成两行。 */
.an-by {
  color: var(--muted);
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 12.5px;
}

.an-by::before {
  content: "·";
  margin-right: 8px;
  opacity: 0.5;
}

/* 最近浏览区域 —— 整条打破 main 容器约束，顶到屏幕左右边缘。
   圆角遮罩让首尾卡片在边缘被切时也是圆角，不会直愣愣断在那里。 */
/* 原来是 width:100vw + margin-left:calc(-50vw + 50%) 的整屏破格。
   两个问题：
   ① 100vw 不认侧边栏——侧边栏是 body 的 padding-left，破格后轨道从它底下钻出来；
   ② 轨道自己没有左右内边距，首卡直接贴死视口边缘，看着像被切了一刀。

   改成只破格到「内容区」边缘（main 的 24px 天沟），再由轨道的内边距把卡片推回来：
   卡片静止时和正文左对齐，滚动时还能往外多走 24px 才被裁掉——
   有余地，不是硬切。 */
.recent-strip {
  width: auto;
  margin-inline: -24px;
  margin-bottom: 28px;
  padding: 0;
  border-radius: 16px;
  overflow: hidden;
  /* 两端软收。静止时首卡在 24px 处（和正文左对齐），渐隐带只占 0–20px，
     所以静止状态卡片是完全实的；一旦横向滚动，卡片滑进那 20px 才开始淡出。
     不这么做的话，overflow:hidden 会在内容区边缘把卡片一刀切平——
     桌面端那一刀正好落在侧边栏旁边，看着像卡片钻到侧边栏底下被剪断了。 */
  -webkit-mask-image: linear-gradient(90deg,
    transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
  mask-image: linear-gradient(90deg,
    transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
}
.recent-strip-title {
  margin: 0 0 14px;
  padding: 0 24px;
  font-family: "SiteLatin", "Noto Serif SC", serif;
  font-size: 15px;
  font-weight: 600;
  color: rgba(255, 246, 238, 0.82);
  letter-spacing: 0.02em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
.recent-strip-title span { color: #e8a09a; margin-right: 6px; }
.recent-track {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-top: 5px;
  padding-bottom: 6px;
  /* 和上面的 -24px 外边距成对：外边距把轨道推到内容区边缘，
     内边距再把卡片推回正文的对齐线上。
     scroll-padding 让吸附也认这条线，否则滚完一格首卡会贴死在边上。 */
  padding-inline: 24px;
  scroll-padding-inline: 24px;
  -webkit-overflow-scrolling: touch;
}
.recent-track::-webkit-scrollbar { height: 4px; }
.recent-track::-webkit-scrollbar-thumb { background: var(--line-dark); border-radius: 2px; }
.recent-item {
  flex: none;
  width: 180px;
  scroll-snap-align: start;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--paper-flat);
  overflow: hidden;
  cursor: pointer;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.recent-item:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.recent-item img { width: 100%; aspect-ratio: 16/11; object-fit: cover; }
.recent-item-body { padding: 10px 12px 12px; }
.recent-item-title {
  /* 和作品卡、公告卡的主标题同一套字：朝华。之前漏了这行，
     它就掉回 body 的正文字体，一排卡片里只有这一处标题是另一副面孔 */
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.recent-item-meta { margin-top: 4px; font-size: 11.5px; color: var(--muted); }
.recent-clear {
  border: 0;
  background: none;
  color: inherit;
  opacity: 0.7;
  font-size: 12.5px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.recent-clear:hover { opacity: 1; color: var(--red); text-decoration-color: var(--red); }

/* ===================== 触屏：卡片图片不给长按取图 =====================
   只挂在 (hover:none) and (pointer:coarse) 里——这是"手指操作的设备"的准确
   写法，比按宽度切靠谱（桌面窗口拖窄不会误伤），也保证桌面端右键存图不受影响。

   三件事各管一头，缺一样都拦不干净：
   -webkit-touch-callout 关掉长按弹出的「存储到照片／拷贝」菜单（iOS 的主力）；
   user-select 关掉长按取词那圈放大镜和选区（Android 上主要靠它）；
   -webkit-user-drag 关掉把图拖出去这条路。
   再补一条 pointer-events:none：图片彻底不再是手势的落点，长按只会落到卡片
   本身。卡片的点击是 viewer.js 的全局委托按 closest('[data-open]') 找的，
   落点变成父级照样能开——所以这条不影响点开作品。 */
@media (hover: none) and (pointer: coarse) {
  .work-card .thumb img,
  .recent-item img,
  .viewer-stage img {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-user-drag: none;
    pointer-events: none;
  }
}

/* ===================== 触屏：取消 hover 动效 =====================
   触屏上没有"悬停"这回事。手指一点，:hover 就被判定成立，而且会一直挂着
   直到你点别处——于是卡片点完就浮在那儿不下来，返回列表还是浮的。
   这些位移本来是给鼠标划过用的反馈，在手指上只剩别扭。

   同样用 (hover:none) and (pointer:coarse) 圈定"手指设备"，桌面完全不受影响。
   只清位移，不动 :active——按下去缩一下那个是点按反馈，触屏上正需要它。
   带 !important 是因为其中几条原规则挂在 [data-theme="dark"] 底下，
   优先级比这里高，不加压不住。 */
@media (hover: none) and (pointer: coarse) {
  .work-card:hover,
  .work-card:hover .thumb img,
  .recent-item:hover,
  .work-row:hover,
  .wr-hit:hover .wr-thumb img,
  .wr-hit:hover .wr-arrow,
  a.news-item:hover .n-body,
  a.news-item:hover .n-arrow,
  .nav-links a:hover,
  a.contact-row:hover,
  .button:hover,
  .button.primary:hover,
  .text-button:hover,
  .filter-pill:hover,
  .filter-pill:hover::after,
  .share-button:hover,
  .icon-button:hover,
  .sort-toggle:hover,
  .ac-chip:hover,
  .tlx-bar:hover,
  .tlx-yr:hover .tlx-yr-bar {
    transform: none !important;
  }

  /* 这条的 transform 里还带着垂直居中用的位移，清成 none 会让它跑位，
     所以只去掉"动"的那 1px，居中那一半原样留着 */
  .filter-pill:hover::after { transform: translateY(-50%) !important; }
}

/* ===================== 标题不留孤字 =====================
   中文逐字都能断行，长标题很容易在末行只剩一个字，看着像排版事故。

   用 pretty 不用 balance——这两个不是一回事，用错了比不用还难看：
     pretty  只回头优化最后几行，把末行的孤字往回带一个，前面各行照常撑满。
     balance 追求各行等长，会把**所有行**一起截短。

   实测「潮涌大湾区，奋楫正当时」11 个字（卡片标题宽 253px）：
     默认      10 字 / 1 字   → 首行 93% 满，末行孤字
     pretty     9 字 / 2 字   → 首行 84% 满，孤字消除
     balance    6 字 / 5 字   → 两行只有 56% / 47%，右边空掉四成
   balance 还会在「潮涌大湾区，」的逗号后断行，把停顿留在行尾，读着别扭。
   曾经用过 balance，就是因为当时只验了「末行是不是只剩一个字」这一个指标——
   两者都能过，但整体观感差得远。

   只给标题用、不给正文用：正文段落本来不在乎末行长短，
   而 pretty 要回溯试算，行数多了并不划算。
   不支持的浏览器直接忽略这条，回退成普通换行，不会比现在更差。 */
.card-title,
.work-card .card-subtitle,
.an-title,
.recent-item-title,
.viewer-main-title,
.viewer-sub-title,
.rd-title b,
.rd-title span {
  text-wrap: pretty;
}

/* ===================== 语言切换按钮 =====================
   展开时和主题按钮并排、整组右对齐（沿用 .sb-theme-row 原有的
   justify-content:flex-end，这里只补一个间距）。语言在左、主题在右，
   顺序由 sidebar.js 里的插入顺序决定。
   收起时那一列只有 44px 宽，两颗并排会互相挤扁——直接把语言按钮收起来，
   只留主题，和收起前的样子一致。 */
.sb-theme-row {
  gap: 6px;
}

.sidebar.is-collapsed .sb-lang-btn {
  display: none;
}

/* 规格逐项对齐 .sb-theme-btn：40×40、同样的圆角、同一条 hover 曲线。
   两颗并排站着，差 2px 都看得出来。 */
.sb-lang-btn {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 11px;
  background: transparent;
  color: rgba(255, 246, 238, 0.86);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}

.sb-lang-btn:hover {
  background: rgba(255, 248, 240, 0.12);
  color: #fff9f3;
}

/* 角标用等宽数字对齐，"EN" 和 "中" 宽度差很大，居中才不会一跳一跳 */
.sb-lang-tag,
.mt-lang-tag {
  font-family: "SiteLatin", "ZhaohuaTitleB", "Noto Serif SC", serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
}

/* 手机顶栏那颗：沿用 .mt-btn 的 36×36 规格，这里只管里面的字 */
.mt-lang-btn .mt-lang-tag {
  font-size: 13.5px;
  color: #fff8ef;
}

/* 书名号整块不拆行。由 script.js / viewer.js 的 nbTitle() 包上这个类。
   中文没有词边界，浏览器只能逐字断，《人民日报》会被断成「《人民日/报》」；
   《…》是少数有明确边界的结构，值得单独保护。
   普通词（「中国」这类）保护不了——那需要分词词库，不值得为排版引进来。 */
.nb {
  white-space: nowrap;
}
