/* ============================================================================
   프로젝트 디자인 시스템

   구조와 부품(화면 뼈대·목록/등록·수정/상세 화면·버튼·입력·표·태그·모달·확인창·도움말·
   알림 상자·쪽 번호 줄·카드·통계 카드·빈 목록·좁은 화면)은 공통 디자인 시스템에서 가져왔다.
   색·그림자·모서리 같은 **테마 값은 맨 위 `:root`에만** 있고, 프로젝트마다
   `.claude/docs/ui/DESIGN-REVIEW.md` 절차에 따라 이 값을 바꿔 끼운다. 부품 규칙은 그대로 둔다.

   왜 override 방식인가: global.css가 Bulma 실사용 클래스만 담은 발췌본이고 화면들이
   이미 그 클래스(.box/.table/.button/.tag/.input/.columns)로 쓰여 있다. 클래스 이름을 바꾸는
   대신 여기서 모양만 덮어쓰면 모든 화면이 한 번에 같은 디자인을 입는다.

   색은 뜻이다: 기본 동작(--app-primary), 정상(--app-success), 삭제·위험(--app-danger),
   주의(--app-warning). 삭제는 테마가 바뀌어도 빨강 계열을 유지한다.
   ========================================================================== */
:root {
  /* 테마 값 - 기준 이미지(`.claude/docs/ui/references/design-system.png`, 2026-09-27)에서 뽑았다.
     거의 흰 바탕 + 흰 카드(얇은 선 + 부드러운 아래 그림자) + 파랑 기본 동작. 흰 빛(하이라이트)은 없다.
     값의 출처·결정은 `.claude/docs/ui/references/README.md`, 표는 `.claude/docs/ui/DESIGN-SYSTEM.md`. */

  /* 사이드바 - 흰 면 + 오른쪽 옅은 선, 지금 메뉴만 파랑 */
  --app-nav-bg: #ffffff;
  --app-nav-bg-hover: #f2f4f7;
  --app-nav-active: #2f77c9;
  --app-nav-text: #5d6470;
  --app-nav-text-strong: #2e313a;

  /* 본문 - 거의 흰 바탕. 들어간 면(표 머리·읽기 전용 칸)은 --app-surface-sunk, 올렸을 때 옅은 바탕은 --app-surface-hover */
  --app-bg: #f7f9fb;
  --app-surface: #ffffff;
  --app-surface-sunk: #f2f4f7;
  --app-surface-hover: #f5f7fa;
  --app-surface-pressed: #dbdee4;
  --app-border: #e1e5ea;
  --app-border-strong: #c9ced6;
  --app-border-soft: #eceff3;

  --app-text: #2e313a;
  --app-text-label: #454b56;
  --app-text-muted: #727784;
  --app-text-faint: #a3a9b4;

  /* 강조색 - 색은 뜻이다(파랑=기본 동작, 초록=정상, 빨강=삭제·위험, 노랑=주의) */
  --app-primary: #2f77c9;
  --app-primary-dark: #2f639e;   /* 누름·보내는 중(기준 이미지 PRESSED STATE) */
  --app-primary-soft: #e6f0fa;
  --app-primary-ring: rgba(47, 119, 201, .18);
  --app-success: #2fa56e;
  --app-success-soft: #dcf3e8;
  --app-danger: #d9444a;         /* 삭제는 빨강 - 이미지의 파란 [Delete]는 따르지 않는다(늘 지키는 규칙 1) */
  --app-danger-dark: #b93639;
  --app-danger-soft: #fde8e8;
  --app-warning: #d98a16;
  --app-warning-soft: #fef0cc;
  --app-info: #2f77c9;
  --app-info-soft: #e6f0fa;
  --app-disabled: #c5c8cf;       /* 못 누름 - 회색 면 + 흰 글자(기준 이미지 DISABLED STATE) */
  --app-tooltip: #3c4552;        /* 말풍선 - 짙은 회색 면 + 흰 글자(기준 이미지 TOOLTIP) */

  /* 글자 크기 척도 - 여섯 단계뿐이다.
     기본은 --fs-base 하나로 가고, 분명히 보조인 것(설명·경로·태그)만 한 단계 줄인다.
     예전에는 .62 ~ 1.6rem이 20종 가까이 흩어져 있어 화면마다 글자가 들쭉날쭉했다. */
  --fs-2xl: 24px;   /* 통계 숫자, 문서 제목 */
  --fs-xl: 20px;    /* 화면 제목 */
  --fs-lg: 16px;    /* 카드 섹션 제목 */
  --fs-base: 14px;  /* 본문 - 표·입력·버튼·메뉴 전부 */
  --fs-sm: 13px;    /* 보조 - 설명, 경로, 표 머리, 메타 */
  --fs-xs: 12px;    /* 가장 작은 것 - 태그, help, 배지 */

  /* 간격 척도 - 블록 사이는 --app-gap 하나만 쓴다.
     예전에는 .9rem / 1rem / .85rem이 섞여 있어 카드가 어떤 건 붙고 어떤 건 떨어져 보였다. */
  --app-gap: 18px;
  --app-gap-sm: .5rem;
  --app-field-h: 34px;

  /* 모서리 - 기준 이미지 척도 4/8/12/16/24(RADIUS SCALE). 버튼은 12px 둥근 네모, 뱃지·알약만 끝이 둥글다 */
  --app-radius-lg: 24px;   /* 모달 */
  --app-radius: 16px;      /* 카드 */
  --app-radius-md: 12px;   /* 버튼, 카드 안 패널, 탭 틀, 토스트 */
  --app-radius-sm: 8px;    /* 입력·쪽 번호·표 안 칸 */
  --app-radius-btn: 12px;  /* 버튼 - 이미지가 알약이면 999px로 바꾼다 */

  /* 층 - 흰 면 + 얇은 선 + **아래로 번지는 옅은 회색 그림자**(기준 이미지 LEVEL 1~5).
     흰 빛(하이라이트)은 없다. 왼쪽 위는 아주 옅게만 준다(늘 지키는 규칙 6 - 오른쪽 아래보다 옅은 같은 색) */
  --app-sh-dark: rgba(40, 52, 72, .12);   /* 아래·오른쪽 */
  --app-sh-soft: rgba(40, 52, 72, .04);   /* 위·왼쪽(들어간 면은 안쪽 위) */
  --app-shadow-1: 0 1px 2px var(--app-sh-dark), 0 2px 6px var(--app-sh-soft);                        /* 작게 뜸 - 버튼·알약 */
  --app-shadow: 0 4px 14px var(--app-sh-dark), 0 1px 3px var(--app-sh-soft);                         /* 올림 - 카드 */
  --app-shadow-4: 0 16px 40px rgba(40, 52, 72, .18), 0 2px 8px var(--app-sh-soft);                    /* 모달·펼침 메뉴 */
  --app-shadow-in: inset 0 2px 5px var(--app-sh-dark), inset 0 -1px 2px var(--app-sh-soft);          /* 들어감 - 눌림 */
  --app-sidebar-w: 224px;
  --app-topbar-h: 60px;
}

/* 제목의 브라우저 기본 마진을 지운다.
   global.css에 h1~h6 리셋이 없어서 <h1>에 위아래 0.67em(약 13px)이 그대로 붙어 있었다 -
   화면 머리 위아래에 빈 띠가 생기고, 카드 안 제목도 위 여백이 제각각이던 원인이다.
   간격은 .page-head / .title.* 가 직접 정한다. */
h1, h2, h3, h4, h5, h6 { margin: 0; font-size: inherit; font-weight: inherit; }

body {
  background: var(--app-bg);
  color: var(--app-text);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

/* ── 전체 뼈대 ──────────────────────────────────────────────────────────────
   사이드바를 고정(fixed)하고 본문에 왼쪽 여백을 준다. 본문이 길어도 메뉴는 늘 같은 자리에
   있어야 화면을 오가며 쓰기 편하다. */
.app-shell {
  min-height: 100vh;
}

.app-sidebar {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--app-sidebar-w);
  height: 100vh;
  background: var(--app-nav-bg);
  display: flex;
  flex-direction: column;
  z-index: 40;
  overflow-y: auto;
  border-right: 1px solid var(--app-border);
  box-shadow: 2px 0 10px var(--app-sh-soft);
}

.app-brand {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: 1.15rem 1.15rem 1rem;
  color: var(--app-nav-text-strong);
  font-weight: 700;
  font-size: var(--fs-lg);
  text-decoration: none;
}

.app-brand:hover { color: var(--app-nav-text-strong); }

.app-brand-mark {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--app-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  flex: 0 0 auto;
  color: #fff;
}

.app-nav {
  padding: .35rem .6rem 1.5rem;
}

.app-nav-item {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .58rem .7rem;
  margin-bottom: .12rem;
  border-radius: var(--app-radius-sm);
  color: var(--app-nav-text);
  font-size: var(--fs-base);
  text-decoration: none;
  transition: background .12s, color .12s;
}

.app-nav-item:hover {
  background: var(--app-nav-bg-hover);
  color: var(--app-nav-text-strong);
}

/* 지금 보고 있는 메뉴 - 어디에 있는지 한눈에 보여야 한다 */
.app-nav-item.is-active {
  background: var(--app-nav-active);
  color: #fff;
  font-weight: 600;
  box-shadow: var(--app-shadow-1);
}

.app-nav-icon {
  width: 1.15rem;
  text-align: center;
  flex: 0 0 auto;
  opacity: .9;
}

.app-nav-label {
  font-size: var(--fs-xs);
  letter-spacing: .06em;
  color: var(--app-text-faint);
  padding: 1rem .7rem .35rem;
}

/* 사이드바 하위 메뉴 - 윗 메뉴에 딸린 화면임이 들여쓰기로 보인다 */
.app-nav-item.is-sub { padding-left: 2.6rem; font-size: var(--fs-sm); }
.app-nav-item.is-sub .app-nav-icon { opacity: .7; }

/* ── 본문 영역 ───────────────────────────────────────────────────────────── */
.app-main {
  margin-left: var(--app-sidebar-w);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* 표가 넓어도 본문이 사이드바를 밀어내지 않게 한다. 넘치는 표는 .list-table이
     자기 안에서 스크롤하므로 여기서 잘라내지는 않는다 */
  min-width: 0;
}

.app-topbar {
  height: var(--app-topbar-h);
  background: var(--app-surface);
  border-bottom: 1px solid var(--app-border);
  box-shadow: 0 2px 8px var(--app-sh-soft);
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1.25rem;
  position: sticky;
  top: 0;
  z-index: 30;
}

.app-topbar-search {
  flex: 1 1 auto;
  max-width: 380px;
}
.app-topbar-search .input { border-radius: var(--app-radius-sm); }

.app-topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .35rem;
}

.app-icon-button {
  width: 34px;
  height: 34px;
  border-radius: var(--app-radius-sm);
  border: 0;
  background: transparent;
  color: var(--app-text-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  position: relative;
}

.app-icon-button:hover { background: var(--app-surface); color: var(--app-text); box-shadow: var(--app-shadow-1); }

.app-user {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .25rem .5rem;
  border-radius: var(--app-radius-sm);
  text-decoration: none;
  color: var(--app-text);
}

.app-user:hover { background: var(--app-surface); color: var(--app-text); }

.app-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--app-primary-soft);
  color: var(--app-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  font-weight: 700;
  flex: 0 0 auto;
  border: 2px solid var(--app-surface);
  box-shadow: var(--app-shadow-1);
}

.app-user-name { font-size: var(--fs-base); font-weight: 600; line-height: 1.15; }
.app-user-role { font-size: var(--fs-xs); color: var(--app-text-faint); line-height: 1.15; }

