/* 콜사대 부설 오죠아카데미 ~ 비밀의 아가씨는 학교에서 무쌍중 ~ — 모바일 우선, PC 확장 (기획서 2장) */

:root {
  --bg: #12121c;
  --bg-raised: #1b1b28;
  --bg-sunken: #0d0d15;
  --line: #2e2e40;
  --line-soft: #242433;

  --text: #e8e6f0;
  --text-dim: #a09eb4;
  --text-faint: #6f6d85;

  --gold: #d8b46a;
  --gold-dim: #9a7f45;
  --plum: #8b5fa8;

  /* 파생 능력치의 색. 공격은 벼려진 쇠의 붉은 기, 방어는 막아서는 푸른 기.
   * 둘을 나란히 세울 때 어느 쪽인지 숫자를 읽기 전에 알아보게 한다. */
  --atk: #e8845c;
  --def: #6fa8dc;

  --primary-stat: #6f9ffb;   /* 주력 상승 스탯 — 기획서 10.2 */
  --secondary-stat: #63c9a8; /* 보조 상승 스탯 */

  --crit: #ffcf5c;
  --damage: #ff8a7a;
  --heal: #7ddba0;
  --loot: #9fd6f5;
  --danger: #e2685f;

  /*
   * 직업 테마 — theme.js가 이 두 값만 갈아끼우면 아래 규칙이 전부 따라온다.
   *   테두리 40% / 배경 8% / 강조 원색. 금색(--gold)은 건드리지 않는다.
   */
  --job: #8b5fa8;
  --job-rgb: 139 95 168;
  --job-line: rgb(var(--job-rgb) / .40);
  --job-line-soft: rgb(var(--job-rgb) / .22);
  --job-wash: rgb(var(--job-rgb) / .08);
  --job-wash-strong: rgb(var(--job-rgb) / .14);

  --radius: 10px;
  --pad: 14px;

  --font: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Malgun Gothic', system-ui, sans-serif;
  --mono: 'SFMono-Regular', 'D2Coding', Consolas, monospace;

  /*
   * 타이틀 전용 명조.
   *
   * 본문은 읽기 쉬운 고딕으로 두고 **제목만 명조로 갈랐다.** 귀족 학원의 간판이
   * 게임 UI와 같은 글씨체이면 그저 화면의 일부가 되어버린다 —
   * 획에 붓의 흔적이 남은 글씨라야 문 앞에 걸린 현판처럼 읽힌다.
   *
   * 글꼴이 오지 않아도 명조 계열로 물러서게 폴백을 둔다.
   */
  --display: 'Gowun Batang', 'Nanum Myeongjo', 'Batang', serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
}

body { overscroll-behavior-y: none; }

.app {
  min-height: 100%;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.boot {
  margin: auto;
  color: var(--text-faint);
}

/* ── 공통 요소 ───────────────────────────────────────── */

button {
  font: inherit;
  color: inherit;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 16px;
  cursor: pointer;
  transition: border-color .15s, background .15s, opacity .15s;
}
button:hover:not(:disabled) { border-color: var(--gold-dim); }
button:disabled { opacity: .45; cursor: not-allowed; }

button.primary {
  background: linear-gradient(180deg, #3a2f52, #2a2340);
  border-color: var(--gold-dim);
  color: var(--gold);
  font-weight: 600;
}
button.primary:hover:not(:disabled) { border-color: var(--gold); }

.ghost { background: transparent; border-color: var(--line-soft); color: var(--text-dim); }
button.link {
  background: none; border: none; padding: 4px 6px;
  color: var(--text-dim); text-decoration: underline; text-underline-offset: 3px;
}
button.link:hover { color: var(--gold); }

input, select, textarea {
  font: inherit;
  width: 100%;
  color: var(--text);
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--plum);
}

label { display: block; font-size: 13px; color: var(--text-dim); margin-bottom: 6px; }
.field { margin-bottom: 14px; }
.hint { font-size: 12px; color: var(--text-faint); margin-top: 6px; }

.error {
  color: var(--danger);
  background: rgba(226, 104, 95, .1);
  border: 1px solid rgba(226, 104, 95, .35);
  border-radius: var(--radius);
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: 12px;
}
.notice {
  color: var(--gold);
  background: rgba(216, 180, 106, .08);
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  padding: 12px;
  font-size: 13px;
  margin-bottom: 12px;
}

.row { display: flex; gap: 8px; align-items: center; }
.spread { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.grow { flex: 1; }
.stack { display: flex; flex-direction: column; gap: 8px; }
.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.mono { font-family: var(--mono); }

/* ── 타이틀 화면 (기획서 6.1) ─────────────────────────── */

.title-screen {
  margin: auto;
  width: 100%;
  max-width: 380px;
  padding: 32px 20px calc(32px + env(safe-area-inset-bottom));
}
.game-title {
  font-family: var(--display);
  text-align: center;
  /* 제목이 길어지면 줄어든다. 좁은 화면에서 마지막 한 글자만 넘어가
   * "…오죠아카데 / 미"처럼 끊기는 것을 막는다.
   * 명조는 같은 크기에서 고딕보다 작아 보이므로 조금 키웠다. */
  font-size: clamp(22px, 7vw, 32px);
  letter-spacing: .06em;
  color: var(--gold);
  margin: 0 0 6px;
  font-weight: 700;
  line-height: 1.35;

  /* 그래도 두 줄이 되면 의미 단위에 가깝게 나눈다 —
   * "콜사대 부설 / 오죠아카데미"처럼 양쪽이 고르게 떨어진다. */
  text-wrap: balance;
  word-break: keep-all;

  /* 금색 획 뒤에 옅은 빛을 깔아 현판처럼 도드라지게 한다 */
  text-shadow: 0 0 24px rgb(216 180 106 / .25);
}
.game-subtitle {
  /* 부제도 같은 명조로. 제목과 한 덩어리로 읽혀야 간판이 된다 —
   * 다만 굵기를 낮추고 자간을 벌려 제목 아래에 조용히 깔리게 했다. */
  font-family: var(--display);
  font-weight: 400;
  text-align: center;
  color: var(--text-faint);
  font-size: 13px;
  letter-spacing: .18em;
  margin-bottom: 32px;
  word-break: keep-all;
}
.title-links {
  display: flex;
  justify-content: center;
  gap: 4px;
  margin-top: 18px;
  flex-wrap: wrap;
}
.recovery-code {
  font-family: var(--mono);
  font-size: 19px;
  letter-spacing: .12em;
  color: var(--gold);
  text-align: center;
  background: var(--bg-sunken);
  border: 1px dashed var(--gold-dim);
  border-radius: var(--radius);
  padding: 14px;
  margin: 12px 0;
  user-select: all;
  word-break: break-all;
}

/* ── 캐릭터 생성 ─────────────────────────────────────── */

.create-screen {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 20px 16px calc(24px + env(safe-area-inset-bottom));
}
.section {
  background: var(--bg-raised);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: var(--pad);
  margin-bottom: 14px;
}
.section > h2 {
  font-size: 14px;
  color: var(--gold);
  margin: 0 0 12px;
  font-weight: 600;
  letter-spacing: .04em;
}

.choice-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
@media (min-width: 560px) { .choice-grid { grid-template-columns: repeat(3, 1fr); } }

.choice {
  text-align: left;
  padding: 10px 12px;
  line-height: 1.4;
}
.choice.selected { border-color: var(--gold); background: rgba(216, 180, 106, .1); }
.choice .choice-name { font-weight: 600; display: block; }
.choice .choice-note { font-size: 11px; color: var(--text-faint); display: block; margin-top: 2px; }

/* 스탯 배분 */
.stat-list { display: flex; flex-direction: column; gap: 6px; }
.stat-row {
  display: grid;
  grid-template-columns: 1fr auto auto auto auto;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 8px;
  background: var(--bg-sunken);
}
.stat-row .stat-name { font-size: 14px; }
.stat-row .stat-total { font-family: var(--mono); font-size: 15px; min-width: 2.4em; text-align: right; }
.stat-row .stat-bonus { font-family: var(--mono); font-size: 12px; color: var(--gold); min-width: 2.2em; text-align: right; }
.stat-row button { padding: 4px 12px; font-size: 16px; line-height: 1; }

/* 성장 특성 표시 (기획서 10.2) */
.growth-primary { color: var(--primary-stat); font-weight: 700; }
.growth-secondary { color: var(--secondary-stat); }

.bonus-counter { font-family: var(--mono); color: var(--gold); }
.bonus-counter.done { color: var(--heal); }

/* ── 초상화 ──────────────────────────────────────────── */

.portrait {
  aspect-ratio: 1 / 1;   /* 이미지가 늦게 와도 레이아웃이 밀리지 않는다 */
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--bg-sunken);
  object-fit: cover;
  display: block;
}
.portrait-thumb {
  width: 56px;
  height: 56px;
  flex: 0 0 56px;
  border-radius: 8px;
  border: 1px solid var(--line);
  object-fit: cover;
  background: var(--bg-sunken);
  cursor: pointer;
}
.portrait-preview { max-width: 200px; margin: 0 auto 12px; }

/* ── 캐릭터 창의 초상화 자리 ─────────────────────────────
 *
 * 초상화는 칸 너비를 다 쓰는 정사각형이라, 좁은 화면에서는 그것 하나로
 * 첫 화면이 끝났다. 정작 보러 온 생명력·공격력·스탯은 한참 굴려야 나왔다.
 * 그래서 **글의 순서(DOM)는 스테이터스가 먼저**이고, 초상화는 맨 아래에 선다.
 *
 * 넓은 화면에서는 되돌린다. 옆 컬럼이 따로 서 있어 위아래로 길어져도
 * 본문을 밀지 않으므로, 얼굴이 먼저 보이는 편이 낫다. */
.pc-portrait-block { margin-top: 16px; }

/* 초상화 아래 두 버튼은 **한 번 누르고 마는 것**이다 —
 * 한 번 정해두면 다시 만질 일이 거의 없는데도 본문 버튼과 같은 크기라
 * 캐릭터 창에서 제 몫보다 큰 자리를 차지했다. 세로를 절반으로 줄인다.
 * 가로 여백은 남겨둔다. 좁히면 «초상화 변경» 다섯 글자가 테두리에 붙는다. */
.pc-portrait-block .row > button {
  /* 42px → 21px. 테두리 2px과 글줄 15px을 빼면 위아래로 2px씩 남는다 */
  padding: 2px 12px;
  font-size: 12px;
  line-height: 1.25;
}

@media (min-width: 900px) {
  /* 사이드 패널을 세로 flex로 세워야 order가 듣는다. */
  .side-column .panel-body { display: flex; flex-direction: column; }
  .side-column .pc-portrait-block { order: -1; margin-top: 0; margin-bottom: 12px; }
}

/* ── 메인 인터페이스 (기획서 26장) ───────────────────── */

/*
 * 메인 화면은 뷰포트에 고정된 앱 셸이다.
 * flex: 1(= flex-basis 0%)은 height를 덮어쓰기 때문에 flex: none으로 두고
 * 높이를 직접 확정해야 한다. 그래야 로그·채팅이 각자 안에서 스크롤되고
 * 하단 액션바가 화면 밖으로 밀려나지 않는다.
 */
.main-screen {
  flex: none;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* 모바일: 상단 요약 고정 */
.char-bar {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 10px 12px;
  padding-top: calc(10px + env(safe-area-inset-top));
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line-soft);
  position: sticky;
  top: 0;
  z-index: 5;
}
.char-bar .char-meta { min-width: 0; flex: 1; }
.char-bar .char-name { font-weight: 600; }
.char-bar .char-name .honorific { color: var(--gold); font-weight: 400; font-size: 12px; margin-left: 4px; }
.char-bar .char-sub { font-size: 12px; color: var(--text-dim); }

.meter {
  height: 4px;
  background: var(--bg-sunken);
  border-radius: 2px;
  overflow: hidden;
  margin-top: 5px;
}
.meter > span { display: block; height: 100%; background: var(--plum); transition: width .3s; }
.meter.time > span { background: var(--gold-dim); }

/* ── 누적 수행 시간 막대 ────────────────────────────────
 *
 * 이 막대만은 숫자를 **위에 겹쳐** 얹는다. 막대 위쪽 한 줄은 탈것이 쓴다 —
 * 둘을 한 줄에 세우면 좁은 화면에서 탈것 설명이 잘리고, 잘린 줄이 두 겹으로
 * 접히면서 그 아래 채팅이 화면 밖으로 밀려났다.
 *
 * 글자를 얹으려면 두께가 있어야 하므로 4px에서 18px로 키운다. */
.meter.bank {
  position: relative;
  height: 18px;
  border-radius: 5px;
  /* 파인 홈처럼 보이게 한다 — 위에서 그림자가 지고 아래 모서리만 살짝 밝다.
   * 막대가 «칠해진 줄»이 아니라 «무언가 담기는 자리»로 읽혀야
   * 차오르는 것이 눈에 들어온다. */
  background: linear-gradient(180deg, #0b0912, #171425);
  box-shadow:
    inset 0 1px 2px rgb(0 0 0 / .7),
    inset 0 -1px 0 rgb(255 255 255 / .04);
}

/* 차오른 쪽. 위가 밝고 아래가 어두운 금빛이라 원통을 옆에서 본 것처럼 부풀어 오른다.
 * 오른쪽 끝의 밝은 선 한 줄이 «여기까지 찼다»는 물마루가 된다. */
.meter.bank > .meter-fill {
  border-radius: 5px 0 0 5px;
  background: linear-gradient(180deg, #e8c887 0%, #c9a558 45%, #8f7434 100%);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .35),
    inset 0 -1px 2px rgb(0 0 0 / .35),
    1px 0 4px rgb(216 180 106 / .45);
  position: relative;
}

/* ══ 클래스가 정하는 화려함 ═══════════════════════════════
 *
 * 클래스 하나에 칸 하나 — 열 칸이다. 전직할 때마다 막대가 달라지므로
 * «올라갔다»가 캐릭터 창을 열지 않아도 액션바에서 바로 읽힌다.
 *
 * <strong>색 → 빛 → 맥동 → 정지 무지개 → 흐름 → 광택 → 겹흐름 → 별가루 → 오로라</strong>
 * 순으로 하나씩 쌓는다. 낮은 칸부터 움직이면 높은 칸이 묻히므로 움직임은 늦게 붙고,
 * 한 번 붙은 것은 위 칸에서 사라지지 않는다.
 *
 * <strong>크기는 어느 칸에서도 18px 그대로다.</strong> 액션바가 두꺼워지면
 * 그만큼 채팅이 줄어들기 때문에, 화려함은 색과 빛으로만 낸다.
 *
 *    1클래스  금빛 원통 — 기본
 *    2        직업 테마색으로 물들고 테두리에 옅은 빛
 *    3        두 색 그라데이션 · 가득 차면 은은한 맥동
 *    4        세 색 · 위쪽 하이라이트 한 줄
 *    5        물마루 반짝임 + 바깥 광채
 *    6        무지개 — 아직 흐르지 않는다
 *    7        무지개가 흐르기 시작한다
 *    8        금속 광택이 주기적으로 스쳐 지나간다
 *    9        두 겹이 서로 다른 속도로 흘러 깊이가 생기고, 별가루가 흩날린다
 *   10        오로라 · 숨 쉬는 색 광채 · 물마루가 섬광이 된다
 */

/* ── 2 — 직업의 색으로 ── */
.meter.bank.tier-2 { box-shadow: inset 0 1px 2px rgb(0 0 0 / .7), 0 0 0 1px rgb(var(--job-rgb) / .35); }
.meter.bank.tier-2 > .meter-fill {
  background: linear-gradient(180deg,
    rgb(var(--job-rgb) / .95) 0%, rgb(var(--job-rgb)) 45%, rgb(var(--job-rgb) / .55) 100%);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .35),
    inset 0 -1px 2px rgb(0 0 0 / .35),
    1px 0 5px rgb(var(--job-rgb) / .55);
}

/* ── 3 — 두 색, 그리고 가득 찼을 때의 숨결 ── */
.meter.bank.tier-3 { box-shadow: inset 0 1px 2px rgb(0 0 0 / .7), 0 0 0 1px rgb(var(--job-rgb) / .45); }
.meter.bank.tier-3 > .meter-fill {
  background: linear-gradient(100deg, rgb(var(--job-rgb)) 0%, #e8c887 60%, rgb(var(--job-rgb)) 100%);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .4),
    inset 0 -1px 2px rgb(0 0 0 / .35),
    1px 0 6px rgb(var(--job-rgb) / .6);
}
/* 가득 찬 것은 «더 기다려도 늘지 않는다»는 뜻이라 그 자체로 알릴 값이 있다. */
.meter.bank.tier-3.full > .meter-fill,
.meter.bank.tier-4.full > .meter-fill,
.meter.bank.tier-5.full > .meter-fill { animation: bank-breathe 3s ease-in-out infinite; }

/* ── 4 — 세 색과 위쪽 하이라이트 ── */
.meter.bank.tier-4 { box-shadow: inset 0 1px 2px rgb(0 0 0 / .7), 0 0 0 1px rgb(var(--job-rgb) / .5); }
.meter.bank.tier-4 > .meter-fill {
  background: linear-gradient(100deg,
    rgb(var(--job-rgb)), #e8c887 45%, #fff4c8 70%, rgb(var(--job-rgb)));
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .45),
    inset 0 -1px 2px rgb(0 0 0 / .35),
    1px 0 7px rgb(var(--job-rgb) / .6);
}

/* 위쪽 한 줄만 밝게 — 원통에 빛이 닿은 자리다. 4칸부터 끝까지 남는다. */
.meter.bank.tier-4 > .meter-fill::before,
.meter.bank.tier-5 > .meter-fill::before,
.meter.bank.tier-6 > .meter-fill::before,
.meter.bank.tier-7 > .meter-fill::before,
.meter.bank.tier-8 > .meter-fill::before,
.meter.bank.tier-9 > .meter-fill::before,
.meter.bank.tier-10 > .meter-fill::before {
  content: '';
  position: absolute;
  inset: 1px 1px auto 1px;
  height: 5px;
  border-radius: 4px 0 0 0;
  background: linear-gradient(180deg, rgb(255 255 255 / .38), transparent);
  pointer-events: none;
  z-index: 1;
}

/* ── 5 — 물마루가 반짝이고 막대 바깥으로 빛이 샌다 ── */
.meter.bank.tier-5 { box-shadow: inset 0 1px 2px rgb(0 0 0 / .7), 0 0 0 1px rgb(var(--job-rgb) / .55), 0 0 10px -2px rgb(var(--job-rgb) / .5); }
.meter.bank.tier-5 > .meter-fill {
  background: linear-gradient(100deg,
    rgb(var(--job-rgb)), #7fd8ff 40%, #fff4c8 65%, #e8c887 85%, rgb(var(--job-rgb)));
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .5),
    inset 0 -1px 2px rgb(0 0 0 / .35),
    2px 0 10px rgb(var(--job-rgb) / .75);
}

/* 차오른 끝의 물마루. 채워진 자리와 빈 자리의 경계에서 빛이 오르내린다.
 * 5칸부터 끝까지 남되, 위 칸에서 점점 밝고 넓어진다. */
.meter.bank.tier-5 > .meter-fill::after,
.meter.bank.tier-6 > .meter-fill::after,
.meter.bank.tier-7 > .meter-fill::after,
.meter.bank.tier-8 > .meter-fill::after,
.meter.bank.tier-9 > .meter-fill::after,
.meter.bank.tier-10 > .meter-fill::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1px;
  width: 3px;
  border-radius: 2px;
  background: rgb(255 255 255 / .85);
  box-shadow: 0 0 8px 2px rgb(255 255 255 / .55);
  animation: bank-crest 1.8s ease-in-out infinite;
  pointer-events: none;
  z-index: 2;
}

/* ══ 6~10 — 무지개 계열 ══════════════════════════════════
 *
 * 여섯 칸에서 무지개가 <strong>서지만 아직 흐르지 않는다.</strong>
 * 멈춘 무지개도 그것대로 좋은 물건이고, 무엇보다 «흐름»을 일곱 칸에 남겨두면
 * 전직 한 번에 눈에 띄는 변화가 하나씩 더 생긴다. */
.meter.bank.tier-6,
.meter.bank.tier-7,
.meter.bank.tier-8,
.meter.bank.tier-9,
.meter.bank.tier-10 {
  box-shadow:
    inset 0 1px 2px rgb(0 0 0 / .7),
    0 0 0 1px rgb(255 255 255 / .3),
    0 0 14px -2px rgb(255 255 255 / .45);
}
.meter.bank.tier-6 > .meter-fill,
.meter.bank.tier-7 > .meter-fill,
.meter.bank.tier-8 > .meter-fill,
.meter.bank.tier-9 > .meter-fill,
.meter.bank.tier-10 > .meter-fill {
  background-image: linear-gradient(100deg,
    #ff5f6d, #ffa751, #ffe259, #55d98d, #3fa9f5, #a970d8, #ff5f6d);
  background-size: 300% 100%;
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .55),
    inset 0 -1px 2px rgb(0 0 0 / .3),
    2px 0 12px rgb(255 255 255 / .5);
}

/* ── 6 — 멈춘 무지개 ──
 * 스펙트럼 한가운데를 세워둔다. 끝자락을 두면 붉은 쪽이나 보랏빛 쪽으로 치우쳐
 * «무지개»가 아니라 «색깔 막대»로 읽힌다. */
.meter.bank.tier-6 > .meter-fill { background-position: 50% 50%; }

/* ── 7 — 무지개가 흐른다 ── */
.meter.bank.tier-7 > .meter-fill { animation: bank-flow 7s linear infinite; }

/* ── 8 — 금속 광택이 스쳐 지나간다 ──
 * 흐름 위에 밝은 띠 하나가 주기적으로 지나간다. 늘 빛나는 것이 아니라
 * <strong>이따금</strong> 번쩍이는 편이 금속처럼 읽힌다 — 그래서 띠는 좁고 주기는 길다. */
.meter.bank.tier-8 > .meter-fill,
.meter.bank.tier-9 > .meter-fill,
.meter.bank.tier-10 > .meter-fill {
  background-image:
    linear-gradient(105deg,
      transparent 42%, rgb(255 255 255 / .62) 50%, transparent 58%),
    linear-gradient(100deg,
      #ff5f6d, #ffa751, #ffe259, #55d98d, #3fa9f5, #a970d8, #ff5f6d);
  background-size: 220% 100%, 300% 100%;
  animation: bank-sheen 7s linear infinite;
}

/* ── 9 — 두 겹이 서로 다른 속도로, 그리고 별가루 ──
 * 앞뒤가 따로 흐르면 평면이 아니라 <strong>깊이</strong>가 생긴다.
 * 배경 세 겹의 위치를 한 키프레임에서 각자 다른 속도로 민다. */
.meter.bank.tier-9 > .meter-fill,
.meter.bank.tier-10 > .meter-fill {
  background-image:
    radial-gradient(circle at 12% 34%, rgb(255 255 255 / .85) 0 1px, transparent 1.6px),
    linear-gradient(105deg,
      transparent 42%, rgb(255 255 255 / .7) 50%, transparent 58%),
    linear-gradient(100deg,
      #ff5f6d, #ffa751, #ffe259, #55d98d, #3fa9f5, #a970d8, #ff5f6d);
  background-size: 34px 100%, 220% 100%, 300% 100%;
  animation: bank-layers 7s linear infinite;
}
.meter.bank.tier-9 { box-shadow: inset 0 1px 2px rgb(0 0 0 / .7), 0 0 0 1px rgb(255 255 255 / .38), 0 0 18px -2px rgb(255 255 255 / .55); }
.meter.bank.tier-9 > .meter-fill::after { width: 4px; box-shadow: 0 0 11px 3px rgb(255 255 255 / .7); }

/* ── 10 — 오로라, 숨 쉬는 광채, 섬광이 된 물마루 ──
 * 여기까지 온 사람은 서버에 몇 없다. 바깥 광채가 색을 띠고 스스로 숨 쉬며,
 * 물마루는 선이 아니라 <strong>터지는 빛</strong>이 된다. */
.meter.bank.tier-10 { animation: bank-halo 4.5s ease-in-out infinite; }
.meter.bank.tier-10 > .meter-fill { animation: bank-layers 5.5s linear infinite; }
.meter.bank.tier-10 > .meter-fill::after {
  width: 6px;
  right: -2px;
  background: rgb(255 255 255 / .95);
  box-shadow:
    0 0 14px 5px rgb(255 255 255 / .75),
    0 0 26px 10px rgb(160 200 255 / .45);
  animation: bank-flare 1.6s ease-in-out infinite;
}

@keyframes bank-flow {
  from { background-position: 0% 50%; }
  to   { background-position: 300% 50%; }
}
/* 광택은 빠르게, 무지개는 느리게 — 두 배경이 한 키프레임 안에서 따로 움직인다 */
@keyframes bank-sheen {
  from { background-position: -60% 50%, 0% 50%; }
  to   { background-position: 220% 50%, 300% 50%; }
}
/* 별가루 · 광택 · 무지개 세 겹이 각자의 속도로 */
@keyframes bank-layers {
  from { background-position: 0 0, -60% 50%, 0% 50%; }
  to   { background-position: -136px 0, 220% 50%, 300% 50%; }
}
@keyframes bank-breathe {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.22); }
}
@keyframes bank-crest {
  0%, 100% { opacity: .45; }
  50%      { opacity: 1; }
}
@keyframes bank-flare {
  0%, 100% { opacity: .55; transform: scaleY(.75); }
  50%      { opacity: 1;   transform: scaleY(1); }
}
@keyframes bank-halo {
  0%, 100% {
    box-shadow: inset 0 1px 2px rgb(0 0 0 / .7),
      0 0 0 1px rgb(255 255 255 / .4), 0 0 16px -2px rgb(140 190 255 / .5);
  }
  50% {
    box-shadow: inset 0 1px 2px rgb(0 0 0 / .7),
      0 0 0 1px rgb(255 255 255 / .6), 0 0 26px 0 rgb(200 160 255 / .7);
  }
}

/**
 * 밝아진 칸에서는 글자 그림자를 한 겹 더 받친다.
 *
 * 4칸부터 바탕에 <strong>흰빛에 가까운 색</strong>이 섞이는데(#fff4c8 · #7fd8ff),
 * 흰 글자가 그 위를 지나면 그대로 묻힌다. 화려해질수록 먼저 깨지는 것이
 * «읽힌다»라서, 단계가 오를 때마다 대비를 함께 올린다.
 */
.meter.bank.tier-4 > .bank-text,
.meter.bank.tier-5 > .bank-text,
.meter.bank.tier-6 > .bank-text,
.meter.bank.tier-7 > .bank-text,
.meter.bank.tier-8 > .bank-text,
.meter.bank.tier-9 > .bank-text,
.meter.bank.tier-10 > .bank-text {
  text-shadow:
    0 1px 2px rgb(0 0 0 / .9),
    0 0 5px rgb(0 0 0 / .75),
    0 0 10px rgb(0 0 0 / .5);
}

/* 움직임을 원치 않는 사람에게는 색만 남긴다 — 화려함의 요점은 색이지 흔들림이 아니다.
 * 여러 겹을 쓰는 칸은 배경 위치를 보기 좋은 자리에 세워둔다. */
@media (prefers-reduced-motion: reduce) {
  .meter.bank,
  .meter.bank > .meter-fill,
  .meter.bank > .meter-fill::after { animation: none; }
  .meter.bank.tier-7 > .meter-fill { background-position: 50% 50%; }
  .meter.bank.tier-8 > .meter-fill { background-position: 130% 50%, 50% 50%; }
  .meter.bank.tier-9 > .meter-fill,
  .meter.bank.tier-10 > .meter-fill { background-position: 0 0, 130% 50%, 50% 50%; }
}

/* ── 관리자 열람 — 여섯 칸을 나란히 ────────────────────
 * 게임 안에서는 제 클래스의 것 하나만 볼 수 있다. 올려놓기 전에 견줘 보는 자리다. */
.bank-samples { display: flex; flex-direction: column; gap: 14px; }
.bank-sample { display: flex; flex-direction: column; gap: 5px; }
.bank-sample-head { font-size: 12px; color: var(--text-soft); }
/* .meter > span 을 덮어써야 한다 — 그대로 두면 글자줄이 자두색 막대로 칠해진다. */
.meter.bank > .bank-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: auto;
  background: none;
  font-size: 11px;
  line-height: 1;
  /* 글자가 채워진 쪽과 빈 쪽에 걸쳐 앉는다. 어느 한쪽에 맞춘 색을 쓰면
   * 막대가 자라며 반대쪽에서 읽히지 않게 되므로, 두 바닥 모두에서 뜨는
   * 밝은 색에 옅은 그림자를 받쳐 어디에 걸치든 읽히게 한다. */
  color: var(--text);
  font-weight: 600;
  letter-spacing: .2px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .7);
  font-variant-numeric: tabular-nums;
  /* 막대가 글자 밑에서 자라므로, 글자가 그 움직임을 가로채면 안 된다. */
  pointer-events: none;
  white-space: nowrap;
}

