/* File    : app.css
   Desc    : 공통 토큰과 컴포넌트. 화면에서 색상값을 직접 쓰지 않는다 (docs/SCREENS.md)
   Date    : 2026-09-02
   Author  : p1n9u (Claude Opus 5의 도움을 받아 작성) */

:root {
  --bg: #f7f7f8;
  --surface: #ffffff;
  --text: #1a1c1e;
  --text-dim: #6b7076;
  --border: #dfe1e5;
  --accent: #2f6fed;
  --accent-text: #ffffff;
  --accent-soft: #eaf0fe;
  --danger: #c0392b;
  --danger-soft: #fdeceb;
  --ok: #12a150;
  --warn: #c2701c;
  /* 형광펜. **밝은 테마와 어두운 테마가 같은 색**이다 - 종이에 그은 형광펜은 어느
     조명에서도 노랗다.
     **옅고 투명하다** (2026-09-06 사용자 지정: "너무 밝다"). 칠하는 것이 아니라
     덧칠하는 것이라, 밑에 깔린 묶음 바탕(초록·빨강)과 글자색이 그대로 비친다. */
  --mark: rgba(255, 206, 0, .17);
  /* 묶음 머리 칸의 바탕. 벌어 온 것은 초록, 쓰거나 판 것은 빨강. */
  --band-in: rgba(18, 161, 80, .16);
  --band-out: rgba(192, 57, 43, .16);
  /* 표의 격자. 눈에 걸리지 않을 만큼만. */
  --grid: rgba(128, 134, 140, .22);
  --radius: 10px;
  /* 머리글·본문·바닥글이 함께 쓰는 한 폭. 화면마다 다르면 좌측 탭이 움직인다. */
  --frame: 1320px;
  --shadow: 0 1px 2px rgba(0, 0, 0, .06), 0 8px 24px rgba(0, 0, 0, .06);
}

/* **고르지 않았으면 다크다** (2026-09-18 사용자 지정). 예전에는 OS 설정을 따랐는데,
   이 사이트는 밤에 보스를 돌면서 보는 화면이라 기본이 밝으면 눈이 아프다.
   OS 가 라이트여도 여기는 다크로 연다 - 라이트를 쓰려면 ◐ 를 누르면 되고,
   그 선택은 localStorage 에 남아 다음에도 이긴다.

   **고른 다크도 이 규칙이 잡는다** - [data-theme="dark"] 역시
   :not([data-theme="light"]) 안에 들어온다. 같은 토큰을 두 벌 두면
   한쪽만 고치는 날이 온다. */
:root:not([data-theme="light"]) {
  --bg: #16181c;
  --surface: #1e2126;
  --text: #e8eaed;
  --text-dim: #9aa0a8;
  --border: #333840;
  --accent: #6ea0ff;
  --accent-text: #10131a;
  --accent-soft: #22293a;
  --danger: #ff8a80;
  --danger-soft: #33211f;
  --ok: #3ddc97;
  --warn: #e0a458;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.6 system-ui, -apple-system, "Segoe UI", "Malgun Gothic", sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); }

/* ── 인증 전 화면: 중앙 카드 한 장, 내비게이션 없음 ─────────────── */

.auth-wrap {
  /* 100vh 로 두면 푸터가 화면 밖으로 밀려 약관 링크를 스크롤해야 볼 수 있다.
     푸터 높이만큼 빼서 로그인 화면에서도 한눈에 들어오게 한다. */
  min-height: calc(100vh - 200px);
  display: grid;
  place-items: center;
  padding: 24px;
}

.auth-card {
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 32px 28px;
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}

.auth-brand img { width: 32px; height: 32px; border-radius: 8px; }
.auth-brand strong { font-size: 17px; letter-spacing: -.01em; }

.auth-sub {
  margin: 0 0 24px;
  color: var(--text-dim);
  font-size: 13px;
}

/* ── 폼 ────────────────────────────────────────────────────────── */

.field { margin-bottom: 16px; }

.field label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
}

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

.field input:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: transparent;
}

.btn {
  /* **글자를 상자 한가운데 둔다** (2026-09-21 사용자 지적: "텍스트가 박스의 우측
     상단으로 기울어 있어"). <button> 은 브라우저가 알아서 가운데로 놓지만 <a> 는
     아니라, 키를 정해 둔 버튼(.btn-inline 44px)에서 글자가 위로 붙었다
     (실측: 위 3px · 아래 21px). */
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%;
  padding: 11px 14px;
  background: var(--accent);
  color: var(--accent-text);
  border: 0;
  border-radius: 8px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.btn:hover { filter: brightness(1.06); }

.btn-quiet {
  width: auto;
  background: transparent;
  color: var(--text-dim);
  border: 1px solid var(--border);
  font-weight: 500;
  padding: 7px 12px;
}

/* 오류는 필드 옆이 아니라 폼 상단에 한 줄로 모은다. 화면이 흔들리지 않는다. */
.form-error {
  display: flex;
  gap: 8px;
  margin-bottom: 18px;
  padding: 10px 12px;
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: 8px;
  font-size: 13px;
}

/* ── 인증 후 화면: 공통 헤더 + 본문 ────────────────────────────── */

.header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.header-inner {
  max-width: var(--frame);
  margin: 0 auto;
  padding: 0 20px;
  height: 56px;
  display: flex;
  align-items: center;
  gap: 20px;
}

.logo {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  text-decoration: none;
  color: var(--text);
}

.logo img { width: 24px; height: 24px; border-radius: 6px; }

.nav { display: flex; gap: 2px; margin-left: 8px; }

.nav a {
  padding: 6px 12px;
  border-radius: 7px;
  text-decoration: none;
  color: var(--text-dim);
  font-size: 14px;
}

.nav a:hover { background: var(--bg); color: var(--text); }
.nav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

.header-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.who { font-size: 13px; color: var(--text-dim); }

.main { max-width: 1040px; margin: 0 auto; padding: 28px 20px 64px; }

.page-title { margin: 0 0 4px; font-size: 21px; letter-spacing: -.01em; }
.page-sub { margin: 0 0 24px; color: var(--text-dim); font-size: 13px; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
}

.card h2 { margin: 0 0 12px; font-size: 15px; }

.grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* 아직 내용이 없는 자리. 무엇이 들어올지 적어두면 다음 작업이 명확해진다. */
.placeholder {
  color: var(--text-dim);
  font-size: 13px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 20px;
  text-align: center;
}

.theme-toggle {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: 7px;
  padding: 6px 9px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  line-height: 1;
}

.theme-toggle:hover { color: var(--text); }

/* ── 넥슨 연동 화면 ─────────────────────────────────────────────── */

.auth-card-wide { max-width: 420px; }

/* 로그아웃 폼이 카드 밖에 있으므로 세로로 쌓는다 (form 중첩 금지 때문). */
.auth-wrap { grid-auto-flow: row; align-content: center; justify-items: center; }

.field-help {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--text-dim);
}

.auth-foot { margin-top: 16px; text-align: center; }

.btn-link {
  background: none;
  border: 0;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  text-decoration: underline;
}

.btn-link:hover { color: var(--text); }

/* ── 대표 캐릭터 카드 ───────────────────────────────────────────── */

.char-card {
  display: flex;
  gap: 20px;
  align-items: center;
  margin-bottom: 16px;
}

/* 넥슨 캐릭터 이미지는 300x300 캔버스인데 캐릭터는 그 안에서 80x75 정도라
   그대로 두면 아주 작게 보인다. 캐릭터 주변만 잘라내 확대한다.
   focus/window 는 실측값이다 - 캐릭터 6명의 실제 이미지를 재서 정했다.
   (docs/SCREENS.md 참고. 임의로 바꾸면 캐릭터가 잘리거나 치우친다.) */
.char-figure {
  --canvas: 300;   /* 넥슨 원본 캔버스 한 변 */
  --focus-x: 152;  /* 캐릭터 중심 x (측정 범위 146~158) */
  --focus-y: 164;  /* 캐릭터 중심 y (측정 범위 162~166) */
  --window: 120;   /* 잘라낼 원본 영역. 키우면 배율이 낮아진다 */
  --box: 180;      /* 화면에 보일 크기(px) */

  position: relative;
  width: calc(var(--box) * 1px);
  height: calc(var(--box) * 1px);
  overflow: hidden;
  background: var(--bg);
  border-radius: var(--radius);
  flex-shrink: 0;
}

.char-figure img {
  position: absolute;
  width: calc(var(--canvas) * var(--box) / var(--window) * 1px);
  height: calc(var(--canvas) * var(--box) / var(--window) * 1px);
  left: calc((var(--box) / 2 - var(--focus-x) * var(--box) / var(--window)) * 1px);
  top: calc((var(--box) / 2 - var(--focus-y) * var(--box) / var(--window)) * 1px);
  max-width: none;
}

/* 이미지가 없는 캐릭터도 있다(넥슨이 안 주는 경우). 칸이 무너지지 않게 자리를 채운다. */
.char-figure-empty {
  display: grid;
  place-items: center;
  color: var(--text-dim);
  font-size: 12px;
}

@media (max-width: 520px) {
  .char-figure { --box: 140; }
}

.char-body { min-width: 0; }
.char-name { margin: 0 0 2px; font-size: 18px; }
.char-meta { margin: 0 0 14px; color: var(--text-dim); font-size: 13px; }
.char-dim { color: var(--text-dim); font-size: 12px; margin: 10px 0 0; }

.char-stats { display: flex; gap: 24px; margin: 0; flex-wrap: wrap; }
.char-stats div { margin: 0; }
.char-stats dt { font-size: 12px; color: var(--text-dim); margin-bottom: 2px; }
.char-stats dd { margin: 0; font-size: 17px; font-weight: 600; }

@media (max-width: 520px) {
  .char-card { flex-direction: column; align-items: flex-start; }
}

/* ── 단계 표시 ──────────────────────────────────────────────────── */

.steps {
  display: flex;
  gap: 8px;
  list-style: none;
  margin: 0 0 22px;
  padding: 0;
  counter-reset: step;
  font-size: 12px;
}

.steps li {
  flex: 1;
  counter-increment: step;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  color: var(--text-dim);
  text-align: center;
}

.steps li::before { content: counter(step) ". "; }
.steps .step-on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.steps .step-done { color: var(--text); }
.steps .step-done::before { content: "\2713 "; }

/* ── 선택 상자 ──────────────────────────────────────────────────── */

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

.field select:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: transparent;
}

/* ── 좌측 탭 레이아웃 ───────────────────────────────────────────── */

.shell {
  max-width: var(--frame);
  margin: 0 auto;
  padding: 24px 20px 64px;
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}

/* 보스 화면은 오른쪽 250px 패널까지 쓰지만, 다른 화면의 본문은 예전 폭 그대로 둔다.
   그러지 않으면 폼과 카드가 쓸데없이 늘어난다.
   **남는 자리는 좌우로 나눈다** - 왼쪽에 붙여 두면 넓은 화면에서 한쪽으로 쏠려
   눈에 잘 안 들어온다 (2026-09-03 사용자 지적). */
.content { max-width: 800px; margin-inline: auto; }
.shell:has(.boss-layout) .content { max-width: none; }
/* **표가 있는 화면은 폭을 채운다** (2026-09-16 사용자 지적: "접고펼칠때 사이즈가 달라서
   거슬려"). margin-inline:auto 는 그리드 항목을 fit-content 로 줄이므로, 본문 폭이
   안쪽 내용에 끌려다닌다 - 카드를 펼치면 폭이 넓어져 표가 좌우로 들썩였다.
   (실측: 접힘 992 -> 펼침 1072) 폭을 채워 두면 접든 펴든 자리가 그대로고,
   물욕정산과 물욕 히스토리의 표 폭도 같아진다.

   **800px 한도도 함께 푼다** (2026-09-18 점검). FHD 에서 재 보니 본문 폭이 화면마다
   1641 · 1332 · 1098 · 800 · 707 로 다섯 가지였다 - 표가 주인공인 화면인데 800 에
   묶여 있던 물욕 히스토리 · 빚정산 · 빚 히스토리 · 캐릭별 통계가 나머지 표 화면과
   어긋났다. 규칙은 하나다: **표는 폭을 채우고, 폼은 800 에 묶는다.** */
.shell:has(.ltable) .content { max-width: none; width: 100%; }
/* 대시보드는 칸을 넷씩 늘어놓는다. 800 으로는 한 줄에 셋뿐이라 넷째가 혼자 내려간다.
   틀(1320) 안에서 본문 폭을 푼다 - 머리 줄과 바닥글은 그대로 둔다. */
.shell:has(.dash-grid) .content { max-width: none; width: 100%; }
/* **달 표(13열)가 있는 화면은 틀을 넓힌다.** 기본 폭(800)으로는 표가 잘려 가로로
   굴려야 한다. 일일은 하루 목록과 나란히 두느라, 연간은 열이 그대로라 똑같이 넓다.
   머리 줄과 바닥글도 함께 넓혀야 어긋나 보이지 않는다. */
.shell:has(.lmonth-wrap) .content { max-width: none; }
/* **관리자 표는 좌우로 굴리지 않는다** (2026-09-18 사용자 지정). 열이 여덟~열둘이라
   본문 800px 안에서는 가로 막대가 붙었다(사용자 표가 790px 인데 자리는 756px).
   폭을 풀어 표가 제 폭을 갖게 한다. */
.shell:has(.admin-scroll) .content { max-width: none; width: 100%; }
/* **좌측 탭은 어느 화면에서도 같은 자리다** (2026-09-18 사용자 지정:
   "가계부-일일/연간 에 맞춰서 카테고리-탭이 안움직이도록").

   틀(--frame 1320)로 가운데 정렬을 하면, 달 표가 있는 화면만 폭을 풀었기 때문에
   일일·연간에서는 탭이 왼쪽 끝에 붙고 다른 화면에서는 가운데로 끌려와 **화면을
   옮길 때마다 탭이 300px 씩 움직였다**(FHD 기준).

   그래서 **넓은 쪽(일일·연간)에 전부 맞춘다.** 로그인 후 화면은 머리글·본문·
   바닥글이 다 같이 폭을 채우고, 안쪽 내용 폭은 .content 규칙이 화면마다 정한다. */
body:has(.shell) .header-inner,
body:has(.shell) .site-foot,
.shell { max-width: none; }

.side {
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: sticky;
  top: 24px;
}

.side a {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 12px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-dim);
  font-size: 14px;
}

.side a:hover { background: var(--surface); color: var(--text); }

.side a[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

.side-icon { font-size: 13px; width: 14px; text-align: center; }

/* 좁은 화면에서는 위에 가로로 눕힌다. 세로 탭이 본문을 밀어내면 못 쓴다. */
@media (max-width: 720px) {
  .shell { grid-template-columns: 1fr; gap: 16px; }
  .side { flex-direction: row; position: static; overflow-x: auto; }
  .side a { white-space: nowrap; }
}

/* ── 계정 설정 ──────────────────────────────────────────────────── */

.stack { display: flex; flex-direction: column; gap: 16px; }

.card-sub { margin: -4px 0 16px; font-size: 13px; color: var(--text); }
.muted { color: var(--text-dim); }

/* 결과는 해당 카드 안에만 띄운다. 화면 맨 위에 몰면 어느 칸의 결과인지 알 수 없다. */
/* 오류는 아니지만 그냥 지나치면 안 되는 안내 (예: 넥슨 호출 한도). */
/* 잘못된 것이 아니라 **알아 두면 좋은 것**. 경고(노랑)보다 조용하게 둔다. */
.form-note {
  display: flex; gap: 8px; align-items: flex-start;
  margin: 0 0 12px; padding: 10px 12px; border-radius: var(--radius);
  background: var(--accent-soft); color: var(--text);
  font-size: 12px; line-height: 1.6;
}
.form-note > span:first-child {
  flex: 0 0 auto; width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); color: var(--accent-text);
  font-size: 11px; font-weight: 700; text-align: center; line-height: 16px;
}

