/* ==========================================================================
   设计 token（与后台管理页共用的色板；深浅色由 [data-theme] 切换）
   选择器用前缀匹配（^=），可直接覆盖 light-hc / dark-hc 高对比度变体，
   与核心 less 的写法一致（见 vendor/flarum/core/less/common/root.less）。
   ========================================================================== */
:root {
    --jn-bg: #0f1115;
    --jn-panel: #141821;
    --jn-card: #1b2029;
    --jn-card-hover: #232a36;
    /*
     * --jn-item-hover：**下拉菜单条目**专用悬停底色（v1.21 新增）。
     *   ⚠️ 为什么不复用 --jn-card / --jn-card-hover ——
     *     菜单底板用的是 --jn-panel；浅色主题下 --jn-panel 与 --jn-card / --jn-card-hover
     *     三者同为 #ffffff，悬停色 == 底板色 ⇒ 悬停完全看不见（用户反馈的直接原因）。
     *     深色主题下 --jn-card(#1b2029) 与 --jn-panel(#141821) 也近乎同色。
     *   ✅ 取值口径：对齐论坛核心「下拉条目悬停底色」的做法
     *     （vendor/flarum/core/less/common/Dropdown.less 的 :hover { background: var(--control-bg) }），
     *     即在各主题的菜单底板色上**明显拉开一档亮度**，保证悬停反馈肉眼可辨。
     */
    --jn-item-hover: #232a36;
    --jn-text: #e6e9ef;
    --jn-text-dim: #98a2b3;
    /*
     * --jn-brand：品牌橙（v1.15 新增），语义是「站点 Logo 的颜色」。
     *   ⚠️ 不要复用 --jn-accent —— 主色是蓝色系，与 Logo 橙是两个维度。
     * 取证口径：Logo 在前台是「管理后台 → 外观」配置的**图片**（.jn-brand-img），
     *   颜色读不到 CSS 变量，只能对 public/assets/logo*.webp **逐像素取样**：
     *   · 浅色版 logo-a6gq69c7.webp      主色 ≈ #F85606（占暖色像素 56.5%）
     *   · 深色版 logo-dark-mode-*.webp   主色 ≈ #F96216（占暖色像素 47.0%）
     * 此处按「首字兜底」的真实底色（图标位灰底 rgba(127,127,127,.12) 压在 --jn-card 上
     *   ≈ #272b33）复核对比度：本值取 #FB6A1E，逼近 Logo 原色又使 22px/600 的兜底字
     *   达到 ≥4.5:1（WCAG 1.4.3 正文口径；纯 #F96216 实测 4.43:1，差一点点）。
     */
    --jn-brand: #FB6A1E;
    /* P5-验收调值：边框对 --jn-card(#1b2029)/--jn-card-hover(#232a36) 实测 ≥3:1
       （WCAG 1.4.11 非文本元素口径；原 #242a35 仅 1.11~1.23:1） */
    --jn-border: #6b7b99;
    /*
     * --jn-line：「结构性描边」专用色，与 --jn-border 分工不同 ——
     *   · --jn-border 服务于「控件轮廓 / 焦点环 / 下拉菜单边界」等可交互元素，
     *     必须维持 ≥3:1 对比度（WCAG 1.4.11），不参与弱化；
     *   · --jn-line 只负责「页面骨架分层」：顶栏底线、侧栏与卡片轮廓、分区标题分隔线、
     *     侧栏内部横分隔线。它纯装饰，可以随主题与视口压淡甚至完全隐藏。
     * 取值口径：用半透明中性灰而不是实色，与任意底色叠加都不产生生硬的「铅笔线」。
     */
    --jn-line: rgba(148, 163, 184, .22);
    /*
     * --jn-anchor：分区锚点定位时顶部需要避让的高度。
     *   搜索条移除后，页面上只剩顶栏这一层 sticky 遮挡；但顶栏在窄屏会换行变高，
     *   写死数值必然在某一端失准，故此处只作为「无 JS 兜底值」，
     *   实际数值由页尾脚本量取 .jn-header 真实高度后回写（见 ANCHOR_OFFSET 注释）。
     *   v1.22：顶栏高度对齐论坛后由 68 → 52px，故本兜底值同步 76 → 60px（= 52 + 8）。
     */
    --jn-anchor: 60px;
    --jn-accent: #38bdf8;
    /* 主色浅底：用于「顶栏主导航悬停胶囊」等需要主色强调但不喧宾夺主的底色 */
    --jn-accent-soft: rgba(56, 189, 248, .14);
    /* 主色实心按钮上的文字色：深色主题主色偏亮，用深色字保证对比度（浅色主题反之） */
    --jn-on-accent: #0b1620;
    --jn-shadow: 0 6px 20px rgba(0, 0, 0, .28);
}
html[data-theme^="light"] {
    --jn-bg: #f5f7fa;
    --jn-panel: #ffffff;
    --jn-card: #ffffff;
    --jn-card-hover: #ffffff;
    /* 下拉条目悬停（见 :root 处说明）：菜单底板为 #ffffff，此值取浅灰拉开一档。
       取值贴近论坛核心 --control-bg（浅色 ≈ hsl(色调, sat, 93%)）的观感。 */
    --jn-item-hover: #eceff4;
    --jn-text: #1f2430;
    --jn-text-dim: #667085;
    /* --jn-brand：品牌橙（见 :root 处的取证说明）。浅色版 Logo 主色 #F85606 在
       「首字兜底」灰底(≈#f0f0f0)上仅 2.91:1，略压深为本值后达 3.1:1（22px/600 的大号字口径 ≥3:1） */
    --jn-brand: #F05205;
    /* P5-验收调值：边框对最亮底色 --jn-bg(#f5f7fa) 需 ≥3:1
       （WCAG 1.4.11 非文本元素口径；原 #e4e8ef 仅 1.23:1，与白卡片几乎融为一体） */
    --jn-border: #7d8899;
    /*
     * 浅色主题「不显示线条」（用户要求）：
     *   白底 + 浅灰底（--jn-bg #f5f7fa）组合下，任何实色描边都会形成明显的「框线网」，
     *   整页看起来像表格。故浅色主题下结构性描边直接置为 transparent，
     *   改由「底色差 + --jn-shadow 投影」承担分层（见各自的 html[data-theme^="light"] 覆盖规则）。
     * 注意：--jn-border 保持不变，登录按钮 / 输入框 / 下拉菜单仍有清晰轮廓。
     */
    --jn-line: transparent;
    --jn-accent: #0ea5e9;
    --jn-accent-soft: rgba(14, 165, 233, .10);
    --jn-on-accent: #ffffff;
    --jn-shadow: 0 6px 20px rgba(16, 24, 40, .08);
}
/* 高对比度变体：与论坛「浅色高对比度 / 深色高对比度」观感对齐（提高文字与边框对比） */
html[data-theme="dark-hc"] {
    --jn-bg: #000000;
    --jn-panel: #0a0c10;
    --jn-card: #12161d;
    --jn-card-hover: #1b212b;
    /* 下拉条目悬停（见 :root 处说明）：菜单底板为 #0a0c10，此值刻意提亮一档，
       保证高对比度模式下悬停反馈比默认深色主题更清晰。 */
    --jn-item-hover: #1b212b;
    --jn-text: #ffffff;
    --jn-text-dim: #c7d0dd;
    /* --jn-brand：高对比度模式需比默认橙更亮/更暗才能守住对比度。此处提亮到 #FF8A3C，
       对「首字兜底」灰底(≈#1f2329) 约 6.7:1，色相仍是 Logo 橙。 */
    --jn-brand: #FF8A3C;
    /* P5-验收调值：高对比度变体的边框要比默认深色主题(--jn-border #6b7b99)更亮，
       对 --jn-card(#12161d)/--jn-card-hover(#1b212b) 需 ≥3:1（原 #4a5568 仅 2.41:1） */
    --jn-border: #8394b3;
    /* 高对比度是用户显式选择的辅助模式：结构性描边必须保留（不参与弱化/隐藏） */
    --jn-line: var(--jn-border);
    --jn-accent: #7dd3fc;
    --jn-accent-soft: rgba(125, 211, 252, .22);
    --jn-on-accent: #00121f;
}
html[data-theme="light-hc"] {
    --jn-bg: #ffffff;
    --jn-panel: #ffffff;
    --jn-card: #ffffff;
    --jn-card-hover: #f2f4f7;
    /* 下拉条目悬停（见 :root 处说明）：菜单底板为 #ffffff，此值取浅灰拉开一档。 */
    --jn-item-hover: #e8ecf2;
    --jn-text: #000000;
    --jn-text-dim: #333333;
    /* --jn-brand：高对比度模式需比默认橙更亮/更暗才能守住对比度。此处压深到 #B8400A，
       对「首字兜底」灰底(≈#f0f0f0) 约 4.9:1，色相仍是 Logo 橙。 */
    --jn-brand: #B8400A;
    --jn-border: #000000;
    /* 高对比度是用户显式选择的辅助模式：结构性描边必须保留（不参与弱化/隐藏） */
    --jn-line: var(--jn-border);
    --jn-accent: #0369a1;
    --jn-accent-soft: rgba(3, 105, 161, .12);
    --jn-on-accent: #ffffff;
}
/*
 * ==========================================================================
 * 宽屏弱化结构线条（用户要求：「宽屏的时候不要线条感那么强」）
 *   - ≥1440px 时视口足够宽，卡片与分区靠留白 + 投影即可分层，骨架线进一步压淡到
 *     近乎不可见（保留一丝以便大屏下仍能分辨卡片边界）；
 *   - 高对比度主题（dark-hc / light-hc）不参与弱化：它们是用户显式开启的辅助模式，
 *     线条本身就是可用性的一部分。此处不需重写，因为
 *     html[data-theme="*-hc"]（0,1,1）权重高于 :root（0,1,0），媒体查询不改变权重；
 *   - 浅色主题同样不受影响（html[data-theme^="light"] 权重更高，保持 transparent），
 *     即「浅色 + 宽屏」= 依旧完全不显示线条。
 * ==========================================================================
 */
@media (min-width: 1440px) {
    :root { --jn-line: rgba(148, 163, 184, .10); }
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    background: var(--jn-bg);
    color: var(--jn-text);
    font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Roboto, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }

/* ==========================================================================
   顶栏
   ========================================================================== */
/*
 * 顶栏：三段式横向布局
 *   ① 品牌区 .jn-brand（我是谁）—— 最左，紧贴容器左边距；
 *   ② 主导航 .jn-nav-main（去哪：论坛 / 博客）—— 紧跟品牌区，同属「左栏」；
 *   ③ 动作区 .jn-actions（账户与偏好：登录/注册/用户/主题）—— 由 margin-left:auto
 *      推到最右，与左栏拉开，形成「左聚合 · 右对齐」的两栏结构。
 * 因此这里用 flex-start + 子项 auto 边距，而不是 space-between：
 * space-between 会把主导航推到正中间，反而让入口离散（已废弃的旧写法）。
 *
 * v1.22 顶栏高度对齐论坛（用户反馈）：
 *   原文「首页和论坛的导航条的高度不同，论坛的高度更好，首页的高度有点高，占用了空间」。
 *   论坛走核心变量 --header-height = 52px（vendor/flarum/core/less/common/variables.less），
 *   本页原为 68px，比论坛高出 16px。此处直接取 52px 与论坛**同高**，
 *   同时把顶栏内各控件按同一比例收一档（Logo 42→34、主导航胶囊 44→38、
 *   动作区按钮 38→34、头像 28→26），避免控件在更矮的顶栏里顶到上下边缘。
 *   依赖顶栏高度的下游数值（.jn-rail 的 sticky top / 限高、--jn-anchor 兜底、
 *   页尾 ANCHOR_OFFSET 兜底）已同步重算，保证滚动吸附与锚点定位不偏。
 */
.jn-header {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 14px;
    height: 52px;                       /* v1.22：68 → 52px，与论坛核心 --header-height 同高 */
    padding: 0 22px;
    background: var(--jn-panel);
    /*
     * 顶栏分层改用 --jn-line：
     *   - 原实现是「1px 实色 border-bottom + box-shadow: 0 1px 0 实色」两条硬线叠加，
     *     1920 宽屏下横贯整屏，是本页「线条感过强」的最主要来源，故删掉 box-shadow 的那层描边；
     *   - --jn-line 在浅色主题下为 transparent（不显示线条），仅靠投影区分顶栏与内容。
     */
    border-bottom: 1px solid var(--jn-line);
    box-shadow: 0 4px 16px rgba(0, 0, 0, .06);
}
/* 浅色主题：顶栏白底 + 页面浅灰底已有底色差，再补一层更柔和的投影保证滚动时层次清晰
   （精确匹配 data-theme="light"：light-hc 仍保留实色描边，不需要投影） */
