/* ==========================================================================
   文章 / 文件浏览界面样式
   --------------------------------------------------------------------------
   与 videos.css 同一套规范：只管结构，颜色引用 theme.css 的变量。
   依赖：theme.css 需先加载
   ========================================================================== */

/* ---------- 顶部栏 ---------- */
.api-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px 16px;
    margin: 2rem 0 1.25rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--border);
}

.api-title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-strong);
}

.api-spacer {
    margin-left: auto;
}

/* ---------- 文章列表 ---------- */
/* 访客列表与控制台列表共用同一套结构与样式，保证两处观感一致 */
.article-list {
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
}

/* 标题链接：继承标题字号字重，仅加可点击反馈 */
.article-item__link {
    color: inherit;
    text-decoration: none;
    transition: color 0.2s ease;
}

.article-item__link:hover {
    color: var(--link);
}

/* ---------- 访客列表：整条可点击 ---------- */
/* 用「拉伸链接」而不是给 li 绑 JS：
   把标题里的 <a> 用伪元素铺满整个 li，点击任何位置都命中这个真实链接，
   因此中键新标签、右键复制链接、键盘 Tab 聚焦等原生行为全部保留。 */
.article-item.is-clickable {
    position: relative;
    padding: 14px 16px;
    margin-bottom: 4px;
    border: 1px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    transition: border-color 0.2s ease, background 0.2s ease;
}

.article-item.is-clickable:hover {
    border-color: var(--border-strong);
    background: var(--bg-subtle);
}

/* 命中层：铺满整条 li。
   关键点：::after 直接挂在 <a> 上，而 <a> 本身没有 position，
   因此它相对最近的定位祖先（就是 .is-clickable）定位，正好覆盖整条。
   z-index 给 1 让它浮在内容之上，这样任何位置点击都命中这个真实链接。 */
.article-item.is-clickable .article-item__link::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
}

/* 内容层保持常态：不加 pointer-events:none，
   否则文字无法选中、标题也无法正常响应。
   命中层已在上层，鼠标事件自然先落到 ::after。 */

/* 键盘聚焦时给出可见轮廓，鼠标点击不显示 */
.article-item.is-clickable .article-item__link:focus-visible::after {
    outline: 2px solid var(--link);
    outline-offset: 3px;
    border-radius: 10px;
}

/* 内容层里的按钮（控制台的删除）必须浮在命中层之上，否则点不到。
   只抬升按钮本身，其余文字仍由命中层接管，保持整条可点。 */
.article-item.is-clickable .article-item__action {
    position: relative;
    z-index: 2;
}

/* 语言标签同样是链接（点了进编辑器并定位到该语言），也要浮在命中层之上；
   否则点击会被整条的拉伸链接抢走，只能进默认语言的编辑页。 */
.article-item.is-clickable .badge-group .badge {
    position: relative;
    z-index: 2;
}

/* 悬停整条时标题同步变色 */
.article-item.is-clickable:hover .article-item__title {
    color: var(--link);
}

/* 右下角「阅读 →」，悬停时轻微右移作为反馈 */
.article-item__read {
    font-size: 0.75rem;
    color: var(--text-muted);
    transition: color 0.2s ease, transform 0.2s ease;
}

.article-item.is-clickable:hover .article-item__read {
    color: var(--link);
    transform: translateX(3px);
}

/* 分页按钮的禁用态：保持占位，视觉上明确不可点 */
.btn.is-disabled {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
}

/* ==========================================================================
   文章阅读页
   --------------------------------------------------------------------------
   左栏（文章列表，read-toc）与右栏（正文大纲，read-outline）各自独立，
   四种组合都要成立：
     只有正文 / 左+正文 / 正文+右 / 左+正文+右
   用 grid-template-columns 按需拼装列轨道，而不是写死几套布局，
   这样新增/移除某一栏都不需要改结构。
   正文列用 minmax(0, 1fr)，保证永远占满剩余空间且不被内容撑破。
   ========================================================================== */
.read-layout {
    display: block;
}

/* 有任意侧栏时切换为 grid 布局。
   列轨道固定成「左栏 | 正文 | 大纲」三条，**有没有大纲都占同样的宽度**：
   没有大纲时第三条轨道留空，所以正文的宽度与位置不会因为大纲的有无而变化
   （以前按需拼装列轨道，有大纲时正文会被挤窄并左移，切换文章就跳）。
   左右两条轨道等宽，正文因此仍在页面中间。 */
.read-layout--with-toc,
.read-layout--with-outline {
    display: grid;
    grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--outline-w);
    column-gap: var(--rail-gap);
    row-gap: 0;
    align-items: start;
}

/* 三栏各就各位：缺哪栏就空着那条轨道，不重排列（重排列会让正文左右跳） */
.read-layout > .read-toc { grid-column: 1; grid-row: 1; }
.read-layout > .page-content { grid-column: 2; grid-row: 1; }
.read-layout > .read-outline { grid-column: 3; grid-row: 1; }

/* 阅读页里 .page-content 嵌在 .read-layout 内部，
   不是 .page-shell 的直接子元素，所以享受不到那条 flex:1 规则。
   这里补上，否则页脚无法被顶到底部。 */
.read-layout > .page-content {
    flex: 1 0 auto;
    min-width: 0;
    /* 用 padding 而不是 margin 来留上下留白：
       margin 会与内部 .article-read 的 margin 合并，
       把整个正文块推下去，导致页脚超出视口底部。

       上下各 1.5rem 是个平衡值：再大一点，1400px 这类常见窗口高度
       下正文加页脚就会超过一屏，页脚就没法贴底了。 */
    padding-top: 1.5rem;
    padding-bottom: 1.5rem;
    box-sizing: border-box;
}

/* 正文外边距归零，避免参与外边距合并、影响父容器高度 */
.read-layout .article-read {
    margin: 0 auto;
}

/* ---------- 左侧目录栏 ---------- */
.read-toc {
    /* sticky 相对滚动容器生效。这里页面整体滚动，
       所以贴在视口顶部即可，滚动正文时目录保持可见。
       注意不能给祖先加 overflow:hidden，否则 sticky 会失效。 */
    position: sticky;
    top: 0;
    align-self: start;
    /* 最多占一屏高，超出时目录内部自己滚动 */
    max-height: 100dvh;
    overflow-y: auto;

    /* 与列表页 / 控制台的 .side-rail 完全一致：同宽、同内边距、同一条右侧竖线 */
    padding: 24px 12px;
    border-right: 1px solid var(--border);
    box-sizing: border-box;
}

.read-toc__head {
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
}
.read-toc__back {
    font-size: 0.8125rem;
    color: var(--link);
    text-decoration: none;
}

.read-toc__back:hover {
    text-decoration: underline;
}

.read-toc__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* ==========================================================================
   右侧大纲（正文里的标题）
   ========================================================================== */
.read-outline {
    /* 与左侧目录同样吸顶，滚动正文时大纲保持可见 */
    position: sticky;
    top: 0;
    align-self: start;

    /* 高度限制在视口内，标题多时大纲内部自己滚动。
       脚本会跟随正文滚动位置把高亮项滚到可见区。 */
    max-height: 100dvh;
    overflow-y: auto;
    /* 平滑滚动，避免高亮切换时跳动 */
    scroll-behavior: smooth;

    padding: 2rem 20px 2rem 12px;
    border-left: 1px solid var(--border);
    box-sizing: border-box;
}