/* 로그 */
.log-pane {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 0;
}
.log-line { font-size: 14px; word-break: break-word; }
.log-line.info { color: var(--text-dim); }
.log-line.hit { color: var(--text); }
.log-line.crit { color: var(--crit); font-weight: 700; letter-spacing: .08em; }
.log-line.critLine { color: var(--crit); font-style: italic; }
.log-line.miss { color: var(--heal); }
.log-line.taken { color: var(--damage); }
.log-line.win { color: var(--gold); font-weight: 600; }
.log-line.lose { color: var(--text-faint); }
.log-line.loot { color: var(--loot); }
.log-line.levelup { color: var(--heal); font-weight: 600; }
.log-sep {
  border-top: 1px dashed var(--line-soft);
  margin: 8px 0 5px;
  padding-top: 6px;
  font-size: 11px;
  color: var(--text-faint);
  letter-spacing: .04em;
}

/* ── 실행 묶음 가르개 ───────────────────────────────────
 *
 * 몬스터마다 붙는 옅은 점선(log-sep)만으로는 어디까지가 이번에 누른 몫인지
 * 알 수 없다 — 벼락치기로 열 판이 한꺼번에 쏟아지면 특히 그렇다.
 * 실선 두 겹으로 한 단계 굵게 그어, 위로 스크롤할 때 묶음이 눈에 잡히게 한다. */
.log-run {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 14px 0 8px;
  padding: 5px 0;
  border-top: 2px solid var(--job-line);
  border-bottom: 1px solid var(--job-line-soft);
  font-size: 11px;
}
.log-run-label { color: var(--job); font-weight: 700; letter-spacing: .06em; }
.log-run-at { color: var(--text-faint); font-family: var(--mono); }

/* 로그 맨 위의 가르개는 위 여백이 필요 없다 */
.log-pane > .log-run:first-child { margin-top: 0; }

/* 쓰러졌을 때의 알림.
 *
 * 위아래를 끊어 **전투 로그의 흐름 밖에** 세운다 — 한 줄로 끼면 몸이 멈췄다는 사실이
 * «누가 몇 대 때렸다»와 같은 무게로 지나간다. 색은 위험을 알리는 붉은 기를 쓰되
 * 배경을 아주 옅게만 깔아, 경고이면서도 걱정하는 말투에 어울리게 둔다. */
/**
 * 구분선으로 끊어 세우는 기능 알림들 — 쓰러짐 · 금융치료, 그리고 앞으로 붙을 것들.
 *
 * <strong>가운데로 세운다.</strong> 위아래를 선으로 끊은 덩어리는 전투 로그의 흐름 밖에
 * 서 있는 것인데, 글이 왼쪽에 붙어 있으면 그 안에서도 «로그 한 줄»처럼 읽혀
 * 애써 끊어놓은 뜻이 옅어진다. 가운데에 서야 판이 바뀌었다는 것이 보인다.
 */
.log-knockout,
.log-heal {
  text-align: center;
}

.log-knockout {
  margin: 14px 0;
  padding: 10px 12px;
  border-top: 2px solid rgb(226 104 95 / .55);
  border-bottom: 2px solid rgb(226 104 95 / .55);
  background: rgb(226 104 95 / .07);
  color: var(--damage);
  font-size: 13px;
  line-height: 1.7;
  /* 한국어는 단어 중간이 아니라 어절에서 끊어야 읽힌다 */
  word-break: keep-all;
  overflow-wrap: break-word;
}
.log-knockout p { margin: 0; }
/* 둘째 줄은 «그래서 이렇게 했다»는 조치라 한 톤 눌러 곁들여 읽히게 한다. */
.log-knockout p + p { margin-top: 2px; color: var(--text-dim); }

/* ── 금융치료 ────────────────────────────────────────────
 *
 * 쓰러짐 알림과 **같은 모양**을 쓴다 — 둘 다 생명력이 크게 움직인 자리라,
 * 로그를 훑다가 눈이 걸려야 하는 것도 같다.
 *
 * 색만 반대편에 둔다. 저쪽이 다쳐서 붉다면 이쪽은 값을 치르고 받은 은총이라
 * 금빛이다. 돈과 성스러움이 같은 색을 쓰는 것이 이 가게의 농담이기도 하다. */
.log-heal {
  margin: 14px 0;
  padding: 10px 12px;
  border-top: 2px solid rgb(216 180 106 / .55);
  border-bottom: 2px solid rgb(216 180 106 / .55);
  background: rgb(216 180 106 / .09);
  color: var(--gold);
  font-size: 13px;
  line-height: 1.7;
  word-break: keep-all;
  overflow-wrap: break-word;
}
/* 수치 둘만 뜻이 갈린다 — 받은 것은 초록, 치른 것은 붉은색.
 * 문장을 읽지 않아도 «얼마 받고 얼마 냈는지»가 색으로 먼저 들어온다. */
.log-heal .healed { color: var(--heal); font-weight: 700; }
.log-heal .paid { color: var(--danger); font-weight: 700; }

/* 하단 액션 바 */
.action-bar {
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  background: var(--bg-raised);
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: sticky;
  bottom: 0;
}
.battle-button { width: 100%; padding: 14px; font-size: 15px; }

/* 한 판과 몰아치기 — 같은 일의 두 가지 크기라 한 줄에 나란히, 같은 폭으로 세운다.
 * 둘 다 "수행한다"는 한 가지 동작이므로 크기로 서열을 매기지 않는다.
 * 어느 쪽을 누를지는 남은 시간이 정하지, 버튼 크기가 정할 일이 아니다.
 *
 * ── 왜 가게 버튼과 같은 크기인가 ──
 * 예전에는 이 둘이 더 컸다. 가장 자주 누르는 버튼이라 그럴 만했지만,
 * 액션바는 **화면 아래를 고정으로 차지하므로** 여기서 부풀린 만큼이
 * 그대로 채팅이 보이는 높이에서 빠진다. 모바일에서는 그 몇십 px이
 * 채팅 두세 줄이었다. 자주 누르는 것은 자리로 알리지 않아도 알고 있다.
 *
 * 높이를 맞추는 방법이 글자 크기와 여백을 맞추는 것인 이유: 고정 height를 주면
 * 라벨이 두 줄로 접히는 순간(좁은 화면에서 늘 그렇다) 글자가 버튼 밖으로 새어 나간다. */
/* .action-grid > button 이 아래쪽에서 padding: 10px 8px 을 걸고 있다. 같은 특이도라
 * 순서로 지므로, 클래스를 하나 더 붙여 이 줄의 여백을 이기게 한다. */
.action-grid.battle-row > button {
  padding: 8px 10px;
  font-size: 12px;
  /* 두 줄 라벨이라 가운데로 모은다. 넘치는 글자는 줄을 바꾸되,
   * 이름 안에서는 끊기지 않게 한국어 규칙을 따른다. */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  word-break: keep-all;
  line-height: 1.3;
}
.action-grid.battle-row > .battle-button { width: auto; padding: 8px 10px; font-size: 12px; }
.battle-row .store-name { font-size: 12px; font-weight: 700; }
.battle-row .store-note { font-size: 11px; }

/* 개인 저택은 혼자 한 줄을 쓴다. 우편이 와 있을 때 눈에 걸려야 하는 자리다. */
/* 개인 저택은 혼자 한 줄을 쓴다. 우편이 와 있을 때 눈에 걸려야 하는 자리다.
 *
 * 세로는 바로 아래 «나가는 길» 넷과 같게 맞춘다 — 위아래로 붙어 있어서
 * 글자 크기는 아래 .manor-button 한 군데에서만 정한다 — 규칙이 두 군데 있으면
 * 한쪽만 고쳤을 때 조용히 어긋난다. */

/* 나가는 길 넷 — 상점가 · 전당 · 설정 · 로그아웃.
 *
 * 글자 수가 제각각이라 폭을 똑같이 나누면 긴 이름이 깨진다.
 * 각자 제 글자만큼 갖고 남는 폭만 비례해 나눠 갖되, **모자라면 줄을 바꾼다** —
 * 좁은 화면에서 넷을 한 줄에 밀어 넣으면 마지막 버튼이 화면 밖으로 나간다. */
.exit-row { flex-wrap: wrap; }
.exit-row > button {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 8px;
  font-size: 13px;
  word-break: keep-all;
}
.chat-form { display: flex; gap: 8px; }
.chat-form input { flex: 1; }

/* 탭 (모바일) */
.tabs { display: flex; border-bottom: 1px solid var(--line-soft); background: var(--bg-raised); }
.tab {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 10px 4px;
  font-size: 13px;
  color: var(--text-faint);
}
.tab.active { color: var(--gold); border-bottom-color: var(--gold); }
.tab .badge {
  display: inline-block;
  min-width: 16px;
  padding: 0 4px;
  margin-left: 4px;
  border-radius: 8px;
  background: var(--plum);
  color: #fff;
  font-size: 10px;
  line-height: 16px;
}

/* 채팅 */
.chat-list { display: flex; flex-direction: column; gap: 5px; }
.chat-line { font-size: 14px; word-break: break-word; }
.chat-line .who { color: var(--gold); font-weight: 600; }
.chat-line .at { color: var(--text-faint); font-size: 11px; font-family: var(--mono); margin-right: 5px; }
.chat-line.system { color: var(--plum); }

/* 접속자 / 인벤토리 */
.list-item {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg-sunken);
  margin-bottom: 5px;
  font-size: 13px;
}
.list-item .sub { color: var(--text-faint); font-size: 12px; }
.empty { color: var(--text-faint); font-size: 13px; text-align: center; padding: 24px 0; }

/* ── 수집품 — 눌러서 펼친다 ─────────────────────────────
 *
 * 예전에는 설명이 브라우저 기본 말풍선(title)에 숨어 있어서
 * <strong>손가락으로 만지는 화면에서는 볼 방법이 없었다.</strong>
 * 줄 자체를 버튼으로 만들어, 눌러서 그 자리에 펼친다. */
button.inv-head {
  width: 100%;
  align-items: baseline;
  border: 1px solid transparent;
  color: inherit;
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
button.inv-head:hover { border-color: var(--line); }
button.inv-head.open {
  border-color: var(--line);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  margin-bottom: 0;
}
.inv-caret {
  display: inline-block;
  margin-left: 6px;
  font-size: 9px;
  transition: transform .15s ease;
}
button.inv-head.open .inv-caret { transform: rotate(180deg); }

/* 펼쳐진 속. 머리줄과 한 덩어리로 보이도록 위쪽 모서리를 맞물린다. */
.inv-detail {
  margin: -1px 0 5px;
  padding: 9px 11px 10px;
  border: 1px solid var(--line);
  border-top: none;
  border-radius: 0 0 8px 8px;
  background: rgb(255 255 255 / .02);
}
.inv-desc {
  margin: 0 0 7px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-dim);
  word-break: keep-all;
}
.inv-facts { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12px; }
.inv-key { color: var(--text-faint); }
/* 로그의 전리품 팝업과 같은 말, 같은 색 — 자리가 달라도 같은 것을 가리킨다 */
.inv-uses { color: var(--gold); font-weight: 600; font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .inv-caret { transition: none; }
}

.narrative {
  white-space: pre-wrap;
  line-height: 1.9;
  color: var(--text-dim);
  font-size: 14px;
}

.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 13px; }
.kv dt { color: var(--text-faint); }
.kv dd { margin: 0; text-align: right; font-family: var(--mono); }

/* «접속 중» 두 글자가 «접속 / 중»으로 접히면 그 줄이 두 겹이 되고,
 * 두 겹이 된 만큼 아래 채팅이 화면 밖으로 밀린다. 좁아도 한 줄로 둔다. */
.conn-status { font-size: 11px; color: var(--text-faint); white-space: nowrap; flex: 0 0 auto; }
.conn-status.online::before { content: '● '; color: var(--heal); }
.conn-status.offline::before { content: '● '; color: var(--danger); }

/* ── 레이아웃: 모바일 (탭 전환) ──────────────────────── */

.columns { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.side-column, .main-column { display: contents; }

/* 모바일에서는 활성 패널 하나만 보인다. */
.panel { display: none; flex: 1; min-height: 0; flex-direction: column; }
.panel.active { display: flex; }
.panel > h3 { display: none; }
.panel > .panel-body { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 12px; min-height: 0; }
.panel > .panel-body.log-pane { padding: 12px; }

/* ── 레이아웃: PC 확장 (기획서 2장 / 27장) ───────────── */

@media (min-width: 900px) {
  .main-screen {
    max-width: 1280px;
    width: 100%;
    margin: 0 auto;
    padding: 16px;
    height: 100dvh;
    gap: 12px;
  }

  .char-bar { display: none; }   /* 좌측 컬럼이 대신한다 */
  /* 메인 화면은 PC에서 모든 패널을 2컬럼으로 동시에 펼치므로 탭이 필요 없다.
   * 다만 **메인 화면의 탭만** 숨긴다 — 개인 저택·상점·설정은 PC에서도 한 번에 한 판씩
   * 보여주는 구조라 탭이 유일한 통로다. 여기서 함께 숨기면 첫 탭에 갇힌다. */
  .main-screen > .tabs { display: none; }

  .columns {
    display: grid;
    grid-template-columns: 320px 1fr;
    /* 행 높이를 명시하지 않으면 auto가 되어 내용만큼 늘어나고,
       채팅 패널과 액션바가 화면 밖으로 밀려난다. */
    grid-template-rows: minmax(0, 1fr);
    gap: 14px;
    min-height: 0;
  }
  .side-column, .main-column {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-height: 0;
  }

  /* 좌측: 캐릭터(고정 높이 없음) + 접속자 + 수집품 */
  .side-column { overflow-y: auto; }
  .side-column > .panel { flex: 0 0 auto; max-height: none; }
  .side-column > .panel > .panel-body { overflow: visible; }

  /* ── 우측 컬럼의 높이 나눠 갖기 ──────────────────────
   *
   * 셋이 한 기둥을 나눠 쓴다. 기둥의 높이는 화면이 정하므로 **총합은 늘 같고**,
   * 접어서 남는 자리는 어딘가가 가져가야 한다. 그 자리를 채팅에 준다 —
   * 접견일지와 착용 장비는 «필요할 때 보는 것»이고 채팅은 «계속 흘러가는 것»이라,
   * 남는 높이가 실제로 쓸모 있는 쪽은 채팅이다.
   *
   * 접힌 판은 제목줄만 남기고 자리를 내놓는다(flex: 0 0 auto).
   * 둘 다 접으면 채팅이 기둥을 통째로 쓴다. 폭은 어느 경우에도 변하지 않는다. */
  .main-column > .panel[data-pane='battle'] { flex: 1 1 auto; }
  .main-column > .panel[data-pane='battle'].collapsed { flex: 0 0 auto; }
  /* 채팅이 늘어나는 동안 접견일지는 **늘지도 줄지도 않는다.**
   * 바탕값은 접는 순간 잰 픽셀을 JS가 넣어준다(refreshMainFlex) —
   * 접견일지의 자연 높이는 «로그 전부»라 늘 기둥보다 크고, 그대로 두면
   * 자리를 다시 나눌 때마다 남는 것을 다 내놓는 쪽이 되어 함께 눌린다. */
  .main-column.chat-grows > .panel[data-pane='battle'] { flex: 0 0 auto; }
  /* 착용 장비는 여섯 칸이 전부라 높이가 정해져 있다. 남는 폭을 나눠 갖게 두면
   * basis 0이 되어 18px로 눌려 제목만 남는다 — 내용만큼만 차지하게 못 박는다. */
  /* 줄어들지 않게 못 박는다. 말풍선 때문에 overflow를 열어두었으므로,
   * 줄어들면 잘리는 대신 **내용이 아래 패널 위로 새어 나온다.**
   * 칸을 촘촘하게 만들어 205px이면 되므로 고정해도 부담이 적다. */
  .main-column > .panel[data-pane='gear'] { flex: 0 0 auto; }
  /* 장비 말풍선은 칸 위로 떠오르므로 패널 밖으로 나간다.
   * 여기서 잘라내면 말풍선이 통째로 사라진다 — 좌측 컬럼과 같은 방식으로 열어둔다. */
  .main-column > .panel[data-pane='gear'],
  .main-column > .panel[data-pane='gear'] > .panel-body { overflow: visible; }
  /**
   * 셋이 다 펼쳐져 있을 때 채팅은 260px에 못 박힌다 — 지금 모습 그대로다.
   * 남는 높이는 접견일지가 가져간다.
   *
   * **위의 둘 중 하나라도 접히면** 그 자리를 채팅이 가져간다(chat-grows).
   * 채팅에도 grow를 상시로 열어두면 셋이 다 펼쳐진 평소에 접견일지와 자리를
   * 나눠 갖게 되어, 접지도 않았는데 기본 모습이 달라진다.
   */
  .main-column > .panel[data-pane='chat'] { flex: 0 0 260px; }
  /* 바탕값은 260px 그대로 두고 grow만 연다. auto로 바꾸면 채팅 내용의
   * 원래 높이가 바탕이 되어, 줄이 많이 쌓인 날에는 접견일지를 밀어내며 자란다 —
   * 접어서 «생긴» 자리만 가져가야 하므로 바탕은 움직이지 않는다. */
  .main-column.chat-grows > .panel[data-pane='chat'] { flex: 1 1 260px; }

  /* 탭 상태와 무관하게 모두 표시한다 */
  .panel { display: flex !important; }
  .panel > h3 {
    display: block;
    margin: 0;
    padding: 10px 14px;
    font-size: 12px;
    letter-spacing: .06em;
    color: var(--gold);
    border-bottom: 1px solid var(--line-soft);
    font-weight: 600;
  }

  /* 접기는 PC에서만 뜻이 있다 — 모바일은 탭으로 한 판씩 보여주므로
   * 제목줄 자체가 없고, 거기서 접히면 빈 탭만 남는다. */
  .panel.collapsed > .panel-body { display: none; }
  .panel.collapsed > h3 { border-bottom: none; }

  /* 제목줄 전체가 누르는 자리다. 글자만 누르게 하면 과녁이 너무 작다. */
  .panel > h3 > .panel-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    margin: -10px -14px;
    padding: 10px 14px;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-align: left;
    cursor: pointer;
  }
  .panel > h3 > .panel-toggle:hover { color: var(--gold-dim); }

  /* ── 접기 손잡이 ─────────────────────────────────────
   *
   * 예전에는 10px짜리 홑화살표였다. **접을 수 있다는 사실 자체가 보이지 않아서**
   * 눌러본 사람만 알았다. 테두리를 두른 네모로 키워 «누르는 것»으로 읽히게 한다.
   *
   * 제목줄 전체가 여전히 과녁이다 — 이 네모는 그 사실을 알리는 표지이지
   * 여기만 눌러야 하는 자리가 아니다. */
  .panel-chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    border-radius: 6px;
    border: 1px solid var(--line-strong);
    background: var(--bg-sunken);
    color: var(--gold-dim);
    font-size: 12px;
    line-height: 1;
    transition: transform .15s ease, border-color .15s ease, color .15s ease;
  }
  .panel > h3 > .panel-toggle:hover .panel-chevron {
    border-color: var(--gold-dim);
    color: var(--gold);
  }
  .panel.collapsed .panel-chevron { transform: rotate(-90deg); }

  @media (prefers-reduced-motion: reduce) {
    .panel-chevron { transition: none; }
  }

  .panel {
    background: var(--bg-raised);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    overflow: hidden;
  }

  .side-column .portrait { max-width: 240px; margin: 0 auto; }
  .action-bar { position: static; border: none; padding: 0; background: none; }
}

