:root {
    --brand: #3370ff;
    --brand-hover: #2860e1;
    --brand-light: #eef3ff;
    --text-primary: #1f2329;
    --text-secondary: #646a73;
    --text-tertiary: #8f959e;
    --border-color: #e5e6eb;
    --border-color-soft: #f0f0f2;
    --bg-page: #f5f6f7;
    --bg-surface: #ffffff;
    --bg-hover: #f5f6f7;
    --radius-md: 8px;
    --radius-sm: 6px;
    --shadow-card: 0 1px 2px rgba(0, 0, 0, .04), 0 2px 8px rgba(0, 0, 0, .04);
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
    /* 警示色——飞书这类按钮/提示不用饱和色描边+白底，而是浅色底+深一点的字色，
       这几个 token 专门给 btn-outline-warning/danger、任务提醒卡片这类场景用 */
    --warning: #b25e09;
    --warning-light: #fdf1e0;
    --warning-border: #f5ddb0;
    --danger: #d0342c;
    --danger-light: #fdefee;
    --danger-border: #f6cfcc;
    --success: #0f8a4b;
    --success-light: #e6f6ed;
    --success-border: #c3ead4;
}

body {
    margin: 0;
    background: var(--bg-page);
    color: var(--text-primary);
    font-family: var(--font-sans);
    font-size: 14px;
}

a {
    color: var(--brand);
}

/* ===== Login ===== */
.login-body {
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(160deg, #eef3ff 0%, #f5f6f7 45%, #f5f6f7 100%);
}

.login-box {
    width: 380px;
    background: var(--bg-surface);
    padding: 40px 36px;
    border-radius: 16px;
    box-shadow: 0 8px 32px rgba(31, 35, 41, .08);
    border: 1px solid var(--border-color-soft);
}

.login-lang a {
    color: var(--text-secondary);
    font-size: 13px;
    text-decoration: none;
}

.login-lang a:hover {
    color: var(--brand);
}

.login-lang .nav-lang-sep {
    color: var(--border-color);
    margin: 0 8px;
}

.login-brand-mark {
    width: 48px;
    height: 48px;
    margin: 0 auto 16px;
    border-radius: 12px;
    background: var(--brand);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 22px;
}

/* ===== Top navbar ===== */
.app-navbar {
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-color);
}

.navbar-fixed {
    height: 56px;
}

.app-navbar .navbar-brand {
    color: var(--text-primary);
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 10px;
}

.brand-mark {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: var(--brand);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 700;
}

.nav-lang a {
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 13px;
}

.nav-lang a:hover {
    color: var(--brand);
}

.nav-lang-sep {
    color: var(--border-color);
    margin: 0 6px;
}

.nav-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 16px;
}

.nav-icon-btn:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.user-chip {
    display: flex;
    align-items: center;
    gap: 8px;
}

.user-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--brand-light);
    color: var(--brand);
    font-weight: 600;
    font-size: 13px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.user-name {
    color: var(--text-primary);
    font-size: 13px;
}

/* ===== Layout ===== */
.app-body {
    display: flex;
    min-height: calc(100vh - 56px);
}

.app-sidebar {
    width: 236px;
    background: var(--bg-surface);
    color: var(--text-primary);
    flex-shrink: 0;
    overflow-y: auto;
    border-right: 1px solid var(--border-color);
    padding: 12px 8px;
}

.app-sidebar .nav-item {
    margin-bottom: 1px;
}

/* 一级分组之间留出呼吸空间，跟组内的二级项区分开——不然所有行挤在一起，层次看不出来 */
.app-sidebar > ul.nav > li.nav-item + li.nav-item {
    margin-top: 6px;
}

.app-sidebar .nav-link {
    color: var(--text-secondary);
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.3;
    cursor: pointer;
}

.app-sidebar .menu-icon {
    font-size: 16px;
    width: 18px;
    text-align: center;
    flex-shrink: 0;
    color: var(--text-tertiary);
}

.app-sidebar .menu-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-sidebar .menu-chevron {
    font-size: 10px;
    color: var(--text-tertiary);
    transition: transform .15s ease;
}

.app-sidebar .sidebar-group-toggle[aria-expanded="true"] .menu-chevron {
    transform: rotate(90deg);
}

/* 通用折叠卡片标题的箭头——跟上面 sidebar 那对是同一个思路，抽成不带 .app-sidebar 前缀的
   通用版本，供详情页里默认折叠的辅助信息卡片（比如⑤销售主管审批的工厂报价/系统参考价）复用 */
.collapse-toggle .chevron {
    font-size: 12px;
    color: var(--text-tertiary);
    transition: transform .15s ease;
}

.collapse-toggle[aria-expanded="true"] .chevron {
    transform: rotate(90deg);
}

