/* 디자인 시스템: Modernist (claude.ai/design)
 *
 * 이식 시 바꾼 것 3가지 — 근거는 SPEC §9·§3 제약이다.
 * 1. Archivo 웹폰트를 받지 않는다. 한글 글리프가 없어 한국어는 어차피 시스템 폰트로 폴백한다.
 *    라틴·숫자만을 위해 새벽 느린 회선에서 폰트를 받는 것은 200KB 예산 대비 손해다.
 *    Modernist의 인상은 폰트가 아니라 weight 800 · 라디우스 0 · 강한 괘선이 만든다.
 * 2. 강조색이 적색이라 위험 신호와 겹친다. 위험은 더 어두운 accent-700 + 텍스트 표식으로 분리한다.
 * 3. color-mix()는 구형 안드로이드 WebView·iOS 13에서 무시된다. 정적 폴백을 먼저 쓴다.
 */

:root {
  /* 바탕 */
  --color-bg: #f3f2f2;
  --color-surface: #eae9e9;
  --color-text: #201e1d;

  /* 강조 (브랜드) */
  --color-accent: #ec3013;
  --color-accent-100: #fff2ef;
  --color-accent-200: #ffe0d9;
  --color-accent-600: #dd2b0f;
  --color-accent-700: #ae1800;

  /* 중립 */
  --color-neutral-100: #f8f4f4;
  --color-neutral-200: #eae7e7;
  --color-neutral-300: #d7d3d3;
  --color-neutral-400: #bab6b6;
  --color-neutral-500: #9b9797;
  --color-neutral-600: #7d7979;
  --color-neutral-700: #605d5d;
  --color-neutral-800: #444141;
  --color-neutral-900: #2d2b2b;

  /* 괘선 — color-mix 미지원 브라우저는 위 정적값을 쓴다 */
  --color-divider: #8d8b8a;
  /* 표 세로선은 가로선보다 옅게. 같은 굵기면 격자가 내용보다 튄다 */
  --color-grid: #cfcbcb;
  --color-zebra: #efeded;

  /* 상태 색 — 배지 구분용. 강조색(주문 차단)과 겹치지 않게 고른다 */
  --color-warn: #b06f00;
  --color-warn-bg: #fff4e0;
  --color-warn-ink: #7a4c00;
  --color-ok: #1c6b3a;
  --color-ok-bg: #e6f4ec;
  --color-ok-ink: #14532a;
  --color-muted: var(--color-neutral-700);
  --color-danger: var(--color-accent-700);

  /* 간격 */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;

  /* 모서리 — Modernist는 전부 각지다 */
  --radius-sm: 0;
  --radius-md: 0;
  --radius-lg: 0;

  --shadow-sm: 0 1px 2px rgba(45, 43, 43, 0.14);
  --shadow-md: 0 3px 10px rgba(45, 43, 43, 0.16);

  /* 한국어 시스템 폰트 우선. 웹폰트 다운로드 없음 */
  --font-body: system-ui, -apple-system, "Apple SD Gothic Neo", "Malgun Gothic",
    "Noto Sans KR", sans-serif;
  --font-heading: var(--font-body);
  --font-heading-weight: 800;

  /* 터치 대상 최소 — 한 손 엄지 조작 전제 */
  --tap: 44px;
  --page-max: 900px;
}

@supports (color: color-mix(in srgb, #000 40%, transparent)) {
  :root {
    --color-divider: color-mix(in srgb, #201e1d 40%, transparent);
  }
}

* {
  box-sizing: border-box;
}

/* `hidden` 속성은 display 를 주는 규칙에 밀린다 — flex·grid 를 쓰는 요소를
   숨기려면 이 한 줄이 있어야 한다. 없으면 담기 버튼과 수량 조절이 동시에 보인다. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font: 16px/1.55 var(--font-body);

  /* ── 한국어 조판. 전역이어야 한다 ──────────────────
   *
   * 브라우저 기본값(`word-break: normal`)은 한국어에서 **단어 한가운데를 끊는다.**
   * 실제로 회원가입 화면에서 "사용할 수 없습니다" 가 "없습 / 니다." 로 쪼개졌다.
   * 영어는 공백에서 끊지만 한글은 글자 단위로 끊을 수 있다고 보기 때문이다.
   *
   * `keep-all` 은 단어(어절)를 통째로 넘긴다. 대신 아주 긴 것(URL·주문번호)이
   * 칸을 넘길 수 있으므로 `overflow-wrap` 으로 그때만 예외를 둔다.
   *
   * `text-wrap: pretty` 는 마지막 줄에 한 어절만 남는 것을 줄인다 —
   * "…주문을 시작하실 / 수 있습니다." 처럼 3글자만 덜렁 남던 문제.
   * 지원하지 않는 브라우저는 그냥 무시한다(있으면 좋고 없으면 그만).
   */
  word-break: keep-all;
  overflow-wrap: break-word;
  text-wrap: pretty;
}

/* ── 헤더 ─────────────────────────────────────────── */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 2px solid var(--color-text);
  background: var(--color-bg);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
  color: var(--color-text);
  text-decoration: none;
}

/* 상단 로고. `width`/`height` 를 속성으로도 적어 두면 이미지가 도착하기 전에
   자리를 잡는다 — 안 그러면 글자가 한 번 밀린다(레이아웃 이동).

   `multiply` 로 흰 배경을 지운다 — 로고 원본이 흰 바탕이라 테두리를 두르면
   상단에 네모난 상자가 하나 떠 있는 것처럼 보인다. 배경(#f3f2f2)이 밝아서
   흰색이 그대로 사라진다. 랜딩의 큰 로고도 같은 처리다. */
.brand__logo {
  width: 32px;
  height: 32px;
  object-fit: cover;
  mix-blend-mode: multiply;
}

/* ── 랜딩 ──────────────────────────────────────────
 * claude.ai/design 「하루식자재센터 랜딩페이지」 를 이 앱에 이식한 것.
 *
 * 점주는 카카오톡 링크를 눌러 들어온다. 그 순간 **어느 마트인지**가 먼저
 * 읽혀야 한다 — 다른 마트와도 거래하는 점주라면 더 그렇다.
 *
 * 원안에서 바꾼 것 3가지, 근거와 함께:
 * 1. Google Fonts(Archivo)를 받지 않는다. CDN 금지(SPEC §3)이고 Archivo 에는
 *    한글 글리프가 없어 어차피 시스템 폰트로 폴백한다. Modernist 의 인상은
 *    폰트가 아니라 weight 800 · 라디우스 0 · 강한 괘선이 만든다.
 * 2. CTA 글자를 17px → 19px 로 키웠다. 브랜드 적색 위 흰 글씨는 4.20:1 이라
 *    일반 텍스트 기준(4.5)에 미달한다. **굵은 18.66px 이상이면 "큰 글씨"가
 *    되어 기준이 3:1 로 내려가고 통과한다.** 원안의 "버튼 크게" 의도와도 맞는다.
 * 3. 좌우 여백은 `.page` 를 그대로 쓴다 — 랜딩만 다른 격자를 쓰면 로그인으로
 *    넘어갈 때 화면이 흔들린다.
 *
 * `--leading: 28px` 리듬과 좌측 정렬은 원안 그대로다.
 */

.landing {
  --leading: 28px;
  /* 40ch 는 한국어에서 너무 넓다 — `ch` 는 「0」 폭 기준이라 한글은 두 배 가깝다
     (`.hint` 에서 겪은 것과 같은 함정). em 으로 잡는다. */
  --measure: 24em;
  padding-block: var(--space-6) var(--space-8);
}

/* 흰 배경 로고를 종이 위에 얹은 것처럼 붙인다 — 네모난 흰 상자가 사라진다 */
.landing__logo {
  display: block;
  height: clamp(80px, 18vw, 128px);
  width: auto;
  margin: 0 0 calc(1.5 * var(--leading));
  mix-blend-mode: multiply;
}

.landing__display {
  margin: 0;
  font-size: clamp(38px, 8vw, 72px);
  line-height: 1.14;
  letter-spacing: -0.02em;
  /* 한국어는 단어 중간에서 끊으면 읽기 어렵다 */
  word-break: keep-all;
}

/* 랜딩의 「한 줄로 서야 하는 것」. 제목과 안내 문단이 같은 방식을 쓴다 —
   따로 두면 줄 간격이 갈린다. 좁은 화면에서 문장 중간이 접혀
   「…넣으실 수 / 있습니다. 휴대폰으로…」 로 읽히는 것을 막는다. */
.landing__display .line,
.landing__lead .line {
  display: block;
}

/* 대형 표제 전용 강조 — 3.76:1 로 큰 글씨 기준(3:1)을 넘는다.
   본문 크기에 이 색을 쓰면 안 된다. */
.landing__display .accent {
  color: var(--color-accent);
}

.landing__lead {
  margin: var(--leading) 0 0;
  max-width: var(--measure);
  font-size: 18px;
  line-height: var(--leading);
  color: var(--color-text);
  word-break: keep-all;
}

.landing__note {
  margin: var(--leading) 0 0;
  max-width: var(--measure);
  font-size: 15px;
  line-height: var(--leading);
  color: var(--color-neutral-700);
  word-break: keep-all;
}

.landing .auth-actions {
  margin-top: calc(1.5 * var(--leading));
}

/* 원안의 `.btn-cta`. 19px 인 근거는 이 블록 맨 위 주석 2번. */
.landing .auth-actions > * {
  min-height: 52px;
  padding: 14px 28px;
  font-size: 19px;
}

