/*
 * 아르카룬 스타일.
 *
 * 번들러도 전처리기도 쓰지 않는다. 파일을 고치고 새로고침하면 끝이다.
 *
 * 색 사용 원칙: 정보를 빨강<->초록 대비에만 싣지 않는다.
 * 적녹색맹이 못 읽으면 게임이 성립하지 않으므로, 상태는 명도/아이콘/문구를 함께 쓴다.
 * (예: 잠긴 관문은 흐리게 + 자물쇠 + "잠김" 문구)
 */

:root {
  --bg: #10161c;
  --bg-panel: #1a232c;
  --bg-raised: #22303c;
  --line: #2f414f;
  --text: #e8eef3;
  --text-dim: #8fa3b2;
  --gold: #e8b64c;
  --crystal: #9b7de0;
  --stamina: #4fb6c4;
  --accent: #4a8fd4;
  --danger: #d4604a;
  --ok: #55b98a;

  /* 다섯 원소 왕국(V88). 룬 원소와 같은 값이라 한 벌로 쓴다. */
  --faction-fire: #e0704a;
  --faction-water: #5aa6d8;
  --faction-wind: #7fcf9a;
  --faction-earth: #c99a5b;
  --faction-iron: #a8aebb;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Malgun Gothic", "맑은 고딕", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  overscroll-behavior: none;
}

.hidden { display: none !important; }
.dim { color: var(--text-dim); }

/* ---------------------------------------------------------- HUD */

