/* ============================================================
   shell.css — AeroNexus 화면 골격 (시안 A: Modern Sidebar & Dual Inspector)
   출처: sein/mockups/design-a-sidebar.html + css/design-a.css

   배치 원칙
   ─ 지도(#map)는 **건드리지 않는다**. 창 전체(inset:0)에 그대로 두고,
     헤더·레일·서랍·인스펙터를 그 위에 띄운다(overlay). flex 워크스페이스로 바꾸면
     Leaflet 크기 재계산·타일 리플로가 따라붙는데, 이번 범위(지도 제외)에 맞지 않는다.
   ─ 지도 자동 맞춤 여백은 skyroute.js uiInsets()가 '#panel,#schedPanel,.skyroute-inset'을
     재서 계산한다. 실제 UI를 재게 하면 모바일(아래로 눕는 배치)에서 오판하므로,
     데스크톱에서만 존재하는 보이지 않는 «자» #insetL·#insetR(.map-ruler)를 대신 재게 한다.
   ─ 좁은 화면에서는 같은 마크업으로 «좌측 레일 → 하단 탭바», «서랍/인스펙터 → 바텀시트»가
     된다 (사용자 요청 2026-08-24: 모바일에서도 볼 수 있어야 한다).
   ============================================================ */

html, body { height: 100%; }
/* 모바일 브라우저 주소창이 접혔다 펴질 때 100%는 «큰 뷰포트»로 굳어, 하단 탭바가 주소창 뒤로
   숨는 일이 생긴다. dvh는 지금 실제로 보이는 높이를 따라온다 (사용자 신고 2026-08-27
   「화면 배치가 겹친다」의 한 갈래 — 탭바가 안 보이면 메뉴를 닫을 방법이 사라진다).
   @supports로 감싼 이유: 못 알아듣는 브라우저에 100%를 남겨 두기 위함이다. */
@supports (height: 100dvh) { html, body { height: 100dvh; } }
/* 본문 타이포 기준 한 곳 (사용자 지시 2026-08-25 「세련된 글자 · 모바일에서도 이쁜 글자로 통일」)
   ⚠ font-size를 body에 **반드시** 준다 — 없으면 크기를 안 정한 요소가 브라우저 기본 16px로 떠서
     같은 패널 안에서 12px 줄과 16px 줄이 섞였다(«글꼴이 다 다르다»고 보이던 원인 중 하나).
   letter-spacing −0.01em: 한글 UI 글꼴(맑은 고딕·Noto)은 기본 자간이 넓어 조금 조이면 정갈해진다.
   text-size-adjust: 모바일 브라우저가 제멋대로 글자를 키우는 것을 막는다(가로 모드에서 특히). */
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  background: var(--bg-space);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow: hidden;
}
/* 숫자를 세로로 맞춰 읽는 칸은 자간을 건드리지 않는다 (모노의 고정폭이 흐트러진다) */
code, kbd, samp, pre, .metric-inline, .srow .v, .fbrow, .fp input, .fp textarea,
.cfgrow input, .alnum, #windRead { letter-spacing: 0; }

/* 화면 골격 치수 — 반응형에서 이 값만 바꾸면 전부 따라온다 */
:root {
  --hdr-h: 52px;
  /* 좌측 안전영역을 폭에 흡수한다 — 가로 모드 폰에서 노치는 화면 «옆»에 온다.
     이 변수 하나에 넣으면 서랍 left·#insetL 폭·#mapCtl left가 전부 따라온다(2026-08-27). */
  --rail-w: calc(58px + var(--sa-l));
  --drawer-w: 390px;
  --insp-w: 380px;
  --gap: 14px;

  /* ── 안전영역(노치·다이내믹 아일랜드·홈 인디케이터) ─────────────────────────
     사용자 지시 2026-08-27 「아이폰과 갤럭시 최신 최소 6인치 이상에 최적화」.
     6인치대 최신 폰은 화면 네 귀퉁이가 둥글고 위에 카메라 섬, 아래에 홈 인디케이터 바가 있다.
     <meta viewport-fit=cover>로 화면 끝까지 그리는 대신, UI는 이 값만큼 안으로 들여야
     ① 하단 탭바 글자가 홈 인디케이터에 깔리지 않고 ② 가로 모드에서 헤더가 노치에 안 잘린다.
     env()를 모르는 브라우저는 0으로 떨어져 종전 배치가 그대로 남는다(안전한 기본값). */
  --sa-t: env(safe-area-inset-top, 0px);
  --sa-r: env(safe-area-inset-right, 0px);
  --sa-b: env(safe-area-inset-bottom, 0px);
  --sa-l: env(safe-area-inset-left, 0px);
  /* 탭바가 실제로 먹는 세로 = 버튼 줄 + 홈 인디케이터. 시트의 bottom은 반드시 이 값을 쓴다
     (--tabbar-h만 쓰면 시트 아랫단이 탭바 뒤로 34px 들어가 마지막 줄이 영영 안 보인다) */
  --tabbar-h: 0px;
  --tabbar-total: calc(var(--tabbar-h) + var(--sa-b));
  /* 헤더가 실제로 먹는 세로 = 내용 줄(--hdr-h) + 상단 안전영역. 「헤더 아래에서 시작」하는
     모든 요소(레일·서랍·인스펙터·경고시트·지도 자)는 --hdr-h가 아니라 이 값을 쓴다. */
  --hdr-total: calc(var(--hdr-h) + var(--sa-t));
}

/* ============================================================
   1. 상단 글로벌 헤더
   ============================================================ */
.top-header {
  position: absolute;
  top: 0; left: 0; right: 0;
  /* 높이는 «내용 줄 + 상단 안전영역». padding-top으로 내용을 노치 아래로 내린다 —
     노치 없는 기기(env=0)에서는 종전과 정확히 같은 52px 헤더다. */
  height: var(--hdr-total);
  z-index: 1400;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: var(--sa-t) calc(14px + var(--sa-r)) 0 calc(14px + var(--sa-l));
  background: var(--bg-surface);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-sm);
}
.brand-section { display: flex; align-items: center; gap: 10px; flex: none; }
.brand-title {
  /* 2026-09-04: AeroNexus Korea Branding.pdf 워드마크 서체(Iceland) 이식 — 대문자 전용 서체라
     text-transform으로만 올리고 마크업의 "AeroNexus" 표기는 그대로 둔다(문서 안 다른 곳의
     대소문자 표기와 어긋나지 않게). letter-spacing을 양수로 바꾼 것도 이 서체가 각지고 좁아
     음수 자간(-0.02em, 이전 굵은 그라디언트 서체용)을 주면 글자가 서로 들러붙기 때문이다. */
  font-family: var(--font-display); text-transform: uppercase;
  font-size: var(--fs-hero); font-weight: 400; letter-spacing: 0.03em; margin: 0;
  background: linear-gradient(135deg, var(--text-primary) 30%, var(--accent-cyan));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* 담당 구역 뱃지 — 현행 #sectorSum을 헤더로 옮긴 것 (클릭 = 담당구역 탭) */
#sectorSum {
  display: flex; align-items: center; gap: 6px; flex: none;
  max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  margin: 0; padding: 4px 10px;
  background: var(--bg-sunken); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-size: var(--fs-md); font-weight: 600; color: var(--text-primary);
  font-variant-numeric: tabular-nums; cursor: pointer;
  transition: background var(--transition-fast);
}
#sectorSum:hover { background: var(--bg-hover); }
/* 열려 있는 동안은 배지가 «켜짐»을 든다 — 레일에 「구역」 버튼이 없으므로(2026-08-25 C안)
   이 강조가 없으면 어느 메뉴가 열려 있는지 화면 어디에도 표시되지 않는다 */
#sectorSum.on {
  background: color-mix(in srgb, var(--accent-cyan) 14%, transparent);
  border-color: var(--border-highlight); color: var(--accent-cyan);
}
#sectorSum:focus-visible { outline: 2px solid var(--accent-cyan); outline-offset: 1px; }
#sectorSum::after { content: '▼'; font-size: var(--fs-xs); opacity: .55; }

/* 시스템 상태 줄 */
.system-status-bar {
  display: flex; align-items: center; gap: 14px; min-width: 0;
  flex: 1 1 auto; overflow: hidden;
  font-size: var(--fs-md); color: var(--text-secondary);
}
#opsStat, #sqCov {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: nowrap; white-space: nowrap;
  margin: 0; font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
}
#opsStat .s { color: var(--text-tertiary); }