@media (max-width: 899px) {
  .desktop-only { display: none !important; }
}

/* ── 관리자 페이지 ───────────────────────────────────── */

.admin-page { max-width: 640px; margin: 0 auto; padding: 24px 16px; width: 100%; }
.admin-page h1 { font-size: 18px; color: var(--gold); margin: 0 0 4px; }

/* 제목과 나가는 문을 한 줄에. 좁은 화면에서는 문이 아래로 내려간다 */
.admin-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 4px;
}
.admin-head > h1 { margin: 0; }
.admin-head > .primary { flex: 0 0 auto; padding: 8px 14px; font-size: 13px; }
.admin-result {
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  font-size: 13px;
  margin-top: 12px;
}
.admin-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

/* ── 개인 저택 / 상점 ─────────────────────────────────────── */

.manor-screen {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
  min-height: 0;
}

.manor-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  padding-top: calc(12px + env(safe-area-inset-top));
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line-soft);
}
.manor-title { font-size: 18px; color: var(--gold); margin: 0; letter-spacing: .06em; }

.manor-tabs { flex: 0 0 auto; }
.manor-tabs .tab { flex-direction: column; gap: 2px; }
.tab-count {
  display: block;
  font-size: 10px;
  color: var(--text-faint);
  font-family: var(--mono);
}

.manor-body {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 14px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
  min-height: 0;
}

.storage-toolbar {
  display: flex;
  white-space: nowrap;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  position: sticky;
  top: 0;
  background: var(--bg);
  z-index: 2;
}

.storage-list { display: flex; flex-direction: column; gap: 5px; }

.storage-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-radius: 8px;
  background: var(--bg-sunken);
  font-size: 14px;
  cursor: pointer;
}
.storage-row input[type='checkbox'] { width: auto; flex: 0 0 auto; accent-color: var(--plum); }
.storage-row .sub { color: var(--text-faint); font-size: 12px; }

.sell-qty {
  width: 82px;
  flex: 0 0 auto;
  padding: 5px 8px;
  font-family: var(--mono);
  font-size: 13px;
  text-align: right;
}

.storage-card {
  padding: 11px 12px;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
}
.storage-card.equipped { border-color: var(--gold-dim); background: rgba(216, 180, 106, .07); }
.storage-card .sub { color: var(--text-faint); font-size: 12px; }

.tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 8px;
  background: var(--plum);
  color: #fff;
  font-size: 10px;
  vertical-align: middle;
}
.enhance { color: var(--crit); font-weight: 700; }

button.danger { border-color: rgba(226, 104, 95, .5); color: var(--danger); }
button.danger:hover:not(:disabled) { border-color: var(--danger); }
button.tiny { padding: 5px 10px; font-size: 12px; }

.gold { color: var(--gold); }
.shards { color: var(--loot); }

/* 우편 */
.mail {
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  padding: 10px 12px;
}
.mail.unread { border-color: var(--plum); }
.mail > summary { cursor: pointer; list-style: none; }
.mail > summary::-webkit-details-marker { display: none; }
.mail > summary::before { content: '✉ '; color: var(--gold-dim); }
.mail-subject { font-weight: 600; }
.mail .sub { color: var(--text-faint); font-size: 12px; }
.mail-body {
  white-space: pre-wrap;
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1.8;
  margin: 10px 0;
  padding-top: 10px;
  border-top: 1px dashed var(--line-soft);
}
.mail-attachments { margin-top: 8px; }

/* 상점 */
.shop-heading { font-size: 13px; color: var(--gold); margin: 0 0 4px; letter-spacing: .04em; }
.shop-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
@media (min-width: 640px) { .shop-grid { grid-template-columns: repeat(2, 1fr); } }

.shop-card {
  padding: 12px;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
}
.shop-card .sub { color: var(--text-faint); font-size: 12px; line-height: 1.5; }

/* 좁은 화면에서는 매각 행을 두 줄로 나눠 아이템 이름이 잘리지 않게 한다. */
@media (max-width: 520px) {
  .storage-row.sell { flex-wrap: wrap; row-gap: 6px; }
  .storage-row.sell > .grow { flex: 1 0 100%; }
  .storage-row.sell .sell-qty { margin-left: auto; }
}

/* 종족 소분류 — 계열 아래에 한 단계 들여 보여주고, 담는 정보가 많아 한 줄에 하나씩 편다 */
.subrace-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  padding-left: 10px;
  border-left: 2px solid var(--line);
}
.subrace-grid .choice-note + .choice-note { margin-top: 4px; }

/* 생명력 — 전투 사이에 이어지므로 항상 보이게 둔다 */
.meter.hp > span { background: var(--heal); }
.meter.hp.low > span { background: var(--danger); }
.hp-text { color: var(--text-faint); }
.hp-low { color: var(--danger); }

.heal-button { flex: 1; }
.heal-cost { color: var(--gold); font-family: var(--mono); font-size: 12px; }
.heal-cost.short { color: var(--danger); }

/* 회복 아이템 표시 */
.heal-tag { color: var(--heal); font-size: 11px; font-family: var(--mono); }

/* ── 수집품 희귀도 ───────────────────────────────────── */
/*
 * 로그가 길어질수록 흔한 재료에 묻혀 귀한 것을 놓치기 쉽다.
 * 이름과 개수를 굵게, 등급을 색으로 구분해 눈에 먼저 들어오게 한다.
 */
.rarity { font-weight: 700; }
.rarity-common    { color: #b9b7c9; }
.rarity-uncommon  { color: #63c9a8; }
.rarity-rare      { color: #6f9ffb; }
.rarity-epic      { color: #b47ae0; }
.rarity-legendary { color: #f0a93c; text-shadow: 0 0 8px rgba(240, 169, 60, .35); }

.log-line.loot { color: var(--text-dim); }

/* 액션바 보조 버튼 — 넷이라 한 줄에 몰면 글자가 세로로 깨진다 */
.action-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.action-grid > button { white-space: nowrap; padding: 10px 8px; }
.heal-button { flex: none; }

/* 종족 안내 — 직업과 종족의 관계를 미리 알려둔다 */
.race-note {
  margin-top: 12px;
  padding: 10px 12px;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px dashed var(--line);
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.7;
}
.favored-job { color: var(--gold); }

/* 아직 열리지 않은 능력치 — 종족의 전부를 보여주되 지금 작동하는 것과 구분한다 */
.ability-locked { color: var(--text-faint); }

/* ── 직업 카드 ───────────────────────────────────────── */
/*
 * 서사와 전직 경로까지 담으므로 한 줄에 하나씩 편다.
 * 조건이 모자란 직업도 내용은 읽혀야 목표로 삼을 수 있어, 흐리게만 하고 가리지 않는다.
 */
.job-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }

.job-grid .choice { display: flex; flex-direction: column; gap: 5px; }
.choice-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }

.job-req { font-size: 12px; line-height: 1.6; }

.job-lore {
  font-size: 12px;
  line-height: 1.75;
  color: var(--text-faint);
  padding-left: 9px;
  border-left: 2px solid var(--line-soft);
}

.job-path { font-size: 11px; color: var(--text-faint); }
.class-title { color: var(--gold); font-weight: 600; }

.job-grid .choice:disabled { opacity: .6; }
.job-grid .choice:disabled .job-lore { opacity: .8; }

/* ── 신성한 직업의 전당 ──────────────────────────────── */

.step-strip {
  display: flex;
  gap: 5px;
  padding: 10px 12px;
  overflow-x: auto;
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line-soft);
}
.step-chip {
  flex: 0 0 auto;
  min-width: 40px;
  padding: 7px 6px;
  font-size: 13px;
  font-family: var(--mono);
  border-radius: 8px;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  color: var(--text-faint);
}
.step-chip.active { border-color: var(--gold); color: var(--gold); background: rgba(216,180,106,.1); }
.step-chip.here::after { content: '●'; display: block; font-size: 7px; color: var(--plum); line-height: 1; }
.step-chip.empty { opacity: .45; }
.step-chip .lock { font-size: 9px; display: block; line-height: 1; }

/*
 * ── 한 줄에 하나씩 ──────────────────────────────────────
 *
 * 예전에는 넓은 화면에서 두 칸으로 갈랐다. 카드가 호칭과 조건뿐이던 때는
 * 그쪽이 한눈에 들어왔는데, 지금은 입학 화면과 같은 것들이 들어 있다 —
 * 능력치 두 줄 · 요구조건 · 소개 · 이야기 · 앞으로의 길.
 * 그만한 글을 반 폭에 밀어 넣으면 <strong>줄이 잘게 끊겨</strong> 읽히지 않고,
 * 옆 칸과 높이가 어긋나 어디까지가 한 자리인지도 흐려진다.
 * 입학 화면의 직업 목록(.job-grid)이 한 줄에 하나인 것과 같은 이유다.
 */
.transfer-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }

.transfer-card {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 12px;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
}
.transfer-card .sub { color: var(--text-faint); font-size: 12px; }
.transfer-title { font-size: 15px; }
.transfer-card.locked { opacity: .72; }
.transfer-card.current { border-color: var(--plum); }

/*
 * 히든 직업은 종족을 고르고 조건을 전부 채웠을 때만 나타난다.
 * 다른 자리와 확실히 구분되도록 테두리와 이름 색을 달리한다.
 */
.transfer-card.hidden-job {
  background: linear-gradient(180deg, rgba(255, 207, 92, .10), var(--bg-sunken));
}
.transfer-card.hidden-job .transfer-title { color: var(--crit); text-shadow: 0 0 10px rgba(255, 207, 92, .35); }

.hidden-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 8px;
  background: var(--crit);
  color: #2a2010;
  font-size: 10px;
  font-weight: 700;
  vertical-align: middle;
}

/* 주요 · 보조 능력치 두 줄. 입학 화면의 종족 카드와 같은 간격으로 붙여둔다 */
.transfer-growth { display: flex; flex-direction: column; gap: 2px; }
.transfer-growth .choice-note { display: block; margin-top: 0; font-size: 11.5px; }

/* 카드 안에서는 직업 소개가 이야기보다 먼저 온다. 위 여백만 살짝 띄운다 */
.transfer-card > .choice-note { display: block; font-size: 12px; color: var(--text-faint); }
.transfer-card > .job-lore { margin: 0; }
.transfer-card > .job-path { line-height: 1.6; word-break: keep-all; }

.transfer-missing { font-size: 12px; color: var(--text-faint); line-height: 1.6; }
.transfer-missing .gap { color: var(--danger); }

/* 전직 버튼은 그 직업의 색을 입는다 (view-transfer.js의 jobButtonStyle).
 * 색은 인라인으로 오므로 여기서는 **원래 배경을 비켜주기만** 한다 —
 * .primary가 깔아둔 보랏빛 그라데이션이 남아 있으면 직업 색이 그 위에 얹혀 탁해진다. */
button.primary.transfer-go { background-color: transparent; }
button.primary.transfer-go:hover:not(:disabled) { filter: brightness(1.25); }

/* 갈 수 있는 자리 — 조건 줄 자리에서 한 번 더 말해준다. */
.transfer-ready {
  font-size: 12px;
  font-weight: 600;
  /* 회복·성장에 쓰는 초록을 그대로 빌린다 — «좋은 소식»의 색이 화면 전체에서 하나여야 한다. */
  color: var(--heal);
  letter-spacing: .02em;
}



/* 아직 닿지 못하는 단계 — 어떤 자리들이 기다리는지 이름만 보여준다 */
.preview-list { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.preview-chip {
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  color: var(--text-faint);
  font-size: 12px;
}

/* 요구조건 표기 — 조건은 늘 보여주고 모자란 부분만 눈에 띄게 한다 */
.req-label { color: var(--text-faint); }
.req-met { color: var(--text-dim); }
.req-unmet { color: var(--text-dim); }

/* 내 종족이 잘 키우는 스탯이면 능력치 표와 같은 색을 입는다.
 * 두 클래스를 함께 달아 여기서 이기게 한다 — 채웠는지 못 채웠는지는
 * 두 상태의 색이 어차피 같고, 뒤에 붙는 붉은 «(N 부족)»이 말해준다. */
.req-met.growth-primary, .req-unmet.growth-primary { color: var(--primary-stat); font-weight: 600; }
.req-met.growth-secondary, .req-unmet.growth-secondary { color: var(--secondary-stat); }
.req-gap { color: var(--danger); font-family: var(--mono); font-size: 11px; }

/* ── 설정 · 위험한 조작 ──────────────────────────────── */
/*
 * 되돌릴 수 없는 조작은 색과 여백으로 먼저 멈춰 세운다.
 * 확인 버튼 하나로 사라지게 두면 실수 한 번에 모든 것이 없어진다.
 */
.danger-box {
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  background: rgba(226, 104, 95, .07);
  padding: 14px;
}
.danger-title {
  color: var(--danger);
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 8px;
}
.danger-body { color: var(--text-dim); font-size: 13px; line-height: 1.75; }
.danger-list {
  margin: 10px 0;
  padding-left: 18px;
  color: var(--text-faint);
  font-size: 12px;
  line-height: 1.9;
}
.danger-name {
  margin-top: 10px;
  padding: 10px;
  border-radius: 8px;
  background: var(--bg-sunken);
  border: 1px dashed var(--danger);
  color: var(--danger);
  font-family: var(--mono);
  font-size: 16px;
  text-align: center;
  user-select: all;
}
.danger-action {
  width: 100%;
  margin-top: 12px;
  background: rgba(226, 104, 95, .14);
  border-color: var(--danger);
  color: var(--danger);
  font-weight: 600;
}
.danger-action:hover:not(:disabled) { background: rgba(226, 104, 95, .24); }
/* 나란히 놓일 때는 폭을 나눠 갖는다. 100%를 그대로 두면 옆 버튼이 세로로 깨진다. */
.row .danger-action { margin-top: 0; width: auto; flex: 1; white-space: nowrap; }
.row .ghost.grow { white-space: nowrap; }

/* 전당의 단계 선택 — 갈 수 있는 자리가 많아지면 단계로 묶어 하나씩 펼쳐 본다 */
.step-bar {
  padding: 10px 12px;
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line-soft);
}
.step-bar-label {
  display: block;
  font-size: 11px;
  color: var(--text-faint);
  letter-spacing: .06em;
  margin-bottom: 6px;
}
.step-strip { padding: 0; background: none; border: none; }
.step-no { display: block; }
.step-count {
  display: block;
  font-size: 10px;
  color: var(--gold-dim);
  line-height: 1.3;
}
.step-chip.active .step-count { color: var(--gold); }

/* ── 직업 테마 적용 ──────────────────────────────────── */
/*
 * 규칙은 셋뿐이다.
 *   테두리 — 캐릭터가 있는 화면의 패널·카드·헤더 경계선
 *   배경   — 같은 곳에 아주 옅게 깔리는 물빛
 *   강조   — 경험치 게이지 · 활성 탭 · 초상화 테두리
 * 재화와 타이틀의 금색은 그대로 두어 게임 전체의 공통 정체성을 남긴다.
 */

/* 테두리 + 배경 */
.char-bar,
.manor-header,
.step-bar {
  border-bottom-color: var(--job-line);
  background: linear-gradient(180deg, var(--job-wash), var(--bg-raised));
}

.panel { border-color: var(--job-line-soft); }
.section { border-color: var(--job-line-soft); }
.storage-card,
.shop-card,
.transfer-card { border-color: var(--job-line-soft); }

.tabs { border-bottom-color: var(--job-line); }
.action-bar { border-top-color: var(--job-line); background: linear-gradient(0deg, var(--job-wash), var(--bg-raised)); }

/* 강조 */
.meter > span { background: var(--job); }
.portrait,
.portrait-thumb { border-color: var(--job-line); }

.tab.active { color: var(--job); border-bottom-color: var(--job); }
.tab .badge { background: var(--job); }

.panel > h3 { color: var(--job); border-bottom-color: var(--job-line-soft); }
.choice.selected { border-color: var(--job); background: var(--job-wash-strong); }
.storage-card.equipped { border-color: var(--job); background: var(--job-wash-strong); }
.transfer-card.current { border-color: var(--job); }
.step-chip.active { border-color: var(--job); color: var(--job); background: var(--job-wash-strong); }
.step-chip.here::after { color: var(--job); }
.tag { background: var(--job); }
.mail.unread { border-color: var(--job); }

input:focus,
select:focus,
textarea:focus { border-color: var(--job); }

/*
 * 히든 직업 카드와 위험한 조작은 테마를 따르지 않는다.
 * 각각 '숨겨져 있던 것'과 '되돌릴 수 없음'을 알리는 고유한 색이 있어야 하기 때문이다.
 */
/* 히든 직업의 테두리는 아래 무지개 규칙이 맡는다. */
.danger-box,
.danger-name { border-color: var(--danger); }

/* ── 채팅 — 클래스 표시와 전직 알림 ─────────────────────
 *
 * 클래스 숫자는 닉네임 앞에 (2)처럼 붙는다. 금색인 닉네임보다 한 단계 죽여
 * 이름이 먼저 읽히고 단계는 곁들여 읽히게 한다.
 */
.chat-line .cls {
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 11px;
  margin-right: 3px;
}

/* 이름 뒤의 클래스 호칭 — (견습생도).
 *
 * 이름과 같은 무게로 서면 둘 중 무엇이 그 사람인지가 흐려진다.
 * 한 칸 눌러 두어 **이름이 먼저 읽히고 호칭이 곁들여 읽히게** 한다.
 * 어절 중간에서 접히면 괄호만 다음 줄로 넘어가므로 통째로 붙들어 둔다. */
.chat-line .who-class {
  color: var(--text-faint);
  font-size: 11px;
  white-space: nowrap;
}

/* 전직 알림.
 *
 * 글자 하나하나를 span으로 쪼개지 않고 배경 그라디언트를 글자 모양으로 오려 쓴다
 * (background-clip: text). 문구가 길어지든 짧아지든 같은 무지개가 흐르고,
 * 화면을 그리는 쪽은 텍스트만 넘기면 되므로 el()의 textContent 원칙도 그대로 지켜진다. */
.chat-line.system.rainbow {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .02em;
  padding: 3px 0;
  /* 전광판이 지나가는 창. 넘치는 글자는 잘라내고 줄은 바꾸지 않는다 —
   * 줄을 바꾸면 «지나간다»가 아니라 «쌓인다»가 된다. */
  overflow: hidden;
  white-space: nowrap;
  word-break: keep-all;

  /**
   * **줄어들지 않게 못 박는다.**
   *
   * 채팅 목록은 세로 flex이고 높이가 정해져 있어, 줄이 쌓이면 칸들이 눌린다.
   * 보통 줄은 «자동 최소 높이»가 제 내용만큼이라 버티는데,
   * <strong>overflow를 hidden으로 두는 순간 그 보호가 0으로 풀린다</strong>(flex 규칙).
   *
   * 그래서 24px짜리 글자가 여백만 남은 6px 틈으로 눌렸고,
   * 그 틈으로 글자 가운데가 지나가 «가는 선 하나»가 흘러가는 것처럼 보였다.
   */
  flex: 0 0 auto;
}

.chat-line.system.rainbow .at { display: none; }

/* ── 전광판 ──────────────────────────────────────────────
 *
 * 오른쪽 밖에서 들어와 왼쪽 밖으로 나간다.
 *
 * <strong>padding-left: 100%</strong>가 요령이다. 이 100%는 <em>바깥 칸의 폭</em>이라
 * 글자가 창 오른쪽 너머에서 시작하고, translateX(-100%)는 <em>자기 폭</em>만큼 —
 * 즉 여백과 글자를 합한 만큼 — 왼쪽으로 민다. 그래서 문구가 길든 짧든
 * 늘 오른쪽 밖에서 들어와 왼쪽 밖으로 완전히 빠져나간다.
 *
 * 무지개를 이 요소에 함께 걸지 않는 이유가 여기 있다. 배경은 padding까지 덮으므로
 * 폭이 두 배가 된 상자에 스펙트럼이 늘어져, 정작 글자에는 한 조각만 걸린다. */
.chat-line.system.rainbow .marquee {
  display: inline-block;
  padding-left: 100%;
  animation: rainbow-march 14s linear infinite;
}

@keyframes rainbow-march {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}