.hud {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  background: linear-gradient(180deg, #1c2731, #16202a);
  border-bottom: 1px solid var(--line);
}

.hud-profile { display: flex; align-items: center; gap: 8px; min-width: 0; }

.hud-level {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  padding: 0 6px;
  border-radius: 14px;
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
}

.hud-nick {
  max-width: 90px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
}

.hud-currencies { display: flex; gap: 6px; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border-radius: 12px;
  background: rgba(0, 0, 0, .3);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.ico {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex: none;
}

.ico-gold { background: var(--gold); border-radius: 50%; }
.ico-crystal { background: var(--crystal); transform: rotate(45deg); border-radius: 2px; }
.ico-stamina { background: var(--stamina); border-radius: 2px 2px 6px 6px; }
/*
 * 메뉴 아이콘은 색만 다른 사각형이었다. 색을 못 가리는 사람에게는 전부 같은
 * 네모라 아무 정보도 주지 않는다. 색과 함께 **모양**을 달리한다
 * (글자 이름은 아이콘 아래에 그대로 있다).
 */
.ico-map     { background: var(--ok); clip-path: polygon(0 20%, 33% 0, 66% 20%, 100% 0, 100% 80%, 66% 100%, 33% 80%, 0 100%); }
.ico-deck    { background: var(--accent); border-radius: 2px; box-shadow: 3px -3px 0 -1px var(--bg-panel), 4px -4px 0 0 var(--accent); }
.ico-card    { background: var(--gold); border-radius: 2px; }
/* 퍼즐 한 조각. 카드(네모) 바로 옆에 놓이므로 한쪽이 물려 들어간 모양으로 가른다. */
.ico-shard   { background: var(--gold); clip-path: polygon(0 0, 62% 0, 62% 18%, 100% 18%, 100% 100%, 38% 100%, 38% 74%, 0 74%); }
.ico-shop    { background: var(--crystal); border-radius: 0 0 5px 5px; clip-path: polygon(15% 25%, 85% 25%, 100% 100%, 0 100%); }
/* 메뉴 - 가로줄 셋. 원작 Menu 단추도 같은 모양이다. */
.ico-menu {
  background: linear-gradient(var(--text) 0 0) 0 15% / 100% 2px no-repeat,
              linear-gradient(var(--text) 0 0) 0 50% / 100% 2px no-repeat,
              linear-gradient(var(--text) 0 0) 0 85% / 100% 2px no-repeat;
}
.ico-enhance { background: var(--danger); clip-path: polygon(50% 0, 100% 38%, 81% 100%, 19% 100%, 0 38%); }
.ico-maze    { background: #7d8fa8; clip-path: polygon(0 0, 100% 0, 100% 25%, 40% 25%, 40% 55%, 100% 55%, 100% 100%, 0 100%, 0 75%, 60% 75%, 60% 45%, 0 45%); }
.ico-temple  { background: #b8a3d8; clip-path: polygon(50% 0, 100% 32%, 100% 44%, 84% 44%, 84% 100%, 66% 100%, 66% 44%, 34% 44%, 34% 100%, 16% 100%, 16% 44%, 0 44%, 0 32%); }
.ico-codex   { background: #8fbfa8; border-radius: 1px 5px 5px 1px; box-shadow: inset 3px 0 0 rgba(0, 0, 0, .4); }
.ico-daily   { background: #e0a860; clip-path: polygon(50% 0, 62% 36%, 100% 38%, 70% 60%, 80% 100%, 50% 76%, 20% 100%, 30% 60%, 0 38%, 38% 36%); }
/* 우승컵. 방패(군단)와 겹치지 않는 모양이어야 한다. */
.ico-arena   { background: #d98a5e; clip-path: polygon(18% 0, 82% 0, 82% 30%, 100% 30%, 100% 48%, 64% 60%, 64% 84%, 88% 84%, 88% 100%, 12% 100%, 12% 84%, 36% 84%, 36% 60%, 0 48%, 0 30%, 18% 30%); }
/* 봉투. 뚜껑은 대각선 두 줄로 그린다 - clip-path 로는 안쪽 구멍을 낼 수 없다. */
.ico-mail    { background: #7fb3d5; border-radius: 2px;
  background-image: linear-gradient(to bottom right, transparent 46%, rgba(0, 0, 0, .45) 47%, transparent 54%),
                    linear-gradient(to bottom left, transparent 46%, rgba(0, 0, 0, .45) 47%, transparent 54%); }
.ico-friends { background: #8fbfa8; border-radius: 50% 50% 30% 30%; }
/* 오르는 막대. 한붓그리기로 이어져야 한다 - clip-path 는 떨어진 도형을 못 그린다. */
.ico-ranking { background: var(--gold); clip-path: polygon(0 100%, 0 62%, 30% 62%, 30% 30%, 62% 30%, 62% 0, 100% 0, 100% 100%); }
.ico-guild   { background: #c08fd0; clip-path: polygon(0 0, 100% 0, 100% 60%, 50% 100%, 0 60%); }

/* ---------------------------------------------------------- 레이아웃 */

#app { padding-bottom: 40px; }

.view { display: none; padding: 12px; max-width: 720px; margin: 0 auto; }
.view.active { display: block; }

.topbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.topbar h2 { margin: 0; font-size: 17px; }

.title-block { text-align: center; padding: 48px 0 24px; }
.title-block h1 { margin: 0; font-size: 40px; letter-spacing: 4px; color: var(--gold); }
.subtitle { color: var(--text-dim); margin: 6px 0 0; font-size: 13px; }

.panel {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 16px;
  margin-bottom: 12px;
}

.panel.narrow { max-width: 360px; margin: 0 auto; }
.panel h3 { margin: 0 0 4px; font-size: 16px; }

.note { color: var(--text-dim); font-size: 12px; line-height: 1.6; margin: 10px 0 0; }

.field { display: block; margin-bottom: 12px; }
.field span { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 4px; }

.field input {
  width: 100%;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
}

.field input:focus { outline: none; border-color: var(--accent); }

/* ---------------------------------------------------------- 버튼 */

.btn {
  padding: 9px 14px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-raised);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
}

.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }
.btn-small { padding: 6px 10px; font-size: 13px; }
.btn-back { padding: 6px 10px; font-size: 13px; }
.btn-link { background: none; border: none; color: var(--accent); padding: 8px 0; font-size: 13px; }
.block { display: block; width: 100%; }

/* ---------------------------------------------------------- 마을 */

/*
 * 마을.
 *
 * 그림 파일을 쓰지 않고 CSS 로만 그린다. 이미지를 넣으면 앱 묶음(OTA)에 실어야
 * 하고, 해상도마다 다른 파일이 필요해진다. 여기는 배경일 뿐이라 그만한 값을
 * 치를 자리가 아니다. 카드 일러스트는 이야기가 다르다 - 그건 그림이 있어야 한다.
 *
 * 조각을 겹쳐 쌓는다: 하늘 → 별 → 달 → 언덕 → 성 → 나무.
 * 뒤에 있는 것일수록 흐리고 어둡다.
 */
.town { position: relative; height: 170px; border-radius: 12px; overflow: hidden; margin-bottom: 12px; }
.town > * { position: absolute; }

.town-sky { inset: 0; background: linear-gradient(180deg, #16273a 0%, #1d3a44 55%, #16262e 100%); }

/* 별. 점을 하나씩 두지 않고 배경 그라디언트를 반복해 찍는다. */
.town-stars {
  inset: 0;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 22%, rgba(255, 255, 255, .55), transparent),
    radial-gradient(1.5px 1.5px at 28% 12%, rgba(255, 255, 255, .40), transparent),
    radial-gradient(1.5px 1.5px at 44% 26%, rgba(255, 255, 255, .30), transparent),
    radial-gradient(1.5px 1.5px at 63% 10%, rgba(255, 255, 255, .50), transparent),
    radial-gradient(1.5px 1.5px at 78% 24%, rgba(255, 255, 255, .35), transparent),
    radial-gradient(1.5px 1.5px at 90% 14%, rgba(255, 255, 255, .45), transparent);
}

.town-moon {
  top: 14px;
  right: 56px; /* 오른쪽 위 ⚙ 을 피한다 */
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 34%, #f2ead2, #d9cfae);
  box-shadow: 0 0 26px rgba(240, 232, 200, .35);
}

/* 먼 언덕. 성보다 어둡게 두어 뒤로 물러나 보이게 한다. */
.town-hills {
  left: -10%;
  right: -10%;
  bottom: 0;
  height: 62%;
  background:
    radial-gradient(60% 100% at 18% 100%, #1e3846 0 70%, transparent 71%),
    radial-gradient(55% 90% at 72% 100%, #1a3140 0 70%, transparent 71%);
}

/*
 * 성. 왼쪽 탑 · 성벽 · 가운데 본채 · 성벽 · 오른쪽 탑을 한붓그리기로 잇는다.
 * clip-path 는 떨어진 도형을 그리지 못하므로 하나로 이어야 한다.
 * 창문은 clip-path 안쪽에 구멍을 낼 수 없어 배경 그라디언트로 찍는다.
 */
.town-castle {
  left: 0;
  right: 0;
  bottom: 0;
  height: 58%;
  /* 하늘보다 어두워야 형체가 선다. 밝게 올리면 그 높이의 하늘색에 묻힌다. */
  background-color: #0b161e;
  background-image:
    radial-gradient(2px 3px at 30.5% 62%, rgba(232, 182, 76, .85), transparent),
    radial-gradient(2px 3px at 50% 55%, rgba(232, 182, 76, .85), transparent),
    radial-gradient(2px 3px at 69.5% 62%, rgba(232, 182, 76, .70), transparent),
    radial-gradient(2px 3px at 43% 78%, rgba(232, 182, 76, .55), transparent),
    radial-gradient(2px 3px at 57% 78%, rgba(232, 182, 76, .55), transparent);
  clip-path: polygon(
    10% 100%, 10% 60%,
    12% 60%, 12% 54%, 15% 54%, 15% 60%, 18% 60%, 18% 54%, 21% 54%, 21% 60%,
    23% 60%, 23% 72%,
    38% 72%, 38% 40%,
    40% 40%, 40% 32%, 44% 32%, 44% 40%, 48% 40%, 48% 32%, 52% 32%, 52% 40%,
    56% 40%, 56% 32%, 60% 32%, 60% 40%,
    62% 40%, 62% 72%,
    77% 72%, 77% 60%,
    79% 60%, 79% 54%, 82% 54%, 82% 60%, 85% 60%, 85% 54%, 88% 54%, 88% 60%,
    90% 60%, 90% 100%
  );
}

/* 나무는 맨 앞이다. 성을 가리지 않도록 왼쪽으로 비켜 세운다. */
.town-tree {
  left: 8%;
  bottom: -18px;
  width: 68px;
  height: 68px;
  border-radius: 50% 50% 40% 40%;
  background: radial-gradient(circle at 50% 35%, #3f7d5c, #1e4535);
  box-shadow: 0 0 40px rgba(80, 180, 130, .18);
}

.occupy-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  margin-bottom: 12px;
  border-radius: 10px;
  border: 1px solid var(--gold);
  background: rgba(232, 182, 76, .1);
  font-size: 13px;
}

.occupy-banner b { color: var(--gold); margin-right: 6px; }

/* ---------------------------------------------------------- 마을(메인 화면)

   원작 배치 그대로다.

     좌상단   초상 + 레벨 + 이름, 그 아래 재화 둘
     우측     Map · Enchant · Temple  (세로 원형 셋)
     하단     Menu · Store · Deck · Social
     좌하단   기력과 충전 단추

   판(배경 그림)이 가운데를 다 쓰고, 단추는 가장자리에 붙는다.
   14칸을 한 판에 늘어놓던 예전 배치는 가로 390px 에서 아랫줄이 잘렸다.
*/

#view-home.active { position: relative; min-height: calc(100vh - 20px); }

/*
 * 배경이 판 전체를 채운다. 단추는 그 위 가장자리에 붙는다.
 *
 * overflow 를 막지 않으면 나무처럼 아래로 삐져나오게 그려 둔 조각이
 * 하단 단추 밑으로 튀어나온다 - 실제로 초록 조각이 하나 새어 나왔다.
 */
#view-home .town {
  position: absolute;
  inset: 0;
  height: auto;
  margin: 0;
  overflow: hidden;
  border-radius: 10px;
  z-index: 0;
}

/*
 * 나무는 예전에 **아래로 삐져나오게** 그려 두었다. 마을 판이 고정 높이의 조각이던
 * 시절에는 그것이 장식이었지만, 배경이 화면을 다 쓰게 된 지금은 하단 단추 밑으로
 * 초록 조각이 새어 나온다. 판 안쪽에 세운다.
 */
#view-home .town-tree { bottom: 0; }

/* 좌상단 - 초상 · 레벨 · 이름 · 재화 */
.home-profile {
  position: absolute;
  top: 8px;
  left: 10px;
  display: flex;
  align-items: flex-start;
  gap: 7px;
  z-index: 2;
}

.home-face {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 2px solid var(--gold);
  /* 초상 그림이 아직 없다. 자리를 비워 두면 레벨 배지가 허공에 뜬다. */
  background: radial-gradient(circle at 50% 35%, #4a5a72, #1b2430);
  flex: 0 0 auto;
}

.home-lv {
  position: absolute;
  top: -3px;
  left: 28px;
  min-width: 18px;
  padding: 0 3px;
  border-radius: 9px;
  background: #12181f;
  border: 1px solid var(--gold);
  color: var(--gold);
  font-size: 11px;
  line-height: 16px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.home-who { display: flex; flex-direction: column; gap: 3px; }
.home-who > b { font-size: 14px; text-shadow: 0 1px 3px #000; }
.home-money { display: flex; gap: 5px; }

/* 우측 세로 넷 */
/*
   관리자 단추. 마을 우측 상단.

   **달을 옆으로 밀어 둔다.** 달(top:14px right:26px)과 이 단추가 겹치기 때문인데,
   미는 것은 단추가 보일 때뿐이다 - 관리자가 아닌 사람의 마을은 그대로다.

   색은 눈에 띄지 않게 둔다. 관리자 기능은 자랑할 것이 아니다.
*/
.home-admin {
  position: absolute;
  top: 8px;
  right: 50px; /* 오른쪽 끝은 ⚙(설정)이다 */
  z-index: 3;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(16, 22, 28, .78);
  color: var(--text-dim);
  font-size: 11px;
  cursor: pointer;
}

.home-admin:active { transform: translateY(1px); }

#view-home.admin-on .town-moon { right: 140px; }

.home-side {
  position: absolute;
  top: 50%;
  right: 10px;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 7px;
  z-index: 2;
}

.round-btn {
  width: 62px;
  height: 62px;
  border-radius: 50%;
  border: 2px solid var(--line);
  background: rgba(18, 24, 31, .86);
  color: var(--text);
  font-family: inherit;
  font-size: 11px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  cursor: pointer;
}

.round-btn:hover { border-color: var(--gold); }
.round-btn .ico { width: 20px; height: 20px; }

/* 좌하단 기력 */
.home-stamina {
  position: absolute;
  left: 10px;
  bottom: 60px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border-radius: 14px;
  background: rgba(18, 24, 31, .86);
  border: 1px solid var(--line);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  z-index: 2;
}

.stamina-plus {
  width: 18px;
  height: 18px;
  margin-left: 2px;
  border-radius: 50%;
  border: 1px solid var(--ok);
  background: transparent;
  color: var(--ok);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

/* 하단 넷 */
.home-bottom {
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 8px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 7px;
  z-index: 2;
}

.bar-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 4px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: rgba(18, 24, 31, .9);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}

/* Menu 는 원작에서도 붉고 크다. 여기서 나머지가 전부 열리기 때문이다. */
.bar-btn.bar-menu { border-color: #7d2b2b; background: rgba(96, 30, 30, .92); }
.bar-btn .ico { width: 17px; height: 17px; }

/*
   Menu / Social 서랍.

   하단 단추 바로 위에서 열린다. 화면을 덮지 않으므로 뒤의 마을이 계속 보이고,
   밖을 누르면 닫힌다.
*/
.home-sheet {
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 52px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 7px;
  padding: 8px;
  border-radius: 10px;
  background: rgba(12, 18, 24, .96);
  border: 1px solid var(--gold);
  z-index: 3;
}

.menu-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }

.menu-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 4px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--bg-panel);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}

.menu-btn .ico { width: 20px; height: 20px; }

/* ---------------------------------------------------------- 지도 */

.chapter-tabs { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 8px; }

.chapter-tab {
  flex: none;
  min-width: 44px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-panel);
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
}

.chapter-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }
.chapter-tab.locked { opacity: .4; }

.chapter-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 0 12px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
}

.chapter-head h3 { margin: 0; font-size: 18px; }
.chapter-head .stars { color: var(--gold); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------- 지도
 *
 * 원작은 챕터마다 그려 둔 배경 위에 관문을 흩어 놓고 길로 잇는다.
 * 목록으로 늘어놓으면 "다음 줄"일 뿐이지만, 지도에서는 어디까지 왔고
 * 끝이 어디인지가 한눈에 보인다.
 *
 * **배경 그림이 아직 없다.** 챕터 번호로 색조만 바꿔 어디에 있는지 느껴지게 해 두었다.
 * 그림이 생기면 이 배경만 갈아 끼우면 된다.
 */

/* 지도는 글줄이 아니라 판이다. 가운데로 모으지 않고 폭을 다 쓴다. */
#view-map { max-width: 100%; }

/* 뒤로·챕터·전투 방식을 한 줄에. 남는 높이는 전부 지도가 가져간다. */
.map-bar { gap: 10px; }
.map-bar .chapter-head { flex: 1 1 auto; min-width: 0; border: 0; padding: 0; margin: 0; }

/*
 * 지도 아래 줄. 원작은 왼쪽에 기력, 오른쪽에 상점·카드를 둔다.
 * 안내 문구가 가운데에서 남는 자리를 먹고, 좁아지면 그것부터 줄어든다.
 */
.map-foot { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
.map-foot .dim { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-foot .btn { flex: 0 0 auto; }
/* 가운데 안내 문구(모드 힌트)가 빠졌다(158-49). 기력이 왼쪽, 단추가 오른쪽에 남도록 기력 칸이 빈 폭을 밀어낸다. */
.map-foot .map-stamina { margin-right: auto; }

/* 기력. 마을 좌하단의 것과 같은 모양이라 눈이 같은 것으로 읽는다. */
.map-stamina {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  padding: 3px 8px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--bg-panel);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.map-foot .dim { font-size: 11px; }

.stage-map {
  position: relative;
  height: 208px;
  border-radius: 12px;
  border: 1px solid var(--line);
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 0%, hsl(var(--map-hue, 200) 30% 22%) 0%, transparent 70%),
    radial-gradient(90% 70% at 20% 100%, hsl(var(--map-hue, 200) 34% 16%) 0%, transparent 65%),
    var(--bg-panel);
}

/* 지나온 길. 관문 사이를 점선으로 잇는다 - 순서가 눈에 보여야 한다. */
.stage-path {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.stage-path polyline {
  fill: none;
  stroke: hsl(var(--map-hue, 200) 30% 45%);
  stroke-width: .5;
  stroke-dasharray: 2 2;
  vector-effect: non-scaling-stroke;
}

.stage-node {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  width: 96px;
}

.stage-node .stage-label {
  font-size: 11px;
  color: var(--text-dim);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stage-node .stage-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  width: 76px;
  padding: 6px 4px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
}

.stage-node .stage-no { font-weight: 700; font-size: 13px; }
.stage-node .stage-meta { font-size: 10px; color: var(--text-dim); }
.stage-node .stars { letter-spacing: 1px; font-size: 11px; color: var(--gold); }
.stage-node .stars .off { color: #3b4a57; }

/* 챕터의 끝. 왕관이 붙은 칸이 그 챕터의 보스다.
   이름줄이 아니라 타일 모서리에 붙인다 - 위에 겹치면 이름을 가린다. */
.stage-node.boss .stage-tile { border-color: var(--gold); }
.stage-node .stage-crown {
  position: absolute;
  top: -8px;
  right: -6px;
  width: 16px;
  height: 16px;
  border-radius: 8px;
  background: var(--bg);
  border: 1px solid var(--gold);
  font-size: 10px;
  line-height: 14px;
  text-align: center;
  color: var(--gold);
}

/* 지금 갈 곳. 색만으로 알리지 않도록 테두리 굵기도 함께 바뀐다. */
.stage-node.current .stage-tile {
  border-width: 2px;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px hsl(var(--map-hue, 200) 40% 30% / .5);
}

/* 잠긴 칸은 흐림만으로 알리지 않는다. 타일 안의 문구도 자물쇠로 바뀐다. */
.stage-node.locked { opacity: .45; cursor: default; }
.stage-node.locked .stage-tile { border-style: dashed; }


/* ---------------------------------------------------------- 카드 */

.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }
/*
 * "카드가 없습니다" 같은 안내 문구를 그리드 안에 넣는 자리가 여럿이다(카드·강화·도감·분해).
 * 그대로 두면 문구가 카드 한 장 너비(104px)로 접혀 한 줄에 두세 글자씩 흐른다.
 * 눈으로 열어 보기 전에는 드러나지 않는 종류라 그리드 쪽에서 한 번에 막는다.
 */
.card-grid > .note { grid-column: 1 / -1; margin-top: 0; }

/*
 * 카드 타일 - **그림이 주인공이고 값은 모서리에 얹힌다**(2026-09-03 대표님).
 *
 * 세로로 흐르는 칸들 사이에 `.card-gap` 이 하나 늘어나며, 그것이 위(별·이름)와
 * 아래(체력·공격력)를 타일의 양 끝으로 민다. **비율을 3:4 로 못 박는 것**은
 * 그림이 그 비율이기 때문이다(1086×1448) - 값의 줄 수에 따라 타일 키가 들쭉날쭉하면
 * 그리드가 계단처럼 어긋난다.
 */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  aspect-ratio: 3 / 4;
  border-radius: 8px;
  border: 2px solid var(--line);
  background: var(--bg-panel);
  padding: 6px 7px;
  font-size: 12px;
  cursor: pointer;
  overflow: hidden;
}

/*
 * 카드 그림.
 *
 * 원작은 이름·COST·별·수치를 **그림 위에 얹는다.** 그래서 그림을 타일 뒤에 깔고
 * 지금 있는 글자들을 그대로 그 위에 둔다 - 화면마다 배치를 고칠 필요가 없다.
 *
 * **어두운 막을 함께 씌운다.** 밝은 그림이 오면 흰 글자가 읽히지 않는데, 그건
 * 그림이 들어온 뒤에야 드러난다. 113장이 제각각일 것이므로 처음부터 막아 둔다.
 * 위(이름)와 아래(수치)를 더 짙게 하는 것은 글자가 거기 몰려 있기 때문이다.
 *
 * 그림이 없으면 이 규칙들은 아무 일도 하지 않는다 - <img> 자체가 없다.
 */
.card-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 글자보다 뒤. .card 가 position: relative 라 이 안에 갇힌다. */
  z-index: 0;
  /* 그림이 늦게 와도 판이 흔들리지 않는다. */
  background: var(--bg-panel);
}

/*
 * 카드 상세의 그림.
 *
 * 세로가 390px 뿐이라 **높이를 못 박는다.** 세로형 그림을 원래 비율로 두면
 * 모달이 화면 밖으로 밀려, 아래쪽 스킬 설명을 못 읽게 된다 - 이 화면에서
 * 여섯 번 겪은 그 사고다.
 */
.cm-art-wrap {
  /*
   * **이 줄이 없으면 그림이 화면을 통째로 덮는다.**
   *
   * 안에 들어오는 <img> 는 타일에서 쓰는 `.card-art` 그대로다.
   * 그 클래스는 `position: absolute; inset: 0` 이라, 감싼 칸이 positioned 가 아니면
   * 더 바깥(모달·화면)을 기준으로 펼쳐진다. 실제로 844x390 을 꽉 채웠다.
   *
   * 이름을 겹치지 않아도 **규칙은 물려받는다** - `.costbar` 를 룬 막대에 가져다 썼을 때와
   * 같은 사고다. 남의 클래스를 쓸 때는 그 클래스가 **어떤 배치를 전제하는지**까지 볼 것.
   */
  position: relative;
  height: 120px;
  margin: -16px -16px 10px;
  overflow: hidden;
  border-radius: 10px 10px 0 0;
  background: var(--bg-raised);
}

/*
 * 낮은 화면에서는 그림이 자리를 내놓는다.
 *
 * 844x390 에서 모달이 쓸 수 있는 높이가 318px 뿐인데, 그림이 120px 을 가져가면
 * 스킬 설명이 스크롤 밖으로 밀린다(모달이 스크롤되긴 하므로 잃지는 않는다).
 * **읽어야 하는 것이 스킬**이고 그림은 분위기다 - 줄이는 쪽은 그림이다.
 */
@media (max-height: 480px) {
  .cm-art-wrap { height: 84px; }
}

.cm-art-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 위쪽에 얼굴이 오는 그림이 많다. 잘릴 때 얼굴부터 남긴다. */
  object-position: 50% 30%;
  display: block;
}

/*
 * 그림 위의 막. 가짜 요소 하나로 끝낸다 - 글자마다 그림자를 주면 지저분해진다.
 *
 * **처음에는 판 전체를 고르게 덮었다**(.82 / .60 / .60 / .90). 글자는 읽혔지만
 * 그림이 **어두운 얼룩**이 됐다 - 그림을 넣은 이유가 사라진다.
 * 실제 그림 서른 장을 올려 놓고 844x390 으로 보고 나서야 보였다.
 *
 * 지금은 **글자가 있는 띠만** 덮는다:
 *
 *   위    이름          진하게
 *   가운데 공/체        연하게 - 여기서 그림이 보인다. 글자는 아래 text-shadow 가 붙든다
 *   아래  COST·대기 턴  진하게
 *
 * 가운데를 .60 에서 .28 로 내렸다. 글자 그림자가 이미 있으므로
 * 막 하나에만 기대지 않는다(바로 아래 규칙).
 */
.card:has(.card-art)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
      180deg,
      rgba(16, 22, 28, .86) 0%,
      rgba(16, 22, 28, .62) 18%,
      rgba(16, 22, 28, .28) 42%,
      rgba(16, 22, 28, .28) 62%,
      rgba(16, 22, 28, .72) 82%,
      rgba(16, 22, 28, .92) 100%);
}

/*
 * 그림 위에서는 흐린 글자를 **덜 흐리게** 한다.
 *
 * `--text-dim` 은 어두운 판 위에서 고른 색이라, 밝은 그림이 깔리면 거의 사라진다.
 * 실제로 크림색 그림을 넣어 보니 "Kingdom · Lv0" 이 읽히지 않았다.
 * 값(ATK·HP)이 아니라 보조 정보지만, **안 보이는 것과 흐린 것은 다르다.**
 */
.card:has(.card-art) .dim,
.card:has(.card-art) .card-skills { color: rgba(232, 238, 243, .82); }

/*
 * 글자 그림자는 그림이 있든 없든 늘 준다.
 *
 * 어두운 판 위에서는 보이지 않고, 그림 위에서만 값을 한다.
 * `:has()` 를 모르는 브라우저에서 위의 막이 안 걸려도 **글자는 읽힌다** -
 * 막 하나에만 기대면 그 브라우저에서 카드가 통째로 못 읽는 상태가 된다.
 */
.card .card-name,
.card .card-stars,
.card .card-top,
.card .card-bottom,
.card .card-stats,
.card .card-foot,
.card .dim { text-shadow: 0 1px 2px rgba(0, 0, 0, .85); }

/* 글자와 표시들은 막보다 위로. */
.card > *:not(.card-art) {
  position: relative;
  z-index: 2;
}

.card.FIRE { border-color: var(--faction-fire); }
.card.WATER { border-color: var(--faction-water); }
.card.WIND { border-color: var(--faction-wind); }
.card.EARTH { border-color: var(--faction-earth); }
.card.IRON { border-color: var(--faction-iron); }

.card.selected { box-shadow: 0 0 0 2px var(--accent) inset; }
/*
 * 고른 카드 표시.
 *
 * 예전에는 여기에 "덱"이라고 한국어를 박아 두었다. CSS 의 content 는 사전을
 * 거치지 않으므로 **어느 언어로 봐도 한국어가 나왔다** - 토너먼트 화면을 영어로
 * 열다가 드러났다. 문구는 사전에만 둔다는 규칙이 CSS 에서 새고 있었던 셈이다.
 *
 * 글자 대신 표시(✓)를 쓴다. 번역할 것이 없으니 어긋날 일도 없다.
 * 안쪽 테두리와 함께 쓰므로 색만으로 알리는 것도 아니다.
 */
.card.selected::after {
  content: "✓";
  position: absolute;
  /* 그림 막의 `inset: 0` 이 아래·왼쪽에 남는다 - 대상 이름표와 같은 함정이다. */
  inset: 4px 4px auto auto;
  background: var(--accent);
  color: #fff;
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 10px;
  font-weight: 700;
}

/*
 * 위 줄 - 왼쪽에 **노란 별**, 오른쪽에 코스트·대기.
 * 아래 줄 - 왼쪽에 **체력**, 오른쪽에 **공격력**. 대표님이 정한 자리다.
 */
.card-top,
.card-bottom {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  font-variant-numeric: tabular-nums;
}
/* 늘어나는 빈 칸. 이것 하나가 위와 아래를 타일의 끝으로 민다. */
.card-gap { flex: 1 1 auto; min-height: 4px; }

.card-name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card-stars { color: var(--gold); font-size: 11px; letter-spacing: 1px; white-space: nowrap; }
.card-kind { font-size: 11px; }
.card-hp, .card-atk { font-weight: 600; }
.card-stats { display: flex; gap: 8px; margin-top: 4px; font-variant-numeric: tabular-nums; }
/*
 * 코스트를 절대 위치로 카드 오른쪽 아래에 띄워 두었더니, 나중에 붙은 스킬 수
 * 줄과 **글자가 겹쳐** 둘 다 읽을 수 없었다("Skills 1/3" 위에 "C3 · Wait 1").
 * 겹치는 자리는 화면을 눈으로 보기 전에는 발견되지 않는 종류다.
 * 흐름 안으로 되돌리고, 스킬 수와 한 줄에 나란히 놓는다.
 */
.card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 2px 6px;
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid var(--line);
  font-size: 10px;
  color: var(--text-dim);
}
.card-cost { flex: none; font-size: 10px; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------- 덱 */

.deck-cost {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  margin-bottom: 8px;
  font-variant-numeric: tabular-nums;
}

.costbar { flex: 1 1 100%; height: 6px; border-radius: 3px; background: var(--bg); overflow: hidden; }
.costbar-fill { height: 100%; width: 0; background: var(--accent); transition: width .2s; }
.costbar-fill.over { background: var(--danger); }

.sticky-bottom {
  position: sticky;
  bottom: 0;
  padding: 10px 0;
  background: linear-gradient(180deg, rgba(16, 22, 28, 0), var(--bg) 40%);
}

/* ---------------------------------------------------------- 전투 */

/*
 * 데스크톱에서의 판 너비.
 *
 * 560 은 영웅이 **가로줄**이던 시절의 값이다. 영웅을 왼쪽 기둥으로 옮기면서
 * 그 104px 을 판이 빼앗겨 레인이 76px 로 쪼그라들었다 - 카드 이름이
 * "지..." 로 잘렸다. 기둥 몫을 더해 준다.
 *
 * 가로 휴대폰에서는 아래 미디어 블록이 100% 로 덮어쓴다.
 */
.view-battle { max-width: 720px; }

/*
 * 판 전체. 왼쪽에 영웅 둘, 오른쪽에 전장 - 원작 배치다.
 *
 * 영웅을 가로줄로 눕히면 세로 390px 에서 판과 로그가 화면 밖으로 밀린다.
 * 두 칸으로 나누면 그 두 줄만큼(약 60px)이 판으로 돌아온다.
 */
.battle-stage {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

/*
 * 영웅 기둥. 위가 상대, 아래가 나 - 각자 자기 줄과 나란히 선다.
 * `space-between` 이라 판이 길어지면 둘이 위아래로 벌어진다.
 */
.battle-heroes {
  flex: 0 0 104px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 6px;
}

/* flex 자식의 기본값은 min-width: auto 다. 안 낮추면 긴 이름이 판을 밀어낸다. */
.battle-stage .field { flex: 1 1 auto; min-width: 0; }

/*
 * 오른쪽 기둥 - 판 위에 떠 있다 (167장 · 158-17).
 *
 * 레인이 90px 고정이라 판의 오른쪽이 비는데, 기둥을 흐름 안에 세우면 그 폭을 판에서 빼 간다.
 * absolute 로 띄우면 판은 그대로고 단추만 빈 자리에 앉는다. 세로 가운데에 자동전투·차례 단추를
 * 크게(결정 지점에서만 보인다), 맨 아래에 배속·스킵을 작게.
 */
.battle-stage { position: relative; }

.battle-rail {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 130px;
  pointer-events: none;   /* 기둥 자체는 판을 가리지 않는다 - 단추만 눌린다 */
}

.battle-rail .deploy-panel {
  pointer-events: auto;
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
}

/* 크게 - 손가락으로 누르는 단추다. */
.battle-rail .deploy-panel .btn {
  width: 130px;
  padding: 12px 8px;
  font-size: 15px;
  white-space: nowrap;
}

.battle-rail .battle-controls-foot {
  pointer-events: auto;
  position: absolute;
  right: 0;
  bottom: 2px;
  display: flex;
  gap: 6px;
  margin: 0;
}

.battle-rail .battle-controls-foot .btn { white-space: nowrap; }

/* 판 위에 이번 턴 낸 카드는 다시 누르면 거둬진다(167-2). 못 누르는 것처럼 보이면 거짓이다. */
.lane.recallable .unit { cursor: pointer; }
.lane.recallable .unit:hover { outline: 2px solid var(--gold); outline-offset: -2px; }


/*
 * 기둥 안의 턴 표시.
 *
 * 판 한가운데 있던 것을 옮겼다 - 거기서는 두 전장 사이를 갈라 34px 을 쓰고,
 * 그만큼 준비구(패)와 묘지가 눌린다.
 *
 * 기둥에서는 **세로로 쌓는다.** "5턴 · 내 차례" 와 그 아래 안내가 한 줄씩이다.
 */
.turn-marker-rail {
  display: flex;
  flex-direction: column;
  /*
   * 부모(.turn-marker)가 align-items: center 다. 세로로 세우면 그 값이
   * **자식을 글자 폭만큼 좁힌다** - 안내가 한 줄로 155px 이 되어 기둥 밖으로
   * 삐져나왔다. stretch 로 되돌려 기둥 폭에 맞춘다.
   */
  align-items: stretch;
  gap: 2px;
  text-align: center;
  padding: 0;
  line-height: 1.35;
}

/*
 * 안내는 여기서 **접힌다.**
 *
 * 원래 규칙은 판 한가운데(가로 한 줄)를 전제로 nowrap + ellipsis 였다 -
 * "좁으면 안내가 먼저 줄어든다" 는 그때 맞는 말이었다. 기둥에서는 반대로
 * **줄을 늘려야** 한다.
 *
 * 대상이 아이디 선택자(#deploy-hint)라 클래스로는 못 이긴다 - 아이디를 함께 쓴다.
 */
.turn-marker-rail #deploy-hint {
  font-size: 10px;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

/*
 * 로그는 화면에 두지 않는다(대표님 결정). DOM 에는 남는다 -
 * `battle.js` 의 `log()` 가 여기에 쓰고 `BattleActionCoverageTest` 가
 * 그 함수를 본다. `display: none` 이면 쓰기는 그대로 되고 보이지만 않는다.
 */
.battle-foot { display: none; }

/*
 * 영웅 한 명. 초상 그림이 없어 **세로로 쌓는다** -
 * 이름 · 레벨 · HP 막대 · 룬 넷.
 */
.hero-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 3px;
  padding: 5px 6px;
  border-radius: 8px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
}

/* 이름과 레벨은 한 줄에 나란히. 그래야 세로를 한 줄 아낀다. */
.hero-row .hero-name,
.hero-row .hero-lv { align-self: flex-start; }
.hero-label {
  font-size: 12px;
  color: var(--text-dim);
  min-width: 30px;
  /* 이름이 길어도 HP 막대를 밀어내지 않는다. */
  max-width: 96px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 0 0 auto;
}

/*
 * 영웅 이름. 서버가 이름을 준 판에서만 뜬다.
 * 이름을 알면 사전 라벨("상대"/"나")을 감춘다 - 둘 다 띄우면 "상대 고블린 주술사"가 된다.
 */
.hero-name {
  font-size: 12px;
  max-width: 110px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 0 0 auto;
}

.hero-name:empty { display: none; }
.hero-row.named .hero-label { display: none; }

/* 영웅 레벨. 원작은 초상 모서리에 동그란 배지로 붙인다. */
.hero-lv {
  flex: 0 0 auto;
  min-width: 17px;
  padding: 0 3px;
  border-radius: 9px;
  background: var(--bg-panel);
  border: 1px solid var(--gold);
  color: var(--gold);
  font-size: 10px;
  line-height: 15px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.hero-lv:empty { display: none; }

/*
 * 낀 룬 네 칸.
 *
 * 원작은 영웅 HP 구슬 **둘레**에 위·아래·좌·우로 붙여 둔다. 가로 390px 에서는
 * 구슬을 그릴 자리가 없어 HP 막대 옆에 한 줄로 세웠다 - 담긴 정보는 같다.
 * 무엇을 끼웠는지(속성 색)와 몇 번 더 터지는지(숫자)가 판 내내 보인다.
 *
 * 빈 칸도 네 개를 다 그린다. 그래야 "더 낄 수 있다"가 판 위에서 읽힌다.
 */
.hero-runes { display: flex; gap: 3px; flex: 0 0 auto; }

.hero-rune {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: #0c1218;
  font-size: 9px;
  line-height: 14px;
  text-align: center;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.hero-rune.filled { border-width: 2px; color: var(--text); }
.hero-rune.filled.FIRE  { border-color: #e2703a; background: #2a1410; }
.hero-rune.filled.WATER { border-color: #4aa3d8; background: #0e1e2a; }
.hero-rune.filled.WIND  { border-color: #6bbf7a; background: #10241a; }
.hero-rune.filled.EARTH { border-color: #c8a24a; background: #241d0e; }
.hero-rune.filled.IRON  { border-color: #a8aebb; background: #1c1e24; }

/* 다 쓴 룬. 흐리게만 두지 않는다 - 남은 횟수가 0 으로 함께 적힌다. */
.hero-rune.spent { opacity: .4; }

/* 터진 순간. 어느 칸이 일했는지 눈이 따라갈 수 있어야 한다. */
.hero-rune.rune-flash { animation: rune-pop calc(520ms * var(--fx)); }

@keyframes rune-pop {
  0%   { transform: scale(1); box-shadow: 0 0 0 rgba(255, 220, 130, 0); }
  35%  { transform: scale(1.45); box-shadow: 0 0 9px rgba(255, 220, 130, .9); }
  100% { transform: scale(1); box-shadow: 0 0 0 rgba(255, 220, 130, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-rune.rune-flash { animation: none; }
}

.hpbar {
  position: relative;
  flex: 1;
  height: 18px;
  border-radius: 9px;
  background: #0c1218;
  border: 1px solid var(--line);
  overflow: hidden;
}

.hpbar span {
  position: relative;
  z-index: 1;
  display: block;
  text-align: center;
  font-size: 11px;
  line-height: 16px;
  font-variant-numeric: tabular-nums;
}

.hpbar-fill { position: absolute; inset: 0; width: 100%; transition: width .25s; }
.hpbar-player .hpbar-fill { background: linear-gradient(90deg, #2f6fa8, #4a8fd4); }
.hpbar-enemy .hpbar-fill { background: linear-gradient(90deg, #8a4a6a, #b06ea8); }

/* ---------------------------------------------------------- 전투 판

   .lane 은 자리, 그 안의 .unit 이 카드다. 카드만 움직이므로 옆 칸이 밀리지 않는다.
   --fx 는 배속에 따라 자바스크립트가 바꾼다. 배속을 올렸는데 연출이 그대로면
   다음 수와 겹쳐서 무슨 일이 일어났는지 읽을 수 없다. */
#view-battle { --fx: 1; }

.field { padding: 10px 0; }
.lane-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; min-height: 88px; }

.lane {
  position: relative;
  border-radius: 8px;
  border: 1px dashed var(--line);
  min-height: 88px;
}

.lane .unit {
  position: absolute;
  inset: 0;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-panel);
  padding: 6px;
  font-size: 11px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  opacity: 0;
  transform: scale(.92);
  transition: opacity .16s, transform .16s;
  pointer-events: none;
}

.lane.filled { border-color: transparent; }
.lane.filled .unit { opacity: 1; transform: none; }
.lane.filled.FIRE .unit { border-color: var(--faction-fire); }
.lane.filled.WATER .unit { border-color: var(--faction-water); }
.lane.filled.WIND .unit { border-color: var(--faction-wind); }
.lane.filled.EARTH .unit { border-color: var(--faction-earth); }
.lane.filled.IRON .unit { border-color: var(--faction-iron); }

.lane .unit-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/*
 * 전투 카드의 그림.
 *
 * **전투 화면에만 그림이 없었다.** 덱·도감·상세는 처음부터 `Ui.cardImage` 로
 * 그림을 깔고 있었는데, 전투 칸은 이름과 수치만 그리고 있었다 -
 * 그림이 올라간 카드로 싸워도 판에서는 빈 상자만 보였다.
 *
 * 여기서 쓰는 <img> 는 타일과 **같은 `.card-art`** 다(`position: absolute; inset: 0`).
 * 그 클래스는 감싼 칸이 positioned 여야 제자리에 앉는다 - `.unit` 과 `.prep-card` 는
 * 둘 다 `position: absolute` 라 조건을 이미 만족한다. 카드 상세에서 이 전제를
 * 빠뜨려 그림이 844x390 을 통째로 덮은 적이 있다(.cm-art-wrap 주석).
 *
 * 그림 위에 어둠을 한 겹 덮는다. **덮지 않으면 밝은 그림에서 수치가 안 읽힌다** -
 * 색만으로 알리지 않는다는 원칙은 배경이 그림일 때 더 크게 걸린다.
 */
.unit-art, .prep-art {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  z-index: 0;
}

/*
 * --- 그림 자르는 위치 ---
 *
 * **전투 칸은 가로로 넓고(138x66) 그림은 세로다(2:3).** `object-fit: cover` 는
 * 폭을 맞추고 남는 위아래를 버리는데, 기본값이 `50% 50%` 라 **한가운데 띠**만 남는다 -
 * 인물 그림에서 그 자리는 대개 **허리춤**이다. 얼굴이 통째로 잘려 나가
 * 어떤 카드인지 알아볼 수 없었다.
 *
 * 세로 여유가 없어 칸을 세로로 바꿀 수는 없다 - 844x390 에서 전투 화면은
 * `docH == innerH` 로 **여유가 0** 이다(두 줄 x 66px + 준비구 + 로그).
 * 그래서 칸이 아니라 **어디를 남길지**를 고친다.
 *
 * 22% 는 폭 123 기준으로 그림의 **13~45% 구간**을 남긴다 - 인물 초상에서
 * 얼굴과 상체가 있는 자리다. `.cm-art-wrap img` 가 같은 이유로 30% 를 쓰는데,
 * 그쪽은 잘리는 양이 훨씬 적어 값이 다르다.
 */
.unit-art img, .prep-art img { object-position: 50% 22%; }

.unit-art::after, .prep-art::after {
  content: "";
  position: absolute;
  inset: 0;
  /*
   * 여기도 가운데를 열어 준다. 이름은 위, 수치는 아래에 있다.
   * 칸이 작을수록 막이 두꺼우면 그림이 통째로 사라진다.
   */
  background: linear-gradient(
      180deg,
      rgba(8, 12, 16, .80) 0%,
      rgba(8, 12, 16, .34) 40%,
      rgba(8, 12, 16, .34) 58%,
      rgba(8, 12, 16, .86) 100%);
}

/*
 * 글자와 수치는 그림 위에. 안 올리면 그림이 나중에 와서 글자를 덮는다.
 *
 * **이미 absolute 인 것에는 position 을 다시 주지 않는다** - `.prep-wait` 과
 * `.prep-lv` 은 구석에 앉히려고 absolute 로 둔 것이라, 여기서 relative 로
 * 덮어쓰면 둘 다 제자리를 잃고 흐름 속으로 돌아온다. z-index 만 올린다.
 */
.lane .unit-name, .lane .unit-stats, .lane .unit-lv, .lane .unit-hp,
.prep-name, .prep-stats {
  position: relative;
  z-index: 1;
}

.unit-badges, .prep-lv, .prep-wait { z-index: 1; }

.lane .unit-stats { display: flex; justify-content: space-between; font-variant-numeric: tabular-nums; }

/*
   카드 레벨. 원작도 카드에 "LV.4" 를 찍는다. 수치가 왜 다른지 알려면 필요하다.

   **한 줄에 셋을 넣지 않는다.** 예전에는 LV·공격력·체력을 한 줄에 두고
   `margin-right: auto` 로 벌려 놓았는데, 칸이 좁으면 남는 자리가 0 이라
   그 auto 가 0 이 되어 **세 값이 통째로 붙었다** - "LV.3" 과 "434" 가
   `LV.3434` 로 읽혀 레벨이 34로 보였다. 세로로 든 휴대전화에서 칸이
   65px 밖에 안 되면 늘 이렇게 된다.

   그래서 전장 칸에서는 LV 이 **자기 줄**이고, 준비구에서는 대기 턴 배지의
   **반대쪽 구석**이다(준비구는 40px 라 줄을 하나 더 못 만든다. `.prep-wait` 이
   이미 왼쪽 아래에 절대 위치로 앉아 있어 그 짝으로 오른쪽 아래에 둔다).
 */
.unit-lv { color: var(--text-dim); line-height: 1.2; }
.unit-lv:empty, .prep-lv:empty { display: none; }

/*
 * 준비구의 카드 레벨.
 *
 * **오른쪽 위**다. 아래는 이미 둘이 차 있다 - 왼쪽 아래에 대기 턴 배지가,
 * 그 줄에 공격력·체력이 있다. 아래에 두었더니 체력 숫자 위에 그대로 겹쳤다.
 */
.prep-lv {
  position: absolute;
  right: 4px;
  top: 3px;
  color: var(--text-dim);
  font-size: 9px;
  line-height: 13px;
  font-variant-numeric: tabular-nums;
}
.lane .unit-atk, .prep-atk { margin-right: 6px; }
.lane .unit-hp { height: 4px; border-radius: 2px; background: #0c1218; overflow: hidden; margin-top: 3px; }
.lane .unit-hp i { display: block; height: 100%; background: var(--ok); transition: width .2s; }
/* 위험을 색으로만 알리지 않는다. 막대 폭이 이미 줄어 있고 색은 거들 뿐이다. */
.lane .unit-hp i.low { background: var(--danger); }

/* 상태 배지. 겹쳐도 읽히도록 카드 오른쪽 위에 모아 둔다. */
.unit-badges { position: absolute; top: 2px; right: 3px; display: flex; gap: 1px; font-size: 10px; }
.unit-badges .badge { line-height: 1; filter: drop-shadow(0 1px 1px #000); }

/* ---------------------------------------------------------- 전투 연출 */

/* 찌르기. 아군은 위로, 적군은 아래로 - 방향만으로 누가 때렸는지 안다. */
.lane .unit.strike-up { animation: strike-up calc(320ms * var(--fx)) ease-out; z-index: 3; }
.lane .unit.strike-down { animation: strike-down calc(320ms * var(--fx)) ease-out; z-index: 3; }
@keyframes strike-up {
  0% { transform: translateY(0); }
  40% { transform: translateY(-14px) scale(1.05); }
  100% { transform: translateY(0); }
}
@keyframes strike-down {
  0% { transform: translateY(0); }
  40% { transform: translateY(14px) scale(1.05); }
  100% { transform: translateY(0); }
}

.lane .unit.hit { animation: unit-hit calc(300ms * var(--fx)); }
@keyframes unit-hit {
  0%, 100% { transform: translateX(0); box-shadow: none; }
  20% { transform: translateX(-4px); box-shadow: inset 0 0 0 99px rgba(212, 96, 74, .35); }
  60% { transform: translateX(4px); box-shadow: inset 0 0 0 99px rgba(212, 96, 74, .18); }
}

.lane .unit.entering { animation: unit-enter calc(360ms * var(--fx)) ease-out; }
@keyframes unit-enter {
  0% { opacity: 0; transform: scale(.7); }
  100% { opacity: 1; transform: none; }
}

/* 죽는 장면은 복제본에만 건다. 원본은 이미 판에서 빠졌다. */
.lane .unit.ghost { pointer-events: none; z-index: 2; }
/*
 * 죽은 카드가 **묘지로 날아간다.**
 *
 * 예전에는 제자리에서 작아지며 사라졌다 - 어디로 갔는지가 안 보였다.
 * 묘지 장수는 룬 조건("묘지에 황야 카드가 1장 이상")과 얽혀 있어 눈이
 * 따라가야 하는 값이다.
 *
 * `--dx` · `--dy` 는 자바스크립트가 묘지 칸까지의 거리를 재서 넣는다.
 * 못 재면 둘 다 0 이라 **제자리에서** 사라진다 - 화면 구석으로 튀지 않는다.
 */
.unit.to-grave {
  --dx: 0px;
  --dy: 0px;
  pointer-events: none;
  z-index: 30;
  animation: unit-to-grave calc(620ms * var(--fx)) cubic-bezier(.4, 0, .7, 1) forwards;
}

@keyframes unit-to-grave {
  0% {
    opacity: 1;
    transform: none;
  }
  /* 먼저 한 번 움찔한다. 맞아서 죽었다는 것이 읽힌다. */
  18% {
    opacity: 1;
    transform: scale(1.06) rotate(-3deg);
  }
  100% {
    opacity: 0;
    transform: translate(var(--dx), var(--dy)) scale(.22) rotate(-16deg);
  }
}

/* 판 안에서 죽는 옛 연출. 묘지 칸을 못 찾았을 때만 쓴다. */
.lane .unit.dying { animation: unit-die calc(520ms * var(--fx)) forwards; }
@keyframes unit-die {
  0% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: scale(.6) rotate(-8deg) translateY(10px); }
}

/* 묘지에 한 장 들어올 때 그 칸도 한 번 뛴다. 어디에 쌓였는지 눈에 걸린다. */
.grave-slot.filled { transition: transform .18s ease-out; }
.grave-slot.landing { transform: scale(1.14); }

@media (prefers-reduced-motion: reduce) {
  .unit.to-grave { animation: none; opacity: 0; }
  .grave-slot.landing { transform: none; }
}

.lane .unit.healing { animation: unit-heal calc(400ms * var(--fx)); }
@keyframes unit-heal {
  0%, 100% { box-shadow: none; }
  50% { box-shadow: inset 0 0 0 99px rgba(85, 185, 138, .3); }
}

.lane .unit.buffing { animation: unit-buff calc(400ms * var(--fx)); }
@keyframes unit-buff {
  0%, 100% { box-shadow: none; }
  50% { box-shadow: inset 0 0 0 99px rgba(232, 182, 76, .28); }
}

/* 얼어서/기절해서 아무것도 못 한 턴. 아무 일도 안 일어나면 버그처럼 보인다. */
.lane .unit.blocked { animation: unit-blocked calc(320ms * var(--fx)); }
@keyframes unit-blocked {
  0%, 100% { filter: none; }
  50% { filter: grayscale(1) brightness(1.4); }
}

/* 칸 위로 떠오르는 숫자. 어디서 무슨 일이 일어났는지 눈이 따라갈 수 있어야 한다. */
.lane .fx { position: absolute; inset: 0; pointer-events: none; overflow: visible; z-index: 4; }

.float {
  position: absolute;
  left: 50%;
  top: 30%;
  transform: translateX(-50%);
  font-weight: 800;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 2px #000, 0 0 6px #000;
  animation: float-up calc(900ms * var(--fx)) ease-out forwards;
  white-space: nowrap;
}

.float.damage { color: #ff8a72; }
.float.damage.crit { color: #ffd24a; font-size: 19px; }
.float.heal { color: #7fe0b0; }
.float.buff { color: var(--gold); font-size: 12px; }
.float.status { font-size: 16px; }
.float.miss { color: var(--text-dim); font-size: 12px; }

@keyframes float-up {
  0% { opacity: 0; transform: translate(-50%, 6px) scale(.8); }
  20% { opacity: 1; transform: translate(-50%, 0) scale(1.1); }
  100% { opacity: 0; transform: translate(-50%, -28px) scale(1); }
}

/* 영웅 체력바 위에 뜨는 숫자는 자리가 좁아 오른쪽에 붙인다. */
.hero-row { position: relative; }
.hero-row .hero-float { left: auto; right: 4px; top: -4px; transform: none; }
@keyframes float-up-right {
  0% { opacity: 0; transform: translateY(6px); }
  20% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-22px); }
}
.hero-row .hero-float { animation-name: float-up-right; }

.hpbar.bar-hit { animation: bar-hit calc(320ms * var(--fx)); }
@keyframes bar-hit {
  0%, 100% { box-shadow: none; }
  40% { box-shadow: 0 0 0 2px var(--danger); }
}
.hpbar.bar-heal { animation: bar-heal calc(320ms * var(--fx)); }
@keyframes bar-heal {
  0%, 100% { box-shadow: none; }
  40% { box-shadow: 0 0 0 2px var(--ok); }
}

/* 영웅이 맞으면 화면이 흔들린다. 카드끼리 치고받는 것과 무게가 달라야 한다. */
.view-battle.shake-screen { animation: screen-shake calc(300ms * var(--fx)); }
@keyframes screen-shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(-3px, 2px); }
  50% { transform: translate(3px, -2px); }
  75% { transform: translate(-2px, -1px); }
}

.view-battle.shock { animation: screen-shock calc(260ms * var(--fx)); }
@keyframes screen-shock {
  0%, 100% { filter: none; }
  30% { filter: brightness(1.35) saturate(1.2); }
}

/* 움직임에 불편함을 느끼는 사람이 있다. 시스템 설정을 따르되 정보는 줄이지 않는다 -
   숫자와 글자는 그대로 바뀌고 움직임만 멈춘다. */
@media (prefers-reduced-motion: reduce) {
  .lane .unit,
  .lane .unit.strike-up,
  .lane .unit.strike-down,
  .lane .unit.hit,
  .lane .unit.entering,
  .lane .unit.dying,
  .lane .unit.healing,
  .lane .unit.buffing,
  .lane .unit.blocked,
  .view-battle.shake-screen,
  .view-battle.shock,
  .hpbar.bar-hit,
  .hpbar.bar-heal,
  .float {
    animation: none !important;
    transition: none !important;
  }
}

/* 한 줄로 못박는다. 안내가 아래로 내려가면 판 전체가 그만큼 밀린다. */
.turn-marker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 0;
  font-size: 12px;
  color: var(--text-dim);
}
/* 안내는 턴 표시 옆에 붙는다. 배치할 때만 글자가 들어간다. */
#deploy-hint {
  font-size: 11px;
  /* 좁으면 안내가 먼저 줄어든다. 턴 표시는 끝까지 지킨다. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
#deploy-hint:empty { display: none; }
#turn-label { padding: 3px 10px; border-radius: 10px; background: var(--bg-panel); border: 1px solid var(--line); white-space: nowrap; flex: 0 0 auto; }

.battle-log {
  height: 110px;
  overflow-y: auto;
  padding: 8px 10px;
  border-radius: 8px;
  background: #0c1218;
  border: 1px solid var(--line);
  font-size: 12px;
  color: var(--text-dim);
}

.battle-log div { padding: 1px 0; }
.battle-log .mine { color: #9fc7ea; }
.battle-log .theirs { color: #d9a8cf; }

.battle-controls { display: flex; gap: 8px; justify-content: flex-end; margin-top: 10px; }

.battle-result {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: rgba(8, 12, 16, .88);
  padding: 24px;
  text-align: center;
}

.battle-result h2 { margin: 0; font-size: 32px; }
/*
 * 결과 글자에 **자기 바탕**을 준다.
 *
 * 덮개가 .88 이라 뒤의 판이 12% 비쳐 나온다. 줄이 둘일 때는 견딜 만했는데
 * 35단계에서 난이도와 조건 두 줄이 늘면서 살아남은 카드 위에 글자가 겹쳤다.
 * 뒤에 무엇이 있든 읽히게 두는 편이, 덮개 농도를 올려 연출을 지우는 것보다 낫다.
 */
.result-detail {
  color: var(--text-dim);
  font-size: 14px;
  line-height: 1.8;
  padding: 10px 18px;
  border-radius: 10px;
  background: rgba(8, 12, 16, .92);
  border: 1px solid var(--line);
  max-width: 92%;
}

/* ---------------------------------------------------------- 기타 */

.rates-box { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 10px; font-size: 12px; }
.rates-box .rate-row { display: flex; justify-content: space-between; padding: 2px 0; }
.rates-box .rate-row b { font-variant-numeric: tabular-nums; }

.shop-actions { display: flex; gap: 8px; margin-top: 10px; }

.toast {
  position: fixed;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  z-index: 40;
  max-width: 90%;
  padding: 10px 16px;
  border-radius: 20px;
  background: rgba(0, 0, 0, .88);
  border: 1px solid var(--line);
  font-size: 13px;
  text-align: center;
}


/* ---------------------------------------------------------- 카드 상세 */

.modal {
  position: fixed;
  inset: 0;
  z-index: 35;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(8, 12, 16, .8);
}

.modal-box {
  width: 100%;
  max-width: 560px;
  max-height: 82vh;
  overflow-y: auto;
  padding: 16px;
  border-radius: 14px 14px 0 0;
  background: var(--bg-panel);
  border: 1px solid var(--line);
}

.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.modal-head h3 { margin: 0 0 2px; font-size: 18px; }
/*
  머리말 오른쪽의 단추 둘(잠금·닫기). 이름은 화면에 있었는데 **규칙이 없었다** -
  block 이라 줄바꿈에 기대고 있었고, 잠금 글자가 길어지면 아래로 흘렀다.
  `flex: none` 이 없으면 이름이 긴 카드에서 이 칸부터 찌그러진다.
*/
.cm-head-actions { display: flex; gap: 6px; flex: none; }

/*
 * 카드 상세의 경험치 막대. 원작이 레벨 옆에 그대로 둔다("Lv.3 [64/320]").
 *
 * **막대는 <span> 이 아니라 블록이어야 한다.** inline 에는 height 가 안 먹어
 * 조용히 안 보인다(룬 막대에서 한 번 겪었다). display:inline-block 으로 둔다.
 *
 * `.costbar` 를 가져다 쓰지 않는다 - 그 클래스는 `flex: 1 1 100%` 를 전제해
 * 여기서는 막대가 아랫줄로 밀려 사라진다. 남의 클래스는 이름만 보면 안 걸린다.
 */
.cm-exp {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
}

.cm-exp-bar {
  display: inline-block;
  position: relative;
  flex: 0 0 auto;
  width: 120px;
  height: 6px;
  border-radius: 999px;
  background: var(--bg);
  border: 1px solid var(--line);
  overflow: hidden;
}

.cm-exp-bar i {
  display: block;
  height: 100%;
  background: var(--gold);
}

.cm-exp-text {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  /* "0 / 60" 이 좁은 칸에서 두 줄로 갈라지면 막대보다 키가 커진다. */
  white-space: nowrap;
}

.cm-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  margin: 12px 0 4px;
  text-align: center;
}

.cm-stats div {
  padding: 8px 4px;
  border-radius: 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}

.cm-stats small { display: block; color: var(--text-dim); font-size: 11px; margin-bottom: 2px; }

/*
 * ---- 넓은 화면: 그림은 왼쪽, 읽을 것은 오른쪽 ----
 *
 * 대표님이 잘린 그림을 보내 주셨다. 위아래로 쌓는 배치에서는 그림이 가로로
 * 꽉 찬 띠(120px)가 되는데, 세로 2:3 으로 올린 그림을 가로로 넓게 자르면
 * **얼굴만 남고 나머지가 다 잘린다.** 그림을 키우면 이번엔 스킬이 밀린다 -
 * 세로 390px 에서는 둘이 같은 높이를 놓고 다툰다.
 *
 * 가로가 남으므로 **세로 대신 가로를 쓴다.** 그림은 원래 비율대로 왼쪽에
 * 세우고, 이름·수치·스킬은 오른쪽에 모아 그 안에서만 스크롤한다.
 *
 * 좁은 화면(데스크톱에서 창을 줄였을 때)에서는 예전 배치 그대로다 -
 * 두 칸으로 쪼개면 양쪽 다 못 읽을 만큼 좁아진다.
 */
@media (min-width: 620px) {
  .modal-box.cm-box {
    display: flex;
    gap: 14px;
    max-width: 720px;
    /*
     * 높이를 못 박는다. 안쪽(오른쪽)이 스크롤하려면 바깥이 늘어나면 안 된다 -
     * 바깥이 함께 늘면 스크롤 막대가 두 개 생기고 그림이 위로 밀려 나간다.
     *
     * **상한을 함께 둔다.** 그림 너비는 높이가 정하므로(2:3), 높이만 82vh 로 두면
     * 데스크톱 800px 짜리 창에서 그림이 415px 까지 커져 오른쪽 칸을 밀어낸다 -
     * 실제로 [잠그기]와 [닫기]가 두 줄로 갈라지고 "Lv 0/10" 이 접혔다.
     */
    height: min(82vh, 420px);
    overflow: hidden;
  }

  .cm-box .cm-art-wrap {
    /*
     * 남은 높이를 다 쓰고, 너비는 비율이 정한다. 자르기 창이 2:3 으로 만들어
     * 올리므로 이 비율에서는 **아무것도 잘리지 않는다**(98·99장).
     * 옛 3:4 그림 스물두 장은 좌우가 조금 잘린다 - object-fit: cover 가 맡는다.
     */
    flex: 0 0 auto;
    width: auto;
    height: 100%;
    aspect-ratio: 2 / 3;
    margin: 0;
    border-radius: 10px;
  }

  /* 비율이 맞으므로 위로 당길 이유가 없다. 가운데를 그대로 보여 준다. */
  .cm-box .cm-art-wrap img { object-position: 50% 50%; }

  .cm-box .cm-body {
    flex: 1 1 auto;
    /* flex 자식의 기본값은 min-width: auto 다. 안 낮추면 긴 스킬 문장이 칸을 밀어낸다. */
    min-width: 0;
    overflow-y: auto;
  }

  /*
   * 그림이 없는 카드는 두 칸으로 쪼갤 것이 없다. 왼쪽을 비운 채 두면
   * 오른쪽만 있는 창이 쓸데없이 좁아진다 - 예전 배치로 되돌린다.
   */
  .modal-box.cm-box:has(.cm-art-wrap.hidden) {
    display: block;
    height: auto;
    max-height: 82vh;
    max-width: 560px;
    overflow-y: auto;
  }
}

.cm-section { margin: 16px 0 6px; font-size: 13px; color: var(--text-dim); font-weight: 400; }
.cm-list { display: flex; flex-direction: column; gap: 6px; }

.skill-row {
  padding: 9px 11px;
  border-radius: 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 13px;
}

/* 잠긴 스킬은 흐리게 + 자물쇠 + 해금 레벨 문구를 함께 쓴다. 색만으로 구분하지 않는다. */
.skill-row.locked { opacity: .5; }
.skill-row .slot { color: var(--text-dim); font-size: 11px; margin-right: 6px; }
.skill-row .unlock { color: var(--gold); font-size: 11px; margin-left: 6px; }
.cm-list .empty { color: var(--text-dim); font-size: 12px; padding: 6px 0; }

/*
 * 좁으면 줄바꿈으로 넘긴다. 말줄임(...)으로 자르면 "스킬 1/3" 의 숫자가
 * 잘려 나가 카드를 키울 이유가 화면에서 사라진다. 정보를 자르느니 한 줄 더 쓴다.
 */
.card .card-skills { min-width: 0; }


/*
 * 뽑기 결과 타일. 다른 화면의 카드와 같은 모양이라야 무엇을 얻었는지 바로 안다.
 * ★4 이상만 테두리가 다르다 - 별이 함께 찍혀 있으므로 색만으로 알리는 것은 아니다.
 */
.draw-rare {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px var(--gold) inset;
}

/* ---------------------------------------------------------- 강화소 */

/*
 * ---- 강화소: 원작 배치 ----
 *
 * 왼쪽에 대상 카드, 가운데에 재료와 비용, 오른쪽에 단추 기둥.
 * 세로 390px 에서 위아래로 쌓으면 아래 카드 목록이 화면 밖으로 밀린다.
 */
.enhance-panel {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  flex-wrap: wrap;
}

.enh-target { flex: 0 0 auto; width: 120px; }
.enh-target h3 { margin: 0 0 6px; font-size: 13px; color: var(--text-dim); font-weight: 400; }
/* 대상 카드는 타일 그대로 쓴다. 목록의 그것과 같아야 어느 카드인지 바로 안다. */
.enh-target .card { margin: 0; }
.enh-target-info { margin-top: 6px; font-size: 11px; }

.enh-materials { flex: 1 1 200px; min-width: 0; }
.enh-cost { font-size: 14px; font-weight: 700; color: var(--gold); margin-bottom: 6px; }

.enh-mat-list { display: flex; flex-direction: column; gap: 4px; }

/* 재료 한 줄: 이름 · 레어도 · 강화레벨. 원작이 이 셋을 적어 준다. */
.enh-mat {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 6px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 12px;
}

.enh-mat-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.enh-mat-star { flex: 0 0 auto; color: var(--gold); font-size: 11px; }
.enh-mat-level { flex: 0 0 auto; color: var(--text-dim); font-size: 11px; }
/*
 * 재료가 주는 경험치. **레벨 옆에 붙여 둔다** - 그 둘을 나란히 봐야
 * "키운 카드가 더 준다" 가 눈에 들어온다. 색은 눈에 띄는 쪽으로.
 */
.enh-mat-exp { flex: 0 0 auto; color: var(--accent); font-size: 11px; font-variant-numeric: tabular-nums; }

/*
 * 룬 줄. 재료 줄(`.enh-mat`)과 같은 모양을 쓰되 **누를 수 있다.**
 * 대상·재료로 고른 줄은 테두리가 달라지고, 글자(대상/재료)도 함께 붙는다 -
 * 색만으로 알리지 않는다(원칙 4번).
 */
/* 아직 안 고른 자리. 카드 한 장 크기의 빈 틀만 둔다 - 안내는 가운데 칸이 적는다. */
.enh-slot-empty {
  height: 96px;
  border-radius: 8px;
  border: 1px dashed var(--line);
  background: var(--bg);
}

.rune-row {
  width: 100%;
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.rune-row.target { border-color: var(--gold); box-shadow: inset 3px 0 0 var(--gold); }
.rune-row.material { border-color: var(--accent); }
/* 다른 덱에 끼워진 룬은 재료가 되지 않는다. 서버가 거절하기 전에 화면이 말한다. */
.rune-row.locked { opacity: .55; }

.enh-actions { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; width: 92px; }
.enh-actions .btn { width: 100%; }
/* 안내 문구는 세 칸 아래에 통째로 눕는다. */
.enh-hint { flex: 1 1 100%; margin: 6px 0 0; }

/*
 * **판과 목록을 좌우로 가른다**(2026-09-03 대표님: "각각 한 화면에 담고싶어").
 *
 * 위아래로 쌓으면 카드를 하나 고르는 순간 판이 304px 이 되어 **목록이 통째로
 * 화면 밖으로 밀린다** - 재료를 고르려면 매번 스크롤해 내려갔다 올라와야 했다.
 * 가로는 844px 이라 남는데 세로만 390px 이라서 생기는 일이다.
 *
 * 넓은 화면(데스크톱·세로)에서는 지금까지처럼 위아래로 둔다 - 세로가 넉넉하면
 * 목록이 넓은 편이 낫다. 좌우로 가르는 것은 아래 세로 560px 이하 규칙이 한다.
 */
/*
 * 거르개 칩. 눌린 것은 **금테와 굵은 글씨**로 함께 말한다 - 색만으로 알리지
 * 않는다(원칙 4번). 진영 칩은 문양만 담지만 모양이 서로 다르고 `title` 에
 * 이름이 있다.
 */
/*
 * 군단전 지난 판. 머리 줄을 누르면 자리별 대진표가 그 밑에 펼쳐진다.
 * 머리 줄은 `<button>` 이라 기본 모양을 지워 주어야 목록의 다른 줄과 같아진다.
 */
.war-row { margin-bottom: 4px; }
.war-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
/* 펼쳐진 자리들은 한 칸 들여써서 어느 판에 딸린 것인지 눈에 보이게 한다. */
.war-slots { padding-left: 12px; border-left: 2px solid var(--line); margin: 2px 0 6px; }

.enh-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 6px; }
.enh-chip-group { display: inline-flex; gap: 4px; }
.enh-chip {
  border: 1px solid var(--line);
  background: var(--bg-raised);
  color: var(--text-dim);
  border-radius: 999px;
  padding: 3px 9px;
  font-size: 11px;
  line-height: 1.4;
  cursor: pointer;
}
.enh-chip:hover { border-color: var(--gold); }
.enh-chip.active {
  border-color: var(--gold);
  color: var(--text);
  font-weight: 700;
  box-shadow: inset 0 0 0 1px var(--gold);
}
/* 문양 칩은 글자가 없으므로 좌우 여백을 줄이고 문양을 가운데에 세운다. */
.enh-chip .elem { display: inline-block; vertical-align: -1px; margin: 0; }

.enh-split > .enh-main { min-width: 0; }
/*
 * **쪽을 나눌지, 몇 칸으로 놓을지는 스타일이 정한다.**
 *
 * - `--enh-paged` 가 1 일 때만 쪽으로 나눈다. 넓은 화면에서는 0 이라 통째로 둔다
 * - `--enh-cols` 는 격자(`grid-template-columns`)와 화면 코드가 **같이** 읽는다 -
 *   칸 수를 자바스크립트에도 적어 두면 칸을 바꾸는 날 둘이 어긋난다(길이를
 *   CSS 와 JS 두 곳에 적어 전투 연출이 어긋났던 144-3 과 같은 종류다)
 * - `--enh-rows` 는 **못 잴 때의 대비책**이다. 화면이 아직 안 보이면
 *   높이가 0 이라 잴 수 없다 - 줄 수는 원래 화면 코드가 재서 정한다
 */
#enhance-list, #rune-enh-list { --enh-paged: 0; --enh-cols: 1; --enh-rows: 99; }

/* 쪽 넘김 줄. 쪽이 하나뿐이면 화면이 통째로 감춘다. */
.enh-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 6px;
}
.enh-page { font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
/*
 * 잠겼으면 오른쪽(목록) 기둥을 통째로 접는다. `lockPanel` 이 목록만 감추고
 * 안내 문구는 남겨 두어, 자물쇠 바로 밑에 "재료를 고르세요" 가 서 있었다.
 */
.enh-split:has(.enh-locked:not(.hidden)) > .enh-main { display: none; }

.enhance-actions { display: flex; gap: 8px; margin-top: 10px; }
.enhance-actions .btn { flex: 1; }

/*
 * 대상은 테두리, 재료는 채움으로 구분한다. 색만으로는 구분되지 않는다.
 *
 * 글자는 **사전에서 온다.** 예전에는 여기에 "대상"/"재료"라고 한국어를 박아 두어,
 * 어느 언어로 봐도 한국어가 나왔다. CSS 의 content 는 사전을 거치지 않으므로
 * 화면이 data-badge 로 넣어 주고 여기서는 그것만 읽는다.
 */
.card.target { box-shadow: 0 0 0 3px var(--gold) inset; }
.card.target::after {
  content: attr(data-badge);
  position: absolute;
  /*
   * **`top`·`right` 만 적으면 안 된다.** 그림이 있는 카드에는 `.card:has(.card-art)::after`
   * 가 먼저 `inset: 0` 을 걸어 두었고, 그것은 네 방향의 줄임말이라 여기서 위·오른쪽만
   * 덮으면 **아래·왼쪽은 0 인 채로 남는다** - 이름표가 타일 전체로 늘어나 금색으로
   * 통째로 칠해진다. 그림 있는 카드가 아직 서른 장뿐이라 여태 아무도 못 봤다.
   */
  inset: 4px 4px auto auto;
  background: var(--gold);
  color: #10161c;
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 10px;
  font-weight: 700;
}

.card.material { opacity: .75; box-shadow: 0 0 0 2px var(--danger) inset; }
.card.material::after {
  content: attr(data-badge);
  position: absolute;
  /* 대상 쪽과 같은 이유(바로 위 주석). */
  inset: 4px 4px auto auto;
  background: var(--danger);
  color: #fff;
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 10px;
  font-weight: 700;
}

.card.evolved-mark { border-style: double; border-width: 4px; }


/* ---------------------------------------------------------- 신전 */

.temple-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }

.temple-npc {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--bg-panel);
}

/* 만날 수 없는 인물은 흐리게 + 문구로 알린다. 색만으로 구분하지 않는다. */
.temple-npc.busy { opacity: .45; }
.temple-npc .npc-name { font-weight: 700; }
.temple-npc .npc-meta { font-size: 12px; color: var(--text-dim); }

.fragment-list { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }

.fragment-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 13px;
}

.frag-3 { color: #7fb6e8; }
.frag-4 { color: #b98fe0; }
.frag-5 { color: var(--gold); }

/* ---------------------------------------------------------- 미로 */

/*
 * 미로 고르기 줄.
 *
 * 가로가 390px 뿐이라 지도가 늘면 넘친다. 줄바꿈이 아니라 **가로 스크롤**로 둔다 -
 * 줄이 늘면 판이 아래로 밀려 미로 자체가 화면 밖으로 나간다.
 */
/* 랭킹전 대기 시간 줄. 시계와 해제 단추를 한 줄에 둔다. */
/* 쿠폰조각 한 줄. 세로가 390px 뿐이라 판을 만들지 않고 줄로 둔다. */
/* 침공 표시. 색만으로 알리지 않으므로 글자와 해골이 함께 온다. */
/*
 * 숨은 관문.
 *
 * 처음에 점선 테두리로 구별하려 했는데 **잠긴 칸이 이미 전부 점선**이었다 -
 * 규칙을 넣고도 아무 일도 일어나지 않았다. 눈으로 보지 않았으면 몰랐을 종류다.
 *
 * 정보를 나르는 것은 번호 자리의 **✦ 글자**다. 색은 거들기만 한다 -
 * 색만으로 알리지 않는다는 원칙 그대로다.
 */
.stage-node.hidden-stage .stage-tile {
  border-color: var(--accent);
}

.invaded {
  color: var(--danger, #d9534f);
  font-size: 12px;
}

.coupon-piece {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  font-size: 13px;
}

.maze-picker {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding-bottom: 6px;
}

.maze-picker button {
  flex: 0 0 auto;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text);
  font-size: 12px;
  cursor: pointer;
}

.maze-picker button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  font-weight: 700;
}

/*
 * 고른 단추 안의 층수.
 *
 * .dim 을 그대로 두면 **파랑 바탕에 파랑 글자**가 된다 - 남의 클래스를 가져다 쓰면
 * 그 클래스가 전제한 바탕까지 따라오지 않는다는 것을 또 한 번 밟았다.
 */
.maze-picker button.active .dim { color: rgba(255, 255, 255, 0.85); }

/* 잠긴 지도는 색만으로 알리지 않는다. 자물쇠 글자를 함께 붙인다. */
.maze-picker button[disabled] {
  opacity: 0.55;
  cursor: default;
}

.maze-head {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: baseline;
  padding-bottom: 10px;
  font-variant-numeric: tabular-nums;
}

.maze-head b { font-size: 18px; }

.maze-grid {
  display: grid;
  gap: 4px;
  margin-bottom: 12px;
}

.maze-cell {
  aspect-ratio: 1;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: #0c1218;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  cursor: default;
}

/* 안개는 어둡게, 걷힌 칸은 밝게. 갈 수 있는 칸에는 테두리를 준다. */
.maze-cell.fog { background: #070a0d; border-color: #1a2430; }
.maze-cell.open { background: var(--bg-panel); }
.maze-cell.cleared { opacity: .5; }
.maze-cell.reachable { border-color: var(--accent); cursor: pointer; }
.maze-cell.player { background: var(--accent); }

/* ---------------------------------------------------------- 도감 */

.codex-summary { padding-bottom: 10px; font-variant-numeric: tabular-nums; }
.codex-filters { display: flex; gap: 6px; padding-bottom: 8px; }
.codex-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }

/* 못 얻은 것은 흐리게 + 물음표. 무엇이 남았는지는 보이되 상세는 숨긴다. */
.codex-entry {
  position: relative;
  padding: 8px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-panel);
  font-size: 12px;
}

.codex-entry.missing { opacity: .4; }
/* 누를 수 있는 타일이라는 것을 손가락 모양으로 알린다(209장). 색으로만 말하지 않는다. */
.codex-entry.codex-openable { cursor: pointer; }
.codex-entry.codex-openable:hover { border-color: var(--gold); }
.codex-entry .codex-name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.codex-entry .codex-count { position: absolute; top: 4px; right: 6px; font-size: 10px; color: var(--text-dim); }


/* ---------------------------------------------------------- 임무 */

.attendance-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 12px;
}

.att-day {
  padding: 8px 4px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  text-align: center;
  font-size: 11px;
  line-height: 1.4;
}

/* 받은 보상은 테두리와 체크로 알린다. 색만으로 구분하지 않는다. */
.att-day.earned { border-color: var(--gold); }
.att-day.earned .att-mark { color: var(--gold); }
.att-day .att-no { font-weight: 700; }
.att-day .att-reward { color: var(--text-dim); }

.quest-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }

.quest-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--bg);
}

.quest-row.done { border-color: var(--ok); }
.quest-row.claimed { opacity: .5; }
.quest-row .quest-body { flex: 1; min-width: 0; }
.quest-row .quest-name { font-size: 13px; }
.quest-row .quest-meta { font-size: 11px; color: var(--text-dim); }

.quest-bar { height: 5px; border-radius: 3px; background: #0c1218; overflow: hidden; margin-top: 4px; }
.quest-bar i { display: block; height: 100%; background: var(--accent); }

/* ---------------------------------------------------------------- 언어 전환 · 설정 단추
   구석에 떠 있던 <select> 는 2026-09-03 에 없앴다. 언어는 설정 안에 있다(.lang-select 셋).
   로그인 화면은 오른쪽 위 ⚙ 이 작은 판을 열고, 마을은 오른쪽 위 ⚙ 이 설정 화면으로 간다. */
.lang-select {
  appearance: none;
  background: var(--bg-raised);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: 13px;
  padding: 4px 10px;
  cursor: pointer;
}
.lang-select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.corner-settings, .home-settings {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(16, 22, 28, .78);
  color: var(--text-dim);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.corner-settings:active, .home-settings:active { transform: translateY(1px); }

#view-login { position: relative; }
#view-login .corner-panel { position: absolute; top: 48px; right: 8px; width: 220px; margin: 0; z-index: 3; }
#view-login .corner-panel .field { margin: 0; }

/* ---------------------------------------------------------------- 소셜 로그인
   각 서비스의 브랜드 색을 쓴다. 사용자가 색으로 먼저 알아보는 버튼이라
   여기서는 색이 곧 정보다. 다만 글자로도 어느 서비스인지 항상 적는다. */
.social-buttons { display: grid; gap: 8px; }

.social-buttons .btn { text-align: center; text-decoration: none; line-height: 1.9; }

.social-kakao  { background: #fee500; color: #191600; border-color: #e5cf00; }
.social-google { background: #ffffff; color: #1f1f1f; border-color: #dadce0; }
.social-apple  { background: #000000; color: #ffffff; border-color: #000000; }

.social-kakao:hover  { background: #f5dc00; }
.social-google:hover { background: #f1f3f4; }
.social-apple:hover  { background: #1a1a1a; }

/* 승패는 글자만이 아니라 화면 전체의 인상으로도 갈린다. */
.battle-result.won h2 { color: var(--gold); text-shadow: 0 0 24px rgba(232, 182, 76, .5); }
.battle-result.lost h2 { color: var(--text-dim); }
.battle-result:not(.hidden) { animation: result-in .28s ease-out; }
@keyframes result-in {
  0% { opacity: 0; }
  100% { opacity: 1; }
}
.battle-result h2 { animation: result-title .4s ease-out; }
@keyframes result-title {
  0% { opacity: 0; transform: scale(.7); }
  100% { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .battle-result:not(.hidden),
  .battle-result h2 { animation: none !important; }
}

/* ---------------------------------------------------------- 가로 고정

   플레이는 가로로 고정한다(2026-08-31 결정). 5레인이 나란히 서야 하는 판이라
   세로로는 카드가 너무 좁아진다. 앱은 네이티브 설정으로 가로에 못박혀 있고,
   여기는 브라우저에서 세로로 열었을 때도 깨지지 않게 두 경우를 모두 둔다.

   가로 휴대폰의 세로 높이는 390~450px 밖에 안 된다. 세로용 치수를 그대로 두면
   영웅 체력바가 화면 밖으로 밀려 나가 자기 체력을 볼 수 없게 된다.

   **여기에 있던 `.battle-foot { display: block; }` 을 지웠다.** 위에서 감춘 것을
   뒤에서 다시 켜고 있었다 - 뒤에 오는 규칙이 이기므로 로그가 110px 을 그대로
   차지했다. 컴파일도 테스트도 통과하고 **높이만 안 맞는** 종류다. */

@media (orientation: landscape) and (max-height: 560px) {
  /*
   * 마을.
   *
   * 가로 휴대폰의 세로 높이는 390~450px 밖에 안 된다. 세로용 치수를 그대로 두면
   * 배경(.town)이 화면의 절반을 먹고, 메뉴 14칸 중 4칸만 보인 채 나머지는
   * 스크롤 밖으로 밀린다. 처음 들어온 사람에게는 그 4칸이 게임의 전부로 보인다.
   *
   * 배경을 띠로 줄이고 메뉴를 7열로 편다. 배경을 아주 없애지 않는 이유는
   * 마을이라는 자리가 사라지면 화면이 그냥 버튼 목록이 되기 때문이다.
   */
  #view-home { padding: 6px 12px; }
  .town { height: 54px; margin-bottom: 8px; }

  /*
   * 마을. 원작처럼 배경이 화면을 다 쓰고 단추가 가장자리에 붙는다.
   * 가로 390px 에서는 원형 넷의 높이(4x62)가 그대로면 위아래가 잘리므로 줄인다.
   */
  .home-face { width: 34px; height: 34px; }
  .home-lv { left: 22px; font-size: 10px; line-height: 14px; }
  .home-who > b { font-size: 12px; }
  .round-btn { width: 54px; height: 54px; font-size: 10px; }
  .round-btn .ico { width: 17px; height: 17px; }
  .home-side { gap: 5px; right: 8px; }
  .home-bottom { gap: 5px; bottom: 6px; }
  .bar-btn { padding: 7px 3px; font-size: 12px; }
  .home-stamina { bottom: 48px; padding: 3px 7px; font-size: 12px; }
  .home-sheet { bottom: 44px; padding: 6px; gap: 5px; }
  .town-tree { width: 64px; height: 64px; bottom: -30px; }
  .town-moon { top: 8px; right: 48px; width: 18px; height: 18px; }
  .home-admin { top: 6px; right: 44px; padding: 3px 8px; font-size: 10px; }
  #view-home.admin-on .town-moon { right: 112px; }
  .corner-settings, .home-settings { top: 6px; right: 6px; width: 30px; height: 30px; font-size: 15px; }

  .occupy-banner { padding: 5px 10px; margin-bottom: 6px; font-size: 12px; }

  .menu-btn { padding: 8px 2px; gap: 3px; font-size: 11px; }
  .menu-btn .ico { width: 16px; height: 16px; }

  /* 목록 화면들도 위쪽 여백부터 줄인다. 판이 아니라 여백이 먼저다. */
  .view { padding: 8px 12px; }
  .topbar { margin-bottom: 8px; }

  /*
   * 폰에서는 화면을 통째로 쓴다 (2026-09-03 대표님: "위로 올리면 올라가 버린다 · 좌우 여백이 너무 많다").
   *
   * 밀리던 이유는 둘이었다. 마을 판(100vh-12) + 화면 여백(8+8) + #app 아래 40px 이 화면보다 길어
   * 페이지 자체가 스크롤됐고, 앱은 Capacitor 의 contentInset=always 가 안전영역만큼 좌우·아래를 비웠다.
   * 이제 페이지는 안 움직이고(#app 이 유일한 스크롤 상자), 좌우는 안전영역의 **절반**만 비운다 -
   * 다이내믹 아일랜드가 한쪽 가장자리 가운데에 걸리는데(가로 모드에서 약 37pt), 절반(약 30pt)에
   * 단추 여백 10px 을 더하면 그 밑으로 들어가지 않는다. 다 비우면(59pt) 예전 여백과 같아진다.
   * 웹 브라우저에서는 안전영역이 0 이라 아무 차이가 없다.
   */
  html, body { height: 100%; overflow: hidden; }
  /*
   * 2026-09-03 밤 대표님(아이폰 17 Pro 스크린샷): "하단이 잘려 · 왼쪽에 여백을 조금만 추가하자, 카메라가 글씨를 가리네".
   *   - 좌우는 안전영역의 절반(약 30pt)이 모자랐다 → **3/4**(약 44pt). 다 비우면(59pt) 예전처럼 여백이 남는다
   *   - 아래는 0 이라 목록의 마지막 줄이 홈 표시줄 밑으로 들어갔다 → 안전영역 + 14px. 스크롤 상자의 안쪽 여백이라
   *     스크롤 끝에서 그만큼 더 올라온다. `box-sizing: border-box` 가 없으면 여백이 상자를 화면 밖으로 키워 헛돈다
   *   - 마을·전투는 한 화면에 딱 맞춘 판이라 아래 여백을 두지 않는다(두면 그만큼 다시 스크롤이 생긴다)
   */
  #app {
    box-sizing: border-box;
    height: 100%;
    overflow-y: auto;
    overscroll-behavior: none;
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
    padding-left: calc(env(safe-area-inset-left, 0px) * 0.75);
    padding-right: calc(env(safe-area-inset-right, 0px) * 0.75);
  }
  #app:has(#view-home.active) { padding-bottom: 0; }
  /* 720px 상한은 데스크톱 브라우저를 위한 것이다. 가로 폰은 있는 폭을 다 쓴다. */
  .view { max-width: none; }

  /*
   * 마을은 화면 높이를 다 쓴다. 그러지 않으면 메뉴 아래가 통째로 비어,
   * 화면이 잘린 것처럼 보인다. 배경(.town)이 남는 높이를 가져간다 -
   * 늘어나야 할 것은 버튼이 아니라 배경이다.
   */
  #view-home.active { position: relative; min-height: calc(100vh - 16px); }
  /* 마을에서는 위쪽 띠가 없다. 배경이 화면을 통째로 쓴다. (16 = .view 의 위아래 여백 8+8 - 넘치면 밀린다) */
  .town { position: absolute; inset: 0; height: auto; margin: 0; }

  /*
   * 전투도 같은 이유로 세로 가운데에 모은다. 판을 늘리지 않고 가운데로만
   * 모으는 이유: 레인 높이를 건드리면 카드 연출(찌르기·흔들림)이 잡아 둔
   * 치수와 어긋난다.
   */
  #view-battle.active { display: flex; flex-direction: column; justify-content: center; min-height: calc(100vh - 8px); }

  /*
   * 전투 화면은 **한 판에 딱 맞춘 화면**이라 스크롤이 없어야 한다.
   * `#app` 의 아래 여백 40px 이 그대로 남아 있어, 판은 다 들어가는데도
   * 40px 만큼 스크롤이 생기고 있었다 - 손가락으로 만지는 기계에서는
   * 그 40px 이 판을 흔든다.
   */
  #app:has(#view-battle.active) { padding-bottom: 0; }

  .view-battle { max-width: 100%; padding: 4px 14px; }

  /* 위아래로 짜낸다. 판(카드)은 마지막까지 지키고 여백부터 줄인다. */
  .view-battle .battle-heroes { flex-basis: 96px; }
  .view-battle .hero-row { padding: 4px 5px; gap: 2px; }
  .view-battle .field { padding: 2px 0; }
  .view-battle .turn-marker { padding: 0; font-size: 11px; }
  .view-battle .hpbar { height: 15px; }
  .view-battle .hpbar span { font-size: 10px; line-height: 13px; }
  .view-battle .hero-rune { width: 14px; height: 14px; font-size: 8px; line-height: 12px; }

  /*
   * **카드는 세로다**(2026-09-02 대표님 결정). 원작 전투 화면이 그렇다.
   *
   * 로그 한 줄이 사라진 110px 을 판이 가져간다. 다만 높이만 키우면 칸이
   * 128x128 로 정사각이 되므로 **폭도 함께 좁힌다** - 5칸을 90px 로 고정하고
   * 가운데로 모은다. 90x128 은 약 3:4 로, 카드 그림 규격과 같은 비율이다.
   *
   * 폭을 `1fr` 로 두면 판이 넓어질 때 다시 가로로 눕는다. 여기서는
   * **고정 폭**이 맞다 - 판이 아니라 카드가 주인공이다.
   */
  /*
   * **왼쓴에서부터 세운다**(167장). 오른쪽 기둥을 없애고 단추를 준비구 줄 오른쪽 끝에 앉혔으므로
   * 판의 오른쪽이 비는데, 가운데로 모으면 준비구(단추가 있어 더 좁다)와 열이 어긋난다.
   * 둘 다 왼쪽 정렬이면 90px 열이 위아래로 정확히 겹친다 - "왼쪽부터 차례로 찬다"가 눈에 보인다.
   */
  .view-battle .lane-row {
    grid-template-columns: repeat(5, 90px);
    justify-content: start;
    min-height: 116px;
  }
  .view-battle .lane { min-height: 116px; }
  /*
   * 강화소 - **한 탭이 한 화면에 들어가게**(2026-09-03 대표님).
   *
   * 탭으로 카드와 룬을 갈라 놓아도 카드 쪽이 447px 이라 넘쳤다. 세로를
   * 먹는 것은 위의 판이다 - 단추 셋이 세로로 쌓여 132px 을 쓰고, 아무것도
   * 안 골랐을 때도 대상 칸이 카드 한 장 높이를 잡아 둔다.
   *
   * **판을 눌러 격자에 준다.** 강화소에서 오래 보는 것은 판이 아니라
   * 고를 카드 쪽이다.
   */
  #view-enhance .enh-tabs .btn { padding: 4px 14px; font-size: 12px; }
  /* 머리말 줄이 탭을 안고 있으므로 그 아래 여백만 있으면 된다. */
  #view-enhance .topbar { margin-bottom: 6px; }
  /*
   * **좌우로 가른다.** 왼쪽 기둥에 판, 오른쪽에 목록. 목록만 따로 구르므로
   * 판은 늘 제자리에 있고 페이지 전체는 구르지 않는다.
   *
   * 높이는 뷰포트에서 계산한다 - 위에 상태줄과 머리말 줄이 얹혀 있고 그 합이
   * 101px 이다(탭이 머리말 줄로 올라가 29px 이 빠졌다). 상수로 박으면 그중
   * 하나가 바뀌는 날 조용히 어긋난다.
   */
  /*
   * **기둥에 키를 준다.** 늘어나게 두면 `.enh-main` 의 높이가 담긴 내용만큼이
   * 되어, 줄 수를 재려고 물어본 "남는 높이" 가 **이미 그린 것의 높이**로 돌아온다 -
   * 한 줄만 그려 놓고 재면 한 줄만 들어간다고 답한다(그래서 22쪽이 됐다).
   * 재는 자가 자기가 그린 것을 재고 있던 것이다(145장과 같은 종류다).
   */
  #view-enhance .enh-split { display: flex; gap: 10px; height: calc(100vh - 101px); }
  /*
   * 잠겼을 때는 좌우로 가르지 않는다 - 목록이 통째로 감춰지므로(`lockPanel`)
   * 오른쪽이 빈 채로 남아 자물쇠만 왼쪽 구석에 쭈그러든다.
   */
  #view-enhance .enh-split:has(.enh-locked:not(.hidden)) { display: block; }
  #view-enhance .enh-side { flex: 0 0 340px; }
  #view-enhance .enh-main { flex: 1 1 0; }
  #view-enhance .enh-side,
  #view-enhance .enh-main { min-height: 0; overflow-y: auto; }

  /*
   * 판은 기둥의 **마지막이자 유일한 것**이라 아래 여백이 갈 곳이 없다 -
   * 그 12px 때문에 기둥이 딱 그만큼 굴렀다(258px 판 + 12px 여백 > 260px 기둥).
   */
  #view-enhance .enh-side > .panel { margin-bottom: 0; }
  #view-enhance .enhance-panel { gap: 8px; padding: 8px 10px; }
  /*
   * **대상 카드를 키운다**(2026-09-03 대표님: "카드를 크게 보여줘 일러스트도
   * 보이게"). 104 → 124px. 3:4 이므로 165px 이 되고 그만큼 그림이 보인다.
   * 그 값을 어디서 빼 오는가는 아래 - 단추와 이름표를 줄였다.
   */
  #view-enhance .enh-target { width: 116px; }
  /*
   * 카드 판의 이름표(「강화할 카드」)는 접는다. 아래에 카드가 서 있고
   * 목록에서 그 장에 「대상」이 붙어 있어 **같은 말이 세 번**이다 -
   * 그 13px 이 그림 쪽으로 간다. 룬 판은 그대로 둔다(거기는 자리가 남는다).
   */
  #enhance-panel .enh-target h3 { display: none; }
  /*
   * 재료 칸의 **기준 너비**를 줄인다(200 → 120). 290px 기둥에서는 200px 이
   * 대상 칸과 나란히 설 수 없어 아랫줄로 접혔고, 그만큼 판이 34px 자라
   * 안내 한 줄이 기둥 밖으로 밀렸다. 넓을 때는 어차피 늘어나 다 쓴다.
   */
  #view-enhance .enh-materials { flex: 1 1 120px; }
  /*
   * **이름표와 단추를 줄여 그림에 준다**(대표님: "버튼들의 크기와 배치를 좀 더
   * 줄이고"). 이름표는 10px 한 줄, 단추는 padding 4px·12px 로 눕힌다.
   */
  #view-enhance .enh-target h3 { margin: 0 0 2px; font-size: 10px; }
  #view-enhance .enh-target-info { margin-top: 2px; font-size: 10px; }
  #view-enhance .enh-cost { font-size: 11px; margin-bottom: 2px; }
  /* 단추 셋을 가로로 눕힌다 - 세로로 쌓인 132px 이 44px 이 된다. */
  #view-enhance .enh-actions {
    flex-direction: row;
    width: auto;
    flex: 1 1 100%;
    gap: 6px;
  }
  #view-enhance .enh-actions .btn { flex: 1 1 0; width: auto; padding: 4px 6px; font-size: 11px; }
  #view-enhance .enh-actions { gap: 4px; }
  /* 안내 한 줄은 거르개가 그 자리를 쓴다. 판의 안내가 같은 말을 한다. */
  #view-enhance .enh-main > .note { display: none; }
  #view-enhance .enh-filters { margin-bottom: 4px; }
  #view-enhance .enh-chip { padding: 2px 7px; font-size: 10px; }
  #view-enhance .enh-hint { margin: 2px 0 0; font-size: 10px; }
  #view-enhance .note { margin: 2px 0 4px; font-size: 10px; }

  /*
   * **목록은 구르지 않는다**(2026-09-03 대표님: "위아래 이동없이 한눈에 전체가
   * 보이게"). 한 쪽에 5 × 2 = 열 장을 담고 나머지는 쪽으로 넘긴다.
   *
   * 칸 수는 여기 한 번만 적는다 - 격자도 화면 코드도 `--enh-cols` 를 읽는다.
   */
  #view-enhance #enhance-list {
    --enh-paged: 1;
    --enh-cols: 4;
    --enh-rows: 2;
    grid-template-columns: repeat(var(--enh-cols), minmax(0, 1fr));
    gap: 6px;
  }
  #view-enhance #rune-enh-list { --enh-paged: 1; --enh-cols: 1; --enh-rows: 5; }

  /*
   * 가로가 좁은 기계(아이폰 SE 가로, 667px). 넉 칸이면 타일이 68px 이 되어
   * 이름표(「대상」·「재료」)가 이름을 덮는다 - 기둥을 줄이고 석 칸으로 놓는다.
   */
  @media (max-width: 760px) {
    #view-enhance .enh-side { flex-basis: 290px; }
    #view-enhance #enhance-list { --enh-cols: 3; }
    /*
     * 667×375 는 390 보다 세로가 15px 짧다 - 대상 카드가 그만큼 양보한다
     * (116 → 104px, 3:4 이므로 키는 155 → 139). 판은 쪽으로 나눌 수 없어
     * 넘치면 굴러야 하고, 여기서 구르면 단추가 화면 밖으로 나간다.
     */
    #view-enhance .enh-target { width: 104px; }
  }

  /*
   * **강화소의 타일은 낮게 쓴다.** 비율(3:4)대로 두면 넉 칸에 149px 이라
   * 두 줄이 안 들어간다. 키를 못 박아 그림을 조금 자른다 - 목록에서는
   * "어느 카드인가" 만 알면 되고, **크게 보는 자리는 왼쪽 판**이다.
   *
   * 진영 줄(문양 · 진영 · Lv)은 목록에서 접는다. 테두리 색과 문양이 이미
   * 진영을 말하고, 레벨은 고른 뒤 왼쪽 판이 크게 적는다.
   */
  #view-enhance #enhance-list .card {
    aspect-ratio: auto;
    height: 110px;
    padding: 4px 6px;
    font-size: 11px;
  }
  #view-enhance #enhance-list .card-kind { display: none; }
  #view-enhance .card-stars { font-size: 10px; letter-spacing: 0; }
  #view-enhance .card-cost { font-size: 9px; }
  #view-enhance .card-top,
  #view-enhance .card-bottom { gap: 4px; }

  /*
   * **대상은 크게 보여 준다**(2026-09-03 대표님: "카드를 크게 보여줘
   * 일러스트도 보이게"). 목록의 타일이 110px 인데 대상까지 그만하면
   * 무엇을 고르고 있는지가 눈에 안 들어온다 - 여기는 그림을 보는 자리다.
   */
  #view-enhance .enh-target .card { height: auto; aspect-ratio: 3 / 4; }

  #view-enhance .enh-pager { margin-top: 4px; }
  #view-enhance .enh-pager .btn { padding: 3px 10px; font-size: 12px; }

  /*
   * **상대 판과 내 판 사이 여백**(158-18, 대표님). 붙어 있으면 어느 줄이 누구 것인지 한눈에 안 갈린다.
   * 레인을 128 → 116 으로 12px 내주고 그중 10px 을 여기, 나머지는 준비구가 가져간다.
   */
  .view-battle #player-lanes { margin-top: 10px; }

  /*
   * 가로 390px 에서는 준비구 두 줄이 새로 들어온다. 판(전장)을 지키고
   * 준비구부터 눌러 둔다 - 준비구는 이름과 대기 턴만 읽히면 된다.
   */
  /* 준비구도 판과 같은 폭으로 세운다. 위아래가 어긋나면 어느 카드가 어디로
     나오는지 눈으로 못 따라간다. 높이는 이름과 대기 턴만 읽히면 된다. */
  .view-battle .prep-row {
    margin: 2px 0;
    gap: 6px; /* 레인과 같은 간격이어야 열이 겹친다 */
    grid-template-columns: repeat(5, 90px) 38px;
    justify-content: start;
  }
  /*
   * 가운데 턴 줄(34px)이 기둥으로 갔다. 그 높이를 **준비구가 가져간다** -
   * 28 → 42 면 이름과 대기 턴 위에 그림도 한 조각 보인다.
   */
  /* 준비구를 키운다(158-18, 대표님: "대기열 크기가 너무 작아"). 42 → 54. 글자도 10 → 11. */
  .view-battle .prep-slot { min-height: 54px; }
  /* 좌우 여백을 4px 로 줄이고 LV 칩 자리를 20px 로 - 이름 칸이 62px 이 되어 다섯 글자가 잘리지 않는다 */
  .view-battle .prep-slot .prep-card { padding: 5px 4px; font-size: 11px; }
  .view-battle .prep-name { padding-right: 20px; }
  .view-battle .grave-mark { font-size: 14px; }

  /*
   * 배치 단추는 로그 옆에 세로로 세운다.
   *
   * 예전에는 판 아래에 줄을 하나 더 만들었는데, 가로 390px 에서는 그 한 줄 때문에
   * 화면이 통째로 37px 밀려 단추가 잘렸다. **판이 남는 높이를 가져간다**는 규칙은
   * 잠깐 뜨는 창에도 똑같이 걸린다.
   */
  .view-battle .deploy-panel { margin-top: 0; padding: 0; flex: 0 0 auto; }

  .view-battle .lane { padding: 4px; font-size: 10px; }
  .view-battle .lane .unit { padding: 4px; font-size: 10px; }

  /* 아래쪽 배속·스킵은 낮은 화면에서 글자를 한 단계 줄인다. 가운데 큰 단추는 그대로 크다. */
  .view-battle .battle-rail .battle-controls-foot .btn { padding: 4px 8px; font-size: 11px; }

  /* 결과 화면도 낮은 높이에 맞춘다. */
  .battle-result { gap: 8px; padding: 12px; }
  .battle-result h2 { font-size: 26px; }
  .result-detail { font-size: 12px; line-height: 1.5; padding: 8px 14px; }

  /*
   * 지도.
   *
   * 원작 지도는 화면을 거의 다 쓴다. 여기서도 **관문 판이 남는 높이를 가져가고**
   * 제목·탭·챕터 머리는 한 줄씩으로 눌러 둔다 - 늘어나야 할 것은 지도지 여백이 아니다.
   * 그러지 않으면 아래 줄의 관문이 화면 밖으로 밀려, 챕터가 절반만 있는 것처럼 보인다.
   */
  #view-map.active { display: flex; flex-direction: column; min-height: calc(100vh - 61px); }
  #view-map .topbar { margin-bottom: 6px; }
  #view-map .chapter-tabs { padding-bottom: 6px; }
  #view-map .chapter-tab { min-width: 34px; padding: 4px 10px; font-size: 12px; }
  #view-map .chapter-head h3 { font-size: 15px; }

  .stage-map { flex: 1 1 auto; height: auto; min-height: 150px; }
  .map-foot { flex: none; margin-top: 6px; }
  .map-stamina { padding: 2px 7px; font-size: 12px; }

  /*
   * 미로.
   *
   * 세로용 치수를 그대로 두면 7x7 중 두 줄 반만 보이고 나머지는 스크롤 밖이다.
   * 미로는 **전체 모양을 보면서 길을 고르는** 화면이라, 판이 잘리면 화면의 목적이
   * 사라진다(마을 메뉴가 4칸만 보이던 것과 같은 종류의 사고다).
   *
   * 칸을 작게 만들고 판의 폭을 남는 높이에 맞춰 묶는다 - 칸이 정사각이므로
   * 폭을 묶으면 높이도 함께 잡힌다.
   */
  .maze-head { padding-bottom: 6px; gap: 10px; font-size: 12px; }
  .maze-head b { font-size: 14px; }
  /* 판을 가운데로 모은다. 왼쪽에 몰아 두면 넓은 화면에서 한쪽만 쓰는 그림이 된다. */
  .maze-grid { gap: 3px; margin: 0 auto 6px; max-width: 214px; }
  .maze-cell { border-radius: 4px; font-size: 12px; }
  #view-maze .note { font-size: 11px; margin: 0 0 5px; text-align: center; }
  #btn-maze-reset { display: block; margin: 0 auto; padding: 5px 14px; font-size: 12px; }
}

/* 전투 밖의 화면들은 목록이라 가로에서도 그대로 쓴다. 다만 폭이 넓어지면
   글줄이 너무 길어져 읽기 어려우므로 가운데로 모아 둔다(.view 의 max-width 가 한다). */

/* ---------------------------------------------------------- 우편 · 친구 · 순위 · 투기장
 *
 * 목록 형태가 서로 닮아 있어 공통 뼈대를 먼저 두고 화면별로 조금씩 다르게 한다.
 * 상태는 색만으로 알리지 않는다 - 받은 우편은 흐리게 + "완료" 문구를 함께 쓰고,
 * 오늘 마감된 상품은 흐리게 + "오늘 마감" 문구를 함께 쓴다.
 */

.section-title {
  margin: 18px 0 8px;
  font-size: 15px;
  color: var(--text-dim);
  font-weight: 600;
}

/* 마을 메뉴의 알림 숫자.
   빨간 점만 찍으면 색으로만 알리는 것이 되므로 숫자를 함께 적는다. */
.badge {
  position: absolute;
  top: 6px;
  right: 8px;
  min-width: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  line-height: 18px;
  text-align: center;
  font-weight: 700;
}
.menu-btn { position: relative; }

/* ---- 우편 ---- */

.mail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}

.mail-list { display: flex; flex-direction: column; gap: 8px; }

.mail-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 8px;
}
/* 받은 것은 흐리게. 문구("완료")가 함께 있으므로 명도만으로도 읽힌다. */
.mail-row.claimed { opacity: 0.55; }
.mail-main { min-width: 0; flex: 1; }
.mail-title { font-size: 14px; }
.mail-atts { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 6px; }
.mail-att {
  padding: 1px 8px;
  border-radius: 10px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-size: 12px;
}

.mail-cat {
  display: inline-block;
  margin-right: 6px;
  padding: 0 6px;
  border-radius: 4px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-size: 11px;
  color: var(--text-dim);
}
.mail-cat-ARENA  { color: var(--accent); }
.mail-cat-FRIEND { color: var(--ok); }
.mail-cat-REWARD { color: var(--gold); }

/* ---- 친구 ---- */

.friend-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.friend-list { display: flex; flex-direction: column; gap: 8px; }

.friend-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.friend-main { min-width: 0; flex: 1; }
.friend-actions { display: flex; gap: 6px; flex-shrink: 0; }

.friend-search { display: flex; gap: 8px; }
/*
  가입 규칙의 체크상자 한 줄. flex 항목은 기본이 stretch 라, 규칙을 안 주면
  옆 입력칸 높이만큼 늘어난 채 글자만 맨 위에 붙는다 - 체크상자가 입력칸·단추와
  어긋나 보인다.
*/
.guild-rule { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.friend-search input {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--bg-raised);
  color: var(--text);
  font-size: 14px;
}

/* ---- 순위표 · 투기장 ---- */

.rank-list { display: flex; flex-direction: column; gap: 4px; }

.rank-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 6px;
}
/* 내 줄은 테두리와 배경을 함께 바꾼다. 색 하나만 바꾸면 못 알아보는 사람이 생긴다. */
/* 순위표에서 그 자리로 자유대전을 건다. 이름과 점수 다음, 줄 끝에 둔다. */
.rank-row .btn-spar { flex: none; padding: 3px 10px; font-size: 11px; }

/*
 * 상대 후보 줄. 순위·이름·레벨·승률·전투 횟수가 한 줄에 들어간다.
 * 좁아지면 승률·전투 횟수가 먼저 줄어든다 - 이름과 단추는 끝까지 지킨다.
 */
/* 미로 완주 표시. 색만으로 알리지 않도록 글자로 적는다. */
.maze-done { margin-left: 6px; padding: 1px 7px; border-radius: 9px;
  background: var(--bg-panel); border: 1px solid var(--gold); color: var(--gold); font-size: 11px; }

/* 마신전 공헌점수·공헌카드·전리품. */
.demon-points { margin-top: 6px; font-size: 14px; font-weight: 700; color: var(--gold); }
.demon-bonus { margin-top: 3px; font-size: 12px; color: var(--text-dim); }
.demon-bonus b { color: var(--text); }

.loot-list { display: flex; flex-direction: column; gap: 5px; }
.loot-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  border-radius: 8px; border: 1px solid var(--line); background: var(--bg-panel); font-size: 12px; }
.loot-row .rank-name { flex: 1 1 auto; min-width: 0; }
/* 받은 줄. 흐리게만 두지 않고 "받음"이라고 글자로도 적는다. */
.loot-row.claimed { opacity: .5; }

.rank-row.mine {
  border-color: var(--accent);
  background: var(--bg-raised);
}
.rank-no {
  min-width: 32px;
  text-align: right;
  font-weight: 700;
  color: var(--text-dim);
}
.rank-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-score { flex-shrink: 0; font-variant-numeric: tabular-nums; }

/* ---- 상점 물건 ---- */

.shop-item-list { display: flex; flex-direction: column; gap: 8px; }

.shop-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 8px;
}
/* 오늘 마감은 흐리게 + 버튼 문구가 "오늘 마감"으로 바뀐다. */
.shop-item.sold-out { opacity: 0.5; }
.shop-item-main { min-width: 0; flex: 1; }

.shop-cat.active, .rank-tab.active { border-color: var(--accent); color: var(--text); }

/* ---- 군단 ---- */

.guild-list { display: flex; flex-direction: column; gap: 8px; }

.guild-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.guild-main { min-width: 0; flex: 1; }

/* 군단 공지는 유저가 쓴 글이다. 줄바꿈을 살리되 긴 글이 판을 밀어내지 않게 한다. */
.guild-notice {
  margin: 8px 0;
  padding: 8px 10px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 6px;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 8em;
  overflow-y: auto;
}

/* 직위는 색과 글자를 함께 쓴다. 색만으로는 서열이 읽히지 않는다. */
.guild-role {
  padding: 0 6px;
  border-radius: 4px;
  border: 1px solid var(--line);
  font-size: 11px;
  color: var(--text-dim);
}
.guild-role-LEADER { color: var(--gold); border-color: var(--gold); }
.guild-role-OFFICER { color: var(--accent); border-color: var(--accent); }

/* ---------------------------------------------------------- 전투는 전체 화면 */

/*
 * 전투 중에는 위쪽 HUD 와 언어 선택기를 감춘다.
 *
 * 가로에서 세로 높이가 390px 밖에 없는데 HUD 가 44px 를 가져가면 그만큼 판이
 * 눌린다. 전투 중에 재화를 볼 이유도 없다(전투 안에서는 쓸 수 없다).
 * 언어 선택기는 오른쪽 아래에 떠 있어 전투 버튼과 겹친다.
 *
 * body 에 표시를 다는 이유: .view 안에서는 바깥의 HUD 를 고를 수 없다.
 */
body.in-battle .hud,

/* 마을은 원작처럼 모서리에 초상과 재화를 둔다. 위쪽 띠까지 띄우면 두 번 나온다. */
body.in-town .hud { display: none; }


/* ---------------------------------------------------------- 덱 룬 칸
 *
 * 룬은 카드가 아니라 덱에 붙는다(원작 符文槽). 그래서 카드 그리드가 아니라
 * 덱 자체를 설명하는 줄에 놓인다.
 *
 * 잠긴 칸을 아예 지우지 않는 것은 무엇이 열릴지 보여야 레벨을 올릴 이유가
 * 생기기 때문이다. 흐림(opacity)만으로 알리지 않고 자물쇠와 글자를 함께 쓴다.
 */

.rune-slots {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 6px;
}

.rune-slot {
  flex: 1 1 120px;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 7px 10px;
  border-radius: 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  color: inherit;
  font: inherit;
  font-size: 12px;
  text-align: left;
}

button.rune-slot { cursor: pointer; }
.rune-slot.filled { border-color: var(--gold); }
.rune-slot.locked { opacity: .5; }
.rune-slot .rune-no { color: var(--text-dim); font-size: 11px; }
.rune-slot .rune-name { font-size: 13px; overflow-wrap: anywhere; }
.rune-slot .rune-name.empty { color: var(--text-dim); }

/* 고르기 목록의 줄. 카드 스킬 줄과 같은 모양을 쓰되 누를 수 있게만 바꾼다. */
button.rune-pick {
  display: block;
  width: 100%;
  color: inherit;
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

button.rune-pick[disabled] { cursor: default; }


/* ------------------------------------------------- 수동 전투 (직접 배치)
 *
 * 원작은 소환 시간과 순서를 유저가 정한다. 자동은 이미 깬 관문을 빨리 넘기는 편의일 뿐이라
 * 기본이 직접 배치다.
 *
 * 켜진 쪽을 색만으로 알리지 않는다. 지도 위의 문구가 함께 바뀌고,
 * 놓을 수 있는 자리는 테두리와 점선이 함께 달라진다.
 */




.deploy-panel { display: flex; align-items: center; }

/*
 * 준비구(准备区).
 *
 * 덱에서 뽑혀 나왔지만 아직 전장에 못 선 카드가 여기 선다. 칸 수는 전장과 같은 5고,
 * 여섯 번째 칸은 묘지다 - 원작 화면이 그 모양이다. 세로로 전장 레인과 맞아떨어져야
 * "왼쪽부터 차례로 찬다"가 눈에 보인다.
 */
.prep-row {
  display: grid;
  grid-template-columns: repeat(5, 1fr) 44px;
  gap: 6px;
  margin: 4px 0;
}

.prep-slot {
  position: relative;
  min-height: 44px;
  border-radius: 7px;
  border: 1px dashed var(--line);
}

.prep-slot .prep-card {
  position: absolute;
  inset: 0;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 4px 5px;
  font-size: 10px;
  /*
   * **카드 밖으로 새지 않게 가둔다.** 좁은 화면에서 체력 숫자가 테두리를 넘어
   * 옆 칸 위에 그려졌다. 잘려 보이는 편이 남의 칸을 덮는 것보다 낫다.
   */
  overflow: hidden;
  /*
   * flex 로 두지 말 것. 칸이 40px 밖에 안 되면 flex 가 줄을 눌러 버려
   * 이름칸이 6px 가 되고 글자가 위아래로 잘린다 - 실제로 그랬다.
   * 블록에 줄높이를 못박으면 칸 높이가 바뀌어도 글자는 그대로 읽힌다.
   */
  opacity: 0;
  transition: opacity .16s;
}

.prep-slot.filled { border-color: transparent; }
.prep-slot.filled .prep-card { opacity: 1; }
.prep-slot.filled.FIRE  .prep-card { border-color: var(--faction-fire); }
.prep-slot.filled.WATER .prep-card { border-color: var(--faction-water); }
.prep-slot.filled.WIND  .prep-card { border-color: var(--faction-wind); }
.prep-slot.filled.EARTH .prep-card { border-color: var(--faction-earth); }
.prep-slot.filled.IRON  .prep-card { border-color: var(--faction-iron); }

.prep-name {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 15px;
  /* 오른쪽 위 LV 칩 자리를 비워 둔다. 안 비우면 긴 이름이 그 밑으로 들어간다. */
  padding-right: 24px;
}

/*
 * 공격력과 체력.
 *
 * **왼쪽을 비워 둔다.** 대기 턴 배지가 왼쪽 아래에 절대 위치로 앉아 있어서,
 * 비우지 않으면 배지가 공격력 위에 겹친다 - 실제로 그랬다.
 * 칸이 44px 밖에 안 되어 배지가 이 줄 높이에 걸린다.
 */
.prep-stats {
  display: flex;
  justify-content: space-between;
  line-height: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  padding-left: 21px;
}

/*
 * 세로로 든 휴대전화처럼 **아주 좁을 때**.
 *
 * 게임은 가로 고정으로 만들었지만(844x390) 브라우저는 세로로도 열린다.
 * 390px 에서는 준비구 칸이 58px 뿐이라 대기 턴 배지 자리를 왼쪽에 비워 두면
 * 공격력과 체력이 들어갈 자리가 없다 - 숫자가 테두리를 넘어 나갔다.
 *
 * 그래서 이 폭에서는 배지를 **이름 줄 오른쪽**으로 올리고 아래 줄을 통째로
 * 수치에 준다. 원작의 "왼쪽 아래" 자리를 지키지 못하지만, 읽히지 않는 것보다는
 * 낫다 - 가로로 들면 원래 자리로 돌아간다.
 *
 * LV 은 이 폭에서만 감춘다. 전장에 서면 카드에 그대로 찍히므로 사라지는 값은 아니다.
 */
@media (max-width: 560px) {
  .view-battle .prep-stats { padding-left: 0; }
  .view-battle .prep-name { padding-right: 22px; }
  .view-battle .prep-lv { display: none; }
  .view-battle .prep-wait { top: 3px; right: 3px; left: auto; bottom: auto; }
}

/*
 * 남은 대기 턴.
 *
 * 원작에서는 카드 **왼쪽 아래**에 모래시계와 함께 붙는다. 10턴짜리 카드도 있다.
 * 이 숫자가 0이 되어야 그 카드를 낼 수 있다 - 준비구에서의 좌우 위치는
 * 뽑힌 순서일 뿐이고, 낼 수 있는지는 오직 이 숫자가 정한다.
 *
 * 0 이 되면 숫자를 지우고 테두리로 "낼 수 있다"를 알린다.
 */
.prep-wait {
  position: absolute;
  left: 3px;
  bottom: 2px;
  padding: 0 3px;
  border-radius: 7px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  font-size: 9px;
  line-height: 13px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* 이름은 오른쪽 위 여백이 필요 없어졌다. 대신 아래쪽 배지를 피한다. */
.prep-stats { padding-left: 26px; }

/*
 * 낼 수 있는 카드.
 *
 * 원작은 **금색 테두리**로 알린다. 대표님이 보내 주신 화면 그대로다.
 * 색만으로 알리지 않는다 - 배지의 숫자가 그때 "0" 이 되어 함께 말해 준다.
 */
.prep-slot.ready .prep-card { border-color: var(--gold); }
.prep-slot.ready .prep-wait { color: var(--gold); }

/*
 * 그중에서도 **지금 내가 고를 수 있는** 카드.
 *
 * 준비된 것(ready)과 고를 수 있는 것(pickable)은 다르다 - 상대 준비구에도 준비된
 * 카드가 서지만 그것은 내가 누를 수 없다. 눌러도 되는 것만 빛과 커서로 더 알린다.
 */
.prep-slot.pickable { cursor: pointer; }
.prep-slot.pickable .prep-card {
  border-width: 2px;
  border-color: var(--gold);
  box-shadow: 0 0 7px rgba(255, 200, 80, .55);
}
/* 이미 낸 카드. 흐리게만 두지 않고 누를 수도 없게 한다. */
/*
 * 낸 카드. **다시 누르면 거둬지므로 손 모양을 그대로 둔다**(대표님 결정) -
 * `cursor: default` 로 두면 "이제 못 누른다" 고 말하는 셈이라 거짓이 된다.
 * 흐리게 하는 것은 "이미 냈다"는 표시이지 잠갔다는 뜻이 아니다.
 */
.prep-slot.used { cursor: pointer; }
.prep-slot.used .prep-card { opacity: .45; }

/*
 * 묘지.
 *
 * 죽은 카드가 몇 장인지가 룬 조건("묘지에 황야 카드가 1장 이상일 때")과 직접 얽힌다.
 * 십자 표시만 두면 어느 언어로 봐도 같고, 장수는 숫자로 적는다.
 */
.grave-slot {
  position: relative;
  border-radius: 7px;
  border: 1px dashed var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  color: var(--text-dim);
}

.grave-slot.filled { border-style: solid; border-color: var(--line); background: var(--bg-panel); }
.grave-mark { font-size: 14px; line-height: 1; }
.grave-count { font-size: 10px; font-variant-numeric: tabular-nums; }



/*
 * 다음 카드가 놓일 자리.
 *
 * 원작은 레인을 고르게 하지 않는다 - 낸 순서대로 왼쪽부터 채워진다.
 * 그래서 이 표시는 "고를 수 있는 곳"이 아니라 "정해진 곳"이다.
 * 그래도 보여 주는 이유는, 레인 위치가 곧 전투 결과인 이 게임에서
 * 어디로 갈지 모른 채 카드를 내면 아무 판단도 할 수 없기 때문이다.
 */
.lane.next-lane {
  border-style: solid;
  border-color: var(--gold);
}


/* ------------------------------------------------- 토너먼트
 *
 * 원작 PVP 세 종 중 이것만 보유 카드를 쓰지 않는다. 그래서 카드 타일은 덱 화면과
 * 같은 뼈대를 쓰되(진영 색·치수가 두 벌이 되지 않게), 고른 것은 덱과 같은 방식으로 표시한다.
 */

.tour-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding-bottom: 8px;
  font-variant-numeric: tabular-nums;
}

.tour-head b { font-size: 18px; }
.tour-head .dim { font-size: 12px; }

.card.tour-card { cursor: pointer; }
/* 덱이 확정된 뒤에는 고를 수 없다. 흐림만이 아니라 커서도 함께 바뀐다. */
.card.tour-card[data-fixed] { cursor: default; }
.card.tour-card[data-fixed]:not(.selected) { opacity: .45; }


/* ------------------------------------------------- 마신전
 *
 * 원작에서 유일하게 시각이 정해진 콘텐츠다. 화면이 가장 먼저 답해야 하는 물음이
 * "지금 되나?"라서 열림 여부를 머리말 맨 앞에 굵게 둔다.
 */

.demon-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding-bottom: 8px;
  font-variant-numeric: tabular-nums;
}

.demon-head b { font-size: 17px; }
.demon-head .dim { font-size: 12px; }

.demon-boss {
  padding: 12px;
  border-radius: 10px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
}

.demon-name { font-size: 15px; margin-bottom: 8px; }

/*
  마신의 스킬 줄. 이름과 문구를 한 줄에 세로로 쌓는다 - 세로 390px 에서
  네 줄이 들어가야 하므로 글자를 작게 두고 줄 간격만 벌린다.
*/
.demon-skills { margin: 8px 0 4px; display: flex; flex-direction: column; gap: 4px; }
.demon-skill { font-size: 11px; line-height: 1.35; text-align: left; }
.demon-skill b { color: var(--gold); margin-right: 4px; }
/*
  `.demon-boss .dim` 이 이 아래에 있어 display:block 으로 덮는다 - 뒤에 있는
  규칙이 이기므로 **한 칸 더 좁혀** 순서와 무관하게 이기게 한다.
  (이 저장소가 두 번 겪은 자리다: 남의 클래스가 전제하는 배치까지 볼 것)
*/
.demon-boss .demon-skill .dim { display: inline; margin: 0; font-size: 11px; }
/* 남은 체력은 막대와 숫자를 함께 쓴다. 막대만 두면 얼마나 남았는지 못 읽는다. */
.demon-boss .hpbar { height: 20px; }
.demon-boss .dim { display: block; margin-top: 8px; font-size: 12px; }

/* ---------------------------------------------------------- 분해소 */

/*
 * 강화소의 탭 둘(카드 강화 · 룬 강화).
 *
 * **새 클래스를 만든다.** 모양이 같다고 `.shard-tab` 을 빌리면 분해소의
 * 클릭 핸들러까지 따라온다(33장). 재질은 아래 「게임 스킨」 층에서 다른
 * 탭들과 함께 준다 - 거기 목록에 이름만 더하면 된다.
 */
/*
 * 강화소의 탭 둘. **머리말 줄(`.topbar`)에 얹혀 있다** - 따로 한 줄을 쓰면
 * 29px 을 먹는데 세로 390px 에서 그것은 카드 한 줄이 더 들어가느냐 마느냐다.
 *
 * `margin-left: auto` 로 오른쪽 끝에 붙는다. 제목이 길어져도 탭이 밀리지 않고
 * 제목 쪽이 줄어든다 - 누르는 것이 읽는 것보다 급하다.
 */
.enh-tabs { display: flex; gap: 6px; margin-left: auto; }
/*
 * **알약 모양**(2026-09-03 대표님: "버튼을 그리고 동그랗게 만들어줘").
 * 높이의 절반보다 큰 반지름이면 양끝이 반원이 된다 - 999px 은 "끝까지" 라는 뜻이다.
 */
.enh-tabs .btn { min-width: 0; border-radius: 999px; padding: 5px 16px; }

.shard-tabs { display: flex; gap: 6px; padding-bottom: 8px; }
.shard-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }

/*
 * 조각 진행도.
 *
 * 원작은 카드 그림이 퍼즐로 덮여 있고 모은 만큼 맞춰진다. 우리는 일러스트가
 * 없으므로 네모 아홉 칸으로 대신한다. 다만 칸만 보면 "거의 다 됐다"까지만 알고
 * 몇 개가 남았는지는 알 수 없다 - 그래서 아래 숫자가 반드시 함께 있어야 한다.
 */
.shard-pieces {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  margin: 6px 0 4px;
}

.shard-pieces i {
  display: block;
  height: 9px;
  border-radius: 2px;
  background: var(--bg);
  border: 1px solid var(--line);
}

.shard-pieces i.on { background: var(--gold); border-color: var(--gold); }

.shard-qty { font-size: 12px; margin-bottom: 6px; }
/* 다 모은 줄은 숫자를 굵게 물들인다. 칸 색만으로는 다 찼는지 알기 어렵다. */
.shard-qty.ready b { color: var(--ok); }

.shard-tile { cursor: default; }
.shard-build { margin-top: 2px; }

.shard-gain { font-size: 13px; padding: 4px 0; border-top: 1px solid var(--line); }
.shard-plus { color: var(--ok); font-weight: 700; }

/* 룬 상세 - 원작 화면의 별 / 스킬 이름 / 경험치 막대 */

.rune-stars { color: var(--gold); font-size: 11px; letter-spacing: -1px; }
.rune-skill { color: var(--accent); font-size: 12px; }

/*
 * `0 / 120` 막대. 숫자를 막대 위에 겹치지 않고 앞에 둔다 - 겹치면 어느 쪽도
 * 읽히지 않는다(카드 타일에서 같은 실수를 한 적이 있다, HANDOVER 18장).
 */
.rune-exp { display: flex; align-items: center; gap: 6px; font-size: 11px; margin-top: 3px; }
.rune-exp > span:first-child { white-space: nowrap; }

/*
 * .costbar 를 그대로 쓰면 막대가 사라진다.
 *
 * 덱 COST 줄에서 쓰라고 만든 규칙이 `flex: 1 1 100%` 라, 이 한 줄짜리 flex 안에서는
 * 막대가 통째로 아랫줄로 밀리고 폭이 0 이 된다. 컴파일도 테스트도 통과하고
 * 화면만 조용히 비어 있다 - 클래스 이름을 다시 쓰다 겪는 그 사고다(CLAUDE.md).
 */
.rune-exp .costbar { display: block; flex: 1 1 auto; min-width: 70px; height: 4px;
  background: var(--line); }
/* span 이라 inline 이다. inline 에는 height 가 먹지 않아 막대가 아예 안 보인다. */
.rune-exp .costbar-fill { display: block; }

/*
 * 덱의 공격력·체력 합. 원작 덱 화면 왼쪽 기둥의 Attack / HP 다.
 * 가로 390px 이라 세 값을 한 줄에 늘어놓는다.
 */
.deck-sum {
  display: flex;
  gap: 14px;
  padding: 8px 12px;
  margin-bottom: 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  font-size: 13px;
}

.deck-sum small { color: var(--text-dim); font-size: 11px; margin-right: 5px; }

/*
 * 상생 한 줄. 상극과 짝을 이루는 우리 축(V93)이라 덱 요약 바로 아래에 붙는다.
 *
 * **글자에 색을 박지 않는다** - 짝 이름을 함께 적어 두었으므로 색이 없어도 읽힌다.
 * `content:` 로 무엇을 적지도 않는다(2-2-2 - CSS 에 박은 글자는 사전을 안 거친다).
 */
.deck-synergy {
  margin: -6px 0 10px;
  color: var(--accent);
  font-size: 12px;
}

/* 스킬 이름. 원작 카드 상세가 이름을 한 줄 띄우고 그 아래 설명을 적는다. */
.skill-title { display: block; color: var(--accent); font-size: 12px; margin-bottom: 2px; }

/* 덱 정렬·필터 탭. 원작의 순서 / 5성급 / 모든종족 */
.deck-filters { display: flex; gap: 6px; padding-bottom: 8px; }
.deck-filters .btn { flex: 1; }

/*
 * 잠긴 카드의 자물쇠. 원작 덱 화면도 카드 그림 위에 얹는다.
 * 모양(자물쇠)과 테두리를 함께 바꾼다 - 색만으로 알리지 않는다.
 */
.card.is-locked { border-style: dashed; }

.card-lock {
  position: absolute;
  top: 2px;
  right: 3px;
  font-size: 11px;
  line-height: 1;
  z-index: 1;
}

/*
 * 보유 상한 표시. 원작은 카드 **그림** 위에 주황 리본으로 "1장 제한"을 얹는다.
 *
 * 우리는 아직 일러스트가 없어 타일 맨 위가 곧 카드 이름이다. 그 위에 띄우면
 * 이름을 덮어 둘 다 못 읽는다 - 실제로 그렇게 만들었다가 눈으로 보고 되돌렸다.
 * 그림이 붙으면 그때 리본으로 옮길 것.
 *
 * 그래서 흐름 안의 한 줄로 둔다. 자리를 차지하지만 무엇도 가리지 않는다.
 */
.card-limit {
  display: block;
  /*
   * 글자 너비만큼만 차지한다. 한 줄을 통째로 먹으면 오른쪽 위의 자물쇠
   * (.card-lock, 절대 위치)를 덮어 잠긴 한정 카드에서 자물쇠가 사라진다.
   */
  width: fit-content;
  max-width: calc(100% - 16px);
  overflow: hidden;
  font-size: 9px;
  line-height: 1;
  padding: 2px 4px;
  margin-bottom: 2px;
  border-radius: 3px;
  background: var(--gold);
  color: #1a1206;
  /* 글자 그대로 적는다 - 색만으로는 무엇이 제한인지 알 수 없다. */
  white-space: nowrap;
}

/* 카드 상세의 한 줄. 리본과 달리 흐름 안에 있어 자리를 차지한다. */
.card-limit-line {
  font-size: 11px;
  color: var(--gold);
}

/* ---------------------------------------------------------- 덱 세트 (Switch Decks)
 *
 * 원작은 단추 하나 위에 "1 2 3" 을 얹는다. 우리는 탭 셋으로 편다 -
 * 세로 390px 밖에 없어 겹쳐 두면 누를 자리가 너무 작아진다.
 *
 * .chapter-tab 을 빌려 쓰지 않는다. 이름이 안 겹쳐도 **규칙을 물려받아**
 * 배치가 무너진 적이 있다(.costbar 로 룬 막대를 만들었을 때).
 */
.deck-sets {
  display: flex;
  gap: 6px;
  align-items: center;
  /* 머리말 줄의 오른쪽 끝으로 민다. 원작도 오른쪽 위 모서리다. */
  margin-left: auto;
}

.deck-set {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 34px;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg-panel);
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
}

.deck-set.picked {
  border-color: var(--accent);
  color: var(--text);
}

/* 잠긴 세트도 그린다 - 안 그리면 덱이 여러 벌이라는 것 자체를 모른다. */
.deck-set.locked {
  opacity: 0.5;
  cursor: not-allowed;
}

/* 지금 쓰는 덱인지, 잠겼는지를 **글자로** 알린다. 색만으로는 알 수 없다. */
.deck-set-mark {
  font-size: 9px;
  color: var(--ok);
}

.deck-set.locked .deck-set-mark { color: var(--text-dim); }

/* ---------------------------------------------------------- 채팅창
 *
 * 원작은 마을 화면에 탭 넷([전체][필드][귓속말][근단])짜리 채팅창을 띄우고
 * [전체] 탭에 [시스템] 공지를 섞는다. **[필드] 는 만들지 않았다** -
 * 그 탭이 무엇을 묶는지 모른다.
 *
 * 자리를 신중히 잡는다 - 마을은 좌상단(초상·재화) · 우측(원형 넷) ·
 * 하단(단추 넷) · 좌하단(기력)이 이미 차 있다. **가운데 위쪽**이 유일하게 빈 곳이다.
 * 세로 390px 이라 높이를 고정해 두지 않으면 하단 단추를 밀어낸다 -
 * 그래서 본문 높이를 66px 로 못 박고, 접을 수 있게 두었다.
 */
.home-chat {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: 8px;
  width: min(46%, 380px);
  border-radius: 6px;
  /* 마을 그림 위에 얹히므로 바탕을 깐다. 안 그러면 글자가 하늘에 묻힌다. */
  background: rgba(16, 22, 28, 0.78);
  border: 1px solid var(--line);
  font-size: 10px;
  line-height: 1.5;
  color: var(--text-dim);
  /* 마을 장식과 단추 사이. 단추를 덮으면 누를 수 없게 된다. */
  z-index: 2;
  overflow: hidden;
}

/* 탭 줄. 접었을 때도 이 줄만 남는다. */
.chat-tabs {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid var(--line);
}

/* 탭이 넷이다. 폭이 좁아 글자가 잘리지 않게 여백을 최소로 둔다. */
.chat-tab {
  flex: 1 1 0;
  min-width: 0;
  padding: 4px 1px;
  background: none;
  border: none;
  border-right: 1px solid var(--line);
  color: var(--text-dim);
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

/* 고른 탭. 색만으로 알리지 않으려고 글자도 굵게 한다. */
.chat-tab.active { color: var(--text); font-weight: 700; background: rgba(74, 143, 212, .18); }

.chat-fold {
  flex: 0 0 22px;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 10px;
  cursor: pointer;
}

.chat-body {
  height: 66px;
  overflow-y: auto;
  padding: 4px 8px;
}

.chat-send {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-top: 1px solid var(--line);
}

.chat-send input {
  flex: 1 1 0;
  min-width: 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--text);
  font-size: 10px;
  padding: 3px 5px;
}

/* 받을 사람 칸은 좁아도 된다. 본문 칸을 좁히면 무엇을 쓰는지 안 보인다. */
.chat-send .chat-to { flex: 0 0 68px; }

.chat-send .btn { padding: 3px 8px; font-size: 10px; }

/*
  접으면 탭 줄만 남는다. 세로 390px 에서 창이 늘 펴져 있으면 마을이 가려진다.
  `display: none` 이 아니라 높이를 0 으로 두는 이유는, 접은 채로도 탭을 눌러
  채널을 바꿀 수 있어야 하기 때문이다.
*/
.home-chat.folded .chat-body,
.home-chat.folded .chat-send { display: none; }

.chat-line {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* [시스템] 표시. 색만으로 알리지 않으려고 글자를 그대로 둔다. */
.chat-tag { color: var(--accent); }

/* 말한 사람. 시스템 줄의 파란 [시스템] 과 헷갈리지 않게 색을 달리한다. */
.chat-who { color: var(--text); }

/* 귓속말이 누구에게 갔는지. 없으면 내가 쓴 것과 받은 것이 구별되지 않는다. */
.chat-to-tag { color: var(--crystal); }

/* 빈 목록 문구. `.chat-line` 안에 넣으면 한 줄로 잘려 읽을 수 없다. */
.chat-empty { color: var(--text-dim); white-space: normal; padding: 2px 0; }

/* ---------------------------------------------------------- 판매소 */

/*
 * 총 가격. 원작 화면도 목록 위에 초록 글씨로 크게 띄운다 -
 * 이 숫자가 곧 받는 골드라 가장 먼저 눈에 들어와야 한다.
 */
.sell-total {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 12px;
  margin-bottom: 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
  font-size: 13px;
}

.sell-total b { color: var(--ok); font-size: 18px; }
.sell-total .dim { margin-left: auto; font-size: 12px; }

.sell-tabs { display: flex; gap: 6px; padding-bottom: 8px; }
.sell-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }

.sell-rune { width: 100%; text-align: left; cursor: pointer; }
.sell-rune.material { border-color: var(--gold); }

/* 뽑기 팩 목록. 원작 상점처럼 이름·설명·값·단추를 한 줄에 늘어놓는다. */
.pack-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }

.pack-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
}

.pack-info { flex: 1; min-width: 0; font-size: 13px; }
.pack-info .dim { font-size: 12px; margin-top: 2px; }
.pack-price { color: var(--gold); font-size: 12px; margin-top: 3px; }
.pack-actions { display: flex; flex-direction: column; gap: 5px; flex: none; }

/* ---------------------------------------------------------- 오우거 상점 */

.ogre-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 12px;
  margin-bottom: 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-panel);
}

.ogre-head b { color: var(--gold); font-size: 18px; }
.ogre-head .dim { margin-left: auto; font-size: 12px; }

.ogre-mats { display: flex; gap: 8px; flex-wrap: wrap; }

.ogre-mat {
  flex: 1 1 140px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg-panel);
  font-size: 12px;
}

/* 0개인 재료도 보여 준다. 무엇을 모아야 하는지 알아야 미로에 갈 이유가 생긴다. */
.ogre-mat.empty { opacity: .55; }
.ogre-mat .ogre-qty { display: block; font-size: 16px; color: var(--ok); margin: 2px 0; }

/* 달성한 줄은 테두리로도 구분한다. 상태 글자는 늘 함께 있다. */
.ogre-row.reached { border-color: var(--ok); }

/*
 * [받기] 단추를 오른쪽 끝으로 민다. 인라인으로 두면 "달성" 글자와 맞붙어
 * 둘 다 읽기 어려웠다 - 상태와 단추는 다른 것이므로 자리도 달라야 한다.
 */
.ogre-row { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.ogre-row .unlock { margin-left: auto; }
.ogre-row .btn { margin-left: 8px; flex: none; }

/* ------------------------------------------------------------------
   관문 상세의 난이도 셋.

   원작 관문 창은 난이도 한 줄에 [이름] [조건] [FINISH] 를 나란히 둔다.
   가로가 좁아 이름과 조건을 위아래로 쌓고, 표시는 오른쪽에 세운다.

   **글자를 CSS 에 박지 않는다.** FINISH 와 "추가 보상"은 화면이 사전에서
   만들어 넣는다 - content 로 박으면 어느 언어로 봐도 그대로 나온다.
   ------------------------------------------------------------------ */
.diff-list { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; }

/*
 * ---- 관문 창: 원작 배치 ----
 *
 * 원작은 **오른쪽에 단추를 세로로** 세우고 왼쪽에 난이도 세 줄을 둔다.
 * 세로 390px 에서 단추를 아래에 눕히면 세 줄이 밀려 나간다 - 카드 상세를
 * 두 칸으로 나눈 것(116장)과 같은 이유다.
 */
.modal-box.sm-box {
  display: flex;
  gap: 12px;
  max-width: 760px;
  max-height: 88vh;
}

.sm-main {
  flex: 1 1 auto;
  /* flex 자식의 기본값은 min-width: auto 다. 안 낮추면 긴 조건 문구가 칸을 밀어낸다. */
  min-width: 0;
  overflow-y: auto;
}

/* 단추 기둥. 원작의 전투 · 탐색 · 공략 · 뒤로 순서 그대로다. */
.sm-actions {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 92px;
}

.sm-actions .btn { width: 100%; }
/* 뒤로는 맨 아래에 떨어져 있다. 원작 화면도 그렇다. */
.sm-actions #sm-close { margin-top: auto; }

.sm-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.sm-head h3 { margin: 0; font-size: 18px; }
.sm-cost { flex: 0 0 auto; font-size: 12px; color: var(--text-dim); }

/* 딴 별. 원작은 제목 아래 가운데다. */
.sm-stars {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 2px 0 10px;
}

.sm-star-row { color: var(--gold); letter-spacing: 2px; font-size: 15px; }
.sm-star-count { font-size: 11px; color: var(--text-dim); }

/* 아래 줄: 남은 기력 · 모드 안내 · 상점/카드. */
.sm-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.sm-stamina { flex: 0 0 auto; font-size: 12px; color: var(--gold); }
.sm-foot .dim { flex: 1 1 auto; min-width: 0; font-size: 11px; }

.diff-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  text-align: left;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* 고른 줄은 테두리와 배경이 함께 달라진다. 색만으로 알리지 않으려고
   왼쪽에 굵은 띠를 둔다 - 색을 못 가려도 어느 줄인지 보인다. */
.diff-row.active {
  border-color: var(--gold);
  background: rgba(232, 182, 76, .12);
  box-shadow: inset 3px 0 0 var(--gold);
}

/*
 * 난이도 이름은 **동그란 배지**다. 원작 관문 화면이 줄 왼쪽에 원을 두고
 * 그 안에 쉬움 · 보통 · 정예를 적는다(117장).
 *
 * 원작은 원 안에 그림(별 · 달 · 해)이 함께 있는데 우리는 아직 그림이 없다.
 * **글자만 둔다** - 없는 그림 자리에 무언가를 얹으면 이름을 덮는다(116장 카드 타일).
 */
.diff-name {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  border: 2px solid var(--gold);
  background: rgba(58, 124, 190, .30);
}

/* 정예만 붉다. 원작도 이 줄만 색이 다르다. */
.diff-row[data-code="HARD"] .diff-name { background: rgba(190, 76, 58, .34); }

/* 배지가 원을 유지해야 하므로 조건 문구는 남는 자리를 다 가져간다. */
.diff-cond { flex: 1 1 auto; min-width: 0; font-size: 12px; color: var(--text-dim); }

.diff-done {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  color: var(--bg);
  background: var(--gold);
}

.diff-extra {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  border: 1px solid var(--gold);
  color: var(--gold);
}

/*
 * 잠긴 난이도. 원작 화면도 아직 못 여는 줄을 회색으로 둔다(117장).
 *
 * **색만으로 알리지 않는다** - 자물쇠와 "앞 난이도 먼저" 글자가 함께 붙는다.
 * 흐리게만 해 두면 색을 못 가리는 사람에게는 그냥 조금 연한 줄이다.
 */
.diff-row.locked {
  opacity: .55;
  cursor: not-allowed;
}

.diff-locked {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  border: 1px solid var(--line);
  color: var(--text-dim);
  white-space: nowrap;
}

.modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 4px;
}

.modal-foot .dim { flex: 1 1 auto; min-width: 0; font-size: 12px; }

/* ------------------------------------------------- 로그인 화면의 약관 링크 */

/*
 * 개인정보처리방침 · 이용약관 링크.
 *
 * **화면 바닥에 붙여 둔다.** 흐름 안에 그냥 두었더니 844x390 에서 윗끝이 570px 이라
 * 스크롤해야 나왔다 - 소셜 단추가 화면 밖으로 밀려 있던 것과 **같은 사고**다
 * (마을 메뉴 · 지도 아랫줄 · 미로 두 줄 반에 이어 다섯 번째다).
 *
 * 로그인 화면에만 걸리게 `#view-login` 안으로 가둔다. document 전체를 상대로 하는
 * 이름은 언젠가 다른 화면과 겹친다.
 *
 * 문서 자체(privacy.html · terms.html)는 이 파일을 쓰지 않는다 - `css/doc.css` 다.
 * 게임 화면은 가로 844x390 을 전제로 짜여 있어 세로로 긴 글을 얹으면 판이 무너진다.
 */
#view-login { padding-bottom: 30px; }

#view-login .legal-links {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  margin: 0;
  padding: 6px 12px 7px;
  text-align: center;
  font-size: 11px;
  color: var(--text-dim);
  /* 뒤로 내용이 지나가도 글자가 겹쳐 보이지 않게 바닥을 깐다. */
  background: linear-gradient(180deg, rgba(16, 22, 28, 0), var(--bg) 55%);
}