/* 섹션 경계 — Modernist 의 2px 룰 */
.landing__rule {
  height: 2px;
  border: 0;
  margin: calc(2 * var(--leading)) 0 0;
  background: var(--color-divider);
}

.landing__foot {
  margin: calc(1.5 * var(--leading)) 0 0;
  font-size: 14px;
  line-height: var(--leading);
  color: var(--color-neutral-700);
}

@media (max-width: 720px) {
  /* 버튼은 화면 폭을 채운다.
     라벨은 **가운데**다 — 원안은 왼쪽 정렬이었는데, 폭을 꽉 채운 단추에서
     글자만 왼쪽에 붙으면 오른쪽이 텅 비어 "눌리는 데가 여기까지인가" 가
     흐려진다. 글자가 가운데 있어야 단추 전체가 대상으로 읽힌다. */
  .landing .auth-actions {
    flex-direction: column;
    align-items: stretch;
  }

  .landing .auth-actions > * {
    flex: 0 0 auto;
    justify-content: center;
    text-align: center;
  }
}

.topnav {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* 상단 링크도 탭 대상이다. 글자 높이(22px)만으로는 절반도 안 된다 —
   세로로 눌리는 면적을 --tap 까지 올린다. 글자 크기는 그대로 둔다.
   `button.linkish` 가 뒤에서 min-height:auto 로 덮으므로 클래스를 함께 적어 이긴다. */
.topnav a,
.topnav button,
.topnav button.linkish {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
}

.whoami {
  font-weight: 700;
}

.nav-form {
  display: inline;
  margin: 0;
}

.page {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-8);
}

/* 열이 많은 관리 표 화면 — 900px 는 읽기 좋은 글 폭이지 표 폭이 아니다.
   좁혀 두면 마지막 열이 잘려 사장님이 매번 옆으로 밀어야 한다. */
.page--wide {
  max-width: 1280px;
}

/* ── 타이포 ───────────────────────────────────────── */

h1,
h2,
h3 {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
  line-height: 1.2;
}

h1 {
  font-size: 1.6rem;
  margin: 0 0 var(--space-3);
}

h2 {
  font-size: 1.15rem;
  margin: var(--space-8) 0 var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-divider);
}

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

.muted {
  color: var(--color-muted);
}

.hint {
  margin: var(--space-1) 0 0;
  font-size: 0.85rem;
  /* 폭 전체로 퍼지면 읽는 눈이 줄을 놓친다. 그래서 줄 길이를 제한한다.
     **`ch` 를 쓰면 안 된다** — `ch` 는 숫자 「0」의 폭이고 한글 글자는 그보다
     두 배 가까이 넓다. `62ch` 는 한국어에서 31자쯤에서 끊겨, 표가 1280px 인
     화면에서 안내문만 520px 로 좁아 보였다. `em` 은 글자 크기 기준이라
     한글 45자 정도로 읽힌다. */
  max-width: 45em;
}

/* 버튼 뒤에 붙는 안내는 줄을 바꿔 놓는다 */
.hint--after {
  display: block;
  margin-top: var(--space-2);
}

/* 안내용 목록. 글머리 기호 없이 줄만 나눈다 — 두 기종 안내처럼 "한 문장으로
   이으면 세 줄이 되고 마지막 줄에 세 글자만 남는" 글에 쓴다. `<ul>` 로 두는
   것은 화면 읽기 프로그램에 "항목 둘"이라고 알려 주기 위해서다. */
.plain-list {
  margin: var(--space-1) 0 0;
  padding-left: 0;
  list-style: none;
}

.plain-list > li + li {
  margin-top: 2px;
}

/* 사진 열 — 자리를 고정해 행 높이를 고르게 유지한다 */
/* 선택 칸. 전에는 `padding-right: 0` 이라 가로로 왼쪽에 치우쳤다 — 지웠다.
   세로 정렬은 `th, td` 의 `vertical-align: middle` 이 이미 맡는다. */
.check-cell {
  width: 1%;
}

/* 체크박스는 16px 인데 행 높이는 48px 이상이다(사진 칸이 정한다).
   **`display: block` 이 실제 수정이다** — 인라인으로 두면 글자 baseline 아래
   여유 공간이 딸려 와서 `vertical-align: middle` 로도 3px 위로 치우친다(실측).
   블록이면 그 공간이 사라진다.

   크기는 20px 로 올려 겨냥을 쉽게 한다. `margin: 0 auto` 는 칸이 넓어질 때를
   위한 보험이다 — 지금은 `width: 1%` 라 없어도 가운데다. */
.check-cell input[type="checkbox"] {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto;
  cursor: pointer;
}

/* 선택 일괄 처리 막대 — 200개 목록에서 여러 품목을 한 번에 내리고 올린다 */
.bulkbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--color-divider);
  background: var(--color-surface);
}

.bulkbar__label {
  font-weight: 700;
  margin-right: var(--space-1);
}

.bulkbar button {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-text);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  font-weight: 700;
  cursor: pointer;
}

.bulkbar button:hover {
  background: var(--color-text);
  color: var(--color-bg);
}

.photo-cell {
  width: 1%;
  white-space: nowrap;
}

.photo-cell > a {
  display: flex;
  align-items: center;
  min-height: 48px;
}

.req {
  color: var(--color-accent);
}

/* ── 메시지 ───────────────────────────────────────── */

.messages {
  list-style: none;
  margin: 0 auto var(--space-4);
  padding: 0 var(--space-4);
  max-width: var(--page-max);
}

/* 안내가 없어도 자리는 남겨 둔다 — htmx 가 그 자리를 찾아 갈아끼워야 하기
   때문이다. 빈 채로 여백을 먹으면 안 되므로 비었을 때만 감춘다. */
.messages:empty {
  display: none;
}

.msg {
  padding: var(--space-3);
  border: 1px solid var(--color-divider);
  border-left: 4px solid var(--color-text);
  background: var(--color-neutral-100);
  margin-bottom: var(--space-2);
}

.msg--error {
  border-left-color: var(--color-danger);
  background: var(--color-accent-100);
  color: var(--color-danger);
  font-weight: 700;
}

.msg--success {
  border-left-color: var(--color-accent);
}

.notice {
  margin: var(--space-4) 0;
  padding: var(--space-4);
  border: 2px solid var(--color-text);
  background: var(--color-neutral-100);
}

.notice code.temp-password {
  font-size: 1.6rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  font-variant-numeric: tabular-nums;
}

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

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-width: 420px;
}

.stack label {
  font-weight: 700;
  margin-top: var(--space-3);
}

input,
select,
textarea {
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-neutral-700);
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--color-text);
  font: inherit;
}

/* 체크박스·라디오는 44px 규칙에서 뺀다 — 늘리면 거대한 빈 사각형이 된다.
   탭 영역은 라벨이 담당한다(.check-row). */
input[type="checkbox"],
input[type="radio"] {
  min-height: auto;
  width: 22px;
  height: 22px;
  padding: 0;
  margin: 0;
  accent-color: var(--color-accent);
}

/* 체크박스 + 라벨을 한 줄로 묶어 탭 영역을 44px 확보한다 */
.check-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap);
}

input:focus,
select:focus,
textarea:focus,
button:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 1px;
}

.field-error {
  margin: var(--space-1) 0 0;
  color: var(--color-danger);
  font-size: 0.9rem;
  font-weight: 700;
}

/* ── 버튼 ─────────────────────────────────────────── */

button {
  font: inherit;
  min-height: var(--tap);
}

button.primary,
.primary-link {
  display: inline-block;
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-6);
  /* 배경은 `--color-accent` 가 아니라 600 이다. 브랜드 적색 #ec3013 위 흰 글씨는
     **4.20:1** 이라 16px 굵은 글씨에서 WCAG AA(4.5) 에 미달한다 — Lighthouse 가
     로그인·가입 화면에서 실제로 잡았다. 600(#dd2b0f) 은 4.74:1 이고 눈으로는
     거의 같은 색이다. 새 색을 만들지 않고 팔레트 안에서 해결한다.
     「큰 글씨」(굵은 18.66px 이상) 예외를 받는 랜딩 표제는 accent 를 그대로 쓴다. */
  border: 2px solid var(--color-accent-600);
  border-radius: var(--radius-sm);
  background: var(--color-accent-600);
  color: #fff;
  font-weight: 800;
  text-decoration: none;
  cursor: pointer;
}

button.primary:hover,
.primary-link:hover {
  /* 눌리는 느낌을 주려면 기본보다 더 어두워야 한다 — 700 은 7.17:1 이다 */
  background: var(--color-accent-700);
  border-color: var(--color-accent-700);
}

/* 로그인 옆에 서는 회원가입. 같은 크기·같은 무게로 보이되 주된 동작은 로그인이므로
   채우지 않고 테두리만 준다 — 둘 다 빨갛게 채우면 어디를 눌러야 할지 흐려진다. */
.secondary-link {
  display: inline-block;
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-6);
  border: 2px solid var(--color-text);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font-weight: 800;
  text-decoration: none;
  cursor: pointer;
}

.secondary-link:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

/* 로그인·회원가입 두 버튼. 넓으면 나란히, 좁으면 아래로 떨어진다 —
   미디어 쿼리 없이 `flex-basis` 가 알아서 접는다. */
/* 본문 폭을 그대로 쓴다. 좁게 묶어 두면 넓은 화면에서 둘 다 왼쪽에 몰려
   가운데가 텅 빈다 — 두 버튼이 좌우 끝까지 반씩 나눠 갖게 한다. */
