/* 모달 - 가운데 뜨는 등록·수정·목록 창. 동작은 modal.js, 규칙 claude_common/ui/modal.md
   여러 프로젝트의 custom.css `── 모달` 절을 합친 것(2026-10-04)
   - 기본 560px · .is-wide 640px · .is-list 1040px(목록 표) · .is-tall(본문 세로 최소 높이)
   - 머리(.modal-head: 제목·부제·×) / 본문(.modal-body, 넘치면 본문만 스크롤) / 바닥(.modal-foot: 왼쪽 [삭제], 오른쪽 [취소][저장])
   - 휴대폰(767px 이하)은 화면 가득 - 머리·바닥은 제자리, 본문만 스크롤 (2026-10-03) */
dialog.modal-sheet {
  --modal-w: 560px;
  --modal-pad: 20px;
  width: min(var(--modal-w), calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 0;
  border: 0;
  border-radius: var(--uk-r-xl);
  background: var(--uk-surface);
  color: var(--uk-fg);
  box-shadow: var(--uk-up-lg);
}
/* 화면보다 커지지 않는다(2026-10-05) - 머리·바닥은 제자리, 본문만 줄어들고 창 안에서 스크롤 */
dialog.modal-sheet[open] { display: flex; flex-direction: column; overflow: hidden; }
dialog.modal-sheet > form { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
/* 창 안의 몸통 없는 폼(바닥 [삭제] 버튼이 form="…"으로 가리키는 숨은 삭제 폼 - 폼 안에 폼을 넣을 수 없다)은 자리를 차지하지 않는다.
   같이 늘어나면 휴대폰 화면 가득 창에서 남은 높이를 반씩 나눠 바닥 버튼 줄이 가운데쯤 떴다 (2026-10-05) */
dialog.modal-sheet > form:not(:has(.modal-body)) { flex: none; }
dialog.modal-sheet .modal-head, dialog.modal-sheet .modal-foot { flex: none; }
dialog.modal-sheet .modal-body { flex: 1 1 auto; min-height: 0; }
dialog.modal-sheet.is-wide { --modal-w: 640px; }
/* 목록이 들어가는 창 - 칸이 줄바꿈되지 않을 만큼 넓게(2026-09-23 - 640px에서 긴 이름·큰 금액이 두 줄로 접혔다) */
dialog.modal-sheet.is-list { --modal-w: 1040px; }
/* 가로가 아니라 세로를 키운다(2026-09-23) - 안에서 위로 펼쳐지는 목록이 창 머리를 덮지 않고 창 안에 들어오게 */
dialog.modal-sheet.is-tall .modal-body { min-height: min(600px, 72vh); max-height: min(80vh, 760px); }

/* 옅은 막(2026-09-26 - 35%는 탁했고 완전 투명은 창이 떠 보이지 않았다). 확인창과 같은 값 */
dialog.modal-sheet::backdrop { background: var(--uk-backdrop); }
/* 뒤 화면 스크롤 잠금 + 폭 지키기 - --uk-scroll-gap은 modal.js가 열기 직전에 잰다 */
html:has(dialog.modal-sheet[open]) { overflow: hidden; padding-right: var(--uk-scroll-gap, 0px); }

/* 머리·바닥에 가르는 선과 회색 바닥을 두지 않는다 - 여백으로 나눈다 */
.modal-head { display: flex; align-items: center; gap: 8px; padding: 18px var(--modal-pad) 6px; }
.modal-title { margin: 0; font-size: 18px; font-weight: 700; line-height: 1.35; min-width: 0; }
.modal-sub { display: inline-flex; align-items: center; gap: 6px; min-width: 0; font-size: 13px; color: var(--uk-muted); font-variant-numeric: tabular-nums; }
.modal-close {
  flex: 0 0 auto; margin-left: auto;
  width: 36px; height: 36px; border: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; color: var(--uk-muted); cursor: pointer; font-size: 22px; line-height: 1;
}
.modal-close:hover { background: var(--uk-head); color: var(--uk-fg); }
.modal-close:focus-visible { outline: 2px solid var(--uk-brand); outline-offset: 2px; }

.modal-body { padding: 12px var(--modal-pad) 16px; max-height: min(70vh, 640px); overflow-y: auto; overscroll-behavior: contain; }

.modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 8px var(--modal-pad) var(--modal-pad); }
.modal-foot > .is-right { display: flex; gap: 8px; margin-left: auto; } /* 왼쪽 [삭제] · 오른쪽 [취소][저장] */
.modal-foot [hidden] { display: none !important; } /* [삭제]는 수정일 때만 - 버튼의 display가 hidden을 이기지 않게 */

/* 본문 안은 `라벨 | 입력` 두 칸 격자 - 목록 검색줄과 같은 규칙이라 입력칸 왼쪽이 맞는다 */
.modal-row { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); align-items: center; gap: 8px; margin-bottom: 9px; }
.modal-row > label, .modal-row > .label { justify-self: end; margin: 0; font-size: 14px; font-weight: 500; color: var(--uk-muted); }
.modal-row .req { color: var(--uk-danger); }
.modal-row > .error-message, .modal-row > .modal-help { grid-column: 2; }
.modal-row > .error-message { margin-top: -4px; font-size: 13px; color: var(--uk-danger); }
.modal-row-wide { grid-column: 1 / -1; }
.modal-help { font-size: 13px; color: var(--uk-muted); margin: -4px 0 8px; }
@media (max-width: 560px) {
  .modal-row { grid-template-columns: minmax(0, 1fr); }
  .modal-row > label, .modal-row > .label { justify-self: start; }
  .modal-row > .error-message, .modal-row > .modal-help { grid-column: 1; }
}

dialog.modal-sheet[open] { animation: modal-in .16s ease; }
dialog.modal-sheet[open]::backdrop { animation: modal-fade .16s ease; }
@keyframes modal-in { from { opacity: 0; transform: translateY(6px) scale(.98); } }
@keyframes modal-fade { from { opacity: 0; } }

/* 휴대폰(767px 이하 - 바닥 시트와 같은 경계)은 화면 가득(2026-10-03). 확인창(.ui-dialog)은 따로 된 창이라 가운데 작은 창 그대로다 */
@media (max-width: 767px) {
  dialog.modal-sheet {
    --modal-pad: 10px; /* 좌우 여백 10px(2026-10-03 - 20px은 좁은 화면에서 입력칸 폭을 깎았다) */
    width: 100%; max-width: none;
    height: 100dvh; max-height: none;
    margin: 0; inset: 0;
    border-radius: 0; box-shadow: none;
  }
  dialog.modal-sheet[open] { display: flex; flex-direction: column; animation-name: modal-in-phone; }
  dialog.modal-sheet > form { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  dialog.modal-sheet .modal-body,
  dialog.modal-sheet.is-tall .modal-body { flex: 1 1 auto; min-height: 0; max-height: none; }
  .modal-head { flex-wrap: wrap; } /* 부제는 다음 줄로 - 제목이 두 줄로 꺾이지 않고 ×는 제목 줄에 남게 */
  .modal-sub { order: 3; flex-basis: 100%; }
  .modal-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .modal-foot { padding-bottom: calc(var(--modal-pad) + env(safe-area-inset-bottom)); }
}
@keyframes modal-in-phone { from { opacity: 0; transform: translateY(12px); } }
@media (prefers-reduced-motion: reduce) { dialog.modal-sheet[open], dialog.modal-sheet[open]::backdrop { animation: none; } }
dialog.modal-sheet.is-narrow { --modal-w: 420px; } /* 짧은 입력 창(React판 Modal size="sm", 2026-10-06) */