.read-outline__head {
    margin-bottom: 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    letter-spacing: 0.03em;
}

.read-outline__nav {
    display: flex;
    flex-direction: column;
}

.read-outline__link {
    /* -webkit-box 配合 line-clamp 做两行截断，
       标题过长时不会把大纲列撑宽 */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;

    padding: 4px 8px;
    border-left: 2px solid transparent;
    border-radius: 0 4px 4px 0;

    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--text-muted);
    text-decoration: none;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.read-outline__link:hover {
    color: var(--text);
    background: var(--bg-subtle);
}

/* 按标题层级缩进，形成大纲的层次感 */
.read-outline__link--h1 { padding-left: 8px; font-weight: 600; }
.read-outline__link--h2 { padding-left: 18px; }
.read-outline__link--h3 { padding-left: 28px; font-size: 0.78125rem; }
.read-outline__link--h4 { padding-left: 38px; font-size: 0.75rem; }

/* 当前阅读位置高亮（由脚本根据滚动位置切换） */
.read-outline__link.is-active {
    color: var(--link);
    border-left-color: var(--link);
    background: var(--bg-subtle);
}

/* 正文里的标题滚动到视口时不要被顶部遮住 */
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4 {
    scroll-margin-top: 1rem;
}

.read-toc__item {
    margin: 0;
}

/* 每条只显示标题，最多两行，超出省略 */
.read-toc__link {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;

    padding: 7px 10px;
    margin-bottom: 2px;
    border-radius: 6px;
    border-left: 2px solid transparent;

    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--text-muted);
    text-decoration: none;
    transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}

.read-toc__link:hover {
    color: var(--text);
    background: var(--bg-subtle);
}

/* ==========================================================================
   手机端文章列表「展开条」的构件
   --------------------------------------------------------------------------
   默认全部隐藏：桌面下侧栏常驻，不需要唤起按钮和收起按钮。
   显示与「变身为可展开区块」的规则统一写在文件末尾的 640px 断点里。
   ========================================================================== */
.read-topbar,
.read-toc__close {
    display: none;
}

/* 当前正在阅读的那篇高亮 */
.read-toc__link.is-current {
    color: var(--link);
    border-left-color: var(--link);
    background: var(--bg-subtle);
    font-weight: 600;
}

.article-read {
    margin: 2rem auto;
}

.article-read__head {
    padding-bottom: 1rem;
    margin-bottom: 1.75rem;
    border-bottom: 1px solid var(--border);
}
.article-read__title {
    margin: 0 0 10px;
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--text-strong);
}

.article-read__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 0.8125rem;
    color: var(--text-muted);
}

/* 正文区复用 markdown.css 的排版，这里只覆盖边距 */
.article-read__body {
    margin: 0;
    padding-bottom: 0;
}

/* --------------------------------------------------------------------------
   代码块复制按钮
   --------------------------------------------------------------------------
   按钮由 article-read.js 动态插入，这里只负责外观与定位。
   -------------------------------------------------------------------------- */
/* 定位容器：pre 自身可横向滚动，按钮放进去会跟着内容滚走，
   所以套一层 relative 容器把它钉在右上角 */
.code-block {
    position: relative;
}

.code-copy {
    position: absolute;
    top: 8px;
    right: 8px;
    padding: 3px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--bg);
    color: var(--text-muted);
    font-family: inherit;
    font-size: 0.75rem;
    line-height: 1.5;
    cursor: pointer;
    /* 默认隐藏，鼠标移到代码块上才出现，避免遮挡代码、干扰阅读 */
    opacity: 0;
    transition: opacity .15s ease, color .15s ease, border-color .15s ease;
}

.code-block:hover .code-copy,
.code-copy:focus-visible,
.code-copy.is-copied {
    opacity: 1;
}

.code-copy:hover {
    border-color: var(--link);
    color: var(--link);
}

.code-copy.is-copied {
    border-color: var(--link);
    color: var(--link);
}

/* 触屏没有 hover，按钮会永远不出现，这里改成常显 */
@media (hover: none) {
    .code-copy {
        opacity: 1;
    }
}

/* 草稿预览提示条 */
.preview-notice {
    margin-bottom: 1.5rem;
    padding: 10px 14px;
    border: 1px solid var(--link);
    border-radius: 8px;
    background: var(--bg-subtle);
    font-size: 0.8125rem;
    color: var(--text);
}

.preview-notice a {
    color: var(--link);
    margin-left: 6px;
}

/* 上一篇 / 下一篇 */
.article-nav {
    display: grid;
    /* 并排优先，放不下才换行：
       minmax 的下限是「还看得下去的最小卡片宽度」，
       两栏都够这个宽度就并排（手机横屏、窄屏也尽量并排），
       屏幕实在太窄（两栏连 140px 都分不到）才塌成上下两行。
       写死 1fr 1fr 的话窄屏上标题会被挤成两三个字一行，反而更难读。 */
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 12px;
    /* 上下留白适度收紧，避免正文末尾到页脚之间空太多 */
    margin: 2rem 0 1rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--border);
}

.article-nav__item {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    text-decoration: none;
    transition: border-color 0.2s ease;
}

.article-nav__item:hover {
    border-color: var(--link);
}

.article-nav__item--right {
    text-align: right;
}

.article-nav__label {
    font-size: 0.75rem;
    color: var(--text-muted);
}

.article-nav__title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-strong);
    /* 标题过长时截断，避免撑破卡片 */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.article-item {
    padding: 14px 0;
    border-bottom: 1px solid var(--border);
}
.article-item:last-child {
    border-bottom: none;
}

.article-item__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px 10px;
}

/* 控制台条目里的语言标签：单独占一行落在标题下方。
   __head 是 flex + wrap，这里用 flex-basis: 100% 抢下整行宽度，自然换到第二行。 */
.article-item__badges {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.article-item__title {
    margin: 0;
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--text-strong);
}

.article-item__body {
    margin: 6px 0 0;
    font-size: 0.875rem;
    line-height: 1.7;
    color: var(--text-muted);
}

