/**
 * 桌面端（Electron）专属样式
 *
 * 作用域全部挂在 <html> 的 .kb-desktop 上，由 js/desktop-bridge.js 在探测到
 * window.kbDesktop 后添加——网页版这个类不存在，下面每一条规则都天然不生效。
 * 两端因此可以共用同一份 CSS，不必分包。
 */

.kb-desktop {
    /* 当前桌面分页器：1px 顶边框 + 65px 内容区。侧栏版本栏复用同一高度口径。 */
    --kb-desktop-bottom-bar-height: 66px;
    /* 交通灯让位高度。侧栏与详情全屏头共用，两处各写一个数字迟早会漂移 */
    --kb-desktop-titlebar-height: 38px;
}

/* ============================================================
 * macOS 交通灯让位
 *
 * titleBarStyle:'hiddenInset' 没有标题栏，但交通灯按钮仍浮在窗口左上角
 * （主进程定位在 x=16,y=18），正压在侧栏顶部的用户头像上。
 *
 * 用 sticky 的伪元素而不是 padding-top：#sidebar 自身是滚动容器，
 * 只加 padding 的话内容一滚就又钻到三个圆点底下去了。
 * 只在 macOS 生效；全屏时交通灯收起，那条要还给内容。
 * ============================================================ */
.kb-desktop-mac:not(.kb-desktop-fullscreen) #sidebar::before {
    content: '';
    /* #sidebar 现在是 flex 容器，伪元素也是 flex 项：
       不写 flex 就会被默认的 flex-shrink:1 压成 0 高，让位等于没做 */
    flex: 0 0 var(--kb-desktop-titlebar-height);
    position: sticky;
    top: 0;
    z-index: 3;
    /* background 默认不继承，这里用 inherit 关键字强制取 #sidebar 的底色，
       深浅主题各自成立，不必再写一遍色值 */
    background: inherit;
    -webkit-app-region: drag;
}

/* 整窗拖动：没有系统标题栏后，顶栏空白处要能拖窗，否则窗口挪不动 */
.kb-desktop #topNav {
    -webkit-app-region: drag;
}

/* 拖动区里的可交互元素必须显式排除，否则点不动 */
.kb-desktop #topNav button,
.kb-desktop #topNav a,
.kb-desktop #topNav input,
.kb-desktop #topNav select {
    -webkit-app-region: no-drag;
}

/*
 * 盖在顶栏拖动带上的浮层。
 *
 * 拖动区是**几何蒙版**，不参与 z-index 层叠：只有显式写了 no-drag 的元素才会被从
 * 蒙版里减掉，单纯「画在上面」不算。AI 抽屉是 position:fixed; top:0 的全高浮层
 * （ai-search.css 的 .ai-drawer），几何上正压着上面那条 0~64px 的拖动带，
 * 于是它标题栏那一行（重建索引 / 清空 RAG 两个按钮就在这里）连 hover 都不触发，
 * 鼠标按下变成拖窗口。
 *
 * 但**反过来把整块浮层标成 no-drag 也不行**：抽屉宽 88vw、高占满整窗，那样一打开
 * 抽屉就等于把整条拖动带都减没了，窗口再也拖不动。两件事要同时成立，只能分开处理：
 *
 *   - 抽屉标题栏接过 #topNav 的角色——它自己就是拖动区，空白处照样拖窗；
 *   - 标题栏里的可交互元素逐个排除，与上面 #topNav 那一组同构；
 *   - 各种居中弹框只排除**内容盒**，不排除铺满整窗的遮罩，理由同上。
 *
 * 遮罩不排除的代价：顶栏那 64px 内点遮罩不会关闭弹框（变成拖窗）。遮罩上本来
 * 也没有别的东西可点，比「弹框一开窗口就钉死」划算得多。
 */
.kb-desktop .ai-drawer-header {
    -webkit-app-region: drag;
}

.kb-desktop .ai-drawer-header button,
.kb-desktop .ai-drawer-header .ai-reindex-btn,
.kb-desktop .ai-drawer-header .ai-clear-rag-btn,
.kb-desktop .ai-drawer-header .ai-drawer-close,
.kb-desktop .utils-dialog .dialog-content,
.kb-desktop .reindex-dialog-panel,
.kb-desktop .kb-update-dialog {
    -webkit-app-region: no-drag;
}