#view-login .legal-links a {
  color: var(--text-dim);
  text-decoration: underline;
}

/*
 * 로그인 화면이 **낮은 화면**에서 넘칠 때.
 *
 * 소셜 수단이 셋이 되자(애플·구글·카카오) 844x390 에서 카카오 단추 아래끝이 367px 이라
 * 바닥의 약관 줄(361px)에 **6px 겹쳤다.** 넷이 되면 더 나빠진다.
 *
 * 머리말이 아니라 **판이 남는 높이를 가져가게** 한다 - 제목은 줄여도 읽히지만
 * 단추는 가려지면 못 누른다. 세로가 넉넉한 화면에서는 그대로 크게 둔다.
 */
@media (max-height: 480px) {
  #view-login .title-block { padding: 14px 0 10px; }
  #view-login .title-block h1 { font-size: 28px; letter-spacing: 3px; }
}

/* ============================================================
   설정 화면

   새 이름만 쓴다. `.card`·`.note`·`.tab` 같은 이미 있는 이름을 빌려 오면
   **그 클래스가 전제하는 배치까지 물려받는다** - `.costbar` 를 룬 막대에
   빌렸다가 `flex: 1 1 100%` 때문에 막대가 아랫줄로 밀려 사라진 적이 있다.
   ============================================================ */