/* 프로필 메뉴(layout.php) - 이름 머리 + 항목, 로그아웃은 구분선 아래 빨강 */
.app-user-menu { position: relative; }
.app-user-menu > summary { list-style: none; cursor: pointer; }
.app-user-menu > summary::-webkit-details-marker { display: none; }
.app-user-menu[open] > .app-user { background: var(--app-surface); }
.app-user-dropdown {
  position: absolute;
  top: calc(100% + .4rem);
  right: 0;
  z-index: 60;
  min-width: 13.5rem;
  padding: .35rem;
  background: var(--app-surface);
  border: 0;
  border-radius: var(--app-radius-md);
  box-shadow: var(--app-shadow-4);
}
.app-user-dropdown-head {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .5rem .6rem .65rem;
  margin-bottom: .3rem;
  border-bottom: 1px solid var(--app-border);
}
.app-user-dropdown-item {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .5rem .6rem;
  border-radius: var(--app-radius-sm);
  color: var(--app-text);
  font-size: var(--fs-base);
  text-decoration: none;
  white-space: nowrap;
}
.app-user-dropdown-item > svg { color: var(--app-text-muted); flex: 0 0 auto; }
.app-user-dropdown-item:hover { background: var(--app-surface-sunk); color: var(--app-text); }
.app-user-dropdown-item.is-danger { color: var(--app-danger); margin-top: .3rem; border-top: 1px solid var(--app-border); border-radius: 0 0 var(--app-radius-sm) var(--app-radius-sm); }
.app-user-dropdown-item.is-danger > svg { color: var(--app-danger); }
.app-user-dropdown-item.is-danger:hover { background: var(--app-danger-soft); color: var(--app-danger); }
.app-user-dropdown form { margin: 0; }
button.app-user-dropdown-item { width: 100%; border: 0; background: none; cursor: pointer; font: inherit; text-align: left; }
.app-user-dropdown-item.is-current { background: var(--app-primary-soft); color: var(--app-primary); font-weight: 600; }
.app-user-dropdown-section { padding: .35rem .6rem .2rem; font-size: var(--fs-xs); color: var(--app-text-faint); }
.app-user-dropdown-divider { height: 1px; background: var(--app-border); margin: .3rem 0; }

.app-content { flex: 1 1 auto; min-width: 0; padding: .9rem 1.1rem 1.75rem; }

/* section/container는 화면들이 이미 쓰고 있다. 사이드바 레이아웃에서는 폭 제한과
   큰 여백이 오히려 방해라서 걷어낸다. */
.app-content .section { padding: 0; }
.app-content .container { max-width: none; width: 100%; margin: 0; }

/* ── global.css에 빠져 있는 Bulma 레이아웃 규칙 ─────────────────────────────
   global.css는 "실사용 클래스만 담은 손 유지 발췌본"이라 .field.is-grouped(가로 배치)가
   들어 있지 않다. 그래서 검색 폼의 .control이 전부 세로로 떨어져 있었다.
   발췌본을 건드리는 대신 여기서 채운다. */
.control { position: relative; }
.control.is-expanded { flex: 1 1 auto; min-width: 0; }

.field.is-grouped { display: flex; align-items: center; gap: .4rem; }
.field.is-grouped.is-grouped-multiline { flex-wrap: wrap; }
.field.is-grouped > .control { margin: 0; }

.buttons { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }

/* 12칸 그리드 - global.css에는 .column.is-12 하나뿐이라 .is-3 같은 폭 지정이 전부
   무시되고 모든 칸이 같은 너비로 벌어져 있었다(폼 레이아웃이 깨지던 원인). */
.column.is-1 { flex: none; width: 8.3333%; }
.column.is-2 { flex: none; width: 16.6666%; }
.column.is-3 { flex: none; width: 25%; }
.column.is-4 { flex: none; width: 33.3333%; }
.column.is-5 { flex: none; width: 41.6666%; }
.column.is-6 { flex: none; width: 50%; }
.column.is-7 { flex: none; width: 58.3333%; }
.column.is-8 { flex: none; width: 66.6666%; }
.column.is-9 { flex: none; width: 75%; }
.column.is-10 { flex: none; width: 83.3333%; }
.column.is-11 { flex: none; width: 91.6666%; }
.column.is-12 { flex: none; width: 100%; }
.column.is-one-fifth { flex: none; width: 20%; }
.column.is-one-quarter { flex: none; width: 25%; }
.column.is-one-third { flex: none; width: 33.3333%; }
.column.is-half { flex: none; width: 50%; }

/* ── 목록 화면 ──────────────────────────────────────────────────────────────
   화면 패턴 `목록 화면`. 검색줄·표·페이지 이동이 **한 장의 카드**
   안에 들어간다. 예전에는 셋이 따로 떠 있어 어디까지가 한 목록인지 경계가 흐렸다. */
.list-card {
  background: var(--app-surface);
  border: 1px solid transparent;
  border-radius: var(--app-radius);
  box-shadow: var(--app-shadow);
  margin-bottom: var(--app-gap);
  overflow: hidden;
}

/* 검색줄 - **조건만** 놓는다. 신규 등록·선택 삭제 같은 동작은 여기 섞지 않는다
   (검색과 등록이 한 덩어리로 보이던 문제, 2026-09-22 지적).

   `라벨 | 입력`을 두 벌 놓은 네 칸 격자다. 조건 두 개가 한 줄에 나란히 앉고 라벨·입력의
   세로줄이 전부 맞는다(사용자가 준 화면 예시 그대로). 라벨 칸 폭을 고정했기 때문에
   왼쪽 벌과 오른쪽 벌의 입력 시작점도 같다.
   보기가 많아 반 칸에 안 들어가는 조건은 `.is-full`로 그 줄을 통째로 쓴다.
   조건은 접지 않고 처음부터 다 펼쳐 둔다 - 관리자 화면이라 넓어도 되고, 접어 두면
   무슨 조건으로 보고 있는지 안 보인다. */
.list-search {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr) 5.5rem minmax(0, 1fr);
  column-gap: .6rem;
  row-gap: .45rem;
  align-items: center;
  padding: 1rem 1.1rem;
  background: transparent;
  border-bottom: 1px solid var(--app-border);
}

/* 왼쪽 라벨 칸 */
.list-search-label {
  font-size: var(--fs-sm);
  color: var(--app-text-muted);
  white-space: nowrap;
  line-height: var(--app-field-h);
}

/* 오른쪽 입력 칸 - 한 칸에 여러 개가 들어가면 여기서 나란히 눕는다 */
.list-search-field {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .4rem;
  min-width: 0;
}

/* 보기가 많은 조건은 남은 칸을 전부 쓴다 */
.list-search-field.is-full { grid-column: 2 / -1; }

/* 알약(보기를 펼쳐 놓은 조건)이 든 칸은 **라벨 옆부터 줄 끝까지**(2 / -1) 늘어난다 - 반 칸에 두면 보기 여섯 개가
   두 줄로 접히면서 둘째 줄 오른쪽이 휑하게 빈다(2026-09-23 지적).
   ⚠️ `grid-column-end: -1`만 주면 안 된다 - 시작이 auto이고 끝만 정해지면 브라우저가 **맨 오른쪽 한 칸**에 놓아서
   보기가 전부 오른쪽 끝으로 쏠렸다(같은 날 지적). 오른쪽 반 칸(3~4열)에 둬야 하는 조건은
   화면에서 `is-half`를 붙인다(아래). */
.list-search-field:has(.filter-options) { grid-column: 2 / -1; }
/* 보기가 적은 두 조건을 한 줄에 나란히 둘 때(`상태 | 구분`처럼) */
.list-search-field.is-half:has(.filter-options) { grid-column: auto; }

/* 한 줄 안의 칸은 같은 높이로 선다 - 1~2px씩 어긋나면 줄이 안 맞아 보인다 */
.list-search .button,
.list-search .input,
.list-search .select,
.list-search .select select { height: var(--app-field-h); }

/* 검색줄의 조건 알약은 **너비를 맞춘다.** 글자 수대로 두면 두 글자 보기는 좁고 여섯 글자 보기는
   넓어서 줄이 들쭉날쭉해 보인다(2026-09-23 지적). 가장 긴 보기가 들어가는 폭으로 고정하고,
   그보다 긴 것만 제 폭을 갖는다. 줄이 바뀌어도 칸이 세로로 맞는다. */
.list-search .filter-check {
  height: var(--app-field-h);
  min-width: 8rem;
  justify-content: flex-start;
}

/* 화면이 좁으면 폭 맞추기를 포기한다 - 알약이 화면 밖으로 밀리는 것보다 낫다 */
@media (max-width: 700px) {
  .list-search .filter-check { min-width: 0; }
}
/* 보기 중 성격이 다른 하나를 조금 떨어뜨린다(_filter_options의 $optApart) */
.filter-check.is-apart { margin-left: 1rem; }
/* 검색줄에서는 떼지 않는다 - 줄이 꺾이면 틈만 남아 어색하다(2026-09-23 사용자 지적). 등록·수정 폼에서만 뗀다 */
.list-search .filter-check.is-apart { margin-left: 0; }

/* 입력칸은 제 칸을 꽉 채운다 - 폭이 제각각이면 오른쪽 끝이 들쭉날쭉해 보인다 */
.list-search-field > .input { flex: 1 1 8rem; min-width: 0; }
.list-search-field > .select { flex: 0 0 auto; }
.list-search .select select { min-width: 8rem; }
/* 날짜 칸은 날짜가 딱 들어갈 만큼만(2026-09-23 사용자 지적 - 너무 크다) */
.list-search .input[type="date"] { flex: 0 0 auto; width: 9.4rem; padding-left: .5rem; padding-right: .4rem; }

/* 검색/초기화는 줄 오른쪽 끝에 */
.list-search-actions {
  grid-column: 2 / -1;
  justify-self: end;
  display: flex;
  gap: .4rem;
}

/* 화면이 좁으면 한 줄에 조건 하나씩 - 억지로 두 벌을 유지하면 입력칸이 너무 좁아진다 */
@media (max-width: 1100px) {
  .list-search { grid-template-columns: 5.5rem minmax(0, 1fr); }
  .list-search-field.is-full { grid-column: 2; }
}

/* 보기가 몇 개뿐인 조건은 드롭다운 대신 **보기를 전부 펼쳐 놓는다**.
   드롭다운은 열기 전까지 어떤 값이 있는지 알 수 없어서, 관리 화면에서는 한눈에
   보이는 쪽이 낫다(2026-09-22 사용자 요구). 하나만 고르면 라디오, 여러 개 고를 수
   있으면 체크박스이고, 생김새는 같은 알약 버튼이다. */
.filter-options {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .25rem;
  min-width: 0;
}

/* 보기 글이 길어 한 줄에 못 담는 조건은 세로로 쌓는다.
   억지로 가로로 붙이면 알약이 화면 밖으로 밀린다. */
.filter-options.is-stack {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: .3rem;
}

.filter-options.is-stack .filter-check {
  height: auto;
  min-height: var(--app-field-h);
  padding: .4rem .7rem;
  white-space: normal;
  text-align: left;
}

/* 보기가 많은 조건은 묶음 이름을 중간에 달아 준다 */
.filter-options-group {
  display: inline-flex;
  align-items: center;
  height: var(--app-field-h);
  font-size: var(--fs-xs);
  color: var(--app-text-faint);
  padding: 0 .1rem 0 .5rem;
  white-space: nowrap;
}

/* 알약 안의 동그라미/네모는 작게 - 글자보다 커 보이면 줄이 들쭉날쭉해진다 */
.filter-check input[type="radio"],
.filter-check input[type="checkbox"] { width: 16px; height: 16px; }

/* 표 바로 위 줄 - 건수와 선택 삭제처럼 **표에 대한** 동작만 */
.list-actions {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .55rem 1.1rem;
  border-bottom: 1px solid var(--app-border);
}

.list-actions .list-count { font-size: var(--fs-sm); color: var(--app-text-muted); }
.list-actions .is-right { margin-left: auto; display: flex; gap: .4rem; }
.list-actions .button { height: 30px; font-size: var(--fs-sm); }

/* 표 - 카드가 이미 테두리를 갖고 있으므로 표는 선만 그린다. 머리 띠가 떠 보이게 좌우·위를 조금 띄운다 */
.list-table { overflow-x: auto; padding: .6rem .75rem 0; }
.list-table .table { margin: 0; }
.list-table .table tbody tr:last-child td { border-bottom: 0; }

.col-check { width: 2.5rem; text-align: center; }
.col-no { width: 4.5rem; }
/* 관리 칸은 **절대 줄을 바꾸지 않는다** - 버튼이 아래로 접히면 행 높이가 그 줄만 두 배가
   되어 표가 들쭉날쭉해진다(2026-09-23 지적). 칸이 모자라면 칸이 넓어진다. */
.col-manage { width: 8.5rem; text-align: right; white-space: nowrap; }
/* 끌어서 순서 바꾸기(public/js/row-reorder.js) - 맨 앞 손잡이 칸.
   끄는 행은 브라우저 고스트가 아니라 **행 자체**가 세로로만 움직인다(translateY) - 떠 있는 것처럼
   그림자를 주고 다른 행 위로 올린다. 손잡이의 touch-action: none은 휴대폰에서 끌 때 화면이
   같이 스크롤되지 않게 한다. */
