/*
Theme Name: 一搜主题
Theme URI: https://www.aisearchhub.com/
Author: AI Search Hub
Description: 网盘搜索、BT 资源搜索站的主题。包含首页、博客、落地页三大版式，内置 SEO 结构化数据与广告位挂载点。自带设计令牌层，所有组件通过语义令牌取色，版式只覆写令牌值。
Version: 0.27.0
Requires at least: 6.0
Tested up to: 6.7
Requires PHP: 7.4
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: ai-search-hub
Tags: search, blog, custom-colors, translation-ready
*/

/* ==========================================================================
   0. 设计令牌（语义层）
   --------------------------------------------------------------------------
   规则：令牌「名」全站统一，令牌「值」由版式在自己的作用域里覆写。
        组件只准用语义名（--ink / --accent / --line），不准写死颜色。
        这样同一个 .aib-btn 在首页是红橙、在博客版式自动变橙，不用写两遍。
   ========================================================================== */

:root {
  /* —— 字体 —— */
  /* 站点规则：正文一律微软雅黑打头，不要用别的 */
  --font: "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont,
          "Segoe UI", "PingFang SC", "Hiragino Sans GB", sans-serif;
  /* 等宽只给真正的代码用。末位挂雅黑，避免代码块里的中文注释掉到默认字体 */
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono",
          "Microsoft YaHei", "微软雅黑", monospace;

  /* —— 面 —— */
  --bg: #ffffff;          /* 页面底色 */
  --surface: #ffffff;     /* 内容面（卡片、面板） */
  --surface-2: #f5f7f8;   /* 次级面（表头、代码底、凹槽） */

  /* —— 墨（文字）——
     三个层级都要在**白底上过 WCAG AA（4.5:1）**。这是硬指标不是偏好：
     --ink-3 原来取 #8b95a3，白底上只有 3.03:1，站头副标题、时间戳、
     面包屑、页脚全是它，等于全站次要信息都读不清。
     现在是 #696f7a：白面 5.05:1，--surface-2（#f5f7f8）4.70:1。

     上一版是 #6b7280，在白面上 4.83 看着没问题，压 --surface-2 却是
     **4.499:1——卡在门槛下 0.001**。这种值最麻烦：两位小数显示出来是
     "4.50"，看着是过的，实际上没有；而且它悬在一根头发丝上，
     以后动任何一个相邻的浅面色，都可能把它推过线或推得更远，
     没人会发现。次要信息本来就遍布各种浅面（徽标底、表头、代码块），
     这里留出余量，别让全站次要文字吊在门槛上。

     代价是它和 --ink-2 的差距被压小了（原来两级差 1.87 倍，现在 1.17 倍）。
     这是没法两全的：任何能过 AA 的 --ink-3 都必然更靠近 --ink-2。
     好在视觉层次主要靠字号和字重撑，颜色只是辅助。

     ⚠ 它压不住**浅灰页面底**（博客版式的 #e9ecee 上只有 4.21:1），
       站头副标题、面包屑、页脚这些直接压在页面底上的次要文字
       正好就在那一层。那一路不看这个值：.layout-blog 和
       body.aish-scheme-mist 各自把 --ink-3 调深了一档，见那两处。 */
  --ink: #1b2634;         /* 标题、正文主色 */
  --ink-2: #5c6878;       /* 次要文字、摘要 */
  --ink-3: #696f7a;       /* 元信息、时间戳、标签 */

  /* —— 线 —— */
  --line: #e4e8ee;        /* 常规分隔线、卡片描边 */
  --line-2: #d3dadf;      /* 加重分隔线、hover 描边 */

  /* —— 强调 ——
     三件套，再加下面单独说的第四件 --accent-btn。 */
  --accent: #de3f27;
  --accent-dk: #a02a19;
  --accent-soft: #fdeeeb;

  /* —— 强调色「实底/当文字」专用的一档 ——

     背景：品牌朱红 #de3f27 有两个位置够不上 AA ——

       白字压在它上面（搜索按钮、当前页码、主按钮）   4.33:1  ✗
       它压在白面/灰底上当**文字**（a:hover、当前导航项）  4.33 / 3.65  ✗

     门槛是 4.5。而它作为**描边、浅底、装饰条**时完全不涉及文字，
     4.33 没有任何问题——那正是它好看的地方，不该跟着一起变深。

     一个令牌被派了两个相互矛盾的差事，就是这个结果：
     当底要足够深，当描边要足够亮。同一句 var(--accent) 不可能两头都对。

     所以拆开：--accent 继续当品牌色（描边、浅底、装饰、插图），
     凡是**要压字、或者自己就是字**的地方一律改用 --accent-btn。

     为什么是 #bf3220 而不是别处：它原来是 --accent-dk 的值，
     白字压它 5.69:1、它压白面 5.04:1，两个位置都过。
     零新颜色——只是把原本用于 hover 的这档提升成常规档。

     那 --accent-dk 就顺次下移一档到 #a02a19（白字压它 7.39:1）。
     hover 必须比常规更深，否则鼠标划过去没有任何反馈。

     三档的深浅关系：--accent(亮) > --accent-btn(中) > --accent-dk(深)。 */
  --accent-btn: #bf3220;

  /* 压在 --accent **实底**上的字色（按钮、当前页码、选中标签）。
     单独给一个令牌，是因为它必须跟着底一起翻面。

     浅色方案里 --accent 是深色，所以这个值是白；但深色方案
     （body.aish-scheme-ink）里 --accent 换成了一组**浅**色——
     不换的话深蓝压深灰底直接看不见。这时候白字压上去只剩
     1.8～2.6:1，按钮上的字基本读不出来。

     所以「白字压强调色」这句话里的"白"不能写死在组件里，
     必须是一个可以被方案覆盖的令牌。全站的实底按钮都取这个值。 */
  --on-accent: #ffffff;

  /* —— 辅助色（用于分类标识、状态）—— */
  --teal: #0e9488;
  --teal-soft: #e6f4f2;
  --steel: #3a5baf;
  --steel-soft: #eaeffa;

  /* —— 入口卡色相 ——
     首页那三张搜索入口卡可以各挑一个色相，见 AISH_Customize::tones()
     和 home.css 的 .home-entry--tone-*。

     为什么是**一组具名色相**而不是让人填色值：
     这套主题的底色有五档（默认 / 纯白 / 浅灰 / 暖白 / 深色），
     任何一个写死的色值到了某一档上都会翻车，而且没人会去核。
     这里每一档都量过对比度，三条硬指标在 check-contrast.php 的
     $mustPass 表里，不合格直接 FAIL：
       tone-X / tone-X-soft  >= 3.0   图标字形压自家浅底
       tone-X / surface      >= 3.0   卡片顶上那道 3px 描边压卡片面
       ink-2  / tone-X-soft  >= 4.5   11px 小标签的字压那块浅底
     第三条最紧：浅底稍微深一点它就掉下去，所以下面这些 -soft
     都比"看着舒服"再浅一档。

     浅色只有**这一套**：五档底色里四档浅底的 --surface 都是 #ffffff，
     --surface-2 也都浅到同一量级，所以一套值在四档上算出来是同一个数。
     深色那档另有一套，在 schemes.css 的 body.aish-scheme-ink 里——
     和 --accent-soft 是同一个分工，不是漏了。

     下面这张数字**是 check-contrast.php 第 5 节打出来的**，不是手算的。
     它每次还会重算一遍——对不上就 FAIL，所以这张表不会悄悄过期。

       --tone-* 八档（浅色四档通用；「页面底」那列取最暗的博客版式 #e9ecee）

         色相       图标/浅底  描边/卡面  描边/页面底  小字/浅底
         蓝            4.34       4.95        4.17       4.97
         青            4.67       5.36        4.52       4.94
         靛            5.73       6.83        5.76       4.75
         紫            5.50       6.40        5.39       4.87
         琥珀          4.12       4.60        3.87       5.08
         玫红          4.32       4.98        4.20       4.91
         绿            4.69       5.33        4.49       4.99
         石板          5.36       6.07        5.12       5.00

      前三列按 3.0 判（图形），最后一列按 4.5 判（它压的是 11px 的字）。
      全表最低的一格就是最后一列的靛 4.75——挑 -soft 的值时盯的就是它。

     深色那一套余量大得多（最低的一格是靛 图标/浅底 6.57，
     离 3.0 的门槛还有一倍），不逐个记；明细同样在第 5 节。 */
  --tone-blue:   #1f6fd0;  --tone-blue-soft:   #e8f1fd;
  --tone-cyan:   #0d7490;  --tone-cyan-soft:   #e3f2f7;
  --tone-indigo: #5145cc;  --tone-indigo-soft: #ece9fb;
  --tone-violet: #8a35b8;  --tone-violet-soft: #f5eafb;
  --tone-amber:  #a8660a;  --tone-amber-soft:  #fdf1de;
  --tone-rose:   #cf2f5c;  --tone-rose-soft:   #fdeaf0;
  --tone-green:  #1a7a50;  --tone-green-soft:  #e5f4ec;
  --tone-slate:  #4e6480;  --tone-slate-soft:  #eef1f5;

  /* —— 形 —— */
  --radius: 8px;
  --radius-sm: 4px;
  --radius-pill: 999px;

  /* —— 间距刻度（组件用：按钮内边距、卡片间距这类"块与块之间"）—— */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px; --sp-5: 20px;
  --sp-6: 24px; --sp-7: 32px; --sp-8: 40px; --sp-9: 56px; --sp-10: 72px;

  /* —— 行距刻度（正文用：标题上下、段落之间这类"阅读节奏"）——
     比 --sp-* 松一档，因为正文的呼吸感要求和界面组件不是一回事：
     组件挤一点显得紧凑，正文挤一点就直接读不下去。

     放在 :root 而不是放在 .layout-read 里，是**必须**的：
     文章页（layout-read）和"页面"（layout-page，比如投稿页、关于我们）
     加载同一份 single.css，而后者身上没有 .layout-read。
     刻度写在 .layout-read 里的话，页面上的 var(--gap-3) 全部求值失败，
     对应的声明会被整条丢掉——不报错，只是间距静默地变成 0。 */
  --gap-1: 8px;  --gap-2: 14px; --gap-3: 22px; --gap-4: 34px;
  --gap-5: 56px; --gap-6: 88px; --gap-7: 128px;

  /* —— 容器 —— */
  --shell: 1200px;        /* 首页 / 落地页内容宽 */
  --shell-wide: 1240px;   /* 博客列表（带侧栏）宽 */
  --side: 320px;          /* 侧栏宽 */

  /* —— 阅读宽 ——
     这一版正文按 16px 排，720px 一行约 45 个汉字。

     45 是什么概念：中文正文的传统舒适区是 25～40 字/行，但那个是
     **印刷**场景——纸上没有滚动条，眼睛的落点是固定的，行太长会串行。
     屏幕上要宽一档，因为读者是靠滚动找位置的，行短了反而要不停地滚。
     国内内容站基本都落在 40～50 字/行（正文栏 640～800px），45 是中间值。

     这个数字**只给 .read-main 用**（max-width），不是"正文栏加内边距"
     凑出来的：.read-prose 自己没有左右内边距，720 就是文字实打实的宽度。
     想让它变窄，改的是这一个令牌，不要去给 .read-prose 加 padding——
     加了之后 h2 左边那条竖线和配图都会跟着往里缩，而它们本该是齐的。

     和 --gap-* 同一个理由放在 :root：文章页（layout-read）和普通页面
     （layout-page）共用 single.css，而页面身上没有 .layout-read。
     放在版式里的话，页面上的 var(--read-measure) 会求值失败，
     连带着用了它的那条声明一起被丢掉。 */
  --read-measure: 720px;

  /* —— 投影 ——

     两档，用在两个不同的场合：

       --shadow-1  常驻的极浅一层。搜索结果的卡片用它，好让白卡片
                   从浅灰页面底上"浮"起来，而不是只靠那根 1px 边框
                   和底色分开。
       --shadow-2  悬停／浮层。列表条目被指到时、下拉菜单、模态框。

     仍然克制：全站没有第三档，也没有把投影当装饰用（大标题后面、
     区块之间那类）。搜索页那批卡片是**唯一**常驻投影的地方，
     因为只有它是一整屏白卡片铺在灰底上。 */
  --shadow-1: 0 1px 2px rgba(27, 38, 52, .04), 0 4px 12px -6px rgba(27, 38, 52, .10);
  --shadow-2: 0 2px 4px rgba(27, 38, 52, .05), 0 14px 32px -12px rgba(27, 38, 52, .16);

  /* —— 遮罩 ——

     浮层背后压的那一层。目前只有一处用它：搜索结果页上点「正版入口」
     那几条链接时弹出的确认框（见本文件第 11 节）。

     ⚠ 它和 --shadow-* 是同一类东西：**半透明层必须在深色方案里翻过来**。
       这里取的是"深墨色压一层"，因为它默认落在白/浅灰底上；
       深色方案下页面本身就是深的，再压一层深色等于什么都没压——
       浮层和背景糊在一起，看不出来哪个在上面。
       body.aish-scheme-ink 里把它翻成了浅色压一层（见
       assets/css/schemes.css「遮罩」那一段）。改这个值之前先看那边。

     0.45 是按"底下的内容看得见、但明显退到后面"调的：再浅一点
     遮不住底下的按钮，再深一点就变成一块实心色，用户会以为页面卡了。 */
  --scrim: rgba(27, 38, 52, .45);
}