.chat-line.system.rainbow .rainbow-text {
  /* 스펙트럼을 두 번 이어 붙이고 창을 그 절반(=한 바퀴)으로 잡는다.
   * 그래야 어느 순간에 잘라 봐도 무지개 전체가 글자에 걸린다 —
   * 창이 좁으면 흐르는 도중 서너 색만 보여 그냥 파란 글씨처럼 읽힌다. */
  background-image: linear-gradient(
    100deg,
    #ff5f6d, #ffa751, #ffe259, #55d98d, #3fa9f5, #a970d8,
    #ff5f6d, #ffa751, #ffe259, #55d98d, #3fa9f5, #a970d8,
    #ff5f6d
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 7px rgba(255, 255, 255, .22));
  animation: rainbow-flow 7s linear infinite;
}

@keyframes rainbow-flow {
  from { background-position: 100% 50%; }
  to   { background-position: 0% 50%; }
}

/* 움직임을 원치 않는 사람에게는 흐르지 않는 무지개를 준다.
 * 전광판도 함께 멈추고 제자리로 되돌린다 — 멈춘 채 왼쪽으로 밀려 있으면
 * 글자가 창 밖에 나가 있어 아예 읽히지 않는다. */
@media (prefers-reduced-motion: reduce) {
  .chat-line.system.rainbow .rainbow-text { animation: none; }
  .chat-line.system.rainbow { white-space: normal; text-align: center; }
  .chat-line.system.rainbow .marquee { animation: none; padding-left: 0; display: inline; }
}

/* ── 캐릭터 패널 — 레벨 · 경험치 바 · 골드 ──────────────
 *
 * 라벨에만 색을 입히고 수치는 기본 색으로 남긴다.
 * 둘 다 물들이면 무엇을 먼저 봐야 할지가 사라진다. */

/* .kv dt가 색을 지정하므로 특이도를 맞춰 눌러준다. */
.kv dt.label-level { color: var(--job); font-weight: 600; }
.kv dt.label-gold  { color: var(--gold); font-weight: 600; }

/* 경험치 바 — 남은 거리를 길이로, 정확한 값을 그 위에 겹쳐서.
 * 채워진 쪽과 빈 쪽 어디에 글씨가 걸려도 읽히도록 흰색에 그림자를 준다. */
.exp-bar {
  position: relative;
  height: 20px;
  border-radius: 10px;
  overflow: hidden;
  background: rgb(var(--job-rgb) / .14);
  border: 1px solid rgb(var(--job-rgb) / .35);
}
.exp-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, rgb(var(--job-rgb) / .75), var(--job));
  transition: width .3s;
}
.exp-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 1px 2px rgb(0 0 0 / .75);
  letter-spacing: .02em;
  pointer-events: none;
}

/* ── 여섯 스탯 — 한 줄에 셋(자릿수가 크면 둘) ──────────── */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(var(--stat-cols, 3), minmax(0, 1fr));
  gap: 4px 10px;
  font-size: 13px;
}
.stat-cell {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgb(var(--job-rgb) / .05);
}
.stat-name { font-weight: 600; white-space: nowrap; }
/* 성장 특성은 굵기와 밑줄로만 알린다 — 색은 이미 스탯 고유색이 쓰고 있다. */
.stat-name.growth-primary { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 2px; }
.stat-name.growth-secondary { text-decoration: underline dotted; text-underline-offset: 3px; }

/* ── 팝업이 딸려 있다는 표시 ───────────────────────────
 *
 * 여섯 스탯에는 저마다 설명 팝업이 붙어 있는데(.stat-tip), 그 사실을 알리는 표시가
 * 없어서 <strong>눌러본 사람만 알았다.</strong> 로그의 몬스터·전리품이 쓰는 것과
 * 같은 점선을 깐다 — 표시가 자리마다 다르면 어디를 만져야 하는지를 매번 시험해야 한다.
 *
 * 손가락으로 만지는 화면에서도 그대로 보인다. 거기서는 hover가 없으니
 * 표시가 없으면 팝업의 존재 자체를 알 길이 없다.
 *
 * 성장 특성 밑줄이 이미 있는 스탯은 비켜준다 — 한 낱말 밑에 줄이 두 겹으로 그어진다.
 * 그 줄이 이미 «만질 수 있는 것»이라는 같은 구실을 한다. */
.stat-grid .stat-name { border-bottom: 1px dotted currentColor; }
.stat-grid .stat-name.growth-primary,
.stat-grid .stat-name.growth-secondary { border-bottom: none; }
.stat-cell { cursor: help; }
.stat-value {
  font-family: var(--mono);
  color: #fff;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 공격력·방어력은 여섯 스탯에서 파생된 값이라 한 칸 띄워 따로 묶는다. */
.kv.derived {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid rgb(var(--job-rgb) / .25);
}

/* ── 착용 장비 — 한 줄에 둘씩 세 줄 ────────────────────── */

.gear-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.gear-cell {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: 8px;
  row-gap: 1px;
  min-width: 0;
  padding: 6px 10px;
  border-radius: 6px;
  border: 1px solid rgb(var(--job-rgb) / .22);
  background: rgb(var(--job-rgb) / .05);
}
/* 비어 있는 자리는 점선으로 둔다 — 아직 갖출 것이 남았다는 뜻이 형태로 읽힌다. */
.gear-cell.empty-slot {
  border-style: dashed;
  background: transparent;
}
.gear-slot {
  font-size: 11px;
  color: var(--job);
  font-weight: 600;
  letter-spacing: .02em;
  white-space: nowrap;
}
.gear-name {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gear-cell.empty-slot .gear-name { color: var(--text-faint); }
/* 수치는 이름 아래, 이름과 같은 열에 건다 — 슬롯 이름 칸 아래는 비워 둔다 */
.gear-stat {
  grid-column: 2;
  display: flex;
  /* 「공」이 아니라 「공격력」을 쓰므로 좁은 화면에서는 두 수치가 한 줄에 안 들어간다.
   * 접히게 두면 칸 밖으로 넘치는 대신 아랫줄로 내려간다 — 잘리는 것보다 낫다. */
  flex-wrap: wrap;
  gap: 2px 8px;
  font-family: var(--mono);
  font-size: 11px;
}
.gear-stat > span { white-space: nowrap; }
.gear-stat .atk { color: var(--danger); }
.gear-stat .def { color: var(--heal); }

/* ── 전당 — 히든 직업의 무지개 테두리 ────────────────────
 *
 * 현재 자리 표시도 금색이라 히든 직업과 구분되지 않았다.
 * 히든만 무지개로 옮겨, **한눈에 다른 종류의 것**임을 알린다.
 *
 * 테두리에 그라디언트를 직접 칠할 수는 없으므로 배경을 두 겹 깔고
 * 안쪽만 원래 바탕색으로 덮는다 (border-box 위에 padding-box).
 * 글자를 쪼개지 않는다는 원칙은 여기서도 지켜진다. */
.transfer-card.hidden-job {
  border: 2px solid transparent;
  background-image:
    linear-gradient(var(--bg-sunken), var(--bg-sunken)),
    linear-gradient(115deg,
      #ff5f6d, #ffc371, #f9f871, #6ee7a8, #4fc3f7, #9b7fd4, #ff5f6d);
  background-origin: border-box;
  background-clip: padding-box, border-box;
  background-size: auto, 300% 100%;
  animation: hidden-sheen 7s linear infinite;
}
@keyframes hidden-sheen {
  from { background-position: 0 0, 0% 50%; }
  to   { background-position: 0 0, 300% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .transfer-card.hidden-job { animation: none; }
}

/* ── 전당 — 지금의 나와 앞으로의 나 ─────────────────────── */

.transfer-portraits {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 14px 12px 4px;
}
.portrait-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.transfer-portrait {
  width: 96px;
  height: 96px;
  border-radius: 10px;
  border: 2px solid rgb(var(--job-rgb) / .5);
  background: var(--bg-sunken);
}
.transfer-portrait.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  border-style: dashed;
  border-color: var(--line);
  color: var(--text-faint);
  font-size: 30px;
}
/* 바뀔 자리가 히든이면 초상화 테두리도 무지개를 두른다. */
.transfer-portrait.hidden-job {
  border-color: transparent;
  background-image:
    linear-gradient(var(--bg-sunken), var(--bg-sunken)),
    linear-gradient(115deg, #ff5f6d, #ffc371, #f9f871, #6ee7a8, #4fc3f7, #9b7fd4, #ff5f6d);
  background-origin: border-box;
  background-clip: padding-box, border-box;
}
.portrait-caption { font-size: 13px; font-weight: 600; text-align: center; }
.portrait-note { font-size: 11px; }
.portrait-arrow {
  font-size: 22px;
  color: var(--text-faint);
  transition: color .2s;
}
.portrait-arrow.on { color: var(--job); }
.portrait-side.empty { opacity: .65; }

/* 고른 자리는 테두리를 굵게 해 어느 것을 보고 있는지 알린다. */
.transfer-card { cursor: pointer; }
.transfer-card.picked { outline: 2px solid var(--job); outline-offset: 1px; }

/* ── 되돌릴 수 없는 전직 확인 ───────────────────────────── */

.confirm-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgb(0 0 0 / .6);
}
.confirm-box {
  width: min(440px, 100%);
  padding: 20px;
  border-radius: 10px;
  border: 2px solid var(--danger);
  background: var(--bg-raised);
  box-shadow: 0 20px 50px rgb(0 0 0 / .5);
}
.confirm-title {
  font-weight: 700;
  font-size: 15px;
  color: var(--danger);
  margin-bottom: 10px;
}
.confirm-body { font-size: 14px; line-height: 1.7; margin: 0; }

/* 위로 오르는 길의 확인창. 되돌릴 수 없다는 점은 같지만 말리는 자리는 아니므로,
 * 붉은 테두리 대신 직업 테마색을 쓴다. 같은 창이 색만으로 두 가지 뜻을 진다. */
.rise-box { border-color: var(--job); }
.rise-title { color: var(--job); }

/* ── 채팅 패널 — 목록 아래에 입력창이 붙는다 ────────────
 *
 * 예전에는 화면 맨 아래 액션바에 있었다. 읽던 자리와 쓰는 자리가 멀어
 * 시선이 한 번 더 움직여야 했다. 같은 패널 안으로 들여놓는다. */
.chat-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}
.chat-pane .chat-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.chat-pane .chat-form {
  flex: none;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}

/* ── 채팅 색 — 클래스 3단계마다 한 칸 ───────────────────
 *
 * 클래스를 올리면 눈에 보이는 것이 하나 생긴다. 전투력처럼 감춰진 수치가 아니라
 * **대화창에서 남들이 알아보는 표시**다.
 *
 *   1~3단계  견습 구간   담백한 기본색
 *   4~6단계  자리를 잡은 구간
 *   7~9단계  사람의 영역 밖
 *   10단계   지고 — 금빛
 *
 * 색을 닉네임에만 입힌다. 말 내용까지 물들이면 길어질수록 읽기 어렵다.
 * 나중에 페인트 아이템이 붙으면 .paint-* 규칙이 이 자리를 대신한다. */
.chat-line.tone-1 .who { color: var(--text-soft); }
.chat-line.tone-2 .who { color: #6ee7a8; }
.chat-line.tone-3 .who { color: #7fb3ff; }
.chat-line.tone-4 .who {
  color: var(--crit);
  text-shadow: 0 0 8px rgba(255, 207, 92, .4);
}

/* ── 가판대 — 하루 한정과 품절 ──────────────────────────
 *
 * 품절이라는 사실만 알려주면 하릴없이 되돌아오는 수밖에 없다.
 * 언제 다시 오면 되는지를 같은 자리에 겹쳐 적는다. */

.stock-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 6px;
  font-size: 12px;
}

.shop-card { position: relative; }
.shop-card.sold-out > *:not(.sold-out-veil) { opacity: .35; }

.sold-out-veil {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: inherit;
  background: rgb(0 0 0 / .55);
  pointer-events: none;
}
.sold-out-mark {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: .06em;
  color: var(--danger);
  /* 어두운 장막 위에서도 또렷하게 */
  text-shadow: 0 0 10px rgb(0 0 0 / .8), 0 1px 2px rgb(0 0 0 / .9);
  transform: rotate(-6deg);
}
.sold-out-gap {
  font-family: var(--mono);
  font-size: 12px;
  color: #fff;
  text-shadow: 0 1px 3px rgb(0 0 0 / .9);
}

/* ── 액션바 마지막 줄 — 상점가 · 전당 · 설정 · 안내서 ────
 *
 * 나가는 길 넷. **폭을 똑같이 나눈다.**
 *
 * 예전에는 각자 제 글자만큼 갖고 남는 폭만 비례해 나눴다. 글자 수가 2~9자로
 * 제각각이라 그렇게 두는 편이 안전했는데, 그 결과 넷의 폭이 다 달라
 * 한 줄로 읽히지 않고 «들쭉날쭉한 네 덩어리»로 보였다.
 * 같은 성격의 버튼이 나란히 설 때는 크기가 같아야 한 줄이 된다.
 *
 * flex-basis를 0으로 두어 남는 폭을 넷이 똑같이 나눈다.
 * 긴 이름(신성한 직업의 전당)이 반 폭에서 넘치므로 줄바꿈을 허용하되
 * 낱말 안에서는 끊지 않는다 — 고정 height를 주면 두 줄째가 버튼 밖으로 샌다. */
/* .row가 align-items: center라 각자 제 높이를 쓴다. 그러면 «설정»만 한 줄이라
 * 짧고 나머지는 두 줄이라 길어져, 폭을 맞춰놓고도 줄이 들쭉날쭉해 보인다.
 * 넷을 같은 높이로 늘려 한 덩어리로 세운다. */
.exit-row { align-items: stretch; }
.exit-row > .ghost {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-width: 0;
  white-space: normal;
  word-break: keep-all;
  line-height: 1.25;
  padding: 8px 4px;
  font-size: 11px;
}
/* 안내서만 <a>다 — 바깥 탭으로 열리는 진짜 링크라서, 가운데 클릭으로도 열리고
 * 주소를 복사해 남에게 건넬 수도 있어야 한다. 대신 button이 기본으로 갖는
 * 생김새(테두리 · 여백 · 가운데 정렬)를 물려받지 못하므로 여기서 맞춰준다.
 * 옆의 셋과 조금이라도 어긋나면 한 줄로 읽히지 않는다. */
.exit-row > a.guide-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius);
  text-decoration: none;
}

/* 폭이 넉넉해지면 글자도 같이 키운다. 11px은 넷을 좁은 화면에 밀어넣기 위한 값이지,
 * 남는 자리가 있는데도 작게 둘 이유는 없다. */
@media (min-width: 560px) {
  .exit-row > .ghost { font-size: 13px; padding: 10px 12px; }
}


/* ── 가게 버튼 두 개 ────────────────────────────────────
 *
 * 왕가슴 사제님의 금융치료와 아이네의 심야식당. 이름이 길어 한 줄에 담기지 않으므로
 * 이름과 부제를 위아래로 세운다. 둘의 모양이 같아 나란히 놓이면 한 거리처럼 읽힌다. */
.store-row > button { padding: 8px 10px; }
.store-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  white-space: normal;
  line-height: 1.3;
}
.store-name { font-size: 12px; font-weight: 700; }
.store-note { font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.store-note.short { color: var(--danger); }
.store-note.on { color: var(--heal); }


/* ── 아이네의 심야식당 ──────────────────────────────────
 *
 * 밤에 문을 여는 가게라 전직 경고창의 붉은 테두리 대신 따뜻한 색을 쓴다.
 * 여기서 일어나는 일은 경고가 아니라 대접이다. */
.diner-box {
  width: min(520px, 100%);
  max-height: min(80vh, 720px);
  overflow-y: auto;
  border-color: var(--gold);
}
.diner-head { margin-bottom: 12px; }
.diner-title { font-size: 16px; font-weight: 700; color: var(--gold); }
.diner-lead { margin: 4px 0 0; font-size: 13px; color: var(--text-dim); }

.buff-strip { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.buff-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  font-size: 12px;
}
.buff-chip > b { color: var(--heal); }
.buff-left { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.diner-empty { margin: 0 0 12px; font-size: 12px; color: var(--text-faint); }

.dish-list { display: flex; flex-direction: column; gap: 8px; }
.dish {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--bg-sunken);
  white-space: normal;
}
.dish:disabled { opacity: .5; }
/* 오마카세만 테두리를 달리해, 무엇이 나올지 모르는 자리라는 것을 알린다 */
.dish.omakase { border-style: dashed; border-color: var(--gold); }
.dish-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.dish-name { font-weight: 700; font-size: 14px; }
.dish-price { font-size: 13px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.dish-price.short { color: var(--danger); }
.dish-effect { margin-top: 3px; font-size: 12px; color: var(--gold); }
.dish-desc { margin-top: 3px; font-size: 12px; color: var(--text-faint); line-height: 1.6; }

.diner-foot {
  margin: 12px 0;
  font-size: 11px;
  color: var(--text-faint);
  line-height: 1.6;
}


/* ── 스탯 설명 말풍선 ────────────────────────────────────
 *
 * 여섯 스탯은 이름과 숫자만으로는 무엇에 쓰이는지 알 수 없다. 위에 잠깐 머무르면
 * 무엇을 올려주는 값인지 뜬다. 여닫는 일을 CSS에만 맡겨, 화면을 다시 그려도
 * 열려 있던 것이 닫히지 않는다.
 *
 * 손가락으로 만지는 화면에는 hover가 없으므로 :focus-within도 함께 건다 —
 * 한 번 누르면 뜨고, 다른 곳을 누르면 사라진다. */
/*
 * 말풍선은 칸이 아니라 **스탯 묶음 전체**에 붙는다.
 *
 * 칸에 붙이면 가운데 칸의 말풍선이 좁은 사이드 패널 밖으로 삐져나가 잘린다.
 * 묶음에 붙여 좌우를 그 폭에 맞추면, 여섯 칸 어디에 머물러도 같은 자리에
 * 같은 크기로 떠서 늘 온전히 읽힌다.
 */
.stat-grid { position: relative; }
.stat-tip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 30;

  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px;

  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-raised);
  box-shadow: 0 10px 28px rgb(0 0 0 / .45);

  font-size: 12px;
  line-height: 1.6;
  color: var(--text-dim);
  text-align: left;
  white-space: normal;

  opacity: 0;
  visibility: hidden;
  transition: opacity .12s ease;
}
.stat-tip > b { font-size: 12px; font-weight: 700; }
.stat-cell:hover > .stat-tip,
.stat-cell:focus-within > .stat-tip { opacity: 1; visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .stat-tip { transition: none; }
}

/* ── 공격력 · 방어력 ────────────────────────────────────
 *
 * 서로 견줘 보는 값이라 위아래로 떼어놓지 않고 한 줄에 나란히 세운다.
 * 여섯 스탯에서 파생된 값이므로 위와 한 칸 띄워 따로 묶는다. */
.derived-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-top: 12px;
}
.derived-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 8px 6px;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
}
.derived-name { font-size: 11px; color: var(--text-dim); }
.derived-value { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.derived-gear { font-size: 10px; color: var(--text-faint); font-variant-numeric: tabular-nums; }


/* ── 지역이동 버튼의 색 ─────────────────────────────────
 *
 * 글자를 읽기 전에 어느 버튼인지 알아보게 하는 장치다.
 * 색은 view-main.js의 NAV_HUE가 --nav-rgb로 넣어주고, 여기서는 그 하나로
 * 배경 그라데이션 · 테두리 · 글자색을 함께 만든다 — 버튼마다 규칙을 따로 쓰지 않는다.
 *
 * 어두운 바탕 위이므로 원색을 그대로 깔지 않는다. 낮은 투명도로 얹어
 * 색이 "칠해진 것"이 아니라 "배어난 것"처럼 보이게 한다. */
.nav {
  background: linear-gradient(
    170deg,
    rgb(var(--nav-rgb) / .30) 0%,
    rgb(var(--nav-rgb) / .14) 55%,
    rgb(var(--nav-rgb) / .07) 100%
  );
  border: 1px solid rgb(var(--nav-rgb) / .42);
  color: rgb(var(--nav-rgb));
  transition: filter .12s ease;
}
.nav:hover:not(:disabled) { filter: brightness(1.25); }
/* 손가락에는 hover가 없다. 누르는 순간 눈에 보이는 반응이 없으면
 * 먹히지 않은 줄 알고 한 번 더 누른다 — 수행이 두 번 나가는 길이다. */
.nav:active:not(:disabled) { filter: brightness(1.4); transform: translateY(1px); }
.nav:disabled { filter: grayscale(.6) brightness(.8); }

/* 가게 버튼은 이름 아래 부제를 단다. 부제는 색을 물려받지 않고 원래 톤을 지킨다 —
 * 값이나 남은 시간은 장소의 색이 아니라 상태의 색으로 읽혀야 한다. */
.nav > .store-name { color: rgb(var(--nav-rgb)); }
.nav > .store-note { color: var(--text-dim); }
.nav > .store-note.short { color: var(--danger); }
.nav > .store-note.on { color: var(--heal); }



/* ── 버프 칩 — 이름 · 효과 · 남은 시간 ────────────────── */
.buff-effect { color: var(--text-dim); }

/* ── 레벨업 능력치 줄 ──────────────────────────────────
 *
 * 레벨 줄에 이어 붙이면 스탯 넷이 오른 날 두 줄로 접혀 모양이 흐트러진다.
 * 줄을 따로 두면 길이가 얼마가 되든 흔들리지 않는다.
 * 스탯 이름은 캐릭터 패널과 같은 성향색을 쓴다 — 같은 스탯이 어디서나 같은 색이어야 한다. */
.gain-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  padding-left: 14px;
  font-size: 12px;
}
.gain-stat { font-weight: 600; }
.gain-amount { margin-left: 2px; color: var(--text); font-variant-numeric: tabular-nums; }
.gain-sep { color: var(--text-faint); }


/* ── 운영자의 말 ────────────────────────────────────────
 *
 * 클래스 단계 색보다 앞선다. 공지인지 잡담인지가 먼저 읽혀야 하기 때문이다.
 * 금색은 이 화면에서 이미 "값어치 있는 것"을 뜻하므로, 운영자에게는
 * 아무도 쓰지 않는 청록을 준다 — 어느 클래스 색과도 겹치지 않는다. */
