/*
 * 应用样式。只依赖 tokens.css 的变量，不写死色值与圆角。
 *
 * 结构：
 *   1. 基础        2. 登录页      3. 应用外壳（顶栏 / 侧栏）
 *   4. 内容区      5. 表格        6. 表单与对话框
 *   7. 提示与徽标  8. 空状态      9. 响应式
 */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    min-height: 100vh;
    background: var(--canvas);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 18px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

[hidden] {
    display: none !important;
}

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

/* ---------- 1. 基础控件 ---------- */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: var(--control-height);
    padding: 0 24px;
    border: 1px solid transparent;
    border-radius: var(--radius-button);
    background: var(--action);
    color: var(--on-action);
    font-family: inherit;
    font-size: 18px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--transition), border-color var(--transition), opacity var(--transition);
}

.button:hover:not(:disabled) {
    background: var(--action-hover);
}

.button:active:not(:disabled) {
    background: var(--action-pressed);
}

.button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.button--secondary {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--text);
}

.button--secondary:hover:not(:disabled) {
    background: var(--surface-sunken);
}

.button--danger {
    background: var(--error-surface);
    color: var(--error-text);
    border-color: transparent;
}

.button--danger:hover:not(:disabled) {
    background: #F7DFE3;
}

.button--small {
    height: var(--control-height-sm);
    padding: 0 16px;
    font-size: 16px;
}

.button--block {
    width: 100%;
}

.field {
    display: block;
    margin-bottom: 20px;
}

/* 一组单选项。默认的 label 是行内元素，几段带换行的候选文案会连成一整段，
   看不出哪个圈属于哪一句——候选内容是多行文本时尤其明显 */
.radio-group {
    display: block;
}

.radio--block {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 0;
    cursor: pointer;
}

.radio--block input {
    margin-top: 4px;
    flex: none;
}

.radio__text {
    white-space: pre-wrap;
    word-break: break-word;
}

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

/* 一个字段的多条（目前只有「话题+标签」）：每条一行，行与行之间留出间隔，
   否则两个相邻的文本框会连成一片，看不出这是两条不同的条目 */
.field-rows {
    display: block;
}

.field-row + .field-row {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

.field-row .field__label {
    margin-bottom: 6px;
}

.field__label {
    display: block;
    margin-bottom: 8px;
    font-size: 16px;
    font-weight: 600;
}

.field__label .required {
    margin-left: 4px;
    color: var(--error-text);
}

.field__hint {
    margin: 6px 0 0;
    color: var(--text-muted);
    font-size: 16px;
}

.input,
.select,
.textarea {
    width: 100%;
    min-height: var(--control-height);
    padding: 12px 16px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-input);
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
    font-size: 18px;
    line-height: 1.4;
}

.select {
    cursor: pointer;
}

.textarea {
    min-height: 108px;
    resize: vertical;
}

.input:focus,
.select:focus,
.textarea:focus {
    outline: 2px solid var(--action);
    outline-offset: 1px;
    border-color: var(--action);
}

.input[aria-invalid="true"],
.select[aria-invalid="true"] {
    border-color: var(--error-text);
}

.checkbox {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-height: var(--touch-target);
    cursor: pointer;
    /* 带说明的勾选项要让说明另起一行；不换行时相邻两项会挤成一行（反馈 1020 #8/#11） */
    flex-wrap: wrap;
}

.checkbox input {
    width: 20px;
    height: 20px;
    accent-color: var(--action);
    cursor: pointer;
    flex: none;
}

/* 勾选框 + 标签：必须**横着**排。早先这里是 column，勾选框单独占一行、
   文字落到下一行，两个勾选项看起来就分不清谁对谁（反馈 1020 #8/#11） */
.checkbox__main {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
}

/* 说明文字另起一行，并与标签左对齐（让出勾选框的宽度） */
.checkbox > .field__hint {
    flex-basis: 100%;
    margin: 0 0 6px 30px;
}

/* ---------- 2. 登录页 ---------- */

.login {
    position: relative;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--gutter);
    overflow: hidden;
}

.login__glow {
    position: absolute;
    inset: -20% -10% auto -10%;
    height: 620px;
    background: radial-gradient(60% 60% at 30% 20%,
            var(--gradient-a) 0%, var(--gradient-b) 45%, var(--gradient-c) 100%);
    opacity: 0.55;
    filter: blur(40px);
    pointer-events: none;
}

.login__card {
    position: relative;
    width: 100%;
    max-width: 480px;
    padding: 48px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-overview);
    box-shadow: var(--shadow-card);
}

.login__eyebrow {
    margin: 0 0 12px;
    color: var(--text-muted);
    font-size: 16px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.login__title {
    margin: 0 0 8px;
    font-size: 40px;
    line-height: 1.3;
    font-weight: 700;
}

.login__subtitle {
    margin: 0 0 32px;
    color: var(--text-muted);
}

.login__error {
    margin-bottom: 20px;
    padding: 12px 16px;
    border-radius: var(--radius-input);
    background: var(--error-surface);
    color: var(--error-text);
    font-size: 16px;
}

.login__footer {
    margin: 24px 0 0;
    padding-top: 24px;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 16px;
    text-align: center;
}

/* ---------- 3. 应用外壳 ---------- */

.app {
    display: grid;
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    min-height: 100vh;
}

.sidebar {
    display: flex;
    flex-direction: column;
    padding: 24px 16px;
    background: var(--surface);
    border-right: 1px solid var(--border);
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
}

.sidebar__brand {
    padding: 0 12px 24px;
}

.sidebar__brand-name {
    display: block;
    font-size: 20px;
    font-weight: 700;
    letter-spacing: 0.01em;
}

.sidebar__brand-sub {
    display: block;
    color: var(--text-muted);
    font-size: 16px;
}

/* 侧栏菜单：一级分组是「章节标签」，二级项是「带导轨的菜单项」。
   两级如果都做成同样的圆角可点条，人会分不清"这是分组还是入口"（2026-10-26 反馈）。 */
.menu {
    flex: 1;
    margin: 0;
    padding: 4px 0 20px;
    list-style: none;
}

.menu__section {
    margin: 0;
}

/* 分组之间的分隔线：先看到"换了一个章节"，再看到章节名。
   第一条分组前面可能是若干一级入口（门户），同样要断开 */
.menu__section + .menu__section,
.menu > li:not(.menu__section) + .menu__section {
    margin-top: 12px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
}

/* 一级：分组头。只有文字与展开箭头，**不做成可点菜单条的样式** */
.menu__group {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    margin: 6px 0 4px;
    padding: 6px 12px;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-align: left;
    cursor: pointer;
}

.menu__group:hover {
    background: transparent;
    color: var(--text);
}

.menu__group:focus-visible {
    outline: 2px solid var(--action);
    outline-offset: 2px;
}

/* 当前页所在章节：只换文字与箭头颜色，不加底色——底色是"选中项"的语义 */
.menu__section.has-active .menu__group {
    color: var(--action);
}

.menu__section.has-active .menu__chevron {
    color: var(--action);
}

.menu__chevron {
    flex: none;
    color: var(--text-disabled);
    font-size: 14px;
    line-height: 1;
    transition: transform var(--transition);
}

.menu__group[aria-expanded="false"] .menu__chevron {
    transform: rotate(-90deg);
}

/* 二级：向左缩进并加一条竖导轨，层级关系在视觉上"连着" */
.menu__children {
    margin: 2px 0 8px 16px;
    padding: 0 0 0 8px;
    border-left: 2px solid var(--border);
    list-style: none;
}

.menu__children li {
    margin: 2px 0;
}

.menu__link {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: var(--touch-target);
    padding: 10px 12px;
    border-radius: var(--radius-button);
    color: var(--text);
    font-size: 18px;
    font-weight: 500;
    text-decoration: none;
    transition: background-color var(--transition), color var(--transition);
}

.menu__link:hover {
    background: var(--surface-sunken);
}

.menu__link.is-active {
    background: var(--selected-surface);
    color: var(--action);
    font-weight: 600;
}

/* 二级菜单项：比一级入口小一号、左侧带圆点，避免与分组头/一级入口混淆 */
.menu__link--child {
    padding: 8px 12px;
    font-size: 17px;
    font-weight: 400;
}

.menu__link--child::before {
    content: "";
    flex: none;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--text-disabled);
}