.article-item__meta {
    margin-top: 8px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* ---------- 阅读数（总数 + 各语言明细浮层） ----------
   总数是合集口径（所有语言之和）；
   悬停、键盘聚焦或点击（加 .is-open）时，在下方浮出各语言的 LR/DR/小计。
   用绝对定位浮层而不是内联展开：明细只在需要时出现，
   不会把列表项撑高、让条目参差不齐。 */
.article-item__reads {
    position: relative;
    /* 必须抬到「整条命中层」之上（见 .article-item__link::after 的 z-index:1），
       否则鼠标事件全被命中层接管：悬浮不出浮层、点击也不展开。
       键盘聚焦不受命中层影响，所以之前只有 Tab 能展开。

       取 3 而不是 2：要盖过「阅读 / 删除」按钮（.article-item__action 也是 2），
       否则同级下 DOM 靠后的按钮会压在浮层之上、把明细挡住。
       这里 z-index 会让本元素成为层叠上下文，内部浮层的 20 只在其内部比较，
       所以必须在这个层级上就超过按钮。 */
    z-index: 3;
    cursor: pointer;
    /* 下划虚线作为「这里可以展开」的轻提示 */
    border-bottom: 1px dashed var(--border-strong);
}

/* 浮层：默认隐藏。
   整块是 span，靠 display:block 变成块级浮层。 */
.reads-pop {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    /* 高于同行的其它元素，避免被后面的按钮盖住 */
    z-index: 20;
    padding: 6px 0;
    min-width: 200px;
    /* 任何情况下都不超出视口：窄屏上「阅读数」可能靠近右边缘，
       不限制的话 200px 宽的浮层会顶出屏幕、撑出横向滚动条。
       32px 给两侧各留一点边距。 */
    max-width: calc(100vw - 32px);
    box-sizing: border-box;

    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: var(--bg);
    box-shadow: var(--shadow-lg);
    color: var(--text);
}

.article-item__reads:hover .reads-pop,
.article-item__reads:focus-visible .reads-pop,
.article-item__reads.is-open .reads-pop {
    display: block;
}

/* 键盘聚焦时给个可见的轮廓，否则 tabindex 聚焦毫无反馈 */
.article-item__reads:focus-visible {
    outline: 2px solid var(--link);
    outline-offset: 2px;
}

.reads-pop__row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 3px 12px;
    /* 窄屏浮层被 max-width 压窄时，允许行内收缩而不是把浮层撑破 */
    min-width: 0;
}