/*
   세로가 390px 뿐이라 여백이 곧 잘려 나가는 줄이 된다.
   마을 메뉴 14칸 중 4칸이 화면 밖으로 밀렸던 것과 같은 자리다(HANDOVER 18장).
   여기서는 padding 을 조여 설정 화면 전체가 한 화면에 들어오게 했다.
*/
.set-block {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 9px 12px;
  margin: 0 0 8px;
}

.set-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 2px 0;
  font-size: 13px;
  color: var(--text-dim);
}

.set-row b { color: var(--text); font-size: 14px; }

/* 약관·방침을 한 줄에 나란히. 세로를 44px 아낀다. */
.set-links { display: flex; gap: 12px; }

.set-link {
  flex: 1 1 0;
  padding: 3px 0;
  color: var(--accent);
  font-size: 13px;
  text-decoration: none;
}

/* 둘 사이를 가르는 선. 세로선이라 줄을 하나도 쓰지 않는다. */
.set-link + .set-link { border-left: 1px solid var(--line); padding-left: 12px; }

/* 접어 둔 탈퇴 판을 여는 작은 글씨. 눈에 띄지 않게, 그러나 있는 줄은 알게. */
.set-footnote { text-align: center; margin: 14px 0 4px; }
.link-dim {
  background: none;
  border: 0;
  padding: 4px 8px;
  color: var(--text-dim);
  opacity: .55;
  font-size: 11px;
  text-decoration: underline;
  cursor: pointer;
}
.link-dim:hover, .link-dim:focus { opacity: .9; }