.auth-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* 요소까지 적는다 — `button.primary`(0,1,1) 가 `.auth-actions > *`(0,1,0) 를 이겨서
   버튼만 규칙이 안 먹고 높이가 어긋났다. 선택자 특정도를 맞춰야 둘 다 적용된다. */
.auth-actions > button,
.auth-actions > a {
  /* 폭을 똑같이 반씩 나눠 갖는다. basis 를 0 으로 둬야 글자 길이가 달라도
     ("로그인" 3자 vs "회원가입" 4자) 폭이 어긋나지 않는다 — auto 면 글자 수가 섞인다. */
  flex: 1 1 0;
  /* 버튼과 링크는 기본 상자가 다르다(button 만 min-height 를 받는다).
     둘 다 같은 flex 상자로 맞춰야 나란히 섰을 때 같은 크기로 보인다. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  margin-top: 0;
  padding: var(--space-3) var(--space-6);
  text-align: center;
}

/* 휴대폰에서는 위아래로 쌓는다. 폭이 아슬아슬하게 맞아 나란히 서면 둘 다 좁아져
   장갑 낀 손으로 누르기 어려워진다 — 세로로 두면 각자 화면 폭을 다 쓴다. */
@media (max-width: 30rem) {
  .auth-actions {
    flex-direction: column;
  }

  /* 세로로 서면 flex-grow 가 높이를 늘린다 — 버튼 하나가 화면 절반을 먹는다.
     방향이 바뀌면 basis·grow 도 '높이'를 뜻하게 되므로 다시 잡아 준다. */
  .auth-actions > button,
  .auth-actions > a {
    flex: 0 0 auto;
  }
}

/* 표 안의 작은 동작 버튼 — 밑줄 링크로 두면 '진짜 링크'와 구분이 안 되고,
   빨간 밑줄 텍스트가 표 전체를 어수선하게 만든다. 눌리는 것처럼 보이게 한다. */
.mini-btn {
  min-height: 32px;
  padding: 4px var(--space-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
}

.mini-btn:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

/* 되돌리려면 손이 가는 동작(주문 취소·입금 삭제). 색만으로 신호하지 않는다 —
   테두리를 굵혀 무게를 함께 준다. 흑백에서도 다르게 보인다. */
/* 위험한 동작(취소·지우기). **테두리 굵기는 옆 단추와 같게 둔다** —
   1px 옆에 2px 을 세우면 두 단추가 어긋나 보인다. 위험은 글자색과
   낱말(「지우기」)로 알리고, 실제 방어는 확인 대화상자다. */
.mini-btn--danger {
  border-color: var(--color-neutral-600);
  color: var(--color-danger);
}

.mini-btn--danger:hover {
  border-color: var(--color-danger);
  background: var(--color-accent-100);
}

/* 링크로 쓰는 mini-btn — 버튼과 같은 모양이어야 같은 무게로 읽힌다 */
a.mini-btn {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

/* 표의 처리 칸. **가로로 선다.**

   전에는 `white-space: nowrap` 만 있었다. 그런데 `<form>` 으로 감싼 단추
   (「지우기」·「취소」)는 폼이 블록이라 줄이 바뀌어, 옆 단추 **바로 아래에
   간격 0 으로 겹쳐** 붙었다(실측).

   **`<td>` 에 `display: flex` 를 주면 안 된다.** 그 칸이 `table-cell` 을
   잃어 표의 열에서 빠지고, 실제로 마지막 열이 표 밖으로 튀어나왔다.
   칸은 그대로 두고 **안의 것들을 인라인으로** 만든다. */
.actions-cell {
  white-space: nowrap;
}

/* 폼으로 감싼 단추도 옆에 서게 한다 — 블록이면 줄이 바뀐다 */
.actions-cell .row-form {
  display: inline-flex;
}

/* 두 번째 선택자가 필요하다 — `.row-form { margin: 0 }` 이 파일에서 뒤에 있고
   같은 (0,1,0) 이라 첫 줄을 이긴다. 클래스 둘로 좁혀 되찾는다.
   없으면 공지 목록만 간격이 4px 이고 품목 목록은 12px 이라 화면마다 달라 보인다. */
.actions-cell > * + *,
.actions-cell > * + .row-form {
  margin-left: var(--space-2);
}

/* 품목명 — 상세로 가는 링크지만 표에서는 제목처럼 읽혀야 한다 */
.row-title {
  color: var(--color-text);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 1px solid var(--color-grid);
}

.row-title:hover {
  color: var(--color-accent-700);
  border-bottom-color: var(--color-accent);
}

.row-sub {
  display: block;
  color: var(--color-muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

/* 사진 칸 — 없을 때 '사진 등록' 링크 대신 눌러서 넣는 자리로 보이게 */
.thumb-link {
  display: inline-block;
  text-decoration: none;
}

.thumb--empty {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border: 1px dashed var(--color-divider);
  color: var(--color-muted);
  font-size: 1.2rem;
}

.thumb-link:hover .thumb--empty {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

/* 단가 — 칸 안에 '원'을 붙여 무슨 숫자인지 바로 읽히게 한다 */
.money-input {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.money-unit {
  color: var(--color-muted);
  font-size: 0.85rem;
}

/* ── 장바구니 ─────────────────────────────────────── */

/* 담기 — 새벽에 장갑 낀 손으로 누른다. 목록에서 가장 큰 대상이어야 한다 */
.add-btn {
  min-width: 4.5rem;
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border: 2px solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-accent-700);
  font-weight: 800;
  cursor: pointer;
}

.add-btn:hover {
  background: var(--color-accent-600);
  color: #fff;
}

/* 수량 조절 — [−] 숫자 [+]. 세 대상이 붙어 있으면 오탭이 난다 */
.stepper {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.stepper__btn {
  width: var(--tap);
  min-height: var(--tap);
  border: 2px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font-size: 1.2rem;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
}

.stepper__btn:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

.stepper__qty {
  width: 3.5rem;
  min-height: var(--tap);
  padding: var(--space-1);
  border: 2px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font: inherit;
  font-size: 1rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.cart-row__unit {
  margin-left: var(--space-1);
  color: var(--color-muted);
  font-size: 0.9rem;
}

/* 담은 것 요약 — 화면 아래에 붙어 따라온다.
   200품목을 스크롤하다 장바구니로 가려고 맨 위까지 올라가지 않아도 된다. */
.cartbar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin: var(--space-6) calc(-1 * var(--space-4)) 0;
  padding: var(--space-3) var(--space-4);
  border-top: 3px solid var(--color-text);
  background: var(--color-surface);
}

.cartbar__sum {
  font-variant-numeric: tabular-nums;
}

.cartbar__go {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--space-2) var(--space-5);
  border: 2px solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-accent-600);
  color: #fff;
  font-weight: 800;
  text-decoration: none;
  white-space: nowrap;
}

.cart-list {
  margin: var(--space-4) 0;
  padding: 0;
  list-style: none;
}

.cart-row {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-divider);
}

.cart-row--blocked {
  background: var(--color-accent-100);
}

.cart-row__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.cart-row__name {
  margin: 0;
  font-weight: 800;
}

.cart-row__remove {
  min-height: 32px;
  padding: 2px var(--space-2);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-muted);
  font-size: 0.85rem;
  cursor: pointer;
}

.cart-row__remove:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

.cart-row__body {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.cart-row__money {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.cart-row__blocked {
  margin: var(--space-2) 0 0;
}

.cart-empty {
  padding: var(--space-8) 0;
  color: var(--color-muted);
  text-align: center;
}

.cart-total {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 2px solid var(--color-text);
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
}

/* ── 그 자리 수정 ─────────────────────────────────── */

/* 평소에는 그냥 값이다. 고칠 수 있다는 것만 옅게 알린다 —
   200행이 전부 입력칸이면 어디를 고쳐야 할지 눈이 쉬지 못한다. */
.cell-edit {
  display: inline-block;
  min-width: 2.5rem;
  padding: 2px var(--space-1);
  border-bottom: 1px dashed transparent;
  cursor: text;
}

.cell-edit:hover,
.cell-edit:focus-visible {
  border-bottom-color: var(--color-divider);
  background: var(--color-accent-100);
}

.cell-edit--open {
  padding: 0;
  border-bottom-color: transparent;
  background: none;
}

.cell-edit--saving {
  opacity: 0.5;
}

/* 저장 실패 — 값이 조용히 사라지면 고쳐진 줄 알고 넘어간다 */
.cell-edit--error {
  background: var(--color-accent-200);
  border-bottom: 2px solid var(--color-accent);
}

.cell-input {
  width: 100%;
  min-width: 4rem;
  min-height: 32px;
  padding: 2px var(--space-1);
  border: 2px solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font: inherit;
  font-variant-numeric: tabular-nums;
}

.num .cell-input {
  text-align: right;
}

.cell-empty {
  color: var(--color-muted);
  font-style: normal;
}

.cell-unit {
  margin-left: 1px;
  color: var(--color-muted);
  font-size: 0.85rem;
}

/* 단가 일괄 수정 모드 — 켜야만 입력칸이 나온다 */
.bulk-only {
  display: none;
}

body.bulk-price-mode .bulk-only {
  display: block;
}

body.bulk-price-mode .price-cell .cell-view {
  display: none;
}

.bulk-toggle {
  margin-top: var(--space-4);
}

kbd {
  padding: 1px 5px;
  border: 1px solid var(--color-divider);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  font-family: var(--font-body);
  font-size: 0.85em;
  font-weight: 700;
}

/* 재고 — 현재 값이 든 칸을 바로 고친다. 빈 '맞출 값' 칸을 따로 두지 않는다 */
.stock-edit {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.stock-input {
  width: 4.5rem;
  min-height: var(--tap);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.stock-warn {
  display: block;
  color: var(--color-accent-700);
  font-weight: 700;
}

/* 엑셀 내려받기 — 보조 동작이라 테두리만. 확정 버튼의 강조를 뺏으면 안 된다 */
.download-link {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  min-height: var(--tap);
  border: 2px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font-weight: 700;
  text-decoration: none;
}

.download-link:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

button.linkish {
  min-height: auto;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-accent-700);
  text-decoration: underline;
  cursor: pointer;
}

/* ── 표 ───────────────────────────────────────────── */

/* 넓은 표는 자기 컨테이너 안에서만 옆으로 스크롤한다 — 본문은 가로 스크롤 없음 */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

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

th,
td {
  padding: var(--space-2) var(--space-3);
  /* 가로선만으로는 200행에서 어느 값이 어느 열인지 눈이 놓친다.
     세로선까지 넣어 칸을 명확히 나눈다 — 사장님이 단가를 훑는 화면이다. */
  border-bottom: 1px solid var(--color-divider);
  border-right: 1px solid var(--color-grid);
  text-align: left;
  vertical-align: middle;
}

th:last-child,
td:last-child {
  border-right: 0;
}

thead th {
  border-bottom: 2px solid var(--color-text);
  background: var(--color-surface);
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* 한 줄 건너 옅은 배경 — 눈이 가로로 따라갈 때 줄을 놓치지 않는다 */
tbody tr:nth-child(even) {
  background: var(--color-zebra);
}

tbody tr:hover {
  background: var(--color-accent-100);
}

.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 상태·동작 열은 줄바꿈되면 링크가 두 동작처럼 보인다 */
td:last-child,
th:last-child {
  white-space: nowrap;
}

/* 품목 표 열 배분 — 그냥 두면 브라우저가 넓은 열(재고 입력)에 자리를 몰아주고
   "포기"가 "포/기"로 쪼개진다. 짧은 열은 내용에 맞추고 품목명에 여유를 준다. */
.product-table {
  table-layout: auto;
}

.product-table th,
.product-table td {
  white-space: nowrap;
}

.product-table .name-cell {
  white-space: normal;
  min-width: 9rem;
}

.product-table .check-cell,
.product-table .photo-cell {
  width: 1%;
}

tr.row--off td {
  color: var(--color-neutral-600);
}

.row-form {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin: 0;
}

.hidden-form {
  display: none;
}

/* ── 배지·상태 ────────────────────────────────────── */

.badge {
  display: inline-block;
  /* 배지 뒤에 바로 글자가 오면 공백 하나(실측 4.8px)만 벌어져 붙어 보인다 —
     25.6px 제목 옆에서는 「중요8월」 처럼 한 낱말로 읽혔다.
     flex 로 늘어선 배지는 `gap` 이 이미 띄우므로 아래에서 되돌린다. */
  margin-right: var(--space-2);
  padding: 2px var(--space-2);
  border: 1px solid var(--color-neutral-700);
  border-radius: var(--radius-sm);
  background: var(--color-neutral-200);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--color-neutral-800);
  white-space: nowrap;
}

/* 상태별 구분 — 세 배지가 같은 회색이면 훑을 때 구분이 안 된다.
   색만으로 신호하지 않는다: 글자가 이미 사유를 말하고, 테두리 굵기도 함께 다르다.
   (색각 이상·흑백 인쇄에서도 읽혀야 한다) */
/* `gap` 이 이미 띄우는 자리에서는 margin 을 걷는다 — 두 배가 되면 배지만 떠 보인다 */
.group > summary .badge,
.cancelled > summary .badge,
.totals > summary .badge,
.actions-cell .badge {
  margin-right: 0;
}

.badge--blocked {
  border-width: 2px;
  border-color: var(--color-accent);
  background: var(--color-accent-200);
  color: var(--color-accent-700);
}

.badge--pending {
  border-width: 2px;
  border-style: dashed;
  border-color: var(--color-warn);
  background: var(--color-warn-bg);
  color: var(--color-warn-ink);
}

.badge--off {
  border-style: dotted;
  background: transparent;
  color: var(--color-neutral-700);
}

.badge--ok {
  border-color: var(--color-ok);
  background: var(--color-ok-bg);
  color: var(--color-ok-ink);
}

/* 주의를 끌지만 막지는 않는 표시 (취합의 「비고」). `--pending` 과 팔레트는 같고
   테두리가 실선이다 — 점선은 "아직 안 끝났다", 실선은 "읽어 볼 것"을 뜻한다. */
.badge--warn {
  border-color: var(--color-warn);
  background: var(--color-warn-bg);
  color: var(--color-warn-ink);
}

/* 음수 재고 — 색만으로 신호하지 않는다. 템플릿이 "(확인 필요)"를 함께 쓴다 */
.stock--low {
  color: var(--color-danger);
  font-weight: 800;
}

/* ── 단가 인라인 입력 (A-01) ──────────────────────── */

input.price-input {
  width: 8ch;
  min-height: var(--tap);
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

input.input--error {
  border: 2px solid var(--color-danger);
  background: var(--color-accent-100);
}

/* ── 사진 ─────────────────────────────────────────── */

img.thumb {
  width: 48px;
  height: 48px;
  object-fit: cover;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  display: block;
}

img.photo-preview {
  max-width: 100%;
  height: auto;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
}

.menu {
  padding-left: var(--space-4);
}

.menu li {
  margin-bottom: var(--space-2);
}

/* 검색 (서버 GET) */
.searchbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.searchbar input[type="search"] {
  flex: 1 1 220px;
  min-width: 0;
}

.searchbar button {
  padding: var(--space-2) var(--space-4);
  border: 2px solid var(--color-text);
  border-radius: var(--radius-sm);
  background: var(--color-text);
  color: var(--color-bg);
  font-weight: 700;
  cursor: pointer;
}

/* ── 마감 배지 (점주 상단) ────────────────────────
 * 200품목을 스크롤하는 동안에도 사라지지 않아야 하는 단일 정보.
 */

.cutoff {
  display: inline-block;
  padding: 2px var(--space-2);
  border: 1px solid var(--color-neutral-700);
  border-radius: var(--radius-sm);
  font-size: 0.8rem;
  font-weight: 700;
  white-space: nowrap;
}

.cutoff--soon {
  border-color: var(--color-accent);
  background: var(--color-accent-200);
  color: var(--color-accent-700);
}

.cutoff--closed {
  border-color: var(--color-neutral-800);
  background: var(--color-neutral-800);
  color: var(--color-bg);
}

/* ── 점주 1열 행 목록 (P-02 품목 · 주문 내역 · 공지) ────────────────────────
 * 카드 그리드가 아니라 1열 행 목록이다 — 200개에서 세로 스크롤 한 방향으로만 움직인다.
 *
 * 이름은 품목에서 왔지만 **점주가 보는 목록 세 화면이 다 이것을 쓴다.**
 * 공지 화면만 `.list` 라는 별도 이름을 쓰다가, 그 이름의 규칙이 없어
 * 브라우저 기본 들여쓰기 40px 이 그대로 먹었다. 이름이 하나여야 어긋나지 않는다.
 */

.catalog {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 2px solid var(--color-text);
}

.item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 72px;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-divider);
}

.item--blocked {
  color: var(--color-neutral-700);
}

.item__photo {
  flex: 0 0 56px;
}

.item__noimg {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border: 1px solid var(--color-divider);
  background: var(--color-neutral-200);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.2rem;
  color: var(--color-neutral-600);
}

.item__photo img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border: 1px solid var(--color-divider);
  display: block;
}

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

.item__name {
  margin: 0;
  font-weight: 700;
  font-size: 1rem;
}

.item__meta {
  margin: 2px 0 0;
  font-size: 0.9rem;
}

.item__price {
  font-variant-numeric: tabular-nums;
}

/* 취합 화면 비고 — 놓치면 배송 사고가 난다. 본문보다 눈에 띄어야 한다 */
.rollup-note {
  display: inline-block;
  padding: 2px var(--space-2);
  border-left: 3px solid var(--color-accent);
  background: var(--color-accent-100);
  font-size: 0.9rem;
}

/* 취합 화면 추가 금액 입력 칸. 모달이 아니라 행 안의 폼이다 — 이 화면에는
   `extra_scripts` 블록이 없어 modal.js 를 못 싣는다.

   **높이를 32px 로 못 박는다.** 화면 전역 규칙 `input { min-height: var(--tap) }`
   때문에 입력이 44px 인데 옆의 `.mini-btn` 은 32px 이라, 같은 줄에서 12px 이
   어긋나 저장 단추가 떠 보였다. 표 안에서는 행 리듬(32px)이 기준이다 —
   같은 줄의 「확정」·「취소」도 32px 이다.

   손가락 목표는 좁은 화면에서 지킨다. 아래 `@media (max-width: 700px)` 에서
   `.mini-btn` 이 --tap 으로 올라갈 때 입력도 함께 올린다. */
.extra-cell__amount,
.extra-cell__note {
  height: 32px;
  min-height: 32px;
  padding-block: 0;
}

.extra-cell__amount {
  /* 상한은 `-2,147,483,647` 로 14자다. 9ch 였을 때 일곱 자리부터 잘려
     사장님이 자기가 넣은 값을 못 읽었다(실측: 1,250,000 이 104px, 칸은 89px).
     `ch` 는 숫자 「0」의 폭이라 여기서는 글자 수와 거의 맞는다 — 쉼표는 더 좁다. */
  width: 15ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.extra-cell__note {
  width: 18ch;
}

.extra-cell .row-form {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  /* 좁으면 세로로 쌓인다 — 한 줄로 밀어붙이면 사유 칸이 표 밖으로 나간다 */
  flex-wrap: wrap;
}

/* 휴대폰에서는 이 표가 통째로 가로 스크롤된다(`.scroll-x`). 사유 칸은 좁혀
   스크롤 거리를 줄인다 — 글자는 칸 안에서 다시 스크롤되고, 한 글자가 잘려도
   뜻이 무너지지 않는다.

   **금액 칸은 좁히지 않는다.** 한때 7ch 로 줄였는데 `-1,234,567` 이 잘려
   사장님이 자기가 넣은 금액을 못 읽었다. 숫자는 한 자리가 가려지면 값이
   달라진다 — 읽히는 것이 스크롤 길이보다 우선한다. */
@media (max-width: 700px) {
  .extra-cell__note {
    width: 11ch;
  }
}

/* 점주 주문 화면의 공지 배너. 중요 공지만 여기 온다 — 주문하러 들어온
   화면이라 여러 줄이 쌓이면 정작 주문을 못 한다. */
.notice-banner {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--color-accent);
  border-left-width: 4px;
  background: var(--color-accent-100);
}

/* 상자 제목. `h2` 기본값은 문서 속 절 제목용이라 위 여백 `--space-8` 과
   아래 구분선이 붙는다 — 작은 상자 안에서는 제목이 아래로 밀리고 선이 겹친다.
   여백과 선만 걷고 **크기는 시스템 값(1.15rem)을 그대로 쓴다**. 라벨이라고
   작게 줄이면 이 앱이 잡은 16px 바닥이 무너진다 — 점주는 40~60대다.

   **색은 본문색이다. 강조색을 쓰면 안 된다.** 붉게 하면 아래 공지 제목(링크,
   붉은 밑줄)과 한 덩어리로 뭉쳐 라벨과 메시지가 안 갈린다 — 두 변형을 나란히
   찍어 확인했다. 게다가 이 화면엔 이미 붉은 것이 셋(로그아웃·담기·링크)이라
   넷째가 붙으면 붉은색이 「여기를 누르라」는 뜻을 잃는다.
   상자가 중요하다는 것은 왼쪽 4px 강조 테두리와 배경색이 이미 말한다. */
.notice-banner__title {
  margin: 0 0 var(--space-2);
  padding-bottom: 0;
  border-bottom: 0;
  color: var(--color-text);
}

.notice-banner__item {
  margin: 0;
  font-weight: 700;
}

.notice-banner__item + .notice-banner__item {
  margin-top: var(--space-2);
}

/* 공지 본문 — 사장님이 친 줄바꿈을 살린다 */
.notice-body {
  max-width: 45em;
  line-height: 1.7;
}

/* 품목 총합 구역. 새벽에 제일 먼저 보는 숫자라 거래처별 집계 위에 둔다.
   취소 구역과 같은 모양(접히는 블록)이되 색은 브랜드색이다 — 위험이 아니라
   오늘 할 일이기 때문이다. */
.totals {
  margin: 0 0 var(--space-4);
  border: 1px solid var(--color-divider);
  border-left: 4px solid var(--color-accent);
  background: var(--color-bg);
}

.totals > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  background: var(--color-neutral-100);
  font-size: 1.05rem;
  font-weight: 800;
  cursor: pointer;
}

.totals > summary:hover {
  background: var(--color-neutral-200);
}

.totals__lead {
  color: var(--color-neutral-700);
  font-size: 0.85rem;
  font-weight: 400;
}

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

.totals > summary::before {
  content: "▸";
  flex: 0 0 auto;
  color: var(--color-neutral-700);
  font-size: 0.9em;
  transition: transform 120ms ease;
}

.totals[open] > summary::before {
  transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
  .totals > summary::before {
    transition: none;
  }
}

.totals > .scroll-x {
  padding: var(--space-3);
}

/* 취소된 주문 구역. **집계와 다른 것으로 읽혀야 한다.**

   거래처 집계 바로 위에 같은 모양 표로 붙어 있으면 표 하나가 더 있는 것으로만
   읽힌다. 그래서 세 가지를 준다 — 위험색, 왼쪽 띠, 아래 여백.

   색은 `--color-danger` 다. `--color-accent`(브랜드 적색)는 로고와 주 실행
   단추 색이라 "우리 브랜드"를 뜻한다 — 취소에 쓰면 뜻이 섞인다. danger 는
   같은 줄의 「취소」 단추가 이미 쓰는 색이라 **같은 뜻에 같은 색**이 된다.

   색만으로 뜻을 전하지는 않는다(WCAG 1.4.1) — 「취소된 주문 N건」과
   「나가지 않습니다」가 글자로 함께 있다. */
.cancelled {
  /* 거래처끼리는 --space-2 다. 성격이 다른 블록이라 그보다 훨씬 띄운다. */
  margin: 0 0 var(--space-6);
  border: 1px solid var(--color-danger);
  border-left-width: 4px;
  background: var(--color-bg);
}

.cancelled > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  background: var(--color-accent-100);
  color: var(--color-danger);
  font-size: 1.05rem;
  font-weight: 800;
  cursor: pointer;
}

.cancelled > summary:hover {
  background: var(--color-accent-200);
}

/* 접힘 표시. `display: flex` 를 준 summary 는 브라우저 기본 삼각형(::marker)이
   사라진다 — 표시가 없으면 눌러도 되는지 알 수 없어 「접을 수 있음」이 그림의
   떡이 된다. 직접 그리고 열림 상태에서 돌린다.

   글자가 아니라 도형이라 화면 읽기 프로그램에는 숨긴다 — details 요소가
   이미 펼침/접힘 상태를 알려 준다. */
.cancelled > summary::-webkit-details-marker {
  display: none;
}

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

.cancelled > summary::before,
.group > summary::before {
  content: "▸";
  flex: 0 0 auto;
  color: var(--color-neutral-700);
  font-size: 0.9em;
  transition: transform 120ms ease;
}

.cancelled[open] > summary::before,
.group[open] > summary::before {
  transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
  .cancelled > summary::before,
  .group > summary::before {
    transition: none;
  }
}

/* 접었을 때도 "왜 봐야 하는지"가 한 줄로 남는다 */
.cancelled__lead {
  color: var(--color-neutral-700);
  font-size: 0.85rem;
  font-weight: 400;
}

.cancelled > .scroll-x {
  padding: var(--space-3);
}

/* 상단 합계 아래 병기하는 내역 — 청구액이 왜 그 값인지 한 줄로 보인다 */
.stats__sub {
  margin-top: 2px;
  font-size: 0.85rem;
  color: var(--color-neutral-700);
}

/* 주문 화면 수량 입력 */
.item__qty {
  flex: 0 0 auto;
  text-align: right;
}

input.qty-input {
  width: 5ch;
  min-height: var(--tap);
  text-align: center;
  font-size: 1.05rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

input.qty-note {
  width: 100%;
  /* 새벽에 장갑 낀 손이 누르는 칸이다 — 프로젝트 기준(--tap)을 지킨다 */
  min-height: var(--tap);
  margin-top: var(--space-1);
  /* 16px 미만이면 iOS 가 포커스 때 화면을 확대해 버려 목록 위치를 잃는다 */
  font-size: 1rem;
}

.item--error {
  background: var(--color-accent-100);
}

/* 하단 전송바 — 고정 배치. 목록 마지막 줄이 가리지 않도록 본문에 여백을 준다 */
.sendbar {
  position: sticky;
  bottom: 0;
  padding: var(--space-3) 0;
  background: var(--color-bg);
  border-top: 2px solid var(--color-text);
}

.sendbar button.primary {
  margin-top: 0;
  width: 100%;
}

textarea {
  width: 100%;
  min-height: 60px;
  margin-top: var(--space-2);
}

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

/* ── 상시 네비게이션 ───────────────────────────────
 *
 * 전에는 이게 없었다. 화면 이동이 `admin/home.html` 의 밑줄 링크 목록과 각 화면
 * 하단 「관리자 홈으로」 뿐이라, 주문 취합 → 원장 이동이 **홈을 거쳐 2클릭**이었다.
 * 지금 어디에 있고 어디로 갈 수 있는지가 화면에 없어서 "복잡하다"고 느껴진다.
 *
 * 본문 링크(`a { color: accent-700 }` + 브라우저 기본 밑줄)를 네비에 그대로 쓰면
 * "읽을 글"로 보인다. 네비는 **누를 것**이므로 면적과 경계를 준다.
 */

.mainnav {
  border-bottom: 2px solid var(--color-text);
  background: var(--color-surface);
}

.mainnav__inner {
  display: flex;
  max-width: var(--page-max);
  margin: 0 auto;
  /* 왼쪽 여백을 **여기서 주지 않는다.** 링크가 이미 좌우 --space-4 를 갖고 있어서
     둘을 더하면 첫 탭 글자가 본문 제목보다 16px 오른쪽으로 밀린다.
     링크의 패딩이 곧 본문 여백이 되게 둔다 — 첫 탭의 누르는 면적도 넓어진다.
     오른쪽은 반대다 — 마지막 탭 뒤에 아무것도 없어서, 옆으로 밀었을 때
     화면 끝에 딱 붙는다. 그쪽만 여백을 준다. */
  padding: 0 var(--space-4) 0 0;
  /* 탭이 많아지면 줄바꿈보다 옆으로 미는 편이 낫다 — 줄바꿈되면 본문이 밀린다 */
  overflow-x: auto;
  scrollbar-width: none;
}

.mainnav__inner::-webkit-scrollbar {
  display: none;
}

.page--wide ~ * .mainnav__inner,
.mainnav--wide .mainnav__inner {
  max-width: 1280px;
}

.mainnav__link {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: var(--tap);
  padding: 0 var(--space-4);
  /* 선택된 탭의 아래 표시와 같은 두께를 미리 비워 둔다 — 없으면 누를 때 1px 흔들린다 */
  border-bottom: 3px solid transparent;
  color: var(--color-neutral-800);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

.mainnav__link:hover {
  background: var(--color-neutral-200);
  color: var(--color-text);
}

/* 현재 화면 — 색만으로 알리지 않는다. 굵기와 아래 막대를 함께 쓴다
   (색각 이상이나 흑백 인쇄에서도 구분되어야 한다) */
.mainnav__link[aria-current="page"] {
  border-bottom-color: var(--color-accent);
  background: var(--color-bg);
  color: var(--color-text);
  font-weight: 800;
}

.mainnav__link:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: -3px;
}

/* ── 관리자 홈 타일 ────────────────────────────────
 * 밑줄 링크 목록 대신 눌리는 면적을 준다. 하루 흐름 순서대로 놓는다 —
 * 아침 단가 → 주문 취합 → 원장 → 거래처 → 설정.
 */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-3);
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}

.tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4);
  border: 2px solid var(--color-divider);
  background: var(--color-neutral-100);
  color: var(--color-text);
  text-decoration: none;
}

