/* 常规左右结构后台：左侧固定导航 + 右侧内容区（工具条 / 列表 / 详情抽屉） */
:root {
  --bg: #f5f6f8;
  --panel: #ffffff;
  --border: #e3e6ea;
  --text: #1f2329;
  --muted: #6b7280;
  --brand: #2f6feb;
  --ok: #1f9d55;
  --warn: #b7791f;
  --err: #d64545;
  --sidebar: 232px;
  --filters: 208px;
  /* 左栏搜索区与列表固定头部共用这个高度，让左右两边的第一行处在同一水平线上 */
  --listhead: 76px;
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font: 14px/1.5 -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif;
  color: var(--text); background: var(--bg);
}
.app-root { height: 100%; }
.boot { padding: 48px; color: var(--muted); }

/* —— 工作台布局 —— */
/* 三个独立滚动区：导航（左）、筛选（左二）、列表（主）。顶栏与操作区固定不动。
   为什么不再是"一个大滚动容器竖着堆 panel"：那样操作块一长就把列表顶出首屏，
   运营得先往下滚看列表、勾选，再滚回上面点操作 —— 一次操作两趟往返。 */
.workbench {
  display: grid; grid-template-columns: var(--sidebar) 1fr;
  height: 100vh; overflow: hidden;
}
/* 收起后只剩一竖排图标：横向空间全让给列表。收起是省地方，不是藏功能 —— 悬停仍看得到全名 */
.workbench.nav-collapsed { --sidebar: 56px; }

