/*
module4_management_ui/static/style.css

관리 화면 스타일. 예전엔 templates/index.html 안에 <style>로 전부 들어있었는데,
파일 하나가 너무 커져서(HTML+CSS+JS 뒤섞임) 기능별로 분리했습니다.
(이 파일 = 화면 생김새, dashboard.js = 화면 동작, index.html = 화면 뼈대)
*/

:root {
  color-scheme: dark;
  --bg: #0d1117;
  --panel: #161b22;
  --border: #30363d;
  --text: #e6edf3;
  --muted: #8b949e;
  /* 2026-07-09 변경 (Todd님 요청 - 관리 화면 색상을 gucollisioncenter.com 톤앤매너에
     맞춤): 기존 파란색(#58a6ff)에서 회사 홈페이지의 시그니처 레드(공식 사이트에서
     실측한 브랜드 레드는 rgb(182,24,44) ≈ #b6182c)로 바꿨습니다. 다만 그 값 그대로
     쓰면 이 어두운 배경(#0d1117) 위에서 텍스트/링크로 쓰기엔 대비가 약해서, 같은
     레드 계열을 유지하면서 살짝 밝힌 값(#e5484d)을 골랐습니다 - 버튼/뱃지처럼 면적이
     넓은 곳은 밝기 대비가 충분하고, 링크처럼 작은 글자로 쓰여도 또렷하게 읽힙니다.
     레이아웃/기능은 그대로 두고 색상 값만 바꾼 것입니다. */
  --accent: #e5484d;
  --danger: #f85149;
  --ok: #3fb950;
  --warn: #ffd60a;
  /* 2026-07-09 추가 (Todd님 요청): 저장/편집/삭제 버튼이 다 비슷한 색이라 헷갈린다는
     제보 반영 - 저장은 초록(--ok), 편집은 파란색(--edit, 예전에 쓰던 --accent 파란색을
     그대로 재활용), 삭제는 기존 빨강(--danger)을 그대로 써서 세 가지가 뚜렷하게
     구분되도록 했습니다. */
  --edit: #58a6ff;
  /* 소스별 고유색 (각 회사 브랜드 색상에서 착안 - 뱃지만으로 어느 소스인지 한눈에 구분되도록).
     CHP는 공식/확정 정보라 위 --accent(브랜드 레드)를 그대로 씁니다. */
  --tomtom: #ff6a3d;  /* TomTom 로고의 빨강/주황 계열 */
  --here: #00d1c1;    /* HERE 로고의 청록(teal) 계열 */
  --ocfa: #a371f7;    /* OCFA EMS 조기신호 - 기존 레드/오렌지/청록과 겹치지 않는 보라 계열
                          (2026-07-11 추가, EMS_DISPATCH_SOURCE_DESIGN.md 참고) */
}
* { box-sizing: border-box; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  margin: 0; padding: 28px; color: var(--text); background: var(--bg);
  font-size: 16px; line-height: 1.5;
}
h1 { font-size: 24px; margin: 0; }
/* 2026-07-09 9차 추가 (Todd님 제보): 데스크톱에서는 "GU Collision Alert - Admin
   Dashboard"처럼 한 줄로 이어붙고, "- " 구분자는 여기 ::before로만 붙여서 모바일에서
   (아래 미디어쿼리) 줄바꿈될 때는 굳이 필요없는 "-"까지 없앨 수 있게 분리해뒀습니다. */
.h1-sub::before { content: '- '; }
/* 헤더 로고 (2026-07-08 추가): 데스크톱 제목(h1) 폰트 크기(24px)에 맞춘 높이. 모바일에서는
   제목이 16px로 줄어드는 미디어쿼리가 있어서, 로고도 그 안에서 같이 줄입니다
   (em 단위는 img 자신의 font-size를 기준으로 계산돼 h1과 자동으로 맞물리지 않기 때문에,
   각 화면 크기에 맞는 고정 px 값을 직접 지정하는 방식을 택했습니다). */
.header-row { display: flex; align-items: center; gap: 10px; }
.header-logo { height: 28px; width: auto; flex-shrink: 0; }

/* 헤더 전체 묶음(로고+제목 / 회사주소 / 통계+언어전환) (2026-07-08 추가). 기본값(모바일 포함)은
   그냥 위아래로 쌓는 것뿐이라 이전 모습과 동일합니다 - PC 전용 배치는 아래 min-width
   미디어쿼리에서만 따로 정의합니다(요청: "모바일은 손대지 않아도 됨"). */
.dashboard-topbar { display: flex; flex-direction: column; }
/* 2026-07-11 추가 (Todd님 요청: 개인별 로그인 시스템) - 로그인 계정/로그아웃 표시줄.
   회사주소 줄 바로 위, 헤더 안에서 눈에 띄지만 과하지 않게 작은 글씨로 배치합니다. */
.login-status-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 6px 0; font-size: 12.5px; color: var(--muted);
}
.login-status-row strong { color: var(--text); }
.login-status-row form { margin: 0; }
.login-status-row button { padding: 3px 10px; font-size: 12px; }

@media (min-width: 761px) {
  /* PC에서는 [로고+제목] .......... [통계 3개 + 언어전환]을 한 줄에 놓고, 회사주소는
     그 아래 한 줄로 따로 둡니다. grid-template-areas는 DOM 순서와 무관하게 이름으로
     배치하기 때문에, 실제 HTML 순서(제목 -> 주소 -> 통계)를 바꾸지 않고도 시각적으로만
     재배치할 수 있습니다 (모바일 쪽 HTML/CSS를 안 건드려도 되는 이유). */
  .dashboard-topbar {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "title . stats" "address address address";
    align-items: center;
    column-gap: 20px;
  }
  .dashboard-topbar .header-row { grid-area: title; }
  .dashboard-topbar .company-address { grid-area: address; margin-top: 6px; }
  .dashboard-topbar .stat-row { grid-area: stats; margin-top: 0; gap: 8px; }

  /* 옆으로 옮기면서 원래 크기(패딩 14px/18px, 숫자 26px)로는 너무 넓어서, 한 줄에
     제목과 나란히 들어갈 만큼 축소합니다. */
  .dashboard-topbar .stat-row .stat { padding: 6px 10px; flex: 0 0 auto; }
  .dashboard-topbar .stat-row .stat .num { font-size: 16px; }
  .dashboard-topbar .stat-row .stat .label { font-size: 10px; white-space: nowrap; }
  .dashboard-topbar .stat-row .lang-switcher { padding: 4px 8px; }
  .dashboard-topbar .stat-row .lang-switcher .globe { font-size: 14px; }
  .dashboard-topbar .stat-row .lang-switcher select { font-size: 12px; padding: 3px 4px; }

  /* 2026-07-10 추가: CCTV 반경 선택창도 언어선택창과 동일한 PC 축소 규격을 따릅니다. */
  .dashboard-topbar .stat-row .cctv-radius-switcher { padding: 3px 8px; height: 20px; }
  .dashboard-topbar .stat-row .cctv-radius-switcher .radius-icon { font-size: 12px; }
  .dashboard-topbar .stat-row .cctv-radius-switcher select { font-size: 11px; padding: 1px 3px; }
}
/* 맨 아래 회사 정보 푸터 (2026-07-08 추가). */
.site-footer { margin-top: 32px; padding: 20px 0 4px; border-top: 1px solid var(--border);
               display: flex; align-items: center; justify-content: center; gap: 14px; }
.footer-logo { height: 36px; width: auto; }
.footer-brand { font-weight: 700; font-size: 13px; letter-spacing: 0.1em; color: var(--text); }
.footer-copyright { font-size: 11px; color: var(--muted); margin-top: 3px; }
/* 2026-07-10 추가 (Todd님 요청: "version 1.2 라고 관리자 페이지 맨하단 로고 옆에
   적당한데 붙여줘") - 브랜드명 옆에 작은 배지로 붙였습니다. 굵은 브랜드 글자와
   구분되게 일반 굵기 + muted 색 + 옅은 배경 박스로 톤을 낮췄습니다. */
.footer-version {
  font-weight: 400; font-size: 10px; letter-spacing: normal; color: var(--muted);
  background: rgba(255,255,255,0.06); border: 1px solid var(--border);
  border-radius: 4px; padding: 1px 5px; margin-left: 4px; vertical-align: middle;
}
h3 { font-size: 17px; margin-top: 0; }
p.hint { font-size: 14px; color: var(--muted); }
p.company-address { margin: 4px 0 0 0; }
/* 2026-07-13: 수신자관리 박스 안 회사주소 사본은 모바일 전용이었음.
   2026-07-15 (Todd님 요청): PC도 모바일과 동일하게 - 상단 헤더의 회사주소는 모든
   화면에서 숨기고, Recipient Management 박스 맨 위(제목 아래) 사본만 보여줍니다. */
.dashboard-topbar .company-address { display: none; }
#recipientsPanel .company-address-recipient { display: block; margin: 2px 0 8px 0; }
a { color: var(--accent); }
.lang-switcher { display: flex; align-items: center; justify-content: center; gap: 6px; background: var(--panel);
                 border: 1px solid var(--border); border-radius: 8px; padding: 6px 10px; flex-shrink: 0; }
.lang-switcher .globe { font-size: 18px; }
.lang-switcher select { margin: 0; padding: 4px 6px; font-size: 14px; }

/* 2026-07-10 추가 (Todd님 요청: CCTV 매칭 반경 선택창을 언어선택창 밑에) - 언어선택창과
   새 반경 선택창을 세로로 쌓는 열. 옆 통계 박스(.stat)와 나란히 놓일 때 자연스럽게
   보이도록 두 항목을 같은 폭/높이 체계로 맞췄습니다. */
.lang-radius-col { display: flex; flex-direction: column; gap: 5px; flex-shrink: 0; }
.lang-radius-col .lang-switcher, .lang-radius-col .cctv-radius-switcher {
  padding: 4px 10px; height: 26px; box-sizing: border-box;
}
.cctv-radius-switcher {
  display: flex; align-items: center; justify-content: center; gap: 6px; background: var(--panel);
  border: 1px solid var(--border); border-radius: 8px; flex-shrink: 0;
}
.cctv-radius-switcher .radius-icon {
  font-size: 11px; font-weight: 700; letter-spacing: 0.03em; color: var(--muted);
}
.cctv-radius-switcher select { margin: 0; padding: 2px 4px; font-size: 12px; }

/* 3단 레이아웃: [압축 사고 목록] [지도] [상세정보 패널] */
.layout { display: flex; gap: 16px; margin-top: 16px; align-items: flex-start; }

/* 2026-07-08: 600px -> 860px로 늘렸다가, 맥북 에어 M2 13인치 기준으로는 오히려 전체
   페이지를 스크롤해야 하는 문제가 생겨서 다시 600px로 되돌렸습니다 (배너카드 약 10개
   기준). 상세패널의 CCTV를 스크롤 없이 다 보이게 하는 문제는 높이를 늘리는 대신
   상세패널 "내부" 배치를 바꿔서 해결했습니다 (CCTV를 지도 위로, 보조 카메라를 CCTV
   오른쪽으로 - 아래 .cctv-main-row 관련 CSS 참고). */
/* 2026-07-13 (Todd님 요청): 카드 텍스트가 더 보이게 사이드바 가로를 300→380px(약 27%↑).
   사이드바는 고정폭이고 지도/상세는 flex라, 넓힌 만큼 지도(flex:5) 영역이 자동으로 줄어듭니다.
   모바일은 아래 @media에서 width:100%로 덮으므로 영향 없음(Todd님: 모바일 미적용). */
.sidebar { width: 380px; flex-shrink: 0; background: var(--panel); border: 1px solid var(--border);
           border-radius: 10px; max-height: 600px; overflow-y: auto; order: 1; }
/* 2026-07-14 (Todd님 요청): position:relative는 새 알람 점(.unread-dot)을 카드 왼쪽
   가운데에 절대배치하기 위한 기준입니다 (아래 .item.is-unread .unread-dot 참고). */
.sidebar .item { padding: 8px 12px; border-bottom: 1px solid var(--border); font-size: 13px; cursor: pointer;
                 display: flex; align-items: center; gap: 8px; position: relative; }
.sidebar .item:hover { background: rgba(88,166,255,0.08); }
.sidebar .item.notified { border-left: 3px solid var(--ok); }
/* 2026-07-12 (Todd님 요청, 배지 가독성): 근무시간 외 발송 스킵 카드의 흐림을 0.75->0.9로
   완화해 소스 배지/텍스트가 잘 읽히게 합니다. 스킵 구분은 왼쪽 회색 테두리(border-left)와
   "Not sent" 메타 문구로 충분히 유지됩니다. */
.sidebar .item.skipped { border-left: 3px solid var(--muted); opacity: 0.9; }
.sidebar .item.selected { background: rgba(88,166,255,0.16); }
/* item-main이 min-width:0이어야 안의 .addr/.meta 말줄임(ellipsis)이 flex 안에서도
   제대로 작동합니다 (안 주면 flex item의 기본 min-width:auto가 줄어드는 걸 막아버림). */
.sidebar .item-main { min-width: 0; flex: 1 1 auto; }
/* 2026-07-13 (Todd님 요청): 카드 1행은 한 줄 유지(줄바꿈 금지)하되, 공간이 모자라면
   소스 배지 텍스트 자체를 "PULSEPOINT (FIRE..."처럼 말줄임. flex로 배치해서 NEW 뱃지는
   고정, 소스 배지와 주소는 필요할 때만 각자 …로 잘립니다("최대한 문자로 표시"). */