.col-drag { width: 2rem; padding-right: 0 !important; text-align: center; }
tbody .col-drag { cursor: grab; touch-action: none; }
.drag-handle { color: var(--app-text-faint); letter-spacing: -2px; white-space: nowrap; user-select: none; }
tbody .col-drag:hover .drag-handle { color: var(--app-text); }
.table tbody tr.is-dragging { position: relative; z-index: 2; box-shadow: var(--app-shadow-4); }
.table tbody tr.is-dragging > td { background: var(--app-primary-soft); }
body.is-row-dragging, body.is-row-dragging * { cursor: grabbing !important; user-select: none; }
.table tbody.is-saving { opacity: .6; pointer-events: none; }
/* 분류·상태 칸은 뱃지 하나뿐이다 - 제 폭만 차지하고 줄을 바꾸지 않는다 */
.col-tone { width: 1%; white-space: nowrap; }

/* 관리 칸의 수정/삭제 - 연한 배경 작은 버튼 */
.btn-row {
  display: inline-flex;
  align-items: center;
  gap: .2rem;
  height: 26px;
  padding: 0 .6rem;
  border: 0;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}

.btn-row svg { display: block; }
.btn-row.is-edit { background: var(--app-primary-soft); color: var(--app-primary); }
.btn-row.is-edit:hover { background: #d4e4f5; color: var(--app-primary-dark); }
.btn-row.is-delete { background: var(--app-danger-soft); color: var(--app-danger); }
.btn-row.is-delete:hover { background: #f9d4d5; color: var(--app-danger-dark); }
/* 보기(고객용 화면을 새 탭에 여는 등) - 고치지도 지우지도 않는 동작이라 무채색 */
.btn-row.is-view { background: var(--app-surface-sunk); color: var(--app-text); }
.btn-row.is-view:hover { background: var(--app-surface-pressed); }
/* 관리 칸에 버튼이 셋인 표(보기·수정·삭제, 수정·감추기·삭제) */
.col-manage.is-wide { width: 12rem; }

/* 아래쪽 - 왼쪽 총건수, 가운데 페이지 이동, 오른쪽 개수 선택 */
.list-foot {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .7rem 1.1rem;
  border-top: 1px solid var(--app-border);
}

.list-foot .list-count { font-size: var(--fs-sm); color: var(--app-text-muted); flex: 0 0 auto; }
.list-foot .pagination { margin: 0 auto; }
.list-foot .list-per-page { flex: 0 0 auto; }
.list-foot .list-per-page select { min-width: 7.5rem; }

/* 목록이 비었을 때 - 표 자리에 안내만. 점선 상자 + 돋보기(기준 이미지 EMPTY STATE, 2026-09-26),
   안의 링크(조건 풀기 등)는 버튼 모양이다 */
.list-empty {
  margin: 1rem;
  padding: 2rem 1rem;
  border: 2px dashed var(--app-border-strong);
  border-radius: var(--app-radius-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .6rem;
  text-align: center;
  color: var(--app-text-muted);
  font-size: var(--fs-base);
  font-weight: 600;
}
.list-empty::before {
  content: "";
  width: 48px;
  height: 48px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a3a9b4' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E") center / contain no-repeat;
}
.list-empty a {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 1rem;
  border-radius: 999px;
  background: var(--app-primary);
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  box-shadow: var(--app-shadow-1);
}

/* ── 등록/수정 화면 ──────────────────────────────────────────────────────────
   한 장의 카드 안에 회색 테두리 패널을 얹는다. 패널은 2열로 놓이고, 긴 항목
   (비고 등)을 담는 패널만 전체 폭을 쓴다. 라벨은 입력 왼쪽에 붙는다 - 위에
   얹으면 줄 수가 두 배가 되어 화면이 쓸데없이 길어진다.                        */
.form-card {
  background: var(--app-surface);
  border: 1px solid transparent;
  border-radius: var(--app-radius);
  box-shadow: var(--app-shadow);
  padding: var(--app-gap);
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--app-gap);
}

/* 등록·수정 화면의 칸(기본 정보·연락처 정보…)은 **한 줄에 하나씩 전체 폭**으로 쌓는다(2026-09-23 사용자 요구).
   좌우 두 칸으로 나누면 칸마다 폭이 반이라 주소·이메일 같은 값이 좁게 접혔다. 칸 **안의** 입력 두 개짜리
   배치(`항목 | 항목`)는 그대로 2열이다 - 그건 칸이 아니라 입력 줄이다. */
.form-card > .form-grid { grid-template-columns: minmax(0, 1fr); }
/* 예외: 짝이 되는 두 칸을 50%씩 나란히 봐야 하는 화면(`보내는 쪽 | 받는 쪽`). is-full 칸은 전체 폭 */
.form-card > .form-grid.is-halves { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* 여러 줄 입력칸(비고·내용·기타사항…)도 300px까지만 늘어나고 넘으면 칸 안에서 스크롤 - 모든 화면 공통.
   끌어서 키울 수는 있지만 300px을 넘지 않는다. */
.textarea { max-height: 300px; overflow-y: auto; }

/* 전체 폭을 쓰는 패널 */
.form-grid > .form-panel.is-full { grid-column: 1 / -1; }
/* 칸 안 두 열 격자에서 한 줄을 통째로 쓰는 입력(긴 내용 등) */
.form-grid > .form-row.is-span { grid-column: 1 / -1; }

.form-panel {
  border: 1px solid var(--app-border);
  border-radius: var(--app-radius-md);
  padding: .85rem;
}

.form-panel-title {
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--app-text);
  margin: 0 0 .7rem;
}

/* 한 줄 = 라벨 + 입력. 라벨 폭을 고정해야 여러 줄의 입력칸 왼쪽이 맞는다. */
.form-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-bottom: .55rem;
}

.form-row:last-child { margin-bottom: 0; }
.form-row.is-top { align-items: flex-start; }
/* 칸 오류(form-validate.js가 입력 바로 뒤에 붙이는 .error-message)는 **입력 아래** 한 줄로 - 라벨 폭만큼 들여쓴다
   (기준 이미지 TEXT FIELD의 Error 문구 자리, 2026-09-27). 옆에 붙으면 입력칸이 그만큼 줄어든다 */
.form-row { flex-wrap: wrap; }
.form-row > .error-message { flex: 0 0 100%; margin: -.2rem 0 0 calc(7rem + .6rem); }

.form-row > .label {
  flex: 0 0 7rem;
  margin: 0;
  padding-top: 0;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--app-text-label);
  line-height: var(--app-field-h);
}

.form-row.is-top > .label { line-height: 1.5; padding-top: .45rem; }

/* 입력이 자리를 다 먹는다. 옆에 버튼이 붙는 줄은 .form-row-input으로 묶는다. */
.form-row > .input,
.form-row > .textarea,
.form-row > .select,
.form-row > .form-row-input { flex: 1 1 0; min-width: 0; } /* 기본 폭 0 - 줄바꿈(flex-wrap)이 켜져 있어도 라벨 옆에 남는다 */

.form-row > .select select { width: 100%; }

.form-row-input { display: flex; align-items: center; gap: .4rem; }
.form-row-input > .input,
.form-row-input > .select { flex: 1 1 auto; min-width: 0; }
.form-row-input > .button { flex: 0 0 auto; }

/* 등록/수정 폼에서도 보기가 정해진 값은 드롭다운 대신 알약으로 펼친다
   (2026-09-22 사용자 요구 - 목록 검색줄과 같은 이유다). */
.form-row > .filter-options { flex: 1 1 auto; min-height: var(--app-field-h); }

/* 알약이 여러 줄로 감싸지면 라벨을 첫 줄에 맞춘다 - 가운데로 두면 라벨만 아래로 내려간다 */
.form-row:has(> .filter-options) { align-items: flex-start; }
.form-row:has(> .filter-options) > .label { line-height: var(--app-field-h); }

/* 줄을 늘렸다 줄였다 하는 입력 묶음(사업자등록번호처럼 개수가 정해지지 않은 값) */
.repeat-rows { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .35rem; }
.repeat-row { display: flex; align-items: center; gap: .35rem; }
.repeat-row > .input { min-width: 0; }
.repeat-row > .input.is-key { flex: 0 0 11rem; }
.repeat-row > .input.is-note { flex: 1 1 auto; }

/* 첫 줄이 대표 값이다 - 표시를 붙여 어느 것이 지금 쓰는 값인지 보이게 한다 */
.repeat-row::before {
  content: "과거";
  flex: 0 0 2.6rem;
  font-size: var(--fs-xs);
  color: var(--app-text-faint);
  text-align: center;
}

.repeat-row.is-first::before {
  content: "대표";
  color: var(--app-primary);
  font-weight: 700;
}

/* 줄 끝의 [＋]/[×] - 품목처럼 줄을 늘리는 표와 같은 규칙이다.
   첫 줄에는 [＋](줄 추가)만, 나머지 줄에는 [×](빼기)만 보인다. */
.repeat-act { display: inline-flex; gap: .2rem; }
.repeat-act .btn-row { min-width: 1.9rem; justify-content: center; font-size: var(--fs-base); }
.repeat-row:first-child .repeat-act [data-repeat-remove],
.repeat-row:not(:first-child) .repeat-act [data-repeat-add] { display: none; }

.repeat-add { align-self: flex-start; }

/* 필수 표시 */
.form-row > .label .req { color: var(--app-danger); margin-left: .15rem; }

/* 라벨 아래가 아니라 입력 아래에 붙는 도움말/에러 - 라벨 폭만큼 들여쓴다 */
.form-help {
  margin: -.25rem 0 .55rem calc(7rem + .6rem);
  font-size: var(--fs-xs);
  color: var(--app-text-faint);
}

/* 아래 버튼 바 - 왼쪽 "목록으로", 오른쪽 저장류 */
.form-actions {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-top: var(--app-gap);
  padding-top: var(--app-gap);
  border-top: 1px solid var(--app-border);
}

.form-actions .is-right { margin-left: auto; display: flex; gap: .4rem; }

/* ── 상세 화면 ──────────────────────────────────────────────────────────────
   수정/삭제/목록으로는 화면 머리 오른쪽에 있고, 본문은 "라벨-값" 2열 패널이다. */
.detail-grid {
  display: grid;
  /* 상세 화면의 칸도 한 줄에 하나씩 전체 폭(2026-09-23 사용자 요구 - 등록·수정 화면과 같다) */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--app-gap);
  margin-bottom: var(--app-gap);
}

/* 비고·내용처럼 길게 쓰는 값 - 줄바꿈은 살리고, 300px을 넘으면 칸 안에서 스크롤(2026-09-23 사용자 요구).
   값이 길다고 상세 화면 전체가 한없이 길어지면 아래 칸을 찾기 어렵다. */
.detail-list dd.detail-long {
  white-space: pre-line;
  max-height: 300px;
  overflow-y: auto;
}

.detail-grid:last-child { margin-bottom: 0; }
.detail-grid > .is-full { grid-column: 1 / -1; }

.detail-panel {
  background: var(--app-surface);
  border: 1px solid transparent;
  border-radius: var(--app-radius);
  box-shadow: var(--app-shadow);
  padding: .85rem 1rem;
}

.detail-panel-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .7rem;
}

.detail-panel-title {
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--app-text);
}

.detail-panel-head .is-right { margin-left: auto; font-size: var(--fs-sm); }

/* 표를 감싼 패널은 표가 테두리에 붙게 한다 */
.detail-panel.has-table { padding: .85rem 0 0; }
.detail-panel.has-table .detail-panel-head { padding: 0 1rem; }
.detail-panel.has-table .table { margin: 0; }
.detail-panel.has-table .table tbody tr:last-child td { border-bottom: 0; }
/* 표 머리글은 위아래 모두 선을 긋는다(2026-09-22 사용자 지적). 목록 카드는 머리글 위에
   `.list-actions`의 밑줄이 있어 위아래가 맞는데, 상세 패널은 패널 제목 아래에 선이 없어서
   머리글 아래쪽에만 선이 보였다. */
.detail-panel.has-table .table thead th { border-top: 0; }
/* 표 머리 띠가 패널 안에서 떠 보이게 좌우를 띄운다 */
.detail-panel.has-table > .table,
.detail-panel.has-table > .table-container { margin: 0 .75rem; width: calc(100% - 1.5rem); }

