/* ══════════ 디자인 토큰 (화면테마_시스템_규격서 §2 준수) ══════════
   테마 변경 = :root 의 이 10개 변수만 덮어쓰면 앱 전체가 즉시 따라온다.
   기존에 쓰던 이름(--bg2/--line/--txt …)은 아래에서 표준 토큰의 별칭으로 두어
   수백 줄의 기존 규칙을 건드리지 않고도 테마가 적용된다. */
:root{
  /* ── 코어 10종: 사용자가 조정 가능(면5 + 글자3 + 강조2) ── */
  --bg:#0d1117;      /* 배경면 */
  --sf:#161b22;      /* 표면1 — 카드·상단바 */
  --sf2:#1c2230;     /* 표면2 — 입력칸·호버 */
  --sf3:#21283a;     /* 표면3 — 태그·강조 표면 */
  --bd:#2a3140;      /* 테두리 */
  --tx:#e6edf3;      /* 기본 글자 */
  --txm:#8b949e;     /* 보조 글자 */
  --txd:#6e7681;     /* 흐린 글자 */
  --ac:#1d4ed8;      /* 강조(버튼·활성) */
  --ac2:#3b82f6;     /* 강조 밝음(링크·활성 텍스트) */

  /* ── 기존 이름 → 표준 토큰 별칭 (기존 CSS 그대로 동작) ── */
  --bg2:var(--sf); --bg3:var(--sf2); --line:var(--bd);
  --txt:var(--tx); --mut:var(--txm); --mut2:var(--txd);
  --pri:var(--ac2); --pri-d:var(--ac);

  /* ── 시맨틱(상태 의미 고정 — 테마에서 바꾸지 않음) ── */
  --ok:#22c55e; --warn:#eab308; --err:#ef4444; --info:#38bdf8;
  --run:#22c55e; --stop:#6b7280; --restart:#eab308; --errc:#ef4444;

  /* ── 상태 '글자'색 ──
     면(background)에 쓰는 색과 글자에 쓰는 색은 달라야 한다.
     어두운 배경에선 밝게 띄워야 읽히고, 밝은 배경에선 어둡게 눌러야 읽힌다.
     밝은 배경 값은 흰 바탕 기준 명도대비 4.5:1 이상으로 잡았다. */
  --okt:#4ade80; --warnt:#facc15; --errt:#f87171; --infot:#93c5fd;
  --linkt:#60a5fa;      /* 서비스 URL 등 링크 */
  --progt:#c4b5fd;      /* '프로그램' 태그 */
  --stopt:#9ca3af;      /* 중지 상태 */
  --bd2:#3a4456;        /* 호버 테두리(기본 테두리보다 한 단계 뚜렷) */
  --topbar:linear-gradient(180deg,#11161f,#0d1117);

  --r:10px; --r-sm:7px;
  --shadow:0 6px 24px rgba(0,0,0,.4);
  --font-scale:1;    /* 규격서 §5.3 — 배율 보정용으로 노출 */
  font-synthesis:none;
}
/* ── 밝은 배경 자동 보정 ──
   테마의 --bg 휘도를 재서 app.js가 html[data-lightui="1"] 을 붙인다.
   프리셋을 새로 만들어도 이 규칙 하나로 라이트 모드가 같이 따라온다.
   (이게 없으면 상단바는 검은 채로 남고, 초록·노랑 글씨가 흰 바탕에서 흐려진다) */
:root[data-lightui="1"]{
  /* 상태 배지·칩은 같은 계열의 옅은 배경 위에 얹히므로, 흰 바탕 기준보다
     한 단계 더 눌러야 실제 대비 4.5:1을 넘긴다(실측으로 맞춘 값). */
  --okt:#0f6b32; --warnt:#854d0e; --errt:#b91c1c; --infot:#1d4ed8;
  --linkt:#1d4ed8; --progt:#6d28d9; --stopt:#4b5563;
  --bd2:var(--txd);
  --topbar:linear-gradient(180deg,var(--sf2),var(--sf));
  --shadow:0 6px 24px rgba(0,0,0,.14);
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{
  background:var(--bg); color:var(--txt);
  font-family:"Segoe UI Variable","Segoe UI",-apple-system,system-ui,"Malgun Gothic",sans-serif;
  font-size:13px; line-height:1.45;
}
.mono{font-family:"Cascadia Code",ui-monospace,Consolas,monospace}
.muted{color:var(--mut)}
code{background:var(--bg3);padding:1px 5px;border-radius:4px;font-family:ui-monospace,Consolas,monospace}
h2{font-size:15px;margin:0}
button{font-family:inherit}

/* 상단 바 */
.topbar{
  position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:14px;
  /* ★ 여기가 하드코딩이면 라이트 테마에서 상단만 까맣게 남는다 — 토큰으로 뺀다 */
  padding:9px 16px;background:var(--topbar);
  border-bottom:1px solid var(--line);
}
.brand{display:flex;align-items:center;gap:9px}
.logo{font-size:22px}
.brand-txt{display:flex;flex-direction:column;line-height:1.1}
.brand-txt strong{font-size:15px;letter-spacing:.3px}
.ver-badge{font-size:10px;font-weight:600;color:var(--mut);background:color-mix(in srgb,var(--tx) 8%,transparent);border:1px solid var(--bd,#333);border-radius:6px;padding:1px 5px;margin-left:4px;letter-spacing:.2px;vertical-align:middle}
.brand-txt small{color:var(--mut);font-size:11px}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;margin-left:2px}
.dot-on{background:var(--ok);box-shadow:0 0 8px var(--ok)}
.dot-off{background:var(--err);box-shadow:0 0 8px var(--err)}
.top-center{flex:1;display:flex;align-items:center;justify-content:center;gap:12px}
.clock{color:var(--mut);font-variant-numeric:tabular-nums;font-size:12px}
.top-actions{display:flex;gap:8px}

.pill{padding:3px 10px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
.pill-ok{background:rgba(34,197,94,.15);color:var(--okt);border:1px solid rgba(34,197,94,.35)}
.pill-err{background:rgba(239,68,68,.15);color:var(--errt);border:1px solid rgba(239,68,68,.35)}
.pill-mut{background:var(--bg3);color:var(--mut)}

.btn{
  background:var(--bg3);color:var(--txt);border:1px solid var(--line);
  padding:6px 12px;border-radius:var(--r-sm);cursor:pointer;font-size:12.5px;font-weight:600;
  transition:.12s;white-space:nowrap;
}
.btn:hover{background:var(--sf2);border-color:var(--bd2)}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--pri);border-color:var(--pri);color:#fff}
.btn-primary:hover{background:var(--pri-d)}
.btn-ghost{background:transparent}
.btn-sm{padding:4px 8px;font-size:12px}
.btn-busy{background:rgba(234,179,8,.18);border-color:rgba(234,179,8,.55);color:var(--warnt);cursor:default}
.btn-busy:hover{background:rgba(234,179,8,.18)}
.btn-danger:hover{background:rgba(239,68,68,.2);border-color:var(--err);color:var(--errt)}

/* 요약 카드 */
.summary{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;padding:14px 16px 4px}
.card{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);padding:12px 14px;position:relative;overflow:hidden}
.card .lbl{color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.4px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ★ 값이 두 줄로 접히면 카드가 커지고 아래 화면 전체가 밀린다(네트워크 0 B/s → 103 B/s).
   줄바꿈을 막고 높이를 한 줄로 고정해, 숫자가 어떻게 바뀌어도 레이아웃이 흔들리지 않게 한다. */
.card .val{font-size:24px;font-weight:700;margin-top:3px;font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.25;height:1.25em}
/* 단위(B/s·KB/s)는 숫자보다 작게 — 좁은 카드에서도 한 줄에 들어간다 */
.card .val u{font-size:12.5px;font-weight:600;font-style:normal;text-decoration:none;
  color:var(--mut);margin-left:2px;letter-spacing:0}
.card .sub{color:var(--mut);font-size:11px;margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.card .sub u{font-style:normal;text-decoration:none}
.card .bar{height:4px;background:var(--bg3);border-radius:3px;margin-top:8px;overflow:hidden}
.card .bar > i{display:block;height:100%;border-radius:3px;transition:width .4s}
.card.accent-ok .val{color:var(--okt)}
.card.accent-warn .val{color:var(--warnt)}
.card.accent-err .val{color:var(--errt)}

/* 차트 */
.charts{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;padding:8px 16px}
.chartcard{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);padding:8px 12px 4px}
.ch-h{display:flex;justify-content:space-between;align-items:baseline;font-size:11px;color:var(--mut)}
.ch-h b{color:var(--txt);font-size:13px;font-variant-numeric:tabular-nums}
.chartcard canvas{width:100%;display:block}

/* 서비스 */
.services-wrap,.peers-wrap{padding:10px 16px}
.svc-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:12px}
.svc-tools{display:flex;gap:8px;align-items:center}
.input{
  background:var(--bg);border:1px solid var(--line);color:var(--txt);
  padding:6px 10px;border-radius:var(--r-sm);font-size:12.5px;width:100%;
}
.input:focus{outline:none;border-color:var(--pri)}
#svcSearch{width:230px}
select.input{cursor:pointer}

.service-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:12px}
/* 그룹(분류) 섹션 */
.svc-groups{display:block}
.svc-group{margin-bottom:16px}
.svc-group-head{display:flex;align-items:center;gap:9px;padding:7px 4px;cursor:pointer;border-bottom:1px solid var(--line);margin-bottom:11px;user-select:none}
.svc-group-head:hover .gh-name{color:var(--tx)}
.gh-caret{color:var(--mut);font-size:11px;transition:.15s;width:10px;text-align:center}
.svc-group.collapsed .gh-caret{transform:rotate(-90deg)}
.gh-name{font-weight:700;font-size:14px}
.gh-count{font-size:11px;color:var(--mut);background:var(--bg3);padding:1px 8px;border-radius:999px}
.gh-actions{margin-left:auto;display:flex;gap:5px}
.gh-actions .icon-btn{padding:2px 8px;font-size:12px}
.svc-group.collapsed .gh-body{display:none}
.gh-grip{cursor:grab;color:var(--mut2);font-size:14px;line-height:1;user-select:none}
.gh-grip:hover{color:var(--txt)}
.gh-grip:active{cursor:grabbing}
.svc-group.gdragging{opacity:.5}
.svc-group.gdragover .svc-group-head{background:rgba(59,130,246,.1);border-bottom:2px solid var(--pri)}
.svc{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);padding:13px 14px;display:flex;flex-direction:column;gap:9px;transition:.15s}
.svc:hover{border-color:var(--bd2)}
.svc.st-errored{border-color:rgba(239,68,68,.5)}
.svc-top{display:flex;align-items:flex-start;gap:9px}
.svc-grip{cursor:grab;color:var(--mut2);font-size:15px;line-height:1;user-select:none;flex:none;padding-top:1px}
.svc-grip:hover{color:var(--txt)}
.svc-grip:active{cursor:grabbing}
.svc.dragging{opacity:.45}
.svc.dragover{outline:2px dashed var(--pri);outline-offset:2px}
.svc-thumb{width:54px;height:40px;border-radius:6px;object-fit:cover;background:var(--bg3);border:1px solid var(--line);flex:none}
.svc-id{flex:1;min-width:0}
.svc-name{font-weight:700;font-size:14px;display:flex;align-items:center;gap:7px}
.svc-name .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.svc-desc{color:var(--mut);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.st{font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;white-space:nowrap;display:inline-flex;align-items:center;gap:5px}
.st::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}
.st-running{color:var(--okt);background:rgba(34,197,94,.13)}
.st-stopped{color:var(--stopt);background:rgba(156,163,175,.13)}
.st-restarting,.st-starting{color:var(--warnt);background:rgba(234,179,8,.13)}
.st-errored{color:var(--errt);background:rgba(239,68,68,.13)}
.st-disabled{color:#6b7280;background:rgba(107,114,128,.1)}
@keyframes pulse{50%{opacity:.45}}
.st-restarting::before,.st-starting::before{animation:pulse 1s infinite}

.tags{display:flex;gap:5px;flex-wrap:wrap}
.tag{background:var(--bg3);color:var(--mut);font-size:10.5px;padding:1px 7px;border-radius:5px}
.tag.grp{color:var(--infot);background:rgba(59,130,246,.12)}
.tag.prog{color:var(--progt);background:rgba(139,92,246,.16)}

.svc-linkrow{display:flex;align-items:center;gap:8px;justify-content:space-between}
.svc-url{color:var(--linkt);font-size:12px;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:"Cascadia Code",ui-monospace,Consolas,monospace;min-width:0}
.svc-url:hover{text-decoration:underline}
.svc-clients{font-size:11px;color:var(--okt);background:rgba(34,197,94,.13);padding:2px 8px;border-radius:999px;white-space:nowrap;flex:none}
.svc-metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;border-top:1px solid var(--line);padding-top:9px}
.met{display:flex;flex-direction:column}
.met .m-l{color:var(--mut2);font-size:10px}
.met .m-v{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
.svc-actions{display:flex;gap:5px;flex-wrap:wrap}
.svc-actions .btn{padding:4px 9px;font-size:11.5px}
.icon-btn{background:transparent;border:1px solid var(--line);color:var(--mut);border-radius:6px;padding:4px 8px;cursor:pointer;font-size:13px}
.icon-btn:hover{color:var(--txt);background:var(--bg3)}

.empty{padding:40px;text-align:center;color:var(--mut);background:var(--bg2);border:1px dashed var(--line);border-radius:var(--r)}

/* peers */
.peer-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(440px,1fr));gap:14px}
.peer{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);padding:14px 16px}
.peer h4{margin:0 0 4px;display:flex;justify-content:space-between;align-items:center;gap:8px}
.peer-title{display:flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap}
.peer-sum{margin-top:7px;font-size:12px}
/* 원격 노드 서비스: 표시개수만큼씩 서브카드(열)로 묶어 나란히 배치 */
/* 서비스 카드를 grid auto-fill로 옆으로 채운다(폭에 맞춰 여러 열, 세로로 안 쌓임) */
.peer-cols{margin-top:10px;border-top:1px solid var(--line);padding-top:11px;display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:9px;align-items:start}
.peer-more{grid-column:1/-1;text-align:center;color:var(--mut);font-size:11px;padding:4px}
/* 서비스 카드: 2줄 (위 상태+이름 / 아래 메트릭+버튼) */
.peer-svc{display:flex;flex-direction:column;gap:6px;padding:9px 11px;background:var(--bg);border:1px solid var(--line);border-radius:8px}
.ps-head{display:flex;align-items:center;gap:8px;min-width:0}
.peer-svc .st{font-size:10.5px;padding:2px 8px;flex:none}
.ps-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;font-weight:600}
.ps-foot{display:flex;align-items:center;justify-content:space-between;gap:8px}
.ps-met{color:var(--mut);font-size:11.5px;white-space:nowrap;font-variant-numeric:tabular-nums}
.ps-act{display:flex;gap:4px;flex:none}
.ps-act .icon-btn{padding:3px 9px;font-size:12px}