.sidebar .item .addr { display: flex; align-items: center; gap: 5px; font-weight: 600; font-size: 13.5px; white-space: nowrap; overflow: hidden; }
.sidebar .item .addr > .unread-badge,
.sidebar .item .addr > .unread-dot,
.sidebar .item .addr > .badge { flex: 0 0 auto; }
.sidebar .item .addr > .source-badge { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar .item .addr > .addr-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* 2026-07-08 추가 (협업 규칙 15-3/15-4): 카드만 보고도 이 위치에 CCTV가 지원되는지
   바로 알 수 있도록 4:3 썸네일을 항상 넣습니다. 카메라가 없으면 회색 "🚫" 박스로
   명시합니다 (조용히 생략 안 함 - 상세패널과 동일 원칙). 이 썸네일은 "있음/없음"을
   한눈에 보여주는 미리보기 용도라 상세패널의 원본 이미지와 달리 4:3으로 잘라도
   무방합니다(object-fit:cover).*/
.sidebar .item-thumb { width: 52px; aspect-ratio: 4 / 3; border-radius: 6px; overflow: hidden; flex-shrink: 0;
                        background: #0d1117; display: flex; align-items: center; justify-content: center;
                        position: relative; }
.sidebar .item-thumb.has-cam img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sidebar .item-thumb.no-cam { color: var(--muted); font-size: 16px; opacity: 0.5; }

/* 2026-07-08 추가 (Todd님 요청): 카메라가 2개 이상이면 썸네일 우상단에 개수 뱃지 */
.sidebar .item-thumb-count {
  position: absolute; top: 2px; right: 2px; background: rgba(0,0,0,0.75); color: #fff;
  font-size: 9px; font-weight: 700; line-height: 1; padding: 2px 4px; border-radius: 999px;
}

/* 안 읽음 표시 (2026-07-08 추가): 이메일 안 읽음처럼, 이 브라우저(기기)에서 아직 클릭해서
   열어보지 않은 카드 앞에 표시합니다. 카드 클릭(selectAccident) 시 즉시 사라집니다.
   2026-07-14 (Todd님 요청): PC의 "NEW" 글자 뱃지를 없애고, 모바일 스타일처럼 모든
   화면에서 "카드 왼쪽 가운데 빨간 점"으로 통일합니다. 점은 .addr 안 인라인 배치가
   아니라 카드(.sidebar .item, position:relative) 기준 절대배치라 카드 높이의 수직
   중앙에 옵니다. NEW 글자 뱃지 마크업(.unread-badge)은 롤백 대비로 남겨두되 항상
   숨김. 소스별 점 색상(OCFA 파랑/Waze 연두/PulsePoint 노랑)은 기존 그대로 유지. */
.unread-badge, .unread-dot { display: none; }
.item.is-unread .unread-badge { display: none; }
.item.is-unread .unread-dot {
  display: block; position: absolute; left: 3px; top: 50%; transform: translateY(-50%);
  width: 7px; height: 7px; border-radius: 50%; background: var(--danger); margin-right: 0;
}
/* 2026-07-11 추가 (Todd님 요청): OCFA EMS 소스의 "NEW" 뱃지/점만 다른 소스(빨강)와
   구분되게 파란색으로 표시. 다른 소스(CHP/TomTom/HERE)의 NEW 표시는 기존 빨강 그대로. */
.item.is-unread .unread-badge.source-new-ocfa { background: var(--edit); }
.item.is-unread .unread-dot.source-new-ocfa { background: var(--edit); }
/* 2026-07-12 추가 (Todd님 요청): Waze 소스의 "NEW" 뱃지/점은 눈에 확 띄는 연두색으로.
   연두 배경엔 흰 글자 대비가 약해서 글자는 어두운색으로 둡니다. */
.item.is-unread .unread-badge.source-new-waze { background: #7ee787; color: #0a0a0a; }
.item.is-unread .unread-dot.source-new-waze { background: #7ee787; }
/* 2026-07-13 (Todd님 요청): PulsePoint 신규 NEW 뱃지는 노랑(정밀 소스 강조). 어두운 글자로 대비. */
.item.is-unread .unread-badge.source-new-pulsepoint { background: #ffd33d; color: #0a0a0a; }
.item.is-unread .unread-dot.source-new-pulsepoint { background: #ffd33d; }
.sidebar .item .meta { color: var(--muted); font-size: 11.5px; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 카드는 매일 날짜별로 리셋되기 때문에 담당자에게는 "몇 시"가 "며칠"보다 중요한 정보입니다.
   그래서 날짜는 주소(.addr)와 같은 밝은 텍스트색으로, "LA" 표기는 나머지 메타 정보와 같은
   일반(회색) 텍스트로 두고, 시:분만 아래 .time-highlight로 노란색 강조합니다. */
.sidebar .item .meta .date-text { color: var(--text); }
/* 사고 접수시간은 카드/상세정보 패널 어디에 있든 항상 노란색으로 강조 표시합니다
   (담당자가 가장 먼저 확인해야 하는 정보라서. 예전엔 .sidebar 안에서만 적용돼서
   상세정보 패널에서는 회색으로 보였습니다). */
.time-highlight { color: var(--warn); font-weight: 700; }

/* 지도: 340px -> 520px -> 600px (2026-07-08: 헤더를 한 줄로 압축하면서 생긴 여유 공간을
   지도/사이드바/상세패널 높이에 그대로 더했습니다). 가로는 flex-grow가 남는 공간을
   다 쓰다가 오른쪽에 상세정보 패널(.detail-panel)이 새로 생기면서 자연히 좁아집니다.
   2026-07-08: PC 전용으로 지도:상세패널 비율을 11:9로 조정합니다(아래 min-width:761px
   미디어 쿼리 참고) - 모바일은 .layout이 세로(column) 방향으로 바뀌기 때문에 flex-basis를
   여기서 건드리면 모바일의 지도 높이(260px)까지 같이 틀어져서, 데스크톱 쿼리 안에서만
   덮어씁니다. 기본값(1 1 auto)은 그대로 둬서 모바일에 영향이 없습니다. */
/* 2026-07-31: 지도 위에 범례를 겹치기 위해 래퍼를 하나 둡니다. flex 속성은
   기존 #map이 갖고 있던 것을 그대로 옮겨받습니다(레이아웃 변화 없음). */
#mapWrap { flex: 1 1 auto; min-width: 0; order: 2; position: relative; }
/* 2026-07-31: 지도/상세패널을 760px로 키웠다가 Todd님 재요청으로 되돌림 -
   "지도를 키우니 그 아래 수신자 목록 등이 화면에서 다 사라져 안 보인다"는
   피드백. 대신 .detail-panel 안쪽 내용(CCTV 썸네일/지도 임베드)을 압축해서
   원래 600px 그대로 배차 행까지 스크롤 없이 보이게 하는 쪽으로 방향을
   바꿨습니다 (아래 .detail-panel/.cctv-wrap/.map-embed-wrap 참고). */
#map { width: 100%; height: 600px; border-radius: 10px; border: 1px solid var(--border); }
/* OpenStreetMap tiles are light by default; invert colors to match the dark theme. */
#map .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) brightness(0.9) contrast(0.9); }
#map .leaflet-marker-icon, #map .leaflet-popup { filter: invert(1) hue-rotate(180deg); }

/* 2026-07-11 추가 (Todd님 제보: "사고 알람을 선택하면 핀마크 위에 말풍선이 뜨는데
   말풍선 뒤에 있는 카메라, 다른 핀마크는 전혀 확인이 안되고 있어") - 사고 팝업이
   불투명한 흰 배경(위 invert 필터를 거쳐 화면엔 불투명한 검은색으로 보임)이라
   바로 뒤에 있는 카메라 마커/다른 사고 핀을 완전히 가렸습니다. 배경만 반투명하게
   낮추고(투명도 28%만큼 지도가 비쳐 보임), 텍스트가 흐려지지 않도록 얇은 그림자를
   더해 가독성을 보강했습니다. 배경색 자체를 바꾸는 게 아니라 알파(투명도)만
   낮추는 것이라, 위의 invert(1) 필터를 그대로 통과해도 여전히 "어두운 반투명
   말풍선 + 흰 텍스트" 톤앤매너가 유지됩니다(알파값은 invert 필터의 영향을 받지
   않음 - RGB 채널만 반전됨).
   2026-07-11 2차 수정 (Todd님 피드백: "말풍선에 투명도를 조금더 줘도 좋을것같아") -
   배경 불투명도를 0.72 -> 0.5로 더 낮춰서(투명도 50%) 뒤 마커/핀이 더 선명하게
   비쳐 보이게 했습니다. 배경이 더 옅어진 만큼 텍스트 그림자도 살짝 진하고
   넓게(3px -> 4px blur, 0.8 -> 0.9 알파) 키워서 가독성이 떨어지지 않게 보강했습니다. */
#map .leaflet-popup-content-wrapper {
  background: rgba(255, 255, 255, 0.5) !important;
  /* 반전 후 어두운 그림자가 되도록 흰색 그림자를 지정합니다(위 참고 주석과 같은 이유). */
  box-shadow: 0 3px 14px rgba(255, 255, 255, 0.5);
}
#map .leaflet-popup-tip {
  background: rgba(255, 255, 255, 0.5) !important;
}
/* 주의: 이 팝업 전체가 위쪽 규칙(#map .leaflet-popup)에서 filter: invert(1)을
   그대로 받으므로, 여기 적는 색상은 전부 "반전되기 전" 값입니다. 반전 후 텍스트
   뒤에 어두운 그림자(가독성 보강)를 만들려면, 반전하면 검은색이 되는 흰색
   그림자를 미리 지정해야 합니다. */
#map .leaflet-popup-content {
  text-shadow: 0 1px 4px rgba(255, 255, 255, 0.9), 0 0 6px rgba(255, 255, 255, 0.6);
}

/* 2026-07-12 (Todd님 요청): 팝업 하단의 위치 불확실 경고("고속도로 핀-진입 전 확인",
   "도시 단위 근사")는 러너에게 가장 중요한 가이드인데, 팝업이 반투명이라 지도 위에서
   잘 안 보였습니다. 팝업 전체에 걸린 invert(1) hue-rotate(180) 필터를 이 요소에만 한 번
   더 걸어 상쇄하면(이중 반전=원본), 아래 색이 화면에 "지정값 그대로" 나옵니다. 그래서
   밝은 색 + 어두운 반투명 박스 + 굵게 + 그림자로 어떤 배경에서도 확실히 읽히게 합니다. */
#map .leaflet-popup-content .popup-uncertainty {
  display: block; margin-top: 4px; padding: 3px 7px; border-radius: 5px;
  font-weight: 800; font-size: 12px; line-height: 1.35;
  filter: invert(1) hue-rotate(180deg);      /* 부모 팝업 필터 상쇄 → 색이 실제 지정값 그대로 */
  background: rgba(0, 0, 0, 0.75);            /* 실제로 어두운 반투명 박스 (대비 확보) */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9); /* 어떤 배경에서도 또렷하게 */
}
#map .leaflet-popup-content .popup-uncertainty-highway_approx { color: #ffd83d; }  /* 밝은 앰버(노랑) */
#map .leaflet-popup-content .popup-uncertainty-city { color: #d6b4ff; }            /* 밝은 라벤더(보라) */

/* 사고 핀 (2026-07-08 변경): 예전엔 Leaflet 기본 파란 마커 이미지 + CSS 필터로 선택
   상태만 색을 바꿨는데, 실증 섹터 기능에서 "이 사고가 어느 섹터(러너)에 속하는지"를
   핀 색으로 바로 보여줘야 해서 순수 CSS 원 모양(회사 핀과 같은 방식)으로 바꿨습니다.
   위의 다크모드 필터(invert+hue-rotate)를 여기서도 꺼서, 섹터 색이 반전 없이 실제
   고른 색 그대로 보이게 합니다. */
#map .leaflet-marker-icon.accident-marker-icon {
  filter: none !important;
  background: transparent;
  border: none;
  /* 2026-07-08 실사결과 배지 작업때 배지(.accident-pin-badge)의 position:absolute
     기준점을 만들려고 여기에 position:relative를 넣었었는데, 이게 바로 "바다 위
     핀"/"핀 체인" 버그의 진짜 원인이었습니다: Leaflet은 .leaflet-marker-icon을
     position:absolute + transform:translate3d(x,y)로 지도 위 (0,0) 기준 절대좌표에
     배치하는데, position:relative로 덮어써버리면 각 마커 <div>가 markerPane 안에서
     "보통 문서 흐름(normal flow)"에 따라 세로로 차곡차곡 쌓인 위치를 기준점으로 삼고
     그 위에 transform이 더해집니다. 그래서 마커가 새로 추가될 때마다(=사고 알람이
     쌓일 때마다) 그 앞에 쌓인 마커 개수만큼 세로로 점점 더 밀려 내려가서 바다 쪽으로
     흘러가는 것처럼 보였던 것입니다(좌표/지오코딩 문제가 전혀 아니었음). 배지 위치
     기준점은 안쪽 .accident-pin으로 옮기고, 여기는 Leaflet 기본값인 absolute를
     그대로 씁니다. */
}
/* 2026-07-30 (Todd님 요청): 경로 출발지 선택 + 러너 배차 패널 */
.route-origin-row { display: flex; align-items: center; gap: 6px; margin: 2px 0 4px; }
.route-origin-label { font-size: 11px; color: var(--muted); }
#routeOriginSelect, .dispatch-row select, .dispatch-row input {
  background: #161b22; color: #e6edf3; border: 1px solid #30363d;
  border-radius: 6px; font-size: 11px; padding: 2px 6px; font-family: inherit;
}
.dispatch-row {
  display: flex; align-items: center; gap: 6px; margin-top: 6px; flex-wrap: wrap;
}
.dispatch-row input { flex: 1; min-width: 120px; }
.dispatch-row button {
  background: #1f6feb; color: #fff; border: none; border-radius: 6px;
  font-size: 11px; padding: 4px 10px; cursor: pointer; font-family: inherit;
  font-weight: bold;
}
.dispatch-row button:hover { background: #388bfd; }
.dispatch-status { font-size: 11px; color: #7ee787; }

.accident-pin {
  /* 2026-07-31 (Todd님): 테두리를 빨강으로 통일했더니 시간 구분이 오히려 안 됐음.
     테두리는 어두운 중립색으로 되돌리고, **채움색 5단계**가 시간을 나타냅니다. */
  position: relative;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1.5px solid rgba(13, 17, 23, 0.8);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  display: flex; align-items: center; justify-content: center;
}
/* 선택된 사고의 핀: 흰 링 + 원 안에 붉은 X (2026-07-31 Todd님 요청 -
   "해당 사고 배너나 지도를 클릭하면 핀마크 원형 안에 붉은 X를 나타나게 해줘.
   어딘지 인식하기 편하게"). 채움색은 그대로 두어 경과시간 정보를 잃지 않습니다. */
.accident-marker-icon.selected .accident-pin {
  width: 22px; height: 22px;
  border-width: 2px;
  box-shadow: 0 0 0 3px #fff, 0 0 0 5px #ff3b30, 0 2px 8px rgba(0,0,0,0.75);
}
.accident-pin-x {
  width: 15px; height: 15px;
  stroke: #ff1c1c; stroke-width: 4; stroke-linecap: round;
  fill: none;
  /* 밝은 채움색(노랑/연두) 위에서도 X가 보이도록 흰 그림자를 깔아줍니다 */
  filter: drop-shadow(0 0 1.5px rgba(255,255,255,0.95));
}
/* 실사결과 배지(✅/❌/🕐, 2026-07-08 추가): 핀 오른쪽 위에 작게 겹쳐서, 사이드바를
   따로 열지 않아도 지도만 보고 확인/오탐/종료 여부를 바로 알 수 있게 합니다. */
.accident-pin-badge {
  position: absolute; top: -6px; right: -8px;
  font-size: 11px; line-height: 1;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.6);
}
/* 2026-07-11 제거: 예전엔 여기 사고 핀에 고정된 원형 CCTV 배지(.accident-pin-cctv-badge,
   2026-07-08 추가)가 있었지만, 사고 자기 좌표에만 고정돼 카메라 실제 위치와 무관해서
   혼란을 줬습니다(Todd님 제보 - dashboard.js buildAccidentIcon() 주석 참고). 아래 실제
   위치 마커(.cctv-camera-pin)만 남기고 이 배지는 완전히 제거했습니다. */

/* 실제 CCTV 카메라 위치 마커 (2026-07-10 추가, Todd님 제보 대응: 사고 핀의 📹 배지는
   항상 사고 지점 옆에 1개만 고정 표시돼서 실제 카메라 개수/위치와 무관했던 문제).
   선택된 사고의 카메라마다 실제 좌표에 이 마커가 하나씩 그려지므로, 사고 핀(원형)과
   확실히 구분되도록 사각형 배지 모양을 쓰고, 라이브 스트리밍 카메라는 초록 테두리로
   정지사진 전용 카메라와 구분합니다. */
#map .leaflet-marker-icon.cctv-camera-marker-icon {
  filter: none !important;
  background: transparent;
  border: none;
}
.cctv-camera-pin {
  width: 18px; height: 18px;
  border-radius: 5px;
  background: #fff;
  border: 2px solid #8b949e;
  box-shadow: 0 2px 5px rgba(0,0,0,0.6);
  font-size: 10px;
  display: flex; align-items: center; justify-content: center;
}
.cctv-camera-pin.live {
  border-color: var(--ok, #3fb950);
}
/* 2026-07-10 추가 (Todd님 요청: "해당 카메라 영상을 선택하면 메인지도에 있는 카메라
   테두리 색상이 변하게 해서 어떤 카메라를 선택했는지 알아보게 해줄수 있어?") - 상세
   패널 CCTV 그리드에서 클릭한 카메라만 파란 테두리 링으로 강조합니다(사고 핀의
   선택 링과 같은 방식이지만, 라이브 초록 테두리와 안 겹치도록 다른 색을 씀). */
.cctv-camera-pin.selected {
  border-color: #58a6ff;
  box-shadow: 0 0 0 2px #58a6ff, 0 2px 6px rgba(0,0,0,0.7);
}
/* 상세패널 안 CCTV 카드에도 같은 강조를 반영해서, 지도까지 안 봐도 어떤 카메라를
   골랐는지 패널 안에서 바로 확인됩니다. */
.cctv-cell { cursor: pointer; }
.cctv-cell.selected .cctv-wrap {
  outline: 2px solid #58a6ff;
  outline-offset: 2px;
  border-radius: 4px;
}

/* 회사(GU Collision Center) 위치 핀 (2026-07-07 추가).
   위 줄의 다크모드 필터(색 반전 + 색상환 180도 회전)가 모든 마커에 자동 적용되기 때문에,
   이 필터를 이 마커에 한해서 꺼서(filter: none) 색을 그대로(빨간색 그대로) 보이게 합니다
   (반전된 색을 역산해서 넣는 것보다 이 방법이 훨씬 명확하고 실수할 여지가 적습니다).
   또한 Leaflet의 L.divIcon 기본 스타일(흰 배경 + 회색 테두리 박스)이 자동으로 씌워지는데,
   이것도 함께 꺼서 제가 그린 원 모양만 깔끔하게 보이게 합니다. */
#map .leaflet-marker-icon.company-marker-icon {
  filter: none !important;
  background: transparent;
  border: none;
}
.company-pin {
  width: 20px; height: 20px;
  background: #ff3b30;
  border: 3px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 2px #ff3b30, 0 2px 6px rgba(0,0,0,0.7);
}
/* 2026-07-09 32차 수정 (Todd님 요청: "빨간 원을 GU Collision Center 로고로 바꿔줄
   수 있어?") - 위 .company-pin(순수 원)을 대신해서 메인 지도에 씁니다. 로고
   이미지(/static/logo.png, 92x100 비정사각형)를 흰 원형 배지 안에 여백을 두고
   넣어서 어떤 지도 배경(어두운 위성/일반 지도) 위에서도 로고가 또렷이 보이게
   했고, 기존 빨간 테두리는 그대로 남겨서 "여기가 회사"라는 강조는 유지했습니다.
   2026-07-09 33차 수정 (Todd님 제보: "로고가 너무 크다. 기존 핀마크하고 사이즈를
   비슷하게 해줘") - 34x34 -> 사고 핀과 같은 22x22로 축소, 안쪽 여백(padding)도
   그만큼 줄였습니다. */
.company-logo-pin {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 2px #ff3b30, 0 2px 5px rgba(0,0,0,0.7);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-sizing: border-box;
}
.company-logo-pin img {
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 2px;
  box-sizing: border-box;
}

/* 상세정보 패널: 목록에서 사고를 클릭하면 여기에 자세한 내용 + 경로 미리보기가 표시됩니다.
   2026-07-08: max-height(내용이 짧으면 짧게 끝남) -> height(항상 지도와 같은 600px)로 바꾸고
   세로 방향 flex로 만들어서, 구글맵 경로 미리보기(.map-embed-wrap)가 위 텍스트 내용이
   몇 줄이든 상관없이 남는 공간을 전부 채워 상단 메인 지도의 아래쪽 끝과 정확히
   맞춰지도록 했습니다. */
/* 2026-07-08: 위 #map 주석 참고 - PC에서는 고정폭(340px) 대신 min-width:761px 미디어
   쿼리에서 flex-grow 비율로 덮어씁니다. 여기 기본값은 모바일도 함께 쓰는 값이라 원래
   고정폭 그대로 둡니다(모바일은 width:100%로 다시 덮어써서 실제로는 안 쓰이지만, 혹시
   모를 다른 화면 크기에서도 안전하게 폭이 있도록). */
