/* ============================================================
   统一组件层（UI Kit）
   布丁跨境数智平台 · 前端 UI 统一

   依赖：theme-tokens.css（设计令牌）+ theme-components.css（Bootstrap 组件覆盖）
   加载位置：theme-components.css 与 ui-feedback.css 之后（见 templates/base.html 尾部），
             即全站最后一个公共样式层，页面内联 <style> 里与本文件同名的组件样式会被本文件接管。

   本文件职责（两件事）：
     1) 补齐 theme-components.css 尚未覆盖的 Bootstrap 组件
        （info/outline 按钮、表单校验态、switch、表格变体、alert 变体、
          进度条、spinner、tooltip/popover、模态与下拉的细节、骨架屏）
     2) 收敛项目内重复度最高的「自研组件」，给出唯一规范实现
        （状态标签、空状态、表格加载、统计/功能卡、上传区、多选标签、步骤条…）

   命名约定：
     - 复刻项目既有类名（.status-badge / .empty-state / .table-loading /
       .feature-card / .upload-zone / .tag-select / .step-indicator …），
       后续接入时可直接覆盖各页面的重复内联样式。
     - 组件层语义色一律由 --app-* 令牌派生，不引入新的硬编码色值。
   ============================================================ */

:root {
    /* 组件层语义色（--ui-*-soft / -line / -ink）已上收至 theme-tokens.css，
       使不加载本文件的页面（工具平台等）也能获得同一套语义色 */

    /* ---------------- 组件几何 ---------------- */
    --ui-line: 1px solid var(--app-border);
}

/* ============================================================
   一、Bootstrap 组件补漏
   ============================================================ */

/* ---------- 1.1 描边按钮（主题层只做了 outline-primary） ---------- */
.btn-outline-success {
    --bs-btn-color: var(--app-success);
    --bs-btn-border-color: rgba(var(--app-success-rgb), .45);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--app-success);
    --bs-btn-hover-border-color: var(--app-success);
    --bs-btn-active-bg: color-mix(in srgb, var(--app-success) 88%, #000);
    --bs-btn-active-border-color: var(--app-success);
    --bs-btn-focus-shadow-rgb: var(--app-success-rgb);
    --bs-btn-disabled-color: var(--app-success);
}

.btn-outline-warning {
    --bs-btn-color: var(--ui-warning-ink);
    --bs-btn-border-color: rgba(var(--app-warning-rgb), .5);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--app-warning);
    --bs-btn-hover-border-color: var(--app-warning);
    --bs-btn-active-bg: color-mix(in srgb, var(--app-warning) 88%, #000);
    --bs-btn-active-border-color: var(--app-warning);
    --bs-btn-focus-shadow-rgb: var(--app-warning-rgb);
}

.btn-outline-danger {
    --bs-btn-color: var(--app-danger-solid);
    --bs-btn-border-color: rgba(var(--app-danger-rgb), .45);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--app-danger-solid);
    --bs-btn-hover-border-color: var(--app-danger-solid);
    --bs-btn-active-bg: var(--app-danger-solid-hover);
    --bs-btn-active-border-color: var(--app-danger-solid-hover);
    --bs-btn-focus-shadow-rgb: var(--app-danger-rgb);
}

.btn-outline-info {
    --bs-btn-color: var(--app-primary);
    --bs-btn-border-color: rgba(var(--app-primary-rgb), .4);
    --bs-btn-hover-color: var(--app-primary);
    --bs-btn-hover-bg: var(--app-primary-soft);
    --bs-btn-hover-border-color: var(--app-primary);
    --bs-btn-active-bg: var(--app-primary-soft-hover);
    --bs-btn-focus-shadow-rgb: var(--app-primary-rgb);
}

/* ---------- 1.2 危险实心按钮（补齐 hover/active） ---------- */
.btn-danger {
    --bs-btn-bg: var(--app-danger-solid);
    --bs-btn-border-color: var(--app-danger-solid);
    --bs-btn-hover-bg: var(--app-danger-solid-hover);
    --bs-btn-hover-border-color: var(--app-danger-solid-hover);
    --bs-btn-active-bg: var(--app-danger-solid-hover);
    --bs-btn-active-border-color: var(--app-danger-solid-hover);
    --bs-btn-focus-shadow-rgb: var(--app-danger-rgb);
    background-color: var(--app-danger-solid);
    border-color: var(--app-danger-solid);
}

/* ---------- 1.3 表单：校验态 ---------- */
.form-control.is-invalid,
.form-select.is-invalid {
    border-color: rgba(var(--app-danger-rgb), .55);
}

.form-control.is-invalid:focus,
.form-select.is-invalid:focus {
    border-color: var(--app-danger-solid);
    box-shadow: 0 0 0 3px rgba(var(--app-danger-rgb), .16);
}

.form-control.is-valid,
.form-select.is-valid {
    border-color: rgba(var(--app-success-rgb), .55);
}

.form-control.is-valid:focus,
.form-select.is-valid:focus {
    border-color: var(--app-success);
    box-shadow: 0 0 0 3px rgba(var(--app-success-rgb), .16);
}

.invalid-feedback,
.valid-feedback {
    font-size: var(--app-font-size-xs);
}

.invalid-feedback { color: var(--ui-danger-ink); }
.valid-feedback { color: var(--ui-success-ink); }

.form-text {
    font-size: var(--app-font-size-xs);
    color: var(--app-muted);
}

/* ---------- 1.4 开关 form-switch ---------- */
.form-switch .form-check-input {
    width: 2.25em;
    height: 1.2em;
    margin-top: .12em;
    border-color: var(--app-border-strong);
    background-color: var(--app-bg);
    transition: background-position .15s ease-in-out, background-color .15s ease-in-out;
}

.form-switch .form-check-input:checked {
    background-color: var(--app-primary);
    border-color: var(--app-primary);
}

.form-switch .form-check-input:focus {
    border-color: rgba(var(--app-primary-rgb), .5);
    box-shadow: 0 0 0 3px rgba(var(--app-primary-rgb), .16);
}

/* ---------- 1.5 表格：变体补齐 ---------- */
.table-wrapper {
    position: relative;
    overflow: auto;
}

.table-light,
.table > thead.table-light th {
    /* 表格统一纯白底：.table-light 不再额外上灰，层次交给边框 */
    --bs-table-bg: var(--app-card);
    --bs-table-color: var(--app-text-secondary);
    background-color: var(--app-card);
    color: var(--app-text-secondary);
}

.table-striped > tbody > tr:nth-of-type(odd) > * {
    /* 斑马纹取消：底色的权威实现见 theme-components.css 第六节（.table / --app-border-row），
       这里只做同名的兜底，掐掉 Bootstrap 5.3 用来画斑马纹的 --bs-table-bg-type。
       注意不要在这里写 background-color —— 本文件晚于 theme-components.css 加载，
       同特异性会把 .table-hover 的行高亮整条盖掉（奇数行永远 hover 不亮）。
       也不要在这里写 color：否则奇数行文字比偶数行深一档，行与行观感不一致。 */
    --bs-table-bg-type: transparent;
}

.table-bordered > :not(caption) > * > * {
    border-color: var(--app-divider);
}

.table-sm > :not(caption) > * > * {
    padding: .4rem .6rem;
}

.table tbody td {
    vertical-align: middle;
}

/* 行内操作列：吸顶 + 不换行（沿用 wb_store 的做法，规范到组件层） */
.table .col-actions,
.table th:last-child.text-end,
.table td:last-child.text-end {
    white-space: nowrap;
}

/* 表头列宽可拖动：table-layout:fixed 才能让列宽严格按设定值生效，
   单元格内容（如标题）随之换行/省略。结构由页面 initColResize() 挂载：
   给表头各列加 .col-resizer 手柄，拖动时本列增减、右邻列反向补偿。 */
.resizable-table {
    table-layout: fixed;
}

.resizable-table > thead > tr > th {
    position: relative;
    overflow: hidden;
}

