/* 헬프데스크 화면 스타일입니다.
   색은 아래 :root의 팔레트로만 씁니다. 다른 자리에 색 값을 직접 적으면
   tests/test_contrast.py가 막습니다. 팔레트의 모든 짝은 WCAG 2의 본문 4.5:1,
   비텍스트 3:1 기준을 넘습니다. 글꼴 정책은 결정 기록 0012를 따릅니다. */

@font-face {
  font-family: "Pretendard Variable";
  src: url("/static/fonts/PretendardVariable.woff2") format("woff2-variations");
  font-weight: 45 920;
  font-style: normal;
  font-display: swap;
}

:root {
  --color-text: #1A1A1A;
  --color-text-muted: #5F6368;
  --color-bg: #F5F6F8;
  --color-card: #FFFFFF;
  --color-table-head: #EEF0F3;
  --color-row-hover: #F1F3F5;
  --color-border: #767676;
  --color-border-soft: #D0D4DA;
  --color-link: #0B57D0;
  --color-focus: #0B57D0;
  --color-error: #B3261E;
  --color-danger: #B3261E;
  --color-danger-text: #FFFFFF;
  --color-sidebar: #141414;
  --color-sidebar-active: #2A2A2A;
  --color-sidebar-text: #FFFFFF;
  --color-sidebar-muted: #B3B3B3;
  --color-primary: #111111;
  --color-primary-text: #FFFFFF;
  --color-bar: #1F4E78;
  --color-bar-done: #0D652D;
  --color-status-0-fg: #3C4043;
  --color-status-0-bg: #E8EAED;
  --color-status-1-fg: #0B3D91;
  --color-status-1-bg: #D2E3FC;
  --color-status-2-fg: #0D652D;
  --color-status-2-bg: #CEEAD6;
  --color-status-3-fg: #A50E0E;
  --color-status-3-bg: #FAD2CF;
  --color-status-4-fg: #7A4B00;
  --color-status-4-bg: #FEEFC3;
  /* 오피스방재 표입니다. 상태 배지와 헷갈리지 않게 그 다섯과 다른 색을 씁니다.
     7.22:1로 본문 기준(4.5:1)을 넉넉히 넘습니다. */
  --color-tag-safety-fg: #5B2A9B;
  --color-tag-safety-bg: #EADDFB;
  /* 매장 상태 공사(바탕)와 승인(흰 바탕에 글자와 테두리)입니다. 짝끼리 6.33:1, 흰 바탕에
     7.75:1입니다. */
  --color-status-5-fg: #005C63;
  --color-status-5-bg: #D0EEF0;
  /* 시설팀만 쓰는 매장 상태 하자입니다(결정 기록 0058, 2026-10-02 전에는 견적, 0050). 매장 상태가 쓰는
     색과 겹치지 않는 자주색이고 짝끼리 6.79:1, 흰 바탕에 8.68:1입니다. */
  --color-status-6-fg: #8A1D5E;
  --color-status-6-bg: #F8DCEB;
}

/* 여백과 크기입니다(설계 문서 2절, 조사 C). 색이 아니라서 팔레트와 떼어 둡니다.
   여백은 4, 8, 12, 16, 24, 32, 48px 일곱 단계뿐이고, 20px 단계(`--space-5`)는 두지 않습니다.
   입력칸과 단추는 40px, 표 안과 작은 단추는 32px입니다. 쪽 폭(`--page-max`)은 한 칸으로 흐르는
   화면(매장 접수 처리)이 넓은 창에서 멈추는 폭입니다. `.content`에는 걸지 않습니다. 시각 칸 폭(`--control-time-w`)은
   한국어 크롬의 `오후 06:48`이 다 드는 폭입니다: 글자 81.2px에 칸의 여백, 테두리, 시계 그림 57px을 더한 138.2px이
   가장 좁은 폭이고(headless Chrome 측정, 2026-10-16), 크롬 기본 폭(143.8px)에 맞춥니다. 작업 시작과 작업 완료,
   오피스 접수시간, 방문 시각이 같이 씁니다(사장님 2026-10-16: 시각의 분이 잘립니다). */
:root {
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 48px;
  --control-h: 40px;
  --control-h-sm: 32px;
  --row-h: 48px;
  --row-h-2line: 64px;
  --badge-h: 24px;
  --page-max: 1200px;
  --control-time-w: 144px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: "Pretendard Variable", Pretendard, "Apple SD Gothic Neo", "Malgun Gothic",
    "Noto Sans KR", sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: var(--color-text);
  background: var(--color-bg);
  /* 한글은 어절로 끊습니다(`경고음이 계 / 속`처럼 낱말 가운데서 끊지 않습니다). 끊을 자리가
     없는 긴 영문과 숫자만 아무 데서나 끊어 화면 밖으로 밀지 않습니다. */
  word-break: keep-all;
  overflow-wrap: anywhere;
}

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* --- 뼈대 ---------------------------------------------------------------- */

.layout {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  min-height: 100vh;
}

.sidebar {
  background: var(--color-sidebar);
  color: var(--color-sidebar-text);
  padding: 24px 16px;
}

.brand {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  padding: 0 8px 24px;
}

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

.nav-link {
  position: relative;
  display: flex;
  align-items: center;
  min-height: var(--control-h);
  padding: 8px 12px;
  border-radius: 8px;
  color: var(--color-sidebar-muted);
  text-decoration: none;
}

.nav-link:hover,
.nav-link:focus-visible {
  background: var(--color-sidebar-active);
  color: var(--color-sidebar-text);
}

/* 지금 화면의 메뉴입니다(`aria-current="page"`, `base.html`). 색만으로 알리지 않고 왼쪽에 흰
   막대를 세웁니다(1.4.1). */
.nav-link[aria-current="page"] {
  background: var(--color-sidebar-active);
  color: var(--color-sidebar-text);
  font-weight: 600;
}

.nav-link[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 8px;
  bottom: 8px;
  left: 0;
  width: 3px;
  border-radius: 2px;
  background: var(--color-sidebar-text);
}

/* 온 화면 공통의 파란 초점 테두리는 검은 사이드바에서 2.88:1이라 3:1에 못 미칩니다(1.4.11).
   사이드바 안에서는 흰색으로 긋습니다. */
.sidebar :focus-visible {
  outline-color: var(--color-sidebar-text);
}

.main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 4px;
  height: 52px;
  padding: 0 24px;
  background: var(--color-card);
  border-bottom: 1px solid var(--color-border-soft);
}

.topbar-user {
  margin-right: 8px;
  font-weight: 600;
}

/* 위 막대의 이름 다음 자리입니다: 관리자 계정(루트만), 비밀번호 변경, 로그아웃(결정 기록
   0046). 로그아웃은 폼 단추지만 옆의 링크와 같은 모양으로 둡니다. 누를 자리는 44px 높이로
   잡습니다 - 글자만 누르게 두면 과녁이 20px 남짓입니다. */

.topbar-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 12px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--color-link);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.topbar-link:hover {
  text-decoration: underline;
}

.topbar-logout {
  display: inline-flex;
  margin: 0;
}

/* 폼 단추는 글자 색을 이어받지 않으므로 링크와 같은 색을 따로 적습니다. */
.topbar-logout-button {
  color: var(--color-link);
}

/* 폭을 제한하지 않습니다. 이 상자가 세로 스크롤을 맡고 있어서(아래 고정 배치) 폭을
   묶으면 창이 그보다 넓을 때 오른쪽에 아무것도 없는 띠가 남고 스크롤 막대가 화면
   가운데에 뜹니다. 표는 `.ledger-scroll` 안에서 따로 가로 스크롤을 합니다.

   `position: relative`로 절대 위치의 기준이 됩니다. 낭독기에만 읽히는 글(`.visually-hidden`)과
   카드 배치의 칸 이름(`.cell-label`)은 절대 위치라, 기준이 없으면 처음 보이는 창을 기준으로
   삼아 스크롤된 본문 아래쪽 자리에서 문서를 늘립니다(출동 기록 목록 60줄, 1920x993에서 문서
   3743px). 그러면 초점이나 낭독기가 그 글에 닿을 때 문서 전체가 밀려 위 막대가 화면 밖으로
   나갑니다. */
.content {
  position: relative;
  padding: 24px;
}

/* 글자 역할입니다(설계 문서 2절). 쪽 제목 24px, 카드 제목 20px, 읽는 글(상세 값, 긴 내용,
   댓글, 입력값) 16px, 표와 이름표와 단추와 탭 14px(`body`), 곁글과 도움말 13px입니다. */
.page-title {
  font-size: 24px;
  line-height: 1.35;
  margin: 0 0 24px;
}

.card {
  background: var(--color-card);
  border: 1px solid var(--color-border-soft);
  border-radius: 10px;
  padding: 16px 24px;
  margin-bottom: 16px;
}

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

.card-title {
  font-size: 20px;
  line-height: 1.4;
  margin: 0;
}

.link {
  color: var(--color-link);
  text-decoration: none;
}

.link:hover {
  text-decoration: underline;
}

/* --- 상태 탭 -------------------------------------------------------------- */

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

/* 옆 여백은 12px입니다. 16px이면 매장 접수의 탭 열한 개(끝의 지연 포함)가 1280에서 두 줄이 됩니다. */
.tab {
  padding: 8px 12px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-card);
  color: var(--color-text);
  text-decoration: none;
  font-weight: 600;
}

.tab.is-active {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-text);
}

/* 매장 접수 목록의 탭은 열두 개(끝의 지연 포함)이고 묶음 사이에 구분선이 넷 섭니다(결정 기록 0058).
   옆 여백 8px, 묶음 안의 사이 4px, 구분선 양옆 4px(사이와 합쳐 8px)이라 1280에서 운영 건수 자릿수로
   988px이고 자리(992px)에 한 줄로 듭니다. 묶음 안의 사이가 8px이면 1,016px로 지연 탭이 둘째 줄로
   내려갑니다. 묶음 안(4px)과 묶음 사이(17px)가 갈려 묶음이 눈에 보입니다. */
.tabs-store {
  gap: 4px;
}

.tabs-store .tab {
  padding: 8px;
}

.tabs-store .tabs-sep {
  margin: 0 4px;
}

/* --- 조건 막대 ------------------------------------------------------------ */

/* 조건 막대를 감싸는 `<form>`입니다. `.card`가 테두리·배경·안 여백을 이미 주므로
   여기서는 그 안의 `.filter-row`가 폼 너비를 그대로 차지하게만 합니다. */
.filters {
  display: block;
  width: 100%;
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 160px;
}

.field-label {
  font-size: 14px;
  color: var(--color-text-muted);
}

.field-help {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-muted);
}

.field-wide {
  flex: 1 1 280px;
}

.field-period {
  min-width: 320px;
}

.period {
  display: flex;
  align-items: center;
  gap: 8px;
}

.period-sep {
  color: var(--color-text-muted);
}

.filter-actions {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

input,
select,
textarea {
  font: inherit;
  font-size: 16px;
  color: var(--color-text);
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 8px 12px;
  min-width: 0;
}

/* 한 줄에 서는 입력칸, 고르는 칸, 단추는 높이가 같습니다(40px, `--control-h`). 이름표가
   칸마다 같은 높이에 섭니다 — 예전에는 37, 39, 41px이 섞여 이름표가 2–4px씩 어긋났습니다.
   `:where`로 감싸 특정성을 올리지 않습니다(칸마다의 규칙이 높이를 바꿀 수 있게). */
input:where(:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"])),
select {
  height: var(--control-h);
  padding-top: 0;
  padding-bottom: 0;
}

/* 표 안의 칸은 32px, 14px입니다(`--control-h-sm`, 표 글자). 곁의 작은 단추와 높이가 같습니다.
   표 칸 안에 들어 있어도 떠서 열리는 창(`.popup-panel`, 매장 공지의 `수정`)은 표의 일부가
   아니라 보통 폼이라 빼 둡니다. 뺀 조건도 `:where` 안이라 구체성은 그대로입니다. */
.ledger input:where(:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"], .popup-panel *)),
.ledger select:where(:not(.popup-panel *)) {
  height: var(--control-h-sm);
  padding: 0 8px;
  font-size: 14px;
}

textarea {
  line-height: 1.6;
  resize: vertical;
}

::placeholder {
  color: var(--color-text-muted);
  opacity: 1;
}

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

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: var(--control-h);
  padding: 0 16px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-card);
  color: var(--color-text);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}

/* 꺼 둔 단추입니다(매장을 고르기 전의 출동 기록 `저장`, 고른 건이 없는 `선택한 0건 출동 기록`). 눌러도
   아무 일이 없다는 것을 흐리게 보여 줍니다(QA 2026-10-01 lane C SF-2). */
.button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* 보낸 폼의 단추입니다(`app.js`가 `aria-busy`·`aria-disabled`를 답니다). 한 번 더 눌러도
   아무 일이 없다는 것을 흐리게 보여 줍니다. 색을 바꾸지 않고 흐리기만 합니다. */
form[aria-busy="true"] [aria-disabled="true"] {
  opacity: 0.6;
  cursor: progress;
}

/* 테두리만 있는 버튼입니다. 지금까지는 이 클래스 자체에 규칙이 없어 `.button`의
   기본 모양(테두리·카드 배경)에 그냥 얹혀 갔습니다 — 여기 그 모양을 그대로 적어
   두어, "테두리 버튼"이라는 이름과 실제로 그리는 모양이 같은 자리에 있게 합니다. */
.button-outline {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  color: var(--color-text);
}

.button-primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-text);
}

.button-small {
  min-height: var(--control-h-sm);
  padding: 0 12px;
  font-size: 13px;
  font-weight: 500;
}

.list-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-bottom: 16px;
}

/* --- 표 ------------------------------------------------------------------ */

.ledger {
  width: 100%;
  border-collapse: collapse;
}

.ledger thead th {
  text-align: left;
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text);
  background: var(--color-table-head);
  height: 40px;
  padding: 8px 12px;
  white-space: nowrap;
}

.ledger td,
.ledger th[scope="row"] {
  padding: 12px;
  border-bottom: 1px solid var(--color-border-soft);
  vertical-align: middle;
}

/* 줄 머리(`th[scope="row"]`)는 낭독기에게 "이 줄은 이것"이라고 말하려고 둔 것이라,
   보이는 모양은 옆 칸(`td`)과 같아야 합니다. 상자 모양은 위에서 `td`와 함께 받고,
   여기서는 브라우저가 `th`에만 주는 굵기와 가운데 정렬을 되돌립니다. 머리글 칸의
   회색 바탕·작은 글씨는 `.ledger thead th`에 있어 여기까지 오지 않습니다. */
.ledger th[scope="row"] {
  font-weight: inherit;
  text-align: inherit;
}

.ledger tbody tr:hover {
  background: var(--color-row-hover);
}

.col-no {
  width: 72px;
  color: var(--color-text-muted);
}

.col-unit {
  white-space: nowrap;
}

.col-title {
  font-weight: 600;
}

.col-edit {
  width: 72px;
  text-align: right;
}

.empty {
  padding: 48px 0;
  text-align: center;
  color: var(--color-text-muted);
}

/* --- 상태 배지 ------------------------------------------------------------ */

/* 모든 시설팀 화면의 상태 뱃지가 이 한 모양입니다(설계 문서 2절). 높이를 박아 두어 줄마다
   뱃지 키가 같습니다. 테두리 자리는 늘 두고 색만 비워 둡니다 — 테두리가 있는 뱃지(신청,
   승인)와 없는 뱃지가 한 줄에 서도 크기가 같습니다. */
.badge {
  display: inline-flex;
  align-items: center;
  height: var(--badge-h);
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.badge-0 {
  color: var(--color-status-0-fg);
  background: var(--color-status-0-bg);
}

.badge-1 {
  color: var(--color-status-1-fg);
  background: var(--color-status-1-bg);
}

.badge-2 {
  color: var(--color-status-2-fg);
  background: var(--color-status-2-bg);
}

.badge-3 {
  color: var(--color-status-3-fg);
  background: var(--color-status-3-bg);
}

.badge-4 {
  color: var(--color-status-4-fg);
  background: var(--color-status-4-bg);
}

/* 목록에 없는 상태 값(검증을 우회해 들어온 값)의 기본 배지입니다. 접수와 같은 색입니다. */
.badge-5 {
  color: var(--color-status-0-fg);
  background: var(--color-status-0-bg);
}

/* 매장 접수 상태입니다(`class="badge st-…"`, 템플릿 필터 `store_status_class`). 설계 문서
   2절의 표 그대로이고, 빨강은 지연과 긴급에만 남겨 두려고 보류는 호박색입니다. 완료와
   공사완료는 같은 초록입니다 — 매장에는 둘 다 `완료`로 보입니다. */
.st-apply {
  background: var(--color-card);
  color: var(--color-text);
  border-color: var(--color-border);
}

.st-accept {
  background: var(--color-status-0-bg);
  color: var(--color-status-0-fg);
}

.st-progress {
  background: var(--color-status-1-bg);
  color: var(--color-status-1-fg);
}

.st-done {
  background: var(--color-status-2-bg);
  color: var(--color-status-2-fg);
}

.st-const {
  background: var(--color-status-5-bg);
  color: var(--color-status-5-fg);
}

.st-constdone {
  background: var(--color-status-2-bg);
  color: var(--color-status-2-fg);
}

.st-approve {
  background: var(--color-card);
  color: var(--color-status-5-fg);
  border-color: var(--color-status-5-fg);
}

.st-hold {
  background: var(--color-status-4-bg);
  color: var(--color-status-4-fg);
}

.st-transfer {
  background: var(--color-tag-safety-bg);
  color: var(--color-tag-safety-fg);
}

/* 시설팀만 쓰는 하자입니다(결정 기록 0058). 매장에는 이관으로 보여 매장 화면에는 서지 않습니다. 거둔
   견적이 쓰던 자주색 짝입니다. */
.st-defect {
  background: var(--color-status-6-bg);
  color: var(--color-status-6-fg);
}

/* 뱃지 곁에 붙는 네모난 표입니다(지연, 요청유형). 상태 뱃지와 높이는 같고 모서리로
   갈립니다. 빨강은 지연과 긴급 둘에만 씁니다. */
.tag {
  display: inline-flex;
  align-items: center;
  height: var(--badge-h);
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
}

.tag-late {
  background: var(--color-status-3-bg);
  color: var(--color-status-3-fg);
}

.tag-normal {
  background: var(--color-status-0-bg);
  color: var(--color-status-0-fg);
  font-weight: 600;
}

.tag-must {
  background: var(--color-card);
  color: var(--color-text);
  border-color: var(--color-border);
}

.tag-urgent {
  background: var(--color-danger);
  color: var(--color-danger-text);
}

/* 보고 전 출동 기록입니다(결정 기록 0056, 출동 기록 목록). 빨강은 지연과 긴급에만 쓰므로 `미분류`와 같은
   호박색 짝(`status-4`)입니다 - 대비 검사가 이미 보는 짝입니다. */
.tag-pending {
  background: var(--color-status-4-bg);
  color: var(--color-status-4-fg);
}

/* 숫자와 날짜는 자릿수 폭을 맞춥니다. 표 칸이면 오른쪽에 붙입니다 — 줄 안의 글자(`span`)는
   정렬을 바꾸지 않습니다. */
.num {
  font-variant-numeric: tabular-nums;
}

td.num,
th.num {
  text-align: right;
}

/* --- 공통 부품 ------------------------------------------------------------ */

/* 표를 담는 카드입니다. 카드의 안 여백을 표 칸이 대신 집니다 — 첫 칸 글자가 카드 제목과
   입력칸의 왼쪽 선(24px)에 맞춰 섭니다. */
.table-card {
  padding: 0;
}

.table-card .ledger tr > :first-child {
  padding-left: 24px;
}

.table-card .ledger tr > :last-child {
  padding-right: 24px;
}

/* 긴 폼의 아래에 붙어 따라오는 저장 막대입니다. 주 단추는 왼쪽, 되돌릴 수 없는 단추는
   `.spacer` 너머 오른쪽 끝에 둡니다. */
.actionbar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 64px;
  padding: 12px 24px;
  background: var(--color-card);
  border-top: 1px solid var(--color-border-soft);
  box-shadow: 0 -4px 16px rgb(0 0 0 / 0.06);
}

