/* ============================================================
   PDF 文智助手 · 界面样式
   深色主题 · 冷灰底 + 蓝色主强调 + 语义色（绿/橙/红/紫）
   设计令牌集中在 :root，改这里即可整体换肤
   ============================================================ */
:root {
  /* —— 层级底色：越靠上层越亮 —— */
  --bg:            #0e1014;
  --bg-panel:      #15181d;
  --bg-elev:       #1b1f25;
  --bg-hover:      #232830;
  --bg-active:     #2a3039;
  --bg-stage:      #0a0c0f;

  /* —— 描边：统一用半透明白，叠在任何底色上都不突兀 —— */
  --bd:            rgba(255, 255, 255, .085);
  --bd-soft:       rgba(255, 255, 255, .05);
  --bd-strong:     rgba(255, 255, 255, .15);

  /* —— 文字三级 —— */
  --tx:            #e9ecf1;
  --tx-2:          #a6afbc;
  --tx-3:          #6b7583;

  /* —— 主强调色 —— */
  --accent:        #5b8cff;
  --accent-2:      #9dbcff;
  --accent-soft:   rgba(91, 140, 255, .13);
  --accent-line:   rgba(91, 140, 255, .34);
  --accent-glow:   rgba(91, 140, 255, .30);

  /* —— 语义色 —— */
  --green:         #3ecf8e;
  --green-soft:    rgba(62, 207, 142, .13);
  --orange:        #f2a33c;
  --orange-soft:   rgba(242, 163, 60, .13);
  --red:           #f26a5f;
  --red-soft:      rgba(242, 106, 95, .13);
  --purple:        #ac82ff;
  --purple-soft:   rgba(172, 130, 255, .13);

  /* —— 尺寸 —— */
  --header-h:      56px;
  --r-sm:          6px;
  --radius:        9px;
  --radius-l:      14px;
  --radius-xl:     18px;

  /* —— 阴影分层 —— */
  --sh-1:          0 1px 2px rgba(0, 0, 0, .28);
  --sh-2:          0 8px 22px rgba(0, 0, 0, .40), 0 2px 6px rgba(0, 0, 0, .28);
  --sh-pop:        0 28px 80px rgba(0, 0, 0, .64), 0 8px 24px rgba(0, 0, 0, .46);
  --shadow-pop:    var(--sh-pop);        /* 旧名兼容 */

  /* —— 动效 —— */
  --ease:          cubic-bezier(.4, 0, .2, 1);
  --dur:           .16s;
}


* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--bg);
  color: var(--tx);
  font: 13px/1.55 "Microsoft YaHei UI", "Microsoft YaHei", -apple-system, "Segoe UI", Roboto, "PingFang SC", "Helvetica Neue", sans-serif;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-variant-numeric: tabular-nums;
}

button { font-family: inherit; }
button, [role="button"] { -webkit-tap-highlight-color: transparent; }
button:focus-visible, .inp:focus-visible, select:focus-visible, input:focus-visible {
  outline: 2px solid var(--accent-line);
  outline-offset: 1px;
}

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .12); border-radius: 8px;
  border: 2px solid transparent; background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, .22); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* ============ 顶部功能区 ============ */
/* 顶栏 = 品牌行 + 工具条（两段式）
   工具条按「检查 / 修复 / 页面 / 印前」四组排布，按钮加大到 34px 高，
   窄窗口时整组换行（而不是把文字藏起来），保证每个按钮都看得见、点得到。 */
.app-header {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  padding: 0 12px 8px;
  background: linear-gradient(180deg, #1c2129 0%, #14171c 100%);
  border-bottom: 1px solid var(--bd);
  box-shadow: 0 1px 0 rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .04);
  user-select: none;
  position: relative;
  z-index: 20;
}
.header-top {
  display: flex; align-items: center; gap: 12px;
  height: 50px;
}
.header-spacer { flex: 1 1 auto; min-width: 8px; }

.brand { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.brand-logo {
  width: 32px; height: 32px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(160deg, rgba(91,140,255,.28), rgba(91,140,255,.09));
  color: var(--accent-2);
  border: 1px solid var(--accent-line);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12), 0 2px 10px rgba(91,140,255,.14);
  transition: transform var(--dur) var(--ease);
}
.brand:hover .brand-logo { transform: translateY(-1px); }

/* 顶部「保存」按钮（左上角，主动落盘） */
.save-btn {
  display: inline-flex; align-items: center; gap: 7px;
  height: 32px; padding: 0 15px;
  background: linear-gradient(180deg, #6295ff, #4b7ef0);
  border: 1px solid rgba(255, 255, 255, .16);
  color: #fff; border-radius: 8px; font-size: 12.5px; font-weight: 600;
  cursor: pointer; transition: background var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(91,140,255,.28), inset 0 1px 0 rgba(255,255,255,.20);
  letter-spacing: .2px;
}
.save-btn:hover { background: linear-gradient(180deg, #74a1ff, #5a8af7); box-shadow: 0 4px 16px rgba(91,140,255,.38), inset 0 1px 0 rgba(255,255,255,.24); }
.save-btn:active { transform: translateY(1px); box-shadow: inset 0 1px 3px rgba(0,0,0,.25); }
.save-btn:disabled { opacity: .42; cursor: not-allowed; box-shadow: none; filter: grayscale(.3); }
.save-btn svg { opacity: .95; }
.brand-text { display: flex; flex-direction: column; line-height: 1.22; }
.brand-title { font-size: 14px; font-weight: 600; letter-spacing: .35px; display: inline-flex; align-items: center; gap: 6px; }
/* 版本号小胶囊：装完打开就能确认是哪一版 */
.brand-ver {
  font-style: normal; font-size: 10px; font-weight: 600; letter-spacing: .2px;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid var(--accent-line); border-radius: 20px;
  padding: 1px 6px 1px; line-height: 1.5; white-space: nowrap;
}
.brand-ver:empty { display: none; }
.brand-sub { font-size: 9px; color: var(--tx-3); letter-spacing: 1.5px; text-transform: uppercase; }

/* #headerReserved 现在是工具条容器（见 .toolbar），这里只做兜底 */
.header-reserved { min-width: 0; }
.reserved-tag { display: none; }
.toolbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px;
}
.tb-group {
  display: flex; align-items: center; gap: 1px;
  padding: 3px 4px 3px 2px;
  border-radius: 12px;
  background: rgba(255, 255, 255, .028);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .052);
}
.tb-cap {
  display: flex; align-items: center;
  height: 18px; padding: 0 6px 0 4px; margin-right: 2px;
  border-right: 1px solid var(--bd);
  font-size: 10px; font-weight: 600; letter-spacing: 1.2px;
  color: var(--tx-3);
  white-space: nowrap; flex: 0 0 auto;
}
.hbtn {
  display: inline-flex; align-items: center; gap: 5px;
  height: 34px; padding: 0 7px;
  background: transparent;
  color: var(--tx-2);
  border: 1px solid transparent;
  border-radius: 9px;
  font-size: 12px; font-weight: 500;
  letter-spacing: .1px;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
  white-space: nowrap;
  flex: 0 0 auto;
}
.hbtn:hover { background: var(--accent-soft); border-color: var(--accent-line); color: var(--tx); }
.hbtn:active { transform: translateY(1px); }
.hbtn svg { width: 16px; height: 16px; opacity: .9; flex: 0 0 auto; }

/* 功能按钮：蓝色图标 + 中性文字，悬停点亮并轻微抬升 */
.hbtn.feature svg { color: var(--accent); opacity: .85; transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease); }
.hbtn.feature:hover svg { opacity: 1; transform: translateY(-1px); }

/* 一键逆序：不带弹窗，按钮自己兼职进度提示 */
.hbtn.busy {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--tx);
  cursor: progress;
}
.hbtn.busy svg { animation: busyPulse 1.3s ease-in-out infinite; }
@keyframes busyPulse {
  0%, 100% { opacity: .45; }
  50%      { opacity: 1; }
}

.header-right { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; padding-left: 4px; }
.engine-state {
  font-size: 10.5px; color: var(--tx-3);
  display: inline-flex; align-items: center; gap: 6px;
  white-space: nowrap;
  padding: 3px 9px 3px 8px;
  border: 1px solid var(--bd-soft);
  border-radius: 999px;
  background: rgba(255, 255, 255, .02);
  letter-spacing: .3px;
}
.engine-state::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px rgba(242, 163, 60, .14);
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.engine-state.ok { color: var(--tx-2); }
.engine-state.ok::before { background: var(--green); box-shadow: 0 0 0 3px var(--green-soft); animation: none; }
.engine-state.err { color: var(--red); border-color: rgba(242, 106, 95, .3); }
.engine-state.err::before { background: var(--red); box-shadow: 0 0 0 3px var(--red-soft); animation: none; }

/* ============ 主体 ============ */
.app-body {
  flex: 1;
  display: flex;
  min-height: 0;
}

/* ---------- 左侧 ---------- */
.side-panel {
  /* 左侧文件栏：默认占 26.4%（在原 33% 基础上缩小五分之一，腾出的空间交给中间预览区） */
  flex: 0 0 26.4%;
  min-width: 240px;
  max-width: 46%;
  display: flex;
  flex-direction: column;
  background: var(--bg-panel);
  border-right: 1px solid var(--bd);
  min-height: 0;
}

.panel-head {
  height: 44px; flex: 0 0 44px;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 12px 0 15px;
  border-bottom: 1px solid var(--bd-soft);
}
.panel-title {
  font-size: 12px; font-weight: 600; color: var(--tx-2); letter-spacing: .6px;
  display: inline-flex; align-items: center; gap: 8px;
}
.panel-title::before {
  content: ''; width: 3px; height: 13px; border-radius: 2px;
  background: linear-gradient(180deg, var(--accent), rgba(91,140,255,.35));
}
.panel-head-actions { display: flex; gap: var(--sp-2); }
/* 面板头部小按钮（添加 / 清空）：灰色系
   nowrap + 不收缩：窄面板时原先会把「文件列表」压成一列竖字 */
