/* 테마: data-theme 없음 = 자동(시스템 따름, 기본값) · light · dark · slate(슬레이트) — static/theme.js 가 설정한다 */
:root {
  --bg: #f5f6f8; --card: #ffffff; --text: #1d2330; --muted: #6b7385; --line: #e1e4ea;
  --accent: #2f6fde; --on-accent: #ffffff; --green: #1f9d55; --amber: #c98a00; --red: #d0342c; --gray: #8a90a0;
  --mono: ui-monospace, "SFMono-Regular", Consolas, monospace;
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg: #12151b; --card: #1b1f27; --text: #e3e6ec; --muted: #8e96a8; --line: #2c323e;
    --accent: #6ea0ff; --on-accent: #0d1220; --green: #3fbf7a; --amber: #e0a930; --red: #ef5b52; --gray: #7c8394;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #12151b; --card: #1b1f27; --text: #e3e6ec; --muted: #8e96a8; --line: #2c323e;
  --accent: #6ea0ff; --on-accent: #0d1220; --green: #3fbf7a; --amber: #e0a930; --red: #ef5b52; --gray: #7c8394;
  color-scheme: dark;
}
/* 슬레이트: 짙은 남색 회색 바탕 + 흰 글자 + 하늘색 강조 (본문 대비 13:1 이상, 운영 콘솔 · IDE 계열) */
:root[data-theme="slate"] {
  --bg: #1b2433; --card: #243044; --text: #ffffff; --muted: #cbd5e1; --line: #3a4a63;
  --accent: #7cc4ff; --accent-btn: #2563eb; --on-accent: #ffffff;
  --green: #4ade80; --amber: #fbbf24; --red: #f87171; --gray: #a7b4c8;
  color-scheme: dark;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text); word-break: keep-all; overflow-wrap: break-word;
       font: 14px/1.5 "Pretendard", "Malgun Gothic", "Apple SD Gothic Neo", system-ui, sans-serif; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono { font-family: var(--mono); font-size: 12.5px; }
h1 { font-size: 20px; margin: 4px 0 12px; }
h2 { font-size: 15px; margin: 0 0 10px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
h3 { font-size: 13px; margin: 16px 0 6px; color: var(--muted); }
small, .small { font-size: 12px; }
.muted { color: var(--muted); }
.bad { color: var(--red); }

/* ---------------------------------------------------------------- 화면 틀: 왼쪽 메뉴 + 본문(화면 폭 전체) */
:root { --side-w: 248px; }
body.with-side { padding-left: var(--side-w); transition: padding-left .2s; }
body.with-side.side-closed { padding-left: 0; }
.side { position: fixed; inset: 0 auto 0 0; width: var(--side-w); z-index: 60; display: flex; flex-direction: column;
        background: var(--card); border-right: 1px solid var(--line); transition: transform .2s; }
body.side-closed .side { transform: translateX(-100%); }
/* 책갈피: 메뉴 오른쪽 가장자리에 붙은 세로 탭 — 메뉴가 접히면 화면 왼쪽 끝에 남는다 */
.side-tab { position: absolute; top: 50%; left: 100%; transform: translateY(-50%); z-index: 61; writing-mode: vertical-rl;
        padding: 14px 5px; border: 1px solid var(--line); border-left: 0; border-radius: 0 8px 8px 0; background: var(--card);
        color: var(--muted); font-size: 12px; letter-spacing: 1px; cursor: pointer; box-shadow: 2px 0 6px rgb(0 0 0 / .12); }
.side-tab:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--card)); }
body.side-closed .side-tab { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
body.side-closed .side-tab .when-open, body:not(.side-closed) .side-tab .when-closed { display: none; }
.side-head { padding: 12px 12px 8px; border-bottom: 1px solid var(--line); }
.brand { font-weight: 700; font-size: 17px; color: var(--text); display: block; text-align: center; }   /* 왼쪽 위 메뉴 머리 — 가운데 정렬 (운용자 요청 2026-10-13) */
.brand small { display: block; font-weight: 400; font-size: 11.5px; color: var(--muted); }
.brand-big { font-size: 23px; white-space: nowrap; letter-spacing: -.2px; line-height: 1.3; }   /* 'DevOps · DevHub' — 메뉴 폭(248px)에 맞춰 최대한 크게 (2026-10-13) */
.side-tools { display: flex; gap: 6px; margin-top: 8px; }
.side-tools button { flex: 1; padding: 3px 6px; font-size: 12px; display: inline-flex; align-items: center; justify-content: center; gap: 4px; }
.side-tools .ico { width: 14px; height: 14px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }   /* 겹화살표 — 아래 = 모두 펼치기 · 위 = 모두 접기 */
.menu { flex: 1; overflow-y: auto; padding: 6px 0 12px; }
.menu ul { list-style: none; margin: 0; padding: 0; }
.menu li a, .grp-toggle { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 14px; border: 0; border-radius: 0;
        background: none; color: var(--text); font-size: 13.5px; text-align: left; cursor: pointer; }
.menu li a:hover, .grp-toggle:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); text-decoration: none; }
.menu li a.active { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); font-weight: 600;
        box-shadow: inset 3px 0 0 var(--accent); }
