Files
Agent-System-Frontend/src/styles.css
T
weichen 413e6ff15c Add login page, forced password change, and admin user management
Why: accounts are admin-issued (user decision 2026-09-30, no self
registration); initial and admin-reset passwords must force a change on
first login.

Evidence:
- LoginPage: password (primary) + API key tabs; ChangePasswordGate
  blocks the app until the forced change completes (mismatch/length
  validation inline); App auth state machine checking -> login ->
  mustchange -> ok, session cookie flows through fetch automatically.
- Account menu gains change-password and admin-only user management;
  AdminUsersModal: create account (random initial password generator,
  admin checkbox), reset password, status table (ADMIN/USER, pending
  change chips).
- Fixed a real bug found in the headless walk: menu toggles passed
  e.currentTarget into the setState updater where React nulls it by
  render time, so anchor was null and the menu never rendered; capture
  the element before the updater.
- Headless journey on the real binary with --auth on: admin bootstrap
  via CLI -> first-login forced change -> app; admin creates bob (bob
  sees no user management); alice reset via CLI -> forced change on
  next login; API-key tab login; logout; zero console errors. npm run
  build + vitest green; make test full suite exit 0.

Impact: replaces the old API-key-only LoginGate; embedded no-auth mode
unchanged (no login page shown).

Refs: proposal 2026-09-30-ui-redesign-and-auth.md section 5 (R2,
admin-issued variant)
2026-09-30 11:14:37 +00:00

