:root {
    color-scheme: light;
    font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
    --brand: #d80c18;
    --brand-dark: #b70813;
    --primary: #315efb;
    --primary-hover: #244cd4;
    --page: #f5f6f8;
    --surface: #ffffff;
    --surface-soft: #f8f9fb;
    --text: #171a21;
    --text-secondary: #606a7b;
    --border: #dfe3ea;
    --border-strong: #cbd2dd;
    --success: #16845b;
    --warning: #aa6500;
    --danger: #c92934;
    /* 流程图三个出口各一个色。刻意写成上面三个色的别名而不是重抄一遍色值 ——
       同一个颜色在两处各写一遍，改了一处忘了另一处就会出现"淘汰在漏斗里是红的、
       在别处是另一种红"。语义各不相同才起新名：淘汰=坏事、放弃=中性提醒、
       人才库=还留着的机会。 */
    --exit-rejected: var(--danger);
    --exit-withdrawn: var(--warning);
    --exit-pool: var(--primary);
    --shadow: 0 8px 24px rgba(28, 39, 58, 0.06);
}

* {
    box-sizing: border-box;
}

html {
    min-width: 320px;
    background: var(--page);
}

body {
    margin: 0;
    min-width: 320px;
    min-height: 100vh;
    background: var(--page);
    color: var(--text);
    font-size: 14px;
}

button,
input,
select,
textarea {
    font: inherit;
}

button,
a,
input,
select,
textarea,
[tabindex] {
    outline: none;
}

button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    box-shadow: 0 0 0 3px rgba(49, 94, 251, 0.24);
}

button {
    color: inherit;
}

[hidden] {
    display: none !important;
}

.app-shell {
    /* 侧栏宽度只写在这里。三个断点各改一次这个值，弹出面板靠 calc 跟着走——
       面板要贴在侧栏外沿，两边各写一份数字迟早会对不上。 */
    --nav-width: 88px;
    display: grid;
    grid-template-columns: var(--nav-width) minmax(0, 1fr);
    min-height: 100vh;
}

.main-nav {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    height: 100vh;
    /* 滚动条在 .nav-items 上，不在这里。侧栏自己滚的话，底部的切换入口会跟着
       滚出屏幕 —— 那块必须一直看得见。 */
    overflow: hidden;
    background: var(--brand);
    color: #ffffff;
    box-shadow: 4px 0 16px rgba(123, 4, 11, 0.12);
}

.brand-mark {
    display: flex;
    min-height: 84px;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-bottom: 1px solid rgba(255, 255, 255, 0.18);
}

.brand-symbol {
    font-size: 20px;
    font-weight: 800;
    font-style: italic;
    letter-spacing: -1px;
}

.brand-caption {
    margin-top: 2px;
    color: rgba(255, 255, 255, 0.76);
    font-size: 10px;
    letter-spacing: 1px;
}

.nav-items {
    display: flex;
    flex: 1;
    /* min-height: 0 是必需的，不是保险。flex 项默认 min-height: auto，会顶着内容
       高度不肯缩：880×860 实测，侧栏窄到 72px 时导航文字换行，10 个导航项要 775px，
       于是这份清单把底部的切换入口整块挤到了屏幕外面（y=859，视口才 860 高）。
       改成「清单自己滚、缩得下去」，切换入口就永远钉在侧栏底部。 */
    min-height: 0;
    overflow: hidden auto;
    flex-direction: column;
    gap: 2px;
    padding: 10px 8px;
}

.nav-item {
    position: relative;
    display: flex;
    min-height: 62px;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 5px 2px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: rgba(255, 255, 255, 0.9);
    font-size: 11px;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
}

.nav-item:hover,
.nav-item.is-active {
    background: rgba(255, 255, 255, 0.18);
    color: #ffffff;
}

.nav-item.is-active::before {
    position: absolute;
    left: -8px;
    width: 3px;
    height: 28px;
    border-radius: 0 4px 4px 0;
    background: #ffffff;
    content: "";
}

.nav-item:disabled {
    color: rgba(255, 255, 255, 0.6);
    cursor: not-allowed;
}

.nav-icon {
    display: grid;
    width: 24px;
    height: 24px;
    place-items: center;
    border: 1px solid rgba(255, 255, 255, 0.36);
    border-radius: 8px;
    font-size: 12px;
    font-weight: 700;
}

.nav-unavailable-label {
    display: block;
    color: rgba(255, 255, 255, 0.52);
    font-size: 8px;
    line-height: 1;
}

.nav-item:disabled:hover .nav-unavailable-label,
.nav-item:disabled:focus-visible .nav-unavailable-label {
    color: rgba(255, 255, 255, 0.78);
}

.workspace {
    min-width: 0;
    background: var(--page);
}

.page {
    min-height: 100vh;
    padding: 24px 28px 32px;
}

.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 24px;
    margin-bottom: 18px;
}

.page-header h1,
.page-header h2 {
    margin: 3px 0 5px;
    font-size: 24px;
    line-height: 1.3;
    letter-spacing: -0.5px;
}

.eyebrow,
.section-kicker {
    margin: 0;
    color: var(--brand);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
}

.page-subtitle {
    margin: 0;
    color: var(--text-secondary);
    line-height: 1.5;
}

.header-actions {
    display: flex;
    align-items: center;
    gap: 14px;
}

.data-stamp {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--text-secondary);
    font-size: 12px;
    white-space: nowrap;
}

.scope-badge,
.safe-label {
    display: inline-flex;
    min-height: 26px;
    align-items: center;
    padding: 0 9px;
    border: 1px solid #c6e6d8;
    border-radius: 999px;
    background: #f1fbf7;
    color: var(--success);
    font-size: 11px;
    font-weight: 700;
}

.primary-button,
.secondary-button,
.danger-button,
.page-button {
    min-height: 40px;
    border-radius: 8px;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 160ms ease, background 160ms ease, transform 160ms ease;
}

.primary-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 0 18px;
    border: 1px solid var(--primary);
    background: var(--primary);
    color: #ffffff;
}

.primary-button:hover {
    border-color: var(--primary-hover);
    background: var(--primary-hover);
}

.primary-button:disabled,
.secondary-button:disabled,
.danger-button:disabled {
    border-color: var(--border);
    background: #eef1f5;
    color: #9aa2af;
    cursor: not-allowed;
}

.secondary-button,
.page-button {
    padding: 0 16px;
    border: 1px solid var(--border-strong);
    background: #ffffff;
    color: #3e4858;
}

.secondary-button:hover:not(:disabled),
.page-button:hover:not(:disabled) {
    border-color: var(--primary);
    color: var(--primary);
}

/* 删除类操作单独一档配色：不做成主按钮，避免顺手点下去。 */
.danger-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 16px;
    border: 1px solid #f0cfd2;
    background: #fdf3f4;
    color: var(--danger);
}

.danger-button:hover:not(:disabled) {
    border-color: var(--danger);
    background: #fbe9eb;
}

.filter-panel,
.panel {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.filter-panel {
    margin-bottom: 18px;
    overflow: hidden;
}

.filter-row {
    display: flex;
    min-width: 0;
    align-items: flex-end;
    gap: 10px;
    padding: 13px 16px;
}

.filter-row + .filter-row {
    border-top: 1px solid #edf0f4;
}

.filter-label {
    display: flex;
    width: 44px;
    min-height: 40px;
    align-items: center;
    color: #424c5b;
    font-weight: 700;
}

.field-control {
    display: flex;
    min-width: 130px;
    flex-direction: column;
    gap: 5px;
}

/* display:flex 会盖掉 hidden 自带的 display:none，要显式补一条才藏得住。 */
.field-control[hidden] {
    display: none;
}

.field-control > span {
    color: var(--text-secondary);
    font-size: 11px;
}

.field-control input,
.field-control select {
    height: 40px;
    min-width: 0;
    padding: 0 11px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: #ffffff;
    color: var(--text);
}

.field-control input:hover,
.field-control select:hover {
    border-color: var(--border-strong);
}

.field-grow {
    flex: 1;
}

.date-field {
    min-width: 150px;
}

.date-separator {
    padding-bottom: 12px;
    color: #8a93a2;
}

.keyword-field {
    min-width: 210px;
    flex: 1;
}

.filter-actions {
    display: flex;
    gap: 8px;
}

.section-heading,
.panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
}

.compact-heading {
    margin: 0 2px 9px;
}

.section-heading h2,
.panel-header h2 {
    margin: 2px 0 0;
    font-size: 16px;
}

.section-heading > p,
.panel-total {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
}

.kpi-section {
    margin-bottom: 18px;
}