.resizable-table > tbody > tr > td {
    overflow: hidden;
}

/* 表头右侧拖拽手柄（悬停 / 拖动时显示竖线） */
.col-resizer {
    position: absolute;
    top: 0;
    right: 0;
    width: 8px;
    height: 100%;
    cursor: col-resize;
    user-select: none;
    touch-action: none;
}

.col-resizer::after {
    content: '';
    position: absolute;
    top: 4px;
    bottom: 4px;
    left: 3px;
    width: 2px;
    background: transparent;
    border-radius: 1px;
}

.col-resizer:hover::after,
.col-resizer.active::after {
    background: var(--app-primary);
}

/* 拖动过程中禁用文本选择，避免误选中表格内容 */
body.col-resizing {
    user-select: none;
    cursor: col-resize;
}

/* ---------- 1.6 提示条 alert：变体补齐 ---------- */
.alert {
    border-radius: var(--app-radius-input);
    border-width: 1px;
    font-size: var(--app-font-size-sm);
}

.alert-success {
    --bs-alert-color: var(--ui-success-ink);
    --bs-alert-bg: var(--ui-success-soft);
    --bs-alert-border-color: var(--ui-success-line);
}

.alert-warning {
    --bs-alert-color: var(--ui-warning-ink);
    --bs-alert-bg: var(--ui-warning-soft);
    --bs-alert-border-color: var(--ui-warning-line);
}

.alert-danger {
    --bs-alert-color: var(--ui-danger-ink);
    --bs-alert-bg: var(--ui-danger-soft);
    --bs-alert-border-color: var(--ui-danger-line);
}

.alert-info {
    --bs-alert-color: var(--ui-info-ink);
    --bs-alert-bg: var(--ui-info-soft);
    --bs-alert-border-color: var(--ui-info-line);
}

.alert-light {
    --bs-alert-color: var(--app-text-secondary);
    --bs-alert-bg: var(--app-bg);
    --bs-alert-border-color: var(--app-border-strong);
}

.alert-primary {
    --bs-alert-color: var(--app-primary);
    --bs-alert-bg: var(--app-primary-soft);
    --bs-alert-border-color: var(--app-primary-soft-border);
}

/* ---------- 1.7 关闭按钮 btn-close ---------- */
.btn-close {
    opacity: .5;
    transition: opacity .15s ease;
}

.btn-close:hover { opacity: .85; }

.btn-close:focus {
    box-shadow: 0 0 0 3px rgba(var(--app-primary-rgb), .16);
}

/* ---------- 1.8 标签页 nav-tabs / nav-pills ---------- */
.nav-tabs {
    border-bottom: 1px solid var(--app-divider);
}

.nav-tabs .nav-link {
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    padding: .55rem .95rem;
    font-size: var(--app-font-size-sm);
    font-weight: 600;
    color: var(--app-text-secondary);
}

.nav-tabs .nav-link:hover {
    color: var(--app-primary);
    border-bottom-color: rgba(var(--app-primary-rgb), .35);
}

.nav-tabs .nav-link.active {
    color: var(--app-primary);
    background: transparent;
    border-bottom-color: var(--app-primary);
}

.nav-pills .nav-link {
    border-radius: var(--app-radius-btn);
    padding: .45rem .95rem;
    font-size: var(--app-font-size-sm);
    font-weight: 600;
    color: var(--app-text-secondary);
}

.nav-pills .nav-link:hover {
    background: rgba(var(--app-primary-rgb), .08);
    color: var(--app-primary);
}

.nav-pills .nav-link.active {
    background: var(--app-primary);
    color: #fff;
}

/* 分段控件（collect-system 的 .tab-switcher，收敛为组件） */
.tab-switcher {
    display: inline-flex;
    gap: 3px;
    padding: 3px;
    background: var(--app-bg);
    border-radius: var(--app-radius-input);
}

.tab-switcher .tab-btn {
    border: 0;
    background: transparent;
    color: var(--app-text-secondary);
    font-size: var(--app-font-size-sm);
    font-weight: 600;
    padding: .35rem .9rem;
    border-radius: var(--app-radius-sm);
    transition: .15s;
}

.tab-switcher .tab-btn:hover { color: var(--app-primary); }

.tab-switcher .tab-btn.active {
    background: var(--app-card);
    color: var(--app-primary);
    box-shadow: var(--app-shadow-card);
}

/* ---------- 1.9 列表组 list-group ---------- */
.list-group {
    border-radius: var(--app-radius-card);
}

.list-group-item {
    border-color: var(--app-divider);
    padding: .65rem .9rem;
    font-size: var(--app-font-size-sm);
    color: var(--app-text);
}

.list-group-item-action:hover {
    background: rgba(var(--app-primary-rgb), .05);
    color: var(--app-primary);
}

.list-group-item.active {
    background: var(--app-primary);
    border-color: var(--app-primary);
    color: #fff;
}

/* ---------- 1.10 进度条 progress ---------- */
.progress {
    height: 8px;
    background: var(--app-bg);
    border-radius: var(--app-radius-pill);
    overflow: hidden;
}

.progress-bar {
    background: var(--app-primary);
    font-size: var(--app-font-size-xs);
}

.progress-bar.bg-success { background: var(--app-success) !important; }
.progress-bar.bg-warning { background: var(--app-warning) !important; }
.progress-bar.bg-danger { background: var(--app-danger-solid) !important; }

/* ---------- 1.11 加载指示 spinner ---------- */
.spinner-border { border-width: .18em; }

/* ---------- 1.12 工具提示 tooltip / popover ---------- */
.tooltip {
    --bs-tooltip-bg: var(--app-text);
    --bs-tooltip-color: #fff;
    --bs-tooltip-border-radius: var(--app-radius-sm);
    --bs-tooltip-font-size: var(--app-font-size-xs);
    --bs-tooltip-opacity: .96;
}

.popover {
    /* 与 .popover-header 一致：气泡主体也走卡片白
       （Bootstrap 的 --bs-popover-bg 默认跟随页面灰底，会出现「白标题 + 灰内容」） */
    --bs-popover-bg: var(--app-card);
    --bs-popover-border-color: var(--app-border);
    --bs-popover-border-radius: var(--app-radius-input);
    --bs-popover-box-shadow: var(--app-shadow-pop);
    --bs-popover-max-width: 300px;
    box-shadow: var(--app-shadow-pop);
    font-size: var(--app-font-size-sm);
}

.popover-header {
    background: var(--app-card);
    border-bottom-color: var(--app-divider);
    font-size: var(--app-font-size-sm);
    font-weight: 600;
}

/* ---------- 1.13 模态框：遮罩 / body / 尺寸 / 关闭按钮 ---------- */
.modal-backdrop.show { opacity: .45; }

.modal-backdrop {
    background-color: #14171c;
}

.modal-body {
    padding: 18px 20px;
    font-size: var(--app-font-size-sm);
}

.modal-header {
    padding: 16px 20px;
}

.modal-footer {
    padding: 12px 20px;
}

.modal-footer > .btn {
    min-width: 84px;
}

.modal-header .btn-close {
    --bs-btn-close-focus-shadow: 0 0 0 3px rgba(var(--app-primary-rgb), .16);
}

.modal-sm .modal-content { border-radius: var(--app-radius-card); }

/* ---------- 1.14 下拉菜单：补 divider / header / active ---------- */
.dropdown-divider {
    border-top-color: var(--app-divider);
    margin: .35rem 0;
}

.dropdown-header {
    font-size: var(--app-font-size-xs);
    color: var(--app-muted);
    padding: .4rem .9rem;
}

.dropdown-item.active,
.dropdown-item:active {
    background: var(--app-primary-soft);
    color: var(--app-primary);
}