.app-sidebar .nav-link:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
}

.app-sidebar .nav-link.active {
    color: var(--brand);
    background: var(--brand-light);
    font-weight: 600;
}

.app-sidebar .nav-link.active .menu-icon {
    color: var(--brand);
}

/* 一级分组标题（开发/系统管理这类可展开的）跟一级独立页面（运营看板/我的审批这类没有子菜单的）
   是同一层级，字号/颜色/粗细都要保持一致，只靠右边的箭头区分"这条会展开"——之前给分组标题单独
   弄了更小更淡的字号，反而让同一层级的字体大小对不上，看着像是随机大小不一致 */

/* 二级子项——用一条竖线把同一组的子项连起来，仿飞书那种树形导航，
   一眼就能看出"这几条属于上面这个分组"，不用靠单纯的缩进猜层级关系 */
.app-sidebar .collapse {
    position: relative;
    margin-left: 21px;
    padding-left: 9px;
    border-left: 1px solid var(--border-color-soft);
}

.app-sidebar .collapse .nav-link {
    padding: 6px 8px;
    font-size: 13px;
    font-weight: 400;
    gap: 8px;
}

.app-sidebar .collapse .menu-icon {
    font-size: 13px;
    width: 15px;
}

.app-sidebar .collapse .nav-link.active {
    font-weight: 600;
}

.app-content {
    flex: 1;
    padding: 24px;
}

/* 面包屑——从一级目录到当前页面，全站通用（GlobalModelAttributeAdvice 算出来的），
   分隔符纯用 CSS ::before 加，模板那边不用关心"是不是最后一个"这种循环状态 */
.app-breadcrumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--text-secondary);
    margin-bottom: 14px;
}

.app-breadcrumb > a {
    color: var(--text-secondary);
    text-decoration: none;
}

.app-breadcrumb > a:hover {
    color: var(--brand);
}

.app-breadcrumb > span:last-child {
    color: var(--text-primary);
    font-weight: 600;
}

.app-breadcrumb > *:not(:first-child)::before {
    content: '›';
    margin: 0 6px;
    color: var(--border-color);
}

/* ===== Cards / surfaces ===== */
.card {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
}

.card-body h6 {
    font-weight: 600;
}

/* "需要你处理"这类提醒卡片（审批操作栏）——之前用 Bootstrap 的 border-primary，
   四周一整圈纯色粗边框，跟警告框一样扎眼。飞书这类强调不会整圈描色，只在左边留一条色条
   + 很淡的底色，其它三边还是普通的浅灰描边，既能一眼看出"这里需要你处理"，又不会太抢戏 */
.task-panel-card {
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--brand);
    background: var(--brand-light);
}

/* ===== "我的处理节点"弹窗——一条贯穿始终的竖线把过去/当前/未来的节点串起来，过去的圆点是
   实心绿勾，当前是琥珀色数字，未来是空心数字；卡片本身过去的正常展示，当前的琥珀色描边高亮，
   未来的虚线灰底。跟 approval-timeline（那个不能动的通用组件）是两套独立样式，互不影响 ===== */
.sr-my-nodes-timeline {
    position: relative;
    padding-left: 40px;
}

.sr-my-nodes-timeline::before {
    content: '';
    position: absolute;
    left: 13px;
    top: 14px;
    bottom: 14px;
    width: 2px;
    background: var(--border-color);
}

.sr-my-nodes-step {
    position: relative;
    margin-bottom: 14px;
}

.sr-my-nodes-step:last-child {
    margin-bottom: 0;
}

/* 底层详情页正文不要那条左侧竖线+圆点了，就是纯堆叠的卡片——"我的处理节点"弹窗里那条
   （#sr-my-nodes-list）还留着，只隐藏详情页正文这一份（#sr-page-timeline-root），
   用 id 选择器盖掉上面 .sr-my-nodes-timeline/.sr-my-nodes-dot 的样式，不用管两条规则谁在前 */
#sr-page-timeline-root {
    padding-left: 0;
}

#sr-page-timeline-root::before {
    display: none;
}

#sr-page-timeline-root .sr-my-nodes-dot {
    display: none;
}

/* 报价详情页正文（#q-page-timeline-root）跟上面 #sr-page-timeline-root 是同一个选择：
   底层正文不要竖线+编号圆点，纯堆叠的卡片；"完成/待处理"状态改用卡片标题旁边的小徽标
   （见 QuoteDetailPage.renderSectionDots），不靠这颗圆点的颜色 */
#q-page-timeline-root {
    padding-left: 0;
}

#q-page-timeline-root::before {
    display: none;
}

#q-page-timeline-root .sr-my-nodes-dot {
    display: none;
}