.kpi-strip {
    display: grid;
    grid-template-columns: repeat(9, minmax(105px, 1fr));
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.kpi-card {
    display: flex;
    min-width: 0;
    min-height: 112px;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    padding: 13px 14px;
    border: 0;
    border-right: 1px solid #edf0f4;
    border-radius: 0;
    background: #ffffff;
    text-align: left;
}

.kpi-card:last-child {
    border-right: 0;
}

button.kpi-card {
    cursor: pointer;
}

.kpi-card.is-actionable:hover {
    z-index: 1;
    background: #f7f9ff;
    box-shadow: inset 0 -3px 0 var(--primary);
}

.kpi-card.is-selected {
    background: #f2f5ff;
    box-shadow: inset 0 -3px 0 var(--primary);
}

.kpi-card.is-unavailable {
    background: #fafafa;
}

.kpi-label {
    overflow: hidden;
    max-width: 100%;
    color: #505a6a;
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kpi-value {
    margin: 6px 0 2px;
    font-size: 28px;
    line-height: 1;
    letter-spacing: -0.5px;
}

.kpi-note {
    color: #9098a5;
    font-size: 10px;
}

.is-skeleton {
    color: transparent !important;
    border-radius: 5px;
    background: linear-gradient(90deg, #eef1f5 20%, #f7f8fa 45%, #eef1f5 70%);
    background-size: 200% 100%;
    animation: skeleton 1.35s ease-in-out infinite;
}

.kpi-value.is-skeleton {
    width: 42px;
    height: 28px;
}

/* 家底卡（B2，2026-09-16）。观感与 kpi-strip 同一套（边框 + 圆角 + 阴影 + 分隔线），
   但刻意不是九列里再多两列 —— 那会被读成第十、第十一张 KPI 卡，而这一块的数字
   不随筛选栏走，两套口径不能长得一模一样。三列：岗位、人才两个大数字 + 板块分布。 */
.asset-section {
    margin-bottom: 18px;
}

.asset-strip {
    display: grid;
    grid-template-columns: minmax(190px, 0.85fr) minmax(190px, 0.85fr) minmax(0, 2.3fr);
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.asset-card {
    display: flex;
    min-width: 0;
    flex-direction: column;
    justify-content: center;
    padding: 13px 14px;
    border-right: 1px solid #edf0f4;
    background: #ffffff;
}

.asset-card:last-child {
    border-right: 0;
}

.asset-label {
    overflow: hidden;
    max-width: 100%;
    color: #505a6a;
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.asset-value {
    margin: 6px 0 4px;
    font-size: 28px;
    line-height: 1;
    letter-spacing: -0.5px;
}

.asset-value.is-skeleton {
    width: 42px;
    height: 28px;
}

/* 卡片小字比 kpi-note 多一点行高：note 是后端下发的整句口径说明（两三行），
   挤成一团会没人读得清。 */
.asset-note {
    color: #9098a5;
    font-size: 10px;
    line-height: 1.6;
}

.asset-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

/* 板块分布的 chip：换行铺开、不截断 —— 条目再多也全部可见，藏进横向滚动
   会让靠后的板块等于没显示。 */
.asset-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 3px 9px;
    border: 1px solid #edf0f4;
    border-radius: 999px;
    background: var(--surface-soft);
    font-size: 11px;
    white-space: nowrap;
}

.asset-chip strong {
    color: #273142;
    font-size: 12px;
}

/* 「未归类」是保留值不是真板块：虚线边框把它和真板块区别开，
   说明文字在悬停 title 里（数据由后端 unassignedCoreUnit.note 下发）。 */
.asset-chip.is-unassigned {
    border-style: dashed;
}

/* .asset-section 自己没有 display 规则，hidden 本可直接生效；显式补一条是照
   .summary-notices 的教训上保险 —— 以后谁给它加了 display:grid/flex，hidden
   会被盖掉，assetOverview 缺失时整块就收不起来了。 */
.asset-section[hidden] {
    display: none;
}

.feedback-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 18px;
    padding: 13px 15px;
    border: 1px solid #efc1c5;
    border-radius: 9px;
    background: #fff5f6;
}

.feedback-banner strong {
    color: var(--danger);
}

.feedback-banner p {
    margin: 3px 0 0;
    color: #6e4a4d;
    font-size: 12px;
}

.insight-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.65fr) minmax(300px, 0.85fr);
    gap: 16px;
    margin-bottom: 16px;
}

.panel {
    min-width: 0;
}

.panel-header {
    min-height: 64px;
    padding: 14px 16px;
    border-bottom: 1px solid #edf0f4;
}

.stage-distribution {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 11px 18px;
    min-height: 174px;
    padding: 16px;
}

.stage-row {
    display: grid;
    grid-template-columns: minmax(92px, 1fr) minmax(90px, 1.3fr) 26px;
    align-items: center;
    gap: 9px;
}

.stage-name {
    overflow: hidden;
    color: #3e4858;
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.stage-row progress {
    width: 100%;
    height: 7px;
    overflow: hidden;
    border: 0;
    border-radius: 999px;
    background: #edf0f4;
}

.stage-row progress::-webkit-progress-bar {
    border-radius: 999px;
    background: #edf0f4;
}

.stage-row progress::-webkit-progress-value {
    border-radius: 999px;
    background: var(--primary);
}

.stage-row progress::-moz-progress-bar {
    border-radius: 999px;
    background: var(--primary);
}

.stage-count {
    color: #273142;
    font-weight: 700;
    text-align: right;
}

/* 面试轮次漏斗（需求 7②）。整行占满：上面两张卡的列宽是调过的，
   塞成第三列会把阶段分布压窄。 */
.funnel-panel {
    grid-column: 1 / -1;
}

/* 漏斗自己那三级筛选。和顶部筛选栏刻意长得不一样（浅底、贴在卡里），
   两套条件同屏时，样式上先分出"这一排管的是这张卡"。 */
.funnel-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
    padding: 14px 16px 0;
}

.funnel-filters .field-control {
    min-width: 150px;
}

.funnel-list {
    /* 阶段名列和数字列的宽度放在这一层，因为下面**两块** grid（主流程图
       .funnel-grid、轮次 .funnel-rounds-grid）要共用同一个值 —— 只有第一列宽度
       一致，轮次那几行才对得齐主图的阶段名。同一个数写两遍迟早只改一处。
       写成变量还有第二个理由：窄屏那份覆盖如果直接写 `88px minmax(0, 1fr)`，
       就会撞上 tests/js/identity.test.mjs 那条守卫 —— 它禁止 CSS 里出现
       `<数字>px minmax(0, 1fr)`，防的是有人绕开 --nav-width 硬写侧栏宽度。
       那条守卫是对的，不该为这里放宽；走变量本来就是更对的写法。 */
    --funnel-name-w: 104px;
    --funnel-num-w: 62px;
    display: grid;
    gap: 10px;
    min-height: 92px;
    padding: 16px;
}

/* ═══════════════ 全链路流程图（2026-09-10 uu 拍板的 v2 漏斗样式） ═══════════════

   五格一排：阶段名 │ 漏斗（灰壳 + 色条 + 层间转化率）│ 到达 │ 分支线 │ 终点。
   五格必须**同高**（都吃 --funnel-h，由 JS 按层数算出来写在 .funnel-grid 上），
   否则各列的第 i 项落在不同的 y 上，引线、干线、终点节点会整体错开。

   ⚠️ --bar-h / --rate-h 和 dashboard.js 的 `FLOW_GEOMETRY` 是同一件事写了两处：
   分支线是 SVG，坐标只能是数字、读不到 CSS 变量。改这两个值必须同时改那边，
   否则支线接不到色条上（线歪几个像素，不报错）。守卫用例：
   tests/js/dashboard-flow.test.mjs。 */
.funnel-grid {
    --bar-h: 34px;
    --rate-h: 20px;
    display: grid;
    grid-template-columns: var(--funnel-name-w) minmax(220px, 1fr) var(--funnel-num-w) 156px 136px;
    align-items: start;
    gap: 0 10px;
    /* 左右不再补 padding：外面那层 .funnel-list 已经给了 16px，
       这里再给一次就是 32px，右边那列终点节点会被挤窄。 */
    padding: 4px 0 0;
}

.funnel-col-head {
    margin-bottom: 9px;
    padding-bottom: 7px;
    border-bottom: 1px solid var(--border);
    color: var(--text-secondary);
    font-size: 11px;
    letter-spacing: .4px;
}

.funnel-col-head.is-center {
    text-align: center;
}

.funnel-col-head.is-right {
    text-align: right;
}

/* ── 左：阶段名。右对齐贴着引线起点，nowrap 是硬要求：最长的「Offer 进行中」
      被截成「Offer 进...」就丢了信息，104px 放得下。 ── */
.funnel-names {
    height: var(--funnel-h);
}

.funnel-name-cell {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    height: var(--bar-h);
    font-size: 12.5px;
    text-align: right;
    white-space: nowrap;
}

/* 层与层之间的空隙。三个"堆叠列"都用它占位，保证同一层在各列里对齐。 */
.funnel-gap {
    height: var(--rate-h);
}

/* ── 中：漏斗本体 ── */
.funnel-stack {
    position: relative;
    height: var(--funnel-h);
}

/* 灰壳的顶点跟着各层真实条宽走，不是随手画的装饰梯形。
   z-index:0 是必须的 —— 它是绝对定位元素，默认压在普通文字之上，不压回去会把
   层间转化率整行盖掉（2026-09-10 uu 截图抓到；只验"文字有没有被容器切断"
   验不出这种图层遮挡）。 */
.funnel-shell {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
}

.funnel-shell polygon {
    fill: #eceff4;
}

.funnel-node {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    height: var(--bar-h);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.funnel-node:hover .funnel-bar {
    filter: brightness(1.08);
}

.funnel-node:hover .funnel-lead::before,
.funnel-node:hover .funnel-lead::after {
    border-top-color: var(--primary);
}

.funnel-node:focus-visible {
    border-radius: 4px;
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* 引线：从格子左右边缘各画一条细线，正好停在色条边上。条越窄引线越长，
   视觉上就是"左边的标签牵到漏斗上"。 */
.funnel-lead {
    position: absolute;
    inset: 0;
}

.funnel-lead::before,
.funnel-lead::after {
    content: "";
    position: absolute;
    top: 50%;
    width: calc((100% - var(--w)) / 2);
    border-top: 1px solid var(--border-strong);
}

.funnel-lead::before {
    left: 0;
}

.funnel-lead::after {
    right: 0;
}

.funnel-bar-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.funnel-bar {
    width: var(--w);
    height: 100%;
    border-radius: 3px;
    background: var(--primary);
    box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .08);
    transition: filter .12s;
}

/* 已入职是好的终点，用系统既有的 --success。不为好看，为了一眼看出哪层是成功。 */
.funnel-node.is-hired .funnel-bar {
    background: var(--success);
}

/* 层间转化率。position + z-index 同样是必须的，否则被灰壳盖住（见 .funnel-shell）。 */
.funnel-rate {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    height: var(--rate-h);
    color: var(--text-secondary);
    font-size: 11.5px;
}

.funnel-rate b {
    color: var(--text);
    font-weight: 600;
}

.funnel-rate.is-none {
    color: #9098a5;
}

/* ── 右：到达人数。放在条**外**，色条上一个字都不放 —— 最后几层只有百分之几宽，
      压上去就完全读不出来了。 ── */
.funnel-nums {
    height: var(--funnel-h);
}

.funnel-num-cell {
    display: flex;
    align-items: center;
    height: var(--bar-h);
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}

.funnel-num-cell small {
    margin-left: 3px;
    color: var(--text-secondary);
    font-size: 11px;
}

/* ── 分支线 ── */
.funnel-wires {
    height: var(--funnel-h);
}

/* overflow:visible 是必须的：最上面那条支线的数字标签字顶会露到视框外几个像素，
   默认 hidden 会剪掉半个字（2026-09-10 实测）。放开溢出比改 viewBox 安全 ——
   改 viewBox 高度会让 y 坐标和像素不再 1:1，右栏终点节点的对齐就得重算。 */
.funnel-wires svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.funnel-wire-num {
    font-family: inherit;
    font-size: 9.5px;
    font-variant-numeric: tabular-nums;
}

/* ── 终点节点：绝对定位到干线出口的精确 y，用 translateY(-50%) 自对齐，
      不靠数节点高度（v1 按槽位数节距，少一个空槽整体错 88px，2026-09-10 踩过）。 ── */
.funnel-terminals {
    position: relative;
    height: var(--funnel-h);
}

.funnel-terminal {
    position: absolute;
    left: 0;
    right: 0;
    display: block;
    padding: 8px 10px;
    transform: translateY(-50%);
    border: 1px solid var(--border);
    border-left: 3px solid var(--c);
    border-radius: 8px;
    background: var(--surface-soft);
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.funnel-terminal:hover {
    border-color: var(--border-strong);
    background: #f1f4ff;
}

.funnel-terminal:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.funnel-terminal-name {
    display: block;
    font-size: 12.5px;
}

.funnel-terminal-num {
    color: var(--c);
    font-size: 17px;
    font-variant-numeric: tabular-nums;
}

.funnel-terminal-note {
    display: block;
    margin-top: 1px;
    color: var(--text-secondary);
    font-size: 10.5px;
}

/* 窄屏兜底文字。宽屏藏起来（那边有线和终点节点，念两遍是啰嗦），
   窄屏才显示 —— 窄屏把分支线整块隐藏会**丢掉信息**，不是布局降级。 */
.funnel-branch-text {
    display: none;
}

.funnel-empty {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
}

/* 「面试中」拆成各轮。第三列 190px 不是 62px：轮次行要放「96 → 62 · 通过 64.6%」，
   按主图那样给 62px 会换行、字被切掉一半（2026-09-10 实测踩过）。 */
.funnel-rounds {
    /* 上下间距交给 .funnel-list 的 gap，这里只负责那条分隔虚线。 */
    margin: 0;
    padding-top: 12px;
    border-top: 1px dashed var(--border);
}

.funnel-rounds-head {
    margin: 0 0 6px;
    color: var(--text-secondary);
    font-size: 11.5px;
}

.funnel-rounds-grid {
    display: grid;
    grid-template-columns: var(--funnel-name-w) minmax(180px, 1fr) 190px;
    align-items: center;
    gap: 0 10px;
}

/* 没有面试流水时那句话要横跨三列，否则被挤在第一列里换行成一条竖条。 */
.funnel-rounds-grid .funnel-empty {
    grid-column: 1 / -1;
}

.funnel-round-name {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    height: 26px;
    color: var(--text-secondary);
    font-size: 11.5px;
    white-space: nowrap;
}

.funnel-round-track {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 26px;
}

.funnel-round-bar {
    width: var(--w);
    height: 16px;
    border-radius: 3px;
    background: #8aa4fd;
}

.funnel-round-num {
    display: flex;
    align-items: center;
    height: 26px;
    color: var(--text-secondary);
    font-size: 12px;
    /* nowrap 是硬要求：这一格文字最长，换行会把「通过 64.6%」甩到下一行去。 */
    white-space: nowrap;
}

.summary-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    padding: 16px;
}

.summary-item {
    padding: 12px;
    border: 1px solid #edf0f4;
    border-radius: 8px;
    background: var(--surface-soft);
}

.summary-item span {
    display: block;
    color: var(--text-secondary);
    font-size: 11px;
}

.summary-item strong {
    display: block;
    margin-top: 5px;
    font-size: 22px;
}

/* 数据源提示位。原先这里钉死一块 .summary-alert（「紧急事务暂不可用」），
   事务模块建好之后那句话变成了假话，却因为写在 HTML 里没人会去改。
   现在容器常驻、内容由后端下发：没话说就整块 hidden，不占位也不留空框。
   `display: grid` 会盖掉 hidden 的默认 display:none，所以下面要显式补一条。 */
.summary-notices {
    display: grid;
    grid-column: 1 / -1;
    gap: 8px;
}

.summary-notices[hidden] {
    display: none;
}

.summary-alert {
    display: flex;
    grid-column: 1 / -1;
    gap: 10px;
    padding: 11px 12px;
    border: 1px solid #f1d8a8;
    border-radius: 8px;
    background: #fffaf0;
}

.alert-mark {
    display: grid;
    width: 22px;
    height: 22px;
    flex: 0 0 22px;
    place-items: center;
    border-radius: 50%;
    background: #fff0c9;
    color: var(--warning);
    font-weight: 800;
}

.summary-alert strong {
    color: #7a4f0c;
    font-size: 12px;
}

.summary-alert p {
    margin: 3px 0 0;
    color: #886b3d;
    font-size: 11px;
    line-height: 1.5;
}

/* 两种严重程度要看得出区别：黄色是「读到的还是新的，只是暂时写不回去」，
   红色是「同步已经断了，屏幕上的数字可能已经落后」。后者要人立刻去处理，
   前者可以等手上的事做完。同一个颜色的话，人会按同一个紧急度对待，
   于是要么把要紧的拖了，要么被不要紧的打断。 */
.summary-alert.is-error {
    border-color: #f0c4c4;
    background: #fff6f6;
}

.summary-alert.is-error .alert-mark {
    color: var(--danger);
}

.summary-alert.is-error strong {
    color: #8c2f2f;
}

.summary-alert.is-error p {
    color: #8a5a5a;
}

.table-panel {
    overflow: hidden;
}

.table-toolbar {
    min-height: 66px;
}

.table-meta {
    display: flex;
    align-items: center;
    gap: 12px;
    color: var(--text-secondary);
    font-size: 12px;
}

.table-density {
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface-soft);
}

.table-scroll {
    overflow: auto;
    border-bottom: 1px solid #edf0f4;
}

table {
    width: 100%;
    min-width: 1040px;
    border-collapse: separate;
    border-spacing: 0;
    background: #ffffff;
}

thead {
    position: sticky;
    top: 0;
    z-index: 2;
}

th,
td {
    height: 48px;
    padding: 0 14px;
    border-right: 1px solid #edf0f4;
    border-bottom: 1px solid #edf0f4;
    text-align: left;
    white-space: nowrap;
}

th:last-child,
td:last-child {
    border-right: 0;
}

th {
    height: 44px;
    background: #f7f8fa;
    color: #525d6d;
    font-size: 12px;
    font-weight: 700;
}

td {
    color: #3f4857;
    font-size: 13px;
}

tbody tr:hover td {
    background: #fafbff;
}

.candidate-name {
    color: #202735;
    font-weight: 700;
}

.muted-cell {
    color: #88919f;
}

.stage-badge,
.source-badge {
    display: inline-flex;
    min-height: 26px;
    align-items: center;
    padding: 0 8px;
    border: 1px solid #dbe2ff;
    border-radius: 999px;
    background: #f4f6ff;
    color: #3451b2;
    font-size: 11px;
    font-weight: 700;
}

.source-badge {
    border-color: #e1e5eb;
    background: #f7f8fa;
    color: #5b6575;
}

.empty-cell,
.error-cell {
    height: 210px;
    text-align: center;
}

.empty-state,
.table-error-state {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 8px;
}

.empty-state strong,
.table-error-state strong {
    color: #303846;
}

.empty-state p,
.table-error-state p {
    margin: 0 0 5px;
    color: var(--text-secondary);
}

.table-skeleton {
    display: block;
    width: 75%;
    height: 12px;
}

.pagination-bar {
    display: flex;
    min-height: 56px;
    align-items: center;
    justify-content: flex-end;
    gap: 18px;
    padding: 8px 16px;
    color: var(--text-secondary);
    font-size: 12px;
}

.pagination-controls {
    display: flex;
    align-items: center;
    gap: 8px;
}

.page-button {
    width: 40px;
    padding: 0;
    font-size: 20px;
}

.page-button:disabled {
    background: #f5f6f8;
    color: #b8bec8;
    cursor: not-allowed;
}

.current-page {
    display: grid;
    min-width: 36px;
    height: 36px;
    place-items: center;
    border-radius: 7px;
    background: var(--primary);
    color: #ffffff;
    font-weight: 700;
}

/* === 弹窗外壳（通用，配合 static/js/modal.js） ===
   2026-09-07「录入简历」从整页收成弹窗时新建。此前项目里一个 overlay 都没有，
   所以遮罩、居中、层级全是这里新定的。刻意写成不带业务的通用类：下一个要弹窗的
   功能直接复用这套骨架，不各自再写一份遮罩——两份遮罩迟早在层级上打起来。

   `display` 带 :not([hidden]) 是沿用本文件既有写法。本项目第 66 行有
   `[hidden] { display: none !important }`，光靠它其实就压得住，所以这不是修补，
   只是别让某一天有人去掉那个 !important 时这里跟着塌。 */
.modal-overlay:not([hidden]) {
    position: fixed;
    /* 100 > 身份切换面板的 40（本文件最高的一个）。弹窗必须盖住它，
       否则左下角那块浮层会从遮罩里穿出来。 */
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    inset: 0;
    background: rgba(15, 23, 36, 0.45);
}

.modal-dialog {
    display: flex;
    width: min(1280px, 100%);
    /* 高度随内容长，长到装不下就顶到视口为止（外层留了 24px padding）。
       不写死 height:100%：第①步只有两个拖放框，撑满一屏会是一大片空白。 */
    max-height: 100%;
    flex-direction: column;
    overflow: hidden;
    border-radius: 14px;
    background: var(--surface);
    box-shadow: 0 18px 48px rgba(15, 23, 36, 0.28);
}

.modal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex: 0 0 auto;
    padding: 13px 18px;
    border-bottom: 1px solid var(--border);
}