.tile:hover {
  border-color: var(--color-accent);
  background: var(--color-accent-100);
}

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

.tile__name {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
}

.tile__what {
  color: var(--color-muted);
  font-size: 0.9rem;
  line-height: 1.45;
}

/* ── 툴바 ──────────────────────────────────────────
 * 날짜 이동 · 내려받기 · 일괄 처리가 각각 다른 <p>·<form> 으로 흩어져 있었다.
 * 한 줄로 묶어야 "이 화면의 조작부"로 읽힌다.
 */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--color-divider);
  background: var(--color-neutral-100);
}

.toolbar form,
.toolbar .toolbar__group {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  /* 폰 폭에서는 날짜 이동 단추 4개가 한 줄에 안 들어간다. 감싸지 않으면
     마지막 단추가 툴바 테두리 밖으로 삐져나온다. */
  flex-wrap: wrap;
}

/* 오른쪽으로 밀어 두는 묶음 (내려받기 등).
   **`.toolbar` 를 함께 적어야 한다** — 위 `.toolbar .toolbar__group { margin: 0 }` 이
   (0,2,0) 이라 `.toolbar__end` (0,1,0) 로는 `margin-left` 를 못 이긴다.
   파일에서 뒤에 있어도 진다. 클래스 둘이 클래스 하나보다 세다. */