/* 경고 요약 알약 — 헤더 오른쪽. 누르면 #alertPopover 토글 */
#alertSum {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  width: auto; margin: 0; padding: 4px 11px;
  background: var(--status-live-bg); color: var(--status-live);
  border: 1px solid var(--status-live); border-radius: var(--radius-full);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 700; cursor: pointer;
  transition: transform var(--transition-fast), filter var(--transition-fast);
}
#alertSum:hover { transform: translateY(-1px); filter: brightness(1.06); }
#alertSum.warn {
  background: var(--status-danger-bg); color: var(--status-danger); border-color: var(--status-danger);
}
#alertSum .n { font-weight: 500; opacity: .85; }
#alertSum::before { content: '👁'; font-size: var(--fs-md); }
#alertSum.warn::before { content: '⚠️'; animation: alertIconPulse 1.5s infinite; }
/* 자동 열기 꺼짐 표시 (2026-08-25) — ⚠와 깜빡임은 **그대로 둔다**. 창을 안 여는 대신
   알약이 유일한 신호가 되므로 여기서까지 주의를 걷으면 새 경고가 완전히 조용해진다.
   🔕은 «창이 안 뜨는 이유»를 알리는 표시일 뿐이다. */
#alertSum.muted::after { content: '🔕'; font-size: var(--fs-sm); opacity: .7; }
@keyframes alertIconPulse { 0%,100%{ transform: scale(1); } 50%{ transform: scale(1.22); } }

.header-actions { display: flex; align-items: center; gap: 6px; flex: none; }
.header-actions .hb {
  width: auto; margin: 0; padding: 5px 10px;
  background: var(--bg-surface-elevated); color: var(--text-secondary);
  border: 1px solid var(--border-medium); border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer; white-space: nowrap;
}
.header-actions .hb:hover { background: var(--bg-hover); color: var(--accent-cyan); border-color: var(--accent-cyan); }
.header-actions .hb.on { background: var(--accent-cyan); color: var(--text-inverse); border-color: var(--accent-cyan); }
/* 첫 방문자에게만 «도움말이 여기 있다»를 잠깐 알린다 (사용자 확정 2026-09-04 「도움말 버튼 강조만」).
   ⛔ 안내 팝업·카드를 띄우지 않기로 한 자리다 — 지도가 본체인 화면에서 첫인상을 가리지 않는다.
   ⚠ 움직임에 민감한 이용자에게는 정지 상태로 보인다(색·테두리 강조는 남아 여전히 눈에 띈다) —
     이 저장소의 확정 규율(app.css .albadge.cf.fresh와 같은 처방). */
.header-actions .hb.nudge { color: var(--accent-cyan); border-color: var(--accent-cyan); animation: hbNudge 1.2s ease-in-out 3; }
@keyframes hbNudge {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent-cyan) 26%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .header-actions .hb.nudge { animation: none; } }

/* ============================================================
   2. 좌측 네비 레일 (id는 #bar 유지 — 호스트 JS의 '#bar button' 배선을 그대로 쓴다)
   ============================================================ */
#bar.nav-rail {
  position: absolute; left: 0; top: var(--hdr-total); bottom: 0; width: var(--rail-w);
  z-index: 1300;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 0 calc(10px + var(--sa-b)) var(--sa-l); overflow-y: auto; overflow-x: hidden;
  /* ⭐ 레일은 **라이트 테마에서도 어둡다** (사용자 확정 2026-09-02 — 건의 ③).
     색은 theme.css의 --rail-* 한 벌에서만 온다. 일반 배경·글자 토큰을 여기 섞지 말 것 —
     흰 바탕용 글자색이 새어들면 어두운 레일에 어두운 글자가 되어 메뉴가 안 읽힌다.
     ⚠ backdrop-filter 제거: 배경이 불투명해져 blur 결과가 100% 가려진다(#drawer가 같은 이유로
       2026-08-25에 제거 — 실측 약 7ms/프레임). 레일은 항상 떠 있고 뒤 지도가 계속 바뀐다. */
  background: var(--rail-bg);
  border-right: 1px solid var(--rail-border);
  transform: none; box-shadow: none; border-radius: 0;
}
#bar.nav-rail button {
  width: 46px; height: 46px; flex: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  background: none; border: 1px solid transparent; border-radius: var(--radius-md);
  color: var(--rail-fg); font-family: inherit; font-size: var(--fs-xs); font-weight: 600;
  cursor: pointer; position: relative; transition: all var(--transition-fast);
}
/* ⚠ features/nav-menu가 숨기는 버튼에 hidden 속성을 준다 — 위 규칙의 display:flex가
   브라우저 기본([hidden]{display:none})보다 우선이라 속성만으로는 안 숨는다. 실측 결함
   (2026-09-03, 사용자 신고 「여전히 왼쪽메뉴는 모두 나타남」) — 재발 방지로 여기 남긴다. */
#bar.nav-rail button[hidden] { display: none; }
#bar.nav-rail button svg {
  width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linejoin: round; stroke-linecap: round;
}
#bar.nav-rail button:hover { color: var(--rail-fg-strong); background: var(--rail-hover); }
#bar.nav-rail button.on {
  color: var(--rail-accent); background: var(--rail-accent-bg);
  border-color: color-mix(in srgb, var(--rail-accent) 45%, transparent);
}
#bar.nav-rail button.on::before {
  content: ''; position: absolute; left: -6px; top: 8px; bottom: 8px; width: 3px;
  background: var(--rail-accent); border-radius: 0 4px 4px 0;
}
/* 레일 4군 구분선 (사용자 지시 2026-09-01 「일관성있게 재배치」) — 코드 순서는 이미
   «무엇을 하러 왔나» 4군이었지만 화면에는 표시가 없어 12개가 한 줄로 보였다. 버튼을
   옮기지 않고 이 선만 넣어 그 구조를 눈에 보이게 한다(위 index.html 레일 주석 참고). */
#bar.nav-rail .rail-sep { width: 26px; height: 1px; margin: 2px 0; flex: none; background: var(--rail-sep); }
/* 4군 그룹 캡션 (사용자 UX 점검 2026-09-04) — 구분선(.rail-sep)만으로는 «무슨 무리인지»가
   화면에 없어 13개 버튼이 처음 보는 사람에게는 그냥 나열로 읽혔다. 버튼 라벨과 안 겹치는
   짧은 이름을 그룹 첫머리에 얹는다(단일 버튼짜리 「요청」 군은 넣지 않는다 — 구분선으로 충분). */
#bar.nav-rail .rail-glabel {
  width: 100%; flex: none; margin: 5px 0 -2px; padding: 0;
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .03em;
  text-align: center; color: var(--rail-fg); opacity: .6;
  pointer-events: none; user-select: none;
}
#bar.nav-rail .rail-glabel:first-child { margin-top: 0; }

/* ============================================================
   3. 좌측 서랍 (#drawer)
   ============================================================ */
/* 예측교통량 탭만 서랍을 넓힌다 (사용자 확정 2026-08-24 "예측교통량은 왼쪽 메뉴 너비를 조금더
   넓게하여 가독성 좋게") — 6개 시간열 × 섹터 표라 390px에서는 숫자·시각이 줄바꿈된다.
   ⚠ --drawer-w를 body에 다시 정의하는 방식이라 서랍뿐 아니라 #insetL(지도 여백)·#mapCtl 위치까지
     **자동으로 함께** 따라온다. 폭을 #drawer에만 주면 지도가 서랍 밑으로 들어간다.
   TAB 값은 openTab()이 body[data-tab]에 실어 준다. */
/* 예측교통량은 7열 표(섹터 + 정시 6개)라 다른 탭보다 넓다.
   2026-08-24: 표 글자를 10.5→12.5px로 올리면서 470→510px. 좁히면 섹터 이름이 잘린다. */
body[data-tab="fcst"] { --drawer-w: 510px; }
/* 공항별 운항현황도 넓힌다 (사용자 확정 2026-08-25 「공항탭 너비 조정필요」) —
   보드 한 줄의 고정열이 시각 88 + 편명 76 + 상태 42 + 게이트 36 + 열간격 16 = 258px를 먼저 가져가,
   서랍이 390px면 도착지(1fr)에 90px 남짓밖에 안 남아 「오사...」로 잘린다. 470px면 ~175px가 남아
   「오사카/간사이 KIX」가 통째로 들어온다. 열 폭을 더 줄이는 대신 서랍을 넓히는 쪽을 택한 이유는
   시각열(변경시각 병기 「09:35→09:20」)·편명열(📡+I98809화물)이 이미 최소폭이기 때문. */