.chat-line.tone-admin > .who { color: #4fd1c5; font-weight: 700; }
.chat-line.tone-admin { color: #a7e8e2; }
/* 왕관은 닉네임 바로 앞에 놓인다. 글자가 아니라 그림이므로 크기만 맞춘다 */
.admin-crown {
  font-size: 12px;
  line-height: 1;
  /* 이모지는 글자보다 살짝 위에 뜬다. 닉네임과 밑선을 맞춘다 */
  vertical-align: -1px;
}

/* ── 수행 소문 ──────────────────────────────────────────
 *
 * 세 줄짜리 알림이라 줄바꿈을 살린다.
 *
 * **레벨업 알림과 색을 갈라놓는다.** 레벨업은 "누가 해냈다"는 소식이라
 * 시스템 기본색으로 담담히 적히지만, 소문은 사건이 아니라 분위기다 —
 * 어디선가 무언가 터지고 있다는 기척이므로 붉은 기가 도는 자줏빛을 준다.
 * 무지개(전직)처럼 앞으로 나서지는 않되, 지나가는 알림들 사이에서는 구분된다. */
.chat-line.system.rumble {
  white-space: pre-line;
  line-height: 1.7;
  color: #c98fb4;
}

/* 관리자가 걸어둔 반복 공지.
 *
 * 게임 안에서 일어난 일이 아니라 «학원의 알림»이므로 다른 시스템 메시지와 색을 나눈다 —
 * 같은 자주색으로 두면 전직 · 강화 알림 사이에 묻혀 읽히지 않는다.
 * 왼쪽에 선을 세워 흘러가는 로그 속에서 덩어리로 잡히게 했다. */
.chat-line.system.notice {
  white-space: pre-line;
  line-height: 1.7;
  color: #e6d9a8;
  border-left: 2px solid #b99b4a;
  padding-left: 8px;
}


/* ── 장비 말풍선 ────────────────────────────────────────
 *
 * 스탯 말풍선과 같은 장치다 — 여닫는 일을 CSS에만 맡겨 화면을 다시 그려도 살아남고,
 * 손가락으로 만지는 화면에서는 :focus-within이 대신 연다.
 * 칸이 아니라 묶음에 붙여 좁은 패널 밖으로 삐져나가지 않게 한다. */
.gear-grid { position: relative; }
.gear-cell { position: static; }
.gear-tip {
  position: absolute;
  /* 칸 위가 아니라 **아래**로 띄운다. 장비 패널은 우측 컬럼 가운데쯤에 있어
   * 위로 띄우면 패널 머리에 걸려 첫 줄이 잘린다. 아래는 채팅 패널이 있을 뿐이고,
   * 그 위로 얹히면 그만이다. */
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 40;

  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px;

  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-raised);
  box-shadow: 0 10px 28px rgb(0 0 0 / .45);

  font-size: 12px;
  line-height: 1.6;
  color: var(--text-dim);
  text-align: left;
  white-space: normal;

  opacity: 0;
  visibility: hidden;
  transition: opacity .12s ease;
}
.gear-tip > b { font-size: 12px; color: var(--job); }
.gear-cell:hover > .gear-tip,
.gear-cell:focus-within > .gear-tip { opacity: 1; visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .gear-tip { transition: none; }
}

/* 강화 단계는 수치가 아니라 이력이다. 금색으로 따로 세운다 */
.gear-enhance { color: var(--gold); font-weight: 600; }
/* 이름 옆의 요구 레벨. 이름을 밀어내지 않도록 한 톤 낮춰 붙인다 */
.gear-tip-lv { color: var(--text-faint); font-weight: 500; font-size: .92em; font-variant-numeric: tabular-nums; }
/* 강화 줄에 함께 적는 그 물건의 능력치. 강화 금색과 갈리게 둔다 */
.gear-tip-stat { color: var(--text-soft); font-weight: 500; font-variant-numeric: tabular-nums; }
.gear-tip-sep { color: var(--text-faint); font-weight: 400; }

/* 괄호 안이 강화로 붙은 몫이다. 본래 수치와 색을 갈라 어느 쪽이 제 손인지 보이게 한다 */
.gear-plus { color: var(--gold); font-weight: 700; }

/* 다음 전직 레벨 — 훑어보는 자리이므로 숫자만 남긴다 */


/* ── 수행 소문의 인용 ───────────────────────────────────
 *
 * 문장 속에서 누구의 목소리인지가 도드라져야 한다.
 * 소문 자체는 자줏빛이므로, 인용만 크리티컬과 같은 금빛으로 들어올린다. */
.rumble-quote { color: var(--crit); font-weight: 700; }

/* ── 설정의 체크 한 줄 ──────────────────────────────────
 * 네모와 글자를 같은 줄에 놓고, 줄 전체를 누르는 자리로 만든다 */
.check-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0;
  font-size: 13px;
  cursor: pointer;
}
.check-row input[type='checkbox'] { width: auto; flex: 0 0 auto; accent-color: var(--job); }


/* 장비 이름 앞의 그림. 이름과 밑선을 맞추고 폭만 조금 벌린다 */
.gear-icon { margin-right: 4px; font-size: 12px; }


/* ── 관리자 문 ──────────────────────────────────────────
 *
 * 설정의 네 탭은 자기 캐릭터를 손보는 자리이고, 이것만 성격이 다르다.
 * 그래서 탭에 끼우지 않고 머리글 아래에 따로 얹는다.
 * 청록은 채팅에서 이미 운영자를 뜻하므로 여기서도 같은 색을 쓴다. */
