/* ============================================================
   KWONMO — mo.css
   서버 모니터링(hm/page/mo/index.php) 전용. 사이트 헤더 아래 본문 틀은 이 파일이 갖는다.
   수치·색은 val.css 토큰만 쓴다. 모바일 한 단 → 넓은 화면 두 단.
   ============================================================ */

.mo-wrap {
  max-width: var(--page-max); margin-inline: auto;
  padding: var(--sp-md) var(--sp-md) calc(var(--sp-xl) + var(--safe-b));
  color: var(--text);
}
.mo-wrap [hidden] { display: none !important; }

/* ================= 머리 ================= */
/* 좁은 화면에서는 제호·상태·버튼이 각자 한 줄을 쓴다(아래 브레이크포인트에서 한 줄로 합친다) */
.mo-top {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-xs) var(--sp-md);
  margin-bottom: var(--sp-lg);
  padding-bottom: var(--sp-sm); border-bottom: var(--rule-w) solid var(--rule-ink);
}
.mo-h1 { flex: 1 1 100%; margin: 0; font-family: var(--ed-display); font-size: 1.4rem; }
/* 조각(호스트·시각·실시간)마다 통째로 줄을 넘긴다 — 폭이 모자랄 때 날짜가 글자 단위로 쪼개지던 자리 */
.mo-meta {
  flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 0 var(--sp-sm);
  font-size: .8rem; color: var(--text-dim);
}
.mo-meta > span { white-space: nowrap; }
.mo-acts { flex: 0 0 auto; margin-left: auto; display: flex; gap: var(--sp-sm); }
.mo-live { color: var(--text-dim); }
.mo-live.is-on::before {
  content: ""; display: inline-block; width: .5rem; height: .5rem; margin-right: .35rem;
  border-radius: 50%; background: var(--cat-work); vertical-align: .05rem;
}
.mo-btn {
  padding: .35rem .9rem; border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font: inherit; font-size: .85rem; cursor: pointer;
}
.mo-btn:hover { background: var(--surface-2); }
.mo-btn:disabled { cursor: wait; opacity: .5; }
/* 멈춘 동안은 눌린 상태가 한눈에 보여야 한다 — 다시 켤 때까지 값이 그대로 멈춰 있기 때문 */
.mo-btn[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }

.mo-status {
  margin-bottom: var(--sp-md); padding: var(--sp-sm) var(--sp-md);
  border-left: var(--stroke-3) solid var(--danger); background: var(--surface);
  font-size: .88rem; color: var(--danger);
}

/* ================= 칸 배치 ================= */
.mo-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-lg); }
.mo-sec { position: relative; min-width: 0; }
.mo-sec h2 {
  margin: 0 0 var(--sp-sm); padding-bottom: var(--sp-xs);
  border-bottom: var(--hairline) solid var(--rule-soft);
  font-family: var(--ed-display); font-size: 1.1rem;
}
.mo-sec h3 { margin: var(--sp-md) 0 var(--sp-xs); font-size: .85rem; font-weight: 600; color: var(--text-mid); }
.mo-sec small { font-family: var(--ed-label); font-size: .72rem; font-weight: 400; color: var(--text-dim); }
/* h2 의 제목 글자를 피해 오른쪽 끝에 붙는다. 제목이 두 줄이 돼도 자리는 모서리 그대로 */
.mo-sec h2 { padding-right: 2rem; }