/* --------------------------------------------------------------------------
   版式覆写：博客版式（blog-03）
   底色是浅灰不是纯白、强调色是橙、容器略宽——这是用户明确认可的设计，
   所以只覆写值，不动任何组件代码。
   -------------------------------------------------------------------------- */
.layout-blog {
  --bg: #e9ecee;
  --surface: #ffffff;
  --surface-2: #f2f5f6;

  --ink: #13212c;
  --ink-2: #41525f;
  /* 和 :root 不同，这一版必须给自己挑一个值，不能继承。
     原因只有一个：这一版的**页面底是浅灰 #e9ecee**，而 :root 那个
     --ink-3 是按白面挑的。

     上一版这里写的是 #647280，附了一句「--ink-3 压页面底只有 4.26:1，
     所以要写在这一层上的次要文字一律用 --ink-2，或者让它落在白面里」——
     这条约定**守不住**：站头副标题（.aib-brand-sub）、面包屑（.aib-crumb）、
     页脚导航（.aib-nav--footer）、侧栏分类计数（.aib-cat-count）全都在
     页面底上，而且都由 header.php / footer.php / 侧栏渲染，
     不属于 blog-03 的模板，改版式模板管不到它们。
     一条靠"所有人都记得"维持的规则，迟早会漏。

     所以直接把这个值调到能在自家页面底上站住：4.65:1（白面 5.51:1）。
     这样上面那些组件不用改一行，自动就是对的。 */
  --ink-3: #5e6a78;

  --line: #e2e6e8;
  --line-2: #d3dadf;

  /* 强调色四件套。
     这里要专门说一句 --accent 为什么是 #c94b06 而不是更跳的 #e8590c：
     强调色有两种用法，对明度的要求是**相反**的。

       当描边、当浅底      —— 越亮越好，和背景分得开就行（按 3:1 判）；
       当实底、上面压白字  —— 越深越安全（按 4.5 判）。

     #e8590c 在白底上当文字、或者反过来白字压它，都只有 3.58:1。
     而它承载的恰恰是**搜索按钮**和**当前页码**这两个最常见的东西，
     还有悬停时的链接色。换成 #c94b06 之后是 4.68:1，过 AA。

     —— 但 #c94b06 只解决了"白底"那半边 ——
     这一版用的是浅灰页面底 #e9ecee，还有自家的浅强调底 #fdeee4。
     #c94b06 压在它们上面是 3.94 和 4.12，都不够。
     （这和 schemes.css 里橙那一档是同一笔账、同一组数字：
       那个文件里的颜色是从这里抄过去的，两处必须一起动。）

     所以四件套按"深浅"分工，而不是按"亮不亮"：
       --accent      #c94b06  描边、浅底、入口卡图标 —— 只要 3:1
       --accent-btn  #a83c04  实底压白字 / 自己当链接文字 —— 要 4.5
       --accent-dk   #8f3303  hover，必须比 --accent-btn 更深
       --accent-soft #fdeee4  浅底，不参与前景判定

     --accent-btn 必须显式写出来：它定义在 :root，这里是
     .layout-blog 自己的作用域，不写就继承到朱红那一档的 #bf3220 ——
     一个橙色版式的按钮长成砖红色，而且不会有任何报错。
     check-contrast.php 会逐块核对这件事。 */
  --accent: #c94b06;
  --accent-btn: #a83c04;
  --accent-dk: #8f3303;
  --accent-soft: #fdeee4;

  --radius: 8px;
  --radius-sm: 6px;

  --shell: 1240px;
}

/* --------------------------------------------------------------------------
   搜索页：在 blog 版式之上再覆写一层（只改页面底色）
   --------------------------------------------------------------------------
   搜索结果页走的是 blog 版式（见 inc/setup.php 的 aish_layout()），
   但它和博客列表页长得不该一样，差别就在这一条。

   博客列表页一篇一篇都是"文章卡"，卡片之间有大量文字、图片、留白，
   #e9ecee 那层浅灰底是给它们当背景的，没问题。
   搜索结果页是**一整屏白卡片**：一排三个、几十条铺下去。白卡片压在
   #e9ecee 上，两者明度差得很少，卡片只能靠那根 1px 边框和页面分开，
   滚起来整页是一片糊的。

   提亮到 #eef0f2 之后差值拉开，卡片靠底色本身就能"浮"起来，
   边框也回到它该有的轻量。粉笔灰而不是纯白，是**故意**的：
   纯白底 + 白卡片等于没有底，卡片会重新糊回去。

   ⚠ 只覆写 --bg 一个值。强调色四件套、墨色三档、描边两档全部沿用
     .layout-blog ——那套值逐格验过对比度（check-contrast.php 第 1/4 节），
     换主色会连着首页、博客、落地页一起歪，而那三页用户没有提过意见。

   ⚠ --ink-3（#5e6a78）是按 #e9ecee 挑的，这里底色**变浅**了，
     它压上去的对比度只会更高，不需要跟着调。反过来（把底调深）
     就一定要重挑——check-contrast.php 会拦。 */
body.aish-search {
  --bg: #eef0f2;
}

/* --------------------------------------------------------------------------
   版式覆写：文章详情版式（blog-02-detail）
   阅读型版式：纯白纸、深墨蓝强调、窄栏。
   -------------------------------------------------------------------------- */
.layout-read {
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-2: #f7f7f5;

  --ink: #101113;
  --ink-2: #24272c;
  /* 这一版是暖中性（--line 是 #e8e8e6，不带蓝），所以 --ink-3 也取暖灰，
     不跟着 :root 用那个偏蓝的墨——一页里两种灰混着用看得出来。

     上一版是 #6f7278，注释里写的是"白底 4.82:1"，那个数没错，
     但**量错了底**：这一版的 --surface-2 是 #f7f7f5，不是纯白，
     而元信息（表头、代码块、图注压在上面的那种块）恰恰就在那一层，
     #6f7278 压上去是 4.496:1——又差 0.004 不过线。

     #6b6e74：页面底 5.11:1，--surface-2 4.77:1，暖灰的调子不变。 */
  --ink-3: #6b6e74;

  --line: #e8e8e6;
  --line-2: #d5d5d2;

  --accent: #1e3a5c;
  --accent-dk: #16293f;
  --accent-soft: #eef2f7;
  /* 同 .layout-blog：墨蓝本来就过线，但必须显式写出，
     否则继承到 :root 那一档的朱红 #bf3220。 */
  --accent-btn: #1e3a5c;

  --radius: 4px;
  --radius-sm: 3px;

  /* —— 容器 ——
     这一版是两栏：正文 + 右侧广告栏（.aib-layout 的网格，见第 2 节）。
     --shell 是**两栏加起来、再加上容器自己的内边距**的宽度，不是正文宽。

     写成 calc() 而不是写死数字，是因为先定的其实是正文宽（--read-measure，
     一行约 45 个汉字，见 :root），容器是被它推出来的。
     写死数字的话，哪天正文宽调了，容器不会跟——正文栏会凭空宽出或窄掉一截，
     而且两张值分在两个文件里，很难想到它们是一对。

     四项，少一项正文就窄一截：

       正文 720 + 侧栏 320 + 列间距 40 + 容器内边距 24×2 = 1128

     最后那 48px 是最容易漏的一项，而且漏了之后从代码上看不出来：
     全局是 border-box，.aib-shell 的 max-width 把 padding **包含在内**，
     只算前两项的话内容盒剩 1080-48=1032，1fr 拿到 672——
     正文悄悄窄了 48px，一行从 45 个字变成 42 个，谁也不会觉得哪里不对。
     （上面那句"45 变 42"是漏算之后的结果，不是笔误——
      这一项漏过，所以留着当记号。）

     列间距和内边距这里引用的是 --sp-8 / --sp-6，和 .aib-layout 的 gap、
     .aib-shell 的 padding 用的是同一个令牌，不是抄来的数字。

     下面 ≤1200px 那条断点会把 --side 调到 280、gap 调到 --sp-6，
     **故意不改这里**：那两处变小只会让 1fr 变宽松（1128 宽时 1fr 是 776），
     而 .read-main 有 max-width: 720 加 margin-inline: auto 兜着，
     正文被居中放回 720，不会跟着变宽也不会被挤窄。
     所以这一版不需要跟着设断点——设了反而多一处要对齐的东西。 */
  --shell: calc(var(--read-measure) + var(--side) + var(--sp-8) + var(--sp-6) * 2);
}