.admin-door {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 14px;
  padding: 10px 14px;
  border-radius: var(--radius);
  border: 1px solid rgb(79 209 197 / .35);
  background: rgb(79 209 197 / .08);
}
.admin-door-label { font-size: 13px; color: #a7e8e2; }
.admin-door-link {
  flex: 0 0 auto;
  text-decoration: none;
  text-align: center;
  padding: 8px 14px;
}

/* ── 강화 등급 ───────────────────────────────────────────
 *
 * 안전 구간을 넘긴 장비는 **보이는 것만으로 알아볼 수 있어야 한다.**
 * 위험을 감수한 사람에게 돌아가는 것이 수치 몇 점뿐이면 감수할 이유가 약하다.
 *
 * 무기는 +6부터 네 단계마다(+6 · +10 · +14),
 * 방어구와 쥬얼리는 +10부터 다섯 단계마다(+10 · +15 · +20) 올라간다.
 * 등급 이름과 경계는 src/game/data/enhance.ts가 정하고, 여기는 생김새만 맡는다.
 *
 * 세 등급이 색 → 빛무리 → 움직임 순으로 쌓이는 것이 요점이다.
 * 처음부터 움직이면 목록 전체가 산만해지고, 진짜 드문 물건이 묻힌다. */

.enh-w1, .enh-w2, .enh-w3,
.enh-a1, .enh-a2, .enh-a3 { font-weight: 700; }

/* 1등급 — 색과 굵기만. 목록에 여럿 있어도 시끄럽지 않다 */
.enh-w1 { color: #d9dee6; }
.enh-a1 { color: #7fc8f0; }

/* 2등급 — 글자 뒤에 빛이 번진다 */
.enh-w2 {
  color: #ff9d4d;
  text-shadow: 0 0 6px rgb(255 157 77 / .55), 0 0 14px rgb(255 120 30 / .3);
}
.enh-a2 {
  color: #c79bff;
  text-shadow: 0 0 6px rgb(199 155 255 / .55), 0 0 14px rgb(150 90 255 / .3);
}

/* 3등급 — 색이 흐르고 맥동한다. 여기까지 온 물건은 거의 없다 */
.enh-w3, .enh-a3 {
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: enh-flow 4s linear infinite, enh-pulse 2.4s ease-in-out infinite;
}
.enh-w3 {
  background-image: linear-gradient(100deg, #ffd76a, #fff4c8, #ff9d3c, #ffd76a);
  filter: drop-shadow(0 0 5px rgb(255 190 90 / .5));
}
.enh-a3 {
  background-image: linear-gradient(100deg, #7fd8ff, #c79bff, #ff9bd8, #7fd8ff);
  filter: drop-shadow(0 0 5px rgb(180 150 255 / .5));
}

@keyframes enh-flow {
  from { background-position: 0% 50%; }
  to   { background-position: 220% 50%; }
}
@keyframes enh-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .78; }
}

/* 움직임을 원치 않는 사람에게는 색만 남긴다.
 * 3등급의 요점은 화려함이지 흔들림이 아니므로 그라데이션은 그대로 둔다. */
@media (prefers-reduced-motion: reduce) {
  .enh-w3, .enh-a3 { animation: none; }
}

/* ── 트자매 공방 ─────────────────────────────────────────
 *
 * 자매가 말을 걸고, 손님이 걸치고 온 것을 한 줄씩 늘어놓고,
 * 하나를 고르면 그 자리에서 펼쳐진다. 화면을 옮기지 않는 것이 요점이다 —
 * 무엇을 강화할지 고르는 일과 얼마나 위험한지 읽는 일은
 * 같은 자리에서 일어나야 판단이 된다. */

/* 자매의 말. 안내문이 아니라 사람의 말이라는 것이 읽혀야 한다 */
.clerk-line {
  margin: 0 0 14px;
  padding: 10px 14px;
  border-radius: var(--radius);
  border: 1px solid rgb(216 180 106 / .25);
  background: rgb(216 180 106 / .07);
  color: #e8dcc0;
  font-size: 13px;
  line-height: 1.6;
}

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

/* [무기⚔️를 강화한다] 한 줄 */
.workshop-entry {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
}
.workshop-entry .we-label { font-size: 14px; font-weight: 600; }
.workshop-entry .we-item { font-size: 13px; color: var(--muted); }

.workshop-detail {
  padding: 14px;
  border-radius: var(--radius);
  border: 1px solid var(--gold-dim);
  background: rgb(255 255 255 / .03);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.wd-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }

/* +6 깨진 유리잔 → +7 깨진 유리잔.
 * 오른쪽은 강화한 뒤의 등급으로 그려지므로, 이펙트가 바뀌는 단계면 여기서 미리 보인다. */
.enh-preview {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 17px;
  padding: 10px 0;
}
.enh-preview .arrow { color: var(--muted); font-size: 14px; }
.enh-grade-note { font-size: 12px; color: var(--gold); }

.enh-spec { display: flex; flex-direction: column; gap: 6px; font-size: 13px; line-height: 1.6; }
.enh-spec .k { color: var(--muted); }
.enh-spec .short { color: #ff8a8a; }
.enh-bonus-from { font-size: 12px; color: var(--muted); padding-left: 10px; }

/* 실패했을 때 무엇을 잃는지. 위험이 커질수록 문장이 눈에 띈다 —
 * 파괴 구간의 경고가 다른 안내문과 같은 회색이면 읽히지 않는다. */
.enh-risk { font-size: 12px; line-height: 1.5; }
.enh-risk.safe { color: #7fd8a0; }
.enh-risk.mild { color: var(--muted); }
.enh-risk.risky { color: #ffb454; }
.enh-risk.doom { color: #ff8a8a; font-weight: 600; }

/* 되돌릴 수 없는 구간에서만 숨을 한 번 삼킨다.
 * 안전한 강화에까지 붙이면 이 한 줄이 아무 뜻도 없어진다. */
.enh-gulp {
  font-size: 13px;
  color: #ff8a8a;
  letter-spacing: .08em;
  animation: enh-gulp-in .5s ease-out;
}
@keyframes enh-gulp-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .enh-gulp { animation: none; } }

.enh-ask { font-size: 14px; color: #e8dcc0; }

.stone-stock { margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--line); }
.stone-stock h3 { font-size: 13px; color: var(--gold); margin: 0 0 10px; }

/* 등급 견본 (관리자 열람) */
.grade-book { display: flex; flex-direction: column; gap: 18px; }
.grade-family h3 { font-size: 14px; color: var(--gold); margin: 0 0 8px; }
.grade-list { display: flex; flex-direction: column; gap: 6px; }
.grade-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-radius: 6px;
  background: rgb(255 255 255 / .03);
  flex-wrap: wrap;
}
.grade-item .range { font-family: var(--mono); font-size: 12px; color: var(--muted); min-width: 7.5em; }
.grade-item .sample { font-size: 15px; flex: 1 1 12em; }
.grade-item .what { font-size: 12px; color: var(--muted); flex: 1 1 14em; }

/* 강화 열람 표. 위험 구간이 행 전체로 읽히도록 왼쪽에 색 띠를 세운다 —
 * 어느 단계부터 되돌릴 수 없는지가 표에서 한눈에 보여야 한다. */
.grade-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.grade-table th, .grade-table td { padding: 5px 10px; text-align: left; white-space: nowrap; }
.grade-table th { color: var(--muted); font-weight: 500; border-bottom: 1px solid var(--line); }
.grade-table td.num, .grade-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.grade-table tbody tr { border-left: 3px solid transparent; }
.grade-table tbody tr.safe { border-left-color: #7fd8a0; }
.grade-table tbody tr.mild { border-left-color: var(--line); }
.grade-table tbody tr.risky { border-left-color: #ffb454; }
.grade-table tbody tr.doom { border-left-color: #ff8a8a; background: rgb(255 90 90 / .05); }

/* ── 강화 돌파 알림 ───────────────────────────────────────
 *
 * 전직(rainbow)과 나눈 이유는 빈도다 — 전직은 누구나 하지만
 * 난이도 2를 넘긴 장비는 서버에 몇 개 없다. 같은 옷을 입히면 그 드묾이 지워진다.
 *
 * 걸작은 금빛으로 번지고, 있어서는 안 될 것은 붉게 맥동한다. */

.chat-line.system.masterwork,
.chat-line.system.forbidden {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .02em;
  padding: 4px 0;
  text-align: center;
  word-break: keep-all;
  /* 두 줄로 나뉜 알림이다 — 앞줄은 무슨 일인가, 뒷줄은 누구의 것이 되었나 */
  white-space: pre-line;
}
.chat-line.system.masterwork .at,
.chat-line.system.forbidden .at { display: none; }

.masterwork-text {
  background-image: linear-gradient(100deg, #ffe9a8, #ffd05e, #fff6d8, #ffb43c, #ffe9a8);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 6px rgb(255 200 100 / .55));
  animation: mw-flow 3.6s linear infinite;
}
@keyframes mw-flow {
  from { background-position: 0% 50%; }
  to   { background-position: 220% 50%; }
}

/* 난이도 3. 축하가 아니라 경고에 가깝게 보이는 것이 요점이다 —
 * 이 문장이 뜨는 날은 서버에 무언가 잘못된 것이 나타난 날이다. */
.chat-line.system.forbidden {
  padding: 8px 0;
  border-top: 1px solid rgb(255 90 90 / .25);
  border-bottom: 1px solid rgb(255 90 90 / .25);
  background: rgb(255 60 60 / .06);
}
.forbidden-text {
  background-image: linear-gradient(100deg, #ff6b6b, #ffd6d6, #c94bff, #ff6b6b);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 7px rgb(255 80 80 / .6));
  animation: mw-flow 3s linear infinite, fb-pulse 1.6s ease-in-out infinite;
}
@keyframes fb-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .7; }
}

@media (prefers-reduced-motion: reduce) {
  .masterwork-text, .forbidden-text { animation: none; }
}

/* ── 벼리는 화면 ─────────────────────────────────────────
 *
 * 상점가를 통째로 덮는다. 강화를 건 몇 초 동안 다른 것을 만질 수 있으면
 * 연출이 배경 애니메이션으로 내려앉기 때문이다. */

.forge-screen {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  padding: 40px 20px;
  text-align: center;
}
.forge-item { font-size: 19px; font-weight: 700; }

.forge-lines {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 9em;
  font-size: 14px;
  line-height: 1.7;
  color: var(--muted);
}
.forge-line { animation: forge-in .25s ease-out; }
.forge-line.hold { letter-spacing: .4em; color: var(--text-faint); }

/* 지금 쓰이는 중인 줄에만 커서가 깜빡인다 */
.forge-line[data-typing="yes"]::after {
  content: '▌';
  margin-left: 1px;
  animation: forge-caret .7s step-end infinite;
}
@keyframes forge-caret { 50% { opacity: 0; } }
@keyframes forge-in { from { opacity: 0; } to { opacity: 1; } }

.forge-result { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.forge-verdict { font-size: 17px; font-weight: 700; animation: forge-in .4s ease-out; }
.forge-verdict.safe { color: #7fd8a0; }
.forge-verdict.mild { color: var(--muted); }
.forge-verdict.risky { color: #ffb454; }
.forge-verdict.doom { color: #ff8a8a; }
.forge-result .clerk-line { margin: 0; max-width: 34em; }

@media (prefers-reduced-motion: reduce) {
  .forge-line, .forge-verdict { animation: none; }
}

/* 오픈 기념 — 재료를 받지 않는 동안의 안내 */
.free-event { color: #7fd8a0; font-weight: 600; }

/* ── 부르기 (@영애명) ────────────────────────────────────
 *
 * 스무 명이 함께 있는 채팅에서 누군가에게 말을 걸려면 이름을 부르는 수밖에 없는데,
 * 부른 쪽은 불렀다고 믿고 받을 쪽은 흘려보내기 쉽다. 색과 쪽지가 그 사이를 메운다. */

.mention { color: #8fd0ff; font-weight: 600; }
/* 나를 부른 이름은 더 세게. 남을 부른 것과 같은 색이면 훑어볼 때 걸리지 않는다 */
.mention.me { color: var(--gold); background: rgb(216 180 106 / .16); border-radius: 4px; padding: 0 3px; }

/* 나를 부른 줄은 통째로 표시가 난다 */
.chat-line.called {
  background: rgb(216 180 106 / .08);
  border-left: 2px solid var(--gold-dim);
  padding-left: 6px;
  margin-left: -8px;
  border-radius: 0 4px 4px 0;
}

/* 접속자 목록에서 이름을 눌러 부른다 */
.call-name { font: inherit; padding: 0; text-align: left; }
.call-name:hover { color: var(--gold); text-decoration: underline; }

/* ── 부름 쪽지 ───────────────────────────────────────────
 *
 * 채팅 줄에 색만 입히면 **다른 탭을 보고 있을 때는 아무것도 모른다.**
 * 화면 위로 잠깐 떠올랐다 사라져, 지금 보고 있는 곳이 어디든 눈에 걸리게 한다. */
.callout {
  position: fixed;
  left: 50%;
  top: 16px;
  transform: translateX(-50%);
  z-index: 60;
  max-width: min(92vw, 30em);
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--gold-dim);
  background: rgb(28 24 34 / .97);
  box-shadow: 0 6px 24px rgb(0 0 0 / .5);
  cursor: pointer;
  animation: callout-in .28s cubic-bezier(.2, .9, .3, 1.3);
}
.callout:hover { border-color: var(--gold); }
.callout-who { font-size: 12px; color: var(--gold); font-weight: 700; }
.callout-text {
  font-size: 13px;
  color: var(--text);
  /* 긴 말이 와도 쪽지가 화면을 덮지 않게 두 줄에서 자른다 */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
@keyframes callout-in {
  from { opacity: 0; transform: translate(-50%, -12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) { .callout { animation: none; } }

/* ── 강화 창의 무게 배분 ─────────────────────────────────
 *
 * 누를지 말지를 정하는 것은 두 가지다 — 될 확률과, 안 됐을 때 잃는 것.
 * 나머지 문장과 같은 크기로 두면 그 둘이 안내문 사이에 묻힌다. */

.enh-rate-line { display: flex; align-items: baseline; gap: 4px; flex-wrap: wrap; }

/* 요구하신 대로 1.5배. 확률이 낮을수록 경고에 가깝게 물든다 —
 * 같은 색으로 두면 90%와 1.4%가 화면에서 같은 무게를 갖는다. */
.enh-rate-big {
  font-family: var(--mono);
  font-size: 1.5em;
  font-weight: 800;
  letter-spacing: -.01em;
}
.enh-rate-big.ok   { color: #7fd8a0; }
.enh-rate-big.warn { color: #ffd166; }
.enh-rate-big.hot  { color: #ff9d4d; text-shadow: 0 0 8px rgb(255 140 60 / .35); }
.enh-rate-big.dire { color: #ff6b6b; text-shadow: 0 0 10px rgb(255 80 80 / .45); }
.enh-rate-big.bonus { color: #8fd0ff; }

/* 「실패할 경우」는 안내이고 그 뒤가 실제로 잃는 것이다.
 * 앞머리를 죽이고 뒤를 세워야 읽는 눈이 미끄러지지 않는다. */
.enh-penalty {
  font-weight: 800;
  font-size: 1.05em;
  color: #ff8a8a;
  text-decoration: underline;
  text-decoration-color: rgb(255 120 120 / .45);
  text-underline-offset: 3px;
}
.enh-risk.risky .enh-penalty { color: #ffb454; text-decoration-color: rgb(255 180 84 / .5); }
.enh-risk.doom .enh-penalty {
  color: #ff6b6b;
  text-shadow: 0 0 8px rgb(255 80 80 / .4);
  text-decoration-color: rgb(255 90 90 / .6);
}
.enh-risk .k { color: var(--muted); font-weight: 400; }

/* ── 강화 실패 알림 ──────────────────────────────────────
 *
 * 성공만 흘러다니면 «반드시 파괴»가 글자로만 남는다.
 * 구간이 깊을수록 안타까움이 커진다 — 물러남 · 부서짐 · 그리고 침묵. */

.chat-line.system.forge {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .02em;
  padding: 4px 0;
  text-align: center;
  word-break: keep-all;
}
.chat-line.system.forge .at { display: none; }

/* 알림 안의 장비 이름은 그 물건이 실제로 보이는 모습 그대로 그린다.
 * 등급 클래스(enh-w2 등)가 함께 붙으므로 색은 거기서 온다 —
 * 여기서는 주위 문장에 묻히지 않을 만큼만 띄운다. */
.forge-item-name { font-weight: 800; padding: 0 2px; }

.setback-text { color: #9aa7b8; }

.chat-line.system.shatter {
  border-top: 1px solid rgb(255 150 90 / .2);
  border-bottom: 1px solid rgb(255 150 90 / .2);
  background: rgb(255 140 70 / .05);
}
.shatter-text { color: #ffb454; text-shadow: 0 0 6px rgb(255 160 60 / .35); }

/* 난이도 3의 실패. 되돌릴 수 없는 것이 사라진 자리다 —
 * 축하 알림보다 조용하되, 조용해서 더 눈에 걸리게. */
.chat-line.system.requiem {
  padding: 9px 0;
  border-top: 1px solid rgb(150 150 170 / .25);
  border-bottom: 1px solid rgb(150 150 170 / .25);
  background: rgb(120 120 150 / .06);
}
.requiem-text {
  color: #c3c7d4;
  letter-spacing: .06em;
  animation: requiem-fade 3.4s ease-in-out infinite;
}
@keyframes requiem-fade {
  0%, 100% { opacity: 1; }
  50%      { opacity: .62; }
}
@media (prefers-reduced-motion: reduce) { .requiem-text { animation: none; } }

/* 난이도 1의 성공. 소박한 성취라 색만 옅게 얹는다 —
 * 여기서부터 요란하면 위의 두 등급이 올라설 자리가 없다. */
.forged-text { color: #a9d6c0; }

/* 강화난이도 N = 리스크허들 N. 어느 쪽으로 불러도 같은 표를 찾을 수 있게 함께 적는다 */
.grade-table .tier-name { white-space: nowrap; }
.grade-table .tier-alias { color: var(--text-faint); font-size: 11px; margin-left: 6px; }

/* ── 이름과 물건의 옷 ────────────────────────────────────
 *
 * 「아카데미의 목소리」에는 시스템 알림과 사람의 말이 뒤섞여 흐른다.
 * 그 안에서 누구의 이야기이고 무엇에 대한 이야기인지가 문장을 읽기 전에 보여야 한다.
 * 이름은 그 사람의 직업 색으로, 물건은 그 물건의 등급 색으로. */

/* 직업 색이 실렸으면 그것을 쓰고, 없으면 클래스 단계 색(tone-*)이 그대로 남는다 */
.chat-line .who[style*="--who-rgb"] { color: rgb(var(--who-rgb)); }
.sys-who {
  color: rgb(var(--who-rgb, 216 180 106));
  font-weight: 700;
  padding: 0 1px;
}
.sys-item { font-weight: 800; padding: 0 2px; }

/* 접속자 목록도 같은 옷을 입는다 — 채팅에서 본 색과 목록에서 본 색이 달라서는 안 된다 */
.call-name[style*="--who-rgb"] { color: rgb(var(--who-rgb)); }
.call-name.self { font: inherit; opacity: .75; }

/* ── 가운데 서는 제목 ────────────────────────────────────
 *
 * 수행 A지역 · 착용 장비 · 아카데미의 목소리. 본문이 가운데를 쓰는 넓은 판들이라
 * 제목만 왼쪽 끝에 붙어 있으면 눈이 판마다 왼쪽 위로 되돌아가야 한다.
 *
 * 접기 화살표는 자리에서 빼내 오른쪽에 붙인다 — 흐름에 두면 그 폭만큼
 * 제목이 왼쪽으로 밀려 "가운데인 듯 아닌" 자리에 선다. */
@media (min-width: 900px) {
  .panel.centered > h3 { text-align: center; }
  .panel.centered > h3 > .panel-toggle {
    position: relative;
    justify-content: center;
    text-align: center;
  }
  .panel.centered > h3 > .panel-toggle > .panel-chevron {
    position: absolute;
    right: 14px;
    top: 50%;
    transform: translateY(-50%);
  }
  .panel.centered.collapsed > h3 > .panel-toggle > .panel-chevron {
    transform: translateY(-50%) rotate(-90deg);
  }
}

/* 접속자 목록의 이름 칸. 왕관이 이름 앞에 서되 줄을 밀지 않는다 */
.list-item .who-cell { display: inline-flex; align-items: center; gap: 4px; min-width: 0; }

/* 박스 한 칸이 두 줄로 늘어날 때 높이가 어긋나지 않게 맞춘다 —
 * 「장비 +12」가 붙은 칸과 안 붙은 칸이 나란히 서면 아래가 들쭉날쭉해진다. */
.derived-cell { min-height: 58px; justify-content: center; }

/* ── 장비의 자리 ─────────────────────────────────────────
 *
 * 진열대에 마흔 벌이 늘어서면 이름만으로는 무기인지 신발인지 매번 읽어야 한다.
 * 운영자 완장과 같은 방식으로 이름 옆에 자리를 붙이고,
 * 카드 테두리도 같은 색으로 물들인다 — 색만 보고 원하는 자리를 골라낼 수 있게. */

.slot-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .02em;
  vertical-align: 2px;
  border: 1px solid rgb(var(--slot-rgb) / .45);
  background: rgb(var(--slot-rgb) / .14);
  color: rgb(var(--slot-rgb));
}

/* 자리마다의 색. 무기는 벼려진 쇠, 드레스는 옷감, 장갑은 손,
 * 신발은 흙길, 쥬얼리는 보석 — 그 물건이 하는 일에서 가져왔다. */
.slot-weapon  { --slot-rgb: 232 132 92; }
.slot-dress   { --slot-rgb: 224 130 186; }
.slot-gloves  { --slot-rgb: 122 196 148; }
.slot-shoes   { --slot-rgb: 218 178 96; }
.slot-jewelry { --slot-rgb: 130 168 232; }

/* 카드 테두리에도 같은 색이 배어난다. 원색을 그대로 쓰면 목록이 요란해지므로
 * 낮은 투명도로 얹어 «물들었다» 정도로만 둔다. */
.shop-card.slot-weapon,
.shop-card.slot-dress,
.shop-card.slot-gloves,
.shop-card.slot-shoes,
.shop-card.slot-jewelry {
  border-color: rgb(var(--slot-rgb) / .34);
  background: linear-gradient(160deg, rgb(var(--slot-rgb) / .07), transparent 60%);
}

/* ── 흐르는 강화 색 ──────────────────────────────────────
 *
 * 리스크허들을 넘길 때마다 색이 늘어난다 — 1에서 둘, 2에서 셋,
 * 3부터는 한 단계마다 하나씩. 개수가 정해져 있지 않아 그라데이션은
 * public/js/flow.js가 인라인으로 짓고, 여기서는 흐르게 하는 일만 맡는다.
 *
 * 전직 알림(rainbow)과 같은 방식이되 색이 그 물건의 것이라는 점이 다르다. */
.enh-flow {
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 800;
  animation-name: enh-flow-run;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes enh-flow-run {
  from { background-position: 0% 50%; }
  to   { background-position: 100% 50%; }
}

/* 움직임을 원치 않는 사람에게는 색만 남긴다 —
 * 여기서 흐름이 요점이긴 하지만, 그것 때문에 못 읽게 되어서는 안 된다. */
@media (prefers-reduced-motion: reduce) {
  .enh-flow { animation: none; background-position: 30% 50%; }
}

/* ── 희귀도 사다리 ──────────────────────────────────────
 *
 * 환상의 차고에서 차급(경차·중형차·대형차·슈퍼카)이 오를수록 이름이
 * 더 화려하게 빛나는 데 처음 쓰인다 (view-manor.js의 VEHICLE_GLOW_CLASS 참조).
 * 장비 등급에 붙는 날에도 그대로 재사용할 수 있도록 이름은 rare-*로 남겨둔다.
 * 색 → 테두리 → 번짐 → 움직임 순으로 쌓인다. */
.rare-common { color: #b9b7c9; }
.rare-uncommon {
  color: #63c9a8;
  text-shadow: 0 0 1px rgb(99 201 168 / .6);
}
.rare-rare {
  color: #6f9ffb;
  text-shadow: 0 0 1px rgb(111 159 251 / .7), 0 0 8px rgb(111 159 251 / .35);
}
.rare-epic {
  color: #b47ae0;
  text-shadow: 0 0 2px rgb(180 122 224 / .7), 0 0 12px rgb(180 122 224 / .4);
  animation: rare-pulse 2.8s ease-in-out infinite;
}
.rare-legendary {
  background-image: linear-gradient(100deg, #f0a93c, #ffe9a8, #ff9d4d, #f0a93c);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 6px rgb(240 169 60 / .5));
  animation: rare-flow 4s linear infinite;
}
@keyframes rare-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .74; } }
@keyframes rare-flow {
  from { background-position: 0% 50%; }
  to   { background-position: 220% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .rare-epic, .rare-legendary { animation: none; }
}

/* ── 지급 입력칸 ─────────────────────────────────────────
 * 숫자칸이 둘 나란히 있으면 어느 쪽이 개수인지 매번 헷갈린다 —
 * 잘못 넣으면 +1짜리를 열네 개 지급하게 된다. */
.field { display: flex; flex-direction: column; gap: 3px; }
.field-name { font-size: 10px; color: var(--text-faint); letter-spacing: .04em; }

/* ── 지급 미리보기 ───────────────────────────────────────
 * 지급은 되돌리기가 번거롭다. 누르기 전에 무엇이 만들어지는지 보여준다. */
.gear-preview {
  margin-top: 12px;
  padding: 12px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--gold-dim);
  background: rgb(255 255 255 / .03);
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.gp-line { font-size: 16px; font-weight: 700; display: flex; align-items: center; gap: 2px; flex-wrap: wrap; }
.gp-stats { font-size: 12px; color: var(--text-dim); }
.gp-meta { font-size: 11px; color: var(--text-faint); }
.gp-desc { font-size: 12px; color: var(--text-dim); line-height: 1.55; margin-top: 2px; }

/* 레벨이 색을 미는 것을 나란히 보여주는 줄 */
.shift-row { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 6px; }
.shift-cell { display: flex; align-items: center; gap: 6px; font-size: 13px; }

/* ── 프로필 카드 ─────────────────────────────────────────
 *
 * 채팅에서 이름에 손을 올리면 뜬다. 스무 명이 함께 있는 자리에서
 * 이름만으로는 누구인지 떠올리기 어렵다.
 *
 * 다른 것 위에 겹쳐 떠도 된다 — 잠깐 보고 손을 떼면 사라지는 것이므로,
 * 자리를 비워두느라 화면을 좁히는 것보다 낫다. */
.chat-line .who.hoverable { cursor: help; }

.profile-card {
  position: fixed;
  z-index: 70;
  width: 260px;
  padding: 12px;
  border-radius: var(--radius);
  border: 1px solid var(--gold-dim);
  /* 완전 불투명이어야 한다 — 조금이라도 비치면 뒤의 채팅 글자와 겹쳐 둘 다 못 읽는다 */
  background: #181520;
  box-shadow: 0 8px 28px rgb(0 0 0 / .6);
  display: flex;
  flex-direction: column;
  gap: 10px;
  animation: pc-in .16s ease-out;
  pointer-events: none;   /* 카드 위로 손이 들어가 깜빡이지 않게 */
}
/* 살짝 올라오기만 한다. opacity를 건드리면 그 동안 뒤가 비쳐 글자가 겹친다 */
@keyframes pc-in { from { transform: translateY(4px); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { .profile-card { animation: none; } }

.pc-head { display: flex; gap: 10px; align-items: center; }
.pc-portrait {
  width: 56px; height: 56px;
  border-radius: var(--radius);
  object-fit: cover;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  flex: 0 0 auto;
}
.pc-name-box { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pc-name { font-size: 14px; font-weight: 700; }
.pc-name .honorific { color: var(--gold); font-weight: 400; font-size: 11px; }
.pc-sub { font-size: 11px; color: var(--text-dim); }
.pc-level { font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

.profile-card .derived-row { margin-top: 0; gap: 6px; }
.profile-card .derived-cell { min-height: 44px; padding: 6px 4px; }
.profile-card .derived-value { font-size: 14px; }

.pc-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 10px; }
.pc-stat { display: flex; justify-content: space-between; gap: 6px; font-size: 12px; }
.pc-stat-value { color: var(--text); font-variant-numeric: tabular-nums; }

/* 감춘 사람. 초상화 자리에는 가면만 둔다 —
 * 비워두면 «불러오지 못했나»로 읽히지만, 가면이 있으면 감춘 것이 읽힌다. */
.hidden-card { align-items: center; text-align: center; width: 220px; }
.pc-portrait.dummy {
  display: grid;
  place-items: center;
  font-size: 26px;
  filter: grayscale(.4);
}
.pc-secret { font-size: 13px; color: var(--gold); font-weight: 600; }

/* 보관함 칸도 상점 카드와 같은 색으로 물든다 — 같은 물건이 두 화면에서
 * 다르게 보이면 «아까 그것이 맞나»를 이름으로만 확인하게 된다. */
.storage-card.slot-weapon,
.storage-card.slot-dress,
.storage-card.slot-gloves,
.storage-card.slot-shoes,
.storage-card.slot-jewelry {
  border-color: rgb(var(--slot-rgb) / .3);
  background: linear-gradient(160deg, rgb(var(--slot-rgb) / .06), transparent 60%);
}

/* ── 등교중인 영애 목록을 촘촘하게 ───────────────────────
 *
 * 한 사람이 두 줄을 쓰면 접속자가 늘수록 목록이 화면을 넘어간다.
 * 이름과 소속을 한 줄에 세우고 여백을 줄여 **한눈에 더 많이** 담는다. */
.list-item {
  align-items: baseline;
  padding: 4px 8px;
  margin-bottom: 2px;
  font-size: 12px;
}
.list-item .sub { font-size: 11px; white-space: nowrap; }
.list-item .who-cell { gap: 3px; }
.list-item .admin-crown { font-size: 11px; }

/* ── 차림 자랑 ───────────────────────────────────────────
 *
 * 강화는 걸고 잃는 놀이인데, 그렇게 얻은 것을 남에게 보일 자리가 없었다.
 * 한 줄로 끝내지 않고 자리마다 늘어놓는다 — 여섯을 갖춘 것 자체가 자랑거리다. */
.chat-line.system.flaunt {
  text-align: center;
  padding: 8px 0;
  border-top: 1px solid rgb(216 180 106 / .2);
  border-bottom: 1px solid rgb(216 180 106 / .2);
  background: rgb(216 180 106 / .05);
}
.chat-line.system.flaunt .at { display: none; }
.flaunt-text { color: #e8dcc0; font-weight: 700; font-size: 14px; }

/* 한 줄에 셋씩, 두 줄로.
 *
 * 자랑에 서는 자리는 장비 다섯에 탈것 하나로 여섯이다. 셋씩 끊으면 정확히 두 줄이 된다.
 * 예전에는 auto-fit이라 넓은 화면에서 여섯이 한 줄로 늘어섰는데, 그러면 칸마다 폭이
 * 170px까지 눌려 「+11 추진 장치가 달린 하이힐」이 세 줄로 접히거나 끝이 잘렸다. */
.flaunt-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px 14px;
  margin-top: 8px;
  text-align: left;
}

/* 좁은 화면에서는 셋이 들어가지 않는다. 둘로 줄여 이름이 접히지 않게 한다. */
@media (max-width: 640px) {
  .flaunt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 한 자리가 두 줄을 쓴다 — 위에 부위, 아래에 물건.
 *
 * 예전에는 한 줄에 나란히 뒀는데, 채팅 패널이 좁아 「+11 해를 가리는 양산」처럼
 * 이름이 길면 라벨이 자리를 먹은 만큼 물건 쪽이 밀려 **글자 중간에서 접혔다.**
 * 줄을 나누면 이름이 폭을 다 쓰므로 온전히 들어간다. */
.flaunt-cell {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 12px;
  min-width: 0;
}
.fc-slot {
  color: var(--text-faint);
  font-size: 10px;
  letter-spacing: .04em;
}
.flaunt-cell > span:last-child {
  font-weight: 600;
  min-width: 0;
  /* 한국어는 단어 중간이 아니라 어절에서 끊어야 읽힌다 */
  word-break: keep-all;
  overflow-wrap: break-word;
  line-height: 1.45;
}

/* 비어 있는 자리. 자리는 지키되 눈이 걸리지 않게 눌러 둔다 —
 * 갖춘 것과 같은 무게로 서면 «없음»이 자랑거리처럼 읽힌다. */
.flaunt-cell > .fc-empty {
  color: var(--text-faint);
  font-weight: 400;
}

/* 진열대 한 칸의 아래 줄 — 왼쪽에 요구 레벨, 오른쪽에 능력치.
 *
 * 살 수 있는 물건이라고 요구 레벨을 감추면 지금 걸친 것과 견줄 수 없다.
 * 레벨이 모자라면 .short가 붙어 붉게 뜬다 — 못 사는 이유가 버튼에만
 * 적혀 있으면, 목록을 훑는 동안에는 어느 것이 아직 이른지 알 수 없다. */
.gear-meta {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--line);
  align-items: baseline;
  gap: 10px;
}
.gear-meta .short { color: var(--danger); font-weight: 600; }

/* ══ 장비 한 칸 — 진열대와 보관함이 함께 쓴다 ═══════════════
 *
 * 두 개씩 늘어놓으면 한 칸의 폭이 절반이라 이름과 설명이 접힌다.
 * 훑는 속도보다 **한 벌을 제대로 읽는 것**이 먼저다 — 한 줄에 하나. */
.gear-list { display: flex; flex-direction: column; gap: 8px; }

.gear-card {
  position: relative;
  padding: 12px 14px;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
}
.gear-card.slot-weapon,
.gear-card.slot-dress,
.gear-card.slot-gloves,
.gear-card.slot-shoes,
.gear-card.slot-jewelry {
  border-color: rgb(var(--slot-rgb) / .34);
  background: linear-gradient(160deg, rgb(var(--slot-rgb) / .07), transparent 60%);
}
/* 지금 걸치고 있는 것은 목록에서 먼저 눈에 들어와야 한다 —
 * 왼쪽에 띠를 세워 «이건 몸에 있는 것»임을 색이 아니라 형태로 알린다. */
.gear-card.equipped {
  border-color: var(--gold-dim);
  box-shadow: inset 3px 0 0 var(--gold);
}

.gc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.gc-title { min-width: 0; display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 0; }
.gc-title > strong { font-size: 14px; word-break: keep-all; }
.gc-icon { margin-right: 5px; }

/* 이름 옆이 요구 레벨의 자리다. 목록을 훑을 때 가장 먼저 걸러내는 것이
 * «이걸 지금 쓸 수 있나»이므로, 이름과 한눈에 함께 읽혀야 한다. */
.gc-head > .gc-level {
  flex: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  white-space: nowrap;
}

/* 능력치는 값을 적는 줄로 내려왔다. 둘뿐이라 가로로 늘어놓아도 좁지 않고,
 * 값이 왼쪽 끝에 붙어야 카드를 세로로 훑을 때 자릿수가 나란히 선다. */
.gc-stats { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 14px; }
.gear-stat { display: flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.gs-label { font-size: 10px; color: var(--text-faint); letter-spacing: .03em; }
.gs-value {
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.gs-gain { font-size: 11px; font-weight: 700; color: var(--gold); }

.gc-desc {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-faint);
  word-break: keep-all;
}

.gc-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
  padding-top: 7px;
  border-top: 1px solid var(--line);
}
/* 못 사는·못 입는 이유가 버튼에만 적혀 있으면, 목록을 훑는 동안에는
 * 어느 것이 아직 이른지 알 수 없다. */
.gc-level.short { color: var(--danger); font-weight: 700; }
.gc-price { font-size: 13px; }

.gc-actions { display: flex; align-items: center; gap: 8px; margin-top: 10px; }

/* 이미 가진 것.
 *
 * 작은 회색 «이미 보유»는 진열대를 훑는 동안 지나친다 — 그리고 같은 것을 또 산다. */
.owned-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 700;
  color: #7fd6a6;
  border: 1px solid rgba(127, 214, 166, .45);
  background: rgba(127, 214, 166, .13);
  vertical-align: 1px;
}
.worn-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  color: var(--gold);
  border: 1px solid var(--gold-dim);
  background: rgba(216, 180, 106, .14);
  vertical-align: 1px;
}

/* 착용과 해제가 같은 자리에서 같은 모습이면, 목록을 훑다가 손이 먼저 나가
 * **입으려다 벗는다.** 걸치는 쪽은 크고 뚜렷하게, 벗는 쪽은 조용하게. */
.gc-actions .equip {
  flex: 1;
  min-height: 38px;
  font-size: 14px;
  font-weight: 700;
}
.gc-actions .unequip {
  flex: 1;
  min-height: 34px;
  font-size: 13px;
  opacity: .85;
}
/* 되돌릴 수 없는 것은 되돌릴 수 없어 보여야 한다. 작게 두어 손이 먼저
 * 가지 않게 하고, 색으로 무슨 버튼인지 미리 알린다. */
.gc-actions .scrap {
  flex: none;
  min-height: 28px;
  padding: 3px 10px;
  font-size: 11px;
  color: var(--danger);
  border-color: rgba(226, 104, 95, .4);
  background: rgba(226, 104, 95, .07);
}
.gc-actions .scrap:hover:not(:disabled) {
  border-color: var(--danger);
  background: rgba(226, 104, 95, .18);
  color: #ff8b82;
}

/* ── 정렬 고르개 ─────────────────────────────────────────
 *
 * 마흔 벌이 늘어서면 원하는 것을 찾는 방법이 사람마다 다르다.
 * 하나만 정해두면 나머지는 매번 눈으로 훑는다. */
.shop-heading-row { align-items: center; margin-bottom: 4px; }
.sort-field { display: inline-flex; align-items: center; gap: 6px; }
.sort-caption { font-size: 11px; color: var(--text-faint); }
.sort-select {
  padding: 5px 8px;
  border-radius: 6px;
  font-size: 12px;
  font-family: inherit;
  color: var(--text);
  background: var(--bg-sunken);
  border: 1px solid var(--line);
}
.sort-select:focus-visible { outline: 2px solid var(--gold-dim); outline-offset: 1px; }

/* ══ 모바일 탭 — 여섯 개의 상자 ═══════════════════════════
 *
 * 밑줄 하나로 지금 어디인지를 알리면, 걷거나 흔들리는 화면에서는
 * 그 2px을 찾느라 눈이 멈춘다. 그리고 여섯이 나란히 있으면
 * 글자를 읽어야 어디인지 알 수 있다 — **읽기 전에 손이 가야 한다.**
 *
 * 자리마다 색을 달리 주되 원색은 쓰지 않는다. 여섯 개가 전부 쨍하면
 * 어느 것도 도드라지지 않는다. 고르지 않은 것은 옅게 물들여 두고,
 * 고른 것만 그 색으로 채운다. */
.tabs {
  gap: 5px;
  padding: 7px 8px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg-raised);
}
.tab {
  flex: 1;
  min-width: 0;
  padding: 8px 2px;
  border-radius: 9px;
  border: 1px solid rgb(var(--tab-rgb) / .28);
  border-bottom: 1px solid rgb(var(--tab-rgb) / .28);
  background: rgb(var(--tab-rgb) / .09);
  color: rgb(var(--tab-rgb) / .82);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -.02em;
  transition: background .12s, color .12s, border-color .12s;
}
.tab.active {
  border-color: rgb(var(--tab-rgb) / .85);
  background: rgb(var(--tab-rgb) / .24);
  color: rgb(var(--tab-rgb));
  font-weight: 700;
  box-shadow: 0 0 0 1px rgb(var(--tab-rgb) / .3);
}
/* 손가락에는 hover가 없다. 눌린 순간이 보여야 한 번 더 누르지 않는다. */
.tab:active { transform: translateY(1px); filter: brightness(1.15); }

/* 그 자리가 하는 일에서 색을 가져왔다 —
 * 캐릭터는 살결, 수행은 불, 장비는 벼려진 쇠,
 * 채팅은 말소리, 접속은 등불, 수집품은 보석. */
.tab[data-tab='character'] { --tab-rgb: 224 148 190; }
.tab[data-tab='battle']    { --tab-rgb: 232 132  92; }
.tab[data-tab='gear']      { --tab-rgb: 196 200 214; }
.tab[data-tab='chat']      { --tab-rgb: 130 176 232; }
.tab[data-tab='presence']  { --tab-rgb: 218 178  96; }
.tab[data-tab='bag']       { --tab-rgb: 138 202 160; }

/* 직업 색이 화면을 물들여도 탭만은 제 색을 지킨다 —
 * 여섯 자리를 색으로 가르는 것이 이 줄의 일이기 때문이다. */
.tab.active { border-bottom-color: rgb(var(--tab-rgb) / .85); }
.tab .badge { background: var(--plum); }

/* 가장 좁은 화면에서는 글자가 접히기 전에 여백부터 줄인다. */
@media (max-width: 360px) {
  .tabs { gap: 3px; padding: 6px 5px; }
  .tab { font-size: 11px; padding: 7px 1px; }
}

/* 착용하기처럼 줄을 다 쓰는 버튼. 카드 폭의 4분의 1짜리가 왼쪽 구석에
 * 놓여 있으면 그 카드에서 할 일이 없어 보인다. */
.gc-actions > button:only-child { flex: 1; min-height: 38px; font-size: 14px; font-weight: 700; }

/* 구입만은 절반이다 — 사는 것은 한 번 멈춰 생각할 일이라
 * 줄 전체를 덮는 과녁으로 두지 않는다. */
.gc-actions > button.buy:only-child {
  flex: 0 0 50%;
  min-height: 34px;
  font-size: 13px;
}
/* «이미 있다»는 사려고 손이 가는 자리에서 읽혀야 한다. */
.buy-owned { font-size: 11px; font-weight: 600; color: #7fd6a6; }
button.buy:disabled .buy-owned { color: inherit; }

/* ══ 전투 로그 ═════════════════════════════════════════════
 *
 * 치명타일 때 문장 전체가 노랗게 물들면 **무엇이 터졌는지가 오히려 묻힌다** —
 * 「의 피해를 입혔습니다」는 어느 타격에나 똑같이 붙는 지문이기 때문이다.
 * 지문은 평소 색으로 두고, 숫자와 꼬리표만 크리티컬 색으로 세운다. */
.log-line.crit { color: var(--text); font-weight: 400; letter-spacing: 0; }
.log-line.crit .dmg {
  color: var(--crit);
  font-weight: 800;
  font-size: 1.12em;
  letter-spacing: .04em;
}
.log-line.crit .mark { color: var(--crit); font-weight: 700; }
.log-line.hit .dmg { color: var(--text); font-weight: 700; }
.log-line.hit .mark { color: var(--text-dim); font-size: .92em; }
.log-line .dmg { font-variant-numeric: tabular-nums; }

/* 상대가 나에게 어느 정도인가.
 *
 * 같은 Lv.30이라도 내가 20일 때와 50일 때는 전혀 다른 상대다.
 * 로그를 훑을 때 어느 판이 위험했는지 숫자를 읽지 않고도 잡히게 한다. */
.monster { font-weight: 600; }
.threat-trivial { color: #6f6d85; }              /* 상대가 안 된다 */
.threat-easy    { color: #7fbf96; }              /* 수월하다 */
.threat-even    { color: #c9c6da; }              /* 엇비슷하다 */
.threat-hard    { color: #e8a45c; }              /* 벅차다 */
.threat-deadly  { color: #e2685f; text-shadow: 0 0 7px rgba(226, 104, 95, .35); }
.log-sep .sep-level { color: var(--text-faint); font-size: .92em; }

/* ── 벼락치기 합산 정리 ──────────────────────────────────
 *
 * 열 판이면 백오십 줄이다. 몰아친 사람이 알고 싶은 것은
 * 판마다의 몸짓이 아니라 **그래서 얼마나 벌었는가**이다. */
.log-line.cram-head {
  margin-top: 4px;
  color: var(--text);
  font-weight: 700;
}
.log-line.cram-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 8px;
  margin-top: 3px;
  padding-left: 4px;
}
.cram-label {
  flex: 0 0 3.6em;
  color: var(--text-faint);
  font-size: 12px;
}
.cram-exp { color: var(--heal); font-variant-numeric: tabular-nums; }
.cram-share { color: var(--text-faint); font-size: 12px; }
.cram-sep { color: var(--text-faint); }
/* 몇 번이었나와 무엇을 했나에만 색을 준다.
 * 두 색은 그 일을 시킨 버튼에서 그대로 가져왔다 —
 * 횟수는 벼락치기의 번개빛, 접견은 찻물빛. */
.cram-count { color: rgb(236 178 72); font-weight: 800; font-variant-numeric: tabular-nums; }
.cram-deed  { color: rgb(108 186 160); font-weight: 700; }
.cram-loot { flex: 1; min-width: 0; line-height: 1.7; word-break: keep-all; }

/* 쓰러져 끊긴 일정에서 «몇 번째»만 따로 물들인다.
 * 예정 수는 연속접견의 번개빛(cram-count)을 그대로 쓰고, 이쪽은 무너진 자리라 붉은 기다. */
.fell-at { color: var(--damage); font-weight: 800; font-variant-numeric: tabular-nums; }

/* 레벨 도달 알림 — 클래스와 직업을 앞세우고, 레벨에 남은 거리를 색으로 싣는다.
 * 색은 서버가 정해 보낸다(인라인). 남은 거리를 아는 것은 클래스 상한을 쥔 쪽뿐이다. */
.sys-job { font-weight: 700; }
.sys-level { font-weight: 800; font-variant-numeric: tabular-nums; }

/* 벼락치기 — 경험치가 무엇이 되었는가.
 *
 * 비율 대신 레벨 두 개를 적는다. 몰아친 뒤에 알고 싶은 것은
 * «몇 할»이 아니라 «어디서 어디까지 왔는가»다. */
.cram-lv { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.cram-lv.up { color: var(--heal); }

/* 오른 스탯. 여섯을 다 세워 여러 번 몰아쳤을 때 서로 견줄 수 있게 하고,
 * 오르지 않은 것은 흐리게 두어 오른 쪽이 먼저 눈에 들어오게 한다. */
.cram-stats { gap: 2px 10px; }
.cram-stat { display: inline-flex; align-items: baseline; gap: 3px; font-size: 12px; white-space: nowrap; }
.cram-stat .cs-name { font-weight: 600; }
.cram-stat .cs-amount { font-variant-numeric: tabular-nums; }
.cram-stat.none { opacity: .3; }

/* 나에게만 보이는 안내 — 왜 안 나갔는지 알려주는 자리.
 * 남의 말과 섞이지 않게 흐린 기울임으로 두되, 읽히기는 해야 한다. */
.chat-line.notice { color: var(--text-faint); font-style: italic; }

/* ══ 미유미유Lab⭐ ═════════════════════════════════════════
 *
 * 첫 화면이 메뉴가 아니라 진행판이다 — 들어와서 가장 먼저 확인하는 것이
 * «다 됐나»이기 때문이다. 메뉴는 그 위에 두 줄로 얹는다. */
.lab-menu { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 10px 0 14px; }
.lab-menu-btn { --nav-rgb: 214 140 196; text-align: left; padding: 10px 12px; }
@media (max-width: 560px) { .lab-menu { grid-template-columns: 1fr; } }

/* 연구는 왼쪽, 제작은 오른쪽. 둘 다 위에서 아래로 쌓인다 */
.lab-board { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 640px) { .lab-board { grid-template-columns: 1fr; } }
.lab-col { min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.lab-col-head {
  margin: 0;
  font-size: 12px;
  letter-spacing: .04em;
  color: var(--gold);
}
.lab-idle { font-style: italic; }

.lab-job {
  padding: 9px 11px;
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
  background: var(--bg-sunken);
}
/* 다 된 것은 형태로 먼저 알린다 — 목록이 길어지면 색만으로는 놓친다 */
.lab-job.ready { border-color: var(--gold); box-shadow: inset 3px 0 0 var(--gold); }
.lab-remain { font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.lab-mats { margin-top: 3px; font-size: 11px; color: var(--text-faint); line-height: 1.5; word-break: keep-all; }
.lab-mats.lost { color: var(--danger); }

/* 남은 시간만 적으면 «얼마나 왔는지»가 안 보인다.
 * 5분짜리의 1분 남음과 30분짜리의 1분 남음은 전혀 다른 상황이다. */
.lab-bar {
  margin-top: 6px;
  height: 5px;
  border-radius: 3px;
  background: var(--line);
  overflow: hidden;
}
.lab-bar > span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #b678d0, #e6a2d8);
  transition: width .9s linear;
}
.lab-report { margin-top: 8px; width: 100%; }

/* ── 재료 표 ─────────────────────────────────────────────
 * 이름과 수량만 보여주므로 행간을 넓힐 이유가 없다.
 * 300종이 한 화면에 들어와야 훑을 수 있다. */
.lab-tools { align-items: center; margin: 8px 0; flex-wrap: wrap; gap: 6px; }
.lab-sort {
  padding: 3px 9px;
  font-size: 11px;
  border-radius: 6px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  color: var(--text-dim);
}
.lab-sort.on { border-color: var(--gold-dim); color: var(--gold); }

.lab-table { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.lab-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 8px;
  border-bottom: 1px solid var(--line-soft);
}
.lab-row:last-child { border-bottom: 0; }
.lab-row.on { background: rgba(214, 140, 196, .12); }
.lab-name { flex: 1; min-width: 0; font-size: 12px; font-weight: 600; word-break: keep-all; }
.lab-have { flex: none; font-size: 11px; color: var(--text-faint); }

.lab-stepper { flex: none; display: flex; align-items: center; gap: 2px; }
.lab-step {
  min-width: 26px;
  padding: 2px 4px;
  font-size: 10px;
  border-radius: 4px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  color: var(--text-dim);
}
.lab-step.edge { color: var(--gold); border-color: var(--gold-dim); }
.lab-qty {
  width: 46px;
  padding: 2px 4px;
  text-align: center;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  border-radius: 4px;
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--text);
}
.lab-actions { margin-top: 12px; gap: 8px; }
@media (max-width: 560px) {
  .lab-row { flex-wrap: wrap; row-gap: 4px; }
  .lab-name { flex: 1 0 60%; }
  .lab-stepper { margin-left: auto; }
}

/* ── 맡기기 직전 ─────────────────────────────────────────
 * 값과 잃을 것을 먼저 말한다. 누르고 나서 알게 하면
 * «환불 안 된다»는 말이 통보가 된다. */
.lab-speech {
  margin: 10px 0;
  padding: 12px 14px;
  border-left: 2px solid rgb(214 140 196);
  background: rgba(214, 140, 196, .08);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 13px;
  line-height: 1.75;
}
.lab-speech p { margin: 0; }
.lab-speech .lab-gap { height: 8px; }
.lab-odds { padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); }
.lab-chance { font-size: 18px; color: var(--crit); font-weight: 800; }
.lab-risks { margin-top: 6px; display: flex; flex-direction: column; gap: 2px; }
.lab-risk { font-size: 11px; }
.lab-destroy { color: var(--danger); font-variant-numeric: tabular-nums; }

/* ── 봉투 (뒷돈) ─────────────────────────────────────────
 *
 * 미유미유는 «안 받는다»고 하면서 손을 내미는 사람이다. 화면도 그래야 한다 —
 * 봉투는 처음부터 펼쳐져 있지 않고, 손님이 먼저 꺼내야 열린다. */

/* 꺼내는 버튼. **다른 버튼의 절반 높이다** —
 * 연구를 맡기는 «해줘.»와 같은 크기로 두면 반드시 거쳐야 하는 단계처럼 보인다.
 * 이건 곁길이고, 곁길은 곁길처럼 생겨야 한다. */
.lab-envelope-open { margin-top: 10px; }
.lab-bribe-open {
  padding: 4px 12px;
  font-size: 11px;
  line-height: 1.4;
  color: rgb(214 140 196);
  border-color: rgba(214, 140, 196, .4);
  background: rgba(214, 140, 196, .06);
}
.lab-bribe-open:hover:not(:disabled) { border-color: rgb(214 140 196); }
.lab-bribe-close { padding: 4px 12px; font-size: 11px; line-height: 1.4; }

.lab-envelope {
  margin-top: 10px;
  padding: 12px 14px;
  border: 1px dashed rgba(214, 140, 196, .45);
  border-radius: var(--radius);
  background: rgba(214, 140, 196, .05);
}
.lab-envelope > .clerk-line { margin: 0 0 10px; }

/* 세 단계. 사려는 것은 «금액»이 아니라 «단계»라서, 줄 자체가 버튼이다 */
.lab-tiers { display: flex; flex-direction: column; gap: 4px; }
.lab-tier {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  font-size: 12px;
  text-align: left;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  color: var(--text-dim);
}
.lab-tier:hover:not(:disabled) { border-color: rgb(214 140 196); }
.lab-tier.on {
  color: var(--text);
  border-color: rgba(130, 176, 232, .55);
  background: rgba(130, 176, 232, .10);
}
/* 가진 골드로 못 닿는 줄. 막지는 않는다 — 지금 못 산다는 것과
 * 살 수 없다는 것은 다르고, 얼마를 모아야 하는지는 보여야 한다. */
.lab-tier.poor { opacity: .55; }
.lab-tier-mark { color: rgb(130 176 232); font-size: 10px; }
.lab-tier.on .lab-tier-mark { color: var(--heal); }
.lab-tier-label { flex: 1; min-width: 0; word-break: keep-all; }
.lab-tier-amount { color: var(--gold); font-size: 11px; white-space: nowrap; }

.lab-notes { margin-top: 6px; font-size: 11px; line-height: 1.5; word-break: keep-all; }

/* 지폐. 누를 때마다 봉투에 «더해진다» */
.lab-bills { margin-top: 8px; gap: 4px; flex-wrap: wrap; }
.lab-bill {
  padding: 4px 8px;
  font-size: 11px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  color: var(--gold);
}
.lab-bill:hover:not(:disabled) { border-color: var(--gold-dim); }
.lab-bill:active:not(:disabled) { transform: translateY(1px); }
.lab-bill.edge { color: var(--text-faint); }
.lab-bill-input {
  width: 8em;
  padding: 4px 6px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  border-radius: 6px;
}
.lab-envelope-sum { margin-top: 10px; font-size: 12px; }
.lab-envelope-actions { margin-top: 10px; }

/* ── 봉투가 바꾼 값 ──────────────────────────────────────
 *
 * 원래 값을 지우지 않고 **그어서** 남긴다. 새 값만 보여주면
 * 방금 낸 돈이 무엇을 샀는지가 보이지 않는다. */
.bribe-was {
  text-decoration: line-through;
  color: var(--text-faint);
  font-weight: 400;
  opacity: .8;
}
.bribe-now { color: rgb(130 176 232); }

/* ── 결과 발표 ───────────────────────────────────────────*/
.lab-outcome { padding: 6px 0; }
.lab-verdict { font-size: 15px; font-weight: 700; color: var(--text-dim); text-align: center; padding: 14px 0; }
.lab-verdict.found { color: var(--heal); }
.lab-found {
  padding: 12px 14px;
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  background: rgba(216, 180, 106, .06);
}
.lab-formula { margin-top: 6px; font-size: 13px; line-height: 1.9; word-break: keep-all; }
.lab-plus { color: var(--text-faint); }
/* 아직 모르는 칸. 몇 칸이 남았는지는 보여준다 —
 * 얼마나 더 태워야 하는지 가늠할 수 있어야 한다. */
.lab-unknown { color: var(--text-faint); border-bottom: 1px dashed var(--line-strong); }

/* 재료는 알지만 몇 개인지 모르는 칸. 한 걸음이 더 남아 있다는 표시다 */
.lab-unknown-qty { color: var(--text-faint); border-bottom: 1px dashed var(--line-strong); font-weight: 700; }

/* 글자가 가려진 이름. 등급 색은 그대로 두고 자간만 벌려 «읽히지 않는 자리»임을 보인다 */
.lab-masked { letter-spacing: .08em; opacity: .82; cursor: help; }
.lab-back { margin-top: 10px; }

/* ── 제작 ────────────────────────────────────────────────*/
.lab-recipe {
  padding: 10px 12px;
  margin-bottom: 8px;
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
  background: var(--bg-sunken);
}
.lab-recipe.dim { opacity: .72; }

/* ── 개인 저택의 제조법 탭 ────────────────────────────────────*/
.recipe-list { display: flex; flex-direction: column; gap: 6px; }
.recipe-row {
  padding: 8px 11px;
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
  background: var(--bg-sunken);
}
/* 전부 알아낸 것은 형태로 먼저 알린다 — 목록이 길어지면 칸 수를 세게 된다 */
.recipe-row.done { border-color: var(--gold-dim); box-shadow: inset 3px 0 0 var(--gold); }
.recipe-pending {
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  background: rgba(216, 180, 106, .07);
}
.recipe-pending-row { margin-top: 6px; align-items: center; }

/* ── 벼락치기 묶음은 가운데 선다 ─────────────────────────
 *
 * 합산 정리는 판마다의 로그와 성격이 다르다 — 흐르는 기록이 아니라
 * **한 번 누른 몫의 결산**이다. 가운데로 모아 그 덩어리가 한눈에 잡히게 한다. */
.log-line.cram-head,
.log-line.cram-kills { text-align: center; }
.log-line.cram-row { justify-content: center; }
.log-line.cram-row .cram-label { flex: none; }
.cram-loot { flex: 0 1 auto; text-align: center; }

/* 누구를 이겼는가. 합계만 있으면 «무엇과 싸웠는지»가 통째로 사라진다 */
.log-line.cram-kills {
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.6;
  word-break: keep-all;
}
.cram-kill-n { color: var(--gold); font-variant-numeric: tabular-nums; }

/* 장비가 전투에서 실제로 거드는 몫.
 * 고유 수치는 그 물건이 «무엇인가»이고, 이 %는 «무엇을 하는가»다. */
.gs-share { font-size: 10px; color: var(--gold); font-weight: 600; }
/* 착용 칸에서 그 자리가 실제로 거드는 몫 */
.gear-share { color: var(--gold); font-weight: 700; }

/* 패널 제목줄에 붙는 조작. 제목은 가운데를 지키고 버튼만 오른쪽 끝에 얹는다 —
 * 흐름에 두면 그 폭만큼 제목이 밀려 «가운데인 듯 아닌» 자리에 선다. */
.panel > h3 { position: relative; }
.panel-action {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  padding: 3px 9px;
  font-size: 11px;
  border-radius: 6px;
  background: var(--bg-sunken);
  border: 1px solid var(--gold-dim);
  color: var(--gold);
}
.panel-action:hover:not(:disabled) { background: rgba(216, 180, 106, .16); }

/* ══ 미유미유Lab — 두 번째 손질 ═══════════════════════════
 *
 * 메뉴 버튼은 이 화면에서 가장 자주 누르는 자리다.
 * 한 줄짜리 작은 버튼이면 «무엇을 하는 곳인지»를 읽기 전에 손이 먼저 헤맨다. */
.lab-menu-btn {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 16px 18px;
  min-height: 84px;
  justify-content: center;
}
.lab-menu-title { font-size: 18px; font-weight: 700; letter-spacing: -.01em; }
.lab-menu-note {
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-faint);
  word-break: keep-all;
  white-space: normal;
}

/* 정렬은 넷이 나란히 선다. 색을 저마다 주면
 * 지금 무엇으로 보고 있는지가 글자를 읽기 전에 잡힌다. */
.lab-sort {
  border-color: rgb(var(--sort-rgb) / .35);
  color: rgb(var(--sort-rgb) / .85);
  background: rgb(var(--sort-rgb) / .08);
  font-weight: 600;
}
.lab-sort.on {
  border-color: rgb(var(--sort-rgb));
  background: rgb(var(--sort-rgb) / .22);
  color: rgb(var(--sort-rgb));
}

/* 이 재료가 걸린 제조법이 내게 몇 개인가.
 * 미완성이 곧 «여기서 나올 수 있다»는 뜻이고, 완성된 것은 이미 알아낼 게 없다. */
.lab-links { flex: none; display: inline-flex; gap: 4px; }
.lab-links b {
  padding: 0 5px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
}
.lab-open { color: #e8c05c; background: rgba(232, 192, 92, .14); border: 1px solid rgba(232, 192, 92, .35); }
.lab-done { color: #7fd6a6; background: rgba(127, 214, 166, .14); border: 1px solid rgba(127, 214, 166, .35); }

/* 가감 버튼 — 예전에는 버튼인지 빈칸인지 알 수 없었다.
 * 테두리와 바탕을 확실히 주고, 끝값(Min·max)은 색으로 갈라둔다. */
.lab-step {
  min-width: 30px;
  min-height: 24px;
  padding: 2px 6px;
  font-size: 11px;
  font-weight: 700;
  border-radius: 5px;
  background: var(--bg-raised);
  border: 1px solid var(--line-strong);
  color: var(--text);
}
.lab-step:hover:not(:disabled) { background: rgba(214, 140, 196, .2); border-color: rgb(214 140 196); }
.lab-step:active { transform: translateY(1px); }
.lab-step.edge {
  color: var(--gold);
  border-color: var(--gold-dim);
  background: rgba(216, 180, 106, .12);
}
.lab-qty {
  width: 54px;
  min-height: 24px;
  font-weight: 700;
  border-color: var(--line-strong);
}

/* 맡기기 직전 — 잃을 수 있는 것을 크게. 흘려 읽으면
 * 돌려받은 개수가 줄어 있는 것을 나중에야 알게 된다. */
.warn-word { color: var(--danger); font-weight: 800; }
.lab-loud {
  display: inline-block;
  font-size: 1.5em;
  line-height: 1.4;
  color: var(--danger);
  font-weight: 800;
}
.lab-time { color: var(--crit); font-weight: 700; }
.lab-fee { font-size: 1.15em; }
.lab-mutter { font-size: 11px; color: var(--text-faint); font-style: italic; }

/* 누르고 나서야 알게 된 것. 직접 닫을 때까지 남는다 */
.lab-alert {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  padding: 11px 13px;
  border-radius: var(--radius);
  border: 1px solid var(--danger);
  background: rgba(226, 104, 95, .1);
  color: var(--text);
  font-size: 13px;
  line-height: 1.5;
}
.lab-alert > span { flex: 1; word-break: keep-all; }
.lab-alert button { flex: none; }

.lab-job-actions { margin-top: 8px; gap: 6px; }
.lab-job-actions .lab-report { flex: 1; }
.lab-cancel { flex: none; font-size: 11px; padding: 4px 10px; color: var(--text-faint); }

/* ── 월드 이벤트 · 반복 공지 (관리자) ──────────────────────
 *
 * 서버 전체의 규칙을 바꾸는 자리다. 다른 관리자 도구와 달리 **되돌리기가 어려운 일**을
 * 다루므로, 지우는 버튼만은 눈에 띄게 위험한 색으로 채워 실수로 스치지 않게 한다.
 * 나머지는 조용히 둔다 — 모든 버튼이 소리치면 어느 것도 들리지 않는다. */

.world-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgb(255 255 255 / .015);
}

.world-row { display: flex; align-items: flex-start; gap: 12px; }
.world-label {
  flex: 0 0 68px;
  font-size: 12px;
  color: var(--muted);
  padding-top: 7px;
}
.world-row > input[type="text"],
.world-row > textarea { flex: 1; min-width: 0; }
.world-row > textarea { resize: vertical; line-height: 1.6; }

.world-inline {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}
.world-inline input[type="number"] { width: 92px; }
.world-inline input[type="datetime-local"] { min-width: 200px; }

/* 체크박스는 글자와 함께 눌리게 한다 — 12px 사각형만 겨냥하게 두지 않는다 */
.world-check {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--text-soft);
  cursor: pointer;
  user-select: none;
  /* 좁은 칸에 밀리면 «무 / 조 / 건»처럼 한 글자씩 접힌다. 접히느니 줄을 바꾼다 */
  white-space: nowrap;
  flex: none;
}
.world-check input { cursor: pointer; }

/* 「= 3일 2시간」처럼 입력에서 곧바로 계산되는 값. 적은 값이 아니라 읽어주는 값이다 */
.world-computed { font-size: 12px; color: var(--gold); font-variant-numeric: tabular-nums; }

.world-note { margin: -4px 0 0 80px; font-size: 11px; }
.world-actions { display: flex; gap: 8px; margin-top: 2px; }
.world-subhead { font-size: 12px; color: var(--muted); margin: 18px 0 8px; font-weight: 500; }

/* 지금 열려 있는 것. 표를 훑어 «진행 중»을 찾게 하지 않는다 */
.world-live {
  margin-bottom: 12px;
  padding: 9px 12px;
  border-radius: var(--radius);
  border: 1px solid rgb(255 180 84 / .35);
  background: rgb(255 180 84 / .08);
  color: #ffcf8a;
  font-size: 12px;
}

.table-scroll { overflow-x: auto; }
.admin-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.admin-table th, .admin-table td { padding: 7px 10px; text-align: left; vertical-align: middle; }
.admin-table th { color: var(--muted); font-weight: 500; border-bottom: 1px solid var(--line); }
.admin-table tbody tr { border-bottom: 1px solid rgb(255 255 255 / .04); }
.admin-table .nowrap { white-space: nowrap; }

/* 표가 좁은 칸에 들어가면 브라우저는 열을 한 글자 너비까지 줄여버린다.
 * «개 / 학 / 기 / 념»으로 접힌 이름은 읽을 수 없으므로, 표에 최소 너비를 주고
 * 모자라면 칸이 접히는 대신 감싼 상자가 옆으로 구른다. */
.world-table { min-width: 740px; }
.world-table th:nth-child(2), .world-table td:nth-child(2) { min-width: 96px; }
.world-table th:nth-child(3), .world-table td:nth-child(3) { min-width: 190px; }
.world-table tbody tr { border-left: 3px solid transparent; }
.world-table tbody tr.world-running { border-left-color: #ffb454; background: rgb(255 180 84 / .05); }
.world-table tbody tr.world-scheduled { border-left-color: #7fb3ff; }
/* 끝났거나 취소된 줄은 기록으로만 남는다. 눈이 지금 살아 있는 줄로 먼저 가게 눌러둔다 */
.world-table tbody tr.world-ended,
.world-table tbody tr.world-cancelled { opacity: .5; }

.world-phase { font-size: 11px; white-space: nowrap; }
.world-phase-running { color: #ffb454; }
.world-phase-scheduled { color: #7fb3ff; }
.world-phase-ended { color: var(--text-faint); }
.world-phase-cancelled { color: var(--text-faint); text-decoration: line-through; }

/* 공지 문장은 길 수 있다. 표를 옆으로 늘리지 않고 칸 안에서 접는다 */
.world-notice-text {
  max-width: 320px;
  white-space: pre-line;
  line-height: 1.5;
  color: var(--text-soft);
}

.world-buttons { display: flex; gap: 5px; justify-content: flex-end; white-space: nowrap; }
button.small { padding: 4px 9px; font-size: 11px; }

/* 삭제. 기록까지 사라지는 유일한 버튼이라 테두리만으로는 부족하다 —
 * 채워서 «누르면 되돌릴 수 없다»가 색으로 먼저 읽히게 한다. */
button.danger.small {
  border-color: var(--danger);
  background: rgb(226 104 95 / .18);
  color: #ffb3ad;
  font-weight: 600;
}
button.danger.small:hover:not(:disabled) {
  background: var(--danger);
  color: #1a1118;
  border-color: var(--danger);
}

@media (max-width: 640px) {
  .world-row { flex-direction: column; gap: 5px; }
  .world-label { flex: none; padding-top: 0; }
  .world-note { margin-left: 0; }
  .world-notice-text { max-width: 180px; }
}

/* ── 공방의 이벤트 알림 ────────────────────────────────────
 *
 * 확률과 값이 평소와 다른데 이유가 화면 어디에도 없으면,
 * 그것은 «오늘 운이 좋았다»로 읽히고 이벤트를 연 뜻이 사라진다. */
.event-banner {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  padding: 10px 14px;
  border-radius: var(--radius);
  border: 1px solid rgb(255 180 84 / .4);
  background: linear-gradient(100deg, rgb(255 180 84 / .14), rgb(255 180 84 / .04));
  font-size: 13px;
}
.event-banner .event-what { color: #ffcf8a; font-weight: 700; }
.event-banner .event-until { color: var(--text-faint); font-size: 11px; }

/* 이벤트가 걸린 확률. 평소 확률과 나란히 놓여도 어느 쪽이 지금 값인지 읽히게 */
.rate-event { color: #ffcf8a; font-weight: 700; }

/* ── 심야식당 브레이크타임 ────────────────────────────────
 *
 * 오마카세만 한 시간에 한 번이다. 눌러보고 나서야 듣는 것보다 문 앞에서 아는 편이 낫다. */
.diner-break {
  margin: 0 0 10px;
  padding: 9px 12px;
  border-radius: var(--radius);
  border: 1px solid rgb(226 104 95 / .35);
  background: rgb(226 104 95 / .08);
  color: #ffb3ad;
  font-size: 12px;
  text-align: center;
}
/* 쉬는 칸은 값이 아니라 남은 시간을 적는다. 눌리지 않는다는 것이 색으로 먼저 읽히게 */
.dish.resting { opacity: .55; }
.dish.resting .dish-name { text-decoration: line-through; text-decoration-color: rgb(255 255 255 / .3); }

/* 장비 카드의 «+274 (+0.5%)».
 *
 * 실수치가 먼저 눈에 들어와야 한다 — 사람이 고를 때 견주는 것은 점수지 비율이 아니다.
 * 비율은 그 점수가 어디서 나왔는지를 말해주는 각주라서 한 톤 낮춘다. */
.gs-points { color: var(--gold); font-weight: 700; font-variant-numeric: tabular-nums; }
.gs-pct { color: var(--text-faint); font-weight: 500; font-size: .92em; }

/* 강화 미리보기의 수치 줄. 두 줄이 될 수 있어 줄바꿈을 살린다 */
.enh-stat-line { white-space: pre-line; line-height: 1.6; }

/* 프로필의 «(6클래스)». 직업 호칭보다 한 톤 작고 옅게 —
 * 이름이 먼저 읽히고 번호는 그 옆에서 거들어야 한다. */
.pc-class { font-size: .85em; color: var(--text-faint); }

/* 프로필의 종족·직업 이름. 색은 서버가 정한 값을 인라인으로 받는다 */
.pc-race, .pc-job { font-weight: 500; }

/* ── 몬스터 팝업 ──────────────────────────────────────────
 *
 * 전투 로그에서 이름에 마우스를 올리면 뜬다. 로그 칸이 좁아 안쪽에 그리면 잘리므로
 * body에 붙이고 좌표로 세운다 — 다른 패널 위로 겹쳐도 괜찮다. */
/* 팝업이 딸린 이름은 **무엇이든** 점선을 깐다 — 몬스터 · 전리품 · 타고 있는 것.
 * 예전에는 몬스터에만 걸려 있어서, 전리품에 팝업이 붙은 뒤에도
 * «손을 올려볼 수 있다»는 사실이 화면에 보이지 않았다.
 * 표시가 종류마다 다르면 어디에 손을 올려야 하는지를 매번 시험해 봐야 한다. */
.has-tip { cursor: help; border-bottom: 1px dotted currentColor; }

.monster-tip {
  position: fixed;
  z-index: 60;
  width: min(280px, calc(100vw - 24px));
  padding: 11px 13px;
  border-radius: 10px;
  border: 1px solid var(--line-strong);
  background: var(--bg-sunken);
  box-shadow: 0 10px 34px -12px rgb(0 0 0 / .75);
  font-size: 12px;
  line-height: 1.6;
  pointer-events: none; /* 팝업 위로 마우스가 넘어가 깜빡이는 것을 막는다 */
}
.mt-head { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.mt-name { font-size: 13.5px; }
.mt-zone { font-size: 11px; color: var(--text-faint); }
.mt-lv { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; margin-top: 1px; }
.mt-flavor {
  margin: 7px 0 0;
  color: var(--text-soft);
  font-size: 11.5px;
  line-height: 1.65;
  word-break: keep-all;
}
.mt-drops { margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--line); }
.mt-drop-head { font-size: 11px; color: var(--muted); margin-bottom: 5px; font-variant-numeric: tabular-nums; }
.mt-none { font-size: 11px; color: var(--text-faint); }
.mt-drop-list { display: flex; flex-wrap: wrap; gap: 4px 9px; font-size: 11.5px; font-weight: 600; }

/* ── 전리품 팝업 ─────────────────────────────────────────
 *
 * 몬스터 팝업과 같은 상자를 쓰되 담기는 것이 적다 — 이름 · 설명 · 쓰이는 곳.
 * 폭을 조금 줄여 짧은 설명 한 줄이 넓은 상자 안에 외로이 뜨지 않게 한다. */
.item-tip { width: min(240px, calc(100vw - 24px)); }

/* «사용되는 제조법(3)». 설명과 성격이 다른 말이라 선 하나로 끊는다 —
 * 위는 이 물건이 무엇인가이고, 아래는 그래서 무엇을 할 수 있는가다. */
.mt-uses {
  margin-top: 9px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

/* 확인창의 덧붙임. 본문보다 한 톤 작게 — 잃는 것이 아니라 자리가 바뀐다는 안내다 */
.confirm-note { font-size: 13px; color: var(--muted); line-height: 1.7; margin: 10px 0 0; }
.confirm-note b { color: var(--text-soft); font-weight: 600; }

/* ── 제조법 열람 (관리자) ─────────────────────────────────
 *
 * 제조법은 플레이어에게 가려진 채로 주어지는 것이라, 운영자 화면에서는
 * 반대로 **한눈에 다 보여야** 한다. 재료 줄이 길어지므로 표에서 그 칸만
 * 줄바꿈을 허락하고, 나머지 칸은 숫자처럼 붙들어 둔다. */

.rb-filter {
  width: 100%;
  margin: 4px 0 14px;
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: rgb(255 255 255 / .03);
  color: var(--text);
  font-size: 13px;
}
.rb-filter:focus { outline: 2px solid var(--gold); outline-offset: 1px; }

.rb-table td, .rb-table th { vertical-align: top; }
.rb-table td.rb-inputs { white-space: normal; line-height: 1.7; }
.rb-table td.rid { color: var(--text-dim); }

/* 재료 하나하나는 희귀도 색으로 읽고, 개수는 색을 빼서 이름과 구분한다 */
.rb-qty { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.rb-plus { color: var(--line); }

/* 앞 단계 장비가 재료로 들어간 자리. 계보의 사슬이 눈에 보이게 밑줄을 긋는다 */
.rb-chain { border-bottom: 1px dashed currentColor; }

.rb-count { color: var(--text-dim); font-weight: 400; font-size: 12px; }
.rb-known { color: var(--gold); }
.rb-partial { color: var(--text-dim); }

/* 만들 수 있는데 상점에서도 파는 물건. 제조법이 헛도는 자리라 눈에 띄어야 한다.
 * 지금은 걸리는 것이 하나도 없고, 그것이 맞는 상태다. */
.rb-shop { color: #ffb454; font-size: 11px; }

/* ── ID 기억하기 ──────────────────────────────────────────
 *
 * 로그인 버튼 바로 위에 둔다. 누르기 전에 눈에 들어와야 하는 것이라
 * 링크들 사이로 내려보내면 늦다. 글씨를 라벨로 감싸 두어
 * 작은 네모를 정확히 겨누지 않아도 켜고 끌 수 있게 한다. */

.remember-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -4px 0 14px;
}
.remember-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--gold);
  cursor: pointer;
  flex: none;
}
.remember-row label {
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}
.remember-row input[type="checkbox"]:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ── 탈것 슬롯 ────────────────────────────────────────────
 *
 * 수행 시간과 같은 줄에 선다. 둘 다 «한 번에 얼마나 도는가»를 말하는 값이라
 * 떼어놓으면 서로 다른 이야기처럼 읽힌다. 좁은 화면에서는 아래로 접힌다. */

.bank-head {
  display: flex;
  align-items: baseline;
  gap: 4px 10px;
  /* **줄을 바꾸지 않는다.** 여기서 접히면 액션바가 한 줄만큼 두꺼워지고,
   * 그 한 줄은 고스란히 채팅이 보이는 높이에서 빠진다. 넘치면 끝을 자른다. */
  flex-wrap: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}

/* 탈것 줄은 무슨 일이 있어도 한 줄이다.
 * 이름은 늘 보이고, 뒤따르는 설명이 화면 폭에 맞춰 잘린다 —
 * 이름과 설명 중 하나를 버려야 한다면 버릴 것은 설명 쪽이다. */
.riding {
  font-size: 12px;
  color: var(--text-dim);
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.riding.afoot { color: var(--text-faint); }
.riding-name { color: var(--gold); font-weight: 700; }
/* 아직 탈것이 없을 때의 «[뚜벅이 영애]».
 * 진짜 탈것 이름과 같은 금색으로 두면 무언가를 타고 있는 것처럼 읽힌다.
 * 자리는 지키되 한 톤 눌러, «채우지 않은 자리»로 보이게 한다. */
.riding.afoot .riding-name { color: var(--text-dim); }

/* 남은 시간은 자릿수가 흔들리면 옆의 글자가 따라 움직인다.
 * 폭이 같은 숫자를 써서 초가 바뀌어도 줄이 떨리지 않게 한다. */
.riding-left { color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* 탈것의 남은 사용시간. 장비의 강화 수치와 달리 «줄어드는 것»이라
 * 숫자만으로는 얼마나 남았는지 가늠이 안 된다. 막대를 함께 세운다. */
.mount-life { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.meter.mount {
  flex: 1 1 80px;
  height: 4px;
  min-width: 60px;
  border-radius: 2px;
  background: var(--line-soft);
  overflow: hidden;
}
.meter.mount > span { display: block; height: 100%; background: var(--gold-dim); }

/* ── 관리자: 경험치 · 레벨 미리보기 ──
   적용하기 전에 «어디로 가는가»를 한눈에 보여주는 자리.
   오르는 쪽과 내려가는 쪽의 색을 나눈다 — 부호 하나를 잘못 보고 누르는 일이 실제로 있다. */
.level-preview {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgb(255 255 255 / .03);
}
.lv-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  font-size: 15px;
}
.lv-from { color: var(--text-faint); }
.lv-arrow { font-size: 13px; }
.lv-to { font-weight: 700; font-size: 17px; }
.lv-up { color: var(--gold); }
.lv-down { color: var(--danger); }
.lv-flat { color: var(--text-faint); }
.lv-meta { margin-top: 6px; font-size: 12px; color: var(--text-dim); }
.lv-stats { margin-top: 4px; font-size: 12px; color: var(--secondary-stat); }
.lv-warn { margin-top: 8px; font-size: 12px; color: var(--danger); }

/* 체크박스와 설명을 한 덩어리로 묶어 어느 쪽을 눌러도 켜지게 한다 */
.check-line {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}
/* 전역 input 규칙이 width: 100%를 물려주어 체크박스가 423px까지 늘어난다.
   늘어난 만큼 설명 글자가 오른쪽으로 밀려나므로 여기서 제 크기로 돌려놓는다. */
.check-line input {
  flex: 0 0 auto;
  width: auto;
  min-width: 0;
  margin: 0;
  cursor: pointer;
}

/* 영향력이 0.1%에 못 미쳐 숫자가 사라진 자리.
   빈칸으로 두면 «못 불러왔다»와 구분되지 않으므로 말로 채운다. */
.gs-token {
  color: var(--text-faint);
  font-style: italic;
  letter-spacing: .02em;
}

/* 히든 자리가 차 있을 때. 조건 미달과 같은 «잠김»으로 보이면 안 된다 —
   내가 못 채운 것과 남이 앉은 것은 해야 할 일이 완전히 다르다. */
.seat-tag {
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--danger);
  border-radius: 3px;
  font-size: 10px;
  color: var(--danger);
  white-space: nowrap;
}
/* 관리자: 재화 잔액 — 지금 값과 도착할 값.
   «얼마를 주느냐»보다 «얼마가 되느냐»가 중요한 값이라 늘 세워둔다. */
.currency-balance { margin-top: 12px; font-size: 13px; }
.cur-now { color: var(--text-dim); }
.cur-now strong { color: var(--gold); }
.cur-after { margin-top: 4px; }
.cur-after strong { color: var(--gold); font-size: 15px; }
.cur-up { color: var(--heal); font-weight: 600; }
.cur-down { color: var(--danger); font-weight: 600; }
.cur-clip { color: var(--danger); font-size: 12px; }

/* 개인 저택과 그 옆자리가 한 줄을 반씩 나눠 쓴다.
 * 옆자리는 던전 입구가 들어올 자리이고, 그때까지는 «공사중»이 서 있다. */
.manor-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

/* 저택 이름은 플레이어가 붙이는 것이라 길이를 미리 알 수 없다. 반 폭에서 넘치면
 * 줄을 바꾸되(고정 height를 주면 두 줄째가 버튼 밖으로 샌다) 낱말 안에서는 끊지 않는다. */
.manor-button {
  width: 100%;
  padding-top: 7px;
  padding-bottom: 7px;
  min-width: 0;
  white-space: normal;
  word-break: keep-all;
  line-height: 1.3;
  /* 바로 아래 «나가는 길» 넷과 같은 크기로 둔다. 예전에는 이 버튼만 15px이라
   * 혼자 도드라졌다 — 액션바에 나란히 선 버튼들은 같은 무게로 읽혀야 한다. */
  font-size: 11px;
}
@media (min-width: 560px) {
  /* 나가는 길이 13px로 커지는 폭. 여기서도 함께 커진다 */
  .manor-button { font-size: 13px; }
}

/* 아직 열지 않은 자리. 눌리지 않는다는 것이 형태로도 읽혀야 한다 —
   테두리를 점선으로 두어 «갖출 것이 남았다»를 알리는 빈 장비 칸과 같은 문법을 쓴다. */
.under-construction {
  border-style: dashed;
  color: var(--text-faint);
  cursor: default;
}
.under-construction:disabled { opacity: 1; }

/* ── 가게 탭과 보관함 탭 — 네 칸씩 여러 줄 ──────────────
 *
 * 다섯을 한 줄에 밀어 넣으면 좁은 화면에서 칸마다 폭이 눌려
 * «공도의 🍊농장»이나 «미유미유Lab⭐»이 두 줄로 접힌다.
 * 격자로 두면 칸 폭이 화면과 무관하게 넷으로 고정되어 접힐 일이 없다. */
.shop-tabs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.shop-tabs .tab {
  min-width: 0;
  white-space: normal;
  word-break: keep-all;
  line-height: 1.25;
  font-size: 12px;
}

/* 아직 들어오지 않은 자리. 개인 저택 옆의 «공사중»과 같은 문법을 쓴다 —
   점선 테두리로 «여기에 무언가 생긴다»를 형태로 알린다. */
.shop-tabs .vacant {
  color: var(--text-faint);
  border-style: dashed;
  cursor: default;
  font-size: 11px;
}
.shop-tabs .vacant:disabled { opacity: 1; }

/* ── 다차원 현실화 연구 ─────────────────────────────────
 *
 * 관측되지 않은 모래시계를 이 세계선에 붙드는 자리.
 * 확률 색은 공방의 강화와 같은 문법을 쓴다 — 따로 배울 것이 없어야 한다. */
.realize-list { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.realize-row {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px;
  background: rgb(255 255 255 / .02);
}
.realize-name { font-size: 14px; }
.realize-becomes { margin-top: 4px; font-size: 12px; }
.realize-real { color: var(--gold); font-weight: 600; }
.realize-spec {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 8px;
  font-size: 12px;
}
.realize-pct { font-size: 15px; }
.realize-pct.ok { color: var(--heal); }
.realize-pct.warn { color: var(--crit); }
.realize-pct.risky { color: #e8945c; }
.realize-pct.doom { color: var(--danger); text-shadow: 0 0 8px rgb(226 104 95 / .4); }

/* ── 다이버전스 미터 ────────────────────────────────────
 *
 * 이 화면의 주인공. 실패해도 잃는 것은 골드뿐이라, 사람이 보고 있는 것은
 * «되었나 안 되었나»가 아니라 «얼마나 왔나»다. */
.div-meter { margin: 10px 0 8px; }
.div-bar {
  height: 8px;
  border-radius: 4px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  overflow: hidden;
}
.div-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--plum), var(--gold));
  transition: width .35s ease;
}
.div-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 5px;
  font-size: 12px;
}
/* 여섯 자리 숫자가 이 물건의 문법이라, 자릿수가 흔들리지 않게 고정폭으로 둔다 */
.div-num { font-size: 15px; color: var(--gold); font-weight: 700; letter-spacing: .02em; }
.div-goal { color: var(--text-faint); }
.div-step { margin-left: auto; color: var(--text-dim); }
.div-surcharge { color: var(--danger); font-size: 11px; }

/* 프로필의 «주요 능력치» 한 줄.
   색은 전당에서 쓰는 것과 같다 — 주력은 색+굵게(.growth-primary),
   보조는 색만(.growth-secondary). 여기서 다시 정의하지 않는다. */
.growth-line { margin-top: 12px; font-size: 12px; line-height: 1.6; text-align: center; }
.growth-line .growth-sep { color: var(--text-faint); margin: 0 3px; }
/* 아직 열리지 않은 자리. 클래스가 오르면 물든다 */
.growth-line .growth-idle { color: var(--text-faint); }

/* 저택의 «착장 자랑하기».
 *
 * 기본 버튼 크기(15px)로는 좁은 화면에서 두 줄로 접혔다. 이 버튼은 옆의 안내문
 * («1 / 30칸 …», 12px)과 한 줄을 나눠 쓰는 자리라, 그쪽에 맞추는 편이 맞다 —
 * 나란히 선 것끼리 크기가 다르면 한 줄로 읽히지 않는다.
 * 낱말 안에서는 끊지 않고, 자리가 정 없으면 «착장 / 자랑하기»로만 나뉜다. */
.flaunt-btn {
  font-size: 12px;
  padding: 7px 10px;
  white-space: nowrap;
  flex: 0 0 auto;
}

/* 접속자 목록의 클래스 단계 — (2)본 파이터.
 *
 * 채팅의 «.chat-line .cls»와 같은 표시이므로 같은 모양으로 둔다.
 * 다만 여기서는 직업 이름에 <strong>붙여 쓴다</strong> — 사이를 띄우면
 * «(2)»가 혼자 선 값처럼 읽히는데, 이건 뒤따르는 이름을 꾸미는 말이다. */
.sub .cls {
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 11px;
}