.sr-my-nodes-dot {
    position: absolute;
    left: -40px;
    top: 14px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    color: #fff;
    background: #2a8a4a;
    z-index: 1;
}

.sr-my-nodes-step-current .sr-my-nodes-dot {
    background: var(--warning);
}

.sr-my-nodes-step-future .sr-my-nodes-dot {
    background: var(--bg-surface);
    color: var(--text-tertiary);
    border: 2px solid var(--border-color);
}

.sr-my-nodes-step-card {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    padding: 14px 16px;
}

.sr-my-nodes-step-current .sr-my-nodes-step-card {
    border-color: var(--warning);
    box-shadow: 0 0 0 1px var(--warning);
}

/* 挂业务板块（基本信息/版师资料/客户验收）对应的节点正好轮到当前处理时，板块自己那张
   .card（不是 .sr-my-nodes-step-card）也要一起描边，不然只有里面那个小审批信息条变了颜色，
   整块大卡片看不出来"这一步正等你处理" */
.sr-my-nodes-step-current > .card {
    border-color: var(--warning);
    box-shadow: 0 0 0 1px var(--warning);
}

.sr-my-nodes-step-future .sr-my-nodes-step-card {
    background: var(--bg-page);
    border-style: dashed;
}

.sr-my-nodes-step-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
}

.sr-my-nodes-step-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 10px;
    white-space: nowrap;
}

.sr-my-nodes-step-badge-done {
    background: #e6f4ea;
    color: #2a8a4a;
}

.sr-my-nodes-step-badge-current {
    background: var(--warning-light);
    color: var(--warning);
}

.sr-my-nodes-step-badge-future {
    background: var(--bg-page);
    color: var(--text-tertiary);
}

.sr-my-nodes-step-badge-pending {
    background: var(--warning-light);
    color: var(--warning);
}

/* ===== 详情页正文那些板块（基本信息/生产/质检...）也挂在同一套时间线视觉上——每个板块是一个
   节点，圆点默认是中性灰（没有"完成/未完成"概念的板块，比如版师资料、评论，就一直是灰的），
   有完整性判断的板块（材料明细、生产、质检、后整、客户验收）由 renderSectionStatusDots 动态
   染色。用两级类选择器 (.sr-my-nodes-dot.sr-page-node-dot) 保证能盖掉 .sr-my-nodes-dot 自己
   默认的绿色，不用依赖样式表里两条规则谁写在后面 ===== */
.sr-my-nodes-dot.sr-page-node-dot {
    background: var(--text-tertiary);
}

.sr-my-nodes-dot.sr-page-node-dot-done {
    background: #2a8a4a;
}

.sr-my-nodes-dot.sr-page-node-dot-pending {
    background: var(--warning);
}

/* 当前节点的点要盖过 .sr-page-node-dot 自己的灰色默认值——三层类选择器保证优先级够高，
   不用管这条规则写在样式表里前面还是后面 */
.sr-my-nodes-step-current .sr-my-nodes-dot.sr-page-node-dot {
    background: var(--warning);
}

/* ===== 流程走完之后整页锁死——除了"转正为正式款式"和"查看日志"，业务板块里所有编辑/新增/
   上传按钮统统隐藏（包括材料明细表格里逐行动态生成的编辑/删除按钮，全站这类按钮都是 Bootstrap
   的 .btn，一条规则全包了，不用每加一个板块就手动记得再排除一次）。附件本身的下载链接/图片
   缩略图不是 .btn，不受影响，锁住之后还能正常看 ===== */
#sr-page-timeline-root.sr-timeline-locked .btn:not(#sample-promote-btn):not(.sr-lock-exempt) {
    display: none !important;
}

/* ===== 详情页顶部的地铁线进度条——严格按流程图节点顺序常驻显示全部站点（包括成本/议价这种
   可选分支，没走到也占位、只是灰着），走过的站+连线变绿，当前站琥珀色放大，效果类似地铁报站
   "到了哪站哪站亮"。连线用真实的 flex 子元素撑开（不是每个点自己拿百分比宽度画一段），这样
   不用管每个站文字长短不一导致宽度不齐、连线对不上圆点中心的问题 ===== */
.sr-subway-timeline {
    display: flex;
    align-items: flex-start;
    overflow-x: auto;
    padding: 18px 12px;
    margin-bottom: 16px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}

.sr-subway-station {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 0 0 auto;
    width: 84px;
}

.sr-subway-connector {
    flex: 1 0 24px;
    height: 3px;
    background: var(--border-color);
    margin-top: 10px;
    min-width: 24px;
}

.sr-subway-connector-done {
    background: #2a8a4a;
}

.sr-subway-dot {
    display: inline-block;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 3px solid var(--border-color);
    box-sizing: border-box;
}