html[data-theme="light"] .jn-header { box-shadow: 0 2px 12px rgba(16, 24, 40, .10); }
/*
 * ==========================================================================
 * P5-5 顶栏毛玻璃（详见 doc/戒导航首页显示优化设计方案.md 五、P5-5）
 *   - 目的：滚动时让内容从顶栏下方「透出来」，保留顶栏层次感，而不是一块实色挡板；
 *   - @supports 包裹：不支持 backdrop-filter 的浏览器继续用上面的实色底，
 *     功能与观感不受影响（只是没有模糊），属纯渐进增强；
 *   - 半透明底色用 color-mix 从 --jn-panel 推导（四套主题自动一致，不新增色板）；
 *     若浏览器支持 blur 但不支持 color-mix，则该声明被丢弃、自动回落实色底，仍然可用；
 *   - -webkit- 前缀供旧版 Safari 使用（其 backdrop-filter 长期只有前缀形式）。
 * ==========================================================================
 */
@supports ((backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px))) {
    .jn-header {
        background: color-mix(in srgb, var(--jn-panel) 78%, transparent);
        -webkit-backdrop-filter: blur(12px);
        backdrop-filter: blur(12px);
    }
}
.jn-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 0 0 auto;
    min-width: 0;
}
.jn-brand-logo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;                        /* v1.22：42 → 34px，随顶栏 52px 收一档 */
    height: 34px;
    font-size: 19px;
    background: linear-gradient(135deg, rgba(56, 189, 248, .22), rgba(56, 189, 248, .06));
    /* 纯装饰性 logo 框：描边随主题弱化（浅色主题下不显示线条），底色渐变本身已给出边界 */
    border: 1px solid var(--jn-line);
    border-radius: 12px;
    overflow: hidden;
}
.jn-brand-logo img { width: 100%; height: 100%; object-fit: cover; }
.jn-brand-name { font-size: 18px; font-weight: 700; letter-spacing: .5px; white-space: nowrap; }

/*
 * 「管理后台 → 外观」配置的图片 Logo（与论坛核心同源，FQ-009）
 *   - 尺寸口径对齐核心 .Header-logo（vendor/flarum/core/less/common/App.less）：
 *     高度自适应、按高度限高；本页顶栏 v1.22 起已与论坛同高（52px），
 *     故 Logo 限高直接取核心同值 30px（原 34px），两处顶栏视觉重量完全一致；
 *   - 同时配置了深色模式 Logo 时（.has-dark-logo），深色主题下明暗互换 ——
 *     等价于核心的 [data-theme^='dark']:has(&--dark-mode) 写法，这里用显式类名实现
 *     （不依赖 :has()，兼容性更好；本页主题选择器统一用 [data-theme^="dark"] 前缀匹配，
 *      因为主题值可能是 dark 或 dark-hc）。
 */
.jn-brand-img { display: block; max-height: 30px; width: auto; }
.jn-brand-img--dark { display: none; }
html[data-theme^="dark"] .jn-brand.has-dark-logo .jn-brand-img:not(.jn-brand-img--dark) { display: none; }
html[data-theme^="dark"] .jn-brand.has-dark-logo .jn-brand-img--dark { display: block; }

/*
 * 顶栏主导航（论坛 / 博客）：紧跟品牌区、左对齐
 *   为什么把它从右侧动作区独立出来：
 *     · 语义不同 —— 主导航是「去内容区」的入口（论坛 / 博客），动作区是「账户与偏好」
 *       （登录 / 注册 / 用户菜单 / 主题菜单）；混在一排会让高频入口被低频按钮淹没；
 *     · 视线动线 —— 品牌 → 主导航 → 动作区 自左向右，视线起点即入口，无需横跨整屏；
 *     · 视觉重量 —— 主导航字号与图标比动作区大一档（15px / 18px 对 14px / 16px），
 *       点击热区同步放大（38px 高），符合「最高频跳转要最好点」的原则。
 *       v1.22：随顶栏 52px，胶囊高 44 → 38px、字号 16 → 15px、图标 20 → 18px。
 *   与品牌区的距离：.jn-header 的 gap(14px) + 本处 margin-left(10px) ≈ 24px，
 *   既明确「紧邻 Logo」，又不至于贴在一起显得拥挤。
 *   悬停：主色浅底胶囊 + 文字与图标转主色，与本站其余交互（卡片描边转主色）同语言。
 */
.jn-nav-main { display: flex; align-items: center; gap: 6px; margin-left: 10px; flex: 0 0 auto; min-width: 0; }
.jn-nav-main-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 38px;                       /* v1.22：44 → 38px */
    padding: 0 13px;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: .3px;
    color: var(--jn-text);
    border-radius: 12px;
    white-space: nowrap;
    transition: background .18s, color .18s;
}
/* 图标着主色：无边框状态下一眼可辨语义（参考站点把导航图标着色的做法） */
.jn-nav-main-link .jn-ic { width: 18px; height: 18px; color: var(--jn-accent); }
.jn-nav-main-link:hover { color: var(--jn-accent); background: var(--jn-accent-soft); }

/* 顶栏右侧动作区：登录 / 注册 / 用户菜单 / 主题菜单（margin-left:auto 将其推到最右，见 .jn-header 注释） */
.jn-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex: 0 1 auto; min-width: 0; }

/*
 * 顶栏次要入口按钮：登录、注册共用同一个基类，保证「同尺寸 / 同圆角 / 同图标间距」，
 * 只用颜色与边框区分主次层级（登录 = 中性描边次要按钮；注册 = 主色实心主按钮）。
 * 注：论坛 / 博客已迁至 .jn-nav-main（字号与热区更大一档），不再使用本基类。
 */
.jn-top-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 34px;                       /* v1.22：38 → 34px */
    padding: 0 12px;
    font-size: 14px;
    font-family: inherit;
    font-weight: 500;
    color: var(--jn-text-dim);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    white-space: nowrap;
    transition: background .18s, color .18s, border-color .18s;
}
.jn-top-link:hover { color: var(--jn-text); background: var(--jn-card); }
.jn-top-link .jn-ic { width: 16px; height: 16px; }
/* 登录：中性描边（次要动作），hover 时描边与文字转主色 */
.jn-top-link.is-outline { color: var(--jn-text); border-color: var(--jn-border); }
.jn-top-link.is-outline:hover { color: var(--jn-accent); border-color: var(--jn-accent); background: var(--jn-card); }
/* 注册：主色实心（主要动作），文字用与主色对比度足够的 --jn-on-accent */
.jn-top-link.is-solid { color: var(--jn-on-accent); background: var(--jn-accent); border-color: var(--jn-accent); }
.jn-top-link.is-solid:hover { color: var(--jn-on-accent); background: var(--jn-accent); filter: brightness(1.08); }

/* ==========================================================================
   下拉菜单（用户菜单 / 主题菜单）：与论坛顶栏同一交互与观感
   - 触发器：悬停高亮；菜单：右上角对齐、圆角、阴影、点击外部或 Esc 关闭
   ========================================================================== */
.jn-dd { position: relative; }
.jn-dd-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 34px;                       /* v1.22：38 → 34px */
    padding: 0 10px;
    font: inherit;
    font-size: 14px;
    color: var(--jn-text-dim);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    transition: background .18s, color .18s, border-color .18s;
}
.jn-dd-btn:hover,
.jn-dd-btn[aria-expanded="true"] { color: var(--jn-text); background: var(--jn-card); border-color: var(--jn-border); }
.jn-dd-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 40;
    min-width: 176px;
    padding: 6px;
    background: var(--jn-panel);
    border: 1px solid var(--jn-border);
    border-radius: 12px;
    box-shadow: var(--jn-shadow);
}
.jn-dd-menu[hidden] { display: none; }
.jn-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    font: inherit;
    font-size: 14px;
    text-align: left;
    color: var(--jn-text);
    background: transparent;
    border: 0;
    border-radius: 8px;
    cursor: pointer;
    white-space: nowrap;
}
/* 悬停底色用 --jn-item-hover（专用 token），不可用 --jn-card —— 浅色主题下
   --jn-card 与菜单底板 --jn-panel 同为 #ffffff，会导致悬停完全无反馈（v1.21 修复） */
.jn-menu-item:hover { background: var(--jn-item-hover); }
.jn-menu-sep { height: 1px; margin: 6px 4px; background: var(--jn-border); }

/* 用户菜单：头像 + 昵称（对齐核心 SessionDropdown 的 Button--user） */
.jn-avatar {
    width: 26px;                        /* v1.22：28 → 26px */
    height: 26px;
    flex: 0 0 auto;
    object-fit: cover;
    background: var(--jn-card);
    border-radius: 50%;
}
.jn-user-name { max-width: 9em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 图标：内联 SVG 精灵（首页零静态资源，故用 symbol + use 复刻核心的 FontAwesome 语义） */
.jn-ic { width: 18px; height: 18px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.jn-menu-item .jn-ic { width: 16px; height: 16px; }
.jn-ic-scheme, .jn-ic-check { pointer-events: none; }

/* 主题菜单触发器：按当前解析后的方案显示对应图标（auto 由 JS 解析为具体方案） */
.jn-theme-toggle .jn-ic { display: none; }
.jn-theme-toggle[data-current="auto"] .jn-ic[data-icon="auto"],
.jn-theme-toggle[data-current="light"] .jn-ic[data-icon="light"],
.jn-theme-toggle[data-current="dark"] .jn-ic[data-icon="dark"],
.jn-theme-toggle[data-current="light-hc"] .jn-ic[data-icon="light-hc"],
.jn-theme-toggle[data-current="dark-hc"] .jn-ic[data-icon="dark-hc"] { display: block; }
/* 主题选项：选中项显示 ✓（同核心 ThemeSwitcher：active → fa-check，其余 → 方案图标） */
.jn-theme-option .jn-ic-check { display: none; }
.jn-theme-option.is-active .jn-ic-scheme { display: none; }
.jn-theme-option.is-active .jn-ic-check { display: block; }
.jn-theme-option.is-active { color: var(--jn-accent); }

/* ==========================================================================
   登录 / 注册弹窗（仅访客）：点击顶栏「登录 / 注册」在本页弹出，无需跳转到论坛
   ========================================================================== */
.jn-modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}
.jn-modal[hidden] { display: none; }
.jn-modal-mask { position: absolute; inset: 0; background: rgba(6, 9, 14, .6); }
.jn-modal-box {
    position: relative;
    width: 100%;
    max-width: 360px;
    padding: 26px 24px 22px;
    background: var(--jn-panel);
    border: 1px solid var(--jn-border);
    border-radius: 16px;
    box-shadow: var(--jn-shadow);
}
.jn-modal-close {
    position: absolute;
    top: 10px;
    right: 12px;
    width: 30px;
    height: 30px;
    font-size: 20px;
    line-height: 1;
    background: transparent;
    border: 0;
    border-radius: 8px;
    color: var(--jn-text-dim);
    cursor: pointer;
}
.jn-modal-close:hover { color: var(--jn-text); background: var(--jn-card); }
.jn-modal-title { margin: 0 0 18px; font-size: 18px; color: var(--jn-text); }
.jn-form { display: flex; flex-direction: column; gap: 14px; }
.jn-field { display: flex; flex-direction: column; gap: 6px; }
/* .jn-field 本身是 flex 容器，其 display 会盖掉 UA 对 [hidden] 的 display:none，
   故显式声明一条，保证两步验证输入框默认真的不可见（见 HTML 中的 #jn-login-2fa）。 */
.jn-field[hidden] { display: none; }
.jn-field > span { font-size: 13px; color: var(--jn-text-dim); }
.jn-field input {
    padding: 10px 12px;
    font-size: 14px;
    font-family: inherit;
    color: var(--jn-text);
    background: var(--jn-card);
    border: 1px solid var(--jn-border);
    border-radius: 10px;
    outline: none;
}
.jn-field input:focus { border-color: var(--jn-accent); }
.jn-remember { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--jn-text-dim); cursor: pointer; }
.jn-form-error {
    margin: 0;
    padding: 9px 12px;
    font-size: 13px;
    color: #fca5a5;
    background: rgba(239, 68, 68, .12);
    border: 1px solid rgba(239, 68, 68, .35);
    border-radius: 10px;
}
.jn-form-error[hidden] { display: none; }

/* ---- 登录人机校验状态条（PoW，FQ-050）-------------------------------------
 * 三态与论坛弹窗的 PowCaptchaWidget 同口径：
 *   solving  解算中：转圈 + 「正在完成安全校验…」
 *   verified 已通过：绿色对勾 + 「安全校验通过」
 *   error    已失败：红色叹号 + 「安全校验失败，点击重试」（整条可点击）
 * ------------------------------------------------------------------------ */
