/* WaaS Lite backoffice — console design system (Ant Design v6 look, light)
   The backoffice is an internal console: antd tokens, dense tables. Fluid,
   bootstrap-style responsive shell — the content column fills the viewport
   and pages may cap themselves with .container-* tiers (see the RESPONSIVE
   section at the end). Screenshot mode (?shot=1) pins 1440×900 so render.sh
   output stays stable. */
*{margin:0;padding:0;box-sizing:border-box;-webkit-font-smoothing:antialiased}
:root{
  --bg:#F5F5F5; --bg2:#FFFFFF; --sider:#FFFFFF; --header:#FFFFFF;
  --card:#FFFFFF; --hover:#F5F7FA; --row-hover:#FAFAFA; --thead:#FAFAFA;
  --stroke:#F0F0F0; --stroke2:#D9D9D9; --stroke3:#BFBFBF;
  --t1:rgba(0,0,0,.88); --t2:rgba(0,0,0,.65); --t3:rgba(0,0,0,.45); --t4:rgba(0,0,0,.25);
  --primary:#1677FF; --primary-h:#4096FF; --primary-bg:#E6F4FF; --primary-bd:#91CAFF;
  --green:#52C41A; --green-bg:#F6FFED; --green-bd:#B7EB8F; --green-tx:#389E0D;
  --red:#FF4D4F; --red-bg:#FFF2F0; --red-bd:#FFCCC7; --red-tx:#CF1322;
  --amber:#FAAD14; --amber-bg:#FFFBE6; --amber-bd:#FFE58F; --amber-tx:#D48806;
  --orange:#FA8C16; --orange-bg:#FFF7E6; --orange-bd:#FFD591; --orange-tx:#D46B08;
  --violet:#722ED1; --violet-bg:#F9F0FF; --violet-bd:#D3ADF7; --violet-tx:#531DAB;
  --cyan:#13C2C2; --cyan-bg:#E6FFFB; --cyan-bd:#87E8DE; --cyan-tx:#08979C;
  --blue:#1677FF; --blue-bg:#E6F4FF; --blue-bd:#91CAFF; --blue-tx:#0958D9;
  --geek:#2F54EB; --geek-bg:#F0F5FF; --geek-bd:#ADC6FF; --geek-tx:#1D39C4;
  --gray-bg:#FAFAFA; --gray-bd:#D9D9D9; --gray-tx:rgba(0,0,0,.65);
  --gold:#B97E04; --gold-bg:rgba(245,168,0,.12);
  --shadow:0 1px 2px rgba(0,0,0,.03),0 1px 6px -1px rgba(0,0,0,.02),0 2px 4px rgba(0,0,0,.02);
  --shadow2:0 6px 16px 0 rgba(0,0,0,.08),0 3px 6px -4px rgba(0,0,0,.12),0 9px 28px 8px rgba(0,0,0,.05);
  --code-bg:#F6F8FA;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Helvetica Neue",Arial,sans-serif;
  --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
  --r:8px; --r-sm:6px; --r-lg:12px;
}
html,body{min-height:100vh}
html.shot,html.shot body{width:1440px;min-height:900px}
body{font-family:var(--sans);background:var(--bg);color:var(--t1);font-size:14px;line-height:1.5715}
a{color:var(--primary);text-decoration:none}