.modal-header h2 {
    margin: 0;
    font-size: 18px;
}

.modal-subtitle {
    margin: 4px 0 0;
    color: var(--text-secondary);
    font-size: 12px;
}

.modal-close {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
}

.modal-close:hover {
    border-color: var(--border-strong);
    color: var(--text);
}

/* 只有中间这块滚，头和脚钉住。底部那两颗步骤按钮跟着滚走的话，第②步那张长表单
   要翻到底才知道自己被什么挡住了。背景用 --page 而不是白色：里面的卡片都是白底，
   衬在白上就看不出边界了（原来它铺在灰色页面上）。 */
.modal-body {
    min-height: 0;
    flex: 1 1 auto;
    padding: 14px 18px;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--page);
}

.modal-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex: 0 0 auto;
    padding: 11px 18px;
    border-top: 1px solid var(--border);
    background: var(--surface);
}

.modal-footer-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}

/* 弹窗开着时锁掉背景滚动（类由 modal.js 加）。不锁的话滚轮会把后面那一整页带走，
   关掉弹窗时人已经不在原来看的位置上了。 */
body.has-modal {
    overflow: hidden;
}

/* === 人才库 · 详情抽屉与激活弹窗（T5b，2026-09-15） ===
   两个都长在上面那套遮罩骨架上，只改"贴哪一边、多宽"，不另写一份遮罩——
   两份遮罩迟早在层级上打起来（这也是上面那段注释立下的规矩）。

   叠层不用改 z-index：所有遮罩共用 100，DOM 里靠后的自然盖在上面，
   所以激活弹窗在 index.html 里写在抽屉之后。 */

/* 抽屉贴右侧、铺满整高。详情有近二十行，居中的方框会变成一根又高又窄的柱子；
   从右边推进来还能保住"名单仍在左边"的感觉——激活前多半要在名单和档案之间
   来回看。所以 padding 归零、圆角只留左边两个，贴边才像抽屉。 */
.modal-overlay.is-drawer:not([hidden]) {
    justify-content: flex-end;
    padding: 0;
}

.modal-dialog.is-drawer {
    width: min(460px, 100%);
    height: 100%;
    border-radius: 14px 0 0 14px;
}

/* 激活那张表只有四格，用弹窗默认的 min(1280px) 会让四个下拉横在一大片空白中间。 */
.modal-dialog.is-compact {
    width: min(560px, 100%);
}

/* 详情字段表：标签一列、值一列。值那列用 minmax(0, 1fr) 让长内容换行，
   不写的话一个超长的专业名会把整张表撑宽、逼出横向滚动条。 */
.talent-detail-list {
    display: grid;
    align-items: baseline;
    margin: 0;
    gap: 6px 12px;
    /* 和 .application-evidence-row 同一个写法：标签列有下限、能按内容长的那几个
       字段（「投递次数（可见 / 全部）」）自己撑开，值列永远还有 0 宽下限可缩。
       写死 96px 的话长标签会折成三行，值那一列跟着掉下去，看着像排版坏了。 */
    grid-template-columns: minmax(96px, auto) minmax(0, 1fr);
}

.talent-detail-list dt {
    color: var(--text-secondary);
    font-size: 12px;
}

.talent-detail-list dd {
    margin: 0;
    overflow-wrap: anywhere;
    color: var(--text);
    font-size: 13px;
}

/* 激活表单单列纵向。四格横排在 560px 里会把下拉压到看不清选项。 */
.talent-activate-form {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 12px;
}

/* 成功回执。配色照 .resume-success 那一份，两处报的是同一件事（投递记录已建好），
   长得不一样只会让人以为是两种不同的结果。 */
.talent-activate-success {
    margin: 12px 0 0;
    padding: 12px 14px;
    border: 1px solid #b8decf;
    border-radius: 9px;
    background: #f1fbf7;
    color: var(--text);
    font-size: 13px;
    line-height: 1.6;
}

/* === 三步向导（「录入简历」弹窗，2026-09-07） ===
   当前步只用 aria-current="step" 标，不另加一个 .is-active 类：两处状态迟早不同步，
   而不同步的时候读屏软件报的和屏幕上高亮的会是不同的两步。 */
.intake-step-rail {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 0 0 14px;
    padding: 0;
    color: var(--text-secondary);
    font-size: 12px;
    list-style: none;
}

.intake-step-rail li {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px 5px 6px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
}

.intake-step-rail li[aria-current="step"] {
    border-color: var(--primary);
    color: var(--primary);
    font-weight: 600;
}

.intake-step-index {
    display: grid;
    width: 18px;
    height: 18px;
    place-items: center;
    border-radius: 50%;
    background: var(--border-strong);
    color: var(--surface);
    font-size: 11px;
    font-weight: 700;
}

.intake-step-rail li[aria-current="step"] .intake-step-index {
    background: var(--primary);
}

/* 已经走过的步骤。这个类由 resume-intake.js 按当前步算，不靠 :nth-child 猜顺序。 */
.intake-step-rail li.is-done {
    border-color: #c6e6d8;
    color: var(--success);
}

.intake-step-rail li.is-done .intake-step-index {
    background: var(--success);
}

/* 底部那行提示：缺什么就逐项列在这里，所以给它吃掉整行剩余宽度，
   不裁成一行省略号——「还缺 2 项」这种话看了也不知道该改哪。 */
.intake-step-hint {
    min-width: 0;
    margin: 0;
    flex: 1 1 auto;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.5;
}

.intake-step-hint.is-blocked {
    color: var(--danger);
}

.resume-boundary-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    margin-bottom: 14px;
    padding: 11px 14px;
    border: 1px solid #c6e6d8;
    border-radius: 9px;
    background: #f1fbf7;
    color: #246348;
    font-size: 12px;
    line-height: 1.5;
}

.resume-boundary-banner strong {
    color: var(--success);
}

/* 三列两行：左边两个拖放窗口跨两行，右边上方是标题和翻页、下方是横向铺开的队列。
   之前靠自动排布，结果标题独占宽格子、队列被挤成窄条，留下一大片死白。 */
.resume-intake-strip {
    display: grid;
    grid-template-columns: minmax(320px, 1.3fr) minmax(0, 1fr) auto;
    grid-template-areas:
        "actions heading nav"
        "actions queue   queue";
    grid-template-rows: min-content minmax(0, 1fr);
    align-content: start;
    align-items: center;
    gap: 8px 14px;
    margin-bottom: 14px;
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

/* 队列为空时收起翻页按钮，整条 strip 只剩两个窗口加一行提示。 */
.resume-intake-strip.is-empty {
    grid-template-areas:
        "actions heading heading"
        "actions queue   queue";
}

.resume-intake-strip.is-empty .resume-queue-navigation {
    display: none;
}

.resume-file-actions {
    display: grid;
    min-width: 0;
    grid-area: actions;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-content: start;
    gap: 8px;
}

.application-form-drop-zone {
    min-width: 0;
    overflow: hidden;
    border: 1px dashed #aeb8c7;
    border-radius: 10px;
    background: #fafbfc;
    transition: border-color 140ms ease, background-color 140ms ease, box-shadow 140ms ease;
}

.application-form-drop-zone:hover,
.application-form-drop-zone.is-dragging {
    border-color: var(--primary);
    background: #f4f6ff;
}

.application-form-drop-zone.is-dragging {
    box-shadow: 0 0 0 3px rgba(63, 81, 181, 0.18);
}

.application-form-drop-zone.is-disabled {
    border-color: #d7dce4;
    background: #f5f6f8;
}

.application-form-trigger {
    display: grid;
    width: 100%;
    min-height: 56px;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 9px;
    padding: 8px 10px 5px;
    border: 0;
    background: transparent;
    color: var(--text);
    text-align: left;
    cursor: pointer;
}

.application-form-trigger:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: -3px;
    border-radius: 8px;
}

.application-form-trigger strong,
.application-form-trigger small {
    display: block;
}

.application-form-trigger strong {
    font-size: 12px;
}

.application-form-trigger small {
    margin-top: 2px;
    color: var(--text-secondary);
    font-size: 10px;
    font-weight: 500;
}

.application-form-icon {
    display: grid;
    width: 34px;
    height: 34px;
    place-items: center;
    border-radius: 8px;
    background: rgba(63, 81, 181, 0.1);
    color: var(--primary);
    font-size: 9px;
    font-weight: 800;
}

.text-button {
    min-height: 36px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: #ffffff;
    color: var(--primary);
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
}

.application-form-trigger[aria-disabled="true"] {
    color: #929aaa;
    cursor: not-allowed;
    pointer-events: none;
}

.application-form-meta {
    display: flex;
    min-width: 0;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 0 10px 8px 53px;
}

.application-form-status {
    min-width: 0;
    color: var(--text-secondary);
    font-size: 11px;
    overflow-wrap: anywhere;
}

.application-form-status.is-error {
    color: var(--danger);
}

.application-form-actions,
.application-form-recovery {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 6px;
}

.application-form-recovery[hidden] {
    display: none;
}

.application-form-recovery button:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.text-button {
    padding: 0 9px;
    color: var(--danger);
}

.resume-queue-heading {
    display: flex;
    grid-area: heading;
    align-items: center;
    gap: 8px;
}

.resume-queue-heading h3 {
    margin: 0;
    font-size: 13px;
}

.resume-intake-strip .resume-queue {
    grid-area: queue;
    align-self: start;
}

.resume-intake-strip .resume-queue-navigation {
    grid-area: nav;
    min-width: 0;
    justify-content: flex-end;
    padding: 0;
}

.resume-evidence-tabs {
    display: flex;
    gap: 4px;
    padding: 8px 12px 0;
    border-bottom: 1px solid #edf0f4;
}

.resume-evidence-tabs button {
    min-height: 38px;
    padding: 0 12px;
    border: 0;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: var(--text-secondary);
    font-weight: 700;
    cursor: pointer;
}

.resume-evidence-tabs button[aria-selected="true"] {
    border-bottom-color: var(--brand);
    color: var(--text);
}

.application-evidence-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 12px;
    padding: 10px 14px 16px;
}

.application-evidence-row {
    display: grid;
    min-width: 0;
    grid-template-columns: minmax(78px, auto) minmax(0, 1fr);
    gap: 3px 10px;
    padding: 9px 0;
    border-bottom: 1px solid #edf0f4;
}

.application-evidence-label,
.application-evidence-coordinate {
    color: var(--text-secondary);
    font-size: 10px;
}

.application-evidence-value {
    min-height: 1.5em;
    overflow-wrap: anywhere;
    font-size: 12px;
}

.application-evidence-coordinate {
    grid-column: 2;
}

.resume-review-column {
    overflow: visible;
    border: 0;
    background: transparent;
    box-shadow: none;
}

.resume-review-column form {
    display: grid;
    min-width: 0;
    gap: 12px;
}

.resume-review-column > form > .resume-column-header {
    border: 1px solid var(--border);
    border-radius: 11px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.resume-review-surface {
    min-width: 0;
    padding: 14px;
    border: 1px solid var(--border);
    border-radius: 11px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.resume-surface-heading {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid #edf0f4;
}

.resume-surface-heading h4 {
    margin: 0;
    font-size: 14px;
}

.resume-review-surface .resume-item-status {
    margin: 12px 0 0;
}

.application-review-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 16px;
}

.application-review-item {
    min-width: 0;
    padding: 8px 0;
    border-bottom: 1px solid #edf0f4;
}

.application-review-item span {
    color: var(--text-secondary);
    font-size: 10px;
}

.application-review-item p {
    min-height: 1.5em;
    margin: 3px 0 0;
    overflow-wrap: anywhere;
    font-size: 11px;
    line-height: 1.5;
}

.application-review-wide {
    grid-column: 1 / -1;
}

.application-form-conflicts {
    display: grid;
    gap: 8px;
    margin-top: 12px;
}

.application-conflict-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px;
    padding: 10px;
    border: 1px solid #e7bd76;
    border-radius: 8px;
    background: #fff9ec;
}

.application-conflict-copy {
    display: grid;
    min-width: 0;
    gap: 2px;
}

.application-conflict-copy strong,
.application-conflict-copy span {
    overflow-wrap: anywhere;
    font-size: 11px;
}

.application-conflict-copy span {
    color: #6e5a36;
}

.application-conflict-actions {
    display: flex;
    align-items: center;
    gap: 6px;
}

.application-conflict-actions button {
    min-height: 36px;
    padding: 0 9px;
    font-size: 10px;
}

.resume-global-error,
.resume-submit-error {
    color: var(--danger);
}

.resume-global-error {
    margin: 0 0 14px;
    padding: 10px 12px;
    border: 1px solid #efc1c5;
    border-radius: 8px;
    background: #fff5f6;
}

.resume-intake-notice {
    margin: 0 0 14px;
    padding: 10px 12px;
    border: 1px solid #cfe0f7;
    border-radius: 8px;
    background: #f4f8ff;
    color: #2c4a75;
    font-size: 12px;
}

.resume-workbench {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    align-items: stretch;
    gap: 14px;
    min-width: 0;
}