/* 탈퇴 자리. 색만으로 알리지 않는다 - 제목과 문구가 무슨 일이 나는지 말한다. */
.set-danger { border-color: var(--danger); }

.set-danger h3 {
  margin: 0 0 4px;
  font-size: 14px;
  color: var(--danger);
}

.note.warn { color: var(--danger); }

.set-danger input[type="text"] {
  width: 100%;
  box-sizing: border-box;
  margin: 6px 0 8px;
  padding: 8px 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
}

.set-actions {
  display: flex;
  gap: 8px;
}

.set-actions .btn { flex: 1 1 0; }

.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
  font-weight: 700;
}

/* ============================================================
   천공의 탑

   원작 화면은 왼쪽에 층이 세로로 쌓인 미니맵을 두고 가운데에 계단을 그린다.
   세로 390px 에서 그것까지 두면 순위표가 화면 밖으로 밀리므로,
   **지금 층과 그 위아래 몇 칸**만 세워 두었다.

   새 이름만 쓴다 - `.rank-row` 같은 이미 있는 이름을 빌리면 그 클래스가
   전제하는 배치까지 물려받는다.
   ============================================================ */

.tower-top {
  display: flex;
  gap: 12px;
  align-items: stretch;
  margin-bottom: 10px;
}

.tower-stairs {
  flex: 0 0 56px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.tower-step {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 5px;
  font-size: 11px;
  color: var(--text-dim);
}

/* 이미 지나온 층. 색만이 아니라 채워진 바탕으로도 다르게 보인다. */
.tower-step.done { background: var(--bg-raised); color: var(--text); }

/* 지금 층. 테두리를 굵게 해 색을 못 보는 사람도 구별할 수 있게 한다. */
.tower-step.here {
  border-color: var(--gold);
  border-width: 2px;
  color: var(--gold);
  font-weight: 700;
}

.tower-now {
  flex: 1 1 0;
  min-width: 0;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
}

.tower-floor { font-size: 15px; margin-bottom: 4px; }
.tower-floor b { color: var(--gold); font-size: 20px; }

.tower-line {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-dim);
  padding: 1px 0;
}