/* ---------- 1.15 骨架屏（项目尚未使用，预置给后续异步占位） ---------- */
.ui-skeleton {
    display: block;
    height: 12px;
    border-radius: var(--app-radius-sm);
    background: linear-gradient(90deg,
        rgba(20, 23, 28, .06) 25%,
        rgba(20, 23, 28, .03) 37%,
        rgba(20, 23, 28, .06) 63%);
    background-size: 400% 100%;
    animation: ui-skeleton 1.4s ease infinite;
}

.ui-skeleton + .ui-skeleton { margin-top: 8px; }
.ui-skeleton.w-60 { width: 60%; }
.ui-skeleton.w-40 { width: 40%; }

@keyframes ui-skeleton {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
    .ui-skeleton { animation: none; }
}

/* ============================================================
   二、自研组件（统一规范实现）
   ============================================================ */

/* ---------- 2.1 状态标签 status-badge ----------
   规则已下沉到 theme-components.css 第七节（全局组件层），
   使不加载本文件的页面（工具平台等）也能获得同一套状态色。 */

/* ---------- 2.2 空状态 empty-state ---------- */
.empty-state {
    text-align: center;
    color: var(--app-muted);
    padding: 48px 16px;
}

.empty-state .empty-state-icon,
.empty-state > i {
    display: block;
    font-size: 2.5rem;
    line-height: 1;
    color: var(--app-border-strong);
    margin-bottom: 12px;
}

.empty-state .empty-state-title {
    font-size: var(--app-font-size-base);
    font-weight: 600;
    color: var(--app-text-secondary);
    margin-bottom: 4px;
}

.empty-state .empty-state-desc {
    font-size: var(--app-font-size-sm);
    color: var(--app-muted);
}

.empty-state.is-compact { padding: 24px 12px; }

/* 表格内空行单元格 */
.empty-cell {
    text-align: center;
    color: var(--app-muted);
    padding: 48px 16px !important;
    font-size: var(--app-font-size-sm);
}

/* ---------- 2.3 表格加载遮罩 table-loading ----------
   统一项目内 9 份复制实现：绝对定位铺满 .table-wrapper。 */
.table-loading {
    position: absolute;
    inset: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: rgba(255, 255, 255, .82);
    backdrop-filter: blur(1px);
    border-radius: var(--app-radius-card);
    font-size: var(--app-font-size-sm);
    color: var(--app-text-secondary);
}

.table-loading .spinner-border {
    width: 2rem;
    height: 2rem;
    border-width: .2em;
    color: var(--app-primary);
}

.table-loading.d-none { display: none !important; }

/* ---------- 2.4 卡片：统计卡 / 功能卡 ---------- */
.stats-card,
.stat-card {
    background: var(--app-card);
    border: var(--ui-line);
    border-radius: var(--app-radius-card);
    box-shadow: var(--app-shadow-card);
    padding: 18px 20px;
    transition: box-shadow .18s ease, transform .18s ease;
}

.stats-card:hover,
.stat-card:hover {
    box-shadow: var(--app-shadow);
    transform: translateY(-2px);
}

.stats-card .number,
.stat-card .stat-num {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    color: var(--app-text);
}

.stats-card .label,
.stat-card .stat-label {
    font-size: var(--app-font-size-sm);
    color: var(--app-muted);
    margin-top: 2px;
}

.stat-card .stat-icon {
    width: 46px;
    height: 46px;
    border-radius: var(--app-radius-card);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.25rem;
    background: var(--app-primary-soft);
    color: var(--app-primary);
}

.color-success, .stat-card.is-success .stat-num { color: var(--app-success); }
.color-warning, .stat-card.is-warning .stat-num { color: var(--app-warning); }
.color-primary, .stat-card.is-primary .stat-num { color: var(--app-primary); }

.feature-card {
    background: var(--app-card);
    border: var(--ui-line);
    border-radius: var(--app-radius-card);
    box-shadow: var(--app-shadow-card);
    padding: 20px;
    transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}

.feature-card:hover {
    box-shadow: var(--app-shadow);
    transform: translateY(-2px);
    border-color: rgba(var(--app-primary-rgb), .2);
}

.feature-card .fc-icon {
    width: 44px;
    height: 44px;
    border-radius: var(--app-radius-btn-lg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    background: var(--app-primary-soft);
    color: var(--app-primary);
    margin-bottom: 10px;
}

.feature-card h6,
.feature-card .fc-title {
    font-size: var(--app-font-size-base);
    font-weight: 600;
    margin-bottom: 4px;
}

.feature-card p,
.feature-card .fc-desc {
    font-size: var(--app-font-size-sm);
    color: var(--app-muted);
    margin-bottom: 0;
}

/* 分组标题（统一各页 section 标题观感） */
.ui-section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--app-font-size-base);
    font-weight: 600;
    color: var(--app-text);
    margin: 0 0 12px;
}

.ui-section-title .bi { color: var(--app-primary); }

/* ---------- 2.5 分页 pagination-custom ---------- */
.pagination-custom {
    display: flex;
    /* 页码数较多时折行，避免整行溢出把页面撑出横向滚动条 */
    flex-wrap: wrap;
    gap: 4px;
    margin: 0;
    list-style: none;
    padding: 0;
}

.pagination-custom .page-link {
    border: 0;
    border-radius: var(--app-radius-btn);
    color: var(--app-text-secondary);
    font-size: var(--app-font-size-sm);
    padding: .3rem .7rem;
    background: transparent;
}

.pagination-custom .page-link:hover {
    background: rgba(var(--app-primary-rgb), .08);
    color: var(--app-primary);
}

.pagination-custom .page-item.active .page-link {
    background: var(--app-primary);
    color: #fff;
}

.pagination-custom .page-item.disabled .page-link {
    color: var(--app-muted);
    opacity: .55;
    pointer-events: none;
}

/* 分页条：左侧总数 + 中间页码 + 右侧跳转，窄屏自动换行 */
.pagination-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 16px;
    margin-top: 16px;
}

/* 不需要跳转时（条内没有 .pagination-jump），页码推到右下角，与左侧总数分开 */
.pagination-bar:not(:has(.pagination-jump)) {
    justify-content: space-between;
}

/* 分页条放进卡片页脚时，去掉自带的上外边距（页脚已有内边距） */
.card-footer.pagination-bar {
    margin-top: 0;
}

/* 条内没有总数时（如操作日志弹窗），页码靠右，避免孤零零贴在左侧 */
.pagination-bar:not(:has(.pagination-total)) > .pagination-custom {
    margin-left: auto;
}

.pagination-total {
    font-size: var(--app-font-size-xs);
    color: var(--app-muted);
}

.pagination-jump {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    font-size: var(--app-font-size-xs);
    color: var(--app-text-secondary);
}

.pagination-jump .form-control {
    width: 54px;
    height: 30px;
    padding: 0 4px;
    text-align: center;
    font-size: var(--app-font-size-xs);
    border-radius: var(--app-radius-sm);
}

.pagination-jump .btn {
    height: 30px;
    padding: 0 12px;
    font-size: var(--app-font-size-xs);
    border-radius: var(--app-radius-sm);
}