.actionbar .spacer {
  flex: 1 1 auto;
}

/* 폼 맨 위의 오류 요약입니다. 줄마다 그 칸으로 가는 링크가 붙습니다. */
.error-summary {
  margin-bottom: 16px;
  padding: 16px 24px;
  border: 2px solid var(--color-error);
  border-radius: 12px;
  background: var(--color-card);
}

.error-summary h2 {
  margin: 0 0 8px;
  font-size: 16px;
  color: var(--color-error);
}

.error-summary ul {
  margin: 0;
  padding-left: 24px;
}

.error-summary a {
  font-weight: 600;
  color: var(--color-error);
}

/* --- 상세 ---------------------------------------------------------------- */

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}

.breadcrumb .page-title {
  margin: 0;
}

.crumb-sep {
  color: var(--color-text-muted);
}

.info {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 32px;
  margin: 0;
}

.info-item {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: 12px;
  align-items: baseline;
}

.info-wide {
  grid-column: 1 / -1;
}

.info dt {
  font-size: 14px;
  color: var(--color-text-muted);
}

.info dd {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
}

.info .strong {
  font-weight: 700;
}

.multiline {
  white-space: pre-wrap;
}

/* 화면에서 뺀 항목(구분·민원인 유형·조치 시작일·투입인원·투입시간)을 값이 있을 때만
   덧붙이는 줄입니다. 본문보다 작게 두어 읽는 흐름을 끊지 않습니다. */
.side-note {
  margin: 16px 0 0;
  font-size: 13px;
  color: var(--color-text-muted);
}

.audit {
  margin: 24px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--color-border-soft);
  font-size: 13px;
  color: var(--color-text-muted);
}

/* 등록과 수정의 시각(`2026-10-16 14:13`)은 값 가운데서 꺾지 않습니다. 줄은 사이에서만 바뀝니다. */
.audit .num {
  white-space: nowrap;
}

/* --- 등록·수정 폼 --------------------------------------------------------- */

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

.grid .field-wide {
  grid-column: 1 / -1;
}

.with-quick {
  display: flex;
  align-items: center;
  gap: 8px;
}

.with-quick input {
  flex: 1 1 auto;
}

/* 날짜와 시간 칸 옆의 `오늘`, `지금`은 칸과 한 줄에 서므로 칸과 같은 40px입니다. */
.with-quick .button-small {
  min-height: var(--control-h);
}

.form-actions {
  display: flex;
  gap: 8px;
  margin-bottom: 24px;
}

.errors {
  border: 1px solid var(--color-error);
  border-radius: 10px;
  background: var(--color-card);
  color: var(--color-error);
  padding: 12px 24px;
  margin-bottom: 16px;
}

/* `.errors`와 모양은 닮았지만 오류가 아니라 알림입니다("아직 열어 보지 않은 접수가
   있습니다") — 색까지 오류로 칠하면 실제로 잘못된 게 없는데도 사람을 놀라게 합니다. */
.notice {
  border: 1px solid var(--color-border-soft);
  border-radius: 10px;
  background: var(--color-card);
  color: var(--color-text-muted);
  padding: 12px 24px;
  margin-bottom: 16px;
}

/* 화면에는 안 보이지만 스크린 리더는 읽습니다. 점 기호(`.awaiting-dot`)처럼 글자가 아닌
   표시에 뜻을 읽을 수 있는 말을 붙일 때 씁니다. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.errors-title {
  margin: 0 0 8px;
  font-weight: 700;
}

.errors ul {
  margin: 0;
  padding-left: 24px;
}

/* --- 대시보드 ------------------------------------------------------------- */
/* 레퍼런스 엑셀 `대시보드` 시트처럼 좌우 2단(.dashboard-grid)으로 배치합니다.
   1200px 미만에서는 한 열로 접힙니다. */

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

.page-head .page-title {
  margin: 0;
}

.as-of-form {
  display: flex;
  align-items: center;
  gap: 8px;
}

.dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: 12px;
}

.dashboard-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.dashboard-section {
  margin-bottom: 0;
  padding: 8px 16px;
}

.dashboard-section .card-head {
  margin-bottom: 4px;
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px 12px;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--color-border-soft);
  border-radius: 8px;
  min-width: 0;
}

.stat-label {
  font-size: 13px;
  color: var(--color-text-muted);
}

.stat-value {
  font-size: 24px;
  font-weight: 700;
}

.ledger.compact thead th {
  height: auto;
  padding: 4px 12px;
}

.ledger.compact td,
.ledger.compact th[scope="row"] {
  padding: 4px 12px;
  font-size: 13px;
  line-height: 1.2;
}

.muted-row td,
.muted-row th[scope="row"] {
  color: var(--color-text-muted);
}

.totals-row td,
.totals-row th[scope="row"] {
  font-weight: 700;
  border-top: 2px solid var(--color-border-soft);
}

.bar-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.bar-value {
  min-width: 24px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.bar-track {
  flex: 1 1 auto;
  max-width: 120px;
  height: 12px;
  background: var(--color-border-soft);
  border-radius: 4px;
  overflow: hidden;
}

.bar {
  height: 100%;
  border-radius: 4px;
}

.bar-received {
  background: var(--color-bar);
}

.bar-done {
  background: var(--color-bar-done);
}

@media (min-width: 1200px) {
  .dashboard-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* --- 좁은 화면 ------------------------------------------------------------ */

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

  .grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .info {
    grid-template-columns: minmax(0, 1fr);
  }

  .stat-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* --- 접수 정보 넷째 줄 ----------------------------------------------------- */

/* 접수일·접수시간·민원인·민원 경로는 값이 짧습니다. 세 칸짜리 격자에 그대로 두면
   빈 자리가 크게 남아, 한 줄에 나란히 놓고 값 길이에 맞춰 폭을 줄입니다. */

.field-line {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 16px;
}

.field-narrow,
.field-time,
.field-medium {
  min-width: 0;
}

.field-narrow input {
  width: 150px;
}

.field-time input {
  width: var(--control-time-w);
}

.field-medium input,
.field-medium select {
  width: 200px;
}

.field-narrow .with-quick input,
.field-time .with-quick input {
  flex: 0 0 auto;
}

/* --- Task 17: 사이드바 고정과 당김 효과 제거 -------------------------------- */

/* 본문(.content)만 스크롤되게 하고 사이드바는 화면에 그대로 둡니다. 맨 위·아래에서
   더 스크롤할 때 화면이 당겨졌다 돌아오는 움직임도 없앱니다(사용자 지적). 브라우저
   기본 스크롤 막대는 그대로 두고, 스크롤되는 자리만 본문 영역으로 좁힙니다. */

html,
body {
  height: 100%;
  overscroll-behavior: none;
}

body {
  overflow: hidden;
}

.layout {
  height: 100vh;
}

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overscroll-behavior: none;
}

.main {
  height: 100vh;
  overflow: hidden;
}

.topbar {
  flex-shrink: 0;
}

.content {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: none;
}

/* --- Task 17: 접수 목록 열 너비와 쪽 나누기 --------------------------------- */

/* 요청 제목만 너비를 지정하지 않아 남는 폭을 가져갑니다.

   표에 최소 너비를 두고 모자라면 `.ledger-scroll` 안에서만 옆으로 넘깁니다. 예전에는
   최소 너비가 없어서, 창이 고정 너비의 합보다 좁으면 너비를 지정하지 않은 요청 제목
   칸이 0으로 찌그러졌습니다. 그러면 머리글 글자가 옆 칸과 겹쳐 보였습니다.

   스크롤 상자는 절대 위치의 기준입니다(`position: relative`). 표 안의 낭독기용 글(`.visually-hidden`, 카드의
   `.cell-label`)이 기준을 상자 밖(`.content`)에서 찾으면 상자의 잘림을 벗어나, 375에서 매장 관리 쪽 전체가 옆으로
   88px 밀렸습니다(2026-10-17 QA). */

.ledger-scroll {
  position: relative;
  overflow-x: auto;
}

.ledger-list {
  table-layout: fixed;
  min-width: 1300px;
}

/* 열두 자리 접수번호 뒤에 붉은 점(신청)과 새 채팅 표시가 붙습니다. 120px이던 때는 점이
   다음 줄로 떨어졌습니다(사용자 지적). 한 줄에 두고 칸을 그만큼 넓힙니다. */
.ledger-list .col-receipt {
  width: 190px;
  white-space: nowrap;
}

.ledger-list .col-business {
  width: 90px;
}

.ledger-list .col-location {
  width: 130px;
}

.ledger-list .col-category {
  width: 130px;
  white-space: nowrap;
}

.ledger-list .col-requester {
  width: 80px;
}

.ledger-list .col-received {
  width: 140px;
  white-space: nowrap;
}

.ledger-list .col-assignee {
  width: 90px;
}

.ledger-list .col-status {
  width: 130px;
}

.ledger-list .col-edit {
  width: 70px;
}

.ledger-list .col-title {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.unit-division {
  display: block;
  font-size: 13px;
  color: var(--color-text-muted);
}

.list-range {
  margin: 0 0 12px;
  color: var(--color-text-muted);
  font-size: 13px;
}

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: 16px;
}

.page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 8px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  color: var(--color-text);
  text-decoration: none;
  font-weight: 600;
  font-size: 13px;
}

a.page-link:hover {
  background: var(--color-row-hover);
}

.page-link.is-current {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-text);
}

.page-link.is-disabled {
  color: var(--color-text-muted);
  border-color: var(--color-border-soft);
}

.page-ellipsis {
  padding: 0 4px;
  color: var(--color-text-muted);
}

/* --- Task 19: 전달 문구 -------------------------------------------------- */

.share-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.share-text {
  flex: 1 1 auto;
  min-width: 0;
}

.copy-feedback {
  align-self: center;
  font-size: 13px;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.copy-feedback.is-error {
  color: var(--color-error);
}

.share-fallback-box {
  display: block;
  margin-top: 8px;
  width: 100%;
}

.ledger-list .col-copy {
  width: 70px;
}

/* --- 완료 후 사진 --------------------------------------------------------- */

.photo-drop {
  display: block;
  padding: 16px;
  border: 1px dashed var(--color-border);
  border-radius: 6px;
  background: var(--color-bg);
  text-align: center;
  cursor: pointer;
}

.photo-drop.is-over {
  border-style: solid;
  background: var(--color-row-hover);
}

.photo-drop-hint {
  margin: 0;
  color: var(--color-text-muted);
  font-size: 13px;
}

.photo-status {
  margin: 8px 0 0;
  font-size: 13px;
  color: var(--color-text-muted);
}

.photo-status.is-error {
  color: var(--color-error);
  font-weight: 600;
}

.photo-file {
  display: block;
  margin-top: 4px;
  font-size: 13px;
}

/* 1080p 화면에서 한 줄에 넉 장이 들어갑니다. */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.photo-grid:empty {
  display: none;
}

.photo-thumb {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
}

.photo-thumb img {
  display: block;
  width: 100%;
  height: 140px;
  object-fit: cover;
  border: 1px solid var(--color-border-soft);
  border-radius: 6px;
  background: var(--color-card);
}

/* 좁은 화면에서는 넉 장이 한 줄에 들어가면 썸네일이 너무 가늘어집니다. */
@media (max-width: 1100px) {
  .photo-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* 사진 크게 보기입니다(매장 접수 처리 화면, 사장님 2026-09-29 — 매장 상세와 같은 모양,
   store.css의 `.store-lightbox`). `app.js`가 만드는 `<dialog>`이고, 화면 전체를 어두운
   판으로 덮고 사진을 화면에 맞춰 줄입니다. 바탕 규칙에는 `display`를 적지 않습니다 —
   적으면 닫힌 창을 감추는 브라우저 규칙을 덮어 늘 보입니다. */
.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  padding: calc(56px + env(safe-area-inset-top, 0px)) 12px
    calc(24px + env(safe-area-inset-bottom, 0px));
  border: none;
  background: transparent;
  color: var(--color-primary-text);
  overflow: hidden;
}

.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox::backdrop {
  background: rgba(0, 0, 0, 0.88);
}

.lightbox-image {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 4px;
}

.lightbox-count {
  position: absolute;
  top: calc(18px + env(safe-area-inset-top, 0px));
  left: calc(16px + env(safe-area-inset-left, 0px));
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.lightbox-close {
  position: absolute;
  top: calc(6px + env(safe-area-inset-top, 0px));
  right: calc(6px + env(safe-area-inset-right, 0px));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: var(--color-primary-text);
  font: inherit;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}

/* --- Task 22: 목록에서 조치결과 바로 바꾸기 -------------------------------- */

/* `hidden`은 표시 규칙보다 약해서 `.button`의 display에 덮입니다. 자바스크립트가
   `변경` 버튼을 숨겨도 보이던 이유가 이것이라 여기서 확실히 눌러 둡니다. */
[hidden] {
  display: none !important;
}

/* 이 표에서는 조치결과를 목록에서 바로 바꿉니다 — 칸 안에 고르는 칸과 `변경` 단추가
   함께 들어가므로, 글자만 담는 매장 접수 목록보다 넓어야 합니다. 예전에는 두 표가
   같은 130px을 쓰면서 그 선언이 이 파일 두 곳에 따로 있었고(위 열 너비 블록의 100px과
   여기 130px), 뒤엣것이 이겨 앞엣것은 아무 일도 하지 않았습니다.

   재어 본 값입니다(2026-09-29, Pretendard 14px 표 글자, 고르는 칸 좌우 여백 8px). 고르는
   칸이 가장 긴 상태(`추가확인 필요`)를 화살표까지 자르지 않고 담으려면 114.5px이 듭니다 —
   고르는 칸의 너비는 고른 값이 아니라 **가장 긴 보기**가 정합니다. 4px 넘게 남도록 바닥을
   120px로 두고, `변경` 단추 48.5px(13px 작은 단추), 사이 4px, 칸의 좌우 여백 24px을 더해
   196.5px이라 칸은 200px입니다. 13px이던 고르는 칸이 표 글자 14px를 따르면서 190px 칸에서는
   1px 모자랐습니다(검토자가 잼). 130px이던 때는 고르는 칸이 56px로 눌려 `조:`처럼 첫 글자
   하나만 보였습니다(사용자 지적: "제목이 너무 길어 조치결과가 너무 작게 표시되어서 실제
   조치결과가 한눈에 보이지 않아").

   ★ 넓힌 만큼 표의 최소 너비도 함께 올립니다(130px에서 70px 넓혀 1300 + 70). 안 올리면
     그 몫을 너비를 정하지 않은 요청 제목 칸이 혼자 내놓아, 한쪽을 고치면서 다른 쪽을 부수는 꼴이 됩니다. 제목은
     원래대로 250px을 지키고, 모자라면 말줄임표로 줄입니다 — 전체 글은 `title` 속성과
     상세 화면에 있습니다. */
.ledger-list.ledger-status-edit {
  min-width: 1370px;
}

.ledger-list.ledger-status-edit .col-status {
  width: 200px;
}

.status-form {
  display: flex;
  align-items: center;
  gap: 4px;
}

.status-select {
  /* 줄어들 수는 있어도 글자가 사라질 만큼은 아닙니다 — 이 칸의 쓸모가 곧 "지금 어떤
     상태인지 한눈에 보이는 것"입니다. 가장 긴 보기와 화살표가 들어갈 만큼(재어 본
     114.5px)에 4px 넘게 남겨 120px을 바닥으로 둡니다. `min-width: 0`이던 때는 옆 단추에
     밀려 56px까지 눌렸습니다. 글자는 표 글자 14px입니다(`.ledger select`와 같은 값 —
     여기 다른 값을 적으면 더 구체적인 그 규칙에 조용히 집니다). */
  min-width: 120px;
  flex: 1 1 auto;
  font-size: 14px;
}


/* --- 접수 건 삭제 ---------------------------------------------------------- */

/* 되돌릴 수 없는 자리라 다른 버튼과 색으로 구분하고, 저장·취소에서 떨어뜨려 둡니다.
   손이 미끄러져 누르는 일을 줄이려는 배치입니다. */
.button-danger {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: var(--color-danger-text);
}

.form-actions-end {
  margin-left: auto;
}

.delete-warning {
  margin: 0 0 16px;
  padding: 12px 16px;
  border: 1px solid var(--color-error);
  border-radius: 10px;
  color: var(--color-error);
}

.delete-note {
  margin: 16px 0 0;
  color: var(--color-text-muted);
  line-height: 1.6;
}

.delete-form {
  display: flex;
  gap: 8px;
  margin-top: 24px;
}

/* 통계 이름표에 딸리는 기간입니다. "이번 주"가 언제인지는 이 숫자가 무엇인지의
   일부라, 화면을 설명하는 문단이 아니라 이름표 안에 둡니다. */
.stat-period {
  display: block;
  font-weight: 400;
  font-size: 13px;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* 기간은 두 날짜 사이(`~` 곁)에서만 꺾입니다. 1280에서 `2026-10-` / `18`로 날짜 가운데서 꺾였습니다(2026-10-16
   측정). 기간 전체를 묶으면 폰(375)의 두 칸 타일 밖으로 나갑니다. */
.stat-period .num {
  white-space: nowrap;
}
/* --- 매장 접수 처리 화면 ------------------------------------------------------
   시설팀이 매장 접수건을 보고 처리하는 화면(`/admin/store/…`)이 씁니다. 오피스
   접수대장과 칸이 달라 표 클래스를 따로 둡니다. */

.section-title {
  margin: 0 0 12px;
  font-size: 20px;
  line-height: 1.4;
  font-weight: 700;
  color: var(--color-text);
}

/* 이름표와 값을 두 칸으로 놓습니다. 한 줄에 하나씩 쌓으면 열두 칸짜리 화면이
   끝없이 길어집니다. */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 0 24px;
  margin: 0;
}

.detail-row {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 12px;
  align-items: baseline;
  padding: 8px 0;
  border-bottom: 1px solid var(--color-border-soft);
}

.detail-row dt {
  color: var(--color-text-muted);
  font-size: 14px;
}

.detail-row dd {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  /* 격자 칸의 기본 최소 너비는 `auto`(내용의 min-content)라, 값 칸에 줄바꿈이 안 되는
     것이 들어가면 `1fr` 트랙이 그만큼 벌어지고 화면이 가로로 밀립니다(1.4.10).
     줄바꿈은 `body`의 `overflow-wrap: anywhere`가 맡으므로 트랙은 줄어들 수 있게 열어
     둡니다. 한글은 `body`의 `keep-all`대로 어절에서 끊습니다. */
  min-width: 0;
}

/* 한 줄이 격자의 두 칸을 다 씁니다. 첨부 사진 썸네일처럼 절반 너비로는 눌려서
   못 알아보는 내용을 담는 줄이 씁니다(`.info-wide`와 같은 일을 합니다). */
.detail-row-wide {
  grid-column: 1 / -1;
}

/* 매장이 적은 본문은 줄바꿈을 그대로 살립니다 — 층·위치·증상을 줄 나눠 적습니다. */
.detail-detail {
  font-size: 16px;
  line-height: 1.6;
  white-space: pre-wrap;
}

/* 증상(또는 M-F.M에서 옮긴 건의 내용)이 서는 자리입니다. `detail-grid`의 두 칸짜리
   이름표·값 줄이 아니라 카드 너비를 그대로 쓰는 칸입니다 — 여러 줄인 본문을 좁은
   칸에 욱여넣지 않습니다. */
.detail-long {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--color-border-soft);
  font-size: 16px;
  line-height: 1.6;
}

.detail-long h3 {
  margin: 0 0 8px;
  font-size: 16px;
}

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

.timeline li {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 8px 0;
  border-bottom: 1px solid var(--color-border-soft);
}

.timeline li:last-child {
  border-bottom: none;
}

.timeline-time {
  flex: 0 0 auto;
  color: var(--color-text-muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.timeline-status {
  font-weight: 700;
}

/* 처리자는 줄지도 꺾이지도 않습니다. 글이 긴 줄(신청일 고치기, 재분류)에서 폰 폭(375px)이면 이름이 9px 폭으로
   줄어 한 글자씩 꺾였습니다(2026-10-14 QA). 남는 폭은 가운데 글이 꺾여 씁니다. */
.timeline-actor {
  flex: none;
  color: var(--color-text-muted);
  font-size: 13px;
  white-space: nowrap;
}

/* 긴급은 목록에서 한눈에 띄어야 합니다. */
.urgency-urgent {
  color: var(--color-error);
  font-weight: 700;
}

/* 저장된 값을 읽을 수 없을 때 그 자리에 대신 적는 글입니다. 빈칸으로 두면 "아직 안
   적었다"와 구별되지 않아, 고쳐야 할 줄인지 알 수 없습니다. */
.detail-broken {
  color: var(--color-error);
}

/* 요청유형을 다시 판정하는 자리입니다. 평소에는 접힌 단추 하나로만 있다가 눌러야
   폼이 열립니다 — 스크립트 없이 `popover` 속성이 합니다(CSP가 `script-src 'self'`라
   인라인 스크립트로 여닫지 않습니다). 단추 자신이 `<dd>` 안에서 값 옆에 붙으므로
   감싸던 `.reclass`는 없고, 옆 여백은 단추 자신이 집니다. */
.reclass-toggle {
  display: inline-block;
  margin-left: 12px;
  padding: 4px 12px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-card);
  color: var(--color-text);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.reclass-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px;
}

/* 본문 위에 뜨는 창입니다(재분류·신청일 고치기·공지 수정이 함께 씁니다). `popover`
   속성이 붙은 요소는 열리면 최상위 레이어로 옮겨져 카드 높이에 영향을 주지 않습니다
   — `<details>` 시절에는 폼이 펼쳐지며 "매장이 낸 내용" 카드가 늘어났습니다. */
.popup-panel {
  width: min(440px, calc(100vw - 32px));
  padding: 24px;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  background: var(--color-card);
  color: var(--color-text);
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.25);
}

.popup-panel::backdrop {
  background: rgb(0 0 0 / 0.35);
}

.popup-title {
  margin: 0 0 12px;
  font-size: 20px;
  line-height: 1.4;
}

.popup-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
}

