/* ============================================================
   관리자 화면 골격 (features/admin-shell) — 머리줄 · 왼쪽 메뉴 · 본문 칸      v1 (2026-09-16)
   ------------------------------------------------------------
   사용자 지시 2026-09-16: 「관리자페이지를 목업처럼 최적화 하자. 관리자페이지에 팝업으로 나오는건
   목업처럼 모두 메인페이지에 나오게 하고 HTML파일도 적절하게 나누어서 관리하고 쉽게」
   (목업: frontend/mockup_sample.html 「관리자 화면」 절 · 검토 → 사용자 「추천대로 진행」).

   ▣ 무엇이 바뀌었나
     · 종전: 설정 13개가 **팝업**이었고, 팝업을 CSS로 본문 자리에 «겹쳐» 띄워 본문처럼 보이게 했다
       (팝업 클래스 목록을 CSS·스크립트 두 곳에서 맞추고, 0.5초 감시로 메뉴 강조를 따라잡았다).
     · 지금: 메뉴 항목마다 **본문 칸(.adm-pane)** 하나. 모듈이 그 칸 안에 직접 그린다(init의 panel).
       팝업 앵커 목록·0.5초 감시는 없어졌다.
   ▣ 모양은 목업의 관리자 절을 따른다 — 넓은 왼쪽 메뉴(그룹 라벨 + 아이콘 + 이름), 고른 항목은
     연한 주색 바탕, 칸 머리는 «제목 + 한 줄 설명». ⛔ 색을 새로 정하지 않는다: 메인과 같은
     theme.css 토큰, 상태 5색은 상태판 CSS(features/admin-health/admin-health.css)의 --ah-*.
   ⛔ 「페이지는 스크롤하지 않는다」(2026-08-25 확정)는 그대로다 — 스크롤은 왼쪽 메뉴와 본문 칸 «안에서만».

   싣는 순서: theme.css → admin-health.css(상태 토큰) → 이 파일.
   ============================================================ */

*{box-sizing:border-box}
/* ⚠ 토큰 이름은 theme.css 정본을 그대로 쓴다 — --bg-app·--text 는 **없는 이름**이라
   폴백(밝은 배경·어두운 글자)이 다크에도 걸려 글자가 통째로 안 보였다 (2026-08-25 실측·수정) */