/* 语言名占满剩余宽，把三个数字推到右侧对齐 */
.reads-pop__lang {
    flex: 1;
    min-width: 0;
    color: var(--text-strong);
    /* 窄屏被压缩时截断而不是换行，保持每语言一行 */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.reads-pop__num {
    flex: none;
    color: var(--text-muted);
    /* 数字列等宽，多行之间竖向对齐；数字本身不换行 */
    font-variant-numeric: tabular-nums;
    min-width: 52px;
    text-align: right;
    white-space: nowrap;
}

.reads-pop__num--sum {
    color: var(--text);
}

.reads-pop__row--empty {
    padding: 3px 12px;
    color: var(--text-muted);
}

/* 状态徽标 */
.badge {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 0.6875rem;
    line-height: 1.7;
    border: 1px solid var(--border-strong);
    color: var(--text-muted);
    white-space: nowrap;
}

.badge--published {
    border-color: var(--link);
    color: var(--link);
}

/* 未发布区里「已存草稿」的标签：蓝色虚线边框（「草稿」文案已取消，靠它认）；
   文字保持灰色，与已发布的实线蓝边区分。 */
.badge--draft {
    border-style: dashed;
    border-color: var(--link);
}

/* 标签分块：已发布一块、其余一块，各自作为一个 flex 项。
   宽度不够时整块换行，两块之间自然留出间隔（组名文案已去掉，不再占位）。 */
.badge-group {
    display: flex;
    gap: 6px;
}

/* 块内标签换行时，与上一行的标签左对齐 */
.badge-group__badges {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* 标签本身是链接（点了进编辑器并定位到该语言）：去掉下划线，悬停时点亮 */
a.badge {
    text-decoration: none;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

a.badge:hover {
    border-color: var(--link);
    color: var(--link);
    background: var(--bg-subtle);
}

/* ---------- 语言切换器 ---------- */
/* 读列表页 / 阅读页共用：一组小胶囊，当前语言高亮且不可点 */
.lang-switch {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.lang-switch__item {
    padding: 2px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    font-size: 0.75rem;
    line-height: 1.8;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.lang-switch__item:hover {
    color: var(--link);
    border-color: var(--link);
}

/* 当前语言：主色描边 + 主色文字，并禁止点击 */
.lang-switch__item.is-current {
    color: var(--link);
    border-color: var(--link);
    cursor: default;
}

/* 无列表上下文时，语言切换单独排一行 */
.article-read__langbar {
    margin-bottom: 1rem;
}

/* 表单控件（输入框 / 下拉框）统一用 base-ui.css 的 .form-field，
   本文件不再重复定义；筛选行里按需用内联样式改宽度、清外边距即可。 */

/* ==========================================================================
   独立编辑器页面
   --------------------------------------------------------------------------
   布局思路：整屏是一个纵向 flex 容器（高度 = 100dvh），
   工具栏与提示条按内容占据自然高度，剩余空间全部给下方两栏。
   两栏再各自 flex 纵向排布，让书写区 / 预览区撑满并内部滚动。

   不使用 calc(100vh - 固定值) 这种写法：工具栏换行、提示条出现或
   消失都会改变实际高度，硬编码的减法必然错位。
   ========================================================================== */

/* 编辑器页不要页面级滚动，滚动只发生在两个面板内部 */
.editor-body {
    height: 100dvh;
    overflow: hidden;
    /* 编辑器是工具型界面，内容铺满整屏、不留两侧边距，
       把宽度全部让给书写区与预览区 */
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}

/* 整屏骨架 */
/* 最左边一列放文章分类，右边是工具栏 + 书写/预览区 */
.editor-shell {
    display: grid;
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    grid-template-rows: auto auto 1fr;
    height: 100%;
    overflow: hidden;
}

/* 左栏分类：条目外观沿用控制台那套（.category-nav__item），只是多一个勾选框 */
.editor-rail {
    grid-column: 1;
    grid-row: 2 / -1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* 顶部不留白：表头贴边，分隔线才能贯穿整列 */
    padding: 0 0 16px;
    overflow-y: auto;
    border-right: 1px solid var(--border);
    background: var(--bg);
}

/* 表头：与 .editor-pane__head（Markdown 那条）同一套分隔样式 ——
   等高、浅底、底边一条线，把「分类 / 全选 / 反选」和下面的列表隔开 */
.editor-rail__head {
    flex: none;
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 34px;
    box-sizing: border-box;
    padding: 6px 10px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-subtle);
}

/* 分类栏的显示 / 隐藏按钮（放在「Markdown」前面，尺寸同「大纲」按钮）：
   展开时显示「<<」——箭头朝左，点它把左侧那一列收起来；
   收起后显示「>>」——箭头朝右，点它再展开。 */
.editor-cats-toggle__show {
    display: none;
}

.editor-shell.is-cats-collapsed .editor-cats-toggle__hide {
    display: none;
}

.editor-shell.is-cats-collapsed .editor-cats-toggle__show {
    display: inline;
}

/* 收起后整列消失：分类栏不占位，其余部件并到唯一的那一列 */
.editor-shell.is-cats-collapsed {
    grid-template-columns: minmax(0, 1fr);
}

.editor-shell.is-cats-collapsed .editor-rail {
    display: none;
}

.editor-shell.is-cats-collapsed .editor-bar,
.editor-shell.is-cats-collapsed .editor-msg,
.editor-shell.is-cats-collapsed .editor-split {
    grid-column: 1;
}

/* 分类栏收起后少了那一列：工具条改回对半，按钮组仍在分界线上 */
/* 栏宽置 0 后，工具条的三列自然退化成「0 | 一半 | 一半」，
   按钮组仍在编辑区与预览区的分界线上，不必另写一套覆盖规则 */
.editor-shell.is-cats-collapsed {
    --rail-w: 0px;
}

/* 列表自己的内边距（表头贴边，列表四边留白）：
   左侧给 8px，选中项的左侧高亮条才不会贴在栏的边缘上 */
.editor-rail .category-nav {
    padding: 10px 12px 0 8px;
}

/* 工具栏横跨整宽（标题那一行），分类列表放在它的分割线下方 */
/* 工具条横跨整宽（左上角不留空），内部第一列留出分类栏的宽度；
   这样它下面的「编辑区 / 预览区」分界线能与按钮组左边缘对上 */
.editor-bar { grid-column: 1 / -1; grid-row: 1; }
.editor-msg { grid-column: 2; grid-row: 2; }
.editor-split { grid-column: 2; grid-row: 3; }

/* 带勾选框的条目：框在左、文字在右；flex 布局下用正常换行，不做两行截断 */
.editor-rail .category-nav__item {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    -webkit-line-clamp: unset;
}

.editor-rail .category-nav__box {
    flex: none;
    margin: 3px 0 0;
}

/* 窄屏：分类栏收到工具栏上方，横向排列，不再占一列 */
@media (max-width: 1100px) {
    .editor-shell {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto auto auto 1fr;
    }

    .editor-rail {
        grid-column: 1;
        grid-row: 2;
        max-height: 28vh;
        padding: 0 0 6px;
        border-right: 0;
        border-bottom: 1px solid var(--border);
    }

    .editor-rail .category-nav {
        padding: 6px var(--page-gutter) 0;
    }

    /* 分类栏自成一行，工具条不再需要为它让出一列 */
    .editor-shell {
        --rail-w: 0px;
    }

    .editor-bar {
        grid-column: 1;
        grid-row: 1;
    }

    .editor-msg { grid-row: 3; }
    .editor-split { grid-row: 4; }

    .editor-rail .category-nav {
        flex-direction: row;
        flex-wrap: wrap;
        gap: 4px;
    }
}

/* 工具栏的网格列必须和下方 .editor-split 完全一致，
   这样「左列右边缘」始终等于编辑区与预览区之间的分界线，
   无论大纲开不开，状态徽标都贴着那条线。
   这里不加水平内边距：否则内容区比编辑器窄，分界线就错位了。 */
.editor-bar {
    flex: none;
    display: grid;
    /* 第一列是分类栏，后两列与 .editor-split 的两栏一一对应（列定义逐项相同，
       开启大纲时两边同样追加 220px 那一列），分界线因此必然重合 */
    grid-template-columns: var(--rail-w) minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
    background: var(--bg-subtle);
}

/* 开启大纲时追加第三列，宽度与 .editor-split 的大纲列保持一致，
   使工具栏的三列与下方三栏逐列对齐。
   （.is-outline-open 由脚本同时加在工具栏和两栏容器上） */
.editor-bar.is-outline-open {
    grid-template-columns: var(--rail-w) minmax(0, 1fr) minmax(0, 1fr) 220px;
}

/* 左列一行内从左到右：标题 · 状态 · 未保存。
   标题框吃掉剩余宽度，把状态与「未保存」顶到左列最右。
   右内边距 14px：让左列最后一个可见元素的右边缘与下方书写区头部
   「n 字符」的右边缘上下对齐（编辑面板头部同样有 14px 右内边距）。
   放在容器上而不是「未保存」上：未保存隐藏时，最后的元素是状态标签，
   由容器统一留白，两种情况都能对齐。
   这里必须 nowrap：一旦允许换行，各浏览器对
   「basis 优先还是内容优先」的判断不一致，Safari 与 Chrome 就会分叉。 */
.editor-bar__left {
    grid-column: 1 / 3;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 10px;
    min-width: 0;
    /* 与 .editor-pane__head 的左右留白一致 */
    padding: 0 14px;
}

/* 右列：返回 · 保存草稿 · 发布。从分界线处开始向右依次排布，
   视觉上就像紧接在「未保存」右侧。
   grid-column 跨到最后一列：大纲打开时把第 3 列也占上，
   按钮组不会因为多出一列而跑到中间去。 */
.editor-bar__actions {
    grid-column: 3;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
    min-width: 0;
}

/* 操作区按钮不参与收缩：nowrap 下若允许收缩，
   各浏览器压缩按钮文字的方式不一致（有的压内边距、有的换行），
   锁定原始尺寸后「返回 / 保存草稿 / 发布」在两个引擎里都一样。 */
.editor-bar__actions .btn {
    flex: none;
}

/* 返回图标按钮的左边缘与下方预览栏头部「全屏预览」按钮的左边缘对齐：
   两者都以两栏分界线为基准，预览头部有 padding-left: 14px（见 .editor-pane__head），
   这里补同样的 14px 左外边距，上下两条竖线才在一条直线上。
   与后面「保存草稿 / 发布」仍保持 8px 组内间距。
   注：窄屏（≤640px）预览头部内边距收成 12px，但那时工具栏已改为单列，
   返回按钮不再从分界线起排，无需跟随。 */
.editor-bar__actions .btn--icon {
    margin-left: 14px;
}

/* 撤销 / 重做：跟在「发布」后面，靠左右 auto 外边距在剩余空间里水平居中 */
.editor-history {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 auto;
}

/* 组内按钮不要继承上面那条 14px 左边距（那是给返回图标的） */
.editor-history .btn--icon {
    margin-left: 0;
}

/* 标题框撑满左列剩余的宽，把后面的徽标推到左列右端。
   flex-basis 用 0 而不是固定值：配合 min-width:0，
   空间不够时它先收缩，而不会把后面的元素顶到下一行。 */
/* 语言选择：浅描边的下拉框，视觉上不抢标题的注意力 */
.editor-bar__lang {
    flex: none;
    padding: 4px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: var(--bg);
    color: var(--text);
    font-size: 0.8125rem;
    font-family: inherit;
    cursor: pointer;
}

.editor-bar__lang:focus {
    outline: none;
    border-color: var(--link);
}

.editor-bar__title {
    flex: 1 1 0;
    min-width: 0;
    box-sizing: border-box;

    padding: 8px 12px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: transparent;
    color: var(--text-strong);
    font-size: 1.0625rem;
    font-weight: 600;
    font-family: inherit;
}

.editor-bar__title:hover {
    border-color: var(--border-strong);
}

.editor-bar__title:focus {
    outline: none;
    border-color: var(--link);
    background: var(--bg);
}

/* 状态标记：替代原来的下拉菜单，改为只读展示 */
.editor-bar__status {
    flex: none;
    padding: 3px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    font-size: 0.75rem;
    color: var(--text-muted);
    white-space: nowrap;
}

.editor-bar__status.is-published {
    border-color: var(--link);
    color: var(--link);
}

/* 「未保存」标记：有改动时由脚本去掉 hidden 显示出来。
   用警示色之外的中性色：它只是状态提示，不是错误，
   抢眼的颜色会让人以为出错了。
   与下方「n 字符」的对齐由左列容器的右内边距负责（见 .editor-bar__left）。 */
.editor-bar__dirty {
    flex: none;
    padding: 3px 10px;
    border: 1px dashed var(--border-strong);
    border-radius: 12px;
    font-size: 0.75rem;
    color: var(--text-muted);
    white-space: nowrap;
}

/* 图标按钮：正方形，仅放图标时内部居中 */
.btn--icon {
    padding: 8px 10px;
    line-height: 0;
}

.btn--icon .icon-back {
    display: block;
}

/* 撤销 / 重做图标沿用同一套图标按钮尺寸 */
.btn--icon .icon-edit {
    display: block;
}

/* 控制台「语言标签写法」按钮里的图标：钉住宽度，并去掉基线留下的空隙 */
.btn .icon-switch {
    display: block;
    flex: none;
}

/* 历史为空时按钮不可点：降透明度 + 禁用光标。
   .btn 已有 :disabled 样式，这里只补图标按钮的差异 */
.btn--icon:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.btn--icon:disabled:hover {
    border-color: var(--border-strong);
    color: var(--text);
}

/* 提示条：为空时彻底不占空间，避免把两栏挤下去 */
.editor-msg {
    flex: none;
    margin: 0;
    padding: 0 14px;
}

.editor-msg:empty {
    display: none;
}

.editor-msg:not(:empty) {
    padding: 8px 14px;
    border-bottom: 1px solid var(--border);
}
/* 两栏容器：flex:1 吃掉工具栏之外的全部剩余高度。
   默认始终左右分栏（编辑区 | 预览区），即使窄屏也保持左右，
   只是两栏各占一半宽度并缩小字号，避免布局在断点处突然跳成上下堆叠。 */
.editor-split {
    flex: 1;
    display: grid;
    /* 默认两栏：书写区 + 预览区。
       开启大纲后由 .is-outline-open 追加第三列。 */
    grid-template-columns: 1fr 1fr;
    gap: 1px;
    min-height: 0; /* 关键：允许 grid 在 flex 容器内收缩，否则会撑破容器 */
    background: var(--border);
}

/* 大纲打开：追加一列（顺序为 书写 / 预览 / 大纲）。
   用 auto-fit 之外的方式显式声明，避免列宽在不同内容下抖动。 */
.editor-split.is-outline-open {
    grid-template-columns: 1fr 1fr 220px;
}

/* 大纲关闭时不占空间 */
.editor-split:not(.is-outline-open) .editor-pane--outline {
    display: none;
}

.editor-pane {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0; /* 关键：让内部 overflow 生效，两栏才能各自滚动 */
    background: var(--bg);
}

/* 全屏预览：编辑栏从布局中移除，预览栏独占剩余空间。
   用 display:none 而不是缩到 0 宽，这样预览能独占整块区域。
   注意这条规则要放在 .editor-split 之后，避免被后面的响应式规则覆盖。
   大纲若处于开启态，本规则由下面的 .is-preview-full.is-outline-open 覆盖，
   此时仍然保留大纲列——全屏预览也能对着大纲查结构。 */
.editor-split.is-preview-full {
    grid-template-columns: 1fr;
}

.editor-split.is-preview-full .editor-pane--editor {
    display: none;
}

/* 全屏预览 + 大纲开启：编辑栏隐藏，但保留「预览 | 大纲」两列，
   这样在全屏预览下依然能看大纲。选择器比单独的 .is-preview-full 多一个类，
   优先级更高，因此能覆盖上面那条单列规则。 */
.editor-split.is-preview-full.is-outline-open {
    grid-template-columns: 1fr 220px;
}

/* ---------- 右侧大纲栏（编辑器内）---------- */
.editor-pane--outline .editor-pane__head {
    /* 大纲栏窄，标题与状态右对齐更紧凑 */
    justify-content: space-between;
}

/* 大纲为空时的占位提示 */
.editor-outline__empty {
    padding: 14px;
    font-size: 0.8125rem;
    line-height: 1.6;
    color: var(--text-muted);
}

/* 大纲列表：复用阅读页大纲的视觉语言，保持全站一致 */
.editor-outline__nav {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 10px 8px;
}

.editor-outline__link {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;

    padding: 4px 8px;
    margin-bottom: 1px;
    border-left: 2px solid transparent;
    border-radius: 0 4px 4px 0;

    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--text-muted);
    text-decoration: none;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.editor-outline__link:hover {
    color: var(--text);
    background: var(--bg-subtle);
}

/* 按层级缩进，与阅读页大纲保持同样的层次感 */
.editor-outline__link--h1 { padding-left: 8px; font-weight: 600; }
.editor-outline__link--h2 { padding-left: 18px; }
.editor-outline__link--h3 { padding-left: 28px; font-size: 12.5px; }
.editor-outline__link--h4 { padding-left: 38px; font-size: 0.75rem; }

/* 当前光标所在的章节高亮 */
.editor-outline__link.is-active {
    color: var(--link);
    border-left-color: var(--link);
    background: var(--bg-subtle);
}

/* ---------- 大纲开关按钮 ---------- */
/* 关闭态用普通描边按钮，开启态填充主色，状态一眼可辨。
   文字色写全六位：三位简写会被按浮点解析，取整后可能不是纯白 */
.btn.is-on {
    background: var(--link);
    border-color: var(--link);
    color: rgb(255, 255, 255);
}

.btn.is-on:hover {
    background: var(--link);
    border-color: var(--link);
    color: #fff;
    opacity: 0.88;
}

/* 全屏预览时把正文收窄到与访客阅读页一致的宽度并居中。
   这样「预览」看到的就是文章真正发布后的样子，而不是被拉到
   整屏宽度、每行上百字的失真排版。 */
.editor-split.is-preview-full .editor-pane--preview {
    background: var(--bg);
}

.editor-split.is-preview-full .editor-preview {
    /* 宽度取 --page-max 而不是 --content-max：
       阅读页的正文装在 .page-content 里，实际宽度由它的 --page-max 决定，
       markdown-body 那 860px 的上限在那里根本用不上。
       按 --content-max 收会让全屏预览比阅读页宽出 60px，
       「预览所见即发布所得」就落空了。 */
    max-width: var(--page-max);
    /* 左右自动居中，产生与阅读页相同的两侧留白 */
    margin: 0 auto;
    width: 100%;
    box-sizing: border-box;
    /* 视口窄于 content-max 时，左右各留一点内边距，文字不贴边 */
    padding-left: 24px;
    padding-right: 24px;
}

/* 三个面板（Markdown / 预览 / 大纲）的标题栏必须等高：
   它们内部放的东西不一样（左栏是图标按钮，右栏是文字按钮，大纲栏没有按钮），
   若各自按内容撑高就会高低不齐，三条分隔线凑不到一条水平线上。
   这里统一用固定的 min-height + 垂直居中 + 上下等距内边距来锁定高度，
   与内部放什么元素无关。 */
.editor-pane__head {
    flex: none;
    display: flex;
    align-items: center;
    gap: 8px;
    /* 固定高度：左右内边距保留，上下由行高与 min-height 共同决定 */
    min-height: 34px;
    box-sizing: border-box;
    padding: 6px 14px;
    border-bottom: 1px solid var(--border);
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--text-muted);
    background: var(--bg-subtle);
}

/* 面板头部里的小按钮（全屏预览 / 大纲）：
   头部条本身很矮，用默认 .btn 会把它撑高，这里收紧内边距与字号。
   统一给定高 22px：文字按钮与图标按钮高度写成同一个值，
   三个标题栏的高度就不会因为按钮尺寸不同而参差。
   flex:none 禁掉收缩，避免头部变窄时按钮被压变形——
   各浏览器对按钮内文字的最小尺寸判断不一致，禁用收缩才稳定。 */
.btn--pane {
    flex: none;
    height: 22px;
    box-sizing: border-box;
    padding: 0 10px;
    font-size: 0.75rem;
    line-height: 1;
    border-radius: 6px;
}

/* 面板头部里的图标按钮（撤销 / 重做）：高度与文字按钮取同一个 22px，
   用 display:grid + place-items:center 让图标在两个方向都正居中——
   比 flex 的居中更不受 SVG 基线 / 行高影响，各浏览器一致。 */
.editor-pane__head .btn--icon {
    flex: none;
    display: grid;
    place-items: center;
    height: 22px;
    width: 26px;
    box-sizing: border-box;
    padding: 0;
    line-height: 0;
    border-radius: 6px;
}

/* 头部条左侧是面板名，右侧是提示（字数 / 渲染状态）。
   hint 用 margin-left:auto 顶到最右，三个面板都依赖这条规则。 */
.editor-pane__hint {
    margin-left: auto;
}

/* 左侧书写区：撑满面板，自身滚动 */
.editor-textarea {
    flex: 1;
    width: 100%;
    box-sizing: border-box;
    min-height: 0;

    padding: 16px 18px;
    border: none;
    outline: none;
    resize: none; /* 由容器控制尺寸，不允许手动拖拽破坏布局 */
    overflow-y: auto;

    background: var(--bg);
    color: var(--text);
    line-height: 1.7;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.875rem;
    tab-size: 4;
}

/* ==========================================================================
   右侧预览区
   --------------------------------------------------------------------------
   这里刻意**不**定义任何 Markdown 元素样式：
   预览容器同时挂了 markdown-body 类，正文排版全部由 markdown.css 提供，
   与文章阅读页完全共用同一份规则。

   之前这里另写了一套「精简版」排版（h1 字号不同、没有标题下划线、
   strong 不生效……），结果预览和发布后的效果对不上，
   正是「所见非所得」的根源。现在只保留容器自身的布局属性。
   ========================================================================== */
.editor-preview {
    flex: 1;
    min-height: 0;

    /* 宽度取文章正文的宽度并居中，而不是铺满整个面板。
       铺满时（max-width: none）面板在超宽屏上能有 1200px 以上，
       同一张图在预览里就比发布后宽出一大截，预览失去参考价值；
       收成 --page-max 之后，预览里的正文宽度、换行位置、图片宽度
       都和阅读页一致，才算「预览所见即发布所得」。
       面板本身比 --page-max 窄时，width:100% 会让它跟着收缩。 */
    margin: 0 auto;
    max-width: var(--page-max);
    padding: 16px 18px;
    /* 铺满面板而不是让内容决定宽度，避免短内容收缩。
       宽度取 100%（整数百分比），不引入浮动像素 */
    width: 100%;
    box-sizing: border-box;

    overflow-y: auto;
    /* 字号与阅读页正文保持一致（1rem），
       否则 em 单位算出的标题/间距会整体偏小 */
    font-size: 1rem;
    color: var(--text);
}

/* 首个子元素去掉上边距，避免预览顶部出现多余的空白 */
.editor-preview > :first-child {
    margin-top: 0;
}

.preview-placeholder {
    color: var(--text-muted);
    font-size: 0.875rem;
}

/* ---------- 表单通用 ---------- */
.form-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.form-label {
    font-size: 0.8125rem;
    color: var(--text-muted);
    min-width: 48px;
}

.msg {
    margin-top: 10px;
    font-size: 0.8125rem;
    line-height: 1.6;
}

.msg:empty {
    display: none;
}

.msg.is-error { color: #d1242f; }
.msg.is-success { color: var(--link); }

/* 分页说明文字（「第 X / Y 页，共 N 篇」）：控制台与访客列表的分页都在用。
   字号写死 13px：0.8125rem 会算出小数像素，各浏览器取整方向不一定相同 */
.crumbs {
    font-size: 13px;
    color: var(--text-muted);
}

.crumbs a {
    color: var(--link);
    text-decoration: none;
    cursor: pointer;
}

.crumbs a:hover { text-decoration: underline; }

/* 列表空状态：没有任何条目时的占位提示 */
.list-empty {
    padding: 3rem 1rem;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.875rem;
}

/* ==========================================================================
   移动端适配
   ========================================================================== */
/* 窄屏：工具栏改为两行（标题一行、按钮一行），
   编辑区/预览区仍保持左右分布（此时还有足够宽度）。 */
@media (max-width: 1200px) {
    /* 中等屏幕下大纲栏收窄，避免书写区太挤。
       工具栏必须用同一组列宽，才能继续和两栏逐列对齐。 */
    .editor-split.is-outline-open,
    .editor-bar.is-outline-open {
        grid-template-columns: 1fr 1fr 180px;
    }
}

@media (max-width: 860px) {
    /* 窄屏下标题太短没法用，工具栏改为单列堆叠：
       标题与徽标一行、操作按钮折到下一行靠左排。
       连 .is-outline-open 一起覆盖，避免大纲列把工具栏挤变形。 */
    .editor-bar,
    .editor-bar.is-outline-open {
        grid-template-columns: 1fr;
        gap: 8px;
    }

    .editor-bar__left {
        grid-column: 1;
        flex-wrap: wrap;
    }

    /* 单列后操作区独占一行（不再从第 2 列跨起） */
    .editor-bar__actions {
        grid-column: 1 / -1;
    }

    /* 两栏变窄，收紧内边距与字号，把宽度留给内容 */
    .editor-textarea,
    .editor-preview {
        padding: 12px 14px;
    }

    .editor-textarea {
        font-size: 0.8125rem;
    }

    .editor-preview {
        font-size: 0.875rem;
    }
}

/* --------------------------------------------------------------------------
   手机端：编辑区与预览区改为上下布局。
   左右分栏在窄屏会把每栏压到不足 160px，中文一行放不下几个字，
   完全没有可读性；上下堆叠能让两栏都用上完整宽度。
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
    .editor-split {
        grid-template-columns: 1fr;
        /* 两栏平分剩余高度，各自内部滚动，滚动同步逻辑照旧生效 */
        grid-template-rows: 1fr 1fr;
    }

    /* 全屏预览时只留预览栏 */
    .editor-split.is-preview-full {
        grid-template-rows: 1fr;
        grid-template-columns: 1fr;
    }

    /* 手机上大纲改为第三行（不单独占一列，避免挤压书写/预览宽度） */
    .editor-split.is-outline-open {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr 1fr auto;
    }

    /* 全屏预览 + 大纲：编辑栏已隐藏，剩「预览 / 大纲」两行 */
    .editor-split.is-preview-full.is-outline-open {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr auto;
    }

    .editor-split.is-outline-open .editor-pane--outline {
        /* 大纲内容不多，给个上限即可 */
        max-height: 22vh;
    }

    .editor-textarea,
    .editor-preview {
        padding: 12px;
    }
}


@media (max-width: 1100px) {
    /* 左栏内边距不动：要与列表页那条竖线保持同一位置 */
    .read-outline {
        padding: 1.5rem 14px 1.5rem 10px;
    }
}

/* 窄屏：两侧栏一起收掉，正文单列。
   不保留单独的「正文 + 大纲」形态：那会让正文在有/无大纲时宽度不同（又跳一次），
   也挤占窄屏本就紧张的阅读宽度。 */
@media (max-width: 960px) {
    .read-layout--with-toc,
    .read-layout--with-outline {
        display: block;
        grid-template-columns: none;
    }

    .read-toc,
    .read-outline {
        display: none;
    }
}

@media (max-width: 640px) {
    .api-header { margin-top: 1.25rem; }
    .api-title { font-size: 1.25rem; }

    /* 手机上「阅读数」常落在 meta 行右侧，浮层若仍 left:0 向右展开会顶出屏幕，
       要横滑才能看全。改成右对齐（right:0 / left:auto）：
       浮层从「阅读数」右边缘向左展开，永远不越出右边界。
       左侧空间由列表项自身占位，通常够用；配合 max-width 兜底。 */
    .reads-pop {
        left: auto;
        right: 0;
    }

    /* 阅读页的左右留白加在 .read-layout 这一层：
       顶部的唤起按钮、可展开的文章列表都在正文容器 .page-content 之外，
       留白放在外层才能和正文共用同一条左边界（值取自 --page-gutter，
       与 .page-content 一致，见 page-content.css） */
    .read-layout {
        padding-left: var(--page-gutter);
        padding-right: var(--page-gutter);
    }

    /* 内层正文容器不再重复留白，否则正文会比按钮和列表多缩进一层 */
    .read-layout > .page-content {
        padding-left: 0;
        padding-right: 0;
    }

    /* 手机上两侧栏都会挤掉正文宽度，全部隐藏；
       正文回到单栏，阅读体验与直接访问该文章一致。

       注意要显式清掉 grid：仅设 display:block 是不够的，
       更窄断点（960px）留下的 grid-template-columns 依然生效，
       会把正文压成很窄一列。这里把列定义一并重置。 */
    .read-layout--with-toc,
    .read-layout--with-outline,
    .read-layout--with-toc.read-layout--with-outline {
        display: block;
        grid-template-columns: none;
    }

    /* 大纲在手机上隐藏；文章列表不再隐藏 —— 它改作抽屉复用（见下方规则） */
    .read-outline {
        display: none;
    }

    /* 编辑器预览区收窄内边距 */
    .editor-preview { padding: 14px; }

    /* 阅读页 */
    .article-read { margin-top: 1.25rem; }
    .article-read__title { font-size: 1.375rem; }

    /* 上下篇不再强制竖排：基础规则里的 auto-fit 会自己决定并排还是换行。
       卡片内边距收一点，给并排的两张卡片多留些宽度 */
    .article-nav__item {
        padding: 10px 12px;
    }

    /* 手机上只留上下内边距：
       左右留白已由 .page-content 统一给出（--page-gutter），
       卡片再留一层就变成「页面留白 + 卡片留白」两份叠加，
       整条列表会比页标题深一大截、看着就是边距突然变大。
       去掉后条目文字与页标题同一条左边界，整条依然可点，
       悬停底色直接铺满内容列。 */
    .article-item.is-clickable {
        padding: 12px 0;
    }

    /* ------------------------------------------------------------------
       文章列表展开条
       ------------------------------------------------------------------
       窄屏下正文必须占满整屏，左侧目录栏没有横向空间，
       于是把它改成「贴在顶部、可展开」的一块：
       收起时高度为 0，点顶部按钮后高度过渡到内容高度，
       正文被顺势推下去，列表就这样被慢慢露出来；再点一次收回，正文盖回去。

       不用抽屉/遮罩那套悬浮方案：悬浮层会盖住正文，
       而这里要的就是「正文往下让位」的位移感。

       复用桌面那条侧栏（同一份列表数据、同一套 .read-toc__link 样式），
       不另起一套 DOM，避免列表渲染逻辑重复维护。
       ------------------------------------------------------------------ */
    .read-topbar {
        display: block;
        /* 左右留白已由 .read-layout 统一给出，这里只留上下 */
        padding: 8px 0;
    }

    .read-topbar__btn {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        padding: 6px 12px;
        border: 1px solid var(--border-strong);
        border-radius: 999px;
        background: var(--bg-subtle);
        color: var(--text);
        font-size: 0.8125rem;
        font-family: inherit;
        cursor: pointer;
    }

    /* 汉堡图标：三条线纯 CSS 画，不为它引入图标库 */
    .read-topbar__icon {
        position: relative;
        flex: none;
        width: 15px;
        height: 2px;
        border-radius: 1px;
        background: currentColor;
    }

    .read-topbar__icon::before,
    .read-topbar__icon::after {
        content: '';
        position: absolute;
        left: 0;
        width: 100%;
        height: 2px;
        border-radius: 1px;
        background: currentColor;
    }

    .read-topbar__icon::before { top: -5px; }
    .read-topbar__icon::after { top: 5px; }

    .read-toc {
        /* 回到普通文档流：不再定位、不再悬浮、不再限宽限高，
           sticky / align-self / 侧栏那套定位属性一并失效 */
        display: block;
        position: static;
        width: auto;
        max-width: none;
        /* 收起态：高度为 0，列表被裁掉，正文顶上来把它盖住 */
        height: 0;
        max-height: none;
        padding: 0;
        border-right: none;
        overflow: hidden;

        /* 高度由脚本写成具体像素值（height:auto 不能参与过渡），
           这里只负责过渡本身 —— 展开就是这条高度在增长，
           正文随之被推下去，视觉上就是「正文往下让出列表」 */
        transition: height 0.28s ease;
    }

    .read-toc__head {
        display: flex;
        align-items: center;
        gap: 8px;
        margin: 0;
        /* 左右不再另加：留白已由 .read-layout 统一给出，
           这样「← 文章列表」与下面的正文标题共用同一条左边界 */
        padding: 12px 0;
    }

    .read-toc__close {
        display: inline-flex;
        align-items: center;
        margin-left: auto;
        padding: 4px 8px;
        border: none;
        border-radius: 6px;
        background: none;
        color: var(--text-muted);
        font-size: 1rem;
        font-family: inherit;
        cursor: pointer;
    }

    .read-toc__list {
        /* 列表整体展开，不单独开滚动区：滚动交给页面，
           手指在列表上滑动时整个页面一起动，手感与正文一致 */
        padding: 0 0 12px;
        /* 分隔线画在列表底部而不是容器上：
           容器收起时高度为 0，边框会留下一条孤零零的横线 */
        border-bottom: 1px solid var(--border);
    }

    /* 条目本身的左右内边距去掉：留白已由 .read-layout 给出，
       保留的话每条标题会比正文标题多缩进一截，看着像没对齐。
       高亮底色（is-current）会铺满整行，点击区域反而更大。 */
    .read-toc__link {
        padding-left: 0;
        padding-right: 0;
        border-left: none;
        border-radius: 6px;
    }
}

/* ---------- 左栏分类 ---------- */
:root {
    /* 三页共用几何：左栏宽 / 栏与正文的间距 / 右侧大纲宽。
       列表页、控制台、阅读页的左栏都取 --rail-w，那条竖线才会落在同一个 x 上。 */
    --rail-w: 200px;
    --rail-gap: 24px;
    --outline-w: 200px;
}

/* 分类栏脱离文档流、贴在视口最左边：这样内容就能在整页宽度里居中。
   它自己滚动，滚到底时会被页脚盖住（页脚有底色，且 z-index 更高）。 */
.side-rail {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--rail-w);
    box-sizing: border-box;
    /* 左右对称留白：条目的左边缘与阅读页左栏对齐（12 + 2px 高亮条 + 10 内边距） */
    padding: 24px 12px;
    overflow-y: auto;
    border-right: 1px solid var(--border);
}

/* 内容居中，同时左右各让开「分类栏 + 间距」的宽度。
   屏幕不够宽时取较小值自动收窄，因此内容永远不会压到分类栏底下；
   够宽时就是完整的 --content-w（访客列表 800px / 控制台 860px）。 */
.page-content--rail {
    max-width: min(
        var(--content-w, var(--page-max)),
        calc(100% - 2 * (var(--rail-w) + var(--rail-gap)))
    );
}

.page-content--rail-wide {
    --content-w: var(--content-max);
}

/* 页脚压住固定栏的底部：它是整宽且不透明的，竖线不会穿进页脚里 */
.page-shell > .site-footer {
    position: relative;
    z-index: 2;
}

/* 栏脱离文档流后自己滚动，里面的列表不必再吸顶 */
.side-rail .category-nav {
    position: static;
}

@media (max-width: 1100px) {
    /* 窄屏：分类栏回到文档流里、落在内容上方，内容恢复原宽度 */
    .side-rail {
        position: static;
        width: auto;
        padding: 12px var(--page-gutter);
        border-right: 0;
        border-bottom: 1px solid var(--border);
        overflow-x: auto;
    }

    .side-rail .category-nav {
        flex-direction: row;
        flex-wrap: wrap;
        gap: 4px;
    }

    /* 栏已经不在两侧，内容恢复原来的最大宽度 */
    .page-content--rail {
        max-width: var(--content-w, var(--page-max));
    }
}

.category-nav {
    display: flex;
    flex-direction: column;
}

/* 与阅读页左栏的文章列表（.read-toc__link）同一套外观：
   两行截断、圆角、左侧 2px 高亮条，字号与间距也一致 */
.category-nav__item {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;

    padding: 7px 10px;
    margin-bottom: 2px;
    border-radius: 6px;
    border-left: 2px solid transparent;

    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--text-muted);
    text-decoration: none;
    transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}