.resume-column {
    min-width: 0;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 11px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

/* 宽屏下弹窗内部排成一列，工作台自动吃掉标题栏、边界提示条、步骤条之后剩下的高度，
   左右两栏因此天然等高，长证据和 17 行确认区各自内部滚动，不再撑到近两屏。
   用 flex 剩余空间而不是手调的 calc 常数：顶部多一条提示时不会溢出。

   2026-09-07 从 #resume-intake-page 改到弹窗：靠的是 .modal-dialog 的
   max-height:100% —— 第②③步内容天然比一屏高，弹窗顶到视口上限，body 就被压成
   「可用高度」，工作台跟着收进来。第①步内容矮，弹窗自己就矮，不会撑出一片空白。
   所有需要 display 的选择器都带 :not([hidden])，和本文件其余部分保持一致。 */
@media (min-width: 1101px) and (min-height: 720px) {
    #resume-intake-modal .modal-body {
        display: flex;
        flex-direction: column;
        /* 这一层不滚了，改成让工作台自己收缩、两栏各自内滚。
           留着 auto 的话会变成"外面滚一层、里面再滚一层"，滚轮到底会跳。 */
        overflow: hidden;
    }

    #resume-intake-modal .modal-body > .resume-workbench:not([hidden]) {
        /* 顶部内容异常高时不把工作台压没，宁可让它溢出（上面 overflow:hidden 会裁掉，
           但 360px 已经够放下两栏的头部和几行内容）。 */
        min-height: 360px;
        flex: 1 1 auto;
        grid-template-rows: minmax(0, 1fr);
    }

    .resume-evidence-column {
        display: flex;
        min-height: 0;
        max-height: 100%;
        flex-direction: column;
    }

    .resume-evidence-content:not([hidden]) {
        display: flex;
        min-height: 0;
        flex: 1 1 auto;
        flex-direction: column;
    }

    /* 证据栏只留一个滚动条：原文 pre 自己撑满剩余高度并滚动。 */
    #resume-evidence-panel-resume:not([hidden]) {
        display: flex;
        min-height: 0;
        flex: 1 1 auto;
        flex-direction: column;
    }

    #resume-evidence-panel-form:not([hidden]) {
        min-height: 0;
        flex: 1 1 auto;
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .resume-page-text {
        height: auto;
        min-height: 140px;
        max-height: none;
        flex: 1 1 auto;
    }

    .resume-review-column {
        min-height: 0;
    }

    .resume-review-column > form {
        max-height: 100%;
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .resume-review-column > form > .resume-column-header {
        position: sticky;
        top: 0;
        z-index: 1;
    }

    .resume-verification-fields .application-review-grid {
        max-height: 300px;
        padding-right: 6px;
        overflow-y: auto;
        overscroll-behavior: contain;
    }
}

.resume-column-header {
    display: flex;
    min-height: 66px;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 13px 14px;
    border-bottom: 1px solid #edf0f4;
}

.resume-column-header h3 {
    margin: 3px 0 0;
    overflow: hidden;
    font-size: 16px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.resume-count {
    flex: 0 0 auto;
    color: var(--text-secondary);
    font-size: 11px;
}

.resume-drop-zone {
    display: flex;
    min-height: 48px;
    align-items: center;
    justify-content: flex-start;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin: 0;
    padding: 9px 12px;
    border: 1px dashed #aeb8c7;
    border-radius: 10px;
    background: #fafbfc;
    color: var(--text-secondary);
    text-align: left;
    cursor: pointer;
}

.resume-drop-zone:hover,
.resume-drop-zone.is-dragging {
    border-color: var(--primary);
    background: #f4f6ff;
}

.resume-drop-zone:focus-within {
    border-color: var(--primary);
    outline: none;
    background: #f4f6ff;
    box-shadow: 0 0 0 3px rgba(63, 81, 181, 0.22);
}

.resume-drop-zone strong {
    color: var(--text);
}

.resume-drop-zone > span:last-of-type {
    flex-basis: 100%;
    font-size: 11px;
}

.resume-drop-icon {
    display: grid;
    width: 42px;
    height: 42px;
    place-items: center;
    border-radius: 10px;
    background: rgba(216, 12, 24, 0.1);
    color: var(--brand);
    font-size: 11px;
    font-weight: 800;
}

.resume-queue {
    display: flex;
    gap: 7px;
    min-width: 0;
    padding: 0;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
}

.resume-queue-item {
    display: grid;
    min-width: 180px;
    max-width: 260px;
    flex: 0 1 220px;
    grid-template-columns: minmax(0, 1fr) auto;
    overflow: hidden;
    border: 1px solid #e4e7ec;
    border-radius: 8px;
}

.resume-queue-item.is-selected {
    border-color: #9eb0ff;
    background: #f5f7ff;
}

.resume-queue-select,
.resume-queue-remove {
    border: 0;
    background: transparent;
    cursor: pointer;
}

.resume-queue-select {
    display: flex;
    min-width: 0;
    align-items: flex-start;
    flex-direction: column;
    gap: 3px;
    padding: 10px;
    text-align: left;
}

.resume-queue-select strong,
.resume-queue-select span {
    overflow: hidden;
    max-width: 100%;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.resume-queue-select span {
    color: var(--text-secondary);
    font-size: 11px;
}

.resume-queue-remove {
    padding: 0 10px;
    color: var(--danger);
    font-size: 11px;
}

.resume-empty-copy {
    margin: 12px 0;
    color: var(--text-secondary);
    font-size: 12px;
    text-align: center;
}

.resume-intake-strip .resume-empty-copy {
    margin: 0;
    text-align: left;
}

.resume-queue-navigation,
.resume-page-navigation {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 0 0;
}

.resume-queue-navigation button,
.resume-page-navigation button {
    min-width: 0;
    flex: 1;
}

.resume-intake-strip .resume-queue-navigation button {
    flex: 0 0 auto;
}

.resume-column-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    padding: 64px 30px;
    color: var(--text-secondary);
    text-align: center;
}

.resume-column-empty > span {
    margin-bottom: 8px;
    color: #c8ced8;
    font-size: 38px;
}

.resume-column-empty strong {
    color: var(--text);
}

.resume-column-empty p {
    max-width: 250px;
    line-height: 1.6;
}

.resume-evidence-toolbar h3 {
    max-width: 220px;
}

.resume-local-note,
.resume-focused-evidence {
    margin: 12px 14px 0;
    padding: 9px 10px;
    border-radius: 7px;
    font-size: 11px;
    line-height: 1.55;
}

.resume-local-note {
    background: #f1fbf7;
    color: #246348;
}

.resume-focused-evidence {
    border: 1px solid #e3e7ef;
    background: #fafbfc;
    color: var(--text-secondary);
}

.resume-page-text {
    height: clamp(240px, 44vh, 520px);
    max-height: 610px;
    margin: 12px 14px 0;
    padding: 15px;
    overflow: auto;
    border: 1px solid #e2e6ed;
    border-radius: 8px;
    background: #fbfcfd;
    color: #323a47;
    font: 12px/1.8 "Cascadia Mono", "Microsoft YaHei", monospace;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}

.resume-review-column form {
    min-width: 0;
}

.resume-item-status {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    margin: 14px;
    padding: 11px 12px;
    border: 1px solid #dbe2ff;
    border-radius: 8px;
    background: #f6f8ff;
}

.resume-item-status p {
    margin: 3px 0 0;
    color: var(--text-secondary);
    font-size: 11px;
}

.resume-item-meta {
    display: flex;
    flex-direction: column;
    color: var(--text-secondary);
    font-size: 10px;
    text-align: right;
}

.resume-item-meta code {
    color: #394357;
    font-size: 10px;
}

.resume-item-status button {
    grid-column: 1 / -1;
}

.resume-fieldset {
    min-width: 0;
    margin: 16px 0 0;
    padding: 0;
    border: 0;
}

.resume-fieldset legend {
    padding: 0 6px;
    color: #4e5868;
    font-size: 12px;
    font-weight: 700;
}

.resume-form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 11px;
}

.resume-form-field {
    display: grid;
    min-width: 0;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 6px;
}

.resume-form-field > span {
    grid-column: 1 / -1;
    color: var(--text-secondary);
    font-size: 11px;
}

.resume-form-field b,
.resume-reason-field b {
    color: var(--danger);
}

.resume-form-field input,
.resume-form-field select,
.resume-reason-field textarea {
    width: 100%;
    min-width: 0;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: #ffffff;
    color: var(--text);
}

.resume-form-field input,
.resume-form-field select {
    min-height: 38px;
    padding: 0 9px;
}

.resume-form-field input[readonly] {
    background: #f6f7f9;
    color: #697383;
}

.resume-workbook-entry-control {
    display: flex;
    min-width: 0;
    align-items: center;
    gap: 8px;
}

.resume-workbook-entry-button {
    min-width: 74px;
    min-height: 34px;
    flex: 0 0 auto;
}

.resume-workbook-entry-button:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.resume-workbook-entry-status {
    min-width: 0;
    color: var(--text-secondary);
    font-size: 10px;
    line-height: 1.45;
    overflow-wrap: anywhere;
}

.resume-form-field button:not(.resume-workbook-entry-button),
.resume-verification-row button {
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--primary);
    font-size: 10px;
    cursor: pointer;
}

.resume-form-wide {
    grid-column: 1 / -1;
}

.resume-verification-fields {
    background: transparent;
}

.resume-verification-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid #edf0f4;
}

.resume-verification-row:last-child {
    border-bottom: 0;
}

.resume-verification-row span {
    color: var(--text-secondary);
    font-size: 10px;
}

.resume-verification-row p {
    margin: 3px 0 0;
    overflow-wrap: anywhere;
    font-size: 11px;
    line-height: 1.55;
}

.resume-dedupe-section {
    margin: 14px;
    padding: 12px;
    border: 1px solid #e4e7ec;
    border-radius: 9px;
}

.resume-dedupe-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.resume-dedupe-heading h4,
.resume-success h4 {
    margin: 0;
}

.resume-dedupe-heading p {
    margin: 3px 0 0;
    color: var(--text-secondary);
    font-size: 10px;
}

.resume-dedupe-results {
    display: grid;
    gap: 7px;
    margin-top: 10px;
}

.resume-dedupe-clear {
    margin: 0;
    padding: 9px;
    border-radius: 7px;
    background: #f1fbf7;
    color: #246348;
    font-size: 11px;
}

.resume-match-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 8px;
    padding: 9px;
    border: 1px solid #efd5a9;
    border-radius: 7px;
    background: #fffaf0;
}

.resume-match-copy {
    display: grid;
    min-width: 0;
    gap: 2px;
}

.resume-match-copy small {
    overflow: hidden;
    color: #756442;
    font-size: 10px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.resume-decision-options {
    display: flex;
    flex-wrap: wrap;
    gap: 9px 16px;
    margin-top: 12px;
    font-size: 11px;
}

.resume-reason-field {
    display: grid;
    gap: 5px;
    margin-top: 12px;
    color: var(--text-secondary);
    font-size: 11px;
}

.resume-reason-field textarea {
    min-height: 74px;
    padding: 8px 9px;
    resize: vertical;
}

.resume-submit-panel {
    display: grid;
    gap: 7px;
    margin: 16px 0 0;
    padding: 0;
    border: 0;
    background: transparent;
}

.resume-submit-panel p,
.resume-submit-panel small {
    margin: 0;
    color: var(--text-secondary);
    font-size: 10px;
    line-height: 1.5;
}

.resume-submit-panel .resume-submit-error {
    color: var(--danger);
}

.resume-success {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 11px;
    margin: 16px 0 0;
    padding: 14px;
    border: 1px solid #b8decf;
    border-radius: 9px;
    background: #f1fbf7;
}

.resume-success-mark {
    display: grid;
    width: 30px;
    height: 30px;
    place-items: center;
    border-radius: 50%;
    background: var(--success);
    color: #ffffff;
    font-weight: 800;
}

.resume-success p {
    margin: 5px 0 0;
    color: #416654;
    font-size: 11px;
    line-height: 1.5;
}

.resume-success-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

/* 「这个人投过 N 次」那一句 + 「查看详情」（uu 2026-09-07）。
   琥珀底而不是跟着上面几句一样的绿底：上面在说"录进去了"，这一句在说
   "这一条要多看一眼"，两种意思共用一种颜色就等于没提示。 */
.resume-success-repeat {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 9px 0 0;
    padding: 7px 9px;
    border: 1px solid #f0dcb4;
    border-radius: 7px;
    background: #fdf6e7;
}

/* display: flex 会盖掉浏览器给 [hidden] 的 display: none，必须补这一条。
   不补的话首投也会显示这一格，而里面是一句空话加一颗按钮。 */
.resume-success-repeat[hidden] {
    display: none;
}

/* .text-button 的默认色是危险红（见上面那条覆盖），这里要改回主色：
   「查看详情」是一次跳转，不是删除。 */
.resume-success-repeat-view {
    color: var(--primary);
}

/* 负责人下拉下面那句说明。只在拿不到名单时出现，所以措辞要说清"接下来怎么办"，
   不是只说"失败了"。占满整行：.resume-form-field 是两列网格，不写会被挤到
   下拉右边那一格里，一句话缩成竖排。 */
.resume-owner-hint {
    grid-column: 1 / -1;
    color: var(--text-secondary);
    font-size: 10px;
    line-height: 1.5;
}

.visually-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    overflow: hidden !important;
    clip: rect(1px, 1px, 1px, 1px) !important;
    white-space: nowrap !important;
}

@keyframes skeleton {
    from {
        background-position: 120% 0;
    }
    to {
        background-position: -80% 0;
    }
}

@media (max-width: 1280px) {
    .page {
        padding: 20px;
    }

    .data-stamp > span:nth-child(2) {
        display: none;
    }

    .filter-row {
        flex-wrap: wrap;
    }

    .field-control {
        flex: 1 1 150px;
    }

    .keyword-field {
        flex-basis: 240px;
    }

    .kpi-strip {
        grid-template-columns: repeat(5, minmax(120px, 1fr));
    }

    .kpi-card:nth-child(5) {
        border-right: 0;
    }

    .kpi-card:nth-child(n + 6) {
        border-top: 1px solid #edf0f4;
    }
}

@media (max-width: 1100px) {
    /* 窄屏改成单列竖排，命名区域必须一起重设，否则三列的区域名会把布局撑坏。 */
    .resume-intake-strip,
    .resume-intake-strip.is-empty {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "actions"
            "heading"
            "queue"
            "nav";
        grid-template-rows: none;
        align-items: stretch;
    }

    .resume-intake-strip .resume-queue-navigation {
        justify-content: flex-start;
    }

    .resume-workbench {
        grid-template-columns: minmax(0, 1fr);
    }

    .resume-review-column {
        grid-column: auto;
    }
}

