/* att-saas 运营控制台 · 设计系统 v3 —— EMQX Dashboard 标准
   深海军蓝外壳(#071927 侧栏/头/画布)+ 白色扁平卡片(16px 圆角, 1px #E2E6F0 边, 无阴影)
   品牌绿 #00B173 / 危险红 #EB4E3D / 表头 #EEF0F5 / 行悬停 #F8FAFE
   令牌单源: docs/design-tokens.md(与 frontend/src/theme.ts 同步)
   分区: 1 设计令牌  2 基础  3 登录(分屏)  4 框架  5 组件  5b 健康  6 图表  7 弹窗  8 动效/响应 */

/* ===== 1. 设计令牌(变量名与 app.js 内联引用契约, 不可改名)
   主题: light(默认)=浅灰画布+白侧栏+白卡片; dark=全暗(深海军蓝) —— html[data-theme] 切换 ===== */
:root{
  /* 外壳 */
  --bg:#F4F6F8;--bg2:#FFFFFF;
  --side-line:#E2E6F0;
  --page-title:#192144;--page-desc:#5A5E66;
  --brand-name:#192144;--brand-sub:#8A8F99;
  --nav-fg:#5A5E66;--nav-hover-fg:#192144;--nav-hover-bg:rgba(0,0,0,.04);--nav-led:#C4CAD4;--nav-active:#00915E;
  --side-foot-fg:#8A8F99;
  --hdr-right-fg:#8A8F99;
  --gpill-fg:#5A5E66;--gpill-border:rgba(0,0,0,.14);
  --gpill-ok:#00875A;--gpill-warn:#B97E1F;--gpill-err:#C43E30;
  --sec-fg:#5A5E66;
  --scroll:rgba(0,0,0,.18);
  /* 卡片/文字 */
  --panel:#FFFFFF;--panel2:#F8FAFE;
  --line:#E2E6F0;--line2:#D0D4DC;
  --fg:#343741;--dim:#5A5E66;--dim2:#8A8F99;
  --fg-head:#192144;
  /* 语义色 */
  --ok:#00A06B;--warn:#D98A1F;--err:#E0483A;
  --acc:#00B173;--acc2:#00C48C;
  --ok-bg:rgba(0,177,115,.10);--warn-bg:rgba(217,138,31,.10);--err-bg:rgba(224,72,58,.09);
  /* 输入/组件面 */
  --field-bg:#FFFFFF;--ghost-bg:#FFFFFF;--thead-bg:#EEF0F5;--addr-bg:#F3F5F9;
  /* 形状 */
  --r-sm:8px;--r:8px;--r-lg:16px;
  /* 字阶 */
  --fs-11:11px;--fs-12:12px;--fs-13:13px;--fs-14:14px;--fs-18:18px;--fs-24:24px;
  --side-w:220px;--top-h:60px;
}
html[data-theme='dark']{
  --bg:#071927;--bg2:#071927;
  --side-line:rgba(255,255,255,.06);
  --page-title:#FFFFFF;--page-desc:rgba(255,255,255,.5);
  --brand-name:#FFFFFF;--brand-sub:rgba(255,255,255,.45);
  --nav-fg:rgba(255,255,255,.65);--nav-hover-fg:#FFFFFF;--nav-hover-bg:rgba(255,255,255,.05);--nav-led:rgba(255,255,255,.22);--nav-active:#00C48C;
  --side-foot-fg:rgba(255,255,255,.5);
  --hdr-right-fg:rgba(255,255,255,.5);
  --gpill-fg:rgba(255,255,255,.7);--gpill-border:rgba(255,255,255,.18);
  --gpill-ok:#2BD997;--gpill-warn:#F0B25A;--gpill-err:#FF7B6E;
  --sec-fg:rgba(255,255,255,.5);
  --scroll:rgba(255,255,255,.15);
  --panel:#0F1E2B;--panel2:#132433;
  --line:#233748;--line2:#2A3F52;
  --fg:#C6D0DC;--dim:#8A97A8;--dim2:#5D6B7D;--fg-head:#E6EDF5;
  --ok:#2BD997;--warn:#F0B25A;--err:#FF7B6E;
  --field-bg:#0C1926;--ghost-bg:transparent;--thead-bg:#142634;--addr-bg:#0C1926;
  color-scheme:dark;
}
:root{color-scheme:light}
select option{background:var(--field-bg);color:var(--fg)}