.sr-subway-station.sr-subway-done .sr-subway-dot {
    background: #2a8a4a;
    border-color: #2a8a4a;
}

.sr-subway-station.sr-subway-current .sr-subway-dot {
    width: 24px;
    height: 24px;
    background: var(--warning);
    border-color: var(--warning);
    box-shadow: 0 0 0 4px var(--warning-light);
}

.sr-subway-label {
    margin-top: 6px;
    font-size: 12px;
    text-align: center;
    color: var(--text-tertiary);
    line-height: 1.3;
}

.sr-subway-station.sr-subway-done .sr-subway-label {
    color: var(--text-primary);
}

.sr-subway-station.sr-subway-current .sr-subway-label {
    color: var(--warning);
    font-weight: 600;
}

.sr-subway-duration {
    margin-top: 2px;
    font-size: 11px;
    color: var(--text-tertiary);
}

/* ===== 挂在业务板块卡片里的"谁审批的+备注+附件"小方块，跟穿插的轻量节点卡片（复用
   .sr-my-nodes-step-card）视觉上是同一路——都是"审批记录"而不是"业务数据" ===== */
.sr-approval-info-box {
    background: var(--bg-page);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    margin-bottom: 12px;
    font-size: 13px;
}

.sr-approval-info-current {
    color: var(--warning);
    background: var(--warning-light);
}

/* ===== Buttons ===== */
.btn {
    border-radius: var(--radius-sm);
    font-weight: 500;
}

.btn-primary {
    background-color: var(--brand);
    border-color: var(--brand);
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
    background-color: var(--brand-hover) !important;
    border-color: var(--brand-hover) !important;
}

.btn-outline-primary {
    color: var(--brand);
    border-color: var(--brand);
}

.btn-outline-primary:hover,
.btn-outline-primary:active {
    background-color: var(--brand) !important;
    border-color: var(--brand) !important;
}

.btn-outline-secondary {
    color: var(--text-secondary);
    border-color: var(--border-color);
}

.btn-outline-secondary:hover {
    background-color: var(--bg-hover);
    border-color: var(--border-color);
    color: var(--text-primary);
}

/* 警示/危险类的描边按钮——飞书不用"白底+饱和色描边"这种撞色很重的搭配，
   改成浅色底+同色系深一点的字，边框也压得很淡，视觉上柔和很多 */
.btn-outline-warning {
    color: var(--warning);
    background-color: var(--warning-light);
    border-color: var(--warning-border);
}

.btn-outline-warning:hover,
.btn-outline-warning:active {
    color: var(--warning) !important;
    background-color: var(--warning-border) !important;
    border-color: var(--warning-border) !important;
}

.btn-outline-danger {
    color: var(--danger);
    background-color: var(--danger-light);
    border-color: var(--danger-border);
}

.btn-outline-danger:hover,
.btn-outline-danger:active {
    color: var(--danger) !important;
    background-color: var(--danger-border) !important;
    border-color: var(--danger-border) !important;
}

/* ===== Forms ===== */
.form-control,
.form-select {
    border-color: var(--border-color);
    border-radius: var(--radius-sm);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-light);
}

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

.form-label {
    font-weight: 500;
    color: var(--text-secondary);
    font-size: 13px;
}

/* 必填字段标记——红色星号跟在字段名后面，不是放在最前面挤占对齐，飞书的表单/审批单都是
   这个位置。故意做成一个类，不是每处手写 <span class="text-danger">*</span>，不然漏加/
   加错颜色的情况以后免不了发生 */
.form-label-required::after {
    content: "*";
    color: var(--danger);
    margin-left: 3px;
}

/* 长表单分区小标题——飞书表单/审批单常见的做法：不用一整张卡片把所有字段堆在一起，
   按业务含义分成几个小节，每节一个不抢眼但看得清楚的标题，节与节之间留出明显比字段间距
   更大的空白，扫一眼就能找到"我要填的是哪一块" */
.form-section-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 16px 0;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border-color-soft);
}

.form-section-title:not(:first-child) {
    margin-top: 32px;
}

/* 表单专用卡片——比看板统计卡片这类小卡片需要更宽松的呼吸空间，不直接改 .card-body 的通用
   padding（那样看板上一堆紧凑的小卡片也会被撑开），单独开一个类只给真正的表单卡片用 */
.form-card .card-body {
    padding: 28px 32px;
}

/* ===== Tables ===== */
.table {
    font-size: 13.5px;
}

.table > thead {
    background: var(--bg-hover);
}

.table > thead th {
    color: var(--text-secondary);
    font-weight: 600;
    border-bottom: 1px solid var(--border-color);
    white-space: nowrap;
}

.table-hover > tbody > tr:hover > * {
    background-color: var(--brand-light);
}