@media (max-width: 900px) {
    .app-shell {
        --nav-width: 72px;
    }

    .page-header,
    .header-actions {
        align-items: flex-start;
        flex-direction: column;
    }

    .insight-grid {
        grid-template-columns: 1fr;
    }

    .stage-distribution {
        grid-template-columns: 1fr;
    }

    /* 窄屏放不下五列（五列本身要 678px，加间距 718px）。让出去的是右边两列
       ——分支线和终点节点，因为它们最宽、也最靠 SVG 像素对齐吃布局。
       ⚠️ 但那两列是**信息**不是装饰：藏了就看不见有多少人被淘汰、放弃、进人才库。
       所以同时把 .funnel-branch-text 那几行文字放出来，把藏掉的数字如实念一遍。
       只藏不补是信息丢失，不算布局降级。 */
    .funnel-list {
        --funnel-name-w: 88px;
        --funnel-num-w: 58px;
    }

    .funnel-grid {
        grid-template-columns: var(--funnel-name-w) minmax(0, 1fr) var(--funnel-num-w);
    }

    .funnel-wires,
    .funnel-terminals,
    .funnel-col-head.is-wire,
    .funnel-col-head.is-terminal {
        display: none;
    }

    .funnel-branch-text {
        display: grid;
        gap: 4px;
        margin: 0;
        padding-top: 10px;
        border-top: 1px dashed var(--border);
    }

    .funnel-branch-line {
        margin: 0;
        color: var(--text-secondary);
        font-size: 11.5px;
    }

    /* 三个终点的合计比每层流出更重要（它是这张图的结论），所以加深一档，
       不跟上面那几行流出混成一片。 */
    .funnel-branch-line.is-terminal {
        color: var(--text);
    }

    /* 轮次那三列一起收窄，否则第三列 190px 会把整块撑出屏幕。
       第一列跟着 --funnel-name-w 走，不再单独写一遍 88px。 */
    .funnel-rounds-grid {
        grid-template-columns: var(--funnel-name-w) minmax(0, 1fr) 160px;
    }

    .data-stamp {
        flex-wrap: wrap;
        white-space: normal;
    }

    .resume-boundary-banner {
        align-items: flex-start;
        flex-direction: column;
        gap: 4px;
    }

    .resume-workbench {
        grid-template-columns: 1fr;
    }

    .resume-review-column {
        grid-column: auto;
    }

    .resume-column-empty {
        padding-block: 40px;
    }
}

@media (max-width: 768px) {
    .kpi-strip {
        overflow-x: auto;
    }

    /* 家底卡窄屏改单列竖排，分隔线从右边挪到底边（对齐 .asset-card:last-child
       那条桌面规则：最后一格不带线）。 */
    .asset-strip {
        grid-template-columns: 1fr;
    }

    .asset-card {
        border-right: 0;
        border-bottom: 1px solid #edf0f4;
    }

    .asset-card:last-child {
        border-bottom: 0;
    }

    .resume-intake-strip,
    .resume-review-surface {
        padding: 12px;
    }

    /* 手机宽度下两个拖拽窗口并排会挤成细条，改成上下叠放。 */
    .resume-file-actions {
        grid-template-columns: minmax(0, 1fr);
    }

    .application-conflict-row {
        grid-template-columns: minmax(0, 1fr);
    }

    .application-form-meta {
        align-items: flex-start;
        flex-direction: column;
        padding-left: 10px;
    }

    .application-evidence-list,
    .application-review-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .application-review-wide {
        grid-column: auto;
    }

    .application-conflict-actions {
        align-items: stretch;
        flex-direction: column;
    }

    .application-conflict-actions button {
        width: 100%;
    }
}

@media (max-width: 480px) {
    .app-shell {
        --nav-width: 60px;
    }

    .page {
        padding: 14px 12px 24px;
    }

    .nav-items {
        padding-right: 4px;
        padding-left: 4px;
    }

    .resume-form-grid {
        grid-template-columns: 1fr;
    }

    .resume-form-wide {
        grid-column: auto;
    }

    .resume-column-header,
    .resume-dedupe-heading,
    .resume-item-status {
        align-items: flex-start;
        grid-template-columns: 1fr;
    }

    .resume-dedupe-heading {
        flex-direction: column;
    }

    .resume-dedupe-heading button {
        width: 100%;
    }

    .resume-page-text {
        height: 280px;
    }
}

@media (max-width: 390px) {
    .page {
        padding-inline: 10px;
    }

    .resume-boundary-banner,
    .resume-intake-strip,
    .resume-review-surface {
        border-radius: 9px;
    }

    .resume-drop-zone,
    .resume-evidence-tabs button {
        min-height: 44px;
    }

    .resume-queue-item {
        min-width: min(180px, calc(100vw - 108px));
    }

    .resume-surface-heading {
        align-items: flex-start;
        flex-direction: column;
        gap: 3px;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        scroll-behavior: auto !important;
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }
}

/* === business-demand start（业务部门需求，Part A + Part D 共用，可整段删除回滚） === */
.demand-panel {
    min-width: 0;
    margin-bottom: 18px;
    padding: 13px 16px 4px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.demand-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
}

.demand-header h2 {
    margin: 2px 0 0;
    font-size: 16px;
}

.demand-meta {
    display: flex;
    align-items: center;
    gap: 8px;
}

.demand-feedback {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 9px 0 0;
    padding: 8px 11px;
    border: 1px solid #f0cfd2;
    border-radius: 8px;
    background: #fdf3f4;
    color: var(--danger);
    font-size: 12px;
}

.demand-feedback[hidden] {
    display: none;
}

.demand-retry {
    min-height: 28px;
    padding: 0 10px;
    font-size: 12px;
}

.demand-empty {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 10px 0 13px;
    padding: 11px 13px;
    border: 1px dashed var(--border-strong);
    border-radius: 8px;
    background: var(--surface-soft);
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.5;
}

.demand-empty[hidden] {
    display: none;
}

.demand-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 11px;
}

.demand-summary[hidden] {
    display: none;
}

.demand-summary-item {
    display: flex;
    min-width: 96px;
    flex-direction: column;
    gap: 2px;
    padding: 8px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-soft);
}

.demand-summary-label {
    color: var(--text-secondary);
    font-size: 11px;
}

.demand-summary-value {
    color: var(--text);
    font-size: 18px;
    font-variant-numeric: tabular-nums;
}

.demand-summary-item.is-status {
    align-items: flex-start;
}

.demand-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 11px;
    padding-top: 11px;
    border-top: 1px solid var(--border);
}

.demand-filters[hidden] {
    display: none;
}

.demand-filters select {
    min-height: 32px;
    padding: 0 8px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font-size: 12px;
}

.demand-expand-all {
    min-height: 32px;
    margin-left: auto;
    padding: 0 11px;
    font-size: 12px;
}

.demand-table-scroll {
    min-width: 0;
    overflow: auto;
    margin-top: 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.demand-table-scroll[hidden] {
    display: none;
}

.demand-table {
    width: 100%;
    min-width: 560px;
}

/* 岗位清单那张表单独给一条更高的下限（2026-09-07，uu 事项 7）。
   成因：这张表原来有 12 列，而 min-width 只有 560px —— 12 个格子分 560px，每格
   被挤到最窄的内容宽度，中文表头就被压成一列竖排碎字（「工作地点」竖成四行）。
   本轮合并成 8 列解决了一半，另一半是这条下限本身太低。

   1080px 是 8 列的合理下限：合并列那三格里放的是两行字（其中「负责人」那一格还可能
   是下拉 + 理由框 + 两颗按钮），按每格 135px 估。到不了这个宽度时宁可让
   `.demand-table-scroll` 出横向滚动条 —— 横着滑一下看得懂，压成竖排碎字看不懂。

   ⚠️ 刻意只写这一张表，不改 `.demand-table` 本身：投递记录和需求明细那两张表也用
   同一个类，各自只有 7 列，跟着提到 1080px 会让它们在本来放得下的屏幕上凭空多出
   横向滚动条 —— 那是本轮没被要求、也没人验证过的改动。 */
.demand-table[data-job-list-table] {
    min-width: 1080px;
}

.demand-caption {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    overflow: hidden !important;
    clip: rect(1px, 1px, 1px, 1px) !important;
    white-space: nowrap !important;
}

.demand-table th,
.demand-table td {
    height: 38px;
    padding: 0 12px;
}

.demand-table thead th {
    height: 34px;
}

.demand-table .is-numeric {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* ---- 合并列的「一格两行」（2026-09-07，uu 事项 7） ----

   岗位清单里「部门」「负责人」「人数」三格各装原来两三列的信息，从左右并排改成
   上下两行。三条判断：

   1. **竖向 padding 放在 `.demand-cell-stack` 上，不动 `.demand-table td`。**
      那条规则（`height: 38px; padding: 0 12px`）是三张表共用的，为了这一处改它
      会让另外两张表的行高一起变。这里 td 的 height 只是下限，两行内容会自己把
      行撑高。
   2. **主行 13px / 次行 11px + 次要色。** 两行同样大小的话，人分不出哪个是主信息；
      而次行绝不能做到看不清 —— 它装的是末级部门、HRBP、在途人数，都是要看的真数据，
      不是装饰。
   3. **小前缀（`BP` / `要` / `缺` / `在途` / `└`）单独一层、用次要色。** 它们是标签，
      不是数据，颜色上分开才不会被读成内容的一部分。 */
.demand-cell-stack {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    /* 竖向 6px 是让两行字不贴住上下边框的最小值；再小行与行之间就糊成一片。 */
    padding: 6px 0;
}

/* 一行。`flex-wrap` 是必须的：「缺」那一格的值可能是「0（超招 2 人）」这样一整句，
   不换行会把它推出格子外面（或者把整列撑宽，间接又把别的列压回竖排碎字）。
   `align-items: baseline` 而不是 center：负责人那一行里可能是好几层块元素
   （当前负责人 + 待批准提示 + 两颗按钮），baseline 会拿它**第一行**的基线对齐，
   于是「招聘」这个小前缀正好和人名齐平；用 center 的话前缀会飘到那一块的正中间。 */
.demand-cell-main,
.demand-cell-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 6px;
    min-width: 0;
}

.demand-cell-main {
    color: var(--text);
    font-size: 13px;
    line-height: 1.45;
}

.demand-cell-sub {
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.45;
}

/* 一行里的一块（一个小前缀 + 一份内容）。同一行放两块时（「要 3 · 缺 2」）
   各自按内容宽度收缩，不平分宽度。 */
.demand-cell-piece {
    display: flex;
    align-items: baseline;
    gap: 4px;
    min-width: 0;
    max-width: 100%;
}

/* 需要撑满剩下宽度的那一块：目前只有「负责人」主行。里面可能是下拉、
   或者下拉 + 理由框 + 提交按钮，缩到内容宽度会把理由框压成十来个像素宽，
   人看不见自己打的字。 */
.demand-cell-piece.is-grow {
    flex: 1 1 auto;
}

.demand-cell-body {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.demand-cell-prefix,
.demand-cell-sep {
    flex: 0 0 auto;
    color: var(--text-secondary);
    font-size: 11px;
}

/* 数字列用等宽字形，上下两行对得齐。**不写 `text-align: right`**：这一格里现在
   混着「要」「缺」这些前缀，右对齐会把前缀和数字一起推到右边，反而读不出配对。 */
.demand-table .is-tabular {
    font-variant-numeric: tabular-nums;
}

.demand-name {
    min-width: 0;
    max-width: 320px;
    white-space: normal;
    padding-left: calc(12px + var(--demand-depth, 0) * 17px);
    color: var(--text);
    font-size: 13px;
    font-weight: 400;
    overflow-wrap: anywhere;
}

.demand-row.is-department .demand-name {
    font-weight: 700;
}

.demand-row.is-department td {
    background: #fbfcfe;
}

.demand-row.is-position .demand-name {
    color: var(--text-secondary);
}

.demand-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 26px;
    padding: 0 4px 0 0;
    border: 0;
    border-radius: 5px;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.demand-toggle:hover .demand-node-name {
    color: var(--primary);
}

.demand-toggle:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.demand-caret {
    color: var(--text-secondary);
    font-size: 10px;
}

.demand-table tfoot th,
.demand-table tfoot td {
    border-top: 1px solid var(--border-strong);
    border-bottom: 0;
    background: var(--surface-soft);
    color: var(--text);
    font-weight: 700;
}

/* --- 招聘需求页：增删改 --- */

.demand-write-feedback {
    margin: 0 0 10px;
    padding: 8px 11px;
    border: 1px solid #c6e6d8;
    border-radius: 8px;
    background: #f1fbf7;
    color: var(--success);
    font-size: 12px;
    line-height: 1.5;
}

.demand-write-feedback.is-error {
    border-color: #f0cfd2;
    background: #fdf3f4;
    color: var(--danger);
}

.demand-write-feedback[hidden],
.demand-toolbar[hidden],
.demand-editor[hidden],
.demand-editor-error[hidden] {
    display: none;
}

/* 窄屏那套卡片样式把单元格改成了 display:flex，会盖掉 hidden 的默认 display:none，
   所以「操作」列在没有写权限时必须再显式藏一次。 */
.demand-table th[hidden],
.demand-table td[hidden] {
    display: none;
}

.demand-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.demand-toolbar-note {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.5;
}

.demand-toolbar-note code {
    padding: 1px 5px;
    border-radius: 5px;
    background: var(--surface-soft);
    font-size: 11px;
}

.demand-editor {
    margin-bottom: 12px;
    padding: 13px 14px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--surface-soft);
}

.demand-editor-head {
    margin-bottom: 11px;
}

.demand-editor-title {
    margin: 0 0 4px;
    font-size: 14px;
}

.demand-editor-hint {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.55;
}

.demand-editor-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(184px, 1fr));
    gap: 11px;
}

.demand-editor-grid .field-control input:disabled,
.demand-editor-grid .field-control select:disabled {
    background: #eef1f5;
    color: #6b7482;
    cursor: not-allowed;
}

.demand-editor-error {
    margin: 11px 0 0;
    padding: 8px 11px;
    border: 1px solid #f0cfd2;
    border-radius: 8px;
    background: #fdf3f4;
    color: var(--danger);
    font-size: 12px;
    line-height: 1.5;
}

.demand-editor-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 9px;
    margin-top: 12px;
}

.demand-actions {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
}

.demand-action {
    min-height: 28px;
    padding: 0 10px;
    font-size: 12px;
}

.demand-actions-blank {
    color: var(--text-secondary);
}