.jn-pow {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: -2px 0 0;
    font-size: 13px;
    line-height: 1.4;
    color: var(--jn-text-dim);
}
.jn-pow[hidden] { display: none; }
.jn-pow-icon {
    position: relative;
    flex: none;
    width: 16px;
    height: 16px;
    box-sizing: border-box;
    border: 2px solid currentColor;
    border-radius: 50%;
}
/* 解算中：缺口圆环 + 匀速旋转 */
.jn-pow[data-state="solving"] .jn-pow-icon {
    border-color: rgba(148, 163, 184, .3);
    border-top-color: var(--jn-text-dim);
    animation: jn-pow-spin .7s linear infinite;
}
/* 通过：实心绿圆 + 白色对勾（用两条边框旋转 45° 画出来，免图标依赖） */
.jn-pow[data-state="verified"] { color: #22c55e; }
.jn-pow[data-state="verified"] .jn-pow-icon { border: 0; background: #22c55e; }
.jn-pow[data-state="verified"] .jn-pow-icon::after {
    content: "";
    position: absolute;
    top: 1px;
    left: 5px;
    width: 4px;
    height: 8px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
/* 失败：实心红圆 + 白色叹号，整条可点击重试 */
.jn-pow[data-state="error"] { color: #fca5a5; cursor: pointer; }
.jn-pow[data-state="error"] .jn-pow-icon { border: 0; background: #ef4444; }
.jn-pow[data-state="error"] .jn-pow-icon::after {
    content: "!";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
    color: #fff;
}
@keyframes jn-pow-spin { to { transform: rotate(360deg); } }

.jn-submit {
    padding: 11px 14px;
    font-size: 15px;
    font-family: inherit;
    color: #fff;
    background: var(--jn-accent);
    border: 0;
    border-radius: 10px;
    cursor: pointer;
}
.jn-submit:hover { filter: brightness(1.08); }
.jn-submit:disabled { opacity: .65; cursor: default; }
/* 弹窗底部「换个入口」链接（注册弹窗 → 去登录），对齐核心 SignUpModal 的 log_in_text */
.jn-form-alt {
    margin: 0;
    font-size: 13px;
    text-align: center;
    color: var(--jn-text-dim);
}
.jn-form-alt button {
    padding: 0;
    font: inherit;
    color: var(--jn-accent);
    background: transparent;
    border: 0;
    cursor: pointer;
}
.jn-form-alt button:hover { text-decoration: underline; }

/* ==========================================================================
   主体：左侧分类栏 + 右侧内容区
   - 内容整体居中并限制最大宽度，避免在超宽屏上无限拉伸导致卡片过宽、阅读线过长
   - P5-2：最大宽度 1440 → 1600px
   - v1.12：1600 → 1800px（配合断点表新增的超宽档 7 / 8 列 —— 用户反馈
     「超宽屏也要列数自适应」，若仍锁 1600px，超宽屏多出的列只能靠压缩现有卡片宽度换来，
     卡片会被挤到折行；放宽到 1800px 后 1920 视口下 8 列仍有约 185px 的卡片宽度）
   - 列间距 18 → 12px（与 .jn-grid 的 12px 对齐）
   ========================================================================== */
.jn-layout {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    width: 100%;
    max-width: 1800px;
    margin: 0 auto;
    /* v1.11：原 18px 22px 40px —— 用户要求「空间利用率更高、更紧凑」，
       左右各收 4px、上收 4px、下收 8px；窄屏另有 14px 规则（见 @media 860px），不受影响 */
    padding: 14px 18px 32px;
}
/*
 * v1.20 侧栏轨道（拆卡）：把「分类导航」与「最近访问」拆成**两张独立卡片**后，
 *   `.jn-side` 不再是 .jn-layout 的直接子项，改由 .jn-rail 统一承载 ——
 *   原因（用户反馈原文）：「最近访问列表和分类导航放在不同的卡片中，这样导航是导航，
 *   最近访问是最近访问，这是两个功能，不应该放到整体，给用户迷惑」。
 *   - 原实现是**同一个** .jn-side 卡片里塞三块（分类导航 / 最近访问 / Star），
 *     靠 .jn-recent 的上边框做分隔，视觉上像同一功能的两个分区，功能边界不清晰；
 *   - 拆开后：.jn-rail 只负责「列宽 200px + sticky 吸附 + 两卡间距」，
 *     两张卡片各自自带背景 / 描边 / 圆角（与改造前的卡片口径完全一致，不新造视觉语言）；
 *   - sticky 上移到轨道层：两张卡**同步吸附**，不会出现「导航卡跟着滚、最近访问卡留在下面」
 *     的割裂动线；max-height 同理上移，由轨道统一限高，卡片各自内部滚动。
 */
.jn-rail {
    flex: 0 0 200px;                    /* P5-5：232 → 200px，为网格多列腾出宽度 */
    position: sticky;
    top: 70px;                          /* v1.22：86 → 70px（顶栏 52 + 内容区上内边距 14 + 4） */
    display: flex;
    flex-direction: column;
    gap: 12px;                          /* 两卡之间的呼吸间距（与 .jn-layout 的栅格间距同值） */
    max-height: calc(100vh - 90px);     /* v1.22：106 → 90px，与 top 同步下移 16px */
}
.jn-side {
    /* 轨道内纵向排列：以内容高为基准，只在轨道限高时才收缩（min-height:0 让 .jn-nav 接管滚动） */
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 14px 10px 12px;
    background: var(--jn-panel);
    /* 结构轮廓改用可弱化的 --jn-line（浅色主题下 transparent）；宽屏与浅色下的分层改由投影承担 */
    border: 1px solid var(--jn-line);
    border-radius: 14px;
}
/*
 * v1.20 拆卡必须补的一条：hidden 属性对元素的隐藏来自 UA 样式表（[hidden] { display: none }），
 *   权重是 0,1,0；而上面 .jn-recent 显式声明了 display: flex（同为 0,1,0 但位于作者样式表，后者胜）
 *   —— 于是 `hidden` 会**失效**，无访问记录时页面仍会露出一张只剩「最近访问 + 清空」的空卡片。
 *   OLD 实现（拆卡前）没有给 .jn-recent 声明 display，所以没有这个问题；
 *   这里显式补一条，恢复「无记录 = 整卡不显示（含标题行与清空按钮）」的既有行为。
 *   注：本卡是 .jn-rail 的 flex 子项，display:none 后不占位，轨道内自然只剩分类导航卡。
 */
.jn-recent[hidden] { display: none; }
/* 浅色主题：卡片无描边，用投影与页面底色分离（否则白卡片贴白底会「糊」在一起）
   （精确匹配 data-theme="light"：light-hc 保留 #000 实色描边）
   v1.20：选择器补上 .jn-recent —— 拆卡后它是同级卡片，投影口径必须同源，否则两卡「厚薄不一」 */
html[data-theme="light"] .jn-side,
html[data-theme="light"] .jn-recent { box-shadow: var(--jn-shadow); }
/*
 * ==========================================================================
 * P5-5 侧栏折叠（详见 doc/戒导航首页显示优化设计方案.md 五、P5-5）
 *   - 展开态 200px，折叠态 56px 只留图标列；折叠按钮置于侧栏**顶部**（用户要求：
 *     「左侧菜单栏的收齐功能放到菜单栏的最上面」），不再随侧栏内容增长而需要滚动才能触达；
 *   - 状态由 <html data-side="collapsed"> 驱动，页首防闪脚本已写好初值，
 *     因此刷新后不会出现「先展开再收起」的跳动；
 *   - 折叠时隐藏文字 / 计数 / 最近访问 / Star —— 56px 放不下它们，硬留会把条目挤成两行；
 *   - 只用 min-width:861px 生效：≤860px 侧栏已变成顶部横条（横向排列），
 *     此时折叠会把横条压成 56px 高，语义不成立。
 * ==========================================================================
 */
/*
 * ==========================================================================
 * v1.18 控制行合并：折叠按钮由「独占一行」移入「分类导航」标题行的右侧
 *   （用户要求：「把『收起』这个功能移动到『分类导航』的右侧，单独占用一行太浪费空间了」）
 *   - 原实现是纵向两块：按钮行（自带下边框）+ 标题行，在只有 200px 宽的侧栏里
 *     白白吃掉一整行高度；合并为 .jn-side-head 一行后，标题居左、按钮居右，行高与标题行等高，
 *     等于零额外垂直开销；
 *   - 分隔线由「按钮的下边框」上移到「本行容器的下边框」：视觉上仍把控制行与下方列表分开；
 *     浅色主题下 --jn-line 为 transparent，分隔改由间距承担；
 *   - 左内边距 10px 与 .jn-side-title 原左内边距一致，标题位置与改造前齐平，不产生错位。
 * ==========================================================================
 */
.jn-side-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 0 2px 12px 10px;
    border-bottom: 1px solid var(--jn-line);
}
/*
 * 按钮视觉语言直接复用侧栏既有的「右侧小按钮」口径（.jn-recent-clear「清空」）：
 * 同字号 12px / 同内边距 2px 6px / 同圆角 6px / 同 hover 反馈（--jn-accent + --jn-accent-soft），
 * 使侧栏上下两处控制按钮观感一致，不新造一套按钮样式。
 * 图标由 .jn-ic 默认 18px 降到 16px：与 12px 文字同一视觉重量，不再「头重脚轻」。
 */
.jn-side-toggle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 6px;
    font-family: inherit;
    font-size: 12px;
    font-weight: 400;
    color: var(--jn-text-dim);
    background: none;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
}
.jn-side-toggle:hover { color: var(--jn-accent); background: var(--jn-accent-soft); }
.jn-side-toggle .jn-ic { width: 16px; height: 16px; transition: transform .18s; }
@media (min-width: 861px) {
    /* v1.20 拆卡：列宽（flex-basis）已上移到 .jn-rail，折叠收窄必须落在轨道上才生效；
       卡片自身的内边距收窄仍留在 .jn-side，保持折叠态图标列的原有几何（56px 卡 − 4px 内边距） */
    html[data-side="collapsed"] .jn-rail { flex-basis: 56px; }
    html[data-side="collapsed"] .jn-side { padding-left: 4px; padding-right: 4px; }
    html[data-side="collapsed"] .jn-side-title,
    html[data-side="collapsed"] .jn-recent,
    html[data-side="collapsed"] .jn-star,
    html[data-side="collapsed"] .jn-side-toggle-text { display: none; }
    /* 图标之后的兄弟节点（分类名 span + 计数 span）一律隐藏：不绑具体类名，模板增删字段也不易失配 */
    html[data-side="collapsed"] .jn-nav-item .jn-nav-icon ~ * { display: none; }
    html[data-side="collapsed"] .jn-nav-item {
        justify-content: center;
        gap: 0;
        padding-left: 0;
        padding-right: 0;
    }
    /* 折叠态标题被隐藏（见上），控制行只剩折叠按钮 → 整行居中（原先居中的是按钮自身） */
    html[data-side="collapsed"] .jn-side-head {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }
    /* 折叠后箭头翻向：同一个图标表达「展开 / 收起」两个方向，不额外引入图标资源 */
    html[data-side="collapsed"] .jn-side-toggle .jn-ic { transform: rotate(180deg); }
}
/* v1.18：内边距移交 .jn-side-head（本元素现为控制行内的左侧标题），故 padding 归零只留字型 */
.jn-side-title { padding: 0; font-size: 15px; font-weight: 700; color: var(--jn-text-dim); }
.jn-nav { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; }
/* 侧栏滚动条细化：与整体轻量观感一致（Firefox / Chromium 各一套，不影响布局） */
.jn-nav { scrollbar-width: thin; scrollbar-color: var(--jn-border) transparent; }
.jn-nav::-webkit-scrollbar { width: 6px; }
.jn-nav::-webkit-scrollbar-thumb { background: var(--jn-border); border-radius: 3px; }
.jn-nav-item {
    /* position:relative 为悬停态左侧「竖条」伪元素提供定位基准（见下方 ::before） */
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    font-size: 15px;
    color: var(--jn-text-dim);
    /*
     * 3px 透明左边框保留作「占位边框」：所有条目（无论选中与否）都占住这 3px，
     *   图标/文字左缘因此恒定不移位，不会因悬停 / 选中状态切换而发生 3px 位移；
     *   同时它也是悬停竖条伪元素的落位区（::before 的 left:-3px 正好压在这条透明边框上）。
     */
    border-left: 3px solid transparent;
    border-radius: 9px;
    cursor: pointer;
    white-space: nowrap;
}
/*
 * ==========================================================================
 * v1.18 左侧竖条：由「选中态常显」改为「悬停态专属」
 *   （用户要求：「把当前选中的菜单上不显示竖条，当鼠标移动到菜单的时候，
 *              在鼠标移动到的对应菜单上显示竖条」）
 *   —— 选中信息不再靠竖条表达，改由 .is-active 的品牌橙浅底 + 加粗字重独立承担（见下方）；
 *      竖条退化为「鼠标在哪一项、哪一项亮条」的即时指向反馈，与选中态互不干扰。
 *   为什么不用 border-left 画（v1.17 已定论的根因）：
 *     条目自带 border-radius: 9px，而 border 永远沿元素自身的圆角路径描边，
 *     故 3px 左边框必然随左上/左下圆角「打弯儿」成一截贴边弧形，画不出直角直条；
 *     正解是改用绝对定位伪元素独立绘制（绝对定位子元素不参与 flex 布局，零布局影响）：
 *     left:-3px        → 精确压在 3px 透明左边框上，与条目最左缘齐平；
 *     top/bottom:4px   → 两端略内缩，避免竖条在条目圆角缺口处露出直角毛边；
 *     width:3px        → 与旧的透明占位边框同宽，视觉重量不变；
 *     border-radius:0  → 直角，不带弯儿。
 *   默认 background:transparent ⇒ 任何条目（**包括当前选中项**）平时都不显示竖条；
 *   只有被 :hover 命中的那一条由下方悬停规则点亮（--jn-brand 品牌橙，与选中底色同一暖色体系）。
 *   注：折叠态（html[data-side="collapsed"]）条目仍保留该 3px 透明边框，悬停竖条照常显示。
 * ==========================================================================
 */
.jn-nav-item::before {
    content: "";
    position: absolute;
    left: -3px;
    top: 4px;
    bottom: 4px;
    width: 3px;
    background: transparent;
    border-radius: 0;
}
/* 悬停态（一）：底色沿用卡片色（深色主题下可见的轻微提亮），文字转为正文色提高可读性 */
.jn-nav-item:hover { color: var(--jn-text); background: var(--jn-card); }
/* 悬停态（二）：点亮左侧竖条 —— 与选中态解耦，只表达「鼠标在这里」 */
.jn-nav-item:hover::before { background: var(--jn-brand); }
/*
 * ==========================================================================
 * v1.17 选中态底色：由「卡片色」改为「品牌橙浅底」（用户要求「背景色显示浅橙色，好看友好美观」）
 *   旧值 var(--jn-card) 在浅色主题下等于 #ffffff，与同样纯白的侧栏面板完全融为一体，
 *   选中项几乎看不出被选中；深色主题下也只是极弱的灰差。
 *   现改为「品牌橙 12% 透明叠加」：
 *     · 四套主题自动跟随 --jn-brand（见文件顶部 :root 的品牌橙取证说明），无需新增任何色板；
 *     · 浅色主题实测合成 ≈ #FDEAE1（柔和浅橙，正对用户诉求）；深色主题为同色系暖暗调；
 *     · 文字仍是 --jn-text（浅色 #1f2430 / 深色 #e6e9ef），对浅橙底对比度远高于 4.5:1。
 *   第一行 var(--jn-card) 是兼容兜底：不支持 color-mix 的浏览器自动沿用改造前表现。
 *   v1.18 调整：不再设置 border-left-color，也不再为 .is-active 单独点亮 ::before ——
 *     竖条已完全移交悬停态（上方），选中态只由「浅橙底 + 字重 600」表达，
 *     两种状态因此可以叠加出现（选中项被悬停时：浅橙底 + 橙色竖条），语义互不覆盖。
 * ==========================================================================
 */
.jn-nav-item.is-active {
    color: var(--jn-text);
    font-weight: 600;
    background: var(--jn-card);                                       /* 兜底：不支持 color-mix 时沿用旧表现 */
    background: color-mix(in srgb, var(--jn-brand) 12%, transparent); /* 品牌橙浅底 */
}
/*
 * v1.13 侧栏分类图标位：改为定尺寸弹性盒（22×22）。
 *   原实现是「22px 宽的行内块 + 18×18 固定小图」，图片在 22px 位里还留 2px 边；
 *   现统一为「容器铺满、图片 100%×100% object-fit:contain」，与卡片图标位 .jn-card-icon.is-img
 *   同一口径（图片型图标同样不垫底色，见下方 .is-img）。emoji / 首字兜底走 font-size 垂直居中。
 */
.jn-nav-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    font-size: 17px;
    line-height: 1;
    overflow: hidden;
}
.jn-nav-icon.is-img img { width: 100%; height: 100%; object-fit: contain; }
/*
 * P2 侧栏条目计数（仅站点启用两级分类时渲染，由模板的 $hasSubs 控制）：
 *   margin-left:auto 把计数推到条目右端，长分类名不会把计数挤成两行；
 *   字号/颜色都借用既有变量（--jn-text-dim），四套主题下对比度同样达标，不新增色板。
 */
.jn-nav-count {
    margin-left: auto;
    padding-left: 8px;
    font-size: 12px;
    font-weight: 500;
    color: var(--jn-text-dim);
    opacity: .85;
}
.jn-star {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 14px;
    padding: 11px 12px;
    font-size: 14px;
    color: var(--jn-text-dim);
    border-top: 1px solid var(--jn-line);
}
.jn-star:hover { color: var(--jn-accent); }
.jn-star svg { flex: 0 0 auto; }

/*
 * P4「最近访问」独立卡片（纯前端渲染，sessionStorage 数据源；无记录时整块不显示）
 *   v1.20 拆卡：原为 .jn-side（分类导航卡）内部、以 border-top 分隔的一个**分区**，
 *     用户反馈「最近访问列表和分类导航放在不同的卡片中，这样导航是导航，
 *     最近访问是最近访问，这是两个功能，不应该放到整体，给用户迷惑」，
 *     故升级为 .jn-rail 内与 .jn-side 同级的**独立卡片**：
 *     · 去掉与 .jn-nav 的分隔上边框，改为与 .jn-side 完全同源的卡片口径
 *       （padding 12px 10px / background / border / border-radius 14px / 浅色投影同值），
 *       拆开后两卡仍是同一套视觉语言，不新造设计；两卡间距由 .jn-rail 的 gap 承担；
 *     · flex: 0 1 auto + min-height: 0：轨道（.jn-rail）限高时本卡可被压缩，
 *       压缩后由内部 .jn-recent-list 自己滚动，不会把卡片顶破；
 *     · 卡片内仍是「小标题行 + 列表」两层，小标题（.jn-side-subtitle）与条目的左内边距
 *       与分类导航卡保持一致，两卡的文字左缘对齐成一条竖线。
 */
.jn-recent {
    flex: 0 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 12px 10px;
    background: var(--jn-panel);
    border: 1px solid var(--jn-line);
    border-radius: 14px;
}
.jn-side-subtitle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 2px 10px 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--jn-text-dim);
}
.jn-recent-clear {
    padding: 2px 6px;
    font-size: 12px;
    font-weight: 400;
    font-family: inherit;
    color: var(--jn-text-dim);
    background: none;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
}
.jn-recent-clear:hover { color: var(--jn-accent); background: var(--jn-accent-soft); }
/* v1.20 拆卡：本卡已改为纵向 flex 容器，列表用 flex:1 1 auto + min-height:0 吃满剩余高度，
   在 220px 上限内自己滚动（轨道限高时先压缩本卡，再由这里接管滚动，卡片不会被顶破） */