/* ===== 2. 基础 ===== */
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;background:var(--bg);color:var(--fg);
     font:var(--fs-14)/1.55 Roboto,"Segoe UI",system-ui,-apple-system,"PingFang SC",sans-serif;
     -webkit-font-smoothing:antialiased}
::selection{background:rgba(0,177,115,.25)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:var(--scroll);border-radius:8px;border:2px solid transparent;background-clip:content-box}
::-webkit-scrollbar-track{background:transparent}
button{font:inherit}
input,select,textarea{background:var(--field-bg);color:var(--fg);border:1px solid var(--line2);border-radius:var(--r-sm);
  padding:6px 10px;height:32px;transition:border-color .15s,box-shadow .15s}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px rgba(0,177,115,.15)}
button{background:var(--acc);border:none;color:#fff;border-radius:var(--r-sm);
  height:32px;padding:0 18px;font-weight:500;cursor:pointer;transition:filter .15s,transform .05s}
button:hover:not(:disabled){filter:brightness(1.08)}
button:active:not(:disabled){transform:translateY(1px)}
button.ghost{background:var(--ghost-bg);border:1px solid var(--line2);color:var(--dim);height:28px}
button.ghost:hover:not(:disabled){color:var(--acc);border-color:var(--acc)}
button:disabled{opacity:.4;cursor:default}
.err{color:var(--err);font-size:var(--fs-13);min-height:18px}
.dim{color:var(--dim)}.count{color:var(--dim);font-size:var(--fs-12)}
.num{font-variant-numeric:tabular-nums}
.mono{font-family:"Cascadia Code",Consolas,monospace}
.ico{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.7;flex:none}