.tower-line b { color: var(--text); }

.tower-actions { display: flex; gap: 6px; margin-top: 8px; }
.tower-actions .btn { flex: 1 1 0; font-size: 13px; padding: 7px 6px; }

.tower-tabs { display: flex; gap: 6px; margin-bottom: 8px; }
.tower-tabs .btn { flex: 1 1 0; }
.tower-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.tower-rank {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  max-height: 190px;
  overflow-y: auto;
}

.tower-rank-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 10px;
  font-size: 12px;
  border-bottom: 1px solid var(--line);
}

.tower-rank-row:last-child { border-bottom: none; }

.tower-rank-no {
  flex: 0 0 26px;
  color: var(--gold);
  font-weight: 700;
}

/* 오른 층은 오른쪽 끝에. 이름이 길어도 이 값이 밀려나지 않는다. */
.tower-rank-floor { margin-left: auto; color: var(--text); white-space: nowrap; }

/* 빈 목록 문구. 줄 칸 안에 넣으면 좁아서 두세 글자씩 흐른다. */
.tower-empty { padding: 14px 12px; font-size: 12px; color: var(--text-dim); }

/* ============================================================
   도전 - 원소의 탑 · 무한의 마궁 · 사악한 물결.

   세로가 390px 뿐이라 **모드 셋을 나란히 펼치지 않고 탭으로 겹친다.**
   셋을 다 펼치면 순위표가 화면 밖으로 밀린다 - 마을 메뉴·지도 아랫줄과
   같은 종류의 사고다.

   새 이름만 쓴다. `.tower-*` 를 빌리면 그 클래스가 전제하는 배치까지
   물려받는다(HANDOVER 의 `.costbar` 사고).
   ============================================================ */

.chal-tabs { display: flex; gap: 6px; margin-bottom: 8px; }
.chal-tabs .btn { flex: 1 1 0; min-width: 0; }
.chal-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.chal-body {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.chal-now {
  flex: 1 1 0;
  min-width: 0;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
}

.chal-now h3 { margin: 0 0 4px; font-size: 15px; }
.chal-now .note { margin: 0 0 6px; font-size: 11px; line-height: 1.4; }

.chal-line {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-dim);
  padding: 1px 0;
}

.chal-line b { color: var(--text); }

.chal-actions { display: flex; gap: 6px; margin-top: 8px; }
.chal-actions .btn { flex: 1 1 0; font-size: 13px; padding: 7px 6px; }

.chal-rank {
  flex: 1 1 0;
  min-width: 0;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  max-height: 250px;
  overflow-y: auto;
}

.chal-rank-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 10px;
  font-size: 12px;
  border-bottom: 1px solid var(--line);
}

.chal-rank-row:last-child { border-bottom: none; }

.chal-rank-no {
  flex: 0 0 26px;
  color: var(--gold);
  font-weight: 700;
}

/* 깬 판 수는 오른쪽 끝에. 이름이 길어도 이 값이 밀려나지 않는다. */
.chal-rank-wave { margin-left: auto; color: var(--text); white-space: nowrap; }

/* 빈 목록 문구. 줄 칸 안에 넣으면 좁아서 두세 글자씩 흐른다. */
.chal-empty { padding: 14px 12px; font-size: 12px; color: var(--text-dim); }

/* ============================================================
   세로로 들었을 때 덮는 판.

   **평소에는 없는 것과 같다.** 아래 미디어 질의가 참일 때만 보인다 -
   가로에서는 display:none 이라 자리도 차지하지 않는다.

   z-index 는 지금 쓰는 가장 큰 값(40, 카드 상세)보다 위여야 한다.
   상세 창이 열린 채로 기계를 돌리면 그 위에 떠야 하기 때문이다.
   ============================================================ */

#rotate-gate { display: none; }

/*
 * `pointer: coarse` = 손가락으로 만지는 기계. 데스크톱에서 창을 좁고 길게
 * 늘였다고 게임을 막지 않는다 - 거기서는 그냥 좁은 가로 화면일 뿐이다.
 */
@media (orientation: portrait) and (pointer: coarse) {
  #rotate-gate {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 100;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: var(--bg);
    text-align: center;
  }

  .rotate-box { max-width: 320px; }
  .rotate-box b { display: block; margin-bottom: 8px; font-size: 17px; color: var(--gold); }
  .rotate-box p { margin: 0; font-size: 13px; line-height: 1.6; color: var(--text-dim); }

  /*
   * 돌리라는 뜻을 그림으로도 한 번 더 말한다. 글자만 있으면 무엇을 하라는
   * 것인지 한 박자 늦게 읽힌다 - 세로로 선 네모가 눕는 모습이다.
   */
  .rotate-phone {
    width: 44px;
    height: 72px;
    margin: 0 auto 18px;
    border: 2px solid var(--gold);
    border-radius: 7px;
    animation: rotate-hint 2.4s ease-in-out infinite;
  }

  @keyframes rotate-hint {
    0%, 40% { transform: rotate(0deg); }
    60%, 100% { transform: rotate(-90deg); }
  }

  /* 움직임을 원하지 않는 사람에게는 눕힌 채로 세워 둔다. 정보는 그대로다. */
  @media (prefers-reduced-motion: reduce) {
    .rotate-phone { animation: none; transform: rotate(-90deg); }
  }
}

/* 오우거 상점 머리의 이벤트 이름. 포인트와 기간 사이에 선다. */
.ogre-event { font-size: 13px; font-weight: 700; color: var(--gold); }


/* ------------------------------------------------------------ 스킬 연출 */

/*
 * 스킬마다 다른 것이 터진다.
 *
 * 여태 화염구도 얼음탄도 독액도 **붉은 숫자 하나**로 끝났다 - 무엇이
 * 일어났는지 판만 보고는 알 수 없었다. 서버가 액션에 효과 종류를 실어 주므로
 * (`detailArgs.effect`) 그것으로 연출을 고른다.
 *
 * **종류마다 하나씩 만들지 않는다.** 효과가 쉰일곱인데 연출도 쉰일곱이면
 * 만들지도 못하고 서로 구분되지도 않는다. **집안**으로 묶는다 -
 * 불 · 얼음 · 번개 · 독 · 어둠 · 빛(회복) · 강화 · 약화 · 막기.
 *
 * 색만으로 알리지 않는다(CLAUDE.md 4장). 집안마다 **모양**도 다르다 -
 * 불은 피어오르고, 얼음은 각지게 맺히고, 번개는 번쩍이고, 독은 방울이 오른다.
 */
.spellfx {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  z-index: 3;
  opacity: 0;
}

/* 시전 - 스킬을 쓰는 카드가 먼저 빛난다. 어디서 날아오는지 눈이 따라간다. */
@keyframes cast-glow {
  0%   { opacity: 0; transform: scale(.9); }
  35%  { opacity: 1; transform: scale(1.04); }
  100% { opacity: 0; transform: scale(1.1); }
}

.unit.casting::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: 0 0 10px 2px var(--cast-color, #cfe4ff) inset;
  animation: cast-glow calc(280ms * var(--fx)) ease-out;
}

/* 불 - 아래에서 피어오른다. */
@keyframes fx-fire {
  0%   { opacity: 0; background-position: 50% 120%; }
  30%  { opacity: 1; }
  100% { opacity: 0; background-position: 50% -20%; }
}
.spellfx.fire {
  background: radial-gradient(ellipse at 50% 100%,
      rgba(255, 176, 68, .95) 0%, rgba(226, 88, 34, .6) 40%, transparent 72%);
  animation: fx-fire calc(340ms * var(--fx)) ease-out;
}

/* 얼음 - 가운데에서 각지게 맺힌다. */
@keyframes fx-ice {
  0%   { opacity: 0; transform: scale(.6) rotate(-8deg); }
  40%  { opacity: 1; transform: scale(1) rotate(0deg); }
  100% { opacity: 0; transform: scale(1.06) rotate(4deg); }
}
.spellfx.ice {
  background:
    linear-gradient(135deg, transparent 44%, rgba(180, 232, 255, .95) 50%, transparent 56%),
    linear-gradient(45deg, transparent 44%, rgba(180, 232, 255, .8) 50%, transparent 56%);
  animation: fx-ice calc(340ms * var(--fx)) ease-out;
}

/* 번개 - 두 번 번쩍인다. */
@keyframes fx-bolt {
  0%, 100% { opacity: 0; }
  15%, 45% { opacity: 1; }
  30%      { opacity: .2; }
}
.spellfx.bolt {
  background: linear-gradient(100deg, transparent 40%, rgba(196, 224, 255, 1) 50%, transparent 60%);
  animation: fx-bolt calc(300ms * var(--fx)) steps(1, end);
}

/* 독 - 방울이 위로 오른다. */
@keyframes fx-venom {
  0%   { opacity: 0; background-position: 50% 100%; }
  35%  { opacity: .95; }
  100% { opacity: 0; background-position: 50% 0%; }
}
.spellfx.venom {
  background: radial-gradient(circle at 50% 90%,
      rgba(140, 226, 120, .9) 0%, rgba(70, 150, 70, .5) 45%, transparent 70%);
  animation: fx-venom calc(380ms * var(--fx)) ease-out;
}

/* 어둠 - 가장자리에서 조여 든다. */
@keyframes fx-dark {
  0%   { opacity: 0; transform: scale(1.25); }
  40%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.86); }
}
.spellfx.dark {
  background: radial-gradient(circle at 50% 50%,
      transparent 30%, rgba(120, 60, 170, .85) 72%, rgba(30, 10, 50, .95) 100%);
  animation: fx-dark calc(360ms * var(--fx)) ease-in;
}

/* 빛(회복·가호) - 위에서 내려앉는다. */
@keyframes fx-light {
  0%   { opacity: 0; background-position: 50% -30%; }
  40%  { opacity: 1; }
  100% { opacity: 0; background-position: 50% 60%; }
}
.spellfx.light {
  background: radial-gradient(ellipse at 50% 0%,
      rgba(255, 244, 190, .95) 0%, rgba(120, 220, 160, .5) 45%, transparent 74%);
  animation: fx-light calc(360ms * var(--fx)) ease-out;
}

/* 강화 - 아래에서 테두리가 차오른다. */
@keyframes fx-boost {
  0%   { opacity: 0; transform: translateY(6px); }
  40%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-6px); }
}
.spellfx.boost {
  background: linear-gradient(0deg, rgba(255, 196, 84, .75) 0%, transparent 60%);
  box-shadow: 0 0 0 2px rgba(255, 196, 84, .55) inset;
  animation: fx-boost calc(340ms * var(--fx)) ease-out;
}

/* 약화 - 위에서 내려 눌린다. 강화와 방향이 반대다. */
@keyframes fx-wither {
  0%   { opacity: 0; transform: translateY(-6px); }
  40%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(6px); }
}
.spellfx.wither {
  background: linear-gradient(180deg, rgba(150, 130, 190, .7) 0%, transparent 60%);
  box-shadow: 0 0 0 2px rgba(150, 130, 190, .5) inset;
  animation: fx-wither calc(340ms * var(--fx)) ease-out;
}

/* 막기 - 테두리만 단단히 한 번. */
@keyframes fx-ward {
  0%   { opacity: 0; transform: scale(.94); }
  45%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.03); }
}
.spellfx.ward {
  box-shadow: 0 0 0 3px rgba(150, 200, 255, .85) inset;
  animation: fx-ward calc(320ms * var(--fx)) ease-out;
}

/*
 * 연출을 끄고 싶은 사람도 있다. 움직임을 줄이라고 한 기기에서는
 * **정보는 그대로 두고 움직임만** 없앤다 - 숫자와 표시는 계속 뜬다.
 */
@media (prefers-reduced-motion: reduce) {
  .spellfx, .unit.casting::after { animation: none; opacity: 0; }
}

/* 각인소(V105) - 담은 칸 셋과 새기기 단추 */
.engrave-panel { margin-bottom: 12px; }
.engrave-slots { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }
.engrave-slot { min-width: 64px; padding: 4px 8px; border: 1px dashed var(--line); border-radius: 6px; text-align: center; color: var(--text-dim); }
.engrave-slot.filled { border-style: solid; color: var(--text); background: var(--bg-raised); }
.engrave-actions { display: flex; gap: 8px; margin-top: 6px; }
/* 정수 다섯을 한 줄에 - 세로로 늘어놓으면 390px 을 넘는다(화면을 고쳤으면 눈으로 본다). */
#essence-list { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
#essence-list .essence-row { flex-direction: column; align-items: stretch; gap: 6px; padding: 8px 10px; }
#essence-list .essence-row .btn { width: 100%; }