/* ================= 섹션 설명(? 버튼) ================= */
.mo-help {
  position: absolute; top: 0; right: 0;
  width: 1.6rem; height: 1.6rem; padding: 0;
  border: var(--hairline) solid var(--line); border-radius: 50%;
  background: var(--surface); color: var(--text-dim);
  font: inherit; font-size: .8rem; line-height: 1; cursor: pointer;
}
.mo-help:hover { color: var(--text); border-color: var(--text-dim); }
.mo-help[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
/* 아래 내용을 밀어내지 않고 덮는다 — 설명을 보려고 표가 움직이면 보던 자리를 잃는다 */
.mo-guide {
  position: absolute; top: 1.9rem; right: 0; z-index: 20;
  width: min(30rem, 100%); max-height: 70dvh; overflow: auto; padding: var(--sp-md);
  border: var(--hairline) solid var(--line); border-radius: var(--radius-md);
  background: rgba(var(--menu-bg), var(--menu-alpha));
  backdrop-filter: blur(var(--sp-sm)); -webkit-backdrop-filter: blur(var(--sp-sm));
}
/* 항목 이름과 설명을 위아래로 둔다 — 좌우로 나누면 좁은 화면에서 설명 쪽이 글자 단위로 쪼개진다 */
.mo-guide dl { margin: 0; font-size: .8rem; line-height: 1.7; }
.mo-guide dt { font-weight: 600; color: var(--text); }
.mo-guide dd { margin: 0 0 var(--sp-sm); color: var(--text-mid); }
.mo-guide dd:last-child { margin-bottom: 0; }
.mo-guide b { font-weight: 600; color: var(--text); }

/* ================= 게이지 ================= */
.mo-gauges { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-sm); }
.mo-gauge {
  padding: var(--sp-sm) .75rem; border: var(--hairline) solid var(--line); border-radius: var(--radius-md);
  background: var(--surface);
}
.mo-gauge h3 { margin: 0; font-size: .75rem; font-weight: 600; color: var(--text-mid); }
.mo-gauge-main { margin: .15rem 0 .35rem; font-family: var(--ed-display); font-size: 1.35rem; white-space: nowrap; }
.mo-gauge-sub { margin-top: .35rem; font-size: .72rem; color: var(--text-dim); overflow-wrap: anywhere; }
.mo-gauge.is-warn .mo-gauge-main, .mo-disk.is-warn .mo-gauge-main { color: var(--warn); }
.mo-gauge.is-danger .mo-gauge-main, .mo-disk.is-danger .mo-gauge-main { color: var(--danger); }

/* 막대 폭은 mo.js 가 el.style 로 넣는다 */
.mo-meter { height: .45rem; border-radius: var(--radius-sm); background: var(--surface-2); overflow: hidden; }
/* 평상시는 중립 잉크 — 조간판 강조색(--accent)이 붉은 잉크라 위험(--danger)과 구별되지 않는다 */
.mo-meter span { display: block; height: 100%; width: 0; background: var(--text-mid); }
.mo-meter.is-warn span { background: var(--warn); }
.mo-meter.is-danger span { background: var(--danger); }

/* ================= 그래프 ================= */
.mo-charts { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-sm); margin-top: var(--sp-sm); }
.mo-chart {
  margin: 0; padding: var(--sp-sm) .75rem;
  border: var(--hairline) solid var(--line); border-radius: var(--radius-md); background: var(--surface);
}
.mo-chart figcaption { display: flex; gap: var(--sp-md); margin-bottom: var(--sp-xs); font-size: .75rem; color: var(--text-mid); }
.mo-chart figcaption b { margin-left: auto; font-weight: 400; color: var(--text-dim); }
.mo-key::before {
  content: ""; display: inline-block; width: .8rem; height: var(--stroke-2); margin-right: .3rem; vertical-align: middle;
}
.mo-key.is-cpu::before, .mo-key.is-rx::before { background: var(--accent); }
.mo-key.is-mem::before, .mo-key.is-tx::before { background: var(--cat-work); }
.mo-chart svg {
  display: block; width: 100%; height: 7rem;
  background: var(--surface-2); border-radius: var(--radius-sm);
}
.mo-chart polyline { fill: none; stroke-width: var(--stroke-2); stroke-linejoin: round; }
.mo-line-0 { stroke: var(--accent); }
.mo-line-1 { stroke: var(--cat-work); }

