/*
 * K-BIM-New 디자인 시스템 — Primavera P6 스타일 엔터프라이즈 테마.
 * 좌측 사이드바 + 상단바 + 밀도 높은 데이터 그리드. 원본 PBIX 추출 브랜드 컬러 유지.
 */
:root {
  /* 브랜드 (원본 PBIX 추출 ✅) */
  --navy: #0E2A47;
  --navy-2: #0B2038;
  /* 사이드바 심층 */
  --navy-3: #16385C;
  /* hover */
  --green: #22C55E;
  /* 실적/양호 */
  --plan: #64748B;
  /* 계획 */
  --muted: #94A3B8;
  /* 미착수/비활성 (차트 공통) */
  --danger: #EF4444;
  --warning: #F59E0B;
  --info: #3B82F6;
  --accent: #38BDF8;
  /* 사이드바 강조선 */

  /* 🚨 차트 색은 js/common/kbim.js 의 palette 가 이 변수들을 읽는다.
     아일랜드에 hex 를 다시 박지 말 것 — 테마 변경이 8파일로 흩어진다. */

  /* ── 신호색 ink 변형 — 글자·배지·얇은 선 전용 ─────────────────
     위 신호색(--green/--warning/--danger/--info)은 <b>면</b>(막대·차트 조각·게이지)에는
     적합하지만 흰 배경 위 <b>글자</b>로 쓰면 전부 WCAG 4.5:1 에 미달한다:
       --green 2.28:1 · --warning 2.15:1 · --danger 3.76:1 · --accent 2.14:1
     이 프로젝트의 주 지표인 실적공정률이 2.28:1 로 화면에서 가장 안 보이는 값이었다.
     ⚠️ 팔레트를 바꾸는 것이 아니다 — 면색은 그대로 두고, <b>글자·배지·선에만</b> 아래를 쓴다.
        (차트 series 색은 여전히 위 원본색을 쓴다. docs/11 §2-④) */
  --green-ink: #15803D;
  /* on white 5.02:1 · on surface-2 4.76:1 */
  --warning-ink: #B45309;
  /* 5.02 / 4.76 — .pb-item.is-pending 이 이미 쓰던 색 */
  --danger-ink: #B91C1C;
  /* 6.47 / 6.13 */
  --info-ink: #0369A1;
  /* 5.93 / 5.63 — 밝은 배경용. --accent 는 사이드바 전용 */

  /* 배지용 tint 배경 — "연한 배경 + 진한 ink" 조합.
     .hazard-chip(#FEF3C7/#92400E, 6.37:1)이 이미 쓰던 정답 패턴을 일반화한 것. */
  --tint-ok: #DCFCE7;
  --ink-ok: #166534;
  /* 6.49:1 */
  --tint-warn: #FEF3C7;
  --ink-warn: #92400E;
  /* 6.37:1 */
  --tint-crit: #FEE2E2;
  --ink-crit: #991B1B;
  /* 6.80:1 */
  --tint-none: #F1F5F9;
  --ink-none: #334155;
  /* 9.45:1 */

  /* 표면 */
  --bg: #EEF1F5;
  /* 콘텐츠 배경 */
  --surface: #FFFFFF;
  --surface-2: #F7F9FC;
  /* 테이블 헤더/줄무늬 */
  --border: #DCE3EC;
  --border-2: #E9EEF4;

  /* 텍스트 */
  --text: #1B2733;
  --text-2: #5B6B7B;
  --text-inv: #E8EEF6;

  --font: "Segoe UI", Arial, "Malgun Gothic", sans-serif;
  /* == 타이포 스케일 =====================================
     🚨 <b>여기가 폰트의 단일 출처다.</b> 2026-08-04 이전에는 font-size 선언 128건 중
        토큰 사용이 7건뿐이고 121건이 px 하드코딩(18종 난립)이었다 — 「글자를 키워 달라」가
        곧 121곳 개별 판단이었고, 되돌리려면 또 121곳이었다.
     🚨 <b>새 px 값을 직접 쓰지 말 것.</b> 아래 8단계 중에서 고른다. 맞는 단계가 없으면
        단계를 늘리는 것이 아니라 <b>왜 필요한지</b>를 먼저 따진다(18종이 그렇게 생겼다).
     🚨 --fs-micro 는 <b>존치용이다(신규 사용 금지)</b>. 문자 정보를 나르면 안 되고
        글리프에만 쓴다. 장기 목표는 사용처 0건.
     발주처 피드백(2026-08-04 「화면 글자가 작아 보기 힘들다」)으로 각 단계를 상향했다.
     🚨 lead 22→20, hero 34→32 는 <b>축소</b>다 — 본문이 커진 만큼 위계 비율을 맞췄다. */
  --fs-micro: 11px;
  /* 글리프 전용 — 문자 정보 금지 */
  --fs-xs: 12px;
  /* 표 안 코드·단위 — 정보를 나르는 최소값 */
  --fs-sm: 13px;
  /* 보조 라벨·배지·부제 */
  --fs: 14px;
  /* 본문·표 셀·폼·버튼 */
  --fs-group: 15px;
  /* 그룹 소제목 · 사이드바 메뉴 */
  --fs-title: 17px;
  /* 패널 제목 */
  --fs-lead: 20px;
  /* 리드 수치 */
  --fs-hero: 32px;
  /* 판정 주값(.kp-value) */
  /* 🚨 구 토큰 별칭 — 사용처 7곳을 한꺼번에 고치지 않기 위해 남긴다. 신규 사용 금지. */
  --fs-sub: var(--fs-sm);
  /* 제목 옆 부제(기준·범위 표기) */
  --radius: 6px;
  --shadow: 0 1px 2px rgba(15, 23, 42, .06), 0 1px 3px rgba(15, 23, 42, .05);
  --sidebar-w: 216px;
  /* 🚨 헤더 밴드가 <b>문서 흐름에서 차지하는 총 높이</b>.
     border-bottom:2px 를 <b>더하지 않는다</b> — 전역 `* { box-sizing:border-box }` 때문에
     .appbar 의 height:68px 에 보더가 이미 포함된다(실측 확인). 70 으로 잡으면 2px 이 뜬다.
     .appbar 는 sticky 라 흐름에서 빠지지 않으므로, 그 아래에서 100vh 를 요구하는 요소는
     이 값을 반드시 빼야 한다(안 빼면 문서 최소 높이가 100vh+68 로 고정된다).
     (구 --topbar-h:52px 를 대체 — 정의만 있고 사용처 0건이었다.
      실제 값 68 은 .sidebar 2곳에 하드코딩돼 있었다. 셋을 여기로 모은다.) */
  /* 🚨 68 → 58(홈 세로 확보) → 64(2026-08-04, 발주처 CI 확대 지시).
     64 인 이유: .ab-center 세로 스택 [CI 38 + margin 2 + 공사명 19] = 59px 이고
     안쪽이 62px 이라 여유 3px 이 남는다. 62 로 잡으면 여유가 1px 뿐이라
     공사명이 한 글자만 길어져도 밴드를 밀어낸다.
     🚨 이 값을 올리면 <b>전 화면 세로를 그만큼 먹는다</b> — .app-shell/.sidebar 가
     같은 토큰을 쓴다. 홈은 이미 예산 초과 상태라 여기서 6px 이 그대로 더해진다. */
  --appbar-h: 64px;
}

* {
  box-sizing: border-box;
}

/*
 * 🚨 height 가 아니라 min-height 여야 한다.
 *    body 높이를 100% 로 <b>고정</b>하면 sticky 요소의 컨테이닝 블록이 뷰포트 한 화면에서 끝난다.
 *    그러면 한 화면을 넘겨 스크롤한 순간 <b>상단바(.appbar)와 사이드바(.sidebar)가 붙어 있지 못하고
 *    함께 밀려 올라간다</b> — 짧은 화면에서는 멀쩡해 보이고 <b>긴 화면에서만</b> 드러난다
 *    (현장 설정 화면에서 실제로 발견).
 *    min-height 면 body 가 콘텐츠만큼 자라 sticky 가 문서 끝까지 유지된다.
 *    전체 높이 레이아웃은 .app-shell{min-height:100vh} 가 이미 책임진다.
 */
html,
body {
  min-height: 100%;
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--info-ink);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* ══ 앱 셸 ═══════════════════════════════════════════════ */
/* 🚨 <b>100vh 가 아니라 100vh − 헤더</b>다(2026-08-04 수정).
   .appbar 는 position:sticky 라 <b>문서 흐름을 그대로 차지한다</b>(68px).
   그 아래에서 100vh 를 요구하면 <b>문서 최소 높이가 100vh+68 로 고정</b>되어,
   콘텐츠를 아무리 줄여도 그 아래로는 절대 내려가지 않는다 —
   즉 이 줄이 있는 한 <b>스크롤 0 은 수학적으로 불가능</b>했다.
   (노선도를 통째로 지워도 68px 은 그대로 남는다. 콘텐츠 문제가 아니었다.)
   바로 아래 .sidebar 는 같은 값을 이미 빼고 있었다 — 셸만 빠져 있었다. */
.app-shell {
  display: flex;
  min-height: calc(100vh - var(--appbar-h));
}

/* 사이드바 */
.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  background: var(--navy-2);
  color: var(--text-inv);
  display: flex;
  flex-direction: column;
  position: sticky;
  /* 헤더 밴드 아래 고정. 값(68)은 종전과 동일하며 --appbar-h 로 단일화만 했다. */
  top: var(--appbar-h);
  height: calc(100vh - var(--appbar-h));
}

.sidebar-brand {
  padding: 14px 16px;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}

.sidebar-brand .b-title {
  font-size: var(--fs-group);
  font-weight: 700;
  letter-spacing: .2px;
}

.sidebar-brand .b-sub {
  font-size: var(--fs-sm);
  color: #7E93AB;
  margin-top: 3px;
}

/*
 * 🚨 min-height: 0 이 <b>반드시</b> 있어야 한다.
 *    .sidebar 는 display:flex; flex-direction:column 에 고정 높이(calc(100vh - var(--appbar-h)))다.
 *    컬럼 flex 아이템의 기본 min-height 는 auto 라, 이 값이 없으면 .nav 가
 *    <b>콘텐츠 높이 아래로 줄어들지 못한다</b> → overflow-y:auto 가 발동하지 않고
 *    메뉴가 사이드바 높이를 넘어 <b>잘린다</b>(스크롤도 안 된다).
 *    메뉴가 10개로 늘고 화면이 짧아지자 실제로 하위 메뉴에 접근할 수 없게 됐다.
 */
.nav {
  padding: 14px 0 8px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* 어두운 사이드바에서 기본 스크롤바는 보이지 않는다 — 스크롤 가능함을 알 수 있게 한다. */
.nav::-webkit-scrollbar {
  width: 8px;
}

.nav::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .18);
  border-radius: 4px;
}