.panel-head .mini-btn {
  height: var(--h-ctl); padding: 0 var(--sp-3);
  font-size: var(--fs-label); color: var(--tx-2);
  background: var(--bg-elev);
  border: 1px solid var(--bd);
  border-radius: var(--r-ctl); cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
  font-weight: 500;
  white-space: nowrap; flex: 0 0 auto;
}
.panel-head .mini-btn:hover { background: var(--bg-hover); color: var(--tx); border-color: var(--bd-strong); }
.panel-head .mini-btn.ghost:hover { background: var(--red-soft); color: var(--red); border-color: rgba(242,106,95,.36); }
/* 「全选」按钮：已全选时点亮，点一下即取消 */
.panel-head .mini-btn.on {
  background: rgba(91,140,255,.16); color: #cfe0ff;
  border-color: rgba(91,140,255,.5);
}
.panel-head .mini-btn.on:hover { background: rgba(91,140,255,.24); color: #fff; }
.panel-head .mini-btn:disabled { opacity: .45; cursor: default; }
.panel-head .mini-btn:disabled:hover { background: var(--bg-elev); color: var(--tx-2); border-color: var(--bd); }

/* 拖放区 */
.dropzone {
  margin: 12px 12px 10px;
  padding: 15px 12px 13px;
  border: 1px dashed rgba(255, 255, 255, .14);
  border-radius: var(--radius);
  background: linear-gradient(180deg, rgba(255,255,255,.024), rgba(255,255,255,.008));
  text-align: center;
  cursor: pointer;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
  flex: 0 0 auto;
}
.dropzone:hover, .dropzone.hot {
  border-color: var(--accent-line);
  background: linear-gradient(180deg, rgba(91,140,255,.10), rgba(91,140,255,.028));
}
.dz-icon {
  width: 40px; height: 40px; border-radius: 50%;
  margin: 0 auto 9px;
  display: flex; align-items: center; justify-content: center;
  color: var(--tx-3);
  background: rgba(255, 255, 255, .045);
  border: 1px solid var(--bd-soft);
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.dropzone:hover .dz-icon, .dropzone.hot .dz-icon {
  color: var(--accent);
  background: rgba(91,140,255,.14);
  border-color: var(--accent-line);
  transform: translateY(-1px);
}
.dz-main { font-size: 12.5px; color: var(--tx); }
.dz-sub { font-size: 11px; color: var(--tx-3); margin-top: 4px; }

/* 文件列表 —— 列式表
 * 每列宽度用 --c-* 变量控制，表头列之间可拖动调整（宽度记在 localStorage）
 */
:root {
  --c-chk: 22px;
  --c-name: minmax(140px, 1fr); /* 文件名列：单行截断，留白时撑满 */
  --c-text: 80px;
  --c-pages: 44px;
  --c-size: 122px;          /* 页面尺寸列 */
  --c-fsize: 92px;          /* 文件大小列（新增，置于页面尺寸之后） */
  --c-del: 24px;
  --list-gap: 6px;
  --grid-cols: var(--c-chk) var(--c-name) var(--c-text)
               var(--c-pages) var(--c-size) var(--c-fsize) var(--c-del);
}
.list-head {
  display: grid;
  grid-template-columns: var(--grid-cols);
  gap: var(--list-gap);
  align-items: center;
  height: 28px;
  padding: 0 8px;            /* 与 .file-row 左右内边距一致，弹性列宽度才能相同 */
  font-size: 10.5px; color: var(--tx-3);
  letter-spacing: .5px;
  font-weight: 500;
  background: var(--bg-elev);  /* 不透明分隔带，盖住下方滚动的行 */
  border-bottom: 1px solid var(--bd-soft);
  user-select: none;
  position: sticky; top: 0; z-index: 3;  /* 与行同处一个滚动容器：横向一起滚、竖向始终置顶 */
  min-width: 580px;           /* 与 .file-row 完全一致，列几何永远对齐，永不错位 */
}
.list-head .col { position: relative; }
.list-head .col-lb {
  display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.list-head .col-name { padding-left: 23px; }   /* 与行内文件名文字对齐（让开图标） */
.list-head .col-pages { text-align: right; }

/* 列分隔线：画在列间距正中，表头与数据行一致 */
.col-text, .col-pages, .col-size, .col-fsize { position: relative; padding-left: 8px; }
.col-text::before, .col-pages::before, .col-size::before, .col-fsize::before {
  content: ''; position: absolute; left: -3px; top: 4px; bottom: 4px;
  width: 1px; background: rgba(255,255,255,.085); pointer-events: none;
}
.list-head .col-text::before, .list-head .col-pages::before, .list-head .col-size::before, .list-head .col-fsize::before {
  top: 7px; bottom: 7px;
}

/* 表头列宽拖拽手柄 */
.col-rz {
  position: absolute; top: 0; right: -4px;
  width: 9px; height: 100%;
  cursor: col-resize; z-index: 2;
}
.col-rz::after {
  content: ''; position: absolute; top: 5px; bottom: 5px; left: 4px;
  width: 1px; background: transparent; transition: background .14s;
}
.col-rz:hover::after, .col-rz.on::after { background: var(--accent); }

.file-list {
  flex: 1;
  overflow-y: auto;
  overflow-x: auto;
  scrollbar-gutter: stable;
  padding: 0 0 6px;
  min-height: 0;
}
.empty-hint {
  text-align: center; color: var(--tx-3); font-size: 12px;
  padding: 26px 0;
}

.file-row {
  display: grid;
  grid-template-columns: var(--grid-cols);
  gap: var(--list-gap);
  align-items: center;
  padding: 4px 8px;
  min-height: 34px;
  /* 行最小宽度：列宽总和超过左侧面板时，整行可左右滚动查看（见 .file-list overflow-x） */
  min-width: 580px;
  border-bottom: 1px solid var(--bd-soft);
  cursor: pointer;
  transition: background var(--dur) var(--ease);
  position: relative;
}
.file-row:hover { background: rgba(255, 255, 255, .035); }
.file-row.active {
  background: linear-gradient(90deg, rgba(91,140,255,.17), rgba(91,140,255,.05) 55%, transparent);
  box-shadow: inset 3px 0 0 var(--accent);
}
.file-row.active .nm { color: #fff; }
.file-row.error { box-shadow: inset 3px 0 0 var(--red); }

/* 多选勾选框：勾选后行首打勾 + 左侧橙色边条，表示加入批量处理 */
.col-chk { display: flex; align-items: center; justify-content: center; }
.row-chk {
  width: 15px; height: 15px; flex: 0 0 15px;
  border: 1.5px solid var(--bd-strong);
  border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; line-height: 1; color: transparent;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.row-chk:hover { border-color: var(--accent); background: rgba(91,140,255,.10); }
.row-chk.on {
  background: var(--accent); border-color: var(--accent); color: #fff;
  box-shadow: 0 0 0 3px rgba(91,140,255,.14);
}
.file-row.checked { box-shadow: inset 3px 0 0 var(--orange); background: var(--orange-soft); }
.file-row.checked.active { box-shadow: inset 3px 0 0 var(--accent); }

/* 表头全选框：三态（全选 ✓ / 部分 – / 未选） */
.list-head .col-chk { display: flex; align-items: center; justify-content: center; }
.chk-all {
  cursor: pointer;
  border-color: var(--tx-3);
}
.chk-all:hover { border-color: var(--accent); background: var(--bg-hover); }
.chk-all.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.chk-all.part {
  border-color: var(--accent); background: transparent; color: var(--accent);
  font-size: 13px; line-height: .6;
}

.col-name { display: flex; align-items: center; gap: 8px; min-width: 0; }
.row-icon { flex: 0 0 15px; width: 15px; height: 18px; color: #e0605f; opacity: .9; }
.nm-wrap { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; }
/* 文件名单行：超出显示范围截断隐藏（不再换行占两行） */
.nm {
  flex: 1 1 auto; min-width: 0;
  font-size: 12px; color: var(--tx); font-weight: 500;
  line-height: 1.4;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 行内状态标记（已转曲 / 已修改 / 处理中）：随文件名同一行，不挤压文件名 */
.nm-flags { flex: 0 0 auto; display: flex; gap: 4px; align-items: center; }
.nm-sub {
  font-size: 10px; color: var(--tx-3); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: 5px;
}
.flag-out {
  color: var(--green); background: var(--green-soft);
  border: 1px solid rgba(62,207,142,.3);
  border-radius: 3px; padding: 0 5px; font-size: 9.5px;
}
.col-text { min-width: 0; }
.col-pages {
  text-align: right; color: var(--tx-2);
  font-variant-numeric: tabular-nums; font-size: 12px;
}
.col-size {
  font-size: 11px; color: var(--tx-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: 4px;
}
/* 文件大小列：与页面尺寸列同款，超长截断；右对齐更易读数 */
.col-fsize {
  font-size: 11px; color: var(--tx-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: right; justify-content: flex-end;
}
.col-del {
  position: sticky; right: 0; z-index: 2;   /* 永远贴着列表右边框，横向滚动也不被裁掉 */
  width: 22px; height: 22px; border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); border: none; color: var(--tx-3);
  cursor: pointer; font-size: 14px; line-height: 1;
  opacity: .55; transition: opacity var(--dur) var(--ease), background var(--dur) var(--ease), color var(--dur) var(--ease);
  box-shadow: -9px 0 9px -7px var(--bg);   /* 左侧内容滚过时不被串色 */
}
.file-row:hover .col-del { opacity: 1; }
.col-del:hover { background: var(--red-soft); color: var(--red); opacity: 1; }
/* 表头末尾的占位格同样贴右，保证与数据行的 × 始终对齐 */
.list-head .col-del {
  position: sticky; right: 0; z-index: 4;
  background: var(--bg-elev);
  box-shadow: -9px 0 9px -7px var(--bg-elev);
}

/* 状态 pill */
.tag {
  font-size: 10.5px;
  padding: 2px 8px;
  border-radius: 999px;
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid transparent;
  white-space: nowrap;
  max-width: 100%;
  font-weight: 500;
  line-height: 1.5;
}
.tag.t-text-ok    { background: var(--green-soft);  color: var(--green);  border-color: rgba(62,207,142,.26); }
.tag.t-text-curve { background: var(--purple-soft); color: var(--purple); border-color: rgba(172,130,255,.26); }
.tag.t-text-warn  { background: var(--orange-soft); color: var(--orange); border-color: rgba(242,163,60,.26); }
.tag.t-text-none  { background: rgba(255,255,255,.045); color: var(--tx-3); border-color: var(--bd); }
.tag.t-text-load  { background: rgba(255,255,255,.045); color: var(--tx-3); border-color: var(--bd); }
.tag.t-done       { background: var(--green-soft); color: var(--green); border-color: rgba(62,207,142,.26); }
.tag.t-fail       { background: var(--red-soft); color: var(--red); border-color: rgba(242,106,95,.28); }

/* ============ 损坏 PDF「另存即修复」 ============
 * 行内「尝试修复」按钮：与「解析失败」标记同排（在 .nm-flags 里），
 * 所以不占列、不改网格，行宽几列都不会因此错位。 */
.fix-btn {
  flex: 0 0 auto;
  height: 18px;
  padding: 0 7px;
  border-radius: 4px;
  border: 1px solid rgba(242,163,60,.42);
  background: var(--orange-soft);
  color: var(--orange);
  font-size: 10px;
  font-family: inherit;
  line-height: 16px;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.fix-btn:hover { background: rgba(242,163,60,.24); color: #ffd9a0; border-color: rgba(242,163,60,.62); }
.fix-btn:disabled { opacity: .6; cursor: default; }
/* 修复要读整份文件，跑起来时把行内的进程提示转起来 */
.fix-btn:disabled::after { content: ' …'; }

/* 预览上方的提示条（次入口）：能打开但内容明显偏少的那一份 */
.fix-notice {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 10px 6px 12px;
  background: var(--orange-soft);
  border-bottom: 1px solid rgba(242,163,60,.28);
  font-size: 11.5px;
  color: var(--tx);
  line-height: 1.5;
}
.fix-notice.hide { display: none; }
.fix-notice-tx { flex: 1 1 auto; min-width: 0; }
.fix-notice-x {
  flex: 0 0 auto;
  width: 18px; height: 18px;
  border: none; background: transparent;
  color: var(--tx-3); font-size: 14px; line-height: 1;
  cursor: pointer; border-radius: 4px;
}
.fix-notice-x:hover { background: rgba(255,255,255,.08); color: var(--tx); }

/* 修复报告：沿用 ask 的遮罩与盒子，正文放宽、可滚动（报告可能十来行） */
.ask-box.fix-report { width: 560px; }
.fix-report .fix-ttl {
  font-size: 13.5px; font-weight: 600; color: var(--tx);
  margin-bottom: 10px;
}
.fix-report .fix-lines {
  max-height: 48vh;
  overflow: auto;
  font-size: 12px;
  line-height: 1.85;
  color: var(--tx-2);
  padding-right: 4px;
}
.fix-report .fix-lines > div {
  padding-left: 11px;
  text-indent: -11px;
  margin-bottom: 3px;
  word-break: break-word;
}
.fix-report .fix-lines > div.fx-ok { color: var(--green); }
.fix-report .fix-lines > div.fx-bad { color: var(--red); }


/* 多规格展开（跨整行） */
.size-toggle {
  color: var(--accent);
  cursor: pointer; user-select: none;
  flex: 0 0 auto;
}
.size-toggle:hover { text-decoration: underline; }
.row-detail {
  grid-column: 1 / -1;
  display: none;
  flex-wrap: wrap; gap: 4px;
  padding: 2px 0 7px 23px;
}
.row-detail.open { display: flex; }
.size-chip {
  font-size: 10px; color: var(--tx-2);
  background: rgba(255,255,255,.045);
  border: 1px solid var(--bd);
  border-radius: 4px; padding: 1px 6px;
}
.row-progress {
  grid-column: 1 / -1;
  height: 2px; background: rgba(255,255,255,.06);
  border-radius: 2px; overflow: hidden;
}
.row-progress i { display: block; height: 100%; background: var(--accent); width: 0%; transition: width .2s; }

/* 已修改标记 */
.tag.tag-dirty {
  background: var(--orange-soft); color: var(--orange);
  border-color: rgba(242,163,60,.3); font-size: 9.5px; padding: 0 5px;
}
.tag.tag-pending {
  background: var(--accent-soft); color: var(--accent-2);
  border-color: var(--accent-line); font-size: 9.5px; padding: 0 5px;
  animation: pendingPulse 1.4s ease-in-out infinite;
}
@keyframes pendingPulse { 0%,100% { opacity: .55 } 50% { opacity: 1 } }

/* 暂存后的操作条（跨整行） */
.row-actions {
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 5px 2px 7px 2px;
  border-top: 1px dashed var(--bd-soft);
  margin-top: 2px;
}
.act-chips { display: flex; flex-wrap: wrap; gap: 4px; min-width: 0; }
.act-chip {
  font-size: 9.5px; color: var(--tx-2);
  background: rgba(255,255,255,.055);
  border: 1px solid var(--bd);
  border-radius: 5px; padding: 1px 7px;
  white-space: nowrap;
}
.act-btns { display: flex; gap: 6px; flex: 0 0 auto; }
.act-btn {
  font-size: 11px; padding: 3px 11px;
  border-radius: 6px; cursor: pointer;
  background: linear-gradient(180deg, #6295ff, #4b7ef0); color: #fff;
  border: 1px solid rgba(255,255,255,.16);
  transition: filter var(--dur) var(--ease), transform var(--dur) var(--ease);
  box-shadow: 0 1px 6px rgba(91,140,255,.24);
}
.act-btn:hover { filter: brightness(1.08); }
.act-btn:active { transform: translateY(1px); }
.act-btn.ghost {
  background: transparent; color: var(--tx-2); border: 1px solid var(--bd);
  box-shadow: none;
}
.act-btn.ghost:hover { color: var(--red); border-color: var(--red); }
/* 返回上一步：弱化的强调色，避免误点「导出」时混淆 */
.act-btn.undo {
  background: transparent; color: var(--orange);
  border: 1px solid rgba(242,163,60,.45);
  box-shadow: none;
}
.act-btn.undo:hover { background: rgba(242,163,60,.14); border-color: var(--orange); }
/* 轨迹里最后一步 = 下一次「返回上一步」会撤销的动作 */
.act-chip-last {
  color: var(--orange);
  border-color: rgba(242,163,60,.45);
  background: rgba(242,163,60,.12);
}

/* 列表顶部：暂存操作工具条 —— 无暂存时整体隐藏，出现时带轻微滑入 */
.list-tools {
  display: none;
  align-items: center; gap: 8px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--bd-soft);
  background: linear-gradient(90deg, rgba(242,163,60,.09), rgba(242,163,60,.03));
}
.list-tools.has-staged { display: flex; animation: toolsIn .2s var(--ease); }
@keyframes toolsIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.lt-label { font-size: 11px; color: var(--tx-2); }
/* 暂存条内的批量按钮（返回上一步 / 导出全部 / 全部还原）：蓝色系 */
.list-tools .mini-btn {
  height: 26px; padding: 0 12px;
  font-size: 11px; font-weight: 600;
  border-radius: 7px; cursor: pointer;
  background: linear-gradient(180deg, #6295ff, #4b7ef0); color: #fff;
  border: 1px solid rgba(255,255,255,.14);
  transition: filter var(--dur) var(--ease);
}
.list-tools .mini-btn:hover { filter: brightness(1.08); }
.list-tools .mini-btn:disabled { opacity: .4; cursor: not-allowed; filter: none; }
.list-tools .mini-btn.ghost { background: transparent; color: var(--tx-2); border-color: var(--bd); font-weight: 500; }
.list-tools .mini-btn.ghost:hover:not(:disabled) { color: var(--orange); border-color: var(--orange); }
.list-tools .mini-btn.undo { background: transparent; color: var(--orange); border-color: rgba(242,163,60,.45); font-weight: 500; }
.list-tools .mini-btn.undo:hover:not(:disabled) { background: rgba(242,163,60,.14); border-color: var(--orange); }
.list-tools .mini-btn.undo:disabled { opacity: .35; }

/* 进度条 */
.fc-progress {
  height: 2px; background: rgba(255,255,255,.06);
  border-radius: 2px; margin-top: 8px; overflow: hidden;
}
.fc-progress i { display: block; height: 100%; background: var(--accent); width: 0%; transition: width .2s; }

.side-footer {
  flex: 0 0 32px; height: 32px;
  display: flex; align-items: center; gap: 6px;
  padding: 0 14px;
  border-top: 1px solid var(--bd-soft);
  font-size: 11px; color: var(--tx-3);
  background: rgba(0, 0, 0, .22);
}
.dot-sep { opacity: .5; }
.foot-spacer { flex: 1; }

/* 分隔条 */
.splitter {
  flex: 0 0 4px; width: 4px;
  cursor: col-resize;
  background: transparent;
  position: relative;
  z-index: 5;
}
.splitter::after {
  content: ''; position: absolute; inset: 0;
  background: transparent; transition: background var(--dur) var(--ease);
}
.splitter:hover::after, .splitter.dragging::after { background: var(--accent-line); }

/* ---------- 右侧预览 ---------- */
.preview-panel {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}

.preview-bar {
  flex: 0 0 44px; height: 44px;
  display: flex; align-items: center;
  padding: 0 12px;
  border-bottom: 1px solid var(--bd);
  background: var(--bg-panel);
  gap: 10px;
}
.pb-left, .pb-right { display: flex; align-items: center; gap: 5px; }
.pb-center {
  flex: 1; min-width: 0; text-align: center;
  font-size: 12px; color: var(--tx-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 0 8px;
}
.pb-btn {
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elev);
  border: 1px solid var(--bd);
  border-radius: 8px;
  color: var(--tx-2); cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.pb-btn:hover:not(:disabled) { background: var(--bg-hover); color: var(--tx); border-color: var(--bd-strong); }
.pb-btn:disabled { opacity: .34; cursor: not-allowed; }
.pb-btn.primary { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }

.page-input-wrap {
  display: inline-flex; align-items: center; gap: 4px;
  height: 30px; padding: 0 9px;
  background: var(--bg-elev);
  border: 1px solid var(--bd);
  border-radius: 8px;
  transition: border-color var(--dur) var(--ease);
}
.page-input-wrap:focus-within { border-color: var(--accent-line); }
.page-input {
  width: 34px; border: none; background: transparent;
  color: var(--tx); font-size: 12px; text-align: right;
  outline: none; font-family: inherit;
  font-variant-numeric: tabular-nums;
}
.page-total { font-size: 11px; color: var(--tx-3); }

.seg {
  display: inline-flex; background: var(--bg-elev);
  border: 1px solid var(--bd); border-radius: 8px; overflow: hidden;
}
.seg-btn {
  height: 30px; padding: 0 12px;
  background: transparent; border: none;
  color: var(--tx-3); font-size: 12px; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.seg-btn + .seg-btn { border-left: 1px solid var(--bd); }
.seg-btn:hover { color: var(--tx); background: var(--bg-hover); }
.seg-btn.active { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
.zoom-label {
  font-size: 11.5px; color: var(--tx-2);
  min-width: 44px; text-align: center;
  font-variant-numeric: tabular-nums;
}

/* 画布舞台：柔和暗场 + 一点点冷色光晕，让纸张「浮」起来。
   以前是 22px 棋盘格，2x 屏上会抢眼、也像透明底，改成渐变后更干净。 */
.preview-viewport {
  flex: 1;
  min-height: 0;
  position: relative;
  background:
    radial-gradient(120% 90% at 50% -10%, rgba(91,140,255,.055) 0%, rgba(91,140,255,0) 60%),
    radial-gradient(90% 70% at 50% 110%, rgba(0,0,0,.42) 0%, rgba(0,0,0,0) 62%),
    var(--bg-stage);
  overflow: auto;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.page-stage {
  /* 上下留 34px（= app.js 的 STAGE_PAD_Y 68 / 2）：覆盖层的提示条骑在页面上沿、
     图例骑在页面下沿，都要有地方待，不能压画面也不能被视口裁掉。
     改这里记得同步 app.js 的 STAGE_PAD_X / STAGE_PAD_Y。 */
  padding: 34px 22px;
  flex: 0 0 auto;
}
.preview-viewport.panning { cursor: grabbing; }
#pdfCanvas {
  display: block;
  background: #fff;
  box-shadow:
    0 1px 2px rgba(0,0,0,.4),
    0 6px 18px rgba(0,0,0,.42),
    0 20px 48px rgba(0,0,0,.38);
  border-radius: 3px;
}
.preview-viewport.no-file #pdfCanvas { display: none; }

.preview-empty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 6px; color: var(--tx-3); text-align: center;
  pointer-events: none;
}
.preview-empty p { margin: 0; font-size: 13px; }
.preview-empty svg { opacity: .45; }
.preview-empty .hint-sm { font-size: 11.5px; color: var(--tx-3); opacity: .8; }
.preview-panel.has-file .preview-empty { display: none; }

.render-loading {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%);
  display: none; align-items: center; gap: 7px;
  background: rgba(28,32,38,.95);
  border: 1px solid var(--bd);
  border-radius: 20px; padding: 5px 13px;
  font-size: 11.5px; color: var(--tx-2);
  box-shadow: 0 4px 14px rgba(0,0,0,.4);
}
.render-loading.on { display: flex; }
.spinner {
  width: 11px; height: 11px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.16);
  border-top-color: var(--accent);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* 预览状态栏 */
.preview-status {
  flex: 0 0 30px; height: 30px;
  display: flex; align-items: center; gap: 10px;
  padding: 0 14px;
  border-top: 1px solid var(--bd);
  background: var(--bg-panel);
  font-size: 11px; color: var(--tx-3);
  user-select: none;
}
.st-item { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 30vw; }
.st-sep { width: 1px; height: 10px; background: var(--bd); flex: 0 0 1px; }
.st-mode {
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent-line);
  border-radius: 999px; padding: 1px 9px; font-size: 10.5px;
  font-weight: 500;
}
.st-mode.full { background: var(--purple-soft); color: var(--purple); border-color: rgba(172,130,255,.32); }

/* ============ 弹窗 ============ */
/* ------------------------------------------------------------------
 * 设计令牌（v1.0.15 收口）
 * 规则出处见 docs/UI设计规范.md，改动任何数值请同步那份文档。
 * 只允许用这里的档位：间距 4/6/9/12/16/20，控件高 24/26/28，
 * 字号 10.5/11/11.5/12/13.5，圆角 7/8/12，弹窗宽 S/M/L。
 * ------------------------------------------------------------------ */
:root {
  /* —— 间距刻度 —— */
  --sp-1: 4px;    /* 标签与控件、图标与文字 */
  --sp-2: 6px;    /* 同一行相邻控件之间 */
  --sp-3: 9px;    /* 表单行与行之间 */
  --sp-4: 12px;   /* 面板块内边距 */
  --sp-5: 16px;   /* 区块与区块之间 */
  --sp-6: 20px;   /* 弹窗内容左右留白上限 */

  /* —— 控件高度（同一行必须同档） —— */
  --h-ctl-xs: 24px;   /* 紧凑：只给宽度受限的窄数字框（.cr-num） */
  --h-ctl: 26px;      /* 标准：输入框 / 下拉 / 分段按钮 / 小按钮 */
  --h-ctl-lg: 28px;   /* 强调：弹窗内独立工具排的大按钮 */
  --h-btn: 32px;      /* 动作按钮：弹窗底部与面板主操作 */

  /* —— 字号 —— */
  --fs-note: 10.5px;  /* 说明文字（参数级补充） */
  --fs-label: 11px;   /* 字段标签 / 单位 */
  --fs-body: 11.5px;  /* 正文：控件文字、选项文字 */
  --fs-strong: 12px;  /* 强调：卡片标题、面板条目、状态栏 */
  --fs-title: 13.5px; /* 弹窗标题 */

  /* —— 圆角 —— */
  --r-ctl: 7px;     /* 控件 */
  --r-block: 8px;   /* 面板块 / 选项卡 */
  --r-modal: 12px;  /* 弹窗 */

  /* —— 弹窗宽度三档（v1.0.94 起整体压到原来的 3/4，客户 2026-11-11 需求） ——
     只改这三个变量：各处 `#xxxModal .modal-box { width: var(--mw-x); }`
     引用的都是它们，自动跟随，不需要逐条改（见下方「弹窗宽度分档」注释）。 */
  --mw-s: 360px;   /* S 纯确认 / 单参数（= 原 480 × 3/4） */
  --mw-m: 420px;   /* M 常规动作（多列参数、报告、列表）—— 默认档（= 原 560 × 3/4） */
  --mw-l: 480px;   /* L 带预览 / 画布 / 双栏定位（= 原 640 × 3/4） */
  --mw-xs: 360px;  /* XS 仅「画框模式」：宽度受「不遮挡画布」约束，不用于普通弹窗；不参与 3/4 压缩 */
  --mw-xxs: 320px; /* XXS 仅「保存进度窗」：一条进度 + 一个「确定」，比 S 再窄一档 */
  --mw: var(--mw-m);

  --mhead: 40px;        /* 标题栏高 */
  --mfoot: 46px;        /* 底部操作栏高 */
  --mpad-x: var(--sp-4);
  --mpad-y: var(--sp-4);
  --m-row-gap: var(--sp-3);   /* 表单行间距 */
}
.modal { position: fixed; inset: 0; z-index: 120; display: none; }
.modal.on { display: block; }
/* 弹窗默认贴屏幕最左侧（见 .modal-box margin-left），右侧预览区不再被遮住，
   所以遮罩只做极轻压暗、不做模糊：挡不住视线，仍可点空白处关闭 */
.modal-mask {
  position: absolute; inset: 0;
  background: rgba(8,10,13,.20); backdrop-filter: none;
  transition: background .18s;
}
/* 窗口被拖动后：遮罩进一步变淡，方便对着文档看 / 操作 */
.modal.moved .modal-mask { background: rgba(8,10,13,.04); }
/* 画框模式：整个弹窗根节点让出鼠标，鼠标可以直接落到下面的页面上画框；
   只有对话框本身（.modal-box）仍然可点，便于继续调参数 / 开始填充 / 关闭 */
.modal.pass-through { pointer-events: none; }
.modal.pass-through .modal-mask {
  background: rgba(8,10,13,.04);
  backdrop-filter: none;
  pointer-events: none;
}
.modal.pass-through .modal-box { pointer-events: auto; }

/* 画框模式下弹窗收成紧凑形态：只留画框 / 颜色 / 填充相关，尽量少挡页面 */
.modal.draw-mode .modal-box { width: var(--mw-xs); }
.modal.draw-mode .modal-body { padding: 10px 12px 11px; }
.modal.draw-mode .modal-body .row-scope,
.modal.draw-mode .modal-body .row-style,
.modal.draw-mode .modal-body .row-width,
.modal.draw-mode .modal-body .row-pages,
.modal.draw-mode .draw-tip { display: none; }
.modal.draw-mode .form-row { margin-bottom: 9px; }
.modal.draw-mode .modal-foot { flex: 0 0 44px; height: 44px; }
.modal-box {
  position: relative;
  width: var(--mw); max-width: calc(100vw - 24px);
  /* 顶部让出顶栏高度，避免弹窗标题压在工具条上（--mtop 由 app.js 按实际头高写入） */
  max-height: calc(100vh - var(--mtop, 104px) - 20px);
  /* 一律贴屏幕最左侧：右侧预览区完整露出；auto 右边距吃掉剩余空间 */
  margin: var(--mtop, 104px) auto 20px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--bd-strong);
  border-radius: var(--r-modal);
  box-shadow: var(--sh-pop);
  display: flex; flex-direction: column;
  overflow: hidden;
  transform: translate(var(--mx, 0px), var(--my, 0px));
  animation: pop .17s var(--ease);
}
@keyframes pop {
  from { opacity: 0; transform: translate(var(--mx, 0px), calc(var(--my, 0px) - 8px)) scale(.995); }
  to   { opacity: 1; transform: translate(var(--mx, 0px), var(--my, 0px)) scale(1); }
}
.modal-box.dragging {
  animation: none;
  box-shadow: 0 28px 70px rgba(0,0,0,.62), 0 0 0 1px var(--accent-line);
}
body.modal-dragging { user-select: none; cursor: move; }
.modal-head {
  flex: 0 0 var(--mhead); height: var(--mhead);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 7px 0 13px;
  background: linear-gradient(180deg, rgba(255,255,255,.045), rgba(255,255,255,.008));
  border-bottom: 1px solid var(--bd-soft);
  cursor: move; user-select: none; touch-action: none;
}
.modal-title {
  font-size: var(--fs-title); font-weight: 600; letter-spacing: .2px;
  display: inline-flex; align-items: center; gap: var(--sp-2);
}
.modal-title::before {
  content: ''; width: 3px; height: 14px; border-radius: 2px;
  background: linear-gradient(180deg, var(--accent), rgba(91,140,255,.35));
}
.modal-x {
  width: 26px; height: 26px; border: none; border-radius: var(--r-ctl);
  background: transparent; color: var(--tx-3);
  font-size: 16px; line-height: 1; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.modal-x:hover { background: var(--bg-hover); color: var(--tx); }
.modal-body { flex: 1; overflow-y: auto; padding: var(--mpad-y) var(--mpad-x); }
.modal-body::-webkit-scrollbar { width: 9px; }
.modal-body::-webkit-scrollbar-track { background: transparent; }
.modal-body::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,.13); border-radius: 5px;
  border: 2px solid transparent; background-clip: content-box;
}
.modal-body::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.22); background-clip: content-box; }
/* 底部操作栏：顺序固定 [关闭][主操作]，主操作永远最右；左侧可挂 run-progress / 提示 */
.modal-foot {
  flex: 0 0 var(--mfoot); height: var(--mfoot);
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-2);
  padding: 0 var(--mpad-x);
  border-top: 1px solid var(--bd-soft);
  background: linear-gradient(180deg, rgba(0,0,0,.10), rgba(0,0,0,.22));
}
/* 工具类按钮（如「导出报告」）允许挂最左，但一个弹窗仍只许有 1 个 primary */
.modal-foot .btn.tool { margin-right: var(--sp-1); }
/* 拼版底部轻量提示：只说明将处理的文件数量与顺序，不展示任何单文件参数 */
.impose-foot-hint {
  margin-right: auto; max-width: 56%;
  font-size: var(--fs-label); color: var(--tx-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ==================================================================
 * 弹窗宽度分档（S / M / L 三档 + 两个专用窄档 —— 见 docs/UI设计规范.md）
 *   S  360：一列参数 + 底部按钮，无预览、无并排多列     （v1.0.94 前是 480）
 *   M  420：常规动作，多列参数 / 报告 / 结果列表（默认档，无需写规则）（前 560）
 *   L  480：带预览、画布、双栏对照                      （前 640）
 *   XS 360：只给「画框模式」用，受「不遮挡画布」约束，不参与 3/4 压缩
 *   XXS 320：只给「保存进度窗」用
 * 禁止再出现 440 / 540 / 548 / 612 / 646 这类一次性数字。
 * 下面这些覆盖规则一律引用变量 —— 调宽度只改 :root 里那三个数即可。
 * ================================================================== */
#outlineModal .modal-box,
#grayModal .modal-box,
#saveModal .modal-box { width: var(--mw-s); }

/* 保存进度窗：比 S 再窄一档，只有一条进度 + 一个按钮。
   z-index 抬到 130：它要盖在 #saveModal（120）上面，成为「保存中」唯一可操作的窗口。
   ★ 位置居中、不贴左上：这个窗是叠在「保存处理结果」弹窗上面的 ——
     两者都贴左上会互相盖住一半，只看得到一团。居中后就是标准的「小进度窗浮在上面」。
   （不用 transform 居中：.modal-box 的 pop 动画和拖动都吃 transform，会打架。） */
#saveProgressModal { z-index: 130; }
#saveProgressModal.on { display: flex; align-items: center; justify-content: center; }
#saveProgressModal .modal-box {
  width: var(--mw-xxs);
  margin: 0;
  max-height: calc(100vh - 80px);
}
/* 遮罩比常规弹窗略深：保存中桌面暂时不该被继续操作 */
#saveProgressModal .modal-mask { background: rgba(8,10,13,.34); }

#pagelocateModal .modal-box,   /* 九点定位 + 四边距离 + 预览，双栏 */
#pagesplitModal .modal-box,    /* 行列 / 尺寸 / 骑马钉三组参数 + 预览 */
#coverModal .modal-box,        /* 封面预览 + 鼠标取色舞台 */
#enhanceModal .modal-box,      /* 原图 / 处理后双预览 */
#delBorderModal .modal-box { width: var(--mw-l); }   /* 原样 / 删线后双预览 */

.form-row { margin-bottom: var(--m-row-gap); }
.form-row:last-child { margin-bottom: 0; }
.form-label { font-size: var(--fs-label); color: var(--tx-3); margin-bottom: var(--sp-1); letter-spacing: .2px; }
/* 参数级说明：全项目唯一允许的说明文字样式（10.5px / tx-3 / 上距 4px） */
.form-note { font-size: var(--fs-note); color: var(--tx-3); margin-top: var(--sp-1); line-height: 1.55; }
/* 参数级报错（例：走纸尺寸"没有合适纸张"）：红字，和普通说明同一行高 */
.form-note.err { color: var(--red); }
/* 空的时候不占位：拼版「大版装不下」的红字提示只在真放不下时出现 */
.form-note.err:empty { display: none; }
.form-note[hidden] { display: none; }
.form-note code { color: var(--tx-2); background: rgba(255,255,255,.05); padding: 1px 5px; border-radius: 3px; }

/* 转专色：印刷通道（勾选 + 浓度）。K 恒勾且禁用，M/C 各带一个 0~100 的浓度框 */
.ink-list { display: flex; flex-direction: column; gap: 6px; margin-top: 2px; }
.ink-row { display: flex; align-items: center; gap: 8px; }
.ink-check { display: flex; align-items: center; gap: 6px; min-width: 92px; cursor: pointer; font-size: var(--fs-body); color: var(--tx); }
.ink-check input[type=checkbox] { width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: #4a7dff; }
.ink-check input[type=checkbox]:disabled { cursor: not-allowed; }
.ink-den { width: 64px; height: 26px; padding: 0 6px; text-align: right; font-variant-numeric: tabular-nums; }
/* 数字框去掉上下箭头：浓度是整十整百地填，箭头反而容易误触 */
.ink-den::-webkit-outer-spin-button,
.ink-den::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ink-den { appearance: textfield; -moz-appearance: textfield; }
.ink-unit { font-size: var(--fs-note); color: var(--tx-3); min-width: 10px; }
.ink-tip { font-size: var(--fs-note); color: var(--tx-3); }
.ink-row.off .ink-check { color: var(--tx-3); }
.ink-row.off .ink-den { opacity: .45; }

/* 双面堆叠：页码排布预览 */
.stack-prev { display: flex; flex-direction: column; gap: 7px; }
.sp-ranges { font-size: 11px; color: var(--tx-2); display: flex; flex-wrap: wrap; gap: 4px 10px; }
.sp-range { background: rgba(91,140,255,.1); border: 1px solid rgba(91,140,255,.25); border-radius: 4px; padding: 1px 7px; }
.sp-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.sp-sheet { border: 1px solid var(--bd); border-radius: 7px; padding: 5px 7px 6px; background: rgba(255,255,255,.02); }
.sp-head { font-size: 10px; color: var(--tx-3); margin-bottom: 4px; display: flex; justify-content: space-between; }
.sp-head b { color: var(--tx-2); font-weight: 600; }
.sp-grid { display: grid; gap: 3px; }
.sp-cell {
  font-size: 10px; text-align: center; padding: 2px 0;
  border-radius: 3px; background: rgba(91,140,255,.13); color: var(--tx-2);
  font-variant-numeric: tabular-nums;
}
.sp-cell.blank { background: rgba(255,255,255,.035); color: var(--tx-3); }
.sp-more { font-size: 10px; color: var(--tx-3); }

/* 拼版后加出血：总开关关掉时，下面的明细整块变灰不可点（与「拆分页面」同一套联动观感） */
.im-bleed-body {
  padding: 8px 10px 2px;
  border: 1px solid var(--bd-soft);
  border-radius: 9px;
  background: rgba(255, 255, 255, .022);
  min-width: 0;
  transition: opacity var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.im-bleed-body > .form-row:last-child { margin-bottom: 0; }
.im-bleed-body.off { opacity: .42; }
.im-bleed-body.off,
.im-bleed-body.off .sf-btn,
.im-bleed-body.off input { pointer-events: none; cursor: not-allowed; }

/* 分段选择：统一成独立胶囊，文字长时自动换行，不会被压扁 */
.seg-form {
  display: flex; flex-wrap: wrap; gap: var(--sp-1);
  border: none; background: transparent;
}
.seg-form .sf-btn {
  flex: 0 0 auto; white-space: nowrap;
  border: 1px solid var(--bd); border-radius: var(--r-ctl);
  background: rgba(255, 255, 255, .055);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.seg-form .sf-btn.active {
  border-color: var(--accent-line);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 500;
}
.seg-form .sf-btn:hover:not(:disabled):not(.active) { border-color: var(--bd-strong); }
/* 拼版方式：等宽铺满一行 */
#imModeSeg { flex-wrap: wrap; gap: var(--sp-1); border: none; overflow: visible; width: 100%; }
#imModeSeg .sf-btn {
  flex: 1 1 auto; white-space: nowrap; padding: 0 10px;
  border: 1px solid var(--bd); border-radius: var(--r-ctl); background: var(--bg-elev);
}
#imModeSeg .sf-btn + .sf-btn { border-left: 1px solid var(--bd); }
/* 选中态必须留 accent 描边（原先写成 transparent，跟其他分段选择不一致） */
#imModeSeg .sf-btn.active { border-color: var(--accent-line); }
.sf-btn {
  height: var(--h-ctl); padding: 0 var(--sp-3);
  background: var(--bg-elev); border: none;
  color: var(--tx-2); font-size: var(--fs-body); cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.sf-btn + .sf-btn { border-left: 1px solid var(--bd); }
.sf-btn:hover:not(:disabled):not(.active) { color: var(--tx); background: var(--bg-hover); }
/* 选中态统一：accent 描边 + accent 底（见规范「选中态」） */
.sf-btn.active { background: var(--accent-soft); color: var(--accent); font-weight: 500; box-shadow: inset 0 0 0 1px var(--accent-line); }

.opt {
  display: grid; grid-template-columns: 15px 1fr; gap: 3px var(--sp-2);
  padding: var(--sp-2) 10px; margin-bottom: var(--sp-1);
  border: 1px solid var(--bd); border-radius: var(--r-block);
  background: var(--bg-elev); cursor: pointer;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.opt:hover { border-color: var(--bd-strong); background: var(--bg-hover); }
/* 选中态（全项目统一）：accent 描边 + 8%~12% accent 底 */
.opt:has(input:checked) {
  border-color: var(--accent-line);
  background: linear-gradient(180deg, rgba(91,140,255,.11), rgba(91,140,255,.04));
}
/* 自绘选择控件：比原生控件更贴合暗色主题 */
.opt input[type=checkbox], .opt input[type=radio] {
  appearance: none; -webkit-appearance: none;
  width: 14px; height: 14px; margin: 2px 0 0;
  border: 1.5px solid var(--bd-strong);
  border-radius: 4px;
  background: rgba(255, 255, 255, .05);
  position: relative; cursor: pointer; flex: 0 0 auto;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.opt input[type=radio] { border-radius: 50%; }
.opt input[type=checkbox]:hover, .opt input[type=radio]:hover { border-color: var(--accent); }
.opt input[type=checkbox]:checked, .opt input[type=radio]:checked {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(91, 140, 255, .13);
}
.opt input[type=checkbox]:checked::after {
  content: ''; position: absolute; left: 4px; top: 1px;
  width: 3px; height: 7px;
  border: solid #fff; border-width: 0 1.6px 1.6px 0;
  transform: rotate(42deg);
}
.opt input[type=radio]:checked::after {
  content: ''; position: absolute; inset: 3.5px;
  border-radius: 50%; background: #fff;
}
.opt-t { font-size: var(--fs-strong); color: var(--tx); }
.opt-t em {
  font-style: normal; font-size: 10px; margin-left: var(--sp-2);
  color: var(--green); background: var(--green-soft);
  border: 1px solid rgba(62,207,142,.3); border-radius: 999px; padding: 0 6px;
}
.opt-d { grid-column: 2; font-size: var(--fs-label); color: var(--tx-3); }

/* 保存弹窗：源文件 / 输出文件名展示 */
.save-opt { padding: 9px 11px; }
.save-src, .save-out {
  font-size: 13px; font-weight: 600; color: var(--tx);
  padding: 8px 11px; border-radius: 7px;
  background: rgba(255,255,255,.05); border: 1px solid var(--bd);
  word-break: break-all;
}
.save-out { color: var(--accent-2); border-color: var(--accent-line); background: rgba(91,140,255,.10); }
.save-src + .form-note, .save-out + .form-note { margin-top: 6px; }

/* 紧凑多选（角线开关等） */
.opt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: var(--sp-2); margin-bottom: var(--sp-3); }
.opt.sm { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); margin-bottom: 0; }
.opt.sm .opt-t { font-size: var(--fs-body); }
.opt.sm input { margin: 0; }
.btn.running {
  background: linear-gradient(180deg, #f5ac4c, #e5932d);
  border-color: rgba(255,255,255,.18); color: #21170a; font-weight: 600;
  animation: runningPulse 1.6s ease-in-out infinite;
}
@keyframes runningPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242,163,60,.34); }
  50% { box-shadow: 0 0 0 4px rgba(242,163,60,.10); }
}

.env-state {
  font-size: 11.5px; padding: 8px 10px; border-radius: 7px;
  background: rgba(255,255,255,.04); border: 1px solid var(--bd);
  color: var(--tx-2); margin-bottom: 12px;
  display: flex; align-items: center; gap: 7px;
}
.env-state.ok { border-color: rgba(62,207,142,.3); background: var(--green-soft); color: var(--green); }
.env-state.warn { border-color: rgba(242,163,60,.3); background: var(--orange-soft); color: var(--orange); }
/* 保存成功提示是单行区：路径过长自动省略，尾部「仍可返回上一步」永不换行。
   只作用于 #saveState，其它 .env-state（环境检测那几行可以多行）不受影响。 */
#saveState .es-main { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 尾部与主文案之间要留一口气：主文案是省略号截断的路径，
   「· 仍可返回上一步」直接贴上去会读成「xxx.pdf· 仍可…」。
   这里用 margin 而不是在文案里塞空格——textContent 的前导空格
   会被省略号布局吃掉，margin 稳定。4px 取间距刻度 --sp-1。 */
#saveState .es-keep { flex: 0 0 auto; white-space: nowrap; margin-left: var(--sp-1, 4px); }

/* ---------- 保存进度窗（v1.0.94） ----------
 * 客户 2026-11-11：「执行保存类型的动作时跳出一个小的弹窗让我直观地看保存进度……
 * 全部保存完点击确定按钮关闭弹窗。」
 * 只在 .svprog-* 里布置，一律复用 .modal / .modal-box / .modal-foot 体系，
 * 不新写遮罩与开合动画。
 * 条形配色只有三种完成态：st-ok 绿 / st-warn 黄 / st-fail 红。
 * ------------------------------------------------------------------ */
.svprog-meter { display: flex; align-items: center; gap: var(--sp-2); }
.svprog-bar {
  flex: 1 1 auto; min-width: 0; height: 6px;
  background: rgba(255,255,255,.08); border-radius: 3px; overflow: hidden;
}
.svprog-bar i {
  display: block; height: 100%; width: 0; border-radius: 3px;
  background: linear-gradient(90deg, #4b7ef0, #7ba7ff);
  transition: width .22s var(--ease), background .22s var(--ease);
}
/* 计数与条形同源同值（都是 n / N），右对齐、定宽数字不抖 */
.svprog-num {
  flex: 0 0 auto; font-size: var(--fs-label); color: var(--tx-2);
  font-variant-numeric: tabular-nums;
}
.svprog-tx { margin-top: var(--sp-3); font-size: var(--fs-body); color: var(--tx-2); }
/* 当前文件名 / 完成后的落点：单行省略号，title 里才是全名 —— 印前文件名经常几十个字符 */
.svprog-file {
  margin-top: 3px; font-size: var(--fs-body); color: var(--tx);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.svprog-file:empty { display: none; }
/* 失败原因允许换行（错误信息没法截断，截了就不知道为什么失败） */
.svprog-err {
  margin-top: var(--sp-2); font-size: var(--fs-label); line-height: 1.5;
  color: var(--orange); word-break: break-all;
}
#saveProgressModal.st-fail .svprog-err { color: var(--red); }
/* 三种完成态：条形满格改色，文字跟着改色 */
#saveProgressModal.st-ok .svprog-bar i { background: linear-gradient(90deg, #2fa36b, #3ecf8e); }
#saveProgressModal.st-warn .svprog-bar i { background: linear-gradient(90deg, #d98a1f, #f2a33c); }
#saveProgressModal.st-fail .svprog-bar i { background: linear-gradient(90deg, #c0392b, #f26a5f); }
#saveProgressModal.st-ok .svprog-tx { color: var(--green); font-weight: 600; }
#saveProgressModal.st-warn .svprog-tx { color: var(--orange); font-weight: 600; }
#saveProgressModal.st-fail .svprog-tx { color: var(--red); font-weight: 600; }
/* 保存中「确定」禁用即可见禁用态：客户要求中途不许关窗 */
#saveProgressModal .btn.primary:disabled { opacity: .45; cursor: default; }

.result-list { display: flex; flex-direction: column; gap: 6px; }
.res-item {
  display: flex; align-items: center; gap: 8px;
  font-size: 11.5px; padding: 7px 10px;
  background: rgba(255,255,255,.04);
  border: 1px solid var(--bd); border-radius: 7px;
  border-left-width: 2px;
}
.res-item .rn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.res-item .rm { color: var(--tx-3); }
.res-item.ok { border-color: rgba(62,207,142,.26); border-left-color: var(--green); }
.res-item.fail { border-color: rgba(242,106,95,.28); border-left-color: var(--red); }

/* 失败项要「一眼能挑出来」（配合汇总条上的「只看失败」）：
   底色微红 + 文件名标红加粗 + 行首一个 ✕。只动样式，不动行里的任何文字。 */
.res-item.fail { background: rgba(242,106,95,.075); }
.res-item.fail .rn { color: var(--red); font-weight: 600; }
.res-item.fail::before { content: '✕'; flex: 0 0 auto; font-size: 10px; color: var(--red); opacity: .95; }
@keyframes rs-hit {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242,106,95,0); }
  40% { box-shadow: 0 0 0 2px rgba(242,106,95,.6); }
}
.res-item.rs-hit { animation: rs-hit .6s ease 2; }

/* ---------- 结果汇总条 ----------
   由 common.js 自动挂在 .result-list 前面的兄弟位置（模块零改动）。
   行数 < 2 且没有失败时 inline display:none，单件处理与改动前完全一样。
   sticky 是为了长列表滚到一半时统计还看得见（.modal-body 是滚动容器）。 */
.res-sum {
  position: sticky; top: 0; z-index: 3;
  display: flex; flex-direction: column; gap: 4px;
  padding: 7px 10px; margin-bottom: 6px;
  background: var(--bg-panel);
  border: 1px solid var(--bd); border-radius: 7px;
  font-size: 11.5px; line-height: 1.5;
}
.res-sum .rs-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.res-sum .rs-cnt { color: var(--tx-2); }
.res-sum .rs-ok { color: var(--green); }
.res-sum .rs-skip { color: var(--tx-3); }
.res-sum .rs-fail {
  margin: 0; padding: 0 4px; border: 0; border-radius: 4px;
  background: none; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--red);
  text-decoration: underline; text-underline-offset: 2px;
}
.res-sum .rs-fail:hover { background: var(--red-soft); }
.res-sum .rs-only {
  margin-left: auto; height: 22px; padding: 0 8px; cursor: pointer;
  font: inherit; color: var(--tx-2);
  background: var(--bg-elev); border: 1px solid var(--bd); border-radius: 5px;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.res-sum .rs-only:hover { color: var(--tx); border-color: var(--bd-strong); }
.res-sum .rs-only.on { color: var(--red); border-color: var(--red); background: var(--red-soft); }
.res-sum .rs-concl { color: var(--tx-3); }
.res-sum .rs-concl.ok { color: var(--green); }
.res-sum .rs-concl.fail { color: var(--red); }
/* 「只看失败」：纯显隐，行始终在 DOM 里（不重跑、不丢数据）。
   info 行（makeResultList.info 的说明/统计行）一起收起，画面才干净。 */
.result-list.sb-only-fail .res-item[data-sb="ok"],
.result-list.sb-only-fail .res-item[data-sb="info"] { display: none; }

.run-progress { flex: 1; height: 5px; background: rgba(255,255,255,.07); border-radius: 3px; overflow: hidden; margin-right: auto; }
.run-progress i {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, #4b7ef0, #7ba7ff);
  transition: width .25s var(--ease);
}
.btn {
  height: var(--h-btn); padding: 0 var(--sp-5); border-radius: var(--r-block);
  background: var(--bg-elev); border: 1px solid var(--bd);
  color: var(--tx-2); font-size: var(--fs-strong); font-weight: 500; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:hover { background: var(--bg-hover); color: var(--tx); border-color: var(--bd-strong); }
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: linear-gradient(180deg, #6a9bff, #4b7ef0);
  border-color: rgba(255,255,255,.2); color: #fff; font-weight: 600;
  box-shadow: 0 3px 12px rgba(91,140,255,.34), inset 0 1px 0 rgba(255,255,255,.22);
  letter-spacing: .2px;
}
.btn.primary:hover {
  background: linear-gradient(180deg, #7caaff, #5a8af7); color: #fff;
  box-shadow: 0 5px 18px rgba(91,140,255,.44), inset 0 1px 0 rgba(255,255,255,.26);
  transform: translateY(-1px);
}
.btn.primary:active { transform: translateY(0); box-shadow: inset 0 1px 3px rgba(0,0,0,.22); }
/* 按钮四级（v1.0.15 收口）：primary 实心强调（每个弹窗只许 1 个，放最右）
   secondary = .btn 默认描边 / ghost 无边框仅悬停有底 / danger 红描边红字 */
.btn.ghost {
  background: transparent; border-color: transparent; color: var(--tx-2);
}
.btn.ghost:hover { background: var(--bg-hover); border-color: transparent; color: var(--tx); }
.btn.danger {
  background: transparent; border-color: rgba(242,106,95,.5); color: var(--red);
}
.btn.danger:hover { background: var(--red-soft); border-color: var(--red); color: var(--red); }
/* ===== 禁用态统一出口：opacity .4 + not-allowed（任何控件都不许再各写一个值） ===== */
.inp:disabled,
select.inp:disabled,
.sf-btn:disabled,
.btn:disabled,
.pf-chk:disabled,
.btn.sm:disabled { opacity: .4; cursor: not-allowed; box-shadow: none; }
.btn:disabled:hover { background: var(--bg-elev); color: var(--tx-2); border-color: var(--bd); transform: none; }
/* 输入框禁用时压掉悬停描边，避免「看着能点」 */
.inp:disabled:hover { border-color: var(--bd); }

/* ---------- 表单行内控件（页面尺寸等功能） ---------- */
.pane {
  margin: -2px 0 var(--sp-2) 20px;
  padding: var(--sp-2) 10px;
  border-left: 2px solid rgba(91, 140, 255, .26);
  background: rgba(255, 255, 255, .025);
  border-radius: 0 var(--r-block) var(--r-block) 0;
  display: none;
}
.pane.on { display: block; }
.field-line {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  /* wrap 只是「防横向溢出」的最后防线：设计上每行都必须放得下，
     折行会被 _tools/_ui_audit.js 记为违规（见 docs/UI设计规范.md「为什么不这么做」） */
  row-gap: var(--sp-2);
}
.field-line + .field-line { margin-top: var(--sp-2); }
/* 页面范围三选项（当前页 / 全部页 / 指定范围） */
.page-scope { flex: 0 0 auto; }
.page-scope .sf-btn { height: var(--h-ctl); padding: 0 var(--sp-3); font-size: var(--fs-body); }
.field-line .inp:not(.num) { flex: 1 1 150px; min-width: 150px; }
.fl-lb { font-size: var(--fs-label); color: var(--tx-3); white-space: nowrap; flex: 0 0 auto; }
.fl-unit { font-size: var(--fs-label); color: var(--tx-3); white-space: nowrap; flex: 0 0 auto; }
.inp {
  height: var(--h-ctl); padding: 0 8px;
  background: var(--bg); border: 1px solid var(--bd);
  border-radius: var(--r-ctl); color: var(--tx); font-size: var(--fs-body);
  font-family: inherit; outline: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background var(--dur) var(--ease);
}
.inp:hover { border-color: var(--bd-strong); }
.inp:focus {
  border-color: var(--accent-line);
  box-shadow: 0 0 0 3px rgba(91,140,255,.13);
  background: var(--bg-panel);
}
.inp::placeholder { color: var(--tx-3); opacity: 1; }
.inp.num { width: 78px; text-align: right; }
input[type=number].inp::-webkit-inner-spin-button { opacity: .35; }
select.inp { min-width: 150px; cursor: pointer; }
select.inp option { background: var(--bg-elev); color: var(--tx); }
#psPages { flex: 1; min-width: 180px; }

/* 全屏拖入遮罩 */
/* ============ 轻提示 toast（动作结果反馈） ============ */
.toast-wrap {
  position: fixed;
  left: 50%; bottom: 44px;
  transform: translateX(-50%);
  z-index: 200;
  display: flex; flex-direction: column-reverse; align-items: center;
  gap: 8px;
  pointer-events: none;
  max-width: min(560px, calc(100vw - 40px));
}
.toast {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 9px 14px 9px 11px;
  border-radius: 9px;
  background: rgba(30, 35, 42, .97);
  border: 1px solid #3a424c;
  box-shadow: 0 10px 28px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
  font-size: 12px; color: var(--tx);
  line-height: 1.5;
  opacity: 0; transform: translateY(8px) scale(.98);
  transition: opacity .2s, transform .2s;
  pointer-events: auto;
  cursor: pointer;
  backdrop-filter: blur(4px);
}
.toast.in { opacity: 1; transform: none; }
.toast-ic {
  flex: 0 0 17px; width: 17px; height: 17px; margin-top: 1px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; line-height: 1;
  background: rgba(255,255,255,.08); color: var(--tx-2);
}
.toast-tx { flex: 1; min-width: 0; word-break: break-word; }
.toast.ok  { border-color: rgba(62,207,142,.4); }
.toast.ok .toast-ic  { background: var(--green-soft); color: var(--green); }
.toast.err { border-color: rgba(242,106,95,.45); }
.toast.err .toast-ic { background: var(--red-soft); color: var(--red); }
.toast.info .toast-ic { background: rgba(255,255,255,.07); color: var(--tx-2); }

/* 动作失败时弹窗轻微抖动，把注意力拉回结果列表（保留拖动后的位置） */
@keyframes shakeX {
  0%, 100% { transform: translate(var(--mx, 0px), var(--my, 0px)); }
  18% { transform: translate(calc(var(--mx, 0px) - 6px), var(--my, 0px)); }
  36% { transform: translate(calc(var(--mx, 0px) + 5px), var(--my, 0px)); }
  54% { transform: translate(calc(var(--mx, 0px) - 4px), var(--my, 0px)); }
  72% { transform: translate(calc(var(--mx, 0px) + 3px), var(--my, 0px)); }
  88% { transform: translate(calc(var(--mx, 0px) - 1px), var(--my, 0px)); }
}
.modal-box.shake { animation: shakeX .5s ease-in-out; }

/* ============ 画布上的画框图层（胶带 · 鼠标画框） ============ */
.page-stage { position: relative; }
.draw-layer {
  position: absolute;
  z-index: 6;
  display: none;
  cursor: crosshair;
  touch-action: none;
}
.page-stage.draw-on .draw-layer { display: block; }
.draw-rect {
  position: absolute;
  box-sizing: border-box;
  border: 1.5px dashed rgba(91,140,255,.95);
  background: rgba(91,140,255,.20);
  border-radius: 1px;
  pointer-events: none;
}
.draw-rect.live {
  border-style: solid;
  border-color: #7ab0ff;
  background: rgba(122,176,255,.32);
  box-shadow: 0 0 0 1px rgba(0,0,0,.35) inset;
}
.draw-rect .dr-idx {
  position: absolute; left: -1px; top: -17px;
  min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 3px;
  background: var(--accent); color: #fff;
  font-size: 10px; line-height: 15px; text-align: center;
  font-variant-numeric: tabular-nums;
}
.draw-hint {
  position: absolute; left: 50%; top: 10px;
  transform: translateX(-50%);
  z-index: 12;
  display: none; align-items: center; gap: 8px;
  padding: 6px 13px;
  border-radius: 16px;
  background: rgba(24,28,34,.94);
  border: 1px solid rgba(91,140,255,.45);
  color: var(--tx-2);
  font-size: 11.5px;
  box-shadow: 0 6px 20px rgba(0,0,0,.45);
  pointer-events: none;
  white-space: nowrap;
}
.draw-hint.on { display: flex; }
.draw-hint b { color: var(--accent); font-weight: 600; }

/* ============ 页面覆盖层（胶带 / 出血 / 裁切：在大预览区直接演示 + 拖边调参） ============ */
.pov-layer {
  position: absolute;
  z-index: 7;
  display: none;
  overflow: visible;          /* 出血带要能画到页面外侧 */
  pointer-events: none;
}
.pov-layer.on { display: block; }
.pov-el {
  position: absolute;
  box-sizing: border-box;
  pointer-events: none;
  border-radius: 1px;
}
/* 会被裁掉 / 不在范围内的部分 */
.pov-el.dim { background: rgba(6, 8, 12, .56); }
/* 保留范围：红色虚线（与裁切输出口径一致） */
.pov-el.keep {
  border: 1.5px dashed #ff5a52;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);
}
.pov-el.keep-soft { border: 1.5px dashed rgba(255, 90, 82, .7); }
/* 成品线（出血的裁切线） */
.pov-el.trim {
  border: 1.5px dashed #ff5a52;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .4);
}
.pov-el.edge-hint { border: 1px solid rgba(91, 140, 255, .55); background: rgba(91, 140, 255, .06); }
/* 出血位：半透明蓝 = 「这一圈是要补出来的」，与页面原有黑白内容无关也能看清 */
.pov-el.bleed-tint {
  background: rgba(91, 140, 255, .30);
  box-shadow: inset 0 0 0 1px rgba(122, 176, 255, .55);
}
/* 出血外边界 */
.pov-el.bleed-edge { border: 1px dashed rgba(122, 176, 255, .95); }
/* 取样线（向内取样的实际位置）：青色、3px 粗、5/5 短划。
   与红虚线（成品线 1.5px / 虚线段更长）、淡蓝虚线（出血边界 1px）在
   颜色、线宽、划长三处都不同，缩到 30% 也一眼分得开；
   外圈一圈暗描边保证压在白色纸面或深色底上都看得清。 */
.pov-el.bleed-sample { box-shadow: 0 0 0 1px rgba(0, 0, 0, .55); }
.pov-el.bleed-sample.h {
  background-image: repeating-linear-gradient(90deg, #22d3ee 0 5px, rgba(34, 211, 238, 0) 5px 10px);
}
.pov-el.bleed-sample.v {
  background-image: repeating-linear-gradient(180deg, #22d3ee 0 5px, rgba(34, 211, 238, 0) 5px 10px);
}
.pov-tag {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  padding: 2px 7px;
  border-radius: 9px;
  background: rgba(14, 17, 22, .82);
  border: 1px solid rgba(255, 255, 255, .16);
  color: #fff;
  font-size: 10.5px; line-height: 1.4; letter-spacing: .2px;
  white-space: nowrap;
  pointer-events: none;
}
.pov-tag.top { top: 0; transform: translate(-50%, -50%); }
.pov-tag.out { top: -2px; transform: translate(-50%, -100%); }
.pov-cv {
  position: absolute;
  pointer-events: none;
  border-radius: 1px;
}
/* 把手：只有它接收鼠标，页面其它地方照常平移 / 滚动 */
.pov-h {
  position: absolute;
  pointer-events: auto;
  touch-action: none;
  z-index: 2;
}
.pov-h::after {
  content: '';
  position: absolute;
  border-radius: 2px;
  background: rgba(255, 255, 255, .62);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .45);
}
.pov-h-l::after, .pov-h-r::after {
  left: 50%; top: 0; width: 2px; height: 100%;
  transform: translateX(-1px);
}
.pov-h-t::after, .pov-h-b::after {
  top: 50%; left: 0; height: 2px; width: 100%;
  transform: translateY(-1px);
}
.pov-h::after { transition: background .12s; }
.pov-h:hover::after { background: var(--accent); }
.pov-h.dot::after {
  left: 50%; top: 50%; width: 11px; height: 11px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: var(--accent);
  box-shadow: 0 0 0 1.5px rgba(0, 0, 0, .5);
}
/* 透视校正：四个角是可自由拖的点，比普通圆点大一圈，好对准纸角 */
.pov-h-pt::after {
  left: 50%; top: 50%; width: 14px; height: 14px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: #4a9eff;
  box-shadow: 0 0 0 2px #fff, 0 0 0 3px rgba(0, 0, 0, .55);
}
.pov-h-pt:hover::after { background: #77baff; }

/* 四角坐标：一个角一行，x / y 并排 */
.quad-grid {
  /* v1.0.94（弹窗 560→420）：四角原来是两列，收窄后每列只剩 ~140px，
     一行「左上 [x] [y] mm」要 209px → 横向溢出。改成一列，
     四个角各占一行，坐标反而好对（同一行的 x / y 视觉上成对）。 */
  display: grid; grid-template-columns: 1fr; gap: 6px 14px; margin-top: 8px;
}
.quad-grid label { display: flex; align-items: center; gap: 6px; }
.quad-grid label > span { width: 30px; flex: none; color: var(--tx-dim); font-size: 12px; }
.quad-grid .inp { width: 70px; }
.quad-grid i { font-style: normal; color: var(--tx-dim); font-size: 11px; }

.pov-hint {
  /* 整条骑在页面上边缘的**外侧**：以前是压进页面里 17px，
     出血取样线（2–3mm 深）正好落在它后面，等于看不见。
     现在完全待在 .page-stage 的上内边距里，不盖画面。 */
  position: absolute; left: 50%; top: auto; bottom: 100%;
  margin-bottom: 4px;
  transform: translateX(-50%);
  z-index: 3;
  display: none; align-items: center; gap: 6px;
  max-width: 92%;
  padding: 5px 12px;
  border-radius: 15px;
  background: rgba(24, 28, 34, .94);
  border: 1px solid rgba(91, 140, 255, .42);
  color: var(--tx-2);
  font-size: 11px; line-height: 1.5;
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
  pointer-events: none;
}
.pov-hint.on { display: flex; }

/* 覆盖层图例：一行极简小字，骑在页面下边缘外侧（与 hint 上下对称）。
   项目铁律：预览里不弹窗 —— 说明只能是这样一行静态文字。 */
.pov-legend {
  position: absolute; left: 50%; top: 100%; margin-top: 2px;
  transform: translateX(-50%);
  z-index: 3;
  display: none;
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(24, 28, 34, .92);
  border: 1px solid rgba(34, 211, 238, .38);
  color: #9fe9f6;
  font-size: 10px; line-height: 1.4;
  white-space: nowrap;
  /* 一行小字，宽度封在页面以内：拼版图例最长（页段 / 双联 / 分贴），
     不封就会顶出页面把整条推出视口（超出的部分谁也读不到）。 */
  max-width: 92%;
  overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .42);
}
.pov-legend.on { display: block; }

/* ---- 封面拼版预览（PageOverlay id=cover）：封底 | 书脊 | 封面 三块分区 ----
   颜色分工与出图的矢量标记对齐：红虚线 = 成品框，淡蓝 = 出血区，
   黄框 = 书脊两条边界（间距就是算出来的背脊厚度），黑实线 = 角线 / 背脊双线。
   全部带 1px 以上描边，压在白色纸面或深色底上都看得清。 */
.pov-el.cv-back {
  background: rgba(91, 140, 255, .16);
  box-shadow: inset 0 0 0 1px rgba(122, 176, 255, .90);
}
.pov-el.cv-front {
  background: rgba(52, 211, 153, .15);
  box-shadow: inset 0 0 0 1px rgba(52, 211, 153, .90);
}
.pov-el.cv-spine {
  background: rgba(255, 214, 0, .22);
  box-shadow: inset 0 0 0 1.5px rgba(255, 214, 0, .95);
}
.pov-el.cv-trim { border: 1.5px dashed #ff5a52; box-shadow: 0 0 0 1px rgba(0, 0, 0, .30); }
/* 出血区：要比「出血边界虚线」更面状地看得出来，又不能盖住底下的合成图 */
.pov-el.cv-bleed {
  background: rgba(91, 140, 255, .22);
  box-shadow: inset 0 0 0 1px rgba(122, 176, 255, .45);
}
.pov-el.cv-bleed-edge { border: 1px dashed rgba(122, 176, 255, .95); }
.pov-el.cv-mark { background: #0b0e13; box-shadow: 0 0 0 .5px rgba(255, 255, 255, .35); }
.pov-tag.cv-lb {
  font-size: 11px; font-weight: 600; letter-spacing: .3px;
  background: rgba(10, 13, 18, .90); border-color: rgba(255, 255, 255, .22);
}

/* ---- 拼版（PageOverlay id=impose）：真实拼版结果 1:1 展示 ----
   2026-11 客户改版：预览不再画「一页原图 + 一个占位小框」，而是按拼版算出来的
   格位逐格贴真实页面内容（canvas 铺白纸 + 每格内容），这一层只画「线」：
     · 淡蓝虚线 = 纸张边界
     · 淡红虚线 = 每格的成品位边界（裁切线，客户要的就是这一圈）
     · 淡蓝点线 = 每格出血区边界
     · 深色短线 = 页面交界角线；蓝灰虚线 = 折缝
   所有线都不填色：内容要看得清（客户原话「虚线用浅色，别盖住内容」）。 */
.pov-el.im-sheet {
  border: 1px dashed rgba(122, 176, 255, .85);
  background: transparent;
}
.pov-el.im-content { border: 1px dashed rgba(52, 211, 153, .38); }
.pov-el.im-cell { border: 1px dashed rgba(255, 90, 82, .85); }
.pov-el.im-cell.empty {
  border-color: rgba(255, 255, 255, .40);
  background: transparent;
}
.pov-el.im-cellbleed {
  background: transparent;
  border: 1px dotted rgba(122, 176, 255, .55);
}
.pov-el.im-junc { background: #0b0e13; box-shadow: 0 0 0 .5px rgba(255, 255, 255, .35); }
/* 页码角标：缩到左上角一小块 —— 内容才是主体，数字只用来核页序 */
.pov-tag.im-num {
  left: 3px; top: 3px; transform: none;
  font-size: 10.5px; font-weight: 700; line-height: 1.25;
  padding: 0 5px;
  background: rgba(10, 13, 18, .74); border-color: rgba(255, 255, 255, .22);
}
.pov-tag.im-num.empty { color: #9aa4b2; font-weight: 400; }

/* 要转 180° 的版位：只换虚线颜色 + 实心角标，不填色（内容要透出来） */
.pov-el.im-cell.rev {
  border-color: rgba(255, 176, 32, .92);
  background: transparent;
  box-shadow: none;
}
.pov-tag.im-num.rev { color: #ffd479; border-color: rgba(255, 176, 32, .60); }
.pov-el.im-revbadge {
  display: flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 800; letter-spacing: .2px;
  color: #1b1205; background: #ffb020;
  border-radius: 4px; box-shadow: 0 1px 4px rgba(0, 0, 0, .45);
  white-space: nowrap; pointer-events: none;
}
/* 90° / 270°（横版折手）：角标换青底、格子边框也换青，和 180° 的橙一眼分开。
   底色只是第二层保险 —— 第一层是角标上写的确切角度（↻90° / ↻270° / ↻180°）。 */
.pov-el.im-cell.rev.rot90 {
  border-color: rgba(78, 201, 255, .92);
  background: transparent;
  box-shadow: none;
}
.pov-tag.im-num.rev.rot90 { color: #b9ecff; border-color: rgba(78, 201, 255, .60); }
.pov-el.im-revbadge.rot90 { color: #04202b; background: #4ec9ff; }
/* 折缝（骑缝）：折页机照这条线折，图上必须是虚线。
   纸面现在是白的（内容真的贴上去了），白虚线会消失 —— 换蓝灰，压在内容上也看得见。 */
.pov-el.im-foldline {
  border-left: 1.5px dashed rgba(28, 92, 168, .70);
  border-top: 1.5px dashed rgba(28, 92, 168, .70);
  background: transparent;
}
/* 分段按钮被禁用（如「骑马钉 + 全开折」结构上不成立） */
.sf-btn[disabled] { opacity: .38; cursor: not-allowed; }

/* 胶带弹窗：画框说明卡 */
.draw-tip {
  font-size: 11.5px; line-height: 1.65;
  color: var(--tx-2);
  background: rgba(91,140,255,.07);
  border: 1px solid rgba(91,140,255,.22);
  border-radius: 7px;
  padding: 9px 11px;
  margin-bottom: 9px;
}
.draw-tip b { color: var(--accent); font-weight: 600; }
.draw-tip kbd {
  font-family: inherit; font-size: 10.5px;
  padding: 0 4px; border-radius: 3px;
  background: rgba(255,255,255,.10);
  border: 1px solid var(--bd);
  color: var(--tx);
}
.draw-count { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 600; }
.pane.flat {
  margin: 0 0 12px 0;
  padding: 10px 12px;
  border-left: 1px solid var(--bd);
  background: rgba(255,255,255,.02);
  border-radius: 7px;
}
.pane.flat > .form-row:last-child { margin-bottom: 0; }
.opt.sm.draw-reuse { margin-top: var(--sp-3); }

/* ============================================================
   印前预检（Preflight）
   ============================================================ */
/* 预检报告表格较宽 → M 档（原 440px 时「判定阈值」三个数字框会折行） */
#pfModal .modal-box { width: var(--mw-m); }
#pfModal .modal-body { overflow-y: auto; }
/* 分页拆分：说明文字多；结果多时列表内滚动 */
#splitModal .modal-box { width: var(--mw-m); }
#splitModal .result-list { max-height: 180px; overflow-y: auto; }
#splitModal .result-list:empty { display: none; }
/* 复制页面：两种复制方式的说明较长 */
#dupModal .modal-box { width: var(--mw-m); }
#dupModal .modal-body { overflow-y: auto; }
#dupModal .result-list { max-height: 170px; overflow-y: auto; }
#dupModal .result-list:empty { display: none; }
/* 页面旋转：选项文字较长 */
#rotateModal .modal-box { width: var(--mw-m); }
#rotateModal .modal-body { overflow-y: auto; }
#rotateModal .result-list { max-height: 170px; overflow-y: auto; }
#rotateModal .result-list:empty { display: none; }

/* 合并页面 */
#mergeModal .modal-box { width: var(--mw-m); }
#mergeModal .modal-body { overflow-y: auto; }
#mergeModal .result-list { max-height: 160px; overflow-y: auto; }
#mergeModal .result-list:empty { display: none; }
.mg-guard {
  display: flex; align-items: flex-start; gap: 7px;
  margin-top: 4px; padding: 9px 11px;
  border: 1px solid rgba(242,163,60,.26);
  border-radius: var(--radius);
  background: var(--orange-soft);
  font-size: 11px; line-height: 1.6; color: var(--tx-2);
}
.mg-guard::before {
  content: '!';
  flex: 0 0 auto;
  width: 15px; height: 15px; margin-top: 1px;
  border-radius: 50%;
  background: rgba(242,163,60,.22);
  color: var(--orange);
  font-size: 10px; font-weight: 700; line-height: 15px; text-align: center;
}
.mg-guard b { color: var(--orange); font-weight: 600; }

/* 检查项开关：像标签一样的小胶囊 */
.pf-checks { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 6px; }
.pf-chk {
  height: var(--h-ctl); padding: 0 var(--sp-3);
  border: 1px solid var(--bd);
  border-radius: 13px;
  background: transparent;
  color: var(--tx-3);
  font-size: var(--fs-body);
  cursor: pointer;
  transition: background .14s, color .14s, border-color .14s;
  white-space: nowrap;
}
.pf-chk:hover { background: var(--bg-hover); color: var(--tx-2); }
.pf-chk.on {
  background: var(--accent-soft);
  border-color: rgba(91,140,255,.42);
  color: var(--accent-2);
}
.pf-chk.on::before {
  content: '✓';
  margin-right: 5px;
  font-size: 10px;
  opacity: .85;
}

/* 概览卡 */
.pf-summary:empty { display: none; }
.pf-sum {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
  padding: 11px 13px;
  margin: 12px 0 10px;
  border-radius: 9px;
  border: 1px solid var(--bd);
  background: rgba(255,255,255,.02);
}
.pf-sum.ok    { border-color: rgba(62,207,142,.32); background: var(--green-soft); }
.pf-sum.warn  { border-color: rgba(242,163,60,.32); background: var(--orange-soft); }
.pf-sum.error { border-color: rgba(242,106,95,.32); background: var(--red-soft); }
.pf-sum-main { display: flex; align-items: center; gap: 10px; }
.pf-sum-ic {
  width: 26px; height: 26px; flex: 0 0 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; font-size: 13px; font-weight: 700;
}
.pf-sum.ok    .pf-sum-ic { background: var(--green);  color: #08150e; }
.pf-sum.warn  .pf-sum-ic { background: var(--orange); color: #1a1004; }
.pf-sum.error .pf-sum-ic { background: var(--red);    color: #1a0806; }
.pf-sum-t { font-size: 13px; font-weight: 600; }
.pf-sum-d { font-size: 11.5px; color: var(--tx-3); margin-top: 1px; }
.pf-sum-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.pf-chip {
  font-size: 11px; padding: 2px 8px; border-radius: 10px;
  border: 1px solid transparent; font-variant-numeric: tabular-nums;
}
.pf-chip.error { background: var(--red-soft);    color: var(--red);    border-color: rgba(242,106,95,.28); }
.pf-chip.warn  { background: var(--orange-soft); color: var(--orange); border-color: rgba(242,163,60,.28); }
.pf-chip.info  { background: var(--accent-soft); color: var(--accent-2); border-color: rgba(91,140,255,.26); }
.pf-chip.ok    { background: var(--green-soft);  color: var(--green);  border-color: rgba(62,207,142,.28); }
.pf-chip.zero  { opacity: .42; }

/* 报告 */
.pf-report { display: flex; flex-direction: column; gap: 12px; }
.pf-file {
  border: 1px solid var(--bd-soft);
  border-radius: 9px;
  overflow: hidden;
  background: rgba(255,255,255,.014);
}
.pf-file-head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 11px;
  background: rgba(255,255,255,.025);
  border-bottom: 1px solid var(--bd-soft);
}
.pf-file-name {
  font-size: 12.5px; font-weight: 600;
  max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pf-file-meta { font-size: 11px; color: var(--tx-3); }
.pf-file-ms { font-size: 10.5px; color: var(--tx-3); margin-left: auto; font-variant-numeric: tabular-nums; }
.pf-badge {
  font-size: 10.5px; padding: 1px 8px; border-radius: 10px; margin-left: auto;
}
.pf-badge.ok    { background: var(--green-soft);  color: var(--green); }
.pf-badge.warn  { background: var(--orange-soft); color: var(--orange); }
.pf-badge.error { background: var(--red-soft);    color: var(--red); }
.pf-badge + .pf-file-ms { margin-left: 8px; }

.pf-items { display: flex; flex-direction: column; }
.pf-item {
  display: flex; gap: 9px;
  padding: 9px 11px;
  border-bottom: 1px solid rgba(255,255,255,.035);
}
.pf-item:last-child { border-bottom: none; }
.pf-dot {
  width: 16px; height: 16px; flex: 0 0 16px;
  margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  font-size: 10px; font-weight: 700;
}
.pf-item.error .pf-dot { background: var(--red-soft);    color: var(--red);    box-shadow: inset 0 0 0 1px rgba(242,106,95,.4); }
.pf-item.warn  .pf-dot { background: var(--orange-soft); color: var(--orange); box-shadow: inset 0 0 0 1px rgba(242,163,60,.4); }
.pf-item.info  .pf-dot { background: var(--accent-soft); color: var(--accent-2); box-shadow: inset 0 0 0 1px rgba(91,140,255,.36); }
.pf-item.ok    .pf-dot { background: var(--green-soft);  color: var(--green);  box-shadow: inset 0 0 0 1px rgba(62,207,142,.36); }
.pf-body { flex: 1 1 auto; min-width: 0; }
.pf-t { font-size: 12.5px; font-weight: 600; }
.pf-item.ok  .pf-t { color: var(--tx-2); font-weight: 500; }
.pf-item.error .pf-t { color: #ffb4ad; }
.pf-item.warn  .pf-t { color: #ffd39b; }
.pf-d { font-size: 11.5px; color: var(--tx-3); margin-top: 2px; line-height: 1.62; }

.pf-pages { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.pf-page {
  height: 21px; padding: 0 7px;
  border: 1px solid var(--bd);
  border-radius: 5px;
  background: transparent;
  color: var(--tx-2);
  font-size: 10.5px;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  transition: background .12s, border-color .12s, color .12s;
}
.pf-page:hover { background: var(--accent-soft); border-color: rgba(91,140,255,.5); color: var(--accent-2); }
.pf-page-more { font-size: 10.5px; color: var(--tx-3); align-self: center; padding-left: 2px; }

/* 解决方案 + 一键修复 */
.pf-sol {
  display: flex; gap: 7px; align-items: flex-start;
  margin-top: 7px; padding: 7px 9px;
  background: rgba(91,140,255,.07);
  border: 1px solid rgba(91,140,255,.22);
  border-radius: 7px;
}
.pf-sol-l {
  flex: 0 0 auto;
  font-size: 10px; font-weight: 700; letter-spacing: .03em;
  color: var(--accent-2);
  background: var(--accent-soft);
  border-radius: 4px; padding: 1px 6px; margin-top: 1px;
  white-space: nowrap;
}
.pf-sol-t { font-size: 11.5px; color: var(--tx-2); line-height: 1.6; }
.pf-fix { margin-top: 8px; }
.pf-fix-btn {
  height: 28px; padding: 0 14px;
  border: 1px solid rgba(62,207,142,.5);
  border-radius: 7px;
  background: linear-gradient(180deg, rgba(62,207,142,.18), rgba(62,207,142,.1));
  color: var(--green);
  font-size: 12px; font-weight: 600;
  cursor: pointer;
  transition: background .12s, border-color .12s, transform .06s;
}
.pf-fix-btn:hover { background: rgba(62,207,142,.28); border-color: rgba(62,207,142,.7); }
.pf-fix-btn:active { transform: translateY(1px); }
.pf-fix-btn:disabled { opacity: .6; cursor: progress; }
.pf-fixed {
  margin-top: 7px; font-size: 11.5px; font-weight: 600; color: var(--green);
}
.pf-item.error .pf-sol { background: rgba(242,106,95,.08); border-color: rgba(242,106,95,.24); }
.pf-item.error .pf-sol-l { color: var(--red); background: var(--red-soft); }
.pf-item.error .pf-fix-btn { border-color: rgba(242,106,95,.5); background: linear-gradient(180deg, rgba(242,106,95,.18), rgba(242,106,95,.1)); color: var(--red); }
.pf-item.error .pf-fix-btn:hover { background: rgba(242,106,95,.28); border-color: rgba(242,106,95,.7); }

/* 一键自动优化 横幅 */
.pf-auto {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 12px; padding: 11px 14px;
  background: linear-gradient(180deg, rgba(91,140,255,.12), rgba(91,140,255,.06));
  border: 1px solid rgba(91,140,255,.32);
  border-radius: 10px;
}
.pf-auto-txt { flex: 1 1 auto; font-size: 12px; color: var(--tx-1); line-height: 1.55; }
.pf-auto-btn {
  flex: 0 0 auto; height: 32px; padding: 0 18px;
  border: none; border-radius: 8px;
  background: linear-gradient(180deg, #5b8cff, #3f6fe6);
  color: #fff; font-size: 13px; font-weight: 700;
  cursor: pointer; white-space: nowrap;
  box-shadow: 0 2px 8px rgba(63,111,230,.32);
  transition: filter .12s, transform .06s;
}
.pf-auto-btn:hover { filter: brightness(1.08); }
.pf-auto-btn:active { transform: translateY(1px); }
.pf-auto-btn:disabled { opacity: .65; cursor: progress; }
.pf-auto.neutral { background: rgba(140,140,150,.08); border-color: rgba(140,140,150,.28); }
.pf-auto-txt.muted { color: var(--tx-2, #8a8f99); opacity: .92; }

/* 窄窗口：整组换行（.toolbar 已 flex-wrap），不隐藏文字。
   只有真的放不下时才稍微收一点内边距。 */
@media (max-width: 1180px) {
  .tb-cap { display: none; }
  .hbtn { padding: 0 8px; gap: 5px; }
  .brand-sub { display: none; }
  /* 窄窗口下左栏头部的按钮排不下（nowrap 后不再把文字压成竖排，改为收紧内边距 + 降一档字号） */
  .panel-head { padding: 0 8px 0 10px; }
  .panel-head .mini-btn { padding: 0 6px; font-size: var(--fs-note); }
  .panel-head .panel-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* ============================================================
   授权 / 购买（v2.0.0）
   状态栏徽标 + 激活弹窗 + 到期全屏锁
   ============================================================ */

/* ---- 状态栏徽标（就是那句小广告） ---- */
.lic-badge {
  display: inline-flex; align-items: center; gap: 6px;
  height: 20px; padding: 0 10px;
  border-radius: 999px;
  border: 1px solid var(--bd);
  background: var(--bg-elev);
  color: var(--tx-2);
  font-size: 10.5px; font-weight: 500;
  letter-spacing: .2px;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.lic-badge:hover { background: var(--bg-hover); border-color: var(--bd-strong); color: var(--tx); }
.lic-badge-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--tx-3); flex: 0 0 auto; }
.lic-badge.warn { border-color: rgba(242,163,60,.30); background: var(--orange-soft); color: var(--orange); }
.lic-badge.warn .lic-badge-dot { background: var(--orange); box-shadow: 0 0 0 3px rgba(242,163,60,.14); }
.lic-badge.ok { border-color: rgba(62,207,142,.28); background: var(--green-soft); color: var(--green); }
.lic-badge.ok .lic-badge-dot { background: var(--green); box-shadow: 0 0 0 3px rgba(62,207,142,.14); }
.lic-badge.bad { border-color: rgba(242,106,95,.32); background: var(--red-soft); color: var(--red); }
.lic-badge.bad .lic-badge-dot { background: var(--red); box-shadow: 0 0 0 3px rgba(242,106,95,.14); }

/* ---- 激活弹窗 ---- */
#licModal .modal-box { width: var(--mw-m); }
#licModal .modal-body { overflow-y: auto; }

.lic-state {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 9px 11px; margin-bottom: 13px;
  border: 1px solid var(--bd); border-radius: var(--radius);
  background: var(--bg-elev);
}
.lic-state-dot { width: 7px; height: 7px; border-radius: 50%; margin-top: 5px; flex: 0 0 auto; background: var(--tx-3); }
.lic-state-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.lic-state-txt b { font-size: 12.5px; font-weight: 600; color: var(--tx); }
.lic-state-txt span { font-size: 11px; color: var(--tx-3); line-height: 1.5; }
.lic-state.ok { border-color: rgba(62,207,142,.26); background: var(--green-soft); }
.lic-state.ok .lic-state-dot { background: var(--green); }
.lic-state.ok .lic-state-txt b { color: var(--green); }
.lic-state.warn { border-color: rgba(242,163,60,.26); background: var(--orange-soft); }
.lic-state.warn .lic-state-dot { background: var(--orange); }
.lic-state.warn .lic-state-txt b { color: var(--orange); }
.lic-state.bad { border-color: rgba(242,106,95,.28); background: var(--red-soft); }
.lic-state.bad .lic-state-dot { background: var(--red); }
.lic-state.bad .lic-state-txt b { color: var(--red); }

.lic-machine-row { display: flex; align-items: center; gap: 7px; }
.lic-machine {
  flex: 1 1 auto; min-width: 0;
  padding: 6px 10px;
  border: 1px dashed var(--bd-strong); border-radius: var(--r-sm);
  background: var(--bg-stage);
  color: var(--accent-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; letter-spacing: 1.2px;
  user-select: all;
}
.lic-mini-btn {
  flex: 0 0 auto;
  height: 28px; padding: 0 10px;
  border: 1px solid var(--bd); border-radius: var(--r-sm);
  background: var(--bg-elev); color: var(--tx-2);
  font-size: 11px; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.lic-mini-btn:hover { background: var(--bg-hover); color: var(--tx); border-color: var(--bd-strong); }

.lic-key-input {
  width: 100%; box-sizing: border-box; resize: none;
  padding: 8px 10px;
  border: 1px solid var(--bd); border-radius: var(--r-sm);
  background: var(--bg-stage); color: var(--tx);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px; line-height: 1.6; letter-spacing: .6px;
  outline: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.lic-key-input:focus { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft); }
.lic-key-input::placeholder { color: var(--tx-3); letter-spacing: 0; font-family: inherit; }

.lic-msg { font-size: 11px; line-height: 1.5; min-height: 0; margin-top: 5px; color: var(--tx-3); }
.lic-msg:empty { display: none; }
.lic-msg.bad { color: var(--red); }
.lic-msg.ok { color: var(--green); }

.lic-actions { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.lic-hint { font-size: 10.5px; color: var(--tx-3); }

/* ---- 购买区：文案 + 二维码 ---- */
.lic-buy {
  display: flex; align-items: center; gap: 14px;
  margin-top: 14px; padding: 12px 13px;
  border: 1px solid var(--accent-line); border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(91,140,255,.10), rgba(172,130,255,.06));
}
.lic-buy-left { flex: 1 1 auto; min-width: 0; }
.lic-buy-left b { display: block; font-size: 12.5px; color: var(--accent-2); margin-bottom: 4px; }
.lic-buy-left p { margin: 0 0 4px; font-size: 11px; color: var(--tx-3); line-height: 1.6; }
.lic-buy-left p:last-child { margin-bottom: 0; }
.lic-wechat code {
  color: var(--tx); background: rgba(255,255,255,.07);
  padding: 1px 6px; border-radius: 4px; letter-spacing: .4px;
}
.lic-qr {
  flex: 0 0 auto; width: 104px; height: 104px;
  display: flex; align-items: center; justify-content: center;
  padding: 5px; box-sizing: border-box;
  background: #fff; border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0,0,0,.35);
}
.lic-qr svg, .lic-qr img { width: 100%; height: 100%; display: block; border-radius: 4px; }
.lic-qr-fail { font-size: 10px; color: #666; text-align: center; }
.lic-qr-fail span { font-size: 9px; color: #999; }

/* ---- 设备与换绑 ---- */
.lic-dev {
  margin-top: 12px; padding: 10px 12px 11px;
  border: 1px solid var(--bd); border-radius: var(--radius);
  background: rgba(0,0,0,.16);
}
.lic-dev-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 8px;
}
.lic-dev-head b { font-size: 12px; font-weight: 600; color: var(--tx); }
.lic-dev-seats {
  font-size: 10.5px; font-weight: 600; letter-spacing: .2px;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid var(--accent-line); border-radius: 20px;
  padding: 1px 8px;
}
.lic-dev-seats.full { color: var(--orange); background: var(--orange-soft); border-color: rgba(242,163,60,.30); }
.lic-dev-line { display: flex; align-items: center; gap: 8px; }
.lic-dev-k { font-size: 11px; color: var(--tx-3); flex: 0 0 auto; }
.lic-dev-line code {
  font-family: var(--font-mono, ui-monospace, Consolas, monospace);
  font-size: 11.5px; letter-spacing: .8px; color: var(--tx);
  background: rgba(255,255,255,.06); padding: 2px 7px; border-radius: 5px;
}
.lic-dev-tag {
  font-style: normal; font-size: 10px; padding: 1px 7px; border-radius: 20px;
  color: var(--tx-3); background: rgba(255,255,255,.06); border: 1px solid var(--bd);
}
.lic-dev-tag.ok { color: var(--green); background: var(--green-soft); border-color: rgba(62,207,142,.26); }
.lic-dev-tag.warn { color: var(--orange); background: var(--orange-soft); border-color: rgba(242,163,60,.26); }
.lic-dev-tag.bad { color: var(--red); background: var(--red-soft); border-color: rgba(242,106,95,.28); }
.lic-dev-note { font-size: 11px; line-height: 1.6; color: var(--tx-3); margin: 7px 0 9px; }
.lic-dev-acts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.lic-dev-acts + .lic-dev-acts { margin-top: 6px; }
/* 小按钮：与输入框同档（26px），用于表单行里与 .inp 并排的场景 */
.btn.sm { height: var(--h-ctl); padding: 0 var(--sp-4); font-size: var(--fs-body); border-radius: var(--r-ctl); }

/* ---- 应用内确认框（替代系统 confirm） ---- */
.ask-mask {
  position: fixed; inset: 0; z-index: 9500;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6,8,11,.58); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity .16s var(--ease);
}
.ask-mask.in { opacity: 1; }
.ask-box {
  width: 384px; max-width: calc(100vw - 40px);
  background: var(--bg-panel); border: 1px solid var(--bd-strong);
  border-radius: 12px; box-shadow: var(--sh-pop);
  padding: 18px 20px 14px;
  transform: scale(.985); transition: transform .16s var(--ease);
}
.ask-mask.in .ask-box { transform: scale(1); }
.ask-tx { font-size: 13px; line-height: 1.78; color: var(--tx); }
.ask-inp {
  width: 100%; margin-top: 11px; height: 32px; padding: 0 10px;
  background: var(--bg); border: 1px solid var(--bd-strong); border-radius: 8px;
  color: var(--tx); font-size: 12.5px; font-family: inherit; outline: none;
}
.ask-inp:focus { border-color: var(--accent-line); box-shadow: 0 0 0 3px rgba(91,140,255,.13); background: var(--bg-panel); }
.ask-ft { display: flex; justify-content: flex-end; gap: 8px; margin-top: 17px; }
.btn.primary.danger { background: var(--red); border-color: transparent; color: #fff; }
/* 危险按钮聚焦时用红色描边，别在红底上套一圈蓝 */
.ask-box .btn.primary.danger:focus-visible { outline-color: rgba(242,106,95,.55); }

/* ============ 页面定位 ============ */
/* 九点定位 + 四边距离 + 尺寸预览三者并排 → L 档（原 612px） */
.pl-modal .modal-box { width: var(--mw-l); }
.pl-plan { min-width: 236px; }

.pl-locate {
  display: flex; gap: 20px; align-items: flex-start;
  margin: 14px 0 8px; padding: 12px 14px;
  background: var(--bg); border: 1px solid var(--bd-soft); border-radius: 9px;
}
.pl-anchor-box { flex: 0 0 auto; }
.pl-cap { font-size: var(--fs-note); color: var(--tx-3); margin-bottom: var(--sp-3); letter-spacing: .2px; }

/* 3×3 九点：圆点按钮，选中实心 */
.pl-anchor { display: grid; grid-template-columns: repeat(3, 26px); gap: 9px; }
.pl-dot {
  width: 26px; height: 26px; padding: 0; border-radius: 50%;
  background: var(--bg-elev); border: 1px solid var(--bd-strong);
  cursor: pointer; position: relative; transition: border-color .14s var(--ease), background .14s var(--ease);
}
.pl-dot:hover { border-color: var(--accent-line); background: var(--bg-hover); }
.pl-dot.on { border-color: var(--accent); background: var(--accent-soft); }
.pl-dot.on::after {
  content: ''; position: absolute; inset: 7px; border-radius: 50%; background: var(--accent);
}
.pl-dot:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; }

/* 四边距离：勾选才可填 */
.pl-offs {
  flex: 1 1 auto; display: grid; grid-template-columns: 1fr 1fr;
  gap: 9px 16px; padding-top: 22px;
}
/* 定位卡片右侧一列：四边距离在上、操作范围在下（贴着「距上 / 距下」，
   把原先独立一行的操作范围并进来，弹窗整体矮一截） */
.pl-side { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 11px; }
.pl-side .pl-offs { flex: 0 0 auto; }
.pl-range-block { border-top: 1px solid var(--bd-soft); padding-top: 9px; }
.pl-range-cap { font-size: var(--fs-note); color: var(--tx-3); margin-bottom: 6px; letter-spacing: .2px; }
.pl-range-pages { margin-top: 6px; }
.pl-range-pages .inp { flex: 0 1 240px; min-width: 140px; }
/* v1.0.94（弹窗 640→480）：右栏只有 265px，「页码」+ 输入框 + 奇/偶两个 chip
   一行放不下。给输入框一个能缩到 100px 的下限（.field-line .inp 的 150px 太宽），
   页码可以随时手输，100px 够看 1-3,5,8-n 这种；chip 固定在右侧不挪位。 */
#plPages { flex: 1 1 100px; min-width: 100px; }
.pl-off { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--tx-2); cursor: pointer; }
.pl-off .inp.num { width: 66px; height: var(--h-ctl-xs); font-size: var(--fs-label); }
/* 小输入框里微调箭头会把数字挤成「140▮」，这里统一关掉；数字框本身支持上下键微调 */
.pl-modal input[type=number].inp::-webkit-inner-spin-button,
.pl-modal input[type=number].inp::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.pl-modal input[type=number].inp { padding-right: 8px; }
.pl-locate ~ .form-note { margin-bottom: 2px; }
.pl-preview {
  min-height: 15px; font-size: var(--fs-note); line-height: 1.6;
  color: var(--accent-2); font-family: var(--mono); letter-spacing: .1px;
}
.pl-preview:empty { display: none; }
.pl-off .inp.num:disabled { opacity: .4; cursor: not-allowed; }
/* 九点已经定死的方向：整行压暗 + 勾选框也锁（勾了也没效果，不如直接不给勾） */
.pl-off.lock { color: var(--tx-3); }
.pl-off.lock .inp.num { opacity: .3; }
.pl-off.lock input[type=checkbox] { opacity: .35; cursor: not-allowed; border-color: var(--bd); }
.pl-off.lock input[type=checkbox]:hover { border-color: var(--bd); }
.pl-off input[type=checkbox],
.pl-check input[type=checkbox] {
  appearance: none; -webkit-appearance: none;
  width: 14px; height: 14px; margin: 0; flex: 0 0 auto;
  border: 1.5px solid var(--bd-strong); border-radius: 4px;
  background: rgba(255, 255, 255, .05); position: relative; cursor: pointer;
}
.pl-off input[type=checkbox]:hover,
.pl-check input[type=checkbox]:hover { border-color: var(--accent); }
.pl-off input[type=checkbox]:checked,
.pl-check input[type=checkbox]:checked { background: var(--accent); border-color: var(--accent); }
.pl-off input[type=checkbox]:checked::after,
.pl-check input[type=checkbox]:checked::after {
  content: ''; position: absolute; left: 4px; top: 1px;
  width: 3px; height: 7px; border: solid #fff; border-width: 0 1.5px 1.5px 0;
  transform: rotate(42deg);
}

.pl-off-note { margin-top: 6px; }
.pl-check { display: flex; align-items: center; gap: 8px; margin-top: 13px; font-size: 11.5px; color: var(--tx-2); cursor: pointer; }
.pl-check em { font-style: normal; font-size: 10.5px; color: var(--tx-3); }

/* ============ 插入标题 ============ */
.ti-modal .modal-box { width: var(--mw-l); }

/* 左参数 / 右页码范围。
   v1.0.94（弹窗 640→480）：原来是「1fr + 固定 240px」两栏。480 时左栏只剩 198px，
   「留边 + X + Y」那一行要 354px，怎么调都放不下 → 两栏改一栏，
   页码范围整体挪到参数下面（同一屏内仍看得见，且不再逼着参数挤成一团）。 */
.ti-grid {
  display: grid; grid-template-columns: 1fr;
  gap: var(--sp-4); align-items: start;
  margin-top: var(--sp-2);
}
.ti-cap { font-size: var(--fs-label); color: var(--tx-3); margin-bottom: var(--sp-2); }

/* 九宫格做成 3×3 按钮而不是下拉：选位置是这里最高频的操作，
   下拉要「点开、找、点选」三步，按钮一步到位。 */
.ti-anchors {
  display: grid; grid-template-columns: repeat(3, 34px);
  grid-auto-rows: 32px; gap: var(--sp-2); margin-bottom: var(--sp-3);
}
.ti-anchor {
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  background: var(--bg-elev); border: 1px solid var(--bd);
  border-radius: var(--r-ctl);
}
.ti-anchor i { width: 9px; height: 9px; border-radius: 2px; background: var(--tx-3); opacity: .5; }
.ti-anchor:hover { border-color: var(--accent-line); }
.ti-anchor.on { background: var(--accent-soft); border-color: var(--accent); }
.ti-anchor.on i { width: 12px; height: 12px; opacity: 1; background: var(--accent); }

.ti-mt { margin-top: var(--sp-3); }
/* 标题框要能看全长文件名（它就是标题来源），所以给足基础宽度，
   两个按钮跟在后面；实在放不下才折到下一行。 */
.ti-text { flex: 1 1 330px; min-width: 220px; }
.ti-num { width: 62px; }
.ti-font { flex: 1 1 118px; min-width: 108px; }
.ti-color {
  width: 34px; height: var(--h-ctl); padding: 2px; cursor: pointer;
  background: var(--bg); border: 1px solid var(--bd); border-radius: var(--r-ctl);
}
.ti-color::-webkit-color-swatch-wrapper { padding: 0; }
.ti-color::-webkit-color-swatch { border: none; border-radius: 4px; }
.ti-chk {
  display: flex; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-body); color: var(--tx-2); cursor: pointer; white-space: nowrap;
}
.ti-rotbar { margin-top: var(--sp-2); }

.ti-col-view { display: flex; flex-direction: column; gap: var(--sp-2); }
.ti-preview {
  height: 226px; display: flex; align-items: center; justify-content: center;
  background: var(--bg-stage); border: 1px solid var(--bd);
  border-radius: var(--r-block); overflow: hidden;
}
/* 纸样：白底 = 真实纸张，标题是透明底 PNG 直接叠上去，
   所以预览里看到的和输出到 PDF 上的就是同一张图。 */
.ti-sheet { position: relative; background: #fff; box-shadow: 0 2px 12px rgba(0,0,0,.45); }
.ti-ghost {
  position: absolute; left: 0; top: 0; cursor: grab; touch-action: none;
  /* 常显淡虚线框：标题字号小的时候（18pt 落在 A4 上只占页宽 3%），
     等比缩到 130px 宽的纸样里就成了一个看不清的点，不画框根本找不到它
     在哪、也就没法拖。框只是预览辅助，不写进 PDF。 */
  outline: 1px dashed rgba(91, 140, 255, .38); outline-offset: 1px;
}
.ti-ghost.drag { cursor: grabbing; outline: 1px solid var(--accent); outline-offset: 1px; }
.ti-ghost img { display: block; pointer-events: none; }
.ti-pageseg { display: flex; margin-top: var(--sp-1); }
.ti-pages { width: 100%; box-sizing: border-box; }

/* =========================================================
 *  添加页码 · 大图 1:1 演示（弹窗里不再放任何预览窗口）
 *  真正的落点由 window.PageOverlay 画在右侧大预览的当前页上；
 *  下面这些规则只服务于一个不可见的「几何数据出口」，供端到端
 *  测试按像素量数，界面上不会出现第二张纸。
 * ========================================================= */
#numModal .modal-box { width: var(--mw-l); }
.nm-prev { display: flex; gap: var(--sp-5); align-items: stretch; }
.nm-stage {
  flex: 0 0 auto; width: 300px; height: 240px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-stage); border: 1px solid var(--bd);
  border-radius: var(--r-block); overflow: hidden;
}
.nm-sheet { position: relative; background: #fff; box-shadow: 0 2px 12px rgba(0, 0, 0, .45); }
.nm-guide {
  position: absolute; pointer-events: none;
  border: 1px dashed rgba(91, 140, 255, .55);
}
/* 显隐一律走内联 display（默认 none 写在 HTML 标签上）。
   这里千万不要再写 display:none —— 否则 JS 把内联清成 '' 之后又回到 none，
   页面上什么都没有，而所有逻辑断言都还是绿的，极难查。 */
.nm-main, .nm-code {
  position: absolute; left: 0; top: 0; pointer-events: none;
}
/* 页码通常只有 8~12pt，落在等比缩小的纸样上不到 2px 宽 —— 等比就是这么大，
   不是画错了。给一个最小可见尺寸，至少让人一眼能找到它贴在哪儿；
   位置（左上角）不受影响，只在极小的时候横向放宽到 3px。 */
.nm-main { min-width: 3px; min-height: 2px; }
.nm-code { min-width: 3px; min-height: 3px; }
.nm-cells {
  position: absolute; inset: 0; display: grid;
  grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr);
}
.nm-cells i { cursor: pointer; }
.nm-cells i:hover { background: rgba(91, 140, 255, .1); box-shadow: inset 0 0 0 1px rgba(91, 140, 255, .55); }
/* 当前选中的格只用淡描边标出来。早先给了 .2 的填充色，结果一整格盖在页码上，
   10pt 的字本来就小，直接看不见了 —— 高亮不能比它要指出的东西更醒目。 */
.nm-cells i.on { background: rgba(91, 140, 255, .06); box-shadow: inset 0 0 0 1px rgba(91, 140, 255, .7); }
.nm-side { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.nm-prevseg { display: flex; }
.nm-prevseg[hidden] { display: none; }
#nmPrevNote.bad { color: var(--orange); }

/* 操作范围：五个单选排成一行（v1.0.15）。原先 grid 3 列会把「当前页 / 奇数页」折到第二行 */
.pl-range {
  /* v1.0.94（弹窗 640→480）：五个「所有页 / 页面范围 / 偶数页 / 当前页 / 奇数页」
     原来强制 nowrap，右栏收窄到 265px 后横向溢出 80px。
     改成允许整组换行（单个 label 仍不换行、不压缩），选项多时自然排成两行。 */
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  margin-bottom: 0; justify-content: flex-start; align-items: center;
}
.pl-range label {
  display: flex; align-items: center; gap: 7px;
  flex: 0 0 auto; white-space: nowrap;   /* 不许压缩、不许文字内换行，否则会挤成两行 */
  font-size: 11.5px; color: var(--tx-2); cursor: pointer;
}
.pl-range input[type=radio] {
  appearance: none; -webkit-appearance: none;
  width: 14px; height: 14px; margin: 0; border-radius: 50%;
  border: 1.5px solid var(--bd-strong); background: rgba(255, 255, 255, .05);
  position: relative; cursor: pointer; flex: 0 0 auto;
}
.pl-range input[type=radio]:hover { border-color: var(--accent); }
.pl-range input[type=radio]:checked { background: var(--accent); border-color: var(--accent); }
.pl-range input[type=radio]:checked::after {
  content: ''; position: absolute; inset: 3.5px; border-radius: 50%; background: #fff;
}
.pl-pages { flex: 1 1 auto; min-width: 200px; }
.pl-pages:disabled { opacity: .4; cursor: not-allowed; }
.pl-read { margin-left: 4px; }
#plResultList:empty { display: none; }

/* ============================================================
   拆分页面
   ============================================================ */
/* 行列 / 尺寸 / 骑马钉三组参数都带预览 → L 档（原 646px） */
.psp-modal .modal-box { width: var(--mw-l); }
/* v1.0.94（弹窗 640→480）：「行列 / 叠放 / 尺寸」两组参数行各要 445~455px，
   480 档只有 428px 可用。数字框从 78 收到 66（右侧已关掉微调箭头，
   「210.0」这种 5 字符在 66px 里排得下），两个参数行都不用折。 */
.psp-modal input[type=number].inp { width: 66px; }
.psp-modal input[type=number].inp::-webkit-inner-spin-button,
.psp-modal input[type=number].inp::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.psp-modal input[type=number].inp { padding-right: 8px; }

/* 两个参数面板常驻显示：不可用时整块变灰，一眼看出「这块不生效」 */
.psp-pane {
  margin: 0 0 10px;
  padding: 9px 12px 8px;
  border: 1px solid var(--bd-soft);
  border-radius: 9px;
  background: rgba(255, 255, 255, .022);
  min-width: 0;
  transition: opacity var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.psp-pane legend {
  padding: 0 6px; margin-left: -2px;
  font-size: 11.5px; font-weight: 500; color: var(--tx-2);
  letter-spacing: .2px;
}
.psp-pane .field-line { margin-top: 2px; }
.psp-pane .form-note { margin-top: 7px; margin-bottom: 0; }
.psp-pane.off { opacity: .44; border-color: var(--bd-soft); }
.psp-pane:disabled { cursor: not-allowed; }
.psp-pane:disabled input { cursor: not-allowed; }

.psp-saddle {
  display: none;
  margin: 0 0 10px;
  padding: 10px 12px;
  font-size: 11.5px; line-height: 1.7; color: var(--tx-2);
  border: 1px solid var(--accent-line);
  border-radius: 9px;
  background: var(--accent-soft);
}
.psp-saddle.on { display: block; }
.psp-saddle b { color: var(--accent-2); font-weight: 600; }

.psp-pages { flex: 1 1 auto; min-width: 200px; }
.psp-pages:disabled { opacity: .4; cursor: not-allowed; }
.pl-range.off { opacity: .34; pointer-events: none; }

.psp-preview {
  min-height: 15px; margin: 10px 0 2px;
  font-size: var(--fs-note); line-height: 1.6;
  color: var(--accent-2); font-family: var(--mono); letter-spacing: .1px;
}
.psp-preview:empty { display: none; }
#pspResultList:empty { display: none; }
#pspModeNote:empty { display: none; }
#pspModeNote:empty { display: none; }

/* ---- 解绑 / 换绑弹窗 ---- */
/* 单列表单 + 一张二维码 → S 档（原 460px） */
.lic-swap-modal .modal-box { width: var(--mw-s); }
.lic-swap-p { margin: 0 0 8px; font-size: 12px; line-height: 1.7; color: var(--tx-2); }
.lic-swap-warn { margin: 0 0 8px; font-size: 12px; line-height: 1.7; color: var(--orange); }
.lic-swap-when { margin: 0; font-size: 11px; line-height: 1.6; color: var(--tx-3); }
.lic-swap-qr {
  width: 168px; height: 168px; margin: 10px auto 8px;
  display: flex; align-items: center; justify-content: center; padding: 8px; box-sizing: border-box;
  background: #fff; border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4);
}
.lic-swap-qr img { width: 100%; height: 100%; display: block; border-radius: 6px; }
.lic-swap-qr .lic-qr-fail { color: #666; }
.lic-swap-fee { margin: 0; text-align: center; font-size: 11.5px; color: var(--tx-3); }
.lic-swap-fee b { color: var(--accent-2); font-size: 14px; margin: 0 2px; }

/* ---- 加机弹窗 ---- */
.lic-add-modal .modal-box { width: var(--mw-s); }
.lic-add-lead {
  margin: 0 0 10px; font-size: 12px; line-height: 1.7; color: var(--tx-2);
  padding: 8px 11px; border-radius: 8px;
  background: rgba(255,255,255,.035); border: 1px solid var(--bd);
}
.lic-add-lead b { color: var(--accent-2); }
.lic-add-note { margin: 8px 0 10px; font-size: 11px; line-height: 1.7; color: var(--tx-3); }
.lic-add-req { display: flex; align-items: flex-start; gap: 8px; }
.lic-add-req code {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--mono, ui-monospace, Consolas, monospace);
  font-size: 10.5px; line-height: 1.6; color: var(--tx-2);
  background: rgba(255,255,255,.04); border: 1px solid var(--bd);
  border-radius: 7px; padding: 7px 9px;
  /* 机器码是 latin 词，别从中间劈开：优先在「｜」和空格处换行；
     申请文案是两行纯文本（含换行符），保留换行 */
  white-space: pre-line;
  word-break: normal; overflow-wrap: break-word;
}

/* ---- 到期全屏锁 ---- */
.lic-gate {
  position: fixed; inset: 0; z-index: 9000;
  display: none;
  align-items: center; justify-content: center;
  background: rgba(8, 10, 13, .93);
  backdrop-filter: blur(6px);
  padding: 24px;
}
.lic-gate.on { display: flex; animation: licFade .18s var(--ease); }
@keyframes licFade { from { opacity: 0 } to { opacity: 1 } }

.lic-gate-card {
  width: 430px; max-width: 100%; max-height: 100%;
  overflow-y: auto;
  padding: 26px 24px 22px;
  border: 1px solid var(--bd-strong); border-radius: var(--radius-l);
  background: var(--bg-panel);
  box-shadow: 0 30px 90px rgba(0,0,0,.7);
  text-align: center;
}
.lic-gate-brand {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--accent-2); font-size: 12px; font-weight: 600;
  letter-spacing: .5px; margin-bottom: 16px;
}
.lic-gate-card h2 { margin: 0 0 6px; font-size: 17px; font-weight: 600; color: var(--tx); }
.lic-gate-sub { margin: 0 0 18px; font-size: 11.5px; color: var(--tx-3); line-height: 1.65; }
.lic-gate-machine {
  display: flex; align-items: center; gap: 7px;
  margin-bottom: 10px;
  font-size: 10.5px; color: var(--tx-3);
}
.lic-gate-machine code {
  flex: 1 1 auto;
  padding: 5px 8px; border-radius: var(--r-sm);
  background: var(--bg-stage); border: 1px dashed var(--bd-strong);
  color: var(--accent-2); font-size: 11.5px; letter-spacing: 1.1px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  user-select: all;
}
.lic-gate-card .lic-key-input { text-align: left; }
.lic-gate-btn { width: 100%; margin-top: 4px; }
.lic-gate-buy {
  margin-top: 14px;
  width: 100%; height: 30px;
  border: none; background: none;
  color: var(--accent); font-size: 11.5px; cursor: pointer;
  transition: color var(--dur) var(--ease);
}
.lic-gate-buy:hover { color: var(--accent-2); text-decoration: underline; }
.lic-gate-buybox {
  margin-top: 10px; padding-top: 14px;
  border-top: 1px solid var(--bd);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.lic-gate-buybox[hidden] { display: none; }
.lic-gate-wechat { font-size: 11px; color: var(--tx-2); }
.lic-gate-wechat code { color: var(--tx); background: rgba(255,255,255,.07); padding: 1px 6px; border-radius: 4px; }
.lic-gate-tip { font-size: 10px; color: var(--tx-3); }

/* 锁定态：底下的界面不可点、不可选 */
body.lic-locked main,
body.lic-locked .app-header,
body.lic-locked .wfstrip { pointer-events: none; user-select: none; }

/* =========================================================
 *  工作流执行进度弹窗
 *  执行期间置顶（z-index 高于所有功能弹窗），遮罩不可点穿，
 *  避免「跑到一半又去点别的功能」把任务打乱；全部跑完自动关闭。
 * ========================================================= */
.modal.wfprog { z-index: 400; }
.modal.wfprog.on {
  display: flex; align-items: center; justify-content: center;
}
.modal.wfprog .modal-mask {
  background: rgba(6,8,11,.62);
  cursor: default;
}
.modal.wfprog .modal-box {
  margin: 0; width: var(--mw-s);
  animation: wfpIn .16s var(--ease);
}
@keyframes wfpIn { from { opacity: 0; transform: translateY(6px) scale(.985); } to { opacity: 1; transform: none; } }

.wfp-file {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 12px; margin-bottom: 4px;
}
.wfp-file-lb { color: var(--tx-3); flex: 0 0 auto; }
.wfp-file-nm {
  color: var(--tx); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 右侧一小组：第几个文件 + 本文件已用时，统一靠右 */
.wfp-file-rt {
  margin-left: auto; flex: 0 0 auto;
  display: flex; align-items: baseline; gap: 8px;
}
.wfp-file-ix { color: var(--tx-3); flex: 0 0 auto; }
.wfp-file-t {
  color: var(--tx-2); flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}

/* 步骤行：步骤名占满剩余宽度，右侧跟当前步已用时 */
.wfp-steprow {
  display: flex; align-items: baseline; gap: 10px;
  margin-bottom: 10px;
}
.wfp-step {
  flex: 1 1 auto; min-width: 0;
  font-size: 12.5px; color: var(--accent-2);
  min-height: 18px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wfp-step-t {
  flex: 0 0 auto; font-size: 11.5px; color: var(--tx-2);
  font-variant-numeric: tabular-nums;
}

.wfp-bar {
  height: 6px; border-radius: 3px;
  background: rgba(255,255,255,.07);
  overflow: hidden;
}
.wfp-bar > i {
  display: block; height: 100%; width: 0;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width .22s var(--ease);
}

.wfp-meta {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 11.5px; color: var(--tx-2);
  margin-top: 7px;
}
/* 累计计时器：等宽数字，避免每秒跳动时左右抖动 */
.wfp-clock {
  display: inline-flex; align-items: baseline; gap: 5px;
  color: var(--tx-3);
}
.wfp-clock > i { font-style: normal; font-size: 11px; }
.wfp-clock > b {
  color: var(--tx); font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 46px; text-align: right;
}

.wfp-stat {
  display: flex; gap: 14px;
  font-size: 11.5px; color: var(--tx-3);
  margin-top: 8px;
}
.wfp-stat b { font-weight: 600; }
.wfp-ok b { color: var(--green); }
.wfp-fail b { color: var(--red); }
.wfp-skip b { color: var(--red); }

/* 步骤去向清单：每一步都要有一行，完成绿、跳过 / 失败红。
   以前跳过与未执行的步骤在日志里根本不存在，事后无法核对，这里是补上的证据面。 */
.wfp-steps {
  margin-top: 9px; padding-top: 8px;
  border-top: 1px solid var(--bd-soft);
  max-height: 172px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
}
.wfp-steps[hidden] { display: none; }
.wfp-st {
  display: flex; align-items: baseline; gap: 8px;
  padding: 2px 2px; border-radius: var(--r-sm);
  font-size: 11.5px; line-height: 1.5;
}
.wfp-st-no {
  flex: 0 0 auto; width: 15px; text-align: right;
  color: var(--tx-3); font-variant-numeric: tabular-nums;
}
.wfp-st-nm {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--tx-2);
}
.wfp-st-tag { flex: 0 0 auto; font-size: 10.5px; color: var(--tx-3); }

/* 完成 → 绿 */
.wfp-st.ok .wfp-st-nm,
.wfp-st.ok .wfp-st-tag { color: var(--green); }
/* 跳过 / 失败 → 红（跳过不是错，但必须一眼看见它没执行） */
.wfp-st.skip .wfp-st-nm,
.wfp-st.skip .wfp-st-tag,
.wfp-st.fail .wfp-st-nm,
.wfp-st.fail .wfp-st-tag { color: var(--red); }
/* 正在执行 → 强调色；还没轮到 → 灰 */
.wfp-st.run .wfp-st-nm { color: var(--accent-2); }
.wfp-st.run .wfp-st-tag { color: var(--accent-2); }

.wfp-tip {
  margin-top: 10px; padding-top: 9px;
  border-top: 1px dashed var(--bd-soft);
  font-size: 11px; color: var(--tx-3);
}

/* 取消中的状态：按钮变灰、文案改为「正在停止」 */
.modal.wfprog .btn.ghost.stopping {
  color: var(--orange); border-color: rgba(242,163,60,.45);
  background: var(--orange-soft); cursor: default;
}

/* =========================================================
 *  导出工作流：多选小窗
 *  勾 1 条 = 单个 .wf.json；勾多条 = 一个 .wfpack.json 合集包。
 *  层级 260：压在工作流面板（120）之上，低于执行进度窗（400）。
 * ========================================================= */
.modal.wfexp { z-index: 260; }
.modal.wfexp.on {
  display: flex; align-items: center; justify-content: center;
}
.modal.wfexp .modal-mask { background: rgba(6,8,11,.48); }
.modal.wfexp .modal-box {
  margin: 0; width: var(--mw-s);
  animation: wfpIn .16s var(--ease);
}

.wfexp-bar {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: 9px var(--mpad-x) 5px;
}
.wfexp-all {
  height: 23px; padding: 0 9px;
  border: 1px solid var(--bd); border-radius: 6px;
  background: var(--bg-elev); color: var(--tx-2);
  font-size: 11.5px; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.wfexp-all:hover {
  background: var(--bg-hover); color: var(--tx); border-color: var(--bd-strong);
}
.wfexp-cnt { font-size: 11.5px; color: var(--tx-3); margin-left: auto; }

.wfexp-list {
  flex: 1 1 auto; min-height: 84px;
  max-height: 46vh; overflow-y: auto; overflow-x: hidden;
  padding: 3px 8px 8px;
}
.wfexp-row {
  display: flex; align-items: center; gap: 8px;
  height: 30px; padding: 0 8px;
  border: 1px solid transparent; border-radius: 7px;
  cursor: pointer; font-size: 12.5px; color: var(--tx);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.wfexp-row:hover { background: var(--bg-hover); }
.wfexp-row input {
  flex: 0 0 auto; width: 13px; height: 13px; margin: 0;
  accent-color: var(--accent); cursor: pointer;
}
.wfexp-nm {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wfexp-meta { flex: 0 0 auto; font-size: 11px; color: var(--tx-3); }
.wfexp-tag {
  flex: 0 0 auto;
  font-size: 10px; line-height: 15px; height: 15px; padding: 0 5px;
  border-radius: 4px;
  color: var(--accent-2); background: rgba(91,140,255,.13);
  border: 1px solid rgba(91,140,255,.24);
}
.wfexp-tag.dim {
  color: var(--tx-3); background: rgba(255,255,255,.05);
  border-color: var(--bd-soft);
}

.wfexp-tip {
  flex: 0 0 auto;
  padding: 0 var(--mpad-x) 10px;
  font-size: 11px; color: var(--tx-3);
}

.modal.wfexp .btn[disabled] { opacity: .45; cursor: not-allowed; }

/* ============================================================
 *  顶部眉头菜单栏（文件 / 编辑 / 视图 / 帮助）
 * ============================================================ */
.menubar {
  display: flex; align-items: center; gap: 2px;
  flex: 0 0 auto; margin-left: 14px;
}
.menu { position: relative; }
.menu-cap {
  height: 30px; padding: 0 12px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--tx-2);
  font-size: 12.5px; font-weight: 500; letter-spacing: .3px;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.menu-cap:hover, .menu.open .menu-cap {
  background: var(--bg-hover); color: var(--tx); border-color: var(--bd-strong);
}
.menu-pop {
  position: absolute; top: calc(100% + 6px); left: 0;
  min-width: 172px; padding: 6px;
  background: var(--bg-elev);
  border: 1px solid var(--bd-strong); border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.05);
  display: none; z-index: 60;
}
.menu.open .menu-pop { display: block; }
.menu-pop .mi {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px;
  background: transparent; border: none; border-radius: 7px;
  color: var(--tx-2); font-size: 12.5px; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.menu-pop .mi:hover { background: var(--accent-soft); color: var(--tx); }

/* 文件列表头部的「自动处理」按钮（蓝色实心，代替原来的「保存」）
   为什么做成实心高亮：它是常驻的监听开关，不是一次性动作，而原先躲在第二行工具条里
   用户找了半天没找到（2026-11-05）。绿色呼吸点是「正在监听」的唯一外部指示 ——
   弹窗关掉以后也得一眼看出后台还在不在跑。 */
.panel-head .mini-btn.autowatch {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0 9px 0 8px;
  background: linear-gradient(180deg, #6295ff, #4b7ef7);
  color: #fff; border-color: rgba(255,255,255,.18); font-weight: 600;
  box-shadow: 0 2px 8px rgba(91,140,255,.28), inset 0 1px 0 rgba(255,255,255,.2);
}
.panel-head .mini-btn.autowatch:hover {
  background: linear-gradient(180deg, #74a1ff, #5a8af7);
  color: #fff; border-color: rgba(255,255,255,.26);
}
.panel-head .mini-btn.autowatch svg { opacity: .95; }
/* 指示灯：未监听时是暗白点，监听中变绿并向外扩一圈（不改变按钮底色，
   所以 .panel-head .mini-btn.on 的浅蓝底不会盖掉这颗绿点） */
.panel-head .mini-btn.autowatch .aw-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(255,255,255,.5); flex: 0 0 auto;
  transition: background var(--dur) var(--ease);
}
.panel-head .mini-btn.autowatch.on {
  background: linear-gradient(180deg, #6f9dff, #4a7ef5);
  border-color: rgba(255,255,255,.26);
}
.panel-head .mini-btn.autowatch.on .aw-dot {
  background: #62e6a1;
  animation: awPulse 1.9s ease-out infinite;
}
@keyframes awPulse {
  0%   { box-shadow: 0 0 0 0 rgba(98,230,161,.65); }
  70%  { box-shadow: 0 0 0 5px rgba(98,230,161,0); }
  100% { box-shadow: 0 0 0 0 rgba(98,230,161,0); }
}

/* ============================================================
 *  右侧动作栏：工作流 + 所有动作（三栏布局的右栏，约 2 份）
 * ============================================================ */
/* 三栏布局右栏：原 22%，按用户要求收窄约 3/5（保留可用最小宽度），
   释放出的空间由中间预览区（flex:1）自动吸收，预览区更宽、翻页更舒服 */
.action-panel {
  /* v1.0.21：动作列表改双列后需要的最小宽度 = 两列 × 5 个中文字 + 图标
     （单列时代是 200px；只加宽 24px 就把可视动作数翻了一倍） */
  flex: 0 0 10%;
  min-width: 224px; max-width: 28%;
  display: flex; flex-direction: column;
  background: var(--bg-panel);
  border-left: 1px solid var(--bd);
  min-height: 0; overflow: hidden;
}
.ap-sec { display: flex; flex-direction: column; min-height: 0; }
.ap-wf { flex: 0 0 auto; border-bottom: 1px solid var(--bd); }
.ap-actions { flex: 1 1 auto; min-height: 0; }

.ap-sec-head {
  display: flex; align-items: center; gap: 8px;
  height: 38px; flex: 0 0 38px; padding: 0 12px;
  border-bottom: 1px solid var(--bd-soft);
  font-size: 12px; font-weight: 600; color: var(--tx-2); letter-spacing: .6px;
}
.ap-sec-head > span {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
}
.ap-sec-head > span::before {
  content: ''; width: 3px; height: 13px; border-radius: 2px;
  background: linear-gradient(180deg, var(--accent), rgba(91,140,255,.35));
}

/* 工作流头部右侧「＋ 新增」：只在新建流程时用，不放进列表 */
.wfstrip-add-head {
  margin-left: auto; flex: 0 0 auto;
  height: 24px; padding: 0 10px;
  display: inline-flex; align-items: center; gap: 3px;
  background: var(--accent-soft, rgba(91,140,255,.12));
  color: var(--accent, #5b8cff);
  border: 1px solid var(--accent-line, rgba(91,140,255,.45));
  border-radius: 7px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .3px;
  cursor: pointer; transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.wfstrip-add-head:hover { background: var(--accent, #5b8cff); color: #fff; border-color: var(--accent, #5b8cff); }
.wfstrip-add-head:active { transform: translateY(1px); }

/* 搜索框 */
.ap-search {
  flex: 1 1 auto; min-width: 0;
  height: var(--h-ctl); padding: 0 10px;
  background: var(--bg); color: var(--tx);
  border: 1px solid var(--bd); border-radius: var(--r-ctl);
  font-size: var(--fs-body); outline: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.ap-search:focus { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft); }
.ap-search::placeholder { color: var(--tx-3); }

/* 工作流容器（复用 .wfstrip-* 类），窄栏里改为纵向可换行 */
.ap-wf-body { position: relative; padding: 8px 10px 10px; }
.ap-wf-body .wfstrip-list {
  flex-wrap: wrap; overflow-x: visible; overflow-y: auto;
  max-height: 136px; padding: 2px; gap: 6px;
}
.ap-wf-body .wfstrip-list::-webkit-scrollbar { display: block; width: 6px; }
.ap-wf-body .wfstrip-manage { margin-top: 8px; width: 100%; justify-content: center; }

/* 所有动作列表：两列并排（一屏看到两倍动作名，减少滚动）
   用 grid 而不是两条 flex 列：分组标题可以 grid-column:1/-1 横跨整行，
   动作顺序仍然是从上到下、从左到右的自然阅读顺序（不用额外算奇偶） */
.ap-list {
  flex: 1 1 auto; min-height: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-content: start;
  gap: var(--sp-1) 6px;
  padding: var(--sp-2) 8px 14px; overflow-y: auto;
}
.ap-list .hbtn {
  width: 100%; height: 30px; min-width: 0;
  justify-content: flex-start; padding: 0 7px; gap: 5px;
  border: 1px solid var(--bd); background: var(--bg-elev); border-radius: 8px;
}
.ap-list .hbtn:hover { background: var(--accent-soft); border-color: var(--accent-line); color: var(--tx); }
/* 图标保持 16px（与工具条、UI 规范一致）：双列后每格仍有约 100px，
   5 个中文字 + 16px 图标刚好排得下，无需缩小图标换空间 */
.ap-list .hbtn svg { width: 16px; height: 16px; }
/* 名称超长时省略，不换行、不撑破格子（完整名称在 title 属性里，悬停可看） */
.ap-list .hbtn span {
  font-size: var(--fs-body);
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
/* 动作分组：横跨两列，否则会被挤进某一个格子里 */
.ap-sep {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: var(--sp-2);
  margin: var(--sp-2) 0 0;
  padding: 0 2px;
  font-size: var(--fs-note); font-weight: 600; letter-spacing: 1px;
  color: var(--tx-3); white-space: nowrap;
}
.ap-sep::after {
  content: ''; flex: 1 1 auto; height: 1px; background: var(--bd);
}
.ap-list .ap-sep:first-child { margin-top: 0; }

.ap-empty { padding: 10px 12px; font-size: 12px; color: var(--tx-3); }

/* ============================================================
 *  使用说明弹窗（帮助 → 使用说明）
 * ============================================================ */
.modal-box.help-box { width: var(--mw-m); }
.help-intro {
  font-size: 12px; line-height: 1.7; color: var(--tx-2);
  padding: 9px 11px; margin-bottom: 12px;
  background: rgba(91,140,255,.07); border: 1px solid rgba(91,140,255,.2);
  border-radius: 9px;
}
.help-intro strong { color: var(--accent-2); font-weight: 600; }
.help-group { margin-bottom: 14px; }
.help-ghead {
  font-size: 11.5px; font-weight: 600; letter-spacing: .6px; color: var(--accent);
  margin-bottom: 7px; padding-left: 9px; position: relative;
}
.help-ghead::before {
  content: ''; position: absolute; left: 0; top: 3px; bottom: 3px;
  width: 3px; border-radius: 2px; background: linear-gradient(180deg, var(--accent), rgba(91,140,255,.35));
}
.help-item {
  padding: 8px 11px; margin-bottom: 6px;
  border: 1px solid var(--bd-soft); border-radius: 9px;
  background: rgba(255,255,255,.018);
}
.help-name { font-size: 12.5px; font-weight: 600; color: var(--tx); margin-bottom: 3px; }
.help-desc { font-size: 11.5px; line-height: 1.66; color: var(--tx-3); }

/* ============================================================
   图像修复 / 增强 · 参数提示 + 实时预览
   ============================================================ */
.form-note.hint { color: var(--accent-2); opacity: .85; }
.form-note.hint:empty { display: none; }
span.fl-unit.hint { white-space: normal; line-height: 1.5; opacity: .9; }

.en-pv {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 7px;
}
.en-pv-cell { min-width: 0; }
.en-pv-frame {
  display: flex; align-items: center; justify-content: center;
  padding: 4px; min-height: 70px;
  border: 1px solid var(--bd); border-radius: 8px;
  background:
    repeating-conic-gradient(var(--bg-elev) 0% 25%, var(--bg-panel) 0% 50%) 50% / 12px 12px;
  overflow: hidden;
}
.en-pv-frame canvas {
  display: block;
  width: 100%; height: auto; max-height: 220px;
  object-fit: contain;
  border-radius: 4px;
  background: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .34);
}
.en-pv-cap {
  margin-top: var(--sp-1);
  font-size: var(--fs-note); letter-spacing: .2px;
  color: var(--tx-3); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 二值模式下清晰化置灰（值仍保留，切回灰度/彩色自动恢复） */
#enSharp:disabled { opacity: .4; cursor: not-allowed; }
#enBnK:disabled { opacity: .4; cursor: not-allowed; }

/* ============================================================
   页面裁切 · 裁切位置预览（红色虚线框标出保留范围）
   ============================================================ */
.cr-pv { width: 100%; min-width: 0; }
.cr-pv-stage {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 6px; min-height: 110px;
  border: 1px solid var(--bd); border-radius: 8px;
  background:
    repeating-conic-gradient(var(--bg-elev) 0% 25%, var(--bg-panel) 0% 50%) 50% / 12px 12px;
  overflow: hidden;
}
.cr-pv-stage canvas {
  display: block; max-width: 100%; max-height: 220px;
  background: #fff;
  box-shadow: 0 1px 6px rgba(0, 0, 0, .38);
}
.cr-pv-stage canvas[hidden] { display: none; }
.cr-pv-empty {
  padding: 28px 10px;
  font-size: 11.5px; letter-spacing: .2px; color: var(--tx-3);
}
.cr-pv-empty[hidden] { display: none; }
.cr-pv-cap {
  margin-top: var(--sp-2); min-height: 15px;
  font-size: var(--fs-note); line-height: 1.6; letter-spacing: .1px;
  color: var(--accent-2); font-family: var(--mono);
}
.cr-pv-cap:empty { display: none; }
/* 四边分别裁切：上/下/左/右 四个小输入框固定排一行（窄框 + 隐藏步进按钮，杜绝折行） */
.cr-sides { flex-wrap: nowrap; gap: 6px; }
.cr-sides .fl-lb { font-size: 11px; }
.cr-sides .inp.num.cr-num {
  flex: 0 1 54px; width: 54px; min-width: 0;
  height: var(--h-ctl-xs); padding: 0 5px; font-size: var(--fs-label);
  text-align: center;
}
.cr-sides input[type=number].inp::-webkit-inner-spin-button,
.cr-sides input[type=number].inp::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0;
}
.cr-sides .fl-unit { margin-left: 1px; }

/* ============================================================
   封面拼版 · 鼠标取色（自适应舞台 + 顶部实时色值）
   ============================================================ */
.cv-pick-head {
  display: flex; align-items: center; gap: 7px;
  margin: 6px 0 6px; min-height: 18px;
  font-size: 11.5px; line-height: 1.45;
}
.cv-pick-sw {
  flex: 0 0 auto; width: 15px; height: 15px; border-radius: 4px;
  border: 1px solid var(--bd-strong); background: transparent;
  transition: background var(--dur) var(--ease);
}
.cv-pick-val {
  color: var(--accent-2);
  font-size: var(--fs-note); letter-spacing: .1px; font-family: var(--mono);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cv-pick-stage {
  display: flex; align-items: center; justify-content: center;
  padding: 5px; max-height: 246px;
  border: 1px solid var(--bd); border-radius: 8px;
  background: var(--bg-elev);
  overflow: hidden;
}
.cv-pick-stage canvas {
  display: block; max-width: 100%; max-height: 236px;
  background: #fff; border-radius: 4px; cursor: crosshair;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .34);
}
/* 取色十字线（跟随鼠标，纯 DOM 叠加，不重绘画布） */
.cv-pick-stage { position: relative; }
.cv-pick-cross { position: absolute; inset: 0; display: none; pointer-events: none; }
.cv-pick-cross.on { display: block; }
.cv-pick-cross i,
.cv-pick-cross b {
  position: absolute; display: block;
  background: rgba(255, 255, 255, .82);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .45), 0 0 3px rgba(0, 0, 0, .7);
}
.cv-pick-cross i { top: 0; bottom: 0; width: 1px; }
.cv-pick-cross b { left: 0; right: 0; height: 1px; }

/* ============================================================
   删边界线 · 「原样 / 删线后」双预览
   宽度一律按容器自适应（网格等分），禁止写死像素宽高 ——
   原先两个 canvas 各写死 320px，弹窗只有 478/640 宽时必然折成一上一下。
   ============================================================ */
.db-pv {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--sp-4); align-items: start;
  margin-bottom: var(--sp-2);
}
.db-pv-cell { min-width: 0; text-align: center; }
.db-pv-cell canvas {
  display: block; margin: 0 auto;
  /* 两个上限同时生效时浏览器会等比缩放（canvas 等同图片），不会拉变形 */
  max-width: 100%; max-height: 240px;
  width: auto; height: auto;
  border: 1px solid var(--bd); border-radius: 4px; background: #fff;
}
.db-pv-cap {
  margin-top: var(--sp-1);
  font-size: var(--fs-note); color: var(--tx-3); letter-spacing: .2px;
}
/* 还没点「预览」时 canvas 是 1×1，别把它画成一个点 */
.db-pv-cell canvas[width="1"] { display: none; }

/* 保存成功提示里的「打开文件夹」：路径被省略号截断时，这是唯一的出口 */
.es-open {
  flex: none;
  height: 20px;
  margin-left: 8px;
  padding: 0 8px;
  border-radius: 5px;
  border: 1px solid var(--bd2, rgba(255,255,255,.16));
  background: rgba(255,255,255,.06);
  color: inherit;
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
}
.es-open:hover { background: rgba(255,255,255,.12); }

/* ============================================================
 *  文件列表 · 列表级操作条（紧贴表头上方）
 *  只放「对整张列表生效」的动作（目前是添加序号），不混暂存操作 ——
 *  暂存条（.list-tools）是有修改时才出现的橙色条，两者语义不同。
 * ============================================================ */
.list-ops {
  display: flex; align-items: center; gap: var(--sp-2);
  /* 左栏可以被 splitter 拖到很窄：四个按钮允许整体折成两行，
     比横向溢出裁掉最后那个按钮好（裁掉的那个永远点不到） */
  flex-wrap: wrap; row-gap: 4px;
  padding: 7px 12px 6px;
  border-bottom: 1px solid var(--bd-soft);
}
/* 「添加序号」与三个保存按钮之间的分组竖线：告诉眼睛这是两组不同的操作 */
.list-ops .lo-sep {
  flex: 0 0 auto; width: 1px; height: 13px;
  background: var(--bd); margin: 0 1px;
}
/* 三种保存方式：直接点就执行，所以做得比普通 mini-btn 更「实」一点 */
.list-ops .mini-btn.sv-quick { padding: 0 8px; color: var(--tx); }
.list-ops .mini-btn.sv-quick:hover { border-color: var(--accent); color: var(--accent); }
.list-ops .mini-btn.sv-quick:disabled:hover { border-color: var(--bd); color: var(--tx-2); }
/* 保存进行中：按钮变「忙碌」态（禁用由 JS 同步，这里只给视觉反馈） */
.list-ops .mini-btn.sv-quick.busy { cursor: progress; opacity: .6; }
.list-ops .mini-btn {
  height: var(--h-ctl-xs); padding: 0 9px;
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-note); font-weight: 500;
  color: var(--tx-2); background: var(--bg-elev);
  border: 1px solid var(--bd); border-radius: var(--r-ctl);
  cursor: pointer; white-space: nowrap; flex: 0 0 auto;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.list-ops .mini-btn:hover { color: var(--tx); background: var(--bg-hover); border-color: var(--bd-strong); }
.list-ops .mini-btn:disabled { opacity: .42; cursor: default; }
.list-ops .mini-btn:disabled:hover { color: var(--tx-2); background: var(--bg-elev); border-color: var(--bd); }
.list-ops .mini-btn svg { opacity: .75; }
.list-ops .mini-btn:hover svg { opacity: 1; }
/* 拖拽排序的唯一可见提示：不给这句话，没人知道列表能拖 */
.lo-hint {
  margin-left: auto; min-width: 0;
  font-size: 10.5px; color: var(--tx-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 拖动中的行：半透明 + 光标变抓取，明确「这是被拎起来的那个」 */
.file-row.dragging {
  opacity: .42;
  cursor: grabbing;
}
.file-list.dragging-rows, .file-list.dragging-rows .file-row { cursor: grabbing; }
/* 插入位置指示线：用伪元素画在原行的上沿 / 下沿，不新增 DOM 也不挤动布局 */
.file-row.drop-before::after,
.file-row.drop-after::after {
  content: ''; position: absolute; left: 0; right: 0; height: 2px;
  background: var(--accent); border-radius: 1px; z-index: 6;
  box-shadow: 0 0 7px rgba(91,140,255,.75);
  pointer-events: none;
}
.file-row.drop-before::after { top: -1px; }
.file-row.drop-after::after { bottom: -1px; }

/* ============================================================
 *  添加序号弹窗
 * ============================================================ */
#addseqModal .modal-box { width: var(--mw-s); }
/* 四个参数标签字数不同（起始序号 4 字 / 序号数字递增数值 9 字），
   统一最低宽度让输入框列对齐，否则看起来像四行错位的表 */
#addseqModal .fl-lb { min-width: 108px; }

.as-prev {
  border: 1px solid var(--bd); border-radius: var(--r-ctl);
  background: var(--bg);
  overflow: hidden;
}
.as-prev-row {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px;
  font-size: 11.5px; color: var(--tx-2);
}
.as-prev-row + .as-prev-row { border-top: 1px solid var(--bd-soft); }
/* 前缀单独上色 + 浅底：一眼看出「哪几个字符是新加的」，
   比在左边再列一列序号更直观（那样要来回对照）。 */
.as-prev-pfx {
  font-style: normal; font-weight: 600;
  color: #8fb4ff;
  background: rgba(91,140,255,.14);
  border-radius: 3px;
  padding: 0 2px;
}
.as-prev-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: "Cascadia Mono", Consolas, monospace;
  color: var(--tx);
}
.as-prev-more {
  padding: 4px 10px 5px 10px;
  font-size: 10.5px; color: var(--tx-3);
  border-top: 1px dashed var(--bd);
  background: rgba(255,255,255,.015);
}
.as-prev-empty { padding: 11px; font-size: 11px; color: var(--tx-3); text-align: center; }

/* ============================================================
   自动处理（投递目录监听）
   目标：不开面板也知道"在不在跑，跑了几件，哪件失败了"
   ============================================================ */
.auto-box { width: 480px; }

/* 状态条：绿点 = 监听中，灰点 = 暂停 */
.auto-state {
  display: flex; align-items: center; gap: 7px;
  padding: 8px 10px; margin-bottom: 12px;
  background: var(--bg-elev); border: 1px solid var(--bd-soft); border-radius: 7px;
}
.auto-state .au-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--tx-3); transition: background .2s, box-shadow .2s;
}
.auto-state.on .au-dot { background: var(--green); box-shadow: 0 0 0 3px var(--green-soft); }
.auto-state b { font-size: 12.5px; font-weight: 600; color: var(--tx-2); }
.auto-state.on b { color: var(--green); }
.auto-state .au-sub {
  margin-left: auto; font-size: 10.5px; color: var(--tx-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 62%;
}

/* 队列计数：四个数一行，等宽，别让位数变化时左右跳 */
.auto-nums { display: flex; gap: 14px; font-size: 11px; color: var(--tx-3); }
.auto-nums b {
  font-weight: 600; color: var(--tx); font-variant-numeric: tabular-nums;
  margin-right: 3px; font-size: 12px;
}

/* 最近记录：等宽小字，最多 6 行，超出自己滚 */
.auto-log {
  margin-top: 6px; max-height: 116px; overflow: auto;
  padding: 6px 8px; border-radius: 6px;
  background: var(--bg-stage); border: 1px solid var(--bd-soft);
  font: 11px/1.75 Consolas, "Courier New", monospace;
  color: var(--tx-3); white-space: pre-wrap; word-break: break-all;
}
.auto-log:empty::before { content: '还没有处理记录'; color: var(--tx-3); opacity: .7; }
.auto-log .l-ok { color: var(--green); }
.auto-log .l-err { color: var(--red); }
.auto-log .l-warn { color: var(--orange); }

/* ---------------------------------------------------------
 *  队列清单（#auQueue）· 待确认提示（#auNotice）· 内页档案（#auMemo）
 *
 *  为什么都在面板里滚、不做成弹窗：
 *    这两件事都只是"扫一眼"（哪件失败了、程序记住了什么）。
 *    弹窗会盖住面板本身，关掉还要重开 —— 项目铁律：别为列表开弹窗。
 *
 *  行排布只用 flex + min-width:0 + ellipsis：
 *    订单名一长串（品牌+工艺+数量+尺寸…）是常态，不写死就会横溢出面板
 *    （_tools/_ui_audit.js 的"横溢"断言盯的就是这个）。
 * --------------------------------------------------------- */

/* 标题栏上的问题数：关到面板之外时唯一能"红一下"的地方 */
.au-badge {
  flex: none; min-width: 17px; height: 17px; padding: 0 5px;
  border-radius: 999px; font-size: 10.5px; line-height: 17px; text-align: center;
  color: #fff; background: var(--red); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.au-badge.is-hold { background: var(--orange); }
.au-badge[hidden] { display: none; }

/* 队列清单：一条作业一行，行高压到 30px 上下，一屏能看四五条 */
.auto-q {
  margin-top: 8px; max-height: 200px; overflow-y: auto; overflow-x: hidden;
  padding: 4px 6px; border-radius: 6px;
  background: var(--bg-stage); border: 1px solid var(--bd-soft);
}
.aq-empty { padding: 7px 2px; font-size: 11px; color: var(--tx-3); text-align: center; }
.aq-row { padding: 4px 2px 5px; border-bottom: 1px dashed var(--bd-soft); }
.aq-row:last-child { border-bottom: 0; padding-bottom: 2px; }
.aq-line { display: flex; align-items: center; gap: 6px; min-width: 0; }
.aq-name {
  flex: 1 1 auto; min-width: 0;
  font-size: 11.5px; color: var(--tx);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 状态胶囊：沿用语义色（排队灰 / 处理中蓝 / 成功绿 / 失败红 / 待确认橙） */
.aq-st {
  flex: none; padding: 0 6px; border-radius: 999px;
  font-size: 10.5px; line-height: 16px; border: 1px solid transparent;
}
.aq-st.s-pend { color: var(--tx-2); background: rgba(255,255,255,.06); border-color: var(--bd); }
.aq-st.s-run { color: var(--accent-2); background: var(--accent-soft); border-color: var(--accent-line); }
.aq-st.s-done { color: var(--green); background: var(--green-soft); border-color: rgba(62,207,142,.30); }
.aq-st.s-fail { color: var(--red); background: var(--red-soft); border-color: rgba(242,106,95,.30); }
.aq-st.s-hold { color: var(--orange); background: var(--orange-soft); border-color: rgba(242,163,60,.32); }
.aq-try { flex: none; font-size: 10.5px; color: var(--tx-3); font-variant-numeric: tabular-nums; }
.aq-ops { flex: none; display: flex; gap: 5px; }
.aq-ops .btn.sm { flex: none; }
/* 原因行：只有失败与待确认才有，写全（宁可两行，也不截断成看不懂的半句） */
.aq-why {
  margin-top: 1px; padding-left: 2px;
  font-size: 10.5px; line-height: 1.5; color: var(--tx-3);
  word-break: break-all;
}
.aq-why.s-fail { color: var(--tx-2); }
.aq-why.s-hold { color: var(--orange); }

/* 提示条：有新失败/新待确认时贴在日志区顶部（不是弹窗，不打断操作） */
.auto-notice { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.auto-notice[hidden] { display: none; }
.an-line {
  padding: 5px 8px; border-radius: 6px;
  font-size: 11px; line-height: 1.5;
  border: 1px solid var(--bd); background: var(--bg-elev); color: var(--tx-2);
  word-break: break-all;
}
.an-line.err { border-color: rgba(242,106,95,.34); background: var(--red-soft); color: var(--red); }
.an-line.warn { border-color: rgba(242,163,60,.32); background: var(--orange-soft); color: var(--orange); }
.an-line.ok { border-color: rgba(62,207,142,.30); background: var(--green-soft); color: var(--green); }
/* "刚发生"的那一条加粗：不闪不跳（车间屏幕上闪动最招人烦），只把字压重一档 */
.an-line.is-new { font-weight: 600; }

/* 内页档案：只读清单，一条一行 */
.auto-memo {
  margin-top: 6px; max-height: 150px; overflow-y: auto; overflow-x: hidden;
  padding: 4px 6px; border-radius: 6px;
  background: var(--bg-stage); border: 1px solid var(--bd-soft);
}
.auto-memo[hidden] { display: none; }
.am-empty { padding: 7px 2px; font-size: 11px; color: var(--tx-3); text-align: center; line-height: 1.6; }
.am-row {
  display: flex; align-items: center; gap: 8px;
  padding: 3px 2px; border-bottom: 1px dashed var(--bd-soft);
  font-size: 11px;
}
.am-row:last-child { border-bottom: 0; }
.am-name {
  flex: 1 1 auto; min-width: 0; color: var(--tx);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.am-pages { flex: none; color: var(--tx-2); font-variant-numeric: tabular-nums; }
.am-left { flex: none; color: var(--tx-3); font-variant-numeric: tabular-nums; }
/* 快过期的（< 2 小时）提一档颜色：封面还没来就快清零了，这时候该提醒 */
.am-left.is-short { color: var(--orange); }

/* ---------------------------------------------------------
 *  自动决策 · 试算结果（#auPlanOut）
 *
 *  这不是"日志"，是一张决策单：判成什么、按什么依据、要动哪几步。
 *  自动处理最招人怕的是"它到底要干什么"，所以这里要能一眼看完再决定开不开。
 * --------------------------------------------------------- */
.plan-out {
  margin-top: 8px; padding: 9px 10px; border-radius: 8px;
  background: var(--bg-stage); border: 1px solid var(--bd-soft);
  font-size: 11.5px; line-height: 1.65;
  /* 决策单最容易变长（书册 5 步、每步一句理由）。
     不限高会把整个弹窗顶高一截，把下面的队列挤出去。 */
  max-height: 268px; overflow-y: auto;
}
.po-wait { color: var(--tx-3); }
.po-err { color: var(--red); }

.po-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.po-head b {
  font-size: 12px; font-weight: 600; color: var(--tx);
  word-break: break-all; flex: 1 1 auto; min-width: 0;
}
.po-verdict {
  padding: 1px 7px; border-radius: 999px; font-size: 11px;
  background: rgba(91,140,255,.16); color: var(--blue, #6b9bff);
  border: 1px solid rgba(91,140,255,.32);
}
.po-conf {
  font-size: 11px; color: var(--green); font-variant-numeric: tabular-nums;
}
.po-conf.low { color: var(--orange); }

.po-facts {
  margin-top: 5px; color: var(--tx-3); font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.po-trail {
  margin-top: 6px; padding: 5px 8px; border-radius: 6px;
  background: rgba(91,140,255,.10); color: var(--tx-2);
  font: 11px/1.7 Consolas, "Courier New", monospace; word-break: break-all;
}

.po-steps { margin: 7px 0 0; padding: 0; list-style: none; }
.po-steps li {
  padding: 3px 0; border-top: 1px dashed var(--bd-soft);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
}
.po-steps li:first-child { border-top: none; }
.po-steps b { font-weight: 600; color: var(--tx-2); font-size: 11.5px; }
.po-why { color: var(--tx-3); font-size: 11px; flex: 1 1 100%; padding-left: 2px; }
.po-tag {
  font-style: normal; font-size: 10px; padding: 0 5px; border-radius: 4px;
  background: var(--bg-elev); color: var(--tx-3); border: 1px solid var(--bd-soft);
}

.po-line { margin-top: 6px; color: var(--tx-3); font-size: 11px; }
.po-line b { color: var(--tx-2); margin-right: 5px; }
.po-line.warn { color: var(--orange); }
.po-line.warn b { color: var(--orange); }

.po-hold {
  margin-top: 7px; padding: 6px 8px; border-radius: 6px;
  background: rgba(255,170,60,.10); border: 1px solid rgba(255,170,60,.30);
  color: var(--orange); font-size: 11px;
}

/* 三个目录输入框只读，但要看得出可以点"选择"；min-width:0 才能被 flex 压缩 */
#auRoot, #auDone, #auSrc { color: var(--tx-2); cursor: default; min-width: 0; }

/* 路径 + 选择 / 打开 / 默认 排在同一行。
   以前是"整行给输入框、按钮另起一行右对齐"，理由是长路径能看全；
   但三行目录各占两行按钮 = 多出 100 多 px，弹窗要滚才能看到队列。
   现在改成一行，代价是超长路径（UNC / 共享盘）尾巴会被截掉 ——
   补偿：三个框都挂了 title（悬停看全）、聚焦时视图自动滚到末尾，
   而且末尾那段「落在哪个文件夹」正是最要紧的信息。 */

/* 状态条右侧的计数提一档亮度：原来用三级文字色，在深底上读着吃力 */
.auto-state .au-sub { color: var(--tx-2); }

/* 主进程报错（读不到收件目录 / 监视中断 / 扫描出错）：
   就用这一行文字，换成警告色 + 放宽到 78% —— 不新做弹窗、不加横幅，
   错误消失时 watch.js 会把这个 class 一起去掉，界面自动回到计数文案。 */
.auto-state .au-sub.is-err {
  color: var(--orange);
  max-width: 78%;
}

/* .inp 本身不带宽度，直接当 .form-row 子元素时只按 20 字符算，长路径看不全。
   这两个控件必须撑满整行。 */
#auWf { width: 100%; box-sizing: border-box; }

/* =========================================================
 *  拼版测算（plan.js）
 *
 *  这一块显示的是「一张卡 = 一单」的测算结果。信息密度高，
 *  所以横排成「标签 值」的碎片，靠 flex-wrap 自然折行 ——
 *  不写死列宽，否则 1024 窗口下会横溢（.field-line 那套折行判断
 *  管不到这里，只能自己保证不溢出）。
 * ========================================================= */
#planModal .modal-box { width: var(--mw-m); }
/* v1.0.94（弹窗 560→420）：大版尺寸那一行是「走纸规格下拉 + 宽 × 高 mm + ⇄」，
   下拉原来吃满剩余宽度，收窄后把整行顶到折行。改成可缩、无最小宽度，
   省下的宽度全给它已经是它最小的时候了 —— 其余控件都是固定尺寸，
   这一行必须一次排下（折行会被 _ui_audit 记违规）。 */
#planModal #planSheet { flex: 1 1 0; min-width: 0; }

/* 文件名输入：多行、等宽，方便一眼看出分隔符断在哪 */
.plan-inp {
  width: 100%; min-height: 56px; max-height: 132px; resize: vertical;
  padding: 6px 8px; border-radius: var(--r-sm);
  background: var(--bg-stage); border: 1px solid var(--bd);
  color: var(--tx);
  font: 11.5px/1.65 Consolas, "Courier New", monospace;
  outline: none; transition: border-color var(--dur) var(--ease);
}
.plan-inp:focus { border-color: var(--accent-line); }
.plan-inp::placeholder {
  color: var(--tx-3); font-family: "Microsoft YaHei UI", "Microsoft YaHei", sans-serif;
}

/* 汇总一行：N 份 / 算得出几份 / 合计几张大版 */
.plan-sum {
  margin: 12px 0 6px; font-size: 11px; color: var(--tx-3);
  display: flex; flex-wrap: wrap; gap: 4px 6px;
}
.plan-sum:empty { display: none; }
.plan-sum b { color: var(--tx); font-weight: 600; font-size: 12px; }
.plan-sum b.bad { color: var(--red); }

.plan-list {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 320px; overflow: auto; padding-right: 2px;
}

.plan-card {
  padding: 8px 10px; border-radius: 7px;
  background: var(--bg-elev); border: 1px solid var(--bd-soft);
}
.plan-card.bad { border-color: rgba(242, 106, 95, .30); }

.plan-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.plan-name {
  flex: 1 1 auto; min-width: 0; font-size: 11.5px; color: var(--tx);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.plan-tag {
  flex: none; padding: 0 5px; border-radius: 4px;
  font-size: 10.5px; line-height: 1.7;
  background: var(--bg-active); color: var(--tx-2);
}
.plan-tag.warn { background: var(--orange-soft); color: var(--orange); }
.plan-tag.bad  { background: var(--red-soft);    color: var(--red); }

/* 工艺结论文（散页类 · 数码输出 → 连拼）：这一行是卡片的"标题句" */
.plan-route {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-top: 5px; font-size: 11px; color: var(--tx-3);
}
.plan-route b { font-weight: 600; color: var(--tx-2); font-size: 11px; }
.plan-route b.hl { color: var(--accent-2); }
.plan-route i { font-style: normal; font-size: 10px; color: var(--tx-3); }
.plan-steps {
  margin-top: 3px; font-size: 10.5px; color: var(--tx-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.plan-steps i { font-style: normal; opacity: .6; }

/* 锁线胶装的分贴：单独一行，别和数值挤在一起 —— 它是这一单最要紧的数 */
.plan-sigs {
  display: flex; align-items: baseline; gap: 6px;
  margin-top: 4px; font-size: 11px; color: var(--tx-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.plan-sigs b { font-weight: 600; color: var(--tx); font-variant-numeric: tabular-nums; }
.plan-sigs i { font-style: normal; font-size: 10px; color: var(--tx-3); }

/* 数值横排：标签用三级色、值用一级色，扫读时先看值再看标签 */
.plan-kv { display: flex; flex-wrap: wrap; gap: 2px 14px; margin-top: 5px; }
.plan-kv > span { font-size: 11px; color: var(--tx-3); white-space: nowrap; }
.plan-kv b {
  font-weight: 600; color: var(--tx); font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.plan-kv i { font-style: normal; color: var(--tx-3); }
.plan-kv .hl b { color: var(--accent-2); }

.plan-msg {
  margin-top: 5px; font-size: 10.5px; line-height: 1.6; color: var(--tx-3);
  word-break: break-all;
}
.plan-msg.warn { color: var(--orange); }
.plan-msg.err  { color: var(--red); }

.plan-empty {
  padding: 20px 0; text-align: center;
  font-size: 10.5px; color: var(--tx-3); opacity: .85;
}

/* 一行四个数字框时，spin 按钮会把 78px 挤掉一半，320 都读不全。
   与 pagelocate / pagesplit 一样按弹窗单独关掉，不动全局 .inp.num。 */
#planModal input[type=number].inp::-webkit-inner-spin-button,
#planModal input[type=number].inp::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0;
}
#planModal input[type=number].inp { padding-right: 8px; }

/* =========================================================
 *  纸张库（#paperModal）：一列纸 + 一份编辑表单
 *  560 档；行内两个小按钮用 24 高（--h-ctl-xs），与 32 高的行不打架。
 * ========================================================= */
#paperModal .modal-box { width: var(--mw-m); }

.paper-bar { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 8px; }
.paper-hint {
  flex: 1 1 auto; min-width: 0; font-size: 10.5px; color: var(--tx-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.paper-list {
  display: flex; flex-direction: column; gap: 4px;
  max-height: 240px; overflow: auto; padding-right: 2px;
}
.paper-row {
  flex: none; display: flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 6px 0 9px; border-radius: 6px;
  background: var(--bg-elev); border: 1px solid var(--bd-soft);
}
.paper-row.on { border-color: var(--accent-line); background: var(--bg-active); }
.paper-nm {
  flex: 1 1 auto; min-width: 0; font-size: 11.5px; color: var(--tx);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.paper-nm i { font-style: normal; margin-left: 5px; font-size: 10px; color: var(--tx-3); }
.paper-dim { flex: none; font-size: 10.5px; color: var(--tx-3); font-variant-numeric: tabular-nums; }
.paper-kind {
  flex: none; padding: 0 5px; border-radius: 4px;
  font-size: 10px; line-height: 1.7; background: var(--bg-active); color: var(--tx-2);
}
.paper-kind.offset { color: var(--orange); background: var(--orange-soft); }
.paper-row .btn.sm { height: var(--h-ctl-xs); padding: 0 7px; font-size: 10.5px; }
.paper-empty { padding: 18px 0; text-align: center; font-size: 10.5px; color: var(--tx-3); }

.paper-edit { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--bd-soft); }
.paper-msg { margin-top: 6px; font-size: 10.5px; line-height: 1.6; color: var(--tx-3); }
.paper-msg.err { color: var(--red); }
.paper-msg.warn { color: var(--orange); }

/* ---------------------------------------------------------
 *  分贴表（#sigModal）：一行一档「≤ [克重] g → [页数] P 一贴」
 *  行内不折行：数字框 78（与全站同宽）+ 页数下拉 104 + 移除靠右
 * --------------------------------------------------------- */
.sig-rows { display: flex; flex-direction: column; gap: 6px; }
.sr-row { display: flex; align-items: center; gap: 4px; height: var(--h-ctl); flex-wrap: nowrap; }
/* 二维表：纸张类型 / ≤克重 / → / 一贴页数。select.inp 全局 min-width:150，
   这一行五个控件放不下，所以这里单独收窄（不动全局）。
   v1.0.94（弹窗 640→480）：整行固定尺寸之和已顶到 480 档的边，
   「移除」按钮溢出 45px → 纸张/页数 104→88、克重 78→68、间距 6→4，
   整行收回 480 档以内（数值仍在可读宽度内，数字框本来关掉了 spinner）。 */
.sr-row .sr-paper { width: 84px; flex: none; min-width: 0; }
.sr-row .sr-max { width: 68px; flex: none; }
.sr-row .sr-sig { width: 84px; flex: none; }
.sr-le, .sr-any { flex: none; width: 26px; text-align: right; font-size: 11.5px; color: var(--tx-3); }
.sr-u { flex: none; font-size: 11px; color: var(--tx-3); }
.sr-arrow { flex: none; font-size: 11px; color: var(--tx-4); }
.sr-row .sr-del, .sr-row .sr-pad { margin-left: auto; flex: none; }
/* 「移除」按钮左右内边距收到 8px：它是这一行里唯一还能再挤的控件
   （纸张下拉与页数下拉要留够看清三个汉字），收窄后整行不再顶出弹窗右边界。 */
.sr-row .sr-del { padding: 0 8px; }
.sr-row .sr-pad { width: 1px; }
#sigModal .modal-box { width: var(--mw-l); }   /* 一行五列，按 L 档（640）放 */
/* 克重数字框只有 78px，spinner 会把 4 位数挤掉 —— 与拼版测算同样关掉 */
#sigModal input[type=number].inp::-webkit-inner-spin-button,
#sigModal input[type=number].inp::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
#sigModal input[type=number].inp { padding-right: 8px; }

.form-note.sig-note { min-height: 0; }
.form-note.sig-note.err { color: var(--red); }
.form-note.sig-note.ok { color: var(--green); }
.form-note.sig-note.warn { color: var(--orange); }

/* 纸张库的数字框只有 78px，spinner 会把 4 位数挤掉一半 —— 与拼版测算同样关掉 */
#paperModal input[type=number].inp::-webkit-inner-spin-button,
#paperModal input[type=number].inp::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

/* 走纸尺寸与工艺：两个下拉各占一半，等宽。
   select.inp 的 min-width:150 在 480 弹窗里会把两个挤成折行（150+150+6 > 456 之外的余量）。
   这里按弹窗单独放开，不动全局。 */
#auSheet, #auMode { flex: 1 1 0; min-width: 0; }

/* 页面范围的「奇数 / 偶数」快捷按钮（pagerange-plus.js 注入）
   —— 只是两个小 chip，跟着输入框走，不改变弹窗原有布局 */
.range-quick {
  display: inline-flex;
  gap: 4px;
  margin-left: 6px;
  vertical-align: middle;
  flex: 0 0 auto;
}
.range-quick button {
  font: inherit;
  font-size: 12px;
  line-height: 1.5;
  padding: 2px 9px;
  border-radius: 6px;
  border: 1px solid var(--bd);
  background: var(--bg-elev);
  color: var(--tx);
  cursor: pointer;
  white-space: nowrap;
}
.range-quick button:hover { background: var(--bg-hover); }
.range-quick button.on {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent);
}

/* =========================================================
 *  文件夹骑马钉合订（folderbatch.js）
 *    清单是「选完文件夹先给你看一遍」的那张表：序号 / 文件名 / 页数→版数。
 *    它必须能滚，否则几十个文件的文件夹会把弹窗撑到屏幕外。
 * ========================================================= */
.fs-list {
  margin-top: 8px;
  max-height: 210px;
  overflow-y: auto;
  border: 1px solid var(--bd);
  border-radius: var(--r-sm);
  background: rgba(0, 0, 0, .12);
}
.fs-list:empty { display: none; }
.fs-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 9px;
  font-size: var(--fs-label);
  border-bottom: 1px solid var(--bd-soft);
}
.fs-row:last-child { border-bottom: 0; }
.fs-row i {
  flex: 0 0 26px;
  font-style: normal;
  text-align: right;
  color: var(--tx-3);
  font-variant-numeric: tabular-nums;
}
.fs-row b {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  color: var(--tx);
}
.fs-row span {
  flex: 0 0 auto;
  color: var(--tx-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.fs-row.bad b { color: var(--red); }
.fs-bad { color: var(--red) !important; }
.fs-empty {
  padding: 14px 10px;
  text-align: center;
  color: var(--tx-3);
  font-size: var(--fs-label);
}
.fs-skip {
  padding: 6px 9px;
  font-size: 11.5px;
  color: var(--orange);
  background: var(--orange-soft);
}
/* 组头（一个子文件夹 = 一本）：清单里几十行滚到中间就分不清哪几行属于哪个班，
   所以让它吸顶；底色必须是不透明的，否则下面的行会从字缝里透出来。 */
.fs-group {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 4px 9px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--tx-3);
  background: var(--bg-panel);
  border-top: 1px solid var(--bd-soft);
  border-bottom: 1px solid var(--bd-soft);
}
.fs-group:first-child { border-top: 0; }

/* 合订弹窗要放清单 + 结果，比普通弹窗宽一些（.modal-box.wide 不是全局类，
   这里只针对这一个弹窗放开，不影响其它 30 个弹窗） */
/* 书刊折手：一直是 min(640px, …) 的一次性数字，绕过了 S/M/L 三档。
   v1.0.94 起并回 L 档（640→480，即 --mw-l 的 3/4），跟着全站一起变窄。 */
#folderSaddleModal .modal-box { width: var(--mw-l); }
#folderSaddleModal .result-list { max-height: 150px; overflow-y: auto; }
#folderSaddleModal .result-list:empty { display: none; }
#folderSaddleModal input[type=number].inp::-webkit-inner-spin-button,
#folderSaddleModal input[type=number].inp::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

/* =========================================================
 *  合并文件（mergefiles.js）
 *    清单行直接复用上面骑马钉合订那套 .fs-list / .fs-row / .fs-empty，
 *    这里只补三处差异：弹窗要放得下清单 + 结果；清单略高（要显示补白页数）；
 *    勾选项的说明行可能是两三行，行高放宽一点免得挤在一起。
 * ========================================================= */
#mergeFilesModal .modal-box { width: var(--mw-l); }
#mergeFilesModal .fs-list { max-height: 190px; }
#mergeFilesModal .result-list { max-height: 150px; overflow-y: auto; }
#mergeFilesModal .result-list:empty { display: none; }
#mergeFilesModal .opt-d { line-height: 1.55; }

/* =========================================================
 *  转专色（spot.js）
 *    参数只有「专色 M/C + 专色名 + 页码」，宽度用默认 M 档（560），
 *    不新增尺寸档；这里只补结果列表的滚动高度，其余的
 *    .form-row / .seg-form / .sf-btn / .result-list 全部复用既有样式。
 * ========================================================= */
#spotModal .result-list { max-height: 168px; overflow-y: auto; }
#spotModal .result-list:empty { display: none; }
#spotModal #spPageNote { margin-top: 4px; }
#spotModal .form-note code { font-family: var(--mono, monospace); }