/* table-bordered 默认是四面围栏式（每个单元格上下左右都画线+外边框），看着很闷——
   飞书的表格只留行与行之间的横线，不画竖线也不画外框，信息密度高的时候才不会觉得挤。
   这里直接把 table-bordered 的边框行为整体压成"只留浅色横线"，不用一个个模板去改 class */
.table-bordered {
    border: none;
}

.table-bordered > :not(caption) > * {
    border-width: 0;
}

.table-bordered > :not(caption) > * > * {
    border-width: 0 0 1px 0;
    border-color: var(--border-color-soft);
}

.table-bordered > thead > tr > * {
    border-bottom-color: var(--border-color);
}

/* ===== Tabs / pagination / misc bootstrap tweaks ===== */
.nav-tabs .nav-link.active {
    color: var(--brand);
    font-weight: 600;
    border-color: var(--border-color) var(--border-color) #fff;
}

.page-link {
    color: var(--text-secondary);
}

.page-item.active .page-link {
    background-color: var(--brand);
    border-color: var(--brand);
}

.list-group-item-action:hover,
.list-group-item-action:focus {
    background-color: var(--brand-light);
}

.text-muted {
    color: var(--text-tertiary) !important;
}

/* 流程图可视化（bpmn-js），SVG 画布不设高度会直接塌成 0 高，什么都看不见 */
#diagram-canvas {
    height: 600px;
    background: var(--bg-surface);
    border-radius: var(--radius-md);
}

.diagram-legend-swatch {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 2px;
    vertical-align: middle;
}

/* bpmn-js 的 canvas.addMarker(id, className) 加的是这个 class 到元素的 <g> 容器上，
   :nth-child(1) 拿到的是实际的图形（矩形/圆形/菱形，具体形状取决于节点类型），
   已办的节点标绿、当前卡着的节点标橙并加粗描边 */
.diagram-completed .djs-visual > :nth-child(1) {
    fill: #d4edda !important;
    stroke: #28a745 !important;
}

.diagram-active .djs-visual > :nth-child(1) {
    fill: #fff3cd !important;
    stroke: #fd7e14 !important;
    stroke-width: 3px !important;
}

/* ===== Translate widget (Twitter/X 风格：原文旁一个小图标，点开在下方展示译文) ===== */
.translate-toggle {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-left: 6px;
    color: var(--text-tertiary);
    font-size: 12px;
    text-decoration: none;
    cursor: pointer;
    vertical-align: middle;
}

.translate-toggle:hover,
.translate-toggle.active {
    color: var(--brand);
}

.translate-toggle.loading {
    pointer-events: none;
    opacity: .5;
}

.translated-block {
    margin-top: 4px;
    padding: 6px 10px;
    background: var(--brand-light);
    border-radius: var(--radius-sm);
    font-size: 0.95em;
    color: var(--text-primary);
}

.translated-block .translated-label {
    font-size: 11px;
    color: var(--text-tertiary);
    margin-bottom: 2px;
}

.copyable-text {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.btn-copy-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    padding: 0 2px;
    color: var(--text-tertiary);
    cursor: pointer;
    font-size: 0.95em;
    line-height: 1;
}

.btn-copy-icon:hover {
    color: var(--brand);
}

/* 地址栏：多行地址（门牌/城市州邮编各占一行）+ 一个复制图标贴在右边，跟单行的 .copyable-text 分开一套样式 */
.copyable-address {
    display: inline-flex;
    align-items: flex-start;
    gap: 6px;
}

.copyable-address .address-lines {
    line-height: 1.5;
    white-space: nowrap;
}

.copyable-address .btn-copy-icon {
    margin-top: 2px;
}

/* 操作日志时间线——入库单/出库单/Routing/提货预约详情页共用，钉钉文档编辑历史那种风格 */
.op-log-feed {
    display: flex;
    flex-direction: column;
}

.op-log-item {
    display: flex;
    gap: 12px;
    position: relative;
    padding-bottom: 18px;
}

.op-log-item::before {
    content: '';
    position: absolute;
    left: 15px;
    top: 32px;
    bottom: 0;
    width: 1px;
    background: var(--border-color-soft);
}

.op-log-item:last-child {
    padding-bottom: 0;
}

.op-log-item:last-child::before {
    display: none;
}

.op-log-avatar {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--brand-light);
    color: var(--brand);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 600;
    position: relative;
    z-index: 1;
}

.op-log-body {
    flex: 1;
    min-width: 0;
    padding-top: 5px;
}

.op-log-header {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}

.op-log-operator {
    font-weight: 600;
    color: var(--text-primary);
    font-size: 13.5px;
}

.op-log-time {
    font-size: 12px;
    color: var(--text-tertiary);
}