.menu__link--child.is-active {
    font-weight: 600;
}

.menu__link--child.is-active::before {
    background: var(--action);
}

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

.topbar {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    min-height: var(--topbar-height);
    padding: 12px var(--gutter);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.topbar__left {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.topbar__title {
    margin: 0;
    font-size: 24px;
    font-weight: 700;
}

.topbar__right {
    display: flex;
    align-items: center;
    gap: 12px;
}

.topbar__user {
    text-align: right;
    line-height: 1.35;
}

.topbar__user-name {
    display: block;
    font-weight: 600;
    font-size: 17px;
}

.topbar__user-meta {
    display: block;
    color: var(--text-muted);
    font-size: 16px;
}

.menu-toggle {
    display: none;
}

/* ---------- 4. 内容区 ---------- */

.content {
    flex: 1;
    padding: 32px var(--gutter) 64px;
}

.panel {
    padding: 24px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}

.panel + .panel {
    margin-top: 24px;
}

.panel__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 20px;
}

.panel__title {
    margin: 0;
    font-size: 22px;
    font-weight: 700;
}

.toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
    margin-bottom: 20px;
}

.toolbar__field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.toolbar__label {
    color: var(--text-muted);
    font-size: 16px;
}

.toolbar .input,
.toolbar .select {
    min-width: 200px;
}

.toolbar__spacer {
    flex: 1;
}

/* 筛选栏：三个字段一排（P1）。手机上单列，不缩字号拼版 */
.filter-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    align-items: end;
    margin-bottom: 20px;
}

.filter-row--four {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.filter-row--action {
    grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
}

.filter-row--four-action {
    grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
}

.filters {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    align-items: end;
    margin-bottom: 20px;
}

.filter {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.filter__label {
    color: var(--text-muted);
    font-size: 16px;
}

@media (max-width: 760px) {
    .filter-row,
    .filter-row--four,
    .filter-row--action,
    .filter-row--four-action,
    .filters {
        grid-template-columns: minmax(0, 1fr);
    }
}

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 20px;
}

.metric {
    padding: 24px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}

.metric__label {
    margin: 0 0 8px;
    color: var(--text-muted);
    font-size: 16px;
}

.metric__value {
    margin: 0;
    font-size: 40px;
    font-weight: 700;
    line-height: 1.2;
}

.metric__hint {
    margin: 8px 0 0;
    color: var(--text-muted);
    font-size: 16px;
}

.detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 20px;
    margin: 0;
}

.detail-grid dt {
    margin-bottom: 4px;
    color: var(--text-muted);
    font-size: 16px;
}

.detail-grid dd {
    margin: 0;
    font-weight: 600;
    word-break: break-all;
}

/*
 * 值后面的小字标注（目前只有 M16 详情里的「未识别为数值」）。
 * 必须把 dd 的 600 字重压回正常：加粗的值 + 加粗的标注连在一起，
 * 看起来像值本身就叫这个名字，而它是我们加上去的判断。
 */
.detail-grid__note {
    margin-left: 4px;
    color: var(--text-muted);
    font-size: 16px;
    font-weight: 400;
}

/* ---------- 5. 表格 ---------- */

.table-wrap {
    overflow-x: auto;
}

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 17px;
}

.table th,
.table td {
    padding: 14px 10px;
    text-align: left;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.table th {
    color: var(--text-muted);
    font-size: 16px;
    font-weight: 600;
}

.table tbody tr:hover {
    background: var(--surface-sunken);
}

/*
 * 整行可点的表格（目前只有 M16 数据明细）。
 * 必须给鼠标手型：表格行默认是普通光标，点上去能开东西却没有一点提示，
 * 人会以为那一行本来就打不开，于是永远不去点它。
 */
.table tbody tr.is-clickable {
    cursor: pointer;
}

/*
 * 需要换行的列（长名称、标签组）。min-width 只保证不被压成竖排单字，
 * max-width 才是关键：没有它，一个很长的名字会把整张表撑出可视区。
 */
.table td.is-wrap {
    white-space: normal;
    min-width: 120px;
    max-width: 240px;
}

/*
 * 钉在右侧的列（操作列）。表格列多时 .table-wrap 会横向滚动，
 * 不钉住的话"停用 / 编辑"会被滚出可视区，用户以为没有这个功能。
 * 必须给不透明背景：否则底层单元格会从它下面透出来。
 */
.table th.is-sticky,
.table td.is-sticky {
    position: sticky;
    right: 0;
    background: var(--surface);
    box-shadow: -10px 0 12px -10px rgba(37, 38, 62, 0.22);
}

.table tbody tr:hover td.is-sticky {
    background: var(--surface-sunken);
}

.table__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.link-button {
    min-height: var(--touch-target);
    padding: 8px 4px;
    border: none;
    background: none;
    color: var(--action);
    font-family: inherit;
    font-size: 17px;
    cursor: pointer;
}

.link-button:hover {
    text-decoration: underline;
}

.link-button--danger {
    color: var(--error-text);
}

.pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 20px;
}

.pagination__info {
    color: var(--text-muted);
    font-size: 16px;
}

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

/* ---------- 6. 表单与对话框 ---------- */

.dialog {
    padding: 0;
    border: none;
    border-radius: var(--radius-dialog);
    background: var(--surface);
    box-shadow: var(--shadow-dialog);
    width: min(640px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    overflow: visible;
}

.dialog--wide {
    width: min(880px, calc(100vw - 32px));
}

.dialog::backdrop {
    background: rgba(37, 38, 62, 0.42);
}

.dialog__form {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 64px);
}