/* 진행율 바 (배포 등) */
.prog{height:9px;background:var(--bg3);border-radius:6px;overflow:hidden}
.prog-bar{height:100%;width:0;border-radius:6px;background:linear-gradient(90deg,var(--pri),var(--info));transition:width .2s ease}

/* 외부 서비스 */
.ext-wrap{margin-top:10px;padding:10px 16px}
.ext-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}
.ext-card{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);padding:12px 14px;display:flex;flex-direction:column;gap:6px}
.ext-top{display:flex;align-items:center;gap:8px;justify-content:space-between}
.ext-top b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ext-url{font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ext-note{font-size:11px;white-space:pre-wrap}

/* 메모 */
.notes-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}
.note-item{background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:6px}
.note-h{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
.note-h b{font-size:13px;word-break:break-word}
.note-act{display:flex;gap:2px;flex:none}
.note-act .icon-btn{padding:2px 6px;font-size:12px}
.note-body{margin:0;font-size:12px;white-space:pre-wrap;word-break:break-word;max-height:160px;overflow:auto;color:var(--txt)}
.sec-row{display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--line)}
.sec-row .muted{width:64px;flex:none;font-size:12px}
.sec-row code{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.foot{display:flex;justify-content:space-between;padding:14px 16px 28px;color:var(--mut);font-size:11.5px}
.foot a{color:var(--mut)}

/* 토스트 */
.toasts{position:fixed;right:16px;bottom:16px;display:flex;flex-direction:column;gap:8px;z-index:60;max-width:340px}
.toast{background:var(--bg2);border:1px solid var(--line);border-left:4px solid var(--info);border-radius:8px;padding:10px 14px;box-shadow:var(--shadow);animation:slidein .25s}
.toast.t-error{border-left-color:var(--err)}
.toast.t-warn{border-left-color:var(--warn)}
.toast.t-info{border-left-color:var(--info)}
.toast.t-ok{border-left-color:var(--ok)}
.toast b{display:block;margin-bottom:2px}
.toast small{color:var(--mut)}
@keyframes slidein{from{transform:translateX(20px);opacity:0}}

/* 모달 */
.modal{position:fixed;inset:0;background:rgba(0,0,0,.55);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:50;padding:20px}
.modal[hidden]{display:none}
.modal-box{background:var(--bg2);border:1px solid var(--line);border-radius:14px;width:560px;max-width:100%;max-height:90vh;display:flex;flex-direction:column;box-shadow:var(--shadow)}
.modal-lg{width:920px}
.modal-head{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-bottom:1px solid var(--line)}
.modal-head h3{margin:0;font-size:15px}
.modal-body{padding:16px 18px;overflow:auto;display:flex;flex-direction:column;gap:11px}
.modal-foot{display:flex;justify-content:flex-end;align-items:center;gap:9px;padding:12px 18px;border-top:1px solid var(--line)}
.modal-foot .err{margin-right:auto}
.x{background:none;border:none;color:var(--mut);font-size:16px;cursor:pointer}
.x:hover{color:var(--txt)}
label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--mut)}
label .input,label textarea{color:var(--txt)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:11px}
.chk{flex-direction:row;align-items:center;gap:7px;color:var(--txt)}
.chk input{width:15px;height:15px}
.adv{border:1px solid var(--line);border-radius:8px;padding:6px 10px}
.adv summary{cursor:pointer;color:var(--mut);font-size:12px}
.adv > label,.adv > .row2{margin-top:10px}
.err{color:var(--errt);font-size:12px}