.op-log-desc {
    margin-top: 2px;
    font-size: 13.5px;
    color: var(--text-secondary);
    line-height: 1.5;
    word-break: break-word;
}

/*
 * 评论区——一条主评论+它的回复是一张卡片（回复最多一层，缩进展示），卡片底部是这条主评论专属的
 * 回复框；没有真实头像系统，跟操作日志一样用姓名首字母撞色圆形当默认头像
 */
.comment-thread {
    background: var(--bg-surface);
    border: 1px solid var(--border-color-soft);
    border-radius: var(--radius-md);
    margin-bottom: 16px;
    overflow: hidden;
}

.comment-thread:last-child {
    margin-bottom: 0;
}

.comment-row {
    display: flex;
    gap: 12px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--border-color-soft);
}

.comment-row.is-reply {
    padding-left: 66px;
}

.comment-avatar {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 600;
    color: #fff;
}

.comment-body {
    flex: 1;
    min-width: 0;
}

.comment-header {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}

.comment-author {
    font-weight: 600;
    color: var(--text-primary);
    font-size: 13.5px;
}

.comment-time {
    font-size: 12px;
    color: var(--text-tertiary);
}

.comment-content {
    margin-top: 3px;
    font-size: 13.5px;
    color: var(--text-primary);
    line-height: 1.55;
    word-break: break-word;
    white-space: pre-wrap;
}

.comment-attachment-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 6px;
    font-size: 12.5px;
    color: var(--brand);
    text-decoration: none;
}

.comment-attachment-chip:hover {
    text-decoration: underline;
}

.comment-composer-row {
    display: flex;
    gap: 12px;
    padding: 14px 18px;
    align-items: flex-start;
}

/* 回复框比主评论框矮一档、字也小一号，跟顶层的"发起新评论"框拉开视觉层级 */
.comment-composer-row.is-reply {
    padding: 9px 18px;
    gap: 10px;
}

.comment-composer-row.is-reply .comment-avatar {
    width: 26px;
    height: 26px;
    font-size: 11px;
}

.comment-composer-row.is-reply .comment-composer-textarea {
    padding: 4px 0;
    font-size: 13px;
}

.comment-composer-row.is-reply .comment-icon-btn {
    font-size: 15px;
}

.comment-composer-row.is-reply .comment-send-btn {
    font-size: 18px;
}

.comment-composer-avatar {
    background: var(--brand);
}

.comment-composer-input {
    flex: 1;
    display: flex;
    align-items: flex-end;
    gap: 8px;
    min-width: 0;
}

.comment-composer-field {
    flex: 1;
    min-width: 0;
}

.comment-composer-textarea {
    width: 100%;
    border: none;
    outline: none;
    resize: none;
    font-size: 13.5px;
    font-family: inherit;
    color: var(--text-primary);
    background: transparent;
    padding: 8px 0;
    line-height: 1.4;
    max-height: 120px;
}

.comment-composer-textarea::placeholder {
    color: var(--text-tertiary);
}

.comment-pending-attachment {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-secondary);
    background: var(--bg-hover);
    border-radius: var(--radius-sm);
    padding: 4px 8px;
    margin-top: 4px;
    width: fit-content;
}

.comment-pending-remove {
    border: none;
    background: none;
    color: var(--text-tertiary);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}

