/* 24K CLUB 회원 웹 — 지도 기반.
 *
 * 토큰은 앱 디자인 v4.4 그대로다(검정·흰색·골드 #F5C542, Pretendard).
 * **한 코드로 PC 와 폰을 다 본다.** 폰은 지도 위에 카드가 올라오고,
 * PC 는 지도 옆에 목록이 선다 — 화면 넓이 하나로 갈린다.
 */

:root {
  --bg: #f4f4f5;
  --card: #fff;
  --ink: #111214;
  --sub: #7a7d85;
  --line: #ececef;
  --y: #f5c542;
  --y2: #ffe38a;
  --point: #b7860b;
  /* 담긴 찜을 표시하는 빨강 — **하트에만** 쓴다(2026-09-08 지시). 금색이던 것을
     빨강으로 돌렸다. `--red`(#ff4d4f)·`--danger-ink`(#c2262a)를 안 쓰고 값을 따로
     두는 이유: 저 둘은 「잘못됐다·위험하다」를 뜻하는 자리(배지·경고 판)에 붙어 있어,
     한쪽을 만지면 담긴 하트까지 같이 변한다. 담긴 것은 사고가 아니다.
     흰 판(카드 머리·찜 창) 위에서 읽히는 진하기로 고른다 — 두 자리 다 흰 판이다. */
  --fav-on: #e23744;
  --night: #0e0e11;
  --night2: #1a1a1f;
  --night-line: #26262c;
  --night-sub: #8d9099;
  --night-body: #c8cad1;
  /* ── 판 위의 색 여섯 — **판이 검은지 흰지에 따라 통째로 갈린다**(2026-09-07 지시) ──
   *
   * 상세 카드의 글 판(매장정보·리워드·홍보영상·방문후기)과 조회 통계 창이 흰 판으로
   * 바뀌었다. 그 판들 안의 글자는 **검은 판을 전제로 흰색·밝은 금색이 박혀 있었다** —
   * 판만 희게 하면 그 글자가 그대로 안 읽힌다(흰 글씨가 흰 바탕에 눕는다).
   *
   * 그래서 색을 자리마다 고치지 않고 **여기 여섯 값으로 모은다.** 아래 「흰 판」
   * 규칙(`.vinfo` 묶음)이 이 여섯을 다시 정의하면, 그 판 **안쪽 전부**가 한 번에 따라온다.
   * 여기 적힌 값은 **검은 판의 값**이다 — 가격표·테이블맵·그림 액자·영상 재생기가
   * 아직 검은 판이라(그림을 넣는 자리다) 이 값들이 그대로 산다.
   *
   * ⚠ 이 여섯을 안 쓰고 `#fff`·`var(--y)` 를 자리에 그대로 적으면, 그 한 줄만
   *   흰 판에서 조용히 안 읽히게 된다. 오류는 한 줄도 안 난다. */
  --panel-ink: #fff;                    /* 판 위에서 가장 진한 글자 */
  --panel-key: var(--y);                /* 머리글·강조 **글자**의 금색 */
  --panel-mark: var(--y);               /* 채워 그리는 금색 — 별·막대(글자가 아니다) */
  --panel-bar: rgba(245, 197, 66, 0.38); /* 꼭대기가 아닌 막대 */
  --panel-keyline: rgba(245, 197, 66, 0.45); /* 금색 테두리 */
  --panel-keysoft: rgba(245, 197, 66, 0.07); /* 금색 옅은 바탕 */
  --panel-red: var(--red);
  --panel-green: var(--green);
  --panel-edge: transparent;            /* 판 테두리 — 검은 판은 안 두른다 */
  /* 지도 말풍선 바탕 — **반투명**(2026-09-05 지시). 아래 지도가 비쳐야 하므로
     불투명 색을 그대로 두지 않는다. 다만 밝은 타일(강·큰길) 위에서도 흰 이름과
     노란 금액이 읽히는 선까지만 내린다 — 골드 바탕은 글자가 검정이라 조금 더 옅다.
     아래 둘은 **시 이름표(`.city`)의 몫**이다. */
  --pin-bg: rgba(14, 14, 17, 0.62);
  --pin-bg-on: rgba(245, 197, 66, 0.72);
  /* 캠페인·클럽 말풍선은 **색과 흐리는 정도를 따로 든다.** 몸통과 꼬리를 한 장에
     그리고 그 한 장을 한 번만 흐리기 때문이다(2026-09-07 지시, `.tail` 규칙 참고).
     보이는 색은 위 두 값과 **같다** — 색만 불투명하게 적고 흐리기를 옆에 떼어 놓았다. */
  --pin-c: #0e0e11;           --pin-a: 0.62;
  --pin-c-on: #f5c542;        --pin-a-on: 0.72;
  --pin-c-blocked: #5b5b62;   --pin-a-blocked: 0.62;
  --pin-c-many: #2b2b33;      --pin-a-many: 0.66;
  --pin-c-lounge: #4b1d63;    --pin-a-lounge: 0.66;
  --pin-c-lounge-on: #ff7ad9; --pin-a-lounge-on: 0.72;
  /* 지도 표식 배수 — **배율이 바뀔 때마다 map.js 가 이 한 값을 갈아 끼운다**
     (`syncZoomLook`, 사다리는 `config.js` 의 `markerScale`). 말풍선·캐릭터·
     누르는 자리가 다 이 값을 타므로 한 덩어리로 커진다.
     여기 적힌 1 은 지도가 아직 안 뜬 판의 값이다 — 없으면 `calc()` 이 통째로
     무효가 되어 표식이 제자리에서 벗어난다. */
  --mz: 1;
  /* **고른 캐릭터가 서는 배수 — 배율을 안 탄다**(2026-09-07 세 번째 지시:
     「어느 거리에서 눌러도 20m 에서 누른 그 크기로」). 사다리의 맨 아래 칸
     (20m = 레벨 1)의 배수를 map.js 가 여기 한 번 박아 준다 — `--mz` 처럼
     배율마다 갈아 끼우는 값이 **아니다.**
     ⚠ 손으로 적지 않는다. `config.js` 의 `markerScale[1]` 을 고치면 이 값도
     따라 움직여야 하고, 그래서 JS 가 넣는다. 여기 적힌 1.86 은 지도가 아직
     안 뜬 판의 값이다(없으면 `calc()` 이 통째로 무효가 되어 고른 캐릭터가
     제자리에서 벗어난다). */
  --mzp: 1.86;
  /* 말풍선만의 배수 — **캐릭터는 안 탄다**(2026-09-05 지시: 말풍선을 모든 배율에서
     20% 줄인다, 폰·PC 둘 다. 같은 날 **한 번 더 10%** — 0.8 × 0.9 = 0.72.
     2026-09-09 지시로 **모든 배율에서 다시 25%** — 0.72 × 0.75 = 0.54).
     `--mz` 위에 곱해지므로 사다리는 그대로 두고 말풍선 한 갈래만 줄어든다.
     **여기 한 값이 모든 줌레벨을 덮는다** — 배율표(`config.js` 의 `markerScale`)를
     칸마다 고치지 않는다. 고른 것(1.26배)도 이 값 위에 곱해지니 같이 따라온다.
     캐릭터(`.mface`)에는 일부러 안 붙인다 — 붙이면 캐릭터까지 같이 작아진다.
     `.pin`·`.venue` 는 이 값만큼 작아지니, 그 안에서 캐릭터 자리를 맡는 `::after` 는
     **이 값으로 나눠** 실제 크기를 되돌린다 — 안 나누면 캐릭터 발치가 안 눌린다
     (0.72 면 폰에서 59px 중 16px 이 죽었다. 0.54 면 27px 이라 더 크게 죽는다 —
     나눗셈이 이미 그 자리에 있으니 값만 바꾸면 된다). 올라서는 높이(안 고른 것
     75·폰 59, 고른 것은 `--mzp` 로 셈한다)는 캐릭터 키라 여기에 곱하지 않는다. */
  --bz: 0.54;
  /* 같은 자리에 겹쳐 선 표식을 벌리는 **한 칸의 폭**(`fan.js`·map.js `place`).
     한 건물에 든 클럽들은 좌표가 글자 하나까지 같아서 그냥 두면 통째로 포개진다.
     **캐릭터 폭(52px)보다 조금 넓다** — 벌려 놓고도 팔이 겹치면 벌린 값이 없다.
     여기를 고치면 벌어지는 폭이 통째로 바뀐다(칸 수는 JS, px 는 여기 한 곳).
     폰은 캐릭터가 42px 로 작아지니 아래 미디어쿼리에서 같이 줄인다. */
  --fan: 56px;
  --green: #22c55e;
  --red: #ff4d4f;
  --chip-bg: #f1f1f3;
  --chip-ink: #44464c;
  --dim-bg: #e4e4e7;
  --dim-ink: #a1a4ad;
  --soft: #f7f7f8;
  --warn-bg: #fff6d6;
  --warn-ink: #5a4200;
  --warn-line: #f0e2b0;
  --danger-bg: #ffeded;
  --danger-line: #f3c2c2;
  --danger-ink: #c2262a;
  --danger-sub: #7a2a2c;
  --request-bg: #fff9e6;

  /* 하단 바를 걷었다(2026-09-05). 자리를 비워 두던 값이라 0 이다.
     변수는 남긴다 — 부르는 자리가 여럿이고, 다시 세울 때 여기 한 곳만 고치면 된다. */
  --tabbar: 0px;
  /* 롤링바 높이. 카드·탭 화면이 이만큼 내려와 **띠 뒤로 지나가지 않는다.** */
  --roll: 26px;
  /* PC 에서 지도 **왼쪽에 서는 창**의 폭. 고른 매장 카드(.panel)와
     예약·채팅 화면(.page)이 같은 자리에 같은 크기로 뜬다 — 둘이 다른 값을
     쓰면 열고 닫을 때 창이 옆으로 튀어 보인다. 닫기 단추 자리도 이 값에서 잰다. */
  --sidew: 380px;
  --safe-b: env(safe-area-inset-bottom, 0px);
}

/* ── 흰 판 (2026-09-07 지시: 「검정 바탕으로 디자인한 것 모두 흰 배경으로」) ──
 *
 * 검은 판이었던 자리를 **여기 한 곳에서** 흰 판으로 돌린다. 자리마다 색을 고치지
 * 않는 이유는 그 자리가 백 군데가 넘어서다 — 한 줄만 놓쳐도 흰 바탕에 흰 글씨가
 * 남고, 그건 오류가 한 줄도 안 나는 종류의 고장이다(눈으로만 잡힌다).
 *
 * **여기 안 드는 검은 자리 = 그림이 들어가는 자리.**
 *   · 가격표 · 테이블맵(`.mfall` 맨몸) — 매장이 올리는 종이·도면이 거의 다 검다.
 *     흰 액자에 두면 그림 가장자리가 어디까지인지 안 보인다(지시받은 예외).
 *   · 그림 액자(`.mframe`) · 크게 보기(`.mviewer`) · 영상 재생기(`.ytv` 맨몸) ·
 *     지도가 안 뜬 판(`.mapfail`) — 전부 그림·영상을 얹는 판이다.
 *
 * ⚠ `.mfall` 은 **가격표·테이블맵과 홍보영상·방문후기가 나눠 쓴다.** 그래서 여기서
 *   `.mfall.ytfall`·`.mfall.rvfall` 두 짝만 집는다 — `.mfall` 을 통째로 집으면
 *   가격표·테이블맵까지 희어져 지시받은 예외가 깨진다.
 *
 * 판 안쪽 글자는 `--panel-*` 여섯과 `--night-*` 다섯을 타고 통째로 따라온다.
 * 새 규칙을 판 안에 더할 때도 그 열하나만 쓴다 — `#fff` 를 직접 적으면 그 줄만 죽는다. */
.vinfo,
.mfall.ytfall,
.mfall.rvfall,
.rvw-box,
.vst-card,
.card.night {
  --night: #fff;
  --night2: #f5f5f7;
  --night-line: var(--line);
  --night-sub: #696c74;
  --night-body: #3f4147;
  --panel-ink: var(--ink);
  /* 금색 글자는 **한 단 낮춘다.** `--y`(#f5c542)를 흰 바탕에 그대로 두면 머리글이
     거의 안 읽힌다. `--point`(#b7860b)도 11.5px 굵은 글씨에서 3.3:1 이라 모자라
     한 단 더 내렸다. **재는 뒤 바탕은 흰색만이 아니다** — 이 금색은 옅은 금색 판
     (`--panel-keysoft`, 「방문후기 쓰기」 단추) 위에도 서고, 회색 글자는 회색 타일
     (`--night2`, 통계 요약 칸) 위에도 선다. 그 둘에서 4.5:1 을 넘는 값이 여기 적힌 것이다
     (흰 바탕에서는 5.2:1). `_tools/light-panel-audit.mjs` 가 자리마다 실제 뒤 색으로 잰다. */
  --panel-key: #8d6608;
  /* 채워 그리는 금색(별·꼭대기 막대)은 글자가 아니라 **덩어리**라 조금 밝아도 읽힌다.
     `--point` 까지 내리면 별이 갈색으로 보인다. */
  --panel-mark: #e0a416;
  --panel-bar: rgba(183, 134, 11, 0.42);
  --panel-keyline: rgba(183, 134, 11, 0.4);
  --panel-keysoft: rgba(245, 197, 66, 0.14);
  --panel-red: var(--danger-ink);
  --panel-green: #15803d;
  /* 흰 판은 흰 카드 위에 선다 — **테두리가 없으면 판이 어디서 시작하는지 안 보인다.**
     그림자 대신 머리카락 한 줄만 두른다(가장 얇게 가르는 방법이다). */
  --panel-edge: var(--line);
  border: 1px solid var(--panel-edge);
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: "Pretendard Variable", Pretendard, "Noto Sans KR", -apple-system, sans-serif;
  line-height: 1.45;
  letter-spacing: -0.1px;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

button { font-family: inherit; border: 0; background: none; cursor: pointer; color: inherit; }
a { color: inherit; text-decoration: none; }
/* ── 누를 때 뜨는 네모를 지운다 (2026-09-07 지시) ──
 *
 * 「단추를 누르면 파란 네모가 켜졌다 꺼진다」의 정체는 **셋**이고, 하나만 막으면
 * 기기를 바꾸는 순간 그대로 다시 보인다. 그래서 여기 한 자리에서 셋을 다 막는다.
 *
 *   ① 탭 하이라이트 — 폰에서 손가락이 닿는 순간 상자 하나가 깔렸다 걷힌다.
 *      브라우저 기본값이 크롬은 검정 18%, **삼성인터넷·안드로이드 WebView 는
 *      하늘색**이다 — 그래서 같은 화면이 기기마다 다른 색으로 번쩍이고, PC 에서
 *      보는 사람 눈에는 **아예 안 보인다**(24K 「앱」이 그 WebView 다 → PWA 기억).
 *   ② 글자 선택 — 알약을 연달아 누르면 두 번 누름으로 읽혀 글자가 파랗게 잡힌다.
 *      `#map` 안은 지도 끌기 때문에 이미 막아 뒀는데 단추는 그 밖이라 안 걸려 있었다.
 *   ③ 포커스 테두리 — 손으로 누른 뒤 **자판을 아무거나 한 번** 두드리면 크롬이
 *      「지금은 자판을 쓰는 중」으로 판정을 뒤집어, 아까 누른 것에 UA 기본
 *      `outline: auto` 를 그린다(지도 표식에서 겪은 그것 — map.js `dropPointerFocus`).
 *
 * ③ 은 **`:focus-visible` 을 건드리지 않는다.** 테두리를 통째로 지우면 자판만 쓰는
 * 사람에게서 「지금 어디에 있나」를 뺏는다. 손으로 눌러 생긴 포커스에서만 지우므로
 * 마우스·손가락에는 자국이 없고 Tab 으로 옮기면 그대로 뜬다.
 *
 * `<a>` 는 탭 하이라이트만 끄고 **글자 선택은 남긴다** — 전화번호·주소처럼 눌러
 * 여는 링크의 글자를 긁어 복사하는 길이 있다. 로고는 그림뿐이라 따로 막는다. */
button, a, label, summary, [role="button"], [tabindex] {
  -webkit-tap-highlight-color: transparent;
}
button, label, summary, [role="button"], .logo {
  -webkit-user-select: none;
  user-select: none;
}
:focus:not(:focus-visible) { outline: none; }

.n { font-weight: 800; letter-spacing: -0.8px; font-variant-numeric: tabular-nums; }
.sub { color: var(--sub); }
.hide { display: none !important; }

/* — 판 — */

.app {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
}

.stage { position: relative; flex: 1; min-height: 0; display: flex; }

/* `position: relative` + `z-index: 0` 은 꾸밈이 아니라 **울타리**다.
 *
 * 지도 라이브러리는 제 안에서 층을 아주 높게 쌓는다(임시 지도로 쓰는 Leaflet 은
 * 판 400·조작 800). `#map` 이 겹침 맥락을 안 만들면 그 400 이 밖으로 새어 나와
 * `.app` 안에서 상단바(z 20)를 **이긴다** — 상단바·검색·계정 메뉴가 통째로
 * 지도 뒤로 숨는다. 화면에는 없는데 DOM 검사는 전부 통과한다(hit-test 는
 * `pointer-events:none` 인 지도 판을 그냥 통과해서 메뉴를 맨 위라고 답한다).
 * 카카오로 그릴 때는 안 나고 **임시 지도로 넘어간 순간에만** 난다.
 *
 * 여기서 맥락을 하나 만들어 두면 지도가 아무리 높이 쌓아도 이 상자를 못 넘는다. */
#map { flex: 1; min-width: 0; background: #e9e9eb; position: relative; z-index: 0; }

/* 지도가 못 뜬 자리. **회색 판만 남기지 않는다** — 왜 안 떴는지 말한다.
 *
 * `display:flex` 는 브라우저 기본 `[hidden]{display:none}` 을 **이긴다.**
 * 그래서 숨긴 줄 알았던 투명한 판이 화면 전체를 덮고 지도 클릭을 먹었다.
 * 아래 한 줄이 없으면 라이브에서 아이콘이 안 눌린다. */
.mapfail[hidden] { display: none !important; }

/* ── 1km 안내 창 ── (2026-09-07 지시)
 *
 * 1km 에 서면 「250m 까지 확대하면 캐릭터 위에 포인트가 뜬다」고 두 줄로 알린다.
 * 조각은 `map.js` 가 짓는다(`syncZoomHint`) — `index.html` 에는 없다.
 *
 * **하단 띠가 아니다.** ✕ 로 닫는 창이라 업데이트 창·앱 설치 창과 같은 갈래다.
 *
 * 자리는 **지도 위쪽**이다. 가운데에 세우면 가리키는 대상(한가운데 선 캐릭터)을
 * 제 손으로 덮는다. 오른쪽 78px 은 레일(`right:12px` + 53px 타일)에 내준다 —
 * `left:50%` 로 세우면 폰에서 글자 끝이 레일 타일 밑에 깔린다.
 *
 * 층 4 — `.mapfail`(5)·`.panel`(6)·`.vlist`(7)·`.page`(8) 아래다. 폰에서 그것들이
 * 지도를 통째로 덮으므로, 이 창이 위에 남으면 지도 아닌 화면에 지도 안내가 뜬다. */
.zhint[hidden] { display: none !important; }

.zhint {
  position: absolute;
  top: calc(var(--roll) + 8px);
  left: 12px;
  right: 78px;
  margin: 0 auto;
  max-width: 320px;
  z-index: 4;
  /* 좌우 여백이 같아야 글 두 줄이 창 한가운데 선다. ✕ 는 그 여백 안에 앉힌다. */
  padding: 10px 30px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.94);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
  text-align: center;
  /* 지도를 끌다가 이 창에 손이 닿아도 지도가 멈추지 않게 — 받는 것은 ✕ 뿐이다. */
  pointer-events: none;
}
.zhint p { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--sub); }
/* 첫 줄이 「무엇을 하면」이고 둘째 줄이 「무엇이 되나」다. 첫 줄을 진하게 세운다. */
.zhint p:first-child { color: var(--ink); font-weight: 700; }
.zhint .x {
  position: absolute;
  right: 3px; top: 3px;
  padding: 7px 9px;
  font-size: 12px;
  line-height: 1;
  color: var(--sub);
  pointer-events: auto;
}

.mapfail {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 24px;
  text-align: center;
  background: var(--night);
  color: #fff;
  z-index: 5;
}
.mapfail b { font-size: 16px; }
.mapfail span { font-size: 12.5px; color: var(--night-sub); max-width: 320px; line-height: 1.6; }

/* 「다시 시도」. 잠깐 끊긴 것이면 이거 하나로 산다. */
.mapfailRetry {
  margin: 4px 0 2px;
  padding: 9px 18px;
  border: 0;
  border-radius: 999px;
  background: var(--y);
  color: #1a1204;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

/* `.mapnotice`(「임시 지도로 그리고 있어요」 띠)는 2026-09-06 에 걷었다 —
   넘어갈 임시 지도가 없어졌다. 이 웹은 화면 아래 띠를 안 쓰기도 한다. */

/* — 상단 — */

/* `.top`(거르개 줄 + 「도움 요청」 `.sos`)은 2026-09-05 지시로 통째로 걷었다.
   지도 위 오른쪽에는 이제 레일 하나만 선다. */

.brand {
  background: var(--night);
  color: #fff;
  border-radius: 14px;
  padding: 10px 14px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}
.brand b { display: block; font-size: 15px; letter-spacing: -0.4px; }
.brand b i { background: var(--y); color: #111; font-style: normal; padding: 0 5px; border-radius: 6px; }
.brand .max { color: var(--y); font-size: 20px; font-weight: 800; letter-spacing: -0.8px; }
.brand .max small { font-size: 11px; font-weight: 700; letter-spacing: 0; }
.brand .why { font-size: 11px; color: var(--night-sub); }

.filters {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1;
  padding: 2px 0;
}
.filters::-webkit-scrollbar { display: none; }
.filters button {
  flex: none;
  background: #fff;
  border-radius: 20px;
  padding: 7px 13px;
  font-size: 12px;
  color: var(--sub);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  white-space: nowrap;
}
.filters button[aria-pressed="true"] { background: var(--ink); color: #fff; font-weight: 700; }

/* — 지도 위 아이콘 — */
/* 카카오 CustomOverlay 안에 들어가는 조각이다. 클릭이 DOM 으로 오기 때문에
   캔버스 위 좌표를 계산할 필요가 없다 — 표식을 「누를 수 있는 것」으로 만드는 핵심. */

/* 지도를 끌면 말풍선 글자가 파랗게 딸려 잡히던 것을 막는다 (2026-09-05 지시).
 *
 * 표식이 캔버스 위에 얹힌 그림이 아니라 **진짜 글자**라서 생긴 일이다. 지도를 끄는
 * 손짓은 브라우저가 보기에 「글자 위에서 누른 채 옮기는 것」과 구별이 안 되므로,
 * 「29,000P 클럽놈코」가 통째로 선택되어 파란 칠이 남는다. 캐릭터 그림 한 장
 * (`.mface .ch`)에만 막아 두었는데, 정작 잡히는 것은 그 위 말풍선 글자였다.
 *
 * **지도 칸 통째로 건다.** 표식은 카카오가 `#map` 안에 넣으므로 말풍선·시 이름표·
 * 캐릭터가 한 줄로 다 덮이고, 나중에 표식을 하나 더 만들어도 빠질 자리가 없다.
 * 지도 안에는 옮겨 적을 글이 없다 — 가게 이름·금액은 카드에서 고르면 그대로 읽힌다.
 *
 * 폰은 `-webkit-touch-callout` 까지 같이 꺼야 한다. 길게 누르면 「복사」 풍선이
 * 뜨면서 지도가 그 자리에 멈춘다 — 끄는 손짓이 도중에 끊긴다. */
#map {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.pin {
  transform: translate(-50%, -100%) scale(calc(var(--mz) * var(--bz)));
  cursor: pointer;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.35));
  /* 바탕 한 장(`.tail`)이 이 상자를 기준으로 눕는다 — 아래 `.pin .tail` 설명. */
  position: relative;
  /* 꼬리가 흐름에서 빠진 만큼 여기서 되돌린다(옛 꼬리 상자 10px − 겹침 6px = 4px).
     이 상자 높이가 곧 **말풍선이 서는 자리**이자 캐릭터를 누르는 자리
     (`.has-face::after` 의 `top: 100%`)의 기준이라, 줄면 말풍선이 4px 내려앉고
     발치가 같이 어긋난다. */
  padding-bottom: 4px;
}
/* 테두리는 **안 그린다**(2026-09-07 지시: 「지저분하다」).
 * 반투명 바탕 위에 흰 선을 1.5px 두르면, 수십 개가 겹치는 배율에서 선끼리 엇갈려
 * 글자보다 선이 먼저 읽힌다. 말풍선을 지도에서 떼어 놓는 몫은 아래 `drop-shadow`
 * 하나로 충분하다 — 그림자는 겹쳐도 서로 지우지 않는다.
 * 꼬리(`.tail`)의 두 변도 같이 걷는다. 안 걷으면 몸통만 매끈하고 꼬리에 실선이 남는다. */
.pin .body {
  /* 바탕은 여기서 안 칠한다 — 몸통·꼬리를 한 장에 그리는 `.tail` 이 뒤에 깔린다.
     이 조각에는 **글자만** 남는다(그래야 바탕을 흐려도 금액·이름이 안 흐려진다). */
  color: #fff;
  border-radius: 14px;
  padding: 6px 9px 7px;
  min-width: 74px;
  text-align: center;
}
/* 포인트 줄 — **한 치수 키웠다**(2026-09-05 지시: 15px → 17px, 굵게).
   말풍선에서 가장 먼저 읽혀야 하는 것이 금액이다. 「20천P」로 줄여 쓰던 것도
   걷어서 이제 「20,000P」가 통째로 들어온다 — 그만큼 자리를 준다. */
.pin .amt { color: var(--y); font-size: 17px; font-weight: 800; letter-spacing: -0.5px; }
/* 가게 이름 — **흰색 굵은 글씨 하나로 통일한다**(2026-09-07 지시).
   회색(`--night-body`)으로 흐려 두던 것을 걷었다. 캠페인·클럽·라운지가 저마다
   다른 색이면 같은 「가게 이름」이 세 가지 물건으로 읽힌다. 아래 `.venue .nm` 과
   **글자 규칙이 한 벌**이니, 한쪽만 고치지 않는다.

   **한 치수 키웠다**(2026-09-09 지시: 11px → 13px). 금액이 15 → 17 로 오를 때와
   같은 폭이다. 같은 날 말풍선을 25% 줄인 뒤라(`--bz`) 이름이 특히 작아져 있었다.

   ⚠ **`max-width` 는 안 건드린다.** 여기는 `ellipsis` 로 자르는 상자라, 글자를 키우면
   잘리는 이름이 늘긴 한다 — 실측으로 PC 5 → 6, 폰 3 → 4곳(늘어난 것은
   「미드나잇라운지클럽」 하나다). 상자를 글자에 맞춰 114px 로 넓혀 보면 잘리는 수는
   원래대로 돌아오지만 **말풍선 최대 폭이 90 → 105px 로 커진다** — 방금 25% 줄인 것을
   긴 이름에서만 도로 무르는 셈이다. 한 곳 더 자르는 쪽이 싸다. */
.pin .nm {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.3px;
  color: #fff;
  max-width: 96px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 말풍선 바탕 — **몸통과 꼬리를 한 장에 그린다**(2026-09-07 지시: 「세모와 네모가
 * 만나는 자리의 선을 지워 말풍선이 하나의 그림처럼 나오게」).
 *
 * 전에는 몸통(`.body`)과 꼬리(`.tail`)가 **저마다 제 바탕을 반투명으로** 깔았다.
 * 꼬리가 몸통을 1px 파고들어 있어서 그 1px 만 두 겹이 되고, 겹친 만큼 짙어져
 * 이음새에 가로줄이 떴다 — 화소로 재면 검은 판에서 39 → 24, 고른 판(노랑)에서
 * 199,164 → 232,187 로 튄다. 꼬리를 잘라 쓰던 것(`clip-path`)은 마름모의 위 반쪽을
 * 걷은 것이지 **겹침을 걷은 것이 아니라**, 남은 1px 이 그대로 줄로 남았다.
 * 겹침을 0 으로 맞춰 봐도(꼬리 윗변이 몸통 아랫변에 딱 닿게) 이번엔 가장자리를
 * 갈아 그리는 값이 서로 안 맞아 **옅은 줄**이 남는다(같은 자리에서 199 → 183).
 *
 * 그래서 **바탕을 한 조각에 몰아 그린다.** 꼬리 조각이 말풍선 전체를 덮는 바탕층이
 * 되고, 그 안에서 몸통(`::before`)과 꼬리(`::after`)를 **불투명한 색으로 겹쳐** 그린
 * 다음, 그 층을 통째로 **한 번만** 흐린다(`opacity`). 아무리 겹쳐도 색이 한 값이라
 * 이음새가 원리상 안 생긴다 — 재 보면 위아래가 한 값으로 이어진다.
 *
 * 글자는 이 층 밖(`.body`)에 그대로 있다 — 층을 흐려도 금액·이름은 안 흐려진다.
 * 그래서 색은 `--bub-c`(불투명 색)와 `--bub-a`(흐리는 정도) 두 값으로 든다.
 *
 * ⚠ 시 이름표(`.city`)는 이 갈래가 아니다 — 거기는 흰 테두리가 몸통과 꼬리를 함께
 * 두르고 있어 꼴 자체가 다르다. 손대려면 테두리부터 같이 옮겨야 한다. */
.pin, .venue { --bub-c: var(--pin-c); --bub-a: var(--pin-a); }
.pin .tail, .venue .tail {
  position: absolute;
  inset: 0;
  /* 글자 뒤로 깔린다. `.pin` 은 `filter` 로 제 층을 이미 세워 두었으니 이 음수가
     말풍선 밖으로 새지 않는다. */
  z-index: -1;
  opacity: var(--bub-a);
}
/* 몸통 — `.pin` 의 안쪽 여백 4px 를 뺀 만큼이 곧 `.body` 상자다. */
.pin .tail::before, .venue .tail::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0; bottom: 4px;
  background: var(--bub-c);
  border-radius: 14px;
}
/* 꼬리 — 옛 마름모와 **같은 자리, 같은 크기**다(윗변이 몸통 아랫변보다 1px 위,
   뾰족한 끝이 6px 아래). 몸통을 파고들어도 이제 안 짙어진다. */