/* 갈림길 회랑(V106) - 층마다 셋 가운데 하나 */
.maze-nodes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 8px 0; }
.maze-node { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--bg-panel); color: var(--text); font-family: inherit; text-align: left; cursor: pointer; min-height: 84px; }
.maze-node .node-name { font-weight: 700; font-size: 14px; }
.maze-node .node-desc { font-size: 12px; color: var(--text-dim); }
.maze-node .node-picked { font-size: 11px; color: var(--accent); }
.maze-node.picked { border-color: var(--accent); }
.maze-node.closed { opacity: .4; cursor: default; }
.maze-node.node-battle, .maze-node.node-gatekeeper { border-left: 4px solid var(--faction-fire, #e0703a); }
.maze-node.node-shrine { border-left: 4px solid var(--faction-water, #4aa3df); }
.maze-node.node-chest { border-left: 4px solid var(--gold, #d9a441); }
.maze-node.node-event { border-left: 4px solid var(--faction-wind, #7ec38a); }
.maze-node.node-rest { border-left: 4px solid var(--faction-earth, #a9865a); }

/* ★5 합성(V110) - 고를 수 있는 ★4 룬 칩 */
.fusion-panel { margin-bottom: 12px; }
.fusion-inputs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.rune-chip { cursor: pointer; font-family: inherit; font-size: 12px; }
.rune-chip:disabled { opacity: .4; cursor: default; }

/* ---------------------------------------------------------------- 서버 점검 오버레이 (js/maintenance.js · 158-36)
   판 전체를 덮는다. 회전 안내(#rotate-gate, z 100)보다 위다 - 점검 중에는 돌려도 할 것이 없다. */
#maint-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: radial-gradient(ellipse at 50% 120%, #1b2b3a 0%, #0f1720 55%, #0b1118 100%);
  color: var(--text);
  text-align: center;
  animation: maintIn .4s ease-out;
}
@keyframes maintIn { from { opacity: 0; } to { opacity: 1; } }
.maint-card { max-width: 360px; word-break: keep-all; line-height: 1.7; }
.maint-icon { font-size: 44px; line-height: 1; display: inline-block; animation: maintSpin 6s linear infinite; }
@keyframes maintSpin { to { transform: rotate(360deg); } }
.maint-card h2 { margin: 12px 0 6px; font-size: 20px; color: var(--gold); }
.maint-card p { font-size: 14px; color: var(--text-dim); }
.maint-dots { margin: 14px 0; }
.maint-dots span {
  display: inline-block; width: 8px; height: 8px; margin: 0 4px; border-radius: 50%;
  background: var(--faction-water); animation: maintDot 1.2s ease-in-out infinite;
}
.maint-dots span:nth-child(2) { animation-delay: .2s; }
.maint-dots span:nth-child(3) { animation-delay: .4s; }
@keyframes maintDot { 0%, 100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.1); } }
.maint-retry { min-width: 120px; }
@media (prefers-reduced-motion: reduce) {
  .maint-icon, .maint-dots span { animation: none; }
}

/* ================================================================ 게임 스킨 (2026-09-03, 158-43)
   대표님: "UI 들도 전부 진짜 게임처럼 구현하고 스킬 시 이펙트도 조금 더 화려하게."
   **크기·배치는 하나도 안 바꾼다** - 색·그라데이션·테두리·그림자·광택만 얹는다. 위의 규칙들을 덮어쓰는 층이므로 맨 아래에 둔다.
   색만으로 알리지 않는다는 원칙(CLAUDE.md 4)은 그대로 - 글자·수치가 다 남아 있다. */

body {
  background: radial-gradient(ellipse at 50% -20%, #1b2a38 0%, var(--bg) 55%, #0a0f14 100%) fixed;
}

.hud {
  background: linear-gradient(180deg, #22303c, #151d25);
  border-bottom: 1px solid #3e5163;
  box-shadow: 0 2px 12px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .05);
}

.panel, .set-block, .modal-box, .home-chat, .home-sheet {
  background: linear-gradient(180deg, #1f2b36 0%, #182129 100%);
  border-color: #3a4c5c;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 8px 24px rgba(0, 0, 0, .35);
}
/* 금빛 실선 - 위쪽만 살짝. 판마다 "테두리가 있다"는 느낌을 준다. */
.panel, .set-block, .modal-box { position: relative; }
.panel::after, .set-block::after, .modal-box::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(232, 182, 76, .55), transparent);
  pointer-events: none;
}

.btn {
  background: linear-gradient(180deg, #2c3c4b, #1d2832);
  border-color: #415668;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), 0 2px 0 #0b1116, 0 3px 8px rgba(0, 0, 0, .35);
  text-shadow: 0 1px 0 rgba(0, 0, 0, .55);
  transition: box-shadow .12s, transform .12s, filter .12s;
}
.btn:hover { filter: brightness(1.08); }
.btn:active { transform: translateY(2px); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .04), 0 0 0 #0b1116; }
.btn:disabled { filter: none; box-shadow: none; }
.btn-primary {
  background: linear-gradient(180deg, #62a9ee, #2f6fb4);
  border-color: #86bdf4;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3), 0 2px 0 #1b4570, 0 4px 12px rgba(74, 143, 212, .4);
}
.btn-danger {
  background: linear-gradient(180deg, #e27b62, #ad4632);
  border-color: #f0a08c;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 2px 0 #6a2418, 0 4px 12px rgba(212, 96, 74, .35);
}
.btn-back, .btn-link { box-shadow: none; text-shadow: none; }
.btn-link { background: none; }

.round-btn {
  background: radial-gradient(circle at 50% 28%, #2c3d4c 0%, #131c25 72%);
  border-color: #4d6376;
  box-shadow: 0 0 0 1px #0b1116, inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 10px rgba(0, 0, 0, .55);
  transition: box-shadow .15s, transform .15s;
}
.round-btn:hover {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px #0b1116, inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 10px rgba(0, 0, 0, .55), 0 0 16px rgba(232, 182, 76, .35);
}
.round-btn:active { transform: translateY(1px) scale(.98); }

.menu-btn {
  background: linear-gradient(180deg, #24313d, #171f27);
  border-color: #3e5163;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 2px 0 #0b1116;
}
.menu-btn:hover { border-color: var(--gold); }

.pill {
  background: linear-gradient(180deg, #283644, #1a242d);
  border: 1px solid #3e5163;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06);
}

/* 제목 - 금빛 그라데이션 글자 + 은은한 빛. */
.title-block h1 {
  background: linear-gradient(180deg, #fff0bf 0%, #e8b64c 55%, #a8772a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 0 rgba(0, 0, 0, .7)) drop-shadow(0 0 18px rgba(232, 182, 76, .35));
}
.topbar h2 { text-shadow: 0 1px 0 #000, 0 0 12px rgba(232, 182, 76, .25); }

/* 체력 막대 - 광택 한 줄과 깊이. */
.hpbar { border-color: #415668; box-shadow: inset 0 2px 5px rgba(0, 0, 0, .65); }
.hpbar-fill::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 50%;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(255, 255, 255, .28), rgba(255, 255, 255, 0));
  pointer-events: none;
}

/* 전장 - 빈 자리는 은은한 바닥, 카드는 진영 색으로 빛나는 틀. */
.lane {
  border-color: rgba(143, 163, 178, .3);
  background: radial-gradient(ellipse at 50% 110%, rgba(255, 255, 255, .035), transparent 70%);
}
.lane .unit {
  background: linear-gradient(180deg, #202d38, #131b23);
  border-color: #3e5163;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .6), 0 6px 14px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .06);
}
.lane.FIRE  .unit { border-color: var(--faction-fire);  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 12px rgba(224,112,74,.35), 0 6px 14px rgba(0,0,0,.45); }
.lane.WATER .unit { border-color: var(--faction-water); box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 12px rgba(90,166,216,.35), 0 6px 14px rgba(0,0,0,.45); }
.lane.WIND  .unit { border-color: var(--faction-wind);  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 12px rgba(127,207,154,.35), 0 6px 14px rgba(0,0,0,.45); }
.lane.EARTH .unit { border-color: var(--faction-earth); box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 12px rgba(201,154,91,.35), 0 6px 14px rgba(0,0,0,.45); }
.lane.IRON  .unit { border-color: var(--faction-iron);  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 12px rgba(168,174,187,.35), 0 6px 14px rgba(0,0,0,.45); }

/* 파티클 층 - 판을 덮는 캔버스. 만지는 것을 막지 않는다. */
#view-battle { position: relative; }
.battle-fx-canvas { position: absolute; inset: 0; pointer-events: none; z-index: 40; }

/* 맞는 순간 하얗게 번쩍. */
@keyframes unit-hit-flash { 0% { opacity: .7; } 100% { opacity: 0; } }
.lane .unit.hit::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: #fff;
  pointer-events: none;
  z-index: 5;
  animation: unit-hit-flash calc(200ms * var(--fx)) ease-out forwards;
}

/* 뜨는 숫자 - 두껍고 테가 있고 톡 튄다. */
.float {
  font-weight: 800;
  text-shadow: 0 0 6px #000, 0 1px 0 #000, 0 -1px 0 #000, 1px 0 0 #000, -1px 0 0 #000;
}
.float.damage { font-size: 17px; }
.float.damage.crit { font-size: 24px; letter-spacing: 1px; }

/* 스킬 연출을 더 세게 - 더 밝고 더 크게, 반짝임 한 겹. */
.spellfx { filter: saturate(1.25) brightness(1.1); }
.spellfx.fire  { background: radial-gradient(ellipse at 50% 100%, rgba(255,200,90,1) 0%, rgba(255,110,40,.85) 35%, rgba(200,40,20,.4) 60%, transparent 76%); }
.spellfx.ice   { box-shadow: inset 0 0 18px rgba(180,232,255,.75); }
.spellfx.bolt  { background: linear-gradient(100deg, transparent 38%, rgba(255,255,255,1) 50%, transparent 62%), radial-gradient(circle, rgba(196,224,255,.6), transparent 70%); }
.spellfx.venom { background: radial-gradient(circle at 50% 90%, rgba(160,240,130,1) 0%, rgba(70,160,70,.6) 45%, transparent 72%); }
.spellfx.dark  { box-shadow: inset 0 0 24px rgba(120,60,170,.9); }
.spellfx.light { background: radial-gradient(ellipse at 50% 0%, rgba(255,250,210,1) 0%, rgba(255,216,106,.6) 45%, transparent 76%); }

@media (prefers-reduced-motion: reduce) {
  .lane .unit.hit::before { animation: none; opacity: 0; }
  .battle-fx-canvas { display: none; }
}

/* 카드 타일(덱·도감·강화 …) - 전장 카드와 같은 틀. 진영 색으로 빛나고 별은 금빛이다. */
.card {
  background: linear-gradient(180deg, #202d38, #131b23);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .6), 0 4px 10px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255, 255, 255, .06);
}
.card.FIRE  { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 10px rgba(224,112,74,.3), 0 4px 10px rgba(0,0,0,.4); }
.card.WATER { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 10px rgba(90,166,216,.3), 0 4px 10px rgba(0,0,0,.4); }
.card.WIND  { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 10px rgba(127,207,154,.3), 0 4px 10px rgba(0,0,0,.4); }
.card.EARTH { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 10px rgba(201,154,91,.3), 0 4px 10px rgba(0,0,0,.4); }
.card.IRON  { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 0 10px rgba(168,174,187,.3), 0 4px 10px rgba(0,0,0,.4); }
.card.selected { box-shadow: 0 0 0 2px var(--accent) inset, 0 0 14px rgba(74, 143, 212, .55); }
.card-stars { color: var(--gold); text-shadow: 0 0 6px rgba(232, 182, 76, .6), 0 1px 0 #000; }
.card-name { text-shadow: 0 1px 0 #000, 0 0 4px rgba(0, 0, 0, .8); }

/* 지도 - 관문 타일도 판때기처럼. 지금 관문은 금빛으로 숨 쉬고, 우두머리는 금빛 테, 잠긴 것은 흐리다(그대로). */
.stage-node .stage-tile {
  background: linear-gradient(180deg, #1c2730, #10171e);
  border-color: #3a4c5c;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), 0 4px 8px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255, 255, 255, .05);
}
@keyframes stage-breathe {
  0%, 100% { box-shadow: 0 0 0 3px hsl(var(--map-hue, 200) 40% 30% / .5), 0 0 10px rgba(232, 182, 76, .35); }
  50%      { box-shadow: 0 0 0 3px hsl(var(--map-hue, 200) 40% 30% / .5), 0 0 22px rgba(232, 182, 76, .7); }
}
.stage-node.current .stage-tile { animation: stage-breathe 2.4s ease-in-out infinite; }
.stage-node.boss .stage-tile { box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), 0 0 12px rgba(232, 182, 76, .3), 0 4px 8px rgba(0, 0, 0, .4); }
.stage-node .stars { text-shadow: 0 0 6px rgba(232, 182, 76, .55), 0 1px 0 #000; }

.chapter-tab {
  background: linear-gradient(180deg, #2c3c4b, #1d2832);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06), 0 2px 0 #0b1116;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .5);
}
.chapter-tab.active {
  background: linear-gradient(180deg, #62a9ee, #2f6fb4);
  border-color: #86bdf4;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3), 0 2px 0 #1b4570, 0 0 12px rgba(74, 143, 212, .45);
}

/* 전투 - 턴 표시는 금테 배지, 영웅 줄은 판, 묘지는 어둡게 가라앉는다. */
.turn-marker {
  background: linear-gradient(180deg, #24313d, #171f27);
  border: 1px solid rgba(232, 182, 76, .45);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06), 0 2px 8px rgba(0, 0, 0, .5);
  text-shadow: 0 1px 0 #000;
}
.hero-row {
  background: linear-gradient(180deg, #1f2b36, #151d25);
  border-color: #3e5163;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 4px 12px rgba(0, 0, 0, .4);
}
.grave-slot {
  background: radial-gradient(circle at 50% 60%, rgba(120, 60, 170, .18), transparent 70%);
  border-color: rgba(183, 132, 255, .35);
}
.grave-slot.filled { box-shadow: 0 0 10px rgba(183, 132, 255, .3); }

@media (prefers-reduced-motion: reduce) {
  .stage-node.current .stage-tile { animation: none; }
}

/* 뽑기 결과 - 타일이 차례로 뒤집혀 나타난다. ★4 이상은 금빛으로 숨 쉰다(색만이 아니라 별도 찍혀 있다). */
@keyframes draw-flip {
  0%   { opacity: 0; transform: perspective(600px) rotateY(90deg) scale(.9); }
  60%  { opacity: 1; transform: perspective(600px) rotateY(-12deg) scale(1.04); }
  100% { opacity: 1; transform: perspective(600px) rotateY(0) scale(1); }
}
.draw-card { animation: draw-flip .5s cubic-bezier(.2, .8, .3, 1) both; }
.draw-card:nth-child(2) { animation-delay: .09s; }
.draw-card:nth-child(3) { animation-delay: .18s; }
.draw-card:nth-child(4) { animation-delay: .27s; }
.draw-card:nth-child(5) { animation-delay: .36s; }
.draw-card:nth-child(n+6) { animation-delay: .45s; }
@keyframes draw-rare-glow {
  0%, 100% { box-shadow: 0 0 0 2px var(--gold) inset, 0 0 10px rgba(232, 182, 76, .4); }
  50%      { box-shadow: 0 0 0 2px var(--gold) inset, 0 0 22px rgba(232, 182, 76, .85); }
}
.draw-rare { animation: draw-flip .5s cubic-bezier(.2, .8, .3, 1) both, draw-rare-glow 1.6s ease-in-out .5s infinite; }
#draw-modal { position: fixed; }
#draw-modal .battle-fx-canvas { z-index: 60; }

/* 목록 줄(우편·순위·상점·친구) - 판과 같은 결로. */
.mail-row, .rank-row, .shop-item {
  background: linear-gradient(180deg, #1f2b36, #182129);
  border-color: #3a4c5c;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .04), 0 2px 6px rgba(0, 0, 0, .3);
}
.rank-row.mine {
  background: linear-gradient(180deg, #27394a, #1c2a37);
  border-color: var(--accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06), 0 0 12px rgba(74, 143, 212, .35);
}
.section-title { color: var(--gold); text-shadow: 0 1px 0 #000; letter-spacing: .5px; }

@media (prefers-reduced-motion: reduce) {
  .draw-card, .draw-rare { animation: none; opacity: 1; }
}

/* 마을 - 살아 있는 밤. 별이 깜빡이고 달이 숨 쉬고 안개가 흐른다. 정보가 아니라 공기다(reduced-motion 이면 멈춘다). */
@keyframes town-twinkle { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
.town-stars { animation: town-twinkle 3.6s ease-in-out infinite; }
@keyframes town-moon-breathe {
  0%, 100% { box-shadow: 0 0 26px rgba(240, 232, 200, .35); }
  50%      { box-shadow: 0 0 40px rgba(240, 232, 200, .6); }
}
.town-moon { animation: town-moon-breathe 5s ease-in-out infinite; }
@keyframes town-fog { 0% { transform: translateX(-6%); } 100% { transform: translateX(6%); } }
.town-sky::after {
  content: "";
  position: absolute;
  left: -10%;
  right: -10%;
  bottom: 18%;
  height: 26%;
  background: linear-gradient(180deg, transparent, rgba(180, 210, 230, .07) 50%, transparent);
  filter: blur(6px);
  animation: town-fog 14s ease-in-out infinite alternate;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .town-stars, .town-moon, .town-sky::after { animation: none; }
}

/* 로그인 화면 - 마을과 같은 밤하늘. 별은 장식이라 낭독기가 읽을 것이 없다. */
#view-login::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    radial-gradient(1.5px 1.5px at 8% 18%, rgba(255, 255, 255, .5), transparent),
    radial-gradient(1.5px 1.5px at 22% 62%, rgba(255, 255, 255, .35), transparent),
    radial-gradient(1.5px 1.5px at 37% 30%, rgba(255, 255, 255, .3), transparent),
    radial-gradient(1.5px 1.5px at 58% 12%, rgba(255, 255, 255, .45), transparent),
    radial-gradient(1.5px 1.5px at 71% 74%, rgba(255, 255, 255, .3), transparent),
    radial-gradient(1.5px 1.5px at 86% 26%, rgba(255, 255, 255, .45), transparent),
    radial-gradient(1.5px 1.5px at 93% 58%, rgba(255, 255, 255, .3), transparent);
  animation: town-twinkle 4.2s ease-in-out infinite;
}
#view-login > * { position: relative; z-index: 1; }
#view-login .corner-settings, #view-login .corner-panel { z-index: 3; }
@media (prefers-reduced-motion: reduce) { #view-login::before { animation: none; } }

/* 결과 - 승리 제목은 금빛 글자, 패배는 잿빛. 색만이 아니라 글자 자체가 승/패를 말한다. */
.battle-result.won h2 {
  background: linear-gradient(180deg, #fff0bf 0%, #e8b64c 55%, #a8772a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: none;
  filter: drop-shadow(0 2px 0 rgba(0, 0, 0, .7)) drop-shadow(0 0 22px rgba(232, 182, 76, .5));
}
.battle-result .result-detail {
  background: linear-gradient(180deg, rgba(31, 43, 54, .96), rgba(24, 33, 41, .96));
  border-color: #3a4c5c;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .05);
}

/* 효과는 카드 전체를 삼킨다(158-43 · 대표님 "테두리까지 불을 질러"). 연출 층은 테두리 밖 6px 까지 번지고,
   테두리는 집안 색으로 타오른다 - 불은 일렁이고, 얼음은 얼어붙고, 번개는 튀고, 독은 스며들고, 어둠은 조여 들고, 빛·강화는 감싼다. */
.lane .fx { overflow: visible; }
.spellfx { inset: -6px; border-radius: 12px; }

@keyframes engulf-fire {
  0%   { box-shadow: 0 0 0 2px #ffb04a, 0 0 14px 4px rgba(255, 120, 40, .9), inset 0 0 18px rgba(255, 140, 40, .55); }
  25%  { box-shadow: 0 0 0 3px #ffd27a, 0 0 26px 10px rgba(255, 110, 30, 1), inset 0 0 26px rgba(255, 120, 30, .7); }
  55%  { box-shadow: 0 0 0 2px #ff8a2a, 0 0 18px 6px rgba(255, 90, 20, .85), inset 0 0 20px rgba(255, 110, 30, .55); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-ice {
  0%   { box-shadow: 0 0 0 2px #b4e8ff, 0 0 14px 4px rgba(120, 200, 255, .9), inset 0 0 22px rgba(180, 232, 255, .6); }
  40%  { box-shadow: 0 0 0 4px #e8f8ff, 0 0 24px 8px rgba(120, 200, 255, 1), inset 0 0 30px rgba(200, 240, 255, .75); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-bolt {
  0%, 30%, 60% { box-shadow: 0 0 0 3px #ffffff, 0 0 26px 8px rgba(160, 200, 255, 1), inset 0 0 20px rgba(200, 224, 255, .8); }
  15%, 45%     { box-shadow: 0 0 0 1px #8fb6ff, 0 0 6px 2px rgba(160, 200, 255, .4), inset 0 0 6px rgba(200, 224, 255, .3); }
  100%         { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-venom {
  0%   { box-shadow: 0 0 0 2px #8ce278, 0 0 12px 4px rgba(90, 200, 90, .8), inset 0 0 22px rgba(120, 220, 110, .6); }
  50%  { box-shadow: 0 0 0 3px #b8ff9a, 0 0 22px 8px rgba(90, 200, 90, .95), inset 0 0 30px rgba(120, 220, 110, .75); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-dark {
  0%   { box-shadow: 0 0 0 6px rgba(120, 60, 170, .8), 0 0 24px 10px rgba(60, 20, 100, .9), inset 0 0 30px rgba(40, 10, 70, .9); }
  50%  { box-shadow: 0 0 0 3px #b784ff, 0 0 16px 6px rgba(120, 60, 170, .9), inset 0 0 22px rgba(60, 20, 100, .8); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-light {
  0%   { box-shadow: 0 0 0 2px #fff4be, 0 0 18px 6px rgba(255, 230, 150, .9), inset 0 0 26px rgba(255, 240, 200, .7); }
  50%  { box-shadow: 0 0 0 4px #ffffff, 0 0 30px 12px rgba(255, 230, 150, 1), inset 0 0 34px rgba(255, 240, 200, .85); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-boost {
  0%   { box-shadow: 0 0 0 2px #ffc454, 0 0 16px 6px rgba(255, 180, 60, .9), inset 0 0 22px rgba(255, 200, 100, .6); }
  50%  { box-shadow: 0 0 0 3px #fff0b0, 0 0 26px 10px rgba(255, 180, 60, 1), inset 0 0 30px rgba(255, 200, 100, .75); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-wither {
  0%   { box-shadow: 0 0 0 4px rgba(150, 130, 190, .8), 0 0 18px 6px rgba(90, 74, 122, .9), inset 0 0 26px rgba(60, 45, 90, .8); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
@keyframes engulf-ward {
  0%   { box-shadow: 0 0 0 5px #96c8ff, 0 0 20px 6px rgba(150, 200, 255, .9), inset 0 0 18px rgba(150, 200, 255, .5); }
  40%  { box-shadow: 0 0 0 3px #ffffff, 0 0 30px 10px rgba(150, 200, 255, 1), inset 0 0 24px rgba(150, 200, 255, .6); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, inset 0 0 0 transparent; }
}
.lane .unit.engulf-fire   { animation: engulf-fire   calc(640ms * var(--fx)) ease-out; z-index: 6; }
.lane .unit.engulf-ice    { animation: engulf-ice    calc(640ms * var(--fx)) ease-out; z-index: 6; }
.lane .unit.engulf-bolt   { animation: engulf-bolt   calc(640ms * var(--fx)) steps(1, end); z-index: 6; }
.lane .unit.engulf-venom  { animation: engulf-venom  calc(640ms * var(--fx)) ease-out; z-index: 6; }
.lane .unit.engulf-dark   { animation: engulf-dark   calc(640ms * var(--fx)) ease-in; z-index: 6; }
.lane .unit.engulf-light  { animation: engulf-light  calc(640ms * var(--fx)) ease-out; z-index: 6; }
.lane .unit.engulf-boost  { animation: engulf-boost  calc(640ms * var(--fx)) ease-out; z-index: 6; }
.lane .unit.engulf-wither { animation: engulf-wither calc(640ms * var(--fx)) ease-out; z-index: 6; }
.lane .unit.engulf-ward   { animation: engulf-ward   calc(640ms * var(--fx)) ease-out; z-index: 6; }
@media (prefers-reduced-motion: reduce) {
  .lane .unit[class*="engulf-"] { animation: none; }
}

/* 공략(158-44) - 관문 창 안의 적 편성. 카드 상세의 스킬 줄과 같은 결. */
.guide-box { margin-top: 8px; max-height: 200px; overflow-y: auto; padding: 8px; border-radius: 8px; background: rgba(8, 12, 16, .6); border: 1px solid var(--line); }
.guide-title { font-weight: 700; margin-bottom: 6px; color: var(--gold); }
.guide-row { display: flex; gap: 8px; padding: 6px 0; border-top: 1px solid rgba(47, 65, 79, .6); font-size: 12px; }
.guide-row:first-of-type { border-top: 0; }
.guide-art { flex: 0 0 44px; width: 44px; height: 60px; border-radius: 6px; overflow: hidden; border: 1px solid var(--line); background: var(--bg-panel); position: relative; }
.guide-art img { width: 100%; height: 100%; object-fit: cover; }
.guide-row.FIRE .guide-art { border-color: var(--faction-fire); }
.guide-row.WATER .guide-art { border-color: var(--faction-water); }
.guide-row.WIND .guide-art { border-color: var(--faction-wind); }
.guide-row.EARTH .guide-art { border-color: var(--faction-earth); }
.guide-row.IRON .guide-art { border-color: var(--faction-iron); }
.guide-main { min-width: 0; flex: 1; }
.guide-head { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.guide-stats { color: var(--text); margin: 2px 0; }
.guide-skills { color: var(--text-dim); line-height: 1.5; }
.guide-pct { margin-top: 6px; font-size: 11px; }

/*
 * ==========================================================================
 * 작은 가로 화면의 전투 판 (158-47, 2026-09-03 밤 - 대표님 "모바일 UI 가 깨지지 않는지 확인")
 *
 * 874×402 에서는 스물여섯 화면 전부 넘치는 요소가 0 이었다. 그런데 **667×375(아이폰 8 가로)** 에서
 * 전투 판만 둘이 걸렸다:
 *   - 레인 5칸(90px) 오른쪽 끝이 592px 인데 오른쪽 기둥(130px) 이 right:0 에 떠 있어
 *     **배치 차례에 자동전투·차례 단추가 다섯째 칸을 70px 덮는다**
 *   - 세로 375 에서 판이 382 라 7px 넘친다
 * 좁은 화면에서만 레인을 90 → 76 으로 좁힌다(76×108, 카드 비율은 그대로 세로다). 준비구 열도 함께
 * 좁혀야 위아래 열이 겹친다(158-18). 기둥은 110px 로 - 118 + 5×76 + 24 = 522 이라 131px 이 남는다.
 * 낮은 화면(≤380)에서는 폭이 넓어도 레인 높이만 108 로 낮춘다 - 812×375 가 그 경우다.
 * ==========================================================================
 */
@media (max-width: 740px) {
  .view-battle .lane-row { grid-template-columns: repeat(5, 76px); min-height: 108px; }
  .view-battle .lane { min-height: 108px; }
  .view-battle .prep-row { grid-template-columns: repeat(5, 76px) 38px; }
  .battle-rail { width: 110px; }
  /*
   * 단추는 기둥보다 6px 좁고 오른쪽에 붙는다. 기둥은 판 위에 떠 있어서
   * 667×375 에서 **다섯째 레인의 오른쪽 5px 을 단추가 덮고 있었다**(221-2 장).
   * 기둥을 좁히지 않고 단추만 물러서게 한다 - 기둥은 pointer-events: none 이라
   * 실제로 칠하는 것은 단추뿐이다.
   */
  .battle-rail .deploy-panel .btn { width: 104px; margin-left: auto; font-size: 13px; padding: 10px 6px; }
}

@media (max-height: 380px) {
  .view-battle .lane-row { min-height: 108px; }
  .view-battle .lane { min-height: 108px; }
}

/*
 * ==========================================================================
 * 단추를 게임 것으로 (158-47, 2026-09-03 밤 - 대표님 "버튼들이 너무 웹사이트 버튼들처럼 생겼어")
 *
 * 158-43 의 스킨이 단추에 그림자와 기울기를 주었지만 **파란 알약 모양**은 그대로였다 - 웹의 것이다.
 * 여기서는 재질을 바꾼다: 어두운 청동판 위에 금빛 테, 안쪽으로 가는 금선 하나, 눌리면 판이 내려앉는다.
 * 주 단추(구매·확인·도전)는 **금판**이다 - 화면의 금색(별·턴 배지·현재 관문)과 같은 계열이라 한 몸으로 보인다.
 *
 * **크기·여백은 건드리지 않는다.** 가로 390 배치가 단추 높이에 맞춰져 있다(2-2-3 · 18장).
 * 모서리는 8 → 5 로만 - 알약이 판으로 읽히는 경계다.
 * ==========================================================================
 */
.btn {
  position: relative;
  border-radius: 5px;
  border: 1px solid #7d5f32;
  background:
    linear-gradient(180deg, rgba(255, 224, 160, .10), rgba(255, 224, 160, 0) 45%),
    linear-gradient(180deg, #3b3025 0%, #241c13 60%, #1a130c 100%);
  color: #f1e3c3;
  font-weight: 600;
  letter-spacing: .02em;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .7);
  box-shadow:
    inset 0 1px 0 rgba(255, 230, 170, .16),
    inset 0 -2px 0 rgba(0, 0, 0, .45),
    0 2px 0 #0d0905,
    0 3px 8px rgba(0, 0, 0, .45);
}
/* 안쪽 금선 - 판에 새긴 테. 글자 밑에 깔린다. */
.btn::before {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: 3px;
  border: 1px solid rgba(232, 182, 76, .22);
  pointer-events: none;
}
.btn:hover { filter: brightness(1.1); }
.btn:active {
  transform: translateY(2px);
  box-shadow: inset 0 1px 0 rgba(255, 230, 170, .08), inset 0 2px 4px rgba(0, 0, 0, .5), 0 0 0 #0d0905;
}
.btn:disabled { filter: grayscale(.7); opacity: .5; box-shadow: none; }
.btn:disabled::before { border-color: rgba(255, 255, 255, .06); }

.btn-primary {
  border-color: #f4d78e;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .28), rgba(255, 255, 255, 0) 50%),
    linear-gradient(180deg, #f0be55 0%, #d3902a 55%, #a5631a 100%);
  color: #2b1a05;
  text-shadow: 0 1px 0 rgba(255, 241, 200, .55);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .45),
    inset 0 -2px 0 rgba(110, 60, 0, .4),
    0 2px 0 #5a3808,
    0 4px 12px rgba(217, 150, 46, .35);
}
.btn-primary::before { border-color: rgba(120, 70, 10, .35); }
.btn-primary:active { box-shadow: inset 0 2px 4px rgba(90, 50, 0, .45), 0 0 0 #5a3808; }

.btn-danger {
  border-color: #f0a08c;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0) 50%),
    linear-gradient(180deg, #d9735a 0%, #a8402c 60%, #7d2a1a 100%);
  color: #fff1ea;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), inset 0 -2px 0 rgba(0, 0, 0, .35), 0 2px 0 #4a180e, 0 4px 12px rgba(212, 96, 74, .3);
}
.btn-danger::before { border-color: rgba(255, 200, 180, .25); }

/* 뒤로·글자 단추는 판이 아니다 - 새김도 그림자도 없이 가볍게. */
.btn-back, .btn-link { box-shadow: none; text-shadow: none; }
.btn-back::before, .btn-link::before { content: none; }
.btn-back {
  background: linear-gradient(180deg, #2a2219, #1a140d);
  border-color: #5a4628;
  color: #d8c7a3;
}
.btn-link { background: none; border-color: transparent; color: var(--gold, #e8b64c); }

/*
 * 탭도 같은 재질이다. 켜진 탭은 금판, 꺼진 탭은 청동판 - 파란 채움은 웹의 것이다.
 * `.pill` 은 상태 표시(재화·배지)라 두고, 사람이 누르는 것만 바꾼다.
 */
.chapter-tab, .tower-tab, .shard-tab, .sell-tab, .chal-tab, .codex-tab, .enh-tab {
  border-radius: 5px;
  border: 1px solid #5f4a28;
  background: linear-gradient(180deg, #2e261c, #1c150e);
  color: #cdbc98;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .6);
  box-shadow: inset 0 1px 0 rgba(255, 230, 170, .08), 0 1px 0 #0d0905;
}
.chapter-tab.active, .tower-tab.active, .shard-tab.active, .sell-tab.active,
.chal-tab.active, .codex-tab.active, .enh-tab.active {
  border-color: #f4d78e;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .25), rgba(255, 255, 255, 0) 50%),
    linear-gradient(180deg, #e9b74f 0%, #c9872a 60%, #a5631a 100%);
  color: #2b1a05;
  font-weight: 700;
  text-shadow: 0 1px 0 rgba(255, 241, 200, .5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4), 0 1px 0 #5a3808, 0 0 10px rgba(232, 182, 76, .25);
}
.chapter-tab.locked, .chapter-tab:disabled { filter: grayscale(.6); opacity: .55; }
/* 채팅 탭은 창머리의 띠라 판으로 만들지 않는다 - 켜진 표시만 파랑에서 금으로. */
.chat-tab.active { background: rgba(232, 182, 76, .16); color: #f1e3c3; }

/*
 * ==========================================================================
 * 결과 창 - 판 둘 (158-47, 2026-09-03 밤 - 대표님 "승리 창이 웹사이트 알림 같아. 설명 영역과 보상 영역을 따로")
 *
 * 금테를 두른 석판 하나 위에 제목, 그 아래 두 판(전투 기록 · 보상), 맨 아래 확인.
 * 보상은 칩 한 줄씩이다 - 골드·정수·경험치가 각자 색을 갖되 **글자로도 무엇인지 적혀 있다**(4장).
 * 세로 375 에서 넘치지 않게 판은 `max-height` 안에서 스크롤한다.
 * ==========================================================================
 */
.battle-result { padding: 12px; gap: 0; }
.result-plaque {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: min(720px, 96vw);
  max-height: 96vh;
  padding: 12px 16px 14px;
  border-radius: 10px;
  border: 1px solid #8a6a3a;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(232, 182, 76, .16), transparent 60%),
    linear-gradient(180deg, #1f1a14 0%, #14100b 100%);
  box-shadow: 0 0 0 3px #0d0905, 0 0 0 4px rgba(232, 182, 76, .35), 0 18px 50px rgba(0, 0, 0, .7);
  animation: result-in .28s ease-out;
}
.result-plaque h2 { font-size: 30px; letter-spacing: .06em; }
.result-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  width: 100%;
  min-height: 0;
}
.result-panel {
  text-align: left;
  border-radius: 8px;
  border: 1px solid #5f4a28;
  background: linear-gradient(180deg, rgba(44, 36, 26, .92), rgba(28, 22, 15, .96));
  box-shadow: inset 0 1px 0 rgba(255, 230, 170, .08);
  padding: 8px 12px 10px;
  min-height: 0;
  overflow: auto;
  max-height: 46vh;
}
.result-panel h3 {
  margin: 0 0 6px;
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #cfae66;
  border-bottom: 1px solid rgba(232, 182, 76, .25);
  padding-bottom: 4px;
}
/* 옛 .result-detail 의 알림창 모양을 벗긴다 - 이제 판 안의 글줄이다. */
.battle-result .result-detail,
.result-detail {
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0;
  max-width: none;
  font-size: 13px;
  line-height: 1.7;
  color: #e6d9bd;
}
.result-line + .result-line { border-top: 1px dashed rgba(232, 182, 76, .14); }
.result-stars { color: var(--gold, #e8b64c); letter-spacing: .08em; }
/* 첫 클리어·레벨 업은 기록 판의 강조 줄이다 - 오른쪽 보상 판에는 받은 것만 둔다. */
.result-first { color: var(--gold, #e8b64c); font-weight: 800; letter-spacing: .04em; text-shadow: 0 0 10px rgba(232, 182, 76, .45); }
.result-levelup { color: #ffd9f3; font-weight: 800; }
.result-levelup-detail { display: block; font-size: 11px; color: #bfae8c; font-weight: 500; }
.result-rewards { display: flex; flex-direction: column; gap: 6px; }
.reward-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 6px;
  border: 1px solid rgba(232, 182, 76, .2);
  background: rgba(0, 0, 0, .28);
  font-size: 13px;
  color: #f1e3c3;
}
.reward-chip small { display: block; font-size: 11px; color: #bfae8c; }
.reward-icon {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .25);
  background: #888;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .35);
}
.reward-gold .reward-icon { background: radial-gradient(circle at 40% 35%, #ffe9a8, #d99a2b 70%); }
.reward-essence .reward-icon { background: radial-gradient(circle at 40% 35%, #c9f0ff, #4aa9d8 70%); border-radius: 30%; }
.reward-exp .reward-icon { background: radial-gradient(circle at 40% 35%, #d8ffd0, #4fae5a 70%); }
.reward-blocked .reward-icon, .reward-none .reward-icon { display: none; }
.reward-blocked .reward-text, .reward-none .reward-text { color: var(--text-dim); font-size: 12px; }
.battle-result.lost .result-plaque { border-color: #55606a; box-shadow: 0 0 0 3px #0d0905, 0 0 0 4px rgba(120, 130, 140, .3), 0 18px 50px rgba(0, 0, 0, .7); }
.battle-result.lost .result-panel h3 { color: #a9b3bb; border-bottom-color: rgba(169, 179, 187, .25); }

@media (max-width: 560px) {
  .result-grid { grid-template-columns: 1fr; }
  .result-panel { max-height: 30vh; }
}
@media (max-height: 420px) {
  .result-plaque { gap: 6px; padding: 8px 12px 10px; }
  .result-plaque h2 { font-size: 24px; }
  .result-panel { padding: 6px 10px 8px; }
  .result-detail { font-size: 12px; line-height: 1.55; }
  .reward-chip { padding: 4px 8px; font-size: 12px; }
}

/*
 * 결과 창 - 글자 위계와 경험치 막대 (158-48, 대표님 "글씨 크기가 다 똑같아서 PPT 보는 것 같아 · 경험치 바 · 레벨업 애님")
 *
 *   제목 38px(금빛, 자간 넓게) > 보상 칩 15px(굵게) > 기록 13px > 판 머리 11px(대문자 자간).
 *   턴 수만 22px 로 튀운다 - 기록 판에서 눈이 먼저 닿을 숫자다.
 *   경험치 칩 아래에 막대: 레벨 · 차오르는 금빛 막대 · "지금 / 필요" 숫자. 레벨을 넘으면 「레벨 업!」이 뜨고 숫자가 튀어오른다.
 */
.result-plaque h2 { font-size: 38px; letter-spacing: .12em; line-height: 1.1; }
.result-panel h3 { font-size: 11px; letter-spacing: .18em; }
.result-line { font-size: 13px; }
.result-line:first-child { font-size: 15px; color: #f1e3c3; }
.result-line:first-child b.result-turns { font-size: 22px; color: var(--gold, #e8b64c); font-weight: 800; margin-right: 2px; }
.reward-chip { font-size: 15px; font-weight: 700; padding: 7px 10px; }
.reward-chip .reward-icon { width: 18px; height: 18px; }
.reward-chip small { font-weight: 500; }
.reward-exp { flex-wrap: wrap; }
.reward-exp .reward-text { flex: 1 1 auto; }

.exp-anim { flex: 1 1 100%; display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 4px 8px; margin-top: 4px; font-weight: 500; }
.exp-lv { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: #cfae66; white-space: nowrap; }
.exp-lv-num { display: inline-block; font-size: 15px; color: var(--gold, #e8b64c); transition: transform .25s; }
.exp-track {
  position: relative; display: block; height: 10px; border-radius: 5px;
  border: 1px solid #7d5f32; background: linear-gradient(180deg, #14100b, #241c13);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .7); overflow: hidden;
}
.exp-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  background: linear-gradient(180deg, #ffe9a8 0%, #e8b64c 45%, #b97b1f 100%);
  box-shadow: 0 0 8px rgba(232, 182, 76, .7);
  transition: width .12s linear;
}
.exp-fill::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, .35) 50%, transparent 100%);
  animation: exp-sheen 1.1s linear infinite;
}
@keyframes exp-sheen { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.exp-nums { font-size: 11px; color: #bfae8c; white-space: nowrap; font-variant-numeric: tabular-nums; }
.exp-shout { font-size: 12px; font-weight: 800; color: #fff0bf; letter-spacing: .1em; opacity: 0; transform: translateY(4px); transition: opacity .2s, transform .2s; text-shadow: 0 0 10px rgba(232, 182, 76, .9); }
.exp-anim.exp-levelup .exp-shout { opacity: 1; transform: translateY(0); animation: exp-shout .9s ease-out; }
.exp-anim.exp-levelup .exp-lv-num { animation: exp-lv-pop .6s ease-out; }
.exp-anim.exp-levelup .exp-track { box-shadow: 0 0 14px rgba(232, 182, 76, .8), inset 0 1px 2px rgba(0, 0, 0, .7); }
@keyframes exp-shout { 0% { transform: scale(.6) translateY(6px); opacity: 0; } 30% { transform: scale(1.25) translateY(0); opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
@keyframes exp-lv-pop { 0% { transform: scale(1); } 40% { transform: scale(1.8); color: #fff6d6; } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .exp-fill::after, .exp-anim.exp-levelup .exp-shout, .exp-anim.exp-levelup .exp-lv-num { animation: none; }
}

/*
 * 마을의 출석·점령 배너가 오른쪽 위 관리자 단추·설정 톱니와 겹쳤다(158-48, 874×402 에서 확인).
 * 흐름 안에 있어 화면 맨 위에 앉았기 때문이다 - 그 줄 아래 오른쪽에 띄운다.
 */
#view-home { position: relative; }
/*
 * 222장(PC 세션)이 잰 대로 오른쪽 위에 두면 세 크기 모두에서 채팅 판·옆 기둥과 겹쳤다. 겹치지 않는 자리는
 * **왼쪽 기둥 아래**다 - 초상·재화 아래(y≈62)부터 기력(y≈285)까지 비어 있고, 폭 164px 이면 667 에서도 채팅(180)에 닿지 않는다.
 * 글줄과 단추를 세로로 쌓는다. 두 띠가 함께 뜨면 점령 띠가 그 아래(140px)로 간다.
 */
#view-home .occupy-banner {
  position: absolute;
  top: 62px;
  left: 14px;
  z-index: 5;
  margin: 0;
  width: 164px;
  max-width: none;
  word-break: keep-all;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 6px 10px;
  font-size: 12px;
  line-height: 1.35;
  border-color: #8a6a3a;
  background: linear-gradient(180deg, rgba(60, 46, 28, .95), rgba(36, 28, 19, .96));
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
}
#view-home .occupy-banner .btn { align-self: flex-end; }
#view-home #signin-banner:not(.hidden) ~ #occupy-banner { top: 140px; }

/* 낮은 화면(≤420)에서는 제목을 줄이고 판에 높이를 더 준다 - 보상 넷 + 경험치 막대가 스크롤 없이 보여야 한다. */
@media (max-height: 420px) {
  .result-plaque { gap: 4px; padding: 6px 12px 8px; }
  .result-plaque h2 { font-size: 26px; letter-spacing: .1em; }
  .result-panel { max-height: 56vh; padding: 5px 10px 7px; }
  .result-panel h3 { margin-bottom: 3px; font-size: 10px; }
  .result-line { font-size: 12px; line-height: 1.5; }
  .result-line:first-child { font-size: 13px; }
  .result-line:first-child b.result-turns { font-size: 19px; }
  .reward-chip { padding: 4px 8px; font-size: 13px; }
  .reward-chip .reward-icon { width: 15px; height: 15px; }
  .result-rewards { gap: 4px; }
  .exp-anim { margin-top: 2px; }
  .exp-track { height: 8px; }
}

/* 보상이 하나도 없으면 오른쪽 판을 접는다 - 기록 판이 가로를 다 쓴다(158-49). */
.battle-result.no-rewards .result-panel-rewards { display: none; }
.battle-result.no-rewards .result-grid { grid-template-columns: 1fr; }
.battle-result.no-rewards .result-plaque { width: min(520px, 96vw); }

/*
 * 입력칸도 같은 재질로 (158-49). 흰 테의 웹 입력칸이 청동 단추 옆에 서면 둘 중 하나가 남의 것으로 보인다.
 * 어두운 가죽 위에 안으로 파인 홈, 초점이 오면 금테. 크기·여백은 그대로다.
 */
.view input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
.view textarea,
.view select {
  border: 1px solid #5f4a28;
  border-radius: 5px;
  background: linear-gradient(180deg, #120e09, #1c150e);
  color: #f1e3c3;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .65), inset 0 0 0 1px rgba(232, 182, 76, .06);
  caret-color: #e8b64c;
}
.view input::placeholder, .view textarea::placeholder { color: #8b7a5a; }
.view input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):focus,
.view textarea:focus,
.view select:focus {
  outline: none;
  border-color: #e8b64c;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .65), 0 0 0 2px rgba(232, 182, 76, .25);
}
.view select option { background: #1c150e; color: #f1e3c3; }

/*
 * 관문 창의 별을 크게 (158-49, 대표님 아이폰 스크린샷: "상단에 별을 크게 만들어 줘, 잘 보이지도 않네").
 * 15px 글자 별이 폰에서는 점 셋이었다. 32px 금빛 별에 빛을 두르고, 빈 별은 어둡게 - 색만으로 알리지 않으려고
 * 옆의 "n / m" 은 그대로 둔다(원칙 4번). 창 높이 +14px 은 안쪽 스크롤이 받는다.
 */
.sm-stars { gap: 10px; margin: 0 0 8px; }
.sm-star-row {
  font-size: 32px;
  line-height: 1;
  letter-spacing: 4px;
  background: linear-gradient(180deg, #fff0bf 0%, #e8b64c 55%, #a8772a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgba(0, 0, 0, .8)) drop-shadow(0 0 10px rgba(232, 182, 76, .45));
}
.sm-star-row .sm-star-empty { background: linear-gradient(180deg, #6b625a, #3a332d); -webkit-background-clip: text; background-clip: text; }
.sm-star-count { font-size: 13px; color: #cfae66; font-weight: 700; }

/*
 * 잠긴 강화 판 (158-49, 대표님 "아예 자물쇠 이미지를 보여 주고 레벨 21부터 룬 강화가 해제됩니다 라고").
 * 자물쇠는 그림 파일 없이 CSS 로 그린다 - 몸통은 청동판, 고리는 굵은 테. 글자는 금빛으로 문턱과 내 레벨을 함께 적는다.
 */
.enh-locked {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 132px;
  padding: 18px;
  text-align: center;
  background:
    repeating-linear-gradient(135deg, rgba(232, 182, 76, .05) 0 8px, transparent 8px 16px),
    linear-gradient(180deg, #1a140d, #110d08);
  border: 1px solid #5f4a28;
}
.enh-locked.hidden { display: none; }
.enh-lock {
  position: relative;
  display: block;
  width: 46px;
  height: 36px;
  margin-top: 18px;
  border-radius: 6px;
  background: linear-gradient(180deg, #8a6a3a 0%, #5a4324 60%, #3d2c16 100%);
  box-shadow: inset 0 1px 0 rgba(255, 230, 170, .35), 0 3px 0 #0d0905, 0 6px 14px rgba(0, 0, 0, .55);
}
.enh-lock::before {
  content: "";
  position: absolute;
  left: 8px;
  right: 8px;
  top: -20px;
  height: 26px;
  border: 6px solid #a8863f;
  border-bottom: 0;
  border-radius: 16px 16px 0 0;
  box-shadow: inset 0 1px 0 rgba(255, 230, 170, .3);
}
.enh-lock::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 11px;
  width: 8px;
  height: 14px;
  margin-left: -4px;
  border-radius: 4px 4px 2px 2px;
  background: #1a130c;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .8);
}
.enh-lock-text { margin: 0; font-size: 15px; font-weight: 700; color: var(--gold, #e8b64c); text-shadow: 0 1px 0 rgba(0, 0, 0, .7); }

/*
 * 원소 문양 (158-49, 대표님 "'미르하 · 물' 이러니까 좀 별로야 - 마크를 달아 줘").
 * 그림 파일 없이 CSS 로 그린다: 물방울·불꽃·잎·바위·다이아. 12px, 글줄 가운데에 앉는다.
 */
.elem {
  display: inline-block;
  width: 12px;
  height: 12px;
  margin: 0 5px 0 0;
  vertical-align: -2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45), 0 1px 2px rgba(0, 0, 0, .6);
}
.elem-WATER { border-radius: 0 50% 50% 50%; transform: rotate(45deg) scale(.92); background: radial-gradient(circle at 35% 35%, #cfeeff, #3d9be0 55%, #1f5fa0); }
.elem-FIRE  { border-radius: 0 50% 50% 50%; transform: rotate(45deg) scale(.92); background: radial-gradient(circle at 40% 70%, #fff1a8, #ff9a3c 45%, #d9481f); }
.elem-WIND  { border-radius: 50% 0 50% 0; transform: rotate(-10deg); background: linear-gradient(135deg, #d8ffd0, #4fae5a 55%, #2b7a3a); }
.elem-EARTH { clip-path: polygon(50% 0, 100% 30%, 86% 100%, 14% 100%, 0 30%); border-radius: 0; background: linear-gradient(180deg, #d1a26a, #8a5a2a 55%, #5a3a18); box-shadow: none; }
.elem-IRON  { transform: rotate(45deg) scale(.85); border-radius: 2px; background: linear-gradient(135deg, #f4f6f8, #9aa7b3 50%, #56636f); }
.card-kind { display: flex; align-items: center; white-space: nowrap; }
.card-kind .elem { flex: none; }

/* 머리띠의 레벨 배지 - 파란 원은 웹의 것이다. 마을 초상 고리와 같은 금테 메달로(158-49). 크기 28px 그대로. */
.hud-level {
  background: radial-gradient(circle at 50% 30%, #3b3025, #1a130c 70%);
  border: 1px solid #c9a24a;
  color: #f1dfae;
  box-shadow: 0 0 0 2px #0d0905, 0 0 0 3px rgba(232, 182, 76, .35), inset 0 1px 0 rgba(255, 230, 170, .25);
  text-shadow: 0 1px 0 rgba(0, 0, 0, .8);
}

/*
 * 카드 상세 - 좁게, 그림 왼쪽 위에 큰 노란 별 (158-49, 대표님 "카드가 쓸데없이 가로로 넓은 느낌 · 좌측 상단에 별이 크게 · 노란색 별로").
 * 두 칸 배치(그림 | 글)의 상한을 720 → 560px 로 좁혀 능력치 네 칸이 늘어지지 않게 한다. 별은 그림 위 왼쪽 모서리,
 * 26px 노란 별에 어두운 테를 두른다 - 밝은 그림 위에서도 읽힌다.
 */
.modal-box.cm-box { max-width: 560px; }
.cm-art-stars {
  position: absolute;
  left: 6px;
  top: 4px;
  z-index: 2;
  font-size: 26px;
  line-height: 1;
  letter-spacing: -2px;
  color: #ffd84a;
  text-shadow: 0 0 2px #000, 0 0 2px #000, 0 2px 0 rgba(0, 0, 0, .9), 0 0 10px rgba(255, 200, 60, .55);
  pointer-events: none;
}
.cm-box .cm-art-wrap { overflow: hidden; }

/*
 * 소셜 로그인 단추는 청동 스킨을 **입지 않는다** - 카카오(#FEE500 노랑)·구글(흰 바탕)은 각 회사의 브랜드 규정이다.
 * `.btn` 스킨이 같은 특이성으로 뒤에 와서 덮었기에 두 클래스로 다시 세운다(158-49).
 */
.btn.social-kakao, .btn.social-google {
  text-shadow: none;
  letter-spacing: 0;
  box-shadow: 0 2px 0 rgba(0, 0, 0, .45), 0 3px 8px rgba(0, 0, 0, .35);
}
.btn.social-kakao::before, .btn.social-google::before { content: none; }
.btn.social-kakao  { background: #fee500; color: #191600; border-color: #e5cf00; }
.btn.social-google { background: #ffffff; color: #1f1f1f; border-color: #dadce0; }
.btn.social-kakao:hover  { background: #f5dc00; filter: none; }
.btn.social-google:hover { background: #f1f3f4; filter: none; }

/* 타일의 별도 상세와 같은 노란 별로, 한 단계 크게(12 → 14px). 그림 위에서 읽히게 어두운 테를 두른다(158-49). */
.card .card-stars { font-size: 14px; line-height: 1.1; color: #ffd84a; letter-spacing: -1px; text-shadow: 0 0 2px #000, 0 1px 0 rgba(0, 0, 0, .9); }

/* 계정 연동 동의 창(226-52) - 소셜 로그인 앞에 한 번. 줄바꿈은 사전 문구의 것을 그대로 쓴다. */
.consent-body { white-space: pre-line; line-height: 1.55; margin: 10px 0; }
.consent-note { font-size: 12px; opacity: .75; margin: 0 0 12px; }
.consent-note a { color: var(--accent); }
.modal-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.modal-actions .btn { flex: 1 1 140px; }

/* 침공당한 관문(226-76) - 그 챕터의 관문 하나에만 붉은 테가 탄다. 색과 함께 chapter.invaded 의 💀 글자가 있다(원칙 4). */
.stage-node.invaded .stage-tile { box-shadow: 0 0 0 2px #ff7a3a inset, 0 0 12px #ff7a3a; }

/* 서버 상태가 유지되는 동안 카드 전체에 남는 화상·빙결. 입력은 카드로 통과한다. */
.unit.status-burning { box-shadow: inset 0 -22px 22px -9px #ff6417, 0 0 15px #ff8421; }
.unit.status-frozen { box-shadow: inset 0 0 0 3px #a4ecff, inset 0 0 24px #54c3e6, 0 0 15px #9eeeff; }
.unit.status-burning.status-frozen { box-shadow: inset 0 0 0 3px #a4ecff, inset 0 -22px 22px -9px #ff6417, 0 0 15px #9eeeff; }

/* 원정 기록: 일일·주간·업적, 28일 캐릭터 보상 */
.journal-tabs { display: flex; gap: 8px; flex-wrap: wrap; }
.journal-milestone { color: var(--gold); font-size: 20px; font-weight: 700; }
.att-day.final-reward { border: 2px solid var(--gold); background: linear-gradient(145deg, #332640, #18283a); }

/* 수령 가능한 보상: 글자 표식은 사전에서 넣는다. */
.reward-attention { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; margin-left: 5px; border-radius: 50%; background: #b63d2d; color: white; font-weight: bold; font-size: 12px; pointer-events: none; }
.reward-attention[hidden] { display: none; }

/* 서버의 레벨 해금 상태를 따른다. */
.content-locked-hidden { display: none !important; }
#content-next { position: absolute; left: 24px; bottom: 66px; color: var(--gold, #e8c882); font-size: 13px; }
.content-unlock-box { animation: content-unlock-reveal .7s ease-out both; border: 1px solid #e8c882; }
.content-unlock-list { max-height: 38vh; overflow-y: auto; }
@keyframes content-unlock-reveal { from { opacity: 0; transform: scale(.88); } to { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .content-unlock-box { animation: none; } }

/* 선택한 모집 원소를 팩 제목과 함께 표시한다. */
.pack-row[data-focus="FIRE"] { --recruit-tone: #ef976c; }
.pack-row[data-focus="WATER"] { --recruit-tone: #72bfe8; }
.pack-row[data-focus="WIND"] { --recruit-tone: #7fcf9a; }
.pack-row[data-focus="EARTH"] { --recruit-tone: #c99a5b; }
.pack-row[data-focus="IRON"] { --recruit-tone: #a8aebb; }
.pack-row[data-focus]:not([data-focus=""]) {
  border: 2px solid var(--recruit-tone);
  background: linear-gradient(135deg, color-mix(in srgb, var(--recruit-tone) 35%, #102331), #102331);
}
.pack-row[data-focus]:not([data-focus=""]) .pack-info > b { color: var(--recruit-tone); }

.ico-ticket { position: relative; width: 18px; height: 12px; border-radius: 3px; background: var(--gold); border: 1px solid #ac7639; }
.ico-ticket::after { content: ""; position: absolute; width: 6px; height: 6px; left: 5px; top: 2px; transform: rotate(45deg); background: #9f4350; }