/* ---------- 2.6 行内操作按钮组 action-btns ---------- */
.action-btns {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.action-btns .btn {
    padding: 3px 10px;
    font-size: var(--app-font-size-xs);
    border-radius: var(--app-radius-sm);
}

.action-btns .btn i { font-size: .95em; }

@media (max-width: 768px) {
    .action-btns { flex-direction: column; align-items: stretch; gap: 3px; }
}

/* ---------- 2.7 筛选栏 filter-bar ---------- */
.filter-bar {
    display: flex;
    /* 必须显式声明方向：本类常与 Bootstrap .card 同时使用，而 .card 声明了
       flex-direction: column，若不覆盖就会让 .card-body 在交叉轴上被
       align-items: center 水平居中，搜索框整块跑到卡片中间。 */
    flex-direction: row;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 12px 14px;
    background: var(--app-card);
    border: var(--ui-line);
    border-radius: var(--app-radius-card);
    box-shadow: var(--app-shadow-card);
    margin-bottom: 14px;
}

.filter-bar .form-control,
.filter-bar .form-select {
    min-width: 140px;
}

/* 与表格同卡时使用：去掉自带卡片外壳，只留一条底部分隔线 */
.filter-bar.is-flush {
    margin-bottom: 0;
    border: 0;
    border-bottom: var(--ui-line);
    border-radius: 0;
    box-shadow: none;
}

.filter-bar .filter-actions {
    margin-left: auto;
    display: flex;
    gap: 8px;
}

/* 带图标的搜索框（operator_list 等页面的写法，收敛到组件层） */
.search-field {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.search-field > i {
    position: absolute;
    left: 10px;
    color: var(--app-muted);
    font-size: var(--app-font-size-sm);
    pointer-events: none;
}

.search-field .form-control {
    padding-left: 30px;
}

.search-field .form-control:focus + i,
.search-field:focus-within > i {
    color: var(--app-primary);
}

/* ---------- 2.7.1 日期区间 date-range ---------- */
/* 与 Bootstrap .input-group.input-group-sm 组合，把「标签 + 起始日期 + 至 + 结束日期」
   收成一个整体控件（写法参考 walmart_auto/product_review 的「抓取时间」，但尺寸与样式
   统一由公共层负责，页面不再写内联尺寸）：
   <div class="input-group input-group-sm date-range">
       <span class="input-group-text">创建时间</span>
       <input type="date" class="form-control form-control-sm" id="fDateStart">
       <span class="input-group-text">至</span>
       <input type="date" class="form-control form-control-sm" id="fDateEnd">
   </div> */
.date-range {
    /* input-group 默认 flex-wrap: wrap，窄容器下会把两个日期拆成两行，这里强制单行 */
    flex-wrap: nowrap;
    /* 作为一个整体控件参与筛选栏排布：定宽 325px（标签 74 + 日期 110 + 至 32 + 日期 110），
       不随容器压缩，也不会撑满整行 */
    flex: 0 0 auto;
    width: 325px;
}

.date-range .input-group-text {
    white-space: nowrap;
}

/* 日期输入定宽：110px 即可完整显示「年/月/日 + 日历图标」，
   加上前缀标签与「至」后整块宽度约 325px */
.date-range .form-control {
    width: 110px;
    min-width: 110px;
}

/* ---------- 2.7.2 数值区间 number-range ---------- */
/* 与 Bootstrap .input-group.input-group-sm 组合，把「标签 + 比较运算符 + 数值」收成一个
   整体控件（与 .date-range 同一套设计：整块定宽、页面不再写内联尺寸）：
   <div class="input-group input-group-sm number-range" title="商品评分区间">
       <span class="input-group-text">评分</span>
       <select class="form-select form-select-sm" id="opRating">
           <option value="ge">≥</option>
           <option value="le">≤</option>
       </select>
       <input type="number" class="form-control form-control-sm" id="valRating" step="0.1" placeholder="如 4.0">
   </div> */
.number-range {
    /* input-group 默认 flex-wrap: wrap，窄容器下会把运算符与数值拆成两行，这里强制单行 */
    flex-wrap: nowrap;
    /* 作为一个整体控件参与筛选栏排布：定宽 200px（标签自适应 + 运算符 56 + 数值占剩余），
       不随容器压缩，也不会撑满整行 */
    flex: 0 0 auto;
    width: 200px;
}

/* 标签等宽：评分 / 评论数 / 月销量 都是 2~3 个字，统一占 60px 并居中，
   这样三个控件的运算符下拉与数值框左右边界完全对齐（标签更长的自动撑开） */
.number-range .input-group-text {
    white-space: nowrap;
    min-width: 60px;
    justify-content: center;
}

/* 运算符下拉只放 ≥ / ≤，定宽 56px 即可；不参与收缩，免得被数值框挤掉。
   注意 Bootstrap 给 input-group 里的 select 留了 3rem 的箭头内边距
   （.input-group-sm > .form-select { padding-right: 3rem }），56px 宽的框
   内容区会被挤成 0，≥ / ≤ 直接看不见，所以这里把内边距收窄、箭头贴到右侧 */
.number-range .form-select {
    flex: 0 0 auto;
    width: 56px;
    min-width: 56px;
    padding-right: 22px;
    background-position: right 8px center;
    text-align: center;
}

/* 数值输入吃掉标签之外的剩余宽度：标签 2 字还是 3 字，整块始终是同一个宽度 */
.number-range .form-control {
    flex: 1 1 auto;
    width: auto;
    min-width: 80px;
}

/* ---------- 2.8 上传区 upload-zone / drop-zone ---------- */
.upload-zone,
.drop-zone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 26px 16px;
    text-align: center;
    border: 2px dashed var(--app-border-strong);
    border-radius: var(--app-radius-card);
    background: var(--app-bg);
    color: var(--app-muted);
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease, transform .15s ease;
}

.upload-zone:hover,
.drop-zone:hover {
    border-color: rgba(var(--app-primary-rgb), .5);
    background: rgba(var(--app-primary-rgb), .04);
}

.upload-zone.dragover,
.drop-zone.dragover {
    border-color: var(--app-primary);
    background: rgba(var(--app-primary-rgb), .08);
    transform: scale(1.008);
}

.upload-zone .upload-icon,
.drop-zone .drop-zone-icon,
.upload-zone > i,
.drop-zone > i {
    font-size: 2rem;
    line-height: 1;
    color: var(--app-primary);
}

.upload-zone .upload-text,
.drop-zone .drop-zone-text {
    font-size: var(--app-font-size-sm);
    color: var(--app-text-secondary);
}

.upload-zone .upload-hint,
.drop-zone .drop-zone-hint {
    font-size: var(--app-font-size-xs);
    color: var(--app-muted);
}

/* 已选择文件状态 */
.upload-zone.has-file,
.drop-zone.has-file {
    border-style: solid;
    border-color: rgba(var(--app-success-rgb), .45);
    background: var(--ui-success-soft);
    color: var(--ui-success-ink);
}

.upload-zone.has-file > i,
.drop-zone.has-file > i {
    color: var(--app-success);
}

/* ---------- 2.9 多选标签下拉 tag-select ---------- */
.tag-select {
    position: relative;
}

.tag-select .tag-box {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 5px;
    min-height: 38px;
    padding: 5px 30px 5px 8px;
    /* 与 .form-control 同用 --app-border-field：浅色描边在白卡上仅 1.4:1，
       不满足 WCAG 1.4.11 的 3:1（tag-box 即本组件的输入框外观） */
    border: 1px solid var(--app-border-field);
    border-radius: var(--app-radius-input);
    background: var(--app-card);
    cursor: pointer;
}

.tag-select .tag-box.is-invalid {
    /* 校验失败描边：原 rgba(危险色,.55) 在白底上仅 1.9:1，改用实心危险色 4.8:1 */
    border-color: var(--app-danger-solid);
}

.tag-select .tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--app-radius-sm);
    background: var(--app-bg);
    color: var(--app-text-secondary);
    font-size: var(--app-font-size-xs);
}

.tag-select .tag-x {
    cursor: pointer;
    color: var(--app-muted);
    font-size: .9em;
}

.tag-select .tag-x:hover { color: var(--app-danger-solid); }

.tag-select .tag-placeholder {
    color: var(--app-muted);
    font-size: var(--app-font-size-sm);
}

.tag-select .tag-arrow {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--app-muted);
    font-size: var(--app-font-size-xs);
    pointer-events: none;
}

.tag-select .tag-dropdown {
    display: none;
    position: absolute;
    z-index: 30;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 220px;
    overflow-y: auto;
    padding: 4px;
    background: var(--app-card);
    border: var(--ui-line);
    border-radius: var(--app-radius-input);
    box-shadow: var(--app-shadow-pop);
}

.tag-select .tag-dropdown.show { display: block; }

.tag-select .tag-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 9px;
    border-radius: var(--app-radius-sm);
    font-size: var(--app-font-size-sm);
    color: var(--app-text);
    cursor: pointer;
}

