:root{
  --bg:#eef1f6; --panel:#ffffff; --line:#e5e9f0; --ink:#1e2733; --mut:#667085;
  --brand:#4f46e5; --brand2:#7c6cf0; --ok:#16a34a; --warn:#f59e0b; --bad:#e5484d;
  --req:#3b82f6; --dev:#10b981; --itm:#f59e0b; --radius:14px; --shadow:0 1px 3px rgba(16,24,40,.06),0 1px 2px rgba(16,24,40,.04);
  --side:#111a2e;
  --chip-bg:#f2f4f8; --chip-border:#e2e6ee; --chip-on:#4f46e5;
  /* 全站唯一的一枚下拉角标（自绘 svg）：表单、查询条、弹窗里的下拉都用它。
     位置、大小、让位（padding-right）由各控件自己定，图只在这里定义一次。 */
  --dd-arrow:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2398a2b3' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
*{box-sizing:border-box;margin:0;padding:0}
/* 整页不滚：外壳固定「一屏高」，滚动交给内容区（.pagewrap）。
   以前是窗口自己滚 —— 滚动条一出现就从右边切掉 10px，而顶栏 / 标签栏是常驻的白条，
   右边被切出一条灰缝（也就是「标题旁边空一块」），页面还会跟着抖一下。 */
html,body{height:100%;overflow:hidden}
body{font-family:"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",system-ui,sans-serif;background:var(--bg);color:var(--ink);font-size:14px}
button{font-family:inherit;cursor:pointer}
input,select,textarea{font-family:inherit;font-size:13px;color:var(--ink)}
/* 滚动条：10px 的槽里放一枚 4px 的半透明圆角「药丸」（3px 透明边框 + background-clip 收细），
   静止时几乎看不见，悬停 / 拖动才显色 —— 原来那条实心 9px 灰条贴在白色卡片旁太扎眼。
   注意：同一元素上若写了 scrollbar-color / scrollbar-width，Chrome 会转用原生滚动条、忽略这一套。 */
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(148,163,184,.45);border:3px solid transparent;background-clip:content-box;border-radius:99px}
::-webkit-scrollbar-thumb:hover{background:rgba(100,116,139,.62);background-clip:content-box}
::-webkit-scrollbar-thumb:active{background:rgba(79,70,229,.55);background-clip:content-box}
/* 横竖滚动条相交的那个小方块：默认是白的，在圆角卡片里同样是一处「空一块」 */
::-webkit-scrollbar-corner{background:transparent}
@supports (-moz-appearance:none){
  /* Firefox 不认 ::-webkit-scrollbar，用标准属性给它一份等价的细滚动条 */
  *{scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.6) transparent}
}