/* 아직 신청 상태(접수 전)인 건입니다. 색만으로 알리지 않고 점 기호를 두며, 낭독기에는
   글자로 읽힙니다(`visually-hidden`). */
.awaiting-dot {
  margin-left: 4px;
  color: var(--color-error);
  font-size: 13px;
  vertical-align: middle;
}

tr.is-unseen .col-receipt {
  font-weight: 700;
}

/* 매장이 쓴 채팅 가운데 아직 안 연 것의 수입니다. 매장 화면과 같은 모양입니다 —
   말풍선 그림(매장과 같은 Lucide 선 그림)과 붉은 점 안의 흰 숫자(검토자의 말 그대로).
   숫자가 글자로 있고 `aria-label`이 낱말과 함께 읽으므로 색만으로 전하지 않습니다(1.4.1). */
.chat-unread {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 4px;
  font-size: 14px;
  font-weight: 400;
  line-height: 1;
  vertical-align: middle;
}

.chat-unread-icon {
  display: inline-flex;
}

.chat-unread-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--color-error);
  color: var(--color-primary-text);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* 새 댓글은 없지만 댓글이 있는 건입니다(사장님 2026-10-16: "없을때도 검은색으로 표시되게 해줘, 이는 매장도
   동일해"). 같은 그림과 모양이고, 숫자는 검은 바탕(단추와 같은 `--color-primary`)의 흰 글자로 이 건의 댓글
   수(지운 것 빼고 자동응답 넣음, 판정 22)입니다. 붉은색은 새 댓글에만 남습니다. 낭독기에는 `댓글 3건`입니다. */
.chat-total {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 4px;
  font-size: 14px;
  font-weight: 400;
  line-height: 1;
  vertical-align: middle;
}

.chat-total-icon {
  display: inline-flex;
}

.chat-total-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--color-primary);
  color: var(--color-primary-text);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* 오래된 것이 위, 새 것이 아래입니다. 시설팀 쪽은 오른쪽, 매장 쪽은 왼쪽에 서고, 줄마다
   쓴 쪽의 이름이 적혀 있어 자리와 바탕만으로 가르지 않습니다. */
.chat {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
}

.chat-item {
  align-self: flex-start;
  max-width: 85%;
  padding: 8px 12px;
  border: 1px solid var(--color-border-soft);
  border-radius: 10px;
  background: var(--color-bg);
}

.chat-item.is-mine {
  align-self: flex-end;
  background: var(--color-card);
}

.chat-meta {
  margin: 0 0 4px;
  font-size: 13px;
  color: var(--color-text-muted);
}

.chat-author {
  font-weight: 700;
  color: var(--color-text);
}

/* 시각은 한 덩이로 줄을 바꿉니다. 폰의 댓글 창에서 `2026-09-` / `30 17:44`로 갈라지지 않게 합니다. */
.chat-time {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 매장이 줄 나눠 적은 것을 그대로 살리고, 끊을 자리가 없는 긴 글도 카드 밖으로 밀지
   않습니다. */
.chat-body {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.chat-error {
  margin: 0 0 8px;
  color: var(--color-error);
  font-weight: 700;
}

.chat-form {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

.chat-input {
  align-self: stretch;
  min-height: 72px;
}

/* 시설팀이 쓴 댓글의 `수정`·`삭제` 단추입니다(3차). 댓글 오른쪽 아래에 작게 둡니다 —
   말풍선보다 눈에 띄면 댓글을 읽는 화면이 단추를 읽는 화면이 됩니다. */
.chat-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

/* 고치기 창의 입력칸은 창 너비를 다 씁니다. `chat-input`의 `align-self`는 폼이 flex일
   때만 듣습니다. */
.popup-panel .chat-input {
  display: block;
  width: 100%;
  margin-top: 4px;
}

/* --- 처리 화면의 댓글 창과 여는 단추, 출동 이력과 처리 과정 한 줄 (사장님 2026-09-30) ---------------
   사장님: "헬프데스크 관리자 화면의 댓글도 VOM처럼 우하단의 아이콘 형태로 할 수 있나? 지금 댓글보려면
   너무 아래로 내려가야하네". 매장 화면의 단추와 창(`store.css`의 `.store-chat-fab`, `.store-chat-sheet`)과
   같은 몸짓을 시설팀 색과 부품으로 옮깁니다. 마크업은 `store_admin_detail.html`, 여닫기를 거드는 일은
   `app.js`의 "매장 접수 처리 화면의 댓글 창입니다" 블록입니다. */

/* 댓글 칸이 창으로 옮겨 간 자리에 출동 이력과 처리 과정이 나란히 섭니다. 둘 다 이 건에 "지금까지 무슨 일이
   있었나"를 적은 기록이라 한눈에 봅니다. 같은 폭 두 칸이고 위를 맞춥니다. */
.detail-b-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
  margin-bottom: 16px;
}

.detail-b-pair > .card {
  margin-bottom: 0;
}

/* 출동 이력 표(다섯 칸)는 좁혀도 약 392px이 듭니다(1280에서 잰 값). 반쪽 카드의 안쪽 폭은 1280에서 438px,
   1100에서 368px이라 1280 아래에서는 표가 카드 밖으로 밀립니다. 매장 접수 목록이 표를 카드로 쌓는 자리와
   같은 1279px 아래에서 한 칸으로 쌓고, 출동 이력이 위입니다. */
@media (max-width: 1279px) {
  .detail-b-pair {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* 오른쪽 아래 둥근 단추입니다. 누르면 `popovertarget`이 댓글 창을 엽니다. 아래 저장 막대(64px)에서 16px 위에
   떠서 `저장`도 `삭제`도 덮지 않습니다. 댓글이 있으면 그림 옆에 그 수를 적어 알약 모양으로 늘어납니다.
   폰에서도 56px이라 누르는 자리(44px)가 넉넉합니다.

   팝오버를 모르는 브라우저에서는 눌러도 열리지 않으므로 기본은 감추고 `@supports`가 켭니다. 그때 창은
   완료 사진 뒤에 평범한 칸으로 펼쳐집니다(아래 `.chat-panel`). */
.chat-fab {
  display: none;
  position: fixed;
  right: 24px;
  bottom: 80px;
  z-index: 6;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 56px;
  min-height: 56px;
  padding: 0 16px;
  border: none;
  border-radius: 999px;
  background: var(--color-primary);
  color: var(--color-primary-text);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.3);
  cursor: pointer;
}

@supports selector(:popover-open) {
  .chat-fab {
    display: inline-flex;
  }
}

/* 검은 단추 둘레의 초점 고리는 흰 틈을 두어 단추 색에 묻히지 않게 합니다. */
.chat-fab:focus-visible {
  outline-offset: 4px;
}

.chat-fab svg {
  flex: none;
}

.chat-fab-count {
  line-height: 1;
}

/* 매장이 쓴 댓글 가운데 시설팀이 아직 안 본 수입니다(목록 줄의 새 댓글 표시와 같은 부품). 붉은 점이 검은
   단추에 묻히지 않게 흰 테두리를 두릅니다. */
.chat-fab .chat-unread-count {
  position: absolute;
  top: -4px;
  right: -4px;
  box-shadow: 0 0 0 2px var(--color-card);
}

/* 단추가 있는 쪽은 아래를 48px 더 비웁니다. 끝까지 내리면 마지막 카드와 막대 사이가 24px뿐이라, 단추(막대
   위 16px에서 56px 높이)가 마지막 카드(완료 사진)의 오른쪽 아래를 덮습니다. */
.detail-b:has(> .chat-fab) {
  padding-bottom: 48px;
}

/* 댓글 창입니다. 팝오버를 모르는 브라우저에서는 이 규칙만 걸려 완료 사진 뒤의 평범한 카드가 됩니다.
   팝오버가 되는 브라우저에서는 닫혀 있는 동안 브라우저가 감춥니다(`display`를 여기 적으면 그 감춤을 덮어
   늘 보이므로 적지 않습니다). */
.chat-panel {
  margin: 0 0 16px;
  padding: 16px 24px;
  border: 1px solid var(--color-border-soft);
  border-radius: 10px;
  background: var(--color-card);
  color: var(--color-text);
  pointer-events: auto;
}

/* 열리면 오른쪽에 붙은 판입니다. 폰에서는 폭이 화면을 넘지 않아(`min(480px, 100%)`) 화면 전체를 덮습니다.
   머리(제목, 닫기)와 쓰는 폼은 제자리에 있고 가운데 목록만 스크롤합니다 - 댓글이 길어도 폼이 창 밖으로
   밀려나지 않습니다. */
.chat-panel:popover-open {
  display: flex;
  flex-direction: column;
  position: fixed;
  inset: 0 0 0 auto;
  width: min(480px, 100%);
  max-width: none;
  height: auto;
  max-height: none;
  margin: 0;
  padding: 0;
  border: none;
  border-left: 1px solid var(--color-border-soft);
  border-radius: 0;
  overflow: hidden;
  box-shadow: -8px 0 24px rgb(0 0 0 / 0.2);
}

.chat-panel::backdrop {
  background: rgb(0 0 0 / 0.35);
}

/* 창이 열리면 `app.js`가 초점을 창 자체로 옮깁니다(낭독기가 `댓글` 창을 읽도록). 창은 누르는 부품이 아니라
   판 전체에 초점 고리를 두르지 않습니다 - 다음 Tab이 닿는 `닫기`부터 고리가 보입니다. */
.chat-panel:focus-visible {
  outline: none;
}

/* 창이 열린 동안 뒤 화면은 눌리지 않습니다. 팝오버는 모달이 아니라서, 어두운 판을 눌러 닫는 몸짓이 그 아래
   단추까지 눌렀습니다 - 판 너머의 `저장`이나 `삭제`가 함께 눌립니다(매장 화면에서는 로그아웃이 눌렸습니다).
   창은 이 틀 안에 있어 값을 물려받으므로 창만 다시 눌리게 합니다(위 `.chat-panel`). 판을 누르면 그 누름이
   `body`에 닿아 창만 닫힙니다. */
.layout:has(.chat-panel:popover-open) {
  pointer-events: none;
}

.chat-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.chat-panel-head .section-title {
  margin: 0;
}

/* 닫기 단추는 창이 팝오버로 열렸을 때만 뜻이 있습니다. */
.chat-panel-close {
  display: none;
}

.chat-panel-scroll {
  margin-top: 12px;
}

.chat-panel-foot {
  margin-top: 12px;
}

.chat-panel:popover-open .chat-panel-head {
  flex: none;
  padding: 16px 24px;
  border-bottom: 1px solid var(--color-border-soft);
}

.chat-panel:popover-open .chat-panel-close {
  display: inline-flex;
}

.chat-panel:popover-open .chat-panel-scroll {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 16px 24px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.chat-panel:popover-open .chat-panel-foot {
  flex: none;
  margin: 0;
  padding: 12px 24px 16px;
  border-top: 1px solid var(--color-border-soft);
}

.chat-panel .chat {
  margin-bottom: 0;
}

.chat-panel .empty {
  padding: 32px 0;
}

.col-brand {
  width: 120px;
}

.col-store {
  width: 220px;
}

/* 관리자 분류가 없으면 매장 값 옆에 `미분류` 표가 붙습니다(3차). 80px에서는 표가 다음
   줄로 떨어져, 줄 높이가 건마다 들쭉날쭉해집니다. */
.col-urgency {
  width: 136px;
}

/* --- 매장·계정 관리 ------------------------------------------------------- */

.col-account {
  width: 160px;
}

.col-contact {
  width: 180px;
}

/* 사업부·전체 계정 표의 표시 이름 칸입니다. 연락처 칸과 같은 작은 폼이 들어갑니다. */
.col-display-name {
  width: 180px;
}

.col-count {
  width: 90px;
  text-align: right;
}

/* 마지막 로그인(`2026-10-02 15:41`)은 날짜와 시각 사이에서 꺾지 않습니다. 사업부, 전체 계정 표는 칸 폭을 내용에
   맞춰 나눠(`table-layout: auto`) 1280 이하에서 이 칸이 121px로 좁혀져 두 줄이 되었습니다(2026-10-17 QA). */
.col-login {
  width: 150px;
  white-space: nowrap;
}

.col-actions {
  width: 190px;
}

.col-password {
  width: 160px;
}

.col-reason {
  width: 240px;
}

/* 매장 표에서 눌러 줄을 세우는 머리글(사업부·브랜드·매장명)입니다. 머리글 칸의 안쪽
   여백(`.ledger thead th`의 10px 12px)만큼 밖으로 넓혀 칸 전체를 누를 자리로 삼고, 높이를
   44px로 잡습니다 — 글자만 누르게 두면 13px 한 줄(약 20px)이 과녁입니다. 링크색이라 누를 수
   있는 머리글과 아닌 머리글이 눈으로 갈립니다(`tests/test_contrast.py`의 link·table-head 짝). */
.sort-link {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
  margin: -8px -12px;
  padding: 0 12px;
  color: var(--color-link);
  text-decoration: none;
}

.sort-link:hover {
  text-decoration: underline;
}

/* 링크가 머리글 칸을 꽉 채우므로 온 화면 공통의 바깥쪽 초점 테두리(`outline-offset: 2px`)는
   칸 밖으로 나가고, 맨 왼쪽 `사업부` 칸에서는 표를 감싼 `.ledger-scroll`이 그 테두리를
   잘라 냅니다. 칸 안쪽에 그립니다. */
.sort-link:focus-visible {
  outline-offset: -2px;
}

/* 한 매장에 계정이 둘 이상일 수 있어 한 칸에 여러 줄이 들어갑니다. */
.account-name {
  display: block;
}

.account-none {
  color: var(--color-text-muted);
}

/* 중지·비밀번호 변경 대기(~기한 날짜)·잠김 표시입니다. 색만으로 알리지 않고 글자를 함께
   둡니다. */
.account-off {
  display: inline-flex;
  align-items: center;
  height: var(--badge-h);
  padding: 0 8px;
  border-radius: 10px;
  background: var(--color-status-0-bg);
  color: var(--color-status-0-fg);
  font-size: 13px;
  white-space: nowrap;
}

/* 표 칸 안에 들어가는 작은 폼입니다. 버튼이 입력칸 아래로 떨어지지 않게 합니다. */
.contact-form {
  display: flex;
  gap: 4px;
  align-items: center;
}

.contact-form input {
  width: 120px;
}

.account-form {
  display: inline-block;
  margin: 0 4px 4px 0;
}

/* 확인 상자와 그 이름표를 한 줄에 둡니다. */
.check-line {
  display: inline-flex;
  gap: 8px;
  align-items: center;
}

/* 초기 비밀번호를 보여 주는 상자입니다. 시설팀이 매장에 불러 줄 값이라 테두리를
   둘러 눈에 먼저 들어오게 합니다. */
.issued-card {
  border: 2px solid var(--color-focus);
}

/* 시설팀이 읽어 주고 매장이 받아 적는 값입니다. 글자를 키우고 자간을 벌려
   l과 1, O와 0을 눈으로 가를 수 있게 합니다(만들 때도 그 글자들은 빼 둡니다). */
.issued-password {
  padding: 4px 8px;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-table-head);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 16px;
  letter-spacing: 1px;
}

/* --- 매장 공지 ------------------------------------------------------------ */

/* 제목과 내용을 한 줄에 하나씩 쌓습니다. 내용 칸이 여러 줄이라 옆으로 나란히 두면
   제목 칸만 덩그러니 위에 떠 있습니다. */
.notice-form-fields {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}

/* 등록일은 꺾지 않습니다. 1000px 아래에서 `2026-10-14`가 두세 줄로 꺾였습니다(2026-10-16 측정). 표가 넓으면
   `.ledger-scroll` 안에서 옆으로 넘깁니다. */
.col-posted {
  width: 110px;
  white-space: nowrap;
}

.col-notice {
  min-width: 280px;
}

.col-author {
  width: 120px;
}

.notice-row-title {
  display: block;
  font-weight: 700;
}

/* 매장이 폰에서 보는 줄바꿈 그대로 보입니다. */
.notice-row-body {
  display: block;
  margin-top: 4px;
  font-weight: 400;
  color: var(--color-text-muted);
  white-space: pre-wrap;
}

/* 공지에 붙인 사진의 장수입니다(결정 기록 0059). 사진은 수정 창에서 봅니다. */
.notice-row-photos {
  display: block;
  margin-top: 4px;
  font-weight: 400;
}

/* 수정 창 안의 붙인 사진입니다. 한 장마다 작은 그림과 그 아래 `삭제` 단추가 섭니다. */
.notice-photo-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.notice-photo-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.notice-photo-thumb {
  display: block;
  width: 72px;
  height: 72px;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: 8px;
}

/* --- 첫 화면(통합 대시보드)과 사이드바 묶음 ------------------------------------ */

.nav-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 12px;
}

.nav-group-title {
  padding: 0 12px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--color-sidebar-muted);
}

.home-alert {
  margin-bottom: 12px;
  padding: 0;
}

.home-alert-link {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 16px 24px;
  color: var(--color-text);
  text-decoration: none;
}

.home-alert-value {
  font-size: 32px;
  font-weight: 700;
  line-height: 1;
}

/* 0이 아닌 숫자만 붉습니다. 안 본 접수와 새 댓글이 한 칸에 있어, 칸 전체를 붉히면
   0인 쪽까지 붉어집니다. */
.home-alert-value.is-waiting {
  color: var(--color-danger);
}

.home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  align-items: start;
  gap: 12px;
}

