/* ═══ 奥瑞奥云盘 · V3（定稿视觉稿 .design-critic/draft-v3.html）
   设计语言：车间档案柜 —— 编号是行的脊柱，归档是盖章，外发是红批。
   颜色给数据不给 chrome；主按钮近黑；蓝色只给交互；红色只给危险/拒绝。
   token 唯一真源：frontend/design-tokens.json                        */

* { box-sizing: border-box; }
:root{
  --bg:#eef2f4; --surface:#fff; --line:#e2e7ea; --line-2:#f0f2f4; --line-s:#e8edef;
  --ink:#14181b; --ink-2:#5b6870; --ink-3:#94a3ab;
  --blue:#1f6feb; --blue-pale:#eaf1fd;
  --seal:#b23a34; --ok:#2f7d68;
  --mono:"JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  --ease:cubic-bezier(.2,.7,.2,1);
}
html,body{height:100%}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font:13px/1.5 -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif;
  -webkit-font-smoothing:antialiased;
}
.hidden{display:none !important}
.muted{color:var(--ink-2)}

/* ── 基础控件 ─────────────────────────────── */
button{font-family:inherit; cursor:pointer}
button:disabled{opacity:.45; cursor:not-allowed}
input,select{
  font-family:inherit; font-size:13px; color:var(--ink);
  padding:7px 10px; border:1px solid var(--line-s); border-radius:7px;
  background:var(--surface); outline:none;
}
input:focus,select:focus{border-color:var(--blue)}
input::placeholder{color:var(--ink-3)}
input[readonly]{color:var(--ink-2); background:#fbfcfc}
button.link{border:none; background:none; color:var(--ink-3); padding:0; font-size:12px}
button.link:hover{color:var(--blue); background:none}

.btn{
  height:32px; padding:0 12px; border-radius:7px; border:1px solid var(--line-s);
  background:var(--surface); color:var(--ink); font-size:13px;
  display:inline-flex; align-items:center; gap:6px; transition:border-color .15s;
}
.btn:hover{border-color:var(--ink-3)}
.btn.pri{background:var(--ink); border-color:var(--ink); color:#fff; font-weight:600}
.btn.pri:hover{background:#000}
.btn.sm{height:26px; padding:0 9px; font-size:12px; border-radius:6px}
.btn.danger{color:var(--seal); border-color:#e8cfcd}
.btn svg{width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.7; flex:none}

/* ═══ 登录（⑨ 档案柜 + 表单）══════════════ */
.login{display:grid; grid-template-columns:1fr 1fr; min-height:100vh}
.login-left{background:var(--bg); position:relative; display:grid; place-items:center; padding:34px}
.login-left svg{width:100%; height:100%; max-height:520px}
.login-footline{
  position:absolute; left:34px; right:34px; bottom:20px; border-top:1px solid #d9e2e6;
  padding-top:12px; font-size:12px; color:var(--ink-2);
  display:flex; justify-content:space-between; font-family:var(--mono);
}
.login-right{display:flex; flex-direction:column; justify-content:center; align-items:center; padding:0 32px}
.login-right > *{width:100%; max-width:360px}
/* 登录页用拟物原图（票 brand-and-sort-01 分工：图标位=原图，界面位=线稿） */
.login-mark{width:84px; height:84px; border-radius:19px; margin:0 auto 18px; display:block}
.login-right h1{font-size:22px; margin:0 0 6px}
.login-right .sub{font-size:13px; color:var(--ink-2); margin-bottom:26px}
.login-wecom{height:42px; justify-content:center; width:100%; font-size:13px}
.login-wecom svg{width:15px; height:15px}
.login-right .hint{font-size:12px; color:var(--ink-3); line-height:1.7; margin-top:14px}
/* 账号密码登录（票 password-login-03）：只在后端开关打开时出现，样式与企微按钮同一套 */
.login-pwd{margin-top:14px; display:flex; flex-direction:column; gap:8px}
.login-pwd input{height:40px; width:100%}
.login-pwd .btn{height:40px; width:100%}
.login-or{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--ink-3)}
.login-or::before, .login-or::after{content:''; height:1px; background:var(--line); flex:1}
.login-dev{margin-top:30px; padding-top:18px; border-top:1px solid var(--line); display:flex; gap:8px}
.login-dev input{flex:1; height:34px}
.login-dev .btn{height:34px}
.login-msg{margin-top:10px; font-size:13px; color:var(--seal); min-height:18px; text-align:center}
@media (max-width:860px){ .login{grid-template-columns:1fr} .login-left{display:none} }

/* ═══ 应用外壳：240px 侧栏 + 主区 ═══════════ */
#app{
  display:grid; grid-template-columns:240px minmax(0,1fr); grid-template-rows:56px minmax(0,1fr);
  grid-template-areas:"top top" "side main"; height:100vh;
}
/* ── 深色顶栏（V3.1 主题：深顶栏 + 白内容区）── */
.topnav{
  grid-area:top; display:flex; align-items:center; gap:16px; padding:0 18px;
  /* 顶栏整条：左白 → 右红（2026-11-06 定案，用户要的"一整条渐变"）。
     左端留白给品牌（红字可读），右端压到 logo 的红 #C52E1E，与品牌标同色收口。
     中间的搜索框落在浅色段，所以用白半透明底 + 深字；最右侧文案落在红段，用白字。 */
  background:linear-gradient(90deg,#fff 0%,#fdf1ef 40%,#e2503b 74%,#C52E1E 100%);
  color:#1b2430; flex:none;
}
/* 品牌：不再单独做白牌（顶栏本身就是白→红渐变，左边已是白的），
   只留 logo + 红字，避免"白牌套在渐变上"多一层边界。
   logo 的红与字色同为 #C52E1E，一个色只出现一处定义。 */
.topnav .brand{padding:4px 6px 4px 0; gap:9px; flex:none; background:transparent;
  color:#C52E1E}
.topnav .brand .mk{width:26px; height:26px; display:block; flex:none}
.topnav .brand .mk img{width:26px; height:26px; display:block; border-radius:50%}
.topnav .brand b{font-size:15.5px; color:#C52E1E; letter-spacing:.02em}
.topnav .brand small{color:rgba(197,46,30,.72); font:10.5px var(--mono); letter-spacing:.06em}
/* 搜索框落在渐变的中段（浅色→过渡色），所以用**白半透明**底：在浅粉和过渡色上
   都够干净，且不会像纯白那样在红段显脏。文字用深色，保证白底上的对比度。 */
.topnav .search{
  margin-left:8px; min-width:0; flex:1 1 auto; max-width:460px; height:34px;
  display:flex; align-items:center; gap:8px; padding:0 11px; border-radius:8px;
  background:rgba(255,255,255,.82); border:1px solid rgba(23,29,34,.10);
  color:#5a6570; font-size:12.5px;
}
.topnav .search:focus-within{border-color:rgba(197,46,30,.45); background:#fff}
.topnav .search svg{width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.7; flex:none}
.topnav .search input{border:none; padding:0; height:100%; flex:1; min-width:0; background:none;
  color:#1b2430; font-size:12.5px; outline:none}
.topnav .search input::placeholder{color:#98a2ab}
.topnav .search kbd{
  font:11px var(--mono); color:#6b7680; border:1px solid rgba(23,29,34,.16); border-radius:4px;
  padding:0 5px; line-height:16px; flex:none;
}
/* 最右侧落在红段：白字（半透明白，别用纯白抢主体） */
.topnav .tnos{margin-left:auto; font:11px var(--mono); color:rgba(255,255,255,.82);
  letter-spacing:.05em; flex:none}

.side{
  grid-area:side; border-right:1px solid var(--line); display:flex; flex-direction:column; min-height:0;
  background:#f8fafb; padding:14px 12px 12px; overflow:auto;
}
.brand{display:flex; align-items:center; gap:9px; padding:2px 8px 18px; flex:none}
.brand .mk{width:26px; height:26px; display:grid; place-items:center; flex:none; color:var(--ink)}
.brand .mk svg{width:26px; height:26px; color:inherit}
.brand b{font-size:16px; letter-spacing:.01em; display:block}
.brand small{display:block; font:11px var(--mono); color:var(--ink-3); letter-spacing:.04em}

.sgrp{font-size:11px; color:var(--ink-3); padding:14px 10px 6px; letter-spacing:.14em; flex:none}
.sgrp-items{display:contents}
.sitem{
  display:flex; align-items:center; gap:9px; height:34px; padding:0 10px;
  border-radius:7px; color:var(--ink-2); font-size:13px; cursor:pointer;
  position:relative; width:100%; text-align:left; border:none; background:none;
  text-decoration:none; flex:none;
}
.sitem svg{width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.6; flex:none}
.sitem:hover{background:#f1f4f5; color:var(--ink)}
.sitem.active{background:var(--blue-pale); color:var(--blue); font-weight:600}
.sitem.dim{color:var(--ink-3); cursor:default}
.sitem.dim:hover{background:none; color:var(--ink-3)}

.side .foot{
  margin-top:auto; border-top:1px solid var(--line); padding:12px 8px 2px;
  display:flex; align-items:center; gap:9px; flex:none;
}
.av{
  width:28px; height:28px; border-radius:50%; background:#e7ecee; display:grid;
  place-items:center; font-size:12px; color:var(--ink-2); flex:none;
}
.foot .who{font-size:13px; line-height:1.25; flex:1; min-width:0; overflow:hidden}
.foot .who small{display:block; font:11px var(--mono); color:var(--ink-3); white-space:nowrap}
.foot .link{flex:none}

/* ── 主区 ─────────────────────────────────── */
.main{grid-area:main; display:flex; flex-direction:column; min-width:0; min-height:0}

/* phead：面包屑 + 30px 页标题 | 常驻搜索 | 计数 */
.phead{padding:20px 24px 12px; display:flex; align-items:flex-end; gap:14px; flex:none}
.phead-l{min-width:0}
.bc{font-size:12px; color:var(--ink-3); margin-bottom:5px; overflow:hidden; white-space:nowrap; text-overflow:ellipsis}
.bc a{color:var(--ink-2); text-decoration:none; cursor:pointer}
.bc a:hover{color:var(--blue)}
.bc span{margin:0 5px}
.phead h1{font-size:30px; line-height:1.1; margin:0; letter-spacing:-.02em; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.phead .search{
  margin-left:auto; display:flex; align-items:center; gap:8px; height:32px;
  min-width:260px; padding:0 10px; border:1px solid var(--line-s); border-radius:7px;
  color:var(--ink-3); font-size:12.5px; background:var(--surface); flex:none;
}
.phead .search:focus-within{border-color:var(--blue)}
.phead .search svg{width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.7; flex:none}
.phead .search input{border:none; padding:0; height:100%; flex:1; min-width:0; background:none; font-size:12.5px}
.phead .search input:focus{border:none}
.phead .search kbd{
  font:11px var(--mono); color:var(--ink-3); border:1px solid var(--line);
  border-radius:4px; padding:0 4px; flex:none;
}
.phead .sum{font:12px var(--mono); color:var(--ink-3); padding-bottom:3px; flex:none; white-space:nowrap}
@media (max-width:980px){ .phead .sum{display:none} .phead .search{min-width:170px} }

/* 工具条 / 批量条：同一槽位（.slot），二选一，行高不变、位次不下坠 */
.slot{
  height:52px; border-top:1px solid var(--line); border-bottom:1px solid var(--line);
  display:flex; align-items:center; gap:8px; padding:0 24px; flex:none; background:var(--surface);
}
.slot-tools{display:flex; align-items:center; gap:8px}
.slot-bulk{display:flex; align-items:center; gap:8px; flex:1; min-width:0}
.slot .right{margin-left:auto; display:flex; align-items:center; gap:10px}
.seg{display:inline-flex; border:1px solid var(--line-s); border-radius:7px; overflow:hidden; height:28px}
.seg span{padding:0 11px; display:grid; place-items:center; font-size:12px; color:var(--ink-2); cursor:pointer}
.seg span:hover{color:var(--ink)}
.seg span[aria-current]{background:var(--ink); color:#fff}
.bulk .cnt, .slot-bulk .cnt{font-size:13px; white-space:nowrap}
.slot-bulk .cnt b{color:var(--blue); font-family:var(--mono); margin:0 3px}
.slot-bulk .x{
  margin-left:2px; color:var(--ink-3); background:none; border:0; font-size:16px; line-height:1; padding:0 4px;
}
.slot-bulk .x:hover{color:var(--ink)}
.slot-bulk .right{flex-wrap:wrap;justify-content:flex-end;display:flex; align-items:center; gap:6px; flex-wrap:nowrap}
.slot-bulk .btn.danger{color:var(--seal); border-color:#e8cfcd; margin-left:8px}

/* 传输抽屉（方案五，票 upload-ui/01）：右上角角标 + 右侧抽屉，不占主列表布局 */
.up-entry{position:fixed; top:14px; right:20px; z-index:59; display:flex; align-items:center; gap:8px;
  background:var(--ink); color:#fff; border:1px solid var(--ink);
  border-radius:8px; padding:7px 12px; font-size:12.5px; cursor:pointer}
.up-badge{background:var(--blue); color:#fff; border-radius:99px; min-width:18px; text-align:center;
  font-size:11px; padding:1px 5px; font-variant-numeric:tabular-nums}
.up-entry.up-fin .up-badge{background:var(--ok)}
.up-drawer{position:fixed; top:0; right:0; bottom:0; width:380px; max-width:100vw; z-index:60;
  background:var(--surface); border-left:1px solid var(--line);
  transform:translateX(100%); transition:transform .22s ease; display:flex; flex-direction:column}
.up-drawer.open{transform:translateX(0)}
.up-d-head{display:flex; align-items:center; gap:10px; padding:12px 16px; border-bottom:1px solid var(--line-s)}
.up-d-head b{font-size:13.5px}
.up-mini{flex:1; height:4px}
.up-total-text{font-family:var(--mono); font-size:11.5px; color:var(--ink-2); white-space:nowrap;
  font-variant-numeric:tabular-nums}
.up-cancel-all{font-size:12px; color:var(--blue); cursor:pointer; white-space:nowrap}
.up-close{color:var(--ink-2); cursor:pointer; padding:0 2px; font-size:13px}
.up-groups{flex:1; overflow:auto; padding-bottom:10px}
.up-group{padding:4px 0}
.up-group.hidden, .up-ops.hidden{display:none}
.up-gtitle{font-size:11.5px; color:var(--ink-3); padding:12px 16px 4px}
.up-gbody{padding:0 16px}
.up-item{padding:9px 0 10px; border-bottom:1px solid var(--line-s)}
.up-item:last-child{border-bottom:0}
.up-row-top{display:flex; align-items:flex-start; gap:8px}
.up-ico{flex:none; width:28px; height:28px; border-radius:6px; background:var(--bg);
  border:1px solid var(--line-s); display:flex; align-items:center; justify-content:center;
  font-size:9.5px; color:var(--ink-2); font-family:var(--mono)}
.up-head{flex:1; min-width:0; display:flex; justify-content:space-between; gap:10px; font-size:12.5px}
.up-head .nm{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.up-head .pct{font-family:var(--mono); color:var(--ink-2); white-space:nowrap;
  font-variant-numeric:tabular-nums}
.up-ops{flex:none}
.up-cancel{font-size:12px; color:var(--blue); cursor:pointer}
.up-dir{font-size:11.5px; color:var(--ink-3); margin:4px 0 0 36px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap}
.up-item .bar{margin:7px 0 0 36px}
.up-item:last-child .bar{margin-bottom:2px}
.bar{height:4px; background:var(--line-s); border-radius:2px; overflow:hidden}
.bar > i{display:block; height:100%; background:var(--blue); width:0; transition:width .2s}
.up-item.done .bar > i{background:var(--ok)}
.up-item.fail .bar > i{background:var(--seal)}
.up-item.fail .up-head .pct, .up-item.fail .up-head .nm{color:var(--seal)}
/* 去重（同内容已存在）/已取消：没建任何东西 → 中性色，不用成功绿（界面不许说谎） */
.up-item.skip .bar > i, .up-item.cancel .bar > i{background:var(--ink-3)}

/* 批次行（票 upload-concurrency/01 定案4）：排队批次一行 + 独立取消 */
.up-batch{display:flex; align-items:center; justify-content:space-between;
  font-size:12px; color:var(--ink-3); padding:6px 2px}
.up-batch .up-cancel{color:var(--ink-3); cursor:pointer}
.up-batch .up-cancel:hover{color:var(--danger)}
.up-queued{font-size:12px; color:var(--ink-3)}
.up-pause{color:var(--ink-2); cursor:pointer; margin-right:10px}
.up-pause:hover{color:var(--ink)}
/* 覆盖成功：旧版进了回收站，谈不上"皆大欢喜"，用中性蓝而不是成功绿 */
.up-item.overwrite .bar > i{background:var(--blue)}
.up-item.overwrite .up-head .pct{color:var(--blue)}
.up-item.skip .up-head .pct, .up-item.cancel .up-head .pct{color:var(--ink-3)}
/* 合并中：条停 95% 脉冲呼吸，不满条（界面不许说谎） */
.up-item .bar.merging > i, .bar.merging > i{animation:up-pulse 1.2s ease-in-out infinite}
@keyframes up-pulse{0%,100%{opacity:1}50%{opacity:.45}}
.up-d-foot{padding:10px 16px; border-top:1px solid var(--line-s); font-size:11.5px; color:var(--ink-3)}

/* 开放接口客户端行（票 open-api/02）：名称|绑定空间|状态|操作 */
.grow.og{grid-template-columns:1fr 1fr 92px 300px}
.grow.og.head{color:var(--ink-2); font-size:12px}
.grow.og .col-r{text-align:right}
/* .au-acts 的规则挂在 .admin-user-row 下，.grow.og 行要单独给布局，否则控件挤成一团 */
.grow.og .au-acts{display:flex; gap:6px; align-items:center; justify-content:flex-end; white-space:nowrap}
.grow.og .au-acts select{max-width:130px}

/* 拖拽上传提示 */
.dropzone{
  position:fixed; inset:0; background:rgba(31,111,235,.06);
  border:2px dashed var(--blue); border-radius:12px; z-index:50;
  display:flex; align-items:center; justify-content:center;
  font-size:16px; color:var(--blue); pointer-events:none;
}

/* ═══ 主列表：Grid 行（禁 table）═══════════
   列序：44 勾选 | 76 编号 | minmax(0,1fr) 名称 | 112 大小 | 138 修改时间 | 104 操作
   （原第 6 列「归档」已撤——后端无归档态字段前不渲染假状态，见 ADR-0001） */
.listwrap{flex:1 1 0; min-height:0; overflow:auto; background:var(--surface)}
.grow{
  display:grid;
  /* 编号列 116px：F-00818(7 字等宽≈60px) + 8px 间隔 + vNN(≈32px) + 两侧 12px padding = 112px，留 4px 余量 */
  grid-template-columns:44px 116px minmax(0,1fr) 112px 138px 104px;
  align-items:center; min-height:54px; border-bottom:1px solid var(--line-2); position:relative;
}
.grow > div{padding:0 12px; min-width:0}
.grow > .c-chk{padding-left:16px;padding-right:0}
.grow:hover{background:#fafbfc}
.grow.sel{background:var(--blue-pale)}
/* 脊柱标记：hover / 选中时左侧 2px 蓝竖标 */
.grow::before{
  content:""; position:absolute; left:0; top:6px; bottom:6px; width:2px;
  background:var(--blue); opacity:0; transition:opacity .16s var(--ease);
}
.grow:hover::before, .grow.sel::before{opacity:1}

/* sticky 表头 */
.ghd{
  position:sticky; top:0; z-index:2; min-height:34px; height:34px;
  background:var(--surface); border-bottom:1px solid var(--line);
  font-size:12px; color:var(--ink-3);
}
.ghd::before{display:none}

/* 编号脊柱：等宽、近黑、nowrap */
.no{font:13px var(--mono); color:var(--ink); letter-spacing:.01em; white-space:nowrap}
/* 编号单元格：编号 + 版本徽标排一行，徽标不许被裁（以前 overflow:hidden 会把 v14 切成 v1(） */
.c-no{display:flex; align-items:center; gap:6px; overflow:visible}
/* 表头排序按钮（票 brand-and-sort-03）：默认与表头同色，激活时用交互蓝 + 箭头 */
.sortbtn{display:inline-flex; align-items:center; gap:4px; background:none; border:none; padding:0;
  font:inherit; color:inherit; cursor:pointer; letter-spacing:inherit}
.sortbtn:hover{color:var(--blue)}
.sortbtn i{font-style:normal; opacity:.45; font-size:10px; line-height:1}
.sortbtn.on{color:var(--blue); font-weight:600}
.sortbtn.on i{opacity:1}

/* 名称列：省略号防折行 */
.nm{display:flex; align-items:center; gap:10px; min-width:0}
.nm b{font-size:13px; font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; flex:1 1 auto}
.nm[data-click]{cursor:pointer}
.nm[data-click]:hover b{color:var(--blue)}
.ico{width:22px; height:22px; flex:0 0 22px; display:grid; place-items:center; color:var(--ink-2)}
.ico svg{width:20px; height:20px; stroke:currentColor; fill:none; stroke-width:1.4}

/* 数字列：等宽 */
.col-r{text-align:right; font:12px var(--mono); color:var(--ink-2); font-variant-numeric:tabular-nums; white-space:nowrap}
.ghd .col-r{font-family:inherit; color:var(--ink-3)}

/* 归档章样式只在 design-tokens.json 保留（未上线元素，ADR-0001）；无后端字段前不渲染任何章 */

/* 勾选框 */
.chk{width:14px; height:14px; border:1.5px solid var(--line-s); border-radius:4px; display:block; cursor:pointer; background:var(--surface)}
.chk:hover{border-color:var(--ink-3)}
.chk.on{background:var(--blue); border-color:var(--blue); position:relative}
.chk.on::after{
  content:""; position:absolute; left:3px; top:1px; width:5px; height:8px;
  border:1.5px solid #fff; border-top:0; border-left:0; transform:rotate(40deg);
}

/* 行内操作：hover 才显形 */
.rowact{display:flex; justify-content:flex-end; gap:2px; opacity:0; transition:opacity .14s}
.grow:hover .rowact, .grow.sel .rowact{opacity:1}
.rowact button, .ctx-menu button{
  height:24px; padding:0 6px; white-space:nowrap; border:1px solid transparent; background:none;
  border-radius:5px; font-size:12px; color:var(--ink-2);
}
.rowact button:hover{background:#f1f4f5; color:var(--ink); border-color:var(--line)}
.rowact button.danger{color:var(--seal)}

/* 空状态：导航和搜索都在，只留文案 */
.emptybox{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:6px; padding:84px 0 96px; text-align:center;
}
.emptybox p{margin:0; font-size:16px; font-weight:600}
.emptybox span{font-size:12.5px; color:var(--ink-3); margin-bottom:14px}

/* 网格视图（先只切样式类：隐藏大小/时间列，行变卡片感） */
.listwrap.gridmode .grow{grid-template-columns:44px 104px minmax(0,1fr) 104px}
.listwrap.gridmode .c-size, .listwrap.gridmode .c-time{display:none}

/* ═══ 分页条 pgr ══════════════════════════ */
/* 筛选条：类型 / 时间 / 上传人 */
.filterbar{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:8px 24px; border-bottom:1px solid var(--line-2); background:#fafbfc;
}
.filterbar select,.filterbar input[type="date"]{
  height:28px; padding:0 8px; font-size:12.5px; border:1px solid var(--line);
  border-radius:6px; background:var(--surface); color:var(--ink); width:auto;
}
.filterbar #f-range{display:inline-flex; align-items:center; gap:6px}
.filterbar .muted{font-size:12px}
.filterbar .link{font-size:12.5px; color:var(--ink-3)}
@media (max-width:860px){
  .filterbar{padding:8px 14px}
  .filterbar select,.filterbar input[type="date"]{flex:1 1 auto; min-width:0}
}
.pgr-btn{
  min-width:26px; height:24px; padding:0 6px; border:1px solid var(--line); background:var(--surface);
  border-radius:6px; color:var(--ink-2); font:12px var(--mono); cursor:pointer;
}
.pgr-btn:hover:not(:disabled){border-color:var(--blue); color:var(--blue)}
.pgr-btn:disabled{opacity:.4; cursor:default}
.pgr{
  flex:none; height:40px; display:flex; align-items:center; gap:16px; padding:0 24px;
  border-top:1px solid var(--line); background:#fbfcfc; font-size:12px; color:var(--ink-2);
}
.pgr b{font-family:var(--mono); font-weight:400; color:var(--ink)}
.pgr a{color:var(--ink-2); text-decoration:none; cursor:pointer}
.pgr a:hover{color:var(--blue)}
.pgr .pages{margin-left:auto; display:flex; align-items:center; gap:2px; font-family:var(--mono); color:var(--ink-3)}
.pgr .pages b{background:var(--ink); color:#fff; border-radius:5px; padding:2px 8px}
@media (max-width:760px){ .pgr a{display:none} }

/* ═══ 右键 / ⋯ 操作菜单 ═══════════════════ */
.ctx-menu{
  position:fixed; z-index:90; min-width:132px; padding:5px;
  background:var(--surface); border:1px solid var(--line); border-radius:8px;
  display:flex; flex-direction:column; gap:1px;
  box-shadow:0 6px 24px rgba(20,24,27,.12);
}
.ctx-menu button{text-align:left; height:30px; padding:0 10px; border-radius:5px}
.ctx-menu button:hover{background:#f1f4f5; color:var(--ink)}
.ctx-menu button.danger{color:var(--seal)}

/* ═══ 弹层（授权 / 分享 / 外发 / 审计 / 管理 / 移交）═══ */
.modal{
  position:fixed; inset:0; background:rgba(20,24,27,.4);
  display:flex; align-items:center; justify-content:center; z-index:60;
}
.modal-card{
  background:var(--surface); border-radius:10px; width:min(560px, 92vw);
  max-height:82vh; overflow:auto; padding:18px 20px;
  border:1px solid var(--line);
}
.modal-card.wide{width:min(720px, 94vw)}   /* 授权面板：搜索框与三个控件要能排一行 */
/* 票 perms-picker-08：卡片原来是 overflow:auto，把绝对定位的下拉列表裁成一条缝，
   用户只能手动往下拖才看得到结果。这里让卡片不裁切，滚动交给「授权列表」自己。 */
#grant-modal .modal-card{overflow:visible; max-height:none}
/* 票 hardening-05：空间授权的添加行换成可搜索选择器后的两个布局决定。
   ① 添加行在面板最底部 → 下拉**向上**展开：这样列表整块落在面板内部，
      不会被 .panel/.modal-card/.pageview 任何一层 overflow 裁掉
      （perms-picker-08 是放开卡片裁剪，这里向上更稳，也不破坏圆角）。
   ② 授权名单自己滚，别把面板撑到屏幕外。 */
/* 授权面板紧贴上面的空间列表会粘在一起（票 ui-2026-11/08）：留一段呼吸空间 */
.sg-host{margin-top:16px}
.sg-host .pick-list{top:auto; bottom:calc(100% + 4px)}
.sg-host .bd{max-height:46vh; overflow:auto}
.sg-host .ft .pick{flex:1 1 200px; min-width:0}
#grant-modal #grant-list{max-height:36vh; overflow:auto}
/* 档位说明（票 perm-roles/08）：说清这一档到底能干什么，两行以内，不抢主体 */
#grant-modal #grant-level-hint{font-size:12px; color:var(--ink-3); line-height:1.6;
  margin:6px 0 10px; min-height:2em}
#grant-modal{
  align-items:flex-start; padding:24px 0;     /* 靠上排：内容高于视口时顶部不会滚不到 */
  overflow:auto;                              /* 整屏兜底：再长也能滚，不会顶出屏幕 */
}
/* 应用内弹层（票 ui-dialog-01）：替代原生 prompt/confirm。
   iOS 企微 webview 不实现 JS 弹窗 → 原生调用静默返回 null/false，用户以为按钮坏了。 */
/* 卡片尺寸只管尺寸：描边/圆角/焦点色/禁用态都用全局 input、button 那套（别抄第二份）。
   小屏与手机键盘：靠上排 + 整屏可滚（同 #grant-modal 的做法），
   否则 detail 长一点或键盘弹起时卡片会顶出屏幕、确定按钮点不到。 */
.ui-dlg-card{width:min(420px, 92vw); max-height:calc(100dvh - 48px)}
#ui-dlg{align-items:flex-start; padding:24px 0; overflow:auto}
#ui-dlg .modal-head{margin-bottom:8px}
.ui-dlg-detail{
  font-size:13px; color:var(--ink-2); line-height:1.55; margin:0 0 12px;
  white-space:pre-wrap; word-break:break-word;
}
.ui-dlg-field{margin-bottom:14px}
.ui-dlg-field input{width:100%; height:40px}          /* 触控高度：手机上手点 */
.ui-dlg-hint{font-size:12px; color:var(--ink-3); margin-top:6px}
/* 就地校验失败（票 preview-stale/02）：弹窗不关，红字就写在它自己的 hint 位。
   以前空名只能「先关弹窗 → 屏幕底部 toast」，弹窗在上方、提示在下方，用户得重新点开重新输。 */
.ui-dlg-hint.ui-dlg-err{color:var(--seal)}
.ui-dlg-actions{display:flex; gap:8px; justify-content:flex-end}
.ui-dlg-actions .btn{min-width:84px; height:40px}      /* 别做 28px 的小按钮 */
/* 危险操作实心红：token 里危险色叫 --seal（没有 --red，写不存在的变量等于没上色） */
.ui-dlg-actions .btn.danger{background:var(--seal); border-color:var(--seal); color:#fff}

.modal-head{display:flex; justify-content:space-between; align-items:center; font-weight:600; font-size:16px; margin-bottom:12px}
.grant-break-row{
  display:flex; gap:8px; align-items:flex-start; font-size:13px; color:var(--ink);
  background:#fbfcfc; border:1px solid var(--line); border-radius:8px;
  padding:10px; margin-bottom:12px; cursor:pointer;
}
.grant-break-row input{margin-top:2px; width:auto}
.grant-item{display:flex; align-items:center; gap:10px; padding:8px 4px; border-bottom:1px solid var(--line-2); font-size:13px}
.grant-item span:first-child{flex:1; min-width:0}
/* 已过期的授权仍留在列表里（便于重新授权），但必须一眼看出是死的（票 perm-roles/07） */
.grant-item.expired{opacity:.55}

/* 空间管理员状态胶囊（票 perm-roles/12）：绿=是，灰=不是；点它切换（切换前必弹确认）。
   取代原来在同一行重复三遍的「管理员」徽标 + 复选框 + 文字。 */
.adm-pill{
  margin-left:8px; height:24px; padding:0 10px; border-radius:12px; cursor:pointer;
  font-size:12px; line-height:22px; font-family:inherit;
  border:1px solid var(--line-s); background:#f2f5f6; color:var(--ink-3);
}
.adm-pill:hover{border-color:var(--ink-3); color:var(--ink-2)}
.adm-pill.on{background:rgba(47,125,104,.10); border-color:rgba(47,125,104,.45); color:var(--ok); font-weight:600}
.adm-pill.on:hover{background:rgba(47,125,104,.16)}
.adm-pill.ro{cursor:default; opacity:.85}
.adm-pill.ro:hover{border-color:var(--line-s); color:var(--ink-3)}
.adm-pill.on.ro:hover{background:rgba(47,125,104,.10); border-color:rgba(47,125,104,.45); color:var(--ok)}


/* ── 行内动作：唯一的"文字按钮"规范（票 ui-2026-11/03）───────────────
   列表行里的动作一律用它，别再各写一套（以前后台用户行是裸文字、授权行是红字、
   弹层是描边按钮，同一种东西三种长相）。
   规则：无边框无底色；hover 才出现浅灰底；圆角 4px；危险动作用 .danger 只改字色。
   弹层主按钮（.btn / .btn.pri）与工具条保持不变。 */
.act{
  appearance:none; border:0; background:none; cursor:pointer; padding:4px 7px;
  border-radius:4px; font:inherit; font-size:12.5px; color:var(--ink-3); line-height:1;
  white-space:nowrap;
}
.admin-user-row .au-acts .act, .grow .act{border:0; background:none}
.act:hover{background:rgba(23,29,34,.06); color:var(--ink)}
.act:focus-visible{outline:2px solid rgba(23,29,34,.25); outline-offset:1px}
.act:disabled{color:var(--ink-4); cursor:not-allowed; background:none}
/* 红字只给**不可逆**动作：删除 / 彻底删除 / 禁用账号 / 解散空间 */
.act.danger{color:var(--seal)}
.act.danger:hover{background:rgba(197,46,30,.08); color:var(--seal)}

.expired-tag{color:var(--seal)}
.grant-empty{padding:10px 4px; color:var(--ink-3)}
.grant-add{display:flex; gap:8px; margin-top:14px; flex-wrap:wrap; align-items:center}
/* 选择器与控件同排（弹窗已加宽到 720）：下拉列表在整行下方展开，
   盖住的是下面的授权列表，而不是"类型/档位/添加授权"这三个按钮 */
.grant-add .pick{flex:1 1 200px; min-width:0}
.share-link-row{display:flex; gap:8px; margin:8px 0 12px}
.share-link-row input{flex:1; min-width:0; font-size:13px}
.grant-item button.danger, button.danger{color:var(--seal)}


/* ── 批量移动弹层（选目标目录）── */
.move-body{padding:4px 0 0}
.move-spaces{display:flex; gap:6px; margin-bottom:10px}
.move-spaces .seg-btn{
  height:28px; padding:0 12px; border:1px solid var(--line); background:var(--surface);
  border-radius:7px; font-size:12.5px; color:var(--ink-2); cursor:pointer;
}
.move-spaces .seg-btn.active{background:var(--blue-pale); border-color:var(--blue); color:var(--blue)}
.move-crumb{font-size:12.5px; color:var(--ink-3); margin-bottom:8px; min-height:18px}
.move-crumb a{color:var(--blue); cursor:pointer; text-decoration:none}
.move-dirs{
  max-height:46vh; overflow:auto; border:1px solid var(--line); border-radius:8px;
}
.move-dir{
  display:flex; align-items:center; gap:8px; padding:8px 12px; font-size:13px;
  border-bottom:1px solid var(--line-2); cursor:pointer;
}
.move-dir:last-child{border-bottom:0}
.move-dir:hover{background:#fafbfc}
.move-dir .no{font-family:var(--mono); font-size:12px; color:var(--ink-3); width:64px}
.move-dir .arrow{margin-left:auto; color:var(--ink-3)}
.move-empty{padding:16px 12px; color:var(--ink-3); font-size:13px}
.move-foot{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-top:12px}
.move-foot .muted{font-size:12.5px; color:var(--ink-3)}

/* 审计日志 */
.audit-card{width:min(1080px, 95vw)}
.audit-filters{display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:10px}
/* 审计页人员筛选（票 perms-picker-06）：固定宽，别把一行撑开导致换行跳动 */
/* 成员与部门（票 admin-ui-2026-11/05 + 05b 视觉定稿 docs/design/members-preview-v3.png）：
   左树右表。树人数 = 自己 + 所有子孙（与后端递归同一口径）；
   行内动作 hover 才出（危险色不当装饰）；0 文件显示「—」。 */
.members-layout{display:flex; gap:12px; align-items:flex-start; min-height:0}
.dept-tree-box{
  flex:0 0 240px;              /* JS applyDeptSize() 会覆盖成拖过的宽度 */
  border:1px solid var(--line); border-radius:10px;
  background:var(--surface); display:flex; flex-direction:column; min-height:0;
  max-height:52vh; overflow:hidden;   /* 拖高之后别让底部按钮溢到边框外面 */
}
.dept-tree-head{
  display:flex; align-items:center; justify-content:space-between;
  padding:8px 12px; border-bottom:1px solid var(--line); font-size:12px; color:var(--ink-2);
}
.dept-tree{flex:1 1 auto; min-height:0; overflow:auto; padding:6px 4px}
.dtree-row{
  display:flex; align-items:center; gap:6px; height:30px; padding-right:8px;
  border-radius:6px; cursor:pointer; font-size:13px; min-width:0;
}
.dtree-row:hover{background:var(--line-2)}
.dtree-row.on{background:var(--blue-pale); color:var(--blue)}
.dtree-row.on .dtree-count{color:var(--blue)}
.dtree-caret{flex:none; width:12px; color:var(--ink-3); font-size:11px; text-align:center}
.dtree-name{flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.dtree-count{flex:none; font-family:var(--mono); font-size:11px; color:var(--ink-3)}
.dtree-warn .dtree-name{color:var(--ink-2)}
/* 未分配部门与真部门之间的分隔线（它是"另一种视图"，不是第 N 个部门） */
.dtree-div{height:1px; background:var(--line); margin:6px 6px}
/* 树是空的时必须说句话：整块留白 = 用户以为页面坏了（票 06） */
.dtree-empty{padding:10px 12px; font-size:12px; color:var(--ink-3); line-height:1.6}
.dtree-empty.retry{color:var(--blue); cursor:pointer}   /* 重试是交互，不是危险 */
.dtree-empty.retry:hover{text-decoration:underline}

/* 拖拽条（票 06）：6px 命中区，看不见但抓得住；hover 才给一点色 */
/* 常态就给一条可见的线（否则用户还是"看不到能拖"），hover/拖动时加深。
   align-self:stretch 必须有：父容器 align-items:flex-start 会把无内容元素压成高度 0。 */
.dept-resize-w{flex:0 0 6px; align-self:stretch; min-height:120px; cursor:col-resize;
  border-radius:3px; background:var(--line); touch-action:none}
.dept-resize-h{flex:0 0 8px; cursor:row-resize; touch-action:none; position:relative}
.dept-resize-h::after{content:''; position:absolute; left:12px; right:12px; top:3px;
  height:2px; border-radius:2px; background:var(--line-2)}   /* 常态一条细线，不像杂线 */
.dept-resize-h:hover::after, .dept-resize-h.dragging::after{background:var(--ink-3)}
.dept-resize-w:hover, .dept-resize-h:hover, .dept-resize-w.dragging,
.dept-resize-h.dragging{background:var(--ink-3)}
/* 拖动时禁用选中，否则整页文字被刷成蓝色 */
body.rs-w, body.rs-h{user-select:none}
body.rs-w{cursor:col-resize}
body.rs-h{cursor:row-resize}
/* 左栏底部：低频操作收成一个按钮，点开才展开 */
.dept-tree-foot .btn{width:100%}

/* 范围行：蓝 pill = 按部门看；灰 pill = 全公司搜索；× 回到全员 */
.members-filters #admin-kw{flex:1 1 auto; height:34px}
.au-scope-row{display:flex; align-items:center; gap:8px; margin:0 0 8px; min-height:22px}
.au-scope{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--ink-2)}
.au-pill{background:var(--blue-pale); color:var(--blue); border-radius:999px;
  padding:2px 10px; font-size:12px; display:inline-flex; align-items:center; gap:6px}
.au-pill.search{background:var(--line-2); color:var(--ink-2)}
.au-pill-x{opacity:.65; cursor:pointer; font-size:13px; line-height:1;
  border:0; background:none; padding:0 2px; color:inherit; font-family:inherit}
.au-pill-x:hover{opacity:1}
.au-scope-n{font-size:12px; color:var(--ink-2); font-family:var(--mono)}
.au-scope-row #admin-user-summary{margin-left:auto; font-size:12px}

/* 成员行：姓名/工号竖排、部门加宽、文件列完整不换行、动作 hover 才出 */
.admin-user-row.has-acts{
  /* 姓名列给足最小宽（左栏可以拖到 560px，主区被挤窄时也不能把人名截成「刘付维明·阿卜…」）；
     部门列同理。文件列固定 168px 不换行。 */
  /* 右对齐的 flex 在窄容器里会**向左溢出**（盖住状态徽标），所以各列都要有可收缩的最小宽：
     150 + 100 + 120 + 84 + 动作(~190) = 644px，主区再窄也放得下。 */
  grid-template-columns:minmax(150px,1.35fr) minmax(100px,1fr) minmax(120px,168px) 84px auto;
  min-height:52px;
}
.admin-user-row .au-who{min-width:0; line-height:1.25}
.admin-user-row .au-nm{display:block; font-size:13px; font-weight:500;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.admin-user-row .au-no{display:block; margin-top:2px; font-family:var(--mono);
  font-size:11px; color:var(--ink-3)}
.admin-user-row .au-dept{color:var(--ink-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.admin-user-row .badge-off{white-space:nowrap; justify-self:center}
.admin-user-row .au-usage{font-family:var(--mono); font-size:12px; color:var(--ink-2);
  text-align:right; white-space:nowrap}
.admin-user-row .au-acts{display:flex; gap:6px; justify-content:flex-end;
  white-space:nowrap; flex-wrap:nowrap;
  visibility:hidden}     /* visibility 才会取消命中测试：opacity:0 在触屏上是"隐形可点" */
.admin-user-row:hover .au-acts, .admin-user-row:focus-within .au-acts{visibility:visible}
@media (hover: none){    /* 车间平板/企微内置浏览器没有 hover → 常显，别让人找不到 */
  .admin-user-row .au-acts{visibility:visible}
}
.admin-user-row:hover{background:#fafbfc}

/* 分页里的每页条数 */
.size-sel{height:26px; padding:0 6px; font-size:12px; border-radius:6px}
.dept-tree-foot{border-top:1px solid var(--line); padding:8px; display:flex;
  flex-direction:column; gap:6px; flex:0 0 auto;   /* 不许被 flex 压缩：树再长按钮也要完整可见 */}
.members-main{flex:1 1 auto; min-width:0}

/* 审计筛选分组（票 admin-ui-2026-11/04）：两个人员选择器各管一件事
   （谁做的 / 被操作的人），平铺一行时会被误读成重复 → 加组标签 + 允许折行 */
.audit-filters.grp{flex-wrap:wrap; row-gap:8px}
.audit-filters .fgroup{display:flex; align-items:center; gap:6px; min-width:0}
.audit-filters .fgroup-end{margin-left:auto}
.audit-filters .fl{font-size:12px; color:var(--ink-3); white-space:nowrap}
.audit-filters .fgroup .pick{flex:0 0 220px}
.audit-filters .fgroup input[type="datetime-local"]{flex:0 0 180px}
.audit-filters .fgroup input#audit-file{flex:0 0 150px}
.audit-filters .pick{flex:0 0 190px; min-width:0}
.audit-filters .pick.pick-dept{flex:0 0 240px}   /* 部门名长 + 上级部门 */
.audit-filters select,.audit-filters input{padding:7px 9px; font-size:13px; width:auto}
.audit-filters input[type="datetime-local"]{font-family:var(--mono); font-size:12px}
.audit-summary{font-size:12px; margin-bottom:8px; font-family:var(--mono)}
.audit-list{max-height:56vh; overflow:auto; border:1px solid var(--line); border-radius:8px}
/* 列宽真源（票 audit-2026-11/25）：表头与数据行共用同一个 .audit-cols，
   加列只改这里一处，表头不会错位 */
.audit-cols{grid-template-columns:138px 88px 104px 88px 1fr 110px}   /* 时间/操作人/动作/对象/文件/IP */
.audit-row{
  display:grid; gap:8px;
  padding:8px 12px; font-size:13px; border-bottom:1px solid var(--line-2); align-items:center;
}
/* 表头：吸顶 + 不透明底色（否则内部滚动时行从表头下面透出来） */
.audit-head{
  position:sticky; top:0; z-index:2; background:#fff;
  font-size:12px; color:var(--ink-3); font-weight:500;
  border-bottom:1px solid var(--line);
}
/* .audit-row:hover 特异性更高，不显式覆盖的话鼠标一过表头就变色 */
.audit-head:hover{background:#fff}
.audit-row:last-child{border-bottom:0}
.audit-row:hover{background:#fafbfc}
.audit-time{color:var(--ink-2); font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:12px; white-space:nowrap}
.audit-pager{display:flex; gap:10px; align-items:center; justify-content:flex-end; margin-top:10px}
.audit-pager button{
  padding:6px 12px; border:1px solid var(--line-s); background:var(--surface);
  border-radius:7px; cursor:pointer; font-size:12.5px;
}
.audit-pager button:disabled{opacity:.45; cursor:default}

/* 管理后台 */
.admin-card{width:min(1120px, 96vw)}
.admin-tabs{display:flex; gap:18px; margin-bottom:14px; border-bottom:1px solid var(--line)}
.admin-tab{
  padding:0 2px 10px; border:none; background:none; font-size:13px; color:var(--ink-3);
  position:relative;
}
.admin-tab.active{color:var(--ink); font-weight:600}
.admin-tab.active::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:2px; background:var(--ink);
}
.admin-card-item{background:#fbfcfc; border:1px solid var(--line); border-radius:8px; padding:12px 14px}
.admin-cards{display:grid; grid-template-columns:repeat(3, 1fr); gap:10px}
.admin-card-value{font:16px var(--mono); margin-top:4px}
.badge-ok{color:var(--ok); background:rgba(47,125,104,.08); border-radius:6px; padding:2px 8px; font-size:12px; text-align:center}
.badge-off{color:var(--ink-2); background:var(--line-2); border-radius:6px; padding:2px 8px; font-size:12px; text-align:center}
/* 列模板只在上面 .admin-user-row.has-acts（:486）定义**一处**。
   原来这里还有一份 `1.2fr 1fr 1.2fr 70px …`，特异性相同但更靠后 → 静默覆盖掉新版，
   部门列反而比文件列窄（用户要求的"部门加宽/文件加宽"两条都没生效，票 05b 评审 #S2）。 */
.usage-row{grid-template-columns:1fr 220px 150px}
.usage-bar{background:var(--line-2); border-radius:3px; height:6px; overflow:hidden}
.usage-fill{background:#9fb6bd; height:100%}
/* 注意：不要用全局 button.danger 加背景 —— 会压坏文件行文字按钮 */
/* 回收磁盘（票 admin-ui-2026-11/06，按 docs/design/reclaim-preview.png 定稿）：
   ① 与上一模块（占用 Top 列表）留 22px + 同级分组标题，读起来是两个段落而不是一个附表
   ② 红色只给危险：数字与清理按钮；说明文字一律灰
   ③ 未预览时结果块整块不出现 —— 画一个禁用的「立即清理」等于让人去点
   ④ 一行控件统一 32px 高，底边对齐 */
.sec-title.reclaim-title{margin:22px 0 6px}
#admin-reclaim .reclaim-hint{padding:12px 16px 0; max-width:62ch}
#admin-reclaim .reclaim-filters{padding:12px 16px 14px; align-items:center}
#admin-reclaim .reclaim-filters input{height:32px; padding:0 10px; width:88px}
#admin-reclaim .reclaim-filters button{height:32px; padding:0 14px; border-radius:7px; font-size:13px;
  border:1px solid var(--line); background:var(--surface); color:var(--ink)}
.reclaim-result{
  margin:0 16px 12px; border:1px solid var(--line); border-radius:8px; background:#fcfdfd;
  padding:12px 14px; display:flex; align-items:center; gap:14px;
}
.reclaim-body{flex:1 1 auto; min-width:0}      /* 数字与细节上下堆叠，不并排读成两栏 */
.reclaim-num{font:17px/1.4 var(--mono); font-weight:600}
.reclaim-num u{text-decoration:none; color:var(--seal)}   /* 要删的数字是危险色 */
.reclaim-meta{font-size:12px; color:var(--ink-2); line-height:1.6; margin-top:2px}
.reclaim-result .btn{margin-left:auto; height:34px; padding:0 16px; flex:none; align-self:center}
.reclaim-foot{font-size:12px; color:var(--ink-3); padding:0 16px 14px; max-width:62ch}
/* 清理结果留痕（票 admin-ui-2026-11/07）：成功绿、失败红，一直留在页面上直到下次操作 */
.reclaim-state{font-size:12px; padding:0 16px 10px; max-width:62ch}
.reclaim-state.ok{color:var(--ok)}
.reclaim-state.fail{color:var(--seal)}

/* ═══ 外发提取页（公开，⑩：验证前模糊锁定）═══ */
.ext-page{
  position:fixed; inset:0; background:var(--bg); z-index:70;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:16px; padding:24px; overflow:auto;
}
.ext-card{
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:24px; width:min(420px, 94vw);
}
.ext-bar{
  display:flex; align-items:center; gap:9px; padding-bottom:14px;
  border-bottom:1px solid var(--line); margin-bottom:18px;
  font-size:12px; letter-spacing:.04em; color:var(--ink-2);
}
.ext-bar .mk{width:22px; height:22px; border:1.5px solid var(--ink); border-radius:5px; flex:none}
.ext-bar .ext-name{font-weight:600; font-size:14px; color:var(--ink); flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.ext-prev{
  position:relative; height:170px; overflow:hidden; border:1px solid var(--line);
  border-radius:8px; background:#fbfcfc; margin-bottom:18px;
}
.ext-sheet{
  position:absolute; inset:14px 70px 14px 14px; filter:blur(6px); opacity:.5;
  background:
    linear-gradient(#cfd8dd 0 0) 0 0/44% 9px no-repeat,
    repeating-linear-gradient(180deg, #dfe6ea 0 1px, transparent 1px 13px);
}
.ext-lock{
  position:absolute; inset:0; z-index:3; display:flex; align-items:center; justify-content:center;
  gap:8px; font-size:12px; color:var(--ink-2); background:rgba(255,255,255,.55);
}
.ext-lock svg{width:18px; height:18px; stroke:currentColor; fill:none; stroke-width:1.6}
.ext-card h3{font-size:16px; margin:0 0 14px}
.ext-lbl{display:block; font-size:12px; color:var(--ink-2); margin-bottom:6px}
.ext-card input{
  width:100%; height:40px; font:16px var(--mono); letter-spacing:.34em;
  text-align:center; margin-bottom:12px;
}
.ext-card input::placeholder{letter-spacing:.3em; color:#c3ccd1}
.ext-full{width:100%; height:44px; justify-content:center}
.ext-msg{margin-top:10px; font-size:13px; color:var(--seal); min-height:18px; text-align:center}
.ext-note-list{font-size:12px; color:var(--ink-3); margin-top:12px; display:flex; flex-direction:column; gap:8px}

/* 外发预览 + 水印 */
.ext-view{
  width:min(1040px, 94vw); height:88vh; display:flex; flex-direction:column;
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:14px 16px; box-sizing:border-box;
}
.ext-view .ext-bar{border-bottom:1px solid var(--line)}
.ext-body{
  flex:1; overflow:auto; margin-top:10px; display:flex; align-items:flex-start;
  justify-content:center; position:relative;
}
.ext-img{max-width:100%; max-height:100%; object-fit:contain}
.ext-frame{width:100%; height:100%; border:0}
.ext-note{font-size:12px; margin-top:8px; text-align:center}
.ext-wm{
  position:fixed; inset:0; z-index:9999; pointer-events:none; display:none; flex-wrap:wrap;
  /* 间距必须写在样式表里，不能写内联样式：防篡改守卫每 800ms 会清空 el.style.cssText，
     写内联的话水印会"先正常 几秒、然后挤成一团"（票 watermark-04）。
     这两个数字与 app.js 的 WM_GAP_X / WM_GAP_Y 必须一致（frontend-wiring-test ⑤v 校验）。 */
  gap:90px 120px;
  align-content:flex-start; padding:24px; overflow:hidden; user-select:none;
}
.ext-wm.on{display:flex}
.ext-wm span{
  white-space:nowrap;              /* 任何文案都不许折行（票 watermark-03：长文案曾被挤成三行） */
  font-size:14px; color:var(--ink); opacity:.13; transform:rotate(-22deg);
  font-family:var(--mono);
}
/* 水印必须"在浅底和深底上都能看见"，所以不能只用一种颜色：
   - 外发页（白底）→ 默认近黑字
   - 内部预览（深色遮罩 + 图可能是白底）→ .light：白字 + 深色描边
   只反白会栽在浅色图片上，只深色会栽在暗色背景上（两个都实际踩过） */
.ext-wm.light span{
  color:#fff; opacity:.42; font-weight:600;
  text-shadow:0 0 3px rgba(0,0,0,.75), 0 1px 2px rgba(0,0,0,.55);
}
/* ═══ 预览弹层 viewer ══════════════════════ */
.viewer{position:fixed; inset:0; background:rgba(20,24,27,.86); z-index:80; display:flex; flex-direction:column}
.viewer-bar{
  display:flex; align-items:center; justify-content:space-between;
  padding:12px 18px; color:#fff; font-size:14px; gap:12px;
}
.viewer-bar span:first-child{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* 历史版本徽标（票 file-versioning/05）：预览层必须一眼看出"这不是当前内容"，
   否则用户会把旧版当新版（界面不许说谎）。柔和琥珀色，不抢内容注意力 */
#viewer-ver{
  flex:none; padding:3px 9px; border-radius:6px; font-size:12px; letter-spacing:.01em;
  background:rgba(255,196,102,.18); border:1px solid rgba(255,196,102,.5); color:#ffd79a;
}
#viewer-ver.hidden{display:none}
.viewer-actions{display:flex; gap:10px; flex:none}
.viewer-actions button{
  height:30px; padding:0 12px; border-radius:7px; border:1px solid transparent;
  background:rgba(255,255,255,.14); color:#fff; font-size:13px;
}
.viewer-actions button:hover{background:rgba(255,255,255,.24)}
.viewer-body{
  flex:1; overflow:auto; display:flex; align-items:center; justify-content:center;
  padding:0 18px 18px;
}
.viewer-body img,.viewer-body video{max-width:100%; max-height:100%; border-radius:6px}
.viewer-body iframe{width:100%; height:100%; border:none; border-radius:6px; background:#fff}
.viewer-body pre{
  background:#fff; padding:18px; border-radius:8px; width:100%; height:100%;
  overflow:auto; margin:0; font-size:13px; line-height:1.6;
}
.viewer-tip{color:#c9cdd4; font-size:14px}

/* toast */
.toast{
  position:fixed; left:50%; bottom:36px; transform:translateX(-50%);
  background:var(--ink); color:#fff; padding:10px 20px; border-radius:8px;
  font-size:13.5px; z-index:99;
}

/* ── 移动端（企微内置浏览器 375~760）：侧栏抽屉 + 列表精简 ── */
.mbtn{display:none}
.side-mask{display:none}
@media (max-width:760px){
  .mbtn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;
    flex:none;border:1px solid var(--line);border-radius:7px;background:var(--surface);
    color:var(--ink-2);cursor:pointer;margin-right:10px}
  .mbtn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.7}

.side{position:fixed;z-index:60;top:0;left:0;bottom:0;width:240px;
    transform:translateX(-105%);transition:transform .2s ease;overflow-y:auto}
  .side.open{transform:none;box-shadow:0 0 0 100vmax rgba(20,24,27,.4)}
  #app{grid-template-columns:minmax(0,1fr) !important; grid-template-areas:"top" "main" !important}
  .main{width:100%;min-width:0;overflow-x:hidden}
  .topnav{padding:0 12px; gap:10px; height:52px}
  .topnav .brand small{display:none}
  .topnav .brand b{font-size:14.5px}
  .topnav .search{max-width:none; height:32px}
  .topnav .tnos{display:none}
  .mbtn{background:rgba(255,255,255,.9); border-color:rgba(23,29,34,.12); color:#1b2430}
  .phead{flex-wrap:wrap;gap:8px}
  .phead .phead-l{flex:1 1 100%;min-width:0}
  .phead h1{font-size:22px}
  .phead .search{min-width:0;flex:1 1 auto}
  .slot{height:auto;min-height:46px;padding:8px 14px;flex-wrap:wrap}
  .slot .btn{height:32px;padding:0 10px;font-size:12.5px}
  .slot .seg{height:26px}
  .slot .seg span{padding:0 8px}
  .phead{padding:12px 14px !important}
  .pgr{padding:10px 14px !important}
  .slot .btn,.slot button{white-space:nowrap}
  /* 列表精简：保留 勾选/编号/名称/操作，隐藏大小/修改时间 */
  .grow{grid-template-columns:36px 108px minmax(0,1fr) 56px !important}
  .listwrap:not(.gridmode) .grow>*:nth-child(4),
  .listwrap:not(.gridmode) .grow>*:nth-child(5){display:none}
  .pgr{flex-wrap:wrap;gap:6px}
}

/* ── 文件类型配色（V3.1：图标带色，界面其余保持克制）── */
.ico svg{stroke-width:1.5}
.ico .i-dir{stroke:#c08a2e}
.ico .i-pdf{stroke:#c0392b}
.ico .i-xls{stroke:#2f7d68}
.ico .i-doc{stroke:#2f6fed}
.ico .i-ppt{stroke:#d2691e}
.ico .i-zip{stroke:#7c8891}
.ico .i-img,.ico .i-video,.ico .i-audio{stroke:#7b5bd6}

/* ── 管理类整页视图（原弹窗改造：侧栏/顶栏常驻，内容区整块切换）── */
.pageview{
  position:fixed; top:56px; left:240px; right:0; bottom:0; z-index:40;
  background:var(--bg); overflow:auto; padding:22px 26px 40px;
}
.pageview > .modal-card{
  width:100%; max-width:1180px; max-height:none; margin:0 auto;
  min-height:calc(100vh - 56px - 22px - 40px); display:flex; flex-direction:column;
  background:var(--surface); border:1px solid var(--line); border-radius:12px;
  padding:22px 26px; box-shadow:none;
}
.pageview .modal-head{padding-bottom:14px; border-bottom:1px solid var(--line); margin-bottom:16px}
.pageview .modal-head span{font-size:20px; font-weight:600; letter-spacing:.01em}
/* 整页视图里用主界面那套 phead（票 ui-2026-11/06）：与页面同宽、不留额外上边距 */
.pageview .phead{padding:0 0 12px; align-items:flex-end}
.pageview .phead h1{font-size:30px}
.pageview .phead .sum{margin-left:14px}
@media (max-width:760px){ .pageview .phead h1{font-size:22px} }
.pageview .audit-list{max-height:none}
@media (max-width:760px){
  .pageview{left:0; top:52px; padding:14px 12px 30px}
  .pageview > .modal-card{padding:16px 14px; border-radius:10px}
  .pageview .modal-head span{font-size:17px}
}

/* ── 共享空间：侧栏分组与条目（票 spaces-06）───────────────── */
/* 空间条目缩进一级，长名省略号（240px 侧栏放不下长空间名） */
.sitem.sub{padding-left:26px}
.sitem .sname{overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0}
.sgrp.sgrp-inline{padding-top:8px}

/* 管理后台「空间管理」列表：Grid 行，编号是脊柱，数字走 mono */
/* 票 admin-ui-2026-11/01：以前这里是 .sphead（静态表头）+ .grow.sp.ghead（JS 生成）两套列模板，
   结果屏幕上两个表头、宽度还不对齐。现在只保留 .grow.sp 那套（它在 .panel 里带「共享空间 N 个」标题）。 */
.sprow{min-height:50px; border-bottom:1px solid var(--line-2); font-size:13px}
.sprow:hover{background:#fafbfc}
.sprow > div{padding:0 10px; min-width:0}
.sprow .sp-no{font-family:var(--mono); color:var(--ink); font-size:12px}
.sprow .sp-name{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.sprow .sp-num{font-family:var(--mono); font-size:12px; color:var(--ink-2); text-align:right}
.sprow .sp-acts{display:flex; gap:6px; justify-content:flex-end; flex-wrap:wrap}
.sprow .sp-acts .btn{padding:3px 8px; font-size:12px}
/* 档位徽标：灰底，不引入新的语义色 */
.badge{
  display:inline-block; padding:1px 7px; border-radius:999px; font-size:11px;
  background:var(--line-2); color:var(--ink-2); font-family:var(--mono);
}
.badge.ok{background:var(--blue-pale); color:var(--blue)}

/* 授权面板：面板感（surface + 圆角 + 内边距），不靠阴影 */
.spanel{
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:14px 16px; margin-top:12px;
}
.spanel h3{margin:0 0 10px; font-size:16px; font-weight:600}
.spanel .hint{font-size:12px; color:var(--ink-3); margin-bottom:10px}

@media (max-width: 860px){
  .sprow{grid-template-columns:1fr; gap:4px; padding:10px 0}
  .sprow > div{padding:0 10px}
  .sprow .sp-num{text-align:left}
  .sprow .sp-acts{justify-content:flex-start}
}

/* ── 通用排版小节（票 ui-polish-01）：把散落的内联样式收进 class ── */
.sec-title{font-size:13px; color:var(--ink-2); margin:10px 0 6px}
/* 同一页的第二个分组标题：与上面那个**同一字号/颜色**，只多留点上间距（票 ui-2026-11/07） */
.sec-title.usage-sub{margin-top:22px}
.hint-line{font-size:13px; color:var(--ink-2); margin-bottom:8px}
.hint-sm{font-size:12px; color:var(--ink-3); line-height:1.6}
.warn-line{font-size:12px; color:var(--ink-3); line-height:1.6; margin-top:8px}
.field-row{display:flex; align-items:center; gap:6px; font-size:13px; color:var(--ink-2)}
.inline-check{display:flex; align-items:center; gap:6px}
.code-input{max-width:120px; text-align:center; letter-spacing:4px}
.num-input{width:90px}

/* 空状态：只留一个主 CTA（规范 §交互规则 5） */
.emptybox .cta{margin-top:12px}
.emptybox .cta .btn{font-size:13px}

/* ── 从 V3 视觉稿移植的组件（票 ui-align-02）────────────────────
   新加的共享空间界面要和文件列表"长得一样"：同一副 .grow 骨架、
   同样的 .ico/.nm/.rowact 行内结构、同样的 .chip/.panel 容器。
   全部走 token，禁止阴影与渐变（规范 §设计 token）。 */

/* 行内小注（作者/部门/说明等次要信息） */
.nm .meta, .meta{font-size:12px; color:var(--ink-3); margin-left:14px;
  white-space:nowrap; flex:none}

/* 胶囊标签：中性灰底，mgr 表示主管/管理员（近黑加粗），off 表示停用 */
.chip{display:inline-block; font-size:12px; padding:1px 8px; border-radius:20px;
  border:1px solid var(--line-s); color:var(--ink-2); line-height:1.6; white-space:nowrap}
.chip.mgr{border-color:var(--line-s); color:var(--ink); background:#fbfcfc; font-weight:600}
.chip.off{border-color:var(--line-2); color:var(--ink-3); background:#fafbfb}

/* 面板：卡片式容器（标题条 + 内容），靠边框分层不靠投影 */
.panel{border:1px solid var(--line); border-radius:10px; overflow:hidden; background:var(--surface)}
.panel .hd{height:40px; display:flex; align-items:center; gap:10px; padding:0 16px;
  font-size:12px; color:var(--ink-2); border-bottom:1px solid var(--line); background:#fbfcfc}
.panel .hd .r{margin-left:auto; font-family:var(--mono); color:var(--ink-3)}
.panel .bd{padding:0}
.panel .ft{padding:12px 16px; border-top:1px solid var(--line); background:#fbfcfc}

/* 共享空间列表行：沿用 .grow 骨架
   列数必须与 JS 里 row.append(...) 的单元格数**一一对应**（少定义一列，多出来的单元格
   会掉进隐式列 → 数字列飘走、操作按钮被挤出并被裁切。2026-11-02 实际踩到） */
.grow.sp{grid-template-columns:76px minmax(0,1fr) 68px 56px 84px 96px 88px 268px}
/* 空间行有 5 个动作（授权/外发/说明/改名/解散），列宽 200px 时第 4 个就把「解散」挤到第二行
   （票 ui-2026-11/08）。加宽到 268px 并禁止折行，按钮再多就横向省略而不是掉行 */
.grow.sp .rowact, .grow.sp .rowact-cell{flex-wrap:nowrap; white-space:nowrap}
.grow.sp .no{font-family:var(--mono); color:var(--ink); font-size:12px}
.grow.sp .col-r{text-align:right; font-family:var(--mono); font-size:12px; color:var(--ink-2)}
.grow.sp .sp-owner{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:12px; color:var(--ink-2)}
.grow.sp .rowact-cell{justify-self:end}
.grow.sp .rowact{flex-wrap:wrap; white-space:nowrap; row-gap:2px}
/* 列标题：与行同一副列模板，sticky 在面板滚动区顶部 */
.grow.sp.ghead{min-height:32px; font-size:12px; color:var(--ink-3); background:var(--surface);
  border-bottom:1px solid var(--line); position:sticky; top:0; z-index:1}
.grow.sp.ghead::before{display:none}
.grow.sp.ghead .col-r{font-family:inherit; color:var(--ink-3)}

/* 空间授权行：人/部门 + 档位下拉 + 删除 */
.grow.gp{grid-template-columns:minmax(0,1fr) 214px 72px; min-height:46px}
/* 档位下拉 + 管理员胶囊排一行（票 perm-roles/12）：原来列宽 132px 放不下，
   胶囊被挤到下一行，看起来像"掉下来了" */
.grow.gp > div:nth-child(2){display:flex; align-items:center; gap:6px; white-space:nowrap;
  min-width:0}
.grow.gp > div:nth-child(2) select{min-width:0}

/* 手机端：行改两列，避免横向溢出 */
@media (max-width: 860px){
  .grow.sp, .grow.sp.ghead{grid-template-columns:76px minmax(0,1fr); grid-auto-rows:auto; padding:8px 0}
  .grow.sp.ghead{display:none}
  .grow.sp .col-r{text-align:left}
  .grow.sp .rowact-cell{grid-column:1 / -1; padding-top:6px; justify-self:start}
  .grow.gp{grid-template-columns:minmax(0,1fr) 186px 64px}
}


/* ── 可搜索选择器（票 perms-picker-05）──────────────────────────
   授权面板原先用原生 select 装全量人员：人多只能滚、同名分不清。
   这里做成「输入框 + 浮层结果列表」，键盘 ↑↓/Enter/Esc 可用。 */
.pick{position:relative; min-width:240px; flex:1 1 240px}
.pick input{width:100%}
.pick-list{
  position:absolute; z-index:40; top:calc(100% + 4px); left:0;
  min-width:100%; width:max-content; max-width:min(420px, 92vw);
  background:var(--surface); border:1px solid var(--line); border-radius:8px;
  box-shadow:0 8px 24px rgba(20,24,27,.10);
  max-height:248px; overflow:auto; padding:4px;
}
.pick-row{
  display:flex; gap:8px; align-items:baseline; padding:7px 9px; border-radius:6px;
  font-size:13px; cursor:pointer; min-width:0;
}
.pick-row .nm{font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.pick-row .meta{color:var(--ink-3); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.pick-row .no{color:var(--ink-3); font-family:var(--mono); font-size:11px; flex:none}
.pick-row .sep{color:var(--ink-3); flex:none}
.pick-row.on{background:var(--line-2)}      /* token，不写死色值 */
.pick-row.on .nm{color:var(--blue)}
.pick-empty{padding:9px; color:var(--ink-3); font-size:13px}

/* 版本号徽标（票 file-versioning/01）：只在该文件被覆盖过（version>1）时出现 */
.vtag{flex:none; font-size:11px; color:var(--ink-3); background:#f2f4f7;
  border-radius:4px; padding:0 4px; line-height:17px; font-family:var(--mono)}

/* 版本历史面板（票 file-versioning/03）：列宽真源在这里，表头与数据行共用 .ver-cols。
   以前复用 .audit-row 但没带 .audit-cols，六列 inline 宽度加起来超过卡片宽度 →
   「系统管理员」被压成一字一行、操作列挤出可视区（验收现场截图）。 */
/* 弹层必须压在预览层上面（.viewer 是 z-index:80）：否则从预览栏点「版本历史」，
   面板开在预览层底下 —— 看得见一角、点不到任何按钮（真实浏览器测出来的） */
#versions-modal{z-index:85}
/* 确认框永远是最后一张：它可能从任何面板里弹出（版本恢复、授权、分享…）。
   .modal 默认 60，比版本面板(85)/预览层(80)都低 —— 从它们里面弹确认框就点不到按钮 */
#ui-dlg{z-index:95}
#versions-modal .modal-card{width:min(720px, 94vw)}
#versions-list{max-height:44vh; overflow:auto}
.ver-cols{grid-template-columns:52px minmax(96px,1fr) 62px 128px 68px 132px}
.ver-row{display:grid; gap:8px; padding:8px 12px; font-size:13px;
  border-bottom:1px solid var(--line-2); align-items:center}
.ver-row:last-child{border-bottom:0}
.ver-row:hover{background:#fafbfc}
.ver-head{position:sticky; top:0; z-index:2; background:#fff;
  font-size:12px; color:var(--ink-3); border-bottom:1px solid var(--line)}
.ver-head:hover{background:#fff}
.ver-row .rowact{display:flex; gap:6px; justify-content:flex-start}
/* 表头末列对齐与数据行写在隔壁：以前只有 .rowact 右贴边、表头默认左对齐，
   「操作」与下面「下载/恢复」错开 40 多像素（票 file-versioning/04 验收现场指出）。
   .ver-cols 只管列宽，列内对齐也得在一处声明，否则改一处漏一处。 */
.ver-head > span:last-child{text-align:left}
/* 主列表的 .rowact 是「悬停才现」（opacity:0）；版本面板不能沿用这条，
   否则「下载 / 恢复」默认透明 —— 验收现场就是"没有下载按钮"（其实有，看不见） */
#versions-list .rowact{opacity:1}