.tag-select .tag-item:hover { background: rgba(var(--app-primary-rgb), .06); }

.tag-select .tag-item.selected {
    background: var(--app-primary-soft);
    color: var(--app-primary);
}

/* ---------- 2.10 步骤条 step-indicator ---------- */
.step-indicator {
    display: flex;
    align-items: center;
    gap: 0;
    padding: 18px 8px;
}

.step-indicator .step-item {
    display: flex;
    align-items: center;
    flex: 1;
}

.step-indicator .step-item:last-child { flex: 0 0 auto; }

.step-indicator .step-circle {
    width: 34px;
    height: 34px;
    flex: 0 0 34px;
    border-radius: var(--app-radius-pill);
    border: 2px solid var(--app-border-strong);
    background: var(--app-card);
    color: var(--app-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--app-font-size-sm);
    font-weight: 600;
}

.step-indicator .step-label {
    margin-left: 8px;
    font-size: var(--app-font-size-sm);
    color: var(--app-muted);
    white-space: nowrap;
}

.step-indicator .step-connector {
    flex: 1;
    height: 2px;
    background: var(--app-border-strong);
    margin: 0 12px;
}

.step-indicator .step-item.active .step-circle {
    border-color: var(--app-primary);
    background: var(--app-primary);
    color: #fff;
}

.step-indicator .step-item.active .step-label {
    color: var(--app-primary);
    font-weight: 600;
}

.step-indicator .step-item.completed .step-circle {
    border-color: var(--app-success);
    background: var(--app-success);
    color: #fff;
}

.step-indicator .step-item.completed .step-label { color: var(--app-text-secondary); }
.step-indicator .step-item.completed .step-connector { background: var(--app-success); }

/* ---------- 2.11 系统状态胶囊 status-dot ---------- */
.system-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: var(--app-radius-pill);
    background: var(--ui-success-soft);
    color: var(--ui-success-ink);
    font-size: var(--app-font-size-xs);
    font-weight: 600;
}

.system-status-badge .dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--app-success);
    animation: ui-pulse 1.6s ease-in-out infinite;
}

.system-status-badge.is-offline {
    background: var(--ui-neutral-soft);
    color: var(--ui-neutral-ink);
}

.system-status-badge.is-offline .dot {
    background: var(--app-muted);
    animation: none;
}

@keyframes ui-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: .45; transform: scale(.82); }
}

@media (prefers-reduced-motion: reduce) {
    .system-status-badge .dot { animation: none; }
}

/* ---------- 2.12 排名徽章 rank-badge ---------- */
.rank-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    font-size: var(--app-font-size-xs);
    font-weight: 700;
    background: var(--ui-neutral-soft);
    color: var(--ui-neutral-ink);
}

.rank-badge.top1 { background: rgba(var(--app-warning-rgb), .18); color: var(--ui-warning-ink); }
.rank-badge.top2 { background: var(--ui-neutral-soft); color: var(--ui-neutral-ink); }
.rank-badge.top3 { background: rgba(var(--app-warning-rgb), .12); color: var(--ui-warning-ink); }

/* ---------- 2.13 右侧抽屉 side-drawer ---------- */
.side-drawer-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1049;
    background: rgba(20, 23, 28, .35);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s ease, visibility .2s ease;
}

.side-drawer-backdrop.show { opacity: 1; visibility: visible; }

.side-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1050;
    width: 520px;
    max-width: 92vw;
    display: flex;
    flex-direction: column;
    background: var(--app-card);
    box-shadow: -10px 0 30px rgba(25, 27, 53, .1);
    transform: translateX(100%);
    transition: transform .24s ease;
}

.side-drawer.open { transform: translateX(0); }

.side-drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--app-divider);
    font-weight: 600;
}

.side-drawer-body {
    padding: 18px 20px;
    overflow-y: auto;
}

/* ---------- 2.14 行内提示条 inline-alert（登录错误等） ---------- */
.inline-alert {
    display: none;
    align-items: center;
    gap: 8px;
    padding: 9px 12px;
    border-radius: var(--app-radius-input);
    background: var(--ui-danger-soft);
    border: 1px solid var(--ui-danger-line);
    color: var(--ui-danger-ink);
    font-size: var(--app-font-size-sm);
}

.inline-alert.show { display: flex; }

.inline-alert.is-success {
    background: var(--ui-success-soft);
    border-color: var(--ui-success-line);
    color: var(--ui-success-ink);
}

.inline-alert.is-warning {
    background: var(--ui-warning-soft);
    border-color: var(--ui-warning-line);
    color: var(--ui-warning-ink);
}

/* ---------- 2.15 代码 / 日志只读文本 ---------- */
.ui-code-block {
    padding: 12px 14px;
    border-radius: var(--app-radius-input);
    background: var(--app-bg);
    border-left: 3px solid var(--app-primary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--app-font-size-xs);
    color: var(--app-text-secondary);
    white-space: pre-wrap;
    word-break: break-all;
}

/* ============================================================
   三、页面框架与导航
   （工作台页签 / 侧边导航 / 欢迎条）

   这些结构此前只存在于 base.html 内联样式与各页自研 CSS 中，
   此处收敛为组件层，供后续各页直接复用。

   注：顶部导航与大菜单（原 3.1 / 3.2）已上移至 theme-components.css——
       本文件只有采集工作台壳页加载，留在原处会让全站顶栏出现两套样式。
   ============================================================ */

/* ---------- 3.1 工作台页签 page-tabs ---------- */
.page-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 6px 8px 0;
    background: var(--app-card);
    border-bottom: 1px solid var(--app-border);
}

.page-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    user-select: none;
}

.page-tab.no-close {
    padding-right: 12px;
}

.page-tab-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font-size: 14px;
    line-height: 1;
    opacity: .6;
    cursor: pointer;
    transition: all .15s ease;
}

.page-tab-close:hover {
    opacity: 1;
}

.page-panel {
    display: none;
}

.page-panel.active {
    display: block;
}

.page-panel-iframe {
    width: 100%;
    min-height: 60vh;
    border: 0;
    border-radius: var(--app-radius-card);
}

/* ---------- 3.2 侧边导航 ---------- */

/* 3.2.1 卡片列表版（.sidebar-nav > .nav-link）：嵌在内容区内的竖向菜单 */
.sidebar-nav {
    min-width: 220px;
    overflow: hidden;
    background: var(--app-card);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-card);
}

.sidebar-nav .nav-link {
    padding: 12px 20px;
    border-radius: 0;
    border-left: 3px solid transparent;
    color: var(--app-text-secondary);
    font-weight: 500;
    transition: all .15s ease;
}

.sidebar-nav .nav-link:hover {
    background: rgba(var(--app-primary-rgb), .05);
    color: var(--app-primary);
    border-left-color: var(--app-primary);
}

.sidebar-nav .nav-link.active {
    background: rgba(var(--app-primary-rgb), .08);
    color: var(--app-primary);
    border-left-color: var(--app-primary);
}

/* 3.2.2 工作台固定侧栏（.app-wrapper > .sidebar > .sidebar-nav）
   复刻采集工作台真实结构：分组标题 + .nav-item（图标 + 文字 + active 态）。
   功能多时用多个 .nav-section-title 分组，字号取最小档（12px）以容纳更多菜单。
   组件层只给形状与配色，定位留给页面：需要整屏固定时由页面补
   position:fixed / top / bottom，需要并排时直接依赖这里的 flex 布局。 */
.app-wrapper {
    display: flex;
    align-items: stretch;
    min-width: 0;
}

.sidebar {
    flex-shrink: 0;
    width: 240px;
    display: flex;
    flex-direction: column;
    background: var(--app-card);
    border-right: 1px solid var(--app-border);
    box-shadow: 4px 0 15px rgba(20, 23, 28, .04);
    transition: transform .3s ease;
}