.home-side {
  margin-bottom: 0;
  min-width: 0;
}

.home-side .stat-row {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.home-side-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

/* 자동 갱신을 끄는 자리입니다. 숫자보다 위에 두면 먼저 읽히므로, 오른쪽 끝에 붙여
   "이 화면에 딸린 설정"으로 보이게 합니다. */
.refresh-toggle {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-bottom: 8px;
  color: var(--color-text-muted);
  font-size: 13px;
  cursor: pointer;
}

/* 오피스 방재실이 맡는 매장입니다. 시설팀이 목록에서 바로 가려낼 수 있게 이름 옆에
   답니다 — 접수를 열어 보고서야 알면 그만큼 늦게 넘어갑니다.

   한때 `var(--color-accent-soft, var(--color-bg))`였습니다. `--color-accent-soft`는
   어디에도 정의된 적이 없어 늘 예비값(`--color-bg`)으로 떨어졌고, 표는 페이지 바탕과
   같은 색에 흐린 회색 11px 글씨로 그려져 사실상 보이지 않았습니다 — 칩이라는 것도
   눈에 띄지 않았습니다. 정의된 짝을 쓰고, 한눈에 읽어야 하는 이름표라 글자도 키웁니다. */
.tag-safety {
  display: inline-flex;
  align-items: center;
  height: var(--badge-h);
  margin-left: 8px;
  padding: 0 8px;
  border-radius: 4px;
  background: var(--color-tag-safety-bg);
  color: var(--color-tag-safety-fg);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}

/* 관리자 분류가 아직 없는 건입니다(3차). 분류하지 않은 건은 신청 단계를 넘길 수 없어 목록과
   처리 화면에서 눈에 띄어야 합니다. 색만으로 알리지 않고 글자(`미분류`)로 씁니다. 색 짝은
   상태 배지의 호박색(`status-4`)을 그대로 씁니다 — 대비 검사가 이미 보는 짝입니다. */
.tag-unclassified {
  display: inline-flex;
  align-items: center;
  height: var(--badge-h);
  margin-left: 8px;
  padding: 0 8px;
  border-radius: 4px;
  background: var(--color-status-4-bg);
  color: var(--color-status-4-fg);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}

/* --- 매장 접수 목록 B안 (헬프데스크 1차, 설계 문서 3절) --------------------------- */

/* 지연 탭입니다. 상태 탭 끝에 가는 선을 두고 섭니다. 빨강은 지연과 긴급에만 씁니다. 색만으로
   알리지 않습니다 - 글자 `지연`이 있고 앞에 점이 붙습니다. */
.tabs-sep {
  align-self: center;
  width: 1px;
  height: 24px;
  background: var(--color-border-soft);
}

.tab-late {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border-color: var(--color-status-3-fg);
  color: var(--color-status-3-fg);
}

.tab-late::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.tab.tab-late.is-active {
  background: var(--color-status-3-fg);
  border-color: var(--color-status-3-fg);
  color: var(--color-primary-text);
}

/* 조건 막대입니다. 첫 줄(늘 보이는 네 칸과 검색), `조건 더 보기`, 칩과 건수 줄을 격자에 둡니다.
   접혀 있으면 `조건 더 보기`와 칩, 건수 줄이 한 줄을 나눠 쓰고, 펼치면 펼친 칸이 한 줄을 다
   차지하고 칩과 건수 줄은 그 아래로 내려갑니다. */
.filterbar {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 12px 16px;
}

/* 첫 줄은 모자라면 다음 줄로 내립니다. 1280(윈도의 스크롤 막대 17px까지)에서는 한 줄에 들고,
   그보다 좁은 창에서는 칸이 화면 밖으로 밀리지 않고 아래로 내려갑니다. */
.fb-row {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.fb-row .button {
  flex: none;
}

.fb-search {
  position: relative;
  flex: 1 1 160px;
  min-width: 160px;
}

.fb-search input {
  width: 100%;
  padding-left: 32px;
  appearance: none;
}

.fb-search-icon {
  position: absolute;
  top: 50%;
  left: 12px;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  pointer-events: none;
}

.fb-period {
  display: flex;
  flex: none;
  align-items: center;
  gap: 8px;
}

.fb-label {
  font-weight: 600;
  white-space: nowrap;
}

.fb-period input {
  width: 152px;
}

.fb-store,
.fb-division {
  flex: none;
}

.fb-store select {
  width: 208px;
}

.fb-division select {
  width: 136px;
}

/* 1280에서는 칸을 조금씩 줄여 검색어 칸에 자리를 남깁니다. 신청일의 날짜 칸은 줄이지 않습니다 - 136px에서
   `2026. 10. 08.`이 잘렸습니다(날짜 칸은 적어도 149.3px, 2026-10-16 측정). */
@media (max-width: 1439px) {
  .fb-store select {
    width: 168px;
  }

  .fb-division select {
    width: 128px;
  }
}

.more-filters[open] {
  grid-column: 1 / -1;
}

.more-filters[open] + .fb-foot {
  grid-column: 1 / -1;
}

.more-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: var(--control-h-sm);
  padding: 0 12px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-card);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  list-style: none;
  cursor: pointer;
}

.more-toggle::-webkit-details-marker {
  display: none;
}

.more-filters[open] .more-toggle svg {
  transform: rotate(180deg);
}

.fb-panel {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(144px, 1fr));
  gap: 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--color-border-soft);
}

.fb-panel .field {
  min-width: 0;
}

.fb-panel select,
.fb-panel input {
  width: 100%;
}

.fb-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  min-width: 0;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 걸린 숨은 조건 하나입니다. 누르면 그 조건만 빠진 목록으로 갑니다. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: var(--control-h-sm);
  padding: 0 8px 0 12px;
  border-radius: 999px;
  background: var(--color-status-1-bg);
  color: var(--color-status-1-fg);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.chip:hover {
  text-decoration: underline;
}

.fb-result {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-left: auto;
}

.fb-count {
  margin: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 모든 탭의 `접수시간순 | 최근 수정일순`이고, 끝난 건의 탭(완료, 공사완료)은 끝에 `완료일순`이 더 섭니다(설계
   2026-10-16 시스템 로그, 그 전에는 끝난 건의 탭에만 섰습니다).
   넘치는 것을 자르지 않습니다(`overflow`) - 자르면 링크의 초점
   테두리까지 잘립니다. 모서리는 양 끝 링크가 둥글게 합니다. */
.sort-switch {
  display: inline-flex;
  border: 1px solid var(--color-border);
  border-radius: 6px;
}

.sort-switch a {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 12px;
  color: var(--color-text);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.sort-switch a + a {
  border-left: 1px solid var(--color-border);
}

.sort-switch a:first-child {
  border-radius: 5px 0 0 5px;
}

.sort-switch a:last-child {
  border-radius: 0 5px 5px 0;
}

.sort-switch a:hover {
  background: var(--color-row-hover);
}

.sort-switch a[aria-current="true"] {
  background: var(--color-primary);
  color: var(--color-primary-text);
}

/* 머리글은 본문 상자(`.content`, 세로 스크롤을 맡은 상자) 안에서 위에 붙어 있습니다. 그래서 표를
   가로 스크롤 상자로 감싸지 않고 `.table-card` 바로 안에 둡니다. 스크롤 상자가 끼면 머리글은 그
   상자에 붙어 함께 올라가 버립니다. 붙는 자리는 `.content`의 안 여백(24px) 안쪽이라, 그만큼
   끌어올려 위 막대 바로 밑에 붙입니다. 0이면 머리글 위 24px 띠로 지나가는 줄이 비쳐 보입니다. */
.table-card thead th {
  position: sticky;
  top: -24px;
  z-index: 2;
  border-bottom: 1px solid var(--color-border-soft);
}

.table-card thead th:first-child {
  border-top-left-radius: 9px;
}

.table-card thead th:last-child {
  border-top-right-radius: 9px;
}

/* 두 줄 표입니다(64px, `--row-h-2line`). 칸마다 윗줄은 본 값, 아랫줄은 13px 곁글입니다. 표는 창
   폭에 맞추고(`min-width: 0`) 넘치는 글자는 줄임표로 줄여, 옆으로 밀리는 칸이 없습니다. 줄 선이
   머리글과 함께 붙어 있도록 칸마다 선을 긋습니다(`separate`). */
.ledger-list.ledger-2line {
  min-width: 0;
  border-collapse: separate;
  border-spacing: 0;
}

.ledger-2line td,
.ledger-2line th[scope="row"] {
  height: var(--row-h-2line);
  padding: 8px 12px;
  white-space: nowrap;
}

/* 칸 폭은 가장 긴 값에 맞춥니다: 열두 자리 번호와 붉은 점과 새 댓글 수(이 칸만 오른쪽 여백까지
   씁니다), 두 글자 상태와 `지연`, `긴급`과 `미분류`, 최근 수정의 날짜, 신청일과 `완료 MM-DD`. 매장명은 앞을 줄이므로
   좁혀도 지점 이름이 남습니다. 남는 폭은 모두 요청 제목이 씁니다. 이 합으로 카드 배치로 바뀌는 폭을 정합니다(아래
   `max-width: 1279px`, `tests/test_store_admin_list_b.py`가 셈을 봅니다). */
.ledger-2line .col-receipt {
  width: 192px;
  font-variant-numeric: tabular-nums;
}

.ledger-2line .col-status {
  width: 120px;
}

/* 매장이 고른 `긴급`(굵은 14px, 24.2px)과 `미분류` 표(49.7px)가 4px 사이를 두고 한 줄입니다(77.9px, headless
   Chrome 측정). 오른쪽 여백을 12px로 두면 안 폭이 76px라 표가 1.9px 넘쳤습니다(2026-10-16). 칸 폭은 그대로 두고
   오른쪽 여백만 최근 수정일 칸처럼 8px로 줄입니다 - 바로 옆 매장명 칸의 왼쪽 여백(12px)이 사이를 지킵니다. */
.ledger-2line .col-urgency {
  width: 100px;
  padding-right: 8px;
}

.ledger-2line .col-urgency .tag-unclassified {
  margin-left: 4px;
}

/* `선택한 N건 출동 기록`의 고르기입니다(결정 기록 0056). 첫 줄의 번호 뒤에 두고 왼쪽으로 띄워 번호 앞에
   섭니다 - 칸을 따로 두면 1280에서 요청 제목이 바닥 밑으로 줄어듭니다. 고를 수 없는 건은 같은 폭의
   빈자리(`.list-pick-gap`)라 번호가 한 줄로 맞습니다. 칸은 감싼 `label`(`.list-pick-hit`)이 띄우고,
   카드 배치에서는 그 label이 누를 자리를 넓힙니다(아래 `max-width: 1279px`). */
.list-pick-hit,
.list-pick-gap {
  float: left;
  width: 16px;
  height: 16px;
  margin: 4px 8px 0 0;
}

.list-pick-hit {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.list-pick {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

/* 댓글 표시(새 댓글 수는 붉게, 새 댓글이 없으면 전체 댓글 수를 검게)는 접수번호 칸의 둘째 줄, 번호 바로
   밑입니다(사장님 2026-10-16, 계획 7b). 첫 줄에 두면 번호(굵게 110px), 새 댓글 수(44~49px), 고르기(24px)가
   칸 안 폭(156px)을 15~27px 넘어 고르기가 둘째 줄로 밀렸습니다(headless Chrome 측정). 둘째 줄은 고르기
   폭(16px과 8px)만큼 들여 번호와 같은 세로줄에 섭니다. */
.receipt-chat {
  display: block;
  margin-top: 4px;
  padding-left: 24px;
}

.receipt-chat .chat-unread,
.receipt-chat .chat-total {
  margin-left: 0;
}

.ledger-2line .col-store {
  width: 184px;
}

/* 신청일과 마지막 출동입니다. 날짜는 오른쪽에 붙이고 자릿수 폭을 맞춥니다. */
.ledger-2line .col-date {
  width: 120px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* 최근 수정일입니다(설계 2026-10-16 시스템 로그). 윗줄은 날짜 `2026-10-15`, 아랫줄은 시각과 사용자 `10:00 VOM`
   입니다(사장님 2026-10-16: 100px 안팎). 신청일처럼 오른쪽에 붙이고 자릿수 폭을 맞춥니다. 폭은 윗줄 날짜(14px에서
   80.9px, headless Chrome 측정)에 왼쪽 여백 12px, 오른쪽 여백 8px, 3px 남짓을 더한 값입니다. 오른쪽 여백을
   12px보다 줄여 칸을 좁힙니다 - 바로 옆 신청일 칸도 오른쪽 맞춤이라 두 날짜 사이는 23px 안팎으로 다른 칸 사이와
   비슷합니다. 아랫줄은 시각이 앞이라 긴 매장 계정 아이디만 줄임표로 줄고 `title`이 다 알려 줍니다. */
.ledger-2line .col-change {
  width: 104px;
  padding-right: 8px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* 1440부터는 매장명 칸을 넓혀 오피스방재 표가 붙은 이름도 지점 이름까지 보입니다. */
@media (min-width: 1440px) {
  .ledger-2line .col-store {
    width: 264px;
  }
}

.ledger-2line .line1 {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ledger-2line .line2 {
  display: block;
  margin-top: 4px;
  overflow: hidden;
  font-size: 13px;
  font-weight: 400;
  color: var(--color-text-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `지연`은 둘째 줄의 현장소장 앞입니다(사장님 2026-10-16, 계획 7b). 첫 줄에서 `공사승인` 뱃지(71px) 곁에
   두면 칸 안 폭(96px)을 19.5px 넘었습니다. 지연인 줄은 표 높이(24px)만큼 3px 높아집니다(사장님이 받아들임). */
.status-manager .tag-late {
  margin-right: 4px;
  vertical-align: middle;
}

/* 현장소장(상태 칸 둘째 줄, 사장님 2026-09-30 "관리자 페이지 화면에서 현장소장이 보이게
   해줘")입니다. `.line2`와 같은 곁글 규칙(줄임표, 흐린 색)이지만 늘 읽는 값이라 표의 14px
   척도 그대로입니다. 표, 카드 배치 모두에서 보입니다 - `cell-label`처럼 표 배치에서 숨지
   않습니다(별 머리글이 없어 늘 읽혀야 합니다). */
.status-manager {
  display: block;
  margin-top: 4px;
  overflow: hidden;
  font-size: 14px;
  color: var(--color-text-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ledger-2line .col-store .line1 {
  display: flex;
  align-items: center;
}

.ledger-2line .col-store .tag-safety {
  flex: none;
}

/* 매장명은 앞을 줄입니다. `무신사 스탠다드 롯데백화점 센텀…`보다 `…롯데백화점 센텀시티점`이 어느
   매장인지 말해 줍니다. 글자 차례는 안쪽 칸이 지킵니다. */
.store-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  direction: rtl;
  text-align: left;
}

.store-name > span {
  direction: ltr;
  unicode-bidi: isolate;
}

/* 머리글이 감춰지는 카드 배치에서 칸의 이름이 되는 글자입니다(상태, 요청유형, 신청일). 표 배치에서는
   머리글이 이름을 말하므로 감춥니다 - 낭독기가 두 번 읽지 않습니다. */
.cell-label {
  display: none;
}

/* 표가 들어가지 않는 폭에서는 줄을 카드로 쌓습니다(폰 375도 여기에 듭니다). 고정 폭 칸의 합(820px)에
   사이드바 240px, 본문 여백, 윈도의 스크롤 막대 17px을 더하면 1280에서 요청 제목은 153px입니다. 2026-10-16에
   최근 수정일 칸(104px)이 들어오기 전에는 257px이었습니다(바닥 240px에 16px 여유). 그보다 좁으면 제목이 바닥 밑으로
   줄어듭니다(`tests/test_store_admin_list_b.py`의 `MIN_TITLE`). 예전에는 720px까지 표로 두어 721에서 905px 사이에 제목 칸이 0이었습니다.
   카드에서 머리글은 감추고, 칸은 격자 자리에 서며, 칸 이름은 `cell-label`이 말합니다. 브라우저가
   `display`를 바꾼 표의 표 의미를 버려도 줄마다 칸 이름과 값이 차례로 읽힙니다. */
@media (max-width: 1279px) {
  /* 조건 막대도 한 칸으로 쌓고, 초기화와 건수, 정렬, 두 단추가 한 흐름으로 줄을 바꿉니다. 오른쪽
     묶음(`.fb-result`)이 통째로 내려가며 제 안에서 또 줄을 바꾸면 초기화만 한 줄에 남습니다. */
  .filterbar {
    grid-template-columns: minmax(0, 1fr);
  }

  .fb-result {
    display: contents;
  }

  .ledger-2line,
  .ledger-2line tbody {
    display: block;
  }

  .ledger-2line thead {
    display: none;
  }

  /* 카드는 손가락으로 누르므로 고르기의 누를 자리를 48px로 넓힙니다(QA 2026-10-01 lane C SF-1). 보이는
     칸(16px)과 그 자리는 그대로이고, 넓힌 만큼 위와 왼쪽을 음수 여백으로 당깁니다 - 칸은 가운데(16px
     안쪽)에 섭니다. 오른쪽은 당기지 않습니다: 당기면 그만큼 접수번호 링크가 누를 자리를 덮습니다. 아래로
     넓힌 8px은 다음 줄(요청 제목) 위에 걸치므로 그 위에 올려 둡니다. */
  .list-pick-hit {
    position: relative;
    z-index: 1;
    width: 48px;
    height: 48px;
    margin: -12px 0 -16px -16px;
  }

  /* 고르기가 없는 줄의 빈자리도 같은 만큼 띄워 접수번호가 한 줄로 맞습니다. */
  .list-pick-gap {
    margin-right: 16px;
  }

  /* 카드의 첫 줄은 넉넉해(375에서 250px 안팎) 댓글 표시가 번호 곁에 그대로 섭니다. */
  .receipt-chat {
    display: inline;
    margin: 0;
    padding: 0;
  }

  .receipt-chat .chat-unread,
  .receipt-chat .chat-total {
    margin-left: 4px;
  }

  .ledger-2line tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas:
      "receipt receipt date"
      "title title title"
      "store status urgency"
      "change change change";
    gap: 8px 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .ledger-2line td,
  .ledger-2line th[scope="row"],
  .table-card .ledger-2line tr > :first-child,
  .table-card .ledger-2line tr > :last-child {
    display: block;
    width: auto;
    height: auto;
    padding: 0;
    border: none;
  }

  /* 칸마다의 넓은 화면 폭(`.ledger-2line .col-…`)이 격자 칸을 붙잡지 않게 여기서 풉니다. */
  .ledger-2line .col-receipt {
    grid-area: receipt;
    width: auto;
  }

  .ledger-2line .col-date {
    grid-area: date;
    width: auto;
  }

  /* 최근 수정일은 카드 맨 아래 한 줄입니다. 날짜(윗줄)와 시각, 사용자(아랫줄)를 나란히 두고 신청일처럼 오른쪽에
     붙입니다. 날짜와 시각은 줄지 않고, 긴 아이디만 줄임표로 줄입니다. 넓은 화면의 오른쪽 여백은
     `.ledger-2line td`의 여백 풀기보다 구체적이라 여기서 다시 풉니다. */
  .ledger-2line .col-change {
    display: flex;
    grid-area: change;
    justify-content: flex-end;
    align-items: baseline;
    gap: 8px;
    width: auto;
    min-width: 0;
    padding: 0;
  }

  .ledger-2line .col-change .line1 {
    flex: none;
  }

  .ledger-2line .col-change .line2 {
    min-width: 0;
    margin-top: 0;
  }

  .ledger-2line .col-title {
    grid-area: title;
    width: auto;
    white-space: normal;
  }

  .ledger-2line .col-title .line1 {
    white-space: normal;
  }

  .ledger-2line .col-store {
    grid-area: store;
    width: auto;
    min-width: 0;
  }

  .ledger-2line .col-status {
    grid-area: status;
    align-self: end;
    width: auto;
  }

  /* 넓은 화면의 오른쪽 여백(8px)은 `.ledger-2line td`의 여백 풀기보다 구체적이라 여기서 다시 풉니다. */
  .ledger-2line .col-urgency {
    grid-area: urgency;
    align-self: end;
    width: auto;
    padding: 0;
  }

  .ledger-2line .cell-label {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* === 출동 기록 (헬프데스크 1차 Task 6) =========================================
   출동 기록 쓰기(`store_admin_visit.html`), 목록(`store_admin_visits.html`), 고치기와
   지우기의 모양입니다. 사장님이 고른 2차 시안(`visit.html`, `visits.html`)을 따르고, 여백과
   글자는 공통 눈금만 씁니다. 다른 화면과 겹치지 않게 이름은 모두 `visit-`로 시작합니다.
   이 묶음은 `출동 기록 끝` 줄까지이고, 폰(720px 이하) 규칙은 아래 공통 좁은 화면 묶음 끝의
   `출동 기록` 자리에 있습니다. */

/* 폼은 넓은 화면에서도 쪽 폭(`--page-max`, 1200px)까지만 폅니다. `.content`의 폭은 묶지
   않습니다 — 스크롤 막대가 화면 가운데로 옵니다. 안의 `visually-hidden`(절대 위치)이 이
   상자 밖으로 문서를 늘리지 않게 가둡니다. */
.visit-wrap {
  position: relative;
  max-width: var(--page-max);
}

/* 저장 막대가 짧은 화면에서도 아래 끝에 붙게, 막대가 있는 화면에서는 본문이 남는 높이를
   채웁니다. */
.content:has(> .visit-actionbar) {
  display: flex;
  flex-direction: column;
}

.content:has(> .visit-actionbar) > .visit-wrap {
  flex: 1 0 auto;
  width: 100%;
}

.visit-back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: var(--control-h-sm);
  margin-bottom: 4px;
  color: var(--color-link);
  font-weight: 600;
  text-decoration: none;
}

.visit-back:hover {
  text-decoration: underline;
}

.visit-icon {
  flex: none;
}

.visit-wrap .page-head,
.visit-page-head {
  margin-bottom: 16px;
}

.visit-card-title {
  margin-bottom: 16px;
}

.visit-count {
  color: var(--color-text-muted);
  font-weight: 600;
}

.visit-muted {
  color: var(--color-text-muted);
  font-weight: 400;
}

.visit-spacer {
  flex: 1 1 auto;
}

.visit-late-text {
  color: var(--color-status-3-fg);
  font-weight: 600;
}

.visit-nothing-saved {
  margin: 0 0 8px;
  font-weight: 600;
}

.visit-empty-inline {
  margin: 0;
  color: var(--color-text-muted);
}

.visit-empty-card {
  padding: 0 24px 24px;
}

.visit-field-error {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  margin: 0;
  color: var(--color-error);
  font-weight: 600;
}

.visit-field-error .visit-icon {
  margin-top: 4px;
}

/* 고르는 칸이 걸리면 테두리를 오류 색으로 두껍게 긋습니다(`aria-invalid`). */
.visit-form [aria-invalid="true"],
#visit-edit [aria-invalid="true"] {
  border-color: var(--color-error);
  box-shadow: inset 0 0 0 1px var(--color-error);
}

/* --- 방문 카드 --- */

/* 넓은 화면은 방문일시, 현장소장, 메모 세 칸이 한 줄입니다. 방문일시 칸은 날짜와 시각이 `오늘`, `지금`과
   한 줄(433px, 시각 칸이 136px이던 2026-10-16 전에는 425px)이라 `max-content`입니다 - `auto`면 `.field`의 최소
   폭(160px)에서 출발해 남는 폭을 현장소장 칸과 나눠 가지므로, 1280에서 414px만 받아 `지금`이 칸 밖으로 11px
   나갔습니다. */
.visit-grid {
  display: grid;
  grid-template-columns: max-content minmax(180px, 240px) minmax(240px, 1fr);
  gap: 16px 24px;
  align-items: start;
}

.visit-field-store {
  grid-column: 1 / -1;
}

/* 출발 화면은 방문일시가 없어 매장 아래 현장소장과 메모 두 칸입니다(결정 기록 0056). */
.visit-grid-depart {
  grid-template-columns: minmax(180px, 240px) minmax(240px, 1fr);
}

/* 세 칸이 한 줄에 서려면 카드 안 폭이 433 + 24 + 180 + 24 + 240 = 901px이어야 합니다. 창 폭으로는 사이드바
   240px(1100px 이하는 200px), 본문 여백 24px 둘, 카드 여백 24px 둘, 테두리 1px 둘을 더해 1239px입니다. 그보다
   좁으면 방문일시가 시각과 `지금`을 현장소장 칸 밑에 숨겼습니다(보고 화면과 고치기 화면, 721~1200px). 이
   폭에서는 방문일시가 한 줄을 다 쓰고 현장소장과 메모가 다음 줄에 섭니다. 경계는 앱의 다른 넓은 화면 경계
   (1279px)와 맞춥니다 - 1280에서 카드 안 폭이 942px라 41px이 남습니다. 폰(720px 이하)은 아래 폰 묶음이 한
   칸으로 쌓습니다. */
@media (min-width: 721px) and (max-width: 1279px) {
  .visit-grid {
    grid-template-columns: minmax(180px, 240px) minmax(240px, 1fr);
  }

  .visit-field-when {
    grid-column: 1 / -1;
  }
}

.visit-field-when .with-quick input {
  flex: none;
}

.visit-field-when .with-quick input[type="date"] {
  width: 168px;
}

.visit-field-when .with-quick input[type="time"] {
  width: var(--control-time-w);
}

.visit-field-manager input {
  width: 100%;
}

.visit-field-memo textarea {
  width: 100%;
  min-height: 72px;
}

.visit-store {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
}

/* 매장 고르기의 사업부입니다(사장님 2026-09-30). 가장 긴 값(`사업부 전체`)이 다 보이는 폭입니다. */
.visit-store-division {
  flex: none;
  width: 160px;
}

.visit-combo {
  position: relative;
  width: min(100%, 480px);
}

.visit-combo > .visit-icon {
  position: absolute;
  top: 12px;
  left: 12px;
  color: var(--color-text-muted);
  pointer-events: none;
}

.visit-combo input {
  width: 100%;
  padding-left: 48px;
  padding-right: 48px;
}

.visit-combo-clear {
  position: absolute;
  top: 4px;
  right: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  color: var(--color-text-muted);
}

.visit-combo-clear:hover {
  background: var(--color-row-hover);
  color: var(--color-text);
}

.visit-store-facts,
.visit-bulk-count {
  white-space: nowrap;
}

.visit-store-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: min(100%, 480px);
  margin: 8px 0 0;
  padding: 8px;
  list-style: none;
  border: 1px solid var(--color-border-soft);
  border-radius: 8px;
  background: var(--color-card);
}

/* 찾은 매장 한 줄입니다. 스크립트 없는 찾기 결과에서는 폼을 보내는 단추(`pick_store`)이고,
   제안 목록에서는 `app.js`가 그리는 `role="option"` 줄입니다. */
.visit-store-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
  padding: 8px 12px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--color-text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.visit-store-option:hover,
.visit-store-option:focus-visible {
  background: var(--color-row-hover);
}

/* 제안 목록에서 화살표로 옮겨 간 줄입니다(`aria-activedescendant`). 초점은 입력칸에 남아 있어
   초점 테두리 대신 왼쪽 막대로 가리킵니다(시안 `visit-search.html`). */
.visit-store-option.is-active {
  background: var(--color-row-hover);
  box-shadow: inset 4px 0 0 var(--color-focus);
}

.visit-store-name {
  font-size: 16px;
  font-weight: 600;
}

.visit-store-meta {
  color: var(--color-text-muted);
  font-size: 13px;
}

.visit-field-store .visit-empty-inline {
  margin-top: 8px;
}

/* 이름을 치면 입력칸 바로 아래에 뜨는 제안 목록입니다(`app.js`). 입력칸 폭 안에 서므로 폰에서도
   옆으로 넘치지 않고, 길면 목록 안에서 스크롤합니다. */
.visit-combo-pop {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 10;
  max-height: 360px;
  overflow-y: auto;
  padding: 8px;
  border: 1px solid var(--color-border-soft);
  border-radius: 8px;
  background: var(--color-card);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.14);
}

.visit-combo-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.visit-combo-note {
  margin: 0;
  padding: 8px 12px;
  color: var(--color-text-muted);
}

.visit-combo-note:empty {
  display: none;
}

/* --- 접수건 카드 --- */

/* 접수 건 카드의 머리는 두 줄입니다(사장님 2026-10-01): 제목 `접수 건 N`, 그 아래 `모두 선택`과 `N건 선택`,
   오른쪽 끝 `완료 건 보기`. 머리의 왼쪽 여백이 표의 첫 칸 여백(`.visit-table tr > :first-child`)과 같아
   `모두 선택`의 칸이 줄마다의 고르기와 같은 세로줄에 섭니다(1280은 24px, 폰은 16px). */
.visit-pick-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 24px;
}

.visit-pick-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 24px;
}

.visit-check-inline {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--control-h-sm);
  font-weight: 600;
  cursor: pointer;
}

.visit-pick-count {
  color: var(--color-text-muted);
}

.visit-pick-count b {
  color: var(--color-text);
}

/* `완료 건 보기` 켜고 끄기입니다. 칸 자체는 누를 수 있게 투명하게 겹쳐 두고, 보이는 것은
   옆의 막대입니다. 초점 테두리도 막대에 긋습니다. */
.visit-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--control-h-sm);
  font-weight: 600;
  cursor: pointer;
}

.visit-switch input {
  position: absolute;
  top: 50%;
  left: 0;
  width: 36px;
  height: 20px;
  margin: -12px 0 0;
  opacity: 0;
  cursor: pointer;
}

.visit-switch-track {
  position: relative;
  flex: none;
  width: 36px;
  height: 20px;
  border: 2px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-card);
}

.visit-switch-track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--color-border);
}