.dialog__head {
    padding: 24px 28px 16px;
}

.dialog__title {
    margin: 0;
    font-size: 24px;
    font-weight: 700;
}

.dialog__body {
    flex: 1;
    padding: 0 28px;
    overflow-y: auto;
}

.dialog__foot {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    padding: 20px 28px 24px;
    border-top: 1px solid var(--border);
}

.form-error {
    margin: 0 28px 16px;
    padding: 12px 16px;
    border-radius: var(--radius-input);
    background: var(--error-surface);
    color: var(--error-text);
    font-size: 16px;
}

.fieldset {
    margin: 0 0 24px;
    padding: 0;
    border: none;
}

.fieldset__legend {
    margin-bottom: 12px;
    font-size: 18px;
    font-weight: 700;
}

.fieldset__hint {
    margin: 0 0 12px;
    color: var(--text-muted);
    font-size: 16px;
}

.checkbox-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 4px 16px;
}

/* 授权对话框里"权限包 + 它自己的适用范围"这一组。
   范围必须紧贴在它所属的权限包下面：两者一旦分开摆放，
   读的人就会以为"先勾包、再单独勾客户"，那正是要避免的理解。 */
.grant-package {
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
}

.grant-package:last-child {
    border-bottom: none;
}

.grant-package__scope {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-top: 8px;
    padding-left: 32px;
}

.grant-package__scope .field__hint {
    margin: 6px 0 0;
    min-width: 64px;
}

.grant-package__scope .select {
    min-width: 240px;
    max-width: 320px;
}

/* ---------- 7. 提示与徽标 ---------- */

.badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 12px;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: 16px;
    white-space: nowrap;
}

.badge--success {
    background: var(--success-surface);
    color: var(--success-text);
}

.badge--warning {
    background: var(--warning-surface);
    color: var(--warning-text);
}

.badge--error {
    background: var(--error-surface);
    color: var(--error-text);
}

.badge--action {
    background: var(--selected-surface);
    color: var(--action);
}

.badge-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.toast-stack {
    position: fixed;
    right: 24px;
    bottom: 24px;
    z-index: 100;
    display: flex;
    flex-direction: column;
    gap: 12px;
    pointer-events: none;
}

.toast {
    max-width: 420px;
    padding: 14px 20px;
    border-radius: var(--radius-button);
    background: var(--text);
    color: var(--surface);
    font-size: 17px;
    box-shadow: var(--shadow-card);
}

.toast--error {
    background: var(--error-text);
}

.toast--success {
    background: var(--success-text);
}

/* ---------- 8. 空状态与加载 ---------- */

.empty {
    padding: 48px 24px;
    color: var(--text-muted);
    text-align: center;
}

.loading {
    padding: 32px 24px;
    color: var(--text-muted);
    text-align: center;
}

.skeleton-row td {
    height: 56px;
}

/* ---------- 9. 响应式 ---------- */

@media (max-width: 760px) {
    body {
        font-size: 16px;
    }

    .app {
        grid-template-columns: minmax(0, 1fr);
    }

    .sidebar {
        position: fixed;
        inset: 0 auto 0 0;
        z-index: 40;
        width: min(320px, 84vw);
        transform: translateX(-100%);
        transition: transform 0.2s ease;
    }

    .app.is-menu-open .sidebar {
        transform: translateX(0);
    }

    .app.is-menu-open::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 30;
        background: rgba(37, 38, 62, 0.4);
    }

    .menu-toggle {
        display: inline-flex;
    }

    .login__card {
        padding: 32px 24px;
    }

    .login__title {
        font-size: 29px;
    }

    .topbar__title {
        font-size: 20px;
    }

    .topbar__user-meta {
        display: none;
    }

    .content {
        padding: 24px var(--gutter) 48px;
    }

    .metric__value {
        font-size: 32px;
    }

    .table {
        font-size: 16px;
    }

    .dialog__head,
    .dialog__body,
    .dialog__foot {
        padding-left: 20px;
        padding-right: 20px;
    }
}

@media (max-width: 360px) {
    .login__title {
        font-size: 26px;
    }

    .toolbar .input,
    .toolbar .select {
        min-width: 140px;
    }
}

/* ---------- 10. 业务模块（M04 Task / M05 Script / M06 样品） ---------- */

/* 未接入 / 有差异这类需要一眼看见的提示。不用红色徽标而用文字色：
   它是"请注意这里"，不是"这里出错了"。 */
.text-error {
    color: var(--error-text);
    font-weight: 600;
}

.warn-list {
    margin: 0 0 16px;
    padding: 16px 20px 16px 40px;
    border-radius: var(--radius-card);
    background: var(--warning-surface);
    color: var(--warning-text);
    font-size: 17px;
}

.warn-list li + li {
    margin-top: 6px;
}

/*
 * 中性说明列表：与 warn-list 同形状，但**不是警告**。
 *
 * 用在两处：结案区里"这一版没有对应流程的环节"、以及"你当前不能结案/重开的原因"。
 * 两条都不表示有东西坏了——前者是"别去找了"，后者是"这件事不该由你点"。
 * 套上橙色警示框，人会以为自己做错了什么或者系统出了问题。
 */
.hint-list {
    margin: 0 0 16px;
    padding: 16px 20px 16px 40px;
    border-radius: var(--radius-card);
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: 17px;
}

.hint-list li + li {
    margin-top: 6px;
}

/* ---------- M08 调度台 ---------- */

/*
 * 顶部计数条。做成可点的按钮而不是纯展示，是因为"现在积压在哪一类"
 * 和"把那一类列出来"本来就是同一个问题的两半——分开做，人得先看数字、
 * 再自己去下面的筛选框里把同样的条件选一遍。
 */
.summary-bar__inner {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 20px;
}

.summary-chip {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 108px;
    /* 触控目标最小 44px（CLAUDE.md §3.8-3） */
    min-height: 44px;
    padding: 12px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
    background: var(--surface);
    cursor: pointer;
    text-align: left;
}

.summary-chip:hover {
    border-color: var(--action);
}

.summary-chip.is-active {
    border-color: var(--action);
    background: var(--selected-surface);
}

.summary-chip__value {
    font-size: 24px;
    font-weight: 600;
    line-height: 1.1;
    color: var(--text);
}

.summary-chip__label {
    font-size: 16px;
    color: var(--text-muted);
}

/* ---------- M08 日历与容量 ---------- */

.section-title {
    margin: 0 0 12px;
    font-size: 24px;
    font-weight: 700;
}

/*
 * 一天一张卡，而不是一个月一格的月历。一天里有好几个工种各自的占比，
 * 那点面积塞不下，月历必然被简化成一个颜色——而那个颜色会让人
 * 把"排了一半"读成"这天还挺空"。
 */
.day-card {
    padding: 20px 24px;
    margin-bottom: 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}