.sidebar .sidebar-nav {
    flex: 1;
    min-width: 0;
    padding: 6px 10px 16px;
    overflow-y: auto;
    background: transparent;
    border: 0;
    border-radius: 0;
}

.sidebar .sidebar-nav::-webkit-scrollbar {
    width: 5px;
}

.sidebar .sidebar-nav::-webkit-scrollbar-thumb {
    background: var(--app-border-strong);
    border-radius: 3px;
}

/* 分组标题：多组依次出现，第二组起用分隔线断开 */
.nav-section-title {
    padding: 10px 10px 6px;
    font-size: calc(var(--app-font-size-xs) - 1px);
    font-weight: 600;
    letter-spacing: .8px;
    text-transform: uppercase;
    color: var(--app-muted);
}

.sidebar-nav > .nav-section-title:first-child {
    padding-top: 4px;
}

.sidebar-nav > .nav-section-title:not(:first-child) {
    margin-top: 6px;
    padding-top: 14px;
    border-top: 1px solid var(--app-divider);
}

.sidebar .nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 2px;
    padding: 8px 12px;
    border: 1px solid transparent;
    border-radius: var(--app-radius-sm);
    font-size: var(--app-font-size-xs);
    font-weight: 500;
    color: var(--app-text-secondary);
    cursor: pointer;
    transition: all .18s ease;
}

.sidebar .nav-item > i {
    width: 16px;
    text-align: center;
    font-size: .85rem;
}

.sidebar .nav-item:hover {
    background: rgba(var(--app-primary-rgb), .08);
    border-color: rgba(var(--app-primary-rgb), .15);
    color: var(--app-primary);
}

.sidebar .nav-item.active {
    background: var(--app-gradient-brand);
    border-color: transparent;
    color: #fff;
    box-shadow: 0 3px 10px rgba(var(--app-primary-rgb), .28);
}

.main-content {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* 3.2.3 胶囊导航（.nav-link-custom）：状态筛选一类的横向切换 */
.nav-link-custom {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 10px 18px;
    border-radius: var(--app-radius-card);
    color: var(--app-text-secondary);
    font-weight: 500;
    text-decoration: none;
    transition: all .15s ease;
}

.nav-link-custom:hover {
    background: rgba(var(--app-primary-rgb), .08);
    color: var(--app-primary);
}

.nav-link-custom.active {
    background: var(--app-primary);
    color: #fff;
}

/* 3.2.4 移动端侧栏开关（.mobile-toggle + .sidebar-overlay）
   < 992px 时侧栏改为抽屉：汉堡按钮唤出，遮罩点击关闭。
   层级：汉堡 1001 > 侧栏 1000 > 遮罩 999，保证侧栏始终在遮罩之上。 */
.mobile-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: var(--app-radius-btn);
    background: var(--app-primary);
    color: #fff;
    font-size: 1.2rem;
    cursor: pointer;
    box-shadow: 0 4px 12px rgba(var(--app-primary-rgb), .35);
}

.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(20, 23, 28, .5);
    z-index: 999;
}

.sidebar-overlay.show {
    display: block;
}

@media (max-width: 992px) {
    .mobile-toggle {
        display: inline-flex;
        position: fixed;
        top: 88px;
        left: 16px;
        z-index: 1001;
    }

    .sidebar {
        position: fixed;
        top: 76px;
        bottom: 0;
        z-index: 1000;
        transform: translateX(-100%);
    }

    .sidebar.show {
        transform: translateX(0);
    }
}

/* ---------- 3.3 欢迎条 welcome-bar（工具首页） ---------- */
.welcome-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 24px;
    padding: 26px 30px;
    border-radius: var(--app-radius-card);
    background: var(--app-gradient-brand);
    color: #fff;
}

.welcome-bar h2 {
    margin: 0;
    font-weight: 700;
}

.welcome-bar p {
    margin: 4px 0 0;
    opacity: .85;
}

/* ============================================================
   四、Bootstrap 组件补漏（第二批）
   ============================================================ */

/* ---------- 4.1 按钮组 btn-group ---------- */
.btn-group > .btn {
    --bs-btn-border-radius: var(--app-radius-btn);
}

.btn-group-sm > .btn {
    --bs-btn-border-radius: var(--app-radius-sm);
}

/* ---------- 4.2 浮动标签 form-floating ---------- */
.form-floating > .form-control,
.form-floating > .form-select {
    border-radius: var(--app-radius-input);
}

.form-floating > .form-control:focus,
.form-floating > .form-select:focus {
    border-color: var(--app-primary);
    box-shadow: 0 0 0 3px rgba(var(--app-primary-rgb), .12);
}

.form-floating > label {
    color: var(--app-muted);
}

/* ---------- 4.3 原生文件选择统一皮肤 ---------- */
.form-control[type="file"] {
    padding: 6px 10px;
    line-height: 1.7;
}

.form-control[type="file"]::file-selector-button {
    margin-right: 10px;
    padding: 4px 12px;
    border: 1px solid var(--app-primary-soft-border);
    border-radius: var(--app-radius-sm);
    background: var(--app-primary-soft);
    color: var(--app-primary);
    font-weight: 600;
    cursor: pointer;
    transition: background-color .15s ease;
}

.form-control[type="file"]::file-selector-button:hover {
    background: var(--app-primary-soft-hover);
}

/* ---------- 4.4 日期 / 时间选择图标 ---------- */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
    opacity: .45;
    cursor: pointer;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover {
    opacity: 1;
}

/* ---------- 4.5 滑块 form-range（项目暂未使用，预置对齐主色） ---------- */
.form-range::-webkit-slider-thumb {
    background-color: var(--app-primary);
}

.form-range::-moz-range-thumb {
    background-color: var(--app-primary);
}

/* ============================================================
   五、图片与浮层
   （缩略图 / 图片卡 / 上传框 / 全屏预览遮罩 / 图片选择器）
   ============================================================ */

/* ---------- 5.1 图片缩略图 / 图片卡 ---------- */
.image-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.image-thumbs img,
.thumb-wrapper img,
.image-thumb,
.thumb-placeholder {
    width: 90px;
    height: 120px;
    object-fit: cover;
    background: var(--app-bg);
    border: 1px solid var(--app-border-strong);
    border-radius: var(--app-radius-sm);
}

.image-thumb {
    width: 48px;
    height: 48px;
    cursor: pointer;
    transition: transform .15s ease;
}

.image-thumb:hover {
    transform: scale(1.08);
}

.thumb-placeholder,
.image-placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--app-muted);
    background: var(--app-bg);
    border-style: dashed;
}

.image-placeholder {
    width: 48px;
    height: 48px;
}

.thumb-wrapper {
    position: relative;
    display: inline-block;
}

.thumb-actions,
.image-actions {
    position: absolute;
    right: 4px;
    bottom: 4px;
    display: flex;
    gap: 2px;
    padding: 2px 4px;
    border-radius: var(--app-radius-sm);
    background: rgba(255, 255, 255, .92);
    box-shadow: var(--app-shadow);
    opacity: 0;
    transition: opacity .15s ease;
}

.thumb-wrapper:hover .thumb-actions,
.image-card:hover .image-actions {
    opacity: 1;
}

.thumb-actions i,
.image-actions i {
    padding: 2px;
    font-size: 12px;
    color: var(--app-primary);
    cursor: pointer;
}

.thumb-actions i:hover,
.image-actions i:hover {
    color: var(--app-text);
}

.thumb-actions .is-danger,
.image-actions .is-danger {
    color: var(--app-danger-solid);
}

.image-card {
    position: relative;
    display: inline-block;
}

.image-card img {
    width: 135px;
    height: 180px;
    object-fit: cover;
    border-radius: var(--app-radius-btn);
}