/* 2026-07-31 1차 수정: route-origin-row/dispatch-row가 추가되며 기존 600px 안에
   다 안 들어가 배차 행이 스크롤 없이는 안 보이던 문제 - 처음엔 패널 높이 자체를
   760px로 키웠는데, Todd님이 "지도가 너무 커져서 그 아래 수신자 목록 등이 화면에서
   안 보인다"고 재요청하셔서 되돌림. 대신 패널 높이는 원래 600px 그대로 두고,
   내부 콘텐츠(CCTV 썸네일 높이, 지도 임베드 최소높이)를 줄여서 여유 공간을
   만드는 쪽으로 수정했습니다 (아래 .cctv-wrap/.map-embed-wrap 참고). */
.detail-panel { width: 340px; flex-shrink: 0; background: var(--panel); border: 1px solid var(--border);
                border-radius: 10px; height: 600px; overflow-y: auto; order: 3; padding: 14px;
                display: flex; flex-direction: column; }

/* 2026-07-08: "지도 60~65% : 상세패널(경로 썸네일 포함) 35~40%" 비율 요청. PC 전용
   미디어 쿼리를 #map/.detail-panel의 기본 규칙보다 "뒤"에 둬서(CSS는 특이도가 같으면
   나중에 나온 규칙이 이깁니다) 확실히 덮어쓰도록 했습니다. (처음 시도했을 때는 이
   미디어 쿼리를 파일 앞쪽 다른 미디어 쿼리 안에 같이 넣었었는데, 그게 오히려 아래
   기본 규칙보다 먼저 나와버려서 일부 속성이 기본 규칙에 덮어써지는 버그가 있었습니다.
   그래서 이번엔 #map/.detail-panel 두 기본 규칙 바로 다음, 그리고 flex 속성을 축약형
   하나로만 통일해서 grow/shrink/basis가 섞여 충돌하지 않게 했습니다.)
   5:3 비율 = 지도 62.5% / 상세패널 37.5%.
   2026-07-31 수정 이력: 이 규칙은 원래 여기 있던 #map을 직접 겨냥했는데, 같은 날
   지도 위에 범례를 겹치기 위해 #mapWrap(#map을 감싸는 새 래퍼)이 추가되면서
   실제 flex 아이템(행의 자식)이 #map에서 #mapWrap으로 바뀌었습니다. 미디어 쿼리
   selector는 여전히 #map을 겨냥한 채로 남아있어서 조용히 죽은 코드가 되었고,
   그 결과 실제로는 #mapWrap의 기본값(1 1 auto)과 .detail-panel(3 1 0)이 부딪혀
   대략 반반(50:50)으로 나오고 있었습니다 - Todd님이 오랫동안 봐오신 "원래" 화면은
   바로 이 50:50입니다. 1차 수정에서 대상을 #mapWrap으로 바로잡으면서 8:3(약
   72.7:27.3)까지 키웠더니, Todd님 기준 "원래(50:50) 대비" 훨씬 더 커져 버려서
   ("너무 줄였다" - 상세패널 텍스트가 여러 줄로 어색하게 줄바꿈됨) 다시 조정
   요청을 받았습니다. 최종적으로 Todd님이 실제로 봐오신 50:50을 기준선으로 놓고
   거기서 요청하신 "10% 정도만" 지도 쪽으로 넓히는 11:9(지도 55% / 상세패널 45%,
   50%의 +10% 상대 확대)로 정했습니다. */
@media (min-width: 761px) {
  #mapWrap { flex: 11 1 0; min-width: 420px; }
  .detail-panel { flex: 9 1 0; width: auto; min-width: 340px; }
}
.detail-panel .empty { flex: 1; display: flex; align-items: center; justify-content: center;
                        color: var(--muted); font-size: 14px; text-align: center; padding: 0 10px; margin: 0; }
/* 2026-07-08: CCTV 이미지(원본 비율 유지)가 잘려 보인다는 피드백을 받아, 지도 크기는
   그대로 두고 텍스트 쪽 폰트/여백을 줄여서 공간을 확보했습니다 (Todd님 요청 - "지도
   사이즈는 줄이지 말고 텍스트 폰트/간격을 줄여도 좋다"). */
.detail-panel .detail-addr { font-size: 14px; font-weight: 700; margin-bottom: 4px; }
.detail-panel .detail-row { font-size: 12px; color: var(--muted); margin-bottom: 2px; }
/* 2026-07-11 추가 (Todd님 요청): 상세정보 패널 맨 위, 소스 배지 옆 빈 공간에 OCFA EMS
   전용 강조 박스(사고유형/유닛수)를 놓기 위한 레이아웃. 배지는 왼쪽 그대로, 박스는
   flex로 오른쪽 끝에 붙습니다. 다른 소스는 highlightHtml 자체가 비어있어서(dashboard.js
   참고) 이 줄이 기존과 완전히 동일하게 보입니다.
   2026-07-12 추가 (Todd님 요청: "CHP/TomTom/HERE도 비슷한 정보 있으면 표시해줘") -
   TomTom(probability+event 설명), HERE(criticality+요약)도 같은 박스 패턴을 재사용하도록
   확장. 소스마다 테두리/배경색만 다르게(.detail-highlight-ocfa/-tomtom/-here) 주고,
   레이아웃/폰트는 공용(.detail-highlight-box)으로 유지합니다. TomTom/HERE의 이벤트
   설명·요약 텍스트는 OCFA의 짧은 유닛수 표기보다 길 수 있어 줄바꿈을 허용하고
   최대 너비를 둡니다(OCFA는 텍스트가 짧아 영향 없음). */
.detail-panel .detail-header-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; flex-wrap: wrap; }
/* 2026-07-13 (Todd님 요청): 박스 안 빈 공간 최소화 + 우측 상단으로 붙이기 + 텍스트가
   길어지면 유연하게 커지기. 세로 스택(코드/유닛/뜻)으로 바꾸고 내용폭에 맞춰(shrink-to-fit)
   차지하며, 부모 .detail-header-row가 space-between이라 자연히 우측 상단에 붙습니다. */
.detail-highlight-box {
  display: inline-flex; flex-direction: column; align-items: flex-start; gap: 1px;
  flex: 0 1 auto; min-width: 0; width: fit-content; max-width: 260px;
  border-radius: 6px; padding: 2px 7px; line-height: 1.25;
  font-size: 11px; font-weight: 600; color: var(--text);
}
/* 2026-07-13 (Todd님 요청): 사고유형 약자(HC 등) 밑에 붙는 뜻 설명. flex-basis:100%로
   같은 박스 안에서 코드 다음 줄로 내려가고, 흐린 작은 글씨로 표시합니다. */
.detail-highlight-gloss {
  color: var(--muted); font-weight: 500; font-size: 10px; white-space: normal;
}
.detail-highlight-ocfa { border: 1px solid var(--ocfa); background: rgba(163, 113, 247, 0.12); }
.detail-highlight-tomtom { border: 1px solid var(--tomtom); background: rgba(255, 106, 61, 0.12); }
.detail-highlight-here { border: 1px solid var(--here); background: rgba(0, 209, 193, 0.12); }
.detail-highlight-waze { border: 1px solid #33ccff; background: rgba(51, 204, 255, 0.12); }
/* PulsePoint 상세 하이라이트(2026-07-13) - 소스 배지와 같은 오렌지 계열 */
.detail-highlight-pulsepoint { border: 1px solid rgba(255,145,20,0.9); background: rgba(255, 145, 20, 0.12); }
.detail-highlight-type { white-space: nowrap; }
.detail-highlight-units { color: var(--muted); font-weight: 500; white-space: normal; word-break: break-word; }

/* 2026-07-12 추가 (Todd님 요청: 무전내용/출동내용 키워드 표시): CHP 상세 디스패치
   로그에서 뽑은 키워드 칩(dashboard.js의 detail_keywords). 견인요청(tow_requested)은
   렉카 비즈니스 관점 최중요 정보라 브랜드 강조색으로 유일하게 눈에 띄게 하고,
   나머지 칩은 무채색 톤으로 은은하게 둡니다. */
.detail-keyword-chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0 2px; }
.detail-keyword-chip {
  display: inline-block; border-radius: 999px; padding: 2px 8px;
  font-size: 11px; font-weight: 600; color: var(--text);
  border: 1px solid var(--border); background: rgba(255, 255, 255, 0.04);
}
.detail-keyword-tow_requested { border-color: var(--accent); background: rgba(229, 72, 77, 0.16); }
/* 2026-07-12 (Todd님 실사 피드백): 위치 성격 단서(진출램프/커넥터/일반도로/본선)는
   러너의 본선 진입 판단에 가장 중요하므로 눈에 띄는 청록 테두리로 강조 + 굵게. */