.form-warn {
  display: flex; align-items: flex-start; gap: 8px;
  margin-bottom: 16px; padding: 12px 14px; border-radius: var(--radius);
  border: 1px solid var(--danger); background: var(--danger-soft);
  color: var(--text); font-size: 13px; line-height: 1.5;
}
.form-warn span[aria-hidden] { color: var(--danger); font-weight: 700; }

/* **넥슨이 막혔다는 전역 한 줄** (2026-09-17 사용자 지정). 모든 화면의 본문 맨 위에
   선다. 오류가 아니라 "지금은 갱신을 못 한다" 는 안내라 붉게 칠하지 않는다 -
   기록은 멀쩡하고 적고 고치는 것도 그대로 된다. */
.api-alert {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 16px; padding: 10px 13px; border-radius: var(--radius);
  border: 1px solid var(--warn); background: var(--surface);
  color: var(--text-dim); font-size: 12px; line-height: 1.6;
}
.api-alert b { color: var(--text); margin-right: 4px; }
.api-alert > span[aria-hidden] {
  flex: 0 0 auto; width: 16px; height: 16px; border-radius: 50%;
  background: var(--warn); color: var(--accent-text);
  font-size: 11px; font-weight: 700; text-align: center; line-height: 16px;
}
.api-alert a { color: var(--accent); }

.form-notice {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  padding: 10px 12px;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 8px;
  font-size: 13px;
}

.row-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.row-form select { flex: 1; min-width: 220px; }

/* 줄에 끼어 앉는 버튼. **키를 못 박는다** - .btn-quiet 는 여백이 달라(7px) 같은
   줄의 .btn 보다 2px 컸다 (2026-09-18 점검에서 잼: 캐릭터 관리 46 · 조회 44). */
.btn-inline { width: auto; height: 44px; box-sizing: border-box; padding: 0 18px; }

@media (max-width: 520px) {
  .row-form select, .btn-inline { width: 100%; }
}

/* ── 좌측 탭 하위 메뉴 ──────────────────────────────────────────── */

.side-group { display: flex; flex-direction: column; gap: 2px; }

/* 부모는 이동할 화면이 없다. 링크가 아니라 라벨로 둬서 누를 수 있다고 오해하지 않게 한다. */
.side-label {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 12px;
  color: var(--text-dim);
  font-size: 14px;
  font-weight: 600;
}

.side-sub {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-left: 23px;
  padding-left: 10px;
  border-left: 1px solid var(--border);
}

.side-sub a { padding: 7px 10px; font-size: 13px; }

.side-group-open .side-label { color: var(--text); }

@media (max-width: 720px) {
  /* 좁은 화면에서는 가로 줄이라 세로 들여쓰기가 의미 없다. 나란히 눕힌다. */
  .side-group { flex-direction: row; align-items: center; gap: 2px; }
  .side-label { display: none; }
  .side-sub {
    flex-direction: row;
    margin-left: 0;
    padding-left: 8px;
    border-left: 1px solid var(--border);
  }
  .side-sub a { white-space: nowrap; }
}

/* ── 미구현 자리 ────────────────────────────────────────────────── */

.placeholder-tall { padding: 32px 20px; text-align: left; }
.placeholder-title { margin: 0 0 12px; font-weight: 600; color: var(--text); }
.placeholder-list { margin: 8px 0 0; padding-left: 18px; }
.placeholder-list li { margin-bottom: 4px; }

/* ── 캐릭터 관리 ────────────────────────────────────────────────── */

.char-figure-sm { --box: 96; --window: 130; }

.row-form input { flex: 1; min-width: 200px; padding: 10px 12px;
  background: var(--bg); color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; font: inherit; }
.row-form input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }

/* 조회 결과 미리보기. 바로 저장하지 않고 확인 후 추가한다. */
.found {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 16px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  flex-wrap: wrap;
}

.found-body { flex: 1; min-width: 180px; }
.found-name { margin: 0 0 4px; font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.found-meta { margin: 0 0 6px; font-size: 13px; color: var(--text-dim); }
.found-note { margin: 0; font-size: 12px; color: var(--text-dim); }

.badge {
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 500;
}

.badge-own { border-color: transparent; background: var(--accent-soft); color: var(--accent); }

.char-list { list-style: none; margin: 0; padding: 0; }

.char-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}

.char-row:first-child { border-top: 0; }
.char-row-main { flex: 1; min-width: 200px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.char-row-name { font-weight: 600; }
.char-row-meta { font-size: 13px; color: var(--text-dim); }
.char-row-actions { display: flex; align-items: center; gap: 12px; }

.toggle {
  display: inline-block;
  padding: 6px 11px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

.toggle-on { border-color: transparent; background: var(--accent); color: var(--accent-text); font-weight: 600; }
.toggle-disabled { cursor: default; opacity: .6; }

/* 챌린저스 월드 표시. 주간 한도가 다르다는 것을 눈에 보이게 한다. */
.badge-season { border-color: transparent; background: var(--accent-soft); color: var(--accent); }


/* --- 주간보스 --------------------------------------------------------------
   캐릭터 카드 하나에 그 캐릭터가 도는 보스를 가로로 늘어놓는다.
   난이도 배지 색만 예외적으로 나무위키 표기를 그대로 쓴다 - 인게임/커뮤니티에서
   같은 색으로 굳어 있어, 무채색으로 바꾸면 오히려 못 알아본다. (docs/SCREENS.md) */

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* 최상단 요약. 왼쪽에 제목과 주차 이동, 가운데에 달성 수익, 오른쪽에 보상별 합계,
   아래에 클리어 진행 막대. 한 줄에서 읽히도록 격자로 자리를 고정한다. */
.summary {
  display: grid; gap: 16px 28px; align-items: center;
  grid-template-columns: minmax(210px, auto) minmax(220px, 1fr) auto;
  grid-template-areas:
    "title income tiles"
    "title progress tiles"
    "worlds worlds  worlds"
    "add   add      add"
    "found found    found";
}
.summary-title { grid-area: title; }
.summary-title h2 { margin: 0 0 8px; font-size: 19px; letter-spacing: -.02em; }

/* **한 줄에 놓이는 것은 키가 같아야 한다** (2026-09-18 사용자 지적: "여기 삐뚤빼뚤한거
   좀 정렬해주고"). 재 보니 넷이 제각각이었다 - 글자 딱지 33px, 화살표 28px(글씨가
   작다), 달력 33px, 범위 딱지 31px. 위아래로도 1~3px 씩 어긋나 있었다.

   키를 하나로 못 박고 **inline-flex 로 가운데 정렬**한다. 글씨 크기가 달라도(화살표
   10px · 범위 12px · 딱지 13px) 상자는 같은 키로 선다. */
.period-nav { display: flex; align-items: center; gap: 6px; margin-top: 10px; --tab-h: 32px; }
.period-tab {
  height: var(--tab-h); box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 14px; border-radius: 99px; font-size: 13px; font-weight: 600;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text-dim); text-decoration: none; white-space: nowrap;
}
a.period-tab:hover { color: var(--text); border-color: var(--text-dim); }
.period-tab[aria-current="page"] {
  border-color: transparent; background: var(--accent); color: var(--accent-text);
}
.period-range { margin: 8px 0 0; font-size: 12px; color: var(--text-dim); }
/* 갈 곳이 없는 탭. 눌러도 되는 것처럼 보이지 않게 한다. */
.period-tab-off { opacity: .45; cursor: default; }

.summary-income { grid-area: income; text-align: right; }
.summary-income, .summary-tiles { align-self: end; }
/* 달성 금액만 색을 쓴다. 이 화면에서 가장 먼저 봐야 할 숫자다. */
.summary-value { margin: 0; font-size: 26px; font-weight: 800; letter-spacing: -.02em; color: var(--ok); }
.summary-sub { margin: 2px 0 0; font-size: 12px; color: var(--text-dim); }
.summary-sub b { color: var(--text); font-weight: 600; }

.summary-tiles { grid-area: tiles; display: flex; gap: 0; margin: 0; }
.summary-tiles div {
  margin: 0; padding: 0 16px; text-align: center; border-left: 1px solid var(--border);
}
.summary-tiles div:first-child { border-left: 0; }
.summary-tiles dd { margin: 0; font-size: 15px; font-weight: 700; }
.summary-tiles dt { font-size: 11px; color: var(--text-dim); margin-top: 2px; }

.summary-progress { grid-area: progress; display: flex; align-items: center; gap: 12px; }
.bar {
  flex: 1; height: 8px; border-radius: 99px; overflow: hidden;
  background: var(--bg); border: 1px solid var(--border);
}
.bar span {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--ok));
}
.summary-progress-text { margin: 0; font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.summary-progress-text b { color: var(--ok); font-weight: 700; }
.summary-progress-percent { color: var(--text-dim); margin-left: 8px; }

.summary-add {
  grid-area: add; justify-content: flex-end;
  border-top: 1px solid var(--border); padding-top: 16px; margin-top: 2px;
}
.summary-add input { flex: 0 0 auto; min-width: 180px; }
.summary .found { grid-area: found; }

@media (max-width: 900px) {
  .summary { grid-template-columns: 1fr; grid-template-areas: "title" "income" "tiles" "progress" "worlds" "add" "found"; }
  .summary-income, .summary-tiles { text-align: left; justify-content: flex-start; }
  .summary-add { justify-content: flex-start; }
}

.wcard-list { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: 14px; }

/* 다른 계정 캐릭터를 가르는 선. 자동체크가 되는 쪽과 손으로 챙기는 쪽이라 다루는 법이 다르다. */
.wcard-divider {
  display: flex; align-items: center; gap: 12px;
  margin: 6px 2px 0; font-size: 12px; color: var(--text-dim);
}
.wcard-divider::before, .wcard-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}

.wcard {
  display: flex; align-items: stretch; gap: 16px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: 14px 16px;
}
.wcard-dragging { opacity: .5; border-style: dashed; }

.wcard-side { width: 118px; flex: 0 0 118px; position: relative; text-align: center; }
.wcard-grip {
  position: absolute; top: -4px; left: -6px; cursor: grab;
  color: var(--text-dim); font-size: 14px; line-height: 1;
}
.wcard-side .char-figure { margin: 0 auto 6px; }
.wcard-name { margin: 0; font-weight: 700; font-size: 15px; }
.wcard-meta { margin: 2px 0 6px; font-size: 11px; color: var(--text-dim); }
/* **다 끝냈는지 · 오늘이 마지막인지** (2026-09-05 사용자 지정).
   카드가 여섯 장 넘게 늘어서면 숫자만으로는 어느 캐릭터가 남았는지 안 보인다.
   끝낸 쪽은 조용히(초록 테두리), 남은 쪽은 마지막 날에만 눈에 띄게(빨간 테두리). */
.wcard-done { border-color: color-mix(in srgb, var(--ok) 45%, var(--border)); }
.wcard-last {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger) inset;
}

.wcard-flag {
  margin: 2px 0 0; padding: 1px 8px; border-radius: 999px;
  display: inline-block; font-size: 11px; font-weight: 700;
}
.wcard-flag-done { background: color-mix(in srgb, var(--ok) 16%, transparent); color: var(--ok); }
.wcard-flag-last { background: var(--danger-soft); color: var(--danger); }

/* 숫자 옆의 작은 표시. 색만으로 알리지 않으려고 기호를 함께 둔다. */
.tag-done { color: var(--ok); font-weight: 700; }
.tag-last { color: var(--danger); font-weight: 700; }

/* 캐릭터 카드의 숫자 넷. **가운데 정렬하지 않는다** - 줄마다 폭이 달라 어긋난다.
   라벨은 왼쪽, 값은 오른쪽에 붙이고 tabular-nums 로 자릿수를 맞춘다. */
.wcard-facts {
  margin: 8px 0 0; display: grid; grid-template-columns: auto 1fr;
  gap: 3px 8px; align-items: baseline; text-align: left; font-size: 11px;
}
.wcard-facts dt { color: var(--text-dim); white-space: nowrap; }
.wcard-facts dd {
  margin: 0; text-align: right; white-space: nowrap;
  font-variant-numeric: tabular-nums; color: var(--text-dim);
}
/* 받은 몫만 눈에 띄게. 크기는 그대로 두고 색과 굵기로만 세운다 - 줄 높이가
   들쭉날쭉해지면 맞춰 놓은 것이 도로 흐트러진다. */
.wcard-facts .wcard-earned { color: var(--accent); font-weight: 700; }
.wcard-facts .wcard-goal { color: var(--text-dim); }

/* 보스는 등록 한도가 12개다. 6칸 두 줄이면 한눈에 들어온다. 일곱째 칸은 시즌 보스
   자리로 비워 둔다 - 하나 때문에 셋째 줄을 만들면 카드만 길어진다.
   자리(grid-row/column)는 서버가 정해 인라인 style 로 온다. */
.wcard-bosses { flex: 1; min-width: 0; }
.boss-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 12px 10px;
}
/* 시즌 보스는 결정석이 아니라 한도 밖이다. 옆에 떨어져 있음을 선으로 알린다. */
.boss-cell-season { border-left: 1px dashed var(--border); padding-left: 8px; }

.boss-cell {
  position: relative; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}

/* 폼을 겹치지 않으려고 밖에 둔 것들. 자리를 차지하면 안 된다. */
.hidden-form { display: contents; }
.boss-toggle { margin: 0; }
.boss-icon {
  position: relative; width: 56px; height: 56px; padding: 0;
  border: 2px solid var(--border); border-radius: 12px; background: var(--bg);
  cursor: pointer; overflow: hidden; display: block;
}
.boss-icon img { width: 100%; height: 100%; object-fit: cover; display: block; filter: grayscale(.85) opacity(.62); }
.boss-icon-empty {
  display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;
  font-size: 13px; font-weight: 700; color: var(--text-dim);
}
.boss-icon:hover { border-color: var(--text-dim); }
.boss-icon:hover img { filter: grayscale(.2) opacity(.95); }

/* 잡은 보스만 색이 살아나고 체크 표시가 붙는다. 안 잡은 보스는 흑백이다. */
.boss-cell-done .boss-icon { border-color: var(--accent); }
.boss-cell-done .boss-icon img { filter: none; }
.boss-check {
  position: absolute; right: 0; bottom: 0; display: none;
  padding: 0 4px; font-size: 11px; font-weight: 700; line-height: 15px;
  background: var(--accent); color: var(--accent-text); border-radius: 8px 0 0 0;
}
.boss-cell-done .boss-check { display: block; }