.jn-recent-list { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-height: 0; max-height: 220px; overflow-y: auto; }
.jn-recent-list { scrollbar-width: thin; scrollbar-color: var(--jn-border) transparent; }
.jn-recent-list::-webkit-scrollbar { width: 6px; }
.jn-recent-list::-webkit-scrollbar-thumb { background: var(--jn-border); border-radius: 3px; }
.jn-recent-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    font-size: 14px;
    color: var(--jn-text-dim);
    border-radius: 9px;
}
.jn-recent-item:hover { color: var(--jn-text); background: var(--jn-card); }
/* 图标位与 .jn-nav-icon 同宽，保证「最近访问」与上方分类列表左对齐成一条竖线 */
/* v1.13：同步改为定尺寸弹性盒（22×22）+ 图片铺满，与 .jn-nav-icon 完全同口径 */
.jn-recent-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    font-size: 15px;
    line-height: 1;
    overflow: hidden;
}
.jn-recent-icon img { width: 100%; height: 100%; object-fit: contain; }
/*
 * v1.19 最近访问图标还原（用户反馈「最近访问的列表没有显示导航栏目的图标」）：
 *   修复方式是从卡片**克隆**图标节点（.jn-card-icon，含 FA 内联 SVG / 图片 / emoji / 首字四态）。
 *   克隆件自带 .jn-card-icon 的 40px 底板几何与灰底 —— 直接塞进 22px 的 .jn-recent-icon 会被裁切，
 *   图案只剩中间一小块。这里就地「去底板」：撑满 22px、去灰底、去圆角、字号对齐导航图标位，
 *   只保留 is-img / is-fa / .jn-icon-letter 三个变体原有的渲染口径（与卡片侧完全一致）。
 *   选择器权重 0,2,0 > .jn-card-icon 的 0,1,0，无需 !important。
 */
.jn-recent-icon .jn-card-icon {
    width: 100%;
    height: 100%;
    font-size: 15px;
    background: transparent;
    border-radius: 0;
}
.jn-recent-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.jn-main { flex: 1 1 auto; min-width: 0; }

/* ==========================================================================
   P5-1 分区「连续流」（详见 doc/戒导航首页显示优化设计方案.md 五、P5-1）
   - 改造前：分区互斥（.jn-section 默认 display:none，只有 .is-active 可见），
     首屏内容量 = 单个分类的条目数，下方大片留白；
   - 改造后：全部分区常驻可见、竖直排列，首屏内容量 = 全部分区条目，
     侧栏由「切换器」变为「锚点导航」，靠滚动定位浏览；
   - .is-active 保留但语义降级：不再控制显隐，只标记「当前阅读位置」。
     收藏分区维护（refreshFavoriteSection）仍读它，故 JS 必须继续同步维护该状态；
   - 分区之间的间距自 P5-4 起改由「标题自身的 margin-top」承担
     （见下方 .jn-section + .jn-section .jn-section-title，v1.11 起为 24px），首个分区不留顶部空白，首屏更紧凑。
   ========================================================================== */
/* 锚点定位留白：scrollIntoView({block:'start'}) 会贴到视口顶端，
   而顶部有 sticky 顶栏遮挡（搜索条已移除，现只有这一层）。
   数值取自 --jn-anchor：无 JS 时为 60px 兜底，有 JS 时由页尾脚本按顶栏真实高度回写，
   窄屏顶栏换行变高也能自动适配；同时兜底供原生 #hash 跳转使用 */
.jn-section { display: block; scroll-margin-top: var(--jn-anchor); }
/* 收藏分区被前端收起时用 hidden 控制显隐（display:block 是作者样式，必须显式覆盖 [hidden]） */
.jn-section[hidden] { display: none; }
/* 注意：分区之间的 32px 间距自 P5-4 起移入标题的 margin-top，
   此处**刻意不再叠加 margin** —— 否则会出现 30 + 32 = 62px 的双重留白。 */
/* 锚点定位后的短暂高亮：让用户明确「跳到了哪」。只动画颜色与下边框，不触发重排 */
.jn-section.is-flash .jn-section-title { animation: jn-flash .9s ease; }
@keyframes jn-flash {
    from { color: var(--jn-accent); border-bottom-color: var(--jn-accent); }
    /* 终态必须回到标题实际使用的描边色（--jn-line），否则动画结束瞬间会「闪」出一条 --jn-border 实线 */
    to { color: var(--jn-text); border-bottom-color: var(--jn-line); }
}

/* ==========================================================================
   P5-1 回到顶部（超长连续流的护航按钮）
   - 按钮由 JS 动态创建（不占首屏 HTML，符合「HTML 零增长」预算）；
   - 与 .jn-toast 同处 position:fixed 覆盖层，z-index 沿用 60 系（互不遮挡：此按钮在右下）；
   - 颜色全部复用既有变量，四套主题下自动一致。
   ========================================================================== */
.jn-backtop {
    position: fixed;
    right: 22px;
    bottom: 32px;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    padding: 0;
    font-family: inherit;
    font-size: 20px;
    line-height: 1;
    color: var(--jn-text-dim);
    background: var(--jn-panel);
    border: 1px solid var(--jn-border);
    border-radius: 50%;
    box-shadow: var(--jn-shadow);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity .2s, transform .2s, visibility .2s, color .16s, border-color .16s;
}
.jn-backtop.is-visible { opacity: 1; visibility: visible; transform: none; }
.jn-backtop:hover { color: var(--jn-accent); border-color: var(--jn-accent); }

/* ==========================================================================
   P5-4 分区视觉层次（详见 doc/戒导航首页显示优化设计方案.md 五、P5-4）
   - 问题：24px 标题偏「文档标题感」，与 P5-3 瘦身后的紧凑卡片（≈68/57px）不搭，层级也弱；
   - 手法：标题降为 19px/700，左侧图标改为 32×32 圆角块，右侧计数胶囊吸到最右，
     形成「图标块 + 分区名 + 计数」的三段式标题行（对齐调研手法 #2/#4 与 AIACG 的 19px/700）；
   - 间距：分区之间统一 32px 上间距，分隔线复用标题的 border-bottom，形成「区块骨架」；
     该 32px **只加在非首个分区**的标题上，首个分区不留顶部空白，首屏更紧凑；
   - 配色零新增色板：图标块底 = var(--jn-card)，文字/边框沿用既有变量，四套主题自动一致；
   - 图标块包裹用的 <span class="jn-section-icon"> 早已存在（见模板 L1320），故本包**不需要改 HTML**。
   - v1.11（用户反馈「缩小在条目上方菜单的字体大小和粗细」）：标题 19px/700 → **15px/600**，
     图标块 32×32 → 26×26（图片 20 → 16），计数胶囊 13px → 12px，标题行 gap/下边距 12 → 10px，
     非首区上间距 32 → 24px。三段式骨架（图标块 + 分区名 + 计数）结构与对齐方式完全不变，
     只等比缩小各元素尺寸，保证缩小后不「头重脚轻」。
   ========================================================================== */