/* ===== 3. 登录(左右分屏: 品牌深色面板 + 白色表单) ===== */
#loginBox{position:fixed;inset:0;display:grid;grid-template-columns:minmax(320px,36%) 1fr;z-index:5}
.login-brand{background:#071927;position:relative;overflow:hidden;display:flex;flex-direction:column;
  justify-content:center;padding:64px;color:#fff}
.login-brand::before{content:'';position:absolute;inset:0;
  background-image:radial-gradient(rgba(255,255,255,.09) 1px,transparent 1px);background-size:28px 28px}
.login-brand>*{position:relative}
.login-brand .mark{width:48px;height:48px;border-radius:12px;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,#00B173,#00C48C);color:#fff;font-weight:700;font-size:20px}
.login-brand h2{margin:22px 0 10px;font-size:26px;font-weight:700}
.login-brand .sub{color:rgba(255,255,255,.55);font-size:var(--fs-14)}
.login-form{background:var(--panel);display:flex;flex-direction:column;justify-content:center;align-items:center;padding:32px}
.login-form .box{width:340px;display:grid;gap:14px}
.login-form h3{margin:0;font-size:22px;font-weight:700;color:var(--fg-head)}
.login-form .sub{color:var(--dim);font-size:var(--fs-13);margin-top:-8px}
.login-form label{display:flex;flex-direction:column;gap:6px;font-size:var(--fs-12);color:var(--dim)}
.login-form input{width:100%;height:40px;padding:8px 12px}
/* 浏览器自动填充(全局, 含弹窗密码框): 内阴影铺底色, 避免直角色块破坏输入框视觉 */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px var(--field-bg) inset;
  -webkit-text-fill-color: var(--fg);
  caret-color: var(--fg);
  transition: background-color 99999s ease-out;
}

/* 顶栏主题切换按钮(随外壳明暗) */
.themebtn{background:transparent;border-color:var(--gpill-border);color:var(--hdr-right-fg);padding:0 10px}
.themebtn:hover:not(:disabled){color:var(--page-title);border-color:var(--acc);filter:none}
.themebtn .ico{width:14px;height:14px}
.login-form .btns{display:flex;gap:10px;margin-top:4px}
.login-form .btns button{flex:1;height:42px;font-size:var(--fs-14)}
.login-form #btnLogin{background:linear-gradient(90deg,#00B173,#00C48C);box-shadow:0 6px 16px rgba(0,177,115,.3)}
.login-form #btnDev{background:var(--ghost-bg);color:var(--dim);border:1px solid var(--line2)}
@media (max-width:760px){#loginBox{grid-template-columns:1fr}.login-brand{display:none}}

/* ===== 4. 框架(外壳随主题: 浅=白侧栏/浅画布, 暗=深海军蓝) ===== */
#app{display:none;height:100vh}
aside{width:var(--side-w);flex:none;background:var(--bg2);border-right:1px solid var(--side-line);
  display:flex;flex-direction:column}
.logo{padding:14px 18px;display:flex;gap:10px;align-items:center;height:var(--top-h);
  border-bottom:1px solid var(--side-line);flex:none}
.logo .mark{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,#00B173,#00C48C);color:#fff;font-weight:700;font-size:14px}
.logo b{font-size:15px;display:block;line-height:1.2;color:var(--brand-name)}
.logo span{font-size:var(--fs-11);color:var(--brand-sub)}
nav{padding:10px;display:flex;flex-direction:column;gap:2px;flex:1;overflow:auto}
nav a{color:var(--nav-fg);text-decoration:none;padding:9px 12px;border-radius:var(--r-sm);
  font-size:var(--fs-13);cursor:pointer;display:flex;align-items:center;gap:10px;
  border-left:2px solid transparent;transition:color .12s,background .12s}
nav a:hover{color:var(--nav-hover-fg);background:var(--nav-hover-bg)}
nav a.active{color:var(--nav-active);background:rgba(0,177,115,.12);border-left-color:var(--acc)}
nav a .led{width:7px;height:7px;border-radius:50%;background:var(--nav-led);margin-left:auto}
nav a .led.ok{background:#00C48C;box-shadow:0 0 6px rgba(0,196,140,.6)}
nav a .led.err{background:var(--err)}nav a .led.warn{background:var(--warn)}
.side-foot{padding:14px 18px;border-top:1px solid var(--side-line);font-size:var(--fs-12);
  color:var(--side-foot-fg);display:flex;flex-direction:column;gap:8px}
.side-foot .ghost{background:transparent;border-color:var(--gpill-border);color:var(--side-foot-fg)}
#main{flex:1;display:flex;flex-direction:column;min-width:0}
header.top{display:flex;align-items:center;gap:14px;padding:0 24px;height:var(--top-h);flex:none;
  border-bottom:1px solid var(--side-line)}
header.top h1{font-size:20px;margin:0;font-weight:700;color:var(--page-title)}
header.top .desc{font-size:var(--fs-13);color:var(--page-desc)}
.gpill{display:inline-flex;align-items:center;gap:7px;font-size:var(--fs-12);border:1px solid var(--gpill-border);
  border-radius:20px;padding:3px 12px;color:var(--gpill-fg)}
.gpill .dot{width:8px;height:8px;border-radius:50%;background:var(--nav-led)}
.gpill.ok{color:var(--gpill-ok);border-color:color-mix(in srgb,var(--gpill-ok) 40%,transparent)}
.gpill.ok .dot{background:var(--gpill-ok);box-shadow:0 0 8px var(--gpill-ok);animation:pulse 2.4s infinite}
.gpill.warn{color:var(--gpill-warn);border-color:color-mix(in srgb,var(--gpill-warn) 40%,transparent)}
.gpill.warn .dot{background:var(--gpill-warn)}
.gpill.err{color:var(--gpill-err);border-color:color-mix(in srgb,var(--gpill-err) 45%,transparent)}
.gpill.err .dot{background:var(--gpill-err)}
header.top .right{margin-left:auto;display:flex;gap:14px;align-items:center;font-size:var(--fs-12);color:var(--hdr-right-fg)}
main.content{flex:1;overflow:auto;padding:22px 24px 80px}
section.view{display:none;max-width:1320px;margin:0 auto;animation:fadein .18s ease}
section.view.active{display:block}

/* ===== 5. 组件 ===== */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:20px;margin-bottom:20px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:16px 18px;
  transition:border-color .12s}
.card:hover{border-color:var(--line2)}
.card .v{font-size:var(--fs-24);font-weight:700;letter-spacing:.02em;color:var(--fg-head)}
.card .v.good{color:var(--ok)}.card .v.bad{color:var(--err)}.card .v.mid{color:var(--warn)}
.card .k{font-size:var(--fs-12);color:var(--dim);margin-top:4px}
h2.sec{font-size:var(--fs-12);color:var(--sec-fg);margin:22px 0 12px;font-weight:600;
  text-transform:uppercase;letter-spacing:.08em;display:flex;align-items:center;gap:10px}
h2.sec .more{margin-left:auto;font-size:var(--fs-12);color:var(--acc);cursor:pointer;text-transform:none;letter-spacing:0}
h2.sec .more:hover{text-decoration:underline}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:20px}
@media (max-width:960px){.grid2{grid-template-columns:1fr}}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:16px}
table{width:100%;border-collapse:separate;border-spacing:0;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;font-size:var(--fs-13)}
th,td{padding:11px 14px;text-align:left;white-space:nowrap}
thead th{color:var(--fg-head);font-weight:700;font-size:var(--fs-12);
  background:var(--thead-bg);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:1}
th.sortable{cursor:pointer;user-select:none}
th.sortable:hover{color:var(--acc)}
th .arrow{font-size:9px;margin-left:3px;color:var(--acc)}
tbody td{border-bottom:1px solid var(--line)}
tbody tr:last-child td{border-bottom:none}
tbody tr{transition:background .1s}
tbody tr:hover{background:var(--panel2)}
tbody:empty::after{content:"暂无数据";display:block;text-align:center;color:var(--dim2);padding:28px;font-size:var(--fs-12)}
tr.loading td{color:var(--dim2);text-align:center;padding:24px;font-size:var(--fs-12);animation:pulse 1.2s infinite}
.pill{display:inline-block;border-radius:20px;padding:1px 10px;font-size:var(--fs-11);border:1px solid var(--line2);color:var(--dim)}
.pill.on{color:var(--ok);border-color:rgba(0,177,115,.35);background:var(--ok-bg)}
.pill.off{color:var(--dim2)}
.st-done,.st-success{color:var(--ok)} .st-pending,.st-sent{color:var(--warn)}
.st-timeout,.st-fail,.st-expired{color:var(--err)}
.toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:14px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:12px 16px}
.toolbar label{display:flex;align-items:center;gap:6px;font-size:var(--fs-12);color:var(--dim)}
.toolbar input,.toolbar select{padding:4px 9px;height:28px}
.toolbar input{width:150px}
.toolbar .count{margin-left:auto}
.pager{display:flex;gap:14px;align-items:center;margin-top:12px;font-size:var(--fs-12);color:var(--dim)}
.pager button{padding:0 14px;font-size:var(--fs-12)}
.banner{border:1px solid rgba(217,138,31,.35);background:var(--warn-bg);color:var(--warn);
  border-radius:var(--r);padding:10px 16px;margin-bottom:16px;font-size:var(--fs-13)}