.comment-icon-btn {
    flex-shrink: 0;
    border: none;
    background: none;
    color: var(--text-tertiary);
    font-size: 18px;
    line-height: 1;
    padding: 4px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.comment-icon-btn:hover {
    color: var(--brand);
}

.comment-send-btn {
    font-size: 22px;
    color: var(--brand);
}

/* ===== Tree view (dict item management, etc.) ===== */
.dict-tree .tree-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.dict-tree .tree-list .tree-list {
    margin-left: .55rem;
    padding-left: 1.25rem;
    border-left: 1px dashed var(--border-color);
}

.dict-tree .tree-list .tree-list > .tree-node {
    position: relative;
}

.dict-tree .tree-list .tree-list > .tree-node::before {
    content: '';
    position: absolute;
    top: 1.05rem;
    left: -1.25rem;
    width: 1.25rem;
    border-top: 1px dashed var(--border-color);
}

.dict-tree .tree-node.collapsed > .tree-children {
    display: none;
}

.dict-tree .tree-row {
    display: flex;
    align-items: center;
    gap: .375rem;
    padding: .3rem .5rem;
    border-radius: var(--radius-sm);
}

.dict-tree .tree-row:hover {
    background: var(--bg-hover);
}

.dict-tree .tree-row:hover .tree-actions {
    opacity: 1;
}

.dict-tree .tree-toggle,
.dict-tree .tree-toggle-spacer {
    flex: none;
    width: 1.1rem;
    height: 1.1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.dict-tree .tree-toggle {
    border: none;
    background: none;
    padding: 0;
    color: var(--text-tertiary);
    cursor: pointer;
    font-size: 11px;
}

.dict-tree .tree-toggle:hover {
    color: var(--brand);
}

.dict-tree .tree-label {
    flex: 1 1 auto;
    min-width: 0;
}

.dict-tree .tree-actions {
    flex: none;
    opacity: 0;
    transition: opacity .12s ease;
}

.dict-tree .tree-actions .btn {
    padding: .1rem .45rem;
    font-size: 12px;
}

/* ===== 仿飞书确认框/提示框（替换原生 confirm()/alert()，见 app.js 的 showConfirm/showAlert） ===== */
.feishu-dialog .feishu-dialog-dialog {
    max-width: 400px;
}

.feishu-dialog .feishu-dialog-content {
    border: none;
    border-radius: 12px;
    box-shadow: 0 12px 40px rgba(31, 35, 41, .16);
    overflow: hidden;
}

.feishu-dialog .feishu-dialog-body {
    padding: 24px 24px 8px;
}

.feishu-dialog .feishu-dialog-message {
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-primary);
    white-space: pre-line;
}

.feishu-dialog .feishu-dialog-input {
    border-radius: var(--radius-sm);
    border-color: var(--border-color);
}

.feishu-dialog .feishu-dialog-input:focus {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-light);
}

.feishu-dialog .feishu-dialog-footer {
    border-top: none;
    padding: 16px 24px 20px;
    gap: 8px;
}

.feishu-dialog .feishu-dialog-footer .btn {
    border-radius: var(--radius-sm);
    padding: 6px 18px;
    font-size: 14px;
    font-weight: 500;
}

.feishu-dialog .feishu-dialog-footer .btn-light {
    background: var(--bg-page);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
}

.feishu-dialog .feishu-dialog-footer .btn-light:hover {
    background: var(--bg-hover);
}

.feishu-dialog .feishu-dialog-footer .btn-primary {
    background: var(--brand);
    border-color: var(--brand);
}

.feishu-dialog .feishu-dialog-footer .btn-primary:hover {
    background: var(--brand-hover);
    border-color: var(--brand-hover);
}

/* ===== 审批流转时间线（仿飞书审批详情页）——常驻展示，跟"现在该不该我处理"完全无关，
   任何有权限看这条记录的人随时都能看完整的过去+现在。见 system-sample-detail.js /
   system-fabric-development-detail.js 的 loadApprovalTimeline ===== */
.approval-timeline {
    position: relative;
    padding-left: 20px;
    font-size: 13px;
}

.approval-timeline-item {
    position: relative;
    padding-bottom: 16px;
}

.approval-timeline-item:last-child {
    padding-bottom: 0;
}

.approval-timeline-item:not(:last-child)::before {
    content: '';
    position: absolute;
    left: -15px;
    top: 13px;
    bottom: -3px;
    width: 1px;
    background: var(--border-color);
}

.approval-timeline-dot {
    position: absolute;
    left: -20px;
    top: 3px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #2a8a4a;
}

.approval-timeline-item.pending .approval-timeline-dot {
    background: var(--bg-surface);
    border: 2px solid var(--brand);
    box-sizing: border-box;
}

.approval-timeline-pending-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 8px;
    border-radius: 10px;
    background: var(--brand-light);
    color: var(--brand);
    margin-left: 6px;
}

/* 审批轨迹每一条的"结论"徽标（同意/不同意/议价、客户接受/修改/放弃……）——跟上面的
   pending 徽标同一个形状，颜色按语义分三档，业务模块自己决定某个任务类型用哪一档，
   见 createApprovalTimeline 的 config.decisionBadge */
.approval-timeline-decision-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 8px;
    border-radius: 10px;
    margin-left: 6px;
}

.approval-timeline-decision-badge.positive {
    background: var(--success-light);
    color: var(--success);
}

.approval-timeline-decision-badge.negative {
    background: var(--danger-light);
    color: var(--danger);
}

.approval-timeline-decision-badge.neutral {
    background: var(--warning-light);
    color: var(--warning);
}

.approval-timeline-decision-badge.done {
    background: var(--border-color-soft);
    color: var(--text-tertiary);
}

/* 详情页"销售确认结果"用的放大版结论徽标——颜色语义跟 approval-timeline-decision-badge
   是同一套（positive/negative/neutral），只是字号/内边距按"一眼看到结论"的场景放大，
   两处样式故意不合并成一份：一个是列表里的小标签，一个是详情页顶部的主视觉，用途不同 */