.jn-section-title {
    display: flex;
    align-items: center;
    gap: 10px;                          /* v1.11：原 12px，随标题块整体收小 */
    margin: 0 0 10px;                   /* v1.11：原 12px，标题与首行卡片贴紧一档 */
    padding-bottom: 10px;               /* v1.11：原 12px，骨架线随之下降，省出一行高度 */
    font-size: 15px;                    /* v1.11：原 19px —— 用户反馈「缩小在条目上方菜单的字体大小」 */
    font-weight: 600;                   /* v1.11：原 700 —— 同步「变细」，避免小字号下仍显笨重 */
    letter-spacing: .2px;               /* v1.11：原 .3px，小字号下过度字距会显松散 */
    /* 分区骨架线改用可弱化的 --jn-line：宽屏压淡、浅色主题完全不显示（改用 24px 留白分层） */
    border-bottom: 1px solid var(--jn-line);
}
/* 非首个分区：24px 上间距在此一次性给出（相邻兄弟选择器命中所有连续分区）；
   首个分区不命中，故天然没有顶部空白。
   v1.11：原 32px，与缩小后的标题行配比更协调（标题越小、留白越要收，否则区块间显空） */
.jn-section + .jn-section .jn-section-title { margin-top: 24px; }

/* 图标块：v1.11 由 32×32 收至 26×26（图片 20→16），与 15px 标题等高对齐；
   原 32px 方块的视觉重量是 19px 标题的搭配尺寸，标题变小后必须等比收，否则「头重脚轻」 */
.jn-section-icon {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    font-size: 15px;
    line-height: 1;
    background: var(--jn-card);
    border-radius: 7px;
    overflow: hidden;
}
/* v1.13：图片型分区图标同样「铺满底板、不垫底色」，与 .jn-card-icon.is-img 同一口径 */
.jn-section-icon.is-img { background: transparent; }
.jn-section-icon.is-img img { width: 100%; height: 100%; object-fit: contain; }

/* ==========================================================================
   P2 两级分类（详见 doc/戒导航导航首页与后台改造设计方案.md 5.3）
   - 仅在「站点确实配置了二级分类」时渲染（模板 $hasSubs / $section['subs'] 控制），
     因此未配置二级的站点与改造前逐像素一致；
   - 二级切换是纯内存过滤（切 hidden 属性），零网络请求、零重排布局抖动；
   - 计数与横条全部复用既有 CSS 变量，不新增色板，四套主题一致。
   ========================================================================== */
/* 分区标题右侧的合计计数（自身条目 + 其所有二级条目）
   P5-4：margin-left:auto 把它吸到标题行最右端，与左侧 32×32 图标块形成「三段式」骨架 */
.jn-section-count {
    margin-left: auto;
    padding: 1px 8px;                   /* v1.11：原 1px 10px，随标题行收窄 */
    font-size: 12px;                    /* v1.11：原 13px */
    font-weight: 600;
    color: var(--jn-text-dim);
    background: var(--jn-accent-soft);
    border-radius: 999px;
}
/* 二级横条：横向排列，窄屏横向滚动而不换行挤压 */
.jn-subtabs {
    display: flex;
    gap: 6px;                           /* v1.11：原 8px，胶囊之间同步收紧 */
    margin: 0 0 10px;                   /* P5-4：原 -6px 0 16px；v1.11 起 12→10px，与标题下边距同节奏 */
    padding-bottom: 2px;
    overflow-x: auto;
    scrollbar-width: none;              /* Firefox 隐藏滚动条，保持观感干净 */
    scroll-snap-type: x proximity;      /* P5-5：滑停时把最近的胶囊对齐左缘（proximity 不强吸，不抢手感） */
}
.jn-subtabs::-webkit-scrollbar { display: none; }
/*
 * P5-5 二级横条左右渐隐：
 *   渐隐类由 JS 按「该侧是否还有未显示内容」动态添加（见页尾 updateSubtabFade），
 *   没有溢出时完全不遮罩 —— 否则首/末胶囊的圆角会被永久裁淡，属视觉缺陷。
 */