.grp-toggle { font-weight: 600; }
.grp.here > .grp-toggle, .grp.here > .grp-head > .grp-toggle { color: var(--accent); }
/* 묶음 머리: ± 단추(펼치기 · 접기) + 이름 링크(첫 화면으로 이동) — 한 줄 전체에 같은 hover */
.grp-head { display: flex; align-items: center; padding-left: 14px; }
.grp-head:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.grp-head > a.grp-toggle { flex: 1; padding-left: 6px; }
.grp-head > a.grp-toggle:hover { background: none; }
.grp-head > button.pm { padding: 0; background: none; cursor: pointer; }
.grp-head > button.pm:hover { border-color: var(--accent); color: var(--accent); }
.grp .pm { display: inline-flex; justify-content: center; align-items: center; width: 18px; height: 18px; flex: none;
        border: 1px solid var(--line); border-radius: 4px; font-size: 13px; line-height: 1; color: var(--muted); }
.grp > ul { display: none; }
.grp.open > ul { display: block; }
.grp ul a { padding-left: 38px; font-size: 13px; }
.grp ul .grp-toggle { padding-left: 30px; font-weight: 500; }
.grp ul ul a { padding-left: 56px; }
.side-user { border-top: 1px solid var(--line); padding: 10px 14px; display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.side-user .who { font-weight: 600; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.side-user .view-switch { margin-left: auto; display: inline-flex; }   /* '관리자' 오른쪽 보기 전환 토글 (viewswitch.py) */
.side-user a.active { font-weight: 600; }
header.top { display: flex; align-items: center; gap: 12px; padding: 8px 20px; background: var(--card);
             border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 50; box-shadow: 0 1px 4px rgb(0 0 0 / .08); }
header.top .crumb { font-weight: 600; flex: 1; }
header.top .crumb a.crumb-a { color: inherit; text-decoration: none; border-bottom: 1px dotted currentColor; }
header.top .crumb a.crumb-a:hover, header.top .crumb a.crumb-a:focus-visible { color: var(--accent, #2f6fde); border-bottom-style: solid; }
.side-btn { padding: 4px 10px; }
header.top .crumb a.crumb-home { font-weight: 700; }   /* ☰ 옆 첫 화면 (대시보드 · 그룹웨어) */
/* 고정 상단 줄 아래로 #앵커가 숨지 않게 (높이는 app.js 가 --top-h 로 맞춤) */
html { scroll-padding-top: calc(var(--top-h, 48px) + 8px); }
main { padding: 16px 20px; }
footer { text-align: center; color: var(--muted); font-size: 12px; padding: 16px; }
/* 좁은 화면: 메뉴는 본문 위로 덮어 열고, 기본은 접힘 (app.js) */
.side-close, .side-backdrop { display: none; }
.side-head { position: relative; }
@media (max-width: 900px) {
  body.with-side, body.with-side.side-closed { padding-left: 0; }
  body.with-side:not(.side-closed) .side { box-shadow: 4px 0 18px rgb(0 0 0 / .25); }
  .side-close { display: block; position: absolute; top: 10px; right: 10px; padding: 2px 9px; }
  .brand-big { font-size: 19px; text-align: left; padding-right: 36px; }   /* 닫기 ✕ 단추와 겹치지 않게 */
  body.with-side:not(.side-closed) .side-backdrop { display: block; position: fixed; inset: 0; z-index: 55; background: rgb(0 0 0 / .35); }
}
/* 마우스를 올리면 뜨는 기능 설명 (data-tip, app.js 가 자리를 잡는다) */
.tip { position: fixed; z-index: 100; max-width: 340px; padding: 7px 10px; border-radius: 6px; pointer-events: none;
       background: var(--text); color: var(--bg); font-size: 12.5px; line-height: 1.45; box-shadow: 0 4px 14px rgb(0 0 0 / .25); }
/* 목록 쪽 나누기 (app.js 가 행이 많은 표에 붙인다) — 생략 없이 처음 · 이전 · 번호 · 다음 · 끝 */
.pg-wrap { max-height: 70vh; overflow: auto; }
.pg-wrap thead th { position: sticky; top: 0; background: var(--card); z-index: 1; }
tr.pg-off { display: none; }
.pager { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin: 8px 0 2px; font-size: 12.5px; }
.pager button { padding: 2px 9px; font-size: 12.5px; }
.pager button.cur { background: var(--accent-btn, var(--accent)); border-color: var(--accent-btn, var(--accent)); color: var(--on-accent); }
.pager button:disabled { opacity: .45; cursor: default; }
.pager .info { color: var(--muted); margin-left: 8px; }
.pager select { padding: 2px 4px; font-size: 12.5px; }
.netmap .pager button, .netmap .pager select { background: var(--panel2); color: var(--ink); border-color: var(--line); }
.netmap .pager button.cur { background: #2563eb; color: #fff; }
.netmap .pg-wrap thead th { background: var(--panel); }

.card { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 14px 16px; margin-bottom: 14px; }
.card.narrow { max-width: 380px; margin: 40px auto; }
.card.warn { border-color: var(--amber); }
.card.danger { border-color: var(--red); }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 14px; margin-bottom: 14px; }
/* 프로젝트 하단 조작부: 왼쪽 서비스 개시 · 오른쪽 외부 접속 (좁으면 위아래) */
.ops2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 18px; }
.ops2 > div + div { border-left: 1px solid var(--line); padding-left: 18px; }
@media (max-width: 860px) { .ops2 { grid-template-columns: 1fr; } .ops2 > div + div { border-left: 0; padding-left: 0; } }
.project { display: flex; flex-direction: column; margin: 0; }
.project header { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.project h2 { margin: 0; }
.project header code { white-space: nowrap; }
/* 카드 순서 ◀ ▶ (관리자) — 카드 제목 줄 오른쪽 끝, 평소에는 흐리게 */
.project header h2 { flex: 1 1 auto; }   /* 코드 · ◀ ▶ 는 오른쪽에 붙는다 */
.card-move { display: flex; gap: 6px; opacity: .35; font-size: 12px; white-space: nowrap; }
.card.project:hover .card-move, .card-move:focus-within { opacity: 1; }
.card-move button[disabled] { opacity: .3; cursor: default; }
/* 프로젝트 코드(예: sonic-build) 강조 — 대시보드 카드 · 상세 · 수정 화면 공통 */
code.pcode { font-weight: 700; color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); border-radius: 5px; padding: 1px 6px; }
h1 code.pcode { font-size: 16px; vertical-align: 1px; }
/* 카드 제목은 두 줄 자리를 잡아, 제목이 긴 카드(두 줄)와 짧은 카드의 내용 시작 줄을 맞춘다 */
@media (min-width: 641px) { .project h2 { min-height: 3em; align-items: flex-start; } }
.project .summary { color: var(--muted); font-size: 12.5px; margin: 4px 0 8px; }
dd.shortcuts details summary { cursor: pointer; color: var(--muted); font-size: 12px; }
/* 대시보드 보기 방법 — 카드 · 목록 / 간략 · 자세히 (dashboard.html .viewsw → #overview-box[data-view-*], 자세히 전용 칸 = .x) */
.viewsw { display: inline-flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.viewsw button { border: 0; border-radius: 0; background: var(--card); color: var(--muted); }
.viewsw button + button { border-left: 1px solid var(--line); }
.viewsw button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); font-weight: 600; }
[data-view-form="card"] .ov-list, [data-view-form="list"] .ov-cards, [data-view-detail="brief"] .x { display: none; }
[data-view-detail="brief"] .project h2 { min-height: 0; }
.ov-list { margin-bottom: 14px; background: var(--card); border: 1px solid var(--line); border-radius: 8px; }
.ov-table .summary { color: var(--muted); font-size: 12px; margin-top: 2px; }
.ov-table td.shortcuts, .ov-table td.shortcuts summary { font-size: 12px; }
.ov-table td.shortcuts summary { cursor: pointer; color: var(--muted); }
.project footer { margin-top: auto; padding: 10px 0 0; text-align: left; border-top: 1px solid var(--line); }
.summary { color: var(--muted); }

.server { display: flex; gap: 18px; flex-wrap: wrap; background: var(--card); border: 1px solid var(--line);
          border-radius: 8px; padding: 8px 14px; margin-bottom: 14px; font-size: 13px; }
/* 대시보드 Claude 사용 비용 · 플랜 요금 배분 줄 — 내려도 늘 위에 (위 띠 바로 아래, --top-h 는 app.js 가 잰 위 띠 높이) */
.server.ov-cost { position: sticky; top: calc(var(--top-h, 48px) + var(--dash-h, 0px)); z-index: 40; box-shadow: 0 2px 6px rgb(0 0 0 / .10); }
/* 대시보드 제목 줄 · 서버 줄도 위에 고정 — 넓은 화면만 (휴대폰은 화면을 너무 가림) */
@media (min-width: 761px) {
  /* margin-top 은 main 위 여백(16px)을 지운다 — 처음부터 고정됐을 때와 같은 간격(위 띠 아래 6px)이라 내려도 벌어졌다 좁혀지지 않음 */
  .dash-head { position: sticky; top: var(--top-h, 48px); z-index: 41; background: var(--bg); padding-top: 6px; margin-top: -16px; }
}
.server .k { color: var(--muted); margin-right: 4px; }
/* 대시보드 맨 위 서버 정보 줄 (서버 · IP · 가동 · 부하 · 디스크) — 흰색 얇은 테두리 (운용자 요청 2026-10-13, 앞서는 2px 흰색 · 주홍 · 분홍 · 밝은 초록) */
section.server { border: 1px solid #ffffff; }
/* Claude 사용 한도 — 리셋(다시 쓸 수 있는) 일시는 노란색 굵게 (운용자 요청 2026-10-13). 밝은 화면에서도 읽히게 어두운 바탕 조각 위에 */
.cl-limits .cl-reset { color: #ffe14d; font-weight: 800; background: #2b2600; border-radius: 6px; padding: 1px 7px; white-space: nowrap; }
.cl-limits .cl-warn { color: var(--red); }
.cl-limits .cl-stale { color: var(--amber); }

dl.facts { display: grid; grid-template-columns: max-content 1fr; gap: 4px 10px; margin: 0; font-size: 13px; }
dl.facts.wide { grid-template-columns: max-content 1fr; }
dl.facts dt { color: var(--muted); }
dl.facts dd { margin: 0; overflow-wrap: anywhere; }

.badge { display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 12px; font-weight: 600;
         background: color-mix(in srgb, var(--gray) 18%, transparent); color: var(--gray); white-space: nowrap; }
.badge.green, .badge.st-on { background: color-mix(in srgb, var(--green) 18%, transparent); color: var(--green); }
.badge.amber, .badge.st-idle, .badge.st-mfa { background: color-mix(in srgb, var(--amber) 18%, transparent); color: var(--amber); }
/* 작업 진행 (빌드중 · 발급중) — 앞에 깜빡이는 점 */
.badge.job-on::before { content: ""; display: inline-block; width: .5em; height: .5em; margin-right: .3em; border-radius: 50%;
  background: currentColor; vertical-align: .05em; animation: job-pulse 1.2s ease-in-out infinite; }
@keyframes job-pulse { 50% { opacity: .25; } }
/* 할 일 남음 (ANL 게시필요) — 배지 전체가 깜빡임 */
.badge.job-need { font-weight: 700; animation: nm-blink 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .badge.job-on::before, .badge.job-need { animation: none; } }
.job-detail { display: block; margin-top: .2em; overflow-wrap: anywhere; }
.badge.red { background: color-mix(in srgb, var(--red) 18%, transparent); color: var(--red); }
.badge.none { background: transparent; color: var(--muted); font-weight: 400; padding: 0; }
/* 서비스 상태 — 테두리로도 알린다: 운영중 = 초록 · 중지 · 빌드 대기 = 회색 (글자색과 같은 색) */
.badge.svc { border: 1.5px solid currentColor; padding: 0 7px; }
.badge.svc.none { border-color: var(--gray); color: var(--gray); padding: 0 7px; }

.scroll { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: 13px; }
th, td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { color: var(--muted); font-weight: 600; white-space: nowrap; }
tr.dim td { color: var(--muted); }
pre { white-space: pre-wrap; font-family: var(--mono); font-size: 12px; margin: 6px 0; }
pre.notes { background: var(--bg); padding: 8px 10px; border-radius: 6px; }
ul.events { margin: 0; padding-left: 18px; font-size: 13px; }

.flash { padding: 8px 12px; border-radius: 6px; margin: 0 0 12px; }
.flash.ok { background: color-mix(in srgb, var(--green) 14%, transparent); }
.anl-need { margin: 8px 0; padding: 8px 12px; border: 1px solid var(--amber); border-radius: 6px; background: color-mix(in srgb, var(--amber) 10%, transparent); }   /* ANL 게시 권고 */
.anl-roles { margin: 10px 0 6px; }  .anl-roles th:first-child { white-space: nowrap; }   /* ANL ①~④ 단계별 역할 */
.anl-users td.nowrap { white-space: nowrap; }  .anl-users form.inline { display: inline; }  .anl-users button.danger { color: var(--red); border-color: var(--red); }   /* ANL 등록 · 사용자 — 사용중지 */
.flash.bad { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--text); }
.flash.warn { background: color-mix(in srgb, var(--amber) 16%, transparent); }

input, select, textarea, button, .button { font: inherit; color: var(--text); background: var(--card);
  border: 1px solid var(--line); border-radius: 6px; padding: 6px 10px; }
textarea { width: 100%; font-family: var(--mono); font-size: 12.5px; }
button, .button { cursor: pointer; display: inline-block; }
button:hover, .button:hover { border-color: var(--accent); text-decoration: none; }
button.primary { background: var(--accent-btn, var(--accent)); border-color: var(--accent-btn, var(--accent));
                 color: var(--on-accent); }
.refresh-bar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.refresh-bar.right { justify-content: flex-end; margin: -6px 0 8px; }
.refresh-bar button { padding: 4px 12px; }
.filters { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.filters input[type=search] { flex: 1 1 260px; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin-bottom: 14px; }
.stat { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px; }
.stat .k { display: block; color: var(--muted); font-size: 12px; }
.stat b { font-size: 20px; margin-right: 4px; }
.stat small { color: var(--muted); }
td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.button.primary { background: var(--accent-btn, var(--accent)); border-color: var(--accent-btn, var(--accent));
                  color: var(--on-accent); }
.group-ops { float: right; display: inline-flex; gap: 6px; }
.group-ops button { padding: 2px 12px; font-size: 12px; }
tr.group td { background: color-mix(in srgb, var(--accent) 10%, transparent); border-top: 2px solid var(--line); }
table.docker td { font-size: 12.5px; }
table.docker td:last-child { min-width: 52px; }
td details summary { white-space: nowrap; }
td details[open] { min-width: 320px; }
details.inline-edit summary, td details summary { cursor: pointer; color: var(--accent); font-size: 12px; }
details.inline-edit form.row { margin-top: 4px; }
.badge.blue { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
table.users tr.editing td { background: color-mix(in srgb, var(--accent) 8%, transparent); }
table.users tr.editing td[data-field] input, table.users tr.editing td[data-field] select { width: 100%; min-width: 120px; }
table.users tr.editing td[data-field="position_duty"] select + select { margin-top: 4px; }
.option-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
ul.chips { list-style: none; padding: 0; margin: 0 0 6px; display: flex; flex-wrap: wrap; gap: 6px; }
ul.chips li { background: var(--bg); border: 1px solid var(--line); border-radius: 12px; padding: 1px 4px 1px 10px; font-size: 12.5px; }
ul.chips li button { padding: 0 4px; }
.servers { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; margin-bottom: 14px; }
.srv { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--line);
       border-radius: 8px; background: var(--card); color: var(--text); }
.srv:hover { border-color: var(--accent); text-decoration: none; }
.srv.active { border: 2px solid var(--accent); }
.srv.off { opacity: .6; }
.srv small { color: var(--muted); font-size: 11.5px; overflow-wrap: anywhere; }
.stat.wide { grid-column: span 2; }
.stat .small-b { font-size: 14px; }
.bar { display: block; height: 6px; margin-top: 6px; background: var(--line); border-radius: 3px; overflow: hidden; }
.bar .fill { display: block; height: 100%; width: 0; background: var(--green); }
.bar .fill.amber { background: var(--amber); }
.bar .fill.red { background: var(--red); }
table.datagrid td { font-size: 12.5px; max-width: 520px; }
table.datagrid td.wrap { white-space: normal; min-width: 14em; max-width: 26em; }
tr.sel td { background: color-mix(in srgb, var(--accent) 12%, transparent); }
#sql textarea { min-height: 120px; }
.theme-pick { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.theme-pick select { padding: 3px 6px; font-size: 12px; }
/* 표 머리칸 정렬 (app.js) — 누른 칸에 ▲(오름) · ▼(내림) */
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable:hover { color: var(--accent); }
th.sortable::after { content: " ↕"; color: var(--muted); font-size: 11px; opacity: .5; }
th.sort-asc::after { content: " ▲"; opacity: 1; color: var(--accent); }
th.sort-desc::after { content: " ▼"; opacity: 1; color: var(--accent); }
.table-search { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; }
.table-search input { max-width: 320px; }
/* 한 줄로 (줄바꿈 없이) — 상태 · 단추 칸 */
td.nowrap, td.actions { white-space: nowrap; }
td.actions form, td.actions form.inline { display: inline; }
td.actions form + form, td.actions .link + form, td.actions form + .link { margin-left: 6px; }
/* 한 화면 목록 수 (기기마다, app.js · prefs.py) */
.rows-pick { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; font-size: 12px; color: var(--muted); white-space: nowrap; }
.rows-pick select { padding: 3px 6px; font-size: 12px; width: auto; }
.top-link { padding: 4px 10px; font-size: 12.5px; white-space: nowrap; }   /* 위 띠 오른쪽 (메신저 내려받기) */
/* 태블릿 · 휴대폰: 표는 옆으로 밀어 보고, 덜 중요한 칸(.opt)은 숨긴다 */
@media (max-width: 760px) {
  main table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  th.opt, td.opt { display: none; }
  .titlebar { flex-wrap: wrap; gap: 8px; }
  .titlebar .ops { flex-wrap: wrap; }
}
button.link { border: 0; background: none; padding: 0; color: var(--accent); }
button.link.bad, button.bad { color: var(--red); }
form.inline { display: inline; }
form.stack { display: flex; flex-direction: column; gap: 10px; }
form.stack label { display: flex; flex-direction: column; gap: 4px; }
form.row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
form.row input { flex: 1 1 140px; }
form.row.tight { margin-top: 0; flex-wrap: nowrap; }
form.row.tight input { min-width: 220px; }
.hint { color: var(--muted); font-size: 12px; margin: 0; }
.titlebar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 통합 검색 (search.py) — 대시보드 제목 옆 칸 · 결과 화면 */
.dash-search { display: flex; gap: 6px; flex: 1 1 260px; max-width: 460px; }
.dash-search input, .search-form input { flex: 1; min-width: 0; }
.search-hits { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
.search-hits mark { background: color-mix(in srgb, var(--amber) 35%, transparent); color: inherit; padding: 0 1px; }
.more { font-size: 12px; font-weight: 400; margin-left: auto; }
code.big { font-size: 18px; padding: 4px 8px; background: var(--bg); border-radius: 6px; user-select: all; }
td.actions { white-space: nowrap; }
td.actions form + form, td.actions details + form { margin-left: 10px; }
td.actions details { display: inline-block; vertical-align: top; }
td.actions details[open] { display: block; margin-bottom: 6px; white-space: normal; }

.form fieldset { border: 1px solid var(--line); border-radius: 6px; margin: 0 0 12px; padding: 10px 12px;
                 display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
.form legend { color: var(--muted); padding: 0 4px; }
.form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.form label.full { grid-column: 1 / -1; }
.form .actions { display: flex; gap: 8px; }

.switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.switch input { appearance: none; width: 38px; height: 22px; border-radius: 11px; background: var(--gray);
                position: relative; cursor: pointer; border: 0; padding: 0; transition: background .15s; }
.switch input::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px;
                       border-radius: 50%; background: #fff; transition: left .15s; }
.switch input:checked { background: var(--green); }
.switch input:checked::after { left: 19px; }
.switch input:disabled { opacity: .5; cursor: wait; }
.switch.busy span { color: var(--amber); }

@media (max-width: 640px) {
  main { padding: 12px; }
  header.top { padding: 8px 12px; }
  .grid { grid-template-columns: 1fr; }
}
/* 접속 IP · 침입 탐지 */
a.stat { color: inherit; text-decoration: none; }
a.stat:hover, .stat.active { border-color: var(--accent); }
.findings > div { margin: 2px 0; white-space: normal; }
.findings .badge { margin-right: 4px; }
table.datagrid td.wrap.findings { max-width: 44em; word-break: keep-all; overflow-wrap: break-word; }   /* 해킹 우려 — 낱말 단위로 줄바꿈 */
td.findings > div { min-width: 30em; }   /* 표 칸은 min-width 를 무시하므로 안쪽 줄로 폭을 잡는다 */
td.when { white-space: nowrap; }   /* 마지막 시각 — 한 줄로 */
.checks { display: flex; flex-wrap: wrap; gap: 6px 18px; }
form.stack label.check, label.check { display: inline-flex; flex-direction: row; align-items: center; gap: 6px; }
label.check input { width: auto; margin: 0; }
label.check.keep { white-space: nowrap; }
label.check.keep select { width: auto; }
.keep-note { margin: -4px 0 0; }
.logout-timer { white-space: nowrap; font-variant-numeric: tabular-nums; }
.logout-timer.soon { color: var(--red); font-weight: 600; }
/* 시계열 · 열지도 */
svg.chart { width: 100%; min-width: 640px; height: auto; display: block; }
svg.chart .axis { stroke: var(--line); }
svg.chart .lbl { fill: var(--muted); font-size: 11px; }
svg.chart .cell0 { fill: var(--line); fill-opacity: .35; }
.s-green { fill: var(--green); } .s-red { fill: var(--red); } .s-amber { fill: var(--amber); }
.s-violet { fill: #a78bfa; } .s-gray { fill: var(--muted); }
.legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-bottom: 6px; }
.legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.sw.s-green { background: var(--green); } .sw.s-red { background: var(--red); } .sw.s-amber { background: var(--amber); }
.sw.s-violet { background: #a78bfa; } .sw.s-gray { background: var(--muted); }
svg.spark { width: 120px; height: 24px; }
.spark-line { fill: none; stroke: var(--accent); stroke-width: 1.5; }
.badge.accepted { background: transparent; border: 1px dashed var(--muted); color: var(--muted); }
.row.wrap { flex-wrap: wrap; }
form.approve-form { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; }
form.approve-form input[name=ports] { width: 9em; }
form.approve-form select { width: auto; }
form.approve-form label.check { white-space: nowrap; }
td { overflow-wrap: break-word; }  /* 칸이 넘칠 때만 끊는다 (anywhere 는 열을 지나치게 좁힌다) */
pre.out { max-height: 50vh; overflow: auto; }
/* 읽기 쉽게 — 한글은 낱말 단위로 줄바꿈, 숫자 열 머리는 오른쪽, 제목 옆 설명은 보통 굵기 */
th.num { text-align: right; }
h1 small, h2 small { font-weight: 400; font-size: 12.5px; color: var(--muted); }
td.wrap { white-space: normal; overflow-wrap: anywhere; }
.page { min-width: 0; overflow-x: clip; }
form.row label.grow { flex: 1 1 28em; display: flex; gap: 6px; align-items: center; }
form.row label.grow input { flex: 1; }
/* 업무 모듈 단추 — 메뉴의 '관리' 묶음 아래 두 줄 띄워서 */
.mod-btns { display: flex; flex-direction: column; gap: 6px; margin-top: 2.6em; padding: 0 14px; font-size: 13px; }
.mod-btns.solo { margin-top: 10px; }   /* 그룹웨어(7443): 위에 DevHub 메뉴가 없으니 바로 붙인다 */
.mod-btns .eapproval { display: flex; align-items: center; gap: 6px; text-align: left; font-weight: 600; }
.mod-btns .eapproval.active { border-color: var(--accent); color: var(--accent); }
.tabs { display: flex; gap: 2px; border-bottom: 2px solid var(--line); margin: 0 0 14px; }
.tabs a { padding: 7px 16px; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -2px; font-weight: 600; }
.tabs a:hover { color: var(--text); text-decoration: none; }
.tabs a.on { color: var(--accent); border-bottom-color: var(--accent); }
.grp-badge { margin-left: auto; }
.card.narrow.left { margin: 0 0 14px; }
/* 인증서 안내 (certguide.html, HTTP 80) */
.ca-state { border-left: 4px solid var(--gray); }
.ca-state.ok { border-left-color: var(--green); }
.ca-state.bad { border-left-color: var(--red); }
.ca-state.bad [data-msg] { color: var(--red); }
.ca-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 10px 0 0; }
.card.mine { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ca-dialog { max-width: 560px; width: calc(100% - 32px); border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: var(--text); padding: 18px 20px; }
.ca-dialog::backdrop { background: rgb(0 0 0 / 45%); }
.ca-dialog h2 { margin-top: 0; }

/* 맨 위로 — 오른쪽 아래, 조금 내려가면 보인다 (app.js data-to-top) */
.to-top { position: fixed; right: 20px; bottom: 20px; z-index: 70; padding: 8px 14px; border-radius: 20px;
          background: var(--card); border: 1px solid var(--line); color: var(--text); font-size: 13px; cursor: pointer;
          box-shadow: 0 4px 14px rgb(0 0 0 / .25); }
.to-top:hover { color: var(--accent); border-color: var(--accent); }
/* 라이선스 발급 화면 (licenses.html) */
.lic-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px 14px; align-items: end; }
.lic-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; font-weight: 600; }
.lic-form label.chk { flex-direction: row; align-items: center; font-weight: 400; white-space: nowrap; }
.lic-form .wide { grid-column: 1 / -1; }
.lic-form textarea { width: 100%; box-sizing: border-box; }
tr.lic-new td { background: color-mix(in srgb, var(--green) 14%, transparent); }
.lic-steps { margin: 8px 0 4px; padding-left: 22px; line-height: 1.75; font-size: 13.5px; }
.lic-steps li { margin-bottom: 4px; }
.lic-how > summary { cursor: pointer; }
.lic-btns { display: flex; gap: 8px; flex-wrap: wrap; }
.lic-pv { border: 1px solid var(--accent); border-radius: 8px; padding: 10px 12px; font-size: 13px;
          background: color-mix(in srgb, var(--accent) 8%, var(--card)); }
.lic-pv.bad { border-color: var(--red); background: color-mix(in srgb, var(--red) 8%, var(--card)); }
.lic-pv .warn { color: var(--red); margin: 6px 0 0; } .lic-pv .ok { color: var(--green); margin: 6px 0 0; font-weight: 700; }
.anl-acts { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 6px 0 10px; }
.anl-acts form { margin: 0; }
.anl-run { animation: nm-blink 1.4s ease-in-out infinite; }
@keyframes nm-blink { 50% { opacity: .45; } }
/* 최근 빌드 일자 — 테두리 색으로 강조 (대시보드 카드 · 메신저 앱 줄 · 앱 받기 화면, 2026-10-14 사용자 요청) */
.build-at { display: inline-block; padding: 0 7px; border: 1.5px solid var(--accent); border-radius: 6px; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent); font-weight: 600; font-size: 12px; line-height: 1.6; white-space: nowrap; }
/* 접속자 수 — 배경색 · 테두리색 (1명 이상 초록 · 0명 회색, 대시보드 카드 · 목록 · '지금 접속' 줄, 2026-10-14 사용자 요청) */
.online-n { display: inline-block; min-width: 1.6em; padding: 0 6px; text-align: center; border: 1.5px solid var(--green); border-radius: 6px;
  color: var(--green); background: color-mix(in srgb, var(--green) 14%, transparent); font-weight: 700; line-height: 1.5; }
.online-n.zero { border-color: var(--gray); color: var(--gray); background: color-mix(in srgb, var(--gray) 12%, transparent); font-weight: 600; }
/* 작업하던 자리를 되살렸을 때 — 위쪽 알림을 오른쪽 위에 잠깐 띄움 (app.js floatFlash). 바탕은 카드색 위에 알림색을 겹쳐 뒤가 비치지 않게 */
.flash.flash-float { --tone: var(--green); position: fixed; top: calc(var(--top-h, 48px) + 10px); right: 16px; z-index: 200;
  max-width: min(460px, calc(100vw - 32px)); margin: 0; border: 1.5px solid var(--tone); color: var(--text);
  background: linear-gradient(color-mix(in srgb, var(--tone) 14%, transparent), color-mix(in srgb, var(--tone) 14%, transparent)), var(--card);
  box-shadow: 0 6px 18px rgb(0 0 0 / .18); cursor: pointer; transition: opacity .6s; }
.flash.flash-float.bad { --tone: var(--red); }
.flash.flash-float.warn { --tone: var(--amber); }
.flash.flash-float.gone { opacity: 0; }

/* 자동 백업(/backups) — 구분 · 확인 막대, 외장 디스크 사용량 강조 (2026-10-15) */
.bk-bar { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin: 4px 0 10px; }
.bk-bar .bk-ver { font-size: .92em; }
.bk-bar .bk-ver .k { color: var(--muted, #8a90a0); margin-right: 4px; }
.bk-disk td { vertical-align: middle; }
.bk-hl { color: var(--accent); font-size: 1.15em; }
.bk-free-green { color: var(--green); } .bk-free-amber { color: var(--amber); } .bk-free-red { color: var(--red); }
.bk-bar-use { display: inline-block; width: 90px; height: 7px; margin-left: 6px; vertical-align: middle; border-radius: 4px;
  background: color-mix(in srgb, var(--gray) 25%, transparent); overflow: hidden; }
.bk-bar-use i { display: block; height: 100%; }
.bk-bar-use i.green { background: var(--green); } .bk-bar-use i.amber { background: var(--amber); } .bk-bar-use i.red { background: var(--red); }
.bk-check { border-left: 4px solid var(--red); }

/* 업무일지(메모) — 왼쪽 메뉴 강조 링크 (2026-10-15) — 화면 스타일은 그룹웨어 업무일지 모듈 static/worklog.css */
.menu li a.hot { width: calc(100% - 20px); margin: 4px 10px; border-radius: 7px; background: var(--accent-btn, var(--accent));
        color: var(--on-accent); font-weight: 700; }
.menu li a.hot:hover { background: var(--accent-btn, var(--accent)); filter: brightness(1.1); }
.menu li a.hot.active { color: var(--on-accent); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--on-accent) 55%, transparent); }
.wl-card-quick { display: flex; gap: 4px; }   /* 대시보드 '업무일지' 카드의 빠른 메모 칸 */
.wl-card-quick input { flex: 1; min-width: 0; }
/* 헤르메스 에이전트 모델 연결 고르기 (hermes_agent.html) */
.ha-choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; margin: 8px 0; }
.ha-choice { display: flex; flex-direction: column; gap: 3px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; cursor: pointer; }
.ha-choice:has(input:checked), .ha-choice.on { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.ha-choice code { overflow-wrap: anywhere; }
/* 서버 IP(개발자 · 최고관리자)에서만 열리는 기능 · 메뉴 — 노란 † (web.srv_mark, 2026-10-15) */
.srv-mark { color: #e0b000; font-weight: 800; margin-left: 2px; font-size: .85em; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .srv-mark { color: #ffd43b; } }
:root[data-theme="dark"] .srv-mark, :root[data-theme="slate"] .srv-mark { color: #ffd43b; }
/* 관리자에게만 보이는 기능 (그룹웨어 7443) — 7444 서버 전용과 같은 노란 † (.srv-mark.adm-mark) */
/* 계정 목록 도구 (app.js — 표 바로 위 왼쪽): 찾기 · 정렬(부서별 · 가나다 · 알파벳) · 오름/내림 · 초성 */
.ppl-bar { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; justify-content: flex-start; margin: 0 0 8px; }
.ppl-bar .ppl-q { min-width: 16em; flex: 0 1 22em; }
.ppl-chips { display: inline-flex; flex-wrap: wrap; gap: 2px; }
.ppl-chips button { padding: 1px 7px; min-width: 2em; font-size: .85em; }
.ppl-chips button.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
tr.ppl-group td { background: color-mix(in srgb, var(--accent) 10%, transparent); font-weight: 700; font-size: .9em; }
@media (max-width: 760px) { .ppl-bar .ppl-q { min-width: 0; flex: 1 1 100%; } }

