/* 디자인 토큰 + 공통 컴포넌트. 서비스 웹과 관리자 웹이 함께 쓴다.
 *
 * 규칙 세 가지.
 *  1. 색·간격·라운드·그림자는 **여기 변수로만** 정한다. 화면마다 색을 직접 쓰면
 *     한 곳을 바꿀 때 나머지가 따라오지 않는다.
 *  2. 외부 리소스를 쓰지 않는다 — 웹폰트·아이콘폰트·배경 이미지 모두. 임베디드/모바일
 *     환경에서 로딩 실패가 곧 "화면이 깨진 것"으로 보이기 때문이다. 배경은 CSS 그라디언트,
 *     아이콘은 인라인 SVG 로 만든다.
 *  3. 글자 크기는 16px 밑으로 내리지 않는다(입력칸). iOS 가 포커스 때 화면을 확대한다.
 */

:root {
  /* 브랜드 — wisecon.kr 사이트에서 가져온 값
     (general.css/layout.css 의 변수: 블루 #0d6efd, 먹 #262628, 바탕 #f4f6f8, 선 #e3e3e3).
     하늘색 계열은 그 블루를 밝게 푼 값으로 맞췄다. */
  --brand: #0d6efd;            /* 사이트 메인 블루 */
  --brand-dark: #0b5ed7;       /* 눌림·호버 */
  --brand-sky: #4dabf7;        /* 하늘색 — 그라디언트·강조 */
  --brand-soft: #e7f1ff;       /* 옅은 배경(선택 항목 등) */
  --ink: #262628;              /* 본문 글자 — 사이트와 동일 */
  --muted: #6b7684;            /* 보조 글자 */
  --bg: #f4f6f8;               /* 화면 바탕 — 사이트와 동일 */
  --card: #ffffff;
  --line: #e3e3e3;             /* 선 — 사이트와 동일 */
  --danger: #c0392b;
  --ok: #157f4a;
  --warn: #ce930b;             /* 사이트의 포인트 골드 */

  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 4px 20px rgba(13, 37, 73, .08);
  --shadow-lg: 0 18px 44px rgba(13, 37, 73, .18);
  --gap: 12px;

  /* 화면 배경 — 로그인부터 로그인 뒤 화면까지 **같은 파랑**을 깐다. 메뉴·카드는 흰색으로 그 위에 뜬다.
     배경 위에 바로 놓이는 글자는 --on-backdrop 계열을 쓴다(아래 "배경 위 글자"). */
  --backdrop-base: #0b2545;
  --backdrop:
    radial-gradient(circle at 15% 12%, rgba(77, 171, 247, .40), transparent 45%),
    radial-gradient(circle at 85% 80%, rgba(13, 110, 253, .45), transparent 50%),
    linear-gradient(160deg, #0b2545 0%, #12417c 45%, #091a30 100%);
  --on-backdrop: #ffffff;
  --on-backdrop-muted: rgba(255, 255, 255, .80);
  --on-backdrop-danger: #ffb4a8;

  /* 상단 바(앱바) — 짙은 파랑 위 흰 글자·흰 선 아이콘 (B안, 2026-09-22).
     로그인 화면의 파랑을 로그인 뒤에도 잇는다. 버튼은 반투명 흰색이라 바탕 파랑이 비친다 */
  --bar: linear-gradient(90deg, #0b2545 0%, #12417c 100%);
  --bar-base: #0b2545;
  --on-bar: #ffffff;
  --bar-btn: rgba(255, 255, 255, .10);
  --bar-btn-line: rgba(255, 255, 255, .24);
  --bar-btn-hover: rgba(255, 255, 255, .20);
  --bar-btn-hover-line: rgba(255, 255, 255, .50);
  --tap: 44px;                 /* 터치 타깃 최소 높이 */
}

@media (prefers-color-scheme: dark) {
  :root {
    --brand: #4dabf7;
    --brand-dark: #339af0;
    --brand-sky: #74c0fc;
    --brand-soft: #14314f;
    --ink: #e7edf3;
    --muted: #97a6b5;
    --bg: #0e1620;
    --card: #16212c;
    --line: #24313f;
    --danger: #ef6b5c;
    --ok: #4ade80;
    --warn: #e0a33e;
    --shadow: 0 4px 20px rgba(0, 0, 0, .35);
    --shadow-lg: 0 18px 44px rgba(0, 0, 0, .5);
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  min-height: 100vh;
  /* 파랑 바탕. --bg(옅은 회색)는 입력칸·줄무늬 같은 카드 안쪽 색으로만 남는다 */
  background-color: var(--backdrop-base);
  background-image: var(--backdrop);
  background-attachment: fixed;
  color: var(--ink);
  font-family: system-ui, -apple-system, "Noto Sans KR", "Apple SD Gothic Neo", sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

/* --- 인증 화면: 전체 배경 + 가운데 카드 ------------------------------- */
/* 배경은 그라디언트 두 겹으로만 만든다. 이미지가 없으니 실패할 것도 없다. */
/* 배경은 body 공통(--backdrop). 인증 화면은 카드를 가운데 놓는 배치만 다르다 */
body.auth {
  display: flex; align-items: center; justify-content: center;
  padding: max(env(safe-area-inset-top), 24px) 16px max(env(safe-area-inset-bottom), 24px);
}
.auth-card {
  width: 100%; max-width: 420px;
  background-color: var(--card);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  padding: 32px 26px 26px;
}
.auth-head { text-align: center; margin-bottom: 24px; }
.auth-head .logo { display: block; margin: 0 auto 12px; }
.auth-head h1 { margin: 0 0 6px; font-size: 1.35rem; letter-spacing: -.01em; }
.auth-head p { margin: 0; color: var(--muted); font-size: .88rem; }
.auth-foot { margin: 20px 0 0; text-align: center; font-size: .9rem; color: var(--muted); }
.auth-foot a { color: var(--brand); font-weight: 600; text-decoration: none; }
.auth-foot a:hover { text-decoration: underline; }

/* --- 앱바 ------------------------------------------------------------- */
header.appbar {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: var(--gap);
  padding: max(env(safe-area-inset-top), 10px) 16px 10px;
  background-color: var(--bar-base);
  background-image: var(--bar);
  border-bottom: 1px solid rgba(255, 255, 255, .10);
  box-shadow: 0 2px 12px rgba(4, 14, 30, .35);       /* 흰 카드와 명암 차이가 커서 경계를 그림자로 */
  color: var(--on-bar);
}
header.appbar h1 { flex: 1; margin: 0; font-size: 1.05rem; color: var(--on-bar); }
/* 바 위 버튼(← 목록 · 아이콘) — 흰 글자·흰 선 아이콘이 제목과 한 묶음으로 읽히게 */
header.appbar button {
  background-color: var(--bar-btn); border-color: var(--bar-btn-line); color: var(--on-bar);
}
header.appbar button:hover {
  background-color: var(--bar-btn-hover); border-color: var(--bar-btn-hover-line); color: var(--on-bar);
}
header.appbar button:focus-visible { outline: 2px solid var(--on-bar); outline-offset: 2px; }
header.appbar .brandmark { display: flex; align-items: center; gap: 8px; }
/* 앱바 오른쪽 아이콘 버튼 묶음. 제목(.brandmark) 옆이 아니라 오른쪽 끝으로 민다 */
.appbar-actions { margin-left: auto; display: flex; align-items: center; gap: 6px; }
button.icon-btn {
  width: var(--tap); height: var(--tap); min-height: var(--tap); padding: 0;
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
button.icon-btn svg { width: 21px; height: 21px; }
/* 파란(primary) 아이콘 버튼을 쓸 때 — button.primary 의 width:100%(폼 제출용)가 뒤에 있어 덮어쓴다 */
button.icon-btn.primary { width: var(--tap); }

/* 제목(카메라 이름)을 바 **한가운데**에 놓는 변형. 좌우에 같은 폭(1fr)의 자리를 두어,
   왼쪽에 [← 목록] 버튼이 있어도 제목이 정확히 가운데 온다 — 남은 공간만 기준으로
   text-align:center 를 주면 버튼 폭만큼 오른쪽으로 밀린다.
   이름이 길면 제목 쪽이 줄어들며 …로 잘린다(버튼을 밀어내지 않는다). */
header.appbar.title-center > .appbar-side {
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center; gap: 6px;
}
header.appbar.title-center > .appbar-side:last-child { justify-content: flex-end; }
header.appbar.title-center > h1 {
  flex: 0 1 auto; min-width: 0; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

main {
  max-width: 720px; margin: 0 auto;
  padding: 16px 16px max(env(safe-area-inset-bottom), 28px);
}
/* 영상 화면은 플레이어가 스스로 폭을 정한다 — 720px 에 묶으면 1080p 를 절반만 쓴다 */
main.watch { max-width: 1600px; }

/* --- 폼 --------------------------------------------------------------- */
.field { margin-bottom: 16px; }
label { display: block; margin-bottom: 7px; font-size: .86rem; font-weight: 600; color: var(--ink); }
input, select, textarea {
  display: block; width: 100%; min-height: var(--tap);
  padding: 10px 13px; font: inherit;
  color: var(--ink); background-color: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
input::placeholder { color: var(--muted); opacity: .65; }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px; border-color: var(--brand);
}
.hint { margin-top: 6px; font-size: .8rem; color: var(--muted); }

button {
  min-height: var(--tap); padding: 10px 16px;
  font: inherit; font-weight: 600;
  color: var(--ink); background-color: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  cursor: pointer;
}
button:hover { border-color: var(--brand); color: var(--brand); }
button:active { transform: translateY(1px); }
button[disabled] { opacity: .5; cursor: default; transform: none; }
button.primary {
  width: 100%;
  background-color: var(--brand); color: #fff; border-color: transparent;
}
button.primary:hover { background-color: var(--brand-dark); color: #fff; }
button.danger { color: var(--danger); }
button.danger:hover { border-color: var(--danger); color: var(--danger); }
button.small { min-height: 38px; padding: 7px 13px; font-size: .88rem; font-weight: 500; }
.actions { display: flex; gap: 8px; flex-wrap: wrap; }

a { color: var(--brand); }
.center { margin-top: 20px; text-align: center; font-size: .9rem; }

/* --- 알림 ------------------------------------------------------------- */
.alert {
  margin-bottom: var(--gap); padding: 11px 13px;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font-size: .9rem;
}
/* 알림은 파랑 배경 위에 뜨므로 바탕을 카드색으로 채운다(반투명이면 빨간 글자가 묻힌다) */
.alert.error { color: var(--danger); border-color: var(--danger);
  background-color: color-mix(in srgb, var(--danger) 8%, var(--card)); }
.alert.ok { color: var(--ok); border-color: var(--ok);
  background-color: color-mix(in srgb, var(--ok) 10%, var(--card)); }
.alert[hidden] { display: none; }

/* --- 카드·배지 -------------------------------------------------------- */
.card {
  background-color: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted);
  font-size: .75rem; font-weight: 600; white-space: nowrap;
}
.badge::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background-color: currentColor;
}
.badge.online { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }

.empty { padding: 44px 16px; text-align: center; color: var(--muted); }
.empty svg { margin-bottom: 12px; opacity: .5; }

/* 스피너 — 이미지 없이 CSS 로만 */
.spinner {
  display: inline-block; width: 18px; height: 18px; vertical-align: -3px;
  border: 2px solid color-mix(in srgb, var(--brand) 30%, transparent);
  border-top-color: var(--brand); border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }

/* --- 배경 위 글자 ------------------------------------------------------ */
/* 카드 밖, 파랑 배경에 바로 놓이는 안내 글자만 밝게 한다. `main >` 로 좁혀서
   카드 안의 같은 클래스(.hint 등)는 원래 색을 유지한다. */
main > .empty, main > p.sub, main > .note, main > .hint, main > .pager,
main > .video-state, main > .quality-row > .hint, .toolbar > .result-count {
  color: var(--on-backdrop-muted);
}
/* 주의 문구(.note)는 페이지에서는 패널 조각 안(main > #panel > div)에 있어 `main >` 로 안 잡힌다 */
main p.note { color: var(--on-backdrop-muted); }        /* p 로 좁힌다 — 관리자 표의 td.note(메모 칸)는 건드리지 않게 */
main .card p.note { color: var(--muted); }
main > .video-state.error { color: var(--on-backdrop-danger); }
main > .hint strong, main > .note strong { color: var(--on-backdrop); }
/* 배경 위 비활성 버튼(페이지 넘김) — 반투명 흰 버튼은 파랑 위에서 탁한 회색이 된다.
   배경에 녹아드는 테두리 버튼으로 "누를 수 없음"을 보인다 */
main > .pager button[disabled] {
  opacity: 1; color: rgba(255, 255, 255, .5);
  background-color: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .22);
}

/* --- 대화상자(팝업) ---------------------------------------------------- */
/* 목록 카드의 아이콘(연결 설정·카메라 설정·이름 수정·삭제)이 여는 화면.
   폰(≤640px)에서는 **화면을 꽉 채우는 시트**, 그보다 넓으면 **가운데 카드**다 —
   좁은 화면에서 작은 카드를 띄우면 입력칸이 키보드에 가리고, 넓은 화면에서 꽉 채우면
   목록을 잃어버린 것처럼 보인다. 같은 마크업에 미디어 쿼리만 다르게 준다.
   버튼은 언제나 **맨 아래 한 줄**(확인·취소 / 닫기·영상 보기)에 고정된다. */
.dialog-backdrop {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: 24px 16px;
  background-color: rgba(4, 14, 30, .62);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  animation: dialog-fade .16s ease;
}
.dialog {
  display: flex; flex-direction: column;
  width: 100%; max-width: 420px; max-height: min(88svh, 760px);
  background-color: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 16px;
  box-shadow: var(--shadow-lg); overflow: hidden;
  animation: dialog-rise .18s ease;
}
.dialog.wide { max-width: 560px; }           /* 연결 설정·카메라 설정처럼 내용이 있는 팝업 */
.dialog:focus { outline: none; }             /* 팝업은 열릴 때 팝업 자체에 포커스(app.js) — 테두리는 그리지 않는다 */

/* 제목은 가운데. 3칸 격자 [빈칸 | 제목 | ✕] — 양옆 칸 폭이 같아야 ✕ 가 있어도 제목이 정확히 가운데 온다 */
.dialog-head {
  display: grid; grid-template-columns: var(--tap) minmax(0, 1fr) var(--tap);
  align-items: center; gap: 8px; flex: 0 0 auto;
  padding: 14px 14px 12px; border-bottom: 1px solid var(--line);
}
.dialog-head h2 { grid-column: 2; grid-row: 1; margin: 0; font-size: 1.05rem; text-align: center; }
.dialog-head .dialog-close { grid-column: 3; grid-row: 1; }
/* 부제(카메라 이름·일련번호) — 제목 아래 작은 글씨 */
.dialog-head .dialog-sub {
  display: block; margin-top: 2px; font-size: .8rem; font-weight: 400; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 알림은 제목과 본문 사이 — 본문을 아래로 내려도 결과가 늘 보인다 */
.dialog > .alert { flex: 0 0 auto; margin: 12px 18px 0; }

/* 내용이 길면 여기만 스크롤한다 — 제목·알림·버튼은 늘 보인다 */
.dialog-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: 16px 18px; }
.dialog-body > :first-child { margin-top: 0; }
/* 팝업 안에서는 카드 테두리를 지운다 — 팝업 자체가 이미 카드다(테두리 두 겹 방지).
   같은 조각이 페이지에서는 원래대로 카드로 보인다 */
.dialog-body .card {
  padding: 0; border: 0; border-radius: 0; box-shadow: none; background: none; margin-bottom: 0;
}
.dialog-body .card + .card {
  margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--line);
}
/* 섹션 팝업(카메라 설정) — 섹션이 여럿이라 페이지(settings.html)처럼 칸칸이 카드로 보인다.
   본문 바탕을 한 톤 낮춰 카드가 떠 보이게 한다 */
.dialog.sections .dialog-body { background-color: var(--bg); }
.dialog.sections .dialog-body .card {
  padding: 18px; margin: 0 0 var(--gap); border: 1px solid var(--line); border-radius: var(--radius);
  background-color: var(--card); box-shadow: var(--shadow);
}
.dialog.sections .dialog-body .card:last-child { margin-bottom: 0; }
.dialog.sections .dialog-body .card > .field:last-child { margin-bottom: 0; }   /* 입력 한 칸(이름 수정) 아래 여백 */
/* 묻는 팝업(삭제 확인 등)의 본문 — 한 줄에 한 문장 */
.dialog-text { margin: 0 0 10px; font-size: .95rem; }
.dialog-text:last-child { margin-bottom: 0; }
.dialog-text.muted { color: var(--muted); font-size: .86rem; }

.dialog-foot {
  display: flex; gap: 8px; flex: 0 0 auto;
  padding: 12px 14px; border-top: 1px solid var(--line);
  background-color: var(--card);
}
/* 버튼은 화면 폭을 똑같이 나눠 갖는다(확인/취소가 한쪽으로 쏠리지 않게) */
.dialog-foot button { flex: 1 1 0; min-width: 0; }
.dialog-foot button.primary { width: auto; }  /* button.primary 의 width:100%(폼 제출용)를 되돌린다 */
/* 되돌릴 수 없는 확인(등록 해제) — 맨 아래 줄에서는 빨간 글자만으로는 약해서 꽉 채운다 */
.dialog-foot button.danger {
  background-color: var(--danger); color: #fff; border-color: transparent;
}
.dialog-foot button.danger:hover {
  background-color: color-mix(in srgb, var(--danger) 86%, #000); color: #fff; border-color: transparent;
}

/* 폰 — 화면을 꽉 채우는 시트. 닫기(✕)는 왼쪽으로 옮겨 "뒤로"처럼 읽히게 한다 */
@media (max-width: 640px) {
  .dialog-backdrop { padding: 0; align-items: stretch; }
  .dialog {
    max-width: none; max-height: none; height: 100dvh;
    border: 0; border-radius: 0; animation: dialog-sheet .2s ease;
  }
  .dialog-head { padding: max(env(safe-area-inset-top), 12px) 14px 12px; }
  .dialog-head .dialog-close { grid-column: 1; }
  .dialog-foot { padding-bottom: max(env(safe-area-inset-bottom), 12px); }
}

/* 팝업이 떠 있는 동안 뒤 목록이 같이 스크롤되지 않게 */
body.dialog-open { overflow: hidden; }

@keyframes dialog-fade { from { opacity: 0; } }
@keyframes dialog-rise { from { opacity: 0; transform: translateY(8px) scale(.98); } }
@keyframes dialog-sheet { from { transform: translateY(16px); } }
@media (prefers-reduced-motion: reduce) {
  .dialog-backdrop, .dialog { animation: none; }
}

/* --- 화면 하단 버튼 바 -------------------------------------------------- */
/* 페이지(연결 설정·카메라 설정)의 "카메라 목록 · 영상 보기" 처럼 **이동**하는 버튼.
   내용이 짧으면 화면 맨 아래에 붙고(margin-top:auto), 길면 스크롤 위에 떠 있는다(sticky).
   `<body class="has-actions">` 가 있어야 아래로 밀린다. */
body.has-actions { display: flex; flex-direction: column; min-height: 100svh; }
/* 버튼 바가 본문 아래 여백 위에 떠 보이지 않게 — 아래 여백은 바가 직접 갖는다 */
body.has-actions > main { flex: 1 1 auto; display: flex; flex-direction: column; padding-bottom: 0; }
.page-actions {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; gap: 8px;
  margin: var(--gap) -16px 0; margin-top: auto;      /* 본문 좌우 여백을 지워 폭을 꽉 채운다 */
  padding: 10px 16px max(env(safe-area-inset-bottom), 10px);
  background-color: color-mix(in srgb, var(--backdrop-base) 86%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border-top: 1px solid rgba(255, 255, 255, .14);
}
.page-actions button { flex: 1 1 0; min-width: 0; }
.page-actions button.primary { width: auto; }

/* --- 카드 안의 동작 줄 -------------------------------------------------- */
/* 폼을 끝내는 버튼([취소][등록하기], [닫기][비밀번호 변경])은 **그 카드 안**, 입력칸 바로
   아래에 둔다. 화면 맨 아래에 따로 띄우면 위의 입력칸과 남남처럼 보인다 — 실제로 그렇게
   보였다(2026-09-23 사용자 지적). 위의 .page-actions 는 "다음 화면으로 이동"하는 바라
   화면에 속하지만, 이 줄은 **그 카드의 내용에 속한다**.
   모양은 팝업 맨 아랫줄(.dialog-foot)과 같게 맞춰 두 화면이 한 앱처럼 읽히게 한다. */
.card-actions {
  display: flex; gap: 8px;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--line);
}
.card-actions button { flex: 1 1 0; min-width: 0; }
.card-actions button.primary { width: auto; }   /* .primary 의 width:100%(폼 제출용)를 되돌린다 */