/* ---------- 5.2 图片上传框 / 详情图区 ---------- */
.img-upload-box {
    padding: 14px;
    /* 虚线描边是上传区唯一的辨识边界，需满足 WCAG 1.4.11 的 3:1
       （原 --app-border-strong 在灰底上仅 1.3:1） */
    border: 2px dashed var(--app-border-field);
    border-radius: var(--app-radius-input);
    background: var(--app-bg);
    color: var(--app-text-secondary);
    font-size: var(--app-font-size-sm);
    text-align: center;
    cursor: pointer;
    transition: all .15s ease;
}

.img-upload-box:hover {
    border-color: var(--app-primary);
    background: var(--app-primary-soft);
    color: var(--app-primary-ink);
}

.detail-img-box {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    padding: 14px;
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-input);
    background: var(--app-bg);
}

.detail-img-box img {
    max-width: 100%;
    max-height: 200px;
    object-fit: contain;
    border-radius: var(--app-radius-sm);
    cursor: pointer;
}

.img-wrap {
    position: relative;
    display: inline-block;
    margin: 0 6px 6px 0;
}

.img-preview {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border: 1px solid var(--app-border-strong);
    border-radius: var(--app-radius-sm);
    cursor: pointer;
}

.img-del {
    position: absolute;
    top: -7px;
    right: -7px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: rgba(20, 23, 28, .6);
    color: #fff;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    z-index: 2;
}

.img-del:hover {
    background: var(--app-danger-solid);
}

/* ---------- 5.3 全屏遮罩层（图片预览 / 自定义弹层） ---------- */
.modal-overlay,
.gallery-overlay,
.custom-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1080;
    align-items: center;
    justify-content: center;
    background: rgba(20, 23, 28, .82);
}

.modal-overlay.show,
.gallery-overlay.show,
.custom-overlay.show {
    display: flex;
}

.gallery-overlay {
    flex-direction: column;
    /* 低于单图预览，便于从相册点开大图 */
    z-index: 1070;
}

.modal-overlay img,
.gallery-overlay img {
    max-width: 90vw;
    max-height: 90vh;
    border-radius: var(--app-radius-card);
}

.gallery-overlay .gallery-container {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    justify-content: center;
    padding: 20px;
}

.gallery-overlay .gallery-container img {
    max-width: 280px;
    max-height: 280px;
    cursor: pointer;
}

.modal-overlay .close-btn,
.gallery-overlay .close-btn {
    position: absolute;
    top: 18px;
    right: 24px;
    border: 0;
    background: transparent;
    color: rgba(255, 255, 255, .85);
    font-size: 34px;
    line-height: 1;
    cursor: pointer;
}

.modal-overlay .close-btn:hover,
.gallery-overlay .close-btn:hover {
    color: #fff;
}

/* 选择型弹层卡片 */
.source-select-modal,
.custom-overlay .custom-dialog {
    background: var(--app-card);
    border-radius: var(--app-radius-card);
    box-shadow: var(--app-shadow-pop);
    overflow: hidden;
}

.custom-overlay .custom-dialog {
    min-width: 320px;
    max-width: 92vw;
}

/* ---------- 5.4 图片选择项 ---------- */
.image-select-item {
    border: 2px solid transparent;
    border-radius: var(--app-radius-sm);
    cursor: pointer;
    transition: all .15s ease;
}

.image-select-item:hover {
    border-color: rgba(var(--app-primary-rgb), .5);
}

.image-select-item.selected {
    border-color: var(--app-primary);
    background: var(--app-primary-soft);
    box-shadow: 0 0 0 2px rgba(var(--app-primary-rgb), .3);
}

/* ---------- 5.5 列表单元格缩略图 cell-thumb（含多图张数角标） ---------- */
/* 用法：列表「图片 / 依据图片」列
   <span class="cell-thumb">
       <img src="..." data-hover-preview onclick="previewImages([...], 0)">
       <span class="img-count">3</span>   ← 仅多图时渲染
   </span> */
.cell-thumb {
    position: relative;
    display: inline-block;
    line-height: 0;
}

.cell-thumb img {
    width: 60px;
    height: 60px;
    object-fit: cover;
    background: var(--app-bg);
    border: 1px solid var(--app-border-strong);
    border-radius: var(--app-radius-sm);
    cursor: pointer;
    transition: transform .15s ease;
}

.cell-thumb img:hover {
    transform: scale(1.06);
}

/* 多图张数角标：贴右下角（放在图片内侧，避免被 .table-wrapper 的 overflow 裁掉） */
.cell-thumb .img-count {
    position: absolute;
    right: 3px;
    bottom: 3px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: var(--app-radius-pill);
    background: rgba(20, 23, 28, .72);
    color: #fff;
    font-size: 11px;
    line-height: 16px;
    text-align: center;
    pointer-events: none;
}

/* ---------- 5.6 列表单元格多图九宫格 cell-grid（3 列 × 2 行小图 + 剩余张数） ---------- */
/* 用法：图片较多时用九宫格代替单图缩略图
   <span class="cell-grid">
       <img src="..." data-hover-preview onclick="previewImages([...], i)">
       <span class="img-more">+5</span>   ← 图片超过 6 张时渲染，占第 6 格，点击同样打开预览
   </span> */
.cell-grid {
    display: grid;
    grid-template-columns: repeat(3, 30px);
    gap: 2px;
    line-height: 0;
}

.cell-grid img {
    width: 30px;
    height: 30px;
    object-fit: cover;
    background: var(--app-bg);
    border: 1px solid var(--app-border-strong);
    border-radius: var(--app-radius-sm);
    cursor: pointer;
}

.cell-grid .img-more {
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(20, 23, 28, .72);
    color: #fff;
    font-size: 11px;
    border-radius: var(--app-radius-sm);
    cursor: pointer;
}

/* ============================================================
   六、树 / 联想下拉 / 已选标记
   （类目树 / 搜索联想 / 已选标签）
   ============================================================ */

/* ---------- 6.1 类目树 category-tree ---------- */
.category-tree,
.category-tree ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.category-tree {
    padding: 0 6px;
}

.category-tree ul {
    display: none;
    padding-left: 16px;
}

.category-tree ul.open {
    display: block;
}

.category-tree-scroll {
    max-height: 320px;
    padding: 4px 0;
    overflow-y: auto;
}

.cat-row {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 4px 6px;
    border-radius: var(--app-radius-sm);
    cursor: pointer;
    transition: background-color .15s ease;
}

.cat-row:hover {
    background: var(--app-bg);
}

.cat-arrow {
    flex: 0 0 16px;
    width: 16px;
    text-align: center;
    color: var(--app-muted);
    cursor: pointer;
    user-select: none;
}

.cat-arrow-empty {
    visibility: hidden;
    cursor: default;
}

.category-tree .cat-label {
    flex: 1;
    font-size: var(--app-font-size-sm);
    color: var(--app-text);
}

.cat-node.selected > .cat-row .cat-label {
    color: var(--app-primary);
    font-weight: 600;
}

/* ---------- 6.2 搜索联想下拉 search-dropdown ---------- */
.search-dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1040;
    max-height: 300px;
    margin-top: 4px;
    overflow-y: auto;
    background: var(--app-card);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-input);
    box-shadow: var(--app-shadow-pop);
}

.search-dropdown.show {
    display: block;
}

.search-dropdown .dropdown-item {
    padding: 8px 12px;
    border-bottom: 1px solid var(--app-divider);
    font-size: var(--app-font-size-sm);
    color: var(--app-text);
    cursor: pointer;
    transition: background-color .12s ease;
}

.search-dropdown .dropdown-item:last-child {
    border-bottom: 0;
}

.search-dropdown .dropdown-item:hover {
    background: var(--app-bg);
    color: var(--app-primary);
}

.search-dropdown .dropdown-item .category-path {
    font-size: var(--app-font-size-xs);
    color: var(--app-muted);
}

/* ---------- 6.3 已选标记 selected-badge ---------- */
.selected-badge {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 4px 10px;
    border: 1px solid var(--app-primary-soft-border);
    border-radius: var(--app-radius-sm);
    background: var(--app-primary-soft);
    color: var(--app-primary);
    font-size: var(--app-font-size-sm);
}

