/*
  색은 지어내지 않고 app/src/main/assets/map_style.json 에서 가져왔다.
  지도가 화면의 대부분이므로 UI 가 지도와 다른 색 체계를 쓰면 두 개의 앱처럼 보인다.

    #14161a  지도 배경        → --map
    #0d0f12  글자 후광        → --ground (지도보다 어두워야 패널이 지도 '위'로 보인다)
    #dde3ec  도시 이름        → --ink
    #9aa6b8  도 이름          → --muted
    #2a2f37  이면도로 선      → --line

  지도 전체가 차가운 청회색이라 따뜻한 계열 하나만 넣어도 크게 튄다.
  그래서 강조색은 한 곳에만 쓴다 — 과속 카메라.
*/
/*
  ⚠️ 테마는 **토큰에서만** 갈린다. 컴포넌트 규칙 안에서 색을 직접 쓰거나 미디어 쿼리로
  다시 칠하면, 나중에 한쪽 테마에서만 색이 어긋나는 자리가 생긴다.
  색이 필요하면 여기 변수를 늘리고 컴포넌트는 변수만 본다.

  루트의 data-theme 은 <head> 의 인라인 스크립트가 첫 페인트 전에 정한다.
*/
:root,
:root[data-theme="dark"] {
  --map:    #14161a;
  --ground: #0d0f12;
  --panel:  #171a20;
  --line:   #2a2f37;
  --ink:    #dde3ec;
  --muted:  #9aa6b8;
  --dim:    #6f7a8a;
  --hover:  #1e222a;
  --btn:    #222731;
  --btn-hi: #2a303b;

  --cam:    #f0a742;  /* 과속·신호 — 유일한 따뜻한 색 */
  --sec:    #e2703a;  /* 구간단속 */
  --park:   #5ec8e0;  /* 주정차 — 차가운 쪽이라 지도에 녹지 않을 만큼만 밝게 */
  /* 방지턱 — 단속이 아니므로 경고색(주황·빨강)을 쓰지 않는다.
     ⚠️ 처음에 회색(#8fa3b8)을 썼더니 **배경 지도에 묻혔다.** 화면에 6~7개가 그려져
     있는데도 눈에 띄지 않았다(스크린샷 픽셀을 세어 확인). 있는데 안 보이면 없는 것과
     같다. 경고로 읽히지 않으면서 배경과는 갈리는 낮은 채도의 보라로 바꿨다. */
  --bump:   #b09bd8;
  --soon-bd: #3a3020;
  --soon-bg: rgb(240 167 66 / .07);
  --soon-fg: #e8c88c;
  --chrome:  rgb(23 26 32 / .92);   /* 지도 위에 떠 있는 것들 */
  --scrim:   rgb(13 15 18 / .97);
  --ctrl-invert: invert(85%);       /* MapLibre 기본 아이콘용 */

  --shadow: 0 6px 24px rgb(0 0 0 / .45);
}

:root[data-theme="light"] {
  --map:    #eef1f5;
  --ground: #ffffff;
  --panel:  #f6f8fa;
  --line:   #e0e5eb;
  --ink:    #131920;
  --muted:  #48525f;
  --dim:    #7b8492;
  --hover:  #eef1f5;
  --btn:    #eef1f5;
  --btn-hi: #e2e7ee;

  /* 밝은 바탕에서는 같은 채도라도 훨씬 옅게 보인다. 점이 배경에 녹지 않도록
     둘 다 어둡게 내린다 — 특히 주정차의 하늘색이 흰 바탕에서 잘 사라진다. */
  --cam:    #c9781a;
  --sec:    #b8471a;
  --park:   #12799a;
  --bump:   #6b53a8;
  --soon-bd: #e8d3a8;
  --soon-bg: rgb(201 120 26 / .08);
  --soon-fg: #7a4a08;
  --chrome:  rgb(255 255 255 / .94);
  --scrim:   rgb(255 255 255 / .97);
  --ctrl-invert: none;

  --shadow: 0 6px 24px rgb(20 30 50 / .14);
}

:root {
  --r: 10px;
  --sans: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Pretendard",
          "Malgun Gothic", "Noto Sans KR", system-ui, sans-serif;
  color-scheme: dark;
}
:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--map);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;
}

#map { position: fixed; inset: 0; }