/* 不上班的那天仍然列出来，只是压暗：跳过它会让人以为区间少了几天 */
.day-card--off {
    background: var(--surface-sunken);
}

.day-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
}

.day-card__schedules {
    margin: 12px 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.day-card__schedules li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    font-size: 17px;
}

/* 日历里的表比主表格矮一档：它嵌在日卡片里，行高按主表格来会喧宾夺主 */
.table--tight th,
.table--tight td {
    padding: 8px 10px;
    font-size: 16px;
}

.people-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 12px;
}

.person-chip {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 180px;
    min-height: 44px;
    padding: 10px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
    background: var(--surface);
    cursor: pointer;
    text-align: left;
    font-size: 16px;
}

.person-chip:hover {
    border-color: var(--action);
}

.person-chip.is-active {
    border-color: var(--action);
    background: var(--selected-surface);
}

.person-chip.is-off {
    opacity: 0.6;
}

/* ---------- Task 详情 ---------- */

.detail-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 20px;
}

.detail-head__title {
    margin: 0;
    font-size: 28px;
    font-weight: 700;
}

.detail-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 20px;
}

.detail-grid__item {
    min-width: 0;
}

.detail-grid__label {
    margin-bottom: 4px;
    color: var(--text-muted);
    font-size: 16px;
}

.detail-grid__value {
    font-weight: 600;
    word-break: break-word;
    /* 文案类字段可能带换行（主标题/副标题/投稿文都是一个字段内的多行），
       不设 pre-wrap 的话界面上看到的是一行，复制出去却带换行——看到的不是存的 */
    white-space: pre-wrap;
    user-select: text;
}

/* 数量看板：一格一件事。挤成"完成数量"是最要避免的做法，
   所以这里每一格都留够呼吸空间，宁可多滚一次屏。 */
.metric-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
}

.metric-grid .metric {
    padding: 16px 20px;
}

.metric__value--muted {
    color: var(--text-muted);
    font-size: 22px;
    font-weight: 600;
}

.todo-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.todo {
    padding: 14px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
}

.todo__main {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

.todo__title {
    font-weight: 600;
}

.todo__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 20px;
    margin-top: 6px;
    color: var(--text-muted);
    font-size: 16px;
}

/* 没有权限时写清缺哪个动作；它比周围正文更重要，不能淹没在灰字里 */
.todo__denied {
    color: var(--warning-text);
    font-size: 16px;
}

/* 待办「去处理」跳到下方区块时闪一下，否则页面滚下去了人也不知道看哪 */
.is-flash {
    animation: section-flash 1.6s ease-out;
}

@keyframes section-flash {
    0% { box-shadow: 0 0 0 0 rgba(98, 84, 217, 0.45); }
    30% { box-shadow: 0 0 0 6px rgba(98, 84, 217, 0.18); }
    100% { box-shadow: 0 0 0 0 rgba(98, 84, 217, 0); }
}

.text-block + .text-block {
    margin-top: 20px;
}

.text-block__body {
    margin: 6px 0 0;
    white-space: pre-wrap;
}

.file-list {
    margin: 6px 0 0;
    padding: 0;
    list-style: none;
    font-size: 17px;
}

.file-list li {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: baseline;
}

.file-list__meta {
    color: var(--text-muted);
    font-size: 16px;
}

.subsection {
    margin-top: 24px;
}

.subsection__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.subsection__title {
    margin: 24px 0 12px;
    font-size: 18px;
    font-weight: 700;
}

.subsection__head .subsection__title {
    margin-top: 0;
}

/* 流程快照：当前一版在上一行，历史版本逐版列开。
   "改之前是什么流程"必须一眼可查，因此不折叠、不隐藏。 */
.flow-current {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 12px;
}

.flow-current__text {
    font-weight: 600;
}

.flow-current__meta,
.flow-version__meta {
    color: var(--text-muted);
    font-size: 16px;
}

