/* 물류(가제) — 지도 화면.
   상단 고정 정보 바는 없다. 화면에 상시 남는 것은 오른쪽 도구 열뿐이고,
   나머지는 필요할 때만 뜬다(설명 팝업 · 코치 카드 · 인스펙터). */

:root {
  color-scheme: light;
  --ink: #172022;
  --muted: #667173;
  --paper: #f1f0e7;
  --station: #d47c32;
  --sea: #a9bdc1;

  --tile: #d8d9ce;             /* 열린 남한 */
  --tile-line: #afb4aa;
  --tile-off: #a8b1a6;         /* 아직 열리지 않은 남한 */
  --tile-off-line: #97a094;
  --tile-dim: #67716f;         /* 잠긴 땅 — 빈 판은 이 색으로 깔린다 (2026-08-27 더 어둡게) */
  --tile-dim-line: #57615f;
  --tile-pickup: #76878f;      /* 원재료가 놓인 칸 — 살짝 밝은 하늘빛 (45.2) */
  --tile-pickup-line: #92a5ad;
  --tile-north: #6d797b;       /* 북한 (튜토리얼 후 — 더 어둡게, 남한과 다른 색) */
  --tile-north-line: #5d696b;

  --city: #e2a35f;
  --city-line: #b06a20;
  --farm: #8fb46a;
  --farm-line: #5c8038;
  --mine: #858d8c;
  --mine-line: #4f5a5b;
  --logging: #7ca06a;
  --logging-line: #486d3d;
  --power: #9a876d;
  --power-line: #65513b;
  --water: #6f9ab0;            /* 수도시설 (38.5) */
  --water-line: #43708a;
  --road: #4a433e;
  --road-edge: #765638;
  --road-shoulder: #c9a26b;
  --road-line: #f4dfaa;

  --shadow: 0 10px 28px rgb(10 18 19 / .2);
  font-family: Pretendard, "Noto Sans KR", "Segoe UI", system-ui, -apple-system, sans-serif;
}