body[data-tab="apt"]  { --drawer-w: 600px; }   /* 2026-08-25 470→520→600 (사용자 제안 「왼쪽메뉴 창의 너비를 더 늘려서 모두 보이게」)
                                                   — 표가 서랍을 꽉 채우는 구조(app.css .fbtbl/.fbc)에서 출도착 열 ≈ 서랍−280px:
                                                   520이면 ~220px(「ORD(시카고/오헤어)」가 간신히), 600이면 ~300px(「SDF(스탠디포드필드(루이스빌))」
                                                   급 최장 지명만 …). 좁은 화면 값(아래 @media)은 그대로. */
/* 「항로」 탭 — CDR 이용 레그 표가 8열(시각·편명·구간·노선·직선이격·항로망이격·구간%·창)이고
   전 칸이 nowrap이라 실측 489px가 필요하다. 390px 서랍에서는 서랍 전체가 **옆으로** 스크롤돼
   (실측 scrollWidth 503 / clientWidth 374) 왼쪽 필터·단추가 화면 밖으로 밀렸다 —
   「가로 스크롤은 없도록」(사용자 확정 2026-08-25, 공항 탭에서 같은 이유로 정한 원칙).
   560px면 안쪽 폭 ≈ 517px로 표가 통째로 들어온다. 하강연료 리포트 표(최대 467px)도 같이 산다. */
/* ⭐ 2026-09-01 「항로」 분리 — 종전엔 셋이 한 폭(560px)을 나눠 썼다.
   ⚠ 연료는 그 뒤 **팝업으로 옮겼다**(8열 순위표라 서랍 폭에 갇히면 「발생」 칸이 잘린다) —
     그래서 여기 남는 서랍은 «효율» 하나다. 창 규칙은 위 #drawer.side-drawer다. */
body[data-tab="ops"]  { --drawer-w: 560px; }
body[data-tab="fuel"] { --drawer-w: 860px; }   /* 8열 순위표 — 좁으면 「발생 고도」 칸이 잘린다 */
body[data-tab="queue"]{ --drawer-w: 760px; }   /* 상수표 */
/* 공항 기상 (2026-09-01 신설) — 카드 그리드가 3열(각 카드 ≥272px)로 늘어서게 한다.
   .wxg-grid는 auto-fit이라 좁아지면 저절로 2열·1열로 접힌다 — 아래 값은 「기본 3열」의 하한. */
body[data-tab="wxg"]  { --drawer-w: 940px; }
/* 경보 이력 (2026-08-31 신설) — 행이 4열 그리드(종류칩·시각·당사자·수치)라 기본 390px에서는
   당사자(편명쌍)가 말줄임된다. 420px이면 편명 7자 × 2 + 수치가 들어온다. */
body[data-tab="alog"] { --drawer-w: 420px; }

/* ══ 메뉴 창 — 2026-09-01 3차: **도킹 서랍 → 떠 있는 창** ═══════════════════════════
   사용자 지시: 「왼쪽 메뉴 누르면 모두 팝업창으로 뜨고, 다른 것으로 바꿔 누르면 기존 창이
   닫히고 새 메뉴 창이 뜨도록. 단 다시 이전 메뉴 누르면 이전에 하던대로 유지」

   ⭐ 창을 화면마다 따로 만들지 않는다 — **이 창 하나**의 내용(pane)만 갈아 끼운다.
     그래야 pane DOM이 살아 있어 «하던대로»(스크롤·입력·불러온 표)가 공짜로 보존된다.
     경위·근거는 index.html의 `MENU_ST` 주석에 있다.
   ⚠ 지도를 밀지 않는다 — 떠 있는 창이므로 #mapCtl·#rcBar·#insetL은 **레일 폭만** 피한다
     (아래 그 규칙들에서 --drawer-w를 뺐다).
   ⚠ 폭은 화면마다 다르다(body[data-tab] --drawer-w, 위). 사용자가 끌거나 늘리면 그 값은
     인라인 style로 덮이고 index.html이 메뉴별로 기억한다. */
#drawer.side-drawer {
  position: fixed; left: calc(var(--rail-w) + var(--gap)); top: calc(var(--hdr-total) + var(--gap));
  bottom: auto; z-index: 1250;
  width: var(--drawer-w);
  height: min(760px, calc(100vh - var(--hdr-total) - var(--gap) * 3));
  max-width: calc(100vw - var(--rail-w) - var(--gap) * 2);
  max-height: calc(100vh - var(--hdr-total) - var(--gap) * 2);
  display: flex; flex-direction: column; overflow: hidden;
  padding: 0; border: 1px solid var(--border-medium); border-radius: var(--radius-lg);
  resize: both;                     /* 모서리로 크기 조절 — 넓은 표는 넓혀 본다 */
  background: var(--bg-surface-elevated);
  /* backdrop-filter 제거 (2026-08-25) — 이 패널의 background는 --bg-surface-elevated로
     **완전 불투명**(#ffffff / 다크 #131c2e)이라 blur 결과가 자기 배경에 100% 가려진다.
     픽셀 대조로 확인: 제거 전후 #drawer 524,280px 중 다른 픽셀 0개.
     비용만 남는 이유 — blur는 요소를 독립 렌더 표면으로 만들어, 뒤 배경(지도)이 바뀔 때마다
     그 영역을 다시 스냅샷·가우시안 처리한다. 지도는 팬·줌 + 항적 캔버스 1Hz로 계속 바뀐다.
     실측 약 7ms/프레임. 되살리려면 배경을 반투명으로 바꾸는 것이 먼저다. */
  box-shadow: var(--shadow-lg);
  transition: transform var(--transition-smooth), opacity var(--transition-smooth);
}
/* 창 닫힘 — 떠 있는 창이라 **display:none**이면 된다(종전 transform 밀어내기는 도킹 서랍의
   방식이었다). ⛔ pane의 DOM은 그대로 산다 — 그것이 「다시 누르면 하던대로」의 뿌리다. */
#drawer.side-drawer.closed { display: none; }
.drawer-header {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 11px 14px; border-bottom: 1px solid var(--border-subtle);
  /* 끌어서 창을 옮긴다 — 지도를 잠깐 봐야 할 때 닫지 않고 치울 수 있어야 한다 */
  cursor: move; user-select: none; touch-action: none;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
/* 제목 + ? 아이콘을 한 덩어리로 (space-between이 둘을 갈라 놓지 않게) */
.drawer-header .dhT { display: flex; align-items: center; min-width: 0; }
.drawer-header #shTitle { font-size: var(--fs-lg); font-weight: 700; color: var(--text-primary);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drawer-header .sec {
  width: 30px; height: 30px; flex: none; margin: 0; padding: 0;
  background: none; border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
  color: var(--text-secondary); font-size: var(--fs-md); line-height: 1; cursor: pointer; font-weight: 700;
}
.drawer-header .sec:hover { background: var(--bg-hover); color: var(--status-danger); }
#sheet {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: none; margin: 0; padding: 12px 14px 18px; border: 0;
}
#sheet.on { display: block; }

/* 예측교통량 안의 하위 탭 (예측 · 섹터현황 — 사용자 확정 2026-08-24 통합) */
.subtabs {
  display: flex; gap: 4px; margin: 0 0 10px;
  border-bottom: 1px solid var(--border-subtle);
}
.subtabs button {
  width: auto; margin: 0; padding: 6px 12px;
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--text-secondary); font-family: inherit; font-size: var(--fs-md); font-weight: 700; cursor: pointer;
}
.subtabs button:hover { color: var(--text-primary); }
.subtabs button.on { color: var(--accent-cyan); border-bottom-color: var(--accent-cyan); }
.subpane { display: none; }
.subpane.on { display: block; }

/* 바텀시트 손잡이 (모바일 전용 — 아래 §8-3에서 켠다).
   데스크톱에서는 자리조차 차지하지 않는다: 서랍은 옆에서 나오지 위에서 올라오지 않는다. */
.sheet-grip { display: none; }

/* ============================================================
   4. 우측 기체 인스펙터 (id는 #acDetail 유지)
   ============================================================ */