@media (max-width: 768px) {
    .demand-panel {
        padding: 11px 12px 4px;
    }

    .demand-header {
        align-items: flex-start;
        flex-direction: column;
        gap: 7px;
    }

    .demand-expand-all {
        margin-left: 0;
    }

    .demand-table-scroll {
        overflow: visible;
        border: 0;
    }

    /* 窄屏整表拆成卡片，横向下限就没有意义了。
       `[data-job-list-table]` 必须一起点名：它的选择器权重比裸 `.demand-table` 高，
       只写前者的话那 1080px 会在这里继续生效，岗位清单在手机上被顶出一条横向滚动条
       而其余两张表正常 —— 症状看着像"只有这一页坏了"。 */
    .demand-table,
    .demand-table[data-job-list-table] {
        min-width: 0;
    }

    .demand-table thead {
        display: none;
    }

    .demand-table tr {
        display: block;
        margin-bottom: 8px;
        border: 1px solid var(--border);
        border-radius: 8px;
        overflow: hidden;
    }

    .demand-table th,
    .demand-table td {
        display: flex;
        height: auto;
        min-height: 34px;
        align-items: center;
        justify-content: space-between;
        gap: 10px;
        border-right: 0;
        padding: 7px 11px;
    }

    .demand-table td::before {
        content: attr(data-label);
        color: var(--text-secondary);
        font-size: 11px;
        font-weight: 400;
    }

    /* 窄屏下每格变成「小标题 ⟷ 值」的一行 flex。这时候不能再让控件占满 100%
       宽度——那会把左边那个小标题挤没，看到的是一列没有标题的输入框。改成
       "标题占自己那点宽度、控件吃掉剩下的"。 */
    .job-cell-input,
    .job-cell-select {
        width: auto;
        flex: 1 1 auto;
        min-width: 0;
    }

    /* 合并列在窄屏下也是「小标题 ⟷ 两行内容」：内容那一侧要吃掉剩下的宽度，
       不然两行字会被挤成一条竖线（正是这次要解决的那个症状，只是换了个屏幕宽度）。
       竖向 padding 交还给外面那个 td（`padding: 7px 11px`），这里清零，
       两处都留着的话卡片里每一格会比别的格高出一截。 */
    .demand-cell-stack {
        flex: 1 1 auto;
        padding: 0;
    }

    /* 合并列里的控件例外：上面那条 `width: auto` 是为了不挤掉格子左边的小标题，
       而合并列的小标题在 stack **外面**，里面这一层没有东西要给它让位。
       所以这里把 100% 要回来，否则「负责人」那一格的下拉会缩成内容宽度，
       和同一张卡片里其余几格的输入框宽度对不齐。 */
    .demand-cell-body .job-cell-input,
    .demand-cell-body .job-cell-select {
        width: 100%;
        flex: 0 1 auto;
    }

    .demand-name {
        max-width: none;
        justify-content: flex-start;
        flex-wrap: wrap;
        padding-left: calc(11px + var(--demand-depth, 0) * 14px);
        background: var(--surface-soft);
    }

    .demand-table tfoot td {
        border-bottom: 1px solid #edf0f4;
    }
}
/* === business-demand end === */

/* === identity switcher start (可整段删除回滚) === */
/* 左侧栏最底下的一块，长得跟上面的导航项一样。uu 2026-08-31 指定：不要在页面上
   浮一个方框——浮层比侧栏宽，会探出去压住正文，看着像个贴上去的补丁。

   这里刻意**不用** position:fixed。它排在 .nav-items（flex: 1）后面，被自动顶到
   侧栏底部；侧栏本身是 sticky + 100vh，所以滚到哪它都在同一个地方，效果和原来
   一样，但它现在是侧栏的一部分。 */
.identity-switcher {
    border-top: 1px solid rgba(255, 255, 255, 0.18);
    padding: 8px;
}

.identity-switcher[hidden] {
    display: none;
}

/* 跟 .nav-item 同一套排版（居中、竖排、小字），只是不复用那个类：
   .nav-item 带 is-active 等一串导航专属状态，切换入口不该跟着变。 */
.identity-switcher-toggle {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 7px 2px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: rgba(255, 255, 255, 0.9);
    font-size: 11px;
    text-align: center;
    cursor: pointer;
}

.identity-switcher-toggle:hover,
.identity-switcher-toggle[aria-expanded="true"] {
    background: rgba(255, 255, 255, 0.14);
}

.identity-switcher-caption {
    color: rgba(255, 255, 255, 0.62);
    font-size: 9px;
    letter-spacing: 0.5px;
}

/* 显示名可能有七八个字，侧栏只有 88px。允许断行，但最多两行——
   再多会把导航挤上去。 */
.identity-switcher-name {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.identity-switcher-meta {
    color: rgba(255, 255, 255, 0.52);
    font-size: 8px;
    line-height: 1.2;
}

/* 面板得探到侧栏外面去——侧栏只有 88px，账号名和区域摘要塞不进。
   position:fixed 是必需的：侧栏是 overflow: hidden auto，absolute 定位的面板
   会被它裁掉一大半。fixed 的包含块是视口，不受祖先 overflow 影响。
   （.main-nav 是 sticky，sticky 不会给 fixed 后代造包含块。） */
.identity-switcher-panel {
    position: fixed;
    z-index: 40;
    bottom: 16px;
    left: calc(var(--nav-width, 88px) + 8px);
    width: min(320px, calc(100vw - var(--nav-width, 88px) - 24px));
    max-height: min(420px, 60vh);
    padding: 10px 12px;
    overflow-y: auto;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.identity-switcher-panel[hidden] {
    display: none;
}

.identity-switcher-note {
    margin: 0 0 8px;
    color: #6b4a00;
    font-size: 11px;
    line-height: 1.6;
}

.identity-switcher-hint {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
}

.identity-switcher-error {
    margin: 8px 0 0;
    color: var(--danger);
    font-size: 12px;
    line-height: 1.6;
}

.identity-account-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.identity-account {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 7px 9px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: var(--surface-soft);
    color: var(--text);
    text-align: left;
    cursor: pointer;
}

.identity-account:hover {
    border-color: var(--primary);
    background: var(--surface);
}

.identity-account.is-current {
    border-color: var(--primary);
    background: #eef3ff;
}

.identity-account-name {
    font-size: 13px;
    font-weight: 600;
}

.identity-account-meta {
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.6;
}

.identity-account-mark {
    align-self: flex-start;
    margin-top: 2px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--primary);
    color: #ffffff;
    font-size: 10px;
}
/* === identity switcher end === */

/* === admin page start (Part E · 可整段删除回滚) === */
.admin-notice {
    margin: 0 0 14px;
    padding: 10px 14px;
    border: 1px solid #f0dcb4;
    border-left: 3px solid var(--warning);
    border-radius: 8px;
    background: #fffaf0;
    color: #6b4a00;
    font-size: 12px;
    line-height: 1.7;
}

.admin-panel,
.admin-editor {
    min-width: 0;
    margin-bottom: 18px;
    padding: 13px 16px 16px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.admin-editor[hidden] {
    display: none;
}

.admin-panel-head,
.admin-editor-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.admin-panel-head h2,
.admin-editor-title {
    margin: 2px 0 0;
    font-size: 16px;
}

.admin-editor-hint {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.6;
}

.admin-feedback {
    margin: 0 0 10px;
    padding: 7px 10px;
    border-radius: 8px;
    background: #eaf7f1;
    color: var(--success);
    font-size: 12px;
}

.admin-error,
.admin-editor-error {
    margin: 0 0 10px;
    padding: 7px 10px;
    border-radius: 8px;
    background: #fdecee;
    color: var(--danger);
    font-size: 12px;
    line-height: 1.6;
}

.admin-feedback[hidden],
.admin-error[hidden],
.admin-editor-error[hidden],
.admin-empty[hidden] {
    display: none;
}

.admin-table-scroll {
    overflow-x: auto;
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.admin-caption {
    margin-bottom: 8px;
    color: var(--text-secondary);
    font-size: 11px;
    text-align: left;
}

.admin-table th,
.admin-table td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    text-align: left;
    vertical-align: top;
}

.admin-table thead th {
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.admin-account-key {
    display: block;
    color: var(--text-secondary);
    font-size: 11px;
}

.admin-scope-cell {
    min-width: 220px;
    line-height: 1.6;
}

/* 「当前身份」小标。挨在显示名后面，提醒管理员这一行改的是自己。 */
.admin-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 999px;
    background: #eef3ff;
    color: var(--primary);
    font-size: 10px;
    font-weight: 600;
    vertical-align: 1px;
}

/* 状态胶囊。JS 一定会带上 is-active 或 is-inactive，所以颜色写在那两个类里，
   基类只管形状——省得哪天漏了修饰类，一个未知状态被画成绿色的"启用"。 */
.admin-state {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 999px;
    background: #f1f3f6;
    color: var(--text-secondary);
    font-size: 11px;
    white-space: nowrap;
}

.admin-state.is-active {
    background: #eaf7f1;
    color: var(--success);
}

.admin-state.is-inactive {
    background: #f1f3f6;
    color: var(--text-secondary);
}

.admin-table tbody tr.is-inactive th,
.admin-table tbody tr.is-inactive td {
    /* 只压淡，不隐藏：停用的账号仍然要能被找到、被启用回来。 */
    opacity: 0.62;
}

.admin-table tbody tr.is-self {
    background: #f7f9ff;
}

/* 「正在读取」「读不到」这类占位行。 */
.admin-row-note td {
    color: var(--text-secondary);
    font-size: 12px;
}

.admin-row-actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

/* 删除的二次确认条：紧跟在目标行下面的一整行。
   做成红底而不是普通灰底，是因为它和上面那行看起来必须是两件事——
   长得像普通表格行的话，人会把它当成又一条账号数据扫过去。 */
.admin-row-confirm td {
    padding: 0;
    background: #fdf3f4;
    border-top: 1px solid #f0cfd2;
}

.admin-confirm-box {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    /* 左边一道红杠，把"这一块属于上面那一行"和"这一块是警告"同时说出来。 */
    border-left: 3px solid var(--danger);
}

.admin-confirm-headline {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--danger);
}

.admin-confirm-note {
    margin: 0;
    font-size: 12px;
    /* 用正文色而不是次要灰：这几句是删之前必须读的内容，不是补充说明。 */
    color: var(--text);
    line-height: 1.6;
}

.admin-confirm-actions {
    display: flex;
    gap: 8px;
    margin-top: 2px;
}

.admin-empty {
    margin: 10px 0 0;
    color: var(--text-secondary);
    font-size: 12px;
}

.admin-editor-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
    margin-bottom: 12px;
}

.admin-editor-active {
    flex-direction: row;
    align-items: center;
    gap: 7px;
}

.admin-editor-active > span {
    font-size: 12px;
}

/* 「这一项现在改不了」的统一表现。JS 在三处用它:建号后不可改的账号名、
   全区域职级下整块无作用的区域勾选、自己那一行的启停开关。压淡而不隐藏,
   因为隐藏之后人会以为这个字段不存在,转头去别处找。 */
.admin-editor .is-muted,
.admin-scope.is-muted {
    opacity: 0.55;
}

.admin-rank-summary {
    margin: 0 0 12px;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.7;
}

.admin-scope {
    margin: 0 0 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.admin-scope[hidden] {
    display: none;
}

.admin-scope > legend {
    padding: 0 5px;
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 600;
}

.admin-scope-hint {
    margin: 0 0 10px;
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.7;
}

.admin-scope-tree {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 380px;
    overflow-y: auto;
}

.admin-scope-unit {
    padding: 7px 9px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-soft);
}

.admin-scope-unit.is-checked {
    border-color: var(--primary);
    background: #eef3ff;
}

/* 一个勾选框那一行：框 + 名字 + 岗位数。整块那一行加 .is-unit 加粗。 */
.admin-scope-check {
    display: flex;
    align-items: baseline;
    gap: 7px;
    font-size: 12px;
    cursor: pointer;
}

.admin-scope-check.is-unit {
    font-size: 13px;
    font-weight: 600;
}

.admin-scope-check input {
    /* baseline 对齐会把方框顶到文字上沿，往下压一点才跟文字齐。 */
    position: relative;
    top: 2px;
    margin: 0;
}

.admin-scope-name {
    line-height: 1.5;
}

.admin-scope-count {
    color: var(--text-secondary);
    font-size: 11px;
}

/* 已配、但当前汇总表里找不到的授权项。刻意显眼:它此刻等于没配,
   而界面上如果画得跟正常项一样,管理员会以为这一块已经授权好了。 */
.admin-scope-unit.is-missing {
    border-color: #e3c98a;
    background: #fdf7e8;
}

.admin-scope-check.is-missing .admin-scope-name,
.admin-scope-check.is-missing .admin-scope-count,
.admin-scope-unit.is-missing > .admin-scope-check .admin-scope-count {
    color: var(--warning);
}

.admin-scope-departments {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    margin: 7px 0 0;
    padding: 7px 0 0 18px;
    border-top: 1px dashed var(--border);
}

.admin-scope-departments[hidden] {
    display: none;
}

/* 部门那一件：勾选行 + 它下面可折叠的岗位层包在一起，flex 换行时
   岗位层不会被甩到别的部门旁边去。 */
.admin-scope-department {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}

/* 第三层岗位。默认收在 <details> 里：一个部门动辄十几个岗位，全铺开的话
   整棵树只剩滚动。展开状态由 admin.js 在重建时从旧 DOM 里读回来恢复。 */
.admin-scope-jobs {
    margin: 0 0 0 6px;
    padding-left: 10px;
    border-left: 1px dashed var(--border);
}

.admin-scope-jobs > summary {
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.5;
    cursor: pointer;
    user-select: none;
}

.admin-scope-jobs-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 4px;
}

/* 功能权限那一组。和 .admin-scope 同一个外框，因为它们是同一层意思的两块设置
   （区域 = 能看哪些数据，功能权限 = 能动什么）。 */
.admin-grants {
    margin: 0 0 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.admin-grants[hidden] {
    display: none;
}

.admin-grants > legend {
    padding: 0 5px;
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 600;
}

.admin-grants-hint {
    margin: 0 0 10px;
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.7;
}

.admin-grants-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
}

.admin-grant-check {
    display: flex;
    align-items: baseline;
    gap: 7px;
    font-size: 12px;
    cursor: pointer;
}

.admin-grant-check input {
    /* 同 .admin-scope-check input：baseline 会把方框顶到文字上沿。 */
    position: relative;
    top: 2px;
    margin: 0;
}

/* 真的改不动的那几项：非职级自带、又不在可加名单里，只会出现在"扣掉的是一项当前
   职级没有的权限"这种记录上。灰掉是必要的——画成和可改的项一样，管理员会去点它，
   然后以为改了权限，而实际什么都没变。职级自带的那些**不**灰：2026-09-07 起它们
   可以取消勾选（= 按账号扣掉），灰掉会让人以为动不了。 */
.admin-grant-check.is-muted {
    color: var(--text-secondary);
    cursor: default;
}

.admin-grant-note {
    color: var(--text-secondary);
    font-size: 11px;
}