.flow-history {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.flow-version {
    padding: 14px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
    background: var(--surface-sunken);
}

.flow-version__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

.flow-version__reason {
    font-weight: 600;
}

.flow-version__text,
.flow-version__from {
    margin: 8px 0 0;
    font-size: 17px;
}

.flow-version__from {
    color: var(--text-muted);
}

/* 变更内容的"原值 → 现值" */
.diff {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 260px;
}

/* Task 时间轴（P-87）：三段的可见性条。
   「权限不足」用警示色、与「没有记录」的中性色分开——两者读起来都是
   "什么都没有"，但一个该去要权限，另一个才是真的空。用同一种颜色写这两件事，
   等于让用户自己去猜。 */
.timeline-segments {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 16px 0;
}

.timeline-segment {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
    background: var(--surface-sunken);
    font-size: 16px;
}

.timeline-segment.is-denied {
    background: var(--warning-surface);
    color: var(--text);
}

.timeline-segment__note {
    color: var(--text-muted);
    font-size: 16px;
}

/* 时间轴里的"这是业务日，不是精确时刻"这类括注。比正文小半档、弱一档，
   但仍然在 16px 以上——辅助文字不缩到看不清（CLAUDE.md §3.8-3）。 */
.timeline-hint {
    color: var(--text-muted);
    font-size: 16px;
}

/* 时间轴的筛选栏不吸顶：它下面就是要读的表格，吸顶会挡掉第一行 */
.timeline-filters {
    margin-bottom: 12px;
}

/* 时间轴单元格里的多个小片段（对象类型 + 编号 + 显示名 + 徽标）。
   要有个容器给 gap：几个 inline 元素直接相邻会连成一个词
   （「样品SMP-000001样品寄出」），浏览器上才看得出来。 */
.timeline-cell {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

/* 时间单元格竖排，且日期/时刻那一行不折行：横排时「2026-10-16」会被从中间
   断开成「2026-10-」+「16」，断开的日期很难一眼读对。
   注意只对"值"那一行 nowrap，辅助说明（如「业务日（无精确时刻）」）要能折行
   ——它比日期长，硬撑着就把隔壁列压掉了。 */
.timeline-when {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}

.timeline-when > span {
    white-space: nowrap;
}

.timeline-when > span.timeline-hint {
    white-space: normal;
}

/* 对象编号不折行：`SMP-` / `000001` 断成两行之后，报编号的人很难读准。 */
.timeline-code {
    white-space: nowrap;
}

/* 时间轴那 7 列的宽度分配。不设的话浏览器按内容平均分，
   结果是把「说明」挤到面板外——而说明恰恰是最该被看到的一列。
   这里按"这一列需要多宽"明确给：时间和事实窄、说明宽。 */
.timeline-table {
    table-layout: fixed;
    width: 100%;
    /* 1000px 是「最长的对象编号放得下」的下限：最长的编号是 `TASK-20261013-00001`
       （19 字符，约 200px），它所在的列按 21% 算，在 1000px 宽度上给到 210px。
       低于这个宽度就横向滚动（`.table-wrap` 本来就是干这个的），
       而不是把编号压到隔壁列上。 */
    min-width: 1000px;
}

/*
 * 两件事都在这一条里：
 *
 * 1) `white-space: normal` 覆盖 `.table` 的全局 nowrap。本表列窄，一旦某格内容
 *    比列宽长，nowrap 会让它**溢出到隔壁列上**——实测「佐藤 翔（发布运营）」压住了
 *    「说明」的第一行，「2026-10-16 ~ 2026-10-17」压住了「工序当前计划」。
 *    压过去之后读到的是一条错的事实，而且没有任何报错，最坏的情况是日期被读成一个
 *    相邻列的数字拼出来的"日期"。所以本表必须能折行。
 *
 * 2) `overflow: hidden` 是兜底：万一将来某格还是装不下，宁可看见"这里被切掉了"，
 *    也不要它安静地改掉隔壁列的意思。兜底不该被触发——所以验收时逐个单元格量
 *    scrollWidth 与 clientWidth，触发过就说明还有列宽没配够。
 */
.timeline-table th,
.timeline-table td {
    white-space: normal;
    overflow: hidden;
}

/*
 * 每列给多少，是按"这一列里最长的那条**不可折行**内容"定的，不是按平均分：
 *   时间 18% —— 「2026-10-12 10:00」这一行 nowrap；
 *   事实 14% —— 「工序当前计划」+ 分列徽标，可折行；
 *   对象 21% —— `TASK-20261013-00001` nowrap，全表最宽的一条，这一列是它的下限；
 *   工序  8% —— 「人工剪辑」；
 *   状态 12% —— 徽标「中台审核通过」；
 *   责任人 10%、说明 17% —— 都可折行，宽度只影响行高。
 * 验收时逐格量过 scrollWidth，触发过 overflow 就说明这里配小了。
 */
.timeline-table th:nth-child(1) { width: 18%; }   /* 时间（含量） */
.timeline-table th:nth-child(2) { width: 14%; }   /* 事实（含分列） */
.timeline-table th:nth-child(3) { width: 21%; }   /* 对象 */
.timeline-table th:nth-child(4) { width: 8%; }    /* 工序 */
.timeline-table th:nth-child(5) { width: 12%; }   /* 状态/结论 */
.timeline-table th:nth-child(6) { width: 10%; }   /* 责任人 */
.timeline-table th:nth-child(7) { width: 17%; }   /* 说明 */

.diff-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px;
    font-size: 17px;
}

.diff-line__label {
    color: var(--text-muted);
}

.diff-line__from {
    text-decoration: line-through;
    color: var(--text-muted);
}

.diff-line__arrow {
    color: var(--text-muted);
}

.diff-line__to {
    font-weight: 600;
}

/* ---------- Task 创建向导 ---------- */

.wizard .steps {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin: 0 0 20px;
    padding: 0;
    list-style: none;
}

.step {
    display: flex;
    flex: 1;
    align-items: center;
    gap: 10px;
    min-width: 180px;
    padding: 12px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
    color: var(--text-muted);
    font-size: 17px;
}

.step__index {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    font-weight: 700;
    font-size: 16px;
}

.step.is-active {
    border-color: var(--action);
    background: var(--selected-surface);
    color: var(--text);
}

.step.is-active .step__index {
    background: var(--action);
    color: var(--on-action);
}

.step.is-done {
    border-color: transparent;
    background: var(--success-surface);
    color: var(--success-text);
}

.step.is-done .step__index {
    background: var(--success-surface);
    color: var(--success-text);
}

.checkbox-group {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 4px 16px;
}

.checkbox--wide {
    display: flex;
    align-items: center;
}

.step__title {
    font-size: 17px;
}


.checkbox__label {
    font-size: 17px;
}

/* ---------- M05 Script ---------- */

.cards--tight {
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
}

/* 左原稿 / 右五字段：核对的正确姿势是"照着左边填右边"，
   所以两块必须同屏，不能做成两个弹窗让人来回切。 */
.split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 24px;
}

.split__left,
.split__right {
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-width: 0;
}

.grid-preview {
    max-height: 460px;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-input);
}

.grid,
.grid-wrap .grid {
    display: grid;
    font-size: 16px;
}

.grid-cell {
    padding: 8px 12px;
    border-right: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    min-width: 120px;
    max-width: 280px;
    vertical-align: top;
    word-break: break-word;
}

.grid-cell--index {
    position: sticky;
    left: 0;
    min-width: 56px;
    background: var(--surface-sunken);
    color: var(--text-muted);
    text-align: center;
}

.grid-cell.is-source {
    background: var(--selected-surface);
    box-shadow: inset 0 0 0 2px var(--action);
}

.script-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.script-card {
    padding: 20px;
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}

.script-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

.script-card__label {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    background: var(--selected-surface);
    color: var(--action);
    font-size: 16px;
    font-weight: 600;
}

.script-card__name {
    font-size: 19px;
    font-weight: 700;
}

.script-card__code,
.script-card__plan {
    color: var(--text-muted);
    font-size: 16px;
}

.script-card__fields {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 14px;
}

.script-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-top: 14px;
    color: var(--text-muted);
    font-size: 16px;
}

.script-card__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}

/* 一个摘取字段一行：字段名 / 取值 / 来源格。
   "值从哪一格来"必须跟着值一起显示，否则核对时无从下手。 */
.field-line {
    display: grid;
    grid-template-columns: 120px minmax(0, 1fr) minmax(0, 220px);
    gap: 12px;
    align-items: baseline;
}

.field-line__name {
    color: var(--text-muted);
    font-size: 16px;
}

.field-line__value {
    font-weight: 600;
    word-break: break-word;
    white-space: pre-wrap;
    user-select: text;
}

.field-line__value.is-missing {
    color: var(--text-muted);
    font-weight: 400;
}

.field-line__source {
    color: var(--text-muted);
    font-size: 16px;
    word-break: break-all;
}

.import-result {
    padding: 16px 20px;
    border-radius: var(--radius-card);
    background: var(--surface-sunken);
}

.import-result__title {
    margin: 0 0 8px;
    font-weight: 700;
}

.import-result__list {
    margin: 0;
    padding-left: 20px;
    font-size: 17px;
}

/* 「封面设计」区的逐版提取结果（导入回执）。
   长文案完整折行、不加省略号、不设最大高度：这里就是给人核对原文的地方，
   省掉的字恰好可能是唯一对不上的那几笔。值都是可选择文本，方便直接复制。 */
.cover-result {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
    user-select: text;
}

.cover-result__title {
    margin: 0 0 12px;
    font-weight: 700;
}

.cover-region {
    margin-bottom: 16px;
    padding: 12px 16px;
    border-radius: var(--radius-button);
    background: var(--surface);
}

.cover-region__head {
    margin: 0 0 6px;
    font-weight: 600;
    word-break: break-word;
}

.cover-region__title {
    color: var(--text-muted);
    font-weight: 400;
}