/* ==========================================================================
   1. 基础重置
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent-btn); }

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }

img, svg, video { max-width: 100%; height: auto; display: block; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; }

/* 焦点可见性：键盘用户必须看得见焦点环，但鼠标用户不该被它打扰 */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection { background: var(--accent-soft); color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  html { scroll-behavior: auto; }
}

/* ==========================================================================
   2. 布局骨架
   ========================================================================== */

.aib-shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--sp-6);
}

/* <main> 本身不设宽度、不设内边距——每一段（区块）自己决定要不要套 .aib-shell。
   写死在这里的话，首页那种「整块通栏 + 内部居中的窄容器」的区块
   就得反过来去抵消它。它唯一的职责是给跳转链接一个落点。 */
.aib-main { display: block; }
/* 跳转链接跳过来时给个焦点落点，否则键盘用户点完「跳到主要内容」
   焦点还在原地，下一次 Tab 又回到导航。 */
.aib-main:focus { outline: none; }

/* 带右侧栏的两列布局（博客列表页 / 落地页） */
.aib-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--side);
  gap: var(--sp-8);
  align-items: start;
}

/* 主列必须能收缩，否则内嵌的表格会把整个 grid 撑破 */
.aib-layout > * { min-width: 0; }

/* 关掉右侧栏的版式：只剩一条轨道，宽度全归主列。
   两处挂这个类：
     · search.php  —— 「搜索页右侧栏」选「不显示」（主列 .blog-main 本身不定宽，
                      所以去掉侧栏之后结果区是真的变宽）；
     · single.php  —— 「文章页右侧栏」选「不显示」（主列 .read-main 仍被
                      --read-measure 封在 720px 并居中，所以那边变的是
                      "有没有那一栏"，不是正文宽窄，见 settings.php 的 hint）。

   为什么是改轨道数而不是"把 aside 藏起来"：藏起来的话第二条轨道还在
   （只是空的），主列仍是 minmax(0, 1fr) 那一份，宽度一点没变——
   两种版式看起来一模一样，站长会以为开关坏了。

   和 .aib-layout 同为 (0,1,0)，靠**源序**赢：它在下面。
   （下面 900px 那条媒体查询里也有 .aib-layout，那一条本来就是一列，
     对 --full 无影响，所以没有跟着写一份。） */
.aib-layout--full { grid-template-columns: minmax(0, 1fr); }

/* 侧栏是个纵向堆叠的槽，里面一块一块（广告位、分类…）。
   gap 在这里而不是各个块自己写 margin：块的上下间距必须一致，
   否则"广告位和分类之间 20px、分类和自己内部 16px"这种不齐会很明显。 */
.aib-side {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* 侧栏**不做 sticky**。
   看着很诱人（长列表上广告一直可见），但侧栏内容高度不可控——
   广告插件往里塞 3 张竖版海报就超过 700px，一旦高于视口，
   sticky 会把底部卡在屏幕外面，用户怎么滚都看不到，
   而"看不到的那个块"恰好可能是真正赚钱的那个。
   加 max-height + overflow 能绕开，代价是侧栏变成一个嵌套滚动区，
   鼠标停在侧栏上时滚轮滚的是侧栏不是页面——那个更烦人。
   广告位放侧栏第一位（首屏内）已经满足了"首屏可见"这条硬要求，
   sticky 带来的额外曝光不值这个复杂度。 */

@media (max-width: 1200px) {
  .aib-layout { --side: 280px; gap: var(--sp-6); }
}

@media (max-width: 900px) {
  .aib-layout { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 768px) {
  .aib-shell { padding: 0 var(--sp-3); }
}

/* ==========================================================================
   3. 共用组件
   —— 只准用语义令牌取色，版式切换时自动跟随
   ========================================================================== */

/* --- 3.1 按钮 -----------------------------------------------------------
   四种视觉层级。type 只管埋点归类，视觉层级一律由 style 决定。
   ---------------------------------------------------------------------- */
.aib-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: 42px;
  padding: 0 var(--sp-5);
  border-radius: var(--radius);
  border: 1px solid transparent;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: .02em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color .16s ease, border-color .16s ease,
              color .16s ease, box-shadow .16s ease, transform .16s ease;
}

.aib-btn--primary {
  background: var(--accent-btn);
  color: var(--on-accent);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}
.aib-btn--primary:hover {
  background: var(--accent-dk);
  color: var(--on-accent);
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}

.aib-btn--secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line-2);
}
.aib-btn--secondary:hover {
  border-color: var(--accent-btn);
  color: var(--accent-btn);
}

.aib-btn--ghost {
  background: transparent;
  color: var(--ink-2);
  border-color: var(--line);
}
.aib-btn--ghost:hover {
  background: var(--surface-2);
  color: var(--ink);
  border-color: var(--line-2);
}

/* 纯文字按钮：用于页脚这类不需要视觉重量的位置 */
.aib-btn--link {
  height: auto;
  padding: 0;
  background: none;
  color: var(--ink-2);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line-2);
}
.aib-btn--link:hover { color: var(--accent-btn); text-decoration-color: currentColor; }

/* 小一号。搜索结果每一条上那两个按钮（打开 / 复制链接）用它。
   42px 高的按钮压在一条结果里，会让每一条都变成一块操作面板，
   而这两个动作是次要的——正文才是。
   取色沿用已有的 --secondary / --ghost，没有引入新的前景/背景组合，
   所以 check-contrast 覆盖的那 90 种状态依然盖得住（见那个脚本第 0b 节）。 */
.aib-btn--sm {
  height: 32px;
  padding: 0 var(--sp-3);
  font-size: 13px;
  font-weight: 500;
}

/* 按钮组：多个按钮并排，窄屏自动换行 */
.aib-btns {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
}
/* 按钮组前面刚跟了一段正文时用它拉开距离。
   不写 .aib-btns { margin-top } 是因为大多数按钮组是紧接标题的，
   统一加上会让那些地方多出一截说不清来由的空白。 */
.aib-btns--spaced { margin-top: var(--sp-6); }

/* --- 3.2 卡片 ----------------------------------------------------------- */
.aib-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* --- 3.2b 素列表 --------------------------------------------------------
   最朴素的「标题 + 时间 + 摘要」。兜底模板用它，搜索结果页也会用。

   刻意不做缩略图、不做网格、不做悬停浮起——那些是版式的活。
   这一套要保证的是「不管站里出了什么状况，内容都还读得到」：
   落地页插件被停用、版式样式没加载、模板掉进兜底……
   这些情况下页面都不该是一堆没有排版的裸文本。
   ---------------------------------------------------------------------- */
.aib-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.aib-list-item { padding: var(--sp-5); }

/* 搜索结果那一条（卡片 + 条目两个类同时挂）。
   ─────────────────────────────────────────────────────────────────
   为什么必须是 flex 纵向，而不是随便什么块
   ─────────────────────────────────────────────────────────────────
   上面 .aib-grid 把同一行的卡片拉成了等高，但**卡片里面**还是各排各的：
   标题两行的卡片，按钮紧跟标题；标题四行的，按钮被顶到更下面。
   整排看过去卡片一样高了，按钮却还在四个不同的高度上——不齐只是从
   卡片下沿挪到了卡片里面。

   flex 纵向 + 下面 .aib-list-actions 的 margin-top: auto，
   才能把按钮那一行推到卡片底部，让整排按钮落在同一条基线上。

   overflow: hidden 是给标题兜底的：截断（.aib-list-title 的
   -webkit-line-clamp）已经拦住了绝大多数情况，但 line-clamp 是
   WebKit 系的私有实现，别的排版引擎上不生效。真到那时候，
   溢出的一行会被裁掉而不是画到卡片外面去——宁可少一行，
   也不要那种"字压在下一页内容上"的样子。 */
.aib-card.aib-list-item {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-1);
  transition: border-color .16s ease, box-shadow .16s ease, transform .16s ease;
}
/* 悬停：描边转强调色 + 轻轻抬起。
   只给搜索结果的条目，不给全部 .aib-card——博客列表那种一条压一条的
   长列表里，鼠标每划过一条整行就动一下，滚轮滚过去满屏都在抖。 */
.aib-card.aib-list-item:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-2);
  transform: translateY(-2px);
}

/* ─────────────────────────────────────────────────────────────────
   标题：**最多两行**。这是需求里点名的那件事
   ─────────────────────────────────────────────────────────────────
   两个毛病是两个独立的原因，要分别治：

   A. 标题跑出卡片
      两个原因叠在一起：没有截断，以及没有地方断行。
      资源名里经常有「【1080P】」「[全集打包]」、magnet 哈希这种
      长串没有空格的字符，浏览器默认只在空格处断行，
      那串东西就把卡片横向撑出去了 → overflow-wrap: anywhere。

   B. 卡片高度参差（配合上面 .aib-grid 的 stretch）
      标题短的卡片如果只按内容撑，整排还是不平 → min-height 兜住两行。

   两行这个数是定死的，不是"可以调的另一种排版"：
   三行就是这次要修的那个高度差的形状。

   17px × 1.45 × 2 就是这里 min-height 的来源，和 font-size /
   line-height 是同一组数——改字号要连着改它。 */
.aib-list-title {
  font-size: 17px;
  font-weight: 700;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  min-height: calc(17px * 1.45 * 2);
}
.aib-list-title a { color: var(--ink); }
/* 双类名：单类的 .aib-list-title a 是 (0,1,1)，和全局 a:hover 同权重，
   谁后加载谁赢——那种"改了一个文件另一个地方就变色"的 bug 来源。 */
.aib-list-title a:hover { color: var(--accent-btn); }

.aib-list-meta {
  margin-top: var(--sp-2);
  font-size: 13px;
  color: var(--ink-3);
}
.aib-list-meta > * + * { margin-left: var(--sp-3); }