/* ================= 표 ================= */
.mo-disk { margin-bottom: var(--sp-sm); }
.mo-table { width: 100%; border-collapse: collapse; font-size: .84rem; }
.mo-table th, .mo-table td {
  padding: .4rem var(--sp-xs); border-bottom: var(--hairline) solid var(--line);
  text-align: left; vertical-align: middle; overflow-wrap: anywhere;
}
.mo-table th { font-weight: 600; }
.mo-table thead th { font-size: .72rem; font-weight: 400; color: var(--text-dim); }
.mo-table tr.is-child th { padding-left: var(--sp-md); font-weight: 400; color: var(--text-mid); }
.mo-table tfoot th, .mo-table tfoot td { border-bottom: 0; border-top: var(--stroke-2) solid var(--rule-ink); }
.mo-kv th { width: 7rem; font-weight: 400; color: var(--text-mid); }
.mo-num { text-align: right !important; white-space: nowrap; font-variant-numeric: tabular-nums; }
.mo-bar-cell { width: 35%; }
.mo-sub, .mo-err { display: block; font-size: .72rem; font-weight: 400; color: var(--text-dim); }
.mo-err { color: var(--danger); }
.mo-dim { color: var(--text-dim); }
.mo-table tr.is-warn td { color: var(--warn); }
.mo-table tr.is-danger th, .mo-table tr.is-danger td { color: var(--danger); }
.mo-ssd-head { margin: 0 0 var(--sp-xs); font-size: .82rem; color: var(--text-mid); }
.mo-ok { color: var(--cat-work); font-weight: 600; }
.mo-bad { color: var(--danger); font-weight: 600; }
.mo-note { margin: var(--sp-xs) 0; font-size: .74rem; color: var(--text-dim); overflow-wrap: anywhere; }

.mo-svc { display: flex; flex-wrap: wrap; gap: var(--sp-xs); margin: 0; padding: 0; list-style: none; }
.mo-svc li {
  padding: .15rem .5rem; border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  font-size: .78rem;
}
.mo-svc span { color: var(--text-dim); }
.mo-svc .is-ok { border-color: var(--cat-work); }
.mo-svc .is-bad { border-color: var(--danger); color: var(--danger); }

.mo-pkgs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); column-gap: var(--sp-md);
  margin: 0; padding: 0; list-style: none; font-size: .84rem;
}
.mo-pkg { position: relative; border-bottom: var(--hairline) solid var(--line); }
.mo-pkg-name {
  display: block; width: 100%; padding: .35rem var(--sp-xs); border: 0; background: none;
  color: inherit; font: inherit; text-align: left; overflow-wrap: anywhere; cursor: help;
}
.mo-pkg-name b { font-weight: 600; }
.mo-pkg:hover .mo-pkg-name, .mo-pkg.is-open .mo-pkg-name { background: var(--surface-2); }
/* 아래 이름들을 밀어내지 않고 덮는다. 칸 폭에 맞춰 바로 아래 붙어서, 마우스를 옮겨 '복사' 를 눌러도 닫히지 않는다 */
.mo-pkg-pop {
  display: none; position: absolute; top: 100%; left: 0; right: 0; z-index: 10;
  padding: var(--sp-xs) var(--sp-sm);
  border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  background: rgba(var(--menu-bg), var(--menu-alpha));
  backdrop-filter: blur(var(--sp-sm)); -webkit-backdrop-filter: blur(var(--sp-sm));
}
.mo-pkg:hover .mo-pkg-pop, .mo-pkg.is-open .mo-pkg-pop { display: block; }
.mo-pkg-pop p { margin: 0 0 var(--sp-xs); font-size: .78rem; line-height: 1.5; color: var(--text-mid); overflow-wrap: anywhere; }
.mo-pkg-pop .mo-pkg-ko { color: var(--text); }
.mo-pkg-pop .mo-pkg-ko.mo-err { color: var(--danger); }
.mo-pkg-copy {
  padding: .1rem .5rem; border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font: inherit; font-size: .74rem; cursor: pointer;
}
.mo-pkg-copy:hover { background: var(--surface-2); }

/* 권한·수집기 문제로 못 읽은 칸 */
.mo-na {
  margin: var(--sp-xs) 0; padding: var(--sp-sm) .75rem;
  border: var(--hairline) dashed var(--line); border-radius: var(--radius-sm);
  font-size: .8rem; color: var(--text-dim);
}
.mo-na b { display: block; margin-bottom: .15rem; color: var(--text-mid); }
.mo-na span { overflow-wrap: anywhere; }