.detail-keyword-location_hint { border-color: #33d6c0; background: rgba(51, 214, 192, 0.16); font-weight: 700; }
.detail-keyword-fatality { border-color: #8b949e; background: rgba(139, 148, 158, 0.18); }
.detail-keyword-ambulance, .detail-keyword-injuries { border-color: #d29922; background: rgba(210, 153, 34, 0.12); }

/* 2026-07-12 추가 (OCFA 위치정밀도 경고): 상세패널의 "도시 단위 근사치" 경고 줄 */
.detail-approx-notice {
  color: #d29922; font-size: 11.5px; font-weight: 600;
  border: 1px dashed rgba(210, 153, 34, 0.5); border-radius: 6px;
  padding: 3px 8px; margin: 4px 0 2px; display: inline-block;
}

/* 2026-07-12 추가 (실사 지표 확장): Field Verification 패널의 위치오차/리드타임 요약 */
.metric-section-title { margin-top: 14px; }
.metric-row {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 11.5px; padding: 2px 0; color: var(--muted);
}
.metric-row .metric-source { font-weight: 600; color: var(--text); }

/* 디스패치 로그 원문 (접힌 상태 기본, 관제용 참고자료) */
.detail-log-fold { margin: 4px 0 2px; }
.detail-log-fold summary { cursor: pointer; font-size: 11px; color: var(--muted); font-weight: 600; }
.detail-log-pre {
  margin: 4px 0 0; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px;
  background: rgba(0, 0, 0, 0.25); font-size: 10.5px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; max-height: 140px; overflow-y: auto;
  color: var(--muted);
}

.detail-panel .detail-row .value { color: var(--text); }
.detail-panel .detail-actions { margin-top: 6px; margin-bottom: 6px; }
.detail-panel .detail-section-title { margin-top: 8px; font-weight: 600; color: var(--text); font-size: 12.5px; }
/* 2026-07-31 2차 수정: min-height를 180px->130px로 낮춰서, route-origin-row/
   dispatch-row 두 줄이 새로 생긴 만큼 이 지도 임베드가 더 유연하게 줄어들 수
   있게 했습니다(flex:1 1 auto라 원래도 남는 공간만큼 줄지만, 바닥값이 너무
   높으면 그 이상은 못 줄어 패널 전체가 넘쳐서 스크롤이 생겼습니다). */
.detail-panel .map-embed-wrap { margin-top: 6px; border-radius: 8px; overflow: hidden; border: 1px solid var(--border);
                                 flex: 1 1 auto; min-height: 130px; display: flex; }
.detail-panel .map-embed-wrap iframe { width: 100%; height: 100%; border: 0; display: block; }

/* 2026-07-08 추가: 상세패널 하단 구글맵이 계속 잘려 보인다는 재피드백으로 텍스트를
   더 압축했습니다. 주소+시간을 한 줄로(한국시간은 이 패널에서 생략, 사이드바 카드와
   같은 짧은 LA 시간 형식만), 부상/도로유형/발송상태도 한 줄로 합쳤습니다. */
.detail-panel .detail-addr-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 6px; margin-bottom: 4px; }
.detail-panel .detail-addr-text { font-size: 14px; font-weight: 700; }
.detail-panel .detail-addr-row .time-highlight { font-size: 11px; }

/* "Open in Google Maps" 링크를 별도 줄 대신 지도 제목(Route preview) 줄 오른쪽에 배치 */
.detail-panel .detail-title-row { display: flex; justify-content: space-between; align-items: baseline; }
.detail-panel .detail-title-row a { font-size: 11px; font-weight: 600; }
/* 2026-07-09 19차 추가 (Todd님 요청: "open in Google Maps 텍스트 컬러를 블루로") -
   기본 링크색(전역 a { color: var(--accent) }, 브랜드 레드) 대신 이미 있는 --edit
   (#58a6ff, 파란색)로 이 링크만 덮어씁니다. detail-title-row 안이든(지도 임베드 있음)
   detail-actions 폴백(임베드 없음)이든 항상 이 클래스를 쓰므로 두 경우 다 적용됩니다. */
.detail-panel a.route-link { color: var(--edit); }
/* 2026-07-09 15차 추가: 데스크톱에서는 긴 제목(etaTitle)만 보이고, 짧은 제목
   (etaTitleShort)과 가운뎃점 구분자는 모바일 전용이라 기본적으로 숨깁니다
   (@media max-width:760px 안에서 서로 바꿔 보여줌 - 아래쪽 참고). */
.detail-panel .eta-title-short, .detail-panel .eta-route-sep { display: none; }
/* 2026-07-09 24차 수정 (Todd님 요청: "Nearby CCTV (reference only) 텍스트를
   삭제해줘") - 20차 수정에서 여기 있던 .detail-title-row-right/.cctv-title-inline
   (CCTV 제목을 Route 줄 오른쪽에 붙이던 래퍼)은 그 텍스트 자체를 없애면서 더 이상
   쓰지 않아 제거했습니다(PC/모바일 모두 텍스트 없음). */

/* 2026-07-08 추가: 인근 CCTV 사전확인 (협업 규칙 15-3).
   2026-07-08 재배치: 맥북 13인치에서 상세패널을 스크롤 없이 한 번에 보여달라는 요청으로,
   CCTV 블록을 "지도 위"로 옮기고(HTML 순서 변경, dashboard.js 참고), 보조 카메라를
   메인 아래가 아니라 메인 오른쪽에 배치했습니다.
   2026-07-08 재수정: 그런데도 "여전히 스크롤해야 지도가 보인다"는 피드백을 다시
   받았습니다. 원인을 다시 보니, 메인 이미지 높이를 width:100%+height:auto(원본 비율)로
   두면 카메라마다 원본 해상도/비율이 달라서 최종 높이가 매번 들쭉날쭉했고, 비율이
   가로로 넓은 카메라를 만나면 생각보다 훨씬 커져서 지도를 다시 밀어냈습니다. 그래서
   "폭을 줄여서 비율대로 줄어들길 기대"하는 대신, 아예 높이를 고정값(150px)으로 못박고
   `object-fit: contain`을 썼습니다 - contain은 이미지를 자르지 않고(크롭 없음 원칙 유지)
   박스 안에 다 들어가게 비율을 유지하며 줄이기만 해서, 어떤 카메라가 와도 이 블록의
   높이가 항상 150px로 고정됩니다 (레터박스 여백이 생길 수 있지만 잘리는 것보다 낫다는
   판단). 이제 지도(map-embed-wrap)가 쓸 수 있는 공간이 항상 예측 가능하게 확보됩니다. */
/* 2026-07-08 UI/UX 업그레이드(CCTV 노출 확대): 예전엔 메인 카메라 1개를 크게 보여주고
   나머지는 클릭해야 바꿔볼 수 있는 작은 썸네일이었는데, 잡힌 카메라(최대 3대)를 전부
   동시에 같은 크기 그리드 셀로 보여주도록 바꿨습니다. 카메라 대수에 따라
   cctv-grid-1/2/3 중 하나가 붙습니다 (dashboard.js의 renderDetail 참고). */
.detail-panel .cctv-grid { display: grid; gap: 6px; margin-top: 6px; flex: 0 0 auto; }
.detail-panel .cctv-grid-1 { grid-template-columns: 1fr; }
.detail-panel .cctv-grid-2 { grid-template-columns: 1fr 1fr; }
.detail-panel .cctv-grid-3 { grid-template-columns: 1fr 1fr 1fr; }
.detail-panel .cctv-cell { display: flex; flex-direction: column; min-width: 0; }
/* 2026-07-31 2차 수정: 130px->108px로 살짝 축소해서 route-origin-row/dispatch-row
   두 줄이 늘어난 만큼의 공간을 확보 (object-fit:contain이라 화면이 잘리지 않고
   레터박스만 조금 더 생김). */
.detail-panel .cctv-wrap { height: 108px; border-radius: 8px; overflow: hidden;
                            border: 1px solid var(--border); position: relative; background: #000; }
.detail-panel .cctv-wrap img { width: 100%; height: 100%; object-fit: contain; display: block; }
.detail-panel .cctv-caption { margin-top: 4px; font-size: 11px; color: var(--muted); }
.detail-panel .cctv-none { margin-top: 6px; margin-bottom: 4px; font-size: 12px; color: var(--muted); flex: 0 0 auto; }

/* 2026-07-08 추가: 관리화면 라이브 스트리밍 (Todd님 확인 - 관리화면만 적용). 비디오도
   이미지와 마찬가지로 object-fit:contain으로 높이 안에서 크롭 없이 다 보이게 맞춥니다. */
.detail-panel .cctv-wrap video { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
/* 2026-07-09 6차 수정 (아이콘/오류 점검 - Caltrans 카메라 영상 자체에 이미 카메라
   번호/타임스탬프가 좌상단에 박혀 있어서, 우리 "LIVE" 뱃지도 같은 자리(top/left)에
   있으면 두 텍스트가 겹쳐 보였습니다). 겹치지 않게 좌하단으로 옮겼습니다 - 우상단은
   PIP/크게보기 버튼 자리라 그대로 둡니다. */
.detail-panel .cctv-live-badge {
  position: absolute; bottom: 6px; left: 6px; background: rgba(0,0,0,0.65); color: #fff;
  font-size: 10px; font-weight: 700; padding: 2px 6px; border-radius: 999px; letter-spacing: 0.02em;
}

/* 2026-07-09 추가 (Todd님 요청): CCTV 썸네일/영상을 클릭 한 번으로 크게 보는 "⛶" 버튼.
   기존에는 <video controls>의 브라우저 기본 메뉴("...") 안에 전체화면이 숨어있어서
   3단계(메뉴 열기 -> 전체화면 항목 찾기 -> 클릭)를 거쳐야 했습니다. 대신 자체 라이트박스
   (아래 .cctv-lightbox)로 옮겨서 화면 대부분을 채워 보여주는 방식을 씁니다 - iOS
   Safari는 <video> 이외의 엘리먼트(이미지 등)에 표준 Fullscreen API를 지원하지 않아서,
   브라우저 기본 전체화면 API 대신 이 방식을 택하면 모든 기기/브라우저에서 동일하게
   동작합니다(dashboard.js의 openCctvLightbox 참고). */
.cctv-enlarge-btn {
  position: absolute; top: 6px; right: 6px; z-index: 5;
  width: 26px; height: 26px; border-radius: 6px; border: none; cursor: pointer;
  background: rgba(0,0,0,0.6); color: #fff; font-size: 14px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.cctv-enlarge-btn:hover, .cctv-enlarge-btn:focus-visible { background: rgba(0,0,0,0.8); }

/* 2026-07-09 6차 수정 (Todd님 요청: "PIP" 글자보다 아이콘이 낫다) - 이제 SVG 아이콘만
   들어가서 다시 .cctv-enlarge-btn과 같은 정사각형이면 충분하므로, 폭/패딩 오버라이드는
   없앴습니다. 이 클래스는 나중에 PIP 전용 색상 등을 구분하고 싶을 때를 위해 남겨둡니다. */
.cctv-pip-btn svg { display: block; }
/* 2026-07-09 16차 수정 (Todd님 제보: "실시간 영상 스트리밍 썸네일 오른쪽 위에 pip
   확장 아이콘이 안보여") - .cctv-enlarge-btn의 반투명 검은 배경(rgba(0,0,0,0.6)) +
   흰색 아이콘은 밝거나 뿌연 CCTV 화면(예: 헤드라이트, 화재 연기) 위에서는 거의 안
   보였습니다. PIP 버튼만 배경을 더 진하게, 테두리와 아이콘 색을 눈에 띄는 노란색
   (--warn)으로 바꿔서 어떤 영상 위에서도 식별 가능하게 했습니다. */
.cctv-pip-btn {
  background: rgba(0,0,0,0.85); border: 1.5px solid var(--warn); color: var(--warn);
  box-shadow: 0 0 4px rgba(0,0,0,0.9);
}
.cctv-pip-btn:hover, .cctv-pip-btn:focus-visible { background: #000; border-color: #fff; color: #fff; }

/* 2026-07-10 수정 (Todd님 제보: "pip 버튼을 누르면 저게 다른 화면을 다 가려서" -
   기존엔 92vw x 92vh + 검은 반투명 배경으로 화면 대부분을 덮어버려서, 뒤에 있는 사고
   목록/지도가 다 가려지는 문제가 있었습니다. 요청대로 화면을 다 가리는 어두운 배경을
   없애고, 맥북에어 M2 13인치 기준 화면의 1/4 정도(그보다 10-20% 더 작아도 OK) 크기의
   작은 창을 우측 하단에 띄우는 방식으로 바꿨습니다. 배경(.cctv-lightbox 자체)은
   pointer-events:none으로 클릭이 그대로 뒤 페이지에 전달되게 해서, 창이 떠 있는 동안도
   나머지 화면(사고 목록, 지도 등)을 그대로 조작할 수 있습니다 - 대신 "배경 클릭 시 닫기"
   동작은 이제 의미가 없어졌으므로, 닫기는 우측 상단 X 버튼과 ESC 키로만 합니다. */
.cctv-lightbox {
  display: none; position: fixed; inset: 0; z-index: 20000;
  background: transparent; pointer-events: none;
  align-items: flex-end; justify-content: flex-end;
  padding: 0 20px 20px 0;
}
.cctv-lightbox.open { display: flex; }
/* 2026-07-10 수정 (Todd님 제보: "종료 버튼이 없어") - 크기/위치는 이 래퍼
   (.cctv-lightbox-box)가 갖고, 안쪽 .cctv-lightbox-content(영상/이미지, overflow:
   hidden으로 모서리 둥글게 클리핑)와 닫기 버튼을 같이 담습니다. 버튼을 이 래퍼
   기준으로 absolute 배치해야 항상 "실제 보이는 작은 영상 박스"의 우상단에 딱
   붙습니다 - 예전엔 버튼이 #cctvLightbox(화면 전체) 기준으로 배치돼서 화면 구석에
   따로 떨어져 있었고, 심지어 그 부모가 pointer-events:none이라 클릭도 안 먹었습니다.
   래퍼 자체는 overflow:hidden이 아니어서, 버튼이 박스 모서리 밖으로 살짝 튀어나와도
   안 잘립니다. */
.cctv-lightbox-box {
  position: relative;
  pointer-events: auto;
  /* 2026-07-10 재수정 (Todd님 제보: "화면 사이즈가 기대한 것보다 작아. 가로, 세로
     각각 2.5배 만큼 크게 해줘") - 19-26에서 300px/23vw로 줄였던 걸 가로/세로 동일
     비율(16:9 유지)로 2.5배 키움: 300px -> 750px, 23vw -> 57.5vw. */
  width: min(750px, 57.5vw);
  aspect-ratio: 16 / 9;
}
.cctv-lightbox-content {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: #000; border-radius: 10px; overflow: hidden;
  box-shadow: 0 8px 28px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.08);
}
.cctv-lightbox-content video, .cctv-lightbox-content img {
  max-width: 100%; max-height: 100%; width: 100%; height: 100%; object-fit: contain;
}
/* 2026-07-10 재수정 (Todd님 제보: "x 표시는 센터도 안맞고 이질감이 들게 배치되어
   있다. 화면 오른쪽 상단 모서리에, 화면 안에 위치시켜줘. 나머지 버튼과의 배열을
   맞추어서. 동그라미는 없애줘도 좋아") - 모서리 밖으로 튀어나온 원형 버튼(top/right
   -14px, border-radius:50%)을 없애고, 같은 영상 박스 안에서 이미 쓰고 있던
   .cctv-enlarge-btn/.cctv-pip-btn과 완전히 동일한 모양(둥근 사각형, 26x26,
   top/right 6px, 박스 "안쪽"에 배치)으로 통일했습니다 - 아이콘 정중앙 정렬도
   .cctv-enlarge-btn과 같은 flex 중앙정렬이라 자동으로 맞습니다. */
.cctv-lightbox-close {
  position: absolute; top: 6px; right: 6px; z-index: 20001;
  width: 26px; height: 26px; border-radius: 6px; border: none; cursor: pointer;
  background: rgba(0,0,0,0.6); color: #fff; font-size: 15px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.cctv-lightbox-close:hover, .cctv-lightbox-close:focus-visible { background: rgba(0,0,0,0.85); }
/* 모바일에서는 화면 자체가 작으므로 폭 기준 비율을 조금 더 크게 (그래도 전체화면은 아님).
   2026-07-10 재수정: 데스크톱과 동일하게 2.5배 키우되(260px -> 650px), 모바일
   화면 자체보다 커지지 않도록 vw 상한은 92vw로 둠(60vw*2.5=150vw는 화면보다
   커져서 의미가 없으므로 안전하게 조정). */
@media (max-width: 760px) {
  .cctv-lightbox-box { width: min(650px, 92vw); }
}

/* 2026-07-09 5차 수정: "탭하여 실시간 보기"(.cctv-play-overlay) 버튼은 30분 자동정지
   기능 제거와 함께 더 이상 쓰이지 않아 스타일도 정리했습니다. */

/* 2026-07-10 추가 (Todd님 제보: "스테이징 작업을 운영에 올릴 때 섹터 데이터가
   섞이는 걸 봤다") - 스테이징/운영 관리화면이 시각적으로 완전히 동일해서, 어느
   탭이 스테이징인지 URL을 안 보면 구분이 안 되던 게 실제 원인으로 보입니다.
   섹터 편집처럼 데이터를 직접 바꾸는 작업 중에 절대 못 보고 지나칠 수 없도록,
   최상단에 고정 배너를 둡니다. position:sticky라서 스크롤을 내려도 계속 보입니다
   (boot-screen처럼 position:fixed 전체 오버레이가 아니라 문서 흐름 맨 위에
   있는 일반 블록이라, 부팅 화면 z-index(99999)와 충돌할 일도 없습니다 - 부팅
   화면이 먼저 전체를 덮었다가 사라지면 자연스럽게 드러납니다).
   2026-07-10 재수정 (Todd님 제보: "색상이 너무 눈에 띈다, 그레이 계열로, 문구도
   짧게, 상하 폭도 50-60%로 줄여줘") - 처음엔 --warn(노란색) + 굵은 검정 텍스트로
   일부러 강하게 튀게 했었는데, 실사용해보니 과했다는 피드백. 그레이 톤(#4b5158,
   앱의 어두운 배경/패널보다는 밝지만 경고색은 아님)으로 톤을 낮추고, 세로 여백을
   절반(8px -> 4px)으로 줄였습니다. */
/* 2026-07-12 (Todd님 요청): 기존 상단 회색 배너(.staging-banner)를 제거하고, 대신 제목
   옆에 " — STAGING"을 밝은 그레이로 붙여 스테이징을 구분합니다. 데스크톱/모바일 공통
   (제목 h1이 공용이라 자동 적용). 운영에는 is_staging=false라 아예 렌더링되지 않습니다. */
.staging-tag {
  color: #c9d1d9;               /* 밝은 그레이 (제목 초록보다 채도 낮아 "부제"처럼 보임) */
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

table { width: 100%; border-collapse: collapse; margin-top: 12px; background: var(--panel); }
th, td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border); font-size: 15px; }
th { color: var(--muted); font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: 0.03em; }
tr.inactive td { color: var(--muted); }
form.inline { display: inline; margin-right: 4px; }
input, select {
  padding: 7px 8px; margin-right: 4px; background: #0d1117; color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; font-size: 15px;
}
input[type=text] { width: 110px; }
/* 2026-07-08 실증 섹터 기능: 러너별 고유 색상 선택. 텍스트/숫자 입력과 달리
   정사각형에 가까운 스와치 모양이 자연스러워서 폭을 좁고 고정으로 줍니다. */
input[type=color] {
  width: 40px; height: 34px; padding: 2px; cursor: pointer; flex-grow: 0 !important;
}

/* 2026-07-09 11차 추가 (Todd님 요청): 섹터 이름 없이 저장을 누르면 튀어나오던 막는
   alert() 대신, 이름 입력칸 밑에 붙는 말풍선으로 안내합니다(dashboard.js의
   showFieldHint 참고). 위쪽 화살표(::before)가 입력칸을 가리킵니다. */
.field-hint-bubble {
  position: fixed; z-index: 700; max-width: 240px;
  background: var(--accent); color: #fff; font-size: 13px; font-weight: 600;
  line-height: 1.4; padding: 8px 12px; border-radius: 8px;
  box-shadow: 0 8px 20px rgba(0,0,0,0.5);
  animation: field-hint-pop 0.15s ease;
}
.field-hint-bubble::before {
  content: ''; position: absolute; top: -8px; left: 16px;
  border-left: 8px solid transparent; border-right: 8px solid transparent;
  border-bottom: 8px solid var(--accent);
}
@keyframes field-hint-pop {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 2026-07-09 10차 추가 (Todd님 요청: "색상 선택창을 전에 사용했던 창으로") - 기기마다
   다르게 생긴 브라우저 기본 <input type="color"> 팝업 대신, 우리가 직접 만든 12색
   프리셋 스와치 팔레트(dashboard.js의 initColorSwatchPicker 참고). 실제 <input
   type="color">는 화면에서만 숨겨두고 그대로 두므로, 이 스와치는 그 값을 보여주고
   바꾸는 대체 UI일 뿐입니다. */
/* 2026-07-09 22차 수정 (Todd님 제보: "텔레그램 수신자 창에 컬러 선택 버튼만 가로
   배열이 달라") - Add New Recipient의 <form>은 flex가 아니라 그냥 inline 흐름이라
   내부 요소들이 기본값(vertical-align: baseline)으로 줄을 맞춥니다. 색상 버튼은
   글자가 없는 빈 인라인-블록이라 그 "baseline"이 자기 박스의 맨 아래쪽이 되는데,
   반면 옆의 입력칸/버튼들은 글자의 baseline으로 맞춰져서 서로 다른 기준으로 줄을
   맞추다 보니 색상 버튼만 위/아래로 어긋나 보였습니다. vertical-align:middle을
   줘서 다른 입력칸/버튼들과 같은 기준(중간)으로 정렬되게 고쳤습니다. */
.color-swatch-picker { position: relative; display: inline-block; flex-grow: 0; vertical-align: middle; }
.color-swatch-current {
  width: 40px; height: 34px; padding: 0; border-radius: 6px;
  border: 2px solid var(--border); cursor: pointer;
}
.color-swatch-current:hover, .color-swatch-current:focus-visible { border-color: var(--text); }
/* 2026-07-09 12차 수정 (Todd님 제보: "스크롤해도 안 사라지고 화면에 계속 떠 있다") -
   전에는 position:fixed로 뷰포트 좌표를 계산해서 붙였는데, fixed는 스크롤해도 화면 위
   같은 자리에 그대로 남아있는 게 정상 동작이라 스크롤할수록 버튼과 멀어져 보였습니다.
   position:absolute로 되돌리고 .color-swatch-picker(position:relative) 바로 밑에
   둬서, 페이지를 스크롤하면 항상 버튼 바로 밑에 붙어서 같이 움직입니다(실제 top/left는
   dashboard.js의 initColorSwatchPicker가 인라인으로 채움). */
.color-swatch-popover {
  z-index: 600;
  display: flex; flex-direction: column; gap: 6px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.5);
}
/* 2026-07-09 13차 수정 (Todd님 제보: "컬러 버튼을 누르지도 않았는데 처음부터
   떠있으면 안되는거야" - 수신자 관리 화면 전체가 팔레트로 뒤덮여 "다 깨진" 것처럼
   보였던 버그) - 위 .color-swatch-popover 규칙과 브라우저 기본 [hidden] 규칙은
   명시도(specificity)가 똑같아서, 나중에 로드되는 이 클래스 규칙이 항상 이겨
   dashboard.js가 popover.hidden = true로 정확히 숨겨도 화면에는 display:flex로
   계속 펼쳐진 채 보였습니다. [hidden]을 붙인 더 구체적인 규칙을 따로 추가해서
   실제로 숨겨지도록 고쳤습니다. */
.color-swatch-popover[hidden] { display: none; }
.color-swatch-close {
  align-self: flex-end; width: 20px; height: 20px; padding: 0; line-height: 1;
  border: 1px solid var(--border); border-radius: 4px; background: transparent;
  color: var(--muted); cursor: pointer; font-size: 12px;
}
.color-swatch-close:hover, .color-swatch-close:focus-visible { color: var(--text); border-color: var(--text); }
/* 2026-07-09 21차 수정 (Todd님 요청: macOS 색상 선택 창의 "색상 견본" 그리드
   스타일로) - 4열 원형 스와치 12개짜리를 12열 정사각 스와치 120개(기본 12 + 그레이
   12 + 스펙트럼 96)짜리 큰 그리드로 바꿨습니다. 원형 대신 살짝 둥근 사각형으로
   바꿔서 macOS 색상 견본 특유의 느낌을 냈습니다. */
.color-swatch-grid { display: grid; grid-template-columns: repeat(12, 18px); gap: 3px; }
.color-swatch-option {
  width: 18px; height: 18px; padding: 0; border-radius: 3px; cursor: pointer;
  border: 1px solid rgba(255,255,255,0.15);
}
.color-swatch-option:hover, .color-swatch-option:focus-visible { border-color: var(--text); z-index: 1; }
.color-swatch-option.active { border-color: #fff; box-shadow: 0 0 0 2px var(--accent); }
/* macOS 색상 창 맨 아래 "색상 보기..." 버튼과 같은 역할 - 팔레트에 없는 임의의 색을
   고르고 싶을 때 네이티브 <input type="color"> 창을 엽니다. */
.color-swatch-more {
  margin-top: 4px; padding: 5px 8px; font-size: 11px; text-align: center;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg);
  color: var(--muted); cursor: pointer;
}
.color-swatch-more:hover, .color-swatch-more:focus-visible { color: var(--text); border-color: var(--text); }
button {
  /* 2026-07-09 변경: --accent가 파란색에서 브랜드 레드로 바뀌면서, 어두운 글씨(#0d1117)는
     대비가 약해져서 흰 글씨로 바꿨습니다 (배경색만 브랜드에 맞춘 것, 버튼 크기/배치는 동일). */
  padding: 7px 14px; cursor: pointer; background: var(--accent); color: #fff;
  border: none; border-radius: 6px; font-weight: 600; font-size: 14px;
}
button.secondary { background: transparent; color: var(--text); border: 1px solid var(--border); }

/* 2026-07-09 재수정 (Todd님 제보: 저장/편집/삭제 색이 너무 vivid해서 눈에 거슬림):
   배경을 rgba 반투명(50-60%)으로 낮춰서 어두운 무채색 패널 배경에 자연스럽게 녹아들게
   하고, 그 위 글자는 밝은 --text로 바꿔 대비를 유지합니다. 아이콘(💾✏️🗑️)은 이모지
   글리프라 원래 CSS color/opacity에 영향을 안 받는 컬러 글자라서, 여기서 element 전체에
   opacity를 주는 대신 background-color에만 알파를 줬습니다 - 그래서 아이콘은 손대지
   않아도 자동으로 그대로 원색 유지됩니다(요청사항: "아이콘은 투명도 안 줘도 괜찮아"). */
button.danger { background: rgba(248,81,73,0.55); color: var(--text); }
button.danger::before { content: '🗑️ '; }

/* 2026-07-09 3차 추가 (Todd님 요청): Save/Cancel 바로 아래 자기만의 줄에 놓이는
   "전체 지우기" 버튼. flex-basis:100%는 .sector-editor(display:flex; flex-wrap:wrap)
   안에서 항상 새 줄로 강제 줄바꿈시키는 흔한 트릭입니다 - 남는 공간과 무관하게 항상
   Save/Cancel 다음 줄에 옵니다. 실제 삭제(danger, 빨강)와 헷갈리지 않도록 경고색
   (--warn, 주황)을 써서 "되돌릴 수 있는 초기화"라는 느낌을 줍니다. */
.sector-clear-all-btn {
  flex-basis: 100%; margin-top: 2px;
  background: transparent; color: var(--warn); border: 1px solid var(--warn);
}
.sector-clear-all-btn:hover, .sector-clear-all-btn:focus-visible { background: rgba(210,153,34,0.12); }

/* "저장" 버튼은 data-i18n="saveBtn"으로 다 통일되어 있어서, HTML 하나하나 고치지 않고
   이 속성 하나로 전부 잡아서 초록색 + 디스켓 아이콘을 입힙니다 (섹터 편집기의 지도 위
   플로팅 저장 버튼은 이미 디스크 아이콘이 있어서 색상만 .sector-save-control 쪽에서
   따로 맞춥니다). */
button[data-i18n="saveBtn"] {
  background: rgba(63,185,80,0.55); color: var(--text);
}
button[data-i18n="saveBtn"]::before { content: '💾 '; }

/* 섹터 목록의 "Edit" 버튼 전용 색상(dashboard.js renderSectorList 참고) - 예전에 전체
   강조색으로 쓰던 파란색(--edit)을 재활용해서, 저장(초록)/삭제(빨강)와 뚜렷하게
   구분되는 세 번째 색을 확보했습니다. */
button.edit-btn {
  background: rgba(88,166,255,0.55); color: var(--text); border-color: rgba(88,166,255,0.55);
}
button.edit-btn::before { content: '✏️ '; }

/* 실사결과 모바일 카드의 스와이프 삭제 배경 문구에도 동일하게 아이콘을 붙입니다
   (dashboard.js가 아니라 index.html의 .v-card-swipe-bg, 버튼은 아니지만 같은 "삭제"
   의미라 아이콘 규칙을 맞춥니다). */
.v-card-swipe-bg::before { content: '🗑️ '; }

/* 2026-07-10 추가 (Todd님 요청): "Add New Recipient"의 Add 버튼은 하늘색, "+ Draw New
   Sector" 버튼은 오렌지색으로 - 둘 다 투명도 20%만 줘서(저장/편집/삭제의 50-60%보다 훨씬
   더 옅게) 배경에 거의 묻히듯 아주 은은하게만 보이게 합니다. 알파가 낮아서 배경만으로는
   버튼 경계가 잘 안 보일 수 있어 테두리(border)만 조금 더 진하게 잡아 클릭 가능한
   영역임을 알아볼 수 있게 했습니다. 하늘색은 --edit(88,166,255)를 재사용하고, 오렌지는
   지도의 사고 클러스터 마커에 이미 쓰는 오렌지(255,159,10)를 재사용해 팔레트를 통일했습니다. */
button[data-i18n="addBtn"] {
  background: rgba(88,166,255,0.2); color: var(--text); border: 1px solid rgba(88,166,255,0.5);
}
#drawSectorBtn {
  background: rgba(255,159,10,0.2); color: var(--text); border: 1px solid rgba(255,159,10,0.5);
}
.panel { margin-top: 24px; padding: 18px; background: var(--panel); border: 1px solid var(--border);
         border-radius: 10px; }

/* 2026-07-09 추가 (Todd님 요청): 자주 안 쓰는 설정(Twilio 등)은 <details>/<summary>로
   접어두고, 제목을 누르면 펼쳐지게 합니다. summary 안의 h3 마진을 없애서 이 안에서만
   기본 h3 스타일이 살짝 달라 보이지 않게 맞춥니다. */
details.panel summary { cursor: pointer; list-style: none; }
details.panel summary::-webkit-details-marker { display: none; }
/* 2026-07-09 8차 수정 (Todd님 제보): "Field Verification Results"만 소스 HTML에서
   <summary>와 <h3> 사이에 줄바꿈 들여쓰기 공백이 있었는데, 그 공백이 렌더링될 때
   빈 칸 하나로 남아서 다른 패널(Twilio/Recipient Management/Add New Recipient)보다
   제목이 살짝 더 오른쪽에서 시작했습니다. 그 우연한 공백에 기대는 대신, 화살표
   글자와 제목 사이에 폭을 명시적으로 지정해서 네 패널 모두 정확히 같은 간격을
   갖도록 통일했습니다(Field Verification Results의 기존 모습을 기준으로 폭을 맞춤). */
details.panel summary h3 { display: inline; margin-left: 4px; }
details.panel summary::before {
  content: '▸'; display: inline-block; margin-right: 8px; color: var(--muted);
  transition: transform 0.15s ease;
}
details.panel[open] summary::before { transform: rotate(90deg); }

/* 2026-07-09 추가 (Todd님 요청): 알림시간 설정을 PC에서는 우측 상단 언어 선택 옆의
   작은 배지로 압축하고(기본은 시:분만 보이게 접힌 상태), 클릭하면 팝오버 형태로
   펼쳐지게 합니다. dashboard.js의 relocateAlertHoursPanel()이 화면 크기에 따라
   이 패널을 #alertHoursDesktopSlot ↔ #alertHoursMobileSlot 사이에서 옮깁니다 -
   모바일에서는 기존과 똑같이 평범한(접기 기능 없는) 패널로 보이도록 아래에서
   따로 무효화합니다. */
/* 2026-07-10 세 번째 수정 (Todd님 요청, 이전 두 번의 시도가 다 잘못됐던 부분):
   (1) 배지 높이를 언어선택 박스와 "확실히" 같게 하려고, 계산에 맡기지 않고 둘 다 동일한
   고정 height(32px)를 직접 줬습니다.
   (2) 펼쳤을 때 배지 자체가 다른 자리로 옮겨가 버리던 근본 원인: <details> 전체(summary
   포함)를 [open] 상태에서 position:absolute로 바꿔버렸던 것 - 그러면 summary까지
   같이 새 위치로 끌려가서, 배지가 원래 있던 자리에서 사라지고 다른 곳에 다시 나타나는
   것처럼 보였습니다. 이제는 <details> 자체는 항상(열려있든 닫혀있든) 그냥 보통 흐름
   안에 그대로 있고, summary 밑의 내용만 별도 <div class="alert-hours-body">로 감싸서
   그 div만 [open]일 때 position:absolute로 띄웁니다 - 그래서 배지(summary)는 절대
   움직이지 않고, 그 밑으로만 내용이 펼쳐집니다. */
#alertHoursDesktopSlot { position: relative; flex-shrink: 0; }
#alertHoursDesktopSlot .alert-hours-panel {
  position: relative; margin: 0; padding: 0; background: transparent; border: none; border-radius: 0;
}
#alertHoursDesktopSlot .alert-hours-panel summary {
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  padding: 0 10px; white-space: nowrap; display: flex; align-items: center;
  height: 32px; box-sizing: border-box;
}
#alertHoursDesktopSlot .alert-hours-panel summary h3 { font-size: 12px; font-weight: 600; margin: 0; }
/* 배지(summary)는 항상 이 자리에 고정. 펼쳤을 때 내용(.alert-hours-body)만 배지 바로
   아래(top:100%+6px)에 절대위치로 뜨고, 배지 자신은 전혀 움직이지 않습니다.
   2026-07-09 18차 수정 (Todd님 제보: "펼쳐진 박스를 위 박스와 같은 가로 사이즈로") -
   예전엔 width를 300px로 고정했는데, 이 값이 배지(summary)의 실제(텍스트 기준) 폭과
   무관한 임의의 숫자라서 펼쳤을 때 두 박스 가로 폭이 서로 안 맞았고, 그 폭 안에 Start/
   End 두 select와 Save 버튼이 다 안 들어가서 어색하게 줄바꿈됐습니다(예: End의 select만
   다음 줄로 넘어감). width를 고정값 대신 내용에 맞게 늘어나는 max-content로 바꾸고,
   dashboard.js의 relocateAlertHoursPanel()이 펼칠 때마다 summary의 실제 폭을
   min-width로 넣어줍니다 - 그래서 이 박스는 "summary와 같은 폭 이상"이면서 동시에
   "Start/End/Save가 한 줄에 다 들어가는 폭 이상"이 항상 보장됩니다. 화면이 좁을 때
   지도를 가리지 않도록 최대 폭은 90vw로 제한합니다. */
#alertHoursDesktopSlot .alert-hours-panel .alert-hours-body {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 500;
  width: max-content; max-width: 90vw;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  padding: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); box-sizing: border-box;
}
#alertHoursDesktopSlot .alert-hours-panel .alert-hours-body .hint { max-width: 300px; }
#alertHoursDesktopSlot .alert-hours-panel .alert-hours-body form {
  display: flex; align-items: center; flex-wrap: nowrap; gap: 6px; white-space: nowrap;
}
/* 2026-07-11 추가 (Todd님 요청: "컴퓨터 알람시간 세팅 창에서 (이모티콘) save 라고
   되어있잖아. 여기서 save 글자를 없애도 될것 같아") - 모바일(#alertHoursMobileSlot,
   아래 @media 블록 참고)에서는 이미 같은 이유(좁은 공간에 Start/End/SAT/SUN/Save가
   한 줄에 다 들어가야 함)로 아이콘만 남기는 처리를 해뒀는데, PC 팝오버도 SAT/SUN
   버튼이 추가되며 한 줄에 넣을 내용이 늘어서 동일하게 적용합니다. */