/* ── 상단 바 ───────────────────────────────────────────── */
.bar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: max(10px, env(safe-area-inset-top)) 14px 10px;
  background: linear-gradient(to bottom, var(--ground) 55%, transparent);
  pointer-events: none;
}
.bar > * { pointer-events: auto; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ink);
  text-decoration: none;
  font-weight: 700;
  letter-spacing: -.01em;
}
.brand .mark {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--cam);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cam) 20%, transparent);
}
.brand .name { font-size: 16px; }

.counts { display: flex; gap: 14px; margin: 0; }
.counts div { text-align: right; }
.counts dt { font-size: 10px; color: var(--dim); letter-spacing: .04em; }
.counts dd {
  margin: 1px 0 0;
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

/* ── 화면 밝기 ─────────────────────────────────────────── */
.theme {
  flex: none;
  width: 32px; height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--chrome);
  color: var(--muted);
  font: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.theme:hover { color: var(--ink); border-color: color-mix(in srgb, var(--muted) 45%, transparent); }

/* ── 범례 겸 토글 ──────────────────────────────────────── */
/* ⚠️ 지도 버튼과 **같은 높이 기준**을 쓴다. 예전에는 여기에 58px 를 따로 적어
   두어서, 주행 패널이 그 위에 앉아 색 안내가 통째로 가려져 있었다(형 제보).
   바닥에 붙는 것은 하나도 빠짐없이 --ctrl-lift 를 따라야 한다 — 값을 각자
   적어두면 패널 높이가 바뀔 때 한 곳을 반드시 잊는다. */
.legend {
  position: fixed;
  left: 12px;
  bottom: var(--ctrl-lift, calc(58px + env(safe-area-inset-bottom)));
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
}
.key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--chrome);
  backdrop-filter: blur(8px);
  color: var(--dim);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  transition: color .15s, border-color .15s, opacity .15s;
}
.key.on { color: var(--ink); border-color: color-mix(in srgb, var(--muted) 45%, transparent); }
.key:not(.on) .dot { opacity: .25; }
.key:hover { border-color: color-mix(in srgb, var(--muted) 55%, transparent); }
.dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.dot.cam  { background: var(--cam); }
.dot.park { background: var(--park); }
.dot.bump { background: var(--bump); }
/* 3D 는 종류가 아니라 보기 방식이라 색을 쓰지 않는다. 자리만 같게 둔다. */
.dot.flat { background: var(--muted); border-radius: 2px; }

/* 방지턱을 켰을 때만 뜨는 한계 안내. 범례 안에 있으므로 자리는 레이아웃이 정한다 —
   띄울 거리를 숫자로 적어두지 않는다(그 숫자는 범례가 자라는 날 반드시 틀린다). */
.caveat {
  order: -1;          /* 버튼 위로 */
  width: 100%;
  margin: 0;
  padding: 9px 12px;
  max-width: 30rem;
  box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--bump) 40%, transparent);
  border-radius: 10px;
  background: var(--chrome);
  backdrop-filter: blur(8px);
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.55;
}
.caveat strong { color: var(--ink); }

/* 주행 패널 안의 선택 항목. 손가락으로 누르는 자리라 넉넉히 잡는다. */
.opt {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 10px;
  padding: 8px 2px;
  min-height: 44px;
  color: var(--muted);
  font-size: 13px;
  cursor: pointer;
}
.opt input { width: 20px; height: 20px; flex: none; accent-color: var(--cam); cursor: pointer; }
.opt b { color: var(--dim); font-weight: 500; }

/* 길찾기 패널. 주행 패널과 같은 자리를 쓰므로 생김새를 맞춘다. */
.route-panel {
  margin-top: 10px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--chrome);
  backdrop-filter: blur(8px);
}
.route-panel[hidden] { display: none; }
.route-panel .tiny {
  margin: 10px 0 0;
  color: var(--dim);
  font-size: 11.5px;
  line-height: 1.5;
}
.route-panel .tiny b { color: var(--muted); font-weight: 600; }
/* ⚠️ display 를 지정했으므로 [hidden] 가드가 따라와야 한다. */
.caveat[hidden] { display: none; }