.nav::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, .30);
}

.nav::-webkit-scrollbar-track {
  background: transparent;
}

.nav {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .18) transparent;
}

/* 🚨 사이드바 그룹 헤더 — 정의만 있고 <b>사용처 0건</b>이던 것을 2026-08-04 에 켰다
   (관리자 메뉴를 1뎁스에서 분리하라는 지시. 새 규칙을 만들지 않고 있는 것을 쓴다).
   켜면서 두 가지를 고쳤다 — 원래 <b>영문 라벨 전제</b>로 만들어진 규칙이었다:
     · color #5E738C 는 --navy-2(#0B2038) 위에서 대비 <b>3.37:1</b> 이었다. 12px 본문이라
       WCAG AA(4.5:1) 미달이고, 발주처가 보는 화면에 접근성 미달 요소를 새로 켜는 셈이다.
       #8FA3B8 로 올려 약 5.6:1 을 확보했다.
     · text-transform:uppercase 는 한글에 <b>무효</b>이고, letter-spacing .8px 는 한글에서
       자간이 벌어져 어색하다. 한글 라벨을 쓰므로 .2px 로 줄이고 uppercase 는 뺐다.
   🚨 <b>헤더는 1개까지다.</b> 1366×768 노트북에서 .nav 가용 높이가 약 485px 인데
      항목 11개(418) + 헤더 1개(32) = 450 으로 여유 35px 다. 2개면 여유가 3px 로 떨어져
      .nav 가 스크롤된다 — 「메뉴가 늘어 하위 항목에 접근할 수 없게 됐다」는 사고가 재발한다. */
.nav-group {
  padding: 12px 16px 4px;
  font-size: var(--fs-xs);
  letter-spacing: .2px;
  font-weight: 700;
  color: #8FA3B8;
  border-top: 1px solid rgba(255, 255, 255, .10);
  margin-top: 8px;
}

/* 🚨 글자를 키우고 진하게 했다(13px/400/#C4D2E2 → 14.5px/600/#DCE7F3, 2026-08-04 지시).
   회의실 대형화면·프로젝터에서 메뉴가 가장 먼저 안 읽히는 요소였다.
   색까지 같이 올린 이유: 굵기만 올리면 남색 판 위에서 #C4D2E2 가 번져 보인다.
   🚨 사이드바 폭은 216px(--sidebar-w) 고정이다. 가장 긴 항목 「작업일보 업로드」가
   아이콘·여백 포함 약 174px 이라 14.5px 까지는 한 줄에 들어간다 — 더 키우면 줄바꿈된다.
   🚨 폰트 전면 확대(P1 토큰화) 때 이 값도 --fs-* 토큰으로 흡수한다. 그 전까지는
   여기가 유일한 사이드바 폰트 출처다. */
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 16px;
  color: #DCE7F3;
  font-size: var(--fs-group);
  font-weight: 600;
  letter-spacing: -.2px;
  border-left: 3px solid transparent;
  cursor: pointer;
}

.nav-item:hover {
  background: var(--navy-3);
  color: #fff;
  text-decoration: none;
}

/* 🚨 기본이 600 으로 올라갔으므로 활성은 700 이어야 구분이 남는다
   (둘 다 600 이면 「지금 어느 화면인가」를 배경색 하나로만 판단하게 된다). */
.nav-item.active {
  background: var(--navy-3);
  color: #fff;
  border-left-color: var(--accent);
  font-weight: 700;
}

/* 글자를 키운 만큼 아이콘도 같이 올린다 — 안 올리면 아이콘만 작아 줄이 어긋나 보인다. */
.nav-item .ic {
  width: 18px;
  text-align: center;
  font-size: var(--fs-title);
  opacity: .9;
}

/* 🚨 그룹 하위 항목 — .nav-group 과 함께 2026-08-04 에 처음 켰다(그전까지 사용처 0건).
   위계는 <b>들여쓰기 + 한 단계 작은 글자</b>로 만든다(15px → 14px).
   🚨 색을 너무 낮추지 말 것. 원래 #A9BCD1 이었는데 상위 항목(#DCE7F3)이 발주처 피드백으로
      밝아진 뒤라, 그대로 두면 <b>비활성 항목처럼</b> 보인다. #C3D2E2 로 올렸다(대비 약 6.9:1).
   🚨 활성 상태(.nav-item.active)는 색을 #fff 로 덮으므로 여기 순서에 영향받지 않는다. */
.nav-item.sub {
  font-size: var(--fs);
  padding-left: 26px;
  color: #C3D2E2;
}

.nav-item.todo {
  opacity: .45;
}

/* 🚨 flex-shrink:0 — 메뉴가 길어지면 푸터가 눌려 사라지던 것을 막는다. */
.sidebar-foot {
  flex-shrink: 0;
  padding: 12px 16px;
  border-top: 1px solid rgba(255, 255, 255, .08);
  font-size: var(--fs-sm);
  color: #9FB2C7;
  line-height: 1.5;
}

/* 🚨 로고(95×36 KCMC 워드마크)의 'K' 가 진한 남색이라 사이드바(#0B2038) 위에서 묻힌다.
      밝은 판에 올려 <b>브랜드 색을 건드리지 않고</b> 대비를 확보한다 —
      filter: brightness 로 띄우면 로고 색이 왜곡되고, 그건 CI 훼손이다. */
.sidebar-foot .sf-logo {
  height: 15px;
  width: auto;
  vertical-align: -3px;
  margin-right: 5px;
  padding: 2px 4px;
  border-radius: 3px;
}

/* 구 마크(|||) 규칙 — 남은 참조가 있어도 깨지지 않게 유지 */
.sidebar-foot .sf-mark {
  color: var(--accent);
  font-weight: 700;
  letter-spacing: -1px;
  margin-right: 4px;
}

.sidebar-foot .sf-sub {
  font-size: var(--fs-micro);
  color: #5E738C;
  letter-spacing: -.2px;
}

/* 메인 */
.app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.app-content {
  /* 🚨 세로만 조인다(18 → 10). 가로 20 은 유지 — 줄이면 노선도 폭이 늘어
     4.13:1 이라 <b>세로가 되레 커진다</b>(가로 8px 이 세로 2px 로 돌아온다). */
  padding: 6px 20px;
  flex: 1;
}

/* ══ 헤더 밴드 (원본 Power BI 1행 구조) ═══════════════════
   [브랜드+프로젝트] [날짜 슬라이서] [공정률 3값] [CI+공사명] [기상 4값] [시공사]
   값은 전부 project_ref / kpi.json 에서 온다 — JSP 하드코딩 금지. */
.appbar {
  display: flex;
  align-items: center;
  gap: 18px;
  /* 🚨 값은 --appbar-h 하나로 잡는다 — .app-shell/.sidebar 가 같은 값을 쓴다.
     68 → 58 (2026-08-04, 홈 세로 확보 −10px). 내부 최대 요소는 날짜 input(약 28px)이라
     58 에서도 위아래 15px 여유가 남는다. 더 낮추면 input 이 밴드를 밀어낸다. */
  height: var(--appbar-h);
  padding: 0 18px;
  background: var(--surface);
  border-bottom: 2px solid var(--navy);
  position: sticky;
  top: 0;
  z-index: 20;
}

.ab-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.ab-kcmc {
  font-size: var(--fs-title);
  font-weight: 300;
  color: var(--navy);
  letter-spacing: .5px;
}

.ab-kcmc b {
  font-weight: 800;
}

.ab-proj {
  font-size: var(--fs-sm);
  max-width: 150px;
  padding: 3px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface-2);
}

/* 날짜 슬라이서 — UI 가 아니라 전 화면 데이터 컨텍스트(종료일 = 기준일) */
.ab-period {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
}

.ab-period input[type="date"] {
  font-size: var(--fs-sm);
  padding: 4px 6px;
  width: 128px;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.ab-tilde {
  color: var(--text-2);
  font-size: var(--fs-sm);
}

.ab-apply {
  padding: 4px 10px;
  font-size: var(--fs-sm);
}

/* 기간 상태 칩 — 입력칸에 찬 값이 <b>기본값</b>인지 <b>사용자 선택</b>인지 밝힌다.
   이 구분이 없으면 채워진 날짜를 보고 "누가 이 기간을 골랐지?"를 알 수 없다. */
.ab-pstate {
  font-size: var(--fs-sm);
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--tint-none);
  color: var(--ink-none);
  white-space: nowrap;
}

.ab-pstate.is-set {
  background: var(--tint-warn);
  color: var(--ink-warn);
}

.ab-reset {
  color: var(--text-2);
  font-size: var(--fs);
  padding: 0 2px;
  text-decoration: none;
}

.ab-reset:hover {
  color: var(--danger-ink);
  text-decoration: none;
}

/* 공정률 3값 / 기상 4값 — 라벨 위 · 값 아래 (원본 배치) */
.ab-metrics,
.ab-weather {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-shrink: 0;
}

.ab-m,
.ab-w {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.25;
}

.ab-w {
  flex-direction: row;
  align-items: center;
  gap: 6px;
}

.ab-w img {
  width: 22px;
  height: 22px;
  object-fit: contain;
}

.ab-w>span {
  display: block;
}

.ab-w .ab-m-l,
.ab-w .ab-m-v {
  text-align: left;
}

.ab-m-l {
  font-size: var(--fs-xs);
  color: var(--text-2);
  white-space: nowrap;
}