/* 상세 화면 탭 - 머리 아래 한 줄. 탭마다 주소가 다른 링크다(`?tab=`).
   지금 탭은 파랑 밑줄로만 표시한다 - 파랑은 기본 동작 색이라 배경까지 칠하면 버튼처럼 보인다.
   스크롤은 두지 않는다(2026-09-23 사용자 요구) - 예전 overflow-x:auto가, 밑줄을 줄 선에 겹치려고 1px 내린 탭
   때문에 세로 스크롤 막대까지 띄웠다. 좁은 화면에서 넘치면 다음 줄로 내려간다. */
/* 2026-09-26 떠 있는 틀 안에 탭(기준 이미지 TABS). 2차(사용자 지적 - 이미지와 달랐다):
   지금 탭은 **진한 굵은 글씨 + 틀 맨 아래에 붙은 파란 밑줄 3px**(글자는 파랑이 아니다), 안 고른 탭은 흐린 글자,
   틀 모서리는 8px, 밑줄이 틀 아래 가장자리에 닿도록 틀에 위아래 여백을 두지 않는다 */
.detail-tabs {
  display: flex;
  width: fit-content;
  max-width: 100%;
  flex-wrap: wrap;
  gap: .25rem;
  margin-bottom: 1rem;
  padding: 0 .5rem;
  border-radius: var(--app-radius-sm);
  background: var(--app-surface);
  box-shadow: var(--app-shadow-1);
  overflow: hidden;
}
.detail-tabs a {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .8rem 1.1rem calc(.8rem - 3px);
  border-bottom: 3px solid transparent;
  color: var(--app-text-label);
  font-size: var(--fs-base);
  white-space: nowrap;
}
.detail-tabs a:hover { color: var(--app-text); }
/* 아이콘은 글자보다 한 톤 연하게 - 켜진 탭에서만 글자와 같은 색이 된다 */
.detail-tabs a svg { color: var(--app-text-muted); }
.detail-tabs a:hover svg,
.detail-tabs a.is-active svg { color: var(--app-text); }
.detail-tabs a.is-active {
  border-bottom-color: var(--app-primary);
  color: var(--app-text);
  font-weight: 700;
}

/* 표 패널 안에 접어 둔 폼(하위 항목 추가 등) - 패널 좌우 여백을 여기서 준다.
   표를 사이에 두고 감쌀 수 없어 <details>로 못 묶는다. 여는 단추는 패널 머리에 있고
   열림 상태만 .is-open으로 옮겨 준다(각 화면의 작은 스크립트). */
.detail-panel.has-table .list-empty { margin: 0; border: 0; }
.panel-form { display: none; padding: .9rem 1rem 1rem; border-top: 1px solid var(--app-border); }
.panel-form.is-open { display: block; }
.panel-form .form-actions { margin-top: .9rem; padding-top: .9rem; }

/* 라벨-값 목록. 표가 아니라 그리드로 짜야 값이 길어도 라벨 폭이 안 밀린다. */
.detail-list {
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr);
  row-gap: 0;
}

.detail-list > dt,
.detail-list > dd {
  padding: .4rem 0;
  border-bottom: 1px solid var(--app-border-soft);
  min-width: 0;
}

.detail-list > dt { color: var(--app-text-muted); font-size: var(--fs-sm); }
.detail-list > dd { margin: 0; color: var(--app-text); word-break: break-all; }
.detail-list > dt:nth-last-of-type(1),
.detail-list > dd:nth-last-of-type(1) { border-bottom: 0; }
.detail-list a { color: var(--app-primary); text-decoration: none; }
.detail-list a:hover { text-decoration: underline; }

/* 화면이 좁아지면 2열을 포기한다 - 가로 스크롤이 생기는 것보다 낫다 */
@media (max-width: 900px) {
  .form-grid,
  .form-card > .form-grid.is-halves,
  .detail-grid { grid-template-columns: minmax(0, 1fr); }
}

/* 검색줄 안의 체크박스 필터 - 버튼처럼 보이되 눌린 상태가 드러나야 한다 */
/* 2026-09-26 알약 모양으로 떠 있고, 고르면 들어간다 */
.filter-check {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  border-color: transparent;
  border-radius: 999px;
  padding: 0 .8rem;
  font-size: var(--fs-base);
  color: var(--app-text-muted);
  background: var(--app-surface);
  box-shadow: var(--app-shadow-1);
  cursor: pointer;
  white-space: nowrap;
}

.filter-check:hover { border-color: var(--app-border-strong); }
.filter-check:has(input:checked) { border-color: var(--app-primary); background: var(--app-primary-soft); color: var(--app-primary); box-shadow: var(--app-shadow-in); }
.filter-check input { margin: 0; }

/* ── 화면 머리 ──────────────────────────────────────────────────────────────
   한 줄이다: 아이콘 + 제목 + (건수·상태) ... 오른쪽 버튼.
   경로 표시는 뺐다 - 사이드바에 지금 위치가 이미 강조돼 있어 같은 정보를 두 번 보여주는
   셈이었고, 머리가 두 줄이 되면서 본문이 그만큼 밀렸다. */
.page-head {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin-bottom: .75rem;
}

.page-head-icon {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--app-surface);
  box-shadow: var(--app-shadow-1);
  color: var(--app-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

.page-head-title { font-size: var(--fs-xl); font-weight: 700; line-height: 1.3; }
.page-head-meta { font-size: var(--fs-sm); color: var(--app-text-muted); }
.page-head-tags { display: inline-flex; align-items: center; gap: .25rem; }
.page-head-actions { margin-left: auto; display: flex; gap: .4rem; align-items: center; }
/* 상세 화면 탭 안에서 연 화면의 제목줄 - 위의 대상 이름보다 작게(_page_head.php의 is-sub) */
.page-head.is-sub { margin-bottom: .8rem; }
.page-head.is-sub .page-head-icon { width: 28px; height: 28px; }
.page-head.is-sub .page-head-icon svg { width: 16px; height: 16px; }
.page-head.is-sub .page-head-title { font-size: var(--fs-lg); }

/* ── 카드(.box) ─────────────────────────────────────────────────────────── */
.box {
  background: var(--app-surface);
  border: 1px solid transparent;
  border-radius: var(--app-radius);
  box-shadow: var(--app-shadow);
  padding: 1rem;
  margin-bottom: var(--app-gap);
}

/* 카드 안 마지막 요소의 아래 여백은 지운다 - 안 그러면 카드 밑이 들쭉날쭉해진다 */
.box > *:last-child { margin-bottom: 0; }
.box p { margin: 0 0 .6rem; }
.box p:last-child { margin-bottom: 0; }

.box .title { color: var(--app-text); }
.title { margin: 0 0 .6rem; font-weight: 700; color: var(--app-text); }
.title.is-6 { font-size: var(--fs-lg); }
.title.is-5 { font-size: var(--fs-lg); }
.title.is-4 { font-size: var(--fs-xl); }
.title.is-3 { font-size: var(--fs-2xl); margin-bottom: .25rem; }
.heading {
  font-size: var(--fs-xs);
  color: var(--app-text-muted);
  letter-spacing: .02em;
  margin-bottom: .3rem;
  text-transform: none;
}

/* 통계 카드 - 대시보드 상단 */
.stat-card {
  display: flex;
  align-items: center;
  gap: .75rem;
  background: var(--app-surface);
  border: 1px solid transparent;
  border-radius: var(--app-radius);
  box-shadow: var(--app-shadow);
  padding: 1rem 1.1rem;
  text-decoration: none;
  color: inherit;
  height: 100%;
}

.stat-card:hover { border-color: var(--app-border-strong); color: inherit; }
.stat-card-body { flex: 1 1 auto; min-width: 0; }
.stat-card-label { font-size: var(--fs-sm); color: var(--app-text-muted); }
.stat-card-value { font-size: var(--fs-2xl); font-weight: 700; line-height: 1.25; margin-top: .15rem; }
.stat-card-hint { font-size: var(--fs-xs); color: var(--app-text-faint); }

.stat-card-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--app-radius-md);
  box-shadow: var(--app-shadow-1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  background: var(--app-primary-soft);
  color: var(--app-primary);
}

.stat-card-icon.is-danger { background: var(--app-danger-soft); color: var(--app-danger); }
.stat-card-icon.is-success { background: var(--app-success-soft); color: var(--app-success); }
.stat-card-icon.is-warning { background: var(--app-warning-soft); color: var(--app-warning); }

/* ── 버튼 ───────────────────────────────────────────────────────────────── */
/* 떠 있는 둥근 네모(--app-radius-btn), 누르면 들어간다(기준 이미지 PRIMARY BUTTON · PRESSED STATE) */
.button {
  border-radius: var(--app-radius-btn);
  border: 1px solid var(--app-border);
  background: var(--app-surface);
  box-shadow: var(--app-shadow-1);
  color: var(--app-text);
  font-size: var(--fs-base);
  font-weight: 600;
  padding: 0 1.05rem;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s, box-shadow .12s;
}

.button:hover { background: var(--app-surface-hover); border-color: transparent; color: var(--app-primary); }
.button:active { box-shadow: var(--app-shadow-in); }
.button.is-small { font-size: var(--fs-sm); padding: 0 .6rem; }
.button[hidden] { display: none !important; } /* .button의 display가 hidden을 이겨서 접수 뒤에도 [보내기]가 남았다(2026-09-24) */

/* 기본 동작(저장/등록/검색)은 --app-primary */
.button.is-dark,
.button.is-link,
.button.is-primary {
  background: var(--app-primary);
  border-color: var(--app-primary);
  color: #fff;
}

.button.is-dark:hover,
.button.is-link:hover,
.button.is-primary:hover {
  background: var(--app-primary-dark);
  border-color: var(--app-primary-dark);
  color: #fff;
}