.jn-subtabs.has-fade-left {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px);
    mask-image: linear-gradient(to right, transparent 0, #000 20px);
}
.jn-subtabs.has-fade-right {
    -webkit-mask-image: linear-gradient(to left, transparent 0, #000 20px);
    mask-image: linear-gradient(to left, transparent 0, #000 20px);
}
.jn-subtabs.has-fade-left.has-fade-right {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
}
.jn-subtab {
    flex: 0 0 auto;
    padding: 4px 11px;                  /* v1.11：原 5px 13px，胶囊整体矮一档 */
    font-family: inherit;
    font-size: 13px;                    /* v1.11：原 14px，与收缩后的分区标题层级更协调 */
    color: var(--jn-text-dim);
    background: var(--jn-card);
    border: 1px solid var(--jn-border);
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    scroll-snap-align: start;           /* P5-5：吸附对齐到横条左缘 */
    transition: color .16s, border-color .16s, background .16s;
}
.jn-subtab:hover { color: var(--jn-text); border-color: var(--jn-accent); }
.jn-subtab.is-active {
    color: var(--jn-on-accent);
    background: var(--jn-accent);
    border-color: var(--jn-accent);
}
.jn-subtab-count { margin-left: 4px; font-size: 12px; opacity: .75; }
/*
 * 二级过滤靠 JS 给卡片加 hidden 属性实现。
 * 注意：.jn-card 的 display:flex 是作者样式，会覆盖浏览器默认的 [hidden]{display:none}，
 * 因此必须显式声明，否则 hidden 完全不生效（过滤看起来「点了没反应」）。
 */
.jn-card[hidden] { display: none; }

/* ==========================================================================
   P5-2 卡片网格列数策略（依据《戒导航首页显示优化设计方案》§五 P5-2）
   --------------------------------------------------------------------------
   1) 后台「每行列数」（模板内联的 --jn-cols）语义降级为「列数上限」，不再等于最终列数；
      P5-6 起该上限可以是**逐分区**的：分类表 columns 字段（1~8）覆盖，0 = 继承全局设置，
      由模板按「分类值 ?? 全局值」内联到各分区 .jn-grid 上，因此同一页允许出现不同列数的分区；
   2) 各断点由下面的媒体查询给出「建议列数」--jn-cols-suggest，实际列数 = min(建议值, 上限)；
      断点列数表（v1.12 起为超宽屏补出 7 / 8 两档）：
        ≥ 1920px → 8 列 ｜ 1760–1919 → 7 列 ｜ 1440–1759 → 6 列 ｜ 1280–1439 → 5 列
        1024–1279 → 4 列 ｜ 861–1023 → 3 列 ｜ 641–860 → 2 列 ｜ ≤ 640 → 1 列
      v1.12 说明（用户反馈「超宽屏还是窄屏幕要列数自适应」）：
      改造前最大档止于「≥1440 → 6」，1440 / 1920 / 2560 三种视口看到的列数完全一样，
      超宽屏多出的横向空间只能靠拉宽卡片吃掉 —— 也就是"列数并不随屏幕自适应"。
      本次为 ≥1760px / ≥1920px 各补一档（7 / 8 列），窄屏各档（5→1）沿用 v1.11 不变。
      注意宽屏档位会被后台「每行列数」上限封顶（jiedh-nav.columns）——
      该上限的默认值已同步提到 8（等于默认不限制），管理员调低即整体封顶
      （IndexController 与后台下拉均允许 1~8）。
   3) 无 JS 兜底：直接采用 --jn-cols-suggest（仅按视口自适应，忽略上限）；
      JS 可用时（见本页尾部脚本）写入 --jn-cols-eff 完成「上限封顶」，
      故此处按 eff → suggest → 8 逐级回落，保证任何情况下都有合法列数。
   4) 间距 16 → 12 → 10px（v1.11），持续为多列腾出宽度。
   ========================================================================== */
.jn-grid {
    display: grid;
    grid-template-columns: repeat(var(--jn-cols-eff, var(--jn-cols-suggest, 8)), minmax(0, 1fr));
    --jn-cols-suggest: 8;
    gap: 10px;
}
/* 断点必须按「由宽到窄」书写：同权重下后者覆盖前者 */
@media (max-width: 1919px) { .jn-grid { --jn-cols-suggest: 7; } }
@media (max-width: 1759px) { .jn-grid { --jn-cols-suggest: 6; } }
@media (max-width: 1439px) { .jn-grid { --jn-cols-suggest: 5; } }
@media (max-width: 1279px) { .jn-grid { --jn-cols-suggest: 4; } }
@media (max-width: 1023px) { .jn-grid { --jn-cols-suggest: 3; } }
@media (max-width: 860px) { .jn-grid { --jn-cols-suggest: 2; } }
@media (max-width: 640px) { .jn-grid { --jn-cols-suggest: 1; } }
/* ==========================================================================
   P5-3 卡片信息密度（标准 / 紧凑两档）—— 方案 §五 P5-3
   设计要点：
     1) 两档共用同一组 CSS 变量，卡片规则只写一份（**不写两套选择器**），
        紧凑档仅覆盖变量 + 隐藏描述，避免样式分叉后失修；
     2) 变量挂 <html>：:root 为「标准档」（默认值即方案表格中的标准档数值），
        html[data-density="compact"] 覆盖为紧凑档 —— 属性选择器权重（0,1,1）
        高于 :root 的（0,1,0），故无需 !important，也不受书写顺序影响；
     3) data-density 由页首防闪烁脚本按 localStorage['jn_density'] 在绘制前写入，
        因此刷新后不会出现「先标准档、再跳紧凑档」的观感抖动；服务端不参与、不写 URL；
     4) 紧凑档隐藏描述（对齐调研共性「描述默认弱化或可关」），
        但 .jn-card-desc 仍保留在 DOM 中（display:none 不影响 textContent），
        这样在两档之间来回切换时纯靠 CSS 生效，无需重排 DOM、也不丢描述文本；
     5) 边框与层次维持原口径：1px solid var(--jn-line) + hover 阴影加深，
        不引入毛玻璃卡片、不加常驻投影。
   ========================================================================== */
:root {
    --jn-card-pad: 11px 13px;          /* 卡片内边距（v1.11：原 12px 14px，配合更窄的列宽再各收 1px） */
    --jn-card-gap: 11px;               /* 图标与文字间距（v1.11：原 12px） */
    --jn-card-radius: 13px;            /* 卡片圆角（v1.11：原 14px，随卡片变小同步微收，避免显得过圆） */
    --jn-card-icon: 40px;              /* 图标底板边长（v1.13：原 36px；v1.11 曾从 40px 缩到 36px，本次按用户反馈放大一档） */
    --jn-card-icon-radius: 10px;       /* 图标底板圆角（v1.13：原 9px，随底板同步放大） */
    --jn-card-icon-font: 22px;         /* emoji / 首字兜底的字号（v1.13：原 20px） */
    --jn-card-title: 14px;             /* 站点名字号（v1.11：原 15px，窄列下减少折行） */
    --jn-card-desc: 12px;              /* 描述字号（不变，仍为单行省略） */
    --jn-card-lift: -2px;              /* hover 上移量（不变） */
    --jn-card-desc-display: block;     /* 描述显示与否：标准档显示 1 行 */
}
html[data-density="compact"] {
    --jn-card-pad: 9px 11px;           /* v1.11：原 10px 12px */
    --jn-card-gap: 9px;                /* v1.11：原 10px */
    --jn-card-radius: 11px;            /* v1.11：原 12px */
    --jn-card-icon: 36px;              /* v1.13：原 32px */
    --jn-card-icon-radius: 9px;        /* v1.13：原 9px（紧凑档保持，36px 底板上 9px 圆角比例已合适） */
    --jn-card-icon-font: 20px;         /* v1.13：原 18px */
    --jn-card-title: 13px;             /* v1.11：原 14px */
    --jn-card-desc: 12px;
    --jn-card-lift: -1px;
    --jn-card-desc-display: none;      /* 紧凑档隐藏描述，把纵向空间让给条目数 */
}
/*
 * P5-3 密度开关（原在首屏搜索条内，搜索条移除后已迁入顶栏动作区 .jn-actions）
 *   · 图标是内联 SVG symbol —— 遵守「首页零外部静态资源请求」守正红线①；
 *     两个图标按 <html data-density> 互斥显示，读屏用户由 aria-pressed 获知当前档位；
 *   · 这枚轻量控件现驻顶栏 .jn-actions，故尺寸 / 圆角 / 描边口径
 *     与同排的 .jn-dd-btn / .jn-top-link 完全对齐（34px 高、10px 圆角、默认透明描边、
 *     hover 才显出卡片底与描边），避免成为顶栏里「尺寸或风格不一致」的异物；
 *   · 保持正方形（width 与 height 同为 34px）：它是纯图标按钮，文字基线对齐无意义。
 *     注：v1.22 顶栏 68 → 52px，本按钮与同排按钮一并 38 → 34px。
 */
.jn-density {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;                        /* v1.22：38 → 34px */
    height: 34px;
    padding: 0;
    color: var(--jn-text-dim);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    transition: background .18s, color .18s, border-color .18s;
}
.jn-density:hover { color: var(--jn-accent); border-color: var(--jn-border); background: var(--jn-card); }
.jn-density .jn-ic { display: none; }
html[data-density="standard"] .jn-density .jn-ic[data-icon="standard"],
html[data-density="compact"] .jn-density .jn-ic[data-icon="compact"] { display: block; }

.jn-card {
    display: flex;
    align-items: center;
    gap: var(--jn-card-gap);
    padding: var(--jn-card-pad);
    /* v1.11：星标已改为名称行内的行内元素（见 .jn-card-fav），卡片不再需要
       position:relative 作为星标的绝对定位上下文，故当时移除了该属性。
       P2-4：现补回 —— 卡片右上角的分享图标（.jn-card-share）需以卡片为绝对定位
       上下文才能「常驻在角上且不占文档流」。position:relative 不参与 flex 布局
       计算（卡片宽高 / 换行 / 间距全不变），故本次补回对既有布局零影响。 */
    position: relative;
    background: var(--jn-card);
    /* 卡片轮廓改用可弱化的 --jn-line；hover 时仍由 border-color 转主色给出明确反馈 */
    border: 1px solid var(--jn-line);
    border-radius: var(--jn-card-radius);
    transition: transform .16s, border-color .16s, box-shadow .16s, background .16s;
}
/* 浅色主题：卡片以投影代替描边（描边在浅色下为 transparent），悬停再抬高一档保证反馈可感
   （精确匹配 data-theme="light"：light-hc 保留 #000 实色描边） */
html[data-theme="light"] .jn-card { box-shadow: var(--jn-shadow); }
html[data-theme="light"] .jn-card:hover { box-shadow: 0 10px 26px rgba(16, 24, 40, .14); }
.jn-card:hover {
    transform: translateY(var(--jn-card-lift));
    background: var(--jn-card-hover);
    border-color: var(--jn-accent);
    box-shadow: var(--jn-shadow);
}
/*
 * v1.13 卡片图标位（针对用户反馈「别人网站的图标很清晰，我们的图标很不显眼」）
 *   1) 图片型图标**铺满**底板：img 撑满 100%×100%（object-fit:contain）。
 *      裁剪上传的都是 1:1 方图，contain 在方底板里即等于满铺；万一管理员粘贴了非方图的外部
 *      favicon，contain 也能完整显示、不裁切。v1.11 之前是「36px 底板 + 24px 图」，
 *      四边留白把图案压得很小 —— 这是「图标发虚、图案只占中间一小块」的直接原因；
 *   2) 图片型图标**不垫底色**（.is-img 把底板底色去掉）：灰底既压掉彩色图标的饱和度，
 *      又会从 PNG 的透明像素里透出来，观感像「图挂了」；底板底色只留给 emoji / 首字兜底当落点；
 *   3) 无图标时不再留空灰块：模板侧用「站点名首字」兜底（见文件顶部 $renderIcon）。
 *   尺寸口径：标准档 40px、紧凑档 36px（v1.11 为 36 / 32）。
 */
.jn-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: var(--jn-card-icon);
    height: var(--jn-card-icon);
    font-size: var(--jn-card-icon-font);
    background: rgba(127, 127, 127, .12);
    border-radius: var(--jn-card-icon-radius);
    overflow: hidden;
}
/* 图片型：铺满底板，且不垫底色（灰底会从透明像素里透出、压掉图标饱和度） */
.jn-card-icon.is-img { background: transparent; }
.jn-card-icon.is-img img { width: 100%; height: 100%; object-fit: contain; }
/*
 * v1.16 FontAwesome 类名图标（$renderIcon ② 分支输出，字形来自 $faInlineSvg 内联的 FA 原始 SVG）：
 *   三处图标位（.jn-nav-icon / .jn-section-icon / .jn-card-icon）共用同一口径 —— SVG 铺满图标位，
 *   与 .is-img 的 img 一致，保证「图片图标 / 字体图标」在页面上观感统一。
 *   颜色：FA 原始 SVG 的 <path> 是 fill="currentColor"，因此直接吃外层图标位的 color
 *        （由 $renderIcon 写行内 style="color:#rrggbb"；未配色时继承各图标位的文字色）。
 *   与 .jn-ic 隔离：.jn-ic 是描边风格精灵（fill:none; stroke:currentColor; stroke-width:2），
 *        本类必须显式 stroke:none，否则描边会被 FA 的填充路径继承出来、图标发胖串味。
 */
.is-fa svg { display: block; width: 100%; height: 100%; }
.is-fa svg path { fill: currentColor; stroke: none; }
/*
 * 首字兜底（无图标 / 图标字段是后台可填但前台无法渲染的 FontAwesome 类名时）：
 *   font-size 由外层图标位的 --jn-card-icon-font 决定，本规则只收敛颜色与字重，
 *   保证它看起来像「设计过的占位」而不是漏渲染的乱码。
 * v1.15（用户要求）：「没有图标显示字体的话，字体颜色显示为橙色，也就是网站 logo 的颜色」
 *   ⇒ 颜色由 --jn-text-dim（灰）改为 --jn-brand（品牌橙，取自 Logo 图片取样，
 *     四套主题各自标定对比度，见文件顶部 :root 的 --jn-brand 注释）。
 */
.jn-icon-letter { font-weight: 600; color: var(--jn-brand); }
.jn-card-body { min-width: 0; }
/*
 * v1.11 名称行（.jn-card-head）→ v1.12 改为「名称 + 星标 + 标签」横向排列。
 *   - 由 inline-block 串联改为 flex 行：星标是 24×24 的固定方块（见 .jn-card-fav），
 *     与文字的对齐靠 align-items:center 完成，比 vertical-align 更稳、也更抗字号变化；
 *   - gap 6px 统一承担「名称↔星标」「星标↔标签」的间距，
 *     故 .jn-card-fav / .jn-card-flag 自带的 margin 已归零，避免两处间距叠加；
 *   - min-width:0 + 名称自身 min-width:0 + nowrap/ellipsis：名称过长时收缩显示省略号，
 *     既不换行把卡片撑高（同排卡片高度不齐会显得"布局被打乱"），
 *     也不把「暂不可达」标签顶出卡片。
 *   - 描述 .jn-card-desc 不在本行内，仍是名称行下方的独立 block（见下一条规则）。
 *
 * v1.12 用户反馈与结论：「星星放到条目网站标题前面打乱了条目的布局，现在改到网站名称的后边」
 *   —— 星标不再占据名称行最左的首位（首位留给名称，视觉起点与所有卡片对齐），
 *      改为紧跟在名称之后；DOM 顺序同步调整，读屏朗读顺序仍是「名称 → 收藏按钮 → 标签」。
 */
.jn-card-head {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
.jn-card-name {
    font-size: var(--jn-card-title);
    font-weight: 600;
    min-width: 0;
    /* v1.12：名称最多 7 个字符（后台配置超长时由 PHP 截断为「前 5 + ..」，见渲染处注释），
       理论上不会溢出；但超宽屏 8 列时单卡片内容宽度仅约 110px，5 个全角字 + 星标仍可能贴边，
       故再上一道 nowrap + 省略号兜底：宁可少显示半个字，也不要折行撑高卡片。 */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * P1 存活检测：仅对 dead 的条目做最小提示（见方案 4.6）。
 *   - .is-dead  —— 整卡不透明度降至 .6；卡片仍可点击（用户可能知道该站已换域名，
 *                  禁用链接反而制造困惑），hover 的位移/描边反馈保持不变。
 *   - .jn-card-flag —— 极小中性标签，只借用既有 CSS 变量（--jn-text-dim / --jn-line），
 *                  不新增色板；四套主题下 --jn-text-dim 对卡片底色的对比度均 ≥ 4.5:1。
 * blocked / null 不产生任何样式与标记。
 */
.jn-card.is-dead { opacity: .6; }
.jn-card-flag {
    display: inline-block;
    /* v1.11：与名称的间距改由 .jn-card-head 的 gap 统一给出，此处归零避免两处叠加 */
    margin-left: 0;
    flex: 0 0 auto;                    /* 名称收缩时标签不被压扁（white-space:nowrap 已保证不折行） */
    padding: 0 5px;
    font-size: 11px;
    font-weight: 400;
    line-height: 16px;
    vertical-align: 1px;
    white-space: nowrap;
    color: var(--jn-text-dim);
    /* 装饰性标签描边：随主题弱化；浅色主题下退化为纯文字标签（文字对白底仍 ≥4.5:1） */
    border: 1px solid var(--jn-line);
    border-radius: 5px;
}
/*
 * 描述：单行省略（等价于 line-clamp:1，且不依赖 -webkit- 前缀，兼容性更好）。
 *   display 由密度变量控制（标准档 block / 紧凑档 none），字号由 --jn-card-desc 控制。
 *   注意：紧凑档下元素仍在 DOM 中（仅 display:none），切回标准档即原样显示。
 */
.jn-card-desc {
    display: var(--jn-card-desc-display);
    margin-top: 3px;
    font-size: var(--jn-card-desc);
    color: var(--jn-text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.jn-empty {
    padding: 60px 20px;
    text-align: center;
    color: var(--jn-text-dim);
    background: var(--jn-panel);
    /* 空状态容器描边属装饰性，随主题弱化；浅色主题下由「白底面板 vs 浅灰页面底」完成分层 */
    border: 1px dashed var(--jn-line);
    border-radius: 14px;
}

/*
 * P4 卡片收藏星标（方案 7.4）
 *   演进：卡片右上角绝对定位 →（v1.11）名称之前的行内星标 →（v1.12）**名称之后**的行内星标
 *
 *   一、为什么从绝对定位改为行内（v1.11 的结论，仍然成立）
 *     1) 绝对定位在右上角时，星标自成一块 24×24 的方块，在本就紧凑的卡片里非常显眼；
 *     2) 更关键的是它**悬浮在内容之上**：卡片右侧必须一直为它让出一条空白，
 *        等于白吃一截卡片宽度——这正是「条目显得太宽、空间利用率低」的成因之一；
 *     3) 移入 .jn-card-head 文档流后，这段右侧留白彻底归还给内容，卡片可以更窄。
 *
 *   二、为什么又从「名称之前」挪到「名称之后」（v1.12）
 *     用户反馈「星星放到条目网站标题前面打乱了条目的布局，现在改到网站名称的后边」——
 *     星标占着名称行首位时，每张卡片的**名称视觉起点都被一个 24px 方块顶开**，
 *     名称长一点就与星标挤在一起、短一点又留下参差的空隙，整行看起来"没有对齐"。
 *     改到名称之后：名称回到行的最左，所有卡片名称起点天然对齐，星标退居其后作为状态标记。
 *     DOM 顺序随之调整为 名称 → 星标 → 「暂不可达」标签，与视觉顺序、读屏朗读顺序三者一致。
 *
 *   三、随之而来的两个必然变化（是位置变更的推论，不是额外加戏）
 *     · **常显**：星标既然在流内就占着位置，若仍未 hover 时 opacity:0，
 *       名称右侧会被一个看不见的空位占住，看起来像排版错位。
 *       故取消「隐藏 → hover 才出现」，改为常显（--jn-text-dim 对卡片底色 ≥4.5:1）。
 *       正向副作用：触屏用户不必再靠 @media (hover: none) 特判也能看见收藏入口，
 *       该媒体查询已随之删除；同时也不再需要 pointer-events 开关
 *       （原先是「不可见时禁止点击」，避免误触看不见的按钮）。
 *     · **去掉底色方块**：行内星标不再需要实心底色去遮住底下的文字，
 *       改为纯图标 + 透明底；hover 时才浮出 --jn-accent-soft 的圆角底作为反馈。
 *
 *   四、尺寸 24×24 保持不变
 *     这是 WCAG 2.5.8(AA) 的最小目标尺寸，是触屏可点性的下限（上一轮已从 30×30 收到此值）。
 *     视觉图标仍是 14px，点击区域由 24×24 的盒子撑满：既不臃肿，也不牺牲可点性。
 *
 *   五、负外边距（v1.12 按新位置重算）
 *     图标 14px 居中放在 24×24 的盒子里，左右各留 5px 内边距；盒子与名称之间还有 6px 的
 *     flex gap ⇒ 名称文字与星标图标的**视觉**间距本是 11px，偏松。
 *     故只保留左侧 -3px 的负外边距把它收到约 8px（与 P5-3 的密度口径一致）。
 *     右侧不再给负值：星标现在可能是一行中的最后一个元素，右侧负外边距会让盒子外沿
 *     越过卡片内容边界（4px），徒增溢出风险，而收益为零。
 *
 *   六、配色只用既有变量
 *     灰色待收藏（--jn-text-dim）、主色已收藏（--jn-accent + --jn-accent-soft）。
 */
.jn-card-fav {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;                    /* 名称收缩显示省略号时星标不被压扁 */
    width: 24px;
    height: 24px;
    /* 行内星标（名称之后）：仅左侧内收 3px 收紧「名称↔星标」观感间距，右侧不外溢（见上方「五」） */
    margin: 0 0 0 -3px;
    padding: 0;
    color: var(--jn-text-dim);
    background: transparent;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
    transition: color .16s, background .16s;
}
/* 卡片 hover：整卡已是可点区域，星标同步转主色，提示「这里也能点」 */
.jn-card:hover .jn-card-fav { color: var(--jn-accent); }
.jn-card-fav:hover { color: var(--jn-accent); background: var(--jn-accent-soft); }
/* 已收藏：主色实心星（path 由描边改为填充），与未收藏的空心星形成一眼可辨的差异 */
.jn-card-fav.is-on { color: var(--jn-accent); }
.jn-card-fav.is-on svg path { fill: currentColor; }
.jn-card-fav svg { width: 14px; height: 14px; pointer-events: none; }
/* 键盘可达（方案 8.2：焦点必须可见）——星标与最近访问「清空」按钮共用同一焦点环 */
.jn-card-fav:focus-visible,
.jn-recent-clear:focus-visible { outline: 2px solid var(--jn-accent); outline-offset: 2px; }

/*
 * P4 轻提示（收藏失败回滚时的一句反馈）
 *   固定底部居中、2.6 秒后自动消失；role="status" + aria-live="polite" 让读屏也能听到，
 *   不打断当前朗读、不抢焦点，也不阻塞任何操作（纯提示）。
 */
.jn-toast {
    position: fixed;
    left: 50%;
    bottom: 32px;
    z-index: 60;
    max-width: calc(100vw - 40px);
    padding: 10px 16px;
    font-size: 14px;
    color: var(--jn-text);
    background: var(--jn-panel);
    border: 1px solid var(--jn-border);
    border-radius: 10px;
    box-shadow: var(--jn-shadow);
    transform: translateX(-50%);
}

/*
 * P2-5 `?ref=share` 游客轻引导（设计依据：《分享功能设计方案.md》§6.9.3）
 *   场景：访客从分享链接（/nav?ref=share）进来且未登录 ⇒ 底部给一条**可关闭的轻提示**，
 *         引导注册（「你在朋友分享里看到这条内容 · 注册后可以一起讨论」+「注册」+「×」）。
 *
 * 逐条对照 §6.9.3 的四项硬约束：
 *   ①「只显示一次」—— 由内联 JS 用 sessionStorage('jiedh-share.ref-guide-seen') 把关；
 *   ②「不遮挡内容」—— fixed 固定在底部居中，**非遮罩、非弹窗**，不阻断阅读（与 .jn-toast 同层 z-index:60）；
 *   ③「不绕过 registration-guard」—— 「注册」按钮复用页面既有的 openRegister()，
 *      与顶栏「注册」入口走的是同一个弹窗、同一套字段、同一个 POST {baseUrl}/register，
 *      不携带任何豁免参数，风控口径完全不变；
 *   ④「与主站一致」—— 设计原文建议用核心 --control-bg / --muted-color，但首页是
 *      IndexController 直出的独立 HTML，**不加载 forum.css**（首屏「零外部静态资源」红线），
 *      那些 token 在首页根本不存在。故此处沿用首页既有的 --jn-* 主题 token ——
 *      五套主题（L263-388）自动跟随，视觉上与 .jn-toast / 卡片完全同源，这才是首页语境下的「一致」。
 *
 * ★ hidden 显隐必须显式覆盖：.jn-ref-guide 的 display:flex 是作者样式，会盖掉 UA 对
 *   [hidden] 的 display:none（同 .jn-card / .jn-share-btn 的既有踩坑注释）。
 * ★ 与 .jn-backtop（right:22px; bottom:32px; 42×42）的碰撞规避：
 *   · 宽度上限 min(560px, calc(100vw - 160px)) ⇒ 两侧各留 ≥80px，
 *     >860px 时右缘永远够不到 .jn-backtop 的热区；
 *   · ≤860px 断点内把 bottom 抬到 88px，整体让开右下角的回到顶部按钮。
 */
.jn-ref-guide {
    position: fixed;
    left: 50%;
    bottom: 32px;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: min(560px, calc(100vw - 160px));
    padding: 10px 12px 10px 16px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--jn-text);
    background: var(--jn-panel);
    border: 1px solid var(--jn-border);
    border-radius: 10px;
    box-shadow: var(--jn-shadow);
    transform: translateX(-50%);
}
.jn-ref-guide[hidden] { display: none; }
/* 文案区允许收缩换行，但不挤压右侧两个按钮 */
.jn-ref-guide-text { flex: 1 1 auto; min-width: 0; }
/* 「注册」：品牌色实心小按钮（比 .jn-submit 轻一档，因为它是「提示条里的行动」而非表单主按钮） */
.jn-ref-guide-cta {
    flex: 0 0 auto;
    padding: 5px 12px;
    font-size: 13px;
    font-family: inherit;
    color: var(--jn-on-accent);
    background: var(--jn-brand);
    border: 0;
    border-radius: 8px;
    cursor: pointer;
}
.jn-ref-guide-cta:hover { filter: brightness(1.06); }
.jn-ref-guide-cta:focus-visible { outline: 2px solid var(--jn-accent); outline-offset: 2px; }
/* 「×」：沿用 .jn-recent-clear 的小号文字按钮范式（弱化层级、悬停给弱底） */
.jn-ref-guide-close {
    flex: 0 0 auto;
    padding: 2px 6px;
    font-size: 16px;
    line-height: 1;
    font-family: inherit;
    color: var(--jn-text-dim);
    background: none;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
}
.jn-ref-guide-close:hover { color: var(--jn-accent); background: var(--jn-accent-soft); }
.jn-ref-guide-close:focus-visible { outline: 2px solid var(--jn-accent); outline-offset: 2px; }

/* ==========================================================================
   移动端：侧栏降级为顶部「分类条」
   - 列数已统一由上方 P5-2 断点表管理，此处不再重复声明 .jn-grid
   ========================================================================== */
@media (max-width: 860px) {
    /*
     * 顶栏窄屏换行策略：品牌区与主导航同属「左栏」，优先留在第一行（视线起点不被打散）；
     * 动作区放不下时整块换到下一行并右对齐（.jn-actions 的 margin-left:auto 换行后依然生效，
     * 故不再需要旧的 flex:1 拉伸写法）。gap / padding 同步收紧，避免两行之间过空。
     * 注意 .jn-actions 的 flex 从 0 0 auto 改为 0 1 auto：动作区（用户菜单 + 主题 + 密度）
     * 在 360~390px 上接近满宽，允许收缩可避免它把顶栏顶出视口。
     */
    .jn-header { height: auto; flex-wrap: wrap; padding: 10px 14px; gap: 10px; }
    .jn-brand { flex: 0 0 auto; }
    .jn-nav-main { margin-left: 0; }
    .jn-nav-main-link { padding: 0 12px; }
    .jn-actions { flex: 0 1 auto; flex-wrap: wrap; justify-content: flex-end; }
    .jn-layout { flex-direction: column; padding: 14px; }
    /* P2-5 游客轻引导：窄屏抬到 bottom:88px 让开右下角 .jn-backtop（42×42 @ bottom:32px），
       宽度改为「两侧各留 16px」，长文案换行后仍不会顶出视口 */
    .jn-ref-guide { bottom: 88px; max-width: calc(100vw - 32px); }
    /* v1.20 拆卡：sticky / 限高 / 列宽都已上移到 .jn-rail，窄屏解吸附必须同步作用在轨道上；
       两卡在窄屏依旧上下分开（轨道内 gap 12px 继续生效），各自满宽 */
    .jn-rail { position: static; flex: 0 0 auto; max-height: none; width: 100%; }
    .jn-side { flex: 0 0 auto; width: 100%; }
    /*
     * v1.11 修复窄屏内容区「缩成内容宽」的缺陷（用户要求「空间利用率更高」）：
     *   - .jn-layout 是 `align-items: flex-start` 的纵向 flex 容器，交叉轴（横向）默认不吃拉伸，
     *     .jn-rail 因自带 width:100% 而满宽，但 .jn-main 没有宽度声明 → 被压成「内容宽」
     *     （实测 390px 视口下仅 194px，右侧白白空掉约 150px，卡片也被迫变窄）；
     *   - 补一句 width:100% 即让主内容区吃满整行，卡片随之变宽、单列排版不再局促。
     */
    .jn-main { width: 100%; }
    /*
     * 窄屏分类条：改为「自动换行」而不是横向滚动（用户反馈「移动端导航出界」）。
     *   - 原实现是 flex-direction:row + overflow-x:auto + 条目 white-space:nowrap，
     *     分类多于一屏时末尾项被裁在视口外（如截图中「戒色」只露出半个字），
     *     必须横向滑动才看得到，而横条没有任何滚动提示，用户会直接判定「坏了」；
     *   - 换行后所有分类一次性可见、无需滑动；条目自身仍保持 nowrap，
     *     避免长分类名被折成两行而破坏横条节奏；
     *   - gap 由桌面纵向的 2px 调整为 6px 4px：换行后同一行内相邻两项会贴在一起，
     *     行距也要略大于列距才不显得拥挤。
     */
    .jn-nav { flex-direction: row; flex-wrap: wrap; overflow-x: visible; gap: 6px 4px; }
    .jn-nav-item { border-left: 0; border-bottom: 3px solid transparent; border-radius: 9px 9px 0 0; }
    /*
     * v1.17 / v1.18：窄屏横条下「竖条」语义变为「底条」。
     *   · 桌面竖条伪元素必须在此隐藏 —— 本档 border-left 已置 0，
     *     若继续显示，left:-3px 会让它跑到条目左外侧；
     *   · 底条仍走 border-bottom 绘制：本档 border-radius 的下半角为 0，
     *     底边天生就是直角直条，无需伪元素，天然满足「不带弯儿」；
     *   · v1.18 同步把「常显」改为「悬停专属」：与桌面档口径一致 ——
     *     当前选中项不显示底条（选中信息由上游 .is-active 的浅橙底 + 字重承担），
     *     鼠标移到哪一项，哪一项才亮底条（--jn-brand 品牌橙）。
     */
    .jn-nav-item::before { display: none; }
    .jn-nav-item:hover { border-bottom-color: var(--jn-brand); }
    .jn-star { border-top: 0; }
    /*
     * P5-5：折叠按钮只在桌面侧栏有意义 —— 窄屏侧栏本身已是「分类条」，
     * 折叠会把分类条压成 56px 高、语义不成立，故直接隐藏。
     * 另：方案 P5-5 原计划让本横条 sticky top:0，实测窄屏下 .jn-rail 内
     * 「分类导航卡 + 最近访问卡（+ Star）」纵排，合计高度约 150~380px（取决于最近访问条数），
     * 常驻吸附会吃掉 551px 视口中 27%~69% 的可视高度，浏览反而更差；
     * （v1.20 拆卡后两卡间距 +12px，结论不变）
     * 故这里不吸附，改由「移动端常显的回到顶部按钮」承担回顶动线（见 updateBackTop）。
     */
    .jn-side-toggle { display: none; }
    /* v1.18：窄屏无折叠按钮（见上行隐藏），控制行只剩「分类导航」标题 → 不需要桌面档的分隔线 */
    .jn-side-head { border-bottom: 0; }
}

/*
 * ==========================================================================
 * v1.25 首页专属页脚（百度式单行链接 · 无底板）
 * --------------------------------------------------------------------------
 * 背景：后台「外观 → 自定义页脚」写入的设置只经核心
 *   vendor/flarum/core/views/frontend/forum.blade.php L51（{!! $forum['footerHtml'] !!}）输出，
 *   而首页是 IndexController 直出的独立 HTML（不走 blade 模板），故论坛页脚在首页不生效，
 *   首页页脚必须在本模板内自研（调研结论见 doc/戒导航首页显示优化设计方案.md §十一）。
 *
 * 形态来源：参照百度首页页脚 —— **一行纯链接**，短文本之间以空白分隔，
 *   无卡片底板、无栏标题、无一句描述性文案；条目文案只用站点真实入口的固有名称，不自拟宣传语。
 *   由 v1.24「单行居中 + 一句简介」收敛而来（更早的 v1.23 为四栏多栏版）。
 *
 * 设计口径 —— 仍全部复用页面既有视觉语言，不新造色板：
 *   · 文字色 = --jn-text-dim（弱化层级，与 .jn-side-subtitle 的次要文字同源）；
 *   · 链接悬停 = color: var(--jn-accent) + background: var(--jn-accent-soft)，
 *     对齐全站 8+ 处既有悬停写法（.jn-nav-main-link:hover / .jn-recent-item:hover 等）；
 *     此处刻意**不用** --jn-brand —— 品牌橙在本站只承担「首字兜底图标」与窄屏导航底条。
 *
 * 分隔方式：改用 flex 的 gap（纯空白），不再用「·」——
 *   一是贴近百度页脚（其条目间即纯空白），二是空白由布局产生而非内容产生，
 *   条件渲染删项（未配置 GitHub 仓库 / 无管理权限）时不会残留悬空分隔符。
 *
 * 约束：零外部静态资源（无 @import、无外链图标字体），页脚无图标需求（纯文字链接）。
 * ==========================================================================
 */
.jn-footer {
    /* 与 .jn-layout 同最大宽度、同左右内边距：保证页脚与内容区左缘对齐，是「同一容器语言」而非另起一套 */
    max-width: 1800px;
    margin: 0 auto;
    /* 顶部仅留 6px：页脚是正文的收尾说明，不需要卡片那种 20px 级留白 */
    padding: 6px 18px 28px;
}
.jn-footer-line {
    display: flex;
    flex-wrap: wrap;
    /* 主轴/交叉轴双居中：>860px 收成一行居中；宽度不足时自动折行，且每一行仍保持居中 */
    justify-content: center;
    align-items: center;
    /* 行间距 4px、列间距 10px：列间距再叠加链接自身 6px 内边距，视觉间隔约 22px，疏密贴近百度页脚 */
    gap: 4px 10px;
    margin: 0;
    font-size: 13px;
    line-height: 1.8;
    color: var(--jn-text-dim);
    text-align: center;
}
/* 链接（页脚唯一可点元素）：内边距撑出可点区域，悬停用全站统一口径 */
.jn-footer-link {
    padding: 2px 6px;
    border-radius: 6px;
    color: var(--jn-text-dim);
    transition: color .18s, background-color .18s;
}
.jn-footer-link:hover,
.jn-footer-link:focus-visible { color: var(--jn-accent); background: var(--jn-accent-soft); }
/*
 * 偈颂行（v1.30 新增）：位于链接行**上方**，是页脚的收尾祝祷文字，纯展示、不可点。
 * 口径：
 *   · 文字与 about 页 .jn-about-verse 完全相同（同一首偈颂），呈现方式也沿用
 *     「居中 + 取消首行缩进」；但此处落在页脚而非文章正文，故**不加分隔线**。
 *   · 配色取 --jn-text-dim，与页脚链接行同源（弱化层级，不抢「关于本站」等入口的注意力）；
 *     字号/行高与 .jn-footer-line 同档（13px / 2），使偈颂与链接行读起来是**同一块页脚**。
 *   · 文案为固定偈颂，不随后台设置变化，故直接写在模板内（**未新增 $data 键**）。
 *   · 仍**零外部静态资源**：纯文本，无图标、无字体、无背景图。
 */
.jn-footer-verse {
    /* 下外边距 10px：把偈颂与下方链接行拉开一档，避免两行粘连 */
    margin: 0 0 10px;
    /* 居中：窄屏自动折行后，每一行仍居中（与 .jn-footer-line 同口径） */
    text-align: center;
    font-size: 13px;
    line-height: 2;
    /* 字距略放，偈颂读起来更舒缓（about 页同款 .5px，此处稍加至 .6px） */
    letter-spacing: .6px;
    color: var(--jn-text-dim);
}
/* 窄屏：内边距同步 .jn-layout 的 14px；折行由 flex-wrap 自然完成，无需改写其它规则 */
@media (max-width: 860px) {
    .jn-footer { padding: 6px 14px 24px; }
}

/* ==========================================================================
 * P2-4 首页精简版分享（样式块）
 * 口径：《分享功能设计方案.md》§6.10.1「首页做/不做表」、§6.10.2「实现要点」、
 *       §19.3「卡片浮层含二维码格」、§20.1 缺口 1（默认常驻）与缺口 2（浮层分端）。
 *
 * 三条硬约束（本段全部遵守）：
 *   1) 只用首页既有 CSS 变量（--jn-*），不新增主题变量 ⇒ 四套主题 / 两档密度
 *      自动跟随，无需为分享单独配色；
 *   2) 不新增外部静态资源、不引图标字体 ⇒ 图标一律 <use href="#jn-i-*">；
 *   3) 布局零变化 ⇒ 卡片分享图标用绝对定位「浮」在角上（P2-4 已给 .jn-card
 *      补回 position:relative 作上下文），不动卡片的 flex/尺寸/间距。
 *
 * z-index 选择（底部图层全局清单：header 20 / 下拉菜单 40 / 本层 50 / toast 60 / 登录弹窗 100）：
 *   取 50 —— 必须高于 header(20) 才能盖住顶栏（站点级分享按钮就在顶栏，
 *   浮层不能躲在它下面）；又必须低于 toast(60) 与登录弹窗(100)，
 *   以便「复制成功」提示与登录弹窗仍能正常压住它。
 * ========================================================================== */

/* ---- 站点级分享按钮（复用顶栏 .jn-top-link 基样式，仅补展开态与窄屏收字）---- */
.jn-share-trigger { position: relative; }
/* 展开态：与 .jn-top-link:hover 同款高亮，让「浮层由谁打开」一眼可见 */
.jn-share-trigger[aria-expanded="true"] {
    color: var(--jn-text);
    background: var(--jn-card);
}
/* 窄屏：顶栏空间紧张（.jn-header 在 ≤860px 会换行），只留图标、收回文字 */
@media (max-width: 860px) {
    .jn-share-trigger > span { display: none; }
    .jn-share-trigger { padding: 0 9px; }
}

/* ---- 卡片分享图标 ----
 * §20.1 缺口 1 定稿：「默认常驻 + 仅确有 hover 能力的指针设备才隐藏」。
 * 理由（原文 ★ 非对称风险论证）：触屏媒体查询有失败面，
 *   「默认隐藏」一旦判错 = 入口彻底消失（用户找不到分享）；
 *   「默认常驻」一旦判错 = 桌面上多一个图标（仅观感问题）。故必须选安全侧。
 * 触控热区 ≥44×44px（不足则 iOS/Android 会误触到卡片本身的跳转）。
 * 不占文档流：绝对定位在右上角，卡片尺寸/换行/间距全部不变。 */
.jn-card-share {
    position: absolute;
    top: 0;
    right: 0;
    /* 44×44 触控热区；图标本身仍按 16px 显示，靠 flex 对齐贴到角上 */
    width: 44px;
    height: 44px;
    display: inline-flex;
    /* 图标贴右上角，热区向左下扩展 ⇒ 视觉上是「角上一个小图标」，
       命中区却是 44×44，兼顾观感与触控（§20.1 缺口 1 第③条） */
    align-items: flex-start;
    justify-content: flex-end;
    padding: 7px;
    /* 右上角随卡片圆角内收，避免小图标浮在圆角外侧显得突兀 */
    border-radius: 0 var(--jn-card-radius) 0 6px;
    color: var(--jn-text-dim);
    /* 常驻可见（触屏安全侧）：不做默认透明 */
    opacity: 1;
    cursor: pointer;
    /* 热区是「浮」在卡片上的，不参与布局；背景默认透明，hover 才给弱底 */
    background: transparent;
    transition: opacity .16s, background .16s, color .16s;
    /* 保证浮在卡片内其它内容之上（星标 .jn-card-fav 等同层元素） */
    z-index: 1;
}
.jn-card-share .jn-ic { width: 16px; height: 16px; }
.jn-card-share:hover { color: var(--jn-text); background: var(--jn-item-hover); }
.jn-card-share:focus-visible {
    /* 内缩 2px：焦点环落在热区内部，不被卡片圆角裁掉 */
    outline: 2px solid var(--jn-accent);
    outline-offset: -2px;
}
/* 仅当设备「确有 hover 能力 且 主指针为精确指针」（桌面鼠标）才收起图标，
   悬停卡片或卡片内获得键盘焦点时再显示 —— 键盘可达性由 focus-within 兜底 */
@media (hover: hover) and (pointer: fine) {
    .jn-card-share { opacity: 0; }
    .jn-card:hover .jn-card-share,
    .jn-card:focus-within .jn-card-share { opacity: 1; }
}
/* 既有元素保护：星标(.jn-card-fav) / 「暂不可达」标签(.jn-card-flag) 都是名称行
 * 内的普通流内元素，本身没有 position / z-index；而 .jn-card-share 是 z-index:1 的
 * 绝对定位热区。当站点名较长、名称行右端顶到卡片右缘时，44×44 的热区会压在这两者
 * 之上 —— 星标（可点）被抢走点击、「暂不可达」文字被盖住。
 * 二者是卡片既有的信息与交互，优先级高于本次新增的分享入口，故抬高一层压住热区：
 *   · z-index:2 > 1 ⇒ 星标仍可点、标签仍可读；
 *   · position:relative 只为让 z-index 生效，不给 top/left ⇒ 位置零位移，布局无变化；
 *   · .jn-card 自身 z-index:auto 不构成层叠上下文，两个定位子元素按 z-index 正常排序；
 *   · 分享热区被压住的只是「与名称行重叠的那条 16~24px 高带」，其左下大部分
 *     （含卡片右缘下方的空白区）仍 ≥40×40，触控规格不受破坏（§20.1 缺口 1 第③条）。
 */
.jn-card-fav,
.jn-card-flag { position: relative; z-index: 2; }

/* ---- 极简浮层容器 ----
 * layer 是覆盖全屏的透明层，只负责「点空白关闭」；真正的面板 .jn-share-pop
 * 由脚本按触发元素位置 fixed 定位（见文件末尾 P2-4 脚本的 place()）。
 * 用 position:fixed 而非 absolute：卡片/顶栏都在可滚动容器内，
 * absolute 会随滚动「粘」在文档坐标上，滚一下就跑偏。 */
.jn-share-layer { position: fixed; inset: 0; z-index: 50; }
.jn-share-pop {
    position: fixed;
    left: 0;
    top: 0;
    min-width: 236px;
    /* 窄屏护栏：16px 留给左右各 8px 安全边距，防止贴边溢出 */
    max-width: calc(100vw - 16px);
    padding: 10px;
    background: var(--jn-panel);
    /* 浅色主题下 --jn-line 为 transparent（仅剩投影），此时靠 box-shadow 定形 */
    border: 1px solid var(--jn-line);
    border-radius: var(--jn-card-radius);
    box-shadow: var(--jn-shadow);
}
/* 标题行：显示「分享的目标名称」，过长省略号截断，避免撑宽浮层 */
.jn-share-title {
    margin: 0 0 8px;
    padding: 0 2px;
    font-size: 12px;
    color: var(--jn-text-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.jn-share-row { display: flex; align-items: stretch; gap: 8px; }
/* 单格按钮：图标在上、文字在下，等宽平分。min-height 44px 保证触控友好 */
.jn-share-btn {
    flex: 1 1 0;
    min-width: 0;
    min-height: 44px;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 6px;
    font-family: inherit;
    font-size: 12px;
    line-height: 1.2;
    color: var(--jn-text-dim);
    background: transparent;
    border: 1px solid var(--jn-line);
    border-radius: 10px;
    cursor: pointer;
    transition: background .16s, border-color .16s, color .16s;
}
.jn-share-btn .jn-ic { width: 18px; height: 18px; }
.jn-share-btn:hover {
    color: var(--jn-text);
    background: var(--jn-item-hover);
    border-color: var(--jn-accent);
}
.jn-share-btn:focus-visible { outline: 2px solid var(--jn-accent); outline-offset: -2px; }
/* 必须显式声明：脚本用 hidden 属性隐藏「系统分享」格（navigator.share 不存在时），
   而 .jn-share-btn 是 inline-flex，会盖掉 [hidden] 的 UA 默认 display:none */
.jn-share-btn[hidden] { display: none; }

/* ---- 浮层分端（§20.1 缺口 2）----
 * 桌面（≥640px）：三格「复制链接 / 二维码 / 分享」，二维码格点开即预览；
 * 移动（<640px）：首屏两格「复制链接 / 系统分享」，二维码下沉到「更多」次级；
 *   移动端不预渲染二维码，点「更多」才加载（省一次脚本请求 + 一次渲染）。 */
.jn-share-btn[data-share-act="qr"] { display: none; }
.jn-share-pop.is-more .jn-share-btn[data-share-act="qr"] { display: inline-flex; }
.jn-share-pop.is-more .jn-share-btn[data-share-act="more"] { display: none; }
@media (min-width: 640px) {
    .jn-share-btn[data-share-act="qr"] { display: inline-flex; }
    .jn-share-btn[data-share-act="more"] { display: none; }
}

/* ---- 二维码预览格 ----
 * 二维码本体（packages/share/js/nav-qr.ts）一律纯黑白 + 白底输出，
 * 故本格在所有主题下都必须给白底（见该项文件头注释「浮层容器自带白底圆角」）。
 * 这是本段唯一允许的硬编码颜色（#fff / #667085），因为它服务于
 * 「二维码必须高对比可扫」这一与主题无关的硬要求；其余全部走 --jn-*。 */
.jn-share-qr {
    margin-top: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 152px;
    padding: 12px;
    background: #fff;
    border-radius: 10px;
}
.jn-share-qr[hidden] { display: none; }
.jn-share-qr svg { display: block; width: 128px; height: 128px; }
/* 白底格内的辅助文字：不能用 --jn-text-dim（浅色主题下在白底上尚可，
   深色主题下对比度不足），故硬编码中性灰 #667085（WCAG AA 对白底 4.9:1） */
.jn-share-qr-hint { margin-top: 8px; font-size: 12px; color: #667085; }
/* 状态提示（复制成功 / 复制失败的手动引导）。用 overflow-wrap 防止长 URL 撑破浮层 */
.jn-share-tip {
    margin-top: 8px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--jn-text-dim);
    overflow-wrap: anywhere;
}
.jn-share-tip[hidden] { display: none; }