.admin-preview {
    margin: 0 0 12px;
    padding: 10px 12px;
    border: 1px solid #cfe0ff;
    border-radius: 8px;
    background: #f4f8ff;
}

.admin-preview h3 {
    margin: 0 0 6px;
    font-size: 13px;
}

.admin-preview-body {
    margin: 0;
    font-size: 12px;
    line-height: 1.7;
}

/* 预览算不出来的时候。这一格必须变色:一句灰色的「预览失败」很容易被当成
   「这套配置什么都看不到」,那是两件完全不同的事。 */
.admin-preview-body.is-error {
    color: var(--danger);
}

.admin-preview-warnings {
    margin: 8px 0 0;
    padding-left: 18px;
    color: #6b4a00;
    font-size: 11px;
    line-height: 1.7;
}

.admin-preview-warnings[hidden] {
    display: none;
}

.admin-editor-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.admin-rank-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 10px;
}

.admin-rank-item {
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-soft);
}

.admin-rank-name {
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.admin-rank-scope {
    display: inline-block;
    margin-top: 4px;
    padding: 1px 7px;
    border-radius: 999px;
    background: #eef3ff;
    color: var(--primary);
    font-size: 10px;
    font-weight: 600;
}

.admin-rank-summary-text {
    display: block;
    margin-top: 4px;
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.7;
}

.admin-rank-permissions {
    margin: 6px 0 0;
    padding-left: 16px;
    font-size: 11px;
    line-height: 1.7;
}

/* --- 判定标准面板（TASK-013）--- */
.admin-rules[hidden] {
    display: none;
}

/* 保存后端下发的那几条警告。用警示色但不用红色：这些不是错误（保存已经成功了），
   是"你刚才这一改会带来什么后果"，红色会让人以为没存上。 */
.admin-rules-warnings {
    margin: 0 0 12px;
    padding: 8px 12px 8px 26px;
    border: 1px solid #f0dcb4;
    border-left: 3px solid var(--warning);
    border-radius: 8px;
    background: #fffaf0;
    color: #6b4a00;
    font-size: 12px;
    line-height: 1.8;
}

.admin-rules-warnings[hidden] {
    display: none;
}

.admin-rule-group + .admin-rule-group {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px dashed var(--border);
}

.admin-rule-group-title {
    margin: 0 0 8px;
    font-size: 13px;
    font-weight: 600;
}

.admin-rule-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 10px;
}

.admin-rule-row {
    display: flex;
    flex-direction: column;
    gap: 5px;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-soft);
}

.admin-rule-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.admin-rule-label {
    font-size: 13px;
    font-weight: 600;
}

/* 「已改过」。和 .admin-rank-scope 同一个胶囊形状，但用警示色——这一格的值
   已经不是代码里的默认值了，看清单的人应该一眼看出哪几项被人动过。 */
.admin-rule-badge {
    flex: none;
    padding: 1px 7px;
    border-radius: 999px;
    background: #fff3d9;
    color: #6b4a00;
    font-size: 10px;
    font-weight: 600;
}

.admin-rule-input {
    width: 100%;
    min-width: 0;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
}

.admin-rule-input:disabled {
    background: var(--surface-soft);
    color: var(--text-secondary);
}

.admin-rule-meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.admin-rule-default,
.admin-rule-hint {
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.6;
}

.admin-rule-reset {
    align-self: flex-start;
    font-size: 11px;
}

/* --- 后台管理：数据通知 --- */
/*
 * 这一组样式原来属于招聘需求页的「信息核对」面板。2026-09-04 uu 决定把那块整体搬到
 * 后台管理（点「已解决」就消失），样式跟着改名沿用、**没有另抄一套**：三档提示
 * （对不上 / 缺字段 / 待确认）搬家前后是同一批，配色再抄一遍就会慢慢分叉。
 *
 * 0 条时整个面板隐藏，所以没有"空状态"那一句 —— 后台不该出现一块写着「没有问题」的空白。
 *
 * 面板外框不在这里写：它挂 `.admin-panel`，边框、圆角、留白和后台其他几块共用一套。
 */
.data-notice-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 9px;
    margin-bottom: 9px;
}

.data-notice-head h3 {
    margin: 0;
    font-size: 14px;
}

.data-notice-count {
    color: var(--text-secondary);
    font-size: 12px;
}