#acDetail.inspector-panel {
  position: absolute; top: calc(var(--hdr-total) + var(--gap)); right: calc(var(--gap) + var(--sa-r));
  width: var(--insp-w); max-height: calc(100% - var(--hdr-total) - var(--gap) * 2);
  z-index: 1260; margin: 0; padding: 0;
  display: none; flex-direction: column; overflow: hidden;
  background: var(--bg-surface-elevated);
  /* backdrop-filter 제거 (2026-08-25) — 이 패널의 background는 --bg-surface-elevated로
     **완전 불투명**(#ffffff / 다크 #131c2e)이라 blur 결과가 자기 배경에 100% 가려진다.
     픽셀 대조로 확인: 제거 전후 #drawer 524,280px 중 다른 픽셀 0개.
     비용만 남는 이유 — blur는 요소를 독립 렌더 표면으로 만들어, 뒤 배경(지도)이 바뀔 때마다
     그 영역을 다시 스냅샷·가우시안 처리한다. 지도는 팬·줌 + 항적 캔버스 1Hz로 계속 바뀐다.
     실측 약 7ms/프레임. 되살리려면 배경을 반투명으로 바꾸는 것이 먼저다. */
  border: 1px solid var(--border-medium); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
#acDetail.inspector-panel.on { display: flex; }
#acDetail .acHd {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 0; padding: 11px 14px;
  background: var(--bg-sunken); border-bottom: 1px solid var(--border-subtle);
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0; color: var(--text-secondary);
}
/* margin-right:auto — space-between 배치에서 가운데(아이콘)가 남는 공간을 다 먹어
   편명 바로 옆에 붙고 ✕만 오른쪽 끝에 남는다 */
#acDetail .acHd .hlp { flex: none; margin-right: auto; }
#acDetail .acHd .sec {
  width: auto; margin: 0; padding: 3px 10px; font-size: var(--fs-sm); letter-spacing: 0;
  background: var(--bg-surface-elevated); color: var(--text-secondary);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); cursor: pointer;
}
#acDetail .acHd .sec:hover { background: var(--bg-hover); color: var(--status-danger); }
/* 인스펙터·경고 팝오버는 같은 우상단을 써서 겹칠 수 있다 — 머리를 잡아 옆으로 옮길 수 있게
   (사용자 확정 2026-08-25). 모바일 바텀시트에서는 드래그 대상이 아니라 커서만 다르다 */
@media (min-width: 761px) {
  #acDetail .acHd, #alertPopover .popover-header { cursor: move; touch-action: none; }
  #acDetail .acHd .sec, #alertPopover .popover-header .sec { cursor: pointer; }
}
body.panel-dragging { -webkit-user-select: none; user-select: none; }
#acBody { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 12px 14px 14px; }

/* 인스펙터 머리 = 편명 + 국문 호출부호 (호스트 restyleAcDetail()이 채운다) */
#acIdent { min-width: 0; font-size: var(--fs-hero); font-weight: 800; letter-spacing: -0.01em;
  color: var(--text-primary); text-transform: none; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#acIdent .pko { display: block; margin-top: 2px; font-size: var(--fs-md); font-weight: 600; color: var(--accent-cyan); }

/* 출도착 — 콜사인 바로 옆 (사용자 지시 2026-08-25). 머리는 nowrap+ellipsis라 길면 잘린다:
   전체 문구는 title 툴팁으로 남긴다(restyleAcDetail이 넣는다). */
#acIdent .acod {
  margin-left: 9px; font-size: var(--fs-md); font-weight: 600; letter-spacing: 0;
  font-family: var(--font-mono); color: var(--text-secondary);
}

/* 기종·등록기호 줄 / 노선 줄 */
#acBody .acType { font-size: var(--fs-sm); color: var(--text-secondary); margin-bottom: 8px; }
#acBody .acRoute { font-size: var(--fs-md); font-weight: 700; color: var(--text-primary);
  background: var(--bg-sunken); border-radius: var(--radius-sm); padding: 7px 10px; margin-bottom: 10px; }

/* 지표는 전부 «라벨 값» 한 줄로 (사용자 지시 2026-08-25 「고도 f310 지상속도 12kt —
   박스에 두 줄을 한 줄로 겹쳐봐. 작은 피드백은 그대로 유지하고」).
   상자 격자(.metric-grid)를 쓰던 자리다 — 기체 하나가 세로로 4줄을 먹어 모바일에서 스크롤만 길었다.
   ⛔ 작은 피드백(.ms = 승강률·가속)은 지우지 않는다: 「FL250」과 「FL250 강하 중」은 다른 사실이다. */
.metric-inline {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 20px;
  margin: -4px 0 12px; padding: 0 2px;
}
.metric-inline.main { margin: 2px 0 6px; }
.metric-inline > span {
  font-size: var(--fs-lg); font-weight: 700; font-family: var(--font-mono);
  color: var(--text-primary); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.metric-inline.main > span { font-size: var(--fs-xl); }
.metric-inline em {
  font-style: normal; font-family: inherit; font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-secondary); margin-right: 6px;
}
/* 부가설명 — 값 뒤에 작은 글씨로. 색은 radar.js가 span에 실어 보낸 것을 그대로 쓴다(상승/강하 구분) */
.metric-inline .ms {
  font-style: normal; font-family: inherit; font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-secondary); margin-left: 6px;
}

/* 인스펙터 접이식 절 (호스트 buildAcSects()가 「앞길 노탐」·「앞길 기상」·「GNSS 환경」을 감싼다)
   — 사용자 지시 2026-08-25 「모바일로 보면 너무 길게 나오는데 각 목록당 접기 펼치기도」.
   머리줄 전체가 누름 대상이다(모바일에서 캐럿만 노리기 어렵다). */
.acsect { margin-top: 8px; }
.acsect + .acsect { margin-top: 6px; }
.acsecHd {
  display: flex; align-items: center; gap: 7px; cursor: pointer; user-select: none;
  margin: 0 -6px 4px; padding: 5px 6px; border-radius: var(--radius-sm);
  font-size: var(--fs-md); font-weight: 700; letter-spacing: .03em; color: var(--text-secondary);
  text-transform: uppercase;
}
.acsecHd:hover { background: var(--bg-hover); color: var(--text-primary); }
.acsecHd .t { flex: none; }
.acsecHd.warn .t { color: var(--accent-amber, #b26a00); }   /* 접혀 있어도 경고가 있는 절은 눈에 띈다 */
.acsecHd .acsum {   /* 접혔을 때만 채워진다 — 무엇을 접었는지 한 줄로 */
  flex: 1 1 auto; min-width: 0; font-size: var(--fs-xs); font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--text-secondary); opacity: .85;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.acsecHd .caret { margin-left: auto; flex: none; font-size: var(--fs-sm); opacity: .7; transition: transform var(--transition-fast, .15s); }
.acsect.fold .acsecHd .caret { transform: rotate(-90deg); }
.acsect.fold .acsecBody { display: none; }

/* ============================================================
   5. 지도 위 플로팅 조작 (좌하단 — 서랍 폭을 따라 움직인다)
   ============================================================ */
#mapCtl {
  /* ⚠ --drawer-w를 빼 두었다 (2026-09-01) — 메뉴 창이 떠 있는 창이 되어 지도를 밀지 않는다 */
  position: absolute; left: calc(var(--rail-w) + var(--gap));
  /* 아래 저작권 크레딧(.credit) 줄을 덮지 않게 한 줄만큼 띄운다 */
  bottom: calc(var(--gap) + 14px); top: auto; right: auto; z-index: 1200;
  display: flex; gap: 6px; align-items: center;
  transition: left var(--transition-smooth);
}
body.drawer-closed #mapCtl { left: calc(var(--rail-w) + var(--gap)); }
#mapCtl .mb {
  width: auto; margin: 0; padding: 7px 12px;
  background: var(--bg-surface-float); color: var(--text-secondary);
  border: 1px solid var(--border-medium); border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
  box-shadow: var(--shadow-md);
}
#mapCtl .mb:hover { background: var(--bg-hover); color: var(--accent-cyan); border-color: var(--accent-cyan); }
#mapCtl .mb.icon { width: 34px; min-width: 34px; padding: 7px 0; text-align: center; font-family: var(--font-mono); }
#mapCtl .mb.on { background: var(--accent-cyan); color: var(--text-inverse); border-color: var(--accent-cyan); }

/* ------------------------------------------------------------
   5a. 배경 전환 (우상단 — 위성 ↔ 심플)
   사용자 요청 2026-09-04 「위성보기 지도와 심플지도 보기 전환버튼을 메인지도 오른쪽 상단에」.
   ─ 생김새는 #mapCtl .mb와 같은 어휘(떠 있는 흰 알약·같은 테두리/그림자)를 쓰되, 두 칸이
     맞붙은 **분절 토글**이라 어느 쪽이 지금 켜져 있는지가 한눈에 보인다.
   ─ 오른쪽은 인스펙터(#acDetail, --insp-w)를 피한다 — 열리면 그 폭만큼 왼쪽으로 물러난다.
     ⚠ 바디 클래스는 `inspector-open`이다(index.html openDetail/closeDetail). 아래 #rcBar가
     쓰는 `insp-on`은 어디서도 붙지 않는 죽은 선택자이므로 따라 하지 않는다.
   ------------------------------------------------------------ */