#alertHoursDesktopSlot .alert-hours-panel .alert-hours-body button[data-i18n="saveBtn"] {
  font-size: 0; padding: 6px 8px; min-width: 32px; flex-shrink: 0;
}
#alertHoursDesktopSlot .alert-hours-panel .alert-hours-body button[data-i18n="saveBtn"]::before {
  font-size: 16px; margin-right: 0;
}
.alert-hours-summary-time { color: var(--muted); font-size: 12px; font-weight: 400; }

/* 2026-07-11 추가 (Todd님 요청: "토요일/일요일은 근무하지 않는 날이라서 이 날은
   기본적으로 문자가 안 오게 하는 걸 기본으로 세팅해야 하는데... SAT/SUN 버튼을
   각각 클릭하면 그날도 알람 문자를 받을 수 있도록") - 꺼짐(기본)은 .sector-mode-tab
   과 같은 톤(테두리만 있는 옅은 버튼), 켜짐은 .badge.on과 같은 초록색 배경/글자로
   맞춰서 다른 On/Off 표시들과 색상 언어를 통일했습니다. PC(#alertHoursDesktopSlot)/
   모바일(#alertHoursMobileSlot) 양쪽 다 같은 .alert-hours-body 안에 있는 같은
   DOM 노드라 별도 반응형 스타일 없이 공통으로 적용됩니다. */
.alert-day-toggle-group { display: flex; gap: 8px; margin: 2px 0 10px 0; }
.alert-day-toggle {
  background: transparent; color: var(--muted); border: 1px solid var(--border);
  border-radius: 6px; padding: 4px 14px; font-size: 12px; font-weight: 700;
  letter-spacing: 0.03em; cursor: pointer;
}
.alert-day-toggle.on { background: rgba(63,185,80,0.15); color: var(--ok); border-color: var(--ok); }

/* 2026-07-11 추가 (Todd님 요청: 수신자별 경로 출발지 - 회사/본인지정위치/본인현재위치
   3버튼 토글). .alert-day-toggle과 같은 톤(꺼짐=테두리만, 켜짐=초록)으로 통일하되,
   버튼 3개가 좁은 열에 들어가야 해서 패딩/글자를 조금 더 압축했습니다. 좁은 화면에서
   (recipient-route-cell이 모바일 카드 목록 안에 들어갈 때) 줄바꿈이 필요하면
   flex-wrap으로 자연스럽게 다음 줄로 넘어갑니다. */
.recipient-route-cell { min-width: 160px; }
.route-origin-toggle-group { display: flex; flex-wrap: wrap; gap: 4px; }
.route-origin-btn {
  background: transparent; color: var(--muted); border: 1px solid var(--border);
  border-radius: 6px; padding: 3px 8px; font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.02em; cursor: pointer; white-space: nowrap;
}
.route-origin-btn.on { background: rgba(63,185,80,0.15); color: var(--ok); border-color: var(--ok); }
/* 주소 입력창은 기본적으로 숨겨져 있다가(모드가 이미 "custom"이어도 페이지 로드
   시엔 접힌 채로 시작 - Todd님 요청: "별도의 편집 버튼없이 다시 (본인지정위치)를
   선택하거나 누르면 주소입력창이 다시 나타나서"), Custom Address 버튼을 누를
   때마다(이미 선택된 상태여도 매번) dashboard.js의 setRouteOriginMode()가
   display를 켜서 보여줍니다. */
.route-origin-address-editor { display: flex; gap: 4px; margin-top: 6px; flex-wrap: wrap; }
.route-origin-address-editor input {
  flex: 1 1 140px; min-width: 120px; font-size: 12px; padding: 3px 6px;
}
/* 2026-07-11 추가 (Todd님 요청: admin 담당자 전체수신/섹터전용 체크박스) - 섹터
   편집기 안에서 다른 입력칸들과 줄이 안 겹치도록 자체 줄로 배치. */