.cover-region__note {
    margin: 0 0 8px;
    color: var(--text-muted);
    font-size: 16px;
}

.cover-version {
    margin-top: 10px;
}

.cover-version__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px;
    margin: 0 0 6px;
}

.cover-version__range {
    color: var(--text-muted);
    font-size: 16px;
}

.cover-version__note {
    color: var(--text-muted);
    font-size: 16px;
}

.cover-version__fields {
    display: grid;
    gap: 6px;
}

.cover-field {
    display: grid;
    grid-template-columns: 160px minmax(0, 1fr) auto;
    gap: 12px;
    align-items: baseline;
}

.cover-field__name {
    color: var(--text-muted);
    font-size: 16px;
}

.cover-field__value {
    font-weight: 600;
    white-space: pre-wrap;
    word-break: break-word;
}

.cover-field__value.is-missing {
    color: var(--text-muted);
    font-weight: 400;
}

.cover-field__value.is-warn {
    color: var(--warning-text);
    font-weight: 600;
}

.cover-field__reason {
    color: var(--text-muted);
    font-size: 16px;
    font-weight: 400;
}

.cover-field__source {
    color: var(--text-muted);
    font-size: 16px;
    white-space: nowrap;
}

/* 安排/改文案对话框里的投稿文：只读，但必须能完整看到并选中复制——
   它随导表带出，用户核对时最想比的就是这一段。 */
.publish-title-note {
    margin: 8px 0 0;
}

.publish-title-note__value {
    margin-top: 4px;
    padding: 8px 12px;
    border-radius: var(--radius-button);
    background: var(--surface-sunken);
    white-space: pre-wrap;
    word-break: break-word;
    user-select: text;
}

.plan__footer {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 20px;
    margin-top: 16px;
}

.plan__total {
    font-size: 20px;
    font-weight: 700;
}

.field__row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* ---------- M06 样品 ---------- */

.gate {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}

.gate__hint {
    color: var(--text-muted);
    font-size: 16px;
}

/* 流水里被点到的那张寄样单高亮出来，避免"打开明细后不知道哪条是它" */
.table tbody tr.is-highlight {
    background: var(--selected-surface);
}

/* ---------- M10 审核中心 ---------- */

/*
 * 内联播放器。M09 的试看与 M10 的客户审核、外部审核都要开一个 <video>。
 * 收成一个类而不是各处写 style：三处各写一遍就会慢慢分叉
 * （本项目已踩过一次同类的坑：`**` 原样显示在 4 个地方）。
 */
.player {
    display: block;
    width: 100%;
    max-height: 60vh;
    background: #000;
    border-radius: var(--radius-button);
}

/*
 * 审核意见里的图片。
 * 限制最大宽度并**保留原始比例**：一张手机截图往往是 1080×2400，
 * 不限宽会把整块版面撑成一条竖线，旁边的意见文字被挤到看不见。
 */
.opinion-image {
    display: block;
    max-width: 320px;
    max-height: 240px;
    object-fit: contain;
    border: 1px solid var(--border);
    border-radius: var(--radius-input);
    background: var(--surface-sunken);
    cursor: zoom-in;
}

.opinion-image--full {
    max-width: 100%;
    max-height: 80vh;
    cursor: default;
}

.opinion-image-box {
    min-width: 120px;
}

/* 驳回对话框里已上传图片的小缩略图：用来确认"选的是哪一张"，不承担查看。 */
.opinion-thumb {
    width: 56px;
    height: 56px;
    object-fit: cover;
    border: 1px solid var(--border);
    border-radius: var(--radius-input);
    background: var(--surface-sunken);
}

/*
 * 对话框里"确认要操作这几条"的清单。
 * 用浏览器默认的项目符号（不去掉 list-style）——批量通过时人需要一眼数清
 * 到底勾了几条，一串没有标记的短行会被看成一段连续文字。
 * 只把缩进收窄，避免长视频名换行后与项目符号对不齐。
 */
.plain-list {
    margin: 0 0 16px;
    padding-left: 22px;
    font-size: 17px;
}

.plain-list li + li {
    margin-top: 4px;
}

/* ---------- M10 外部审核方页面（无登录，独立 HTML） ---------- */

/*
 * 外部方多半是用手机打开的：整页居中、留出边距，卡片纵向排。
 * 这一屏**没有侧边菜单**，所以不能复用 .app 那套外壳的栅格。
 */
.external-shell {
    max-width: 880px;
    margin: 0 auto;
    padding: 32px 16px 64px;
    display: flex;
    flex-direction: column;
    gap: 20px;
}

/*
 * 一条片子的卡片：视频 → 操作 → 记录。
 * 一条一段竖着排，而不是塞进表格——外部方要一边看视频一边写字，
 * 表格里那一格放不下播放器。
 */
.case {
    padding: 16px 0;
    border-top: 1px solid var(--border);
}

.case:first-child {
    border-top: none;
    padding-top: 0;
}

.case__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

@media (max-width: 760px) {
    .detail-head {
        flex-direction: column;
        align-items: flex-start;
    }

    .detail-head__title {
        font-size: 24px;
    }

    .split {
        grid-template-columns: minmax(0, 1fr);
    }

    .field-line {
        grid-template-columns: minmax(0, 1fr);
        gap: 2px;
    }

    .step {
        min-width: 0;
    }

    .grid-preview {
        max-height: 320px;
    }
}

/* 面板级的一句说明：不用点，但要读（M11 素材池的「来源现状」说明）。
   与 .field__hint 同色同字号，区别是它挂在 panel 下面、要自己留出下边距 */
.panel__hint {
    margin: 0 0 16px;
    color: var(--text-muted);
    font-size: 16px;
}

/* ==================================================================== 10. 日本客户门户（M14）
 *
 * 门户与内部后台共用这一份样式表（CLAUDE.md §2.1：前后端同一套代码、同一个部署），
 * 所以这一段只加门户**独有**的东西，不复写内部已有的卡片、面板、徽标。
 * 两条贯穿全段的：
 *   1. 触控目标 ≥ --touch-target（44px）——门户是手机优先，手指比鼠标粗（PRD `:472`）；
 *   2. 不依赖 hover——悬停才出现的东西在手机上没有意义（M14 §3.9）。
 */

/* ---- 界面语言切换（顶栏右上角 / 「账号设置」页共用） ---- */
.locale-switch {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
}

.locale-switch__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 触控目标：这个按钮比正文小，但面积不能小 */
    min-height: var(--touch-target);
    padding: 0 16px;
    border: none;
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 16px;
    cursor: pointer;
    transition: background-color var(--transition), color var(--transition);
}

/* 选中态不靠 hover，靠**常驻**的底色 + 字重：手机上根本没有悬停那一步 */
.locale-switch__item.is-active {
    background: var(--surface);
    color: var(--text);
    font-weight: 600;
    box-shadow: 0 1px 2px rgba(37, 38, 62, 0.08);
}

