﻿/* ============================================================================
   CoFo 태양광 웹 모니터링 — 디자인 시스템

   Bootstrap 을 걷어내지 않고 위에 덮는다. 격자·유틸리티는 그대로 쓰고
   생김새만 우리 것으로 바꾼다.

   ── 색을 이렇게 쓰는 이유 ────────────────────────────────────────────────

   **초록은 상태 색이지 동작 색이 아니다.**

   예전에는 현장 WPF 의 PrimaryButton 을 그대로 가져와 초록을 기본 버튼에
   썼다. 그런데 이 화면에서 초록은 이미 "정상" 이라는 뜻을 갖는다. 같은 색이
   [저장] 버튼과 "정상" 배지에 동시에 쓰이면, 초록을 보고 상태인지 누를
   것인지 매번 다시 판단해야 한다.

   그래서 나눴다 —

       파랑   누를 수 있는 것 (링크 · 기본 버튼 · 선택된 메뉴 · 차트)
       초록   정상
       노랑   주의
       빨강   이상 · 통신 없음

   **현장 WPF 와 색이 갈린다.** 의도한 것이다. 다만 나중에 WPF 도 같은
   기준으로 정리하는 것이 맞다 — 같은 제품이므로 두 화면이 다른 규칙으로
   색을 쓰면 안 된다.
   ============================================================================ */

:root {
    /* 바탕 */
    --cofo-page-bg:   #F4F6FA;
    --cofo-card-bg:   #FFFFFF;
    --cofo-soft:      #F7F9FC;   /* 표 머리 · 계측 칸처럼 살짝 눌린 면 */
    --cofo-border:    #E8ECF3;
    --cofo-border-2:  #DDE3EC;   /* 입력칸처럼 또렷해야 하는 테두리 */
    --cofo-hair:      #EEF1F6;   /* 카드 · 표 행을 나누는 최소한의 선 */
    --cofo-line:      #E3E8F0;   /* 표 머리 아래처럼 한 단계 또렷한 선 */

    /* 사이드바 — 밝게 간다. 어두운 기둥이 화면 왼쪽을 무겁게 눌렀다. */
    --cofo-sidebar-bg:     #FFFFFF;
    --cofo-sidebar-text:   #6B7488;
    --cofo-sidebar-active: #1D4ED8;
    --cofo-nav-active-bg:  #F1F3F8;
    --cofo-nav-hover-bg:   #F7F8FB;

    /* 로그인 왼쪽 패널은 계속 어둡게 둔다. 거기서는 무게가 필요하다. */
    --cofo-brand-bg:   #151B33;
    --cofo-brand-text: #98A0BC;

    /* 글자 */
    --cofo-text:      #171C2C;
    --cofo-text-2:    #5A6479;
    --cofo-text-3:    #98A1B3;

    /* 뜻을 가진 색 */
    --cofo-blue:      #2563EB;
    --cofo-blue-soft: #EEF4FF;
    --cofo-green:     #16A34A;
    --cofo-green-soft:#E9F8EF;
    --cofo-yellow:    #C2740A;
    --cofo-yellow-soft:#FEF4E3;
    --cofo-red:       #DC2626;
    --cofo-red-soft:  #FDECEC;
    --cofo-violet:    #7C3AED;
    --cofo-violet-soft:#F3EDFF;
    --cofo-accent:    var(--cofo-blue);

    --cofo-radius:    12px;
    --cofo-radius-sm: 9px;
    --cofo-sidebar-w: 224px;
    --cofo-shadow:    0 1px 2px rgba(23, 28, 44, .04);
    --cofo-shadow-2:  0 8px 24px rgba(23, 28, 44, .10);

    /* Bootstrap 토큰도 같이 맞춘다 — 안 맞추면 기본 파랑이 곳곳에 남는다. */
    --bs-primary:      #2563EB;
    --bs-body-color:   var(--cofo-text);
    --bs-body-bg:      var(--cofo-page-bg);
    --bs-border-color: var(--cofo-border);
}

/* ── 바탕 ─────────────────────────────────────────────────────────────── */

html, body { height: 100%; }