#app{display:flex;height:100vh;height:100dvh}   /* 100dvh：手机上按「当前可见高度」算，地址栏不会盖住底部 */
/* ---------- sidebar ---------- */
.side{width:232px;flex:0 0 232px;background:linear-gradient(180deg,#111a2e,#0d1526);color:#c6cfe2;display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
.brand{padding:20px 18px 14px;border-bottom:1px solid rgba(255,255,255,.07)}
.brand .logo{display:flex;align-items:center;gap:10px}
.brand .logo .cube{width:34px;height:34px;border-radius:9px;background:linear-gradient(135deg,#6366f1,#a78bfa);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:16px;box-shadow:0 4px 12px rgba(99,102,241,.4)}
.brand .logo b{color:#fff;font-size:15px;letter-spacing:.5px}
.brand .src{font-size:11px;color:#7f8aa6;margin-top:10px;line-height:1.5}
.nav{padding:12px 10px;flex:1;overflow:auto}
/* 分组 = 树状分区手风琴：一级是带图标的分组头，二级缩进到竖线里 */
.nav .ngroup{margin:0 0 4px}
.nav .nhead{width:100%;display:flex;align-items:center;gap:9px;background:none;border:none;font-family:inherit;font-size:12.5px;font-weight:600;letter-spacing:.3px;color:#98a5c0;padding:7px 8px;border-radius:9px;cursor:pointer;text-align:left;transition:background .15s,color .15s}
.nav .nhead:hover{background:rgba(255,255,255,.06);color:#e2e8f7}
/* 分组头左边那枚色块：现在是分组对象自带的图标（render.js 的 navGroups() 读出来），
   不再借组里第一项凑 —— 配套支持图片档（dataURL 嵌进 img）和「不要底色」（nobg）。 */
.nav .nhead .gico{width:22px;height:22px;flex:none;border-radius:7px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#fff;box-shadow:0 2px 6px rgba(0,0,0,.25);overflow:hidden}
.nav .nhead .gico img{width:100%;height:100%;display:block;object-fit:cover;border-radius:inherit;pointer-events:none}
.nav .nhead .gico:not(.nobg) img{padding:14%}
.nav .nhead .gico.nobg{background:transparent;color:#98a5c0;box-shadow:none;border:1px dashed rgba(151,168,196,.4)}
.nav .nhead .gname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav .nhead .gcount{flex:none;background:rgba(255,255,255,.08);color:#93a1bd;font-size:10.5px;font-weight:500;letter-spacing:0;line-height:16px;padding:0 6px;border-radius:9px;font-variant-numeric:tabular-nums}
.nav .nhead .chev{width:12px;flex:none;display:inline-flex;align-items:center;justify-content:center;font-size:14px;line-height:1;color:#5c6b8c;transition:transform .22s,color .15s}
.nav .ngroup.open .nhead .chev{transform:rotate(90deg)}
/* 当前页所在的分组：整条分组头点亮，一眼看出「我在哪一片」 */
.nav .ngroup.on .nhead{background:rgba(99,102,241,.14);color:#c7cdfb}
.nav .ngroup.on .nhead .chev{color:#a5b4fc}
.nav .ngroup.on .nhead .gcount{background:rgba(99,102,241,.34);color:#ccd2fb}
.nav .nbody{display:grid;grid-template-rows:0fr;overflow:hidden;transition:grid-template-rows .24s cubic-bezier(.4,0,.2,1)}
.nav .ngroup.open .nbody{grid-template-rows:1fr}
/* 二级项缩进到分组图标的中线上（8px 内边距 + 11px 半个色块），左边一条竖线把「这一片」串起来 */
.nav .ninner{min-height:0;overflow:hidden;margin:2px 0 6px 19px;padding-left:9px;border-left:1px solid rgba(255,255,255,.1)}
.nav a{display:flex;align-items:center;gap:9px;color:#b9c3da;text-decoration:none;padding:8px 10px;border-radius:9px;margin:2px 0;transition:.15s;font-size:13.5px}
.nav a:hover{background:rgba(255,255,255,.06);color:#fff}
.nav a.on{background:linear-gradient(90deg,rgba(99,102,241,.28),rgba(99,102,241,.06));color:#fff;box-shadow:inset 2px 0 0 #6366f1}
.nav a .ico{width:22px;height:22px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:12px;color:#fff;font-weight:700;flex:none}
/* 图标配了图片时（菜单管理里可上传）：图片在色块里留一圈边距、不顶着四条边；字符模式照旧显示文字。
   图片是贴着自身边缘画的（上传的图基本都这样），铺满时会跟色块边界糊在一起 —— 圆角被图片的直角吃掉、
   四周没有一圈底色兜着，小小一枚看着就是"糊在格子上"，一眼分不出是什么图。留 14% 的边距后，
   底色变成图片外围那圈框，图片自己四边留白、形状才看得清（勾了「不配置底色」的 .nobg 没有底色可留，
   照旧铺满整格）。用 padding 而不是 margin：色块的 22 / 24 / 34px 尺寸不动，字符图标那一档也不受影响。 */
.ico img,.tico img{width:100%;height:100%;display:block;object-fit:cover;border-radius:inherit;pointer-events:none}
.ico:not(.nobg) img,.tico:not(.nobg) img{padding:14%}
/* 菜单名过长时省略号收起，而不是把右侧数量徽章一起挤出侧栏 */
.nav a .txt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav a .cnt{margin-left:auto;flex:none;background:rgba(255,255,255,.09);padding:1px 8px;border-radius:20px;font-size:11px;color:#aab7d2;font-variant-numeric:tabular-nums;white-space:nowrap}
.navfoot{padding:10px;border-top:1px solid rgba(255,255,255,.07);flex:none}
.nav-toggle{width:100%;background:rgba(255,255,255,.06);border:none;color:#aab7d2;border-radius:8px;padding:8px;cursor:pointer;font-size:13px;display:flex;align-items:center;justify-content:center;transition:.15s}
.nav-toggle:hover{background:rgba(255,255,255,.12);color:#fff}
.side{transition:width .2s,flex-basis .2s}
.side.collapsed{width:64px;flex:0 0 64px}
.side.collapsed .brand{padding:20px 14px 14px}
.side.collapsed .brand .logo{justify-content:center}
.side.collapsed .brand b,.side.collapsed .brand .src{display:none}
.side.collapsed .nav{padding:12px 8px}
.side.collapsed .nav .ngroup{margin:0 0 3px}
/* 收起后分组头自己变成一枚图标按钮：分组名 / 数量 / 箭头都收起来，只留色块，内容改在浮层里看 */
.side.collapsed .nav .nhead{display:flex;justify-content:center;width:100%;height:auto;padding:8px;margin:0;background:none;border-radius:9px;overflow:visible;pointer-events:auto;cursor:pointer}
.side.collapsed .nav .nhead:hover{background:rgba(255,255,255,.08)}
.side.collapsed .nav .nhead .gname,.side.collapsed .nav .nhead .gcount,.side.collapsed .nav .nhead .chev{display:none}
.side.collapsed .nav .nhead .gico{width:24px;height:24px;font-size:12.5px}
.side.collapsed .nav .ngroup.on .nhead{background:rgba(99,102,241,.2)}
.side.collapsed .nav .nbody{display:none}
.side.collapsed .nav a{justify-content:center;padding:10px;gap:0;margin:2px 0}
.side.collapsed .nav a .txt,.side.collapsed .nav a .cnt{display:none}
.side.collapsed .nav-toggle span{transform:rotate(180deg)}
/* ---------- 收起状态的分组浮层（元素挂在 body 上，见 render.js 的 navFlyShow） ----------
   白底卡片浮在侧栏右侧，列出该分组下的页面；当前页用淡紫底 + 左侧竖条标出来。 */
.navfly{position:fixed;z-index:200;min-width:174px;max-width:264px;max-height:70vh;overflow:auto;
  background:#fff;border:1px solid #e6e9f2;border-radius:12px;padding:6px;
  box-shadow:0 12px 34px rgba(16,24,40,.24);opacity:0;transform:translateX(-4px);
  transition:opacity .12s ease,transform .12s ease}
.navfly.show{opacity:1;transform:none}
.navfly-t{font-size:11.5px;font-weight:700;letter-spacing:.4px;color:#98a2b3;padding:5px 10px 4px}
.navfly a{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:9px;margin:1px 0;
  color:#344054;font-size:13.5px;text-decoration:none;cursor:pointer;transition:.15s}
.navfly a:hover{background:#f2f4f9;color:#111827}
.navfly a.on{background:#eef1ff;color:#4f46e5;font-weight:600;box-shadow:inset 2px 0 0 #6366f1}
.navfly a .ico{width:22px;height:22px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:12px;color:#fff;font-weight:700;flex:none}
.navfly a .txt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ---------- main ---------- */
.main{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}
/* 顶栏 / 标签栏不再需要 sticky：它们本来就常驻（滚动发生在下面的 .pagewrap 里） */
.topbar{height:58px;flex:none;background:rgba(255,255,255,.85);backdrop-filter:blur(8px);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:14px;padding:0 22px;position:relative;z-index:20}
/* 折叠侧栏的那 200ms 里（body.nav-anim，见 render.js 的 toggleNav）顶栏毛玻璃临时关掉：
   宽度每帧都在变，blur 就得每帧重算 —— 这是折叠卡顿的另一半来源。 */
body.nav-anim .topbar{backdrop-filter:none}
.topbar .t1{font-size:16px;font-weight:700}
.topbar .t2{font-size:12px;color:var(--mut);margin-top:1px}
.topbar .sp{flex:1}
.chip{font-size:11.5px;color:#475467;background:#f2f4f8;border:1px solid #e2e6ee;padding:3px 10px;border-radius:20px;white-space:nowrap}
.statbar{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:12px;align-items:center}
.statbar .chip{cursor:pointer;transition:.15s}
.statbar .chip:hover{background:#e9ecf2}
.statbar .chip.on{background:var(--brand);border-color:var(--brand);color:#fff}
.content{padding:20px 22px 22px;max-width:1500px;width:100%;margin:0 auto}
.content.full{max-width:none;padding:22px 28px 22px}
/* ---------- 页面标签栏 ---------- */
.tabs{flex:none;height:40px;display:flex;align-items:flex-end;gap:4px;padding:0 14px;background:#fff;border-bottom:1px solid var(--line);position:relative;z-index:19;overflow-x:auto;overflow-y:hidden;scrollbar-width:thin;scrollbar-color:#d5dbe6 transparent}
/* 下面两行是老内核 / Safari 的兜底：新一点的 Chrome 写了 scrollbar-width 后走原生滚动条，不再认这里 */
.tabs::-webkit-scrollbar{height:3px}
.tabs::-webkit-scrollbar-thumb{background:#d5dbe6}
/* ---------- 内容区滚动 ----------
   外壳固定一屏高（html,body / #app），顶栏与标签栏常驻，只有这一层滚：
   滚动条于是落在窗口最右侧、且只占内容区这一段，不会再从上面那些标题条上切过去。 */
.pagewrap{flex:1;min-height:0;overflow:auto;position:relative}   /* relative：外链页那层 .linkhost 靠它铺满这一格 */
.tab{display:inline-flex;align-items:center;gap:6px;height:32px;max-width:196px;flex:none;padding:0 6px 0 10px;border:1px solid transparent;border-bottom:none;border-radius:9px 9px 0 0;font-size:12.5px;color:#475467;cursor:pointer;white-space:nowrap;user-select:none;position:relative;transition:background .12s,color .12s,border-color .12s}
.tab:hover{background:#f4f6fb;color:#1e2733}
.tab.on{background:#eef2ff;border-color:#dbe0ff;color:#4338ca;font-weight:600}
.tab.on::after{content:'';position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--brand)}
.tab .tico{width:16px;height:16px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;font-size:9px;line-height:1;color:#fff;font-weight:700;flex:none;font-style:normal}
.tab .tname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tab .tx{width:16px;height:16px;flex:none;padding:0;border:none;background:transparent;color:#98a2b3;border-radius:50%;font-size:10px;line-height:1;display:inline-flex;align-items:center;justify-content:center;opacity:0;transition:opacity .12s,background .12s,color .12s}
.tab:hover .tx,.tab.on .tx{opacity:1}
.tab .tx:hover{background:#dfe3ee;color:#1e2733}
.tabdd{flex:none;position:sticky;right:0;z-index:2;width:24px;height:24px;margin:0 0 4px 2px;padding:0;border:1px solid var(--line);background:#fff;color:#667085;border-radius:7px;font-size:11px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .12s,color .12s,border-color .12s}
.tabdd:hover{background:#eef2ff;border-color:#dbe0ff;color:#4338ca}
.tabmenu{position:fixed;left:-9999px;top:-9999px;z-index:200;min-width:146px;background:#fff;border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 24px rgba(16,24,40,.16);padding:5px;display:none;flex-direction:column;gap:1px}
.tabmenu.show{display:flex}
.tabmi{display:block;width:100%;text-align:left;border:none;background:transparent;padding:7px 10px;border-radius:7px;font-size:12.5px;color:#344054;cursor:pointer}
.tabmi:hover:not(:disabled){background:#f4f6fb;color:#4338ca}
.tabmi:disabled{opacity:.4;cursor:not-allowed}
.tabmenu.tabacts{min-width:158px;padding:6px}
.tabmenu.tabacts .tabmi{padding:8px 12px}
.tabmenu.tabacts .tabmi:last-child{color:#b42318}
.tabmenu.tabacts .tabmi:last-child:hover{background:#fef3f2;color:#b42318}
/* JSON ⇄ Excel 这页自带虚拟滚动列表，必须拿到确定高度：
   外壳（#app）与内容区（.pagewrap）现在都是硬高度，这里再给一个显式 height（顶栏 58px + 标签栏 40px），
   正好等于 .pagewrap 那一格，页面本身不滚、交给页内的虚拟滚动。 */
.content.jsonpage{padding:0;background:transparent;border:none;box-shadow:none;display:flex;flex-direction:column;min-height:0;max-width:none;margin:0;height:calc(100vh - 98px);max-height:calc(100vh - 98px);overflow:hidden}
/* ---------- 自定义链接页（菜单里「类型＝自定义链接」那一项）：链接画面嵌在页面里 ----------
   画法：和全站其它页一样是一张卡片 —— 顶上一条常驻工具条（这是导航里的哪一项、连到哪儿、两个出口），
   下面整块给药嵌进来的那个网页。
   位置：这一页的画面不在内容区（.content）里，而是挂在 .pagewrap 下另一层常驻容器 .linkhost 上。
   内容区是整块 innerHTML 重画的，而嵌进来的网页最怕重画：一挪窝、甚至只是「摘下来再挂回去」，
   浏览器就会把它整个重载一遍（图多的、接口多的页面每次切回来都要重新等）。
   所以每个外链页在 .linkhost 里占一块自己的面板（.lkpane），切页只切「哪一块可见」——
   DOM 一个节点都不动，于是切多少次、切多远，看到的都还是刚才那一份（见 menumgr.js 的 linkShow）。
   面板外面那圈留白 / 白卡片由 .lkpane + .linkcard 出，和其它页一个观感。
   早先这里是「iframe 铺满整格 + 右上角一条半透明小条」，后来又改成「挪进 body 尾部一块看不见的
   #linkPark」—— 两条路都试过，现在这版才是真正不重载的那一版。 */
.content.linkpage{display:none}      /* 这一页的画面在 .linkhost 里，内容区整格让开 */
.linkhost{position:absolute;left:0;right:0;top:0;bottom:0;visibility:hidden;pointer-events:none}
.linkhost.show{visibility:visible;pointer-events:auto}
/* 一块面板＝一个外链页（里面装着工具条、舞台和那块 iframe）。
   切走的那块只是「不可见」：尺寸和内容原样留着 —— 用 visibility 而不是 display:none，
   免得里面的网页在 0 尺寸和真尺寸之间来回重排（那是要重新算布局的，外链页尤其吃不消）。 */
.lkpane{position:absolute;left:0;right:0;top:0;bottom:0;padding:14px 16px 16px;visibility:hidden;pointer-events:none}
.lkpane.on{visibility:visible;pointer-events:auto}
/* 容器整层收起来时，里面的面板一律跟着藏。
   这条是兜底：visibility 是继承来的，子元素写 visible 就能盖掉父级的 hidden ——
   万一哪天出现「容器收着、面板还带着 .on」，那就不只是看不见，还会有一块透明的 iframe
   盖在内容区上把点击全吃掉。这里按容器状态一刀切，不看面板自己的类。 */
.linkhost:not(.show) .lkpane{visibility:hidden;pointer-events:none}
.linkcard{height:100%;display:flex;flex-direction:column;background:#fff;border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
.linkhead{flex:none;height:48px;display:flex;align-items:center;gap:9px;padding:0 10px 0 12px;background:#fbfcfe;border-bottom:1px solid var(--line)}
.linkhead .ico{width:24px;height:24px;border-radius:7px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#fff;flex:none}
.linkhead .ico.nobg{background:transparent;color:#475467}
/* 菜单名在前、网址在后：一眼认出「这是导航里的哪一项」，再看它到底连到哪儿。
   网址那条现在是地址栏（可改、回车就跳），不再只是给人看的一行字。 */
.linkhead .lk-name{flex:none;max-width:180px;font-size:13.5px;font-weight:700;color:#1e2733;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ← → ↻：这一页自己的前进 / 后退 / 重新加载（见 menumgr.js 的 LINK_HIST）。
   三枚连成一组，和右边那两个出口按钮分开 —— 「走去哪儿」和「打开方式」是两回事。
   退到底 / 进到底时按钮自己变灰（disabled），不用另外画一档「不能用」的样子。 */
.linkhead .lk-nav{flex:none;display:flex;align-items:center;gap:3px}
.lkbtn{width:27px;height:27px;flex:none;padding:0;border:1px solid var(--line);background:#fff;border-radius:8px;
       color:#475467;font-size:13px;line-height:1;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
.lkbtn:hover:not(:disabled){background:#eef2ff;border-color:#c7d2fe;color:var(--brand)}
.lkbtn:disabled{opacity:.35;cursor:default}
/* 地址栏：跟着这一页现在停在哪儿变（站内跳转也同步），改完回车或点「前往」就在这一格里跳过去 */
.linkhead .lk-addr{flex:1;min-width:90px;height:28px;padding:0 10px;border:1px solid var(--line);border-radius:8px;
                  background:#fff;font-size:12.5px;font-family:inherit;color:#475467}
.linkhead .lk-addr:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.linkhead .lk-acts{margin-left:auto;flex:none;display:flex;align-items:center;gap:8px}
.linkhead a.btn{text-decoration:none}
.linkstage{position:relative;flex:1;min-height:0;background:#fff}
.linkframe{position:absolute;left:0;right:0;top:0;bottom:0;width:100%;height:100%;border:0;background:#fff;display:block}
/* 加载遮罩：跨域网页在加载完之前整片白，一句话都不说就像「这一页打不开」。
   .stuck 是「8 秒了还没好」那一档（见 menumgr.js 的 linkWatch）：白底让开、只留一张小卡摆在中间，
   底下其实在慢慢加载的页面也看得见，遮罩不再吃鼠标 —— 想直接看那个页面就看。 */
.linkwait{position:absolute;left:0;right:0;top:0;bottom:0;z-index:2;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;text-align:center;background:rgba(255,255,255,.92);transition:opacity .18s;pointer-events:auto}
.linkwait.gone{opacity:0;pointer-events:none}
.linkwait .lkw-box{display:flex;flex-direction:column;align-items:center;gap:10px;padding:22px 26px;background:#fff;border:1px solid var(--line);border-radius:14px;box-shadow:0 10px 30px rgba(16,24,40,.12);max-width:520px}
.linkwait.stuck{background:rgba(255,255,255,.6)}
.linkwait.stuck .lkw-spin{display:none}
.linkwait.stuck .lkw-t{font-size:14px}
.linkwait.stuck .lkw-box{box-shadow:0 10px 30px rgba(16,24,40,.14)}
.linkwait .lkw-spin{width:22px;height:22px;border-radius:50%;border:2.5px solid #e6e9f4;border-top-color:var(--brand);animation:lkwspin .7s linear infinite}
@keyframes lkwspin{to{transform:rotate(360deg)}}
.linkwait .lkw-t{font-size:13px;font-weight:600;color:#1e2733}
.linkwait .lkw-d{max-width:470px;font-size:12px;color:var(--mut);line-height:1.8}
.linkwait .lkw-act{display:flex;align-items:center;gap:8px}
.linkwait .lkw-act a.btn{text-decoration:none}
/* 工具条上多了「← → ↻ + 地址栏 + 两个出口」：窄一点先把菜单名收掉，
   再窄才动出口按钮 —— 地址栏是能看清「现在在哪儿」的唯一一处，尽量留住。 */
@media(max-width:1180px){.linkhead .lk-name{display:none}}
@media(max-width:820px){.linkhead .lk-acts .btn{font-size:12px;padding:4px 8px}}

/* ---------- cards & home（首页统计整版重做） ---------- */
/* 首页顶部查询条：一条**通栏白条**（不是圆角面板）——左右各用负边距吃掉 .content 的内边距，
   于是它从内容区最左铺到最右，横贯一整条，查询条件就装在这条白条里。
   上一版给的是圆角白面板，看着像一张卡片、不像「一条」，所以这里去圆角、只留底边线。 */
.homebar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:#fff;border:none;border-bottom:1px solid var(--line);border-radius:0;box-shadow:0 1px 2px rgba(16,24,40,.04);padding:10px 22px;margin:0 -22px 16px}
@media(max-width:760px){.homebar{padding:10px 14px;margin:0 -14px 12px}}

/* —— 清单卡片：四栏一行（窗口窄了自动降两栏 / 一栏）——
   顶部一道表色 + 右上角一团同色光晕（8% 透明）：不靠边框也认得出是谁；
   图标放大到 34px 圆角块；「已完成 / 进行待办」做成胶囊徽章；
   分布条加粗、图例两列 —— 卡片窄，一列会把卡片撑得很高。 */
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px}
@media(max-width:1240px){.cards{grid-template-columns:repeat(2,1fr)}}
@media(max-width:760px){.cards{grid-template-columns:1fr}}
.modcard{position:relative;overflow:hidden;background:#fff;border:1px solid #e8ecf5;border-radius:16px;padding:16px 16px 15px;
  box-shadow:0 1px 2px rgba(16,24,40,.05);transition:transform .18s,box-shadow .18s,border-color .18s}
.modcard:hover{transform:translateY(-2px);box-shadow:0 10px 26px rgba(16,24,40,.10);border-color:#dfe4f2}
.modcard .bar{position:absolute;left:0;right:0;top:0;height:3px}
.modcard .glow{position:absolute;right:-34px;top:-34px;width:110px;height:110px;border-radius:50%;opacity:.08;pointer-events:none}
.modcard h3{position:relative;z-index:1;display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:14px;font-weight:700;color:#111a2e}
.modcard h3 .tag{font-size:10.5px;color:#fff;padding:2px 8px;border-radius:999px;font-weight:600}
/* 卡片标题前那枚图标：与左导航、标签栏同一枚（字符 / 图片），跟着菜单管理的配置走 */
.modcard h3 .ico{width:34px;height:34px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:13px;color:#fff;font-weight:700;flex:none;box-shadow:0 3px 8px rgba(16,24,40,.14)}
.modcard h3 .ico.nobg{background:transparent;color:#475467;box-shadow:none}
.modcard .big{font-size:34px;font-weight:800;letter-spacing:-.5px;line-height:1.1;color:#111a2e;margin:14px 0 8px}
.modcard .big a{color:inherit;text-decoration:none;cursor:pointer}
.modcard .big small{font-size:12.5px;color:#98a2b3;font-weight:600;margin-left:6px}
.modcard .big.zero a{color:#c7cdd9}
/* 两枚胶囊徽章：以前是带虚线的文字，和后面的分类统计挤成一行、分不清哪是哪 */
.modcard .meta{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:12px}
.modcard .meta a.pill{font-size:11.5px;font-weight:600;border-radius:999px;padding:3px 10px;cursor:pointer;transition:.15s;text-decoration:none}
.modcard .meta a.pill.done{background:#ecfdf3;color:#067647;border:1px solid #d1fadf}
.modcard .meta a.pill.done:hover{background:#d1fadf}
.modcard .meta a.pill.open{background:#fff7ed;color:#b54708;border:1px solid #fed7aa}
.modcard .meta a.pill.open:hover{background:#fed7aa}
.modcard .meta .note{font-size:11.5px;color:#98a2b3}
.segstack{display:flex;height:10px;border-radius:5px;overflow:hidden;background:#eef1f6;margin:0 0 9px}
.segstack i{display:block}
.modcard .legend2{display:grid;grid-template-columns:1fr 1fr;gap:0 14px}
.modcard .legend2 .minirow{min-width:0;overflow:hidden;white-space:nowrap}
.minirow{display:flex;align-items:center;gap:8px;font-size:12px;color:#3d4757;margin:3px 0}
.minirow .dot{width:8px;height:8px;border-radius:50%;flex:none}
.minirow .n{margin-left:auto;font-weight:700}
/* 左右两栏（即将到期 / 最近更新）：头一行浅底把标题分开，列表项悬停给底；
   内容长了在面板里滚，不再把整页撑老长 */
.grid2 .panel{border-radius:16px;border-color:#e8ecf5}
.grid2 .panel .ph{background:#fafbfe;border-radius:16px 16px 0 0}
.grid2 .panel .pb{max-height:46vh;overflow:auto;padding:10px 16px}
.grid2 .feed li{padding:10px 8px;border-radius:10px;transition:background .15s}
.grid2 .feed li:hover{background:#fafbfe}

.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:1000px){.grid2{grid-template-columns:1fr}}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.panel .ph{padding:13px 16px;border-bottom:1px solid var(--line);font-weight:700;font-size:13.5px;display:flex;align-items:center;gap:8px}
.panel .ph .sp{flex:1}
.panel .pb{padding:14px 16px}
/* 三个小统计：左侧一枚淡色图标块（发 / 今 / 周）+ 数字；左色条与图标同色（紫 / 绿 / 橙） */
.statmods{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:16px}
@media(max-width:900px){.statmods{grid-template-columns:1fr}}
.statmod{position:relative;overflow:hidden;display:flex;align-items:center;gap:12px;background:#fff;border:1px solid #e8ecf5;border-radius:16px;
  padding:14px 16px;cursor:pointer;box-shadow:0 1px 2px rgba(16,24,40,.05);transition:transform .18s,box-shadow .18s,border-color .18s}
.statmod:hover{transform:translateY(-2px);box-shadow:0 10px 26px rgba(16,24,40,.10);border-color:#dfe4f2}
.statmod:before{content:'';position:absolute;left:0;top:0;bottom:0;width:4px;background:#6366f1}
.statmod .sic{width:38px;height:38px;border-radius:12px;flex:none;display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:800}
.statmod .stit{font-size:12px;color:#98a2b3;margin-bottom:2px}
.statmod .snum{font-size:26px;font-weight:800;color:#111a2e;line-height:1.15}
.statmod .snum small{font-size:12px;font-weight:600;color:#98a2b3;margin-left:5px}
.statmods .statmod:nth-child(2):before{background:#10b981}
.statmods .statmod:nth-child(3):before{background:#f59e0b}
.statmods .statmod:nth-child(1) .sic{background:#eef0ff;color:#4f46e5}
.statmods .statmod:nth-child(2) .sic{background:#ecfdf3;color:#067647}
.statmods .statmod:nth-child(3) .sic{background:#fff7ed;color:#b54708}
.charts{display:none}

/* bar */
.bars{margin-top:6px}
.barline{display:grid;grid-template-columns:86px 1fr 44px;gap:8px;align-items:center;margin:7px 0;font-size:12px}
.barline .nm{color:#475467;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.barline .tr{background:#eef1f6;border-radius:5px;height:12px;overflow:hidden}
.barline .tr i{display:block;height:100%;border-radius:5px;min-width:2px}
.barline .v{font-weight:700;text-align:right;color:#1e2733}
.donutwrap{display:flex;align-items:center;gap:18px}
.donut{width:104px;height:104px;border-radius:50%;position:relative;flex:none}
.donut .in{position:absolute;inset:16px;background:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:22px}
.legend{flex:1;min-width:0}
.legend .minirow{margin:5px 0}

/* due & recent list */
.feed{list-style:none}
.feed li{display:flex;gap:10px;padding:9px 0;border-bottom:1px dashed #eef0f5;align-items:flex-start}
.feed li:last-child{border-bottom:0}
.feed .b{width:30px;height:30px;border-radius:8px;color:#fff;flex:none;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700}
/* 首页清单（即将到期 / 最近更新 / 记录弹窗）里那枚小方块：与左导航同一枚图标，
   菜单管理里换了图标或底色，这里跟着变。配了图片就贴图片（同 .ico 一样留一圈边距，
   不顶着四条边）；勾掉「不配置底色」时不铺底，改用所在行的文字颜色 —— 白字在白底上会看不见。 */
.feed .b img{width:100%;height:100%;display:block;object-fit:cover;border-radius:inherit;padding:14%;pointer-events:none}
.feed .b.nobg{background:transparent;color:inherit}
.feed .b.nobg img{padding:0}
.feed .tx{flex:1;min-width:0}
.feed .tx .nm{font-weight:600;font-size:13px;color:#1e2733;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.feed .tx .dt{font-size:11.5px;color:var(--mut);margin-top:2px}
.feed .due-ov{color:var(--bad)} .feed .due-ok{color:var(--warn)}

/* ---------- table view ---------- */
.pagehead{display:flex;align-items:flex-end;gap:12px;margin-bottom:10px;flex-wrap:wrap}
.pagehead .tt{font-size:20px;font-weight:800}
.pagehead .tt small{font-size:12.5px;color:var(--mut);font-weight:500;margin-left:10px}
.pagehead .meta{font-size:12px;color:var(--mut);margin-top:3px}
.pagehead .sp{flex:1}
.btn{border:1px solid #d9deea;background:#fff;color:#344054;padding:6px 12px;border-radius:9px;font-size:13px;display:inline-flex;align-items:center;gap:6px;transition:.15s;font-weight:500;white-space:nowrap}
.btn:hover{border-color:#aab4cc;background:#f8fafd}
.btn.primary{background:linear-gradient(135deg,#4f46e5,#6d5cf0);border:none;color:#fff;box-shadow:0 4px 10px rgba(79,70,229,.25)}
.btn.primary:hover{filter:brightness(1.06)}
.btn.danger{color:#d92d20;border-color:#f2b8b5;background:#fff7f7}
.btn.danger:hover{background:#fee9e8}
.btn.sm{padding:3px 8px;font-size:12px;border-radius:7px}
.btn.ghost{border-color:transparent;background:transparent;color:#475467}
.btn.ghost:hover{background:#f2f4f8}
.toolbar{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:10px 14px;display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:12px}
.toolbar .right{margin-left:auto;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.toolbar>.btn,.toolbar .drop>.btn{height:34px}
.searchbox{position:relative;display:inline-flex;align-items:center}
.searchbox input{padding:0 0 0 22px;border:none;background:transparent;outline:none;font-size:13px;color:#344054;height:24px;width:170px}
.searchbox input::placeholder{color:#98a2b3}
/* 放大镜是纯装饰，不能吃点击（否则点图标那一下落不到输入框上，感觉「点了没反应」） */
.searchbox svg{position:absolute;left:0;top:5px;opacity:.45;width:14px;height:14px;pointer-events:none}
.drop{position:relative;display:inline-block}
.drop .drop-menu{position:absolute;right:0;top:calc(100% + 6px);background:#fff;border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow);display:none;min-width:150px;z-index:50;padding:6px;flex-direction:column;gap:2px}
.drop.open .drop-menu{display:flex}
.drop-menu .btn{width:100%;justify-content:flex-start;border:none;background:transparent;padding:7px 12px}
.drop-menu .btn:hover{background:#f6f7ff}
/* 下拉项上的图标（与查询框同一枚放大镜 SVG）：.btn 已是 inline-flex + gap:6px，天然对齐。
   只锁住不被压扁，并压淡一档 —— 下拉里其它图标都是 ☰ ▦ ↻ 这类文字字形，线条比它们黑会显得跳。 */
.drop-menu .btn svg{flex:none;opacity:.75}
.qfilters{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.qfilters .qf{display:inline-flex;align-items:center;gap:6px;background:#fff;border:1px solid #dde2ec;border-radius:9px;padding:0 10px;font-size:13px;color:#475467;height:34px}
.qfilters .qf label{display:inline-flex;align-items:center;gap:3px;font-size:12px;color:#64748b;margin:0}
.qfilters .qf>span{color:#64748b;font-size:12px;white-space:nowrap}
.qfilters .qf input[type=date], .qfilters .qf input[type=text], .qfilters .qf input[type=number], .qfilters .qf select{padding:0;border:none;font-size:13px;color:#344054;background:transparent;outline:none;height:24px;box-sizing:border-box;width:auto}
.qfilters .qf input[type=text]{width:110px}
.qfilters .qf input:focus, .qfilters .qf select:focus{border:none;box-shadow:none}
.qfilters .qf option{color:#344054}
/* 查询条里的下拉角标：和表单（.fitem select）用的是同一枚自绘箭头。
   以前统一角标时漏了工具栏这一层，查询条件里的 <select> 和「可自由输入」的 input[list]
   还在用浏览器原生角标 —— 跟表单里的形状/大小/位置都对不上。 */
.qfilters .qf select,
.qfilters .qf input[list],
.qfilters .qf input[data-sug]{appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:var(--dd-arrow);
  background-repeat:no-repeat;background-position:right 0 center;background-size:12px 12px;padding-right:19px}
/* 可输入下拉（机构 / 团队）：已不用 datalist（弹层改由 .ddpanel 自绘），
   浏览器原生那枚下拉指示就是这个伪元素，一并藏掉 */
.qfilters .qf input[list]::-webkit-calendar-picker-indicator,
.qfilters .qf input[data-sug]::-webkit-calendar-picker-indicator{display:none;opacity:0}
.qfilters .dpick{position:relative;display:inline-flex;align-items:center;justify-content:center;height:28px;min-width:108px;padding:0 8px;background:#fff;border:1px solid #dde2ec;border-radius:6px;cursor:pointer;transition:.12s;overflow:hidden}
.qfilters .dpick:hover{border-color:#6366f1;background:#f6f7ff}
.qfilters .dpick .dinp{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;border:none;padding:0;margin:0}
.qfilters .dpick .dface{position:relative;z-index:1;font-size:13px;color:#344054;pointer-events:none;display:flex;align-items:center;gap:4px;white-space:nowrap}
.qfilters .dpick .dface i{font-style:normal;font-size:12px;color:#64748b}
.qfilters .dpick .dface em{font-style:normal;color:#98a2b3}
.qfilters .btn.sm{height:34px;padding:0 12px;font-size:13px}
/* ---------- 首页筛选条（.home-filters）----------
   外壳就是全站那张 .toolbar（白卡 + 同一套内边距 / 圆角 / 阴影），里面的 .qf / 输入框 /
   日期块 / 清空按钮一律沿用 .qfilters 的默认尺寸 —— 首页不再自造一套更大的，
   与清单页查询条逐像素对齐。这里只写「不改变尺寸」的一件事：条件生效时给它上色。 */
.home-filters .qf.active{border-color:rgba(79,70,229,.35);background:#fbfbfe}
.home-filters .qf.active>span:first-child{color:var(--brand)}   /* 只换颜色，字号字重不动 —— 一加粗就会比别页宽 */
.badge{display:inline-block;font-size:11px;padding:2px 8px;border-radius:10px;font-weight:600}
.tblwrap{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:auto;max-height:calc(100vh - 300px)}
table.list{border-collapse:separate;border-spacing:0;width:100%;min-width:max-content}
table.list thead th{background:#f8fafd;position:sticky;top:0;z-index:3;font-size:12px;color:#475467;font-weight:600;padding:9px 12px;border-bottom:1px solid var(--line);white-space:nowrap;cursor:pointer;user-select:none}
table.list thead th:hover{color:#1e2733}
table.list thead th .arr{color:#6366f1;font-size:10px}
table.list tbody td{padding:9px 12px;border-bottom:1px solid #f1f3f8;font-size:13px;vertical-align:top;white-space:normal;max-width:460px;min-width:92px;word-break:break-word}
table.list tbody tr{cursor:default}
table.list tbody tr:hover{background:#f7f9fe}
table.list tbody tr:last-child td{border-bottom:0}
.cell{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.45}
.cell.cell-wrap{-webkit-line-clamp:unset;display:block;white-space:normal;overflow:visible}
.cell.cell-ellipsis{-webkit-line-clamp:unset;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cell.mono{font-variant-numeric:tabular-nums;color:#344054}
.cell.empty{color:#a8b0bf}
.ta-l{text-align:left}
.ta-c{text-align:center}
table.list th.idx,table.list td.idx{width:46px;min-width:46px;max-width:46px;text-align:center;color:#64748b;font-size:12px;font-variant-numeric:tabular-nums;padding:9px 6px}
.ta-r{text-align:right}
.va-t{vertical-align:top}
.va-m{vertical-align:middle}
.va-b{vertical-align:bottom}
th.act,td.act{position:sticky;right:0;text-align:right;white-space:nowrap;width:172px;min-width:172px;max-width:172px}
th.act{z-index:4;background:#f8fafd}
td.act{z-index:1;padding-right:16px;background:var(--panel);border-left:1px solid #eef1f6}
tr:hover td.act{background:#f7f9fe}
.rowact{display:inline-flex;align-items:center;gap:4px;justify-content:flex-end;flex-wrap:nowrap}
.rowact .btn{flex:none;white-space:nowrap}
.kvgrid{display:flex;flex-direction:column;gap:0;border:1px solid #e5e9f0;border-radius:12px;overflow:hidden;background:#fff}
.prow{display:flex;align-items:stretch;border-bottom:1px solid #f1f3f8;font-size:13px}
.prow:last-child{border-bottom:0}
.pcell{flex:1;min-width:0;display:flex;align-items:flex-start}
.pcell+.pcell{border-left:1px solid #f1f3f8}
.pcell .pl{width:110px;flex:none;padding:10px 12px;background:#f8fafd;color:#667085;font-size:12px;font-weight:600;border-right:1px solid #f1f3f8;white-space:nowrap}
.pcell .pv{flex:1;padding:10px 12px;color:#1e2733;word-break:break-word;line-height:1.5;min-width:0}
.pcell .pv:empty::before{content:'—';color:#a8b0bf}
@media(max-width:760px){.prow{flex-direction:column}.pcell+.pcell{border-left:0;border-top:1px solid #f1f3f8}}
.pager{display:flex;gap:6px;align-items:center;padding:10px 14px;font-size:12.5px;color:var(--mut);flex-wrap:wrap}
.pager .sp{flex:1}
.pager .pgbtn{width:26px;height:26px;border:1px solid #dfe4ee;border-radius:7px;background:#fff;font-size:12px}
.pager .pgbtn.on{background:#4f46e5;border-color:#4f46e5;color:#fff}
.pager .dots{color:#98a2b3;padding:0 2px}
.pager .jump{display:inline-flex;align-items:center;gap:4px;margin-left:8px;white-space:nowrap}
.pager .jump input{width:42px;height:26px;border:1px solid #dfe4ee;border-radius:7px;text-align:center;font-size:12px;outline:none;background:#fff;color:#344054;-moz-appearance:textfield}
.pager .jump input::-webkit-outer-spin-button,.pager .jump input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.pager .jump input:focus{border-color:#6366f1;box-shadow:0 0 0 2px rgba(99,102,241,.12)}
.pager .jump .go{height:26px;padding:0 9px;border:1px solid #d9deea;background:#fff;border-radius:7px;font-size:12px;color:#475467;cursor:pointer;line-height:1}
.pager .jump .go:hover{border-color:#aab4cc;background:#f5f7fb}
/* 分页条上的「每页多少行」（列表 / 资料 / 日志 三个分页视图共用）：
   下拉角标仍用全站那一枚 --dd-arrow；底色写 background-color，避免简写把角标图一起抹掉。 */
.pager .psize{display:inline-flex;align-items:center;gap:5px;margin-left:12px;white-space:nowrap}
.pager .psize select{height:26px;padding:0 21px 0 8px;border:1px solid #dfe4ee;border-radius:7px;background-color:#fff;color:#344054;font-size:12px;outline:none;cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none;background-image:var(--dd-arrow);background-repeat:no-repeat;background-position:right 6px center;background-size:11px 11px}
.pager .psize select:focus{border-color:#6366f1;box-shadow:0 0 0 2px rgba(99,102,241,.12)}

/* ================= 卡片视图 card.js（v2） =================
   一条记录一张卡，与表格视图（.tblwrap / .list）完全分家：只共用 :root 那几个变量，
   类名全是 r 前缀（rcard / rhead / rmeta / rfoot），改卡片碰不到表格，改表格也碰不到卡片。

   v2 的关键改动：字段区（.rmeta）就是一张标准 CSS 网格。
   一行几格 ＝ 配置写成内联的 --cc（写在 .rmeta 上），
   每一项占几格 ＝ 写在 .mi 上的 grid-column:span N。
   渲染出来的 HTML 已经是终态，不再有「渲染完再让 JS 回去贴第几行第几列」那一步
   （v1 的 cardFitSpans），也不拿整块渐变背景刷线：
     1. 格线 ＝ 每一格自己的 border：开了格线才有颜色，不开时这条边框照样占着位（透明），
        于是「内容框」天然就是这一格的框，内容压在框正中，线与内容不可能错位；
        开关格线也不抖（边框一直在，只换颜色）。
     2. 行高不再由 JS 算：grid 的行高就是这一行里最高的那一项，
        图片 / 多行文本都能自己把行撑开。
   横向也让网格自己管：一行排不下就折到下一行，不需要第二条排版算法。 */

/* 卡片区：站的是表格视图里 .tblwrap 的位置 —— 固定高度、自己滚，
   底下的分页条因此常驻屏内（否则一页 20 张卡会把分页条顶到屏幕外）。
   不设底色与边框：卡片本身已是白面板，外面再套一层会成双重相框。 */
.cardwrap{overflow:auto;max-height:calc(100vh - 300px);padding:1px 6px 4px 1px}
/* 一张卡多宽 ＝ 一行排几张：自适应用自动填充（每张至少 380px，屏幕不够宽自己往下折） */
.cardgrid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(380px,1fr))}
.cardgrid.w1{grid-template-columns:1fr}
.cardgrid.w2{grid-template-columns:repeat(2,minmax(0,1fr))}
.cardgrid.w3{grid-template-columns:repeat(3,minmax(0,1fr))}
@media(max-width:1200px){.cardgrid.w2,.cardgrid.w3{grid-template-columns:1fr}}
/* ---------- 卡片壳 ----------
   两块可调的变量：--cg ＝ 列间距，--crg ＝ 行间距，密度三档各自给一份，改这儿就够。 */
.rcard{--cg:14px;--crg:6px;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:13px 15px 11px;display:flex;flex-direction:column;gap:9px;cursor:pointer;
  transition:box-shadow .15s,border-color .15s,transform .15s}
.rcard.d-c{--cg:10px;--crg:3px}
.rcard.d-l{--cg:18px;--crg:10px}
.rcard:hover{border-color:#c7d0e4;box-shadow:0 6px 16px rgba(16,24,40,.10);transform:translateY(-1px)}
/* 配置弹窗里的预览卡：长得跟列表上一模一样（同一个 cardOneHTML），只是不可点 */
.rcard.prev{cursor:default}
.rcard.prev:hover{border-color:var(--line);box-shadow:var(--shadow);transform:none}
/* ---------- 头部：序号 · 主标题 · 状态 ---------- */
.rcard .rhead{display:flex;align-items:flex-start;gap:8px;min-height:22px}
.rcard .rseq{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:19px;
  margin-top:1px;border-radius:6px;background:#f1f4f9;color:#98a2b3;font-size:11.5px;font-weight:600;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
/* 徽标改成显示某个字段时（.f）：值是随便什么内容，长度没法预期 —— 一律限宽 + 省略号收掉，
   宁可截断也不能把右边的主标题顶得换行；底色沿用行号那枚，字体回到正文（中文用等宽字反而难读） */
.rcard .rseq.f{display:inline-block;max-width:96px;height:19px;line-height:19px;padding:0 7px;
  font-family:inherit;color:#667085;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.rcard .rseq.f.empty{color:#b8c0cc}
.rcard .rtitle{flex:1;min-width:0;font-size:14px;font-weight:600;line-height:1.55;color:var(--ink);
  word-break:break-word;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.rcard .rtitle.empty{color:#98a2b3;font-weight:500}
.rcard .rstats{display:inline-flex;align-items:center;gap:6px;flex:none;flex-wrap:wrap}
.rcard .rno{font-size:12px;color:var(--mut);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.rcard .rstat{font-size:12.5px;line-height:1.5}
.rcard .rstat.text{color:#344054;font-weight:600}
.rcard .rstat.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:#475467}
.rcard .rsline{display:flex;align-items:center;gap:6px;margin-top:-2px}
.rcard .rstats .opt-tag{flex:none}
/* ---------- 字段区：一张标准网格 ----------
   align-items:stretch ⇒ 同一行的各项一样高，矮的那一项在自己这一格内垂直居中（见 .mi）。 */
.rcard .rmeta{display:grid;grid-template-columns:repeat(var(--cc,6),minmax(0,1fr));
  gap:var(--crg) var(--cg);align-items:stretch}
/* 一格 ＝ 一个字段。那条 1px 透明边框一直占着位：开了格线只换成浅灰，
   所以「画不画线」不会让内容位移 —— 这正是 v1 里内容框与格线错位的根治办法。
   横向位置（justify-content）与竖向摆放（值多行时改成竖排）由 card.js 写在这一格的内联样式上。 */
.rcard .rmeta .mi{display:flex;gap:6px;font-size:12.5px;min-width:0;overflow:hidden;
  border:1px solid transparent;border-radius:6px;padding:2px 5px}
.rcard .rmeta .mi.bd{border-color:#e9eef6}      /* 每一格自带「要不要描边」＝这一格的格线 */
/* 字段名与值谁让谁：字段名 **不收缩**（flex:none，最多占 70%）、值吃满剩下的宽度、超了用「…」收。
   早先两边都可收缩，一条长内容就能把字段名压成一条缝甚至压没 —— 那一格就只剩值、不知道是什么字段了。

   ⚠ 这里**不许折行**（flex-wrap:nowrap，也就是下面这条）. 曾经为了「格子太窄时别把名字和值都挤成一两个字」
   放开了折行，结果适得其反：值的 flex-basis 是 auto，算的是**整段文字的宽度**（值是 white-space:nowrap），
   于是「文字比余量长」＝ 每一条长内容都被判成这一行放不下，值**一律**掉到第二行 ——
   明明只配了「值 1 行」，格子里却永远是两条：上面一个字段名、下面一坨值，
   同一行的其它格子还被它俩撑高（.rmeta 是 grid，行高＝这一行最高的那一项），整张卡看着就散了。
   现在：写几行就是几行 —— 值留在原地、该收多窄收多窄，超出的部分用「…」收；
   min-width:0 才收得动（留 34px 的下限＝文字比这还长时只能溢出，而外层 overflow:hidden 会把它整段裁掉，
   连「…」都看不见）。竖排的格子（.tall / .pic）本来就不折：flex-direction:column 时换行＝换列，
   值会被挤出这一格，同样被外层裁掉。 */
.rcard .rmeta .mi:not(.tall):not(.pic):not(.grp){flex-wrap:nowrap}
.rcard .rmeta .mi i{color:#98a2b3;font-style:normal;flex:none;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;max-width:70%}
.rcard .rmeta .mi.nol i{display:none}                 /* 隐藏字段名：格子里只留值 */
.rcard .rmeta .mi b{font-weight:500;color:#344054;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  min-width:0;flex:1 1 auto;background:#f4f6fb;border-radius:5px;padding:1px 7px}
/* 彩色的值（下拉选项那枚圆角标签）一样只占一行：格子窄了它自己收窄、用「…」收。
   inline-block 的 min-width 是 auto＝整段文字的宽度，不收窄就只能被外层 overflow:hidden 硬裁成半截。 */
.rcard .rmeta .mi .opt-tag{flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.rcard .rmeta .mi.tall b,.rcard .rmeta .mi.pic b{min-width:0}   /* 竖排：不设下限，避免多出一列被裁掉 */
/* 预览占位的那几个类型名（「文本 / 下拉 / 数字」）：淡灰字，一眼跟真值分开。
   底色不再一律抹平 —— 勾了「值底色」就得在预览里看得见；没勾时由内联样式给透明底，
   所以这里只管字色（跟下面那条「空格子的 —」分工）。 */
.rcard .rmeta .mi b.mt{color:#a9b1c0}
/* 空格子里那个「—」：什么也不垫 */
.rcard .rmeta .mi b.mt.nolbg{background:transparent;padding:0}
.rcard .rmeta .mi b.mo{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.rcard .rmeta .mi b.ml{white-space:normal;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
  line-height:1.5}
.rcard .rmeta .mi.tall{gap:2px}
/* 竖排（值多行 / 图）时字段名单独占一行：不吃上面那条 60% 的上限 */
.rcard .rmeta .mi.tall i,.rcard .rmeta .mi.pic i{max-width:none}
/* 值是图片：图跟着这一格走，高度由图片自己撑（最多 190px），不再掐行高 */
.rcard .rmeta .mi.pic img.cimg{width:100%;height:auto;max-height:190px;min-height:52px;object-fit:contain;
  object-position:center;display:block;border-radius:4px;background:#f7f8fb}
/* 分组标题：占整行的一条小标题加右边一条延长线（把下面的字段分成一段） */
.rcard .rmeta .mi.grp{grid-column:1/-1;align-items:center;padding:3px 0 0;flex-wrap:nowrap}
.rcard .rmeta .mi.grp span{font-size:12px;font-weight:600;color:#667085;white-space:nowrap}
.rcard .rmeta .mi.grp::after{content:'';flex:1;height:1px;background:#edf0f6}
/* 分组勾了「隐藏名称」：字收掉，只留右边那条延长线当分隔 */
.rcard .rmeta .mi.grp.noname{padding:1px 0}
.rcard .rmeta .mi.grp.noname span{display:none}
.rcard .rmeta .mi.grp{border-color:transparent}
/* ---------- 容器：格子里的一张小表（里面还能放字段 / 再套一个容器） ----------
   它跟外面的字段一模一样：占「几格宽 × 几个行高」（card.js 写成内联的 span / min-height），
   肚子里那张小网格用**它自己的** --cc 分格，跟外面一行几格无关。 */
.rcard .rmeta .mib{display:flex;flex-direction:column;gap:5px;min-width:0;overflow:hidden;
  border:1px solid transparent;border-radius:8px;padding:5px 6px}
.rcard .rmeta .mib.bd{border-color:#e9eef6;background:#fcfdff}
.rcard .rmeta .mib .bt{font-size:11.5px;font-weight:600;color:#667085;line-height:1.4;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rcard .rmeta .mib .rmeta{gap:var(--crg) calc(var(--cg) * .7)}
/* 占了几行高的图片：把这一格的高度撑满（留给以后往卡片上放图） */
.rcard .rmeta .mi.fill img.cimg{flex:1;height:100%;min-height:0;max-height:none}
/* ---------- 脚部：时间 · 操作按钮 ---------- */
.rcard .rfoot{display:flex;align-items:center;gap:8px;padding-top:9px;border-top:1px dashed #edf0f6}
.rcard .rfoot .rtime{font-size:12px;color:#98a2b3;white-space:nowrap}
.rcard .rfoot .sp{flex:1}
.rcard .ract{display:flex;gap:4px;flex:none}
/* 预览卡里的操作按钮只给个样子（真按钮点了会改数据），所以屏蔽触碰 */
.rcard.prev .ract.ccfg-fake{pointer-events:none;opacity:.75}

/* ---------- 「卡片显示列」配置弹窗：一页装完 ----------
   ① 上头一张「卡片外观」卡（排版 / 头部 / 状态 / 脚部，就四行）
   ② 下面一张「显示字段」卡（工具带 + 批量条 + 清单）
   ③ 右边一整栏预览
   两张卡同一个长相，控件统一 28px 高、8px 圆角，同行的小标签对齐在同一列上；
   所有能敲数的小框和 <select> 展开后都走 util.js 那块自绘面板（.ddpanel），全站一个观感。 */
/* 弹窗里三块的摆法（与 ccRender 一一对应）：顶上一条预览通栏，
   下面「卡片外观」「显示字段」左右并排 —— 预览画的是「列表上那张卡」（同一个渲染器），
   通栏后一行 8 格的格子约 100px，字段名与值能并排站住，看得清真实排版。
   宽度要给足：这两块里一行都塞着好几枚控件（「状态」那一行是 字段 + 放哪 + 样式 三枚），
   1160px 时每块只剩 530px，塞不下的行就折成两行，末尾那几枚小按钮被 margin-left:auto
   推到第二行最右边 —— 一行折成两行、还折得歪歪扭扭，就是又挤又丑的来源。
   1680px 是量着最挤的那两处定的：
     · 「显示字段」那条工具带（选中 / 上卡 / 插入 / 一键排版 四组十一枚按钮，约 670px）
       必须一行排得下，否则最后一组会孤零零掉到第二行；
     · 「卡片外观」那几行控件更多，左栏还要再宽一点（见下面 .cc-app 的 flex-grow）。 */
.ov.ccfgwide>.box{width:min(1680px,96vw);max-width:96vw}
/* 内容区不整体上下滚：改成竖排 flex，剩下的高度平分给下面两块，各滚各的
   （选择器带上 .box .bb 才压得住「.box .bb{overflow:auto}」那条通用规则） */
.box .bb.ccfg-bb{overflow:hidden;display:flex;flex-direction:column}
.ccfg-wrap{display:flex;flex-direction:column;gap:12px;flex:1;min-height:0}
/* ① 预览：通栏、定高（卡片高矮不定，超了在框里滚，不会把下面两块顶下去） */
.ccfg-prev{flex:none;margin-bottom:0}
.ccfg-prev>.cc-set-b{padding:10px 12px 12px}
/* ② 下半边：两块并排，一样高、各自在框里滚。宽窄按内容分：「卡片外观」一行里控件多
   （一条状态就是「字段 + 放哪 + 样式」三枚），比右边略宽一点点就好；
   右边那条工具带（约 670px）是全场最宽的硬需求，宁可让给右边 —— 它一折行，
   最后一组按钮就会孤零零掉到第二行，比左栏紧一点更难看。 */
.ccfg-mods{flex:1;min-height:0;display:flex;gap:12px;align-items:stretch}
.ccfg-mods>.cc-set{flex:1 1 0;min-width:0;min-height:0;margin-bottom:0;
  display:flex;flex-direction:column}
.ccfg-mods>.cc-app{flex-grow:1.08}
.ccfg-mods>.cc-set>.cc-set-b{flex:1;min-height:0;overflow:auto}
/* 「显示字段」那块：工具带与批量条钉在上头，只有清单自己滚 */
.ccfg-list>.cc-set-b{display:flex;flex-direction:column;overflow:hidden;padding:10px 12px 12px}
.ccfg-list .cc-list{flex:1;min-height:140px;max-height:none}
/* 窗口窄就上下排：三块变成一条竖列，整页自己上下滚，两块各给一个高度上限、在里头滚。
   门槛放在 1240px（＝并排时每块只剩 600px 以下）：再窄下去，一行里的控件就塞不下要折行了。
   这里一律改成 display:block —— 竖排时留空的 flex:1 会把下面那块压成 0 高（内容溢出去，
   外层又 overflow:hidden，整块就"显示不全"了）；选择器也都带上 .box .bb / .ccfg-mods
   才压得住上面那几条更具体的规则。 */
@media(max-width:1240px){
  .box .bb.ccfg-bb{display:block;overflow:auto}
  .ccfg-wrap{display:block;min-height:0}
  .ccfg-prev{margin-bottom:12px}
  .ccfg-mods{display:block;min-height:0}
  .ccfg-mods>.cc-set{display:block;margin-bottom:0}
  .ccfg-mods>.cc-set+.cc-set{margin-top:12px}
  .ccfg-mods>.cc-set>.cc-set-b{min-height:0;max-height:52vh;overflow:auto}
}
/* 预览框：占满这一栏。早先会按「一行几张卡」把自己缩到 35% / 52% 宽，
   缩完卡片内容挤成一团根本看不清；现在宽度这个事交给底下的说明文字。
   垫一层很浅的底：卡片是白的，白底上再摆白卡只能靠那条 1px 边框认边界，
   浅底一垫，卡片（自带阴影）就浮起来了 —— 一眼看出"这是一张卡"。 */
.cc-prev{border:1px solid var(--line);border-radius:12px;background:#f8fafd;padding:15px;width:100%;
  height:clamp(210px,30vh,320px);overflow:auto;box-shadow:inset 0 1px 2px rgba(16,24,40,.03)}
.cc-prev-note{margin-top:8px;font-size:11.5px;color:#98a2b3;line-height:1.6}
.cc-nodata{padding:30px 12px;text-align:center;font-size:12px;color:#98a2b3;line-height:1.8}
/* ---------- ② 显示字段：一行一项 ---------- */
/* 工具带：同一类事挨在一起，中间一条竖线隔开，不再是一长串按钮挤成一堆 */
.cc-bar{display:flex;align-items:center;gap:5px 10px;flex-wrap:wrap;margin-bottom:8px}
.cc-grp{display:inline-flex;align-items:center;gap:5px;padding-right:10px;border-right:1px solid #eef1f6}
.cc-grp:last-child{border-right:0;padding-right:0}
.cc-grp .t{color:#98a2b3;font-size:11.5px}
/* 窗口不够宽时（并排两块、右边那块不到 ~700px）先把这四枚组名小字收掉：
   按钮自己「全选 / 全部上卡 / 分组标题 / 单列」就把事说明白了，竖线分隔还在，
   分组照样看得出来 —— 总比最后那一组被挤到第二行、孤零零挂在那儿强。
   1560px 这个门槛是量出来的：再宽一点，带上组名也排得下（见上面弹窗宽度那段）。 */
@media(max-width:1560px){ .cc-bar .cc-grp>.t{display:none} }
.cc-list{border:1px solid #eef1f6;border-radius:10px;overflow-y:auto;background:#fff;
  max-height:calc(100vh - 470px);min-height:150px}
/* 一行：拖动把 · 序号 · 多选框 · 字段名 …… 宽 / 高 / 对齐 · ⋯ · ↑↓✕ */
.cc-row{display:flex;align-items:center;gap:7px;padding:6px 10px;font-size:12.5px;
  border-top:1px solid #f4f6fb;background:#fff;min-height:40px}
.cc-row:first-child{border-top:0}
.cc-row:hover{background:#f8faff}
.cc-row.grp{background:#fbfbfe}
.cc-row .hdl{flex:none;color:#c2c9d8;cursor:grab;user-select:none;padding:0 2px;font-size:13px;letter-spacing:-1px}
.cc-row .hdl:hover{color:#6366f1}
.cc-row .ord{flex:none;min-width:20px;text-align:center;color:#a9b1c0;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px}
.cc-row .cl{display:inline-flex;align-items:center;gap:4px;cursor:pointer;white-space:nowrap;color:#475467}
.cc-row .cl input{margin:0;cursor:pointer}
.cc-row .cl i{font-style:normal;color:#98a2b3;font-size:11.5px}
.cc-row .nm{color:#344054;font-weight:500;min-width:80px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cc-row:not(.on) .nm,.cc-row:not(.on) .sel,.cc-row:not(.on) .num,.cc-row:not(.on) .cc-col,
.cc-row:not(.on) input.gname{opacity:.55}
.cc-row .sp{flex:1}
/* 「宽 3」「高 0」「对齐 居中」：小灰字标签 + 控件，每行的标签都对齐在同一列上 */
.cc-row .fs,.cc-l2 .fs,.cc-bat .fs{display:inline-flex;align-items:center;gap:4px;flex:none}
/* 这一列小标签（宽 / 高 / 对齐 / 内列）一律占 26px：单字的「宽」也留出两个字的位置，
   于是同一行的「宽 / 高 / 对齐」在行与行之间落在同一条竖线上。
   早先按内容自然宽，容器行那个三字标签把整排控件往左推十来像素，一行一个样。
   （「格」那种跟在框后面的后缀标签不在此列 —— 它不需要参与这列对齐） */
.cc-row .fs .lb,.cc-l2 .fs .lb,.cc-bat .fs .lb{color:#98a2b3;font-size:11.5px;white-space:nowrap;min-width:26px}
.cc-set-r .lb{color:#98a2b3;font-size:11.5px;white-space:nowrap}
/* 尾部三枚（↑ ↓ ✕）：平时淡着，鼠标移到这一行才亮 —— 一行里一眼要看的就只剩字段名与宽/高/对齐 */
.cc-row .acts{display:inline-flex;align-items:center;gap:1px;flex:none;opacity:0;transition:opacity .12s}
.cc-row:hover .acts,.cc-row.sel .acts,.cc-row:focus-within .acts{opacity:1}
@media(hover:none){ .cc-row .acts{opacity:1} }      /* 触屏没有 hover，就一直亮着 */
.cc-row .mini{border:0;background:transparent;color:#98a2b3;cursor:pointer;font-size:13px;line-height:1;padding:0 3px}
.cc-row .mini:hover{color:var(--brand)}
.cc-row .mini.x:hover{color:#dc2626}
.cc-row .mini.exp{flex:none;height:28px;min-width:30px;padding:0 6px;border:1px solid #e4e8f0;border-radius:8px;
  background:#fff;font-size:12px;color:#667085}
.cc-row .mini.exp:hover{border-color:#c7d2fe;color:var(--brand)}
.cc-row .mini.fold{flex:none;width:14px;padding:0 2px;font-size:11px;color:#667085}
/* 拖动排序：整块抬起来一点，别让用户以为没抓住（容器＝连里面那张列表一起抬） */
.cc-row.dragging,.cc-wrap.dragging{background:#eef1ff;border-radius:8px;position:relative;z-index:2;
  box-shadow:0 6px 16px rgba(16,24,40,.14)}
.cc-row .hdl{touch-action:none;-webkit-user-select:none;user-select:none}
body.cc-drag{cursor:grabbing;user-select:none}
body.cc-drag .cc-row .hdl{cursor:grabbing}
/* 行里、批量条里、外观卡里的控件全是一个长相：28px 高、8px 圆角、同一个边色 —— 看着才齐 */
.cc-row select,.cc-row input.num,.cc-row input.gname,.cc-l2 select,.cc-l2 input.num,
.cc-l2 input.hex,.cc-bat select,.cc-bat input.num,.cc-bat input.hex,.cc-set input.hex,
.cc-set select,.cc-set input.num,.cc-bar input.num{height:28px;border:1px solid #e4e8f0;border-radius:8px;
  background:#fff;color:#344054;font-size:12.5px;outline:none;padding:0 7px}
/* 能敲个数的小框（占几格 / 占几行 / 值行数 / 容器里一行几格 / 排版列数）：
   点一下或聚焦就展开全站那块自绘面板（util.js 的 .ddpanel），候选是固定那几张；
   框里照样能直接敲，敲过的值不留痕。右边那枚小角标＝「这儿有现成的数可挑」，
   与旁边 <select> 收起时用的是同一枚箭头。 */
.cc-row input.num,.cc-l2 input.num,.cc-bat input.num,.cc-set input.num,.cc-bar input.num{
  width:38px;flex:none;text-align:center;padding:0 15px 0 4px;font-variant-numeric:tabular-nums;
  cursor:text;background-image:var(--dd-arrow);background-repeat:no-repeat;
  background-position:right 3px center;background-size:9px 9px}
.cc-row input.num.w2,.cc-l2 input.num.w2,.cc-bat input.num.w2{width:54px}   /* 占几格：可能是「整行」两个字 */
.cc-bar input.num{width:54px}                                              /* 排版：可能是「12 项」两三个字 */
.cc-row select,.cc-l2 select,.cc-bat select,.cc-set select{padding-right:20px;cursor:pointer;appearance:none;
  -webkit-appearance:none;-moz-appearance:none;background-color:#fff;
  background-image:var(--dd-arrow);background-repeat:no-repeat;background-position:right 6px center;background-size:11px 11px}
/* 聚焦时的一圈淡紫光：和 .ddinput 用同一套（图二那个下拉弹层就是这个观感） */
.cc-row input.num:focus,.cc-row input.gname:focus,.cc-row select:focus,.cc-row .ddinput:focus,
.cc-l2 input.num:focus,.cc-l2 input.hex:focus,.cc-l2 select:focus,
.cc-bat input.num:focus,.cc-bat input.hex:focus,.cc-bat select:focus,
.cc-set input.num:focus,.cc-set input.hex:focus,.cc-set select:focus,.cc-bar input.num:focus{
  border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.cc-row input.num::placeholder,.cc-l2 input.num::placeholder{color:#c2c9d8}
.cc-row input.gname{width:150px}
.cc-row.grp input.gname{width:200px}
.cc-row.box input.gname{width:130px}
/* 色值框：紧挨着挑色的小色块，#f4f6fb / f4f6fb / #abc 都能直接敲 */
.cc-l2 input.hex,.cc-bat input.hex{width:80px;flex:none;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.cc-l2 input.hex::placeholder,.cc-bat input.hex::placeholder{color:#c2c9d8;font-family:inherit}
/* ---------- 多选：行首那个小方框 + 上面那条「批量设置」 ---------- */
.cc-row .pick{flex:none;margin:0;cursor:pointer;width:14px;height:14px;accent-color:var(--brand)}
.cc-row.sel{background:#eef1ff;box-shadow:inset 3px 0 0 var(--brand)}
.cc-bat{border:1px solid #dbe1f7;background:#f7f9ff;border-radius:10px;padding:9px 11px;margin-bottom:8px}
.cc-bat .cc-bt{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:12.5px;color:#475467}
.cc-bat .cc-bt+.cc-bt{margin-top:8px;padding-top:8px;border-top:1px dashed #e4e9f8}
.cc-bat .cc-bt .t{font-weight:600;color:#4f46e5}
.cc-bat .cc-bt .sp{flex:1}
.cc-bat .sub{font-weight:400;color:#98a2b3;font-size:11.5px}
.cc-bat .cl,.cc-l2 .cl{display:inline-flex;align-items:center;gap:4px;cursor:pointer;white-space:nowrap;color:#475467}
.cc-bat .cl input,.cc-l2 .cl input{margin:0;cursor:pointer}
.cc-bat .lb,.cc-l2 .lb{color:#a9b1c0;white-space:nowrap;font-size:11.5px}
/* ---------- 容器：外面一层套住「它自己那一行 + 肚子里那张列表」 ---------- */
.cc-wrap{position:relative}
.cc-row.box{background:#f7f8fc}
.cc-row.box .bx{flex:none;color:#a5b4fc;font-size:13px}
.cc-list.kids{margin:0 0 0 22px;border:1px solid #e8eaf3;border-left:2px solid #cfd6ea;border-radius:0 10px 10px 0;
  border-top:0;max-height:none;min-height:0;overflow:visible;background:#fff}
.cc-list.kids .cc-row:first-child{border-top:0}
.cc-empty{padding:14px 12px;font-size:11.5px;color:#a9b1c0;text-align:center;line-height:1.7}
/* 细调那一行（点 ⋯ 摊开的那行 / 容器张开时的那行） */
.cc-l2{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12.5px;color:#475467;
  padding:8px 10px 8px 26px;margin:0;background:#fafbff;border-top:1px dashed #eaf0f8}
.cc-l2 .t{flex:none;font-size:11.5px;font-weight:600;color:#98a2b3}
.cc-l2 .cl i,.cc-bat .cl i{font-style:normal;font-size:11.5px;color:#475467}
/* 描底色那个小色块：input[type=color] 自带一圈厚边框还留白，压瘦一点才与旁边的下拉一般高 */
.cc-col{width:28px;height:28px;flex:none;padding:0;border:1px solid #e4e8f0;border-radius:8px;
  background:none;cursor:pointer;vertical-align:middle}
.cc-col::-webkit-color-swatch-wrapper{padding:2px}
.cc-col::-webkit-color-swatch{border:none;border-radius:5px}
/* ---------- ① 卡片外观：整张卡的公共样子，三行装完 ----------
   早先这块分在「② 卡片外观」页签里、还拆成三张带边框的小卡；现在合成一张卡的三个小标题行，
   跟字段清单同处一页 —— 改什么都在这一个屏里。 */
.cc-set{border:1px solid var(--line);border-radius:12px;background:#fff;margin-bottom:12px;overflow:hidden}
.cc-set-h{display:flex;align-items:center;gap:8px;padding:9px 12px;background:#fafbfd}
.cc-set:not(.fold) .cc-set-h{border-bottom:1px solid var(--line)}
.cc-set-h b{font-size:12.5px;color:#344054}
.cc-set-h .sub{font-size:11.5px;color:#98a2b3}
.cc-set-h .sp{flex:1}
/* 收起 / 展开：按钮原来挂的是 .mini.exp，可那套样式写在 .cc-row 里（标题条上够不着），
   于是落成浏览器默认长相 —— 在一整条浅灰的标题上像块补丁。现在跟全站 .btn.sm 同一款，
   只是压矮一点、圆成药丸，前面那枚小箭头朝上＝收起、朝下＝展开。 */
.btn.cc-fold{height:24px;padding:0 10px 0 9px;gap:4px;font-size:11.5px;border-radius:999px;background:#fff}
.btn.cc-fold .cv{font-size:9px;line-height:1;color:#98a2b3}
.btn.cc-fold:hover .cv{color:var(--brand)}
.cc-set-b{padding:6px 12px 8px}
/* 「显示字段」那张卡的身子：里面是工具带 + 批量条 + 清单，四周留一样的白 */
.cc-set-b.flush{padding:10px 12px 12px}
/* 一行一件事：左边小标题（排版 / 头部 / 状态 / 脚部），右边一串控件 */
.cc-set-r{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12.5px;color:#475467;min-height:38px}
.cc-set-r+.cc-set-r{border-top:1px dashed #f0f3f9}
.cc-set-r .t{flex:none;width:34px;font-size:12px;font-weight:600;color:#667085}
/* 徽标配色（「头部」那行末尾那两对）：边框 / 高度 / 圆角 / 内边距走上面那条「控件统一长相」，
   这儿只补宽度与等宽字体。早先那条统一规则的后代选择器里漏了 `.cc-set input.hex`，
   于是这两枚色值框落成浏览器默认长相：比旁边的下拉高一头、边框又黑又粗 —— 一眼就不齐 */
.cc-set-r input.hex{width:80px;flex:none;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.cc-set-r input.hex::placeholder{color:#c2c9d8;font-family:inherit}
.cc-set-r .cc-col{width:26px;height:26px}
.cc-set-r .sp{flex:1}
/* 「标签 + 控件」这一对（ccFs 生成的 .fs）：与字段清单里那一行同一个长相 ——
   灰字标签与控件之间留 4px，别贴在一起 */
.cc-set-r .fs{display:inline-flex;align-items:center;gap:4px;flex:none}
/* 头部 / 状态 / 脚部三小节：小节头（带那几枚「＋」）与它下头的条目行用两种底色，
   一眼看出哪几行归哪一个小节管。 */
.cc-set-r.sec{background:#fafbfd;padding-top:6px;padding-bottom:6px}
.cc-set-r.sec+.cc-set-r{border-top:1px solid #eef1f7}
.cc-set-r.sec .t{color:#475467}
.cc-set-r .hint{font-size:11.5px;color:#98a2b3}
/* 条目行：缩进一点，行首一枚小标签（徽标 / 标题 / 状态 / 时间 / 小字 / 按钮） */
.cc-set-r.it{padding-left:22px;background:#fcfdff}
.cc-set-r.it .k{flex:none;min-width:30px;text-align:center;font-size:11px;font-weight:600;color:#7b8598;
  background:#f1f4fb;border-radius:5px;line-height:19px;padding:0 4px}
/* 条目末尾那三枚小按钮：一直亮着（一行就一条，不像字段清单那么挤，不必等鼠标移上去） */
.cc-set-r .acts{display:inline-flex;align-items:center;gap:1px;flex:none;margin-left:auto}
.cc-set-r .acts .mini{border:0;background:transparent;color:#98a2b3;cursor:pointer;
  font-size:13px;line-height:1;padding:0 3px}
.cc-set-r .acts .mini:hover{color:var(--brand)}
.cc-set-r .acts .mini.x:hover{color:#dc2626}
/* 脚部「小字」那个输入框：与旁边那排下拉 / 数字框同一款，只是宽一点好敲字 */
.cc-set-r input.txt{height:28px;flex:0 1 160px;min-width:110px;border:1px solid #e4e8f0;border-radius:8px;
  background:#fff;color:#344054;font-size:12.5px;outline:none;padding:0 7px}
.cc-set-r input.txt:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}

/* side drawer */
.ov{position:fixed;inset:0;background:rgba(15,23,42,.45);backdrop-filter:blur(2px);z-index:100;display:none;justify-content:flex-end;align-items:stretch}
.ov.show{display:flex}
.ov.ovp{z-index:120}
.ov>.box{background:#fff;border-radius:16px 0 0 16px;box-shadow:-10px 0 40px rgba(15,23,42,.25);width:780px;max-width:90%;display:flex;flex-direction:column;overflow:hidden;transform:translateX(100%);transition:transform .25s ease;margin-left:auto;height:100%;max-height:none}
.ov.show .box{transform:translateX(0)}
.box .bh{padding:13px 18px;border-bottom:1px solid var(--line);font-weight:800;font-size:15px;display:flex;align-items:center;gap:10px}
.box .bh .sp{flex:1}
.box .bb{padding:14px 18px;overflow:auto;flex:1}
.box .bf{padding:12px 18px;border-top:1px solid var(--line);display:flex;gap:8px;justify-content:flex-start;background:#fafbfd}
/* footer 没内容时整条收掉。例如「记录详情」：它的编辑 / 删除按钮放在内容区里，
   footer 是空的，不收掉就会在底下留一条带分隔线的空白灰边，看着像没加载出来。 */
.box .bf:empty{display:none}
/* 弹窗底部按钮一律左对齐。早先的 footer 会在按钮前塞一个 <span class="sp">/<span class="fmsp">
   当弹性占位，把「取消 / 保存」顶到右边；这里让占位彻底不占位（display:none 连 gap 一起消掉，
   否则按钮会凭空右移 8px）。保底规则：以后新写的 footer 即使忘了删占位，也是左对齐。 */
.box .bf>.sp,.box .bf>.fmsp{display:none}
.x{border:none;background:#f1f4f8;width:28px;height:28px;border-radius:8px;color:#475467;font-size:15px}
.x:hover{background:#e5e9f2}
@media(max-width:760px){.ov .box{width:100%;max-width:100%;border-radius:0}}
/* 两列表单：成对字段各占一半；落单字段也保持半宽（不被 grow 拉成整行），
   让每一行都凑成对要靠字段排列（把同行的短字段放在一起），
   CSS 不做拉伸 —— 避免「单独一个短字段被拉成整行宽」看起来像长输入框。
   字段顺序和 Tab 顺序都保持原样（DOM 顺序＝视觉顺序）。 */
.fgrid{display:grid;grid-template-columns:repeat(12,1fr);gap:10px 16px;align-items:start}
.fgrid>.fitem{grid-column:span 6}
.fgrid>.fitem.wide{grid-column:span 12}
@media(max-width:760px){.fgrid{grid-template-columns:1fr}.fgrid>.fitem,.fgrid>.fitem.wide{grid-column:span 1 !important}}
.fitem{display:flex;flex-direction:column;gap:4px;min-width:0;position:relative}
.fitem label{font-size:12px;font-weight:600;color:#475467}
.fitem .reqstar{color:#e5484d}
.fitem input[type=text],.fitem input[type=date],.fitem input[type=number],.fitem input[type=color],.fitem select,.fitem textarea{border:1px solid #d9deea;border-radius:9px;padding:8px 10px;outline:none;transition:.15s;background:#fff}
.fitem input[type=color]{padding:2px;height:34px;cursor:pointer}
.fitem input:focus,.fitem select:focus,.fitem textarea:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.fitem .fsel{position:relative}
/* 下拉角标：全站只用这一枚自绘箭头（图在 :root 的 --dd-arrow）。
   以前同一个表单里混着三种角标 —— ①清单表字段下拉的自绘箭头、②普通 <select> 的浏览器原生箭头、
   ③「可自由输入」的 input[list] 自带的原生下拉指示，三种高度/位置/颜色都不一样。
   现在把浏览器自带的都藏掉，统一用同一枚 svg。 */
.fitem select,
.fitem input[list]{color:var(--ink);appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:var(--dd-arrow);
  background-repeat:no-repeat;background-position:right 10px center;background-size:12px 12px;padding-right:30px}
/* 可输入下拉（input[list]）：Chrome / Edge 的原生下拉指示就是这个伪元素，一并藏掉 */
.fitem input[list]::-webkit-calendar-picker-indicator{display:none;opacity:0}
/* 带「清空 ×」的字段下拉（清单表）：角标仍是同一枚，只是文字要多让开一点 */
.fitem .fsel select{width:100%;box-sizing:border-box;padding-right:54px}
.fitem .fsel select:required:invalid{color:#9aa3b2}
.fitem .fsel option{color:#1e2733;background:#fff}
.fitem .fsel .selclear{position:absolute;right:28px;top:50%;transform:translateY(-50%);border:none;background:transparent;padding:0;width:19px;height:19px;border-radius:50%;color:#98a2b3;font-size:13px;line-height:1;cursor:pointer;display:none;align-items:center;justify-content:center}
.fitem .fsel.has .selclear{display:flex}
.fitem .fsel .selclear:hover{color:#344054;background:#e8ecf4}
.fitem input:disabled{background:#f4f6fa;color:#98a2b3}
.fitem textarea{resize:vertical;line-height:1.55}
.fhint{font-size:11px;color:#98a2b3}
.ff .reqstar{color:#e5484d}

/* ================= 12 栅格通用表单弹窗（formmodal.js） ================= */
/* 弹窗体内部说明 */
.fmhint{font-size:12.5px;color:#64748b;margin:0 0 12px;line-height:1.6}
/* 12 列栅格：每行 12 等分，每个字段宽度 = span 份 */
.fgrid12{display:grid;grid-template-columns:repeat(12,1fr);gap:10px 16px;align-items:start}
.fgrid12>.ff,.fgrid12>.fv{grid-column:span 12}
/* 默认 span（移动端 / 窄屏下让每个字段都占满，避免挤压看不清） */
@media (max-width:760px){
  .fgrid12{grid-template-columns:1fr}
  .fgrid12>.ff,.fgrid12>.fv{grid-column:span 1 !important}
}
/* 单个字段框：与原 .fitem 视觉一致 */
.ff{display:flex;flex-direction:column;gap:4px;min-width:0;position:relative}
.ff>label{font-size:12px;font-weight:600;color:#475467}
/* 输入框底色统一用 background-color（不能用 background 简写，否则会把下面的下拉角标图一起抹掉） */
.ff>input[type=text],.ff>input[type=date],.ff>input[type=number],.ff>input[type=color],.ff>textarea,.ff .fsel>select{
  border:1px solid #d9deea;border-radius:9px;padding:8px 10px;outline:none;transition:.15s;background-color:#fff;width:100%;box-sizing:border-box;font:inherit;color:inherit}
.ff>input[type=color]{padding:2px;height:34px;cursor:pointer}
.ff>input:focus,.ff>select:focus,.ff>textarea:focus,.ff .fsel>select:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.ff>input:disabled{background-color:#f4f6fa;color:#98a2b3}
.ff>textarea{resize:vertical;line-height:1.55;font-family:inherit}
/* 下拉框：之前只给 .ff>select 写了样式，但 select 实际套在 .ff>.fsel 里，
   选择器根本没命中 —— 结果这套弹窗里的下拉一直是浏览器原生样式（原生灰边框 + 原生箭头），
   和 .fitem 里的字段长得不一样。这里补上：统一自绘角标 + 圆角边框。 */
.ff select,.ff input[list]{appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:var(--dd-arrow);background-repeat:no-repeat;background-position:right 10px center;background-size:12px 12px;
  padding-right:30px;cursor:pointer}
.ff select option{color:#1e2733;background:#fff}
.ff input[list]::-webkit-calendar-picker-indicator{display:none;opacity:0}
/* 带「清空 ×」的字段下拉：角标仍是同一枚，只是文字多让开一点，× 压在角标左边 */
.ff .fsel{position:relative;display:block}
.ff .fsel>select{width:100%;box-sizing:border-box;padding-right:54px}
.ff .fsel>select:required:invalid{color:#9aa3b2}
.ff .fsel>.selclear{position:absolute;right:28px;top:50%;transform:translateY(-50%);border:none;background:transparent;padding:0;width:19px;height:19px;border-radius:50%;color:#98a2b3;font-size:13px;line-height:1;cursor:pointer;display:none;align-items:center;justify-content:center;z-index:3}
.ff .fsel.has>.selclear{display:flex}
.ff .fsel>.selclear:hover{color:#344054;background:#e8ecf4}
/* 多选下拉（清单表上的「团队」）：样子照抄上面那排单选下拉 —— 同一枚角标、同一套圆角边框、
   右边同样留出一枚「清空 ×」，点开才是一张带勾选框的浮层（团队一多，铺一排格子会把表单撑得老高）。
   浮层 .msel-pop 是点开时临时建在 body 上的（见 formmodal.js 的 fmMultiOpen），
   所以这里的定位只看视口：fixed + 高 z-index，压在弹窗（z-index 100/120）之上。 */
/* 两处表单容器都用：.fitem（机构 / 团队 / 用户等手写弹窗）和 .ff（清单表的记录表单） */
.fsel.msel{display:block;min-width:0}
.msel-btn{width:100%;box-sizing:border-box;border:1px solid #d9deea;border-radius:9px;padding:8px 54px 8px 10px;
  background-color:#fff;background-image:var(--dd-arrow);background-repeat:no-repeat;background-position:right 10px center;background-size:12px 12px;
  text-align:left;font:inherit;color:inherit;cursor:pointer;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:.15s}
.msel-btn:hover{border-color:#c7cee0}
.msel.open>.msel-btn{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
/* 一个都没勾：灰字占位，和必填单选的「未选择」一个观感 */
.msel-btn .ph{color:#9aa3b2}
.msel-pop{position:fixed;z-index:400;max-height:264px;overflow:auto;box-sizing:border-box;
  background:#fff;border:1px solid #d9deea;border-radius:10px;box-shadow:0 14px 32px rgba(16,24,40,.18);padding:5px}
.msel-opt{display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;cursor:pointer;font-size:12.5px;color:#344054}
.msel-opt:hover{background:#eef2ff}
.msel-opt input{margin:0;cursor:pointer;flex:none}
.msel-opt span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ================= 编辑布局模式 =================
   两套表单容器都要能进编辑态：
     .fgrid12 > .ff     —— 清单表的记录表单（formmodal.js 渲染）
     .fgrid12 > .fv     —— 清单表的记录详情（同一套引擎，但样式独立，见上面 .fv 一组）
     .fgrid   > .fitem  —— 机构 / 团队 / 角色 / 用户 / 资料 / 字典 / 菜单等手写弹窗
   三边共用同一套把手与工具条，所以下面统一用 .fgrid12.editing, .fgrid.editing 并列选择器。 */
/* 编辑模式下整个栅格背景染一层淡色，提示「现在是布局模式」 */
.fgrid12.editing,.fgrid.editing{background:repeating-linear-gradient(45deg,#f6f7ff,#f6f7ff 6px,#eef0ff 6px,#eef0ff 12px);padding:12px;border-radius:10px}
.ff.ff-edit,.fitem.ff-edit,.fv.ff-edit{outline:1.5px dashed #6366f1;outline-offset:3px;background:#fff}
/* 拖拽把手 */
.fm-handle{position:absolute;background:rgba(99,102,241,.55);border-radius:3px;z-index:5;transition:.12s}
.fm-handle-r{right:-6px;top:0;bottom:0;width:10px;cursor:col-resize}
.fm-handle-b{left:0;right:0;bottom:-6px;height:10px;cursor:row-resize}
/* 排序把手：左上角那枚 ⠿，按住可把整个字段拖到别的位置 */
.fm-handle-g{left:-7px;top:-7px;width:20px;height:20px;background:#6366f1;color:#fff;font-size:12px;line-height:20px;text-align:center;border-radius:6px;cursor:grab;user-select:none;font-weight:700}
.fm-handle-g:hover{background:#4f46e5}
/* 拖动排序时：跟随指针的浮动替身 + 目标位置的空占位框 */
.fm-ghost{position:fixed;z-index:300;pointer-events:none;opacity:.92;background:#fff;border-radius:10px;padding:8px;box-sizing:border-box;box-shadow:0 14px 34px rgba(15,23,42,.28)}
.fm-sort-ph{border:1.5px dashed #6366f1;border-radius:10px;background:#eef0ff;min-height:46px;box-sizing:border-box}
body.fm-sorting,body.fm-sorting *{cursor:grabbing !important;user-select:none !important;-webkit-user-select:none !important}
/* label 可改名提示 */
.ff-edit>label{cursor:text;border-bottom:1px dashed transparent;padding-bottom:1px}
.ff-edit>label:hover{border-bottom-color:#6366f1}
/* 编辑态顶部提示条（面板里是右侧工作台的说明） */
.fm-editbar{display:flex;align-items:center;gap:10px;background:#eef0ff;border:1px solid #c5cdf7;border-radius:9px;padding:8px 12px;margin-bottom:10px}
.fm-edithint{flex:1;font-size:12.5px;color:#475467;line-height:1.5}
.fm-edithint b{color:#4f46e5}
/* 拖拽进行中：禁用文本选中 */
body.fm-dragging,body.fm-dragging *{user-select:none !important;-webkit-user-select:none !important}
/* 布局模式下输入框置灰并锁住交互（只调布局，别误改数据）。
   把手必须重新开启 pointer-events，否则布局模式下拖不了把手。
   注意用 background-color，别把下拉角标图抹掉。 */
.fgrid12.editing .ff input,.fgrid12.editing .ff select,.fgrid12.editing .ff textarea,
.fgrid.editing .fitem input,.fgrid.editing .fitem select,.fgrid.editing .fitem textarea{
  pointer-events:none;background-color:#f4f6fa;color:#98a2b3}
/* 表单里的按钮（清空 ×、自定义字段的加/删、附件删除…）一律点不动，避免「调布局时误触」 */
.fgrid12.editing .ff button,.fgrid.editing .fitem button{pointer-events:none}
.fgrid12.editing .ff .fm-handle,.fgrid.editing .fitem .fm-handle{pointer-events:auto}

/* ================= 弹窗布局工作台（更多操作 → 弹窗布局设置） =================
   左边＝弹窗清单（当前页面的排最前），右边＝该弹窗的真实预览，可直接拖拽调整。
   预览是把弹窗真正渲染一遍再"截"过来，所以所见即所得；不用先关掉面板再开目标弹窗。 */
.ov.fmwide>.box{width:1010px}
.fmwork{display:flex;gap:14px;align-items:flex-start}
.fmwnav{width:198px;flex:none;display:flex;flex-direction:column;gap:3px;position:sticky;top:0;max-height:calc(100vh - 210px);overflow:auto;padding-right:2px}
.fmwnav-h{font-size:11px;font-weight:700;color:#98a2b3;padding:9px 8px 3px;letter-spacing:.6px}
.fmwnav-g{font-size:11.5px;color:#b4bccb;padding:6px 8px 2px}
.fmwnav-i{display:flex;align-items:center;gap:6px;text-align:left;border:1px solid transparent;background:#f6f7fb;border-radius:8px;padding:7px 9px;font-size:12.5px;color:#475467;cursor:pointer;transition:.12s;font:inherit;font-size:12.5px;line-height:1.4}
.fmwnav-i:hover{background:#eef0ff;color:#4f46e5}
.fmwnav-i.on{background:#eef0ff;border-color:#c7d2fe;color:#4f46e5;font-weight:700}
.fmwnav-i .fmwnav-d{margin-left:auto;flex:none;width:6px;height:6px;border-radius:50%;background:#22c55e}
.fmwmain{flex:1;min-width:0}
/* 限制成和真弹窗内容区一样宽（780px 的 box 减去左右各 18px 内边距），
   这样预览里"半宽 / 全宽"看到的比例和真正打开时一致，不会因为工作台更宽而失真 */
.fmwstage{border:1px solid #e5e9f2;border-radius:10px;padding:10px;background:#fff;max-width:744px;box-sizing:border-box}
.fmwstage .fm-tid{display:none}
.fmwempty{font-size:13px;color:#98a2b3;padding:40px 20px;text-align:center;line-height:1.8}
/* ── 记录详情的「字段块」：独立命名空间 .fv，和表单字段（.ff）彻底分家 ──
   详情是「看」的、表单是「填」的。早先两者共用 .ff，表单那套「灰底圆角输入框」的样式
   就连带把详情页画成一堆假输入框 —— 所以详情块单独用 .fv，改表单永远改不到它。
   外观还原成以前那张表（原先由 .kvgrid / .prow / .pcell 画）：
   整张表一个圆角外框，标签格灰底 + 右分隔线，行列之间一条细线。
   做法：外层 .fvtable 用 1px 的 gap，底色就是线的颜色 —— 缝隙自然成了表格线；
   每个 .fv 是白底、标签在左值在右，和 .pcell 的观感一致。
   底下仍然是 .fgrid12 栅格、每块带 data-fid，所以「弹窗布局设置」照样能拖顺序 / 调宽度。 */
.fvtable{background:#f1f3f8;gap:1px;border:1px solid #e5e9f0;border-radius:12px;overflow:hidden;align-items:stretch}
/* 进编辑态时放开裁切，否则最右边那列的拖拽把手（悬在格子外侧 -6px）会被切掉够不着 */
.fvtable.editing{overflow:visible}
.fv{background:#fff;display:flex;align-items:stretch;min-width:0;position:relative;
  border-bottom:5px solid #fff}
/* 容易看花眼的是「标签」：灰底原来从上铺到下，一行接一行连成一条灰带，糊成一片。
   标签格 140px：「预处理工时（人/天）」十个字要 114px，110px 装不下会顶到分隔线。
   做法：每个字段块（.fv，布局设置里的一「格」）自己带一条 5px 白底边 ——
   格子拉多宽白条就多长，和布局设置里的单元等长。标签灰底铺到白条为止就收住。
   上下内边距 10px → 8px，正好抵掉这 5px，整行高度没变。 */
.fv>.pl{width:140px;flex:none;padding:8px 12px;border-right:1px solid #f1f3f8;
  background:#f8fafd;
  color:#667085;font-size:12px;font-weight:600;line-height:1.5;white-space:nowrap}
.fv>.pv{flex:1;min-width:0;padding:8px 12px;color:#1e2733;font-size:13px;line-height:1.5;
  word-break:break-word;white-space:pre-wrap}
.fv>.pv:empty::before{content:'—';color:#a8b0bf}
.fm-tid{display:none}

/* column picker / export */
.colpick{display:grid;grid-template-columns:repeat(auto-fill,minmax(186px,1fr));gap:8px}
.colpick label{display:flex;align-items:flex-start;gap:7px;border:1px solid #e5e9f2;border-radius:9px;padding:7px 9px;font-size:12.5px;cursor:pointer;transition:.12s}
.colpick label:hover{border-color:#b9c4ff;background:#f6f7ff}
.colpick label.on{border-color:#6366f1;background:#eef0ff}
.colpick input{display:none}
.colpick .ck{flex:none;width:15px;text-align:center;line-height:1.45;color:#98a2b3}
.colpick label.on .ck{color:#6366f1}
.colpick .tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.colpick .tx b{font-weight:600;color:#475467;line-height:1.4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.colpick .tx i{font-style:normal;font-size:10.5px;color:#98a2b3;line-height:1.4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.colpick label.on .tx b{color:#4338ca}
/* column manager: reorder + rename */
.colmgr{display:flex;flex-direction:column;gap:6px;max-height:60vh;overflow:auto;padding-right:4px}
.colmgr .crow{display:flex;align-items:center;gap:8px;border:1px solid #e5e9f2;border-radius:9px;padding:8px 10px;background:#fff;transition:.12s}
.colmgr .crow.on{border-color:#6366f1;background:#eef0ff}
.colmgr .crow .ord{width:24px;text-align:center;font-size:12px;color:#98a2b3;font-variant-numeric:tabular-nums;flex:none}
.colmgr .crow .mv{display:flex;flex-direction:column;gap:1px;flex:none}
.colmgr .crow .mv button{width:22px;height:17px;padding:0;border:none;background:#f1f4f8;color:#475467;border-radius:4px;font-size:11px;line-height:1;cursor:pointer}
.colmgr .crow .mv button:hover{background:#e0e6f5}
.colmgr .crow .mv button:disabled{opacity:.35;cursor:not-allowed}
/* 固定列（#序号 / 创建时间 / 操作…）：位置钉死，不给 ↑ ↓，换成一把小锁 */
.colmgr .crow.pin{border-style:dashed}
.colmgr .crow .pinfix{width:22px;text-align:center;font-size:11px;flex:none;cursor:help;opacity:.8;line-height:1}
.colmgr .crow .chk{width:18px;text-align:center;color:#6366f1;font-size:14px;cursor:pointer;flex:none;user-select:none}
.colmgr .crow .name{flex:1;font-size:13px;color:#1e2733;cursor:text;padding:3px 6px;border-radius:5px;border:1px solid transparent;word-break:break-all}
.colmgr .crow .name:hover{background:#f6f7ff;border-color:#e5e9f2}
.colmgr .crow .name input.edit{width:100%;border:1px solid #6366f1;border-radius:5px;padding:4px 7px;font-size:13px;outline:none;box-shadow:0 0 0 2px rgba(99,102,241,.12);background:#fff}
.colmgr .hint{font-size:11.5px;color:#98a2b3;margin-bottom:10px;line-height:1.5}
.colmgr .crow{flex-wrap:wrap}
.colmgr .crow .name{flex:1;min-width:140px}
.lset{display:flex;gap:6px;flex:1;justify-content:flex-end;min-width:180px}
.lset select{border:1px solid #e5e9f2;border-radius:6px;padding:3px 6px;font-size:12px;color:#475467;background:#fff;outline:none}
.lset select:focus{border-color:#6366f1}
/* 标题（表头）对齐：与左侧数据格排版分组，中间加一道竖线，一眼能看出是两份独立配置 */
.thset{display:flex;align-items:center;gap:4px;flex:none;margin-left:2px;padding-left:9px;border-left:1px solid #e5e9f2}
.thset em{font-style:normal;font-size:11.5px;color:#98a2b3}
.thset select{border:1px solid #e5e9f2;border-radius:6px;padding:3px 6px;font-size:12px;color:#475467;background:#fff;outline:none}
.thset select:focus{border-color:#6366f1}
/* —— 列宽 ——
   ① 配置弹窗里的「宽度」输入（长期生效，存 prefs）；
   ② 列表表头右边界 8px 是拖拽区，拖动＝临时宽度（只存内存，刷新还原）。
      命中判定与光标切换都在 JS 里做（th 上的内联 cursor），不影响表头本身的 sticky。 */
.wset{display:flex;align-items:center;gap:4px;flex:none;margin-left:2px;padding-left:9px;border-left:1px solid #e5e9f2}
.wset em{font-style:normal;font-size:11.5px;color:#98a2b3}
.wset input{width:62px;border:1px solid #e5e9f2;border-radius:6px;padding:3px 6px;font-size:12px;color:#475467;background:#fff;outline:none}
.wset input:focus{border-color:#6366f1}
.wset i{font-style:normal;font-size:11px;color:#b8c0d0}
body.colresize,body.colresize *{cursor:col-resize!important;user-select:none!important}
/* —— Excel 导入弹窗：一张工作表一张卡片，左边「表名 + 匹配情况」，右边「导入到哪张清单」 —— */
.xlhint{font-size:11.5px;color:#98a2b3;line-height:1.65;margin:0 0 10px}
.xlhint b{color:#475467}
.xlrows{display:flex;flex-direction:column;gap:6px}
.xlrow{display:flex;align-items:center;gap:10px;padding:9px 11px;border:1px solid #e5e9f2;border-radius:10px;background:#fff;transition:.12s}
.xlrow.on{border-color:#6366f1;background:#f8f9ff}
.xlrow.unmatch{border-style:dashed}
.xlpick{display:flex;align-items:flex-start;gap:8px;flex:1;min-width:0;cursor:pointer}
.xlpick .cb,.xlclear .cb{width:15px;height:15px;margin:2px 0 0;flex:none;cursor:pointer;accent-color:#6366f1}
.xltx{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.xltx b{font-size:13px;font-weight:600;color:#1e2733;line-height:1.45;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.xltx i{font-style:normal;font-size:11.5px;color:#98a2b3;line-height:1.5;word-break:break-word}
.xlrow.unmatch .xltx i{color:#e0873a}
.xlsel{flex:none;max-width:220px;padding:5px 7px;border:1px solid #dfe4ee;border-radius:8px;font-size:12px;color:#475467;background:#fff;outline:none;cursor:pointer}
.xlsel:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.xlclear{display:flex;align-items:center;gap:8px;margin-top:12px;padding:9px 11px;border:1px solid #e5e9f2;border-radius:10px;font-size:12.5px;color:#475467;cursor:pointer}
.xlclear .cb{margin:0}
.xlwarn{margin:8px 0 0;padding:7px 10px;border:1px solid #fdeccb;border-radius:9px;background:#fffaeb;color:#b54708;font-size:11.5px;line-height:1.6}
@media(max-width:560px){.xlrow{flex-wrap:wrap}.xlsel{max-width:100%;width:100%}.xltx b{white-space:normal}}
.exportfmt{display:flex;gap:18px;margin:6px 0 12px}
.exportfmt label{display:flex;align-items:center;gap:6px;font-size:13px;cursor:pointer}
.countnote{font-size:12px;color:var(--mut);background:#f6f7fb;border:1px dashed #d9deea;border-radius:9px;padding:8px 12px;margin-bottom:12px;line-height:1.6}
.empty-state{text-align:center;padding:42px 0;color:#98a2b3}
.empty-state .big{font-size:34px;margin-bottom:8px}
/* ---------- 按页取数：这一页的数据「正在取 / 没取到」时的内容区 ----------
   数据没到位就绝不画页面（见 js/render.js 里 render() 的那道闸），
   所以这一屏要说清楚三件事：在等哪几份文档、取不到是为什么、该怎么办。 */
.pgwait{padding:56px 24px;text-align:center;color:var(--mut)}
.pgwait .pgwait-t{font-size:15px;font-weight:700;color:var(--ink);margin-bottom:10px}
.pgwait .pgwait-d{font-size:12.5px;line-height:1.8;margin:0 auto 6px;max-width:640px}
.pgwait .pgwait-a{margin-top:16px}
a.mini{color:#4f46e5;text-decoration:none;cursor:pointer;font-weight:600}
a.mini:hover{text-decoration:underline}
.tag-line{font-size:11px;color:#98a2b3;line-height:1.7}
/* 空列表时回显「当前筛选条件」，避免筛选把数据挡住了却看不出来 */
.empty-cond{margin-top:10px;font-size:12.5px;color:#667085}
.opt-tag{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;color:#fff;font-weight:600;white-space:nowrap}
/* dictionary manager (list view) */
.tname{font-size:12px;color:#94a0b8;font-weight:600}
.fname{font-weight:700;color:#1e2733;font-size:13px}
/* 组内非首行重复出现的表名 / 字段名（.rep）：浅一档 —— 每行字段都补齐，又不跟组首行抢眼。
   见 dict.js 的 cells（表名 / 字段两仓都按 _g0 决定加不加这个类）。 */
.tname.rep{font-weight:500;color:#b8c1d1}
.fname.rep{font-weight:600;color:#7b8aa3}
.fmeta{font-size:11px;color:#98a2b3;margin-top:2px}
.empty-cell{color:#c3cad8;font-size:12px}
/* 点得动的单元格（进「编辑字段字典项」抽屉）：字段名 / 选项值标签 / 「未设置选项」 */
.de-open{cursor:pointer}
.fname.de-open:hover,.empty-cell.de-open:hover{color:var(--brand)}
.fname.de-open:hover{text-decoration:underline}
.opt-tag.de-open:hover{box-shadow:0 0 0 2px rgba(99,102,241,.25)}
/* ================= 编辑字段字典项（抽屉） =================
   一行＝标签预览 / 选项名称 / 底色 / 文字色 / 使用次数 / 操作。
   列表里只留「这一项长什么样」，改名字与配色都在这儿（见 dict.js 的 openDictFieldModal）。 */
.de-head,.de-row{display:flex;align-items:center;gap:10px}
.de-head{font-size:11.5px;color:#98a2b3;padding:2px 8px 5px;border-bottom:1px solid var(--line);margin-bottom:8px}
.de-row{border:1px solid #e5e9f2;border-radius:9px;background:#fff;padding:6px 8px;margin-bottom:6px}
.de-row:hover{border-color:#c7d2fe}
.de-row.focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.de-row.bad{border-color:#e5484d;background:#fff7f7}
.de-c1{width:118px;flex:none;min-width:0;overflow:hidden;text-align:center}
.de-c1 .opt-tag{max-width:100%;overflow:hidden;text-overflow:ellipsis}
.de-c2{flex:1;min-width:110px}
.de-c3{width:42px;flex:none;display:flex;justify-content:center}
.de-c4{width:50px;flex:none;text-align:right;font-size:11.5px;color:#98a2b3;font-variant-numeric:tabular-nums}
.de-c5{width:58px;flex:none;display:flex;justify-content:flex-end;gap:4px}
.de-row .de-name{width:100%;border:1px solid #d9deea;border-radius:8px;padding:6px 9px;font-size:13px;outline:none;background:#fff}
.de-row .de-name:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.de-row input[type="color"]{width:30px;height:26px;padding:2px;border:1px solid #e4e8f0;border-radius:7px;background:#fff;cursor:pointer}
.de-row input[type="color"]::-webkit-color-swatch-wrapper{padding:1px}
.de-row input[type="color"]::-webkit-color-swatch{border:none;border-radius:5px}
.de-row input[type="color"]::-moz-color-swatch{border:none;border-radius:5px}
.oc-rst{border:1px solid #e4e8f0;background:#fff;color:#98a2b3;font-size:11px;line-height:1;
  border-radius:6px;padding:4px 6px;cursor:pointer}
.oc-rst:hover{border-color:#c7d2fe;color:var(--brand)}
.de-del{border:none;background:#fff1f2;color:#e5484d;border-radius:7px;width:26px;height:26px;
  font-size:12px;line-height:1;cursor:pointer}
.de-del:hover{background:#ffe4e6}
.de-foot{display:flex;align-items:center;gap:10px;margin-top:12px}
.de-foot .sp{flex:1}
.de-tip{font-size:11px;color:#98a2b3}
/* 使用次数徽章：0 次淡出，不抢视觉 */
.ucnt{display:inline-block;min-width:24px;padding:1px 8px;border-radius:9px;background:#ecfdf5;color:#0f766e;font-size:12px;font-weight:600;text-align:center;font-variant-numeric:tabular-nums}
.ucnt.zero{background:#f1f5f9;color:#aab3c4}
/* json to excel (built-in) */
.j2e{flex:1;display:flex;flex-direction:column;min-height:0;gap:12px;padding:16px 22px 18px;width:100%;max-width:none;margin:0}
.j2e-hd .sp,.j2e-hd2 .sp,.j2e-foot .sp{flex:1}
.j2e-main{flex:1;min-height:0;display:flex;gap:14px;overflow:hidden}
.j2e-col{display:flex;flex-direction:column;min-height:0;overflow:hidden;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.j2e-src{width:400px;flex:none}
.j2e-out{flex:1;min-width:0}
.j2e-hd{flex:none;display:flex;align-items:center;gap:8px;padding:10px 14px;background:#fbfcff;border-bottom:1px solid var(--line);font-weight:600;font-size:13px;color:#1e2733}
.j2e-hd2{flex:none;display:flex;align-items:center;gap:8px;padding:10px 14px;background:#fbfcff;border-top:1px solid var(--line)}
.j2e-src textarea{flex:1;min-height:0;width:100%;border:none;outline:none;resize:none;padding:12px 14px;background:transparent;color:#334155;font-size:12.5px;line-height:1.6;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre;overflow:auto}
.j2e-out #j2eCols{flex:none;max-height:186px;overflow:auto;padding:12px 14px;grid-template-columns:repeat(auto-fill,minmax(186px,1fr))}
.j2e-out .tblwrap{flex:1;min-height:0;max-height:none;border:none;border-radius:0;box-shadow:none}
.j2e-foot{flex:none;display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:10px 14px;background:#fbfcff;border-top:1px solid var(--line)}
.j2e-foot .exportfmt{margin:0;gap:16px}
#j2eMsg{font-size:12px}
@media(max-width:900px){.j2e-main{flex-direction:column}.j2e-src{width:auto;height:250px}}
/* 进度条：分片流水线的心跳 */
.j2e-prog{flex:none;height:3px;border-radius:2px;background:#e9edf6;overflow:hidden;opacity:0;transition:opacity .2s}
.j2e-prog.on{opacity:1}
.j2e-prog i{display:block;height:100%;width:0;border-radius:2px;background:linear-gradient(90deg,#6366f1,#22d3ee);transition:width .18s linear}
/* 大文件提示 */
.j2e-note{flex:none;margin:10px 12px 0;padding:8px 11px;border:1px solid #fde68a;background:#fffbeb;color:#92400e;border-radius:9px;font-size:12px;line-height:1.65}
.j2e-note b{color:#78350f}
/* 表头翻译引擎选择 */
.j2e-sel{height:27px;max-width:158px;padding:0 4px;border:1px solid #dde2ec;border-radius:7px;background:#fff;color:#475467;font-size:12.5px;outline:none;cursor:pointer}
.j2e-sel:focus{border-color:#6366f1}
/* 这里只改底色，用 background-color 而不是 background 简写 —— 简写会把上面的角标图一起抹掉 */
.j2e-sel.on{border-color:#6366f1;background-color:#eef0ff;color:#4338ca;font-weight:600}
/* 弹窗里的功能型下拉（列设置的排版/标题对齐、Excel 导入的目标清单、JSON→Excel 的引擎）：
   角标统一换成全站那一枚，让位宽度按各控件自己的右内边距留（它们原来还是浏览器原生角标）。 */
.lset select,.thset select,.xlsel,.j2e-sel{appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:var(--dd-arrow);background-repeat:no-repeat;background-position:right 6px center;background-size:11px 11px}
.lset select,.thset select{padding-right:20px}
.xlsel{padding-right:22px}
.j2e-sel{padding-right:19px;background-position:right 5px center}
.j2e-trf{font-size:11.5px;color:#98a2b3}
/* 「数据源」选择：一份 JSON 里有多处数组时，让用户明确转哪一处。
   单独占一行贴在输入框上方 —— 下拉展开时盖住的是自己的输入区，不会压到下面的按钮；
   宽度跟着面板走，选项里那串「路径（N 行 · M 字段）」也不会被挤掉。 */
.j2e-srcwrap{flex:none;display:flex;align-items:center;gap:8px;padding:8px 14px;background:#fbfcff;border-bottom:1px solid var(--line);font-size:12px;color:#667085;white-space:nowrap}
.j2e-srcwrap .j2e-sel{flex:1;min-width:0;max-width:none;height:26px}
/* 自绘下拉会把 <select> 套进 .ddw（util.js）：本行原来靠 select 自己的 flex:1 撑满，
   套壳后 flex 项变成了 .ddw，得把这套「撑满」原样交给外层，否则数据源下拉会缩回内容宽度 */
.j2e-srcwrap .ddw{flex:1;min-width:0;max-width:none}
.j2e-srcwrap .ddw>.j2e-sel{width:100%}
/* 虚拟滚动预览：定高行 + 绝对定位，万行也只渲染可视区 */
#j2ePrev{position:relative}
.j2ev{min-width:100%}
.j2ev-h{position:sticky;top:0;z-index:2;display:grid;grid-template-columns:var(--gt);background:#f8fafd;border-bottom:1px solid var(--line)}
.j2ev-h>div{padding:9px 12px;font-size:12px;font-weight:600;color:#475467;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-right:1px solid #eef1f8}
.j2ev-b{position:relative}
.j2ev-r{position:absolute;top:0;left:0;right:0;box-sizing:border-box;display:grid;grid-template-columns:var(--gt);border-bottom:1px solid #f4f6fb;will-change:transform;contain:layout style paint}
.j2ev-r:hover{background:#f7f9fe}
.j2ev-r>div{padding:0 12px;line-height:27px;font-size:12.5px;color:#1e2733;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-right:1px solid #f7f9fe}
.j2ev-h>.j2ev-i{text-align:center;padding:9px 0;color:#64748b}
.j2ev-r>.j2ev-i{text-align:center;padding:0;color:#64748b;font-size:11.5px;font-variant-numeric:tabular-nums}
.toast{position:fixed;bottom:26px;left:50%;transform:translateX(-50%);background:#1e2733;color:#fff;padding:10px 18px;border-radius:10px;font-size:13px;z-index:300;box-shadow:0 8px 24px rgba(0,0,0,.25);opacity:0;transition:.25s;pointer-events:none}
.toast.show{opacity:1}
/* ---------- 菜单管理 ---------- */
.menu-tree{border-collapse:separate}
.menu-tree .ghead{background:#f8fafc;cursor:pointer}
.menu-tree .ghead:hover{background:#f1f5f9}
.menu-tree .ghead td{border-top:1px solid var(--line);font-size:13px;color:#1e2733}
.menu-tree .ghead .arrow{display:inline-block;width:16px;text-align:center;color:#64748b;font-size:11px}
/* 分组行的小图标：跟左侧导航 .gico 同一套规格（22×22，圆角 7px）。字符档直接写字，图片档贴图。
   不铺底色（.nobg）时和左导航一致 —— 透明背景 + 当前文字色，圆角外加一圈灰描边避免和浅背景糊在一起。 */
.menu-tree .ghead .gico{width:22px;height:22px;border-radius:7px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#fff;box-shadow:0 1px 3px rgba(15,23,42,.18);margin-right:8px;vertical-align:middle;overflow:hidden;flex:none}
.menu-tree .ghead .gico img{width:100%;height:100%;display:block;object-fit:cover;border-radius:inherit;pointer-events:none}
.menu-tree .ghead .gico:not(.nobg) img{padding:14%}
.menu-tree .ghead .gico.nobg{background:transparent;color:#475467;box-shadow:none;border:1px dashed #cbd5e1}
.menu-tree .ghead .gname{font-weight:600;margin-right:8px}
.menu-tree .ghead .gcount{font-size:11.5px;color:#98a2b3;background:#fff;border:1px solid #e2e8f0;padding:1px 8px;border-radius:12px}
.menu-tree .ichild td:first-child{padding-left:24px;color:#64748b;font-size:12.5px}
.menu-tree .ichild .ico{width:24px;height:24px;border-radius:6px;color:#fff;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;margin-right:8px;vertical-align:middle}
/* 「编辑菜单」的图标一行：左边是输入框 / 上传区，右边跟「预览」+ 一枚小方块 ——
   文字、图片两档同一套排法，整行铺满不留空档。 */
.mgrow{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.mgrow>input[type=text]{flex:1;min-width:130px}
.mgrow>.upbox{flex:1;min-width:0;margin-bottom:0}
.mglab{margin-left:auto;font-size:12px;color:#98a2b3;flex:none}
.mgprev{flex:none;display:inline-flex;align-items:center}
.mgprev .ico{width:34px;height:34px;border-radius:9px;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:#fff;flex:none;overflow:hidden}
/* 底色这一块就一行：勾选框在最前，取色框吃掉剩下的一整行（右端与上面那枚下拉齐平），
   两块都在这行的中线上下居中。取色框不再固定宽度 —— input[type=color] 的浏览器默认宽度只有
   四十来像素，在半栏里缩成一小块，跟旁边的下拉对不上。
   「配置底色」勾掉时取色框原地变灰禁用（不收起、不换文字），两种状态长得一样、行高不跳。 */
.mgcrow{display:flex;align-items:center;flex-wrap:nowrap;gap:10px;min-height:34px}
.mgcrow>input[type=checkbox]{flex:none;width:15px;height:15px;margin:0;cursor:pointer;accent-color:#6366f1}
.mgcrow>input[type=color]{flex:1;width:auto;min-width:0}
.mgcrow>input[type=color]:disabled{opacity:.45;cursor:not-allowed}
/* 「配置底色」勾掉时（.nobg）：图标不铺底色，字符 / 图片改用所在行的文字颜色 ——
   深色左导航里跟着导航字色、亮色页面里跟着正文色，不用再为两种背景各配一套色。
   弹窗预览那枚是虚线框，一眼看得出「没有底色」而不是忘了选色。 */
.ico.nobg,.tico.nobg{background:transparent}
.nav a .ico.nobg,.tab .tico.nobg,.menu-tree .ichild .ico.nobg{background:transparent;color:inherit}
.mgprev .ico.nobg{background:#fff;border:1px dashed #cbd5e1;color:#475467}
.menu-tree .ichild .mname{font-weight:500;color:#1e2733}
.menu-tree .ellip{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#475467;font-size:12.5px}
/* 「目标 / 链接」列里的页面数据接口地址（外链那格仍是纯文字）：地址长，压成一枚可点的链接，
   超宽照样省略号收住 —— td 的 overflow 对行内元素不生效，所以截断交给这枚 a 自己做。
   宽度写 100% 而不是固定值：这一列在「表格显示列」里配过多宽（td 上带 width/min/max），
   链接就跟着铺多宽，不会卡在旧上限上；没配过时跟着上面那条 .ellip 的上限走。 */
.menu-tree .ellip a{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#4f46e5;text-decoration:none}
.menu-tree .ellip a:hover{text-decoration:underline}
.mtype{display:inline-block;font-size:11px;padding:2px 8px;border-radius:12px;font-weight:600}
.mtype-page{background:#e0e7ff;color:#4f46e5}
.mtype-link{background:#dcfce7;color:#16a34a}
.mtype-dir{background:#f1f5f9;color:#475467;border:1px solid #e2e8f0}
.mstat{display:inline-block;font-size:11px;padding:2px 10px;border-radius:12px;font-weight:600;cursor:pointer;border:1px solid transparent}
.mstat{background:#ecfdf5;color:#16a34a;border-color:#bbf7d0}
.mstat.off{background:#f8fafc;color:#94a3b8;border-color:#e2e8f0}
.mstat:hover{opacity:.85}
/* ---------- 字典管理左右布局 ---------- */
/* 左栏＝一条常驻的分类导航：卡片高度写成一屏（顶栏 58 + 标签栏 40 + 内容区上下留白 44 = 142，同 .content.jsonpage 的算法），
   列表长了在卡片里自己滚；整页往下翻时它还 sticky 在顶上，翻到哪儿都够得着。
   从前是 max-height:calc(100vh - 250px)，卡片总比右边的表矮一截，底下就空出一块底色。 */
.dict-layout{display:flex;gap:14px;align-items:flex-start}
.dict-tree{width:236px;flex:none;display:flex;flex-direction:column;position:sticky;top:0;height:calc(100vh - 142px);background:#fff;border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
.dict-tree .dt-head{flex:none;display:flex;align-items:center;gap:8px;padding:11px 15px;font-weight:600;font-size:13px;color:#1e2733;border-bottom:1px solid var(--line);background:#fbfcff}
.dict-tree .dh-dot{width:6px;height:6px;border-radius:50%;background:#6366f1;flex:none}
.dict-tree .dt-body{flex:1;min-height:0;padding:6px 0 10px;overflow:auto}
.dict-tree .dt-group{margin-top:2px}
.dict-tree .dt-gname{display:flex;align-items:center;gap:8px;padding:11px 15px 5px;font-size:11.5px;color:#a2abbd;font-weight:700}
.dict-tree .dt-gname::after{content:'';flex:1;height:1px;background:#eef1f6}
.dict-tree .dt-item{display:flex;align-items:center;gap:7px;margin:0 8px;padding:7px 9px;border-radius:8px;font-size:12.5px;color:#334155;cursor:pointer;transition:.12s}
.dict-tree .dt-item:hover{background:#f4f6fb}
.dict-tree .dt-item.on{background:#eef2ff;color:#4338ca;font-weight:600;box-shadow:inset 3px 0 0 #6366f1}
.dict-tree .dt-icon{width:14px;text-align:center;color:#bcc4d4;flex:none;font-size:11px}
.dict-tree .dt-item.on .dt-icon{color:#6366f1}
.dict-tree .dt-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dict-tree .dt-cnt{font-size:10.5px;color:#94a3b8;background:#f2f5fa;padding:1px 6px;border-radius:9px;flex:none;font-variant-numeric:tabular-nums}
.dict-tree .dt-item.on .dt-cnt{background:#e0e7ff;color:#4338ca}
.dict-main{flex:1;min-width:0}
.dict-crumb{display:inline-flex;align-items:center;gap:8px;padding:4px 13px;border-radius:20px;background:#eef2ff;color:#4338ca;font-size:12.5px;font-weight:600;margin-bottom:10px}
.dict-crumb .lbl{color:#9aa3b8;font-weight:500}
/* 字典项按「表 + 字段」分组：组首行才显示表名/字段，组间加分隔线，奇偶组换底便于扫读 */
table.list.dict-tb tbody td{padding:7px 12px;vertical-align:middle}
table.list.dict-tb tbody tr.g0>td{border-top:1px solid #e2e8f4}
table.list.dict-tb tbody tr.g0:first-child>td{border-top:0}
table.list.dict-tb tbody tr.alt>td{background:#fbfcfe}
table.list.dict-tb tbody tr:hover>td{background:#f4f7ff}
table.list.dict-tb tbody tr.alt:hover>td{background:#f1f5ff}
/* 操作列不再单独定宽 / 居中：直接走上面全局的 th.act / td.act（右对齐、172px）。
   从前这里是「编辑 / 删除」两个带下划线的文字链接，宽度按这两个词量了个 112px 并居中；
   现在换成了全站同款按钮（.btn.sm / .btn.danger.sm + .rowact），就按全站规则来 ——
   和机构 / 团队 / 角色 / 菜单那几页长得一模一样。宽度仍可在「表格显示列」里单独改。 */
@media print{.side,.topbar,.tabs,.tabmenu,.toolbar,.pagehead .btn,.act{display:none!important}}

/* ================= 资料管理 ================= */
.doc-title{font-size:18px;font-weight:800;color:#1e2733;margin-bottom:8px;word-break:break-word}
.doc-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:12.5px;color:#64748b;margin-bottom:14px}
.rt-content{border:1px solid #eef1f6;border-radius:12px;padding:14px 16px;background:#fff;line-height:1.75;font-size:14px;color:#1e2733;word-break:break-word}
.rt-content h1,.rt-content h2,.rt-content h3,.rt-content h4{margin:12px 0 6px;line-height:1.35}
.rt-content p{margin:6px 0}
.rt-content ul,.rt-content ol{margin:6px 0 6px 22px}
.rt-content blockquote{margin:8px 0;padding:6px 12px;border-left:3px solid #c7d2fe;background:#f8fafc;color:#475467;border-radius:0 8px 8px 0}
.rt-content a{color:#4f46e5}
.rt-content img{max-width:100%;border-radius:8px}
.doc-empty{color:#a8b0bf}
/* 自定义字段（资料管理：字段名 + 内容，输入框可自行增删） */
.cfbox{display:flex;flex-direction:column;gap:8px;border:1px dashed #cfd7e6;border-radius:10px;padding:10px 12px;background:#fafbfd}
.cfrow{display:flex;align-items:center;gap:8px}
.cfrow input[type=text]{border:1px solid #d9deea;border-radius:9px;padding:7px 10px;font-size:13px;color:#1e2733;background:#fff;outline:none;transition:.15s}
.cfrow input[type=text]:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.12)}
.cfrow .cfk{flex:0 1 210px}
.cfrow .cfv{flex:1 1 120px}
.cfbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:8px}
.cfquick{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:8px;font-size:11.5px;color:#98a2b3}
.cf-pick{border:1px solid #dbe0ff;background:#f4f5ff;color:#4f46e5;border-radius:20px;padding:2px 10px;font-size:12px;cursor:pointer;line-height:1.6}
.cf-pick:hover{background:#e8ebff}
.cf-eg{color:#98a2b3}
/* 常用字段：一键添加 + 右侧「✕」移除（移除只影响这里的快捷入口，资料里的字段内容不受影响） */
.cf-quick{display:inline-flex;align-items:center;border:1px solid #dbe0ff;background:#f4f5ff;border-radius:20px;overflow:hidden}
.cf-quick:hover{background:#e8ebff;border-color:#c7cffb}
.cf-quick .cf-pick{border:none;background:transparent;border-radius:0;padding:2px 4px 2px 10px}
.cf-quick .cf-pick:hover{background:transparent}
.cf-quick .cf-del{border:none;background:transparent;color:#98a2b3;font-size:11px;line-height:1.6;padding:2px 9px 2px 2px;cursor:pointer}
.cf-quick .cf-del:hover{color:#b42318}
/* 移除了几个 → 给一条「放回去」的出口，避免手滑之后找不回来 */
.cf-restore{border:none;background:transparent;color:#98a2b3;font-size:11.5px;cursor:pointer;padding:2px 4px;text-decoration:underline;text-underline-offset:3px}
.cf-restore:hover{color:#4f46e5}
/* 列表里的自定义字段胶囊 */
.cf-chip{display:inline-block;max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;font-size:11.5px;color:#475467;background:#f2f4f8;border:1px solid #e2e6ee;border-radius:8px;padding:1px 7px;margin:0 4px 4px 0}
.cf-chip b{color:#4338ca;font-weight:700;margin-right:4px;font-size:11px}
.cf-chip.more{color:#4f46e5;background:#eef0ff;border-color:#dbe0ff}
/* 详情里的自定义字段 */
.doc-customs{border:1px solid #eef1f6;border-radius:12px;padding:10px 14px;background:#fbfcfe;display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.doc-customs .cf-line{display:flex;gap:10px;font-size:13px;line-height:1.6}
.doc-customs .cf-name{flex:none;min-width:84px;color:#64748b;font-weight:600}
.doc-customs .cf-val{flex:1 1 auto;color:#1e2733;word-break:break-word}
.doc-files{margin-top:16px}
.doc-files-h{font-size:13px;font-weight:700;color:#475467;margin-bottom:8px}
/* 附件 */
.attach-list{display:flex;flex-direction:column;gap:8px}
.attach-item{display:flex;align-items:center;gap:8px;border:1px solid #e5e9f0;border-radius:10px;padding:8px 10px;background:#fff}
.attach-item .aico{width:26px;height:26px;border-radius:7px;color:#fff;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex:none;letter-spacing:.3px}
.attach-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:#1e2733;cursor:pointer}
.attach-name:hover{color:#4f46e5;text-decoration:underline}
.attach-size{font-size:11.5px;color:#98a2b3;flex:none;font-variant-numeric:tabular-nums}
.att-new{font-size:11px;color:#b45309;background:#fef3c7;border-radius:10px;padding:1px 8px;flex:none}
.attach-empty{color:#98a2b3;font-size:12.5px;padding:6px 0}
.att-chip{display:inline-block;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;font-size:11.5px;color:#475467;background:#f2f4f8;border:1px solid #e2e6ee;border-radius:8px;padding:1px 7px;margin:0 4px 4px 0}
.att-chip.att-more{color:#4f46e5;background:#eef0ff;border-color:#dbe0ff}
/* ---------- 清单里的附件字段（需求表的「截图」这类） ----------
   表格列：只画第一张的小图，其余折成「+n」；卡片：图铺满这一格；详情：全都铺出来。
   二进制存在 docs 的附件库里，图是渲染后异步填进去的（util.js 的 attachHydrate）。 */
.att-cell{display:inline-flex;align-items:center;gap:4px}
.att-th{width:38px;height:38px;object-fit:cover;border-radius:7px;background:#f2f4f8;border:1px solid #e6eaf0;cursor:zoom-in;display:block}
.att-th.on{border-color:#c7d2fe}
.att-th-more{font-size:11.5px;font-weight:700;color:#4f46e5;background:#eef0ff;border:1px solid #dbe0ff;border-radius:8px;padding:1px 6px;flex:none}
.att-cbox{display:inline-flex;align-items:flex-end;gap:4px;max-width:100%}
.att-cbox img.cimg{max-height:150px}
/* 详情里的附件区 */
.att-grid{display:flex;flex-wrap:wrap;gap:8px}
.att-gitem{position:relative;width:104px;border:1px solid #e6eaf0;border-radius:10px;background:#fbfcfe;padding:6px;display:flex;flex-direction:column;align-items:center;gap:5px;cursor:zoom-in;transition:border-color .15s,box-shadow .15s}
.att-gitem:hover{border-color:#c7d2fe;box-shadow:0 2px 8px rgba(79,70,229,.08)}
.att-gitem .att-gimg{width:100%;height:76px;object-fit:cover;border-radius:7px;background:#f2f4f8;display:block}
.att-gitem .att-gname{font-size:11.5px;color:#475467;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.att-gico .aico{width:34px;height:34px;font-size:10px}
/* 表单里的附件区 */
.fmb-attach{display:flex;flex-direction:column;gap:8px}
.fmb-attach .att-list{flex-direction:row;flex-wrap:wrap;gap:8px}
.fmb-attach .upbox{margin-bottom:0}
.fmb-attach .att-empty{font-size:12.5px;color:#98a2b3}
.att-thwrap{display:flex;align-items:center;gap:8px;border:1px solid #e5e9f0;border-radius:10px;padding:6px 8px;background:#fff;max-width:100%}
.att-thwrap .att-th{width:34px;height:34px}
.att-thwrap .aico{width:34px;height:34px;font-size:10px}
.att-thwrap .att-tname{font-size:12.5px;color:#1e2733;cursor:pointer;max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.att-thwrap .att-tname:hover{color:#4f46e5;text-decoration:underline}
.att-thwrap .att-tsize{font-size:11.5px;color:#98a2b3;flex:none}
.att-thwrap .att-del{width:20px;height:20px;border:1px solid #e5e9f0;background:#fff;color:#98a2b3;border-radius:6px;font-size:13px;line-height:1;cursor:pointer;flex:none}
.att-thwrap .att-del:hover{color:#dc2626;border-color:#fecaca;background:#fef2f2}
/* 上传区 */
.upbox{border:1px dashed #cfd7e6;border-radius:10px;padding:9px 12px;background:#fafbfd;display:flex;flex-direction:row;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}
/* 原生 file 控件长得丑且压不住样式：视觉上藏起来（仍可 Tab 聚焦），
   实际点击交给旁边这个 label 按钮 */
.upbox .up-file{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);border:0}
.upbox .up-btn{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 13px;border:1px solid #dbe0ff;background:#eef0ff;color:#4f46e5;border-radius:8px;font-size:12.5px;font-weight:600;cursor:pointer;flex:none;transition:background .15s,border-color .15s,box-shadow .15s}
.upbox .up-btn:hover{background:#e3e7ff;border-color:#c7cffd}
.upbox .up-btn:active{background:#dbe0ff}
.upbox .up-file:focus-visible+.up-btn{box-shadow:0 0 0 3px rgba(79,70,229,.18)}
.upbox .fhint{font-size:12px;color:#98a2b3;line-height:1.5}
/* 富文本编辑器 */
.rte{border:1px solid #d9deea;border-radius:10px;overflow:hidden;background:#fff}
.rte-toolbar{display:flex;align-items:center;gap:2px;flex-wrap:wrap;padding:6px 8px;background:#f8fafd;border-bottom:1px solid #eef1f6}
.rte-toolbar button{min-width:28px;height:26px;padding:0 7px;border:1px solid transparent;background:transparent;border-radius:6px;font-size:12.5px;color:#475467;line-height:1;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
.rte-toolbar button:hover{background:#eef0ff;color:#4f46e5;border-color:#dbe0ff}
.rte-toolbar .rte-sep{width:1px;height:18px;background:#e2e6ee;margin:0 4px}
.rte-body{min-height:180px;max-height:46vh;overflow:auto;padding:12px 14px;outline:none;font-size:14px;line-height:1.75;color:#1e2733}
.rte-body:empty:before{content:attr(data-ph);color:#98a2b3}
.rte-body h1,.rte-body h2,.rte-body h3,.rte-body h4{margin:10px 0 6px}
.rte-body ul,.rte-body ol{margin:6px 0 6px 22px}
.rte-body blockquote{margin:8px 0;padding:6px 12px;border-left:3px solid #c7d2fe;background:#f8fafc;color:#475467}
.rte-body a{color:#4f46e5}
/* 文件预览 */
.pv-wrap{display:flex;align-items:center;justify-content:center}
.pv-img{max-width:100%;max-height:72vh;border-radius:10px}
.pv-frame{width:100%;height:72vh;border:0;border-radius:10px;background:#fff}
.pv-media{max-width:100%;max-height:72vh;border-radius:10px}
.pv-pre{margin:0;width:100%;max-height:72vh;overflow:auto;background:#0f172a;color:#e2e8f0;padding:14px 16px;border-radius:10px;font-size:12.5px;line-height:1.6;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,Consolas,'Courier New',monospace}
.pv-unknown{text-align:center;padding:44px 20px}
.pv-unknown-t{font-size:15px;font-weight:700;color:#1e2733;margin-bottom:6px}
.pv-unknown-s{font-size:12.5px;color:#98a2b3;margin-bottom:16px;word-break:break-all}
/* 图片查看器：缩放 / 平移 / 旋转 */
.pv-imgview{display:flex;flex-direction:column;gap:10px}
.pv-imgbar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:8px 10px;background:#f8fafc;border:1px solid #e6eaf0;border-radius:10px}
.pv-btn{appearance:none;border:1px solid #d7dde7;background:#fff;color:#334155;height:30px;min-width:34px;padding:0 10px;border-radius:8px;font-size:13px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .15s,border-color .15s,color .15s}
.pv-btn:hover{background:#eef2ff;border-color:#c7d2fe;color:#4f46e5}
.pv-btn:active{transform:translateY(1px)}
.pv-scale{min-width:54px;text-align:center;font-size:12.5px;font-weight:700;color:#475467;font-variant-numeric:tabular-nums}
.pv-sep{width:1px;height:18px;background:#e2e8f0;margin:0 3px}
.pv-info{margin-left:auto;font-size:12px;color:#98a2b3}
.pv-stage{position:relative;overflow:hidden;height:62vh;min-height:240px;border-radius:10px;background:#0b1220;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}
.pv-stage.grabbing{cursor:grabbing}
.pv-stage .pv-img{position:absolute;left:50%;top:50%;max-width:none;max-height:none;border-radius:0;transform-origin:center center;will-change:transform;pointer-events:none}
.pv-hint{font-size:11.5px;color:#98a2b3;text-align:center}
.pv-btn.on{background:#4f46e5;border-color:#4f46e5;color:#fff}
/* 表格预览（xlsx / xls / csv / tsv） */
.ov.ovp>.box{width:1180px;max-width:96vw}
.pv-tbview{display:flex;flex-direction:column;gap:10px}
.pv-tbbar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:8px 10px;background:#f8fafc;border:1px solid #e6eaf0;border-radius:10px}
.pv-tbbar .sp{flex:1}
.pv-tbtabs{display:flex;gap:4px;flex-wrap:wrap;max-width:100%}
.pv-tab{border:1px solid #d7dde7;background:#fff;color:#475467;height:28px;padding:0 12px;border-radius:8px;font-size:12.5px;cursor:pointer;white-space:nowrap;max-width:180px;overflow:hidden;text-overflow:ellipsis}
.pv-tab:hover{background:#eef2ff;border-color:#c7d2fe;color:#4f46e5}
.pv-tab.on{background:#4f46e5;border-color:#4f46e5;color:#fff;font-weight:600}
.pv-tbinfo{font-size:12px;color:#64748b;font-variant-numeric:tabular-nums;white-space:nowrap}
.pv-tbwrap{overflow:auto;max-height:64vh;min-height:220px;border:1px solid #e2e8f0;border-radius:10px;background:#fff;overscroll-behavior:contain}
.pv-tbl{border-collapse:separate;border-spacing:0;width:max-content;min-width:100%}
.pv-tbl th,.pv-tbl td{border-right:1px solid #eef1f6;border-bottom:1px solid #eef1f6;text-align:left;vertical-align:middle;font-weight:400;padding:0}
.pv-tbl td .ct{display:block;padding:6px 12px;min-width:86px;max-width:340px;line-height:1.55;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pv-tbl.wrap td .ct{white-space:pre-wrap;word-break:break-word;text-overflow:clip}
.pv-tbl thead th{position:sticky;top:0;z-index:2;background:#eef2f7;border-bottom:1px solid #dbe2ec;padding:7px 12px;white-space:nowrap}
.pv-tbl thead th .cl{display:inline-block;min-width:14px;margin-right:6px;padding:0 4px;border-radius:4px;background:#dde4ee;color:#8896ab;font-size:10px;font-weight:700;line-height:15px;text-align:center;vertical-align:1px}
.pv-tbl thead th .hd{font-weight:700;color:#1e293b;font-size:inherit}
.pv-tbl thead th.nohead{background:#f1f5f9;text-align:center}
.pv-tbl thead th.nohead .cl{margin:0;background:transparent;color:#94a3b8;font-size:11px}
.pv-tbl th.rn,.pv-tbl td.rn{padding:6px 12px;min-width:56px;position:sticky;left:0;z-index:1;background:#f4f7fb;color:#9aa6b8;text-align:right;font-size:11.5px;font-variant-numeric:tabular-nums;white-space:nowrap;box-shadow:1px 0 0 #e6ecf4}
.pv-tbl thead th.rn{z-index:3;background:#eef2f7;padding:7px 12px}
.pv-tbl tbody td{background:#fff}
.pv-tbl tbody tr:nth-child(even) td{background:#fafbfe}
.pv-tbl tbody tr td.rn{background:#f4f7fb}
.pv-tbl tbody tr:nth-child(even) td.rn{background:#eef3f9}
.pv-tbl tbody tr:hover td{background:#eef4ff}
.pv-tbl tbody tr:hover td.rn{background:#e0e9fd}
.pv-loading{padding:56px 20px;text-align:center;color:#64748b;font-size:13.5px}

/* ================= 操作日志 ================= */
.statbar .chip .dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:5px;vertical-align:middle}
.log-switch{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;color:#475467;cursor:pointer;user-select:none;white-space:nowrap;padding:0 2px}
.log-switch input{margin:0;cursor:pointer}
.log-kind{display:inline-block;min-width:38px;text-align:center;color:#fff;font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;letter-spacing:.5px}
table.list.log-tb td{vertical-align:middle}
.log-detail-cell .log-cnt{display:inline-block;margin-left:6px;font-size:11px;color:#4f46e5;background:#eef0ff;border:1px solid #dbe0ff;border-radius:8px;padding:0 6px;white-space:nowrap}
.log-diff{margin-top:16px}
.log-diff-h{font-size:13px;font-weight:700;color:#475467;margin-bottom:8px}
table.list.log-difftb th{font-size:12px}
table.list.log-difftb td{font-size:12.5px}
.log-from{color:#b42318;background:#fef3f2;border-radius:6px;padding:1px 6px;display:inline-block;word-break:break-word}
.log-to{color:#067647;background:#ecfdf3;border-radius:6px;padding:1px 6px;display:inline-block;word-break:break-word}
@media(max-width:760px){.pv-stage{height:54vh}.pv-imgbar{gap:5px;padding:7px 8px}.pv-info{display:none}.ov.ovp>.box{width:100%;max-width:100%;border-radius:0}.pv-tbwrap{max-height:54vh}.pv-tbbar{padding:6px 8px}.pv-tbl td .ct{min-width:70px;max-width:200px;padding:5px 9px}.pv-tbl th.rn,.pv-tbl td.rn{min-width:44px;padding:5px 9px}}

/* ================= 登录页 ================= */
body.locked{overflow:hidden}
.login-ov{position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;padding:24px;
  background:radial-gradient(1200px 600px at 20% 10%,#eef2ff 0%,#f7f8fc 45%,#eef7fb 100%)}
.login-ov.show{display:flex}
.login-ov::before{content:"";position:absolute;inset:0;pointer-events:none;background:
  radial-gradient(420px 260px at 85% 15%,rgba(99,102,241,.16),transparent 70%),
  radial-gradient(460px 300px at 10% 90%,rgba(13,148,136,.14),transparent 70%)}
.lg-card{position:relative;width:390px;max-width:100%;background:#fff;border:1px solid var(--line);border-radius:20px;
  box-shadow:0 30px 70px rgba(30,41,59,.18);overflow:hidden;animation:lgIn .35s ease}
@keyframes lgIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.lg-brand{display:flex;align-items:center;gap:12px;padding:24px 26px 16px}
.lg-cube{width:44px;height:44px;border-radius:13px;flex:none;background:linear-gradient(135deg,#4f46e5,#7c3aed);color:#fff;
  display:flex;align-items:center;justify-content:center;font-weight:800;font-size:20px;box-shadow:0 8px 18px rgba(79,70,229,.35)}
.lg-tt{display:flex;flex-direction:column;gap:3px;min-width:0}
.lg-tt b{font-size:16px;color:#1e2733}
.lg-tt span{font-size:12px;color:#98a2b3}
.lg-body{padding:0 26px 26px}
.lg-field{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.lg-field label{font-size:12px;font-weight:600;color:#475467}
.lg-field input{border:1px solid #d9deea;border-radius:10px;padding:10px 12px;font-size:14px;outline:none;transition:.15s;background:#fff;color:var(--ink)}
.lg-field input:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.14)}
.lg-err{display:none;font-size:12.5px;color:#b42318;background:#fef3f2;border:1px solid #fee4e2;border-radius:9px;padding:8px 10px;margin-bottom:12px}
.lg-btn{width:100%;border:none;border-radius:11px;padding:11px 0;font-size:14.5px;font-weight:700;color:#fff;cursor:pointer;
  background:linear-gradient(135deg,#4f46e5,#6d5cf0);box-shadow:0 10px 22px rgba(79,70,229,.3)}
.lg-btn:hover{filter:brightness(1.06)}
.lg-btn:disabled{opacity:.7;cursor:default}
.lg-hint{margin-top:14px;font-size:12px;color:#98a2b3;text-align:center;line-height:1.7}
.lg-hint b{color:#4f46e5}
/* 登录页的「从备份文件恢复」：换浏览器 / 无痕打开时正是这一屏，是恢复数据的主要入口 */
.lg-restore{margin-top:16px;padding-top:14px;border-top:1px dashed #e5e9f0;display:flex;flex-direction:column;align-items:center;gap:9px}
.lg-restore span{font-size:12px;color:#98a2b3;text-align:center;line-height:1.65}
.lg-restore button{border:1px solid #dbe0ff;background:#fff;color:#4f46e5;font-weight:600;font-size:13px;border-radius:10px;padding:8px 14px;cursor:pointer;transition:.15s}
.lg-restore button:hover{background:#f7f8ff;border-color:#c7cffb}

/* ================= 顶栏用户信息 ================= */
.userchip{background:#eef2ff;border-color:#dbe0ff;color:#4f46e5;font-weight:600}
.userchip .ucrole{font-size:10.5px;color:#7c3aed;background:#f3e8ff;border-radius:6px;padding:1px 6px;margin-right:3px}

/* ================= 组织与权限（机构 / 团队 / 角色 / 用户） ================= */
table.list.sys-tb td{vertical-align:middle}
.badge-root{background:#eef2ff;color:#4f46e5}
.badge-team{background:#e6f7f4;color:#0d9488}
.badge-user{background:#f3e8ff;color:#7c3aed}
.badge-super{background:#fef3c7;color:#b45309}
.badge-scope{background:#f1f5f9;color:#475467}
.badge-on{background:#ecfdf3;color:#067647}
.badge-off{background:#fef3f2;color:#b42318}
.sys-tb .fmeta{margin-top:2px}
.rm-master{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:#4f46e5;background:#f5f6ff;
  border:1px solid #e0e4ff;border-radius:10px;padding:10px 12px;cursor:pointer}
.rm-master input{margin:0;cursor:pointer}
.rm-tools{display:flex;gap:8px;margin:10px 0}
.rolemenus{border:1px solid var(--line);border-radius:12px;max-height:46vh;overflow:auto;padding:6px 4px;background:#fcfdff}
.rm-group{padding:6px 8px}
.rm-gh{font-size:12px;font-weight:700;color:#64748b;margin:4px 0 6px}
.rm-items{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px 10px}
.rm-item{display:flex;align-items:center;gap:7px;font-size:12.5px;color:#344054;padding:5px 8px;border-radius:8px;cursor:pointer;border:1px solid transparent;min-width:0}
.rm-item:hover{background:#eef2ff;border-color:#dfe4ff}
.rm-item input{margin:0;cursor:pointer}
.rm-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 权限勾选项里的图标同上：嵌在文字里，压到文字基线上 */
.rm-item span svg{vertical-align:-3px;margin-right:5px;flex:none}
.scope-orgs{display:flex;flex-wrap:wrap;gap:6px 12px;border:1px solid var(--line);border-radius:10px;padding:9px 11px;background:#fcfdff;max-height:150px;overflow:auto}
.scope-org{display:flex;align-items:center;gap:6px;font-size:12.5px;color:#344054;cursor:pointer}
.scope-org input{margin:0;cursor:pointer}
/* 角色「权限」弹窗：① 可见菜单 + ② 操作按钮 两段 */
.permhint{margin:0 0 6px;font-size:12.5px;line-height:1.7;color:#475467;background:#f8fafc;border:1px solid var(--line);border-radius:10px;padding:9px 11px}
.rm-sect{display:flex;align-items:center;gap:8px;margin:14px 0 8px;font-size:13px;font-weight:700;color:#1f2937;
  border-left:3px solid #6366f1;padding-left:8px}
.rm-sect .rm-tip{font-weight:500;font-size:11.5px;color:#98a2b3}
.rm-gh{display:flex;align-items:center;gap:8px}
.ra-page{display:flex;align-items:center;gap:7px;cursor:pointer}
.ra-page input{margin:0;cursor:pointer}
.ra-cnt{margin-left:auto;font-weight:500;font-size:11px;color:#98a2b3}
.rolemenus.acts{max-height:38vh}
/* ②操作按钮区：菜单一个都没勾时的占位（说明「按钮跟着菜单走」） */
.rm-empty{padding:26px 14px;text-align:center;font-size:12.5px;line-height:1.9;color:#98a2b3}
@media(max-width:760px){.rm-items{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* ================= 自绘下拉面板（替掉 select / datalist 的系统弹层） =================
   收起状态的 <select> 早就能靠 CSS 美化（角标统一成 --dd-arrow），但点开后的候选列表是
   浏览器 / 系统自己画的，CSS 完全够不着。这里用一块自绘面板顶上：
   <select> 本体照旧保留（.value 读写、change 事件都不变），只是被一枚透明按钮 .ddhit 盖住；
   可输入的机构 / 团队框改用 input[data-sug] 挂同一块面板 ⇒ 全站下拉只有这一个样子。 */
.ddpanel{position:fixed;left:0;top:0;z-index:400;display:none;min-width:118px;max-height:288px;overflow:auto;
  padding:6px;background:#fff;border:1px solid #e6eaf3;border-radius:12px;
  box-shadow:0 16px 40px rgba(15,23,42,.16),0 2px 8px rgba(15,23,42,.06)}
.ddpanel.show{display:block}
.ddpanel::-webkit-scrollbar{width:8px}
.ddpanel::-webkit-scrollbar-thumb{background:#e3e8f0;border-radius:8px}
.ddopt{display:flex;align-items:center;gap:7px;width:100%;padding:7px 9px;border:none;background:transparent;
  border-radius:8px;font-family:inherit;font-size:13px;line-height:1.5;color:#344054;text-align:left;white-space:nowrap;cursor:pointer}
.ddopt .tick{flex:none;width:13px;font-size:12px;color:#4f46e5}
.ddopt .tx{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.ddopt:hover,.ddopt.hl{background:#f3f5fd}
.ddopt.on{background:#eef2ff;color:#4338ca;font-weight:600}
.ddopt.on:hover,.ddopt.on.hl{background:#e6ebff}
.ddopt.off{color:#9aa3b2;cursor:default}
.ddopt.off:hover{background:transparent}
/* 盖在 <select> 上的透明触发按钮；父容器自己不能定位时，由 .ddw 兜住 */
.ddw{position:relative;display:inline-flex;align-items:center;min-width:0;vertical-align:middle}
/* 注意不能限定 .ddw 直接子级：记录表单的 select 挂在 .fsel（它自己就是定位容器，util.js 不再套 .ddw），
   按钮同样是 .fsel 的直接子元素 —— 只写 .ddw>.ddhit 会让表单里那枚按钮掉出绝对定位、露出本体 */
.ddhit{position:absolute;left:0;top:0;width:100%;height:100%;border:none;background:transparent;padding:0;margin:0;cursor:pointer;z-index:2}
.ddhit:focus{outline:none}
/* 表单里的 <select> 是 .fitem 的直接子元素、靠 flex 横向撑满：套一层后把「撑满」交给内层，外观不变 */
.fitem>.ddw{display:block}
.fitem>.ddw>select{width:100%}
/* 记录表单的 .fsel 里还有一枚「清空选择 ×」：让它压在透明按钮之上，别被吃掉点击 */
.fitem .fsel .selclear{z-index:3}

/* ================= 可输入下拉：<select> 退居值容器，输入框接管显示 =================
   原生 <select> 留在 DOM 里当值容器，文字透明 + 让出鼠标；边框 / 底色 / 角标照常显示，
   上面盖一枚透明输入框 .ddinput，既能显示当前选中项，又能边打边过滤候选。 */
select.ddsel{color:transparent !important;pointer-events:none}
.ddinput{position:absolute;left:0;top:0;width:100%;height:100%;border:none !important;background:transparent !important;
  box-shadow:none;outline:none;font-family:inherit;color:inherit;cursor:pointer;z-index:1;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ddinput.ph,.ddinput::placeholder{color:#98a2b3}
.ddinput:focus{outline:none;box-shadow:0 0 0 3px rgba(99,102,241,.12);border-radius:9px}
/* 查询条里的下拉：宽度跟着外层走，别再被「查询框文本输入 110px」钉死 */
.qfilters .qf .ddinput:focus{box-shadow:none}
.qfilters .qf input.ddinput[type=text]{width:100%;padding-right:19px}
/* 记录表单的下拉：文字让开右侧角标；带「清空 ×」的再让开一点 */
.fitem input.ddinput[type=text]{padding-right:30px}
.fitem .fsel input.ddinput[type=text]{padding-right:54px}
/* 原生日历图标：全站改用自绘日历，藏掉这枚浏览器图标（点它仍会弹原生日历） */
input[type=date]::-webkit-calendar-picker-indicator{display:none}

/* ================= 自绘日历面板（.dpcal，复用 .ddpanel 的壳） ================= */
.dpcal{width:266px;max-height:none;padding:8px}
.dpcal-h{display:flex;align-items:center;gap:4px;padding:2px 2px 8px}
.dpcal-nav,.dpcal-ttl{height:28px;border:none;background:transparent;border-radius:7px;font-family:inherit;font-size:13px;color:#1e2733;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
.dpcal-nav{width:28px;font-size:17px;color:#64748b;line-height:1}
.dpcal-ttl{flex:1;font-weight:600}
.dpcal-nav:hover,.dpcal-ttl:hover{background:#f3f5fd}
.dpcal-w{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;padding:0 2px 4px;color:#98a2b3;font-size:11px;text-align:center}
.dpcal-w i{font-style:normal;padding:2px 0}
.dpcal-g{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;padding:0 2px}
.dpcal-d{height:30px;border:none;background:transparent;border-radius:8px;font-family:inherit;font-size:13px;color:#344054;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
.dpcal-d:hover{background:#f3f5fd}
.dpcal-d.today{color:#4f46e5;font-weight:700;box-shadow:inset 0 0 0 1px #c7ccf5}
.dpcal-d.on{background:#4f46e5;color:#fff;font-weight:700;box-shadow:none}
.dpcal-d.oth{visibility:hidden}
.dpcal-f{display:flex;align-items:center;gap:6px;padding:8px 2px 2px;margin-top:6px;border-top:1px solid #eef1f7}
.dpcal-lk{border:none;background:transparent;border-radius:7px;padding:5px 10px;font-family:inherit;font-size:12.5px;color:#4f46e5;cursor:pointer}
.dpcal-lk:hover{background:#eef2ff}
.dpcal-f>.dpcal-lk:first-child{margin-right:auto}
/* 右侧三个快捷按钮：今年 / 本周 / 今天，成组靠右 */
.dpcal-qks{display:flex;align-items:center;gap:2px}
.dpcal-ym{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:2px}
.dpcal-ym button{height:34px;border:none;background:#f8fafd;border-radius:8px;font-family:inherit;font-size:13px;color:#344054;cursor:pointer}
.dpcal-ym button:hover{background:#eef2ff;color:#4338ca}
.dpcal-ym button.on{background:#4f46e5;color:#fff;font-weight:600}
/* 「按年选择」图标按钮：压在右侧，图标本身也居中 */
.dpcal-ybtn{margin-left:2px}
.dpcal-ybtn svg{width:14px;height:14px;display:block}
/* 年份列表：一页 12 年 */
.dpcal-yg{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;padding:2px}
.dpcal-yg button{height:34px;border:none;background:#f8fafd;border-radius:8px;font-family:inherit;font-size:13px;color:#344054;cursor:pointer}
.dpcal-yg button:hover{background:#eef2ff;color:#4338ca}
.dpcal-yg button.today{color:#4f46e5;font-weight:700;box-shadow:inset 0 0 0 1px #c7ccf5}
.dpcal-yg button.on{background:#4f46e5;color:#fff;font-weight:600;box-shadow:none}

/* 在线更新演示：这一行由演示包写入，回滚后会消失 */