.aib-list-excerpt {
  margin-top: var(--sp-3);
  font-size: 14.5px;
  line-height: 1.75;
  color: var(--ink-2);
}
.aib-list-excerpt > * + * { margin-top: var(--sp-2); }
/* 摘要里不该出现超大标题：the_excerpt 一般不带上标题，
   但插件在摘要里塞 <h2> 的情况真实存在过。 */
.aib-list-excerpt h1,
.aib-list-excerpt h2,
.aib-list-excerpt h3 { font-size: 15px; font-weight: 600; }

/* 条目底部那两个动作按钮（打开 / 复制链接）。
   只在家里有按钮时才渲染这一块（没有地址的条目整块不出），
   所以这里不需要 :empty 之类的兜底。

   margin-top: auto 是「整排卡片按钮落在同一条基线上」的后半截
   （前半截是 .aib-card.aib-list-item 的 flex 纵向）。它把这一行
   推到卡片底部——注意推到的是**父容器底部**，所以父级必须是
   flex 容器、且这一块是最后一个子元素。换成块级父容器的话
   auto 会算成 0，这一整条就悄悄失效了。

   padding-top 而不是 margin-top 来留这段距离：分隔线要贴着
   按钮那一行的上沿，margin 的话线会跟着按钮一起飘。
   线平时是透明的（transparent 而不是不写），悬停才显色——
   写成"平时没有边框、悬停才加"的话，卡片内容会在鼠标划过时
   整体下移 1px，一排卡片跟着抖。 */
.aib-list-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: auto;
  padding-top: var(--sp-4);
  border-top: 1px solid transparent;
}
.aib-card.aib-list-item:hover .aib-list-actions { border-top-color: var(--line); }

/* --- 3.2c 网格 ----------------------------------------------------------
   同一个列表的另一副排布：一排 N 个。选它还是不选由
   「搜索结果 → 结果排布」决定，模板二选一地挂 aib-list / aib-grid。

   默认（没有 --N 修饰类）是 2 列。写一个默认值而不是让裸 .aib-grid
   回落到单列，是因为修饰类万一没拼上（模板改了、值非法），
   2 列仍然是个能看的版面；单列会让"卡片"这个选择看起来毫无效果。

   minmax(0, 1fr) 里的那个 0 是必须的：只写 1fr 时，网格项的最小宽度
   是内容的**最大不可断行宽度**，而搜索结果里恰好有那种东西——
   一长串没有空格的资源名、magnet 哈希。它们会把所在那一列撑出去，
   表现是卡片溢出容器、页面横向滚动条出现，而"是哪一列撑的"
   在浏览器里要逐个排查才看得出来。

   ─────────────────────────────────────────────────────────────────
   **这里不能写 align-items: start**（原来就是它）
   ─────────────────────────────────────────────────────────────────
   网格项默认是 stretch：同一行里每张卡片拉到那一行最高的那张的高度，
   于是整排的底边齐平。写了 start 之后每一项只取自身内容高度，
   标题两行的卡片就比三行的矮一截——一排卡片下沿参差不齐。

   这不是"两种都行"的排版口味问题。结果列表里标题长度本来就不可控
   （资源名多长完全看别人怎么命名），所以这个差异**每一页都会出现**，
   而且是页面主体上最显眼的一处不齐。

   等高只解决了一半：卡片内部还得把"打开 / 复制链接"那一行推到卡片底
   （.aib-list-actions 的 margin-top: auto），否则卡片一样高、
   按钮却悬在各自标题下面不同的高度上，看着还是乱的。
   ---------------------------------------------------------------------- */
.aib-grid {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.aib-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.aib-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* 窄屏降级。四列在 900px 上每列只剩 200px，标题会被折成单字一行——
   那不是"卡片版式不好用"，是四列不好用。所以降的是列数，不是版式：
   选了卡片的人拿到的仍然是卡片，只是一排少几个。 */
@media (max-width: 1100px) {
  .aib-grid,
  .aib-grid--3,
  .aib-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .aib-grid,
  .aib-grid--3,
  .aib-grid--4 { grid-template-columns: minmax(0, 1fr); }
}

/* --- 3.3 徽标 / 分类标签 ------------------------------------------------- */
.aib-badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  /* 浅强调底上必须用 --accent-dk，不能用 --accent。
     这一条 single.css 的 .read-tags 那里写着，这里原来没照做：
     #de3f27 压在 #fdeeeb 上只有 3.84:1，而橙那一档 4.12:1，都不过。
     换成 --accent-dk 之后朱红 5.04、橙 5.59，五档全部过线。 */
  color: var(--accent-dk);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  white-space: nowrap;
}
/* 用 --ink-2 而不是 --ink-3：12px 的标签要"读得清"，不是"退到背景里"。
   这是**层级取舍，不是合规补救**——--ink-3 压 --surface-2 是过的
   （默认版式 4.70，博客版式 5.03，版式那一列见 check-contrast.php 第 4 节）。
   特意写清楚，是因为这条注释以前记的是另一个数（"4.47:1，不合格"），
   那个数对应的 --ink-3 早就不在了；再照它去推，结论会反。 */
.aib-badge--muted {
  background: var(--surface-2);
  color: var(--ink-2);
}
/* 推广标识：广告位必须带，且要和正文标签在视觉上区分得开 */
.aib-badge--promo {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--ink-3);
  font-weight: 500;
  font-size: 11px;
}

/* --- 3.4 区块标题 ------------------------------------------------------- */
.aib-sec-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}
.aib-sec-head > h2 {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -.01em;
}
.aib-sec-head > .aib-sec-more {
  margin-left: auto;
  font-size: 13.5px;
  color: var(--ink-3);
}
.aib-sec-head > .aib-sec-more:hover { color: var(--accent-btn); }

/* --- 3.5 面包屑 ---------------------------------------------------------
   与 inc/breadcrumbs.php 生成的 BreadcrumbList JSON-LD 同源。
   结构化数据必须对应读者看得见的内容，所以这两者只能由同一份数据渲染。
   ---------------------------------------------------------------------- */
.aib-crumb {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2);
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--ink-3);
  letter-spacing: .06em;
}
.aib-crumb a { color: var(--ink-3); }
.aib-crumb a:hover { color: var(--accent-btn); }
.aib-crumb .aib-crumb-sep { color: var(--line-2); }
.aib-crumb [aria-current] { color: var(--ink-2); }

/* --- 3.6 广告位挂载点 ---------------------------------------------------
   方案 C（混合）：静态导出时这里已有一份兜底内容，JS 加载后拉实时结果替换。
   所以这个容器绝不能是空的——它同时承担容灾职责。
   ---------------------------------------------------------------------- */
.aib-slot { display: block; }
.aib-slot[data-slot-state="loading"] { opacity: .55; }
/* 一条兜底内容都没有（新站还没配广告）：整块不显示。
   藏整个 .aib-slot 而不是里面的内容——标题还留着、下面空着，
   比整块不出现更像"坏了"。JS 拉到结果后把状态改掉，它就回来了。 */
.aib-slot[data-slot-state="empty"] { display: none; }

.aib-slot-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .12em;
  color: var(--ink-3);
  text-transform: uppercase;
  margin-bottom: var(--sp-3);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.aib-slot-title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* --- 3.7 分页 ----------------------------------------------------------- */
.aib-pager {
  display: flex;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-8) 0;
  font-variant-numeric: tabular-nums;
}
.aib-pager .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 14px;
  color: var(--ink-2);
}
/* 描边和文字必须同一档。
   这里原来两个都是 --accent，只换文字的话就只剩描边是亮的——
   1px 的亮朱红圈着一行深朱红字，看着像描边漏改了。 */
.aib-pager .page-numbers:hover { border-color: var(--accent-btn); color: var(--accent-btn); }
.aib-pager .page-numbers.current {
  background: var(--accent-btn);
  border-color: var(--accent-btn);
  color: var(--on-accent);
  font-weight: 600;
}

/* dots 不是页码，是「中间还隔着几页」的省略号，但核心给它的类名就是
   page-numbers dots，会被上面那条规则一视同仁地套上边框和白底——
   于是它看上去像一个**按不动的页码**，而旁边"上一页"又能按，
   这个对比会让人以为是坏了。这里只把框去掉。
   颜色不另设，跟着 .page-numbers 的 --ink-2 走：和两边的真数字同色，
   读起来才是"这段是空的"，而不是"这个按钮是灰的"。
   用 border-color: transparent 而不是 border: 0——后者会少掉 2px
   边框高度，这一格就比两边的页码矮一截，除非全站设了 border-box。 */
.aib-pager .page-numbers.dots {
  border-color: transparent;
  background: transparent;
}

/* 窄屏：paginate_links() 在中间页给出的是一长串
   （上一页 1 … 3 4 5 … 12 下一页，mid_size=2 时最多 9 格），
   375px 下按 9×34 + 8×8 的间距算是 370px，比内容宽还宽，会直接溢出。
   flex-wrap 是这里的**兜底**（宁可折成两行也不能横向溢出），
   缩尺寸和去掉省略号是为了让它在大多数情况下根本用不着折行。 */
@media (max-width: 560px) {
  .aib-pager {
    flex-wrap: wrap;
    gap: var(--sp-2);
    padding: var(--sp-6) 0;
  }
  .aib-pager .page-numbers {
    min-width: 34px;
    height: 34px;
    padding: 0 var(--sp-2);
    font-size: 13.5px;
  }
  .aib-pager .page-numbers.dots { display: none; }
}

/* --- 3.7b 结果组的分页条（网盘 / 磁力那两栏）---------------------------
   和上面那条的区别，不是样式而是**它是什么**：上面那条是主查询的翻页，
   一格一个真实地址，点了就重新请求；这条是前端切片，按钮是 <button>，
   翻的是已经取回来的那一批（两个数据源都不支持翻页，见
   inc/template-tags.php 的 aish_group_pager()）。所以共用 .aib-pager
   和 .page-numbers 的外观，只在结构和几个按钮专属的属性上分家。
   ---------------------------------------------------------------------- */

/* 少了这一条，这条分页条会**一直显示**。
   .aib-pager 的 display:flex 是作者样式，压得过浏览器默认表里
   [hidden]{display:none} 那条 UA 规则（作者样式优先，跟特异性无关），
   所以 <nav hidden> 照样铺在页面上。而 JS 那边一切正常——它确实
   摘了 hidden，摘不摘看起来都一样，谁也想不到问题在这儿。 */
.aib-pager--group[hidden] { display: none; }

.aib-pager--group {
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  /* 上面那条是 var(--sp-8) 的上下留白，这条下面是列表、上面压着卡片，
     只要上边距。 */
  padding: var(--sp-6) 0 0;
}