/* ── 검색 ─────────────────────────────────────────────── */
.find {
  position: fixed;
  /* 시트(5)보다 위여야 한다. 검색 결과가 열려 있는 동안은 그게 화면의 주인공이다. */
  z-index: 7;
  left: 12px; right: 12px;
  top: calc(52px + env(safe-area-inset-top));
  display: block;
}
.find input {
  width: 100%;
  padding: 11px 34px 11px 14px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--chrome);
  backdrop-filter: blur(8px);
  color: var(--ink);
  font: inherit;
  font-size: 13.5px;
  box-shadow: var(--shadow);
}
.find input::placeholder { color: var(--dim); }
.find input:focus { outline: 2px solid var(--cam); outline-offset: 1px; }
/* 사파리가 붙이는 기본 지우기 버튼을 없앤다. 우리 버튼과 두 개가 겹쳐 보인다. */
.find input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

.find .clear {
  position: absolute;
  top: 50%; right: 8px;
  transform: translateY(-50%);
  width: 26px; height: 26px;
  border: 0; border-radius: 7px;
  background: transparent;
  color: var(--dim);
  font-size: 12px;
  cursor: pointer;
}
.find .clear:hover { background: var(--hover); color: var(--ink); }
.find .clear[hidden] { display: none; }

.hits {
  list-style: none;
  margin: 6px 0 0;
  padding: 4px;
  max-height: min(46vh, 380px);
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 11px;
  /* ⚠️ 반투명(--scrim)이었는데, 글자 위에 뜨는 목록이라 뒤 내용이 비쳐 겹쳐 읽혔다.
     흐림 효과로도 가려지지 않는다 — 목록은 **완전히 불투명**해야 한다. */
  background: var(--ground);
  box-shadow: var(--shadow);
}
.hits[hidden] { display: none; }
.hits li {
  padding: 9px 11px;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.hits li:hover, .hits li[aria-selected="true"] { background: var(--hover); }
.hits .t {
  flex: none;
  width: 7px; height: 7px;
  border-radius: 50%;
  transform: translateY(-1px);
}
.hits .t.cam  { background: var(--cam); }
.hits .t.sec  { background: var(--sec); }
.hits .t.park { background: var(--park); }
/* 지역은 지점과 다른 것이라 표시도 달라야 한다 — 네모로 '범위'를 뜻한다. */
.hits .t.region { background: var(--muted); border-radius: 3px; }
.hits .n { color: var(--ink); font-size: 13px; flex: 1; min-width: 0;
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hits .c { color: var(--dim); font-size: 11.5px; flex: none; }
.hits .none { color: var(--dim); font-size: 12.5px; cursor: default; }
.hits .none:hover { background: none; }

/* ── 불러오는 중 ───────────────────────────────────────── */
.loading {
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--scrim);
  box-shadow: var(--shadow);
  color: var(--muted);
  font-size: 13px;
}
.loading[hidden] { display: none; }
.spinner {
  width: 15px; height: 15px;
  border: 2px solid var(--line);
  border-top-color: var(--cam);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}

/* ── 선택한 지점 ───────────────────────────────────────── */
.sheet {
  position: fixed;
  z-index: 5;
  left: 12px; right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom));
  padding: 16px 18px 18px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--scrim);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow);
}
.sheet[hidden] { display: none; }
@media (min-width: 640px) {
  .sheet { left: 12px; right: auto; width: 340px; }
}

.sheet .close {
  position: absolute;
  top: 10px; right: 10px;
  width: 30px; height: 30px;
  border: 0; border-radius: 8px;
  background: transparent;
  color: var(--dim);
  font-size: 14px;
  cursor: pointer;
}
.sheet .close:hover { background: var(--hover); color: var(--ink); }

.sheet .kind {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--cam);
}
.sheet .kind[data-t="park"] { color: var(--park); }
.sheet .kind[data-t="sec"]  { color: var(--sec); }

.sheet h2 {
  margin: 0;
  font-size: 17px;
  line-height: 1.35;
  font-weight: 700;
  text-wrap: balance;
  padding-right: 26px;
}
.sheet .city { margin: 5px 0 0; font-size: 13px; color: var(--muted); }

.facts {
  list-style: none;
  margin: 13px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}
