/* ============================================================
   Known UI · 全局样式
   ------------------------------------------------------------
   1. 变量 & 双色主题
   2. 基础重置 & 图标
   3. 布局（侧边栏 / 顶栏 / 内容区）
   4. 全局浮层（遮罩 / 加载条 / 提示 / 命令面板）
   5. 通用组件（按钮 / 卡片 / 表单 / 表格 / 反馈 …）
   6. 响应式
   ============================================================ */

/* ============================================================
   1. 变量 & 双色主题
   ============================================================ */
:root {
    --font-sans: 'Plus Jakarta Sans', 'Inter', system-ui, -apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Code', Consolas, 'Liberation Mono', monospace;
    --transition: 0.2s ease;
    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 16px;
    --shadow-sm: 0 1px 2px rgba(16,24,40,.05), 0 1px 3px rgba(16,24,40,.05);
    --shadow-md: 0 2px 4px rgba(16,24,40,.04), 0 16px 32px -16px rgba(16,24,40,.16);
    --shadow-lg: 0 8px 24px rgba(16,24,40,.08), 0 40px 80px -32px rgba(23,43,99,.32);

    --bg-app: #F4F7FC;
    --bg-sidebar: #FFFFFF;
    --bg-card: #FFFFFF;
    --bg-hover: #EAF0F8;
    --bg-input: #FFFFFF;
    /* 表面分层（插件页常用）：elevated=浮于卡片之上的浅色面板；soft=表头/分区条的极淡底色；subtle=更淡的占位底 */
    --bg-elevated: #FAFBFF;
    --bg-soft: #F7F8FC;
    --bg-subtle: #F4F7FC;
    --text-primary: #0B1424;
    --text-secondary: #2A3752;
    --text-muted: #61708C;
    --border-color: #E4EAF3;
    --border-light: #D3DDEC;
    --primary-color: #2F5BFF;
    --primary-hover: color-mix(in srgb, var(--primary-color) 70%, white);
    --primary-soft: color-mix(in srgb, var(--primary-color) 8%, white);
    --success-color: #12B76A;
    --success-soft: rgba(18,183,106,.10);
    --warning-color: #E08A00;
    --warning-soft: rgba(224,138,0,.12);
    --danger-color: #F0568A;
    --danger-soft: rgba(240,86,138,.10);
    --info-color: #0EA5E9;
    --info-soft: rgba(14,165,233,.10);
    --tag-bg: #F7F9FD;
    --tag-text: #2A3752;
    --badge-bg: #F7F9FD;
    --badge-text: #2A3752;
    --scrollbar-thumb: #D3DDEC;
    --scrollbar-track: #EAF0F8;
    --skeleton-base: #E4EAF3;
    --skeleton-shine: #F7F9FD;

    /* 表格表头 / 斑马纹底色：比 --bg-hover 更淡，贴近白底卡片，弱化"数据网格"感；
       表头是较深的那条带，斑马纹再淡一档，保证「表头 > 斑马纹 > 纯白行」的层次 */
    --table-head-bg: #F4F7FC;
    --table-stripe-bg: #F9FBFE;

    --sidebar-width: 240px;
    --sidebar-width-collapsed: 72px;
    --topbar-height: 60px;

    /* 界面密度（默认＝标准）：zoom 整体缩放字号/图标/布局，控件/顶栏/菜单/表格/分页器留白各自收紧 */
    --ctl-pad-y: 10px;
    --ctl-h: 39.2px;
    --btn-pad-y: 8px;
    --topbar-pad-y: 14px;
    --nav-pad-y: 10px;
    --cell-pad-y: 13px;
    --pager-h: 34px;
    --pager-pad-x: 8px;
    zoom: 1;
}

/* 紧凑 / 宽松两档：整页随 zoom 等比缩放，控件/顶栏/菜单/表格/分页器留白再额外收紧或舒展 */
[data-density='small'] {
    --ctl-pad-y: 6px;
    --ctl-h: 32px;
    --btn-pad-y: 5px;
    --topbar-pad-y: 9px;
    --nav-pad-y: 6px;
    --cell-pad-y: 8px;
    --pager-h: 28px;
    --pager-pad-x: 6px;
    --topbar-height: 50px;
    zoom: .9;
}
[data-density='large'] {
    --ctl-pad-y: 13px;
    --ctl-h: 46px;
    --btn-pad-y: 11px;
    --topbar-pad-y: 18px;
    --nav-pad-y: 13px;
    --cell-pad-y: 17px;
    --pager-h: 40px;
    --pager-pad-x: 10px;
    --topbar-height: 70px;
    zoom: 1.1;
}

[data-theme='dark'] {
    --bg-app: #090E18;
    --bg-sidebar: #111A2B;
    --bg-card: #111A2B;
    --bg-hover: #0B1220;
    --bg-input: #0D1626;
    --bg-elevated: #16223A;
    --bg-soft: #0F1B2E;
    --bg-subtle: #0D1626;
    --text-primary: #E9EFF9;
    --text-secondary: #B7C4DB;
    --text-muted: #8493AD;
    --border-color: #1D2A41;
    --border-light: #283a58;
    --primary-color: #5B7CFF;
    --primary-hover: color-mix(in srgb, var(--primary-color) 80%, white);
    --primary-soft: color-mix(in srgb, var(--primary-color) 18%, #090E18);
    --success-color: #34D399;
    --success-soft: rgba(52,211,153,.12);
    --warning-color: #FBB03B;
    --warning-soft: rgba(251,176,59,.12);
    --danger-color: #FF6E9C;
    --danger-soft: rgba(255,110,156,.12);
    --info-color: #38BDF8;
    --info-soft: rgba(56,189,248,.12);
    --tag-bg: #0D1626;
    --tag-text: #B7C4DB;
    --badge-bg: #0D1626;
    --badge-text: #B7C4DB;
    --scrollbar-thumb: #283a58;
    --scrollbar-track: #0D1626;
    --skeleton-base: #1D2A41;
    --skeleton-shine: #283a58;

    --table-head-bg: #16223A;
    --table-stripe-bg: #131D31;
}

/* ============================================================
   2. 基础重置 & 图标
   ============================================================ */
* { box-sizing: border-box; margin: 0; padding: 0; }

html { height: 100%; }

body {
    font-family: var(--font-sans);
    background: var(--bg-app);
    color: var(--text-primary);
    transition: background var(--transition), color var(--transition);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    height: 100%;
    overflow: hidden;
}

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--scrollbar-track); }
::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 20px; }

/* 内联图标：由 Known.Icon 组件渲染，随字号缩放 */
.icon { width: 1em; height: 1em; flex-shrink: 0; vertical-align: -0.125em; }
.icon-pulse { animation: icon-spin 1s linear infinite; }
@keyframes icon-spin { to { transform: rotate(360deg); } }

/* 启动加载动画（index.html 使用） */
.loading-progress { position: relative; display: block; width: 8rem; height: 8rem; margin: 20vh auto 1rem auto; }
    .loading-progress circle { fill: none; stroke: #e0e0e0; stroke-width: .6rem; transform-origin: 50% 50%; transform: rotate(-90deg); }
    .loading-progress circle:last-child { stroke: #2F5BFF; stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * .8), 500%; transition: stroke-dasharray .05s ease-in-out; }
.loading-progress-text { position: absolute; text-align: center; font-weight: bold; inset: calc(20vh + 3.25rem) 0 auto .2rem; }
    .loading-progress-text::after { content: var(--blazor-load-percentage-text, 'Loading'); }

/* ============================================================
   3. 布局
   ============================================================ */
#app { width: 100%; height: 100%; overflow: hidden; }
.app-layout { display: flex; width: 100%; height: 100%; }

/* ── 侧边栏 ── */
.sidebar {
    width: var(--sidebar-width);
    background: var(--bg-sidebar);
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    padding: 16px 12px;
    transition: width .28s cubic-bezier(.4, 0, .2, 1), background var(--transition), border var(--transition);
    flex-shrink: 0;
    overflow: visible;
    position: relative;
    z-index: 110;
}
    .sidebar.collapsed { width: var(--sidebar-width-collapsed); }

.logo-area {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 3px 10px 18px 10px;
    border-bottom: 1px solid var(--border-light);
    margin-bottom: 16px;
    position: relative;
    transition: padding .28s ease;
}
.logo-icon {
    width: 32px;
    height: 32px;
    background: var(--primary-color);
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-weight: 600;
    font-size: 16px;
    box-shadow: 0 6px 12px rgba(47, 91, 255, .25);
    flex-shrink: 0;
}
    .logo-icon--img { background: none; box-shadow: none; }
    .logo-img {
        width: 32px;
        height: 32px;
        display: block;
        object-fit: contain;
        border-radius: 10px;
        flex-shrink: 0;
    }
.logo-text {
    font-weight: 600;
    font-size: 18px;
    letter-spacing: -.3px;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    transition: opacity .2s ease, width .2s ease;
}
    .logo-text span { color: var(--primary-color); }

.collapse-btn {
    margin-left: auto;
    width: 26px;
    height: 26px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 50%;
    box-shadow: var(--shadow-sm);
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    transition: all var(--transition);
    flex-shrink: 0;
}
    .collapse-btn:hover { background: var(--primary-soft); color: var(--primary-color); border-color: var(--primary-color); }
    .collapse-btn .icon { transition: transform .28s ease; }

    .sidebar.collapsed .logo-area { justify-content: center; padding: 3px 0 18px; position: static; }
    .sidebar.collapsed .logo-text { display: none; }
    .sidebar.collapsed .collapse-btn {
        position: absolute;
        right: -13px;
        top: 23px;
        z-index: 60;
    }

/* ── 导航 ── */
.nav-list { display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden; flex: 1; }

.nav-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: var(--nav-pad-y) 14px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    font-weight: 500;
    font-size: 14px;
    transition: all var(--transition);
    cursor: default;
    margin-bottom: 4px;
    position: relative;
    white-space: nowrap;
}
    .nav-item > .icon { width: 20px; font-size: 16px; color: var(--text-muted); transition: color var(--transition); text-align: center; flex-shrink: 0; }
    .nav-item.active { background: var(--primary-soft); color: var(--primary-color); }
        .nav-item.active > .icon { color: var(--primary-color); }
    .nav-item:not(.active):hover { background: var(--bg-hover); color: var(--text-primary); }
    .nav-badge {
        margin-left: auto;
        flex-shrink: 0;
        min-width: 22px;
        text-align: center;
        padding: 1px 7px;
        font-size: 11px;
        font-weight: 600;
        line-height: 1.7;
        color: var(--text-muted);
        background: var(--bg-hover);
        border: 1px solid var(--border-color);
        border-radius: 999px;
    }
    .nav-item.active > .nav-badge { color: var(--primary-color); background: var(--primary-soft); border-color: transparent; }

    .sidebar.collapsed .nav-item { justify-content: center; padding: 12px; }
    .sidebar.collapsed .nav-item > .icon { width: auto; font-size: 17px; }
    .sidebar.collapsed .nav-item > span { display: none; }
    .sidebar.collapsed .nav-divider { margin: 16px 8px; }

    .sidebar.collapsed .nav-item::after {
        content: attr(data-tip);
        position: absolute;
        left: calc(100% + 14px);
        top: 50%;
        transform: translateY(-50%) translateX(-4px);
        background: var(--text-primary);
        color: var(--bg-card);
        padding: 6px 10px;
        border-radius: 6px;
        font-size: 12px;
        white-space: nowrap;
        opacity: 0;
        pointer-events: none;
        transition: all .16s ease;
        z-index: 200;
        font-weight: 500;
        box-shadow: var(--shadow-md);
    }
    .sidebar.collapsed .nav-item:hover::after { opacity: 1; transform: translateY(-50%) translateX(0); }
    .sidebar.collapsed .nav-item[data-tip]::before { display: none; }

/* 折叠态：放开滚动容器，让右侧浮出 Tooltip 不被裁剪、也不再出现多余滚动条 */
.sidebar.collapsed .nav-list { overflow: visible; }
/* 展开态：菜单已显示文字，抑制 data-tip 浮层，避免顶部被裁剪的半截 Tooltip */
.sidebar:not(.collapsed) .nav-item:hover::after,
.sidebar:not(.collapsed) .nav-item:hover::before { opacity: 0; }

/* ── 导航分组（多级嵌套）── */
.nav-group { display: flex; flex-direction: column; margin-bottom: 4px; }
.nav-group-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    font-size: 14px;
    cursor: pointer;
    transition: all var(--transition);
    white-space: nowrap;
    user-select: none;
}
    .nav-group-header > .icon { width: 20px; font-size: 16px; color: var(--text-muted); text-align: center; flex-shrink: 0; transition: color var(--transition); }
    .nav-group-header > span { flex: 1; }
    .nav-group-header:hover { background: var(--bg-hover); color: var(--text-primary); }
    .nav-group-header.active { color: var(--primary-color); }
        .nav-group-header.active > .icon { color: var(--primary-color); }
    /* 分组头徽标：上方 span{flex:1} 会把徽标也拉成一条，这里收回成贴内容的胶囊 */
    .nav-group-header > .nav-badge { flex: 0 0 auto; }
    .nav-group-header.active > .nav-badge { color: var(--primary-color); background: var(--primary-soft); border-color: transparent; }
.nav-arrow { width: auto !important; font-size: 12px !important; margin-left: auto; transition: transform .2s ease; }
    .nav-group.open > .nav-group-header .nav-arrow { transform: rotate(90deg); }
.nav-sub {
    display: flex;
    flex-direction: column;
    margin: 2px 0 0 20px;
    padding-left: 10px;
    border-left: 1px solid var(--border-light);
}
    .nav-sub .nav-item { font-size: 13.5px; padding: calc(var(--nav-pad-y) - 2px) 12px; }
    .nav-sub .nav-group-header { font-size: 13.5px; padding: 8px 12px; }

/* ── 折叠态悬浮子菜单 / Tooltip 浮层 ── */
.sidebar.collapsed .nav-collapsed-group,
.sidebar.collapsed .nav-collapsed-leaf { position: relative; }
.nav-flyout {
    display: none;
    position: absolute;
    left: 100%;
    top: -6px;
    min-width: 190px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    z-index: 500;
}
    .sidebar.collapsed .nav-item:hover > .nav-flyout { display: block; }
.nav-flyout-title {
    padding: 6px 10px 5px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .4px;
    color: var(--text-muted);
}
.fly-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 13.5px;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: all var(--transition);
}
    .fly-item > .icon { width: 16px; text-align: center; color: var(--text-muted); flex-shrink: 0; }
    .fly-item:hover { background: var(--bg-hover); color: var(--text-primary); }
    .fly-item:hover > .icon { color: var(--primary-color); }
    .fly-item.active { background: var(--primary-soft); color: var(--primary-color); }
    .fly-item.active > .icon { color: var(--primary-color); }
.fly-group { margin-top: 2px; }
.fly-group-title {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px 4px;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
}
    .fly-group-title > .icon { width: 14px; }
.fly-children { margin-left: 12px; padding-left: 8px; border-left: 1px solid var(--border-light); }
/* 叶子：单行 Tooltip 药丸，替代被裁剪的伪元素 Tooltip */
.nav-flyout.nav-flyout-single {
    top: 50%;
    transform: translateY(-50%);
    min-width: 0;
    padding: 6px 11px;
    border: none;
    border-radius: 6px;
    background: var(--text-primary);
    color: var(--bg-card);
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
    box-shadow: var(--shadow-md);
    cursor: pointer;
}

.nav-divider { height: 1px; background: var(--border-light); margin: 16px 10px; flex-shrink: 0; }

/* ── 用户卡片 ── */
.user-wrapper { margin-top: auto; padding-top: 16px; position: relative; flex-shrink: 0; border-top: 1px solid var(--border-light); }

.user-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: background var(--transition);
    border: 1px solid transparent;
}
    .user-card:hover { background: var(--bg-hover); }

.user-avatar {
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: linear-gradient(135deg, var(--primary-color), #7C5CFF);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 14px;
    flex-shrink: 0;
    box-shadow: 0 4px 10px rgba(47, 91, 255, .22);
}
.user-info { flex: 1; min-width: 0; }
.user-name { font-size: 13.5px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-role { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-chevron { font-size: 11px; color: var(--text-muted); transition: transform .2s ease; flex-shrink: 0; }
    .user-card.open .user-chevron { transform: rotate(180deg); }
    /* 经 Dropdown 组件驱动时，open 态挂在 .dropdown 根上 */
    .dropdown.open .user-card { background: var(--bg-hover); }
    .dropdown.open .user-chevron { transform: rotate(180deg); }

    .sidebar.collapsed .user-card { justify-content: center; padding: 8px; }
    .sidebar.collapsed .user-info,
    .sidebar.collapsed .user-chevron { display: none; }

/* ── 顶栏身份：头像+姓名下拉（个人中心 / 偏好设置 / 退出登录）── */
.user-trigger-wrapper { position: relative; }
.user-trigger {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 8px 4px 4px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid transparent;
    cursor: pointer;
    transition: background var(--transition), border var(--transition);
    flex-shrink: 0;
}
    .user-trigger:hover { background: var(--bg-hover); }
    .dropdown.open .user-trigger { background: var(--bg-hover); border-color: var(--border-color); }
    .user-trigger .user-avatar { width: 32px; height: 32px; border-radius: 9px; font-size: 13px; box-shadow: none; }
    .user-trigger .user-meta { display: flex; flex-direction: column; gap: 1px; line-height: 1.15; min-width: 0; }
    .user-trigger .user-name { font-size: 13px; max-width: 120px; }
    .user-trigger .user-role { font-size: 11px; }
    .user-trigger .user-chevron { transition: transform .2s ease; }
    .dropdown.open .user-trigger .user-chevron { transform: rotate(180deg); }

.user-menu {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 250px;
    max-width: calc(100vw - 40px);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    opacity: 0;
    visibility: hidden;
    transform: scale(.95) translateY(-4px);
    transform-origin: top right;
    transition: all .2s ease;
    z-index: 1000;
}
    .user-menu.open { transform: scale(1) translateY(0); opacity: 1; visibility: visible; }
.um-head { display: flex; align-items: center; gap: 10px; padding: 10px; }
.um-head .user-avatar.lg { width: 40px; height: 40px; border-radius: 12px; font-size: 16px; }
.um-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.um-name { font-size: 14px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.um-role { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.um-profile-btn { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 1px solid var(--border-light); border-radius: 9px; background: transparent; color: var(--text-muted); cursor: pointer; transition: background .15s, color .15s, border-color .15s; }
.um-profile-btn:hover { background: var(--primary-soft); color: var(--primary-color); border-color: var(--primary-color); }
.um-group { padding: 2px 0; }
.um-group-title { font-size: 11px; font-weight: 600; color: var(--text-muted); letter-spacing: .4px; padding: 8px 10px 4px; }
.um-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; }
.um-row-label { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-secondary); white-space: nowrap; }
    .um-row-label > .icon { color: var(--text-muted); }
.um-seg { display: inline-flex; gap: 4px; }
.um-seg-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 9px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-color);
    background: var(--bg-input);
    color: var(--text-secondary);
    font-family: var(--font-sans);
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    transition: all var(--transition);
}
    .um-seg-btn:hover { border-color: var(--primary-color); color: var(--primary-color); }
    .um-seg-btn.active { background: var(--primary-color); border-color: var(--primary-color); color: #fff; }

/* 「布局」这类候选名较长、条数由宿主决定的行：面板固定 250px 宽，按钮组装不下就整组换到第二行，
   标签靠左、按钮组靠右并在组内继续右对齐，避免出现横向溢出或裁字。 */
.um-row-wrap { align-items: flex-start; flex-wrap: wrap; }
    .um-row-wrap .um-seg { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; row-gap: 4px; }
    .um-row-wrap .um-row-label { line-height: 24px; }

/* 偏好设置面板内的语言 / 密度下拉：固定宽度、不参与伸缩，右栏整齐对齐（提高特异性以覆盖 .select 的 width:100%） */
.um-row .um-select { width: 128px; flex: none; min-height: 20px; }

/* 开发中心：偏好设置下方的一行放大图标入口，悬停 Tooltip 显模块名 */
.um-dev-row { display: flex; gap: 8px; padding: 4px 8px 8px; }
.um-dev-btn {
    width: 30px;
    height: 30px;
    padding: 0;
    border-radius: 8px;
    border: 1px solid var(--border-light);
    background: var(--bg-hover);
    color: var(--text-secondary);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all var(--transition);
}
    .um-dev-btn:hover {
        background: var(--primary-soft);
        color: var(--primary-color);
        border-color: var(--primary-color);
        transform: translateY(-2px);
    }

/* 主题色色卡：固定每行 5 个（9 预设 + 1 自定义 = 两行铺满），整体靠右 */
.um-swatches { display: grid; grid-template-columns: repeat(5, 20px); justify-content: end; align-items: center; gap: 8px 6px; }
.um-swatch {
    position: relative;
    width: 20px;
    height: 20px;
    padding: 0;
    border-radius: 50%;
    border: 2px solid transparent;
    outline: 1px solid var(--border-color);
    cursor: pointer;
    transition: transform var(--transition), outline-color var(--transition);
}
    .um-swatch:hover { transform: scale(1.12); }
    .um-swatch.active { outline-color: var(--text-primary); outline-width: 2px; }
.um-swatch-auto {
    background: conic-gradient(#F0568A, #FBB03B, #12B76A, #0EA5E9, #7C5CFF, #EC4899, #F0568A);
}
    .um-swatch-custom input[type='color'] {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        padding: 0;
        border: none;
        border-radius: 50%;
        opacity: 0;
        cursor: pointer;
    }

.context-menu {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 4px;
    width: 200px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px) scale(.97);
    transform-origin: bottom left;
    transition: all .18s ease;
    z-index: 300;
}
    .context-menu.open { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }

.context-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    color: var(--text-secondary);
    cursor: default;
    transition: all var(--transition);
}
    .context-item > .icon { width: 16px; font-size: 13px; color: var(--text-muted); text-align: center; }
    .context-item:hover { background: var(--bg-hover); color: var(--text-primary); }
        .context-item:hover > .icon { color: var(--primary-color); }
    .context-item.danger { color: var(--danger-color); }
        .context-item.danger > .icon { color: var(--danger-color); }
        .context-item.danger:hover { background: var(--danger-soft); }
.context-divider { height: 1px; background: var(--border-light); margin: 6px 4px; }

/* ── 主内容 ── */
.main-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }

.top-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: var(--topbar-pad-y) 28px;
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-color);
    transition: background var(--transition), border var(--transition);
    flex-shrink: 0;
    position: relative;
    z-index: 100;
    min-height: var(--topbar-height);
}
/* 标题可收缩并用省略号收尾：nowrap 让它默认拒绝变窄，窄屏下会把右侧按钮挤出屏幕。 */
.page-title { font-size: 19px; font-weight: 600; letter-spacing: -.3px; color: var(--text-primary); white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.top-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.top-left { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1 1 auto; }
.top-divider { width: 1px; height: 22px; background: var(--border-color); margin: 0 4px; flex-shrink: 0; opacity: .8; }
.crumb-sep { width: 1px; height: 18px; background: var(--border-color); flex-shrink: 0; opacity: .7; }
.page-crumb { min-width: 0; overflow: hidden; }
    .page-crumb .breadcrumb { font-size: 12.5px; gap: 6px; flex-wrap: nowrap; }
    .page-crumb .breadcrumb-item { gap: 5px; white-space: nowrap; }
    .page-crumb .breadcrumb-item.active { font-weight: 500; }
.page-host { display: contents; }

/* ── 导航页签条（多页签模式，挂在顶栏与内容区之间） ── */
.page-tabs {
    position: relative;
    display: flex;
    align-items: center;
    flex-shrink: 0;
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
    transition: background var(--transition), border var(--transition);
}
    /* 页签较多时横向滚动，不换行：换行会把这条细高条撑成两三行，压掉正文空间。
       滚动条隐去（条高仅 ~34px，出现滚动条会让页签抖动）。 */
    .page-tab-list {
        display: flex;
        align-items: center;
        flex: 1;
        min-width: 0;
        gap: 4px;
        padding: 5px 16px;
        overflow-x: auto;
        scrollbar-width: none;
    }
        .page-tab-list::-webkit-scrollbar { display: none; }
    /* 溢出收纳：known.pagetabs.fit 量完才摘掉这个类，量不到就一直藏着 */
    .page-tab-more.ovf { display: none; }
    /* 收成一颗无边框的方形图标按钮：描边胶囊夹在一排页签里，看着像个坏掉的页签 */
    .page-tab-more {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 26px;
        height: 26px;
        flex-shrink: 0;
        margin-right: 10px;
        border-radius: var(--radius-sm);
        color: var(--text-muted);
        cursor: pointer;
        user-select: none;
        transition: all var(--transition);
    }
        .page-tab-more:hover { background: var(--bg-hover); color: var(--text-primary); }
        .page-tab-more[aria-expanded="true"] { background: var(--primary-soft); color: var(--primary-color); }
        .page-tab-more svg { transition: transform var(--transition); }
        .page-tab-more[aria-expanded="true"] svg { transform: rotate(180deg); }
    /* 下拉贴着页签条右端向下展开：.main-content 是 overflow:hidden，靠右/靠下都不越界 */
    .page-tab-menu {
        position: absolute;
        top: 100%;
        right: 12px;
        z-index: 1001;
        margin-top: 2px;
        min-width: 200px;
        max-width: 320px;
        max-height: 60vh;
        overflow-y: auto;
        padding: 6px;
        background: var(--bg-card);
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
        box-shadow: var(--shadow-lg);
    }
        /* 数量从触发按钮挪到这里：按钮只留一颗尖角，总数在面板里交代 */
        .page-tab-menu-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
            padding: 2px 8px 6px;
            margin-bottom: 4px;
            border-bottom: 1px solid var(--border-color);
            font-size: 11.5px;
            color: var(--text-muted);
        }
            .page-tab-menu-head b { font-weight: 600; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
    .page-tab-menu-item {
        display: flex;
        align-items: center;
        gap: 6px;
        padding: 6px 8px;
        border-radius: var(--radius-sm);
        font-size: 12.5px;
        color: var(--text-secondary);
        cursor: pointer;
        user-select: none;
        transition: all var(--transition);
    }
        .page-tab-menu-item:hover { background: var(--bg-hover); color: var(--text-primary); }
        .page-tab-menu-item.active { background: var(--primary-soft); color: var(--primary-color); font-weight: 500; }

.page-tab {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    max-width: 220px;
    padding: 4px 7px 4px 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
    transition: all var(--transition);
}
    .page-tab:hover { background: var(--bg-hover); color: var(--text-primary); }
    .page-tab.active {
        background: var(--primary-soft);
        border-color: color-mix(in srgb, var(--primary-color) 26%, transparent);
        color: var(--primary-color);
        font-weight: 500;
    }
    .page-tab-icon { flex-shrink: 0; opacity: .75; }
    .page-tab-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .page-tab-close, .page-tab-pin {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 16px;
        height: 16px;
        flex-shrink: 0;
        border-radius: 4px;
        color: var(--text-muted);
        transition: all var(--transition);
    }
        .page-tab-close:hover { background: var(--danger-soft); color: var(--danger-color); }
    /* 首页钉位：不可关闭，用锁形图标占住关闭按钮的位置，页签宽度不因此参差不齐 */
    .page-tab-pin { opacity: .5; }

/* 右键菜单：整屏透明垫层负责「点别处即收起」，菜单本体定位在鼠标处（与 StudioLeftNav 的右键菜单同一套路） */
.page-tab-backdrop { position: fixed; inset: 0; z-index: 1000; }
.page-tab-ctx {
    position: fixed;
    z-index: 1001;
    min-width: 148px;
    padding: 6px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.global-search {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--bg-hover);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 8px 12px;
    width: 260px;
    transition: all var(--transition);
    cursor: text;
}
    .global-search:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); background: var(--bg-input); }
    .global-search > .icon { color: var(--text-muted); font-size: 13px; }
    .global-search input {
        border: none;
        background: transparent;
        outline: none;
        color: var(--text-primary);
        font-family: var(--font-sans);
        font-size: 13.5px;
        flex: 1;
        min-width: 0;
    }
        .global-search input::placeholder { color: var(--text-muted); }
    .global-search kbd {
        font-family: var(--font-sans);
        font-size: 10.5px;
        padding: 2px 6px;
        border-radius: 4px;
        background: var(--bg-card);
        border: 1px solid var(--border-color);
        color: var(--text-muted);
        font-weight: 500;
        flex-shrink: 0;
    }

.icon-btn {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-secondary);
    font-size: 16px;
    cursor: pointer;
    transition: all var(--transition);
    position: relative;
    flex-shrink: 0;
}
    .icon-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
    .icon-btn:disabled { opacity: .4; cursor: not-allowed; }

.bell-wrapper, .lang-wrapper { position: relative; }
.bell-btn { position: relative; }
.bell-dot {
    position: absolute;
    top: 7px;
    right: 9px;
    width: 8px;
    height: 8px;
    background: var(--danger-color);
    border-radius: 50%;
    border: 2px solid var(--bg-card);
    animation: pulse 2s infinite;
}
@keyframes pulse {
    0% { box-shadow: 0 0 0 0 rgba(240, 86, 138, .5); }
    70% { box-shadow: 0 0 0 6px rgba(240, 86, 138, 0); }
    100% { box-shadow: 0 0 0 0 rgba(240, 86, 138, 0); }
}

/* 通知面板 */
.notification-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 360px;
    max-width: calc(100vw - 40px);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transform-origin: top right;
    transform: scale(.95) translateY(-4px);
    opacity: 0;
    visibility: hidden;
    transition: all .2s ease;
    z-index: 1000;
}
    .notification-panel.open { transform: scale(1) translateY(0); opacity: 1; visibility: visible; }

.notif-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-light);
    font-weight: 600;
    font-size: 15px;
    color: var(--text-primary);
}
.notif-list { max-height: 320px; overflow-y: auto; }
.notif-item {
    display: flex;
    gap: 12px;
    padding: 14px 20px;
    border-bottom: 1px solid var(--border-light);
    transition: background var(--transition);
    cursor: default;
    position: relative;
}
    .notif-item:last-child { border-bottom: none; }
    .notif-item:hover { background: var(--bg-hover); }
    .notif-item.unread { background: var(--primary-soft); }
    .notif-item.unread:hover { filter: brightness(.97); }
    [data-theme='dark'] .notif-item.unread:hover { filter: brightness(1.12); }

.notif-icon {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    flex-shrink: 0;
}
    .notif-icon.primary { background: var(--primary-soft); color: var(--primary-color); }
    .notif-icon.success { background: var(--success-soft); color: var(--success-color); }
    .notif-icon.warning { background: var(--warning-soft); color: var(--warning-color); }
    .notif-icon.danger { background: var(--danger-soft); color: var(--danger-color); }

.notif-content { flex: 1; min-width: 0; }
.notif-title { font-size: 14px; font-weight: 500; color: var(--text-primary); margin-bottom: 2px; }
.notif-desc { font-size: 12.5px; color: var(--text-secondary); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.notif-time { font-size: 11.5px; color: var(--text-muted); margin-top: 4px; }
.notif-footer { padding: 12px 20px; text-align: center; border-top: 1px solid var(--border-light); font-size: 13px; color: var(--primary-color); font-weight: 500; cursor: default; transition: background var(--transition); }
    .notif-footer:hover { background: var(--bg-hover); }

/* 语言切换 */
.lang-btn { position: relative; }
    .lang-btn .lang-code {
        position: absolute;
        right: 3px;
        bottom: 3px;
        min-width: 15px;
        height: 13px;
        padding: 0 3px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 8.5px;
        font-weight: 700;
        line-height: 1;
        border-radius: 4px;
        background: var(--primary-soft);
        color: var(--primary-color);
        pointer-events: none;
        transition: background var(--transition), color var(--transition);
    }
    .lang-btn:hover .lang-code { background: var(--primary-color); color: #fff; }

.lang-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 224px;
    max-width: calc(100vw - 40px);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transform-origin: top right;
    transform: scale(.95) translateY(-4px);
    opacity: 0;
    visibility: hidden;
    transition: all .2s ease;
    z-index: 1000;
}
    .lang-panel.open { transform: scale(1) translateY(0); opacity: 1; visibility: visible; }

.lang-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 13px 16px;
    border-bottom: 1px solid var(--border-light);
    font-weight: 600;
    font-size: 13.5px;
    color: var(--text-primary);
}
.lang-list { padding: 6px; max-height: 300px; overflow-y: auto; }
.lang-option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition);
}
    .lang-option:hover { background: var(--bg-hover); color: var(--text-primary); }
    .lang-option.active { background: var(--primary-soft); color: var(--primary-color); }
.lang-flag {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10.5px;
    font-weight: 700;
    background: var(--tag-bg);
    color: var(--text-secondary);
    flex-shrink: 0;
    transition: background var(--transition), color var(--transition);
}
    .lang-option.active .lang-flag { background: var(--primary-color); color: #fff; }
.lang-name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.lang-label { font-size: 13.5px; }
.lang-native { font-size: 11.5px; color: var(--text-muted); }
    .lang-option.active .lang-label { font-weight: 600; }
    .lang-option.active .lang-native { color: var(--primary-color); opacity: .8; }
.lang-check { font-size: 11px; color: var(--primary-color); opacity: 0; flex-shrink: 0; transition: opacity var(--transition); }
    .lang-option.active .lang-check { opacity: 1; }

/* 顶栏自定义功能：宿主注册的下拉面板（观感与内置“通知/语言/缩放”面板一致） */
.top-wrapper { position: relative; }
.icon-btn.active, .top-wrapper.open .icon-btn { background: var(--primary-soft); color: var(--primary-color); }

/* 智能体入口：主题色渐变实心小方块（与 .user-avatar 同一支渐变、同一族圆角），让它在顶栏一排线性图标里一眼可辨。
   刻意比 .icon-btn 的 38px 圆底小一圈——实心色块本身视觉重量就高，等尺寸会压过邻居；圆角方块也比实心圆更像「徽标」而非按钮。
   打开抽屉时保留渐变、补一圈细主题色外发光确认状态，避免退回 .icon-btn.active 的淡底样式。 */
.icon-btn.agent-btn {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    font-size: 14px;
    background: linear-gradient(135deg, var(--primary-color), #7C5CFF);
    border-color: transparent;
    color: #fff;
    box-shadow: 0 1px 5px color-mix(in srgb, var(--primary-color) 26%, transparent);
}
    .icon-btn.agent-btn:hover {
        background: linear-gradient(135deg, var(--primary-hover), #8F73FF);
        color: #fff;
        transform: translateY(-1px);
        box-shadow: 0 3px 9px color-mix(in srgb, var(--primary-color) 34%, transparent);
    }
    .icon-btn.agent-btn.active {
        box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary-color) 28%, transparent),
                    0 1px 5px color-mix(in srgb, var(--primary-color) 26%, transparent);
    }
.top-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    min-width: 240px;
    max-width: 380px;
    max-height: min(70vh, 520px);
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transform-origin: top right;
    transform: scale(.95) translateY(-4px);
    opacity: 0;
    visibility: hidden;
    transition: all .2s ease;
    z-index: 1000;
}
    .top-panel.open { transform: scale(1) translateY(0); opacity: 1; visibility: visible; }
.top-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 13px 16px;
    border-bottom: 1px solid var(--border-light);
    font-weight: 600;
    font-size: 13.5px;
    color: var(--text-primary);
    flex-shrink: 0;
}
.top-panel-body { padding: 14px 16px; overflow-y: auto; color: var(--text-secondary); font-size: 13.5px; }

/* 开发中心面板：右上角图标下拉，列出宿主以 [DevMenu] 注入的菜单项 */
.dev-wrapper { position: relative; }
.dev-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 232px;
    max-width: calc(100vw - 40px);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transform-origin: top right;
    transform: scale(.95) translateY(-4px);
    opacity: 0;
    visibility: hidden;
    transition: all .2s ease;
    z-index: 1000;
}
    .dev-panel.open { transform: scale(1) translateY(0); opacity: 1; visibility: visible; }
.dev-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 13px 16px;
    border-bottom: 1px solid var(--border-light);
    font-weight: 600;
    font-size: 13.5px;
    color: var(--text-primary);
}
.dev-list { padding: 6px; max-height: 340px; overflow-y: auto; }
.dev-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition);
}
    .dev-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.dev-item-ico {
    width: 26px;
    height: 26px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--tag-bg);
    color: var(--text-secondary);
    flex-shrink: 0;
    transition: background var(--transition), color var(--transition);
}
    .dev-item:hover .dev-item-ico { background: var(--primary-soft); color: var(--primary-color); }
.dev-item-name { flex: 1; min-width: 0; font-size: 13.5px; }
.dev-item-chev { color: var(--text-muted); flex-shrink: 0; transition: color var(--transition), transform var(--transition); }
    .dev-item:hover .dev-item-chev { color: var(--primary-color); transform: translateX(2px); }

/* 内容滚动区 */
.content-scroll {
    flex: 1;
    overflow-y: auto;
    padding: 28px;
    display: flex;
    flex-direction: column;
    gap: 24px;
    scroll-behavior: smooth;
    position: relative; /* 内容区滚动容器；为页内绝对定位元素提供定位基准（水印已改挂布局根的固定层，不在此列） */
}

/* 全站防截图水印：固定铺满整个视口（position:fixed 不随内容滚动），z-index 压过模态遮罩(2800)与抽屉(2900)，
   故 Modal / Drawer 打开时水印仍覆盖全屏；pointer-events:none 令其绝不拦截交互 */
.app-watermark {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 4000;
    background-repeat: repeat;
}

/* 内容区页脚：版权 / ICP 备案，居中弱化文字，钉在内容流末尾 */
.app-footer {
    margin-top: auto;
    padding: 8px 4px 4px;
    text-align: center;
    font-size: 12.5px;
    color: var(--text-muted);
    letter-spacing: 0.2px;
}

/* 维护模式横幅：钉在顶栏与内容之间（main-content 纵向流），随主题翻转的警示条 */
.app-maintenance {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 20px;
    font-size: 13px;
    font-weight: 600;
    color: var(--warning-color, #b26a00);
    background: var(--warning-soft, #fff6e6);
    border-bottom: 1px solid color-mix(in srgb, var(--warning-color, #b26a00) 30%, transparent);
}

/* ── 窄边栏主布局 RailLayout ──
   顶栏横贯全宽，下方三段：图标窄边栏（一级模块）+ 二级导航列（多子页面才展开）+ 内容区。
   两个宽度收成变量，宿主按实例覆写即可（<div class="rail-layout" style="--rail-width:88px">），不必改框架。 */
.rail-layout {
    --rail-width: 74px;
    --rail-sub-width: 186px;
    display: flex; flex-direction: column; width: 100%; height: 100%;
    overflow: hidden; background: var(--bg-app);
}

/* 顶行：品牌位与窄边栏同宽对齐，右侧顶栏占满余量 */
.rail-top { display: flex; flex: none; }
.rail-brand {
    width: var(--rail-width); flex: none; display: flex; align-items: center; justify-content: center;
    cursor: pointer; background: var(--bg-sidebar); border-bottom: 1px solid var(--border-color);
    transition: background var(--transition), border var(--transition);
}
.rail-brand:hover { background: var(--bg-hover); }
.rail-mark {
    width: 28px; height: 28px; border-radius: 9px; display: grid; place-items: center; user-select: none;
    background: linear-gradient(135deg, var(--primary-color), color-mix(in srgb, var(--primary-color) 45%, #9b5bd6));
    color: #fff; font-weight: 700; font-size: 14px;
}
.rail-mark-img { width: 28px; height: 28px; object-fit: contain; display: block; }
.rail-bar { flex: 1; min-width: 0; }
.rail-below { flex: 1; display: flex; min-height: 0; }

/* 一级：图标窄边栏 */
.rail-nav {
    width: var(--rail-width); flex: none; background: var(--bg-sidebar);
    border-right: 1px solid var(--border-color); display: flex; flex-direction: column;
    padding: 10px 0; overflow-y: auto; transition: background var(--transition), border var(--transition);
}
.rail-item {
    position: relative; flex: none; width: 100%; height: 58px; border: 0; background: transparent; cursor: pointer;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
    color: var(--text-secondary); transition: background var(--transition), color var(--transition);
}
.rail-item i { font-style: normal; font-size: 12px; line-height: 1; }
.rail-item:hover { background: var(--bg-hover); }
.rail-item.on { background: var(--primary-soft); color: var(--primary-color); font-weight: 600; }
.rail-item.on .icon { color: var(--primary-color); }
.rail-item.on::before {
    content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px;
    border-radius: 0 3px 3px 0; background: var(--primary-color);
}
.rail-dot {
    position: absolute; top: 8px; right: 12px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px;
    background: var(--primary-color); color: #fff; font-size: 10px; font-style: normal; line-height: 16px;
    text-align: center;
}

/* 二级：子页面导航列（当前模块多于一个子页面才展开） */
.rail-sub {
    width: var(--rail-sub-width); flex: none; background: var(--bg-card);
    border-right: 1px solid var(--border-color); display: flex; flex-direction: column;
    padding: 14px 10px; overflow-y: auto; transition: background var(--transition), border var(--transition);
}
.rail-sub h4 {
    font-size: 13px; font-weight: 650; color: var(--text-primary); padding: 0 8px 10px; margin: 0 0 8px;
    border-bottom: 1px solid var(--border-color);
}
.rail-link {
    display: flex; align-items: center; gap: 8px; width: 100%; height: 31px; padding: 0 8px; margin: 1px 0;
    border: 0; border-radius: 7px; background: transparent; cursor: pointer; text-align: left;
    font-size: 13px; color: var(--text-secondary); transition: background var(--transition), color var(--transition);
}
.rail-link .icon { opacity: .7; }
.rail-link:hover { background: var(--bg-hover); }
.rail-link.on { background: var(--primary-soft); color: var(--primary-color); font-weight: 600; }
.rail-link.on .icon { opacity: 1; color: var(--primary-color); }
.rail-badge {
    margin-left: auto; font-size: 10.5px; min-width: 17px; height: 17px; padding: 0 5px; border-radius: 9px;
    background: var(--primary-soft); color: var(--primary-color); font-weight: 600; display: grid; place-items: center;
}

/* 内容列：与 AdminLayout 的 main-content 同构（页签条 / 维护横幅 / 滚动区纵向堆叠） */
.rail-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.rail-scroll {
    flex: 1; overflow-y: auto; padding: 28px; display: flex; flex-direction: column; gap: 24px;
    scroll-behavior: smooth; position: relative;
}
.rail-host { display: contents; }

/* 窄屏二级导航：桌面不显示抽屉开关与遮罩，≤860px 时常驻列换成从左侧滑出的抽屉 */
.rail-toggle { display: none; }
.rail-scrim { display: none; }

@media (max-width: 860px) {
    /* 顶行原本是透明的：品牌位和顶栏各自带底色与下边框，中间这段露出应用底色、还断了底线，
       看起来像缺一截。这里把顶行铺成和顶栏同色同线，两个按钮再各自盖在上面。 */
    .rail-top { background: var(--bg-card); border-bottom: 1px solid var(--border-color); }
    /* 底线只留顶行这一道：品牌位和顶栏再各自画一条，就会在 74px 处错开 1px，看着像断线。 */
    .rail-top > .rail-brand, .rail-top > .rail-bar { border-bottom: 0; }
    /* 抽屉开关只留图标：分组名和窄边栏的高亮项重复，胶囊撑到 79px 宽，把标题挤成省略号。 */
    .rail-toggle {
        display: inline-flex; align-items: center; justify-content: center; align-self: center; flex: none;
        width: 36px; height: 36px; padding: 0; margin-right: 6px; border: 0; border-radius: 10px;
        background: transparent; color: var(--text-secondary); cursor: pointer;
    }
    .rail-toggle:hover { background: var(--bg-hover); color: var(--text-primary); }
    .rail-scrim { position: fixed; inset: 0; z-index: 1000; background: rgba(15, 23, 42, .45); }
    .rail-sub {
        display: none; position: fixed; left: 0; top: 0; bottom: 0; z-index: 1001; width: 240px; padding-top: 20px;
        box-shadow: 0 18px 48px rgba(15, 23, 42, .22);
    }
    .rail-sub.open { display: flex; }
    .rail-scrim.open { display: block; }
}

.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }

/* ============================================================
   4. 全局浮层
   ============================================================ */

/* 顶部加载条进度流光（LoadingBar 组件确定/不确定共用背景动画） */
@keyframes barFlow { to { background-position: -200% 0; } }

/* LoadingBar 组件：顶部进度条（全局浮层与局部/独立实例共用） */
.lbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    overflow: hidden;
    z-index: 3400;
    opacity: 0;
    visibility: hidden;
    transition: opacity .3s ease, visibility .3s ease;
}
    .lbar.show { opacity: 1; visibility: visible; }
    .lbar.lbar-absolute { position: absolute; }
.lbar-fill {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 0;
    background: var(--lbar-bg, linear-gradient(90deg, var(--primary-color), #7C5CFF, var(--primary-color)));
    background-size: 200% 100%;
    box-shadow: 0 0 10px var(--lbar-glow, rgba(47, 91, 255, .5));
    transition: width .3s ease;
}
    .lbar.indeterminate .lbar-fill {
        width: 40%;
        left: -40%;
        animation: lbarSlide 1.25s cubic-bezier(.4, 0, .2, 1) infinite, barFlow 1.25s linear infinite;
    }
.lbar-success { --lbar-bg: linear-gradient(90deg, var(--success-color), var(--success-soft), var(--success-color)); --lbar-glow: rgba(34, 197, 94, .45); }
.lbar-warning { --lbar-bg: linear-gradient(90deg, var(--warning-color), var(--warning-soft), var(--warning-color)); --lbar-glow: rgba(245, 158, 11, .45); }
.lbar-danger { --lbar-bg: linear-gradient(90deg, var(--danger-color), var(--danger-soft), var(--danger-color)); --lbar-glow: rgba(239, 68, 68, .45); }
.lbar-info { --lbar-bg: linear-gradient(90deg, var(--info-color), var(--info-soft), var(--info-color)); --lbar-glow: rgba(59, 130, 246, .45); }
@keyframes lbarSlide { 0% { left: -40%; } 100% { left: 100%; } }

/* Toast */
.toast-container {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: center;
    z-index: 3300;
    pointer-events: none;
}
.toast {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    min-width: 282px;
    max-width: 420px;
    padding: 13px 16px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--primary-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
    animation: toastIn .3s cubic-bezier(.4, 0, .2, 1);
}
    .toast.success { border-left-color: var(--success-color); }
    .toast.warning { border-left-color: var(--warning-color); }
    .toast.danger { border-left-color: var(--danger-color); }
    .toast.info { border-left-color: var(--info-color); }
    .toast > .icon:first-child { font-size: 15px; margin-top: 2px; flex-shrink: 0; }
    .toast.info > .icon:first-child { color: var(--info-color); }
    .toast.success > .icon:first-child { color: var(--success-color); }
    .toast.warning > .icon:first-child { color: var(--warning-color); }
    .toast.danger > .icon:first-child { color: var(--danger-color); }
.toast-body { flex: 1; min-width: 0; }
.toast-title { font-size: 13.5px; font-weight: 600; color: var(--text-primary); }
.toast-desc { font-size: 12.5px; color: var(--text-secondary); margin-top: 2px; line-height: 1.45; }
.toast-close { background: transparent; border: none; color: var(--text-muted); cursor: pointer; font-size: 12px; padding: 2px; flex-shrink: 0; transition: color var(--transition); }
    .toast-close:hover { color: var(--text-primary); }
    .toast.hide { animation: toastOut .25s ease forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateY(-14px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes toastOut { to { opacity: 0; transform: translateY(-10px) scale(.97); } }
@keyframes toastInUp { from { opacity: 0; transform: translateY(14px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes toastOutUp { to { opacity: 0; transform: translateY(10px) scale(.97); } }
    .toast-container.bottom { top: auto; bottom: 20px; flex-direction: column-reverse; }
    .toast-container.bottom .toast { animation-name: toastInUp; }
    .toast-container.bottom .toast.hide { animation-name: toastOutUp; }

/* Notification 角落滑出通知 */
.notification {
    position: fixed;
    display: flex;
    flex-direction: column;
    gap: 12px;
    width: 360px;
    max-width: calc(100vw - 32px);
    z-index: 3300;
    pointer-events: none;
}
.notification-top-right { top: 20px; right: 24px; }
.notification-top-left { top: 20px; left: 24px; }
.notification-bottom-right { bottom: 20px; right: 24px; flex-direction: column-reverse; }
.notification-bottom-left { bottom: 20px; left: 24px; flex-direction: column-reverse; }
.notification-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 15px 16px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
    animation: notifInRight .34s cubic-bezier(.4, 0, .2, 1);
}
.notification-top-left .notification-item,
.notification-bottom-left .notification-item { animation-name: notifInLeft; }
.notification-item .notification-icon { width: 34px; height: 34px; }
.notification-item.hide { animation: notifOutRight .26s ease forwards; }
.notification-top-left .notification-item.hide,
.notification-bottom-left .notification-item.hide { animation-name: notifOutLeft; }
.notification-body { flex: 1; min-width: 0; }
.notification-title { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.notification-desc { font-size: 12.5px; color: var(--text-secondary); margin-top: 3px; line-height: 1.45; }
.notification-close { background: transparent; border: none; color: var(--text-muted); cursor: pointer; font-size: 12px; padding: 2px; flex-shrink: 0; transition: color var(--transition); }
    .notification-close:hover { color: var(--text-primary); }
@keyframes notifInRight { from { opacity: 0; transform: translateX(110%); } to { opacity: 1; transform: none; } }
@keyframes notifOutRight { to { opacity: 0; transform: translateX(110%); } }
@keyframes notifInLeft { from { opacity: 0; transform: translateX(-110%); } to { opacity: 1; transform: none; } }
@keyframes notifOutLeft { to { opacity: 0; transform: translateX(-110%); } }

/* 遮罩：模态框 */
.overlay-mask {
    position: fixed;
    inset: 0;
    z-index: 2800;
    background: rgba(15, 20, 30, .5);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    opacity: 0;
    visibility: hidden;
    transition: opacity .25s ease, visibility .25s ease;
}
    .overlay-mask.open { opacity: 1; visibility: visible; }

.modal {
    width: 100%;
    max-width: 460px;
    max-height: 100%;
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transform: translateY(18px) scale(.96);
    opacity: 0;
    transition: all .28s cubic-bezier(.4, 0, .2, 1);
}
    .overlay-mask.open .modal { transform: none; opacity: 1; }
    .modal.sm { max-width: 360px; }
    .modal.lg { max-width: 640px; }
    .modal.xl { max-width: 900px; }
    .modal.full { max-width: calc(100vw - 48px); height: calc(100vh - 48px); }

/* 最大化态：遮罩去内边距、弹窗贴边、去掉圆角与描边（尺寸由行内样式撑满，这里只收拾外观与拖拽让位）；
   标题栏按钮成组靠右，最大化与关闭并排 */
.overlay-mask.maximized { padding: 0; }
.modal.maximized { border-radius: 0; border: none; }
.modal-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }

/* 可拖拽弹窗：JS 用 style.left/top 叠加相对偏移（不改 transform，入场动画不受扰），故需 position:relative 让偏移生效；
   标题栏给 move 光标并禁选中，拖动时 .dragging 关掉 transition 免得跟手发飘 */
.modal.draggable { position: relative; }
    .modal.draggable .modal-header { cursor: move; user-select: none; -webkit-user-select: none; }
.modal.dragging { transition: none; }

.modal-header, .drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 22px;
    border-bottom: 1px solid var(--border-color);
    font-weight: 600;
    font-size: 15px;
    color: var(--text-primary);
    flex-shrink: 0;
}
.modal-body, .drawer-body { padding: 22px; overflow-y: auto; color: var(--text-secondary); font-size: 14px; }
.modal-footer, .drawer-footer { padding: 16px 22px; border-top: 1px solid var(--border-color); display: flex; justify-content: flex-end; gap: 10px; flex-shrink: 0; }
.modal-title .icon, .drawer-title .icon { margin-right: 10px; }

.close-x {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    border: none;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    transition: all var(--transition);
}
    .close-x:hover { background: var(--bg-hover); color: var(--text-primary); }
    /* 关闭 × 悬停转 danger 色（与页签关闭同一套 danger-soft 语义），最大化按钮保持中性 */
    .close-x.is-danger:hover { background: var(--danger-soft); color: var(--danger-color); }

/* 抽屉 */
.drawer-mask {
    position: fixed;
    inset: 0;
    z-index: 2800;
    background: rgba(15, 20, 30, .5);
    backdrop-filter: blur(2px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .28s ease, visibility .28s ease;
}
    .drawer-mask.open { opacity: 1; visibility: visible; }

.drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: 420px;
    max-width: 92vw;
    background: var(--bg-card);
    border-left: 1px solid var(--border-color);
    box-shadow: var(--shadow-lg);
    transform: translateX(101%);
    transition: transform .32s cubic-bezier(.4, 0, .2, 1);
    z-index: 2900;
    display: flex;
    flex-direction: column;
}
    .drawer.open { transform: translateX(0); animation: drawer-in-right .32s cubic-bezier(.4, 0, .2, 1); }
    .drawer.left { left: 0; right: auto; transform: translateX(-101%); border-left: none; border-right: 1px solid var(--border-color); }
        .drawer.left.open { transform: translateX(0); animation-name: drawer-in-left; }
    .drawer.top { left: 0; right: 0; bottom: auto; width: auto; max-width: none; height: 320px; max-height: 92vh; transform: translateY(-101%); border-left: none; border-bottom: 1px solid var(--border-color); }
        .drawer.top.open { transform: translateY(0); animation-name: drawer-in-top; }
    .drawer.bottom { left: 0; right: 0; top: auto; width: auto; max-width: none; height: 320px; max-height: 92vh; transform: translateY(101%); border-left: none; border-top: 1px solid var(--border-color); }
        .drawer.bottom.open { transform: translateY(0); animation-name: drawer-in-bottom; }
    .drawer.wide { width: 640px; }
        .drawer.top.wide, .drawer.bottom.wide { width: auto; height: 480px; }
    .drawer-body { flex: 1; }
.drawer-footer { justify-content: flex-end; }

/* 入场方向固定：起点写死对应轴向，避免切换 placement 与打开同帧时 transition 跨轴补间 */
@keyframes drawer-in-right { from { transform: translateX(101%); } to { transform: translateX(0); } }
@keyframes drawer-in-left { from { transform: translateX(-101%); } to { transform: translateX(0); } }
@keyframes drawer-in-top { from { transform: translateY(-101%); } to { transform: translateY(0); } }
@keyframes drawer-in-bottom { from { transform: translateY(101%); } to { transform: translateY(0); } }

/* 气泡确认 */
.popconfirm-wrap { position: relative; display: inline-block; }
.popconfirm {
    position: absolute;
    bottom: calc(100% + 10px);
    left: 50%;
    transform: translateX(-50%) translateY(6px) scale(.96);
    width: 224px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 14px;
    z-index: 600;
    opacity: 0;
    visibility: hidden;
    /* 只动入场两属性：left/top 交给 known.popup 传送门写视口坐标，
       若走 transition: all 会把「从关闭态停放点搬到触发按钮边」这段位移当成动画播出来——确认框就从页面中间飞入了。 */
    transition: opacity .18s ease, transform .18s ease, visibility .18s ease;
}
    .popconfirm.open { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0) scale(1); }
    .popconfirm::after { content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: var(--bg-card); }
    .popconfirm.top { bottom: auto; top: calc(100% + 10px); }
        .popconfirm.top::after { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: var(--bg-card); }
.popconfirm-text { display: flex; gap: 9px; font-size: 13px; color: var(--text-primary); margin-bottom: 12px; line-height: 1.5; }
    .popconfirm-text .icon { color: var(--warning-color); font-size: 15px; margin-top: 1px; flex-shrink: 0; }
.popconfirm-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* 命令面板 */
.cmd-mask {
    position: fixed;
    inset: 0;
    z-index: 3200;
    background: rgba(15, 20, 30, .5);
    backdrop-filter: blur(3px);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 12vh 20px 20px;
    opacity: 0;
    visibility: hidden;
    /* visibility 不能跟着 opacity 一起过渡：过渡起始帧它仍是 hidden，
       而面板在 OnAfterRenderAsync 里立刻 FocusAsync，隐藏元素拿不到焦点，
       于是 Ctrl+K 打开后输入框没焦点、绑在输入框上的 Esc 关闭失效。
       打开时立即 visible，关闭时延后到淡出结束再 hidden。 */
    transition: opacity .22s ease, visibility 0s linear .22s;
}
    .cmd-mask.open { opacity: 1; visibility: visible; transition: opacity .22s ease, visibility 0s; }
.cmd-panel {
    width: 100%;
    max-width: 560px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    transform: translateY(-14px) scale(.98);
    /* 同上：只过渡 transform，别把继承来的 visibility 也拖进过渡里 */
    transition: transform .25s cubic-bezier(.4, 0, .2, 1);
}
    .cmd-mask.open .cmd-panel { transform: none; }
.cmd-input-row { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--border-color); }
    .cmd-input-row > .icon { color: var(--text-muted); font-size: 15px; }
    .cmd-input-row input { flex: 1; min-width: 0; border: none; background: transparent; outline: none; font-family: var(--font-sans); font-size: 15px; color: var(--text-primary); }
    .cmd-input-row kbd { font-family: var(--font-sans); font-size: 10.5px; padding: 2px 7px; flex-shrink: 0; border-radius: 4px; background: var(--bg-hover); border: 1px solid var(--border-color); color: var(--text-muted); }
.cmd-list { max-height: 330px; overflow-y: auto; padding: 8px; }
.cmd-group-title { font-size: 10.5px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase; color: var(--text-muted); padding: 10px 12px 6px; }
.cmd-item {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition);
}
    .cmd-item > .icon { width: 17px; text-align: center; color: var(--text-muted); font-size: 13px; }
    .cmd-item:hover, .cmd-item.active { background: var(--bg-hover); color: var(--text-primary); }
        .cmd-item:hover > .icon { color: var(--primary-color); }
    .cmd-item .cmd-tag { margin-left: auto; font-size: 11px; color: var(--text-muted); }
.cmd-empty { padding: 32px; text-align: center; font-size: 13.5px; color: var(--text-muted); }

/* 返回顶部 */
.back-top {
    position: fixed;
    right: 30px;
    bottom: 30px;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--primary-color);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    cursor: pointer;
    border: none;
    box-shadow: 0 8px 20px rgba(47, 91, 255, .35);
    opacity: 0;
    visibility: hidden;
    transform: translateY(14px) scale(.9);
    transition: all .28s cubic-bezier(.4, 0, .2, 1);
    z-index: 1400;
}
    .back-top.show { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
    .back-top:hover { background: var(--primary-hover); transform: translateY(-2px) scale(1.05); }

/* Tooltip（data-tip） */
[data-tip] { position: relative; }
/* .back-top 为 position:fixed，需压过上面的 [data-tip]{position:relative}，否则脱离角落 */
.back-top[data-tip] { position: fixed; }
[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    bottom: calc(100% + 9px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    background: var(--text-primary);
    color: var(--bg-card);
    padding: 6px 11px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: all .16s ease;
    z-index: 400;
    box-shadow: var(--shadow-md);
    letter-spacing: -.1px;
}
    [data-tip]::before {
        content: '';
        position: absolute;
        bottom: calc(100% + 4px);
        left: 50%;
        transform: translateX(-50%) translateY(4px);
        border: 5px solid transparent;
        border-top-color: var(--text-primary);
        opacity: 0;
        pointer-events: none;
        transition: all .16s ease;
        z-index: 400;
    }
    [data-tip]:hover::after { opacity: 1; transform: translateX(-50%) translateY(0); }
    [data-tip]:hover::before { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Tooltip 边缘对齐：窄容器（工具条、卡片角）里居中气泡会左右各甩半个气泡宽，被父级 overflow 裁掉。
   data-tip-align="start" 贴左缘向右长、="end" 贴右缘向左长，箭头跟着挪 10px 保持视觉居中。
   只管横向：纵向仍是基础规则的向上弹；容器若另有方向覆盖（.top-actions 等），两者别混用。 */
[data-tip][data-tip-align='start']::after { left: 0; transform: translateY(4px); }
[data-tip][data-tip-align='start']::before { left: 10px; transform: translateY(4px); }
[data-tip][data-tip-align='end']::after { left: auto; right: 0; transform: translateY(4px); }
[data-tip][data-tip-align='end']::before { left: auto; right: 10px; transform: translateY(4px); }
[data-tip][data-tip-align]:hover::after,
[data-tip][data-tip-align]:hover::before { transform: translateY(0); }

/* 顶栏图标 Tooltip 改为向下弹出：.main-content 为 overflow:hidden，向上会被裁剪 */
.top-actions [data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.top-actions [data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.top-actions [data-tip]:hover::after, .top-actions [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
.top-actions [data-tip-below='0']::after { bottom: calc(100% + 9px); top: auto; transform: translateX(-50%) translateY(4px); }
.top-actions [data-tip-below='0']::before { bottom: calc(100% + 4px); top: auto; transform: translateX(-50%) translateY(4px); border-top-color: var(--text-primary); border-bottom-color: transparent; }

/* 通用「向下弹」开关：贴顶但父级不叫 .top-actions 的元素（登录页右上角、窄屏角标等）标一个
   data-tip-below='1' 即可翻转，不必每个宿主各自重抄一遍 bottom/top + 箭头换向——
   与 .top-actions 的「默认向下、='0' 回撤向上」共用同一套语义，方向只由这一个属性说了算。 */
[data-tip][data-tip-below='1']::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
[data-tip][data-tip-below='1']::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
[data-tip][data-tip-below='1']:hover::after, [data-tip][data-tip-below='1']:hover::before { transform: translateX(-50%) translateY(0); }

/* 弹窗 / 抽屉标题栏图标（最大化 / 关闭）Tooltip 向下弹出：.modal 为 overflow:hidden 且标题栏贴顶，
   抽屉又整幅贴视口顶边——向上弹都会被自身或视口顶裁掉，统一改向下 */
.modal-header [data-tip]::after, .drawer-header [data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.modal-header [data-tip]::before, .drawer-header [data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.modal-header [data-tip]:hover::after, .modal-header [data-tip]:hover::before,
.drawer-header [data-tip]:hover::after, .drawer-header [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }

/* 用户菜单等下拉面板内的 Tooltip 一律向上：面板向下展开、上方有空间，
   若沿用顶栏的向下规则，位于面板底部的元素（如主题色色板）Tooltip 会顶出面板底边被遮挡。 */
.user-menu [data-tip]::after { bottom: calc(100% + 9px); top: auto; transform: translateX(-50%) translateY(4px); }
.user-menu [data-tip]::before { bottom: calc(100% + 4px); top: auto; transform: translateX(-50%) translateY(4px); border-top-color: var(--text-primary); border-bottom-color: transparent; }
.user-menu [data-tip]:hover::after, .user-menu [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }

/* 侧栏折叠按钮位于顶部，Tooltip 同样向下弹出 */
.collapse-btn[data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.collapse-btn[data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.collapse-btn[data-tip]:hover::after, .collapse-btn[data-tip]:hover::before { transform: translateX(-50%) translateY(0); }

/* Studio 宿主工具栏（撤销 / 恢复 / 全屏 / 保存）贴顶：向上弹会被视口 / 全屏顶边裁掉，改向下弹 */
.studio-host .sh-bar [data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.studio-host .sh-bar [data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.studio-host .sh-bar [data-tip]:hover::after, .studio-host .sh-bar [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
/* 左栏资源树表头的「新建分组 / 新建XX」图标按钮贴面板顶边：向上弹会被裁掉，改向下弹 */
.studio-host .ln-ico[data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.studio-host .ln-ico[data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.studio-host .ln-ico[data-tip]:hover::after, .studio-host .ln-ico[data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
.studio-host .sh-bar button { cursor: pointer; }

/* ============================================================
   5. 通用组件
   ============================================================ */

/* ── 卡片 ── */
.card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 22px 24px;
    box-shadow: var(--shadow-sm);
    transition: background var(--transition), border var(--transition), box-shadow var(--transition);
    min-width: 0;
}
.card-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; font-weight: 600; font-size: 15.5px; color: var(--text-primary); }
    .card-header .header-title { display: flex; align-items: center; gap: 8px; }
    .card-header .header-title > .icon { color: var(--primary-color); font-size: 15px; }
    .card-header .header-extra { display: flex; align-items: center; gap: 10px; font-weight: 500; font-size: 13px; color: var(--text-secondary); }
    .card-header > .icon { color: var(--text-muted); font-size: 16px; }
    .card-header.collapsible { cursor: pointer; user-select: none; }
    .card-header.collapsible:hover { color: var(--primary-color); }
    .card-header .card-toggle { color: var(--text-muted); font-size: 14px; }
.card-footer { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border-light); font-size: 13px; color: var(--text-secondary); }
.card-flush { padding: 0; overflow: hidden; }
    .card-flush > .card-header { padding: 20px 24px 0; margin-bottom: 16px; }
    .card-flush > .card-footer { padding: 0 24px 18px; margin-top: 14px; }

/* ── 按钮 ── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-weight: 500;
    font-size: 14px;
    padding: var(--btn-pad-y) 18px;
    cursor: default;
    transition: all var(--transition);
    background: transparent;
    color: var(--text-primary);
    font-family: var(--font-sans);
    white-space: nowrap;
}
    .btn .icon { font-size: 13px; }
    .btn .btn-loader { font-size: 13px; width: 13px; height: 13px; }
    .btn:disabled { opacity: .5; cursor: not-allowed; }
    /* 带提示的禁用按钮外层包裹：见 Button.razor。禁用按钮本身不派发指针事件，提示挪到这个 span 上；
       让内层按钮 pointer-events:none，鼠标落到 span，[data-tip]:hover 才弹得出来。 */
    .btn-tipwrap { display: inline-flex; cursor: not-allowed; }
        .btn-tipwrap > .btn:disabled { pointer-events: none; }
    .btn-primary { background: var(--primary-color); color: #fff; box-shadow: 0 4px 10px rgba(47, 91, 255, .25); }
        .btn-primary:not(:disabled):hover { background: var(--primary-hover); }
    .btn-outline { border-color: var(--border-color); background: var(--bg-input); color: var(--text-primary); }
        .btn-outline:not(:disabled):hover { background: var(--bg-hover); border-color: var(--text-muted); }
    .btn-soft { background: var(--primary-soft); color: var(--primary-color); }
        .btn-soft:not(:disabled):hover { background: var(--primary-color); color: #fff; }
    .btn-danger { background: var(--danger-color); color: #fff; }
        .btn-danger:not(:disabled):hover { filter: brightness(.93); }
    .btn-danger-soft { background: var(--danger-soft); color: var(--danger-color); }
        .btn-danger-soft:not(:disabled):hover { background: var(--danger-color); color: #fff; }
    .btn-success { background: var(--success-color); color: #fff; }
        .btn-success:not(:disabled):hover { filter: brightness(.93); }
    .btn-success-soft { background: var(--success-soft); color: var(--success-color); }
        .btn-success-soft:not(:disabled):hover { background: var(--success-color); color: #fff; }
    .btn-warning { background: var(--warning-color); color: #fff; }
        .btn-warning:not(:disabled):hover { filter: brightness(.93); }
    .btn-warning-soft { background: var(--warning-soft); color: var(--warning-color); }
        .btn-warning-soft:not(:disabled):hover { background: var(--warning-color); color: #fff; }
    .btn-info { background: var(--info-color); color: #fff; }
        .btn-info:not(:disabled):hover { filter: brightness(.93); }
    .btn-info-soft { background: var(--info-soft); color: var(--info-color); }
        .btn-info-soft:not(:disabled):hover { background: var(--info-color); color: #fff; }
    .btn-text { padding: 4px 6px; color: var(--text-secondary); }
        .btn-text:not(:disabled):hover { color: var(--primary-color); }
    .btn-default { background: var(--bg-input); color: var(--text-primary); }
        .btn-default:not(:disabled):hover { background: var(--bg-hover); }
    .btn-link { padding: 4px 6px; color: var(--primary-color); }
        .btn-link:not(:disabled):hover { color: var(--primary-hover); text-decoration: underline; }
    .btn-lg { padding: 11px 22px; font-size: 15px; }
    .btn-sm { padding: 6px 14px; font-size: 13px; border-radius: var(--radius-sm); }
    .btn-xs { padding: 4px 10px; font-size: 12px; border-radius: var(--radius-sm); }
    .btn-icon { padding: 8px; width: 36px; height: 36px; border-radius: var(--radius-md); }
    .btn-lg.btn-icon { width: 44px; height: 44px; }
    .btn-sm.btn-icon { width: 28px; height: 28px; padding: 5px; border-radius: var(--radius-sm); }
    .btn-xs.btn-icon { width: 24px; height: 24px; padding: 4px; border-radius: var(--radius-sm); }
    .btn-circle { border-radius: 50%; padding: 0; width: 36px; height: 36px; }
    .btn-lg.btn-circle { width: 44px; height: 44px; }
    .btn-sm.btn-circle { width: 32px; height: 32px; }
    .btn-xs.btn-circle { width: 28px; height: 28px; }
    .btn-block { width: 100%; }

/* ── 标签 / 徽章 ── */
.tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border-radius: 30px;
    font-size: 12px;
    font-weight: 500;
    background: var(--tag-bg);
    color: var(--tag-text);
    transition: background var(--transition), color var(--transition);
    border: 1px solid transparent;
}
    .tag .icon { font-size: 11px; }
    .tag-primary { background: var(--primary-soft); color: var(--primary-color); }
    .tag-success { background: var(--success-soft); color: var(--success-color); }
    .tag-warning { background: var(--warning-soft); color: var(--warning-color); }
    .tag-danger { background: var(--danger-soft); color: var(--danger-color); }
.tag-lg { padding: 6px 16px; font-size: 13px; }
.tag-sm { padding: 2px 9px; font-size: 11px; }
.tag .tag-close { cursor: pointer; font-size: 10px; opacity: .7; transition: opacity var(--transition); }
    .tag .tag-close:hover { opacity: 1; }
.tag-closing { opacity: 0; transform: scale(.9); transition: opacity .18s ease, transform .18s ease; }

.badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 2px 8px;
    border-radius: 30px;
    font-size: 11px;
    font-weight: 600;
    background: var(--badge-bg);
    color: var(--badge-text);
    line-height: 1.4;
}
    .badge-primary { background: var(--primary-color); color: #fff; }
    .badge-success { background: var(--success-color); color: #fff; }
    .badge-warning { background: var(--warning-color); color: #fff; }
    .badge-danger { background: var(--danger-color); color: #fff; }
    .badge-lg { padding: 4px 12px; font-size: 13px; }
    .badge-dot { width: 8px; height: 8px; padding: 0; border-radius: 50%; }

/* ── 分割线 ── */
.divider { display: flex; align-items: center; gap: 12px; margin: 14px 0; font-size: 12px; color: var(--text-muted); }
    .divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--border-color); }
    .divider.left::before { flex: 0 0 26px; }
    .divider.right::after { flex: 0 0 26px; }
    .divider.plain::before, .divider.plain::after { flex: 1; }
.divider-v { display: inline-block; width: 1px; height: 14px; background: var(--border-color); margin: 0 10px; vertical-align: middle; }

/* ── 状态点 ── */
/* 状态色只写一份：变量挂在根上，圆点据此取色；Tint 时整行文字共用同一个色。
   类名同时挂在纯圆点和 status-line 根节点上，所以两种形态各写一个选择器。 */
.status-dot, .status-line { --sd: var(--text-muted); --sd-soft: var(--border-light); }
    .status-dot.online, .status-line.online { --sd: var(--success-color); --sd-soft: var(--success-soft); }
    .status-dot.busy, .status-line.busy { --sd: var(--warning-color); --sd-soft: var(--warning-soft); }
    .status-dot.error, .status-line.error { --sd: var(--danger-color); --sd-soft: var(--danger-soft); }
    .status-dot.primary, .status-line.primary { --sd: var(--primary-color); --sd-soft: var(--primary-soft); }
    .status-dot.success, .status-line.success { --sd: var(--success-color); --sd-soft: var(--success-soft); }
    .status-dot.warning, .status-line.warning { --sd: var(--warning-color); --sd-soft: var(--warning-soft); }
    .status-dot.danger, .status-line.danger { --sd: var(--danger-color); --sd-soft: var(--danger-soft); }
    /* offline / info / default 与上面的兜底同色，列出只为可读性 */
    .status-dot.offline, .status-line.offline, .status-dot.info, .status-line.info, .status-dot.default, .status-line.default { --sd: var(--text-muted); --sd-soft: var(--border-light); }
.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; background: var(--sd); box-shadow: 0 0 0 3px var(--sd-soft); }
.status-line { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-secondary); }
    .status-line.tint { color: var(--sd); }
    /* 胶囊形态：可点的状态过滤器。active 由组件按 Active 参数挂上，边框跟着状态色走 */
    .status-line.pill { padding: 3px 10px; border: 1px solid var(--border-color); border-radius: 999px; cursor: pointer; user-select: none; transition: background var(--transition), border-color var(--transition); }
        .status-line.pill:hover { background: var(--bg-hover); }
        .status-line.pill.active { border-color: currentColor; background: var(--bg-hover); }
.status-count { font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── 头像 ── */
.avatar-group { display: flex; align-items: center; }
.avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 600;
    background: var(--primary-color);
    color: #fff;
    flex-shrink: 0;
    object-fit: cover;
}
    .avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
    .avatar-group .avatar { margin-left: -11px; border: 2px solid var(--bg-card); }
    .avatar-group .avatar:first-child { margin-left: 0; }
    .avatar-group .avatar.more { background: var(--bg-hover); color: var(--text-secondary); font-size: 11.5px; }
.avatar-sm { width: 28px; height: 28px; font-size: 11px; }
.avatar-lg { width: 48px; height: 48px; font-size: 17px; }
.avatar-square { border-radius: 10px; }

/* ── 水印 ── */
.watermark-box {
    position: relative;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
    min-height: 160px;
    padding: 22px;
    background: var(--bg-card);
}
.watermark-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 1;
    background-repeat: repeat;
}
.watermark-content { position: relative; z-index: 2; }
    .watermark-content h4 { font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 10px; }
    .watermark-content p { font-size: 13px; color: var(--text-secondary); line-height: 1.6; }

/* ── 布局辅助 ── */
.flex-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-center { display: flex; align-items: center; justify-content: center; }
.flex-column { display: flex; flex-direction: column; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 16px; }
.mb-2 { margin-bottom: 8px; }
.mb-3 { margin-bottom: 16px; }
.text-muted { color: var(--text-muted); }
.text-secondary { color: var(--text-secondary); }
.text-primary { color: var(--text-primary); }
.text-danger { color: var(--danger-color); }
.text-success { color: var(--success-color); }
.text-warning { color: var(--warning-color); }
.text-center { text-align: center; }
.w-full { width: 100%; }
.section-note { font-size: 12px; color: var(--text-muted); margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--border-light); }

/* ── 表单 ── */
.form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
    .form-group:last-child { margin-bottom: 0; }
.form-label { font-size: 13px; font-weight: 600; color: var(--text-secondary); display: flex; align-items: center; gap: 6px; }
    .form-label .icon { font-size: 12px; color: var(--text-muted); }
.form-label.required::after { content: '*'; color: var(--danger-color); font-weight: 600; }

.input, .select, .textarea {
    width: 100%;
    padding: var(--ctl-pad-y) 14px;
    background: var(--bg-input);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 14px;
    /* 高度契约的唯一来源：padding(--ctl-pad-y) + 正文 × 1.5 行高 + 上下边框。
       必须显式写死 —— 原生 input / textarea 的 UA line-height 是 normal（≈1.43），
       而 .select 是 div、继承 body 的 1.5，同一个 padding 下二者会差 3px（40 vs 43）；
       .number 也按这条公式对齐。四种控件（input / select / textarea / number）因此处处同高。 */
    line-height: 1;
    color: var(--text-primary);
    transition: border var(--transition), box-shadow var(--transition);
    outline: none;
}
    .input:focus, .select:focus, .textarea:focus { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
    .input::placeholder, .textarea::placeholder { color: var(--text-muted); }
    .input:disabled, .select:disabled, .textarea:disabled { background: var(--bg-hover); color: var(--text-muted); cursor: not-allowed; }
    .input-lg, .select-lg { padding: 12px 16px; font-size: 15px; }
    .input-sm, .select-sm { padding: 6px 10px; font-size: 13px; }
.textarea { resize: vertical; min-height: 84px; }
.input-group { display: flex; align-items: center; gap: 8px; }
    .input-group .input { flex: 1; min-width: 0; }
    .input-group-text { display: flex; align-items: center; padding: 0 12px; height: var(--ctl-h); background: var(--bg-hover); border: 1px solid var(--border-color); border-radius: var(--radius-md); color: var(--text-secondary); font-size: 13px; white-space: nowrap; }
    .input-affix { position: relative; display: flex; align-items: center; }
        .input-affix.has-icon > .input { padding-left: 35px; }
        .input-affix > .icon:first-child { position: absolute; left: 13px; color: var(--text-muted); font-size: 13px; pointer-events: none; z-index: 1; }
    .input-error, .input-error:focus { border-color: var(--danger-color); box-shadow: 0 0 0 3px var(--danger-soft); }
.form-error { font-size: 12px; color: var(--danger-color); }
.form-help { font-size: 12px; color: var(--text-muted); }

.select {
    position: relative;
    display: flex;
    align-items: center;
    min-height: var(--ctl-h);
    padding-right: 34px;
    cursor: pointer;
    user-select: none;
}
    .select.open { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
    .select.select-disabled { background: var(--bg-hover); color: var(--text-muted); cursor: not-allowed; }
.select-view { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .select-view.placeholder { color: var(--text-muted); }
.select-arrow { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; transition: transform var(--transition); }
    .select.open .select-arrow { transform: translateY(-50%) rotate(180deg); }
.select-list {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    z-index: 400;
    max-height: 240px;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: all .18s ease;
}
    .select.open .select-list,
    .select-list.open { opacity: 1; visibility: visible; transform: translateY(0); }
    /* 开合瞬间：加了 open 类、传送门还没写坐标那一帧先按住，避免列表在原位闪一下再跳走 */
    .select-list.open:not(.placed) { visibility: hidden; }
.select-group { padding: 8px 12px 4px; font-size: 12px; font-weight: 600; color: var(--text-muted); }
.select-opt {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition);
}
    .select-opt .icon { font-size: 12px; color: var(--text-muted); }
    .select-opt:hover, .select-opt.active { background: var(--bg-hover); color: var(--text-primary); }
    .select-opt.selected { color: var(--primary-color); font-weight: 600; }
    .select-opt.disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.select-opt-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-shortcut { font-size: 12px; color: var(--text-muted); }
.select-check { margin-left: auto; color: var(--primary-color); }
.select-empty { padding: 18px; text-align: center; font-size: 13px; color: var(--text-muted); }
[dir='rtl'] .select { padding-right: 14px; padding-left: 34px; }
    [dir='rtl'] .select-arrow { right: auto; left: 12px; }

/* 开关 */
.switch { position: relative; display: inline-flex; align-items: center; gap: 8px; flex-shrink: 0; }
    .switch input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.slider-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: 44px;
    height: 24px;
    padding: 0 6px 0 24px;
    background-color: var(--border-color);
    border-radius: 34px;
    cursor: pointer;
    transition: var(--transition);
}
    .slider-toggle::before {
        position: absolute;
        content: '';
        height: 18px;
        width: 18px;
        left: 3px;
        top: 3px;
        background-color: #fff;
        transition: var(--transition);
        border-radius: 50%;
        box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
    }
    .switch-text { display: grid; place-items: center; }
        .switch-text .st-on, .switch-text .st-off { grid-area: 1 / 1; font-size: 12px; font-weight: 600; line-height: 1; white-space: nowrap; }
        .switch-text .st-on { opacity: 0; color: #fff; }
        .switch-text .st-off { opacity: 1; color: var(--text-secondary); }
    input:checked + .slider-toggle { background-color: var(--primary-color); padding: 0 24px 0 6px; }
        input:checked + .slider-toggle::before { left: calc(100% - 21px); }
        input:checked + .slider-toggle .st-on { opacity: 1; }
        input:checked + .slider-toggle .st-off { opacity: 0; }
    input:disabled + .slider-toggle { opacity: .5; cursor: not-allowed; }
.switch-label { font-size: 14px; color: var(--text-primary); }
.switch.sm { gap: 6px; }
    .switch.sm .slider-toggle { min-width: 36px; height: 20px; padding: 0 5px 0 20px; }
        .switch.sm .slider-toggle::before { height: 14px; width: 14px; }
        .switch.sm .slider-toggle .st-on, .switch.sm .slider-toggle .st-off { font-size: 10px; }
    .switch.sm input:checked + .slider-toggle { padding: 0 20px 0 5px; }
        .switch.sm input:checked + .slider-toggle::before { left: calc(100% - 17px); }
.switch.lg { gap: 10px; }
    .switch.lg .slider-toggle { min-width: 56px; height: 30px; padding: 0 8px 0 30px; border-radius: 40px; }
        .switch.lg .slider-toggle::before { height: 24px; width: 24px; }
        .switch.lg .slider-toggle .st-on, .switch.lg .slider-toggle .st-off { font-size: 13px; }
    .switch.lg input:checked + .slider-toggle { padding: 0 30px 0 8px; }
        .switch.lg input:checked + .slider-toggle::before { left: calc(100% - 27px); }

/* 单选 / 多选 */
.radio-group, .checkbox-group { display: flex; flex-wrap: wrap; gap: 16px; }
.radio-item, .checkbox-item { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text-primary); cursor: default; }
    .radio-item input, .checkbox-item input { width: 17px; height: 17px; accent-color: var(--primary-color); cursor: default; margin: 0; }
    .radio-item.disabled, .checkbox-item.disabled { opacity: .5; cursor: not-allowed; }
.checkbox-group.vertical, .radio-group.vertical { flex-direction: column; gap: 10px; }

/* 数值输入
   高度契约与 .input / .select / .date-input 完全一致：上下留白取 --ctl-pad-y、正文 14px × 1.5 行高、容器只算边框。
   原来靠内部按钮与输入框自带的 8px padding 撑高，实测只有 36px，和同一行旁边 43px 的下拉/文本框并排时肉眼可见地矮一截。
   现在换成 --ctl-pad-y（三种密度自动对齐 35 / 43 / 49），加减按钮改为 align-self:stretch 撑满整高，
   悬停底色才不会被上下各留一条白边。 */
.number { display: flex; align-items: center; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-md); overflow: hidden; width: fit-content; min-height: var(--ctl-h); }
    .number button { display: inline-flex; align-items: center; justify-content: center; align-self: stretch; background: transparent; border: none; padding: 0 12px; font-size: 16px; color: var(--text-secondary); cursor: pointer; transition: background var(--transition); }
        .number button:not(:disabled):hover { background: var(--bg-hover); }
        .number button:disabled { opacity: .35; cursor: not-allowed; }
    .number input { width: 60px; border: none; background: transparent; padding: var(--ctl-pad-y) 0; line-height: 1; text-align: center; font-family: var(--font-sans); font-size: 14px; color: var(--text-primary); outline: none; }

/* 日期 / 时间 */
.date-input { display: flex; align-items: center; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: 0 14px; gap: 8px; }
    .date-input > .icon { color: var(--text-muted); font-size: 13px; }
    .date-input input { border: none; background: transparent; padding: var(--ctl-pad-y) 0; font-family: var(--font-sans); font-size: 14px; color: var(--text-primary); outline: none; width: 100%; min-width: 0; }
    .date-input input::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .55; }
    [data-theme='dark'] .date-input input::-webkit-calendar-picker-indicator { filter: invert(1); }
.date-range { display: flex; align-items: center; gap: 8px; }
    .date-range .date-input { flex: 1; min-width: 0; }
.date-range > span { color: var(--text-muted); font-size: 13px; flex-shrink: 0; }

/* 复合控件（数值 stepper / 日期框）的焦点环
   它们的边框画在容器上，内层 input / button 都写了 outline:none，所以 :focus 永远不会落到有边框的那个元素上 ——
   表现为「点进去毫无反馈」，而旁边的 .input / .select 都会亮主题色。这里用 :focus-within 把焦点环还给容器，
   取值与 .input:focus / .select.open 完全一致（主色边框 + 3px 主色柔光），后续新增同类复合控件也照这条写。 */
.number:focus-within,
.date-input:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }

/* 滑动输入 */
.slider-wrap { display: flex; flex-direction: column; gap: 16px; }
.slider-row { display: flex; align-items: center; gap: 14px; }
.slider-label { font-size: 13px; font-weight: 500; color: var(--text-secondary); min-width: 60px; }
.range-input {
    -webkit-appearance: none;
    appearance: none;
    flex: 1;
    min-width: 0;
    height: 6px;
    border-radius: 10px;
    background: var(--bg-hover);
    outline: none;
    cursor: pointer;
}
    .range-input::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 18px;
        height: 18px;
        border-radius: 50%;
        background: var(--primary-color);
        border: 3px solid var(--bg-card);
        box-shadow: 0 2px 6px rgba(47, 91, 255, .35);
        cursor: pointer;
        transition: transform .15s ease;
    }
    .range-input::-webkit-slider-thumb:hover { transform: scale(1.15); }
    .range-input::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--primary-color); border: 3px solid var(--bg-card); box-shadow: 0 2px 6px rgba(47, 91, 255, .35); cursor: pointer; }
    .range-input.primary::-webkit-slider-thumb { background: var(--primary-color); }
    .range-input.success::-webkit-slider-thumb { background: var(--success-color); box-shadow: 0 2px 6px rgba(18, 183, 106, .35); }
    .range-input.warning::-webkit-slider-thumb { background: var(--warning-color); box-shadow: 0 2px 6px rgba(224, 138, 0, .35); }
    .range-input.danger::-webkit-slider-thumb { background: var(--danger-color); box-shadow: 0 2px 6px rgba(240, 86, 138, .35); }
.slider-value { font-size: 13px; font-weight: 600; color: var(--text-primary); min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; }

/* 评分 */
.rating { display: inline-flex; align-items: center; gap: 5px; }
    .rating > .icon { font-size: 19px; color: var(--border-color); cursor: pointer; transition: transform .15s ease, color .15s ease; }
    .rating > .icon.on { color: var(--warning-color); }
    .rating > .icon:hover { transform: scale(1.15); }
.rating-value { font-size: 13px; color: var(--text-muted); margin-left: 6px; font-weight: 600; font-variant-numeric: tabular-nums; }
.rating[data-readonly='1'] > .icon { cursor: default; }
    .rating[data-readonly='1'] > .icon:hover { transform: none; }

/* 取色器 */
.color-picker { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.swatch {
    width: 30px;
    height: 30px;
    border-radius: 8px;
    cursor: pointer;
    border: 2px solid transparent;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .07);
    transition: transform .15s ease, border-color .15s ease;
    position: relative;
}
    .swatch:hover { transform: scale(1.1); }
    .swatch.active { border-color: var(--text-primary); }
    .swatch.active::after {
        content: '';
        position: absolute;
        inset: 0;
        background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/polyline%3E%3C/svg%3E") center / 15px no-repeat;
        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .4));
    }
.color-native { width: 42px; height: 32px; border: 1px solid var(--border-color); border-radius: 8px; background: var(--bg-input); cursor: pointer; padding: 2px; }

/* 自动补全 */
.autocomplete { position: relative; }
    .autocomplete .input-affix > .icon:first-child { left: auto; right: 13px; }
    .autocomplete .input-affix > .input { padding-right: 36px; }
.autocomplete-list {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    z-index: 400;
    max-height: 220px;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: all .18s ease;
}
    /* 列表开启时会搬到 body（传送门），脱离 .autocomplete 这层祖先，故开合态同时看列表自身的 open 类 */
    .autocomplete.open .autocomplete-list,
    .autocomplete-list.open { opacity: 1; visibility: visible; transform: translateY(0); }
    /* 传送门还没写坐标那一帧不许露脸：否则列表先按本体位置闪一下，再跳到翻转后的位置 */
    .autocomplete-list.open:not(.placed) { visibility: hidden; }
/* 关闭态脱流（不撑高 .modal-body 的 scrollHeight）见后文「关闭态浮层统一脱流」清单。 */
.autocomplete-opt {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition);
}
    .autocomplete-opt .icon { font-size: 12px; color: var(--text-muted); }
    .autocomplete-opt:hover, .autocomplete-opt.active { background: var(--bg-hover); color: var(--text-primary); }
.autocomplete-empty { padding: 18px; text-align: center; font-size: 13px; color: var(--text-muted); }

/* 下拉表格 */
.selecttable { position: relative; }
    .selecttable .input-affix > .icon:first-child { left: auto; right: 13px; }
    .selecttable .input-affix > .input { padding-right: 36px; }
.selecttable-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    min-width: 100%;
    width: max-content;
    max-width: 560px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    z-index: 400;
    max-height: 260px;
    overflow: auto;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: all .18s ease;
}
    .selecttable.open .selecttable-panel { opacity: 1; visibility: visible; transform: translateY(0); }
.selecttable-table { font-size: 13px; }
    .selecttable-table th { position: sticky; top: 0; z-index: 1; padding: 8px 12px; font-size: 12px; }
    .selecttable-table td { padding: 8px 12px; }
    .selecttable-table tbody tr { cursor: pointer; }
    .selecttable-table tbody tr.active { background: var(--primary-soft); }
    .selecttable-table tbody tr.selected { background: var(--primary-soft); box-shadow: inset 3px 0 0 var(--primary-color); }
    .selecttable-table tbody tr.selected td { color: var(--primary-color); }
.selecttable-empty { text-align: center; color: var(--text-muted); cursor: default; }
.selecttable.selecttable-disabled { opacity: .6; }

/* 级联选择（单框多列，触发框复用 .select 样式） */
.cascader-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    display: flex;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    z-index: 400;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: all .18s ease;
}
    .select.open .cascader-panel,
    .cascader-panel.open { opacity: 1; visibility: visible; transform: translateY(0); }
    /* 开合瞬间：加了 open 类、传送门还没写坐标那一帧先按住，避免面板在原位闪一下再跳走 */
    .cascader-panel.open:not(.placed) { visibility: hidden; }
.cascader-col {
    min-width: 148px;
    max-height: 240px;
    overflow-y: auto;
    padding: 0 6px;
}
    .cascader-col + .cascader-col { border-left: 1px solid var(--border-light); }
.cascader-node {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition);
}
    .cascader-node > span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .cascader-node:hover { background: var(--bg-hover); color: var(--text-primary); }
    .cascader-node.active { background: var(--bg-hover); color: var(--primary-color); font-weight: 600; }
.cascader-caret { color: var(--text-muted); font-size: 12px; }
.cascader-check { color: var(--primary-color); }

/* 日期选择（自定义下拉日历，触发框复用 .select 样式） */
.date-input { padding-left: 34px; }
    .date-input > .date-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }
.date-overlay { position: fixed; inset: 0; z-index: 1000; }
.date-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    width: 280px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 10px;
    z-index: 1001;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: all .18s ease;
}
    .select.open .date-panel,
    .date-panel.open { opacity: 1; visibility: visible; transform: translateY(0); }
    /* 开合瞬间：加了 open 类、传送门还没写坐标那一帧先按住，避免面板在原位闪一下再跳走 */
    .date-panel.open:not(.placed) { visibility: hidden; }
.date-header { display: flex; align-items: center; justify-content: space-between; gap: 2px; padding: 2px 2px 8px; }
.date-title { flex: 1; text-align: center; font-size: 14px; font-weight: 600; color: var(--text-primary); }
.date-nav { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: none; background: transparent; border-radius: var(--radius-sm); color: var(--text-muted); cursor: pointer; transition: all var(--transition); }
    .date-nav:hover { background: var(--bg-hover); color: var(--text-primary); }
.date-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); margin-bottom: 4px; }
    .date-weekdays span { text-align: center; font-size: 12px; color: var(--text-muted); padding: 4px 0; }
.date-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.date-cell { height: 34px; border: none; background: transparent; border-radius: var(--radius-sm); font-family: inherit; font-size: 13px; color: var(--text-primary); cursor: pointer; transition: all var(--transition); }
    .date-cell:hover { background: var(--bg-hover); }
    .date-cell.muted { color: var(--text-muted); opacity: .45; }
    .date-cell.today { box-shadow: inset 0 0 0 1px var(--primary-color); color: var(--primary-color); }
    .date-cell.selected { background: var(--primary-color); color: #fff; box-shadow: none; opacity: 1; }
    .date-cell.disabled { opacity: .35; cursor: not-allowed; }
    .date-cell.disabled:hover { background: transparent; }
.date-months { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; padding: 6px 2px; }
.date-month { height: 40px; border: 1px solid var(--border-color); background: var(--bg-input); border-radius: var(--radius-sm); font-family: inherit; font-size: 13px; color: var(--text-primary); cursor: pointer; transition: all var(--transition); }
    .date-month:hover { border-color: var(--primary-color); color: var(--primary-color); }
    .date-month.selected { background: var(--primary-color); border-color: var(--primary-color); color: #fff; }
.date-time { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 14px 0; }
.date-time-inline { padding: 10px 0 2px; border-top: 1px solid var(--border-light); margin-top: 6px; }
.date-num { width: 52px; padding: 6px 8px; text-align: center; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-sm); font-family: inherit; font-size: 15px; color: var(--text-primary); }
    .date-num:focus { border-color: var(--primary-color); outline: none; }
.date-colon { font-size: 16px; font-weight: 600; color: var(--text-secondary); }
.date-footer { display: flex; align-items: center; justify-content: space-between; padding-top: 8px; margin-top: 6px; border-top: 1px solid var(--border-light); }
.date-foot-right { display: inline-flex; gap: 4px; }
.date-btn { border: none; background: transparent; color: var(--primary-color); font-family: inherit; font-size: 13px; cursor: pointer; padding: 4px 8px; border-radius: var(--radius-sm); transition: all var(--transition); }
    .date-btn:hover { background: var(--primary-soft); }
    .date-btn.primary { background: var(--primary-color); color: #fff; }
        .date-btn.primary:hover { background: var(--primary-color); filter: brightness(1.05); }

/* 日历（复用 .date-weekdays / .date-grid / .date-cell / .date-months / .date-month 样式） */
.calendar { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: 12px; }
.calendar-full { padding: 16px 20px 20px; }
.calendar-header { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 2px 2px 12px; }
    .calendar-full .calendar-header { justify-content: space-between; }
.calendar-nav { display: inline-flex; align-items: center; gap: 4px; }
.calendar-title { border: none; background: transparent; font-family: inherit; font-size: 15px; font-weight: 600; color: var(--text-primary); cursor: pointer; padding: 4px 12px; border-radius: var(--radius-sm); transition: all var(--transition); }
    .calendar-title:hover { background: var(--bg-hover); color: var(--primary-color); }
.calendar-right { display: inline-flex; align-items: center; gap: 12px; }
.calendar-seg { display: inline-flex; background: var(--bg-hover); border-radius: var(--radius-sm); padding: 2px; }
    .calendar-seg button { border: none; background: transparent; font-family: inherit; font-size: 13px; color: var(--text-secondary); padding: 4px 14px; border-radius: var(--radius-sm); cursor: pointer; transition: all var(--transition); }
        .calendar-seg button.active { background: var(--bg-card); color: var(--primary-color); font-weight: 600; box-shadow: var(--shadow-sm); }
.calendar-grid-full { gap: 0; border-top: 1px solid var(--border-light); border-left: 1px solid var(--border-light); }
.calendar-cell { display: flex; height: auto; min-height: 86px; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 0; padding: 4px 6px 6px; border-right: 1px solid var(--border-light); border-bottom: 1px solid var(--border-light); border-radius: 0; background: var(--bg-card); }
    .calendar-cell:hover { background: var(--bg-hover); }
    .calendar-cell.today { box-shadow: none; }
    .calendar-cell.today .calendar-day { color: var(--primary-color); font-weight: 700; }
    .calendar-cell.selected { background: var(--primary-soft); color: var(--text-primary); }
.calendar-day { align-self: flex-end; font-size: 13px; line-height: 22px; min-width: 22px; text-align: center; color: var(--text-primary); }
.calendar-content { display: block; align-self: stretch; flex: 1; margin-top: 2px; font-size: 12px; text-align: left; color: var(--text-secondary); overflow: hidden; }
.calendar-events { display: flex; flex-direction: column; align-self: stretch; gap: 3px; margin-top: 4px; }
.cal-evt { display: flex; align-items: center; gap: 4px; padding: 2px 4px 2px 6px; border-left: 2px solid var(--evt, var(--primary-color)); border-radius: var(--radius-sm); background: var(--bg-hover); font-size: 11.5px; line-height: 16px; max-width: 100%; }
.cal-evt-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-primary); }
.cal-evt-del { border: none; background: transparent; color: var(--text-muted); font-family: inherit; cursor: pointer; display: inline-flex; align-items: center; padding: 0 2px; opacity: 0; transition: all var(--transition); }
    .cal-evt:hover .cal-evt-del { opacity: 1; }
    .cal-evt-del:hover { color: var(--danger-color); }
.cal-add-btn { display: inline-flex; align-items: center; gap: 3px; align-self: flex-start; border: none; background: transparent; color: var(--text-muted); font-family: inherit; font-size: 11px; padding: 2px 5px; border-radius: var(--radius-sm); cursor: pointer; opacity: .45; transition: all var(--transition); }
    .calendar-cell:hover .cal-add-btn { opacity: 1; }
    .cal-add-btn:hover { color: var(--primary-color); background: var(--primary-soft); }
.cal-add { margin-top: 2px; }
.cal-add-input { width: 100%; box-sizing: border-box; padding: 2px 6px; border: 1px solid var(--primary-color); border-radius: var(--radius-sm); background: var(--bg-input); font-family: inherit; font-size: 11.5px; color: var(--text-primary); outline: none; }

/* 穿梭框 */
.transfer { display: flex; align-items: stretch; gap: 12px; }
.transfer-box {
    flex: 1;
    min-width: 0;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-input);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.transfer-head {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-color);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: var(--bg-hover);
}
    .transfer-head .count { font-size: 11px; background: var(--badge-bg); color: var(--badge-text); padding: 1px 7px; border-radius: 20px; font-weight: 600; }
.transfer-body { max-height: 180px; overflow-y: auto; padding: 6px 0; flex: 1; }
.transfer-item { display: flex; align-items: center; gap: 9px; padding: 7px 14px; font-size: 13px; color: var(--text-primary); cursor: pointer; transition: background var(--transition); }
    .transfer-item:hover { background: var(--bg-hover); }
    .transfer-item input { width: 16px; height: 16px; accent-color: var(--primary-color); cursor: pointer; margin: 0; flex-shrink: 0; }
.transfer-actions { display: flex; flex-direction: column; justify-content: center; gap: 8px; }
.transfer-btn {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-color);
    background: var(--bg-card);
    color: var(--text-secondary);
    font-size: 13px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all var(--transition);
}
    .transfer-btn:not(:disabled):hover { background: var(--primary-soft); color: var(--primary-color); border-color: var(--primary-color); }
    .transfer-btn:disabled { opacity: .35; cursor: not-allowed; }

/* 上传 */
.upload-zone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    border: 2px dashed var(--border-color);
    border-radius: var(--radius-md);
    padding: 32px 20px;
    text-align: center;
    transition: all var(--transition);
    cursor: pointer;
    background: var(--bg-input);
}
    .upload-zone:hover, .upload-zone.dragging { border-color: var(--primary-color); background: var(--primary-soft); }
    .upload-zone .icon { font-size: 34px; color: var(--primary-color); margin-bottom: 12px; }
    .upload-zone .upload-title { font-size: 14px; font-weight: 500; color: var(--text-primary); margin-bottom: 4px; }
    .upload-zone .upload-title em { font-style: normal; color: var(--primary-color); }
    .upload-zone .upload-desc { font-size: 12px; color: var(--text-muted); }
.upload-list { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.upload-file { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: var(--radius-sm); background: var(--bg-hover); font-size: 13px; }
    .upload-file > .icon.file-icon { color: var(--primary-color); font-size: 16px; }
.upload-file-name { flex: 1; min-width: 0; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.upload-file-size { font-size: 11.5px; color: var(--text-muted); }
    .upload-file .status-ok { color: var(--success-color); font-size: 14px; }
    .upload-file .status-error { color: var(--danger-color); font-size: 14px; }
.upload-progress { width: 60px; height: 4px; background: var(--border-color); border-radius: 4px; overflow: hidden; }
    .upload-progress span { display: block; height: 100%; background: var(--primary-color); border-radius: 4px; transition: width .2s ease; }
.upload-remove { background: transparent; border: none; color: var(--text-muted); cursor: pointer; font-size: 13px; padding: 2px; border-radius: 4px; transition: color var(--transition); }
    .upload-remove:hover { color: var(--danger-color); }
/* 上传：按钮模式 */
/* 触发器是 <label>：按钮元素走 UA 的 line-height:normal，label 却继承正文的 21px，
   并排时上传按钮会比邻座高 3px。跟 <button> 取同一个行高才对齐。 */
.upload-btn { cursor: pointer; line-height: normal; }
/* 上传：图片卡片模式 */
.upload-picture { display: flex; flex-wrap: wrap; gap: 12px; }
    .upload-picture-trigger, .upload-picture-item { width: 104px; height: 104px; border-radius: var(--radius-md); position: relative; box-sizing: border-box; }
    .upload-picture-trigger {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 8px;
        border: 1px dashed var(--border-color);
        background: var(--bg-input);
        color: var(--text-muted);
        font-size: 13px;
        cursor: pointer;
        transition: all var(--transition);
    }
        .upload-picture-trigger:hover { border-color: var(--primary-color); color: var(--primary-color); background: var(--primary-soft); }
        .upload-picture-trigger .icon { color: inherit; }
    .upload-picture-item { border: 1px solid var(--border-color); overflow: hidden; background: var(--bg-hover); }
        .upload-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
        .upload-thumb-ph { width: 100%; height: 100%; display: grid; place-items: center; color: var(--text-muted); }
        .upload-thumb-bar {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 5px 8px;
            background: rgba(0, 0, 0, .55);
            color: #fff;
            font-size: 11px;
            opacity: 0;
            transition: opacity var(--transition);
        }
        .upload-picture-item:hover .upload-thumb-bar { opacity: 1; }
        .upload-thumb-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .upload-picture-item .upload-remove { color: #fff; padding: 0; }
            .upload-picture-item .upload-remove:hover { color: var(--danger-color); }

/* Markdown 编辑器：工具栏 + 源码 / 实时预览，分栏或整幅 */
/* overflow 必须 visible：工具栏的 data-tip 气泡（::after 绝对定位）会被 hidden 整块裁掉，圆角改由下面三个子元素各自负责 */
.md-editor { position: relative; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-input); transition: border-color var(--transition), box-shadow var(--transition); }
    .md-editor:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
    .md-editor.fullscreen { position: fixed; inset: 0; z-index: 3000; display: flex; flex-direction: column; border: none; border-radius: 0; background: var(--bg-card); }
    .md-editor.fullscreen .md-body { flex: 1; min-height: 0; }
    .md-editor.fullscreen > * { border-radius: 0; }

.md-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; padding: 5px 8px; background: var(--bg-hover); border-bottom: 1px solid var(--border-light); border-radius: var(--radius-md) var(--radius-md) 0 0; }
/* 工具栏贴着边框上沿（全屏时贴住视口顶），气泡只能朝下弹，同 .top-actions 的处理 */
    .md-tools [data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
    .md-tools [data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
    .md-tools [data-tip]:hover::after, .md-tools [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
/* 全屏按钮与视图切换最后一项贴着工具栏右沿，居中的气泡会有一半探出编辑器与视口（窄屏全屏时尤甚），改成右对齐 */
    .md-tools > .md-tool:last-child[data-tip]::after, .md-views > .md-view:last-child[data-tip]::after { left: auto; right: 0; transform: translateY(-4px); }
    .md-tools > .md-tool:last-child[data-tip]::before { left: auto; right: 8px; transform: translateY(-4px); }
    .md-views > .md-view:last-child[data-tip]::before { left: auto; right: 7px; transform: translateY(-4px); }
    .md-tools > .md-tool:last-child[data-tip]:hover::after, .md-tools > .md-tool:last-child[data-tip]:hover::before, .md-views > .md-view:last-child[data-tip]:hover::after, .md-views > .md-view:last-child[data-tip]:hover::before { transform: translateY(0); }
.md-tool { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: all var(--transition); }
    .md-tool:hover { background: var(--bg-card); color: var(--primary-color); }
    .md-tool:disabled { color: var(--text-muted); background: transparent; opacity: .45; cursor: not-allowed; }
    .md-tool .icon { color: inherit; }
.md-tool-sep { width: 1px; height: 16px; margin: 0 5px; background: var(--border-color); }
.md-tool-grow { flex: 1 1 auto; }
.md-views { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--radius-sm); background: var(--bg-card); }
.md-view { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 22px; border: none; border-radius: 4px; background: transparent; color: var(--text-muted); cursor: pointer; transition: all var(--transition); }
    .md-view:hover { color: var(--primary-color); }
    .md-view.on { background: var(--primary-soft); color: var(--primary-color); }

.md-body { display: flex; align-items: stretch; }
    .md-body > * { flex: 1 1 50%; min-width: 0; height: 100%; }
    .md-body:not(.split) > * { flex-basis: 100%; }
.md-input { display: block; box-sizing: border-box; padding: 12px 14px; border: none; outline: none; background: transparent; color: var(--text-primary); resize: none; font-family: var(--font-mono, ui-monospace, monospace); font-size: 13.5px; line-height: 1.75; }
    .md-input::placeholder { color: var(--text-muted); }
.md-preview { box-sizing: border-box; padding: 12px 18px; overflow: auto; background: var(--bg-card); border-left: 1px solid var(--border-light); }
    .md-body:not(.split) .md-preview { border-left: none; }
.md-preview-empty { padding: 30px 0; text-align: center; color: var(--text-muted); font-size: 13px; }
.md-foot { display: flex; align-items: center; gap: 14px; padding: 5px 12px; border-top: 1px solid var(--border-light); background: var(--bg-hover); font-size: 11.5px; color: var(--text-muted); border-radius: 0 0 var(--radius-md) var(--radius-md); }
    .md-foot b { color: var(--text-secondary); font-weight: 500; }
    .md-foot-tip { margin-left: auto; }
/* 关掉底栏时，圆角落在编辑区这一层 */
.md-editor > .md-body:last-child > :last-child { border-radius: 0 0 var(--radius-md) 0; }
.md-editor > .md-body:last-child > :first-child { border-radius: 0 0 0 var(--radius-md); }
    .md-editor > .md-body:last-child > :first-child:last-child { border-radius: 0 0 var(--radius-md) var(--radius-md); }

/* Markdown 预览排版：类名由 MarkdownRenderer 产出，全局 table/th/td 的重 padding 规则要在这一层收回 */
.md-md { color: var(--text-primary); font-size: 14px; line-height: 1.75; overflow-wrap: break-word; }
    .md-md > :first-child { margin-top: 0; }
    .md-md > :last-child { margin-bottom: 0; }
    .md-md p { margin: 0 0 12px; }
    .md-md h1, .md-md h2, .md-md h3, .md-md h4, .md-md h5, .md-md h6 { margin: 20px 0 10px; font-weight: 600; line-height: 1.35; }
    .md-md h1 { font-size: 22px; }
    .md-md h2 { font-size: 18px; }
    .md-md h3 { font-size: 16px; }
    .md-md h4, .md-md h5, .md-md h6 { font-size: 14.5px; }
    .md-md ul, .md-md ol { margin: 0 0 12px; padding-left: 22px; }
    .md-md li { margin: 4px 0; }
    .md-md li::marker { color: var(--text-muted); }
    .md-md li.md-task { display: flex; align-items: flex-start; gap: 7px; margin-left: -20px; list-style: none; }
        .md-md li.md-task input { margin-top: 5px; }
        .md-md li.md-task.done { color: var(--text-muted); }
    .md-md code { padding: 1px 5px; border-radius: 4px; background: var(--bg-hover); color: var(--primary-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12.5px; }
    .md-md .md-pre { margin: 0 0 12px; padding: 12px 14px; border-radius: var(--radius-sm); background: var(--bg-hover); overflow-x: auto; }
        .md-md .md-pre code { padding: 0; border-radius: 0; background: transparent; color: var(--text-primary); line-height: 1.6; }
    .md-md .md-quote { margin: 0 0 12px; padding: 8px 14px; border-left: 3px solid var(--primary-color); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; background: var(--bg-hover); color: var(--text-secondary); }
        .md-md .md-quote > :first-child { margin-top: 0; }
        .md-md .md-quote > :last-child { margin-bottom: 0; }
    .md-md table.md-table { width: 100%; margin: 0 0 12px; border-collapse: collapse; font-size: 13px; }
        .md-md table.md-table th, .md-md table.md-table td { padding: 6px 10px; border: 1px solid var(--border-light); white-space: normal; }
        .md-md table.md-table th { background: var(--bg-hover); color: var(--text-secondary); font-weight: 600; }
        .md-md table.md-table tbody tr:hover { background: transparent; }
    .md-md img.md-img { max-width: 100%; margin: 4px 0; border-radius: var(--radius-sm); }
    .md-md a.md-link { color: var(--primary-color); text-decoration: none; }
        .md-md a.md-link:hover { text-decoration: underline; }
    .md-md del { color: var(--text-muted); }
    .md-md strong { font-weight: 600; }
    .md-md hr { margin: 16px 0; border: none; border-top: 1px solid var(--border-color); }

/* 查询筛选栏 */
.filter-bar { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; }
.filter-item { display: flex; flex-direction: column; gap: 6px; min-width: 168px; flex: 1 1 168px; }
    .filter-item .form-label { margin: 0; }
.filter-actions { display: flex; gap: 10px; flex: 0 0 auto; }

/* ── 表格 ── */
.table-wrapper { overflow-x: auto; border-radius: var(--radius-sm); border: 1px solid var(--border-color); }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
    text-align: left;
    background: var(--table-head-bg);
    color: var(--text-secondary);
    font-weight: 500;
    font-size: 13px;
    border-bottom: 1px solid var(--border-color);
    white-space: nowrap;
}
td { padding-top: var(--cell-pad-y); padding-bottom: var(--cell-pad-y); border-bottom: 1px solid var(--border-light); color: var(--text-primary); white-space: nowrap; }
table:not(.pg-grid):not(.table-pro) th { padding-left: 18px; padding-right: 18px; }
table:not(.pg-grid):not(.table-pro) td { padding-left: 18px; padding-right: 18px; }
    tr:last-child td { border-bottom: none; }
    tbody tr { transition: background var(--transition); }
    tbody tr:hover { background: var(--bg-hover); }

.table-pro th.sortable { cursor: pointer; user-select: none; }
    .table-pro th.sortable:hover { color: var(--text-primary); }
    .table-pro th .sort-ico { font-size: 9px; margin-left: 5px; color: var(--text-muted); transition: color var(--transition); }
    .table-pro th.sorted { color: var(--primary-color); }
        .table-pro th.sorted .sort-ico { color: var(--primary-color); }
.table-pro td.row-check, .table-pro th.row-check { width: 46px; text-align: center; padding-left: 0; padding-right: 0; }
.table-pro input[type='checkbox'] { width: 16px; height: 16px; accent-color: var(--primary-color); cursor: pointer; vertical-align: middle; margin: 0; }
.table-pro tbody tr.selected { background: var(--primary-soft); }
    .table-pro tbody tr.selected:hover { background: var(--primary-soft); filter: brightness(.97); }
    [data-theme='dark'] .table-pro tbody tr.selected:hover { filter: brightness(1.12); }
.table-pro td.row-action { text-align: right; padding: 4px 8px; }
/* 行内操作按钮：垂直内边距收到最小，让操作列不再撑高整行（行高由单元格留白决定） */
.table-pro .row-actions .btn-xs { padding: 2px 8px; }
.table-fixed { table-layout: fixed; }
.table-striped tbody tr:nth-child(even) { background: var(--table-stripe-bg); }
.table-bordered td, .table-bordered th { border-right: 1px solid var(--border-light); }

.table-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 12px; border-bottom: 1px solid var(--border-color); flex-wrap: wrap; }
    .table-toolbar .sel-info { font-size: 13px; color: var(--text-secondary); }
        .table-toolbar .sel-info strong { color: var(--primary-color); font-weight: 600; }
    .table-toolbar .toolbar-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.batch-bar { display: flex; align-items: center; gap: 12px; padding: 10px 12px; background: var(--primary-soft); border-bottom: 1px solid var(--border-color); font-size: 13px; color: var(--primary-color); font-weight: 500; }
    .batch-bar .batch-actions { margin-left: auto; display: flex; gap: 8px; }
.table-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 40px 20px; color: var(--text-muted); }
    .table-empty .icon { font-size: 30px; opacity: .5; }
    .table-empty span { font-size: 13.5px; }
.table-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 34px; font-size: 13.5px; color: var(--text-secondary); }

/* ── 表格：滚动容器 / 固定表头 / 固定列 ── */
.table-wrapper { overflow: hidden; }
/* overflow-anchor 必须关闭：虚拟滚动的上下占位行高度随渲染变化，
   浏览器的滚动锚定会自动补偿 scrollTop，触发新的 scroll 事件，
   与 SetViewport → StateHasChanged 形成永不收敛的循环。 */
.table-scroll { overflow: auto; overflow-anchor: none; }
    .table-scroll > table { border-collapse: separate; border-spacing: 0; }
.table-layout-fixed { table-layout: fixed; }
/* 仅在有纵向滚动容器时把表头吸附到容器顶部，避免无 ScrollY 时表头被页面滚动条吸附 */
.table-scroll.scroll-y > table > thead > tr > th { position: sticky; top: 0; z-index: 3; background: var(--table-head-bg); }

.stk-l, .stk-r { position: sticky; }
/* 固定列数据单元格需不透明底色，横向滚动时盖住从下方滑过的普通单元格 */
.table-scroll td.stk-l, .table-scroll td.stk-r { z-index: 2; background: var(--bg-card); }
/* 固定列表头角单元格：不透明底色 + 更高 z-index，随横向滚动时普通表头不得盖过它。
   层级必须无条件给：.table-pro thead th 是 position:relative（为表头气泡定位），
   普通表头据此按 DOM 顺序绘制、后绘制的盖住先绘制的；而 z-index:5 原本只写在
   .scroll-y 分支，控制台页的表格（高度由外层卡片给、无 ScrollY）钉住的表头就没有任何
   层级——横向滚动时「客户/日期」等普通表头从钉住的「单号」表头上碾过，只在左缘漏残字。
   给 4：高于普通表头(auto)，低于悬停抬层(6)与 scroll-y 角格(5)。 */
.table-scroll th.stk-l, .table-scroll th.stk-r { background: var(--table-head-bg); z-index: 4; }
/* 与 scroll-y 基础规则同特异性(0,2,4)但后置，z-index 抬到 5，确保角单元格压在最上 */
.table-scroll.scroll-y > table > thead > tr > th.stk-l,
.table-scroll.scroll-y > table > thead > tr > th.stk-r { z-index: 5; }
.stk-l { box-shadow: 1px 0 0 var(--border-light); }
.stk-r { box-shadow: -1px 0 0 var(--border-light); }
/* 悬停抬层：钉左/钉右的粘性单元格带 z-index:2，本身已是一个层叠上下文，操作列图标的 data-tip 气泡（::after，z-index 400）
   出不去这个上下文，于是第一行的提示被固定表头（th 3 / th.stk-r 5，还是不透明底色）整个盖住。
   抬到 6 压过表头与钉底行（同为 5）；只动 z-index，绝不能改 position——sticky 一改单元格就不粘了。 */
.table-scroll tbody td.stk-l:hover, .table-scroll tbody td.stk-r:hover { z-index: 6; }
/* 固定列的斑马纹 / 悬停 / 选中：三者同特异性(0,4,3)，靠源码顺序 base→zebra→hover→selected 逐级覆盖 */
    .table-scroll .table-striped tbody tr:nth-child(even) td.stk-l,
    .table-scroll .table-striped tbody tr:nth-child(even) td.stk-r { background: var(--table-stripe-bg); }
    .table-scroll .table-pro tbody tr:hover td.stk-l,
    .table-scroll .table-pro tbody tr:hover td.stk-r { background: var(--bg-hover); }
    .table-scroll .table-pro tbody tr.selected td.stk-l,
    .table-scroll .table-pro tbody tr.selected td.stk-r { background: var(--primary-soft); }

/* ── 合计行：普通模式在主表 tfoot；滚动模式单独作为 footer table 贴在底部，避免 sticky tfoot 保留原始占位造成尾部留白 ── */
.table-pro tfoot td { font-weight: 600; color: var(--text-primary); background: var(--table-head-bg); border-top: 1px solid var(--border-light); }
.table-scroll.has-sticky-footer > table > tfoot > tr > td {
    position: sticky;
    z-index: 3;
    box-sizing: border-box;
    height: var(--tfoot-h, 42px);
    padding-top: 0;
    padding-bottom: 0;
    line-height: calc(var(--tfoot-h, 42px) - 2px);
}
.table-scroll.has-sticky-footer > table > tfoot > tr:first-child > td { bottom: var(--tfoot-h, 42px); }
.table-scroll.has-sticky-footer > table > tfoot > tr:last-child > td { bottom: 0; }
/* 合计行的钉列角单元格：保持表头底色并抬到最上，横滚时不被普通格盖过 */
.table-scroll > table > tfoot > tr > td.stk-l,
.table-scroll > table > tfoot > tr > td.stk-r { background: var(--table-head-bg); }
.table-scroll.has-sticky-footer > table > tfoot > tr > td.stk-l,
.table-scroll.has-sticky-footer > table > tfoot > tr > td.stk-r { z-index: 5; }

/* 滚动容器内关闭态浮层脱流的处理已统一到「关闭态浮层统一脱流」清单（本处原为 .table-scroll 局部补丁）。 */

/* ── 表头：标题 + 排序指示器 + 过滤框 ── */
.table-pro thead th { font-weight: 700; font-size: 12px; color: var(--text-muted); background: var(--table-head-bg); border-bottom: 1px solid var(--border-light); padding: var(--cell-pad-y) 12px; }
/* 「操作」列头：普通列头文字裹在 .th-title 里（全局 .th-title = 13px/600/--text-secondary），
   操作列是无包裹的纯文本，会落到 th 自身的 12px/700/muted 上，于是和其他列头不一致；这里对齐到同一档 */
.table-pro thead th.row-action { font-size: 13px; font-weight: 600; color: var(--text-secondary); }
.table-pro th .th-main { display: inline-flex; align-items: center; gap: 6px; }
.table-pro th.sortable .th-main { cursor: pointer; user-select: none; }
.table-pro .sorter { display: inline-flex; flex-direction: column; gap: 3px; vertical-align: middle; }
    .table-pro .sorter .caret { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; opacity: .38; transition: opacity var(--transition), border-color var(--transition); }
    .table-pro .sorter .up { border-bottom: 5px solid var(--text-muted); }
    .table-pro .sorter .down { border-top: 5px solid var(--text-muted); }
    .table-pro .sorter.asc .up, .table-pro .sorter.desc .down { opacity: 1; border-bottom-color: var(--primary-color); border-top-color: var(--primary-color); }
.table-pro th.sortable:hover .sorter .caret { opacity: .7; }
.table-pro th.sorted-asc .th-title, .table-pro th.sorted-desc .th-title { color: var(--primary-color); }
/* 列头过滤：漏斗图标 + 下拉面板 */
.table-pro thead th { position: relative; }
.table-pro th.th-open { z-index: 50; }
.table-pro .th-filter-btn { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin-left: 2px; padding: 0; border: none; background: transparent; color: var(--text-muted); border-radius: var(--radius-sm); cursor: pointer; vertical-align: middle; transition: color var(--transition), background var(--transition); }
    .table-pro .th-filter-btn:hover { background: var(--bg-hover); color: var(--text-secondary); }
    .table-pro .th-filter-btn.active { color: var(--primary-color); }
/* 表头贴着滚动容器顶边（scroll-y 时还 sticky 在 top:0），气泡向上弹出会被容器整个裁掉，改为向下弹出 */
.table-pro thead [data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.table-pro thead [data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.table-pro thead [data-tip]:hover::after, .table-pro thead [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
.table-pro .th-filter-pop { position: absolute; top: calc(100% + 5px); left: 0; z-index: 60; display: flex; align-items: center; gap: 6px; padding: 8px; white-space: nowrap; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-md); box-shadow: var(--shadow-md); }
    .table-pro .th-filter-pop .th-filter-input { box-sizing: border-box; width: 160px; padding: 5px 8px; font-size: 12px; font-weight: 400; color: var(--text-primary); background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-sm); }
    .table-pro .th-filter-pop .th-filter-input::-webkit-search-cancel-button { -webkit-appearance: none; }
    .table-pro .th-filter-pop .th-filter-clear { padding: 5px 8px; font-size: 12px; color: var(--text-secondary); background: transparent; border: 1px solid var(--border-color); border-radius: var(--radius-sm); cursor: pointer; }
        .table-pro .th-filter-pop .th-filter-clear:hover { color: var(--primary-color); border-color: var(--primary-color); }

/* ── 单元格省略号 / 内容不越界 ── */
/* 单元格只裁横向、不裁纵向：
   横向 —— 固定布局下超宽文字会串到相邻列，渗出表格右边框后还会计入滚动容器的 scrollWidth，横向滚到尽头时右侧留白；
   纵向 —— 必须保持 visible，否则单元格内 [data-tip] 气泡（::after 绝对定位在图标上方）会被裁成一条细边。
   注：不能用 overflow-clip-margin 折中，溢出量会计入 scrollWidth（实测 clip+60px 仍多出 59px）。 */
.table-pro td { overflow-x: clip; overflow-y: visible; }
/* 操作列只有图标按钮，内容不会横向溢出，放开双向裁剪，让气泡完整显示（含被钉列/表头压盖的情况，见 .stk-* 悬停抬层） */
.table-pro td.row-action { overflow: visible; }
/* 操作列的气泡贴右缘向左长（同 .cp-toolbar-right 与 data-tip-align="end" 的写法）。
   默认的 left:50% + translateX(-50%) 居中气泡有一半甩在按钮右边，而按钮就顶着表格右缘，
   这一截又落在唯一不裁剪的 td.row-action 里，于是被算进 .table-scroll 的 scrollWidth：
   明明放得下的表格凭空多出一条横向滚动条，钉右列的 .stk-r 分隔线也跟着出现（实测 700px 容器 scrollWidth 848）。
   箭头照旧挪 10px 保持视觉居中，hover 时只补回 translateY，别再带上 translateX(-50%)。 */
.table-pro td.row-action [data-tip]::after { left: auto; right: 0; transform: translateY(4px); }
.table-pro td.row-action [data-tip]::before { left: auto; right: 10px; transform: translateY(4px); }
    .table-pro td.row-action [data-tip]:hover::after,
    .table-pro td.row-action [data-tip]:hover::before { transform: translateY(0); }
/* 省略号单元格：裁剪放在内层 .ell-in，外层 .ell 不裁剪。
   若 .ell 自己 overflow:hidden，挂在它身上的 data-tip 气泡（绝对定位在文字上方）会被裁成一条边。 */
.table-pro td.cell-ell .ell { display: block; max-width: 100%; }
.table-pro td.cell-ell .ell-in { display: block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 气泡按单元格宽度换行：td 是 overflow-x:clip，气泡若比列宽就横向被切；限宽后左右不越界，纵向照常向上弹出 */
.table-pro td.cell-ell .ell[data-tip]::after { white-space: normal; width: max-content; max-width: 100%; line-height: 1.45; text-align: left; }
/* 首行省略号气泡改为向下弹：默认向上会撞进 sticky 表头带（.cp-page 卡片把 thead th 钉在 z-index:5、scroll-y 还钉 top:0），
   标题一长就换行变高，气泡顶边被滚动容器（.table-scroll overflow:auto）顶边整个裁掉，看着就像被表头「遮挡」。
   只翻首行——它上方紧贴表头没余量；其余行上方还有整行高度，保持默认向上弹。方向/箭头/滑入与 thead 那套（见上）同口径。 */
.table-pro tbody tr:first-child td.cell-ell .ell[data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.table-pro tbody tr:first-child td.cell-ell .ell[data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
    .table-pro tbody tr:first-child td.cell-ell .ell[data-tip]:hover::after,
    .table-pro tbody tr:first-child td.cell-ell .ell[data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
/* 行高收紧 + 与 TablePage(.pg-grid) 观感统一：表体 13px、12px 横向内边距、柔和正文色（text-secondary）、分隔线用 border-light */
.table-pro { font-size: 13px; }
/* 收紧行高：单元格上下留白从 cell-pad-y-3 收到 cell-pad-y-6（下限 3px 兜住紧凑密度），
   让一行的高度整体降下来，操作列按钮也随之不再突出 */
.table-pro td { padding: max(3px, calc(var(--cell-pad-y) - 6px)) 12px; color: var(--text-secondary); border-bottom: 1px solid var(--border-light); }
    .table-pro td .tag { padding-top: 1px; padding-bottom: 1px; }

/* ── 合并表头（多级列头） ── */
.table-pro thead th.th-group { text-align: center; background: var(--table-head-bg); }
.table-pro .th-group-title { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; color: var(--text-primary); }
/* ── 合并单元格（跨行 / 跨列）：合并格垂直居中 ── */
.table-pro td[rowspan], .table-pro td[colspan] { vertical-align: middle; }

/* ── 虚拟滚动占位行 ── */
.table-scroll tr.vt-spacer > td { padding: 0; border: 0; }

/* 分页 */
.pagination { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pagination-left { display: flex; align-items: center; gap: 8px; margin-right: auto; min-width: 0; }
.pagination-right { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.page-btn {
    min-width: var(--pager-h);
    height: var(--pager-h);
    padding: 0 var(--pager-pad-x);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-color);
    background: var(--bg-input);
    color: var(--text-secondary);
    font-size: 13px;
    font-weight: 500;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all var(--transition);
    font-family: var(--font-sans);
}
    .page-btn .icon { font-size: 11px; }
    .page-btn:not(:disabled):hover { background: var(--bg-hover); color: var(--text-primary); border-color: var(--text-muted); }
    .page-btn.active { background: var(--primary-color); border-color: var(--primary-color); color: #fff; box-shadow: 0 4px 10px rgba(47, 91, 255, .25); }
    .page-btn:disabled { opacity: .4; cursor: not-allowed; }
        .page-btn:disabled:hover { background: var(--bg-input); border-color: var(--border-color); color: var(--text-secondary); }
.page-ellipsis { min-width: var(--pager-h); height: var(--pager-h); display: flex; align-items: center; justify-content: center; color: var(--text-muted); font-size: 13px; letter-spacing: 1px; }
/* 翻页按钮的 Tooltip 容器：气泡挂在它身上，不继承 button 禁用态的 opacity:.4 */
.page-tip { display: flex; }
/* 末尾两枚翻页键贴着卡片右缘，而 .cp-card 是 overflow:hidden：气泡默认以按钮为中线居中，
   右半截会越出卡片被裁掉（「最后一页」四个字只露前三个）。这两枚改为按右缘对齐，箭头同步右移。
   用 :nth-last-of-type 而不是 :last-child——分页里还夹着 .pagination-info / .page-ellipsis 等 span，
   但结构上它们都排在这两枚之前，末尾两个 span 恒为「下一页 / 最后一页」。 */
.pagination-right > .page-tip:nth-last-of-type(-n+2)[data-tip]::after { left: auto; right: 0; transform: translateY(4px); }
.pagination-right > .page-tip:nth-last-of-type(-n+2)[data-tip]::before { left: auto; right: 7px; transform: translateY(4px); }
    .pagination-right > .page-tip:nth-last-of-type(-n+2)[data-tip]:hover::after,
    .pagination-right > .page-tip:nth-last-of-type(-n+2)[data-tip]:hover::before { transform: translateY(0); }
.pagination-info { font-size: 13px; color: var(--text-muted); margin-right: 4px; white-space: nowrap; }

/* ── 列表 ── */
.list { display: flex; flex-direction: column; }
.list-item { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid var(--border-light); transition: background var(--transition); font-size: 14px; gap: 12px; }
    .list-item:last-child { border-bottom: none; }
    .list-item:hover { background: var(--bg-hover); }
.list-avatar { width: 36px; height: 36px; border-radius: 10px; background: var(--primary-soft); color: var(--primary-color); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 14px; flex-shrink: 0; }
.list-content { flex: 1; min-width: 0; margin-left: 12px; }
.list-title { font-weight: 500; color: var(--text-primary); }
.list-desc { font-size: 12px; color: var(--text-muted); }
.list-extra { flex-shrink: 0; display: flex; align-items: center; gap: 8px; }

/* ── 描述列表 ── */
.descriptions { border: 1px solid var(--border-color); border-radius: var(--radius-md); overflow: hidden; }
.desc-item { display: flex; border-bottom: 1px solid var(--border-light); }
    .desc-item:last-child { border-bottom: none; }
.desc-label { width: 128px; flex-shrink: 0; padding: 12px 16px; background: var(--bg-hover); font-size: 13px; font-weight: 500; color: var(--text-secondary); border-right: 1px solid var(--border-light); display: flex; align-items: center; }
.desc-value { flex: 1; min-width: 0; padding: 12px 16px; font-size: 13.5px; color: var(--text-primary); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.descriptions.vertical .desc-item { flex-direction: column; }
    .descriptions.vertical .desc-label { width: auto; border-right: none; border-bottom: 1px solid var(--border-light); }
.descriptions.grid-2-col { display: grid; grid-template-columns: repeat(2, 1fr); }
    .descriptions.grid-2-col .desc-item:nth-last-child(2) { border-bottom: none; }

/* 多列描述列表：列数由组件写入的 --desc-cols 决定，任意 N 都成立。
   网格线用 1px 缝隙 + 容器底色画，避免为每个列数各写一套 nth-last-child 去去掉末行边框
   ——那正是原先只支持 2 列的原因：第 3、4 种情况没人补规则，参数就静默失效了。 */
.descriptions.grid-multi { display: grid; grid-template-columns: repeat(var(--desc-cols, 2), minmax(0, 1fr)); gap: 1px; background: var(--border-light); }
    .descriptions.grid-multi .desc-item { border-bottom: none; background: var(--bg-card); }
    .descriptions.grid-multi .desc-label { width: 112px; }
    /* 窄屏收拢成两列：四列时每列不足 160px，label 会把值挤成竖排 */
    @@media (max-width: 900px) {
        .descriptions.grid-multi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    }

/* ── 时间轴 ── */
.timeline { display: flex; flex-direction: column; }
.timeline-item { position: relative; display: flex; gap: 14px; }
    /* 竖向连接线：从本圆点中心连到下一圆点中心，末节点不画 */
    .timeline-item::before { content: ''; position: absolute; top: 7px; bottom: -7px; left: 6px; width: 2px; background: var(--border-color); }
    .timeline-item:last-child::before { display: none; }
.timeline-dot { position: relative; z-index: 1; width: 14px; height: 14px; border-radius: 50%; background: var(--primary-color); box-shadow: 0 0 0 4px var(--primary-soft); margin-top: 0; flex-shrink: 0; }
    .timeline-dot.success { background: var(--success-color); box-shadow: 0 0 0 4px var(--success-soft); }
    .timeline-dot.warning { background: var(--warning-color); box-shadow: 0 0 0 4px var(--warning-soft); }
    .timeline-dot.danger { background: var(--danger-color); box-shadow: 0 0 0 4px var(--danger-soft); }
    .timeline-dot.info { background: var(--info-color); box-shadow: 0 0 0 4px var(--info-soft); }
    .timeline-dot.muted { background: var(--text-muted); box-shadow: 0 0 0 4px var(--border-light); }
.timeline-content { flex: 1; min-width: 0; padding-bottom: 22px; }
    .timeline-item:last-child .timeline-content { padding-bottom: 0; }
.timeline-time { font-size: 11.5px; line-height: 14px; color: var(--text-muted); }
.timeline-title { font-size: 14px; font-weight: 600; color: var(--text-primary); margin: 2px 0 3px; }
.timeline-desc { font-size: 13px; color: var(--text-secondary); line-height: 1.6; }
/* 左对齐：轨道在右、内容在左 */
.timeline-left .timeline-item { flex-direction: row-reverse; }
    .timeline-left .timeline-item::before { left: auto; right: 6px; }
    .timeline-left .timeline-content { text-align: right; }

/* ── 进度条 ── */
.progress-group { margin-bottom: 18px; }
    .progress-group:last-child { margin-bottom: 0; }
.progress-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }
.progress-label { font-size: 13px; font-weight: 500; color: var(--text-secondary); }
.progress-value { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.progress { height: 8px; background: var(--bg-hover); border-radius: 10px; overflow: hidden; position: relative; }
    .progress.sm { height: 6px; }
    .progress.lg { height: 12px; }
.progress-bar { height: 100%; border-radius: 10px; background: var(--primary-color); transition: width .4s cubic-bezier(.4, 0, .2, 1); }
    .progress-bar.success { background: var(--success-color); }
    .progress-bar.warning { background: var(--warning-color); }
    .progress-bar.danger { background: var(--danger-color); }
    .progress-bar.striped { background-image: linear-gradient(45deg, rgba(255, 255, 255, .18) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .18) 50%, rgba(255, 255, 255, .18) 75%, transparent 75%, transparent); background-size: 12px 12px; }

/* ── 环形进度 ── */
.ring-wrap { display: flex; align-items: center; justify-content: space-around; gap: 22px; flex-wrap: wrap; }
.progress-ring { position: relative; width: 108px; height: 108px; }
    .progress-ring svg { transform: rotate(-90deg); display: block; }
    .progress-ring circle { fill: none; stroke-width: 9; stroke-linecap: round; }
    .progress-ring .ring-track { stroke: var(--bg-hover); }
    .progress-ring .ring-value { transition: stroke-dashoffset .5s cubic-bezier(.4, 0, .2, 1); }
    .progress-ring .ring-text { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; }
    .progress-ring .ring-num { font-size: 20px; font-weight: 600; color: var(--text-primary); font-variant-numeric: tabular-nums; }
    .progress-ring .ring-cap { font-size: 11px; color: var(--text-muted); }
.progress-ring.sm { width: 72px; height: 72px; }
    .progress-ring.sm .ring-num { font-size: 15px; }
    .progress-ring.lg { width: 148px; height: 148px; }
    .progress-ring.lg .ring-num { font-size: 26px; }

/* ── 空状态 / 结果 ── */
.empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 32px 20px; text-align: center; }
.empty-icon { width: 72px; height: 72px; border-radius: 50%; background: var(--bg-hover); display: flex; align-items: center; justify-content: center; font-size: 30px; color: var(--text-muted); margin-bottom: 16px; }
.empty-title { font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 6px; }
.empty-desc { font-size: 13px; color: var(--text-muted); max-width: 300px; line-height: 1.5; margin-bottom: 16px; }

.result { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 28px 20px; }
.result-icon { width: 64px; height: 64px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 18px; }
    .result-icon.success { background: var(--success-soft); color: var(--success-color); }
    .result-icon.warning { background: var(--warning-soft); color: var(--warning-color); }
    .result-icon.danger { background: var(--danger-soft); color: var(--danger-color); }
    .result-icon.info { background: var(--info-soft); color: var(--info-color); }
    .result-icon.empty { background: var(--bg-hover); color: var(--text-muted); }
.result-title { font-size: 17px; font-weight: 600; color: var(--text-primary); margin-bottom: 8px; }
.result-desc { font-size: 13.5px; color: var(--text-secondary); max-width: 380px; line-height: 1.6; margin-bottom: 20px; }
.result-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.result-extra { margin-top: 18px; padding: 14px 18px; background: var(--bg-hover); border-radius: var(--radius-md); font-size: 13px; color: var(--text-secondary); width: 100%; text-align: left; }
.result-extra-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 3px 0; }

/* ── 图片 ── */
.image-showcase { display: flex; flex-direction: column; gap: 14px; }
.image-row { display: flex; gap: 14px; flex-wrap: wrap; }
.image-card { border-radius: var(--radius-md); overflow: hidden; border: 1px solid var(--border-color); position: relative; background: var(--bg-hover); }
    .image-card img { display: block; width: 100%; height: 100%; object-fit: cover; }
    .image-card.small { width: 90px; height: 90px; }
    .image-card.medium { width: 140px; height: 100px; }
    .image-card.large { width: 100%; height: 160px; }
.image-caption { position: absolute; bottom: 0; left: 0; right: 0; padding: 8px 12px; background: linear-gradient(transparent, rgba(0, 0, 0, .72)); color: #fff; font-size: 12px; font-weight: 500; }
/* 图片点击放大预览（应用内 Lightbox，不新开浏览器标签页） */
.image-clickable { cursor: zoom-in; }
.image-lightbox {
    position: fixed; inset: 0; z-index: 3000;
    background: rgba(8, 10, 16, .82); backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center;
    padding: 48px; cursor: zoom-out; animation: image-lightbox-fade .2s ease;
}
.image-lightbox-figure { margin: 0; max-width: 100%; max-height: 100%; display: flex; flex-direction: column; align-items: center; gap: 14px; cursor: default; }
.image-lightbox-img { max-width: 100%; max-height: calc(100vh - 96px); object-fit: contain; border-radius: var(--radius-md); box-shadow: 0 24px 70px -20px rgba(0, 0, 0, .6); background: var(--bg-card); }
.image-lightbox-caption { color: rgba(255, 255, 255, .86); font-size: 13px; line-height: 1.6; text-align: center; max-width: 720px; }
.image-lightbox-close {
    position: absolute; top: 20px; right: 24px; width: 40px; height: 40px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid rgba(255, 255, 255, .28); background: rgba(255, 255, 255, .1); color: #fff;
    font-size: 18px; cursor: pointer; transition: background .15s ease, transform .15s ease;
}
    .image-lightbox-close:hover { background: rgba(255, 255, 255, .22); transform: rotate(90deg); }
@keyframes image-lightbox-fade { from { opacity: 0; } to { opacity: 1; } }
.image-fallback { width: 90px; height: 90px; border-radius: var(--radius-md); border: 1px dashed var(--border-color); background: var(--bg-hover); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var(--text-muted); font-size: 11px; }
    .image-fallback .icon { font-size: 22px; }
    .image-card .image-fallback { position: absolute; inset: 0; width: auto; height: auto; border: 0; border-radius: 0; }

/* ── 视频播放器 ── */
.video-card { border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
.video-stage { position: relative; background: #000; display: flex; align-items: center; justify-content: center; overflow: hidden; }
    .video-stage video { width: 100%; height: 100%; }
.video-el { display: block; }
.video-fallback { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: rgba(255, 255, 255, .72); font-size: 13px; background: var(--bg-hover); }
    .video-fallback .icon { font-size: 30px; }
.video-busy { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; }
.video-spinner { width: 42px; height: 42px; border-radius: 50%; border: 3px solid rgba(255, 255, 255, .28); border-top-color: #fff; animation: video-spin .8s linear infinite; }
@keyframes video-spin { to { transform: rotate(360deg); } }

/* 自定义控制条覆盖层 */
.video-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: space-between; opacity: 0; transition: opacity var(--transition); background: linear-gradient(180deg, rgba(0,0,0,.35) 0%, transparent 22%, transparent 55%, rgba(0,0,0,.62) 100%); }
    .video-stage:hover .video-overlay, .video-overlay:focus-within, .video-overlay.open { opacity: 1; }
.video-top { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; }
.video-badge { font-size: 12px; color: #fff; background: rgba(0, 0, 0, .4); padding: 3px 10px; border-radius: 999px; }
.video-bar { display: flex; align-items: center; gap: 10px; padding: 8px 14px 12px; color: #fff; flex-wrap: wrap; }
.video-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 0; border-radius: 8px; background: transparent; color: #fff; cursor: pointer; transition: background var(--transition); flex-shrink: 0; }
    .video-btn:hover { background: rgba(255, 255, 255, .16); }
    .video-btn .icon { font-size: 17px; }
.video-time { font-size: 12px; font-variant-numeric: tabular-nums; min-width: 42px; text-align: center; color: rgba(255,255,255,.9); }
.video-scrub { position: relative; flex: 1 1 160px; height: 16px; display: flex; align-items: center; min-width: 120px; }
    .video-scrub > div { position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 3px; }
    .video-track-bg { width: 100%; background: rgba(255, 255, 255, .28); }
    .video-track-buf { right: auto; background: rgba(255, 255, 255, .45); }
    .video-track-fg { right: auto; background: var(--primary-color); }
    .video-scrub:hover > div { height: 6px; margin-top: -3px; }
.video-range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; background: transparent; -webkit-appearance: none; appearance: none; cursor: pointer; }
    .video-range::-webkit-slider-runnable-track { height: 100%; background: transparent; }
    .video-range::-moz-range-track { height: 100%; background: transparent; }
    .video-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 13px; height: 13px; border-radius: 50%; background: #fff; margin-top: 1.5px; box-shadow: 0 1px 4px rgba(0, 0, 0, .5); opacity: 0; transition: opacity var(--transition); }
    .video-range::-moz-range-thumb { width: 13px; height: 13px; border: 0; border-radius: 50%; background: #fff; opacity: 0; transition: opacity var(--transition); }
    .video-scrub:hover .video-range::-webkit-slider-thumb, .video-range:focus::-webkit-slider-thumb { opacity: 1; }
    .video-scrub:hover .video-range::-moz-range-thumb, .video-range:focus::-moz-range-thumb { opacity: 1; }
.video-vol-wrap { position: relative; display: inline-flex; }
.video-vol-menu { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); width: 34px; height: 116px; background: rgba(20, 24, 32, .95); backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, .12); border-radius: 10px; opacity: 0; visibility: hidden; transition: opacity .18s ease, visibility .18s ease; z-index: 31; }
    .video-vol-wrap:hover .video-vol-menu, .video-vol-menu:focus-within { opacity: 1; visibility: visible; }
.video-vvol { position: absolute; left: 50%; top: 50%; width: 92px; height: 6px; margin: 0; transform: translate(-50%, -50%) rotate(-90deg); -webkit-appearance: none; appearance: none; background: rgba(255, 255, 255, .35); border-radius: 3px; cursor: pointer; }
    .video-vvol::-webkit-slider-runnable-track { height: 6px; border-radius: 3px; background: transparent; }
    .video-vvol::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 13px; height: 13px; border-radius: 50%; background: #fff; margin-top: -3.5px; box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }
    .video-vvol::-moz-range-track { height: 6px; border-radius: 3px; background: transparent; }
    .video-vvol::-moz-range-thumb { width: 13px; height: 13px; border: 0; border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }
/* 自定义控制条的弹层菜单（倍速 / 字幕），替代原生 select */
.video-menu-wrap { position: relative; }
.video-menu-btn { width: auto; padding: 0 8px; gap: 3px; font-size: 12px; font-weight: 600; }
    .video-menu-btn .icon { font-size: 11px; }
.video-menu-label { font-variant-numeric: tabular-nums; letter-spacing: .2px; }
.video-menu-caret { transition: transform var(--transition); }
    .video-menu-wrap.open .video-menu-caret { transform: rotate(180deg); }
.video-menu { position: absolute; bottom: calc(100% + 8px); right: 0; min-width: 92px; max-height: 220px; overflow-y: auto; background: rgba(20, 24, 32, .95); backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, .12); border-radius: 10px; padding: 5px; opacity: 0; visibility: hidden; transform: translateY(6px); transition: all .18s ease; z-index: 30; }
    .video-menu-wrap.open .video-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.video-menu-option { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 10px; border-radius: 6px; font-size: 12.5px; font-weight: 500; color: rgba(255, 255, 255, .82); cursor: pointer; transition: background var(--transition), color var(--transition); white-space: nowrap; }
    .video-menu-option:hover { background: rgba(255, 255, 255, .12); color: #fff; }
    .video-menu-option.active { color: var(--primary-color); font-weight: 600; }
.video-menu-check { font-size: 12px; opacity: 0; }
    .video-menu-option.active .video-menu-check { opacity: 1; }

/* 工具按钮（截图预览行等复用） */
.video-tool-btn { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border: 1px solid var(--border-color); border-radius: 8px; background: var(--bg-hover); color: var(--text-primary); font-size: 12.5px; cursor: pointer; text-decoration: none; transition: all var(--transition); }
    .video-tool-btn:hover { border-color: var(--primary-soft); color: var(--primary-color); }
    .video-tool-btn .icon { font-size: 14px; }

/* 截图预览 */
.video-snap { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-top: 1px solid var(--border-color); background: var(--bg-hover); }
    .video-snap img { max-height: 72px; border-radius: 6px; border: 1px solid var(--border-color); }
.video-caption { padding: 8px 14px; font-size: 12.5px; color: var(--text-muted); border-top: 1px solid var(--border-color); }

/* ── 音频播放器 ── */
.audio-card { border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
.audio-el { display: none; }
.audio-failed { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 22px; color: var(--text-muted); font-size: 13px; }
    .audio-failed .icon { font-size: 22px; }
.audio-player { position: relative; display: flex; align-items: center; gap: 16px; padding: 16px 18px; background: linear-gradient(135deg, var(--primary-soft), transparent 60%); }
.audio-cover { position: relative; width: 68px; height: 68px; border-radius: 14px; flex-shrink: 0; overflow: hidden; display: flex; align-items: center; justify-content: center; background: var(--primary-color); color: #fff; font-size: 26px; box-shadow: var(--shadow-sm); }
    .audio-cover img { width: 100%; height: 100%; object-fit: cover; }
    .audio-cover.spinning .icon { animation: audio-spin 2.4s linear infinite; }
.audio-main { flex: 1; min-width: 0; }
.audio-title { font-size: 15px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audio-artist { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audio-visualizer { position: absolute; top: 14px; right: 18px; display: flex; align-items: flex-end; gap: 3px; height: 22px; opacity: .55; pointer-events: none; }
    .audio-visualizer span { width: 3px; height: 20%; background: var(--primary-color); border-radius: 2px; }
    .audio-visualizer.playing span { animation: audio-dance .9s ease-in-out infinite; }
    .audio-visualizer span:nth-child(2) { animation-delay: .1s; }
    .audio-visualizer span:nth-child(3) { animation-delay: .2s; }
    .audio-visualizer span:nth-child(4) { animation-delay: .3s; }
    .audio-visualizer span:nth-child(5) { animation-delay: .4s; }
    .audio-visualizer span:nth-child(6) { animation-delay: .15s; }
    .audio-visualizer span:nth-child(7) { animation-delay: .25s; }
    .audio-visualizer span:nth-child(8) { animation-delay: .35s; }
@keyframes audio-dance { 0%, 100% { height: 20%; } 50% { height: 100%; } }
.audio-scrub { position: relative; height: 14px; margin-top: 12px; display: flex; align-items: center; }
    .audio-scrub > div { position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 3px; }
    .audio-track-bg { width: 100%; background: var(--border-color); }
    .audio-track-fg { right: auto; background: var(--primary-color); }
    .audio-scrub:hover > div { height: 6px; margin-top: -3px; }
.audio-range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; background: transparent; -webkit-appearance: none; appearance: none; cursor: pointer; }
    .audio-range::-webkit-slider-runnable-track { height: 100%; background: transparent; }
    .audio-range::-moz-range-track { height: 100%; background: transparent; }
    .audio-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #fff; border: 2px solid var(--primary-color); margin-top: 1px; box-shadow: 0 1px 3px rgba(0, 0, 0, .3); opacity: 0; transition: opacity var(--transition); }
    .audio-range::-moz-range-thumb { width: 12px; height: 12px; border: 2px solid var(--primary-color); border-radius: 50%; background: #fff; opacity: 0; transition: opacity var(--transition); }
    .audio-scrub:hover .audio-range::-webkit-slider-thumb, .audio-range:focus::-webkit-slider-thumb { opacity: 1; }
    .audio-scrub:hover .audio-range::-moz-range-thumb, .audio-range:focus::-moz-range-thumb { opacity: 1; }
.audio-times { display: flex; justify-content: space-between; margin-top: 6px; }
.audio-time { font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.audio-controls { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.audio-btn { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border: 0; border-radius: 50%; background: var(--bg-hover); color: var(--text-primary); cursor: pointer; transition: all var(--transition); flex-shrink: 0; }
    .audio-btn:hover { background: var(--primary-soft); color: var(--primary-color); }
    .audio-btn .icon { font-size: 15px; }
    .audio-btn.active { background: var(--primary-soft); color: var(--primary-color); }
.audio-play { width: 42px; height: 42px; background: var(--primary-color); color: #fff; box-shadow: 0 4px 12px color-mix(in srgb, var(--primary-color) 40%, transparent); }
    .audio-play:hover { background: var(--primary-hover); color: #fff; transform: scale(1.05); }
    .audio-play .icon { font-size: 17px; }
.audio-spinner { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, .4); border-top-color: #fff; animation: audio-spin .7s linear infinite; }
.audio-vol-wrap { position: relative; display: inline-flex; margin-left: auto; }
.audio-vol-menu { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); width: 34px; height: 116px; background: rgba(20, 24, 32, .95); backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, .12); border-radius: 10px; opacity: 0; visibility: hidden; transition: opacity .18s ease, visibility .18s ease; z-index: 31; }
    .audio-vol-wrap:hover .audio-vol-menu, .audio-vol-menu:focus-within { opacity: 1; visibility: visible; }
.audio-vvol { position: absolute; left: 50%; top: 50%; width: 92px; height: 6px; margin: 0; transform: translate(-50%, -50%) rotate(-90deg); -webkit-appearance: none; appearance: none; background: rgba(255, 255, 255, .35); border-radius: 3px; cursor: pointer; }
    .audio-vvol::-webkit-slider-runnable-track { height: 6px; border-radius: 3px; background: transparent; }
    .audio-vvol::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 13px; height: 13px; border-radius: 50%; background: #fff; margin-top: -3.5px; box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }
    .audio-vvol::-moz-range-track { height: 6px; border-radius: 3px; background: transparent; }
    .audio-vvol::-moz-range-thumb { width: 13px; height: 13px; border: 0; border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }
.audio-caption { padding: 8px 14px; font-size: 12.5px; color: var(--text-muted); border-top: 1px solid var(--border-color); }
@keyframes audio-spin { to { transform: rotate(360deg); } }

/* ── 语音输入 ── */
.speech-input { display: flex; flex-direction: column; gap: 6px; }
.speech-box { position: relative; display: flex; align-items: flex-start; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-input); transition: border var(--transition), box-shadow var(--transition); }
    .speech-box:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
    .speech-box.listening { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
    .speech-box.error { border-color: var(--danger-color); }
    .speech-box.error:focus-within { box-shadow: 0 0 0 3px var(--danger-soft); }
.speech-field { flex: 1; min-width: 0; border: 0; background: transparent; padding: var(--ctl-pad-y) 14px; padding-right: 52px; font-family: var(--font-sans); font-size: 14px; line-height: 1.5; color: var(--text-primary); resize: vertical; min-height: 60px; outline: none; }
    .speech-field::placeholder { color: var(--text-muted); }
    .speech-field:disabled { cursor: not-allowed; }
.speech-btn { position: absolute; right: 8px; top: 8px; display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border: 0; border-radius: 50%; background: var(--bg-hover); color: var(--text-primary); cursor: pointer; transition: all var(--transition); }
    .speech-btn:hover:not(:disabled) { background: var(--primary-soft); color: var(--primary-color); }
    .speech-btn:disabled { opacity: .45; cursor: not-allowed; }
    .speech-btn .icon { font-size: 16px; }
    .speech-box.listening .speech-btn { background: var(--primary-color); color: #fff; animation: speech-pulse 1.4s ease-in-out infinite; }
@keyframes speech-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary-color) 45%, transparent); } 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--primary-color) 0%, transparent); } }
.speech-hint { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); }
    .speech-hint .icon { font-size: 13px; }
.speech-warn { color: var(--warning-color); }
.speech-live { color: var(--primary-color); }
.speech-pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--primary-color); animation: speech-dot 1s ease-in-out infinite; }
@keyframes speech-dot { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }
.speech-interim { font-size: 13px; color: var(--text-muted); font-style: italic; padding: 2px; }

/* ── 语音播报 ── */
.tts { display: flex; flex-direction: column; gap: 10px; }
.tts-field { width: 100%; box-sizing: border-box; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-input); padding: var(--ctl-pad-y) 14px; font-family: var(--font-sans); font-size: 14px; line-height: 1.5; color: var(--text-primary); resize: vertical; min-height: 60px; outline: none; transition: border var(--transition), box-shadow var(--transition); }
    .tts-field::placeholder { color: var(--text-muted); }
    .tts-field:focus { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
    .tts-field:disabled { cursor: not-allowed; opacity: .6; }
.tts-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.tts-ctrl { display: inline-flex; align-items: center; gap: 8px; }
.tts-transport { gap: 6px; }
.tts-btn { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border: 1px solid var(--border-color); border-radius: 50%; background: var(--bg-hover); color: var(--text-primary); cursor: pointer; transition: all var(--transition); }
    .tts-btn:hover:not(:disabled) { background: var(--primary-soft); color: var(--primary-color); border-color: transparent; }
    .tts-btn:disabled { opacity: .45; cursor: not-allowed; }
    .tts-btn .icon { font-size: 16px; }
.tts-play { width: 40px; height: 40px; background: var(--primary-color); color: #fff; border-color: transparent; }
    .tts-play:hover:not(:disabled) { background: var(--primary-color); color: #fff; filter: brightness(1.08); }
    .tts-play .icon { font-size: 18px; }
.tts-ghost { background: transparent; }
.tts-voice { min-width: 0; flex: 1; }
.tts-select { flex: 1; min-width: 120px; max-width: 340px; height: 34px; padding: 0 10px; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-input); color: var(--text-primary); font-family: var(--font-sans); font-size: 13px; outline: none; cursor: pointer; transition: border var(--transition), box-shadow var(--transition); }
    .tts-select:focus { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
.tts-tuning { display: flex; flex-wrap: wrap; gap: 16px; }
.tts-slider { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; }
    .tts-slider input[type='range'] { width: 120px; accent-color: var(--primary-color); cursor: pointer; margin: 0; }
.tts-lbl { color: var(--text-muted); white-space: nowrap; }
.tts-num { min-width: 34px; text-align: right; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.tts-status { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); }
    .tts-status .icon { font-size: 13px; }
.tts-warn { color: var(--warning-color); }
.tts-live { color: var(--primary-color); }
.tts-pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--primary-color); animation: tts-dot 1s ease-in-out infinite; }
@keyframes tts-dot { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }

/* ── PDF 预览 ── */
.pdf-preview { display: flex; flex-direction: column; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
.pdf-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--border-color); background: var(--bg-hover); }
.pdf-title { display: inline-flex; align-items: center; gap: 8px; min-width: 0; font-size: 13px; font-weight: 600; color: var(--text-primary); }
    .pdf-title .icon { flex-shrink: 0; color: var(--text-muted); }
    .pdf-title > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pdf-tools { display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0; }
.pdf-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-md); background: transparent; color: var(--text-primary); text-decoration: none; cursor: pointer; transition: all var(--transition); }
    .pdf-btn:hover { background: var(--primary-soft); color: var(--primary-color); }
    .pdf-btn:disabled { opacity: .45; cursor: not-allowed; }
    .pdf-btn .icon { font-size: 16px; }
.pdf-stage { position: relative; width: 100%; background: #525659; }
.pdf-frame { display: block; width: 100%; height: 100%; border: 0; }
.pdf-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; width: 100%; color: var(--text-muted); background: var(--bg-input); }
    .pdf-empty .icon { font-size: 34px; opacity: .55; }
.pdf-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 13px; color: var(--text-muted); background: rgba(255, 255, 255, .06); }
.pdf-spinner { width: 16px; height: 16px; border: 2px solid var(--border-color); border-top-color: var(--primary-color); border-radius: 50%; animation: pdf-spin .8s linear infinite; }
@keyframes pdf-spin { to { transform: rotate(360deg); } }
.pdf-preview:fullscreen, .pdf-preview:-webkit-full-screen { border: 0; border-radius: 0; height: 100vh; background: var(--bg-card); }
    .pdf-preview:fullscreen .pdf-stage, .pdf-preview:-webkit-full-screen .pdf-stage { height: auto !important; flex: 1 1 auto; min-height: 0; }

/* ── 条码 ── */
.barcode { display: inline-flex; }
.barcode-figure { display: inline-flex; flex-direction: column; align-items: center; gap: 6px; }
    .barcode-figure .barcode-svg { display: block; max-width: 100%; }
    .barcode-figure .barcode-text { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; letter-spacing: .5px; line-height: 1.2; color: var(--text-primary); word-break: break-all; text-align: center; }
.barcode-invalid { display: inline-flex; align-items: center; gap: 8px; padding: 14px 18px; border: 1px dashed var(--border-color); border-radius: var(--radius-md); background: var(--bg-input); color: var(--text-muted); font-size: 13px; }
    .barcode-invalid .icon { font-size: 18px; color: var(--warning-color); flex-shrink: 0; }

/* ── 二维码 ── */
.qrcode { display: inline-flex; }
    .qrcode .qrcode-svg { display: block; max-width: 100%; height: auto; }
.qrcode-invalid { display: inline-flex; align-items: center; gap: 8px; padding: 14px 18px; border: 1px dashed var(--border-color); border-radius: var(--radius-md); background: var(--bg-input); color: var(--text-muted); font-size: 13px; }
    .qrcode-invalid .icon { font-size: 18px; color: var(--warning-color); flex-shrink: 0; }

/* ── 扫码（相机 / PDA 按键 / 图片） ── */
.codescanner { display: flex; flex-direction: column; gap: 12px; padding: 14px; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-card, var(--bg-input)); max-width: 460px; }
    .codescanner .cs-bar { display: flex; }
    .codescanner .cs-hint { display: inline-flex; align-items: center; gap: 8px; color: var(--text-muted); font-size: 13px; padding: 8px 2px; }
    .codescanner .cs-hint .icon { color: var(--primary-color); }
    .codescanner .cs-tip { margin: 0; font-size: 12px; color: var(--text-muted); line-height: 1.6; }

    /* 相机取景 */
    .codescanner .cs-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: #0b0f14; aspect-ratio: 4 / 3; }
        .codescanner .cs-video { width: 100%; height: 100%; object-fit: cover; display: block; }
    .codescanner .cs-reticle { position: absolute; inset: 16%; pointer-events: none; opacity: .55; transition: opacity .2s; }
    .codescanner .cs-stage.scanning .cs-reticle { opacity: 1; }
    .codescanner .cs-reticle i { position: absolute; width: 22px; height: 22px; border: 3px solid var(--primary-color); border-radius: 3px; }
        .codescanner .cs-reticle i:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; }
        .codescanner .cs-reticle i:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; }
        .codescanner .cs-reticle i:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; }
        .codescanner .cs-reticle i:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; }
    /* 扫描线：仅在相机取景(.scanning)时上下往复扫动 */
    .codescanner .cs-scanline { position: absolute; left: 16%; right: 16%; top: 16%; height: 2px; pointer-events: none; border-radius: 2px; background: linear-gradient(90deg, transparent, var(--primary-color) 20%, var(--primary-color) 80%, transparent); box-shadow: 0 0 10px 1px var(--primary-color); animation: cs-scan-sweep 2.2s ease-in-out infinite; }
    @keyframes cs-scan-sweep {
        0% { top: 16%; opacity: .2; }
        10% { opacity: 1; }
        50% { top: 80%; opacity: 1; }
        90% { opacity: 1; }
        100% { top: 16%; opacity: .2; }
    }
    .codescanner .cs-cover { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: #9aa4b0; font-size: 13px; background: rgba(11,15,20,.72); }
        .codescanner .cs-cover .icon { font-size: 30px; }

    /* PDA 按键 */
    .codescanner .cs-pda { display: flex; align-items: center; gap: 12px; padding: 14px; border: 1px dashed var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); color: var(--text-muted); }
        .codescanner .cs-pda .icon { font-size: 24px; flex-shrink: 0; }
        .codescanner .cs-pda.on { border-style: solid; border-color: var(--primary-color); color: var(--text-color); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
            .codescanner .cs-pda.on .icon { color: var(--primary-color); }
    .codescanner .cs-pda-txt { display: flex; flex-direction: column; gap: 3px; }
        .codescanner .cs-pda-txt strong { font-size: 13px; }
        .codescanner .cs-pda-txt small { font-size: 12px; line-height: 1.5; opacity: .8; }

    /* 警告 / 动作 */
    .codescanner .cs-warn { display: flex; align-items: flex-start; gap: 8px; padding: 9px 12px; border-radius: var(--radius-sm); background: color-mix(in srgb, var(--warning-color) 12%, transparent); color: var(--warning-color); font-size: 12px; line-height: 1.5; }
        .codescanner .cs-warn.error { background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); color: var(--danger-color, #d4380d); }
        .codescanner .cs-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .codescanner .cs-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
    .codescanner .cs-spacer { flex: 1 1 auto; }

    /* 结果区 */
    .codescanner .cs-out { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 40px; padding: 8px 12px; border-radius: var(--radius-sm); border: 1px solid var(--border-color); background: var(--bg-input); }
        .codescanner .cs-out.has { border-color: var(--primary-color); }
    .codescanner .cs-out-empty { color: var(--text-muted); font-size: 12px; }
    .codescanner .cs-out-main { display: flex; align-items: center; gap: 10px; min-width: 0; }
    .codescanner .cs-fmt { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 600; color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 12%, transparent); }
        .codescanner .cs-fmt .icon { font-size: 13px; }
    .codescanner .cs-text { font-family: var(--font-mono, ui-monospace, monospace); font-size: 13px; word-break: break-all; }
    .codescanner .cs-out-ops { display: inline-flex; gap: 4px; flex-shrink: 0; }
    .codescanner .cs-op { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-muted); cursor: pointer; }
        .codescanner .cs-op:hover { background: var(--bg-hover, rgba(0,0,0,.06)); color: var(--text-color); }

    /* 历史 */
    .codescanner .cs-hist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
        .codescanner .cs-hist li { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: var(--radius-sm); color: var(--text-muted); }
        .codescanner .cs-hist li:hover { background: var(--bg-hover, rgba(0,0,0,.04)); }
    .codescanner .cs-h-fmt { flex-shrink: 0; min-width: 46px; font-weight: 600; }
    .codescanner .cs-h-txt { font-family: var(--font-mono, ui-monospace, monospace); word-break: break-all; }

/* ── 视频监控直播 ── */
.liveplayer { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
    /* 状态栏 */
    .liveplayer .lp-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .liveplayer .lp-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .liveplayer .lp-title .icon { color: var(--primary-color); font-size: 16px; }
    .liveplayer .lp-spacer { flex: 1 1 auto; }
    .liveplayer .lp-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .liveplayer .lp-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .liveplayer .lp-badge.live { color: #fff; background: var(--danger-color, #d4380d); }
        .liveplayer .lp-badge.live .lp-dot { animation: lp-pulse 1.1s ease-in-out infinite; }
    .liveplayer .lp-badge.buffering { color: var(--warning-color); background: color-mix(in srgb, var(--warning-color) 14%, transparent); }
    .liveplayer .lp-badge.error { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .liveplayer .lp-badge.idle { color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 12%, transparent); }
    .liveplayer .lp-badge.off { color: var(--text-muted); background: var(--bg-input); }
    .liveplayer .lp-chip { display: inline-flex; align-items: center; padding: 1px 8px; border-radius: var(--radius-sm); font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; color: var(--text-muted); background: var(--bg-input); border: 1px solid var(--border-color); }
@keyframes lp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

    /* 画面 */
    .liveplayer .lp-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: #06080b; display: flex; align-items: center; justify-content: center; }
        .liveplayer .lp-video { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
    .liveplayer .lp-cover { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; color: #8b97a6; font-size: 13px; background: radial-gradient(120% 120% at 50% 0%, rgba(20,28,38,.35), rgba(6,8,11,.85)); }
        .liveplayer .lp-cover .icon { font-size: 38px; }
    .liveplayer .lp-busy { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
    .liveplayer .lp-spinner { width: 34px; height: 34px; border-radius: 50%; border: 3px solid rgba(255,255,255,.25); border-top-color: var(--primary-color); animation: lp-spin .8s linear infinite; }
@keyframes lp-spin { to { transform: rotate(360deg); } }
    .liveplayer .lp-error { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: #fff; font-size: 12px; background: color-mix(in srgb, var(--danger-color, #d4380d) 88%, #000); }
        .liveplayer .lp-error .icon { flex-shrink: 0; }
        .liveplayer .lp-error small { margin-left: auto; opacity: .7; font-family: var(--font-mono, ui-monospace, monospace); }

    /* 地址 / 协议栏 */
    .liveplayer .lp-url { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

    /* 控制条 */
    .liveplayer .lp-bar { display: flex; align-items: center; gap: 6px; padding: 4px 2px; flex-wrap: wrap; }
    .liveplayer .lp-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition); flex-shrink: 0; }
        .liveplayer .lp-btn:hover:not(:disabled) { background: var(--bg-hover, rgba(0,0,0,.06)); color: var(--primary-color); }
        .liveplayer .lp-btn:disabled { opacity: .4; cursor: not-allowed; }
    .liveplayer .lp-vol { width: 90px; height: 4px; accent-color: var(--primary-color); cursor: pointer; }
        .liveplayer .lp-vol:disabled { opacity: .4; cursor: not-allowed; }

    /* 抓拍记录 */
    .liveplayer .lp-snaps { border-top: 1px solid var(--border-light, var(--border-color)); padding-top: 8px; }
    .liveplayer .lp-snaps-head { display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: var(--text-muted); margin-bottom: 6px; }
    .liveplayer .lp-op { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-muted); cursor: pointer; }
        .liveplayer .lp-op:hover { background: var(--bg-hover, rgba(0,0,0,.06)); color: var(--text-color); }
    .liveplayer .lp-snaps-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
    .liveplayer .lp-snap { display: block; border: 1px solid var(--border-color); border-radius: var(--radius-sm); overflow: hidden; line-height: 0; }
        .liveplayer .lp-snap img { width: 100%; height: 60px; object-fit: cover; display: block; transition: transform var(--transition); }
        .liveplayer .lp-snap:hover { border-color: var(--primary-color); }
        .liveplayer .lp-snap:hover img { transform: scale(1.04); }

/* ── 宫格磁贴模式（LivePlayer Dense，供 LiveWall 内嵌）── */
    .liveplayer.lp-dense { padding: 0; border: 0; border-radius: var(--radius-sm); background: transparent; gap: 0; overflow: hidden; }
    .liveplayer.lp-dense .lp-stage { border-radius: 0; }
    .liveplayer.lp-dense .lp-video { cursor: zoom-in; }
    .liveplayer.lp-dense .lp-cover .icon { font-size: 26px; }
    .liveplayer.lp-dense .lp-cover span { display: none; }
    /* 左上角标：状态点 + 名称 + 协议，压在画面上，细描边保证深/浅画面都可读 */
    .liveplayer .lp-tile-head { position: absolute; left: 6px; top: 6px; display: inline-flex; align-items: center; gap: 5px; max-width: calc(100% - 12px); padding: 2px 8px 2px 6px; border-radius: 999px; font-size: 11.5px; font-weight: 600; color: #fff; background: rgba(6,8,11,.5); backdrop-filter: blur(3px); pointer-events: none; z-index: 2; }
        .liveplayer .lp-tile-head .lp-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
        .liveplayer .lp-tile-head .lp-tile-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .liveplayer .lp-tile-head .lp-tile-proto { font-family: var(--font-mono, ui-monospace, monospace); font-size: 10px; opacity: .8; }
        .liveplayer .lp-tile-head .lp-dot.live { color: #ff4d4f; animation: lp-pulse 1.1s ease-in-out infinite; }
        .liveplayer .lp-tile-head .lp-dot.buffering { color: var(--warning-color, #faad14); }
        .liveplayer .lp-tile-head .lp-dot.error { color: #ff4d4f; }
        .liveplayer .lp-tile-head .lp-dot.idle { color: var(--primary-color, #1677ff); }
        .liveplayer .lp-tile-head .lp-dot.off { color: #9aa6b2; }
    /* 悬停浮现控件：默认隐藏，鼠标进入整格才现 */
    .liveplayer .lp-hov { position: absolute; right: 6px; top: 6px; display: flex; align-items: center; gap: 2px; padding: 2px; border-radius: var(--radius-sm); background: rgba(6,8,11,.55); backdrop-filter: blur(3px); opacity: 0; transform: translateY(-4px); transition: opacity var(--transition), transform var(--transition); z-index: 3; }
        .liveplayer .lp-hov .lp-btn { width: 26px; height: 26px; color: #fff; }
            .liveplayer .lp-hov .lp-btn:hover:not(:disabled) { background: rgba(255,255,255,.18); color: #fff; }
    .liveplayer.lp-dense:hover .lp-hov, .liveplayer.lp-dense:focus-within .lp-hov { opacity: 1; transform: none; }
    /* 断流 / 报错：整格描红边高亮，便于宫格中一眼定位问题路 */
    .liveplayer.lp-dense.has-error .lp-stage { box-shadow: inset 0 0 0 2px var(--danger-color, #d4380d); }
    .liveplayer.lp-dense .lp-error { position: absolute; inset: auto 0 0 0; padding: 5px 8px; font-size: 11px; }
    .liveplayer.lp-dense .lp-error small { display: none; }
    /* 单路全屏：整个容器铺满屏幕，画面自适应居中，控制簇 / 角标仍可用 */
    .liveplayer.lp-dense:fullscreen { width: 100vw; height: 100vh; background: #000; display: flex; }
    .liveplayer.lp-dense:fullscreen .lp-stage { flex: 1 1 auto; width: 100%; height: 100%; aspect-ratio: auto; }

/* ── 三维模型查看器（ModelViewer）── */
.modelviewer { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
    /* 舞台底色画在容器上：canvas 开 alpha 通道，明暗主题各给一档渐变，模型于是像摆在卡片里而不是贴黑纸 */
    .modelviewer .m3d-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: radial-gradient(125% 95% at 50% 6%, #f6f8fd 0%, #e6ecf7 58%, #d7dfef 100%); }
        [data-theme='dark'] .modelviewer .m3d-stage { background: radial-gradient(125% 95% at 50% 6%, #1b2536 0%, #131b29 58%, #0c1220 100%); }
    /* 画布容器由 JS 追加 canvas，自身保持空节点，避免 Blazor DOM 补丁与外部子节点抢位 */
    .modelviewer .m3d-view { position: absolute; inset: 0; }
        .modelviewer .m3d-view canvas { display: block; outline: none; touch-action: none; cursor: grab; }
            .modelviewer .m3d-view canvas:active { cursor: grabbing; }
    .modelviewer .m3d-busy { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 12px; color: var(--text-secondary); background: color-mix(in srgb, var(--bg-card) 55%, transparent); }
    .modelviewer .m3d-meta { position: absolute; left: 10px; top: 8px; display: inline-flex; align-items: center; gap: 8px; padding: 3px 9px; border-radius: var(--radius-sm); font-size: 11.5px; color: var(--text-secondary); background: color-mix(in srgb, var(--bg-card) 72%, transparent); }
        .modelviewer .m3d-name { font-weight: 600; color: var(--text-primary); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .modelviewer .m3d-tri { font-variant-numeric: tabular-nums; opacity: .8; }
    .modelviewer .m3d-hint { position: absolute; right: 10px; bottom: 8px; font-size: 11px; color: var(--text-muted); opacity: .78; pointer-events: none; }
    .modelviewer .m3d-error { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: #fff; font-size: 12px; background: color-mix(in srgb, var(--danger-color, #d4380d) 88%, #000); }
        .modelviewer .m3d-error .icon { flex-shrink: 0; }
    .modelviewer .m3d-bar { display: flex; align-items: center; gap: 6px; padding: 2px 0; flex-wrap: wrap; }
    .modelviewer .m3d-spacer { flex: 1 1 auto; }
    .modelviewer .m3d-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition), color var(--transition); flex-shrink: 0; }
        .modelviewer .m3d-btn:hover:not(:disabled) { background: var(--bg-hover, rgba(47,91,255,.08)); color: var(--primary-color); }
        .modelviewer .m3d-btn:disabled { opacity: .45; cursor: not-allowed; }
        /* 常开项（自动旋转 / 网格 / 坐标轴 / 线框）点亮时描上主色，状态一眼可辨 */
        .modelviewer .m3d-btn.on { background: color-mix(in srgb, var(--primary-color) 14%, transparent); color: var(--primary-color); }
    .modelviewer.has-error .m3d-stage { box-shadow: inset 0 0 0 2px var(--danger-color, #d4380d); }

/* ── 思维导图（MindMap）── */
.mindmap { display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
    .mindmap .mm-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: var(--bg-surface, #f8f9fa); }
        [data-theme='dark'] .mindmap .mm-stage { background: var(--bg-surface-dark, #1a1a1a); }
    .mindmap .mm-view { position: absolute; inset: 0; }
        .mindmap .mm-svg { display: block; width: 100%; height: 100%; cursor: grab; }
            .mindmap .mm-svg:active { cursor: grabbing; }
    .mindmap .mm-busy { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 12px; color: var(--text-secondary); background: color-mix(in srgb, var(--bg-card) 55%, transparent); }
    .mindmap .mm-error { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: #fff; font-size: 12px; background: color-mix(in srgb, var(--danger-color, #d4380d) 88%, #000); }
        .mindmap .mm-error .icon { flex-shrink: 0; }
    .mindmap .mm-bar { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-top: 1px solid var(--border-color); background: var(--bg-card); }
    .mindmap .mm-spacer { flex: 1 1 auto; }
    .mindmap .mm-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition), color var(--transition); flex-shrink: 0; }
        .mindmap .mm-btn:hover:not(:disabled) { background: var(--bg-hover, rgba(47,91,255,.08)); color: var(--primary-color); }
        .mindmap .mm-btn:disabled { opacity: .45; cursor: not-allowed; }
    .mindmap.has-error .mm-stage { box-shadow: inset 0 0 0 2px var(--danger-color, #d4380d); }
    .mindmap .mm-node { cursor: pointer; transition: opacity var(--transition); }
        .mindmap .mm-node:hover { opacity: .85; }
        .mindmap .mm-node.selected rect { stroke-width: 2; }
    .mindmap .mm-edge { pointer-events: none; }
    /* 折叠开关：有子节点的节点右缘一颗小圆点，+/− 随折叠状态切换 */
    .mindmap .mm-toggle { cursor: pointer; }
    .mindmap .mm-toggle circle { fill: var(--bg-card); stroke: var(--border-color); stroke-width: 1; transition: stroke var(--transition); }
    .mindmap .mm-toggle text { fill: var(--text-secondary); font-size: 12px; user-select: none; pointer-events: none; transition: fill var(--transition); }
    .mindmap .mm-toggle:hover circle { stroke: var(--primary-color); }
    .mindmap .mm-toggle:hover text { fill: var(--primary-color); }
    /* 双击改名：输入框用 foreignObject 盖在节点上，天然跟着 viewBox / 缩放走 */
    .mindmap .mm-edit { overflow: visible; }
    .mindmap .mm-edit-input { width: 100%; height: 100%; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--primary-color); border-radius: 6px; background: var(--bg-card); color: var(--text-primary); font-family: inherit; font-size: 13px; text-align: center; outline: none; }
    .mindmap .mm-node text { user-select: none; }
    /* 右键菜单：垫层铺满视口收「点别处」，菜单本体 fixed 跟在鼠标处 */
    .mindmap .mm-ctx-backdrop { position: fixed; inset: 0; z-index: 1000; }
    .mindmap .mm-ctx {
        position: fixed;
        z-index: 1001;
        min-width: 148px;
        padding: 6px;
        background: var(--bg-card);
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
        box-shadow: var(--shadow-lg);
    }
        .mindmap .mm-ctx .context-item.disabled { opacity: .45; pointer-events: none; }
    /* 工具条按钮的 Tooltip 沿用基础规则的「向上弹」：气泡挂在 .mm-btn 上，是 .mm-bar 的子节点、
       .mm-stage 的兄弟，不受舞台 overflow 影响；反倒向下弹会跑出 .mindmap（overflow:hidden）被裁光。
       横向用 data-tip-align 贴边（见组件标记：前两个 start、后两个 end）。 */

/* ── 地图（Map）── */
.map { display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
    .map .map-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: var(--bg-surface, #f8f9fa); }
        [data-theme='dark'] .map .map-stage { background: var(--bg-surface-dark, #1a1a1a); }
    .map .map-view { position: absolute; inset: 0; z-index: 1; }
        .map .map-view .leaflet-container { width: 100%; height: 100%; }
    .map .map-busy { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 12px; color: var(--text-secondary); background: color-mix(in srgb, var(--bg-card) 55%, transparent); }
    .map .map-error { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: #fff; font-size: 12px; background: color-mix(in srgb, var(--danger-color, #d4380d) 88%, #000); }
        .map .map-error .icon { flex-shrink: 0; }
    .map .map-bar { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-top: 1px solid var(--border-color); background: var(--bg-card); }
    .map .map-spacer { flex: 1 1 auto; }
    .map .map-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition), color var(--transition); flex-shrink: 0; }
        .map .map-btn:hover:not(:disabled) { background: var(--bg-hover, rgba(47,91,255,.08)); color: var(--primary-color); }
        .map .map-btn:disabled { opacity: .45; cursor: not-allowed; }
        .map .map-btn.on { background: color-mix(in srgb, var(--primary-color) 14%, transparent); color: var(--primary-color); }
    .map.has-error .map-stage { box-shadow: inset 0 0 0 2px var(--danger-color, #d4380d); }
    /* 工具条 Tooltip 沿用基础规则的「向上弹」并用 data-tip-align 贴边：气泡虽挂在 .map-btn 上，
       但 .map-btn 不是定位祖先，绝对定位会一路找到 overflow:hidden 的 .map，向下弹会越过底边被整条裁掉。 */

/* ── 走马灯（Carousel）── */
.carousel { position: relative; border-radius: var(--radius-lg); overflow: hidden; background: var(--bg-card); }
    .carousel-stage { position: relative; width: 100%; overflow: hidden; }
    .carousel-track { position: relative; width: 100%; height: 100%; }
        .carousel-item { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity .5s ease; cursor: pointer; }
            .carousel-item.active { opacity: 1; z-index: 1; }
            .carousel-item.slide-left { transform: translateX(-100%); transition: transform .5s ease; }
            .carousel-item.slide-right { transform: translateX(100%); transition: transform .5s ease; }
            .carousel-item.active.slide-left, .carousel-item.active.slide-right { transform: translateX(0); }
        .slide-mode .carousel-item { opacity: 1; }
        .carousel-caption { text-align: center; color: #fff; padding: 20px; }
            .carousel-caption h3 { margin: 0 0 8px; font-size: 20px; font-weight: 600; }
            .carousel-caption p { margin: 0; font-size: 14px; opacity: .85; }
    .carousel-arrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border: 0; border-radius: 50%; background: rgba(0,0,0,.35); color: #fff; cursor: pointer; transition: background var(--transition); opacity: 0; }
        .carousel:hover .carousel-arrow { opacity: 1; }
        .carousel-arrow:hover { background: rgba(0,0,0,.55); }
        .carousel-prev { left: 12px; }
        .carousel-next { right: 12px; }
    .carousel-indicators { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 2; display: flex; gap: 6px; }
        .carousel-dot { width: 8px; height: 8px; border: 0; border-radius: 50%; background: rgba(255,255,255,.5); cursor: pointer; transition: background var(--transition), width var(--transition); padding: 0; }
            .carousel-dot.active { width: 24px; border-radius: 4px; background: #fff; }

/* ── 手写签名（Signature）── */
.sig { display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
    .sig-stage { position: relative; width: 100%; background: repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(0,0,0,.02) 10px, rgba(0,0,0,.02) 20px), var(--bg-surface, #f8f9fa); border-bottom: 1px dashed var(--border-color); }
        [data-theme='dark'] .sig-stage { background: repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(255,255,255,.03) 10px, rgba(255,255,255,.03) 20px), var(--bg-surface-dark, #1a1a1a); }
    .sig-stage .sig-canvas { display: block; width: 100%; height: 100%; cursor: crosshair; }
    .sig-placeholder { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var(--text-secondary); font-size: 13px; pointer-events: none; opacity: .55; }
        .sig-placeholder .icon { font-size: 22px; opacity: .7; }
    .sig-bar { display: flex; align-items: center; gap: 6px; padding: 6px 8px; background: var(--bg-card); }
    .sig-spacer { flex: 1 1 auto; }
    .sig-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition), color var(--transition); flex-shrink: 0; }
        .sig-btn:hover:not(:disabled) { background: var(--bg-hover, rgba(47,91,255,.08)); color: var(--primary-color); }
        .sig-btn:disabled { opacity: .45; cursor: not-allowed; }

/* ── 甘特图（Gantt）──
   frappe-gantt 有三处必须绕开的坑：
   ① 宿主根节点和它生成的画布 svg 共用 .gantt 类名，而 frappe 里有一条 `.gantt{position:absolute}`，
      会连带把宿主根节点拽出文档流——宽度退化成 max-content（实测撑到 3600+ px）、卡片高度塌成 0、
      整张图浮在后续内容之上（把下面「换一份数据」那行的 Select 压进了画布）。用 :not(svg) 提权钉回普通流。
   ② 它的配色走 --g-* 变量、声明在 :root，而它的 CSS 是懒加载后注入的，同权重下会压过我们写在 :root 的值。
      所以在 .gantt 作用域内重声明：元素自身声明优先于从根继承，与注入顺序无关。
   ③ 其余覆盖一律写成 `.gantt .gantt …`（宿主内的那张画布）或带 .gantt-view 的更长链，
      权重才压得住后注入的 frappe 规则。 */
/* 宿主盒子的排版属性必须排除画布 svg：svg 也带 .gantt 类，会被套上 1px 边框 + 圆角 + overflow:hidden，
   结果甘特图外圈多一道灰框、两端被圆角裁掉一截。 */
.gantt:not(svg) {
    position: relative;
    display: flex; flex-direction: column; gap: 8px;
    border: 1px solid var(--border-color); border-radius: var(--radius-lg);
    background: var(--bg-card); overflow: hidden;
}

.gantt {
    /* 画布配色：轨道浅蓝、进度中蓝——深色任务名压在两者上都还清楚（实心主色条上深色字会糊） */
    --g-bar-color: color-mix(in srgb, var(--primary-color) 10%, var(--bg-card));
    --g-bar-border: transparent;
    --g-progress-color: color-mix(in srgb, var(--primary-color) 46%, var(--bg-card));
    --g-expected-progress: color-mix(in srgb, var(--primary-color) 20%, transparent);
    --g-arrow-color: color-mix(in srgb, var(--primary-color) 42%, transparent);
    --g-handle-color: #fff;
    --g-text-dark: var(--text-primary);
    --g-text-light: #fff;
    --g-text-muted: var(--text-muted);
    --g-row-color: var(--bg-card);
    --g-row-border-color: var(--border-color);
    --g-border-color: color-mix(in srgb, var(--border-color) 62%, transparent);
    --g-tick-color: color-mix(in srgb, var(--border-color) 40%, transparent);
    --g-tick-color-thick: var(--border-color);
    --g-header-background: var(--bg-card);
    --g-actions-background: var(--bg-hover);
    --g-today-highlight: var(--primary-color);
    --g-weekend-label-color: var(--bg-hover);
    --g-weekend-highlight-color: color-mix(in srgb, var(--primary-color) 5%, var(--bg-card));
    --g-popup-actions: var(--bg-hover);
}
    .gantt .gantt-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: var(--bg-card); }
    .gantt .gantt-view { width: 100%; height: 100%; overflow: auto; }
        /* frappe 自带 svg 画布：宽度由内容（时间轴跨度）决定，高度铺满容器，
           不能限死 width:100%，否则横向滚动失效、甘特条会挤成一团。 */
        .gantt .gantt-view .gantt-container { height: 100%; }
        .gantt .gantt-view .gantt-container .gantt { background: transparent; }

        /* 表头：月份深色加粗、日期次要色，今天那颗日期胶囊用主色 */
        .gantt .gantt-view .gantt-container .grid-header { border-bottom: 1px solid var(--border-color); }
        .gantt .gantt-view .gantt-container .upper-text { color: var(--text-primary); font-size: 13px; font-weight: 600; }
        .gantt .gantt-view .gantt-container .lower-text { color: var(--text-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
        /* 今天那格：frappe 给它加 .current-date-highlight（主色底 + 白字），但我们上面那条 .lower-text 的
           文字色权重更高，会把白字压成灰字——主色底上几乎看不清。这里补一条更高权重的白字。 */
        .gantt .gantt-view .gantt-container .lower-text.current-date-highlight { color: #fff; font-weight: 600; border-radius: var(--radius-sm); }
        /* 周末/节假日底色：按日、按周视图里是有用的「非工作日」提示，切到按月 / 按季 / 按年后一列一列
           的浅色条纹只会变成噪声，直接隐掉（视图模式由 web.js 写在容器的 data-view 上）。 */
        .gantt .gantt-view .gantt-container[data-view='Month'] .holiday-highlight,
        .gantt .gantt-view .gantt-container[data-view='Quarter'] .holiday-highlight,
        .gantt .gantt-view .gantt-container[data-view='Year'] .holiday-highlight { display: none; }
        /* 今天竖线：降到半透明并加宽一点，别像原来那样一条实心黑杠劈开整张图 */
        .gantt .gantt-view .gantt-container .current-highlight { width: 2px; opacity: .45; border-radius: 2px; }
        .gantt .gantt-view .gantt-container .current-ball-highlight { box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 18%, transparent); }
        .gantt .gantt-view .gantt-container .popup-wrapper { border: 1px solid var(--border-color); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); }
        /* frappe 自带的「Today」浮标是英文、灰底，还压在月份标题上；底部工具条已有「回到今天」，隐掉它 */
        .gantt .gantt-view .gantt-container .side-header { display: none; }

        /* 甘特条：frappe 只给了个 1px outline（灰线套白条），这里换成浅蓝描边 */
        .gantt .gantt .bar-wrapper .bar { outline: none; stroke: color-mix(in srgb, var(--primary-color) 26%, transparent); stroke-width: 1; transition: stroke var(--transition); }
        .gantt .gantt .bar-wrapper:hover .bar { stroke: var(--primary-color); }
        .gantt .gantt .bar-label { font-family: var(--font-sans); font-size: 12px; font-weight: 600; }
        .gantt .gantt .bar-label.big { font-weight: 500; fill: var(--text-secondary); }
        /* custom_class 落在 .bar-wrapper 上：里程碑（单日节点）换个色相，跟普通任务分开 */
        .gantt .gantt .bar-wrapper.bar-milestone .bar { fill: color-mix(in srgb, var(--warning-color) 16%, var(--bg-card)); stroke: color-mix(in srgb, var(--warning-color) 48%, transparent); }
        .gantt .gantt .bar-wrapper.bar-milestone .bar-progress { fill: color-mix(in srgb, var(--warning-color) 72%, var(--bg-card)); }
    .gantt .gantt-busy { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 12px; color: var(--text-secondary); background: color-mix(in srgb, var(--bg-card) 55%, transparent); }
    .gantt .gantt-error { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: #fff; font-size: 12px; background: color-mix(in srgb, var(--danger-color, #d4380d) 88%, #000); }
        .gantt .gantt-error .icon { flex-shrink: 0; }
    .gantt .gantt-bar { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-top: 1px solid var(--border-color); background: var(--bg-card); }
    .gantt .gantt-spacer { flex: 1 1 auto; }
    .gantt .gantt-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition), color var(--transition); flex-shrink: 0; }
        .gantt .gantt-btn:hover:not(:disabled) { background: var(--bg-hover, rgba(47,91,255,.08)); color: var(--primary-color); }
        .gantt .gantt-btn:disabled { opacity: .45; cursor: not-allowed; }
        .gantt .gantt-btn.on { background: color-mix(in srgb, var(--primary-color) 14%, transparent); color: var(--primary-color); }
    .gantt.has-error .gantt-stage { box-shadow: inset 0 0 0 2px var(--danger-color, #d4380d); }
    /* 工具条 Tooltip 沿用基础规则的「向上弹」并用 data-tip-align 贴边：理由同 .map */

/* ── 监控宫格墙（LiveWall）── */
.livewall { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
    .livewall .lw-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .livewall .lw-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .livewall .lw-title .icon { color: var(--primary-color); font-size: 16px; }
    .livewall .lw-spacer { flex: 1 1 auto; }
    .livewall .lw-chip { display: inline-flex; align-items: center; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 11.5px; color: var(--text-muted); background: var(--bg-input); border: 1px solid var(--border-color); }
    .livewall .lw-pager { display: inline-flex; align-items: center; gap: 2px; }
    .livewall .lw-page { min-width: 44px; text-align: center; font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .livewall .lw-grid { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .livewall .lw-cell { position: relative; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: #06080b; overflow: hidden; }
        /* 删除角标：悬停格才现，置于右上（避开左上角标） */
        .livewall .lw-x { position: absolute; right: 6px; bottom: 6px; z-index: 4; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: 0; border-radius: var(--radius-sm); background: rgba(6,8,11,.6); color: #fff; cursor: pointer; opacity: 0; transition: opacity var(--transition), background var(--transition); backdrop-filter: blur(3px); }
            .livewall .lw-cell:hover .lw-x { opacity: 1; }
            .livewall .lw-x:hover { background: var(--danger-color, #d4380d); }
            .livewall .lw-x .icon { font-size: 13px; }
        /* 空白占位格：虚线框 + 居中「空位」，保持宫格均匀 */
        .livewall .lw-cell.blank { display: flex; align-items: center; justify-content: center; aspect-ratio: 16 / 9; background: var(--bg-input); border-style: dashed; }
            .livewall .lw-blank { display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--text-muted); font-size: 12px; opacity: .6; }
                .livewall .lw-blank .icon { font-size: 24px; }
    .livewall .lw-add { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-top: 4px; border-top: 1px solid var(--border-light, var(--border-color)); }

/* ── iframe 内嵌（Embed 组件：工具条 + 桥接 postMessage + 全屏）── */
.embed { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
    .embed .em-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .embed .em-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .embed .em-title .icon { color: var(--primary-color); font-size: 16px; }
    .embed .em-spacer { flex: 1 1 auto; }
    .embed .em-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .embed .em-badge .em-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .embed .em-badge.ok { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .embed .em-badge.loading { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .embed .em-badge.loading .em-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .embed .em-badge.error { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .embed .em-chip { display: inline-flex; align-items: center; padding: 1px 8px; border-radius: var(--radius-sm); font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; color: var(--text-muted); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
        .embed .em-chip:hover { border-color: var(--primary-color); color: var(--primary-color); }
    .embed .em-tools { display: inline-flex; align-items: center; gap: 2px; }
    .embed .em-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background var(--transition), color var(--transition); text-decoration: none; flex-shrink: 0; }
        .embed .em-btn:hover { background: var(--bg-hover, rgba(0,0,0,.06)); color: var(--primary-color); }
        .embed .em-btn.em-btn-hi { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .embed .em-stage { position: relative; overflow: hidden; border-radius: var(--radius-sm); background: #0b0f14; border: 1px solid var(--border-color); }
        .embed .em-frame { width: 100%; height: 100%; border: 0; display: block; background: #fff; }
    .embed .em-busy { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; background: radial-gradient(120% 120% at 50% 0%, rgba(20,28,38,.35), rgba(6,8,11,.55)); }
        .embed .em-spinner { width: 34px; height: 34px; border-radius: 50%; border: 3px solid rgba(255,255,255,.25); border-top-color: var(--primary-color); animation: em-spin .8s linear infinite; }
    .embed .em-error { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: #fff; font-size: 12px; background: color-mix(in srgb, var(--danger-color, #d4380d) 88%, #000); }
        .embed .em-error .icon { flex-shrink: 0; }
    .embed .em-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; color: var(--text-muted); font-size: 13px; background: var(--bg-input); border: 1px dashed var(--border-color); border-radius: var(--radius-sm); }
        .embed .em-empty .icon { font-size: 34px; opacity: .55; }
    /* 整卡进入原生全屏时：容器铺满视口，画面自动撑高，工具条仍在（StageStyle 在 C# 里同步切 flex 让内联不遮蔽） */
    .embed:fullscreen { padding: 0; border-radius: 0; border: 0; background: #000; }
        .embed:fullscreen .em-stage { flex: 1 1 auto; border-radius: 0; border: 0; }
@keyframes em-spin { to { transform: rotate(360deg); } }
@keyframes em-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ── Studio 设计器宿主（Known.Runtime.StudioHost） ── */
/* 圆角边框整体挂这里；容器不 overflow:hidden（否则切掉工具栏向上浮出的 data-tip），iframe 方角改由 .sh-body 单独裁圆。 */
.studio-host { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; min-width: 0; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
    .studio-host .sh-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex-shrink: 0; padding: 12px 18px; background: var(--bg-card); border-bottom: 1px solid var(--border-color); border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-lg); }
        .studio-host .sh-icon { display: inline-flex; align-items: center; color: var(--primary-color); }
        .studio-host .sh-heading { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
        .studio-host .sh-title { font-weight: 600; font-size: 15px; color: var(--text-primary); }
        .studio-host .sh-desc { font-size: 12px; color: var(--text-muted, #8a94a6); }
        .studio-host .sh-nav { display: inline-flex; align-items: center; gap: 8px; margin-left: 6px; }
    /* 子设计器分段切换器（页面 | 表单 | 流程）：胶囊底 + 当前档白底浮起高亮，随 ActiveDesigner 变 */
    .studio-host .sh-modes { display: inline-flex; align-items: center; gap: 2px; margin-left: 10px; padding: 3px; background: var(--bg-hover, rgba(120, 130, 150, .14)); border-radius: 999px; }
        .studio-host .sh-mode { display: inline-flex; align-items: center; gap: 5px; padding: 4px 12px; border: 0; background: transparent; color: var(--text-muted, #8a94a6); font-size: 13px; font-weight: 500; line-height: 1; border-radius: 999px; cursor: pointer; white-space: nowrap; transition: background .15s, color .15s, box-shadow .15s; }
            .studio-host .sh-mode:hover { color: var(--text-primary); }
            .studio-host .sh-mode.active { background: var(--bg-card); color: var(--primary-color); font-weight: 600; box-shadow: 0 1px 3px rgba(0, 0, 0, .12); }
        .studio-host .sh-spacer { flex: 1 1 auto; }
        .studio-host .sh-flag { color: var(--warning-color, #b7791f); font-size: 12px; }
        .studio-host .sh-divider { width: 1px; height: 18px; background: var(--border-color); margin: 0 2px; }
    /* 只裁 iframe 底部两角（白色画面），容器不再 overflow:hidden，工具栏向上浮出的 data-tip 提示不被切 */
    .studio-host .sh-body { min-width: 0; flex: 1 1 auto; min-height: 0; overflow: hidden; border-bottom-left-radius: var(--radius-lg); border-bottom-right-radius: var(--radius-lg); }
    /* 覆写 Embed：去掉其自带的内边距与圆角边框，让设计器 iframe 满铺到宿主圆角内缘 */
    .studio-host .embed { padding: 0; gap: 0; border: 0; border-radius: 0; background: transparent; }
        .studio-host .embed .em-stage { border: 0; border-radius: 0; background: transparent; }
    /* 全屏：整块宿主铺满视口（含工具栏），iframe 主体 flex 撑高 */
    .studio-host.is-fullscreen { position: fixed; inset: 0; z-index: 2000; border: 0; border-radius: 0; }
        .studio-host.is-fullscreen .sh-bar { border-top-left-radius: 0; border-top-right-radius: 0; }
        .studio-host.is-fullscreen .sh-body { flex: 1 1 auto; min-height: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
        .studio-host.is-fullscreen .embed { height: 100%; }
            .studio-host.is-fullscreen .embed .em-stage { flex: 1 1 auto; height: auto !important; }

/* ── 蓝牙（Web Bluetooth GATT 面板） ── */
.ble { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
    .ble .bl-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .ble .bl-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .ble .bl-title .icon { color: var(--primary-color); font-size: 16px; }
    .ble .bl-spacer { flex: 1 1 auto; }
    .ble .bl-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .ble .bl-badge .bl-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .ble .bl-badge.idle { color: var(--text-muted); background: var(--bg-input); }
    .ble .bl-badge.busy { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .ble .bl-badge.busy .bl-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .ble .bl-badge.on { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .ble .bl-badge.off { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .ble .bl-dev { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .ble .bl-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .ble .bl-warn { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.6; color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger-color, #d4380d) 24%, transparent); }
        .ble .bl-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .ble .bl-hint { font-size: 12px; color: var(--text-muted); line-height: 1.6; }

    .ble .bl-tree { display: flex; flex-direction: column; gap: 6px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 6px; max-height: 320px; overflow: auto; }
    .ble .bl-empty { padding: 16px; text-align: center; color: var(--text-muted); font-size: 12.5px; }
    .ble .bl-svc { border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); overflow: hidden; }
    .ble .bl-svc-h { display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer; user-select: none; }
        .ble .bl-svc-h:hover { background: var(--bg-hover, rgba(0,0,0,.04)); }
        .ble .bl-svc-h .icon { color: var(--text-muted); font-size: 12px; transition: transform var(--transition); }
        .ble .bl-svc.open .bl-svc-h .icon.bl-caret { transform: rotate(90deg); }
    .ble .bl-uuid { font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; color: var(--text-secondary); }
    .ble .bl-svc-name { font-size: 12.5px; font-weight: 600; color: var(--text-color); }
    .ble .bl-chars { display: flex; flex-direction: column; border-top: 1px solid var(--border-color); }
    .ble .bl-char { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 7px 10px; }
        .ble .bl-char + .bl-char { border-top: 1px dashed var(--border-color); }
    .ble .bl-props { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; }
    .ble .bl-prop { padding: 0 6px; border-radius: var(--radius-sm); font-size: 10.5px; font-weight: 600; color: var(--text-muted); background: var(--bg-input); border: 1px solid var(--border-color); }
    .ble .bl-acts { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; }
    .ble .bl-btn { display: inline-flex; align-items: center; justify-content: center; gap: 4px; height: 26px; padding: 0 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-secondary); font-size: 12px; cursor: pointer; transition: all var(--transition); }
        .ble .bl-btn:hover { border-color: var(--primary-color); color: var(--primary-color); }
        .ble .bl-btn:disabled { opacity: .45; cursor: not-allowed; }
        .ble .bl-btn.on { color: var(--success-color, #2e7d32); border-color: color-mix(in srgb, var(--success-color, #2e7d32) 40%, transparent); background: color-mix(in srgb, var(--success-color, #2e7d32) 10%, transparent); }
    .ble .bl-write { display: flex; align-items: center; gap: 6px; flex-basis: 100%; margin-top: 4px; }
        .ble .bl-write input { flex: 1 1 auto; min-width: 120px; height: 28px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }

    .ble .bl-log { display: flex; flex-direction: column; gap: 2px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; max-height: 200px; overflow: auto; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.7; }
    .ble .bl-log .bl-lt { color: var(--primary-color); }
    .ble .bl-log .bl-lk { color: var(--text-muted); }
    .ble .bl-log .bl-err { color: var(--danger-color, #d4380d); }
    .ble .bl-log .bl-ok { color: var(--success-color, #2e7d32); }

/* ── 串口（Web Serial 面板） ── */
.serial { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
    .serial .sr-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .serial .sr-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .serial .sr-title .icon { color: var(--primary-color); font-size: 16px; }
    .serial .sr-spacer { flex: 1 1 auto; }
    .serial .sr-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .serial .sr-badge .sr-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .serial .sr-badge.idle { color: var(--text-muted); background: var(--bg-input); }
    .serial .sr-badge.busy { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .serial .sr-badge.busy .sr-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .serial .sr-badge.on { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .serial .sr-badge.off { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .serial .sr-info { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .serial .sr-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .serial .sr-bar .sr-lbl { font-size: 12px; color: var(--text-secondary); }
    .serial .sr-warn { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.6; color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger-color, #d4380d) 24%, transparent); }
        .serial .sr-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .serial .sr-hint { font-size: 12px; color: var(--text-muted); line-height: 1.6; }

    .serial .sr-tx { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
        .serial .sr-tx input[type="text"] { flex: 1 1 auto; min-width: 160px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }
    .serial .sr-mode { display: inline-flex; border: 1px solid var(--border-color); border-radius: var(--radius-sm); overflow: hidden; }
        .serial .sr-mode button { border: 0; background: var(--bg-card); color: var(--text-secondary); font-size: 12px; padding: 4px 12px; cursor: pointer; }
        .serial .sr-mode button + button { border-left: 1px solid var(--border-color); }
        .serial .sr-mode button.on { background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color); font-weight: 600; }
    .serial .sr-nl { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-secondary); cursor: pointer; user-select: none; }

    .serial .sr-log { display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; max-height: 260px; overflow: auto; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.7; }
        .serial .sr-log .sr-lt { color: var(--text-muted); margin-right: 6px; }
        .serial .sr-log .sr-dir { font-weight: 700; margin-right: 6px; }
    .serial .sr-row.rx .sr-dir { color: var(--success-color, #2e7d32); }
    .serial .sr-row.tx .sr-dir { color: var(--primary-color); }
    .serial .sr-row.sys .sr-text { color: var(--text-muted); }
    .serial .sr-row.err .sr-text { color: var(--danger-color, #d4380d); }
    .serial .sr-row .sr-text { color: var(--text-color); }
    .serial .sr-empty { color: var(--text-muted); }

/* ── WebUSB 通信面板（Usb） ── */
.usb { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius); background: var(--bg-card); }
    .usb .us-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .usb .us-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .usb .us-title .icon { color: var(--primary-color); font-size: 16px; }
    .usb .us-spacer { flex: 1 1 auto; }
    .usb .us-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .usb .us-badge .us-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .usb .us-badge.idle { color: var(--text-muted); background: var(--bg-input); }
    .usb .us-badge.busy { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .usb .us-badge.busy .us-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .usb .us-badge.on { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .usb .us-badge.off { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .usb .us-dev { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .usb .us-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .usb .us-bar .us-lbl { font-size: 12px; color: var(--text-secondary); }
    .usb .us-warn { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.6; color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger-color, #d4380d) 24%, transparent); }
        .usb .us-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .usb .us-hint { font-size: 12px; color: var(--text-muted); line-height: 1.6; }
    .usb .us-num { width: 74px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }

    .usb .us-tree { display: flex; flex-direction: column; gap: 6px; }
    .usb .us-empty { font-size: 12.5px; color: var(--text-muted); padding: 8px 10px; border: 1px dashed var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
    .usb .us-iface { border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); overflow: hidden; }
    .usb .us-iface-h { display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer; font-size: 12px; }
        .usb .us-iface-h:hover { background: var(--bg-hover, rgba(0, 0, 0, .03)); }
    .usb .us-caret { transition: transform .15s ease; }
    .usb .us-iface.open .us-caret { transform: rotate(90deg); }
    .usb .us-ifname { font-weight: 600; color: var(--text-color); }
    .usb .us-cls { color: var(--text-muted); font-family: var(--font-mono, ui-monospace, monospace); }
    .usb .us-eps { display: flex; flex-direction: column; gap: 4px; padding: 6px 10px 10px; border-top: 1px solid var(--border-color); }
    .usb .us-ep { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 5px 8px; border-radius: var(--radius-sm); background: var(--bg-card); border: 1px solid var(--border-color); font-size: 12px; }
    .usb .us-epn { font-family: var(--font-mono, ui-monospace, monospace); font-weight: 600; color: var(--text-color); }
    .usb .us-tag { font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--bg-input); color: var(--text-secondary); border: 1px solid var(--border-color); }
    .usb .us-tag.in { color: var(--success-color, #2e7d32); border-color: color-mix(in srgb, var(--success-color, #2e7d32) 34%, transparent); }
    .usb .us-tag.out { color: var(--primary-color); border-color: color-mix(in srgb, var(--primary-color) 34%, transparent); }
    .usb .us-last { color: var(--text-muted); font-family: var(--font-mono, ui-monospace, monospace); }
    .usb .us-acts { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
    .usb .us-btn { border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-secondary); border-radius: var(--radius-sm); font-size: 11.5px; padding: 3px 10px; cursor: pointer; }
        .usb .us-btn:hover { border-color: var(--primary-color); color: var(--primary-color); }
        .usb .us-btn[disabled] { opacity: .5; cursor: not-allowed; }
    .usb .us-write { display: inline-flex; align-items: center; gap: 6px; }
        .usb .us-write input { width: 150px; height: 26px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; }

    .usb .us-ctl { border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); overflow: hidden; }
    .usb .us-ctl-h { display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer; font-size: 12px; font-weight: 600; color: var(--text-color); }
    .usb .us-ctl-b { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px 10px; border-top: 1px solid var(--border-color); }
    .usb .us-field { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-secondary); }
    .usb .us-mode { display: inline-flex; border: 1px solid var(--border-color); border-radius: var(--radius-sm); overflow: hidden; }
        .usb .us-mode button { border: 0; background: var(--bg-card); color: var(--text-secondary); font-size: 12px; padding: 4px 12px; cursor: pointer; }
        .usb .us-mode button + button { border-left: 1px solid var(--border-color); }
        .usb .us-mode button.on { background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color); font-weight: 600; }

    .usb .us-log { display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; max-height: 240px; overflow: auto; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.7; }
        .usb .us-log .us-lt { color: var(--text-muted); margin-right: 6px; }
        .usb .us-log .us-dir { font-weight: 700; margin-right: 6px; }
    .usb .us-row.in .us-dir { color: var(--success-color, #2e7d32); }
    .usb .us-row.out .us-dir { color: var(--primary-color); }
    .usb .us-row.ctl .us-dir { color: var(--warning-color, #faad14); }
    .usb .us-row.sys .us-text { color: var(--text-muted); }
    .usb .us-row.err .us-text { color: var(--danger-color, #d4380d); }
    .usb .us-row .us-text { color: var(--text-color); }
    .usb .us-log-empty { color: var(--text-muted); }

/* ── WebNFC 读写面板（Nfc） ── */
.nfc { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius); background: var(--bg-card); }
    .nfc .nf-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .nfc .nf-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .nfc .nf-title .icon { color: var(--primary-color); font-size: 16px; }
    .nfc .nf-spacer { flex: 1 1 auto; }
    .nfc .nf-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .nfc .nf-badge .nf-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .nfc .nf-badge.idle { color: var(--text-muted); background: var(--bg-input); }
    .nfc .nf-badge.busy { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .nfc .nf-badge.busy .nf-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .nfc .nf-badge.on { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .nfc .nf-badge.off { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .nfc .nf-dev { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono, ui-monospace, monospace); }
    .nfc .nf-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .nfc .nf-warn { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.6; color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger-color, #d4380d) 24%, transparent); }
        .nfc .nf-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .nfc .nf-hint { font-size: 12px; color: var(--text-muted); line-height: 1.6; }

    .nfc .nf-reads { display: flex; flex-direction: column; gap: 8px; }
    .nfc .nf-empty { font-size: 12.5px; color: var(--text-muted); padding: 10px 12px; border: 1px dashed var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); text-align: center; }
    .nfc .nf-tag { border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); overflow: hidden; }
    .nfc .nf-tag-h { display: flex; align-items: center; gap: 8px; padding: 7px 10px; font-size: 12px; border-bottom: 1px solid var(--border-color); }
    .nfc .nf-recs { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; }
    .nfc .nf-rec { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; padding: 6px 8px; border-radius: var(--radius-sm); background: var(--bg-card); border: 1px solid var(--border-color); font-size: 12px; }
    .nfc .nf-rt { font-weight: 600; color: var(--primary-color); font-family: var(--font-mono, ui-monospace, monospace); }
    .nfc .nf-meta { color: var(--text-muted); font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; }
    .nfc .nf-val { flex: 1 1 100%; color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); word-break: break-all; }
    .nfc .nf-hex { flex: 1 1 100%; color: var(--text-muted); font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; word-break: break-all; }

    .nfc .nf-write { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
        .nfc .nf-write .nf-in { flex: 1 1 auto; min-width: 160px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }
    .nfc .nf-mode { display: inline-flex; border: 1px solid var(--border-color); border-radius: var(--radius-sm); overflow: hidden; }
        .nfc .nf-mode button { border: 0; background: var(--bg-card); color: var(--text-secondary); font-size: 12px; padding: 4px 12px; cursor: pointer; }
        .nfc .nf-mode button + button { border-left: 1px solid var(--border-color); }
        .nfc .nf-mode button.on { background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color); font-weight: 600; }

    .nfc .nf-log { display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; max-height: 220px; overflow: auto; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.7; }
        .nfc .nf-log .nf-lt { color: var(--text-muted); margin-right: 6px; }
        .nfc .nf-log .nf-dir { font-weight: 700; margin-right: 6px; }
    .nfc .nf-row.rx .nf-dir { color: var(--success-color, #2e7d32); }
    .nfc .nf-row.tx .nf-dir { color: var(--primary-color); }
    .nfc .nf-row.sys .nf-text { color: var(--text-muted); }
    .nfc .nf-row.err .nf-text { color: var(--danger-color, #d4380d); }
    .nfc .nf-row .nf-text { color: var(--text-color); }
    .nfc .nf-log-empty { color: var(--text-muted); }

/* ── WebHID 通信面板（Hid） ── */
.hid { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius); background: var(--bg-card); }
    .hid .hd-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .hid .hd-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .hid .hd-title .icon { color: var(--primary-color); font-size: 16px; }
    .hid .hd-spacer { flex: 1 1 auto; }
    .hid .hd-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .hid .hd-badge .hd-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .hid .hd-badge.idle { color: var(--text-muted); background: var(--bg-input); }
    .hid .hd-badge.busy { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .hid .hd-badge.busy .hd-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .hid .hd-badge.on { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .hid .hd-badge.off { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .hid .hd-dev { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .hid .hd-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .hid .hd-bar .hd-lbl { font-size: 12px; color: var(--text-secondary); }
    .hid .hd-warn { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.6; color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger-color, #d4380d) 24%, transparent); }
        .hid .hd-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .hid .hd-hint { font-size: 12px; color: var(--text-muted); line-height: 1.6; }
    .hid .hd-num { width: 74px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }

    .hid .hd-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--text-secondary); }
    .hid .hd-tag { font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--bg-input); color: var(--text-secondary); border: 1px solid var(--border-color); font-family: var(--font-mono, ui-monospace, monospace); }
    .hid .hd-tag.in { color: var(--success-color, #2e7d32); border-color: color-mix(in srgb, var(--success-color, #2e7d32) 34%, transparent); }
    .hid .hd-tag.out { color: var(--primary-color); border-color: color-mix(in srgb, var(--primary-color) 34%, transparent); }
    .hid .hd-tag.fe { color: var(--warning-color, #faad14); border-color: color-mix(in srgb, var(--warning-color, #faad14) 34%, transparent); }

    .hid .hd-cols { display: flex; flex-direction: column; gap: 4px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; }
    .hid .hd-cols-h { font-size: 12px; font-weight: 600; color: var(--text-color); }
    .hid .hd-col { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--text-secondary); }
    .hid .hd-col .hd-page { font-family: var(--font-mono, ui-monospace, monospace); color: var(--text-color); }
    .hid .hd-col .hd-usage { font-family: var(--font-mono, ui-monospace, monospace); color: var(--text-muted); }

    .hid .hd-in { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
    .hid .hd-in-h { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-secondary); }
    .hid .hd-inval { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; color: var(--text-color); word-break: break-all; }
    .hid .hd-empty { font-size: 12px; color: var(--text-muted); }

    .hid .hd-form { display: flex; flex-direction: column; gap: 8px; }
    .hid .hd-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
        .hid .hd-row .hd-hex { flex: 1 1 auto; min-width: 160px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }
    .hid .hd-lt { color: var(--text-muted); margin-right: 6px; }
    .hid .hd-dir { font-weight: 700; margin-right: 6px; }
    .hid .hd-rid { font-family: var(--font-mono, ui-monospace, monospace); color: var(--text-muted); }

    .hid .hd-log { display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; max-height: 240px; overflow: auto; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.7; }
        .hid .hd-log .hd-lt { color: var(--text-muted); margin-right: 6px; }
        .hid .hd-log .hd-dir { font-weight: 700; margin-right: 6px; }
    .hid .hd-row-log.in .hd-dir { color: var(--success-color, #2e7d32); }
    .hid .hd-row-log.out .hd-dir { color: var(--primary-color); }
    .hid .hd-row-log.fe .hd-dir { color: var(--warning-color, #faad14); }
    .hid .hd-row-log.sys .hd-text { color: var(--text-muted); }
    .hid .hd-row-log.err .hd-text { color: var(--danger-color, #d4380d); }
    .hid .hd-row-log .hd-text { color: var(--text-color); }
    .hid .hd-log-empty { color: var(--text-muted); }

/* ── Web MIDI 通信面板（Midi） ── */
.midi { display: flex; flex-direction: column; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--radius); background: var(--bg-card); }
    .midi .md-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .midi .md-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text-color); }
        .midi .md-title .icon { color: var(--primary-color); font-size: 16px; }
    .midi .md-spacer { flex: 1 1 auto; }
    .midi .md-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
        .midi .md-badge .md-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
    .midi .md-badge.idle { color: var(--text-muted); background: var(--bg-input); }
    .midi .md-badge.busy { color: var(--warning-color, #faad14); background: color-mix(in srgb, var(--warning-color, #faad14) 14%, transparent); }
        .midi .md-badge.busy .md-dot { animation: em-pulse 1.1s ease-in-out infinite; }
    .midi .md-badge.on { color: var(--success-color, #2e7d32); background: color-mix(in srgb, var(--success-color, #2e7d32) 12%, transparent); }
    .midi .md-badge.off { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
    .midi .md-dev { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); background: var(--bg-input); border: 1px solid var(--border-color); max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .midi .md-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .midi .md-bar .md-lbl { font-size: 12px; color: var(--text-secondary); }
    .midi .md-warn { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.6; color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger-color, #d4380d) 24%, transparent); }
        .midi .md-warn .icon { flex-shrink: 0; margin-top: 1px; }
    .midi .md-hint { font-size: 12px; color: var(--text-muted); line-height: 1.6; }
    .midi .md-num { width: 74px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }
    .midi .md-sel { height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-size: 12px; max-width: 320px; }

    .midi .md-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--text-secondary); }
    .midi .md-tag { font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--bg-input); color: var(--text-secondary); border: 1px solid var(--border-color); font-family: var(--font-mono, ui-monospace, monospace); }
    .midi .md-tag.in { color: var(--success-color, #2e7d32); border-color: color-mix(in srgb, var(--success-color, #2e7d32) 34%, transparent); }
    .midi .md-tag.out { color: var(--primary-color); border-color: color-mix(in srgb, var(--primary-color) 34%, transparent); }

    .midi .md-ports { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 8px; }
    .midi .md-port-col { display: flex; flex-direction: column; gap: 4px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; }
    .midi .md-port-h { font-size: 12px; font-weight: 600; color: var(--text-color); display: flex; align-items: center; gap: 6px; }
        .midi .md-port-h .icon { font-size: 13px; color: var(--primary-color); }
    .midi .md-port { display: flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--text-secondary); border: 1px solid transparent; cursor: default; overflow: hidden; }
        .midi .md-port .md-pname { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .midi .md-port .md-pstate { font-size: 10px; color: var(--text-muted); }
    .midi .md-port.off { opacity: .55; }
    .midi .md-port.pick { cursor: pointer; }
        .midi .md-port.pick:hover { background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
    .midi .md-port.sel { color: var(--primary-color); border-color: color-mix(in srgb, var(--primary-color) 40%, transparent); background: color-mix(in srgb, var(--primary-color) 10%, transparent); }

    .midi .md-msg { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
    .midi .md-msg-h { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-secondary); }
    .midi .md-msgval { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; color: var(--text-color); word-break: break-all; }
    .midi .md-empty { font-size: 12px; color: var(--text-muted); }

    .midi .md-send { display: flex; flex-direction: column; gap: 8px; }
    .midi .md-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); }
        .midi .md-row .md-lbl { font-size: 12px; color: var(--text-secondary); }
        .midi .md-row .md-hex { flex: 1 1 auto; min-width: 160px; height: 30px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-color); font-family: var(--font-mono, ui-monospace, monospace); font-size: 12px; }
    .midi .md-lt { color: var(--text-muted); margin-right: 6px; }
    .midi .md-dir { font-weight: 700; margin-right: 6px; }

    .midi .md-log { display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input); padding: 8px 10px; max-height: 240px; overflow: auto; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.7; }
    .midi .md-row-log.in .md-dir { color: var(--success-color, #2e7d32); }
    .midi .md-row-log.out .md-dir { color: var(--primary-color); }
    .midi .md-row-log.sys .md-text { color: var(--text-muted); }
    .midi .md-row-log.err .md-text { color: var(--danger-color, #d4380d); }
    .midi .md-row-log .md-text { color: var(--text-color); }
    .midi .md-log-empty { color: var(--text-muted); }

/* ── 对话气泡 Bubble ── */
.bubble { display: flex; align-items: flex-start; gap: 10px; max-width: 100%; }
.bubble-end { flex-direction: row-reverse; }
.bubble-end .bb-body { align-items: flex-end; }
.bubble-end .bb-meta { flex-direction: row-reverse; }
.bubble > .bb-avatar { flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; overflow: hidden; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 600; color: #fff; background: var(--primary-soft); }
.bubble > .bb-avatar img { width: 100%; height: 100%; object-fit: cover; }
.bubble > .bb-avatar .icon { color: var(--primary-color); }
.bubble-end .bb-avatar { background: color-mix(in srgb, var(--primary-color) 88%, #000); color: #fff; }
.bubble-end .bb-avatar .icon { color: #fff; }
.bb-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; max-width: min(560px, 82%); position: relative; }
.bb-meta { display: flex; align-items: baseline; gap: 8px; font-size: 12px; color: var(--text-muted); }
.bb-name { font-weight: 600; color: var(--text-secondary); }
.bb-time { letter-spacing: .2px; }
.bb-content { position: relative; padding: 10px 14px; border-radius: var(--radius-lg); font-size: 14px; line-height: 1.6; color: var(--text-primary); background: var(--bg-hover); border: 1px solid transparent; word-break: break-word; }
.bubble-start .bb-content { border-top-left-radius: 6px; }
.bubble-end .bb-content { border-top-right-radius: 6px; }
.bb-text { white-space: pre-wrap; }
.bb-content .bb-text + * , .bb-content > :not(.bb-text):first-child { margin-top: 0; }
/* 变体：soft 默认浅底；filled 主色实心；outlined 描边；ghost 无底 */
.bubble-soft .bb-content { background: var(--bg-hover); }
.bubble-filled .bb-content { background: var(--primary-color); color: #fff; border-color: transparent; }
.bubble-filled.bubble-start .bb-content { background: var(--primary-soft); color: var(--text-primary); }
.bubble-outlined .bb-content { background: transparent; border-color: var(--border-light); }
.bubble-ghost .bb-content { background: transparent; padding-left: 0; padding-right: 0; }
.bb-header { font-size: 12.5px; color: var(--text-secondary); margin-bottom: 6px; padding: 6px 10px; border-radius: var(--radius-sm); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
.bb-footer { font-size: 12.5px; color: var(--text-muted); margin-top: 6px; }
/* 悬停操作行：贴在气泡下方的 14px 行距里，靠左右对齐贴住正文那一边。
   默认 visibility:hidden 既不吃事件也不占位——它要是走正常流，hover 一下就会把下面的消息顶下去。 */
.bb-tools { position: absolute; top: 100%; margin-top: 3px; display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--text-muted); white-space: nowrap; opacity: 0; visibility: hidden; transition: opacity .12s ease; }
.bubble-start .bb-tools { left: 0; }
.bubble-end .bb-tools { right: 0; }
.bubble:hover .bb-tools, .bubble:focus-within .bb-tools { opacity: 1; visibility: visible; }
/* 操作按钮只留图形：这一行已经贴在正文下缘，再描一圈边框就成了第二个气泡，点一下只换个颜色就够。 */
.bb-tool { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 7px; background: transparent; color: var(--text-muted); cursor: pointer; }
.bb-tool:hover { color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
.bb-model { display: inline-flex; align-items: center; gap: 4px; max-width: 190px; overflow: hidden; text-overflow: ellipsis; }
/* 输入中三点 */
.bubble-typing .bb-content { padding: 12px 16px; }
.bb-dots { display: inline-flex; align-items: center; gap: 5px; }
.bb-dots > i { width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); animation: bb-dot 1.2s ease-in-out infinite; }
.bb-dots > i:nth-child(2) { animation-delay: .2s; }
.bb-dots > i:nth-child(3) { animation-delay: .4s; }
@keyframes bb-dot { 0%, 60%, 100% { opacity: .35; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
/* 加载骨架 */
.bb-skeletons { display: flex; flex-direction: column; gap: 7px; width: 220px; max-width: 100%; }
.bb-skeletons > i { height: 11px; border-radius: 5px; background: color-mix(in srgb, var(--text-muted) 16%, transparent); position: relative; overflow: hidden; }
.bb-skeletons > i:nth-child(2) { width: 82%; }
.bb-skeletons > i:nth-child(3) { width: 56%; }
.bb-skeletons > i::after { content: ''; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-muted) 12%, transparent), transparent); animation: shimmer 1.4s infinite; }
.bubble-filled .bb-skeletons > i { background: rgba(255,255,255,.28); }
.bubble-filled .bb-skeletons > i::after { background: linear-gradient(90deg, transparent, rgba(255,255,255,.4), transparent); }
.bubble-filled .bb-dots > i { background: rgba(255,255,255,.85); }

/* ── 会话管理 Conversations ── */
.conversations { display: flex; flex-direction: column; gap: 2px; padding: 8px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
.cv-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 2px 6px 8px; }
.cv-head-title { font-size: 13px; font-weight: 600; color: var(--text-secondary); }
.cv-list { display: flex; flex-direction: column; gap: 2px; }
.cv-group { font-size: 11.5px; font-weight: 600; letter-spacing: .3px; color: var(--text-muted); padding: 10px 8px 4px; }
.cv-item { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: var(--radius-md); cursor: pointer; transition: background var(--transition); min-width: 0; }
    .cv-item:hover { background: var(--bg-hover); }
    .cv-item.active { background: var(--primary-soft); }
        .cv-item.active .cv-title { color: var(--primary-color); font-weight: 600; }
        .cv-item.active .cv-lead { color: var(--primary-color); }
    .cv-item.disabled { opacity: .5; cursor: not-allowed; }
    .cv-item.editing { background: var(--bg-hover); }
.cv-lead { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; flex-shrink: 0; color: var(--text-muted); }
    .cv-lead.cv-lead-empty { opacity: .7; }
.cv-title { flex: 1; min-width: 0; font-size: 13.5px; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-time { flex-shrink: 0; font-size: 11.5px; color: var(--text-muted); }
.cv-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
.cv-menu { flex-shrink: 0; }
.cv-more { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: var(--radius-sm); color: var(--text-muted); opacity: 0; transition: all var(--transition); }
    .cv-item:hover .cv-more, .cv-item.active .cv-more { opacity: 1; }
    .cv-more:hover { background: var(--bg-hover); color: var(--text-primary); }
.conversations .dropdown-menu { min-width: 132px; }
.cv-rename { flex: 1; min-width: 0; font-size: 13.5px; padding: 3px 8px; border: 1px solid var(--primary-color); border-radius: var(--radius-sm); background: var(--bg-input); color: var(--text-primary); outline: none; }
.cv-empty { padding: 28px 12px; text-align: center; font-size: 13px; color: var(--text-muted); }
/* 单条流式加载 */
.cv-title-loading { display: inline-block; height: 12px; width: 60%; border-radius: 5px; background: color-mix(in srgb, var(--text-muted) 16%, transparent); position: relative; overflow: hidden; }
    .cv-title-loading i { position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-muted) 12%, transparent), transparent); animation: shimmer 1.4s infinite; }
/* 整表骨架行 */
.cv-skeleton { cursor: default; }
    .cv-skeleton:hover { background: transparent; }
    .cv-skeleton .cv-lead i, .cv-skeleton .cv-title i { display: block; height: 12px; border-radius: 5px; background: color-mix(in srgb, var(--text-muted) 14%, transparent); position: relative; overflow: hidden; }
    .cv-skeleton .cv-lead i { width: 20px; }
    .cv-skeleton .cv-title { flex: 1; }
    .cv-skeleton .cv-title i { width: 70%; }
    .cv-skeleton .cv-title i::after, .cv-skeleton .cv-lead i::after { content: ''; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-muted) 10%, transparent), transparent); animation: shimmer 1.4s infinite; }

/* ── 欢迎 Welcome ── */
.welcome { display: flex; flex-direction: column; gap: 18px; padding: 8px; }
.wc-hero { display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center; }
.welcome-start .wc-hero { flex-direction: row; align-items: flex-start; text-align: left; gap: 14px; }
.wc-avatar { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: 16px; background: var(--primary-soft); color: var(--primary-color); flex-shrink: 0; box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary-color) 12%, transparent); }
    .welcome-start .wc-avatar { width: 44px; height: 44px; border-radius: 13px; }
    .wc-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
.wc-intro { min-width: 0; }
.wc-title { font-size: 19px; font-weight: 700; color: var(--text-primary); line-height: 1.35; }
    .welcome-start .wc-title { font-size: 16px; }
.wc-desc { margin-top: 6px; font-size: 13px; color: var(--text-muted); line-height: 1.6; }
.wc-prompts { display: flex; flex-direction: column; gap: 10px; }
.wc-prompts-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 2px; }
.wc-prompts-title { font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
.wc-grid { display: grid; gap: 10px; }
.wc-card { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-card); cursor: pointer; transition: all var(--transition); min-width: 0; text-align: left; }
    .wc-card:hover { border-color: var(--primary-color); background: var(--bg-hover); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
    .wc-card.disabled { opacity: .5; cursor: not-allowed; }
        .wc-card.disabled:hover { border-color: var(--border-color); background: var(--bg-card); transform: none; box-shadow: none; }
.wc-card-lead { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 8px; background: var(--primary-soft); color: var(--primary-color); flex-shrink: 0; }
.wc-card-body { flex: 1; min-width: 0; }
.wc-card-text { font-size: 13.5px; color: var(--text-primary); line-height: 1.45; }
.wc-card-desc { margin-top: 2px; font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wc-card-arrow { color: var(--text-muted); opacity: 0; transform: translateX(-4px); transition: all var(--transition); flex-shrink: 0; }
    .wc-card:hover .wc-card-arrow { opacity: 1; transform: translateX(0); color: var(--primary-color); }
.wc-extra { display: flex; flex-direction: column; gap: 10px; }
.wc-skeleton { cursor: default; }
    .wc-skeleton:hover { border-color: var(--border-color); background: var(--bg-card); transform: none; box-shadow: none; }
    .wc-skeleton i { display: block; height: 13px; width: 70%; border-radius: 6px; background: color-mix(in srgb, var(--text-muted) 14%, transparent); position: relative; overflow: hidden; }
        .wc-skeleton i::after { content: ''; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-muted) 10%, transparent), transparent); animation: shimmer 1.4s infinite; }

/* ── 提示集 Prompts ── */
.prompts { display: flex; flex-direction: column; gap: 10px; }
.pr-head { display: flex; align-items: center; gap: 8px; padding: 0 2px; }
.pr-head-title { font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
.pr-grid { display: grid; gap: 10px; }
.pr-item { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.pr-item-main { display: flex; align-items: flex-start; gap: 10px; padding: 11px 13px; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-card); cursor: pointer; transition: all var(--transition); min-width: 0; text-align: left; }
    .pr-item-main:hover { border-color: var(--primary-color); background: var(--bg-hover); box-shadow: var(--shadow-sm); }
    .pr-item.disabled .pr-item-main { opacity: .5; cursor: not-allowed; }
        .pr-item.disabled .pr-item-main:hover { border-color: var(--border-color); background: var(--bg-card); box-shadow: none; }
.pr-lead { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 8px; background: var(--primary-soft); color: var(--primary-color); flex-shrink: 0; }
.pr-body { flex: 1; min-width: 0; }
.pr-text { font-size: 13.5px; color: var(--text-primary); line-height: 1.45; }
.pr-desc { margin-top: 2px; font-size: 12px; color: var(--text-muted); line-height: 1.5; }
.pr-children { display: flex; flex-wrap: wrap; gap: 6px; padding-left: 2px; }
.pr-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; font-size: 12.5px; color: var(--text-secondary); border: 1px solid var(--border-color); border-radius: 999px; background: var(--bg-app); cursor: pointer; transition: all var(--transition); }
    .pr-chip:hover { border-color: var(--primary-color); color: var(--primary-color); background: var(--primary-soft); }
    .pr-chip.disabled { opacity: .5; cursor: not-allowed; }
        .pr-chip.disabled:hover { border-color: var(--border-color); color: var(--text-secondary); background: var(--bg-app); }
.pr-empty { padding: 20px 12px; text-align: center; font-size: 13px; color: var(--text-muted); }
.pr-skeleton .pr-item-main { cursor: default; }
.pr-skeleton .pr-lead i, .pr-skeleton .pr-body i { display: block; border-radius: 6px; background: color-mix(in srgb, var(--text-muted) 14%, transparent); position: relative; overflow: hidden; }
.pr-skeleton .pr-lead i { width: 26px; height: 26px; border-radius: 8px; }
.pr-skeleton .pr-body { display: flex; flex-direction: column; gap: 6px; }
.pr-skeleton .pr-body i:first-child { height: 13px; width: 60%; }
.pr-skeleton .pr-body i:last-child { height: 11px; width: 85%; }
    .pr-skeleton .pr-lead i::after, .pr-skeleton .pr-body i::after { content: ''; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-muted) 10%, transparent), transparent); animation: shimmer 1.4s infinite; }

/* ── 思考过程 Think ── */
.think { border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-app); overflow: hidden; }
.th-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px; user-select: none; }
    .th-head.clickable { cursor: pointer; transition: background var(--transition); }
        .th-head.clickable:hover { background: var(--bg-hover); }
.th-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--primary-color); flex-shrink: 0; }
    .th-icon.pulse { animation: th-pulse 1.4s ease-in-out infinite; }
.th-title { font-size: 13px; font-weight: 600; color: var(--text-secondary); }
.th-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--primary-color); }
.th-dots { display: inline-flex; align-items: center; gap: 3px; }
    .th-dots i { width: 4px; height: 4px; border-radius: 50%; background: var(--primary-color); opacity: .4; animation: th-dot 1.2s infinite ease-in-out; }
    .th-dots i:nth-child(2) { animation-delay: .2s; }
    .th-dots i:nth-child(3) { animation-delay: .4s; }
.th-elapsed { font-size: 12px; color: var(--text-muted); font-family: var(--font-mono, ui-monospace, monospace); }
.th-chevron { margin-left: auto; display: inline-flex; align-items: center; color: var(--text-muted); transition: transform .2s ease; flex-shrink: 0; }
    .th-chevron.open { transform: rotate(180deg); }
.th-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .26s ease; }
    .th-panel.open { grid-template-rows: 1fr; }
.th-inner { overflow: hidden; min-height: 0; }
.th-body { margin: 0 12px 12px; padding: 10px 12px; border-left: 2px solid var(--primary-soft); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; background: var(--bg-card); }
.th-text { font-size: 13px; line-height: 1.7; color: var(--text-secondary); white-space: pre-wrap; }
@keyframes th-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
@keyframes th-dot { 0%, 80%, 100% { transform: translateY(0); opacity: .4; } 40% { transform: translateY(-3px); opacity: 1; } }

/* ── 思维链 ThoughtChain ── */
.thought-chain { display: flex; flex-direction: column; gap: 4px; }
.tc-caption { font-size: 12.5px; font-weight: 600; color: var(--text-secondary); padding: 2px 2px 6px; }
.tc-empty { padding: 20px 12px; text-align: center; font-size: 13px; color: var(--text-muted); }
.tc-list { display: flex; flex-direction: column; }
.tc-step { display: grid; grid-template-columns: 28px 1fr; }
.tc-rail { display: flex; flex-direction: column; align-items: center; }
.tc-node { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-muted); }
.tc-node-loading { color: var(--primary-color); border-color: color-mix(in srgb, var(--primary-color) 40%, var(--border-color)); }
    .tc-node-loading svg { animation: tc-spin .9s linear infinite; transform-origin: center; }
.tc-node-success { color: var(--success-color, #52c41a); border-color: color-mix(in srgb, var(--success-color, #52c41a) 40%, var(--border-color)); }
.tc-node-error { color: var(--danger-color, #d4380d); border-color: color-mix(in srgb, var(--danger-color, #d4380d) 40%, var(--border-color)); }
.tc-line { flex: 1; width: 2px; min-height: 12px; background: var(--border-color); margin: 4px 0; border-radius: 2px; }
.tc-body { padding: 0 0 14px 12px; min-width: 0; }
.tc-step.tc-last .tc-body { padding-bottom: 2px; }
.tc-head { display: flex; align-items: center; gap: 8px; min-height: 24px; }
.tc-head.clickable { cursor: pointer; }
.tc-step-title { font-size: 13.5px; font-weight: 600; color: var(--text-primary); min-width: 0; }
.tc-head.clickable:hover .tc-step-title { color: var(--primary-color); }
.tc-chip { font-size: 11px; line-height: 1.4; padding: 1px 7px; border-radius: 999px; flex-shrink: 0; background: var(--bg-hover); color: var(--text-muted); }
.tc-chip-loading { background: var(--primary-soft); color: var(--primary-color); }
.tc-chip-success { background: color-mix(in srgb, var(--success-color, #52c41a) 14%, transparent); color: var(--success-color, #52c41a); }
.tc-chip-error { background: color-mix(in srgb, var(--danger-color, #d4380d) 14%, transparent); color: var(--danger-color, #d4380d); }
.tc-chevron { margin-left: auto; display: inline-flex; align-items: center; color: var(--text-muted); transition: transform .2s ease; flex-shrink: 0; }
.tc-chevron.open { transform: rotate(180deg); }
.tc-desc { margin-top: 4px; font-size: 12.5px; line-height: 1.6; color: var(--text-muted); }
.tc-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .26s ease; }
.tc-panel.open { grid-template-rows: 1fr; }
.tc-inner { overflow: hidden; min-height: 0; }
.tc-content { margin-top: 8px; padding: 10px 12px; font-size: 12.5px; line-height: 1.7; color: var(--text-secondary); white-space: pre-wrap; border-left: 2px solid var(--primary-soft); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; background: var(--bg-app); }
.tc-content-open { margin-top: 8px; }
@keyframes tc-spin { to { transform: rotate(360deg); } }

/* ── 输入附件 ── */
.attachments { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; }
.at-list { display: flex; flex-wrap: wrap; gap: 10px; }
.attachments.at-pill .at-list { gap: 8px; }
.at-item { position: relative; display: flex; align-items: center; gap: 10px; box-sizing: border-box; max-width: 260px; padding: 9px 12px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); box-shadow: var(--shadow-sm); transition: all var(--transition); }
.at-item.clickable { cursor: pointer; }
.at-item.clickable:hover { border-color: color-mix(in srgb, var(--primary-color) 45%, var(--border-color)); box-shadow: var(--shadow-md); }
.attachments.at-pill .at-item { max-width: 200px; padding: 5px 10px 5px 8px; border-radius: 999px; background: var(--bg-app); box-shadow: none; }
.at-icon { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; flex-shrink: 0; border-radius: 9px; background: var(--bg-hover); color: var(--text-secondary); overflow: hidden; }
.attachments.at-pill .at-icon { width: 22px; height: 22px; border-radius: 50%; }
.at-thumb { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.at-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.at-name { font-size: 13px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
.at-size { font-size: 11.5px; color: var(--text-muted); }
.at-error { font-size: 11.5px; color: var(--danger-color, #d4380d); }
.at-meta { display: flex; align-items: center; gap: 6px; }
.at-status { font-size: 11px; color: var(--primary-color); flex-shrink: 0; }
.at-progress { flex: 1; height: 4px; min-width: 46px; border-radius: 999px; background: var(--bg-hover); overflow: hidden; }
.at-progress i { display: block; height: 100%; border-radius: 999px; background: var(--primary-color); transition: width .3s ease; }
.at-pct { font-size: 11px; color: var(--text-muted); flex-shrink: 0; }
.at-item.at-uploading { border-color: color-mix(in srgb, var(--primary-color) 40%, var(--border-color)); }
    .at-item.at-uploading .at-icon svg { animation: tc-spin .9s linear infinite; transform-origin: center; color: var(--primary-color); }
.at-item.at-error { border-color: color-mix(in srgb, var(--danger-color, #d4380d) 45%, var(--border-color)); background: color-mix(in srgb, var(--danger-color, #d4380d) 6%, var(--bg-card)); }
    .at-item.at-error .at-icon { color: var(--danger-color, #d4380d); background: color-mix(in srgb, var(--danger-color, #d4380d) 12%, transparent); }
.at-close { position: absolute; top: -7px; right: -7px; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; padding: 0; border: none; border-radius: 50%; background: var(--text-muted); color: var(--bg-card); cursor: pointer; opacity: 0; transition: opacity var(--transition), background var(--transition); }
.attachments.at-pill .at-close { position: static; width: 16px; height: 16px; margin-left: 2px; opacity: 1; }
.at-item:hover .at-close { opacity: 1; }
.at-close:hover { background: var(--danger-color, #d4380d); }
.at-empty { padding: 8px 4px; font-size: 12.5px; color: var(--text-muted); }
.at-add { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border: 1px dashed var(--border-color); border-radius: var(--radius-lg); background: transparent; color: var(--text-secondary); font-size: 13px; cursor: pointer; transition: all var(--transition); }
.at-add:hover { border-color: var(--primary-color); color: var(--primary-color); background: var(--primary-soft); }

/* ── 输入框（Sender） ── */
.sender { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; box-sizing: border-box; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); box-shadow: var(--shadow-sm); transition: border-color var(--transition), box-shadow var(--transition); }
.sender.sd-focused { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
.sender.sd-disabled { background: var(--bg-app); opacity: .7; }
.sd-header { display: flex; flex-direction: column; gap: 8px; }
.sd-field { display: flex; min-width: 0; }
.sd-input { flex: 1; width: 100%; min-width: 0; padding: 4px 6px; margin: -4px -6px; box-sizing: border-box; font: inherit; font-size: 14px; line-height: 1.6; color: var(--text-primary); background: transparent; border: none; outline: none; resize: none; max-height: 200px; overflow-y: auto; }
    .sd-input::placeholder { color: var(--text-muted); }
.sender.sd-auto .sd-input { field-sizing: content; min-height: 24px; }
.sd-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 32px; }
.sd-tools { display: inline-flex; align-items: center; gap: 6px; min-width: 0; flex-wrap: wrap; }
.sd-actions { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; flex-shrink: 0; }
.sd-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: 10px; cursor: pointer; color: #fff; background: var(--primary-color); transition: opacity var(--transition), background var(--transition), transform var(--transition); }
    .sd-btn:active { transform: scale(.94); }
.sd-btn:disabled { cursor: not-allowed; opacity: .45; }
.sd-send { background: var(--primary-color); }
    .sd-send:not(:disabled):hover { background: color-mix(in srgb, var(--primary-color) 85%, #000); }
.sd-stop { background: var(--danger-color, #d4380d); }
    .sd-stop:hover { background: color-mix(in srgb, var(--danger-color, #d4380d) 85%, #000); }
.sd-footer { font-size: 12px; color: var(--text-muted); }
    /* 插槽没内容时整条消失：卡片是 flex column + gap，留着这个 0 高的 div 也会白占一个 gap 的高度。 */
    .sd-footer:empty { display: none; }

/* ── 快捷指令（Suggestion） ── */
.suggestion { box-sizing: border-box; min-width: 220px; max-width: 360px; padding: 6px; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); box-shadow: var(--shadow-md); }
.sg-title { font-size: 11.5px; font-weight: 600; color: var(--text-muted); padding: 4px 8px 6px; }
.sg-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; max-height: 280px; overflow-y: auto; }
.sg-group { font-size: 11px; color: var(--text-muted); padding: 6px 8px 2px; letter-spacing: .02em; }
.sg-item { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: var(--radius-sm); cursor: pointer; color: var(--text-primary); transition: background var(--transition); }
    .sg-item.active { background: var(--primary-soft); }
    .sg-item.disabled { cursor: not-allowed; opacity: .5; }
.sg-ico { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex-shrink: 0; border-radius: 7px; background: var(--bg-hover); color: var(--text-secondary); }
    .sg-item.active .sg-ico { background: color-mix(in srgb, var(--primary-color) 16%, transparent); color: var(--primary-color); }
.sg-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.sg-label { font-size: 13.5px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sg-desc { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sg-hint { flex-shrink: 0; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; color: var(--text-muted); background: var(--bg-app); border: 1px solid var(--border-color); border-radius: 6px; padding: 1px 6px; }
.sg-empty { padding: 16px 10px; text-align: center; font-size: 12.5px; color: var(--text-muted); }
    .sg-skeleton .sk { background: linear-gradient(90deg, var(--bg-hover) 25%, var(--bg-app) 37%, var(--bg-hover) 63%); background-size: 400% 100%; animation: sg-shimmer 1.3s ease infinite; border-radius: 6px; color: transparent; }
    .sg-skeleton .sg-label.sk { height: 12px; width: 60%; }
    .sg-skeleton .sg-desc.sk { height: 10px; width: 85%; }
@keyframes sg-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* ── 操作列表（Actions） ── */
.actions { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 2px; }
.actions.ac-disabled { opacity: .55; }
.ac-item { display: inline-flex; align-items: center; gap: 5px; height: 28px; padding: 0 8px; border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); font-size: 12.5px; line-height: 1; cursor: pointer; transition: background var(--transition), color var(--transition), border-color var(--transition), transform var(--transition); }
    .ac-item:hover:not(.disabled) { background: var(--bg-hover); color: var(--text-primary); }
    .ac-item:active:not(.disabled) { transform: scale(.94); }
    .ac-item.active { background: var(--primary-soft); color: var(--primary-color); border-color: color-mix(in srgb, var(--primary-color) 30%, transparent); }
    .ac-item.disabled { cursor: not-allowed; opacity: .45; }
.ac-icon { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; }
.ac-label { white-space: nowrap; }

/* ── 代码高亮（CodeHighlighter） ── */
.code-highlighter { --ch-kw:#b13a8f; --ch-str:#0a7a3c; --ch-num:#9a5b00; --ch-com:#8a939f; --ch-fg:var(--text-primary); box-sizing: border-box; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); overflow: hidden; }
[data-theme="dark"] .code-highlighter { --ch-kw:#f0a8d0; --ch-str:#7ee2a8; --ch-num:#e3b341; --ch-com:#7a8595; }
.ch-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 10px; background: var(--bg-app); border-bottom: 1px solid var(--border-color); }
.ch-meta { display: inline-flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.ch-file { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-secondary); min-width: 0; }
    .ch-file .ch-file-name { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-lang { font-size: 10.5px; letter-spacing: .03em; text-transform: uppercase; color: var(--primary-color); background: var(--primary-soft); border-radius: 6px; padding: 1px 7px; }
.ch-copy { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-secondary); font-size: 12px; cursor: pointer; flex-shrink: 0; transition: color var(--transition), border-color var(--transition), background var(--transition); }
    .ch-copy:hover { color: var(--primary-color); border-color: var(--primary-soft); background: var(--bg-hover); }
.ch-body { overflow: auto; }
.ch-pre { margin: 0; padding: 10px 0; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); font-size: 12.5px; line-height: 1.6; color: var(--ch-fg); }
.ch-code-block { display: block; }
.ch-line { display: flex; padding: 0 12px; }
.ch-no { flex-shrink: 0; width: 2.2em; padding-right: 1em; margin-right: 1em; text-align: right; color: var(--text-muted); opacity: .6; user-select: none; border-right: 1px solid var(--border-color); }
.ch-tokwrap { white-space: pre; min-width: 0; }
.ch-pre.ch-wrap .ch-tokwrap { white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; }
.ch-kw { color: var(--ch-kw); }
.ch-str { color: var(--ch-str); }
.ch-num { color: var(--ch-num); }
.ch-com { color: var(--ch-com); font-style: italic; }

/* ── 文件卡片（FileCard） ── */
.file-card { display: flex; flex-wrap: wrap; gap: 12px; }
.fc-item { display: flex; flex-direction: column; gap: 8px; width: 120px; padding: 10px; box-sizing: border-box; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition); }
    .fc-large .fc-item { width: 152px; }
    .fc-small .fc-item { width: 96px; }
    .fc-item.clickable { cursor: pointer; }
    .fc-item.clickable:hover { border-color: var(--primary-soft); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.fc-thumb { position: relative; display: flex; align-items: center; justify-content: center; height: 64px; border-radius: var(--radius-sm); background: var(--bg-app); overflow: hidden; }
    .fc-large .fc-thumb { height: 86px; }
    .fc-small .fc-thumb { height: 52px; }
.fc-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fc-badge { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 12px; color: #fff; background: var(--fc-c, #94a3b8); }
    .fc-plain .fc-badge { background: color-mix(in srgb, var(--fc-c, #94a3b8) 14%, transparent); color: var(--fc-c, #94a3b8); }
.fc-ext { position: absolute; right: 6px; bottom: 6px; font-size: 9.5px; font-weight: 700; letter-spacing: .02em; color: #fff; background: var(--fc-c, #94a3b8); padding: 1px 5px; border-radius: 5px; }
    .fc-plain .fc-ext { color: var(--fc-c, #94a3b8); background: color-mix(in srgb, var(--fc-c, #94a3b8) 16%, var(--bg-card)); }
.fc-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fc-name { font-size: 12.5px; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fc-size { font-size: 11px; color: var(--text-muted); }
.fc-empty { padding: 16px; text-align: center; font-size: 12.5px; color: var(--text-muted); }

/* ── 文件夹树（Folder） ── */
.folder { display: flex; flex-direction: column; padding: 6px; box-sizing: border-box; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-card); }
.fd-node { min-width: 0; }
.fd-children { padding-left: 16px; margin-left: 9px; border-left: 1px solid var(--border-color); }
.fd-row { display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 8px; border-radius: var(--radius-sm); color: var(--text-primary); font-size: 13px; line-height: 1; user-select: none; transition: background var(--transition), color var(--transition); }
    .fd-row.is-clickable { cursor: pointer; }
    .fd-row.is-clickable:hover { background: var(--bg-hover); }
    .fd-row.is-selected { background: var(--primary-soft); color: var(--primary-color); }
    .fd-row.is-disabled { opacity: .5; }
    .fd-row.is-disabled.is-clickable { cursor: not-allowed; }
.fd-toggle { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; flex: none; color: var(--text-muted); border-radius: 4px; transition: color var(--transition), background var(--transition); }
    .fd-toggle:hover { color: var(--text-primary); background: var(--bg-hover); }
.fd-ico { display: inline-flex; align-items: center; justify-content: center; width: 18px; flex: none; color: var(--fd-c, #94a3b8); }
.fd-row.is-dir .fd-ico { color: var(--fd-c, #e0a94f); }
.fd-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fd-size { flex: none; font-size: 11.5px; color: var(--text-muted); }
.fd-row.is-selected .fd-size { color: inherit; opacity: .8; }
.fd-empty { padding: 16px; text-align: center; font-size: 12.5px; color: var(--text-muted); }

/* ── Mermaid 图表（Mermaid） ── */
.mermaid { display: block; }
.mmd-box { position: relative; width: 100%; }
.mmd-canvas { color: var(--text-primary); }
    .mmd-canvas svg { max-width: 100%; height: auto; display: block; margin: 0 auto; }
.mmd-loading .mmd-box, .mmd-error .mmd-box, .mmd-empty .mmd-box { min-height: 120px; }
.mmd-mask { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 12px; box-sizing: border-box; background: var(--bg-card); color: var(--text-muted); font-size: 12.5px; text-align: center; }
.mmd-mask-text { letter-spacing: .01em; }
.mmd-spin { display: inline-flex; animation: mmd-spin .9s linear infinite; }
@keyframes mmd-spin { to { transform: rotate(360deg); } }
.mmd-err { color: #d0554f; }
.mmd-empty { color: var(--text-muted); }
.mmd-errmsg { max-width: 100%; max-height: 160px; margin: 4px 0 0; padding: 8px 10px; box-sizing: border-box; font-size: 11.5px; line-height: 1.5; color: var(--text-secondary); background: var(--bg-app); border: 1px solid var(--border-color); border-radius: var(--radius-sm); white-space: pre-wrap; word-break: break-word; text-align: left; overflow: auto; }

/* ── 来源引用（Sources） ── */
.sources { display: block; }
.sr-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.sr-label { font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
.sr-count { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 6px; font-size: 11px; line-height: 1; color: var(--text-muted); background: var(--bg-hover); border-radius: 999px; }
.sr-body { display: flex; flex-direction: column; gap: 8px; }
.sr-item { display: flex; align-items: flex-start; gap: 9px; padding: 8px 10px; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-sm); transition: background .15s ease, border-color .15s ease; }
.sr-item.clickable { cursor: pointer; }
.sr-item.clickable:hover { background: var(--bg-hover); border-color: color-mix(in srgb, var(--sr-c, #94a3b8) 40%, var(--border-color)); }
.sr-idx { flex: none; min-width: 15px; font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--text-muted); line-height: 22px; text-align: center; }
.sr-ico { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 6px; color: var(--sr-c, #94a3b8); background: color-mix(in srgb, var(--sr-c, #94a3b8) 14%, transparent); overflow: hidden; }
.sr-fav { width: 22px; height: 22px; object-fit: cover; }
.sr-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sr-name { font-size: 13px; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
a.sr-name { text-decoration: none; }
a.sr-name:hover { text-decoration: underline; color: var(--primary-color); }
.sr-meta { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sr-snip { margin: 3px 0 0; font-size: 12px; line-height: 1.55; color: var(--text-secondary); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.sr-empty { padding: 14px; text-align: center; font-size: 12.5px; color: var(--text-muted); }
.sources.sr-chips .sr-body { flex-direction: row; flex-wrap: wrap; gap: 8px; }
.sources.sr-chips .sr-item { align-items: center; padding: 5px 11px 5px 8px; border-radius: 999px; }
.sources.sr-chips .sr-main { flex-direction: row; align-items: center; }
.sources.sr-chips .sr-idx { line-height: normal; }
.sources.sr-chips .sr-ico { width: 18px; height: 18px; border-radius: 5px; }
.sources.sr-chips .sr-fav { width: 18px; height: 18px; }
.sources.sr-chips .sr-meta,
.sources.sr-chips .sr-snip { display: none; }
.sources.sr-chips .sr-name { font-size: 12.5px; }

/* ── 统计卡片 ── */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.stat-card { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: 18px 20px; box-shadow: var(--shadow-sm); transition: all var(--transition); min-width: 0; }
    .stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--primary-soft); }
.stat-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.stat-icon { width: 40px; height: 40px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 17px; flex-shrink: 0; margin-left: 10px; }
    .stat-icon.primary { background: var(--primary-soft); color: var(--primary-color); }
    .stat-icon.success { background: var(--success-soft); color: var(--success-color); }
    .stat-icon.warning { background: var(--warning-soft); color: var(--warning-color); }
    .stat-icon.danger { background: var(--danger-soft); color: var(--danger-color); }
    .stat-icon.info { background: var(--info-soft); color: var(--info-color); }
.stat-value { font-size: 26px; font-weight: 600; letter-spacing: -.6px; color: var(--text-primary); font-variant-numeric: tabular-nums; line-height: 1.2; }
    .stat-value.primary { color: var(--primary-color); }
    .stat-value.success { color: var(--success-color); }
    .stat-value.warning { color: var(--warning-color); }
    .stat-value.danger { color: var(--danger-color); }
    .stat-value.info { color: var(--info-color); }
.stat-label { font-size: 13px; color: var(--text-muted); margin-top: 4px; }
.stat-foot { display: flex; align-items: center; gap: 6px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.stat-trend { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; font-size: 12.5px; }
    .stat-trend.up { color: var(--success-color); }
    .stat-trend.down { color: var(--danger-color); }

/* ── 图表 ── */
.chart-legend { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-bottom: 6px; }
.legend-item { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-secondary); }
.legend-dot { width: 9px; height: 9px; border-radius: 3px; flex-shrink: 0; }
.chart-svg { width: 100%; height: auto; display: block; overflow: visible; }
.chart-axis-text { font-size: 10.5px; fill: var(--text-muted); font-family: var(--font-sans); }
.chart-grid-line { stroke: var(--border-light); stroke-width: 1; }
.bar-chart { display: flex; align-items: flex-end; gap: 14px; height: 190px; padding-top: 10px; }
.bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 8px; height: 100%; justify-content: flex-end; min-width: 0; }
.bar-fill { width: 100%; max-width: 42px; border-radius: 6px 6px 3px 3px; background: linear-gradient(180deg, var(--primary-color), var(--primary-hover)); transition: filter var(--transition), height .5s cubic-bezier(.4, 0, .2, 1); }
    .bar-col:hover .bar-fill { filter: brightness(1.15); }
    .bar-fill.success { background: linear-gradient(180deg, var(--success-color), #0A7A46); }
    .bar-fill.warning { background: linear-gradient(180deg, var(--warning-color), #B4690A); }
    .bar-fill.danger { background: linear-gradient(180deg, var(--danger-color), #B42318); }
.bar-label { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }

/* ── 折叠面板 ── */
.collapse { border: 1px solid var(--border-color); border-radius: var(--radius-md); overflow: hidden; }
.collapse-item { border-bottom: 1px solid var(--border-light); }
    .collapse-item:last-child { border-bottom: none; }
.collapse-head { display: flex; align-items: center; gap: 10px; padding: 14px 18px; font-size: 14px; font-weight: 500; color: var(--text-primary); cursor: pointer; transition: background var(--transition), color var(--transition); user-select: none; }
    .collapse-head:hover { background: var(--bg-hover); }
    .collapse-head .icon.lead { font-size: 13px; }
    .collapse-head i.chev, .collapse-head .chev { margin-left: auto; font-size: 11px; color: var(--text-muted); transition: transform .25s ease; }
    .collapse-item.open .collapse-head .chev { transform: rotate(180deg); }
    .collapse-item.open > .collapse-head { color: var(--primary-color); }
.collapse-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .3s cubic-bezier(.4, 0, .2, 1); }
    .collapse-item.open .collapse-body { grid-template-rows: 1fr; }
.collapse-body > div { overflow: hidden; }
.collapse-content { padding: 0 18px 16px; font-size: 13.5px; color: var(--text-secondary); line-height: 1.65; }

/* ── 树 ── */
.tree { font-size: 13.5px; }
.tree-children { padding-left: 20px; margin-left: 9px; border-left: 1px dashed var(--border-color); display: none; }
    .tree-node.open > .tree-children { display: block; }
.tree-row { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: var(--radius-sm); color: var(--text-primary); transition: all var(--transition); cursor: pointer; }
    .tree-row:hover { background: var(--bg-hover); }
    .tree-row.active { background: var(--primary-soft); color: var(--primary-color); font-weight: 600; }
.tree-arrow { width: 16px; height: 16px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 10px; color: var(--text-muted); transition: transform .2s ease; }
    .tree-node.open > .tree-row .tree-arrow { transform: rotate(90deg); }
    .tree-arrow.leaf { visibility: hidden; }
    .tree-row > .icon.tree-folder { color: var(--warning-color); font-size: 15px; }
    .tree-row > .icon.tree-file { color: var(--text-muted); font-size: 14px; }
.tree-count { margin-left: auto; font-size: 11px; color: var(--text-muted); background: var(--tag-bg); padding: 1px 7px; border-radius: 20px; }
.tree-tag { flex: none; font-size: 10px; padding: 0 6px; border-radius: 9px; background: var(--primary-soft); color: var(--primary-color); }
    .tree-tag.db { background: #e8f5e9; color: #2e7d32; }
.tree-row.muted { color: var(--text-muted); }
/* 复选框：CSS 自绘三态（勾选/半选/未选），与原生 accent 观感对齐但无需 JS 设 indeterminate 属性 */
.tree-check { flex: none; width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--border-color); border-radius: 4px; background: var(--bg-color, #fff); cursor: pointer; transition: all var(--transition); }
    .tree-check:hover { border-color: var(--primary-color); }
    .tree-check.checked { background: var(--primary-color); border-color: var(--primary-color); }
        .tree-check.checked::after { content: ""; width: 4px; height: 8px; margin-top: -2px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }
    .tree-check.indeterminate { border-color: var(--primary-color); }
        .tree-check.indeterminate::after { content: ""; width: 8px; height: 2px; border-radius: 1px; background: var(--primary-color); }
    .tree-check.disabled { opacity: .45; cursor: not-allowed; }

/* 下拉树（触发框复用 .select 样式，面板复用 .tree-* 样式） */
.selecttree-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    min-width: 100%;
    max-height: 280px;
    overflow: auto;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    z-index: 400;
}
.selecttree-empty { padding: 18px; text-align: center; font-size: 13px; color: var(--text-muted); }
.tree-row.focused { background: var(--bg-hover); box-shadow: inset 0 0 0 1px var(--primary-color); }
.selecttree-clear { position: absolute; right: 32px; display: inline-flex; align-items: center; color: var(--text-muted); cursor: pointer; opacity: 0; transition: all var(--transition); }
    .selecttree:hover .selecttree-clear { opacity: 1; }
    .selecttree-clear:hover { color: var(--danger-color); }

/* ── 菜单 ── */
.menu { display: flex; flex-direction: column; gap: 2px; }
.menu-item { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: var(--radius-sm); font-size: 13.5px; color: var(--text-secondary); cursor: pointer; transition: all var(--transition); }
    .menu-item:hover { background: var(--bg-hover); color: var(--text-primary); }
    .menu-item.active { background: var(--primary-soft); color: var(--primary-color); font-weight: 600; }
    .menu-item > .icon.lead { width: 18px; text-align: center; font-size: 13px; flex-shrink: 0; }
    .menu-item .menu-arrow { margin-left: auto; font-size: 10px; color: var(--text-muted); transition: transform .2s ease; }
    .menu-group.open > .menu-item .menu-arrow { transform: rotate(90deg); }
.menu-sub { display: none; flex-direction: column; gap: 2px; margin-top: 2px; padding-left: 18px; margin-left: 15px; border-left: 1px solid var(--border-light); }
    .menu-group.open > .menu-sub { display: flex; }
.menu-horizontal { flex-direction: row; align-items: center; }
    .menu-horizontal .menu-group { position: relative; }
    .menu-horizontal .menu-sub {
        position: absolute;
        top: calc(100% + 8px);
        left: 0;
        margin: 0;
        padding: 6px;
        border-left: 0;
        min-width: 176px;
        background: var(--bg-card);
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
        box-shadow: var(--shadow-lg);
        z-index: 500;
    }

/* ── 下拉菜单 ── */
.dropdown { position: relative; display: inline-block; }
.dropdown-menu {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    min-width: 176px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    z-index: 500;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px) scale(.97);
    transform-origin: top left;
    /* 同 .popconfirm：position 类属性不能进过渡，否则 Portal 下拉每次改写视口坐标都会滑出一段飞行轨迹。 */
    transition: opacity .18s ease, transform .18s ease, visibility .18s ease;
}
    .dropdown-menu.right { left: auto; right: 0; transform-origin: top right; }
    /* 朝上开：贴着窗口底边的下拉（如智能体输入框里的模型选择）往下长会被裁掉，只能往上顶 */
    .dropdown-menu.up { top: auto; bottom: calc(100% + 8px); transform-origin: bottom left; transform: translateY(6px) scale(.97); }
        .dropdown-menu.up.right { transform-origin: bottom right; }
    /* 只点亮自己那一层：必须是直接子元素，否则「弹层里再套弹层」（会话清单行的更多操作）会被外层一开就全点亮 */
    .dropdown.open > .dropdown-menu { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
    /* 传送门模式：菜单被搬到 <body> 后脱离 .dropdown 祖先，改用自身 .open 显隐（定位由 JS 以 fixed 接管） */
    .dropdown-menu.open { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }

/* ── 关闭态浮层统一脱流（唯一清单，新增浮层组件请往这里加） ──
   opacity:0 + visibility:hidden 的浮层**仍然占布局**：它们照样计入所在滚动容器（.modal-body / .drawer-body /
   .table-scroll / 会话清单 …）的可滚动区域，于是弹窗一打开就凭空多出一条滚动条、表格滚到尽头还留白一截。
   关闭态统一改 fixed——fixed 元素不参与任何祖先的滚动溢出计算；打开时各自的 absolute 规则重新接管，
   opacity/visibility/transform 一个没动，开合动画不受影响。
   两处旧补丁（.table-scroll 与 .cv-list 里单独写的 dropdown-menu/popconfirm）已并入本清单。 */
.user-menu:not(.open),
.context-menu:not(.open),
.notification-panel:not(.open),
.lang-panel:not(.open),
.top-panel:not(.open),
.dev-panel:not(.open),
.popconfirm:not(.open),
.dropdown-menu:not(.open),
.select:not(.open) .select-list,
/* Select 的列表会经传送门搬到 body，上面那条祖先选择器那时不再匹配，关闭态改用自身状态判断；
   .open:not(.placed) 是「已打开、坐标还没写」的那一帧，同样按住，否则搬家瞬间滚动条会闪一下 */
.select-list:not(.open),
.select-list.open:not(.placed),
.select:not(.open) .cascader-panel,
.select:not(.open) .date-panel,
/* Cascader / Date 的面板同样会经传送门搬到 body，祖先选择器在那时失效，补自身状态的两条 */
.cascader-panel:not(.open),
.cascader-panel.open:not(.placed),
.date-panel:not(.open),
.date-panel.open:not(.placed),
.selecttable:not(.open) .selecttable-panel,
.autocomplete-list:not(.open),
/* Autocomplete 开了但传送门还没写坐标的那一帧也按住：那一帧若回落到 absolute，滚动条会闪一下 */
.autocomplete-list.open:not(.placed),
/* 气泡悬停工具行：不悬停时不占位，省得聊天列表每条消息都往下多撑一行 */
.bubble:not(:hover):not(:focus-within) .bb-tools { position: fixed; }

/* ── Tabs ── */
.tabs { display: flex; flex-direction: column; gap: 16px; }
/* 分隔线用 inset 阴影画在盒子内侧，不用 border-bottom：
   选中下划线要靠标签自己的盒子压住这条线，border 在盒子外，只能靠 margin-bottom:-1px 让标签越界 1px 去盖；
   而 overflow-x:auto 会把 overflow-y 一并算成 auto，越界的 1px 就让导航右缘长出一条竖直滚动条
   （截图里那根灰色竖条）。线画在盒内、下划线也画在盒内，两边都不用越界。 */
.tabs-nav {
    display: flex; align-items: center; gap: 4px;
    box-shadow: inset 0 -1px 0 var(--border-color);
    overflow-x: auto; overflow-y: hidden;
}
.tab-item { position: relative; padding: 10px 16px; font-family: inherit; font-size: 14px; font-weight: 500; color: var(--text-secondary); cursor: pointer; border: none; background: transparent; transition: all var(--transition); white-space: nowrap; display: flex; align-items: center; gap: 7px; }
    .tab-item .icon { font-size: 13px; }
    .tab-item:hover { color: var(--text-primary); }
    .tab-item.active { color: var(--primary-color); }
        /* 选中下划线贴在标签底部内侧，正好盖住导航的 inset 分隔线 */
        .tab-item.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--primary-color); border-radius: 1px; }
.tab-panel { font-size: 14px; color: var(--text-secondary); line-height: 1.6; }
/* 卡片式标签页：标签是卡片顶部的页签，选中项与内容面板连成一片 */
.tabs-card { gap: 0; }
    /* 分隔线交给内容面板的上边框：标签的底边框与它落在同一行上，选中项把底边框换成卡片底色，
       就和面板连成一片，不再多出一条横线。面板上移 1px 才能和标签底边重叠，标签靠 z-index 压在上面。 */
    .tabs-card > .tabs-nav { gap: 6px; box-shadow: none; align-items: flex-end; }
        .tabs-card > .tabs-nav .tab-item { padding: 8px 16px; background: var(--bg-hover); border: 1px solid var(--border-color); border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
        .tabs-card > .tabs-nav .tab-item:hover { background: var(--bg-card); color: var(--text-primary); }
        .tabs-card > .tabs-nav .tab-item.active { z-index: 1; background: var(--bg-card); color: var(--primary-color); border-bottom-color: var(--bg-card); }
            .tabs-card > .tabs-nav .tab-item.active::after { content: none; }
    /* 面板四角都收圆；左上角不留圆是刻意的——它正好在第一个页签底下，收圆会在页签下沿漏出一个小缺口 */
    .tabs-card > .tab-panel { position: relative; margin-top: -1px; padding: 16px 18px; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 0 var(--radius-sm) var(--radius-sm) var(--radius-sm); }

/* ── 分段控制器 ── */
.segmented { display: inline-flex; align-items: center; gap: 4px; padding: 4px; background: var(--bg-hover); border: 1px solid var(--border-color); border-radius: 12px; box-shadow: inset 0 1px 2px rgba(16,24,40,.05); position: relative; }
.segment-item { position: relative; padding: 7px 16px; border: none; background: transparent; border-radius: 9px; font-family: inherit; font-size: 13px; font-weight: 500; line-height: 1.2; color: var(--text-muted); cursor: pointer; display: flex; align-items: center; gap: 6px; white-space: nowrap; user-select: none; transition: color var(--transition), background var(--transition), box-shadow var(--transition), transform var(--transition); }
    .segment-item .icon { font-size: 12px; }
    .segment-item:hover:not(.active):not(:disabled) { color: var(--primary-color); background: var(--primary-soft); }
    .segment-item:active:not(.active):not(:disabled) { transform: scale(.97); }
    .segment-item:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; }
    .segment-item.active { color: var(--primary-color); background: var(--bg-card); font-weight: 600; box-shadow: var(--shadow-sm); }
    .segment-item:disabled { opacity: .45; cursor: not-allowed; }
.segmented.block { display: flex; width: 100%; }
    .segmented.block .segment-item { flex: 1; justify-content: center; }
.segmented.sm { padding: 3px; gap: 2px; border-radius: 10px; }
    .segmented.sm .segment-item { padding: 4px 12px; font-size: 12px; border-radius: 7px; }

/* ── 步骤条 ── */
.steps { display: flex; align-items: flex-start; }
.step-item { flex: 1; display: flex; flex-direction: column; align-items: center; position: relative; text-align: center; }
    .step-item:not(:last-child)::after { content: ''; position: absolute; top: 15px; left: calc(50% + 18px); width: calc(100% - 36px); height: 2px; background: var(--border-color); border-radius: 2px; z-index: 0; }
    .step-item.completed:not(:last-child)::after { background: var(--primary-color); }
.step-circle { width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600; background: var(--bg-hover); color: var(--text-muted); border: 2px solid var(--border-color); z-index: 1; transition: all var(--transition); flex-shrink: 0; }
    .step-circle .icon { font-size: 11px; }
    .step-item.active .step-circle { background: var(--primary-color); border-color: var(--primary-color); color: #fff; box-shadow: 0 0 0 4px var(--primary-soft); }
    .step-item.completed .step-circle { background: var(--primary-soft); border-color: var(--primary-color); color: var(--primary-color); }
    .step-item.error .step-circle { background: var(--danger-color); border-color: var(--danger-color); color: #fff; }
    .step-item.error:not(:last-child)::after { background: var(--danger-color); }
.step-label { font-size: 13px; font-weight: 500; color: var(--text-muted); margin-top: 8px; }
    .step-item.active .step-label { color: var(--primary-color); font-weight: 600; }
    .step-item.completed .step-label { color: var(--text-primary); }
.step-desc { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.steps.vertical { flex-direction: column; align-items: stretch; }
    .steps.vertical .step-item { flex-direction: row; align-items: flex-start; gap: 12px; text-align: left; }
    .steps.vertical .step-item::after { top: 34px; bottom: 2px; left: 15px; width: 2px; height: auto; }
    .steps.vertical .step-label, .steps.vertical .step-desc { margin-top: 0; }
    .steps.vertical .step-label { margin-top: 4px; }

/* ── 面包屑 ── */
.breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 13.5px; }
.breadcrumb-item { display: inline-flex; align-items: center; gap: 7px; color: var(--text-muted); transition: color var(--transition); }
    .breadcrumb-item.link { color: var(--text-secondary); cursor: default; }
        .breadcrumb-item.link:hover { color: var(--primary-color); }
    .breadcrumb-item.active { color: var(--text-primary); font-weight: 600; }
.breadcrumb-sep { font-size: 9px; color: var(--text-muted); opacity: .6; }

/* ── 页头 ── */
.page-header { display: flex; align-items: center; justify-content: space-between; gap: 18px; }
    .page-header-main { min-width: 0; }
    .page-header-title { display: flex; align-items: center; gap: 10px; }
    .page-header h2 { font-size: 20px; font-weight: 600; letter-spacing: -.3px; color: var(--text-primary); margin: 0; }
.page-header-desc { font-size: 13px; color: var(--text-muted); margin-top: 5px; }
.page-header-actions { display: flex; align-items: center; gap: 10px; }

/* ── 状态通知 ── */
.alert { display: flex; align-items: flex-start; gap: 12px; padding: 14px 16px; border-radius: var(--radius-md); border: 1px solid transparent; font-size: 14px; margin-bottom: 12px; position: relative; }
    .alert:last-child { margin-bottom: 0; }
    /* 图标盒做成「标题行高 + 1px」，图形在盒内垂直居中，才对得上标题文字的中线。
       之前图标盒是 1em（17px）配 margin-top:1px：盒子顶对齐行框顶，而 13.5px 的字在 20px 行框里
       往下沉了半行，实测图形墨迹中心比文字墨迹中心高 1～1.6px，看着就是图标往上翘。
       横向 padding 为 0，图形左缘仍贴着 16px 内边距，不用调左对齐。 */
    .alert > .icon:first-child { width: 17px; height: 21px; padding: 2px 0; margin-top: 0; font-size: 17px; flex-shrink: 0; }
.alert-body { flex: 1; min-width: 0; }
    /* 行高写死，和上面图标盒的 21px 是一对，改一个得改另一个 */
    .alert-title { font-weight: 600; font-size: 13.5px; line-height: 20px; color: var(--text-primary); }
.alert-desc { font-size: 12.5px; color: var(--text-secondary); margin-top: 2px; line-height: 1.45; }
/* 关闭按钮与左侧图标同高（21px），图形居中，× 才不会比标题高半头 */
.alert-close { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 21px; padding: 0; background: transparent; border: none; color: var(--text-muted); cursor: default; font-size: 13px; border-radius: var(--radius-sm); transition: all var(--transition); flex-shrink: 0; }
    .alert-close:hover { color: var(--text-primary); background: rgba(0, 0, 0, .05); }
    [data-theme='dark'] .alert-close:hover { background: rgba(255, 255, 255, .08); }
.alert-success { background: var(--success-soft); }
    .alert-success > .icon:first-child { color: var(--success-color); }
.alert-warning { background: var(--warning-soft); }
    .alert-warning > .icon:first-child { color: var(--warning-color); }
.alert-danger { background: var(--danger-soft); }
    .alert-danger > .icon:first-child { color: var(--danger-color); }
.alert-info { background: var(--info-soft); }
    .alert-info > .icon:first-child { color: var(--info-color); }
.alert-closing { opacity: 0; transform: translateX(10px); transition: opacity .2s ease, transform .2s ease; }

/* ── 骨架屏 ── */
.skeleton { background: var(--skeleton-base); border-radius: 6px; position: relative; overflow: hidden; }
    .skeleton::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, var(--skeleton-shine), transparent); animation: shimmer 1.4s infinite; }
@keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }
.skeleton-circle { border-radius: 50%; }
.skeleton-group { display: flex; gap: 14px; }
.skeleton-lines { flex: 1; display: flex; flex-direction: column; gap: 9px; }
.skeleton-card { border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: 16px; background: var(--bg-card); }

/* ── 加载动画 ── */
.spin { display: inline-block; width: 20px; height: 20px; border: 2.5px solid var(--primary-soft); border-top-color: var(--primary-color); border-radius: 50%; animation: spin .7s linear infinite; vertical-align: middle; }
    .spin.lg { width: 34px; height: 34px; border-width: 3.5px; }
    .spin.sm { width: 14px; height: 14px; border-width: 2px; }
@keyframes spin { to { transform: rotate(360deg); } }
.spin-wrap { display: inline-flex; align-items: center; gap: 8px; vertical-align: middle; }
.spin-text { font-size: 13px; color: var(--text-secondary); }
.spin-dot { display: inline-flex; gap: 6px; align-items: center; }
    .spin-dot span { width: 8px; height: 8px; border-radius: 50%; background: var(--primary-color); animation: bounce 1.2s infinite ease-in-out; }
    .spin-dot span:nth-child(2) { animation-delay: .18s; }
    .spin-dot span:nth-child(3) { animation-delay: .36s; }
    .spin-dot.lg { gap: 9px; }
    .spin-dot.lg span { width: 12px; height: 12px; }
    .spin-dot.sm { gap: 4px; }
    .spin-dot.sm span { width: 6px; height: 6px; }
@keyframes bounce { 0%, 80%, 100% { transform: scale(.6); opacity: .5; } 40% { transform: scale(1); opacity: 1; } }
.spin-overlay { position: relative; border-radius: var(--radius-md); overflow: hidden; }
    .spin-overlay .spin-content { position: relative; }
.spin-mask { position: absolute; inset: 0; background: rgba(255, 255, 255, .6); backdrop-filter: blur(1px); display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 10px; font-size: 13px; color: var(--text-secondary); z-index: 5; }
    [data-theme='dark'] .spin-mask { background: rgba(14, 18, 27, .65); }

/* ── 异常页 ── */
.error-page {
    --error-accent: var(--primary-color);
    --error-accent-2: #7C5CFF;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; padding: 36px 16px;
}
    .error-page.e403 { --error-accent: var(--warning-color); --error-accent-2: #F5B301; }
    .error-page.e500 { --error-accent: var(--danger-color); --error-accent-2: #FF3D71; }

.error-medallion { position: relative; width: 168px; height: 168px; display: grid; place-items: center; margin-bottom: 18px; }
    .error-medallion .error-ring {
        position: absolute; inset: 0; border-radius: 50%;
        background: conic-gradient(from 140deg, var(--error-accent), var(--error-accent-2), transparent 58%, var(--error-accent));
        -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
        mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
        animation: error-spin 9s linear infinite;
    }
    .error-medallion .error-disc {
        position: absolute; inset: 12px; border-radius: 50%;
        background: radial-gradient(circle at 50% 32%, var(--bg-card), var(--bg-hover));
        box-shadow: inset 0 0 0 1px var(--border-color), 0 24px 48px -24px var(--error-accent);
    }
    .error-medallion .error-code {
        position: relative; font-size: 58px; font-weight: 800; letter-spacing: -2px; line-height: 1;
        background: linear-gradient(135deg, var(--error-accent), var(--error-accent-2));
        -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
    }

.error-label {
    display: inline-flex; align-items: center; gap: 6px; margin-bottom: 14px;
    font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
    color: var(--error-accent);
    background: color-mix(in srgb, var(--error-accent) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--error-accent) 30%, transparent);
    padding: 5px 12px; border-radius: 999px;
}

.error-title { font-size: 18px; font-weight: 600; color: var(--text-primary); margin-bottom: 8px; }
.error-desc { font-size: 13.5px; color: var(--text-muted); max-width: 340px; line-height: 1.6; margin-bottom: 20px; }

@keyframes error-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .error-medallion .error-ring { animation: none; } }

/* ── 上下文菜单通用项 ── */
.context-list { display: flex; flex-direction: column; gap: 2px; }

/* ============================================================
   6. 响应式
   ============================================================ */
@media (max-width: 1200px) { .grid-3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 1150px) {
    .grid-2, .grid-3 { grid-template-columns: 1fr; }
    .stat-grid { grid-template-columns: repeat(2, 1fr); }
    .descriptions.grid-2-col { grid-template-columns: 1fr; }
        .descriptions.grid-2-col .desc-item { border-bottom: 1px solid var(--border-light); }
        .descriptions.grid-2-col .desc-item:last-child { border-bottom: none; }
}
@media (max-width: 900px) {
    .global-search { display: none; }
    .user-trigger .user-meta { display: none; }
    .content-scroll { padding: 18px; gap: 18px; }
    .top-bar { padding: 12px 18px; }
    .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
    .stat-grid { grid-template-columns: 1fr; }
    .page-header h2 { font-size: 18px; }
    .notification { width: auto; left: 16px; right: 16px; }
    /* 手机顶栏：品牌位 + 抽屉开关先占掉约 116px，剩下的两三百像素要全留给操作区。
       面包屑和顶栏标题、窄边栏高亮项三处重复，刷新/全屏是桌面动作（浏览器下拉刷新已覆盖），
       下拉箭头和分隔线只是装饰——一并收起，标题才有位置，头像也不会被挤出屏幕。 */
    .top-bar { padding: 10px 12px; gap: 8px; }
    .page-title { font-size: 17px; }
    .crumb-sep, .page-crumb, .tb-refresh, .tb-fullscreen, .top-divider, .user-chevron { display: none; }
    .top-actions { gap: 4px; }
    .user-trigger { padding: 2px; }
}
@media print {
    .sidebar, .top-bar, .back-top, .lbar, .toast-container, .notification { display: none !important; }
    .content-scroll { overflow: visible; padding: 0; }
}

/* ============================================================
   7. 组件补充样式（Blazor 迁移新增）
   ============================================================ */

/* 表格：工具栏左区、分页容器、行内操作、合计行 */
.table-toolbar .toolbar-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.table-toolbar .header-title { display: inline-flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 600; color: var(--text-primary); }
    .table-toolbar .header-title .icon { color: var(--primary-color); }
.table-pagination { display: flex; align-items: center; justify-content: flex-end; gap: 12px; padding: var(--cell-pad-y) 12px; flex-wrap: wrap; }
    .table-pagination .pagination { width: 100%; }
.table-page-hint { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-muted); }
.row-actions { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
.table-pro tfoot td { background: var(--table-head-bg); font-weight: 600; color: var(--text-primary); border-top: 1px solid var(--border-color); }
.table-pro.table-fixed thead th { position: sticky; top: 0; z-index: 2; }
/* 斑马纹底色（本段与上文 .table-striped 规则重复声明，特异性更高，实际生效的是这条）：
   用 --table-stripe-bg 保证比表头 --table-head-bg 更浅，避免偶数行反比表头还深 */
.table-pro.table-striped tbody tr:nth-child(even) { background: var(--table-stripe-bg); }
    .table-pro.table-striped tbody tr.selected, .table-pro.table-striped tbody tr.selected:nth-child(even) { background: var(--primary-soft); }
.table-pro tbody tr { cursor: default; }
/* 页容量切换用的是 Select 组件：边框、背景、聚焦环都由 .input,.select 统一给，
   这里只把它压回分页按钮那一档——行高跟 --pager-h、13px 字号、小圆角，免得撑高整条分页器。
   右侧留 24px 是给 .select-arrow 让位（组件默认 34px 对紧凑控件太宽）。 */
.page-size { width: auto; min-height: var(--pager-h); padding: 0 24px 0 var(--pager-pad-x); border-radius: var(--radius-sm); color: var(--text-secondary); font-size: 13px; }
    .page-size .select-arrow { right: 8px; }

/* 描述列表：值区域 */
.desc-value { flex: 1; padding: 12px 16px; color: var(--text-primary); font-size: 14px; min-width: 0; word-break: break-word; }

/* 列表：额外操作区 */
.list-extra { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

/* 树：计数与文本截断 */
.tree-row > span:not(.tree-arrow):not(.tree-count):not(.tree-check):not(.tree-tag) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tree-children { animation: tree-in .15s ease; }
@keyframes tree-in { from { opacity: 0; } to { opacity: 1; } }
.tree-arrow.placeholder { visibility: hidden; }

/* 步骤条：可点击态 */
.steps .step-item { cursor: default; }

/* 进度：条形自定义色由内联 style 提供，此处仅补充过渡 */
.progress-bar { transition: width .4s cubic-bezier(.4, 0, .2, 1); }
.progress-ring .ring-value { transition: stroke-dashoffset .5s cubic-bezier(.4, 0, .2, 1); }

/* 统计卡片可点击态 */
.stat-card.clickable { cursor: default; }
.stat-card .stat-icon .icon { display: block; }

/* ============================================================
   8. 组件补充样式（Dropdown / Menu / Chart / 瀑布流等）
   ============================================================ */

/* 下拉菜单：头部与分组标题 */
.dropdown-header { padding: 8px 10px 10px; font-size: 12px; font-weight: 600; color: var(--text-muted); border-bottom: 1px solid var(--border-light); margin-bottom: 4px; }
.dropdown-divider { height: 1px; background: var(--border-light); margin: 4px 6px; }

/* 菜单项：快捷键与禁用态 */
.menu-shortcut { margin-left: auto; font-size: 11px; color: var(--text-muted); font-family: var(--font-mono); }
.menu-item.disabled { opacity: .45; pointer-events: none; }
.menu-item .icon.lead { width: 18px; text-align: center; flex-shrink: 0; }

/* 图表容器 */
.chart-box { width: 100%; }
.chart-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }

/* 表格行内操作：文本型按钮 */
.row-actions .link-btn { background: none; border: none; padding: 0; font: inherit; font-size: 13px; color: var(--primary-color); cursor: default; }
    .row-actions .link-btn:hover { text-decoration: underline; }
    .row-actions .link-btn.danger { color: var(--danger-color); }

/* 表格：排序图标对齐 */
.table-pro th .sort-ico { font-size: 9px; margin-left: 5px; color: var(--text-muted); vertical-align: middle; }
    .table-pro th.sorted .sort-ico { color: var(--primary-color); }

/* 分段控制器内的徽标 */
.segment-item .badge { margin-left: 2px; }

/* 标签页：卡片式的内边距统一走 .tabs-card > .tab-panel，这里不再单独收顶边距 */

/* ============================================================
   9. 页面区块组件（TablePage / ListPage / ChartPage / LeftRightPage / TopBottomPage / TabsPage）
   设计态与运行时渲染共用同一批组件，样式随 [data-theme] 自动切换。
   ============================================================ */
.pg-block { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: 14px; }
.pg-block-title { font-size: 13.5px; font-weight: 700; color: var(--text-primary); margin-bottom: 10px; display: flex; align-items: center; gap: 6px; }
.pg-block-title::before { content: ''; width: 3px; height: 14px; border-radius: 2px; background: var(--primary-color); }
.pg-empty { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 18px 12px; margin-bottom: 10px; border: 1.5px dashed var(--border-color); border-radius: var(--radius-sm); color: var(--text-muted); font-size: 12.5px; background: var(--bg-app); }
    .pg-empty .icon { color: var(--text-muted); }
.pg-skeleton-rows { display: flex; flex-direction: column; gap: 8px; }

/* 表格 */
.pg-grid { width: 100%; border-collapse: collapse; font-size: 13px; }
.pg-grid th, .pg-grid td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--border-light); color: var(--text-secondary); }
.pg-grid thead th { font-size: 12px; font-weight: 700; color: var(--text-muted); background: var(--bg-hover); }
.pg-grid tbody tr:last-child td { border-bottom: none; }
.pg-cell-sk { display: block; height: 12px; width: 80%; }
.pg-col-op { width: 96px; }
.pg-op { color: var(--primary-color); font-size: 12px; margin-right: 10px; }

/* 表格：查询条件栏 + 工具栏 */
.pg-filter { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 12px; margin-bottom: 10px; border: 1px solid var(--border-light); border-radius: var(--radius-sm); background: var(--bg-app); }
.pg-filter-fields { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: 1; min-width: 0; }
.pg-filter-item { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.pg-filter-label { color: var(--text-secondary); white-space: nowrap; }
.pg-filter-input { display: inline-flex; align-items: center; gap: 6px; min-width: 128px; height: 26px; padding: 0 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--primary-color); }
.pg-filter-ph { flex: 1; color: var(--text-muted); font-size: 12px; }
.pg-filter-ghost { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border: 1.5px dashed var(--border-color); border-radius: var(--radius-sm); color: var(--text-muted); font-size: 12px; background: color-mix(in srgb, var(--bg-app) 60%, transparent); }
.pg-filter-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.pg-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.pg-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pg-tools-right { display: flex; align-items: center; gap: 2px; }

/* 图表 */
.pg-chart { display: flex; flex-direction: column; }
.pg-chart-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.pg-chart-type { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
.pg-chart-type .icon { color: var(--primary-color); }
.pg-chart-legend { display: flex; gap: 14px; font-size: 12px; color: var(--text-secondary); }
.pg-chart-legend i, .pg-pie-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.pg-chart-body { display: flex; align-items: flex-end; gap: 14px; height: 160px; padding: 14px 14px 8px; border: 1px solid var(--border-light); border-radius: var(--radius-sm); background: var(--bg-app); }
.pg-chart-bar { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 6px; height: 100%; min-width: 0; }
.pg-chart-bar i { display: block; width: 62%; max-width: 34px; border-radius: 4px 4px 0 0; background: linear-gradient(180deg, var(--primary-color), color-mix(in srgb, var(--primary-color) 35%, transparent)); }
.pg-chart-bar span { font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.pg-chart-line .pg-chart-body, .pg-chart-body.pg-chart-line { align-items: stretch; }
.pg-line-svg { width: 100%; height: 100%; overflow: visible; }
.pg-chart-pie { align-items: center; justify-content: center; gap: 22px; flex-wrap: wrap; }
.pg-donut { width: 130px; height: 130px; border-radius: 50%; flex-shrink: 0; -webkit-mask: radial-gradient(circle, transparent 44%, #000 45%); mask: radial-gradient(circle, transparent 44%, #000 45%); }
.pg-pie-legend { display: flex; flex-direction: column; gap: 7px; font-size: 12px; color: var(--text-secondary); }
.pg-chart-axis { margin-top: 8px; font-size: 11px; color: var(--text-muted); text-align: right; }

/* Tab 布局（下划线高亮，页签按内容宽度靠左排列，不拉伸占满） */
.pg-tabs { display: flex; flex-direction: column; }
.pg-tabbar { display: flex; justify-content: flex-start; border-bottom: 1px solid var(--border-color); margin-bottom: 12px; }
.pg-tab {
    flex: 0 0 auto;
    padding: 9px 16px;
    border: none; border-bottom: 2px solid transparent;
    background: transparent;
    font-size: 12.5px; font-weight: 500; color: var(--text-muted);
    text-align: center; white-space: nowrap;
    cursor: pointer; user-select: none;
    transition: color var(--transition), border-color var(--transition);
}
.pg-tab:hover { color: var(--text-primary); }
.pg-tab.active { color: var(--primary-color); border-bottom-color: var(--primary-color); font-weight: 600; }
.pg-tab-pane { min-height: 40px; }

/* 列表 */
.pg-list-item { display: flex; align-items: center; gap: 12px; padding: 10px 2px; border-bottom: 1px solid var(--border-light); }
    .pg-list-item:last-child { border-bottom: none; }
.pg-list-thumb { width: 38px; height: 38px; border-radius: var(--radius-sm); flex-shrink: 0; }
.pg-list-lines { flex: 1; display: flex; flex-direction: column; gap: 7px; }

/* 布局容器 */
.pg-layout { display: flex; flex-direction: column; border: none; background: transparent; padding: 0; border-radius: 0; }
.pg-lr-row { display: flex; gap: 12px; flex: 1; min-height: 0; }
.pg-tb-col { display: flex; flex-direction: column; gap: 12px; flex: 1; min-height: 0; }
.pg-region { min-width: 0; min-height: 80px; display: flex; flex-direction: column; gap: 12px; }
.pg-slot { flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px; min-height: 80px; border: 1.5px dashed var(--border-color); border-radius: var(--radius-sm); color: var(--text-muted); font-size: 12.5px; background: color-mix(in srgb, var(--bg-app) 60%, transparent); }
.pg-unknown { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border: 1px solid var(--danger-color); border-radius: var(--radius-sm); color: var(--danger-color); font-size: 12.5px; background: var(--danger-soft); }

/* 栅格布局容器：子区块作为网格单元平铺，列数由内联 grid-template-columns 决定 */
.pg-grid-wrap { border: none; background: transparent; padding: 0; border-radius: 0; }
.pg-cells { display: grid; gap: 12px; grid-auto-rows: min-content; }

/* 运行时指标卡：一排统计卡片（总数 + 各数值列合计） */
.rt-stat-row { display: flex; flex-wrap: wrap; gap: 12px; }
.rt-stat { flex: 1 1 160px; min-width: 150px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-app); }
.rt-stat-ic { width: 38px; height: 38px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 10px; color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 12%, transparent); }
.rt-stat-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rt-stat-value { font-size: 22px; font-weight: 800; line-height: 1.1; color: var(--text-primary); }
.rt-stat-label { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 运行时主从「从」区表单块 */
.rt-formblock { display: flex; flex-direction: column; }
.rt-formfoot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border-light); }
.rt-empty { flex: 1; min-height: 120px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--text-muted); font-size: 13px; }
    .rt-empty .icon { color: var(--text-muted); }

/* 运行时「子表」数据块（一对多，随主记录选中联动） */
.rt-subtable { display: flex; flex-direction: column; gap: 4px; }
    .rt-subtable .rt-subtable-head { justify-content: space-between; }
    .rt-subtable .rt-empty { min-height: 90px; }

/* 运行时「未建模预览」骨架：页面优先流程下实体尚未建模时，开发期宿主按区块列渲染结构占位（外观贴近成品 ConsolePage） */
.rt-preview { border-style: dashed; background: color-mix(in srgb, var(--primary-color) 4%, var(--bg-card)); }
    .rt-preview .rt-preview-head {
        display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
        margin-bottom: 12px;
    }
    .rt-preview .rt-preview-titles { min-width: 0; }
    .rt-preview .rt-preview-title {
        display: flex; align-items: center; gap: 8px;
        font-size: 16px; font-weight: 600; color: var(--text-primary);
    }
    .rt-preview .rt-preview-desc {
        margin-top: 4px; font-size: 12.5px; color: var(--text-muted); line-height: 1.5;
    }
    .rt-preview .rt-preview-actions { display: flex; align-items: center; gap: 8px; flex: none; }
    .rt-preview .rt-preview-search {
        display: inline-flex; align-items: center; gap: 6px;
        padding: 4px 12px; border-radius: 999px; font-size: 12.5px; color: var(--text-muted);
        border: 1px solid var(--border-light); background: var(--bg-input, #f7f7f7);
    }
        .rt-preview .rt-preview-search .icon { opacity: .6; }
.rt-preview-badge {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 11.5px; font-weight: 600; color: var(--warning-color, #e08a00);
    background: color-mix(in srgb, var(--warning-color, #e08a00) 12%, transparent);
    border-radius: 999px; padding: 2px 9px;
}
.rt-preview-hint {
    display: flex; align-items: center; gap: 6px; margin-top: 10px;
    padding-top: 10px; border-top: 1px dashed var(--border-light);
    font-size: 12.5px; color: var(--text-muted);
}
    .rt-preview-hint .icon { flex: none; }

/* 运行时「内联可编辑」设置面板（固定行集，逐行就地编辑、整表一次保存） */
.rt-ed { display: flex; flex-direction: column; gap: 10px; }
    .rt-ed .rt-empty { min-height: 90px; }
.rt-ed-head {
    display: grid; gap: 12px; align-items: center;
    padding: 2px 2px 8px; border-bottom: 1px solid var(--border-light);
}
.rt-ed-th { font-size: 12px; font-weight: 600; color: var(--text-muted); letter-spacing: .02em; }
.rt-ed-row {
    display: grid; gap: 12px; align-items: center;
    padding: 9px 2px; border-bottom: 1px solid var(--border-light);
    transition: background .15s ease;
}
    .rt-ed-row:last-of-type { border-bottom: 0; }
    .rt-ed-row:hover { background: color-mix(in srgb, var(--primary-color) 4%, transparent); }
.rt-ed-cell { min-width: 0; }
.rt-ed-static { font-size: 13px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rt-ed-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--border-light); }

/* 折线图 x 轴标签行 */
.pg-chart-xlabels { display: flex; justify-content: space-between; margin-top: 6px; font-size: 11px; color: var(--text-muted); overflow: hidden; }
    .pg-chart-xlabels span { flex: 1; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 树：节点禁用 */
.tree-row.disabled { opacity: .45; pointer-events: none; }

/* 折叠面板：标签 */
.collapse-head .tag { margin-left: 6px; }

/* 后台门禁校验占位（AdminLayout 在 RequireAuth 且会话未就绪时显示）：品牌化启动屏，会话就绪前铺满视口居中呈现 */
.app-boot {
    position: fixed; inset: 0; z-index: 100;
    display: flex; align-items: center; justify-content: center;
    background:
        radial-gradient(120% 90% at 50% 34%, color-mix(in srgb, var(--primary-color) 10%, transparent) 0%, transparent 62%),
        var(--bg-app);
}
.boot-card { display: flex; flex-direction: column; align-items: center; text-align: center; animation: boot-in .55s cubic-bezier(.22,.61,.36,1) both; }
.boot-logo {
    width: 62px; height: 62px; box-sizing: border-box; object-fit: contain; border-radius: 16px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--bg-card);
    box-shadow: var(--shadow-md), 0 0 0 1px var(--border-light);
    animation: boot-float 2.8s ease-in-out infinite;
}
    .boot-logo--icon { color: var(--primary-color); }
    .boot-logo--text { font-size: 30px; font-weight: 800; color: var(--primary-color); }
.boot-title { margin-top: 18px; font-size: 18px; font-weight: 700; letter-spacing: .3px; color: var(--text-primary); }
/* 不确定态进度条：轨道用 border-light，一道柔光带自左向右循环扫过，比单纯转圈更像「在加载」 */
.boot-progress { position: relative; margin-top: 22px; width: 208px; height: 4px; border-radius: 999px; background: var(--border-light); overflow: hidden; }
    .boot-progress > i { position: absolute; top: 0; left: 0; height: 100%; width: 42%; border-radius: 999px; background: linear-gradient(90deg, transparent, var(--primary-color), transparent); animation: boot-slide 1.5s ease-in-out infinite; }
.boot-status { margin-top: 16px; display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-secondary); }
@keyframes boot-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes boot-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes boot-slide { 0% { transform: translateX(-110%); } 100% { transform: translateX(350%); } }
@media (prefers-reduced-motion: reduce) {
    .boot-card, .boot-logo, .boot-progress > i { animation: none; }
    .boot-progress > i { width: 100%; opacity: .45; }
}

/* ============================================================
   AppLayout · 移动端母版（顶部 App 栏 + 内容区 + 底部 Tab）
   ============================================================ */
.app-mobile {
    display: flex;
    flex-direction: column;
    height: 100%;
    max-width: 560px;
    margin: 0 auto;
    background: var(--bg-app);
    position: relative;
}

.app-bar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    height: 52px;
    padding: 0 16px;
    background: color-mix(in srgb, var(--bg-card) 88%, transparent);
    backdrop-filter: saturate(1.4) blur(8px);
    border-bottom: 1px solid var(--border-color);
}

.app-bar-lead {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
}

.app-bar-title {
    font-size: 16px;
    font-weight: 650;
    color: var(--text-primary);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-bar-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

.app-icon-btn {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background .16s ease, color .16s ease;
}
.app-icon-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
.app-icon-btn:active { transform: scale(.94); }

.app-main {
    flex: 1 1 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
}
.app-page { display: block; }

.app-tabbar {
    flex: 0 0 auto;
    display: flex;
    align-items: stretch;
    gap: 2px;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--bg-card) 92%, transparent);
    backdrop-filter: saturate(1.4) blur(8px);
    border-top: 1px solid var(--border-color);
}

.app-tab {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 6px 4px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1.2;
    cursor: pointer;
    transition: color .16s ease, background .16s ease;
}
.app-tab .app-tab-ico { display: inline-flex; align-items: center; justify-content: center; }
.app-tab .app-tab-txt { font-weight: 550; }
.app-tab:hover { color: var(--text-primary); }
.app-tab:active { background: var(--bg-hover); }
.app-tab.active { color: var(--primary-color); }
.app-tab.active .app-tab-txt { font-weight: 650; }

/* ---- 应用页常用积木 ---- */
.app-hero { display: flex; flex-direction: column; gap: 4px; margin-bottom: 16px; }
.app-hero-eyebrow { font-size: 12.5px; color: var(--text-muted); }
.app-hero-title { font-size: 22px; font-weight: 700; color: var(--text-primary); }

.app-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 18px; }
.app-stat {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}
.app-stat-num { font-size: 20px; font-weight: 700; color: var(--text-primary); }
.app-stat-label { font-size: 12px; color: var(--text-muted); }

/* 陈列行：标签 / 徽标等小元素自动换行 */
.app-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 18px; }


.app-section-title { font-size: 13px; font-weight: 650; color: var(--text-secondary); margin: 0 2px 10px; }

.app-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 18px; }
.app-grid-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 14px 6px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    font-size: 12px;
    text-align: center;
    cursor: pointer;
    transition: background .16s ease, color .16s ease, transform .12s ease;
}
.app-grid-item:hover { color: var(--primary-color); }
.app-grid-item:active { transform: scale(.96); background: var(--bg-hover); }
.app-grid-item .icon { color: var(--primary-color); }

.app-list { display: flex; flex-direction: column; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-md); overflow: hidden; margin-bottom: 18px; }
.app-list-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    color: var(--text-secondary);
    font-size: 14px;
    cursor: pointer;
    border-top: 1px solid var(--border-color);
}
.app-list-row:first-child { border-top: none; }
.app-list-row:hover { background: var(--bg-hover); }
.app-list-row .app-list-ico { color: var(--primary-color); flex-shrink: 0; }
.app-list-row .app-list-txt { flex: 1 1 auto; min-width: 0; }
/* 状态徽标是"一眼判定"的东西，被挤成两行就没法看了：宽度先让给正文，徽标自己不许缩。 */
.app-list-row .tag { flex-shrink: 0; white-space: nowrap; }
.app-list-row .app-list-chev { color: var(--text-muted); }

.app-profile {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 16px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    margin-bottom: 18px;
}
.app-profile-avatar {
    width: 52px; height: 52px; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--primary-color);
    color: #fff; font-size: 20px; font-weight: 700;
}
.app-profile-name { font-size: 17px; font-weight: 650; color: var(--text-primary); }
.app-profile-role { font-size: 12.5px; color: var(--text-muted); }

/* 卡片：详情 / 摘要等子页承载块 */
.app-card { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: 14px 16px; margin-bottom: 18px; }

/* ===== 控制台页脚手架 ConsolePage：页头 + 概览统计 + 数据卡片（工具栏固定、仅表格区滚动） ===== */
.cp-page { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; gap: 16px; }

/* 页头 */
.cp-page .cp-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.cp-page .cp-head h2 { font-size: 20px; font-weight: 600; letter-spacing: -.3px; margin: 0 0 4px; color: var(--text-primary); }
.cp-page .cp-head p { font-size: 13.5px; color: var(--text-muted); margin: 0; }
/* 右侧操作区贴右缘。单靠 .cp-head 的 space-between 只在「同一行」成立：
   标题 + 说明较宽时操作区会折到下一行，那一行只有它一个，space-between 便把它顶到左边，
   视觉上像是跑到了标题下面。margin-left:auto 让它无论同行还是折行都靠右。 */
.cp-page .cp-head-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; white-space: nowrap; flex-shrink: 0; }

/* 页头筛选条（ConsolePage.Filters / SearchInHeader）：查询条件集中在标题行下方的一整行，卡片里不再有工具栏行。
   三条要点：
   1. 直接子控件一律按内容宽度排。表单那套 .input/.select/.textarea{width:100%} 在这里必须失效——
      筛选条是横排 flex，width:100% 会让每个日期、每个下拉各占一整行，于是「一行条件」竖着堆成四五行，
      把表格往下压（业务页把条件放在卡片正文里时正是这个结果）。
   2. nowrap + 允许收缩：条件多时压缩控件，而不是折成第二行。折行会把「一行」重新变成竖排，
      而挤一挤至少还是同一行（下拉内的文字本来就有省略号）。
   3. 不设 overflow。日期面板、下拉列表都是绝对定位在控件内部的，滚动容器会沿两个轴一起裁剪，
      筛选条一旦 overflow:auto，日历弹层就会被切掉半截。 */
.cp-page .cp-head-filters { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; min-width: 0; }
.cp-page .cp-head-filters > * { flex: 0 1 auto; min-width: 0; }
/* 直连的日期控件：DateInput 的根节点本身就是 .select，比下拉更窄——日期只有 10 个字符 */
.cp-page .cp-head-filters > .select { width: auto; flex: 0 1 132px; min-width: 104px; max-width: 150px; }
/* 业务页放 Select 的槽位（.cp-filter-item）：与日期同宽，窄窗口下一起收缩 */
.cp-page .cp-head-filters > .cp-filter-item { flex: 0 1 150px; min-width: 104px; }
.cp-page .cp-head-filters > .cp-search { flex: 1 1 240px; min-width: 140px; max-width: 340px; width: auto; }
/* 条尾的「共 N 条」「口径：…」这类说明文字：贴右缘，不参与收缩 */
.cp-page .cp-head-filters > .cp-filter-tail { margin-left: auto; flex: 0 0 auto; white-space: nowrap; }
/* 窄窗口下先让位给控件。这些合计文字与表格分页里的合计是同一个数，
   而控件被压到看不清就没法用了——信息重复的一侧先退让。 */
@media (max-width: 1180px) {
    .cp-page .cp-head-filters > .cp-filter-tail { display: none; }
}
/* 与工具栏同一把尺子：按钮高度对齐 --ctl-h，一行里几种控件不参差 */
.cp-page .cp-head-filters .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg) { min-height: var(--ctl-h); }
/* 分段控件（审计日志的「类型 / 结果」）也进筛选条：不撑高的话 sm 尺寸比旁边搜索框矮一截，
   一行里看着像没对齐。拉伸填充 + 去掉上下 padding，与 .cp-toolbar 里的同款处理一致。 */
.cp-page .cp-head-filters .segmented { height: var(--ctl-h); align-items: stretch; flex: 0 0 auto; }
.cp-page .cp-head-filters .segmented .segment-item { padding-top: 0; padding-bottom: 0; }

/* 概览统计 */
.cp-page .cp-stats { display: flex; gap: 12px; flex-wrap: wrap; }
.cp-page .cp-stat {
    flex: 1 1 160px; min-width: 150px; display: flex; align-items: center; gap: 14px;
    background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-lg);
    padding: 14px 18px; box-shadow: var(--shadow-sm); transition: background var(--transition), border var(--transition);
}
.cp-page .cp-stat-ico { width: 42px; height: 42px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 19px; color: #fff; flex-shrink: 0; }
.cp-page .cp-stat-num { font-size: 22px; font-weight: 700; line-height: 1.1; color: var(--text-primary); }
.cp-page .cp-stat-label { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }
/* 紧凑统计胶囊：CompactStats 时统计不再占一排卡片，而是收进工具栏右侧，与在线用户页的状态胶囊同一视线高度。
   只读指标，不像状态胶囊那样可点，所以不给 cursor / hover。
   高度刻意对齐 .status-line.pill（3px 上下内距 + 19px 图标芯片 + 1px 边框 = 27px），两种胶囊并排时不会一高一低。 */
.cp-page .cp-stat-pill {
    display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px 3px 3px;
    border: 1px solid var(--border-color); border-radius: 999px;
    font-size: 13px; color: var(--text-secondary); white-space: nowrap;
}
.cp-page .cp-stat-pill-ico { width: 19px; height: 19px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 11px; color: #fff; flex-shrink: 0; }
.cp-page .cp-stat-pill b { color: var(--text-primary); }

/* 数据卡片 */
.cp-page .cp-card {
    display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-md); box-shadow: var(--shadow-sm); overflow: hidden;
    transition: background var(--transition), border var(--transition);
}

/* Cardless 页（正文自带若干张 Card 的看板/报表页）：解除上面那条「压在视口内」的约束。
   .cp-page 的 flex:1 1 auto + min-height:0 是为列表页设计的——它撑满可视高度，让溢出发生在
   .cp-card 内部的表格滚动区。Cardless 时外层卡片没了，若页面高度仍被钉在一屏，
   正文各卡片的总高就溢出到页框之外：外层滚动容器量不到这段溢出，于是滚不到底、
   底部也拿不到 .content-scroll 那 28px 内边距，最后一排卡片直接贴死在窗口下沿。
   改成自己撑高，滚动交回 .content-scroll，底部留白随之生效。 */
.cp-page.cp-cardless { flex: 0 0 auto; min-height: auto; }

/* 例外：有些 Cardless 页自带「左右分栏、各栏内滚」的双卡片布局（数据字典、组织架构），
   它们要的是列表页那套「撑满视口高度、溢出发生在卡片内部滚动区」，而非看板页的整页滚动。
   显式挂 .cp-fill 即把 .cp-page 的 flex:1 1 auto + min-height:0 找回来——两栏卡片随之等高、顶端对齐，
   某一栏内容超过页面高度时在其内部出滚动条，而不是把整页撑高。纯 opt-in，未加此类的看板/报表 Cardless 页不受影响。 */
.cp-page.cp-cardless.cp-fill { flex: 1 1 auto; min-height: 0; }

/* 工具栏（固定） */
.cp-page .cp-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; padding: 14px 18px; border-bottom: 1px solid var(--border-light); flex-shrink: 0; }
.cp-page .cp-toolbar .cp-toolbar-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.cp-page .cp-toolbar .cp-toolbar-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }

/* 工具栏里的过滤控件按内容排一行，不吃表单那套 width:100%。
   .input / .select / .textarea 在表单栅格里必须撑满一格，但工具栏是 flex-wrap 的横排：
   width:100% 的下拉会独占一整行——「搜索框 / 全部属性 / 含停用」于是竖着堆成三行，
   一行过滤条件被撑成三行高，还把表格往下压。这里只放宽 toolbar 的直接子控件，
   表单里的行为不受影响；需要更宽仍由业务页用 Style 覆盖（行内样式优先级更高）。
   DateInput / Cascader / SelectTable 的触发框都复用 .select 类，一并受益。 */
.cp-page .cp-toolbar-left > .select,
.cp-page .cp-toolbar-right > .select {
    width: auto;
    min-width: 132px;
    max-width: 240px;
    flex: 0 1 auto;
}

/* 工具栏气泡：整条工具栏贴着卡片上沿，而 .cp-card 是 overflow:hidden（要裁表格滚动区的圆角），
   默认「向上弹」的 ::after 会整个越出卡片上沿被切成一条黑边。这里统一改向下弹，落到表格区上方，
   与 .top-actions / .md-tools 同一套姿势。
   右侧另加贴边：状态圆点挨着卡片右缘，气泡比圆点宽约 140px，居中会甩出右边界再被裁一次。 */
.cp-page .cp-toolbar [data-tip]::after { bottom: auto; top: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.cp-page .cp-toolbar [data-tip]::before { bottom: auto; top: calc(100% + 4px); transform: translateX(-50%) translateY(-4px); border-top-color: transparent; border-bottom-color: var(--text-primary); }
.cp-page .cp-toolbar [data-tip]:hover::after,
.cp-page .cp-toolbar [data-tip]:hover::before { transform: translateX(-50%) translateY(0); }
/* 贴右缘时不能再带 translateX(-50%)：整条 transform 得重写一遍，只改 left/right 会把气泡甩回中间 */
.cp-page .cp-toolbar-right [data-tip]::after { left: auto; right: 0; transform: translateY(-4px); }
.cp-page .cp-toolbar-right [data-tip]::before { left: auto; right: 10px; transform: translateY(-4px); }
.cp-page .cp-toolbar-right [data-tip]:hover::after,
.cp-page .cp-toolbar-right [data-tip]:hover::before { transform: translateY(0); }

/* 内置搜索框。高度跟表单控件同一把尺子（--ctl-h）：写死 31px 时，一行里的下拉 39.2、按钮 ~34，
   三个控件三条基线，边框上下对不齐。 */
.cp-page .cp-search { display: flex; align-items: center; gap: 8px; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: 0 12px; height: var(--ctl-h); width: 320px; max-width: 100%; transition: all var(--transition); }
.cp-page .cp-search:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-soft); }
.cp-page .cp-search .icon { color: var(--text-muted); font-size: 14px; flex-shrink: 0; }
.cp-page .cp-search input { border: none; background: transparent; outline: none; color: var(--text-primary); font-family: inherit; font-size: 13.5px; flex: 1; min-width: 0; height: 100%; }
.cp-page .cp-search input::placeholder { color: var(--text-muted); }
.cp-page .cp-search-clear { background: transparent; border: none; color: var(--text-muted); cursor: pointer; font-size: 12px; padding: 2px; border-radius: 4px; display: flex; align-items: center; }
.cp-page .cp-search-clear:hover { color: var(--text-primary); background: var(--bg-hover); }

/* 工具栏一排控件的等高契约：统一到表单控件高度 --ctl-h。
   .btn 的高度由 padding 撑出来（约 34px），和搜索框、下拉（--ctl-h）差 5~8px，
   同一行里三种高度，边框上下沿参差、基线发虚。这里只补 min-height，内文靠 inline-flex 居中，
   不动 .btn-sm / .btn-xs / .btn-lg 这些显式尺寸档；高度取变量，切紧凑/宽松密度时一起变。 */
.cp-page .cp-toolbar .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg) { min-height: var(--ctl-h); }

/* 工具栏里的「非输入框」控件沿用同一把尺子：分段控制器与状态/统计胶囊。
   三者原生高度是各自 padding 撑出来的——分段器 30（3 内距 + 4 文字内距 + 14.4 行高 + 2 边框），
   状态胶囊 27、统计胶囊 27（3 内距 + 19 图标芯片 + 2 边框），和搜索框、下拉的 39.2 并排时
   上沿高高低低，三条边框线彼此错开。这里统一 height: var(--ctl-h) 收死：
   - 分段器不能只给外层高度，内块是 align-items:center 的定高块，会浮在中间留一圈空；
     所以内块改 stretch，并清掉上下 padding，让外层把剩余高度整块分给它。
   - 胶囊本来就是 inline-flex 居中，定高即可，图标芯片与文字自动居中。
   高度全部取变量，切紧凑（32）/ 宽松（46）时一起变；只作用于工具栏，表单与弹窗里的
   分段器、状态胶囊保持原尺寸。 */
.cp-page .cp-toolbar .segmented { height: var(--ctl-h); align-items: stretch; }
.cp-page .cp-toolbar .segmented .segment-item { padding-top: 0; padding-bottom: 0; }
.cp-page .cp-toolbar .status-line.pill,
.cp-page .cp-toolbar .cp-stat-pill { height: var(--ctl-h); }

/* 表格区：整张表只留「一个」滚动容器（框架 Table 的 .table-scroll），外层 .table-wrapper 只负责撑满剩余高度。
   原写法让 .table-wrapper 也 overflow:auto 并挂 scrollbar-gutter:stable —— 那道 gutter 会在表头右端
   恒定让出一条滚动条宽度的空白：竖向滚动条在时被条占住，不在时就是一条白条，看起来像表头缺了一块。
   同时双层滚动容器还会让 sticky 表头/钉列认错滚动参照，横向溢出时无法吸右。 */
.cp-page .cp-card > .table-wrapper {
    display: flex; flex-direction: column;
    flex: 1 1 auto; min-height: 0; overflow: hidden;
    border: none; border-radius: 0;
}
.cp-page .cp-card > .table-wrapper > .table-scroll { flex: 1 1 auto; min-height: 0; }
/* 卡片内的工具栏 / 批量条 / 空态等固定高度，只让表格滚动 */
.cp-page .cp-card > .table-wrapper > :not(.table-scroll) { flex: 0 0 auto; }
.cp-page .cp-card .table-pro thead th { position: sticky; top: 0; z-index: 5; }
/* 钉住列头必须高过同排普通表头：上式把所有表头都抬到 5，同层按 DOM 顺序绘制，
   横向滚动时后面的「客户/日期」等普通表头会从钉住的「单号」表头上碾过（只漏残字）。
   钉住列头抬到 6；行悬停抬层也是 6，但 tbody 在 thead 之后绘制，同层仍压住表头。 */
.cp-page .cp-card .table-pro thead th.stk-l,
.cp-page .cp-card .table-pro thead th.stk-r { z-index: 6; }
.cp-page .cp-card .table-pro th:last-child,
.cp-page .cp-card .table-pro td:last-child { text-align: right; }
.cp-page .cp-card .table-pro .row-actions { justify-content: flex-end; }

/* 分页固定在卡片底部，不随表格区滚动 */
.cp-page .cp-card > .table-pagination { flex-shrink: 0; border-top: 1px solid var(--border-light); }

.cp-page .cell-empty { color: var(--text-muted); opacity: .5; }

@media (max-width: 720px) {
    .cp-page .cp-stat { flex: 1 1 45%; }
}

/* ── ConsoleLog 实时日志控制台（反馈组件：经 signalr.js 流式显示后端 worker 运行日志） ── */
.log-console {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-card);
    overflow: hidden;
}

.log-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-color);
    background: var(--bg-hover);
}

.log-title { display: flex; align-items: center; gap: 8px; color: var(--text-primary); font-weight: 600; font-size: 13px; }
.log-title .icon { color: var(--primary-color); }
.log-name { white-space: nowrap; }

.log-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 12%, transparent);
}
.log-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); }
.log-status.status-connected { color: var(--success-color); background: var(--success-soft); }
.log-status.status-connected .log-dot { background: var(--success-color); animation: log-pulse 1.4s ease-in-out infinite; }
.log-status.status-connecting, .log-status.status-reconnecting { color: var(--info-color); background: var(--info-soft); }
.log-status.status-connecting .log-dot, .log-status.status-reconnecting .log-dot { background: var(--info-color); animation: log-pulse 1s ease-in-out infinite; }
.log-status.status-closed { color: var(--text-muted); }
.log-status.status-error { color: var(--danger-color); background: var(--danger-soft); }
.log-status.status-error .log-dot { background: var(--danger-color); }
@keyframes log-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.log-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.log-search { display: inline-flex; align-items: center; gap: 6px; padding: 0 8px; height: 28px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-muted); }
.log-search:focus-within { border-color: var(--primary-color); }
.log-input { border: none; outline: none; background: transparent; color: var(--text-primary); font-size: 12px; width: 130px; }
.log-input::placeholder { color: var(--text-muted); }

.log-select { height: 28px; padding: 0 6px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-secondary); font-size: 12px; outline: none; cursor: pointer; }
.log-select:focus { border-color: var(--primary-color); }

.log-auto { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-secondary); cursor: pointer; user-select: none; }
.log-auto input { accent-color: var(--primary-color); }

.log-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding: 0; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-card); color: var(--text-secondary); cursor: pointer; transition: .15s; }
.log-btn:hover { color: var(--primary-color); border-color: var(--primary-color); background: var(--primary-soft); }

.log-body { overflow: auto; padding: 8px 10px; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-size: 12px; line-height: 1.7; background: var(--bg-app); }
.log-empty { color: var(--text-muted); opacity: .6; font-style: italic; }

.log-line { display: flex; align-items: baseline; gap: 8px; white-space: pre-wrap; word-break: break-all; }
.log-line + .log-line { border-top: 1px dashed var(--border-color); }
.log-time { color: var(--text-muted); flex-shrink: 0; }
.log-lvl { flex-shrink: 0; width: 44px; font-weight: 700; }
.log-src { color: var(--text-secondary); flex-shrink: 0; padding: 0 6px; border-radius: 4px; background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
.log-text { color: var(--text-primary); }

.log-line.lv-debug .log-lvl { color: var(--text-muted); }
.log-line.lv-debug .log-text { color: var(--text-muted); }
.log-line.lv-info .log-lvl { color: var(--info-color); }
.log-line.lv-warn { background: var(--warning-soft); }
.log-line.lv-warn .log-lvl { color: var(--warning-color); }
.log-line.lv-error { background: var(--danger-soft); }
.log-line.lv-error .log-lvl { color: var(--danger-color); }
.log-line.lv-error .log-text { color: var(--danger-color); }

/* ── 运行时 Schema 驱动渲染（Known.Runtime）· 通用渲染页 .rt-* ──
   SchemaPage 动态装配的表单/钩子面板样式；控件本体复用框架 Input/Number/Switch/DateInput，此处仅排版。
   面板底色用 --bg-card（明暗主题各自取色），勿用硬编码白底。 */
/* 字段改用框架 Form/FormGroup 装配（必填星号走 .form-label.required::after 的红色）：
   .form-group 自带 margin-bottom:16px，与这里的 gap 叠加会翻倍，故交还给 gap 统一控制间距。 */
.rt-form { display: flex; flex-direction: column; gap: 14px; }
    .rt-form .form-group { margin-bottom: 0; }
.rt-hookpanel { margin-top: 16px; padding: 14px 16px; border: 1px solid var(--border-color); border-radius: 10px; background: var(--bg-card); }
.rt-hooktitle { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.rt-hooklist { margin: 0; padding-left: 18px; font-size: 13px; line-height: 1.9; color: var(--text-primary); }

/* 运行时表格 · 只读运维形态的单元格渲染与详情弹窗（schema-table render 提示驱动） */
.rt-cell-mono { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12.5px; }
.rt-cell-time { font-size: 12.5px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.rt-dot-wrap { display: inline-flex; align-items: center; gap: 7px; }
.rt-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.rt-dot-text { font-size: 12.5px; font-weight: 500; color: var(--text-primary); font-family: var(--font-mono, ui-monospace, monospace); }
.rt-detail { display: flex; flex-direction: column; gap: 12px; }
.rt-drow { display: flex; gap: 12px; font-size: 13px; line-height: 1.6; }
.rt-dk { width: 84px; flex-shrink: 0; color: var(--text-muted); }
.rt-dv { color: var(--text-primary); word-break: break-word; }
.rt-dv.rt-mono { font-family: var(--font-mono, ui-monospace, monospace); }
.rt-stack { background: var(--bg-input); border: 1px solid var(--border-light); border-radius: var(--radius-md); padding: 12px; font-size: 12px; line-height: 1.5; color: var(--text-secondary); white-space: pre-wrap; word-break: break-word; overflow: auto; max-height: 320px; font-family: var(--font-mono, ui-monospace, monospace); }

/* 运行时表格 · 通用复合单元格令牌（person / status / client / rel-time，render 提示驱动，跨页复用） */
.rt-person { display: flex; align-items: center; gap: 10px; }
.rt-avatar { width: 34px; height: 34px; border-radius: 9px; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 14px; color: #fff; flex-shrink: 0; }
.rt-person-meta { display: flex; flex-direction: column; min-width: 0; }
.rt-person-name { font-weight: 500; color: var(--text-primary); display: flex; align-items: center; gap: 6px; }
.rt-self { font-size: 10.5px; font-weight: 600; color: var(--primary-color); background: var(--primary-soft); border-radius: 5px; padding: 1px 6px; }
.rt-person-sub { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rt-client { display: flex; flex-direction: column; gap: 2px; line-height: 1.35; }
.rt-dev { font-size: 12.5px; color: var(--text-primary); }
.rt-br { font-size: 11.5px; color: var(--text-muted); }
.rt-rel { font-size: 12.5px; color: var(--text-muted); }
.rt-time { font-size: 12.5px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }

/* known-ai 生成弹窗（AiStudioDialog，模块/页面/表单/流程共用）· 状态横幅 + 下载引导 + 输入 + 预览 */
.ai-studio { display: flex; flex-direction: column; gap: 14px; }
    .ai-studio > .form-group { margin-bottom: 0; }
/* 模型配置条（AiModelBar）：弹窗里是完整版，抽屉发送框里是 tight 版 */
.ai-bar { display: flex; flex-direction: column; gap: 14px; }
    .ai-bar.tight { gap: 8px; }
        .ai-bar.tight .ai-modelbar { gap: 6px; }
        .ai-bar.tight .ai-model-select { flex: 1 1 auto; min-width: 0; }
        .ai-bar.tight .ai-dot { width: 8px; height: 8px; flex-shrink: 0; }
        .ai-bar.tight .ai-download { padding: 10px 12px; gap: 10px; }
    .ai-bar .ai-dot.on { background: #12b76a; box-shadow: 0 0 0 3px rgba(18, 183, 106, .16); }
    .ai-bar .ai-dot.warn { background: #f79009; box-shadow: 0 0 0 3px rgba(247, 144, 9, .16); }
    .ai-bar .ai-dot.off { background: #f04438; box-shadow: 0 0 0 3px rgba(240, 68, 56, .14); }
    .ai-bar .ai-dot.checking { background: #b0b3bb; animation: ai-pulse 1s ease-in-out infinite; }
.ai-status { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-radius: var(--radius-md, 8px); border: 1px solid var(--border-light, #e5e5e5); background: var(--bg-input, #fafafa); font-size: 13px; }
.ai-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; background: #b0b3bb; }
.ai-state { font-weight: 600; flex-shrink: 0; }
.ai-msg { color: var(--text-secondary); }
.ai-flex { flex: 1 1 auto; }
.ai-status.on { border-color: rgba(18,183,106,.35); background: rgba(18,183,106,.08); }
    .ai-status.on .ai-dot { background: #12b76a; box-shadow: 0 0 0 3px rgba(18,183,106,.16); }
    .ai-status.on .ai-state { color: #12b76a; }
.ai-status.warn { border-color: rgba(247,144,9,.35); background: rgba(247,144,9,.08); }
    .ai-status.warn .ai-dot { background: #f79009; box-shadow: 0 0 0 3px rgba(247,144,9,.16); }
    .ai-status.warn .ai-state { color: #f79009; }
.ai-status.off { border-color: rgba(240,68,56,.35); background: rgba(240,68,56,.07); }
    .ai-status.off .ai-dot { background: #f04438; box-shadow: 0 0 0 3px rgba(240,68,56,.14); }
    .ai-status.off .ai-state { color: #f04438; }
.ai-status.checking .ai-dot { background: #b0b3bb; animation: ai-pulse 1s ease-in-out infinite; }
    .ai-status.checking .ai-state { color: var(--text-muted); }
@keyframes ai-pulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }
.ai-dl-btn { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; padding: 7px 14px; border-radius: var(--radius-md, 8px); background: var(--primary-color, #7c3aed); color: #fff; font-size: 12.5px; font-weight: 600; text-decoration: none; white-space: nowrap; transition: box-shadow .15s ease; }
    /* 悬停提亮用「内阴影叠白」而不是 filter: brightness()：filter 会给按钮造一个层叠上下文，
       把 data-tip 气泡的 z-index 困在按钮内部（按钮自身 z-index:auto），于是气泡被选择器下拉（.select-list，z-index:400）整个盖住。 */
    .ai-dl-btn:hover { box-shadow: inset 0 0 0 100px rgba(255, 255, 255, .08); text-decoration: none; color: #fff; }
/* 模型条右端的 known-ai 下载按钮：说明较长，让 data-tip 浮层换行成块、朝下并右对齐——
   上方是弹窗标题会被裁、按钮又贴着模型条右缘，居中上弹会顶出右边界，故改成向下、以右缘为锚。 */
.ai-dl-btn[data-tip]::after {
    white-space: normal; width: max-content; max-width: 260px; text-align: left; line-height: 1.5;
    left: auto; right: 0; bottom: auto; top: calc(100% + 9px); transform: translateY(-4px);
    z-index: 500;
}
    .ai-dl-btn[data-tip]::before {
        left: auto; right: 14px; bottom: auto; top: calc(100% + 4px); transform: translateY(-4px);
        border-top-color: transparent; border-bottom-color: var(--text-primary);
        z-index: 500;
    }
    .ai-dl-btn[data-tip]:hover::after, .ai-dl-btn[data-tip]:hover::before { transform: translateY(0); }
.ai-field { display: flex; flex-direction: column; gap: 6px; }
.ai-label { font-size: 13px; font-weight: 500; color: var(--text-secondary); }
.ai-actions { display: flex; align-items: center; gap: 12px; }
.ai-hint { font-size: 12px; color: var(--text-secondary); }
.ai-preview { margin-top: 2px; max-height: 320px; overflow: auto; }

/* AiModelBar「模型管理」主从弹窗：known-ai 不内置任何模型，用户就地登记 / 编辑本机 CPU .gguf 或 OpenAI 兼容在线服务（都经 known-ai 网关发现与调用） */
/* 模型条整体抬到后续表单字段之上：右端下载按钮的 data-tip 浮层朝下展开，若不抬高层级会被下方「系统名称」等字段盖住（遮挡）。 */
.ai-modelbar { display: flex; align-items: center; gap: 8px; position: relative; z-index: 30; }
/* 芯片不参与收缩、文字不换行：条内挤时「模型」二字曾被竖排折成两行 */
.ai-model-chip { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; white-space: nowrap; padding: 5px 11px; border-radius: 999px; font-size: 12.5px; font-weight: 600; color: var(--primary-color, #2F5BFF); background: var(--primary-soft, rgba(47,91,255,.10)); }
.ai-model-select { flex: 0 1 260px; min-width: 150px; }
/* 「模型管理」主从弹窗：左列可滚动清单（点选 / 顶部新增）+ 底部沉底的补充说明与 models 目录（可自定义、可打开），右列表单（FormGroup） */
.ai-mgr { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 20px; align-items: stretch; }
.ai-mgr-side { display: flex; flex-direction: column; gap: 10px; min-width: 0; padding-right: 20px; border-right: 1px solid var(--border-light, #e5e5e5); }
.ai-mgr-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ai-mgr-list { display: flex; flex-direction: column; gap: 4px; max-height: 360px; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.ai-mgr-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; border-radius: var(--radius-md, 8px); border: 1px solid transparent; background: transparent; cursor: pointer; text-align: left; color: var(--text-primary); transition: background .15s, border-color .15s; }
.ai-mgr-item:hover { background: var(--bg-input, #fafafa); }
.ai-mgr-item.active { background: var(--primary-soft, rgba(47,91,255,.10)); border-color: var(--primary-color, #2F5BFF); }
.ai-mgr-item .icon { flex-shrink: 0; color: var(--text-muted); }
.ai-mgr-item.active > .icon { color: var(--primary-color, #2F5BFF); }
.ai-mgr-name { flex: 1 1 auto; min-width: 0; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-mgr-empty { font-size: 12.5px; color: var(--text-muted); line-height: 1.55; padding: 2px 2px 0; }
/* 左列底部整块：补充说明 + models 目录。margin-top:auto 把它压到左列最底，与右列表单底边齐平——清单短时不留大片空洞，清单长时（≤360px 滚动区）也不会被顶出可视范围。 */
.ai-mgr-foot { display: flex; flex-direction: column; gap: 8px; margin-top: auto; padding-top: 12px; border-top: 1px dashed var(--border-light, #e5e5e5); }
/* known-ai models 目录落点：左列仅 260px，标题行（标签 + 打开/更改）与路径分两行——原来挤一行，长路径被省略号截成只剩盘符。
   路径整行折行显示（break-all + 不限行），配合 data-tip 悬停看全称；更改时就地展开 Input 占满整行。 */
.ai-mgr-dir { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.ai-mgr-dir-head { display: flex; align-items: center; gap: 2px; min-width: 0; }
.ai-mgr-dir-cap { flex: 1 1 auto; min-width: 0; font-size: 12px; color: var(--text-secondary); }
.ai-mgr-dir-head .btn { flex-shrink: 0; }
.ai-mgr-dir-path { min-width: 0; }
.ai-mgr-dir-path .input-affix { width: 100%; box-sizing: border-box; }
.ai-mgr-dir-path .input-affix input { min-width: 0; }
/* 窄列里的路径输入框收成紧凑档：左列仅 260px，14px 字号下长路径只剩二十来字可见，降到 12px 能多读一截 */
.ai-mgr-dir-path .input { font-size: 12px; padding: 6px 10px; }
.ai-mgr-dir-text { display: block; min-width: 0; font-size: 11.5px; line-height: 1.45; color: var(--text-muted); overflow-wrap: anywhere; word-break: normal; white-space: normal; cursor: default; }
.ai-mgr-main { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.ai-mgr-main .form-group { margin-bottom: 0; }
/* 编辑态只读的类型说明（本机 / 在线不可互改）：一个小胶囊替代被隐藏的切换钮 */
.ai-kind-static { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px; font-size: 12.5px; color: var(--text-secondary); background: var(--bg-input, #fafafa); border: 1px solid var(--border-light, #e5e5e5); }
/* 「本机」徽标：编辑器可管理清单里给本机 CPU 模型打的软色小药丸，跟在线模型区分开 */
.ai-model-tag { flex-shrink: 0; display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 999px; font-size: 11px; font-weight: 500; line-height: 1.6; color: var(--primary-color, #2F5BFF); background: var(--primary-soft, rgba(47,91,255,.10)); }
.ai-provider-note { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-md, 8px); background: var(--bg-input, #fafafa); border: 1px solid var(--border-light, #e5e5e5); font-size: 12px; line-height: 1.55; color: var(--text-secondary); }
.ai-provider-note .icon { flex-shrink: 0; margin-top: 2px; color: var(--text-muted); }
.ai-provider-msg { padding: 9px 12px; border-radius: var(--radius-md, 8px); font-size: 12.5px; line-height: 1.5; }
.ai-provider-msg.ok { color: #12b76a; background: rgba(18,183,106,.08); border: 1px solid rgba(18,183,106,.3); }
.ai-provider-msg.err { color: #f04438; background: rgba(240,68,56,.07); border: 1px solid rgba(240,68,56,.3); }

/* 运行时页面 SchemaPage 开发期设计入口：右下角浮动入口 + 空态动作 + AI 草稿预览 + 探针加载（设计态直接复用 StudioHost 单栏工具条，与建模管理同款） */
/* 右下角浮动设计入口（AI 生成 / 进入设计器）：悬停展开菜单，不占正文篇幅 */
.rt-fab { position: fixed; right: 28px; bottom: 28px; z-index: 900; display: flex; flex-direction: column; align-items: flex-end; gap: 12px; }
.rt-fab-btn { width: 52px; height: 52px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; color: #fff; background: var(--primary-color, #7c3aed); box-shadow: var(--shadow-md, 0 6px 18px rgba(124,58,237,.35)); transition: transform .18s ease, box-shadow .18s ease; }
    .rt-fab:hover .rt-fab-btn { transform: translateY(-2px); }
    .rt-fab-btn.on { box-shadow: 0 0 0 3px var(--primary-soft, rgba(124,58,237,.25)), var(--shadow-md); }
.rt-fab-menu { display: flex; flex-direction: column; align-items: stretch; gap: 4px; padding: 8px; border-radius: var(--radius-lg, 12px); border: 1px solid var(--border-light, #e5e5e5); background: var(--bg-card, #fff); box-shadow: var(--shadow-lg, 0 10px 30px rgba(0,0,0,.12)); opacity: 0; visibility: hidden; transform: translateY(8px) scale(.98); transform-origin: bottom right; transition: opacity .18s ease, transform .18s ease, visibility .18s; pointer-events: none; }
    .rt-fab:hover .rt-fab-menu { opacity: 1; visibility: visible; transform: translateY(0) scale(1); pointer-events: auto; }
.rt-fab-flag { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px 6px; font-size: 12px; color: var(--primary-color, #7c3aed); border-bottom: 1px solid var(--border-light, #eee); margin-bottom: 2px; }
.rt-fab-item { display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px; border: none; border-radius: var(--radius-md, 8px); background: transparent; color: var(--text-primary); font-size: 13px; font-weight: 500; cursor: pointer; white-space: nowrap; text-align: left; transition: background .15s ease; }
    .rt-fab-item:hover { background: var(--bg-input, #f5f5f5); }
.rt-empty-actions { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 14px; }
.rt-draft-json { max-height: 260px; overflow: auto; margin: 0; padding: 10px 12px; border-radius: var(--radius-md, 8px); border: 1px solid var(--border-light, #e5e5e5); background: var(--bg-input, #fafafa); font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.5; color: var(--text-primary); white-space: pre-wrap; word-break: break-all; }
.rt-hint { font-size: 12px; line-height: 1.6; color: var(--text-muted); margin-top: 8px; }
.rt-probe { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 40px 0; font-size: 13px; color: var(--text-muted); }

/* ── 「生成建模」确认弹窗：页面区块反推的表结构，逐字段核对类型 / 必填后落库建表 ── */
.md-wrap { display: flex; flex-direction: column; gap: 14px; }
.md-tip { font-size: 12.5px; line-height: 1.6; color: var(--text-muted); }
.md-table { border: 1px solid var(--border-light, #e5e5e5); border-radius: var(--radius-lg, 12px); background: var(--bg-card, #fff); overflow: hidden; }
.md-table-head { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--bg-input, #fafafa); border-bottom: 1px solid var(--border-light, #eee); }
    .md-table-head .md-name { flex: 0 0 180px; }
    .md-table-head .md-display { flex: 1 1 160px; }
.md-badge { flex: none; font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 999px; white-space: nowrap; }
    .md-badge.main { color: var(--primary-color, #7c3aed); background: var(--primary-soft, rgba(124,58,237,.12)); }
    .md-badge.child { color: #0f766e; background: rgba(15,118,110,.12); }
.md-parent { font-size: 12px; color: var(--text-secondary); white-space: nowrap; }
.md-fields { padding: 6px 12px 12px; }
.md-row { display: grid; grid-template-columns: 1.2fr 1.2fr 150px 72px 40px; align-items: center; gap: 10px; padding: 4px 0; }
.md-row-head { padding: 6px 0 4px; font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.md-c-req { display: flex; justify-content: center; }
.md-c-act { display: flex; justify-content: center; }
.md-add-field, .md-add-table { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; padding: 7px 14px; border: 1px dashed var(--border, #d4d4d4); border-radius: var(--radius-md, 8px); background: transparent; color: var(--text-secondary); font-size: 12.5px; cursor: pointer; transition: border-color .15s ease, color .15s ease; }
    .md-add-field:hover, .md-add-table:hover { border-color: var(--primary-color, #7c3aed); color: var(--primary-color, #7c3aed); }
.md-add-table { align-self: flex-start; }

/* ── 顶栏智能体抽屉：消息流 + 技能条 + 上下文条 + 带附件的发送框 ──
   抽屉主体改成不滚动的三段纵向布局：只有消息流滚动，技能/上下文与发送框钉在底部，
   否则长对话会把输入条顶出可视区，用户得先滚到最底才能提问。 */
.drawer.agent-drawer .drawer-body { padding: 0; display: flex; flex-direction: column; overflow: hidden; }

/* 底边多留一截：最后一条气泡的悬停操作行浮在它下面，不留就会被滚动容器裁掉。 */
.agent-log { flex: 1; min-height: 0; overflow-y: auto; padding: 18px 18px 30px; display: flex; flex-direction: column; gap: 14px; }
    /* 模型回复多为多行纯文本，气泡默认折叠换行会把它挤成一行 */
    .agent-log .bb-text { white-space: pre-wrap; word-break: break-word; }
.agent-msg-error .bb-content { color: var(--danger-color, #d4380d); }
    .agent-msg-error.bb-outlined { border-color: color-mix(in srgb, var(--danger-color, #d4380d) 45%, transparent); }

.agent-intro { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; }
.agent-intro-icon { width: 54px; height: 54px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--primary-color); background: var(--primary-soft); }
.agent-intro-title { font-size: 15px; font-weight: 600; color: var(--text-primary); }
.agent-intro-desc { font-size: 12.5px; line-height: 1.6; color: var(--text-muted); max-width: 300px; }

.agent-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.agent-tag { display: inline-flex; align-items: center; gap: 4px; max-width: 220px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border-light, #e5e5e5); background: var(--bg-input, #fafafa); font-size: 11.5px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 宿主技能交回的卡片：排在正文之下、标签之上，几张就竖着排几张。
   气泡本身是浅底，卡片要更实一点才分得开「模型说的话」和「系统查出来的数据」；
   Card 与 Descriptions 原本是给整页用的宽松尺寸，这里一律收到气泡里能看的密度。 */
.agent-cards { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.agent-card { padding: 10px 12px; border-radius: var(--radius-md); box-shadow: none; }
    .agent-card .card-header { margin-bottom: 6px; gap: 8px; font-size: 13px; }
.agent-card-summary { font-size: 12px; line-height: 1.6; color: var(--text-muted); }
.agent-card-fields { margin-top: 6px; }
    .agent-card .desc-label { width: 88px; padding: 6px 10px; font-size: 12px; }
    .agent-card .desc-value { padding: 6px 10px; font-size: 12.5px; }
.agent-card-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.agent-card-tag { display: inline-flex; align-items: center; gap: 4px; max-width: 180px; padding: 1px 7px; border-radius: 999px; background: var(--primary-soft); color: var(--primary-color); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 三样都空的卡片只留这一句：画一个空盒子会让人以为查询失败了。 */
.agent-card.blank { padding: 6px 10px; border-radius: var(--radius-sm); background: var(--bg-input, #fafafa); font-size: 12px; color: var(--text-muted); }

.agent-rail { flex-shrink: 0; display: flex; flex-direction: column; gap: 6px; padding: 10px 16px 2px; border-top: 1px solid var(--border-color); }
.agent-row { display: flex; align-items: flex-start; gap: 10px; }
.agent-label { display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; padding: 4px 0; border: none; background: none; font-size: 12px; font-weight: 600; color: var(--text-secondary); }
button.agent-label { cursor: pointer; }
    button.agent-label:hover { color: var(--primary-color); }
.agent-count { min-width: 17px; padding: 0 5px; border-radius: 999px; background: var(--bg-hover); color: var(--text-muted); font-size: 11px; font-weight: 600; text-align: center; }

.agent-chips { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.agent-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-secondary); font-size: 12px; transition: all var(--transition); }
button.agent-chip { cursor: pointer; }
    button.agent-chip:hover { border-color: var(--primary-color); color: var(--primary-color); }
.agent-chip.on { border-color: var(--primary-color); background: var(--primary-soft); color: var(--primary-color); font-weight: 600; }
.agent-chip.ctx { background: var(--bg-input, #fafafa); }
.agent-chip-text { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agent-chip-x { display: inline-flex; align-items: center; justify-content: center; padding: 0; border: none; background: none; color: var(--text-muted); cursor: pointer; }
    .agent-chip-x:hover { color: var(--danger-color, #d4380d); }
.agent-none { padding: 4px 0; font-size: 12px; color: var(--text-muted); }

/* 底边留得比两侧窄：这一条是抽屉里唯一贴着窗口下缘的控件，再高就成了没人用的空白。 */
.agent-sender { flex-shrink: 0; margin: 8px 16px 12px; }
.agent-tool { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 10px; border: 1px solid transparent; background: none; color: var(--text-muted); transition: all var(--transition); }
    .agent-tool:hover { color: var(--primary-color); background: var(--primary-soft); }
    .agent-tool:disabled { opacity: .45; cursor: not-allowed; }
    /* 附件按钮是 label[for]，直接唤起原生 file input，无需 JS 也保持键盘可达 */
    label.agent-tool { cursor: pointer; }

/* 发送框右下角：迷你模型选择 + 麦克风。无边框、只占一行文字高，长模型名靠省略号截断，不让它挤掉发送按钮。 */
.agent-sender .sd-actions { flex-shrink: 1; min-width: 0; }
/* 模型选择、技能选择与会话选择共用这套触发键：三者都贴在窗口底边的工具条里、都朝上开、都要截断长名字。
   拆成三份近似规则的话，改一处忘另一处几乎是必然的。 */
.ai-model-mini, .agent-skill, .agent-sessions { min-width: 0; }
    .ai-model-mini .ai-pick, .agent-skill .ai-pick, .agent-sessions .ai-pick { display: inline-flex; align-items: center; gap: 5px; max-width: 168px; height: 26px; padding: 0 6px; border: none; border-radius: 8px; background: none; color: var(--text-secondary); font-size: 12.5px; cursor: pointer; transition: all var(--transition); }
        .ai-model-mini .ai-pick:hover, .agent-skill .ai-pick:hover, .agent-sessions .ai-pick:hover { color: var(--primary-color); background: var(--primary-soft); }
    /* 选中技能后触发键保持点亮：它现在表示「本轮会带上这条指令」，和 hover 态不是一个意思。 */
    .agent-skill .ai-pick.on { color: var(--primary-color); background: var(--primary-soft); font-weight: 600; }
    .ai-model-mini .ai-pick-label, .agent-skill .ai-pick-label, .agent-sessions .ai-pick-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .ai-model-mini .ai-pick-arrow, .agent-skill .ai-pick-arrow, .agent-sessions .ai-pick-arrow { flex-shrink: 0; color: var(--text-muted); transition: transform var(--transition); }
    .ai-model-mini .dropdown-menu { min-width: 208px; }
    .ai-model-mini.open .ai-pick-arrow, .agent-skill.open .ai-pick-arrow, .agent-sessions.open .ai-pick-arrow { transform: rotate(180deg); }
    /* 技能触发键紧贴抽屉左缘：气泡默认居中，左半边会溢出抽屉、被 .drawer-body 的 overflow 裁掉（同样理由，
       上面技能弹层也是左对齐）。改成与触发键左对齐、向右展开；hover 态要一并覆盖，否则又会套回 translateX(-50%)。
       箭头 ::before 必须同步左移：它默认 left:50%，落在触发键中线上；芯片/tab 比气泡宽时（ctx 值常常只有 "/" 一两个字符），
       中线已经跑到气泡右外侧，只剩一颗孤零零的三角浮在旁边。 */
    .agent-skill .ai-pick[data-tip]::after { left: 0; transform: translateY(4px); }
    .agent-skill .ai-pick[data-tip]::before { left: 10px; transform: translateY(4px); }
    .agent-skill .ai-pick[data-tip]:hover::after,
    .agent-skill .ai-pick[data-tip]:hover::before { transform: translateY(0); }
    /* 上下文 chips 同样贴着抽屉左缘（右侧是长 URL / 长标题，居中必被裁一半），一并左对齐，箭头同上 */
    .agent-chip[data-tip]::after { left: 0; transform: translateY(4px); }
    .agent-chip[data-tip]::before { left: 10px; transform: translateY(4px); }
    .agent-chip[data-tip]:hover::after,
    .agent-chip[data-tip]:hover::before { transform: translateY(0); }

/* 技能弹层：一行过滤框 + 图标 / 名称 / 说明的清单。
   Suggestion 自带面板外框（它是给页面上独立浮层用的），嵌进 dropdown-menu 时把框交还给浮层，只留清单本身。 */
/* 弹层贴着触发键左对齐、朝上开。抽屉只有 640px、窄屏还要收到 92vw，触发键又紧贴抽屉左缘，
   所以宽度必须同时按视口夹一道：手机满屏抽屉时 88px 是「弹层左缘到视口右缘之间必然空出来的那段」，
   再多给一像素都会被 .drawer-body 的 overflow 裁掉半行技能名。（别改走 Portal：known.popup 把浮层钉在 z-index 2000，
   抽屉是 2900、遮罩 2800，传送到 body 只会让它整块沉到抽屉背后。） */
.agent-skill .dropdown-menu { min-width: min(268px, calc(100vw - 88px)); max-width: min(328px, calc(100vw - 88px)); }
.agent-skill-filter { box-sizing: border-box; width: 100%; margin-bottom: 6px; padding: 6px 9px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: var(--bg-input, #fafafa); color: var(--text-primary); font: inherit; font-size: 12.5px; outline: none; }
    .agent-skill-filter:focus { border-color: var(--primary-color); background: var(--bg-card); }
.agent-skill-list { min-width: 0; max-width: none; padding: 0; border: none; border-radius: 0; background: none; box-shadow: none; }
    .agent-skill-list .sg-list { max-height: 268px; }
/* 清除技能：只在选中时出现的收尾一行，上面用分隔线跟清单断开。不是危险操作，配色跟着技能行走。 */
.agent-skill-clear { display: flex; align-items: center; gap: 8px; width: 100%; margin-top: 4px; padding: 7px 8px; border: none; border-top: 1px solid var(--border-color); border-radius: 0 0 var(--radius-sm) var(--radius-sm); background: none; color: var(--text-secondary); font: inherit; font-size: 12.5px; cursor: pointer; transition: background var(--transition); }
    .agent-skill-clear:hover { background: var(--bg-hover); color: var(--primary-color); }
/* 会话弹层：Conversations 自带面板外框（它是给页面上独立浮层用的），嵌进 dropdown-menu 时把框交还给浮层，只留清单本身。
   宽度跟着技能弹层那道夹法：抽屉 640px、窄屏收到 92vw，超出一律被 .drawer-body 的 overflow 裁掉。
   用 > 只夹会话这一层弹层——清单每行还套着一个「更多操作」子弹层，跟着长到 300px 就横跨整块弹层、右边全裁掉。 */
.agent-sessions > .dropdown-menu { min-width: min(300px, calc(100vw - 88px)); max-width: min(360px, calc(100vw - 88px)); }
.agent-sessions .conversations { padding: 0; border: none; border-radius: 0; background: none; }
    /* 长会话一多就得滚，但滚动容器是清单而不是整块弹层：底部那行删除确认与顶部的失败回执要一直看得见。 */
    .agent-sessions .cv-list { max-height: 268px; overflow-y: auto; }
/* 清单里的子弹层走传送门（Dropdown.Portal）跳出去，关闭态脱流已统一到「关闭态浮层统一脱流」清单。 */
/* 存储侧的回执：读取或写入失败都写在这里、不动气泡——答案已经送到用户眼前了，别再拿红色标一条成功的回答。 */
.agent-store-error { margin-bottom: 6px; padding: 6px 8px; border-radius: var(--radius-sm); background: var(--danger-soft); color: var(--danger-color); font-size: 12px; line-height: 1.45; }
/* 删除确认行：会话列表里唯一的不可逆动作。点一次「⋯ → 删除」只是把它标出来，这一行按下去才真的动手。 */
.agent-store-confirm { display: flex; align-items: center; gap: 8px; margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border-color); font-size: 12px; color: var(--text-secondary); }
    .agent-store-confirm span { flex: 1; min-width: 0; }
.agent-confirm-no, .agent-confirm-ok { flex-shrink: 0; padding: 3px 9px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: none; color: var(--text-secondary); font: inherit; font-size: 12px; cursor: pointer; transition: all var(--transition); }
    .agent-confirm-no:hover { background: var(--bg-hover); }
.agent-confirm-ok { border-color: var(--danger-color); color: var(--danger-color); }
    .agent-confirm-ok:hover { background: var(--danger-color); color: #fff; }
.sd-voice { flex-shrink: 0; }
    /* 聆听中：底色点亮 + 呼吸圈，让人知道它在听，而不是卡住了 */
    .sd-voice.on { color: var(--primary-color); background: var(--primary-soft); animation: agent-voice 1.5s ease-in-out infinite; }
@keyframes agent-voice { 0%, 100% { box-shadow: 0 0 0 0 var(--primary-soft); } 60% { box-shadow: 0 0 0 6px transparent; } }
.agent-voice-live { display: inline-flex; align-items: center; gap: 5px; color: var(--primary-color); }