/* ============================================================
 * 备案栏
 *
 * 桌面应用不是网站，底部的 ICP 备案条没有意义。
 * 藏掉之后必须把那 30px 高度还给内容——各视图的高度都写着
 * calc(100dvh - 64px - 30px)。这里的规则与 beian.css 里 body.pwa-mode
 * 那一组完全同构，改动时两处要一起看。
 * ============================================================ */
.kb-desktop .beian-info,
.kb-desktop .beian-info-login,
.kb-desktop .beian-info-settings,
.kb-desktop .mobile-beian-bar {
    display: none !important;
}

.kb-desktop #mainContentArea,
.kb-desktop .knowledge-area {
    height: calc(100dvh - 64px) !important;
}

/*
 * AI 助手悬浮球同理。
 *
 * ai-search.css 里的 bottom: 80px 是按「备案栏还在」定的（那行注释写着 Above the beian bar），
 * 桌面端把这条 30px 的栏藏了却没人把位置还回来，球就悬在离底边一大截的地方。
 * 桌面端没有东西要避让，与 right 对齐取常规 FAB 的边距即可。
 *
 * 不必再套 @media：这条是 .kb-desktop 限定的两级选择器，比 ai-float-trigger.css 里
 * max-width:1023px 那条单类规则权重高，无论视口多宽都压得住。
 * （注意那条移动端规则在桌面端**会**命中：主窗口 minWidth 虽是 1024，但页面缩放到 110% 以上
 * CSS 视口就窄过 1023 了——这与浏览器里放大页面的表现一致，桌面端专属规则都要能扛住这一点。）
 */
.kb-desktop .ai-trigger-dock {
    bottom: 24px;
}

.kb-desktop .knowledge-graph-view,
.kb-desktop .knowledge-stats-view,
.kb-desktop .plugin-market-view,
.kb-desktop .tools-view {
    height: calc(100dvh - 64px);
}

/*
 * 窄窗口（<1024px）下还要把**下外边距**也还回来。
 *
 * 三个全局视图在移动端布局里都会把 .mobile-main-content 的下边距收成
 * calc(30px + safe-area)，给固定定位的备案栏让位（见 knowledge-graph.css /
 * knowledge-stats.css / plugin-market.css 的移动端块）——而那三处各自只为
 * pwa-mode 写了豁免。桌面端同样藏了备案栏却没有对应豁免，于是把窗口拖窄后
 * 底部就空出这 30px，正是上面「高度」那一组栽过的同一个坑，只是换到了外边距上。
 *
 * 不用 @media 包住：这些外边距本来就只在 max-width:1023px 里才有值，
 * 宽窗口下这条规则是空转。也不能用 display-mode: standalone 代替 .kb-desktop——
 * Electron 窗口是否匹配那个媒体特性并不可靠。
 */
.kb-desktop body.kb-graph-mode .mobile-main-content,
.kb-desktop body.kb-stats-mode .mobile-main-content,
.kb-desktop body.kb-plugins-mode .mobile-main-content,
.kb-desktop body.kb-tools-mode .mobile-main-content {
    margin-bottom: 0 !important;
}

/*
 * 图谱要单独再压一次。
 *
 * knowledge-graph.css 里有 `body.kb-graph-mode .knowledge-graph-view { height: calc(100% - 30px) }`
 * 给固定定位的备案栏让位，并为 PWA 写了一条豁免——桌面端同样藏了备案栏，却没有对应的豁免，
 * 于是图谱底部空出 30px。
 *
 * 上面那条通用规则救不了它：两者权重都是 (0,2,0)，而 desktop.css 在 knowledge-graph.css
 * **之前**加载，同权重后来者胜。所以这里带上 body 把权重抬到 (0,3,0)。
 * 看板与插件页不受影响——它们的基础规则只有 (0,1,0)，通用那条已经压得住。
 */
.kb-desktop body.kb-graph-mode .knowledge-graph-view {
    height: 100%;
}

/* 详情全屏时主内容区占满整窗，与 beian.css 的同名例外保持一致。
   注意是 `body.detail-fullscreen`：.kb-desktop 挂在 <html>、detail-fullscreen 挂在 <body>，
   写成 .kb-desktop.detail-fullscreen 要求两个类在同一元素上，永远匹配不上 */
.kb-desktop body.detail-fullscreen #mainContentArea,
.kb-desktop body.detail-fullscreen .knowledge-area {
    height: 100dvh !important;
}

