/* ══════════════════════════════════════════════
   DAWN CALL OPS BOARD — 관제탑 디자인 v4
══════════════════════════════════════════════ */
:root{
  /* 라이트 팔레트 — Phase 1b (2026-05-19): v4 색 변수의 *값만* Wanted DS 톤(--w-*, common.jsx T 1:1)으로
     재지정. 변수명·소비처(var(--bg)/var(--cc)... 호출부) 0 변경 → 전 화면 새 룩 자동 반영.
     상태→색 *의미* 불변(done=green/cp-unv·watch=amber/critical=red/warn=orange/move·brand=blue).
     bg=Wanted 페이지배경(#F7F7F8), surface=white. 글로우는 --w- alpha변형 부재로 hue만 Wanted로(rgba 리터럴). */
  --bg:var(--w-bg);--bg2:var(--w-surface);--bg3:var(--w-surface);--bg4:var(--w-surface-alt);--bg5:var(--w-brand-subtle);
  --bdr:var(--w-border);--bdrg:var(--w-border-strong);--bdrb:var(--w-brand);
  --t1:var(--w-t1);--t2:var(--w-t2);--t3:var(--w-t3);--t4:var(--w-t4);
  --cc:var(--w-red);--cw:var(--w-orange);--cy:var(--w-yellow);--cb:var(--w-brand);--cg:var(--w-green);--cp:var(--w-t4);
  --cc-glow:rgba(240,62,62,.40);--cw-glow:rgba(247,103,7,.4);
  --cy-glow:rgba(240,140,0,.4);--cb-glow:rgba(0,102,255,.35);--cg-glow:rgba(12,166,120,.4);
  --sw:196px;
  /* ── Wanted DS 폰트 토큰화 (Phase 1a 2026-05-19) — body 기본 폰트만 Pretendard/Wanted로.
     65개 var(--font)/var(--mono) 소비처 전부 이 2변수 경유 → 마크업 변경 0으로 전 화면 폰트 격상.
     Noto/JetBrains는 폴백 체인 말미에 안전망으로 유지(1a 단계). 색·레이아웃은 미변경(후속 클러스터). */
  --font:"Pretendard Variable","Pretendard","Noto Sans KR",-apple-system,BlinkMacSystemFont,"Apple SD Gothic Neo","Segoe UI",Roboto,sans-serif;
  --mono:"Pretendard Variable",ui-monospace,"SF Mono",Menlo,Monaco,Consolas,"JetBrains Mono",monospace;
  --font-brand:"Wanted Sans Variable","Wanted Sans","Pretendard Variable","Pretendard",sans-serif;

  /* ── Wanted DS 토큰 정본 (wanted-tokens.css / common.jsx T 객체 이식). prefix --w-* 로
     기존 v4 변수(--bg/--cc/--t1...)와 네임스페이스 충돌 0 — 1a는 정의만, 소비는 후속 클러스터. */
  /* 컬러 — semantic alias (common.jsx T) */
  --w-bg:#F7F7F8;--w-surface:#FFFFFF;--w-surface-alt:#FAFAFB;--w-surface-muted:#F4F4F5;
  --w-hairline:#EDEEF0;--w-border:#DBDCDF;--w-border-strong:#C2C4C8;
  --w-t1:#171719;--w-t2:#46474B;--w-t3:#7C7E83;--w-t4:#989BA2;--w-t5:#C2C4C8;
  --w-brand:#0066FF;--w-brand-strong:#0052CC;--w-brand-subtle:#EBF2FF;--w-brand-hairline:#CFE0FF;
  --w-red:#F03E3E;--w-red-soft:#FFEAEA;--w-red-line:#FFC2C2;--w-red-ink:#B41E1E;
  --w-orange:#F76707;--w-orange-soft:#FFF1E0;--w-orange-line:#FFD0A6;--w-orange-ink:#C53B00;
  --w-yellow:#F08C00;--w-yellow-soft:#FFF6E0;--w-yellow-line:#FFE0A1;--w-yellow-ink:#A86200;
  --w-green:#0CA678;--w-green-soft:#E4F8F2;--w-green-line:#9FE3CB;--w-green-ink:#0B7959;
  --w-blue-soft:#EBF2FF;--w-blue-line:#A8C7FF;--w-blue-ink:#0052CC;
  /* 타입 스케일 (wanted-tokens.css) */
  --w-type-title-1-size:32px;--w-type-title-1-line:1.375;--w-type-title-1-track:-0.0253em;--w-type-title-1-weight:700;
  --w-type-title-3-size:24px;--w-type-title-3-line:1.334;--w-type-title-3-track:-0.023em;--w-type-title-3-weight:700;
  --w-type-heading-1-size:22px;--w-type-heading-1-line:1.364;--w-type-heading-1-track:-0.0194em;--w-type-heading-1-weight:700;
  --w-type-heading-2-size:20px;--w-type-heading-2-line:1.4;--w-type-heading-2-track:-0.012em;--w-type-heading-2-weight:700;
  --w-type-headline-1-size:18px;--w-type-headline-1-line:1.445;--w-type-headline-1-track:-0.002em;--w-type-headline-1-weight:600;
  --w-type-headline-2-size:17px;--w-type-headline-2-line:1.412;--w-type-headline-2-track:0em;--w-type-headline-2-weight:600;
  --w-type-body-1-size:16px;--w-type-body-1-line:1.5;--w-type-body-1-track:0.0057em;--w-type-body-1-weight:500;
  --w-type-body-2-size:15px;--w-type-body-2-line:1.467;--w-type-body-2-track:0.0096em;--w-type-body-2-weight:500;
  --w-type-label-1-size:14px;--w-type-label-1-line:1.429;--w-type-label-1-track:0.0145em;--w-type-label-1-weight:600;
  --w-type-label-2-size:13px;--w-type-label-2-line:1.385;--w-type-label-2-track:0.0194em;--w-type-label-2-weight:600;
  --w-type-caption-1-size:12px;--w-type-caption-1-line:1.334;--w-type-caption-1-track:0.0252em;--w-type-caption-1-weight:500;
  --w-type-caption-2-size:11px;--w-type-caption-2-line:1.273;--w-type-caption-2-track:0.0311em;--w-type-caption-2-weight:500;
  /* 스페이싱 (2px 그리드) */
  --w-space-2:2px;--w-space-4:4px;--w-space-6:6px;--w-space-8:8px;--w-space-10:10px;--w-space-12:12px;
  --w-space-14:14px;--w-space-16:16px;--w-space-20:20px;--w-space-24:24px;--w-space-28:28px;--w-space-32:32px;
  --w-space-40:40px;--w-space-48:48px;--w-space-56:56px;--w-space-64:64px;--w-space-80:80px;--w-space-96:96px;--w-space-128:128px;
  /* 라운드 */
  --w-radius-2:2px;--w-radius-4:4px;--w-radius-6:6px;--w-radius-8:8px;--w-radius-12:12px;--w-radius-16:16px;
  --w-radius-20:20px;--w-radius-24:24px;--w-radius-32:32px;--w-radius-40:40px;--w-radius-pill:999px;--w-radius-circle:50%;
  /* 섀도 */
  --w-shadow-emphasize:0 1px 4px rgba(23,23,23,.08),0 4px 8px rgba(23,23,23,.06);
  --w-shadow-strong:0 2px 8px rgba(0,0,0,.08),0 8px 24px rgba(0,0,0,.12);
  --w-shadow-heavy:0 4px 12px rgba(0,0,0,.1),0 16px 40px rgba(0,0,0,.14);
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;font-family:var(--font);background:var(--bg);color:var(--t1);-webkit-font-smoothing:antialiased;overflow:hidden}
::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-thumb{background:#CBD5E1;border-radius:3px}
::-webkit-scrollbar-track{background:#F1F5F9}
body::before{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(ellipse 60% 40% at 8% -5%,rgba(37,99,235,.05) 0%,transparent 60%),
             radial-gradient(ellipse 40% 30% at 92% 105%,rgba(5,150,105,.04) 0%,transparent 55%)}

/* ── SCREENS ── */
.screen{position:fixed;inset:0;z-index:10;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .25s}
.screen.active{opacity:1;pointer-events:all}

/* ── LOADING ── */
#scr-load{flex-direction:column;gap:14px}
.ld-ic{width:64px;height:64px;border-radius:18px;background:linear-gradient(135deg,#3B82F6,#2563EB);display:flex;align-items:center;justify-content:center;font-size:30px;color:#fff;animation:spin-in .5s cubic-bezier(.34,1.56,.64,1);box-shadow:0 8px 22px rgba(37,99,235,.25)}
.ld-tt{font-size:18px;font-weight:800;color:var(--t1)}
.ld-dt{display:flex;gap:5px}.ld-dt span{width:5px;height:5px;border-radius:50%;background:var(--cb);opacity:.3;animation:dot-blink 1.2s ease-in-out infinite}
.ld-dt span:nth-child(2){animation-delay:.2s}.ld-dt span:nth-child(3){animation-delay:.4s}
/* ⚡ 2026-07-20 상시 렉 실측 fix: .screen은 비활성 시 opacity:0(display 유지)라 로딩 점 3개의
   dot-blink가 로그인/관제 화면 뒤에서 영원히 틱 — 안 보이는 애니메이션도 매 프레임 스타일 틱+
   프레임 파이프라인 상시 가동(트레이스 실측: 평시 유휴 메인 709→413ms/8s·컴포지터 164→19ms).
   비활성 screen에서는 정지(시각 불변 — opacity:0 뒤라 원래 안 보임). */
.screen:not(.active) .ld-dt span{animation:none}

/* ── LOGIN ── */
#scr-login{padding:16px;background:linear-gradient(160deg,#F8FAFC,#EFF6FF)}
.lcard{width:100%;max-width:380px;background:#fff;border:1px solid var(--bdr);border-radius:22px;overflow:hidden;box-shadow:0 20px 50px rgba(15,23,42,.08),0 4px 12px rgba(15,23,42,.04)}
.lcard-top{padding:30px 28px 22px;background:linear-gradient(135deg,#F8FAFC,#EFF6FF);border-bottom:1px solid var(--bdr);text-align:center}
.lcard-ic{width:54px;height:54px;border-radius:14px;margin:0 auto 11px;background:linear-gradient(135deg,#3B82F6,#2563EB);display:flex;align-items:center;justify-content:center;font-size:24px;color:#fff;box-shadow:0 6px 18px rgba(37,99,235,.25)}
.lcard-top h2{font-size:17px;font-weight:800;margin-bottom:2px;color:var(--t1)}.lcard-top p{font-size:11px;color:var(--t2)}
.lcard-body{padding:22px 26px 26px;display:flex;flex-direction:column;gap:12px}
.btn-google{width:100%;padding:13px;background:#fff;border:1px solid var(--bdr);border-radius:11px;color:#1F2937;font-size:13px;font-weight:700;font-family:var(--font);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:9px;box-shadow:0 1px 3px rgba(15,23,42,.06);transition:all .15s}
.btn-google:hover{background:#F9FAFB;box-shadow:0 2px 8px rgba(15,23,42,.08)}
.btn-google:active{transform:scale(.99)}
.btn-google svg{width:18px;height:18px;flex-shrink:0}
.login-note{font-size:10px;color:var(--t3);text-align:center;line-height:1.7}
.login-err{min-height:13px;font-size:11px;color:var(--cc);text-align:center}

/* ══ APP ══ */
#scr-app{align-items:stretch;justify-content:stretch;padding:0}
.app-wrap{display:flex;width:100%;height:100%;position:relative;z-index:1}

/* ── SIDEBAR ── (Phase 1c 2026-05-19: Wanted DS 셸 리스킨 — 시각 속성값만, 마크업/id/핸들러 0 변경)
   목업 src-v3/common.jsx Sidebar 기준: surface 흰 카드, 메뉴 active = 둥근 brand-subtle pill(border-left 막대 폐기),
   av 원형 brand 그라데이션. --sw 폭은 정보밀도 유지로 불변(목업 232 미적용 — 레이아웃 영향). */
.sidebar{width:var(--sw);flex-shrink:0;height:100%;background:var(--w-surface);border-right:1px solid var(--w-hairline);display:flex;flex-direction:column;z-index:20;box-shadow:var(--w-shadow-emphasize)}
.sb-logo{padding:var(--w-space-20) var(--w-space-14) var(--w-space-16);border-bottom:1px solid var(--w-hairline);overflow:hidden}
/* 브랜드 = 목업 BLBrand 재현 (사용자 요청 2026-05-19: 합본 다크 PNG → 흰 아이콘박스). PNG 폐기, 인라인 SVG+텍스트 */
.sb-bl{display:inline-flex;align-items:center;gap:10px}
.sb-bl-mark{width:34px;height:34px;border-radius:8px;overflow:hidden;display:inline-flex;flex-shrink:0;box-shadow:0 1px 3px rgba(0,0,0,.14)}
.sb-bl-mark img{width:100%;height:100%;display:block;object-fit:cover}
.sb-bl-tx{display:flex;flex-direction:column;gap:1px;min-width:0}
.sb-bl-wm{font-family:"Wanted Sans Variable",Pretendard,sans-serif;font-weight:800;font-size:18px;letter-spacing:-.028em;color:var(--w-t1);line-height:1;white-space:nowrap}
.sb-bl-wm b{color:var(--w-brand);margin-left:2px;font-weight:800}
.sb-bl-sub{font-size:9.5px;letter-spacing:.18em;color:var(--w-t3);font-weight:600;text-transform:uppercase;white-space:nowrap}
.sb-clk{padding:var(--w-space-14) var(--w-space-14);border-bottom:1px solid var(--w-hairline);font-family:var(--mono)}
.sb-clk-t{font-size:24px;font-weight:700;color:var(--w-t1);letter-spacing:.02em}
.sb-clk-d{font-size:10px;color:var(--w-t3);margin-top:4px;font-weight:600}
.sb-user{padding:var(--w-space-14) var(--w-space-14);border-bottom:1px solid var(--w-hairline);display:flex;align-items:center;gap:10px}
.sb-av{width:32px;height:32px;border-radius:var(--w-radius-circle);background:linear-gradient(135deg,var(--w-brand),var(--w-brand-strong));color:#fff;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;flex-shrink:0}
.sb-un{font-size:13px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--w-t1)}
.sb-ur{font-size:10px;color:var(--w-t3);margin-top:2px}
nav.sb-nav{flex:1;padding:var(--w-space-8) var(--w-space-12);overflow-y:auto}
.ni{display:flex;align-items:center;gap:11px;padding:11px 12px;cursor:pointer;color:var(--w-t2);font-size:13px;font-weight:600;border-left:2.5px solid transparent;border-radius:var(--w-radius-10);margin-bottom:2px;transition:all .14s;white-space:nowrap}
.ni:hover{background:var(--w-surface-muted);color:var(--w-t1)}.ni.active{background:var(--w-brand-subtle);color:var(--w-brand);border-left-color:transparent;font-weight:800}
.ni i{font-size:16px;width:18px;text-align:center;flex-shrink:0}
.nb{margin-left:auto;background:var(--cc);color:#fff;font-size:10px;font-weight:800;padding:1px 7px;border-radius:var(--w-radius-pill);flex-shrink:0;display:none}.nb.show{display:block}
.sb-foot{padding:var(--w-space-12) var(--w-space-14);border-top:1px solid var(--w-hairline);background:var(--w-surface)}
.btn-lo{width:100%;padding:10px;background:transparent;border:1px solid var(--w-border);border-radius:var(--w-radius-10);color:var(--w-t3);font-size:11.5px;font-weight:600;font-family:var(--font);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;transition:all .14s}
.btn-lo:hover{border-color:var(--cc);color:var(--cc);background:var(--w-red-soft)}

/* ── 🚨 위험 전광판 (사이드바 시계 아래) ──
   3가지 상태:
   .rb-quiet    : critical=0 & warning=0 → 회색 톤, "정상" 표기
   .rb-warn     : warning만 있음 → 주황 톤, 깜빡임 없음
   .rb-crit     : critical 있음 → 빨강 채움 + 깜빡임, 마퀴 굴림
   클릭 시 가장 위험한 기사로 jumpToDriver() */
/* Phase 1c (2026-05-19): 목업 common.jsx 위험 전광판 = 사이드바 풀폭 띠 → margin 있는 둥근 카드형.
   ★ issues-perf 정책 절대 보존: 마퀴 OFF/::before opacity-only/animation:none — 시각 컨테이너(틴트·보더·radius)만 Wanted 토큰화. */
.risk-board{cursor:pointer;margin:var(--w-space-14) var(--w-space-16) var(--w-space-6);padding:12px 14px;border-radius:var(--w-radius-16);border:1px solid var(--w-border);transition:background .2s}
.risk-board.rb-quiet{background:var(--w-surface-muted);color:var(--w-t2);border-color:var(--w-border)}
.risk-board.rb-warn{background:var(--w-orange-soft);color:var(--w-orange-ink);border-color:var(--w-orange-line)}
/* ★ rb-crit-flash 기존엔 background-gradient 무한 변경 → paint 비쌈.
   ::before 오버레이 opacity만 변경 → GPU 합성, paint cost 0 */
.risk-board.rb-crit{
  background:var(--w-red-soft);
  color:var(--w-red-ink);border-color:var(--w-red-line);
  position:relative;overflow:hidden;
}
.risk-board.rb-crit>*{position:relative;z-index:1}
/* ::before 펄스 OFF (2026-05-14 리소스 절감) — 정적 진한 배경(.rb-crit 본체)으로 경고 충분.
   본체 그라데이션 + ⚠️ 아이콘 + 카운트로 critical 식별 가능. */
.risk-board.rb-crit::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background:linear-gradient(135deg,#FECACA,#EF4444);
  opacity:.35;
}
.risk-board:hover{filter:brightness(1.04)}
.rb-head{display:flex;align-items:baseline;gap:8px;line-height:1}
.rb-ico{font-size:14px}
.rb-cnt{font-family:var(--mono);font-size:26px;font-weight:900;letter-spacing:-.04em}
.rb-lbl{font-size:11px;font-weight:800;letter-spacing:.4px}
.rb-marquee{margin-top:6px;height:14px;overflow:hidden;font-size:11px;font-weight:700;white-space:nowrap}
.rb-track{display:inline-block;padding-left:0;will-change:auto}
/* 마퀴 OFF (2026-05-14 리소스 절감) — transform 무한 합성 cost 누적.
   text-overflow ellipsis로 잘리지만 카운트("28명") + ⚠️ 아이콘으로 충분히 식별. 첫 5명만 보임. */
.risk-board.rb-crit .rb-marquee{text-overflow:ellipsis}
.risk-board.rb-crit .rb-track{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 사이드바가 콘텐츠로 좁아질 때 깨지지 않도록 */
.sidebar{min-width:var(--sw);transition:width .25s ease,min-width .25s ease,opacity .2s ease}
/* ── 사이드바 접기/펼치기 (2026-05-14) ── 데스크탑 floating 토글. 모바일은 기존 mhdr 햄버거가 담당. */
.sb-tg{
  position:fixed;top:10px;left:calc(var(--sw) + 8px);z-index:30;
  width:36px;height:36px;border-radius:var(--w-radius-10);border:1px solid var(--w-border);background:var(--w-surface);
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  color:var(--w-t2);box-shadow:var(--w-shadow-emphasize);
  transition:left .25s ease,background .14s,color .14s,border-color .14s;
}
.sb-tg:hover{background:var(--w-surface-muted);color:var(--w-t1);border-color:var(--w-border-strong)}
.sb-tg i{font-size:18px;line-height:1}
body.sb-collapsed .sb-tg{left:8px}
/* ★ [구조 교정 2026-07-14] 데스크톱 접기 규칙은 반드시 @media(min-width:769px) 안에 둔다.
   미디어쿼리 밖(base)에 두면 특이도 0,2,0 이라 모바일 off-canvas 규칙(.sidebar{width:240px}, 0,1,0)을
   이기고 모바일에도 그대로 걸린다 → 모바일에서 되받아치는 오버라이드(width/opacity 재선언)를 낳고,
   그 width/opacity 트랜지션이 transform 슬라이드와 겹쳐 깜빡인다(폴드뿐 아니라 일반 안드로이드도 재현).
   ★ 안티-회귀: 이 규칙을 base 로 되돌리지 말 것. 모바일 사이드바는 off-canvas transform 전용이다.
   (→ issues-perf.md) */
@media(min-width:769px){
  body.sb-collapsed .sidebar{width:0;min-width:0;overflow:hidden;opacity:0;border-right-color:transparent;box-shadow:none}
}

/* ── MOBILE ── */
/* .mhdr = 모바일 전용 헤더(목업 데스크톱 TopBar는 라이브 admin에 부재 — 동기화/벨/기어 핸들러 없어
   비기능 셸 신설 금지. Wanted surface/hairline 토큰만 정합, 구조·핸들러 불변). */
.mhdr{display:none;height:50px;background:var(--w-surface);border-bottom:1px solid var(--w-hairline);padding:0 14px;align-items:center;justify-content:space-between;flex-shrink:0;box-shadow:var(--w-shadow-emphasize)}
.btn-mnu{background:var(--w-surface-alt);border:1px solid var(--w-border);border-radius:var(--w-radius-8);color:var(--w-t1);font-size:15px;padding:5px 9px;cursor:pointer}
.sb-ovl{display:none;position:fixed;inset:0;z-index:15;background:rgba(15,23,42,.4)}

/* ── MAIN ── */
.main-area{flex:1;display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}
.main-cnt{flex:1;display:flex;flex-direction:column;overflow:hidden}
.page{display:none;flex-direction:column;height:100%;overflow:hidden}.page.active{display:flex}

/* ══════════════════════════════════════════════
   관제 현황 — 핵심 페이지
══════════════════════════════════════════════ */

/* 요약 대시보드 — Phase 1c (2026-05-19): 목업 common.jsx SummaryChip = surface 위 평면 둥근 칩.
   배경 슬레이트→surface, 칩 radius 12·평면(섀도 제거), active=brand-subtle+ring. 마크업/data-f/id 0 변경. */
.sum-strip{display:flex;flex-wrap:wrap;align-items:stretch;background:var(--w-surface);padding:var(--w-space-14) var(--w-space-24);gap:var(--w-space-8);flex-shrink:0;border-bottom:1px solid var(--w-hairline)}
.si{display:flex;align-items:center;gap:8px;padding:8px 14px;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-12);flex-shrink:0;white-space:nowrap;cursor:pointer;transition:all .15s}
/* 펀치업: 요약칩 hover 미세 엘리베이션(sum-strip=그리드 무관 영역, 의도적 인터랙션 →
   "디자인된 UI" 체감. transform은 칩 1개 hover만 — perf 무관). */
.si:hover{background:var(--w-brand-subtle);border-color:var(--w-brand-hairline);transform:translateY(-1px)}
.si.si-on{background:var(--w-brand-subtle);border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.10)}
.si-num{font-size:19px;font-weight:900;font-variant-numeric:tabular-nums;font-family:var(--mono);line-height:1;color:var(--w-t1)}
.si-lbl{font-size:11px;color:var(--w-t3);font-weight:600;line-height:1.4}
.si-pip{width:8px;height:8px;border-radius:50%;flex-shrink:0}

/* 필터 바 — Phase 1d (2026-05-19): 목업 common.jsx FilterBar/DensityToggle 톤.
   배경 슬레이트→surface-alt, input/select radius 10·brand focus ring, 밀도토글 brand active.
   "내 담당" 보라 토글은 목업도 보라(#7C3AED) — 정합만. 마크업/id/data-d/data-s 0 변경. */
.ctrl-bar{display:flex;align-items:center;gap:var(--w-space-8);padding:10px var(--w-space-24);background:var(--w-surface-alt);border-bottom:1px solid var(--w-hairline);flex-shrink:0;flex-wrap:wrap}
.fi{padding:5px 9px;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-10);color:var(--w-t1);font-size:11px;font-family:var(--font);outline:none;height:27px}
.fi:focus{border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.10)}.fi option{background:var(--w-surface)}
.fi-src{min-width:150px}.fi-src::placeholder{color:var(--w-t3)}
.den-tog{display:flex;gap:2px;background:var(--w-surface-muted);border:1px solid var(--w-border);border-radius:var(--w-radius-10);padding:3px}
.den-btn{padding:3px 10px;border-radius:var(--w-radius-6);font-size:10px;font-weight:700;color:var(--w-t2);cursor:pointer;border:none;background:transparent;font-family:var(--font);transition:all .14s}
.den-btn.active{background:var(--w-brand);color:#fff}
/* 담당자 스코프 토글 — den-tog와 유사한 스타일, "내 담당만" 활성 시 보라 강조로 시각적 차별화 (목업도 보라 #7C3AED) */
.scope-tog{display:flex;gap:2px;background:#F5F3FF;border:1px solid #DDD6FE;border-radius:var(--w-radius-10);padding:3px}
.scope-btn{padding:3px 10px;border-radius:var(--w-radius-6);font-size:10px;font-weight:800;color:#6D28D9;cursor:pointer;border:none;background:transparent;font-family:var(--font);transition:all .14s;display:inline-flex;align-items:center;gap:3px}
.scope-btn.active{background:#7C3AED;color:#fff;box-shadow:0 1px 3px rgba(124,58,237,.35)}
.scope-cnt{font-size:9px;opacity:.85;font-family:var(--mono);font-weight:600;padding:0 4px;border-radius:3px;background:rgba(255,255,255,.18)}
.scope-btn:not(.active) .scope-cnt{background:#EDE9FE;color:#5B21B6}
/* 담당자 뷰 드롭다운 — scope-tog 보라 계열 미러. active=다른 담당자 뷰로 보는 중(명확 표시·닫힌 셀렉트에 "이름 뷰 · N명") */
.scope-mgr{padding:4px 8px;border-radius:var(--w-radius-10);border:1px solid #DDD6FE;background:#F5F3FF;color:#6D28D9;font-size:10px;font-weight:800;font-family:var(--font);cursor:pointer;max-width:190px}
.scope-mgr.active{background:#7C3AED;border-color:#7C3AED;color:#fff;box-shadow:0 1px 3px rgba(124,58,237,.35)}
.btn-ap2{padding:5px 11px;background:var(--w-green);border:none;border-radius:var(--w-radius-10);color:#fff;font-size:11px;font-weight:700;font-family:var(--font);cursor:pointer;display:flex;align-items:center;gap:6px;height:27px;margin-left:auto;transition:opacity .14s}
.btn-ap2:hover{opacity:.92}

/* ══════════════════════════════════════════════
   ★ 핵심: CSS GRID 컬럼 레이아웃 (PC 와이드 모니터 최적화)
   col1:알람(54) col2:출발(54) col3:기사·노선(1fr)
   col4:상태 패널(1.5fr) col5:액션(150)
   ─ 액션 컬럼: 6버튼 × 28px(컴팩트 24px) + gap 4px = 28*6+4*5 = 188px(160px) 균일
   ─ 상태 패널: 4 체크포인트 grid + 우측 플래그 — 최소 380px 확보 (truncation 방지)
   ─ 헤더(.ctrl-th)와 본문(.dr) 동일 grid → 컬럼 정렬 일관
══════════════════════════════════════════════ */
.ctrl-th,.dr{
  display:grid;
  /* 정보 컬럼은 고정 폭(콘텐츠 길이와 무관 모든 row 동일 정렬) — auto/content 쓰면 행마다
     너비가 달라져 수직 정렬이 깨짐(이슈 재발 방지). 잉여 가로 공간은 모두 인증 컬럼(1fr)이
     흡수, 내부 pill flex-grow 로 비례 확장 → 와이드 모니터에서 pill이 가득 차고 sp-flag
     직전 공백도 없음.
     컴팩트 모드(≤1500px)는 좁은 모니터/줌인 환경에서도 행이 안 잘리도록 일찍 발동.
     액션 컬럼 232px = 7버튼×28 + 6gap×4 + 12padding (📝 메모 추가로 6→7개).
     ★ 텍스트 크기 변경 시 반드시 컬럼 폭 동시 재계산 (2026-05-14 알람·차/출·차량번호 14/13px 키우며 폭 보강):
       - col-time 64→72 (5char mono 14px = ~50px + padding)
       - col-dep 96→132 (mono `05:41 ↦ 06:35` 11char 13px = ~85px + arrow + gap)
       - col-info 280→320 (차량번호 13px 7char ≈ 55px + 🚌 + 이름·노선·거래처 칩) */
  grid-template-columns:72px 132px 440px minmax(580px,1fr) 232px;
  align-items:center;
}
@media (max-width:1500px){
  /* 컴팩트: 정보 380 고정(sub-grid 295 + 1fr 노선 ~70), 인증 minmax(510,1fr), 액션 208
     (2026-06-18 전화버튼 복원 7버튼: 7×28 + 6×gap4 + pad12 = 232 / 컴팩트 7×24 + 6×4 + 12 = 208) */
  .ctrl-th,.dr{grid-template-columns:66px 120px 380px minmax(510px,1fr) 208px}
}
/* 모든 행 셀 box-sizing 명시 — padding/border 포함 안 깨지게 */
.ctrl-th>*,.dr>*,.cn,.sp-flag,.rb2,.time-alarm,.info-plate,.info-cli,.info-co,.info-mgr{box-sizing:border-box}

/* ── 컬럼 헤더 ── Phase 1d (2026-05-19): 목업 common.jsx ColumnHeader 톤(슬레이트→bg, hairline, uppercase).
   ★ grid-template-columns(L241/246 ROW_COLS, DriverRow 공유)·height·padding-right:15px(scrollbar-gutter 보정)
   전부 불변 — 색/배경/타이포만 변경. .th-cell border-right 변경은 box-sizing:border-box라 트랙 폭 무영향. */
.ctrl-th{
  background:var(--w-bg);
  border-bottom:1px solid var(--w-hairline);
  flex-shrink:0;font-size:10px;font-weight:800;color:var(--w-t3);
  letter-spacing:.5px;text-transform:uppercase;
}
.ctrl-th.dn-c{height:26px}.ctrl-th.dn-n{height:30px}
/* 헤더 셀 — 모든 컬럼 가운데 정렬, 본문 셀과 grid-template-columns 동일 → 한 화면 정렬 일관 */
.th-cell{display:flex;align-items:center;justify-content:center;padding:0 4px;gap:4px;text-align:center;border-right:1px dashed var(--w-hairline);height:100%}
.th-cell:last-child{border-right:none}
/* 인증 상태 헤더 — 6노드(알람/기상/차고지모바일/차고지GPS/출발지모바일/출발지GPS) 위에 정렬된 라벨 범례.
   (2026-08-10 사용자 지시로 5노드 → 6노드. 출발지도 차고지처럼 모바일/GPS 를 각각 보여준다.)
   본문 status-panel = <cp-row repeat(6,1fr) flex:1> + <sp-flag 96px(컴팩트 84) 고정>과 동형 구조로
   라벨이 6노드 컬럼 바로 위에 근접 정렬. 제목 "인증 상태" 텍스트는 생략(6라벨이 컬럼 자체를 설명). */
.th-status{display:flex;width:100%;align-items:center}
/* ★ 2026-08-10 6신호 — 출발지도 차고지처럼 모바일/GPS 두 칸(사용자 지시). 라벨이 길어져(차고지모바일)
   nowrap+말줄임으로 **행 높이가 늘지 않게** 가둔다(줄바꿈되면 헤더가 2줄이 되어 목록이 밀린다). */
.th-steps{flex:1;display:grid;grid-template-columns:repeat(6,1fr);font-size:9px;font-weight:800;color:var(--w-t2);text-align:center}
.th-steps>span{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 1px}
.th-step-spacer{width:96px;flex-shrink:0}
.ctrl-th.dn-c .th-step-spacer{width:84px}   /* 컴팩트 모드 flag 84px 정합 */
/* 신호등 헤더: 3개 레이블 정렬 */
.th-tl{display:flex;align-items:center;justify-content:center;gap:0}
.th-tl-lbl{font-size:8px;font-weight:700;color:var(--w-t2);text-align:center;width:20px}
.th-tl-sp{width:16px}

/* ── 빈 셀 placeholder — 정보 없을 때 회색 italic 톤으로 일관 표시 (행 간격 동일 유지) ── */
.ph-empty{color:#94A3B8;font-weight:500;font-size:10px;font-style:italic;letter-spacing:.1px;opacity:.75}
.ph-empty.ph-sm{font-size:10px}
.ph-empty.ph-tiny{font-size:10px;font-family:var(--mono)}
/* Phase 1e: 목업 미동기화/빈 배지 톤 — surfaceAlt+border+t4 italic (목업 L402~405 "미동기화" 톤). */
.info-plate.ph-plate{color:var(--w-t4);background:var(--w-surface-alt);border-color:var(--w-border);font-style:italic;opacity:.8}
.info-cli.ph-cli   {color:var(--w-t4);background:var(--w-surface-alt);border-color:var(--w-border);font-style:italic;opacity:.8;font-weight:600}
.info-co.ph-co     {color:var(--w-t4);background:var(--w-surface-alt);border-color:var(--w-border);font-style:italic;opacity:.8;font-weight:600}

/* ── 기사 행 ── */
.dr{
  border-left:4px solid transparent;
  /* 펀치업(2026-05-19 CSS-only): 행 구분선 #F3F4F6→Wanted hairline 토큰(동일 1px,
     폭/행높이 무영향 — 색만 일관·또렷). transition에 box-shadow 추가(hover 엘리베이션용,
     무한 애니메이션 아님 — issues-perf 펄스 정책 무관). */
  border-bottom:1px solid var(--w-hairline);
  background:#fff;
  transition:background .12s,border-left-color .2s,box-shadow .12s;
  cursor:default;
  /* ★ content-visibility:auto 완전 제거 (2026-05-18, 8회 실패 끝 최종 결론).
     사유: 진단 실측으로 확정 — 폴더블은 *펼치면* `pointer:fine=true`로 보고(터치인데도)해
     어떤 미디어쿼리(폭/pointer)로 게이트해도 paint-skip이 켜져 done(dr-past) 행 뒤로
     2400px+ 유령 공백 + 합성레이어 critical pill 고아를 만듦. 이 앱의 기기 매트릭스와
     content-visibility:auto는 근본적으로 양립 불가. 73~500행은 `contain:layout style`(행간
     레이아웃 격리) + renderCtrl rAF 디바운스 + opacity-only 펄스로 충분. 진짜 500행 데스크톱
     페인트가 측정상 병목이면 그때 *제대로 된 JS 가상화*(content-visibility 아님)로 해결.
     contain-intrinsic-size/content-visibility 어떤 형태로도 .dr 재도입 금지. (→ issues-perf.md) */
  contain:layout style;
}
/* 펀치업: hover 엘리베이션 — 정적 행 → "반응하는 UI" 인지. inset box-shadow라
   레이아웃/행높이 0 영향(외곽 그림자 아님 → 인접행 밀림 없음), hover 1행만 페인트
   (500행 스크롤 비용 0 — 무한 애니메이션 아님, issues-perf 가드 무관). */
.dr:hover{background:#F9FAFB;box-shadow:inset 0 0 0 1px var(--w-border-strong)}
/* 밀도별 높이 — 모든 컬럼 단일 행 → 더 컴팩트 (500명 관제 시야 극대화).
   컴팩트 모드 36px / 일반 모드 42px */
.ctrl-body.dn-c .dr{min-height:36px}
.ctrl-body.dn-n .dr{min-height:42px}

/* 위험도별 행 스타일
   ★ critical 펄스 — 기존엔 background-gradient를 매 frame 갱신 → paint 비용 큼.
   이제 ::before 오버레이의 opacity만 변경 → GPU 합성으로 paint cost 0. */
/* Phase 1e: 목업 DriverRow riskTint — 좌측 강조 그라데이션 → Wanted soft 평면 단색
   (위험 시인성은 좌측 4px 컬러바가 담당, 목업 L330~335 톤). border-left 토큰화.
   ★ ::before 펄스 OFF 정책 유지(opacity:1 정적), .dr base contain:layout style 불변,
   paint-skip/intrinsic-size 재도입 0(issues-perf 8회 실패 가드) — 색/배경만. */
.dr-cc{
  position:relative;
  border-left-color:var(--w-red)!important;
  background:#FFF5F5;
}
/* ::before 펄스 OFF (2026-05-14 리소스 절감) — 본체 soft 배경(.dr-cc) + 빨간 border-left로 충분.
   critical 행이 30개 중 5~10개 동시 펄스 시 GPU 합성 누적. 정적 좌측 보강만 유지. */
.dr-cc::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background:linear-gradient(90deg,rgba(240,62,62,.10) 0%,rgba(240,62,62,.035) 35%,transparent 70%);
  opacity:1;
}
.dr-cc>*{position:relative;z-index:1}   /* 자식 요소가 ::before 위로 올라오게 */
.dr-cw{border-left-color:var(--w-orange)!important;background:#FFFAF2}
.dr-cy{border-left-color:var(--w-yellow)!important;background:#FFFCF5}
.dr-cb{border-left-color:var(--w-brand)!important;background:#F4F8FF}
.dr-cg{background:var(--w-surface)}
.dr-cp{background:var(--w-surface)}
/* ★ opacity:.5는 늦은 시간대(거의 전원 완료)에 화면 전체가 50% 흐려져 "깨짐/빈칸"으로
   오인됨(2026-05-18). 완료 행 de-emphasis는 유지하되 가독성 확보 위해 .72로 완화. */
.dr-past{opacity:.72}
/* 오늘 제외 안내 행 */
.dr-excl{opacity:.5;background:#F9FAFB!important;border-left-color:transparent!important}
/* ── 이슈메모(reportNote) 노출 줄 — 관제 행(.dr) 바로 아래 전체폭 블록(grid 아님).
   내용 많으면 줄넘김해 전부 보임(pre-wrap·break-word). 앰버 톤 + 좌측 컬러 accent.
   클릭=메모 모달. 항목6 기록용(reportNoteToReport===false)은 .dr-note-tag만 부착. ── */
.dr-note{
  padding:6px 12px 7px;margin:0 0 1px;background:var(--w-orange-soft);
  border-left:3px solid var(--w-orange);font-size:12px;line-height:1.5;color:var(--w-t1);
  white-space:pre-wrap;word-break:break-word;cursor:pointer;transition:background-color .14s;
}
.dr-note:hover{background:#FFEAD0}
.dr-note b{color:var(--w-orange-ink);font-weight:800;margin-right:2px}
.dr-note .dr-note-txt{color:var(--w-t1);font-weight:500}
.dr-note .dr-note-tag{display:inline-block;margin-left:7px;padding:1px 7px;font-size:10px;font-weight:700;
  color:var(--w-t3);background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-pill);vertical-align:middle}
body.ctrl-readonly .dr-note{pointer-events:none}
/* ── 고정메모(dispatchNote) 노출 줄 — 관제 행(.dr) 바로 아래 전체폭 블록(.dr-note 앰버 미러·블루 톤).
   영구 인수인계 특이사항(영구 메타). 📌 아이콘 + 전문(pre-wrap·줄넘김 전부 노출). 클릭=openDispNoteModal.
   당일메모(.dr-note 앰버·reportNote)와 색·채널 분리. dr 직후 disp:${id} keyed item push(당일메모 note 앞). ── */
.dr-disp{
  padding:6px 12px 7px;margin:0 0 1px;background:var(--w-blue-soft);
  border-left:3px solid var(--w-brand);font-size:12px;line-height:1.5;color:var(--w-t1);
  white-space:pre-wrap;word-break:break-word;cursor:pointer;transition:background-color .14s;
}
.dr-disp:hover{background:var(--w-blue-line)}
.dr-disp b{color:var(--w-blue-ink);font-weight:800;margin-right:2px}
.dr-disp .dr-disp-txt{color:var(--w-t1);font-weight:500}
body.ctrl-readonly .dr-disp{pointer-events:none}
/* ── 과거 이슈메모 carry-over 줄 — 당일메모(.dr-note 앰버)를 회색 톤으로 미러(시각 구분).
   교대 인계용(작성일 포함 7일 = 오늘 + 이전 6일).
   ★ 2026-08-20 편집·삭제 개방 — 종전 "display-only(편집 불가)" 는 폐기(클릭 → openPastNoteModal).
   📝 날짜 배지(MM/DD(요일)) 부착. 오늘 관제(ctrlIsToday)에서만 push — dr→고정📌→당일📝→과거(회색·최신순) 순. ── */
.dr-note-yest{
  padding:5px 12px 6px;margin:0 0 1px;background:var(--w-surface-alt,#F3F4F6);
  border-left:3px solid var(--w-border-strong,#D1D5DB);font-size:11.5px;line-height:1.5;
  color:var(--w-t2);white-space:pre-wrap;word-break:break-word;opacity:.82;
  /* 2026-08-20 편집·삭제 개방 — 클릭 가능해졌으므로 커서·호버로 알린다(종전 cursor:default) */
  cursor:pointer;transition:background-color .14s,opacity .14s}
.dr-note-yest:hover{background:var(--w-surface-muted,#E9EBEF);opacity:1}
.dr-note-yest b{color:var(--w-t2);font-weight:700;margin-right:2px}
.dr-note-yest .dr-note-txt{color:var(--w-t2);font-weight:500}
.dr-note-yest .dr-yest-badge{display:inline-block;margin-right:6px;padding:1px 7px;font-size:10px;
  font-weight:700;color:var(--w-t3);background:var(--w-surface);border:1px solid var(--w-border);
  border-radius:var(--w-radius-pill);vertical-align:middle}

/* ── 컬럼1: 알람시각 (한 줄, 컬럼 중앙). 64px 컬럼에 안전하게 맞도록 padding 절약 ── */
.col-time{padding:0 2px;display:flex;align-items:center;justify-content:center}
/* 알람시각 텍스트 — 윤일향 실장 피드백(2026-05-14): 이름처럼 굵고 검정으로.
   기존 파랑(#1D4ED8) → var(--t1) 검정, font-size 13→14px, 배경/테두리는 연한 톤 유지. */
/* Phase 1e: 목업 DriverRow ① 알람칩 톤 — 중립=Wanted surfaceMuted+t1, radius8(목업 L357).
   테두리 제거(목업은 무테 칩), 폭 산식 무영향(box-sizing:border-box, 1.5px border→0은 트랙 불변). */
/* 펀치업(2026-05-19 CSS-only): 알람칩 = 더 의도적 칩(목업 padding:5px 8px 방향).
   ★ col-time 트랙 72(컴팩트 66) 가드: 14px mono `04:30` 5자 ≈ 50 + padding 14 = 64 < 72
   (컴팩트 13px ≈ 47 + 12 = 59 < 66) → 잘림/줄바꿈/가로스크롤 0. font-size/radius 불변. */
.time-alarm{
  font-size:14px;font-weight:900;font-family:var(--mono);color:var(--w-t1);letter-spacing:.2px;
  background:var(--w-surface-muted);border:1px solid transparent;border-radius:var(--w-radius-8);
  padding:4px 7px;line-height:1;white-space:nowrap;
}
.ctrl-body.dn-c .time-alarm{font-size:13px;padding:3px 6px;border-radius:var(--w-radius-6)}
/* 위험 강조 — crit=red 채움 흰글자, warn=orange 채움 흰글자 (목업 L357~360, 깔끔 — 글로우 제거) */
.dr-cc .time-alarm{color:#fff!important;background:var(--w-red)!important;border-color:var(--w-red)!important}
.dr-cw .time-alarm{color:#fff!important;background:var(--w-orange)!important;border-color:var(--w-orange)!important}
.dr-cc .dep-time{color:var(--w-red)!important;font-weight:900}

/* ── 컬럼2: 차고지·출발 (한 줄, 절대 겹치지 않도록 충분한 gap + flex-shrink:0) ── */
.col-dep{display:flex;align-items:center;justify-content:center;padding:0 4px;gap:5px;white-space:nowrap;overflow:hidden}
/* 차고지/출발 시각 — 윤일향 실장 피드백(2026-05-14): 이름처럼 굵고 검정으로 */
/* Phase 1e: 목업 DriverRow ② 차고지→출발 톤 — mono t1, arrow는 중립 t3(목업 L371 arrow color:t3). */
.dep-time{font-size:11px;font-weight:700;font-family:var(--mono);color:var(--w-t1);letter-spacing:.2px;line-height:1;flex-shrink:0}
.dep-arrow{font-size:10px;color:var(--w-t3);font-weight:800;flex-shrink:0;line-height:1}
.dep-dep{font-size:11px;font-weight:700;font-family:var(--mono);color:var(--w-t1);letter-spacing:.2px;line-height:1;flex-shrink:0}
.ctrl-body.dn-c .dep-time{font-size:10.5px}
.ctrl-body.dn-c .dep-dep{font-size:10.5px}
.ctrl-body.dn-c .col-dep{gap:4px}

/* ── 컬럼3: 기사·차량·노선·거래처 — sub-grid 4트랙 고정 정렬 (2026-05-14 윤일향 실장 피드백 "삐뚤빼뚤" 해소)
   flex packed 였을 때 이름 길이마다 다음 칩 시작 X 좌표가 달라져 행마다 columns 정렬이 어긋남.
   grid 고정 트랙(이름 110 / 차량번호 100 / 노선 1fr / 거래처 60)으로 모든 행에서 동일 X 시작 강제.
   placeholder(ph-empty, ph-plate, ph-cli)도 같은 트랙 점유 → 빈 항목 행도 정렬 일관.
   모바일(≤768px) 분기에서 `display:flex; flex-direction:column` 오버라이드. */
.col-info{
  padding:0 8px;min-width:0;display:grid;
  /* 차량번호 트랙 140px = 🚌(18) + space(4) + 7~8 chars × 13px mono + padding(14) + border(2) ≈ 138~150
     (한국 차량번호는 `서울72바6788`(7자) ~ `경기74아7740`(7자) — 일부 케이스 8자도 수용) */
  /* track1 110→136: 전화번호 확대(14px mono ≈125px + 아이콘) 수용. route(1fr·minmax(0,1fr))가 흡수·
     헤더는 col-info 단일 라벨이라 정렬 무영향. 이름(14px 한글)도 여유 ↑ */
  grid-template-columns: 136px 140px minmax(0,1fr) 60px;
  align-items:center;gap:6px;overflow:hidden;
}
/* Phase 1e: 목업 DriverRow ③ 기사·차량·노선·거래처 톤 — 차량배지=#F1F4FA+blueLine+t1
   (목업 L399~410 정상), client=brand(목업 L417), co=t3(L419), 노선=t2. radius7(L401).
   ★ border/padding 값 불변(1px·2px) → box-sizing:border-box로 sub-grid 트랙 폭(L377/389) 무영향. */
/* 펀치업(2026-05-19 CSS-only): 기사명=행의 시선 앵커 — 목업 DriverRow(fontSize:14/800) 정합으로
   13→14px(보조정보 노선과 위계 콘트라스트 ↑). 트랙폭 110(컴팩트 95) 불변 — 14px 한글 6자 ≈ 84px
   < 110, white-space:nowrap+ellipsis 보유라 잘림/줄바꿈/가로스크롤 0. line-height:1·min-width:0 유지. */
.info-name{font-size:14px;font-weight:800;color:var(--w-t1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1;min-width:0}
/* 기사명 클릭 = 지도 뷰 Lock-on (2026-08-20). 클릭 영역이 **이름 글자만** 덮도록 width:auto +
   align-self:flex-start — 트랙 폭(136px)을 다 먹으면 이름 오른쪽 여백까지 지도로 튀어 종전
   "이 기사만 보기"(col-info) 가 그만큼 죽는다. padding 은 음수 margin 으로 상쇄해 레이아웃 무영향. */
.info-name.nm-map{cursor:pointer;align-self:flex-start;width:auto;max-width:100%;padding:2px 4px;margin:-2px -4px;border-radius:4px}
.info-name.nm-map:hover{color:var(--w-brand);text-decoration:underline}
/* 차량번호 — 목업 차량 배지(연한 블루 톤 + 트럭 아이콘은 마크업 영역). justify-self:start로 트랙 안 좌측 packed. */
.info-plate{font-size:13px;font-weight:900;color:var(--w-t1);background:#F1F4FA;border:1px solid var(--w-blue-line);padding:2px 7px;border-radius:var(--w-radius-6);font-family:var(--mono);letter-spacing:.3px;white-space:nowrap;line-height:1;justify-self:start;max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* 펀치업: 노선=보조정보 → t2에서 t3로 한 단계 약하게(이름 14/t1과 위계 콘트라스트 확대,
   목업도 route는 t2지만 라이브 이름이 강해진 만큼 상대 약화로 "한눈 위계" 체감 ↑). 폭·줄 무영향. */
.info-route-text{font-size:11px;color:var(--w-t3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;font-weight:600}
.info-cli{font-size:9px;color:var(--w-brand);padding:2px 6px;border-radius:var(--w-radius-6);background:var(--w-brand-subtle);border:1px solid var(--w-brand-hairline);font-weight:800;letter-spacing:.2px;line-height:1;white-space:nowrap;justify-self:start;max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* 🤝 거래처 + 담당자 세로 스택 (2026-08-27) — col-info sub-grid 4번째 트랙(60px/컴팩트 55px) 단일 자식.
   ★ 자식 4개 불변식 보존이 목적이다(`info-plate-wrap` 패턴 미러). 트랙 폭·grid-template 미변경.
   행 높이도 안 늘어난다 — 좌측 이름+전화 스택이 이미 2줄이라 min-height(36/42px) 안에서 소화된다. */
.info-cli-wrap{min-width:0;max-width:100%;display:flex;flex-direction:column;gap:2px;align-items:flex-start;justify-content:center;justify-self:start;overflow:hidden}
/* 담당자 칩 — 기본은 영업(보라). 역할이 다르면 data-role 로 색만 갈아끼운다(MGR_ROLES 색과 동일 계열). */
.info-mgr{font-size:9px;color:#5B21B6;background:#F5F3FF;border:1px solid #DDD6FE;padding:2px 6px;border-radius:var(--w-radius-6);font-weight:800;letter-spacing:.2px;line-height:1;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
.info-mgr:hover{background:#EDE9FE;text-decoration:underline}
.info-mgr[data-role="callcenter"]{color:#1D4ED8;background:#EFF6FF;border-color:#BFDBFE}
.info-mgr[data-role="callcenter"]:hover{background:#DBEAFE}
.info-mgr[data-role="affiliate"]{color:#0F766E;background:#F0FDFA;border-color:#99F6E4}
.info-mgr[data-role="affiliate"]:hover{background:#CCFBF1}
.info-mgr.info-mgr-np,.info-mgr.ph-mgr{cursor:default}
.info-mgr.info-mgr-np:hover{text-decoration:none;background:#F5F3FF}
.info-mgr.ph-mgr{color:var(--w-t4);background:var(--w-surface-alt);border-color:var(--w-border);font-style:italic;opacity:.8;font-weight:600}
.info-mgr.ph-mgr:hover{text-decoration:none;background:var(--w-surface-alt)}
.info-co{font-size:9px;color:var(--w-t3);padding:2px 6px;border-radius:var(--w-radius-6);background:var(--w-surface-muted);border:1px solid var(--w-border);font-weight:700;letter-spacing:.2px;line-height:1;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* 차량번호 셀(track2) 세로 스택 — 차량번호 위 + 업체명(info-co) 아래. col-info 자식 수 4개 불변식 유지
   (info-name-wrap 패턴 미러). 긴 업체명은 info-co ellipsis로 흡수(트랙 140px 넘침 방지). */
.info-plate-wrap{min-width:0;max-width:100%;display:flex;flex-direction:column;gap:2px;align-items:flex-start;justify-content:center;justify-self:start;overflow:hidden}
/* 이름+전화번호 세로 스택 래퍼 — col-info sub-grid 트랙1(110/95px) 점유, 자식 4개 불변식 보존
   (래퍼가 track1 단일 자식, 안에서 이름 위·전화 아래로 stack). */
.info-name-wrap{min-width:0;display:flex;flex-direction:column;gap:2px;justify-content:center;overflow:hidden}
/* 🔁 대차 운행 중 표시 (2026-08-24) — 이름 오른쪽 작은 배지. 대차가 아닐 땐 이 래퍼 자체를 안 만든다
   (마크업 종전과 동일 → 회귀 0). 배지는 flex 축소 금지, 이름만 ellipsis 로 줄어든다. */
/* ⚠ 알람 꺼짐 (2026-08-24) — 시각 조합이 알림톡을 구조적으로 0회로 만드는 상태. 조용한 결함을
   콜센터가 아침에 바로 보도록 차고지↦출발 칸 아래에 붙인다. 해당 없으면 노드 자체를 안 만든다(회귀 0). */
.dep-alarm-off{display:block;margin-top:2px;font-size:8.5px;font-weight:900;color:#B91C1C;background:#FEE2E2;border:1px solid #FCA5A5;border-radius:4px;padding:1px 3px;line-height:1.2;white-space:nowrap;text-align:center}
.info-name-row{display:flex;align-items:center;gap:4px;min-width:0}
.info-sub{flex:0 0 auto;font-size:8.5px;font-weight:900;color:#B45309;background:#FEF3C7;border:1px solid #FDE68A;border-radius:4px;padding:1px 4px;line-height:1;white-space:nowrap;letter-spacing:.2px}
/* 좌측 기사명 아래 전화번호 — 클릭 시 복사+전화(window.copyAndCall). brand 톤 mono, 클릭 가능 표시. */
.info-phone{display:inline-flex;align-items:center;gap:3px;font-size:14px;color:var(--w-brand);font-family:var(--mono);font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:.3px;line-height:1;white-space:nowrap;cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;align-self:flex-start}
.info-phone:hover{text-decoration:underline}
.info-phone i{font-size:9px;opacity:.8}
.ctrl-body.dn-c .info-phone{font-size:12.5px}
/* 펀치업: 컴팩트 이름도 12→13px(트랙 95 불변 — 13px 한글 6자 ≈ 78 < 95, nowrap+ellipsis). */
.ctrl-body.dn-c .info-name{font-size:13px}
/* 컴팩트 모드 — sub-grid 트랙 폭 (이름 95 / plate 125 / 노선 1fr / 거래처 55) */
.ctrl-body.dn-c .col-info{grid-template-columns: 118px 125px minmax(0,1fr) 55px;gap:4px}
.ctrl-body.dn-c .info-plate{font-size:12px;padding:1px 6px}
.ctrl-body.dn-c .info-cli,.ctrl-body.dn-c .info-co,.ctrl-body.dn-c .info-mgr{font-size:8px;padding:1px 5px}
.ctrl-body.dn-c .info-route-text{font-size:10px}

/* ════════════════════════════════════════════════
   ★★★ 상태 패널 — 단일 라인 4 체크포인트 (대규모 인원 관제용 컴팩트 디자인)
   - 채널 분리: 알람·기상·차고지(앱)·GPS출발(차량) 4 미니 pill
   - 우측에 컴팩트 플래그 (완료/경고만, 대기 중은 생략)
   - 500명까지 한 화면에서 빠르게 스캐닝 가능 (행 높이 ~38px)
════════════════════════════════════════════════ */
.col-status{display:flex;align-items:center;padding:3px 4px;min-width:0}
/* Phase 1e: 목업 DriverRow ④ 5단계 컨테이너 박스만 — surfaceAlt + hairline + radius12
   (목업 L425~430). ★ gap/padding/height/width/flex/box-sizing 레이아웃 전부 불변(내부
   `cp`/`sp-flag` 5-stage 알약 = 1f 영역 절대 미접촉, 상태패널 생성 함수 DOM 0 변경). */
/* 펀치업(2026-05-19 CSS-only): 5단계 컨테이너 = 더 분명한 카드(목업 DriverRow ④
   padding:6px 12px 방향). ★ width:100%/height:32/gap:6/box-sizing 불변(그리드 산식 보존) —
   padding 3→4(수직)·8→10(수평)만. height:32 고정+box-sizing:border-box라 4px 수직패딩에도
   32-8=24 ≥ cp 22px(컴팩트 28-6=22 ≥ 20) 수용, 수평 +2는 cp-row flex-grow가 흡수
   (cp min-width:88/컴팩트78 불변, 인증컬럼 1fr 트랙폭 불변 → 잘림/스크롤 0). border
   hairline→border로 박스 또렷이(1px 동일·폭 무영향). */
.status-panel{
  width:100%;display:flex;align-items:center;gap:6px;
  padding:4px 10px;height:32px;border-radius:var(--w-radius-12);
  font-family:var(--font);
  background:var(--w-surface-alt);border:1px solid var(--w-border);
  position:relative;overflow:hidden;transition:background-color .2s,border-color .2s;
  box-sizing:border-box;
}

/* ════════ 5단계 신호 = StepTrack (원형 상태노드 5개 + 연결선) ════════
   [리디자인 2026-05-19] 가로 텍스트칩(.cp/.cp-lbl/.cp-time/.cp-tick)을
   목업 common.jsx StepTrack/StepNode(L114~187) 구조로 과감히 전환 —
   신호가 행에서 시각적 주인공. JS pill() 마크업도 .cn-* 노드로 동반 변경
   (계산부 byte-동일, 출력 조립부만). 구 .cp-* 셀렉터는 전부 .cn-*로 재작성
   (고아 CSS·죽은 셀렉터 0).
   ★ 폭 계약 대체: 구 `.cp{flex:1 1 88px;min-width:88px}` 가로 flex →
     `.cp-row{display:grid;grid-template-columns:repeat(6,1fr)}` 6등분 grid(2026-08-10 5→6).
     `.status-panel{width:100%}` 컨테이너가 인증컬럼 1fr 트랙을 채우는 계약은
     불변 → ROW_COLS(L252/257)·sub-grid·col-status 마운트 폭 산식 byte-불변.
     1fr(=인증컬럼/6)에 dot+시각 여유 수용 — 실측 노드 폭 1280/1440 66px · 1920 120px ·
     모바일 360px 47px 로 잘림/줄바꿈/가로스크롤 0(행 높이도 5노드 때와 동일).
   ★ 행높이 계약 보존: status-panel height:32(컴팩트28)·padding 펀치업값 불변.
     노드 = dot14(컴팩트12) + gap1 + cn-time(8.5px line-height1) ≈ 24 ≤ 가용
     24(32-8패딩). overflow:hidden 안전망 유지.
   ★ 6상태 색 의미분리 보존(issues-auth-gps 불변식): done=그린✓ /
     unv=앰버⚠ / off=빈원중립○ / crit=레드✕ / warn=오렌지! — done≠unv≠off
     hue 명확 이격. `cp-pulse` 2.4/1.8s 주기·정적 글로우 불변(issues-perf).
   ★ 연결선 = .cp-row::before 정적 1px(무한 애니메이션 절대 없음 — issues-perf
     ::before opacity OFF 정책 계승, paint cost 0). */
.cp-row{
  /* ★ 2026-08-10: 5 → 6 등분(출발지 모바일/GPS 분리). **컬럼 트랙 폭 산식은 미접촉** —
     늘어난 건 인증 컬럼 *안쪽* 밀도뿐이라 grid-template-columns(L282/288)·col-actions·col-info 불변. */
  display:grid;grid-template-columns:repeat(6,1fr);align-items:center;
  flex:1;min-width:0;position:relative;height:100%;
}
/* 노드 사이 진행 연결선 — 양끝 노드 1/10 inset(목업 L130), dot 중심 높이에 정적 배치 */
.cp-row::before{
  content:"";position:absolute;left:10%;right:10%;
  top:11px;height:2px;border-radius:var(--w-radius-2);
  background:var(--w-surface-muted);pointer-events:none;z-index:0;
}
/* 상태 노드 = 원형 dot + 라벨/시각 미니 세로스택. grid 1fr 셀이라 폭 100% 종속
   (구 .cp min-width:88 계약을 5등분 grid가 대체 — flex-basis 폭 산식 소멸). */
.cn{
  display:flex;flex-direction:column;align-items:center;gap:1px;
  position:relative;z-index:1;min-width:0;overflow:hidden;
}
.cn-dot{
  width:14px;height:14px;border-radius:var(--w-radius-circle);
  border:1.5px solid;flex-shrink:0;line-height:1;
  font-size:8.5px;font-weight:900;
  display:inline-flex;align-items:center;justify-content:center;
}
/* 시각 = 데스크톱 신호 본문(목업 {time||label} 규칙 — 계산부가 time을 "04:30"/"미인증"/
   "미감지"/"미도달"/"예정없음"으로 항상 채워주므로 시각 자체가 단계+상태를 전달). */
.cn-time{
  display:inline-flex;align-items:baseline;gap:2px;
  font-family:var(--mono);font-size:8.5px;font-weight:900;line-height:1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
}
/* 라벨(알/기/차/GPS/출) — 데스크톱은 시각이 본문이라 숨김, 모바일에서만 노출
   (모바일은 시각 글자 너무 작아 숨기고 라벨로 단계 식별 — 기존 .cp 모바일 정책 계승). */
.cn-lbl{display:none;font-family:var(--font);font-weight:900;letter-spacing:.2px;flex-shrink:0}
.cn-dly{font-size:8px;font-weight:900;font-style:normal;flex-shrink:0;padding:0 2px;border-radius:var(--w-radius-4)}
/* (2026-08-10) 📱 배지(.cn-src)는 출발지가 모바일/GPS 두 칸으로 갈리면서 폐기 — 칸 이름이 채널을 말한다. */

/* 상태 팔레트 ── common.jsx StepNode palette(L151~156) 1:1.
   dot = 채움색+흰글자(done/crit/warn/unv) 또는 빈원+중립(off). cn-time 색=상태 ink. */
.cn-off  { color:var(--w-t4) }
.cn-off  .cn-dot{ background:var(--w-surface);border-color:var(--w-border);color:var(--w-t4) }
.cn-done { color:var(--w-green-ink) }
.cn-done .cn-dot{ background:var(--w-green);border-color:var(--w-green);color:#fff }
/* cp 펄스 주기 완화 (2026-05-14 리소스 절감) — 1.6/1.1s → 2.4/1.8s.
   30 행 × ~3 critical 노드 = 동시 펄스 90개. 주기 ↑로 frame당 active 펄스 수 감소.
   ★ 펄스는 opacity-only(issues-perf), dot 자체에만 적용 — paint 비용 최소. */
.cn-warn { color:var(--w-orange-ink) }
.cn-warn .cn-dot{ background:var(--w-orange);border-color:var(--w-orange);color:#fff;animation:cp-pulse 2.4s ease-in-out infinite }
.cn-crit { color:var(--w-red-ink) }
.cn-crit .cn-dot{ background:var(--w-red);border-color:var(--w-red);color:#fff;animation:cp-pulse 1.8s ease-in-out infinite;box-shadow:0 0 0 3px var(--w-red-soft) }
/* unv = 기록은 있으나 반경 미검증(좌표없음/반경밖). 앰버(yellow) — 일반 미인증(off ○ 중립)·
   완료(done 그린)와 hue 명확 분리. 펄스 없음(만성 상태일 수 있어 노이즈/CPU 최소,
   2026-05-14 리소스 절감 정책 준수). issues-auth-gps 불변식: 앰버 ≠ done ≠ off. */
.cn-unv  { color:var(--w-yellow-ink) }
.cn-unv  .cn-dot{ background:var(--w-yellow);border-color:var(--w-yellow);color:#fff }
/* 관리자 수동 차고지 체크 — 신호 자체를 진한 파랑(blue-700 #1D4ED8)으로 채움(사용자 요청 2026-06-18).
   done 그린이 아니라 dot/라벨/ink 모두 진한 파랑 = "관리자가 직접 확인한 차고지" 한눈 식별.
   tick은 state="done"이라 ✓ 유지. 차고지·기상 공용(cn-admin 재사용). 애니메이션 없음(issues-perf 준수). */
.cn-admin       { color:#1D4ED8 }
.cn-admin .cn-dot{ background:#1D4ED8;border-color:#1D4ED8;color:#fff;box-shadow:0 0 0 2px rgba(29,78,216,.20) }
/* 🅿 주차 잔류 칩 (2026-08-12) — 차량이 밤새 차고지 반경 안에 서 있던 경우. 우리 검출은 "당일 반경 안
   첫 좌표"라 **인정 하한(차고지시각 −10분)에 그대로 붙는다**. busin 은 시동이 꺼져 있어도 마지막 좌표를
   새 시각으로 계속 보고하므로(functions/index.js 주석 참조) 그 시각은 **도착 시각이 아니라 하한 시각**이다.
   ★ 표시 전용 — 판정(computeAuthState)·집계·위험도·알람은 한 줄도 안 바뀐다. 지연 칩(.cn-dly)과 같은
   슬롯·같은 규격이라 이미 측정된 6신호 레이아웃에 새 자리를 요구하지 않는다. */
.cn-park{font-size:8px;font-weight:900;flex-shrink:0;padding:0 2px;border-radius:var(--w-radius-4);background:var(--w-yellow-soft);color:var(--w-yellow-ink)}
/* 지연 분 강조 — done이면 앰버, warn/crit이면 동일 톤 (구 cp-dly 매핑 계승) */
.cn-done .cn-dly{background:var(--w-yellow-soft);color:var(--w-yellow-ink)}
.cn-warn .cn-dly,.cn-crit .cn-dly{background:rgba(0,0,0,.08)}
@keyframes cp-pulse{0%,100%{opacity:1}50%{opacity:.55}}

/* ── 우측 컴팩트 플래그 — 고정 폭 92px(컴팩트 80px) → 메시지 길이 무관 동일 정렬.
   "✅완료(지연)" 최대 컨텐츠 수용. flag 비어있는 pending 상태도 빈 칸이 동일 폭 차지하도록 .sp-flag.flag-empty 처리. */
/* Phase 1f (2026-05-19): 우측 플래그 = 목업 common.jsx StatusChip — soft/line/ink +
   pill 라운드(999px). ★ width:96px(컴팩트 84)/height:22px/padding 0 6px/flex-shrink:0/
   margin-left:auto 폭 산식 전부 불변(issues-grid `sp`+`flag`). `flag`+`crit` 정적 글로우 유지(issues-perf). */
.sp-flag{
  font-size:10px;font-weight:800;letter-spacing:.2px;
  padding:0 6px;height:22px;width:96px;flex-shrink:0;margin-left:auto;
  display:inline-flex;align-items:center;justify-content:center;gap:3px;
  border-radius:var(--w-radius-pill);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.sp-flag.flag-empty{visibility:hidden}   /* 위치 고정용 invisible spacer */
.flag-done { background:var(--w-green-soft);color:var(--w-green-ink);border:1.5px solid var(--w-green-line) }
.flag-warn { background:var(--w-orange-soft);color:var(--w-orange-ink);border:1.5px solid var(--w-orange-line) }
.flag-crit { background:var(--w-red-soft);color:var(--w-red-ink);border:1.5px solid var(--w-red-line);box-shadow:0 0 6px rgba(240,62,62,.2) }
.flag-watch{ background:var(--w-yellow-soft);color:var(--w-yellow-ink);border:1.5px solid var(--w-yellow-line) }
.flag-move { background:var(--w-blue-soft);color:var(--w-blue-ink);border:1.5px solid var(--w-blue-line) }
.flag-pend { color:#94A3B8;font-weight:600;font-style:italic }

/* Phase 1e: 패널 박스 위험도 톤 — 그라데이션 베이스를 Wanted hue + surfaceAlt 끝점으로
   정합(박스 시각만, 내부 pill 미접촉). ★ ::before 펄스 OFF·정적 글로우 정책 전부 보존. */
.status-panel.sp-warn { background:linear-gradient(90deg,rgba(247,103,7,.08),var(--w-surface-alt) 50%);border-color:var(--w-orange-line) }
/* ★ critical 글로우 — 기존 box-shadow 애니메이션은 paint cost 큼.
   ::before 오버레이의 opacity만 변경 → GPU 합성으로 paint cost 0.
   다수 critical 행 동시 펄스 시 PC CPU 점유 ↓ (다른 탭 작업과 병행해도 쾌적) */
.status-panel.sp-crit {
  background:linear-gradient(90deg,rgba(240,62,62,.10),var(--w-surface-alt) 50%);
  border-color:var(--w-red-line);
  box-shadow:0 0 10px rgba(240,62,62,.18);          /* 정적 글로우 */
  position:relative;
}
/* ::before 펄스 OFF (2026-05-14 리소스 절감) — 본체 box-shadow 정적 글로우(L364) 유지. */
.status-panel.sp-crit::before{
  content:"";position:absolute;inset:-1px;pointer-events:none;
  border-radius:inherit;
  box-shadow:0 0 16px rgba(240,62,62,.28);
  opacity:1;
}
.status-panel.sp-done { background:linear-gradient(90deg,rgba(12,166,120,.07),var(--w-surface-alt) 50%);border-color:var(--w-green-line) }
.status-panel.sp-move { background:linear-gradient(90deg,rgba(0,102,255,.06),var(--w-surface-alt) 50%);border-color:var(--w-blue-line) }
.status-panel.sp-watch{ background:linear-gradient(90deg,rgba(240,140,0,.07),var(--w-surface-alt) 50%);border-color:var(--w-yellow-line) }

/* ── 컴팩트 모드 (대규모 관제 — 행 더 빽빽하게) ── */
/* 펀치업: 컴팩트도 호흡 ↑ — height:28/gap:4 불변, padding 2→3·7→9만(28-6=22 ≥ 노드 21px). */
.ctrl-body.dn-c .status-panel{height:28px;padding:3px 9px;gap:4px}
/* 컴팩트 노드 = dot12 + gap1 + cn-time8 ≈ 21 ≤ 22(28-6패딩). grid 5등분이라 폭 자동(min-width 불요). */
.ctrl-body.dn-c .cn-dot{width:12px;height:12px;font-size:7.5px}
.ctrl-body.dn-c .cn-time{font-size:8px}
.ctrl-body.dn-c .cn-dly{font-size:7px}
.ctrl-body.dn-c .cp-row::before{top:9px}
.ctrl-body.dn-c .sp-flag{height:20px;width:84px;font-size:9px;padding:0 5px}

/* ── 지연 컬럼 새 스타일 ── */
.col-delay{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0 4px}
.delay-num{font-size:15px;font-weight:900;font-family:var(--mono);text-align:center;letter-spacing:-.5px}
.ctrl-body.dn-c .delay-num{font-size:13px}
.delay-plus{color:var(--cc)}.delay-minus{color:var(--cg)}.delay-zero{color:var(--t2)}.delay-near{color:var(--cy)}

/* ── 위험도 배지 ── */
.col-risk{display:flex;align-items:center;justify-content:center;padding:0 4px}
.rbadge{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border-radius:7px;font-size:10px;font-weight:800;white-space:nowrap;min-width:78px;justify-content:center;letter-spacing:.2px}
.ctrl-body.dn-c .rbadge{font-size:9px;padding:3px 6px;min-width:68px}
.rbadge-crit{background:#FEF2F2;color:#B91C1C;border:1.5px solid #FCA5A5;box-shadow:0 0 8px rgba(220,38,38,.15)}
.rbadge-warn{background:#FFF7ED;color:#C2410C;border:1.5px solid #FDBA74}
.rbadge-watch{background:#FFFBEB;color:#B45309;border:1.5px solid #FCD34D}
.rbadge-move{background:#EFF6FF;color:#1D4ED8;border:1.5px solid #93C5FD}
.rbadge-done{background:#ECFDF5;color:#047857;border:1.5px solid #6EE7B7}
.rbadge-pend{background:#F3F4F6;color:var(--t2);border:1px solid var(--bdr)}

/* ── 컬럼4: 경과+지연 ── */
.col-metrics{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:2px;padding:0 4px}
.metric-elapsed{font-size:13px;font-weight:900;font-family:var(--mono);text-align:center}
.metric-dep{font-size:9px;color:var(--t3);text-align:center;white-space:nowrap}
/* 색상 */
.mel-cc{color:var(--cc)}.mel-cw{color:var(--cw)}.mel-cy{color:var(--cy)}.mel-cb{color:var(--cb)}.mel-cg{color:var(--cg)}.mel-cp{color:var(--t3)}
.ctrl-body.dn-c .metric-elapsed{font-size:11px}
.ctrl-body.dn-c .metric-dep{font-size:9px;color:var(--t3)}
.metric-dep.delay-warn{color:var(--cw)!important;font-weight:700}
.metric-dep.delay-crit{color:var(--cc)!important;font-weight:700}
.metric-dep.delay-near{color:var(--cy)!important;font-weight:700}

/* ── 컬럼7: 액션 — 7버튼 완벽 균일 (정사각 28px, 아이콘 폰트도 통일 13px) ──
   색·테두리로만 시각적 구분(전화=초록, 푸시=파랑, 담=보라, SOS=빨강 등)
   Phase 1f (2026-05-19): 목업 common.jsx ActionBtn 톤 — 라운드 8 + 평면 단색(그라데이션
   제거) + Wanted 토큰. ★ width:28px/height:28px(컴팩트 24)/flex-shrink:0 폭 산식 불변
   (issues-grid 액션 7버튼=232/208). gap:4·urgent `phone`+`urgent` 1.2s 펄스·@keyframes 불변(issues-perf). */
.col-actions{display:flex;align-items:center;gap:4px;padding:0 6px;justify-content:flex-end}
/* ★ 과거 참고(읽기전용) 모드 — 쓰기성 인터랙션만 차단(액션 7버튼·차고지 pill 토글). 5단계 pill 표시는 그대로(그날 인증 상태 확인이 핵심). col-info 단일 기사 뷰는 순수 읽기 네비게이션이라 유지. */
.ctrl-body.ctrl-readonly .col-actions{pointer-events:none;opacity:.45}
.ctrl-body.ctrl-readonly .col-status{pointer-events:none}
.rb2{padding:0;border-radius:var(--w-radius-8);border:none;font-size:13px;font-weight:800;font-family:var(--font);cursor:pointer;transition:transform .08s,box-shadow .12s;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;box-sizing:border-box}
.rb2 i{font-size:13px;line-height:1;display:inline-flex;align-items:center;justify-content:center}
/* ── M2 (2026-05-19): 모바일 액션버튼 라벨화(fold.jsx MobActBtn) ──
   .rb2-lbl = 모바일 전용 라벨(전화/담당/채팅), .rb2-gly = mgr 데스크톱 글자(담).
   ★ 데스크톱(>768)은 라벨 숨김·글자 표시 → 데스크톱 룩·폭·grid·액션컬럼 산식
   완전 불변(issues-grid 232/208). 모바일 표시·버튼 형태는 @media(max-width:768px). */
.rb2-lbl{display:none}
.rb2-gly{display:inline}
.rb2:hover{transform:translateY(-1px)}
.rb2:active{transform:scale(.92)}
/* 전화 버튼 — 그린 평면 채움(목업 ActionBtn call) */
.rb2-call{background:var(--w-green);color:#fff;box-shadow:0 1px 3px rgba(12,166,120,.3)}
.rb2-call:hover{box-shadow:0 2px 6px rgba(12,166,120,.4)}
/* 푸시 버튼 — 브랜드 블루 평면 채움(라이브 시각 위계 보존, 그라데이션만 평면화) */
.rb2-push{background:var(--w-brand);color:#fff;border:none;box-shadow:0 1px 3px rgba(0,102,255,.3)}
.rb2-push:hover{box-shadow:0 2px 6px rgba(0,102,255,.4)}
.rb2-chat:hover,.rb2-excl:hover,.rb2-emerg:hover,.rb2-mgr:hover,.rb2-note:hover{box-shadow:var(--w-shadow-emphasize)}
.rb2-chat{background:var(--w-surface);color:var(--w-brand);border:1.5px solid var(--w-blue-line)}
.rb2-excl{background:var(--w-surface);color:var(--w-t3);border:1.5px solid var(--w-border)}
/* 메모 버튼 — 기본 회색, 메모 있으면 노란 채움(.rb2-note-has) — 한눈에 "이슈 기록됨" 식별.
   클릭 시 mo-rpt-note 모달 → reportNote 입력. 업무보고 페이지의 메모 컬럼과 동일 데이터. */
.rb2-note{background:var(--w-surface);color:var(--w-orange-ink);border:1.5px solid var(--w-orange-line);position:relative}
.rb2-note.rb2-note-has{background:var(--w-orange-soft);color:var(--w-orange-ink);border-color:var(--w-orange);box-shadow:0 0 0 2px rgba(247,103,7,.18)}
.rb2-note.rb2-note-has::after{content:"";position:absolute;top:-2px;right:-2px;width:7px;height:7px;background:var(--w-orange);border:1.5px solid var(--w-surface);border-radius:var(--w-radius-circle)}
/* dispatchNote(고정메모)는 2026-07 관제 행 아래 전체폭 블루 줄 `.dr-disp`로 이전 —
   구 노선 셀 인라인 칩(.rt-disp/.info-route-text.irt-has-note)은 제거(dead). 노선 셀은 단일 info-route-text 복귀. */
/* 담당자(매니저) 호출 — 평상시 보라톤, 위험도 critical/warning이면 진한 보라로 강조.
   담당자 미지정이면 흐리게(.rb2-mgr-empty). 보라는 목업 ActionBtn mgr과 동일 hue 유지. */
.rb2-mgr{background:var(--w-surface);color:#6D28D9;border:1.5px solid #C4B5FD;font-weight:800;letter-spacing:.5px}
.rb2-mgr.rb2-mgr-empty{color:var(--w-t3);border-color:var(--w-border)}
.rb2-mgr.rb2-mgr-urgent{background:#7C3AED;color:#fff;border-color:#7C3AED;box-shadow:0 0 8px 2px rgba(124,58,237,.45)}
/* 비상연락망 버튼 — 평상시 옅은 회색, critical 시 빨강 채움으로 시인성 ↑.
   비상연락망이 비어있으면 .rb2-emerg-empty 로 더 흐리게 + 클릭 시 안내. */
.rb2-emerg{background:var(--w-surface);color:var(--w-red-ink);border:1.5px solid var(--w-red-line)}
.rb2-emerg.rb2-emerg-empty{color:var(--w-t3);border-color:var(--w-border)}
.rb2-emerg.rb2-emerg-urgent{background:var(--cc);color:#fff;border-color:var(--cc);box-shadow:0 0 8px 2px rgba(239,68,68,.45)}
.rb2-call a{color:inherit;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%}
.ctrl-body.dn-c .rb2{width:24px;height:24px;font-size:11px;border-radius:var(--w-radius-6)}
.ctrl-body.dn-c .rb2 i{font-size:11px}
/* ── 2차경고: 전화버튼 빨간 깜빡임 ── 주기 완화 (2026-05-14 리소스 절감): 0.65s → 1.2s.
   행 다수 critical 시 phone-urgent 동시 펄스 누적 부담 ↓. */
.rb2-urgent{
  background:var(--cc)!important;
  border:none!important;
  box-shadow:0 0 14px 4px rgba(239,68,68,.8),0 0 28px 8px rgba(239,68,68,.4)!important;
  animation:phone-urgent 1.2s ease-in-out infinite!important;
}
.rb2-urgent a{color:#fff!important}
/* ⚡ 2026-07-20 상시 렉 실측 fix: keyframes에서 box-shadow 애니메이션 제거 — box-shadow는 합성 불가
   속성이라 프레임마다 메인스레드 스타일 틱+페인트를 유발(200행·크리티컬 20행 유휴 트레이스 실측:
   메인 7,934→983ms/8s·4x 스로틀 = 유휴 소비 -88%, ABAB 재현 확인). 깜빡임 자체는 유지(사용자 결정
   "빨강 위험 애니메이션 유지") — .rb2-urgent 정적 box-shadow(글로우)가 요소 opacity와 함께 페이드되어
   시각 결과 사실상 동일. ★ box-shadow를 keyframes에 재도입 금지(같은 상시 렉 재발). */
@keyframes phone-urgent{
  0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.45;transform:scale(.92)}
}

/* ── 리스트 본체 ── */
/* scrollbar-gutter:stable — body 스크롤바를 항상 예약(보이든 안 보이든 동일 폭) → 좁아짐으로 인한
   마지막 액션 버튼 잘림 방지. 헤더(.ctrl-th)는 padding-right로 동일 인셋 적용해 컬럼 정렬 유지. */
/* ★ 2026-05-26 Fold 7 lazy paint fix: overscroll-behavior:contain — 스크롤 chain 차단 + paint 안정.
   -webkit-overflow-scrolling:touch — iOS Safari 풍 momentum scroll(Android Chrome도 일부 안정 효과).
   transform/contain:paint 추가 안 함 — issues-perf 금지 가드 보존(스크롤 컨테이너 GPU 강제 분리는
   layer count 폭증 위험. lazy paint는 Chrome 자체 동작이라 paint 부담 감축 + scroll 안정으로 1차 대응). */
.ctrl-body{flex:1;overflow-y:auto;min-height:0;background:#fff;scrollbar-gutter:stable;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
.ctrl-th{padding-right:15px} /* .ctrl-body의 scrollbar-gutter와 동일 폭 보정 — 컬럼 정렬 */

/* ── 그룹 헤더 (sticky 시간 슬롯) ──
   ★ contain + GPU 합성: sticky가 매 frame 재계산되는 줌/스크롤 상황에서 다른 요소 페인트 영향 차단 */
/* Phase 1e (2026-05-19): 목업 common.jsx GroupBanner 컨테이너 톤(soft 배경 + 3px 좌측 컬러바 +
   하단 hairline, 그라데이션→Wanted soft 단색). ★ sticky/z-index/contain:layout style paint/
   will-change 전부 불변(.grp-hdr는 issues-perf paint 금지 대상이 아님 — .dr만 해당).
   클래스명·JS 분기(hdrCls)·카운트·인라인 마크업 텍스트 스펙 0 변경, CSS 속성값만. */
.grp-hdr{
  position:sticky;top:0;z-index:5;
  display:flex;align-items:center;gap:var(--w-space-8);
  /* ★ 2026-07-27 (사용자 요청 — 관제 현황이 화면의 주인공이 되게): 세로 35px → 20px.
     padding 8px→2px · ⏰ 아이콘과 "알람 그룹" 글자 제거 · 배지 높이 축소.
     대신 좌측 컬러바 3px→5px + 배경 한 단계 진하게로 시간대 구분은 더 도드라지게(아래 gh-* 참조).
     ★ 후속(사용자 결정): 접기 폐지 — 모든 행 상시 표시. 헤더는 순수 구분선 역할만 한다. */
  /* ★ 2026-07-27 3차(사용자 요청 "더 얇게 + 색"): 20px → 16px. 세로 패딩 0 + line-height 14px 로
     배지 높이(12+테두리2)가 곧 바 높이가 되게 맞췄다. 색은 아래 gh-* 에서 50단계 → 200단계로 올림. */
  padding:0 var(--w-space-24);font-size:11px;font-weight:700;
  letter-spacing:.3px;line-height:14px;
  border-top:1px solid;border-bottom:1px solid;
  border-left:6px solid transparent;
  contain:layout style;
  /* ★ 2026-05-26 (사용자 Fold 7 paint 폭주 진단 후 fix): contain:paint + will-change:transform 제거.
     issues-perf "contain:paint 0=0" 보고가 .dr 한정이었고 .grp-hdr는 누락. sticky 헤더 다수
     × paint culling × GPU layer 승격 → Chrome on Fold가 sticky 헤더 자리잡힌 후 아래 행 일부만
     paint(거대 빈 공백). 스크롤/줌 = layer 재구축 = 일시 정상 → viewport 변경 다시 폐기.
     안티-회귀: .grp-hdr에 contain:paint·will-change:transform 재도입 금지. (issues-perf.md 참조) */
}
/* ★ 2026-07-27 3차 색 강화(사용자 요청 "색을 넣어주면 좋겠다") — 2차의 50단계 톤은 거의 흰색이라
   시간대 경계가 여전히 안 읽혔다. **배경 200단계 + 좌측 컬러바 6px** 로 올려 얇아진 바가 색으로 구분을 맡는다.
   행 배경은 전부 아주 옅은 톤(#FFFAF2·#F4F8FF 등)이라 200단계 헤더가 확실히 튄다. */
.gh-norm{background:#E2E8F0;border-top-color:transparent;border-bottom-color:#CBD5E1;border-left-color:#64748B;color:#334155}
.gh-curr{background:#BFDBFE;border-top-color:transparent;border-bottom-color:#60A5FA;border-left-color:var(--w-brand);color:#1E40AF}
.gh-past{background:#EEF2F6;border-top-color:transparent;border-bottom-color:#DDE3EA;border-left-color:#94A3B8;color:#64748B}
.gh-urg{background:#FECACA;border-top-color:transparent;border-bottom-color:#F87171;border-left-color:var(--w-red);color:#991B1B}
.gh-warn1{background:#FED7AA;border-top-color:transparent;border-bottom-color:#FB923C;border-left-color:var(--w-orange);color:#9A3412}
/* 시각 = 헤더의 주인공. 얇아진 대신 굵게(높이는 line-height 로 고정해 바 두께를 안 늘린다). */
.gh-time{font-family:var(--mono);font-size:11.5px;font-weight:900;letter-spacing:0;line-height:14px}
/* (2026-07-27 후속) 접기 폐지 — 모든 행 상시 표시. 화살표·클릭 토글 관련 규칙 제거. */
/* Phase 1e: 우측 배지 — 목업 StatusChip(soft 배경 + line 테두리 + pill radius) 톤.
   pip-blink는 issues-perf 리소스절감 정책상 데스크톱 유지(모바일 OFF는 @media에 별도) —
   목업이 애니메이션 제거를 강제하지 않으므로 현행 유지(시각 토큰만 Wanted화). */
.gh-badges{display:flex;gap:var(--w-space-6);align-items:center;margin-left:auto}
.gh-pill{padding:0 6px;border-radius:var(--w-radius-pill);font-size:8.5px;font-weight:800;border:1px solid transparent;line-height:12px}
.gh-tag-warn1{background:#FFEDD5;color:#C2410C;border-color:var(--w-orange-line)}
.gh-tag-curr{background:var(--w-brand-subtle);color:var(--w-blue-ink);border-color:var(--w-brand-hairline)}
.gh-tag-urg{background:var(--w-red-soft);color:var(--w-red-ink);border-color:var(--w-red-line)}
.gh-tag-cnt{background:var(--w-surface);color:var(--w-t3);border-color:var(--w-border)}
.gh-tag-done{background:var(--w-green-soft);color:var(--w-green-ink);border-color:var(--w-green-line)}

/* ── 구분선 ── */
.time-div{display:flex;align-items:center;gap:10px;padding:5px 16px;font-size:9px;font-weight:800;color:var(--cb);letter-spacing:.5px;flex-shrink:0;background:#EFF6FF}
.time-div::before,.time-div::after{content:"";flex:1;height:1px;background:#BFDBFE}

/* 빈 상태 */
.ctrl-empty{flex:1;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:9px;color:var(--t3);font-size:12px}

/* ── 미진척 핀 트레이 (상단 고정) ── */
/* Phase 1d (2026-05-19): 목업 common.jsx PinTray 톤 — CSS만(칩은 JS 생성, 마크업·핸들러 불변). */
.pin-tray{flex-shrink:0;display:flex;align-items:center;gap:var(--w-space-8);padding:10px var(--w-space-24);background:linear-gradient(180deg,#FFF6F6,#FFEAEA);border-bottom:1px solid var(--w-red-line);overflow-x:auto;overflow-y:hidden;scrollbar-width:thin}
.pin-tray.empty{display:none}
/* 단일 기사 뷰 배너 — pin-tray 바로 아래에 위치, 활성 시만 노출 */
/* single-view-bar = 라이브 고유(목업 무대응) — 정보 배너라 brand 블루 의미 유지, Wanted 토큰 정합만. */
.single-view-bar{flex-shrink:0;display:flex;align-items:center;gap:10px;padding:7px 12px;background:var(--w-brand-subtle);border-bottom:1px solid var(--w-brand-hairline);color:var(--w-blue-ink);font-size:12px;font-weight:700}
.single-view-bar .sv-ic{width:22px;height:22px;background:var(--cb);color:#fff;border-radius:var(--w-radius-circle);display:inline-flex;align-items:center;justify-content:center;font-size:12px;flex-shrink:0}
.single-view-bar .sv-txt b{font-size:13px;font-weight:900;color:var(--w-blue-ink)}
.single-view-bar .sv-hint{margin-left:auto;font-size:10px;font-weight:500;color:var(--w-brand);opacity:.85}
.single-view-bar .sv-clr{margin-left:8px;background:var(--w-surface);border:1.5px solid var(--cb);color:var(--cb);padding:4px 10px;border-radius:var(--w-radius-6);font-size:11px;font-weight:700;cursor:pointer;display:inline-flex;align-items:center;gap:4px;font-family:var(--font);transition:all .14s}
.single-view-bar .sv-clr:hover{background:var(--cb);color:#fff}
/* ── 숨김 인원 안내 바 (2026-07-22) ──
   관제 목록에서 "내 담당(담당자 뷰)"으로 걸러졌거나 "운휴 거래처"라서 화면에 안 나오는 인원이
   몇 명인지 알려준다. 콜센터가 화면에 없는 인원을 "데이터 누락"으로 오인해 신고하던 건의 대응.
   숨김 0명이면 display:none 이라 평소 화면은 그대로. 필터 바 아래·핀트레이 위(비-sticky)라
   모바일 sticky 질서(grp-hdr 5 < pin-tray 6 < mhdr 14)와 --pin-h 실측에 영향 없음. */
.hid-bar{flex-shrink:0;display:flex;align-items:center;gap:var(--w-space-8);padding:6px var(--w-space-24);background:var(--w-surface-alt);border-bottom:1px solid var(--w-hairline);color:var(--w-t2);font-size:11.5px;font-weight:700;flex-wrap:wrap}
.hid-bar .hid-ic{flex-shrink:0;font-size:12px}
.hid-bar .hid-txt{color:var(--w-t1);font-weight:800}
.hid-bar .hid-btn{background:var(--w-surface);border:1.5px solid var(--w-brand);color:var(--w-brand);padding:3px 10px;border-radius:var(--w-radius-6);font-size:11px;font-weight:700;cursor:pointer;font-family:var(--font);transition:all .14s;flex-shrink:0}
.hid-bar .hid-btn:hover{background:var(--w-brand);color:#fff}
.hid-bar .hid-pause{background:var(--w-surface);border:1px solid var(--w-orange-line);color:var(--w-orange-ink);padding:3px 10px;border-radius:var(--w-radius-pill);font-size:11px;font-weight:700;cursor:pointer;font-family:var(--font);transition:all .14s;flex-shrink:0}
.hid-bar .hid-pause:hover{background:var(--w-orange-soft)}

/* ★ 신호 안내 (2026-07-27) — 관제 담당자가 5단계 신호·기호·신호를 만든 주체를 화면에서 바로 확인.
   ctrl-bar 의 [❓ 신호 안내] 버튼으로 접기/펼치기(localStorage.buslink_sig_guide, 기본 접힘).
   ★ 비-sticky·flex-shrink:0 — 모바일 sticky 질서(grp-hdr 5 < pin-tray 6 < mhdr 14)와 --pin-h 실측에 영향 0.
   ★ 스와치는 관제 행이 실제로 쓰는 클래스(.cn/.cn-dot/.sp-flag)를 그대로 재사용 = 범례와 실제 렌더가 어긋날 수 없음.
      단 펄스(cp-pulse)는 여기서만 끈다(신규 무한 애니메이션 0 — issues-perf 준수). */
.btn-sig{padding:5px 11px;background:var(--w-surface);border:1.5px solid var(--w-border);border-radius:var(--w-radius-10);color:var(--w-t1);font-size:11px;font-weight:700;font-family:var(--font);cursor:pointer;display:flex;align-items:center;gap:5px;height:27px;transition:all .14s;flex-shrink:0}
.btn-sig:hover{border-color:var(--w-brand);color:var(--w-brand)}
.btn-sig.on{background:var(--w-brand);border-color:var(--w-brand);color:#fff}
.sig-guide{flex-shrink:0;padding:11px var(--w-space-24) 13px;background:var(--w-surface);border-bottom:1px solid var(--w-hairline)}
.sig-cols{display:flex;flex-wrap:wrap;gap:10px 28px}
.sig-col{min-width:240px;flex:1 1 240px}
.sig-hd{font-size:9.5px;font-weight:800;color:var(--w-t3);letter-spacing:.3px;margin-bottom:6px}
.sig-it{display:flex;align-items:flex-start;gap:7px;font-size:11px;color:var(--w-t2);line-height:1.5;padding:1.5px 0}
.sig-k{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;min-width:28px;height:18px;padding:0 5px;border-radius:var(--w-radius-6);background:var(--w-surface-alt);border:1px solid var(--w-border);font-size:10px;font-weight:900;color:var(--w-t1)}
/* 2026-08-10 6신호 — `차고지모바일`처럼 긴 이름은 2줄 키(기존 .sig-k 은 height 18px 고정이라 별도 modifier로만 늘린다) */
.sig-k.sig-k2{height:auto;min-height:18px;padding:2px 5px;line-height:1.15;font-size:9px;text-align:center}
.sig-nm{font-weight:800;color:var(--w-t1)}
.sig-guide .cn{display:inline-flex;align-items:center;flex-shrink:0;width:auto;height:auto;padding:0;background:none;border:none}
.sig-guide .cn-dot{animation:none}
.sig-guide .sp-flag{flex-shrink:0;margin-left:0;width:82px;height:19px;font-size:9.5px}   /* 관제 행의 margin-left:auto(우측 정렬)만 해제 — 범례에선 좌측 정렬 */
.sig-note{margin-top:9px;padding-top:8px;border-top:1px dashed var(--w-hairline);font-size:11px;color:var(--w-t2);line-height:1.6}
.sig-note b{color:var(--w-t1)}
/* 기사 행 클릭 가능 영역 — col-info hover 시 시각적 단서 */
.dr .col-info{cursor:pointer;border-radius:5px;transition:background-color .14s}
.dr .col-info:hover{background:rgba(37,99,235,.06)}
.dr.dr-sv{box-shadow:inset 3px 0 0 var(--cb)}
.dr.dr-sv .col-info{background:rgba(37,99,235,.08)}
.pin-label{flex-shrink:0;font-size:10px;font-weight:900;color:var(--w-red-ink);letter-spacing:.5px;padding:0 4px;display:flex;align-items:center;gap:4px}
.pin-chip{flex-shrink:0;display:inline-flex;align-items:center;gap:6px;padding:4px 11px;border-radius:var(--w-radius-pill);font-size:11.5px;font-weight:700;cursor:pointer;border:1px solid;transition:all .15s;font-family:var(--font);background:var(--w-surface);white-space:nowrap}
.pin-chip:hover{transform:translateY(-1px);box-shadow:var(--w-shadow-emphasize)}
.pin-chip .pc-t{font-family:var(--mono);font-size:10.5px;opacity:.7}
.pin-chip.pc-critical{border-color:var(--w-red-line);color:var(--w-red-ink);background:var(--w-surface)}
.pin-chip.pc-warning{border-color:var(--w-orange-line);color:var(--w-orange-ink);background:var(--w-surface)}
.pin-chip.pc-stale{border-color:var(--w-border);color:var(--w-t2);background:var(--w-surface-alt)}
.dr.dr-flash{animation:dr-flash 1.6s ease-out}
@keyframes dr-flash{0%{box-shadow:inset 0 0 0 2px var(--cb),0 0 18px rgba(37,99,235,.4);background:#DBEAFE}100%{box-shadow:none}}

/* ══════════════════════════════════════════════
   지도
══════════════════════════════════════════════ */
/* display/flex-direction은 .page.active 룰에서 처리 — 여기서 ID로 덮어쓰면
   비활성 페이지도 강제로 display:flex가 되어 관제 화면 하단에 같이 노출됨. */
#page-map{padding:12px 14px;gap:10px;overflow:hidden;background:var(--bg)}
.map-grid{display:grid;grid-template-columns:280px 1fr;gap:10px;flex:1;min-height:0}
/* ── 지도 캔버스 둘러싼 오버레이 chrome — Phase 2B (2026-05-19): 목업 admin-map.jsx Legend/Counter
   blur 카드 톤(CSS만). #kakao-map 캔버스·.dp-* 핀 마커·.map-pop = 지도 위 기능요소라 미접촉(issues-map).
   범례/마커 아이콘은 지도 위 마커(.map-grg/.map-sp)와 색 의미 일치 위해 hue 보존(차고지 회색/출발지 주황). ── */
.map-wrap{position:relative;border-radius:var(--w-radius-16);overflow:hidden;border:1px solid var(--w-border);box-shadow:var(--w-shadow-emphasize);min-height:0}
#kakao-map{width:100%;height:100%;min-height:320px;background:#F1F5F9}
/* ── 지도 범례 = 전체 색상표 (2026-07-15) ─────────────────────────────────────────
   관제 담당자가 "주황색 원이 무슨 뜻이냐"고 물어 색·기호를 전부 노출하도록 재작성.
   ★ 스와치는 반드시 지도에 실제로 쓰이는 **리터럴 hex** 그대로 쓴다 — Wanted 토큰(--cc #F03E3E /
   --cb #0066FF / --cg #0CA678)은 핀 색(#EF4444/#3B82F6/#10B981)과 미묘하게 달라 범례가 거짓말을 한다.
   지도를 가리지 않도록 헤더 클릭으로 접기/펼치기(localStorage.buslink_map_leg) — 접히면 헤더 한 줄만.
   본문은 스크롤(max-height 52vh) + 지도 높이 상한(calc(100% - 20px))이라 절대 지도를 넘지 않는다.
   전부 정적(애니메이션 0 — issues-perf 준수). ── */
.map-leg{position:absolute;bottom:10px;left:10px;z-index:5;width:210px;max-height:calc(100% - 20px);background:rgba(255,255,255,.95);backdrop-filter:blur(20px);border:1px solid var(--w-border);border-radius:var(--w-radius-12);display:flex;flex-direction:column;min-height:0;overflow:hidden;box-shadow:var(--w-shadow-strong)}
.leg-hd{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:7px 10px;font-size:11px;font-weight:800;color:var(--w-t1);cursor:pointer;user-select:none;flex-shrink:0}
.leg-hd:hover{background:var(--w-surface-alt)}
.leg-chev{font-size:9px;color:var(--w-t3)}
.map-leg.off .leg-bd{display:none}
.map-leg.off .leg-chev::before{content:"▸"}
.map-leg .leg-chev::before{content:"▾"}
.leg-bd{flex:1 1 auto;min-height:0;max-height:52vh;overflow-y:auto;padding:4px 10px 9px;display:flex;flex-direction:column;gap:3px;border-top:1px solid var(--w-hairline)}
.leg-gr{margin-top:7px;font-size:9px;font-weight:800;color:var(--w-t3);letter-spacing:.3px}
.leg-gr:first-child{margin-top:1px}
.leg-it{display:flex;align-items:center;gap:6px;font-size:10px;color:var(--w-t2);line-height:1.35}
.leg-grg-ic{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;background:#fff;border:1.5px solid #6B7280;border-radius:4px;font-size:10px;line-height:1}
.leg-sp-ic{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;background:#FFF7ED;border:1.5px solid #EA580C;border-radius:50%;font-size:9px;line-height:1;color:#9A3412;font-weight:900}
.leg-home-ic{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;background:#F5F3FF;border:1.5px solid #7C3AED;border-radius:50%;font-size:9px;line-height:1}
/* 기사 핀 점(.dp-dot 축소판) — 흰 테두리 + 옅은 외곽선까지 지도와 동일하게 */
.leg-dt{width:11px;height:11px;border-radius:50%;flex-shrink:0;border:2px solid #fff;box-shadow:0 0 0 1px rgba(15,23,42,.3)}
/* 핀 주변 옅은 링(.dp-dot::after) — 강조 효과일 뿐 의미 없음. 정적(애니메이션 없음) */
.leg-pr{position:relative;width:16px;height:16px;flex-shrink:0;display:inline-block}
.leg-pr::before{content:"";position:absolute;left:3px;top:3px;width:10px;height:10px;border-radius:50%;background:#3B82F6;border:2px solid #fff;box-shadow:0 0 0 1px rgba(15,23,42,.3)}
.leg-pr::after{content:"";position:absolute;inset:0;border-radius:50%;border:1.5px solid #3B82F6;opacity:.35}
/* 반경 원 — 모양이 드러나게 반투명 채움 + 테두리 */
.leg-cr{width:15px;height:15px;border-radius:50%;flex-shrink:0;border:2px solid}
/* 선 — 점선 바 / 궤적 리본 바 */
.leg-ln{width:18px;height:0;flex-shrink:0;border-top:3px dashed}
.leg-rib{width:18px;height:5px;flex-shrink:0;border-radius:3px;background:#0066FF;box-shadow:0 0 0 1.5px #93C5FD}
/* 라벨 테두리·배지(출/RT) */
.leg-bg{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;height:16px;padding:0 4px;background:#fff;border:1.5px solid;border-radius:5px;font-size:8px;font-weight:900;line-height:1;letter-spacing:.3px}
.map-cnt-b{position:absolute;top:10px;right:10px;z-index:5;background:rgba(255,255,255,.95);backdrop-filter:blur(20px);border:1px solid var(--w-border);border-radius:var(--w-radius-12);padding:7px 12px;font-size:10px;color:var(--w-t3);box-shadow:var(--w-shadow-strong)}
.map-cnt-b strong{color:var(--w-t1);font-size:18px;display:block;font-weight:900}
/* 지도 과거(읽기전용) 모드 — 실시간 전용 도구 토글 버튼 비활성 */
body.map-readonly #btn-gps-diag-pins,body.map-readonly #btn-gps-time-pins,body.map-readonly #btn-mv-pins,body.map-readonly #btn-mv-near,body.map-readonly #btn-rt-track,body.map-readonly #btn-home-pins{pointer-events:none;opacity:.45}
.map-fi{padding:7px 11px;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-10);color:var(--w-t1);font-size:12px;font-family:var(--font);outline:none;min-width:185px;transition:border-color .12s,box-shadow .12s}
.map-fi:focus{border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.12)}
.map-fi::placeholder{color:var(--w-t3)}
/* ── Driver Pin v2 — 정확한 위치 점 + 연결선 + 라벨 (오프셋되어 차고지/출발지 아이콘과 안 겹침) ── */
.dp-wrap{position:relative;font-family:var(--font);width:0;height:0;line-height:1}
.dp-dot{position:absolute;left:-8px;top:-8px;width:16px;height:16px;border-radius:50%;border:2.5px solid #fff;box-shadow:0 0 0 1.5px rgba(15,23,42,.3),0 3px 9px rgba(15,23,42,.4);cursor:pointer;z-index:2;transition:transform .15s}
.dp-dot::after{content:"";position:absolute;inset:-12px;border-radius:50%;border:1.5px solid currentColor;opacity:.18;animation:none;pointer-events:none}
/* 지도 활성 시 위험(critical)·선택(Lock-on) 핀만 맥동 — 200핀 전체 무한 맥동 GPU 부하 제거(2026-07-15).
   ★ 데스크톱 한정(min-width:769px): 모바일은 L1745 .dp-dot::after{animation:none}로 이미 OFF인데
   이 규칙 특이도(0,4,2)가 모바일 규칙(0,1,1)을 이겨 맥동을 되살리므로 데스크톱으로 게이트. */
@media(min-width:769px){
  body:not(.map-idle) .dp-wrap.dp-crit .dp-dot::after,
  body:not(.map-idle) .dp-wrap.lk .dp-dot::after{animation:dp-pulse 2.4s ease-in-out infinite}
}
/* 지도 탭 이탈 유휴화(map-idle) 시 핀 맥동 완전 정지(전 기기) */
body.map-idle .dp-dot::after{animation:none}
@keyframes dp-pulse{0%,100%{transform:scale(.85);opacity:.18}50%{transform:scale(1.25);opacity:0}}
.dp-dot:hover{transform:scale(1.2)}
.dp-leader{position:absolute;left:0;top:-58px;width:50px;height:58px;overflow:visible;pointer-events:none}
.dp-leader line{stroke-dasharray:3 2.5;opacity:.7;stroke-linecap:round}
.dp-label{position:absolute;left:40px;top:-78px;background:#fff;border:1.5px solid;border-radius:8px;padding:5px 9px;white-space:nowrap;cursor:pointer;font-size:10px;font-weight:700;color:#1F2937;box-shadow:0 3px 12px rgba(15,23,42,.28);z-index:3;transition:transform .15s,box-shadow .15s}
.dp-label:hover{transform:translateY(-1px);box-shadow:0 5px 16px rgba(15,23,42,.35)}
.dp-nm{font-weight:800;color:#1F2937;line-height:1.3;display:flex;align-items:center}
.dp-times{font-size:9px;font-weight:600;color:#6B7280;margin-top:2px;font-variant-numeric:tabular-nums;letter-spacing:.2px;line-height:1.3}
.dp-times b{color:#1F2937;font-weight:800}
.dp-sp-badge{display:inline-block;background:#EA580C;color:#fff;font-weight:800;font-size:9px;padding:1px 5px;border-radius:5px;margin-left:5px;line-height:1.3;letter-spacing:.4px}
.dp-label.dp-sp{box-shadow:0 3px 12px rgba(234,88,12,.4),0 0 0 1px rgba(234,88,12,.18)}
/* Lock-on 강조 */
.dp-wrap.lk .dp-dot{transform:scale(1.3);box-shadow:0 0 0 2.5px rgba(37,99,235,.45),0 4px 14px rgba(37,99,235,.5)}
.dp-wrap.lk .dp-label{box-shadow:0 6px 20px rgba(37,99,235,.4),0 0 0 3px rgba(37,99,235,.22);transform:translateY(-2px)}
.dp-wrap.lk .dp-leader line{opacity:1;stroke-width:2.2}
.map-grg{position:relative;width:22px;height:22px;display:flex;align-items:center;justify-content:center;background:#fff;border:2px solid #6B7280;border-radius:5px;font-size:13px;line-height:1;box-shadow:0 2px 5px rgba(15,23,42,.25);cursor:default}
.map-grg::after{content:"";position:absolute;bottom:-5px;left:50%;transform:translateX(-50%);width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid #6B7280}
.map-sp{position:relative;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:#FFF7ED;border:2px solid #EA580C;border-radius:50%;font-size:11px;line-height:1;box-shadow:0 2px 5px rgba(234,88,12,.35);color:#9A3412;font-weight:900;cursor:default}
/* ── 집(자택) 위치 핀 (관리자 전용 driverHomes) — 차고지/출발지와 다른 보라 톤으로 시각 분리. `.map-sp` 미러. ── */
.map-home{position:relative;width:22px;height:22px;display:flex;align-items:center;justify-content:center;background:#F5F3FF;border:2px solid #7C3AED;border-radius:50%;font-size:12px;line-height:1;box-shadow:0 2px 5px rgba(124,58,237,.35);cursor:default;font-family:var(--font),"Noto Sans KR",sans-serif}
.map-home .mgd-lbl{position:absolute;left:26px;top:0;font-size:9px;font-weight:700;color:#6D28D9;background:rgba(255,255,255,.92);padding:1px 4px;border-radius:4px;white-space:nowrap;line-height:1.3;border:1px solid #C4B5FD}
@keyframes pinRipple{0%{transform:scale(.4);opacity:.7}100%{transform:scale(2.2);opacity:0}}
.pin-ripple{position:absolute;left:50%;top:50%;margin-left:-7px;margin-top:-7px;width:14px;height:14px;border-radius:50%;background:#3B82F6;animation:pinRipple 1.4s ease-out forwards;pointer-events:none}
.map-pop{background:#fff;border:1px solid var(--bdr);border-radius:12px;padding:12px 14px;min-width:170px;box-shadow:0 12px 30px rgba(15,23,42,.18);font-family:var(--font)}
.mp-nm{font-size:13px;font-weight:700;color:var(--t1);margin-bottom:4px}
.mp-inf{font-size:10px;color:var(--t2);line-height:1.7;margin-bottom:9px}
.mp-bts{display:flex;gap:5px}
.mp-btn{flex:1;padding:6px;border-radius:7px;border:none;cursor:pointer;font-size:10px;font-weight:700;font-family:inherit;white-space:nowrap;text-align:center;text-decoration:none}
.mp-call{background:linear-gradient(135deg,#10B981,#059669);color:#fff}
.mp-chat{background:#EFF6FF;color:var(--cb);border:1px solid #93C5FD!important}
/* 담당자 전화 (2026-08-27) — 기사 전화(mp-call 초록)와 색으로 구분(담당자=보라, MGR_ROLES sales 톤). */
.mp-mgr{background:#F5F3FF;color:#5B21B6;border:1px solid #DDD6FE!important}

/* ── GPS 진단 핀 (2026-05-21, 검증 도구) — 신규 마커 채널. `.map-grg`/`.map-sp` 패턴 미러.
   토글 OFF 기본·ON 시만 표시. 차고지 미진입=주황 ⚠ 펄스(강조), 진입=옅은 녹색 ✓(확인용).
   Driver Pin v2(`.dp-*`)/차고지(`.map-grg`)/출발지(`.map-sp`)/정보팝업(`.map-pop`) 채널 미접촉.
   zIndex: driver pin 50 > 출발지 15 > GPS진단 핀 12 > 차고지 10(issues-map 갱신). */
.map-gps-diag{position:relative;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:#fff;border:2px solid var(--w-orange,#F76707);border-radius:50%;font-size:11px;line-height:1;font-weight:900;color:var(--w-orange,#F76707);box-shadow:0 2px 5px rgba(247,103,7,.35);cursor:pointer;font-family:var(--font),"Noto Sans KR",sans-serif}
.map-gps-diag.in{border-color:var(--w-green,#0CA678);color:var(--w-green,#0CA678);box-shadow:0 2px 5px rgba(12,166,120,.30)}
.map-gps-diag.out::after{content:"";position:absolute;left:50%;top:50%;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:50%;border:2px solid var(--w-orange,#F76707);animation:pinRipple 1.8s ease-out infinite;pointer-events:none}
.map-gps-diag:hover{box-shadow:0 4px 10px rgba(15,23,42,.30)}
.map-gps-diag .mgd-lbl{position:absolute;left:24px;top:-2px;font-size:9px;font-weight:700;color:var(--w-t1,#111827);background:rgba(255,255,255,.92);padding:1px 4px;border-radius:4px;white-space:nowrap;line-height:1.3;border:1px solid var(--w-hairline,#E5E7EB)}

/* ── 거리 기준 GPS 진단 핀 SP 측 (2026-05-21 추가, 사용자 버그 리포트 후속) — 차고지 측(`.map-gps-diag`, 원형)과
   같은 색 정책(주황 ⚠ 미진입 / 녹색 ✓ 진입)이지만 사각형으로 시각 분리(border-radius:5px). 시각 기준 SP 핀(`.map-gps-time-sp`, 원형 보라)과도 시각 분리.
   라벨 prefix 'S·'로 SP임을 명시. polyline → driver.startPoint 좌표.
   _gpsDiagOvs/_gpsDiagLines 배열을 차고지 핀과 공유 회수. zIndex 12(차고지 거리 기준과 동일 레벨, gps-time 13 아래). */
.map-gps-diag-sp{position:relative;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:#fff;border:2px solid var(--w-orange,#F76707);border-radius:5px;font-size:11px;line-height:1;font-weight:900;color:var(--w-orange,#F76707);box-shadow:0 2px 5px rgba(247,103,7,.35);cursor:pointer;font-family:var(--font),"Noto Sans KR",sans-serif}
.map-gps-diag-sp.in{border-color:var(--w-green,#0CA678);color:var(--w-green,#0CA678);box-shadow:0 2px 5px rgba(12,166,120,.30)}
.map-gps-diag-sp.out::after{content:"";position:absolute;left:50%;top:50%;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:5px;border:2px solid var(--w-orange,#F76707);animation:pinRipple 1.8s ease-out infinite;pointer-events:none}
.map-gps-diag-sp:hover{box-shadow:0 4px 10px rgba(15,23,42,.30)}
.map-gps-diag-sp .mgd-lbl{position:absolute;left:24px;top:-2px;font-size:9px;font-weight:700;color:var(--w-t1,#111827);background:rgba(255,255,255,.92);padding:1px 4px;border-radius:4px;white-space:nowrap;line-height:1.3;border:1px solid var(--w-hairline,#E5E7EB)}

/* ── 시각 기준 GPS 핀 채널 (2026-05-21, 검증 도구 2번째) — 거리 기준 핀(`.map-gps-diag`)과 별개·동시 ON 가능.
   각 차량의 차고지·출발지 등록 시각에 가장 근접한 시각의 좌표 표시(시각 차이 최소). 캘리브레이션 + 시각 정확도 검증.
   차고지=파랑 🕐(Wanted blue #2563EB), 출발지=보라 🕘 (#7C3AED). 두 색 분리로 채널 구별.
   zIndex: driver pin 50 > 출발지 15 > 시각 기준 핀 13 > 거리 기준 핀(gps-diag) 12 > 차고지 10. */
.map-gps-time-grg{position:relative;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:#fff;border:2px solid #2563EB;border-radius:50%;font-size:11px;line-height:1;font-weight:900;color:#2563EB;box-shadow:0 2px 5px rgba(37,99,235,.30);cursor:pointer;font-family:var(--font),"Noto Sans KR",sans-serif}
.map-gps-time-grg:hover{box-shadow:0 4px 10px rgba(15,23,42,.30)}
.map-gps-time-grg .mgt-lbl{position:absolute;left:24px;top:-2px;font-size:9px;font-weight:700;color:#1E40AF;background:rgba(255,255,255,.95);padding:1px 5px;border-radius:4px;white-space:nowrap;line-height:1.3;border:1px solid #DBEAFE}
.map-gps-time-sp{position:relative;width:20px;height:20px;display:flex;align-items:center;justify-content:center;background:#fff;border:2px solid #7C3AED;border-radius:50%;font-size:11px;line-height:1;font-weight:900;color:#7C3AED;box-shadow:0 2px 5px rgba(124,58,237,.30);cursor:pointer;font-family:var(--font),"Noto Sans KR",sans-serif}
.map-gps-time-sp:hover{box-shadow:0 4px 10px rgba(15,23,42,.30)}
.map-gps-time-sp .mgt-lbl{position:absolute;left:24px;top:-2px;font-size:9px;font-weight:700;color:#5B21B6;background:rgba(255,255,255,.95);padding:1px 5px;border-radius:4px;white-space:nowrap;line-height:1.3;border:1px solid #EDE9FE}

/* ── 모바일 수동 인증 핀 (2026-06-05) — 기사앱에서 손으로 누른 기상/차고지/출발지 도착 인증 좌표 표시.
   차량 GPS 진단/시각 핀(흰 배경·차량 GPS)과 달리 **colored tint 배경**으로 시각 분리(모바일 인증 채널).
   차고지·출발지는 등록 반경 대비 in(녹색 tint)/out(빨강 tint — false 수동 인증 의심), 기상은 인디고 중립(목표 좌표 없음).
   zIndex: driver pin 50 > 출발지(map-sp) 15 > 모바일 인증 14 > 시각 기준 13 > 거리 기준(gps-diag) 12 > 차고지 10. */
.map-mv{position:relative;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:6px;font-size:11px;line-height:1;cursor:pointer;background:#F3F4F6;border:2px solid #9CA3AF;box-shadow:0 2px 5px rgba(15,23,42,.25);font-family:var(--font),"Noto Sans KR",sans-serif}
.map-mv.mv-wake{background:#EEF2FF;border-color:#6366F1}
.map-mv.in{background:#E7F7F0;border-color:var(--w-green,#0CA678)}
.map-mv.out{background:#FEECEC;border-color:#EF4444}
/* 📱 폰 최근접(2026-08-11) — 인증 실패 쪽 채널이라 기본은 앰버(주의), 반경 안이면 .in 초록이 이긴다(specificity 3>2) */
.map-mv.mv-near{background:#FFF7ED;border-color:#F59E0B}
.map-mv.mv-near.in{background:#E7F7F0;border-color:var(--w-green,#0CA678)}
.map-mv:hover{box-shadow:0 4px 10px rgba(15,23,42,.30)}
.map-mv .mgd-lbl{position:absolute;left:24px;top:-2px;font-size:9px;font-weight:700;color:var(--w-t1,#111827);background:rgba(255,255,255,.92);padding:1px 4px;border-radius:4px;white-space:nowrap;line-height:1.3;border:1px solid var(--w-hairline,#E5E7EB)}
/* ── 출처 코너 배지 (2026-06-08): 📱=모바일 수동 / 🛰️=자동 차량 GPS(진단·시각 공통). 좌상단 코너(우측 라벨과 비충돌). 모든 핀 position:relative라 그대로 적용 ── */
.pin-src{position:absolute;left:-5px;top:-5px;font-size:9px;line-height:1;background:#fff;border-radius:50%;padding:1px;box-shadow:0 1px 2px rgba(15,23,42,.30);pointer-events:none;z-index:1}
/* 궤적 시각 라벨 — 이동 경로 위 HH:MM (시작=초록 ▶ / 끝=빨강 ■ / 중간=파랑) */
.trk-time-lbl{display:inline-block;background:rgba(255,255,255,.95);border:1px solid #0066FF;color:#0052CC;font:700 11px/1.3 ui-monospace,SFMono-Regular,Menlo,monospace;padding:1px 5px;border-radius:6px;box-shadow:0 1px 3px rgba(15,23,42,.28);white-space:nowrap;pointer-events:none}
.trk-time-lbl.tt-start{background:#0CA678;border-color:#0CA678;color:#fff}
.trk-time-lbl.tt-end{background:#EF4444;border-color:#EF4444;color:#fff}
/* 기상 인증 위치 마커 — 기상 버튼 누른 지점(인디고, 궤적 시각 라벨과 색 구분) */
.trk-wake-lbl{display:inline-block;background:#4F46E5;border:1px solid #4F46E5;color:#fff;font:700 11px/1.3 ui-monospace,SFMono-Regular,Menlo,monospace;padding:1px 6px;border-radius:6px;box-shadow:0 1px 3px rgba(15,23,42,.30);white-space:nowrap;pointer-events:none}

/* ── 추적 패널(좌측) — Phase 2A (2026-05-19): 목업 admin-map.jsx 좌측 패널 + MapListItem 톤.
   CSS 속성값만(트랙 폭 산식·gap·padding·line-height = 한글 잘림 방지 불변식 issues-map,
   추적패널 렌더 JS·마크업·핸들러 0 변경). 지도 캔버스·핀 마커는 별개(미접촉). ── */
.trk-pan{display:flex;flex-direction:column;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-16);overflow:hidden;box-shadow:var(--w-shadow-emphasize);min-height:0}
.trk-hd{padding:10px 12px;border-bottom:1px solid var(--w-hairline);display:flex;flex-direction:column;gap:8px;background:var(--w-surface-alt);flex-shrink:0}
.trk-hd-tl{display:flex;align-items:center;justify-content:space-between;font-size:12px;font-weight:800;color:var(--w-t1)}
.trk-hd-tl .trk-cnt{font-size:11px;font-weight:800;color:var(--w-brand);background:var(--w-brand-subtle);border:1px solid var(--w-brand-hairline);border-radius:var(--w-radius-pill);padding:2px 9px}
.trk-fil{display:flex;gap:5px;flex-wrap:wrap}
.trk-fil-chip{padding:5px 10px;border:1px solid var(--w-border);border-radius:var(--w-radius-8);background:var(--w-surface);color:var(--w-t2);font-size:11px;font-weight:700;cursor:pointer;font-family:var(--font);transition:all .14s}
.trk-fil-chip:hover{border-color:var(--w-brand-hairline);color:var(--w-brand)}
.trk-fil-chip.active{background:var(--w-brand);color:#fff;border-color:var(--w-brand)}
.trk-fil-chip.active.fil-crit{background:var(--w-red);border-color:var(--w-red)}
.trk-li{flex:1;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:6px}
/* ── 추적 카드 — 한글 기사명 잘림 방지: line-height 명시 + 충분한 행간/패딩 (불변) ── */
.trk-card{position:relative;display:grid;grid-template-columns:6px 1fr auto;gap:9px;align-items:center;padding:12px 10px 12px 8px;border:1px solid var(--w-border);border-radius:var(--w-radius-12);background:var(--w-surface);cursor:pointer;transition:all .14s}
.trk-card:hover{border-color:var(--w-brand-hairline);background:var(--w-surface-alt);transform:translateX(2px)}
.trk-card.lk{border-color:var(--w-brand);background:var(--w-brand-subtle);box-shadow:0 0 0 2px rgba(0,102,255,.18)}
/* 위치 정보 없는 기사 (운행 시작 전/GPS 미수신) — 톤다운, 클릭 가능 */
.trk-card.noloc{background:var(--w-surface-alt);opacity:.78}
.trk-card.noloc:hover{opacity:1;background:var(--w-surface-muted);transform:translateX(2px)}
.trk-card.noloc .trk-nm{color:var(--w-t2)}
.trk-card.noloc .trk-bar{background:var(--w-t5)!important}
.trk-card.noloc.lk{opacity:1;background:var(--w-brand-subtle)}
.trk-bar{align-self:stretch;border-radius:3px}
/* trk-mid: 행간 + 약간의 padding으로 한글 디센더가 padding 안에서 안전하게 그려지게 */
.trk-mid{min-width:0;display:flex;flex-direction:column;gap:4px;padding:2px 0;justify-content:center;overflow:hidden}
.trk-nm{font-size:13px;font-weight:800;color:var(--w-t1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.5;padding:2px 0}
.trk-meta{font-size:10px;color:var(--w-t3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.5}
/* 🤝 거래처 · 👤 담당자 (2026-08-27) — 지도 뷰에는 거래처 표기가 아예 없어서 담당자와 함께 신설.
   차고지·노선(trk-meta) 아래 한 줄. 담당자 이름 클릭 = 복사+전화(카드 클릭 Lock-on 과 분리). */
.trk-own{font-size:9.5px;color:var(--w-t3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.5}
.trk-own .to-mgr{color:#5B21B6;font-weight:800;cursor:pointer}
.trk-own .to-mgr:hover{text-decoration:underline}
.trk-times{display:flex;gap:6px;font-size:9.5px;color:var(--w-t2);font-variant-numeric:tabular-nums;line-height:1.4;padding-top:1px}
.trk-times .tt-lbl{color:var(--w-t3);font-weight:600;margin-right:2px}
.trk-times .tt-val{font-weight:800;color:var(--w-t1)}
.trk-times .tt-none{color:var(--w-t5);font-style:italic;font-weight:500}
.trk-times .tt-wk{font-weight:800;padding:0 1px;border-radius:3px}
.trk-times .tt-wk-ok{color:var(--w-green)}
.trk-times .tt-wk-warn{color:var(--w-orange)}
.trk-times .tt-wk-miss{color:var(--w-red)}
.trk-times .tt-stale{color:#fff;background:var(--w-red);font-weight:800;padding:1px 5px;border-radius:4px}
.trk-rt{text-align:right;flex-shrink:0;display:flex;flex-direction:column;align-items:flex-end;gap:2px}
.trk-dist{font-size:13px;font-weight:800;color:var(--w-t1);font-variant-numeric:tabular-nums;line-height:1.2}
.trk-dist small{font-size:9px;font-weight:700;color:var(--w-t3);margin-left:1px}
.trk-pct{font-size:9px;font-weight:700;color:var(--w-t2);font-variant-numeric:tabular-nums;line-height:1.2}
.trk-card.lk .trk-pct{color:var(--w-brand)}
.trk-gauge{grid-column:1/-1;height:3px;background:var(--w-surface-muted);border-radius:2px;overflow:hidden;margin-top:4px}
.trk-gauge-f{height:100%;background:linear-gradient(90deg,var(--w-brand),var(--w-green));transition:width .4s ease}
.trk-empty{padding:30px 18px;text-align:center;color:var(--w-t3);font-size:11px;line-height:1.7}
.trk-empty-ic{font-size:28px;color:var(--w-t5);margin-bottom:8px;display:block}
.trk-empty strong{display:block;font-size:13px;color:var(--w-t2);font-weight:700;margin-bottom:4px}

/* ── 모바일: 패널을 지도 위로 ── */
@media(max-width:768px){
  #page-map{overflow:auto}
  .map-grid{grid-template-columns:1fr;gap:8px}
  .trk-pan{max-height:230px}
  .map-wrap{min-height:340px}
}

/* ══════════════════════════════════════════════
   관리 (기사 + 차고지)
══════════════════════════════════════════════ */
#page-manage{overflow-y:auto;background:var(--bg)}
.mg-in{padding:16px;display:flex;flex-direction:column;gap:14px}
.mg-tabs{display:flex;background:#F3F4F6;border:1px solid var(--bdr);border-radius:10px;padding:3px;width:fit-content;gap:2px}
.mt{padding:7px 16px;border-radius:8px;font-size:12px;font-weight:600;color:var(--t2);cursor:pointer;transition:all .14s;border:none;background:transparent;font-family:var(--font)}
.mt.active{background:var(--cb);color:#fff;box-shadow:0 1px 3px rgba(37,99,235,.25)}
/* 차고지·출발지 관리 내부 탭 — .mg-tabs/.mt 스타일 미러하되 별도 셀렉터(기존 .mt onclick 핸들러 충돌 회피) */
.gt-tabs{display:flex;background:#F3F4F6;border:1px solid var(--bdr);border-radius:10px;padding:3px;width:fit-content;gap:2px;margin-bottom:14px}
.gt{padding:7px 16px;border-radius:8px;font-size:12px;font-weight:600;color:var(--t2);cursor:pointer;transition:all .14s;border:none;background:transparent;font-family:var(--font)}
.gt.active{background:var(--cb);color:#fff;box-shadow:0 1px 3px rgba(37,99,235,.25)}
/* 기사 마스터 내부 탭 — 신규 .rt 클래스(외부 .mt·차고지 .gt 핸들러와 충돌 회피). .gt 스타일 미러 */
.rt-tabs{display:flex;background:#F3F4F6;border:1px solid var(--bdr);border-radius:10px;padding:3px;width:fit-content;gap:2px;margin-bottom:14px}
.rt{padding:7px 16px;border-radius:8px;font-size:12px;font-weight:600;color:var(--t2);cursor:pointer;transition:all .14s;border:none;background:transparent;font-family:var(--font)}
.rt.active{background:var(--cb);color:#fff;box-shadow:0 1px 3px rgba(37,99,235,.25)}
/* ── 노선별 요일 기사 배정 (drivers.workDays 편집) ── */
.ra-card{border:1px solid var(--bdr);border-radius:10px;margin-bottom:8px;background:var(--w-surface,#fff);overflow:hidden}
.ra-card>summary{list-style:none;cursor:pointer;padding:10px 14px;font-weight:700;font-size:13px;color:var(--t1);
  background:var(--w-surface-muted,#F9FAFB);display:flex;align-items:center;gap:8px;user-select:none}
.ra-card>summary::-webkit-details-marker{display:none}
.ra-card>summary::before{content:"▸";color:var(--t3);font-size:11px;transition:transform .12s}
.ra-card[open]>summary::before{transform:rotate(90deg)}
.ra-cnt{font-size:11px;font-weight:600;color:var(--t2);background:var(--w-brand-subtle,#EFF4FF);
  color:var(--w-brand,#0066FF);padding:1px 8px;border-radius:var(--w-radius-pill,999px)}
.ra-body{padding:6px 10px 10px}
.ra-row{display:flex;align-items:center;gap:12px;padding:6px 4px;border-top:1px solid var(--w-hairline,#F1F2F4)}
.ra-row:first-child{border-top:none}
.ra-name{flex:0 0 130px;font-weight:700;font-size:12.5px;color:var(--t1);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ra-days{display:flex;gap:4px;flex-wrap:wrap}
.ra-day{position:relative;display:inline-flex;align-items:center;justify-content:center;min-width:30px;height:28px;
  padding:0 4px;font-size:11.5px;font-weight:700;color:var(--t2);background:var(--w-surface,#fff);
  border:1px solid var(--bdr);border-radius:7px;cursor:pointer;transition:all .12s;user-select:none}
.ra-day input{position:absolute;opacity:0;width:0;height:0;pointer-events:none}
.ra-day:has(input:checked){background:var(--w-brand-subtle,#EFF4FF);color:var(--w-brand,#0066FF);border-color:var(--w-brand,#0066FF)}
.ra-day.ra-sun{color:#DC2626}.ra-day.ra-sat{color:#2563EB}
.ra-day.ra-sun:has(input:checked){background:#FEE2E2;border-color:#DC2626;color:#B91C1C}
.ra-day.ra-sat:has(input:checked){background:#DBEAFE;border-color:#2563EB;color:#1D4ED8}
/* 출발지 테이블 한정 컬럼 폭 — 전역 table.dt(width:100% auto-layout)의 우측 빈 띠 제거.
   기사명·차고지·반경·수정은 고정폭, 출발지명이 가변폭으로 잉여 흡수. */
table.dt.dt-sp{table-layout:fixed}
table.dt.dt-sp col.dtc-name{width:130px}
table.dt.dt-sp col.dtc-grg{width:200px}
table.dt.dt-sp col.dtc-sp{width:auto}
table.dt.dt-sp col.dtc-rad{width:96px}
table.dt.dt-sp col.dtc-act{width:72px}
table.dt.dt-sp .dt-rad{font-family:var(--mono);font-weight:800;color:var(--t1)}
.tbl-tb{display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.tbl-t{font-size:14px;font-weight:800;color:var(--t1)}
/* 📐 GPS 반경 일괄 적용 카드 — 차고지 관리 탭 */
.radius-bulk{margin:10px 0 14px;padding:14px 16px;background:var(--w-surface-muted);border:1px solid var(--w-border);border-radius:var(--w-radius-10)}
.radius-bulk .rb-hd{font-size:13px;font-weight:800;color:var(--t1)}
.radius-bulk .rb-desc{font-size:11px;color:var(--t2);margin:3px 0 10px;line-height:1.6}
.radius-bulk .rb-row{display:flex;gap:18px;flex-wrap:wrap}
.radius-bulk .rb-fld{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.radius-bulk .rb-fld label{font-size:11px;font-weight:700;color:var(--t2)}
.radius-bulk .rb-fld .fi2{width:110px}
.tbl-fi,.tbl-sel{padding:7px 10px;background:#fff;border:1px solid var(--bdr);border-radius:8px;color:var(--t1);font-size:12px;font-family:var(--font);outline:none}
.tbl-fi:focus,.tbl-sel:focus{border-color:var(--bdrb);box-shadow:0 0 0 3px rgba(37,99,235,.1)}
.tbl-fi::placeholder{color:var(--t3)}
.dtw{overflow-x:auto;border-radius:10px;border:1px solid var(--bdr);background:#fff;box-shadow:0 1px 3px rgba(15,23,42,.04)}
table.dt{width:100%;border-collapse:collapse;font-size:12px}
table.dt th{background:#F9FAFB;color:var(--t2);font-weight:700;padding:10px 11px;text-align:left;border-bottom:1px solid var(--bdr);white-space:nowrap}
table.dt td{padding:9px 11px;border-bottom:1px solid #F3F4F6;color:var(--t1);vertical-align:middle}
table.dt tr:last-child td{border-bottom:none}
table.dt tr:hover td{background:#F9FAFB}
/* 🤝 기사 마스터 거래처 셀 하단 줄 — 운행요일 + 담당자 칩 (2026-08-27).
   🔴 관제 칩(.info-mgr)을 재사용하지 않는다: 그쪽은 모바일에서 display:none 이라 표에서도 사라진다. */
.dt-cli-sub{display:flex;align-items:center;gap:4px;flex-wrap:wrap;font-size:9px;margin-top:2px;line-height:1.2}
.dt-mgr{font-size:9px;color:#5B21B6;background:#F5F3FF;border:1px solid #DDD6FE;padding:1px 5px;border-radius:6px;font-weight:800;letter-spacing:.2px;line-height:1.3;white-space:nowrap;cursor:pointer}
.dt-mgr:hover{background:#EDE9FE;text-decoration:underline}
.dt-mgr[data-role="callcenter"]{color:#1D4ED8;background:#EFF6FF;border-color:#BFDBFE}
.dt-mgr[data-role="callcenter"]:hover{background:#DBEAFE}
.dt-mgr[data-role="affiliate"]{color:#0F766E;background:#F0FDFA;border-color:#99F6E4}
.dt-mgr[data-role="affiliate"]:hover{background:#CCFBF1}
.dt-mgr.dt-mgr-np{cursor:default}
.dt-mgr.dt-mgr-np:hover{text-decoration:none;background:#F5F3FF}
.sp{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;border-radius:9px;font-size:10px;font-weight:600}
.sp-on{background:#ECFDF5;color:#047857;border:1px solid #6EE7B7}
.sp-off{background:#F3F4F6;color:#6B7280;border:1px solid #D1D5DB}

/* ══ 📋 변경 이력(changeLogs) — 목록 행 + ack 팝업 공용 마크업 ══ */
.chg-cnt{font-size:11px;font-weight:800;color:var(--w-brand);background:var(--w-brand-subtle);border:1px solid var(--w-brand-hairline);border-radius:var(--w-radius-pill);padding:2px 9px}
#chg-list,#chg-ack-list{display:flex;flex-direction:column;gap:7px}
.chg-row{background:var(--w-surface);border:1px solid var(--w-border);border-left:3px solid var(--w-border-strong);border-radius:var(--w-radius-10);padding:9px 12px}
.chg-a-create{border-left-color:var(--w-green)}
.chg-a-update{border-left-color:var(--w-brand)}
.chg-a-delete{border-left-color:var(--w-red)}
.chg-a-bulk{border-left-color:var(--w-orange)}
/* 금일 제외(임시제외) 변경 — 제외=빨강 / 복귀=초록 (기존 .chg-a-* 패턴 미러, 2026-07-14) */
.chg-a-exclude{border-left-color:var(--w-red)}
.chg-a-restore{border-left-color:var(--w-green)}
.chg-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--w-t2)}
.chg-tm{font-family:var(--mono);font-weight:800;color:var(--w-t1);flex-shrink:0}
.chg-who{display:inline-flex;align-items:center;gap:4px;font-weight:700;color:var(--w-t1)}
.chg-role{font-size:9.5px;font-weight:800;padding:1px 6px;border-radius:var(--w-radius-pill);background:var(--w-surface-alt);color:var(--w-t2);border:1px solid var(--w-hairline)}
.chg-r-sales{background:var(--w-orange-soft);color:var(--w-orange-ink);border-color:var(--w-orange)}
.chg-r-callcenter{background:var(--w-blue-soft);color:var(--w-blue-ink);border-color:var(--w-brand)}
.chg-r-affiliate{background:var(--w-green-soft);color:var(--w-green-ink);border-color:var(--w-green)}
.chg-r-super{background:var(--w-surface-alt);color:var(--w-t2)}
.chg-tgt{font-weight:800;color:var(--w-t1)}
.chg-act{font-size:9.5px;font-weight:800;color:var(--w-t3);border:1px solid var(--w-hairline);border-radius:var(--w-radius-4);padding:1px 5px}
.chg-ack{margin-left:auto;font-size:10.5px;font-weight:800;color:var(--w-orange-ink)}
.chg-ack.ok{color:var(--w-green-ink)}
.chg-body{margin-top:6px;display:flex;flex-direction:column;gap:3px}
.chg-line{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;line-height:1.6}
.chg-line i{font-size:10px;color:var(--w-t3);flex-shrink:0}
.chg-fl{font-size:10.5px;font-weight:800;color:var(--w-t2);background:var(--w-surface-alt);border:1px solid var(--w-hairline);border-radius:var(--w-radius-4);padding:1px 6px;flex-shrink:0}
.chg-bf{color:var(--w-t3);text-decoration:line-through;word-break:break-word}
.chg-af{color:var(--w-t1);font-weight:700;word-break:break-word}
.chg-empty{text-align:center;color:var(--w-t3);font-size:12px;padding:26px 0}

/* ── 🚨 문제 기사 기록 (driverIssues, 2026-07-20) ──
   메모 모달 내부 기록 섹션 + 관리 탭 통계. 상세 목록은 관리 테이블 .dt 재사용, 여기는 부속 스타일만. */
.di-sec{margin-top:4px;padding:10px 12px;background:#FFF5F5;border:1px solid #FECACA;border-radius:var(--w-radius-10);display:flex;flex-direction:column;gap:7px}
.di-hd{font-size:12px;font-weight:800;color:var(--w-red);display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.di-recent{font-size:10.5px;font-weight:700;color:var(--w-t2)}
.di-recent b{color:#B41E1E}
.di-row{display:flex;gap:6px;align-items:center}
.di-row .fi2{flex:1;min-width:0}
.di-note{font-size:10px;color:var(--w-t3);line-height:1.55}
.di-agg-row{cursor:pointer}
.di-agg-row:hover{background:var(--w-surface-alt)}
.di-agg-row.sel{background:var(--w-brand-subtle)}
.di-cnt-b{display:inline-block;min-width:26px;text-align:center;font-weight:800;color:#B41E1E;background:#FFF5F5;border:1px solid #FECACA;border-radius:var(--w-radius-pill);padding:1px 8px}
.di-reason{color:var(--w-t1);word-break:break-word}
.di-reason.none{color:var(--w-t3);font-style:italic}
/* 🚨 사유 분류 (2026-08-27) — 기록 모달 셀렉트 + 통계 배지·요약 칩. 화면엔 코드가 아니라 라벨만 나온다. */
.di-row .di-cat{flex:0 0 130px;padding:10px 8px;font-size:12px}
.di-cat-b{display:inline-block;font-size:10px;font-weight:800;color:#B41E1E;background:#FEE2E2;border:1px solid #FCA5A5;border-radius:6px;padding:1px 6px;margin-right:5px;white-space:nowrap}
.di-cat-sum{display:flex;flex-wrap:wrap;gap:5px;padding:0 0 10px}
.di-cat-chip{font-size:11px;font-weight:700;color:var(--w-t2);background:var(--w-surface-alt);border:1px solid var(--w-border);border-radius:var(--w-radius-pill);padding:2px 9px}
.di-cat-chip b{color:#B41E1E;margin-left:3px}
/* 🔎 담당자 관리 「관할 범위」 클릭 → 담당 거래처 목록 (2026-08-27) */
.mgr-scope{cursor:pointer;border-bottom:1px dashed var(--w-border);padding-bottom:1px}
.mgr-scope:hover{color:var(--w-brand);border-bottom-color:var(--w-brand)}
.mgr-scope-list{margin-top:6px;display:flex;flex-wrap:wrap;gap:4px;max-height:168px;overflow:auto;max-width:420px}
.msl-cnt{width:100%;font-size:10px;font-weight:800;color:var(--w-t2)}
.msl-cnt b{color:var(--w-brand)}
.msl-i{font-size:10px;font-weight:700;color:#1D4ED8;background:#EFF6FF;border:1px solid #BFDBFE;border-radius:6px;padding:1px 6px;white-space:nowrap}
.msl-note{width:100%;font-size:10px;color:var(--w-t3);line-height:1.5}

/* ── 🚌 차량 매핑 수동 지정 (2026-07-24) ──
   기사 모달 내부 "차량 매핑" 섹션 + 차량 검색 모달(mo-veh) 결과 목록. 관제 그리드·액션바 미접촉. */
.vm-sec{margin-top:4px;padding:10px 12px;background:var(--w-surface-alt);border:1px solid var(--w-border);border-radius:var(--w-radius-10);display:flex;flex-direction:column;gap:8px}
.vm-hd{font-size:12px;font-weight:800;color:var(--w-t1);display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.vm-cur{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11.5px;color:var(--w-t2)}
.vm-plate{font-family:var(--mono);font-weight:800;font-size:13px;color:#0F766E;word-break:break-all}
.vm-plate.none{font-family:var(--font);font-size:11.5px;font-weight:700;color:var(--w-t3)}
.vm-cid{font-family:var(--mono);font-size:10px;color:var(--w-t3)}
.vm-lock{display:inline-flex;align-items:center;gap:4px;font-size:9.5px;font-weight:800;padding:1px 7px;border-radius:var(--w-radius-pill);background:var(--w-orange-soft);color:var(--w-orange-ink);border:1px solid var(--w-orange);white-space:nowrap}
.vm-by{font-size:10px;color:var(--w-t3);word-break:break-all}
.vm-btns{display:flex;gap:6px;flex-wrap:wrap}
.vm-note{font-size:10px;color:var(--w-t3);line-height:1.55}
.vs-bar{display:flex;gap:6px;align-items:center;margin-bottom:8px}
.vs-bar .fi2{flex:1;min-width:0}
.vs-stat{font-size:11px;color:var(--w-t2);background:var(--w-surface-alt);border:1px solid var(--w-border);border-radius:var(--w-radius-8);padding:7px 10px;margin-bottom:8px;line-height:1.55;word-break:break-word}
.vs-stat b{color:var(--w-t1)}
.vs-list{display:flex;flex-direction:column;gap:6px;max-height:52vh;overflow-y:auto}
.vs-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:9px 11px;border:1px solid var(--w-border);border-radius:var(--w-radius-10);background:var(--w-surface);cursor:pointer}
.vs-row:hover{background:var(--w-brand-subtle);border-color:var(--w-brand)}
.vs-pl{font-family:var(--mono);font-weight:800;font-size:14px;color:var(--w-t1);flex-shrink:0;word-break:break-all}
.vs-id{font-family:var(--mono);font-size:10px;color:var(--w-t3);flex-shrink:0}
.vs-meta{font-size:11px;color:var(--w-t2);word-break:break-word;min-width:0}
.vs-b{font-size:9.5px;font-weight:800;padding:1px 7px;border-radius:var(--w-radius-pill);border:1px solid var(--w-hairline);flex-shrink:0;white-space:nowrap}
.vs-b-ok{background:var(--w-green-soft);color:var(--w-green-ink);border-color:var(--w-green)}
.vs-b-no{background:var(--w-surface-alt);color:var(--w-t2)}
.vs-b-end{background:var(--w-orange-soft);color:var(--w-orange-ink);border-color:var(--w-orange)}
.vs-empty{text-align:center;color:var(--w-t3);font-size:12px;padding:26px 0;line-height:1.6}

/* ══════════════════════════════════════════════
   배정 패널 슬라이드
══════════════════════════════════════════════ */
/* 임시 제외 슬라이드 패널 — Phase 2D (2026-05-19): 모달 mc 셸과 동일 Wanted 톤 정합(CSS만,
   임시제외 슬라이드 패널 열림 토글·마크업·핸들러 0 변경). */
.ap-panel{position:fixed;top:0;right:0;bottom:0;z-index:50;width:min(560px,100vw);background:var(--w-surface);border-left:1px solid var(--w-border);display:flex;flex-direction:column;transform:translateX(100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:var(--w-shadow-heavy)}
.ap-panel.open{transform:translateX(0)}
.ap-hdr{padding:15px 16px;border-bottom:1px solid var(--w-hairline);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;background:var(--w-surface-alt)}
.ap-hdr h3{font-size:14px;font-weight:800;color:var(--w-t1)}
.ap-tb{padding:9px 16px;border-bottom:1px solid var(--w-hairline);display:flex;gap:5px;align-items:center;flex-wrap:wrap;flex-shrink:0;background:var(--w-surface)}
.ap-body{flex:1;overflow-y:auto;background:var(--w-surface)}
table.ap-t{width:100%;border-collapse:collapse;font-size:11px}
table.ap-t th{background:var(--w-surface-alt);color:var(--w-t2);font-weight:700;padding:8px 10px;text-align:left;border-bottom:1px solid var(--w-border);position:sticky;top:0;z-index:2}
table.ap-t td{padding:8px 10px;border-bottom:1px solid var(--w-hairline);color:var(--w-t1)}
table.ap-t tr.ap-lk td{opacity:.5}
.ap-ft{padding:12px 16px;border-top:1px solid var(--w-hairline);display:flex;gap:7px;justify-content:flex-end;flex-shrink:0;background:var(--w-surface-alt)}
.ach{accent-color:var(--w-brand);width:14px;height:14px;cursor:pointer}

/* ══════════════════════════════════════════════
   채팅
══════════════════════════════════════════════ */
/* 채팅 — Phase 2D (2026-05-19): Wanted 토큰 정합(CSS 속성값만, 마크업/onSnapshot/핸들러 0 변경).
   admin 버블·전송 버튼 그라데이션→brand 평면. unread=red 의미 보존(soft 톤). */
#page-chat{overflow:hidden;background:var(--bg)}
.chat-wrap{display:flex;height:100%}
.chat-lp{width:300px;flex-shrink:0;border-right:1px solid var(--w-border);display:flex;flex-direction:column;background:var(--w-surface)}
.chat-lh{padding:14px 16px;font-size:13px;font-weight:800;border-bottom:1px solid var(--w-hairline);flex-shrink:0;color:var(--w-t1);background:var(--w-surface-alt);display:flex;flex-direction:column;gap:8px}
.chat-lh-t{font-size:13px;font-weight:800}
.chat-search{width:100%;font-size:11.5px;padding:6px 8px;border:1px solid var(--w-border);border-radius:var(--w-radius-6);font-family:var(--font);color:var(--w-t1);background:var(--w-surface);outline:none;transition:border-color .12s,box-shadow .12s}
.chat-search:focus{border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.12)}
.chat-search::placeholder{color:var(--w-t3)}
.chat-digest-bar{display:flex;gap:6px;align-items:center}
.chat-digest-date{flex:1;min-width:0;font-size:11px;padding:5px 7px;border:1px solid var(--w-border);border-radius:var(--w-radius-6);font-family:var(--font);color:var(--w-t1);background:var(--w-surface)}
.chat-digest-btn{flex-shrink:0;font-size:11px;font-weight:700;padding:5px 9px;border:1px solid var(--w-brand-line);border-radius:var(--w-radius-6);background:var(--w-brand-subtle);color:var(--w-brand-strong);cursor:pointer;white-space:nowrap}
.chat-digest-btn:hover{background:var(--w-brand-hairline)}
/* 읽기전용 일자별 트랜스크립트 — 버블 대화와 달리 시간·역할·본문 한 줄 리스트 */
.chat-digest-body{flex:1;overflow-y:auto;padding:14px 16px;background:var(--w-surface-alt);display:flex;flex-direction:column;gap:3px}
.cdg-sum{font-size:11px;color:var(--w-t3);font-weight:700;padding:0 2px 6px}
.cdg-empty{text-align:center;color:var(--w-t3);font-size:12px;padding:28px 12px}
.cdg-drv{margin-top:14px;padding:6px 10px;background:var(--w-brand-subtle);border:1px solid var(--w-brand-hairline);border-radius:8px;font-size:12px;font-weight:800;color:var(--w-brand-strong);display:flex;justify-content:space-between;align-items:center;gap:8px}
.cdg-drv:first-of-type{margin-top:0}
.cdg-drv-ph{font-size:10px;font-weight:600;color:var(--w-t3);font-family:var(--mono);white-space:nowrap}
.cdg-msg{display:flex;gap:8px;padding:3px 10px;font-size:12px;line-height:1.5;align-items:baseline}
.cdg-msg-t{flex-shrink:0;font-size:10px;color:var(--w-t3);font-family:var(--mono);width:38px}
.cdg-msg-r{flex-shrink:0;font-size:10px;font-weight:800;width:28px}
.cdg-msg.r-driver .cdg-msg-r{color:var(--w-brand)}
.cdg-msg.r-admin .cdg-msg-r{color:var(--w-green-ink)}
.cdg-msg-x{flex:1;min-width:0;color:var(--w-t1);word-break:break-word;white-space:pre-wrap}
.chat-li{flex:1;overflow-y:auto}
.cli{padding:12px 16px;border-bottom:1px solid var(--w-hairline);cursor:pointer;transition:all .14s;position:relative}
.cli:hover{background:var(--w-surface-alt)}
.cli.active{background:var(--w-brand-subtle);border-left:3px solid var(--w-brand)}
.cli.unread{background:var(--w-red-soft);border-left:3px solid var(--w-red)}
.cli.unread:hover{background:var(--w-red-soft)}
.cli.unread.active{background:var(--w-brand-subtle);border-left:3px solid var(--w-brand)}
.cli-n{font-size:13px;font-weight:700;color:var(--w-t1);display:flex;align-items:center;gap:6px}
.cli.unread .cli-n{color:var(--w-red-ink)}
.cli-dot{width:8px;height:8px;border-radius:50%;background:var(--w-red);flex-shrink:0;box-shadow:0 0 0 3px rgba(240,62,62,.18)}
@keyframes cliDotPulse{0%,100%{box-shadow:0 0 0 3px rgba(240,62,62,.18)}50%{box-shadow:0 0 0 5px rgba(240,62,62,.05)}}
.cli-p{font-size:11px;color:var(--w-t3);margin-top:3px}
.cli-m{display:flex;justify-content:space-between;align-items:center;margin-top:4px}.cli-t{font-size:9px;color:var(--w-t3)}
.cli-nw{background:var(--cc);color:#fff;font-size:9px;font-weight:700;padding:2px 7px;border-radius:var(--w-radius-6)}
.cli-unr{background:var(--w-red);color:#fff;font-size:9px;font-weight:800;padding:2px 7px;border-radius:var(--w-radius-6)}
.chat-main{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--w-surface)}
.chat-mh{padding:14px 18px;border-bottom:1px solid var(--w-hairline);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;background:var(--w-surface-alt)}
.chat-mh h4{font-size:14px;font-weight:800;color:var(--w-t1)}
.chat-msgs{flex:1;overflow-y:auto;padding:16px 18px;display:flex;flex-direction:column;gap:8px;background:var(--w-surface-alt)}
.cm{max-width:65%;padding:10px 14px;border-radius:14px;font-size:12px;line-height:1.6}
.cm-d{align-self:flex-start;background:var(--w-surface);border:1px solid var(--w-border);color:var(--w-t1);border-bottom-left-radius:3px;box-shadow:var(--w-shadow-emphasize)}
.cm-a{align-self:flex-end;background:var(--w-brand);color:#fff;border-bottom-right-radius:3px}
.cm-t{font-size:9px;opacity:.55;margin-top:4px;display:block}
.cm-sep{align-self:center;font-size:10px;font-weight:700;color:var(--w-t3);background:var(--w-surface);border:1px solid var(--w-hairline);border-radius:999px;padding:3px 12px;margin:6px 0}
.chat-ib{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--w-hairline);flex-shrink:0;background:var(--w-surface)}
.chat-ta{flex:1;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-10);padding:10px 13px;color:var(--w-t1);font-size:13px;font-family:var(--font);outline:none;resize:none;transition:border-color .12s,box-shadow .12s}
.chat-ta:focus{border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.12)}
.btn-cs{width:36px;height:36px;border-radius:50%;background:var(--w-brand);border:none;color:#fff;font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .12s}
.btn-cs:hover{opacity:.92}
.chat-hint{flex:1;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:10px;color:var(--w-t3);font-size:13px;background:var(--w-surface-alt)}

/* ══════════════════════════════════════════════
   업무보고 — 노선별 그룹 카드 + 결재자 메일 발송
══════════════════════════════════════════════ */
#page-report{overflow-y:auto;background:var(--bg);padding:14px 16px;gap:10px}

/* ══════════════════════════════════════════════
   💡 개선요청 게시판 (improvementRequests, 2026-07-14)
   텍스트 전용(첨부 없음 — Firebase Storage 미활성). #page-report 레이아웃 미러.
   ══════════════════════════════════════════════ */
#page-board{overflow-y:auto;background:var(--bg);padding:14px 16px;gap:10px}
.imp-chips{display:flex;gap:5px;align-items:center;flex-wrap:wrap}
.imp-chip{padding:5px 10px;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-pill);color:var(--w-t2);font-size:11px;font-weight:700;font-family:var(--font);cursor:pointer;transition:all .12s}
.imp-chip:hover{background:var(--w-surface-alt);color:var(--w-t1)}
.imp-chip.on{background:var(--w-brand-subtle);border-color:var(--w-brand);color:var(--w-brand)}
.imp-chip b{font-weight:900;margin-left:2px}
.imp-mine{padding:6px 11px;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-8);color:var(--w-t2);font-size:11px;font-weight:700;font-family:var(--font);cursor:pointer;white-space:nowrap}
.imp-mine.on{background:var(--w-brand-subtle);border-color:var(--w-brand);color:var(--w-brand)}
#imp-list{display:flex;flex-direction:column;gap:7px}
.imp-row{background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-10);padding:11px 13px;cursor:pointer;transition:all .12s}
.imp-row:hover{border-color:var(--w-border-strong);box-shadow:var(--w-shadow-emphasize)}
.imp-row-h{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.imp-ttl{font-size:13px;font-weight:800;color:var(--w-t1);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto}
.imp-row-m{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:5px;font-size:11px;color:var(--w-t3)}
.imp-hist{font-weight:700;color:var(--w-t2)}
.imp-st{font-size:10px;font-weight:800;padding:2px 8px;border-radius:var(--w-radius-pill);flex-shrink:0;border:1px solid transparent}
.st-req{background:var(--w-surface-alt);color:var(--w-t2);border-color:var(--w-hairline)}
.st-rev{background:var(--w-blue-soft);color:var(--w-blue-ink);border-color:var(--w-brand)}
.st-prg{background:var(--w-orange-soft);color:var(--w-orange-ink);border-color:var(--w-orange)}
.st-done{background:var(--w-green-soft);color:var(--w-green-ink);border-color:var(--w-green)}
.st-rej{background:var(--w-red-soft);color:var(--w-red-ink);border-color:var(--w-red)}
.imp-priv{font-size:10px;font-weight:800;padding:2px 7px;border-radius:var(--w-radius-pill);background:var(--w-surface-muted);color:var(--w-t2);border:1px solid var(--w-hairline);flex-shrink:0}
.imp-empty{text-align:center;color:var(--w-t3);font-size:12px;padding:26px 0}
/* 상세 모달 */
.imp-d-top{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.imp-d-ttl{font-size:15px;font-weight:800;color:var(--w-t1);word-break:break-word}
.imp-d-meta{font-size:11px;color:var(--w-t3);line-height:1.6}
.imp-d-lbl{font-size:10px;font-weight:800;color:var(--w-t2);letter-spacing:.5px;text-transform:uppercase}
/* 본문·결과·코멘트 = 평문 textarea → white-space:pre-wrap 으로 줄바꿈 보존 */
.imp-d-body{white-space:pre-wrap;word-break:break-word;font-size:13px;line-height:1.75;color:var(--w-t1);background:var(--w-surface-alt);border:1px solid var(--w-hairline);border-radius:var(--w-radius-10);padding:11px 13px}
.imp-d-res{display:flex;flex-direction:column;gap:5px}
.imp-d-res .imp-d-body{background:var(--w-green-soft);border-color:var(--w-green-line)}
.imp-edit{border:1px solid var(--w-border);border-radius:var(--w-radius-10);padding:9px 11px;background:var(--w-surface-alt)}
.imp-edit summary{font-size:12px;font-weight:700;color:var(--w-t2);cursor:pointer}
.imp-edit>*:not(summary){margin-top:9px}
.imp-tl{display:flex;flex-direction:column;gap:6px}
.imp-hl{background:var(--w-surface-alt);border:1px solid var(--w-hairline);border-left:3px solid var(--w-border-strong);border-radius:var(--w-radius-8);padding:7px 10px}
.imp-hl-m{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:11px;color:var(--w-t2)}
.imp-hl-t{font-family:var(--mono);font-weight:800;color:var(--w-t1)}
.imp-hl-w{font-weight:700;color:var(--w-t1)}
.imp-hl-c{margin-top:4px;font-size:12px;line-height:1.65;color:var(--w-t1);white-space:pre-wrap;word-break:break-word}
.imp-acts{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.imp-sup-lbl{font-size:10px;font-weight:800;color:var(--w-t3);letter-spacing:.5px;margin-left:auto}
.imp-priv-chk{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--w-t1);cursor:pointer}
.imp-priv-chk input{width:15px;height:15px;cursor:pointer;accent-color:var(--w-brand)}
/* 📎 첨부(Firebase Storage, 2026-07-14) — 칩 + 이미지 썸네일(가로 스크롤).
   본문 인라인 붙여넣기 에디터는 없음(본문은 평문 textarea 유지 — XSS 표면 최소화). */
.imp-att-box{display:flex;flex-direction:column;gap:7px}
.imp-att-chips{display:flex;flex-wrap:wrap;gap:6px}
.imp-att-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:5px 9px;background:var(--w-surface-alt);border:1px solid var(--w-border);border-radius:var(--w-radius-8);font-size:11px;font-weight:700;color:var(--w-t1)}
.imp-att-nm{cursor:pointer;color:var(--w-brand);max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.imp-att-nm:hover{text-decoration:underline}
.imp-att-sz{color:var(--w-t3);font-weight:600;font-family:var(--mono);flex-shrink:0}
.imp-att-del{background:none;border:0;color:var(--w-t3);cursor:pointer;font-size:12px;line-height:1;padding:0 1px;flex-shrink:0}
.imp-att-del:hover{color:var(--w-red)}
.imp-att-thumbs{display:flex;gap:6px;overflow-x:auto;padding-bottom:3px}
.imp-att-thumbs img{max-height:120px;border-radius:var(--w-radius-8);border:1px solid var(--w-border);cursor:pointer;flex-shrink:0}
/* ── 본문 인라인 이미지(2026-07-22) ──
   본문은 여전히 평문이다. 붙여넣은 이미지는 기존 첨부(Storage)로 올리고 본문엔 토큰만 남기며,
   상세에서 그 자리에 <img> 로 렌더한다(사용자 HTML 저장·렌더 없음 = XSS 표면 0). */
.imp-inline-img{display:block;max-width:100%;height:auto;margin:8px 0;border-radius:var(--w-radius-8);border:1px solid var(--w-border);cursor:pointer;background:var(--w-surface-muted)}
/* 작성/수정 중 붙여넣은 이미지 미리보기 — 어떤 이미지가 몇 번 토큰인지 보이게 */
.imp-paste{display:none;gap:8px;flex-wrap:wrap;margin-top:6px}
.imp-pst{display:flex;flex-direction:column;align-items:center;gap:3px;max-width:110px}
.imp-pst img{max-height:74px;max-width:110px;border-radius:var(--w-radius-6);border:1px solid var(--w-border)}
.imp-pst-k{font-size:10px;font-weight:700;color:var(--w-brand);font-family:var(--mono)}
.imp-paste-hint{font-size:11px;color:var(--w-t3);margin-top:4px}
.imp-att-prog{font-size:11px;font-weight:700;color:var(--w-brand)}
.imp-att-none{font-size:11px;color:var(--w-t3)}
.rpt-tools{display:flex;gap:7px;align-items:center;flex-wrap:wrap;flex-shrink:0}
/* 수신자 선택 바 — 인라인 체크박스 칩 + 관리 버튼 */
.rpt-recipients-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;flex-shrink:0;padding:10px 12px;background:#EFF6FF;border:1px solid #BFDBFE;border-radius:9px}
.rpt-recipients-bar .rrb-lbl{font-size:12px;font-weight:800;color:#1E40AF;flex-shrink:0}
.rpt-recipients-bar .rrb-list{display:flex;gap:5px;flex-wrap:wrap;flex:1;min-width:0;align-items:center}
.rpt-recipients-bar .rrb-empty{font-size:11px;color:var(--t3);font-style:italic}
.rrb-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;background:#fff;border:1.5px solid var(--bdr);border-radius:7px;font-size:11px;font-weight:600;color:var(--t2);cursor:pointer;transition:all .14s;user-select:none}
.rrb-chip:hover{border-color:#93C5FD;background:#F0F9FF}
.rrb-chip.checked{background:#1D4ED8;color:#fff;border-color:#1D4ED8;box-shadow:0 1px 3px rgba(29,78,216,.3)}
.rrb-chip .rrb-role{font-size:9px;opacity:.75;font-weight:500}
.rrb-chip.checked .rrb-role{opacity:.9}
.rrb-chip input{display:none}
/* 수신자 관리 모달 — 리스트 + add/edit/delete */
.rrm-list{display:flex;flex-direction:column;gap:6px;max-height:400px;overflow-y:auto}
.rrm-row{display:flex;align-items:center;gap:8px;padding:8px 12px;background:#fff;border:1px solid var(--bdr);border-radius:7px}
.rrm-row .rrm-info{flex:1;min-width:0}
.rrm-row .rrm-nm{font-size:12px;font-weight:700;color:var(--t1)}
.rrm-row .rrm-em{font-size:10px;color:var(--t3);font-family:var(--mono)}
.rrm-row .rrm-role{font-size:10px;color:#6D28D9;background:#EDE9FE;border:1px solid #C4B5FD;padding:1px 6px;border-radius:4px;font-weight:700}
.rrm-row .rrm-default{font-size:9px;color:#1D4ED8;background:#DBEAFE;border:1px solid #93C5FD;padding:1px 6px;border-radius:4px;font-weight:700}
.rrm-empty{padding:30px 20px;text-align:center;color:var(--t3);font-size:12px}
/* ── 항목7 다이제스트 — 그날 이슈 메모 모듬(기록용 포함). 노선별 그룹 테이블 위 1카드. ── */
.rpt-digest{flex-shrink:0;background:var(--w-orange-soft);border:1px solid var(--w-orange-line);border-radius:10px;padding:12px 14px;margin-bottom:4px}
.rpt-digest .rd-h{font-size:13px;font-weight:800;color:var(--w-orange-ink);margin-bottom:9px}
.rpt-digest .rd-list{display:flex;flex-direction:column;gap:6px}
.rpt-digest .rd-item{display:flex;align-items:baseline;gap:8px;font-size:12px;line-height:1.5;flex-wrap:wrap}
.rpt-digest .rd-nm{font-weight:800;color:var(--w-t1);flex-shrink:0}
.rpt-digest .rd-cli{font-size:11px;color:var(--w-brand);flex-shrink:0}
.rpt-digest .rd-tag{font-size:10px;font-weight:700;color:var(--w-t3);background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-pill);padding:1px 7px;flex-shrink:0}
.rpt-digest .rd-txt{color:var(--w-t1);white-space:pre-wrap;word-break:break-word;flex:1;min-width:0}
.rpt-sum{display:flex;gap:8px;flex-wrap:wrap;flex-shrink:0;padding:8px 0;font-size:12px;color:var(--t2)}
.rpt-sum .rs-c{padding:5px 12px;background:#fff;border:1px solid var(--bdr);border-radius:7px;font-weight:700;display:inline-flex;align-items:center;gap:5px}
.rpt-sum .rs-c b{font-family:var(--mono);font-size:13px}
.rpt-sum .rs-ok b{color:var(--cg)}
.rpt-sum .rs-late b{color:#F59E0B}
.rpt-sum .rs-miss b{color:#EF4444}
.rpt-sum .rs-tot b{color:var(--t1)}
/* rpt-body: flex column이지만 flex:1 + min-height:0 시 자식 .rpt-grp가 flex-shrink:1 기본값으로
   0 높이까지 압축되는 함정 → 카드 30+개일 때 본문 안 보임. 자연 높이 유지 + 페이지 #page-report가
   overflow-y:auto로 스크롤. 새 보고 본문 자식 추가 시 flex-shrink:0 잊지 말 것. */
.rpt-body{display:flex;flex-direction:column;gap:10px}
.rpt-grp{flex-shrink:0}
.rpt-empty{flex:1;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:10px;color:var(--t3);font-size:13px;padding:60px 20px}
.rpt-grp{background:#fff;border:1px solid var(--bdr);border-radius:10px;overflow:hidden;box-shadow:0 1px 2px rgba(15,23,42,.04)}
.rpt-grp-h{padding:10px 14px;background:#F9FAFB;border-bottom:1px solid var(--bdr);display:flex;align-items:center;gap:8px;font-size:13px;font-weight:800;color:var(--t1)}
.rpt-grp-h .rh-cli{font-size:10px;color:#1D4ED8;background:#EFF6FF;border:1px solid #BFDBFE;padding:2px 7px;border-radius:5px;font-weight:700}
.rpt-grp-h .rh-cnt{font-size:11px;color:var(--t3);font-weight:600;font-family:var(--mono);margin-left:auto}
table.rpt-t{width:100%;border-collapse:collapse;font-size:12px}
table.rpt-t th{background:#F9FAFB;color:var(--t2);font-weight:700;padding:7px 10px;text-align:left;border-bottom:1px solid var(--bdr);white-space:nowrap;font-size:11px}
table.rpt-t td{padding:7px 10px;border-bottom:1px solid #F3F4F6;color:var(--t1);vertical-align:middle;font-family:var(--mono)}
table.rpt-t tr:last-child td{border-bottom:none}
table.rpt-t .rt-nm{font-family:var(--font);font-weight:700}
table.rpt-t .rt-co{font-family:var(--font);font-size:10px;color:var(--t3)}
table.rpt-t .rt-tg-ok{color:var(--cg);font-weight:700;font-family:var(--font);font-size:11px}
table.rpt-t .rt-tg-late{color:#F59E0B;font-weight:700;font-family:var(--font);font-size:11px}
table.rpt-t .rt-tg-miss{color:#EF4444;font-weight:700;font-family:var(--font);font-size:11px}
table.rpt-t .rt-na{color:var(--t3)}
table.rpt-t .rt-late{color:#F59E0B;font-weight:700}
/* 메모 표시 — read-only 텍스트. 입력/수정은 관제현황 행 📝 버튼에서만(단일 진입점).
   업무보고는 종합 조회·발송 용도, 입력은 실시간 관제에서 처리하는 분리 정책. */
table.rpt-t .rt-note-txt{font-family:var(--font);color:#78350F;background:#FEF3C7;border:1px solid #F59E0B;border-radius:5px;padding:4px 8px;font-size:11px;font-weight:600;display:inline-block;line-height:1.4;max-width:280px;word-break:break-word;white-space:normal}
table.rpt-t .rt-note-empty{color:var(--t3);font-size:11px}

/* ══════════════════════════════════════════════
   모달
══════════════════════════════════════════════ */
/* Phase 2C (2026-05-19): 목업 common.jsx 모달 톤 — 컨테이너 chrome CSS만(마크업/id/onclick/`open`er 헬퍼·핸들러 0 변경).
   backdrop 동일 농도·blur 유지(Wanted t1 잉크 기반), 카드 surface·radius20·Wanted heavy shadow, 헤더/푸터 surface-alt+hairline. */
/* ★ 2026-08-27 5라운드 — 팝업 「슬로우모션」 해소. 종전 이 한 줄이 세 가지를 동시에 했다:
   ① `backdrop-filter:blur(4px)` = 뒤에 있는 **문서 전체**(현장 캡처 기준 51,367 노드)를 스냅샷해 블러.
      전환 250ms 동안 **매 프레임** 다시 블러하므로 페이지가 커질수록 그대로 비싸진다.
   ② 그런 뷰포트 전체 fixed 레이어가 **문서에 17개** 상주했다(`display:flex`+`opacity:0` 이라
      숨은 게 아니라 **투명**했다) — 자식들도 늘 레이아웃 대상이었다.
   ③ `transition:opacity .25s` + `.mc` 의 `fadeUp .26s` 가 겹쳐 열리는 데 0.5초가 든다.
   → 딤은 반투명 단색으로 충분하다(2026-07-14 모바일에서 같은 판단을 이미 내렸고, 데스크톱만
     남겨 둔 것이 페이지가 커지며 되돌아왔다). 블러 제거분은 농도 .42 → .5 로 보상한다.
   🔴 `display:none` 이 핵심이다 — `opacity:0` 은 레이어를 없애지 않는다.
   ★ 안티-회귀: `.mo` 에 `backdrop-filter` 를 되살리지 말 것 · 닫힌 모달을 `opacity:0` 로만
     감추지 말 것(둘 다 이 신고의 정확한 재발 경로다). */
.mo{position:fixed;inset:0;z-index:100;background:rgba(23,23,25,.5);display:none;align-items:center;justify-content:center;padding:16px}
.mo.open{display:flex}
.mc{background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-20);width:100%;max-width:500px;max-height:88vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:var(--w-shadow-heavy);animation:fadeUp .14s ease}
.mc-h{padding:16px 20px 13px;border-bottom:1px solid var(--w-hairline);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;background:var(--w-surface-alt)}
.mc-h h3{font-size:14px;font-weight:800;color:var(--w-t1)}
.btn-mc{background:none;border:none;color:var(--w-t3);font-size:17px;cursor:pointer;padding:2px 5px;border-radius:var(--w-radius-6);transition:all .12s}.btn-mc:hover{color:var(--w-t1);background:var(--w-surface-muted)}
.mc-b{padding:18px 20px;display:flex;flex-direction:column;gap:12px;flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;background:var(--w-surface)}
.mc-f{padding:13px 20px 17px;border-top:1px solid var(--w-hairline);display:flex;justify-content:flex-end;gap:7px;flex-shrink:0;background:var(--w-surface-alt)}
/* Phase 2D (2026-05-19): 목업 common.jsx 입력 비주얼 — CSS 속성값만(폼 id/마크업/핸들러 0 변경). */
.fr{display:flex;flex-direction:column;gap:5px;min-width:0}
.fr label{font-size:10px;font-weight:700;color:var(--w-t2);letter-spacing:.5px;text-transform:uppercase}
/* width:100%+min-width:0 — box-sizing:border-box 전역(L74)이라 padding 포함 트랙 정확히 채움 + grid 1fr 트랙 축소 허용(입력 min-content가 트랙 못 줄이던 우측 잘림 일괄 해소). 더 구체적 .radius-bulk .rb-fld .fi2{width:110px}(L968)는 셀렉터 특이도 우선이라 영향 0. */
.fi2,.fs2{width:100%;min-width:0;padding:10px 12px;background:var(--w-surface);border:1.5px solid var(--w-border);border-radius:var(--w-radius-10);color:var(--w-t1);font-size:13px;font-family:var(--font);outline:none;transition:border-color .12s,box-shadow .12s}
.fi2:focus,.fs2:focus{border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.12)}.fs2 option{background:var(--w-surface)}
.fr2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.fr3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}
/* 담당자 모달 관할 거래처/업체 체크박스 피커 (2026-06-16): 기존 <select multiple>의 Ctrl/⌘ 클릭 함정 해소.
   신규 클래스만 추가 — 기존 모달/그리드/폼 CSS 미접촉. Wanted DS 토큰만 사용. */
.mgr-chk-bar{display:flex;gap:6px;align-items:center;margin-bottom:6px}
.mgr-chk-bar input{flex:1 1 auto;min-width:0;padding:7px 10px;background:var(--w-surface);border:1.5px solid var(--w-border);border-radius:var(--w-radius-6);color:var(--w-t1);font-size:12px;font-family:var(--font);outline:none}
.mgr-chk-bar input:focus{border-color:var(--w-brand);box-shadow:0 0 0 3px rgba(0,102,255,.12)}
.mgr-chk-bar button{flex:0 0 auto;padding:6px 10px;background:var(--w-surface);border:1px solid var(--w-border);border-radius:var(--w-radius-6);color:var(--w-t2);font-size:11px;font-weight:700;font-family:var(--font);cursor:pointer;white-space:nowrap}
.mgr-chk-bar button:hover{background:var(--w-surface-alt);color:var(--w-t1)}
.mgr-chk-sum{font-size:11px;color:var(--w-t3);margin-bottom:6px;line-height:1.4}
.mgr-chk-list{max-height:180px;overflow:auto;border:1.5px solid var(--w-border);border-radius:var(--w-radius-8);background:var(--w-surface);padding:4px}
.mgr-chk{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--w-radius-6);cursor:pointer;font-size:12px;color:var(--w-t1)}
.mgr-chk:hover{background:var(--w-surface-alt)}
.mgr-chk input{flex:0 0 auto;width:15px;height:15px;cursor:pointer;accent-color:var(--w-brand)}
.mgr-chk:has(input:checked){background:var(--w-brand-subtle);color:var(--w-brand-strong);font-weight:600}
.mgr-chk-empty{font-size:11px;color:var(--w-t4);padding:8px;text-align:center}
/* 공통 버튼 — Phase 2D (2026-05-19): 목업 common.jsx 평면 단색 톤(그라데이션→Wanted 의미색 단색).
   상태→색 의미 분리 보존: p=brand 블루 / s=green / d=red. CSS만(클래스명·onclick·핸들러 0 변경). */
.btn{padding:9px 16px;border-radius:var(--w-radius-8);font-size:12px;font-weight:700;font-family:var(--font);cursor:pointer;border:none;transition:all .12s;display:inline-flex;align-items:center;gap:5px}
.btn:active{transform:scale(.98)}.btn:hover{filter:brightness(1.04)}
.btn-p{background:var(--w-brand);color:#fff}
.btn-s{background:var(--w-green);color:#fff}
.btn-d{background:var(--w-red);color:#fff}
.btn-g{background:var(--w-surface);border:1px solid var(--w-border);color:var(--w-t1)}
.btn-g:hover{background:var(--w-surface-alt);filter:none}
.btn-sm{padding:6px 11px;font-size:11px}

#ov{position:fixed;inset:0;z-index:40;background:rgba(15,23,42,.4);display:none}
.toast{position:fixed;top:13px;left:50%;transform:translateX(-50%) translateY(-60px);z-index:999;background:#1F2937;border:none;border-radius:10px;padding:10px 16px;font-size:12px;color:#fff;font-weight:600;box-shadow:0 8px 24px rgba(15,23,42,.25);display:flex;align-items:center;gap:7px;transition:transform .28s cubic-bezier(.34,1.56,.64,1),opacity .28s;opacity:0;white-space:nowrap}
.toast.show{transform:translateX(-50%) translateY(0);opacity:1}
/* ── 2차경고 알림 배너 (요약형 한 줄) ── */
.alert-banner{
  display:none!important; /* 사용자 요청(2026-05-19): 상단 경고 배너 제거. 요소·checkCriticalAlert JS는 onSnapshot 안전 위해 유지, 표시만 영구 차단(사이드바 위험 전광판이 동일 정보 제공) */
  position:fixed;top:0;left:0;right:0;z-index:9999;
  background:linear-gradient(90deg,#7F1D1D,#991B1B);
  border-bottom:2px solid var(--cc);
  padding:7px 20px;
  display:flex;align-items:center;gap:10px;
  height:38px;
  transform:translateY(-100%);
  transition:transform .3s cubic-bezier(.34,1.56,.64,1);
  box-shadow:0 4px 20px rgba(239,68,68,.5);
  cursor:pointer;
}
.alert-banner.show{transform:translateY(0)}
.alert-banner-ic{font-size:16px;flex-shrink:0}
.alert-banner-txt{font-size:13px;font-weight:700;color:#FEE2E2;white-space:nowrap}
.alert-banner-sub{font-size:11px;color:#FCA5A5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1}
.btn-alert-close{background:rgba(255,255,255,.2);border:none;border-radius:6px;color:#fff;font-size:11px;font-weight:700;padding:4px 10px;cursor:pointer;font-family:var(--font);flex-shrink:0}

/* 운행 요일 체크박스 — Phase 2D (2026-05-19): Wanted 토큰 정합(CSS만). 체크=brand-subtle, 주말=yellow 톤 유지. */
.wd-grid{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-top:4px}
.wd-cb{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;background:var(--w-surface);border:1.5px solid var(--w-border);border-radius:var(--w-radius-8);font-size:12px;font-weight:600;color:var(--w-t2);cursor:pointer;user-select:none;transition:all .14s;font-family:var(--font)}
.wd-cb input{cursor:pointer;margin:0;accent-color:var(--w-brand)}
.wd-cb:hover{border-color:var(--w-brand-hairline);background:var(--w-surface-alt)}
.wd-cb:has(input:checked){background:var(--w-brand-subtle);border-color:var(--w-brand-hairline);color:var(--w-blue-ink);font-weight:800}
.wd-cb.wd-weekend{color:var(--w-t3)}
.wd-cb.wd-weekend:has(input:checked){background:var(--w-yellow-soft);border-color:var(--w-yellow-line);color:var(--w-yellow-ink)}
/* 거래처 요일별 시각 오프셋(±분) 입력 셀 — 7열 그리드, 값 있으면 강조(:has) */
.off-cell{display:flex;flex-direction:column;align-items:center;gap:3px;font-size:11px;font-weight:700;color:var(--w-t2);user-select:none;min-width:0}
.off-cell.off-weekend{color:var(--w-t3)}
.off-cell input{width:100%;text-align:center;padding:5px 2px;border:1px solid var(--w-border);border-radius:var(--w-radius-6);font-family:var(--mono);font-size:12px;background:var(--w-surface);color:var(--w-t1)}
.off-cell:has(input:not(:placeholder-shown)) input{background:var(--w-brand-subtle);border-color:var(--w-brand-hairline);color:var(--w-brand);font-weight:700}
.wd-preset{padding:7px 12px;background:var(--w-surface);border:1px dashed var(--w-border);border-radius:var(--w-radius-6);font-size:11px;font-weight:600;color:var(--w-t2);cursor:pointer;font-family:var(--font);transition:all .14s;margin-left:auto}
.wd-preset:first-of-type{margin-left:8px}
.wd-preset:hover{background:var(--w-brand-subtle);border-color:var(--w-brand);border-style:solid;color:var(--w-blue-ink)}

/* ══════════════════════════════════════════════
   ── 태블릿/모바일 768px 이하 ──
   ★ 2줄 grid — 1줄=신호(시간 stack/이름/5-stage), 2줄=액션 6버튼 모두 노출
   레이아웃:
     1줄: [알람 stack 52][차/출 stack 56][이름·노선 1fr][5-stage auto]
     2줄: 6버튼 액션 (전화·비상·담·채팅·제외·푸시) 우측 정렬
   ─ "한눈에 보이는 신호"는 1줄에 모두, 액션은 별도 행에 분리 → 기능 빠짐 없음
   ─ col-time/col-dep 각자 세로 stack → 알람/차고지/출발 시각 셋 다 표시 (잘림 방지)
   ─ .dr 자체 GPU 합성 레이어 분리(translateZ + isolation) → 줌/스크롤 시 페인트 폭주 차단
══════════════════════════════════════════════ */
@media(max-width:768px){
  .sidebar{position:fixed;top:0;bottom:0;left:0;z-index:25;transform:translateX(-100%);width:240px}
  /* ★ [근본원인 2026-05-18] 기존 `.sb-ovl{display:block}`이 여기 무조건 깔려 있어, 모바일
     첫 로드(탭전환 없음→closeSB 미호출→인라인 none 없음)부터 전체화면 오버레이
     (position:fixed;inset:0;z-index:15)가 콘텐츠를 덮고 탭을 가로챔 → 화면 깨짐 + 햄버거
     먹통. 오버레이는 JS 인라인(btn-mnu 표시/closeSB 숨김)으로만 제어해야 하므로 미디어쿼리
     강제 block 제거. base `.sb-ovl{display:none}`가 올바른 기본값. (→ issues-misc.md) */
  .sidebar.open{transform:translateX(0)}.mhdr{display:flex}
  .sb-tg{display:none} /* 모바일은 mhdr 햄버거가 담당 */
  /* ★ [구조 교정 2026-07-14] 2026-07-13 에 여기 있던 "되받아치기" 오버라이드
     (body.sb-collapsed .sidebar{width:240px;min-width:240px;overflow:visible;opacity:1;box-shadow:...})는
     제거했다. 데스크톱 접기 규칙이 이제 @media(min-width:769px) 안에 있어 모바일엔 애초에 안 걸리므로
     되받아칠 대상 자체가 없다 — 오버라이드 전쟁 종료. 모바일 사이드바 = off-canvas transform 전용(원설계).
     ★ 안티-회귀: 여기에 width/opacity 재선언을 다시 넣지 말 것(깜빡임의 근원). */
  /* base .sidebar 의 transition(width/min-width/opacity)을 transform 전용으로 교체 — 모바일에서
     폭·투명도는 애니메이션할 일이 없고, 슬라이드와 겹치면 합성 아티팩트가 된다. */
  .sidebar{transition:transform .25s ease}
  /* 닫힌 사이드바는 position:fixed + translateX(-100%) 라 우측 방향 box-shadow 가 화면 좌측
     가장자리로 새어 스크롤마다 페인트된다 → 열렸을 때만 그림자(페인트 감소). */
  .sidebar:not(.open){box-shadow:none}
  #kakao-map{height:340px}

  .ctrl-th{display:none}
  /* ─────── 모바일 행 = flex-wrap 2줄 (★ 2026-05-18 grid→flex 전면 교체) ───────
     사유: CSS Grid `grid-template-rows:auto auto` + 자식 grid-span 조합이 특정
     폴더블 Chrome에서 row 트랙 높이를 깨먹어(자식 col-actions만 분리되어 떠다니고
     행 사이 거대 공백) PC·일반폰은 정상인데 그 기기만 사망. content-visibility·
     레이어·캐시·오버레이 전부 배제 후 남은 유일 원인 = grid 트랙 산정. flex-wrap은
     트랙 개념이 없어(각 자식=콘텐츠 크기, 줄바꿈은 basis로) 구조적으로 그 현상 불가.
     1줄: [알람 고정][차/출 고정][이름·노선 grow][5-stage grow], 2줄: [6버튼 우측].
     자식의 기존 grid-row/grid-column 선언은 flex 부모 아래 자동 무효(무해)라 미삭제. */
  /* ── M1 (2026-05-19): fold.jsx MobileDriverCard 룩 — CSS만, 모바일 행 wrap 구조·
     자식 flex 산식 불변(폴더블 안정화 결과 grid→flex 절대 유지). 카드화: 흐름행 → 분리된
     카드(테두리+radius+카드 간격). 위험 컬러바(risk-tint 클래스 4종)·tint bg는
     Phase1e에서 이미 제공 → 미접촉(재정의 0). base 행 구분선(border-bottom)은
     위험 tint 클래스와 무관한 흐름행용이므로 모바일 카드화 시 카드 간 margin이
     분리를 담당(아래 위험 클래스 규칙은 byte-불변). */
  .dr{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    row-gap:4px;
    column-gap:5px;
    padding:12px 12px 10px;
    border-top:1px solid var(--w-border);
    border-right:1px solid var(--w-border);
    border-bottom:1px solid var(--w-border);
    border-radius:14px;
    margin:0 0 8px;
  }
  /* ★ border-left 미선언 — base 행의 4px 투명 좌측테두리(L298)가 cascade로 유지되고,
     위험 tint 클래스 4종(L332~347)이 border-left-color !important로 4px 컬러 좌측바를
     그대로 부여(fold.jsx MobileDriverCard borderLeft:4px 정합). shorthand border 미사용
     사유 = 4px 좌측바 폭이 1px로 깎이는 것 방지.
     ★ background 미선언 — 위험 tint 클래스 4종(동일 특이도 0,1,0)이 소스순서상 이
     @media 블록보다 앞이라, 여기서 background를 박으면 위험 tint를 덮어버림. base 행의
     흰 배경(L303)은 cascade로 비위험 행에 유지·위험 행은 soft tint 보존
     (재정의 0 — 작업 명세 불변식). */
  /* 카드 좌우 호흡 — 컨테이너 패딩(모바일 한정, 데스크톱 .ctrl-body L659 byte-불변) */
  .ctrl-body{padding:10px 12px 0}
  .dr>.col-time{flex:0 0 60px}
  .dr>.col-dep{flex:0 0 56px}
  .dr>.col-info{flex:1 1 auto;min-width:0}       /* R1: 1행에서 col-status 빠진 만큼 이름/노선이 남은 폭 차지(cramped 해소). 알람·차출은 고정폭 유지 */
  .dr>.col-status{flex:1 0 100%}                 /* R1 (2026-05-19): 신호중심 — 5단계를 자기 전체폭 줄로 독립(col-actions와 동형 검증된 wrap 패턴, grid 아님·폴더블 안전). 시각 3줄: [알람·차출·이름]/[5단계 전체폭]/[액션바]. fold.jsx MobileDriverCard 세로 3블록 정합 */
  .dr>.col-actions{flex:1 0 100%}                /* basis 100% → 무조건 셋째 줄 전체폭 */
  /* ★ content-visibility 제거(2026-05-18) 후 구식 min-height:90 + intrinsic-size는
     불필요·역효과(2행 grid 자연높이 ≈80px인데 90 강제 → 행마다 ~10px 빈여백 누적,
     완료 행 흐림과 겹쳐 "빈칸"처럼 보임). min-height:0 → 자연 콘텐츠 높이로 촘촘하게. */
  .ctrl-body.dn-c .dr,.ctrl-body.dn-n .dr{min-height:0}

  /* ── col-time (1행 1열): 알람 ── */
  .col-time{flex-direction:column;align-items:center;justify-content:center;gap:0;padding:0;line-height:1.1;grid-row:1;grid-column:1}
  .col-time::after{display:none}
  .time-alarm,.ctrl-body.dn-c .time-alarm,.ctrl-body.dn-n .time-alarm{
    font-size:15px;padding:3px 7px;border-radius:6px;line-height:1.1;font-weight:900
  }

  /* ── col-dep (1행 2열): 차/출 stack ── */
  .col-dep,.ctrl-body.dn-c .col-dep{
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    padding:0;gap:1px;grid-row:1;grid-column:2;overflow:hidden;
  }
  .col-dep .dep-arrow{display:none}
  .col-dep .dep-time,.ctrl-body.dn-c .dep-time{
    font-size:11px;line-height:1.2;font-weight:800;color:var(--cy);
    display:inline-flex;align-items:baseline;gap:1px;
  }
  .col-dep .dep-dep,.ctrl-body.dn-c .dep-dep{
    font-size:11px;line-height:1.2;font-weight:800;color:var(--t2);
    display:inline-flex;align-items:baseline;gap:1px;
  }
  .col-dep .dep-time::before{content:"차";opacity:.6;font-size:9.5px;margin-right:2px;font-weight:700}
  .col-dep .dep-dep::before{content:"출";opacity:.6;font-size:9.5px;margin-right:2px;font-weight:700}

  /* ── col-info (1행 3열): 이름+노선 stack — 좁아도 충분히 알아볼 글자 크기.
     데스크탑은 grid sub-track 정렬이지만 모바일은 다시 flex column stack. */
  .col-info{
    display:flex;
    flex-direction:column;align-items:flex-start;justify-content:center;
    padding:0 2px;gap:2px;overflow:hidden;
    grid-row:1;grid-column:3;
  }
  .info-name-wrap{width:100%}                       /* R1: 모바일 컬럼 flex에서 이름 래퍼 전폭(전화 chip은 모바일 숨김) */
  .col-info .info-name,.ctrl-body.dn-c .info-name,.ctrl-body.dn-n .info-name{
    font-size:14.5px;font-weight:900;max-width:none;flex-shrink:0;line-height:1.2;color:var(--t1);
    width:100%;
  }
  /* 모바일은 이름 래퍼가 전폭이라 위 규칙(width:100%)이 그대로면 정보칸 대부분이 지도 이동이 되어
     행 탭("이 기사만 보기")을 잡아먹는다 → 지도 링크는 글자 폭만 차지. 터치 타깃은 padding 으로 확보. */
  .col-info .info-name.nm-map{width:auto;max-width:100%;padding:3px 5px;margin:-3px -5px}
  .info-route-text,.ctrl-body.dn-c .info-route-text,.ctrl-body.dn-n .info-route-text{
    font-size:11px;line-height:1.2;font-weight:600;width:100%;
  }
  /* 차량번호/거래처/회사/전화 chip — 모바일 좁아서 노이즈, 행 클릭 상세에서 확인 */
  .info-plate,.info-plate-wrap,.info-cli,.info-cli-wrap,.info-mgr,.info-co,.info-phone{display:none}

  /* ── col-status (1행 4열): 5-stage — 1.8fr 가로 확장, cp 가중 분배 ── */
  .col-status{padding:0;display:flex;align-items:center;justify-content:stretch;grid-row:1;grid-column:4;overflow:hidden}
  /* R1 (2026-05-19): 5단계가 자기 전체폭 줄로 독립(col-status flex:1 0 100%)되어
     신호중심으로 키움 — fold.jsx MobileDriverCard 5단계 서브패널(L142~155)이
     세로 독립 블록인 정합. ★ width:100%/gap:3/cp-row flex·GPS-가중·::before숨김·
     cn-time숨김 전부 불변(issues-grid 불변식 — 부모 grid 아님·폴더블 안전).
     자기 줄에서 height 34→46(시원하게)·수직 padding 2→4(호흡)·cn-dot 16→19·
     cn-lbl 11→12.5(신호가 주인공). border hairline·radius·수평 padding 유지.
     box-sizing:border-box(base L443)라 height 고정+수직패딩4 → dot19+lbl12.5
     ≈ 35 ≤ 46-8 수용. justify-content:space-between 유지로 5노드 균등 시원.
     ── R2 (2026-05-19, 사용자 피드백 "신호파트가 가장 크게 보이도록" +
     "전화 버튼행은 최소화"): fold.jsx MobileDriverCard 비율 정합 — 신호 밴드를
     카드의 압도적 주인공으로(큰 원형 노드), 액션행은 얇은 띠로 최소화(R2-B).
     [R2-A] height 46→64(압도적 밴드)·수직 padding 4→6(호흡). cn 38→54,
     cn-dot 19→28(목업 큰 원형 filled dot, 글리프 10.5→14), cn-lbl 12.5→11.5
     (dot이 주인공·라벨 보조, weight 900 유지). budget: dot28+lbl12+gap ≈ 46
     ≤ 64-12 수용. ★ 원형(base .cn-dot border-radius 불변·크기만)·gap 3·
     width:100%·cp-row flex·GPS-가중·::before숨김·cn-time숨김 전부 byte-불변. */
  .status-panel,.ctrl-body.dn-c .status-panel,.ctrl-body.dn-n .status-panel{
    height:64px;padding:6px 10px;gap:3px;border-radius:10px;width:100%;justify-content:space-between;
    background:var(--w-surface-alt);border-color:var(--w-hairline)
  }
  /* 모바일: cp-row를 grid→flex 오버라이드(issues-grid 불변식 — 부모 grid면 자식 깨짐).
     노드 5개 가로 균등, 연결선(::before)은 flex라 정적 정렬 불가 → 숨김. */
  .status-panel .cp-row{flex:1 1 auto;display:flex;gap:3px;justify-content:space-between;width:100%;min-width:0;grid-template-columns:none}
  .status-panel .cp-row::before{display:none}
  .cn,.ctrl-body.dn-c .cn,.ctrl-body.dn-n .cn{
    flex:1 1 0;min-width:0;width:auto;height:54px;gap:3px;flex-direction:column;justify-content:center;
    overflow:visible;                                              /* 라벨 위·아래 잘림 방지 */
  }
  /* R3 (2026-05-19, 사용자 "열 간격이 동일했으면"): 모바일은 cn-time 숨김 +
     .cn{overflow:visible}이라 "GPS" 3글자 라벨이 균등 칸에서도 안 잘림 →
     GPS :nth-child(4) flex 가중을 1(나머지 4노드와 동일)로 해제, 5칸 균등.
     ★ 사용자 명시 결정 — future 세션 재가중 금지(회귀 아님). 데스크톱 cp-row
     grid의 GPS 트랙 처리는 별개·미접촉(이 규칙은 모바일 flex 오버라이드 한정). */
  .status-panel .cp-row .cn:nth-child(4){flex:1 1 0}
  .cn .cn-time{display:none}                                       /* 모바일은 dot+라벨만(시각 숨김) */
  .cn .cn-lbl{display:block;font-size:11.5px;font-weight:900;letter-spacing:0}
  .cn-dot,.ctrl-body.dn-c .cn-dot,.ctrl-body.dn-n .cn-dot{width:28px;height:28px;font-size:14px}
  .sp-flag{display:none}

  /* col-metrics / col-risk → 노이즈, 숨김 */
  .col-metrics,.col-risk{display:none}

  /* ── col-actions (2행 전체): M2 (2026-05-19) fold.jsx MobActBtn 라벨형 액션바.
     7개 액션 전부 액션바에 도달(오버플로우 메뉴·JS 동작 도입 0). 전화=라벨+아이콘
     와이드(green, flex 2, critical urgent 링 유지), 담당/채팅=라벨 컴팩트, 나머지
     (비상/메모/제외/푸시)=아이콘 컴팩트. 기존 .rb2-call/-mgr/-chat/... 색·urgent
     클래스 의미 전부 보존(배경 미재정의 — base flat 토큰 그대로). 데스크톱(>768)은
     이 블록 미적용 → 룩·폭·grid·액션컬럼 산식(issues-grid 232/208) byte-불변. */
  /* R2-B (2026-05-19, 사용자 "전화 버튼 행은 최소화"): 신호 밴드(R2-A ~64px)와
     시각 대비되게 액션바를 얇은 띠로 축소 — 신호=주인공, 액션=보조. 7버튼 전부
     도달 유지(오버플로우 메뉴·JS·마크업 0). 라벨(전화/담당/채팅) 표시·rb2-gly/lbl
     토글·색/urgent 클래스 의미 전부 보존(배경 미재정의). gap 6→5. */
  .col-actions{
    grid-column:1 / -1;grid-row:2;
    justify-content:flex-end;
    padding:0 2px;gap:5px;
    flex-wrap:nowrap;
  }
  .rb2-chat,.rb2-excl,.rb2-mgr,.rb2-emerg,.rb2-push,.rb2-note{display:inline-flex}
  /* fold.jsx MobActBtn: 평면 단색. 기본 = 아이콘 컴팩트. 라벨 버튼만 아래에서
     flex 부여 + 라벨 표시. R2-B: height 40→30·font 12→11·flex 38→30(얇은 띠). */
  .rb2,.ctrl-body.dn-c .rb2,.ctrl-body.dn-n .rb2{
    width:auto;flex:0 0 30px;height:30px;font-size:11px;font-weight:800;
    border-radius:var(--w-radius-10);gap:6px;padding:0
  }
  /* 데스크톱 글자(담) 숨기고 모바일 라벨(전화/담당/채팅) 표시 */
  .rb2 .rb2-gly{display:none}
  .rb2 .rb2-lbl{display:inline}
  /* 전화 = 와이드 라벨+아이콘(fold MobActBtn green, primary). a 태그가 내부
     클릭영역이므로 라벨/아이콘 정렬도 a 안에서. critical 시 rb2-urgent 링 유지.
     R1 (2026-05-19): flex 2→1.7 — 전화가 담당·채팅(flex 1) 대비 과대해
     불균형이던 것 완화(여전히 primary 우세, 라벨 안 잘리는 충분 폭).
     R2-B (2026-05-19): 얇은 띠 — height 40→30·font 13→12·flex 1.7→1.5
     (전화 여전히 primary지만 과대X·목업 비율). */
  .rb2-call,.ctrl-body.dn-c .rb2-call,.ctrl-body.dn-n .rb2-call{
    flex:1.5 1 0;min-width:0;height:30px;font-size:12px;padding:0 8px
  }
  .rb2-call a{gap:6px}
  .rb2-call .rb2-lbl{font-weight:800}
  /* 담당·채팅 = 라벨 컴팩트(fold flex 1). R2-B: padding 0 8→0 7(라벨 안 잘리게). */
  .rb2-mgr,.ctrl-body.dn-c .rb2-mgr,.ctrl-body.dn-n .rb2-mgr,
  .rb2-chat,.ctrl-body.dn-c .rb2-chat,.ctrl-body.dn-n .rb2-chat{
    flex:1 1 0;min-width:0;padding:0 7px;letter-spacing:0
  }
  /* 비상/메모/제외/푸시 = 아이콘 컴팩트(라벨 없음 — 명세상 라벨 미부여).
     R1 (2026-05-19): 7버튼 균형 — 아이콘 40→38px 균일(라벨버튼에 폭 양보,
     아이콘 4개 동일 크기로 욱여듦 해소). gap 6 균일 유지.
     R2-B (2026-05-19): 얇은 띠 — 아이콘 38→30 정사각 소형(액션 보조화). */
  .rb2-emerg,.ctrl-body.dn-c .rb2-emerg,.ctrl-body.dn-n .rb2-emerg,
  .rb2-note,.ctrl-body.dn-c .rb2-note,.ctrl-body.dn-n .rb2-note,
  .rb2-excl,.ctrl-body.dn-c .rb2-excl,.ctrl-body.dn-n .rb2-excl,
  .rb2-push,.ctrl-body.dn-c .rb2-push,.ctrl-body.dn-n .rb2-push{
    flex:0 0 30px
  }
  .rb2 i{font-size:15px}

  /* ── 그 외 모바일 일반 ── */
  /* 요약 스트립 가로 스크롤 허용 */
  .sum-strip{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:thin;-webkit-overflow-scrolling:touch}
  /* 필터 바도 가로 스크롤 */
  .ctrl-bar{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch}
  .btn-ap2{margin-left:8px}
  /* ★ [근본원인 2026-07-13] 모바일 채팅 2결함:
     ① 기사 리스트 잘림 — `.chat-lp{height:170px}` 고정인데 헤더(제목+검색+모아보기)가 ~110px를
        먹어 리스트가 60px 남짓만 남음. → 높이 auto + `max-height:38vh`(헤더 아래로 리스트가
        실제 공간을 갖고, 넘치면 .chat-li가 스크롤). 모바일 헤더 자체도 컴팩트하게.
     ② 입력창(.chat-ib) 안 보임 — flex 자식의 기본 `min-height:auto` 때문에 `.chat-main`/
        `.chat-msgs`가 콘텐츠만큼 부풀어 세로 합이 100%를 초과 → `.page{overflow:hidden}`이
        하단(입력창)을 잘라냄. 메시지가 쌓일수록 재현. → min-height:0 체인으로 축소 허용
        (스크롤은 .chat-msgs가 담당·입력창은 flex-shrink:0으로 항상 하단 고정). */
  .chat-wrap{flex-direction:column;min-height:0}
  .chat-lp{width:100%;height:auto;max-height:38vh;flex:0 0 auto;min-height:0;border-right:none;border-bottom:1px solid var(--w-border)}
  .chat-lh{padding:10px 12px;gap:6px}
  .chat-main{flex:1;min-height:0}
  #chat-act,#chat-digest{min-height:0}
  .chat-msgs{min-height:0}
  .chat-msgs .cm{max-width:82%}
  /* 모달은 작은 화면에서 가득 차게 */
  .mc{max-width:100%;max-height:92vh;border-radius:14px}
  .mo{padding:8px}
  /* 알림 배너 + 모바일 헤더 겹침 방지 */
  .alert-banner{position:sticky;top:0;height:auto;padding:6px 12px}

  /* ── 본문 컨테이너 — 줌/스크롤 페인트 격리 ── */
  .ctrl-body{
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    overflow-anchor: none;       /* 행 추가/제거 시 스크롤 점프 방지 */
    touch-action: pan-y;         /* 가로 pan은 본문이 안 먹게 (행 swipe 등) */
  }
}

/* ── 480px 이하 (스마트폰 세로) — 2행 grid 동일, 약간 축소 ── */
@media(max-width:480px){
  :root{--sw:220px}
  /* 480px: flex·M1 카드구조 유지(768 상속), padding/radius/margin·폭만 비례 축소
     (구조 동일 — 명세). border/border-left/background는 768 블록·base에서 cascade. */
  .dr{column-gap:4px;padding:10px 10px 8px;border-radius:12px;margin:0 0 7px}
  .dr>.col-time{flex:0 0 54px}
  .dr>.col-dep{flex:0 0 52px}
  .dr>.col-info{flex:1 1 auto}                    /* R1: 768 계승 — 이름/노선이 1행 남은 폭 차지 */
  .dr>.col-status{flex:1 0 100%}                  /* R1: 768 계승 — 5단계 독립 전체폭 줄(신호중심) */
  /* ★ 480px도 동일: content-visibility 제거 후 구식 min-height:85+intrinsic 제거, 자연높이. */
  .ctrl-body.dn-c .dr,.ctrl-body.dn-n .dr{min-height:0}
  .time-alarm,.ctrl-body.dn-c .time-alarm,.ctrl-body.dn-n .time-alarm{font-size:14px;padding:3px 6px}
  .col-dep .dep-time,.col-dep .dep-dep,.ctrl-body.dn-c .dep-time,.ctrl-body.dn-c .dep-dep{font-size:10.5px}
  .col-dep .dep-time::before,.col-dep .dep-dep::before{font-size:9px;margin-right:1px}
  .info-name,.ctrl-body.dn-c .info-name,.ctrl-body.dn-n .info-name{font-size:13.5px}
  .info-route-text,.ctrl-body.dn-c .info-route-text,.ctrl-body.dn-n .info-route-text{font-size:10.5px}
  /* 5-stage — R1: 768 독립줄 신호중심 비례 축소(자기 전체폭 줄이라 480도 시원하게).
     R2-A (2026-05-19): 768 신호밴드 최대화의 480 비례판 — height 38→56·
     dot 17→24(글리프 9.5→12)·lbl 11.5→11·padding 3→4. ::before숨김 계승.
     R3 (2026-05-19): GPS :nth-child(4) flex 가중 1.8→1 해제(768과 동일,
     5칸 균등 — 사용자 명시 결정, 재가중 금지·회귀 아님). */
  .status-panel,.ctrl-body.dn-c .status-panel{height:56px;padding:4px 7px;gap:2px}
  .cn,.ctrl-body.dn-c .cn{flex:1 1 0;min-width:0;height:46px;gap:2px}
  .status-panel .cp-row .cn:nth-child(4){flex:1 1 0}
  .cn .cn-lbl{font-size:11px;font-weight:900;letter-spacing:0}
  .cn-dot,.ctrl-body.dn-c .cn-dot{width:24px;height:24px;font-size:12px}
  /* 액션 버튼 — M2 라벨형 유지(768 상속), R1 균형비율 계승 비례 축소.
     R2-B (2026-05-19): 768 얇은 띠의 480 비례판 — rb2/아이콘 35→28·height
     36→28, 전화 flex 1.7→1.5, gap 5(768 계승). 7버튼 도달·라벨 보존. */
  .rb2,.ctrl-body.dn-c .rb2,.ctrl-body.dn-n .rb2{flex:0 0 28px;height:28px;font-size:11px;border-radius:var(--w-radius-8)}
  .rb2-call,.ctrl-body.dn-c .rb2-call,.ctrl-body.dn-n .rb2-call{flex:1.5 1 0;height:28px;font-size:11.5px;padding:0 7px}
  .rb2-mgr,.ctrl-body.dn-c .rb2-mgr,.ctrl-body.dn-n .rb2-mgr,
  .rb2-chat,.ctrl-body.dn-c .rb2-chat,.ctrl-body.dn-n .rb2-chat{flex:1 1 0;padding:0 7px}
  .rb2-emerg,.ctrl-body.dn-c .rb2-emerg,.ctrl-body.dn-n .rb2-emerg,
  .rb2-note,.ctrl-body.dn-c .rb2-note,.ctrl-body.dn-n .rb2-note,
  .rb2-excl,.ctrl-body.dn-c .rb2-excl,.ctrl-body.dn-n .rb2-excl,
  .rb2-push,.ctrl-body.dn-c .rb2-push,.ctrl-body.dn-n .rb2-push{flex:0 0 28px}
  .col-actions{gap:5px}
  .step{height:24px;min-width:54px;padding:0 8px;font-size:10px}
  .sp-main{font-size:11px}.sp-sub{font-size:10px}
  .si{padding:4px 9px}
  .si-num{font-size:15px}
  .si-lbl{font-size:9px}
  .grp-hdr{padding:0 10px}                                /* 2026-07-27 3차: 모바일도 16px(세로 패딩 0) */
  .gh-badges{gap:3px}
  .gh-pill{padding:0 5px;font-size:8px;line-height:12px}
  .gh-time{font-size:11px;line-height:14px}
  .pin-tray{padding:5px 8px}
}

/* ── 사용자 모션 감소 선호 — 깜빡임/애니메이션 최소화 ── */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;
  }
  .risk-board.rb-crit,.dr-cc,.status-panel.sp-crit,.rb2-urgent,.gh-tag-curr,
  .step-active-watch,.step-active-warn,.step-active-crit,.step-active-move,
  .status-panel.sp-crit::before,.risk-board.rb-crit::before,.dr-cc::before{animation:none!important;opacity:0!important}
  .risk-board.rb-crit .rb-track{animation:none!important}
}

/* ── 모바일 페인트 절감 — 동시 진행 애니메이션 일부 단순화 ──
   원칙: 모바일에선 깜빡임 ALL OFF, 시인성은 정적 빨강 채움 + 작은 정적 글로우로 대체.
   이유: rAF 디바운스로 DOM이 자주 새로 생성돼 애니메이션 phase가 reset → "불규칙 깜빡임"으로 보임.
        box-shadow + transform: scale 동시 애니메이션은 모바일 스크롤 시 페인트 폭주(특히 다수 critical 행). */
@media(max-width:768px){
  .dr-cc{animation:none}
  /* ★ critical 행 ::before opacity 펄스도 모바일 줌/스크롤 시 페인트 폭주 원인 → OFF.
     시인성은 빨강 컬러바(border-left) + 알람시각 빨강 채움 + 전화버튼 깜빡임으로 충분 */
  .dr-cc::before{animation:none;opacity:.35}
  .status-panel.sp-crit{animation:none;box-shadow:none}
  .status-panel.sp-crit::before{animation:none;opacity:0}    /* 모바일에선 새 ::before 펄스도 OFF (페인트 절감) */
  .risk-board.rb-crit::before{animation:none;opacity:0}      /* 위험 전광판 ::before 펄스도 모바일 OFF */
  /* 그룹 헤더 깜빡임 OFF (모바일 sticky 페인트 절감) */
  .gh-tag-curr{animation:none}
  /* 지도뷰 driver pin: 터치 디바이스 hover 비활성 + lk scale 축소 */
  .dp-dot:hover{transform:none}
  .dp-label:hover{transform:none;box-shadow:0 3px 12px rgba(15,23,42,.28)}
  .dp-wrap.lk .dp-dot{transform:scale(1.18)}
  .dp-wrap.lk .dp-label{transform:translateY(-1px)}
  .dp-dot::after{animation:none}   /* 모바일은 펄스 OFF (페인트 비용 절감) */
  /* 추적 카드 hover translateX 모바일 OFF (스크롤/줌 시 페인트 절감) */
  .trk-card:hover{transform:none;background:#fff;border-color:var(--bdr)}
  .trk-card.lk:hover{background:#EFF6FF;border-color:var(--cb)}
  /* 핀 칩 hover transform 모바일 OFF */
  .pin-chip:hover{transform:none;box-shadow:none}
  /* 위험 전광판 hover filter 모바일 OFF (sticky 영역에서 페인트 폭주 원인) */
  .risk-board:hover{filter:none}
  /* ★ 전화 버튼 — 부드러운 깜빡임 유지 (opacity-only, GPU 레이어 승격으로 스크롤 폭주 방지)
     기존 phone-urgent는 box-shadow+transform+opacity 동시 변경 → 모바일 페인트 폭주.
     opacity만 변경하면 페인트 비용 0 (GPU 합성). translateZ(0)로 자체 레이어 분리해 스크롤 시 재페인트 X. */
  .rb2-urgent{
    animation:phone-soft 1.2s ease-in-out infinite!important;
    box-shadow:0 0 10px rgba(239,68,68,.6)!important;
    transform:translateZ(0);
    will-change:opacity;
  }
  /* 사이드바 위험 전광판도 모바일에선 정적 빨강 (rb-crit-flash OFF, 마퀴는 유지) */
  .risk-board.rb-crit{animation:none}
  /* time-alarm critical 빨강은 정적이므로 OK, 그림자만 줄여 페인트 부담 ↓ */
  .dr-cc .time-alarm{box-shadow:none!important}
}

/* 모바일용 부드러운 깜빡임 (opacity만, 페인트 비용 0) */
@keyframes phone-soft{0%,100%{opacity:1}50%{opacity:.5}}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes spin-in{from{opacity:0;transform:scale(.5) rotate(-20deg)}to{opacity:1;transform:none}}
@keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
@keyframes dot-blink{0%,80%,100%{opacity:.3}40%{opacity:1}}
@keyframes pip-blink{0%,100%{opacity:1}50%{opacity:.25}}
@keyframes lamp-pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.95)}}

/* ════════════════════════════════════════════════════════════════════════
   Fold simple 모드 (2026-05-26) — Samsung Galaxy Fold paint 부담 감축
   ─ UA 감지 IIFE(body 시작 직후 일반 script)가 body에 .fold-simple 추가
   ─ 모든 룰이 body.fold-simple 셀렉터 한정 → 다른 기기 0 영향
   ─ Fold compositor 한도 초과 paint 폭주 회피(backdrop-filter/animation/
     shadow/will-change OFF + 시스템 폰트 폴백 + 사이드바 오버레이 차단)
   ─ ?fold=off URL 강제 해제 가능
   ════════════════════════════════════════════════════════════════════════ */

/* 전역 transition/animation 차단 (광범위·안전) */
body.fold-simple,
body.fold-simple *{
  transition:none !important;
  animation:none !important;
  will-change:auto !important;
}

/* backdrop-filter는 폴더블 Chrome layer 폭주 주범 */
body.fold-simple .sidebar,
body.fold-simple .risk-board,
body.fold-simple .mc,
body.fold-simple .map-leg,
body.fold-simple .map-cnt-b,
body.fold-simple .toast,
body.fold-simple .mhdr,
body.fold-simple .sb-clk{
  backdrop-filter:none !important;
  -webkit-backdrop-filter:none !important;
}

/* 그림자/엘리베이션 단순화 (layer 감소) */
body.fold-simple .sidebar,
body.fold-simple .mc,
body.fold-simple .map-wrap,
body.fold-simple .status-panel,
body.fold-simple .si,
body.fold-simple .trk-pan,
body.fold-simple .map-leg{
  box-shadow:none !important;
}

/* 펄스 강제 OFF (issues-perf 정책 더 강화 — Fold만) */
body.fold-simple .cp-pulse,
body.fold-simple .rb2-urgent,
body.fold-simple .dr-cc::before,
body.fold-simple .sp-crit::before,
body.fold-simple .rb-crit::before,
body.fold-simple .flag-crit{
  animation:none !important;
  box-shadow:none !important;
}

/* 사이드바 오버레이 자체 차단 (햄버거 먹통 회귀 동반 해소 — issues-misc) */
body.fold-simple .sb-ovl{
  display:none !important;
}

/* will-change/transform layer hint 제거. issues-perf에 박힌 'paint contain
   금지'는 그대로 — contain:layout style만 추가(paint 미포함) */
body.fold-simple .dr,
body.fold-simple .ctrl-th,
body.fold-simple .grp-hdr,
body.fold-simple .status-panel{
  contain:layout style !important;
}

/* Pretendard CDN 폰트 load 부담 감축 — 시스템 폰트로 폴백 */
body.fold-simple,
body.fold-simple *{
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif !important;
}

/* ════════════════════════════════════════════════════════════════════════
   ★★ 모바일 페인트 감축 (2026-07-14) — "스크롤 시 하단 흰 화면" 정식 fix
   ────────────────────────────────────────────────────────────────────────
   [근본원인 — 측정으로 확정, 추측 아님]
   일반 안드로이드(360×641, 폴더블 아님)에서 관제 목록을 스크롤하면 아랫부분이 하얗게 됨.
   ?diag=1 실측: gap>120 = 0(그룹헤더 1건뿐)·drCount 85·drLastBottom+scrollTop ≈ ctrlScrollH
   ·jsErr 0 → **DOM·레이아웃은 완전 정상, 순수 래스터(paint) 실패**.
   스크롤 컨테이너가 15,564px로 긴데 그 위에 backdrop-filter·box-shadow·펄스 애니메이션이
   얹혀 있어 Chrome Android가 타일 래스터를 놓치고 레이어 배경(#fff)이 드러남.
   ★ 사용자 A/B 확정: `?fold=on`(=페인트 감축 모드)로 접속하면 **흰 화면이 사라짐**.
     → 폴드 전용으로 묶여 있던 페인트 감축을 **모든 좁은 모바일에 정식 적용**한다.

   [fold-simple에서 가져오지 않은 2가지 — 의도적 제외]
   ① `.sb-ovl{display:none}` — 모바일 햄버거 오버레이(딤·탭하면 닫기)가 죽는다. 유지.
   ② 시스템 폰트 강제 — 폰트는 래스터 실패 원인이 아니고 브랜드 룩이 깨진다. 유지.

   ★ 안티-회귀: content-visibility·contain:paint 는 여기서도 절대 도입 금지(8회 실패 영역).
     contain은 layout style 까지만(paint 미포함). (→ issues-perf.md)
   ════════════════════════════════════════════════════════════════════════ */
@media(max-width:768px){
  /* 전역 transition/animation/will-change 차단 — 합성 레이어 생성 억제 */
  body, body *{
    transition:none !important;
    animation:none !important;
    will-change:auto !important;
  }
  /* backdrop-filter = Chrome layer 폭주 주범
     ★ [2026-07-14 추가] `.mo`(모달 딤) 가 이 목록에서 **빠져 있었다**. `.mo` 는
     `position:fixed;inset:0` + `backdrop-filter:blur(4px)` = 뷰포트 전체를 덮는 fixed 레이어이고,
     backdrop-filter 는 "뒤에 있는 것"(= 거대 고정 셸)을 스냅샷해 블러하므로, 그 배경 레이어의
     래스터가 실패하면 딤도 그 지점부터 안 칠해진다. 실제로 Fold 7 펼침에서 딤이 화면 위쪽
     ~1/3 까지만 칠해지고 끊겼다(팝업 카드 `.mc` 는 자기 배경이 있어 정상). 딤은 반투명 단색만으로
     충분하므로 모바일에선 블러를 끈다. */
  .sidebar, .risk-board, .mc, .mo, .map-leg, .map-cnt-b, .toast, .mhdr, .sb-clk{
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
  }
  /* 그림자/엘리베이션 제거 (레이어 감소) */
  .sidebar, .mc, .map-wrap, .status-panel, .si, .trk-pan, .map-leg{
    box-shadow:none !important;
  }
  /* 펄스 강제 OFF (모바일 한정 — 데스크톱 위험 펄스는 그대로) */
  .cp-pulse, .rb2-urgent, .dr-cc::before, .sp-crit::before, .rb-crit::before, .flag-crit{
    animation:none !important;
    box-shadow:none !important;
  }
  /* ★ .rb2-urgent 의 translateZ(0) 자체 레이어 분리도 제거.
     원래 목적은 "opacity 펄스를 GPU 합성으로 싸게 돌리기"였는데 위에서 펄스를 껐으므로
     레이어만 남는다. 관제 행이 161개면 긴급 버튼 수만큼 합성 레이어가 쌓여 페인트 예산을 먹는다. */
  .rb2-urgent{ transform:none !important; }
  /* layer hint 제거 + 격리(paint 미포함) */
  .dr, .ctrl-th, .grp-hdr, .status-panel{
    contain:layout style !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   ★★★ 모바일 문서 스크롤 셸 (2026-07-14) — "앱형 fixed 셸" 해체
   ────────────────────────────────────────────────────────────────────────
   [근본원인 — 전부 실측, 추측 아님]
   Fold 7 펼침(innerW 749 · innerH 687 · dpr 2.63 = 실픽셀 1971×1807)에서 화면 아래쪽이
   하얗게 남는다. ?diag=1 실측: gap>120=0 · jsErr 0 · 행 높이 합 == scrollHeight
   → **레이아웃·DOM 완전 정상, 순수 래스터(paint) 실패**.
   ★ 결정적 단서: 목록뿐 아니라 **모달 딤(`position:fixed;inset:0`)도 위쪽 ~1/3 까지만 칠해진다**.
     팝업 카드(.mc)는 그 경계를 넘어 정상. 목록을 1행만 남겨도 동일(= 콘텐츠 양과 무관).
     Chrome·삼성 인터넷 둘 다 재현(둘 다 Blink) → 브라우저 앱 문제 아님.
   ⇒ **화면 전체를 덮는 거대 `position:fixed` 레이어를 이 해상도에서 Blink가 끝까지
      래스터하지 못한다.** 콘텐츠 양·레이어 높이 문제가 아니라 *fixed 레이어 자체*가 문제.

   [기존 구조 — 이것이 그 거대 fixed 레이어다]
     html,body{height:100%;overflow:hidden}      ← 문서는 스크롤하지 않음
     .screen{position:fixed;inset:0}             ← 화면 전체 = 하나의 고정 레이어
       └ 사이드바 + main-area … 스크롤은 안쪽 .ctrl-body{overflow-y:auto} 가 담당

   [해법] 모바일(≤768px) 한정으로 이 셸을 해체해 **body(문서)가 스크롤하는 평범한 웹페이지**로
   되돌린다. 거대 fixed 레이어가 사라지면 Chrome 이 일반 문서 타일 래스터 경로를 타므로
   이 벽을 원천 회피한다. 스크롤 주체가 `.ctrl-body` → `window` 로 바뀌므로 JS 도 분기한다
   (getCtrlScroll/setCtrlScroll · switchTab 스크롤 리셋 · renderPinTray --pin-h).

   ★ 데스크톱(≥769px)은 기존 fixed 셸 그대로 — 이 블록은 전부 @media(max-width:768px) 안이다.
     PC 관제가 주력 운영 화면이라 회귀 절대 금지.
   ★ 안티-회귀: 여기서 다시 화면 전체를 덮는 fixed 레이어(전면 오버레이·풀스크린 래퍼)를 만들지 말 것.
     작은 fixed(사이드바 240px·토스트·모달 카드)는 무해하지만 **뷰포트를 통째로 덮는 fixed + 그 위의
     backdrop-filter** 조합이 이 버그의 트리거다. (→ issues-perf.md)
   ★ 안티-회귀: content-visibility·contain:paint 는 여기서도 절대 도입 금지(8회 실패 영역).
   ════════════════════════════════════════════════════════════════════════ */
@media(max-width:768px){
  /* ── 1. 셸 해체 — 문서(body)가 스크롤 주체 ── */
  /* ★ 함정: 여기에 `overflow-x:hidden` 을 붙이면 안 된다. 한 축이 visible 이 아니면 CSS 규칙상
     나머지 축이 auto 로 계산돼 **body 자체가 스크롤 컨테이너**가 되고, 그러면 body 안의
     position:sticky(모바일 헤더·핀트레이·그룹헤더)가 "절대 스크롤되지 않는 body"를 기준으로
     삼아 **영원히 붙지 않는다**(헤드리스 스크린샷으로 실측 확인 — computed style 은 sticky 인데
     rect 는 안 붙음). 가로 넘침은 sum-strip/ctrl-bar 가 각자 overflow-x:auto 로 처리하고
     행(.dr)은 flex-wrap 이라 애초에 안 넘친다. */
  html,body{height:auto;min-height:100%;overflow:visible}
  /* 전면 fixed 그라데이션 성운(body::before)도 뷰포트 전체 fixed 레이어 → 모바일 제거(장식). */
  body::before{display:none}
  /* .screen 은 3개(로딩·로그인·앱)가 겹쳐 있는 fixed 스택이다. 앱 화면만 문서 흐름으로 내리고,
     static 이 되면 비활성 화면이 흐름을 차지하므로 비활성일 때는 display:none 으로 확실히 뺀다.
     (로딩·로그인 화면은 뷰포트에 들어가는 작은 카드라 기존 fixed 유지 — 스크롤 불필요.) */
  #scr-app{position:static;inset:auto;display:block;height:auto;min-height:100vh;z-index:auto}
  #scr-app:not(.active){display:none}
  .app-wrap{height:auto;min-height:100vh}
  .main-area{height:auto;overflow:visible}
  .main-cnt{overflow:visible}
  .page{height:auto;overflow:visible}
  /* 이 3개는 자기 자신이 overflow-y:auto 스크롤러였다 → 문서 스크롤로 통합(내용이 길어 오히려 자연스럽다) */
  #page-manage,#page-report,#page-board{overflow:visible}

  /* ── 2. 관제 목록 — 자체 스크롤 해제 → 문서와 함께 흐름 ──
     base `.ctrl-body{flex:1;overflow-y:auto;min-height:0}` 를 무력화. scrollbar-gutter·
     overscroll-behavior 는 스크롤 컨테이너가 아니게 되면 자동 무효라 건드리지 않는다(불변식 보존). */
  .ctrl-body{flex:0 0 auto;height:auto;max-height:none;overflow:visible}

  /* ── 3. 고정 UI — sticky (작은 레이어라 무해) ──
     z-index 질서: grp-hdr 5 < pin-tray 6 < mhdr 14 < sb-ovl 15 < sidebar 25.
     mhdr 를 사이드바(25)보다 낮게 둬야 햄버거로 연 사이드바가 헤더 위로 덮인다. */
  .mhdr{position:sticky;top:0;z-index:14}
  /* ★ 2026-07-27 게시판 요청(노영화) — "모바일 관제현황에서 상단 신호바가 스크롤이 내려가더라도 고정되었으면
     좋겠다(pc버전에서는 현재 작동중)". PC 는 앱형 fixed 셸이라 요약 스트립이 원래 안 밀리는데,
     모바일은 셸을 해체해 문서가 스크롤 주체라 같이 밀려 올라갔다 → 요약 스트립만 sticky 로.
     z 질서에 13 을 끼운다: grp-hdr 5 < pin-tray 6 < **sum-strip 13** < mhdr 14 < sb-ovl 15 < sidebar 25.
     ★ --sum-h 는 syncSumH() 가 실측해 설정(칩 줄바꿈·가로회전 대응). 아래 pin-tray·grp-hdr·
       scroll-margin-top 이 전부 이 값을 더해 붙으므로 고정 매직넘버를 쓰지 말 것. */
  .sum-strip{position:sticky;top:50px;z-index:13}
  .pin-tray{position:sticky;top:calc(50px + var(--sum-h,0px));z-index:6}   /* 차고지 미실시 칩 — 스크롤해도 계속 보이게(원 의도 보존) */
  /* 그룹헤더는 mhdr + 요약바 + (있을 때) 핀트레이 아래에 붙는다. --pin-h 는 renderPinTray 가 실측해 설정
     (핀트레이는 비면 display:none → 0px). 고정 매직넘버를 쓰면 핀트레이 유무에 따라 어긋난다. */
  .grp-hdr{top:calc(50px + var(--sum-h,0px) + var(--pin-h,0px))}
  /* scrollIntoView(jumpToDriver·자동스크롤·scrollToCritical)가 sticky 바 밑으로 파묻히지 않게 여백 확보.
     scrollIntoView 는 문서/컨테이너 양쪽에서 그대로 동작하므로 JS 분기가 필요 없다 — 이 CSS 한 줄이면 된다. */
  .dr,.grp-hdr{scroll-margin-top:calc(58px + var(--sum-h,0px) + var(--pin-h,0px))}

  /* ── 4. 채팅만 뷰포트 고정 박스 유지 ──
     채팅은 "입력창이 항상 하단"이 핵심(2026-07-13 min-height:0 체인 fix). 문서 스크롤로 풀면
     입력창이 메시지 목록 끝(문서 저 아래)으로 밀려 못 쓴다 → 뷰포트 높이 박스 + 내부 스크롤 유지.
     ★ position:fixed 가 아니라 **흐름 안의 100vh 블록**이라 거대 fixed 레이어를 만들지 않는다.
     지도(#page-map)는 이미 모바일에서 `overflow:auto` + `.trk-pan{max-height:230px}` 라
     문서 스크롤로 내려도 트랙 패널이 자체 스크롤을 유지한다 → 별도 처리 불필요. */
  #page-chat{height:calc(100vh - 50px);overflow:hidden}

  /* ── 5. 모달 열림 = 배경 문서 스크롤 잠금 ──
     문서가 스크롤 주체가 됐으므로 모달 뒤에서 본문이 같이 밀리는 것을 막는다.
     Chrome 은 body overflow:hidden 시 스크롤 위치를 보존하므로 닫으면 원위치. */
  body.mo-open{overflow:hidden}
}

/* ★ 2026-05-26 추가 (사용자 검증 후): 펼친 Fold(749px viewport) lazy paint 잔존 fix.
   전면(411px) `?fold=on`에서는 흰 영역 사라졌으나 펼친 모드에서는 잔존 → ctrl-body GPU layer 강제 분리로
   paint 캐시 + Chrome layer drop 회피. issues-perf 박힌 "행 단위 layer 승격 금지"는 .dr 한정이고
   .ctrl-body 컨테이너 1개 승격은 layer count +1만(안전). sticky .grp-hdr은 ctrl-body 안이라 동작 무변.
   Fold class 한정이라 다른 기기 0 영향.
   ★★ [근본원인 2026-07-14] 이 승격 룰에 **폭 가드가 없던 것**이 "스크롤 시 하단 흰 화면"의 근인.
   승격(단일 GPU 레이어)은 아래 가상 스크롤(.dr.vh-off)과 **짝**으로만 성립한다 — 가상화가 레이어를
   ~6,600px로 줄여주기 때문. 그런데 가상 스크롤은 `innerW>=500` 가드가 있고 이 승격은 없어서,
   **innerW<500 + fold-simple**(예: Z Flip 세로 412px, 또는 `?fold=on` 잔재가 남은 폰)이면
   "승격 O / 가상화 X" = 160행이 통째로 한 레이어(실측 scrollHeight ≈ 25,000px)에 남는다.
   모바일 GPU max texture(통상 4096~8192px)를 초과 → 타일 래스터 실패 → 레이어 배경(#fff)이
   그대로 드러남 = 하단 흰 화면. will-change:transform이 레이어를 고정해 일반 래스터 폴백도 막는다.
   → 승격을 가상 스크롤과 **동일한 폭 가드(min-width:500px)** 안에 넣어 둘을 항상 짝으로 만든다.
   원 의도(펼친 Fold 749px) 보존 — 749>=500이라 승격+가상화 둘 다 그대로 켜진다.
   ★ 안티-회귀: 이 미디어쿼리 밖으로 승격을 다시 꺼내지 말 것. 가상화를 <500px로 넓히는 방향도 금지
     (base .dr content-visibility 8회 실패 영역으로 되돌아감). (→ issues-perf.md) */
/* ★★★ [최종 결론 2026-07-14] 이 승격 룰은 **완전 제거**한다 (아래는 이력·안티-회귀 가드).

   2026-07-14 1차 fix에서 `@media(min-width:500px)`로 가드를 붙였으나, Fold 7 펼침(749px)에서
   흰 화면이 잔존. ?diag=1 실측(Fold 7 펼침): innerW 749 · drCount 161 · **ctrlScrollH 28,619px**
   · gap>120=0(그룹헤더 1건) · jsErr 0 → 레이아웃 정상, 순수 래스터 실패.

   근인: 승격된 `.ctrl-body`가 **28,619px짜리 단일 합성 레이어**가 된다. 모바일 GPU max texture는
   통상 4096~8192px → 초과분 타일 래스터 실패 → 레이어 배경(#fff) 노출 = 흰 화면.
   ★ 가상 스크롤(.dr.vh-off)은 **페인트를 건너뛸 뿐 레이어 높이를 줄이지 않는다** —
     `contain-intrinsic-size:auto`가 한 번 렌더된 실제 높이를 기억하기 때문(실측 28,619px 그대로).
     즉 "승격 + 가상화" 짝이 성립한다는 1차 가정 자체가 틀렸다.

   대체 수단: 모바일 페인트 감축(@media max-width:768px — 749px 포함)이 backdrop-filter·
   box-shadow·animation·will-change를 끄므로, 승격이 풀려던 lazy paint 문제는 그쪽이 담당한다.

   ★ 안티-회귀: `.ctrl-body`에 transform/translate3d/will-change/backface-visibility 로
     레이어를 강제 승격하지 말 것. 긴 스크롤 컨테이너(수만 px)를 단일 레이어로 만들면
     GPU 텍스처 한도를 넘어 흰 화면이 재발한다. 페인트가 병목이면 감축(위 모바일 블록)으로
     풀고, 그래도 부족하면 *제대로 된 JS 가상화*(DOM에서 행 제거)로 스크롤 높이 자체를 줄여야 한다.
     (→ issues-perf.md) */

/* ★ 2026-05-26 추가 — Fold simple + 펼침(innerW>=500) 한정 가상 스크롤 vh-off 룰
   ─ IntersectionObserver로 viewport 안 + buffer 300px 안만 visible 유지
   ─ off-screen 행은 .vh-off 토글로 content-visibility:hidden + 측정 동적 높이 spacer
   ─ Paint 부담 .dr×98 → ~15개로 1/6 감축 (Chrome on Fold compositor 한계 회피)
   ─ ★ issues-perf 박힌 base .dr content-visibility 금지(8회 실패)는 그대로 — 본 룰은:
     ① body.fold-simple 한정(다른 기기 0 영향)
     ② 펼침 innerW>=500 한정(전면 411px 미적용)
     ③ .dr.vh-off 동적 토글(visible 확정된 행은 normal·off-screen만 hidden)
     ④ measured intrinsic-size(8회 실패의 90/85 매직넘버 회피)
   ─ --dr-avg-h CSS variable은 IIFE가 첫 5개 .dr 측정 후 동적 설정 */
/* ★★★ [최종 2026-07-14] 이 룰은 **제거**했다 — Fold 7 펼침 흰 화면의 근인.
   화면에 들어온 행의 .vh-off 가 제때 안 벗겨지면 content-visibility:hidden 때문에
   그 행은 아무것도 그려지지 않는다(= 흰 화면). 짝이 되는 foldVirtualScroll IIFE 도
   isActive(){return false} 로 영구 비활성화했다.
   ★ 안티-회귀: base .dr 든 .dr.vh-off 든 **어떤 형태로도 content-visibility 재도입 금지**
     (issues-perf 8회 실패 + 본 건). 대체 수단 = 모바일 페인트 감축(@media max-width:768px).
   구 룰(참고): body.fold-simple .dr.vh-off{ content-visibility:hidden; contain-intrinsic-size:auto var(--dr-avg-h,42px) } */

/* 2026-05-26 폭 460px 강제는 사용자 거부(외관 비채움) → 제거.
   펼친 Fold(749px viewport)에서 paint 영역 감축은 사용성과 양립 불가 — 풀스크린 유지 필수.
   추가 fix는 paint 부담 감축으로만 — 또는 환경 한계로 인정 후 펼침 미지원 명시. */

/* Fold 단순 모드 활성 배지 — 우상단 작은 텍스트, 클릭 시 ?fold=off */
.fold-mode-indicator{
  position:fixed;
  top:4px;
  right:8px;
  z-index:9999;
  font-size:10px;
  color:var(--w-t3);
  background:rgba(255,255,255,.85);
  padding:2px 6px;
  border:1px solid rgba(0,0,0,.08);
  border-radius:4px;
  cursor:pointer;
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  line-height:1.3;
  user-select:none;
}
.fold-mode-indicator:hover{background:rgba(255,255,255,.95)}