/* 로그 뷰어 */
.log-controls{display:flex;align-items:center;gap:8px}
.log-controls .input{width:auto}
#logSearch{width:160px}
.logview{margin:0;padding:14px 16px;overflow:auto;background:#0a0d12;font-family:"Cascadia Code",ui-monospace,Consolas,monospace;font-size:12px;line-height:1.5;flex:1;min-height:300px;max-height:64vh;white-space:pre-wrap;word-break:break-all}
.logview .ln{display:block}
.logview .l-error{color:var(--errt)}
.logview .l-fatal{color:#fff;background:rgba(239,68,68,.25)}
.logview .l-warn{color:var(--warnt)}
.logview .l-sys{color:var(--linkt)}
.logview .l-info{color:var(--tx)}
.logview .lt{color:var(--mut2);margin-right:8px}

/* 실행 중 서버 발견 */
.discover-list{display:flex;flex-direction:column;gap:8px;max-height:62vh;overflow:auto}
.disc-item{display:flex;justify-content:space-between;align-items:center;gap:12px;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:9px 12px}
.disc-item.reg{opacity:.55}
.disc-main{min-width:0;flex:1}
.disc-title{font-size:13px;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.disc-rt{background:rgba(59,130,246,.15);color:var(--infot);font-size:10px;padding:1px 7px;border-radius:5px}
.disc-tag{background:rgba(34,197,94,.13);color:var(--okt);font-size:10px;padding:1px 7px;border-radius:5px}
.disc-port{color:var(--mut);font-size:12px}
.disc-cmd{font-size:11px;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:2px}
.disc-act{flex:none}

.set-section{border-top:1px solid var(--line);padding-top:12px;margin-top:6px}
.set-section h4{margin:0 0 8px;color:var(--info);font-size:13px}

@media(max-width:1100px){.summary{grid-template-columns:repeat(3,1fr)}.charts{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.summary{grid-template-columns:repeat(2,1fr)}.charts{grid-template-columns:1fr}.top-center{display:none}.service-grid{grid-template-columns:1fr}}

/* 배포 진행 배너(비차단) — 화면 하단 고정 */
.deploy-banner{position:fixed;left:50%;bottom:18px;transform:translateX(-50%);z-index:60;
  width:min(560px,calc(100% - 32px));background:var(--bg2);border:1px solid var(--line);
  border-radius:12px;box-shadow:var(--shadow);padding:12px 14px;animation:dbUp .2s ease}
@keyframes dbUp{from{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%,0)}}
.deploy-banner.err{border-color:var(--err)}
.deploy-banner.done{border-color:var(--ok)}
.db-row{display:flex;align-items:center;gap:11px}
.db-icon{font-size:18px;flex:none}
.db-main{flex:1;min-width:0}
.db-label{font-size:12.5px;color:var(--txt);margin-bottom:6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.db-pct{font-size:12px;font-weight:600;color:var(--info);flex:none;min-width:38px;text-align:right}
.deploy-banner .x{flex:none;background:none;border:none;color:var(--mut);cursor:pointer;font-size:14px;padding:2px 4px;border-radius:5px}
.deploy-banner .x:hover{color:var(--txt);background:var(--bg3)}

/* 요약 카드 '세부' 버튼 + 리소스 세부 모달 */
.card-detail{position:absolute;top:8px;right:8px;background:var(--bg3);border:1px solid var(--line);color:var(--mut);font-size:10px;padding:2px 8px;border-radius:6px;cursor:pointer;z-index:2}
.card-detail:hover{color:var(--txt);border-color:var(--pri)}
.res-list{display:flex;flex-direction:column;gap:3px;max-height:60vh;overflow:auto}
.res-row{display:grid;grid-template-columns:92px 1fr 130px 72px;align-items:center;gap:9px;padding:3px 2px;font-size:12px;border-bottom:1px solid rgba(255,255,255,.03)}
.res-node{color:var(--mut);font-size:10.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.res-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.res-bar{height:8px;background:var(--bg3);border-radius:5px;overflow:hidden}
.res-bar i{display:block;height:100%;border-radius:5px}
.res-val{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--txt);font-weight:600}

/* 리소스 세부 모달 노드 탭 */
.res-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
.res-tab{background:var(--bg3);border:1px solid var(--line);color:var(--mut);font-size:12px;padding:4px 11px;border-radius:999px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}
.res-tab:hover{color:var(--txt);border-color:var(--bd2)}
.res-tab.active{background:var(--pri);border-color:var(--pri);color:#fff}
.res-tabn{font-size:10px;background:rgba(255,255,255,.18);padding:0 6px;border-radius:999px;font-variant-numeric:tabular-nums}
.res-tab.active .res-tabn{background:rgba(255,255,255,.28)}

/* 서비스 카드 스파크라인 */
.svc-spark{display:flex;gap:12px;margin-top:2px}
.svc-spark .sp{flex:1;display:flex;align-items:center;gap:6px;min-width:0}
.sp-l{font-size:10px;color:var(--mut);width:26px;flex:none}
.spark{flex:1;height:22px;display:block}
.svc-flash{animation:svcFlash 1.6s ease}
@keyframes svcFlash{0%,100%{box-shadow:none}20%,60%{box-shadow:0 0 0 2px var(--pri),0 0 18px rgba(59,130,246,.5)}}
/* 리소스 세부: 스파크라인 열 + 클릭 이동 */
.res-row{grid-template-columns:80px 1fr 66px 84px 58px}
.res-click{cursor:pointer;border-radius:6px}
.res-click:hover{background:rgba(59,130,246,.08)}
.res-spark{height:20px;overflow:hidden}
.res-spark .spark{height:20px;width:100%}

/* 선택적 배포: 파일 트리 */
.dp-toolbar{display:flex;align-items:center;gap:8px;margin:4px 0 8px}
.dp-toolbar #dpSummary{margin-left:auto;font-size:12px;font-variant-numeric:tabular-nums}
.dp-tree{max-height:46vh;overflow:auto;border:1px solid var(--line);border-radius:8px;padding:6px;background:var(--bg)}
.dp-row{display:flex;align-items:center;gap:7px;padding:2px 4px;border-radius:5px;white-space:nowrap}
.dp-row:hover{background:rgba(255,255,255,.03)}
.dp-caret{width:14px;text-align:center;color:var(--mut);cursor:pointer;font-size:11px;flex:none}
.dp-ck{flex:none;cursor:pointer}
.dp-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:12.5px}
.dp-name.dir{cursor:pointer;font-weight:600}
.dp-size{color:var(--mut);font-size:11px;flex:none;font-variant-numeric:tabular-nums}

/* ══════════ SSM — 서비스 구성도 ══════════ */
.ssm-wrap{padding:10px 16px}
.ssm-list{display:flex;flex-direction:column;gap:12px}
.ssm-card{background:var(--bg2);border:1px solid var(--line);border-left:3px solid var(--stop);border-radius:var(--r);padding:12px 14px}
.ssm-card.ok{border-left-color:var(--ok)}
.ssm-card.warn{border-left-color:var(--warn)}
.ssm-card.down{border-left-color:var(--err)}
.ssm-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.ssm-title{display:flex;align-items:center;gap:8px;min-width:0}
.ssm-title b{font-size:14px}
.ssm-acts{display:flex;align-items:center;gap:8px}
.ssm-meta{font-size:11.5px}
.ssm-desc{font-size:12px;margin-top:3px}
.ssm-co{font-size:11px;padding:1px 7px;border-radius:20px;background:color-mix(in srgb,var(--co,#4ea1ff) 18%,transparent);
  border:1px solid color-mix(in srgb,var(--co,#4ea1ff) 45%,transparent);color:var(--co,#4ea1ff)}
.ssm-pill{font-size:11px;font-weight:700;padding:2px 9px;border-radius:20px;letter-spacing:.2px}
.ssm-pill.ok{background:rgba(34,197,94,.16);color:var(--okt)}
.ssm-pill.warn{background:rgba(234,179,8,.16);color:var(--warnt)}
.ssm-pill.down{background:rgba(239,68,68,.18);color:var(--errt)}
.ssm-pill.unk{background:rgba(139,148,158,.16);color:var(--mut)}
.ssm-cause{margin-top:8px;padding:7px 10px;border-radius:var(--r-sm);font-size:12.5px;
  background:rgba(239,68,68,.10);border:1px solid rgba(239,68,68,.35);color:var(--errt)}
.ssm-canvas{margin-top:10px;overflow-x:auto;padding-bottom:4px}
.ssm-svg{display:block;color:var(--mut2)}

/* 구성도 노드 박스 */
.ssm-node{cursor:pointer}
.ssm-node rect{fill:var(--bg3);stroke:var(--line);stroke-width:1.5}
.ssm-node:hover rect{stroke:var(--pri)}
.ssm-node:focus{outline:none}
.ssm-node:focus rect{stroke:var(--pri)}
.ssm-node .ssm-ico{font-size:15px}
.ssm-node .ssm-lb{fill:var(--txt);font-size:12.5px;font-weight:600}
.ssm-node .ssm-sub{fill:var(--mut);font-size:10px}
.ssm-node .ssm-dt{fill:var(--mut2);font-size:9.5px}
.ssm-node .ssm-dot{fill:var(--stop)}
.ssm-node .ssm-warn{fill:var(--warn);font-size:9px;font-weight:700}
.ssm-node.ok rect{stroke:rgba(34,197,94,.55)}
.ssm-node.ok .ssm-dot{fill:var(--ok)}
.ssm-node.warn rect{stroke:rgba(234,179,8,.6)}
.ssm-node.warn .ssm-dot{fill:var(--warn)}
.ssm-node.down rect{stroke:var(--err);fill:rgba(239,68,68,.12)}
.ssm-node.down .ssm-dot{fill:var(--err)}
.ssm-node.impacted rect{stroke:rgba(234,179,8,.55);stroke-dasharray:4 3;fill:rgba(234,179,8,.07)}
.ssm-node.impacted .ssm-dot{fill:var(--warn)}
.ssm-link{fill:none;stroke:var(--line);stroke-width:2}
.ssm-link.dead{stroke:rgba(239,68,68,.5);stroke-dasharray:5 4}

/* 구성도 편집기 */
.modal-wide{width:940px}
.ssm-ed-head{display:flex;align-items:center;gap:10px;margin-top:4px}
.ssm-ed-head small{flex:1}
.ssm-ed-rows{display:flex;flex-direction:column;gap:6px}
.ssm-row{display:flex;align-items:center;gap:6px}
.ssm-row .input{height:30px;padding:3px 7px;font-size:12.5px}
.ssm-row .n-type{width:132px;flex:none}
.ssm-row .n-label{width:150px;flex:none}
.ssm-row .n-kind{width:150px;flex:none}
.ssm-row .n-args{display:flex;gap:5px;flex:1;min-width:0}
.ssm-row .n-args .input{flex:1;min-width:0}
.ssm-grip{color:var(--mut2);cursor:default;font-size:11px;flex:none}
.ssm-ed-hint{font-size:11.5px}

/* 회사 관리 */
.co-list{display:flex;flex-direction:column;gap:5px;margin-top:6px}
.co-row{display:flex;align-items:center;gap:9px;padding:6px 9px;background:var(--bg3);border-radius:var(--r-sm)}
.co-row b{flex:1}
.co-dot{width:11px;height:11px;border-radius:50%;background:var(--mut2);flex:none}

/* ══════════ 보기 전환(탭) · 표시 크기 ══════════ */
.viewbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:2px 16px 8px}
.viewtabs{display:flex;gap:4px;background:var(--bg2);border:1px solid var(--line);border-radius:9px;padding:3px}
.vtab{background:none;border:0;color:var(--mut);padding:5px 14px;border-radius:7px;cursor:pointer;font-size:12.5px;font-weight:600}
.vtab:hover{color:var(--txt)}
.vtab.on{background:var(--bg3);color:var(--txt);box-shadow:0 1px 4px rgba(0,0,0,.3)}
.viewsize{display:flex;align-items:center;gap:4px;font-size:11.5px}
.szbtn{background:var(--bg2);border:1px solid var(--line);color:var(--mut);padding:3px 9px;border-radius:6px;cursor:pointer;font-size:11.5px}
.szbtn:hover{color:var(--txt)}
.szbtn.on{background:var(--pri);border-color:var(--pri);color:#fff}

/* 탭별 표시 — 상단(요약/차트)은 항상 보인다 */
body[data-view="svc"] .ssm-wrap{display:none}
body[data-view="ssm"] .services-wrap,
body[data-view="ssm"] .peers-wrap,
body[data-view="ssm"] .ext-wrap{display:none}

/* 표시 크기: '작게'면 상단 자원(요약+차트)이 2단 → 1단으로 붙는다 */
.topgrid{display:block}
body[data-size="sm"]{font-size:12px}
body[data-size="sm"] .topgrid{display:grid;grid-template-columns:minmax(320px,1fr) minmax(420px,1.4fr);gap:8px;align-items:start}
body[data-size="sm"] .summary,body[data-size="sm"] .charts{padding:8px 0}
body[data-size="sm"] .topgrid .summary{padding-left:16px}
body[data-size="sm"] .topgrid .charts{padding-right:16px}
/* 좌우로 붙은 만큼 각 영역은 한 줄로 눌러 담아 실제 높이를 줄인다 */
body[data-size="sm"] .summary{grid-template-columns:repeat(6,minmax(0,1fr));gap:6px}
body[data-size="sm"] .charts{grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}
body[data-size="sm"] .summary>*{padding:6px 8px}
body[data-size="sm"] .chartcard{padding:5px 8px}
body[data-size="sm"] .chartcard canvas{height:34px}
body[data-size="sm"] .ssm-row .input{height:28px}
body[data-size="lg"]{font-size:14px}
body[data-size="lg"] .chartcard canvas{height:78px}
body[data-size="lg"] .ssm-node .ssm-lb{font-size:13.5px}
@media(max-width:1100px){body[data-size="sm"] .topgrid{display:block}}

/* ══════════ 상위 연결 선택 팝오버(분기·합류) ══════════ */
.link-pop{position:fixed;z-index:60;width:280px;background:var(--bg2);border:1px solid var(--line);
  border-radius:10px;box-shadow:var(--shadow);padding:8px;font-size:12.5px}
.lp-head{color:var(--mut);padding:2px 4px 6px;border-bottom:1px solid var(--line);margin-bottom:6px}
.lp-item{display:flex;align-items:center;gap:7px;padding:5px 6px;border-radius:6px;cursor:pointer}
.lp-item:hover{background:var(--bg3)}
.lp-auto{border-bottom:1px solid var(--line);margin-bottom:4px;padding-bottom:7px}
.lp-list{max-height:190px;overflow:auto}
.lp-foot{display:flex;justify-content:flex-end;padding-top:7px}
.n-link.on{border-color:var(--pri);color:var(--pri)}

/* ══════════ 경로 자동 탐색 ══════════ */
.ssm-auto{display:flex;align-items:center;gap:7px;padding:9px 11px;background:var(--bg3);
  border:1px solid var(--line);border-radius:var(--r-sm);flex-wrap:wrap}
.ssm-auto b{font-size:12.5px;flex:none}
.ssm-auto .input{flex:1;min-width:130px;height:30px;padding:3px 7px;font-size:12.5px}
.ssm-auto-note{font-size:12px;padding:8px 11px;background:rgba(34,197,94,.08);
  border:1px solid rgba(34,197,94,.3);border-radius:var(--r-sm);color:var(--okt)}
.ssm-auto-note ul{margin:5px 0 0;padding-left:18px;color:var(--mut)}

/* ══════════ 구성 요소 상세 팝업 ══════════ */
.sn-grid{display:grid;grid-template-columns:92px 1fr;gap:7px 12px;align-items:start;font-size:12.5px}
.sn-grid>span{color:var(--mut)}
.sn-grid>b{font-weight:500;min-width:0;word-break:break-word}
.sn-chip{display:inline-block;font-size:11px;padding:1px 8px;border-radius:20px;margin:0 3px 3px 0;
  background:var(--bg3);border:1px solid var(--line)}
.sn-chip.ok{border-color:rgba(34,197,94,.5);color:var(--okt)}
.sn-chip.warn,.sn-chip.impacted{border-color:rgba(234,179,8,.5);color:var(--warnt)}
.sn-chip.down{border-color:rgba(239,68,68,.5);color:var(--errt)}
.sn-imp{color:var(--warn);font-size:11.5px;margin-left:6px}
.sn-svc{margin-top:12px;padding:9px 11px;background:var(--bg3);border-radius:var(--r-sm)}
.sn-svc-h{color:var(--mut);font-size:11.5px;margin-bottom:6px}
.sn-svc-b{display:flex;align-items:center;gap:14px;flex-wrap:wrap;font-size:12.5px}
.sn-hint{margin-top:12px;padding:9px 11px;font-size:12.5px;border-radius:var(--r-sm);
  background:rgba(234,179,8,.10);border:1px solid rgba(234,179,8,.35);color:var(--warnt)}

/* 상세 팝업 안에서 바로 수정 */
.sn-edit{display:flex;flex-direction:column;gap:10px}
.sn-edit label{display:grid;grid-template-columns:92px 1fr;align-items:center;gap:12px;color:var(--mut);font-size:12.5px}
.sn-edit .input{height:32px;padding:4px 8px;font-size:12.5px}
.sn-edit .n-args{display:flex;gap:6px;min-width:0}
.sn-edit .n-args .input{flex:1;min-width:0}
.sn-edit .n-args>.muted{align-self:center}

/* ══════════ 섹션 순서 변경 (손잡이 드래그) ══════════ */
#sections{display:block}
.sec-grip{display:inline-block;cursor:grab;color:var(--mut2);font-size:13px;line-height:1;
  margin-right:7px;user-select:none;vertical-align:1px}
.sec-grip:hover{color:var(--txt)}
.sec-grip:active{cursor:grabbing}
[data-sec].sec-dragging{opacity:.4}
/* 드래그 중에는 각 섹션 경계를 보여줘 어디에 놓이는지 알기 쉽게 */
.sec-dragging-on [data-sec]{outline:1px dashed var(--line);outline-offset:3px;border-radius:var(--r)}
.sec-dragging-on [data-sec].sec-dragging{outline:2px dashed var(--pri)}
#btnSecReset{margin-left:6px}
.sec-move{display:inline-flex;gap:2px;margin-right:8px;vertical-align:1px}
.sec-mv{background:var(--bg3);border:1px solid var(--line);color:var(--mut);
  width:20px;height:18px;line-height:1;border-radius:4px;cursor:pointer;font-size:9px;padding:0}
.sec-mv:hover:not(:disabled){color:var(--txt);border-color:var(--bd2)}
.sec-mv:disabled{opacity:.3;cursor:default}

/* ══════════ 접속 현황 (옵션) ══════════ */
.conn-wrap{padding:10px 16px}
.vs-sep{display:inline-block;width:1px;height:16px;background:var(--line);margin:0 4px}
#btnConnPanel.on{background:var(--pri);border-color:var(--pri);color:#fff}
.conn-chk{display:flex;align-items:center;gap:5px;color:var(--mut);font-size:12px;cursor:pointer;white-space:nowrap}
.conn-grid{display:grid;grid-template-columns:1.15fr 1fr;gap:12px}
@media(max-width:900px){.conn-grid{grid-template-columns:1fr}}
.conn-col{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.conn-h{padding:8px 12px;border-bottom:1px solid var(--line);color:var(--mut);font-size:11.5px;
  text-transform:uppercase;letter-spacing:.4px}
.conn-list{max-height:280px;overflow:auto}
.conn-row{display:flex;align-items:center;gap:10px;padding:7px 12px;border-bottom:1px solid rgba(42,49,64,.5);font-size:12.5px}
.conn-row:last-child{border-bottom:0}
.conn-row:hover{background:var(--bg3)}
.conn-ip{flex:none;width:132px;color:var(--infot);overflow:hidden;text-overflow:ellipsis}
.conn-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conn-port{flex:none;width:78px;color:var(--mut);font-size:11.5px}
.conn-n{flex:none;min-width:26px;text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.conn-cl{flex:none;width:66px;text-align:right;font-size:11.5px}
.conn-svcs{flex:1;min-width:0;color:var(--mut);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conn-svcs i{font-style:normal;color:var(--txt);background:var(--bg3);border-radius:4px;padding:0 4px;margin-left:3px;font-size:10.5px}
.conn-empty{padding:16px 12px;text-align:center;font-size:12.5px}

/* ══════════ 가동률(SLA) · 장애 이력 ══════════ */
.sla-wrap,.disk-wrap{padding:10px 16px}
.sla-win{padding:2px}
.sla-win .vtab{padding:3px 10px;font-size:11.5px}
.sla-list{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.sla-row{display:flex;align-items:center;gap:10px;padding:8px 12px;border-bottom:1px solid rgba(42,49,64,.5);
  font-size:12.5px;cursor:pointer}
.sla-row:last-child{border-bottom:0}
.sla-row:hover{background:var(--bg3)}
.sla-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.sla-bar{flex:none;width:110px;height:6px;background:var(--bg3);border-radius:3px;overflow:hidden}
.sla-bar>i{display:block;height:100%;background:var(--ok);border-radius:3px}
.sla-row.warn .sla-bar>i{background:var(--warn)}
.sla-row.bad .sla-bar>i{background:var(--err)}
.sla-row.unk .sla-bar>i{background:var(--mut2)}
.sla-pct{flex:none;width:62px;text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.sla-row.ok .sla-pct{color:var(--okt)}
.sla-row.warn .sla-pct{color:var(--warnt)}
.sla-row.bad .sla-pct{color:var(--errt)}
.sla-cnt{flex:none;width:44px;text-align:right;color:var(--mut)}
.sla-down{flex:none;width:88px;text-align:right;color:var(--mut);font-variant-numeric:tabular-nums}
.sla-last{flex:none;width:170px;text-align:right;color:var(--mut);font-size:11.5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:1000px){.sla-last,.sla-down{display:none}}

/* 장애 이력 상세 */
.sla-kpi{display:grid;grid-template-columns:repeat(5,1fr);gap:9px}
.sla-kpi>div{background:var(--bg3);border-radius:var(--r-sm);padding:9px 11px;display:flex;flex-direction:column;gap:2px}
.sla-kpi span{color:var(--mut);font-size:11px}
.sla-kpi b{font-size:17px;font-variant-numeric:tabular-nums}
.sla-kpi b.ok{color:var(--okt)}.sla-kpi b.warn{color:var(--warnt)}.sla-kpi b.bad{color:var(--errt)}
.sla-tl{display:flex;height:16px;border-radius:5px;overflow:hidden;background:var(--bg3);margin-top:12px}
.sla-seg{display:block;height:100%}
.sla-seg.up{background:var(--ok)}.sla-seg.down{background:var(--err)}
.sla-seg.off{background:var(--mut2)}.sla-seg.gap{background:repeating-linear-gradient(45deg,var(--bd2),var(--bd2) 4px,var(--bd) 4px,var(--bd) 8px)}
.sla-legend{display:flex;gap:14px;margin-top:7px;font-size:11.5px;flex-wrap:wrap}
.sla-legend span{display:flex;align-items:center;gap:5px}
.sla-legend i{width:10px;height:10px;border-radius:3px;display:inline-block}
.sla-legend i.up{background:var(--ok)}.sla-legend i.down{background:var(--err)}
.sla-legend i.off{background:var(--mut2)}
.sla-legend i.gap{background:repeating-linear-gradient(45deg,var(--bd2),var(--bd2) 3px,var(--bd) 3px,var(--bd) 6px)}
.sla-h4{margin:16px 0 7px;font-size:13px}
.sla-inc{max-height:260px;overflow:auto;background:var(--bg3);border-radius:var(--r-sm)}
.sla-inc-row{display:flex;align-items:center;gap:9px;padding:7px 11px;border-bottom:1px solid rgba(42,49,64,.6);font-size:12px}
.sla-inc-row:last-child{border-bottom:0}
.sla-inc-row b{margin-left:auto;color:var(--errt)}
.sla-reason{flex:none;width:76px;text-align:right;color:var(--mut);font-size:11px}

/* ══════════ 디스크 ══════════ */
.disk-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px}
.disk-card{background:var(--bg2);border:1px solid var(--line);border-left:3px solid var(--ok);
  border-radius:var(--r);padding:11px 13px}
.disk-card.warn{border-left-color:var(--warn)}
.disk-card.crit{border-left-color:var(--err)}
.disk-card.ign{border-left-color:var(--mut2);opacity:.65}
.disk-h{display:flex;align-items:center;gap:8px}
.disk-h b{font-size:14px}
.disk-pct{margin-left:auto;font-weight:700;font-variant-numeric:tabular-nums}
.disk-card.warn .disk-pct{color:var(--warnt)}
.disk-card.crit .disk-pct{color:var(--errt)}
.disk-badge{font-size:10.5px;font-weight:700;padding:1px 7px;border-radius:20px}
.disk-badge.warn{background:rgba(234,179,8,.16);color:var(--warnt)}
.disk-badge.crit{background:rgba(239,68,68,.18);color:var(--errt)}
.disk-ign{font-size:10.5px}
.disk-bar{height:6px;background:var(--bg3);border-radius:3px;overflow:hidden;margin:8px 0 6px}
.disk-bar>i{display:block;height:100%;background:var(--ok);border-radius:3px}
.disk-card.warn .disk-bar>i{background:var(--warn)}
.disk-card.crit .disk-bar>i{background:var(--err)}
.disk-card.ign .disk-bar>i{background:var(--mut2)}
.disk-sub{font-size:11.5px}
.disk-sub b{color:var(--txt)}

/* ══════════ 가동률(SLA) · 장애 이력 ══════════ */
.sla-wrap,.disk-wrap{padding:10px 16px}
.sla-win{padding:2px}
.sla-win .vtab{padding:3px 10px;font-size:11.5px}
.sla-list{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.sla-row{display:flex;align-items:center;gap:10px;padding:8px 12px;border-bottom:1px solid rgba(42,49,64,.5);
  font-size:12.5px;cursor:pointer}
.sla-row:last-child{border-bottom:0}
.sla-row:hover{background:var(--bg3)}
.sla-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.sla-bar{flex:none;width:110px;height:6px;background:var(--bg3);border-radius:3px;overflow:hidden}
.sla-bar>i{display:block;height:100%;background:var(--ok);border-radius:3px}
.sla-row.warn .sla-bar>i{background:var(--warn)}
.sla-row.bad .sla-bar>i{background:var(--err)}
.sla-row.unk .sla-bar>i{background:var(--mut2)}
.sla-pct{flex:none;width:62px;text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.sla-row.ok .sla-pct{color:var(--okt)}
.sla-row.warn .sla-pct{color:var(--warnt)}
.sla-row.bad .sla-pct{color:var(--errt)}
.sla-cnt{flex:none;width:44px;text-align:right;color:var(--mut)}
.sla-down{flex:none;width:88px;text-align:right;color:var(--mut);font-variant-numeric:tabular-nums}
.sla-last{flex:none;width:170px;text-align:right;color:var(--mut);font-size:11.5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:1000px){.sla-last,.sla-down{display:none}}

/* 장애 이력 상세 */
.sla-kpi{display:grid;grid-template-columns:repeat(5,1fr);gap:9px}
.sla-kpi>div{background:var(--bg3);border-radius:var(--r-sm);padding:9px 11px;display:flex;flex-direction:column;gap:2px}
.sla-kpi span{color:var(--mut);font-size:11px}
.sla-kpi b{font-size:17px;font-variant-numeric:tabular-nums}
.sla-kpi b.ok{color:var(--okt)}.sla-kpi b.warn{color:var(--warnt)}.sla-kpi b.bad{color:var(--errt)}
.sla-tl{display:flex;height:16px;border-radius:5px;overflow:hidden;background:var(--bg3);margin-top:12px}
.sla-seg{display:block;height:100%}
.sla-seg.up{background:var(--ok)}.sla-seg.down{background:var(--err)}
.sla-seg.off{background:var(--mut2)}
.sla-seg.gap{background:repeating-linear-gradient(45deg,var(--bd2),var(--bd2) 4px,var(--bd) 4px,var(--bd) 8px)}
.sla-legend{display:flex;gap:14px;margin-top:7px;font-size:11.5px;flex-wrap:wrap}
.sla-legend span{display:flex;align-items:center;gap:5px}
.sla-legend i{width:10px;height:10px;border-radius:3px;display:inline-block}
.sla-legend i.up{background:var(--ok)}.sla-legend i.down{background:var(--err)}
.sla-legend i.off{background:var(--mut2)}
.sla-legend i.gap{background:repeating-linear-gradient(45deg,var(--bd2),var(--bd2) 3px,var(--bd) 3px,var(--bd) 6px)}
.sla-h4{margin:16px 0 7px;font-size:13px}
.sla-inc{max-height:260px;overflow:auto;background:var(--bg3);border-radius:var(--r-sm)}
.sla-inc-row{display:flex;align-items:center;gap:9px;padding:7px 11px;border-bottom:1px solid rgba(42,49,64,.6);font-size:12px}
.sla-inc-row:last-child{border-bottom:0}
.sla-inc-row b{margin-left:auto;color:var(--errt)}
.sla-reason{flex:none;width:76px;text-align:right;color:var(--mut);font-size:11px}

/* ══════════ 디스크 ══════════ */
.disk-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px}
.disk-card{background:var(--bg2);border:1px solid var(--line);border-left:3px solid var(--ok);
  border-radius:var(--r);padding:11px 13px}
.disk-card.warn{border-left-color:var(--warn)}
.disk-card.crit{border-left-color:var(--err)}
.disk-card.ign{border-left-color:var(--mut2);opacity:.65}
.disk-h{display:flex;align-items:center;gap:8px}
.disk-h b{font-size:14px}
.disk-pct{margin-left:auto;font-weight:700;font-variant-numeric:tabular-nums}
.disk-card.warn .disk-pct{color:var(--warnt)}
.disk-card.crit .disk-pct{color:var(--errt)}
.disk-badge{font-size:10.5px;font-weight:700;padding:1px 7px;border-radius:20px}
.disk-badge.warn{background:rgba(234,179,8,.16);color:var(--warnt)}
.disk-badge.crit{background:rgba(239,68,68,.18);color:var(--errt)}
.disk-ign{font-size:10.5px}
.disk-bar{height:6px;background:var(--bg3);border-radius:3px;overflow:hidden;margin:8px 0 6px}
.disk-bar>i{display:block;height:100%;background:var(--ok);border-radius:3px}
.disk-card.warn .disk-bar>i{background:var(--warn)}
.disk-card.crit .disk-bar>i{background:var(--err)}
.disk-card.ign .disk-bar>i{background:var(--mut2)}
.disk-sub{font-size:11.5px}
.disk-sub b{color:var(--txt)}

/* ══════════ 상단 차트 박스 고정 ══════════
   값 텍스트가 길어지거나(↓12.3 MB/s) 그래프가 튀어도 카드 높이가 변하면 안 된다.
   높이가 변하면 아래 내용이 통째로 밀려 화면이 위아래로 흔들린다.
   → 박스는 고정, 그래프는 그 안에서만 변한다. */
.chartcard{display:flex;flex-direction:column;gap:4px}
.ch-h{flex:none;gap:8px;line-height:1.45;white-space:nowrap;min-width:0}
.ch-h>span{overflow:hidden;text-overflow:ellipsis;min-width:0}
.ch-h>b{flex:none;white-space:nowrap}          /* 값은 절대 줄바꿈하지 않는다 */
.chartcard canvas{flex:none;height:60px;width:100%;display:block}

/* ══════════ 가동률 — 간단히 보기(카드형) ══════════
   서비스가 많으면 한 줄씩 나열하는 목록은 화면을 다 잡아먹는다.
   카드형은 한 줄에 여러 개를 놓고 핵심(가동률·장애수)만 보여준다. */
.sla-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(178px,1fr));gap:9px}
.sla-card{background:var(--bg2);border:1px solid var(--line);border-left:3px solid var(--ok);
  border-radius:var(--r);padding:9px 11px;cursor:pointer;transition:.12s}
.sla-card:hover{border-color:var(--bd2);background:var(--bg3)}
.sla-card.warn{border-left-color:var(--warn)}
.sla-card.bad{border-left-color:var(--err)}
.sla-card.unk{border-left-color:var(--mut2)}
.slc-name{font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mut)}
.slc-pct{font-size:19px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.25;color:var(--okt)}
.sla-card.warn .slc-pct{color:var(--warnt)}
.sla-card.bad .slc-pct{color:var(--errt)}
.sla-card.unk .slc-pct{color:var(--mut)}
.slc-bar{height:4px;background:var(--bg3);border-radius:3px;overflow:hidden;margin:3px 0 5px}
.slc-bar>i{display:block;height:100%;background:var(--ok);border-radius:3px}
.sla-card.warn .slc-bar>i{background:var(--warn)}
.sla-card.bad .slc-bar>i{background:var(--err)}
.sla-card.unk .slc-bar>i{background:var(--mut2)}
.slc-sub{font-size:11px;color:var(--errt);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#slaMode .vtab{padding:3px 11px}
body[data-size="sm"] .sla-cards{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
body[data-size="sm"] .slc-pct{font-size:17px}
/* 양방향(A↔B) 연결선 — 되돌아오는 쪽은 아래로 우회하며 점선으로 구분 */
.ssm-link.bi{stroke:var(--txd);stroke-dasharray:6 3}

/* ══════════ 구성도 편집 — 왼쪽 실시간 미리보기 ══════════
   행만 보고는 분기·합류·양방향이 어떻게 그려질지 감이 안 온다. 편집하면서 바로 확인한다. */
.modal-wide{width:1280px}
.ssm-ed-body{display:grid;grid-template-columns:370px minmax(0,1fr);gap:14px;align-items:start}
.ssm-ed-form{display:flex;flex-direction:column;gap:11px;min-width:0}
.ssm-ed-pv{position:sticky;top:0;background:var(--bg3);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:10px 11px;min-width:0}
.ssm-pv-head{font-size:12px;font-weight:700;margin-bottom:8px;display:flex;align-items:baseline;gap:7px}
.ssm-pv-head small{font-weight:400;font-size:11px}
.ssm-pv-canvas{background:var(--bg);border-radius:var(--r-sm);padding:6px;overflow-x:auto;min-height:120px}
.ssm-pv-canvas .ssm-svg.fit{display:block;max-width:100%}
.ssm-pv-empty{padding:34px 10px;text-align:center;font-size:12px}
.ssm-pv-info{margin-top:8px;font-size:11px;line-height:1.6;word-break:keep-all}
@media(max-width:1120px){
  .modal-wide{width:96vw}
  .ssm-ed-body{grid-template-columns:1fr}
  .ssm-ed-pv{position:static}
}

/* ══════════ 팝업 이동 · 크기 조절 ══════════ */
.drag-head{cursor:move;user-select:none}
.modal.modal-free{display:block;background:transparent;pointer-events:none}
.modal.modal-free .modal-box{pointer-events:auto;box-shadow:0 12px 40px rgba(0,0,0,.55)}
.win-resize{position:absolute;right:2px;bottom:2px;width:16px;height:16px;cursor:nwse-resize;
  background:linear-gradient(135deg,transparent 46%,var(--txd) 46%,var(--txd) 54%,transparent 54%,
    transparent 72%,var(--txd) 72%,var(--txd) 80%,transparent 80%);opacity:.65}
.win-resize:hover{opacity:1}
.modal-box{position:relative}
body.dragging-win{user-select:none;cursor:move}
body.dragging-win iframe{pointer-events:none}

/* ══════════ 모달리스 미리보기 창 ══════════ */
.float-win{position:fixed;z-index:70;display:flex;flex-direction:column;
  background:var(--sf);border:1px solid var(--bd);border-radius:12px;box-shadow:0 12px 40px rgba(0,0,0,.55);
  min-width:320px;min-height:200px;overflow:hidden}
/* ★ 클래스의 display:flex 가 브라우저 기본 [hidden]{display:none} 을 이긴다.
   이게 없으면 hidden=true 로 바꿔도 창이 안 닫혀서, 편집기를 닫은 뒤에도
   미리보기 창만 덩그러니 떠 있게 된다(닫기 ✕ 도 안 먹힘). */
.float-win[hidden]{display:none}
.fw-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 10px;
  border-bottom:1px solid var(--bd);background:var(--sf2);cursor:move;user-select:none}
.fw-title{font-size:12.5px;font-weight:700}
.fw-tools{display:flex;align-items:center;gap:3px}
.fw-zoom{font-size:11.5px;color:var(--txm);min-width:42px;text-align:center;font-variant-numeric:tabular-nums}
.fw-body{flex:1;overflow:auto;padding:8px;background:var(--bg)}
.fw-body .ssm-svg{display:block}
.fw-foot{padding:6px 10px;border-top:1px solid var(--bd);font-size:11px;line-height:1.5;
  max-height:56px;overflow:auto}
.fw-resize{position:absolute;right:2px;bottom:2px;width:16px;height:16px;cursor:nwse-resize;
  background:linear-gradient(135deg,transparent 46%,var(--txd) 46%,var(--txd) 54%,transparent 54%,
    transparent 72%,var(--txd) 72%,var(--txd) 80%,transparent 80%);opacity:.65}

/* ══════════ 테마 패널 ══════════ */
.th-sec{display:flex;flex-direction:column;gap:8px}
.th-sec-h{display:flex;align-items:center;justify-content:space-between;gap:10px}
.th-presets{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:7px}
.th-preset{display:flex;align-items:center;gap:8px;background:var(--sf2);border:1px solid var(--bd);
  border-radius:var(--r-sm);padding:7px 9px;cursor:pointer;color:var(--tx);text-align:left;font-size:12px}
.th-preset:hover{border-color:var(--ac2)}
.th-preset.on{border-color:var(--ac2);box-shadow:0 0 0 1px var(--ac2) inset}
.th-swatch{display:inline-flex;gap:2px;align-items:center;padding:3px;border-radius:5px;border:1px solid;flex:none}
.th-swatch i{width:8px;height:14px;border-radius:2px;display:block}
.th-pname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.th-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px}
.th-grp{background:var(--sf2);border-radius:var(--r-sm);padding:8px 9px}
.th-grp-h{font-size:11px;color:var(--txm);text-transform:uppercase;letter-spacing:.4px;margin-bottom:6px}
.th-field{display:flex;align-items:center;gap:8px;padding:4px 2px;cursor:pointer}
.th-field input[type=color]{width:30px;height:24px;padding:0;border:1px solid var(--bd);border-radius:5px;
  background:none;cursor:pointer;flex:none}
.th-flab{flex:1;min-width:0;font-size:12px;display:flex;flex-direction:column;line-height:1.25}
.th-flab small{color:var(--txd);font-size:10.5px}
.th-fval{font-size:10.5px;color:var(--txd);flex:none}

/* ══════════ 네트워크 관리 ══════════ */
.net-wrap{padding:10px 16px}
.net-ip{font-size:12px;color:var(--txm)}
.net-ip b{color:var(--tx)}
.net-grid{display:grid;grid-template-columns:1.5fr 1.2fr 1fr;gap:12px;align-items:start}
@media(max-width:1200px){.net-grid{grid-template-columns:1fr}}
.net-card{background:var(--bg2);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.net-h{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:8px 11px;border-bottom:1px solid var(--line);font-size:12.5px}
.net-h-btns{display:flex;gap:4px}
.net-router{padding:7px 11px;border-bottom:1px solid rgba(42,49,64,.5);font-size:11.5px;line-height:1.6}
.net-list{max-height:320px;overflow:auto}
.net-row{display:flex;align-items:center;gap:8px;padding:7px 11px;
  border-bottom:1px solid rgba(42,49,64,.5);font-size:12px;cursor:pointer}
.net-row:last-child{border-bottom:0}
.net-row:hover{background:var(--bg3)}
.net-host{flex:none;width:172px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--infot)}
.net-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--txm)}
.net-label i{font-style:normal;color:var(--txd)}
.net-chip{flex:none;font-size:10.5px;padding:1px 7px;border-radius:20px;white-space:nowrap;
  background:var(--bg3);border:1px solid var(--line);color:var(--txm)}
.net-chip.ok{border-color:rgba(34,197,94,.5);color:var(--okt)}
.net-chip.warn{border-color:rgba(234,179,8,.5);color:var(--warnt)}
.net-chip.down{border-color:rgba(239,68,68,.5);color:var(--errt)}
.net-fname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.net-fport{flex:none;font-size:11px;color:var(--txm)}
.net-dev{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.net-dip{flex:none;width:104px}
.net-dmac{flex:none;font-size:10.5px}
.net-empty{padding:16px 11px;text-align:center;font-size:12px}
/* AI 카드는 계획 본문이 길어서 한 줄 전체를 쓴다 */
.net-card-wide{grid-column:1/-1}

/* ── AI 도우미 ── */
.ai-conn{font-size:11.5px;white-space:nowrap}
.ai-conn .ai-on{color:var(--okt)}
.ai-conn .ai-off{color:var(--txd)}
.input-sm{height:26px;padding:2px 6px;font-size:11.5px;width:auto;min-width:120px;max-width:180px}
.ai-ask{display:flex;gap:8px;padding:10px 11px;align-items:flex-start}
.ai-ask textarea{flex:1;resize:vertical;min-height:44px}
.ai-ask .btn{flex:none;align-self:stretch}
.ai-hint{padding:0 11px 9px;font-size:11.5px;line-height:1.6}
.ai-loading{padding:22px 11px;text-align:center;font-size:12.5px;color:var(--txm)}
.ai-planbox{border-top:1px solid var(--line);padding:11px}
.ai-plan-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11.5px;margin-bottom:7px}
.ai-status{margin-left:auto;font-size:11px;padding:1px 8px;border-radius:20px;
  background:var(--bg3);border:1px solid var(--line);color:var(--txm)}
.ai-status.applied{border-color:rgba(34,197,94,.5);color:var(--okt)}
.ai-status.failed{border-color:rgba(239,68,68,.5);color:var(--errt)}
.ai-summary{font-size:12.5px;line-height:1.7;margin-bottom:9px;white-space:pre-wrap}
.ai-steps{display:flex;flex-direction:column;gap:7px}
.ai-step{background:var(--bg3);border:1px solid var(--line);border-radius:var(--r);padding:8px 10px}
.ai-step.manual{opacity:.82;border-style:dashed}
.ai-step-h{display:flex;align-items:center;gap:8px;font-size:12.5px;cursor:pointer;flex-wrap:wrap}
.ai-step-h input{width:15px;height:15px;flex:none}
.ai-step-h b{flex:1;min-width:120px}
.ai-detail{font-size:11.5px;color:var(--txm);line-height:1.7;margin:5px 0 0 23px;white-space:pre-wrap}
.ai-kv{font-size:11.5px;margin:5px 0 0 23px;color:var(--infot)}
.ai-code{margin:6px 0 4px 23px;padding:8px 10px;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r);font-size:11px;line-height:1.55;max-height:220px;overflow:auto;white-space:pre}
.ai-step .btn-sm{margin-left:23px}
.ai-actions{display:flex;align-items:center;gap:10px;margin:11px 0 8px}
.ai-actions .chk{margin-right:auto;font-size:12px}
.ai-plans .net-row{cursor:pointer}
.ai-plan-goal{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ai-test:empty{display:none}
/* 안내/결과 박스 — 모달과 카드 양쪽에서 쓴다 */
.hint-box,.ok-box,.err-box,.warn-box{font-size:11.5px;line-height:1.7;padding:8px 10px;
  border-radius:var(--r);border:1px solid var(--line);background:var(--bg3);margin:8px 0}
.hint-box{color:var(--txm)}
.ok-box{border-color:rgba(34,197,94,.45);color:var(--okt)}
.err-box{border-color:rgba(239,68,68,.45);color:var(--errt)}
.warn-box{border-color:rgba(234,179,8,.45);color:var(--warnt)}
.warn-box ul{margin:4px 0 0;padding-left:18px}
.raw-box{padding:9px;background:var(--bg);border:1px solid var(--line);border-radius:var(--r);
  font-size:10.5px;line-height:1.5;max-height:280px;overflow:auto;white-space:pre-wrap;color:var(--txm)}

/* 탭: 네트워크 */
body[data-view="net"] .ssm-wrap,
body[data-view="net"] .services-wrap,
body[data-view="net"] .peers-wrap,
body[data-view="net"] .ext-wrap,
body[data-view="net"] .sla-wrap,
body[data-view="net"] .disk-wrap,
body[data-view="net"] .conn-wrap{display:none}
body[data-view="svc"] .net-wrap,
body[data-view="ssm"] .net-wrap{display:none}

/* 탭바를 상단바 바로 아래에 붙여 고정 — 스크롤해도 탭 전환이 항상 손에 닿는다 */
.viewbar{position:sticky;top:0;z-index:29;background:var(--bg);
  border-bottom:1px solid var(--line);padding:7px 16px;margin-bottom:2px}
.topbar{z-index:31}

/* ── DNS 설정 도우미 ── */
.dns-tabs{margin-bottom:10px}
.dns-pane h4{margin:14px 0 6px;font-size:12.5px;display:flex;align-items:center;gap:8px}
.dns-table{width:100%;border-collapse:collapse;font-size:11.5px;margin:8px 0}
.dns-table th{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line);color:var(--mut);font-weight:600}
.dns-table td{padding:6px 8px;border-bottom:1px solid rgba(42,49,64,.5)}
.dns-table tr:last-child td{border-bottom:0}
.dns-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:10px}
.dns-prop{background:var(--bg3);border:1px solid var(--line);border-radius:var(--r);padding:8px 10px;margin-bottom:7px}
.dns-prop-h{display:flex;align-items:center;gap:8px;font-size:12.5px;flex-wrap:wrap}
.dns-prop-r{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}
.dns-block{margin-bottom:10px}
.dns-block-h{display:flex;align-items:center;gap:8px;font-size:12.5px;margin-bottom:4px}
.dns-dom{margin-bottom:14px}
.dns-all-table td{vertical-align:top;font-size:11.5px}
.dns-rt{width:120px;color:var(--mut);white-space:nowrap}
.dns-find{background:var(--bg3);border:1px solid var(--line);border-radius:var(--r);padding:8px 10px;margin:6px 0}
.dns-find-h{display:flex;align-items:center;gap:8px;font-size:12.5px}
.dns-find .ai-detail{margin-left:0}
.dns-find .ai-code{margin-left:0}
.net-target{display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--mut);flex-direction:row}
.dns-provbox{margin-top:14px;border-top:1px solid var(--line);padding-top:10px}
.dns-prov{background:var(--bg3);border:1px solid var(--line);border-radius:var(--r);padding:8px 10px;margin-bottom:8px}
.dns-prov .dns-prov-h{margin-bottom:4px}
.dns-prov .ai-detail{margin-left:0;margin-top:2px}
.dns-key{min-width:220px}