.boss-cell-name {
  margin: 0; font-size: 11px; text-align: center; line-height: 1.2;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 7칸이라 자리가 좁다. 금액은 줄바꿈되면 셀 높이가 들쭉날쭉해져 격자가 무너진다. */
.boss-cell-income {
  margin: 0; font-size: 10px; font-weight: 600; color: var(--accent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.boss-cell-done .boss-cell-name { font-weight: 600; }

.boss-cell select, .party-input {
  width: 100%; padding: 3px 4px; font: inherit; font-size: 11px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text);
}
.party-input::placeholder { color: var(--text-dim); }
.boss-cell select:focus, .party-input:focus {
  outline: 2px solid var(--accent); outline-offset: -1px;
}
/* 잠긴 세팅은 읽는 값이지 고치는 값이 아니다. 눌러도 반응하지 않는 것을 보여 준다. */
.boss-cell select:disabled { opacity: .9; cursor: default; }

/* 파티원. 1인이면 "솔로" 만, 파티면 칩과 [+ 파티원] 버튼이 보인다. */
.party-box { width: 100%; display: flex; flex-direction: column; gap: 3px; align-items: center; }
.party-solo { font-size: 10px; color: var(--text-dim); }
.party-chips { display: flex; flex-wrap: wrap; gap: 3px; justify-content: center; }
.chip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 6px; border-radius: 99px; font-size: 10px;
  background: var(--accent-soft); color: var(--accent); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chip-drop {
  border: 0; background: none; color: inherit; cursor: pointer;
  font-size: 11px; line-height: 1; padding: 0;
}
/* 분배 표기. 블빵이면 "블빵", 순서면 그 주 차례를 적는다. 내 차례만 색이 붙는다. */
.party-tag {
  font-size: 10px; color: var(--text-dim); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.party-tag-me { color: var(--ok); font-weight: 700; }
.party-tag-other { color: var(--accent); font-weight: 700; }

/* **파티 전체를 셀에 적는다** (2026-09-18 사용자 지정). 기본은 펴 둔 상태이고,
   끄면 body.hide-party 가 붙어 예전처럼 차례 한 명만 남는다 - CSS 가 기본을
   맡으므로 자바스크립트가 늦어도 깜빡이지 않는다.

   칸이 좁다(실측 80px). **이름을 접지 않고 자른다** - 접으면 줄 수가 이름 길이에
   끌려다녀 카드 높이가 제멋대로가 되고, 닉네임이 글자 단위로 끊기면 누구인지
   알아볼 수도 없다. 전체 이름은 툴팁에 있다.

   셋 이상이면 **두 칸씩** 놓는다 - 여섯 명을 한 줄로 흘리면 다섯 줄(70px)이 되어
   카드가 54px 씩 길어졌다. 두 칸이면 세 줄이다 (2026-09-18 사용자 물음). */
.party-all {
  /* 9px 이다. 두 칸 폭이 37px 라 10px 에서는 네 글자가 잘렸다 - 한 단계 줄이니
     네 글자가 딱 들어간다(다섯 글자부터 잘린다). 보스 이름·금액이 주인공이고
     이 줄은 곁들이라 한 단계 작은 것이 오히려 읽기 낫다. */
  font-size: 9px; line-height: 1.45; text-align: center; color: var(--text-dim);
  max-width: 100%; display: grid; gap: 0 5px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.party-all > span {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 홀수면 마지막 하나가 한 칸을 통째로 쓴다 - 왼쪽에 붙어 혼자 떠 보이지 않게. */
.party-all > span:last-child:nth-child(odd) { grid-column: 1 / -1; }
.hide-party .party-all { display: none; }
/* 이번 차례. 색만으로 알리지 않도록 굵기도 함께 준다. */
.party-now { color: var(--accent); font-weight: 700; }
.party-now-me { color: var(--ok); }
.party-rest { opacity: .75; }

/* 켜고 끄는 스위치. 기간 줄 아래에 조용히 앉는다. */
.party-switch {
  display: inline-flex; align-items: center; gap: 6px; margin: 8px 0 0;
  font-size: 11px; color: var(--text-dim); cursor: pointer; user-select: none;
}
.party-switch input { width: auto; margin: 0; cursor: pointer; }
.party-switch:hover { color: var(--text); }

/* 수정하기 버튼은 수정모드에서 이 칸에 마우스를 올려야 나온다.
   늘 떠 있으면 격자가 버튼으로 덮인다. */
/* **버튼 뒤로 이름이 비치지 않게 한다** (2026-09-22 사용자 지적: "너무 뭉개져서 어색하게").
   예전에는 아래쪽에만 붙은 낮은 버튼(10px 글씨)이라, 파티원이 두세 줄이면 위아래로
   이름이 삐져나와 글자와 버튼이 겹쳐 보였다.
   칸 높이를 통째로 덮어 봤더니 3인 파티(이름 두 줄)만 버튼이 크고 낮아 옆 칸과
   어긋났다. 그래서 **높이는 22px 로 고정해 윗줄에 세우고**, 떠 있는 동안 아래 이름은
   감춘다(visibility 라 자리는 그대로 - 칸이 흔들리지 않는다). 글씨는 한 줄로 둔다. */
.party-add {
  position: absolute; left: 0; right: 0; top: 0;
  height: 22px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  padding: 0 4px; margin: 0; font: inherit; font-size: 11px; font-weight: 600;
  white-space: nowrap;
  border: 1px solid var(--accent); border-radius: 8px;
  background: var(--surface); color: var(--accent); cursor: pointer;
  opacity: 0; pointer-events: none;
}
.is-editing .party-box:hover .party-add,
.is-editing .party-add:focus-visible { opacity: 1; pointer-events: auto; }
.is-editing .party-box:hover > :not(.party-add),
.is-editing .party-box:has(.party-add:focus-visible) > :not(.party-add) { visibility: hidden; }
.party-box { position: relative; }

/* 팝업의 분배 방식 고르기 */
.dialog .field { margin-top: 20px; }
.dialog .row-form + .field { margin-top: 24px; }
.split-modes { display: flex; gap: 8px; }
.split-mode {
  flex: 1; padding: 8px 0; font: inherit; font-weight: 600;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text-dim); cursor: pointer;
}
.split-mode:hover { color: var(--text); }
.split-mode-on {
  border-color: transparent; background: var(--accent); color: var(--accent-text);
}
.party-me { color: var(--ok); font-weight: 700; }

/* 캐릭터 카드의 스케줄러 경고. 획득 메소량 바로 아래에 둔다. */
/* 월드가 바뀌어 어긋난 것. 경고보다 부드럽게 - 지금 당장 깨진 것은 아니고
   사람이 손대면 되는 일이다. 다만 무엇을 해야 하는지는 다 적는다. */
.wcard-stale {
  margin: 6px 0 0; padding: 6px 8px; border-radius: 8px;
  background: var(--danger-soft); color: var(--danger);
  font-size: 11px; line-height: 1.45; text-align: left;
}

.wcard-warning {
  margin: 8px 0 0; font-size: 11px; color: var(--danger); text-align: center;
  cursor: help;
}

.party-list { list-style: none; margin: 12px 0; padding: 0; }

/* 캐릭터 관리 목록. 순서와 삭제를 한 곳에 모은다. */
.manage-list { list-style: none; margin: 12px 0; padding: 0; }
.manage-list li {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 0; border-top: 1px solid var(--border); font-size: 13px;
}
.manage-list li:first-child { border-top: 0; }
.manage-name { display: flex; flex-direction: column; gap: 2px; }
.manage-name .muted { font-size: 11px; }
.manage-actions { display: flex; gap: 10px; white-space: nowrap; }
/* 삭제 확인은 그 줄에서 한다. 팝업을 닫지 않고 되돌릴 수 있어야 한다. */
.manage-confirm {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: var(--danger);
}
/* 관리 목록의 계정 구분선 */
.manage-divider {
  display: flex; align-items: center; gap: 10px;
  font-size: 11px; color: var(--text-dim); padding: 8px 0 4px;
}
.manage-divider::before, .manage-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.party-list li {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 0; border-top: 1px solid var(--border); font-size: 13px;
}
.party-list li:first-child { border-top: 0; }
.party-empty { color: var(--text-dim); justify-content: flex-start !important; }
.party-list-actions { display: flex; gap: 10px; }
.party-list-lead { display: flex; align-items: center; gap: 8px; }
/* 빼기는 순서 숫자 왼쪽의 x 다. 지우는 동작이라 눌렀을 때만 붉어진다. */
.party-drop {
  width: 18px; height: 18px; flex: 0 0 18px; padding: 0; line-height: 16px;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--surface); color: var(--text-dim); cursor: pointer; font-size: 12px;
}
.party-drop:hover { color: var(--danger); border-color: var(--danger); }
.party-drop-blank { border-color: transparent; background: none; cursor: default; }

/* 이름 고치기는 그 줄에 마우스를 올려야 나온다. 늘 떠 있으면 목록이 버튼으로 덮인다. */
.party-rename {
  border: 0; background: none; color: var(--accent); cursor: pointer;
  font: inherit; font-size: 11px; padding: 0 4px; opacity: 0;
}
.party-list li:hover .party-rename, .party-rename:focus-visible { opacity: 1; }
.party-rename-input {
  padding: 3px 6px; font: inherit; font-size: 13px;
  border: 1px solid var(--accent); border-radius: 6px;
  background: var(--surface); color: var(--text); max-width: 160px;
}

/* 순서 분배 안내. 잘못 보면 수익이 통째로 어긋나는 값이라 눈에 띄게 둔다. */
#party-dialog-sub { display: flex; flex-direction: column; gap: 2px; }
.party-turn-note { color: var(--danger); font-weight: 700; font-size: 13px; }

/* 적용을 눌렀는지 알 수 있게 한 줄로 알린다. 팝업은 닫지 않는다. */
.dialog-note {
  margin: 16px 0 0; padding: 8px 10px; border-radius: 8px;
  background: var(--accent-soft); color: var(--accent); font-size: 12px;
}

/* 지난 주에 잡지 않은 보스는 고칠 것이 없다. 수정모드에서도 열리지 않는다. */
.boss-cell-locked { opacity: .5; }
.is-editing .boss-cell-locked .party-add { display: none; }
.is-editing { border-color: var(--accent); }
.is-editing .boss-cell select { cursor: pointer; }

/* 난이도 배지. 색과 테두리는 나무위키 '강렬한 힘의 결정' 문서의 표기를 그대로 옮겼다.
   평소에는 배지(span)로 보여주고 수정모드에서만 드롭박스로 바꾼다 - select 는 브라우저가
   화살표와 여백을 얹어 배지처럼 보이지 않는다. */
.diff-badge {
  display: block; width: 100%; box-sizing: border-box;
  padding: 2px 0; border: 1px solid transparent; border-radius: 12px;
  font-size: 11px; font-weight: 700; letter-spacing: -.4px; text-align: center;
}
.diff-select {
  -webkit-appearance: none; appearance: none;
  font-weight: 700; text-align: center; border-radius: 12px; letter-spacing: -.4px;
  padding-left: 2px; padding-right: 2px;
}
/* 평소에는 배지만, 수정 중에는 드롭박스만 보인다. */
.wcard:not(.is-editing) .diff-select { display: none; }
.is-editing .diff-badge { display: none; }
.diff-easy    { background: linear-gradient(#888888, #666666); border-color: #bbbbbb; color: #ffffff; }
.diff-normal  { background: linear-gradient(#51B2CC, #319DBC); border-color: #44bbcc; color: #ffffff; }
.diff-hard    { background: linear-gradient(#bb4466, #aa3355); border-color: #cc5588; color: #ffffff; }
.diff-chaos   { background: #282828; border-color: #ffe1be; color: #ffe1be; }
.diff-extreme { background: #282828; border-color: #ee3355; color: #ee3355; }
.diff-select option { background: var(--surface); color: var(--text); font-weight: 400; }

/* 팝업에서 고른 결과는 **아직 저장 전**이다 (2026-09-06 사용자 지정).
   무엇이 들어오고 무엇이 빠질지 카드에서 먼저 보이고, [적용] 을 눌러야 저장된다. */
.boss-cell-drop { opacity: .4; }
.boss-cell-drop .boss-icon { border-style: dashed; }
.boss-cell-drop .boss-remove { display: none; }
.boss-cell-new .boss-icon { border-color: var(--accent); border-style: dashed; }
.boss-cell-new .boss-icon img { filter: none; }
.boss-pending {
  font-size: 10px; font-weight: 700; line-height: 1.4; text-align: center;
  padding: 0 5px; border-radius: 8px;
  background: var(--bg); color: var(--text-dim); border: 1px solid var(--border);
}
.boss-pending-add { color: var(--accent); border-color: var(--accent); }
.boss-pending-edit { color: var(--warn); border-color: var(--warn); }
.boss-cell-edit .boss-icon { border-color: var(--warn); border-style: dashed; }
/* 새로 고른 칸은 드롭박스가 없어 배지로 난이도를 보인다. 수정모드는 배지를 감추므로
   이 칸만 되살린다 - 안 그러면 무슨 난이도로 넣는지가 안 보인다. */
.is-editing .boss-cell-new .diff-badge { display: block; }
/* 카드 위 한 줄: 무엇이 바뀌는지와 아직 저장 전이라는 것. */
.pick-summary {
  margin: 0 0 8px; padding: 4px 10px;
  font-size: 12px; font-weight: 600; color: var(--accent);
  border: 1px dashed var(--accent); border-radius: 8px; background: var(--accent-soft);
}

/* 보스 빼기는 수정모드에서만 보인다. 평소에 보이면 잘못 누른다. */
.boss-remove { position: absolute; top: -7px; right: -3px; margin: 0; display: none; }
.is-editing .boss-remove { display: block; }
.boss-remove-btn {
  width: 18px; height: 18px; padding: 0; line-height: 16px;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--surface); color: var(--text-dim); cursor: pointer; font-size: 12px;
}
.boss-remove-btn:hover { color: var(--danger); border-color: var(--danger); }

.wcard-actions {
  width: 148px; flex: 0 0 148px; padding-left: 6px;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
.wcard-actions .toggle:disabled { cursor: default; opacity: .5; }
/* 전체완료 / 전체해제. 한 줄에 나란히 둔다 - 카드마다 자주 누르는 버튼이다.
   아래 안내와 붙으면 버튼 묶음이 어디까지인지 알기 어려워 사이를 벌린다. */
/* 액션 열이 stretch 라 직접 자식인 [수정]만 폭을 채우고 알약 둘은 글자 크기로 남아
   [수정]만 커 보였다. 알약도 열을 나눠 채우게 해서 크기를 맞춘다. */
.bulk-check { display: flex; gap: 6px; margin-bottom: 0; flex-wrap: nowrap; }
.bulk-check form { flex: 1; display: flex; }
.bulk-check .pill { width: 100%; }
.pill {
  padding: 5px 12px; font: inherit; font-size: 12px; font-weight: 700;
  white-space: nowrap; cursor: pointer;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text-dim);
}
.pill-done { border-color: var(--ok); color: var(--ok); }
.pill-clear { border-color: var(--danger); color: var(--danger); }
.pill-done:hover { background: var(--ok); color: var(--surface); }
.pill-clear:hover { background: var(--danger); color: var(--surface); }
/* 자동체크 중에는 아이콘이 눌리지 않는다. 눌러도 되는 것처럼 보이면 안 된다. */
.boss-icon:disabled { cursor: not-allowed; }
.boss-icon:disabled:hover { border-color: var(--border); }
.boss-icon:disabled:hover img { filter: grayscale(.85) opacity(.62); }
.boss-cell-done .boss-icon:disabled:hover img { filter: none; }

.wcard-view-actions, .wcard-edit-actions {
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
}
/* display 를 준 요소는 hidden 속성만으로 숨지 않는다. 브라우저 기본 규칙보다
   우리 규칙이 세서 그렇다 - 실제로 수정 버튼이 항상 보였다. */
[hidden] { display: none !important; }
.auto-state { margin: 0; font-size: 12px; color: var(--text-dim); text-align: right; line-height: 1.3; }
.auto-state-on { color: var(--accent); font-weight: 600; }
.auto-check {
  display: flex; align-items: center; gap: 6px; font-size: 12px; cursor: pointer;
  line-height: 1.3; justify-content: flex-end;
}
.auto-check input { accent-color: var(--accent); }
.auto-note { margin: 0; font-size: 12px; text-align: right; max-width: 170px; }

.btn-danger { color: var(--danger); }
.btn-danger:hover { color: var(--danger); text-decoration: underline; }

.week-total-side { margin-left: auto; }
.week-total-side .row-form input { min-width: 160px; }

/* 팝업 */
.dialog {
  width: min(420px, calc(100vw - 32px)); padding: 20px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
}
.dialog::backdrop { background: rgba(0, 0, 0, .45); }

/* 월드별 결정석 현황. 캐릭터당 12개와 별개로 **월드마다 주 90개**가 걸린다.
   요약 카드 안에 한 줄로 붙어 카드를 다 세지 않아도 남은 자리가 보인다. */
.world-crystals {
  list-style: none; margin: 0; padding: 0; grid-area: worlds;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.world-crystal {
  padding: 2px 8px; border-radius: 12px; font-size: 11px;
  background: var(--bg); color: var(--text-dim); border: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
}
.world-crystal b { color: var(--text); font-weight: 600; }
/* 다 쓴 월드는 더 등록할 수 없다. 눈에 띄어야 왜 막히는지 안다. */
.world-crystal-full { color: var(--danger); border-color: var(--danger); }
.world-crystal-full b { color: var(--danger); }

/* 보스 고르기 팝업. 25칸이 들어가므로 기본 팝업(420px)보다 넓다. */
.dialog-wide { width: min(760px, calc(100vw - 32px)); }
.pick-grid {
  list-style: none; margin: 12px 0 0; padding: 4px 2px;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px 8px;
  max-height: min(64vh, 560px); overflow-y: auto;
}
/* 시즌 보스는 결정석이 아니라 한도 밖이다. **줄을 바꿔** 따로 세운다 - 결정석 사이에
   끼어 있으면 12개를 세다가 헷갈린다. 보스 수가 바뀌어도 자리가 흔들리지 않는다. */
.pick-cell-season { grid-column: 1; border-left: 1px dashed var(--border); padding-left: 6px; }
.pick-cell { min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 3px; }

/* 그림·이름·체크박스를 한 label 로 묶는다. 어디를 눌러도 체크되는 것은 label 이 한다 -
   직접 클릭을 받으면 라디오처럼 두 번 토글되는 사고가 난다. */
.pick-face {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  cursor: pointer; max-width: 100%;
}
/* 체크박스 자체는 감춘다. 켜진 것은 테두리와 ✓ 로 보여 준다(카드의 보스 칸과 같은 모양). */
.pick-box { position: absolute; opacity: 0; width: 0; height: 0; }
.pick-icon {
  position: relative; width: 52px; height: 52px; overflow: hidden;
  border: 2px solid var(--border); border-radius: 12px; background: var(--bg);
  font-size: 13px; font-weight: 700; color: var(--text-dim);
  display: flex; align-items: center; justify-content: center;
}
.pick-icon img { width: 100%; height: 100%; object-fit: cover; display: block; filter: grayscale(.85) opacity(.6); }
.pick-face:hover .pick-icon { border-color: var(--text-dim); }
.pick-face:hover .pick-icon img { filter: grayscale(.2) opacity(.95); }
.pick-box:focus-visible + .pick-icon { outline: 2px solid var(--accent); outline-offset: 2px; }

.pick-check {
  position: absolute; right: 0; bottom: 0; display: none;
  padding: 0 4px; font-size: 11px; font-weight: 700; line-height: 15px;
  background: var(--accent); color: var(--accent-text); border-radius: 8px 0 0 0;
}
.pick-cell-on .pick-icon { border-color: var(--accent); }
.pick-cell-on .pick-icon img { filter: none; }
.pick-cell-on .pick-check { display: block; }

.pick-name {
  font-size: 11px; text-align: center; line-height: 1.2; color: var(--text-dim);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pick-cell-on .pick-name { color: var(--text); font-weight: 600; }

/* 난이도는 카드와 같은 배지 색이다. 고르지 않은 보스는 옅게 두어 눈이 체크된 것에 간다. */
.pick-diff {
  /* 난이도와 가격을 한 줄에 담아야 해 카드의 배지(11px)보다 한 단계 작다. */
  width: 100%; padding: 2px 1px; font: inherit; font-size: 10px; font-weight: 700;
  text-align: center; letter-spacing: -.4px; border-radius: 12px;
  border: 1px solid var(--border); cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.pick-cell:not(.pick-cell-on) .pick-diff { opacity: .45; }
.pick-diff option { background: var(--surface); color: var(--text); font-weight: 400; }

.pick-count { margin: 8px 0 0; font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.pick-count-over { color: var(--danger); font-weight: 600; }
/* 한도를 넘으면 [적용] 이 잠긴다. 눌리지 않는 것이 보여야 왜 안 되는지 안다. */
.dialog-actions .btn:disabled { opacity: .45; cursor: not-allowed; }

@media (max-width: 720px) {
  .pick-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.dialog h2 { margin: 0 0 4px; font-size: 16px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; align-items: center; }
.dialog .found { border: 0; padding: 0; margin: 12px 0; }
.dialog .found-note {
  margin: 0; padding: 10px 12px; border-radius: 8px; background: var(--bg);
  font-size: 12px; line-height: 1.5; color: var(--text-dim);
}
.dialog .found-note b { color: var(--text); }

/* 좁은 화면에서는 카드가 세로로 쌓인다. 세 칸이 옆으로 붙으면 보스가 안 보인다. */
@media (max-width: 720px) {
  .wcard { flex-direction: column; }
  .wcard-side, .wcard-actions { width: auto; flex: none; text-align: left; }
  .wcard-actions { flex-direction: row; align-items: center; }
  .week-total-note { margin-left: 0; text-align: left; }
}

/* --- 바닥글 ----------------------------------------------------------------
   넥슨 오픈 API 약관이 요구하는 표기와 저작권. 모든 화면에 나온다. */
/* 푸터. 가운데 정렬이 아니라 왼쪽 정렬이다 - 줄이 여러 개라 가운데로 모으면 읽기 나쁘다. */
.site-foot {
  max-width: var(--frame); margin: 0 auto; padding: 24px 20px 48px;
  border-top: 1px solid var(--border);
  color: var(--text-dim); font-size: 11px; line-height: 1.7;
}
.site-foot p { margin: 0; }
.site-foot a { color: var(--text-dim); text-decoration: none; }
.site-foot a:hover { color: var(--text); text-decoration: underline; }

/* 약관 링크는 본문보다 눈에 띄어야 한다 - 법적으로 찾을 수 있어야 하는 문서다. */
.site-foot-links { display: flex; gap: 18px; margin-bottom: 14px; }
.site-foot-links a { color: var(--text); font-size: 13px; font-weight: 600; }

.site-foot-by { margin-top: 2px; }
.site-foot-brand {
  display: flex; align-items: center; gap: 6px;
  margin-top: 16px; color: var(--text); font-size: 15px; font-weight: 700;
}
.site-foot-copy { margin-top: 2px; opacity: .8; }

/* 맨 위로. 화면 오른쪽 아래에 떠 있고, 조금 내려가야 나타난다(base.html 의 스크립트). */
.to-top {
  position: fixed; right: 20px; bottom: 20px; z-index: 40;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-dim);
  font-size: 9px; font-weight: 700; line-height: 1.1; letter-spacing: .04em;
  box-shadow: var(--shadow); cursor: pointer;
}
.to-top span { font-size: 11px; }
.to-top:hover { color: var(--text); border-color: var(--text-dim); }

/* 약관 문서(이용약관 · 개인정보처리방침). 읽으라고 만든 화면이라 폭을 좁게 잡는다 -
   한 줄이 길면 눈이 다음 줄을 놓친다. (app/routers/legal.py) */
.legal {
  max-width: 760px; margin: 0 auto; padding: 32px 20px 8px;
  color: var(--text); font-size: 13px; line-height: 1.85;
}
.legal h1 { margin: 0 0 8px; font-size: 22px; }
.legal h2 {
  margin: 32px 0 8px; padding-top: 16px; border-top: 1px solid var(--border);
  font-size: 15px;
}
.legal h3 { margin: 22px 0 6px; font-size: 14px; }
.legal p { margin: 6px 0; }
.legal ol, .legal ul { margin: 6px 0; padding-left: 20px; }
.legal li { margin: 3px 0; }
.legal-lead { margin-bottom: 20px; color: var(--text-dim); }
.legal-note {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radius);
  background: var(--accent-soft); font-size: 12px;
}

.legal-table { width: 100%; margin: 10px 0; border-collapse: collapse; font-size: 12px; }
.legal-table th, .legal-table td {
  padding: 8px 10px; border: 1px solid var(--border); text-align: left; vertical-align: top;
}
.legal-table th { background: var(--bg); font-weight: 600; white-space: nowrap; }
.legal-table td:first-child { white-space: nowrap; color: var(--text-dim); }

/* ── 월간보스 ───────────────────────────────────────────────────────
   보스가 검은 마법사 하나뿐이라 카드가 아니라 줄 하나가 캐릭터 하나다.
   보스가 늘면 주간과 같은 카드 화면으로 바꾼다. (docs/BOSS_INCOME.md) */

.month-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.month-head > div:first-child { flex: 1; min-width: 220px; }
.month-total { text-align: right; }
.month-total-value { margin: 0; font-size: 20px; letter-spacing: -.01em; }
.month-total-value strong { color: var(--ok, var(--accent)); }
.month-total-label { margin: 2px 0 0; font-size: 12px; color: var(--text-dim); }

.month-bar {
  display: flex; align-items: center; gap: 14px;
  margin: 0 0 18px; flex-wrap: wrap;
}
/* 주간과 같은 알약 버튼이라 .period-nav 의 위 여백은 여기서 지운다. */
.month-bar .period-nav { margin-top: 0; }
.month-bar .period-range { margin: 0; }

.month-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }

/* 줄마다 폭이 내용에 따라 달라지면(이름 길이, 비활성화 태그 유무) 난이도부터
   오른쪽이 전부 삐뚤어진다. **칸 너비를 고정한 격자**로 두어 줄끼리 열을 맞춘다. */
.mrow {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) 190px 80px 96px;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.mrow-who { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
/* 버튼 칸. 비활성화(4글자) -> 활성화(3글자) 로 바뀌어도 폭이 줄지 않아야 한다. */
.mrow-side { display: flex; flex-direction: column; gap: 4px; }

/* 잡은 줄은 테두리로 알린다. 색만으로 알리지 않으므로 체크 표시가 함께 켜진다. */
.mrow-done { border-color: var(--ok, var(--accent)); }
/* 이번 달 빼둔 줄은 흐리게. 지운 것이 아니라 이번 달만 쉬는 것이다. */
.mrow-off { opacity: .55; }

.mrow-check, .mrow-skip { display: flex; align-items: center; gap: 6px; margin: 0; }
.mrow-name { font-weight: 600; white-space: nowrap; }

.check-box {
  width: 22px; height: 22px; flex-shrink: 0;
  display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: transparent;
  font-size: 13px; line-height: 1; cursor: pointer; padding: 0;
}
.check-box:hover:not(:disabled) { border-color: var(--accent); }
.check-box:disabled { cursor: default; opacity: .5; }
.mrow-done .check-box {
  background: var(--ok, var(--accent));
  border-color: var(--ok, var(--accent));
  color: var(--accent-text);
}

.boss-badge {
  padding: 2px 8px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 11px; font-weight: 600; white-space: nowrap;
}

/* 줄 아무 데나 눌러도 체크된다(weekly.js). 누를 수 있다는 것을 손 모양으로 알린다.
   비활성화한 줄은 눌러도 안 되므로 기본 커서로 둔다. */
.mrow { cursor: pointer; }
.mrow-off { cursor: default; }
/* 버튼·입력칸 위에서는 제 모양을 지킨다. */
.mrow button, .mrow select, .mrow input, .mrow a { cursor: auto; }
.mrow button:not(:disabled), .mrow a { cursor: pointer; }

.mrow-off-tag { font-size: 12px; color: var(--text-dim); }

/* 자동으로 채워지는 줄임을 알리는 표시. 비활성화 표시와 같은 크기로 두되 색만
   다르다 - 하나는 "안 한다", 하나는 "저절로 된다" 라 헷갈리면 안 된다. */
.auto-tag {
  padding: 1px 6px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 10px; font-weight: 700; white-space: nowrap;
}

.mrow-income { font-weight: 700; text-align: right; }

/* 카드·줄의 조작 버튼. 화면마다 크기가 다르면 눈에 걸린다. 한 곳에서 정한다.
   주간 카드와 월간 줄이 같은 것을 쓴다. */
.act {
  background: none; border: 1px solid var(--border); border-radius: 8px;
  color: var(--text-dim); font: inherit; font-size: 12px; font-weight: 600;
  padding: 5px 12px; cursor: pointer; white-space: nowrap; text-align: center;
}
.act:hover:not(:disabled) { color: var(--text); border-color: var(--text-dim); }
/* 링크에도 같은 규칙을 쓴다(히스토리의 [물욕정산]·[빚정산]). 인라인이면 위아래
   여백이 줄 높이에 안 잡혀 혼자 납작해 보인다 (2026-09-16 사용자 지적). */
a.act { display: inline-block; text-decoration: none; }
/* **.btn 도 마찬가지다** (2026-09-18) - 링크로 쓴 [넥슨 API 사이트 열기] 에
   밑줄이 그어졌다. 버튼처럼 생긴 것에 밑줄이 있으면 무엇인지 알기 어렵다. */
/* 링크로 쓴 버튼도 **글자를 한가운데** 둔다. inline-block 으로 두면 키를 정해 둔
   버튼에서 글자가 위로 붙는다 (2026-09-21 실측: 위 3px · 아래 21px). 이 줄이
   `.btn` 보다 구체적이라, 위에서 flex 로 바꿔도 여기서 다시 덮여 있었다. */
a.btn {
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none; text-align: center;
}
a.act:hover { color: var(--text); border-color: var(--text-dim); }
.act:disabled { opacity: .4; cursor: default; }
.act-primary { border-color: var(--accent); color: var(--accent); }
.act-primary:hover:not(:disabled) { background: var(--accent); color: var(--accent-text); }

/* 난이도는 주간과 같은 규칙이다 - 평소에는 배지, 수정 중에만 드롭박스.
   .wcard 에만 걸려 있던 규칙을 줄에도 똑같이 적용한다. */
.mrow:not(.is-editing) .diff-select { display: none; }
.mrow.is-editing .diff-badge { display: none; }
.mrow.is-editing { border-color: var(--accent); }
.mrow .party-select { min-width: 68px; }

/* 파티원 버튼은 수정 중에, 그 줄에 마우스를 올렸을 때만 나온다(주간과 같다).
   늘 떠 있으면 줄이 버튼으로 덮인다. */
.mrow.is-editing .party-box:hover .party-add,
.mrow.is-editing .party-add:focus-visible { opacity: 1; pointer-events: auto; }

/* 좁은 화면에서는 격자를 풀어 두 줄로 흐르게 둔다. 190px 칸을 그대로 두면 넘친다. */
@media (max-width: 760px) {
  .mrow { grid-template-columns: auto minmax(0, 1fr) auto; }
  .mrow-set { grid-column: 2 / -1; }
  .mrow-income { grid-column: 2; text-align: left; }
  .mrow-side { grid-column: 3; grid-row: 3; }
}

/* 순서 분배의 그 주기 차례. 색만으로 알리지 않으므로 이름 자체가 누구인지 말해 준다. */
.turn-tag { font-size: 12px; color: var(--accent); }
.turn-tag.turn-mine { color: var(--ok, var(--accent)); font-weight: 600; }
.turn-tag.muted { color: var(--text-dim); }

/* 월간 줄의 가운데 칸: 난이도 한 줄, 인원+분배 표기 한 줄. 세 줄이면 낭비다. */
.mrow-set { display: flex; flex-direction: column; gap: 4px; align-items: center; }
.mrow-diff { display: flex; align-items: center; }
.mrow-party { display: flex; align-items: center; gap: 6px; }

/* 주간은 좁은 칸이라 파티 표기를 아래로 쌓지만, 월간은 옆에 붙인다. */
.mrow .party-box { width: auto; flex-direction: row; gap: 4px; }
.mrow .party-solo, .mrow .party-tag { font-size: 12px; }
/* 월간은 줄이라 폭이 넉넉하다. 주간 칸에 맞춘 9px 2열 격자를 그대로 쓰면 이름이
   찌그러져 보였다(2026-09-22 사용자 지적). 한 줄에 옆 글자와 같은 12px 로 적는다. */
.mrow .party-all {
  display: flex; flex-wrap: wrap; gap: 0 6px;
  font-size: 12px; line-height: 1.4; text-align: left;
}
.mrow .party-all > span { overflow: visible; }
/* 월간도 주간처럼 **이름 자리에 겹쳐 띄우고 이름은 감춘다** (2026-09-22). 옆에 세웠더니
   버튼이 자리를 빼앗아 이름이 `블…` 로 잘리고 두 줄로 접혔다 - 사진의 "뭉개짐" 이 그것.
   솔로처럼 이름이 짧아도 버튼 글자는 다 보이게 폭은 글자에 맞춘다(max-content).
   **왼쪽 끝을 이름 칸 시작에 붙인다** - 가운데로 맞추면 솔로 줄에서 버튼이 왼쪽의
   인원 드롭박스를 덮었다. 오른쪽(금액 앞)은 비어 있어 그쪽으로 뻗어도 된다. */
.mrow .party-add {
  left: 0; right: auto; top: 50%; transform: translateY(-50%);
  width: max-content; min-width: 100%; height: 24px; padding: 0 8px;
}

/* 비활성화·수정을 세로로 쌓고 폭을 맞춘다. 나란히 두면 크기가 안 맞아 눈에 걸린다. */
.mrow-actions { display: flex; flex-direction: column; gap: 4px; align-items: stretch; }
/* 버튼이 폼 안에 있으면 폼만 늘어나고 버튼은 글자 크기로 남는다.
   비활성화하면 글자가 짧아져 그 줄만 버튼이 쪼그라들어 보였다. */
.mrow-actions form { display: flex; }
.mrow-actions .act { width: 100%; }

/* 난이도는 배지든 드롭박스든 **같은 폭**이어야 한다. 주간 격자에서는 칸을 채우도록
   width:100% 에 좌우 여백이 0 인데, 줄에서는 그러면 글자에 딱 붙어 쪼그라든다. */
.mrow .diff-badge, .mrow .diff-select {
  width: 86px; padding: 4px 10px; font-size: 12px;
}

/* ── 보스 수익 공용 패널 ─────────────────────────────────────────
   주간·월간·분배금 오른쪽에 같은 패널이 붙는다. (docs/BOSS_INCOME.md) */

.boss-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 250px;
  gap: 20px;
  align-items: start;
}

.income-panel {
  display: flex; flex-direction: column; gap: 12px;
  position: sticky; top: 24px;
}

.ipanel-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
}

.ipanel-head {
  margin: 0 0 10px; font-size: 12px; font-weight: 600; color: var(--text-dim);
}

.ipanel-rows { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.ipanel-rows div { display: flex; justify-content: space-between; gap: 10px; margin: 0; }
.ipanel-rows dt { font-size: 12px; color: var(--text-dim); }
.ipanel-rows dd { margin: 0; font-size: 12px; font-weight: 700; }

/* 색은 상태를 말할 때만 쓴다. 벌어들인 돈은 초록, 월간 보스는 구분을 위해 주황이다. */
.ipanel-rows .pos, .ipanel-sum b { color: var(--ok); }
.ipanel-rows .warn, .ipanel-sum b.warn { color: var(--warn, #e08a3c); }

/* 이번 주 줄은 눈에 먼저 들어와야 한다. 색만으로 알리지 않으므로 (이번주) 를 함께 적는다. */
.ipanel-now dt, .ipanel-now dd { color: var(--accent); }
.ipanel-tag { font-size: 11px; }

/* 보스 화면만 넓히던 것을 걷어냈다 - 화면마다 폭이 다르면 가운데 정렬 때문에
   **좌측 탭이 좌우로 움직인다**. 지금은 머리글·본문·바닥글이 모두 --frame 하나를 쓴다.
   (app.css 위쪽 .shell / .header-inner / .site-foot) */

.ipanel-month { border-top: 1px solid var(--border); padding-top: 6px; margin-top: 2px; }

.ipanel-sum {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  margin: 10px 0 0; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--text-dim);
}
.ipanel-sum b { font-size: 14px; }
.ipanel-total b { font-size: 15px; color: var(--text); }

/* 좁은 화면에서는 본문 아래로 내린다. 250px 를 옆에 두면 카드가 눌린다.
   **1300px 부터 내린다** (2026-09-18 점검). 1100 으로 두었더니 1200px 창에서
   물욕정산의 열한 칸짜리 표가 옆 패널에 눌려 71px 넘쳤다 - 패널을 내리면 그 폭이
   표에 돌아온다. 1100 과 1300 사이가 노트북에서 가장 흔한 폭이다. */
@media (max-width: 1300px) {
  .boss-layout { grid-template-columns: minmax(0, 1fr); }
  .income-panel { position: static; }
}

/* ── 득템기록 ────────────────────────────────────────────────────
   한 줄이 아이템 하나다. 등록 폼은 팝업에 있다 - 본문에 펼치면 칸이 열 개라
   오른쪽 패널에 눌려 세로 목록이 된다. (docs/LOOT_SPLIT.md) */

.lsplit-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  margin-bottom: 16px;
}
/* **.btn 은 width:100% 라 flex 안에서 폭을 다 먹는다.** 그대로 두면 버튼이 줄을
   차지하고 제목이 한 글자씩 세로로 접힌다. 실제로 그렇게 나왔다(2026-09-03).
   버튼은 btn-inline 으로 폭을 풀고, 제목 쪽이 남은 자리를 갖게 한다. */
.lsplit-head-text { flex: 1 1 auto; min-width: 0; }
.lsplit-head .page-title, .lsplit-head .page-sub { margin: 0; }
/* **머리 묶음이 두 갈래다** - `.lsplit-head` 를 쓰는 화면과 `<h1>+<p>` 만 쓰는 화면.
   아래 여백이 16px 과 24px 로 달라 화면을 옮길 때 본문이 8px 씩 들썩였다
   (2026-09-18 점검에서 잼: 155 vs 163). 같은 값으로 맞춘다. */
.page-sub { margin-bottom: 16px; }
.lsplit-head .page-sub { margin-top: 4px; }
.lsplit-head .btn { flex: 0 0 auto; width: auto; white-space: nowrap; }

/* 표가 좁은 화면에서 본문을 밀어내지 않게 자기 안에서 가로로 구른다. */
.ltable-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.ltable { width: 100%; border-collapse: collapse; font-size: 12px; min-width: 720px; }

.ltable th {
  padding: 9px 10px; text-align: left; font-weight: 600; font-size: 11px;
  color: var(--text-dim); background: var(--bg);
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
.ltable td {
  padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: middle;
}
.ltable tbody tr:last-child td { border-bottom: 0; }

/* 금액은 오른쪽 정렬 + 같은 폭 숫자. 자리가 흔들리면 세로로 못 읽는다. */
.ltable .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ltable .acts { width: 1%; white-space: nowrap; }

.ltable td small {
  display: block; margin-top: 1px; color: var(--text-dim); font-size: 11px;
  max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ltable td small.warn { color: var(--warn); max-width: none; white-space: normal; }
.lcell-name { font-weight: 600; }

.lcell-seller { max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 판매자가 나면 굵게 - 나머지를 가져가는 쪽이라 눈에 보여야 한다. */
.lcell-seller.is-me { font-weight: 700; color: var(--accent); }

.lcell-mine { font-weight: 700; color: var(--ok); }
/* 올릴 금액·지급액은 딸린 값이라 굵기와 색을 뺀다. 숫자 두 개가 같은 무게면 헷갈린다. */
.ltable .lcell-mine small,
.ltable .num small { font-weight: 400; color: var(--text-dim); text-align: right; }
.lcell-split small { display: block; }

.ltable .acts form { display: inline-block; margin: 0 0 0 4px; }
.ltable .acts form:first-child { margin-left: 0; }

/* 정산이 끝난 줄은 가라앉힌다. 지우지는 않는다 - 진행률의 분모에 남아 있어야 한다. */
.ltable tr.is-settled td { opacity: .5; }
.ltable tr.is-settled .lcell-mine { color: var(--text-dim); }

.ltable-empty td { text-align: center; color: var(--text-dim); padding: 28px 10px; }

.tag {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  background: var(--border); color: var(--text-dim); font-size: 11px; white-space: nowrap;
}
.tag-party { background: var(--accent-soft); color: var(--accent); }

/* --- 등록 팝업 --- */
/* 칸이 열 개라 기본 팝업(420px)보다 넓어야 두 줄로 앉는다. */
.dialog-wide { width: min(560px, calc(100vw - 32px)); }
.ladd-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; margin-top: 14px;
}
.ladd-grid .field { margin: 0; }
.ladd-grid .field-wide { grid-column: 1 / -1; }
.ladd-grid .field > span {
  display: block; margin-bottom: 4px; font-size: 11px; color: var(--text-dim);
}
/* 미리보기는 서버가 정본이 아니라는 뜻으로 흐리게 둔다. */
.ladd-preview {
  margin: 14px 0 0; padding: 9px 11px; border-radius: 8px; background: var(--bg);
  font-size: 12px; color: var(--text-dim); min-height: 17px;
}

@media (max-width: 520px) {
  .lsplit-head { flex-direction: column; }
  .ladd-grid { grid-template-columns: 1fr; }
  /* 좁은 화면에서는 한 줄에 하나씩. 빈 자리는 아예 접는다 - 세로로 쌓이면 헛간이 된다. */
  .ledger-head, .ledger-fields { grid-template-columns: minmax(0, 1fr); }
  .field-blank { display: none; }
}

/* --- 득템기록: 먹은 날 · 펼침 카드 --- */

/* 먹은 날. 연도는 작게 - 대부분 올해라 월/일만 봐도 된다. */
.lcell-date { white-space: nowrap; font-variant-numeric: tabular-nums; }
.lcell-date small { display: block; margin-top: 0; color: var(--text-dim); font-size: 10px; }

/* 아직 안 팔린 것. 0 원과 헷갈리면 안 되므로 숫자가 아니라 글자로 쓴다. */
.undecided { color: var(--text-dim); font-weight: 400; }

/* 줄 아래로 펼치는 카드. 팝업이 아니라서 표를 보면서 옆 줄과 견줄 수 있다. */
.ldetail-row > td { padding: 0; background: var(--bg); }
.ldetail {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: 16px 24px;
  padding: 14px 12px 12px;
  border-top: 1px solid var(--border);
}
.ldetail h3 {
  margin: 0 0 8px; font-size: 11px; font-weight: 600; color: var(--text-dim);
}

.ldetail-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.ldetail-table th {
  padding: 6px 8px; text-align: left; font-size: 11px; font-weight: 600;
  color: var(--text-dim); border-bottom: 1px solid var(--border); white-space: nowrap;
}
.ldetail-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
.ldetail-table tr:last-child td { border-bottom: 0; }
/* 여기서는 원 단위로 정확히 보여 준다 - 실제로 그 금액을 경매장에 입력해야 한다. */
.ldetail-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ldetail-table .is-me { font-weight: 700; color: var(--accent); }
.ldetail-table .is-final { font-weight: 700; color: var(--ok); }

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

.ldetail-fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px 12px;
}
.ldetail-fields .field { margin: 0; }
.ldetail-fields .field > span {
  display: block; margin-bottom: 3px; font-size: 11px; color: var(--text-dim);
}
.ldetail-fields .field-wide { grid-column: 1 / -1; }
.ldetail-actions { margin-top: 10px; }

/* 정산을 마친 줄의 카드. 고칠 것이 실수령액 하나뿐이라 두 칸으로 나눌 것이 없다.
   (2026-09-16 사용자 지정: "정보가 너무많아 다보여줄 필요없이") */
.ldetail-slim { grid-template-columns: minmax(0, 1fr); gap: 10px; }
.ldetail-slim .ldetail-fields { grid-template-columns: repeat(auto-fit, minmax(160px, 220px)); }
.ldetail-slim .ldetail-remove { display: flex; gap: 8px; justify-content: flex-end; }

/* 칸 이름이 위에 붙는 다른 칸들과 달리, 체크박스는 글이 옆에 붙어야 읽힌다.
   [내가 올릴 금액만] 처럼 **바로 위 칸의 뜻을 바꾸는** 것이라, 딸린 것으로 보이게
   테두리로 묶어 바로 아래에 붙인다.

   **함정**: 이 폼의 input 은 width:100% 다. 체크박스에도 그대로 걸려 **네모 하나가
   줄을 320px 먹고 글자를 오른쪽 끝으로 밀어낸다** - 실제로 그렇게 나왔다(2026-09-16).
   .btn 이 줄을 통째로 먹던 것과 같은 함정이다. width 를 꼭 되돌려 준다. */
.field-check {
  grid-column: 1 / -1;
  display: flex; align-items: flex-start; gap: 8px; cursor: pointer;
  margin-top: -2px; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg);
  font-size: 11px; line-height: 1.5; color: var(--text-dim); text-align: left;
}
.field-check:hover { border-color: var(--accent); }
.field-check input {
  width: auto; flex: none; margin: 2px 0 0; accent-color: var(--accent);
}
.ladd-grid .field-check > span,
.ldetail-fields .field-check > span {
  display: block; margin: 0; flex: 1; font-size: 11px; color: inherit;
}
.field-check b { color: var(--text); }

/* 삭제는 카드 맨 아래 오른쪽. 저장 버튼과 멀리 둔다 - 나란히 두면 잘못 누른다.
   **카드 안 버튼은 .act 하나로** 맞춘다 (docs/SCREENS.md) - .btn 은 팝업의 큰
   버튼이라 줄 안에서 혼자 커진다. */
.ldetail-remove { grid-column: 1 / -1; display: flex; justify-content: flex-end;
                  gap: 8px; padding-top: 10px; border-top: 1px solid var(--border); }

/* 화면에 안 보이지만 읽어 주는 글. 빈 머리글 칸에 쓴다. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

@media (max-width: 900px) {
  .ldetail { grid-template-columns: minmax(0, 1fr); }
}

/* 펼침 기호 칸. 글자가 아니라 기호라 폭이 거의 필요 없다. */
.ltable th.fold, .ltable td.fold { width: 1%; padding-left: 4px; padding-right: 4px; }
.fold-btn {
  border: 0; background: transparent; color: var(--text-dim);
  font-size: 10px; line-height: 1; padding: 4px 2px; cursor: pointer;
}
.fold-btn:hover { color: var(--text); }
.fold-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* 등록 팝업의 분배 안내. 폼 아래에 한 줄로 붙는다. */
.ladd-help { margin: 10px 0 0; }
.ldetail-actions .field-help { margin: 0 0 8px; }

/* 펼침 카드 맨 위의 자취. 언제 넣고 언제 손댔는지. */
.ldetail-stamp {
  grid-column: 1 / -1; margin: 0 0 2px;
  font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums;
}

/* 지우는 버튼. 테두리까지 붉히지는 않는다 - 옆 버튼들과 크기가 어긋나면 안 된다. */
.act-danger { color: var(--danger); }
.act-danger:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); }

/* 공용 확인창의 [예]. 되돌릴 수 없는 쪽이라 눈에 띄어야 한다. */
.btn-danger-solid {
  background: var(--danger); color: #fff; border: 0;
}
.btn-danger-solid:hover { filter: brightness(1.08); }
.dialog-confirm { width: min(360px, calc(100vw - 32px)); }
.dialog-confirm p { margin: 6px 0 0; font-size: 13px; line-height: 1.6; color: var(--text-dim); }

/* 관리 팝업의 줄. 버튼이 되면서 높이가 늘어 간격을 다시 잡는다. */
.manage-list li { align-items: center; }
.manage-actions { display: flex; gap: 6px; }

/* 줄 어디를 눌러도 펴지므로 손 모양을 준다. 펼쳐진 카드는 대상이 아니다. */
.ltable tr.lrow-main { cursor: pointer; }
.ltable tr.lrow-main:hover > td { background: var(--surface-hover, rgba(127, 127, 127, .06)); }
.ltable tr.lrow-main .acts, .ltable tr.lrow-main .fold { cursor: default; }

/* 확인창의 두 버튼은 글자 수가 달라도 같은 폭이다 - "예" 와 "아니오" 가 제각각이면
   눈이 한쪽으로 쏠린다 (2026-09-03 사용자 지정). */
.dialog-confirm .dialog-actions .btn { min-width: 92px; }

/* ── 가계부 ──────────────────────────────────────────────────────
   위에 입력줄, 아래에 표. 표는 득템기록과 같은 것(.ltable)을 쓴다.
   **카테고리가 입력란을 정한다** - 묶음을 모두 그려 두고 고른 것만 편다. */

.ledger-add {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; margin-bottom: 16px;
}
/* 머리 줄도 자리를 고정한다. 방법 드롭박스가 있고 없고에 따라 줄이 접히면 안 된다. */
.ledger-head {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 14px;
}
.ledger-head .field { margin: 0; }
.ledger-head .field > span,
.ledger-fields .field > span {
  display: block; margin-bottom: 4px; font-size: 11px; color: var(--text-dim);
}
/* 카테고리 설명. 두 줄까지 자리를 잡아 둔다 - 한 줄짜리와 두 줄짜리가 섞여 있어
   그때그때 늘렸다 줄이면 아래가 흔들린다. */
.ledger-hint {
  margin: 10px 0 0; font-size: 11px; line-height: 1.5; color: var(--text-dim);
  min-height: 33px;
}

/* **칸 수가 달라도 폼 크기는 같아야 한다** (2026-09-03 사용자 지적).
   자리를 늘 같은 수로 잡고(app/ledger.py 의 slot_count) 모자란 만큼은 빈 자리를 둔다.
   auto-fit 은 칸 수에 따라 열이 줄어 크기가 널뛰므로 쓰지 않는다. */
.ledger-fields {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 14px;
  margin-top: 12px;
}
.ledger-fields .field { margin: 0; }

/* 빈 자리. 눈에는 안 보이지만 자리는 지킨다. */
.field-blank { visibility: hidden; pointer-events: none; }

.ledger-add-foot {
  display: flex; justify-content: flex-end;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border);
}

.ledger-table-wrap .ltable { min-width: 560px; }

/* 얻은 것은 초록, 나간 것은 붉게. 부호 대신 색과 자리로 가른다.
   한 줄이 여러 단위를 얻을 수 있어(사냥 = 메소 + 조각) 칸 안에서 줄을 나눈다. */
.lcell-in span, .lcell-out span { display: block; }
.lcell-out { color: var(--danger); }
.lcell-in { color: var(--ok); font-weight: 600; }
.lcell-memo {
  color: var(--text-dim); max-width: 180px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 합계는 단위마다 따로 낸다 - 메소와 조각을 더할 수는 없다.
   **th 에도 같은 선을 준다** - 첫 칸만 `<th scope="row">합계</th>` 인 표에서는
   그 칸만 선이 없어 합계 줄의 윗선이 중간에서 시작했다 (2026-09-18 점검). */
.ltable tfoot td, .ltable tfoot th {
  border-top: 1px solid var(--border); font-size: 11px; color: var(--text-dim);
  padding: 10px;
}
/* 하루 목록 왼쪽, 그 달 전체 오른쪽 (2026-09-06 사용자 지정).
   달 표는 열이 많아 자기 안에서 가로로 구른다. 좁은 화면에서는 위아래로 쌓인다. */
.ledger-split {
  display: grid; gap: 14px; align-items: start;
  /* 하루 목록은 열이 여섯뿐이라 자리를 붙박이로 주고, **남는 폭은 모두 달 표**에
     준다 - 열이 스물여덟이라 한 픽셀이 아쉽다 (2026-09-06 사용자 지정). */
  /* [수정]·[삭제] 두 개가 들어가 조금 더 넓다 (2026-09-06). */
  grid-template-columns: minmax(0, 464px) minmax(0, 1fr);
}
@media (max-width: 1100px) {
  .ledger-split { grid-template-columns: 1fr; }
}
/* 여섯 열뿐이라 720px 을 고집할 이유가 없다. 자리에 맞춰 줄인다. */
.ledger-split .ledger-table-wrap .ltable { min-width: 0; }
/* 합계가 "21.0억 메 / 소" 로 접혔다. 금액은 한 덩어리로 둔다. */
.ledger-split .ledger-total span { white-space: nowrap; }
.ledger-split .ledger-total { gap: 8px; }
.ledger-split .ledger-table-wrap .ltable th,
.ledger-split .ledger-table-wrap .ltable td { padding: 8px 7px; }
.ledger-split .lcell-memo {
  max-width: 72px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 흐름 이름이 길면 두 줄로 접힌다. 자리를 못 늘리니 글자를 조금 줄인다.
   **한글은 기본값이면 글자 사이에서 끊긴다**("아이템 강/화"). 빈칸에서만 끊게 한다. */
.ledger-split .lcell-name { font-size: 11px; word-break: keep-all; }
.ledger-split .ltable .acts .act { padding: 3px 7px; }

.lmonth-wrap {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); overflow: hidden;
}
.lmonth-head { padding: 12px 14px 10px; border-bottom: 1px solid var(--border); }
.lmonth-head-text { min-width: 0; }
.lmonth-head h2 { margin: 0; font-size: 15px; letter-spacing: -.02em; }
.lmonth-head .card-sub { margin: 3px 0 0; font-size: 11px; }

/* 이 달에 재화가 얼마나 움직였나 (2026-09-16 사용자 지정). 표 맨 아래 합계 줄까지
   내려가지 않고 머리에서 본다. 늘었으면 초록, 줄었으면 빨강. */
.lmonth-change {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none;
}
.lmonth-change li {
  display: grid; gap: 1px; min-width: 88px; padding: 5px 10px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg);
}
.lmonth-change span { font-size: 10px; color: var(--text-dim); white-space: nowrap; }
.lmonth-change b {
  font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* **색은 여기서도 따로 줘야 한다** (2026-09-16 실제로 당함). .pos/.neg 는 표 안으로
   한정된 규칙(.lmonth td.pos 등)뿐이라, 클래스만 붙여 두고 초록인 줄 알았다.
   실제로는 그냥 흰 글씨였고 사용자가 먼저 봤다. */
.lmonth-change .pos { color: var(--ok); }
.lmonth-change .neg { color: var(--danger); }

/* 줄이 서른 남짓이라 세로로도 구른다. 머리 줄과 날짜 칸은 붙잡아 둔다 -
   굴렸을 때 무슨 날의 무슨 열인지 놓치면 표가 아니다.
   세로 막대가 마지막 열을 덮지 않게 자리를 미리 비워 둔다. */
.lmonth-scroll { overflow: auto; scrollbar-gutter: stable; max-height: min(76vh, 720px); }
.lmonth { min-width: 900px; font-size: 11px; table-layout: fixed; }
/* **엑셀표처럼 격자를 다 친다** (2026-09-06 사용자 지정). 열이 열셋이라 세로줄이
   없으면 어느 열의 값인지 눈으로 짚어야 한다. 선은 아주 옅게 둔다. */
.lmonth th, .lmonth td {
  padding: 4px 8px;
  border-right: 1px solid var(--grid);
  border-bottom: 1px solid var(--grid);
}
.lmonth th:last-child, .lmonth td:last-child {
  border-right: 0;
  /* 마지막 열이 세로 막대에 딱 붙어 답답했다 (2026-09-06 사용자 지적). 자리를 띄운다. */
  padding-right: 16px;
}
.lmonth td { font-variant-numeric: tabular-nums; }
/* "8/27~9/2 전달" 이 들어갈 만큼은 되어야 한다. 좁으면 옆 칸으로 삐져나온다. */
.lmonth-col-date { width: 98px; }

/* --- 머리 줄 두 단. 위는 묶음, 아래가 열 이름이다 ------------------------- */
.lmonth thead th {
  position: sticky; z-index: 2; text-align: center; line-height: 1.25;
  font-size: 10px; vertical-align: bottom; background: var(--bg);
}
/* 이름은 서버가 줄 단위로 나눠 준다(MONTH_COLUMNS 의 head). 한 덩어리로 두면
   칸 폭에 맞춰 아무 데서나 잘려 읽기 나쁘다 (2026-09-06 사용자 지적). */
.lmonth thead th span { display: block; }
.lmonth thead tr:first-child th { top: 0; }
.lmonth thead tr:last-child th { top: 21px; padding-bottom: 7px; }
/* `.ltable .num` 의 nowrap 을 이겨야 이름이 접힌다 - 머리 칸에도 num 이 붙어 있다. */
/* **`.ltable .num` 를 이겨야 한다.** 머리 칸에도 num 이 붙어 있어 `.lmonth thead th`
   로는 특이도가 밀린다 - 접힘도 가운데 정렬도 여기서 다시 못박는다 (두 번째로 당함). */
.lmonth thead th.num { white-space: normal; word-break: keep-all; text-align: center; }
/* 묶음 이름은 가운데. 위의 right 를 이겨야 해서 한 겹 더 준다. */
.lmonth thead .lmonth-groups th {
  text-align: center; font-size: 10px; letter-spacing: -.02em;
  padding: 5px 8px 4px; border-bottom: 1px solid var(--border);
}
/* **색은 묶음 머리 칸에만 칠한다** (2026-09-06 사용자 지정). 열 전체에 깔았더니
   숫자 뒤가 늘 물들어 초록·빨강 글자와 섞여 읽기 나빴다. 어디까지가 한 묶음인지는
   머리 칸 하나로 충분하다 - 아래는 세로줄이 이어 준다. */
.lmonth-group-in { color: var(--ok); background: var(--band-in); }
.lmonth-group-out { color: var(--danger); background: var(--band-out); }
.lmonth-group-sum { color: var(--text-dim); }
/* 묶음이 갈리는 자리. 몸통에서는 색 대신 선이 묶음을 가른다. */
.lmonth th.lmonth-edge, .lmonth td.lmonth-edge { border-right: 1px solid var(--border); }

/* --- 날짜 칸 -------------------------------------------------------------- */
.lmonth .lmonth-date {
  position: sticky; left: 0; z-index: 1; white-space: nowrap;
  background: var(--surface); font-weight: 600; color: var(--text-dim);
  text-align: left; padding-left: 10px;
}
.lmonth thead .lmonth-date, .lmonth tfoot .lmonth-date { background: var(--bg); z-index: 3; }
.lmonth-dow { margin-left: 3px; font-weight: 400; opacity: .6; }

/* --- 날 줄 ---------------------------------------------------------------- */
.lmonth tbody tr:hover td, .lmonth tbody tr:hover .lmonth-date {
  background-image: linear-gradient(color-mix(in srgb, var(--text) 5%, transparent),
                                    color-mix(in srgb, var(--text) 5%, transparent));
}
/* 손을 올린 줄이 보고 있는 날이면 형광이 덮이지 않게 둘을 겹쳐 깐다. */
.lmonth tbody tr.lmonth-now:hover td, .lmonth tbody tr.lmonth-now:hover .lmonth-date {
  background-image: linear-gradient(var(--mark), var(--mark)),
                    linear-gradient(color-mix(in srgb, var(--text) 5%, transparent),
                                    color-mix(in srgb, var(--text) 5%, transparent));
}
.lmonth td.pos { color: var(--ok); }
.lmonth td.neg { color: var(--danger); }
/* 안 적은 날은 비어 있다. 0 과 구별되게 점만 찍는다. */
.lmonth-none { color: var(--border); }

/* 값이 있는 칸은 눌러서 내역을 본다. 버튼이지만 표의 숫자처럼 보여야 한다.
   **칸 어디를 눌러도 눌린다** (2026-09-16 사용자 지정) - 글자만 과녁이면 너무 작다.
   칸의 여백을 안쪽 버튼·링크가 갖고, 칸 자체는 여백을 두지 않는다. */
.lmonth td:has(> .lmonth-cell),
.lmonth .lmonth-date:has(> .lmonth-go) { padding: 0; }
.lmonth-cell, .lmonth-go { padding: 4px 8px; }
.lmonth td:last-child .lmonth-cell { padding-right: 16px; }
.lmonth .lmonth-date .lmonth-go { padding-left: 10px; }
.lmonth-go { display: block; color: var(--text-dim); text-decoration: none; }
a.lmonth-go:hover { color: var(--accent); }
/* 그 주에 잡은 보스가 없다는 표시. 안 적은 칸(·)과 구별되어야 한다. */
.lmonth-zero { color: var(--text-dim); opacity: .75; }
.lmonth-cell {
  display: block; width: 100%; border: 0; background: none;
  color: inherit; font: inherit; text-align: right; cursor: pointer;
}

/* **하이라이트는 칸이 받는다** (2026-09-16 사용자 지정). 글자에 밑줄을 그으면 과녁이
   글자처럼 보인다 - 칸을 채운 버튼·링크가 바탕을 칠한다.
   바탕을 갈아 끼우지 않고 **덧칠한다**(background-image) - 밑에 깔린 묶음 색과
   보고 있는 날의 형광을 지우지 않기 위해서다. */
.lmonth-cell:hover,
a.lmonth-go:hover {
  background-image: linear-gradient(color-mix(in srgb, var(--accent) 18%, transparent),
                                    color-mix(in srgb, var(--accent) 18%, transparent));
}
.lmonth-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* 지난 달부터 걸친 주라는 표시. 날짜 칸이 좁아 아주 작게 둔다. */
.lmonth-over {
  margin-left: 3px; padding: 0 3px; border-radius: 5px; white-space: nowrap;
  background: var(--bg); color: var(--text-dim); font-size: 9px; font-weight: 600;
}
/* 주 라인의 날짜는 두 조각(범위 + 전달)이라 한 줄에 붙여 둔다. */
.lmonth-wsum .lmonth-date { white-space: nowrap; }

/* 칸 내역 팝업. 왼쪽은 무엇, 오른쪽은 얼마다. */
.lmonth-detail {
  list-style: none; margin: 10px 0 0; padding: 0; max-height: 46vh;
  /* **가로로는 넘치지 않는다** (2026-09-18 사용자 지적: "좌우 스크롤바 생기는게
     거슬리는데"). overflow-y 만 주면 overflow-x 가 visible 로 남지 못해 auto 가
     되는데, 줄이 1px 이라도 넓으면 그때부터 가로 막대가 붙는다. */
  overflow-y: auto; overflow-x: hidden;
}
.lmonth-detail li {
  /* **stretch 다.** center 로 두면 안쪽 링크가 줄 높이를 다 못 채워 아래쪽
     한두 픽셀이 과녁에서 빠진다 (2026-09-18 검사가 모서리를 찍어 잡았다). */
  display: flex; align-items: stretch; gap: 10px;
  border-bottom: 1px solid var(--border); font-size: 12px;
}
.lmonth-detail li:last-child { border-bottom: 0; }

/* **줄을 누르면 그것이 있는 화면으로 간다** (2026-09-17 사용자 지정).
   달 표의 칸과 같은 규칙 - 글자만 과녁이면 너무 작아 **여백까지 링크가 갖는다.**
   좌우 음수 여백은 줄 끝까지 칠해지게 하려는 것이다. */
.lmonth-detail-body {
  flex: 1; min-width: 0; display: flex; align-items: baseline;
  gap: 12px; justify-content: space-between;
  /* **바깥으로 밀지 않는다.** 한때 좌우 -6px 로 빼서 줄 끝까지 칠했더니 목록이
     12px 넓어져 가로 스크롤바가 붙었다 (2026-09-18 사용자 지적). 여백은 원래
     줄 패딩(2px)과 같게 두어 글자 자리도 그대로다. */
  padding: 7px 2px; border-radius: 8px;
  color: inherit; text-decoration: none;
}
a.lmonth-detail-body { cursor: pointer; }
/* 바탕을 갈아 끼우지 않고 덧칠한다 - 밑에 깔린 것을 지우지 않기 위해서다. */
a.lmonth-detail-body:hover {
  background-image: linear-gradient(color-mix(in srgb, var(--accent) 14%, transparent),
                                    color-mix(in srgb, var(--accent) 14%, transparent));
}
a.lmonth-detail-body:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.lmonth-detail-name { min-width: 0; }
.lmonth-detail-name small { display: block; color: var(--text-dim); font-size: 11px; }
.lmonth-detail-amount { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.lmonth-detail-amount.pos { color: var(--ok); }
.lmonth-detail-amount.neg { color: var(--danger); }
.lmonth-detail-note { padding: 10px 2px; color: var(--text-dim); font-size: 12px; }

/* 지난 주 보스 몫은 **줄의 금액 자리에서 바로 고친다** (2026-09-16 사용자 정정).
   금액과 입력칸이 같은 자리를 번갈아 쓰므로 폭을 맞춰 둔다 - 안 그러면 [수정] 을
   누를 때마다 줄이 들썩인다. */
.lmonth-detail-edit { display: flex; align-items: center; gap: 5px; white-space: nowrap; }
.lmonth-detail-edit small { color: var(--text-dim); font-size: 11px; }

/* 팝업 아래 규칙 한 줄. 잡고도 체크를 잊는 일이 있다. */
.lmonth-hint {
  margin: 12px 0 0; padding: 9px 11px; border-radius: 8px;
  background: var(--bg); color: var(--text-dim); font-size: 11px; line-height: 1.6;
}
.lmonth-typing-input {
  /* 원본 메소를 콤마까지 담는다 - "11,375,633,333" 이 안 잘려야 한다. */
  width: 148px; padding: 4px 8px; font: inherit; font-size: 12px; text-align: right;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font-variant-numeric: tabular-nums;
}
.lmonth-typing-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* 하루 목록의 [수정]·[삭제] 는 나란히 둔다.
   **td 에만 건다.** 머리 칸(th)까지 flex 로 만들었더니 그 칸이 접혀 머리 줄이
   깨졌다 - 표의 칸을 flex 컨테이너로 바꾸면 칸 노릇을 그만둔다 (2026-09-06 지적). */
.ledger-split .ltable td.acts { display: flex; gap: 4px; align-items: center; }
.ledger-split .ltable th.acts { width: 1%; }

/* 보고 있는 날은 **형광 노랑**이다 (2026-09-06 사용자 지정) - 왼쪽 목록이 어느
   줄인지 한눈에 보여야 하는데, 강조색은 주 라인·링크에도 쓰여 눈에 덜 띄었다.

   **바탕을 갈아 끼우지 않고 위에 덧칠한다.** background-color 로 칠하면 밑에 깔린
   묶음 바탕(초록·빨강)이 지워진다 - 칸 하나에 바탕색은 하나뿐이라서다. 그래서
   그러데이션으로 이미지 층에 얹는다. 색은 옅고 투명해 글자색도 그대로 산다. */
.lmonth-now td, .lmonth-now .lmonth-date {
  background-image: linear-gradient(var(--mark), var(--mark));
}
.lmonth-now .lmonth-date, .lmonth-now .lmonth-date a { color: var(--text); }
.lmonth-now .lmonth-date a { font-weight: 700; }

/* --- 주 라인 -------------------------------------------------------------- */
/* 주(목~수)를 한 묶음으로 본다. 보스 메소는 여기에만 있다.
   **묶음 이름(lmonth-sum)과 겹치지 않게 wsum 이다** - 겹쳐 두었더니 `.lmonth-sum` 이
   주 라인과 "재화 변동" 칸을 함께 잡았다. */
/* 주를 가르는 선은 두지 않는다 (2026-09-06 사용자 지정) - 회색 주 라인 하나로
   이미 갈린다. 선까지 두면 줄이 촘촘한 표에서 눈이 어지럽다. */
/* 주 라인도 덧칠이다. 밑의 묶음 바탕을 지우지 않는다. */
.lmonth-wsum th, .lmonth-wsum td {
  background-image: linear-gradient(color-mix(in srgb, var(--text) 8%, transparent),
                                    color-mix(in srgb, var(--text) 8%, transparent));
  font-weight: 700; border-top: 1px solid var(--border);
}
.lmonth-wsum .lmonth-date { color: var(--text); font-size: 10px; }
.lmonth-week-now .lmonth-wsum th, .lmonth-week-now .lmonth-wsum td {
  background-image: linear-gradient(color-mix(in srgb, var(--accent) 14%, transparent),
                                    color-mix(in srgb, var(--accent) 14%, transparent));
}
.lmonth-week-now .lmonth-wsum .lmonth-date { color: var(--accent); }

.lmonth tfoot th, .lmonth tfoot td {
  position: sticky; bottom: 0; background: var(--bg); font-weight: 700;
  border-top: 2px solid var(--border); font-size: 11px;
}

.ledger-total { display: flex; gap: 14px; flex-wrap: wrap; justify-content: flex-end; }
.ledger-total span { font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.ledger-total .pos { color: var(--ok); }
.ledger-total .neg { color: var(--danger); }

/* 계산이 끼는 방법(메소로 메포 구매)의 미리보기. 서버가 정본이라는 뜻으로 흐리게 둔다. */
.ledger-preview {
  margin: 12px 0 0; padding: 9px 11px; border-radius: 8px; background: var(--bg);
  font-size: 12px; color: var(--text-dim); min-height: 17px;
}

/* 기간 이동. 화살표 · 달력 · 복귀 버튼이 한 줄에 앉는다. 키는 .period-nav 가 정한다. */
.period-arrow { padding-left: 11px; padding-right: 11px; font-size: 10px; }
.period-pick { margin: 0; display: flex; align-items: center; }
/* 달력 칸도 같은 키다 - 한 줄에 섞여 있어 어긋나면 눈에 띈다. */
.period-pick input {
  height: var(--tab-h, 32px); box-sizing: border-box;
  padding: 0 8px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font: inherit; font-size: 12px;
}
.period-pick input:hover { border-color: var(--text-dim); }
.period-pick input:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

/* 가계부 날짜 이동. 표 바로 위에 앉는다. */
/* 이동줄과 표가 딱 붙으면 어느 날·어느 달을 보고 있는지 눈에 안 들어온다
   (2026-09-16 사용자 지적). 가계부 화면들이 함께 쓴다. */
.ledger-nav { margin-bottom: 18px; }

/* 하루 이동줄 · [기록 추가] · 달 재화 변동을 한 줄에 둔다 (2026-09-16 사용자 지정).
   **아래 두 칸과 같은 폭으로 갈라** 각자 자기 표 위에 서게 한다 - 어긋나면 어느
   표의 머리인지 알 수 없다. (.ledger-split 과 같은 열 폭) */
.ledger-topbar {
  display: grid; gap: 14px; align-items: center; margin-bottom: 14px;
  grid-template-columns: minmax(0, 464px) minmax(0, 1fr);
}
.ledger-topbar-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ledger-topbar .period-nav { margin-top: 0; }
@media (max-width: 1100px) {
  .ledger-topbar { grid-template-columns: 1fr; align-items: start; }
}

/* 줄 안에 같이 서는 버튼. 기본 크기는 이동줄 옆에서 혼자 큼직해 눈에 먼저 든다. */
.btn-small { padding: 6px 14px; font-size: 13px; border-radius: 99px; }

/* 표 바로 위 오른쪽에 두는 버튼 줄. 제목 옆에 두면 표에서 멀다. */
.ltable-top { display: flex; justify-content: flex-end; margin: 14px 0 8px; }

/* --- 대시보드 (2026-09-16) -------------------------------------------------
   칸 셋이 한 줄. 좁아지면 둘, 더 좁아지면 하나로 접힌다. */
.dash-grid {
  display: grid; gap: 12px; margin-bottom: 12px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.dash-grid-two { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.dash-card { padding: 14px 16px; }
.dash-card h2 { margin: 0; font-size: 13px; color: var(--text-dim); font-weight: 700; }
.dash-card h2 a { color: inherit; text-decoration: none; }
.dash-card h2 a:hover { color: var(--accent); }
/* 한 칸에 하나만 크게. 먼저 보라고 둔 숫자다. */
.dash-big {
  margin: 6px 0 8px; font-size: 26px; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.dash-of { margin-left: 4px; font-size: 13px; font-weight: 600; color: var(--text-dim); }
.dash-card .bar { margin-bottom: 8px; }
.dash-card .ipanel-rows { gap: 2px; }

/* 재화 변동은 **한 줄에 셋**이다 (2026-09-17 사용자 지정) - 들어온 것 · 나간 것 ·
   변동. 숫자가 서로 견주는 값이라 자릿수를 맞춰 오른쪽으로 세운다. */
.dash-change { width: 100%; border-collapse: collapse; margin-top: 8px; }
.dash-change th, .dash-change td {
  padding: 3px 0; font-size: 12px; font-weight: 400; text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.dash-change thead th {
  padding-bottom: 5px; font-size: 10px; color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}
/* 단위 이름은 왼쪽. 숫자 칸이 넓어야 하므로 이 칸은 내용만큼만 먹는다. */
.dash-change tbody th { width: 1%; text-align: left; color: var(--text-dim); }
.dash-change tbody tr + tr td, .dash-change tbody tr + tr th {
  border-top: 1px solid color-mix(in srgb, var(--border) 45%, transparent);
}
/* 셋 중 **변동이 결론**이라 그것만 굵게 둔다. */
.dash-change-net { font-weight: 700; }
.dash-change .pos { color: var(--ok); }
.dash-change .neg { color: var(--danger); }
.dash-change .muted { color: var(--text-dim); opacity: .55; }
.dash-note { margin: 8px 0 0; font-size: 10px; line-height: 1.5; color: var(--text-dim); }
.dash-note-top { margin: 2px 0 8px; }

/* 빚 장부. 칸이 여섯뿐이라 팝업 없이 본문에 둔다. */
.debt-add { padding: 14px; margin-bottom: 16px; }
.debt-add .ladd-grid { margin-top: 0; }
.debt-add .ltable-top { margin-bottom: 0; }
.debt-owed { margin-bottom: 10px; }
/* 규칙 한 줄. 칸 둘을 가로질러 한 줄로 앉는다. */
.debt-help { grid-column: 1 / -1; margin: 0; line-height: 1.6; }
.debt-done-head { margin: 26px 0 0; font-size: 14px; font-weight: 700; color: var(--text); }
.ltable-top .btn { width: auto; }
/* 팝업 안에서는 입력줄 상자를 따로 두지 않는다 - 팝업이 이미 상자다. */
.dialog .ledger-head, .dialog .ledger-fields { margin-top: 0; }
.dialog .ledger-head { margin-bottom: 0; }


/* --- 관리자 화면 (2026-09-18 사용자 지정) ---------------------------------
   초대 링크와 임시 비밀번호는 **옮겨 적는 값이 아니다.** 링크는 60자가 넘고
   임시 비밀번호는 한 번만 보이는 값이라, 한 글자 틀리면 왜 안 되는지 알 길이 없다.
   그래서 통째로 보여 주고 옆에 복사 버튼을 둔다 (static/js/admin.js). */
.admin-secret {
  display: inline-block; max-width: 100%; overflow-x: auto; vertical-align: middle;
  padding: 3px 7px; border-radius: 6px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px;
  white-space: nowrap;
}
/* **칸을 flex 로 만들면 줄의 아래 선이 끊긴다** (2026-09-18 사용자 지적:
   "링크-상태 사이에는 라인이 깨져"). td 가 table-cell 이 아니게 되어 줄 높이만큼
   늘어나지 못하고(45px 줄에서 41px), 그 칸의 아래 테두리만 4px 위에 그려진다.
   **칸은 칸으로 두고 안에 상자를 하나 넣는다.** */
.admin-link-cell > .admin-link-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* **칸이 쪼개지지 않게 한다.** 메모나 날짜가 글자 단위로 접히면 줄 높이가 널뛴다.
   남는 폭은 링크 칸이 먹는다. */
.admin-invites td, .admin-invites th { white-space: nowrap; }
.admin-invites .admin-link-cell { width: 100%; }
.admin-invite-form { display: flex; align-items: center; gap: 8px; }
/* 제목은 왼쪽, 만드는 칸은 오른쪽. 공용 .ltable-top 은 오른쪽 정렬이라 제목까지
   밀려가므로 여기서는 쓰지 않는다 (공용 규칙을 덮어쓰지 않는다 - docs/SCREENS.md). */
.admin-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 10px;
}
.admin-head h2 { margin: 0; font-size: 15px; }
.admin-invite-form input { width: 220px; }
/* 다 쓴 링크는 흐리게. 지우지 않는 것은 누가 그 링크로 들어왔는지가 기록이기 때문이다. */
.admin-dim td { opacity: .55; }


/* **CapsLock 안내** (2026-09-18 사용자 지정). 비밀번호는 점으로만 보여서, 대문자로
   들어가고 있는 줄 모르고 몇 번을 틀린 뒤 계정이 잠긴 줄 안다.
   오류가 아니라 귀띔이라 붉게 칠하지 않는다. */
.caps-note {
  display: flex; align-items: center; gap: 6px;
  margin: 6px 0 0; font-size: 11px; line-height: 1.5; color: var(--warn);
}
.caps-note::before {
  content: "⇪"; font-size: 12px; font-weight: 700;
}

/* 가입 화면 맨 아래, 다음 단계에 필요한 것을 미리 알려 주는 줄. 오류가 아니라
   준비물 안내라 옅게 둔다 (2026-09-18 사용자 지정). */
.signup-next {
  margin: 14px 0 0; padding: 10px 12px; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--bg);
  color: var(--text-dim); font-size: 11px; line-height: 1.6;
}
.signup-next b { color: var(--text); }

/* **관리자 표는 길어진다** (2026-09-18 사용자 물음: "몇 줄 넘으면 스크롤 하게
   되어있어?" - 안 되어 있었다). 활동 로그가 200줄에 11,394px 이라 화면 높이의
   열세 배였다. 달 표와 같은 방식으로 세로를 끊는다(.lmonth-scroll).

   짧은 표에는 아무 일도 일어나지 않는다 - 넘칠 때만 막대가 생긴다. */
/* **열 줄을 넘으면 끊는다** (2026-09-18 사용자 지정). 정확한 높이는 줄마다 달라
   (날짜가 두 줄인 칸이 있다) 자바스크립트가 열째 줄을 재서 정해 준다
   (static/js/admin.js). 여기 값은 그것이 죽었을 때 받쳐 주는 한도다. */
.admin-scroll { max-height: min(70vh, 620px); overflow-y: auto; }
/* **머리 줄을 붙잡아 둔다.** 굴렸을 때 무슨 열인지 놓치면 표가 아니다.
   바탕을 깔지 않으면 지나가는 줄이 글자 뒤로 비친다. */
.admin-scroll .ltable thead th {
  position: sticky; top: 0; z-index: 1; background: var(--bg);
}
/* 합계 줄은 바닥에 붙인다. 표가 길면 합계를 보러 끝까지 굴려야 한다. */
.admin-scroll .ltable tfoot td, .admin-scroll .ltable tfoot th {
  position: sticky; bottom: 0; background: var(--bg);
  border-top: 1px solid var(--border);
}

/* 초대 링크 머리의 단추 묶음. 만들기는 오른쪽 끝, 지우는 것은 그 왼쪽이다. */
.admin-invite-form { flex-wrap: wrap; }
.admin-invite-form .admin-invite-form { margin-left: auto; }
/* 고르는 칸은 폼의 input 규칙(width:100%)을 되돌려야 한다 - 안 그러면 줄을 먹는다
   (체크박스에서 한 번 당한 것과 같다. docs/SCREENS.md). */
.ltable td.fold input[type="checkbox"],
.ltable th.fold input[type="checkbox"] { width: auto; margin: 0; }

/* 머리를 눌러 차례를 바꾼다. **누를 수 있는 것처럼 보여야 한다** - 화살표는
   지금 차례를 말하고, 안 누른 열에는 손을 올렸을 때만 옅게 뜬다. */
.sort-head { cursor: pointer; user-select: none; white-space: nowrap; }
.sort-head:hover { color: var(--text); }
.sort-head::after {
  content: "↕"; margin-left: 4px; font-size: 10px; opacity: 0; color: var(--text-dim);
}
.sort-head:hover::after, .sort-head:focus-visible::after { opacity: .5; }
.sort-head[data-dir]::after { opacity: 1; color: var(--accent); }
.sort-head[data-dir="asc"]::after { content: "↑"; }
.sort-head[data-dir="desc"]::after { content: "↓"; }
.sort-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* 찾는 칸. 표 머리 오른쪽에 붙는다. */
.admin-find { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.admin-find input[type="search"] { width: 220px; }
/* 안내글은 남는 폭을 먹고 줄어들 수 있어야 한다. 안 그러면 찾는 칸을 밀어낸다.
   **여백을 지운다** - 공용 `.card-sub` 는 제목 밑에 놓일 것이라 `margin: -4px 0 16px`
   인데, 한 줄에 나란히 두면 그 여백이 그대로 남아 글자만 10px 위로 뜬다. */
.admin-find .card-sub {
  flex: 0 1 auto; min-width: 0; margin: 0; text-align: right; font-size: 11px;
}


/* --- 한 줄에 놓인 것은 높이가 같아야 한다 (2026-09-18 사용자 지적) ----------
   "박스, 체크박스나 텍스트들이 너무 삐뚤빼뚤하다"

   재 보니 초대 링크 머리 한 줄에 **높이가 셋**이었다 - 입력칸 21px(꾸미지 않아
   브라우저 기본 그대로였다), `.act` 31px, `.btn-small` 33px. 기준 높이를 하나
   정하고 그 줄에 놓이는 것을 전부 거기에 맞춘다. */
.admin-head { --admin-control: 32px; }
.admin-head .act,
.admin-head .btn,
.admin-head input { height: var(--admin-control); box-sizing: border-box; }
/* 관리자 머리줄의 입력칸은 `.field` 밖이라 아무 규칙도 안 받고 있었다.
   폼 입력칸과 같은 모양으로 맞춘다 (공용 `.field input` 을 그대로 베낀다). */
.admin-head input {
  padding: 0 10px; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; font: inherit; font-size: 12px;
}
.admin-head input::placeholder { color: var(--text-dim); }
.admin-head input:focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent;
}
/* 알약(99px)과 각진 것(8px)이 나란히 있으면 키가 같아도 어긋나 보인다. */
.admin-head .btn-small { border-radius: 8px; padding: 0 14px; font-size: 12px; }
.admin-head h2 { line-height: var(--admin-control); }

/* **줄 높이가 51px 과 38px 로 널뛰었다.** 링크 칸의 [복사](31px)와 코드 상자(26px)가
   그 줄만 밀어 올린 탓이다. 칸 안의 것을 낮추고, 모든 줄에 같은 키를 준다.

   키를 45px 로 잡은 것은 **단추가 든 줄에 맞춘 값**이다. 단추 26px + 위아래 여백
   9px + 칸 아래 테두리 1px = 45px 이라, 단추가 없는 줄에 같은 최소 높이를 주면
   관리자 화면 세 장의 줄이 모두 같아진다. (테두리 1px 을 빼먹어 한참 44 와 45 가
   섞였다 - 줄 높이를 잴 때 `getBoundingClientRect` 는 테두리까지 센다.) */
.admin-scroll .ltable tbody tr { height: 45px; }
.admin-scroll .ltable .act {
  height: 26px; line-height: 24px; padding-top: 0; padding-bottom: 0;
  box-sizing: border-box;
}
/* inline-block 은 글자가 앉는 줄(strut) 위에 얹혀 밑에 1px 이 더 붙는다. 그 1px
   때문에 단추가 든 줄만 45px 이 되어 다른 표와 어긋났다. **칸의 줄 높이를 0 으로**
   두면 단추 자신의 키(26px)만 남아, 위아래 여백(9px)까지 정확히 44px 이 된다. */
.admin-scroll .ltable td.acts { line-height: 0; }
.admin-scroll .ltable .acts .act,
.admin-scroll .ltable .acts form,
.admin-scroll .ltable .admin-link-cell .act { vertical-align: top; }
/* 링크 칸의 코드 상자는 낮게(22px), **단추는 다른 단추와 같은 키(26px)** 로 둔다 -
   한때 둘 다 22px 로 묶었더니 같은 표 안에서 [복사]만 작았다 (2026-09-18 점검). */
.admin-invites .admin-secret {
  height: 22px; line-height: 20px; padding-top: 0; padding-bottom: 0;
  box-sizing: border-box;
}
/* 비율(%)은 **제 칸을 가진다.** 숫자 옆에 붙였다가 두 번 지적받았다 (2026-09-18
   "위아래줄 정렬이 삐뚤빼뚤", 2026-09-21 "정렬이 거슬려 %의미도 좀 모호하고").

   한 칸에 `350 100%` 를 넣으면 앞 숫자의 글자 수가 제각각이라, 오른쪽 끝을 맞춰도
   중간이 어긋난다. 고정 폭(38px)으로 눌러 봤지만 `320 KB` 와 `399 B` 처럼 단위까지
   다르면 그것도 안 맞았다. **칸에 한 가지만 넣으면** 오른쪽 맞춤만으로 줄이 선다.
   곁들여 머리글이 붙으니 무엇에 대한 비율인지도 보인다.

   (칸을 flex 로 만들어 맞추려 하면 안 된다 - td 가 table-cell 이 아니게 되어 줄에서
   떨어져 나온다. 링크 칸에서 한 번, 여기서 한 번 당했다.) */
/* 머리글의 작은 글씨(로그 제외 · 대략)도 같은 자리에 오도록 오른쪽에 맞춘다. */
.admin-scroll .ltable th.num small { display: block; text-align: right; }
/* 이름표(나 · 관리자)와 이름의 밑선을 맞춘다. 배지가 글자보다 커서 따로 논다. */
.admin-scroll .ltable td .tag,
.admin-scroll .ltable td .lcell-name,
.admin-scroll .ltable td .muted,
.admin-scroll .ltable td .pos { vertical-align: middle; }
/* **칸이 글자 단위로 접히면 그 줄만 두 배가 된다.** 1200px 에서 재 보니 이름표가
   둘 달린 줄과 "2026-09-18 16:07" 이 접혀 첫 줄만 58px 이었다. 관리자 표에 담기는
   것은 아이디·날짜·숫자처럼 짧은 것뿐이라 접을 이유가 없다 - 남는 폭은 표가 알아서
   나눠 갖는다. */
.admin-scroll .ltable tbody td { white-space: nowrap; }

/* **좁은 화면에서는 한 단계 줄여서 들인다.** 1200px 창에서 본문은 877px 인데 통계
   표는 칸이 열셋이라 993px 이 필요했다. 글자와 여백을 줄이는 편이 좌우로 굴리는
   것보다 낫다 (2026-09-18 사용자 지정: "좌우로는 스크롤 안하도록"). */
@media (max-width: 1400px) {
  .admin-scroll .ltable { font-size: 11px; }
  .admin-scroll .ltable th, .admin-scroll .ltable td {
    padding-left: 6px; padding-right: 6px;
  }
}
/* 칸 폭을 정하는 것은 숫자가 아니라 **머리글**이다 ("보스 건너뛰기" 는 85px 인데
   그 아래 숫자는 여덟 자리라도 50px 이다). 머리글은 자리가 모자랄 때만 접히므로,
   넓은 화면에서는 아무 일도 일어나지 않는다.

   **달 표는 뺀다**(.lmonth) - 13열짜리라 애초에 자기 안에서 구르게 만든 표이고,
   머리글이 접히면 날짜 줄과 높이가 어긋난다. (2026-09-18 점검에서 물욕정산이
   1200px 창에서 가로로 넘치는 것을 보고 규칙을 넓혔다.) */
/* **낱말은 쪼개지 않는다**(word-break: keep-all). 그냥 normal 로 두면 한글이 글자
   단위로 끊겨 "정산" 이 "정/산" 으로 접힌다 (2026-09-18 점검에서 물욕정산이 그랬다).
   빈칸에서만 접히므로 "보스 건너뛰기" 는 두 줄, "정산" 은 한 줄이다. */
.ltable:not(.lmonth) thead th { white-space: normal; word-break: keep-all; }
@media (max-width: 1400px) {
  .ltable:not(.lmonth) th, .ltable:not(.lmonth) td {
    padding-left: 7px; padding-right: 7px;
  }
}

/* 고르는 칸은 가운데. 왼쪽에 붙여 두면 머리의 칸과 줄의 칸이 어긋나 보인다.
   **표 테두리에 닿지 않게** 왼쪽을 넉넉히 준다 (2026-09-18 사용자 지적:
   "체크박스가 표라인에 너무 붙어 있고"). 공용 .fold 는 4px 인데 접기 단추에
   맞춘 값이라, 여기서만 벌린다. */
.admin-scroll .ltable th.fold, .admin-scroll .ltable td.fold {
  text-align: center; padding-left: 14px; padding-right: 10px;
}
.admin-scroll .ltable th.fold input[type="checkbox"],
.admin-scroll .ltable td.fold input[type="checkbox"] { vertical-align: middle; }


/* --- 이번 주 메모장 (2026-09-18 사용자 지정) --------------------------------
   대시보드 맨 위. 숫자판과 달리 **손으로 적는 자리**라 한 장을 통째로 쓴다.
   목요일이 지나면 저절로 빈 종이가 된다 - 주기 키로 묶여 있다 (app/memo.py). */
.memo-card { margin-bottom: 16px; }
.memo-form { display: flex; flex-direction: column; gap: 10px; }
.memo-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.memo-head h2 { margin: 0; font-size: 15px; }
.memo-when { font-size: 11px; }

.memo-form textarea {
  width: 100%; box-sizing: border-box; padding: 10px 12px; resize: vertical;
  min-height: 84px; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  font: inherit; font-size: 13px; line-height: 1.6;
}
.memo-form textarea:focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent;
}
.memo-form textarea::placeholder { color: var(--text-dim); }

.memo-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.memo-left { font-size: 11px; font-variant-numeric: tabular-nums; }
/* 다 차 가면 눈에 띄게 한다. 다 쓰고 나서 잘리는 것을 알면 늦다. */
.memo-left.warn { color: var(--warn); }
.memo-form .form-error, .memo-form .form-notice { margin: 0; }


/* --- 캐릭별 보스 통계 (2026-09-18 사용자 지정) ------------------------------
   읽기만 하는 표다. **한 번에 한 기간**이라 칸은 메소 · 마릿수 · 물욕 셋뿐이다
   (한때 셋을 나란히 두어 칸이 여섯이었는데, 무엇이 무엇인지 흐렸다). */
.boss-stats td small { display: inline; margin-left: 6px; }
/* 이 기간에 아무것도 없는 캐릭터. **지우지 않는다** - 0 이라고 적혀 있어야
   "안 잡았다" 를 안다. */
.boss-stats-quiet td { opacity: .55; }
.boss-stats-note { margin-top: 10px; }
/* 기간 딱지 안의 실제 범위(9/3~9/30). 옅게 두어 "9월" 이 먼저 읽히게 하되,
   **딱지 색 위에서 읽혀야 한다** - var(--text-dim) 을 쓰면 켜진 딱지의 바탕과
   붙어 글자가 사라진다. 같은 색을 옅게 쓴다.

   이름을 .period-span 으로 따로 둔다. .period-range 는 보스 화면에서 **딱지가 아닌
   한 줄**(`<p>`)로 쓰고 있어, 그 여백(margin: 8px 0 0)이 딱지에 그대로 붙어 혼자
   2px 내려앉아 있었다. */
.period-span small {
  margin-left: 6px; color: inherit; opacity: .7; font-weight: 400; font-size: 11px;
}
/* 합계 줄의 첫 칸만 th 라 머리 바탕이 깔려 혼자 회색이었다. 줄 전체를 같게 둔다. */
.boss-stats tfoot th { background: none; color: var(--text); font-size: 12px; }
/* 합계 줄은 왼쪽 끝까지 한 선이다. */
.boss-stats tfoot th, .boss-stats tfoot td { border-top: 1px solid var(--border); }


/* --- 한 해 아카이브 버튼 (2026-09-21 사용자 지정) ---------------------------
   대시보드 머리 오른쪽 끝. **평소에는 잠겨 있다** - 1월 첫 목요일부터 2월 첫
   목요일 전까지만 열린다. 잠긴 동안에도 버튼은 그대로 보인다: 없으면 그런 기능이
   있는 줄도 모르고, 정작 지워지기 직전에 찾게 된다. */
.archive-btn { flex: 0 0 auto; white-space: nowrap; gap: 6px; }
/* 잠긴 동안에도 **읽히기는 해야 한다** (2026-09-21 사용자 지적: "가시성도 안좋고").
   opacity .45 는 다크에서 바탕에 묻혔다. 흐리게 하는 대신 **테두리를 점선으로** 바꿔
   잠긴 것을 알린다 - 색이 아니라 모양으로 말한다. */
.archive-btn.is-locked {
  opacity: .8; border-style: dashed; cursor: not-allowed;
  pointer-events: auto;   /* 손을 올려야 힌트가 뜬다 */
}
.archive-btn.is-locked:hover { color: var(--text-dim); border-color: var(--border); }
/* 자물쇠는 글자 앞에 작게. 색만으로 알리지 않는다. */
.archive-btn.is-locked::before { content: "🔒"; font-size: 11px; }


/* --- 아카이브 안내 (2026-09-21 사용자 지정) ---------------------------------
   버튼 둘을 오른쪽 끝에 나란히. 안내는 왼쪽, 내려받기는 오른쪽이다 - 읽고 나서
   받는 차례다. */
.archive-acts { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* **기간이 되면 경고한다.** 받지 않으면 지워지는 것이 우리 정책이라, 그 기간에만
   눈에 걸리게 한다 - 평소에 띄워 두면 눈에 익어 정작 읽지 않는다. */
.archive-alert { border-color: var(--warn); }
.archive-alert > span:first-child { color: var(--warn); }
.archive-alert a { font-weight: 700; }

/* 정책 표는 팝업 안에서 좁게. 두 줄뿐이라 최소 폭을 풀어 준다. */
.archive-policy { min-width: 0; font-size: 12px; }
.archive-policy td:first-child { width: 1%; white-space: nowrap; }
.dialog-wide { max-width: min(680px, 92vw); }