* { box-sizing: border-box; }
html, body { width: 100%; height: 100%; }
body { margin: 0; min-width: 320px; overflow: hidden; color: var(--ink); background: var(--sea); }
button { color: inherit; font: inherit; }
button:focus-visible { outline: 3px solid #e59b45; outline-offset: 2px; }

.map-stage {
  position: relative; width: 100%; height: 100dvh; min-width: 0; min-height: 0; overflow: hidden;
  background: radial-gradient(circle at 45% 38%, rgb(255 255 255 / .34), transparent 42%), var(--sea);
  /* 두 손가락 확대·이동을 직접 처리하므로 브라우저 기본 제스처는 끈다. */
  touch-action: none;
}
.map-stage.is-panning { cursor: grabbing; }
.map-stage::before {
  content: ""; position: absolute; inset: 0; opacity: .15; pointer-events: none;
  background-image: radial-gradient(#526d71 1px, transparent 1px); background-size: 19px 19px;
}
.world-map { position: relative; width: 100%; height: 100%; display: block; filter: drop-shadow(0 12px 15px rgb(34 51 53 / .19)); }

/* 덧그리는 층은 클릭을 가로채지 않는다.
   예외는 시설 도형 하나뿐이다 — 도시·건물은 칸이 아니라 그 도형이 누르는 대상이다.
   길은 게임이 깔고 게임이 쓰므로 누를 일이 없다. */
.road-layer, .site-layer, .cue-layer { pointer-events: none; }
.site-shape { pointer-events: auto; }

/* ------------------------------------------------------------------ 타일 */

.map-tile.is-south { cursor: pointer; }
.tile-face {
  fill: var(--tile); stroke: var(--tile-line); stroke-width: 1.4;
  vector-effect: non-scaling-stroke; transition: fill .2s, stroke .2s;
}
.map-tile.is-south:hover .tile-face { fill: #eeeadd; stroke: var(--station); stroke-width: 2.2; }
.map-tile.is-selected .tile-face { fill: #fbf4e2; stroke: var(--station); stroke-width: 3; }

/* 북한은 플레이 영역이 아니라 배경이다. 회색으로 낮춰 남한이 먼저 눈에 들어오게 한다. */
.map-tile.is-north { pointer-events: none; }
.map-tile.is-north .tile-face { fill: var(--tile-dim); stroke: var(--tile-dim-line); }
.map-tile.is-north .tile-coord { fill: #7f8b8d; }
/* 잠긴 판에서 켜진 칸(시설 자리·이벤트로 열린 곳)은 밝게 — 스마트 포커싱이 이 칸들을 담는다 (41장) */
.map-tile.is-north.is-active .tile-face { fill: var(--tile); stroke: var(--tile-line); }
/* 원재료가 놓인 칸. 아이콘만 떠 있을 때보다 "저 칸에 뭔가 있다"가 멀리서도 잡힌다.
   잠긴 땅과 같은 계열이되 한 톤 밝고 푸른 쪽으로 기울여, 열린 땅(따뜻한 색)과도 갈린다. */
.map-tile.has-pickup .tile-face { fill: var(--tile-pickup); stroke: var(--tile-pickup-line); transition: fill .3s, stroke .3s; }
.map-tile.is-north.is-active .tile-coord { fill: var(--ink); }

/* 아직 열리지 않은 땅. 남한은 중간 밝기, 북한은 그보다 더 어둡게 — 둘이 같은 회색이면
   "손댈 수 있는 땅"과 "아예 다른 나라"가 구분되지 않는다. */
.world-map.is-dimmed .map-tile.is-south .tile-face { fill: var(--tile-off); stroke: var(--tile-off-line); }
.world-map.is-dimmed .map-tile.is-north .tile-face { fill: var(--tile-north); stroke: var(--tile-north-line); }
.world-map.is-dimmed .map-tile.is-south:not(.is-active) { pointer-events: none; cursor: default; }
.world-map.is-dimmed .map-tile.is-active .tile-face { fill: var(--tile); stroke: var(--tile-line); }

/* 좌표는 항상 붙어 있고 필요할 때만 보이기를 켠다(C 키 또는 koreaMap.showCoords). */
.tile-coord {
  fill: #5a6668; font-size: 9px; font-weight: 700; text-anchor: middle;
  dominant-baseline: middle; pointer-events: none; opacity: 0;
}
.world-map.show-coords .tile-coord { opacity: 1; }
.world-map.show-coords .map-tile.is-north .tile-coord { opacity: .55; }
.map-tile.is-selected .tile-coord { fill: #7a4415; }

/* ------------------------------------------------------------------ 도시·건물 */

/* 시설이 놓인 칸은 스스로 그리지 않는다.
   칸마다 색과 테두리가 있으면 도시 7칸이 "칸 일곱 개"로 보인다. 안쪽 선을 지우고
   합친 겉선 하나(.site-shape)만 남겨야 7칸짜리 한 덩어리로 읽힌다.
   fill을 none이 아니라 transparent로 두는 이유는 none이면 클릭 판정에서도 빠지기 때문이다. */
.map-tile.is-site .tile-face,
.world-map.is-dimmed .map-tile.is-site .tile-face { fill: transparent; stroke: none; }
.map-tile.is-site,
.world-map.is-dimmed .map-tile.is-site { pointer-events: none; }
.map-tile.is-site .tile-coord { fill: #4b3417; }

/* 합쳐진 시설 도형. 안쪽 구분선이 없고 겉선만 있다. */
.site-shape {
  stroke-width: 2.4; vector-effect: non-scaling-stroke; cursor: pointer;
  transition: filter .15s, stroke-width .15s;
}
.site-shape.is-city { fill: var(--city); stroke: var(--city-line); }
.site-shape.is-farm { fill: var(--farm); stroke: var(--farm-line); }
.site-shape.is-mine { fill: var(--mine); stroke: var(--mine-line); }
.site-shape.is-logging { fill: var(--logging); stroke: var(--logging-line); }
.site-shape.is-power { fill: var(--power); stroke: var(--power-line); }
.site-shape.is-water { fill: var(--water); stroke: var(--water-line); }
.site-shape:hover { filter: brightness(1.07); stroke-width: 3.4; }
.site-shape.is-selected { stroke: #ffffff; stroke-width: 4; filter: brightness(1.05); }
.site-shape.is-cued { animation: rim 1.3s ease-in-out infinite; }
@keyframes rim {
  0%, 100% { stroke: #ffffff; stroke-width: 2.4; }
  50% { stroke: #ffffff; stroke-width: 5.5; }
}

.site-label {
  fill: #33240f; font-size: 11px; font-weight: 800; letter-spacing: -.04em;
  text-anchor: middle; dominant-baseline: middle; paint-order: stroke;
  stroke: rgb(255 255 255 / .55); stroke-width: 3; vector-effect: non-scaling-stroke;
}

/* 시설 내부 레벨 아트. 투명 WebP를 SVG 지도에 얹고 클릭은 외곽선으로 통과시킨다. */
.site-art { pointer-events: none; transform-box: fill-box; transform-origin: center; }
.site-art-image { pointer-events: none; image-rendering: auto; }

/* 도시 — 빌딩 수와 높이가 레벨마다 늘어난다. */

/* 농장 — 흙, 새싹, 성숙 작물, 수확기와 저장 시설 순서로 채운다. */

/* 광산 — 노두에서 갱도·광차·지지대·권양탑으로 확장된다. */

/* 벌목장 — 숲 일부에서 통나무·제재대·제재소·크레인으로 발전한다. */

/* 수도시설 — 펌프동과 정수조에서 여과조·급수탑(없음 → 1개 → 2개)으로 늘어난다. */

/* 새로 생긴 것은 한 번 부풀었다 가라앉는다. 어디가 바뀌었는지 눈이 따라가게. */
.map-tile.is-appearing,
.site-shape.is-appearing { animation: pop .65s ease-out; transform-box: fill-box; transform-origin: center; }
@keyframes pop {
  0% { opacity: 0; transform: scale(.4); }
  60% { opacity: 1; transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* ------------------------------------------------------------------ 도로 (47장) */

/*
  도로는 **칸 위**에 있다(2026-08-27에 경계선에서 옮겼다).

  칸을 통째로 칠하지 않는다. 칠하면 육각형 여섯 개가 늘어선 것으로 보이고 어느 쪽으로
  이어졌는지가 안 보인다. 가운데 마디에서 **이어진 쪽으로만** 팔을 뻗으면 갈림길도
  막다른 길도 생김새로 드러난다. 양쪽 칸이 각자 절반씩 뻗어 가운데에서 만난다.
*/
.road-bed,
.road-shoulder,
.road-arm,
.road-mark {
  fill: none;
  stroke-linecap: butt;
  stroke-linejoin: round;
}
.road-bed { stroke: var(--road-edge); stroke-width: 23; }
.road-shoulder { stroke: var(--road-shoulder); stroke-width: 20; }
.road-arm { stroke-width: 15; }
.road-hub-bed { fill: var(--road-edge); }
.road-hub-shoulder { fill: var(--road-shoulder); }
/* 이음매는 팔 **밑에** 깔린다(roads.js). 꺾이는 자리의 빈 쐐기만 메우고, 노면 위로는
   올라오지 않는다 — 올라오면 칸마다 단추가 달린 것처럼 보인다. */
.road-mark {
  stroke: var(--road-line); stroke-width: 1.45; stroke-linecap: round;
  stroke-dasharray: 4 5; opacity: .82;
}

/* 놓는 중 — 아직 확정 전이라는 것을 옅은 색과 흐르는 점선으로 말한다.
   건설 자리 표시(46.3)와 같은 말투다: 점선이 흐르면 "지금 고르는 중"이 잡힌다. */
.road-tile.is-pending .road-bed { stroke: #8f7555; opacity: .42; }
.road-tile.is-pending .road-shoulder { stroke: #f0cf9b; opacity: .52; }
.road-tile.is-pending .road-arm { stroke: #817870; opacity: .52; }
.road-tile.is-pending .road-hub-bed { fill: #8f7555; opacity: .42; }
.road-tile.is-pending .road-hub-shoulder { fill: #f0cf9b; opacity: .52; }
.road-tile.is-pending .road-hub-face { opacity: .52; }

/* 손으로 굽힌 자리(47.6). 잡아 끌면 여기가 따라오고, 톡 누르면 풀린다.
   집을 수 있는 것이라는 말은 **또렷한 손잡이**로 한다 — 나머지 점선 마디보다 진하다. */
.road-tile.is-pending.is-bend .road-hub {
  fill: #fff3dc; fill-opacity: .96;
  stroke: #c8752c; stroke-width: 2.8; stroke-dasharray: none; animation: none;
}
.road-tile.is-pending .road-mark { opacity: .38; }
.road-tile.is-pending .road-hub {
  fill: #817870; fill-opacity: .52;
  stroke: #fff1d8; stroke-width: 1.8; stroke-dasharray: 4 4;
  vector-effect: non-scaling-stroke; animation: ghost-march 1.1s linear infinite;
}

/* ------------------------------------------------------------------ 트럭 (48장) */

/* 길 위를 달리는 것뿐이라 눌리지 않는다. 시설 층보다 아래라 건물 그림 밑으로 드나든다. */
.truck-layer { pointer-events: none; }
.truck { filter: drop-shadow(0 2px 2px rgb(0 0 0 / .35)); }

/* -------------------------------------------------- 지붕 위에 쌓인 것 (49장) */

/* 건물 위에 얹히는 표시라 누르지 않는다. 누르면 그 밑의 건물이 골라져야 한다. */
.stock-slot { pointer-events: none; }
.stock-bit {
  pointer-events: none;
  filter: drop-shadow(0 1.5px 2px rgb(0 0 0 / .45));
  transform-box: fill-box; transform-origin: center;
}
/* 다 된 한 개만 튀어 오른다. 다 같이 튀면 무엇이 늘었는지 알 수 없다. */
.stock-bit.is-new { animation: stock-pop .42s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes stock-pop {
  0%   { opacity: 0; transform: scale(.3) translateY(6px); }
  60%  { opacity: 1; transform: scale(1.25) translateY(0); }
  100% { opacity: 1; transform: scale(1); }
}

/* 만드는 중인 한 개 — 그림은 아직 흐리고, 둘레의 고리가 다 돌면 완성된다(49.1).
   흐린 그림이 "자리는 잡았지만 아직 아니다"를, 도는 고리가 "얼마나 남았나"를 말한다. */
.stock-bit.is-making .stock-icon { opacity: .55; }
.stock-track {
  fill: rgb(16 24 26 / .3); stroke: rgb(255 255 255 / .16); stroke-width: 2.4;
}
.stock-fill {
  fill: none; stroke: #ffc978; stroke-width: 2.6; stroke-linecap: round;
  transition: stroke-dashoffset .1s linear;
}

/* ------------------------------------------- 길을 놓을 수 없는 곳 (49.7) */

/* 도착으로 고를 수 없는 시설은 **눌러 보기 전에** 흐림으로 먼저 말한다. */
.site-shape.is-noroad { opacity: .45; stroke-dasharray: 6 5; }
.site-art-slot.is-noroad { opacity: .3; }

/* 안 되는 이유. 누른 그 자리 위에 뜨고 두어 초 뒤 스스로 사라진다. */
.road-notice {
  pointer-events: none;
  animation: notice-in .16s ease-out, notice-out .5s ease-in 2.1s forwards;
}
.road-notice-plate {
  fill: rgb(23 32 33 / .93); stroke: rgb(226 122 92 / .6); stroke-width: 1.3;
  vector-effect: non-scaling-stroke;
}
.road-notice-text {
  fill: #f6e7dc; font-size: 12px; font-weight: 700; letter-spacing: -.02em;
  text-anchor: middle; dominant-baseline: central;
}

/* ------------------------------------------------------------------ 코치 한 줄 */
/* 가르치기(56장). 안내 알약과 **색으로** 구별한다 — 안내는 어둡고, 코치는 주황이다.
   생김새는 안내 알약을 그대로 따르되, 두 줄이 들어가고 더 오래 머문다. */

/* 이름이 coach-pill인 이유: 아래쪽에 옛 튜토리얼 카드가 쓰던 .coach가 아직 남아 있다
   (js/tutorial.js는 내려 두었지만 마크업과 규칙은 그대로다, 기획서 40.2). 겹치면
   그쪽 display:flex가 이 SVG 그룹에 얹힌다. */
.coach-pill { pointer-events: none; animation: coach-in .2s ease-out; }

.coach-pill-plate {
  fill: rgb(224 140 74 / .96); stroke: rgb(255 224 186 / .8); stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
}
.coach-pill-text {
  fill: #20160a; font-size: 12px; font-weight: 800; letter-spacing: -.02em;
  text-anchor: middle; dominant-baseline: central;
}
@keyframes coach-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .coach-pill { animation: none; } }
@keyframes notice-in { from { opacity: 0; } }
@keyframes notice-out { to { opacity: 0; } }

/* ------------------------------------------------------------------ 클릭 유도 */

.tap-ring {
  fill: none; stroke: #ffffff; stroke-width: 3; vector-effect: non-scaling-stroke;
  transform-box: fill-box; transform-origin: center; animation: ping 1.4s ease-out infinite;
}
.tap-dot { fill: rgb(255 255 255 / .9); animation: blink 1.4s ease-in-out infinite; }
@keyframes ping {
  0% { opacity: .95; transform: scale(.55); }
  100% { opacity: 0; transform: scale(1.35); }
}
@keyframes blink { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

.tool-button.is-cued { animation: cue 1.2s ease-in-out infinite; }
@keyframes cue {
  0%, 100% { box-shadow: 0 4px 14px rgb(10 18 19 / .38), 0 0 0 0 rgb(212 124 50 / .7); }
  50% { box-shadow: 0 4px 14px rgb(10 18 19 / .38), 0 0 0 9px rgb(212 124 50 / 0); }
}

/* ------------------------------------------------------------------ 도구 열 */

.tool-rail {
  position: absolute; z-index: 3;
  top: calc(env(safe-area-inset-top) + 14px); right: calc(env(safe-area-inset-right) + 14px);
  display: flex; flex-direction: column; gap: 9px;
}
.tool-button {
  width: 38px; aspect-ratio: 1; padding: 0;
  display: grid; place-items: center;
}
.tool-button[hidden] { display: none; } /* display:grid가 UA의 [hidden] 규칙을 덮는 함정 방지 */
.tool-button {
  border: 2px solid var(--station); border-radius: 50%;
  color: #ffdcb4; background: rgb(23 32 33 / .82);
  box-shadow: 0 4px 14px rgb(10 18 19 / .38), inset 0 0 0 2px rgb(255 255 255 / .07);
  cursor: pointer; font-weight: 800; line-height: 1;
  transition: transform .12s, background .12s, color .12s;
}
.tool-button.is-guide { font-size: 19px; }
.tool-button:hover { color: #fff2e0; background: rgb(212 124 50 / .92); transform: scale(1.06); }
.tool-button:active { transform: scale(.96); }
.tool-button.is-active { color: #2c1f12; background: var(--station); }
.tool-button.is-appearing { animation: drop .6s ease-out; }
@keyframes drop {
  0% { opacity: 0; transform: translateY(-14px) scale(.6); }
  100% { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ 자원 현황 */

/*
  자원 현황은 **오른쪽 기둥 맨 위부터** 세로로 쌓는다.
  가로로 늘어놓으면 칩이 늘어날수록 화면 가운데의 시간 HUD를 향해 뻗어 나가
  결국 겹친다. 세로로 쌓으면 몇 개가 되든 오른쪽 기둥 안에서 끝난다.

  예전에는 ! 버튼 한 칸(38px)을 비워 두고 그 아래에서 시작했다. 튜토리얼을 내리면서
  ! 버튼이 사라져 그 자리가 빈 여백으로만 남았다(2026-08-27). 이제 맨 위부터 시작하고,
  도구 버튼이 다시 생기면 바로 아래 규칙이 알아서 한 칸 내려 준다 — 자리를 미리
  비워 두는 대신 **있을 때만** 비운다.

  숫자만 보여 주는 표시이므로 클릭을 먹지 않는다 — 뒤쪽 지도를 계속 만질 수 있어야 한다.
*/
.resource-bar {
  position: absolute; z-index: 3; pointer-events: none;
  top: calc(env(safe-area-inset-top) + 14px);
  right: calc(env(safe-area-inset-right) + 14px);
  display: flex; flex-direction: column; gap: 7px;
}
/* 보이는 도구 버튼이 하나라도 있으면 그 열 아래로 내려간다. */
.map-stage:has(.tool-button:not([hidden])) .resource-bar {
  top: calc(env(safe-area-inset-top) + 14px + 38px + 14px);
}
/* display를 지정하면 브라우저 기본 [hidden] 규칙을 덮어쓴다. 직접 다시 숨겨야 한다.
   34.7에 적어 둔 함정이고, 코치 카드에서 실제로 한 번 겪었다. */
.resource-bar[hidden] { display: none; }

/*
  기둥 접기 (50장). 접으면 **아이콘만** 남는다 — 숫자와 이름이 함께 접히는 것이 아니라
  이름만 접힌다. 자원 칩은 숫자가 곧 내용이라 그것까지 접으면 볼 것이 없어진다.

  버튼은 기둥 맨 위, 칩들보다 오른쪽 끝에 맞춰 선다. 접힌 뒤에도 자리가 흔들리지 않게
  폭을 고정해 둔다.
*/
.rail-toggle {
  pointer-events: auto; cursor: pointer; align-self: flex-end;
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 22px; margin-bottom: 1px;
  border: 1px solid rgb(255 255 255 / .1); border-radius: 999px;
  color: #9ba4a2; background: rgb(23 32 33 / .72);
  font-size: 13px; font-weight: 800; line-height: 1; letter-spacing: -.05em;
  transition: color .12s, border-color .12s, transform .12s;
}
.rail-toggle:hover { color: #eceade; border-color: rgb(255 255 255 / .28); }
.rail-toggle:active { transform: scale(.94); }
.resource-bar[data-open="false"] .resource-name,
.resource-bar[data-open="false"] .build-name { display: none; }

/* ---------------------------------------------------------------- 게임 시간 */

/*
  날짜와 배속을 **한 알약 안에 가로로 나란히** 둔다(2026-08-27).

  위아래로 쌓으면 가운데 기둥만 두 줄(38+7+28=73px)을 차지해, 맨 위에 붙인
  인스펙터·자원과 높이가 어긋나 상단이 들쭉날쭉해 보인다. 한 줄로 두면 왼쪽·가운데·
  오른쪽 세 기둥이 모두 같은 높이에서 시작하고, 미디어 플레이어처럼 한 덩어리로 읽힌다.

  좁은 화면에서도 한 줄이다. 대신 거기서는 알약이 작아지고, 인스펙터가 열려 있는 동안
  시계가 오른쪽 끝으로 가고 자원 칩이 한 줄 아래로 내려간다(아래 미디어 쿼리).
*/
.time-hud {
  position: absolute; z-index: 3; pointer-events: none;
  top: calc(env(safe-area-inset-top) + 14px);
  left: 50%; transform: translateX(-50%);
  height: 38px; padding: 0 6px 0 9px;
  display: flex; align-items: center; gap: 8px;
  border: 1px solid rgb(255 255 255 / .1); border-radius: 999px;
  background: rgb(23 32 33 / .82);
  box-shadow: 0 4px 14px rgb(10 18 19 / .3), inset 0 0 0 1px rgb(255 255 255 / .05);
}
.time-date {
  margin: 0;
  display: flex; align-items: center; gap: 7px;
  color: #eceade; font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums;
}
/* 하루 진행 파이. 반지름 8에 선 굵기 16이면 원판이 꽉 찬다(파이 트릭).
   dasharray를 time.js가 매 프레임 채운다. */
.time-pie { display: block; width: 24px; height: 24px; flex: none; }
.time-pie svg { display: block; width: 100%; height: 100%; }
.pie-track, .pie-fill {
  fill: none; stroke-width: 16;
  transform: rotate(-90deg); transform-origin: center;   /* 12시에서 시작 */
}
/* 바탕이 주황(남은 하루), 지난 시간이 어둡게 차오른다 */
.pie-track { stroke: var(--station); stroke-dasharray: 50.3 50.3; }
.pie-fill { stroke: rgb(23 32 33 / .88); }
/* 진행 중 이펙트 — 점선 고리가 돈다. 도는 속도는 배속을 따라간다(time.js가 정한다). */
.pie-ring {
  fill: none; stroke: rgb(236 234 222 / .55); stroke-width: 1.6;
  stroke-dasharray: 3.5 4.4; stroke-linecap: round;
  transform-origin: center;
  animation: time-ring-spin 6s linear infinite;
  filter: drop-shadow(0 0 2px rgb(212 124 50 / .5));
}
@keyframes time-ring-spin { to { transform: rotate(360deg); } }

/* 같은 알약 안이라 배경을 다시 깔지 않는다. 옅은 세로선 하나로 날짜와 갈린다. */
.time-controls {
  pointer-events: auto;
  display: flex; align-items: center; gap: 2px;
  padding-left: 8px; border-left: 1px solid rgb(255 255 255 / .14);
}
.time-speed {
  min-width: 30px; text-align: center;
  color: #eceade; font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.time-button {
  width: 22px; aspect-ratio: 1; padding: 0;
  display: grid; place-items: center;
  border: none; border-radius: 50%;
  color: #eceade; background: transparent;
  font: inherit; font-size: 15px; font-weight: 800; line-height: 1;
  cursor: pointer;
}
.time-button svg { display: block; width: 13px; height: 13px; fill: currentColor; }
.time-button:hover:not(:disabled) { color: #2c1f12; background: var(--station); }
.time-button:disabled { opacity: .3; cursor: default; }

/* 꺼낼 때는 ! 버튼에 가까운(위쪽) 칩부터 차례로 떨어진다. */
.resource-bar.is-appearing .resource { animation: drop .55s ease-out backwards; }
.resource-bar.is-appearing .resource:nth-child(2) { animation-delay: .07s; }
.resource-bar.is-appearing .resource:nth-child(3) { animation-delay: .14s; }
.resource-bar.is-appearing .resource:nth-child(4) { animation-delay: .21s; }
/* 칩은 **누를 수 있다.** 눌러 보면 그 자원이 무엇에 드는지 창이 뜬다(37.10).
   막대(.resource-bar)는 클릭을 먹지 않고 칩만 먹는다 — 칩 사이 빈틈으로는 지도를 만진다. */
.resource {
  pointer-events: auto; cursor: pointer;
  display: flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 11px 0 9px;
  border: 1px solid rgb(255 255 255 / .1); border-radius: 999px;
  color: #eceade; background: rgb(23 32 33 / .82);
  box-shadow: 0 4px 14px rgb(10 18 19 / .3), inset 0 0 0 1px rgb(255 255 255 / .05);
  transition: transform .12s, border-color .12s;
}
.resource:hover { border-color: rgb(212 124 50 / .8); transform: translateX(-2px); }
.resource:active { transform: scale(.97); }
/* 고른 자원은 창에 열려 있는 것과 같은 것이라고 알려 준다. */
.resource.is-selected { border-color: var(--station); box-shadow: 0 4px 14px rgb(10 18 19 / .3), inset 0 0 0 1px var(--station); }
.resource-icon {
  display: block; width: 22px; height: 22px; flex: none; object-fit: contain;
  filter: drop-shadow(0 1px 2px rgb(0 0 0 / .38));
}
.resource-value {
  font-size: 14px; font-weight: 800;
  /* 자릿수가 바뀌어도 칩이 덜컹거리지 않게 숫자 폭을 고정한다. */
  font-variant-numeric: tabular-nums;
  /* 자간을 좁히지 않는다. 1.25K의 소수점이 붙어 버리면 1,25K이나 125K로 읽힌다. */
  letter-spacing: 0;
}
/* 세로로 서면 이름 위치가 제각각이라 지저분하다. 오른쪽 끝으로 민다. */
.resource-name { margin-left: auto; color: #9ba4a2; font-size: 11px; font-weight: 600; letter-spacing: -.03em; }

/* 값이 달라진 순간만 한 번 튕긴다. 어느 자원이 늘었는지 눈이 따라갈 수 있어야 한다. */
.resource-value.is-bumped { animation: bump .5s ease-out; }
@keyframes bump {
  0% { transform: none; }
  30% { transform: translateY(-3px) scale(1.16); color: #ffd39a; }
  100% { transform: none; }
}

/* -------------------------------------------------------- 건설 칩 (46.1) */

/*
  자원 칩 아래에 건물 셋이 붙는다. **간격만 넓혀** 둘을 가른다 —
  가진 것 바로 아래에 그것으로 지을 수 있는 것이 오는 편이 읽기 쉽고,
  선이나 제목을 넣으면 오른쪽 모서리가 무거워진다.
*/
.build-bar { display: flex; flex-direction: column; gap: 7px; margin-top: 15px; }
.build {
  pointer-events: auto; cursor: pointer;
  display: flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 11px 0 8px;
  border: 1px solid rgb(212 124 50 / .35); border-radius: 999px;
  color: #f3ead9; background: rgb(46 33 22 / .84);
  box-shadow: 0 4px 14px rgb(10 18 19 / .3), inset 0 0 0 1px rgb(255 255 255 / .05);
  font-size: 12px; font-weight: 700; letter-spacing: -.02em;
  transition: transform .12s, border-color .12s;
}
.build:hover { border-color: var(--station); transform: translateX(-2px); }
.build:active { transform: scale(.97); }
.build.is-selected { border-color: var(--station); box-shadow: 0 4px 14px rgb(10 18 19 / .3), inset 0 0 0 1px var(--station); }

/* 코치가 "이걸 고르세요"라고 부르는 동안 (56장). 원재료가 지도에서 튀는 것과 같은
   신호를 오른쪽 기둥에서도 쓴다 — 통통 튀는 것은 "누르는 것"이라는 이 게임의 약속이다. */
.build.is-inviting, .level-up.is-inviting {
  border-color: #ffc45c;
  box-shadow: 0 0 0 2px rgb(255 196 92 / .85), 0 0 16px rgb(255 196 92 / .45);
  animation: build-call .82s ease-in-out infinite;
}
@keyframes build-call {
  0%, 100% { transform: scale(1); }
  44%      { transform: scale(1.09); }
}
@media (prefers-reduced-motion: reduce) {
  .build.is-inviting, .level-up.is-inviting { animation: none; }
}
/* "+"가 맨 앞이라 세는 칩이 아니라 **더하는 버튼**으로 읽힌다. */
.build-plus { color: var(--station); font-size: 15px; font-weight: 800; line-height: 1; }
.build-icon { display: block; width: 20px; height: 20px; flex: none; object-fit: contain; filter: drop-shadow(0 1px 2px rgb(0 0 0 / .38)); }
/* 이름은 **아이콘 바로 옆**이다. 오른쪽 끝으로 밀어 두면 그림과 이름이 한 덩어리로
   읽히지 않고, 칩 안에서 눈이 두 번 움직인다. */
.build-name { margin-left: 1px; }
/* 도로 칩은 아이콘이 없어 글자 대신 그림을 그린다 — 위에서 본 길과 가운데선. */
.road-glyph { display: block; }
.road-glyph-bed { fill: var(--road); }
.road-glyph-line { fill: none; stroke: #fff3dd; stroke-width: 1.7; stroke-linecap: round; }

/* ------------------------------------------------------------------ 인스펙터 */

.inspector {
  position: absolute; z-index: 3;
  top: calc(env(safe-area-inset-top) + 14px); left: calc(env(safe-area-inset-left) + 14px);
  width: 208px; padding: 13px 15px 15px;
  border-radius: 13px; color: var(--ink); background: rgb(241 240 231 / .96);
  box-shadow: var(--shadow); animation: slide .25s ease-out;
}
@keyframes slide { from { opacity: 0; transform: translateX(-10px); } }
/* 창 아무 곳이나 눌러 접는다. 손가락으로 작은 버튼을 겨누지 않아도 되게. */
.inspector { cursor: pointer; }

.inspector-fold {
  position: absolute; top: 7px; right: 7px;
  width: 22px; height: 22px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid #cdcec2; border-radius: 7px;
  color: var(--muted); background: rgb(255 255 255 / .7);
  cursor: pointer; font-size: 15px; font-weight: 800; line-height: 1;
}
.inspector-fold:hover { color: var(--ink); background: #ffffff; }

/* 접힌 인스펙터. 창이 있던 자리에 창 모양 그대로 작아진다. */
.inspector-open {
  position: absolute; z-index: 3;
  top: calc(env(safe-area-inset-top) + 14px); left: calc(env(safe-area-inset-left) + 14px);
  width: 38px; aspect-ratio: 1; padding: 0;
  display: grid; place-items: center;
  border: 2px solid var(--station); border-radius: 11px;
  background: rgb(23 32 33 / .82);
  box-shadow: 0 4px 14px rgb(10 18 19 / .38), inset 0 0 0 2px rgb(255 255 255 / .07);
  cursor: pointer; animation: slide .2s ease-out;
  transition: transform .12s, background .12s;
}
.inspector-open[hidden] { display: none; }
.inspector-open:hover { background: rgb(212 124 50 / .92); transform: scale(1.06); }
.inspector-open:active { transform: scale(.96); }
.inspector-open svg { width: 22px; height: 22px; }
.inspector-open .frame { fill: none; stroke: #ffdcb4; stroke-width: 1.6; }
.inspector-open .row { stroke: #ffdcb4; stroke-width: 1.6; stroke-linecap: round; opacity: .75; }
.inspector-open .row.is-head { stroke: var(--station); opacity: 1; stroke-width: 2.2; }
.inspector-tag { margin: 0; color: var(--station); font-size: 10px; font-weight: 800; letter-spacing: .07em; }
.inspector-title { margin: 3px 0 9px; font-size: 17px; letter-spacing: -.035em; }
/* 제목 옆 그림. 무엇에 대한 창인지 글자보다 먼저 읽힌다. */
.inspector-title-icon { width: 22px; height: 22px; object-fit: contain; vertical-align: -4px; margin-right: 5px; }
/* 한 문장 설명. 숫자보다 먼저 온다 — "이게 뭔데?"가 풀려야 숫자가 읽힌다. */
.inspector-lead { margin: -4px 0 10px; color: var(--muted); font-size: 11.5px; line-height: 1.55; }
.inspector-lead[hidden] { display: none; }
/* 묶음 사이에만 옅은 선을 둔다. 줄마다 선을 그으면 표가 되고, 표는 읽는 데 힘이 든다. */
.inspector-stats { display: flex; flex-direction: column; font-size: 12px; }
.inspector-group { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 4px 10px; }
.inspector-group + .inspector-group { margin-top: 9px; padding-top: 9px; border-top: 1px solid #dcddd2; }
.inspector-group dt { color: var(--muted); }
.inspector-group dd { margin: 0; font-weight: 700; text-align: right; }
.inspector-group dd.is-warn { color: #b0521c; }
/* 비용 줄 — 가진 것 / 드는 것. 모자란 쪽만 붉다(46.2). 드는 쪽은 늘 같은 값이라 흐리게. */
.inspector-group /* 창 안에서 바로 고르는 단추 (도로의 놓는 방식 등). 지금 고른 것 하나만 진하다. */
dd.has-picks { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.pick {
  border: 1px solid #d8c3a0; background: #fffaf1; color: #6b5540;
  border-radius: 999px; padding: 3px 10px; font-size: .82rem; font-weight: 700;
  cursor: pointer; transition: background .15s, color .15s, border-color .15s;
}
.pick:hover { background: #f6e7cf; }
.pick.is-on { background: #c8752c; border-color: #a75f20; color: #fff8ec; }

dd.has-cost { font-variant-numeric: tabular-nums; }
.cost-have { font-weight: 800; }
.cost-have.is-short { color: #c3352a; }
.cost-need { color: var(--muted); }
/* 사용처 — 그림 + 이름을 한 줄씩 쌓는다. 이름만 적으면 아직 본 적 없는 시설은 글자일 뿐이다. */
.inspector-group dd.has-uses { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.use { display: flex; align-items: center; gap: 5px; }
.use-art { width: 30px; height: 30px; object-fit: contain; flex: none; }
/* 이름 옆 아이콘. inline 이미지라 글 흐름·줄바꿈·오른쪽 정렬이 그대로 살아 있다.
   글자만 있을 때보다 어느 줄이 무엇에 대한 것인지 훨씬 빨리 잡힌다. */
.inspector-icon { width: 15px; height: 15px; object-fit: contain; vertical-align: -3px; margin-right: 1px; }
/* 트럭 그림은 사방이 넓게 비어 있어 같은 상자에 넣으면 절반 크기로 보인다. 상자를 키운다. */
.inspector-icon[data-icon="truck-icon"] { width: 26px; height: 26px; vertical-align: -8px; margin-right: 0; }
/* 발전 단계 줄의 도시 그림. 값 왼쪽에 붙어 "지금 이 모습"을 글보다 먼저 보여 준다. */
/* 아트 원본이 정사각(384×384)이라 납작한 칸에 넣으면 높이에 맞춰 작아진다. 칸도 정사각으로 둔다. */
.inspector-art { width: 36px; height: 36px; object-fit: contain; vertical-align: -12px; margin-right: 4px; }
/* 자원 상태 줄 — 지도 위 배지와 같은 생김새(어두운 판 + 아이콘 + 상태 색 테두리). */
.inspector-group dd.has-badges { display: flex; justify-content: flex-end; gap: 4px; }
.need-dot {
  width: 22px; height: 22px; flex: none;
  display: grid; place-items: center;
  border: 2px solid; border-radius: 50%; background: rgb(23 32 33 / .9);
}
.need-dot img { width: 14px; height: 14px; object-fit: contain; }
.need-dot.is-ok { border-color: #4fbe63; }
.need-dot.is-warn { border-color: #f0a03a; }
.need-dot.is-bad, .need-dot.is-danger { border-color: #e2503f; }
/* 위험 — 지도 위 배지처럼 둘레가 퍼지고 테두리가 한 번 밝아진다. */
.need-dot.is-danger { animation: dot-alarm 1.15s ease-in-out infinite; }
@keyframes dot-alarm {
  0%       { box-shadow: 0 0 0 0 rgb(226 80 63 / .55); border-color: #e2503f; }
  45%      { border-color: #ff9083; }
  70%, 100% { box-shadow: 0 0 0 7px rgb(226 80 63 / 0); border-color: #e2503f; }
}
.inspector-note { margin: 10px 0 0; padding-top: 9px; border-top: 1px solid #d5d6cb; color: var(--muted); font-size: 11px; line-height: 1.55; }
.inspector-note b { color: #8a4a12; }

/* --------------------------------------------------- 도시 위 자원 상태 배지 (43.9) */

/* SVG 도형은 transform-box를 fill-box로 두어야 자기 자신을 기준으로 커진다.
   기본값(view-box)이면 지도 원점을 기준으로 커져서 화면 밖으로 날아간다. */
.need-badge, .badge-halo { transform-box: fill-box; transform-origin: center; }
.need-badge { pointer-events: none; }

/*
  아이콘을 받치는 **동그란 판**과 그 둘레.

  일은 둘로 나뉜다 — 아이콘은 *무슨 자원인지*, 테두리는 *그 자원이 어떤 상태인지*.
  판 전체를 상태 색으로 칠하면 아이콘 그림과 색이 뒤섞여 둘 다 흐려진다.
  어두운 판은 어떤 색 아이콘(빵 상자·물방울·번개)이든 또렷하게 받쳐 준다.
*/
.badge-plate { fill: rgb(23 32 33 / .9); stroke-width: 3; }
.badge-icon { filter: drop-shadow(0 1px 1.5px rgb(0 0 0 / .45)); }

/* 평소에는 보이지 않는다. 위험일 때만 커졌다 작아지며 눈에 띈다. */
.badge-halo { fill: none; stroke-width: 2.4; opacity: 0; }

.need-badge.is-ok .badge-plate { stroke: #4fbe63; }
.need-badge.is-warn .badge-plate { stroke: #f0a03a; }
.need-badge.is-bad .badge-plate,
.need-badge.is-danger .badge-plate { stroke: #e2503f; }

/* 위험 — 심각과 같은 붉은색이되 둘레가 커졌다 작아지고, 테두리가 한 번 밝아졌다 돌아온다. */
.need-badge.is-danger .badge-halo { stroke: #e2503f; animation: badge-alarm 1.15s ease-in-out infinite; }
.need-badge.is-danger .badge-plate { animation: badge-flash 1.15s ease-in-out infinite; }
@keyframes badge-alarm {
  0%   { opacity: .55; transform: scale(1); }
  70%  { opacity: 0;   transform: scale(1.9); }
  100% { opacity: 0;   transform: scale(1.9); }
}
@keyframes badge-flash {
  0%, 100% { stroke: #e2503f; }
  45%      { stroke: #ff9083; }
}

/*
  노동자 칩 — 배지 줄 왼쪽 아래에 붙는 작은 알약.
  상태 색을 쓰지 않는다. 위험 배지의 붉은 알람과 색으로 경쟁하면 둘 다 안 보인다.
*/
.worker-chip { pointer-events: none; }
.chip-plate { fill: rgb(23 32 33 / .9); stroke: rgb(255 255 255 / .22); stroke-width: 1.2; }
.chip-icon { filter: drop-shadow(0 1px 1.5px rgb(0 0 0 / .45)); }
.chip-text {
  fill: #eceade; font-size: 10.5px; font-weight: 800; letter-spacing: -.02em;
  dominant-baseline: central;
}

/* 단계가 바뀐 순간에만 한 번 부풀었다 가라앉는다. 무엇이 달라졌는지 눈이 따라간다. */
.need-badge.is-popping { animation: badge-pop .45s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes badge-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.55); }
  100% { transform: scale(1); }
}

/* ------------------------------------------- 지도에 나타나는 원재료 (45장) */

/* 층은 클릭을 먹지 않고 아이콘만 먹는다. 아이콘 사이 빈 곳으로는 지도를 만진다. */
.pickup-layer { pointer-events: none; }
.pickup {
  pointer-events: auto; cursor: pointer;
  transform-box: fill-box; transform-origin: center;
}
/* 커졌다 작아지는 것은 **아이콘만** 한다. 지도 위에서 "이건 누르는 것"이라고 말하는
   신호다. 남은 시간 고리까지 같이 커지면 눈금이 흔들려 얼마 남았는지 못 읽는다. */
.pickup-icon {
  transform-box: fill-box; transform-origin: center;
  animation: pickup-bob 1.4s ease-in-out infinite;
  filter: drop-shadow(0 2px 3px rgb(0 0 0 / .5));
}
@keyframes pickup-bob {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.2); }
}
.pickup:hover { filter: brightness(1.12); }
.pickup:focus-visible .pickup-icon { filter: drop-shadow(0 0 4px #e59b45) drop-shadow(0 0 8px #e59b45); }

/* 남은 시간 고리 (45.4). 하루가 지나면 사라지므로 얼마 남았는지 아이콘 밖에 그린다.
   **줄어드는** 쪽으로 그린다 — 여기서 쓰이는 판단은 "얼마 찼나"가 아니라 "얼마 남았나"다. */
.pickup-ring-track, .pickup-ring-fill { fill: none; stroke-width: 3; }
.pickup-ring-track { stroke: rgb(10 18 19 / .34); }
.pickup-ring-fill { stroke: #ffe0ad; stroke-linecap: round; }
/* 마지막 4분의 1 — 고리가 붉어지고 아이콘이 빨리 뛴다. 작은 고리는 길이만으로
   "이제 곧"이 안 읽혀서, 색과 빠르기를 두 번째 신호로 얹는다. */
.pickup.is-urgent .pickup-ring-fill { stroke: #ff7a52; }
.pickup.is-urgent .pickup-icon { animation-duration: .55s; }

/* 코치가 "이게 원재료다"라고 부르는 동안 (56장). 평소의 통통거림보다 **크고 빠르게**
   튀고, 머리 위에 이름표를 단다. 말풍선이 "눌러서 주우세요"라고 해도 무엇을 누르라는
   것인지 눈이 못 찾으면 소용이 없다. 부르는 동안에는 남은 시간도 멈춰 있다. */
.pickup.is-inviting .pickup-icon {
  animation: pickup-call .72s ease-in-out infinite;
  filter: drop-shadow(0 0 6px #ffc45c) drop-shadow(0 2px 3px rgb(0 0 0 / .5));
}
@keyframes pickup-call {
  0%, 100% { transform: scale(1); }
  40%      { transform: scale(1.55); }
  62%      { transform: scale(1.34); }
}
.pickup.is-inviting .pickup-ring-fill { stroke: #ffc45c; }

.pickup-call { pointer-events: none; animation: pickup-call-in .22s ease-out; }
.pickup-call-plate {
  fill: rgb(23 32 33 / .93); stroke: rgb(255 196 92 / .65); stroke-width: 1.2;
  vector-effect: non-scaling-stroke;
}
.pickup-call-text {
  fill: #ffe9cd; font-size: 12px; font-weight: 800; letter-spacing: -.02em;
  text-anchor: start; dominant-baseline: central;
}
@keyframes pickup-call-in { from { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .pickup.is-inviting .pickup-icon { animation-duration: 2.4s; }
}

/* 시간이 다 되어 스러진다. 주울 때와 달리 아무것도 날아가지 않는다 —
   창고가 늘지 않았다는 것이 눈에도 그렇게 보여야 한다. */
.pickup.is-expiring {
  pointer-events: none; opacity: 0; transform: scale(.55);
  transition: opacity .32s ease-in, transform .32s ease-in;
}

/* 칩으로 날아가는 아이콘. 지도(SVG)와 칩(HTML)은 좌표계가 달라 화면 좌표로 띄운다.
   날아가는 길(포물선 + 커졌다 작아지기)은 pickups.js가 키프레임으로 그린다 —
   CSS 전환은 두 점 사이를 직선으로만 이어서 곡선을 만들 수 없다. */
.pickup-fly {
  position: absolute; z-index: 5; pointer-events: none;
  object-fit: contain; filter: drop-shadow(0 3px 6px rgb(0 0 0 / .45));
  transition: opacity .22s linear;
}
.pickup-fly.is-arrived { opacity: 0; }

/* 얼마가 늘어나는지 그 자리에서 알린다. 떠오르며 사라진다. */
.pickup-gain {
  position: absolute; z-index: 5; pointer-events: none;
  color: #ffe9cd; font-size: 16px; font-weight: 800; letter-spacing: -.02em;
  text-shadow: 0 2px 7px rgb(10 18 19 / .85), 0 0 2px rgb(10 18 19 / .9);
  animation: pickup-gain 1.5s ease-out forwards;
}
@keyframes pickup-gain {
  0%   { opacity: 0; transform: translate(-50%, -70%) scale(.8); }
  16%  { opacity: 1; transform: translate(-50%, -105%) scale(1); }
  68%  { opacity: 1; transform: translate(-50%, -135%); }
  100% { opacity: 0; transform: translate(-50%, -170%); }
}

/* ------------------------------------------------- 건설할 자리 (46.3~46.5) */

/* 층은 클릭을 먹지 않는다. 확정·취소 버튼만 먹는다 — 나머지 지도는 자리를 고르는 데 쓴다. */
.ghost-layer { pointer-events: none; }

/*
  아직 세운 것이 아니라는 것을 **점선과 반투명**으로 말한다.
  점선이 흐르면 "지금 고르는 중"이 한눈에 잡힌다.
*/
.ghost-shape {
  fill: rgb(126 208 150 / .3); stroke: #86e3a6; stroke-width: 2.6;
  stroke-dasharray: 8 6; vector-effect: non-scaling-stroke;
  animation: ghost-march 1.1s linear infinite;
}
@keyframes ghost-march { to { stroke-dashoffset: -14; } }
.ghost-art { opacity: .6; }

/* 못 짓는 자리는 붉다(46.4). 그림도 함께 흐려져, 색만 바뀐 것이 아니라는 것이 보인다. */
.ghost.is-blocked .ghost-shape { fill: rgb(226 80 63 / .32); stroke: #ff9484; }
.ghost.is-blocked .ghost-art { opacity: .32; filter: grayscale(.65); }

.ghost-act, .ghost-cancel { pointer-events: auto; cursor: pointer; }
.ghost.is-blocked .ghost-act { pointer-events: none; }
.ghost-plate { fill: #37864a; stroke: rgb(255 255 255 / .3); stroke-width: 1.4; vector-effect: non-scaling-stroke; }
.ghost.is-blocked .ghost-plate { fill: #6f4a44; }
.ghost-cancel .ghost-plate { fill: rgb(23 32 33 / .9); }
.ghost-act:hover .ghost-plate, .ghost-cancel:hover .ghost-plate { filter: brightness(1.15); }
.ghost-text {
  fill: #f2fff4; font-size: 14px; font-weight: 800; letter-spacing: -.03em;
  text-anchor: middle; dominant-baseline: central; pointer-events: none;
}
/* ✕는 글자 하나뿐이라 같은 크기로 두면 알약보다 작아 보인다. 한 호 크게 잡는다. */
.ghost-cancel .ghost-text { font-size: 17px; }
/* 안 되는 이유 · 잃는 것. 자리 아래 한 줄이다 — 붉게만 칠하면 왜 붉은지를 추측하게 된다. */
.ghost-why {
  fill: #ffcfc5; font-size: 10.5px; font-weight: 700; letter-spacing: -.03em;
  text-anchor: middle; dominant-baseline: middle; paint-order: stroke;
  stroke: rgb(10 18 19 / .8); stroke-width: 3.4; vector-effect: non-scaling-stroke;
}
.ghost-why.is-cost { fill: #ffe0a8; }

/* ------------------------------------------------------------------ 화면 아래 안내 */

/* 코치 카드와 확인 바를 한 칸에 쌓는다. 둘이 동시에 뜰 때 겹치지 않게. */
.bottom-dock {
  position: absolute; z-index: 4;
  left: 50%; bottom: calc(env(safe-area-inset-bottom) + 18px); transform: translateX(-50%);
  width: min(430px, calc(100% - 28px));
  display: flex; flex-direction: column; gap: 10px;
  pointer-events: none;
}
.bottom-dock > * { pointer-events: auto; }

/* display를 지정하면 브라우저 기본 [hidden] 규칙을 덮어 버린다. 직접 숨겨 준다. */
.coach[hidden] { display: none; }
.coach {
  display: flex; align-items: center; gap: 12px; padding: 13px 15px;
  border-radius: 13px; color: #f2f3ee; background: rgb(23 32 33 / .93);
  box-shadow: 0 10px 26px rgb(8 14 15 / .42);
  animation: rise .25s ease-out;
}
@keyframes rise { from { opacity: 0; transform: translateY(12px); } }

.coach.is-done { background: rgb(38 58 44 / .95); }
.coach-text { flex: 1 1 auto; min-width: 0; }
.coach-title { display: block; font-size: 13px; letter-spacing: -.03em; }
.coach-body { display: block; margin-top: 3px; color: #b9c3c1; font-size: 12px; line-height: 1.5; }
.coach.is-done .coach-body { color: #9fd8b0; }
.coach-button {
  flex: 0 0 auto; min-height: 38px; padding: 0 16px;
  border: 0; border-radius: 9px; color: #2c1f12; background: var(--station);
  cursor: pointer; font-size: 13px; font-weight: 800;
}

/* --------------------------------------------------------------- 레벨업 버튼 */

/* 인스펙터 창 **맨 아래**. 읽고 나서 누르는 자리라 읽는 것들 밑에 둔다.
   창을 채운 유일한 주황 덩어리라 "여기가 누르는 곳"이 한눈에 잡힌다.
   다음 단계의 도시 그림이 왼쪽에 붙어 "무엇이 될지"를 글보다 먼저 보여 준다. */
.level-up {
  width: 100%; margin-top: 11px;
  display: flex; align-items: center; gap: 8px;
  height: 44px; padding: 0 12px 0 6px;
  border: none; border-radius: 10px;
  color: #2c1f12; background: var(--station);
  box-shadow: 0 3px 0 #a95c1c;
  cursor: pointer; font-size: 14px; font-weight: 800; letter-spacing: -.02em;
  transition: transform .12s, filter .12s, box-shadow .12s;
}
.level-up[hidden] { display: none; }   /* display:flex가 UA의 [hidden] 규칙을 덮는 함정 */
.level-up-art { width: 34px; height: 34px; object-fit: contain; flex: none; }
.level-up:hover { filter: brightness(1.06); }
.level-up:active { transform: translateY(2px); box-shadow: 0 1px 0 #a95c1c; }
/* 자원이 모자라면 눌리지 않는다. 사라지게 두지 않는 이유는, 없어진 버튼은
   "무엇을 하면 되는지"까지 같이 가져가기 때문이다(46.2). */
.level-up:disabled { cursor: not-allowed; filter: grayscale(.55) brightness(.86); box-shadow: 0 3px 0 #7d7469; }
.level-up:disabled:active { transform: none; }

/* 트럭 레벨업(48.2)은 건물 레벨업 **바로 아래**에 붙는다. 같은 색으로 두면 둘이 한 덩어리로
   보여 무엇을 올리는지 헷갈린다. 색을 낮춰 "이 다음 것"이라는 것을 색으로도 말한다. */
.truck-up { background: #4a6c7a; box-shadow: 0 3px 0 #33505c; }
.truck-up:active { box-shadow: 0 1px 0 #33505c; }

/* 자리를 고르는 동안 — **눌린 채로 켜져 있는 버튼**이다(46.2).
   튀어나온 그림자를 안으로 넣어 "지금 들어가 있다"를 만든다. 다시 누르면 꺼진다. */
.build-start.is-picking {
  color: #ffe6cd; background: #8a4f1b;
  box-shadow: inset 0 3px 0 rgb(0 0 0 / .3), 0 0 0 2px rgb(212 124 50 / .6);
  transform: translateY(2px);
}
.build-start.is-picking:active { transform: translateY(3px); }

/* ------------------------------------------------------------------ 설명 팝업 */

.guide-popup {
  width: min(430px, 100% - 32px); max-height: min(560px, 100dvh - 40px); padding: 0;
  margin: auto; overflow: auto;
  border: 0; border-radius: 16px;
  color: var(--ink); background: var(--paper); box-shadow: var(--shadow);
}
.guide-popup::backdrop { background: rgb(14 21 22 / .58); }
.guide-popup > * { padding: 0 24px; }

.guide-step { margin: 22px 0 0; color: var(--station); font-size: 11px; font-weight: 800; letter-spacing: .06em; }
.guide-title { margin: 6px 0 0; font-size: 20px; letter-spacing: -.035em; }
.guide-body { margin-top: 14px; }
.guide-body p { margin: 0 0 10px; font-size: 14px; line-height: 1.62; }
.guide-body ul { margin: 0 0 10px; padding-left: 18px; }
.guide-body li { margin-bottom: 6px; font-size: 14px; line-height: 1.55; }
.guide-body b { color: #8a4a12; }
.guide-body .guide-note { color: var(--muted); font-size: 12px; }

/* 장 제목은 본문보다 크게. "여기서부터 다른 이야기"라는 표시다. */
.guide-popup.is-chapter .guide-title { margin-top: 4px; font-size: 27px; }
.guide-popup.is-chapter .guide-step { color: var(--muted); }

.guide-actions {
  position: sticky; bottom: 0;
  display: flex; gap: 9px; padding: 16px 24px 20px;
  background: linear-gradient(rgb(241 240 231 / 0), var(--paper) 40%);
}
.guide-button {
  flex: 1 1 0; min-height: 44px; padding: 0 14px;
  border: 1px solid #c3c7bd; border-radius: 10px;
  background: #fbfaf4; cursor: pointer;
  font-size: 14px; font-weight: 700;
}
.guide-button.is-next { border-color: var(--station); color: #2c1f12; background: var(--station); }
.guide-button.is-next:disabled { border-color: #cfd2c9; color: #9aa2a1; background: #e6e5dc; cursor: default; }
.guide-button.is-close:hover { background: #edece2; }

@media (max-width: 720px) {
  body { background: #172021; }
  .tool-rail { top: calc(env(safe-area-inset-top) + 10px); right: calc(env(safe-area-inset-right) + 10px); gap: 8px; }
  .tool-button { width: 34px; }
  .tool-button.is-guide { font-size: 17px; }
  /* 좁은 화면에서는 이름을 접고 아이콘과 숫자만 남긴다. 정확한 이름은 aria-label에 남아 있다. */
  .resource-bar {
    top: calc(env(safe-area-inset-top) + 10px);
    right: calc(env(safe-area-inset-right) + 10px);
    gap: 5px;
  }
  .map-stage:has(.tool-button:not([hidden])) .resource-bar {
    top: calc(env(safe-area-inset-top) + 10px + 34px + 10px);
  }
  /* 좁은 화면에서는 한 줄이 가로를 다 먹는다. 여기서만 날짜 위 · 배속 아래로 되돌린다. */
  /* 한 줄은 그대로 두고 알약만 줄인다. 좁을수록 가로를 아껴야 옆자리가 산다. */
  .time-hud { top: calc(env(safe-area-inset-top) + 10px); height: 34px; padding: 0 4px 0 8px; gap: 6px; }
  .time-date { font-size: 13px; gap: 6px; }
  .time-controls { padding-left: 6px; }
  .time-speed { min-width: 25px; font-size: 12px; }
  .time-button { width: 20px; }
  .time-button svg { width: 12px; height: 12px; }
  .time-pie { width: 21px; height: 21px; }
  .resource { height: 28px; padding: 0 8px 0 7px; gap: 4px; }
  .resource-icon { width: 19px; height: 19px; }
  .resource-value { font-size: 12px; }
  /* **건물·도로 이름은 좁은 화면에서도 남긴다.** 자원 칩과 달리 이쪽은 누르는 버튼이라
     "무엇을 짓는 것인지"가 그림만으로 읽히지 않는다. 대신 글자를 한 급 줄인다. */
  .build { height: 28px; padding: 0 9px 0 7px; gap: 4px; font-size: 11px; }
  .build-icon { width: 19px; height: 19px; }
  /*
    좁은 화면에서 창이 열리면 가운데가 없어진다. 왼쪽은 인스펙터가 다 쓰기 때문이다.
    그동안만 **시계가 오른쪽 끝으로 가고, 자원 칩이 그 아래로 한 줄 내려간다.**
    창을 닫으면 둘 다 제자리(가운데 · 맨 위)로 돌아온다.

    시계를 줄이거나 두 줄로 접지 않는다 — 시간과 배속은 한 덩어리로 읽혀야 한다.
    대신 자리를 내주는 쪽은 숫자만 보여 주는 자원 칩이다. 한 줄 내려가도 잃는 것이 없다.
  */
  .map-stage:has(#inspector:not([hidden])) .time-hud {
    left: auto; right: calc(env(safe-area-inset-right) + 10px);
    transform: none; z-index: 4;
  }
  .map-stage:has(#inspector:not([hidden])) .resource-bar {
    top: calc(env(safe-area-inset-top) + 10px + 34px + 6px);
  }
  /* 좁은 화면에서도 왼쪽 맨 위부터. 폭은 오른쪽으로 비켜선 시계와 겹치지 않는 선에서 정한다. */
  .inspector { top: calc(env(safe-area-inset-top) + 10px); left: calc(env(safe-area-inset-left) + 10px); width: 176px; padding: 11px 13px 13px; }
  .inspector-open { top: calc(env(safe-area-inset-top) + 10px); left: calc(env(safe-area-inset-left) + 10px); width: 34px; }
  .inspector-open svg { width: 20px; height: 20px; }
  .inspector-title { font-size: 15px; }
  .bottom-dock { bottom: calc(env(safe-area-inset-bottom) + 12px); gap: 8px; }
  .coach { padding: 11px 13px; }
  .guide-popup { width: calc(100% - 24px); max-height: calc(100dvh - 32px); }
  .guide-popup > * { padding: 0 18px; }
  .guide-step { margin-top: 18px; }
  .guide-title { font-size: 18px; }
  .guide-popup.is-chapter .guide-title { font-size: 23px; }
  .guide-actions { padding: 14px 18px 18px; }
}

/*
  아주 좁은 화면(430px 미만).

  가장 긴 날짜("12년 11월 30일")에서 시계 알약은 214px이다. 창 176px과 나란히 두려면
  410px + 여백이 필요하다. 그보다 좁으면 둘이 스친다.

  시간을 줄이거나 두 줄로 접지 않는다 — 날짜와 배속은 한 덩어리로 읽혀야 하고,
  시간은 언제나 읽혀야 한다. 대신 시계가 위로 오고(z-index), 그 자리에 있던
  접기 버튼을 감춘다. 창은 **아무 곳이나 눌러도 접히므로**(34.5) 잃는 기능이 없다.
*/
@media (max-width: 430px) {
  .map-stage:has(#inspector:not([hidden])) .inspector-fold { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ------------------------------------------------------------------ 수도관 (51장) */

/* 땅 밑에 묻힌 것이라 평소에는 흐리다(51.6). 수도시설을 고르면 그 시설의 관만
   또렷해진다(51.7). 홈 → 외피 → 금속 재질 → 물빛 순서로 겹쳐 작은 크기에서도
   도로와 다른 둥근 산업용 배관으로 읽힌다. */
.pipe-layer { pointer-events: none; }
.pipe {
  opacity: .18;
  transition: opacity .3s;
  filter: drop-shadow(0 1.5px 1.5px rgb(8 28 34 / .32));
}
.pipe :is(.pipe-trench, .pipe-bed, .pipe-rim, .pipe-face, .pipe-highlight, .pipe-flow) {
  stroke-linecap: round;
}
.pipe-trench { stroke: #142d33; stroke-width: 16; opacity: .32; }
.pipe-bed { stroke: #17343d; stroke-width: 12; }
.pipe-rim { stroke: #6f929a; stroke-width: 10; }
.pipe-face { stroke: url(#pipe-metal); stroke-width: 8; }
.pipe-highlight { stroke: rgb(197 233 238 / .42); stroke-width: 3.2; }
.pipe-flow {
  stroke: #8de9f5;
  stroke-width: 1.8;
  stroke-dasharray: 7 17;
  opacity: .46;
}
.pipe-coupler-shadow { fill: #18363e; stroke: #102b31; stroke-width: 1; }
.pipe-coupler-band { fill: #527b84; stroke: #91c6cf; stroke-width: .8; }
.pipe-coupler-bolt { fill: #d5eef1; stroke: #264d56; stroke-width: .45; }
.pipe-joint { fill: #315a64; stroke: #9adce6; stroke-width: 2; }
.pipe-terminal-core { fill: #173c47; stroke: #74c7d6; stroke-width: 1; }
.pipe-terminal-glint { fill: #d8fbff; opacity: .86; }
.pipe.is-live { opacity: .98; }
.pipe.is-live .pipe-flow { opacity: 1; animation: pipe-water 1.15s linear infinite; }
.pipe.is-live .pipe-coupler-band { fill: #5f929d; stroke: #b9f2f7; }

/* 놓기 전의 관(51.3). 또렷하되 **점선이 흐른다** — 아직 놓인 것이 아니라는 표시다.
   도로의 놓는 중인 칸이 점선 마디로 말하는 것과 같은 말이다. */
.pipe.is-pending { opacity: .9; }
.pipe.is-pending .pipe-trench { opacity: .16; }
.pipe.is-pending :is(.pipe-bed, .pipe-rim, .pipe-face) { opacity: .62; }
.pipe.is-pending .pipe-flow { opacity: 1; animation: pipe-water .82s linear infinite; }
.pipe.is-pending :is(.pipe-joint, .pipe-coupler-band) { stroke-dasharray: 3 3; }
@keyframes pipe-water { to { stroke-dashoffset: -24; } }
/* 방금 이은 관 — 또렷하게 떴다가 제 밝기로 가라앉는다. */
.pipe.is-fresh { animation: pipe-settle 2.4s ease-out; }
@keyframes pipe-settle { 0%, 45% { opacity: .98; } 100% { opacity: .18; } }

/* 수도시설을 골랐다 — 관과 이어진 도시만 남고 나머지는 한 발 물러난다(51.7). */
.world-map.is-pipe-focus :is(.tile-layer, .road-layer, .truck-layer, .pickup-layer, .ghost-layer) {
  opacity: .25;
}
.site-shape.is-backdrop { opacity: .15; }
.site-art-slot.is-backdrop, .site-label.is-backdrop, .site-overlay.is-backdrop { opacity: .12; }

/* --------------------------------------------- 도착을 고르는 동안 (47.8 · 51장) */

/* 시작이 정해지면 지도가 한 발 물러난다 — 고를 수 있는 곳만 또렷하게 남는다.
   흐려진 것을 눌러도 되지만, 그때는 왜 안 되는지가 그 자리에 뜬다(49.7의 알약). */
.world-map.is-route-pick :is(.tile-layer, .road-layer, .truck-layer, .pickup-layer),
.world-map.is-pipe-pick :is(.tile-layer, .road-layer, .truck-layer, .pickup-layer) {
  opacity: .4;
}
.tile-layer, .road-layer, .truck-layer, .pickup-layer, .ghost-layer,
.site-shape, .site-art-slot, .site-label, .site-overlay { transition: opacity .3s; }

/* 시작점 화살표 — 시설 머리 위에서 아래로 떨어지기를 되풀이한다(47.8). */
.pick-cue { pointer-events: none; }
.pick-cue-arrow {
  fill: none; stroke: #ffb35c; stroke-width: 4.5; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 2px 3px rgb(0 0 0 / .5));
  animation: pick-drop 1.25s ease-in infinite;
}
@keyframes pick-drop {
  0%   { opacity: 0; transform: translateY(-10px); }
  25%  { opacity: 1; }
  70%  { opacity: 1; transform: translateY(4px); }
  100% { opacity: 0; transform: translateY(8px); }
}

/* ------------------------------------------------------------------ 발전소 (52장) */

/* 영역은 **한 번에 하나만** 보인다 — 고른 발전소의 것이거나, 놓는 중인 자리의 것이거나.
   땅 위에 깔린 빛이라 도로·시설보다 아래에 깐다. */
.power-layer { pointer-events: none; }
.power-field { opacity: 0; transition: opacity .25s; }
.power-field.is-on { opacity: 1; }
.power-field-fill { fill: rgb(255 206 120 / .1); }
.power-field-edge {
  fill: none; stroke: rgb(255 196 92 / .75); stroke-width: 2.5;
  stroke-dasharray: 10 7; vector-effect: non-scaling-stroke;
  animation: power-turn 24s linear infinite;
}
@keyframes power-turn { to { stroke-dashoffset: -340; } }

/* 발전소를 고르면 영역 안 도시만 남는다(52.5) — 수도관(51.5)과 같은 물러남이다. */
.world-map.is-power-focus :is(.tile-layer, .road-layer, .truck-layer, .pickup-layer) { opacity: .3; }

/* 자리를 고르는 동안 도시가 갈린다(52.4). 초록이면 전기가 가고, 붉으면 안 간다.
   지을 수 있나(반투명 자리 표시)와 겹치지 않도록 **도시 도형의 테두리**로만 말한다. */
.site-shape.is-inpower {
  stroke: #6ede8a; stroke-width: 5; filter: drop-shadow(0 0 7px rgb(110 222 138 / .75));
}
.site-shape.is-outpower {
  stroke: #e2704f; stroke-width: 4; stroke-dasharray: 7 5;
  filter: drop-shadow(0 0 5px rgb(226 112 79 / .5));
}

/* --------------------------------------------------- 곡선 엔진 (53장 · Balance) */

/* 레벨업 값 — 단추 오른쪽 끝의 작은 글씨. 모자라면 단추가 잿빛으로 잠긴다(:disabled). */
.level-up-cost {
  margin-left: auto; flex: 0 1 auto;
  font-size: 11px; font-weight: 700; line-height: 1.2; text-align: right;
  opacity: .78; letter-spacing: -.03em;
}

/* 굶는 도시의 타이머 — 붉은 호가 다 차면 무너진다(53.1). 도시 배지 줄 위에 뜬다. */
.starve-ring { pointer-events: none; animation: starve-blink 1s ease-in-out infinite; }
.starve-track { fill: rgb(23 32 33 / .72); stroke: rgb(255 255 255 / .28); stroke-width: 2; }
.starve-fill { fill: none; stroke: #ff5c48; stroke-width: 3.6; stroke-linecap: round; }
@keyframes starve-blink { 0%, 100% { opacity: .95; } 50% { opacity: .55; } }

/* 무너진 도시 — 어두운 채 남는다. 잿더미가 보여야 다음 판에 아프다. */
.site-shape.is-collapsed { opacity: .5; filter: grayscale(1) brightness(.6); }
.site-art-slot.is-collapsed { opacity: .45; filter: grayscale(1) brightness(.55); }
.site-label.is-collapsed { opacity: .55; filter: grayscale(1); }
.site-overlay.is-collapsed { opacity: .2; filter: grayscale(1); }

/* 보급 상자 — 화면 아래 안내(coach)와 같은 자리·같은 말투. 고를 때까지 기다린다. */
.supply {
  display: flex; flex-direction: column; gap: 9px;
  padding: 13px 15px; border-radius: 14px;
  background: rgb(23 32 33 / .94); color: #f6e7dc;
  border: 2px solid #d47c32; box-shadow: 0 10px 28px rgb(10 18 19 / .5);
  max-width: min(430px, calc(100vw - 24px));
}
.supply[hidden] { display: none; }
.supply-title { font-size: 14px; font-weight: 800; letter-spacing: -.02em; }
.supply-title small { font-weight: 600; opacity: .75; margin-left: 6px; }
.supply-row { display: flex; gap: 8px; }
.supply-pick {
  flex: 1; padding: 9px 6px; border: none; border-radius: 9px; cursor: pointer;
  background: var(--station); color: #2c1f12; font-size: 13px; font-weight: 800;
  box-shadow: 0 2px 0 #a95c1c; letter-spacing: -.02em;
}
.supply-pick:hover { filter: brightness(1.06); }
.supply-pick:active { transform: translateY(1px); box-shadow: 0 1px 0 #a95c1c; }
.supply-pick:disabled { cursor: not-allowed; filter: grayscale(.6) brightness(.85); }

/* 무너졌습니다 — 판의 끝. 뒤는 브라우저가 직접 막는다(dialog.showModal). */
.game-over {
  border: 2px solid #d47c32; border-radius: 18px; padding: 26px 30px;
  background: #1d2526; color: #f6e7dc; text-align: center;
  box-shadow: 0 18px 50px rgb(0 0 0 / .55); min-width: 300px;
}
.game-over::backdrop { background: rgb(10 14 15 / .62); }
.game-over h1 { font-size: 24px; margin: 0 0 6px; letter-spacing: -.02em; }
.game-over .game-over-days { font-size: 40px; font-weight: 900; color: #ffb35c; margin: 8px 0 2px; }
.game-over p { margin: 4px 0; font-size: 14px; opacity: .9; }
.game-over .game-over-restart {
  margin-top: 16px; padding: 11px 26px; border: none; border-radius: 999px; cursor: pointer;
  background: var(--station); color: #2c1f12; font-size: 15px; font-weight: 800;
  box-shadow: 0 3px 0 #a95c1c;
}

/* ─────────────────────────────── 절기 (54장) ───────────────────────────────
   시간 알약 안, 날짜 바로 뒤. 평온한 절기는 이름만, 급한 절기는 아이콘과 배수까지.
   글자만으로는 "여름"이 무슨 뜻인지 알 수 없어서 그림을 함께 붙인다. */
.time-season {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 9px 3px 8px; border-radius: 999px;
  border: 1px solid rgb(255 255 255 / .12);
  background: rgb(255 255 255 / .07);
  color: #d8dcd2; font-size: 12px; font-weight: 800; line-height: 1;
}
.time-season.is-heavy {
  border-color: rgb(224 140 74 / .55);
  background: rgb(224 140 74 / .16);
  color: #ffd7ad;
}
.time-season-icon { width: 15px; height: 15px; display: block; }
.time-season-by { font-variant-numeric: tabular-nums; }
/* 절기가 바뀌는 순간 한 번 반짝인다 — 소비가 달라지는 그 순간을 놓치면 안 된다. */
.time-season.is-heavy { animation: season-turn 1.4s ease-out 1; }
@keyframes season-turn {
  0% { transform: scale(1); box-shadow: 0 0 0 0 rgb(224 140 74 / .6); }
  40% { transform: scale(1.08); box-shadow: 0 0 0 7px rgb(224 140 74 / 0); }
  100% { transform: scale(1); box-shadow: 0 0 0 0 rgb(224 140 74 / 0); }
}

/* ─────────────────────────────── 재난 (55장) ───────────────────────────────
   예고는 점선 원이 천천히 돌고, 떨어지면 원 안이 어두워지고 테두리가 굳는다.
   예고와 진행이 **같은 자리, 같은 크기**라 "저기가 저만큼 걸린다"가 미리 읽힌다. */
.disaster-layer { pointer-events: none; }
.disaster-fill { fill: rgb(120 160 200 / .09); }
.disaster-edge {
  fill: none; stroke-width: 2.5; vector-effect: non-scaling-stroke;
  stroke: rgb(150 190 230 / .8); stroke-dasharray: 12 8;
}
.disaster-text {
  text-anchor: middle; font-size: 15px; font-weight: 800;
  fill: #eaf2ff; paint-order: stroke; stroke: rgb(16 24 32 / .85); stroke-width: 4;
}
/* 예고 — 아직 아무것도 끊기지 않았다. 원이 돌면서 "온다"를 알린다. */
.disaster.is-warn .disaster-edge { animation: disaster-turn 14s linear infinite; }
.disaster.is-warn .disaster-fill { animation: disaster-breathe 2.4s ease-in-out infinite; }
/* 진행 — 원 안이 어두워지고 테두리가 굳는다(점선이 멈춘다). */
.disaster.is-hit .disaster-fill { fill: rgb(18 26 36 / .42); }
.disaster.is-hit .disaster-edge { stroke-dasharray: none; stroke-width: 3.5; }
.disaster.is-snow.is-hit .disaster-edge { stroke: rgb(198 226 255 / .95); }
.disaster.is-freeze.is-hit .disaster-edge { stroke: rgb(120 220 240 / .95); }
.disaster.is-overload.is-hit .disaster-edge { stroke: rgb(255 196 92 / .95); }
@keyframes disaster-turn { to { stroke-dashoffset: -200; } }
@keyframes disaster-breathe { 50% { opacity: .45; } }

/* 언 수도관 — 흐르는 점선이 멈추고 물빛이 얼음빛으로 바뀐다(55장). */
.pipe.is-frozen .pipe-flow { animation: none; stroke: #bfe9f2; opacity: .8; }
.pipe.is-frozen .pipe-face { filter: saturate(.3) brightness(1.15); }

/* 과부하로 줄어든 발전소 영역 — 테두리가 붉어져 "지금은 좁다"가 보인다(55장). */
.power-field.is-shrunk .power-field-edge { stroke: rgb(224 122 92 / .9); }
.power-field.is-shrunk .power-field-fill { fill: rgb(224 122 92 / .1); }

/* ──────────────────────── 하나씩 열린다 (56장 · js/unlock.js) ────────────────────────
   화면에 있는 것은 **이미 쓸 일이 생긴 것**뿐이다. 나머지는 아직 없다.
   새로 생긴 것 옆에는 그것이 무엇인지 적은 쪽지가 붙는다. */

/* 아직 안 열린 것. display를 정해 둔 것들이라 [hidden]을 다시 말해 줘야 한다(34.7의 함정). */
.resource[hidden], .build[hidden], .time-controls[hidden] { display: none; }

/* 방금 생겼다 — 자원 기둥이 꺼내질 때와 같은 낙하다. 새것은 늘 같은 방식으로 온다. */
.resource.is-new, .build.is-new, .time-controls.is-new { animation: drop .55s ease-out; }

/* 쪽지가 붙어 있는 동안 그것만 밝다. 통통 튀지는 않는다 — 바로 옆에 읽을 글이 있어서
   눈이 두 군데로 나뉘면 둘 다 놓친다. 부르는 것(is-inviting)은 읽을 글이 없을 때 쓴다. */
.resource.is-lit, .build.is-lit, .time-controls.is-lit {
  border-color: #ffc45c;
  box-shadow: 0 0 0 2px rgb(255 196 92 / .8), 0 0 18px rgb(255 196 92 / .4);
}
.time-controls.is-lit { border-radius: 999px; padding-right: 5px; }

/* 쪽지. 코치의 말풍선은 지도 위 SVG라 왼쪽 창·오른쪽 기둥을 넘어가지 못한다.
   이것은 HTML이라 **화면 어디에 붙은 것이든** 가리킬 수 있다 — 열리는 것이 죄다 기둥에 있다. */
.unlock-tip {
  position: fixed; z-index: 30;
  width: min(268px, calc(100vw - 24px));
  padding: 13px 15px 14px;
  border: 2px solid #d47c32; border-radius: 14px;
  background: rgb(23 32 33 / .96); color: #f6e7dc;
  box-shadow: 0 12px 30px rgb(10 18 19 / .5);
}
.unlock-tip[hidden] { display: none; }
.unlock-tip.is-in { animation: unlock-in .26s ease-out; }
@keyframes unlock-in { from { opacity: 0; transform: scale(.94); } }

.unlock-tag { margin: 0; color: var(--station); font-size: 11px; font-weight: 800; letter-spacing: .06em; }
.unlock-title { margin: 4px 0 0; font-size: 15px; letter-spacing: -.025em; }
.unlock-body { margin: 7px 0 0; font-size: 12.5px; line-height: 1.62; color: #ded6c8; }
.unlock-body b { color: #ffc45c; font-weight: 800; }
.unlock-close {
  margin-top: 11px; width: 100%; min-height: 32px; padding: 0 12px;
  border: none; border-radius: 9px; cursor: pointer;
  background: var(--station); color: #2c1f12;
  font: inherit; font-size: 12px; font-weight: 800;
  box-shadow: 0 2px 0 #a95c1c;
}
.unlock-close:hover { filter: brightness(1.06); }
.unlock-close:active { transform: translateY(1px); box-shadow: 0 1px 0 #a95c1c; }

/* 화살표 — 같은 테두리를 두른 네모를 45° 돌려 모서리에 반쯤 걸친다.
   data-side는 **화살표가 붙은 판의 모서리**이고, --aim은 가리키는 것의 한가운데다. */
.unlock-arrow {
  position: absolute; width: 12px; height: 12px;
  background: rgb(23 32 33 / .96); border: 2px solid #d47c32;
  transform: rotate(45deg);
}
.unlock-tip[data-side="right"] .unlock-arrow { right: -8px; top: var(--aim); margin-top: -6px; border-left: 0; border-bottom: 0; }
.unlock-tip[data-side="left"] .unlock-arrow { left: -8px; top: var(--aim); margin-top: -6px; border-right: 0; border-top: 0; }
.unlock-tip[data-side="top"] .unlock-arrow { top: -8px; left: var(--aim); margin-left: -6px; border-right: 0; border-bottom: 0; }
.unlock-tip[data-side="bottom"] .unlock-arrow { bottom: -8px; left: var(--aim); margin-left: -6px; border-left: 0; border-top: 0; }