.ab-m-v {
  font-size: var(--fs-group);
  font-weight: 700;
  color: var(--navy);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.ab-m-v.is-actual {
  color: var(--green-ink);
}

/* 중앙: 발주처 CI + 공사명 (원본은 캔버스 정중앙 정렬) */
.ab-center {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* 🚨 발주처 CI — 30 → 38px (2026-08-04 지시, --appbar-h 58→64 와 한 묶음).
   🚨 <b>38 이 이 자산의 천장이다.</b> 원본이 185×38px 이라 38 을 넘기면 확대되어 흐려진다.
      더 크게 하려면 <b>고해상도 CI 파일로 교체</b>해야 한다 — 밴드만 키워도 소용없다.
      (현장 설정 화면에서 「발주처 CI」로 업로드하면 코드 수정 없이 바뀐다.)
   🚨 밴드 높이와 <b>연동된다.</b> .ab-center 는 [CI + 공사명] 세로 스택이라
      CI 38 + margin 2 + 공사명 19 = 59px 가 필요하고, --appbar-h 64 의 안쪽이 62px 다.
      밴드를 다시 낮추면 공사명이 먼저 잘린다(58px 에서 실측으로 확인). */
.ab-ci {
  height: 38px;
  object-fit: contain;
}

.ab-title {
  font-size: var(--fs);
  font-weight: 700;
  color: var(--navy);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.ab-right {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

.ab-logo-c {
  height: 26px;
  object-fit: contain;
}

/* 🚨 헤더 6요소 중 5개가 flex-shrink:0 이라 폭이 모자라면 문서 전체에 가로 스크롤이 생겼다.
      실측 최소폭 약 1,200px — 노트북 1366 에서 여유가 160px 뿐이었다.
      좁아질수록 정보 밀도가 낮은 것부터 접는다: 기상 라벨 → 아이콘 → 슬라이서 폭 → 기상 전체. */
@media (max-width: 1500px) {

  /* 좁은 화면에서는 기상 라벨을 접어 헤더 1행을 유지한다(줄바꿈보다 낫다) */
  .ab-weather .ab-m-l {
    display: none;
  }

  .ab-weather {
    gap: 10px;
  }
}

@media (max-width: 1360px) {
  .appbar {
    gap: 12px;
    padding: 0 12px;
  }

  .ab-pstate {
    display: none;
  }

  /* 값이 칩보다 우선이다 */
  .ab-period input[type="date"] {
    width: 112px;
  }

  .ab-weather img {
    display: none;
  }

  /* 아이콘보다 숫자가 중요하다 */
  .ab-metrics {
    gap: 10px;
  }
}

@media (max-width: 1180px) {

  /* 기상 4값은 일일보고·주간보고에 있다 — 헤더에서 먼저 내린다.
     공정률 3값과 기간 슬라이서는 <b>전 화면 데이터 컨텍스트</b>라 끝까지 남긴다. */
  .ab-weather {
    display: none;
  }

  .ab-ci {
    display: none;
  }
}

/* ══ 공사개요 띠 (참여조직·공기 경과율 — 원본에 없는 신규) ═══ */
.projbar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  /* 세로 7 → 3 (2026-08-04, 홈 세로 확보). 가로 20 은 .app-content 와 맞춘다. */
  padding: 3px 20px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}

.projbar .pb-crumb b {
  font-size: var(--fs-group);
  font-weight: 700;
  color: var(--navy);
}

.projbar .pb-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  flex: 1;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.projbar .pb-item b {
  color: var(--text);
  font-weight: 700;
}

.projbar .pb-item.is-pending {
  color: #B45309;
  font-weight: 600;
}

.projbar .spacer {
  flex: 1;
}

.projbar .chip {
  font-size: var(--fs-sm);
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 12px;
}

.projbar .pb-gauge {
  display: inline-block;
  width: 110px;
  height: 7px;
  background: #E7ECF3;
  border-radius: 4px;
  overflow: hidden;
}

.projbar .pb-gauge-fill {
  display: block;
  height: 100%;
  background: var(--navy-3);
  border-radius: 4px;
}

/* ══ 카드/패널 ═══════════════════════════════════════════ */
.panel,
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  /* 세로 11 → 9 (2026-08-04, 홈 세로 확보 −8px 실측).
     🚨 가로 14 는 건드리지 말 것 — 노선도 폭이 늘어 세로가 되레 커진다(4.13:1). */
  padding: 9px 14px;
  /* 🚨 홈을 한 화면에 담기 위해 조였다(16 → 10 → 4). 패널이 세로로 쌓이는 화면이라
     한 곳당 몇 px 이 배로 돌아온다. */
  margin-bottom: 4px;
}

/* 🚨 제목은 <b>구분선 하나</b>로만 표시되고 있었고, 그 선이 --border-2(대비 1.17:1)라
   사실상 보이지 않았다. 섹션 경계가 없으니 화면을 훑을 때 "여기서부터 다른 이야기"라는
   신호가 없었다(2026-07-31 총괄 지적).
   → <b>좌측 액센트 바</b>를 붙인다. 보고서 절 제목(.rpt-h2)이 이미 쓰는 언어라
     화면과 인쇄물의 제목 표현이 같아진다. */
.panel-title {
  font-size: var(--fs-title);
  font-weight: 700;
  color: var(--navy);
  /* 🚨 세로 여백 조임(margin 8→5, padding-bottom 6→4). 홈에 제목이 3개라 9px.
     좌측 액센트 바(::before)의 bottom 값도 <b>같이</b> 줄여야 한다 — 안 그러면
     바가 밑줄에서 떠 보인다(11 → 9). */
  margin: 0 0 3px;
  padding: 1px 0 3px 11px;
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
  letter-spacing: -.2px;
}

/* 🚨 제목줄에 칩(.route-cap)까지 들어오는 패널 전용.
   .panel-sub 와 .route-cap 이 <b>둘 다</b> margin-left:auto 면 남는 가로를 나눠 가져
   부제가 제목에서 떨어져 한가운데 뜬다(실측). 부제는 제목에 붙이고,
   남는 가로는 전부 칩이 가져가게 한다. */
.panel-title.has-cap>.panel-sub {
  margin-left: 8px;
}

.panel-title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 2px;
  /* .panel-title 의 padding-bottom 과 연동된다(6→3 으로 줄였으므로 11→7). */
  bottom: 7px;
  width: 3px;
  border-radius: 2px;
  background: var(--navy);
}

/* 부제 — 기준·범위를 밝히는 보조 문구. 제목과 같은 줄 우측. */
.panel-sub {
  font-size: var(--fs-sub);
  color: var(--text-2);
  font-weight: 500;
  letter-spacing: 0;
}

/* ══ KPI ════════════════════════════════════════════════ */

/* (구) KPI 카드 그리드 — 판정 패널(.kpi-panels)로 대체돼 제거됨. 2026-07-30 */
/*
   나열은 "지표 4분류"라는 구조를 지우고, 인접 공구의 최대 결함(기준 혼재)을 우리가 재현하게 된다. */
/* tabular-nums: 카드가 나란히 놓일 때 자릿수가 흔들리지 않게 한다(폰트 폴백 대비 우측정렬 병행) */

/* ══ 데이터 그리드 (Primavera 밀도) ═════════════════════ */
/* table.grid 는 사용처가 없어 제거(2026-07-30) */
.issues-table,
.cbs-table,
.rpt-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs);
}

.issues-table th,
.cbs-table th,
.rpt-table th,
.issues-table td,
.cbs-table td,
.rpt-table td {
  border: 1px solid var(--border-2);
  padding: 5px 9px;
}

/* 🚨 머리행은 .panel-list 안에서 <b>스티키</b>로 떠 있다 — 본문 위로 겹칠 때
   경계가 없으면 어디까지가 머리행인지 알 수 없다. 하단 경계를 명시하고 글자도 진하게. */
.issues-table th,
.cbs-table th,
.rpt-table thead th {
  background: var(--surface-2);
  color: var(--text);
  text-align: left;
  font-weight: 700;
  border-bottom: 2px solid var(--border);
}

.issues-table tbody tr:hover,
.cbs-table tbody tr:hover {
  background: #F4F8FF;
}

.cbs-table th:not(:first-child),
.cbs-table td.amt,
.rpt-table td.amt {
  text-align: right;
}

.amt {
  font-variant-numeric: tabular-nums;
}

/* ── 컬럼 폭 조절(엑셀식) ─────────────────────────────────
   머리행 오른쪽 경계에 붙는 잡이. 🚨 보이지 않아도 커서로는 잡히도록
   너비를 주되 색은 hover 때만 드러낸다 — 표 격자에 선이 하나 더 있는 것처럼
   보이면 어디까지가 칸 경계인지 오히려 헷갈린다. */
/* 🚨 <b>손잡이는 칸 안쪽에 완전히 들어와야 한다</b>(right:0). 경계에 걸치게 두었더니
   (right:-3px) 튀어나온 부분을 <b>옆 칸이 덮어</b> 정확히 그 자리에서 클릭이 옆 칸의
   th 로 갔다 — 머리행이 sticky 라 각 칸이 위치 지정 요소이고, DOM 순서상 뒤엣것이
   위에 그려지기 때문이다. 헤드리스 드래그로 재현했고(elementFromPoint → TH),
   폭이 1px 도 변하지 않았다. */
.col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  cursor: col-resize;
  user-select: none;
  touch-action: none;
  z-index: 3;
}