.category-nav__item:hover {
    color: var(--text);
    background: var(--bg-subtle);
}

/* 当前分类：与阅读页「正在读的那篇」同一套高亮（蓝字 + 左侧蓝条 + 底色） */
.category-nav__item.is-active {
    color: var(--link);
    border-left-color: var(--link);
    background: var(--bg-subtle);
    font-weight: 600;
}

/* 控制台左栏：每行 = 分类（链接）+ 悬停出现的「+ 子分类 / 改名 / 删除」 */
.category-nav__row {
    display: flex;
    align-items: center;
    gap: 2px;
}

.category-nav__row .category-nav__item {
    flex: 1;
    min-width: 0;
}

.category-nav__act {
    flex: none;
    padding: 2px 4px;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.75rem;
    line-height: 1;
    cursor: pointer;
    /* 平时不显示，悬停该行或键盘聚焦时才出现，左栏才不会太花 */
    opacity: 0;
}

.category-nav__row:hover .category-nav__act,
.category-nav__act:focus-visible {
    opacity: 1;
}

.category-nav__act:hover {
    background: var(--bg-subtle);
    color: var(--link);
}

/* 「＋ 新增分类」与其它项区分开：虚线边框 */
/* 「＋ 新增分类」：与分类项同样的字号 / 圆角 / 内边距，用虚线边框区分出「这是个动作」 */
.category-nav__add {
    margin-top: 6px;
    padding: 7px 10px;
    border: 1px dashed var(--border-strong);
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.8125rem;
    line-height: 1.5;
    text-align: left;
    cursor: pointer;
}