.facts li {
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.facts li b { color: var(--ink); font-weight: 700; }

.route {
  display: block;
  width: 100%;
  margin-top: 10px;
  padding: 11px;
  border: 0;
  border-radius: 9px;
  background: var(--btn);
  color: var(--ink);
  text-align: center;
  text-decoration: none;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}
.route:hover { background: var(--btn-hi); }
/* ⚠️ `display: block` 은 hidden 속성을 이긴다. 이걸 빠뜨려서 "숨겼다"고 믿은 버튼이
   화면에는 계속 보이고 있었다 — 코드와 화면이 서로 다른 말을 하는 상태였다. */
.route[hidden] { display: none; }

/* ── 주행 모드 ─────────────────────────────────────────── */
/* 바닥에 뜨는 것들의 공통 자리. ⚠️ 안에 있는 것들은 각자 띄우지 않는다 —
   하나가 숨으면 나머지가 배치로 내려오게 두는 것이 요점이다. */
.dock {
  position: fixed;
  z-index: 5;
  left: 12px; right: 12px;
  bottom: calc(58px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dock[hidden] { display: none; }
.nav[hidden] { display: none; }
/* 설명을 펼치는 동안 치운다. display 를 지정하지 않아 UA 기본으로도 사라지지만,
   나중에 누가 display 를 주면 그날 조용히 안 사라진다 — 명시해 둔다.
   (이 프로젝트에서 네 번째로 밟는 함정이라 tools/check_hidden_guards.py 가 전수 검사한다.) */
.drive[hidden] { display: none; }
.go, .stop {
  display: block;
  width: 100%;
  padding: 14px;
  border: 1px solid var(--soon-bd);
  border-radius: 12px;
  background: var(--soon-bg);
  color: var(--soon-fg);
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  backdrop-filter: blur(8px);
}
.go:hover { border-color: var(--cam); }
/* ⚠️ display 를 지정한 요소에는 [hidden] 가드가 반드시 따라와야 한다. 없으면 JS 로
   hidden 을 켜도 화면에서 사라지지 않는다 — 이 프로젝트에서 세 번째로 밟은 함정이라
   tools/check_hidden_guards.py 가 이제 전수로 검사한다. */
.go[hidden], .stop[hidden], .live[hidden] { display: none; }
.stop {
  margin-top: 10px;
  border-color: var(--line);
  background: var(--btn);
  color: var(--muted);
  font-size: 13.5px;
}
.try {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 11px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--chrome);
  backdrop-filter: blur(8px);
  color: var(--muted);
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
}
.try:hover { color: var(--ink); border-color: color-mix(in srgb, var(--muted) 45%, transparent); }
.try[hidden] { display: none; }

/* 경고음 크기. 차마다 스피커와 주행 소음이 달라 하나로 정할 수 없다. */
.vol {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--chrome);
  backdrop-filter: blur(8px);
  color: var(--muted);
  font-size: 13px;
}
.vol label { white-space: nowrap; }
.vol input { flex: 1; min-width: 0; accent-color: var(--cam); }
.vol output { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* 정지 옆에 헤드업 토글을 둔다. 주행 중 누르는 것이라 손가락만 한 크기가 필요하다. */
.row { display: flex; gap: 8px; margin-top: 10px; }
.row .stop { margin-top: 0; }
.headup {
  flex: 0 0 auto;
  padding: 14px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--btn);
  color: var(--muted);
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.headup[aria-pressed="true"] {
  border-color: var(--cam);
  background: color-mix(in srgb, var(--cam) 16%, transparent);
  color: var(--cam);
}

.live {
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--scrim);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow);
}
.live[hidden] { display: none; }