.selected-badge .remove-btn {
    color: var(--app-danger-solid);
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
}

.selected-badge .remove-btn:hover {
    color: var(--app-danger-solid-hover);
}

/* ============================================================
   七、版式块与文本工具
   （页面标题 / 参数区块 / 结果统计 / 截断与复制 / 状态胶囊映射）
   ============================================================ */

/* ---------- 7.1 页面与分组标题 ---------- */
.page-title {
    margin-bottom: 0;
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--app-text);
}

.page-title i {
    color: var(--app-primary);
}

.section-title {
    margin-bottom: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--app-text);
}

/* ---------- 7.2 参数区块 / SKU 面板 / AI 结果条 ---------- */
.parameter-section {
    margin-bottom: 18px;
    padding: 20px;
    background: var(--app-card);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-card);
    box-shadow: var(--app-shadow-card);
}

.sku-panel {
    margin-bottom: 16px;
    padding: 16px;
    background: var(--app-card);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-card);
}

.sku-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--app-divider);
}

.ai-result-inline {
    display: none;
    margin-top: 8px;
    padding: 10px 14px;
    border-radius: var(--app-radius-input);
    background: var(--app-primary-soft);
    font-size: var(--app-font-size-sm);
    color: var(--app-text-secondary);
}

.ai-result-inline.show {
    display: block;
}

/* ---------- 7.3 结果统计块 result-card ---------- */
.result-card {
    padding: 40px 20px;
    text-align: center;
}

.result-number {
    font-size: 3.4rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--app-primary);
}

.result-label {
    margin-top: 6px;
    font-size: 1.05rem;
    color: var(--app-text-secondary);
}

/* ---------- 7.4 长文本截断与点击复制 ---------- */
.cell-url {
    display: inline-block;
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
    font-size: var(--app-font-size-xs);
    color: var(--app-primary);
}

.ui-copy,
.nm-id {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 6px;
    border-radius: var(--app-radius-sm);
    background: var(--app-bg);
    color: var(--app-text-secondary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--app-font-size-xs);
    cursor: pointer;
    transition: all .15s ease;
}

.ui-copy:hover,
.nm-id:hover {
    background: var(--app-primary-soft);
    color: var(--app-primary);
}

/* 表格单元格文本最多两行，超出省略（配合可拖动列宽，变宽后行数随之变化）。
   line-clamp 必须作用在内层元素上：直接给 td 设 display:-webkit-box 会让 td
   脱离表格列布局导致错位。用法：<td><div class="cell-clamp2" title="全文">…</div></td> */
.cell-clamp2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
    line-height: 1.35;
}

/* ---------- 7.5 悬浮抬升（工具侧 hover-lift） ---------- */
.hover-lift {
    transition: transform .18s ease, box-shadow .18s ease;
}

.hover-lift:hover {
    transform: translateY(-3px);
    box-shadow: var(--app-shadow-pop);
}

/* ---------- 7.6 采集侧状态胶囊 badge-status（30px 圆点款 → 统一语义色） ---------- */
.badge-status {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 12px;
    border-radius: var(--app-radius-pill);
    background: var(--ui-neutral-soft);
    color: var(--ui-neutral-ink);
    font-size: var(--app-font-size-xs);
    font-weight: 600;
    /* 状态胶囊必须单行：列被挤窄时若允许换行，「已编辑」会折成 3 行、
       把胶囊撑到 62px，进而把整行行高一起顶起来。
       （operator_list.css 里的旧版同款胶囊一直带着这条，这里是补齐。） */
    white-space: nowrap;
}

.badge-status i {
    font-size: .5rem;
}

/* 上面那条 .5rem 是给 bi-circle-fill 小圆点用的；若胶囊里放的是
   「圆圈对号 / 圆圈叉 / 沙漏」这类状态字形（见 ui-feedback-demo.html），
   按 .5rem 会小到看不清，恢复成跟随胶囊文字比例。 */
.badge-status .bi-check-circle,
.badge-status .bi-x-circle,
.badge-status .bi-hourglass-split,
.badge-status .bi-exclamation-circle,
.badge-status .bi-question-circle,
.badge-status .bi-trash {
    font-size: .9em;
}

.badge-status.badge-active,
.badge-status.status-success,
.badge-status.badge-status-done {
    background: var(--ui-success-soft);
    color: var(--ui-success-ink);
}

.badge-status.badge-inactive,
.badge-status.badge-warning,
.badge-status.status-pending {
    background: var(--ui-warning-soft);
    color: var(--ui-warning-ink);
}

.badge-status.status-failed,
.badge-status.status-failure {
    background: var(--ui-danger-soft);
    color: var(--ui-danger-ink);
}

.badge-status.badge-pending,
.badge-status.status-processing {
    background: var(--ui-info-soft);
    color: var(--ui-info-ink);
}

/* ---------- 7.7 表格内加载 / 空行单元 ---------- */
.loading-overlay {
    position: fixed;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, .8);
    z-index: 1080;
}

.loading-overlay.show {
    display: flex;
}

.empty-row,
.loading-cell {
    padding: 60px 20px !important;
    text-align: center;
    color: var(--app-muted);
}

/* ---------- 7.8 表格行展开箭头 row-toggle ---------- */
/* 折叠态为右向 chevron，展开后旋转 90° 指向下方；替代原先直接写 ▶ / ▼ 字符的做法。
   用法：<td class="expand-icon"><span class="row-toggle"><i class="fas fa-chevron-right"></i></span></td>
   展开时在所属 <tr> 上加 .is-expanded。 */
.expand-icon {
    width: 34px;
    text-align: center;
    color: var(--app-muted);
}

.row-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: var(--app-radius-sm);
    background: var(--ui-neutral-soft);
    color: var(--app-muted);
    font-size: 11px;
    line-height: 1;
    transition: background-color .15s ease, color .15s ease;
}

.row-toggle i {
    transition: transform .2s ease;
}

/* 整行悬停、或该行已展开时点亮箭头 */
tr:hover .row-toggle,
.is-expanded .row-toggle {
    background: var(--app-primary-soft);
    color: var(--app-primary);
}

.is-expanded .row-toggle i {
    transform: rotate(90deg);
}

/* ---------- 7.9 必填标记 required ---------- */
/* 表单分组统一用 7.2 的 .parameter-section + 7.1 的 .section-title（见 ui-feedback-demo 的
   #mXl 模态框示例）；本组件只负责必填星号：
   <label class="form-label required">站点</label> 或 <span class="required">产品链接</span> */
.required::after {
    content: " *";
    color: var(--app-danger-solid);
}

/* ---------- 7.10 详情定义块 detail-card ---------- */
/* 详情弹窗里的「字段名 + 字段值」分组（原样复刻 walmart_auto / mercadolibre_auto 的
   product_review、quality_product 四份复制实现，收敛到这里）：
   <div class="detail-card">
       <dl class="row mb-0">
           <dt class="col-sm-3">店铺</dt><dd class="col-sm-9">xxx</dd>
       </dl>
   </div> */
.detail-card {
    margin-bottom: 12px;
    padding: 16px;
    background: var(--app-bg);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-input);
}

.detail-card dt {
    margin-bottom: 4px;
    font-size: var(--app-font-size-xs);
    font-weight: 400;
    color: var(--app-muted);
}

.detail-card dd {
    margin-bottom: 8px;
    font-weight: 500;
    color: var(--app-text-secondary);
}

.detail-card dd:last-child {
    margin-bottom: 0;
}

/* 详情图：单图缩放展示（图片本身的圆角 / 缩放走 5.1 的 .image-thumb） */
.detail-img {
    max-width: 200px;
    max-height: 200px;
    border-radius: var(--app-radius-input);
    object-fit: contain;
}

@media (prefers-reduced-motion: reduce) {
    .hover-lift,
    .image-thumb {
        transition: none;
    }

    .row-toggle,
    .row-toggle i {
        transition: none;
    }
}