html,body{height:100%}
body{margin:0;background:var(--bg-space,#eef2f6);color:var(--text-primary,#0f172a);
     font:13px/1.5 var(--font-sans,"Malgun Gothic","맑은 고딕",system-ui,sans-serif);
     height:100dvh;display:flex;flex-direction:column;overflow:hidden}
a{color:inherit}

/* 골격 치수 — --adm-top은 셸 스크립트가 머리줄 실측 높이로 갈아 준다(좁은 화면에서 두 줄로 접힌다).
   --adm-sel = 고른 메뉴 바탕(목업 --primary-subtle). theme.css에 없는 값이라 여기서만 둔다. */
:root{--adm-navw:216px;--adm-top:52px;--adm-sel:#e0f2fe}
:root[data-theme="dark"]{--adm-sel:rgba(56,189,248,.16)}

/* ── 로그인 창 · 로그인 칩 (2026-08-25 관리자 인증) ─────────────────────── */
.ah-login{position:fixed;inset:0;z-index:950;display:flex;align-items:center;justify-content:center;
  background:var(--bg-space,#eef2f6)}
/* ⛔ 2026-08-25 실결함: 위 display:flex가 hidden 속성(UA의 display:none)을 이겨 로그인 창이 **항상** 보였다 —
   localhost에서도 로그인 창이 뜬 원인. 속성 선택자로 되돌려 둔다. 다른 오버레이도 같은 함정에 걸린다 */
.ah-login[hidden]{display:none}
.ah-loginbox{width:min(360px,92vw);display:flex;flex-direction:column;gap:9px;padding:22px 22px 18px;
  border:1px solid var(--ah-line);border-radius:14px;background:var(--ah-card);box-shadow:var(--ah-shadow)}
.ah-lt{font-size:16px;font-weight:800;letter-spacing:-.3px}
.ah-ls{font-size:12px;color:var(--ah-off);margin-top:-4px}
.ah-loginbox input{font:inherit;font-size:14px;padding:9px 11px;border-radius:9px;
  border:1px solid var(--ah-line);background:var(--ah-sunken);color:inherit}
.ah-loginbox input:focus{outline:2px solid var(--ahc);outline-offset:-1px}
.ah-loginbox button{font:inherit;font-size:13px;font-weight:700;padding:9px;border-radius:9px;border:0;
  background:var(--ahc);color:#fff;cursor:pointer}
.ah-loginbox button:disabled{opacity:.5;cursor:default}
.ah-lm{font-size:12px;min-height:16px;color:var(--ah-err)}
.ah-lw{font-size:11px;color:var(--ah-off);line-height:1.45}
.ah-authchip{font-size:11px;font-weight:700;padding:2px 9px;border-radius:999px;background:var(--ah-sunken);
  color:var(--ah-off);white-space:nowrap;border:1px solid var(--ah-line)}
.ah-authchip.on{color:var(--ah-ok);background:var(--ah-ok-bg);border-color:currentColor}

/* ── 머리줄 (목업 .adm-top) ─────────────────────────────────────────────── */
header.ah-hd{flex:none;min-height:var(--adm-top);display:flex;align-items:center;gap:8px;flex-wrap:nowrap;
  padding:6px 14px;background:var(--ah-card);border-bottom:1px solid var(--ah-line)}
.ah-brand{display:flex;align-items:center;gap:8px;min-width:0}
.ah-logo{width:18px;height:18px;flex:none;display:block;border-radius:4px}
.ah-hd h1{margin:0;font-size:15px;font-weight:800;letter-spacing:-.2px;white-space:nowrap}
.ah-crumb{color:var(--ah-off);font-size:12px;font-weight:600;white-space:nowrap}
.ah-hd .sp{flex:1}
/* 머리줄 단추 — 목업 .f-chip(알약) 모양 */
.ah-hd button,.ah-hd a.btn{font:inherit;font-size:11.5px;font-weight:700;height:28px;padding:0 11px;
  border-radius:999px;border:1px solid var(--border-medium,#cbd5e1);background:var(--ah-card);
  color:var(--text-secondary,#475569);cursor:pointer;text-decoration:none;display:inline-flex;
  align-items:center;gap:4px;white-space:nowrap}
.ah-hd button:hover,.ah-hd a.btn:hover{background:var(--bg-hover,#f1f5f9);color:var(--text-primary,#0f172a)}
/* ⛔ 위 display:inline-flex가 hidden 속성(UA display:none)을 이긴다 — 로그아웃이 루프백에서도 보였다
   (2026-09-16 실측). 이 저장소에서 여러 번 겪은 함정이라 명시해 둔다. */
.ah-hd [hidden]{display:none}
.ah-hd .live{font-size:11.5px;color:var(--ah-off);min-width:88px;text-align:right;
  font-family:var(--font-mono,Consolas,monospace);font-variant-numeric:tabular-nums;white-space:nowrap}
.ah-navtg{flex:none}
@media (max-width:980px){
  header.ah-hd{flex-wrap:wrap;gap:6px} .ah-hd .live{min-width:0}   /* ⚠ header. 을 붙인다 — 위 기본 규칙(header.ah-hd)이 특정도로 이긴다 */
  .ah-hd .btn-t{display:none}                  /* 좁으면 단추 글자를 접고 아이콘만 */
}
/* 폰 폭 — 첫 줄은 ☰ + 이름·현재 위치, 둘째 줄에 단추들. 한 줄에 다 넣으면 이름이 알약에 덮였다(2026-09-16 390px 실측) */
@media (max-width:640px){
  .ah-brand{flex:1 1 calc(100% - 60px)}
  .ah-hd .sp{display:none}
  .ah-hd .live{margin-right:auto;text-align:left}
}

/* ── 2단 골격 — [왼쪽 메뉴 | 본문] ─────────────────────────────────────── */
.adm-shell{flex:1;min-height:0;display:flex}

/* 왼쪽 메뉴 (목업 .adm-rail) — ⭐ 항목·그룹의 정본은 admin-menu.js 다(여기는 모양만).
   ⚠ 배지(.nv-b)는 상태판 스냅샷에서 뽑는다 — 「안 열어 본 탭의 빨강이 어디에 있어도 보여야
     한다」는 admin-health 머리말의 요구를 이 메뉴가 물려받는다. ⛔ 배지를 지우면 그 요구가 깨진다. */
.adm-nav{flex:none;width:var(--adm-navw);min-width:0;overflow-x:hidden;overflow-y:auto;
  overscroll-behavior:contain;background:var(--ah-card);border-right:1px solid var(--ah-line);
  padding:6px 8px 16px;display:flex;flex-direction:column;transition:width .14s ease,padding .14s ease}
body.nav-off .adm-nav{width:0;padding-left:0;padding-right:0;border-right-width:0}
/* 그룹 머리 = 접기 단추 (2026-10-01 2단계). 줄 모양은 종전 이름표 그대로, 오른쪽에 ▾ 하나만 더한다. */
.nv-grp{flex:none;display:flex;flex-direction:column}
.nv-g{flex:none;display:flex;align-items:center;gap:6px;width:100%;font:inherit;font-size:11px;font-weight:800;
  letter-spacing:.04em;color:var(--text-muted,#64748b);padding:12px 8px 5px;white-space:nowrap;
  border:0;border-radius:6px;background:transparent;cursor:pointer;text-align:left}
.nv-grp:first-child .nv-g{padding-top:6px}
.nv-g:hover{color:var(--text-primary,#0f172a)}
.nv-g:focus-visible{outline:2px solid var(--ahc);outline-offset:-2px}
.nv-gt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.nv-chev{flex:none;width:14px;text-align:center;font-size:12px;opacity:.75;transition:transform .14s ease}
.nv-grp.is-closed .nv-chev{transform:rotate(-90deg)}
/* 지금 보는 칸이 접힌 그룹 안에 있다 — 머리를 강조색으로(「어디 있는지」) */
.nv-grp.is-cur .nv-g{color:var(--ahc);background:var(--adm-sel)}
.nv-gi{display:flex;flex-direction:column}
.nv-gi[hidden]{display:none}                   /* ⚠ 위 display:flex가 hidden 속성을 이기지 않게 */
@media (prefers-reduced-motion:reduce){.nv-chev{transition:none}}
.nv-i{flex:none;display:flex;align-items:center;gap:8px;width:100%;font:inherit;font-size:12.5px;font-weight:600;
  padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--text-secondary,#475569);
  cursor:pointer;text-align:left;white-space:nowrap}
.nv-i:hover{background:var(--bg-hover,#f1f5f9);color:var(--text-primary,#0f172a)}
.nv-i:focus-visible{outline:2px solid var(--ahc);outline-offset:-2px}
.nv-i.is-on{background:var(--adm-sel);color:var(--ahc);font-weight:700}
/* 모듈이 안 실려 눌러도 무반응일 항목은 «누를 수 있는 것»으로 보이지 않게 한다(조용한 무반응 방지) */
.nv-i.is-dim{opacity:.42;cursor:default}
.nv-ic{flex:none;width:16px;text-align:center;font-size:12px;opacity:.85}
.nv-t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.nv-b{flex:none;min-width:17px;height:17px;padding:0 5px;border-radius:999px;font-size:10px;font-weight:800;
  display:inline-flex;align-items:center;justify-content:center;font-variant-numeric:tabular-nums}
.nv-b:empty{display:none}
/* 「회원 관리」의 오늘 신규가입 배지 — 건강 상태(ah-t-*)가 아니라 헤더 #adminSignupBadge와 같은
   «새 소식» 뜻이라 색도 그와 같은 빨간 원(흰 글자)으로 간다(2026-10-02). */
.nv-b.nv-new{color:#fff;background:var(--status-danger,#c2261f)}
.nv-sp{flex:1;min-height:10px}
/* 「ADS-B 피더」 아래 피더 한 줄 — 새 탭으로 나가는 줄이라 활성(is-on) 표시를 하지 않는다 */
.nv-sub{padding-left:22px;font-size:11.5px;font-weight:500;color:var(--text-muted,#64748b)}
.nv-sub .nv-ic{width:10px;font-size:9px;opacity:.55}

/* ── 본문 · 칸 ──────────────────────────────────────────────────────────
   ⚠ container-type — 칸 안쪽 반응형을 «뷰포트»가 아니라 «본문 폭»으로 잰다(상태판 CSS의
     @container admbody). 부수효과: main 안의 position:fixed 자손이 main 영역에 갇힌다 —
     그래서 화면 전체를 덮어야 하는 로그인 창은 main 밖(body 직속)에 둔다. ⛔ 다시 넣지 말 것. */
main{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;
     container-type:inline-size;container-name:admbody}
/* 칸 하나 = 메뉴 항목 하나. 보이는 칸은 늘 하나다. 칸이 스스로 스크롤한다(페이지는 안 한다). */
.adm-pane{flex:1;min-height:0;overflow:auto;padding:14px 16px 28px;display:flex;flex-direction:column}
.adm-pane[hidden]{display:none}
/* 채움 칸 — 안쪽이 자기 스크롤을 갖는 것(상태판·요청사항·앱 도움말). 칸은 스크롤하지 않고 높이만 넘긴다 */
.adm-pane.is-fill{overflow:hidden;padding-bottom:12px}
.adm-pane.is-fill>.adm-pbody{flex:1;min-height:0;display:flex;flex-direction:column}
/* 칸 머리 (목업 .adm-hd) — 제목 + 한 줄 설명 + (있으면) 오른쪽 동작 */
.adm-hd{flex:none;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0 0 10px}
.adm-hd h1{margin:0;font-size:18px;font-weight:800;letter-spacing:-.3px;color:var(--text-primary,#0f172a)}
.adm-hd h1:focus{outline:none}          /* 메뉴 이동 뒤 초점 자리 — 눈에 띄는 테두리는 필요 없다 */
.adm-sub{font-size:12px;color:var(--text-muted,#64748b)}
.adm-hd-act{margin-left:auto;display:flex;gap:6px;align-self:center}
.adm-hd-act button{font:inherit;font-size:11.5px;font-weight:700;height:28px;padding:0 11px;border-radius:999px;
  border:1px solid var(--border-medium,#cbd5e1);background:var(--ah-card);color:var(--text-secondary,#475569);cursor:pointer}
.adm-hd-act button:hover{background:var(--bg-hover,#f1f5f9);color:var(--text-primary,#0f172a)}
/* 칸 탭 줄 (2026-10-01 3단계) — under로 묶인 칸(방문 → 현황·랭킹·유입경로). 상태판 탭 줄(.ah-tabs)과 같은 모양. */
.adm-subtabs{flex:none;display:flex;gap:2px;margin:-2px 0 12px;border-bottom:1px solid var(--ah-line);
  overflow-x:auto;scrollbar-width:none}
.adm-subtabs::-webkit-scrollbar{display:none}
.adm-subtab{font:inherit;font-size:12.5px;font-weight:600;color:var(--ah-off);cursor:pointer;background:none;border:0;
  border-bottom:2.5px solid transparent;padding:8px 12px;white-space:nowrap}
.adm-subtab:hover{color:var(--text-primary,#0f172a)}
.adm-subtab.is-on{color:var(--text-primary,#0f172a);border-bottom-color:var(--ahc)}
.adm-subtab:focus-visible{outline:2px solid var(--ahc);outline-offset:-2px;border-radius:6px}
.adm-pbody{min-width:0}
.adm-pbody:empty::before{content:"불러오는 중…";display:block;padding:30px;text-align:center;color:var(--ah-off)}
#extWarn{flex:none;margin:10px 16px 0}

/* ── 대시보드 칸 — 요약 카드 + 상태판 ────────────────────────────────────── */
.adm-sec-hd{display:flex;align-items:baseline;gap:10px;margin:14px 0 8px}
.adm-sec-hd h2{margin:0;font-size:15px;font-weight:800;color:var(--text-primary,#0f172a)}
.adm-sec-hd .adm-sec-s{font-size:11.5px;color:var(--text-muted,#64748b)}
/* 대시보드가 아닌 모니터링 탭(수집 경로·DB·사용량·읽는 법)은 카드를 감추고 상태판에 높이를 다 준다 */
body[data-view="focus"] .adm-cards,body[data-view="focus"] .adm-health-hd{display:none}
.adm-health-body{flex:1;min-height:0;display:flex;flex-direction:column}
/* 상태판도 카드와 같은 껍데기를 입는다. (탭 줄은 2026-10-01부터 보인다 — 아래 주석) */
.adm-health-body #ah{flex:1;min-height:240px;display:flex;flex-direction:column;border:1px solid var(--ah-line);
  border-radius:10px;background:var(--ah-card);box-shadow:var(--ah-shadow);overflow:hidden}
/* 대시보드에서는 카드가 위를 먹는다 — 상태판 개요는 안쪽이 스크롤하지 않는 고정 배치라 눌리면 글자가
   상자 밖으로 샌다(2026-09-16 1440×900 실측: 「상태 분포」의 키 파일 줄). 눌리는 대신 칸이 스크롤하게 바닥을 둔다. */
body[data-view="dash"] .adm-health-body #ah{min-height:600px}
/* 2026-10-01 — 탭 줄을 다시 보인다: 왼쪽 메뉴의 상태판 4줄을 「상태판」 한 줄로 합쳤다(admin-menu.js hide·under).
   탭 이름표의 배지가 「안 열어 본 탭의 빨강」을 알리고, 메뉴의 「상태판」 줄은 그 합계를 단다. */
.adm-health-body .ah-pane{padding:10px 12px 12px}
.ah-wait{padding:40px;text-align:center;color:var(--ah-off)}

/* 요약 카드 (목업 .adm-cards) — 카드 하나 = 설정 항목 하나의 칩 한 줄. 누르면 그 칸으로 간다.
   ⚠ 카드 폭 268px 이상 — 좁히면 칩 글자가 3~4줄로 접혀 오히려 세로를 더 먹는다(2026-09-09 실측).
   ⚠ flex-wrap — grid(auto-fit)는 마지막 줄에 혼자 남은 카드가 한 칸만 차지했다(2026-09-04 실측). */
.ops-bar{display:flex;flex-wrap:wrap;gap:10px}
/* 2026-09-23 2차 — 카드마다 붙였던 무지개 장식은 정보 의미가 없고, 짧은 상단 선도 잘린
   테두리처럼 보였다. 장식색은 전부 없애고 표면·테두리·깊이만으로 카드를 구분한다.
   색은 안쪽의 상태 배지·진행 막대·실제 설정값처럼 뜻이 있는 곳에서만 쓴다. */
.ops-cell{flex:1 1 268px;max-width:380px;min-width:0;border:1px solid var(--border-subtle,#dfe5ec);
  border-radius:14px;background:var(--ah-card);overflow:hidden;
  box-shadow:0 1px 2px rgba(15,23,42,.045),0 7px 18px rgba(15,23,42,.045);
  transition:border-color .16s,box-shadow .16s,transform .16s;
  display:flex;flex-direction:column;position:relative}
.ops-cell:hover{border-color:var(--border-medium,#c5cfdb);transform:translateY(-1px);
  box-shadow:0 2px 4px rgba(15,23,42,.055),0 11px 26px rgba(15,23,42,.075)}
:root[data-theme="dark"] .ops-cell{box-shadow:0 1px 2px rgba(0,0,0,.2),0 8px 22px rgba(0,0,0,.16)}
:root[data-theme="dark"] .ops-cell:hover{box-shadow:0 2px 4px rgba(0,0,0,.24),0 12px 28px rgba(0,0,0,.22)}
/* 안쪽 칩이 카드 높이를 다 쓴다. ⛔ display·flex-direction은 강제하지 않는다 — 칩은 가로 한 줄이다 */
.ops-cell>*{flex:1;min-height:0}
@media (prefers-reduced-motion:reduce){.ops-cell{transition:none}.ops-cell:hover{transform:none}}
/* 안쪽 모듈 칩의 «자기 여백·테두리»를 걷어낸다 — 카드가 그 역할을 대신한다 */
.ops-cell .af-chip{display:flex;width:100%;margin:0;border:0;border-radius:0;background:transparent}
.ops-cell .ae-wrap,.ops-cell .an-wrap{margin:0;height:100%;border:0;border-radius:0;background:transparent}
.ops-cell .al-hd,.ops-cell .aac-hd,.ops-cell .ntc-hd{margin:0;border:0;border-radius:0;background:transparent}
.ops-cell .ak-chip,.ops-cell .acd-chip{width:100%;border:0;border-radius:0;background:transparent;padding:9px 11px}
.ops-cell .af-chip,.ops-cell .afd-chip,.ops-cell .ae-hd,.ops-cell .an-hd,.ops-cell .al-hd,
.ops-cell .aac-hd,.ops-cell .ntc-hd{padding:7px 10px 7px 12px;min-height:36px}
/* 카드 폭에서 배지가 세 줄로 접혀 동그라미가 되던 것(유사호출부호 「미작성 425」) */
.ops-cell .asx-badge{white-space:nowrap}

/* 모듈 색 토큰 맞춤 — 모듈은 폴백(라이트)만 갖고, 이 화면의 값은 여기서 덮는다.
   API 키 대장(--ak-*)의 상태색은 상태판(--ah-*)과 **같은 값** — 같은 화면에서 노랑이 둘이면 안 된다. */
:root,:root[data-theme="dark"]{
  --ak-line:var(--ah-line); --ak-card:var(--ah-card); --ak-sunken:var(--ah-sunken);
  --ak-fg:var(--text-primary); --ak-dim:var(--text-muted);
  --ak-ok:var(--ah-ok); --ak-warn:var(--ah-warn); --ak-err:var(--ah-err); --ak-accent:var(--ahc);
}
:root{--af-accent:#2563eb;--af-accent-bg:#dbeafe;--af-accent-fg:#1d4ed8;--af-fill:#94a3b8;
      --af-danger:#b91c1c;--af-ok:#15803d;--af-warn:#b45309}
:root[data-theme="dark"]{--af-accent:#60a5fa;--af-accent-bg:#1e3a5f;--af-accent-fg:#bfdbfe;--af-fill:#64748b;
      --af-danger:#f87171;--af-ok:#4ade80;--af-warn:#fbbf24;
      --ent-bg:#2e1f4a;--ent-fg:#d8b4fe;--ent-bd:#5b3d8a;--ent-cbg:rgba(168,85,247,.12)}

/* 요청사항 칸 — 목록·상세가 각자 스크롤하는 카드라 칸 높이를 다 쓴다 */
#admb-feedback>*{flex:1;min-height:0;border:1px solid var(--ah-line);border-radius:10px;
  background:var(--ah-card);box-shadow:var(--ah-shadow);overflow:hidden}

/* ── 좁은 화면(≤900px): 왼쪽 메뉴를 «미는 기둥»이 아니라 «덮는 서랍»으로 (2026-09-11) ────
   서랍이면 본문은 언제나 전체 폭이고, 고르면 저절로 닫힌다(셸 스크립트 navAutoClose).
   ⚠ 900px는 admin-shell.js의 NAV_NARROW와 **같은 값**이어야 한다. */
.adm-scrim{display:none}
@media (max-width:900px){
  .adm-nav{position:fixed;top:var(--adm-top);left:0;bottom:0;z-index:1650;box-shadow:4px 0 18px rgba(0,0,0,.18)}
  body.nav-off .adm-nav{box-shadow:none}
  .adm-scrim{position:fixed;top:var(--adm-top);left:0;right:0;bottom:0;z-index:1640;background:rgba(15,23,42,.38)}
  body:not(.nav-off) .adm-scrim{display:block}
  .adm-pane{padding:12px 12px 24px}
}
/* 로그인 창이 보일 때는 메뉴·스크림을 감춘다 — 로그인 전에는 메뉴를 쓸 수 없다.
   (#loginWrap·.adm-shell·.adm-scrim은 body 직속 형제다 — 순서가 바뀌면 이 선택자가 조용히 죽는다.) */
#loginWrap:not([hidden])~.adm-shell .adm-nav,#loginWrap:not([hidden])~.adm-scrim{display:none}