.gauge { display: flex; gap: 14px; margin: 0 0 10px; }
.gauge div { flex: 1; min-width: 0; }
.gauge dt { font-size: 10px; color: var(--dim); letter-spacing: .04em; }
.gauge dd {
  margin: 2px 0 0;
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.warns {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
}
.warns.bad { color: var(--sec); }

/* 경고 순간에 화면 전체를 물들인다. 차 화면은 시야 가장자리에 있어서
   작은 표시는 안 보이고, 색이 바뀌는 것은 보인다. */
.flash {
  position: fixed;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  background: rgb(226 60 40 / .34);
  animation: pulse 1.5s ease-out;
}
.flash[hidden] { display: none; }
@keyframes pulse {
  0%   { opacity: 0; }
  12%  { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .flash { animation-duration: 2.5s; }
}

/* 무엇을 경고했는지 크게. 운전 중에는 한 줄만 읽힌다. */
.alert {
  position: fixed;
  z-index: 9;
  left: 50%; top: 18%;
  transform: translateX(-50%);
  max-width: min(92vw, 620px);
  padding: 18px 26px;
  border-radius: 16px;
  background: #c8321f;
  color: #fff;
  font-size: clamp(20px, 4.4vw, 34px);
  font-weight: 800;
  text-align: center;
  letter-spacing: -.02em;
  box-shadow: 0 10px 40px rgb(0 0 0 / .4);
  animation: pulse 4s ease-out;
}
.alert[hidden] { display: none; }

@media (min-width: 940px) {
  .drive {
    position: static;
    flex: none;
    padding: 0 22px 16px;
  }
  .go, .live { backdrop-filter: none; }
  .live { background: var(--panel); box-shadow: none; }
}

/* ── 하단 설명 ─────────────────────────────────────────── */
.intro {
  position: fixed;
  z-index: 4;
  left: 0; right: 0;
  bottom: 0;
  background: var(--ground);
  border-top: 1px solid var(--line);
}
.handle {
  display: block;
  width: 100%;
  padding: 13px 14px calc(13px + env(safe-area-inset-bottom));
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.handle::after { content: " ▾"; color: var(--dim); }
.handle[aria-expanded="true"]::after { content: " ▴"; }

.intro-body {
  max-height: min(62vh, 520px);
  overflow-y: auto;
  padding: 0 16px 20px;
  font-size: 13.5px;
  line-height: 1.72;
  color: var(--muted);
}
.intro-body[hidden] { display: none; }
.intro-body p { margin: 0 0 12px; max-width: 62ch; }
.intro-body strong { color: var(--ink); }
.intro-body h3 {
  margin: 20px 0 8px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -.01em;
}
.intro-body .soon {
  padding: 11px 13px;
  border: 1px solid var(--soon-bd);
  border-radius: 9px;
  background: var(--soon-bg);
  color: var(--soon-fg);
}
.intro-body .soon b { color: var(--cam); }
.src { margin: 0 0 12px; padding-left: 18px; max-width: 62ch; }
.src li { margin-bottom: 5px; }
.src em { font-style: normal; color: var(--ink); }
.note { font-size: 12.5px; color: var(--dim); }
.links { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 16px; }
.links a { color: var(--muted); font-size: 12.5px; }
.links a:hover { color: var(--ink); }
.by { margin: 14px 0 0; font-size: 11.5px; color: var(--dim); }

a { color: var(--muted); }
:focus-visible { outline: 2px solid var(--cam); outline-offset: 2px; }

/* MapLibre 기본 컨트롤을 어두운 화면에 맞춘다. */
.maplibregl-ctrl-group {
  background: var(--chrome) !important;
  border: 1px solid var(--line);
  backdrop-filter: blur(8px);
}
.maplibregl-ctrl-group button + button { border-top-color: var(--line) !important; }
.maplibregl-ctrl-group button .maplibregl-ctrl-icon { filter: var(--ctrl-invert); }
.maplibregl-ctrl-attrib {
  background: var(--scrim) !important;
  color: var(--dim);
}
.maplibregl-ctrl-attrib a { color: var(--muted); }
/* ⚠️ 좁은 화면에서 이 버튼들이 주행 패널 **뒤에** 깔려 보이지도 눌리지도 않았다.
   패널 높이는 주행 중이냐에 따라 달라져 고정값으로 둘 수 없다 — app.js 의
   liftMapControls() 가 실제 높이를 재서 이 값을 채운다. */
.maplibregl-ctrl-bottom-left,
.maplibregl-ctrl-bottom-right { bottom: var(--ctrl-lift, 46px); }

/* 본문 문서(개인정보처리방침·라이선스)에서만 쓴다. */
.doc {
  position: static;
  max-width: 68ch;
  margin: 0 auto;
  padding: 40px 20px 80px;
  line-height: 1.78;
  color: var(--muted);
  font-size: 14.5px;
}
.doc h1 { color: var(--ink); font-size: 24px; margin: 0 0 6px; letter-spacing: -.02em; }
.doc h2 { color: var(--ink); font-size: 15px; margin: 30px 0 8px; }
.doc .lead { color: var(--dim); font-size: 13px; margin: 0 0 28px; }
.doc ul { padding-left: 20px; }
.doc li { margin-bottom: 6px; }
.doc pre {
  overflow-x: auto;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel);
  font-size: 12px;
  line-height: 1.6;
  color: var(--muted);
  white-space: pre-wrap;
  word-break: break-word;
}
.doc .back { display: inline-block; margin-bottom: 24px; font-size: 13px; }

/* 지도 배경만 실패했을 때 띄운다. 점은 보이는데 배경이 없으면 사용자는
   앱이 고장난 줄 알고 새로고침을 반복한다 — 무슨 일인지 알려주는 편이 낫다. */
.warn {
  position: fixed;
  left: 50%;
  top: calc(52px + env(safe-area-inset-top));
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  max-width: calc(100vw - 24px);
  padding: 9px 14px;
  border: 1px solid var(--soon-bd);
  border-radius: 9px;
  background: var(--scrim);
  color: var(--soon-fg);
  font-size: 12.5px;
  text-align: center;
}

/* ══════════════════════════════════════════════════════════
   넓은 화면 — 사이드바 배치

   좁은 화면의 규칙(위쪽 전부)은 지도를 화면 전체로 쓰고 요소들을 그 위에 띄운다.
   화면이 좁으면 그것 말고 방법이 없기 때문이다. 그런데 그 배치를 PC 에 그대로
   가져가면 좌우가 텅 비고, 설명은 서랍 안에 접혀 있어 아무도 열어보지 않는다.

   그래서 940px 부터는 왼쪽에 고정 사이드바를 세우고 지도를 그 옆으로 민다.
   같은 DOM 을 쓰되 .panel 이 display:contents(없는 것처럼) → 실제 컨테이너로 바뀐다.
   ══════════════════════════════════════════════════════════ */
.panel { display: contents; }

@media (min-width: 940px) {
  .panel {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0; top: 0; bottom: 0;
    width: 380px;
    z-index: 3;
    background: var(--ground);
    border-right: 1px solid var(--line);
  }

  #map { left: 380px; }

  /* 자식들은 더 이상 화면에 떠 있지 않고 사이드바 안에 쌓인다. */
  .panel > * { position: static; }

  .bar {
    flex: none;
    padding: 20px 22px 16px;
    background: none;
    border-bottom: 1px solid var(--line);
  }
  .brand .name { font-size: 18px; }
  .counts dd { font-size: 15px; }

  .legend {
    flex: none;
    flex-direction: row;
    /* ⚠️ 안내문이 범례 안에 있다. 줄바꿈을 허용하지 않으면 넓은 화면에서
       버튼들과 한 줄에 끼여 셋 다 찌그러진다. */
    flex-wrap: wrap;
    gap: 8px;
    padding: 14px 22px;
    border-bottom: 1px solid var(--line);
  }
  .key { background: transparent; backdrop-filter: none; }

  .sheet {
    /* ⚠️ static 으로 두면 안 된다. 안의 닫기 버튼이 absolute 라서, 기준이 되는
       조상이 .sheet 가 아니라 .panel 로 올라가버린다 — ✕ 가 사이드바 맨 위
       개수 표시 위에 겹쳐 떴다. 위치 기준을 여기로 되돌린다. */
    position: relative;
    /* 좁은 화면용 규칙이 남긴 오프셋을 지운다. relative 에서는 left 가 살아 있어
       사이드바 안에서 12px 밀려 보인다. */
    left: 0; right: auto; bottom: auto;
    flex: none;
    margin: 0;
    padding: 18px 22px 20px;
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    width: auto;
    background: color-mix(in srgb, var(--cam) 5%, transparent);
    backdrop-filter: none;
    box-shadow: none;
  }
  .sheet .close { top: 14px; right: 16px; }

  /* 설명은 접지 않는다. 사이드바에는 자리가 있고, 접힌 정보는 읽히지 않는다. */
  .intro {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    border-top: 0;
    background: none;
  }
  .handle { display: none; }
  .intro-body {
    display: block !important;   /* [hidden] 이어도 넓은 화면에서는 편다 */
    flex: 1 1 auto;
    max-height: none;
    padding: 18px 22px 24px;
  }

  /* 검색은 사이드바 안, 범례 바로 위에 앉는다. 지도 위에 떠 있을 이유가 없다. */
  .find {
    /* DOM 에서 이미 헤더 다음·범례 앞이라 order 를 줄 필요가 없다.
       order:-1 을 주면 오히려 헤더보다 위로 올라간다.

       ⚠️ position 을 static 으로 두면 안 된다(.panel > * 규칙이 그렇게 만든다).
       static 이면 안의 .hits 가 기준 조상을 .panel 로 잡아 자리가 어긋나고,
       z-index 도 효력을 잃어 시트가 목록 위로 올라온다. 실제로 그렇게 났다. */
    position: relative;
    z-index: 7;
    flex: none;
    padding: 14px 22px 0;
    top: auto;
  }
  .find input {
    background: var(--panel);
    backdrop-filter: none;
    box-shadow: none;
  }
  .find .clear { right: 30px; }
  .hits {
    position: absolute;
    left: 22px; right: 22px;   /* .find 의 좌우 패딩과 같게 */
    z-index: 7;
  }

  /* 지도 컨트롤이 사이드바 밑에 깔리지 않게 한다. */
  .maplibregl-ctrl-bottom-left,
  .maplibregl-ctrl-bottom-right { bottom: 8px; }

  .loading { left: calc(380px + (100vw - 380px) / 2); }
  .warn { left: calc(380px + (100vw - 380px) / 2); top: 16px; }
}

/* 아주 넓은 화면에서 사이드바만 늘어나면 글줄이 길어져 읽기 나빠진다. */
@media (min-width: 1600px) {
  .panel { width: 420px; }
  #map { left: 420px; }
  .loading { left: calc(420px + (100vw - 420px) / 2); }
  .warn { left: calc(420px + (100vw - 420px) / 2); }
}


/* ── 길찾기 패널 ────────────────────────────────────────
   ⚠️ 버튼 나열이 아니라 **칸 두 개**다. 출발·도착은 한 쌍이고 자리를 바꿀 수 있는
   관계인데, 버튼을 하나씩 두면 그 관계가 화면에 안 보인다(형 8/19). */
.route-modes {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
}
.route-modes button {
  flex: 1;
  padding: 9px 6px;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 9px;
  cursor: pointer;
}
.route-modes button[aria-pressed="true"] {
  color: var(--ink);
  border-color: var(--cam);
  background: var(--card-on, var(--card));
  font-weight: 600;
}
.route-form { display: flex; gap: 6px; align-items: stretch; }
.route-inputs { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.route-inputs input {
  width: 100%;
  padding: 11px 12px;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 9px;
}
/* 어느 칸을 채우는 중인지 보이지 않으면 지도를 눌렀을 때 어디에 들어갈지 모른다. */
.route-inputs input:focus {
  outline: none;
  border-color: var(--cam);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--cam) 28%, transparent);
}
.swap {
  flex: none;
  width: 40px;
  font-size: 17px;
  color: var(--muted);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 9px;
  cursor: pointer;
}
/* 결과 목록은 위쪽 검색창의 것과 같은 모양을 쓴다 — 같은 일을 하는 것은 같아 보여야 한다. */
#route-hits {
  position: static;
  margin: 6px 0 0;
  max-height: 30vh;
}
#route-hits[hidden] { display: none; }