#bgSwitch {
  position: absolute; top: calc(var(--hdr-total) + var(--gap)); right: calc(var(--gap) + var(--sa-r));
  z-index: 1200; display: flex; gap: 0;
  border: 1px solid var(--border-medium); border-radius: var(--radius-md);
  overflow: hidden; box-shadow: var(--shadow-md);
  background: var(--bg-surface-float);
  transition: right var(--transition-smooth);
}
body.inspector-open #bgSwitch { right: calc(var(--insp-w) + var(--gap) * 2 + var(--sa-r)); }
#bgSwitch .bgs {
  width: auto; margin: 0; padding: 7px 14px;
  background: transparent; color: var(--text-secondary);
  border: 0; border-left: 1px solid var(--border-subtle);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
}
#bgSwitch .bgs:first-child { border-left: 0; }
#bgSwitch .bgs:hover { background: var(--bg-hover); color: var(--accent-cyan); }
#bgSwitch .bgs.on { background: var(--accent-cyan); color: var(--text-inverse); }
#bgSwitch .bgs.on:hover { color: var(--text-inverse); }

/* ============================================================
   5b. 재생 바 (지도 하단 — 사용자 지시 2026-08-30
       「재생버튼은 메인화면 지도 하단에 표시하고 배속도 선택하여 빠르게 재생할지 선택」)
   ------------------------------------------------------------
   ⭐ 재생 조작은 **서랍이 아니라 지도에** 있어야 한다 — 되돌려 보는 동안 사용자가 보는 것은
     지도이고, 서랍은 닫을 수 있기 때문이다. 서랍에는 «무엇을 불러올지»(날짜·간격)만 남긴다.
   ─ #mapCtl과 같은 규약으로 서랍 폭(--drawer-w)을 따라 움직인다. 오른쪽은 인스펙터를 피한다.
   ─ 불러오기 전에는 없다(display:none) — features/replay-clock이 켠다.
   ============================================================ */
/* #rcBar는 rail/drawer/inspector를 피하는 «범위(투명 트랙)»일 뿐이고, 흰 알약(배경·테두리·
   그림자·padding)은 안쪽 .rcbPill이 갖는다 (사용자 지시 2026-09-01 「좌우 흰색 공백 자체를
   줄여줘」「재생바가 가운데 있도록」) — #rcBar를 흰 박스로 두고 padding만 주면, 흰 박스 자체는
   여전히 range 전체 폭이라 콘텐츠 양옆에 «빈 흰 여백»이 남는다. 흰 박스를 콘텐츠 폭(.rcbPill의
   width)만큼 줄이고, #rcBar는 justify-content:center로 그 안에서 가운데 배치만 담당한다. */
#rcBar {
  position: absolute; z-index: 1240;
  left: calc(var(--rail-w) + var(--gap));   /* 위와 같은 이유로 --drawer-w를 뺐다 */
  right: calc(var(--gap) + var(--sa-r));
  /* 저작권 크레딧·#mapCtl 줄을 덮지 않게 한 단 위로 */
  bottom: calc(var(--gap) + 52px);
  display: none; justify-content: center;
  transition: left var(--transition-smooth), right var(--transition-smooth);
}
#rcBar.on { display: flex; }
body.drawer-closed #rcBar { left: calc(var(--rail-w) + var(--gap)); }
body.insp-on #rcBar { right: calc(var(--insp-w) + var(--gap) * 2); }
/* ⭐ 알약 폭 70% (사용자 지시 2026-09-01 「지금보다 70%로 줄여」) — #rcBar 범위 안에서 이
   폭만큼만 흰 박스가 생기고, 나머지 30%(좌우 15%씩)는 지도가 그대로 보인다. */
.rcbPill {
  display: flex; align-items: center; gap: 10px;
  width: 70%; max-width: 100%; box-sizing: border-box;
  padding: 8px 12px;
  background: var(--bg-surface-float, rgba(255,255,255,.97));
  border: 1px solid var(--border-medium); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
#rcBar .rcb {
  /* ⚠ width:auto·margin 명시 필수 — app.css의 전역 button{width:100%;margin-top:8px}을 되돌리지
     않으면 재생 버튼이 바 전체 폭을 차지해 옆 컨트롤(시계·슬라이더·배속·종료)이 오른쪽으로
     밀려나 화면 밖으로 나간다(실측 2026-08-30 — 사용자 스크린샷: 재생 버튼만 보임).
     이 저장소의 다른 버튼 그룹(.sheet-back 등)도 같은 이유로 하나같이 width를 다시 적는다. */
  width: auto; margin: 0;
  font: inherit; font-size: var(--fs-md); cursor: pointer; white-space: nowrap;
  padding: 7px 12px; background: var(--bg-surface-elevated, #fff); color: var(--text-primary);
  border: 1px solid var(--border-medium); border-radius: var(--radius-md);
}
#rcBar .rcb:hover { background: var(--bg-hover); color: var(--accent-cyan); border-color: var(--accent-cyan); }
#rcBar .rcb.play { min-width: 84px; font-weight: 600; background: var(--accent-cyan); color: var(--text-inverse, #fff); border-color: var(--accent-cyan); }
#rcBar .rcb.play:hover { filter: brightness(1.06); color: var(--text-inverse, #fff); }
#rcBar #rcbClock { font-family: var(--font-mono, monospace); font-size: var(--fs-xl); font-weight: 700; min-width: 58px; text-align: center; }
/* ⭐ 타임라인이 가장 길어야 한다 (사용자 지시 2026-08-30) — .rcbPill 안에서 슬라이더만 남는
   폭을 전부 차지하고, 버튼·배속·날짜는 내용 크기로 고정한다(flex 0). 알약 자체의 폭(70%)이
   위 .rcbPill에서 이미 여백을 만들어 주므로, 슬라이더는 margin 없이 안쪽을 꽉 채운다. */
#rcBar #rcbSeek { flex: 1 1 auto; min-width: 240px; width: auto; height: 22px; cursor: pointer;
  margin: 0; accent-color: var(--accent-cyan); }
#rcBar .rcb, #rcBar select, #rcBar #rcbClock, #rcBar #rcbDate, #rcBar #rcbWx,
#rcBar .rcbChk { flex: 0 0 auto; }
/* 「이 기체만」 체크칸 (2026-09-01) — 3D 미니의 같은 칸과 짝이다.
   ⚠ 슬라이더가 남는 폭을 차지하도록 flex:0 0 auto(위)에 함께 넣는다. */
#rcBar .rcbChk {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-sm); color: var(--text-secondary); white-space: nowrap; cursor: pointer;
}
#rcBar .rcbChk input { width: auto; margin: 0; cursor: pointer; }   /* 전역 input 규칙 되돌리기 */
/* 「그 시각 기상 자료가 있나」 (2026-09-01) — 평소엔 눈에 안 띄는 회색, «없음»일 때만 주황.
   ⛔ 빨강을 쓰지 않는다: 자료가 없는 것은 고장이 아니라 사실일 수 있다(수집 이전 구간 등).
      빨강이 상시가 되면 경보 피로로 진짜 고장을 못 본다(admin_health.grade 주석과 같은 규율). */
#rcBar #rcbWx { font-size: var(--fs-sm); color: var(--text-tertiary); white-space: nowrap; cursor: help; }
#rcBar #rcbWx.bad { color: var(--status-warn, #b45309); font-weight: 600; }
#rcBar #rcbDate { font-size: var(--fs-sm); color: var(--text-tertiary); white-space: nowrap; }
#rcBar select { font: inherit; font-size: var(--fs-md); padding: 6px 8px; width: auto; border-radius: var(--radius-md);
  border: 1px solid var(--border-medium); background: var(--bg-surface-elevated, #fff); color: var(--text-primary); }
/* 화면낭독기 전용 라벨 — 이 정의가 없으면 #rcBar의 「배속」 라벨이 화면에 그대로 보인다
   (실측 2026-08-30 스크린샷: 배·속 두 줄로 노출). 표준 시각 숨김 패턴. */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* 좁은 화면 — 하단 탭바 위로 올리고, 서랍/인스펙터 폭을 따지지 않는다 */
@media (max-width: 760px) {
  #rcBar, body.drawer-closed #rcBar, body.insp-on #rcBar {
    left: calc(var(--gap) + var(--sa-l)); right: calc(var(--gap) + var(--sa-r));
    bottom: calc(var(--tabbar-total) + var(--gap));
  }
  .rcbPill { width: 100%; flex-wrap: wrap; gap: 8px; }   /* 좁은 화면은 줄바꿈되므로 70% 폭을 두지 않는다 */
  #rcBar #rcbDate { display: none; }          /* 좁은 화면에서는 날짜를 서랍에서 본다 */
}