/* ---------- shell ---------- */
.shell{display:flex;min-height:100vh}
html.shot .shell{min-height:900px}
.sider{width:220px;flex:none;background:var(--sider);border-right:1px solid var(--stroke);display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
html.shot .sider{height:900px}
.brand{height:56px;display:flex;align-items:center;gap:10px;padding:0 20px;border-bottom:1px solid var(--stroke)}
.brand .logo{width:28px;height:28px;border-radius:8px;background:linear-gradient(140deg,#F5B70A,#B97E04);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:13px}
.brand .name{font-weight:600;font-size:15px}
.brand .env{margin-left:auto;font-size:10.5px;padding:1px 6px;border-radius:4px;background:var(--amber-bg);color:var(--amber-tx);border:1px solid var(--amber-bd);font-family:var(--mono)}
.nav{padding:8px;flex:1;overflow:auto}
.nav .grp{font-size:11.5px;color:var(--t3);padding:12px 12px 4px;letter-spacing:.2px}
.nav .it{display:flex;align-items:center;gap:10px;height:40px;padding:0 12px;border-radius:var(--r-sm);color:var(--t1);cursor:pointer;font-size:14px;margin-bottom:2px;position:relative}
.nav .it:hover{background:var(--hover)}
.nav .it.on{background:var(--primary-bg);color:var(--primary);font-weight:500}
.nav .it svg{color:inherit;flex:none}
.nav .it .cnt{margin-left:auto;font-size:11.5px;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:var(--red);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:600}
.nav .it .cnt.gray{background:var(--stroke2);color:var(--t2)}
.sider-foot{padding:10px 12px;border-top:1px solid var(--stroke);font-size:11.5px;color:var(--t3);display:flex;align-items:center;gap:8px}
.sider-foot .ok{width:7px;height:7px;border-radius:50%;background:var(--green)}
.main{flex:1;min-width:0;display:flex;flex-direction:column}
.header{height:56px;background:var(--header);border-bottom:1px solid var(--stroke);display:flex;align-items:center;padding:0 24px;gap:16px;position:sticky;top:0;z-index:20}
.crumb{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--t3)}
.crumb b{color:var(--t1);font-weight:500}
.header .sp{flex:1}
.hbtn{height:32px;padding:0 10px;border-radius:var(--r-sm);display:flex;align-items:center;gap:6px;color:var(--t2);font-size:13px;cursor:pointer;border:1px solid transparent}
.hbtn:hover{background:var(--hover)}
.avatar{width:28px;height:28px;border-radius:50%;background:var(--primary);color:#fff;display:flex;align-items:center;justify-content:center;font-size:12.5px;font-weight:600}
.avatar.lg{width:56px;height:56px;font-size:22px;border-radius:12px}
.content{padding:20px 24px 28px;display:flex;flex-direction:column;gap:16px;flex:1}

/* ---------- page header ---------- */
.ph{display:flex;align-items:flex-start;gap:16px}
.ph h1{font-size:20px;font-weight:600;line-height:28px}
.ph .sub{font-size:13px;color:var(--t2);margin-top:2px;max-width:820px}
.ph .acts{margin-left:auto;display:flex;gap:8px;align-items:center;flex:none}
.ph .back{width:28px;height:28px;border-radius:var(--r-sm);display:flex;align-items:center;justify-content:center;color:var(--t2);cursor:pointer;margin-top:1px}
.ph .back:hover{background:var(--hover)}

/* ---------- cards ---------- */
.card{background:var(--card);border:1px solid var(--stroke);border-radius:var(--r);box-shadow:var(--shadow)}
.card .ch{display:flex;align-items:center;gap:10px;padding:12px 20px;border-bottom:1px solid var(--stroke);min-height:52px}
.card .ch .t{font-weight:600;font-size:15px}
.card .ch .d{font-size:12.5px;color:var(--t3)}
.card .ch .sp{flex:1}
.card .cb{padding:16px 20px}
.card .cb.p0{padding:0}
.grid{display:grid;gap:16px}
.grid>*{min-width:0}
.g2{grid-template-columns:1fr 1fr}.g3{grid-template-columns:repeat(3,1fr)}.g4{grid-template-columns:repeat(4,1fr)}.g5{grid-template-columns:repeat(5,1fr)}.g6{grid-template-columns:repeat(6,1fr)}
.g-2-1{grid-template-columns:2fr 1fr}.g-1-2{grid-template-columns:1fr 2fr}.g-3-2{grid-template-columns:3fr 2fr}

/* stat */
.stat{padding:16px 20px}
.stat .l{font-size:13px;color:var(--t2);display:flex;align-items:center;gap:6px}
.stat .v{font-size:26px;font-weight:600;margin-top:6px;line-height:1.2;font-variant-numeric:tabular-nums}
.stat .v small{font-size:13px;color:var(--t3);font-weight:400;margin-left:4px}
.stat .f{font-size:12px;color:var(--t3);margin-top:8px;display:flex;gap:6px 8px;align-items:center;flex-wrap:wrap}
.stat .ic{width:32px;height:32px;border-radius:var(--r);display:flex;align-items:center;justify-content:center;float:right;margin-top:-2px}
.stat .l{padding-right:36px}
.up{color:var(--green-tx)}.down{color:var(--red-tx)}

/* ---------- tags ---------- */
.tag{display:inline-flex;align-items:center;gap:4px;height:22px;padding:0 7px;border-radius:4px;font-size:12px;line-height:20px;border:1px solid var(--gray-bd);background:var(--gray-bg);color:var(--gray-tx);white-space:nowrap;vertical-align:middle}
.tag.green{background:var(--green-bg);border-color:var(--green-bd);color:var(--green-tx)}
.tag.red{background:var(--red-bg);border-color:var(--red-bd);color:var(--red-tx)}
.tag.amber{background:var(--amber-bg);border-color:var(--amber-bd);color:var(--amber-tx)}
.tag.orange{background:var(--orange-bg);border-color:var(--orange-bd);color:var(--orange-tx)}
.tag.blue{background:var(--blue-bg);border-color:var(--blue-bd);color:var(--blue-tx)}
.tag.geek{background:var(--geek-bg);border-color:var(--geek-bd);color:var(--geek-tx)}
.tag.violet{background:var(--violet-bg);border-color:var(--violet-bd);color:var(--violet-tx)}
.tag.cyan{background:var(--cyan-bg);border-color:var(--cyan-bd);color:var(--cyan-tx)}
.tag.dot::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
.tag.mono{font-family:var(--mono);font-size:11.5px}
.badge{display:inline-flex;align-items:center;gap:6px;font-size:13px}
.badge::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--stroke3)}
.badge.green::before{background:var(--green)}.badge.red::before{background:var(--red)}.badge.amber::before{background:var(--amber)}.badge.blue::before{background:var(--blue)}.badge.gray::before{background:var(--stroke3)}

/* ---------- buttons ---------- */
.btn{height:32px;padding:0 15px;border-radius:var(--r-sm);border:1px solid var(--stroke2);background:var(--bg2);color:var(--t1);font-size:14px;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;cursor:pointer;white-space:nowrap;box-shadow:0 2px 0 rgba(0,0,0,.02)}
.btn:hover{color:var(--primary);border-color:var(--primary-h)}
.btn.primary{background:var(--primary);border-color:var(--primary);color:#fff;box-shadow:0 2px 0 rgba(5,145,255,.1)}
.btn.primary:hover{background:var(--primary-h);border-color:var(--primary-h);color:#fff}
.btn.danger{color:var(--red);border-color:var(--red-bd)}
.btn.danger:hover{color:#fff;background:var(--red);border-color:var(--red)}
.btn.danger.primary{background:var(--red);border-color:var(--red);color:#fff}
.btn.text{border-color:transparent;background:transparent;box-shadow:none;color:var(--t2)}
.btn.text:hover{background:var(--hover);color:var(--t1)}
.btn.link{border-color:transparent;background:transparent;box-shadow:none;color:var(--primary);padding:0 4px;height:auto}
.btn.link.danger{color:var(--red)}
.btn.sm{height:24px;padding:0 7px;font-size:13px}
.btn.lg{height:40px;padding:0 20px;font-size:15px}
.btn.block{width:100%}
.btn[disabled],.btn.disabled{opacity:.45;cursor:not-allowed;pointer-events:none}
.btn-group{display:inline-flex}
.btn-group .btn{border-radius:0}.btn-group .btn:first-child{border-radius:var(--r-sm) 0 0 var(--r-sm)}.btn-group .btn:last-child{border-radius:0 var(--r-sm) var(--r-sm) 0}.btn-group .btn+.btn{margin-left:-1px}

/* ---------- forms ---------- */
.inp{height:32px;padding:0 11px;border:1px solid var(--stroke2);border-radius:var(--r-sm);background:var(--bg2);color:var(--t1);font:inherit;font-size:14px;width:100%;display:flex;align-items:center;gap:8px}
.inp:focus-within,.inp:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(5,145,255,.1);outline:none}
.inp input,.inp select{border:none;outline:none;background:transparent;font:inherit;color:var(--t1);flex:1;min-width:0}
.inp input::placeholder{color:var(--t4)}
.inp .pre{color:var(--t3);display:flex}
.inp.ro{background:var(--bg);color:var(--t2)}
.inp.lg{height:40px;font-size:15px}
.sel{position:relative}.sel::after{content:"";position:absolute;right:11px;top:50%;width:6px;height:6px;border-right:1.5px solid var(--t3);border-bottom:1.5px solid var(--t3);transform:translateY(-70%) rotate(45deg)}
.sel select{appearance:none;-webkit-appearance:none;padding-right:16px}
textarea.inp{height:auto;min-height:72px;padding:6px 11px;display:block;resize:vertical;font-family:var(--mono);font-size:12.5px}
.fi{display:flex;flex-direction:column;gap:6px}
.fi .lb{font-size:13.5px;color:var(--t1)}
.fi .lb.req::before{content:"* ";color:var(--red)}
.fi .hint{font-size:12px;color:var(--t3)}
.fi.h{flex-direction:row;align-items:center;gap:16px}.fi.h .lb{width:150px;flex:none;text-align:right;color:var(--t2)}
.form{display:flex;flex-direction:column;gap:18px}
.toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.toolbar .inp{width:220px}.toolbar .inp.w160{width:160px}.toolbar .inp.w130{width:130px}.toolbar .inp.w260{width:260px}
.toolbar .sp{flex:1}
.switch{width:44px;height:22px;border-radius:11px;background:var(--stroke3);position:relative;cursor:pointer;flex:none;transition:background .2s}
.switch::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:left .2s;box-shadow:0 2px 4px rgba(0,0,0,.2)}
.switch.on{background:var(--primary)}.switch.on::after{left:24px}
.switch.sm{width:28px;height:16px}.switch.sm::after{width:12px;height:12px}.switch.sm.on::after{left:14px}
.chk{width:16px;height:16px;border:1px solid var(--stroke2);border-radius:4px;display:inline-flex;align-items:center;justify-content:center;background:var(--bg2);cursor:pointer;flex:none;vertical-align:middle}
.chk.on{background:var(--primary);border-color:var(--primary);color:#fff}
.chk.ind{background:var(--bg2)}.chk.ind::after{content:"";width:8px;height:8px;background:var(--primary);border-radius:1px}
.radio{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-size:14px}
.radio i{width:16px;height:16px;border-radius:50%;border:1px solid var(--stroke2);display:inline-flex;align-items:center;justify-content:center;flex:none}
.radio.on i{border-color:var(--primary);border-width:5px}
.seg{display:inline-flex;background:var(--bg);border-radius:var(--r-sm);padding:2px;gap:2px}
.seg .s{height:28px;padding:0 12px;border-radius:4px;font-size:13.5px;display:flex;align-items:center;cursor:pointer;color:var(--t2)}
.seg .s.on{background:var(--bg2);color:var(--t1);box-shadow:var(--shadow);font-weight:500}
.tabs{display:flex;gap:28px;border-bottom:1px solid var(--stroke);padding:0 20px}
.tabs .tab{height:44px;display:flex;align-items:center;gap:6px;cursor:pointer;color:var(--t2);font-size:14px;border-bottom:2px solid transparent;margin-bottom:-1px}
.tabs .tab:hover{color:var(--primary)}
.tabs .tab.on{color:var(--primary);border-bottom-color:var(--primary);font-weight:500}
.tabs .tab .n{font-size:11px;background:var(--bg);padding:0 6px;border-radius:9px;color:var(--t3)}

/* ---------- table ---------- */
.tbl{width:100%;border-collapse:separate;border-spacing:0;font-size:13.5px}
.tbl th{background:var(--thead);text-align:left;font-weight:600;color:var(--t1);padding:11px 16px;border-bottom:1px solid var(--stroke);white-space:nowrap;position:relative}
.tbl th+th::before{content:"";position:absolute;left:0;top:50%;height:1.6em;transform:translateY(-50%);width:1px;background:var(--stroke2);opacity:.6}
.tbl td{padding:11px 16px;border-bottom:1px solid var(--stroke);vertical-align:middle}
.tbl tr:last-child td{border-bottom:none}
.tbl tbody tr:hover td{background:var(--row-hover)}
.tbl tr.sel td{background:var(--primary-bg)}
.tbl .r{text-align:right}.tbl .c{text-align:center}
.tbl .num{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:12.5px}
.tbl .acts{display:flex;gap:2px;justify-content:flex-end;white-space:nowrap}
.tbl .acts .btn.link{padding:0 6px}
.tbl .sub{font-size:12px;color:var(--t3);margin-top:1px}
.tbl td .who{display:flex;align-items:center;gap:10px}
.tbl.compact th,.tbl.compact td{padding:8px 12px}
.tbl th .sort{display:inline-flex;flex-direction:column;margin-left:4px;color:var(--t4);vertical-align:middle}
.tfoot{display:flex;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid var(--stroke);font-size:13px;color:var(--t2)}
.tfoot .sp{flex:1}
.pg{display:flex;gap:6px;align-items:center}
.pg .p{min-width:30px;height:30px;padding:0 6px;border:1px solid var(--stroke2);border-radius:var(--r-sm);display:flex;align-items:center;justify-content:center;font-size:13px;cursor:pointer;background:var(--bg2)}
.pg .p.on{border-color:var(--primary);color:var(--primary);font-weight:500}
.pg .p.dis{opacity:.4;cursor:default}
.pg .dots{color:var(--t3);padding:0 4px}
.mono{font-family:var(--mono);font-size:12.5px}
.addr{font-family:var(--mono);font-size:12.5px;display:inline-flex;align-items:center;gap:6px}
.addr .cp{color:var(--t3);cursor:pointer;display:inline-flex}.addr .cp:hover{color:var(--primary)}
.chain-ic{width:20px;height:20px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff;flex:none;vertical-align:middle}
.ci-eth{background:#627EEA}.ci-tron{background:#EF0027}.ci-bsc{background:#F0B90B;color:#1a1a1a}.ci-btc{background:#F7931A}.ci-sol{background:linear-gradient(135deg,#9945FF,#14F195)}.ci-pol{background:#8247E5}.ci-shasta{background:#EF0027;opacity:.6}.ci-sep{background:#627EEA;opacity:.6}
.tok{display:inline-flex;align-items:center;gap:7px}
.tok .chain-ic{width:22px;height:22px}
.tok .n{font-weight:500}.tok .s{font-size:11.5px;color:var(--t3)}

/* ---------- descriptions ---------- */
.desc{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px}
.desc.c3{grid-template-columns:repeat(3,minmax(0,1fr))}.desc.c1{grid-template-columns:1fr}
.desc .it{display:flex;gap:12px;padding:9px 0;border-bottom:1px dashed var(--stroke);min-width:0;align-items:baseline}
.desc .it .k{width:120px;flex:none;color:var(--t3);font-size:13px}
.desc .it .v{font-size:13.5px;min-width:0;word-break:break-all;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.desc.bd{border:1px solid var(--stroke);border-radius:var(--r);gap:0}
.desc.bd .it{padding:10px 16px;border-bottom:1px solid var(--stroke);margin:0}
.desc.bd .it:nth-child(odd){border-right:1px solid var(--stroke)}

/* ---------- alerts ---------- */
.alert{display:flex;gap:10px;padding:10px 14px;border-radius:var(--r);border:1px solid;font-size:13.5px;align-items:flex-start}
.alert svg{flex:none;margin-top:2px}
.alert .b{font-weight:500}.alert .d{color:var(--t2);font-size:13px;margin-top:2px}
.alert .sp{flex:1}
.alert.info{background:var(--blue-bg);border-color:var(--blue-bd)}.alert.info>svg{color:var(--blue)}
.alert.warn{background:var(--amber-bg);border-color:var(--amber-bd)}.alert.warn>svg{color:var(--amber)}
.alert.err{background:var(--red-bg);border-color:var(--red-bd)}.alert.err>svg{color:var(--red)}
.alert.ok{background:var(--green-bg);border-color:var(--green-bd)}.alert.ok>svg{color:var(--green)}

/* ---------- misc ---------- */
.kv{display:flex;justify-content:space-between;gap:10px;padding:8px 0;border-bottom:1px solid var(--stroke);font-size:13.5px}
.kv:last-child{border-bottom:none}.kv .k{color:var(--t2)}
.tl{position:relative;padding-left:22px}
.tl .ev{position:relative;padding-bottom:18px;font-size:13px}
.tl .ev::before{content:"";position:absolute;left:-18px;top:6px;width:9px;height:9px;border-radius:50%;background:var(--bg2);border:2px solid var(--primary)}
.tl .ev::after{content:"";position:absolute;left:-14px;top:17px;bottom:-2px;width:1px;background:var(--stroke2)}
.tl .ev:last-child::after{display:none}
.tl .ev.green::before{border-color:var(--green)}.tl .ev.red::before{border-color:var(--red)}.tl .ev.amber::before{border-color:var(--amber)}.tl .ev.gray::before{border-color:var(--stroke3)}
.tl .ev .t{font-weight:500}.tl .ev .m{color:var(--t3);font-size:12px}
.code{background:var(--code-bg);border:1px solid var(--stroke);border-radius:var(--r-sm);padding:10px 12px;font-family:var(--mono);font-size:12px;line-height:1.6;color:var(--t1);white-space:pre;overflow:auto}
.code .k{color:#0550AE}.code .s{color:#0A3069}.code .n{color:#8250DF}.code .c{color:var(--t3)}
.bar{height:6px;border-radius:3px;background:var(--bg);overflow:hidden}
.bar i{display:block;height:100%;border-radius:3px;background:var(--primary)}
.bar i.green{background:var(--green)}.bar i.red{background:var(--red)}.bar i.amber{background:var(--amber)}
.empty{padding:40px 20px;text-align:center;color:var(--t3);font-size:13px}
.empty svg{color:var(--stroke3);margin-bottom:8px}
.divider{height:1px;background:var(--stroke);margin:4px 0}
.muted{color:var(--t3)}.t2{color:var(--t2)}.sm{font-size:12.5px}.xs{font-size:12px}.b{font-weight:600}.md{font-weight:500}
.row{display:flex;align-items:center;gap:8px}.col{display:flex;flex-direction:column;gap:4px}.wrap{flex-wrap:wrap}
.sp{flex:1}
.mt8{margin-top:8px}.mt12{margin-top:12px}.mt16{margin-top:16px}.mb12{margin-bottom:12px}
.tt{position:relative}
.stepper{display:flex;align-items:center;gap:8px}
.stepper .st{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--t3)}
.stepper .st .n{width:24px;height:24px;border-radius:50%;border:1px solid var(--stroke2);display:flex;align-items:center;justify-content:center;font-size:12px}
.stepper .st.on{color:var(--t1);font-weight:500}.stepper .st.on .n{background:var(--primary);border-color:var(--primary);color:#fff}
.stepper .st.done .n{border-color:var(--primary);color:var(--primary)}
.stepper .ln{width:48px;height:1px;background:var(--stroke2)}
.mini-chart{display:flex;align-items:flex-end;gap:4px;height:64px}
.mini-chart i{flex:1;background:var(--primary-bg);border-radius:2px 2px 0 0;position:relative}
.mini-chart i.hi{background:var(--primary)}
.spark{display:flex;align-items:flex-end;gap:2px;height:28px}
.spark i{width:4px;background:var(--primary-bd);border-radius:1px}
.pw{filter:blur(4px);user-select:none}
.tree{font-size:13.5px}
.tree .tn{display:flex;align-items:center;gap:8px;height:30px;padding:0 6px;border-radius:4px}
.tree .tn:hover{background:var(--hover)}
.tree .tn.lvl1{padding-left:28px}
.tree .tn .desc-t{color:var(--t3);font-size:12px;margin-left:auto;font-family:var(--mono)}
.login-bg{min-height:100vh;background:var(--bg);display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}
.login-bg::before{content:"";position:absolute;inset:0;background-image:linear-gradient(rgba(0,0,0,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(0,0,0,.03) 1px,transparent 1px);background-size:32px 32px;-webkit-mask-image:radial-gradient(circle at 50% 40%,#000,transparent 70%)}
.login-card{width:420px;max-width:calc(100vw - 48px);position:relative;padding:36px 40px 32px}
html.shot .login-bg{min-height:900px}
.otp{display:flex;gap:8px}
.otp .c{flex:1;height:48px;border:1px solid var(--stroke2);border-radius:var(--r-sm);display:flex;align-items:center;justify-content:center;font-size:20px;font-family:var(--mono);background:var(--bg2)}
.otp .c.f{border-color:var(--primary)}
.otp .c.cur{border-color:var(--primary);box-shadow:0 0 0 2px rgba(5,145,255,.1)}

/* ---------- RESPONSIVE (bootstrap-style) ----------
   The shell is fluid: .content fills whatever the viewport gives it. A page
   that wants a readable cap adds ONE of these to its own <main> (buildShell
   keeps the tag's classes when it becomes .content):
     <main class="container-xl">  → capped per the tier table below
     <main>                       → fluid, the default
   Tier caps are the conventional 540/720/960/1140/1320; each trigger point
   is the conventional breakpoint (576/768/992/1200/1400) PLUS the 220px
   sider, because the cap is measured against the CONTENT column and the
   sider sits outside it. */
.container,.container-sm,.container-md,.container-lg,.container-xl,.container-xxl,.container-fluid{width:100%;margin-left:auto;margin-right:auto}
@media (min-width:796px){.container,.container-sm{max-width:540px}}
@media (min-width:988px){.container,.container-md{max-width:720px}}
@media (min-width:1212px){.container,.container-lg{max-width:960px}}
@media (min-width:1420px){.container,.container-xl{max-width:1140px}}
@media (min-width:1620px){.container,.container-xxl{max-width:1320px}}

/* Grids step down instead of squeezing: 4-6 cols → 3 → 2 → 1. Never in shot
   mode — screenshots are the 1440 desktop reference. */
@media (max-width:1399px){html:not(.shot) .g5,html:not(.shot) .g6{grid-template-columns:repeat(4,1fr)}}
@media (max-width:1199px){html:not(.shot) .g4,html:not(.shot) .g5,html:not(.shot) .g6{grid-template-columns:repeat(3,1fr)}}
@media (max-width:991px){
  html:not(.shot) .g3,html:not(.shot) .g4,html:not(.shot) .g5,html:not(.shot) .g6{grid-template-columns:1fr 1fr}
  html:not(.shot) .g-2-1,html:not(.shot) .g-1-2,html:not(.shot) .g-3-2{grid-template-columns:1fr}
  html:not(.shot) .desc.c3{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:767px){
  html:not(.shot) .g2,html:not(.shot) .g3,html:not(.shot) .g4,html:not(.shot) .g5,html:not(.shot) .g6{grid-template-columns:1fr}
  html:not(.shot) .desc,html:not(.shot) .desc.c3{grid-template-columns:1fr}
  html:not(.shot) .content{padding:16px 16px 24px}
}

/* Wide tables scroll inside their card instead of striping the page: the
   card clips, the table keeps its natural width. */
.tbl-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.tbl-wrap .tbl{min-width:900px}
@media (max-width:1199px){html:not(.shot) .card{overflow-x:auto}html:not(.shot) .card .tbl{min-width:860px}}

/* ---------- EXTENDED KIT (styleguide 20) ----------
   Colour buttons follow .btn.danger's pattern: tinted outline at rest, solid
   on hover; add .primary for always-solid. */
.btn.green{color:var(--green-tx);border-color:var(--green-bd)}
.btn.green:hover{color:#fff;background:var(--green);border-color:var(--green)}
.btn.green.primary{background:var(--green);border-color:var(--green);color:#fff}
.btn.green.primary:hover{background:#73D13D;border-color:#73D13D}
.btn.amber{color:var(--amber-tx);border-color:var(--amber-bd)}
.btn.amber:hover{color:#fff;background:var(--amber);border-color:var(--amber)}
.btn.amber.primary{background:var(--amber);border-color:var(--amber);color:#fff}
.btn.amber.primary:hover{background:#FFC53D;border-color:#FFC53D}
.btn.violet{color:var(--violet-tx);border-color:var(--violet-bd)}
.btn.violet:hover{color:#fff;background:var(--violet);border-color:var(--violet)}
.btn.violet.primary{background:var(--violet);border-color:var(--violet);color:#fff}
.btn.violet.primary:hover{background:#9254DE;border-color:#9254DE}
.btn.cyan{color:var(--cyan-tx);border-color:var(--cyan-bd)}
.btn.cyan:hover{color:#fff;background:var(--cyan);border-color:var(--cyan)}
.btn.cyan.primary{background:var(--cyan);border-color:var(--cyan);color:#fff}
.btn.cyan.primary:hover{background:#36CFC9;border-color:#36CFC9}
.btn.orange{color:var(--orange-tx);border-color:var(--orange-bd)}
.btn.orange:hover{color:#fff;background:var(--orange);border-color:var(--orange)}
.btn.orange.primary{background:var(--orange);border-color:var(--orange);color:#fff}
.btn.orange.primary:hover{background:#FFA940;border-color:#FFA940}
.btn.geek{color:var(--geek-tx);border-color:var(--geek-bd)}
.btn.geek:hover{color:#fff;background:var(--geek);border-color:var(--geek)}
.btn.geek.primary{background:var(--geek);border-color:var(--geek);color:#fff}
.btn.geek.primary:hover{background:#597EF6;border-color:#597EF6}
.btn.dark{background:var(--t1);border-color:var(--t1);color:var(--bg2)}
.btn.dark:hover{opacity:.85;color:var(--bg2)}
.btn.danger.primary:hover{background:#FF7875;border-color:#FF7875}

/* Interaction states — every button gets a visible focus ring (keyboard) and
   a pressed shade; the ring colour follows the button's own palette. */
.btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--primary-bg)}
.btn.danger:focus-visible{box-shadow:0 0 0 3px var(--red-bg)}
.btn.green:focus-visible{box-shadow:0 0 0 3px var(--green-bg)}
.btn.amber:focus-visible{box-shadow:0 0 0 3px var(--amber-bg)}
.btn.violet:focus-visible{box-shadow:0 0 0 3px var(--violet-bg)}
.btn.cyan:focus-visible{box-shadow:0 0 0 3px var(--cyan-bg)}
.btn.orange:focus-visible{box-shadow:0 0 0 3px var(--orange-bg)}
.btn.geek:focus-visible{box-shadow:0 0 0 3px var(--geek-bg)}
.btn.dark:focus-visible{box-shadow:0 0 0 3px rgba(0,0,0,.15)}
.btn.primary:active{background:#0958D9;border-color:#0958D9}
.btn.danger.primary:active{background:#D9363E;border-color:#D9363E}
.btn.green.primary:active{background:var(--green-tx);border-color:var(--green-tx)}
.btn.amber.primary:active{background:var(--amber-tx);border-color:var(--amber-tx)}
.btn.violet.primary:active{background:var(--violet-tx);border-color:var(--violet-tx)}
.btn.cyan.primary:active{background:var(--cyan-tx);border-color:var(--cyan-tx)}
.btn.orange.primary:active{background:var(--orange-tx);border-color:var(--orange-tx)}
.btn.geek.primary:active{background:var(--geek-tx);border-color:var(--geek-tx)}

/* Link/text buttons never gain a filled background — hover only shifts the
   text colour (the .danger fill above must not reach them). */
.btn.link:hover,.btn.link:active,.btn.link:focus-visible{background:transparent;border-color:transparent}
.btn.link:hover{color:var(--primary-h)}
.btn.link:active{color:#0958D9}
.btn.link.danger:hover{background:transparent;border-color:transparent;color:#FF7875}
.btn.link.danger:active{color:#D9363E}
.btn.text.danger{color:var(--red)}
.btn.text.danger:hover{background:var(--red-bg);color:var(--red-tx)}

/* Plain links .lnk — for prose and table cells. */
.lnk{color:var(--primary);cursor:pointer;border-radius:4px}
.lnk:hover{color:var(--primary-h)}
.lnk:active{color:#0958D9}
.lnk:focus-visible{outline:none;box-shadow:0 0 0 3px var(--primary-bg)}
.lnk.underline{text-decoration:underline;text-underline-offset:3px}
.lnk.quiet{color:var(--t2)}
.lnk.quiet:hover{color:var(--primary)}
.lnk.danger{color:var(--red)}
.lnk.danger:hover{color:#FF7875}
.lnk.danger:active{color:#D9363E}
.lnk.dis{color:var(--t4);cursor:not-allowed;pointer-events:none}
.lnk.ic{display:inline-flex;align-items:center;gap:4px}

/* Width utilities — inputs, selects, anything. Appended after .inp so the
   single-class tie resolves to these. */
.w80{width:80px}.w100{width:100px}.w130{width:130px}.w160{width:160px}
.w200{width:200px}.w260{width:260px}.w320{width:320px}.w400{width:400px}

/* Typography scale + semantic text colours. */
.fs12{font-size:12px}.fs13{font-size:13px}.fs14{font-size:14px}.fs15{font-size:15px}
.fs16{font-size:16px}.fs18{font-size:18px}.fs20{font-size:20px}.fs24{font-size:24px}
.fs28{font-size:28px}.fs32{font-size:32px}
.fw400{font-weight:400}.fw500{font-weight:500}.fw600{font-weight:600}.fw700{font-weight:700}
.c-1{color:var(--t1)}.c-2{color:var(--t2)}.c-3{color:var(--t3)}
.c-primary{color:var(--primary)}.c-green{color:var(--green-tx)}.c-red{color:var(--red-tx)}
.c-amber{color:var(--amber-tx)}.c-violet{color:var(--violet-tx)}.c-blue{color:var(--blue)}

/* 12-column row/col grid, bootstrap-shaped: .cols is the row, .col-N spans N
   of 12. Under 768px every col takes the full row (not in shot mode). */
.cols{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}
.cols.g8{gap:8px}.cols.g12{gap:12px}
.col-1{grid-column:span 1}.col-2{grid-column:span 2}.col-3{grid-column:span 3}
.col-4{grid-column:span 4}.col-5{grid-column:span 5}.col-6{grid-column:span 6}
.col-7{grid-column:span 7}.col-8{grid-column:span 8}.col-9{grid-column:span 9}
.col-10{grid-column:span 10}.col-11{grid-column:span 11}.col-12{grid-column:span 12}
@media (max-width:767px){html:not(.shot) .cols>[class^="col-"]{grid-column:span 12}}

/* Upload — the dashed picker, image thumbs with hover actions, file rows. */
.upload{width:104px;height:104px;border:1.5px dashed var(--stroke2);border-radius:var(--r-sm);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:var(--t3);cursor:pointer;background:var(--bg2);font-size:12.5px;flex:none}
.upload:hover{border-color:var(--primary);color:var(--primary)}
.upthumb{width:104px;height:104px;border:1px solid var(--stroke);border-radius:var(--r-sm);position:relative;overflow:hidden;background:var(--bg);flex:none}
.upthumb img{width:100%;height:100%;object-fit:cover;display:block}
.upthumb .ops{position:absolute;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;gap:12px;color:#fff;opacity:0;transition:opacity .15s}
.upthumb:hover .ops{opacity:1}
.upthumb .ops i,.upthumb .ops svg{cursor:pointer}
.filelist{display:flex;flex-direction:column;gap:4px}
.filelist .f{display:flex;align-items:center;gap:8px;height:30px;padding:0 8px;border-radius:var(--r-sm);font-size:13px;color:var(--t2)}
.filelist .f:hover{background:var(--hover)}
.filelist .f .x{margin-left:auto;color:var(--t3);cursor:pointer}
.filelist .f .x:hover{color:var(--red)}

/* Pagination — the full pager: total text, size select, numbered cells with
   ellipsis, quick jumper. .tfoot stays the slim table variant. */
.pager{display:flex;align-items:center;gap:8px;font-size:13.5px;flex-wrap:wrap}
.pager .p{min-width:32px;height:32px;padding:0 6px;border:1px solid var(--stroke2);border-radius:var(--r-sm);background:var(--bg2);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--t1)}
.pager .p:hover{color:var(--primary);border-color:var(--primary)}
.pager .p.on{color:var(--primary);border-color:var(--primary);font-weight:500}
.pager .p.dis{opacity:.4;pointer-events:none}
.pager .dots{min-width:32px;text-align:center;color:var(--t3);letter-spacing:2px}
.pager.sm .p{min-width:24px;height:24px;font-size:12.5px}

/* Top navigation bars — the PUBLIC chrome (home / content pages, pre-login).
   The signed-in backoffice keeps the left sider; these are for everything in
   front of it. Three looks: light content bar, dark brand bar, centered
   marketing bar. */
.topnav{height:56px;display:flex;align-items:center;gap:24px;padding:0 24px;background:var(--bg2);border-bottom:1px solid var(--stroke)}
.topnav .tn-brand{display:flex;align-items:center;gap:10px;font-weight:600;font-size:15px;color:var(--t1)}
.topnav .tn-links{display:flex;align-items:center;gap:4px}
.topnav .tn-links a{height:36px;padding:0 14px;border-radius:var(--r-sm);display:inline-flex;align-items:center;color:var(--t2);font-size:14px}
.topnav .tn-links a:hover{background:var(--hover);color:var(--t1)}
.topnav .tn-links a.on{color:var(--primary);background:var(--primary-bg);font-weight:500}
.topnav.dark{background:#131A2C;border-bottom-color:rgba(255,255,255,.08)}
.topnav.dark .tn-brand{color:#fff}
.topnav.dark .tn-links a{color:rgba(255,255,255,.65)}
.topnav.dark .tn-links a:hover{background:rgba(255,255,255,.08);color:#fff}
.topnav.dark .tn-links a.on{background:rgba(245,183,10,.15);color:#F5B70A}
.topnav.center{justify-content:space-between}
.topnav.center .tn-links{position:absolute;left:50%;transform:translateX(-50%)}

/* ---------- SPACING UTILITIES ----------
   m/p + t b l r x y, steps 0 4 8 12 16 24 32 (px). .mx-auto centres. */
.m0{margin:0px}
.m4{margin:4px}
.m8{margin:8px}
.m12{margin:12px}
.m16{margin:16px}
.m24{margin:24px}
.m32{margin:32px}
.mt0{margin-top:0px}
.mt4{margin-top:4px}
.mt8{margin-top:8px}
.mt12{margin-top:12px}
.mt16{margin-top:16px}
.mt24{margin-top:24px}
.mt32{margin-top:32px}
.mb0{margin-bottom:0px}
.mb4{margin-bottom:4px}
.mb8{margin-bottom:8px}
.mb12{margin-bottom:12px}
.mb16{margin-bottom:16px}
.mb24{margin-bottom:24px}
.mb32{margin-bottom:32px}
.ml0{margin-left:0px}
.ml4{margin-left:4px}
.ml8{margin-left:8px}
.ml12{margin-left:12px}
.ml16{margin-left:16px}
.ml24{margin-left:24px}
.ml32{margin-left:32px}
.mr0{margin-right:0px}
.mr4{margin-right:4px}
.mr8{margin-right:8px}
.mr12{margin-right:12px}
.mr16{margin-right:16px}
.mr24{margin-right:24px}
.mr32{margin-right:32px}
.mx0{margin-left:0px;margin-right:0px}
.mx4{margin-left:4px;margin-right:4px}
.mx8{margin-left:8px;margin-right:8px}
.mx12{margin-left:12px;margin-right:12px}
.mx16{margin-left:16px;margin-right:16px}
.mx24{margin-left:24px;margin-right:24px}
.mx32{margin-left:32px;margin-right:32px}
.my0{margin-top:0px;margin-bottom:0px}
.my4{margin-top:4px;margin-bottom:4px}
.my8{margin-top:8px;margin-bottom:8px}
.my12{margin-top:12px;margin-bottom:12px}
.my16{margin-top:16px;margin-bottom:16px}
.my24{margin-top:24px;margin-bottom:24px}
.my32{margin-top:32px;margin-bottom:32px}
.p0{padding:0px}
.p4{padding:4px}
.p8{padding:8px}
.p12{padding:12px}
.p16{padding:16px}
.p24{padding:24px}
.p32{padding:32px}
.pt0{padding-top:0px}
.pt4{padding-top:4px}
.pt8{padding-top:8px}
.pt12{padding-top:12px}
.pt16{padding-top:16px}
.pt24{padding-top:24px}
.pt32{padding-top:32px}
.pb0{padding-bottom:0px}
.pb4{padding-bottom:4px}
.pb8{padding-bottom:8px}
.pb12{padding-bottom:12px}
.pb16{padding-bottom:16px}
.pb24{padding-bottom:24px}
.pb32{padding-bottom:32px}
.pl0{padding-left:0px}
.pl4{padding-left:4px}
.pl8{padding-left:8px}
.pl12{padding-left:12px}
.pl16{padding-left:16px}
.pl24{padding-left:24px}
.pl32{padding-left:32px}
.pr0{padding-right:0px}
.pr4{padding-right:4px}
.pr8{padding-right:8px}
.pr12{padding-right:12px}
.pr16{padding-right:16px}
.pr24{padding-right:24px}
.pr32{padding-right:32px}
.px0{padding-left:0px;padding-right:0px}
.px4{padding-left:4px;padding-right:4px}
.px8{padding-left:8px;padding-right:8px}
.px12{padding-left:12px;padding-right:12px}
.px16{padding-left:16px;padding-right:16px}
.px24{padding-left:24px;padding-right:24px}
.px32{padding-left:32px;padding-right:32px}
.py0{padding-top:0px;padding-bottom:0px}
.py4{padding-top:4px;padding-bottom:4px}
.py8{padding-top:8px;padding-bottom:8px}
.py12{padding-top:12px;padding-bottom:12px}
.py16{padding-top:16px;padding-bottom:16px}
.py24{padding-top:24px;padding-bottom:24px}
.py32{padding-top:32px;padding-bottom:32px}
.mx-auto{margin-left:auto;margin-right:auto}
.gap4{gap:4px}
.gap8{gap:8px}
.gap12{gap:12px}
.gap16{gap:16px}
.gap24{gap:24px}

/* ---------- TABLE VARIANTS ---------- */
.tbl.striped tbody tr:nth-child(odd) td{background:var(--bg)}
.tbl.striped tbody tr:hover td{background:var(--row-hover)}
.tbl.bordered th,.tbl.bordered td{border-right:1px solid var(--stroke)}
.tbl.bordered th:last-child,.tbl.bordered td:last-child{border-right:none}
.tbl.plain th{background:transparent;border-bottom:2px solid var(--stroke2)}
.tbl.plain th+th::before{display:none}

/* ---------- TOPNAV VARIANTS ---------- */
.topnav{position:relative}
/* active = bottom underline, full-height tab feel */
.topnav.underline .tn-links{align-self:stretch;align-items:stretch;gap:20px}
.topnav.underline .tn-links a{height:auto;border-radius:0;padding:0 4px}
.topnav.underline .tn-links a:hover{background:transparent;color:var(--t1)}
.topnav.underline .tn-links a.on{background:transparent;color:var(--primary);font-weight:500;box-shadow:inset 0 -2px 0 var(--primary)}
/* active = coloured text only, no pill */
.topnav.plain .tn-links a:hover{background:transparent;color:var(--primary)}
.topnav.plain .tn-links a.on{background:transparent;color:var(--primary);font-weight:600}
/* burger + collapsed menu: .collapsible folds under 768px; .force-collapse
   keeps a demo folded at any width */
.tn-burger{display:none;width:36px;height:36px;border-radius:var(--r-sm);align-items:center;justify-content:center;cursor:pointer;color:var(--t2)}
.tn-burger:hover{background:var(--hover);color:var(--t1)}
@media (max-width:767px){
  html:not(.shot) .topnav.collapsible .tn-links{display:none}
  html:not(.shot) .topnav.collapsible .tn-burger{display:inline-flex}
}
.topnav.force-collapse .tn-links{display:none}
.topnav.force-collapse .tn-burger{display:inline-flex}
.tn-menu{position:absolute;top:100%;left:0;right:0;background:var(--bg2);border:1px solid var(--stroke);border-top:none;border-radius:0 0 var(--r-sm) var(--r-sm);box-shadow:0 8px 20px rgba(15,23,42,.1);display:none;flex-direction:column;padding:8px;z-index:60}
.tn-menu.open{display:flex}
.tn-menu a{height:40px;padding:0 12px;border-radius:var(--r-sm);display:flex;align-items:center;color:var(--t2);font-size:14px;cursor:pointer}
.tn-menu a:hover{background:var(--hover);color:var(--t1)}
.tn-menu a.on{color:var(--primary);background:var(--primary-bg);font-weight:500}

/* ---------- BORDER UTILITIES ----------
   .bd = 1px solid neutral; add .bd2/.bd3 for width, .bd-dashed for style,
   .bd-<colour> for colour, .bd-t/b/l/r for one side only. */
.bd{border:1px solid var(--stroke2)}
.bd-t{border-top:1px solid var(--stroke2)}
.bd-b{border-bottom:1px solid var(--stroke2)}
.bd-l{border-left:1px solid var(--stroke2)}
.bd-r{border-right:1px solid var(--stroke2)}
.bd2{border-width:2px}.bd3{border-width:3px}
.bd-dashed{border-style:dashed}
.bd-light{border-color:var(--stroke)}
.bd-primary{border-color:var(--primary)}
.bd-green{border-color:var(--green)}
.bd-red{border-color:var(--red)}
.bd-amber{border-color:var(--amber)}
.bd-violet{border-color:var(--violet)}
.bd-cyan{border-color:var(--cyan)}
.bd-orange{border-color:var(--orange)}
.bd-geek{border-color:var(--geek)}
.bd-dark{border-color:var(--t1)}

/* ---------- FORM VALIDATION ----------
   Fields declare rules via data-v="required|email|min:2|max:20|num|url|match:#id"
   (see app.js). Failure always paints the red border + a .hint.err line; the
   flags below on the .fi opt into extra looks and combine freely:
   .v-lb red label · .v-bg tinted input · .v-ic ✕/✓ suffix icon ·
   .v-ok green border + ✓ when a filled field passes · .v-shake shake on fail. */
.req{color:var(--red)}
.inp.err{border-color:var(--red)}
.inp.err:focus-within,.inp.err:focus{border-color:var(--red);box-shadow:0 0 0 2px rgba(255,77,79,.12)}
.fi .hint.err{color:var(--red-tx)}
.inp.ok{border-color:var(--green)}
.inp.ok:focus-within,.inp.ok:focus{border-color:var(--green);box-shadow:0 0 0 2px rgba(82,196,26,.12)}
.fi .hint.ok{color:var(--green-tx)}
.fi.v-lb.bad .lb{color:var(--red-tx)}
.fi.v-bg .inp.err{background:var(--red-bg)}
.inp .vic{display:none;flex:none;line-height:0;margin-left:4px}
.inp .vic.on{display:flex}
.inp .vic.bad{color:var(--red)}
.inp .vic.good{color:var(--green)}
@keyframes vshake{0%,100%{transform:none}20%,60%{transform:translateX(-4px)}40%,80%{transform:translateX(4px)}}
.fi.shake .inp{animation:vshake .3s}

/* ---------- PICKERS (date / time / multi-select) ----------
   Anchored panels, same dismiss model as .dd/.pop: fixed to body, killed on
   outside click. Trigger inputs: .inp[data-date] / [data-time] / [data-msel]. */
.dp,.tp,.ms{position:absolute;z-index:120;background:var(--bg2);border:1px solid var(--stroke);border-radius:var(--r);box-shadow:var(--shadow2);font-size:13px}
.dp{width:252px;padding:8px}
.dp .dh2{display:flex;align-items:center;justify-content:center;gap:2px;height:32px;font-weight:500}
.dp .dh2 .yl{padding:0 6px}
.dp .nv{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;color:var(--t3);cursor:pointer;font-family:var(--mono)}
.dp .nv:hover{background:var(--hover);color:var(--t1)}
.dp .wd,.dp .days{display:grid;grid-template-columns:repeat(7,1fr)}
.dp .wd span{height:26px;display:flex;align-items:center;justify-content:center;color:var(--t3);font-size:12px}
.dp .d{height:30px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;font-variant-numeric:tabular-nums}
.dp .d:hover{background:var(--hover)}
.dp .d.oth{color:var(--t4)}
.dp .d.today{box-shadow:inset 0 0 0 1px var(--primary);color:var(--primary)}
.dp .d.on{background:var(--primary);color:#fff}
.dp .ft,.tp .ft{display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--stroke);margin-top:6px;padding:6px 4px 0}
.tp{width:198px;padding:6px 6px 8px}
.tp .cols3{display:flex;height:192px}
.tp .cols3 ul{flex:1;overflow:auto;list-style:none;margin:0;padding:2px;border-right:1px solid var(--stroke);scrollbar-width:thin}
.tp .cols3 ul:last-child{border-right:none}
.tp li{height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;font-variant-numeric:tabular-nums}
.tp li:hover{background:var(--hover)}
.tp li.on{background:var(--primary-bg);color:var(--primary);font-weight:500}
.ms{min-width:200px;max-height:244px;overflow:auto;padding:4px}
.ms .op{display:flex;align-items:center;gap:8px;height:32px;padding:0 10px;border-radius:4px;cursor:pointer;font-size:13.5px}
.ms .op:hover{background:var(--hover)}
.ms .op .ck{width:14px;height:14px;flex:none;border:1px solid var(--stroke2);border-radius:3px;display:flex;align-items:center;justify-content:center;color:#fff}
.ms .op.on .ck{background:var(--primary);border-color:var(--primary)}
.ms .op.on{color:var(--primary)}
/* the multi-select trigger grows with its tags */
.inp.msel{height:auto;min-height:32px;flex-wrap:wrap;padding:3px 8px;gap:4px;cursor:pointer}
.inp.msel .ph{color:var(--t4)}
.mtag{display:inline-flex;align-items:center;gap:4px;height:22px;padding:0 6px;background:var(--bg);border:1px solid var(--stroke2);border-radius:4px;font-size:12px;white-space:nowrap}
.mtag .x{display:flex;color:var(--t3);cursor:pointer}
.mtag .x:hover{color:var(--red)}

/* ---------- TABLE RENDERER (list ajax) ---------- */
.tbl-box{position:relative;border:1px solid var(--stroke);border-radius:var(--r-sm);overflow-x:auto;overflow-y:hidden}
.tbl-loading{position:absolute;inset:0;background:rgba(255,255,255,.65);display:flex;align-items:center;justify-content:center;z-index:5}
.spin{width:22px;height:22px;border:2.5px solid var(--primary-bg);border-top-color:var(--primary);border-radius:50%;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* range picker: two calendars side by side; .in tints the days between the
   endpoints. searchable multi-select: sticky filter box atop the .ms panel. */
.dp.rg{width:532px;padding:10px}
.dp.rg .cals{display:flex;gap:16px}
.dp.rg .cal{flex:1;min-width:0}
.dp .d.in{background:var(--primary-bg);border-radius:0;color:var(--primary)}
.ms .ms-s{position:sticky;top:-4px;background:var(--bg2);padding:4px 4px 6px;margin:-4px -4px 4px;border-bottom:1px solid var(--stroke);z-index:1}
.ms .ms-s input{width:100%;height:28px;border:1px solid var(--stroke2);border-radius:4px;padding:0 8px;font:inherit;font-size:13px;outline:none;background:var(--bg2);color:var(--t1)}
.ms .ms-s input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(5,145,255,.1)}
.ms .ms-none{padding:10px;text-align:center;color:var(--t3);font-size:12.5px}

/* ---------- PRIMARY-COLOUR DOTS (styleguide switcher) ---------- */
.prim-dots{display:inline-flex;gap:8px;align-items:center;flex-wrap:wrap}
.prim-dots .pd{width:20px;height:20px;border-radius:50%;cursor:pointer;flex:none;transition:transform .12s}
.prim-dots .pd:hover{transform:scale(1.15)}
.prim-dots .pd.on{box-shadow:0 0 0 2px var(--bg2),0 0 0 4px var(--t2)}

/* ---------- IMAGE STYLES ----------
   Utilities compose: give the <img> a size, then shape/fit/effect classes. */
.img-rd{border-radius:var(--r)}
.img-circle{border-radius:50%}
.img-thumb{padding:3px;background:var(--bg2);border:1px solid var(--stroke2);border-radius:var(--r-sm)}
.img-cover{object-fit:cover}
.img-contain{object-fit:contain;background:var(--bg)}
.img-gray{filter:grayscale(1);transition:filter .25s}
.img-gray:hover{filter:none}
.img-zoombox{overflow:hidden;border-radius:var(--r);display:inline-block;line-height:0}
.img-zoombox img{display:block;transition:transform .3s}
.img-zoombox:hover img{transform:scale(1.08)}
figure.img-cap{margin:0;display:inline-flex;flex-direction:column;gap:6px}
figure.img-cap figcaption{font-size:12.5px;color:var(--t3);text-align:center}
.img-stack{display:inline-flex;align-items:center}
.img-stack img{width:32px;height:32px;border-radius:50%;border:2px solid var(--bg2);margin-left:-9px;position:relative}
.img-stack img:first-child{margin-left:0}
.img-stack .more{width:32px;height:32px;border-radius:50%;border:2px solid var(--bg2);margin-left:-9px;background:var(--bg);color:var(--t2);font-size:11px;display:flex;align-items:center;justify-content:center}
.gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}
.gallery img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--r-sm);cursor:zoom-in;transition:opacity .15s;display:block}
.gallery img:hover{opacity:.85}

/* ---------- QUERY-FORM TOOLBAR ----------
   Field name ABOVE the control (never as the control's placeholder), with
   explicit 查询 / 重置 buttons — mirrors the real backoffice convention. Add
   `qf` next to `toolbar`; each control sits inside <div class="f"> with a
   <div class="lb"> label. */
.toolbar.qf{align-items:flex-end;gap:12px 16px}
.qf .f{display:flex;flex-direction:column;gap:5px}
.qf .f>.lb{font-size:12px;color:var(--t2);line-height:1}

/* ---------- CLEARABLE INPUTS ----------
   Any .inp with typed content grows a ✕ at its right edge on hover/focus
   (app.js wires it globally); clicking clears the value and re-fires
   input/change so validation and listeners stay honest. Selects, multi-selects
   and readonly picker inputs are exempt — they have their own clear paths. */
.inp .clr{display:none;flex:none;margin-left:4px;color:var(--t4);cursor:pointer;line-height:0}
.inp.has-val:hover .clr,.inp.has-val:focus-within .clr{display:flex}
.inp .clr:hover{color:var(--t2)}

/* ---------- STICKY ACTIONS COLUMN ----------
   When a table scrolls horizontally (.tbl-wrap), the last column — actions —
   stays pinned to the right edge while the rest slides under it. */
.tbl-wrap .tbl th:last-child,.tbl-wrap .tbl td:last-child,
.tbl-box .tbl th:last-child,.tbl-box .tbl td:last-child{position:sticky;right:0;background:var(--bg2);box-shadow:-8px 0 8px -8px rgba(0,0,0,.12)}
.tbl-wrap .tbl th:last-child,.tbl-box .tbl th:last-child{background:var(--thead)}
.tbl-wrap .tbl tbody tr:hover td:last-child,.tbl-box .tbl tbody tr:hover td:last-child{background:var(--row-hover)}