/* 길찾기 중에는 곁다리를 접는다. ⚠️ '주행 모드 시작' 은 접지 않는다 —
   길찾기를 하다가 그대로 출발하는 것이 가장 흔한 흐름이다. */
body.routing .vol,
body.routing #try-tone { display: none; }

/* 안내 중에는 칸과 목록을 접는다. 화면에 남길 것은 남은 길과 다음 회전이지
   이미 정한 출발·도착이 아니다. ⚠️ 안내를 멈추면 다시 펼쳐진다(stopDrive).

   ⚠️ 다만 **접는 것과 못 쓰게 하는 것은 다르다.** 처음엔 그냥 접었더니, 목적지를
   바꾸려면 위의 '정지' 를 눌러 주행을 끝내는 수밖에 없었다(형 실차 8/19).
   목적지를 바꾸려고 안내를 멈춰야 한다면 그건 길안내가 아니다.
   엔진은 원래 갈아끼울 수 있었다 — recompute() 가 guide 를 새로 만든다.
   막고 있던 건 이 규칙뿐이었으므로 `:not(.rerouting)` 으로 길을 낸다.

   ⚠️ 되살릴 때 display 로 덮어쓰지 않는다. 그러면 [hidden] 가드를 이겨서
   숨겨야 할 때 안 숨는다 — 이 프로젝트에서 다섯 번 밟은 함정이다. */