.toolbar .toolbar__end {
  margin-left: auto;
}

/* 툴바의 입력칸. **flex 를 주지 않는다** — 아래 단추 규칙에서 일부러 빼 뒀다.

   `<input type="date">` 는 치환 요소다. 겉 상자는 우리 것이지만 안은 브라우저가
   만든 섀도 DOM(`::-webkit-datetime-edit`,
   `::-webkit-calendar-picker-indicator`)이고, 거기에 flex 를 씌우면 그 배치가
   구현 정의 영역으로 들어간다. 실측: 입력칸에 `display: inline-flex` 를 주면
   그 섀도 가상 요소들의 계산된 display 까지 `flex` 로 바뀐다.

   증상은 **달력을 못 고르는 것**이었다 — 아이콘을 눌러 달력이 떠도 날짜를
   고르려고 마우스를 옮기면 사라졌다.

   flex 는 애초에 필요 없다. 높이를 고정한 입력칸은 자기 글자를 스스로 세로
   중앙에 놓는다 (실측: flex 를 빼도 높이 44px·top 13px 로 옆 단추와 같은 자리). */
.toolbar input[type="date"],
.toolbar input[type="time"],
.toolbar input[type="text"],
.toolbar input[type="search"] {
  margin-top: 0;
  height: var(--tap);
  min-height: var(--tap);
  padding-block: 0;
}