body {
    /* Pretendard 는 CDN 의 woff2 동적 서브셋을 쓴다(_Layout 참조).
       못 받아오면 아래 시스템 폰트로 내려간다 — 한글이 깨지지 않는 것이 우선이다. */
    font-family: Pretendard, -apple-system, BlinkMacSystemFont, "Malgun Gothic",
                 "맑은 고딕", "Apple SD Gothic Neo", sans-serif;
    font-size: 14px;
    line-height: 1.55;
    color: var(--cofo-text);
    background: var(--cofo-page-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* 숫자는 자릿수가 흔들리면 표에서 읽기 어렵다. 폭을 고정한다. */
.num, .cofo-kpi-value, .cofo-meas .val, table td.text-end, .cofo-ch {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}

a { color: var(--cofo-blue); text-decoration: none; }
a:hover { text-decoration: underline; }

/* 표 안의 이름은 평소엔 본문 색이고, 가리켰을 때만 링크가 된다. */
.table a { color: var(--cofo-text); }
.table a:hover { color: var(--cofo-blue); }

::selection { background: #D7E4FF; }

/* 스크롤바가 굵으면 표 옆에서 눈에 먼저 띈다. */
* { scrollbar-width: thin; scrollbar-color: #CBD3E1 transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: #CBD3E1; border-radius: 8px; }
*::-webkit-scrollbar-track { background: transparent; }

/* ── 뼈대 ─────────────────────────────────────────────────────────────── */

.cofo-shell { display: flex; min-height: 100vh; }

.cofo-side {
    width: var(--cofo-sidebar-w);
    flex: 0 0 var(--cofo-sidebar-w);
    background: var(--cofo-sidebar-bg);
    border-right: 1px solid var(--cofo-hair);
    color: var(--cofo-sidebar-text);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
}

.cofo-brand {
    padding: 20px 18px 16px;
    color: var(--cofo-text);
    font-weight: 700;
    font-size: 15px;
    letter-spacing: -0.3px;
    line-height: 1.25;
}
.cofo-brand small {
    display: block;
    font-size: 11px;
    font-weight: 500;
    color: var(--cofo-text-3);
    letter-spacing: 0;
    margin-top: 2px;
}

.cofo-nav { padding: 6px 10px; flex: 1 1 auto; overflow-y: auto; }

.cofo-nav-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    color: var(--cofo-text-3);
    padding: 14px 8px 6px;
    text-transform: uppercase;
}

.cofo-nav a {
    display: block;
    padding: 9px 12px 9px 14px;
    margin-bottom: 2px;
    border-radius: var(--cofo-radius-sm);
    color: var(--cofo-sidebar-text);
    font-size: 13.5px;
    font-weight: 500;
    text-decoration: none;
    transition: background .12s, color .12s;
}
.cofo-nav a:hover { background: var(--cofo-nav-hover-bg); color: var(--cofo-text); text-decoration: none; }
/* _Layout 의 on() 도우미는 "active" 를 돌려준다. 현장 선택 메뉴만 "on" 을 쓴다.
   둘 다 받아둔다 — 한쪽만 적으면 그 화면에서 선택 표시가 사라진다. */
.cofo-nav a.active,
.cofo-nav a.on {
    background: var(--cofo-nav-active-bg);
    color: var(--cofo-text);
    font-weight: 600;
    /* 선택 표시를 색이 아니라 위치로 준다. 파랑을 메뉴에까지 쓰면
       정작 눌러야 할 것이 묻힌다.
       안쪽 그림자로 그린다 — ::before 는 nav 의 overflow 에 잘린다. */
    box-shadow: inset 3px 0 0 var(--cofo-blue);
}
.cofo-nav a.active .ico, .cofo-nav a.on .ico { color: var(--cofo-blue); }

.cofo-side-foot {
    border-top: 1px solid var(--cofo-hair);
    padding: 14px 16px 16px;
}
.cofo-side-foot .who { font-size: 13px; font-weight: 600; color: var(--cofo-text); }
.cofo-side-foot .role { font-size: 11px; color: var(--cofo-text-3); }

.cofo-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.cofo-top {
    background: var(--cofo-card-bg);
    border-bottom: 1px solid var(--cofo-hair);
    padding: 14px 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    position: sticky;
    top: 0;
    z-index: 20;
}
.cofo-top-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.cofo-top h1 { font-size: 19px; font-weight: 700; margin: 0; letter-spacing: -0.5px; }
.cofo-top .sub { font-size: 11.5px; color: var(--cofo-text-3); white-space: nowrap; }
.cofo-top-right { display: flex; align-items: center; gap: 8px; white-space: nowrap; }

.cofo-body { padding: 24px 28px 48px; flex: 1 1 auto; }

/* 구역 제목. 카드 묶음 위에 얹는다. */
.cofo-sec {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--cofo-text-3);
    margin: 26px 0 11px;
    letter-spacing: .03em;
}
.cofo-sec:first-child { margin-top: 0; }

/* ── 카드 ─────────────────────────────────────────────────────────────── */

/* **부트스트랩 카드처럼 보이지 않게 한다.**
   그림자를 빼고 테두리를 머리카락 굵기로 낮춘다. 흰 판이 여러 장 떠 있는
   대신 한 장의 종이에 구역이 나뉜 것처럼 읽혀야 한다. */
.card {
    background: var(--cofo-card-bg);
    border: 1px solid var(--cofo-hair);
    border-radius: 14px;
    box-shadow: none;
}
/* 머리에 가로선을 긋지 않는다. 그 선 하나가 부트스트랩 카드의 인상을
   거의 다 만든다. 제목은 상자의 뚜껑이 아니라 그냥 작은 라벨이다. */
.card-header {
    background: transparent;
    border-bottom: none;
    padding: 16px 18px 2px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--cofo-text-3);
    border-radius: 0;
}
.card-body { padding: 14px 18px 18px; }
.card > .table-responsive:last-child,
.card > .table:last-child { border-radius: 0 0 var(--cofo-radius) var(--cofo-radius); overflow: hidden; }

/* ── KPI ──────────────────────────────────────────────────────────────── */

.cofo-kpi { padding: 15px 16px 14px; }
.cofo-kpi-label {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--cofo-text-3);
    margin-bottom: 5px;
}
.cofo-kpi-value {
    font-size: 25px;
    font-weight: 700;
    letter-spacing: -0.8px;
    line-height: 1.15;
    color: var(--cofo-text);
}
.cofo-kpi-value .unit {
    font-size: 12px;
    font-weight: 500;
    color: var(--cofo-text-3);
    margin-left: 4px;
    letter-spacing: 0;
}
/* **빈 상태를 큰 글자로 강조하지 말 것.** 자리는 흐리게 비우고
   안내는 아래 작은 줄이 한다. 굵게 쓰면 값이 있는 카드보다 더 눈에 띈다. */
.cofo-kpi-value.empty { color: var(--cofo-text-3); font-weight: 500; }
.cofo-kpi-foot { font-size: 11px; color: var(--cofo-text-3); margin-top: 5px; }

/* ── 표 ───────────────────────────────────────────────────────────────── */

.table { margin: 0; color: var(--cofo-text); border-color: var(--cofo-border); }
.table > :not(caption) > * > * { background: transparent; }

/* 표 머리에 회색 띠를 깔지 않는다. 띠가 있으면 어느 사이트나 같은 표로
   보인다. 아래 실선 하나로만 나눈다. */
.table thead th {
    background: transparent;
    border-bottom: 1px solid var(--cofo-line);
    border-top: none;
    padding: 8px 14px 10px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .03em;
    color: var(--cofo-text-3);
    white-space: nowrap;
}
.table tbody td {
    padding: 11px 14px;
    border-top: 1px solid var(--cofo-hair);
    border-bottom: none;
    vertical-align: middle;
    font-size: 13px;
}
.table tbody tr:first-child td { border-top: none; }
.table-hover tbody tr:hover > * { background: #F8F9FC; }
.table-responsive { border-radius: inherit; }

/* ── 상태 배지 ────────────────────────────────────────────────────────── */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    border: 1px solid transparent;
}
.pill::before {
    content: "";
    width: 5px; height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}
.pill-ok   { background: var(--cofo-green-soft);  color: var(--cofo-green); }
.pill-warn { background: var(--cofo-yellow-soft); color: var(--cofo-yellow); }
.pill-down { background: var(--cofo-red-soft);    color: var(--cofo-red); }
.pill-mute { background: var(--cofo-soft);        color: var(--cofo-text-2); }
/* 시험용으로 넣은 현장. 진짜 현장과 한 목록에 섞여도 한눈에 갈린다.
   회색으로 두지 말 것 — 스쳐보면 그냥 라벨로 읽힌다. */
.pill-test { background: var(--cofo-violet-soft); color: var(--cofo-violet); }

/* ── 값의 뜻 ──────────────────────────────────────────────────────────── */

/* 측정하지 않은 것. 0 으로 보이면 고장으로 오해한다. */
.val-none { color: var(--cofo-text-3); font-weight: 400; }
.val-warn { color: var(--cofo-yellow); font-weight: 600; }
.val-bad  { color: var(--cofo-red);    font-weight: 600; }

/* 빈 상태. 아이콘 없이 문장 둘로 간다 — 큰 그림이 자리를 먹으면
   "없다" 는 사실보다 그림이 먼저 읽힌다. */
.t-empty {
    padding: 34px 20px;
    text-align: center;
    color: var(--cofo-text-3);
    font-size: 13px;
}

/* ── 버튼 ─────────────────────────────────────────────────────────────── */

/* 부트스트랩 기본 버튼은 두껍고 둥글다. 얇고 각지게 간다. */
.btn {
    border-radius: 8px;
    font-size: 12.5px;
    font-weight: 500;
    padding: 7px 13px;
    border: 1px solid transparent;
    transition: background .12s, border-color .12s, color .12s;
}
.btn:focus, .btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .18);
}
.btn-sm { padding: 6px 11px; font-size: 12.5px; }