body.guiding:not(.rerouting) .route-form,
body.guiding:not(.rerouting) #route-hits,
body.guiding:not(.rerouting) .route-modes,
body.guiding:not(.rerouting) .route-panel .row,
body.guiding:not(.rerouting) .route-panel .tiny { display: none; }

/* 안내 중에는 '이 길로 안내 시작' 이 다시 뜰 자리가 없다 — 이미 그 길로 가는 중이다. */
body.guiding #route-start { display: none; }

/* 목적지를 바꾸는 문. 안내 중에만, 그리고 바꾸는 중이 아닐 때만 보인다. */
#route-again { display: none; }
#route-again[hidden] { display: none; }
body.guiding:not(.rerouting) #route-again { display: block; }

#route-start { margin-bottom: 8px; }
/* 이 프로젝트에서 다섯 번 밟은 함정이라 명시한다 — display 는 hidden 을 이긴다. */
#route-start[hidden] { display: none; }

/* 이름 색인 결과. 카메라·주정차와 눈으로 구별돼야 무엇을 고르는지 안다. */
.hits .t.place { background: var(--ink); opacity: 0.45; border-radius: 2px; }

#route-switch[hidden] { display: none; }

/* ── 차 화면 키보드 대응 ─────────────────────────────────
   ⚠️ 테슬라 화면 키보드는 아래 45% 가량을 덮는다(형 실차 2026-08-19). 그런데 길찾기
   입력칸은 패널 **맨 아래**에 있고 후보 목록은 그 아래로 펼쳐진다 — 글자를 치는 순간
   후보가 통째로 키보드 뒤로 숨는다. 차 화면은 좌우로 갈려 있어 세로가 더 좁다.

   ⚠️ **키보드가 열렸는지는 알 수 없다.** 차 화면 키보드는 브라우저 뷰포트를 줄이지
   않아 visualViewport 이벤트가 오지 않는다. 그래서 감지하지 않는다 — 입력 중에는
   무조건 위로 올린다. 키보드가 없는 기기에서도 나쁘지 않은 배치라 갈래를 만들 이유가 없다.
   ═══════════════════════════════════════════════════════ */