.toolbar input[type="date"] {
  min-width: 9.5rem;
}

/* 툴바 안의 단추·링크는 **한 줄에 나란히 선다.** 그런데 만든 곳이 저마다 달라
   높이와 여백이 어긋났다 — `button.primary` 는 `margin-top: var(--space-4)` 와
   `padding: 12px 24px`, `.download-link` 는 `padding: 8px 16px`, `.mini-btn` 은
   `min-height: 32px`. 세 개를 나란히 두면 계단처럼 보인다.

   툴바 안에서만 같은 높이·같은 세로 여백으로 맞춘다. 밖에서는 원래 크기를 쓴다
   (본문의 「저장」은 커야 한다).

   `min-height` 만으로는 안 맞는다 — 세로 패딩이 저마다 달라 내용 높이가
   44 를 넘으면 그만큼 커진다 (실측 44·44·53·45). **높이를 고정**하고 세로
   패딩을 0 으로 두어 flex 가운데 정렬이 자리를 잡게 한다. */
.toolbar button,
.toolbar .download-link,
.toolbar .primary-link,
.toolbar .mini-btn {
  margin-top: 0;
  height: var(--tap);
  min-height: var(--tap);
  padding-block: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 주된 동작(등록·저장)은 가로 여백을 더 줘 눈에 먼저 들어오게 한다 */
.toolbar button.primary,
.toolbar .primary-link {
  padding-inline: var(--space-6);
}

.toolbar .download-link,
.toolbar .mini-btn {
  padding-inline: var(--space-4);
}

/* ── 요약 숫자 줄 ──────────────────────────────────
 * "거래처 5곳 · 주문 8건 · 합계 720,000원" 을 한 문장으로 흘리면 훑을 수 없다.
 * 항목별로 끊고 값을 크게 둔다.
 */

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  margin: 0 0 var(--space-4);
  padding: 0;
}

.stats > div {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.stats dt,
.stats__label {
  color: var(--color-muted);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
}

.stats dd,
.stats__value {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.25rem;
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
}

.stats__value--money {
  color: var(--color-accent-700);
}

/* ── 접이식 묶음 ───────────────────────────────────
 * 거래처 N곳에 표 2개씩 세로로 쌓으면 10곳이면 표 20개다 — "한눈에"가 불가능하다.
 * 요약 줄만 남기고 상세는 접는다. 여는 것은 사장님이 고른다.
 */

.group {
  margin: 0 0 var(--space-2);
  border: 1px solid var(--color-divider);
  background: var(--color-bg);
}

.group > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  background: var(--color-neutral-100);
  cursor: pointer;
  font-weight: 700;
}

.group > summary:hover {
  background: var(--color-neutral-200);
}

.group[open] > summary {
  border-bottom: 1px solid var(--color-divider);
}

.group__name {
  font-family: var(--font-heading);
  font-size: 1.05rem;
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
}

.group__sum {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

.group__code {
  padding: 1px var(--space-2);
  border: 1px solid var(--color-neutral-400);
  background: var(--color-bg);
  color: var(--color-neutral-800);
  font-size: 0.85rem;
  font-weight: 700;
}

.group__body {
  padding: var(--space-3);
}

/* 카드 안의 표는 **카드 폭을 꽉 채운다.** 안쪽 여백을 그대로 두면 표 왼쪽이
   다른 화면의 표보다 12px 안쪽에서 시작해, 화면을 옮겨 다닐 때 표가 들쭉날쭉
   보인다. 여백은 상쇄하고 표 자신의 셀 패딩이 숨 쉴 자리를 만든다. */
.group__body > .scroll-x {
  margin-inline: calc(-1 * var(--space-3));
}

/* 펼친 안쪽의 작은 제목. h2 처럼 위 여백을 크게 두면 카드 안이 헐렁해진다 */
.group__subhead {
  margin: var(--space-4) 0 var(--space-2);
  font-size: 0.95rem;
  color: var(--color-muted);
}

/* 주문 취합은 표 10개를 실제 규모(1280px)로 펼쳤을 때 세로선이 183칸이었다.
   숫자는 오른쪽 정렬이고 지브라 행·1px 가로선이 행을 붙잡으므로 화면에서는
   세로선을 빼도 6~7열을 놓치지 않았다. 2px 머리선 59칸은 실제로는 표마다
   이어진 기준선 10개라 위계를 만들므로 그대로 둔다. */
@media screen {
  .rollup-page th,
  .rollup-page td {
    border-right: 0;
  }
}

/* 펼친 거래처끼리 8px 만 떨어져 있으면 표 묶음이 이어져 보였다. 16px 공백은
   새 굵은 선을 더하지 않고도 흑백에서 다음 거래처의 시작을 가른다. */
.rollup-page .group {
  margin-bottom: var(--space-4);
}

/* ── 표 끝과 합계 줄 ──────────────────────────────
 * 사장님이 두 자리를 짚었다 — 「품목 총합」 합계 아래, 거래처 주문표 아래.
 * 표의 마지막 선(y=698·1341)과 구역 테두리(y=711·1355)가 **13~14px 떨어져
 * 두 줄로** 읽혔다. 구역(`.totals`·`.group`)이 이미 끝을 알리므로 표가 자기
 * 끝을 한 번 더 그을 이유가 없다.
 */
.rollup-page tbody tr:last-child > *,
.rollup-page tfoot tr:last-child > * {
  border-bottom: 0;
}

/* 합계·소계·추가 금액·청구액은 굵은 글씨만으로 갈려 있었다(위선 실측 0px).
   어디부터 합계인지 선이 도와주지 않으면 눈이 본문 끝을 놓친다 — 머리글과
   같은 2px 로 한 번만 긋는다. */
.rollup-page tfoot tr:first-child > * {
  border-top: 2px solid var(--color-text);
}

/* 꼬리 **안쪽**에는 긋지 않는다. 소계·추가 금액·청구액이 연달아 있는데
   줄마다 선을 두면 굵은 글씨와 겹쳐 오히려 덩어리로 뭉친다. 오른쪽 정렬된
   숫자가 이미 줄을 가른다. */
.rollup-page tfoot > tr > * {
  border-bottom: 0;
}

/* 추가 금액의 사유를 접어 둔다. 「지난주 알배기 반품건 보상처리」 같은
   문장이 펼쳐져 있으면 합계 줄이 그 문장에 밀려 금액이 안 읽힌다.
   라벨 왼쪽에 서므로 값과 라벨의 짝은 그대로 붙어 있다. */
/* `inline-flex` 다 — `<details>` 는 기본으로 자식을 **세로로 쌓는다.**
   그대로 두면 펼쳤을 때 「사유」 단추와 문장이 두 줄로 어긋난다(실측). */
.rollup-page .extra-note-toggle {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-right: var(--space-2);
  font-weight: 400;
  vertical-align: baseline;
}

.rollup-page .extra-note-toggle > summary {
  display: inline-block;
  cursor: pointer;
  padding: 0 var(--space-2);
  border: 1px solid var(--color-neutral-600);
  color: var(--color-neutral-700);
  /* 크기를 줄이지 않는다 — 이 앱이 잡은 16px 바닥은 누르는 것에도 적용된다.
     사장님이 새벽에 눌러야 하는 단추다. */
  font-size: 1rem;
  /* 기본 삼각형을 지운다 — 표 안에서 줄 높이를 흔든다. 대신 테두리로
     「누를 수 있는 것」임을 알린다. */
  list-style: none;
}

.rollup-page .extra-note-toggle > summary::-webkit-details-marker {
  display: none;
}

.rollup-page .extra-note-toggle[open] > summary {
  background: var(--color-surface);
}

.rollup-page .extra-note-toggle__body {
  display: inline-block;
  margin-left: var(--space-2);
}

/* 한 건 비고는 라벨과 값이 한 줄에 선다. 여러 건이거나 문장이 길 때만 자연스럽게
   감싸며, 글자를 줄여 억지로 맞추지 않는다. */
.rollup-page .rollup-note--list {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-2);
}