.btn-primary {
    background: var(--cofo-blue);
    border-color: var(--cofo-blue);
    color: #fff;
}
.btn-primary:hover, .btn-primary:focus { background: #1D4ED8; border-color: #1D4ED8; color: #fff; }

/* 보조 버튼은 배경을 두지 않는다. 흰 알약이 나란히 있으면 부트스트랩이다. */
.btn-outline-secondary {
    background: transparent;
    border-color: var(--cofo-line);
    color: var(--cofo-text-2);
}
.btn-outline-secondary:hover {
    background: var(--cofo-soft);
    border-color: var(--cofo-border-2);
    color: var(--cofo-text);
}

.btn-danger { background: var(--cofo-red); border-color: var(--cofo-red); }
.btn-danger:hover { background: #B91C1C; border-color: #B91C1C; }

/* ── 폼 ───────────────────────────────────────────────────────────────── */

.form-label {
    font-size: 11.5px;
    color: var(--cofo-text-2);
    margin-bottom: 5px;
    font-weight: 600;
}
/* 테두리 대신 눌린 면으로 입력칸을 만든다. 포커스에서만 선이 생긴다. */
.form-control, .form-select {
    border: 1px solid transparent;
    border-radius: 8px;
    font-size: 13px;
    padding: 8px 11px;
    color: var(--cofo-text);
    background-color: var(--cofo-soft);
}
.form-control:focus, .form-select:focus {
    background-color: var(--cofo-card-bg);
    border-color: #9BB8F5;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .12);
}
.form-control::placeholder { color: var(--cofo-text-3); }
.form-text { font-size: 11.5px; color: var(--cofo-text-3); }

/* ── 알림 띠 ──────────────────────────────────────────────────────────── */

/* 사방을 두른 색 상자 대신 왼쪽에 굵은 선만 둔다.
   상자는 시선을 가두고, 선은 문장을 읽게 한다. */
.alert {
    border-radius: 0 8px 8px 0;
    border: none;
    border-left: 3px solid currentColor;
    padding: 11px 14px;
    font-size: 13px;
}
.alert-success   { background: var(--cofo-green-soft);  border-color: #C6EBD5; color: #15803D; }
.alert-danger    { background: var(--cofo-red-soft);    border-color: #F6C9C9; color: #B91C1C; }
.alert-secondary { background: var(--cofo-soft);        border-color: var(--cofo-border); color: var(--cofo-text-2); }

/* ── 현장 이름 ────────────────────────────────────────────────────────── */

.cofo-site-name { font-size: 15px; font-weight: 700; letter-spacing: -0.3px; }
.cofo-site-name a { color: var(--cofo-text); }
.cofo-site-name a:hover { color: var(--cofo-blue); text-decoration: none; }
.cofo-site-sub { font-size: 11.5px; color: var(--cofo-text-3); }

/* ── 막대 차트 ────────────────────────────────────────────────────────────
   **발전량은 막대다.** 기간마다 끊어진 합계라 점 사이에 값이 없다 —
   선으로 이으면 없는 값을 있는 것처럼 보여준다.                              */

/* 눈금선을 칸마다 그리면 칸 사이 간격 때문에 가로줄이 끊겨 점선처럼 조각난다.
   바깥 상자에 한 번만 그리고, 값 라벨(14px)·X 라벨(16px)을 뺀 안쪽에만 건다. */
.cofo-bars {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    height: 190px;
    position: relative;
}
.cofo-bars::before {
    content: "";
    position: absolute;
    left: 0; right: 0; top: 14px; bottom: 16px;
    background: repeating-linear-gradient(
        to top, var(--cofo-border) 0 1px, transparent 1px 25%);
    opacity: .8;
    z-index: 0;
}
.cofo-bar-col { position: relative; z-index: 1; }
.cofo-bar-col {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100%;
    min-width: 0;
}
.cofo-bar-val { font-size: 10px; color: var(--cofo-text-3); height: 14px; line-height: 14px; }
.cofo-bar-track {
    flex: 1 1 auto;
    width: 100%;
    display: flex;
    align-items: flex-end;
    position: relative;
}
/* 그라데이션을 빼고 한 가지 색으로 간다. 위아래로 색이 변하면
   높이 말고 색까지 읽게 되어 값이 흐려진다. */
.cofo-bar-fill {
    width: 100%;
    background: #8FB3F0;
    border-radius: 3px 3px 0 0;
    min-height: 2px;
    transition: background .12s;
}
.cofo-bar-col:hover .cofo-bar-fill { background: var(--cofo-blue); }
.cofo-bar-x { font-size: 10.5px; color: var(--cofo-text-3); height: 16px; line-height: 16px; }

/* 어제 같은 시각의 수준. 막대 위에 얹는 눈금선이다.
   색을 경고색으로 쓰지 말 것 — 어제보다 낮은 것은 날씨일 뿐 고장이 아니다. */
.cofo-bar-prev {
    position: absolute;
    left: 0; right: 0;
    height: 0;
    border-top: 2px dashed #9AA4B8;
    opacity: .85;
    pointer-events: none;
}
.cofo-legend-prev {
    display: inline-block;
    width: 18px;
    border-top: 2px dashed #9AA4B8;
    vertical-align: middle;
    margin-right: 5px;
}

/* 표 안에 눕는 막대. 비중을 눈으로 견주게 한다. */
.cofo-inline-bar {
    height: 6px;
    background: var(--cofo-soft);
    border-radius: 999px;
    overflow: hidden;
}
.cofo-inline-bar > div {
    height: 100%;
    background: #8FB3F0;
    border-radius: 999px;
}

/* ── 접속반 채널 ──────────────────────────────────────────────────────── */

.cofo-ch-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.cofo-ch {
    min-width: 64px;
    padding: 8px;
    border: 1px solid var(--cofo-border);
    border-radius: var(--cofo-radius-sm);
    background: var(--cofo-soft);
    text-align: center;
}
.cofo-ch .n { display: block; font-size: 10.5px; color: var(--cofo-text-3); }
.cofo-ch .v { display: block; font-size: 13.5px; font-weight: 600; }
/* 형제 채널 중앙값보다 크게 처지는 채널. 절대값이 아니라 상대 판정이다. */
.cofo-ch.low { border-color: #F3BFBF; background: var(--cofo-red-soft); }
.cofo-ch.low .v { color: var(--cofo-red); }
.cofo-ch.na .v { color: var(--cofo-text-3); font-weight: 400; }

/* ── 이벤트 목록 ──────────────────────────────────────────────────────── */

.cofo-events { max-height: 262px; overflow-y: auto; }
.cofo-event {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 18px;
    border-top: 1px solid var(--cofo-hair);
    font-size: 12.5px;
}
.cofo-event > .pill { flex: none; align-self: flex-start; margin-top: 2px; }
.cofo-event:first-child { border-top: none; }
.cofo-event .t { color: var(--cofo-text-3); white-space: nowrap; font-size: 11.5px; }
.cofo-event .m { min-width: 0; }

/* ── 현장 선택 ────────────────────────────────────────────────────────── */

.cofo-switch { position: relative; }
.cofo-switch-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 11px;
    border: 1px solid var(--cofo-border-2);
    border-radius: 999px;
    background: var(--cofo-card-bg);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--cofo-text-2);
    cursor: pointer;
}
.cofo-switch-btn:hover { background: var(--cofo-soft); color: var(--cofo-text); }
.cofo-switch-menu {
    min-width: 230px;
    border: 1px solid var(--cofo-border);
    border-radius: var(--cofo-radius-sm);
    box-shadow: var(--cofo-shadow-2);
    padding: 6px;
}
.cofo-switch-menu .dropdown-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    border-radius: 7px;
    padding: 8px 10px;
    font-size: 13px;
}
.cofo-switch-menu .dropdown-item:hover { background: var(--cofo-nav-hover-bg); }
.cofo-switch-menu .dropdown-item.on { background: var(--cofo-nav-active-bg); color: var(--cofo-sidebar-active); font-weight: 600; }
.cofo-switch-menu .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 끊긴 현장은 목록에서부터 표시한다. 들어가야 아는 것보다 낫다. */
.cofo-switch-menu .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.cofo-switch-menu .dot.ok { background: var(--cofo-green); }
.cofo-switch-menu .dot.down { background: var(--cofo-red); }

/* ── 설비 상세 ────────────────────────────────────────────────────────────
   목록의 한 줄로는 담기지 않는 것을 여기서 본다 — PV 입력, 3상 각 상,
   역률, 주파수, 고장 비트. 목록에 다 넣으면 가로로 스무 칸이 되어
   아무것도 안 읽힌다.                                                       */

.cofo-crumb {
    font-size: 12px;
    color: var(--cofo-text-3);
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
.cofo-crumb a { color: var(--cofo-text-2); }
.cofo-crumb strong { color: var(--cofo-text); font-weight: 600; }

.cofo-dev-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}
.cofo-dev-head h2 {
    font-size: 21px;
    font-weight: 700;
    letter-spacing: -0.5px;
    margin: 0 0 5px;
    display: flex;
    align-items: center;
    gap: 9px;
}
.cofo-dev-meta { font-size: 12px; color: var(--cofo-text-3); }
.cofo-dev-act { display: flex; gap: 6px; }

/* 묶음 제목. 카드 안에서 계측을 갈래로 나눈다. */
.cofo-sub {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--cofo-text-2);
    margin: 0 0 9px;
    padding-bottom: 7px;
    border-bottom: 1px solid var(--cofo-border);
    letter-spacing: .02em;
}

/* 계측값 한 칸. 값과 단위를 같이 둔다 — 단위 없는 숫자는 읽는 사람이 짐작한다. */
.cofo-meas {
    background: var(--cofo-soft);
    border: 1px solid transparent;
    border-radius: var(--cofo-radius-sm);
    padding: 10px 12px;
    height: 100%;
}
.cofo-meas .lbl { font-size: 11px; color: var(--cofo-text-3); margin-bottom: 3px; }
.cofo-meas .val { font-size: 18px; font-weight: 700; letter-spacing: -0.4px; }
.cofo-meas .val span {
    font-size: 11px;
    font-weight: 500;
    color: var(--cofo-text-3);
    margin-left: 3px;
    letter-spacing: 0;
}

.cofo-fault { margin: 0 0 10px; padding-left: 18px; }
.cofo-fault li { font-size: 12.5px; color: var(--cofo-red); margin-bottom: 3px; }

/* ── 로그인 ───────────────────────────────────────────────────────────── */

.cofo-login-split { min-height: 100vh; display: flex; }

.cofo-login-brand {
    width: 480px;
    flex: 0 0 480px;
    background: linear-gradient(160deg, #1B2445 0%, var(--cofo-brand-bg) 100%);
    color: #FFFFFF;
    padding: 44px 44px 36px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    overflow: hidden;
}
.cofo-login-brand .curve { position: absolute; left: 0; bottom: 0; opacity: .16; }
/* Bootstrap 에 .mark(형광펜) 유틸리티가 있어서 노란 배경이 붙는다.
   이름이 겹치는 자리라 배경·여백을 명시적으로 되돌린다. */
.cofo-login-brand .mark {
    display: flex;
    align-items: center;
    gap: 10px;
    position: relative;
    background: transparent;
    padding: 0;
    color: inherit;
}
.cofo-login-brand .mark-icon {
    width: 34px; height: 34px; border-radius: 9px;
    background: rgba(96, 165, 250, .22);
    display: flex; align-items: center; justify-content: center; flex: none;
}
.cofo-login-brand .mark-name { font-size: 15px; font-weight: 600; }
.cofo-login-brand h2 {
    font-size: 28px; font-weight: 800; letter-spacing: -0.8px;
    line-height: 1.3; margin: 34px 0 0;
}
.cofo-login-brand .lead { font-size: 13px; color: var(--cofo-brand-text); margin-top: 12px; line-height: 1.7; }
.cofo-login-stats { position: relative; display: flex; gap: 32px; flex-wrap: wrap; }
.cofo-login-stats .k { font-size: 11px; color: var(--cofo-brand-text); }
.cofo-login-stats .v { font-size: 22px; font-weight: 800; margin-top: 2px; letter-spacing: -0.5px; }
.cofo-login-stats .v span { font-size: 12px; font-weight: 500; color: var(--cofo-brand-text); margin-left: 3px; letter-spacing: 0; }

.cofo-login-form {
    flex: 1 1 auto;
    background: var(--cofo-card-bg);
    display: flex; align-items: center; justify-content: center;
    padding: 44px;
    min-width: 0;
}
.cofo-login-form .inner { width: 100%; max-width: 340px; }
.cofo-login-form h1 { font-size: 21px; font-weight: 700; margin: 0; letter-spacing: -0.4px; }
.cofo-login-form .sub { font-size: 13px; color: var(--cofo-text-2); margin: 5px 0 28px; }
.cofo-login-form .btn { width: 100%; min-height: 42px; font-size: 14px; }
.cofo-login-foot { font-size: 11px; color: var(--cofo-text-3); margin-top: 18px; text-align: center; }

/* ── 좁은 화면 ────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
    .cofo-login-split { display: block; }
    .cofo-login-brand {
        width: auto; flex: none;
        padding: 26px 24px 22px;
    }
    .cofo-login-brand h2 { font-size: 22px; margin-top: 20px; }
    .cofo-login-form { padding: 32px 24px 48px; }
}

@media (max-width: 1100px) {
    .cofo-side {
        position: static;
        height: auto;
        width: 100%;
        flex: none;
        flex-direction: row;
        align-items: center;
        overflow-x: auto;
        border-right: none;
        border-bottom: 1px solid var(--cofo-border);
    }
    .cofo-shell { display: block; }
    .cofo-nav { display: flex; gap: 4px; padding: 8px; overflow-x: auto; }
    .cofo-nav a { white-space: nowrap; }
    /* 가로로 눕는 메뉴에서는 왼쪽 액센트가 방향이 맞지 않는다. 아래로 돌린다. */
    .cofo-nav a.active, .cofo-nav a.on { box-shadow: inset 0 -2px 0 var(--cofo-blue); }
    .cofo-nav-label { display: none; }
    /* 브랜드가 두 줄로 접히면서 머리가 들쭉날쭉해진다. 부제를 접는다. */
    .cofo-brand { white-space: nowrap; padding: 12px 14px; }
    .cofo-brand small { display: none; }
    .cofo-side-foot { border-top: none; border-left: 1px solid var(--cofo-border); }
    .cofo-body { padding: 16px 14px 32px; }
    .cofo-top { padding: 12px 14px; }
}

/* ── 인쇄 ──────────────────────────────────────────────────────────────────
   보고서를 종이나 PDF 로 남기는 현장이 있다. 화면 그대로 인쇄하면
   왼쪽 메뉴가 지면의 1/5 를 먹고 배경색이 토너를 다 쓴다.

   기준 시각은 지우지 말 것 — 종이에는 언제 뽑은 것인지가 남지 않아서,
   책상에 쌓이면 어제 것과 오늘 것을 구분할 방법이 없다.

   카드와 표는 중간에서 자르지 않는다. 표 머리는 장마다 다시 찍는다.       */

@media print {
    .cofo-side,
    .cofo-switch-btn,
    .cofo-switch-menu,
    .no-print,
    .btn {
        display: none !important;
    }

    .cofo-shell, .cofo-main { display: block; }
    .cofo-body, .cofo-top { padding: 0; border: 0; position: static; }

    body, .cofo-shell, .cofo-main, .card, .cofo-kpi, .cofo-meas {
        background: #fff !important;
        color: #000 !important;
    }

    .card, .cofo-kpi, .cofo-sec, .cofo-ch, .cofo-meas {
        break-inside: avoid;
        page-break-inside: avoid;
        box-shadow: none !important;
    }

    /* 표가 두 장에 걸치면 두 번째 장부터 머리가 없어 어느 열인지 모른다. */
    thead { display: table-header-group; }
    tr { break-inside: avoid; page-break-inside: avoid; }

    /* 배경색으로만 구분하던 것은 인쇄에서 사라진다. 테두리를 남긴다. */
    .table th, .table td { border-color: #999 !important; }
    .pill { border-color: #999 !important; }
    .table-responsive { overflow: visible !important; }

    a { text-decoration: none; color: #000 !important; }

    @page { margin: 12mm; }
}

/* ── 상태 밴드 ────────────────────────────────────────────────────────────
   숫자를 늘어놓기 전에 "지금 괜찮은가" 부터 답한다.
   확인 불가를 정상 쪽에 넣지 말 것 — 응답이 없다는 것은 이상이 없다는 뜻이
   아니라 알 수 없다는 뜻이다.                                                */

.cofo-band {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    background: var(--cofo-card-bg);
    border: 1px solid var(--cofo-hair);
    border-radius: 14px;
    box-shadow: none;
    padding: 13px 18px;
    margin-bottom: 12px;
}
.cofo-band-main { display: flex; align-items: baseline; gap: 5px; }
.cofo-band-main .n { font-size: 22px; font-weight: 700; letter-spacing: -0.6px; }
.cofo-band-main .u { font-size: 12px; color: var(--cofo-text-3); }
.cofo-band-chips { display: flex; gap: 7px; flex-wrap: wrap; }
.cofo-band-chips .chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 500;
    background: var(--cofo-soft);
    color: var(--cofo-text-2);
}
.cofo-band-chips .chip b { font-weight: 700; }
.cofo-band-chips .chip i { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.chip-ok   { background: var(--cofo-green-soft)  !important; color: var(--cofo-green)  !important; }
.chip-warn { background: var(--cofo-yellow-soft) !important; color: var(--cofo-yellow) !important; }
.chip-down { background: var(--cofo-red-soft)    !important; color: var(--cofo-red)    !important; }
.chip-ok i { background: var(--cofo-green); }
.chip-warn i { background: var(--cofo-yellow); }
.chip-down i { background: var(--cofo-red); }
.cofo-band-right { margin-left: auto; font-size: 12px; }

/* ── KPI 스트립 ───────────────────────────────────────────────────────────
   카드 여덟 장으로 흩어놓으면 화면의 절반을 먹는다. 한 줄에 모으고
   세로선으로만 나눈다. 좁은 화면에서는 두 칸씩 접힌다.                      */

/* Bootstrap 의 .card 가 flex-direction: column 을 준다. 가로로 세우려면
   방향을 명시해야 한다 — 빼면 KPI 여덟 개가 세로로 쌓인다. */
.cofo-strip { display: flex; flex-direction: row; flex-wrap: wrap; padding: 0; }
.cofo-strip .s {
    flex: 1 1 0;
    min-width: 148px;
    padding: 15px 17px;
    border-left: 1px solid var(--cofo-hair);
}
.cofo-strip .s:first-child { border-left: none; }
.cofo-strip .k {
    font-size: 11px;
    font-weight: 600;
    color: var(--cofo-text-3);
    letter-spacing: .01em;
    margin-bottom: 6px;
}
.cofo-strip .v {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -1px;
    line-height: 1.12;
    font-variant-numeric: tabular-nums;
}
.cofo-strip .v span {
    font-size: 11.5px;
    font-weight: 500;
    color: var(--cofo-text-3);
    margin-left: 3px;
    letter-spacing: 0;
}
/* 빈 상태를 큰 글자로 강조하지 말 것. 값이 있는 칸보다 더 눈에 띈다. */
.cofo-strip .v.empty { color: var(--cofo-text-3); font-weight: 500; }
.cofo-strip .f {
    font-size: 11px;
    color: var(--cofo-text-3);
    margin-top: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media (max-width: 1400px) {
    .cofo-strip .s { flex: 1 1 25%; min-width: 0; }
    .cofo-strip .s:nth-child(4n+1) { border-left: none; }
    .cofo-strip .s:nth-child(n+5) { border-top: 1px solid var(--cofo-border); }
}
@media (max-width: 760px) {
    .cofo-strip .s { flex: 1 1 50%; }
    .cofo-strip .s:nth-child(4n+1) { border-left: 1px solid var(--cofo-border); }
    .cofo-strip .s:nth-child(2n+1) { border-left: none; }
    .cofo-strip .s:nth-child(n+3) { border-top: 1px solid var(--cofo-border); }
}

/* ── 아이콘 ───────────────────────────────────────────────────────────────
   색은 currentColor 다. 부모 글자색을 따라가므로 선택된 메뉴·경고색에
   저절로 맞는다. 아이콘마다 색을 지정하지 말 것 — 테마가 바뀌면 어긋난다. */

.ico { width: 16px; height: 16px; flex: none; }

/* 메뉴 — 글자만 있으면 일곱 줄이 다 똑같아 보인다. */
.cofo-nav a { display: flex; align-items: center; gap: 9px; }
.cofo-nav a .ico { width: 17px; height: 17px; opacity: .72; }
.cofo-nav a.active .ico, .cofo-nav a.on .ico { opacity: 1; }

.cofo-brand { display: flex; align-items: center; gap: 10px; }
.cofo-brand .mk {
    width: 32px; height: 32px;
    border-radius: 9px;
    background: linear-gradient(140deg, #3B82F6 0%, #1D4ED8 100%);
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    flex: none;
}
.cofo-brand .mk .ico { width: 18px; height: 18px; }

/* 버튼 안의 아이콘 */
.btn .ico { width: 14px; height: 14px; margin-right: 5px; vertical-align: -2px; }

/* 기준 시각 — 앞에 점을 둔다. "지금 값" 이 아니라 "이 시각 기준" 이라는 표시다. */
.cofo-asof {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 11.5px; color: var(--cofo-text-3); white-space: nowrap;
}
.cofo-asof i {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--cofo-green);
    box-shadow: 0 0 0 3px var(--cofo-green-soft);
}

/* KPI 칸의 아이콘 칩. 여덟 칸이 라벨만 다르면 눈이 어디를 봐야 할지 모른다. */
.ico-chip {
    width: 20px; height: 20px;
    border-radius: 6px;
    display: inline-flex; align-items: center; justify-content: center;
    margin-right: 6px;
    vertical-align: -5px;
}
.ico-chip .ico { width: 12.5px; height: 12.5px; }
.i-blue   { background: var(--cofo-blue-soft);   color: var(--cofo-blue); }
.i-green  { background: var(--cofo-green-soft);  color: var(--cofo-green); }
.i-yellow { background: var(--cofo-yellow-soft); color: var(--cofo-yellow); }
.i-red    { background: var(--cofo-red-soft);    color: var(--cofo-red); }
.i-violet { background: var(--cofo-violet-soft); color: var(--cofo-violet); }

.cofo-strip .k { display: flex; align-items: center; }

/* 사이드바 하단 — 이름만 있으면 빈 칸처럼 보인다. */
.cofo-side-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cofo-side-foot .av {
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--cofo-blue-soft);
    color: var(--cofo-blue);
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; font-weight: 700;
    flex: none;
}
.cofo-side-foot .me { min-width: 0; flex: 1 1 auto; }
.cofo-side-foot form { width: 100%; }
.cofo-side-foot .btn { width: 100%; }

/* 로그인 오른쪽 — 흰 바탕에 입력칸만 떠 있으면 화면이 비어 보인다.
   바탕을 눌러 두고 폼만 흰 카드로 세운다. */
.cofo-login-form { background: var(--cofo-page-bg); }
.cofo-login-form .inner {
    background: var(--cofo-card-bg);
    border: 1px solid var(--cofo-border);
    border-radius: 14px;
    box-shadow: 0 6px 24px rgba(23, 28, 44, .07);
    padding: 34px 32px 30px;
    max-width: 380px;
}

/* ── 검색 · 정렬 ──────────────────────────────────────────────────────────
   현장이 쉰 곳을 넘으면 눈으로 훑는 것이 불가능하다.                        */

.cofo-sec-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
.cofo-sec-row .cofo-sec { margin-bottom: 11px; }

.cofo-find {
    display: flex;
    align-items: center;
    gap: 7px;
    background: var(--cofo-soft);
    border: 1px solid transparent;
    border-radius: 9px;
    padding: 6px 11px;
    margin-bottom: 11px;
    min-width: 230px;
}
.cofo-find:focus-within {
    background: var(--cofo-card-bg);
    border-color: #9BB8F5;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .12);
}
.cofo-find .ico { width: 14px; height: 14px; color: var(--cofo-text-3); }
.cofo-find input {
    border: none;
    background: transparent;
    outline: none;
    font-size: 12.5px;
    width: 100%;
    color: var(--cofo-text);
}
.cofo-find input::placeholder { color: var(--cofo-text-3); }
.cofo-find .clr { color: var(--cofo-text-3); font-size: 12px; line-height: 1; }
.cofo-find .clr:hover { color: var(--cofo-text); text-decoration: none; }

/* 정렬 가능한 표 머리. 누를 수 있다는 것이 보여야 한다. */
.cofo-th {
    color: inherit;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
}
.cofo-th:hover { color: var(--cofo-text); text-decoration: none; }
.cofo-th.on { color: var(--cofo-blue); }
.cofo-th .sm { font-size: 8px; }

/* 페이지 이동. 몇 쪽 중 몇 쪽인지 늘 보여준다 —
   "다음" 만 있으면 끝이 어딘지 모른 채 계속 누르게 된다. */
.cofo-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin-top: 14px;
    font-size: 12.5px;
}
.cofo-pager a { color: var(--cofo-text-2); }
.cofo-pager a:hover { color: var(--cofo-blue); text-decoration: none; }
.cofo-pager .now { color: var(--cofo-text-3); font-variant-numeric: tabular-nums; }

/* 화면 위에 얹는 한 줄 안내. 알림이 아니라 설명이라 색을 쓰지 않는다. */
.cofo-note {
    font-size: 12.5px;
    color: var(--cofo-text-2);
    background: var(--cofo-soft);
    border-radius: 9px;
    padding: 11px 14px;
}

/* ── 조건 바 ──────────────────────────────────────────────────────────────
   조회 조건은 결과가 아니라 손잡이다. 카드로 감싸면 화면의 한 칸을
   통째로 차지하면서 세로만 길어진다.                                        */

.cofo-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    background: var(--cofo-card-bg);
    border: 1px solid var(--cofo-hair);
    border-radius: 12px;
    padding: 10px 12px;
}
.cofo-bar .sp { flex: 1 1 auto; }
.cofo-bar .form-select, .cofo-bar .form-control { width: auto; min-width: 120px; }
.cofo-bar .form-select.site { max-width: 260px; }
.cofo-bar .form-select { padding-right: 30px; text-overflow: ellipsis; }

/* 표가 자기 안에서만 스크롤한다. 차트와 나란히 둘 때 높이를 맞춘다. */
.cofo-scroll { max-height: 348px; overflow-y: auto; }
.cofo-scroll thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--cofo-card-bg);
}