/* ============================================================
   6. 실시간 안전경고 팝오버 (헤더 알약 → 목록)
   ============================================================ */
#alertPopover {
  position: absolute; top: calc(var(--hdr-total) + 6px); right: calc(var(--gap) + var(--sa-r));
  width: 380px; max-height: calc(100% - var(--hdr-total) - 28px);
  z-index: 1500; display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-surface-elevated);
  /* backdrop-filter 제거 (2026-08-25) — 이 패널의 background는 --bg-surface-elevated로
     **완전 불투명**(#ffffff / 다크 #131c2e)이라 blur 결과가 자기 배경에 100% 가려진다.
     픽셀 대조로 확인: 제거 전후 #drawer 524,280px 중 다른 픽셀 0개.
     비용만 남는 이유 — blur는 요소를 독립 렌더 표면으로 만들어, 뒤 배경(지도)이 바뀔 때마다
     그 영역을 다시 스냅샷·가우시안 처리한다. 지도는 팬·줌 + 항적 캔버스 1Hz로 계속 바뀐다.
     실측 약 7ms/프레임. 되살리려면 배경을 반투명으로 바꾸는 것이 먼저다. */
  border: 1px solid var(--border-medium); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
#alertPopover.closed { display: none; }
.popover-header {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid var(--border-subtle);
  font-size: var(--fs-md); font-weight: 700;
}
.popover-header .sec {
  width: 30px; height: 30px; margin: 0; padding: 0;
  background: none; border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
  color: var(--text-secondary); font-size: var(--fs-md); font-weight: 700; line-height: 1; cursor: pointer;
}
.popover-header .sec:hover { background: var(--bg-hover); color: var(--status-danger); }
#alertWrap { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 10px 12px 14px; }
#alertWrap .agrp:first-child { margin-top: 0; }

/* 팝오버 바닥 줄 — «경보 창 자동으로 열지 않기» (사용자 요청 2026-08-25).
   목록과 같이 스크롤되면 경고가 많을 때 손에 안 잡힌다 → flex:none으로 바닥에 고정. */
.popover-foot {
  flex: none; display: flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-top: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--text-secondary); font-size: var(--fs-sm); user-select: none;
}
.popover-foot label { display: flex; align-items: center; gap: 7px; cursor: pointer; }
.popover-foot label:hover { color: var(--text-primary); }
.popover-foot input { margin: 0; flex: none; cursor: pointer; }

/* ============================================================
   7. 지도 팝업 — 시안 A 카드 조형으로 통일 (위치 고정은 그대로)
   ============================================================ */