.locale-switch__item:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* 「账号设置」页里那一组比顶栏的大一点：那一页有位置，也不挤 */
.locale-switch--inline {
    margin-top: 4px;
}

/* ---- 门户的一行「名称 → 值」 ----
 * 不复用 .field-line：那是三列网格（名称 / 值 / 来源），门户这里只有两列，
 * 硬用会在右边留一条 220px 的空档，窄屏折行时更明显 */
.portal-field {
    display: grid;
    grid-template-columns: 180px minmax(0, 1fr);
    gap: 4px 12px;
    align-items: baseline;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}

.portal-field:last-child {
    border-bottom: none;
}

.portal-field__name {
    color: var(--text-muted);
    font-size: 16px;
}

.portal-field__value {
    font-weight: 600;
    word-break: break-word;
}

.portal-field__value.is-missing {
    color: var(--text-muted);
    font-weight: 400;
}

/* ---- Task 列表：卡片，不是表格 ----
 * 「我这个单到哪了」是要一行读完的；8 个字段的数据表在手机上只能横向拖（M14 §3.9）。
 * 桌面两列、手机一列，同一份 DOM */
.portal-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.portal-card {
    display: block;
    padding: 20px 22px;
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--surface);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--transition), box-shadow var(--transition);
}

/* 悬停只是"锦上添花"，不承载任何信息——手机上永远不会触发它 */
.portal-card:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-card);
}

.portal-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
}

.portal-card__title {
    font-size: 19px;
    font-weight: 600;
    word-break: break-word;
}

.portal-card__code {
    color: var(--text-muted);
    font-size: 16px;
}

.portal-card__meta {
    margin-top: 4px;
    word-break: break-word;
}

.portal-card__foot {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 16px;
}

@media (max-width: 760px) {
    /* 一列。两列会给每个卡片压到不足 160px 宽，标题一定折成三行 */
    .portal-cards {
        grid-template-columns: minmax(0, 1fr);
    }

    /* 名称与值上下排：180px 的名称列在 320px 宽的屏上要占掉一多半 */
    .portal-field {
        grid-template-columns: minmax(0, 1fr);
        gap: 2px;
    }

    /* 顶栏在窄屏放不下：标题 + 语言切换 + 用户名 + 退出登录。
       语言切换是这一行里最宽的一个（两个按钮），不让它换行就会把退出登录
       挤出屏幕外——而那是**离开这一页唯一的路**（M14 §3.9）。
       换行后两行都装得下：`[语言切换][用户名]` / `[退出登录]`。
       **不收掉用户名**：内部后台本来也显示它，收了是拿别人的功能补门户的窟窿 */
    .topbar,
    .topbar__right {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    /* 顶栏那几个按钮在窄屏都拉到触控下限，别让人戳偏 */
    .topbar .button--small,
    .topbar .locale-switch__item {
        min-height: var(--touch-target);
    }
}
/* ============================ M15 流程与字典（P-26 / P-28） ============================ */

/* ---- 流程的三方对照（确认页）----
   继承来源 / 本次修改 / 将生成的快照 并排。
   窄屏改成上下叠：三列并排时每列都太窄，而这三块是要逐项比对的，
   挤成三细条就比不了了 */
.compare {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
    margin: 12px 0 4px;
}

.compare__col {
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--surface-sunken);
    min-width: 0;
}

.compare__title {
    margin: 0 0 12px;
    font-size: 18px;
    color: var(--text);
}

.compare__col .detail-grid {
    margin: 0;
    grid-template-columns: 1fr;
}

.compare__list {
    margin: 0;
    padding-left: 20px;
    color: var(--text);
    line-height: 1.7;
}

/* ---- 流程预览 ----
   逐节点「出现 / 跳过」。跳过的整行压暗但不隐藏——
   只列出现的节点时，"跳过了"与"不在清单里"看起来一模一样 */
.flow-preview {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.flow-preview__item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-button);
}

.flow-preview__item.is-skipped {
    opacity: 0.55;
}

.flow-preview__name {
    font-size: 18px;
}

/* 「由『xx』控制」：辅助文字，最小 16px（§3.8-3 不缩字号拼版） */
.flow-preview__by {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 16px;
}

/* ---- 业务字典：按 dict_type 分组 ----
   类型本身也是数据（"不写死分类"），所以每一组要有自己的标题 */
.dict-group {
    margin-bottom: 28px;
}

.dict-group:last-child {
    margin-bottom: 0;
}

.dict-group__title {
    margin: 0 0 12px;
    font-size: 18px;
    /* dict_type 是机器标识（如 MATERIAL_TYPE），等宽更好读。
       本项目没有 --font-mono 这个令牌，直接写字面量，不引一个不存在的变量 */
    font-family: monospace;
    color: var(--text);
}

@media (max-width: 760px) {
    /* 三方对照在窄屏改上下叠，见上面那段说明 */
    .compare {
        grid-template-columns: 1fr;
    }
}

/* ==========================================================================
   15 阶段业务进度（2026-10-25 优化 V1）
   ========================================================================== */