.aib-pager-status {
  font-size: 13px;
  color: var(--ink-3);
}

.aib-pager-numbers {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
}

/* 页码是 <button> 不是 <a>：浏览器给按钮的默认字体、行高和外观
   跟链接不是一套，不重置的话这一格的字比旁边那条分页条小一号，
   而且带着系统按钮的立体边框。 */
.aib-pager-numbers .page-numbers {
  font-family: inherit;
  line-height: 1;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}
/* 上一页 / 下一页是箭头，比数字窄一点才不显得空。 */
.aib-pager-numbers .page-numbers--step { min-width: 32px; }

/* 停在第一页时的「上一页」、最后一页时的「下一页」。禁用而不是隐藏：
   藏起来的话页码那一行会在翻页时**左右横移**，鼠标正好停在
   第 1 页的位置上会点到别的格。
   放在 .aib-pager .page-numbers:hover 后面，是因为两者特异性相同
   （都是三个选择器），靠先后决定谁赢——写到前面去的话，
   悬停在一个禁用按钮上仍然会亮起来。 */
.aib-pager-numbers .page-numbers[disabled] {
  opacity: .4;
  cursor: default;
  border-color: var(--line);
  color: var(--ink-2);
}

/* --- 3.8 侧栏面板 -------------------------------------------------------
   侧栏里的白色面板，以及装在里面的一组「分类 + 计数」。

   **为什么在共用层而不是某个版式的文件里**：同一份侧栏内容
   （template-parts/side.php → aish_side_fallback()）在博客列表页和
   文章页上都会出现，而这两个页面加载的是**不同的版式样式表**
   （blog.css / single.css）。把面板写在 blog.css 里的话，
   文章页上这几个类名一条规则都匹配不到——页面不报错，
   面板只是变成没有内边距、没有背景的一段裸文字。
   共用层保证"哪一版加载了 style.css，面板就是完整的"。

   版式要改的是**观感**而不是结构：博客版式的页面底是浅灰，
   白面板自己就分得开，所以那边只把描边去掉（见 blog.css）。
   ---------------------------------------------------------------------- */

.aib-panel {
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.aib-panel-title {
  margin-bottom: var(--sp-4);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--ink);
}

.aib-cats {
  display: flex;
  flex-direction: column;
}
.aib-cats > li + li {
  border-top: 1px solid var(--line);
}

.aib-cat-link {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
  font-size: 14px;
  color: var(--ink-2);
}
.aib-cat-link:hover { color: var(--accent-btn); }

/* 计数靠右。tabular-nums 让不同位数的数字占同样宽，
   否则整个列表右边缘会参差不齐。 */
.aib-cat-count {
  margin-left: auto;
  font-size: 12.5px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* 用户往侧栏里拖了小工具之后，模板就不渲染兜底面板了，
   改由 dynamic_sidebar() 输出。它给的容器也是 .aib-panel，
   但里面装的是第三方小工具，结构不受我们控制——
   所以这里只保证标题和间距，不假设里面有什么。 */
.aib-panel .widget-title,
.aib-panel > h2:first-child {
  margin-bottom: var(--sp-4);
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
}
.aib-panel ul { list-style: none; }
.aib-panel li + li { margin-top: var(--sp-2); }
.aib-panel a { color: var(--ink-2); }
.aib-panel a:hover { color: var(--accent-btn); }
.aib-panel img { max-width: 100%; height: auto; }

/* ==========================================================================
   4. 无障碍与 WordPress 必备类
   ========================================================================== */

.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.screen-reader-text:focus {
  position: static !important;
  width: auto; height: auto;
  margin: 0; padding: var(--sp-3) var(--sp-4);
  clip: auto;
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  font-weight: 600;
  z-index: 100000;
}

/* 跳转到主内容：键盘用户的第一个可聚焦元素 */
.aib-skip {
  position: absolute;
  left: -9999px;
  top: 0;
}
.aib-skip:focus {
  left: var(--sp-4);
  top: var(--sp-4);
}

.alignleft  { float: left;  margin: 0 var(--sp-5) var(--sp-4) 0; }
.alignright { float: right; margin: 0 0 var(--sp-4) var(--sp-5); }
.aligncenter { margin-left: auto; margin-right: auto; }

.wp-caption-text,
.wp-element-caption {
  font-size: 13px;
  color: var(--ink-3);
  text-align: center;
  margin-top: var(--sp-2);
}

.sticky .aib-card { border-color: var(--accent); }

/* ==========================================================================
   5. 表格（资费表这类结构，窄屏必须能横向滚动而不是撑破版式）
   ========================================================================== */

.aib-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.aib-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
}
.aib-table th {
  text-align: left;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--ink-3);
  padding: 0 var(--sp-4) var(--sp-3) 0;
  border-bottom: 1px solid var(--ink);
}
.aib-table td {
  padding: var(--sp-4) var(--sp-4) var(--sp-4) 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  color: var(--ink-2);
}
.aib-table td:first-child { color: var(--ink); font-weight: 600; }

/* ==========================================================================
   6. 站头 / 品牌 / 导航
   ========================================================================== */

.aib-logo {
  flex: none;
  border-radius: var(--radius-sm);
  /* 品牌色原样保留在标志上。这不是文字，是图形——按 WCAG 1.4.11
     的门槛只要 3:1，亮朱红压白底 4.33 是够的。
     上面那一大段换 --accent-btn 的理由（要压字/自己就是字）在这里不成立。 */
  color: var(--accent);        /* 内联 SVG 回退时用 currentColor 取色 */
}

.aib-brand-lock {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: none;
}
.aib-brand-lock:hover { color: inherit; }

.aib-brand-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.aib-brand-name {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1.25;
  white-space: nowrap;
}
.aib-brand-sub {
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: .04em;
  line-height: 1.3;
  white-space: nowrap;
}

/* --- 6.0 顶部工具条 -----------------------------------------------------
   Demo 里的 .utility。左边一句公告，右边一排快捷入口。

   底色用 --surface-2 而不是 --surface：它贴在站头上面，两个都是纯白
   的话中间那条分界线就只剩 1px，看起来像站头被拉长了 40px。
   压深一档，它才读起来是"另一条"。

   里面所有文字都用 --ink-2 / --ink，**不用强调色**：
   工具条是一整页最靠上的一排 13px 小字，它的职责是"读得到"，
   而强调色在这个站里的职责是"点得到、找得到"。
   这里不涉及合规问题（强调色真的要用，该取的是 --accent-btn，
   那个档在 --surface-2 上也过），是分工问题。 */
.aib-utility {
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  font-size: 13px;
}
.aib-utility-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-6);
  min-height: 40px;
  padding-top: var(--sp-2);
  padding-bottom: var(--sp-2);
}
.aib-utility-notice {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--ink-2);
  line-height: 1.6;
}
.aib-utility-nav { flex: none; margin-left: auto; }

/* .aib-nav--utility 那几条在 6.1 末尾、紧挨着 .aib-nav 本体。

   ⚠ 别挪回这里。它们和 .aib-nav 的规则**权重完全相同**——
   .aib-nav--utility 和 .aib-nav 都只是一个类，`.aib-nav--utility a`
   和 `.aib-nav a` 都是 (0,1,1)，`.aib-nav--utility .current-menu-item > a`
   和 `.aib-nav .current-menu-item > a` 都是 (0,2,1)。权重相同就看谁在后面，
   而 .aib-nav 在那个位置。写在这里是必输的，而且输得毫无痕迹：
   当前项那条会安静地退回 --accent，在 --surface-2 上只有 4.03:1。 */

@media (max-width: 768px) {
  /* 窄屏两行：公告一行，快捷入口一行。挤成一行的话，
     公告会被压到只剩省略号，而它恰恰是这一条里唯一有内容的部分。 */
  .aib-utility-inner { flex-direction: column; align-items: flex-start; gap: var(--sp-2); }
  .aib-utility-nav { margin-left: 0; }
}

.aib-masthead {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.aib-masthead-inner {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  height: 64px;
}
.aib-masthead-tools { margin-left: auto; }

/* 首页的站头是「功能条」，不是品牌条——品牌标识在这一页的中部
   （见 front-page.php 的 hero）。所以这里比常规站头矮一档、
   也不画底线：hero 自己有一条底线，两条线隔着一屏顶部会显得碎。
   矮一档还顺带把首屏的纵向预算让给了搜索区和广告位，
   而广告位必须落在首屏内是硬要求。 */
.aib-masthead--bare {
  height: 52px;
  border-bottom: 0;
}
.aib-masthead--bare .aib-masthead-inner { height: 52px; }

.aib-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  font-size: 14px;
  color: var(--ink-2);
}
.aib-nav a { color: var(--ink-2); }
.aib-nav a:hover { color: var(--accent-btn); }
.aib-nav .current-menu-item > a,
.aib-nav .current_page_item > a { color: var(--accent-btn); font-weight: 600; }

.aib-nav--footer {
  font-size: 13.5px;
  color: var(--ink-3);
  gap: var(--sp-4);
}
.aib-nav--footer a { color: var(--ink-3); }

/* 工具条里的快捷入口。放在这里是因为**必须在 .aib-nav 之后**，
   理由见 6.0 里那段说明：权重一样，只看先后。 */
.aib-nav--utility { font-size: 13px; }   /* .aib-nav 钉了 14px，传不进来 */
.aib-nav--utility a { color: var(--ink-2); }
/* 悬停和当前项 .aib-nav 默认给的是 --accent。#de3f27 压 --surface-2
   只有 4.03:1，而这条工具条上的字只有 13px，达不到 AA。 */
.aib-nav--utility a:hover,
.aib-nav--utility .current-menu-item > a,
.aib-nav--utility .current_page_item > a { color: var(--ink); }

@media (max-width: 900px) {
  .aib-masthead-inner {
    height: auto;
    padding-top: var(--sp-3);
    padding-bottom: var(--sp-3);
    flex-wrap: wrap;
  }
  .aib-nav--primary {
    order: 3;
    width: 100%;
    gap: var(--sp-4);
    font-size: 13.5px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .aib-nav--primary a { white-space: nowrap; padding: var(--sp-2) 0; }
  .aib-masthead-tools { flex: 1; min-width: 0; }
  .aib-brand-sub { display: none; }
}

/* ==========================================================================
   7. 搜索框
   ========================================================================== */

.aib-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.aib-search:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.aib-search input[type="search"] {
  flex: 1;
  min-width: 0;
  height: 44px;
  padding: 0 var(--sp-4);
  border: 0;
  background: transparent;
  font-size: 15px;
  outline: none;
  /* 去掉浏览器自带的清除叉，用我们自己的，样式才统一 */
  -webkit-appearance: none;
  appearance: none;
}
.aib-search input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }
.aib-search input::placeholder { color: var(--ink-3); }