.leaflet-popup-content-wrapper {
  background: var(--bg-surface-elevated); color: var(--text-primary);
  border: 1px solid var(--border-medium); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.leaflet-popup-tip { background: var(--bg-surface-elevated); box-shadow: none; }
.leaflet-tooltip { background: var(--bg-surface-elevated); color: var(--text-primary); border-color: var(--border-medium); }

/* ============================================================
   8. 반응형 — 창이 줄면 배치가 따라 바뀐다 (사용자 요청 2026-08-24)
   ============================================================ */

/* 8-1. 노트북 폭 — 서랍·인스펙터를 좁힌다 */
@media (max-width: 1280px) {
  :root { --drawer-w: 340px; --insp-w: 330px; }
  body[data-tab="fcst"] { --drawer-w: 400px; }   /* 좁은 화면에서도 예측표는 조금 더 준다 */
  body[data-tab="apt"]  { --drawer-w: 430px; }   /* 도착지 열 ~135px — 공항명이 안 잘리는 하한 */
  body[data-tab="ops"]  { --drawer-w: 470px; }   /* 나머지는 표 자체의 안쪽 스크롤(.os-scroll)로 넘긴다 */
  body[data-tab="fuel"] { --drawer-w: 620px; }
  body[data-tab="queue"]{ --drawer-w: 560px; }
  body[data-tab="wxg"]  { --drawer-w: 620px; }   /* 2열로 접힌다 */
  #sqCov { display: none; }
}

/* 8-2. 태블릿 — 인스펙터가 서랍을 덮는다. 상태 텍스트는 접는다 */
@media (max-width: 1024px) {
  :root { --drawer-w: 320px; --insp-w: 310px; }
  body[data-tab="fcst"] { --drawer-w: 320px; }   /* 태블릿부터는 넓힘 해제 — 지도가 더 밀리면 안 된다 */
  /* 공항 보드는 넓힘을 완전히 풀면 도착지 열이 20px대로 무너져 글자가 아예 안 보인다 —
     예측표와 달리 표가 아니라 «안내판»이라 잘린 목적지는 쓸모가 없다. 390px까지만 양보한다 */
  body[data-tab="apt"]  { --drawer-w: 390px; }
  body[data-tab="ops"]  { --drawer-w: 390px; }
  body[data-tab="fuel"] { --drawer-w: 420px; }
  body[data-tab="queue"]{ --drawer-w: 400px; }
  body[data-tab="wxg"]  { --drawer-w: 400px; }   /* 1열로 접힌다 */
  .system-status-bar { gap: 8px; }
  #opsStat .s, #opsStat #stSrc { display: none; }
  #alertSum .n { display: none; }
}

/* 8-3. 모바일 — 레일은 하단 탭바, 서랍·인스펙터·경고는 바텀시트 */
@media (max-width: 760px) {
  :root { --hdr-h: 48px; --rail-w: 0px; --tabbar-h: 58px; --gap: 10px; }

  .top-header { gap: 8px; padding: 0 10px; }
  .brand-title { font-size: var(--fs-xl); }
  #sectorSum { max-width: 150px; font-size: var(--fs-sm); padding: 3px 8px; }
  .system-status-bar { flex: 1 1 auto; }
  #opsStat { font-size: var(--fs-sm); }
  #opsStat #stMsg, #opsStat #stT { display: none; }
  .header-actions .hb.wide { display: none; }   /* 전체화면은 모바일에서 의미가 적다 */

  /* 6.1~6.5인치 폰(CSS 뷰포트 360~430px — iPhone 15/16 393·Pro Max 430·Galaxy S24 360~412) 최적화
     (사용자 지시 2026-08-25 「모바일화면은 6.1~6.5인치에 최적화」·「화면 배치와 팝업창들의 너비와 겹침 없이」).
     ⚠ 실측(Playwright 393×852·360×780 에뮬레이션): 헤더 오른쪽 .header-actions가 437px까지 삐져나가
        **레이아웃 뷰포트가 439px로 늘어나고 화면 전체가 ~10% 축소**됐다 — 모바일 브라우저는 넘치는 내용
        폭에 맞춰 뷰포트를 넓힌다. 바텀시트·탭바·헤더 자체는 겹치지 않았다(#drawer 266~794 · #bar 794~852).
     ① html/body·헤더의 가로 넘침을 막는다(뷰포트가 다시는 안 늘어난다)
     ② 경고 알약을 줄어들 수 있게 — flex 0 1 auto + 말줄임(👁 아이콘은 ::before라 남는다). 종전 flex:none이
        문구(「감시 중 — 경고 없음」 ~135px)를 통째로 지켜 버튼들을 밀어냈다
     ③ 상태줄은 내용만큼만(빈 채로 자리를 먹어 알약을 밀지 않게), 버튼은 오른쪽 끝에 */
  html, body { overflow-x: hidden; }
  .top-header { overflow: hidden; gap: 6px; padding: 0 8px; }
  .brand-section { gap: 6px; }
  .system-status-bar { flex: 0 1 auto; }
  #alertSum { flex: 0 1 auto; min-width: 44px; display: block; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; padding: 4px 9px; }
  #alertSum::before { margin-right: 5px; }
  .header-actions { gap: 4px; margin-left: auto; }
  .header-actions .hb { padding: 5px 7px; }
  /* 시트 안쪽 여백 — 360px에서 4px라도 내용에 준다. 가로 모드 노치 쪽만 더 들인다 */
  #sheet { padding: 10px calc(12px + var(--sa-r)) 16px calc(12px + var(--sa-l)); }
  #acBody { padding-left: calc(14px + var(--sa-l)); padding-right: calc(14px + var(--sa-r)); }
  #alertWrap { padding-left: calc(12px + var(--sa-l)); padding-right: calc(12px + var(--sa-r)); }
  /* 시트 안의 넓은 표(예측교통량·공항 보드)는 **시트 안에서** 옆으로 스크롤한다 —
     시트 밖으로 넘치면 화면 전체가 가로로 밀려 배치가 무너진다(실측: 360px에서 섹터표가 잘렸다).
     ⚠ overflow-x만 주면 세로 스크롤이 함께 잘리므로 overflow-y는 visible로 못 둔다 → auto. */
  #sheet .fbtbl, #sheet table, #sheet .fcstTbl { max-width: 100%; }
  /* 지도 팝업 — Leaflet이 maxWidth(공항 330·기체 360)를 inline width로 박는다. 360px 폰에서는 팝업이
     화면보다 넓어 autoPan이 좌우로 흔들리고 한쪽이 잘린다. 화면−60px로 캡(inline width보다 max-width가 이긴다) */
  .leaflet-popup-content { max-width: calc(100vw - 60px - var(--sa-l) - var(--sa-r)) !important; }
  /* 예측 섹터 팝업(#fcstPop — 호스트 JS가 inline으로 width:720px·left:50%를 박는다) —
     폰에서는 **헤더와 탭바 사이를 통째로 쓰는 한 장짜리 화면**으로 만든다 (2026-08-27).
     종전에는 헤더 바로 밑에 «떠 있는 카드»였다: 아래 서랍 시트가 그대로 보이는 채로 겹쳐
     어느 창의 목록인지 구분되지 않았다(사용자 신고 「팝업과 섞여서 잘 안보임」).
     안의 표(min-width:780px)는 이 안에서 옆으로 스크롤한다(overflow:auto 유지).
     !important = inline 스타일을 이기기 위함(머리 끌기(pointerdown)가 left/top을 다시 박아도
     폰에서는 자리가 고정된다 — 작은 화면에서 창을 끌어 옮길 이유가 없다). */
  #fcstPop { left: calc(6px + var(--sa-l)) !important; right: calc(6px + var(--sa-r));
    width: auto !important; max-width: none !important;
    transform: none !important; top: calc(var(--hdr-total) + 6px) !important;
    bottom: calc(var(--tabbar-total) + 6px);
    max-height: none !important;
    border-radius: var(--radius-lg); }

  /* 하단 가로 탭바 — 높이는 «버튼 줄 + 홈 인디케이터».
     ⚠ padding-bottom에 --sa-b를 넣는 것이 핵심이다: 아이폰 홈 인디케이터(34px)는 화면 맨 아래
       가로 막대라, 안 넣으면 탭 글자(「예측」·「공항」…)가 그 막대에 깔려 읽히지 않고
       그 자리를 누르면 브라우저가 «홈으로 쓸어올리기»로 가로챈다. */
  #bar.nav-rail {
    left: 0; right: 0; top: auto; bottom: 0; width: auto;
    height: var(--tabbar-total);
    flex-direction: row; justify-content: flex-start;
    gap: 2px;
    padding: 4px calc(6px + var(--sa-r)) var(--sa-b) calc(6px + var(--sa-l));
    overflow-x: auto; overflow-y: hidden;
    /* 세로 레일이 가로 탭바가 되어도 색은 같은 --rail-* 한 벌을 쓴다 (배경은 위 규칙이 이미 준다) */
    border-right: 0; border-top: 1px solid var(--rail-border);
    box-shadow: 0 -4px 16px rgba(0,0,0,.10);
    -webkit-overflow-scrolling: touch;
  }
  /* 탭 전부(7개, min 44px×7+여백 ≈ 330px)가 가로로 다 들어오게 균등 분배 —
     가로 스크롤은 «메뉴가 더 있다»는 걸 숨긴다.
     height:100%는 쓰지 않는다 — 부모 높이에 --sa-b가 섞여 있어 버튼이 인디케이터까지 늘어난다. */
  #bar.nav-rail button { flex: 1 1 0; width: auto; min-width: 44px; height: auto; align-self: stretch; }
  #bar.nav-rail button.on::before { left: 6px; right: 6px; top: auto; bottom: -4px; width: auto; height: 3px; border-radius: 4px 4px 0 0; }
  /* 가로 탭바에서는 구분선도 눕는다 — 세로 폭(1px)이 아니라 세로 높이(1px)의 세로줄로 */
  #bar.nav-rail .rail-sep { width: 1px; height: 20px; margin: 0 3px; align-self: center; }
  /* 그룹 캡션은 세로 레일 전용 — 가로 탭바는 이미 폭이 빠듯해 라벨까지 끼우면 버튼 하나처럼
     끼어들어 탭 수가 늘어난 것으로 오인된다(flex:1 1 0 균등분배 대상이 되어 폭도 뺏는다). */
  .rail-glabel { display: none; }

  /* 바텀시트 손잡이 — 잡아 끌어 높이를 조절한다 (사용자 지시 2026-08-25
     「모바일의 경우 하단에서 창이 위로 올라가는데 이때 창 높이도 조절이 가능하면 좋을듯」).
     ⚠ touch-action:none 필수 — 없으면 브라우저가 세로 끌기를 «페이지 스크롤»로 먼저 채 간다.
     톡 누르면(끌지 않으면) 38% → 62% → 90% 로 한 단씩 올라간다(호스트 JS). */
  .sheet-grip {
    display: flex; align-items: center; justify-content: center; flex: none;
    /* 28px — 손끝으로 «잡는» 것이라 눈에 보이는 막대(4px)보다 훨씬 넉넉해야 한다.
       종전 22px은 잡으려다 시트 안 목록을 스크롤하는 일이 잦았다. */
    height: 28px; cursor: ns-resize; touch-action: none; user-select: none;
  }
  .sheet-grip::before {
    content: ''; width: 44px; height: 5px; border-radius: 3px;
    background: var(--border-medium);
    transition: background var(--transition-fast), width var(--transition-fast);
  }
  .sheet-grip:active::before, .sheet-grip.on::before, .sheet-grip:focus-visible::before {
    background: var(--accent-cyan); width: 60px;
  }
  .sheet-grip:focus { outline: none; }

  /* 서랍 = 바텀시트 */
  #drawer.side-drawer {
    left: 0; right: 0; width: auto; top: auto; bottom: var(--tabbar-total);
    /* --sheet-h = 사용자가 손잡이로 정한 높이(호스트 JS가 px로 실어 준다). 없으면 종전 62vh */
    height: var(--sheet-h, 62vh); max-height: calc(100% - var(--hdr-total) - var(--tabbar-total));
    border-right: 0; border-top: 1px solid var(--border-medium);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }
  #drawer.side-drawer.closed { transform: translateY(100%); }

  /* 인스펙터 = 바텀시트 (서랍보다 위)
     ⚠ 기본은 auto다 — 내용만큼만 차지한다. 손잡이를 한 번이라도 끌면 그때부터 --sheet-h가 붙는다.
        처음부터 고정 높이를 주면 짧은 기체 정보에도 빈 상자가 절반씩 남는다. */
  #acDetail.inspector-panel {
    left: 0; right: 0; width: auto; top: auto; bottom: var(--tabbar-total);
    height: var(--sheet-h, auto);
    max-height: calc(100% - var(--hdr-total) - var(--tabbar-total));
    z-index: 1270; border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }

  /* 경고 목록 = **바텀시트** (2026-08-27, 종전 «상단 전체폭 시트»에서 바꿈).
     바꾼 이유 두 가지 —
       ① 6.5~6.9인치 폰에서 화면 위쪽은 한 손으로 닿지 않는다. 닫기(✕)가 거기 있으면
          기기를 고쳐 쥐어야 창을 닫을 수 있었다.
       ② 위에 붙어 있으면 아래 서랍 시트와 **위아래로 동시에** 떠서 지도가 가운데 몇십 px
          띠만 남았다 — 사용자 신고 2026-08-27 「메뉴를 누르면 겹쳐서 팝업과 섞여서 잘 안보임」의
          가장 뚜렷한 형태(실측 360×780: 지도가 y=214~238, 24px만 남았다).
     이제 서랍·인스펙터와 같은 자리에서 같은 방식으로 뜨고, 아래 «한 번에 한 장» 규칙이
     동시 노출 자체를 막는다. */
  #alertPopover {
    left: 0; right: 0; width: auto; top: auto; bottom: var(--tabbar-total);
    height: var(--sheet-h, auto);
    max-height: calc(100% - var(--hdr-total) - var(--tabbar-total));
    z-index: 1280;                                        /* 서랍(1250)·인스펙터(1270)보다 위 — 안전 알림이 가장 앞 */
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }

  /* ── 바텀시트는 한 번에 한 장만 (2026-08-27) ───────────────────────────────
     사용자 신고: 「메뉴를 누르면 겹쳐서 팝업과 섞여서 잘 안보임」.
     서랍(메뉴)·기체 인스펙터·경고 목록은 셋 다 화면 아래에서 올라오는 같은 자리를 쓴다.
     종전에는 z-index만 달라 **뒤 시트가 앞 시트 밑으로 삐져나온 채** 함께 보였다 —
     둥근 모서리가 두세 겹 겹치고, 어느 창의 내용인지 구분이 안 됐다.
     이제 호스트 JS가 «지금 맨 앞 시트»를 body[data-sheet]에 실어 주고, 여기서 나머지를
     완전히 숨긴다.
     ⛔ 숨기는 것은 «보이기»뿐 — 논리 상태(열림·선택한 탭·스크롤 위치)는 그대로 살아 있어,
        앞 시트를 닫으면 뒤 시트가 있던 그대로 돌아온다.
     ⚠ visibility가 아니라 display:none을 쓴다. 숨긴 시트가 자리를 차지하면
        호스트 fitPad()가 그 높이를 지도 여백으로 계산해 화면이 엉뚱하게 맞춰진다. */
  body[data-sheet="insp"]   #drawer.side-drawer,
  body[data-sheet="alerts"] #drawer.side-drawer { display: none !important; }
  body[data-sheet="drawer"] #acDetail.inspector-panel,
  body[data-sheet="alerts"] #acDetail.inspector-panel { display: none !important; }
  body[data-sheet="drawer"] #alertPopover,
  body[data-sheet="insp"]   #alertPopover { display: none !important; }

  /* 뒤로 밀린 시트가 있다는 것을 알린다 — 앞 시트 머리에 «＜ 돌아가기» 칩이 뜬다(호스트 JS가 넣는다).
     이 표시가 없으면 «메뉴가 사라졌다»로 읽힌다(실제로는 뒤에 살아 있다). */
  .sheet-back {
    /* ⚠ width:auto·margin 명시 필수 — app.css의 전역 button{width:100%;margin-top:8px}을 되돌리지 않으면
       칩이 머리줄 절반을 차지한다(실측: 786px 폭에서 330px). 이 저장소의 다른 헤더 버튼들도 같은 이유로
       하나같이 width를 다시 적는다(.header-actions .hb · .drawer-header .sec …). */
    display: inline-flex; align-items: center; gap: 4px; flex: none;
    width: auto; margin: 0 6px 0 0; padding: 4px 9px; min-height: 30px;
    background: var(--bg-sunken); color: var(--text-secondary);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-full);
    font-family: inherit; font-size: var(--fs-xs); font-weight: 700; cursor: pointer;
    max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .sheet-back::before { content: '‹'; font-size: var(--fs-lg); line-height: 1; }
  /* 칩이 들어오면 머리가 [칩][제목][✕] 세 덩이가 된다 — space-between이 제목을 한가운데로 밀어
     칩과 붙어 읽힌다. 왼쪽으로 모으고 ✕만 오른쪽 끝에 남긴다. */
  body[data-sheet] .popover-header, body[data-sheet] .drawer-header,
  body[data-sheet] #acDetail .acHd { justify-content: flex-start; gap: 8px; }
  body[data-sheet] .popover-header > .sec, body[data-sheet] .drawer-header > .sec { margin-left: auto; }

  /* 지도 조작 — 우측 세로 스택 (탭바·시트 위). 가로 모드 노치를 피해 오른쪽 안전영역만큼 더 들인다 */
  #mapCtl, body.drawer-closed #mapCtl {
    left: auto; right: calc(var(--gap) + var(--sa-r)); bottom: calc(var(--tabbar-total) + var(--gap));
    flex-direction: column; align-items: flex-end;
  }
  /* 시트가 한 장이라도 떠 있으면 지도 조작은 가린다 (시트 뒤에 반쯤 걸려 눌리지 않는 버튼이 된다).
     ⚠ 종전 `body:not(.drawer-closed)`는 서랍만 봤다 — 인스펙터·경고 시트가 떠 있을 때는
       버튼이 시트 밑에 깔린 채 남아 있었다. */
  body[data-sheet] #mapCtl { display: none; }

  /* 배경 전환(위성↔심플)은 우상단 그대로 둔다 — 모바일에서 인스펙터는 **바텀시트**라
     데스크톱처럼 --insp-w만큼 왼쪽으로 물러날 이유가 없다(물러나면 화면 한가운데로 온다). */
  body.inspector-open #bgSwitch { right: calc(var(--gap) + var(--sa-r)); }

  .credit { display: none; }
  .copyright { display: none; }
  /* 좁은 화면은 #mapCtl이 우측으로 옮겨 앉는다(위 725행) — 범례까지 얹으면 겹친다. 큰 화면 전용으로 접는다. */
  .rcMapLegend { display: none; }
  /* 손끝으로 누를 수 있게 (호버가 없는 기기에서는 탭이 유일한 방법이다).
     24px + 주변 여백 — 20px은 실기에서 빗나가는 일이 잦았다 */
  .hlp { width: 24px; height: 24px; font-size: var(--fs-sm); }
  /* 시트 안 닫기(✕)·머리 버튼도 손끝 크기로 */
  .drawer-header .sec, .popover-header .sec, #acDetail .acHd .sec { min-width: 36px; min-height: 36px; }
  .drawer-header, .popover-header { padding-left: calc(14px + var(--sa-l)); padding-right: calc(14px + var(--sa-r)); }
  #acDetail .acHd { padding-left: calc(14px + var(--sa-l)); padding-right: calc(14px + var(--sa-r)); }
}