.banner.crit{border-color:rgba(224,72,58,.4);background:var(--err-bg);color:var(--err)}
.copybtn{padding:0 10px;font-size:var(--fs-12);height:24px}

/* ===== 5b. 健康视图 ===== */
.dot{width:9px;height:9px;border-radius:50%;background:var(--dim2);flex:none}
.dot.ok{background:var(--acc);box-shadow:0 0 6px rgba(0,177,115,.5)}
.dot.err{background:var(--err)} .dot.warn{background:var(--warn)}
.hgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px;margin-bottom:16px}
.hbox{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:16px}
.hbox .t{display:flex;align-items:center;gap:10px;font-weight:600;font-size:var(--fs-14);color:var(--fg-head)}
.hbox .d{font-size:var(--fs-12);color:var(--dim);margin-top:8px;word-break:break-all;min-height:16px}
.hbox .addr{display:inline-block;font-family:"Cascadia Code",Consolas,monospace;font-size:var(--fs-11);
  color:var(--dim);background:var(--addr-bg);border:1px solid var(--line);border-radius:6px;
  padding:3px 8px;margin-top:8px;word-break:break-all}
.hbox .errmsg{font-size:var(--fs-11);color:var(--err);margin-top:6px;word-break:break-all}
.st-badge{margin-left:auto;font-size:var(--fs-11);font-weight:600;border-radius:20px;padding:2px 10px;flex:none}
.st-badge.ok{color:var(--ok);background:var(--ok-bg);border:1px solid rgba(0,177,115,.35)}
.st-badge.err{color:var(--err);background:var(--err-bg);border:1px solid rgba(224,72,58,.4)}
.st-badge.dim{color:var(--dim);border:1px solid var(--line2)}