.q-result-highlight {
    display: inline-block;
    font-size: 20px;
    font-weight: 700;
    padding: 6px 18px;
    border-radius: 8px;
}

.q-result-highlight.positive {
    background: var(--success-light);
    color: var(--success);
}

.q-result-highlight.negative {
    background: var(--danger-light);
    color: var(--danger);
}

.q-result-highlight.neutral {
    background: var(--warning-light);
    color: var(--warning);
}

.approval-timeline-comment {
    background: var(--bg-page);
    border-radius: 6px;
    padding: 6px 10px;
    margin-top: 4px;
    color: var(--text-secondary);
}

/* ===== 报价详情页"同款号其他报价"侧边目录——跟正文卡片是分开的，常驻在页面左侧，
   不是滚动到才看到的一张卡片。position: sticky 让它在滚动正文的时候留在视口里 ===== */
.q-style-sidebar {
    width: 240px;
    flex-shrink: 0;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 10px;
    position: sticky;
    top: 12px;
}

.q-style-sidebar-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 4px 6px 10px;
    border-bottom: 1px solid var(--border-color);
    margin-bottom: 6px;
}

.q-style-sidebar-head .title {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--text-primary);
}

.q-style-sidebar-head .tag {
    font-size: 10.5px;
    font-weight: 600;
    color: var(--text-tertiary);
}

.q-style-item {
    display: block;
    text-decoration: none;
    color: var(--text-primary);
    border-radius: var(--radius-sm);
    padding: 8px 9px;
    margin-bottom: 3px;
    border: 1px solid transparent;
}

.q-style-item:hover {
    background: var(--bg-hover);
}

.q-style-item .row1 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 6px;
}

.q-style-item .round {
    font-size: 12.5px;
    font-weight: 700;
}

.q-style-item .price {
    font-size: 12.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.q-style-item .row2 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-top: 3px;
}

.q-style-item .date {
    font-size: 11px;
    color: var(--text-tertiary);
    font-variant-numeric: tabular-nums;
}

.q-style-item .status {
    font-size: 11px;
    font-weight: 600;
}

/* 正在看的这一轮在列表里高亮出来，不能跟别的条目长一个样，不然看不出自己现在在哪 */
.q-style-item.current {
    background: var(--brand-light);
    border-color: var(--brand);
    cursor: default;
}

.q-style-item.current .round {
    color: var(--brand);
}

.q-style-item .viewing-now {
    display: none;
    font-size: 10px;
    color: var(--brand);
    font-weight: 600;
    margin-top: 3px;
}

.q-style-item.current .viewing-now {
    display: block;
}

/* ===== "我的待办"弹窗——每个节点自己的完整内容（③参考卡片、表单这些）现在都套一层带边框的
   scope 容器，容器自己的标题就是这个节点的名字，跟弹窗顶部那个笼统的大标题分开——不然弹窗顶部
   写"④CEO审批价格"，底下却混进了⑥的卡片，看起来像⑥是④的子项，其实两个是并列的、各自独立的
   节点。active（当前轮到、能操作）用主题色边框强调，inactive（同一个角色管的另一个节点，只读）
   用灰底弱化，一眼分得清哪个是"该我做的"、哪个只是"顺带看一眼的" ===== */
.q-my-nodes-scope {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 14px 16px 4px;
    margin-bottom: 16px;
}

.q-my-nodes-scope-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border-color-soft);
}

.q-my-nodes-scope-head h5 {
    margin: 0;
    font-size: 16px;
}

.q-my-nodes-scope-active {
    border-color: var(--brand);
}

.q-my-nodes-scope-active .q-my-nodes-scope-head h5 {
    color: var(--brand);
    font-weight: 700;
}

.q-my-nodes-scope-inactive {
    background: var(--bg-page);
    opacity: .85;
}

.q-my-nodes-scope-inactive .q-my-nodes-scope-head h5 {
    color: var(--text-secondary);
    font-weight: 600;
}

/* ===== 下拉菜单（仿飞书）——全局统一皮肤，Bootstrap 的 .dropdown-menu/.dropdown-item 本身
   就是纯 markup+CSS，不用额外写 JS 组件，这里改一次，全站现在和以后任何地方用的下拉菜单
   （比如"发起打样"这种把几个同类按钮合并成一个下拉的场景）都自动是这个风格 ===== */
.dropdown-menu {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 6px;
    font-size: 14px;
}

.dropdown-item {
    border-radius: var(--radius-sm);
    padding: 7px 10px;
    color: var(--text-primary);
}

.dropdown-item:hover,
.dropdown-item:focus {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.dropdown-item.active,
.dropdown-item:active {
    background: var(--brand-light);
    color: var(--brand);
}

.btn.dropdown-toggle::after {
    vertical-align: 2px;
}