.col-resizer:hover,
body.col-resizing .col-resizer {
  background: var(--accent, #2563EB);
  opacity: .55;
}

/* 드래그 중에는 문서 전체에서 선택·커서를 고정한다 — 표 밖으로 나가도 유지되게 */
body.col-resizing {
  cursor: col-resize;
  user-select: none;
}

@media print {
  .col-resizer {
    display: none;
  }
}

/* ══ 로그인 · 비밀번호 변경 ═══════════════════════════════
   🚨 공용 셸을 쓰지 않는 두 화면이다(로그인 전에는 사이드바·현장 셀렉터가 의미 없다). */
.login-body {
  min-height: 100vh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, var(--navy, #0B2038) 0%, #16375C 100%);
}

.login-card {
  width: 360px;
  max-width: calc(100vw - 32px);
  background: var(--surface, #fff);
  border-radius: 8px;
  padding: 28px 26px 20px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .28);
}

.login-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
}

.login-brand img {
  height: 30px;
}

.login-brand b {
  display: block;
  font-size: var(--fs-title);
  color: var(--navy, #0B2038);
}

.login-brand span {
  font-size: var(--fs-sm);
  color: var(--text-2, #64748B);
}

.login-field {
  display: block;
  margin-bottom: 12px;
}

.login-field>span {
  display: block;
  font-size: var(--fs);
  font-weight: 600;
  color: var(--text-2, #64748B);
  margin-bottom: 4px;
}

.login-field>input {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 10px;
  font-size: var(--fs-group);
  border: 1px solid var(--border, #CBD5E1);
  border-radius: 4px;
}

.login-field>input:focus {
  outline: none;
  border-color: var(--navy, #0B2038);
  box-shadow: 0 0 0 2px rgba(11, 32, 56, .12);
}

.login-btn {
  width: 100%;
  padding: 10px;
  margin-top: 6px;
  font-size: var(--fs-group);
  font-weight: 700;
  color: #fff;
  background: var(--navy, #0B2038);
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}

.login-btn:disabled {
  opacity: .6;
  cursor: default;
}

/* 🚨 오류 자리를 미리 잡아 둔다 — 나중에 삽입하면 카드 높이가 튀어
   사용자가 방금 무엇을 눌렀는지 놓친다. */
.login-msg {
  min-height: 18px;
  margin: 10px 0 0;
  font-size: var(--fs);
  color: var(--red, #DC2626);
}

.login-msg.is-ok {
  color: var(--green, #16A34A);
}

.login-note {
  margin: 0 0 14px;
  padding: 8px 10px;
  font-size: var(--fs);
  line-height: 1.6;
  background: #FFFBEB;
  border-left: 3px solid #F59E0B;
  color: var(--text, #0F172A);
}

.login-hint {
  margin: -4px 0 8px;
  font-size: var(--fs-sm);
  color: var(--text-2, #64748B);
}

.login-foot {
  margin: 16px 0 0;
  font-size: var(--fs-sm);
  text-align: center;
  color: var(--text-2, #64748B);
}

/* ── 상단바 로그인 사용자 ─────────────────────────────── */
.ab-user {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 10px;
  font-size: var(--fs-sm);
  white-space: nowrap;
}

/* 🚨 .au-name / .au-role / .au-admin / .au-editor / .au-viewer 5개 규칙을 제거했다
   (2026-08-04). 상단바의 사용자명·권한 배지를 총괄 지시로 내리면서 마크업이 사라졌고,
   CSS 만 사용처 0건으로 남아 있었다 — 구 --topbar-h 와 같은 경우다.
   🚨 배지를 되살리려면 이 규칙들도 함께 복원해야 한다. 「누구로 보고 있나」는 현재
      로그인 화면과 사용자 관리 화면이 답한다. */
.au-out {
  color: inherit;
  text-decoration: none;
  border: 1px solid currentColor;
  border-radius: 3px;
  padding: 1px 6px;
  opacity: .75;
}

.au-out:hover {
  opacity: 1;
}

/* 전환할 현장이 하나뿐이면 셀렉터를 잠근다 — 전환 UI 가 있는데 안 되는 상태를 만들지 않는다. */
.ab-proj.is-single {
  appearance: none;
  background-image: none;
  cursor: default;
  opacity: .9;
}

/* 임시 비밀번호 — 한 번만 보이므로 눈에 띄고 선택하기 쉬워야 한다. */
.temp-pw {
  display: inline-block;
  padding: 2px 8px;
  margin: 0 2px;
  font-size: var(--fs-group);
  font-weight: 700;
  letter-spacing: .5px;
  background: #FEF3C7;
  border: 1px solid #F59E0B;
  border-radius: 3px;
  user-select: all;
}

/* ── 접이식 도움말 ────────────────────────────────────────
   🚨 긴 안내를 지우지 않고 접는다. 매번 읽을 내용은 아니지만 처음 쓰는 사람에게는
   필요하고, 상주 문구로 두면 정작 중요한 한 줄이 묻힌다. */
.kbim-help {
  margin: 6px 0 0;
  font-size: var(--fs);
  color: var(--text-2, #64748B);
}

.kbim-help>summary {
  cursor: pointer;
  color: var(--accent, #2563EB);
  font-weight: 600;
  width: fit-content;
}

.kbim-help ul {
  margin: 6px 0 0;
  padding-left: 18px;
  line-height: 1.7;
}

@media print {
  .kbim-help {
    display: none;
  }
}

.issues-table tr.lv-ERROR td {
  background: #FEF2F2;
}

.issue-msg {
  color: var(--text);
}

.cbs-name {
  color: var(--text);
}

.cbs-code {
  color: var(--text-2);
  font-size: var(--fs-xs);
  margin-left: 6px;
}

/* CBS 펼침/접기 토글 — 리프도 같은 폭을 차지해 들여쓰기가 흔들리지 않게 한다 */
.cbs-tog {
  display: inline-block;
  width: 16px;
  margin-right: 4px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--text-2);
  font-size: var(--fs-micro);
  cursor: pointer;
  text-align: left;
  line-height: 1;
}

.cbs-tog:hover {
  color: var(--navy);
  background: none;
}

.cbs-tog.is-leaf {
  cursor: default;
}

.row-nonadd {
  background: #FBFAF3;
}

.mp-total td {
  font-weight: 700;
  background: var(--surface-2);
  color: var(--navy);
}

/* 배지 — "tint 배경 + ink 글자".
   흰 글자 + 신호색 배경(구 방식)은 2.15~3.76:1 로 전부 대비 미달이었고,
   인쇄물에서 배경이 안 찍히면 흰 글자만 남아 <b>판정이 통째로 사라졌다</b>. */
.badge,
.node-badge {
  display: inline-block;
  border-radius: 999px;
  font-weight: 700;
}

.badge {
  padding: 2px 9px;
  font-size: var(--fs-sm);
}

.node-badge {
  padding: 1px 7px;
  font-size: var(--fs-xs);
  margin-left: 4px;
}

.badge-OK {
  background: var(--tint-ok);
  color: var(--ink-ok);
}

.badge-ERROR {
  background: var(--tint-crit);
  color: var(--ink-crit);
}

.badge-WARN {
  background: var(--tint-warn);
  color: var(--ink-warn);
}

.nb-sum {
  background: var(--tint-none);
  color: var(--ink-none);
}

.nb-tax {
  background: var(--tint-warn);
  color: var(--ink-warn);
}

.nb-est {
  background: #E0E7FF;
  color: #3730A3;
}

/* 7.06:1 */
.hazard-chip {
  display: inline-block;
  padding: 4px 11px;
  margin: 3px;
  border-radius: 999px;
  background: #FEF3C7;
  color: #92400E;
  font-size: var(--fs-sm);
  font-weight: 700;
}

/* ══ 판정(신호등)·증감 ═══════════════════════════════════
   🚨 이 블록이 화면에서 "좋다/나쁘다"를 말하는 유일한 곳이다.
      개선 전에는 판정 배지 10.5px · 좌측선 3px(대비 2.15:1)로,
      <b>불변 상수인 계약금액(24px)보다 작고 흐렸다</b> — 위계가 거꾸로였다. */
.grade-badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: var(--fs);
  font-weight: 700;
  vertical-align: middle;
}

.gb-ok {
  background: var(--tint-ok);
  color: var(--ink-ok);
}

.gb-warn {
  background: var(--tint-warn);
  color: var(--ink-warn);
}

.gb-crit {
  background: var(--tint-crit);
  color: var(--ink-crit);
}

.gb-none {
  background: var(--tint-none);
  color: var(--ink-none);
}

.kpi-delta {
  font-size: var(--fs-sm);
  font-weight: 700;
  margin-left: 6px;
  font-variant-numeric: tabular-nums;
}

.dt-up {
  color: var(--green-ink);
}

.dt-down {
  color: var(--danger-ink);
}

.dt-flat {
  color: var(--text-2);
}

/* 지연일수 등 판정 근거 — "며칠 늦었나"는 한국 건설 보고의 헤드라인인데
   11px 회색이라 화면에서 가장 작은 글자였다(실측 236일). */

/* 좌측 판정선 — 3px/2.15:1 → 4px/ink 로 비텍스트 3:1 확보 */

/* ── KPI 3단 위계 ─────────────────────────────────────────
   모든 카드가 24px/700/navy 로 같은 무게였다. 그래서 준공까지 변하지 않는
   계약금액(2,445.985억)과 유일한 판정 지표인 대비공정률이 픽셀 단위로 동일했다.
     is-primary … 판정 지표(대비공정률·기성률·착수율)   → 크게
     (기본)      … 일반 지표
     is-ref      … 참조 상수(계약금액·총 활동 수)        → 작고 흐리게        */

/* ══ 판정 패널 — 화면 상단 요약의 단일 규범 ═══════════════
   카드 9장 나열(세로 290px / 실제 정보 3개)을 대체한다.
   구조: 제목+판정 → 주값(크게) → 산식 → 경고 → 근거(인라인).
   숫자를 줄이는 것이 목적이 아니라 <b>읽는 순서</b>를 만드는 것이 목적이다. */
/* 🚨 로딩 중 높이를 예약한다 — 예약이 없으면 렌더 순간 아래 콘텐츠가 통째로 밀린다(CLS). */
.kpi-panels {
  /* 🚨 홈을 한 화면에 담기 위해 낮췄다(168 → 132 → 130).
     124 는 <b>실측 렌더 높이</b>다(2026-08-04, .kpi-panel 패딩 11 기준). 예약값이 실제보다
     크면 그 차이가 그대로 빈 여백으로 남는다 — 예약은 CLS 방지용이지 여백용이 아니다.
     🚨 .kpi-panel 패딩을 바꾸면 이 값도 <b>같이</b> 재야 한다. */
  min-height: 124px;
}

.kpi-panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 10px;
  margin-bottom: 6px;
}

.kpi-panel {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  /* 세로 14 → 11 (2026-08-04, 홈 세로 확보). 가로 16 유지 — 줄이면 판정 3분할이 좁아진다. */
  padding: 11px 16px;
}

.kpi-panel::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--navy);
}

.kpi-panel.is-ok::before {
  background: var(--green-ink);
}

.kpi-panel.is-warn::before {
  background: var(--warning-ink);
}

.kpi-panel.is-crit::before {
  background: var(--danger-ink);
}

.kp-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* 판정 패널은 <b>패널 자체</b>에 좌측 판정색 바(::before)가 있다 —
   제목에 액센트를 또 붙이면 바가 둘이 된다. 크기·자간만 패널 제목과 맞춘다. */
.kp-title {
  /* 총괄 지시로 확대 — 픽토그램을 뺀 자리를 제목이 받는다(블록 구분은 이제 글자가 한다) */
  font-size: var(--fs-lead);
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.3px;
}

/* 고정 픽토그램 — 블록을 눈으로 구분한다. 값에 반응하지 않는다(도넛 금지). */
.kp-note {
  font-size: var(--fs-sub);
  color: var(--text-2);
  margin-left: auto;
}

/* more 버튼이 있으면 note 는 그 왼쪽에 붙는다(둘 다 margin-left:auto 면 겹친다) */
.kp-head .kp-note+.kp-more {
  margin-left: 0;
}

/* 주값 — 화면에서 가장 큰 숫자여야 한다. 판정이 상수(계약금액)에 묻히던 것이 문제였다. */
.kp-main {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 4px 0 0;
}

.kp-value {
  font-size: var(--fs-hero);
  font-weight: 700;
  color: var(--navy);
  letter-spacing: -1.2px;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

/* 「2 / 11」의 분모 — 분자보다 작게 해 비율임을 드러낸다 */
.kp-of {
  font-size: var(--fs-lead);
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: -.5px;
}

/* 경고 — "며칠 늦었나"는 한국 건설 보고의 헤드라인이다. 보조문구로 두지 않는다. */
/* 판정 배지 옆에 붙는 경고(지연일수). 별도 줄이 아니라 인라인이라
   패널마다 높이가 달라지지 않는다. */
.kp-alert {
  font-size: var(--fs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-2);
}

.kp-alert.is-warn {
  background: var(--tint-warn);
  color: var(--ink-warn);
}

.kp-alert.is-crit {
  background: var(--tint-crit);
  color: var(--ink-crit);
}

.kp-alert.is-ok {
  background: var(--tint-ok);
  color: var(--ink-ok);
}

.kp-alert.is-warn {
  color: var(--warning-ink);
}

.kp-alert.is-crit {
  color: var(--danger-ink);
}

.kp-alert.is-ok {
  color: var(--green-ink);
}

/* 근거 — 카드로 흩어져 있던 값들이 판정 아래 인라인으로 붙는다 */
.kp-facts {
  list-style: none;
  margin: 7px 0 0;
  padding: 7px 0 0;
  border-top: 1px dashed var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
}

.kp-facts li {
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: var(--fs);
}

.kp-fl {
  color: var(--text-2);
}

.kp-fv {
  color: var(--text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.kp-fv.is-actual {
  color: var(--green-ink);
}

.kp-fv.is-muted {
  color: var(--text-2);
  font-weight: 600;
}

@media (max-width: 1200px) {
  .kp-value {
    font-size: var(--fs-hero);
  }
}

/* 월별 연인원 요약 — 일별 추이 차트 위에 얹는 한 줄 집계 */
.mp-months {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
}

.mp-months li {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: var(--fs);
}

/* ══ 노선 위치도 ═══════════════════════════════════════
   원본 Power BI 홈의 <b>중앙 최대 면적</b>이 이 노선도였다.

   🚨 자산 정정(2026-07-31): 처음에 `route-map.jpg` 를 썼는데 그것은 <b>안전관리 화면용</b>이다.
      우측 하단에 안전 위험 아이콘 8종 범례가 <b>이미지에 박혀</b> 있고, 범례와 노선이 세로로
      겹쳐 CSS 크롭으로는 노선을 자르지 않고 제거할 수 없었다.
      원본 PBI 폴더의 <b>홈 전용 정본</b>(`홈 대쉬보드-01.png`, 범례 없는 선화)으로 교체했다.
      8.9MB PNG → 349KB JPEG(가로 2000px, 검은 띠 25px 제거).

   🚨 비율이 <b>4.13:1</b> 이다 — 절반 폭(2fr)에 넣으면 세로 242px 로 납작해진다.
      <b>반드시 전폭 영역</b>에 둔다(다단 그리드의 한 칸에 넣지 말 것).

   🚨 <b>max-width 로 줄이지 말 것</b>(2026-08-04, 시도 후 철회).
      「세로가 너무 크다」에 대해 max-width:1320→1100px 로 폭을 조여 봤다. 이미지 자체는
      멀쩡했지만(자산 실측 2000×484 = 4.1322, aspect-ratio 와 정확히 일치 — 왜곡 없음)
      <b>전폭 패널 안에서 지도만 가운데로 쪼그라들어</b> 좌우에 270px 씩 흰 여백이 생겼다.
      제목은 패널 왼쪽 끝, 지도는 한가운데 — <b>「비율이 깨져 보인다」</b>는 지적이 나왔다.
      패널을 같이 좁히지 않는 한 폭 축소는 이 어긋남을 반드시 만든다. 철회한다.

   🚨 그러면 세로를 줄일 방법이 남지 않는다 — <b>그것이 이 자산의 성질이다.</b>
      · aspect-ratio 를 낮추면 → cover 가 상하를 자른다. 이 이미지는 위아래 여백이 없어
        상단 나침반·「동묘앞」, 하단 「환기구#16/#17」·「GTX-C」 범례가 곧바로 잘린다.
      · max-height 를 걸면 → 폭은 100% 인 채 높이만 눌려 4.13:1 이 깨지고
        결국 cover 가 같은 곳을 잘라낸다(같은 손실이 조용히 일어난다).
      · max-width 로 줄이면 → 위의 좌우 여백 문제.
      전폭 · 무손실 · 4.13:1 을 동시에 만족하는 높이는 <b>폭 ÷ 4.132</b> 하나뿐이다.
      정말 낮춰야 한다면 남은 길은 <b>가로로 더 긴 자산으로 교체</b>하는 것이다.        */
.route-map {
  width: 100%;
  aspect-ratio: 2000 / 484;
  border-radius: var(--radius);
  /* 🚨 자산 경로 정본은 project_config.route_map_home_img 다(JSP 가 --route-map-url 로 넘긴다).
        여기 url() 은 설정이 없는 현장을 위한 폴백일 뿐이다 — 이 값을 고쳐 현장을 바꾸지 말 것. */
  background-image: var(--route-map-url, url("../img/route-map-home.jpg"));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-color: var(--surface-2);
  border: 1px solid var(--border);
}

/* 구조물 현황 — 별도 패널이 아니라 <b>지도 캡션</b>이다.
   노선 설명이므로 지도에서 떨어뜨리면 연결이 끊기고, 5줄 패널로 세우면
   옆에 놓인 무엇과도 높이가 맞지 않는다(총괄 지적). 한 줄 칩으로 붙인다.

   🚨 폭은 <b>.route-map 과 항상 같이</b> 간다 — 캡션이 지도보다 넓게 퍼지면
      「지도에 붙은 설명」이 아니라 별개 목록으로 읽힌다. 지도가 전폭이므로 여기도 전폭.

   🚨 자리를 <b>지도 아래 → 패널 제목줄 우측</b>으로 옮겼다(2026-08-04).
      지도 아래에 독립 줄로 두면 32px 을 쓰는데, 제목줄에는 남는 가로가 있었다.
      「지도에 붙은 설명」 원칙을 깨는 것이 아니다 — 오히려 더 붙는다(제목 = 공사명).
      JSP 에서 <ul class="route-cap"> 를 <h2 class="panel-title"> 안으로 넣는다. */
.route-cap {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 6px;
  /* 제목줄 안에서 우측으로 밀어 붙인다. 제목(공사명)과 같은 줄. */
  margin: 0 0 0 auto;
  padding: 0;
  font-size: var(--fs);
  font-weight: 400;
  color: var(--text);
}

.route-cap>li {
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
}

.route-cap .rc-label {
  background: none;
  border: 0;
  padding: 4px 0;
  font-weight: 700;
  color: var(--text-2);
  margin-right: 2px;
}

/* 미등록 안내도 제목줄 안에 산다(칩과 같은 자리). display:block 이면 제목줄이
   통째로 줄바꿈되므로 인라인 유지 — 세로를 벌지 못한다. */
.route-cap.is-empty {
  color: var(--warning-ink);
  margin-left: auto;
  font-weight: 400;
}

/* ══ 패널 높이 통일 규칙 ═══════════════════════════════
   🚨 같은 행에 놓인 패널은 <b>같은 높이</b>여야 한다.
      개선 전 공정관리는 좌 614px(11공종) vs 우 998px(26직종)로 <b>384px</b> 벌어져,
      스크롤을 내리면 왼쪽 아래가 텅 비었다(2026-07-31 총괄 지적).

   해법: 콘텐츠 양이 다른 것을 억지로 맞추지 않고 <b>목록 영역에 상한을 두고 스크롤</b>한다.
   - 데이터를 버리지 않는다(26직종 전부 남는다)
   - 패널 높이가 콘텐츠 양과 무관해진다 → 어떤 조합이든 세로가 맞는다
   - 표 머리행은 고정돼 스크롤해도 어느 열인지 알 수 있다                       */
.panel-list {
  max-height: 360px;
  overflow-y: auto;
  /* 🚨 가로도 명시한다. 컬럼 폭을 넓히면 표가 컨테이너보다 커지는데, 이 값이 없으면
     브라우저 계산에 맡겨져 넓힌 칸이 잘린 채 보인다(마지막 칸에서 특히 드러난다). */
  overflow-x: auto;
  /* 스크롤 가능하다는 것을 알 수 있게 — 잘린 경계에 옅은 선 */
  border-bottom: 1px solid var(--border-2);
}

.panel-list>table {
  margin: 0;
}

/* CBS 트리는 탐색이 목적이라 더 길게. 그래도 무한히 늘어나지 않게 상한은 둔다 */
.panel-list.cbs-tall {
  max-height: 620px;
}

.panel-list thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

@media print {
  .panel-list {
    max-height: none;
    overflow: visible;
    border-bottom: 0;
  }
}

/* 현장 영상 링크.
   🚨 처음에 --accent(#38BDF8)를 썼는데 <b>그것은 사이드바·다크 패널 전용</b>이다.
      이 패널 배경은 rgba(232,238,246,.94) 로 <b>밝은</b> 회청색이라 대비가 2.14:1 이었다.
      밝은 배경용 --info-ink(5.93:1)로 바꾼다. (docs/11 §2-④ 와 같은 실수를 반복했다.) */
.link-video {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex-shrink: 0;
  padding: 3px 7px;
  border-radius: 5px;
  font-size: var(--fs-xs);
  font-weight: 700;
  white-space: nowrap;
  line-height: 1.2;
  background: #E4EDF8;
  color: var(--info-ink);
  border: 1px solid #B9C9DE;
  text-decoration: none;
}

.link-video:hover {
  background: var(--info-ink);
  color: #fff;
  border-color: var(--info-ink);
  text-decoration: none;
}

/* 작업내용 줄 목록 — 원문의 줄바꿈을 항목으로 나눈 것 */
.wn-lines {
  list-style: none;
  margin: 0;
  padding: 0;
}

.wn-lines li {
  padding: 2px 0 2px 11px;
  position: relative;
}

.wn-lines li::before {
  content: "-";
  position: absolute;
  left: 2px;
  color: var(--text-2);
}

/* 특이사항 수정·삭제 — 다크 패널 위라 밝은 색을 쓴다 */
.sn-act {
  margin-left: 6px;
  white-space: nowrap;
}

.sn-act button {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, .28);
  color: #C4D2E2;
  padding: 1px 7px;
  border-radius: 4px;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
}

.sn-act button+button {
  margin-left: 3px;
}

.sn-act .sn-edit:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--navy-2);
}

.sn-act .sn-del:hover {
  background: #FCA5A5;
  border-color: #FCA5A5;
  color: #7F1D1D;
}

/* 구역 버튼이 영상 링크가 됐다 — 링크형/미등록형을 구분한다 */
.sim-zone.is-video {
  display: block;
  text-align: center;
  text-decoration: none;
}

.sim-zone.is-video:hover {
  background: var(--navy);
  color: #fff;
  border-color: var(--navy);
  text-decoration: none;
}

.sim-zone.is-novideo {
  display: block;
  text-align: center;
  cursor: default;
  background: #EDF1F7;
  color: var(--text-2);
  border-color: #D5DEEA;
}

/* ══ 차트 레이아웃 ═══════════════════════════════════════ */
.chart-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-bottom: 16px;
}

.chart-half {
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px;
}

/* 표 아래 보조 차트 — 같은 데이터의 다른 표현이므로 주 차트보다 작게 */
.chart-sm {
  height: 200px !important;
  margin-top: 10px;
  border-top: 1px dashed var(--border);
  padding-top: 8px;
}

.chart-box {
  width: 100%;
  /* 🚨 280 → 200. <b>여기서 더 낮추지 말 것</b> — 이 값은 전 화면 공용이고,
     기성·공정표의 <b>도넛</b>(pay-donut · p6-donut)이 같은 상자를 쓴다.
     180 으로 내렸더니 도넛 바깥 라벨이 하단 범례와 겹쳐 잘렸다(2026-08-04 실측·철회).
     홈만 더 낮춰야 한다면 아래 .chart-compact 를 쓴다. */
  height: 200px;
}

/* 🚨 홈 전용 축소분(200 → 180). 홈은 1920×919 에 스크롤 없이 들어가야 하는데
   그 마지막 7px 이 여기서 나온다. <b>도넛이 없는 화면에만</b> 붙인다 —
   .chart-box 자체를 낮추면 다른 화면의 도넛이 잘린다(위 주석 참조).
   🚨 홈 2단 행의 높이는 좌·우 중 <b>큰 쪽</b>이 정한다. 좌측 「최근 주간 실적」이
   232px 이므로 이 값을 180 아래로 더 내려도 홈은 1px 도 줄지 않는다. */
.chart-box.chart-compact {
  height: 180px;
}

.p6-note {
  font-size: var(--fs);
  color: var(--text-2);
  line-height: 1.7;
}

.p6-note b {
  color: var(--navy);
}

.grid-2 {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 16px;
  align-items: start;
}

/* 행동(조치)과 근거를 같은 폭으로 — 행동이 좁으면 읽히지 않는다 */
/* 🚨 align-items 를 start 로 두면 짧은 쪽이 위에 붙어 아래가 뚫린다.
   기본값(stretch)을 써서 <b>두 패널을 같은 높이</b>로 만든다.
   남는 공간은 아래 .panel-list / .chart-sm 규칙이 흡수한다. */
.grid-2-even {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

/* 패널 내부를 세로 flex 로 — 목록은 자기 크기대로, 차트가 남는 높이를 먹는다 */
.grid-2-even>.panel {
  display: flex;
  flex-direction: column;
}

.grid-2-even>.panel>.panel-list {
  flex: 0 1 auto;
  min-height: 0;
}

/* 🚨 차트가 여백을 흡수한다 — 빈 칸으로 남기는 것보다 차트를 키우는 편이 낫다.
   11공종 세로축 라벨도 칸이 넓어져 덜 빽빽해진다. */
.grid-2-even>.panel>.chart-box.chart-sm {
  flex: 1 1 auto;
  height: auto !important;
  min-height: 190px;
}

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

@media (max-width: 1200px) {

  .chart-row,
  .grid-2,
  .grid-2-even,
  .grid-3 {
    grid-template-columns: 1fr;
  }
}

/* ══ 공종 진척 바 ═══════════════════════════════════════ */
.wt-row {
  display: grid;
  grid-template-columns: 150px 1fr 190px;
  align-items: center;
  gap: 12px;
  margin: 5px 0;
}

.wt-name {
  font-size: var(--fs);
}

.wt-unit {
  color: var(--text-2);
  font-size: var(--fs-xs);
  margin-left: 4px;
}

.wt-bar {
  background: #E7ECF3;
  border-radius: 3px;
  height: 13px;
  overflow: hidden;
}

.wt-fill {
  background: var(--green);
  height: 100%;
  border-radius: 3px;
}

.wt-figure {
  font-size: var(--fs);
  font-weight: 700;
  color: var(--navy);
  text-align: right;
}

.wt-qty {
  font-weight: 400;
  color: var(--text-2);
  font-size: var(--fs-xs);
}

/* ══ 조치 필요(알림) ═══════════════════════════════════ */
.alert-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.alert-list li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 0;
  border-bottom: 1px dashed var(--border);
  font-size: var(--fs);
}

.alert-list li:last-child {
  border-bottom: 0;
}

.al-badge {
  flex-shrink: 0;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: #fff;
  border-radius: 3px;
  padding: 2px 7px;
  margin-top: 1px;
}

.al-CRIT .al-badge {
  background: var(--danger);
}

.al-WARN .al-badge {
  background: var(--warning);
}

.al-INFO .al-badge {
  background: var(--info);
}

.al-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.al-body b {
  color: var(--navy);
}

.al-msg {
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin-top: 1px;
}

.al-link {
  flex-shrink: 0;
  font-size: var(--fs-sm);
  align-self: center;
}

/* ══ 홈 요약 라인 ═══════════════════════════════════════ */
/* 데이터 지연 배너 — 과거 주간을 보여주고 있다는 사실을 값보다 먼저 말한다 */
.hl-lag {
  margin: 0 0 6px;
  padding: 6px 10px;
  border-radius: 5px;
  background: var(--tint-warn);
  color: var(--ink-warn);
  font-size: var(--fs);
  line-height: 1.5;
}

.sum-line {
  display: flex;
  justify-content: space-between;
  /* 🚨 홈을 한 화면에 담기 위해 조였다(7 → 4). 이 목록이 6줄이고, 옆 차트 패널의
     높이를 결정하는 쪽이라 한 줄당 6px 이 양쪽에서 돌아온다. */
  padding: 2px 0;
  border-bottom: 1px dashed var(--border);
  font-size: var(--fs-group);
}

.sum-line span:last-child {
  font-weight: 700;
  color: var(--navy);
}

.sum-line .is-actual {
  color: var(--green-ink);
}

/* ══ P6 Gantt — WBS 트리 + 선후행 ═══════════════════════ */
.p6-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 12px;
  align-items: start;
}

.wbs-pane {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}

.wbs-head {
  padding: 7px 10px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs);
  font-weight: 700;
  color: var(--navy);
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: space-between;
}

/* 모두 펼치기 / 접기 — 트리 머리에 붙는 작은 조작 */
.wbs-tools {
  display: inline-flex;
  gap: 3px;
}

.wbs-tools>button {
  width: 20px;
  height: 18px;
  padding: 0;
  line-height: 1;
  font-size: var(--fs-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-2);
  border-radius: 3px;
  cursor: pointer;
}

.wbs-tools>button:hover {
  color: var(--navy);
  border-color: var(--navy);
}

/* 🚨 접기 화살표는 CBS 트리(.cbs-tog)와 같은 모양이어야 한다 — 같은 동작이 화면마다
   다르게 생기면 사용자가 매번 다시 배운다. 폭·색·크기를 그대로 맞춘다. */
.wbs-tog {
  flex: none;
  width: 14px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--text-2);
  font-size: var(--fs-micro);
  line-height: 1;
  text-align: left;
  cursor: pointer;
}

.wbs-tog:hover {
  color: var(--navy);
  background: none;
}

.wbs-tog.is-leaf {
  display: inline-block;
  cursor: default;
}

.wbs-node.on .wbs-tog {
  color: #fff;
}

.wbs-tree {
  max-height: 620px;
  overflow: auto;
}

.wbs-node {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px 3px 6px;
  font-size: var(--fs-sm);
  cursor: pointer;
  border-bottom: 1px solid var(--border-2);
}

.wbs-node:hover {
  background: #F4F8FF;
}

.wbs-node.on {
  background: var(--navy);
  color: #fff;
}

/* Gantt 상태 필터 활성 표시 — 인라인 opacity 대신 클래스(.wbs-node.on 과 같은 규범).
   초기 렌더에서도 「지금 무엇이 걸려 있는지」가 보여야 한다. */
.gf.on {
  background: var(--accent);
  color: #fff;
  border-color: var(--navy);
}

/* 업로드 이력 — 일자 단위 되돌리기 목록 */
.ud-list {
  list-style: none;
  margin: 4px 0 6px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.ud-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  font-size: var(--fs);
}

.ud-item b {
  font-variant-numeric: tabular-nums;
}

/* 🚨 삭제는 되돌릴 수 없다 — 버튼이 그렇게 보여야 한다(일반 버튼과 구분). */
.ud-del {
  font-size: var(--fs-sm);
  padding: 2px 7px;
  color: var(--danger-ink);
  border-color: var(--danger-ink);
}

.ud-del:hover:not(:disabled) {
  background: var(--tint-crit);
}

.ud-btn {
  font-size: var(--fs-sm);
  padding: 2px 7px;
}

/* 기준정보 등록 — 미사용(is_active=0) 행. 🚨 삭제가 아니라 '쓰지 않음'이므로 흐리게만 둔다. */
.is-off td {
  color: var(--text-2);
}

.dim-edit {
  font-size: var(--fs-sm);
  padding: 2px 7px;
}

/* ══ 현장 설정 화면 ═══════════════════════════════════════ */
.set-row {
  padding: 12px 0;
  border-bottom: 1px solid var(--border-2);
}

.set-row:last-child {
  border-bottom: 0;
}

.set-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 3px;
}

.set-key {
  font-size: var(--fs-xs);
  color: var(--text-2);
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 4px;
}

/* 설명은 「무엇을 넣는 칸인가」가 아니라 「잘못 넣으면 무슨 일이 나는가」를 적는다 */
.set-help {
  margin: 0 0 6px;
  font-size: var(--fs);
  color: var(--text-2);
  line-height: 1.6;
}

.set-ctl {
  display: flex;
  align-items: center;
  gap: 6px;
}

.set-input {
  font-size: var(--fs);
  padding: 4px 7px;
  width: 160px;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-variant-numeric: tabular-nums;
}

.set-input.is-wide {
  width: 420px;
  max-width: 100%;
}

.set-asset {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.set-thumb {
  width: 220px;
  min-height: 74px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  overflow: hidden;
  flex-shrink: 0;
  padding: 4px;
}

.set-thumb img {
  max-width: 100%;
  max-height: 120px;
  object-fit: contain;
}

.set-asset-ctl {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: flex-start;
}

.set-msg {
  margin-top: 5px;
  font-size: var(--fs);
  min-height: 1em;
}

.set-msg.is-ok {
  color: var(--green-ink);
}

.set-msg.is-err {
  color: var(--danger-ink);
}

.set-clear {
  color: var(--danger-ink);
  border-color: var(--danger-ink);
  font-size: var(--fs-sm);
  padding: 2px 7px;
}

.wbs-node.on .wn-count {
  background: rgba(255, 255, 255, .22);
  color: #fff;
}

.wn-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wn-count {
  font-size: var(--fs-xs);
  background: var(--surface-2);
  color: var(--text-2);
  border-radius: 999px;
  padding: 0 6px;
  font-variant-numeric: tabular-nums;
}

.gantt-pane {
  min-width: 0;
}

/* 선후행 패널 — 선택 활동 기준(전체 1,249건은 판독 불가) */
.pred-panel {
  margin-top: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: 10px 12px;
  font-size: var(--fs);
}

.pred-panel.is-empty {
  color: var(--text-2);
  font-style: italic;
}

.pr-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  color: var(--navy);
}

.pr-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

.pr-col-h {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-2);
  margin-bottom: 5px;
}

.pr-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.pr-list li {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  font-size: var(--fs);
}

.pr-type {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: #fff;
  background: var(--navy-3);
  border-radius: 3px;
  padding: 0 5px;
}

.pr-st {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.pr-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pr-lag {
  font-size: var(--fs-sm);
  color: var(--warning-ink);
  font-weight: 700;
}

@media (max-width: 1200px) {
  .p6-layout {
    grid-template-columns: 1fr;
  }
}

/* ══ 안전관리 — 원본 구조(노선맵 + 시뮬레이션 패널 + 구역별 특이사항) ═══ */
/* 지도는 정적 이미지 자산(GIS 불필요). 비율 유지를 위해 aspect-ratio 사용. */
.safety-map {
  position: relative;
  width: 100%;
  aspect-ratio: 1800 / 629;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 12px;
  box-shadow: var(--shadow);
}

/* 좌표(map_x/map_y %) 있는 마커만 배치된다 */
.map-markers {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.map-marker {
  position: absolute;
  transform: translate(-50%, -100%);
  pointer-events: auto;
}

.map-marker img {
  width: 34px;
  height: 34px;
  object-fit: contain;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .45));
}

/* 마커 배치 상태 — "배치 0건"을 말하지 않으면 빈 지도가 "위험 없음"으로 읽힌다(null ≠ 0) */
.map-status {
  position: absolute;
  left: 10px;
  top: 10px;
  max-width: 52%;
  padding: 5px 10px;
  border-radius: 6px;
  font-size: var(--fs);
  line-height: 1.45;
  background: rgba(255, 255, 255, .92);
  color: var(--text-2);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
}

.map-status:empty {
  display: none;
}

.map-status.is-warn {
  background: rgba(255, 247, 230, .96);
  color: #7a4a00;
  border-color: var(--warning);
  font-weight: 600;
}

/* 안전 시뮬레이션 패널 — 원본 목업의 우하단 위치를 덮는다(이미지에 구워진 패널 가림) */
/* 🚨 height 고정이면 내용이 늘 때 잘린다 — min-height 로 두어 위로 자라게 한다
   (bottom:0 고정이므로 성장 방향이 위쪽이다). */
.sim-panel {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 44%;
  min-height: 36%;
  display: flex;
  gap: 10px;
  padding: 10px 12px;
  background: rgba(232, 238, 246, .94);
  border-top-left-radius: 10px;
  border-top: 1px solid rgba(255, 255, 255, .6);
  border-left: 1px solid rgba(255, 255, 255, .6);
  backdrop-filter: blur(2px);
}

.sim-body {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}

.sim-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px 6px;
  width: 100%;
}

.sim-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 0;
}

.sim-item img {
  width: 40px;
  height: 40px;
  object-fit: contain;
}

.sim-noicon {
  font-size: var(--fs-hero);
  line-height: 40px;
}

.sim-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* 🚨 128px 였는데 구역 버튼(약 94px) + 영상 칩(약 48px)이 들어가지 않아 화면이 깨졌다.
   버튼은 min-width:auto 라 글자 폭 밑으로 줄지 않는다 — 칸을 넓히는 것이 답이다. */
.sim-side {
  width: 176px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 6px;
}

.sim-title {
  font-size: var(--fs-title);
  font-weight: 700;
  color: var(--navy);
  letter-spacing: -.2px;
}

.sim-zones {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
}

/* 구역 버튼(특이사항 필터) + 4D 딥링크 + 현장 영상 — 역할이 달라 분리해 나란히 둔다.
   🚨 flex-wrap 은 <b>안전망</b>이다. 구역명이 길거나 링크가 늘면 넘치는 대신 줄이 바뀐다. */
.sim-zone-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

.sim-zone-wrap .sim-zone {
  flex: 1 1 auto;
  min-width: 0;
}

.sim-zone {
  background: #DDE6F2;
  color: var(--navy);
  border: 1px solid #B9C9DE;
  border-radius: 6px;
  padding: 5px 8px;
  font-size: var(--fs);
  font-weight: 700;
  cursor: pointer;
  width: 100%;
}

.sim-zone:hover {
  background: #CBD9EC;
}

.sim-zone.on {
  background: var(--navy);
  color: #fff;
  border-color: var(--navy);
}

/* 안전 사항 및 특이 사항 — 원본은 검은 배경 + 구역별 열 */
.safety-notes {
  background: #0B1520;
  color: #E8EEF6;
  border-radius: var(--radius);
  padding: 14px 18px 18px;
  margin-bottom: 16px;
}

.sn-title {
  font-size: var(--fs-title);
  font-weight: 700;
  margin: 0 0 12px;
  color: #fff;
  display: flex;
  align-items: center;
  gap: 8px;
}

.sn-dot {
  color: var(--accent);
  font-size: var(--fs);
}

.sn-sub {
  font-size: var(--fs-sm);
  font-weight: 400;
  color: #9FB2C7;
}

.sn-cols {
  display: grid;
  gap: 18px;
}

.sn-col {
  border-left: 2px solid rgba(255, 255, 255, .14);
  padding-left: 14px;
  min-width: 0;
}

.sn-col:first-child {
  border-left: 0;
  padding-left: 0;
}

.sn-col-head {
  font-size: var(--fs-group);
  font-weight: 700;
  color: #FF6B6B;
  margin-bottom: 8px;
}

.sn-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.sn-list li {
  font-size: var(--fs);
  line-height: 1.75;
  padding: 3px 0;
  border-bottom: 1px dashed rgba(255, 255, 255, .08);
}

.sn-list li::before {
  content: "• ";
  color: var(--accent);
}

.sn-ym {
  color: #9FB2C7;
  font-size: var(--fs-sm);
  margin-right: 6px;
}

.sn-haz {
  background: rgba(245, 158, 11, .22);
  color: #FBBF24;
  border-radius: 3px;
  padding: 0 5px;
  font-size: var(--fs-xs);
  margin-right: 6px;
}

.sn-text {
  color: #E8EEF6;
}

.sn-none {
  color: #7E93AB;
  font-size: var(--fs);
  font-style: italic;
}

.safety-notes a {
  color: var(--accent);
}

@media (max-width: 1200px) {
  .sim-panel {
    position: static;
    width: 100%;
    height: auto;
    border-radius: 0;
  }

  .safety-map {
    aspect-ratio: auto;
    min-height: 360px;
  }
}

/* 노선맵 히어로 */
/* 히어로는 판단 정보가 0 이다 — 150px 는 첫 화면에서 판정을 아래로 밀어냈다 */
.hero {
  position: relative;
  height: 84px;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 16px;
  background-color: var(--navy);
  background-image: linear-gradient(120deg, #0E2A47, #16385C);
  background-size: cover;
  background-position: center;
}

.hero-overlay {
  position: absolute;
  inset: auto 0 0 0;
  padding: 8px 16px;
  background: linear-gradient(transparent, rgba(11, 32, 56, .9));
  color: #fff;
}

.hero-title {
  font-size: var(--fs-title);
  font-weight: 700;
}

.hero-sub {
  font-size: var(--fs-sm);
  opacity: .9;
  margin-top: 2px;
}

/* ══ 폼/툴바 ════════════════════════════════════════════ */
.toolbar {
  display: flex;
  gap: 18px;
  align-items: center;
  flex-wrap: wrap;
}

.toolbar label,
.note-form label {
  font-size: var(--fs);
  color: var(--text-2);
}

select,
input,
textarea {
  font-family: inherit;
  font-size: var(--fs);
  padding: 5px 8px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fff;
  color: var(--text);
}

button {
  background: var(--navy);
  color: #fff;
  border: 0;
  padding: 7px 16px;
  border-radius: 5px;
  font-weight: 600;
  font-size: var(--fs);
  cursor: pointer;
}

button:hover {
  background: var(--navy-3);
}

button:disabled {
  opacity: .5;
  cursor: not-allowed;
}

.upload-form {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.upload-hint {
  color: var(--text-2);
  font-size: var(--fs-sm);
}

/* 기준 데이터셋 인입 */
.ds-row {
  padding: 9px 0;
  border-bottom: 1px dashed var(--border);
}

.ds-row:last-child {
  border-bottom: 0;
}

.ds-label {
  display: inline-block;
  min-width: 84px;
  color: var(--navy);
  font-size: var(--fs);
}

.ds-result {
  margin-top: 7px;
  font-size: var(--fs);
}

.ds-counts {
  margin-top: 5px;
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

.ds-cnt {
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.ds-cnt b {
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}

.ds-issues {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.ds-issues .lv-ERROR {
  color: var(--danger-ink);
}

.btn-link {
  display: inline-block;
  background: var(--navy);
  color: #fff;
  text-decoration: none;
  padding: 6px 14px;
  border-radius: 5px;
  font-size: var(--fs);
  font-weight: 600;
}

.btn-link:hover {
  background: var(--navy-3);
  text-decoration: none;
  color: #fff;
}

.note-form {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  align-items: end;
}

.note-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.note-form label.wide {
  grid-column: 1 / -1;
}

.filter-zone {
  margin-left: 8px;
}

.save-msg {
  font-size: var(--fs);
  font-weight: 700;
}

.save-msg.is-ok {
  color: var(--green-ink);
}

.save-msg.is-err {
  color: var(--danger-ink);
}

/* 업로드 결과 */
.result-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0 10px;
}

.result-file {
  font-weight: 700;
  color: var(--navy);
}

.result-uid {
  color: var(--text-2);
  font-size: var(--fs-sm);
}

.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px;
  margin-bottom: 8px;
}

.sum-item {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 9px 12px;
}

.sum-item .sum-label {
  display: block;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.sum-item .sum-value {
  display: block;
  font-size: var(--fs-lead);
  font-weight: 700;
  color: var(--navy);
}

.issues-title {
  color: var(--navy);
  font-size: var(--fs-title);
  font-weight: 700;
  border-left: 3px solid var(--navy);
  border-radius: 2px;
  padding-left: 9px;
  margin: 20px 0 9px;
  letter-spacing: -.2px;
}

.ok-note {
  color: var(--green-ink);
}

/* 상태 */
.kbim-loading {
  color: var(--text-2);
  padding: 20px;
}

.kbim-error {
  color: var(--danger-ink);
  padding: 20px;
}

/* ══ 일일보고 (인쇄) ════════════════════════════════════ */
.rpt-sheet {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px 30px;
  max-width: 900px;
}

/* 주간보고는 표가 넓다(공종 6열 + 계획) — 가로 여유를 더 준다 */
.rpt-sheet.rpt-wide {
  max-width: 1120px;
}

.rpt-title {
  text-align: center;
  font-size: var(--fs-lead);
  color: var(--navy);
  margin: 0 0 6px;
}

.rpt-meta {
  text-align: center;
  color: var(--text-2);
  font-size: var(--fs);
  margin-bottom: 16px;
}

/* 화면 패널 제목과 같은 언어 — 좌측 3px 액센트 + 자간. 인쇄물과 화면의 제목이 같아진다. */
.rpt-h2 {
  font-size: var(--fs-title);
  font-weight: 700;
  color: var(--navy);
  border-left: 3px solid var(--navy);
  border-radius: 2px;
  padding-left: 9px;
  margin: 20px 0 9px;
  letter-spacing: -.2px;
}

.rpt-kv th {
  background: var(--surface-2);
  color: var(--text-2);
  text-align: left;
  width: 108px;
}

/* 결재란 — 관급 보고서 양식(서명 공간 확보) */
.rpt-sign {
  width: 320px;
  margin: 22px 0 0 auto;
  border-collapse: collapse;
  font-size: var(--fs);
}

.rpt-sign th,
.rpt-sign td {
  border: 1px solid var(--border);
  text-align: center;
}

.rpt-sign th {
  background: var(--surface-2);
  color: var(--text-2);
  padding: 5px 0;
  font-weight: 600;
}

.rpt-sign td {
  height: 52px;
}

/* ══ 폴리싱 (전이·호버·구조) ═════════════════════════════ */
.nav-item,
.kpi-panel,
.panel,
.chart-half,
button,
.kp-more {
  transition: all .15s ease;
}

.chart-half:hover {
  box-shadow: 0 3px 10px rgba(15, 23, 42, .10);
  transform: translateY(-1px);
}

/* 구분선 — 액센트 바가 무게를 지므로 선 자체는 은은하게, 다만 --border-2(1.17:1)는 너무 옅어 --border 로 */
.panel-title {
  border-bottom: 1px solid var(--border);
}

.panel-title .panel-sub {
  margin-left: auto;
}

.sidebar-brand .b-title::before {
  content: "▪ ";
  color: var(--accent);
}

/* 판정 패널은 상세 화면으로 가는 출발점이다 — 예전에는 CSS 가 클릭 대상이 아님을 선언했다 */
/* 상세 이동 — 링크가 아니라 <b>버튼</b>으로 보여야 누를 것이라고 인식된다.
   주 동작(업로드·적용)과 구분하려고 채움이 아닌 외곽선 스타일을 쓴다. */
.kpi-panel a.kp-more {
  margin-left: auto;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 12px;
  border-radius: 5px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--navy);
  font-size: var(--fs);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.kpi-panel a.kp-more::after {
  content: "›";
  font-size: var(--fs-group);
  line-height: 1;
}

.kpi-panel a.kp-more:hover {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
  text-decoration: none;
}

button:active {
  transform: translateY(1px);
}

/* 스크롤바(웹킷) */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-thumb {
  background: #C6D0DC;
  border-radius: 6px;
  border: 2px solid var(--bg);
}

::-webkit-scrollbar-thumb:hover {
  background: #A9B7C7;
}

.nav::-webkit-scrollbar-thumb {
  background: #274A6E;
  border-color: var(--navy-2);
}

/* 빈 상태 */
.ok-note {
  padding: 14px;
  background: var(--surface-2);
  border-radius: var(--radius);
  text-align: center;
}

/* ══ 포커스 표시 ═══════════════════════════════════════════
   키보드 사용자가 "지금 어디에 있는지" 알 수 있어야 한다(KWCAG 2.2 / WCAG 2.4.7).
   🚨 outline 을 지우지 말 것 — 지우면 키보드 조작이 사실상 불가능해진다.
   :focus-visible 을 쓰므로 마우스 클릭에는 링이 뜨지 않는다(디자인 훼손 없음).
   --info(#3B82F6)는 흰 배경 대비 3.68:1 로 비텍스트 기준 3:1 을 통과한다.        */
:focus-visible {
  outline: 2px solid var(--info);
  outline-offset: 2px;
  border-radius: 3px;
}

/* 어두운 배경(사이드바·안전 패널) 위에서는 --info 가 묻힌다 — 밝은 accent 로 바꾼다 */
.sidebar :focus-visible,
.safety-notes :focus-visible {
  outline-color: var(--accent);
}

/* navy 버튼 위 — 링이 버튼에 먹히지 않게 바깥쪽으로 띄운다 */
button:focus-visible {
  outline-offset: 3px;
}

/* CBS 토글은 16px 로 작아 링만으로는 존재감이 없다 — 배경도 함께 준다 */
.cbs-tog:focus-visible {
  background: var(--surface-2);
}

/* 인쇄 전용 / 화면 전용 유틸 — 관급 문서와 화면의 문구가 달라야 하는 경우에 쓴다 */
.print-only {
  display: none;
}

.rpt-writein td {
  height: 12mm;
}

/* ══ 인쇄 — 일일·주간보고는 감리 서명이 붙는 관급 성격 문서다 ════════════
   화면용 규칙을 그대로 쓰면 제출물에서 실제로 사고가 난다:
     ① 배지가 "색 배경 + 흰 글자"라 배경 미인쇄 시 판정(정상/주의/위험)이 백지가 된다
     ② 표 머리행이 2페이지째에 안 나와 어느 열이 "금일"인지 알 수 없다
     ③ 결재란이 페이지 경계에서 반토막 나면 반려 사유가 된다
   아래 규칙은 그 세 가지를 막는 것이 목적이다. (docs/11 §2-③)                */
@page {
  size: A4 portrait;
  margin: 14mm 12mm;
}

@media print {

  .sidebar,
  .appbar,
  .projbar,
  .no-print {
    display: none !important;
  }

  .print-only {
    display: revert;
  }

  body {
    background: #fff;
  }

  .app-content {
    padding: 0;
  }

  .rpt-sheet {
    border: 0;
    box-shadow: none;
    max-width: none;
  }

  /* ① 배경색·글자색을 인쇄에 강제 — 이게 없으면 배지 안의 흰 글자가 사라진다.
        판정 배지는 관급 문서의 핵심 정보이므로 장식이 아니라 필수 출력이다. */
  * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }

  /* ② 표 머리행을 매 페이지 반복 */
  thead {
    display: table-header-group;
  }

  tfoot {
    display: table-footer-group;
  }

  /* ③ 끊기면 안 되는 블록 */
  .rpt-sign,
  tr,
  .kpi-panel,
  .ok-note {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .rpt-table,
  .mp-table {
    break-inside: auto;
  }

  .rpt-h2 {
    break-after: avoid;
    page-break-after: avoid;
  }

  /* 제목만 페이지 끝에 남지 않게 */

  /* 결재란 — 현장 직인 지름 18~30mm 가 들어가야 한다(화면 기본 52px ≈ 14mm 로는 불가) */
  .rpt-sign {
    width: 100%;
    max-width: none;
  }

  .rpt-sign th {
    padding: 4px 0;
  }

  .rpt-sign td {
    height: 30mm;
  }

  /* 안전관리 화면의 다크 패널 — 그대로 인쇄하면 흰 글자가 백지에 사라진다 */
  .safety-notes {
    background: #fff !important;
    color: var(--text) !important;
  }

  .safety-notes .sn-item {
    border-color: var(--border) !important;
  }

  /* 링크 밑줄·색 제거(종이에서 파란 밑줄은 의미가 없다) */
  a {
    color: inherit !important;
    text-decoration: none !important;
  }

  /* 차트는 캔버스라 인쇄 품질이 낮다 — 잘리지 않게만 한다 */
  .chart-box {
    break-inside: avoid;
    page-break-inside: avoid;
  }
}

/* ══ BIM 4D 뷰어 (계획/실적 2뷰 + 타임슬라이더) ═══════════ */
.bim-timebar {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* 재생 배속 — P6 상태 필터(.gf/.gf.on)와 같은 언어를 쓴다. 화면마다 토글 모양이
   달라지면 「눌리는 것」인지 「표시」인지 매번 다시 배워야 한다.
   🚨 bim/viewer.jsp 의 배속 버튼 4개(data-speed)와 bim-viewer.island.js 의
   bindSpeed() 가 이 클래스에 의존한다 — 지우면 버튼이 스타일 없이 남는다. */
.bim-speed {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
}

.bim-speed .gf {
  padding: 3px 7px;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}

.bim-range {
  flex: 1;
  min-width: 200px;
  accent-color: var(--accent);
  cursor: pointer;
}

.bim-range:disabled {
  cursor: not-allowed;
  opacity: .5;
}

.bim-date {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: var(--fs-group);
  color: var(--text);
  min-width: 96px;
  text-align: center;
  padding: 4px 10px;
  background: var(--surface-2);
  border-radius: var(--radius);
}

/* 계획 vs 실적 델타 — 좌우 형상 차이가 이 화면의 존재 이유이므로 숫자로 못박아 보여준다 */
.bim-delta {
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: var(--radius);
  font-size: var(--fs);
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--border-2);
}

.bim-delta:empty {
  display: none;
}

.bim-delta.is-warn {
  background: rgba(255, 247, 230, .96);
  color: #7a4a00;
  border-color: var(--warning);
  font-weight: 600;
}

/* 딥링크 적용 범위(?zone=&date=). 걸린 필터를 밝히지 않으면 전체로 착각한다. */
.bim-scope {
  margin-top: 6px;
  padding: 5px 10px;
  border-radius: var(--radius);
  font-size: var(--fs);
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px dashed var(--border-2);
}

.bim-scope:empty {
  display: none;
}

.bim-scope.is-warn {
  background: rgba(255, 247, 230, .96);
  color: #7a4a00;
  border-color: var(--warning);
}

.bim-scope-clear {
  margin-left: 8px;
}

/* 데이터 행 → 4D 딥링크 배지. 표·차트 안에서 눈에 걸리되 값을 가리지 않는 크기. */
.link-3d {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  font-size: var(--fs-xs);
  line-height: 16px;
  font-weight: 700;
  color: var(--info-ink);
  border: 1px solid var(--border-2);
  border-radius: 4px;
  text-decoration: none;
  vertical-align: middle;
}

.link-3d:hover {
  background: var(--surface-2);
}

.bim-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 12px;
}

.bim-pane {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.bim-pane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border-2);
  background: var(--surface-2);
}

.bim-pane-title {
  font-weight: 700;
  font-size: var(--fs-group);
  color: var(--text);
}

.bim-count {
  font-size: var(--fs);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

/* 🚨 캔버스는 높이를 명시해야 한다 — WebGL 캔버스는 내용으로 높이가 잡히지 않아
      비워 두면 0px 이 되고, 모델이 로드돼도 아무것도 보이지 않는다. */
.bim-canvas-wrap {
  position: relative;
  height: 520px;
}

.bim-canvas {
  width: 100%;
  height: 100%;
}

.bim-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  text-align: center;
  font-size: var(--fs-group);
  color: var(--text-2);
  background: var(--surface-2);
}

@media (max-width: 1280px) {
  .bim-split {
    grid-template-columns: 1fr;
  }

  .bim-canvas-wrap {
    height: 420px;
  }
}