.category-nav__add:hover {
    border-color: var(--link);
    color: var(--link);
}


.editor-cats__label {
    font-size: 0.8125rem;
    color: var(--text-muted);
}


/* 顶栏第二行：独占整行（语言切换有 13 项，和标题挤一行会把右侧按钮挤下去） */
.api-header__row {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
}

/* 分类的全选 / 反选：小号文字按钮，推到此行末尾 */
.editor-cats__acts {
    display: inline-flex;
    gap: 10px;
}

.editor-cats__act {
    padding: 0;
    border: 0;
    background: none;
    color: var(--link);
    font-size: 0.8125rem;
    line-height: 1.5;
    cursor: pointer;
}

.editor-cats__act:hover {
    text-decoration: underline;
}

/* ---------- 访客侧的分类标签 ---------- */
/* 沿用控制台那套 .badge 胶囊外观，一行排开、放不下自动换行 */
.article-item__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 8px 0 2px;
}

.article-read__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 10px 0 4px;
}

/* ---------- 阅读页：有左侧文章列表时（read-layout--with-toc） ---------- */
/* 页头已经显示标题，正文里那个一级标题（Markdown 普遍以 "# 标题" 开头）就是重复，隐掉它。
   只处理「正文下第一个一级标题」：其它级别的标题、段落、代码块一律不动，
   正文以段落开头时也不会被吞掉。 */