/* ===== 6. 图表(纯 SVG, 颜色读 CSS 变量) ===== */
.charts{display:grid;grid-template-columns:2fr 1fr;gap:20px;margin-bottom:20px}
@media (max-width:960px){.charts{grid-template-columns:1fr}}
.chart .spark{width:100%;height:64px;display:block}
.chart .gaugebox{display:flex;align-items:center;gap:16px}
.chart .gmeta{display:flex;flex-direction:column;gap:2px}
.chart .gmeta b{font-size:var(--fs-24);font-weight:700;color:var(--fg-head)}
h2.sec .sec-note{margin-left:auto;font-size:var(--fs-11);color:var(--dim2);font-weight:400;
  text-transform:none;letter-spacing:0}

/* ===== 6b. 数据流拓扑(健康视图; 控制面专用色板, 暖色保留给异常语义) ===== */
:root{--flow-cmd:#3D6FC2}
html[data-theme='dark']{--flow-cmd:#6FA3EE}
.flowwrap{padding:18px 18px 13px;margin-bottom:16px}
#flowMap{width:100%;height:auto;display:block}
.flowlegend{display:flex;gap:20px;align-items:center;flex-wrap:wrap;font-size:var(--fs-12);
  color:var(--dim);margin-top:12px;border-top:1px solid var(--line);padding-top:11px}
.flowlegend .lg{display:inline-block;width:26px;height:0;border-top:2.5px solid var(--acc);
  opacity:.75;margin-right:7px;vertical-align:middle;border-radius:2px}
.flowlegend .lg.cmd{border-top-style:dashed;border-top-color:var(--flow-cmd);opacity:.9}
.flowlegend .lg.warn{border-top-color:var(--warn);opacity:.9}
.flowlegend .lg.err{border-top-color:var(--err);opacity:.95}
.flowlegend .flowmeta{margin-left:auto;color:var(--dim2)}
@media (prefers-reduced-motion:reduce){#flowMap animate,#flowMap animateMotion{display:none}}

/* ===== 7. 弹窗 ===== */
#modal{position:fixed;inset:0;background:rgba(7,25,39,.55);z-index:10;align-items:center;justify-content:center;
  backdrop-filter:blur(3px)}
.modal-box{width:420px;max-width:92vw;max-height:86vh;overflow:auto;border-radius:var(--r-lg);
  animation:pop .16s ease;color:var(--fg)}
.modal-box label{display:flex;flex-direction:column;gap:4px;font-size:var(--fs-12);color:var(--dim);margin-bottom:8px}
#modalTitle{color:var(--fg-head)}

/* ===== 8. 动效 ===== */
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.45}}
@keyframes fadein{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
@keyframes pop{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:none}}