.sidebar { background: #1f2430; color: #cfd6e4; display: flex; flex-direction: column; overflow: hidden; }
.sidebar-brand {
  display: flex; align-items: center; gap: 10px; white-space: nowrap;
  padding: 15px 16px; font-size: 15px; font-weight: 600; color: #fff; border-bottom: 1px solid #2c3342;
}
.sidebar-nav { flex: 1; padding: 8px 0; overflow: auto; }
.nav-item {
  display: flex; align-items: center; gap: 10px; white-space: nowrap;
  width: 100%; text-align: left; background: none; border: 0; border-radius: 0; cursor: pointer;
  color: #cfd6e4; padding: 9px 16px; font-size: 14px;
}
/* 两个 class 起头，压过通用 button:hover 的悬停配色（否则深底上出现蓝字） */
.sidebar .nav-item:hover { background: #272e3d; color: #fff; border-color: transparent; }
.sidebar .nav-item.active { background: var(--brand); color: #fff; }
.nav-icon {
  flex: 0 0 24px; width: 24px; height: 24px; border-radius: 5px; font-size: 13px;
  display: inline-flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, .08);
}
.sidebar .nav-item.active .nav-icon { background: rgba(255, 255, 255, .22); }
.nav-text { overflow: hidden; text-overflow: ellipsis; }
.sidebar-foot { padding: 8px 0 12px; border-top: 1px solid #2c3342; font-size: 12px; color: #97a2b6; }
.sidebar-identity { padding: 4px 16px 8px; }
.workbench.nav-collapsed .nav-text,
.workbench.nav-collapsed .sidebar-identity { display: none; }
.workbench.nav-collapsed .sidebar-brand,
.workbench.nav-collapsed .nav-item { gap: 0; justify-content: center; padding-left: 0; padding-right: 0; }

.main { display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.topbar {
  display: flex; align-items: center; gap: 12px; flex: 0 0 auto;
  padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--border);
}
.topbar h1 { margin: 0; font-size: 16px; white-space: nowrap; }
.topbar-notice { flex: 1; min-width: 0; }
.topbar-notice .hint { margin: 0; }
.nav-toggle { flex: 0 0 auto; width: 30px; height: 28px; padding: 0; line-height: 1; color: var(--muted); }

/* 工作区：左筛选栏 + （上操作区 / 主列表）。筛选栏为空时整栏收掉，列表直接铺满 */
.workarea { flex: 1; display: flex; min-height: 0; overflow: hidden; }
.filterbar {
  flex: 0 0 var(--filters); width: var(--filters); overflow: auto;
  padding: 12px 12px 24px; background: var(--panel); border-right: 1px solid var(--border);
}
.filterbar:empty { display: none; }
.filter-section { margin-bottom: 12px; }
.filter-section h4 { margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); }
.filter-section input, .filter-section select { width: 100%; }
/* 搜索区与列表固定头部同高：两边的第一行对齐，视线不用上下找 */
.filter-search {
  min-height: var(--listhead); box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: center;
  margin: -12px -12px 12px; padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.filter-search h4 { margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); }
.filter-search input { width: 100%; }
/* 左栏里的按钮撑满栏宽。原来用 row-end 右对齐，在 208px 的窄栏里会把按钮挤成一个
   看不出是什么的小方块（截图里"标记"下面那个不明元素就是它）。 */
.filter-section > button, .filter-section .row > button { width: 100%; }
.workmain { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }

/* 操作区：固定在列表正上方，常驻展开。
   常驻而不是"勾选后才出现"：省下的那点空间换来的是每次勾选都跳一次版，
   而跳版比多占一行更难用。勾选前后只有"能不能点"的差别，位置与高度一律不动。 */
.actionbar {
  flex: 0 0 auto; padding: 8px 16px 10px;
  background: var(--panel); border-bottom: 1px solid var(--border);
}
.actionbar:empty { display: none; }
.action-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: nowrap; min-width: 0; }
.action-head .hint { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.action-head strong { flex: 0 0 auto; }
/* 未选中时把"先勾选"摆出来，选中后换成操作提示 —— 两句话占同一行，不产生高度变化 */
.action-head .muted { flex: 0 0 auto; }
.actionbar.has-selection .action-head .muted { display: none; }
.actionbar:not(.has-selection) .action-head .hint { display: none; }

/* 工序带：从左到右一步一格，顺序即工作流。带宽不够时整条横向滚动，高度恒定。 */
.action-steps { display: flex; align-items: stretch; gap: 0; overflow-x: auto; margin-top: 6px; }
.action-step {
  flex: 0 0 auto; min-width: 0; padding: 2px 14px 2px 14px;
  border-left: 1px solid var(--border);
}
.action-step:first-child { border-left: 0; padding-left: 0; }
/* 需要写长篇的那一步吃掉剩余横向空间，而不是所有步骤挤成等宽窄柱 */
.action-step-wide { flex: 1 1 300px; min-width: 260px; }
.action-step-title {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); white-space: nowrap;
}
.action-step-no {
  flex: 0 0 16px; width: 16px; height: 16px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #eef1f6; color: #6b7280; font-size: 11px; font-weight: 600;
}
.action-step > .hint {
  margin: 0 0 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 320px;
}
.action-step-wide > .hint { white-space: normal; max-width: none; }
.action-step .tile-block { margin-top: 0; }
.action-step .tiles { margin-top: 0; }
.action-step .row { margin-top: 6px; }
/* 长文本框：审批意见与回复都要能写长篇，默认给足行数且可继续拉高 */
.action-longtext { width: 100%; min-height: 84px; resize: vertical; }
/* 收尾那一步整体压低分量：它是流程末端，不该在视觉上和主要动作抢位置 */
.action-step-quiet .action-step-no { background: #f4f5f7; color: #9aa3b2; }

/* 列表区的固定头部：统计磁贴 + 分页条。
   它必须不随列表滚动 —— 之前它跟着表格一起滚，滚下去之后吸顶的表头就压在它身上
   （表现就是"顶栏穿透、顶部详情栏被带跑"），而"共 N 条 / 第 M 页"恰恰是滚到一半时最需要看见的。
   高度与左栏搜索区对齐（都用 --listhead），让左右两边的第一行处在同一水平线上。 */
.listhead {
  flex: 0 0 auto; min-height: var(--listhead); box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: center; gap: 8px;
  padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--border);
  /* 自成一层并压过列表区：第三道防线。
     列表区的吸顶表头是定位元素（z-index 3），万一将来它或别的什么溢出到这条头部的位置上，
     也只能在它**下面**。头部装的是"共 N 条 / 第 M 页"与统计，那是滚到一半时最需要看清的东西，
     不能被任何列表内容盖住或透过来。列头的筛选面板一律向下展开，不会被这一层挡到。 */
  position: relative; z-index: 4;
}
.listhead:empty { display: none; }
.listhead .panel { margin: 0; padding: 0; border: 0; background: none; }

/* 时间范围：快捷档在前（九成用法是"看看最近发生了什么"），自定义区间在后 */
.range { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12px; }
.range-label { color: var(--muted); }
.range-preset { padding: 3px 10px; font-size: 12px; }
.range-preset-on { border-color: var(--brand); background: #eef4ff; color: var(--brand); }
.range-sep { color: #cfd4dd; }
/* 触发钮上直接写着当前区间，点开是日历。position:relative 给弹窗定位用 */
.range-custom { position: relative; display: inline-flex; align-items: center; }
.range-pick { font-size: 12px; padding: 3px 10px; }
.range-pick-on { border-color: var(--brand); background: #eef4ff; color: var(--brand); }

/* —— 日历（所有取日期范围的地方共用这一套）—— */
/* 弹窗靠 CSS 定位，不用 JS 量位置：量位置要写内联 style，而 CSP 的 style-src 'self' 会把它丢掉 */
.cal-pop {
  position: absolute; top: 100%; left: 0; z-index: 30; margin-top: 4px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 8px 24px rgba(15, 20, 28, .12); padding: 10px;
}
.cal { display: flex; flex-direction: column; gap: 6px; }
.cal-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cal-title { font-size: 13px; font-weight: 600; white-space: nowrap; }
.cal-nav { padding: 0 8px; line-height: 1.7; }
/* 七列固定网格：日期按周对齐，宽度不随内容变（一位数与两位数的日子要在同一列上） */
.cal-grid { display: grid; grid-template-columns: repeat(7, 30px); gap: 2px; }
.cal-wd { font-size: 11px; color: var(--muted); text-align: center; line-height: 22px; }
.cal-pad { display: block; }
.cal-day {
  padding: 0; line-height: 26px; font-size: 12px; text-align: center;
  border-color: transparent; background: none; border-radius: 4px;
}
.cal-day:hover { border-color: var(--brand); color: var(--brand); }
/* 今天给一个底纹：它是"到现在"这个边界，选范围时最常用的锚点 */
.cal-day-today { font-weight: 600; box-shadow: inset 0 0 0 1px #cfd6e4; }
/* 无数据的日子标灰：可选但明显不推荐，省掉"选了个空区间、以为筛坏了"这一次来回 */
.cal-day-empty { color: #c2c8d2; }
/* 未来的日子点不动：这些日历都在回溯已经发生的事，未来必然是空的 */
.cal-day-future { color: #dfe3ea; cursor: not-allowed; }
.cal-day-future:hover { border-color: transparent; color: #dfe3ea; }
/* 区间是一段而不是两个点：中间的日子整片串起来，选完一眼看出"我选的是这一段" */
.cal-day-in { background: #eef4ff; color: var(--brand); border-radius: 0; }
.cal-day-start, .cal-day-end { background: var(--brand); color: #fff; border-color: var(--brand); }
.cal-day-start { border-radius: 4px 0 0 4px; }
.cal-day-end { border-radius: 0 4px 4px 0; }
/* 起止同一天：两个 class 同时命中，四角都要圆回来 */
.cal-day-start.cal-day-end { border-radius: 4px; }
.cal-day-start:hover, .cal-day-end:hover { color: #fff; }
.cal-foot { font-size: 12px; }
/* 列头筛选面板里的日历是内联的（面板自带"确定/清除本列"），不要再叠一层浮层的边框 */
.th-popover .cal-pop { position: static; border: 0; box-shadow: none; padding: 0; }

/* 统计磁贴 */
.stats { display: flex; gap: 8px; flex-wrap: wrap; }
.stat {
  display: flex; align-items: baseline; gap: 6px;
  padding: 5px 12px; border: 1px solid var(--border); border-radius: 6px; background: #fafbfc;
  font: inherit; text-align: left; white-space: nowrap;
}
.stat-pick { cursor: pointer; }
.stat-pick:hover { border-color: var(--brand); background: #f5f8ff; }
.stat-value { font-size: 17px; font-weight: 600; line-height: 1.1; }
.stat-label { font-size: 12px; color: var(--muted); }
/* 口径色调复用状态徽标色板，让磁贴上的数字和列表里的标签是同一套视觉语言 */
.stat-pending .stat-value { color: #ad6800; }
.stat-processed .stat-value { color: var(--ok); }
.stat-danger .stat-value { color: var(--err); }
.stat-brand .stat-value { color: var(--brand); }

/* 主列表：唯一的主滚动区，占屏幕大部分。

   **上沿不留 padding**（间距改用子元素的 margin 让出）。
   原因是吸顶表头钉住的位置是这个容器的内容上沿：只要这里有 padding-top，
   那段 padding 就成了表头上方的一条空带，行滚上去会从带子里露出来 ——
   看起来就是"列表条顶部透出内容"。margin 会随内容一起滚走，不产生这条带。
   下、左、右的 padding 不影响吸顶，照旧。 */
.listarea { flex: 1; min-height: 0; overflow: auto; padding: 0 16px 32px; }
/* 用 margin 补回原来那 12px 的视觉间距（它滚走即消失，不会变成表头上方的空带） */
.listarea > :first-child { margin-top: 12px; }
.listarea .panel:last-child { margin-bottom: 0; }
/* 每个可筛选维度都独立成列之后列数变多。给表格一个下限宽度，窄屏就让主区横向滚动，
   而不是把十几列挤成一列两个字 —— 挤到看不清等于这些列不存在。 */
.listarea table { min-width: 1040px; }

/* —— 通用控件 —— */
button {
  font: inherit; padding: 6px 12px; border-radius: 4px; border: 1px solid var(--border);
  background: #fff; cursor: pointer;
}
button:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }
button:disabled { opacity: .5; cursor: not-allowed; }
button.primary { background: var(--brand); border-color: var(--brand); color: #fff; }
button.primary:hover:not(:disabled) { filter: brightness(1.08); color: #fff; }
button.danger { color: var(--err); border-color: #f0c2c2; }
input, select, textarea {
  font: inherit; padding: 6px 8px; border: 1px solid var(--border); border-radius: 4px; background: #fff;
  color: var(--text); max-width: 100%;
}
textarea { min-height: 76px; resize: vertical; width: 100%; }
label { font-size: 13px; color: var(--muted); display: inline-flex; gap: 6px; align-items: center; }
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 6px; padding: 14px; margin-bottom: 14px; }
.row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.row-end { justify-content: flex-end; }
.grow { flex: 1; }
.muted { color: var(--muted); }
.mono { font-family: ui-monospace, Consolas, monospace; font-size: 12px; }
.hint { font-size: 12px; color: var(--muted); }

/* —— 表格 —— */
table { width: 100%; border-collapse: collapse; background: var(--panel); }
th, td { padding: 8px 10px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
/* 表头吸顶。分页条已经搬到固定头部（.listhead），所以这里贴的是滚动区顶边，不会再压到它身上。

   "穿透"这件事踩过两次，成因不同，两道都要留着：
   1) border-collapse 下 sticky 表头的**边框会跟着滚**，缝隙里透出下一行文字。
      所以分隔线用 box-shadow 画，不用 border。
   2) 表头钉住的位置是滚动容器的**内容上沿**，而容器有 padding-top 时，
      那段 padding 就成了表头上方的一条空带 —— 行滚上去会从这条带里露出来。
      根子在 .listarea 的 padding-top（已改成用 margin 让出间距，见那里的注释）；
      这里再用一片**向上的同色投影**把表头顶边封住，作为第二道：
      即使将来又有人给滚动容器加回 padding、或者出现亚像素取整的一线缝隙，
      露出来的也只是表头自己的底色，而不是下一行的内容。
   两道都不依赖 JS 量位置 —— 量位置就得写内联 style，而 CSP 会把它丢掉。 */
th {
  font-size: 12px; color: var(--muted); font-weight: 600;
  position: sticky; top: 0; z-index: 3;
  background: #f2f4f7; border-bottom: 0;
  /* 背景只铺到内边距边缘，避免与相邻单元格的边线重叠出深浅不一的接缝 */
  background-clip: padding-box;
  box-shadow: 0 -10px 0 #f2f4f7, inset 0 -1px 0 var(--border), 0 1px 0 var(--border);
}
tr.selected td { background: #eef4ff; }
td.nowrap, th.nowrap { white-space: nowrap; }
/* 整行可点：hover 给出可点的信号，否则"能点"这件事没人会发现 */
tr.row-pick { cursor: pointer; }
tr.row-pick:hover td { background: #f5f8ff; }
tr.row-pick.selected:hover td { background: #e6eeff; }
/* 勾选格：整格是热区，但只做勾选，不参与"整行点开详情"（分层触发，见 dataTable）。
   宽度写死是为了让一列复选框对得齐 —— 靠内容撑宽时首页与末页的列宽会差几像素。 */
td.col-pick, th.col-pick { cursor: pointer; width: 40px; padding: 6px 8px; text-align: center; }
/**
 * 列表复选框比浏览器默认的 13px 大一圈。
 * 默认尺寸在一屏几十行里是最难点中的目标：行高 33px，方框只占其中 13px，
 * 而这一列是批量操作的入口，点不中就得再点一次，或者误点到别处把详情抽屉弹出来。
 * 18px 是"明显好点"与"不抢走内容视觉重量"之间的那一档；accent-color 让勾上之后
 * 与全站同一个蓝，扫一眼就分得出哪几行选着（行底色也会跟着变，见 tr.selected）。
 */
.pick-box { width: 18px; height: 18px; margin: 0; cursor: pointer; accent-color: var(--brand); }

/* —— 徽标 —— */
.tag { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 12px; border: 1px solid var(--border); }
/* 待填写：等的是用户，不是客服。用中性灰而不是"待处理"的黄——
   黄色在这一屏的含义是"该你动手了"，而这一档恰恰是客服动不了的。 */
.tag-to_fill { background: #f2f3f5; border-color: #d0d5dd; color: #4b5563; }
.tag-pending { background: #fff7e6; border-color: #ffd591; color: #ad6800; }
/* 处理中：用品牌蓝而不是"待处理"的黄或"已处理"的绿 —— 它既不是待办也不是完成，
   同色系会让人在列表里扫不出"这单已经有人接手了"。 */
.tag-processing { background: #eef4ff; border-color: #9fb6e8; color: var(--brand); }
.tag-processed { background: #e8f6ee; border-color: #b7e0c8; color: var(--ok); }
.tag-cancelled { background: #f2f3f5; color: var(--muted); }
.tag-closed { background: #eef1f6; color: #4b5563; }
.tag-archived { background: #eceef2; color: #4b5563; }
/* 有限枚举（签收 / 退款 / 寄出）也用徽标，和处理进度同一套视觉语言：
   一屏几十行时，纯文字要逐个读，色块能一眼扫出"哪几行还没退款"。
   未开始用中性灰而不是黄：黄留给"有人在等"，几十行全黄等于没有重点。 */
.tag-neutral { background: #f2f3f5; border-color: #e3e6ea; color: #6b7280; }
.tag-mid { background: #eef4ff; border-color: #c5d6f8; color: #2f6feb; }
.tag-done { background: #e8f6ee; border-color: #b7e0c8; color: var(--ok); }
/* 已删除：比"封存"更强的视觉分量 —— 它意味着用户端已经完全看不到这张工单 */
.tag-deleted { background: #fdeaea; border-color: #f3b7b7; color: #a12626; }
/* 审批结论：待审批要显眼（有人在等），已驳回用告警色，已通过不加徽标（免得满屏都是标签） */
.tag-approval-pending { background: #fff7e6; border-color: #ffd591; color: #ad6800; }
.tag-approval-rejected { background: #fdeaea; border-color: #f3b7b7; color: #a12626; }
.tag-approval-approved { background: #e8f6ee; border-color: #b7e0c8; color: var(--ok); }

/* 标记色板：颜色必须走 CSS 类，不能用内联 style —— CSP 的 style-src 'self' 会丢掉内联样式，
   那样色块变成白字白底，界面上就是"标记没有内容"。 */
.mark-chip {
  display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 12px;
  color: #fff; margin-right: 4px; background: #6b7280;
}
.mark-c1 { background: #2f6feb; }
.mark-c2 { background: #1f9d55; }
.mark-c3 { background: #b7791f; }
.mark-c4 { background: #d64545; }
.mark-c5 { background: #7c3aed; }
.mark-c6 { background: #0e7490; }
.mark-c7 { background: #be185d; }
.mark-c8 { background: #4b5563; }

/* —— 抽屉 —— */
.drawer-backdrop { position: fixed; inset: 0; background: rgba(15, 20, 28, .35); display: flex; justify-content: flex-end; z-index: 40; }
/* 抽屉：标题栏固定，只有内容滚。内容很长（申请材料 + 附件 + 审批 + 处理表单 + 改动记录），
   标题跟着滚走之后就看不出"我在改的是哪一张工单"，关闭按钮也一起没了。 */
.drawer {
  width: min(680px, 92vw); height: 100%; display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg); box-shadow: -6px 0 20px rgba(0,0,0,.12);
}
.drawer-head {
  flex: 0 0 auto; padding: 14px 18px 10px;
  background: var(--panel); border-bottom: 1px solid var(--border);
}
.drawer-head .hint { margin: 4px 0 0; }
.drawer-body { flex: 1; min-height: 0; overflow: auto; padding: 14px 18px 40px; }
.drawer h2 { margin: 0; font-size: 16px; }
.kv { display: grid; grid-template-columns: 108px 1fr; gap: 6px 12px; font-size: 13px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { border-left: 2px solid var(--border); padding: 0 0 12px 12px; margin-left: 4px; }
.timeline li:last-child { padding-bottom: 0; }
.change-line { font-size: 12px; color: #374151; }

/* —— 登录 —— */
/* 居中用 flex + min-height：grid place-items 在被撑高的祖先里可能不居中，flex 这套更稳 */
.login-wrap {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; width: 100%; padding: 24px; box-sizing: border-box;
}
.login-card {
  width: 100%; max-width: 340px; background: var(--panel);
  border: 1px solid var(--border); border-radius: 8px; padding: 24px;
  box-shadow: 0 2px 10px rgba(31, 35, 41, .06);
}
.login-card h1 { font-size: 17px; margin: 0 0 18px; text-align: center; }
.login-card .field { margin: 0 0 14px; }
.login-card input { width: 100%; height: 38px; padding: 8px 10px; }
.login-card button[type="submit"] { min-width: 88px; height: 36px; }
/* 登录失败的唯一反馈：中性文案，不区分失败原因（避免成为账号枚举/状态探测接口） */
.login-notice { color: var(--err); font-size: 13px; min-height: 20px; line-height: 20px; }

/* —— toast —— */
.toasts { position: fixed; right: 16px; bottom: 16px; display: flex; flex-direction: column; gap: 8px; z-index: 60; }
.toast { padding: 8px 12px; border-radius: 4px; background: #223; color: #fff; max-width: 380px; box-shadow: 0 4px 14px rgba(0,0,0,.18); }
.toast-err { background: var(--err); }
.toast-ok { background: #1f2937; }

/* —— 批量结果 —— */
.result-list { max-height: 220px; overflow: auto; margin: 8px 0 0; padding-left: 18px; font-size: 13px; }
.result-fail { color: var(--err); }
.result-rolled { color: var(--warn); }

/* —— 危险操作区 —— */
/* 删除面板独立成一块并加左边框：它和上面的常规操作不是同一类动作，
   视觉上分开是为了让人不至于顺着操作节奏点下去。 */
.panel-danger { border-left: 3px solid var(--err); }
.warn { color: #ad6800; font-size: 12px; }

/* —— 申请附件：缩略图墙 —— */
/* 横向排布、直接罗列，不需要"先点一次查看"：审批的依据就是这些材料。
   缩略图与视频都是浏览器直连对象存储（预签名 URL），不经控制台；
   视频用 preload="metadata" 只取文件头与首帧，一屏十个也不会各自缓冲几百 MB 把页面拖垮。 */
.gallery { margin-top: 8px; }
.gallery-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.gallery-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.gallery-item {
  width: 148px; padding: 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel);
}
/* 附件卡片的勾选：整条是热区（label 天然带转发），复选框与列表用同一个尺寸（.pick-box） */
.gallery-pick { display: flex; align-items: center; min-height: 22px; margin-bottom: 4px; cursor: pointer; }
.gallery-thumb {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100px; padding: 0; overflow: hidden;
  border: 1px solid var(--border); border-radius: 4px; background: #fafbfc; cursor: zoom-in;
}
.gallery-thumb:hover:not(:disabled) { border-color: var(--brand); }
.gallery-thumb-plain { cursor: default; }
.gallery-thumb:disabled { opacity: 1; }
.gallery-thumb-media { max-width: 100%; max-height: 100%; object-fit: contain; }
.gallery-thumb-file { font-size: 28px; color: #b6bdc9; }
/* 文件名可能很长（相册导出的名字动辄几十个字符）：截断但把全名放进 title，
   否则一个附件就能把整块布局撑破，还会和相邻卡片的文字叠在一起 */
.gallery-name {
  margin-top: 6px; font-size: 12px; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gallery-meta { font-size: 11px; color: var(--muted); }
.gallery-dl, .gallery-zip {
  display: inline-block; margin-top: 6px; padding: 3px 8px;
  border: 1px solid var(--border); border-radius: 4px; background: #fff;
  color: var(--text); font-size: 12px; text-decoration: none;
}
.gallery-dl:hover, .gallery-zip:hover { border-color: var(--brand); color: var(--brand); }
.gallery-zip { margin-top: 0; }
/* 没选中时打包链接不可点：一个点了没反应的链接比一个明显不可点的更让人困惑 */
.gallery-zip-off { color: #a5adba; border-color: #ecedf1; background: #fafbfc; cursor: not-allowed; }
.gallery-zip-off:hover { color: #a5adba; border-color: #ecedf1; }
/* 打包在浏览器里做，是一段有过程的事：进度就地显示，旁边留一个取消。
   不做成进度条：条数与文件名比一个百分比有用（卡在哪个文件上一眼可见）。 */
.gallery-progress { font-size: 12px; color: var(--brand); }
.gallery-cancel { font-size: 12px; }

/* —— 灯箱 —— */
/* 用原生 <dialog>：模态、焦点收拢、Esc 关闭、::backdrop 都是浏览器自带的。
   CSP 是 script-src 'self'，引第三方灯箱库得先本地托管，等于给一个已解决的问题增加供应链面。 */
.lightbox {
  width: 92vw; max-width: 1200px; height: 88vh; padding: 0;
  border: 0; border-radius: 8px; background: var(--panel); color: var(--text);
}
.lightbox::backdrop { background: rgba(15, 20, 28, .72); }
.lightbox-head {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px; border-bottom: 1px solid var(--border);
}
.lightbox-caption { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; }
.lightbox-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.lightbox-close { flex: 0 0 auto; width: 28px; height: 28px; padding: 0; font-size: 18px; line-height: 1; }
.lightbox-body {
  display: flex; align-items: center; gap: 8px;
  height: calc(88vh - 49px); padding: 10px; box-sizing: border-box;
}
.lightbox-stage { flex: 1; min-width: 0; height: 100%; display: flex; align-items: center; justify-content: center; }
/* 大图按可用空间等比缩放：客服要看清损坏位置，原图铺满屏幕反而要来回滚动 */
.lightbox-media { max-width: 100%; max-height: 100%; object-fit: contain; }
.lightbox-nav { flex: 0 0 auto; width: 34px; height: 56px; padding: 0; font-size: 22px; line-height: 1; }

/* —— 磁贴 —— */
/* 一格一个动作，点一下就生效：替代"下拉框 + 应用按钮"的三次交互（展开、选、点应用）。
   运营一天要做几十次，磁贴一次点击即达，且当前状态一眼可见。 */
.tile-block { margin-top: 10px; }
.tile-block + .tile-block { border-top: 1px solid var(--border); padding-top: 10px; }
.tiles { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.tile {
  display: inline-flex; align-items: center; gap: 6px;
  min-width: 96px; padding: 9px 14px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 8px; background: #fff;
  font-size: 13px; line-height: 1.2; text-align: left;
}
.tile:hover:not(:disabled) { border-color: #9fb6e8; background: #f5f8ff; }
/* 未勾选工单时磁贴是"待命"而不是"坏了"：保持尺寸与轮廓，只把对比度降下来。
   用 opacity 会连边框一起打薄，看着像渲染没完成。 */
.tile:disabled { opacity: 1; cursor: not-allowed; color: #a5adba; background: #fafbfc; border-color: #ecedf1; }
.tile:disabled .tile-badge { color: #b8bfca; }
.tile-active { border-color: #2f6feb; background: #eef4ff; box-shadow: inset 0 0 0 1px #2f6feb; }
/* 半选：只打在部分选中工单上。虚线边＝"还没统一"，配合角标张数，一眼看出差多少 */
.tile-mixed { border-style: dashed; border-color: #2f6feb; background: #f7faff; }
/* 弱化色调：删除这类收尾动作平时不抢注意力，指过去才显出危险色 */
.tile-quiet { border-color: transparent; background: transparent; color: var(--muted); }
.tile-quiet:hover:not(:disabled) { border-color: #f0c2c2; background: #fff6f6; color: var(--err); }
.tile-quiet:disabled { background: transparent; border-color: transparent; }
.tile-label { flex: 1; }
.tile-badge { font-size: 11px; color: var(--muted); }
/* 磁贴色调复用状态徽标色板，让"处理进度磁贴"和列表里的进度标签是同一套视觉语言 */
.tile-to_fill { border-left: 3px solid #d0d5dd; }
.tile-pending { border-left: 3px solid #ffd591; }
.tile-processing { border-left: 3px solid #9fb6e8; }
.tile-processed { border-left: 3px solid #b7e0c8; }
.tile-cancelled { border-left: 3px solid #d0d5dd; }
.tile-closed { border-left: 3px solid #b9c2d0; }
.tile-danger { border-left: 3px solid var(--err); }
/* 标记磁贴：色块在左，名字在右 */
.tile-mark { padding-left: 10px; }
.tile-mark .tile-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }

/* 纵向磁贴：长清单（处理进度、标记、审批）一行一格竖着排 —— 名字不会被挤断，
   而且加高不会顶宽；配合 .tiles-scroll 压在固定高度里，列表不会被推出首屏。 */
.tiles-col { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
.tiles-col .tile { width: 100%; min-width: 0; padding: 7px 10px; }
/* 固定高度只能写成 class：内联 max-height 会被 CSP 的 style-src 'self' 丢掉 */
.tiles-scroll { max-height: 152px; overflow-y: auto; padding-right: 2px; }
.tile-block-col > h3 { margin: 0 0 4px; font-size: 12px; color: var(--muted); }
.tile-block-col > .hint { margin: 0 0 4px; }
/* 危险动作独立成块并带红边：留给"整组都是危险动作"的场景（工单页的删除已改为收尾里的弱化磁贴） */
.tile-block-danger { border-left: 3px solid var(--err); padding-left: 10px; }

/* —— 分页条 —— */
.pager-label { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-size: 12px; }
.pager-size { min-width: 64px; }

/* —— 失败状态：一句话 + 重试 + 可展开的技术详情 —— */
/* 不做成红底警告框：连不上服务多半是瞬时的，一片红只会让人以为出了大事。
   左侧一道红边足够表明"这里失败了"，注意力留给重试按钮。 */
.errorstate { border-left: 3px solid var(--err); padding: 12px 14px; background: #fff; border-radius: 4px; }
.errorstate-text { margin: 0 0 10px; font-size: 14px; }
.errorstate-toggle { font-size: 12px; color: var(--muted); }
.errorstate-toggle-on { border-color: var(--brand); color: var(--brand); }
.errorstate-detail { margin-top: 10px; }

/* 只读诊断框：等宽字体 + 不换行横向滚动。堆栈换行之后每一帧会断成两行，行数翻倍反而更难读。 */
.diag { margin-top: 6px; }
.diag-text {
  width: 100%; min-height: 160px; resize: vertical;
  font-family: ui-monospace, Consolas, monospace; font-size: 12px; line-height: 1.5;
  background: #f7f8fa; color: #374151; border: 1px solid var(--border);
  white-space: pre; overflow: auto;
}

/* —— 口令输入（明文开关 / 随机生成 / 复制）—— */
.pw-field { display: inline-flex; align-items: center; gap: 6px; }
.pw-field input { min-width: 200px; }
.pw-toggle { font-size: 12px; }
.pw-toggle-on { border-color: var(--brand); color: var(--brand); }
.inline-check { font-size: 12px; }

/* 标记面板：用与管在同一处。每行点一下＝按它筛选（可多选），右侧删除，末尾新增。
   之前筛选在列头、新建在左栏、删除没有入口 —— 三件围绕同一批对象的事分散在三处。 */
.mark-panel { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
/* 磁贴组本身负责纵向排布与滚动（.tiles-col/.tiles-scroll），这里只把删除按钮塞进同一行 */
.mark-rows .mark-row { display: flex; align-items: center; gap: 4px; }
.mark-rows .mark-row .tile { flex: 1; min-width: 0; }
.mark-del {
  flex: 0 0 auto; padding: 0 6px; line-height: 1.6; font-size: 13px;
  color: var(--muted); border-color: transparent; background: none;
}
.mark-del:hover { color: var(--err); border-color: var(--err); background: #fff; }
.mark-clear, .mark-new { font-size: 12px; }
/* 新增撑满栏宽：右对齐会在窄栏里被挤成一个看不出是什么的小方块 */
.mark-new { width: 100%; }
/* 哨兵提示：正常永远不出现，出现即说明"共 N 条"与翻页已经不可信 */
.warn { color: var(--err); font-size: 12px; }

/* 状态下拉：以当前值打开，改成别的值就点亮。
   目的是让"现在是什么"和"将要改成什么"在同一个控件上分得开 ——
   之前这里默认显示 `—`，既看不出当前状态，也看不出这一格有没有待提交的改动。
   原生 <option> 的样式在各平台上无法统一（Windows 多由系统绘制），所以"当前"靠文字后缀标注，
   这里只负责整体那一层提示色。 */
.status-select-changed {
  border-color: var(--brand); background: #eef4ff; color: var(--brand); font-weight: 600;
}

/* 表格里可就地编辑的单元格（账号备注）：看着像文字，点一下才知道能改，
   所以给一道虚线下划线当可编辑的信号 —— 摆一个"编辑"按钮会把这一列撑宽一倍。 */
.cell-edit {
  border: 0; background: none; padding: 0; font: inherit; color: var(--text);
  text-align: left; cursor: text; border-bottom: 1px dashed #c9ced6; max-width: 220px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cell-edit:hover { border-bottom-color: var(--brand); color: var(--brand); }

/* —— 窄屏退化 —— */
/* 不把三栏硬挤成三条窄柱：先压筛选栏宽度，再窄就把导航锁成图标条、筛选栏翻到列表上方横着摆。 */
@media (max-width: 1280px) {
  .workbench { --filters: 180px; }
  .action-step { padding-left: 10px; padding-right: 10px; }
  .action-step-wide { flex: 1 1 240px; min-width: 220px; }
}
@media (max-width: 1024px) {
  .workbench { --sidebar: 56px; }
  .nav-text, .sidebar-identity { display: none; }
  .sidebar-brand, .nav-item { gap: 0; justify-content: center; padding-left: 0; padding-right: 0; }
  .nav-toggle { display: none; }
  .workarea { flex-direction: column; }
  .filterbar {
    flex: 0 0 auto; width: auto; max-height: 34vh;
    border-right: 0; border-bottom: 1px solid var(--border);
  }
  .filter-section { display: inline-block; width: 220px; margin-right: 12px; vertical-align: top; }
  .filterbar .tiles-col { flex-direction: row; flex-wrap: wrap; }
  .filterbar .tiles-col .tile { width: auto; min-width: 92px; }
  .filterbar .tiles-scroll { max-height: none; }
}

/* —— 列头排序与筛选（Excel 式）—— */
/* 排序与筛选都是整体生效（下推到数据库），列头只是入口。
   面板用 CSS 定位而不是 JS 量位置：量位置就得写内联 style，而 CSP 的 style-src 'self' 会把它丢掉。 */
.th-cell { display: flex; align-items: center; gap: 4px; min-width: 0; }
.th-label { overflow: hidden; text-overflow: ellipsis; }
.th-sort {
  flex: 1 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 4px; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600; color: var(--muted); text-align: left;
}
.th-sort:hover:not(:disabled) { color: var(--brand); background: #eef4ff; border-color: transparent; }
.th-sorted { color: var(--brand); }
/* 箭头位固定占宽：未排序时也占着，点一下不会让整行标题左右挪一下 */
.th-arrow { flex: 0 0 12px; width: 12px; text-align: center; font-size: 10px; opacity: .55; }
.th-sorted .th-arrow { opacity: 1; }
.th-filter {
  flex: 0 0 18px; width: 18px; height: 18px; padding: 0; line-height: 1;
  border: 0; background: none; color: #aab2bf; font-size: 10px; cursor: pointer;
}
.th-filter:hover:not(:disabled) { color: var(--brand); background: #eef4ff; border-color: transparent; }
/* 已筛选的列头点亮并加一个小圆点：翻页之后也能一眼看出条件还挂着 */
.th-filtered { color: var(--brand); }
.th-filtered::after {
  content: ''; position: absolute; margin: -8px 0 0 -3px;
  width: 4px; height: 4px; border-radius: 50%; background: var(--brand);
}
.th-popover {
  position: absolute; top: 100%; left: 0; z-index: 30; min-width: 190px; max-width: 260px;
  margin-top: 2px; padding: 10px; text-align: left;
  background: var(--panel); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 6px 18px rgba(31, 35, 41, .14);
}
/* 靠右的几列向左展开，否则会被主区右边缘裁掉 */
.th-popover-left { left: auto; right: 0; }
.th-pop-list { max-height: 200px; overflow-y: auto; margin-bottom: 8px; }
.th-pop-item { display: flex; align-items: center; gap: 6px; padding: 3px 0; font-size: 13px; color: var(--text); }
.th-pop-row { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; font-size: 12px; }
.th-pop-row input { flex: 1 1 auto; min-width: 0; }
.th-popover .row { margin-top: 4px; }