.visit-switch input:checked + .visit-switch-track {
  border-color: var(--color-primary);
  background: var(--color-primary);
}

.visit-switch input:checked + .visit-switch-track::after {
  left: 18px;
  background: var(--color-primary-text);
}

.visit-switch input:focus-visible + .visit-switch-track {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.visit-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
}

.visit-col-check {
  width: 56px;
}

.visit-col-receipt {
  width: 152px;
}

.visit-col-status {
  width: 148px;
}

.visit-col-result {
  width: 168px;
}

.visit-col-photo {
  width: 72px;
}

.visit-col-date {
  width: 124px;
}

.visit-table thead th {
  height: 40px;
  padding: 0 12px;
  border-top: 1px solid var(--color-border-soft);
  border-bottom: 1px solid var(--color-border-soft);
  background: var(--color-table-head);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}

.visit-table td,
.visit-table tbody th {
  height: var(--row-h-2line);
  padding: 8px 12px;
  border-bottom: 1px solid var(--color-border-soft);
  font-weight: 400;
  text-align: left;
  vertical-align: middle;
}

.visit-table tr > :first-child {
  padding-left: 24px;
}

.visit-table tr > :last-child {
  padding-right: 24px;
}

.visit-table .num {
  text-align: right;
}

.visit-table tbody tr:hover > * {
  background: var(--color-row-hover);
}

.visit-table .visit-c-check {
  padding-right: 0;
}

.visit-hit {
  display: flex;
  align-items: center;
  min-height: var(--control-h);
  cursor: pointer;
}