/* 값이 0 인 칸. 야간처럼 원래 없는 구간이 절반을 차지하는데
   다 진하게 찍으면 있는 값이 묻힌다. */
.table tbody tr.z td { color: var(--cofo-text-3); }

/* ── 선 차트 ──────────────────────────────────────────────────────────────
   전압 · 전류 · 온도 · 일사량처럼 계속 변하는 값에만 쓴다.
   발전량은 기간마다 끊어진 합계라 막대로 그린다.                            */

.cofo-line { width: 100%; }
.cofo-line .lg {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-bottom: 10px;
    font-size: 11.5px;
    color: var(--cofo-text-3);
}
/* **범례에 지금 값을 같이 적는다.** 눈금에서 읽어내게 하지 않는다. */
.cofo-line .lg .it { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.cofo-line .lg i { width: 12px; height: 2px; border-radius: 2px; display: inline-block; }
.cofo-line .lg b { color: var(--cofo-text); font-weight: 700; font-variant-numeric: tabular-nums; }
.cofo-line .lg em { font-style: normal; }
.cofo-line svg { width: 100%; height: 210px; display: block; }
.cofo-line svg .gr { stroke: var(--cofo-hair); stroke-width: 1; vector-effect: non-scaling-stroke; }
.cofo-line .xs {
    display: flex;
    justify-content: space-between;
    font-size: 10.5px;
    color: var(--cofo-text-3);
    margin-top: 2px;
}

/* 지금 보고 있는 설비 줄. 형제 목록에서 자기 자리를 잃지 않게 한다. */
.table tbody tr.on td { background: var(--cofo-nav-active-bg); }
.cofo-line .lg .nt { margin-left: auto; font-size: 10.5px; }

/* 보기 전환. 눌러서 바꾸는 것이라는 게 보여야 한다. */
.cofo-toggle { display: inline-flex; align-items: center; gap: 2px; font-size: 11.5px; }
.cofo-toggle a {
    padding: 3px 9px;
    border-radius: 7px;
    color: var(--cofo-text-3);
}
.cofo-toggle a:hover { background: var(--cofo-soft); color: var(--cofo-text); text-decoration: none; }
.cofo-toggle a.on { background: var(--cofo-nav-active-bg); color: var(--cofo-blue); font-weight: 600; }
.cofo-toggle .gap { width: 10px; }
.cofo-side-foot .pw { font-size: 11.5px; color: var(--cofo-text-3); width: 100%; }
.cofo-side-foot .pw:hover { color: var(--cofo-blue); text-decoration: none; }

/* ── TV 현황판 ────────────────────────────────────────────────────────────
   사무실 벽에 걸어두고 서너 걸음 밖에서 보는 화면이다.
   글자를 키우고, 색을 어둡게 깔아 눈부심을 줄인다.                          */

.tv {
    /* **화면 높이 안에서 끝낸다.** min-height 로 두면 내용이 늘어나 아래로
       흘러가는데, 벽에 걸린 화면은 아무도 스크롤하지 않는다. 넘치는 것은
       안쪽 패널이 각자 스크롤한다. */
    height: 100vh;
    overflow: hidden;
    background: #0E1320;
    color: #E8ECF5;
    padding: 22px 26px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.tv-top { display: flex; align-items: center; gap: 18px; }
.tv-brand { display: flex; align-items: center; gap: 10px; font-size: 18px; }
.tv-brand b { font-weight: 700; }
.tv-brand .mk {
    width: 34px; height: 34px; border-radius: 9px;
    background: linear-gradient(140deg, #3B82F6 0%, #1D4ED8 100%);
    display: flex; align-items: center; justify-content: center;
}
.tv-now { margin-left: auto; font-size: 14px; color: #8C97AE; display: flex; align-items: center; gap: 8px; }
.tv-now .dot { width: 8px; height: 8px; border-radius: 50%; background: #22C55E; box-shadow: 0 0 0 4px rgba(34,197,94,.18); }
.tv-now .hint { color: #5E6980; font-size: 12.5px; }
/* 무인 화면에서 빠져나갈 길. 없으면 재부팅밖에 답이 없다. */
.tv-back { color: #8C97AE; font-size: 13px; }
.tv-back:hover { color: #E8ECF5; text-decoration: none; }

.tv-kpi { display: flex; gap: 14px; flex-wrap: wrap; }
.tv-kpi .c {
    flex: 1 1 220px;
    background: #161C2C;
    border: 1px solid #222A3D;
    border-radius: 14px;
    padding: 18px 20px;
}
.tv-kpi .k { font-size: 14px; color: #8C97AE; margin-bottom: 6px; }
.tv-kpi .v {
    font-size: 46px; font-weight: 800; letter-spacing: -1.6px; line-height: 1.05;
    font-variant-numeric: tabular-nums;
}
.tv-kpi .v span { font-size: 17px; font-weight: 500; color: #8C97AE; margin-left: 6px; letter-spacing: 0; }
.tv-kpi .f { font-size: 13px; color: #6E7A93; margin-top: 6px; }
.tv-kpi .c.good .v { color: #4ADE80; }
.tv-kpi .c.warn .v { color: #FBBF24; }
.tv-kpi .c.bad  .v { color: #F87171; }

.tv-body { display: flex; gap: 14px; flex: 1 1 auto; min-height: 300px; }
.tv-chart, .tv-side {
    background: #161C2C;
    border: 1px solid #222A3D;
    border-radius: 14px;
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
}
.tv-chart { flex: 1 1 66%; }
.tv-side { flex: 1 1 34%; min-width: 300px; }
.tv-chart .t, .tv-side .t { font-size: 14px; color: #8C97AE; margin-bottom: 12px; }
.tv-chart .t span { font-size: 12px; }
.tv-chart .cofo-bars { flex: 1 1 auto; }
.tv .cofo-bar-fill { background: #3B82F6; }
/* 눈금선. 밝은 화면용 색(--cofo-border)을 그대로 쓰면 어두운 바탕에서
   흰 줄이 되어 막대보다 눈에 띈다. */
.tv .cofo-bars::before {
    background: repeating-linear-gradient(
        to top, rgba(148,163,184,.16) 0 1px, transparent 1px 25%);
    opacity: 1;
}
.tv .cofo-bar-x { color: #5E6980; font-size: 12px; }

.tv-empty, .tv-ok { flex: 1 1 auto; display: flex; flex-direction: column;
    align-items: center; justify-content: center; color: #6E7A93; font-size: 15px; gap: 6px; }
.tv-ok .big { font-size: 30px; font-weight: 700; color: #4ADE80; }

.tv-list { display: flex; flex-direction: column; gap: 10px; overflow-y: auto; }
.tv-list .it {
    background: #1C2437;
    border-radius: 10px;
    padding: 12px 14px;
}
.tv-list .nm { font-size: 17px; font-weight: 600; }
.tv-list .st { font-size: 14px; margin-top: 3px; }
.tv-list .st.bad { color: #F87171; }
.tv-list .st.warn { color: #FBBF24; }

@media (max-width: 1100px) {
    .tv-body { flex-direction: column; }
    .tv-kpi .v { font-size: 34px; }
}

/* ── 알람 배너 ────────────────────────────────────────────────────────────
   현장 WPF 는 팝업을 띄운다. 웹에서 창을 막아세우면 브라우저가 막거나
   사람이 곧 닫는 것에만 익숙해진다. 모든 화면 위에 띠로 남긴다.
   **접수한 것은 세지 않는다** — 누가 봤으면 처리 중이라는 뜻이다.        */

.cofo-alarm {
    display: flex;
    align-items: center;
    gap: 9px;
    background: var(--cofo-red-soft);
    color: var(--cofo-red);
    border-left: 3px solid var(--cofo-red);
    border-radius: 0 9px 9px 0;
    padding: 11px 14px;
    margin-bottom: 14px;
    font-size: 13px;
}
.cofo-alarm .ic { display: inline-flex; }
.cofo-alarm b { font-weight: 700; }
.cofo-alarm a { margin-left: auto; color: var(--cofo-red); font-weight: 600; white-space: nowrap; }

/* ── 게시판 ────────────────────────────────────────────────
   본문은 적은 그대로 보여야 한다. 줄바꿈을 살리고(pre-wrap),
   긴 주소가 칸을 밀어내지 않게 감싸안는다.                                */

.cofo-post-head {
    border-bottom: 1px solid var(--cofo-line);
    padding-bottom: 12px;
    margin-bottom: 16px;
}
.cofo-post-head h2 {
    font-size: 17px;
    font-weight: 700;
    margin: 0 0 6px;
    line-height: 1.45;
}
.cofo-post-head .meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 12px;
    color: var(--cofo-text-3);
}
.cofo-post-body {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: 13.5px;
    line-height: 1.8;
    color: var(--cofo-text);
    min-height: 120px;
}

/* ── TV 총괄 대시보드 ─────────────────────────────────────────────────────
   위 .tv 뼈대에 얹는 것들. 벽에 걸린 화면 하나로 "지금 전부 어떤가" 가
   읽혀야 하므로, 현장 타일 · 알람 · 점검 · 공지를 한 화면에 같이 둔다.
   세로로 흐르게 두면 아래쪽은 아무도 못 읽는다 — 화면 높이 안에서 끝낸다. */

.tv-main {
    display: flex;
    gap: 14px;
    flex: 1 1 auto;
    min-height: 0;                   /* 안쪽 스크롤이 살려면 이게 있어야 한다 */
}
.tv-col-l { flex: 1 1 66%; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.tv-col-r { flex: 0 0 380px; display: flex; flex-direction: column; gap: 14px; min-height: 0; }

.tv-panel {
    background: #161C2C;
    border: 1px solid #222A3D;
    border-radius: 14px;
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.tv-panel > .t {
    font-size: 14px; color: #8C97AE; margin-bottom: 12px;
    display: flex; align-items: center; gap: 8px;
}
.tv-panel > .t .n { margin-left: auto; font-size: 13px; color: #5E6980; }
/* 안쪽 스크롤. 화면 높이가 모자라면(1366×768 같은 자리) 여기서 넘긴다.
   **잘린 줄을 그냥 두지 말 것** — 반쯤 잘린 타일은 고장으로 읽힌다.
   아래를 흐리게 덮어서 "더 있다" 를 보이게 한다. */
.tv-panel .sc {
    overflow-y: auto;
    min-height: 0;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
}

/* 현장 타일 — 길이는 부하율(절대 성능), 색은 상태.
   현장 WPF 의 인버터 목록과 같은 규칙이다. 둘이 서로 다른 것을 말한다. */
.tv-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.tv-tile { background: #1C2437; border-radius: 11px; padding: 12px 13px; border-left: 3px solid #2A3450; }
.tv-tile.ok   { border-left-color: #22C55E; }
.tv-tile.warn { border-left-color: #FBBF24; }
.tv-tile.bad  { border-left-color: #F87171; }
.tv-tile .nm { font-size: 15px; font-weight: 600; margin-bottom: 6px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tv-tile .kw { font-size: 25px; font-weight: 800; letter-spacing: -0.8px;
    font-variant-numeric: tabular-nums; line-height: 1.1; }
.tv-tile .kw span { font-size: 13px; font-weight: 500; color: #8C97AE; margin-left: 4px; letter-spacing: 0; }
.tv-tile .kw.off { color: #6E7A93; font-size: 19px; }
.tv-tile .tr { height: 5px; border-radius: 3px; background: #2A3450; margin: 8px 0 6px; overflow: hidden; }
.tv-tile .fi { height: 100%; background: #3B82F6; border-radius: 3px; }
.tv-tile .sub { font-size: 12.5px; color: #6E7A93; display: flex; gap: 6px; }
.tv-tile .sub .bad { color: #F87171; }

/* 목록 줄 — 알람 · 점검 · 공지가 같은 모양을 쓴다.
   셋을 다르게 그리면 벽에서 볼 때 어디를 봐야 할지 모른다. */
.tv-rows { display: flex; flex-direction: column; gap: 8px; overflow-y: auto; min-height: 0; }
.tv-rows .r { background: #1C2437; border-radius: 9px; padding: 9px 11px; font-size: 13.5px; }
.tv-rows .r .h { display: flex; align-items: baseline; gap: 7px; }
.tv-rows .r .h b { font-weight: 600; font-size: 14.5px; }
.tv-rows .r .h .w { margin-left: auto; font-size: 12px; color: #6E7A93; white-space: nowrap; }
.tv-rows .r .d { color: #8C97AE; font-size: 12.5px; margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tv-rows .r.bad  { box-shadow: inset 3px 0 0 #F87171; }
.tv-rows .r.warn { box-shadow: inset 3px 0 0 #FBBF24; }
.tv-rows .r.info { box-shadow: inset 3px 0 0 #60A5FA; }
.tv-rows .none { color: #5E6980; font-size: 13.5px; padding: 6px 2px; }

/* 어제 곡선. 오늘 막대 뒤에 옅게 깔아 견줄 상대로 쓴다. */
.tv .cofo-bar-track { position: relative; }
.tv .cofo-bar-prev {
    position: absolute; left: 0; right: 0; bottom: 0;
    background: rgba(148,163,184,.20);
    border-top: 1px solid rgba(148,163,184,.45);
    border-radius: 3px 3px 0 0;
}
.tv-legend { display: flex; gap: 14px; font-size: 12.5px; color: #6E7A93; margin-top: 8px; }
.tv-legend i { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; }
.tv-legend .a i { background: #3B82F6; }
.tv-legend .b i { background: rgba(148,163,184,.45); }

@media (max-width: 1400px) {
    .tv-col-r { flex-basis: 320px; }
    .tv-tiles { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}
@media (max-width: 1100px) {
    .tv-main { flex-direction: column; }
    .tv-col-r { flex: 1 1 auto; }
}

/* ── 표 안에서 펼치는 편집 칸 ─────────────────────────────────────────────
   현장이 쉰 곳이면 폼 쉰 개가 한 화면에 펼쳐져서 목록으로 못 쓰게 된다.
   그래서 접어두고 <details> 로 필요한 줄만 연다. 스크립트를 쓰지 않는다. */

.cofo-inline > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 12.5px;
}
.cofo-inline > summary::-webkit-details-marker { display: none; }
.cofo-inline > summary .ed {
    margin-left: auto;
    color: var(--cofo-text-3);
    font-size: 11.5px;
    border: 1px solid var(--cofo-border-2);
    border-radius: 6px;
    padding: 1px 7px;
    white-space: nowrap;
}
.cofo-inline > summary:hover .ed { color: var(--cofo-blue); border-color: var(--cofo-blue); }
.cofo-inline[open] > summary .ed { color: var(--cofo-blue); border-color: var(--cofo-blue); }
.cofo-inline-form {
    margin-top: 10px;
    padding: 12px;
    background: var(--cofo-soft);
    border: 1px solid var(--cofo-hair);
    border-radius: 9px;
}
.cofo-inline-form .form-label { font-size: 11px; margin-bottom: 2px; }

/* ── 오류 · 없는 화면 ─────────────────────────────────────────────────────
   무엇이 잘못됐는지 적지 않는다. 스택 추적은 바깥으로 나가면 안 되고,
   사람이 읽어도 할 수 있는 일이 없다. 대신 시각을 크게 적어서
   서버 기록에서 찾을 수 있게 한다.                                        */

.cofo-oops {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: var(--cofo-page-bg);
}
.cofo-oops .box {
    background: var(--cofo-card-bg);
    border: 1px solid var(--cofo-border);
    border-radius: 16px;
    padding: 34px 36px;
    max-width: 460px;
    text-align: center;
}
.cofo-oops .mk {
    width: 46px; height: 46px; margin: 0 auto 16px;
    border-radius: 13px;
    background: var(--cofo-yellow-soft);
    color: var(--cofo-yellow);
    font-size: 25px; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
}
.cofo-oops h1 { font-size: 19px; font-weight: 700; margin: 0 0 10px; }
.cofo-oops p { font-size: 13px; color: var(--cofo-text-2); line-height: 1.7; margin: 0 0 14px; }
.cofo-oops .when {
    background: var(--cofo-soft);
    border: 1px solid var(--cofo-hair);
    border-radius: 9px;
    padding: 11px 14px;
    margin-bottom: 14px;
}
.cofo-oops .when span { display: block; font-size: 11.5px; color: var(--cofo-text-3); margin-bottom: 3px; }
.cofo-oops .when b { font-size: 16px; font-variant-numeric: tabular-nums; }
.cofo-oops .hint { font-size: 12.5px; }
.cofo-oops .go { display: flex; gap: 8px; justify-content: center; }

/* 고정 공지 띠. 알람 배너와 색을 나눈다 — 둘이 같으면 공지 때문에 알람을 흘린다. */
.cofo-notice {
    display: flex;
    align-items: center;
    gap: 9px;
    background: var(--cofo-blue-soft);
    color: var(--cofo-blue);
    border-left: 3px solid var(--cofo-blue);
    border-radius: 0 9px 9px 0;
    padding: 10px 14px;
    margin-bottom: 10px;
    font-size: 13px;
}
.cofo-notice .ic { display: inline-flex; }
.cofo-notice a { color: var(--cofo-blue); font-weight: 600; }
.cofo-notice .w { margin-left: auto; font-size: 11.5px; opacity: .75; white-space: nowrap; }

/* 자동 갱신 주기 안내. 기준 시각 옆에 붙어서 "이 화면은 저 혼자 바뀐다" 를
   말해준다. 안 적으면 보던 자리가 사라진 것을 고장으로 읽는다. */
.cofo-asof-hint {
    font-size: 11px;
    color: var(--cofo-text-3);
    white-space: nowrap;
}
