/* 实现工作项 #107 抽出全部 CSS 到 styles.css（从 index.html <style> 内块原样迁出，规则未改） */
/* ===== 设计令牌：亮色品牌（SPESIT 藏蓝 + 海青·v2） ===== */
:root{
  --bg:#EEF3FA; --bg-2:#F5F8FD; --bg-3:#FFFFFF;
  --surface:rgba(255,255,255,.75); --surface-2:rgba(255,255,255,.9); --surface-3:#FFFFFF;
  --border:rgba(39,39,110,.12); --border-2:rgba(39,39,110,.2);
  --glow:rgba(30,144,200,.3);
  --primary:#1E90C8; --cyan:#1E90C8; --cyan-2:#3FA9DA; --violet:#27276E; --indigo:#3A3A8C;
  --fg:#1B2147; --fg-2:#3A4170; --muted:#6B73A0; --dim:#9AA0C4;
  --danger:#E5476E; --ok:#16A874; --warn:#E0920E; --doing:#1E90C8;
  --c-todo:#8088B0; --c-doing:#1E90C8; --c-testing:#E0920E; --c-done:#16A874;
  --c-paused:#7A6BC4; --c-cancelled:#A0A6C0;
  --radius:14px; --radius-sm:10px;
  --shadow:0 10px 30px rgba(39,39,110,.1);
  --display:'Sora','Fira Sans',system-ui,sans-serif;
  --body:'Fira Sans',-apple-system,'PingFang SC','Microsoft YaHei',system-ui,sans-serif;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{font-family:var(--body);color:var(--fg);font-size:15px;line-height:1.55;background:var(--bg);
  background-image:
    radial-gradient(1200px 600px at 78% -8%,rgba(30,144,200,.1),transparent 60%),
    radial-gradient(900px 500px at 8% 102%,rgba(39,39,110,.07),transparent 55%),
    linear-gradient(180deg,#EEF3FA,#F2F6FC);
  background-attachment:fixed;min-height:100vh;-webkit-font-smoothing:antialiased}
.mono{font-family:'Fira Code',monospace}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
a{color:inherit;text-decoration:none}
::selection{background:rgba(30,144,200,.25)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:rgba(39,39,110,.18);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background:rgba(39,39,110,.3);background-clip:padding-box}
/* ===== 登录页 ===== */
#login{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:200;overflow:hidden;
  background:radial-gradient(1000px 700px at 70% 20%,#DCEAFB,transparent 60%),
             radial-gradient(900px 600px at 20% 90%,#E4EDFA,transparent 55%),
             linear-gradient(150deg,#EEF4FC 0%,#E3ECF8 55%,#DEEDF8 100%)}
#login::before{content:"";position:absolute;inset:0;opacity:.6;
  background-image:linear-gradient(rgba(39,39,110,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(39,39,110,.05) 1px,transparent 1px);
  background-size:46px 46px;mask-image:radial-gradient(circle at 50% 45%,#000,transparent 72%)}
.orb-bg{position:absolute;border-radius:50%;filter:blur(60px);opacity:.45;pointer-events:none}
.orb-bg.a{width:420px;height:420px;background:radial-gradient(circle,rgba(30,144,200,.5),transparent 70%);top:-120px;right:-80px;animation:float1 14s ease-in-out infinite}
.orb-bg.b{width:360px;height:360px;background:radial-gradient(circle,rgba(39,39,110,.35),transparent 70%);bottom:-120px;left:-60px;animation:float2 17s ease-in-out infinite}
@keyframes float1{0%,100%{transform:translate(0,0)}50%{transform:translate(-30px,40px)}}
@keyframes float2{0%,100%{transform:translate(0,0)}50%{transform:translate(40px,-30px)}}
.login-card{position:relative;z-index:1;width:380px;max-width:92vw;padding:42px 40px 36px;border-radius:22px;
  background:linear-gradient(180deg,rgba(255,255,255,.96),rgba(255,255,255,.88));
  border:1px solid rgba(39,39,110,.1);backdrop-filter:blur(20px) saturate(140%);
  box-shadow:0 30px 80px rgba(39,39,110,.18),inset 0 1px 0 rgba(255,255,255,.7);
  animation:cardIn .7s cubic-bezier(.16,1,.3,1)}
@keyframes cardIn{from{opacity:0;transform:translateY(24px) scale(.97)}to{opacity:1;transform:none}}
.login-card .logo-wrap{display:flex;justify-content:center;margin-bottom:6px;
  animation:fadeUp .7s .05s both}
.login-card .logo-wrap img{width:180px;max-width:62%;height:auto;object-fit:contain;
  filter:drop-shadow(0 4px 14px rgba(30,144,200,.22))}
.login-title{text-align:center;font-family:var(--display);font-weight:700;font-size:23px;letter-spacing:.04em;
  margin:14px 0 4px;color:var(--violet);animation:fadeUp .7s .12s both}
.login-title .sp{background:linear-gradient(120deg,var(--cyan),var(--cyan-2));-webkit-background-clip:text;background-clip:text;color:transparent}
.login-sub{text-align:center;color:var(--muted);font-size:13px;letter-spacing:.1em;margin-bottom:30px;animation:fadeUp .7s .18s both}
.field{margin-bottom:16px;animation:fadeUp .7s both}
.field:nth-of-type(1){animation-delay:.24s}.field:nth-of-type(2){animation-delay:.3s}
.field label{display:block;font-size:12px;font-weight:500;margin-bottom:7px;color:var(--fg-2);letter-spacing:.04em}
.field .inp{position:relative;display:flex;align-items:center}
.field .inp svg{position:absolute;left:13px;width:17px;height:17px;color:var(--dim);pointer-events:none}
.field input{width:100%;padding:12px 14px 12px 40px;border-radius:11px;font-size:14px;font-family:inherit;
  background:#F4F7FC;border:1px solid rgba(39,39,110,.16);color:var(--fg);
  transition:border .18s,box-shadow .18s,background .18s}
.field input::placeholder{color:var(--dim)}
.field input:focus{outline:none;border-color:var(--cyan);background:#fff;box-shadow:0 0 0 4px rgba(30,144,200,.14)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:11px 18px;border-radius:11px;
  font-size:14px;font-weight:600;transition:transform .08s,box-shadow .2s,background .2s,filter .2s;font-family:var(--body)}
.btn:active{transform:translateY(1px) scale(.99)}
.btn-primary{width:100%;color:#fff;font-weight:700;letter-spacing:.02em;margin-top:6px;animation:fadeUp .7s .36s both;
  background:linear-gradient(120deg,var(--cyan-2),var(--cyan) 55%,var(--primary));
  box-shadow:0 10px 26px rgba(30,144,200,.32),inset 0 1px 0 rgba(255,255,255,.4)}
.btn-primary:hover{filter:brightness(1.05);box-shadow:0 14px 34px rgba(30,144,200,.42),inset 0 1px 0 rgba(255,255,255,.5)}
.login-foot{margin-top:22px;text-align:center;font-size:12px;color:var(--dim);animation:fadeUp .7s .42s both}
.login-foot .tag{display:inline-flex;align-items:center;gap:6px;padding:4px 11px;border-radius:20px;
  background:rgba(30,144,200,.1);border:1px solid rgba(30,144,200,.22);color:var(--cyan)}
@keyframes fadeUp{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
/* 实现工作项 #122 登录 loading/禁用态：登录中禁用按钮+转圈，避免慢登录被当卡死；禁用态所有 .btn 通用 */
.btn:disabled,.btn.is-loading{opacity:.72;cursor:not-allowed;pointer-events:none}
.login-btn-spin{display:none;width:15px;height:15px;border-radius:50%;border:2px solid rgba(255,255,255,.4);border-top-color:#fff;animation:bootspin .7s linear infinite}
#login-btn.is-loading .login-btn-spin{display:inline-block}
#login-btn.is-loading .login-btn-arrow{display:none}

/* ===== 启动过渡态（防登录页闪烁）===== */
#boot-splash{position:fixed;inset:0;z-index:300;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:22px;
  background:radial-gradient(1000px 700px at 70% 20%,#DCEAFB,transparent 60%),
             radial-gradient(900px 600px at 20% 90%,#E4EDFA,transparent 55%),
             linear-gradient(150deg,#EEF4FC 0%,#E3ECF8 55%,#DEEDF8 100%)}
#boot-splash img{width:118px;height:auto;opacity:.96}
.boot-spinner{width:30px;height:30px;border-radius:50%;border:3px solid rgba(39,39,110,.15);border-top-color:rgba(30,144,200,.85);animation:bootspin .8s linear infinite}
@keyframes bootspin{to{transform:rotate(360deg)}}

/* ===== 主框架 ===== */
#app{display:none;min-height:100vh}
.sidebar{position:fixed;left:0;top:0;bottom:0;width:236px;z-index:60;display:flex;flex-direction:column;padding:20px 0 14px;
  background:linear-gradient(180deg,rgba(255,255,255,.95),rgba(245,248,253,.95));
  border-right:1px solid var(--border);backdrop-filter:blur(14px);box-shadow:2px 0 24px rgba(39,39,110,.05)}
.sidebar .brand{display:flex;align-items:center;gap:12px;padding:0 22px 20px;margin-bottom:14px;border-bottom:1px solid var(--border)}
.sidebar .brand .mark{width:38px;height:38px;object-fit:contain;flex-shrink:0;
  filter:drop-shadow(0 2px 6px rgba(39,39,110,.3));animation:markPulse 4s ease-in-out infinite}
@keyframes markPulse{0%,100%{filter:drop-shadow(0 2px 5px rgba(39,39,110,.22))}50%{filter:drop-shadow(0 2px 9px rgba(30,144,200,.45))}}
.sidebar .brand .bt{font-family:var(--display);font-weight:700;font-size:18px;letter-spacing:.04em;color:var(--violet);line-height:1.1}
.sidebar .brand .bt .sp{background:linear-gradient(120deg,var(--cyan),var(--cyan-2));-webkit-background-clip:text;background-clip:text;color:transparent}
.sidebar .brand .bs{font-size:11px;letter-spacing:.06em;color:var(--dim);margin-top:3px}
.nav-label{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);padding:6px 24px 8px}
.nav-item{position:relative;display:flex;align-items:center;gap:12px;margin:2px 12px;padding:11px 12px;border-radius:11px;
  font-size:14px;color:var(--fg-2);cursor:pointer;transition:background .16s,color .16s}
.nav-item svg{width:19px;height:19px;flex-shrink:0;color:var(--muted);transition:color .16s}
.nav-item:hover{background:rgba(30,144,200,.08);color:var(--violet)}
.nav-item:hover svg{color:var(--cyan)}
.nav-item.active{background:linear-gradient(100deg,rgba(30,144,200,.16),rgba(30,144,200,.04));color:var(--violet);font-weight:600}
.nav-item.active svg{color:var(--cyan)}
.nav-item.active::before{content:"";position:absolute;left:-12px;top:50%;transform:translateY(-50%);width:3px;height:22px;border-radius:3px;background:linear-gradient(180deg,var(--cyan),var(--violet));box-shadow:0 0 10px var(--glow)}
.nav-foot{margin-top:auto;padding:14px 16px 0;border-top:1px solid var(--border)}
.user-chip{display:flex;align-items:center;gap:11px;padding:10px;border-radius:12px;background:var(--surface);border:1px solid var(--border)}
.user-chip .av{width:36px;height:36px;border-radius:10px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  font-weight:700;font-size:14px;color:#fff;background:linear-gradient(135deg,var(--cyan),var(--violet))}
.user-chip .meta{min-width:0}
.user-chip .nm{font-size:13px;font-weight:600;color:var(--violet)}
.user-chip .rl{font-size:11px;color:var(--muted)}

.main{margin-left:236px;min-height:100vh}
.topbar{position:sticky;top:0;z-index:40;height:64px;display:flex;align-items:center;justify-content:space-between;
  padding:0 28px;background:rgba(255,255,255,.8);backdrop-filter:blur(14px);border-bottom:1px solid var(--border)}
.topbar .left{display:flex;align-items:center;gap:14px;min-width:0}
.hamburger{display:none;width:38px;height:38px;border-radius:10px;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--border);color:var(--fg-2)}
.hamburger svg{width:20px;height:20px}
.topbar h2{font-family:var(--display);font-size:18px;font-weight:600;color:var(--violet);display:flex;align-items:center;gap:11px;white-space:nowrap}
.topbar h2::before{content:"";width:4px;height:20px;border-radius:3px;background:linear-gradient(180deg,var(--cyan),var(--violet));box-shadow:0 0 10px var(--glow)}
.topbar .right{display:flex;align-items:center;gap:10px}
.icon-btn{width:38px;height:38px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--border-2);color:var(--fg-2);cursor:pointer;transition:.15s}
.icon-btn:hover{background:#fff;color:var(--violet);border-color:var(--cyan)}
.icon-btn svg{width:18px;height:18px}
/* 项目选择器：紧贴标题，用竖线分隔，样式与总览标题区调和 */
.proj-pick{display:flex;align-items:center;gap:9px;min-width:0;padding-left:14px;border-left:1px solid var(--border)}
.proj-pick select{max-width:100%;padding:7px 12px;border-radius:9px;font-family:inherit;font-size:13px;font-weight:500;color:var(--violet);
  background:var(--surface-2);border:1px solid var(--border-2);cursor:pointer;transition:border .15s,box-shadow .15s}
.proj-pick select:hover{border-color:var(--cyan)}
.proj-pick select:focus{outline:none;border-color:var(--cyan);box-shadow:0 0 0 3px var(--glow)}
.proj-pick select option{background:#fff;color:var(--fg)}
.content{padding:26px 28px 80px;max-width:1320px}
.view{display:none}
.view.active{display:block;animation:viewIn .45s cubic-bezier(.16,1,.3,1)}
@keyframes viewIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* ===== 卡片 / 玻璃面 ===== */
.glass{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);backdrop-filter:blur(8px);box-shadow:var(--shadow)}
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(228px,1fr));gap:16px}
.kpi{position:relative;padding:20px;overflow:hidden;transition:transform .2s,border-color .2s,box-shadow .2s}
.kpi:hover{transform:translateY(-3px);border-color:var(--border-2);box-shadow:0 14px 40px rgba(39,39,110,.14)}
.kpi::after{content:"";position:absolute;top:-30px;right:-30px;width:110px;height:110px;border-radius:50%;
  background:radial-gradient(circle,var(--kpiglow,rgba(30,144,200,.16)),transparent 70%)}
.kpi .ic{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;margin-bottom:14px;
  background:rgba(30,144,200,.1);border:1px solid rgba(30,144,200,.18);color:var(--cyan)}
.kpi .ic svg{width:19px;height:19px}
.kpi .label{font-size:12.5px;color:var(--muted);margin-bottom:6px;letter-spacing:.02em}
.kpi .num{font-family:var(--display);font-size:32px;font-weight:700;line-height:1;color:var(--violet)}
.kpi .num.grad{background:linear-gradient(120deg,var(--cyan),var(--violet));-webkit-background-clip:text;background-clip:text;color:transparent}
.kpi .sub{font-size:12px;color:var(--dim);margin-top:7px}
.section-title{font-family:var(--display);font-size:14px;font-weight:600;letter-spacing:.03em;color:var(--fg-2);
  margin:30px 0 14px;display:flex;align-items:center;gap:9px}
.section-title::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--cyan);box-shadow:0 0 8px var(--glow)}
.badge{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;font-size:11.5px;font-weight:600}
.badge::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}
.badge-todo{background:rgba(128,136,176,.16);color:#5A6390}
.badge-doing{background:rgba(30,144,200,.14);color:#176B9E}
.badge-testing{background:rgba(224,146,14,.16);color:#B5740A}
.badge-done{background:rgba(22,168,116,.14);color:#0F8159}
.badge-req{background:rgba(39,39,110,.12);color:#3A3A8C}
.badge-task{background:rgba(30,144,200,.12);color:#176B9E}
.badge-paused{background:rgba(122,107,196,.14);color:#5B4CA0}
.badge-cancelled{background:rgba(160,166,192,.18);color:#6E7396}
.badge::before{display:none}

/* ===== 表格 ===== */
.tbl-wrap{border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;background:var(--surface-3);box-shadow:var(--shadow)}
table{width:100%;border-collapse:collapse}
th{background:rgba(39,39,110,.04);text-align:left;padding:12px 16px;font-size:11px;font-weight:600;
  color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
td{padding:13px 16px;border-top:1px solid var(--border);font-size:14px;color:var(--fg-2)}
tbody tr{transition:background .14s}
tbody tr:hover{background:rgba(30,144,200,.04)}
td b{color:var(--violet);font-weight:600}
.id-tag{font-family:'Fira Code';font-size:12px;color:var(--cyan);font-weight:600}
.progress{height:7px;background:rgba(39,39,110,.08);border-radius:6px;overflow:hidden;display:flex}
.progress span{display:block;height:100%}
.role-tag{font-size:11.5px;padding:3px 10px;border-radius:7px;font-weight:600}
.role-pm{background:rgba(30,144,200,.16);color:#176B9E}
.role-member{background:rgba(39,39,110,.1);color:#5A6390}
.role-tester{background:rgba(214,158,46,.16);color:#B7791F}/* 实现工作项 #99：测试角色标签 */
.avatar{width:26px;height:26px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:700;color:var(--fg-2);background:#EAEEF6;border:1px solid rgba(39,39,110,.1);flex-shrink:0}
.avatar.v{background:rgba(39,39,110,.1);color:var(--violet);border-color:rgba(39,39,110,.16)}
.who-none{font-size:12px;color:var(--muted);opacity:.72;font-style:italic}
.member-row{display:flex;align-items:center;gap:10px;color:var(--violet);font-weight:500}
.member-sub{display:block;font-size:11.5px;color:var(--dim);font-weight:400;margin-top:1px}
.member-count{font-size:12px;color:var(--dim);font-weight:500;margin-left:10px}
.proj-card .ph-l{display:flex;align-items:center;gap:2px;flex-wrap:wrap}
.member-empty{padding:26px 18px;text-align:center;border:1px dashed var(--border);border-radius:var(--radius);background:var(--surface-3)}
.member-empty-tt{font-size:14px;font-weight:600;color:var(--fg-2);margin-bottom:5px}
.member-empty-sub{font-size:12.5px;color:var(--dim);line-height:1.5}
/* 指派弹窗：可多选的账号列表（复选框卡片） */
.picker{max-height:240px;overflow-y:auto;border:1px solid var(--border-2);border-radius:10px;background:#F4F7FC;padding:5px}
.form-field.invalid .picker{border-color:var(--danger);background:#FEF3F6;box-shadow:0 0 0 4px rgba(229,71,110,.12)}
.pick-row{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:8px;cursor:pointer;transition:background .12s}
.pick-row:hover{background:#E9F0FA}
.pick-row input{width:16px;height:16px;flex-shrink:0;margin:0;accent-color:var(--cyan);cursor:pointer}
.pick-row .avatar{flex-shrink:0}
.pick-nm{font-size:13.5px;color:var(--fg);font-weight:500}
.pick-un{font-size:11.5px;color:var(--dim);margin-left:4px}
.picker-empty{padding:22px 12px;text-align:center;font-size:12.5px;color:var(--dim)}
.pick-bar{display:flex;justify-content:space-between;align-items:center;margin-bottom:7px}
.pick-count{font-size:12px;color:var(--muted);font-weight:500}
.pick-all{font-size:12px;color:var(--cyan);cursor:pointer;user-select:none}
.pick-all:hover{opacity:.75}
/* 成员行内 PM 切换（仅管理员可见） */
.role-toggle{color:var(--cyan);font-size:13px;cursor:pointer;margin-right:12px;transition:opacity .15s}
.role-toggle:hover{opacity:.7}

/* ===== 看板 ===== */
.stat-bar{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-bottom:18px}
.stat-pill{padding:16px 18px;position:relative;overflow:hidden}
.stat-pill .n{font-family:var(--display);font-size:26px;font-weight:700;line-height:1}
.stat-pill .l{font-size:12px;color:var(--muted);margin-top:6px}
.stat-pill .progress{margin-top:10px}
.kanban{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
.kb-col{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px;min-height:340px;overflow:hidden}
.kb-col::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;background:var(--col,var(--cyan))}
.kb-col::after{content:"";position:absolute;left:0;right:0;top:0;height:90px;
  background:linear-gradient(180deg,var(--col-soft,rgba(30,144,200,.08)),transparent);pointer-events:none}
.kb-head{position:relative;z-index:1;display:flex;align-items:center;justify-content:space-between;padding:2px 4px 14px;font-weight:600;font-size:13.5px;color:var(--fg)}
.kb-head .dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;box-shadow:0 0 8px currentColor}
.kb-count{background:var(--surface-2);border:1px solid var(--border);border-radius:20px;padding:1px 10px;font-size:12px;color:var(--muted);font-family:'Fira Code'}
.kb-card{position:relative;z-index:1;background:#fff;border:1px solid var(--border);border-left:3px solid var(--col,var(--cyan));
  border-radius:9px;padding:13px;margin-bottom:11px;cursor:grab;transition:transform .14s,border-color .14s,box-shadow .14s;box-shadow:0 2px 8px rgba(39,39,110,.05)}
.kb-card:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(39,39,110,.12)}
.kb-card:active{cursor:grabbing}
.kb-card .t{font-size:13px;font-weight:500;color:var(--violet);margin-bottom:11px;line-height:1.45}
.kb-card .m{display:flex;align-items:center;justify-content:space-between;font-size:12px;color:var(--muted)}
.kb-card .who{display:flex;align-items:center;gap:6px}
.kb-card .who .avatar{width:22px;height:22px}
.kb-card .id-tag{color:var(--col,var(--cyan))}
.note{font-size:12px;color:var(--dim);margin:2px 2px 14px;line-height:1.5}

/* ===== 项目与成员 ===== */
.toolbar{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-bottom:18px;flex-wrap:wrap}
.toolbar .hint{font-size:13px;color:var(--muted);max-width:560px}
.btn-sm{padding:9px 15px;font-size:13px;border-radius:10px}
.btn-accent{color:#fff;font-weight:700;background:linear-gradient(120deg,var(--cyan-2),var(--primary));box-shadow:0 6px 18px rgba(30,144,200,.28)}
.btn-accent:hover{filter:brightness(1.05)}
.btn-ghost{background:var(--surface-2);border:1px solid var(--border-2);color:var(--fg-2)}
.btn-ghost:hover{background:#fff;color:var(--violet);border-color:var(--cyan)}
/* 实现工作项 #41：删除按钮（危险色）+ 已归档灰标 / 卡片降调 */
.btn-danger{background:var(--surface-2);border:1px solid var(--border-2);color:var(--danger)}
.btn-danger:hover{background:var(--danger);color:#fff;border-color:var(--danger)}
.arch-tag{font-size:11px;font-weight:600;color:var(--dim);background:var(--surface-2);border:1px solid var(--border-2);border-radius:6px;padding:2px 8px;margin-left:10px}
.proj-card-archived{opacity:.72}
.proj-card-archived .nm{color:var(--dim)}
.proj-card{padding:20px;margin-bottom:18px}
.proj-card .ph{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:6px;flex-wrap:wrap}
.proj-card .ph .nm{font-family:var(--display);font-size:16px;font-weight:600;color:var(--violet)}
.proj-card .repo{font-family:'Fira Code';font-size:12px;color:var(--dim);margin-bottom:16px}
.proj-card-ops{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.proj-card .proj-desc{font-size:12.5px;color:var(--dim);line-height:1.5;margin:0 0 14px}
.link-danger{color:var(--danger);font-size:13px;cursor:pointer;transition:opacity .15s}
.link-danger:hover{opacity:.7}
/* 实现工作项 #246：详情抽屉「接口契约」入口超链 */
.link-contract{color:var(--primary);font-size:13.5px;font-weight:600;cursor:pointer;transition:opacity .15s}
.link-contract:hover{opacity:.75;text-decoration:underline}

/* ===== 智能问数：悬浮球 + 右侧滑出面板 ===== */
.ai-orb{position:fixed;right:26px;bottom:26px;z-index:90;width:60px;height:60px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;color:#fff;
  background:linear-gradient(135deg,var(--cyan-2),var(--primary) 70%,var(--violet));
  box-shadow:0 10px 30px rgba(30,144,200,.45),inset 0 1px 0 rgba(255,255,255,.5);
  transition:transform .2s,box-shadow .2s}
.ai-orb::after{content:"";position:absolute;inset:-6px;border-radius:50%;border:2px solid rgba(30,144,200,.45);
  animation:orbRing 2.4s ease-out infinite}
@keyframes orbRing{0%{transform:scale(.85);opacity:.8}100%{transform:scale(1.5);opacity:0}}
.ai-orb:hover{transform:scale(1.08) translateY(-2px);box-shadow:0 16px 40px rgba(30,144,200,.55)}
.ai-orb svg{width:26px;height:26px;position:relative;z-index:1}
.ai-orb .tip{position:absolute;right:72px;white-space:nowrap;background:#fff;border:1px solid var(--border-2);
  color:var(--fg);font-size:12.5px;padding:7px 12px;border-radius:9px;opacity:0;transform:translateX(8px);
  transition:opacity .2s,transform .2s;pointer-events:none;box-shadow:var(--shadow)}
.ai-orb:hover .tip{opacity:1;transform:translateX(0)}

.ai-overlay{position:fixed;inset:0;z-index:95;background:rgba(27,33,71,.3);backdrop-filter:blur(2px);
  opacity:0;visibility:hidden;transition:opacity .3s,visibility .3s}
.ai-overlay.open{opacity:1;visibility:visible}
.ai-panel{position:fixed;top:0;right:0;bottom:0;z-index:96;width:600px;max-width:94vw;display:flex;flex-direction:column;
  background:linear-gradient(180deg,#FFFFFF,#F5F8FD);border-left:1px solid var(--border-2);
  box-shadow:-30px 0 80px rgba(39,39,110,.18);transform:translateX(100%);transition:transform .38s cubic-bezier(.16,1,.3,1)}
.ai-panel.open{transform:translateX(0)}
.ai-ph{display:flex;align-items:center;gap:12px;padding:20px 22px;border-bottom:1px solid var(--border)}
.ai-ph .badge-ai{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;color:#fff;
  background:linear-gradient(135deg,var(--cyan),var(--violet));box-shadow:0 4px 14px var(--glow)}
.ai-ph .badge-ai svg{width:20px;height:20px}
.ai-ph .ttl{font-family:var(--display);font-weight:600;font-size:15px;color:var(--violet)}
.ai-ph .st{font-size:11.5px;color:var(--ok);display:flex;align-items:center;gap:5px;margin-top:2px}
.ai-ph .st::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok)}
.ai-close{margin-left:auto;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--border);color:var(--muted);transition:color .15s,background .15s}
.ai-close:hover{color:var(--violet);background:#fff}
.ai-close svg{width:17px;height:17px}
.ai-log{flex:1;overflow-y:auto;padding:20px 22px}
.ai-welcome{margin-bottom:16px;padding:16px 18px;border-radius:14px;
  background:linear-gradient(135deg,rgba(30,144,200,.08),rgba(39,39,110,.05));border:1px solid var(--border)}
.ai-welcome .aw-tt{font-family:var(--display);font-size:15px;font-weight:600;color:var(--violet);margin-bottom:6px}
.ai-welcome .aw-sub{font-size:12.5px;color:var(--muted);line-height:1.55}
.ai-welcome .aw-scope{margin-top:10px;font-size:12px;color:var(--fg-2);padding:6px 11px;border-radius:8px;
  background:rgba(255,255,255,.6);border:1px solid var(--border);display:inline-block}
.ai-welcome .aw-scope b{color:var(--cyan);font-weight:600}
.suggest{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}
.chip{background:var(--surface-2);border:1px solid var(--border-2);border-radius:20px;padding:7px 13px;font-size:12.5px;
  color:var(--fg-2);cursor:pointer;transition:background .15s,border-color .15s,color .15s}
.chip:hover{background:rgba(30,144,200,.1);border-color:rgba(30,144,200,.35);color:var(--cyan)}
.msg{margin-bottom:18px}
.msg.user{display:flex;justify-content:flex-end}
.msg.user .bubble{background:linear-gradient(120deg,var(--primary),var(--cyan));color:#fff;font-weight:500;
  border-radius:14px 14px 4px 14px;padding:10px 14px;font-size:13.5px;max-width:88%}
.msg.ai .who{font-size:11.5px;color:var(--muted);margin-bottom:9px;display:flex;align-items:center;gap:7px}
.ai-dot{width:18px;height:18px;border-radius:6px;background:linear-gradient(135deg,var(--cyan),var(--violet));
  display:inline-flex;align-items:center;justify-content:center;box-shadow:0 0 8px var(--glow)}
.ai-dot::after{content:"";width:6px;height:6px;border-radius:50%;background:#fff}
.fn-call{font-family:'Fira Code';font-size:11.5px;background:rgba(30,144,200,.08);color:#176B9E;
  padding:8px 12px 8px 28px;border-radius:9px;margin-bottom:12px;display:inline-block;position:relative;border:1px solid rgba(30,144,200,.2)}
.fn-call::before{content:"";position:absolute;left:11px;top:50%;transform:translateY(-50%);width:7px;height:7px;border-radius:50%;
  background:var(--cyan);box-shadow:0 0 8px var(--cyan);animation:pulse 1.6s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
.fn-call b{color:var(--violet)}
.ai-result{position:relative;background:#fff;border:1px solid var(--border);border-radius:12px;padding:16px 16px 16px 20px;overflow:hidden;box-shadow:0 2px 10px rgba(39,39,110,.06)}
.ai-result::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(180deg,var(--cyan),var(--violet))}
.ai-result .rt{font-weight:600;font-size:14px;color:var(--violet);margin-bottom:4px}
.ai-result .cap{font-size:12px;color:var(--muted);margin-bottom:14px}
.chart-box{position:relative;height:230px}
.num-card{display:inline-block;background:linear-gradient(135deg,rgba(30,144,200,.1),rgba(39,39,110,.04));
  border:1px solid var(--border-2);border-radius:12px;padding:18px 26px;text-align:center;min-width:140px}
.num-card .n{font-family:var(--display);font-size:36px;font-weight:700;line-height:1;
  background:linear-gradient(120deg,var(--cyan),var(--violet));-webkit-background-clip:text;background-clip:text;color:transparent}
.num-card .l{font-size:12px;color:var(--muted);margin-top:7px}
.ai-result table{font-size:13px}
.ai-result th,.ai-result td{padding:9px 12px}
.ai-input{display:flex;gap:10px;padding:16px 22px;border-top:1px solid var(--border)}
.ai-input input{flex:1;padding:11px 14px;border-radius:11px;font-size:13.5px;font-family:inherit;color:var(--fg);
  background:#F4F7FC;border:1px solid var(--border-2);transition:border .15s,box-shadow .15s}
.ai-input input::placeholder{color:var(--dim)}
.ai-input input:focus{outline:none;border-color:var(--cyan);box-shadow:0 0 0 3px rgba(30,144,200,.14)}
.ai-input .btn{width:auto;padding:0 16px;border-radius:11px;flex-shrink:0;color:#fff;
  background:linear-gradient(120deg,var(--cyan-2),var(--primary))}
/* 新会话按钮：紧贴输入框左侧，点它清空上下文重开一轮 */
.ai-new{flex-shrink:0;width:42px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--border-2);color:var(--fg-2);transition:color .15s,background .15s,border-color .15s}
.ai-new:hover{background:#fff;color:var(--cyan);border-color:var(--cyan)}
.ai-new svg{width:19px;height:19px}

/* ===== 需求→任务 钻取树 ===== */
.req-group{margin-bottom:14px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-3);overflow:hidden;box-shadow:var(--shadow)}
.req-head{display:flex;align-items:center;gap:12px;padding:15px 18px;cursor:pointer;transition:background .15s}
.req-head:hover{background:rgba(30,144,200,.04)}
.req-head .caret{width:18px;height:18px;flex-shrink:0;color:var(--muted);transition:transform .2s}
.req-group.open .req-head .caret{transform:rotate(90deg)}
.req-head .rq-main{flex:1;min-width:0}
.req-head .rq-title{font-weight:600;color:var(--violet);font-size:14.5px;display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.req-head .rq-sub{font-size:12px;color:var(--dim);margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.req-head .rq-meta{display:flex;align-items:center;gap:14px;flex-shrink:0}
.req-head .rq-prog{width:120px}
.req-head .rq-prog .pl{font-size:11px;color:var(--muted);margin-bottom:4px;text-align:right}
.rq-view{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;padding:7px 12px;border-radius:8px;font-size:12.5px;font-weight:600;
  color:var(--cyan);background:rgba(30,144,200,.1);border:1px solid rgba(30,144,200,.25);transition:background .15s,color .15s,border-color .15s,box-shadow .15s}
.rq-view:hover{background:var(--cyan);color:#fff;border-color:var(--cyan);box-shadow:0 4px 12px rgba(30,144,200,.3)}
.rq-view svg{width:15px;height:15px}
.req-body{display:none;border-top:1px solid var(--border)}
.req-group.open .req-body{display:block}
.task-row{display:flex;align-items:center;gap:12px;padding:11px 18px 11px 46px;border-top:1px solid var(--border);cursor:pointer;transition:background .14s}
.task-row:first-child{border-top:none}
.task-row:hover{background:rgba(30,144,200,.05)}
.task-row .tr-id{font-family:'Fira Code';font-size:12px;color:var(--cyan);font-weight:600;flex-shrink:0;width:34px}
.task-row .tr-title{flex:1;min-width:0;font-size:13.5px;color:var(--fg-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.task-row .tr-who{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted);flex-shrink:0}
.task-row .tr-act{font-size:11px;color:var(--dim);display:flex;align-items:center;gap:4px;flex-shrink:0}
.task-row .tr-act svg{width:13px;height:13px}
.empty-hint{padding:18px;text-align:center;color:var(--dim);font-size:13px}

/* ===== 工作项详情抽屉 ===== */
.dt-overlay{position:fixed;inset:0;z-index:100;background:rgba(27,33,71,.32);backdrop-filter:blur(2px);opacity:0;visibility:hidden;transition:opacity .3s,visibility .3s}
.dt-overlay.open{opacity:1;visibility:visible}
.dt-panel{position:fixed;top:0;right:0;bottom:0;z-index:101;width:560px;max-width:94vw;display:flex;flex-direction:column;
  background:linear-gradient(180deg,#FFFFFF,#F5F8FD);border-left:1px solid var(--border-2);
  box-shadow:-30px 0 80px rgba(39,39,110,.18);transform:translateX(100%);transition:transform .38s cubic-bezier(.16,1,.3,1)}
.dt-panel.open{transform:translateX(0)}
.dt-head{padding:22px 24px 18px;border-bottom:1px solid var(--border)}
.dt-head .dt-top{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.dt-head .dt-id{font-family:'Fira Code';font-size:13px;color:var(--cyan);font-weight:600}
.dt-close{margin-left:auto;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;background:var(--surface-2);border:1px solid var(--border);color:var(--muted);transition:.15s}
.dt-close:hover{color:var(--violet);background:#fff}
.dt-close svg{width:17px;height:17px}
.dt-title{font-family:var(--display);font-size:18px;font-weight:600;color:var(--violet);line-height:1.4}
.dt-sub{font-size:12.5px;color:var(--muted);margin-top:8px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.dt-body{flex:1;overflow-y:auto;padding:22px 24px 40px}
.dt-sec{margin-bottom:26px}
.dt-sec-t{font-family:var(--display);font-size:13px;font-weight:600;color:var(--fg-2);letter-spacing:.02em;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.dt-sec-t::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--cyan)}
.dt-sec-t .cnt{font-family:'Fira Code';font-size:11px;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);border-radius:20px;padding:1px 8px}
.dt-desc{background:#fff;border:1px solid var(--border);border-radius:11px;padding:14px 16px;font-size:13.5px;color:var(--fg-2);line-height:1.6;white-space:pre-wrap}
.timeline{position:relative;padding-left:22px}
.timeline::before{content:"";position:absolute;left:5px;top:4px;bottom:4px;width:2px;background:var(--border-2)}
.tl-item{position:relative;margin-bottom:16px}
.tl-item:last-child{margin-bottom:0}
.tl-item::before{content:"";position:absolute;left:-21px;top:4px;width:10px;height:10px;border-radius:50%;background:var(--cyan);border:2px solid #fff;box-shadow:0 0 0 2px var(--border-2)}
.tl-item.change::before{background:var(--warn)}
.tl-card{background:#fff;border:1px solid var(--border);border-radius:10px;padding:12px 14px}
.tl-card .tl-sum{font-size:13.5px;color:var(--fg);font-weight:500;line-height:1.5;white-space:pre-wrap;word-break:break-word}
.tl-card .tl-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:9px;font-size:12px;color:var(--muted)}
.tl-card .tl-time{margin-left:auto;color:var(--dim);font-family:'Fira Code';font-size:11px}
.dt-hint{margin-top:10px;font-size:12px;color:var(--dim);line-height:1.5;padding:9px 12px;background:rgba(39,39,110,.04);border:1px dashed var(--border-2);border-radius:9px}
.tl-card .tl-reason{font-size:12.5px;color:var(--muted);margin-top:7px;padding-top:7px;border-top:1px dashed var(--border)}
.dt-bug{background:#fff;border:1px solid var(--border);border-left:3px solid var(--danger);border-radius:10px;padding:12px 14px;margin-bottom:10px}
.dt-bug .bg-d{font-size:13.5px;color:var(--fg);font-weight:500}
.dt-bug .bg-row{font-size:12.5px;color:var(--muted);margin-top:7px}
.dt-bug .bg-row b{color:var(--fg-2);font-weight:600}
.bug-st{font-size:11px;font-weight:600;padding:2px 9px;border-radius:20px}
.bug-open{background:rgba(229,71,110,.14);color:#C0335A}
.bug-fixed{background:rgba(22,168,116,.14);color:#0F8159}
.bug-closed{background:rgba(128,136,176,.16);color:#5A6390}
/* ===== 实现工作项 #84 详情抽屉只读测试区块（.dt-tl- 前缀，防污染既有视图） ===== */
.dt-tl-cap{font-size:12px;color:var(--muted);line-height:1.5;margin-bottom:10px}
.dt-tl-cap b{color:var(--cyan);font-weight:600}
.dt-tl-list{display:flex;flex-direction:column;gap:8px}
.dt-tl-row{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:9px;background:#fff;border:1px solid var(--border);cursor:pointer;transition:border-color .14s,box-shadow .14s,transform .14s}
.dt-tl-row:hover{border-color:var(--cyan);box-shadow:0 4px 14px rgba(30,144,200,.12);transform:translateY(-1px)}
.dt-tl-seq{flex-shrink:0;width:22px;height:22px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-family:'Fira Code';font-size:11px;font-weight:700;color:#fff;background:linear-gradient(135deg,var(--cyan),var(--violet))}
.dt-tl-what{flex:1;min-width:0;font-size:13px;color:var(--fg-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ===== 需求确认表（Markdown 文档化排版） ===== */
.md-doc{font-size:13.5px;color:var(--fg-2);line-height:1.7;white-space:normal}
.md-doc h1{font-family:var(--display);font-size:17px;font-weight:700;color:var(--violet);letter-spacing:.01em;
  margin:0 0 16px;padding-bottom:12px;border-bottom:2px solid var(--border-2);display:flex;align-items:center;gap:9px}
.md-doc h1::before{content:"";width:5px;height:20px;border-radius:3px;flex-shrink:0;
  background:linear-gradient(180deg,var(--cyan),var(--violet));box-shadow:0 0 10px var(--glow)}
.md-doc h2{font-family:var(--display);font-size:14px;font-weight:600;color:var(--violet);
  margin:24px 0 12px;display:flex;align-items:center;gap:8px}
.md-doc h2::before{content:"";width:6px;height:6px;border-radius:50%;flex-shrink:0;
  background:var(--cyan);box-shadow:0 0 8px var(--glow)}
.md-doc h2:first-child{margin-top:0}
.md-doc p{margin:0 0 12px}
.md-doc strong{color:var(--violet);font-weight:600}
.md-doc ul,.md-doc ol{margin:0 0 14px;padding-left:4px;list-style:none}
.md-doc li{position:relative;padding:5px 0 5px 24px;line-height:1.65}
.md-doc ul>li::before{content:"";position:absolute;left:7px;top:13px;width:6px;height:6px;border-radius:50%;
  background:var(--cyan);box-shadow:0 0 6px var(--glow)}
.md-doc ol{counter-reset:mdli}
.md-doc ol>li{counter-increment:mdli;padding-left:30px}
.md-doc ol>li::before{content:counter(mdli);position:absolute;left:0;top:6px;width:19px;height:19px;border-radius:6px;
  display:flex;align-items:center;justify-content:center;font-family:'Fira Code';font-size:11px;font-weight:600;
  color:#fff;background:linear-gradient(135deg,var(--cyan),var(--violet))}
.md-doc li ul{margin:6px 0 2px}
.md-doc li ul>li{padding:3px 0 3px 22px;font-size:13px;color:var(--muted)}
.md-doc li ul>li::before{top:11px;width:5px;height:5px;background:var(--dim);box-shadow:none}
.md-doc blockquote{margin:18px 0 0;padding:12px 16px;border-radius:10px;font-size:12.5px;color:var(--muted);
  background:rgba(30,144,200,.06);border-left:3px solid var(--cyan)}
.md-doc table{width:100%;border-collapse:separate;border-spacing:0;margin:0 0 18px;border-radius:11px;overflow:hidden;
  border:1px solid var(--border);box-shadow:0 2px 10px rgba(39,39,110,.05)}
.md-doc thead th{background:linear-gradient(135deg,rgba(39,39,110,.08),rgba(30,144,200,.06));
  font-family:var(--display);font-size:11.5px;font-weight:600;color:var(--violet);text-transform:none;
  letter-spacing:.02em;padding:11px 14px;text-align:left}
.md-doc tbody td{padding:12px 14px;border-top:1px solid var(--border);font-size:13px;color:var(--fg-2);
  line-height:1.65;vertical-align:top}
.md-doc tbody tr td:first-child{font-weight:600;color:var(--violet);white-space:nowrap;width:96px;
  background:rgba(39,39,110,.02)}
.md-doc tbody tr:hover{background:transparent}

/* ===== 通用表单弹窗（建账号 / 新建项目） ===== */
.modal-overlay{position:fixed;inset:0;z-index:150;display:flex;align-items:center;justify-content:center;padding:20px;
  background:rgba(27,33,71,.34);backdrop-filter:blur(3px);opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s}
.modal-overlay.open{opacity:1;visibility:visible}
.modal-card{width:440px;max-width:100%;max-height:90vh;overflow:auto;border-radius:18px;
  background:linear-gradient(180deg,#FFFFFF,#F7FAFE);border:1px solid var(--border-2);
  box-shadow:0 40px 90px rgba(39,39,110,.28),inset 0 1px 0 rgba(255,255,255,.7);
  transform:translateY(16px) scale(.98);transition:transform .28s cubic-bezier(.16,1,.3,1)}
.modal-overlay.open .modal-card{transform:none}
.modal-head{display:flex;align-items:center;gap:12px;padding:22px 24px 16px;border-bottom:1px solid var(--border)}
.modal-head .m-ic{width:40px;height:40px;border-radius:12px;flex-shrink:0;display:flex;align-items:center;justify-content:center;color:#fff;
  background:linear-gradient(135deg,var(--cyan),var(--violet));box-shadow:0 6px 16px var(--glow)}
.modal-head .m-ic svg{width:20px;height:20px}
.modal-head .m-tt{font-family:var(--display);font-size:16px;font-weight:600;color:var(--violet)}
.modal-head .m-sub{font-size:12px;color:var(--muted);margin-top:2px}
.modal-close{margin-left:auto;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--border);color:var(--muted);transition:.15s}
.modal-close:hover{color:var(--violet);background:#fff}
.modal-close svg{width:17px;height:17px}
.modal-body{padding:20px 24px 8px}
.form-field{margin-bottom:16px}
.form-field label{display:block;font-size:12.5px;font-weight:500;margin-bottom:7px;color:var(--fg-2);letter-spacing:.02em}
.form-field label .req{color:var(--danger);font-weight:700;margin-left:3px}
.form-field label .opt{color:var(--dim);font-weight:400;font-size:11px;margin-left:6px}
.form-field input,.form-field select,.form-field textarea{width:100%;padding:11px 13px;border-radius:10px;font-size:14px;font-family:inherit;color:var(--fg);
  background:#F4F7FC;border:1px solid var(--border-2);transition:border .16s,box-shadow .16s,background .16s}
.form-field textarea{resize:vertical;min-height:66px;line-height:1.5}
.form-field input::placeholder,.form-field textarea::placeholder{color:var(--dim)}
.form-field input:focus,.form-field select:focus,.form-field textarea:focus{outline:none;border-color:var(--cyan);background:#fff;box-shadow:0 0 0 4px rgba(30,144,200,.14)}
.form-field.invalid input,.form-field.invalid select{border-color:var(--danger);background:#FEF3F6;box-shadow:0 0 0 4px rgba(229,71,110,.12)}
.form-field .err{display:none;font-size:11.5px;color:var(--danger);margin-top:6px}
.form-field.invalid .err{display:block}
.form-hint{font-size:12px;color:var(--dim);line-height:1.5;margin:-4px 2px 14px;display:flex;align-items:center;gap:6px}
.form-hint .star{color:var(--danger);font-weight:700}
.form-tip{font-size:11.5px;color:var(--dim);line-height:1.5;margin-top:7px}
.modal-foot{display:flex;justify-content:flex-end;gap:10px;padding:14px 24px 22px}
.modal-foot .btn{padding:10px 20px;font-size:14px;border-radius:10px}

/* ===== 响应式 ===== */
@media(max-width:1100px){
  .stat-bar{grid-template-columns:repeat(3,1fr)}
  .kanban{grid-template-columns:1fr 1fr}
}
@media(max-width:860px){
  .sidebar{transform:translateX(-100%);transition:transform .32s cubic-bezier(.16,1,.3,1);box-shadow:30px 0 80px rgba(39,39,110,.18)}
  .sidebar.open{transform:translateX(0)}
  .main{margin-left:0}
  .hamburger{display:flex}
  .content{padding:20px 16px 80px}
  .topbar{padding:0 16px}
  .proj-pick span{display:none}
  .nav-backdrop{position:fixed;inset:0;z-index:55;background:rgba(27,33,71,.35);opacity:0;visibility:hidden;transition:.3s}
  .nav-backdrop.open{opacity:1;visibility:visible}
  .ai-orb{right:18px;bottom:18px;width:54px;height:54px}
}
@media(max-width:600px){
  .stat-bar{grid-template-columns:repeat(2,1fr)}
  .kanban{grid-template-columns:1fr}
  .cards{grid-template-columns:1fr 1fr}
  .topbar h2{font-size:16px}
}
@media(max-width:420px){
  .cards{grid-template-columns:1fr}
  .stat-bar{grid-template-columns:1fr 1fr}
}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
.uml-card{position:relative;margin-top:12px;padding:12px;border:1px solid rgba(39,39,110,.12);border-radius:10px;background:#fff;overflow:auto;cursor:zoom-in}
.uml-card svg{max-width:100%;height:auto}
.uml-note{margin-top:8px;font-size:12px;color:#8088B0}
.uml-zoom-hint{position:absolute;top:8px;right:10px;font-size:11px;color:#8088B0;background:rgba(255,255,255,.85);padding:2px 8px;border-radius:20px;pointer-events:none}
/* 图片放大 lightbox */
.uml-lightbox{position:fixed;inset:0;z-index:200;background:rgba(18,22,45,.82);display:none;align-items:center;justify-content:center;padding:24px;cursor:zoom-out}
.uml-lightbox.open{display:flex}
.uml-lightbox .box{max-width:96vw;max-height:92vh;overflow:auto;background:#fff;border-radius:12px;padding:20px;box-shadow:0 24px 60px rgba(0,0,0,.4);cursor:default}
.uml-lightbox .box svg{display:block;transform-origin:top left}
.uml-lightbox .lb-close{position:absolute;top:18px;right:24px;width:40px;height:40px;border:none;border-radius:50%;background:rgba(255,255,255,.9);color:#27276E;font-size:22px;cursor:pointer;line-height:40px}
.uml-lightbox .lb-tools{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);display:flex;gap:8px}
.uml-lightbox .lb-tools button{border:none;border-radius:8px;background:rgba(255,255,255,.92);color:#27276E;padding:8px 14px;font-size:14px;cursor:pointer}
/* AI 回复 markdown 渲染 */
.ai-reply{line-height:1.65;margin-bottom:8px;color:#3A3F63;word-break:break-word}
.ai-reply h1,.ai-reply h2,.ai-reply h3{margin:14px 0 8px;font-weight:700;color:#27276E;line-height:1.35}
.ai-reply h1{font-size:18px}.ai-reply h2{font-size:16px}.ai-reply h3{font-size:14px}
.ai-reply p{margin:6px 0}
.ai-reply ul,.ai-reply ol{margin:6px 0;padding-left:22px}
.ai-reply li{margin:3px 0}
.ai-reply strong{font-weight:700;color:#27276E}
.ai-reply code{background:rgba(39,39,110,.08);padding:1px 6px;border-radius:5px;font-family:'Fira Code',monospace;font-size:12.5px}
.ai-reply blockquote{margin:8px 0;padding:6px 12px;border-left:3px solid #1E90C8;background:rgba(30,144,200,.06);color:#5A6088;border-radius:0 6px 6px 0}
.ai-reply table{border-collapse:separate;border-spacing:0;width:100%;margin:14px 0;font-size:13px;
  background:#fff;border:1px solid rgba(39,39,110,.1);border-radius:12px;overflow:hidden;
  box-shadow:0 3px 14px rgba(39,39,110,.07)}
.ai-reply th,.ai-reply td{padding:10px 14px;text-align:left;vertical-align:top;border-bottom:1px solid rgba(39,39,110,.07)}
.ai-reply thead th{background:linear-gradient(120deg,#27276E,#1E90C8);color:#fff;font-weight:600;
  font-size:12.5px;letter-spacing:.02em;border-bottom:none;white-space:nowrap}
.ai-reply tbody td{color:#3A3F63;line-height:1.5}
.ai-reply tbody td:first-child{font-weight:600;color:#27276E}
.ai-reply tbody td strong{color:#1E90C8}
.ai-reply tbody tr:nth-child(even){background:rgba(30,144,200,.035)}
.ai-reply tbody tr:hover{background:rgba(30,144,200,.09);transition:background .15s}
.ai-reply tbody tr:last-child td{border-bottom:none}
.ai-reply a{color:#1E90C8;text-decoration:none}
/* ===== 实现工作项 #59 完成情况看板视图（全部限定 #view-dashboard，防污染既有视图） ===== */
#view-dashboard .dash-hint{font-family:var(--body);font-weight:400;font-size:12.5px;color:var(--dim);margin-left:6px}
#view-dashboard .dash-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:16px}
#view-dashboard .kpi{position:relative;padding:20px;overflow:hidden;transition:transform .2s,border-color .2s,box-shadow .2s}
#view-dashboard .kpi:hover{transform:translateY(-3px);border-color:var(--border-2);box-shadow:0 14px 40px rgba(39,39,110,.14)}
#view-dashboard .kpi::after{content:"";position:absolute;top:-30px;right:-30px;width:110px;height:110px;border-radius:50%;background:radial-gradient(circle,var(--kpiglow,rgba(30,144,200,.16)),transparent 70%)}
#view-dashboard .kpi .ic{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;margin-bottom:14px;background:rgba(30,144,200,.1);border:1px solid rgba(30,144,200,.18);color:var(--cyan)}
#view-dashboard .kpi .ic svg{width:19px;height:19px}
#view-dashboard .kpi .label{font-size:12.5px;color:var(--muted);margin-bottom:6px;letter-spacing:.02em}
#view-dashboard .kpi .num{font-family:var(--display);font-size:32px;font-weight:700;line-height:1;color:var(--violet)}
#view-dashboard .kpi .num.grad{background:linear-gradient(120deg,var(--cyan),var(--violet));-webkit-background-clip:text;background-clip:text;color:transparent}
#view-dashboard .kpi .num small{font-size:16px;font-weight:600;color:var(--muted);margin-left:2px}
#view-dashboard .kpi .sub{font-size:12px;color:var(--dim);margin-top:7px}
#view-dashboard .kpi.alarm .ic{background:rgba(229,71,110,.1);border-color:rgba(229,71,110,.22);color:var(--danger)}
#view-dashboard .kpi.alarm .num{color:var(--danger)}
#view-dashboard .kpi .dual{display:flex;align-items:flex-end;gap:20px}
#view-dashboard .kpi .dual .d{display:flex;flex-direction:column;gap:5px}
#view-dashboard .kpi .dual .dv{font-family:var(--display);font-size:32px;font-weight:700;line-height:1;color:var(--violet)}
#view-dashboard .kpi .dual .dl{font-size:11.5px;color:var(--muted);letter-spacing:.02em}
#view-dashboard .kpi .dual .d.t .dv{color:var(--warn)}
#view-dashboard .kpi .dual .sep{align-self:stretch;width:1px;background:var(--border);margin:3px 0}
#view-dashboard .dash-grid2{display:grid;grid-template-columns:1.15fr 1fr;gap:18px;align-items:start}
#view-dashboard .dash-panel{padding:20px}
#view-dashboard .dash-panel .p-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
#view-dashboard .dash-panel .p-t{font-family:var(--display);font-size:15px;font-weight:600;color:var(--violet);display:flex;align-items:center;gap:9px}
#view-dashboard .dash-panel .p-t svg{width:17px;height:17px;color:var(--cyan)}
#view-dashboard .dash-chart-box{position:relative;height:250px}
#view-dashboard .dash-gap-zone{position:relative;padding:22px 24px;overflow:hidden;background:linear-gradient(135deg,rgba(229,71,110,.06),rgba(224,146,14,.05));border:1px solid rgba(229,71,110,.2)}
#view-dashboard .dash-gap-zone::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:linear-gradient(180deg,var(--danger),var(--warn))}
#view-dashboard .gap-head{display:flex;align-items:center;gap:12px;margin-bottom:6px}
#view-dashboard .gap-head .g-ic{width:40px;height:40px;border-radius:12px;flex-shrink:0;display:flex;align-items:center;justify-content:center;color:#fff;background:linear-gradient(135deg,var(--danger),var(--warn));box-shadow:0 6px 16px rgba(229,71,110,.3)}
#view-dashboard .gap-head .g-ic svg{width:21px;height:21px}
#view-dashboard .gap-head .g-t{font-family:var(--display);font-size:16px;font-weight:600;color:var(--violet)}
#view-dashboard .gap-head .g-sub{font-size:12.5px;color:var(--muted);margin-top:2px}
#view-dashboard .gap-head .g-count{margin-left:auto;font-family:var(--display);font-size:30px;font-weight:700;color:var(--danger);line-height:1}
#view-dashboard .gap-list{margin-top:16px;display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}
#view-dashboard .gap-item{display:flex;flex-direction:column;gap:9px;padding:15px 16px;border-radius:12px;background:#fff;border:1px solid var(--border);border-top:3px solid var(--gapc,var(--dim));transition:transform .14s,box-shadow .14s}
#view-dashboard .gap-item:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(39,39,110,.12)}
#view-dashboard .gap-item .gi-top{display:flex;align-items:center;gap:9px}
#view-dashboard .gap-item .gi-badge{flex-shrink:0;font-size:11px;font-weight:600;padding:4px 10px;border-radius:7px;white-space:nowrap}
#view-dashboard .gi-badge.todo{background:rgba(128,136,176,.16);color:#5A6390}
#view-dashboard .gi-badge.doing{background:rgba(30,144,200,.14);color:#176B9E}
#view-dashboard .gi-badge.testing{background:rgba(224,146,14,.14);color:#B5740A}
#view-dashboard .gap-item .gi-idle{margin-left:auto;font-family:'Fira Code';font-size:11px;font-weight:600;padding:2px 8px;border-radius:6px;white-space:nowrap;background:rgba(224,146,14,.14);color:#B5740A}
#view-dashboard .gap-item .gi-idle.hot{background:rgba(229,71,110,.12);color:#C0335A}
#view-dashboard .gap-item .gi-title{font-size:14px;font-weight:600;color:var(--violet);display:flex;align-items:baseline;gap:8px}
#view-dashboard .gap-item .gi-title .id{font-family:'Fira Code';font-size:12px;color:var(--cyan);font-weight:600;flex-shrink:0}
#view-dashboard .gap-item .gi-title .tt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#view-dashboard .gap-item .gi-desc{font-size:12px;color:var(--muted);line-height:1.5}
#view-dashboard .gap-item .gi-foot{margin-top:2px;padding-top:10px;border-top:1px dashed var(--border);display:flex;align-items:center;justify-content:space-between;gap:10px}
#view-dashboard .gap-item .gi-who{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--fg-2)}
#view-dashboard .gap-item .gi-th{font-family:'Fira Code';font-size:11px;color:var(--dim)}
#view-dashboard .gap-empty{margin-top:14px;padding:24px;text-align:center;border-radius:11px;background:rgba(22,168,116,.06);border:1px dashed rgba(22,168,116,.3)}
#view-dashboard .gap-empty .ge-t{font-family:var(--display);font-size:15px;font-weight:600;color:var(--ok);display:flex;align-items:center;justify-content:center;gap:9px}
#view-dashboard .gap-empty .ge-sub{font-size:12.5px;color:var(--muted);margin-top:6px}
#view-dashboard .dash-gov-panel{padding:20px 24px}
#view-dashboard .gov-head{display:flex;align-items:center;gap:12px;margin-bottom:4px;flex-wrap:wrap}
#view-dashboard .gov-head .gh-ic{width:38px;height:38px;border-radius:11px;flex-shrink:0;display:flex;align-items:center;justify-content:center;color:#fff;background:linear-gradient(135deg,var(--violet),var(--cyan));box-shadow:0 6px 16px rgba(39,39,110,.2)}
#view-dashboard .gov-head .gh-ic svg{width:20px;height:20px}
#view-dashboard .gov-head .gh-t{font-family:var(--display);font-size:16px;font-weight:600;color:var(--violet)}
#view-dashboard .gov-head .gh-sub{font-size:12.5px;color:var(--muted);margin-top:2px}
#view-dashboard .gov-head .gh-chips{margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
#view-dashboard .gh-chip{font-size:12px;font-weight:600;padding:5px 12px;border-radius:20px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
#view-dashboard .gh-chip::before{content:"";width:7px;height:7px;border-radius:50%}
#view-dashboard .gh-chip.viol{background:rgba(229,71,110,.1);color:#C0335A;border:1px solid rgba(229,71,110,.22)}
#view-dashboard .gh-chip.viol::before{background:var(--danger)}
#view-dashboard .gh-chip.paused{background:rgba(122,107,196,.1);color:#5B4CA0;border:1px solid rgba(122,107,196,.24)}
#view-dashboard .gh-chip.paused::before{background:var(--c-paused)}
#view-dashboard .gov-list{margin-top:16px;display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:12px}
#view-dashboard .gov-row{display:flex;flex-direction:column;gap:9px;padding:15px 16px;border-radius:12px;background:#fff;border:1px solid var(--border);transition:transform .14s,box-shadow .14s}
#view-dashboard .gov-row:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(39,39,110,.12)}
#view-dashboard .gov-row.is-viol{border-top:3px solid var(--danger)}
#view-dashboard .gov-row.is-nomerge{border-top:3px solid var(--c-paused)}
#view-dashboard .gov-row .r-top{display:flex;align-items:center;gap:9px}
#view-dashboard .gov-row .r-badge{flex-shrink:0;font-size:11px;font-weight:600;padding:4px 10px;border-radius:7px;white-space:nowrap}
#view-dashboard .r-badge.viol{background:rgba(229,71,110,.12);color:#C0335A}
#view-dashboard .r-badge.nomerge{background:rgba(122,107,196,.14);color:#5B4CA0}
#view-dashboard .gov-row .r-chip{margin-left:auto;flex-shrink:0;font-family:'Fira Code';font-size:11px;font-weight:600;padding:3px 9px;border-radius:6px;white-space:nowrap}
#view-dashboard .r-chip.viol{background:rgba(229,71,110,.12);color:#C0335A}
#view-dashboard .r-chip.paused{background:rgba(122,107,196,.14);color:#5B4CA0}
#view-dashboard .gov-row .r-title{font-size:13.5px;font-weight:600;color:var(--violet);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
#view-dashboard .gov-row .r-title .id{font-family:'Fira Code';font-size:12px;color:var(--cyan);font-weight:600}
#view-dashboard .gov-row .r-tasks{display:flex;gap:7px;flex-wrap:wrap}
#view-dashboard .gov-row .r-task{font-size:12px;color:var(--fg-2);background:rgba(30,144,200,.06);border:1px solid rgba(30,144,200,.14);padding:2px 9px;border-radius:6px;display:inline-flex;align-items:center;gap:6px}
#view-dashboard .gov-row .r-task .id{font-family:'Fira Code';font-size:11px;color:var(--doing);font-weight:600}
#view-dashboard .gov-row .r-sub{font-size:12px;color:var(--muted);line-height:1.5}
#view-dashboard .gov-row .r-foot{margin-top:2px;padding-top:10px;border-top:1px dashed var(--border)}
#view-dashboard .gov-row .r-mr{font-family:'Fira Code';font-size:11.5px;color:var(--cyan);display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:6px;background:rgba(30,144,200,.08);border:1px solid rgba(30,144,200,.18)}
#view-dashboard .gov-row .r-mr:hover{background:rgba(30,144,200,.14)}
#view-dashboard .gov-row .r-mr svg{width:12px;height:12px}
#view-dashboard .gov-empty{margin-top:14px;padding:24px;text-align:center;border-radius:11px;background:rgba(22,168,116,.06);border:1px dashed rgba(22,168,116,.3)}
#view-dashboard .gov-empty .ge-t{font-family:var(--display);font-size:15px;font-weight:600;color:var(--ok);display:flex;align-items:center;justify-content:center;gap:9px}
#view-dashboard .gov-empty .ge-sub{font-size:12.5px;color:var(--muted);margin-top:6px}
#view-dashboard .dash-avatar{width:26px;height:26px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;font-size:11px;font-weight:700;color:#fff;background:linear-gradient(135deg,var(--cyan),var(--violet))}
#view-dashboard .dash-tbl{border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;background:var(--surface-3);box-shadow:var(--shadow)}
#view-dashboard .dash-tbl table{width:100%;border-collapse:collapse}
#view-dashboard .dash-tbl th{background:rgba(39,39,110,.04);text-align:left;padding:13px 18px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
#view-dashboard .dash-tbl td{padding:15px 18px;border-top:1px solid var(--border);font-size:14px;color:var(--fg-2);vertical-align:middle}
#view-dashboard .dash-tbl tbody tr{transition:background .14s}
#view-dashboard .dash-tbl tbody tr:hover{background:rgba(30,144,200,.04)}
#view-dashboard .who-cell{display:flex;align-items:center;gap:11px}
#view-dashboard .who-cell .nm{font-weight:600;color:var(--violet)}
#view-dashboard .dash-tbl .progress{height:8px;background:rgba(39,39,110,.08);border-radius:6px;overflow:hidden;display:flex;min-width:120px}
#view-dashboard .dash-tbl .progress span{display:block;height:100%}
#view-dashboard .rate-line{display:flex;align-items:center;gap:10px}
#view-dashboard .rate-line .pct{font-family:'Fira Code';font-size:13px;font-weight:600;color:var(--violet);min-width:40px}
#view-dashboard .tsplit{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
#view-dashboard .tsplit .ts{font-size:12px;font-weight:600;padding:3px 9px;border-radius:7px;white-space:nowrap}
#view-dashboard .tsplit .ts.done{background:rgba(22,168,116,.14);color:#0F8159}
#view-dashboard .tsplit .ts.testing{background:rgba(224,146,14,.16);color:#B5740A}
#view-dashboard .tsplit .ts.prog{background:rgba(128,136,176,.16);color:#5A6390}
#view-dashboard .tsplit .ts.total{background:rgba(39,39,110,.08);color:var(--violet)}
#view-dashboard .wl{display:flex;align-items:center;gap:14px;font-family:'Fira Code';font-size:12.5px}
#view-dashboard .wl .ins{color:var(--ok);font-weight:600}
#view-dashboard .wl .del{color:var(--danger);font-weight:600}
#view-dashboard .wl .cm{color:var(--muted)}
#view-dashboard .dot-ok{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--ok);font-weight:500}
#view-dashboard .dot-ok::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok)}
#view-dashboard .dot-idle{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--warn);font-weight:500}
#view-dashboard .dot-idle::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--warn);box-shadow:0 0 8px var(--warn)}
#view-dashboard .dot-na{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--dim);font-weight:500}
#view-dashboard .dot-na::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--dim)}
#view-dashboard .dash-empty{padding:30px;text-align:center;color:var(--dim);font-size:13px}
#view-dashboard .mono{font-family:'Fira Code',monospace}
@media(max-width:980px){#view-dashboard .dash-grid2{grid-template-columns:1fr}#view-dashboard .dash-cards{grid-template-columns:1fr 1fr}}
@media(max-width:560px){#view-dashboard .dash-cards{grid-template-columns:1fr}#view-dashboard .wl{flex-wrap:wrap;gap:6px}}
/* ===== 实现工作项 #83 测试管理视图（验收操作台）：全部限定 #view-testmgmt / .tm- 前缀，防污染既有视图 ===== */
/* 结果徽标（全局复用：详情抽屉等也可用） */
.badge-pass{background:rgba(22,168,116,.14);color:#0F8159}
.badge-fail{background:rgba(229,71,110,.14);color:#C42C54}
.badge-pending{background:rgba(128,136,176,.16);color:#5A6390}
/* —— 列表态：待验收需求卡 —— */
/* 实现工作项 #75：列表查询开关——默认隐藏已通过需求，勾选显示全部 */
#view-testmgmt .tm-filter{display:inline-flex;align-items:center;gap:7px;font-family:var(--body);font-weight:400;font-size:13px;color:var(--muted);cursor:pointer;user-select:none}
#view-testmgmt .tm-filter input{width:16px;height:16px;margin:0;accent-color:var(--cyan);cursor:pointer}
#view-testmgmt .tm-filter:hover{color:var(--cyan)}
#view-testmgmt .tm-reqcard{padding:18px 20px;margin-bottom:14px;cursor:pointer;transition:transform .15s,border-color .15s,box-shadow .15s}
#view-testmgmt .tm-reqcard:hover{transform:translateY(-2px);border-color:var(--border-2);box-shadow:0 12px 30px rgba(39,39,110,.12)}
#view-testmgmt .tm-rc-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px}
#view-testmgmt .tm-rc-id{font-family:'Fira Code';font-size:12px;color:var(--cyan);font-weight:600}
#view-testmgmt .tm-rc-title{font-family:var(--display);font-size:15px;font-weight:600;color:var(--violet)}
#view-testmgmt .tm-rc-proj{font-size:12px;color:var(--dim);margin-left:auto}
/* 实现工作项 #102：可收口高亮——测试全绿但未收口的需求，卡片左描边+微光，标题旁挂绿色「可做收口验收」徽标 */
#view-testmgmt .tm-reqcard.can-close{border-color:rgba(22,168,116,.5);box-shadow:0 0 0 1px rgba(22,168,116,.28),0 10px 26px rgba(22,168,116,.14);background:linear-gradient(120deg,rgba(22,168,116,.06),transparent 60%)}
#view-testmgmt .tm-reqcard.can-close:hover{border-color:var(--c-done);box-shadow:0 0 0 1px rgba(22,168,116,.4),0 14px 32px rgba(22,168,116,.2)}
#view-testmgmt .tm-rc-canclose{font-size:11.5px;font-weight:700;color:var(--c-done);background:rgba(22,168,116,.12);border:1px solid rgba(22,168,116,.35);border-radius:20px;padding:2px 10px;letter-spacing:.02em}
/* 实现工作项 #115：已收口需求——卡片整体降饱和/降亮，与在制需求拉开视觉层次；标题旁挂实心「已收口」徽标 */
#view-testmgmt .tm-reqcard.closed{opacity:.72;border-style:dashed;background:linear-gradient(120deg,rgba(90,99,144,.06),transparent 60%)}
#view-testmgmt .tm-reqcard.closed:hover{opacity:1;border-color:var(--border-2)}
#view-testmgmt .tm-reqcard.closed .tm-rc-title{color:var(--muted)}
#view-testmgmt .tm-rc-closed{font-size:11.5px;font-weight:700;color:#fff;background:var(--c-done);border:1px solid var(--c-done);border-radius:20px;padding:2px 10px;letter-spacing:.02em}
#view-testmgmt .tm-rc-pre{font-size:12.5px;color:var(--muted);line-height:1.55;margin:2px 0 12px;padding:9px 12px;background:rgba(30,144,200,.05);border-left:3px solid var(--cyan);border-radius:0 8px 8px 0}
#view-testmgmt .tm-rc-foot{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
#view-testmgmt .tm-stat{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600}
#view-testmgmt .tm-stat .n{font-family:'Fira Code'}
#view-testmgmt .tm-stat.pass{color:#0F8159}#view-testmgmt .tm-stat.fail{color:#C42C54}#view-testmgmt .tm-stat.pend{color:#5A6390}
#view-testmgmt .bar{flex:1;min-width:120px;max-width:320px;height:8px;background:rgba(39,39,110,.08);border-radius:6px;overflow:hidden}
#view-testmgmt .bar>i{display:block;height:100%;background:linear-gradient(90deg,var(--c-done),var(--cyan-2));transition:width .3s}
#view-testmgmt .tm-pct{font-family:'Fira Code';font-size:13px;font-weight:600;color:var(--violet);min-width:42px;text-align:right}
/* —— 详情态：单需求逐条验收 —— */
#view-testmgmt .tm-detail-head{padding:18px 22px;margin-bottom:16px}
#view-testmgmt .tm-back{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--muted);cursor:pointer;margin-bottom:12px;transition:color .15s}
#view-testmgmt .tm-back:hover{color:var(--cyan)}
#view-testmgmt .tm-pre{font-size:13px;color:var(--fg-2);line-height:1.6;margin-bottom:14px}
#view-testmgmt .tm-pre b{color:var(--violet)}
#view-testmgmt .tm-progress{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
#view-testmgmt .tm-progress .lbl{font-size:12.5px;color:var(--muted);font-weight:500}
/* —— 验收条目卡 —— */
#view-testmgmt .tm-item,#rv-confirm .tm-item{background:#fff;border:1px solid var(--border);border-left:3px solid var(--dim);border-radius:11px;padding:15px 17px;margin-bottom:12px;transition:border-color .2s,box-shadow .2s}
#view-testmgmt .tm-item.pass{border-left-color:var(--ok)}
#view-testmgmt .tm-item.fail{border-left-color:var(--danger)}
/* 实现工作项 #84：从详情抽屉跳转锚定到某验收项时的短暂高亮（jumpToItem 加类后移除）
   实现工作项 #91：单次闪烁 0.5s 一个完整亮起→淡出周期，重复 2 次共 1s，更醒目（JS 定时器同步 1000ms） */
#view-testmgmt .tm-item.tm-item-flash{animation:tmItemFlash .5s ease-out 2}
@keyframes tmItemFlash{0%{box-shadow:0 0 0 0 rgba(30,144,200,0);border-color:var(--cyan)}25%{box-shadow:0 0 0 5px rgba(30,144,200,.2);border-color:var(--cyan)}100%{box-shadow:0 0 0 0 rgba(30,144,200,0);border-color:var(--border)}}
#view-testmgmt .tm-item-h,#rv-confirm .tm-item-h{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap}
#view-testmgmt .tm-item-h .seq,#rv-confirm .tm-item-h .seq{width:24px;height:24px;border-radius:7px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-family:'Fira Code';font-size:12px;font-weight:700;color:#fff;background:linear-gradient(135deg,var(--cyan),var(--violet))}
#view-testmgmt .tm-item-h .what,#rv-confirm .tm-item-h .what{flex:1;min-width:0;font-size:14px;font-weight:600;color:var(--violet)}
#view-testmgmt .tm-row,#rv-confirm .tm-row{display:flex;gap:10px;margin-bottom:9px;font-size:13px;line-height:1.6}
#view-testmgmt .tm-row .k,#rv-confirm .tm-row .k{flex-shrink:0;width:56px;color:var(--muted);font-weight:600}
#view-testmgmt .tm-row .v,#rv-confirm .tm-row .v{flex:1;min-width:0;color:var(--fg-2)}
#view-testmgmt .tm-steps,#rv-confirm .tm-steps{margin:0;padding:0;list-style:none;counter-reset:tmstep}
#view-testmgmt .tm-steps li,#rv-confirm .tm-steps li{counter-increment:tmstep;position:relative;padding:3px 0 3px 26px;color:var(--fg-2);line-height:1.6}
#view-testmgmt .tm-steps li .n,#view-testmgmt .tm-steps li::before,#rv-confirm .tm-steps li::before{content:counter(tmstep);position:absolute;left:0;top:4px;width:18px;height:18px;border-radius:5px;display:flex;align-items:center;justify-content:center;font-family:'Fira Code';font-size:10.5px;font-weight:600;color:var(--cyan);background:rgba(30,144,200,.1);border:1px solid rgba(30,144,200,.2)}
#view-testmgmt .tm-links,#rv-confirm .tm-links{display:flex;gap:8px;flex-wrap:wrap;margin:4px 0 12px}
#view-testmgmt .tm-tasklink,#view-testmgmt .tm-buglink,#view-review .tm-tasklink,#rv-confirm .tm-tasklink{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;padding:3px 10px;border-radius:7px;font-family:'Fira Code'}
#view-testmgmt .tm-tasklink,#view-review .tm-tasklink,#rv-confirm .tm-tasklink{color:#176B9E;background:rgba(30,144,200,.08);border:1px solid rgba(30,144,200,.18);cursor:pointer;transition:background .15s} /* 实现工作项 #84：任务号可点跳详情，补手型+hover；#312 返修：#view-review 的需求/任务号同款可点 */
#view-testmgmt .tm-tasklink:hover,#view-review .tm-tasklink:hover,#rv-confirm .tm-tasklink:hover{background:rgba(30,144,200,.16)}
#view-testmgmt .tm-buglink{color:#C42C54;background:rgba(229,71,110,.08);border:1px solid rgba(229,71,110,.2);cursor:pointer;transition:background .15s}
#view-testmgmt .tm-buglink:hover{background:rgba(229,71,110,.16)}
#view-testmgmt .tm-meta{font-size:12px;color:var(--fg-2);font-weight:500;margin:2px 0 12px}
#view-testmgmt .tm-ops{display:flex;gap:9px;flex-wrap:wrap;padding-top:12px;border-top:1px dashed var(--border)}
#view-testmgmt .tm-noperm{font-size:12px;color:var(--dim);font-style:italic;padding-top:12px;border-top:1px dashed var(--border)}
#view-testmgmt .tm-empty{padding:30px;text-align:center;color:var(--dim);font-size:13px}
/* —— 严重程度段选（记 Bug 弹窗，全局 .tm- 前缀） —— */
.tm-fld{margin-bottom:16px}
.tm-fld-lbl{font-size:12.5px;font-weight:600;color:var(--fg-2);margin-bottom:8px}
.tm-sev-seg{display:flex;gap:8px;flex-wrap:wrap}
/* 实现工作项 #87：对齐原型——去掉 flex:1/min-width/justify-content 让选项按内容宽度左对齐、padding 收成 8px 16px */
.tm-sev-opt{display:flex;align-items:center;gap:7px;padding:8px 16px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;background:var(--surface-2);border:1px solid var(--border-2);color:var(--fg-2);transition:.15s}
.tm-sev-opt::before{content:"";width:9px;height:9px;border-radius:50%;background:currentColor;flex-shrink:0}
.tm-sev-opt:hover{border-color:var(--cyan)}
/* 实现工作项 #76 修复：选中态文字统一压白。提高特异性到 [data-sev].on(0,3,0)，压过下方 [data-sev]{color}(0,2,0)，否则同权重按源码顺序后者胜 → 深色文字压深底看不见 */
.tm-sev-opt[data-sev].on{color:#fff;border-color:transparent}
.tm-sev-opt.on::before{background:#fff}
.tm-sev-opt[data-sev="blocker"]{color:#C42C54} .tm-sev-opt[data-sev="blocker"].on{background:#C42C54}
.tm-sev-opt[data-sev="critical"]{color:#E5476E} .tm-sev-opt[data-sev="critical"].on{background:#E5476E}
.tm-sev-opt[data-sev="major"]{color:#B5740A} .tm-sev-opt[data-sev="major"].on{background:#E0920E}
.tm-sev-opt[data-sev="minor"]{color:#5A6390} .tm-sev-opt[data-sev="minor"].on{background:#8088B0}
.tm-textarea{width:100%;min-height:96px;padding:11px 13px;border-radius:10px;font-size:14px;font-family:inherit;line-height:1.55;resize:vertical;color:var(--fg);background:#F4F7FC;border:1px solid var(--border-2);transition:border .16s,box-shadow .16s,background .16s}
.tm-textarea:focus{outline:none;border-color:var(--cyan);background:#fff;box-shadow:0 0 0 4px rgba(30,144,200,.14)}
.tm-textarea::placeholder{color:var(--dim)}
.tm-bv-desc{font-size:13.5px;color:var(--fg-2);line-height:1.65;white-space:pre-wrap;padding:12px 14px;background:#F4F7FC;border:1px solid var(--border);border-radius:10px}
.sev-badge{font-size:11px;font-weight:600;padding:2px 9px;border-radius:20px}
.sev-badge.blocker{background:rgba(229,71,110,.14);color:#C42C54}
.sev-badge.critical{background:rgba(224,146,14,.16);color:#B5740A}
.sev-badge.major{background:rgba(30,144,200,.14);color:#176B9E}
.sev-badge.minor{background:rgba(128,136,176,.16);color:#5A6390}
.btn-danger{background:var(--surface-2);border:1px solid var(--border-2);color:var(--danger)}
.btn-danger:hover{background:var(--danger);color:#fff;border-color:var(--danger)}
.btn-ok{color:#fff;background:linear-gradient(120deg,#1FBE84,var(--ok));box-shadow:0 4px 12px rgba(22,168,116,.28)}
.btn-ok:hover{filter:brightness(1.05)}
/* —— 记 Bug 弹窗对齐原型：加大弹窗 + 截图上传（Ctrl+V 粘贴）+ 缩略图 + 放大灯箱。全部作用域限定 #tm-bug-modal / #tm-bugview-modal，不动其它共享 .modal-card 弹窗 —— */
#tm-bug-modal .modal-card,#tm-bugview-modal .modal-card{width:min(720px,96vw)}
#tm-bug-modal .tm-textarea{min-height:150px;font-size:14.5px;line-height:1.6;padding:14px 16px}
/* Ctrl+V 提示：贴在字段标签右侧 */
.tm-paste-kbd{margin-left:auto;font-weight:400;font-size:11.5px;color:var(--dim);display:inline-flex;align-items:center;gap:4px}
.tm-fld-lbl{display:flex;align-items:center;gap:6px}
.tm-paste-kbd b{font-family:'Fira Code',monospace;font-weight:600;color:var(--fg-2);background:var(--surface-2);border:1px solid var(--border-2);border-bottom-width:2px;border-radius:5px;padding:1px 6px;font-size:11px}
/* 截图上传拖放区 */
.tm-shot-zone{border:2px dashed var(--border-2);border-radius:12px;padding:18px;text-align:center;color:var(--muted);cursor:pointer;transition:.15s;background:var(--surface-2)}
.tm-shot-zone:hover{border-color:var(--cyan);color:var(--cyan);background:rgba(30,144,200,.05)}
.tm-shot-zone svg{width:26px;height:26px;stroke:currentColor;fill:none;stroke-width:1.8;margin-bottom:6px}
.tm-shot-zone .tip{font-size:12.5px}
/* 缩略图网格 */
.tm-shot-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:10px;margin-top:12px}
.tm-shot-thumb{position:relative;aspect-ratio:4/3;border-radius:10px;overflow:hidden;border:1px solid var(--border);background:linear-gradient(135deg,#E3ECF8,#DCEAFB);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--muted);padding:6px;text-align:center;word-break:break-all}
.tm-shot-thumb.has-img{padding:0;cursor:zoom-in}
.tm-shot-thumb .shot-img{width:100%;height:100%;object-fit:cover;display:block}
.tm-shot-thumb .shot-fn{padding:6px}
.tm-shot-thumb .rm{position:absolute;top:4px;right:4px;width:20px;height:20px;border-radius:50%;background:rgba(27,33,71,.7);color:#fff;font-size:13px;display:flex;align-items:center;justify-content:center;line-height:1;z-index:2;cursor:pointer}
.tm-shot-thumb .rm:hover{background:var(--danger)}
/* 图片放大灯箱 */
.tm-lightbox{position:fixed;inset:0;background:rgba(15,18,40,.82);backdrop-filter:blur(4px);z-index:210;display:none;align-items:center;justify-content:center;padding:40px}
.tm-lightbox.open{display:flex}
.tm-lb-inner{position:relative;max-width:min(1000px,92vw);max-height:88vh;display:flex;flex-direction:column;align-items:center;gap:14px}
.tm-lb-close{position:absolute;top:-14px;right:-14px;width:40px;height:40px;border-radius:50%;background:#fff;color:var(--violet);border:none;font-size:22px;line-height:40px;cursor:pointer;box-shadow:0 6px 18px rgba(0,0,0,.3)}
.tm-lb-img{max-width:min(900px,88vw);max-height:min(600px,70vh);border-radius:14px;object-fit:contain;background:linear-gradient(135deg,#E3ECF8,#DCEAFB)}
.tm-lb-name{color:#fff;font-size:13px}
/* ===== 待确认问题（巡检发现）视图，实现工作项 #312 ===== */
.rv-card{padding:14px 16px;margin-bottom:12px}
.rv-head{display:flex;align-items:center;gap:8px;margin-bottom:6px;flex-wrap:wrap}
.rv-head b{font-size:15px;color:var(--fg)}
.rv-sev{font-size:12px;padding:2px 9px;border-radius:10px;font-weight:600;white-space:nowrap}
.rv-sev-blocker{background:rgba(229,71,110,.16);color:var(--danger)}
.rv-sev-critical{background:rgba(229,71,110,.12);color:var(--danger)}
.rv-sev-major{background:rgba(224,146,14,.16);color:var(--warn)}
.rv-sev-minor{background:rgba(22,168,116,.15);color:var(--ok)}
.rv-sev-info{background:rgba(39,39,110,.08);color:var(--muted)}
.rv-desc{color:var(--fg);margin:4px 0;white-space:pre-wrap;font-size:14px}
.rv-anchor{font-size:12px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;margin:4px 0}
.rv-suggest{margin:6px 0 2px;font-size:13px;color:var(--fg-2)}
.rv-target{color:var(--dim);font-size:12px}
.rv-reason{font-size:12px;color:var(--dim);margin:2px 0 6px}
.rv-actions{display:flex;align-items:center;gap:8px;margin-top:8px;flex-wrap:wrap}
.rv-actions select{padding:5px 8px;border-radius:8px;border:1px solid rgba(39,39,110,.16);background:#F4F7FC;color:var(--fg);font-size:13px}
/* 待确认问题：导航未读徽标（红底白字圆形，实现工作项 #312 返修） */
.nav-badge{min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:var(--danger);color:#fff;font-size:11px;font-weight:700;line-height:18px;text-align:center;display:inline-block;margin-left:auto;flex:none}
/* 处置确认弹窗预览（实现工作项 #312 返修） */
.rv-preview{font-size:14px}
.rv-pv-row{margin:6px 0;color:var(--fg-2);font-weight:500}
.rv-pv-desc{margin:6px 0;color:var(--fg);white-space:pre-wrap;padding:8px 10px;background:#F4F7FC;border-radius:8px}
.rv-pv-note{font-size:13px;color:var(--dim);margin:6px 0}
.rv-analyze-row{display:flex;align-items:center;gap:8px;margin:4px 0 8px;font-size:13px;font-weight:600;color:var(--fg-2)}
.rv-analyze-row select{padding:5px 8px;border-radius:8px;border:1px solid rgba(39,39,110,.16);background:#F4F7FC;color:var(--fg);font-size:13px}
.rv-gen{margin-top:4px}

/* ===== AI 质量检查（实现工作项 #328 S4）===== */
.qa-day{border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-3);margin-bottom:14px;overflow:hidden}
.qa-day-head{display:flex;align-items:center;gap:10px;padding:14px 18px;cursor:pointer;user-select:none}
.qa-day-head:hover{background:var(--bg-2)}
.qa-chev{transition:transform .2s;color:var(--dim)}
.qa-day.open .qa-chev{transform:rotate(90deg)}
.qa-date{font-weight:700;font-size:15px}
.qa-sum{color:var(--muted);font-size:12.5px}
.qa-sum b{color:var(--fg)}
.qa-day-body{display:none;padding:0 18px 18px;border-top:1px dashed var(--border)}
.qa-day.open .qa-day-body{display:block}
.qa-overview{background:linear-gradient(135deg,#F5F8FF,#EEF4FC);border:1px solid rgba(30,144,200,.15);border-radius:11px;padding:14px 16px;margin-top:14px}
.qa-ov-row{display:flex;flex-wrap:wrap;gap:8px 22px;font-size:13px;color:var(--fg-2)}
.qa-kv b{color:var(--fg)}
.qa-n-严重{color:var(--danger)} .qa-n-一般{color:var(--warn)} .qa-n-轻微{color:var(--cyan)} .qa-n-提示{color:var(--dim)}
.qa-sec-title{font-size:13px;font-weight:700;color:var(--fg-2);margin:20px 0 10px;display:flex;align-items:center;gap:8px}
.qa-cnt{background:var(--danger);color:#fff;font-size:11px;font-weight:700;border-radius:9px;padding:1px 8px}
.qa-sol{border:1px solid var(--border);border-left:3px solid var(--warn);border-radius:11px;padding:14px 16px;margin-bottom:10px;background:var(--surface-3)}
.qa-sol.qa-严重,.qa-sol.qa-阻断{border-left-color:var(--danger)}
.qa-sol-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.qa-title{font-weight:700;font-size:14px}
.qa-tag{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px}
.qa-tag-严重,.qa-tag-阻断{background:rgba(229,71,110,.14);color:var(--danger)}
.qa-tag-一般{background:rgba(224,146,14,.15);color:#B5740A}
.qa-tag-轻微{background:rgba(30,144,200,.13);color:#176B9E}
.qa-tag-提示{background:rgba(39,39,110,.08);color:var(--muted)}
.qa-tag-转任务{background:rgba(30,144,200,.13);color:#176B9E}
.qa-sol-sub{color:var(--muted);font-size:12.5px;margin:6px 0}
.qa-covers{font-size:12.5px;color:var(--fg-2);margin:4px 0}
.qa-c{display:inline-block;background:var(--bg-2);border:1px solid var(--border);border-radius:6px;padding:2px 8px;margin:2px 4px 2px 0;font-family:ui-monospace,monospace;font-size:12px}
.qa-taskdesc-toggle{color:var(--cyan);font-size:12.5px;cursor:pointer;font-weight:600;margin:6px 0 2px;display:inline-block}
.qa-taskdesc{display:none;background:var(--bg-2);border:1px solid var(--border);border-radius:9px;padding:12px 14px;margin:8px 0;font-size:13px;color:var(--fg-2)}
.qa-taskdesc.open{display:block}
.qa-sol-actions{display:flex;gap:8px;margin-top:10px}
.qa-sol-status{display:flex;align-items:center;gap:8px;margin-top:10px;font-size:13px;color:var(--ok);font-weight:600}
.qa-taskno{color:var(--cyan);cursor:pointer;font-weight:700;text-decoration:underline}
.qa-sugg{border:1px dashed var(--border);border-radius:11px;padding:6px 16px;margin-bottom:10px;font-size:12.5px}
.qa-s-item{display:flex;align-items:center;gap:8px;padding:6px 0;color:var(--fg-2);border-bottom:1px dashed var(--border)}
.qa-s-item:last-child{border-bottom:none}
.qa-s-item .qa-tag{flex-shrink:0}
.qa-s-text{flex:1}
.qa-ign{color:var(--dim);font-size:12px;cursor:pointer}
.qa-ign:hover{color:var(--danger)}
.qa-warn{color:var(--danger);font-weight:700;margin-left:6px;cursor:pointer}
.qa-edit-row{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}
.qa-edit-row label{font-size:12px;font-weight:700;color:var(--muted)}
.qa-edit-row input,.qa-edit-row textarea{background:var(--bg-2);border:1px solid var(--border);border-radius:8px;padding:8px 10px;font-size:13px;color:var(--fg);font-family:inherit;width:100%;box-sizing:border-box}
.qa-links{font-size:12.5px;margin:4px 0}
.qa-links .tm-tasklink{margin-right:8px}
.qa-desc-preview{background:var(--bg-2);border:1px solid var(--border);border-radius:9px;padding:12px 14px;max-height:340px;overflow:auto}
.qa-desc-toggle{font-size:11px;color:var(--cyan);cursor:pointer;font-weight:600;margin-left:8px}
.qa-desc-toggle:hover{color:var(--violet)}
.qa-loc{font-family:ui-monospace,monospace;font-size:12px;color:var(--cyan)}
.qa-s-title{cursor:pointer}
.qa-s-title:hover{color:var(--cyan)}
/* 实现工作项 #298：qa-s-desc 改 class 切换（原内联 style="display:none"），状态进 class 才能被 snapshot/restore 跨重渲保持 */
.qa-s-desc{display:none;margin-top:4px;color:var(--muted);font-size:12px;white-space:pre-wrap}
.qa-s-desc.open{display:block}
.qa-help{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--dim);color:#fff;font-size:10px;font-weight:700;cursor:help;position:relative;vertical-align:middle;margin-left:6px}
.qa-help .qa-tip{display:none;position:absolute;bottom:22px;left:50%;transform:translateX(-50%);width:260px;background:#2A2E4E;color:#fff;font-size:12px;line-height:1.5;padding:9px 11px;border-radius:8px;z-index:50;box-shadow:0 6px 18px rgba(0,0,0,.25)}
.qa-help:hover .qa-tip{display:block}
.qa-help .qa-tip b{color:#7BB8E0}
.qa-hl{animation:qaflash 1.2s ease 2}
@keyframes qaflash{0%,100%{box-shadow:0 0 0 rgba(30,144,200,0)}50%{box-shadow:0 0 0 4px rgba(30,144,200,.45)}}

/* ===== 实现工作项 #133 实现计划管理视图：列表卡片+详情 =====
   两段命名空间，都保证不污染既有视图：
   1) 视图内的规则一律 `#view-plans` 限定；
   2) 四个弹窗挂在 body 下、够不到 #view-plans，只能靠 `pl-` 类名前缀隔离
      （落笔前 grep 过全表，`pl-` 前缀此前无人占用）。
   颜色只取 :root 令牌、不新引色值：健康度与漂移的三档复用 --ok/--warn/--danger，
   让「绿黄红」在全站是同一套含义。 */

/* ---------- 列表：总体计划分组 ---------- */
#view-plans .pl-list{display:flex;flex-direction:column;gap:14px}
#view-plans .pl-kids{display:none;flex-direction:column;gap:10px;
  margin:10px 0 0 26px;padding-left:16px;border-left:2px dashed var(--border-2)}
#view-plans .pl-group.open .pl-kids{display:flex}
#view-plans .pl-cv{display:inline-block;color:var(--muted);font-size:11px;cursor:pointer;transition:transform .16s;flex-shrink:0}
#view-plans .pl-group.open .pl-cv{transform:rotate(90deg)}
#view-plans .pl-kids-empty{font-size:12.5px;color:var(--dim);padding:10px 14px;border:1px dashed var(--border);border-radius:var(--radius-sm)}
#view-plans .pl-flat{display:flex;flex-direction:column;gap:10px}
#view-plans .pl-orphan-t{font-size:12.5px;font-weight:600;color:var(--muted);margin:20px 0 8px}

/* ---------- 列表：计划卡片 ---------- */
#view-plans .pl-card{padding:16px 18px}
#view-plans .pl-card.master{border-left:3px solid var(--violet)}
#view-plans .pl-card.arch{opacity:.62}
#view-plans .pl-c-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
#view-plans .pl-c-nm{font-family:var(--display);font-size:15px;font-weight:600;color:var(--fg);cursor:pointer}
#view-plans .pl-c-nm:hover{color:var(--cyan)}
#view-plans .pl-c-kid{font-size:11.5px;color:var(--dim);margin-left:auto}
#view-plans .pl-c-desc{font-size:12.5px;color:var(--muted);margin-top:6px}
#view-plans .pl-c-bars{margin-top:12px;max-width:520px}
#view-plans .pl-c-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:11px}
#view-plans .pl-c-date{font-family:'Fira Code',monospace;font-size:12px;color:var(--fg-2)}
#view-plans .pl-c-ms{font-size:12px;color:var(--muted)}
#view-plans .pl-c-acts{display:flex;gap:7px;margin-top:12px;padding-top:11px;border-top:1px dashed var(--border)}

/* ---------- 行内新建阶段（实现工作项 #378 Task 6）---------- */
/* 虚线框 + 浅底：它是一份**还没提交的草稿**，与卡片上那些已落库的数据要分得开。
   表单本身是条件渲染、不靠 class 显隐，所以这里没有显示开关——但下面
   .invalid 那两条是真开关：JS 加/去 invalid 类，「不能为空」才显隐得了，
   守卫成对断言这两半（只有一半时提示永远不出现）。 */
#view-plans .pl-phi{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin-top:11px;padding:11px 12px;border-radius:var(--radius-sm);
  background:rgba(30,144,200,.05);border:1px dashed rgba(30,144,200,.32)}
#view-plans .pl-phi input{padding:7px 10px;border-radius:8px;font-family:inherit;font-size:13px;
  color:var(--fg);background:#fff;border:1px solid var(--border-2);transition:border .15s,box-shadow .15s}
#view-plans .pl-phi input:focus{outline:none;border-color:var(--cyan);box-shadow:0 0 0 3px rgba(30,144,200,.12)}
#view-plans .pl-phi input.nm{flex:1;min-width:150px}
#view-plans .pl-phi input.sq{width:74px;font-family:'Fira Code',monospace;font-size:12.5px}
#view-plans .pl-phi .err{display:none;flex-basis:100%;font-size:11.5px;color:var(--danger)}
#view-plans .pl-phi.invalid input.nm{border-color:var(--danger);background:#FEF3F6}
#view-plans .pl-phi.invalid .err{display:block}

/* ---------- 徽标：类型 / 状态 / 阶段 ---------- */
#view-plans .pl-tag{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;white-space:nowrap}
#view-plans .pl-tag.master{background:rgba(39,39,110,.11);color:var(--indigo)}
#view-plans .pl-tag.detail{background:rgba(30,144,200,.12);color:#176B9E}
#view-plans .pl-tag.phase{background:rgba(122,107,196,.13);color:#5B4CA0}
#view-plans .pl-tag.arch{background:rgba(160,166,192,.2);color:#6E7396}

/* ---------- 健康度差值 / 基线漂移 ---------- */
/* 「未设区间」「未排期」用点线框而非实底：它是**信息缺失**，不该跟 ok/bad 一样张扬 */
/* 实现工作项 #377：加 position:relative，给下方自绘气泡当定位参照 */
#view-plans .pl-gap,#view-plans .pl-drift{position:relative;font-size:11.5px;font-weight:600;padding:2px 9px;border-radius:20px;white-space:nowrap;cursor:help}
#view-plans .pl-gap.ok{background:rgba(22,168,116,.13);color:#0F8159}
#view-plans .pl-gap.warn{background:rgba(224,146,14,.15);color:#B5740A}
#view-plans .pl-gap.bad{background:rgba(229,71,110,.13);color:#C42C54}
#view-plans .pl-drift.ok{background:rgba(22,168,116,.13);color:#0F8159}
#view-plans .pl-drift.bad{background:rgba(229,71,110,.13);color:#C42C54}
#view-plans .pl-gap.none,#view-plans .pl-drift.none{background:transparent;border:1px dashed var(--border-2);color:var(--dim)}

/* 实现工作项 #377 计划管理缺陷修复：徽标提示不出现
   原生 title 有 1~2 秒的浏览器延迟，而 WS 铃铛静默刷新会整块重建 innerHTML——
   节点一换，tooltip 计时就从头开始；刷新间隔短于这个延迟时，提示永远等不出来。
   改为自绘气泡：它是徽标的子节点，纯 CSS :hover 即显、零延迟，也不存在
   「要跨重渲维持的悬停态」。样式与下方 .pl-help .pl-tip 同源，全站气泡一副观感。
   三处刻意的取值，别改歪：
   1) left:0 而非居中——徽标可能贴着卡片左缘（KPI 卡里的「进度偏差」就是），
      居中会让 288px 的气泡探出屏幕左侧；左对齐永远只向右铺开。
   2) 逐项复位继承——徽标自身是 nowrap / 600 字重，KPI 卡的 .v 还是等宽字，
      不复位的话说明文案会被拉成一条横到天边的单行。
   3) pointer-events:none——气泡会盖住下方的操作按钮（计划卡里紧跟着
      「打开 / 编辑 / 归档 / 删除」），不穿透的话鼠标移向按钮时点击会落在气泡上。 */
#view-plans .pl-gap .pl-tip,#view-plans .pl-drift .pl-tip{display:none;position:absolute;
  top:calc(100% + 6px);left:0;z-index:30;width:288px;pointer-events:none;
  padding:10px 12px;border-radius:9px;background:#2A2E4E;color:#fff;
  font-family:var(--body);font-size:11.5px;font-weight:400;line-height:1.6;
  white-space:normal;text-align:left;text-transform:none;letter-spacing:normal;
  box-shadow:0 8px 22px rgba(27,33,71,.32)}
#view-plans .pl-gap .pl-tip b,#view-plans .pl-drift .pl-tip b{color:#7BB8E0}
#view-plans .pl-gap:hover .pl-tip,#view-plans .pl-drift:hover .pl-tip{display:block}

/* ---------- 双轨进度条 ---------- */
/* 时间轨取中性灰紫、完成轨取品牌青：两根条一眼能分清哪根是「钟表」哪根是「产出」 */
#view-plans .pl-bar-row{display:flex;align-items:center;gap:9px;margin-bottom:6px}
#view-plans .pl-bar-lb{font-size:11.5px;color:var(--muted);width:28px;flex-shrink:0}
#view-plans .pl-bar{flex:1;height:8px;background:rgba(39,39,110,.08);border-radius:6px;overflow:hidden;min-width:80px}
#view-plans .pl-bar i{display:block;height:100%;border-radius:6px;transition:width .3s}
#view-plans .pl-bar i.t{background:linear-gradient(90deg,#9AA0C4,#8088B0)}
#view-plans .pl-bar i.d{background:linear-gradient(90deg,var(--cyan),var(--cyan-2))}
/* 未设区间：斜纹占位。**绝不画一根 0% 的实心条**——那会把「还没定日期」伪装成「刚开始」 */
#view-plans .pl-bar.unset{background:repeating-linear-gradient(45deg,rgba(39,39,110,.07),rgba(39,39,110,.07) 4px,transparent 4px,transparent 8px)}
#view-plans .pl-bar-v{font-family:'Fira Code',monospace;font-size:11.5px;font-weight:600;color:var(--fg-2);width:64px;text-align:right;flex-shrink:0}
#view-plans .pl-bar-v.dim{color:var(--dim);font-weight:500}
#view-plans .pl-bar-sub{font-size:11.5px;color:var(--dim);margin-top:2px}

/* ---------- 一致性提醒（§5 必须显式展示、不能吞） ---------- */
#view-plans .pl-warn{background:rgba(224,146,14,.07);border:1px solid rgba(224,146,14,.28);
  border-left:3px solid var(--warn);border-radius:var(--radius-sm);padding:12px 15px;margin-bottom:16px}
#view-plans .pl-warn-t{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:700;color:#B5740A;flex-wrap:wrap}
#view-plans .pl-warn-t .n{background:var(--warn);color:#fff;font-size:11px;border-radius:9px;padding:0 7px}
#view-plans .pl-warn-sub{font-size:11.5px;font-weight:400;color:var(--muted)}
#view-plans .pl-warn ul{margin:7px 0 0 18px}
#view-plans .pl-warn li{font-size:12.5px;color:var(--fg-2);margin-top:3px}

/* ---------- 详情头 + KPI ---------- */
#view-plans .pl-dt-h{display:flex;align-items:flex-start;gap:14px;margin-bottom:16px;flex-wrap:wrap}
#view-plans .pl-dt-t{flex:1;min-width:220px}
#view-plans .pl-dt-nm{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  font-family:var(--display);font-size:19px;font-weight:700;color:var(--fg)}
#view-plans .pl-dt-sb{font-size:12.5px;color:var(--muted);margin-top:4px}
#view-plans .pl-dt-acts{display:flex;gap:8px}
#view-plans .pl-kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;margin-bottom:14px}
#view-plans .pl-kpi{padding:13px 16px}
#view-plans .pl-kpi .k{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
#view-plans .pl-kpi .v{font-family:'Fira Code',monospace;font-size:14px;font-weight:600;color:var(--fg);margin-top:6px}
#view-plans .pl-dt-bars{padding:15px 18px;margin-bottom:18px;max-width:560px}

/* ---------- 详情分区（阶段 / 下挂计划 / 里程碑 / 归属） ---------- */
#view-plans .pl-sec{padding:0;overflow:hidden;margin-bottom:16px}
#view-plans .pl-sec-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:14px 18px;border-bottom:1px solid var(--border)}
#view-plans .pl-sec-t{font-family:var(--display);font-size:14px;font-weight:600;color:var(--fg)}
#view-plans .pl-sec-sb{flex:1;min-width:160px;font-size:11.5px;color:var(--dim)}
#view-plans .pl-sec-f{padding:11px 18px;border-top:1px dashed var(--border);font-size:11.5px;color:var(--dim)}
#view-plans .pl-tb{width:100%}
#view-plans .pl-tb th{padding:10px 18px}
#view-plans .pl-tb td{padding:11px 18px;vertical-align:top}
#view-plans .pl-td-empty{padding:22px 18px;text-align:center;font-size:12.5px;color:var(--dim);line-height:1.6}
#view-plans .pl-sq{font-family:'Fira Code',monospace;color:var(--muted)}
#view-plans .pl-ph-nm{font-size:13.5px;font-weight:600;color:var(--fg)}
#view-plans .pl-ph-ds{font-size:12px;color:var(--muted);margin-top:3px}
#view-plans .pl-row-acts{display:flex;gap:5px;justify-content:flex-end}
/* pl-mono 故意不加 #view-plans 限定：弹窗里的编号列也用它，两处得同一副等宽字观感 */
.pl-mono{font-family:'Fira Code',monospace;font-size:12.5px}

/* 下挂详细计划：一行一条，整行可点进详情 */
#view-plans .pl-kid-list{padding:6px 10px}
#view-plans .pl-kid-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:11px 8px;
  border-bottom:1px dashed var(--border);cursor:pointer;transition:background .12s}
#view-plans .pl-kid-row:last-child{border-bottom:none}
#view-plans .pl-kid-row:hover{background:rgba(30,144,200,.05)}
#view-plans .pl-kid-row .nm{font-size:13.5px;font-weight:600;color:var(--fg)}
#view-plans .pl-kid-row .dt{font-family:'Fira Code',monospace;font-size:12px;color:var(--fg-2)}
#view-plans .pl-kid-row .ms{font-size:12px;color:var(--muted);margin-left:auto}

/* ---------- 一页到底的折叠区块（实现工作项 #378 Task 2，取代原来的四个分页）----------
   折叠头自成一块、不复用 .pl-sec：展开时它与下方区块要看得出是一体的，
   所以头部只给一条下缘留白，靠三角形的朝向表达开合。 */
#view-plans .pl-fold{margin-bottom:16px}
#view-plans .pl-fold-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;cursor:pointer;
  padding:11px 4px 9px;user-select:none}
#view-plans .pl-fold-h:hover .pl-sec-t{color:var(--cyan)}
#view-plans .pl-fold-ar{display:inline-block;flex-shrink:0;width:14px;color:var(--muted);
  font-size:11px;transition:transform .16s,color .16s}
#view-plans .pl-fold-h:hover .pl-fold-ar{color:var(--cyan)}
#view-plans .pl-fold-ar.open{transform:rotate(90deg)}
/* 折起来时标题弱化成「这里还有东西」的提示，不与展开的区块抢注意力 */
#view-plans .pl-fold:not(.open) .pl-sec-t{color:var(--muted)}
/* 只有一个按钮的裸区块头（里程碑在一页到底里的样子：标题在折叠头上，这里只剩按钮）*/
#view-plans .pl-sec-h-bare{justify-content:flex-end;padding:10px 18px}

/* ---------- 里程碑 ---------- */
#view-plans .pl-ms-list{padding:10px 14px}
#view-plans .pl-ms{border:1px solid var(--border);border-left:3px solid var(--border-2);border-radius:var(--radius-sm);
  padding:12px 14px;margin-bottom:9px;background:var(--surface-3)}
#view-plans .pl-ms:last-child{margin-bottom:0}
#view-plans .pl-ms.reach{border-left-color:var(--ok);background:rgba(22,168,116,.04)}
#view-plans .pl-ms-h{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
#view-plans .pl-ms-nm{font-size:13.5px;font-weight:600;color:var(--fg)}
#view-plans .pl-ms-due{font-family:'Fira Code',monospace;font-size:12px;color:var(--muted)}
#view-plans .pl-ms-h .pl-row-acts{margin-left:auto}
#view-plans .pl-ms-b{margin-top:8px}
#view-plans .pl-ms-kind{font-size:11.5px;padding:2px 8px;border-radius:6px}
#view-plans .pl-ms-kind.auto{background:rgba(30,144,200,.1);color:#176B9E}
#view-plans .pl-ms-kind.manual{background:rgba(39,39,110,.07);color:var(--muted)}
#view-plans .pl-ms-lks{display:flex;gap:6px;flex-wrap:wrap;margin-top:7px}
#view-plans .pl-ms-lk{font-size:11.5px;padding:2px 8px;border-radius:6px;background:var(--bg-2);
  border:1px solid var(--border);color:var(--fg-2);cursor:help}
#view-plans .pl-ms-lk.ok{background:rgba(22,168,116,.1);border-color:rgba(22,168,116,.26);color:#0F8159}

/* 达成开关。locked = 关联了工作项、达成实时算（后端会静默丢弃手工值），
   所以做成明确"按不动"的样子：变灰 + not-allowed，点了给解释而不是假装生效。 */
#view-plans .pl-sw{position:relative;width:34px;height:18px;border-radius:20px;background:rgba(39,39,110,.16);
  flex-shrink:0;cursor:pointer;transition:background .18s}
#view-plans .pl-sw i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
  background:#fff;box-shadow:0 1px 3px rgba(39,39,110,.3);transition:left .18s}
#view-plans .pl-sw.on{background:var(--ok)}
#view-plans .pl-sw.on i{left:18px}
#view-plans .pl-sw.locked{background:rgba(160,166,192,.3);cursor:not-allowed}
#view-plans .pl-sw.locked.on{background:rgba(22,168,116,.42)}
#view-plans .pl-sw.ro{cursor:default}

/* ---------- 归属管理表：§4.1 去重行 ---------- */
/* 不重复计数的子任务压暗并当场说明，否则用户会觉得进度数错了 */
#view-plans tr.pl-dup td{opacity:.62}
#view-plans .pl-dup-tip{display:inline-block;margin-left:7px;font-size:11px;padding:1px 7px;border-radius:5px;
  background:rgba(39,39,110,.07);color:var(--muted);opacity:1}

/* ---------- 弹窗（body 下，靠 pl- 前缀隔离） ---------- */
.pl-md-note{background:rgba(30,144,200,.06);border:1px solid rgba(30,144,200,.2);border-radius:var(--radius-sm);
  padding:10px 13px;margin-bottom:14px;font-size:12px;color:var(--fg-2);line-height:1.6}
.pl-md-note b{color:var(--violet)}
.pl-md-note code{font-family:'Fira Code',monospace;font-size:11.5px;background:rgba(39,39,110,.07);padding:1px 5px;border-radius:4px}
.pl-md-2col{display:grid;grid-template-columns:1fr 1fr;gap:12px}
/* 里程碑关联选择器：复用 .picker 的盒子观感，行高更紧（一行要塞下 4 段信息） */
.pl-picker{max-height:230px;overflow-y:auto;border:1px solid var(--border-2);border-radius:10px;background:#F4F7FC;padding:5px}
.form-field.invalid .pl-picker{border-color:var(--danger);background:#FEF3F6}
.pl-pk-row{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:8px;cursor:pointer;transition:background .12s}
.pl-pk-row:hover{background:#E9F0FA}
.pl-pk-row input{width:15px;height:15px;flex-shrink:0;margin:0;accent-color:var(--cyan);cursor:pointer}
.pl-pk-row .id{color:var(--cyan);font-weight:600;flex-shrink:0}
.pl-pk-row .tt{flex:1;font-size:13px;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pl-pk-row .due{color:var(--muted);flex-shrink:0}
/* 已被别的计划占用：置灰 + 不可点（一个工作项只能属一个计划，§3.2） */
.pl-pk-row.taken{opacity:.6;cursor:not-allowed}
.pl-pk-row.taken:hover{background:transparent}
.pl-taken{font-size:11px;padding:1px 7px;border-radius:5px;background:rgba(224,146,14,.14);color:#B5740A;flex-shrink:0}
/* 选工作项弹窗：按需求分组，组头可折叠、可整组勾选 */
.pl-pick-tool{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.pl-pick-tool input{flex:1;background:var(--bg-2);border:1px solid var(--border-2);border-radius:8px;
  padding:8px 11px;font-size:13px;color:var(--fg);font-family:inherit}
.pl-pick-tool input:focus{outline:none;border-color:var(--cyan);box-shadow:0 0 0 3px rgba(30,144,200,.12)}
.pl-pick-cnt{font-size:12px;color:var(--muted);flex-shrink:0}
.pl-pick-cnt b{color:var(--cyan)}
/* ---------- 实现工作项 #378 Task 5：弹窗滚动容器与已选汇总 ---------- */
/* position:relative 给汇总条留一个定位参照（将来若要 sticky 就在这一层） */
.pl-pick-body{position:relative}
.pl-pick-sum{margin-bottom:10px;border:1px solid var(--border);border-radius:var(--radius-sm);
  background:rgba(39,39,110,.03);font-size:12.5px}
.pl-pick-sum-h{display:flex;align-items:center;gap:7px;padding:8px 11px;cursor:pointer;color:var(--fg-2)}
.pl-pick-sum-h b{color:var(--cyan)}
.pl-pick-sum-h.dim{cursor:default;color:var(--dim)}
.pl-pick-sum-h .cv{color:var(--muted);font-size:10px;transition:transform .16s}
.pl-pick-sum-h .cv.open{transform:rotate(90deg)}
.pl-pick-sum-h .hint{margin-left:auto;font-size:11px;color:var(--dim)}
/* 明细限高自滚：它在可滚的 body 里，不限高会把候选列表整个顶到看不见 */
.pl-pick-sum-b{display:flex;flex-wrap:wrap;gap:6px;max-height:132px;overflow-y:auto;
  padding:9px 11px;border-top:1px solid var(--border)}
.pl-pick-sum-i{padding:2px 8px;border-radius:5px;background:var(--bg-2);
  border:1px solid var(--border-2);color:var(--fg-2);font-size:11.5px}
.pl-pg{border:1px solid var(--border);border-radius:var(--radius-sm);margin-bottom:8px;overflow:hidden}
.pl-pg-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 12px;
  background:rgba(39,39,110,.03);cursor:pointer;font-size:13px}
.pl-pg-h input{width:15px;height:15px;margin:0;accent-color:var(--cyan);cursor:pointer;flex-shrink:0}
.pl-pg-h .cv{color:var(--muted);font-size:11px;transition:transform .16s;flex-shrink:0}
.pl-pg.open .pl-pg-h .cv{transform:rotate(90deg)}
.pl-pg-h .id{font-family:'Fira Code',monospace;font-size:12px;color:var(--cyan);font-weight:600}
.pl-pg-h .tt{font-weight:600;color:var(--fg)}
.pl-pg-h .sub{margin-left:auto;font-size:11.5px;color:var(--dim)}
/* 实现工作项 #378 Task 5：**删掉** max-height 与 overflow-y，消掉与 .modal-body 的嵌套滚动。
   display:none 与下一条 .pl-pg.open .pl-pg-b 是折叠机制本身，删多了整组内容永远不显示。 */
.pl-pg-b{display:none;padding:4px 6px;border-top:1px solid var(--border)}
.pl-pg.open .pl-pg-b{display:block}
/* 实现工作项 #377：需求行独立一层，不在 .pl-pg-b 内——组头全选只扫 .pl-pg-b，
   需求行因此天然不被全选波及，但仍可单独手动勾。左侧竖线示意「这是本组的需求本体」。 */
.pl-pg-req{display:none;padding:4px 6px;border-top:1px solid var(--border);
  background:rgba(39,39,110,.015);box-shadow:inset 2px 0 0 var(--cyan)}
.pl-pg.open .pl-pg-req{display:block}

/* ===== 实现工作项 #332 实现排期表格与只读时间线 =====
   延用 #133 的两条命名约定：视图内规则一律 `#view-plans` 限定、类名走 `pl-` 前缀。
   颜色仍只取 :root 令牌——逾期/漂移/超界一律 --danger，待保存/警示一律 --warn，
   已交付一律 --ok，让「红黄绿」在全站是同一套含义。 */

/* ---------- 排期表 ---------- */
#view-plans .pl-sch td{vertical-align:middle}
/* 子任务行缩进：需求紧跟自己的子任务，缩进是唯一的层级线索（表里没有树线） */
#view-plans tr.pl-sub .pl-nm{padding-left:44px}
#view-plans .pl-sch .pl-nm{font-size:13px;color:var(--fg)}
#view-plans .pl-sch .pl-nm .tt{color:var(--fg)}
/* 逾期行整行淡红：让「该催了」在一屏里跳出来，不必逐格找日期 */
#view-plans tr.pl-late td{background:rgba(229,71,110,.045)}
#view-plans .pl-late-tag{display:inline-block;margin-left:7px;font-size:10.5px;font-weight:700;
  padding:1px 7px;border-radius:5px;background:rgba(229,71,110,.13);color:#C42C54}
/* 未排期是**信息缺失**、不是错误：点线框、不用实底，视觉权重压在逾期之下 */
#view-plans .pl-unset-tag{display:inline-block;margin-left:7px;font-size:10.5px;
  padding:1px 7px;border-radius:5px;border:1px dashed var(--border-2);color:var(--dim)}
#view-plans .pl-unset{color:var(--dim)}
/* ---------- 实现工作项 #378 Task 4：行首里程碑菱形 ---------- */
/* 菱形形状画在 ::before 上、**不是把 .pl-dia 自己旋转 45 度**：气泡是它的子节点，
   父节点一转，子节点连着转——一个歪 45 度的说明框没人读得出来。
   position:relative 是气泡定位的锚，删了气泡会跑到页面角落去（守卫成对断言这一条）。
   达成填实、未达成空心，与时间线泳道的菱形同源；越线再叠一层红，形状不变——
   一列里混三种形状，人得先学图例才能读表。 */
#view-plans .pl-dia{position:relative;display:inline-block;width:8px;height:8px;
  margin-right:6px;vertical-align:middle;cursor:help}
#view-plans .pl-dia::before{content:'';position:absolute;inset:0;transform:rotate(45deg);
  background:var(--violet);border:1px solid var(--violet);border-radius:1px}
#view-plans .pl-dia.hollow::before{background:transparent}
#view-plans .pl-dia.late::before{background:var(--danger);border-color:var(--danger)}
#view-plans .pl-dia.hollow.late::before{background:transparent}
/* 第三族 .pl-tip：菱形贴在名字列最左边，气泡只能往右摊（同 .pl-gap / .pl-drift 的 left:0），
   不能照抄表头问号那族的 right:0——那会把它推出表格左边界。
   pointer-events:none：气泡宽 240px、盖住右边的标题文字，可点的话鼠标一移进去就
   离开了菱形、气泡自己闪掉，看着像点不着。
   white-space:normal：这一列继承了表格的紧凑排版，不显式放开会被压成一行横着溢出。 */
#view-plans .pl-dia .pl-tip{display:none;position:absolute;top:16px;left:0;z-index:30;width:240px;
  padding:9px 11px;border-radius:9px;background:#2A2E4E;color:#fff;pointer-events:none;
  font-family:var(--body);font-size:11.5px;font-weight:400;line-height:1.6;white-space:normal;
  text-transform:none;letter-spacing:normal;box-shadow:0 8px 22px rgba(27,33,71,.32)}
#view-plans .pl-dia:hover .pl-tip{display:block}
/* 越线告警是文字而不是只给菱形染个红：颜色是辅助通道，色盲用户与截图转述都会丢掉它。
   独占一行（display:block）——它比「已逾期」长得多，挤在标题后面会把行撑成两行还读不顺。 */
#view-plans .pl-sch-late-ms{display:block;margin-top:3px;font-size:11px;font-weight:600;
  color:#C42C54;line-height:1.5}
#view-plans .pl-ro{font-family:'Fira Code',monospace;font-size:12.5px;color:var(--muted)}
/* 日期格。dirty（待保存）压黄、over（晚于计划结束日）压红——保存前就把后果画出来 */
#view-plans .pl-sch input[type=date]{width:100%;padding:6px 8px;border-radius:8px;
  font-family:'Fira Code',monospace;font-size:12.5px;color:var(--fg);
  background:#F4F7FC;border:1px solid var(--border-2);transition:border .15s,box-shadow .15s,background .15s}
#view-plans .pl-sch input[type=date]:focus{outline:none;border-color:var(--cyan);background:#fff;
  box-shadow:0 0 0 3px rgba(30,144,200,.12)}
#view-plans .pl-sch input.dirty{border-color:var(--warn);background:rgba(224,146,14,.08)}
#view-plans .pl-sch input.over{border-color:var(--danger);background:rgba(229,71,110,.06)}
/* 漂移天数：正数（往后挪）才是坏消息，压红；提前/零漂移压绿；null 走 none 灰 */
#view-plans .pl-drift-n{font-family:'Fira Code',monospace;font-size:12.5px;font-weight:600}
#view-plans .pl-drift-n.pos{color:#C42C54}
#view-plans .pl-drift-n.neg,#view-plans .pl-drift-n.zero{color:#0F8159}
#view-plans .pl-drift-n.none{color:var(--dim);font-weight:400}
/* 表头「基线」的问号气泡：基线永不覆盖这条规矩不解释清楚，没人看得懂漂移列 */
#view-plans .pl-help{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:14px;height:14px;border-radius:50%;background:var(--dim);color:#fff;
  font-size:10px;font-weight:700;cursor:help;vertical-align:middle;margin-left:5px}
#view-plans .pl-help .pl-tip{display:none;position:absolute;top:20px;right:0;z-index:30;width:282px;
  padding:10px 12px;border-radius:9px;background:#2A2E4E;color:#fff;
  font-family:var(--body);font-size:11.5px;font-weight:400;line-height:1.6;
  text-transform:none;letter-spacing:normal;box-shadow:0 8px 22px rgba(27,33,71,.32)}
#view-plans .pl-help .pl-tip b{color:#7BB8E0}
#view-plans .pl-help:hover .pl-tip{display:block}
/* 保存条：待保存条数与「几条超界」都写在这，按钮旁边就是后果说明 */
#view-plans .pl-savebar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:12px 18px;border-top:1px solid var(--border);background:rgba(30,144,200,.04)}
#view-plans .pl-savebar-st{flex:1;min-width:220px;font-size:12.5px;color:var(--fg-2);line-height:1.6}
#view-plans .pl-savebar-st b{color:var(--violet);font-weight:700}
#view-plans .pl-over-n{color:#C42C54}
#view-plans .pl-savebar-r{display:flex;gap:8px;flex-shrink:0}
/* 待保存计数（实现工作项 #378 Task 2 从分页标签搬到保存条自己身上）。
   原来它挂在分页标签上（.pl-tab-n），人在别的分页时压根看不见自己有几条没存；
   一页到底后分页没了，数字跟「保存排期」按钮同处一地——要按下去的那一刻才最需要它。 */
#view-plans .pl-savebar-n{display:inline-block;min-width:16px;padding:0 6px;margin-right:6px;
  border-radius:9px;background:var(--warn);color:#fff;
  font-family:'Fira Code',monospace;font-size:10.5px;font-weight:700;line-height:16px;
  text-align:center;vertical-align:middle}

/* ---------- 只读时间线 ---------- */
/* 布局要点：整块用 padding-left 让出标签列，轴/里程碑泳道/横条区都落在同一条轨道上，
   于是所有按日期投影出来的 left:% 共享同一个参照宽度——三层才可能对齐。
   行标签反过来用负 left 绝对定位进那块 padding 里。 */
#view-plans .pl-tl{padding:16px 18px 22px;overflow-x:auto}
#view-plans .pl-tl-grid{position:relative;padding-left:226px;min-width:520px}
#view-plans .pl-tl-lg{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:14px;
  font-size:11.5px;color:var(--muted)}
#view-plans .pl-tl-lg span{display:inline-flex;align-items:center;gap:6px}
#view-plans .pl-tl-lg i{flex-shrink:0}
#view-plans .lg-bar,#view-plans .lg-done,#view-plans .lg-late{width:22px;height:8px;border-radius:4px}
#view-plans .lg-bar{background:linear-gradient(90deg,var(--cyan),var(--cyan-2))}
#view-plans .lg-done{background:var(--ok)}
#view-plans .lg-late{background:var(--danger)}
#view-plans .lg-dot{width:9px;height:9px;border-radius:50%;background:var(--dim)}
#view-plans .lg-ms{width:9px;height:9px;background:var(--ok);transform:rotate(45deg)}
#view-plans .lg-ms.hollow{background:#fff;border:2px solid var(--muted)}
/* X 轴刻度 */
#view-plans .pl-tl-axis{display:flex;justify-content:space-between;padding-bottom:6px;
  border-bottom:1px solid var(--border)}
#view-plans .pl-tl-axis .wk{font-family:'Fira Code',monospace;font-size:10.5px;color:var(--dim);white-space:nowrap}
/* 里程碑泳道。菱形填实=已达成、空心=未达成；「注定达不成」只压红名称、不换第三种形状 */
#view-plans .pl-tl-ms-lane{position:relative;height:78px}
#view-plans .pl-tl-ms{position:absolute;top:12px;width:11px;height:11px;
  background:var(--ok);transform:translateX(-50%) rotate(45deg);cursor:help;
  box-shadow:0 1px 4px rgba(39,39,110,.25)}
#view-plans .pl-tl-ms.hollow{background:#fff;border:2px solid var(--muted);box-shadow:none}
#view-plans .pl-tl-ms-lb{position:absolute;top:30px;transform:translateX(-50%);max-width:130px;
  font-size:10.5px;color:var(--muted);text-align:center;line-height:1.35;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#view-plans .pl-tl-ms-lb.doomed{color:#C42C54;font-weight:600}
/* 横条区 */
#view-plans .pl-tl-body{position:relative;padding-top:4px}
#view-plans .pl-tl-today{position:absolute;top:0;bottom:0;width:2px;
  background:var(--danger);opacity:.45;z-index:2}
#view-plans .pl-tl-today::after{content:'今天';position:absolute;bottom:-15px;left:3px;
  font-size:10px;font-weight:600;color:var(--danger);opacity:1;white-space:nowrap}
#view-plans .pl-tl-row{position:relative;height:26px;margin-bottom:6px}
#view-plans .pl-tl-lb{position:absolute;left:-226px;top:0;width:212px;height:26px;
  display:flex;align-items:center;gap:6px;font-size:12px;color:var(--fg-2);overflow:hidden}
#view-plans .pl-tl-lb.sub{padding-left:18px}
#view-plans .pl-tl-lb .badge{flex-shrink:0}
#view-plans .pl-tl-lb .pl-mono{flex-shrink:0;color:var(--cyan);font-weight:600}
#view-plans .pl-tl-lb .tt{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#view-plans .pl-tl-track{position:absolute;left:0;right:0;top:8px;height:10px;
  background:rgba(39,39,110,.05);border-radius:5px}
/* 已开工：横条（开工日 → 截止日）。未开工：只有截止日那一个点。 */
#view-plans .pl-tl-item{position:absolute;top:0;height:10px;border-radius:5px;cursor:help;
  background:linear-gradient(90deg,var(--cyan),var(--cyan-2));min-width:3px}
#view-plans .pl-tl-item.done{background:var(--ok)}
#view-plans .pl-tl-item.late{background:var(--danger)}
#view-plans .pl-tl-point{position:absolute;top:0;width:10px;height:10px;border-radius:50%;
  background:var(--dim);transform:translateX(-50%);cursor:help;
  box-shadow:0 0 0 2px rgba(255,255,255,.9)}
#view-plans .pl-tl-point.done{background:var(--ok)}
#view-plans .pl-tl-point.late{background:var(--danger)}
/* 画不进时间线的（没截止日）单独列出来 + 一键去排期表，别让人自己找 */
#view-plans .pl-tl-noplan{padding:11px 18px;border-top:1px dashed var(--border);
  font-size:12px;color:var(--muted);line-height:1.7}
#view-plans .pl-tl-noplan b{font-family:'Fira Code',monospace;color:var(--cyan);font-weight:600}
#view-plans .pl-tl-noplan .lk{margin-left:8px;color:var(--cyan);font-weight:600;cursor:pointer}
#view-plans .pl-tl-noplan .lk:hover{text-decoration:underline}
/* 窄屏收窄标签列：时间线的价值在轨道，标签可以挤 */
@media(max-width:860px){
  #view-plans .pl-tl-grid{padding-left:138px}
  #view-plans .pl-tl-lb{left:-138px;width:126px}
  #view-plans .pl-tl-lb .badge{display:none}
}

/* ===== 实现工作项 #334 实现 AI 计划按钮与草案审核面板 =====
   延用 #133/#332 的两条命名约定：视图内规则 `#view-plans` 限定、弹窗靠 `pl-` 前缀隔离。
   本块唯一的新语义是**「AI 说的」与「代码算的」必须看得出区别**：
   AI 措辞一律低饱和 + 虚线框 + 显式标签，代码算出的数字走实底。
   把两者画成一个样子，PM 就会把推测当事实用来排期——这是这套面板最大的风险。 */

/* ---------- 详情页的 AI 按钮（只在详细计划出现，且只在有写权限时渲染） ---------- */
#view-plans .pl-ai-btn{color:#fff;font-weight:700;
  background:linear-gradient(120deg,var(--cyan-2),var(--primary) 60%,var(--violet));
  box-shadow:0 6px 18px rgba(30,144,200,.28)}
#view-plans .pl-ai-btn:hover{filter:brightness(1.05)}
/* 生成中：转圈 + 禁用。AI 要等几秒到几十秒，没有等待反馈的话人会以为点空了、连点好几下 */
#view-plans .pl-ai-btn .pl-ai-spin{display:inline-block;width:13px;height:13px;border-radius:50%;
  border:2px solid rgba(255,255,255,.42);border-top-color:#fff;animation:plAiSpin .7s linear infinite}
@keyframes plAiSpin{to{transform:rotate(360deg)}}

/* ---------- 弹窗外壳 ---------- */
.pl-ai-modal .modal-body{padding:0 24px 8px}
/* 只读横幅：这个面板的核心承诺——不点「保存排期」就一个字节都不落库。
   写在最上面而不是脚注里，PM 才敢放心点 AI、反复点 AI。 */
.pl-ai-ro{display:flex;align-items:flex-start;gap:9px;margin:14px 0 16px;padding:11px 14px;
  border-radius:var(--radius-sm);background:rgba(22,168,116,.07);
  border:1px solid rgba(22,168,116,.26);border-left:3px solid var(--ok);
  font-size:12px;color:var(--fg-2);line-height:1.6}
.pl-ai-ro b{color:#0F8159}
.pl-ai-ro .ic{flex-shrink:0;color:var(--ok);font-size:13px;line-height:1.5}
/* 草案级置信度 + 工期校准来源。逐行不给置信度——AiScheduleRow 里没有这个字段，
   按行编一个出来就是凭空造精度。 */
.pl-ai-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:14px}
.pl-ai-conf{font-size:11px;font-weight:700;padding:2px 9px;border-radius:20px;white-space:nowrap}
.pl-ai-conf.high{background:rgba(22,168,116,.13);color:#0F8159}
.pl-ai-conf.medium{background:rgba(224,146,14,.15);color:#B5740A}
.pl-ai-conf.low{background:rgba(160,166,192,.2);color:#6E7396}
.pl-ai-note{font-size:11.5px;color:var(--muted);line-height:1.6;flex:1;min-width:200px}

/* ---------- 四个审核区块的公用外壳 ---------- */
/* 条数挂在区块标题上：四块内容长短悬殊，不写数字的话「风险点」有没有内容得滚到才知道 */
.pl-ai-blk{margin-bottom:18px}
.pl-ai-blk-t{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:9px;
  font-family:var(--display);font-size:13.5px;font-weight:600;color:var(--violet)}
.pl-ai-blk-t .n{min-width:17px;padding:0 6px;border-radius:9px;background:var(--cyan);color:#fff;
  font-family:'Fira Code',monospace;font-size:10.5px;font-weight:700;line-height:17px;text-align:center}
.pl-ai-blk-t .n.zero{background:var(--dim)}
.pl-ai-blk-t .all{margin-left:auto;font-size:11.5px;font-weight:600;color:var(--cyan);cursor:pointer;user-select:none}
.pl-ai-blk-t .all:hover{opacity:.75}
.pl-ai-blk-sb{font-size:11.5px;font-weight:400;color:var(--dim);line-height:1.6;margin:-4px 0 9px}
.pl-ai-empty{padding:16px 14px;border-radius:var(--radius-sm);border:1px dashed var(--border-2);
  font-size:12px;color:var(--dim);line-height:1.6}

/* ---------- 区块一：排期建议（可改可弃） ---------- */
.pl-ai-sch{width:100%;border-collapse:separate;border-spacing:0;border:1px solid var(--border);
  border-radius:var(--radius-sm);overflow:hidden;background:var(--surface-3)}
.pl-ai-sch th{padding:9px 11px;font-size:10.5px}
.pl-ai-sch td{padding:9px 11px;border-top:1px solid var(--border);font-size:12.5px;vertical-align:middle}
.pl-ai-sch input[type=checkbox]{width:15px;height:15px;margin:0;accent-color:var(--cyan);cursor:pointer}
/* 取消采纳的行整行压暗，但**不隐藏**——PM 要能一眼看清自己弃了哪几条 */
.pl-ai-sch tr.off td{opacity:.45}
.pl-ai-sch .nm{color:var(--fg)}
.pl-ai-sch .nm .tt{color:var(--fg)}
.pl-ai-sch input[type=date]{width:100%;padding:5px 7px;border-radius:7px;
  font-family:'Fira Code',monospace;font-size:12px;color:var(--fg);
  background:#F4F7FC;border:1px solid var(--border-2);transition:border .15s,box-shadow .15s}
.pl-ai-sch input[type=date]:focus{outline:none;border-color:var(--cyan);background:#fff;
  box-shadow:0 0 0 3px rgba(30,144,200,.12)}
/* PM 手改过的格子压黄：改完一片日期后，哪几个是自己改的、哪几个还是 AI 原值，得分得出来 */
.pl-ai-sch input.edited{border-color:var(--warn);background:rgba(224,146,14,.08)}
.pl-ai-sch .dur{font-family:'Fira Code',monospace;color:var(--muted)}

/* ---------- 区块二：风险点 ---------- */
/* computed=true 走实底 + 实线左描边（数字由代码算，可直接拿来排期）；
   computed=false 是 AI 措辞，走虚线 + 不给底色。两者画成一个样子是这套面板最大的风险。 */
.pl-ai-risk{display:flex;align-items:flex-start;gap:9px;padding:10px 13px;margin-bottom:7px;
  border-radius:var(--radius-sm);background:var(--surface-3);border:1px solid var(--border);
  border-left:3px solid var(--dim);font-size:12.5px;color:var(--fg-2);line-height:1.6}
.pl-ai-risk:last-child{margin-bottom:0}
.pl-ai-risk.sev-high{border-left-color:var(--danger);background:rgba(229,71,110,.05)}
.pl-ai-risk.sev-mid{border-left-color:var(--warn);background:rgba(224,146,14,.05)}
.pl-ai-risk.sev-low{border-left-color:var(--border-2)}
.pl-ai-risk.guess{border-style:dashed;background:transparent}
.pl-ai-risk .sev{flex-shrink:0;font-size:10.5px;font-weight:700;padding:2px 8px;border-radius:6px;white-space:nowrap}
.pl-ai-risk.sev-high .sev{background:rgba(229,71,110,.13);color:#C42C54}
.pl-ai-risk.sev-mid .sev{background:rgba(224,146,14,.15);color:#B5740A}
.pl-ai-risk.sev-low .sev{background:rgba(39,39,110,.07);color:var(--muted)}
.pl-ai-risk .tx{flex:1;min-width:0}
/* 「代码算的 / AI 判断」直接写字，不只靠颜色暗示——色盲用户与截图转述都不会丢这个信息 */
.pl-ai-src{flex-shrink:0;font-size:10.5px;padding:1px 7px;border-radius:5px;white-space:nowrap}
.pl-ai-src.calc{background:rgba(30,144,200,.1);color:#176B9E}
.pl-ai-src.ai{background:transparent;border:1px dashed var(--border-2);color:var(--dim)}

/* ---------- 区块三：里程碑候选（逐条采纳，不采纳什么都不写） ---------- */
.pl-ai-ms{display:flex;align-items:center;gap:10px;padding:10px 13px;margin-bottom:7px;
  border-radius:var(--radius-sm);background:var(--surface-3);border:1px solid var(--border);
  border-left:3px dashed var(--border-2);font-size:12.5px}
.pl-ai-ms .dia{flex-shrink:0;width:9px;height:9px;background:#fff;border:2px solid var(--muted);transform:rotate(45deg)}
.pl-ai-ms .nm{font-weight:600;color:var(--fg)}
.pl-ai-ms .due{font-family:'Fira Code',monospace;font-size:12px;color:var(--muted);flex-shrink:0}
.pl-ai-ms .rs{flex:1;min-width:0;color:var(--muted);font-size:11.5px;line-height:1.5}
.pl-ai-ms .ops{margin-left:auto;flex-shrink:0}
/* 已采纳=已落库：菱形填实 + 整行压绿，与「还只是候选」一眼分开 */
.pl-ai-ms.done{border-left:3px solid var(--ok);background:rgba(22,168,116,.05)}
.pl-ai-ms.done .dia{background:var(--ok);border-color:var(--ok)}
.pl-ai-ms .ok{font-size:11.5px;font-weight:700;color:#0F8159;white-space:nowrap}

/* ---------- 区块四：遗漏检测（两层必须看得出区别，§4.4） ---------- */
/* 确定性层是 parent_id 查出来的**事实**（同需求的兄弟任务不在计划里），100% 准，给可点的
   「加入计划」；AI 推测层是跨需求猜的，准确率中等。两层画成一个样子，PM 就会拿猜测当事实排期。 */
.pl-ai-layers{font-size:11.5px;color:var(--muted);line-height:1.6;margin:-4px 0 10px;
  padding:9px 12px;border-radius:var(--radius-sm);background:rgba(39,39,110,.03);border:1px solid var(--border)}
.pl-ai-layers b{color:var(--violet)}
.pl-ai-miss{display:flex;align-items:center;gap:9px;padding:10px 13px;margin-bottom:7px;
  border-radius:var(--radius-sm);font-size:12.5px;color:var(--fg-2);line-height:1.6}
.pl-ai-miss:last-child{margin-bottom:0}
/* 确定性：实底 + 实线左描边，与「代码算的」风险同一副观感 */
.pl-ai-miss.sure{background:var(--surface-3);border:1px solid var(--border);border-left:3px solid var(--cyan)}
/* AI 推测：虚线 + 无底色 + 整行压淡。弱到不像个待办，才不会被顺手当事实执行 */
.pl-ai-miss.guess{background:transparent;border:1px dashed var(--border-2);
  border-left:3px dashed var(--dim);opacity:.88}
.pl-ai-miss .id{flex-shrink:0;font-family:'Fira Code',monospace;font-size:12px;color:var(--cyan);font-weight:600}
.pl-ai-miss .tt{min-width:0;font-weight:600;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pl-ai-miss.guess .tt{font-weight:500;color:var(--fg-2)}
.pl-ai-miss .pa{flex:1;min-width:0;color:var(--muted);font-size:11.5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pl-ai-miss .ops{margin-left:auto;flex-shrink:0}
.pl-ai-miss .ok{font-size:11.5px;font-weight:700;color:#0F8159;white-space:nowrap}
/* AI 推测行没有「建任务」按钮：本系统的需求/任务一律由 CC 走 /split 建，前端没有建 item 的路。
   给个假按钮点了报错，比明说「去 CC 建」糟得多。 */
.pl-ai-miss .hint{margin-left:auto;flex-shrink:0;font-size:11px;color:var(--dim);white-space:nowrap}

/* ---------- 弹窗脚：采纳计数 ---------- */
/* 「已采纳 N / M 条」写在按钮旁边而不是标题上：手要按下去的那一刻才最需要知道会存几条。
   N=0 时按钮置灰并改写文案——「保存排期」灰着不解释，人会以为页面坏了。 */
.pl-ai-foot-st{flex:1;min-width:180px;align-self:center;font-size:12px;color:var(--fg-2);line-height:1.6}
.pl-ai-foot-st b{color:var(--violet);font-weight:700}
.pl-ai-foot-st .warn{color:#B5740A;font-weight:600}