.pin .tail::after, .venue .tail::after {
  content: '';
  position: absolute;
  left: 50%; bottom: -2px;
  width: 14px; height: 7px;
  transform: translateX(-50%);
  background: var(--bub-c);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.pin.on { --bub-c: var(--pin-c-on); --bub-a: var(--pin-a-on); }
.pin.on .amt { color: #111; }
.pin.on .nm { color: #4a3a06; }
/* 겹쳐서 못 고르는 자리는 미리 회색으로 말한다. 눌러 봐야 알게 하지 않는다. */
.pin.blocked { --bub-c: var(--pin-c-blocked); --bub-a: var(--pin-a-blocked); }
.pin.blocked .amt { color: #d6d6da; }

/* 묶음 아이콘 — **수는 안 적는다**(2026-09-05 지시). 금액 오른쪽에 「N곳」 노란 칩이
 * 있었는데, 좁은 말풍선에서 금액과 나란히 서면 「17,000P 2곳」이 한 덩어리로 읽혔다.
 * 여럿이 겹쳤다는 말은 이제 **바탕색과 노란 테두리**가 대신한다.
 * (`.city .cnt` 는 다른 것이다 — 축소했을 때 뜨는 도시 알약이고 그건 그대로 둔다.)
 *
 * **여기만 테두리가 남는다.** 2026-09-07 에 말풍선 테두리를 다 걷었지만, 이 노란 선은
 * 꾸밈이 아니라 「여러 곳이 겹쳐 있다」는 유일한 표식이다(바탕색 차이는 43,43,51 대
 * 14,14,17 이라 눈으로 안 갈린다). 걷으면 그 말이 통째로 사라진다. 그래서 `border-color`
 * 만 두던 것을 **선까지 여기서 직접 적는다** — 아래 `.pin .body` 에 선이 없어졌으니
 * 색만 적으면 아무것도 안 그려진다.
 *
 * 바탕색은 말풍선 한 장(`.tail`)이 지고, 테두리만 여기 `.body` 에 남는다. 선은
 * 흐리지 않는다 — 바탕층 밖이라 `--bub-a` 를 안 탄다.
 * **이 줄이 `.pin.on` 보다 아래인 것은 그대로 둔다**: 묶음은 골라도 회색으로 남아야
 * 「여러 곳」이라는 말이 안 지워진다(무게가 같아 나중에 적힌 쪽이 이긴다). */
.pin.many { --bub-c: var(--pin-c-many); --bub-a: var(--pin-a-many); }
.pin.many .body { border: 1.5px solid var(--y); }

/* 실존 클럽 표시 — **캠페인 아이콘과 같은 꼴이다**(2026-09-05 지시).
 *
 * 전에는 일부러 다르게 생기게 했다(작은 점 + 흰 이름표). 계약한 곳이 아닌데
 * 같은 옷을 입히면 제휴한 것처럼 읽힌다고 봤기 때문이다. 지시로 뒤집었다.
 * 대신 **금액 줄이 없는 것**이 유일한 차이로 남는다 — 얹을 금액 자체가 없다.
 * 카드 맨 위 경고 상자는 2026-09-05 지시로 걷었다 — 「예시」칩과
 * 맨 아래 「아직 예약할 수 없어요」가 그 몫을 진다. */
.venue {
  transform: translate(-50%, -100%) scale(calc(var(--mz) * var(--bz)));
  cursor: pointer;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.35));
  /* 캠페인 아이콘과 같은 이유다 — 바탕 한 장이 이 상자를 기준으로 눕고,
     꼬리가 흐름에서 빠진 4px 를 여기서 되돌린다(`.pin` 의 설명). */
  position: relative;
  padding-bottom: 4px;
}
.venue .body {
  /* 바탕은 `.tail` 한 장이 진다(`.pin .body` 와 같은 이유). */
  color: #fff;
  border-radius: 12px;
  /* 캠페인 아이콘보다 한 치수 작다 — 117곳을 다 세우면 서로 덮는 넓이가 그만큼 준다. */
  padding: 5px 8px;
  text-align: center;
  /* 테두리 없음 — 캠페인 아이콘과 같은 이유다(`.pin .body` 의 설명). */
}
/* 캠페인 아이콘과 **같은 크기**로 둔다 — 같은 말풍선에서 한쪽 금액만 작으면
   그 가게가 덜 주는 것처럼 읽힌다. 값은 예시지만 글자 크기는 거짓말을 하면 안 된다. */
.venue .amt { color: var(--y); font-size: 17px; font-weight: 800; letter-spacing: -0.5px; }
/* 위 `.pin .nm` 과 **한 벌이다** — 2026-09-09 에 같이 13px 로 올렸다.
   한쪽만 두면 캠페인 말풍선과 클럽 말풍선의 이름 크기가 갈린다. */
.venue .nm {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.3px;
  max-width: 92px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 몸통이 한 치수 작으니 굴린 정도도 `.body` 와 같은 12px 로 맞춘다. */
.venue .tail::before { border-radius: 12px; }
/* `.on` 은 **눌러서 고른 것**이다 — 캠페인 아이콘과 같은 규칙(map.js 의 `highlightVenue`).
   손을 떼도 남아야 하므로 `:hover` 와 한 줄에 묶어 두되 켜는 손은 따로다. */
.venue:hover, .venue:focus, .venue.on { --bub-c: var(--pin-c-on); --bub-a: var(--pin-a-on); }
.venue:hover .nm, .venue:focus .nm, .venue.on .nm { color: #111; }
/* 바탕이 노랑으로 바뀌면 노란 금액은 사라진다 — 글자색도 같이 뒤집는다. */
.venue:hover .amt, .venue:focus .amt, .venue.on .amt { color: #111; }

/* — 16km ~ 64km: 시 이름표 — (2026-09-05 지시)
 *
 * 물러나면 가게 하나하나는 사라지고 이것만 남는다. 레벨 11 에서 5km 짜리 구가 20px 라
 * 430곳을 다 세우면 말풍선이 서로 완전히 덮여 글자가 아니라 검은 얼룩이 된다.
 *
 * **읽히는 것은 이름과 수 둘뿐이다.** 한 줄에 나란히 둔다 — 두 줄로 쌓으면 이 배율에서
 * 이름표 높이가 시 사이 간격(부산~창원이 30px 쯤)을 먹는다.
 * 수는 노란 알약이다. 캠페인 묶음의 「N곳」칩과 **같은 꼴**이라, 지도 어디서 보든
 * 「노란 알약에 든 수 = 그 안에 몇 곳」으로 한 가지로 읽힌다. */
.city {
  transform: translate(-50%, -100%);
  cursor: pointer;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.45));
}
.city .body {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--pin-bg);
  color: #fff;
  border-radius: 15px;
  padding: 7px 11px 8px;
  border: 1.5px solid rgba(255, 255, 255, 0.16);
  white-space: nowrap;
}
.city .nm { font-size: 14px; font-weight: 800; letter-spacing: -0.4px; }
.city .cnt {
  background: var(--y);
  color: #111;
  font-size: 11px;
  font-weight: 800;
  border-radius: 8px;
  padding: 1px 6px;
  letter-spacing: 0;
}
.city .tail {
  width: 10px; height: 10px;
  background: var(--pin-bg);
  margin: -6px auto 0;
  transform: rotate(45deg);
  border-right: 1.5px solid rgba(255, 255, 255, 0.16);
  border-bottom: 1.5px solid rgba(255, 255, 255, 0.16);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
}
.city:hover .body, .city:focus .body { background: var(--pin-bg-on); border-color: var(--pin-bg-on); }
.city:hover .nm, .city:focus .nm { color: #111; }
.city:hover .cnt, .city:focus .cnt { background: #111; color: var(--y); }
.city:hover .tail, .city:focus .tail { background: var(--pin-bg-on); border-color: var(--pin-bg-on); }

/* — 말풍선 아래에 서는 캐릭터 —
 *
 * 좌표에 닿는 것은 **캐릭터의 발**이다. 말풍선은 그 위에 얹힌다.
 *
 * 캐릭터는 말풍선과 **딴 조각으로** 선다(map.js 의 「층대」 설명). 카카오 오버레이는
 * z-index 가 박힌 껍데기라 그 안쪽 자식이 밖으로 못 나간다 — 한 조각에 두면 앞뒤를
 * 말풍선 하나로밖에 못 정한다. 대신 두 조각의 층 번호를 **이웃하게** 붙여, 한 사람이
 * 말풍선까지 통째로 앞뒤를 타게 한다. 남쪽(=화면 아래) 사람이 앞이다.
 *
 * 말풍선은 캐릭터가 빠져나간 만큼 **스스로 올라선다** — 보이는 자리는 전과 똑같고
 * 덮이는 앞뒤만 바뀐다. 74px(캐릭터) + 1px(사이 여백) = 75px.
 *
 * `height` 를 **미리 박아 둔다.** 그림이 늦게 오는 동안 자리가 0 이면 말풍선이
 * 발 자리까지 내려앉았다가 그림이 도착할 때 위로 튄다.
 * (국모에서 같은 일을 겪었다: 늦게 오는 그림은 아래 것들을 끌어올린다.) */
.pin.has-face, .venue.has-face {
  /* **75px 이 아니라 75px × 배수다.** 캐릭터가 배수만큼 커지면 그 키도 같이 커지니,
     올라서는 높이를 고정으로 두면 배율을 당길수록 말풍선이 얼굴에 파묻힌다.
     기준점이 아래 가운데라 `scale` 은 이 자리를 안 흔든다(아래 `transform-origin`). */
  transform: translate(-50%, calc(-100% - 75px * var(--mz))) scale(calc(var(--mz) * var(--bz)));
  position: relative;
}

/* — 캐릭터 자리를 눌러도 그 가게가 열린다 —
 *
 * 「캐릭터가 말풍선보다 큰데 눌러도 아무 일이 없다」를 고치는 자리다(2026-09-05 지시).
 *
 * **캐릭터 조각에 직접 클릭을 먹이면 안 된다.** 캐릭터는 오려낸 그림인데 클릭을 받는 것은
 * 그림이 아니라 **사각형**이라, 팔다리 옆 빈 자리가 옆 가게 말풍선을 덮으면 보이지도 않는
 * 것이 그 말풍선의 클릭을 가로챈다 — 연기시험이 「아이콘을 누르면 정보가 뜬다」에서
 * 실제로 멈췄다.
 *
 * 그래서 누르는 자리를 **말풍선 쪽에** 둔다. 말풍선은 캐릭터 높이만큼 올라서 있으니,
 * 바로 아래 그만큼을 투명하게 덮으면 그게 곧 캐릭터가 선 자리다. 이 조각은 말풍선과
 * **같은 칸**이라 앞뒤가 사다리 하나로만 정해진다 — 앞에 선 사람이 곧 눌리는 사람이다. */
.pin.has-face::after, .venue.has-face::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 100%;
  /* **말풍선 배수(`--bz`)로 나눈다.** 이 조각은 말풍선의 자식이라 말풍선이 0.8배가
     되면 같이 0.8배가 된다 — 나누지 않으면 캐릭터가 선 자리(52×75)보다 좁게 깔려
     발치가 안 눌린다. 나눠 두면 화면에 놓이는 크기는 늘 캐릭터와 같다. */
  width: calc(52px / var(--bz));
  height: calc(75px / var(--bz));
  transform: translateX(-50%);
  cursor: pointer;
  /* `.pin` 은 `pointer-events: none !important` 라 여기서 되살려야 눌린다
     (`.pin .body`·`.tail` 과 같은 이유다). */
  pointer-events: auto;
}

/* 이름이 `mface` 인 이유: `.face` 는 마이페이지의 프로필 사진칸이 이미 쓰고 있다.
   같은 이름을 쓰면 그 52×52 칸이 지도 규칙(translate·pointer-events)을 물려받아 망가진다. */
.mface {
  transform: translate(-50%, -100%) scale(var(--mz));
  /* 이 층은 **클릭을 하나도 안 먹는다.** 누르는 자리는 바로 위 `::after` 가 맡는다. */
  pointer-events: none;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.35));
}
.mface .ch {
  display: block;
  height: 74px;
  width: auto;
  margin: 0 auto;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
/* 고른 곳은 캐릭터도 같이 밝아진다 — 어느 것을 보고 있는지 지도에서 안 보이면 길을 잃는다.
   층이 갈려 `:hover` 가 못 건너오므로 `hot`·`on` 을 map.js 가 손으로 붙여 준다. */
.mface.hot .ch, .mface.on .ch { filter: brightness(1.12) drop-shadow(0 0 6px var(--y)); }

/* — 고른 것은 커진다 (2026-09-05 지시) —
 *
 * 눌러서 고른 곳은 **말풍선이 26%, 캐릭터가 25%** 커진다. 밝기만 바뀌던 때는
 * 「내가 누른 게 저건가」를 색 하나로 판정해야 했는데, 수십 곳이 겹쳐 선 자리에서는
 * 그게 잘 안 읽힌다. 크기는 겹쳐 있어도 읽힌다.
 *
 * **키우는 기준점은 아래 가운데다**(`transform-origin: 50% 100%`). 좌표에 닿는 것이
 * 말풍선 꼬리 끝이자 캐릭터의 발이라, 가운데를 기준으로 키우면 고른 순간 표식이
 * 제 가게 자리에서 미끄러진다. 자리를 잡는 `translate` 는 그대로 두고 뒤에 `scale` 만 잇는다 —
 * 순서를 뒤집으면 `translate` 의 `-50%` 까지 배율에 곱해져 옆으로 밀린다.
 *
 * 말풍선 `1.26` 은 첫 값(20%)에서 **한 번 더 올린 것**이다(2026-09-05 두 번째 지시:
 * 그 위에 5% — 1.2 × 1.05 = 1.26).
 *
 * ── 캐릭터는 **배율을 안 탄다** ── (2026-09-07 세 번째 지시)
 *
 * 「어느 거리에서 눌러도 **20m 에서 누른 그 크기**로」. 그래서 고른 캐릭터의 배수는
 * `var(--mz)` 가 **안 들어간다** — 사다리 맨 아래 칸(20m)의 배수 `--mzp`(=1.86) 에
 * 고르기 배수 1.25 를 곱한 **한 값**(2.325)으로 어느 배율에서나 똑같이 선다.
 * 1km(0.875)에서 누르면 2.66배로 뛰고 20m(1.86)에서 누르면 1.25배만 커지는데,
 * **뛰는 폭이 배율마다 다른 것이 맞다** — 고정된 것은 «커지는 비율»이 아니라
 * «서는 크기»다. 고른 하나를 어디서든 같은 크기로 읽히게 하는 것이 이 규칙의 뜻이다.
 *
 * 고르기 배수 1.25 는 그 앞 지시다(2026-09-07 두 번째: 「클릭하면 지금 크기에서 25% 더」.
 * 그날 아침 1.43 → 1.1 을 거쳐 왔다). 말풍선 배수 1.26 은 그 자리에 그대로 둔다.
 *
 * ── 말풍선도 배율을 안 탄다 ── (2026-09-10 지시)
 *
 * 「클릭한 뒤 줌아웃을 해도 말풍선 크기는 안 바뀌어야 하고, 고른 말풍선은 **말풍선의
 * 가장 큰 크기**여야 한다」. 그래서 **고른 말풍선의 배수에서도 `var(--mz)` 를 뺀다** —
 * 캐릭터가 `--mzp` 로 선 것과 같은 이유, 같은 값이다. 사다리 맨 아래 칸(20m = 1.86)이
 * 곧 말풍선이 제일 큰 칸이라, `--mzp × --bz × 1.26`(=1.266) 한 값으로 어느 배율에서나
 * 똑같이 선다. 그 전에는 1km 에서 캐릭터만 172px 로 고정된 채 말풍선이 절반 크기로
 * 쪼그라들어, 같은 하나를 눌렀는데 거리에 따라 글자가 읽히다 안 읽히다 했다.
 *
 * ⚠ **안 고른 것(`.pin`·`.venue`·`.mface`)은 그대로 `--mz` 를 탄다** — 사다리를 걷어내는
 *   것이 아니다. 배율에 안 묶이는 것은 «고른 하나»뿐이고, 그것이 이 규칙 전부다.
 * ⚠ 올라서는 높이는 손댈 것이 없다 — 이미 `--mzp` 로 셈해 두었다(바로 아래).
 * ⚠ **캐릭터를 누르는 자리(`.has-face::after`)도 같이 커진다** — 그 조각은 말풍선의
 *   자식이라 같은 배수를 탄다. 폰 1km 에서 46×51px → 98×138px 이 되는데, 그려진
 *   캐릭터가 원래 98×135px 이니 **비로소 맞아진 것**이다(전에는 보이는 것의 절반도
 *   안 눌렸다). 대가는 그 네모의 투명한 모서리가 이웃 캐릭터의 클릭을 더 가로채는
 *   것이다 — 폰 1km 에서 이웃 107 중 7 → 37. 이 배수를 또 건드릴 때 같이 재라.
 *
 * 캐릭터 키가 74px × 2.325 = 172px 가 되니, 캐릭터 높이 + 1px 위에 서던 말풍선도
 * 그만큼 올라선다. **그 높이도 `--mz` 를 떼고 `--mzp` 를 단다** — 안 그러면 1km 에서
 * 고른 순간 캐릭터는 172px 인데 말풍선은 81px 만 올라서서 얼굴을 통째로 덮는다.
 * 픽셀을 손으로 적지 않고 `74px × var(--mzp) × 1.25 + 1px` 로 셈해 둔다(폰은 58px) —
 * 사다리를 고쳐도 따라 움직인다.
 *
 * 안 고른 것들(`.pin`·`.venue`·`.mface`)만 `× var(--mz)` 다 — 고른 것은 캐릭터도
 * 말풍선도 `--mzp` 다. */
.pin, .venue, .mface { transform-origin: 50% 100%; transition: transform 0.14s ease-out; }

/* — 같은 자리에 겹쳐 선 것들을 좌우로 벌린다 — (2026-09-07 지시)
 *
 * 한 건물에 층만 다른 클럽이 여럿 든 곳이 있다. 주소가 같으니 좌표도 같고, 그러면
 * 말풍선도 캐릭터도 **통째로 포개져** 뒤엣것이 지도에서 사라진다. 몇 칸 밀지는
 * `fan.js` 가 정해 `--fx`(칸 수)로 넣어 주고, 한 칸의 폭은 `--fan` 이다.
 *
 * **`transform` 이 아니라 `translate` 속성에 건다.** 위 규칙들의 `transform` 은
 * 자리잡기와 배수가 아홉 자리(캐릭터 있는 것·고른 것·폰…)에 흩어져 있어서 거기에
 * 더하면 한 자리를 빠뜨린다. `translate` 는 그 앞에 따로 얹히므로 이 한 줄이 아홉
 * 자리에 다 든다. `scale` 보다 먼저 적용되니 **배수는 여기서 손으로 곱한다**(`--mz`) —
 * 그래야 캐릭터가 커지는 만큼 벌어지는 폭도 같이 커진다.
 *
 * 미는 것은 **좌우뿐이다.** 앞뒤(z)는 위도로만 정해지므로(map.js `rankAt`) 위아래로
 * 밀면 눈에 보이는 위아래와 그 규칙이 어긋난다 — 아래에 그려진 사람이 뒤로 숨는다. */
.pin, .venue, .mface { translate: calc(var(--fx, 0) * var(--fan) * var(--mz)) 0; }

/* 캐릭터가 없는 표식도 같은 규칙을 탄다 — 고른 하나는 어디서 봐도 같은 크기다. */
.pin.on, .venue.on { transform: translate(-50%, -100%) scale(calc(var(--mzp) * var(--bz) * 1.26)); }
.pin.has-face.on, .venue.has-face.on {
  transform: translate(-50%, calc(-100% - (74px * var(--mzp) * 1.25 + 1px)))
             scale(calc(var(--mzp) * var(--bz) * 1.26));
}
.mface.on { transform: translate(-50%, -100%) scale(calc(var(--mzp) * 1.25)); }

/* 폰에서는 한 뼘에 수십 곳이 겹친다. 캐릭터를 한 치수 줄여 이름표를 덜 가린다. */
@media (max-width: 480px) {
  .mface .ch { height: 58px; }
  /* 캐릭터가 작아진 만큼 **벌리는 폭도 같이 줄인다**(누르는 자리 42px 에 맞춘다).
     안 줄이면 폰 화면에서 겹치지도 않은 것이 한 뼘씩 벌어져 선다. */
  :root { --fan: 46px; }
  .pin.has-face, .venue.has-face {
    transform: translate(-50%, calc(-100% - 59px * var(--mz))) scale(calc(var(--mz) * var(--bz)));
  }
  /* 누르는 자리도 같이 줄인다 — 캐릭터보다 넓으면 옆 가게 말풍선을 덮는다.
     PC 와 같은 이유로 `--bz` 로 나눈다(위 `::after` 설명). */
  .pin.has-face::after, .venue.has-face::after {
    width: calc(42px / var(--bz));
    height: calc(59px / var(--bz));
  }
  /* 고른 것이 커지는 만큼 폰에서도 말풍선을 더 올린다 — 폰 캐릭터는 58px 이라
     58 × var(--mzp) × 1.25, +1px 여백. **여기도 `--mz` 가 아니다**(위 설명). */
  .pin.has-face.on, .venue.has-face.on {
    transform: translate(-50%, calc(-100% - (58px * var(--mzp) * 1.25 + 1px)))
               scale(calc(var(--mzp) * var(--bz) * 1.26));
  }
}

/* — 1km 부터는 캐릭터만 남는다 — (2026-09-06 지시. 그 전 문턱은 2km 였다)
 *
 * 배율을 물리면 화면에 드는 가게가 수십에서 수백으로 늘고, 그때부터는 말풍선이
 * 서로를 덮어 글자가 아니라 검은 얼룩이 된다. 그 자리부터 **말풍선을 걷고 캐릭터만**
 * 세운다 (16km 부터 시 이름표만 남기는 것과 같은 결의 규칙이고, 그 사이 네 칸
 * — 1km·2km·4km·8km — 이 이 갈래다). 스위치는 `map.js` 의 `syncZoomLook` 이 뿌리에 건다.
 *
 * **지우지 않고 감춘다, 그것도 몸통과 꼬리만.** `.pin` 을 통째로 없애면 그 안의
 * `::after` — 곧 「캐릭터를 누르는 자리」 — 까지 같이 사라져, 캐릭터만 서 있는 이
 * 배율에서 지도 전체가 「보이는데 안 눌린다」가 된다. `visibility: hidden` 은
 * 자리를 그대로 남기므로 말풍선이 올라서 있던 높이도 안 흔들린다 — `::after` 는
 * 그 높이를 기준으로 캐릭터 자리를 잡는다.
 *
 * 캐릭터가 없는 표식(`has-face` 아님)은 **건드리지 않는다.** 그것까지 감추면
 * 그 가게는 지도에서 통째로 사라지고 누를 자리도 없다.
 *
 * **고른 것(`.on`)은 이 배율에서도 말풍선을 보여 준다**(2026-09-07 지시:
 * 「말풍선이 나오지 않는 구간에도 클릭했을 때는 말풍선도 같이 나와야 한다」).
 * 여기서 감추는 것은 «수백 개가 서로를 덮어 검은 얼룩이 된다» 때문인데, 고른 것은
 * 한 번에 하나뿐이라 그 이유가 안 걸린다. 캐릭터를 눌렀는데 사람만 커지고 금액·이름이
 * 안 뜨면 「내가 누른 게 저건가」를 다시 묻게 된다 — 누른 값을 그 자리에서 읽혀 준다.
 * (`.pin` 은 캠페인 쪽 `highlight`, `.venue` 는 클럽 쪽 `highlightVenue` 가 켠다.
 *  둘 다 고른 것을 `raise` 로 맨 앞에 올리므로 이웃 캐릭터에 가리지도 않는다.) */
:root.map-nobubble .pin.has-face:not(.on) > .body,
:root.map-nobubble .pin.has-face:not(.on) > .tail,
:root.map-nobubble .venue.has-face:not(.on) > .body,
:root.map-nobubble .venue.has-face:not(.on) > .tail { visibility: hidden; }

.filters .venue-toggle[aria-pressed="true"] { background: #33353b; color: #fff; }

/* 내 위치 — 금색 핀이 제자리에서 떠 있고 발밑에서 금빛 동그라미가 퍼진다
 * (2026-09-07 지시). 그 전에는 파란 점 하나였다.
 *
 * **조각의 왼쪽 위가 곧 내 좌표다** — `place()` 가 카카오 앵커를 (0, 0)으로 주고
 * 자리잡기를 CSS 에 맡기기 때문이다(map.js 의 `overlayPane` 설명). 그래서 상자를
 * `translate(-50%, -50%)` 로 물려 **한가운데가 내 자리**가 되게 하고, 그 점에 둘을
 * 매단다: 물결은 상자를 꽉 채워 그 점에서 퍼지고, 핀은 `bottom: 50%` — 곧 발끝이
 * 그 점에 닿아 그 위에 선다.
 *
 * **상자에 크기를 준다.** 0×0 으로 두면 자리는 똑같지만 검사 도구가 「안 보이는
 * 조각」으로 읽어(빈 사각형) 화면에 멀쩡히 서 있는 것을 못 봤다고 답한다.
 *
 * 안쪽 자리를 `left`·`bottom` 으로 잡는 이유는 **움직임이 transform 을 쓰기
 * 때문**이다. 한 조각에서 자리잡기와 움직임이 transform 을 겸하면 뜨는 동작이
 * 자리를 덮어써서 핀이 반 폭 왼쪽으로 튀어 나간다.
 *
 * 지도 배수(`--mz`)는 **상자에 통째로** 건다 — 핀과 물결이 같이, 내 자리를 기준으로
 * 커진다(말풍선·캐릭터와 같은 사다리다).
 *
 * `pointer-events: none` 이 중요하다. 누를 일이 없는 조각이고, 안 끄면 내가 선
 * 자리 둘레의 말풍선이 「보이는데 안 눌린다」가 된다(캐릭터 층에서 이미 한 번
 * 겪은 함정이다. 상자가 54px 이던 첫 판에서 실제로 그랬다).
 *
 * ── 크기는 **덩어리 하나로** 줄인다 ──
 * 조각마다 px 을 깎지 않고 배수 두 값(`--mep`·`--mew`)으로만 줄인다 — 뜨는 높이·
 * 테두리·그림자가 같이 안 줄면 작아진 핀에 굵은 테만 남아 비율이 깨진다.
 * 기준값은 핀 34×45 · 물결 상자 54px 이고, 2026-09-07 지시로 「절반·30%」였다가
 * 같은 날 **둘 다 1.2배**가 됐다(0.5→0.6 · 0.3→0.36). 키우고 줄이는 것은 여기 두 줄뿐이다. */
.me {
  --mep: 0.6;                    /* 핀 배수 — 34×45 에 곱해진다 */
  --mew: 0.36;                   /* 물결 배수 — 54px 상자에 곱해진다 */
  position: relative;
  width: calc(54px * var(--mew)); height: calc(54px * var(--mew));
  transform: translate(-50%, -50%) scale(var(--mz));
  pointer-events: none;
}
/* 발밑 물결 — **한 겹이다**(2026-09-07 지시). 처음에는 둘을 반 박자 어긋나게
 * 띄웠는데, 소유자가 한 겹으로 줄이라고 했다.
 *
 * **밝은 지도 위라 금색이 잘 안 읽힌다.** 처음 판은 옅게(테 0.7·속 0.5) 깔았다가
 * 라이브 배경 위에서 거의 안 보여 진하게 올렸다 — 금색 테에 **어두운 테 한 겹**을
 * 밖에 덧대야 베이지색 지도 위에서 테두리가 산다. 테 굵기·번짐도 `--mew` 를 탄다. */
.me .mewave {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: calc(2.5px * var(--mew)) solid rgba(245, 197, 66, 0.95);
  box-shadow: 0 0 0 calc(1px * var(--mew)) rgba(90, 60, 0, 0.28),
    0 0 calc(12px * var(--mew)) rgba(245, 197, 66, 0.55);
  background: radial-gradient(circle,
    rgba(245, 197, 66, 0.62) 0%, rgba(245, 197, 66, 0.3) 55%, rgba(245, 197, 66, 0) 74%);
  animation: me-wave 2.2s cubic-bezier(0.22, 0.61, 0.36, 1) infinite;
}
/* 발판 — 늘 서 있는 작은 금색 점. 핀은 떠 있으니 **내가 정확히 어디 서 있나**를
   말하는 것은 이 점이다. 물결이 퍼져 나가는 자리도 여기라고 읽힌다.
   크기는 **핀을 따라간다**(`--mep`) — 물결을 따라가면 30% 라 점이 아니라 티끌이 된다. */
.me .mespot {
  position: absolute;
  left: 50%; top: 50%;
  width: calc(7px * var(--mep)); height: calc(7px * var(--mep));
  margin: calc(-3.5px * var(--mep)) 0 0 calc(-3.5px * var(--mep));
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #ffe89a 0%, #f5c542 45%, #c98f14 100%);
  box-shadow: 0 0 0 calc(1.5px * var(--mep)) rgba(255, 255, 255, 0.85),
    0 1px calc(3px * var(--mep)) rgba(0, 0, 0, 0.4);
}
/* 핀 — 이름을 `.pin` 으로 두면 안 된다. 지도 말풍선이 이미 그 이름으로
   `position:absolute`·`translate(-50%,-100%)`·`pointer-events:none !important` 를
   물고 있어서, 그대로 쓰면 이 핀이 말풍선 자리 규칙을 통째로 물려받는다
   (채팅의 `.kmsg.mine` 이 `.me` 를 피해 간 것과 같은 이유다). */
.me .mepin {
  position: absolute;
  left: 50%; bottom: 50%;
  margin-left: calc(-17px * var(--mep));
  width: calc(34px * var(--mep)); height: calc(45px * var(--mep));
  animation: me-bob 1.8s ease-in-out infinite;
  filter: drop-shadow(0 calc(3px * var(--mep)) calc(4px * var(--mep)) rgba(0, 0, 0, 0.35));
}
/* 뜨는 높이도 `--mep` 를 탄다. 안 태우면 작아진 핀이 제 키만큼 널뛴다. */
@keyframes me-bob {
  0%, 100% { transform: translateY(calc(-2px * var(--mep))); }
  50% { transform: translateY(calc(-10px * var(--mep))); }
}
/* 사라지는 것을 **뒤로 미룬다**(0.85). 앞쪽에서 흐려지면 퍼져 나가는 동안은 이미
   안 보이고, 화면에는 작은 동그라미가 깜빡이는 것만 남는다. */
@keyframes me-wave {
  0% { transform: scale(0.3); opacity: 1; }
  85% { opacity: 0.45; }
  100% { transform: scale(1.5); opacity: 0; }
}
/* 움직임을 줄여 달라고 한 사람에겐 세워 둔다 — 자리는 그대로, 뜨는 만큼만 굳힌다. */
@media (prefers-reduced-motion: reduce) {
  .me .mepin { animation: none; transform: translateY(calc(-4px * var(--mep))); }
  .me .mewave { animation: none; opacity: 0.45; }
}

/* 2026-09-07 지시로 **동그라미에서 「앱 아이콘」 꼴(모서리 굴린 네모)로** 바꿨다.
   굴린 정도는 오른쪽 레일 타일과 **같은 결**이다 — 레일은 53.1px 에 11.4px(0.215),
   여기는 44px 이니 9.5px 이 같은 비율인데 이 단추는 홀로 서서 조금 더 굴려야 「앱」으로
   읽힌다(11px = 0.25 · 아이폰 아이콘이 0.2237 이다). 레일 밑에 이어 붙어 하나의
   오른쪽 기둥으로 읽히는 자리라, **여기를 고치면 `--rail-r` 도 같이 본다.** */
.locate {
  position: absolute;
  right: 12px;
  bottom: 16px;
  z-index: 5;
  width: 44px; height: 44px;
  border-radius: 11px;
  /* 2026-09-07 지시로 **반투명**이 됐다. 값은 레일 타일의 `--rail-bg` 와 **똑같다** —
     오른쪽 기둥이 한 판으로 읽혀야 하는 자리라 여기만 다른 투명도를 쓰면 어긋난다.
     (`--rail-bg` 는 `.rail` 위에 선언돼 있어 여기서는 못 쓴다. 한쪽을 고치면 다른
     쪽도 같이 본다.) 그림자는 그대로 둔다 — 밝은 지도 위에서 이것마저 없으면
     반투명한 흰 판이 배경에 녹아 단추로 안 읽힌다. */
  background: rgba(255, 255, 255, 0.72);
  color: var(--ink);
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.2);
  /* 2026-09-07 지시로 핀 **아래에 「내위치」 글자**가 붙어 위아래 두 칸이 됐다.
     `gap` 1px — 핀 끝이 뾰족해서 그 아래는 이미 빈 자리라, 더 벌리면 글자가 떨어져
     보인다. 22 + 1 + 10 = 33 이 44 안에 서서 위아래 5.5px 씩 남는다. */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
}
/* 2026-09-07 지시로 아이콘이 조준점 그림(SVG)에서 **지도에 서는 그 핀의 회색판**이 됐다.
   크기는 **높이 하나로만 잡는다** — 폭을 같이 박으면 핀(227×300)이 눌려 찌그러진다.
   `width: auto` 를 굳이 적는 것은 `.locate` 가 flex 라서다: 안 적으면 flex 계산이
   폭을 제 마음대로 정해 비율이 깨진다.
   30px 로 섰다가 **22px 이 됐다**(2026-09-07 「아래에 「내위치」 글자」 지시).
   44px 안에 글자 한 줄이 같이 서니 핀이 그만큼 자리를 내준다 — 22 + 틈 1 + 글자 10.
   ★ **여기서 더 줄이지 않는다.** 이 그림은 조준점 선 그림과 달리 테·안쪽·그림자가
   다 든 조각이라, 작아지면 그 셋이 한 색으로 뭉쳐 **어두운 덩어리**로 읽힌다
   (28px 로 냈다가 그래서 30px 로 키웠던 자리다). 글자를 키우고 싶으면 핀을 깎지 말고
   **글자 크기를 만진다** — 20px 아래로 내려가면 사람 실루엣이 안 읽힌다.
   원본은 300px 로 구워 뒀으니 3배 화면에서도 4.5배가 남는다 — 늘어나서 뭉갤 일이
   없다(`make-locate-icon.mjs` · 판정은 `locate-shot.mjs` 의 「원본배수」). */
.locate .licon {
  display: block;
  width: auto;
  height: 22px;
  /* 다 그려진 조각이라 끌리면 안 된다 — PC 에서 단추가 딸려 나온다.
     사파리는 `user-select` 를 접두어 붙은 쪽으로만 알아듣는다(아이폰이 이 화면의 절반이다). */
  -webkit-user-drag: none;
  -webkit-user-select: none;
  user-select: none;
}
/* 2026-09-07 지시 — 핀 아래 **「내위치」 글자**. (그 앞에 잠깐 서 있던 회색 타원 선은
   같은 지시로 걷었다. 되살릴 일이 있으면 이 파일의 옛 판을 본다.)

   ★ **글자를 `index.html` 이 아니라 CSS `content` 로 넣는다.** HTML 에 span 을 더하면
   «새 HTML + 캐시에 굳은 옛 CSS» 라는 어긋난 조합이 생기는데, 그 판에서는 글자가
   본문 크기(16px)로 튀어 **44px 단추를 뚫는다.** `app.css` 에는 `?v=` 가 안 붙어 있어
   그 조합이 실제로 난다. 여기 넣으면 옛 CSS 를 든 사람은 그냥 옛 모습을 보고 끝난다.
   한글이 CSS 에 들어가지만 깨지지 않는다 — 이 파일은 UTF-8 이고, 문서(UTF-8)를 따라
   읽힌다. 그래도 **판정은 화면을 찍어서** 한다(깨지면 눈에만 보인다).

   ★★ **글자는 바로 위 레일 이름표(예약·채팅·탐색·제휴문의)를 그대로 베낀다** —
   색(`--rail-ink` = `#64748b`)에 이어 **크기·굵기·자간·줄높이까지**(2026-09-07 지시
   둘). 이 단추는 레일 밑에 이어 붙어 **하나의 오른쪽 기둥**으로 읽히는 자리라, 여기만
   다른 값을 쓰면 두 판으로 갈린다 — 바탕(`--rail-bg`)을 글자로 적어 맞춰 둔 것과
   **같은 이유**다. 그 값들은 `.rail`(변수)·`.rail-body button`(글자) 쪽에 있어
   **여기서는 물려받을 수가 없고 값을 적어야 한다.** 그래서 검사가 대신 지킨다 —
   `locate-label-check.mjs` 가 레일 이름표의 **계산된 색·크기·자간**을 이 규칙과
   맞대 보고 어긋나면 실패로 낸다. **한쪽을 고치면 반드시 다른 쪽도 같이 본다.**
   (레일 쪽 11.2px 은 PC·폰이 같은 값이다 — 폰에서 바뀌는 것은 타일 크기뿐이다.)

   ★ 처음엔 10px·자간 -0.4px 로 냈다. 44px 안에 겨우 앉히려던 값인데, 레일과 같은
   11.2px 로 올려도 **여전히 든다**: 가로 「내위치」 석 자 28.4px(양옆 7.8px씩) ·
   세로 22 + 틈 1 + 12.3 = 35.3(위아래 4.3px씩). 핀과 글자는 44px 을 나눠 쓰는
   사이라 **한쪽을 키우면 반드시 다른 쪽이 준다** — 여기서 더 키우려면 핀 22px 을
   깎아야 하는데, 20px 밑으로 내려가면 테·안쪽·그림자가 한 색으로 뭉쳐 사람 실루엣이
   안 읽힌다. **이 조합이 천장이다.** */
.locate::after {
  content: '내위치';
  font-size: 11.2px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.2px;
  color: #64748b;
  white-space: nowrap;
  /* 44px 안에서 flex 가 글자를 줄이거나 늘이지 못하게 못 박는다. */
  flex: none;
  pointer-events: none;
}
/* 옛 조준점 SVG 를 든 판(캐시에 굳은 index.html)에서도 크기가 안 튀게 남겨 둔다. */
.locate svg { width: 22px; height: 22px; }
/* ★ **누른 뒤에도 아이콘 색은 그대로다**(2026-09-07 지시). 전에는 «내 자리를 잡아 뒀다»를
   파란색(SVG 시절)으로, 그다음엔 진한 회색으로 알렸는데 — 그 알림을 걷었다.
   자리를 잡았다는 것은 **지도가 내 자리로 옮겨 가고 핀이 서는 것**으로 이미 말하고 있어서,
   단추까지 같이 변하면 눌릴 때마다 색이 바뀌는 것으로만 읽힌다.

   `aria-pressed` 는 **그대로 켠다** — 화면낭독기에게는 여전히 상태이고, 검사 도구도
   그 값으로 「자리를 잡았나」를 잰다. 여기서 걷은 것은 **눈에 보이는 색 변화뿐**이다.
   그러니 이 자리에 `color`·`filter` 를 다시 넣지 않는다.

   흐려졌다 밝아지는 것(`.busy`)은 **그대로 둔다** — 그건 상태 표시가 아니라
   「지금 재는 중」이라는 말이고, 끝나면 원래 색으로 돌아온다. */
/* 자리를 재는 동안. 전에는 `opacity: 0.5` 로 눌러 놨는데 — 이제는 핀이 **먼저** 서고
   그 뒤에 값을 다듬는 자리라, 흐려진 단추가 「눌린 채 멈췄다」로 읽힌다.
   자리는 벌써 나왔고 아직 다듬는 중이라는 뜻이 되게 **숨만 쉬게** 둔다.

   ★ **한 번만 쉰다**(2026-09-07 지시). `infinite` 였을 때는 GPS 가 3초 걸리면
   1.1초짜리가 **세 바퀴** 돌아 「단추가 세 번 깜빡인다」로 보였다(라이브 실측:
   시작 1 + 되풀이 2). 깜빡임은 «누른 것이 먹혔다»는 대답이면 족하고, 그 뒤로는
   핀이 서는 것이 이미 진행을 말한다. **횟수를 늘리지 않는다** — 늘리는 순간
   기다림의 길이만큼 깜빡임이 늘어나 다시 「몇 번이나 깜빡인다」가 된다.
   재는 법은 `_tools/locate-shot.mjs` — 3.6초를 지켜보며 되풀이가 0회여야 한다. */
.locate.busy { animation: locateBusy 1.1s ease-in-out 1; }
@keyframes locateBusy { 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) {
  .locate.busy { animation: none; opacity: 0.75; }
}

/* — 카드(폰) / 목록(PC) — */

/* 폰에서는 **한 장으로 꽉 찬다**(2026-09-06 지시). 전에는 화면 아래에서 72%만
   올라오는 반쪽 카드였는데, 캐릭터를 눌러 여는 것은 「지도 위 미리보기」가 아니라
   상세 화면이다 — 예약·채팅(`.page`)과 같은 자리·같은 크기로 선다.
   위는 **롤링바 아래**(`--roll`)에서 시작한다. 띠를 덮으면 굴릴 때 글이 반투명
   띠 뒤로 비쳐 지나간다 — `.page` 가 `inset: var(--roll) 0 0` 인 것과 같은 이유다.
   올라오는 동작(`translateY`)은 그대로 둔다. 자리만 커졌지 여는 몸짓은 같다. */
.panel {
  background: var(--card);
  border-radius: 0;
  box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.14);
  position: absolute;
  left: 0; right: 0;
  top: var(--roll); bottom: 0;
  z-index: 6;
  max-height: none;
  display: flex;
  flex-direction: column;
  transform: translateY(0);
  transition: transform 0.22s ease;
}
.panel[hidden] { display: flex !important; transform: translateY(110%); pointer-events: none; }
/* 드래그 바는 걷었다(2026-09-05 지시) — 끌어서 여닫는 동작이 없는데
   손잡이만 있으면 되지도 않는 조작을 시키는 그림이 된다. */
.panel .handle { display: none; }
/* 닫기 — **사진 위가 아니라 그 위쪽 빈 자리**에 앉는다. 사진에 겹치면
   어두운 사진에서 ✕ 가 안 보이고, 사진을 누르려다 창이 닫힌다.
   `.body` 밖(카드 바로 밑)이라 **글을 내려도 제자리에 남는다** — 자리는
   `.panel .body` 의 위쪽 여백 46px 이 비워 둔다. */
.panel > .close {
  position: absolute;
  /* 롤링바 바로 밑에 딱 붙지 않게 한 칸 내린다 — 붙으면 띠와 ✕ 가 한 덩어리로 보인다. */
  right: 8px; top: 12px;
  z-index: 2;
  font-size: 18px;
  line-height: 1;
  color: var(--sub);
  padding: 8px 12px;
}
.panel .close:hover { color: var(--ink); }
/* 조회 통계 — ✕ 왼쪽에 같은 줄로 선다(2026-09-07 지시).
   같은 날 **찜 하트가 그 둘 사이로 들어오면서** 한 칸 더 왼쪽이 됐다:
   ✕ 가 오른쪽 8px 에서 폭 42px 을 먹고(8 + 42 = 50), 하트가 38px 을 먹으니
   (50 + 38 = 88) 그 다음 자리가 여기다.
   높이를 ✕ 와 같은 34px 로 맞춘다 — 46px 띠 안에서 단추들의 중심이 어긋나면
   나란히 선 것이 아니라 하나가 흘러내린 것으로 보인다.
   **색은 ✕ 와 같은 흐린 잉크다.** 금색으로 세우면 이 줄에서 이것만 눌러야 할
   것처럼 튄다(국모의 창 머리도 같은 이유로 흐린 잉크 하나로 세운다). */
.panel > .pstats {
  position: absolute;
  right: 88px; top: 12px;
  z-index: 2;
  width: 38px; height: 34px;
  display: grid;
  place-items: center;
  color: var(--sub);
  cursor: pointer;
}
.panel > .pstats:hover { color: var(--ink); }
.panel > .pstats svg { display: block; }
/* 찜 — **그래프와 ✕ 사이**(2026-09-07 지시). 상자·높이·색을 그래프 단추와 똑같이
   맞춘다: 세 단추가 한 줄에 나란히 선 것으로 읽혀야 한다.

   **꺼짐은 윤곽선, 켜짐은 속을 채운 빨강**(2026-09-08 지시로 금색에서 바뀌었다).
   색만 바꾸면 어두운 사진 위·밝은 판 위에서 「흐린 회색」과 잘 안 갈린다 —
   모양이 달라야 곁눈으로도 읽힌다.
   `aria-pressed` 하나로만 가른다(JS 도 그 값 하나만 쓴다 — 두 곳에 적어 두면
   어긋나는 날 화면만 봐서는 어느 쪽이 참인지 모른다). */
.panel > .pfav {
  position: absolute;
  right: 50px; top: 12px;
  z-index: 2;
  width: 38px; height: 34px;
  display: grid;
  place-items: center;
  color: var(--sub);
  cursor: pointer;
}
.panel > .pfav:hover { color: var(--ink); }
.panel > .pfav svg {
  display: block;
  fill: none;
  stroke: currentColor;
  /* 레일의 하트(18px, `index.html` 에 1.8 이 박혀 있다)보다 **한 단 굵다**(2026-09-08 지시).
     여기 것은 사진 위·흰 띠 위 어디에도 설 수 있는 20px 짜리라 1.8 로는 가늘게 보였다. */
  stroke-width: 2.4;
  stroke-linejoin: round;
}
.panel > .pfav[aria-pressed="true"] { color: var(--fav-on); }
.panel > .pfav[aria-pressed="true"] svg { fill: currentColor; }
/* 담는 그 순간에만 한 번 뛴다. 담긴 뒤에도 계속 움직이면 지도 위에 상시 애니메이션이
   하나 더 늘어난다 — 국모에서 그것이 끌기를 통째로 느리게 만든 자리를 이미 겪었다. */
.panel > .pfav.pop svg { animation: favpop 0.3s ease; }
@keyframes favpop {
  0% { transform: scale(1); }
  45% { transform: scale(1.35); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .panel > .pfav.pop svg { animation: none; }
}
/* 업체명 — ✕ 와 **같은 줄·같은 붙박이**다(2026-09-07 지시). 사진 밑에 있던 것을
   위 46px 띠로 올렸다. `.body` 밖이라 글을 내려도 제자리에 남는다.
   높이를 ✕ 와 같은 34px 로 잡아 두 글자가 한 줄에 나란히 앉는다(12 + 34 = 46).
   오른쪽에 단추가 **셋**이다 — ✕(8 + 42 = 50) · 찜(50 + 38 = 88) ·
   통계(88 + 38 = 126). 그래서 오른쪽을 132px 비우고, 긴 이름은 … 로 자른다.
   **단추를 늘리면 이 값도 같이 늘린다** — 안 늘리면 긴 이름이 단추 밑을 지나간다
   (이름은 누름을 안 먹으니 눌리기는 하지만, 글자와 그림이 겹쳐 보인다).
   누름을 안 먹는다 — 이름 위를 눌러 단추가 안 눌리는 일이 없게. */
.panel > .ptitle {
  position: absolute;
  left: 16px; right: 132px; top: 12px;
  height: 34px; line-height: 34px;
  z-index: 2;
  font-size: 17px; font-weight: 800; letter-spacing: -0.4px;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
}
/* ✕ 가 붙박이가 되면서 **글이 그 밑으로 지나간다.** 위 46px 을 카드 바탕색으로
   막아 둔다 — `.panel .body` 가 비워 두는 여백과 같은 값이라, 안 굴렸을 때는
   원래대로 빈자리고 굴렸을 때만 덮개 노릇을 한다. ✕(z 2)보다 아래(z 1)다. */
.panel::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 46px;
  background: var(--card);
  z-index: 1;
  pointer-events: none;
}
/* 위쪽 40px 은 **닫기 단추 자리**다. 사진이 그 밑에서 시작해 서로 안 겹친다. */
.panel .body { position: relative; overflow-y: auto; padding: 46px 16px 16px; }

/* 두루마리 막대를 **화면 전체에서** 없앤다(2026-09-05 지시).
   굴리는 것은 그대로 되고 막대만 안 보인다. 카드가 지도 위에 얹히는 판이라
   회색 막대가 지도까지 갈라 보였다. 한 군데만 막으면 다른 창에서 또 나온다. */
html, body, .panel .body, .page, .hits, .signup .sheet, .filters,
.mreel, .mvreel, .mvcell {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

@media (prefers-reduced-motion: reduce) {
  .panel { transition: none; }
}

/* — 카드 조각 — */

.photo { border-radius: 14px; overflow: hidden; position: relative; background: #222; }
.photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* 여기 있던 `.photo .tag`(사진 왼쪽 위 「클럽 · 이태원」 칩)를 걷어냈다(2026-09-08 지시).
   두 카드 다 안 얹는다 — 종류·지역은 「매장정보」 첫 칸의 `.vi-chips` 가,
   거리는 같은 탭의 `.vi-dist` 줄이 들고 있다. `.tag` 라는 이름은 관리자 머리띠와
   날씨 특보가 각자 따로 쓰니 그쪽 규칙을 이 자리 것으로 착각하지 않는다. */
.photo .live {
  position: absolute; left: 10px; bottom: 10px;
  font-size: 10px; background: var(--red); color: #fff;
  padding: 2px 7px; border-radius: 6px; font-weight: 700;
}

/* 카드 맨 위 사진 묶음 — **2~15장을 좌우로 넘긴다**(2026-09-07 지시).
   가격표 액자와 **같은 부품**(`.mreel`·`.mnav`·`data-dots`)을 쓰되, 여기는
   높이가 정해진 상자라 장마다 폭·높이를 꽉 채운다 — 가격표는 그림 비율대로
   늘어나는 자리여서 규칙이 다르다. 그래서 `.mreel` 을 여기서 덮어쓴다. */
.photo .sreel { height: 100%; align-items: stretch; }
.photo .sreel img {
  flex: 0 0 100%; width: 100%; height: 100%;
  object-fit: cover;
  scroll-snap-align: center;
  /* 여기는 **탭해도 안 커진다**(2026-09-07 지시) — `.mreel img` 의 `zoom-in` 을 되돌린다.
     커서가 돋보기인 채로 두면 눌러도 아무 일이 없어 고장으로 읽힌다. */
  cursor: default;
}

/* 점은 **사진 위**다. 가격표처럼 액자 밖(`.mfoot`)에 두면 사진과 탭 띠 사이가
   한 줄만큼 벌어져 카드 맨 위가 통째로 내려앉는다.
   열다섯까지 서므로 동그라미를 작게 잡는다 — 15 × 6px + 14 × 5px = 160px 이라
   폰 카드 폭(340px) 안쪽에 든다. 밝은 사진 위에서도 보이라고 그림자를 깐다. */
.photo .sdots {
  position: absolute; left: 0; right: 0; bottom: 9px;
  display: flex; gap: 5px; justify-content: center;
  pointer-events: none;
}
.photo .sdots i {
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.65);
}
.photo .sdots i.on { background: var(--y); }
/* 「지금 N명 예약 중」은 점 위로 한 줄 올린다 — 안 올리면 왼쪽 끝 점 두어 개를 덮는다. */
.photo.shots .live { bottom: 24px; }

.row { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.name { font-size: 17px; font-weight: 800; letter-spacing: -0.4px; }
/* .amount(카드 머리의 큰 금액)는 2026-09-07 지시로 걷었다 — 두 카드 모두에서 뺐고
   그 수는 「포인트」탭 첫 줄(.vi-pt) 하나만 남았다. 규칙도 같이 지운다. */
.cond { font-size: 12px; color: var(--sub); }

.chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.chip {
  font-size: 10.5px; padding: 3px 8px; border-radius: 8px;
  background: var(--chip-bg); color: var(--chip-ink); font-weight: 500;
}
.chip.g { background: #e7f8ee; color: #157a3c; }
.chip.y { background: var(--y2); color: #5a4200; }
.chip.r { background: var(--danger-bg); color: var(--danger-ink); }
.chip.k { background: var(--ink); color: #fff; }
.chip.dim { background: var(--dim-bg); color: #6b6e78; }

/* 같은 매장의 시간대 갈아타기 — 캠페인 카드 안(2026-09-07).
   묶음을 누르면 뜨던 「시간대 N개」 고르는 화면을 걷어내면서, 시간대를 고르는 일이
   카드 안으로 들어왔다. 고른 판은 검게 채워 **지금 보고 있는 것**을 말한다 —
   위 `.chip` 은 읽기만 하는 꼬리표라 따로 둔다(눌리는 것과 안 눌리는 것을 안 섞는다). */
.slotpick { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
.slotpick button {
  font-size: 12px; font-weight: 700; padding: 7px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: #fff; color: var(--sub); cursor: pointer;
}
.slotpick button[aria-pressed="true"] {
  border-color: var(--ink); background: var(--ink); color: #fff;
}

.kv {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 12px;
}
.kv:last-child { border: 0; }
.kv span { color: var(--sub); flex: none; }
.kv b { font-weight: 600; text-align: right; }

.notice {
  background: var(--ink); color: #fff; border-radius: 14px;
  padding: 10px 12px; font-size: 11.5px; line-height: 1.5; margin: 10px 0;
}

.tile {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 14px;
  background: var(--request-bg); margin-bottom: 6px;
}
.tile i {
  width: 30px; height: 30px; border-radius: 10px; background: var(--y);
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-style: normal; flex: none;
}
.tile b { display: block; font-size: 12px; }
.tile span { font-size: 11px; color: var(--sub); }

.btn {
  display: block; width: 100%;
  padding: 14px; border-radius: 14px;
  background: var(--ink); color: #fff;
  font-weight: 700; font-size: 14px; letter-spacing: -0.2px; text-align: center;
}
.btn.y { background: var(--y); color: #111; }
.btn.gh { background: #fff; color: var(--ink); border: 1.5px solid var(--line); }
.btn.dim { background: var(--dim-bg); color: var(--dim-ink); cursor: not-allowed; }
/* 신고 전용. **빨강은 경고·정지·신고에만 쓴다** — 다른 데로 새면
   빨강이 「눈에 띄는 색」이 되어 정작 경고가 안 읽힌다. */
.btn.r { background: var(--red); color: #fff; }
.btn.row2 { display: inline-block; width: auto; flex: 1; padding: 11px; font-size: 12px; }

.warn {
  background: var(--warn-bg); border-radius: 14px;
  padding: 10px 12px; margin: 8px 0;
}
.warn b { font-size: 12.5px; color: var(--warn-ink); }
.warn .kv { border-color: var(--warn-line); font-size: 11px; }
.warn .kv span { color: #7a5a12; }

.danger {
  background: var(--danger-bg); border: 1.5px solid var(--danger-line);
  border-radius: 14px; padding: 12px;
}
.danger b { color: var(--danger-ink); font-size: 13px; }
.danger p { margin: 4px 0 0; font-size: 11.5px; color: var(--danger-sub); line-height: 1.5; }

/* — 포인트 · 가격표 · 테이블맵 —
 *
 * 매장 이름 **바로 밑, 조건표 바로 위**에 앉는다. 조건(시간·체크인·최소 체류)은
 * 이 서비스가 약속하는 것이라 늘 첫 화면이다 — 그래서 「포인트」가 기본이고,
 * 가격표·테이블맵은 눌러야 나온다.
 *
 * **띠는 상세 화면이면 어디서나 여섯이다**(캠페인 카드·실존 클럽 카드 둘 다,
 * 2026-09-07 지시 — 앞에 「매장정보」, 끝에 「홍보영상」·「방문후기」가 붙었다). 매장이 안 올린 자리는
 * 빈 칸이 아니라 예시 표(`.mfall`)가 채운다. 단 매장정보(`.vinfo`)만은 예시를 안 세운다. */

.mtabs {
  display: flex;
  margin-top: 12px;
  border-bottom: 1px solid var(--line);
}
/* 여섯이 한 줄에 선다. 폰 카드 폭이 340px 안쪽이라 13px 로는 「매장정보」가 접히면서
   이 칸만 두 줄이 된다 — 글자를 줄이고 **줄바꿈을 막는다.**
   다섯째(홍보영상)에서 한 칸이 61px 였고, 여섯째(방문후기)가 붙으면서 **51px** 이 됐다.
   11.5px 네 글자에 자간을 −0.5px 로 조이면 45px 라 아직 들어간다 —
   **일곱째 칸을 붙일 자리는 여기 없다.** 더 붙일 것이 생기면 글자를 또 깎지 말고
   띠를 옆으로 미는 물건으로 바꾼다(글자를 깎으면 어느 날 조용히 못 읽게 된다).
   `_tools/review-tab-audit.mjs` 가 여섯 칸이 **한 줄에 서 있는지**(칸 높이가 다 같은지)
   폰·PC 에서 잰다 — 접히면 그 칸만 키가 커진다. */
.mtabs button {
  flex: 1;
  min-width: 0;
  padding: 10px 0 9px;
  font-size: 11.5px;
  letter-spacing: -0.5px;
  white-space: nowrap;
  color: var(--sub);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.mtabs button[aria-selected="true"] { color: var(--ink); font-weight: 700; border-bottom-color: var(--y); }

/* 그림 액자. 배경이 검은 이유는 가격표·도면이 거의 다 검은 판이어서다 —
   흰 바탕에 두면 그림 가장자리가 어디까지인지 안 보인다. */
.mframe {
  position: relative;
  margin-top: 12px;
  border-radius: 14px;
  overflow: hidden;
  background: #0a0a0b;
}
.mreel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  align-items: flex-start;
}
.mreel img {
  flex: 0 0 100%;
  width: 100%;
  display: block;
  scroll-snap-align: center;
  cursor: zoom-in;
}
.mnav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(0, 0, 0, 0.55); color: #fff;
  font-size: 20px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.mnav[hidden] { display: none; }
.mnav.p { left: 8px; }
.mnav.n { right: 8px; }
/* 점과 갱신 날짜가 같이 앉는 줄. 날짜는 **그림 밖**이다 — 매장이 준 그림은
   네 귀퉁이가 이미 차 있어서, 어디에 얹어도 언젠가 남의 글씨를 가린다. */
.mfoot {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  min-height: 20px; margin-top: 8px;
}
.mstamp {
  position: absolute; right: 0;
  font-size: 10px; color: var(--sub);
  background: var(--chip-bg);
  padding: 2px 7px; border-radius: 7px;
  font-variant-numeric: tabular-nums;
}
.mdots { display: flex; gap: 5px; justify-content: center; }
.mdots i { width: 14px; height: 3px; border-radius: 2px; background: var(--dim-bg); }
.mdots i.on { background: var(--y); width: 20px; }
.mhint { text-align: center; margin: 6px 0 0; }

/* 크게 보기 — 화면을 통째로 쓴다. 카드 폭(폰 기준 340px)에서는
   가격표 글자가 10px 안쪽이라 값을 못 읽는다. */
/* **국모 선거공보 뷰어와 같은 꼴이다**(2026-09-07 지시) — 왼쪽 위 「N / M장」 알약 ·
   오른쪽 위 ✕ · 좌우 큰 동그란 화살표. 아래 값은 전부 국모의
   `campaign_material_viewer.dart` 에서 그대로 가져온 것이라 **혼자 손대지 않는다.**

   조각들은 **띠가 아니라 그림 위에 뜬다**(국모는 Stack). 띠로 자리를 차지하게 두면
   그림이 그만큼 눌려서 같은 그림이 국모보다 작게 보인다. */
.mviewer {
  position: fixed; inset: 0; z-index: 60;
  background: #08080a;
  /* 확대해서 그림을 끌어 볼 때 브라우저에겐 그것이 「그림 위에서 누른 채 옮기는 것」이라
     그림 한 장이 통째로 파랗게 잡힌다(지도 표식과 같은 일이다 — 「지도 위 아이콘」 절).
     ★ **그림 위에서 시작한 끌기는 dnd 라 고치기 전에도 선택이 안 생긴다** — 재현은
     빈 여백에서 출발해 그림을 가로지르는 것이다(`hero-shots-audit` 의 `dragAcrossPhoto`).
     사파리는 접두어 붙은 쪽으로만 알아듣는다. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
/* 끌면 그림이 통째로 딸려 나가는 것(브라우저 기본 이미지 끌기)도 같이 막는다 —
   `user-select` 만으로는 안 막히고, 끄는 손짓이 「파일 옮기기」로 새면 확대가 안 굴러간다. */
.mviewer img { -webkit-user-drag: none; user-drag: none; }
.mviewer[hidden] { display: none; }

/* 위 줄 — 알약(왼쪽)과 ✕(오른쪽). 국모 값: 위 8px(x2) · 좌우 16px(x4). */
.mvtop {
  position: absolute; top: 8px; left: 16px; right: 16px; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  /* 줄 자체는 통과시키고 그 안의 조각만 받는다 — 안 그러면 그림 맨 윗줄이 안 눌린다. */
  pointer-events: none;
}
.mvtop > * { pointer-events: auto; }
/* 「2 / 11장」 알약. 국모 값: 검은 0.55 · 라운드 16 · 안쪽 4/12 · 12.5px·600·자간 0.2. */
.mvpill {
  margin-right: auto;
  padding: 4px 12px;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 16px;
  color: #fff;
  font-size: 12.5px; font-weight: 600; line-height: 1.3; letter-spacing: 0.2px;
  font-variant-numeric: tabular-nums;
}
.mvpill:empty { display: none; }
/* ✕ 와 확대 — 국모 `_ViewerButton`: 44×44 · 아이콘 22px · 흰색 · 라운드 16. */
.mvbtn {
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 16px;
  color: #fff; font-size: 22px; line-height: 1;
}
.mvbtn:focus-visible { background: rgba(255, 255, 255, 0.15); }
/* ★ 아래 가운데 확대(＋/－) 알약은 걷었다(2026-09-07 지시). 국모에는 있지만 여기서는
   **그림을 탭하는 것**이 확대라 같은 일을 하는 자리가 둘이었고, 알약이 그림 아랫줄을 덮었다.
   그만큼 `.mvcell` 아래 여백도 같이 줄인다. */

/* 좌우 화살표 — 국모 `_ArrowButton` 값 그대로다.
   원 58px · 아이콘 34px · 바탕 `#8E8E93` 알파 0x8C(=0.55) · 좌우 여백 8px(x2).
   바탕이 검정이 아니라 옅은 회색인 이유는 국모가 적어 둔 그대로다 —
   **어두운 지면 위에서 검은 원이 통째로 사라진다.** 흰 화살표가 이 회색 위에서 3.3:1 이다.
   반투명이라 밝은 지면 위에서는 바탕이 지면에 물들므로 아이콘에 그림자를 깐다. */
.mvarrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  /* 누르는 자리는 원이 줄어도 44px 아래로 안 내려간다(국모 `_arrowTapMin`). */
  width: max(58px, 44px); height: max(58px, 44px);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
}
.mvarrow[hidden] { display: none; }
.mvarrow.p { left: 8px; }
.mvarrow.n { right: 8px; }
.mvarrow::before {
  content: ''; position: absolute;
  width: 58px; height: 58px; border-radius: 50%;
  background: rgba(142, 142, 147, 0.55);
}
.mvarrow svg {
  position: relative; width: 34px; height: 34px;
  filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.55));
}
.mvarrow:focus-visible::before { outline: 2px solid rgba(255, 255, 255, 0.6); outline-offset: 2px; }
/* ★ 폰에서만 원을 절반으로 줄인다(국모 값 36/21) — 화면이 좁아 원 둘이 지면 양옆을
   크게 덮었다. **누르는 자리는 44px 그대로다.** 국모의 compact 문턱과 같은 600px 이다. */
@media (max-width: 599px) {
  .mvarrow { width: 44px; height: 44px; }
  .mvarrow::before { width: 36px; height: 36px; }
  .mvarrow svg { width: 21px; height: 21px; }
}

.mvreel {
  position: absolute; inset: 0;
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}
.mvcell {
  flex: 0 0 100%;
  scroll-snap-align: center;
  overflow: auto;
  display: flex; align-items: center; justify-content: center;
  /* 위 여백은 알약·✕ 가 그림 귀퉁이를 덮지 않을 만큼이다. 아래는 확대 알약을 걷은 뒤로
     덮는 것이 없어서 여백만 남긴다(72 → 20). */
  padding: 56px 10px 20px;
}
/* ★ **눌러도 안 커진다**(2026-09-07 지시). 돋보기 커서도 같이 없앴다 —
   커서만 남기면 눌러도 아무 일이 없어 고장으로 읽힌다.
   `.mviewer.zoom` 규칙 셋(스냅 끄기·왼쪽 위 붙이기·200%)도 통째로 걷었다. */
.mvcell img { max-width: 100%; max-height: 100%; object-fit: contain; cursor: default; }

/* — 홍보영상 — 탭 띠의 다섯째 칸(2026-09-07 지시).
 *
 * 생김새는 **구글이 검색 결과에 영상을 세우는 꼴**이다(지시받은 그림 그대로):
 * 왼쪽에 가로 섬네일, 오른쪽에 제목 두 줄, 그 아래 「채널 · 며칠 전」.
 * 판은 **흰색**이다(2026-09-07 저녁 지시) — `.mfall` 맨몸은 아직 검지만
 * `.mfall.ytfall` 은 위 「흰 판」 규칙에 들어 색이 통째로 갈린다.
 *
 * ⚠ `.vlist`·`.vrow` 는 **「내 주변」 목록의 것이다.** 이름을 나눠 쓰면 지도 옆
 * 목록이 같이 바뀐다 — 그래서 여기는 `yt-` 계열로 따로 둔다. */
.ytfall { padding: 10px; }
.ytlist { display: flex; flex-direction: column; }
.ytrow {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 8px 4px;
  text-align: left;
  border-bottom: 1px solid var(--night-line);
}
.ytlist .ytrow:last-child { border-bottom: 0; }
.ytrow:hover, .ytrow:focus { background: var(--night2); }
/* 섬네일 자리. **16:9 를 못 박는다** — 그림이 늦게 오거나 아예 안 와도 줄 높이가
   흔들리지 않아야 목록이 한 칸씩 밀리지 않는다. */
.ytrow .ytthumb {
  position: relative;
  flex: none;
  width: 112px;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  overflow: hidden;
  background: var(--night2);
  border: 1px solid var(--night-line);
}
.ytrow .ytthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 재생 세모. 그림이 없는 예시 줄에서는 이것 하나가 「영상 자리」라고 말한다. */
.ytrow .ytplay {
  position: absolute; inset: 0; margin: auto;
  width: 26px; height: 26px; border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
}
.ytrow .ytplay::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 0; height: 0;
  border-left: 8px solid #fff;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transform: translateX(1px);
}
.ytrow .ytthumb em {
  position: absolute; right: 4px; bottom: 4px;
  font-style: normal; font-size: 10px; font-weight: 700;
  color: #fff; background: rgba(0, 0, 0, 0.75);
  padding: 1px 4px; border-radius: 4px;
  font-variant-numeric: tabular-nums;
}
.ytrow .ytmeta { flex: 1; min-width: 0; }
/* 제목은 **두 줄까지**다. 한 줄로 자르면 유튜브 제목은 거의 다 끝이 날아가고
   (앞쪽이 채널 이름·해시태그인 경우가 흔하다), 안 자르면 다섯 줄짜리가 목록을 먹는다. */
.ytrow .ytmeta b {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 12.5px; font-weight: 600; line-height: 1.45; color: var(--panel-ink);
}
.ytrow .ytmeta span {
  display: block; margin-top: 4px;
  font-size: 11px; color: var(--night-sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 재생기 — **목록 위, 이 탭 안에서 켜진다**(2026-09-07 지시: 「팝업 말고 홍보영상 틀
   안에서, 국모와 같은 구조로」). 국모 뉴스 탭이 꼭 이 꼴이다 — 목록 위에 붙박이로 서고,
   다른 영상을 골라도 자리는 그대로이며 아래 목록만 바뀐다.
   창을 띄우지 않는 이유는 폰에서 뚜렷하다: 덮는 창은 카드·지도를 다 가리고, 닫으면
   보던 목록의 어디였는지가 사라진다.

   ⚠ `.ytv`·`.ytv-box`(덮는 창 뼈대)는 **지우지 않는다** — 방문후기 쓰기 창이 그 둘을
   그대로 쓴다(`openReviewForm`). 여기서 쓰지 않게 됐을 뿐이다. */
.ytv {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(0, 0, 0, 0.82);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.ytv[hidden] { display: none; }
.ytv-box {
  width: min(680px, 100%);
  background: #0a0a0b;
  border: 1px solid var(--night-line);
  border-radius: 14px;
  overflow: hidden;
}
/* **재생 중에는 카드 맨 위에 붙어 선다**(2026-09-08 지시: 「영상이 플레이될 때는
   영상이 고정되고 아래 게시글만 오르내리게」). 국모 뉴스 탭과 같은 몫이다 —
   목록의 아래쪽 줄을 누르러 굴려 내려가도 보고 있던 영상이 화면 밖으로 안 나간다.

   ⚠ **`top` 은 0 이지 46px 이 아니다.** 카드 위 46px 은 업체명·✕·찜·통계가 앉은
   붙박이 띠고 그 자리는 `.panel::before` 가 카드색으로 덮는데(z 1), 그 46px 은
   **구르는 상자(`.panel .body`)의 위쪽 여백이라 붙박이 자리에 이미 들어 있다** —
   브라우저는 여백 안쪽부터 세기 때문이다(재어 봤다: 카드 위 80 + 여백 46 + top).
   46px 이라고 적으면 92px 에 서서 띠와 영상 사이가 한 뼘 벌어진다.
   그러니 `.panel .body` 의 위쪽 여백을 고치면 **이 붙박이 자리도 같이 움직인다.**

   `z-index: 1` 이 필요하다. 목록의 섬네일(`.ytrow .ytthumb`)이 `position: relative` 라
   붙박이 재생기와 **같은 층**에 서는데, DOM 에서 뒤에 있어 그냥 두면 섬네일이 영상
   위를 지나간다. 덮개(`.panel::before`)도 z 1 이지만 붙박이가 46px 밑으로는 안 내려와
   둘이 겹칠 일이 없다.

   좌우·위 여백을 **음수 마진으로 걷고 같은 만큼 안쪽 여백으로 되돌린다** — 판
   (`.ytfall`)의 안쪽 여백 10px 이 뚫려 있으면 굴러 올라오는 줄이 그 틈으로 비친다.
   생김새는 그대로고 **불투명한 넓이만** 판 끝까지 넓어진다. 아래 10px 도 안쪽 여백으로
   옮겨서(전에는 `margin-bottom`) 재생기 바로 밑에서 줄이 튀어나오지 않게 한다. */
.ytnow {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: -10px -10px 0;
  padding: 10px 10px 10px;
  background: var(--night);
  /* 판 모서리가 14px 인데 테두리 1px 안쪽이라 13px. 각지게 두면 붙박이 판이
     둥근 모서리 밖으로 흰 귀퉁이를 낸다. */
  border-radius: 13px 13px 0 0;
}
.ytnow[hidden] { display: none; }
/* 16:9 를 못 박는다 — 재생기 호스트(`.kmytp`)가 `height:100%` 라, 부모가 높이를 안 들면
   재생기가 0px 로 접힌다. 세로 영상은 그 안에서 재생기가 스스로 기둥으로 좁힌다.
   ⚠ **여기서 iframe 에 자리를 주지 않는다.** `.ytnow-frame iframe { inset:0 }` 같은 줄 하나가
   크롭(`.kmytp-frame` 의 `top:-120px`)을 이겨 **유튜브 제목·로고를 도로 불러온다** —
   그것으로 「국모와 100% 같게」가 깨진다. 재생기 CSS 는 `ytplayer.js` 가 스스로 심는다. */
.ytnow-frame {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: #000; border-radius: 10px; overflow: hidden;
}
.ytnow-none {
  position: absolute; inset: 0; margin: 0;
  display: flex; align-items: center; justify-content: center; text-align: center;
  font-size: 12.5px; line-height: 1.7; color: var(--night-sub);
}
/* 제목 줄 — **✕ 는 첫 줄 눈높이에 선다**(국모와 같다). 가운데 맞춤이면 제목이 두 줄일
   때 단추가 아래로 내려가, 같은 자리를 두 번 못 누른다. */
.ytnow-t {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 2px 10px;
  border-bottom: 1px solid var(--night-line);
}
.ytnow-t b {
  flex: 1; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: 12.5px; font-weight: 600; line-height: 1.45; color: var(--panel-ink);
}
.ytnow-t button {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  font-size: 13px; color: var(--night-sub); background: var(--night2);
}
/* 지금 트는 줄 — 목록에도 표시가 남아야 어디까지 봤는지 안다(국모 목록과 같은 몫). */
.ytrow.on { background: var(--panel-keysoft); box-shadow: inset 3px 0 0 var(--panel-mark); }
.ytrow.on .ytmeta b { color: var(--panel-key); }

/* — 방문후기 — 탭 띠의 여섯째 칸(2026-09-07 지시).
 *
 * 판은 **흰색**이다(2026-09-07 저녁 지시). `.mfall` 맨몸(가격표·테이블맵)은 검은 채로
 * 남고 `.mfall.rvfall` 만 위 「흰 판」 규칙에 들어간다.
 *
 * ⚠ 이름을 `.rv-` 로 뗀다. `.vrow`·`.vlist` 는 「내 주변」 목록의 것이고
 *   `.yt-` 는 홍보영상의 것이다 — 나눠 쓰면 지도 옆 목록이 같이 바뀐다. */
.rvfall { padding: 12px; }

/* 요약 — 왼쪽에 평균, 오른쪽에 별점 분포. 폰 카드 폭에서 둘이 나란히 들어간다. */
.rvsum {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--night-line);
}
.rvavg { flex: none; text-align: center; }
.rvavg > b {
  display: block;
  font-size: 30px; line-height: 1.05; font-weight: 800;
  color: var(--panel-ink);
  font-variant-numeric: tabular-nums;
}
/* 개수 줄에 **제 이름을 준다.** 별(`.rvst`)도 이 칸의 직계 `<span>` 이라
   `.rvavg > span` 으로 잡으면 별을 집는다 — 검사가 실제로 그렇게 헛짚었다. */
.rvavg > .rvn { display: block; margin-top: 3px; font-size: 11px; color: var(--night-sub); }

/* 별 다섯 — **회색 다섯 장 위에 금색 다섯 장을 겹쳐 놓고 폭으로 자른다.**
   반 개짜리를 따로 그리지 않아도 4.1 이 4.1 만큼 칠해진다. */
.rvst {
  position: relative;
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
  font-size: 11px;
  letter-spacing: 0.5px;
}
.rvst i { font-style: normal; color: var(--night-line); }
.rvst b {
  position: absolute; left: 0; top: 0;
  overflow: hidden;
  color: var(--panel-mark);
  font-weight: 400;
}
.rvst-b { font-size: 14px; margin-top: 4px; }

/* 분포 막대. **가장 많은 칸을 꽉 채운 상대 길이**다 — 전체로 나누면 열 개짜리
   목록에서 막대가 전부 손톱만 해져 아무것도 안 보인다(`reviewSummary` 주석). */
.rvbars { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.rvbar { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--night-sub); }
/* 왼쪽 숫자는 「5점」처럼 **점을 붙여 굵은 검정으로** 적는다(2026-09-08 지시).
   폭은 두 글자가 들어가게 8px→22px. 검정은 토큰이 아니라 **리터럴**이다 —
   내 색 이름이 라이브에 없으면 조용히 안 보이는 색이 된다. */
.rvbar > span:first-child { width: 22px; text-align: right; font-weight: 700; color: #111; }
.rvbar > span:last-child { width: 12px; text-align: left; font-variant-numeric: tabular-nums; }
/* 막대 굵기는 **8px** — 2026-09-08 지시로 4px 에서 두 배로 키웠다. 얇으면 「분포」가
   아니라 밑줄처럼 읽힌다(→ [[owner-glance-not-graph]]: 한눈에 = 크기 이야기다). */
.rvbar i { flex: 1; height: 8px; border-radius: 4px; background: var(--night2); overflow: hidden; }
.rvbar em { display: block; height: 100%; border-radius: 4px; background: var(--panel-mark); }

/* 「후기 쓰기」 — 목록 **위**에 둔다. 열 개를 다 내려간 뒤에야 나오면 아무도 안 누른다. */
.rvnew {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%;
  margin: 12px 0 2px;
  padding: 10px 0;
  border: 1px solid var(--panel-keyline);
  border-radius: 10px;
  font-size: 12.5px; font-weight: 700;
  color: var(--panel-key);
  background: var(--panel-keysoft);
}

.rvrow { padding: 12px 0; border-bottom: 1px solid var(--night-line); }
.rvlist .rvrow:last-child { border-bottom: 0; padding-bottom: 0; }
.rvhead { display: flex; align-items: center; gap: 7px; }
.rvhead > b { font-size: 12px; font-weight: 700; color: var(--panel-ink); }
/* 날짜는 **오른쪽 끝**이다. 이름 옆에 붙이면 이름 길이에 따라 자리가 흔들린다. */
.rvhead > span { margin-left: auto; font-size: 10.5px; color: var(--night-sub); }
.rvtext {
  margin: 7px 0 0;
  font-size: 12.5px; line-height: 1.65;
  color: var(--night-body);
  overflow-wrap: anywhere;
}

/* 사진 열 장 — **가로로 민다.** 격자로 깔면 후기 하나가 화면 두 판을 먹는다.
   장수 표는 스크롤러 **밖**에 얹는다 — 안에 두면 사진과 같이 밀려 나간다. */
.rvphotos { position: relative; margin-top: 8px; }
.rvshots {
  display: flex; gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.rvshots::-webkit-scrollbar { display: none; }
/* ⚠ `padding: 0` 을 지우지 않는다. 이 칸은 `<button>` 이고, 맨 위 `button` 초기화는
   테두리만 지우지 padding 은 안 지운다 — 브라우저 기본 `1px 6px` 가 남아 사진이
   76 네모 안에서 **62×72 로 쪼그라들어** 좌우에 칸 배경색이 띠로 보였다.
   테두리도 `border` 가 아니라 **속그림자**다. border 는 border-box 에서 1px 을
   먹어 또 그만큼 사진이 줄어든다 — 선은 사진 가장자리 **위에** 얹는다. */
.rvshot {
  flex: none;
  width: 76px; height: 76px;
  padding: 0;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--night-line);
  background: var(--night2);
  cursor: zoom-in;
}
.rvshot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rvcnt {
  position: absolute; right: 5px; bottom: 5px;
  pointer-events: none;
  font-size: 10px; font-weight: 700; color: #fff;
  background: rgba(0, 0, 0, 0.72);
  padding: 2px 6px; border-radius: 6px;
}

/* — 후기 쓰기 창 — 홍보영상 재생기(`.ytv`, z 60)의 **판과 층을 그대로 쓴다.**
 *   둘이 같이 뜨는 길은 없다(카드가 하나뿐이다). */
/* 판은 **흰색**이다(2026-09-07 지시). 뼈대를 빌려 온 `.ytv-box` 가 `#0a0a0b` 을
   박아 두므로 여기서 되돌린다 — 재생기는 영상을 얹는 자리라 검은 채로 둔다. */
.rvw-box { display: flex; flex-direction: column; max-height: min(86vh, 720px); background: var(--night); }
.rvw-head {
  flex: none;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--night-line);
}
.rvw-head b { flex: 1; min-width: 0; font-size: 13px; color: var(--panel-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rvw-head button { flex: none; width: 30px; height: 30px; border-radius: 50%; font-size: 15px; color: var(--night-sub); background: var(--night2); }
/* 속만 구른다 — 창 전체를 구르게 두면 「올리기」 단추가 화면 밖으로 나간다. */
.rvw-body { flex: 1; min-height: 0; overflow-y: auto; padding: 14px; }
.rvw-q { margin: 0; font-size: 12.5px; color: var(--night-body); }

/* 별 고르기. **44px 은 지킨다** — 다섯 개가 나란히 선 자리라 하나가 작으면
   옆 별이 눌린다(누르는 자리는 보이는 크기보다 커야 한다). */
.rvw-pick { display: flex; align-items: center; gap: 2px; margin: 6px 0 12px; }
.rvw-pick button {
  width: 44px; height: 44px;
  font-size: 26px; line-height: 1;
  color: var(--night-line);
}
.rvw-pick button.on { color: var(--panel-mark); }
.rvw-pick span { margin-left: 8px; font-size: 12px; color: var(--panel-key); }

.rvw-body textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid var(--night-line);
  border-radius: 10px;
  background: var(--night2);
  color: var(--panel-ink);
  font: inherit; font-size: 12.5px; line-height: 1.6;
  resize: none;
}
.rvw-body textarea::placeholder { color: var(--night-sub); }
/* 왼쪽에 「몇 자 이상」 · 오른쪽에 글자수. 안내가 없는 동안에도 **글자수는 오른쪽에
   그대로 선다** — `justify-content` 가 아니라 글자수 쪽에 `margin-left:auto` 를 준다. */
.rvw-cnt { display: flex; align-items: baseline; gap: 8px; margin: 5px 0 0; text-align: right; font-size: 10.5px; color: var(--night-sub); font-variant-numeric: tabular-nums; }
.rvw-cnt > span:last-child { margin-left: auto; }
.rvw-min { color: var(--panel-key); font-weight: 700; }

.rvw-phhead {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 14px;
  font-size: 12px; color: var(--night-body);
}
.rvw-phhead b { color: var(--panel-key); font-variant-numeric: tabular-nums; }
.rvw-phhead button {
  padding: 6px 12px;
  border: 1px solid var(--panel-keyline);
  border-radius: 999px;
  font-size: 11.5px; font-weight: 700; color: var(--panel-key);
}
/* 다 채우면 **눌러도 아무 일이 없는 단추**가 아니라 꺼진 단추로 보여야 한다. */
.rvw-phhead button:disabled { color: var(--night-sub); border-color: var(--night-line); }
/* 열 장이라 **넉 줄까지** 간다. 창 속이 구르므로 아래 글과 단추는 안 밀린다. */
.rvw-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin-top: 8px; }
.rvw-th { position: relative; aspect-ratio: 1; border-radius: 8px; overflow: hidden; background: var(--night2); }
.rvw-th img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rvw-th button {
  position: absolute; right: 2px; top: 2px;
  width: 20px; height: 20px; border-radius: 50%;
  font-size: 11px; color: #fff; background: rgba(0, 0, 0, 0.7);
}
.rvw-note { margin: 14px 0 0; font-size: 11px; line-height: 1.65; color: var(--night-sub); }
/* 「지금은 미리보기」 줄. **더 흐리게 두지 않는다** — 한 번 `--night-line` 으로 깔았더니
   검은 판 위에서 사실상 안 보였다. 스스로 한 약속을 못 읽을 만큼 흐리게 적는 것은
   안 적은 것과 같다. 위의 안내와는 색이 아니라 **줄 하나로** 가른다. */
.rvw-dim { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--night-line); }
.rvw-foot { flex: none; padding: 12px 14px; border-top: 1px solid var(--night-line); }
.rvw-foot .btn:disabled { opacity: 0.4; }

/* — 하단 카테고리 — 그대로 유지한다(오늘·예약·채팅·지갑·MY) — */

.tabs {
  flex: none;
  height: calc(var(--tabbar) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: #fff;
  border-top: 1px solid var(--line);
  display: flex;
  z-index: 7;
}
.tabs button {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  font-size: 10px;
  color: var(--dim-ink);
}
.tabs button svg { width: 22px; height: 22px; }
.tabs button[aria-selected="true"] { color: var(--ink); font-weight: 700; }

/* — 다른 탭 — */

/* 롤링바 밑에서 시작한다 — 안 그러면 굴릴 때 글이 반투명 띠 뒤로 비쳐 지나간다. */
.page { position: absolute; inset: var(--roll) 0 0; background: var(--bg); overflow-y: auto; z-index: 8; }
/* 오른쪽 76px 은 레일 자리다. 안 비우면 글이 기둥 밑으로 들어간다. */
.page .inner { padding: 46px 76px 16px 16px; max-width: 720px; margin: 0 auto; }
.page h2 { margin: 6px 0 12px; font-size: 20px; letter-spacing: -0.5px; }
/* 팝업 뒤를 덮는 판은 **기본이 꺼짐**이다. 켜는 자리는 900px 위 한 곳뿐이라
   (`body.pgmodal .page-scrim`), 폰에서는 표가 붙어 있어도 아무 일도 안 일어난다. */
.page-scrim { display: none; }

/* ── 마지막에 누른 것이 앞에 온다 ── (2026-09-07 지시)
 *
 * PC 에서 `.page`(예약·채팅·제휴문의…)와 매장 상세 카드(`.panel`)는 **같은 왼쪽
 * 기둥**을 쓴다. 그래서 **둘 중 하나를 층으로 못 박으면 다른 쪽이 영영 안 보인다.**
 * 두 꼴을 다 겪었다:
 *   창이 늘 위(카드 6·창 8) — 창을 열어 둔 채 캐릭터를 누르면 카드는 실제로 열리는데
 *     (`pickVenue` 가 `panel.hidden=false` 를 한다) 창 밑에 깔린다.
 *   카드가 늘 위(`:has` 로 12 로 올렸던 첫 고침) — 카드를 열어 둔 채 레일의 「채팅」을
 *     눌러도 채팅이 카드 밑에 깔린다. **한쪽을 고치면 반대쪽이 똑같이 고장 난다.**
 *
 * 그래서 층을 상황이 아니라 **켠 순서**로 정한다. 카드를 여는 손이 `.cardfront` 를
 * 켜고, 창을 여는 손과 카드를 닫는 손이 끈다(→ `app.js` 의 `frontCard`).
 * 어느 쪽도 상대를 지우지 않는다 — 뒤에 그대로 남아서 앞엣것을 닫으면 도로 나온다.
 * 그래야 제휴문의처럼 **적던 폼이 안 날아간다**(`showTab()` 은 `innerHTML` 을 갈아치운다).
 *
 * 12 는 폰에서 전체화면이 되는 예약·채팅(`body.pgfull .page` 의 11)까지 넘는 값이다.
 *
 * ✕ 가 둘이 되면 안 된다. 창의 ✕(`#pageClose`)는 PC 에서 카드의 ✕ 와 **거의 같은
 * 자리**라, 둘 다 서면 위에 있는 쪽이 아래를 가려 「보이는데 안 눌린다」가 된다
 * (실제로 `#pageClose` 가 카드의 ✕ 를 먹는 것을 `page-pick-audit` 이 잡았다).
 * **카드가 앞일 때만** 창의 ✕ 를 걷는다 — 창이 앞이면 카드는 그 밑이라 안 겹친다. */
.stage.cardfront .panel:not([hidden]) { z-index: 12; }
.stage.cardfront:has(.panel:not([hidden])) #pageClose { display: none; }

/* **「예약」 제목 줄은 굴려도 그 자리에 남는다**(2026-09-07 지시).
 *
 * 나가는 ✕(`#pageClose`)는 원래 `.page` **밖**에 서 있어(index.html) 굴려도 안
 * 움직인다 — 실제로 흘러 올라가던 것은 제목 하나뿐이라 손대는 것도 이것 하나다.
 *
 * 띠는 **칸의 좌우 여백만큼 넓혀** 만든다(`margin: 0 -16px`). 안 넓히면 글자 폭만큼만
 * 가려서, 밑으로 지나가는 카드가 제목 양옆으로 비쳐 지나간다.
 * `--bg` 로 채운다 — `.page` 바탕과 같은 값이라 안 굴린 첫 화면은 지금과 똑같이 보인다.
 *
 * **위 여백을 `.inner` 에서 이 띠로 옮긴다.** `.inner` 에 남겨 두면 그 여백만큼
 * 제목이 내려앉은 자리에서 붙어 서고, 그 위 12px 틈으로 글이 지나간다.
 * 그래서 `18px = 12(칸 여백) + 6(h2 의 옛 margin-top)` 이다 — **제목 줄 한가운데가
 * 그대로 있어야** ✕ 와 같은 줄이 유지된다(`booking-fullscreen-audit.mjs` 가 잰다).
 * 아래 여백도 margin 이 아니라 padding 으로 든다: margin 이면 그 12px 이 띠 밖이라
 * 굴릴 때 제목 바로 밑으로 글이 비친다.
 *
 * **예약에만 건다.** 같은 칸을 쓰는 제휴문의까지 끌고 가지 않는다. */
body.pgfull .page h2 {
  position: sticky;
  top: 0;
  /* 카드는 z 를 안 든다 — 2 면 그 밑으로 지나가기에 넉넉하다.
     바깥 ✕ 는 `.page`(z 8·11) 밖이라 이 값과 겨루지 않는다. */
  z-index: 2;
  background: var(--bg);
  margin: 0 -16px;
  padding: 18px 16px 12px;
}
.card { background: #fff; border-radius: 18px; padding: 14px; margin-bottom: 10px; }

/* — 탭 화면 조각 — */

/* 현장에서 쓰는 카드만 밤 모드다. 테마 토글이 아니라 **화면 종류**다. */
.card.night { background: var(--night); color: var(--panel-ink); }
.card.night .cond { color: var(--night-body); }
.card.night .cond.dim { color: var(--night-sub); }
.card.soft { background: var(--soft); }

.chip.night { background: var(--night-line); color: var(--night-body); }

.storename { font-size: 17px; font-weight: 700; margin-top: 8px; }
.n.big { font-size: 34px; margin: 6px 0; }
.n.big small { font-size: 16px; letter-spacing: 0; }
.n.huge { font-size: 42px; margin: 4px 0 2px; color: var(--panel-ink); }
.n.huge small { font-size: 18px; letter-spacing: 0; }

/* 체크인 창이 열리기 전에는 누를 게 없다 — 버튼이 아니라 안내 띠다. */
.strip {
  margin-top: 14px;
  padding: 10px;
  border-radius: 12px;
  background: var(--night-line);
  color: var(--night-body);
  text-align: center;
  font-size: 11.5px;
}

.acts {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 12px;
  font-size: 11.5px;
  color: var(--night-sub);
  flex-wrap: wrap;
}

.av {
  width: 34px; height: 34px;
  border-radius: 12px;
  background: linear-gradient(135deg, #ffb6c1, #ffd966);
  flex: none;
}

.qrbox {
  width: 150px; height: 150px;
  margin: 10px auto;
  border-radius: 14px;
  border: 10px solid #fff;
  background:
    repeating-linear-gradient(90deg, #111 0 8px, transparent 8px 16px),
    repeating-linear-gradient(0deg, #111 0 8px, #fff 8px 16px);
}
.card.qr { text-align: center; }

/* — PC — 지도 옆에 목록이 선다 — */

@media (min-width: 900px) {
  /* PC 에서도 지도가 화면을 다 쓴다. 고른 매장 카드는 지도 **위에** 뜬다 —
     옆에 붙여 세우면 아무것도 안 골랐을 때도 지도가 그만큼 좁아진다. */
  /* 카드는 **왼쪽 끝**에 붙는다(2026-09-05 지시). 오른쪽에 세웠더니
     지도 확대 단추와 같은 편이라 서로를 가렸다. */
  /* 카드는 **왼쪽에 위아래로 꽉 붙는다**(2026-09-05 지시).
     띄워 두면 지도가 카드 둘레로 비집고 보여 어디까지가 카드인지 흐려진다. */
  .panel {
    left: 0;
    right: auto;
    /* 롤링바 밑에서 시작한다 — 카드를 굴리면 사진이 띠 뒤로 지나가 겹쳐 보였다. */
    top: var(--roll);
    bottom: 0;
    width: var(--sidew);
    max-height: none;
    border-radius: 0;
    box-shadow: 8px 0 28px rgba(0, 0, 0, 0.18);
  }
  .panel[hidden] { transform: translateX(-120%); }
  .panel .handle { display: none; }
  .panel .body { padding: 46px 16px 16px; }

  /* 예약·채팅도 **고른 매장 카드와 같은 자리에** 뜬다(2026-09-05 지시).
     전에는 지도를 통째로 덮는 한 장이라, 레일에서 예약을 누르면 보던 지도가
     사라졌다. 왼쪽 기둥으로 세우면 지도는 그대로 남고 창만 옆에 선다. */
  .page {
    inset: var(--roll) auto 0 0;
    width: var(--sidew);
    /* 바탕은 흰색(--card)이 아니라 회색(--bg) 그대로 둔다. 안에 쌓이는
       `.card` 가 흰 판이라, 창까지 희게 하면 카드 경계가 통째로 사라진다. */
    box-shadow: 8px 0 28px rgba(0, 0, 0, 0.18);
  }
  /* 오른쪽 76px 은 레일을 피하려고 비웠던 자리다. 창이 왼쪽 기둥이 되어
     레일과 겹치지 않으니 도로 채운다 — 안 그러면 좁은 창에서 글이 더 눌린다. */
  .page .inner { padding: 46px 16px 16px; max-width: none; margin: 0; }
  /* 닫기 ✕ 는 **창 안 오른쪽 위**로 간다. 창이 좁아졌는데 화면 왼쪽 끝에
     그대로 두면 창 제목과 겹쳐 앉는다. 카드의 ✕(.panel .close)와 같은 자리다.
     **id 로 부른다** — 기본 `.page-close` 규칙이 이 미디어 블록보다 아래에 있어서
     같은 무게(클래스)로 쓰면 나중에 나온 `left: 12px` 가 이긴다. */
  #pageClose { left: calc(var(--sidew) - 46px); top: calc(var(--roll) + 12px); }
  /* **예약은 ✕ 가 「예약」 글자와 같은 줄이다**(2026-09-07 지시). 폰만 그렇게 고쳤더니
     PC 에서는 ✕ 가 제목보다 34px 위 제 한 줄에 남아 있었다 — 같은 지시가 두 자리에
     걸린 것을 놓쳤다. 옮기는 것은 **위 여백 하나뿐**이다: 46px 이면 제목이 ✕ 밑으로
     내려가고, 12px 이면 제목 줄 한가운데가 `--roll + 32px` 이 되어 ✕ 상자(높이 40,
     top `--roll + 12`)의 한가운데와 딱 맞는다. **두 값은 한 짝이라 하나만 못 고친다.**

     `body.pgfull` 로만 건다 — 예약 화면에만 내린 지시다. `.page .inner` 를 통째로
     고치면 같은 칸을 쓰는 다른 화면(제휴문의)까지 제 뜻과 상관없이 끌려간다.

     그 12px 은 이제 **제목 띠가 든다**(`body.pgfull .page h2` 의 `padding-top: 18px`
     = 12 + h2 의 옛 margin-top 6). 여기 남겨 두면 굴릴 때 제목 위 12px 틈으로
     글이 지나간다 — 그래서 여기는 0 이다. 제목 줄 한가운데 값은 그대로다. */
  body.pgfull .page .inner { padding-top: 0; }

  /* ── 제휴문의는 **PC 에서만** 가운데 팝업이다 ── (2026-09-08 지시)
   *
   * 다른 `#page` 화면(예약·채팅)은 그대로 왼쪽 380px 기둥에 선다. 이 화면만 다른
   * 이유가 있다: **여기는 지도를 보면서 쓰는 화면이 아니라 글을 적는 폼**이라,
   * 316px 기둥에 세우면 담당자·연락처가 한 줄에 하나씩 내려가고 「010-0000-0000」
   * 자리표가 잘렸다(→ `.pf-row` 의 `@container`). 가운데 팝업이면 그 칸이 496px 이
   * 되어 두 칸이 한 줄에 선다.
   *
   * 켜는 표는 `body.pgmodal` 하나고 켜고 끄는 곳은 `app.js` 의 `showTab()` 이다
   * (`pgfull` 과 **같이** 켜진다 — 폰 전체화면 규칙은 899px 아래에만 있어서 안 겹친다).
   *
   * **폭·높이를 `body` 에 적는다.** 팝업의 ✕(`#pageClose`)는 `.page` 밖 형제라
   * (index.html: 다시 그릴 때 같이 지워지면 안 된다) 같은 값을 봐야 팝업 오른쪽 위에
   * 붙는다. 그래서 높이는 내용에 따라 늘어나는 값이 아니라 **정해진 값**이다 —
   * `max-height` 로 두면 폼이 짧아진 「접수 완료」 화면에서 ✕ 만 허공에 남는다. */
  body.pgmodal {
    --pgw: min(560px, calc(100vw - 48px));
    --pgh: min(760px, calc(100vh - 80px));
  }
  /* 뒤를 덮는 판. **팝업이면 지도를 못 만진다** — 그래야 창을 열어 둔 채 캐릭터를
     눌러 카드가 팝업 뒤에 깔리는 일이 없다(그 층 다툼은 `.stage.cardfront` 몫이다). */
  body.pgmodal .page-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 29;
    background: rgba(17, 18, 22, 0.42);
  }
  /* **레일은 덮개 위에 남는다.** 하단 바가 없어서 이 화면의 길잡이는 오른쪽 기둥
     하나뿐이다 — 같이 덮으면 팝업을 연 순간 예약·채팅·탐색·찜이 통째로 닫히고,
     「같은 타일을 다시 누르면 지도로 돌아온다」는 앱 전체의 규칙도 제휴문의에서만
     깨진다(그 규칙은 `partner-audit` 이 잰다 — 실제로 여기서 걸려 잡혔다).
     덮개가 진짜로 막아야 하는 것은 **지도**다: 캐릭터를 눌러 카드가 팝업 뒤에
     열리는 것을 막는 것이 이 판의 몫이고, 그건 레일을 올려도 그대로 산다. */
  body.pgmodal .rail { z-index: 31; }
  body.pgmodal .page {
    position: fixed;
    inset: auto;
    left: calc(50% - var(--pgw) / 2);
    top: calc(50% - var(--pgh) / 2);
    width: var(--pgw);
    height: var(--pgh);
    border-radius: 20px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
    /* 모서리를 둥글렸으니 속도 같이 잘라야 한다 — 안 자르면 굴릴 때 제목 띠가
       둥근 모서리 밖으로 네모나게 삐져 나온다. */
    overflow: hidden auto;
    z-index: 30;
  }
  /* 제목 띠(`body.pgfull .page h2`)는 좌우 여백만큼 넓혀 칠하는데, 그 값이 팝업
     모서리와 같은 자리라 위 두 모서리를 네모로 덮는다. 띠에도 같은 라운드를 준다. */
  body.pgmodal .page h2 { border-radius: 20px 20px 0 0; }
  /* ✕ 는 팝업 **안 오른쪽 위**다. 기본 PC 규칙(`left: calc(var(--sidew) - 46px)`)은
     기둥에 세울 때 값이라 여기서 다시 적는다 — 위 `--pgw`·`--pgh` 와 한 짝이다. */
  body.pgmodal #pageClose {
    position: fixed;
    left: calc(50% + var(--pgw) / 2 - 46px);
    top: calc(50% - var(--pgh) / 2 + 12px);
    z-index: 31;
  }
  /* 팝업이 떠 있으면 카드는 그 뒤 덮개 밑이라 ✕ 가 겹칠 상대가 없다.
     기본 규칙(`.stage.cardfront:has(.panel:not([hidden])) #pageClose`)이 걷어 버리면
     팝업에서 나갈 길이 사라진다 — 무게를 한 칸 더 얹어 되살린다. */
  body.pgmodal .stage.cardfront:has(.panel:not([hidden])) #pageClose { display: block; }

  .tabs {
    height: 64px;
    justify-content: center;
    gap: 8px;
  }
  .tabs button { flex: none; width: 96px; }
  .brand .max { font-size: 24px; }
}

/* ── 상단바 · 롤링바 (2026-09-05 지시: 국모와 같은 구조) ──
 *
 * **지도를 덮지 않는다.** 지도 위에 띄우면 그 밑에 깔린 클럽 이름표가
 * 보이면서 안 눌린다 — 이 화면에서 여러 번 겪은 결함이다. 그래서 자리를 차지한다. */
.topbar {
  flex: none;
  height: 54px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  background: #fff;
  border-bottom: 1px solid var(--line);
  position: relative;
  z-index: 20;
}
/* 상단바 왼쪽 로고. 2026-09-06 에 글자(`<b>24K</b>`)에서 그림으로 바꿨다.
 *
 * `display:block` 이 **꼭 있어야 한다.** 인라인으로 두면 그림 밑에 글꼴의 baseline
 * 여백이 3~4px 붙어, 여백 0으로 자른 그림인데도 상단바에서 아래로 밀려 보인다.
 *
 * 높이 20px 는 앞서 서 있던 24px/800 글자의 숫자 높이와 같은 자리다 — 상단바 높이
 * (54px)나 옆 검색칸을 건드리지 않는다. **크기를 바꾸려면 이 한 값만 고친다**;
 * 폭은 `auto` 라 원본 비율(266:128)이 알아서 따라온다. */
.topbar .logo { display: flex; align-items: center; flex: none; text-decoration: none; }
/* 머니업 글자 로고(2026-09-13). 옛 24K 글자 로고와 같은 굵기·색이고, 한글 세 자라
   `white-space:nowrap` 으로 폰 폭에서도 줄이 안 갈린다. */
.topbar .logo .logo-text {
  display: block;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -1px;
  line-height: 1;
  color: #F5C542;
  white-space: nowrap;
}
.topbar .logo img {
  display: block;
  height: 20px;
  width: auto;
}
.topbar .search {
  flex: 1;
  max-width: 460px;
  margin: 0 auto;
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--soft);
  border: 1px solid var(--line);
  /* 알약(22px)이 아니라 **모서리만 둥근 네모**다. 상단바 셋(검색·계정·로그인)이
     같은 12px 를 쓴다 — 하나만 알약으로 남으면 그 자리가 따로 논다. */
  border-radius: 12px;
  padding: 0 14px;
  height: 38px;
}
.topbar .search svg { width: 17px; height: 17px; color: var(--sub); flex: none; }
.topbar .search input {
  flex: 1;
  border: 0;
  background: none;
  font: inherit;
  font-size: 13.5px;
  outline: none;
  min-width: 0;
}
.topbar .me-btn {
  width: 38px; height: 38px;
  border-radius: 12px;
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  flex: none;
}
.topbar .me-btn svg { width: 19px; height: 19px; }

/* 검색 결과 — 상단바 밑으로 떨어뜨린다. */
.hits {
  position: absolute;
  left: 0; right: 0; top: 42px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.16);
  max-height: 300px;
  overflow-y: auto;
  padding: 4px;
  z-index: 21;
}
.hits button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 13px;
}
/* 짚은 줄 — 위/아래 키로 옮긴 줄(`.on`)과 마우스로 얹은 줄이 **같은 색이어야** 한다.
   다르게 두면 키로 내려가는 도중 마우스가 목록을 스칠 때 두 줄이 켜진 것처럼 보인다
   (그래서 app.js 가 마우스가 얹힌 줄로 `.on` 을 옮겨 준다).
   **`--soft` 로는 안 된다** — 흰 상자 위 `#f7f7f8` 은 키로 내려가는 줄을 눈으로 못 쫓는다.
   연노랑 바탕 하나로만 표시한다. 왼쪽에 금색 띠를 세워 봤는데 **지저분해서 걷었다**
   (2026-09-05 지시). */
.hits button:hover, .hits button:focus, .hits button.on {
  background: var(--warn-bg);
}
/* 왼쪽 정사각 대표 사진 — 목록(`vrow`)과 같은 그림·같은 모서리다.
   **`flex: none` 이 없으면 이름이 긴 줄에서 사진이 눌려 찌그러진다.** */
.hits .hthumb {
  width: 38px; height: 38px; flex: none;
  border-radius: 8px; object-fit: cover;
  background: var(--soft); border: 1px solid var(--line);
}
/* 글 묶음은 남는 폭을 다 쓰되 **`min-width: 0`** 을 준다 —
   안 주면 긴 주소가 줄을 밀어내 상자 밖으로 흘러나간다. */
.hits button > div { flex: 1; min-width: 0; }
.hits button b { display: block; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hits button span { display: block; font-size: 11px; color: var(--sub); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hits .none { padding: 10px; font-size: 12.5px; color: var(--sub); }

/* 롤링바 — 한 줄이 왼쪽으로 흐른다. */
.roll {
  flex: none;
  /* 26px — 한 줄 글자 높이에 딱 맞춘다. 더 줄이면 글자가 잘린다. */
  height: 26px;
  /* **지도 위에 얹는다.** 자리를 차지하면 뒤에 아무것도 없어서 반투명이 뜻이 없다.
     흐림은 걷었다(2026-09-05 지시) — 뒤가 그대로 보여야 한다.
     대신 pointer-events 를 끈다. 안 끄면 이 띠가 그 밑 아이콘의 클릭을 먹는다. */
  /* **position 은 여기 한 번만 적는다.** 아래에 relative 가 남아 있어서
     absolute 를 덮었고, 띠가 54px 밀려 내려가면서 원래 자리는 회색 빈칸으로
     남았다 — 「회색 창」의 정체가 그것이었다. */
  position: absolute;
  top: 54px; left: 0; right: 0;
  pointer-events: none;
  /* **불투명 검정**(2026-09-07 지시). 반투명(검정 50%)이던 자리다 — 뒤 지도가
     비쳐 굵은 흰 획이 밝은 타일 위에서 뭉개 보였다. 뒤가 하나도 안 보여야 한다.
     `rgba(…, 0.5)`·`opacity` 로 되돌리지 않는다. */
  background: #000;
  color: #fff;
  overflow: hidden;
  z-index: 19;
}
/* 거리뷰가 열리면 걷는다. 나중에 `.roll` 에 display 가 붙어도 안 새도록 여기서 못을 박는다. */
.roll[hidden] { display: none !important; }
.roll .track {
  display: flex;
  align-items: center;
  gap: 28px;
  height: 100%;
  white-space: nowrap;
  width: max-content;
  /* 46s → 66s → 82s → 71s. 두 번 느리게 했다가 15% 도로 빠르게 했다(2026-09-05 지시).
     시간을 줄이는 것이 빨라지는 것이다 — 82 ÷ 1.15 = 71.3 이라 71s. */
  animation: roll 71s linear infinite;
}
/* 겨눌 수 있어야 누른다. 흘러가는 글자는 못 누르니 손이 얹히면 멈춰 세운다.
   `:focus-within` 도 같이 — 탭으로 짚어 둔 줄이 손 밑에서 흘러가면 안 된다. */
.roll .track:hover,
.roll .track:focus-within { animation-play-state: paused; }
/* 띠는 통째로는 클릭을 안 먹지만(`.roll` 의 `pointer-events: none`) **줄 하나하나는 먹는다** —
   누르면 그 매장 상세 카드가 열린다. 줄 사이 빈칸은 그대로 통과해서 밑의 지도·표식이 눌린다.
   **`#roll` 로 잡는다** — 관리자 미리보기(`.rollprev`)가 같은 `.it` 규칙을 물려받는데,
   거기서는 눌러도 갈 곳이 없어 손가락 커서만 뜨면 고장으로 읽힌다. */
#roll .it[data-roll-club] { pointer-events: auto; cursor: pointer; border-radius: 6px; }
#roll .it[data-roll-club]:focus-visible { outline: 2px solid var(--y); outline-offset: 2px; }
/* 11px → 13px, 굵게(2026-09-05 지시). 띠가 26px 이라 13px 한 줄(약 19px)이 그대로 들어간다 —
   더 키우면 글자가 잘리니 높이(`--roll`)부터 올려야 한다. */
.roll .it { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; }
.roll .it i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--y);
  flex: none;
}
.roll .it b { color: var(--y); font-weight: 800; }
/* 「오늘 최고」 — **전체 캠페인 중 최고 포인트**에만 붙는다. 골드 바탕에 검정 글씨라
   흰 글씨 줄 사이에서 혼자 튄다. 그게 이 띠가 하는 말의 전부다. */
.roll .it em {
  font-style: normal;
  font-weight: 800;
  font-size: 11px;
  letter-spacing: -0.3px;
  background: var(--y);
  color: #111;
  border-radius: 5px;
  padding: 1px 6px;
  flex: none;
}
@keyframes roll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
/* 움직임을 줄여 달라고 한 사람에겐 멈춰 세운다. */
@media (prefers-reduced-motion: reduce) {
  .roll .track { animation: none; }
  .roll { overflow-x: auto; }
}

/* ── 가입·본인인증 창 ── */
.signup {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.signup .sheet {
  background: var(--card);
  width: 100%;
  max-width: 420px;
  border-radius: 20px 20px 0 0;
  padding: 22px 18px calc(22px + var(--safe-b));
  position: relative;
  max-height: 92vh;
  overflow-y: auto;
}
.signup .x {
  position: absolute;
  right: 10px; top: 10px;
  font-size: 17px;
  color: var(--sub);
  padding: 6px 10px;
}
.signup h2 { font-size: 18px; margin: 0 0 6px; letter-spacing: -0.5px; }

/* ── 동의 ──
   항목별로 나눠 받고 필수·선택을 갈라 보여 준다(개인정보보호법 §22).
   **누르는 자리를 44px 로 잡는다** — 체크박스만 작게 두면 폰에서 자꾸 빗나간다. */
.signup .agree { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 6px; }
.signup .agr {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; cursor: pointer; font-size: 13px; line-height: 1.5; color: var(--sub);
}
.signup .agr input { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--y); }
.signup .agr.all { border-bottom: 1px solid var(--line); font-size: 14px; color: var(--ink); }
.signup .agr a { color: var(--sub); text-decoration: underline; }
.signup .agdet { margin: 4px 0 2px; }
.signup .agnote { margin: 8px 0 0; line-height: 1.75; }
.signup .agdet summary {
  cursor: pointer; font-size: 12px; color: var(--sub); padding: 8px 0; min-height: 34px;
}
.signup .agtbl { width: 100%; border-collapse: collapse; font-size: 12px; margin-top: 4px; }
.signup .agtbl th, .signup .agtbl td {
  border: 1px solid var(--line); padding: 7px 9px; text-align: left; vertical-align: top;
  line-height: 1.6; color: var(--sub);
}
.signup .agtbl th { white-space: nowrap; color: var(--ink); background: rgba(255,255,255,.03); }
/* 로그인 칸. 안내 글과 갈라 보이게 줄 하나를 긋는다.
   **동의는 이 아래가 아니라 다음 화면이다** — 회원은 그 화면을 아예 안 본다. */
.signup .gsec { margin-top: 18px; border-top: 1px solid var(--line); padding-top: 16px; }
.signup .gsec #errG:empty { display: none; }

.signup .fld { display: block; margin-top: 12px; }
.signup .fld span { display: block; font-size: 12px; color: var(--sub); margin-bottom: 5px; }
.signup .fld input {
  width: 100%;
  height: 46px;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 0 14px;
  font: inherit;
  font-size: 15px;
  outline: none;
}
.signup .fld input:focus { border-color: var(--y); }
.signup .btn { margin-top: 12px; }
.btn.google {
  background: #fff;
  border: 1.5px solid var(--line);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.btn.google span {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #4285f4;
  color: #fff;
  font-weight: 800;
  display: grid;
  place-items: center;
  font-size: 13px;
}

/* 몇 단계 중 몇 번째인가. 다섯 관문이라 이게 없으면 「끝이 있긴 한가」가 된다. */
/* **`display:flex` 는 `hidden` 을 이긴다.** 이 줄이 없으면 진행 표시가 없어야 할
   자리(로그인·환영·끝 화면)에서도 띠가 그대로 남는다 — 방금 로그인한 회원 머리 위에
   「1 / 5 단계」가 붙으면 가입을 다시 하는 줄 안다. */
.signup .prog[hidden] { display: none; }
.signup .prog {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0 14px;
  /* 닫기 ✕ 가 이 줄의 오른쪽 끝에 떠 있다. 자리를 비워 두지 않으면
     「3 / 5 단계」의 뒷글자가 ✕ 밑에 깔린다 — 실제로 그렇게 찍혔다. */
  padding-right: 34px;
}
.signup .prog .bar {
  flex: 1;
  height: 4px;
  border-radius: 4px;
  background: var(--line);
  overflow: hidden;
}
.signup .prog .bar i {
  display: block;
  height: 100%;
  background: var(--y);
  transition: width 0.2s ease;
}
.signup .prog span { font-size: 11.5px; color: var(--sub); flex: none; }
.signup .keep { line-height: 1.6; color: var(--sub); }

/* 서류·사진 고르는 칸. 고르면 왼쪽 아이콘 자리가 미리보기로 바뀐다. */
.signup .pick {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding: 12px;
  border: 1.5px dashed var(--line);
  border-radius: 14px;
  cursor: pointer;
}
.signup .pick.on { border-style: solid; border-color: var(--y); }
.signup .pick .pv {
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 10px;
  background: var(--bg);
  display: grid;
  place-items: center;
  color: var(--sub);
  overflow: hidden;
}
.signup .pick .pv svg { width: 24px; height: 24px; }
.signup .pick .pv img { width: 100%; height: 100%; object-fit: cover; }
.signup .pick .pt { display: block; min-width: 0; }
.signup .pick .pt b { display: block; font-size: 14px; }
/* 파일 이름이 길다. 줄바꿈 없이 잘라야 칸 높이가 안 튄다. */
.signup .pick .pt span {
  display: block;
  font-size: 11.5px;
  color: var(--sub);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.signup .btn:disabled { opacity: 0.45; cursor: default; }

@media (min-width: 900px) {
  .signup { align-items: center; }
  .signup .sheet { border-radius: 20px; }
}

/* ── 계정 메뉴 ── */
.topbar .acct { position: relative; flex: none; }
.usermenu {
  position: absolute;
  right: 0; top: 46px;
  z-index: 30;
  width: 250px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
  padding: 6px;
  overflow: hidden;
}
.usermenu .who { padding: 12px 12px 10px; border-bottom: 1px solid var(--line); margin-bottom: 6px; }
.usermenu .who b { display: block; font-size: 14.5px; letter-spacing: -0.4px; }
.usermenu .who span { display: block; font-size: 12px; color: var(--sub); margin-top: 2px; word-break: break-all; }
/* 단추와 링크를 같은 규칙으로 묶는다. 관리자 입구는 **진짜 주소로 가는 것**이라
   `<a>` 라야 새 탭으로 열고 주소를 복사할 수 있다 — 단추로 만들면 둘 다 막힌다. */
.usermenu button, .usermenu a {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 11px 12px;
  border-radius: 10px;
  font-size: 14px;
  text-align: left;
  color: inherit;
  text-decoration: none;
  box-sizing: border-box;
}
.usermenu button svg, .usermenu a svg { width: 19px; height: 19px; color: var(--sub); flex: none; }
.usermenu button:hover, .usermenu button:focus,
.usermenu a:hover, .usermenu a:focus { background: var(--soft); }
.usermenu .sep { height: 1px; background: var(--line); margin: 6px 6px; }
/* `display:flex` 는 브라우저가 [hidden] 에 주는 `display:none` 을 이긴다.
   이 한 줄이 없으면 `el.hidden = true` 가 **조용히 아무 일도 안 한다** —
   관리자 입구를 회원에게서 감추는 일이 여기 걸렸다. */
.usermenu button[hidden], .usermenu a[hidden], .usermenu .sep[hidden] { display: none; }

/* 업체 정보 묶음(.venueinfo)은 상세에서 통째로 뺐다(2026-09-07 지시) — 규칙도 같이 지웠다. */

/* ── 매장정보(`.vinfo`) ──
 *
 * 탭 띠의 첫 칸(2026-09-07 지시). 판은 **흰색**이다 — 같은 날 저녁 지시로
 * 검은 판을 걷었다. 색은 `--night-*`·`--panel-*` 을 타고 위 「흰 판」 규칙에서 온다.
 * 가격표·테이블맵만 검은 채로 남는다(그림을 넣는 자리라 지시받은 예외다).
 *
 * 한 칸은 「금색 머리글 → 굵은 값 한 줄 → 설명」이다. 굵은 값 줄(`.vi-big`)이 있는
 * 칸과 「아직 등록 전」(`.vi-none`)인 칸이 **같은 높이로 읽혀야** 어디가 비었는지
 * 한눈에 보인다. */
.vinfo {
  margin-top: 12px;
  background: var(--night);
  border-radius: 14px;
  padding: 2px 14px 14px;
  color: var(--panel-ink);
}
.vi { padding: 12px 0; border-bottom: 1px solid var(--night-line); }
.vi:last-of-type { border-bottom: 0; }
.vi h4 {
  margin: 0 0 6px;
  font-size: 11.5px; font-weight: 700; letter-spacing: 1px;
  color: var(--panel-key);
}
.vi p { margin: 4px 0 0; font-size: 12.5px; line-height: 1.65; color: var(--night-body); }
.vi p.dim { color: var(--night-sub); font-size: 11.5px; }
.vi p.dim b { color: var(--night-body); font-weight: 700; }
.vi-big { display: block; font-size: 14px; line-height: 1.5; color: var(--panel-ink); }
.vi-big a { color: var(--panel-ink); text-decoration: underline; text-underline-offset: 3px; }
/* 「아직 등록 전」. **회색으로 흐리되 지우지는 않는다** — 빈칸을 안 그리면
   자료가 빠진 것이 화면에서 사라진다. */
.vi-none { margin: 4px 0 0; font-size: 13px; color: var(--night-sub); }
.vi-none .dim { font-size: 11.5px; }
/* 지금 문 열었나. **배지를 옆에 다는 게 아니라 값 줄 자체를 초록으로 쓴다** —
   「오늘은 쉬어요」 옆에 「영업 중」 배지가 나란히 서는 꼴을 안 만들려는 것이다. */
.vi-big.vi-open { color: var(--panel-green); }
/* 값은 있는데 진짜가 아닌 자리(실존 클럽 카드의 입장료 하나뿐이다). */
.vi-ex {
  font-style: normal; font-size: 10px; color: var(--night-sub);
  border: 1px solid var(--night-line); border-radius: 6px;
  padding: 1px 5px; margin-left: 6px; vertical-align: 2px;
}
.vi-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.vi-chips span {
  font-size: 11px; color: var(--night-body);
  background: var(--night2); border: 1px solid var(--night-line);
  border-radius: 999px; padding: 3px 9px;
}
/* 주소 칸의 단추 셋(`.vi-links` — 지도에서 보기·길찾기·카카오맵 장소)은
   걷었다(2026-09-07 지시). 규칙도 같이 지운다 — 남겨 두면 다시 붙었을 때 소리 없이 산다. */

/* ── 주소 복사(2026-09-07 지시) ──
 *
 * 「주소 우측에 복사 아이콘 · 주소나 아이콘을 누르면 복사」. 그래서 **줄 통째가 단추**다 —
 * 아이콘만 누를 수 있게 두면 15px 짜리 과녁을 손가락으로 겨눠야 한다.
 *
 * 단추인데 단추처럼 안 생겼다: 바탕·테두리가 없고 글자는 `.vi-big` 그대로다. 옆 칸들
 * (영업시간·입장료)과 **같은 자리에서 같은 크기로** 읽혀야 하기 때문이다. 누를 수 있다는
 * 표시는 오른쪽 아이콘 하나가 진다.
 *
 * ⚠ `<button>` 의 브라우저 기본값을 다 걷어야 한다 — 안 걷으면 padding 이 남아 주소 줄만
 *   옆 칸보다 안쪽으로 들어가 앉고, `text-align: center` 가 주소를 가운데로 민다. */
.vi-copy {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
/* 아이콘은 **주소 바로 오른쪽**에 붙는다(지시가 그렇다) — 카드 오른쪽 끝으로 밀면
   주소와 멀어져 「이 아이콘이 저 주소의 것」이 안 읽힌다. 주소가 길어 한 줄을 다 쓰면
   그때는 `1 auto` 가 줄여 주므로 아이콘이 밖으로 밀려나지 않는다. */
.vi-copy .vi-big { flex: 0 1 auto; min-width: 0; }
/* 복사가 막힌 폰에서는 이 글자를 골라 놓는다 — 그때 손으로 긁을 수도 있어야 한다.
   (`user-select` 를 안 적으면 단추 안 글자는 긁히지 않는 판이 있다.) */
.vi-copy .vi-big { -webkit-user-select: text; user-select: text; }
/* 아이콘은 첫 줄 높이에 맞춰 세운다 — 주소가 두 줄로 접히면 아래로 따라 내려가면 안 된다.
   `.vi-big` 의 line-height(1.5 × 14px = 21px)와 같은 높이를 준다. */
.vi-cp {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  color: var(--night-sub);
}
.vi-copy:hover .vi-cp { color: var(--panel-ink); }
/* 복사된 순간. 금색은 이 화면에서 「됐다」는 뜻으로 이미 쓰고 있다. */
.vi-copy.done .vi-cp { color: var(--panel-key); }
/* 답하는 줄. **띠가 아니라 그 자리다** — 24K 회원 웹에 화면 아래 띠는 없다. */
.vi-cpsay { margin-top: 4px; }

/* 글 속에서 다른 탭으로 데려가는 단추. 링크처럼 보이게 둔다. */
.vi-go { color: var(--panel-key); text-decoration: underline; font-size: inherit; }
.vi-list { margin: 6px 0 0; padding: 0; list-style: none; }
.vi-list li {
  position: relative; padding-left: 12px; margin-bottom: 6px;
  font-size: 12.5px; line-height: 1.65; color: var(--night-body);
}
.vi-list li::before { content: '·'; position: absolute; left: 2px; color: var(--panel-key); }
.vi-list li b { color: var(--panel-ink); }
.vi-foot {
  margin: 0; padding-top: 12px;
  border-top: 1px solid var(--night-line);
  font-size: 11px; line-height: 1.6; color: var(--night-sub);
}

/* ── 포인트(리워드) 탭도 같은 판 위에 선다(2026-09-07 지시) ──
 *
 * `.kv` 는 카드 본문에 맞춰 놓은 규칙이라 이 판 안에서는 줄 간격·밑줄이 다르다.
 * **`.vinfo` 안에서만** 갈아 끼운다 — `.kv` 는 예약·마이페이지 등 다른 카드에서도 쓴다. */
.vinfo .kv {
  padding: 9px 0;
  border-bottom: 1px solid var(--night-line);
  font-size: 12.5px;
}
.vinfo .kv:last-child { border-bottom: 0; padding-bottom: 0; }
.vinfo .kv span { color: var(--night-sub); }
.vinfo .kv b { color: var(--panel-ink); font-weight: 700; }
/* 머리글 바로 밑 첫 줄은 위 여백을 줄인다 — 안 줄이면 칸 사이가 벌어져 보인다. */
.vi h4 + .kv { padding-top: 2px; }

/* 경고·겹침은 **판이 바뀌어도 색은 그대로 지킨다.** 빨강은 경고·정지·신고에만 쓴다는
   규칙이 여기서도 그대로다 — 판에 맞춘다고 흐리면 그게 곧 경고가 안 읽히는 자리다.
   흰 판에서는 `--panel-red` 가 짙은 빨강(`--danger-ink`)으로 갈린다. */
.vi-warn h4 { color: var(--panel-key); }
.vi-danger h4 { color: var(--panel-red); }
/* `.vinfo .kv b` 가 흰색을 박아 두므로 **같은 무게로 이겨야 한다** —
   `.vi-red` 한 낱말로 적으면 조용히 흰 글씨로 남는다. */
.vi-danger .vi-big, .vinfo .kv b.vi-red { color: var(--panel-red); }
/* 맨 윗줄의 받는 값. 카드 머리의 큰 숫자와 **같은 금색**이라 둘이 한 값으로 읽힌다. */
.vinfo .kv b.vi-pt { color: var(--panel-key); font-size: 14px; }
/* 「신청인원」은 **두 수 다** 리워드와 같은 금색이다(2026-09-07 지시).
   위 `.vinfo .kv b` 가 흰색을 박아 두므로 **`.vi-seats` 를 붙여 이긴다** —
   `.vi-seats` 한 낱말로 적으면 조용히 흰 글씨로 남는다(`.vi-red` 가 그랬다). */
.vinfo .kv b.vi-seats { color: var(--panel-key); }
.vi-danger p { color: var(--night-body); }

/* 가격표 · 테이블맵 — **매장이 아직 안 올린 자리에 서는 예시 표.**
 *
 * 접었다 펴는 상자(`.pricebox`)에 들어 있던 것을 위 탭 띠(`.mtabs`) 밑으로 옮겼다
 * (2026-09-06 지시). 옆의 그림 액자(`.mframe`)가 검은 판이라 이 표도 같은 검은 판에
 * 앉힌다 — 흰 카드 위에 그냥 두면 여기 글자색(`--night-*`)이 바탕에 묻혀 안 읽힌다. */
.mfall {
  margin-top: 12px;
  background: var(--night);
  border-radius: 14px;
  padding: 14px;
  color: #fff;
}
.pg { margin-bottom: 12px; }
.pg > b { display: block; font-size: 11.5px; letter-spacing: 1px; color: var(--y); margin-bottom: 6px; }
.pg-items { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; }
.pg-item { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; }
.pg-item span { color: var(--night-body); }
.pg-item b { font-variant-numeric: tabular-nums; }
.pg-note {
  font-size: 11px;
  color: var(--night-sub);
  border-top: 1px solid var(--night-line);
  padding-top: 8px;
  line-height: 1.6;
}

.tmap {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: 40px repeat(3, 46px);
  gap: 5px;
  margin-bottom: 10px;
}
.tm {
  border-radius: 8px;
  display: grid;
  place-items: center;
  font-size: 11.5px;
  font-weight: 700;
  border: 1px solid var(--night-line);
  background: var(--night2);
  color: var(--night-body);
}
.tm.stage { background: linear-gradient(90deg, var(--y), #ffb020); color: #241a00; border: 0; letter-spacing: 2px; }
.tm.vip { border-color: var(--y); color: var(--y); }
.tm.bar { background: #2b2b33; }

.pb-warn { font-size: 11.5px; color: var(--night-sub); line-height: 1.6; margin: 10px 0 0; }

/* 매장이 그 칸에 적어 둔 **안내 글**(2026-09-08 지시: 「모든 곳에 작성하고 이미지
 * 올릴 수 있게」). 가격표·테이블맵·홍보영상 판 **밑**과 카드 맨 위 사진 밑에 선다.
 *
 * ⚠ **판 안이 아니라 판 밖이다.** 그래서 `--night-*`·`--panel-*` 을 쓰면 안 된다 —
 *   저 값들은 판(`.vinfo`·`.mfall`) 안에서만 뜻이 있고, 바깥은 흰 카드다.
 *   흰 카드 위에 `--panel-ink`(#fff)를 적으면 글자가 그대로 사라진다.
 *
 * 왼쪽 금색 줄 하나로 **매장이 쓴 글**임을 표시한다 — 24K 가 적은 안내문
 * (`.pb-warn`·`.mhint`)과 눈으로 갈려야 한다. */
.mnote {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-left: 3px solid var(--y);
  border-radius: 0 10px 10px 0;
  background: var(--bg);
  color: var(--ink);
  font-size: 12.5px;
  line-height: 1.7;
  overflow-wrap: anywhere;
}
/* 카드 맨 위 사진 바로 밑 — 탭 띠와 붙어 서므로 위아래 여백을 따로 준다. */
.mnote.hero { margin: 10px 0 2px; }

/* ── 우측 레일 ──
 * 하단 바를 걷고 여기로 옮겼다(2026-09-05 지시). 지도가 주인공인 화면에서
 * 화면 아래를 통째로 쓰는 띠보다 옆 기둥이 지도를 덜 가린다.
 *
 * 레일 자체는 클릭을 받지 않는다(pointer-events:none). 안 그러면 기둥 둘레의
 * 빈 자리가 지도 클릭을 먹는다 — 이 화면에서 이미 여러 번 겪은 결함이다. */
/* **다른 화면 위에도 뜬다**(z-index 가 .page 보다 높다). 안 그러면 예약 화면에서
   채팅으로 바로 못 넘어가고 한 번 닫았다 다시 눌러야 한다. */
/* 생김새는 **국모 웹과 같다**(2026-09-05 지시). 아래 값은 국모
 * `lib/core/theme/map_chrome.dart` 의 토큰과 `_CatTile`·`_MapTypeSelector` 의
 * 실제 치수를 그대로 옮긴 것이다. 지도 종류 선택기와 카테고리 기둥의 좌우 변이
 * **정확히** 맞아야 하나로 이어진 레일로 읽히므로 폭·라운드는 여기서만 정한다.
 *
 * 폰에서 한 단계 줄이는 것도 국모와 같다 — 줄이는 건 **상자**뿐이고 아이콘 글리프
 * (18.1)와 라벨 글자(11.2)는 그대로다. 기준 폭도 국모(600)를 따른다. */
.rail {
  position: absolute;
  right: 12px;
  /* 롤링바(`.stage` 위 0~var(--roll), z 19)가 여기 있는 것을 전부 가리므로
     **띠 아래에서 시작한다.** z 로 이기면 안 된다 — 그러면 띠가 안 읽힌다. */
  top: calc(var(--roll) + 8px);
  z-index: 10;
  display: flex;
  flex-direction: column;
  /* 선택기가 왼쪽으로 펼쳐져도 **오른쪽 변은 움직이지 않는다.** center 로 두면
     펼칠 때 기둥 전체가 왼쪽으로 밀려 레일이 흔들린다. */
  align-items: flex-end;
  gap: var(--rail-gap);
  pointer-events: none;

  --rail-w: 53.1px;
  --rail-h: 53.1px;
  --rail-gap: 9.2px;
  --rail-r: 11.4px;
  --rail-pad: 7.6px;
  --rail-lh: 1.3px;
  /* 지도 위 컨트롤은 뒤 지도가 은은히 비치도록 반투명으로 깐다(국모 0.72).
     켜짐/꺼짐은 진하기가 아니라 **색**(검정 ↔ 흰 바탕)으로 가른다. */
  --rail-bg: rgba(255, 255, 255, 0.72);
  --rail-on-bg: rgba(0, 0, 0, 0.72);
  --rail-ink: #64748b;
  --rail-on-ink: #fff;
  --rail-line: #e5e7eb;
  --rail-brand: #1e3a8a;
}
@media (max-width: 599px) {
  .rail {
    --rail-w: 47.79px;
    /* PC 는 정사각, 폰은 세로만 키운다 — 국모가 좌측 날씨 칩과 아랫변을 맞춘 값이다. */
    --rail-h: 54px;
    --rail-gap: 8.28px;
    --rail-r: 10.26px;
    --rail-pad: 5.4px;
    --rail-lh: 1.17px;
  }
}
.rail > * { pointer-events: auto; }

/* 예약·채팅 타일. 2026-09-06 에 한 번 걷었다가 **같은 날 되돌렸다** — 지도뷰
   선택기만 남으니 두 화면으로 들어가는 문이 웹에 아예 없었다. 다시 걷을 일이
   생기면 `.rail-body` 규칙·`index.html` 의 div·`app.js` 의 두 줄(눌림 표시와
   손잡이)이 한 벌이라는 것부터 본다.

   그 밑 「필터」 타일(`.vfsw`)은 걷은 채로 둔다 — 종류를 고르는 자리는 「내 주변」
   목록 머리의 알약(`.vl-opt`) 하나다.

   타일을 붙여 쓰므로 개별 테두리·그림자는 두지 않는다 — 지도 위에 평평하게 얹힌
   판으로 읽히게(국모와 같은 지침). 타일 사이 경계는 1px 선 하나로만 낸다. */
.rail-body {
  width: var(--rail-w);
  border-radius: var(--rail-r);
  overflow: hidden;
}
.rail-body button {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--rail-lh);
  padding: var(--rail-pad) 2px;
  background: var(--rail-bg);
  font-size: 11.2px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.2px;
  white-space: nowrap;
  color: var(--rail-ink);
}
.rail-body button + button { border-top: 1px solid var(--rail-line); }
.rail-body button svg { width: 18.1px; height: 18.1px; }
.rail-body button[aria-pressed="true"] { background: var(--rail-on-bg); color: var(--rail-on-ink); }

/* 네 글자짜리 이름표(「제휴문의」)만 자간을 한 번 더 좁힌다.
   11.2px × 4자 − 0.2px × 4 = 44.0px 인데 **폰 타일의 속폭이 43.79px** 이다
   (`--rail-w` 47.79 − 좌우 2px). `nowrap` 이라 넘친 글자는 잘리지도 접히지도 않고
   **조용히 타일 밖으로 삐져나간다** — 고정 배치 안의 가로 넘침은 `body.scrollWidth`
   로도 아무 검사로도 안 잡힌다. 글자 크기를 줄이면 옆 타일(예약·채팅·탐색)과 키가
   어긋나므로 **자간만** 좁힌다(−0.6px → 43.2px).
   다섯 글자를 달게 되면 이 값으로는 모자라다 — 그때는 재고 다시 정한다. */
.rail-body button.wide { letter-spacing: -0.6px; }

/* 내 위치는 레일 밑에 이어 붙는다. 오른쪽 기둥 하나로 읽히게. */
.locate { right: 20px; }

/* 다른 화면에서 지도로 돌아가는 문. 하단 바가 없어졌으니 이게 유일한 길이다. */
/* 레일이 오른쪽을 쓰니 닫기는 **왼쪽**에 둔다 — 겹치면 서로를 가린다. */
.page-close {
  position: absolute;
  left: 12px; top: calc(var(--roll) + 8px);
  z-index: 9;
  font-size: 18px;
  color: var(--sub);
  padding: 8px 12px;
}

/* ── 찜 창 ──
 * 레일 「찜」이 여는 **지도 위 창**(`#favlist`). 2026-09-07 지시로 「탐색 창과 같은
 * 크기·같은 자리」가 되면서 `#page` 화면에서 옮겨 왔다.
 *
 * ★ **판때기 규칙은 여기 없다.** 자리·폭·높이·그림자·폰에서 좌우로 펴지는 것까지
 * 전부 `.vlist`(「내 주변 목록」)에서 물려받는다 — 문서가 `class="vlist favlist"` 로
 * 두 이름을 같이 달고 있다. 값을 여기 베껴 적으면 둘 중 하나만 옮겨지는 날이 온다.
 * **이 자리는 「다른 점」만 적는 곳이다.**
 *
 * ⚠ 줄 이름은 `.fv-` 로 뗀다. 판은 나눠 써도 **줄은 안 나눈다** — `.vrow` 에는
 * 「겨눈 줄」 표시(`.aimed`)와 폰에서 썸네일이 커지는 규칙이 딸려 있어서, 이름을
 * 물려받으면 그것들이 이 창에까지 따라 들어온다.
 * (`.vs-` 는 지도뷰 선택기, `.vst-` 는 조회 통계가 이미 쓰는 이름이다.) */

/* 머리 둘째 줄(「이 기기에만 담겨요」)과 그 규칙 `.fv-note` 는 2026-09-07 지시로
   걷었다. 머리가 한 줄이 되면서 「내 주변」 목록의 머리와 같은 꼴이 됐다 —
   그래서 이 창의 머리에는 **따로 적을 규칙이 없다**(전부 `.vlist-head` 몫이다). */

/* 줄 = 여는 단추 + 빼는 하트. 하트를 줄 **안**에 넣는다 — 밖에 두면 줄 전체가
   누르는 자리인데 그 위에 또 누르는 자리가 얹혀 「어디를 눌러야 열리나」가 흐려진다.
   높이·여백은 옆 창(`.vrow`)과 **일부러 같게** 뒀다 — 같은 서비스에서 「가게 한 줄」은
   어디서나 같아 보여야 한다. 값이 같은 것이지 규칙을 나눠 쓰는 것이 아니다. */
.fv-row { display: flex; align-items: center; }
.fv-row + .fv-row { border-top: 1px solid var(--line); }
.fv-open {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 0 9px 12px;
  text-align: left;
  cursor: pointer;
}
.fv-open:hover, .fv-open:focus-visible { background: var(--soft); }
/* 글이 **네 줄**(이름 · 포인트 · 거리 · 영업시간)이 되면서 옆 창의 `.vrow .vthumb`
   와 같은 60px 으로 키웠다(2026-09-08 지시). 48px 이던 것은 글이 두 줄이던 때 값이다 —
   그대로 두면 키 큰 글 옆에서 눌린 단추처럼 보인다.
   폭은 걱정 안 해도 된다: 오른쪽 하트가 44px 이지만 옆 창은 그 자리에 거리(`em`)와
   여백을 쓰고 있어서, 이름이 들어갈 폭은 두 창이 거의 같다(실측 175px 대 158px). */
.fv-thumb {
  width: 60px; height: 60px;
  flex: none;
  border-radius: 10px;
  object-fit: cover;
  background: var(--soft);
}
.fv-noimg {
  display: grid;
  place-items: center;
  color: var(--dim-ink);
  font-size: 18px;
}
.fv-txt { flex: 1; min-width: 0; }
.fv-txt b {
  display: block;
  font-size: 13px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 리워드 포인트 — 옆 창(`.vrow .vpt`)과 **같은 값**이다. 규칙을 나눠 쓰지 않는 것은
   위의 「줄은 안 나눈다」 그대로고, 값이 어긋나면 두 창의 같은 가게가 다르게 보인다.
   `<i>` 라 **`display: block` 이 꼭 있어야 한다** — 없으면 인라인이라 이름 옆에 되붙는다. */
.fv-txt .vpt {
  display: block; font-style: normal;
  margin-top: 2px;
  font-size: 12px; font-weight: 700; color: var(--point);
}
.fv-sub {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  color: var(--sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 거리 — 2026-09-08 지시로 이 줄이 「클럽 · 강남」에서 거리로 바뀌었다.
   **옆 창에서는 줄 오른쪽 끝(`.vrow em`)에 서는 값**인데, 이 창은 그 자리가
   빼는 하트라 여기로 내려왔다. 색·굵기는 그 `em` 과 같은 값으로 둔다 —
   자리가 달라져도 「거리」로 읽혀야 한다(잿빛으로 두면 종류·지역 줄과 안 갈린다).
   **비어 있을 수 있다**(「내 위치」를 아직 안 잡았을 때) — 그때는 빈 줄로 남는다.
   자리를 죽이지 않는 이유는 아래 `.fv-gone` 이 이 줄에 글을 적기 때문이다. */
.fv-sub.fv-dist { color: var(--point); font-weight: 700; }
/* 영업시간 — 옆 창(`.vrow .hrs`)과 같은 세 갈래·같은 색이다. 「미확인」을 빈칸으로
   두지 않는 규칙까지 `hoursLine()` 한 곳에서 나오므로 여기서는 색만 받는다. */
.fv-hrs {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  color: var(--sub);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fv-hrs .hrs.on { color: var(--point); font-weight: 700; }
.fv-hrs .hrs.off, .fv-hrs .hrs.unknown { color: var(--dim-ink); }
/* 원장에서 사라진 것(지나간 캠페인)을 누르면 그 줄에 이유가 뜬다. 아무 일도
   안 일어나면 「눌러도 안 된다」로만 보인다.
   **`.fv-dist` 뒤에 둔다** — 셈이 같아서(0,2,0) 뒤엣것이 이긴다. 앞에 두면
   거리가 서던 줄에 이 글이 들어갔을 때 빨강이 골드에 먹힌다. */
.fv-sub.fv-gone { color: #c2262a; font-weight: 700; }
/* 빼는 하트. **담긴 것만 이 창에 있으므로 언제나 채워진 빨강**이다 —
   여기서 윤곽선을 보여 줄 일이 없다(그건 「안 담김」이라는 뜻이다).
   색은 카드 머리의 하트와 **같은 값 하나**(`--fav-on`)에서 나온다 —
   두 자리가 한 기능이라 한쪽만 빨강이면 다른 곳으로 읽힌다.
   상자를 44px 로 잡는다 — 폰에서 그림만 한 자리를 주면 못 맞춘다
   (창 머리의 ✕ 가 28px 상자를 쓰는 것과 같은 이유). 상자는 손가락 몫이고,
   **하트 자체의 크기는 안쪽 SVG 가 정한다** — 둘을 헷갈리면 안 된다.

   ── 하트는 카드 머리의 것과 **같은 그림·같은 크기**다(2026-09-08 지시) ──

   원래 여기는 글꼴 글리프(`♥`) 16px 이었다. 글리프는 크기가 **글꼴에서** 나와
   숫자를 아무리 맞춰도 저쪽 20px SVG 와 안 같아진다 — 그래서 그림 자체를
   `fav.js` 의 `HEART`(= `#panelFav` 와 같은 조각)로 바꿨다.
   채우기 위에 2.4 획을 한 겹 더 얹는 것까지 `.panel > .pfav[aria-pressed="true"]`
   와 같다. 한쪽만 고치면 그날로 두 하트가 다시 어긋난다. */
.fv-drop {
  flex: none;
  width: 44px; height: 44px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  line-height: 1;
  color: var(--fav-on);
  cursor: pointer;
}
.fv-drop svg {
  display: block;
  fill: currentColor;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linejoin: round;
}
.fv-drop:hover, .fv-drop:focus-visible { background: var(--soft); }
/* 아무것도 안 담겼을 때는 옆 창의 빈 판(`.vlist .none`)을 그대로 쓴다 —
   한 판에 두 가지 빈 화면이 있으면 같은 창으로 안 읽힌다.
   **막다른 골목을 만들지 않는다**: 어떻게 담는지를 그 자리에 적는다. */
.favlist .none b { color: var(--point); font-weight: 800; }

/* ── 제휴 문의 폼 ──
 * 레일 「제휴문의」가 여는 `#page` 화면. **회원이 아닌 사람(매장 사장님)이 쓰는
 * 유일한 화면**이라 회원 카드(`.card.night`·등급 알약 같은 것)를 하나도 안 쓴다.
 *
 * 간격은 **한 값(`--pf-gap`)에서만** 나온다. 칸마다 margin 을 손으로 적으면 칸을
 * 하나 끼워 넣는 날 그 줄만 어긋나고, 그 어긋남은 스크롤을 내려야 보인다.
 * 가입 폼(`.signup .fld`)의 치수를 그대로 따른다 — 이 서비스에서 「글 적는 칸」은
 * 46px 높이에 12px 라운드다. 두 폼이 다르게 생기면 같은 서비스로 안 읽힌다. */
.pf-lead {
  font-size: 13px;
  color: var(--sub);
  line-height: 1.75;
  margin: -4px 0 14px;
}
.pf {
  --pf-gap: 16px;
  display: flex;
  flex-direction: column;
  gap: var(--pf-gap);
  padding: 16px 16px 18px;
  /* 아래 `.pf-row` 가 **화면 폭이 아니라 이 카드의 폭**으로 갈리게 한다(이유는 그 자리에). */
  container-type: inline-size;
}
.pf .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* 한 칸. `label` 이 통째로 누르는 자리가 되도록 block 이다. */
.pf-f { display: block; margin: 0; border: 0; padding: 0; min-width: 0; }
.pf-lb {
  display: block;
  font-size: 12px;
  font-weight: 700;
  color: var(--sub);
  margin-bottom: 6px;
  padding: 0;
}
/* 필수 별은 **노랑**이다. 빨강으로 두면 아직 아무것도 안 틀렸는데 화면이 경고로 읽힌다. */
.pf-lb i { color: var(--y); font-style: normal; margin-left: 3px; font-weight: 800; }
.pf-lb em {
  font-style: normal;
  font-weight: 600;
  font-size: 11px;
  color: var(--dim-ink);
  background: var(--dim-bg);
  border-radius: 5px;
  padding: 1px 5px;
  margin-left: 5px;
}
.pf input, .pf textarea {
  width: 100%;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 0 14px;
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  background: #fff;
  outline: none;
}
.pf input { height: 46px; }
.pf textarea {
  padding: 12px 14px;
  line-height: 1.65;
  min-height: 116px;
  /* 세로로만 늘린다. 가로로 늘리면 카드 밖으로 나간다. */
  resize: vertical;
}
.pf input:focus, .pf textarea:focus { border-color: var(--y); }
.pf input::placeholder, .pf textarea::placeholder { color: #b6b9c2; }
.pf-hint { display: block; font-size: 11.5px; font-weight: 500; color: var(--sub); margin-top: 5px; }
/* 글자 수는 칸 **오른쪽 아래**에 붙는다 — 왼쪽에 두면 도움말(`.pf-hint`) 자리와 다툰다. */
.pf-count {
  display: block;
  text-align: right;
  font-size: 11px;
  font-weight: 600;
  color: var(--dim-ink);
  font-variant-numeric: tabular-nums;
  margin-top: 5px;
}

/* 담당자명·연락처는 자리가 넉넉할 때만 한 줄에 둘. 좁으면 한 줄에 하나로 돌아간다 —
   둘로 쪼개서 칸 하나가 150px 이 되면 「010-0000-0000」이 안 들어간다.

   **화면 폭(`@media`)이 아니라 카드 폭(`@container`)으로 가른다.** 이 폼은 PC 에서
   화면 가운데가 아니라 **왼쪽 380px 기둥**(`--sidew`) 안에 선다. 화면 폭으로 재면
   1280px 짜리 화면이 「넓다」고 답하는데 정작 폼이 놓인 칸은 316px 이라, 가장 넓은
   화면에서 칸이 가장 좁아진다 — 처음에 `@media (min-width: 520px)` 로 짰다가
   PC 에서 연락처 자리표가 잘리는 것을 그림에서 보고 고쳤다.
   컨테이너 질의를 모르는 옛 브라우저에서는 늘 한 줄에 하나다(그래도 안 깨진다). */
.pf-row { display: grid; grid-template-columns: 1fr; gap: var(--pf-gap); }
@container (min-width: 420px) {
  .pf-row { grid-template-columns: 1fr 1fr; }
}

/* 업체 유형 — 알약 셋. **진짜 라디오 버튼**을 숨기고 옆의 `span` 을 칠한다.
   `appearance:none` 으로 지우지 않고 화면 밖으로 보낸다 — 지운 라디오는 포커스가
   갈 자리를 잃어서 키보드로 고를 수가 없다. */
.pf-fs { border: 0; }
.pf-seg { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.pf-seg label { display: block; position: relative; }
.pf-seg input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  margin: 0;
}
.pf-seg span {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 46px;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: #fff;
  font-size: 14px;
  font-weight: 600;
  color: var(--sub);
  cursor: pointer;
  white-space: nowrap;
}
.pf-seg input:checked + span {
  border-color: var(--ink);
  background: var(--ink);
  color: #fff;
  font-weight: 700;
}
/* 키보드로 왔을 때만 테두리를 준다. 손가락으로 눌렀을 때도 나오면 고른 알약 둘레에
   파란 네모가 남아 「뭐가 잘못됐나」로 읽힌다. */
.pf-seg input:focus-visible + span { box-shadow: 0 0 0 3px rgba(245, 197, 66, 0.45); }

/* ── 동의 ──
   가입 화면과 같은 규칙이다: 누르는 자리를 44px 로 잡는다(작으면 폰에서 자꾸 빗나간다).
   여기 있던 거부 권리 고지(`.pf-note`)는 **소유자 지시로 걷어냈다(2026-09-07)** —
   다시 세울 일이 있으면 `<details>` **밖**이다(안에 두면 펼친 사람에게만 알린 꼴이다). */
.pf-agree { border-top: 1px solid var(--line); padding-top: 4px; }
.pf-ag {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  cursor: pointer;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink);
}
.pf-ag input { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--y); }
.pf-ag b { color: var(--point, var(--ink)); }
.pf-det { margin: 2px 0 0; }
.pf-det summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--sub);
  padding: 8px 0;
  min-height: 34px;
}
.pf-tbl { width: 100%; border-collapse: collapse; font-size: 12px; margin: 2px 0 4px; }
.pf-tbl th, .pf-tbl td {
  border: 1px solid var(--line);
  padding: 7px 9px;
  text-align: left;
  vertical-align: top;
  line-height: 1.6;
  color: var(--sub);
}
.pf-tbl th { white-space: nowrap; color: var(--ink); background: var(--soft); width: 74px; }

/* 실패는 **단추 바로 위**에서 말한다. 눈이 가 있는 자리다 —
   이 웹은 화면 아래 띠를 안 쓴다. */
.pf-err {
  margin: 0;
  background: var(--danger-bg);
  color: var(--danger-ink);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 12.5px;
  line-height: 1.6;
}
.pf-err[hidden] { display: none; }
.pf-send { margin: 0; }
.pf-send:disabled { background: var(--dim-bg); color: var(--dim-ink); cursor: progress; }

/* 접수된 뒤. **폼이 있던 자리를 그대로 쓴다.** */
.pf-done { padding: 26px 20px; text-align: center; }
.pf-tick {
  width: 46px; height: 46px;
  margin: 0 auto 12px;
  border-radius: 50%;
  background: var(--y);
  color: #111;
  font-size: 24px;
  font-weight: 800;
  display: grid;
  place-items: center;
}
.pf-done b { display: block; font-size: 15.5px; letter-spacing: -0.4px; }
.pf-done p { margin: 8px 0 0; font-size: 13px; color: var(--sub); line-height: 1.75; }
.pf-rc {
  margin: 16px 0 18px;
  padding: 10px 12px;
  background: var(--soft);
  border-radius: 10px;
  font-size: 12px;
  color: var(--sub);
  display: flex;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}
.pf-rc code { font-weight: 700; color: var(--ink); }

/* ── 내가 낸 제휴 문의 ──
 * **두 화면에 같은 칸이 선다** — 지도 홈의 「제휴문의」와 마이페이지(`my.html`).
 * 그래서 `my.css` 가 아니라 여기 둔다(두 화면이 다 `app.css` 를 싣는다).
 * 제 머리글을 안 만든다 — 머리글 크기가 두 화면에서 다르고, 여기서 정하면 한쪽이
 * 반드시 어긋난다. 이 칸은 「목록」만 그린다. */
.pfm { margin: 0 0 18px; }
.pfm-top { display: flex; align-items: baseline; gap: 8px; margin: 0 2px 8px; }
.pfm-top b { font-size: 13.5px; letter-spacing: -0.3px; }
.pfm-n { font-size: 12px; color: var(--sub); font-variant-numeric: tabular-nums; }
.pfm-list { display: flex; flex-direction: column; gap: 8px; }
.pfm-dim { font-size: 12px; color: var(--sub); line-height: 1.7; margin: 0; }

.pfm-row { background: #fff; border-radius: 14px; overflow: hidden; }
/* 줄 전체가 누르는 자리다 — 화살표만 누르게 두면 폰에서 자꾸 빗나간다. */
.pfm-head {
  width: 100%;
  display: grid;
  /* 이름은 남는 만큼, 상태·날짜·화살표는 제 크기대로. 이름이 길면 거기서만 줄어든다. */
  grid-template-columns: minmax(0, 1fr) auto auto 14px;
  align-items: center;
  gap: 8px;
  padding: 13px 14px;
  text-align: left;
  min-height: 52px;
}
.pfm-head > b {
  font-size: 14px;
  letter-spacing: -0.3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pfm-when { font-size: 11px; color: var(--dim-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pfm-caret { font-style: normal; color: var(--dim-ink); transition: transform 0.12s; text-align: right; }
.pfm-head[aria-expanded="true"] .pfm-caret { transform: rotate(90deg); }

/* 진행 상태. **낸 사람에게 쓰는 말이라 관리자 알약과 색을 나눈다** —
   「연락함」은 좋은 소식이라 초록, 「접수됨」은 아직 기다리는 것이라 노랑이다. */
.pfm-st {
  font-size: 10.5px;
  font-weight: 700;
  border-radius: 5px;
  padding: 2px 7px;
  white-space: nowrap;
  background: var(--dim-bg);
  color: var(--dim-ink);
}
.pfm-st.wait { background: var(--y2); color: #5a4200; }
.pfm-st.go { background: #e7f8ee; color: #157a3c; }
.pfm-st.ok { background: var(--ink); color: #fff; }

.pfm-body { padding: 0 14px 14px; border-top: 1px dashed var(--line); margin-top: -1px; }
.pfm-body[hidden] { display: none; }
.pfm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px 16px;
  margin: 12px 0 0;
}
.pfm-grid > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pfm-grid b { font-size: 11px; color: var(--sub); font-weight: 700; }
.pfm-grid span { font-size: 13px; color: var(--ink); word-break: break-all; }
.pfm-h { display: block; font-size: 11px; color: var(--sub); font-weight: 700; margin: 14px 0 5px; }
/* 적어 낸 줄바꿈을 그대로 살린다 — 눌러 붙이면 내가 쓴 글이 아닌 것처럼 보인다. */
.pfm-msg {
  background: var(--soft);
  border-radius: 10px;
  padding: 11px 13px;
  font-size: 13px;
  line-height: 1.75;
  white-space: pre-wrap;
  word-break: break-word;
  margin-bottom: 10px;
}
.pfm-drop {
  margin-top: 10px;
  font-size: 12px;
  color: var(--sub);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 7px 11px;
  background: #fff;
}
.pfm-note { font-size: 11.5px; color: var(--sub); line-height: 1.7; margin: 9px 2px 0; }

/* ── 지도뷰 고르기 ──
 * **국모 웹과 같은 썸네일 카드 접힘/펼침**이다(2026-09-05 지시). 좌하단에 세 개를
 * 늘 펼쳐 두던 알약을 걷고, 레일 맨 위 정사각 카드 하나로 접었다.
 *
 * 미리보기는 국모가 쓰는 그림 그대로다(assets/mapview/*.webp). CSS 로 그리던 조각은
 * 「무슨 뜻인지」만 보였지 국모 화면과 같아 보이지 않았다 — 여기서는 그것이 요건이다.
 * 원본(200×150 png)을 128×128 로 잘라 webp 로 구웠다: 세 장 합쳐 19KB 라
 * 첫 화면 무게 때문에 그림을 피할 이유도 사라졌다.
 *
 * 치수는 전부 국모 `_MapTypeSelector` 의 실측값이다. 카드 크기(47.3×45.9)와
 * 라벨 글자(9.8 / 8.5)는 레일 폭과 달리 폰에서도 줄지 않는다. */
.viewsw {
  display: flex;
  align-items: center;
}

/* 접힘 상태의 카드 = 지금 보고 있는 뷰. 누르면 왼쪽으로 패널이 열린다. */
.vs-toggle {
  position: relative;
  flex: none;
  display: block;
  padding: 0;
  width: var(--rail-w);
  height: var(--rail-h);
  border-radius: var(--rail-r);
  overflow: hidden;
}
/* 펼치면 패널과 한 판으로 이어지도록 왼쪽 모서리를 편다. */
.viewsw.open .vs-toggle { border-radius: 0 var(--rail-r) var(--rail-r) 0; }
.vs-toggle img,
.vs-card img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  /* 그림을 못 받았을 때 남는 색 — 국모의 폴백(0xFFE5E0D5)과 같다. */
  background: #e5e0d5;
}
.vs-toggle b {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 3.8px 0;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 9.8px;
  font-weight: 700;
  line-height: 1.1;
  text-align: center;
}
.viewsw.open .vs-toggle b { background: var(--rail-brand); }

/* 왼쪽으로 펼쳐지는 패널: [‹접기 | 구분선 | 카드 셋].
   애니메이션 없이 즉시 바뀐다(클릭 애니메이션 금지 — 국모와 같은 지침). */
.vs-panel {
  display: flex;
  align-items: center;
  height: var(--rail-h);
  padding: 0 7.6px;
  background: var(--rail-bg);
  border-radius: var(--rail-r) 0 0 var(--rail-r);
}
.vs-panel[hidden] { display: none !important; }
.vs-fold {
  flex: none;
  display: grid;
  place-items: center;
  width: 18.1px;
  height: var(--rail-h);
  padding: 0;
  color: #94a3b8;
}
.vs-fold svg { width: 25.1px; height: 25.1px; }
.vs-sep {
  flex: none;
  width: 1px;
  height: 27.8px;
  margin-right: 7.6px;
  background: var(--rail-line);
}
.vs-card {
  position: relative;
  flex: none;
  display: block;
  padding: 0;
  width: 47.3px;
  height: 45.9px;
  border-radius: 6px;
  overflow: hidden;
  /* 테두리는 상자 **안쪽**에 그린다(국모 foregroundDecoration) — 바깥에 두면
     카드가 그만큼 커져 셋의 간격이 어긋난다. */
  box-shadow: inset 0 0 0 1px #fff;
}
.vs-card + .vs-card { margin-left: 5.1px; }
.vs-card[aria-pressed="true"] { box-shadow: inset 0 0 0 1.8px var(--rail-brand); }
.vs-card b {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 2.2px 0;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 8.5px;
  font-weight: 700;
  line-height: 1.1;
  text-align: center;
}
.vs-card[aria-pressed="true"] b { background: var(--rail-brand); }
/* 고른 것에는 체크를 붙인다 — 색만으로는 무엇이 켜졌는지 흑백 화면에서 안 갈린다. */
.vs-card i {
  position: absolute;
  top: 1px; right: 1px;
  display: none;
  place-items: center;
  width: 9.3px; height: 9.3px;
  border-radius: 50%;
  background: var(--rail-brand);
  color: #fff;
  font-size: 7.3px;
  font-style: normal;
  line-height: 1;
}
.vs-card[aria-pressed="true"] i { display: grid; }

/* ── 거리뷰 ──
 * 지도를 **덮기만 한다** — 지도를 지우지 않으니 끄면 보던 자리가 그대로 돌아온다. */
.roadview {
  position: absolute;
  /* 위를 `--roll` 만큼 비워 두던 자리다 — 롤링바가 그 위에 얹혀 있었기 때문이다.
     이제 거리뷰가 열리면 띠를 걷으므로(app.js `setRollForRoadview`) 비워 둘 이유가
     없다. 남겨 두면 그 26px 이 **지도 타일 한 줄로 남아** 파노라마 위에 뜬다. */
  inset: 0;
  z-index: 12;
  background: #111;
}
.roadview[hidden] { display: none !important; }

.rv-close {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* 거리뷰가 위까지 꽉 차므로 단추도 같이 올라온다 — 띠 자리를 더하지 않는다. */
  top: 12px;
  z-index: 13;
  background: rgba(17, 18, 20, 0.86);
  color: #fff;
  border-radius: 999px;
  padding: 9px 16px;
  font-size: 12.5px;
  font-weight: 700;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}
.rv-close[hidden] { display: none !important; }

/* 「카드가 올라오면 지도뷰 조각을 감춘다」던 규칙은 필요 없어졌다 — 선택기가
   좌하단을 떠나 우측 레일 맨 위로 갔고, 카드는 폰에서 아래·PC 에서 왼쪽이라
   이제 서로 겹치는 자리가 아니다. */

/* 지도 위에 떠 있던 클럽·라운지 거르개(.vfilters) 는 2026-09-05 지시로 걷었고
   규칙도 같이 지웠다. 2026-09-06 에 우측 레일 타일(.vfsw)로 한 번 돌아왔다가
   **같은 날 그것도 걷었다.** 지금 종류를 고르는 자리는 「내 주변」 목록 머리의
   알약(`.vl-tabs` · `.vl-opt`) 하나뿐이다. */

/* 클럽은 검정·금색, 라운지는 보라·핑크. **꼴은 같게 두고 색만 가른다** —
   꼴까지 다르면 지도 위에서 둘이 다른 종류의 물건으로 읽힌다. */
.venue.lounge { --bub-c: var(--pin-c-lounge); --bub-a: var(--pin-a-lounge); }
/* 이름은 **흰색으로 둔다**(2026-09-07 지시). 연분홍(#ffd9f4)으로 바탕과 짝을 맞추던
   것을 걷었다 — 라운지만 이름이 분홍이면 캠페인·클럽과 같은 「가게 이름」인데도
   다른 종류로 읽힌다. 색으로 가르는 것은 **바탕과 금액**의 몫이다. */
.venue.lounge .nm { color: #fff; }
/* `.on` 이 여기에도 있어야 한다 — 클럽 쪽(`.venue.on`)에는 있는데 라운지에는 `:hover`·
   `:focus` 뿐이었다. 손으로 누른 표식은 이제 포커스를 놓으므로(map.js 의
   `dropPointerFocus`), `.on` 이 없으면 고른 라운지가 분홍을 잃고 클럽의 노랑으로
   돌아간다. **「고른 것」을 포커스에 얹어 두지 않는다** — 켜는 손과 보이는 꼴은 따로다. */
.venue.lounge:hover, .venue.lounge:focus, .venue.lounge.on {
  --bub-c: var(--pin-c-lounge-on);
  --bub-a: var(--pin-a-lounge-on);
}
.venue.lounge:hover .nm, .venue.lounge:focus .nm, .venue.lounge.on .nm { color: #2a0b38; }
.venue.lounge:hover .amt, .venue.lounge:focus .amt, .venue.lounge.on .amt { color: #2a0b38; }

/* ── 내 주변 목록 ── */
/* **윗변은 레일 「예약」 단추와 같은 줄, 오른쪽 변은 레일 옆**(2026-09-07 지시).
   PC·폰이 **같은 자리 규칙**을 쓴다 — 다른 것은 폰이 좌우로 넓어진다는 것뿐이다.

   자리가 여러 번 옮겨졌다: 우하단(`right:84px; bottom:14px`) → **화면 한가운데**
   (「탐색」이 여는 창이 되면서 «지금 보고 있는 것»이 됐는데 구석에 붙은 창은 지도의
   곁다리로 읽혔다) → **오른쪽 위**(지금). 한가운데에 세우니 이번엔 **보려던 지도의
   복판을 제가 덮었다** — 창은 「지금 보는 것」이 맞지만 지도를 가리면 고른 곳이
   어디쯤인지가 사라진다.

   `right: 78px` = `12(레일 여백) + 53.1(레일 폭) + 12` — 창의 오른쪽 변이 레일
   기둥과 12px 떨어져 한 덩어리로 읽힌다. **폰 폭(47.79px)이 아니라 PC 폭으로
   잡는다**(레일 치수는 599px 에서 줄지만 이 규칙은 전 폭 공통이다).

   `top` 의 62px 은 **레일 첫 칸(지도뷰 선택기) 높이 + 칸 사이 틈**이다 —
   PC 53.1+9.2, 폰 54+8.28 로 **양쪽이 62 로 같아서** 한 값으로 쓴다. 레일 치수를
   고치면 여기도 같이 고쳐야 한다(`--rail-h`·`--rail-gap` 은 `.rail` 안에 있어
   형제인 여기서는 못 읽는다). 실측으로 재라: `.rail-body` 윗변 − `.rail` 윗변.

   **`aimLift()` 가 이 자리를 읽는다**(app.js). 창이 위에 붙으면서 창 **위** 띠가
   70px 밖에 안 남아, 켜진 캐릭터를 위로 밀면 머리가 잘린다 — 그래서 그 함수가
   **위·아래 중 캐릭터가 들어가는 쪽**으로 민다. 이 `top` 을 키우면 그 판정이 바뀐다. */
.vlist {
  position: absolute;
  right: 78px;
  top: calc(var(--roll) + 8px + 62px);
  z-index: 7;
  width: 300px;
  /* 30줄이 드는 창이라 46% 에서 한 치수 키웠다 — 그래도 다 안 들어가면 안에서 굴린다. */
  max-height: 58%;
  display: flex;
  flex-direction: column;
  background: rgba(255, 255, 255, 0.96);
  border-radius: 16px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.18);
  overflow: hidden;
}
/* 머리는 **두 줄이다** — 위는 「내 주변 N곳」과 ✕, 아래는 종류 알약 셋.
   한 줄에 다 넣으면 300px 안에서 알약이 눌려 글자가 잘린다. */
.vlist-head {
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  /* 머리는 굴리지 않는다 — 줄이 30개라 몸통만 굴러야 ✕ 와 알약이 늘 제자리에 있다. */
  flex: none;
}
.vlist-top {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* `flex: 1` 로 남은 폭을 먹어서 ✕ 를 **오른쪽 끝으로** 민다. */
.vlist-top b { flex: 1; min-width: 0; font-size: 12.5px; white-space: nowrap; }
/* ✕ — 매장 카드의 `.panel > .close` 와 같은 꼴·같은 자리(오른쪽 위)다.
   **누르는 자리를 글자보다 넓게 잡는다** — 폰에서 12px 짜리 ✕ 만 하면 못 맞춘다. */
.vl-close {
  flex: none;
  width: 28px; height: 28px;
  margin: -4px -4px -4px 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 9px;
  font-size: 14px; line-height: 1;
  color: var(--sub);
}
.vl-close:hover, .vl-close:focus { background: var(--soft); color: var(--ink); }
/* 종류 알약 — 셋이 폭을 똑같이 나눈다(`flex: 1`). 고른 것만 검게 채운다.
   꼴은 상단 필터 알약(`.filters button`)과 같은 규칙이다. */
.vl-tabs { display: flex; gap: 6px; margin-top: 8px; }
.vl-opt {
  flex: 1;
  padding: 6px 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--sub);
}
.vl-opt:hover { background: var(--soft); }
.vl-opt[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
  font-weight: 700;
}
.vlist-body { overflow-y: auto; }
/* 「내 위치」로 걷었을 때. **이 줄이 없으면 hidden 이 안 먹는다** — 위 .vlist 의
   display:flex 가 브라우저 기본 [hidden]{display:none} 을 이긴다.
   접기 단추(`.closed`)는 걷었다(2026-09-05 지시) — 여닫이는 이 hidden 하나뿐이다. */
.vlist[hidden] { display: none; }

.vrow {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 12px;
  text-align: left;
  border-bottom: 1px solid var(--line);
}
.vrow:hover, .vrow:focus { background: var(--soft); }
/* 종류를 알리던 점(`i.club`/`i.lounge`)은 걷었다(2026-09-05 지시) — 그 자리에
   업체 대표 사진이 정사각형으로 들어간다. 종류는 아랫줄 글이 이미 말한다.
   **`flex: none` 이 없으면 이름이 긴 줄에서 사진이 눌려 찌그러진다.** */
.vrow .vthumb {
  width: 38px; height: 38px; flex: none;
  border-radius: 8px; object-fit: cover;
  background: var(--soft); border: 1px solid var(--line);
}
.vrow div { flex: 1; min-width: 0; }
/* 리워드 포인트는 **이름 바로 아래 줄**에 선다(2026-09-07 지시).
   2026-09-06 에는 이름 옆(`.vnl` flex 한 줄)이었는데, 이름이 긴 줄에서 오른쪽으로
   밀려 거리 숫자와 붙어 섰다. `.vnl` 은 그때 같이 걷었다 — `b` 가 다시 제 줄을
   통째로 쓰므로 이름 잘림(`…`)은 아래 `.vrow b` 규칙이 그대로 맡는다.
   `<i>` 라 **`display: block` 이 꼭 있어야 한다** — 없으면 인라인이라 이름 옆에 되붙는다.
   글자는 한 치수 키웠다(11→12px): 제 줄을 갖게 됐으니 아래 두 잿빛 줄과 섞이면 안 된다. */
.vrow .vpt {
  display: block; font-style: normal;
  font-size: 12px; font-weight: 700; color: var(--point);
}
.vrow b { display: block; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vrow span { display: block; font-size: 11px; color: var(--sub); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vrow em { font-style: normal; font-size: 11px; color: var(--point); font-weight: 700; white-space: nowrap; }
/* 영업시간 줄 — **오늘 것만** 적는다(요일별 전체 시간표는 매장 카드에 있다).
   세 갈래를 색으로도 가른다: 지금 영업 중(골드·굵게) · 오늘 휴무와 미확인(흐리게).
   **「미확인」을 빈칸으로 두지 않는다** — 빈칸으로 두면 자료가 빠진 것이 조용히 덮인다. */
.vrow .hrs.on { color: var(--point); font-weight: 700; }
.vrow .hrs.off, .vrow .hrs.unknown { color: var(--dim-ink); }
/* 사진 크기는 **옆 글 덩어리 높이를 따라간다** — 38px→46px→60px→46px 으로 오갔다.
   2026-09-08 에 「클럽 · 강남」 줄이 걷히며 글이 네 줄에서 세 줄(실측 42px)이 되어
   60px 을 도로 46px 으로 내렸다. 안 내리면 사진이 글보다 18px 높아 **줄 높이를 사진이
   정하게 되고**, 그만큼 창에 드는 곳 수가 줄어든다(반대로 글보다 작으면 키 큰 글 옆에서
   눌린 단추처럼 보인다). 재는 자리는 _tools/nearby-list-audit.mjs 의 「사진이 옆 글
   키에 맞는다」다 — 12px 안쪽이면 맞은 것으로 센다.

   ⚠ **찜 창의 `.fv-thumb` 는 60px 그대로다.** 거기는 줄 오른쪽 끝이 하트라 거리가 글
   덩어리 **안으로** 내려와 네 줄이다. 두 창은 같은 자리에 서서 **한 번에 하나만 뜨므로**
   나란히 놓고 볼 일이 없다 — 각자 제 글 높이에 맞추는 것이 맞다. */
.vrow .vthumb { width: 46px; height: 46px; }
/* **고른 줄** — 목록에도 「지금 이걸 골랐다」가 남아 있어야 한다. 2026-09-07 부터는
   줄을 누르면 카드가 같이 열리는데, **PC 에서는 카드가 왼쪽 기둥이라 목록이 옆에
   그대로 선다** — 그때 켜진 줄이 없으면 카드에 뜬 곳이 목록의 어느 줄인지가 안 보인다.
   (폰은 카드가 화면을 다 써서 목록이 그동안 걷힌다. 카드를 닫으면 켜짐도 같이 풀린다 —
   `closePanel` 의 「한쪽만 끄지 않는다」.)

   왼쪽 4px 골드 띠 + 옅은 골드 바탕. 글자색·굵기는 안 건드린다 — 줄 높이가 흔들리면
   목록 전체가 한 칸씩 밀린다. 띠는 `inset` 그림자라 **자리를 안 먹는다**(테두리로
   내면 그 4px 만큼 안쪽 내용이 밀려 다른 줄과 안 맞는다). */
.vrow.aimed { background: rgba(183, 134, 11, 0.1); box-shadow: inset 4px 0 0 var(--point); }
/* `--soft` 회색으로 뒀더니 `:hover` 와 **똑같은 값**이라 마우스가 얹힌 줄과 구별이 안 됐다.
   골드가 옅게 깔려야 「고른 줄」이 한눈에 잡힌다. 얹힌 줄이 더 진해지면 다시 뒤집히므로
   `:hover` 는 이 줄을 못 이기게 못 박는다. */
.vrow.aimed:hover, .vrow.aimed:focus { background: rgba(183, 134, 11, 0.16); }
.vlist .none { padding: 16px 12px; font-size: 12px; color: var(--sub); line-height: 1.6; margin: 0; }

@media (max-width: 899px) {
  /* **폰에서 다른 것은 「좌우로 넓어진다」뿐이다**(2026-09-07 지시: 「모바일에서도
     동일한 위치에」). 윗변(`top`)도 오른쪽 변(`right: 78px`)도 위 규칙 그대로 쓴다 —
     여기서 되짚지 않는다. `left` 와 `width:auto` 만 더해 왼쪽 끝까지 편다.

     높이는 48%→57%. 아랫변이 **좌하단 날씨 창**(390×844 에서 731px)에 닿으면 안 되고,
     아래 띠에 **켜진 캐릭터 한 명(120px)이 서야 한다** — 창이 위에 붙으면서 캐릭터가
     아래로 가기 때문이다(`aimLift()`). 390×844 실측: 창 150–600 · 아래 띠 244px.
     이 값을 키울 때 그 둘을 같이 재라(`nearby-list-audit` 에 재는 줄이 있다). */
  .vlist {
    right: 78px; left: 10px; width: auto;
    max-height: 57%;
  }
  /* 카드가 열려 있으면 목록을 걷는다. **`~` 로는 안 걸린다** — 목록이 카드보다
     먼저 나와서 뒤를 보는 형제 선택자가 영영 안 맞는다. 그런데 목록의 z-index 가
     카드보다 높아서, 안 걸리는 동안 목록이 카드 위에 떠 눌림을 가로챘다
     (가격표를 눌러도 크게 보기가 안 열리던 것이 이것이었다). */
  .stage:has(.panel:not([hidden])) .vlist { display: none; }
  /* 카드가 화면을 통째로 쓰게 되면서(2026-09-06) 우측 레일도 같이 걷는다.
     레일은 z 10 이라 카드(6)를 이긴다 — 그냥 두면 지도뷰 썸네일이 카드의 ✕ 와
     같은 자리(오른쪽 위)에 겹쳐 앉고, 예약·채팅 타일이 상세 내용 위에 뜬다.
     **`.page`(예약·채팅) 위에서는 그대로 둔다** — 거기서는 레일이 두 화면을
     오가는 길이라 지우면 한 번 닫았다 다시 눌러야 한다. */
  .stage:has(.panel:not([hidden])) .rail { display: none; }
}

/* ── 예약 이력 · 채팅 방 · 대화 ──
 *
 * 예약·채팅 화면을 시연 자료로 채우면서 같이 들어온 조각들이다
 * (자료 자체는 `assets/js/demo-activity.js`).
 *
 * 대화는 **말풍선이 좌우로 갈린다.** 누가 한 말인지를 색이 아니라 자리로 말한다 —
 * 색만으로 가르면 골드가 이미 「포인트」를 뜻하는 이 화면에서 뜻이 겹친다. */

/* 화면 안에서 묶음을 나누는 작은 제목. `h2`(화면 이름)보다 한 단 아래다. */
.sect {
  margin: 22px 2px 8px;
  font-size: 13px;
  font-weight: 700;
  color: var(--sub);
  letter-spacing: -0.3px;
}

/* 지난 방문 한 줄. `.kv` 와 같은 구분선을 쓰되 두 줄이 들어간다. */
.visit { padding: 11px 0; border-bottom: 1px solid var(--line); }
.visit:last-child { border: 0; }
.visit b { font-size: 13.5px; }
.visit .cond { display: block; margin-top: 3px; }
/* 칩이 이름을 밀어내지 않게 — 이름이 길면 이름 쪽이 줄어든다. */
.visit .row b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.visit .row .chip { flex: none; }

/* ── 채팅(시안 19~23) — **카카오톡과 같은 꼴** ──
   2026-09-07 지시: 「목록도 방도 카톡과 똑같이」. 그래서 이 화면만 앱의 카드·여백 규칙을
   벗고 카톡의 것을 그대로 쓴다 — 흰 목록, 하늘빛 방(#B2C7DA), 노란 내 말풍선(#FEE500),
   꼬리 달린 말풍선, 56px 머리줄, 아래 붙는 입력바.

   앞판은 내 말풍선을 검정으로 뒀다 — 「골드는 포인트 몫이라 안 쓴다」는 이유였다.
   그 판단은 24K 골드(#F5C542)에 대한 것이고, 여기 쓰는 카카오 노랑(#FEE500)은
   다른 값이다. 포인트 금액과 나란히 놓여도 헷갈리지 않는다. */

/* **폰에서만** 화면을 통째로 쓴다. 상단바·레일·롤링바를 덮지 않고 아예 걷는다 —
   폰에서 카톡 화면 위에 다른 앱의 기둥이 서 있으면 그 순간 「비슷한 것」이 된다.
   걷는 표는 두 짝이고 켜고 끄는 곳은 app.js 의 `showTab()` 하나다.
     `body.kkfull` — 채팅(카톡 꼴). 나가는 문이 **제 머리줄 안**이라 바깥 ✕ 는 끈다.
     `body.pgfull` — 예약(2026-09-07 지시: 「채팅과 동일하게」). 나가는 문이
                     **바깥 ✕(`#pageClose`) 하나뿐**이라 그 단추를 같이 끌어올린다.
   둘은 **동시에 켜지지 않는다** — 채팅이면 kkfull, 아니면 pgfull 이다.

   **PC 는 아니다.** 거기서는 예약·지갑과 같은 왼쪽 기둥으로 선다(2026-09-07 지시) —
   지도를 덮으면 보던 지도가 사라진다. 아래 900px 갈림에서 되돌린다. */
@media (max-width: 899px) {
  body.kkfull .topbar, body.kkfull .roll, body.kkfull .rail,
  body.pgfull .topbar, body.pgfull .roll, body.pgfull .rail { display: none; }
  .page.kakao,
  body.pgfull .page { inset: 0; z-index: 11; }

  /* 화면이 z 11 로 올라오면 z 9 짜리 ✕ 는 **그 밑에 깔린다** — 보이기는 하는데
     눌리지 않는 단추가 되어, 폰에서 예약에 들어가면 나갈 길이 사라진다.

     자리는 **「예약」 글자와 같은 줄 오른쪽 끝**이다(2026-09-07 지시). 왼쪽 위에
     따로 세우면 제 한 줄을 차지해 제목이 그만큼 밀려 내려간다. 기본 규칙이
     `left: 12px` 라 **`left: auto` 로 풀어야** 오른쪽이 먹는다.
     높이는 제목 줄 한가운데에 맞춘 값이고 **아래 `.inner` 의 위 여백과 한 짝이다** —
     둘 중 하나만 고치면 어긋난다. 판정은 「top 이 같나」가 아니라 **가운데 높이 차**로
     한다(글자와 단추는 키가 다르다). */
  body.pgfull #pageClose {
    z-index: 12;
    left: auto;
    right: 6px;
    top: calc(env(safe-area-inset-top, 0px) + 12px);
  }
  /* 오른쪽 76px 은 레일을 피하려고 비웠던 자리다. 그 레일을 걷었으니 도로 채운다 —
     안 그러면 화면만 넓어지고 글은 그대로 왼쪽에 몰려 있다.
     위 여백은 ✕ 가 제목 줄로 들어오면서 46px → 12px 로 줄었다가, 제목이 굴려도
     남게 되면서 **제목 띠 안으로 옮겨 갔다**(바로 아래) — 여기는 0 이다. */
  body.pgfull .page .inner {
    padding: 0 16px 16px;
  }
  /* 폰에서는 화면이 `inset: 0` 이라 **띠가 노치 밑까지 올라간다.** 위 여백에
     안전영역을 같이 들어야 제목이 상태바에 겹치지 않고, 그러고도 띠는 화면
     맨 위(y 0)부터 칠해져 그 틈으로 글이 지나가지 않는다.
     18px 은 PC 와 같은 값이다 — 12(칸 여백) + 6(h2 의 옛 margin-top). */
  body.pgfull .page h2 {
    padding-top: calc(env(safe-area-inset-top, 0px) + 18px);
  }
}
.page.kakao {
  background: #fff;
  overflow: hidden;
}

.kk {
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
  background: #fff;
  overflow: hidden;
}
.kk-room { background: #b2c7da; }

/* — 머리줄 — 목록은 흰 바탕에 큰 제목, 방은 하늘빛 바탕에 이름 — */
.kkbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: calc(env(safe-area-inset-top, 0px) + 10px) 6px 8px 16px;
  background: #fff;
}
.kk-room .kkbar { background: #b2c7da; padding-left: 4px; }
.kktit { flex: 1; font-size: 22px; font-weight: 800; letter-spacing: -0.6px; }
.kkacts { display: flex; align-items: center; gap: 0; }
.kkico {
  flex: none;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: none;
  color: #1a1a1c;
  cursor: pointer;
}
.kkico svg { width: 23px; height: 23px; }
.kkico:hover { background: rgba(0, 0, 0, 0.06); }
/* 뒤로 ‹ 는 `.back` 을 쓰지 않는다 — 그 이름은 `my.html` 의 네모 단추가 이미 쓰고 있어
   (배경·테두리가 붙는다) 카톡 머리줄에 흰 상자가 하나 얹힌 꼴이 됐다. */
.kkico.kkback svg { width: 25px; height: 25px; }
.kkwho { flex: 1; min-width: 0; }
.kkwho b {
  display: block;
  font-size: 15.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kkwho span {
  display: block;
  font-size: 11px;
  color: #46586a;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* — 찾기 줄. 목록에서도 방에서도 같은 꼴이고, **다시 그리지 않는다**(→ chat.js) — */
/* `display: flex` 는 브라우저 기본 `[hidden] { display: none }` 을 **이긴다** —
   못을 안 박으면 접어 둔 찾기줄이 늘 펼쳐진 채로 뜬다(이 화면에서 실제로 그랬다). */
.kkfind[hidden] { display: none !important; }
.kkfind {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  margin: 0 12px 8px;
  padding: 0 12px;
  border-radius: 12px;
  background: #f2f3f5;
}
.kk-room .kkfind { margin: 0 10px 6px; background: rgba(255, 255, 255, 0.85); }
.kkfind svg { flex: none; width: 18px; height: 18px; color: #8b8d94; }
.kkfind input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 13.5px;
  color: var(--ink);
}
.kkfind input:focus { outline: none; }
/* **✕ 가 둘로 보이던 것.** 우리 ✕ 는 하나뿐이고, 나머지 하나는 브라우저가
   `type="search"` 에 저절로 그리는 지우개다(크롬·엣지는 파란 ✕).
   HTML 에는 안 보이니 검사도 «단추 한 개»로 통과한다 — **눌러서** 가른다.
   `-ms-clear` 는 옛 엣지 몫이다. */
.kkfind input::-webkit-search-cancel-button,
.kkfind input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }
.kkfind input::-ms-clear { display: none; width: 0; height: 0; }
.kkfind .hits { flex: none; font-size: 11.5px; color: #5d6f80; }
.kkfind button {
  flex: none;
  padding: 4px;
  border: 0;
  background: none;
  color: #8b8d94;
  font-size: 13px;
  cursor: pointer;
}

/* 머리줄 아래는 통째로 굴러간다 — 알약·띠광고·목록·맨 아래 규칙까지 한 덩어리다.
   목록만 굴리면 규칙 문단이 화면 밑에 **늘 붙어** 앉아 방 두 개만큼을 먹는다. */
.kkscroll { flex: 1; min-height: 0; overflow-y: auto; }

.kkchips {
  display: flex;
  gap: 6px;
  padding: 2px 14px 10px;
  overflow-x: auto;
  scrollbar-width: none;
}
.kkchips::-webkit-scrollbar { display: none; }
.kkchips button {
  flex: none;
  height: 32px;
  padding: 0 14px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: #fff;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: #3a3d45;
  white-space: nowrap;
  cursor: pointer;
}
.kkchips button[aria-pressed="true"] { background: #111214; border-color: #111214; color: #fff; }

/* 띠광고 자리(카톡 목록 맨 위 그 띠). 여기서는 **지도로 가는 문**이다 —
   방을 새로 만드는 길이 예약뿐이라 그 길을 이 자리에 둔다. */
.kkbanner {
  display: flex;
  align-items: center;
  gap: 10px;
  width: calc(100% - 24px);
  margin: 0 12px 8px;
  padding: 10px;
  padding-left: 14px;
  border: 0;
  border-radius: 14px;
  background: #f2f3f5;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.kkbanner .t { flex: 1; min-width: 0; }
.kkbanner .t b { display: block; font-size: 13px; color: #1a1a1c; }
.kkbanner .t span {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: #7c7f88;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kkbanner img { flex: none; width: 74px; height: 46px; border-radius: 10px; object-fit: cover; }

/* 방 한 줄(19). 마지막 메시지는 **한 줄로 자른다** — 길면 방 목록이 아니라
   대화 목록이 되어, 어느 방이 몇 개인지 한눈에 안 들어온다.
   카톡처럼 **줄 사이에 선이 없다** — 얼굴 사진이 이미 줄을 가른다. */
/* 아래 안내 문단을 걷은 뒤로 마지막 줄이 바닥에 붙는다 — 그 문단이 두던 만큼 띄운다. */
.chatlist { padding-bottom: 20px; }
/* **검색이 줄을 걷는 유일한 손이 이것이다.** `display:flex` 는 `[hidden]` 을 이겨서,
   `row.hidden = true` 가 분명히 걸리는데도 줄이 그대로 서 있었다 — 「검색이 안 된다」의
   정체가 이것이었다(찾기줄 `.kkfind` 는 같은 함정을 이미 한 번 밟았다).
   검사는 속성이 아니라 **그려진 높이**로 잰다(`_tools/chat-search-audit.mjs`). */
.chl[hidden] { display: none !important; }
.chl {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 16px;
  text-align: left;
  background: none;
  border: 0;
  font-family: inherit;
  color: inherit;
  cursor: pointer;
}
.chl:hover { background: #f7f7f8; }
.chl .m { flex: 1; min-width: 0; }
.chl .m b { display: flex; align-items: center; gap: 4px; font-size: 15px; font-weight: 600; }
.chl .m b .nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chl .m b .mk { flex: none; font-style: normal; font-size: 11px; }
.chl .m b .mgr {
  flex: none;
  font-style: normal;
  font-size: 11.5px;
  font-weight: 500;
  color: #a8aab0;
}
.chl .last {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  color: #8b8d94;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chl .r {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
  text-align: right;
}
.chl .r .t { font-size: 10.5px; color: #b0b2b8; }
/* 종료된 방은 흐리게. **숨기지 않는다** — 지난 대화가 신고의 근거다. */
.chl.closed .cav { filter: grayscale(1); opacity: 0.55; }
.chl.closed .m b { color: var(--sub); }
/* 나가기(2026-09-07 지시) — 「종료」 바로 아래, 같은 꼬리표 꼴에 빨간 글씨다.
   줄 자체가 button 이라 이것은 span 이다(button 안 button 은 줄을 두 토막 낸다).
   `.chip.r` 을 안 쓴 이유: `.chl .r` 이 오른쪽 칸 규칙이라 클래스 `r` 을 달면
   이 꼬리표가 세로 flex 통이 된다. 색은 같은 값을 여기서 준다. */
/* **둘의 크기를 맞춘다**(2026-09-07 이어진 지시). 「종료」는 두 자, 「나가기」는 세 자라
   글자에 맡기면 넓이가 다르다 — 위아래로 붙어 서니 그 차이가 그대로 보인다.
   넓은 쪽(나가기)에 맞춰 **둘 다 같은 min-width** 를 주고 글자를 가운데로 모은다.
   높이는 글자 크기·안여백이 같아 저절로 같다. */
.chl .r .chip {
  min-width: 48px;
  box-sizing: border-box;
  text-align: center;
}
.chl .leave {
  background: var(--danger-bg);
  color: var(--danger-ink);
  font-weight: 700;
  cursor: pointer;
  user-select: none;
}
.chl .leave:hover { background: #ffdcdc; }
.chl .leave:focus-visible { outline: 2px solid var(--danger-ink); outline-offset: 1px; }

.kkempty { margin: 24px 0; text-align: center; font-size: 12.5px; color: var(--sub); }
/* 목록 아래 안내 문단(.kknote)은 2026-09-07 지시로 걷었다 — 규칙도 같이 지웠다. */

/* 아바타. 동그라미가 아니라 **둥근 네모**다(카톡과 같은 꼴). */
.cav {
  position: relative;
  flex: none;
  display: inline-block;
  overflow: hidden;
  background: var(--dim-bg);
}
.cav img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cav .on {
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 10px;
  height: 10px;
  border-radius: 5px;
  background: var(--green);
  border: 2px solid #fff;
}
.cav.k24 {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  color: var(--y);
  font-size: 11px;
  font-weight: 800;
}

/* 안 읽은 수. 골드가 아니라 빨강이다 — 골드는 이 앱에서 포인트 몫이다.
   알림을 끈 방은 회색이다(카톡과 같다) — 꺼 놓고도 빨간 점이 남으면 끈 보람이 없다. */
.unread {
  flex: none;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--red);
  color: #fff;
  font-size: 11px;
  font-weight: 800;
  line-height: 20px;
  text-align: center;
}
.unread.muted { background: #c3c5ca; }

/* 대화 목록으로 돌아가는 문은 이제 머리줄의 ‹ 다(`.kkico.back`).
   `.back` 은 다른 화면이 아직 쓰고 있어 규칙만 남겨 둔다. */
.back {
  display: inline-block;
  padding: 7px 12px 7px 8px;
  border-radius: 10px;
  background: var(--card);
  border: 1.5px solid var(--line);
  font-size: 12px;
  font-weight: 700;
  color: var(--sub);
}

/* 예약 띠(20) — **왜 이 방이 열려 있는지**. 머리줄 바로 밑, 하늘빛 위에 뜬다.
   2026-09-07 부터 **누르는 자리**다 — 누르면 밑으로 예약 판이 내려온다. */
.bookbar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: calc(100% - 20px);
  margin: 0 10px 6px;
  padding: 8px 12px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.85);
  font-family: inherit;
  font-size: 11.5px;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.bookbar .cond { display: inline-flex; align-items: center; gap: 3px; white-space: nowrap; }
/* 내려오는 것을 화살표로 알린다 — 열리면 뒤집힌다. */
.bookbar .cv { font-style: normal; transition: transform 0.16s ease; }
.bookbar.open .cv { transform: rotate(180deg); }
.bookbar.open { border-radius: 12px 12px 0 0; margin-bottom: 0; }

/* 예약 판 — 「예약 보기」를 누르면 **띠 밑으로 내려온다**(2026-09-07 지시).
   대화창을 덮지 않는다: 창이 아니라 접혔다 펴지는 칸이라, 열어 둔 채로 대화를 볼 수 있다.
   길면 이 칸만 굴린다 — 화면을 다 먹으면 방을 열어 둔 뜻이 없다. */
.bkpanel {
  flex: none;
  max-height: 46vh;
  overflow-y: auto;
  margin: 0 10px 6px;
  padding: 10px 12px 12px;
  border-radius: 0 0 12px 12px;
  background: rgba(255, 255, 255, 0.95);
  font-size: 12px;
  animation: bkdrop 0.18s ease;
}
@keyframes bkdrop {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}
.bkhd { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.bkhd b { font-size: 13.5px; }
.bkchip {
  padding: 2px 7px;
  border-radius: 9px;
  background: #ffe873;
  font-size: 10.5px;
  font-weight: 700;
  color: #6b5200;
}
.bkchip.dim { background: #e8eaee; color: #6b6f77; }
.bkkv { margin: 0; }
.bkkv > div {
  display: flex;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid #eceef1;
}
.bkkv > div:first-child { border-top: 0; }
.bkkv dt { flex: none; width: 42px; color: var(--sub); }
.bkkv dd { margin: 0; flex: 1; min-width: 0; font-weight: 600; }
.bkkv dd .cond { display: block; margin-top: 2px; font-weight: 400; line-height: 1.5; }
.bkreq { margin-top: 8px; padding-top: 8px; border-top: 1px solid #eceef1; }
.bkreq > b { display: block; margin-bottom: 4px; color: var(--sub); font-weight: 400; }
.bkreq p { display: flex; gap: 6px; margin: 0 0 4px; font-weight: 600; flex-wrap: wrap; }
.bkreq i { font-style: normal; }
.bkreq .cond { flex-basis: 100%; margin-left: 20px; font-weight: 400; }
.bkacts { display: flex; gap: 6px; margin: 0 0 8px; }
.bkacts .btn { flex: 1; padding: 9px; font-size: 12px; text-align: center; }
.bkfoot { margin: 8px 0 0; font-size: 11px; line-height: 1.55; color: var(--sub); }

/* 대화창. **밤 모드가 아니다** — 카톡과 같은 하늘빛이다.
   `container-type` 은 사진 묶음이 「대화창 폭의 55%」를 잴 수 있게 두는 것이다
   (→ `.pgrid` 의 `55cqw`). 폰이냐 PC 기둥이냐로 대화창 폭이 갈리는데, 사진 크기를
   화소로 박으면 한쪽에서 반드시 어긋난다. */
.kroom {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 12px 12px;
  background: #b2c7da;
  container-type: inline-size;
}
.daybar { text-align: center; margin: 6px 0 12px; }
.daybar span {
  background: rgba(0, 0, 0, 0.16);
  color: #fff;
  font-size: 10.5px;
  padding: 4px 12px;
  border-radius: 12px;
}
/* 시스템 줄 — 말풍선이 아니다. 사람이 한 말이 아니라는 것이 꼴에서 보여야 한다. */
.ksys { text-align: center; margin: 8px 0; }
.ksys span {
  display: inline-block;
  max-width: 92%;
  background: rgba(255, 255, 255, 0.7);
  color: #3c4a5a;
  font-size: 10.5px;
  line-height: 1.5;
  padding: 5px 10px;
  border-radius: 12px;
}

.kmsg { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 6px; }
/* 내 말은 오른쪽. **`.me` 라고 부르지 않는다** — 지도의 「내 위치」 표식이
   전역 `.me { transform: translate(-50%,-50%) }` 를 물고 있어서, 그 이름을 쓰면
   말풍선 줄이 통째로 왼쪽으로 반 폭 밀린다(앞판이 실제로 그렇게 밀려 있었다). */
.kmsg.mine { flex-direction: row-reverse; }
.kmsg .cavwrap { flex: none; width: 34px; }
/* 말풍선·이름·공감이 세로로 쌓인다. **가로 정렬을 여기서 잡는다** — 내 쪽은
   오른쪽 끝에 맞아야 공감 표가 말풍선 꼬리 밑에 붙는다. */
.kmsg .stack { display: flex; flex-direction: column; align-items: flex-start; }
.kmsg.mine .stack { align-items: flex-end; }
.kmsg.mine .cavwrap { display: none; }
.kmsg .stack { min-width: 0; max-width: calc(100% - 46px); }
.kmsg .kwho { display: block; margin-bottom: 4px; font-size: 11.5px; color: #33414f; }
.kmsg .meta {
  flex: none;
  align-self: flex-end;
  padding-bottom: 2px;
  font-size: 10px;
  line-height: 1.35;
  color: #4f6070;
  text-align: right;
}
.kmsg.mine .meta { text-align: left; }
/* 「1」은 상대가 아직 안 읽었다는 뜻. 읽으면 사라진다. 카톡처럼 노란빛이다. */
.kmsg .meta i { display: block; font-style: normal; font-weight: 700; color: #d9a400; }

/* 찾기에 걸린 줄만 남긴다 — 나머지는 흐리게. 지우지 않는 이유는 앞뒤 맥락이
   함께 보여야 그 말이 무슨 말이었는지 알 수 있어서다. */
.kmsg.dimmed { opacity: 0.32; }
.kmsg.hit .kbub, .kmsg.hit .kann { box-shadow: 0 0 0 2.5px #ffd900; }

.kbub {
  position: relative;
  display: inline-block;
  max-width: 100%;
  padding: 8px 11px;
  border-radius: 15px;
  background: #fff;
  font-size: 13.5px;
  line-height: 1.45;
  word-break: break-word;
}
/* 꼬리. 묶음의 **첫 줄에만** 붙는다 — 줄마다 붙이면 말풍선이 톱니처럼 보인다. */
.kbub.head::before {
  content: "";
  position: absolute;
  top: 9px;
  left: -5px;
  border-style: solid;
  border-width: 5px 6px 5px 0;
  border-color: transparent #fff transparent transparent;
}
.kbub.mine { background: #fee500; color: #111; }
.kbub.mine.head::before {
  left: auto;
  right: -5px;
  border-width: 5px 0 5px 6px;
  border-color: transparent transparent transparent #fee500;
}
.kbub a { color: #1a5fa8; text-decoration: underline; word-break: break-all; }
.kbub.mine a { color: #7a5c00; }
.kbub.blocked {
  background: #ffeded;
  border: 1px solid #f3c2c2;
  color: #8a1f22;
}
.kbub.blocked span { font-size: 10.5px; color: #a54a4d; }
.kbub .cond { font-size: 10.5px; }
.sticker { font-size: 44px; line-height: 1.1; }

/* 링크 미리보기 — 카톡이 주소 밑에 세우는 그 카드다.
   우리 주소면 카톡 카드와 **같은 그림·같은 글**을 쓴다(index.html 의 og 태그). */
/* 앞의 `.kbub a` 보다 **무게가 무거워야** 한다 — 가벼우면 카드 한 장이 통째로
   파란 밑줄 글씨가 된다(제목·설명·주소까지 전부 그랬다). */
.kbub .klink {
  display: block;
  margin-top: 7px;
  border-radius: 10px;
  overflow: hidden;
  background: #f4f5f7;
  color: inherit;
  text-decoration: none;
}
.kbub.mine .klink { background: rgba(0, 0, 0, 0.07); }
.klink img { display: block; width: 100%; aspect-ratio: 1200 / 630; object-fit: cover; }
.klink .t { display: block; padding: 8px 10px; }
.klink b { display: block; font-size: 12px; }
.klink .s {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.45;
  color: #5c5f66;
}
.klink .h { display: block; margin-top: 5px; font-size: 10.5px; color: #8b8d94; }

/* 담당자 공지(23) — 왼쪽에 골드 띠가 선 흰 카드.
   **몇 명에게 갔는지만** 적는다. 누구에게 갔는지는 회원끼리 보이지 않는다. */
.kann {
  display: block;
  max-width: 100%;
  padding: 10px 12px;
  border-radius: 14px;
  border-left: 3px solid var(--y);
  background: #fff;
}
.kann b { display: block; font-size: 12.5px; margin-bottom: 3px; }
.kann .s { display: block; font-size: 13px; line-height: 1.45; color: #3c4a5a; }
.kann .cond { display: block; margin-top: 6px; font-size: 10px; }
.kann .pgrid { margin-top: 8px; }

/* 사진 묶음. **보낸 것은 다 보인다**(2026-09-07 지시) — 「+N」으로 접지 않는다.
   한 통에 열 장까지라 다 펴도 세 칸 넉 줄이다.

   **가로는 대화창의 55%**(같은 지시). 화소로 박아 두면(전에는 200px) 폰에서는 크고
   PC 기둥에서는 작다 — 대화창 폭을 기준으로 재야 어디서나 같은 크기로 읽힌다.
   `cqw` 는 `.kroom` 안쪽 폭이다(→ `.kroom` 의 `container-type: inline-size`).
   컨테이너 질의를 모르는 옛 브라우저에는 **앞 줄의 200px 이 그대로 남는다.**

   **두 장부터는 빈틈 없는 네모 한 덩어리다**(2026-09-07 지시). 칸을 셋으로 고정하면
   10장이 3·3·3·1 이 되어 마지막 줄에 구멍이 남는다 — 그래서 줄마다 장수를 다르게
   나누고(chat.js 의 `photoRows`), 줄 높이는 다 같게 둔다(`flex: 1`).
   덩어리에 `aspect-ratio: 1` 을 주면 **홀수로 올려도 네모가 꽉 찬다.** */
.pgrid {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 200px;
  width: 55cqw;
  max-width: 100%;
  aspect-ratio: 1;
  border-radius: 12px;
  overflow: hidden;
}
/* 한 줄. 줄 안에서는 폭을 똑같이 나눈다. */
.pgrid .prow { display: flex; flex: 1; min-height: 0; gap: 2px; }
.pgrid .prow > button { flex: 1; min-width: 0; }
/* 공지 카드 안의 사진은 카드 폭을 그대로 쓴다 — 카드가 이미 좁다. */
.pgrid.wide { width: 100%; }
.pgrid button {
  position: relative;
  padding: 0;
  border: 0;
  background: var(--night2, #1a1a1f);
  cursor: pointer;
}
.pgrid img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 한 장이면 **자르지 않는다** — 그 사진의 비율이 곧 그 사진이다.
   세로 사진을 네모로 자르면 얼굴이 날아간다. 그래서 네모 규칙에서 빠진다. */
.pgrid.one { display: block; aspect-ratio: auto; }
.pgrid.one img { height: auto; }

/* 21 — 막힌 말 뒤의 두 단추. 신고가 먼저 오고, 그 옆이 「괜찮아요」다. */
.kact { display: flex; gap: 8px; padding: 0 20px; margin: 0 0 10px; }
.kact .btn { flex: 1; padding: 10px; font-size: 12px; }

/* 입력줄 — 화면 맨 아래, **하늘빛 위에 뜬 흰 알약 하나**다(2026-09-07 지시, 카톡 그대로).
   흰 띠를 깔지 않는다 — 띠를 깔면 방이 거기서 잘려 화면이 두 층으로 읽힌다.

   그 위에 있던 「빠른 답장」 줄은 걷었다(같은 지시). 자주 묻는 말은 어차피 회원마다
   다르고, 늘 떠 있는 알약 넉 줄이 방을 그만큼 잡아먹었다. */
.kinp {
  flex: none;
  padding: 6px 10px calc(8px + var(--safe-b));
  background: transparent;
}
.kinp .kpill {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 8px;
  border-radius: 24px;
  background: #fff;
}
.kinp .plus, .kinp .emo, .kinp .hash, .kinp .send {
  flex: none;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 17px;
  background: none;
  color: #3f4147;
  cursor: pointer;
}
.kinp .plus svg, .kinp .emo svg, .kinp .hash svg, .kinp .send svg { width: 21px; height: 21px; }
/* 메뉴가 열리면 + 가 × 로 돌아간다(시안 22). */
.kinp .plus.open { transform: rotate(45deg); color: #111; }
/* 이모티콘만 회색 동그라미를 입는다 — 카톡이 그 한 칸만 그렇게 둔다. */
.kinp .emo { background: #eceef1; }
.kinp .emo.open { background: #dfe2e6; color: #111; }
.kinp .send { background: #fee500; color: #111; }
/* **글자가 있을 때만 보내기가 선다.** 빈 칸에서는 그 자리에 「#」(찾기)가 있다. */
.kinp .send { display: none; }
.kinp.has .send { display: flex; }
.kinp.has .hash { display: none; }
/* 글 쓰는 칸. **`<input>` 이 아니라 `contenteditable` 이다** — 폰에서 `<input>` 을
   누르면 삼성 키보드가 자동입력 줄(열쇠·카드·주소)을 한 층 얹었다. 까닭은 chat.js
   같은 자리 주석에. 그래서 안내 글자도 `::placeholder` 가 아니라 직접 그린다. */
.kinp .tin {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  padding: 9px 4px;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink);
  /* 긴 말은 `<input>` 처럼 옆으로 흐르지 않고 접힌다(카톡과 같다). 다만 알약이
     화면을 다 먹지 않게 넉 줄에서 멈추고, 그 뒤로는 이 칸만 굴린다. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 78px;
  overflow-y: auto;
}
.kinp .tin:empty::before {
  content: attr(data-ph);
  color: #a6a9b0;
}
.kinp .tin:focus { outline: none; }

/* + 메뉴(22). **세 칸뿐이다**(2026-09-07 지시) — 앨범·카메라·내 위치.
   꺼진 넷(동영상·파일·연락처·음성)과 이모티콘 칸은 걷었다(이모티콘은 입력줄 ☺ 자리에 있다).
   칸이 셋이어도 **자리는 네 칸 그대로** 잡는다 — 카톡처럼 왼쪽부터 같은 크기로 선다. */
.kmenu {
  flex: none;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  padding: 14px 14px calc(14px + var(--safe-b));
  background: #fff;
}
.kmenu button {
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 10.5px;
  color: #444;
  cursor: pointer;
}
.kmenu button i {
  display: block;
  width: 46px;
  height: 46px;
  margin: 0 auto 5px;
  border-radius: 15px;
  background: #f2f3f5;
  font-size: 20px;
  font-style: normal;
  line-height: 46px;
}
.kmenu button span { display: block; font-size: 9px; color: var(--sub); }
.kmenu .note {
  grid-column: 1 / -1;
  margin: 4px 0 0;
  padding: 10px;
  border-radius: 12px;
  background: var(--soft);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--chip-ink, #44464c);
}

/* 이모티콘 판. 그림 이모티콘은 아직 없어서 글자 이모지로 낸다. */
.kkemo {
  flex: none;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
  padding: 10px 12px calc(10px + var(--safe-b));
  background: #fff;
}
.kkemo button {
  padding: 6px 0;
  border: 0;
  border-radius: 10px;
  background: none;
  font-size: 26px;
  line-height: 1.1;
  cursor: pointer;
}
.kkemo button:hover { background: #f4f4f6; }

/* 내 말이 필터에 걸렸을 때. **쓴 글은 입력칸에 그대로 남는다** — 고쳐 보낼 수 있게. */
.sendblock {
  flex: none;
  margin: 0 10px 6px;
  padding: 10px 12px;
  border-radius: 12px;
  background: #ffeded;
  border: 1.5px solid #f3c2c2;
  font-size: 11.5px;
  line-height: 1.6;
  color: #7a2a2c;
}
.sendblock b { display: block; color: #c2262a; }

/* 닫힌 방·알림 방의 아래 띠. 입력칸이 서던 자리에 **왜 없는지**가 선다. */
.kkclosed {
  flex: none;
  padding: 12px 16px calc(12px + var(--safe-b));
  background: #fff;
  border-top: 1px solid #e8e8ea;
}
.kkclosed b { display: block; font-size: 13px; margin-bottom: 3px; }
.kkclosed span { display: block; font-size: 11.5px; line-height: 1.6; color: var(--sub); }

/* — 덮개 · 서랍 · 아래 창 · 줄 메뉴 — 카톡의 그 자리들 — */
.kkdim { position: absolute; inset: 0; z-index: 3; background: rgba(0, 0, 0, 0.42); }

.kkside {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  width: min(300px, 86%);
  padding: calc(env(safe-area-inset-top, 0px) + 14px) 16px calc(16px + var(--safe-b));
  background: #fff;
  overflow-y: auto;
  box-shadow: -8px 0 28px rgba(0, 0, 0, 0.2);
}
.kkside .hd { display: flex; align-items: center; gap: 10px; }
.kkside .hd div { flex: 1; min-width: 0; }
.kkside .hd b { display: block; font-size: 15px; }
.kkside .hd span { display: block; font-size: 11px; color: var(--sub); }
.kkside .bk, .kkside .shots { margin-top: 14px; }
.kkside .bk b, .kkside .shots b { display: block; font-size: 12px; color: var(--sub); }
.kkside .bk span { display: block; margin-top: 3px; font-size: 13px; font-weight: 600; }
.kkside .shots div { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin-top: 6px; }
.kkside .shots button { padding: 0; border: 0; background: none; aspect-ratio: 1; cursor: pointer; }
.kkside .shots img { width: 100%; height: 100%; border-radius: 8px; object-fit: cover; }
.kkside .menu { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 8px; }
.kkside .menu button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 11px 4px;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 13.5px;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.kkside .menu button svg { width: 19px; height: 19px; }
.kkside .rule {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 11px;
  line-height: 1.7;
  color: var(--sub);
}
/* **첫 줄만** 제목이다. `b` 를 통째로 block 으로 두면 문장 한가운데의 「전에」가
   제 줄로 튀어 나가 규칙이 세 토막으로 읽힌다. */
.kkside .rule .h { display: block; color: var(--ink); }

.kksheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  padding: 10px 18px calc(18px + var(--safe-b));
  background: #fff;
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.18);
}
.kksheet .grip {
  display: block;
  width: 38px;
  height: 4px;
  margin: 0 auto 12px;
  border-radius: 2px;
  background: #dcdce0;
}
.kksheet b { font-size: 15px; }
.kksheet p { margin: 6px 0 14px; font-size: 12.5px; line-height: 1.75; color: #4a4c53; }
.kksheet .btn { width: 100%; margin-bottom: 8px; }

/* 공감 표 — 말풍선 아래 귀퉁이에 살짝 걸쳐 앉는다(카톡과 같은 자리). */
.kreact {
  margin-top: -7px;
  margin-right: 8px;
  padding: 1px 7px;
  border-radius: 11px;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  font-size: 12px;
  line-height: 1.5;
}
.kmsg:not(.mine) .kreact { margin-right: 0; margin-left: 8px; }

/* 공감 고르는 줄. 말풍선을 길게 누르면 뜬다. */
.kkreact {
  position: absolute;
  left: 50%;
  bottom: 90px;
  transform: translateX(-50%);
  z-index: 4;
  display: flex;
  gap: 2px;
  padding: 6px;
  background: #fff;
  border-radius: 24px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
.kkreact button {
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 20px;
  background: none;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.kkreact button:hover { background: #f2f3f5; }
.kkreact .off { width: auto; padding: 0 12px; font-size: 12px; color: var(--sub); }

.kkpop {
  position: absolute;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 4;
  width: min(320px, 90%);
  padding: 14px;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.22);
}
.kkpop > b { display: block; margin-bottom: 8px; font-size: 13.5px; }
.kkpop button {
  display: block;
  width: 100%;
  margin-bottom: 6px;
  padding: 11px 10px;
  border: 0;
  border-radius: 10px;
  background: #f7f7f8;
  font-family: inherit;
  font-size: 13px;
  color: #1a1a1c;
  text-align: left;
  cursor: pointer;
}
.kkpop p { margin: 4px 0 0; font-size: 11px; line-height: 1.6; color: var(--sub); }
/* 줄 메뉴·방 서랍의 「나가기」. 되돌릴 수 없는 칸이라 빨간 글씨다. */
.kkpop button.out, .kkside .menu button.out { color: var(--danger-ink); font-weight: 600; }

/* 「채팅방을 나가시겠어요?」 — 눌러야 사라지는 **가운데 창**이다.
   이 웹은 화면 아래 띠를 안 쓴다(2026-09-05 지시) — 되돌릴 수 없는 일은 그 자리에서
   묻고, 답을 받기 전에는 아무것도 지우지 않는다. */
.kkask {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  width: min(300px, 84%);
  padding: 20px 18px 14px;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.26);
  text-align: center;
}
.kkask b { display: block; font-size: 15px; letter-spacing: -0.2px; }
.kkask p { margin: 8px 0 16px; font-size: 12.5px; line-height: 1.6; color: var(--sub); }
.kkask .row { display: flex; gap: 8px; }
.kkask .row button {
  flex: 1;
  padding: 11px 0;
  border: 0;
  border-radius: 10px;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}
.kkask .row .no { background: #f2f3f5; color: #4a4c53; }
.kkask .row .yes { background: var(--danger-ink); color: #fff; }

/* PC — **예약 화면과 같은 왼쪽 기둥**이다(2026-09-07 지시). 자리도 폭도 같고,
   다른 것은 속이 카톡 꼴이라는 것뿐이다. 지도는 오른쪽에 그대로 남는다.
   (한때 화면을 통째로 덮게 뒀는데, 채팅을 열면 보던 지도가 사라졌다.) */
@media (min-width: 900px) {
  .page.kakao {
    inset: var(--roll) auto 0 0;
    width: var(--sidew);
    background: #fff;
  }
  .page.kakao .kk { max-width: none; margin: 0; box-shadow: none; }
  /* 머리줄 위 여백은 폰의 노치 몫이었다 — PC 에서는 그냥 빈 자리로 남는다. */
  .page.kakao .kkbar { padding-top: 12px; }
}
/* 사진 크게 보기. */
#kphoto {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 20px;
  background: rgba(0, 0, 0, 0.92);
}
#kphoto img { max-width: 100%; max-height: 78vh; border-radius: 12px; }
#kphoto p { margin: 0; color: #c8cad1; font-size: 11.5px; }
#kphoto .x {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 15px;
  cursor: pointer;
}
/* 여러 장일 때만 붙는 셋 — 왼쪽 위 「2 / 6장」과 좌우 넘김 단추.
   자리는 사진이 아니라 **화면**에 잡는다(사진마다 크기가 달라, 사진에 붙이면
   넘길 때마다 단추가 들썩여 연달아 누르던 손가락이 헛짚는다). */
#kphoto .count {
  position: absolute;
  top: 14px;
  left: 14px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.2px;
}
#kphoto .nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  border: 0;
  border-radius: 24px;
  background: rgba(255, 255, 255, 0.22);
  color: #fff;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
}
#kphoto .nav.prev { left: 12px; }
#kphoto .nav.next { right: 12px; }
/* 끝에서는 지우지 않고 흐리게 죽인다 — 자리가 그대로 남아야 손이 안 헛짚는다. */
#kphoto .nav:disabled { opacity: 0.25; cursor: default; }
@media (hover: hover) {
  #kphoto .nav:not(:disabled):hover { background: rgba(255, 255, 255, 0.36); }
}

/* 카메라(+ 메뉴의 「카메라」). 화면을 통째로 덮고 아래에 셔터가 선다.
   **큰 사진 보기(`#kphoto`)와 같은 층**이다 — 둘이 같이 뜰 일은 없다. */
#kcam {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #000;
}
#kcam video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
#kcam .bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px calc(28px + var(--safe-b));
  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55));
}
#kcam .bar button {
  border: 0;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 16px;
  cursor: pointer;
}
#kcam .bar .x, #kcam .bar .flip {
  width: 44px;
  height: 44px;
  border-radius: 22px;
}
/* 셔터 — 카메라 앱의 그 흰 동그라미. 글자가 없어서 크기가 곧 이름이다.
   **`#kcam .shot` 으로 쓰면 진다** — 위의 `#kcam .bar button` 이 더 무거워서(요소 하나 더)
   셔터가 검은 화면 위에 검은 동그라미로 뜬다. 실제로 그렇게 나왔다. */
#kcam .bar .shot {
  width: 68px;
  height: 68px;
  border-radius: 34px;
  background: #fff;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.35);
}

/* 위치 말풍선의 「지도에서 열기」. `.kbub a` 보다 무게가 무거워야 파란 밑줄을 이긴다. */
.kbub .mapgo {
  display: inline-block;
  margin-top: 6px;
  padding: 5px 10px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.07);
  color: inherit;
  font-size: 11.5px;
  text-decoration: none;
}
.kbub.mine .mapgo { background: rgba(0, 0, 0, 0.1); }

/* 진행 막대(예약 이행). **CSS 가 아예 없어서 빈 칸으로 그려지고 있었다** —
   `tabs.js` 는 `.bar2 > i` 를 폭 %로 그리는데 받는 규칙이 없어 높이 0 이었다.
   예약 화면을 채우면서 같이 잡았다. */
.bar2 {
  height: 6px;
  border-radius: 3px;
  background: var(--dim-bg);
  overflow: hidden;
}
.bar2 i { display: block; height: 100%; background: var(--y); border-radius: 3px; }

/* ── 「앱으로 보기」 창 (폰에서만, app-invite.js) ──
 *
 * 화면 **가운데**에 선다. 아래 띠가 아니다 — 이 저장소는 하단 알림을 전부 걷어냈고
 * 되돌리지 않는다. 이건 눌러야 사라지는 창이라 업데이트 창과 같은 갈래다.
 *
 * z 는 가입 창(60)보다 **아래**다. 둘이 같이 뜰 일은 없지만, 겹치면 사람이 방금 누른
 * 가입 창이 이겨야 한다. */
.invite {
  position: fixed;
  inset: 0;
  z-index: 58;
  background: rgba(0, 0, 0, 0.58);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.invite .sheet {
  background: var(--card);
  width: 100%;
  max-width: 340px;
  border-radius: 20px;
  padding: 24px 20px calc(20px + var(--safe-b));
  position: relative;
  max-height: 88vh;
  overflow-y: auto;
  text-align: center;
}
.invite .x {
  position: absolute;
  right: 8px; top: 8px;
  font-size: 16px;
  color: var(--sub);
  padding: 8px 11px;
}
/* 로고 자리. 아이콘 파일이 아직 없어서 글자로 세운다 — 없는 그림을 부르면
   깨진 이미지 상자가 남는다. */
.invite .mark {
  width: 58px; height: 58px;
  margin: 2px auto 12px;
  border-radius: 16px;
  background: var(--night);
  color: var(--y);
  display: grid;
  place-items: center;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -1px;
}
.invite h2 { font-size: 18px; margin: 0 0 6px; letter-spacing: -0.5px; }
.invite .lead { margin: 0 0 14px; font-size: 12.5px; color: var(--sub); line-height: 1.55; }
.invite .pts {
  list-style: none;
  margin: 0 0 16px;
  padding: 12px 12px;
  background: var(--soft);
  border-radius: 14px;
  text-align: left;
}
.invite .pts li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 12.5px;
  line-height: 1.5;
  letter-spacing: -0.2px;
}
.invite .pts li + li { margin-top: 8px; }
/* 점 하나. 글머리 기호를 글자로 넣으면 줄바꿈에서 본문과 같이 흐른다. */
.invite .pts li i {
  flex: none;
  width: 5px; height: 5px;
  margin-top: 7px;
  border-radius: 50%;
  background: var(--y);
}
.invite .btn + .btn { margin-top: 8px; }
.invite a.btn { display: block; }
/* 앱 안의 브라우저(mode 'browser')에서만 나오는 잔글씨.
   「크롬에서 열기」가 막히는 폰이 있어서 복사 쪽 길을 늘 같이 세워 둔다. */
.invite .fine {
  margin: 10px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--sub);
  letter-spacing: -0.2px;
}
/* 복사까지 막히면 단추 글자가 주소로 바뀐다 — 길어도 잘리면 안 된다. */
.invite .copy { word-break: break-all; }
.invite .copy.done { color: var(--ink); border-color: var(--y); }

/* ── 상단 우측 「로그인」 ──
 *
 * 2026-09-05 지시: **폰에서 로그인 자리가 안 보인다.** 실제로는 있었다 —
 * 회색 테두리 동그라미 안 사람 실루엣 하나였고, 라이브 폰 화면에서 자리도 크기도
 * 멀쩡했다(x 338 · 38×38 · 눌림도 됐다). 안 보였던 것이 아니라
 * **「로그인」으로 안 읽혔다.** 아이콘만으로는 그게 문인지 그림인지 모른다.
 *
 * 그래서 **로그인 전에는 글자로 말한다.** 들어온 뒤에는 아이콘으로 돌아간다 —
 * 그때는 「내 계정」이라는 뜻이 이미 통하고, 좁은 상단바에서 검색이 자리를 되찾는다.
 * 판정은 `paintAcct()` 한 곳이다(app.js). */
.topbar .me-btn.signin {
  width: auto;
  height: 38px;
  border-radius: 12px;
  border: 0;
  background: var(--y);
  color: #111;
  padding: 0 15px;
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: -0.3px;
  white-space: nowrap;
}
/* 글자로 말하는 동안 실루엣은 지운다. 둘 다 두면 「사람 아이콘 로그인」이 된다. */
.topbar .me-btn.signin svg { display: none; }
/* 반대로 회원일 땐 글자를 지운다 — 아이콘 동그라미가 글자에 밀려 찌그러진다. */
.topbar .me-btn:not(.signin) .lbl { display: none; }

/* **상단바가 좁은 폰에서 넘치고 있었다.** 재 보니 360px 에서 24px, 320px 에서 41px 이
   화면 밖이었다 — 오른쪽 끝이 잘리는 것이 바로 그 로그인 자리다.

   .app 이 position:fixed 라 넘친 만큼이 **조용히 잘린다.** 가로 스크롤바도 안 생기고
   body.scrollWidth 도 안 늘어서, 화면 폭으로 재는 검사는 전부 통과한다.
   잡히는 자리는 .topbar 의 scrollWidth - clientWidth 하나뿐이다.

   원인은 검색창이 **안 줄어든다**는 것. flex:1 만 주고 min-width 를 안 잡으면
   기본값이 auto(=내용 폭)라 238px 밑으로 안 내려간다. 안쪽 input 에는 이미
   min-width:0 이 있는데, **줄어들어야 하는 것은 그 바깥 상자**다.

   알약을 넣기 전에도 12px 넘치고 있었다(38px 아이콘일 때). 내 알약이 그걸 키워
   드러냈을 뿐이라, 알약을 좁히는 것으로 덮지 않고 넘침 자체를 고친다. */
.topbar .search { min-width: 0; }

/* 아주 좁은 폰(320px)에서도 검색이 살아 있어야 한다. 알약이 조금 줄어드는 편이
   검색창이 사라지는 것보다 낫다. */
@media (max-width: 359px) {
  .topbar { gap: 8px; padding: 0 10px; }
  .topbar .me-btn.signin { padding: 0 11px; font-size: 12.5px; }
}

/* ── 상단 「앱 설치」 아이콘 단추 (로그인 단추 왼편) ──
 *
 * 2026-09-07 지시. 계정 메뉴 안에 있던 「앱 설치」 줄을 여기로 꺼냈다 — 메뉴를
 * 열어야만 보이는 자리라 앱을 아직 안 깐 사람에게는 없는 것과 같았다.
 *
 * **앱 아이콘 꼴이다** — 정사각형에 모서리만 둥글다. 반지름 10px 는 38px 의 26% 로,
 * 홈 화면 아이콘이 깎이는 곡률과 같은 자리다(옆 검색·계정의 12px 와도 한 식구다).
 * 그림은 여백까지 포함한 정사각이라 `object-fit` 없이 그대로 채운다.
 *
 * ⚠ **폭이 늘어난다.** 이 상단바는 좁은 폰에서 조용히 넘쳤던 자리다
 *   (`.topbar` 의 scrollWidth − clientWidth 로만 잡힌다). 그래서 이 단추가 서면
 *   검색창이 그만큼 줄어야 하는데, 위 `.topbar .search { min-width: 0 }` 이 이미
 *   그 길을 열어 뒀다. 320px 에서까지 재고 넣은 값이다.
 *
 * ⚠ **뜰지 말지는 CSS 가 안 정한다.** `hidden` 을 걷는 것은 app-invite.js 뿐이고,
 *   판정은 기기·설치 여부다 — 화면 폭으로 가르면 창을 좁힌 PC 에 뜬다. */
.topbar .app-install {
  width: 38px; height: 38px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 10px;
  overflow: hidden;
  background: #0E0E11;
  cursor: pointer;
  line-height: 0;
}
.topbar .app-install img { width: 100%; height: 100%; display: block; }
/* 손가락이 아니라 키보드로 왔을 때만 테두리를 두른다. 늘 두르면 아이콘 위에
   검은 테가 얹혀 흠집처럼 보인다. */
.topbar .app-install:focus-visible { outline: 2px solid var(--y); outline-offset: 2px; }
@media (max-width: 359px) {
  /* 320px 에서는 검색창이 먼저 죽는다. 아이콘을 2px 줄여 그만큼 돌려준다. */
  .topbar .app-install { width: 36px; height: 36px; }
}

/* ── 마이페이지(`my.html`) ──
 *
 * **지도 화면이 아니다.** 여기서는 `.app`(position:fixed)을 쓰지 않는다 — 그 상자에
 * 넣으면 화면 높이에 갇혀서, 카드가 길어질수록 안쪽에 스크롤이 하나 더 생긴다.
 * 이 화면은 문서처럼 그냥 본문이 굴러가는 한 장이다.
 *
 * 폰과 PC 가 같은 코드다. 갈리는 곳은 아래 @media 한 군데뿐이다 —
 * 폰은 한 줄로 쌓이고, PC 는 나(왼쪽)와 지갑(오른쪽) 두 기둥이 된다. */

body.mypg { background: var(--bg); min-height: 100%; }

/* 머리줄 — 돌아가는 문과 제목. **붙박이(sticky)로 둔다** — 카드가 길어서 한참
   내려가면 돌아갈 길이 화면 밖으로 사라진다. */
.mypg-head {
  position: sticky;
  top: 0;
  z-index: 10;
  height: 56px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
}
.mypg-head .back {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 12px;
  color: var(--ink);
  flex: none;
}
.mypg-head .back:hover { background: var(--soft); }
.mypg-head .back svg { width: 20px; height: 20px; }
.mypg-head h1 { margin: 0; font-size: 16px; font-weight: 700; letter-spacing: -0.4px; }

.mypg-body { max-width: 1100px; margin: 0 auto; padding: 16px 14px 40px; }
.mypg-body h2 { margin: 2px 0 12px; font-size: 20px; letter-spacing: -0.5px; }
.mypg-grid { display: grid; gap: 4px; }

/* 이름·메일·등급. 이름은 길고 메일은 더 길다 — 넘치면 **줄을 바꾼다.**
   한 줄로 밀어 넣으면 좁은 폰에서 메일이 잘려 누구 계정인지 못 읽는다. */
.mypg-who { display: flex; align-items: center; gap: 12px; }
.mypg-who .face {
  width: 52px; height: 52px;
  flex: none;
  border-radius: 18px;
  display: grid; place-items: center;
  background: var(--night);
  color: var(--y);
  font-size: 20px; font-weight: 800;
}
.mypg-who .txt { min-width: 0; flex: 1; }
.mypg-who .nm { display: block; font-size: 16px; }
.mypg-who .ml { display: block; font-size: 12px; color: var(--sub); overflow-wrap: anywhere; }

/* 가입 안내는 가운데 한 장이다. 두 기둥으로 벌려 놓을 내용이 없다. */
.mypg-join { max-width: 420px; margin: 24px auto 0; }

@media (min-width: 900px) {
  .mypg-head { height: 60px; padding: 0 16px; }
  .mypg-body { padding: 24px 20px 60px; }
  /* 두 기둥은 **위를 맞춘다**(align-items:start). 안 맞추면 짧은 쪽 카드가
     긴 쪽 높이만큼 늘어나 흰 판 아래가 텅 빈다. */
  .mypg-grid { grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
}

/* 로그아웃 — 두 기둥 **다음**에 온다. PC 에서 화면 폭을 다 쓰는 단추는 「끝났다」가
   아니라 「여기를 누르라」로 읽혀서, 왼쪽 기둥 폭까지만 늘린다. */
.mypg-out { margin-top: 6px; }
@media (min-width: 900px) {
  .mypg-out { max-width: calc(50% - 10px); }
}

/* ── 동의 내역 ──
   언제 무엇에 동의했는지 · 선택 항목 끄기 · 탈퇴. **탈퇴 단추를 눈에 띄게 만들지 않는다** —
   되돌릴 수 없는 문이라 찾는 사람만 찾으면 된다. 대신 숨기지도 않는다. */
.mypg-consent { margin-top: 18px; }
.cs-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 44px; border-bottom: 1px solid var(--line); font-size: 13px; line-height: 1.5;
}
.cs-row:last-of-type { border-bottom: 0; }
.cs-at { color: var(--sub); font-size: 12px; white-space: nowrap; }
.cs-req, .cs-opt {
  font-size: 11px; padding: 1px 6px; border-radius: 999px; margin-left: 4px; font-weight: 700;
}
.cs-req { background: rgba(255,255,255,.08); color: var(--sub); }
.cs-opt { background: rgba(245,197,66,.14); color: var(--y); }
.cs-tog { cursor: pointer; }
.cs-tog input { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--y); }
.cs-quit { margin-top: 10px; }
@media (min-width: 900px) { .cs-quit { max-width: 220px; } }

/* 전자상거래법 사업자 표기. 화면 맨 아래, 조용히. */
.mypg-biz {
  margin: 26px 0 0; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 11px; line-height: 1.9; color: var(--sub); text-align: center;
}
.mypg-biz a { color: var(--sub); text-decoration: underline; }

/* ── 「앱 설치」 창의 아이폰 안내 ──
 *
 * 아이폰에는 설치 API 가 없다. 사파리 「공유 → 홈 화면에 추가」를 사람이 직접 밟아야
 * 해서, 글로 «공유 단추»라고만 하면 어느 것인지 모른다 — **화면에 있는 그 모양을
 * 그대로 그려 준다.** */
.invite .steps {
  list-style: none;
  counter-reset: s;
  margin: 0 0 16px;
  padding: 12px;
  background: var(--soft);
  border-radius: 14px;
  text-align: left;
}
.invite .steps li {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12.5px;
  line-height: 1.5;
  letter-spacing: -0.2px;
  color: var(--sub);
}
.invite .steps li + li { margin-top: 10px; }
.invite .steps li b { color: var(--ink); font-weight: 700; }
.invite .steps .ic {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--card);
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  color: #0a84ff;   /* 사파리 아이콘 색 그대로 — 화면에서 찾을 것과 같아야 한다 */
}
.invite .steps .ic svg { width: 17px; height: 17px; }

/* ── 지도 좌하단 날씨 ─────────────────────────────────────────────────
 *
 * **파마 웹 날씨 위젯의 치수를 그대로 옮긴 것이다**(원본은 Flutter,
 * `fama_web/apps/web/lib/features/weather/weather_panel.dart`·`weather_overlay.dart`).
 * 값 옆 괄호는 그 파일의 줄 번호다 — 파마가 고치면 여기도 같이 고친다.
 *
 * 그래서 이 구역만 `--y`·`--ink` 같은 24K 토큰을 안 쓴다. 파마의 색을 그대로 쓴다 —
 * 토큰으로 바꿔 적으면 「파마와 같은 디자인」이 그 순간 아니게 된다.
 *
 * Flutter 의 `elevation` 은 CSS 그림자로 옮겼다(머티리얼 표준값 그대로):
 *   elevation 2 = 칩 · 6 = PC 상세 카드 · 8 = 폰 상세 창.
 */

.wx {
  position: absolute;
  left: 12px;
  /* 파마는 `bottom: 12` 다(weather_overlay.dart L45-47). **여기만 24 로 올렸다.**
   *
   * 카카오가 좌하단 바닥에 눈금(`1km`)과 제 로고를 심는데, 그 띠의 윗변이 바닥에서
   * **19px** 이다(라이브 화면에서 실측: 눈금 x 6~135 · 로고 x 99~131, 둘 다 top 19).
   * 12 로 두면 칩이 그 위에 얹혀 **카카오 로고를 덮는다** — 지도 제공자 표기를 가리는
   * 것은 취향 문제가 아니라 이 지도를 쓸 수 있느냐의 문제다. 파마는 네이버 지도라
   * 그 자리가 다르다. 왼쪽 12 는 그대로다. */
  bottom: 24px;
  z-index: 5;                         /* 「내 위치」와 같은 층 — 매장 카드(6)보다 아래다 */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;                           /* 상세 카드와 칩 사이 (L52) */
  letter-spacing: normal;             /* 파마 글자는 자간을 안 준다(24K 기본은 -0.1px) */
}
/* 배율이 500m 밖으로 물러나면 통째로 접는다(`Config.zoom.wxTo` · weather.js).
   **`!important` 가 필요하다** — 위 `display: flex` 는 저자 규칙이라, 브라우저가 기본으로
   들고 있는 `[hidden] { display: none }` 을 그냥 이긴다. 값만 `hidden` 으로 세우고
   여기를 안 적으면 **아무 일도 안 일어난다**(`.wx-card` 도 같은 이유로 이렇게 적혀 있다). */
.wx[hidden] { display: none !important; }

/* 여기 있던 규칙 — 「임시 지도 안내띠가 뜨면 날씨를 접는다」 — 는 2026-09-06 에 걷었다.
   그 띠(`.mapnotice`)도 임시 지도도 이제 없다.

   대신 **지도가 아예 못 떴을 때** 지도에만 딸린 손잡이를 접는다 — 보기 방식
   (`.viewsw`: 일반뷰·거리뷰·위성)·「내 위치」·날씨 셋이다. 날씨는 지도 중심을 물어야
   값이 나오고, 나머지 둘은 누를 지도가 없다.

   **접는 것은 `.viewsw` 이지 `.rail` 이 아니다.** 예약·채팅 입구를 걷은 지금은
   레일 안에 선택기 하나뿐이라 결과가 같아 보이지만, 여기에 「레일을 접는다」로
   적어 두면 나중에 지도와 상관없는 단추를 레일에 세우는 순간 그 단추까지 같이
   사라진다 — 전에 그렇게 걸었다가 `chat-web-audit` 이 「채팅 단추가 안 보인다」로
   멈췄다. 지도가 안 뜨는 것과 다른 화면에 못 들어가는 것은 **다른 일이다.** */
.stage:has(.mapfail:not([hidden])) .wx,
.stage:has(.mapfail:not([hidden])) .viewsw,
.stage:has(.mapfail:not([hidden])) .locate { display: none; }

/* ── 접힌 칩 ── */

.wx-chip {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 8px 12px;                          /* L282-285 */
  border: 1px solid #EDEDED;                  /* L171 */
  border-radius: 10px;                        /* L170 */
  background: rgba(255, 255, 255, 0.82);      /* FamaOverlayStyle.surface */
  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14),
              0 1px 5px 0 rgba(0, 0, 0, .12); /* elevation 2 */
  cursor: pointer;
  text-align: left;
  color: #1A1A1A;

  /* ── 「작게」는 조각 하나가 아니라 **칩 통째로** 다 (2026-09-05 지시) ──
   *
   * 처음엔 아이콘 px 만 24 → 19.2 → 14.4 → 10.8 → 9.18 로 깎았는데, 그러면 해만
   * 쪼그라들고 글자는 그대로라 **비례가 무너진다.** 원한 것은 「날씨 버튼이 작아지는
   * 것」이었다. 그래서 조각 치수는 파마 값 그대로 되돌리고, 여기서 **한 번에 줄인다.**
   *
   * 값이 하나뿐이라 다음에 「더/덜」이 와도 이 숫자만 만진다 — 파마에서 옮겨 적은
   * 치수(글자 13·17, 아이콘 24, 여백, 게이지, 그림자)는 손대지 않아도 된다.
   *
   * `bottom left` 로 줄인다 — 칩이 좌하단에 붙어 있으니 그 모서리를 붙박아야 줄여도
   * 카카오 로고 띠 위 제자리에 선다. 가운데로 줄이면 바닥에서 떠오른다. */
  transform: scale(0.8);
  transform-origin: bottom left;
}
.wx-dong {
  display: block;
  max-width: 132px;
  margin-bottom: 4px;                         /* L243 */
  font-size: 13px; font-weight: 700; line-height: 1;   /* L213-218 */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wx-now { display: flex; align-items: center; }
.wx-icon { flex: none; width: 24px; height: 24px; }   /* L247 */
.wx-icon svg { display: block; width: 100%; height: 100%; }
.wx-temp {
  margin-left: 6px;                                    /* L248 */
  font-size: 17px; font-weight: 700; line-height: 1;   /* L191-196 */
}
.wx-dust { margin-left: 10px; display: flex; flex-direction: column; gap: 4px; } /* L250, L353 */
.wx-dust-row { display: flex; align-items: center; }
.wx-dust-lbl { width: 26px; font-size: 9px; color: #666666; }  /* L334-342 */
.wx-gauge {
  width: 30px; height: 5px;                   /* L345 */
  border-radius: 4px;
  background: #EDEDED;                        /* L137 */
  overflow: hidden;
}
.wx-gauge i { display: block; height: 100%; border-radius: 4px; }

/* 발효중 기상특보 — 칩 우상단 바깥으로 걸친다 (L300-322). */
.wx-warnbadge {
  position: absolute;
  top: -6px; right: -6px;
  padding: 2px 6px;
  border: 1.5px solid #fff;
  border-radius: 8px;
  color: #fff;
  font-size: 10px; font-weight: 800; line-height: 1;
}

/* ── 펼친 상세 카드 ── */

.wx-card[hidden] { display: none !important; }
.wx-card {
  display: block;
  width: 340px;                               /* weather_overlay.dart L74 */
  max-width: calc(100vw - 24px);
  padding: 12px 12px 12px 16px;               /* fromLTRB(16,12,12,12) (L76) */
  background: #fff;
  border-radius: 16px;                        /* L71 */
  box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14),
              0 1px 18px 0 rgba(0, 0, 0, .12); /* elevation 6 */
  color: #1A1A1A;
  letter-spacing: normal;
}
.wx-loading { height: 140px; display: grid; place-items: center; }  /* L64-66 */

.wx-head { display: flex; align-items: center; gap: 8px; }
.wx-head b {
  flex: 1; min-width: 0;
  font-size: 16px; font-weight: 700;          /* L393-396 */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wx-close {
  flex: none;
  width: 30px; height: 30px;                  /* 아이콘 18 + 패딩 6 (L492-494) */
  display: grid; place-items: center;
  border-radius: 50%;
  background: #F1F3F5;                        /* L487 */
  color: #6B7280;
  cursor: pointer;
}
.wx-close svg { width: 18px; height: 18px; }

.wx-big {
  margin-top: 4px;                            /* L403 */
  display: flex; align-items: center; justify-content: center;
}
.wx-big-i { width: 60px; height: 60px; }      /* L409 */
.wx-big-i svg { display: block; width: 100%; height: 100%; }
.wx-big b {
  margin-left: 12px;                          /* L410 */
  font-size: 48px; font-weight: 800; line-height: 1;  /* L413-417 */
}
.wx-cond {
  margin-top: 2px;                            /* L421 */
  text-align: center;
  font-size: 14px; color: #666666;            /* L425-428 */
}
.wx-meta {
  margin-top: 10px;                           /* L431 */
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  font-size: 13px; color: #666666;            /* L512 */
}
.wx-meta b { font-weight: 700; }              /* 색은 등급마다 달라 인라인으로 준다 */
/* 가운뎃점 — 좌우 8px 씩 (L500-504). 탭 사이에도 같은 것을 쓴다. */
.wx-sep { font-style: normal; padding: 0 8px; font-size: 13px; color: #757575; }

.wx-sub {
  margin-top: 14px;                           /* L434 */
  padding: 10px 14px 12px;                    /* fromLTRB(14,10,14,12) (L437) */
  background: #F7F8FA;                        /* L439 */
  border-radius: 14px;
}
.wx-tabs { display: flex; align-items: center; }
.wx-tabs button { font-size: 14px; cursor: pointer; }               /* L561-565 */
.wx-tabs button[aria-selected="false"] { font-weight: 500; color: #757575; }
.wx-tabs button[aria-selected="true"] { font-weight: 700; color: #1A1A1A; }
/* Divider(height: 14) — 1px 선이 14px 칸 한가운데 (L453) */
.wx-rule { height: 1px; margin: 6.5px 0; background: rgba(0, 0, 0, .12); }
.wx-view { height: 116px; }                                          /* L455 */
.wx-none { height: 100%; display: grid; place-items: center; font-size: 13px; color: #757575; }

/* 가로로 흐르는 두 줄. 파마도 막대를 감춘다(scrollbars: false).
 *
 * **끌어서 넘기는 것은 JS 가 한다**(`weather.js`) — 브라우저는 이 상자를 마우스로
 * 못 끌게 하기 때문이다. 여기서는 그 손이 쓸 자리만 만들어 둔다:
 *  · `cursor: grab` — 끌 수 있다는 표시. 이게 없으면 오른쪽에 더 있는 줄을 아무도 모른다.
 *  · `user-select: none` — 없으면 끌 때 「12시 13시 14시」가 파랗게 딸려 잡힌다. */
.wx-hours, .wx-days {
  height: 100%;
  display: flex;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.wx-hours.grab, .wx-days.grab { cursor: grabbing; }
.wx-hours::-webkit-scrollbar, .wx-days::-webkit-scrollbar { display: none; }

.wx-h {
  flex: none;
  width: 52px;                                /* L591 */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.wx-h + .wx-h { margin-left: 4px; }           /* L588 */
.wx-h-t { font-size: 12px; color: #666666; }  /* L598-601 */
.wx-h-i { margin-top: 6px; width: 28px; height: 28px; }   /* L603-604 */
.wx-h-i svg { display: block; width: 100%; height: 100%; }
/* 강수확률 자리는 **비어도 높이를 남긴다** — 안 그러면 칸마다 기온 줄이 어긋난다 (L606-620). */
.wx-pop {
  margin-top: 4px;
  height: 13px; line-height: 13px;
  font-size: 10px; color: #2671FB;
}
.wx-h-c { margin-top: 2px; font-size: 13px; font-weight: 600; }  /* L621-627 */

.wx-d {
  position: relative;
  flex: none;
  width: 80px;                                /* L660 */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
/* 칸 사이 세로 선 — 위아래 8px 은 비운다 (L653-657). */
.wx-d + .wx-d::before {
  content: '';
  position: absolute; left: 0; top: 8px; bottom: 8px;
  width: 1px; background: #EEEEEE;
}
.wx-d-t { font-size: 12px; text-align: center; }
.wx-d-r { margin-top: 8px; display: flex; align-items: flex-start; gap: 6px; }  /* L670-677 */
.wx-ampm { display: flex; flex-direction: column; align-items: center; }
.wx-d-i { width: 26px; height: 26px; }        /* L720 */
.wx-d-i svg { display: block; width: 100%; height: 100%; }
.wx-ampm .wx-pop { margin-top: 0; }
.wx-d-c { margin-top: 6px; display: flex; align-items: center; font-size: 12px; }
.wx-d-c b { font-weight: 600; }
.wx-d-c .lo { color: #2671FB; }
.wx-d-c .hi { color: #EF4444; }
.wx-d-c i { font-style: normal; color: #757575; }

/* 발효중 기상특보 — 예보 아래 풀폭 띠 (L67-97, L462-469). 색은 종류마다 달라 인라인. */
.wx-warn {
  margin-top: 12px;
  display: flex; align-items: center; gap: 8px;
  padding: 8px;
  border-radius: 8px;
  color: #fff;
}
.wx-warn + .wx-warn { margin-top: 6px; }
.wx-warn .tag {
  flex: none;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, .28);
  font-size: 11px; font-weight: 800;
}
.wx-warn .txt {
  flex: 1; min-width: 0;
  font-size: 13px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 갈래 아이콘. 글자와 6px 만 띄운다 — 나머지 여백은 `txt` 가 늘어나며 먹는다 (L92-93). */
.wx-warn .ic { flex: none; margin-left: -2px; width: 18px; height: 18px; }
.wx-warn .ic svg { display: block; width: 100%; height: 100%; }
.wx-foot { margin-top: 8px; font-size: 11px; color: #757575; }   /* L470-474 */

/* 자료를 받는 동안 도는 물레. 파마의 CircularProgressIndicator 자리다. */
.wx-spin {
  display: inline-block;
  width: 16px; height: 16px;                  /* L197-201 */
  border: 2px solid rgba(38, 113, 251, .25);
  border-top-color: #2671FB;
  border-radius: 50%;
  animation: wx-turn .9s linear infinite;
}
.wx-spin.big { width: 36px; height: 36px; border-width: 3px; }
@keyframes wx-turn { to { transform: rotate(360deg); } }

/* ── 폰: 상세는 화면 한가운데 창으로 뜬다 (weather_overlay.dart L119-153) ──
 *
 * `.app` **밖**에 서 있다. 안에 두면 상단바(z 20)·가입 창(z 60) 사이에 끼어
 * 어느 쪽이 위인지가 화면 크기마다 달라진다. 가입 창과 같은 층에 나란히 둔다. */
.wx-modal[hidden] { display: none !important; }
.wx-modal { position: fixed; inset: 0; z-index: 60; }
.wx-dim { position: absolute; inset: 0; background: rgba(0, 0, 0, .54); }  /* Colors.black54 */
/* **격자(`grid`)가 아니라 `flex` 다.** 여기가 한때 `display: grid; place-items: center`
 * 였는데, 폰에서 창이 가운데가 아니라 **왼쪽으로 붙고 오른쪽이 잘려** 나갔다.
 *
 * 격자의 `auto` 칸은 제 안에 든 것의 크기부터 잰다. 카드는 `width: 360px` 이라 칸이
 * 360 으로 벌어지는데, 여백 20+20 을 뺀 자리는 폰(390px)에서 350.4 뿐이라 칸이
 * 통째로 넘친다. 그 다음 아래 `max-width: 100%` 가 **넘쳐 버린 그 칸(360)** 을
 * 기준으로 풀려서 아무것도 못 깎는다 — 퍼센트가 제 자신을 기준 삼는 고리다.
 * 넘친 것은 가운데 정렬이 **시작 쪽 정렬로 되물러나서**(넘칠 때의 기본 동작)
 * 왼쪽 20px 에 붙고 오른쪽이 화면 밖으로 나갔다.
 *
 * `flex` 는 이 고리가 없다 — 이 판은 `inset: 0` 이라 폭이 이미 정해져 있어서
 * `max-width: 100%` 가 **판의 안쪽 폭(350.4)** 으로 곧장 풀린다. 그래서 카드가
 * 먼저 줄고, 줄었으니 가운데 정렬이 그대로 산다. 세로도 같은 이유로 같이 낫는다.
 * 되돌리려거든 390px 폭에서 카드 좌우 여백이 **둘 다 20** 인지부터 재 본다. */
.wx-sheet {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;                              /* L131 */
  pointer-events: none;                       /* 카드 밖 빈 자리는 딤이 받는다 */
}
.wx-sheet .wx-card {
  pointer-events: auto;
  width: 360px;                               /* L137 */
  max-width: 100%;
  max-height: 100%;
  overflow-y: auto;
  padding: 16px 20px;                         /* fromLTRB(20,16,20,16) (L145) */
  border-radius: 20px;                        /* L141 */
  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14),
              0 3px 14px 2px rgba(0, 0, 0, .12); /* elevation 8 */
}

/* 폰 칩은 동 이름 + 아이콘 + 기온까지만이다 (2026-09-05 지시).
 *
 * 미세·초미세 두 줄이 칩을 옆으로 60px 넘게 늘려서, 좁은 화면에서는 칩이 지도
 * 한복판까지 밀고 들어와 표식을 가렸다. 등급은 **눌러서 펼친 상세**에 그대로 있다
 * (`.wx-meta` — 거기서는 색과 글자로 읽히니 미니 게이지보다 오히려 잘 보인다).
 *
 * `isPhone()` 과 같은 899px 로 끊는다. 여기서 갈라지면 상세가 가운데 창으로 뜨는
 * 폭과 칩이 줄어드는 폭이 어긋난다. */
@media (max-width: 899px) {
  .wx-chip .wx-dust { display: none; }

  /* 그리고 남은 셋을 **세로로 쌓는다** — 동 이름 / 아이콘 / 기온 (2026-09-05 지시).
   *
   * 가로로 두면 칩이 「보광동」 폭에 끌려 옆으로 길어지는데, 폰에서 지도를 가리는 것은
   * 높이가 아니라 **폭**이다(좌하단이라 세로로는 카카오 눈금 띠 위 빈자리가 남는다).
   * 가운데 정렬은 취향이 아니다 — 아이콘 24px 과 기온 글자의 중심이 안 맞으면
   * 세로 줄이 지그재그로 읽힌다.
   *
   * `.wx-temp` 의 `margin-left: 6px` 는 **가로였을 때 아이콘과 띄우던 값**이다.
   * 세로에서는 왼쪽으로 미는 힘이 되어 가운데가 틀어진다 — 위쪽 여백으로 옮긴다. */
  /* 폰은 한 단 더 작다 — PC 의 0.8 에 15% 를 더 물려 **0.68** 이다.
   * `transform-origin` 은 위에서 정한 `bottom left` 를 그대로 물려받는다. */
  .wx-chip { align-items: center; padding: 8px 10px; transform: scale(0.68); }
  .wx-now { flex-direction: column; }
  .wx-temp { margin-left: 0; margin-top: 4px; }

  /* 동 이름을 한 단 더 굵게 (2026-09-06 지시).
   * 파마에서 옮겨 온 700 은 PC 에서는 굵어 보이지만, 폰은 13px 에 scale(0.68) 이
   * 겹쳐 실효 8.8px 이라 아래 기온(17px·700)보다 훨씬 가늘게 읽힌다.
   * Pretendard Variable 이 45~920 가변이라 900 이 실제로 다른 굵기로 그려진다
   * (같은 글자의 잉크량이 700 대비 800 은 +12%, 900 은 +23% — 800 은 이 크기에서
   *  「변한 게 없다」로 읽힌다. 글자 폭은 셋 다 같아서 칩은 안 커진다.) */
  .wx-dong { font-weight: 900; }
}

/* ── 업체 조회 통계 창 (venue-stats.js) ────────────────────────────
 *
 * 상세 카드 머리의 그래프 단추가 여는 창. 국모의 「의원 조회 통계」와 **같은 뼈대**를
 * 24K 것으로 갈아입힌 것이다(2026-09-07 지시).
 *
 * 겉은 「앱으로 보기」창(`.invite`)과 **같은 갈래**다 — 화면 가운데에 서는, 눌러야
 * 사라지는 창. 이 저장소는 아래 띠를 안 쓰고 되돌리지 않는다.
 * z 도 그것과 같은 58 이다: 상세 카드(6)·화면(8)·상단바(20) 위, 가입 창(60) 아래 —
 * 둘이 겹치면 방금 누른 가입 창이 이겨야 한다.
 *
 * 안쪽 판(`.vst-card`)은 **상세 카드의 검은 판(`.vinfo`)과 같은 것**이다. 취향이 아니라
 * 색 때문이다 — 브랜드 금색(#f5c542)을 흰 종이에 막대로 세우면 그 막대가 안 읽힌다.
 * 검은 판 위에서는 금색이 가장 잘 서고, 카드 안에서 이미 보던 판이라 창이 낯설지 않다. */
.vstat {
  position: fixed;
  inset: 0;
  z-index: 58;
  background: rgba(0, 0, 0, 0.58);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.vstat[hidden] { display: none !important; }
/* 가운데 정렬은 **flex 로만 한다.** grid + place-items 로 짰다가 폰에서 고정폭 판이
   칸을 넘치는 순간 가운데가 풀려 왼쪽에 붙는 것을 겪었다(`.wx-sheet` 주석과 같은 건). */
.vstat .sheet {
  background: var(--card);
  width: 100%;
  max-width: 420px;
  border-radius: 20px;
  padding: 20px 16px calc(16px + var(--safe-b));
  position: relative;
  max-height: 88vh;
  overflow-y: auto;
}
.vstat .x {
  position: absolute;
  right: 8px; top: 8px;
  font-size: 16px;
  color: var(--sub);
  padding: 8px 11px;
}

/* — 머리 — 사진 · 이름 · 칩. 오른쪽 34px 은 ✕ 자리라 비운다. */
.vst-head { display: flex; gap: 12px; align-items: flex-start; padding-right: 34px; }
.vst-thumb {
  width: 46px; height: 46px; flex: none;
  border-radius: 12px; overflow: hidden; background: #222;
}
.vst-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vst-hd { min-width: 0; }
/* 이름이 길다 — 「레벨라운지앤클럽」처럼. 한 줄로 자르면 무슨 가게인지 사라지니
   **두 줄까지 늘리고** 그 다음에 자른다. */
.vst-head h2 {
  margin: 2px 0 0;
  font-size: 15.5px; font-weight: 800; letter-spacing: -0.5px; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.vst-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.vst-chips span {
  font-size: 11px; color: var(--chip-ink); background: var(--chip-bg);
  border-radius: 999px; padding: 3px 9px;
}
/* 「예시」 — 채운 칩이 아니라 **테두리만** 이다. 옆의 종류·지역 칩은 사실이고
   이것만 단서라, 같은 무게로 서면 셋이 다 같은 말로 읽힌다. */
.vst-chips .ex {
  background: transparent; color: var(--sub);
  border: 1px solid var(--line); padding: 2px 8px;
}

/* — 일별 · 월별 · 년별 — 카드 안 시간대 고르개(`.slotpick`)와 **같은 꼴**이다.
     같은 화면에서 같은 일(칸 고르기)을 하는 알약이 둘로 갈리면 안 된다. */
.vst-units { display: flex; gap: 6px; margin-top: 14px; }
.vst-units button {
  font-size: 12px; font-weight: 700; padding: 7px 14px; border-radius: 999px;
  border: 1px solid var(--line); background: #fff; color: var(--sub); cursor: pointer;
}
.vst-units button[aria-pressed="true"] { border-color: var(--ink); background: var(--ink); color: #fff; }

/* — 판 — 상세 카드의 `.vinfo` 와 같은 흰 판·같은 모서리다(2026-09-07 지시). */
.vst-card {
  margin-top: 12px;
  background: var(--night);
  border-radius: 14px;
  padding: 14px;
  color: var(--panel-ink);
}

/* 요약 칸 셋. **줄바꿈을 막지 않는다** — 「가장 많았던 일」은 날짜까지 들어서 폰에서
   혼자 아래로 내려앉는데, 억지로 한 줄에 세우면 셋 다 글자가 뭉갠다. */
.vst-tiles { display: flex; flex-wrap: wrap; gap: 8px; }
.vst-tile { flex: 1 1 96px; background: var(--night2); border-radius: 10px; padding: 8px 10px; }
.vst-tile span { display: block; font-size: 10.5px; color: var(--night-sub); }
.vst-tile b {
  display: block; margin-top: 3px;
  font-size: 15px; font-weight: 800; color: var(--panel-ink);
  font-variant-numeric: tabular-nums;
}
.vst-tile.wide { flex: 1 1 100%; }
.vst-tile.wide b { font-size: 13px; }

/* — 막대그래프 —
 *
 * 그림 파일도, 그리는 라이브러리도 안 쓴다. 막대는 그냥 `flex` 칸이고 높이는 %다 —
 * 그래야 폰·PC 폭이 바뀔 때 **다시 그리지 않고** 저절로 맞는다(SVG 로 그리면 폭을
 * 재서 다시 그려야 하고, 그 재는 코드가 창 크기 바뀔 때마다 도는 짐이 된다).
 *
 * 위쪽 22px 여백은 **꼭대기 막대 위에 앉는 수** 자리다. 안 비우면 그 글자가 판
 * 밖으로 잘려 나간다. */
.vst-chart { display: flex; margin-top: 22px; --ch: 148px; }
.vst-y { width: 28px; flex: none; position: relative; height: var(--ch); }
.vst-yl {
  position: absolute; right: 5px;
  transform: translateY(50%);
  font-size: 9.5px; font-style: normal; color: var(--night-sub);
  font-variant-numeric: tabular-nums;
}
.vst-plot { flex: 1; min-width: 0; position: relative; height: var(--ch); }
/* 가로줄만 긋는다. 세로줄은 서른 칸에서 막대보다 촘촘해져 막대를 지운다. */
.vst-grid { position: absolute; left: 0; right: 0; height: 1px; background: var(--night-line); }
.vst-bars { position: absolute; inset: 0; display: flex; align-items: flex-end; gap: 2px; }
.vst-col { flex: 1; min-width: 0; height: 100%; display: flex; align-items: flex-end; position: relative; }
/* 꼭대기가 아닌 막대는 **같은 금색을 흐리게** 쓴다. 다른 색을 주면 그래프에 색이
   둘이 되고, 사람은 두 색을 「두 가지 자료」로 읽는다. */
/* **막대에 최대 폭이 있다.** 칸 폭을 그냥 다 쓰게 두면, 문 연 지 얼마 안 된 가게의
   「년별」은 칸이 하나뿐이라 판을 가로로 다 채운 금색 덩어리가 된다 — 그건 그래프가
   아니라 색면이다(실제로 그렇게 나왔다). 서른 칸일 때는 한 칸이 24px 에 한참 못 미쳐
   이 한도가 아무 일도 안 하고, 한두 칸일 때만 걸린다. 국모 그래프도 같은 값(24)이다. */
.vst-col i {
  width: 100%; max-width: 24px; margin: 0 auto;
  border-radius: 3px 3px 0 0;
  background: var(--panel-bar);
}
.vst-col.pk i { background: var(--panel-mark); }
.vst-col b {
  position: absolute; left: 50%; transform: translateX(-50%);
  font-size: 10.5px; font-weight: 800; color: var(--panel-key);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* 가로축 — 막대와 **같은 칸·같은 틈**이라야 이름표가 제 막대 밑에 선다.
   왼쪽 28px 은 세로축 눈금 자리만큼 밀어 준 값이다. */
.vst-xax { display: flex; gap: 2px; margin: 7px 0 0 28px; height: 14px; }
.vst-xc { flex: 1; min-width: 0; position: relative; }
.vst-xl {
  position: absolute; left: 50%; top: 0; transform: translateX(-50%);
  font-size: 9.5px; font-style: normal; color: var(--night-sub);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}

/* — 기록 줄 — 머리글은 카드 안 판의 머리글(`.vi h4`)과 같은 금색·같은 자간이다. */
.vst-list { margin-top: 18px; }
.vst-lhd {
  display: flex; align-items: baseline; gap: 8px;
  margin-bottom: 2px;
  font-size: 11.5px; font-weight: 700; letter-spacing: 1px; color: var(--panel-key);
}
.vst-lhd em { font-style: normal; font-size: 10.5px; letter-spacing: 0; color: var(--night-sub); }
.vst-row {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 0; border-bottom: 1px solid var(--night-line);
  font-size: 12px;
}
.vst-row:last-child { border-bottom: 0; }
.vst-row .d { width: 92px; flex: none; color: var(--night-body); font-variant-numeric: tabular-nums; }
.vst-row .n { margin-left: auto; font-weight: 700; color: var(--panel-ink); font-variant-numeric: tabular-nums; }
/* 「최다」 — 국모가 여기 불꽃 그림을 쓰는데, 24K 는 **글자만** 쓴다. 카드 안
   어디에도 뜻을 그림으로 대신하는 자리가 없어서 여기만 그러면 튄다. */
.vst-row .top { font-size: 10.5px; font-weight: 800; color: var(--panel-key); }
/* 0 인 날도 **지우지 않는다** — 안 그리면 「그날은 아무도 안 봤다」가 화면에서 사라진다. */
.vst-row.zero .d, .vst-row.zero .n { color: var(--night-sub); font-weight: 500; }

.vst-empty { margin: 14px 0 0; font-size: 12.5px; line-height: 1.6; color: var(--night-sub); }
.vst-note {
  margin: 14px 0 0;
  font-size: 11px; line-height: 1.6; color: var(--night-sub);
}
.vst-note b { color: var(--night-body); font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
  .vstat .sheet { transition: none; }
}

/* 기업 로그인 (2026-09-08 지시) — **파란 글자 한 줄.**
   회원 가입란 안에 있지만 회원이 갈 곳이 아니라서, 이 화면에서 **혼자만 파랗다**
   (나머지는 금색·검정이다). 색만으로 가르지 않는다 — 아래 한 줄이 누구를 위한
   자리인지 말한다. 색을 못 보는 사람에게는 그 문장이 유일한 표식이다.
   `--biz` 를 따로 판 이유: 파랑이 이 화면 말고 업체 포털에도 서고, 두 곳에 같은
   색값을 적어 두면 한쪽만 고쳐진다. 어두운 판에서도 읽히는 밝기로 잡았다. */
:root { --biz: #1d64f2; --biz-soft: rgba(29, 100, 242, 0.09); }
body.dark, .dark { --biz: #6ea1ff; --biz-soft: rgba(110, 161, 255, 0.14); }
.signup .bizgate {
  margin: 14px 0 0;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--biz-soft);
  text-align: center;
}
.signup .bizgate a {
  display: inline-block;
  color: var(--biz);
  font-weight: 800;
  font-size: 15px;
  letter-spacing: -0.3px;
  text-decoration: none;
  /* 누를 자리를 44px 로 띄운다 — 폰에서 글자 한 줄은 손가락으로 못 겨눈다. */
  padding: 6px 10px;
  min-height: 32px;
}
.signup .bizgate a:hover, .signup .bizgate a:focus-visible { text-decoration: underline; }
.signup .bizgate .cond { display: block; margin: 2px 0 0; line-height: 1.6; }

/* ── 제휴 매장과의 1:1 채팅 판 (2026-09-08) ──
   가입 창(`.signup`)과 **같은 자리에 같은 꼴**로 뜬다 — 회원이 이 앱에서 보는
   「덮는 판」이 둘뿐이라, 둘이 다르게 생기면 그것부터 딴 앱처럼 읽힌다.
   방 안쪽만 카톡 하늘빛이다(매장 쪽 화면 `biz.css` 와 **같은 색값**을 쓴다 —
   두 사람이 같은 대화를 보고 있다고 느끼려면 같은 방이어야 한다). */
.bizchat {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(14, 14, 17, 0.5);
}
@media (min-width: 900px) { .bizchat { align-items: center; } }
.bizchat .sheet {
  width: 100%;
  max-width: 460px;
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  padding: 16px;
  padding-bottom: calc(16px + var(--safe-b));
  border-radius: 20px 20px 0 0;
  background: var(--card);
  color: var(--ink);
}
@media (min-width: 900px) { .bizchat .sheet { border-radius: 20px; padding-bottom: 16px; } }
.bizchat header { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.bizchat header b { font-size: 17px; letter-spacing: -0.5px; }
.bizchat header .cond { margin: 0; }
.bizchat .x {
  order: 3;
  margin-left: auto;
  width: 34px; height: 34px;
  border: 0; border-radius: 11px;
  background: var(--chip-bg); color: var(--chip-ink);
  font: 700 14px/1 Pretendard, sans-serif; cursor: pointer;
}
.bizchat .log {
  flex: 1;
  min-height: 220px;
  overflow-y: auto;
  padding: 12px;
  border-radius: 14px;
  background: #b2c7da;
  display: flex; flex-direction: column; gap: 8px;
}
.bizchat .log .empty { margin: auto; text-align: center; color: #33404b; line-height: 1.8; }
/* ⚠ 말풍선 이름이 `.bcm` 인 이유 — **`.m`·`.me` 는 이미 임자가 있다.**
   `.me` 는 이 판(app.css §지도)의 **「내 위치」 핀**이다:
   `width: calc(54px * var(--mew))` 에 `transform: translate(-50%,-50%) scale(var(--mz))`.
   말풍선에 `me` 를 달면 그 규칙을 그대로 물려받아 **19px 짜리 세로 막대**가 된다
   (글자가 한 줄에 한 자씩 선다). 오류는 한 줄도 안 나고, 화면을 봐야만 보인다.
   실제로 그렇게 났다 — 두 글자짜리 클래스는 이 판에서 이미 쓰이고 있다고 봐야 한다. */
.bizchat .bcm { max-width: 78%; align-self: flex-start; }
.bizchat .bcm p {
  margin: 0; padding: 9px 12px;
  border-radius: 4px 14px 14px 14px;
  background: #fff; color: #111214;
  font-size: 14px; line-height: 1.6; word-break: break-word;
}
.bizchat .bcm i { display: block; margin-top: 3px; font-style: normal; font-size: 10.5px; color: #4a5a68; }
.bizchat .bcm.mine { align-self: flex-end; }
.bizchat .bcm.mine p { border-radius: 14px 4px 14px 14px; background: #fee500; }
.bizchat .bcm.mine i { text-align: right; }
.bizchat .bcm.blocked {
  align-self: center; max-width: 92%;
  padding: 9px 12px; border-radius: 12px;
  background: rgba(194, 38, 42, 0.92); color: #fff; text-align: center;
}
.bizchat .bcm.blocked b { display: block; font-size: 12.5px; }
.bizchat .bcm.blocked span { font-size: 11.5px; opacity: 0.92; line-height: 1.6; }
.bizchat .err { color: var(--danger-ink); margin: 8px 0 0; }
.bizchat .err:empty { display: none; }
.bizchat .bar { display: flex; gap: 8px; align-items: flex-end; margin-top: 10px; }
.bizchat .bar textarea {
  flex: 1; box-sizing: border-box;
  padding: 12px; max-height: 120px;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--soft); color: var(--ink);
  /* **16px 이어야 한다** — 그보다 작으면 iOS 사파리가 칸을 누를 때 화면을 확대하고,
     확대된 뒤에는 손으로 되돌리기 전까지 지도까지 커진 채로 남는다. */
  font: 400 16px/1.5 Pretendard, sans-serif;
  resize: none;
}
.bizchat .bar .btn { width: auto; margin: 0; flex: 0 0 auto; padding: 0 18px; min-height: 46px; }
.bizchat .rule { margin-top: 10px; line-height: 1.7; }