.billing-cell__primary,
.billing-cell__secondary {
  display: block;
}

/* 종이용 값. **화면에서는 숨긴다** — 바로 옆에 입력칸이 있어 같은 값이 두 번
   보이고, 추가 금액이 없는 주문은 「—」 가 군더더기로 남는다(실측 확인).
   `@media print` 에서만 드러난다.

   배포 메모 — 이 파일은 `ManifestStaticFilesStorage` 가 내용 해시를 붙여
   `app.<해시>.css` 로 낸다. `collectstatic` 이 그 파일을 **두 번에 나눠 쓰는
   순간** Cloudflare 가 중간 상태를 잡아 캐시한 적이 있다(실측: 정상 74,450B
   자리에 1,049B 가 `immutable` 로 박혔다. `max-age=315360000` 이라 스스로는
   10년간 안 풀린다). 배포 뒤에는 받아지는 크기를 한 번 확인하는 편이 안전하다. */
.rollup-page .extra-cell__print {
  display: none;
}

.billing-cell__secondary {
  color: var(--color-neutral-700);
  font-weight: 400;
}

/* 취합 화면의 보조 글자는 실측 12.8~15.2px 였다. 줄 수를 줄인 대가로 글자까지
   작아지지 않게 이 화면 안의 읽기·조작 표식을 16px 바닥에 맞춘다. */
.rollup-page .mini-btn,
.rollup-page .stats dt,
.rollup-page .stats__sub,
.rollup-page .totals__lead,
.rollup-page .cancelled__lead,
.rollup-page thead th,
.rollup-page .rollup-note,
.rollup-page .group__code,
.rollup-page .badge,
.rollup-page .hint,
.rollup-page .group__subhead {
  font-size: 1rem;
}

/* 인쇄에서는 접힌 것도 펼쳐 보여 준다. 화면에서 접어 둔 게 종이에서 빠지면
   인쇄물이 조용히 불완전해진다 — 배송 나가는 종이라 더 위험하다. */
@media print {
  .group[open] > summary,
  .group > summary {
    background: none;
  }

  .group > .group__body {
    display: block !important;
  }

  /* 종이는 지브라 배경이 빠질 수 있다. 6~7열을 흑백으로 따라가도록 세로선을
     3:1 이상 중립색으로 되살리고 마지막 칸에는 중복 선을 만들지 않는다. */
  .rollup-page th:not(:last-child),
  .rollup-page td:not(:last-child) {
    border-right: 1px solid var(--color-neutral-600);
  }

  /* **종이에서 못 누르는 것은 싣지 않는다.** 「처리」 열(확정·취소 단추)과
     추가 금액 입력 폼이 그대로 인쇄되면 7열 표가 A4 폭(794px)을 넘어
     오른쪽이 잘린다 — 실측 795.98px > 778px. 사장님이 그 종이를 들고
     배송을 나가므로 잘린 열은 그날 정보가 통째로 빠지는 것이다.
     입력칸 자리에는 확정된 값(`.extra-cell__print`)을 대신 싣는다. */
  .rollup-page .actions-heading,
  .rollup-page .actions-cell,
  .rollup-page .extra-cell .row-form {
    display: none;
  }

  .rollup-page .extra-cell__print {
    display: block;
  }

  /* **접어 둔 사유를 종이에서는 늘 편다.** `<details>` 는 닫혀 있으면 안의
     글자가 `display: none` 이 아니라 렌더 트리에서 빠진다 — `display` 만
     보면 「보인다」고 착각한다(실측). `content-visibility` 로 강제로 드러낸다.
     누를 수 없는 「사유」 단추는 지운다 — 종이에 남으면 눌러 보게 된다. */
  .rollup-page .extra-note-toggle > summary {
    display: none;
  }

  .rollup-page .extra-note-toggle > .extra-note-toggle__body {
    display: inline-block !important;
    content-visibility: visible !important;
  }
}

/* ── 빈 상태 ───────────────────────────────────────
 * "이 날짜에는 주문이 없습니다" 를 muted 한 줄로 두면 화면이 고장 난 것처럼 보인다.
 * 무엇이 없고 다음에 무엇을 하면 되는지 함께 적는다.
 */

.empty {
  margin: var(--space-4) 0;
  padding: var(--space-6) var(--space-4);
  border: 1px dashed var(--color-divider);
  background: var(--color-neutral-100);
  text-align: center;
  line-height: 1.7;
}

/* ── 모달 ──────────────────────────────────────────
 * `<dialog>` 를 쓴다 — 브라우저가 포커스 트랩·Esc·백드롭·접근성 트리를 맡는다.
 * 손으로 만든 div 모달은 그 네 가지를 전부 다시 구현해야 하고, 보통 빠뜨린다.
 *
 * JS 가 없으면 모달이 안 열린다. 그래서 여는 자리는 **링크**로 두고 JS 가
 * 가로챈다 — 링크는 JS 없이도 원래 페이지로 간다 (product_form.html 이 남아 있다).
 */

.modal {
  width: min(34rem, calc(100vw - 2 * var(--space-4)));
  max-height: calc(100vh - 2 * var(--space-6));
  padding: 0;
  border: 2px solid var(--color-text);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  color: var(--color-text);
  overflow: auto;
}

.modal::backdrop {
  background: rgba(32, 30, 29, 0.55);
}

.modal__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 2px solid var(--color-text);
  background: var(--color-surface);
}

.modal__title {
  margin: 0;
  font-size: 1.15rem;
}

.modal__close {
  margin-left: auto;
  width: var(--tap);
  min-height: var(--tap);
  border: 1px solid var(--color-divider);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}

.modal__close:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-700);
}

.modal__body {
  padding: var(--space-4);
}

/* 모달 안의 폼은 폭 제한을 풀어 준다 — .stack 의 420px 는 본문 기준이다 */
.modal__body .stack {
  max-width: none;
}

/* 폼이 길면(품목 등록은 칸 7개다) 제출 단추가 스크롤 아래로 사라진다.
   아래에 붙여 둔다 — 사장님이 다 채운 뒤 어디를 눌러야 할지 찾지 않게. */
.modal__foot {
  position: sticky;
  bottom: 0;
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-divider);
  background: var(--color-neutral-100);
}

/* 「등록」과 「취소」가 나란히 선다. 만든 곳이 달라(`button.primary` 는
   padding 12/24 + margin-top 16, `.mini-btn` 은 min-height 32) 높이가 어긋났다.
   여기서 둘을 같은 높이로 맞춘다 — 나란한 단추가 계단이면 어느 쪽이 주된
   동작인지도 흐려진다. */
.modal__foot > button,
.modal__foot > .mini-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 0;
  min-height: var(--tap);
  padding-inline: var(--space-6);
}

/* 주된 동작이 넓다 — 「취소」는 글자 폭만 차지한다 */
.modal__foot .primary {
  flex: 1 1 auto;
}

.modal__foot [data-modal-close] {
  flex: 0 0 auto;
}

/* ── 정렬 가능한 표 머리 ───────────────────────────── */

th .sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: inherit;
  text-decoration: none;
}

th .sort:hover {
  color: var(--color-accent-700);
  text-decoration: underline;
}