.data-notice-list {
    display: grid;
    gap: 7px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.data-notice-item {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 4px 12px;
    padding: 8px 11px;
    border: 1px solid var(--border);
    border-left-width: 3px;
    border-radius: 8px;
    background: #ffffff;
    font-size: 12px;
    line-height: 1.55;
}

/* 文字一列、按钮一列。`min-width: 0` 是必需的：没有它，长正文会把 flex 项撑过
   容器宽度，按钮被挤出面板右边缘。 */
.data-notice-body {
    display: grid;
    gap: 2px;
    flex: 1 1 240px;
    min-width: 0;
}

.data-notice-ack {
    flex: 0 0 auto;
    font-size: 11px;
}

.data-notice-item.is-mismatch {
    border-left-color: var(--danger);
}

.data-notice-item.is-missing {
    border-left-color: var(--warning);
}

.data-notice-item.is-info {
    border-left-color: var(--border-strong);
}

.data-notice-title {
    color: var(--text);
    font-weight: 700;
}

.data-notice-text {
    color: var(--text-secondary);
}

.admin-notices[hidden] {
    display: none;
}

@media (max-width: 720px) {
    /* 切换入口本身不用管了——它在侧栏里，跟着侧栏走。只把弹出面板收紧一点。 */
    .identity-switcher-panel {
        bottom: 10px;
        left: calc(var(--nav-width, 88px) + 6px);
        width: calc(100vw - var(--nav-width, 88px) - 16px);
    }

    .admin-panel-head,
    .admin-editor-head {
        flex-direction: column;
        align-items: flex-start;
    }
}
/* === admin page end === */


/* === 岗位清单 + 投递流转 + 事务页 start（可整段删除回滚） ===

   这一段刻意不新写卡片和表格布局：两个新板块用的是现成的 .demand-panel /
   .demand-header / .demand-table-scroll / .demand-table，连窄屏下"整行拆成竖排
   卡片"那套响应式规则都一起继承。这里只补三样现成样式里没有的东西：
   板块头上的计数、板块下的说明行，以及流转面板自己那一套。 */

.demand-count {
    color: var(--text-secondary);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.panel-note {
    margin: 8px 0 10px;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.6;
}

.panel-note[hidden] {
    display: none;
}

/* 保存失败时那句话（demand-jobs.js 给 note 加 .is-error）。必须和常规说明不同色：
   共用一种样式的话，"这一行没存上、已经退回原值"会长得跟"点右侧按钮可以展开"
   一样不起眼，而前者是必须被看到的。 */
.panel-note.is-error {
    color: var(--danger);
    font-weight: 700;
}

/* 空表 / 报错时整行合并的那一格。不用现成的 .empty-cell：那个是给总览那张大表
   写的，固定 210px 高，放在这两块小板块里会撑出一大片空白。 */
.panel-empty-cell {
    height: auto;
    padding: 14px 12px;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.6;
    text-align: left;
}

/* ---- 岗位清单：能改的两格、在途人数、只看某岗位的提示条 ---- */

/* 输入框和下拉要塞进 .demand-table 那 38px 的行高里，所以比页面上别处的输入控件
   矮一号。宽度写 100% 而不是固定值：这一列多宽由表格自己分，写死会在窄屏上把
   整张表撑出横向滚动条。 */
.job-cell-input,
.job-cell-select {
    width: 100%;
    min-width: 0;
    height: 28px;
    padding: 0 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: #ffffff;
    color: var(--text);
    font-family: inherit;
    font-size: 12px;
}

.job-cell-input:focus-visible,
.job-cell-select:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

/* 日期那两格。`<input type="date">` 和文本框不一样：它里面塞着"年-月-日"和一个
   日历图标，宽度被压到 100% 以下时浏览器会把日期文字截断成「2026-0…」，看不出
   到底是哪一天。所以给一条下限，宁可让这一列宽一点。
   padding 右侧收窄，是因为日历图标本身就占掉一段空间。

   ⚠️ 2026-09-02 起**暂时没有使用者**：岗位清单里那两个日期输入框跟着「目标到岗」
   「需求关闭」一起撤了，新加的「需求时间」是只读文本，用不到这条规则。留着是因为
   它写的是"表格里的 date 输入框该多宽"这条通用结论——那个结论没有过时，
   下次表格里再出现可编辑日期时删了还得重新踩一遍截断的坑。 */
.job-cell-input.is-date {
    min-width: 122px;
    padding-right: 4px;
    font-variant-numeric: tabular-nums;
}

/* 正在保存：控件禁用 + 整行压一层淡背景。只灰掉控件不够——一行里两个控件同时
   变灰，看起来像"这一行不让改"，加上行背景才说得清是"正在存"。 */
.job-cell-input:disabled,
.job-cell-select:disabled {
    background: var(--surface-soft);
    color: var(--text-secondary);
    cursor: progress;
}

.demand-table tr.is-saving > td {
    background: var(--surface-soft);
}

/* 在途人数那个可点的数字。基础样式借现成的 .text-button，这里改三处：高度压到
   能塞进 38px 行高、颜色从那个类默认的危险红改回主色（看一眼候选人不是危险动作）、
   数字用等宽字形以便一列上下对齐。右对齐由 .demand-table .is-numeric 的
   text-align 负责——按钮是 inline-block，会跟着一起被推到右边。 */
.job-active-link {
    min-height: 26px;
    padding: 0 9px;
    color: var(--primary);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

/* ---- 岗位清单「招聘负责人」那一格的三种形态（2026-09-03，uu 需求①②） ---- */

/* 当前负责人的名字。它在申请档和待审档里都要出现，所以单独一条规则：
   那两档下面还压着控件或提醒，名字得自己占满一行，不能和它们并排挤在 38px 里。 */
.job-assign-owner {
    display: block;
    color: var(--text);
    font-size: 12px;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

/* 「待批准：转给某某」。用 warning 那个橙色，不用主色也不用红：
   主色在这张表里是"可以点"的意思（见 .job-active-link），而这行字点不了；
   红色是出错的意思，而挂着一份等人批的申请不是错误。 */
.job-assign-pending {
    display: block;
    margin-top: 3px;
    color: var(--warning);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

/* 申请转交那一档：下拉 + 理由 + 提交，竖着排。
   刻意不横排：这一格分不到多少宽度，三个控件挤一行会把理由框压成十来个像素宽，
   人看不见自己打的字。竖排会把这一行撑高，那是接受的代价。
   （2026-09-07 表从 12 列合并成 8 列，这一格还多了一行 HRBP 小字，宽度略微宽松了些，
   但仍然不够横排三个控件——结论没变。） */
.job-assign-request {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 4px;
}

/* 提交按钮：借 .text-button 的底子，压到和格子里其他控件一样的 28px，
   颜色从那个类默认的危险红改回主色——提交一份要人批的申请不是危险动作。
   禁用态要看得出来：没选人或没写理由时按钮一直是禁用的，不给个灰样子的话
   人会以为自己点了没反应。 */
.job-assign-request .text-button {
    min-height: 28px;
    color: var(--primary);
    font-size: 11px;
}

.job-assign-request .text-button:disabled {
    border-color: var(--border);
    background: var(--surface-soft);
    color: var(--text-secondary);
    cursor: not-allowed;
}

/* 批准 / 驳回两颗按钮。横排，因为只有两颗短按钮，挤得下。
   `.text-button` 的默认色是危险红（第二条同名规则覆盖过），所以这里只把**批准**
   那颗改回主色，驳回原样留红：两颗字数一样、位置相邻，点错的代价却不对称——
   驳回会把申请人写的理由一起丢掉，而且不通知他。 */
.job-assign-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 4px;
}

.job-assign-actions .text-button {
    min-height: 26px;
    padding: 0 9px;
    font-size: 11px;
}

.job-assign-actions .text-button.is-approve {
    color: var(--primary);
}

/* 当前生效的筛选条件那一条（需求 6）。单独一个容器，不和 .panel-note 挤在一格：
   note 要留给读取失败和分页说明，而"正在筛"恰恰是出错时最该看见的上下文。
   flex-wrap 是必须的：三个条件叠满时一行放不下，不换行会把「清空筛选」挤出可视区。 */
.filter-chip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 9px;
    margin: 9px 0 0;
    padding: 7px 11px;
    border: 1px solid var(--primary);
    border-radius: 8px;
    background: rgba(63, 81, 181, 0.06);
    font-size: 12px;
}

/* display: flex 会盖掉浏览器给 [hidden] 的 display: none，所以这一条必须写。 */
.filter-chip[hidden] {
    display: none;
}

/* 一个条件一颗徽标。自带边框和白底，好让"三个条件是三件事"这一点看得出来——
   全挤在同一条底色上，读起来像一句被分号断开的长句。 */
.filter-chip-item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    padding: 2px 3px 2px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
}

.filter-chip-label {
    min-width: 0;
    overflow: hidden;
    color: var(--text);
    font-weight: 700;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* 摘掉这一个条件的「×」。做成圆形、和文字同高，点击区 22px 见方——
   再小就点不准，而这颗按钮是唯一能单独放宽一个条件的入口。 */
.filter-chip-clear {
    display: inline-flex;
    width: 22px;
    height: 22px;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
}

.filter-chip-clear:hover {
    background: rgba(63, 81, 181, 0.1);
    color: var(--primary);
}

/* 「清空筛选」。同样借 .text-button，同样要把默认的危险红改回主色：
   取消筛选不是危险动作。 */
.filter-chip .text-button {
    min-height: 28px;
    margin-left: auto;
    padding: 0 10px;
    color: var(--primary);
}

/* 「仅看这一个候选人」那一行（uu 2026-09-07）。平时整行不在，只有从录入弹窗
   的「查看详情」跳过来时才出现，所以默认带 hidden。

   `[hidden]` 这一条**必须**写：.filter-row 是 display: flex，作者样式会盖掉
   浏览器给 [hidden] 的 display: none。不写的话这一行永远显示，而且里面是一颗
   空徽标——比不显示更让人困惑。（上面 .filter-chip[hidden] 已经踩过同一个坑。）

   align-items 改成 center：这一行没有带标题的输入框，不需要跟别的行一样底部对齐。 */
.filter-row-candidate {
    align-items: center;
}

.filter-row-candidate[hidden] {
    display: none;
}

/* 行内那句「本页所有数字都只算这个人的投递」。灰字小号：它是提醒，
   不该抢过左边那颗写着人名的徽标；但也不能不写——KPI 卡片跟着一起收窄，
   看的人若不知道这件事，会以为公司这个月只收到两份简历。 */
.filter-chip-note {
    color: var(--text-secondary);
    font-size: 12px;
}

/* 候选人名字后面的「投过 N 次 / 同岗位 N 次」（需求 7①）。用琥珀色而不是主色：
   这不是一条链接、也不是状态标签，是一句"这一条不一样，多看一眼"的提醒。
   借 .stage-badge 的形状（同一列里两种徽标高度不齐会让整列看起来是歪的），
   只换配色。字号压到 10px：它挂在名字后面，抢不过名字才对。 */
.repeat-badge {
    display: inline-flex;
    min-height: 20px;
    align-items: center;
    margin-left: 5px;
    padding: 0 7px;
    border: 1px solid #f0dcb4;
    border-radius: 999px;
    background: #fdf6e7;
    color: var(--warning);
    font-size: 10px;
    font-weight: 700;
    white-space: nowrap;
}

/* ---- 流转面板：展开在投递行下面的那一整格 ---- */

.demand-table tr.is-expanded > td {
    background: var(--surface-soft);
}

.flow-panel-row > td {
    height: auto;
    padding: 0;
    background: var(--surface-soft);
}

.flow-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 12px 14px;
    border-top: 1px dashed var(--border-strong);
}

.flow-head {
    display: flex;
    align-items: center;
    gap: 10px;
}

.flow-version {
    color: var(--text-secondary);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

.flow-note,
.flow-empty {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.6;
}

/* 「这一步在等什么」那句说明。用和 .flow-note 相同的次要色，但左边加一道细线
   把它和提醒类文字分开：它是给不熟悉招聘流程的人看的常驻解释，不是提醒也不是
   警告，长得和「你的职级只能查看进度」一样会让人以为哪里出了状况。 */
.flow-hint {
    margin: 0;
    padding-left: 8px;
    border-left: 2px solid var(--border);
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.6;
}

/* 「本轮面试已通过」比同位置的其他提示更需要一眼看见：它决定了下一步能点什么。
   只换左边那条竖线的颜色，不改字号也不加背景——同一处出现两种块状底色，
   看着像两条不同性质的消息。 */
.flow-hint.is-passed {
    border-left-color: var(--success);
    color: var(--text);
}

/* 报错和成功各自一种颜色。两者共用一种样式的话，"版本冲突、没有改动"
   和"已经提交成功"会长得一样，那是最不该混的两句话。 */
.flow-error {
    margin: 0;
    padding: 9px 11px;
    border: 1px solid #f3c9c9;
    border-radius: 8px;
    background: #fdf3f3;
    color: #a12b2b;
    font-size: 12px;
    line-height: 1.6;
}

.flow-feedback {
    margin: 0;
    padding: 9px 11px;
    border: 1px solid #cfe6d4;
    border-radius: 8px;
    background: #f3faf5;
    color: #24704a;
    font-size: 12px;
    line-height: 1.6;
}

.flow-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.flow-action {
    min-height: 30px;
    padding: 0 11px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text);
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
}

.flow-action:hover:not(:disabled) {
    border-color: #3451b2;
    color: #3451b2;
}

.flow-action.is-active {
    border-color: #3451b2;
    background: #f4f6ff;
    color: #3451b2;
}

/* 截断动作单独一种配色：它是不可逆的，和"往下推一步"混成同一个样子，
   手一滑点错的代价完全不一样。 */
.flow-action.is-truncate {
    border-color: #e8c4c4;
    color: #a12b2b;
}

.flow-action.is-truncate:hover:not(:disabled),
.flow-action.is-truncate.is-active {
    border-color: #a12b2b;
    background: #fdf3f3;
    color: #a12b2b;
}

.flow-action:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.flow-form {
    display: flex;
    flex-direction: column;
    gap: 9px;
    max-width: 520px;
    padding: 11px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
}

.flow-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.flow-field-label {
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 700;
}

.flow-field input,
.flow-field select,
.flow-field textarea {
    width: 100%;
    padding: 6px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 12px;
}

.flow-field textarea {
    resize: vertical;
}

.flow-confirm-note {
    margin: 0;
    color: var(--text);
    font-size: 12px;
    font-weight: 700;
    line-height: 1.6;
}

.flow-timeline {
    display: flex;
    flex-direction: column;
    gap: 7px;
}

/* 展开面板里的 BP 维护区（事项 3）。上下各一条细线把它和动作区、流水隔开：
   改 BP 不推进任何阶段、不写流水，混在动作按钮里会被当成"又一个动作"。 */
.flow-bp {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: 8px 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

.flow-bp-current {
    margin: 0;
    font-size: 12px;
    line-height: 1.6;
}

.flow-bp-label {
    color: var(--text-secondary);
}

.flow-subtitle {
    margin: 0;
    font-size: 12px;
}

.flow-events {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.flow-event {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    font-size: 12px;
    line-height: 1.5;
}

.flow-event-time {
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.flow-event-move {
    font-weight: 700;
}

.flow-event-action,
.flow-event-actor,
.flow-event-reason,
.flow-event-comment {
    color: var(--text-secondary);
}

/* 备注可能很长，单独占一行，别把上面那几格挤成一列。 */
.flow-event-comment {
    flex-basis: 100%;
}

/* ---- 事务页 ---- */

.tasks-tablist {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
}

.tasks-tab {
    min-height: 32px;
    padding: 0 14px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-secondary);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}

.tasks-tab:hover {
    border-color: #3451b2;
    color: #3451b2;
}

.tasks-tab.is-active {
    border-color: #3451b2;
    background: #f4f6ff;
    color: #3451b2;
}

/* Tab 上那个小数字。读不到数据时 JS 会把它留空，这时 :empty 让它整块不占位——
   留一个空的圆点会被当成"0 条"，而实际含义是"还没查到"。 */
.tasks-tab-count {
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--surface-soft);
    color: var(--text-secondary);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

.tasks-tab-count:empty {
    display: none;
}

.tasks-tab.is-active .tasks-tab-count {
    background: #dbe2ff;
    color: #3451b2;
}

.tasks-panel[hidden] {
    display: none;
}

/* 口径说明。用虚线框和有数据的实线卡片区分开——这一段说的是"这套提醒有哪些
   做不到的地方"，不是数据本身。
   刻意**不做成 flex**：它是个 <ul>，flex 会让每条前面的项目符号位置变得不好预测，
   而这几条正是最需要被逐条读到的。保持普通列表布局，条间距用相邻选择器给。 */
.task-notices {
    margin: 0 0 14px;
    padding: 14px 18px 14px 34px;
    border: 1px dashed var(--border-strong);
    border-radius: 10px;
    background: var(--surface-soft);
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.7;
}

.task-notice + .task-notice {
    margin-top: 6px;
}

.task-notice strong {
    color: var(--text);
}

/* 「非后台实时提醒」那一条是几条里最要紧的，给它一条左侧竖线。
   list-style-position 改成 inside，让项目符号落到竖线右边——默认的 outside 会把
   符号甩到竖线外面，看着像竖线是画错位置的。 */
.task-notice.is-key {
    padding-left: 8px;
    border-left: 3px solid var(--primary);
    list-style-position: inside;
}

/* 占位口径（岗位关键度统一按 C2）。用警示色标出来，免得被当成已经生效的规则。 */
.task-notice.is-placeholder {
    color: #8a5a00;
}

/* 统计条。五个数横排，窄屏自动换行。 */
.task-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 14px;
}

.task-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 96px;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}

.task-stat-label {
    color: var(--text-secondary);
    font-size: 11px;
}

.task-stat-value {
    color: var(--text);
    font-size: 20px;
    font-variant-numeric: tabular-nums;
}

/* 人工升级的理由框。 */
.task-escalate {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 14px;
    padding: 14px 18px;
    border: 1px solid var(--danger);
    border-radius: 10px;
    background: #fff8f8;
}

.task-escalate[hidden] {
    display: none;
}

.task-escalate-target {
    margin: 0;
    color: var(--text);
    font-size: 13px;
    font-weight: 700;
}

.task-escalate-warning {
    margin: 0;
    color: var(--danger);
    font-size: 12px;
    line-height: 1.7;
}

.task-escalate-actions {
    display: flex;
    gap: 8px;
}

/* ---- 事务表格 ---- */

/* 这张表借的是 .demand-table，但那张表每格只放一个值、行高钉死 38px；事务这边
   一格里要竖着放两三样东西（优先级 + 人工升级标记 + 严重度、事务类型 + 来源
   说明、候选人 + 岗位 + 阶段）。所以三处覆盖：
   - height 放开，行随内容长；
   - 补上上下 padding（.demand-table 那边是 `0 12px`，竖排内容会贴到边线上）；
   - vertical-align 顶对齐，否则一行里各格内容多少不同时，少的那几格会浮在中间，
     一眼看不出哪几样是同一条事务的。 */
.task-table th,
.task-table td {
    height: auto;
    padding: 9px 12px;
    vertical-align: top;
}

/* 优先级徽标。P0/P1 用红和橙，P2 用蓝，P3 落回灰底（2026-09-08 起没有 P4，
   规划书 3.7）。颜色不是唯一线索——徽标里本来就写着 P0 / P1 的字样，
   不靠颜色也读得出档位。 */
.task-priority {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--surface-soft);
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 700;
}

.task-priority.is-p0 {
    background: #fdecec;
    color: #b42318;
}

.task-priority.is-p1 {
    background: #fff4e5;
    color: #93500a;
}

.task-priority.is-p2 {
    background: #eef2ff;
    color: #3451b2;
}

/* 人工升级标记。必须和规则算出来的 P0 长得不一样，否则两种 P0 混在一起，
   规则算出的那几条会被插队上来的埋掉，整套优先级就没人信了。
   用虚线边框而不是另一种底色：底色再加一种，一格里就有三块色，反而更难认。 */
.task-escalated {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border: 1px dashed var(--danger);
    border-radius: 999px;
    color: var(--danger);
    font-size: 10px;
    font-weight: 700;
}

.task-severity,
.task-source,
.task-object-job,
.task-due-at,
.task-closed-reason,
.task-no-action,
.task-p0-source,
.task-p0-factors {
    display: block;
    margin-top: 3px;
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1.5;
}

/* 「这条 P0 凭什么」那两行（规划书 3.2 / 3.3）。刻意跟着上面那组次要文字走，
   **不另给强调色**：它们是 P0 徽标的解释，不是又一条警报。给红色的话，
   同一格里会出现三处红（徽标、逾期时长、这两行），最要紧的那个徽标就不显眼了。

   只有加重项那行左侧加一条竖线，用来把"哪条车道"和"闸门凭哪几格开的"分开看。 */
.task-p0-factors {
    padding-left: 6px;
    border-left: 2px solid var(--border);
}

.task-type {
    display: block;
    color: var(--text);
    font-size: 12px;
}

/* 逾期用危险红，未逾期用普通文本色。同样不只靠颜色：文字本身写的是
   「已逾期 X」还是「还剩 X」，读字就能分清。 */
.task-overdue {
    display: block;
    color: var(--danger);
    font-size: 12px;
}

.task-remaining {
    display: block;
    color: var(--text);
    font-size: 12px;
}

/* 逾期那一行压一层极淡的红底，扫一眼就看出哪几行已经晚了。
   刻意压得很淡：一屏里可能有一半的行都逾期，重了会变成满屏红，等于没有标记。 */
.task-table tr.is-overdue > td {
    background: #fffaf9;
}

/* 一行里可能有四五颗按钮（接收 / 完成 / 转给我 / 退回未分配 / 升级到 P0），
   所以按钮压小一号并允许换行，而不是把这一列越撑越宽。 */
.task-action {
    min-height: 26px;
    margin: 0 4px 4px 0;
    padding: 0 10px;
    font-size: 11px;
}

/* ---- 行内「转交」那一组（2026-09-04，事项 4） ---- */

/* 下拉 + 备注 + 按钮竖排。和 .job-assign-request 同一个理由，也同一个代价：
   这一列还要放接收 / 完成 / 升级 / 延期那一串按钮，三个控件挤一行会把备注框压成
   十几个像素宽，人看不见自己打的字。竖排把行撑高，那是接受的代价。 */
.task-transfer {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0 0 6px;
}

/* 下拉和备注压到和这一格里其他控件一样的 28px；宽度给一个下限，
   否则「选择接手人」这几个字在窄列里会被截成「选择接…」。 */
.task-transfer-target,
.task-transfer-note {
    min-width: 120px;
    min-height: 28px;
    padding: 0 6px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font-size: 11px;
}

.task-transfer-target:disabled,
.task-transfer-note:disabled {
    background: var(--surface-soft);
    color: var(--text-secondary);
    cursor: not-allowed;
}

/* 「待审批：转交给某某，备注：…」。橙色，和岗位那边的 .job-assign-pending 一致：
   主色在表里是"可以点"的意思，而这行字点不了；红色是出错，而挂着一份等人批的
   申请不是错误。 */
.task-transfer-pending {
    margin: 0 0 4px;
    color: var(--warning);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

@media (max-width: 768px) {
    /* 断点必须和上面那套「整行拆成竖排卡片」的规则一致（.demand-table 那一段用的
       也是 768px）。写成 720px 的话，721–768px 这个区间里卡片规则生效、下面这些
       补丁不生效，事务那几格就会被 space-between 摊平——那正是最难复现的一类
       样式 bug：只在某一小段窗口宽度里出现。 */
    /* 窄屏下 .demand-table 会把每格变成「小标题 ⟷ 值」的一行 flex。事务这边一格里
       有两三样东西，套那条规则会被 space-between 摊开成一行，读起来像三个不相干的
       值。所以这一张表改成「小标题在左、内容在右侧自己竖着排」：靠 flex-wrap 让
       内容换行，align-items 顶对齐，gap 分开小标题和内容。 */
    .task-table td {
        flex-wrap: wrap;
        align-items: flex-start;
        justify-content: flex-start;
        gap: 4px 10px;
    }

    .task-table td::before {
        flex: 0 0 auto;
        min-width: 52px;
        padding-top: 1px;
    }

    /* 这几样在宽屏是「独占一行」，窄屏下要跟着一起换行，所以从 block 改成
       占满剩余宽度的 flex 项——不改的话它们会挤在小标题右边排成一列很窄的字。 */
    .task-severity,
    .task-source,
    .task-object-job,
    .task-due-at,
    .task-closed-reason,
    .task-p0-source,
    .task-p0-factors,
    .task-overdue,
    .task-remaining,
    .task-type {
        flex: 1 0 100%;
        margin-top: 0;
    }
}
/* === 岗位清单 + 投递流转 + 事务页 end === */