.stage-chain__controls {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.stage-chain__controls .button.is-active {
    border-color: var(--action);
    color: var(--action);
    background: var(--action-soft, #F1EFFF);
}

.stage-chain__summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin: 8px 0 12px;
    padding: 16px 18px;
    border: 1px solid var(--border);
    border-radius: 18px;
    background: linear-gradient(135deg, #F8F6FF 0%, #FCFBFF 100%);
}

.stage-chain__summary strong {
    display: block;
    font-size: 20px;
    color: var(--text);
}

.stage-chain__summary-note {
    margin: 4px 0 0;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1.45;
}

.stage-chain__summary-badge {
    flex: 0 0 auto;
}

.stage-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 18px;
    margin-bottom: 14px;
    color: var(--text-muted);
    font-size: 16px;
}

.stage-legend__item {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.stage-legend__dot {
    width: 12px;
    height: 12px;
    border-radius: 999px;
    border: 2px solid transparent;
    display: inline-block;
}

.stage-legend__dot--done { background: #DDF3E6; border-color: #8FCBA6; }
.stage-legend__dot--mine { background: #F1EFFF; border-color: var(--action); }
.stage-legend__dot--other { background: #FFF6E6; border-color: #D99A32; }
.stage-legend__dot--parallel { background: #EFF6FF; border-color: #7FA9D9; }
.stage-legend__dot--pending { background: #F4F4F8; border-color: #C9C9D6; }
.stage-legend__dot--rework { background: #FFF0F0; border-color: #D97373; }
.stage-legend__dot--na { background: transparent; border-color: #B9BACB; border-style: dashed; }

.stage-chain {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 12px;
}

.stage-chain.is-single-row {
    display: flex;
    overflow-x: auto;
    padding-bottom: 8px;
}

.stage-chain.is-single-row .stage-card {
    min-width: 230px;
    flex: 0 0 230px;
}

.stage-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-height: 190px;
    padding: 14px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: #FFFFFF;
    color: var(--text);
    outline: none;
}

.stage-card:focus-visible {
    box-shadow: 0 0 0 3px rgba(98, 84, 217, 0.25);
}

.stage-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.stage-card__order {
    color: var(--text-muted);
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.stage-card__title {
    margin: 0;
    font-size: 18px;
    line-height: 1.3;
}

.stage-card__note {
    margin: 0;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.stage-card__note--empty {
    color: #A7A8BA;
}

.stage-card__counts {
    margin: 0;
    font-size: 16px;
    color: var(--text);
}

.stage-card__executor {
    margin: 0;
    font-size: 16px;
    color: var(--text-muted);
}

.stage-card__action {
    margin-top: auto;
    width: 100%;
}

.stage-card--done { background: #F2FAF5; border-color: #CFE9D8; }
.stage-card--mine { background: #F5F3FF; border: 2px solid var(--action); }
.stage-card--other { background: #FFFAF0; border: 2px solid #E0A548; }
.stage-card--parallel { background: #F2F7FF; border-color: #C9DDF8; }
.stage-card--pending { background: #FAFAFC; color: #6B6C82; }
.stage-card--rework { background: #FFF3F3; border-color: #E8B4B4; }
.stage-card--na { background: transparent; border: 1px dashed #B9BACB; color: #8F90A6; }

@media (max-width: 1200px) {
    .stage-chain { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
    .stage-chain { grid-template-columns: 1fr; }
    .stage-chain__summary { flex-direction: column; align-items: flex-start; }
    .stage-chain__controls { justify-content: flex-start; }
}

.detail-head__stage {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.detail-head__stage .fieldset__hint {
    margin: 0;
    font-size: 16px;
    line-height: 1.45;
}

.stage-card__denied {
    margin: 0;
    color: var(--text-muted);
    font-size: 16px;
}

/* ==========================================================================
   拍摄中台（周/月，只读）
   ========================================================================== */
.pcal-head {
    margin-bottom: 12px;
}

.pcal-switch {
    display: inline-flex;
    gap: 4px;
    padding: 2px;
    border: 1px solid var(--border);
    border-radius: 12px;
}

.pcal-range {
    font-size: 18px;
    color: var(--text);
    margin: 0 6px;
}

.pcal-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 18px;
    margin-top: 8px;
    color: var(--text-muted);
    font-size: 16px;
}

.pcal-legend__item {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.pcal-legend__dot {
    width: 14px;
    height: 10px;
    border-radius: 4px;
    display: inline-block;
}

.pcal-legend__dot--bar { background: #E3F4EA; border: 1px solid #8FCBA6; }
.pcal-legend__dot--tentative { background: #FFF6E6; border: 1px dashed #D99A32; }

.pcal-legend__note {
    margin-left: auto;
}

.pcal-scroll {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: #fff;
}

.pcal-grid {
    display: grid;
    min-width: 760px;
}

.pcal-cell {
    border-right: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    min-height: 52px;
    padding: 6px 8px;
    background: #fff;
    min-width: 0;
}

.pcal-cell--corner {
    background: #FAFAFE;
    font-weight: 600;
    color: var(--text-muted);
    position: sticky;
    left: 0;
    z-index: 2;
}

.pcal-cell--day {
    background: #FAFAFE;
    text-align: center;
    font-size: 16px;
    color: var(--text-muted);
}

.pcal-cell--day.is-weekend,
.pcal-cell--slot.is-weekend {
    background: #F6F6FA;
}

.pcal-cell--day.is-today,
.pcal-cell--slot.is-today {
    background: #F1EFFF;
    color: var(--action);
}

.pcal-cell--person {
    background: #FCFCFF;
    position: sticky;
    left: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    gap: 4px;
    align-items: flex-start;
}

.pcal-cell--person .muted {
    font-size: 14px;
}

.pcal-empty {
    color: var(--text-muted);
    background: #FCFCFF;
}

.pcal-cell--slot {
    background: #fff;
    min-height: 52px;
}

.pcal-bar {
    z-index: 3;
    align-self: center;
    margin: 6px 3px;
    padding: 6px 10px;
    border-radius: 10px;
    background: #E3F4EA;
    border: 1px solid #8FCBA6;
    color: #1F5B36;
    cursor: pointer;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 46px;
}

.pcal-bar:hover {
    box-shadow: 0 0 0 2px rgba(98, 84, 217, 0.25);
}

.pcal-bar--tentative {
    background: #FFF6E6;
    border: 1px dashed #D99A32;
    color: #7A4B00;
}

.pcal-bar__name {
    font-weight: 600;
    font-size: 15px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pcal-bar__meta {
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media (max-width: 760px) {
    .pcal-range { font-size: 16px; }
    .pcal-legend__note { margin-left: 0; }
    .pcal-grid { min-width: 680px; }
}

/* ---------- 视频管理：分组 + 卡片网格（优化 V1 §3.11） ---------- */

.video-group {
    margin-bottom: 28px;
}

.group-head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
    margin-bottom: 16px;
}

.group-head__title {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.group-head__select {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 16px;
    flex: none;
}

.video-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
}

.video-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.video-card__media {
    position: relative;
    /* 产品要求 16:9（P12）；原来的 min-height:120px 是一块没有内容的空白 */
    aspect-ratio: 16 / 9;
    min-height: 0;
    overflow: hidden;
    background: var(--surface-sunken);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    cursor: pointer;
    color: var(--action);
}

/* 首帧封面：<video> 只当海报，不加 controls；点击仍交给外层打开试看弹窗 */
.video-card__poster {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
}

.video-card__media--disabled {
    cursor: not-allowed;
    opacity: .55;
}

.video-card__play,
.video-card__media-hint {
    position: relative;
    z-index: 1;
}

.video-card__play {
    font-size: 30px;
    line-height: 1;
    color: #fff;
    text-shadow: 0 1px 6px rgba(0, 0, 0, .55);
}

.video-card__media-hint {
    font-size: 16px;
    color: var(--text-muted);
    background: rgba(255, 255, 255, .82);
    border-radius: 999px;
    padding: 1px 8px;
}

/* 状态标签与播放/判定同一行：标签在左，操作在右（P12） */
.video-card__review-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
}

.video-card__body {
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

.video-card__head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.video-card__title {
    font-weight: 600;
    color: var(--text);
    text-decoration: none;
}

.video-card__name {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
    word-break: break-word;
}

.video-card__meta {
    margin: 0;
    font-size: 16px;
    color: var(--text-muted);
    word-break: break-word;
}

.video-card__review,
.video-card__note {
    margin: 0;
    font-size: 16px;
    word-break: break-word;
}

.video-card__actions {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    margin-top: 4px;
}

@media (max-width: 1100px) {
    .video-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 760px) {
    .video-grid {
        grid-template-columns: 1fr;
    }
}