.read-layout--with-toc .markdown-body > h1:first-of-type {
    display: none;
}

/* ---------- 控制台左栏：拖动调整分类的父子关系 ---------- */
/* 把手平时不显示，悬停该行或键盘聚焦时才出现（与 ＋ / ✎ / ✕ 保持一致） */
.category-nav__drag {
    flex: none;
    padding: 2px;
    border-radius: 4px;
    color: var(--text-muted);
    font-size: 0.75rem;
    line-height: 1;
    cursor: grab;
    opacity: 0;
}

.category-nav__row:hover .category-nav__drag,
.category-nav__drag:focus-visible {
    opacity: 1;
}

.category-nav__drag:hover {
    background: var(--bg-subtle);
    color: var(--link);
}

.category-nav__drag:active {
    cursor: grabbing;
}

/* 拖动中：正文别被选中；可落脚的行用虚线框标出 */
.category-nav.is-dragging .category-nav__item {
    user-select: none;
}

.category-nav__row.is-drop-target {
    outline: 2px dashed var(--link);
    outline-offset: -2px;
    border-radius: 6px;
}

/* 拖到列表空白处 = 挂回顶级，用「＋ 新增分类」那条高亮提示 */
.category-nav.is-drop-root .category-nav__add {
    border-color: var(--link);
    color: var(--link);
}