/* ============================================================
 * 详情全屏下的交通灯让位与拖动
 *
 * 全屏时侧栏（带让位伪元素）与 #topNav（带拖动区）**都被隐藏**，
 * 于是既没人给交通灯让位、也没人负责拖窗——标题被三个圆点压住，
 * 想拖窗口只会拖出一片选中的文字。这里让详情头把这两件事接过来。
 * ============================================================ */
/*
 * 用 padding-top 而不是 ::before 让位。
 *
 * detail.js 会把 #detailHeader 设成 display:flex，伪元素于是变成**行方向**上的
 * flex 项：它只把这一行撑高，标题仍然停在原地——表现就是「标题栏变高了，但交通灯
 * 照样压着标题」。侧栏那边能用 ::before 是因为它是列方向的 flex 容器。
 *
 * 这里不必担心 padding 被滚走：详情头本身不滚动，滚的是它下面的正文。
 */
.kb-desktop-mac:not(.kb-desktop-fullscreen) body.detail-fullscreen .detail-header {
    padding-top: var(--kb-desktop-titlebar-height);
}

.kb-desktop body.detail-fullscreen .detail-header {
    -webkit-app-region: drag;
}

/* 拖动区内部的文本是选不中的，所以标题与按钮都必须排除在外 */
.kb-desktop body.detail-fullscreen .detail-header button,
.kb-desktop body.detail-fullscreen .detail-header a,
.kb-desktop body.detail-fullscreen .detail-header input,
.kb-desktop body.detail-fullscreen .detail-header #detailTitle,
.kb-desktop body.detail-fullscreen .detail-header .detail-meta {
    -webkit-app-region: no-drag;
}

/* ============================================================
 * 全屏浮层（流程图预览 / 图表预览）的交通灯让位与拖动
 *
 * 与上面详情全屏是同一个问题：这两个浮层都是 inset:0，盖住了侧栏（让位）和
 * #topNav（拖动区），于是三个圆点直接压在预览标题上，窗口也拖不动了。
 * 处理方式沿用详情全屏那一套，保证几处全屏的手感一致。
 *
 * 让位高度改的是浮层自己的 `--kb-fullscreen-titlebar`（两个浮层各自声明默认 0），
 * 而不是直接写 padding：顶栏高度、以及流程图那边图的最大高度都从这个变量算，
 * 只在这里翻一个开关，网页版与非 mac 桌面端天然是 0，不必再写一遍还原规则。
 * 窗口进系统全屏后交通灯收起，`.kb-desktop-fullscreen` 让这条规则失效，
 * 那 38px 立刻还给内容。
 *
 * 变量声明在**浮层**而不是 <html> 上：只有真正盖满整窗、把系统标题栏区域一起吃掉的
 * 浮层才需要让位，挂在根节点上会让任何元素都能顺手取到一个它并不该关心的数字。
 * ============================================================ */
.kb-desktop-mac:not(.kb-desktop-fullscreen) .kb-mermaid-fullscreen,
.kb-desktop-mac:not(.kb-desktop-fullscreen) .ai-chart-fullscreen {
    --kb-fullscreen-titlebar: var(--kb-desktop-titlebar-height);
}

.kb-desktop .kb-mermaid-fullscreen-header,
.kb-desktop .ai-chart-fullscreen-header {
    -webkit-app-region: drag;
}

.kb-desktop .kb-mermaid-fullscreen-header button,
.kb-desktop .kb-mermaid-fullscreen-header h2,
.kb-desktop .ai-chart-fullscreen-header button,
.kb-desktop .ai-chart-fullscreen-header .ai-chart-fullscreen-title {
    -webkit-app-region: no-drag;
}

/* ============================================================
 * 侧栏底部的版本号
 * ============================================================ */

/* #sidebar 的 flex 列布局现在是**两端共用**的基础样式，见 styles/sidebar.css。
   这里不再重复声明，免得两处各写一份、迟早对不上。 */

/* flex 子项默认可压缩，不锁住的话标签树一长，上面的用户信息就会被挤扁。
   ⚠️ 必须排除 .sidebar-scroll：它靠 `flex: 1 1 auto` + `min-height: 0` 才能
   收缩到剩余高度并在内部滚动，被这条锁成 flex-shrink:0 就会撑破容器，
   而 #sidebar 现在是 overflow:hidden——表现是标签树被直接裁掉、怎么都滚不到。 */
.kb-desktop #sidebar > *:not(.sidebar-scroll) {
    flex-shrink: 0;
}