324 lines
27 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 设计令牌与 UI 组件样式已迁入 Agent-System-UI(@ui/tokens.css、@ui/ui.css,main.tsx 引入)。 */
* { box-sizing: border-box; }
html, body, #root { height: 100%; margin: 0; }
body { font-family: var(--font); background: var(--bg); color: var(--text); font-size: 14px; }
button { font-family: inherit; }
.app { display: flex; height: 100vh; overflow: hidden; }
/* ---- 三栏布局 ---- */
.left-nav { background: var(--bg-panel); border-right: 1px solid var(--border); display: flex; flex-direction: column; min-width: 0; flex-shrink: 0; transition: width .08s ease; }
.left-nav.collapsed { width: 52px !important; }
.divider { width: 4px; cursor: col-resize; background: transparent; flex-shrink: 0; position: relative; }
.divider:hover, .divider.dragging { background: var(--accent); }
.divider::after { content: ''; position: absolute; inset: 0 -3px; } /* 拖拽热区加宽 */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--bg); }
.right-panel { background: var(--bg-panel); border-left: 1px solid var(--border); display: flex; flex-direction: column; flex-shrink: 0; min-width: 0; }
.right-panel.collapsed { width: 44px !important; }
/* ---- 左栏:任务中心(方案 A)见文末 tsb-* ---- */
/* ---- 中栏对话 ---- */
/* min-height:0 须随列向 flex 链下传(.main→.chat→.chat-body):flex 项的自动最小
高度是内容高度,缺失时消息会撑破 .chat——chat-body 永不滚动、输入栏被顶出视口。 */
.chat { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.chat-header { padding: 10px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px; background: var(--bg-raised); }
.chat-title { font-weight: 600; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-body { flex: 1; overflow-y: auto; min-height: 0; padding: 18px max(18px, 12%); }
.empty-state { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--text-faint); gap: 8px; }
.msg { margin-bottom: 18px; }
.msg-role { font-size: 12px; color: var(--text-faint); margin-bottom: 4px; display: flex; gap: 8px; align-items: center; }
.msg.user .bubble { background: var(--accent-soft); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; white-space: pre-wrap; }
.part { margin: 4px 0; }
.part-thinking { color: var(--text-dim); font-size: 12.5px; border-left: 2px solid var(--border); padding-left: 10px; margin: 4px 0; }
.part-thinking summary { cursor: pointer; color: var(--text-faint); user-select: none; }
.part-thinking .part-thinking-body { white-space: pre-wrap; margin-top: 4px; }
.part-tool { font-size: 12.5px; }
.tool-chip { display: inline-flex; gap: 6px; align-items: center; background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 4px 8px; margin: 2px 4px 2px 0; font-family: var(--mono); }
.tool-chip .status-ok { color: var(--ok); }
.tool-chip .status-error { color: var(--err); }
.part-text { line-height: 1.65; overflow-wrap: break-word; }
/* 块级间距只由标签自身控制:禁用 pre-wrap(marked 输出的块间换行会被渲染成空行,
叠加出"列表逐项空一行"的松散排版);行内换行语义交给 Markdown 段落。 */
.part-text > *:first-child { margin-top: 0; }
.part-text > *:last-child { margin-bottom: 0; }
.part-text p { margin: 0.5em 0; }
.part-text :is(h1,h2,h3,h4) { margin: 0.8em 0 0.35em; line-height: 1.35; font-weight: 600; }
.part-text h1 { font-size: 1.3em; }
.part-text h2 { font-size: 1.18em; }
.part-text h3 { font-size: 1.06em; }
.part-text :is(ul,ol) { margin: 0.4em 0; padding-left: 1.6em; }
.part-text li { margin: 0.15em 0; }
.part-text li > p { margin: 0.2em 0; }
.part-text pre { background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px; overflow-x: auto; font-family: var(--mono); font-size: 12.5px; }
.part-text code { font-family: var(--mono); background: var(--bg-raised); border-radius: 4px; padding: 1px 5px; font-size: 12.5px; }
.part-text pre code { background: none; padding: 0; }
.part-text blockquote { margin: 0.5em 0; padding: 0.1em 0 0.1em 10px; border-left: 3px solid var(--border); color: var(--text-dim); }
.part-text table { border-collapse: collapse; margin: 0.6em 0; font-size: 0.95em; display: block; max-width: 100%; overflow-x: auto; }
.part-text th, .part-text td { border: 1px solid var(--border); padding: 4px 10px; text-align: left; }
.part-text th { background: var(--bg-raised); font-weight: 600; }
.part-text hr { border: none; border-top: 1px solid var(--border); margin: 0.8em 0; }
.part-text a { color: var(--accent); }
.part-error { color: var(--err); background: color-mix(in srgb, var(--err) 8%, transparent); border: 1px solid var(--err); border-radius: var(--radius-sm); padding: 8px 12px; }
.artifact-chip { display: inline-flex; align-items: center; gap: 6px; color: var(--accent); background: var(--accent-soft); border-radius: var(--radius-sm); padding: 4px 10px; cursor: pointer; font-size: 13px; }
.artifact-chip:hover { filter: brightness(1.15); }
/* 过程折叠(run 终态后) */
.process-collapse { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-raised); margin: 6px 0; }
.process-collapse summary { list-style: none; cursor: pointer; padding: 6px 12px; color: var(--text-faint); font-size: 12.5px; display: flex; align-items: center; gap: 8px; user-select: none; }
.process-collapse summary::before { content: '▸'; transition: transform .12s; font-size: 10px; }
.process-collapse[open] summary::before { transform: rotate(90deg); }
.process-collapse .body { padding: 4px 12px 10px; }
/* 流式状态行 */
.live-bar { display: flex; align-items: center; gap: 8px; color: var(--text-faint); font-size: 12.5px; margin-bottom: 6px; }
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); animation: pulse 1.2s infinite; }
@keyframes pulse { 0%,100% { opacity: .4 } 50% { opacity: 1 } }
.cursor-blink::after { content: '▍'; animation: pulse 1s infinite; color: var(--accent); }
/* HITL 卡片 */
.hitl-card { border: 1px solid var(--accent); background: var(--bg-raised); border-radius: var(--radius); padding: 14px; margin: 10px 0; }
.hitl-q { font-weight: 600; margin-bottom: 10px; }
.hitl-choices { display: flex; flex-direction: column; gap: 8px; }
.hitl-choice { text-align: left; border: 1px solid var(--border); background: var(--bg); color: var(--text); border-radius: var(--radius-sm); padding: 10px 12px; cursor: pointer; }
.hitl-choice:hover { border-color: var(--accent); background: var(--accent-soft); }
.hitl-choice .label { font-weight: 600; display: flex; gap: 8px; align-items: center; }
.hitl-choice .desc { color: var(--text-dim); font-size: 12.5px; margin-top: 3px; }
.hitl-choice .impact { color: var(--warn); font-size: 12px; margin-top: 3px; }
.hitl-choice .rec { font-size: 10.5px; color: var(--accent); border: 1px solid var(--accent); padding: 0 5px; border-radius: 8px; }
.hitl-free { display: flex; gap: 8px; margin-top: 10px; }
/* 输入区 */
.input-bar { border-top: 1px solid var(--border); padding: 12px 18px; background: var(--bg-raised); }
.attachments { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.attach-chip { display: inline-flex; gap: 6px; align-items: center; background: var(--bg-hover); border-radius: var(--radius-sm); padding: 3px 8px; font-size: 12px; }
.input-row { display: flex; gap: 8px; align-items: flex-end; }
.input-row textarea { flex: 1; resize: none; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); padding: 10px 12px; font-family: inherit; font-size: 14px; max-height: 160px; outline: none; }
.input-row textarea:focus { border-color: var(--accent); }
.btn { background: var(--accent); color: #fff; border: none; border-radius: var(--radius-sm); padding: 9px 16px; cursor: pointer; font-size: 13px; white-space: nowrap; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn.ghost { background: none; border: 1px solid var(--border); color: var(--text-dim); }
.btn.ghost:hover { color: var(--text); border-color: var(--text-faint); }
.btn.danger { background: var(--err); }
.icon-btn { background: none; border: 1px solid var(--border); color: var(--text-dim); border-radius: var(--radius-sm); padding: 8px 10px; cursor: pointer; font-size: 14px; }
.icon-btn:hover { color: var(--text); border-color: var(--text-faint); }
.cmd-menu { border: 1px solid var(--border); background: var(--bg-raised); border-radius: var(--radius-sm); margin-bottom: 6px; max-height: 180px; overflow-y: auto; }
.cmd-item { padding: 7px 12px; cursor: pointer; display: flex; gap: 10px; font-size: 13px; }
.cmd-item:hover, .cmd-item.active { background: var(--accent-soft); }
.cmd-item .name { color: var(--accent); font-family: var(--mono); }
/* ---- 右栏 ---- */
.tabs { display: flex; border-bottom: 1px solid var(--border); overflow-x: auto; }
.tab { background: none; border: none; color: var(--text-dim); padding: 9px 12px; cursor: pointer; font-size: 12.5px; border-bottom: 2px solid transparent; white-space: nowrap; }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-body { flex: 1; overflow-y: auto; padding: 12px; }
.step { display: flex; gap: 8px; align-items: flex-start; padding: 6px 4px; font-size: 13px; }
.step-icon { width: 16px; text-align: center; flex-shrink: 0; }
.step.pending .step-icon { color: var(--text-faint); }
.step.in_progress .step-icon { color: var(--accent); }
.step.done .step-icon { color: var(--ok); }
.step.skipped { color: var(--text-faint); text-decoration: line-through; }
.artifact-row { display: flex; align-items: center; gap: 8px; padding: 8px; border-radius: var(--radius-sm); cursor: pointer; }
.artifact-row:hover { background: var(--bg-hover); }
.artifact-row .name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.artifact-row .kind { font-size: 11px; color: var(--text-faint); }
.agent-row { display: flex; align-items: center; gap: 8px; padding: 8px; border-radius: var(--radius-sm); cursor: pointer; font-size: 13px; }
.agent-row:hover { background: var(--bg-hover); }
.status-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.status-running { background: var(--accent); animation: pulse 1.2s infinite; }
.status-completed { background: var(--ok); }
.status-failed, .status-cancelled, .status-interrupted { background: var(--err); }
.status-pending, .status-waiting_hitl { background: var(--warn); }
/* ---- 弹窗 / 模态 ---- */
.overlay { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; z-index: 50; }
.modal { background: var(--bg-raised); border: 1px solid var(--border); border-radius: 12px; width: min(860px, 92vw); max-height: 86vh; display: flex; flex-direction: column; box-shadow: 0 18px 60px rgba(0,0,0,.4); }
.modal-header { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; font-weight: 600; }
.modal-header .spacer { flex: 1; }
.modal-body { flex: 1; overflow-y: auto; padding: 16px 18px; }
.subagent-tree { font-size: 13px; }
.subagent-node { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px; margin-bottom: 8px; }
.subagent-node .head { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
/* ---- 设置(schema 驱动)---- */
.settings-group { margin-bottom: 18px; }
.settings-group h4 { margin: 0 0 8px; font-size: 13px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em; }
.setting-row { display: grid; grid-template-columns: 200px 1fr auto; gap: 10px; align-items: center; padding: 7px 0; border-bottom: 1px dashed var(--border); font-size: 13px; }
.setting-row label { color: var(--text-dim); }
.setting-row input, .setting-row select { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text); padding: 7px 10px; font-family: inherit; }
.effect-badge { font-size: 10.5px; padding: 2px 7px; border-radius: 8px; border: 1px solid var(--border); color: var(--text-faint); white-space: nowrap; }
.effect-hot { color: var(--ok); border-color: var(--ok); }
.effect-new-run { color: var(--warn); border-color: var(--warn); }
/* UI schema 渲染器样式(.ui-*)与 .doc-viewer 已迁入 @ui/ui.css。 */
/* ---- 杂项 ---- */
.toast { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); background: var(--bg-raised); border: 1px solid var(--border); color: var(--text); padding: 9px 16px; border-radius: 20px; box-shadow: 0 8px 30px rgba(0,0,0,.35); z-index: 90; font-size: 13px; }
.conn-dot { width: 8px; height: 8px; border-radius: 50%; }
.conn-dot.on { background: var(--ok); }
.conn-dot.off { background: var(--err); }
.doc-toolbar { display: flex; gap: 8px; margin-bottom: 10px; align-items: center; }
.search-input { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text); padding: 6px 10px; font-family: inherit; width: 100%; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb { background: var(--bg-active); border-radius: 5px; }
::-webkit-scrollbar-track { background: transparent; }
/* ---- 登录门(API key 认证,PG 档默认开启) ---- */
.login-gate {
position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
background: var(--bg, #14161a); z-index: 1000;
}
.login-card {
width: 360px; padding: 28px; border-radius: 12px; text-align: center;
background: #1d2026; border: 1px solid #2c313a; box-shadow: 0 8px 32px rgba(0,0,0,.45);
}
.login-card h2 { margin: 0 0 8px; }
.login-hint { font-size: 12px; color: #9aa3b2; margin-bottom: 16px; line-height: 1.6; }
.login-card input {
width: 100%; box-sizing: border-box; padding: 9px 12px; border-radius: 8px;
border: 1px solid #2c313a; background: #14161a; color: inherit; font-size: 14px;
}
.login-card button {
margin-top: 12px; width: 100%; padding: 9px 0; border: none; border-radius: 8px;
background: #3b82f6; color: #fff; font-size: 14px; cursor: pointer;
}
.login-err { margin-top: 10px; font-size: 12px; color: #f87171; }
/* 工作区快捷区(T4/D26 左栏)与当前任务常驻卡(D25 R4) */
.current-task{border:1px solid var(--border);border-radius:8px;padding:8px 10px;margin-bottom:8px;background:rgba(120,160,255,.05)}
.ct-head{display:flex;align-items:center;gap:8px;font-weight:600;margin-bottom:6px}
.ct-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ct-progress{font-size:11px;color:var(--text-faint)}
.project-badge{font-size:11px;padding:1px 8px;border-radius:8px;background:rgba(120,160,255,.15);color:var(--accent);white-space:nowrap}
.tab-hint{font-size:10px;color:var(--text-faint);align-self:center}
.empty-note{color:var(--text-faint);font-size:12px;padding:12px 4px}
/* 收纳策略(T5):收起态角标 + 收纳按钮 */
.rail-badge{position:absolute;top:-4px;right:-6px;font-size:10px;background:var(--warn,#d29922);color:#fff;border-radius:8px;padding:0 5px;min-width:14px}
.right-panel.collapsed .icon-only button{position:relative}
.tab-action{font-size:11px;color:var(--text-faint)}
/* ---- 左栏:任务中心(方案 A,提案 2026-09-30) ---- */
.tsb-brand { display: flex; align-items: center; gap: 8px; padding: 12px 12px 6px; }
.tsb-logo { width: 22px; height: 22px; border-radius: 6px; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex: none; }
.tsb-name { font-weight: 600; font-size: 13.5px; }
.tsb-iconbtn { width: 26px; height: 26px; border-radius: var(--radius-sm); background: none; border: none; color: var(--text-faint); cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; position: relative; }
.tsb-iconbtn:hover { background: var(--bg-hover); color: var(--text); }
.tsb-iconbtn.active { color: var(--accent); background: var(--accent-soft); }
.tsb-actions { padding: 4px 10px 8px; display: flex; flex-direction: column; gap: 6px; }
.tsb-newtask { display: flex; height: 34px; border-radius: var(--radius-sm); border: 1px solid var(--accent); background: var(--accent-soft); overflow: hidden; }
.tsb-newtask-main { flex: 1; display: flex; align-items: center; gap: 6px; padding: 0 10px; background: none; border: none; color: var(--accent); font-weight: 600; font-size: 13px; cursor: pointer; min-width: 0; }
.tsb-newtask-main:hover { filter: brightness(1.08); }
.tsb-newtask-proj { font-weight: 400; font-size: 11.5px; opacity: .8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tsb-newtask-caret { width: 28px; border: none; border-left: 1px solid var(--accent); background: none; color: var(--accent); cursor: pointer; font-size: 10px; }
.tsb-newtask-caret:hover { background: var(--accent-soft); }
.tsb-search { height: 30px; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--bg-raised); color: var(--text); padding: 0 10px; font-size: 12.5px; width: 100%; }
.tsb-search:focus { border-color: var(--accent); outline: none; }
.tsb-search::placeholder { color: var(--text-faint); }
.tsb-orgrow { display: flex; align-items: center; gap: 6px; padding: 0 10px 8px; }
.tsb-pills { flex: 1; display: flex; background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 2px; gap: 2px; }
.tsb-pill { flex: 1; height: 22px; border: none; border-radius: 4px; background: none; color: var(--text-faint); font-size: 12px; cursor: pointer; }
.tsb-pill:hover { color: var(--text-dim); }
.tsb-pill.on { background: var(--bg-active); color: var(--text); font-weight: 500; }
.tsb-scroll { flex: 1; overflow-y: auto; padding: 0 8px 8px; min-height: 0; }
.tsb-seclabel { font-size: 11px; color: var(--text-faint); padding: 10px 8px 4px; letter-spacing: .3px; }
.tsb-empty { padding: 18px 12px; color: var(--text-faint); font-size: 12.5px; line-height: 1.8; text-align: center; }
.tsb-newproject { padding: 0 2px 8px; }
.tsb-group { margin-bottom: 2px; }
.tsb-group-head { display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 6px 0 8px; border-radius: var(--radius-sm); color: var(--text-dim); font-weight: 500; font-size: 13px; cursor: pointer; user-select: none; }
.tsb-group-head:hover { background: var(--bg-hover); }
.tsb-group.closed .tsb-chev { transform: rotate(-90deg); }
.tsb-chev { font-size: 9px; color: var(--text-faint); transition: transform .12s; }
.tsb-group-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tsb-group-cnt { font-size: 11px; color: var(--text-faint); font-weight: 400; }
.tsb-group-add { visibility: hidden; width: 20px; height: 20px; border: none; border-radius: 4px; background: none; color: var(--text-faint); font-size: 12px; cursor: pointer; }
.tsb-group-head:hover .tsb-group-add { visibility: visible; }
.tsb-group-add:hover { background: var(--bg-active); color: var(--text); }
.tsb-task { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 8px; margin: 1px 0; border-radius: var(--radius-sm); color: var(--text-dim); cursor: pointer; }
.tsb-task:hover { background: var(--bg-hover); color: var(--text); }
.tsb-task.active { background: var(--bg-active); color: var(--text); }
.tsb-task.archived .tsb-task-t { text-decoration: line-through; opacity: .6; }
.tsb-task-t { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
.tsb-task-tm { font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.tsb-pbadge { font-size: 10.5px; color: var(--text-faint); border: 1px solid var(--border); border-radius: 4px; padding: 0 5px; line-height: 1.5; white-space: nowrap; }
.tsb-row-acts { display: none; gap: 2px; }
.tsb-task:hover .tsb-row-acts { display: inline-flex; }
.tsb-task:hover .tsb-task-tm { display: none; }
.tsb-row-act { border: none; background: none; color: var(--text-faint); font-size: 11px; cursor: pointer; padding: 2px 3px; border-radius: 4px; }
.tsb-row-act:hover { background: var(--bg-active); color: var(--text); }
.tsb-wsstrip { display: flex; align-items: center; gap: 2px; padding: 6px 10px; border-top: 1px solid var(--border); }
.tsb-wsstrip button { border: none; background: none; color: var(--text-faint); font-size: 13px; cursor: pointer; padding: 4px 5px; border-radius: var(--radius-sm); }
.tsb-wsstrip button:hover { background: var(--bg-hover); color: var(--text); }
.tsb-footer { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-top: 1px solid var(--border); }
.tsb-user { flex: 1; display: flex; align-items: center; gap: 8px; min-width: 0; height: 38px; padding: 0 7px; border: none; border-radius: var(--radius-sm); background: none; cursor: pointer; text-align: left; }
.tsb-user:hover { background: var(--bg-hover); }
.tsb-avatar { width: 26px; height: 26px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 600; display: flex; align-items: center; justify-content: center; flex: none; }
.tsb-user-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.tsb-user-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tsb-user-sub { font-size: 11px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tsb-user-chev { color: var(--text-faint); font-size: 10px; }
.tsb-menu { position: fixed; z-index: 300; min-width: 210px; background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 12px 32px rgba(0,0,0,.35); padding: 5px; display: flex; flex-direction: column; }
.tsb-mi { display: flex; align-items: center; gap: 8px; min-height: 30px; padding: 4px 9px; border: none; border-radius: var(--radius-sm); background: none; color: var(--text-dim); font-size: 13px; cursor: pointer; text-align: left; }
.tsb-mi:hover { background: var(--bg-hover); color: var(--text); }
.tsb-mi.danger { color: var(--err); }
.tsb-mi.danger:hover { background: var(--err); color: #fff; }
.tsb-mi kbd { margin-left: auto; font-size: 10.5px; color: var(--text-faint); }
.tsb-menu-div { height: 1px; background: var(--border); margin: 5px 4px; }
.tsb-acc-head { display: flex; gap: 10px; align-items: center; padding: 8px 9px 6px; }
.tsb-acc-name { font-size: 13.5px; font-weight: 600; }
.tsb-acc-mail { font-size: 11.5px; color: var(--text-faint); }
/* ---- 登录页与改密门(R2 管理员发放制) ---- */
.lg-wrap { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--bg); z-index: 500; }
.lg-card { width: 380px; max-width: calc(100vw - 32px); background: var(--bg-panel); border: 1px solid var(--border); border-radius: 12px; padding: 28px 28px 22px; box-shadow: 0 12px 40px rgba(0,0,0,.25); }
.lg-brand { display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 15px; margin-bottom: 18px; }
.lg-logo { width: 26px; height: 26px; border-radius: 8px; background: var(--accent); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 13px; }
.lg-title { margin: 0 0 4px; font-size: 19px; }
.lg-sub { margin: 0 0 16px; font-size: 12.5px; color: var(--text-faint); line-height: 1.6; }
.lg-tabs { display: flex; background: var(--bg-raised); border: 1px solid var(--border); border-radius: 8px; padding: 3px; gap: 3px; margin-bottom: 14px; }
.lg-tab { flex: 1; height: 28px; border: none; border-radius: 6px; background: none; color: var(--text-faint); font-size: 12.5px; cursor: pointer; }
.lg-tab.on { background: var(--bg-active); color: var(--text); font-weight: 500; }
.lg-label { display: block; font-size: 12px; font-weight: 500; color: var(--text-dim); margin: 10px 0 5px; }
.lg-input { width: 100%; height: 36px; border-radius: 8px; border: 1px solid var(--border); background: var(--bg-raised); color: var(--text); padding: 0 11px; font-size: 13.5px; }
.lg-input:focus { border-color: var(--accent); outline: none; }
.lg-mono { font-family: var(--mono); font-size: 12.5px; }
.lg-err { margin-top: 10px; font-size: 12.5px; color: var(--err); background: rgba(224,86,107,.08); border: 1px solid rgba(224,86,107,.3); border-radius: 8px; padding: 7px 10px; }
.lg-submit { width: 100%; height: 36px; margin-top: 14px; border: none; border-radius: 8px; background: var(--accent); color: #fff; font-weight: 600; font-size: 13.5px; cursor: pointer; }
.lg-submit:disabled { opacity: .55; cursor: default; }
.lg-submit:not(:disabled):hover { filter: brightness(1.08); }
.lg-cancel { width: 100%; margin-top: 8px; border: none; background: none; color: var(--text-faint); font-size: 12.5px; cursor: pointer; }
.lg-cancel:hover { color: var(--text-dim); text-decoration: underline; }
.lg-hint { margin: 12px 0 0; font-size: 11.5px; color: var(--text-faint); line-height: 1.7; }
.lg-hint code { font-family: var(--mono); font-size: 10.5px; background: var(--bg-raised); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; }
/* ---- 用户管理 / 改密弹层 ---- */
.ad-body { padding: 14px 18px 18px; display: flex; flex-direction: column; gap: 12px; max-height: 70vh; overflow-y: auto; }
.ad-create { border: 1px solid var(--border); border-radius: 10px; padding: 12px; display: flex; flex-direction: column; gap: 8px; background: var(--bg-raised); }
.ad-row { display: flex; gap: 8px; align-items: center; }
.ad-row .lg-input { flex: 1; min-width: 0; height: 32px; }
.ad-check { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--text-dim); white-space: nowrap; }
.ad-create-btn { width: auto; padding: 0 14px; margin-top: 0; height: 32px; flex: none; }
.ad-mini { border: 1px solid var(--border); background: var(--bg-raised); color: var(--text-dim); border-radius: 6px; padding: 4px 9px; font-size: 12px; cursor: pointer; white-space: nowrap; }
.ad-mini:hover { color: var(--text); border-color: var(--accent); }
.ad-hint { margin: 0; font-size: 11.5px; color: var(--text-faint); }
.ad-notice { font-size: 12.5px; color: var(--ok); background: rgba(65,178,110,.09); border: 1px solid rgba(65,178,110,.3); border-radius: 8px; padding: 8px 11px; }
.ad-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.ad-table th { text-align: left; font-weight: 500; color: var(--text-faint); font-size: 11.5px; padding: 6px 8px; border-bottom: 1px solid var(--border); }
.ad-table td { padding: 7px 8px; border-bottom: 1px solid var(--border); }
.ad-mono { font-family: var(--mono); font-size: 12px; }
.ad-dim { color: var(--text-faint); }
.ad-chip { font-size: 10px; border-radius: 4px; padding: 1px 6px; font-weight: 700; letter-spacing: .4px; background: var(--bg-hover); color: var(--text-faint); }
.ad-chip-admin { background: var(--accent-soft); color: var(--accent); }
.ad-chip-warn { background: rgba(217,155,63,.15); color: var(--warn); }
.ad-reset { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; border: 1px solid var(--warn); border-radius: 10px; padding: 12px; font-size: 12.5px; }
.ad-reset .lg-input { flex: 1; min-width: 160px; height: 32px; }
.ad-changepw { display: flex; flex-direction: column; gap: 2px; max-width: 340px; }