.admin-all-alerts-toggle {
  display: flex; align-items: center; gap: 6px; margin: 6px 0;
  font-size: 12.5px; color: var(--text-secondary, #9da5b4); cursor: pointer;
}
.admin-all-alerts-toggle input[type="checkbox"] { cursor: pointer; }
/* 2026-07-10 수정: 언어선택 박스 밑에 CCTV 반경 선택창이 추가되어, 언어선택 박스 높이를
   32px -> 26px로 줄여 두 항목이 쌓였을 때 옆 통계 박스/Alert Hours 배지와 높이가 맞도록 함. */
.dashboard-topbar .stat-row .lang-switcher { height: 26px; box-sizing: border-box; }

/* 2026-07-11 수정 (Todd님 요청: "모바일에서 이 시간 세팅창도 폴딩 처리 해줘 - 모바일
   에서만 적용") - 예전엔 모바일에서 이 패널이 "접기 기능 없는 평범한 패널"(항상
   펼침, 클릭 안 먹음)이었는데, 이제는 다른 패널(Twilio 등)처럼 접혔다 펼쳐지게
   바꿔달라는 요청이라 클릭을 막던 pointer-events:none과 화살표를 숨기던 규칙을
   제거했습니다 - details.panel summary의 기본 스타일(화살표 + 클릭 가능)이 그대로
   적용됩니다. 실제 기본 접힘 상태는 dashboard.js의 relocateAlertHoursPanel()에서
   panel.open = false로 설정합니다(PC와 동일하게 통일).
   같은 요청의 "타이틀 옆 (07:30 - 17:00) 없애도 될 것 같다"는 모바일에서만
   적용하는 것이라(PC는 접혀있을 때 이 배지가 유일한 시간 확인 수단이라 그대로
   둠), 모바일 슬롯에 있을 때만 이 배지를 숨깁니다. */
#alertHoursMobileSlot .alert-hours-panel .alert-hours-summary-time { display: none; }
/* 핀 경과시간 범례 (2026-07-31 개편, Todd님 요청: "메인지도 오른쪽에 세로로
   위치시키면 어떨까? 러너위치나 존표시 마크는 빼도 좋아").
   지도 위에 겹쳐 세로로 띄웁니다 - 지도 면적을 뺏지 않으면서 항상 보입니다. */
.pin-legend {
  position: absolute; top: 10px; right: 10px; z-index: 500;
  display: flex; flex-direction: column; gap: 5px;
  padding: 7px 9px;
  background: rgba(13, 17, 23, 0.82);
  border: 1px solid var(--border); border-radius: 8px;
  font-size: 10px; color: var(--muted);
  backdrop-filter: blur(3px);
  pointer-events: none;   /* 지도 드래그를 막지 않도록 */
}
.pin-legend .pl-title {
  font-weight: bold; color: var(--text); font-size: 9px;
  letter-spacing: 0.4px; text-transform: uppercase; margin-bottom: 1px;
}
.pin-legend .pl-item { display: flex; align-items: center; gap: 5px; white-space: nowrap; }
.pin-legend .pl-dot {
  width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto;
  border: 1.5px solid rgba(13, 17, 23, 0.8);
  display: inline-flex; align-items: center; justify-content: center;
}
/* 선택 표시(붉은 X) 견본 */
.pin-legend .pl-dot-x { background: #8b949e; }
.pin-legend .pl-dot-x svg {
  width: 9px; height: 9px; fill: none;
  stroke: #ff1c1c; stroke-width: 4; stroke-linecap: round;
}
@media (max-width: 760px) {
  /* 모바일: 더 작게 - 지도가 좁으므로 (아이폰 앱 웹뷰 포함) */
  .pin-legend { top: 6px; right: 6px; padding: 5px 7px; gap: 3px; font-size: 9px; }
  .pin-legend .pl-dot { width: 9px; height: 9px; }
  .pin-legend .pl-title { font-size: 8px; }
}

.stat-row { display: flex; gap: 16px; margin-top: 16px; align-items: center; }
.stat { background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
        padding: 14px 18px; flex: 1; }
.stat .num { font-size: 26px; font-weight: 700; }
.stat .label { color: var(--muted); font-size: 13px; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; }
.badge.on { background: rgba(63,185,80,0.15); color: var(--ok); }
.badge.off { background: rgba(139,148,158,0.15); color: var(--muted); }

/* 소스 뱃지: 소스마다 고유 색으로 구분합니다 (CHP=파랑/확정, TomTom=빨강/주황,
   HERE=청록 — 전부 "미확인"이지만 같은 색이면 TomTom과 HERE가 구별이 안 됐던 문제를
   해결하기 위해 2026-07-06에 소스별로 색을 분리했습니다). */
.source-badge { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 10.5px;
                font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; margin-right: 4px; }
/* 2026-07-09 2차 수정 (Todd님 요청): 배경색/모양(소스별 구분 색)은 그대로 두고, 글자색만
   불투명 흰색으로 바꿔서 가독성을 높였습니다. 소스 구분은 배경 색상만으로도 유지됩니다. */
/* 2026-07-12 (Todd님 요청): 소스 배지(WAZE/HERE/OCFA/CHP + 확정/비확정) 글자가 회색빛으로
   흐려 보인다는 피드백. 실제 글자는 #fff지만, 근무시간 외 발송 스킵 카드의 opacity(아래
   .item.skipped)가 배지까지 흐리게 만든 게 원인이었습니다. 배경 알파를 0.18->0.32로 올려
   배지를 더 또렷한 색블록으로 만들고, 흰 글자에 옅은 어두운 그림자를 더해 어떤 카드
   상태에서도 확실히 읽히게 합니다. */
.source-badge { color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.6); }
/* 2026-07-13 (Todd님 요청): 반투명이라 칙칙했던 배지를 불투명 선명색으로. 밝은 배경엔
   어두운 글자로 가독성 확보(text-shadow 제거). CHP=피에스타 레드, HERE=연두, Waze=하늘색,
   PulsePoint=완전 오렌지. */
.source-badge.source-confirmed { background: #e5484d; color: #fff; }
.source-badge.source-tomtom { background: rgba(255,106,61,0.5); }
.source-badge.source-here { background: #a6e22e; color: #14210a; text-shadow: none; }
.source-badge.source-ocfa { background: rgba(163,113,247,0.5); }
/* Waze 사용자 신고 (2026-07-12 추가) - Waze 브랜드에 가까운 하늘색 계열, 기존 색과 안 겹침 */
.source-badge.source-waze { background: #4fc3f7; color: #06283d; text-shadow: none; }
/* PulsePoint 소방 배차 (2026-07-13) - Todd님 요청으로 오렌지 계열. TomTom(주황빨강)과
   구분되게 좀 더 밝은 골든 오렌지로. */
.source-badge.source-pulsepoint { background: #ff8c00; color: #241300; text-shadow: none; }
.table-wrap { overflow-x: auto; }

/* 2026-07-09 7차 추가 (Todd님 요청): 수신자 관리 표의 마지막 칸(Save/Assign Sector/
   Activate·Deactivate/Delete 버튼 4개)이 데스크톱에서도 칸이 좁으면 두 줄로 줄바꿈돼서
   그 행 전체 높이가 늘어나던 문제. 데스크톱(761px 이상)에서만 이 칸을 줄바꿈 없이
   한 줄로 강제하고 오른쪽으로 정렬합니다 - 표(table)가 100% 너비라 남는 공간이 있으면
   버튼들이 오른쪽 끝에 붙어서, 섹터 관리 카드의 버튼 줄과 비슷하게 한 줄로 압축된
   모습이 됩니다. 모바일(카드형 스택 레이아웃)은 손대지 않습니다. */
@media (min-width: 761px) {
  .recipient-actions-cell { white-space: nowrap; text-align: right; }
}

/* 실증 섹터 관리 패널 (2026-07-08 추가). 섹터 목록을 표가 아니라 간단한 flex 행으로
   보여줍니다 - 항목 수가 러너 수(최대 8명)만큼만 있어서 표보다 가벼운 카드형이 더
   적합합니다. */
.sector-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
/* 2026-07-09 재구성 (Todd님 제보: 지명이 길면 옆의 활성/편집/비활성화/삭제 버튼이
   다음 줄로 밀려나서 줄마다 버튼 위치가 들쑥날쑥해짐). 한 줄짜리 flex-wrap 대신
   "이름/러너" - "지명(2~3줄)" - "상태뱃지+버튼" 세 줄로 구조를 나눴습니다. 버튼
   그룹(.sector-row-actions)은 항상 자기만의 줄에 있어서 지명 텍스트 길이와
   무관하게 절대 다음 줄로 밀려나지 않습니다. */
.sector-row {
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
}
.sector-row.inactive { opacity: 0.55; }
.sector-row-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sector-color-dot {
  width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0;
  border: 2px solid #fff; box-shadow: 0 0 0 1px var(--border);
}
.sector-row-name { font-weight: 600; }
.sector-row-runner { color: var(--muted); font-size: 13px; flex: 0 0 auto; min-width: 80px; }
/* 2026-07-09 추가 (Todd님 요청: 이름/담당자 옆 빈 공간에 섹터를 구성하는 지명
   표시). 2026-07-09 재수정 (Todd님 요청: 한 줄 줄임표 대신 2~3줄까지 펼쳐서
   보이게) - white-space: nowrap + ellipsis 대신 line-clamp로 최대 3줄까지
   보여주고 그 이상은 생략(...)합니다. */
.sector-row-cities {
  color: var(--muted); font-size: 12px; font-style: italic;
  white-space: normal;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  line-clamp: 3; overflow: hidden;
}
.sector-row-actions { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }
.sector-editor {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px; border: 1px dashed var(--border); border-radius: 8px; margin-bottom: 10px;
}
.sector-editor .hint { flex-basis: 100%; margin: 4px 0 0 0; }

/* 2026-07-08 추가, 2026-07-09 재작성: 섹터 편집 패널의 "행정구역 클릭 모드로 전환"/
   "자유 드로잉 시작" 버튼입니다 (이름/러너/색상 옆에 나란히 배치, Todd님 요청).
   예전엔 선택된(.active) 버튼에 color:var(--accent)만 덧씌웠는데, 기본 button 스타일의
   배경색이 이미 var(--accent)라서 글씨가 배경과 같은 색이 되어 "빈 하늘색 박스"처럼
   보이는 버그가 있었습니다(Todd님 제보). 이제 선택 안 된 버튼은 테두리만 있는 옅은
   모양으로, 선택된 버튼만 꽉 찬 강조색으로 - 배경/글씨 색이 겹치지 않게 고쳤습니다. */
.sector-mode-tab {
  background: transparent; color: var(--text); border: 1px solid var(--border); opacity: 1;
}
.sector-mode-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }
#sectorBoundaryMode, #sectorFreeMode { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex-basis: 100%; }
.sector-city-chip-list { display: flex; flex-wrap: wrap; gap: 6px; flex-basis: 100%; }
.sector-city-chip {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; color: #04121f; cursor: pointer; user-select: none;
}
.sector-city-chip:hover { filter: brightness(1.1); }

/* 2026-07-09 추가: 섹터 다각형을 직접 그리기(Leaflet.draw 플러그인 제거 후 자체
   구현)에 쓰는 꼭짓점/중간점 마커 아이콘입니다. 지도 전체를 반전(invert)시키는
   다크모드 필터(#map .leaflet-marker-icon 규칙 참고) 영향을 안 받도록 배경을
   투명하게 두고 실제 모양은 .sector-draft-dot(순수 div)으로 그립니다. */
#map .leaflet-marker-icon.sector-draft-icon { filter: none !important; }
/* 2026-07-09 확대 (Todd님 제보 - 점이 너무 작아서 클릭/드래그하기 불편했음, 특히
   사고 핀과 겹칠 때). iconSize도 dashboard.js의 SECTOR_DRAFT_ICON_SIZE에서 함께 키웠습니다. */
.sector-draft-dot {
  width: 16px; height: 16px; border-radius: 50%;
  /* 2026-07-09 변경: 예전 accent 파란색(#58a6ff) 하드코딩 값을 var(--accent)로 바꿔서
     브랜드 레드로 자동 반영되게 했습니다. */
  background: var(--accent); border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,0.5);
}
/* 3점 이상 찍었을 때 "여길 다시 클릭하면 완성됨"을 알려주는 첫 번째 점 강조 */
.sector-draft-vertex-start .sector-draft-dot {
  width: 22px; height: 22px; background: #ff9f0a;
  box-shadow: 0 0 0 4px rgba(255,159,10,0.35), 0 0 0 1px rgba(0,0,0,0.5);
}
/* 완성된 다각형의 변 중간에 놓이는 "클릭하면 새 점 추가" 표시 */
.sector-draft-midpoint .sector-draft-dot {
  width: 11px; height: 11px; background: #8b949e; opacity: 0.85;
}
/* 2026-07-09 추가 (Todd님 요청): 섹터를 그리거나 편집하는 동안 지도 배경 + 꼭짓점/
   중간점 위에서도 전부 "지금 편집 중"임을 알아보기 쉬운 노란색 십자선을 유지합니다
   (예전엔 꼭짓점/중간점 위에서 pointer/copy로 바뀌어서 편집 커서가 자꾸 손모양으로
   끊겨 보인다는 제보가 있었습니다). CSS 변수로 한 곳에만 정의해서 크기를 바꿀 때
   두 규칙이 어긋나지 않게 했습니다. dashboard.js의 _setSectorEditingCursor()가
   #map에 sector-editing-cursor 클래스를 붙였다 뗐다 합니다. !important는 Leaflet의
   .leaflet-grab/.leaflet-dragging 커서 규칙, 그리고 pointer/copy를 확실히 덮어쓰기
   위함입니다. (2026-07-09 재확대: 20% 더 키움, 26px -> 32px) */
:root {
  --sector-edit-cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000000' stroke-width='4' stroke-linecap='round'%3E%3Cline x1='16' y1='3' x2='16' y2='29'/%3E%3Cline x1='3' y1='16' x2='29' y2='16'/%3E%3C/g%3E%3Cg stroke='%23ffcc00' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='16' y1='3' x2='16' y2='29'/%3E%3Cline x1='3' y1='16' x2='29' y2='16'/%3E%3C/g%3E%3Ccircle cx='16' cy='16' r='5' fill='none' stroke='%23ffcc00' stroke-width='2'/%3E%3C/svg%3E") 16 16, crosshair;
}
#map.sector-editing-cursor {
  cursor: var(--sector-edit-cursor) !important;
}
.sector-draft-vertex, .sector-draft-vertex-start, .sector-draft-midpoint {
  cursor: var(--sector-edit-cursor) !important;
}

/* 실사(현장 검증) 결과 패널: 왼쪽 70%엔 원본 표(주소/소스/결과/메모/응답자/삭제버튼),
   오른쪽 30%엔 소스x결과별 건수를 막대그래프로 시각화합니다 (2026-07-07 추가).
   차트 라이브러리를 새로 불러오지 않고 CSS width(%)만으로 그리는 단순한 막대라서
   외부 의존성이 늘지 않습니다. */
/* 2026-07-09 추가: 실사결과 자동삭제 시간 선택 + 전체삭제 버튼 한 줄 (좁은 화면에서는
   줄바꿈되도록 flex-wrap). */
.verification-controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  margin-bottom: 12px; font-size: 13px;
}
.verification-controls form.inline { display: flex; align-items: center; gap: 6px; margin-right: 0; }

.verification-layout { display: flex; gap: 16px; align-items: flex-start; }
.verification-data { flex: 0 0 68%; min-width: 0; }

/* 2026-07-08 추가: 실사 결과가 쌓일수록 표가 한없이 길어져서 페이지 스크롤이 너무
   길어지는 문제. 메인 지도 왼쪽의 사고 배너 목록(.sidebar)이 max-height +
   overflow-y:auto로 자체 스크롤을 갖는 것과 같은 방식으로, 이 표(데스크톱 전용 -
   모바일은 .verification-mobile-list를 따로 씀)도 높이로 제한하고 그 안에서만
   스크롤되게 했습니다. 헤더(th)는 position:sticky로 고정해서 스크롤해도
   "주소/소스/결과..." 제목 행이 계속 보이게 했습니다.
   (처음엔 8~10행 기준 480px로 했다가, "그래도 많다"는 요청으로 6행 기준
   320px로 줄였습니다.) */
.verification-data .table-wrap { max-height: 320px; overflow-y: auto; }
.verification-data .table-wrap th {
  position: sticky; top: 0; background: var(--panel); z-index: 1;
}
.verification-chart { flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--border);
                       border-radius: 8px; padding: 14px; }
.verification-chart-title { font-weight: 600; font-size: 12px; color: var(--muted);
                             text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 12px; }
.v-bar-row { margin-bottom: 12px; }
.v-bar-label { font-size: 12px; color: var(--text); margin-bottom: 4px; white-space: nowrap;
               overflow: hidden; text-overflow: ellipsis; }
.v-bar-track { background: var(--panel); border: 1px solid var(--border); border-radius: 4px;
               height: 14px; overflow: hidden; }
.v-bar-fill { height: 100%; border-radius: 3px; }
.v-bar-count { font-size: 11px; color: var(--muted); margin-top: 3px; text-align: right; }

/* 실사결과 모바일 전용 압축 카드 (2026-07-08 추가). 기존 반응형 표(.table-wrap)는
   필드 6개를 한 줄씩 쌓아서 카드 하나가 화면을 거의 다 채웠는데, 이 카드는 배지 두 개
   + 주소(최대 2줄) + (있으면) 응답자/메모 한 줄로 압축해서 원래 대비 20% 안팎으로
   줄였습니다. 데스크톱에서는 숨기고(.table-wrap 그대로 사용), 모바일에서만 보여줍니다
   (아래 @media 블록에서 서로 전환). */
.verification-mobile-list { display: none; }
.v-card {
  position: relative; overflow: hidden; border: 1px solid var(--border);
  border-radius: 8px; margin-bottom: 6px; background: var(--danger);
}
/* 오른쪽으로 밀면 뒤에 깔린 "Delete" 문구가 드러나고, 위 카드 내용(.v-card-content)이
   손가락을 따라 오른쪽으로 이동합니다 (요청: "오른쪽으로 살짝 밀어서 지우는 방식"). */