.kb-desktop .pagination-wrapper {
    height: var(--kb-desktop-bottom-bar-height);
}

.kb-desktop .pagination-container {
    height: calc(var(--kb-desktop-bottom-bar-height) - 1px);
    box-sizing: border-box;
}

.kb-desktop-footer {
    display: flex;
    align-items: center;
    flex: 0 0 var(--kb-desktop-bottom-bar-height);
    height: var(--kb-desktop-bottom-bar-height);
    margin-top: auto;
    padding: 0 16px;
    box-sizing: border-box;
    border-top: 1px solid var(--border-color, #e5e7eb);
}

[data-theme="dark"] .kb-desktop-footer,
html.dark .kb-desktop-footer {
    border-top-color: #374151;
}

.kb-desktop-version {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 6px 8px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary, #6b7280);
    font-size: 0.75rem;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.kb-desktop-version:hover {
    background: rgba(148, 163, 184, 0.16);
    color: var(--kb-accent, #2563eb);
}

/* ============================================================
 * 更新弹窗
 *
 * 六个状态与 video-hunter 的 UpdateNotification 一一对应：
 * checking / available / downloading / downloaded / error / no_update。
 * ============================================================ */

.kb-update-mask {
    position: fixed;
    inset: 0;
    z-index: 12500;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--kb-scrim-bg);
    backdrop-filter: blur(var(--kb-scrim-blur));
    -webkit-backdrop-filter: blur(var(--kb-scrim-blur));
}

.kb-update-dialog {
    width: 100%;
    max-width: 28rem;
    margin: 0 16px;
    padding: 24px;
    border: 1px solid var(--border-color, #e5e7eb);
    border-radius: 12px;
    background: var(--bg-secondary, #ffffff);
    box-shadow: 0 20px 48px rgba(15, 23, 42, 0.28);
}

.kb-update-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}

.kb-update-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 8px;
    font-size: 1.5rem;
}

.kb-update-icon--accent {
    background: rgba(37, 99, 235, 0.14);
    color: var(--kb-accent, #2563eb);
}

.kb-update-icon--success {
    background: rgba(16, 185, 129, 0.16);
    color: #059669;
}

.kb-update-icon--danger {
    background: rgba(220, 38, 38, 0.14);
    color: #dc2626;
}

.kb-update-title {
    margin: 0;
    font-size: 1.0625rem;
    font-weight: 600;
}

.kb-update-subtitle {
    margin: 2px 0 0;
    color: var(--text-secondary, #6b7280);
    font-size: 0.8125rem;
}

.kb-update-section {
    margin-bottom: 20px;
}

.kb-update-section-title {
    margin-bottom: 8px;
    font-size: 0.8125rem;
    font-weight: 500;
}

.kb-update-notes {
    padding: 12px;
    border-radius: 6px;
    background: rgba(148, 163, 184, 0.12);
    color: var(--text-secondary, #6b7280);
    font-size: 0.8125rem;
    line-height: 1.6;
    white-space: pre-wrap;
    max-height: 12rem;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.kb-update-hint {
    margin-bottom: 20px;
    color: var(--text-secondary, #6b7280);
    font-size: 0.75rem;
}

.kb-update-progress-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
    font-size: 0.8125rem;
}

.kb-update-percent {
    color: var(--kb-accent, #2563eb);
    font-weight: 500;
}

.kb-update-progress {
    height: 8px;
    border-radius: 999px;
    background: rgba(148, 163, 184, 0.24);
    overflow: hidden;
}

.kb-update-progress-bar {
    height: 100%;
    border-radius: 999px;
    background: var(--kb-accent, #2563eb);
    transition: width 0.3s ease;
}

.kb-update-banner {
    display: flex;
    /* 文案会折行，居中会让图标吊在两行中间；对齐到首行更稳 */
    align-items: flex-start;
    gap: 10px;
    /* 上方标题区已有 16px，这里再补 4px；下方给足到操作按钮的呼吸空间 */
    margin: 4px 0 24px;
    padding: 14px 16px;
    border-radius: 8px;
    font-size: 0.8125rem;
    line-height: 1.65;
}

.kb-update-banner > i {
    flex-shrink: 0;
    /*
     * 图标要与**首行**文本垂直居中，而不是与整段居中（文案会折行）。
     *
     * 做法是把图标撑成与首行等高的盒子，再在盒子里居中；高度就是 banner 自己的
     * font-size × line-height。不能靠 margin-top 去凑：图标字号（1rem）比正文
     * （0.8125rem）大，两者的行盒高度本就不同，凑出来的偏移换个字号就又歪了。
     */
    display: inline-flex;
    align-items: center;
    height: calc(0.8125rem * 1.65);
    font-size: 1rem;
}

.kb-update-banner--success {
    border: 1px solid rgba(16, 185, 129, 0.5);
    background: rgba(16, 185, 129, 0.14);
    color: #047857;
}

.kb-update-banner--danger {
    border: 1px solid rgba(220, 38, 38, 0.5);
    background: rgba(220, 38, 38, 0.12);
    color: #b91c1c;
    max-height: 12rem;
    overflow: auto;
}

/*
 * 暗色下的状态条。
 *
 * 亮色用的 #047857 / #b91c1c 是**深**绿与**深**红，专为浅色底挑的；暗色弹窗底色本身
 * 就深，深绿压深底几乎看不清文字。这里换成同色系的浅色，并把底色的透明度压低一点，
 * 免得半透明色块叠在深底上把文字进一步拉灰。
 */
[data-theme="dark"] .kb-update-banner--success {
    border-color: rgba(16, 185, 129, 0.42);
    background: rgba(16, 185, 129, 0.1);
    color: #6ee7b7;
}

[data-theme="dark"] .kb-update-banner--danger {
    border-color: rgba(248, 113, 113, 0.42);
    background: rgba(248, 113, 113, 0.1);
    color: #fca5a5;
}

/* 错误详情可能是一长串 URL 或堆栈，必须能断行，否则会把弹窗撑破 */
.kb-update-error-text {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.75rem;
    word-break: break-all;
}

.kb-update-actions {
    display: flex;
    gap: 12px;
}

.kb-update-action {
    flex: 1 1 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 38px;
    padding: 8px 16px;
    border: 1px solid var(--border-color, #e5e7eb);
    border-radius: 6px;
    background: transparent;
    color: inherit;
    font-size: 0.875rem;
    cursor: pointer;
    transition: background-color 0.2s ease;
}

.kb-update-action--primary {
    border-color: var(--kb-accent, #2563eb);
    background: var(--kb-accent, #2563eb);
    /* 同 .plugin-card-action--primary：深色主题下 --kb-accent 是浅色，白字会糊掉 */
    color: var(--text-inverse, #ffffff);
    font-weight: 500;
}

/* 禁用态统一走语义变量，不用 opacity 表达（见 docs/theme-control-states.md） */
.kb-update-action:disabled {
    cursor: not-allowed;
    border-color: var(--kb-control-disabled-border);
    background: var(--kb-control-disabled-bg);
    color: var(--kb-control-disabled-text);
}

/* ============================================================
 * 缩放百分比气泡（js/desktop-zoom.js；只有桌面端会创建这个节点）
 *
 * 主进程处理完 Cmd/Ctrl + = / - / 0 之后页面在右上角闪一下「125%」，1 秒淡出。
 * 颜色全走 --kb-* 变量（reading-theme.css 明暗各一套），不写 dark 覆盖。
 * z-index 要压过更新蒙层（12500）与 AI 抽屉：缩放随时可能发生，气泡不该被任何浮层挡住。
 * ============================================================ */
.kb-zoom-tip {
    position: fixed;
    top: 76px;                      /* 顶栏 64px 之下，避开 macOS 交通灯与拖动带 */
    right: 20px;
    z-index: 20000;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--kb-paper-line);
    border-radius: 999px;
    background: var(--kb-paper-panel);
    color: var(--kb-ink);
    box-shadow: var(--kb-paper-shadow);
    font-size: 0.8125rem;
    line-height: 1.2;
    font-variant-numeric: tabular-nums;   /* 90% → 100% 宽度不抖 */
    pointer-events: none;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
}

.kb-zoom-tip.is-visible {
    opacity: 1;
    transform: none;
}

.kb-zoom-tip-icon {
    font-size: 1rem;
    color: var(--kb-ink-soft);
}

.kb-zoom-tip-value {
    font-weight: 600;
    color: var(--kb-accent);
}

/* 到顶/到底：数值变灰，表示再按也不会动了 */
.kb-zoom-tip.is-limit .kb-zoom-tip-value {
    color: var(--kb-ink-muted);
}

@media (prefers-reduced-motion: reduce) {
    .kb-zoom-tip {
        transition: none;
    }
}