.button.is-danger { background: var(--app-danger); border-color: var(--app-danger); color: #fff; }
.button.is-danger:hover { background: var(--app-danger-dark); border-color: var(--app-danger-dark); color: #fff; }
.button.is-danger.is-light { background: var(--app-danger-soft); border-color: transparent; color: var(--app-danger); }
.button.is-danger.is-light:hover { background: #f9d4d5; color: var(--app-danger-dark); }

.button.is-success { background: var(--app-success); border-color: var(--app-success); color: #fff; }
.button.is-warning { background: var(--app-warning); border-color: var(--app-warning); color: #fff; }
.button.is-white { background: var(--app-surface); }
.button[disabled] { background: var(--app-disabled); border-color: transparent; color: #fff; box-shadow: none; cursor: not-allowed; }

/* 보내는 중 - form-validate.js가 누른 버튼에 붙인다. 들어간 면 + 도는 표시(기준 이미지 PRESSED STATE, 2026-09-26).
   버튼은 잠겨(disabled) 있지만 제 색을 지킨다 - 무엇을 눌렀는지 보여야 한다 */
.button.is-loading,
.button.is-loading[disabled] { color: var(--app-text); background: var(--app-surface); box-shadow: var(--app-shadow-in); pointer-events: none; }
.button.is-primary.is-loading,
.button.is-primary.is-loading[disabled] { background: var(--app-primary-dark); border-color: var(--app-primary-dark); color: #fff; }
.button.is-danger.is-loading,
.button.is-danger.is-loading[disabled] { background: var(--app-danger-dark); border-color: var(--app-danger-dark); color: #fff; }
.button.is-loading::before {
  content: "";
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  animation: app-spin .8s linear infinite;
}
@keyframes app-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .button.is-loading::before { animation-duration: 2.4s; } }

/* 체크박스를 버튼처럼 쓰는 필터 토글 */
.button.checkbox { font-weight: 500; display: inline-flex; align-items: center; gap: .3rem; }

/* ── 입력 ───────────────────────────────────────────────────────────────── */
.input,
.textarea,
.select select {
  border-color: var(--app-border);
  border-width: 1px;
  border-style: solid;
  border-radius: var(--app-radius-sm);
  background: var(--app-surface);
  box-shadow: none;
  color: var(--app-text);
  font-size: var(--fs-base);
  padding: 0 .65rem;
  width: 100%;
  line-height: 1.4;
}

.textarea { padding: .5rem .65rem; line-height: 1.5; }

.input:focus,
.textarea:focus,
.select select:focus {
  outline: 0;
  background: var(--app-surface);
  border-color: var(--app-primary);
  box-shadow: 0 0 0 3px var(--app-primary-ring);
}

.input::placeholder, .textarea::placeholder { color: var(--app-text-faint); }
.input.is-small, .select.is-small select { font-size: var(--fs-sm); padding: 0 .5rem; }
.textarea.is-small { font-size: var(--fs-sm); padding: .4rem .5rem; }
.input[readonly] { background: var(--app-surface-sunk); color: var(--app-text-muted); }

.label { font-size: var(--fs-sm); font-weight: 600; color: var(--app-text-label); margin-bottom: .3rem; display: block; }
.label.is-small { font-size: var(--fs-sm); }
.help { font-size: var(--fs-xs); color: var(--app-text-faint); margin-top: .25rem; }
.field { margin-bottom: var(--app-gap); }

/* 체크박스·라디오 - 브라우저 기본 모양 대신 기준 이미지(CHECKBOX · RADIO)대로 직접 그린다(늘 지키는 규칙 7).
   안 고른 것: 흰 면 + 회색 선(체크박스는 둥근 네모, 라디오는 동그라미).
   고른 체크박스: 파랑 네모 + 흰 체크. 고른 라디오: **파랑 원 + 가운데 흰 점**.
   크기는 쓰는 곳에서 width/height로 정한다(기본 18px). 숨겨 쓰는 입력(.switch-option > input 등)은 opacity 0 그대로라 영향 없다. */
input[type="checkbox"],
input[type="radio"] {
  -webkit-appearance: none;
  appearance: none;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1.5px solid var(--app-border-strong);
  background: var(--app-surface) center / 72% no-repeat;
  box-shadow: 0 1px 2px var(--app-sh-soft);
  cursor: pointer;
  vertical-align: middle;
  transition: background-color .12s, border-color .12s, box-shadow .12s;
}
input[type="checkbox"] { border-radius: 5px; }
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:checked {
  border-color: var(--app-primary);
  background-color: var(--app-primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  box-shadow: 0 1px 3px var(--app-primary-ring);
}
input[type="radio"]:checked {
  border-color: var(--app-primary);
  background: var(--app-primary) radial-gradient(circle, var(--app-surface) 0 31%, transparent 35%);
  box-shadow: 0 1px 3px var(--app-primary-ring);
}
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible { outline: 2px solid var(--app-primary); outline-offset: 2px; }
input[type="checkbox"]:disabled,
input[type="radio"]:disabled { opacity: .5; cursor: not-allowed; }

/* 검증 실패 표시 - form-validate.js가 붙이는 클래스 */
.field-error { border-color: var(--app-danger) !important; }
.error-message { display: block; font-size: var(--fs-xs); color: var(--app-danger); margin-top: .25rem; }

/* ── 표 ─────────────────────────────────────────────────────────────────── */
/* 2026-09-26 표 머리는 떠 있는 둥근 띠(기준 이미지 COMPACT TABLE HEADER). 띠 모서리를 둥글리려고 separate로 그린다 */
.table {
  width: 100%;
  background: transparent;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-base);
}

.table th {
  background: var(--app-surface-sunk);
  color: var(--app-text-muted);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-align: left;
  padding: .5rem .6rem;
  border-bottom: 1px solid var(--app-border);
  white-space: nowrap;
}

/* 표 본문 글자 크기는 한 가지다. 화면마다 셀에 크기 클래스를 붙였다 말았다 해서
   같은 행 안에서도 글자가 커졌다 작아졌다 하던 문제(2026-09-22 지적)를 여기서 막는다.
   정말 보조인 것(태그·help·머리글)만 따로 작아진다. */
.table td {
  font-size: var(--fs-base);
  padding: .45rem .6rem;
  border-bottom: 1px solid var(--app-border-soft);
  vertical-align: middle;

  /* 긴 값(주소 등)은 줄을 바꾼다. 칸 밖으로 삐져나가는 건 그냥 오류다
     (2026-09-22 지적 - 상세 화면의 주소가 상자 테두리를 뚫고 나갔다). */
  white-space: normal;
  overflow-wrap: anywhere;
}

/* 목록 표만 한 줄 고정 - 행 높이가 들쭉날쭉하면 훑어보기 어렵다.
   길어지면 카드 안에서 가로로 밀린다(.list-table이 스크롤을 갖고 있다). */
.list-table .table th,
.list-table .table td { white-space: nowrap; }
.list-table .table td.is-wrap { white-space: normal; }

/* 긴 글(내용·비고)만 줄바꿈을 허용한다 - 전부 허용하면 행 높이가 제각각이 되어
   표를 훑어 내려가기 어렵다 */
.table td.is-wrap { white-space: normal; }

.table tbody tr:hover { background: var(--app-surface-hover); }
.table thead th { background: var(--app-surface); border-bottom: 0; }
.table thead { filter: drop-shadow(-2px -2px 3px var(--app-sh-soft)) drop-shadow(2px 3px 4px var(--app-sh-dark)); }
.table thead tr:first-child th:first-child { border-radius: 10px 0 0 10px; }
.table thead tr:first-child th:last-child { border-radius: 0 10px 10px 0; }
.table thead + tbody tr:first-child td { padding-top: .6rem; }
.table.is-narrow th, .table.is-narrow td { padding: .36rem .5rem; }

.table tfoot th { background: var(--app-surface-sunk); border-top: 1px solid var(--app-border); }
.table a { color: var(--app-primary); text-decoration: none; font-weight: 500; }
.table a:hover { text-decoration: underline; }
.table-container { overflow-x: auto; }

/* 표 안의 작은 입력(행 단위 편집 화면) - 칸이 꽉 차 보이지 않게 */
.table .input.is-small, .table .select.is-small select { min-width: 0; }

/* ── 상태 태그 ──────────────────────────────────────────────────────────── */
/* 2026-09-26 뜻 색을 **진한 바탕 + 흰 글자**로(기준 이미지 BADGES 모양, 사용자가 A안 - 색 구분은 그대로) */
.tag {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  padding: .16rem .6rem;
  font-size: var(--fs-xs);
  font-weight: 600;
  background: var(--app-text-muted);
  color: #fff;
  white-space: nowrap;
}

.tag.is-light { background: var(--app-text-faint); color: #fff; }
.tag.is-dark, .tag.is-primary { background: var(--app-primary); color: #fff; }
.tag.is-success { background: var(--app-success); color: #fff; }
.tag.is-danger, .tag.is-danger.is-light { background: var(--app-danger); color: #fff; }
.tag.is-warning, .tag.is-warning.is-light { background: var(--app-warning); color: #fff; }
.tag.is-info { background: var(--app-info); color: #fff; }
.tag.is-medium { font-size: var(--fs-sm); padding: .25rem .7rem; }


/* ── 분류·상태 색(tone) ────────────────────────────────────────────────────
   목록 뱃지(`.tag.is-coded`)와 검색줄 알약(`.filter-check.is-coded`)이 **같은 이름**을 쓴다.
   검색줄에서 고른 색과 표에서 보이는 색이 다르면 색이 아무것도 알려주지 못한다
   (2026-09-22 사용자 요구). 값 → 색 이름 표는 각 모델이 갖고, `_filter_options.php`의
   `$optTones`로 알약에 넘긴다.

   분류처럼 **성격**을 가르는 값은 blue·purple·teal·orange·pink·gray를 서로 다르게만 쓴다 -
   초록=좋음/빨강=나쁨 같은 뜻은 없다. 상태는 화면 색 규칙 그대로 green=정상, yellow=주의,
   red=끝·위험, sky=다른 곳으로 넘어감, gray=임시. 두 상태가 같은 색이면 구분이 안 된다.
   뱃지는 --c-fg 바탕 + 흰 글자, 알약·행은 옅은 --c-bg/--c-row */
.app-tone-blue   { --c-bg: #e3edf9; --c-fg: #2f6fb7; --c-line: #b4cdea; --c-row: #f3f7fc; }
.app-tone-purple { --c-bg: #ece6f7; --c-fg: #6a4fa3; --c-line: #cdbfe6; --c-row: #f7f4fb; }
.app-tone-teal   { --c-bg: #dff3f1; --c-fg: #1f7f78; --c-line: #a8dbd5; --c-row: #f2faf9; }
.app-tone-orange { --c-bg: #fdeedd; --c-fg: #b45f16; --c-line: #f1c796; --c-row: #fdf7ef; }
.app-tone-pink   { --c-bg: #fbe6ee; --c-fg: #b03a6a; --c-line: #eeb6cc; --c-row: #fdf4f7; }
.app-tone-gray   { --c-bg: #eceff3; --c-fg: #5d6470; --c-line: #cfd4dc; --c-row: #f6f7f9; }
.app-tone-green  { --c-bg: #dcf3e8; --c-fg: #1f8a58; --c-line: #a6dcc2; --c-row: #f1faf5; }
.app-tone-yellow { --c-bg: #fef0cc; --c-fg: #a86a0c; --c-line: #f3d58a; --c-row: #fef9ec; }
.app-tone-red    { --c-bg: #fde8e8; --c-fg: #c0383e; --c-line: #f2b6b8; --c-row: #fdf4f4; }
.app-tone-sky    { --c-bg: #e0f1fb; --c-fg: #1f78a8; --c-line: #a9d5ee; --c-row: #f2f9fd; }

.tag.is-coded { background: var(--c-fg); color: #fff; }

/* 상태는 뱃지만이 아니라 **행 전체**를 칠할 수 있다(`tr.app-row.app-tone-*`, 2026-09-22 사용자 요구) -
   목록을 훑을 때 상태 칸까지 눈을 옮기지 않고 알 수 있어야 한다.
   배경은 뱃지보다 훨씬 옅은 톤(`--c-row`)이다 - 뱃지 색을 그대로 깔면 글자가 묻힌다.
   `tr`이 아니라 `td`에 칠하는 이유: 셀 배경이 행 배경을 덮기 때문에 한쪽만 칠하면
   마우스를 올렸을 때 색이 안 바뀐다. */
.table tbody tr.app-row > td { background: var(--c-row); }
.table tbody tr.app-row:hover > td { background: var(--c-bg); }

/* 알약은 **끄고 켜기 전부** 제 색을 띤다 - 켠 것만 색이 나오면 무엇이 무슨 색인지
   눌러 보기 전에는 알 수 없다. 켜면 배경까지 칠해져 지금 고른 것이 드러난다. */
.filter-check.is-coded { border-color: var(--c-line); color: var(--c-fg); }
.filter-check.is-coded:hover { border-color: var(--c-fg); }
.filter-check.is-coded:has(input:checked) {
  border-color: var(--c-fg);
  background: var(--c-bg);
  color: var(--c-fg);
}

/* ── 모달 ──────────────────────────────────────────────────────────────────
   짧은 입력(한 건 등록·작은 항목 추가)만 모달로 연다. 긴 화면은 페이지다 -
   탭이 여러 개인 상세 화면 같은 것을 모달에 넣으면 스크롤이 두 겹이 된다(2026-09-23).

   `<dialog>`를 쓴다. 브라우저가 겹침 순서·ESC 닫기·바깥 영역을 알아서 처리해서,
   직접 만들면 늘 빠뜨리는 것들(뒤 화면이 같이 스크롤되는 것 등)이 생기지 않는다. */
.modal-sheet {
  width: min(560px, calc(100vw - 32px));
  padding: 0;
  border: 0;
  border-radius: var(--app-radius-lg);
  background: var(--app-surface);
  color: var(--app-text);
  box-shadow: var(--app-shadow-4);
}

.modal-sheet.is-wide { width: min(640px, calc(100vw - 32px)); }
/* 목록이 들어가는 모달 - 칸이 줄바꿈되지 않을 만큼 넓게(2026-09-23 지적).
   640px에서는 긴 이름·큰 금액이 두 줄로 접혀 읽기 어려웠다. */
.modal-sheet.is-list { width: min(1040px, calc(100vw - 32px)); }
/* 가로가 아니라 **세로**를 키우는 모달(2026-09-23 사용자 요구) - 안에서 위로 펼쳐지는 목록(폴다운)이
   창 머리까지 덮지 않고 창 안에 들어오게 본문에 최소 높이를 준다 */
.modal-sheet.is-tall .modal-body { min-height: min(600px, 72vh); max-height: min(80vh, 760px); }
/* 모달이 열려 있는 동안 **뒤 페이지는 스크롤되지 않는다**(2026-09-23 사용자 요구 - 모달에서 휠을 굴리면 뒤가 내려가서
   닫고 나면 보던 자리가 아니었다). <dialog>는 스크롤을 잠가 주지 않는다. 모달 안(목록·입력칸)의 스크롤은 그대로다.
   **뒤 화면 폭이 바뀌면 안 된다**(2026-09-26 사용자): 예전 `scrollbar-gutter: stable`은 스크롤이 **없던** 화면에도 스크롤 자리를
   만들어 모달을 여는 순간 뒤 화면이 그만큼 좁아졌다. 이제 modal.js가 열기 직전에 실제 스크롤바 폭을 재서 --app-scroll-gap에 넣는다 -
   스크롤이 없던 화면은 0, 있던 화면은 그 폭만큼만 오른쪽을 비워 둔다. */
html:has(dialog[open]) { overflow: hidden; padding-right: var(--app-scroll-gap, 0px); }
/* 뒤 막은 **아주 옅은 색**(2026-09-26 사용자 - 35% 갈색 막은 탁했고, 완전 투명은 창이 떠 보이지 않았다 → 16%). <dialog>라 뒤 화면은
   눌리지 않고, 바깥을 누르면 창이 닫힌다(modal.js). 창은 --app-shadow-4로 떠 보인다. */
.modal-sheet::backdrop { background: rgba(30, 40, 56, .16); }

.modal-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: 1.1rem 1.25rem .4rem;
}

.modal-head .modal-title { font-size: var(--fs-lg); font-weight: 700; margin: 0; }
/* 부제 뒤에 조건 알약이 붙을 수 있다(목록 모달) - 글자와 알약의 밑줄을 맞춘다 */
.modal-head .modal-sub {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: var(--fs-sm);
  color: var(--app-text-muted);
  font-variant-numeric: tabular-nums;
}

.modal-close {
  margin-left: auto;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  color: var(--app-text-faint);
  padding: .2rem .3rem;
}

.modal-close:hover { color: var(--app-text); }
.modal-body { padding: .75rem 1.25rem 1rem; max-height: min(70vh, 640px); overflow-y: auto; }

.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: .4rem;
  padding: .5rem 1.25rem 1.25rem;
}
/* 왼쪽 [삭제] · 오른쪽 [취소][저장] (수정 모달) */
.modal-foot > .is-right { display: flex; gap: .4rem; margin-left: auto; }
/* [삭제]는 수정일 때만 - hidden은 위 `.button[hidden]`이 지켜 준다 */

/* 확인창·알림창(public/js/ui-dialog.js - confirm()/alert() 대신, 2026-09-25). 문구의 줄바꿈을 살린다 */
.modal-sheet.ui-dialog { width: min(420px, calc(100vw - 32px)); }
/* 기준 이미지 MODAL CONFIRMATION(2026-09-27): 왼쪽에 둥근 그림(뜻 색 옅은 바탕), 오른쪽에 제목 + 문구(왼쪽 맞춤),
   아래에 [취소] | [확인] 두 버튼을 같은 폭으로. 그림은 위험한 확인이면 주의(노랑 !), 그냥 확인·알림이면 참고(파랑 i).
   버튼 색은 이미지를 따르지 않는다 - 삭제는 빨강(늘 지키는 규칙 1). */
.ui-dialog-main { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: .9rem; align-items: start; padding: 1.35rem 1.35rem .4rem; }
.ui-dialog-icon {
  grid-row: 1 / span 2;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--app-info-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232f77c9' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M12 11v6M12 7h.01'/%3E%3C/svg%3E") center / 24px no-repeat;
}
.ui-dialog:has([data-ui-ok].is-danger) .ui-dialog-icon {
  background: var(--app-warning-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23c26422' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M12 6v8M12 18h.01'/%3E%3C/svg%3E") center / 26px no-repeat;
}
.ui-dialog .ui-dialog-main .modal-title { font-size: var(--fs-lg); font-weight: 700; margin: .1rem 0 .2rem; }
.ui-dialog .ui-dialog-body { padding: 0; white-space: pre-line; word-break: keep-all; line-height: 1.6; color: var(--app-text-muted); }
.ui-dialog .modal-foot { display: flex; gap: .6rem; padding: 1rem 1.35rem 1.35rem; border-top: 0; }
.ui-dialog .modal-foot .button { flex: 1 1 0; min-width: 0; }
/* 골라진 버튼이 보여야 Enter·←/→가 무엇을 누르는지 안다 - 마우스로 연 창에서도 늘 테두리를 그린다 */
.ui-dialog .modal-foot .button:focus { outline: 2px solid var(--app-primary); outline-offset: 2px; }
.ui-dialog .modal-foot .button.is-danger:focus { outline-color: var(--app-danger); }

/* 모달 안은 `라벨 | 입력` 두 칸 격자 - 목록 검색줄과 같은 규칙이라 입력칸 왼쪽이 맞는다 */
.modal-row {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  align-items: center;
  gap: .5rem;
  margin-bottom: .55rem;
}

.modal-row > .label {
  justify-self: end;
  margin: 0;
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--app-text-muted);
}

.modal-row .req { color: var(--app-danger); }
/* 칸 오류는 입력 칸 아래(라벨 칸은 비운다) */
.modal-row > .error-message { grid-column: 2; margin-top: -.3rem; }
.modal-row-wide { grid-column: 1 / -1; }

/* 라벨 칸을 비우고 입력 아래에 붙는 설명·에러 */
.modal-help {
  grid-column: 2;
  font-size: var(--fs-sm);
  color: var(--app-text-faint);
  margin: -.3rem 0 .5rem;
}

/* [?] 도움말 말풍선(public/js/help-tip.js) - 설명은 누를 때만 뜬다(2026-09-23 사용자 요구) */
.help-wrap { display: inline-flex; vertical-align: middle; margin-left: .25rem; }
.help-tip {
  width: 1.15rem;
  height: 1.15rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 50%;
  background: var(--app-surface);
  box-shadow: var(--app-shadow-1);
  color: var(--app-text-faint);
  font-size: var(--fs-xs);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.help-tip:hover,
.help-tip[aria-expanded="true"] { border-color: var(--app-primary); color: var(--app-primary); }
.help-bubble {
  position: fixed; /* 자리는 help-tip.js가 [?] 옆으로 잡는다 - 스크롤 칸 끝에서 잘리지 않게 */
  z-index: 1000;
  width: max-content;
  max-width: 20rem;
  padding: .55rem .75rem;
  border-radius: 8px;
  background: var(--app-tooltip);   /* 짙은 회색(기준 이미지 TOOLTIP, 2026-09-27) */
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: 1.5;
  text-align: left;
  white-space: normal;
  box-shadow: var(--app-shadow-1);
}
.help-bubble[hidden] { display: none; }
/* 꼬리 - [?]를 가리킨다 */
.help-bubble::before {
  content: "";
  position: absolute;
  top: -5px;
  left: calc(8px + .575rem - 5px);
  border: 5px solid transparent;
  border-top: 0;
  border-bottom-color: var(--app-tooltip);
}
.help-bubble.is-above::before { top: auto; bottom: -5px; border: 5px solid transparent; border-bottom: 0; border-top-color: var(--app-tooltip); }
.help-bubble b { color: #fff; }

.modal-row .fixed-value {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  height: var(--app-field-h);
  font-weight: 600;
}

@media (max-width: 560px) {
  .modal-row { grid-template-columns: minmax(0, 1fr); }
  .modal-row > .label { justify-self: start; }
  .modal-help { grid-column: 1; }
}

/* 감춰 둔 줄 - `감춘 것도 보기`를 켰을 때만 보인다. 흐리게 두어 지금 쓰는 줄과 갈린다.
   (Bulma의 `.is-hidden`과 이름이 겹치지 않게 `-row`를 붙였다 - 그건 display:none이다) */
.table tbody tr.is-hidden-row > td { background: var(--app-surface-hover); }
.table tbody tr.is-hidden-row a { color: var(--app-text-muted); }
.table tbody tr.is-hidden-row td { color: var(--app-text-faint); }

/* 연·월 넘기기 - 드롭다운보다 화살표가 낫다. 한 해씩 넘길 때 한 번만 누르면 된다 */
.year-nav { display: inline-flex; align-items: center; gap: .3rem; }
.year-nav strong { min-width: 4.5rem; text-align: center; font-size: var(--fs-lg); }
.year-nav .button { min-width: 2.2rem; padding: 0 .5rem; }
.year-nav.is-lead strong { font-size: var(--fs-xl); min-width: 6rem; }

/* 통계 화면의 집계 칸 - **몇 줄씩 끊어** 보여주고 제목 줄 오른쪽 버튼으로 넘긴다
   (2026-09-23 사용자 요구). 줄 수백 개를 한꺼번에 펼치면 아래 칸들이 화면 밖으로
   밀리고, 스크롤만 달면 지금 몇 번째를 보는지 알 수 없다. 넘기기는 public/js/stat-panel.js. */
.stat-panel-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .5rem;
}

.stat-panel-head .title { margin: 0; }

/* 표 안의 사업자등록번호(bizno-input.js로 넣은 값) - 줄을 바꾸지 않고 자릿수가 눈에 들어오게 */
.is-bizno { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* 칸마다 무엇을 세는지 한 줄로 적는다 - 금액만 있으면 들어온 돈인지 나간 돈인지
   이름만 봐서는 알 수 없었다(2026-09-23 지적) */
.stat-basis {
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--app-text-faint);
  margin-left: .35rem;
}

/* 막대 - 순액이라 음수가 나온다. 길이는 절댓값, 색으로 방향을 보인다 */
.stat-bar { height: .75rem; border-radius: 2px; background: var(--app-primary); }
.stat-bar.is-minus { background: var(--app-danger); }

.stat-pager { margin-left: auto; display: inline-flex; align-items: center; gap: .2rem; }
.stat-pager .button { min-width: 1.9rem; height: 26px; padding: 0 .4rem; font-size: var(--fs-sm); }
.stat-pager-meta { font-size: var(--fs-sm); color: var(--app-text-muted); min-width: 3rem; text-align: center; }

.stat-panel .table { margin-bottom: 0; }
.stat-panel .table tbody tr:last-child td { border-bottom: 0; }

/* 순위 - 금액 순으로 줄 세운 목록이라 몇 등인지가 곧 내용이다 */
.stat-rank {
  width: 2.2rem;
  color: var(--app-text-faint);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* ── 알림 상자 ──────────────────────────────────────────────────────────── */
/* 기준 이미지 ALERT BANNER(2026-09-27): 뜻 색의 **옅은 바탕 + 왼쪽 아이콘 + 오른쪽 ×**.
   기본은 참고(파랑 i), is-warning 주의(노랑 삼각형), is-danger 오류(빨강 !), is-success 완료(초록 ✓).
   ×는 `<button class="delete" type="button" aria-label="닫기"></button>`를 넣으면 생기고, 누르면 띠가 사라진다(layout.php).
   is-light는 아이콘 없는 흐린 안내 한 줄이다. */
.notification {
  position: relative;
  border-radius: var(--app-radius-md);
  padding: .8rem 1rem .8rem 2.75rem;
  font-size: var(--fs-base);
  background: var(--app-info-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232f77c9' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5M12 8h.01'/%3E%3C/svg%3E") 1rem .85rem / 20px no-repeat;
  color: var(--app-text);
  border: 1px solid #c9dcf2;
  box-shadow: var(--app-shadow-1);
  margin-bottom: var(--app-gap);
}
.notification:has(> .delete) { padding-right: 2.6rem; }
.notification > .delete {
  position: absolute;
  top: .7rem;
  right: .7rem;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 6px;
  background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235d6470' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6 6 18'/%3E%3C/svg%3E") center / 14px no-repeat;
  cursor: pointer;
}
.notification > .delete:hover { background-color: rgba(40, 52, 72, .08); }

.notification.is-light { background: var(--app-surface); color: var(--app-text-muted); border-color: transparent; padding-left: 1rem; }
.notification.is-danger, .notification.is-danger.is-light { background: var(--app-danger-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23d9444a' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v6M12 16.5h.01'/%3E%3C/svg%3E") 1rem .85rem / 20px no-repeat; color: var(--app-danger-dark); border-color: #f2b6b8; padding-left: 2.75rem; }
.notification.is-warning { background: var(--app-warning-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e8950f' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E") 1rem .85rem / 20px no-repeat; color: #5c3d08; border-color: #f3d58a; }
.notification.is-success { background: var(--app-success-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232fa56e' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='m8 12.5 3 3 5-6'/%3E%3C/svg%3E") 1rem .85rem / 20px no-repeat; color: #1f6e48; border-color: #a6dcc2; }
.notification a { color: inherit; font-weight: 700; text-decoration: underline; }

/* ── 페이지네이션 ───────────────────────────────────────────────────────── */
.pagination { display: flex; align-items: center; gap: .3rem; justify-content: center; margin: var(--app-gap) 0 0; }
/* 지금 쪽만 파란 둥근 네모, 나머지는 글자만(기준 이미지 PAGINATION) */
.pagination-first, .pagination-last, .pagination-previous, .pagination-next, .pagination-link {
  border: 1px solid transparent;
  background: transparent;
  border-radius: var(--app-radius-sm);
  padding: .35rem .7rem;
  font-size: var(--fs-sm);
  color: var(--app-text-muted);
  text-decoration: none;
}
.pagination-first:hover, .pagination-last:hover, .pagination-previous:hover, .pagination-next:hover, .pagination-link:hover { border-color: var(--app-primary); color: var(--app-primary); }
.pagination-link.is-static { background: transparent; border-color: transparent; }
.pagination-first[disabled], .pagination-last[disabled], .pagination-previous[disabled], .pagination-next[disabled] { opacity: .45; pointer-events: none; }
.pagination-list { display: flex; gap: .3rem; list-style: none; margin: 0; padding: 0; }

/* 좁은 화면의 쪽 번호 줄 - `« ‹ [11 / 16] › » [20개씩]` 한 줄(2026-09-27 사용자 요구).
   번호 목록과 `11 / 16 쪽` 글자는 감추고 쪽 고르기 드롭다운(_list_foot.php)을 보인다. 더 좁으면 « »도 뺀다 */
.pagination-jump { display: none; }
@media (max-width: 700px) {
  .list-foot { flex-wrap: nowrap; gap: .4rem; padding: .6rem .75rem; }
  .list-foot .list-count,
  .list-foot .pagination-list { display: none; }
  .list-foot .pagination { margin: 0; gap: .1rem; flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; justify-content: flex-start; }
  .list-foot .pagination-jump { display: inline-flex; }
  .list-foot .pagination-jump select { min-width: 5.6rem; }
  .list-foot .pagination-first, .list-foot .pagination-last,
  .list-foot .pagination-previous, .list-foot .pagination-next { padding: .35rem .45rem; flex: 0 0 auto; }
  .list-foot .list-per-page select { min-width: 0; }
}
@media (max-width: 340px) {
  .list-foot .pagination-first, .list-foot .pagination-last { display: none; }
}

/* ── 그 밖 ──────────────────────────────────────────────────────────────── */
.level { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-bottom: var(--app-gap); }
.level-left, .level-right { display: flex; align-items: center; gap: .5rem; }
.level-right { margin-left: auto; }
.level-item { flex: 1 1 0; }

/* 열 묶음 - 가로 여백은 음수 마진으로 상쇄하고, **세로 간격은 칸의 아래 padding 하나로만**
   만든다. 둘 다 주면 간격이 두 배가 되어 "어떤 카드는 붙고 어떤 건 떨어져" 보인다.
   마지막 줄의 아래 padding이 다음 블록과의 간격도 겸한다. */
.columns { display: flex; flex-wrap: wrap; margin: 0 -.5rem; }
.column { padding: 0 .5rem var(--app-gap); flex: 1 1 0; min-width: 0; }
.columns.is-multiline { flex-wrap: wrap; }

/* 칸 안의 카드는 자기 아래 여백을 갖지 않는다 - 칸 padding과 겹쳐 두 배가 된다 */
.column > .box,
.column > .stat-card { margin-bottom: 0; }

/* 카드 안 마지막 열 묶음은 아래 여백을 지운다 - 카드 padding이 이미 있다 */
.box > .columns:last-child > .column { padding-bottom: 0; }

.has-text-grey { color: var(--app-text-muted) !important; }
.has-text-grey-light { color: var(--app-border-strong) !important; }
.has-text-danger { color: var(--app-danger) !important; }

/* global.css(Bulma 발췌본)에 빠져 있던 유틸리티 - 화면들이 실제로 쓰는 것만 채운다.
   없으면 간격이나 글자 굵기가 조용히 무시되어 배치가 어긋난다.
   크기 유틸리티는 global.css 값(제각각)을 덮어써서 위 척도에 맞춘다. */
.is-size-4 { font-size: var(--fs-xl); }
.is-size-5 { font-size: var(--fs-lg); }
.is-size-6 { font-size: var(--fs-base); }
.is-size-7 { font-size: var(--fs-sm); }
.has-text-weight-semibold { font-weight: 600; }
.has-text-weight-normal { font-weight: 400; }
.has-text-left { text-align: left; }
.is-fullwidth { width: 100%; }
.select.is-fullwidth, .select.is-fullwidth select { width: 100%; }
.ml-1 { margin-left: .25rem; }
.ml-2 { margin-left: .5rem; }
.ml-3 { margin-left: .75rem; }
.mr-3 { margin-left: 0; margin-right: .75rem; }
.mt-1 { margin-top: .25rem; }
.p-3 { padding: .75rem; }

/* ── 선과 높이 기준 ─────────────────────────────────────────────────────────
   버튼·입력·드롭다운·필터 칩이 제각각 1~2px씩 달라 줄이 안 맞아 보였다.
   높이는 --app-field-h 하나, 테두리는 --app-border 하나로 통일한다. */

/* 인라인 SVG는 기본이 inline이라 글자 baseline에 걸려 아래로 내려앉는다.
   아이콘이 텍스트와 세로 중앙이 안 맞던 진짜 원인이다. */
.app-nav-icon svg,
.app-brand-mark svg,
.app-icon-button svg,
.page-head-icon svg,
.stat-card-icon svg,
.button svg,
.tag svg { display: block; }

/* 조작 요소 높이는 세 단계뿐이다 - 기본 / 작게 / 표 안.
   예전에는 요소마다 padding으로 높이가 정해져 1~3px씩 어긋나 있었다. */
.input,
.select select,
.button,
.filter-check {
  height: var(--app-field-h);
  border-width: 1px;
  border-style: solid;
}

.textarea { height: auto; }

/* `is-small`은 글자만 한 단계 작아지고 **높이는 같다**. 높이까지 줄이면 같은 줄에
   섞였을 때 1~2px씩 어긋나 보인다(2026-09-22 지적). 표 안의 칸만 예외로 28px다. */

/* 드롭다운 화살표(global.css의 .select::after)가 글자를 덮지 않게 자리를 비워 둔다 */
.select select { padding-right: 2rem; }
.select::after { top: 50%; }

/* 버튼 안의 글자·아이콘을 가운데로. 예전에는 블록이라 줄 높이에 따라 흔들렸다. */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  vertical-align: middle;
  padding-top: 0;
  padding-bottom: 0;
}

/* 표 안에서 태그·버튼이 글자 baseline에 걸리지 않게 */
.tag,
.table .button { vertical-align: middle; }

/* 표 줄 - 머리와 본문, 카드 테두리가 서로 다른 회색이었다.
   머리 아래만 진하게 긋고 행 사이는 연한 한 가지로 통일한다. */
.table th { border-bottom: 1px solid var(--app-border); }
.table thead th { border-bottom: 0; }
.table td { border-bottom: 1px solid var(--app-border-soft); }
.table tfoot th { border-top: 1px solid var(--app-border); border-bottom: 0; }

/* 카드로 감싼 표는 바깥 테두리가 이미 있으므로 마지막 줄 선을 지운다 */
.list-table .table tfoot tr:last-child th { border-bottom: 0; }

/* 표 안의 작은 입력이 행 높이를 밀어 올리지 않게.
   높이만 줄이고 글자는 --fs-base 그대로 둔다 - 같은 줄에서 그냥 글자칸은 14px인데
   입력칸만 13px이면 한 행 안에서 크기가 갈린다. */
.table .input,
.table .select select,
.table .textarea,
.table .button { height: 28px; font-size: var(--fs-base); }
.table .textarea { height: auto; }

details > summary { cursor: pointer; }
details > summary::-webkit-details-marker { display: none; }

/* 정의형 표(상세 화면) - "기본 정보 / 연락처 정보" 같은 블록 */
.detail-table th {
  background: var(--app-surface-sunk);
  color: var(--app-text-muted);
  font-weight: 600;
  width: 8.5rem;
}

.detail-table td { color: var(--app-text); }

/* ── NovaP Story(2026-09-28) - Story 카드 · 타임라인 · 사진 바둑판 · 코멘트 · 초대장 ─────────────
   `.claude/features/story/`. 판은 상세 화면의 떠 있는 패널(.detail-panel)을 그대로 쓰고, 여기에는 배치만 더한다.
   색·크기는 :root 변수만 쓴다. Event 항목은 파란 왼쪽 띠 + 별로 일상 기록과 가른다(명세: Event는 하나의 큰 항목). */
.story-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--app-gap);
}
.story-card { display: flex; flex-direction: column; gap: .45rem; color: var(--app-text); }
.story-card:hover { box-shadow: var(--app-shadow-4); }
.story-card-title { font-size: var(--fs-lg); font-weight: 700; }
.story-card-meta { display: flex; flex-wrap: wrap; gap: .35rem .75rem; font-size: var(--fs-sm); color: var(--app-text-muted); }

.story-timeline { display: flex; flex-direction: column; gap: var(--app-gap); }
.story-month {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--app-text-muted);
  padding-left: .25rem;
  margin-bottom: calc(var(--app-gap) * -0.5);
}
.story-item-head { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .6rem; margin-bottom: .5rem; }
.story-item-date { font-weight: 700; color: var(--app-text); }
.story-item-by { font-size: var(--fs-sm); color: var(--app-text-muted); }
.story-item-head .is-right { margin-left: auto; display: inline-flex; gap: .35rem; }
.story-item-title { font-size: var(--fs-lg); font-weight: 700; margin-bottom: .3rem; }
.story-item-title .tag { vertical-align: middle; margin-left: .35rem; }
.story-item-body { white-space: pre-line; word-break: break-word; margin-bottom: .6rem; }
.story-item-foot { display: flex; align-items: center; gap: .75rem; margin-top: .6rem; font-size: var(--fs-sm); color: var(--app-text-muted); }
.story-item-foot a { color: var(--app-primary); }
.story-item.is-hidden { opacity: .65; }
.story-item.is-event { border-left: 4px solid var(--app-primary); }
.story-item.is-event .story-item-title svg { display: inline-block; color: var(--app-primary); vertical-align: -3px; margin-right: .3rem; }
.story-event-row { display: flex; gap: 1rem; align-items: center; }
.story-event-row .story-photo { flex: 0 0 96px; }

.story-photos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: .4rem;
}
.story-photos.is-large { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .6rem; }
.story-photo {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;
  border-radius: var(--app-radius-sm);
  overflow: hidden;
  background: var(--app-surface-sunk);
}
.story-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.story-photo.is-empty { display: flex; align-items: center; justify-content: center; color: var(--app-text-faint); }
.story-photo.is-hidden img { opacity: .4; }
.story-photo-badge {
  position: absolute;
  left: .35rem;
  bottom: .35rem;
  padding: 0 .4rem;
  border-radius: 999px;
  background: rgba(30, 40, 56, .7);
  color: #fff;
  font-size: var(--fs-xs);
  line-height: 1.6;
}
.story-photo-tools { display: flex; gap: .3rem; margin-top: .3rem; }
.story-figure { display: flex; flex-direction: column; }
.story-figure video, .story-figure img.is-full { width: 100%; border-radius: var(--app-radius-sm); background: #000; }

.story-comments { display: flex; flex-direction: column; }
.story-comment { padding: .6rem 0; border-bottom: 1px solid var(--app-border-soft); }
.story-comment:last-child { border-bottom: 0; }
.story-comment.is-hidden { opacity: .6; }
.story-comment-head { display: flex; align-items: center; gap: .5rem; font-size: var(--fs-sm); color: var(--app-text-muted); }
.story-comment-head strong { color: var(--app-text); font-size: var(--fs-base); }
.story-comment-head .is-right { margin-left: auto; display: inline-flex; gap: .3rem; }
.story-comment-body { white-space: pre-line; word-break: break-word; margin-top: .2rem; }
.story-comment-form { display: flex; gap: .5rem; align-items: flex-start; margin-top: .75rem; }
.story-comment-form .textarea { min-height: 2.6rem; }

.story-invite {
  text-align: center;
  padding: 2rem 1rem;
  border-radius: var(--app-radius);
  background: var(--app-primary-soft) center / cover no-repeat;
}
.story-invite-title { font-size: var(--fs-2xl); font-weight: 800; margin-bottom: .6rem; }
.story-invite-when { font-size: var(--fs-lg); font-weight: 700; }
.story-invite-where { margin-top: .3rem; color: var(--app-text-label); }
.story-invite-message { margin: 1rem auto 0; max-width: 32rem; white-space: pre-line; }
.story-invite .button { margin-top: 1rem; }

.story-share { display: flex; gap: .5rem; align-items: center; }
.story-share.is-stacked { margin-bottom: .5rem; }
.story-desc { margin: -.25rem 0 1rem; color: var(--app-text-muted); white-space: pre-line; }
.story-bar { margin-bottom: .75rem; }
.story-foot { margin-top: var(--app-gap); padding: 0; }
.story-after-form { margin-top: var(--app-gap); }
.story-subtitle { margin-top: .75rem; }
.story-notice-actions { margin-top: .6rem; }
.story-item-head.is-tight { margin-bottom: .25rem; }
.story-share .input { flex: 1 1 auto; }

@media (max-width: 900px) {
  .story-photos { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); }
  .story-photos.is-large { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* ── 좁은 화면 ──────────────────────────────────────────────────────────── */
.app-nav-toggle { display: none; }

@media (max-width: 1023px) {
  .app-sidebar {
    transform: translateX(-100%);
    transition: transform .18s ease;
    box-shadow: 0 0 40px rgba(60, 50, 38, .35);
  }

  body.nav-open .app-sidebar { transform: translateX(0); }
  .app-main { margin-left: 0; }
  .app-nav-toggle { display: inline-flex; }
  .app-topbar-search { max-width: none; }
  .app-user-name, .app-user-role { display: none; }
  .column,
  .column[class*="is-"] { flex: 1 1 100%; width: 100%; }
  .app-content { padding: .9rem; }
}

.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(60, 50, 38, .35);
  z-index: 35;
}

.nav-backdrop.is-active { display: block; }

/* 화면 머리가 좁은 화면에서 한 줄을 고집하면 버튼이 제목을 밀어 제목이 한 글자씩 세로로 서고 화면이 가로로 넘친다
   (2026-09-28 NovaP Story 화면에서 발견 - 버튼이 셋인 머리). 700px 아래에선 버튼 줄을 제목 아래로 내린다. */
@media (max-width: 700px) {
  .page-head { flex-wrap: wrap; }
  .page-head-actions { margin-left: 0; width: 100%; flex-wrap: wrap; }
  .story-event-row { align-items: flex-start; }
  .story-event-row .story-photo { flex-basis: 72px; }
}


/* ── 숫자 요약 줄 · 기간 버튼 · 링크 줄 ──────────────────────────────────── */
/* 상세 화면 위쪽의 숫자 요약 줄 - 합계·잔액처럼 먼저 읽어야 하는 값 몇 개.
   .level을 쓰면 화면마다 칸 폭이 달라져서 전용 그리드로 고정한다. */
.detail-stats {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  background: var(--app-surface);
  border: 1px solid transparent;
  border-radius: var(--app-radius);
  box-shadow: var(--app-shadow);
  margin-bottom: var(--app-gap);
}
/* 기간 옆 [이번달][1개월전][2개월전][전체 기간] - 붙여서 한 덩어리로 */
.period-buttons { display: inline-flex; gap: .25rem; margin-left: .2rem; }
/* 달 · 분기 · 연도 · 전체 기간 묶음 사이는 조금 띄운다 */
.period-buttons + .period-buttons { margin-left: .55rem; }
/* [전체 기간] - 기간을 푸는 버튼이라 색을 달리한다(짙은 회색). 파랑은 기본 동작 색이라 겹치지 않게 */
/* 맨 오른쪽 끝으로 민다(기간 칸 안에서) - 좁으면 다음 줄로 내려가도 오른쪽에 붙는다 */
.period-buttons:last-child { margin-left: auto; padding-left: .8rem; }
.button.is-period-all { border-color: var(--app-text-muted); color: var(--app-text-label); }
.button.is-period-all:hover { background: var(--app-surface-sunk); color: var(--app-text-label); }
.button.is-period-all.is-on { background: var(--app-text-label); border-color: var(--app-text-label); color: #fff; }
.period-buttons .button.is-small { height: var(--app-field-h); padding: 0 .5rem; font-size: var(--fs-sm); }

.detail-stats > div {
  padding: .8rem 1rem;
  border-left: 1px solid var(--app-border-soft);
  text-align: center;
  min-width: 0;
}

.detail-stats > div:first-child { border-left: 0; }
.detail-stats .ds-label { font-size: var(--fs-sm); color: var(--app-text-muted); }
.detail-stats .ds-value { font-size: var(--fs-xl); font-weight: 700; line-height: 1.3; margin-top: .15rem; }
.detail-stats .ds-value.is-lead { font-size: var(--fs-2xl); }
.detail-stats .ds-value s { color: var(--app-text-faint); font-weight: 600; }
/* 숫자 아래 한 줄 - 그 값이 어떻게 나왔는지(`들어온 돈 − 나간 돈`) 같은 보조 설명 */
.detail-stats .ds-sub { font-size: var(--fs-xs); margin-top: .15rem; }
.detail-stats .ds-hint {
  grid-column: 1 / -1;
  border-left: 0;
  border-top: 1px solid var(--app-border-soft);
  padding: .45rem 1rem;
  font-size: var(--fs-sm);
  color: var(--app-text-faint);
}

@media (max-width: 720px) {
  .detail-stats { grid-auto-flow: row; }
  .detail-stats > div { border-left: 0; border-top: 1px solid var(--app-border-soft); }
  .detail-stats > div:first-child { border-top: 0; }
}

/* 고객에게 보낼 링크 줄 - 입력칸 + [복사](copy-link.js) */
.share-link { display: flex; gap: .4rem; align-items: center; }
.share-link .input { flex: 1 1 auto; min-width: 0; }
.share-link .button { flex: 0 0 auto; }

@media print {
  /* 인쇄에서는 조작용 요소를 전부 걷어낸다 */
  .no-print,
  .app-sidebar,
  .app-topbar,
  .nav-backdrop,
  .button {
    display: none !important;
  }

  body { background: #fff; }
  .app-main { margin-left: 0; }
  .app-content { padding: 0; }
  .section { padding: 0; }

  .box {
    box-shadow: none !important;
    border: 1px solid #ddd;
    break-inside: avoid;
  }

  /* 배경색을 지우면 표 구분이 사라지므로 테두리로 대신한다 */
  .table th,
  .table td {
    border-color: #999 !important;
  }
}

/* 통계 카드 내부는 <a> 안이라 span으로 짰다 - 줄바꿈이 되게 block으로 편다 */
.stat-card-body,
.stat-card-label,
.stat-card-value,
.stat-card-hint { display: block; }

/* 현재 쪽 표시 */
.pagination-link.is-current {
  background: var(--app-primary);
  border-color: var(--app-primary);
  color: #fff;
  font-weight: 700;
  box-shadow: var(--app-shadow-1);
}

.pagination-ellipsis { padding: 0 .3rem; color: var(--app-text-faint); }

/* 목록 체크박스 - 기본 크기가 제각각이라 줄이 안 맞아 보인다 */
.col-check input[type="checkbox"] { width: 17px; height: 17px; vertical-align: middle; margin: 0; }



/* 숫자 표의 0 - 칸이 비어 보이지 않게 흐리게 적는다(2026-09-23 사용자 요구 - "0도 노출") */
.money-zero { color: var(--app-text-faint); }

/* 그래프(Chart.js) - 캔버스는 이 높이를 채운다 */
.chart-canvas { position: relative; height: 360px; }

/* 토스트(partials/_toast.php, Bootstrap .bs-scope) - 흰 카드 + 성공 초록 체크 / 실패 빨간 느낌표
   (기준 이미지 TOAST STACK). 부트스트랩의 text-bg-* 색(초록·빨강 바탕)을 덮는다 */
.bs-scope .toast.text-bg-success,
.bs-scope .toast.text-bg-danger {
  background: var(--app-surface) !important;
  color: var(--app-text) !important;
  border-radius: var(--app-radius-md);
  box-shadow: var(--app-shadow);
}
/* 기준 이미지 TOAST STACK(2026-09-27): 왼쪽 **색 동그라미 + 흰 표시**(성공 초록 ✓ / 실패 빨강 !) + 굵은 머리말(`성공:`·`오류:`, _toast.php가 넣는다) + 흐린 × */
.bs-scope .toast { border: 1px solid var(--app-border) !important; }
.bs-scope .toast .toast-body { display: flex; align-items: center; gap: .6rem; font-weight: 400; padding: .75rem .9rem; }
.bs-scope .toast .toast-body::before {
  content: "";
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--app-success) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 12.5l4 4L18 8.5'/%3E%3C/svg%3E") center / 15px no-repeat;
}
.bs-scope .toast.text-bg-danger .toast-body::before {
  background: var(--app-danger) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round'%3E%3Cpath d='M12 6v8M12 18h.01'/%3E%3C/svg%3E") center / 15px no-repeat;
}
.bs-scope .toast .toast-body strong { font-weight: 700; margin-right: .2rem; }
.bs-scope .toast .btn-close-white { filter: none; opacity: .45; }
/* ── 비로그인 화면(위쪽 메뉴바·푸터·회원 화면) ── */
/* layout.php가 사이드바(.app-shell) 없이 위쪽 메뉴바 + main.section + 푸터로 그리는 화면.
   푸터가 내용이 짧아도 화면 바닥에 붙도록 body를 세로 flex로 편다 - 로그인 뒤의 .app-shell 화면은
   자기 높이 규칙(.app-main)이 있으므로 여기에 걸리지 않게 :has()로 가른다. */
.error-message:empty { display: none; }

html:not(:has(.app-shell)) { height: 100%; }
/* 풀블리드(전체 폭) 섹션이 100vw 계산 시 가로 스크롤을 만들지 않도록 */
html:not(:has(.app-shell)) { overflow-x: hidden; }

body:not(:has(.app-shell)) {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

body:not(:has(.app-shell)) > main { flex: 1 0 auto; }

.site-footer {
  padding: 1.25rem 1.5rem;
  border-top: 1px solid var(--app-border);
  color: var(--app-text-muted);
  font-size: var(--fs-sm);
}

/* 로그인/회원가입처럼 박스 하나짜리 작은 페이지가 화면 위쪽에 붙어 보이지 않도록
   세로 중앙보다 살짝 위에 오게 배치 (min-height 안에서만 정중앙 정렬하고,
   그 아래 여백을 더 둬서 시각적 중심을 위로 당김) */
.auth-page {
  min-height: 60vh;
  align-items: center;
  padding-bottom: 4rem;
}

/* 소셜 로그인 버튼 - 구글/카카오는 자기 브랜드 색을 쓰는 게 사용자에게 즉시 인지되는
   관용적 UI라 테마 색 대신 각 회사 안내 색을 그대로 쓴다. */
.social-login-divider { display: flex; align-items: center; text-align: center; color: var(--app-text-faint); font-size: var(--fs-xs); }
.social-login-divider::before, .social-login-divider::after { content: ""; flex: 1; border-bottom: 1px solid var(--app-border); }
.social-login-divider span { padding: 0 .75rem; }
.social-login-btn { border: 1px solid #dbdbdb; font-weight: 600; }
.social-login-btn.google { background: #fff; color: #3c4043; }
.social-login-btn.google:hover { background: #f7f7f7; color: #3c4043; }
.social-login-btn.kakao { background: #FEE500; color: #191600; border-color: #FEE500; }
.social-login-btn.kakao:hover { background: #f5dc00; color: #191600; }

/* 회원가입 약관동의 - 개별 항목은 접었다 펼 수 있는 <details>로 본문을 보여준다 */
.terms-agreement { padding: 1rem; }
.terms-content summary { cursor: pointer; }
.terms-content-box {
  max-height: 160px;
  overflow-y: auto;
  margin-top: .4rem;
  padding: .6rem .75rem;
  background: var(--app-surface-sunk);
  border: 1px solid var(--app-border);
  border-radius: var(--app-radius-sm);
  color: var(--app-text-muted);
  line-height: 1.5;
}

.banner-image {
  width: 100%;
  border-radius: var(--app-radius-sm);
  margin-bottom: 0.75rem;
}

.comment-item {
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--app-border-soft);
}