body.editing .route-panel {
  position: fixed;
  top: calc(10px + env(safe-area-inset-top));
  left: 12px;
  right: 12px;
  z-index: 8;               /* 검색(.find, 7)보다 위 — 지금은 이게 화면의 주인공이다 */
  margin-top: 0;
  max-height: 46vh;
  /* ⚠️ 반투명이면 **뒤의 검색창이 비쳐 글자가 겹쳐 읽힌다.** 흐림 효과로도 안 가려진다.
     같은 이유로 .hits 도 불투명으로 바꿨었다 — 위에 뜨는 것은 불투명해야 한다. */
  background: var(--ground);
  backdrop-filter: none;
  box-shadow: var(--shadow);
}
/* ⚠️ 위로 올린 패널이 **검색창과 겹친다.** z-index 를 8 로 올려도 .find(7) 이 위에
   그려졌다(실측 — 쌓임 맥락을 만드는 속성은 어디에도 없었는데 그랬다).
   원인을 더 캐는 대신 겹침 자체를 없앤다: 지금은 길찾기 칸이 화면의 주인공이고,
   가려진 검색창은 어차피 못 누른다. 두 개의 검색이 동시에 보일 이유가 없다.
   ⚠️ display 가 아니라 visibility — display 로 지우면 [hidden] 가드와 엉킨다. */
body.editing .find { visibility: hidden; }

/* 입력 중에 아래쪽 것들은 어차피 키보드 뒤다. 자리만 먹으니 치운다. */
body.editing .route-panel .row,
body.editing .route-panel .tiny,
body.editing .route-panel .warns,
body.editing .route-panel .caveat,
body.editing .route-panel .go { display: none; }

/* ⚠️ 사이드바 배치에서 .hits 는 absolute 다. 패널이 fixed 가 되면 기준 조상이 패널로
   바뀌어 좌우 22px 이 안쪽으로 파고든다. 입력 중에는 그냥 흐름에 태운다. */
body.editing #route-hits {
  position: static;
  max-height: 32vh;
  overflow-y: auto;
}

@media (min-width: 940px) {
  /* 사이드바 폭(380) 안에 맞춘다. 지도 위로 튀어나오면 무엇을 누르는지 헷갈린다. */
  body.editing .route-panel { left: 12px; right: auto; width: 356px; }
}
@media (min-width: 1600px) {
  body.editing .route-panel { width: 396px; }
}