/* ================= 로그 ================= */
.mo-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-xs); margin-bottom: var(--sp-sm); }
.mo-tabs button {
  padding: .3rem .75rem; border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text-mid); font: inherit; font-size: .8rem; cursor: pointer;
}
.mo-tabs button[aria-selected="true"] { border-color: var(--accent); color: var(--text); }
/* 긴 줄은 상자 안에서만 가로로 넘긴다 — 페이지 전체가 옆으로 밀리지 않게 */
.mo-log {
  max-height: 28rem; margin: 0; padding: var(--sp-sm) .75rem; overflow: auto;
  border-radius: var(--radius-sm); background: var(--surface-2); color: var(--text);
  font-family: "JetBrains Mono", Consolas, monospace; font-size: .72rem; line-height: 1.55; white-space: pre;
}

/* ================= fail2ban 오늘 분석 ================= */
.mo-report-acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-xs); margin-bottom: var(--sp-sm); }
.mo-report-acts .mo-note { margin: 0; }
.mo-verdict { margin: 0 0 var(--sp-sm); padding: 0; list-style: none; font-size: .84rem; }
.mo-verdict li {
  margin-bottom: var(--sp-xs); padding: .35rem .6rem; border-left: var(--stroke-3) solid var(--cat-work);
  background: var(--surface); overflow-wrap: anywhere;
}
.mo-verdict li.is-warn { border-left-color: var(--warn); }

.mo-f2b-rows { margin-top: var(--sp-sm); max-height: 28rem; overflow: auto; }
.mo-f2b-row { border-bottom: var(--hairline) solid var(--line); font-size: .8rem; }
.mo-f2b-row summary {
  display: flex; gap: var(--sp-sm); align-items: baseline; padding: .3rem var(--sp-xs);
  cursor: pointer; list-style: none;
}
.mo-f2b-row summary::-webkit-details-marker { display: none; }
.mo-f2b-row summary:hover { background: var(--surface-2); }
.mo-f2b-time { flex: none; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.mo-f2b-tag {
  flex: none; min-width: 3.6rem; padding: 0 .4rem; border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  font-size: .72rem; text-align: center; color: var(--text-mid);
}
.mo-f2b-text { min-width: 0; overflow-wrap: anywhere; }
.mo-f2b-row.is-found .mo-f2b-tag { border-color: var(--warn); color: var(--warn); }
.mo-f2b-row.is-ban .mo-f2b-tag, .mo-f2b-row.is-restore .mo-f2b-tag, .mo-f2b-row.is-already .mo-f2b-tag { border-color: var(--cat-work); color: var(--cat-work); }
.mo-f2b-row.is-error .mo-f2b-tag, .mo-f2b-row.is-error .mo-f2b-text { border-color: var(--danger); color: var(--danger); }
.mo-f2b-row.is-other .mo-f2b-tag { border-style: dashed; }
.mo-f2b-row > p { margin: 0 0 var(--sp-xs); padding: 0 var(--sp-xs); color: var(--text-mid); line-height: 1.6; }
.mo-f2b-row > pre {
  margin: 0 0 var(--sp-sm); padding: var(--sp-xs) .6rem; overflow: auto; border-radius: var(--radius-sm);
  background: var(--surface-2); font-family: "JetBrains Mono", Consolas, monospace; font-size: .7rem; white-space: pre;
}
/* nginx 에러 묶음 — 등급(ok·warn·danger)으로만 색을 나눈다 */
.mo-f2b-row.is-err-ok .mo-f2b-tag { border-color: var(--cat-work); color: var(--cat-work); }
.mo-f2b-row.is-err-warn .mo-f2b-tag { border-color: var(--warn); color: var(--warn); }
.mo-f2b-row.is-err-danger .mo-f2b-tag, .mo-f2b-row.is-err-danger .mo-f2b-text { border-color: var(--danger); color: var(--danger); }
.mo-verdict li.is-danger { border-left-color: var(--danger); color: var(--danger); }
/* 재기동 묶음 안의 줄은 한 단 들여 */
.mo-f2b-row .mo-f2b-row { margin-left: var(--sp-md); }

/* ================= 넓은 화면 ================= */
@media (min-width: 48rem) {
  .mo-wrap { padding-inline: var(--sp-lg); }
  .mo-h1 { flex: 0 0 auto; }
  .mo-meta { flex: 1 1 auto; }
  .mo-gauges { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .mo-charts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 64rem) {
  .mo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-lg) var(--sp-xl); }
  .mo-sec-wide { grid-column: 1 / -1; }
  .mo-gauges { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