.aib-search-submit {
  flex: none;
  height: 44px;
  padding: 0 var(--sp-5);
  background: var(--accent-btn);
  color: var(--on-accent);
  font-size: 14.5px;
  font-weight: 600;
  border-radius: 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0;
  transition: background-color .16s ease;
}
.aib-search-submit:hover { background: var(--accent-dk); }

.aib-search-clear {
  flex: none;
  width: 28px;
  height: 28px;
  margin-right: var(--sp-2);
  border-radius: var(--radius-pill);
  color: var(--ink-3);
  font-size: 18px;
  line-height: 1;
}
.aib-search-clear:hover { background: var(--surface-2); color: var(--ink); }

/* 站头里的紧凑版：不占满整行 */
.aib-search--compact { width: 220px; }
.aib-search--compact input[type="search"] { height: 36px; font-size: 13.5px; }
.aib-search--compact .aib-search-submit { height: 36px; padding: 0 var(--sp-4); font-size: 13.5px; }

/* --- 搜索框下面那行「同时搜索」说明 -------------------------------------
   这行字属于 aish_search_note()，首页 hero、搜索页、404 都用同一份。

   原来这个位置是三个提交按钮（.aib-search-types / .aib-search-type），
   连同它们的外层包装 .aib-search--multi 和 .aib-search-bar 一起删掉了：
   那三个按钮要求用户"先选范围再搜"，而这个站的搜索页现在一次把
   三类结果都取回来，不需要任何人在搜索之前先做一次选择。

   共 90 行左右的选择器没有别的地方引用。留着它们不会报错，
   只会让下一个改这版 CSS 的人以为页面上还有那三个按钮，
   然后照着一段已经不存在的结构去调间距。
   ---------------------------------------------------------------------- */
.aib-search-note {
  margin-top: var(--sp-3);
  font-size: 13px;
  line-height: 1.6;
  /* 用 --ink-2 而不是 --ink-3：这是搜索框下面那行说明，13px，
     属于"要读的内容"，不是元信息。同样是层级取舍，不是合规补救——
     --ink-3 在白面上过（默认版式 5.05，见 check-contrast.php 第 4 节）。
     这条注释以前记的是"3.0:1"，那是上一版墨色的数。 */
  color: var(--ink-2);

  /* 0.25.0 起这一格是**多行框**，站长在里面敲的回车就是换行。
     不加这条的话 \n 在 HTML 里只等于一个空格——他会以为是自己
     填错了，然后把整段挤成一行。 */
  white-space: pre-line;

  /* 这一行里可能出现站长贴的长地址（图片、跳转）。
     三个调用点的父容器都没有 overflow，不换行就会把版面撑破，
     而且是**只在窄屏上**出现——桌面上看不出来。 */
  overflow-wrap: anywhere;
}

/* 说明行可以自己写 <a>（0.25.0 起这一格收 HTML）。
   继承段落的颜色，不用强调色：这样"有链接"和"没链接"两种状态
   的对比度是同一个数，check-contrast.php 那一大套不用再为它多跑一格。
   认得出是链接靠下划线——这也是它和普通文字之间唯一的区别，
   所以下划线不能省。悬停时才换强调色。

   ⚠ 0.24.0 那版唯一的链接来源是「说明行的链接」那一栏，整行包成一个
     <a>。那一栏已经删了，现在链接是站长写在内容里的，可以有多个。 */
.aib-search-note a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.aib-search-note a:hover {
  color: var(--accent);
}

/* 说明行里的横幅（站长贴的图片地址）。
   不限宽的话图片按原始尺寸铺，窄屏上直接溢出——而这一行出现在
   首页首屏，是整页最显眼的位置。 */
.aib-search-note img {
  max-width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius);
}

/* --- 说明行的字色档（对应 AISH_Customize::note_classes()）---------------
   八个类名，每一个都必须是令牌，都要在 check-contrast.php 的 $mustPass
   里有对应条目。谁加一个类名而不加条目，就是引入一格"没人核过"的字色。

   ⚠ 这四档的颜色是 靛 / 紫 / 青 / 石板，**不是** 蓝 / 琥珀 / 绿。
     理由写在 note_classes() 的注释里：那三档压浅灰页面底（#e9ecee，
     就是 body.aish-scheme-mist 选中之后首页的 --bg）只有
     4.17 / 3.87 / 4.49，够不着正文级的 4.5。而 --tone-* 同时是入口卡
     的描边色（按 3.0 判），不能为一个说明行的字色去动那八档。

   ⚠ 选择器都要挂在 .aib-search-note 下面。这几个类名只在说明行里
     出现，挂上去是为了让"这个类只在说明行里生效"这件事写在样式里，
     而不是靠约定。 */
.aib-search-note .aish-note-ink {
  color: var(--ink);
}
.aib-search-note .aish-note-dim {
  color: var(--ink-3);
}
.aib-search-note .aish-note-accent {
  /* 取 --accent-btn 而不是 --accent：后者白字压它只有 4.33:1，
     当描边可以、当文字不行。见 :root 那一段。 */
  color: var(--accent-btn);
}
.aib-search-note .aish-note-indigo {
  color: var(--tone-indigo);
}
.aib-search-note .aish-note-violet {
  color: var(--tone-violet);
}
.aib-search-note .aish-note-cyan {
  color: var(--tone-cyan);
}
.aib-search-note .aish-note-slate {
  color: var(--tone-slate);
}
/* 荧光笔：浅底 + 深字，不是"把字染成某个色"。
   底取 --accent-soft（浅强调底），字取 --ink。这一对要单独核一次
   对比度——它和上面七档不是同一对令牌。 */
.aib-search-note .aish-note-mark {
  background: var(--accent-soft);
  color: var(--ink);
  padding: 0 2px;
  border-radius: 2px;
}

@media (max-width: 768px) {
  .aib-search--compact { width: 100%; }
}

/* --- 热点词 ------------------------------------------------------------- */
.aib-hotwords {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  font-size: 13.5px;
}
.aib-hotwords-title {
  color: var(--ink-3);
  flex: none;
}
.aib-hotword {
  padding: 3px var(--sp-3);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  transition: background-color .16s ease, color .16s ease;
}
.aib-hotword:hover {
  background: var(--accent-soft);
  color: var(--accent-btn);
}

/* ==========================================================================
   8. 广告位
   ========================================================================== */

/* .aib-slot 本身和 .aib-slot-title 在 3.6 节，这里只补挂载点内部。
   别在这儿再写一遍 .aib-slot{display:block}——同一份声明两处写，
   将来改一处漏一处，而且看不出哪份是权威的。 */
.aib-slot-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* 广告格（aish_ad_row() 渲染出来的每一格）。

   这两条以前在 home.css 里。搬过来的原因：搜索页的说明行下面也挂了
   一格（见 search.php），而那一页加载的是 blog.css，不加载 home.css——
   留在原处的话，那边的格子 `:empty` 不会被隐藏，没配广告时
   会在结果上方留出一块空的网格轨道。

   没配广告的格子完全不占地方：
   :empty 只看子节点，data-ad-slot 是属性、不影响判定——
   所以这里能同时满足"给广告插件的 JS 留出挂载点"和"空着不出现空洞"。
   广告一旦被填进来，格子自然出现，不需要再加任何显隐逻辑。 */
.aib-ad-cell { min-width: 0; }
.aib-ad-cell:empty { display: none; }

.aib-ad {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color .16s ease, box-shadow .16s ease, transform .16s ease;
}
.aib-ad:hover {
  border-color: var(--line-2);
  box-shadow: var(--shadow-1);
  transform: translateY(-1px);
  color: inherit;
}

.aib-ad-img {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  object-fit: contain;
  background: var(--surface-2);
}

/* logo 形态没配图时的首字母方块。
   尺寸、圆角、底色三项和 .aib-ad-img 完全一致，是为了让同一个列表里
   「配了图的」和「没配图的」卡片高度一样——否则两行广告的标题会差 0，
   而图片加载完的那一刻还会再跳一下。

   --ink-2 压 --surface-2 是 4.9:1（白面 5.6:1），过 AA。 */
.aib-ad-initial {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
  -webkit-user-select: none;
  user-select: none;
}

/* --- 投放形态 -----------------------------------------------------------
   三种形态共用 .aib-ad 的骨架（图标 + 标题 + 描述 + 推广标），
   只在这个基础上改排布。形态由投放后台的 type 字段决定。

   三种都必须有实际规则。只发类名不写规则的话，后台选了「海报」
   投出来的还是文字卡——而这件事在后台完全看不出来，
   只有在前台看才发现，那时候投放已经跑了一段时间了。
   ---------------------------------------------------------------------- */

/* text：没有图，省掉为图标留的横向空间 */
.aib-ad--text { padding: var(--sp-3) var(--sp-4); }

/* logo：单行内容（品牌名 + 一句话），图标跟着居中才不显得吊在半空。
   基线的 align-items: flex-start 是给两行的文字卡准备的。 */
.aib-ad--logo { align-items: center; }

/* poster：图在上、文在下。整块仍然是一个 <a>，所以点击区域不变 */
.aib-ad--poster { flex-direction: column; align-items: stretch; }
.aib-ad--poster .aib-ad-img {
  width: 100%;
  height: auto;
  /* 固定比例而不是固定高度：不同尺寸的海报裁出来高度一致，
     同一行里的卡片底部才对得齐，也不会因为图没加载完而抖一下。 */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius);
}

.aib-ad-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}
.aib-ad-title {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.4;
}
.aib-ad-desc {
  font-size: 13px;
  color: var(--ink-3);
  line-height: 1.55;
}

.aib-ad .aib-badge--promo {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
}

/* ==========================================================================
   9. 站脚
   ========================================================================== */

.aib-footer {
  margin-top: var(--sp-10);
  background: var(--surface);
  border-top: 1px solid var(--line);
}
/* 页脚的多列区。既没有小工具、也没有后台配的栏目时，
   footer.php 根本不输出这一段，所以这里不用再考虑"空着会长什么样"。
   两种列共用同一套 class：小工具那几列是 dynamic_sidebar 画的，
   配的那几列是 footer.php 直接画的，宽度和间距必须长得一样——
   它们会并排出现，不一致一眼就能看见。 */