/* 방향 표시는 글자로 낸다 — 아이콘 폰트를 받지 않는다 */
th .sort__dir {
  font-size: 0.75rem;
}

th[aria-sort] .sort {
  color: var(--color-accent-700);
}

/* 코드 칸 — 자리를 고정해 위아래로 훑을 수 있게 한다. `tabular-nums` 가 없으면
   P1·P10 의 숫자 폭이 달라 왼쪽 정렬인데도 들쭉날쭉 보인다. */
.code-cell {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--color-neutral-800);
}

/* 표가 빈 경우. muted 한 줄로 두면 화면이 고장 난 것처럼 보인다 —
   무엇이 없고 다음에 무엇을 하면 되는지 함께 적는다. */
.empty-cell {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  line-height: 1.7;
}

/* 블록 맨 위에 오는 힌트 — `.hint` 의 위 여백을 없앤다 */
.hint--top {
  margin-top: 0;
}

/* ── 거래처 정보 정의목록 ──────────────────────────── */

.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-1) var(--space-4);
  margin: 0 0 var(--space-4);
}

.facts dt {
  color: var(--color-muted);
  font-size: 0.9rem;
}

.facts dd {
  margin: 0;
}

/* ── 상태 카드 ─────────────────────────────────────
 * 주문 설정의 현재 상태를 산문 3덩이로 적어 두면 지금 무엇이 켜져 있는지
 * 읽어야 알 수 있다. 항목별로 끊어 카드로 낸다.
 */

.statecards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-3);
  margin: 0 0 var(--space-6);
}

.statecard {
  padding: var(--space-4);
  border: 1px solid var(--color-divider);
  border-left: 4px solid var(--color-divider);
  background: var(--color-neutral-100);
}

.statecard--on {
  border-left-color: var(--color-ok);
}

.statecard--off {
  border-left-color: var(--color-neutral-500);
}

.statecard--warn {
  border-left-color: var(--color-warn);
}

.statecard__label {
  margin: 0 0 var(--space-1);
  color: var(--color-muted);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
}

.statecard__value {
  margin: 0 0 var(--space-2);
  font-family: var(--font-heading);
  font-size: 1.1rem;
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
}

.statecard__what {
  margin: 0;
  color: var(--color-muted);
  font-size: 0.9rem;
  line-height: 1.45;
}

/* ── 필드 묶음 ─────────────────────────────────────
 * 브라우저 기본 fieldset 은 안쪽 여백이 제멋대로다. 직접 잡는다.
 */

.fieldset {
  margin: 0 0 var(--space-4);
  padding: 0 var(--space-4) var(--space-4);
  border: 1px solid var(--color-divider);
  max-width: 460px;
}

.fieldset > legend {
  padding: 0 var(--space-2);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  letter-spacing: -0.02em;
}

/* ── 모바일 ────────────────────────────────────────
 * 점주는 전 과정을 폰으로 한다. 관리자도 배송 중 폰으로 확인한다.
 * 본문에는 가로 스크롤이 생기지 않는다 — 넓은 표만 .scroll-x 안에서 옆으로 민다.
 */

@media (max-width: 700px) {
  body {
    font-size: 16px; /* iOS가 16px 미만 입력칸에서 자동 확대하는 것을 막는다 */
  }

  /* 탭 5개는 폰 폭에 안 들어간다. 줄바꿈시키면 본문이 두 줄만큼 밀리므로
     옆으로 미는 쪽을 택한다. 현재 탭은 아래 막대로 보이니 위치를 잃지 않는다.

     `padding-inline` 은 위 `.page` 와 **같은 값**이어야 한다 — 그 값이 곧 첫 탭
     글자와 본문 제목의 정렬이다 (위 `.mainnav__inner` 주석). 여기서 `.page` 가
     `--space-3` 으로 좁아지므로 탭도 함께 좁힌다. */
  .mainnav__link {
    padding-inline: var(--space-3);
    font-size: 0.95rem;
  }

  /* 툴바는 한 줄에 다 못 들어간다 — 묶음별로 줄을 내리고 각 묶음은 폭을 채운다 */
  .toolbar {
    padding: var(--space-2);
  }

  .toolbar__end {
    margin-left: 0;
    width: 100%;
  }

  .toolbar__end .download-link {
    width: 100%;
    text-align: center;
  }

  .stats {
    gap: var(--space-3) var(--space-4);
  }

  .stats dd,
  .stats__value {
    font-size: 1.1rem;
  }

  /* 접이식 요약에서 금액은 오른쪽 끝에 붙지 않고 아래로 내려온다 —
     좁은 폭에서 이름과 금액이 붙으면 둘 다 읽기 어렵다 */
  .group__sum {
    margin-left: 0;
    width: 100%;
  }

  /* 모달은 폰에서 화면을 거의 다 쓴다. 아래에서 올라오는 시트처럼 둔다 */
  .modal {
    width: 100vw;
    max-width: 100vw;
    max-height: 92vh;
    margin: auto auto 0;
    border-width: 2px 0 0;
  }

  .topbar {
    padding: var(--space-2) var(--space-3);
    gap: var(--space-2);
  }

  .brand {
    font-size: 0.95rem;
  }

  .topnav {
    gap: var(--space-2);
    font-size: 0.9rem;
  }

  .whoami {
    max-width: 9em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .page {
    padding: var(--space-4) var(--space-3) var(--space-8);
  }

  .messages {
    padding: 0 var(--space-3);
  }

  h1 {
    font-size: 1.3rem;
  }

  h2 {
    font-size: 1.05rem;
    margin-top: var(--space-6);
  }

  /* 폼은 화면 폭을 꽉 채운다 — 420px 고정이면 폰에서 좁아 보인다 */
  .stack {
    max-width: none;
  }

  /* 표는 셀 여백을 줄이고, 넘치는 만큼만 컨테이너 안에서 민다 */
  .scroll-x {
    /* 좌우 여백까지 써서 스크롤 폭을 확보한다 */
    margin-inline: calc(-1 * var(--space-3));
    padding-inline: var(--space-3);
  }

  th,
  td {
    padding: var(--space-2) var(--space-1);
    font-size: 0.95rem;
  }

  thead th {
    font-size: 0.8rem;
  }

  /* 그 자리 수정 칸(`[data-edit-field]`)도 눌리는 대상이다 — 더블탭해서 고친다.
     PC 에서는 24~29px 로 두어 표를 촘촘히 유지하지만, 손가락으로는 겨냥이 안 된다.
     사장님이 폰으로도 단가를 고치므로 --tap 까지 올린다. **표는 그만큼 길어진다** —
     한 화면에 보이는 품목 수와 맞바꾼 것이다.

     `inline-flex` + `align-items: center` 가 함께 필요하다. 높이만 늘리면
     `inline-block` 의 글자가 위에 붙어 칸이 비어 보인다. */
  .cell-edit {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }

  /* 입력칸이 열렸을 때는 입력칸 자신이 --tap 을 갖는다. 겉칸까지 늘리면
     칸 안에 칸이 있는 것처럼 두 겹으로 보인다. */
  .cell-edit--open {
    min-height: 0;
  }

  /* 인라인 단추(판매중지·중지·취소·주문 취소)는 PC에서 25px면 충분하지만
     손가락으로는 오탭이 난다. 상태를 바꾸는 단추라 잘못 누르면 되돌려야 한다.
     글자 크기는 그대로 두고 눌리는 세로 면적만 넓힌다.

     `td` 안만 잡으면 안 된다 — 표 밖에 서 있는 것도 같은 25px 이다:
     점주 주문 상세의 “이 주문 취소하기”, 거래처 상세의 “임시 비밀번호 발급”,
     사진 화면의 “사진 삭제”. 하나같이 되돌리기 번거로운 동작이다.
     그래서 `td` 한정을 풀고 `button.linkish` 전체를 잡는다.
     (`.topnav button.linkish` 는 이미 위에서 --tap 을 받고 있어 변화 없다.)

     `.mini-btn` 은 `button.mini-btn` 으로 좁히면 안 된다 — 같은 칸의 「재고 이력」은
     `a.mini-btn` 이고, `.mini-btn{min-height:32px}` (0,1,0) 가 `td a` (0,0,2) 를
     이겨서 32px 로 남는다. 클래스 하나가 요소 둘보다 세다. */
  button.linkish,
  .mini-btn,
  td a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }

  /* 단추가 --tap 으로 올라가면 옆의 입력도 같이 올라가야 줄이 안 어긋난다.
     손가락 목표를 32px 로 줄이는 쪽은 선택지가 아니다. */
  .extra-cell__amount,
  .extra-cell__note {
    height: var(--tap);
    min-height: var(--tap);
  }

  /* 일괄 처리 버튼은 한 줄에 하나씩 — 오탭 방지 */
  .bulkbar {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
  }

  .bulkbar button {
    min-height: var(--tap);
    width: 100%;
  }

  .bulkbar__label {
    margin: 0;
  }

  /* 주요 버튼은 폭을 꽉 채워 엄지로 누르기 쉽게 */
  button.primary,
  .primary-link {
    width: 100%;
    text-align: center;
  }

  .searchbar button {
    min-height: var(--tap);
  }

  .notice code.temp-password {
    font-size: 1.3rem;
    letter-spacing: 0.1em;
    word-break: break-all;
  }
}

/* 아주 좁은 기기(320px대 구형) */
@media (max-width: 380px) {
  .page {
    padding-inline: var(--space-2);
  }

  .scroll-x {
    margin-inline: calc(-1 * var(--space-2));
    padding-inline: var(--space-2);
  }

  .whoami {
    max-width: 6em;
  }
}