/* 8-4. 소형 폰 — 브랜드와 상태를 더 줄인다.
   기준을 460 → 400px로 내렸다 (2026-08-27, 사용자 지시 「최신 6인치 이상에 최적화」):
   요즘 6인치대는 CSS 폭 402(iPhone 16 Pro)·412(Galaxy S24+/Ultra)·440(16 Pro Max)이라
   460 기준은 **그 기기 전부를 «소형»으로 몰아** 접속 상태줄(#opsStat)을 통째로 지웠다.
   400px 아래(Galaxy S24 360·구형 폰)에서만 줄인다. */
@media (max-width: 400px) {
  .brand-title { font-size: 0; }
  .brand-title::after { content: 'AN'; font-size: var(--fs-xl); -webkit-text-fill-color: var(--accent-cyan); }
  #opsStat { display: none; }
  #sectorSum { max-width: 108px; }
}
/* 401~460px — 브랜드는 «AN»으로 줄이되 접속 상태(수신 기체 수)는 남긴다.
   기체 수는 «지금 화면이 살아 있는가»의 유일한 신호라, 지울 것 중 마지막 순서다. */
@media (min-width: 401px) and (max-width: 460px) {
  .brand-title { font-size: 0; }
  .brand-title::after { content: 'AN'; font-size: var(--fs-xl); -webkit-text-fill-color: var(--accent-cyan); }
  #sectorSum { max-width: 132px; }
}

/* 8-5. 높이가 낮은 창(가로 모드 폰) — 시트를 더 낮게 */
@media (max-height: 520px) and (max-width: 900px) {
  #drawer.side-drawer { height: calc(100% - var(--hdr-total) - var(--tabbar-total)); }
}

/* ============================================================
   9. 지도 맞춤 «자» — 보이지 않지만 skyroute.js uiInsets()가 재는 요소
   ------------------------------------------------------------
   opacity:0이지만 display/visibility는 살아 있다 (uiInsets는 그 둘만 본다).
   모바일에서는 display:none → 좌우 여백을 만들지 않는다(그 배치는 위/아래이므로,
   호스트의 fitPad()가 따로 계산한다).
   ============================================================ */
.map-ruler {
  position: absolute; top: var(--hdr-total); height: 8px;
  opacity: 0; pointer-events: none; z-index: 0;
}
/* ⚠ 지도 맞춤 여백의 «자» — 떠 있는 창은 여기 안 센다(레일만 상시 왼쪽을 차지한다) */
#insetL { left: 0; width: var(--rail-w); }
body.drawer-closed #insetL { width: var(--rail-w); }   /* 위와 같은 값 — 남겨 둔다(무해) */
#insetR { right: 0; width: 1px; }
body.inspector-open #insetR { width: var(--insp-w); }
@media (max-width: 760px) { .map-ruler { display: none; } }