.aib-footer-cols {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--sp-8) var(--sp-6);
  padding-top: var(--sp-8);
}
.aib-foot-col { flex: 1 1 160px; min-width: 0; }
.aib-foot-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: var(--sp-3);
}
/* 小工具自己带的 ul 在站内其它地方没有统一样式，
   而这里几列并排，行距不齐会很明显。 */
.aib-foot-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.aib-foot-col li { margin-top: var(--sp-2); }
.aib-foot-col li:first-child { margin-top: 0; }
.aib-foot-col,
.aib-foot-col p,
.aib-foot-col a { font-size: 13.5px; color: var(--ink-3); line-height: 1.7; }
.aib-foot-col a:hover { color: var(--ink); }

/* 上面有列的时候，和下面那行之间的间距收一档——
   两个 --sp-8 叠起来是 64px，中间什么都没有，看着像断开的两块。 */
.aib-footer-cols + .aib-footer-inner { padding-top: var(--sp-6); }

.aib-footer-inner {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--sp-6);
  padding-top: var(--sp-8);
  padding-bottom: var(--sp-8);
}
.aib-footer-brand { flex: 1 1 260px; }
.aib-footer-tagline {
  margin-top: var(--sp-3);
  font-size: 13.5px;
  color: var(--ink-3);
  line-height: 1.7;
  max-width: 34em;
}
.aib-footer-nav { flex: none; }
.aib-footer-meta {
  flex: 1 1 100%;
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  justify-content: space-between;
  font-size: 13px;
  color: var(--ink-3);
}
.aib-footer-icp a { color: var(--ink-3); }
.aib-footer-icp a:hover { color: var(--accent-btn); }

/* 站长自己在「参数设置 → 页脚 → 页脚页面」里写的那几篇。
   一篇都没写、或者写的都还没填正文时，footer.php 整段不输出。 */
.aib-footer-pages {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}
.aib-footer-pages a { color: var(--ink-3); }
.aib-footer-pages a:hover { color: var(--accent-btn); }

/* 底栏的「隐私政策 / 免责声明」。两篇都不存在时 footer.php 整段不输出。 */
.aib-footer-legal {
  /* 上面那行是 space-between。没有这一句的话，多出来的那个 <p>
     会把中间那个（备案号）顶到正中间去——而备案号紧挨版权才是
     常规排法。margin-left:auto 把余量全吸到这一项左边，
     于是前面几项都靠左、这一项靠右，和有没有备案号无关。 */
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}
.aib-footer-legal a { color: var(--ink-3); }
.aib-footer-legal a:hover { color: var(--accent-btn); }
/* 间隔点比链接淡一档：同色的话容易被当成链接的一部分，
   而它是 aria-hidden 的装饰，点不动。 */
.aib-footer-sep { opacity: .45; }

@media (max-width: 768px) {
  .aib-footer-inner { flex-direction: column; gap: var(--sp-5); }
  .aib-footer-meta { flex-direction: column; }
}

/* ==========================================================================
   10. 卡片反馈面板（⋯ → 我要反馈）
   --------------------------------------------------------------------------
   结构：每张结果卡上一个 28px 的「⋯」按钮，全页**唯一**一份面板
   （aish_report_panel()，模板里那个 static 守卫保证的），面板的位置
   由 assets/js/aish-report.js 用 getBoundingClientRect() 算。
   ========================================================================== */

/* --- 10.1 那条 [hidden] 兜底规则 ---------------------------------------
   ⚠ **它必须留在文件这个位置**，不能再往上挪。

   浏览器默认样式表里有 [hidden]{display:none}，但那是**用户代理样式**，
   任何一条作者样式里的 display 都能盖过它——跟特异性无关，作者样式优先。

   这就是上面 .aib-pager--group[hidden] 那一条存在的原因（见那段注释），
   也是这一整个文件里每一个"HTML 里带 hidden、靠 JS 摘掉"的元素
   共同的风险：某个组件后来加了一句 display:flex，元素就再也不隐藏了，
   而且**不报错**——JS 那边确实摘了 hidden，摘不摘看起来都一样。

   补这一条之后这条风险就没有了，前提是它排在那些组件**后面**：
   [hidden] 的特异性是 (0,1,0)，和 .aib-pager 这类单类选择器一样，
   同特异性时按出现顺序定胜负。写在文件开头的话，它就谁都说不动。

   会不会误伤？
     · .aib-search-clear  —— 带 hidden，样式里没有 display，本来就靠 UA 那条
     · .aib-pager--group  —— 已经有 (0,2,0) 的 [hidden] 规则，结果一样
     · .aib-report / -menu / -msg —— 下面三个规则块里一个 display 都没有
   插件自己样式表里带 display 的 hidden 元素不受影响：那些规则后加载。 */
[hidden] { display: none; }

/* --- 10.2 卡片上那个「⋯」按钮 -----------------------------------------
   刻意做得轻：它压在卡片右下角，而卡片上的主操作是标题和「打开」。
   做成一个和「复制链接」同级的按钮的话，一页 200 张卡上就有 200 个
   同等重量的"举报"，那会把这一页的视觉重点整个拉偏。

   两个宿主，一处一份偏移：
     · 数据源那两栏 —— 按钮是 .aib-card.aib-list-item 的**直接子元素**，
       而那张卡是 flex 纵向，不写 align-self 的话按钮会被拉满整行宽。
     · 内容那一栏 —— 按钮在 <p class="blog-card-meta"> 里，是行内元素，
       跟着 <time> 后面排，间距由 blog.css 的 `> * + *` 那条给。
   所以这条规则里只有 align-self 是给上面那个宿主的，在下面那个里
   被忽略（非 flex 项上的 align-self 无效），不需要两个选择器。 */
.aib-report-more {
  flex: none;
  align-self: flex-end;
  width: 28px;
  height: 28px;
  padding: 0;
  margin-top: var(--sp-3);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 16px;
  line-height: 1;
  color: var(--ink-3);
  vertical-align: middle;
  cursor: pointer;
}
/* 焦点态和悬停态**合并写**：JS 在打开面板时会把焦点移到面板里，
   所以这个按钮正常不会带焦点；但用 Tab 键的人是会走到它上面的，
   那时候必须有可见的落点。 */
.aib-report-more:hover,
.aib-report-more:focus,
.aib-report-more[aria-expanded="true"] {
  color: var(--ink);
  background: var(--surface-2);
  border-color: var(--line);
}
/* 内容那一栏的卡片里，时间戳和「⋯」在同一行。上面那个 margin-top
   是给 flex 纵向那个宿主的，在这里会让这一行整体下沉一档。 */
.blog-card-meta .aib-report-more { margin-top: 0; }

/* --- 10.3 面板 ---------------------------------------------------------
   ─────────────────────────────────────────────────────────────────
   **这个规则块里一个 display 都不要写**（下面三条同理）
   ─────────────────────────────────────────────────────────────────
   面板的显隐只靠 hidden 属性，而三种状态（整个面板 / 两级菜单 /
   那句提示语）都是在 hidden 上来回切。这里写一句 display:flex
   就会盖过 [hidden]（作者样式优先），于是"关掉"这个动作
   只是把 hidden 摘了又加、加完什么也没发生——而 JS 那边一切正常。

   上面 10.1 那条 [hidden] 兜底能挡掉这件事，但**别靠它**：
   那条规则解决的是"忘了写"这一类，这里刻意不写才是对的写法。

   position: fixed 是硬要求，两个理由：
     1. .aib-card.aib-list-item 上有 overflow: hidden（给标题的
        -webkit-line-clamp 兜底），面板只要是卡片的后代就会被裁掉；
     2. 计算结果和滚动位置无关，JS 不用把 scrollX/scrollY 算进去——
        那是这类浮层最容易算错的一处。
   z-index 90：全站只有跳转链接那条是 100000（它必须压过一切），
   站点没有 sticky 站头，90 足够压住所有正常内容。 */