.visit-pick,
.visit-thumb-pick input,
.visit-check-inline input {
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

.visit-c-receipt a {
  font-weight: 600;
}

.visit-stack {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  min-width: 0;
}

.visit-title {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.visit-line2 {
  overflow: hidden;
  color: var(--color-text-muted);
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.visit-badges {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}

.visit-c-result select {
  width: 100%;
  height: var(--control-h-sm);
  padding: 0 8px;
  font-size: 14px;
}

/* 고른 줄은 왼쪽에 파란 막대를 세웁니다. 고르지 않은 줄의 결과와 사진 수는 감춥니다 — 저장에도
   들어가지 않습니다. 스크립트 없이 CSS(`:has`)가 합니다. */
.visit-row:has(.visit-pick:checked) > :first-child {
  box-shadow: inset 4px 0 0 var(--color-focus);
}

.visit-row:not(:has(.visit-pick:checked)) .visit-c-result select,
.visit-row:not(:has(.visit-pick:checked)) .visit-c-photo span {
  visibility: hidden;
}

.visit-row.has-error > * {
  border-bottom: none;
}

.visit-row.has-error > :first-child {
  box-shadow: inset 4px 0 0 var(--color-error);
}

.visit-table .visit-row-error > td {
  height: auto;
  padding: 0 24px 16px;
  box-shadow: inset 4px 0 0 var(--color-error);
}

.visit-table tbody tr.visit-row-error:hover > * {
  background: none;
}

/* 까닭은 제목 칸 밑에서 시작합니다(고르기 칸 너비만큼 들여 씁니다). */
.visit-row-error-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  padding-left: 48px;
}

.visit-table .visit-group-row > th,
.visit-table .visit-group-row > td {
  height: 40px;
  padding: 0 24px;
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 13px;
  font-weight: 700;
}

/* `완료 건 보기`가 꺼져 있으면 끝난 건 묶음을 감춥니다(스크립트 없이 `:has`). 고른 끝난 건은
   감춰져도 저장에 들어갑니다 — 고른 수가 막대에 보입니다. */
.visit-tickets:not(:has(#show-done:checked)) .visit-done-rows {
  display: none;
}

/* --- 사진 카드 --- */

.visit-upload {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 16px;
  border: 1px dashed var(--color-border);
  border-radius: 8px;
}

.visit-upload input[type="file"] {
  max-width: 100%;
  padding: 4px 0;
  border: none;
  font-size: 14px;
}

.visit-upload-facts {
  color: var(--color-text-muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.visit-photo-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin: 16px 0;
}

.visit-warn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  border-radius: 4px;
  background: var(--color-status-4-bg);
  color: var(--color-status-4-fg);
  font-weight: 600;
  white-space: nowrap;
}

.visit-bulk {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 4px 4px 4px 12px;
  border-radius: 8px;
  background: var(--color-status-1-bg);
  color: var(--color-status-1-fg);
  font-weight: 600;
}

.visit-bulk select {
  width: 240px;
  height: var(--control-h-sm);
  padding: 0 8px;
  font-size: 14px;
}

.visit-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(176px, 1fr));
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.visit-photo {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.visit-thumb {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--color-border-soft);
  border-radius: 8px;
  background: var(--color-table-head);
}

.visit-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.visit-thumb-pick {
  position: absolute;
  top: 8px;
  left: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background: var(--color-card);
}

.visit-photo:has(.visit-thumb-pick input:checked) .visit-thumb {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

.visit-photo-name {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
  color: var(--color-text-muted);
  font-size: 13px;
}

.visit-photo-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.visit-link-button {
  flex: none;
  padding: 0;
  border: none;
  background: none;
  color: var(--color-link);
  font: inherit;
  cursor: pointer;
}

.visit-link-button:hover {
  text-decoration: underline;
}

.visit-photo select {
  width: 100%;
  height: var(--control-h-sm);
  padding: 0 8px;
  font-size: 14px;
}

/* 건을 아직 고르지 않은 사진은 고르는 칸에 호박색 테두리를 긋습니다(오류는 붉은 테두리). */
.visit-photo.is-unassigned select:not([aria-invalid="true"]) {
  border-color: var(--color-status-4-fg);
  box-shadow: inset 0 0 0 1px var(--color-status-4-fg);
}

/* --- 저장 막대 --- */

/* 본문 상자(`.content`)의 안 여백을 넘어 양옆과 아래에 붙습니다. 막대가 스크롤되는 상자의
   아래 끝에 서도록 그 여백만큼 내려 둡니다. */
.visit-actionbar {
  bottom: -24px;
  margin: 24px -24px -24px;
}

.visit-actionbar-inner {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  gap: 8px;
  max-width: var(--page-max);
}


.visit-sum {
  margin-left: 8px;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.visit-sum b {
  color: var(--color-text);
}

.visit-delete-link {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

/* --- 고치기, 지우기 --- */

.visit-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 32px;
  margin: 0 0 16px;
}

.visit-facts div {
  display: flex;
  gap: 8px;
}

.visit-facts dt {
  color: var(--color-text-muted);
}

.visit-facts dd {
  margin: 0;
  font-weight: 600;
}

.visit-change-title {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- 출동 기록 목록 --- */

.visit-page-head .button {
  gap: 8px;
}

.visit-filters {
  padding: 12px 16px;
}

.visit-filter-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.visit-filter-store select {
  width: 208px;
}

.visit-filter-division select {
  width: 148px;
}

.visit-filter-period {
  display: flex;
  align-items: center;
  gap: 8px;
}

.visit-filter-period input {
  width: 152px;
}

/* 1280px 창에서도 거르는 칸 넷과 `검색`이 한 줄에 섭니다. 날짜 칸은 줄이지 않습니다 - 140px에서
   `2026. 10. 08.`이 잘렸습니다(적어도 149.3px, 2026-10-16 측정). 그만큼 현장소장 칸이 줄어듭니다. */
@media (max-width: 1439px) {
  .visit-filter-store select {
    width: 180px;
  }

  .visit-filter-period input {
    width: 152px;
  }
}

.visit-filter-label {
  font-weight: 600;
}

.visit-filter-manager {
  flex: 0 1 160px;
  min-width: 140px;
}

.visit-filter-manager input {
  width: 100%;
}

.visit-log-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  min-height: 48px;
  padding: 8px 16px 8px 24px;
  border-bottom: 1px solid var(--color-border-soft);
}

.visit-log-count {
  margin: 0;
}

.visit-log-sort {
  color: var(--color-text-muted);
  font-size: 13px;
}

.visit-log {
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
}

/* 머리글 고정은 표 카드 공통 규칙(`.table-card thead th`, 과제 4)이 합니다. 이 두 표는 머리글
   위에 막대(고르기 머리, 건수 줄)가 있어 머리글이 카드 위 끝이 아니므로, 그 규칙이 카드 모서리에
   맞춰 굴리는 머리글 양 끝을 곧게 둡니다. */
.visit-table thead th:first-child,
.visit-table thead th:last-child,
.visit-log thead th:first-child,
.visit-log thead th:last-child {
  border-radius: 0;
}

.visit-log td,
.visit-log tbody th {
  height: var(--row-h-2line);
  padding-top: 8px;
  padding-bottom: 8px;
}

.visit-col-store {
  width: 248px;
}

.visit-col-manager {
  width: 136px;
}

/* 보고 전 줄의 첫 줄(`보고 전` 표 54px, 8px, 굵은 `YYYY-MM-DD HH:MM` 141px)과 칸의 좌우 여백(12px씩)이 한
   줄에 드는 폭입니다(227px, 사장님 2026-10-02). 예전 168px은 시각만 들어가 안 폭(144px)에서 시각이 둘로
   꺾입니다. */
.visit-col-when {
  width: 232px;
}

.visit-log-store-line {
  display: flex;
  align-items: center;
  min-width: 0;
}

.visit-log-date .visit-stack {
  align-items: flex-end;
}

/* 방문일시 칸의 첫 줄입니다. 보고 전 줄은 `보고 전` 표가 시각 왼쪽에 섭니다(사장님 2026-10-02). 칸
   (`.visit-col-when`)이 둘을 담는 폭이고, 글꼴이 달라 넘치면 표가 시각 위로 올라가 오른쪽에 붙습니다. 시각은
   꺾이지 않습니다. 처리 화면의 출동 이력은 아래 `.history-when`입니다. */
.visit-log-when {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 4px 8px;
  font-weight: 600;
  white-space: nowrap;
}

/* 방문일시 칸의 둘째 줄 링크입니다: 보고를 마친 줄은 `수정`, `삭제`, 보고 전 줄은 `보고`, `출발 취소`.
   카드 배치(1279px 이하)의 보고 전 줄은 그 앞에 `보고 전` 표가 섭니다(`.visit-tag-narrow`). */
.visit-log-actions,
.history-actions {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

/* 보고 전 표의 둘째 벌입니다. 카드 배치에서만 둘째 줄에 보입니다(아래 1279px 묶음, 템플릿의 설명). */
.visit-tag-narrow {
  display: none;
}

/* 보고 전 줄은 굵습니다(사장님 2026-10-02): 매장과 사업부, 현장소장과 건수, 방문일시 칸. 접수 건 결과의
   표는 제 굵기 그대로이고, 둘째 줄의 링크는 보고를 마친 줄의 `수정`, `삭제`와 같은 굵기입니다. */
.visit-log tr[data-pending] > .visit-log-store,
.visit-log tr[data-pending] > .visit-log-manager,
.visit-log tr[data-pending] > .visit-log-date,
.visit-log tr[data-pending] .visit-log-when {
  font-weight: 700;
}

.visit-log tr[data-pending] .visit-log-actions {
  font-weight: 400;
}

.visit-change-list {
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  gap: 4px 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.visit-change-list li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--badge-h);
  white-space: nowrap;
}

.visit-change-list-full {
  flex-direction: column;
  align-content: flex-start;
  gap: 12px;
}

.visit-change-list-full li {
  flex-wrap: wrap;
  white-space: normal;
}

.visit-change {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.visit-arrow {
  color: var(--color-text-muted);
}

.visit-kept {
  color: var(--color-text-muted);
  font-size: 13px;
  font-weight: 600;
}

.visit-change-more a {
  color: var(--color-text-muted);
  font-weight: 600;
}

.visit-none {
  color: var(--color-text-muted);
}

/* 출동 기록 목록과 출동 보고, 출동 기록 수정의 접수 건 표는 1280 아래에서 줄을 카드로 쌓습니다(폰 375도 여기에
   듭니다, 2026-10-16 전에는 720px 아래만). 칸 폭의 합(출동 기록 목록 616px와 결과 칸, 접수 건 표 648px와 제목 칸)이
   본문보다 넓어, 768에서 방문일시와 신청일이 화면 밖에 있었고 870~1200에서는 꺾이지 않는 결과 표가 방문일시를
   덮었습니다(측정). 표를 가로 스크롤 상자(`.ledger-scroll`)로 감싸지 않습니다 - 머리글 고정이 그 상자에 붙습니다.
   카드의 옆 여백은 위 막대(`.visit-log-bar`, `.visit-pick-head`)의 24px에 맞추고, 폰 묶음이 16px로 좁힙니다.
   경계는 매장 접수 목록이 표를 카드로 쌓는 경계(1279px)와 같습니다. */
@media (max-width: 1279px) {
  .visit-table,
  .visit-table tbody {
    display: block;
  }

  .visit-table thead,
  .visit-table colgroup {
    display: none;
  }

  .visit-table tr {
    display: grid;
    grid-template-columns: 32px minmax(0, 1fr) auto;
    grid-template-areas:
      "check receipt status"
      ". title title"
      ". result photo";
    gap: 8px 12px;
    padding: 12px 24px;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .visit-table td,
  .visit-table tbody th,
  .visit-table tr > :first-child,
  .visit-table tr > :last-child {
    display: block;
    height: auto;
    padding: 0;
    border: none;
  }

  /* 출발 화면의 표에는 결과와 사진 칸이 없어 둘째 줄(제목)에서 끝납니다(결정 기록 0056). */
  .visit-table-depart tr {
    grid-template-areas:
      "check receipt status"
      ". title title";
  }

  .visit-c-check {
    grid-area: check;
  }

  .visit-hit {
    min-height: var(--control-h-sm);
  }

  .visit-c-receipt {
    grid-area: receipt;
    align-self: center;
  }

  .visit-c-status {
    grid-area: status;
    align-self: center;
    justify-self: end;
  }

  .visit-c-title {
    grid-area: title;
  }

  .visit-title {
    white-space: normal;
  }

  .visit-c-result {
    grid-area: result;
  }

  .visit-table .visit-c-photo {
    grid-area: photo;
    align-self: end;
    padding-bottom: 8px;
  }

  .visit-table tr > .visit-c-date {
    display: none;
  }

  .visit-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 4px;
    font-size: 13px;
    font-weight: 600;
  }

  .visit-table .visit-c-photo[data-label]::before {
    display: inline;
    margin: 0 4px 0 0;
  }

  .visit-row:not(:has(.visit-pick:checked)) .visit-c-result,
  .visit-row:not(:has(.visit-pick:checked)) .visit-c-photo {
    display: none;
  }

  .visit-row:has(.visit-pick:checked) > :first-child,
  .visit-row.has-error > :first-child {
    box-shadow: none;
  }

  .visit-row:has(.visit-pick:checked) {
    box-shadow: inset 4px 0 0 var(--color-focus);
  }

  .visit-row.has-error {
    border-bottom: none;
    box-shadow: inset 4px 0 0 var(--color-error);
  }

  .visit-table tr.visit-row-error {
    display: block;
    padding: 0 24px 16px;
    box-shadow: inset 4px 0 0 var(--color-error);
  }

  .visit-table .visit-row-error > td {
    padding: 0;
    box-shadow: none;
  }

  .visit-table tr.visit-group-row {
    display: block;
    padding: 0;
  }

  /* 묶음 머리(`출발한 건 2`, `완료 건 55`)의 이름과 수 사이 빈칸은 flex에서 사라지므로 틈을 둡니다. */
  .visit-table .visit-group-row > th,
  .visit-table .visit-group-row > td {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0 24px;
  }

  .visit-log,
  .visit-log tbody {
    display: block;
  }

  .visit-log thead,
  .visit-log colgroup {
    display: none;
  }

  .visit-log tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "store date"
      "manager manager"
      "changes changes";
    gap: 8px 12px;
    padding: 12px 24px;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .visit-log td,
  .visit-log tbody th,
  .table-card .visit-log tr > :first-child,
  .table-card .visit-log tr > :last-child {
    display: block;
    height: auto;
    padding: 0;
    border: none;
  }

  .visit-log-store {
    grid-area: store;
    min-width: 0;
  }

  .visit-log-date {
    grid-area: date;
  }

  .visit-log-manager {
    grid-area: manager;
  }

  .visit-log-manager .visit-stack {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: baseline;
    gap: 4px 12px;
  }

  .visit-log-changes {
    grid-area: changes;
  }

  /* 보고 전 줄의 `보고 전` 표는 카드에서 둘째 줄의 `보고` 왼쪽에 섭니다(2026-10-02). 첫 줄은 보고를 마친 줄처럼
     시각만이라 매장 이름이 같은 폭을 받습니다. 첫 줄의 표는 눈에만 감추어 낭독기가 읽고, 둘째 줄의 표는
     `aria-hidden`입니다. */
  .visit-tag-wide {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .visit-tag-narrow {
    display: inline-flex;
  }

  /* 방문일시 칸의 폭은 첫 줄(시각)이 정합니다. 둘째 줄은 칸 폭으로 그리고(`width: 0`, `min-width: 100%`),
     더 길면(보고 전 줄의 표와 두 링크, 약 170px) 오른쪽에 붙은 채 왼쪽, 사업부 줄 옆의 빈자리로 나옵니다.
     사업부는 가장 긴 것도 넉 자(약 44px)라 겹치지 않습니다. */
  .visit-log-date .visit-log-actions {
    width: 0;
    min-width: 100%;
    justify-content: flex-end;
    overflow: visible;
  }
}

/* === 출동 기록 끝 === */

/* --- 매장 접수 처리 B안 (헬프데스크 1차 과제 5, 설계 문서 4절) ------------------------------
   한 칸으로 흐릅니다: 머리, 접수 상세, 저장 폼(처리)과 추가 검토, 출동 이력과 처리 과정(한 줄), 견적서,
   작업 보고, 작업 사진(사장님 2026-10-08, 결정 기록 0058). 댓글은 오른쪽 아래 단추로 여는 창이고, 저장은
   화면 아래에 붙은 막대(`.actionbar`)에 있습니다.

   흔한 이름(`.back`, `.badges`, `.count`, `.form-grid`, `.span-*`)은 이 화면의
   쪽(`.detail-b`) 아래에만, 막대의 자리 잡기와 그 안의 것은 이 화면의 막대(`.detail-b-bar`) 아래에만
   겁니다. 출동 기록 화면(과제 6)도 `.actionbar`를 쓰므로, 한 파일에 합쳐도 서로를 건드리지
   않습니다. */

/* 넓은 화면에서는 본문 상자가 스크롤을 맡습니다. Tab으로 옮겨 간 칸이 아래 막대(64px) 밑에 숨지
   않게, 막대 높이에 24px을 더한 만큼 비워 두고 칸을 올려 보입니다(2.4.11). 폰은 문서가 스크롤해
   720px 묶음이 따로 맡습니다. */
.content {
  scroll-padding-bottom: 88px;
}

/* 쪽은 `--page-max`(1200px)를 넘지 않습니다. 1920에서 칸이 끝까지 늘어나면 한 칸이 1500px을 넘어
   이름표와 값이 멀어집니다. 본문 상자(`.content`)가 아니라 이 쪽에 겁니다.

   `position: relative`는 낭독기에만 읽히는 글(출동 이력의 결과, `.visually-hidden`, 절대 위치)을
   이 쪽 안에 가둡니다. 없으면 그 글이 스크롤된 본문 아래쪽 자리에서 문서를 늘려(1920에서 1080 →
   1304px), 초점이나 `#` 링크로 뛸 때 문서가 함께 밀려 위 막대가 화면 밖으로 사라집니다. */
.detail-b {
  position: relative;
  max-width: var(--page-max);
}

.dh {
  margin-bottom: 16px;
}

/* 목록으로 가는 링크와 `이 매장 미완료 건 함께 기록`입니다. 폰에서 한 줄에 들지 않으면 단추가
   다음 줄로 내려갑니다. */
.dh-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  min-height: var(--control-h);
  margin-bottom: 4px;
}

.detail-b .back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: var(--control-h-sm);
  color: var(--color-link);
  font-weight: 600;
  text-decoration: none;
}

.detail-b .back:hover {
  text-decoration: underline;
}

.detail-b .back svg {
  flex: none;
}

.dh .page-title {
  margin: 0;
}

/* 접수번호와 매장명 사이, 신청일과 방문 기한 사이의 `|`입니다. 낭독기는 읽지 않습니다. */
.dh-sep {
  color: var(--color-text-muted);
  font-weight: 400;
}

.dh-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 8px 0 0;
  font-size: 14px;
}

/* 상태 뱃지 곁에 붙는 표들입니다(요청유형, 지연). 출동 이력의 `[접수] 유지`, `[접수] → [완료]`도
   같은 묶음입니다. */
.detail-b .badges {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}

.dh-due {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* 지금 지연입니다. 빨강은 지연과 긴급에만 씁니다(`.tag-late`와 같은 색). 기한 뒤에 다녀온 건의
   `N일 늦음`은 지연이 이미 풀렸으므로 검정입니다. */
.dh-due.is-late {
  color: var(--color-status-3-fg);
}

/* 저장 폼과 그 곁 카드(추가 검토, 견적서, 작업 보고)의 12칸 격자입니다. 처리 카드의 첫 줄은 상태, 관리자
   분류, 작업 항목, 현장소장(3칸씩)이고, 두 사유는 6칸씩 한 줄입니다(결정 기록 0054). */
.detail-b .form-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 16px;
}

/* 칸의 최소 너비(`.field`의 160px)가 좁은 자리를 넘쳐 옆 칸을 덮지 않게 풉니다. */
.detail-b .form-grid .field {
  min-width: 0;
}

.detail-b .span-3 {
  grid-column: span 3;
}

/* 두 사유와 작업 보고의 작업 시작과 완료는 6칸씩입니다(결정 기록 0052, 0054). 방문 예정일과 완료일도
   6칸씩입니다 - 예정 시각과 소요 예정을 빼며 3칸씩 넷에서 6칸씩 둘로 다시 나눴습니다(결정 기록 0055).
   견적서 안의 4칸 자리(견적여부, 견적금액, 승인결과)는 2026-10-02에 없어졌습니다(결정 기록 0058). */
.detail-b .span-6 {
  grid-column: span 6;
}

.detail-b .span-9 {
  grid-column: span 9;
}

/* 처리 폼 안의 작은 묶음 제목(`방문 예정`, `메모`)입니다. 카드 제목(20px) 아래 단계라 읽는 글 크기로 굵게
   둡니다. */
.form-subtitle {
  margin: 24px 0 12px;
  font-size: 16px;
  font-weight: 700;
}

/* 접는 칸입니다(결정 기록 0054): 처리 카드의 `추가 항목`과 견적서. 접힌 줄(`<summary>`)은 브라우저의 삼각형
   대신 오른쪽을 가리키는 화살표 하나를 쓰고 펼치면 아래로 돕니다. 줄 전체가 누르는 자리라 40px 높이이고,
   폭이 모자라면(375) 상태 글이 다음 줄로 내려가 옆으로 밀지 않습니다. 스크립트 없이 여닫힙니다. */
.fold-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
  min-height: var(--control-h);
  list-style: none;
  cursor: pointer;
}

.fold-summary::-webkit-details-marker {
  display: none;
}

.fold-icon {
  flex: none;
  color: var(--color-text-muted);
  transition: transform 0.15s ease;
}

.fold[open] > .fold-summary .fold-icon {
  transform: rotate(90deg);
}

.fold[open] > .fold-summary {
  margin-bottom: 12px;
}

.fold-summary .section-title {
  margin: 0;
}

/* 접힌 견적서 줄의 짧은 상태(`견적완료 | 합계 ₩117,480 | 3줄`)입니다. 보조 글자라 한 단계 작고 흐립니다. */
.fold-status {
  color: var(--color-text-muted);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

/* 처리 카드 끝의 `추가 항목`입니다. 접힌 줄은 묶음 제목(`.form-subtitle`)과 같은 크기와 굵기입니다. */
.form-more {
  margin-top: 12px;
}

.form-more > .fold-summary {
  font-size: 16px;
  font-weight: 700;
}

/* 견적서는 카드 전체가 접힙니다. 펼치면 카드 제목 아래 틈은 카드 제목(`.section-title`)과 같은 몫입니다. */
.estimate-fold[open] > .fold-summary {
  margin-bottom: 16px;
}

/* 처리 화면 작업 사진 카드의 자리(작업 전 사진, 작업 후 사진)입니다(결정 기록 0058). 자리
   사이는 묶음 제목(`.form-subtitle`)의 위 여백이 띄웁니다. */
.work-photos + .work-photos {
  border-top: 1px solid var(--color-border-soft);
}

.work-photos .form-subtitle {
  margin-top: 16px;
}

/* 자리마다의 파일 칸입니다. 2026-10-05에는 칸 오른쪽에 `올리기` 단추가 섰지만, 2026-10-08부터 고른 사진은
   아래 막대의 `저장`으로 처리 내용과 함께 올라가 단추가 없습니다(결정 기록 0058). 칸이 남은 폭을 다 쓰고, 더
   좁은 폭에서도 줄어 화면을 옆으로 밀지 않습니다(`min-width: 0`). */
.work-photo-upload {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: 8px;
}

.work-photo-upload .photo-file {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 100%;
  margin-top: 0;
}

/* 작업 사진의 `삭제`는 바로 지우지 않고 표시합니다(사장님 2026-10-08, 결정 기록 0058). 단추처럼 보이는
   이름표 안의 확인란이 처리 폼에 딸려 `저장`이 지웁니다. 확인란은 보이지 않지만 Tab으로 닿고 Space로 켜며,
   초점 고리는 이름표에 그립니다. 표시한 사진은 흐려지고 단추 글이 `삭제 취소`로 바뀝니다 - 스크립트 없이
   CSS만으로입니다. */
.photo-delete {
  position: relative;
  cursor: pointer;
}

.photo-delete-mark {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}

.photo-delete:has(.photo-delete-mark:focus-visible) {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.photo-delete-undo {
  display: none;
}

.photo-delete-mark:checked ~ .photo-delete-do {
  display: none;
}

.photo-delete-mark:checked ~ .photo-delete-undo {
  display: inline;
}

.photo-delete:has(.photo-delete-mark:checked) {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.photo-thumb:has(.photo-delete-mark:checked) img {
  opacity: 0.4;
}

/* 올리지 못한 사진의 까닭입니다. 그 자리의 파일 칸 바로 위에 섭니다(`.errors`와 같은 모양). 고른 사진이
   모두 합쳐 상한을 넘을 때 스크립트가 보이는 줄(`.photo-oversize`)도 파일 칸마다 바로 위, 오류 줄 아래입니다
   (2026-10-21 QA - 예전에는 작업 사진 카드 맨 아래 하나였습니다). */
.photo-errors {
  margin: 8px 0 0;
}

.photo-errors .errors-title:last-child {
  margin-bottom: 0;
}

.photo-oversize {
  margin: 8px 0 0;
}

/* 첨부 사진이 이미 상한인 건에서 파일 칸, 붙여넣기 상자, 저장 전 목록 대신 서는 한 줄입니다(2026-10-21 QA). 모양은
   도움말(`.field-help`)이고 위 격자나 오류 줄과 8px 띄웁니다. */
.photo-full {
  margin: 8px 0 0;
}

/* 작업 사진의 붙여넣기와 끌어다 놓기 상자입니다(사장님 2026-10-16, 과제 7c). 모양은 오피스 접수 등록의 상자
   (`.photo-drop`) 그대로이고 파일 칸 바로 아래에 섭니다. 붙여넣기는 상자에 초점이 있어야 받으므로, 눌러 둔
   상자는 테두리가 실선의 초점 색이 됩니다. 스크립트가 없으면 상자는 숨어 있습니다(`hidden`). */
.work-photo-drop {
  margin-top: 8px;
}

.work-photo-drop:focus {
  border-style: solid;
  border-color: var(--color-focus);
}

/* 저장 전 사진입니다. 그 자리의 파일 칸에서 기다리는 파일마다 썸네일, `저장 전` 표, `빼기`가 섭니다. 저장된
   사진(`.photo-thumb img`, 실선)과 달리 점선 테두리라 아직 올라가지 않은 사진이 한눈에 갈립니다. 표와 단추는
   한 줄에 서고, 칸이 좁으면 단추가 다음 줄로 내려갑니다. 비어 있으면 목록째 숨습니다(`.photo-grid:empty`). */
.work-photo-pending {
  margin-top: 12px;
}

.work-photo-pending-item img {
  border-width: 2px;
  border-style: dashed;
  border-color: var(--color-border);
}

.work-photo-pending-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* 견적서 안, 견적서 폼 위의 견적여부 줄입니다. 견적서 합계로 센 값이라 읽기만 합니다(결정 기록 0058). */
.estimate-fields {
  margin-bottom: 24px;
}

/* 날짜와 시각 두 칸(작업 시작, 작업 완료)입니다. 날짜 칸과 `오늘`, 시각 칸과 `지금`이 한 줄에 서고, 폭이
   모자라면(폰) 시각이 다음 줄로 내려갑니다. */
.field-when .with-quick {
  flex-wrap: wrap;
}

.field-when .with-quick input {
  flex: none;
}

.field-when .with-quick input[type="date"] {
  width: 168px;
}

.field-when .with-quick input[type="time"] {
  width: var(--control-time-w);
}

.detail-b .span-12 {
  grid-column: span 12;
}

/* 사이드바가 200px로 줄어드는 폭에서는 3칸짜리 날짜 칸과 `오늘`이 한 줄에 들지 않으므로 칸을 넓힙니다:
   한 줄에 둘(6칸)입니다. */
@media (max-width: 1100px) {
  .detail-b .span-3,
  .detail-b .span-9 {
    grid-column: span 6;
  }
}

/* 출동 이력입니다. 표 글자(14px)이고, 방문일시는 오른쪽에 붙여 자릿수 폭을 맞춥니다(`.num`). 이름은
   `history-`입니다 - `visit-`는 출동 기록 화면의 이름입니다. */
.detail-b .section-title .count {
  color: var(--color-text-muted);
  font-weight: 600;
}

.history-table {
  width: 100%;
  border-collapse: collapse;
}

.history-table th {
  padding: 0 12px 8px 0;
  border-bottom: 1px solid var(--color-border-soft);
  color: var(--color-text-muted);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}

.history-table td {
  padding: 12px 12px 12px 0;
  border-bottom: 1px solid var(--color-border-soft);
  vertical-align: top;
}

.history-table th.num,
.history-table td.num {
  padding-right: 0;
  text-align: right;
  white-space: nowrap;
}

/* 메모는 그 출동 줄 바로 아래에 붙습니다. 두 줄 사이에는 선을 긋지 않습니다. */
.history-row:has(+ .history-memo) td {
  border-bottom: none;
}

.history-memo td {
  padding-top: 0;
  color: var(--color-text-muted);
}

/* 보고 전 기록의 방문일시 칸은 출동 기록 목록의 보고 전 줄과 같은 모양입니다(사장님 2026-10-02, 위
   `.visit-log-when`, `.visit-log-actions`): 첫 줄 `[보고 전] 출발 시각`, 둘째 줄 `보고`와 `출발 취소`.
   오른쪽에 붙습니다. 예전에는 앞의 사진 칸(`td.num`)에 오른쪽 여백이 없어 왼쪽에 12px을 따로 두었는데, 그
   칸이 빠져(사장님 2026-10-05) 이제 앞은 오른쪽 여백 12px이 있는 함께 기록 칸입니다. */
.history-pending {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}

/* 첫 줄 `[보고 전] 출발 시각`입니다. 이 표는 1280에서 처리 과정 옆의 반쪽 카드(안 폭 436px)라, 표와 시각이
   한 줄에 들지 않으면 표가 시각 위로 올라가 오른쪽에 붙습니다(2026-10-02). 들면 시각 왼쪽입니다. 시각은
   꺾이지 않습니다(`td.num`). */
.history-when {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 4px 8px;
}

/* 보고 전 줄은 굵습니다. 링크(함께 기록의 접수번호, `보고`, `출발 취소`)는 보통 굵기입니다 - 출동 기록
   목록도 접수 건 결과와 둘째 줄의 링크는 굵게 하지 않습니다. 굵은 접수번호는 12px 더 넓어 반쪽 카드에 들지
   않습니다. */
.history-row[data-pending] td {
  font-weight: 700;
}

.history-row[data-pending] .link {
  font-weight: 400;
}

/* 함께 기록의 접수번호는 숫자 가운데서 꺾이지 않습니다(`20260 / 00000 / 04`가 되던 자리). 여러 건이면
   `, ` 뒤에서 줄이 바뀝니다. */
.history-table .link.num {
  white-space: nowrap;
}

/* 1280의 반쪽 카드에서는 네 칸의 가장 좁은 폭(현장소장 56, 이 건 결과 129, 접수번호 120, 보고 전 방문일시
   141px)의 합이 안 폭 436px을 넘습니다. 접수번호와 시각은 꺾지 않으므로, 좁을 때는 이 건 결과의
   `[전] → [후]`가 표 사이에서 줄을 바꿔 표가 카드 밖으로 나가지 않습니다. */
.history-table .visit-change {
  flex-wrap: wrap;
}

/* 폰에서만 보이는 칸 이름(`함께 기록`)입니다. 표 배치에서는 머리글이 말합니다. */
.history-table td[data-label]::before {
  display: none;
}

/* 아래 저장 막대는 본문 상자의 여백(24px)까지 펴서 좌우 끝과 바닥에 붙습니다. 붙는 자리는 스크롤
   상자의 안 여백만큼 안쪽이라, `bottom`을 그만큼 내려야 막대 밑으로 본문이 비치지 않습니다. */
.content > .detail-b-bar {
  bottom: -24px;
  margin: 8px -24px -24px;
}

/* 막대 안의 단추 줄은 쪽 폭에 맞춰, `삭제`가 화면 끝이 아니라 쪽의 오른쪽 끝에 섭니다. */
.detail-b-bar .actionbar-inner {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: 8px;
  min-width: 0;
  max-width: var(--page-max);
}

/* `마지막 저장 10-06 09:40 kim`입니다. 곁글이라 13px, 흐린 글자입니다. */
.detail-b-bar .saved-note {
  margin-left: 8px;
  color: var(--color-text-muted);
  font-size: 13px;
  white-space: nowrap;
}

/* `저장하지 않은 변경이 있습니다`입니다(사장님 2026-10-08). 곁글 크기지만 지금 할 일이라 흐리지 않고 굵습니다.
   스크립트가 `hidden`을 붙였다 뗍니다(`[hidden]`이 이깁니다). */
.detail-b-bar .unsaved-changes {
  margin-left: 8px;
  color: var(--color-text);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}

/* `견적서에 저장하지 않은 줄이 있습니다.`입니다(사장님 2026-10-26). 견적서는 따로 난 폼이라 위 알림이 세지 않습니다. 위
   알림 바로 뒤에 같은 크기와 굵기로 섭니다. 막대의 `저장`이 한 번 멈추면(`is-blocked`) 붉은 글자가 되고 뒤의 한 마디
   (`견적서 저장을 먼저 눌러 주세요.`)가 붙습니다. 1101~1279px에서는 두 알림과 `마지막 저장`이 한 줄에 다 들지 않아 `삭제`가
   쪽 밖으로 밀렸으므로(2026-10-26 측정, 1101에서 `마지막 저장`부터), 이 알림만 줄어들며 글을 꺾습니다. 단추 줄은
   그대로입니다. */
.detail-b-bar .estimate-unsaved {
  flex: 0 1 auto;
  min-width: 0;
  margin-left: 8px;
  color: var(--color-text);
  font-size: 13px;
  font-weight: 600;
}

.detail-b-bar .estimate-unsaved.is-blocked {
  color: var(--color-error);
}

/* 막대의 `삭제`는 흰 바탕에 빨간 글자와 테두리입니다(시안). 되돌릴 수 없는 자리지만 화면을 열 때마다
   눈에 가장 먼저 들어오는 단추가 되면 안 됩니다. 확인 화면의 `삭제`는 꽉 찬 빨강 그대로입니다. */
.detail-b-bar .button-danger {
  background: var(--color-card);
  border-color: var(--color-danger);
  color: var(--color-danger);
}

/* 보낸 폼의 `저장`입니다. 단추가 폼 밖(막대)에 있어 `form[aria-busy]` 규칙이 닿지 않습니다.
   출동 기록 화면의 막대(`.visit-actionbar`)도 같습니다. */
.detail-b-bar [aria-disabled="true"],
.visit-actionbar [aria-disabled="true"] {
  opacity: 0.6;
  cursor: progress;
}

/* 1100px까지는 저장하지 않은 변경 알림이 폰처럼 단추 줄 위의 한 줄입니다. 721~816px에서 알림이 단추 사이에 끼면
   `삭제`가 쪽 밖으로 밀렸습니다(2026-10-16 마지막 검토, 768에서 49px). 1100px 위는 단추 옆 그대로입니다. 폰은 아래
   720px 묶음이 맡습니다. */
@media (min-width: 721px) and (max-width: 1100px) {
  .detail-b-bar .actionbar-inner {
    flex-wrap: wrap;
  }

  .detail-b-bar .unsaved-changes {
    order: -1;
    flex-basis: 100%;
    margin-left: 0;
  }

  /* 견적서 알림(2026-10-26)도 같은 한 줄이고 처리 폼의 알림 아래에 섭니다. */
  .detail-b-bar .estimate-unsaved {
    order: -1;
    flex-basis: 100%;
    margin-left: 0;
  }

  /* 알림이 한 줄을 차지하는 동안 막대는 65px에서 93px로 자랍니다. 댓글 단추(`bottom: 80px`)가 막대 위 끝을 13px
     덮었으므로(2026-10-17 QA) 그만큼(알림 줄 20px과 줄 사이 8px) 올려, 알림이 없을 때처럼 막대와 15px 띄웁니다.
     375는 아래 720px 묶음이, 1101px 위는 알림이 단추 옆이라 그대로입니다. */
  .content:has(> .detail-b-bar .unsaved-changes:not([hidden])) .chat-fab {
    bottom: 108px;
  }

  /* 견적서 알림만 서도 막대는 93px이라 같은 만큼 올리고, 두 알림이 함께 서면(120px) 한 줄 더 올립니다(2026-10-26). */
  .content:has(> .detail-b-bar .estimate-unsaved:not([hidden])) .chat-fab {
    bottom: 108px;
  }

  .content:has(> .detail-b-bar .unsaved-changes:not([hidden])):has(> .detail-b-bar .estimate-unsaved:not([hidden])) .chat-fab {
    bottom: 136px;
  }

  /* 알림 줄로 자란 막대(93px, 두 알림이면 120px)가 Tab으로 옮겨 간 칸을 덮지 않게, 비워 두는 자리도 막대 높이에
     24px을 더한 만큼 늘립니다(2026-10-26 측정: 88px이면 막대 위 끝 바로 아래의 칸이 4~12px 덮였습니다). */
  .content:has(> .detail-b-bar .unsaved-changes:not([hidden])),
  .content:has(> .detail-b-bar .estimate-unsaved:not([hidden])) {
    scroll-padding-bottom: 120px;
  }

  .content:has(> .detail-b-bar .unsaved-changes:not([hidden])):has(> .detail-b-bar .estimate-unsaved:not([hidden])) {
    scroll-padding-bottom: 144px;
  }
}

/* --- 좁은 화면: 사이드바를 위로 접는다 (1.4.10) ----------------------------- */

/* 1280px 화면에서 400% 확대하면 320px 시야가 됩니다. 저시력 직원이 실제로 겪는
   자리입니다. 그때 200px 사이드바와 `body { overflow: hidden }`이 겹쳐, 본문 자리가
   120px만 남고 화면 밖으로 나간 요소가 사백 개가 넘는데 **스크롤조차 되지 않았습니다**
   — 거르개 폼과 `검색`·`초기화`에 손이 닿을 방법이 아예 없었습니다.

   넓은 화면의 `body { overflow: hidden }`은 사이드바를 제자리에 고정하려고 있는
   것이고 거기서는 문제가 아니므로 그대로 둡니다. 표 자체는 2차원 배치라 1.4.10의
   예외입니다 — `.ledger-scroll` 안에서 계속 옆으로 넘깁니다. */
@media (max-width: 720px) {
  html,
  body {
    height: auto;
    overflow: visible;
  }

  .layout {
    display: block;
    height: auto;
  }

  .sidebar {
    position: static;
    width: 100%;
    height: auto;
    overflow: visible;
    padding: 12px;
  }

  .brand {
    padding: 0 8px 12px;
  }

  .nav,
  .nav-group {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .nav-group {
    margin-top: 0;
  }

  /* 메뉴가 한 줄로 누우면 막대도 글자 아래로 눕힙니다. */
  .nav-link[aria-current="page"]::before {
    top: auto;
    right: 12px;
    bottom: 2px;
    left: 12px;
    width: auto;
    height: 3px;
  }

  .main {
    height: auto;
    overflow: visible;
  }

  /* 이름과 링크 셋이 한 줄에 들지 않으면 다음 줄로 내립니다. 높이를 묶어 두면 넘친
     링크가 본문 위로 겹칩니다. */
  .topbar {
    flex-wrap: wrap;
    height: auto;
    min-height: 52px;
    padding: 4px 16px;
  }

  .content {
    height: auto;
    overflow: visible;
    padding: 16px;
  }

  .card {
    padding: 16px;
  }

  /* 칸마다 제 너비를 주장하면 한 줄에 들어가지 못하고 그대로 잘려 나갑니다. */
  .field,
  .field-period,
  .field-wide {
    min-width: 0;
    width: 100%;
    flex: 0 1 auto;
  }

  .period {
    flex-wrap: wrap;
  }

  .breadcrumb,
  .list-actions,
  .filter-actions,
  .form-actions,
  .with-quick,
  .share-row,
  .as-of-form {
    flex-wrap: wrap;
  }

  .filter-actions,
  .list-actions {
    justify-content: flex-start;
    margin-left: 0;
  }

  .grid,
  .info,
  .home-grid,
  .detail-grid,
  .stat-row,
  .home-side .stat-row {
    grid-template-columns: minmax(0, 1fr);
  }

  /* 매장 접수 목록(B안)의 조건 막대는 폰(375)에서 칸을 한 줄씩 쌓습니다(설계 3절). 줄을 카드로
     쌓는 것은 위의 `max-width: 1279px` 묶음입니다. */
  .table-card {
    padding: 0;
  }

  .fb-search,
  .fb-period,
  .fb-store {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* 신청일의 두 날짜는 한 줄씩 씁니다. 한 줄에 둘이면 칸이 120px라 날짜가 잘렸습니다(375, 2026-10-16). */
  .fb-period {
    flex-wrap: wrap;
  }

  .fb-period input {
    flex: 1 1 100%;
    width: auto;
    min-width: 0;
  }

  .fb-period .period-sep {
    display: none;
  }

  .fb-division {
    flex: 1 1 0;
    min-width: 0;
  }

  .fb-store select,
  .fb-division select {
    width: 100%;
  }

  .fb-panel {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  /* 폰의 카드에서는 매장명에 한 줄을 다 줍니다. 상태와 요청유형 곁에 두면 375에서 매장명이
     `…토어 성수점`처럼 지점 이름까지 잘립니다. */
  .ledger-2line tr {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "receipt receipt date"
      "title title title"
      "store store store"
      "status urgency urgency"
      "change change change";
  }

  /* --- 출동 기록(헬프데스크 1차 Task 6): 표를 카드로 --- */

  .visit-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* 사업부와 매장 이름이 한 줄씩 폭을 다 씁니다. */
  .visit-store-division,
  .visit-combo,
  .visit-store-list {
    width: 100%;
  }

  /* 날짜와 `오늘`이 한 줄, 시각과 `지금`이 다음 줄에 섭니다. */
  .visit-field-when .with-quick input[type="date"],
  .visit-field-when .with-quick input[type="time"] {
    flex: 1 1 calc(100% - 80px);
    width: auto;
  }

  .visit-pick-head {
    padding: 16px;
  }

  .visit-pick-bar {
    gap: 8px 16px;
  }

  .visit-empty-card {
    padding: 0 16px 16px;
  }

  /* 접수 건 표의 카드(위 1279px 묶음)는 폰에서 카드의 안 여백(16px)에 맞춥니다. */
  .visit-table tr {
    padding: 12px 16px;
  }

  .visit-table tr.visit-row-error {
    padding: 0 16px 16px;
  }

  .visit-table .visit-group-row > th,
  .visit-table .visit-group-row > td {
    padding: 0 16px;
  }

  .visit-upload {
    flex-direction: column;
    align-items: stretch;
  }

  .visit-photo-toolbar .visit-spacer {
    display: none;
  }

  .visit-bulk {
    width: 100%;
  }

  .visit-bulk select {
    flex: 1 1 120px;
    width: auto;
  }

  .visit-photo-grid {
    grid-template-columns: 1fr 1fr;
    gap: 12px;
  }

  /* 좁은 화면에서는 본문 상자가 아니라 창이 스크롤합니다(아래 공통 좁은 화면 규칙). 창에는 안
     여백이 없어 막대를 창 아래 끝에 그대로 붙입니다. */
  .visit-actionbar {
    bottom: 0;
    margin: 16px -16px -16px;
    padding: 12px 16px;
  }

  .visit-actionbar .visit-sum {
    display: none;
  }

  .visit-actionbar .visit-warn {
    font-size: 13px;
  }

  .visit-filter-store,
  .visit-filter-division,
  .visit-filter-manager {
    flex: 1 1 calc(50% - 4px);
    min-width: 0;
  }

  .visit-filter-store select,
  .visit-filter-division select {
    width: 100%;
  }

  .visit-filter-period {
    flex: 1 1 100%;
    flex-wrap: wrap;
  }

  .visit-filter-label {
    flex: 1 1 100%;
  }

  /* 두 날짜는 한 줄씩 씁니다. 한 줄에 둘이면 칸이 142px라 날짜가 잘렸습니다(375, 2026-10-16). */
  .visit-filter-period input {
    flex: 1 1 100%;
    width: auto;
    min-width: 0;
  }

  .visit-filter-period .period-sep {
    display: none;
  }

  .visit-filters .button-primary {
    flex: 1 1 100%;
  }

  .visit-log-bar {
    padding: 8px 16px;
  }

  /* 출동 기록의 카드(위 1279px 묶음)는 폰에서 카드의 안 여백(16px)에 맞춥니다. 결과 표 사이도 조금 넓힙니다. */
  .visit-log tr {
    padding: 12px 16px;
  }

  .visit-change-list {
    gap: 8px 16px;
  }

  /* 방문일시 칸 둘째 줄의 링크(`수정`, `삭제`, `보고`, `출발 취소`)는 폰에서 누를 자리가 44px입니다(QA
     2026-10-01 lane C SF-4, 보고를 마친 줄도 2026-10-02부터). 13px 한 줄 글자만 누르게 두면 과녁이 20px
     남짓입니다. 처리 화면의 출동 이력도 같습니다. */
  .visit-log-actions .link,
  .history-actions .link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
  }

  /* 매장 접수 처리(B안): 저장 폼은 칸마다 한 줄입니다. 문서가 스크롤하므로, 초점이 아래 막대 밑에
     숨지 않게 막대가 있는 화면(처리, 출동 기록 쓰기와 고치기)만 문서에 같은 만큼 비워 둡니다. 막대는
     본문 여백(16px)까지 폅니다. */
  .detail-b .form-grid > .field {
    grid-column: 1 / -1;
  }

  html:has(.visit-actionbar),
  html:has(.detail-b-bar) {
    scroll-padding-bottom: 88px;
  }

  .content > .detail-b-bar {
    bottom: 0;
    margin: 8px -16px -16px;
    padding: 12px 16px;
  }

  /* 375에서는 저장, 목록으로, 삭제만으로 막대가 찹니다(시안도 폰에서는 감춥니다). */
  .detail-b-bar .saved-note {
    display: none;
  }

  /* 저장하지 않은 변경 알림은 단추 줄 위의 한 줄로 섭니다. 단추 사이에 끼면 375에서 단추가 밀려납니다. */
  .detail-b-bar .actionbar-inner {
    flex-wrap: wrap;
  }

  .detail-b-bar .unsaved-changes {
    order: -1;
    flex-basis: 100%;
    margin-left: 0;
    white-space: normal;
  }

  /* 견적서 알림도 단추 줄 위의 한 줄이고, 처리 폼의 알림 아래에 섭니다(DOM 차례). 막힌 `저장`의 긴 문구는 375에서
     두 줄입니다. */
  .detail-b-bar .estimate-unsaved {
    order: -1;
    flex-basis: 100%;
    margin-left: 0;
    white-space: normal;
  }

  /* 알림 줄로 자란 막대가 Tab으로 옮겨 간 칸과 댓글 단추 밑의 글을 덮지 않게 합니다(2026-10-26 측정, 375). 막대는 알림
     하나면 93px, 견적서 알림이 막혀 두 줄이면 112px, 두 알림이 함께면 140px까지 자랍니다. 비워 두는 자리는 막대 높이에
     24px을, 댓글 단추(`bottom: 80px`)는 막대 위로 16px을 둡니다. 처리 폼의 알림 한 줄만일 때의 댓글 단추는 알림 글이
     짧아 단추에 닿지 않으므로 그대로입니다. */
  html:has(.detail-b-bar .unsaved-changes:not([hidden])),
  html:has(.detail-b-bar .estimate-unsaved:not([hidden])) {
    scroll-padding-bottom: 136px;
  }

  html:has(.detail-b-bar .unsaved-changes:not([hidden])):has(.detail-b-bar .estimate-unsaved:not([hidden])) {
    scroll-padding-bottom: 164px;
  }

  .content:has(> .detail-b-bar .estimate-unsaved:not([hidden])) .chat-fab {
    bottom: 128px;
  }

  .content:has(> .detail-b-bar .unsaved-changes:not([hidden])):has(> .detail-b-bar .estimate-unsaved:not([hidden])) .chat-fab {
    bottom: 156px;
  }

  /* 작업 사진 자리의 파일 칸과 사진의 `삭제`(`삭제 취소`)는 폰에서 누를 자리가 44px입니다. 파일 칸은 위아래
     여백으로 키워 글이 칸 가운데에 섭니다. */
  .work-photo-upload .photo-file {
    min-height: 44px;
    padding-top: 12px;
    padding-bottom: 12px;
  }

  .photo-thumb .photo-delete {
    min-height: 44px;
  }

  /* 저장 전 사진의 `빼기`도 같은 44px입니다. */
  .work-photo-pending-item .button {
    min-height: 44px;
  }

  /* 접수 상세의 첨부 사진 줄은 이름표를 값 위에 쌓습니다(2026-10-21 검토). 이름표 칸(120px)을 두면 오류 줄, 파일 칸,
     붙여넣기 상자가 250px 남짓에 눌립니다. 작업 사진 카드처럼 카드 폭을 다 쓰고, 이름표와 값 사이는 폼 칸(`.field`)과
     같은 4px입니다. 다른 줄은 그대로 두 칸입니다. */
  .detail-row-stack {
    grid-template-columns: minmax(0, 1fr);
    gap: 4px;
  }

  /* 출동 이력은 네 칸이 375에 들지 않아 줄을 카드로 쌓습니다: 첫 줄 현장소장과 방문일시, 둘째 줄
     결과, 셋째 줄 함께 기록. 머리글은 감추고 함께 기록 앞에 칸 이름을 답니다. */
  .history-table,
  .history-table tbody {
    display: block;
  }

  .history-table thead {
    display: none;
  }

  .history-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .history-row:has(+ .history-memo) {
    border-bottom: none;
  }

  /* 방문일시는 표 배치처럼 오른쪽에 붙습니다(`td.num`). */
  .history-table td {
    display: block;
    padding: 0;
    border: none;
  }

  .history-row > :nth-child(1) {
    grid-area: 1 / 1;
  }

  .history-row > :nth-child(4) {
    grid-area: 1 / 2;
  }

  .history-row > :nth-child(2) {
    grid-area: 2 / 1 / 3 / 3;
  }

  .history-row > :nth-child(3) {
    grid-area: 3 / 1 / 4 / 3;
  }

  .history-table td[data-label]::before {
    display: inline;
    content: attr(data-label) " ";
    color: var(--color-text-muted);
    font-size: 13px;
    font-weight: 600;
  }

  .history-memo {
    display: block;
    padding: 0 0 12px;
    border-bottom: 1px solid var(--color-border-soft);
  }

  /* 폰에서는 상태 줄이 두 줄로 나뉘어 신청일 뒤의 `|`가 줄 끝에 홀로 남습니다. 방문 기한이 제 줄로
     내려가므로 `|` 없이도 갈립니다. */
  .dh-meta .dh-sep {
    display: none;
  }
}

/* --- 시설팀 로그인 화면 (결정 기록 0046) ------------------------------------
   매장 로그인(`store.css`의 `.store-login-*`, `.store-field-stacked`)과 같은 짜임입니다.
   가운데 제목, 이름을 위에 둔 칸, 폭을 채운 로그인 단추입니다. 사이드바 틀(`.layout`)을
   쓰지 않는 화면이라 폭을 폰 폭으로 묶어 가운데 세웁니다. 입력칸 글자는 16px입니다 - iOS
   사파리가 그보다 작은 칸을 누르면 화면을 확대하고 돌아오지 않습니다. */

.login-page {
  min-height: 100vh;
  background: var(--color-bg);
}

.login-shell {
  max-width: 420px;
  margin: 0 auto;
  padding: 48px 16px 32px;
}

.login-brand {
  margin: 0 0 24px;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-align: center;
}

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

.login-error {
  margin: 0;
  padding: 12px 16px;
  border: 1px solid var(--color-error);
  border-radius: 10px;
  background: var(--color-card);
  color: var(--color-error);
  font-size: 16px;
}

.login-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--color-card);
}

.login-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px;
  border-radius: 10px;
  background: var(--color-bg);
}

.login-label {
  font-size: 14px;
  color: var(--color-text);
}

.login-input {
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-card);
  font-size: 16px;
}

.login-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 48px;
  border: none;
  border-radius: 10px;
  background: var(--color-primary);
  color: var(--color-primary-text);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

/* --- 비밀번호 변경, 관리자 계정 (결정 기록 0046) ------------------------------ */

/* 칸 셋을 한 줄에 하나씩 쌓습니다. 조건 막대처럼 옆으로 늘어놓으면 어느 칸이 확인란인지
   헷갈립니다. */
.password-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 420px;
}

/* 역할 이름(`루트`, `전체`, `매장 전용`)은 한 줄에 둡니다. 폰 너비에서 표가 넘치면 줄바꿈이
   되는 칸이 이 칸뿐이라 `루`/`트`처럼 한 글자씩 떨어졌습니다. 넘치는 만큼은 표 상자 안에서
   옆으로 넘깁니다(`.ledger-scroll`). */
.col-role {
  width: 110px;
  white-space: nowrap;
}

/* 관리자 계정 표의 `비밀번호 초기화` 링크입니다. 옆(좁으면 아래)에 `잠금 해제` 폼이 오므로
   매장 계정 표의 폼 단추(`.account-form`)와 같은 틈을 둡니다. */
.account-link {
  margin: 0 4px 4px 0;
}

.reset-form {
  display: flex;
  gap: 8px;
  margin-top: 24px;
}

.reset-done {
  margin: 24px 0 8px;
  font-weight: 700;
}

.reset-rule {
  margin: 0 0 8px;
}

/* 관리자 계정 화면 아래의 관리자 추가 폼입니다(결정 기록 0057). 칸은 매장 계정 발급 폼과 같은
   `.filter-row`입니다. */
.admin-add {
  margin-top: 24px;
}

/* 관리자 계정 표입니다. 넓은 화면은 다른 표와 같고, 폰에서는 줄을 카드로 쌓습니다(아래 `max-width:
   720px`) - 네 칸이 375에 들지 않아 아이디가 글자 사이에서 끊기고 `비밀번호 초기화`가 잘렸습니다(QA
   2026-10-01 lane C NIT-9). */
.admin-table .col-account {
  overflow-wrap: normal;
}

@media (max-width: 720px) {
  .admin-table,
  .admin-table tbody {
    display: block;
    min-width: 0;
  }

  .admin-table thead {
    display: none;
  }

  /* 첫 줄은 아이디와 상태 표시가 줄 전체를 쓰고(아이디는 끊지 않습니다), 둘째 줄은 역할과 표시 이름,
     셋째 줄은 단추입니다. */
  .admin-table tr {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "account account"
      "role name"
      "actions actions";
    gap: 4px 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .admin-table th[scope="row"],
  .admin-table td {
    display: block;
    width: auto;
    height: auto;
    padding: 0;
    border: none;
  }

  .admin-table .col-account {
    grid-area: account;
  }

  .admin-table .col-display-name {
    grid-area: name;
  }

  .admin-table .col-role {
    grid-area: role;
  }

  .admin-table .col-actions {
    grid-area: actions;
  }

  .admin-table .col-actions:empty {
    display: none;
  }
}

/* --- 시스템 로그 (설계 2026-10-16 시스템 로그, 계획 7) ------------------------------------- */

/* 관리자 추가 아래의 카드입니다. 검색, 초기화, 쪽 넘기기 뒤에는 이 카드(`#system-log`)로 내려오고 위에 조금
   틈을 둡니다. 거르기 칸은 오피스 목록과 같은 `.filter-row`라 모자라면 줄을 바꾸고, 폰에서는 칸마다 한 줄입니다. */
.system-log {
  margin-top: 24px;
  scroll-margin-top: 16px;
}

.system-log-filters {
  margin-bottom: 16px;
}

/* 칸 너비는 표가 정하고(`table-layout: fixed`), 모자라면 `.ledger-scroll` 안에서만 옆으로 넘깁니다(375, 768).
   내용 칸만 너비가 없어 남는 폭을 갖고, 긴 글은 줄을 바꿉니다. */
.system-log-table {
  table-layout: fixed;
  min-width: 900px;
}

/* 시각 `2026-10-15 10:00`은 한 줄에 다 듭니다(14px, headless Chrome에서 122.4px). 사장님 2026-10-16: 시각이 잘리면
   안 됩니다. 값처럼 머리글도 오른쪽에 붙입니다. `.ledger thead th`의 왼쪽 맞춤이 `th.num`보다 구체적이라 따로 적습니다. */
.system-log-table .log-at {
  width: 152px;
  white-space: nowrap;
}

.system-log-table thead .log-at {
  text-align: right;
}

.system-log-table .log-actor {
  width: 120px;
}

.system-log-table .log-kind {
  width: 120px;
}

.system-log-table .log-store {
  width: 160px;
}

/* 열두 자리 접수번호(링크, 103.2px)가 한 줄에 듭니다. */
.system-log-table .log-receipt {
  width: 136px;
  white-space: nowrap;
}

.system-log-table .log-summary {
  overflow-wrap: anywhere;
}

/* 채운 줄의 `지난 기록` 표입니다. 회색 바탕의 작은 표로 내용 글과 갈립니다(상태 `접수`와 같은 짝). 틈은 글 뒤에서만
   둡니다. 내용이 빈 줄의 표는 다른 줄의 글과 같은 자리에서 시작합니다. */
.system-log-table .tag {
  background: var(--color-status-0-bg);
  color: var(--color-status-0-fg);
}

.system-log-table .log-text + .tag {
  margin-left: 8px;
}

/* 폰에서는 두 날짜를 한 줄에 하나씩 쌓고 `~`를 감춥니다. 375에서 둘을 한 줄에 두면 142px씩인데 날짜 칸은 149.3px보다
   좁으면 글자가 잘립니다(계획 7b 조사). */
@media (max-width: 720px) {
  .system-log-filters .period input {
    flex: 1 1 100%;
  }

  .system-log-filters .period-sep {
    display: none;
  }
}

/* --- 견적서 (결정 기록 0051) -------------------------------------------------------- */

/* 처리 화면의 견적서 카드입니다. 노무비와 자재비 두 묶음의 표, 묶음 소계, 세 합계 줄입니다. 금액은
   오른쪽에 붙고 자릿수 폭을 맞춥니다(`.num`). 저장한 뒤 이 카드(`#estimate`)로 돌아오면 위에 조금
   틈을 둡니다. */
.estimate {
  scroll-margin-top: 16px;
}

.estimate-group + .estimate-group {
  margin-top: 24px;
}

.estimate-group-title {
  margin: 0 0 8px;
  font-size: 16px;
  font-weight: 700;
}

.estimate-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.estimate-table th {
  padding: 0 12px 8px 0;
  border-bottom: 1px solid var(--color-border-soft);
  color: var(--color-text-muted);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}

.estimate-table th:nth-child(2),
.estimate-table th:nth-child(4) {
  width: 144px;
}

.estimate-table th:nth-child(3) {
  width: 96px;
}

.estimate-table th:nth-child(5) {
  width: 72px;
}

.estimate-table td {
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--color-border-soft);
  vertical-align: middle;
}

.estimate-table .num {
  text-align: right;
}

/* 표 안의 칸이라 32px, 14px입니다(`.ledger` 안의 칸과 같습니다). */
.estimate-table input {
  width: 100%;
  height: var(--control-h-sm);
  padding: 0 8px;
  font-size: 14px;
}

.estimate-table td.num input {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* 폰에서만 보이는 칸 이름입니다. 표 배치에서는 머리글이 말합니다. */
.estimate-table td[data-label]::before {
  display: none;
}

.estimate-remove {
  text-align: right;
}

.estimate-table td.estimate-remove {
  padding-right: 0;
}

/* 걸린 줄의 문구는 그 줄 바로 아래에 붙습니다. 두 줄 사이에는 선을 긋지 않습니다. */
.estimate-row:has(+ .estimate-row-error) td {
  border-bottom: none;
}

.estimate-row-error td {
  padding-top: 0;
}

.estimate-error {
  margin: 0;
  color: var(--color-error);
  font-weight: 600;
}

.estimate-form [aria-invalid="true"] {
  border-color: var(--color-error);
  box-shadow: inset 0 0 0 1px var(--color-error);
}

.estimate-group-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
}

.estimate-subtotal {
  margin: 0;
  font-weight: 600;
}

.estimate-subtotal .num {
  margin-left: 8px;
}

.estimate-totals {
  display: grid;
  justify-content: end;
  gap: 4px;
  margin: 24px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
}

.estimate-total-line {
  display: grid;
  grid-template-columns: auto 144px;
  gap: 16px;
}

.estimate-total-line dt {
  color: var(--color-text-muted);
}

.estimate-total-line dd {
  margin: 0;
  text-align: right;
}

.estimate-total-line.is-total {
  font-size: 16px;
  font-weight: 700;
}

.estimate-total-line.is-total dt {
  color: var(--color-text);
}

.estimate-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
}

/* 저장된 줄을 모두 삭제하게 되는 저장을 한 번 묻는 문구입니다(사장님 2026-10-26). `모두 삭제`와 `견적서 저장` 바로
   위에 붙고, 걸린 줄의 문구(`.estimate-error`)와 같은 붉은 글자입니다. */
.estimate-clear-note {
  margin: 16px 0 0;
  color: var(--color-error);
  font-weight: 600;
  text-align: right;
}

/* 견적서를 제자리에서 저장했을 때(사장님 2026-10-26) 카드 안에 서는 알림입니다. 저장했다는 한 줄은 보통 글자, 걸린 저장과
   남이 먼저 고친 저장과 끊긴 연결(`is-error`)은 묻는 문구와 같은 붉은 글자입니다. 단추 줄 바로 위에 붙습니다. 초점을
   받으므로(`tabindex="-1"`) 초점 고리 대신 글자로 알립니다. */
.estimate-message {
  margin: 16px 0 0;
  color: var(--color-text);
  font-weight: 600;
  text-align: right;
}

.estimate-message:focus-visible,
.estimate-clear-note:focus-visible {
  outline: none;
}

.estimate-message p {
  margin: 0;
}

.estimate-message.is-error {
  color: var(--color-error);
}

/* 폰(720px 이하)에서는 다섯 칸이 들지 않아 줄을 카드로 쌓습니다: 첫 줄 항목, 둘째 줄 단가와 수량,
   셋째 줄 금액합계와 빼기. 머리글은 감추고 칸 앞에 이름을 답니다. */
@media (max-width: 720px) {
  .estimate-table,
  .estimate-table tbody {
    display: block;
  }

  .estimate-table thead {
    display: none;
  }

  .estimate-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .estimate-row:has(+ .estimate-row-error) {
    border-bottom: none;
  }

  .estimate-table td {
    display: block;
    padding: 0;
    border: none;
  }

  .estimate-row > .estimate-name {
    grid-column: 1 / -1;
  }

  .estimate-table td[data-label]::before {
    display: block;
    margin-bottom: 4px;
    content: attr(data-label);
    color: var(--color-text-muted);
    font-size: 13px;
    font-weight: 600;
    text-align: left;
  }

  .estimate-row > .estimate-remove {
    align-self: end;
  }

  .estimate-row-error {
    display: block;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--color-border-soft);
  }

  .estimate-group-foot {
    flex-wrap: wrap;
  }

  .estimate-totals {
    justify-content: stretch;
  }

  .estimate-total-line {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  /* 폰에서는 두 줄로 꺾이므로 오른쪽에 붙이지 않고 읽는 차례대로 왼쪽에서 시작합니다. */
  .estimate-clear-note,
  .estimate-message {
    text-align: left;
  }
}

/* --- 자동응답 메시지 변경 (사장님 2026-10-20, 결정 기록 0058 덧붙임) ------------------------- */

/* 방문 일수 칸 줄 아래, 상태 문장 묶음의 제목입니다. 위의 칸 줄과 24px 띄웁니다. */
.status-message-title {
  margin-top: 24px;
}

/* 상태 문장 칸 여덟을 한 줄에 하나씩 쌓습니다. 칸이 줄을 채워 긴 문장도 한눈에 보입니다. */
.status-message-fields {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}

/* 칸 앞의 고정 머리말 `[자동응답]`입니다. 375 폰에서도 줄거나 꺾이지 않고, 칸이 남은 폭을 씁니다. */
.status-message-prefix {
  flex: none;
  white-space: nowrap;
}

/* 걸린 칸 바로 아래의 까닭입니다. 칸이 `aria-describedby`로 가리키는 위 오류 줄과 같은 글입니다. */
.status-message-error {
  margin: 0;
  color: var(--color-error);
  font-weight: 600;
}

/* 폰에서는 머리말을 상태 이름 옆(`공사 [자동응답]`)으로 올리고 칸이 줄을 다 씁니다. 칸 옆에 두면 375에서 칸이
   241px이라 기본 문장(`진행상황이 신청으로 변경되었습니다.`, 257px)의 끝이 잘립니다(2026-10-20 측정). */
@media (max-width: 720px) {
  .status-message-fields .field {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    gap: 4px 8px;
  }

  .status-message-fields .with-quick {
    display: contents;
  }

  .status-message-fields input,
  .status-message-error {
    grid-column: 1 / -1;
  }
}