.v-card-swipe-bg {
  position: absolute; inset: 0; display: flex; align-items: center; padding-left: 16px;
  color: #fff; font-weight: 700; font-size: 13px;
}
.v-card-content {
  position: relative; background: var(--panel); padding: 7px 10px;
  touch-action: pan-y; /* 세로 스크롤은 그대로 두고, 가로 드래그만 JS가 처리 */
}
.v-card-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; gap: 8px; }
.v-card-result { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; flex-shrink: 0; }
.v-result-pending { color: var(--muted); }
.v-result-confirmed { color: var(--ok); }
.v-result-false_alarm { color: var(--danger); }
.v-result-resolved { color: var(--warn); }
.v-card-address {
  font-size: 13px; font-weight: 600; line-height: 1.3;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.v-card-sub {
  font-size: 11px; color: var(--muted); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 사고 밀도 클러스터 지도 (2026-07-08 추가): 매일 리셋되는 상단 통계와 달리, CHP
   확정 사고를 도입일 이후 계속 누적해서 보여주는 별도의 작은 지도입니다. 위쪽 메인
   지도(#map)와 달리 다크모드 필터 트릭 대신 실제로 어두운 지도 타일(CartoDB dark_all)을
   써서, 클러스터 핀(오렌지색 원)이 필터에 영향받지 않고 항상 또렷하게 보이게 했습니다.
   덤으로 이 타일은 주요 도로가 기본적으로 옅은 주황색으로 표시돼서, 요청하신
   "도로는 오렌지색으로" 느낌도 별도 스타일링 없이 자연스럽게 충족됩니다. */
/* "회사 위치로 리셋" 버튼 (2026-07-08 추가) - 확대/축소 버튼 바로 아래, 같은 흰색
   박스 스타일(Leaflet 기본 leaflet-bar)을 그대로 씁니다.
   2026-07-11 수정 (Todd님 제보: "홈버튼이 락 버튼보다 식별이 잘 안된다, 락버튼과
   비슷한 느낌으로 키워달라") - "⌂" 텍스트 글자 대신 dashboard.js의 HOME_ICON_SVG
   (락 아이콘과 동일한 16x16/두께의 SVG)를 쓰도록 바꾸면서, 정렬 방식도 아래
   .area-lock-control a와 완전히 동일하게 맞췄습니다(두 버튼이 같은 크기/두께로
   보이도록 통일). */
.home-control a { display: flex; align-items: center; justify-content: center; }
.home-control a svg { display: block; }

/* 2026-07-09 수정 (Todd님 요청): "LA/OC Lock" 버튼 - 홈 버튼 바로 아래, 같은 흰색
   박스 스타일을 그대로 씁니다. 원래는 잠긴 상태일 때 배경을 브랜드 레드로 바꿔서
   구분했는데, Todd님이 "색상보다 자물쇠 열림/잠김 아이콘으로 구분하는 게 더 알아보기
   쉽다"고 요청하셔서 배경색 강조는 없애고, 아이콘만으로 상태를 구분합니다.
   [2026-07-10 추가 수정] 이모지 방식(🔓/🔒, 180도 회전) 둘 다 원하시는 모양과 달라서,
   dashboard.js의 LOCK_ICON_SVG/UNLOCK_ICON_SVG(걸쇠가 실제로 닫힘/열림으로 다르게
   그려지는 SVG 아이콘 세트)로 교체했습니다. 이 규칙은 그 SVG를 가운데 정렬만 합니다. */
.area-lock-control a { display: flex; align-items: center; justify-content: center; }
.area-lock-control a svg { display: block; }

/* LA 현지시각 시계 (2026-07-10 추가, Todd님 요청: "메인 지도 좌측하단에 LA 현지시간
   (24시간표기) 시,분,초"). 버튼이 아니라 항상 떠 있는 읽기전용 표시라서 leaflet-bar
   (흰색 사각 버튼) 스타일 대신 반투명 어두운 배경의 작은 캡슐로 다른 지도 요소들과
   구분되게 했습니다. 숫자만 있어도 폭이 흔들리지 않도록 고정폭 폰트를 씁니다. */
.la-clock-control {
  background: rgba(13,17,23,0.78);
  color: #f0f3f6;
  padding: 5px 10px;
  border-radius: 6px;
  font-size: 13px;
  font-family: 'SFMono-Regular', Consolas, Menlo, monospace;
  letter-spacing: 0.02em;
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
  user-select: none;
  pointer-events: none;
}

/* 2026-07-09 추가 (Todd님 요청): 섹터 편집 중 지도 왼쪽 위에 떠 있는 저장 버튼
   (디스크 아이콘 + "Save" 텍스트). 확대/축소·홈 버튼과 같은 자리(topleft)라
   Leaflet이 자동으로 그 아래에 이어붙여줍니다. 다른 컨트롤과 달리 아이콘+글자를
   같이 보여줘야 해서, Leaflet 기본 26x26 정사각형 크기를 풀어줍니다. */
.sector-save-control a {
  width: auto !important; height: auto !important; min-width: 26px;
  padding: 6px 12px; display: flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; line-height: 1.2; white-space: nowrap;
  /* 2026-07-09 추가/재수정: 다른 저장 버튼들과 동일하게 초록색으로 맞췄고(저장=초록,
     편집=파란색, 삭제=빨강으로 구분), 색이 너무 vivid하다는 제보로 반투명하게
     낮췄습니다. 안의 💾 이모지는 색 글리프라 그대로 원색으로 보입니다. */
  background: rgba(63,185,80,0.55) !important; color: var(--text) !important;
}
/* 2026-07-09 4차 추가: Save 링크 바로 아래(leaflet-bar가 자동으로 세로로 쌓아줌)에
   놓이는 "전체 지우기" 링크. Save(초록)와 구분되게 경고색(--warn, 주황)을 씁니다. */
.sector-save-control-clear {
  background: rgba(210,153,34,0.35) !important; color: var(--warn) !important;
}

/* 밀도 지도(왼쪽) + 통계 사이드(오른쪽) 레이아웃 (2026-07-08 추가, 2026-07-10 확장).
   랭킹 쪽은 지도(#density-map)와 높이(400px)를 똑같이 맞추고, 그 안에 10줄이 스크롤
   없이 다 들어오도록 여백/막대 두께를 최대한 압축했습니다 (한 줄 = 이름+건수 한 줄 +
   얇은 막대 한 줄, 기존엔 3줄이었습니다).
   2026-07-10 수정 (Todd님 요청: "통계 표시하는 지도 사이드를 줄이더라도 요일별
   통계도 세로 막대형태로 만들어줘 - 지역통계/시간대별통계/요일별통계를 잘 배치") -
   오른쪽 통계 박스가 이제 Top 10 지역 + 시간대별(24칸) + 요일별(7칸) 세 섹션을
   전부 담아야 해서, 지도 60% : 박스 40% 였던 비율을 50:50으로 조정해 통계 박스에
   더 넓은 공간을 줬습니다(지도가 조금 좁아지는 트레이드오프는 Todd님이 명시적으로
   허용). */
.density-layout { display: flex; gap: 16px; align-items: flex-start; margin-top: 12px; }
#density-map, #cctv-density-map { flex: 1 1 50%; min-width: 0; height: 400px; border-radius: 10px; border: 1px solid var(--border); }
/* 2026-07-22 CCTV Anomaly Density Map: 랭킹 줄 아래 패턴별 칩 + 패널 내 차트 소제목 여백.
   같은 날 추가 (Todd님): 막대는 종류별 색상 스택, 칩엔 색점+아이콘+짧은 설명(PC만). */
.cctv-rank-kinds { margin: 2px 0 0 24px; font-size: 12px; display: flex; flex-wrap: wrap; gap: 3px 12px; }
.cctv-kind-chip { white-space: nowrap; display: inline-flex; align-items: center; gap: 4px; }
.cctv-kind-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.cctv-stacked { display: flex; }
.cctv-stacked .density-rank-fill { border-radius: 0; }
.cctv-stacked .density-rank-fill:first-child { border-radius: 2px 0 0 2px; }
.cctv-stacked .density-rank-fill:last-child { border-radius: 0 2px 2px 0; }
.cctv-chart-title { margin-top: 18px; }
.density-ranking { flex: 0 0 50%; min-width: 0; background: var(--bg); border: 1px solid var(--border);
                    border-radius: 8px; padding: 10px 14px; height: 400px; overflow-y: auto;
                    display: flex; flex-direction: column; }
.density-ranking-title { font-weight: 600; font-size: 11px; color: var(--muted);
                          text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 2px; }
.density-ranking .hint { font-size: 10px; line-height: 1.25; margin: 0 0 6px 0; }
.density-rank-row { margin-bottom: 5px; }
.density-rank-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
                     font-size: 12px; color: var(--text); margin-bottom: 2px; }
.density-rank-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.density-rank-num { display: inline-block; min-width: 14px; color: var(--muted); font-weight: 700; margin-right: 3px; }
.density-rank-count { flex-shrink: 0; font-size: 10.5px; color: var(--muted); }
.density-rank-track { background: var(--panel); border: 1px solid var(--border); border-radius: 3px;
                       height: 6px; overflow: hidden; }
.density-rank-fill { height: 100%; border-radius: 2px; background: #ffb300; }

/* 2026-07-10 재작성 (Todd님 요청: "시간대별 통계를 24시간 세로 막대그래프로 변경",
   "요일별 통계도 세로 막대형태로"). 기본(모바일/독립 위치)은 class="panel"이 이미
   있어서 다른 패널들과 같은 모양입니다. PC에서는 dashboard.js의
   relocateStatsChartPanels()가 이 두 패널(시간대별/요일별)을 #densityRankingBox
   (Top 10 지역 랭킹 박스) 안으로 옮기는데, 그 안에서는 자체 테두리/배경 없이 위
   섹션과 구분선 하나로만 이어지게 스타일을 리셋합니다 - 지역/시간대별/요일별
   통계 세 섹션이 한 박스 안에서 시각적으로 하나의 통계 패널처럼 보이게 합니다. */
#densityRankingBox .chart-panel {
  margin: 14px 0 0 0; padding: 12px 0 0 0; background: transparent; border: none;
  border-radius: 0; border-top: 1px solid var(--border); flex-shrink: 0;
}

/* 세로 막대그래프 공용 스타일 (2026-07-10 추가) - 시간대별(24칸)/요일별(7칸) 둘 다
   같은 구조(막대 트랙 + 채움 + 아래 라벨)를 쓰고, chart-hourly/chart-weekday 수식자로
   칸 너비/간격/폰트만 다르게 줍니다. 막대 높이는 서버가 계산한 bar_percent(%)를
   그대로 height로 씁니다(가장 큰 값이 100%인 상대값 - density-rank-fill의 세로 버전). */
.vbar-chart { display: flex; align-items: flex-end; gap: 2px; height: 100px; margin-top: 8px; }
.vbar-chart-weekday { gap: 10px; height: 104px; }
.vbar-col { flex: 1 1 0; min-width: 0; height: 100%; display: flex; flex-direction: column;
            align-items: center; justify-content: flex-end; }
.vbar-count { font-size: 9px; color: var(--muted); margin-bottom: 2px; line-height: 1; }
.vbar-track { width: 100%; max-width: 12px; flex: 1 1 auto; display: flex; align-items: flex-end;
              background: var(--panel); border: 1px solid var(--border); border-radius: 2px;
              overflow: hidden; }
.vbar-chart-weekday .vbar-track { max-width: 26px; }
.vbar-fill { width: 100%; min-height: 1px; background: #ffb300; border-radius: 1px 1px 0 0; }
/* 2026-07-11 재작성 (Todd님 1차 요청: "막대 안에 퍼센트 % 수치도 표기해줄래? 작게라도
   흰색으로" -> 2차 피드백: "위 % 수치도 너무 작아. 막대안에 갇히게 하지말고 막대
   그래프 밖으로 나와도 괜찮으니 밑의 시간 표시와 동일하게 약간 크게해줘") - 처음엔
   막대 안(트랙 내부)에 작게 절대배치했는데, 너무 작고 막대 안에 갇혀 있었습니다.
   이제 요일별 차트의 .vbar-count와 같은 방식으로 트랙 "바깥" 위쪽에 독립된 한 줄로
   빼서(막대 높이/트랙 overflow와 완전히 무관), 아래 시간 라벨(.vbar-label)과 같은
   크기(10px)로 키웠습니다. 흰색+볼드로 눈에 띄게 유지합니다. */
.vbar-percent { font-size: 10px; color: #fff; font-weight: 700; margin-bottom: 2px; line-height: 1; white-space: nowrap; }
.vbar-label { font-size: 10px; color: var(--muted); margin-top: 3px; line-height: 1; white-space: nowrap; }
.vbar-chart-weekday .vbar-label { font-size: 11px; font-weight: 600; color: var(--text); }

/* 회사(GU Collision Center) 위치 핀 - 밀도 지도용 (2026-07-08 추가). 사고 데이터는 전부
   오렌지 계열이라, 회사 위치는 파란색으로 확실히 구분되게 표시합니다.
   2026-07-09: 전체 UI 색상을 브랜드 레드로 맞추면서도 이 핀은 일부러 그대로 파란색으로
   남겨뒀습니다 - 여기를 레드로 바꾸면 바로 옆 주황색 사고 마커들과 구분이 잘 안 되기
   때문입니다 (이 파란색은 "브랜드색"이 아니라 "사고 데이터와 겹치지 않는 대비색"이라는
   기능적 목적이라 톤앤매너 변경 대상에서 제외). */
.density-company-pin {
  width: 20px; height: 20px;
  background: #58a6ff;
  border: 3px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 2px #58a6ff, 0 2px 6px rgba(0,0,0,0.7);
}

/* 클러스터 아이콘: 건수 구간별로 원 크기를 3단계로 키웁니다 (건수가 많을수록 눈에
   더 잘 띄어야 하므로). L.divIcon으로 그린 순수 CSS 원이라 이미지 파일이 필요 없습니다. */
.density-cluster-icon { background: transparent; border: none; }
.density-cluster-inner {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; border-radius: 50%;
  background: rgba(255, 140, 0, 0.75); border: 2px solid #ffb300;
  color: #1a1200; font-weight: 700; font-size: 12px;
}
.density-cluster-sm .density-cluster-inner { width: 30px; height: 30px; font-size: 11px; }
.density-cluster-md .density-cluster-inner { width: 42px; height: 42px; font-size: 13px; }
.density-cluster-lg .density-cluster-inner { width: 56px; height: 56px; font-size: 15px; }

/* 개별(클러스터로 묶이지 않은) 사고 지점: 클러스터와 같은 오렌지 계열이지만 훨씬
   작은 점으로 표시해서, 확대했을 때 클러스터 원과 시각적으로 자연스럽게 이어지게 합니다. */
.density-point-icon { background: transparent; border: none; }
.density-point-inner {
  width: 14px; height: 14px; border-radius: 50%;
  background: #ffb300; border: 2px solid #1a1200;
}

/* Narrow screens (phones): compact single-row stats (+ language switcher in the same
   row to save vertical space), map first, banner right below it, detail panel last,
   and the recipient table becomes a stacked card list (no horizontal scrolling). */
@media (max-width: 760px) {
  body { padding: 10px; font-size: 15px; }
  /* 2026-07-09 26차 수정 (Todd님 요청: "(logo) GU Collision Alert-Admin Dashboard를
     한 단락으로 해서 다음칸으로 넘어가지 않도록") - 9차 수정에서는 반대로 두 줄로
     강제 분리했었는데, 이번엔 정반대 요청 - 로고+제목을 한 줄에 다 담고 절대
     다음 줄로 안 넘어가게 해달라는 것. 폰트를 12px까지 줄이고(요청: "폰트 사이즈를
     조금 줄여도 괜찮아"), .h1-sub를 다시 inline으로(9차의 display:block 되돌림),
     h1 자체에 white-space:nowrap + overflow:hidden + text-overflow:ellipsis를
     걸어서 혹시 그래도 안 들어가는 아주 좁은 화면에서는 줄바꿈 대신 "..."로
     잘리게(다음 줄로 넘어가는 것보다 훨씬 자연스러움) 안전장치를 뒀습니다.
     flex 안에서 ellipsis가 동작하려면 min-width:0이 필요합니다. */
  h1 {
    font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    min-width: 0; flex: 1 1 auto;
  }
  .h1-sub { display: inline; }
  .header-row { flex-wrap: nowrap; }
  .header-logo { height: 16px; }
  .site-footer { margin-top: 20px; padding: 14px 0 4px; gap: 10px; }
  .footer-logo { height: 28px; }
  p.company-address { font-size: 11px; margin-top: 2px; }
  /* 2026-07-15: 회사주소 위치는 이제 PC/모바일 공통(기본 규칙)으로 수신자관리 박스
     안에만 표시되므로, 예전 모바일 전용 전환 규칙은 필요 없어져 제거했습니다. */
  .stat-row { flex-direction: row; gap: 4px; margin-top: 8px; }
  .stat { padding: 6px 6px; }
  .stat .num { font-size: 15px; }
  .stat .label { font-size: 9px; }
  .lang-switcher { padding: 3px 6px; }
  .lang-switcher select { font-size: 12px; padding: 2px 2px; }
  /* 2026-07-09 추가: 알림시간 설정은 모바일에서는 원래 자리(지도 아래)에 그대로 두므로,
     이 자리는 비어있는 채로 남아 불필요한 여백만 차지합니다 - 숨겨서 정리합니다. */
  #alertHoursDesktopSlot { display: none; }
  .layout { flex-direction: column; margin-top: 8px; }
  /* 2026-07-13 (Todd님 요청): 모바일 지도 높이를 약 15% 줄여(260 -> 221px) 아래로
     스크롤하기 쉽게 함. 모바일에서만 적용(데스크톱 #map은 600px 그대로). */
  /* 2026-07-31 (아이폰 앱/모바일 지도 미표시 진짜 원인): 2026-07-31에 범례용
     래퍼(#mapWrap)가 생기면서 .layout의 flex 자식이 #map -> #mapWrap으로
     바뀌었는데, 이 모바일 블록의 order/width는 여전히 #map만 겨냥하고 있었음
     (§19-74에서 데스크톱 쿼리만 고치고 모바일 쿼리는 놓침). .layout은
     align-items:flex-start라 세로(column) 모드에서 자식이 가로로 늘어나지 않고,
     #mapWrap엔 width가 없어 내용맞춤 -> 빈 #map(width:100% = 부모 기준 = 0)과
     맞물려 가로폭이 0으로 붕괴 -> 화면엔 테두리만 세로줄로 남음("왼쪽으로
     밀려 보임"). order/width는 #mapWrap이 받아야 합니다. */
  #mapWrap { order: 1; width: 100%; flex: 0 0 auto; }
  #map { width: 100%; height: 221px; }
  /* 2026-07-08: 320px -> 210px로 낮춰서 카드 4~5개 정도만 보이게 함 (카드 하나당
     대략 45px). 스크롤 없이 지도 밑에 너무 많은 카드가 이어지면 상세정보 패널까지
     내려가기가 힘들다는 요청 반영. */
  .sidebar { order: 2; width: 100%; max-height: 210px; }
  /* 2026-07-08: 데스크톱에서는 지도 높이(600px)에 맞추려고 height를 고정값으로 바꿨는데,
     모바일은 지도 밑에 세로로 쌓이는 구조라 그 고정값이 필요 없어서(오히려 빈 공간이
     생김) 다시 내용에 맞게 자연스러운 높이로 풉니다. */
  .detail-panel { order: 3; width: 100%; height: auto; max-height: none; }
  .detail-panel .map-embed-wrap { flex: 0 0 auto; min-height: 0; }
  .detail-panel .map-embed-wrap iframe { height: 220px; }
  /* 2026-07-13 (Todd님 요청): 모바일에서 사고유형 박스 여백 최소화 + 우측 상단 고정.
     max-width를 60%로 둬서 왼쪽 소스배지와 같은 줄(우측 상단)에 유지되게 합니다. */
  .detail-highlight-box { padding: 2px 6px; max-width: 60%; }
  /* 2026-07-09 15차 추가 (Todd님 제보: "Route preview (Company →"가 한 줄, "Scene)
     Open in Google Maps"가 다음 줄로 잘려서 어긋나 보임) - space-between 플렉스
     배치 때문에 제목 span 자체가 좁아져서 텍스트가 두 줄로 쪼개지고, 짧은 링크는
     그 중 아래쪽 줄 옆에 걸쳐 보였던 것. 모바일에서만 이 줄을 flex 대신 일반 블록
     흐름으로 바꿔서 제목(짧은 버전)과 링크가 한 단락처럼 자연스럽게 이어지도록
     합니다 - 화면이 좁으면 평범한 텍스트처럼 줄바꿈되지만, 두 요소가 서로 어긋나
     보이지는 않습니다. */
  .detail-panel .detail-title-row { display: block; }
  .detail-panel .eta-title-long { display: none; }
  .detail-panel .eta-title-short { display: inline; }
  .detail-panel .eta-route-sep { display: inline; margin: 0 6px; color: var(--muted); }
  .detail-panel .detail-title-row a { display: inline; }
  .detail-panel .empty { flex: 0 0 auto; padding: 40px 10px; }
  /* 2026-07-08 추가: 좁은 화면에서 카메라 그리드를 2~3열로 욱여넣으면 셀이 너무
     작아져서 알아보기 힘들어, 모바일에서는 항상 1열로 세로 쌓기로 바꿉니다. */
  .detail-panel .cctv-grid-2, .detail-panel .cctv-grid-3 { grid-template-columns: 1fr; }
  input[type=text] { width: 90px; }
  input[type=number] { width: 100px; }
  .panel { padding: 14px; }

  .table-wrap table, .table-wrap thead, .table-wrap tbody, .table-wrap tr, .table-wrap th, .table-wrap td {
    display: block;
  }
  .table-wrap thead { display: none; }
  .table-wrap tr {
    border: 1px solid var(--border); border-radius: 8px; padding: 8px; margin-bottom: 10px;
  }
  .table-wrap td {
    border: none; padding: 5px 2px; display: flex; align-items: center; gap: 8px;
  }
  .table-wrap td::before {
    content: attr(data-label); font-weight: 600; color: var(--muted); font-size: 12px;
    min-width: 70px; flex-shrink: 0;
  }
  .table-wrap td input, .table-wrap td select { flex-grow: 1; margin-right: 0; }

  .verification-layout { flex-direction: column; }
  .verification-data, .verification-chart { flex: 1 1 auto; width: 100%; }

  .density-layout { flex-direction: column; }
  #density-map, #cctv-density-map, .density-ranking { flex: 1 1 auto; width: 100%; }
  #density-map, #cctv-density-map { height: 260px; }
  /* 2026-07-11 추가 (Todd님 제보: "accident density map 아래 설명 텍스트 폰트가
     너무 큰것 같아. accidents by hour of day 아래 설명 텍스트 폰트 사이즈로
     줄여줄래") - "Accidents by Hour of Day" 등 시간대별/요일별 차트의 설명
     문구가 작게 보이는 이유는 그 문구들이 모바일에서 relocateStatsChartPanels()
     에 의해 #densityRankingBox(.density-ranking 클래스) 안으로 옮겨지면서 위
     ".density-ranking .hint { font-size: 10px; }" 규칙을 물려받기 때문입니다.
     Accident Density Map 패널의 설명 문구는 옮겨지지 않는 별개의 자리에 있어서
     기본값(14px) 그대로였던 것 - 모바일 전용으로 같은 크기를 맞춰줍니다
     (PC는 그대로 14px 유지). */
  #densityMapPanel > .hint { font-size: 10px; line-height: 1.25; }
  /* 2026-07-08 추가 (Todd님 피드백): 데스크톱은 랭킹 박스가 지도(400px)와 높이를
     맞추느라 순위가 5~6개뿐이면 아래쪽에 빈 공간이 남는데, 모바일에서는 세로로
     이어붙는 레이아웃이라 그 빈 공간이 훨씬 크게 느껴집니다. 고정 height 대신
     내용 높이(auto)로 바꿔서 박스 자체가 내용만큼만 짧아지게 합니다.
     2026-07-11 수정 (Todd님 요청: "사고발생 지역순위에 스크롤을 없애 줘도
     괜찮아") - §19-53에서 지역 랭킹 박스가 지도 패널 밖으로 완전히 독립된
     카드가 되면서, 안에 내부 스크롤(overflow-y:auto)이 있을 이유가 없어졌습니다
     (예전엔 지도(400px)와 높이를 맞추려고 넣었던 제약인데, 독립 카드가 된
     지금은 내용 높이만큼 자연스럽게 늘어나도 되므로) - max-height 제한과
     overflow-y:auto를 모두 제거해서 몇 개가 나오든 스크롤 없이 전부 보이게
     합니다. */
  .density-ranking { height: auto; max-height: none; overflow-y: visible; }

  /* 2026-07-11 추가 (Todd님 요청: "모바일에서 지역 통계 막대기를 독립시켜서 아래
     시간별/요일별 통계 그래프처럼 독립된 박스로 만들어줘 - 모바일만 적용") -
     dashboard.js의 relocateStatsChartPanels()가 모바일에서 #densityRankingBox를
     #densityRankingMobileSlot 안으로 옮기는데, 그 상태(.density-layout 밖, 지도
     패널과 완전히 분리된 위치)일 때만 위쪽에 24px 여백을 줘서 시간별/요일별 통계
     패널(.panel)과 똑같이 위아래로 숨 쉴 공간이 있는 독립된 카드로 보이게 합니다.
     (데스크톱에서 .density-layout 안에 있을 때는 이 마진이 적용되지 않도록 슬롯
     안에 있을 때로 한정 - 기존 flex gap:16px 그대로 유지) */
  #densityRankingMobileSlot .density-ranking { margin-top: 24px; }

  /* 실사결과: 좁은 화면에서는 원래 표 대신 위에서 정의한 압축 카드 목록을 보여줍니다. */
  .verification-data .table-wrap { display: none; }
  .verification-mobile-list { display: block; }

  /* 2026-07-08 추가: 모바일 카드 목록도 데스크톱 표(.table-wrap, 6행 기준 320px)와
     같은 이유로 개수 제한이 필요하다는 요청 - 실사결과가 많아지면 카드가 계속 늘어나서
     페이지 전체를 스크롤하기 힘들어짐. 카드 1개가 표 1행보다 키가 커서(주소 2줄까지
     허용) 320px 그대로 쓰면 3~4개밖에 안 보이므로, 6~7개가 보이도록 480px로 맞췄고
     그 안에서만 스크롤되게 했습니다 (페이지 자체 스크롤과 분리). */
  .verification-mobile-list { max-height: 480px; overflow-y: auto; -webkit-overflow-scrolling: touch; }

  /* 2026-07-14: PC/모바일 모두 "왼쪽 가운데 빨간 점"으로 통일되어(위 기본 규칙),
     예전의 모바일 전용 점 전환 규칙은 필요 없어졌습니다. 기본 규칙(absolute 점)이
     그대로 적용되도록 여기서는 아무것도 덮어쓰지 않습니다. */

  /* 2026-07-09 7차 추가 (Todd님 요청): 모바일 알림시간 패널에서 Save 버튼이 Start/End
     시간 선택 옆에 안 들어가서 다음 줄로 밀려나던 문제 - 글자를 숨기고 아이콘(💾)만
     남겨서 좁은 공간에도 한 줄로 들어가게 합니다. ::before의 '💾 ' 콘텐츠는 버튼의
     font-size를 그대로 상속하므로, 버튼은 0으로 줄이고 ::before만 다시 키워서
     아이콘만 보이게 합니다(글자 "Save" 텍스트 노드는 font-size:0이라 자리를 안 차지함). */
  #alertHoursMobileSlot .alert-hours-body form { display: flex; align-items: center; flex-wrap: nowrap; gap: 4px; }
  #alertHoursMobileSlot .alert-hours-body button[data-i18n="saveBtn"] {
    font-size: 0; padding: 6px 8px; min-width: 32px; flex-shrink: 0;
  }
  #alertHoursMobileSlot .alert-hours-body button[data-i18n="saveBtn"]::before { font-size: 16px; margin-right: 0; }

  /* 2026-07-09 7차 추가 (Todd님 요청): 실사결과 패널의 Save/Delete All 버튼도 같은
     이유로 아이콘만 남겨서, "Auto-delete ... after" 선택창(3h) 옆에 한 줄로 정렬되게
     합니다. */
  .verification-controls button[data-i18n="saveBtn"],
  .verification-controls button.danger {
    font-size: 0; padding: 6px 8px; min-width: 32px;
  }
  .verification-controls button[data-i18n="saveBtn"]::before,
  .verification-controls button.danger::before { font-size: 16px; margin-right: 0; }
  /* 2026-07-09 8차 추가 (Todd님 요청): 라벨을 자기 줄로 통째로 보내서(flex-basis:100%,
     .verification-controls는 이미 flex-wrap:wrap), 그 다음 줄에 select+Save+Delete All이
     한 줄로 나란히 들어갈 공간을 확보합니다. */
  .verification-controls .v-auto-delete-label { flex-basis: 100%; }

  /* 2026-07-09 7차 추가 (Todd님 제보): 섹터 카드의 상태뱃지+버튼 줄(.sector-row-actions)이
     desktop에선 flex-wrap:nowrap으로 한 줄을 강제하는데, 좁은 모바일 화면에서는 badge+
     버튼 3개가 한 줄에 다 안 들어가서 카드 테두리 밖으로 넘쳐버렸습니다. 모바일에서는
     다시 줄바꿈을 허용하고(이 div는 이미 "이름/러너" 줄, "지명" 줄과 분리된 자기만의
     줄이라, 줄바꿈돼도 다른 텍스트를 밀어내는 예전 문제는 재발하지 않음) 버튼도
     살짝 작게 줄여서 최대한 한 줄에 들어가도록 합니다. */
  .sector-row-actions { flex-wrap: wrap; }
  .sector-row-actions button { padding: 5px 8px; font-size: 13px; }
}

/* ============================================================================
   2026-07-15 (Todd님 요청) 패드(태블릿)/PC UI 보강 5건 - HANDOFF.md §31-12 참고
   ============================================================================ */

/* (1)(2)(3) 아이패드 등 태블릿 폭(761~1180px) 전용.
   문제: 아이패드에서 상단 타이틀("GU Collision Alert - Admin Dashboard")이 가로 폭
   부족으로 5줄까지 꺾여 보임. 폰(<=760px)은 기존 모바일 레이아웃 그대로, PC(>1180px)도
   변화 없음 - 이 구간에서만 가로 공간을 확보합니다. */
@media (min-width: 761px) and (max-width: 1180px) {
  /* (1) 타이틀 글자를 줄여 1~2줄 안에 들어가게 */
  .dashboard-topbar h1 { font-size: 19px; line-height: 1.25; }
  /* (2) 통계 라벨("Today's Accidents" 등)을 2줄로 꺾어 각 박스 가로 폭 축소 */
  .dashboard-topbar .stat-row .stat .label {
    white-space: normal; width: min-content; min-width: 52px; line-height: 1.25;
  }
  /* (3) Alert Hours 박스: 설정 시간 "(07:30 - 18:00)"을 다음 줄로 내려 박스 폭 축소
     (패드에서만 - PC는 기존 한 줄 유지, 폰은 이 슬롯 자체를 안 씀) */
  #alertHoursDesktopSlot .alert-hours-panel summary h3 .alert-hours-summary-time {
    display: block; margin-top: 2px;
  }
}

/* (4) 상세패널 CCTV 캡션 폴딩 (패드+PC, >=761px - 폰은 기존 그대로).
   카메라 설명 텍스트를 2줄까지만 보여주고 나머지는 접습니다(말줄임 "..." 표시).
   잘린 캡션을 클릭하면 전체 텍스트가 "오버레이"(.cctv-caption-full, dashboard.js가
   생성)로 캡션 위치에서 펼쳐집니다 - 절대배치라 아래 구글맵 경로 썸네일을 밑으로
   밀어내지 않고 그 위에 겹쳐서 열립니다. 다시 클릭하면 닫힙니다. */
@media (min-width: 761px) {
  .detail-panel .cctv-cell { position: relative; }
  .detail-panel .cctv-caption {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; cursor: pointer;
  }
  .detail-panel .cctv-caption-full {
    position: absolute; left: 0; right: 0; z-index: 40; cursor: pointer;
    font-size: 11px; line-height: 1.45; color: var(--text);
    background: var(--panel); border: 1px solid var(--border); border-radius: 6px;
    padding: 6px 8px; box-shadow: 0 8px 18px rgba(0, 0, 0, 0.55);
  }
}

/* ============================================================================
   2026-07-20 Phase A(알림 시점 스냅샷) + CCTV 월 링크 버튼 - HANDOFF.md §32 참고
   ============================================================================ */

/* 헤더 "CCTV 확인" 링크 버튼 (로그인 상태줄 안, /wall 새 탭) */
.wall-link {
  display: inline-block; background: rgba(31,111,235,0.18); border: 1px solid #1f6feb;
  color: var(--text); font-size: 12px; font-weight: 600; padding: 3px 10px;
  border-radius: 6px; text-decoration: none; white-space: nowrap;
}
.wall-link:hover { background: rgba(31,111,235,0.35); }

/* 상세패널 "알림 시점 스냅샷" 미니 그리드 (현재 CCTV 그리드 아래) */
.snapshot-title { margin-top: 8px; font-size: 11.5px; font-weight: 700; color: var(--muted); }
.snapshot-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: 4px; }
.snapshot-cell { display: block; border-radius: 6px; overflow: hidden; background: #0d1117;
                 aspect-ratio: 4 / 3; }
.snapshot-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* 2026-07-22 (Todd님 요청): 로고+타이틀을 링크로 감쌈 - 기존 배치/색 그대로 유지 */
.brand-link { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.brand-link h1 { color: inherit; }

/* 2026-07-22 (Todd님): CCTV 랭킹 칩의 짧은 설명 텍스트는 컴퓨터에서만 - 모바일은
   아이콘+건수만 (캐스케이드 원칙에 따라 모바일 규칙은 파일 끝에 위치). */
@media (max-width: 760px) {
  .cctv-kind-name { display: none; }
}

/* 2026-07-23 (Todd님): 메인 지도 Hotspot 12km/Orange 토글 (PC) - 헥사 사고 밀도 연동.
   주의: Leaflet이 터치 지원 브라우저에 .leaflet-touch를 붙이면
   ".leaflet-touch .leaflet-bar a { width:30px }"가 이겨서 텍스트가 잘림(실측) -
   같은 특이도 이상으로 재정의해야 함. */
.main-zone-toggle { display: flex; }
.leaflet-bar.main-zone-toggle a,
.leaflet-touch .leaflet-bar.main-zone-toggle a {
  width: auto; height: auto; padding: 6px 12px; line-height: 1.4;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.main-zone-toggle a.active { background: #2563eb; color: #fff; }
/* CCTV 버튼 짧은 라벨: PC에선 풀 라벨 */
.wall-btn-short { display: none; }
.lbl-short { display: none; }
/* 헥사 밀도 체크박스: PC에선 숨김 (지도 토글이 담당) */
.hex-toggle-mobile { display: none; align-items: center; gap: 7px; cursor: pointer; }
.hex-toggle-mobile input { width: 16px; height: 16px; accent-color: #f0883e; }

/* 2026-07-23 모바일: Active Recipients 칸 -> 사고 밀도 체크박스로 교체,
   지도 위 존 토글은 숨김 (캐스케이드 원칙: 모바일 규칙은 파일 끝) */
@media (max-width: 760px) {
  .stat-recipients { display: none; }
  .hex-toggle-mobile { display: flex; }
  .main-zone-toggle { display: none; }
}

/* 2026-07-23 (Todd님) 모바일 상단 정리 - 캐스케이드 원칙: 모바일 규칙은 파일 끝.
   1) 역할(developer) 숨김 2) CCTV Wall -> "CCTV" 3) CCTV+Log Out을 최상단 우측
   한 행으로 4) 통계 라벨 단축(Today's/Sent) + 세 박스 상하 높이 통일 */
@media (max-width: 760px) {
  .login-role { display: none; }
  .wall-btn-full { display: none; }
  .wall-btn-short { display: inline; }
  .dashboard-topbar { position: relative; }
  .top-actions { position: absolute; top: 6px; right: 8px; display: flex;
                 gap: 6px; align-items: center; z-index: 5; }
  .top-actions .wall-link { padding: 6px 10px; font-size: 12px; }
  .top-actions form.inline { margin: 0; }
  .top-actions button { white-space: nowrap; padding: 6px 10px; font-size: 12px; }
  .lbl-full { display: none; }
  .lbl-short { display: inline; }
  .stat-row { align-items: stretch; }
  .stat-row .stat { display: flex; flex-direction: column; justify-content: center; }
  .hex-toggle-mobile { align-items: center; }
}
@media (min-width: 761px) {
  .top-actions { display: contents; }
}

/* 2026-07-23 (Todd님) 모바일 간격 축소 + Active Recipients 제거 재수정.
   주의: 위 블록의 ".stat-row .stat { display:flex }"(0,2,0)가 ".stat-recipients
   { display:none }"(0,1,0)를 이겨서 칸이 다시 보였음(실측) - 특이도 높여 재정의. */
@media (max-width: 760px) {
  .dashboard-topbar .stat-row .stat.stat-recipients { display: none !important; }
  /* 4) 상단: 타이틀/버튼 행 <-> 통계 행 간격 최소화 */
  body { padding: 12px; }
  .login-status-row { margin: 2px 0; }
  .stat-row { margin-top: 6px; gap: 8px; }
  /* 5) 메인지도 <-> 사고알림 배너 간격 최소화 */
  .layout { margin-top: 8px; gap: 8px; }
  /* 6) 이하 모든 패널 박스 사이 간격 최소화 (상세정보/알림시간/수신자/섹터/
     Twilio/유저관리/실사결과/밀도지도/CCTV 통계까지) */
  .panel, details.panel { margin-top: 10px; padding: 12px; }
  .chart-panel { margin-top: 10px; }
  .site-footer { margin-top: 14px; }
}

/* 배차 대상 라벨 (2026-07-31, Todd님 요청: "이름 앞에 To: 를 넣어서 누구에게
   보내는 디스패치인지 정확히 표시") */
.dispatch-to {
  font-size: 11px; font-weight: bold; color: var(--muted);
  letter-spacing: 0.5px; margin-right: 2px; flex: 0 0 auto;
}