.aib-report {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 90;
  min-width: 176px;
  max-width: 260px;
  padding: var(--sp-1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}

.aib-report-menu { margin: 0; padding: 0; }

.aib-report-item {
  display: block;
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.aib-report-item:hover,
.aib-report-item:focus {
  background: var(--surface-2);
  color: var(--accent-btn);
}

.aib-report-msg {
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* ==========================================================================
   11. 正版入口（零结果那一块 + 「下载」的跳转确认框）
   --------------------------------------------------------------------------
   0.21.0 换过形状。原来是"结果列表上面一块常驻的卡片"，被否掉了
   （原话和理由见 inc/legal.php 第一节）。现在两半：

     .aib-legal        零结果那一块，由 inc/legal.php 的 aish_legal_empty()
                       画。它**只在那一栏这次一条都没搜到时**出现，是列表
                       的替身——没有显隐状态，所以这一半里写 display 是
                       安全的。
     .aib-legal-modal  「下载」的确认框。全页唯一一份，显隐靠 hidden 切。

   ⚠ 第二半里**一个 display 都不能随便写**（下面 11.2 那段同理）。
     写一句 display:flex 就会盖过 [hidden]（作者样式优先），于是"关掉"
     这个动作只是把 hidden 加回去、什么也没发生——而 JS 那边一切正常。
     这是第 10 节那三条规则的同一条纪律。

   ⚠ 弹窗里那几列正版站（[data-aib-legal-group] 那几个 div）**也是
     hidden 切的**，所以它们自己身上一个 display 都不能写——正因为
     这样，下面根本没有一条 `.aib-legal-group` 规则。那一列里有规则的
     只有它里面的标题（.aib-legal-group-title）和共用的链接表
     （.aib-legal-list；它自己写 display:flex 是安全的，那个元素从来
     不带 hidden）。

   ⚠ 这一块里**不要**出现新的前景/背景组合。它整块用的都是已有的、
     check-contrast 逐格量过的搭配：--ink / --ink-2 压 --surface-2、
     --ink / --ink-2 压 --surface、--accent-btn 压 --surface。
     新配一对颜色就等于新开一格没人量过的组合，
     见 _deliver\check-contrast.ps1 的 $mustPass。
   ========================================================================== */

/* --- 11.1 零结果那一块 -------------------------------------------------
   底色用 --surface-2（次级面）而不是 --surface：
   它长在**列表本该在的那个位置**上，而列表里每一条都是一张 --surface
   的白卡片。同色的话它会看起来像一条结果——而它是另一类东西
   （一个指路牌，不是一条结果）。 */
.aib-legal {
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.aib-legal-title {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--ink);
}

.aib-legal-desc {
  margin: var(--sp-1) 0 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* 链接横排、窄屏自动换行。
   一栏最多 6 条（AISH_LEGAL_MAX_SITES），6 × 约 100px 一般放得下；
   放不下时换到第二行，比挤成一行窄条好读。 */
.aib-legal-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: var(--sp-3) 0 0;
  padding: 0;
  list-style: none;
}

/* 每条做成一个胶囊，而不是一行带下划线的文字：
   这些是"通往别处的出口"，和结果卡片上那个「打开」按钮是同一类东西。
   做成裸链接的话，一块灰底上六行蓝字看起来像注释。 */
.aib-legal-link {
  display: inline-block;
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--accent-btn);
  text-decoration: none;
}
.aib-legal-link:hover,
.aib-legal-link:focus {
  border-color: var(--accent-btn);
}

/* ⚠⚠ 这一块里**一个 display 都不能写** ⚠⚠
   ─────────────────────────────────────────────────────────────────
   它就是靠 hidden 属性切显隐的那一层（aish_legal_panel() 里那个
   `hidden`，assets/js/aish-legal.js 摘掉它）。

   为什么在这里特别要命：第 10.1 节那条 `[hidden]{display:none}` 兜底
   在**本文件第 1752 行**，而这一节在它后面。两条的特异性都是
   (0,1,0)，同特异性按出现顺序定胜负——所以这一节里写的任何一句
   `display` 都会**压过那条兜底**，元素从此不再隐藏，而且不报错。

   10.2 / 10.3 那两块也在这条线后面，它们靠的就是"一句 display 都不写"。
   这一节照同一条纪律来。

   所以下面那个对话框的**竖直位置不是靠父层 flex 居中的**，
   是靠 .aib-legal-modal 自己的上内边距让出来的。

   ⚠ position: fixed 也是硬要求（第 10 节那份理由，这里是同一份）：
     卡片长在 .aib-card 那一组里，而那个类上有 overflow: hidden，
     absolute 定位的浮层会被卡片自己裁掉，表现是"点了没反应"。
     这一份虽然是页面末尾的唯一节点，按 fixed 写以后谁把它挪进
     卡片里也不会坏。

   z-index 100001：**比跳转链接那条 100000 还高**。
   那个值是「必须压过一切」的意思，这里也确实要压过它——确认框打开时
   整页都该被盖住，包括左上角那个只有键盘用户会碰到的跳转链接。
   全站只有这两个值过万，写在这里免得下一个人以为 100001 是手滑。 */
.aib-legal-modal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100001;
  background: var(--scrim);
  /* 框比视口还高的那一档（窄屏 + 放大的字体）：整层自己滚，
     而不是把对话框的上下两头切在屏幕外。 */
  overflow: auto;
  /* 上边距就是那个"居中"：视口 10% 的高度留在框上面。
     用 vh 不用百分比——百分比算的是这一层的高度，而这一层是满屏的，
     那和 0 没区别（对话框会贴着顶）。 */
  padding: 10vh var(--sp-3) var(--sp-4);
}

/* 灰底上的那个对话框。整块用的都是已有的、量过的搭配：
   --surface 底 + --ink / --ink-2 文字 + --line 描边 + --shadow-2 投影。
   这里写 display: flex 是安全的——被切 hidden 的是上面那一层，
   这一层从来没有 hidden。 */
.aib-legal-dialog {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  max-width: 420px;
  /* 横向居中靠这一句；纵向那个 10vh 在上面那个父层的内边距上。
     不用 top:50% + translateY(-50%)：那句话在框比视口高的那一档
     会让顶部跑出屏幕之外，而且**滚不到**（负的 translate 不参与滚动范围）。 */
  margin: 0 auto;
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  text-align: left;
}

.aib-legal-dialog-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--ink);
}

.aib-legal-dialog-text {
  margin: 0;
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-2);
}

/* 两个按钮靠右，和全站其它操作区一致（主操作在最右）。 */
.aib-legal-dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-2);
  margin: 0;
}

/* 「将在 N 秒后……」里那个数字。
   ─────────────────────────────────────────────────────────────────
   tabular-nums：等宽数字。10 那一档会从两位数数到一位数（10 → 9），
   比例数字下这一跳会让整句话左右抖一下——一个每秒抖一次、又没什么
   信息量的动画。等宽之后每个数字占的宽度一样，那句文字纹丝不动。
   这一档就是为它留的：5 那档只数一位数，抖不起来。

   底下的加粗和 --ink（正文用的是 --ink-2）是为了让它在一句话里
   一眼能被找到——用户要找的就是这个数。两个颜色都在已量过的清单里。 */
.aib-legal-count {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--ink);
}

/* 「请支持正版，尊重原创。」那一行小字。
   ─────────────────────────────────────────────────────────────────
   它是**另一件事**，不是上面那句话的后半截：上面说的是"你要离开本站了"
   （提示），这一句说的是"希望你去正版那边"（立场）。
   两个意思挤在同一段里，读起来像在给提示加条件。

   所以上面那道细线 + 缩到 13px。线用 --line（全站描边色），
   字用 --ink-2 压 --surface——都是清单里量过的搭配。 */
.aib-legal-dialog-note {
  margin: 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* 弹窗里每一列正版站的小标题（「想支持正版的话：」）。
   ─────────────────────────────────────────────────────────────────
   ⚠ 注意这里**没有** `.aib-legal-group` 那条规则，而且是故意的：
     那个 div 是靠 hidden 属性切显隐的，它身上写任何一句 display
     都会压过第 10.1 节那条兜底（它在第 1752 行，比这一节靠前），
     于是"隐藏"这个动作从此不再生效——而且不报错。

   字号和上面那行小字一样（13px），因为它俩是同一档的信息：
   都是「附注」，不是正文。 */
.aib-legal-group-title {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* 零结果那一块**没有下边距**。
   ─────────────────────────────────────────────────────────────────
   基础样式给的 var(--sp-4) 那一档，前提是它下面还接着东西（列表、
   分页条）。但零结果这一支里，它后面直接就是 </section>——
   组与组之间的间距由 .blog-group 自己负责，这里留着就是一段
   谁都不是的空白，而且看起来像是"下面还有内容没加载出来"。 */
.aib-legal--entry {
  margin-bottom: 0;
}

/* --- 11.3 「提交屏蔽词」那张卡片 ---------------------------------------
   由 inc/blocklist.php 的 aish_blockword_panel() 画，全页唯一一份，
   显隐靠 hidden 切（assets/js/aish-blockword.js 摘 / 加那个属性）。

   ⚠⚠ 这一节里**凡是带 hidden 的元素，一个 display 都不能写** ⚠⚠
     和上面 11.2 那段是同一条纪律，理由在 11.2 的注释里：
     `[hidden]{display:none}` 那条兜底在本文件第 1752 行，**比这一节靠前**，
     两条特异性都是 (0,1,0)，同特异性按出现顺序定胜负——这里写的任何
     一句 display 都会**压过那条兜底**，于是"关掉卡片"只是把 hidden
     加回去、屏幕上什么也没发生，而且不报错。

     被切的那一层是 .aib-bw-modal。所以：
       · .aib-bw-modal         → 一句 display 都没有（看下面）
       · .aib-bw-dialog-msg    → 同理，它是 JS 用 hidden 显隐的那行提示

     里面那两个写 display 的是安全的：.aib-bw-dialog 和
     .aib-bw-dialog-actions 从来没有 hidden。

   ⚠ 这一份**不和法律那个弹窗合并**：那是一个合规界面（跳转前的确认），
     改这张卡片的样子不该顺手改掉它。两处各留各的，几何参数一致只是
     因为它们看起来应该是一套东西，不是因为它们是同一个组件。

   ⚠ 不引入新的前景 / 背景组合。整块用的都是 check-contrast 量过的：
     --ink / --ink-2 压 --surface、--ink 压 --surface-2、
     --accent-btn 压 --surface。 */

/* 灰底 + 居中。几何照抄 .aib-legal-modal（见上面那段注释里的理由）：
   竖直位置靠这一层自己的上内边距让出来，不是靠父层 flex 居中——
   框比视口高的那一档才能自己滚，而不是把上下两头切在屏幕外。
   position: fixed 是硬要求：卡片长在 .aib-card 那一组里，而那个类上有
   overflow: hidden，absolute 会被卡片自己裁掉，表现是"点了没反应"。

   z-index 100001 和 .aib-legal-modal 同值——两个都是"必须压过一切"
   （包括左上角那个 100000 的跳转链接）。全站只有这两个值过万。 */
.aib-bw-modal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100001;
  background: var(--scrim);
  overflow: auto;
  padding: 10vh var(--sp-3) var(--sp-4);
}

/* 灰底上那个对话框。这里写 display: flex 是安全的——被切 hidden 的是
   上面那一层，这一层从来没有 hidden。 */
.aib-bw-dialog {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  /* 比法律那个（420px）宽一点：那边是几行按钮，这边是一个要打字的框，
     窄了会让人一个词换一行地看。仍然小于窄屏，不撑破。 */
  max-width: 480px;
  margin: 0 auto;
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  text-align: left;
}

.aib-bw-dialog-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--ink);
}

.aib-bw-dialog-text {
  margin: 0;
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-2);
}

/* 「要屏蔽的词」那一行标签。13px + --ink-2，和上面那行说明同档——
   它不是标题，是这一格的说明。 */
.aib-bw-dialog-label {
  display: block;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* 那个多行输入框。
   ─────────────────────────────────────────────────────────────────
   font: inherit 是必须的：表单控件**不继承**祖先字体，不写这一句
   这里会掉回浏览器默认的等宽 / 小字号，和旁边那几行字对不上。

   底色用 --surface-2 而不是 --surface：卡片本身就是 --surface，
   同色的话这个框只剩一圈描边，看起来像"点不动的一块白"，而不是
   一个能打字的地方。

   vertical 而不是 both：横向拉宽会把这个居中卡片撑歪，而词很短、
   一行一个，横向没有拉的需要。 */
.aib-bw-dialog-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: var(--sp-3);
  font: inherit;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  resize: vertical;
}

.aib-bw-dialog-input:focus {
  border-color: var(--accent-btn);
  outline: none;
}

/* 「收到了」/「没能提交」那一行。
   ⚠ 它没有 display ——它是 JS 用 hidden 显隐的（say()）。见节首那段。

   点色和上面那两行说明同一档：13px + --ink-2，靠上面那道细线分开。
   细线而不是加粗 / 变色：成功的绿和失败的红都得是**新配的颜色**，
   而这一块不许出现没量过的组合（见节首）。 */
.aib-bw-dialog-msg {
  margin: 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* 两个按钮靠右，和全站其它操作区一致（主操作在最右）。 */
.aib-bw-dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-2);
  margin: 0;
}
