/* 사장님 화면 공용 스타일 — 장부(Ledger) 밀도 설계
   WHY: 시술 중 장갑 낀 손으로 30초 본다. 예약 1건 = 카드 1장이 아니라 한 그릇 안의 한 행.
        "텅 비어 보임"은 정보가 적어서가 아니라 면이 2장(#f7f8fa/#fff, 대비 1.06:1)뿐이라 생긴다.
        그래서 색을 한 개도 더하지 않고 (1) 면 4단 (2) 회색 4단 (3) 근사흑색 선택표시로 층을 만든다.
   대비는 전부 WCAG 식으로 직접 계산해 확정했다(주석의 숫자는 계산값). */

:root {
  /* ---- 면 4단: 바탕 → 카드 → 눌린 면 → 선 ---- */
  --bg: #eef1f4;        /* 바탕. 흰 카드 대비 1.13:1 (기존 #f7f8fa는 1.06:1로 사실상 같은 면이었다) */
  --card: #ffffff;      /* 카드·장부 면 */
  --inset: #f5f7f9;     /* 카드 안 눌린 면: 칩·표머리·접힘줄·지난 예약 */
  --line: #dfe3e8;      /* 바깥 테두리 1.29:1 (기존 #e6e8ec는 1.23:1) */
  --line-2: #e9edf1;    /* 행 사이 얇은 괘선 1.18:1 */
  --line-3: #ccd3db;    /* 표머리 아래 굵은 선 1.51:1 */

  /* ---- 잉크 4단: 색을 하나도 안 늘리고 위계를 한 단 늘린다 ---- */
  --text: #1f2328;      /* 15.80:1  시각·화면 제목·숫자 */
  --text-2: #434a54;    /*  8.95:1  시술명·손님 이름·날짜 구분줄 */
  --muted: #5a626c;     /*  6.18:1  설명·라벨·메모 (기존 #6b7280은 4.83:1) */
  --muted-2: #666d76;   /*  5.23:1  보조 캡션. 눌린 면 위 4.87:1, 바탕 위 4.61:1 — 세 면 모두 AA 통과 */
  --ink: #20232c;       /* 15.69:1  '선택됨' 전용 근사흑색. 채도 0이라 절대 튀지 않는다 */

  /* ---- 강조는 파랑 하나. 색조·채도는 그대로 두고 명도만 낮춘다 ---- */
  --accent: #3557a8;      /* H222 S52% · 흰 글자 6.82:1. 기존 #5b7fd6(H222 S60% L60%)은 3.85:1로 AA 미달 */
  --accent-ink: #2f55b0;  /* 연한 파랑 배경 위 글자 5.92:1 */
  --accent-soft: #e8eefb; /* .badge.info / .btn.soft / 통계 .box.money 에만. '선택됨' 표시에는 절대 쓰지 않는다(흰 대비 1.16:1) */

  /* ---- 상태색: 채도를 올리지 말고 명도를 내린다. 막대는 전부 흰 대비 3:1 이상 ---- */
  --wait: #b4841a;   --wait-bg: #fdf2d9;  --wait-ink: #78560a;  /* 막대 3.36:1 · 배지글자 6.02:1 */
  --ok: #2e8b6b;                                                /* 막대 4.18:1 (청록쪽) */
  --bad: #c0533c;    --bad-bg: #fbe7e2;   --bad-ink: #94301c;   /* 막대 4.62:1 · 배지글자 6.53:1 */
  --gray-soft: #eef0f3; --gray-ink: #414952;                    /* 회색 배지 8.00:1 */

  /* ---- 모양: 값의 종류를 줄이는 것만으로 '설계된' 인상이 생긴다 ---- */
  --r-card: 12px;   /* 카드·장부 */
  --r-ctl: 8px;     /* 버튼·입력 (부모 12 - 자식 8 = 4px 차이라야 안에 들어앉아 보인다) */
  --sh: 0 1px 2px rgba(16, 24, 40, .05);   /* 화면 전체에서 유일한 그림자 */
  --ls-title: -0.02em; --ls-body: -0.01em;
  --h-sm: 44px;     /* 작은 버튼·탭·검색창 (Apple 44pt 하한) */
  --h-md: 48px;     /* 기본 버튼·입력칸 */

  /* ---- sticky 층 높이 ----
     WHY: 값이 실제 높이보다 작으면 스크롤 중 띠 사이로 본문이 비친다. '날짜 선택' 탭에서
     날짜 입력칸이 끼어들면 툴바 높이가 바뀌므로, today.html이 렌더 뒤 실측값으로 덮어쓴다
     (App.syncStickyOffsets). 아래 값은 그 전에 쓰이는 안전한 기본값이다(실측 73 / 110). */
  --gut: 54px;      /* 시각 열 폭 (폰) */
  --top-h: 74px;    /* 상단바 */
  --tb-h: 110px;    /* 툴바(탭 줄 + 검색 줄) */
  --head-h: 0px;    /* 표머리 (폰에는 없음) */
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: "Pretendard", -apple-system, "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
  background: var(--bg); color: var(--text);
  font-size: 16px; line-height: 1.5; letter-spacing: var(--ls-body);
  -webkit-font-smoothing: antialiased;
}
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
h1, h2, h3 { letter-spacing: var(--ls-title); }
/* WHY: display를 지정한 요소에는 hidden 속성이 안 먹는다(작성자 규칙이 브라우저 기본 규칙을 이긴다).
   JS가 hidden 으로 숨기는 곳(#pickDate, .summary, .sheet-head)이 있어 한 줄로 못 박는다.
   실제로 브라우저에서 .summary[hidden] 이 display:grid 로 계속 보이는 것을 확인하고 넣었다. */
[hidden] { display: none !important; }
/* WHY: 가게 컴퓨터에서 키보드로 넘길 때 지금 어디에 있는지 보여야 한다. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* WHY: Pretendard 기본은 비례숫자라 '11:00'과 '10:00'의 폭이 달라 세로로 반복되는 시각 열이 흔들린다.
   한글 문장 안 숫자에는 걸지 않는다(비례숫자가 자연스럽다). */
.num, .bk .time, .summary .box .n, .seg button b, .daylabel, .foldrow .n,
.tally dd, .item .main .s, .code { font-variant-numeric: tabular-nums; }

/* =========================================================================
   상단바 — 가게 이름 + 오늘 상태 한 줄
   WHY(요청 항목): 기존 .title은 18px인데 예약 시각이 20px, 요약 숫자가 22px이라
   가게 이름이 화면에서 세 번째로 작은 굵은 글자였다. 게다가 flex:1이 걸려
   이름 뒤 빈 공간이 전부 제목 박스가 되고 이름만 왼쪽 끝에 작게 붙어 있었다.
   폰에서도 sticky를 유지한다 — 이름을 키워 놓고 스크롤할 때 사라지게 하면
   요청을 절반만 이행한 것이다.
   ========================================================================= */
.topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--card); border-bottom: 1px solid var(--line);
  padding: 10px 16px 9px; display: flex; align-items: center; gap: 12px;
  min-height: var(--top-h);
}
.topbar .brand { flex: 1 1 auto; min-width: 0; }   /* flex는 이름이 아니라 '이름+상태' 묶음에 준다 */
/* 이름 + [찾기]를 한 줄로. 이름만 줄어들고 버튼은 늘 보이게 한다 */
.topbar .titlerow { display: flex; align-items: center; gap: 8px; min-width: 0; }
.topbar .titlerow .title { flex: 0 1 auto; }
/* 이름 옆 상태 배지(운영자·테스트). 이름이 길어도 이 배지는 줄지 않는다 */
.topbar .titlerow .nameflag { flex: 0 0 auto; font-size: 13px; }
/* WHY(사용자 2026-09-28): 흰 버튼은 흰 상단바에 묻혀 "색 때문에 찾기 힘들다". 연한 파랑 + 진한 파랑 글자로 띄운다
   (배경 위 글자 5.92:1). 채운 파랑은 쓰지 않는다 — 그건 [전화 예약]처럼 '지금 할 일' 자리이고,
   상단바에서 제일 먼저 읽혀야 하는 것은 가게 이름이다. */
.topbar .findshop {
  flex: 0 0 auto; border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent-ink);
  border-radius: 999px; padding: 0 14px; min-height: var(--h-sm); font-size: 14px; font-weight: 600;
  cursor: pointer; white-space: nowrap;
}
.topbar .findshop:hover { background: #dbe6f9; }
/* 폰에서는 [찾기]만 — 가게 이름 자리를 뺏지 않는다(어느 가게를 보는지가 먼저다) */
.topbar .findshop .wide { display: none; }
@media (min-width: 700px) { .topbar .findshop .wide { display: inline; } }
.topbar .title {
  font-size: 24px; font-weight: 700; line-height: 1.2;
  letter-spacing: var(--ls-title); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 이름 밑 한 줄 = 화면에서 가장 비어 보이던 자리를 색 없이 정보로 채운다 */
.topbar .sub {
  margin-top: 3px; font-size: 14px; font-weight: 500; color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.topbar .sub b { font-weight: 600; color: var(--wait-ink); }   /* '입금 대기 n'만 6.70:1 */
.topbar .topacts { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
/* 가게 찾기 창: 검색칸 + 목록. 가게가 많아도 한 화면 안에서 스크롤로 고른다 */
.shoppick input {
  width: 100%; border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: 0 12px; min-height: var(--h-md); font-size: 16px; color: var(--text);
}
.shoppick .picklist { margin-top: 10px; max-height: 52vh; overflow: auto; }
.shoppick .pick {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: #fff; border: none; border-bottom: 1px solid var(--line);
  padding: 12px 4px; font-size: 16px; color: var(--text); cursor: pointer;
}
.shoppick .pick:hover { background: var(--gray-soft); }
.shoppick .pick .nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shoppick .pick.on .nm { font-weight: 700; }
.shoppick .pick .tag.on { background: var(--wait-bg); color: var(--wait-ink); }
.topbar .link {
  color: var(--muted); font-size: 15px; font-weight: 500; background: none; border: none;
  padding: 0 8px; min-height: var(--h-sm); cursor: pointer; border-radius: var(--r-ctl);
}

/* 하단 탭 (폰) */
.tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  background: var(--card); border-top: 1px solid var(--line);
  display: flex; padding-bottom: env(safe-area-inset-bottom);
}
.tabbar a {
  flex: 1; text-align: center; padding: 10px 4px 8px; text-decoration: none;
  color: var(--muted); font-size: 14px; font-weight: 500; min-height: var(--h-sm);
}
.tabbar a.active { color: var(--text); font-weight: 700; box-shadow: inset 0 2px 0 var(--ink); }

main { max-width: 760px; margin: 0 auto; padding: 12px 12px calc(72px + env(safe-area-inset-bottom)); }

/* =========================================================================
   한 줄 알림 (비즈니스 인증 전 등)
   WHY: 인증에 영업일 3~7일 걸린다. 그동안 매일 노란 카드가 화면 위를 통째로
   먹으면 "빨리 예약 확인해야 할 때 광고창 뜨는" 것과 체감이 같다.
   큰 면적에서 채도를 빼고, 접힌 한 줄로 둔다.
   ========================================================================= */
.notice {
  background: var(--inset); border: 1px solid var(--line); border-left: 4px solid var(--wait);
  border-radius: var(--r-ctl); padding: 0 12px; margin-bottom: 10px;
  font-size: 14px; color: var(--text-2);
}
.notice > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px;
  min-height: var(--h-sm); font-weight: 500;
}
.notice > summary::-webkit-details-marker { display: none; }
/* 말줄임은 .t 에만 — '자세히 ∨'와 [×]까지 잘리면 접은 줄을 펼 수가 없다 */
.notice > summary .t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notice > summary .more { flex: 0 0 auto; color: var(--muted); font-weight: 500; }
.notice[open] > summary .t { white-space: normal; }
.notice[open] > summary .more { visibility: hidden; }
.notice > p { margin: 0 0 10px; color: var(--muted); font-size: 14px; }
.notice .x {
  flex: 0 0 auto; border: none; background: transparent; color: var(--muted);
  min-height: var(--h-sm); min-width: 36px; font-size: 16px; cursor: pointer;
}

/* =========================================================================
   툴바 — 탭(건수 포함) + 검색 + 전화 예약
   WHY: 기존에는 최상단 3블록이 전부 조작부(탭 → 요약 박스 4개 → 검색창)라
   정작 오늘 예약이 네 번째로 밀렸다. 요약 박스 줄을 장부 머리줄로 흡수해
   목록을 화면 위로 끌어올린다. 검색창은 항상 노출한다(돋보기 뒤로 접지 않는다).
   ========================================================================= */
.toolbar {
  position: sticky; top: calc(var(--top-h) - 2px); z-index: 15;
  background: var(--bg); padding: 8px 0; margin: 0 0 8px;
}
.seg { display: flex; gap: 4px; background: var(--gray-soft); border-radius: var(--r-card); padding: 3px; }
.seg button {
  flex: 1 1 0; min-width: 0; border: none; background: transparent; padding: 0 6px;
  border-radius: var(--r-ctl); min-height: 38px; font-size: 15px; font-weight: 500;
  color: var(--muted); white-space: nowrap; cursor: pointer;
}
/* WHY: 선택 표시를 연한 파랑(1.16:1 = 사실상 표시 없음)에서 근사흑색(15.69:1)으로 바꾼다.
   튀는 색을 한 방울도 안 쓰면서 화면에 중심이 생기는 가장 값싼 한 수다. */
.seg button.active { background: var(--ink); color: #fff; font-weight: 600; box-shadow: var(--sh); }
.seg button b { font-weight: 700; margin-left: 4px; }
.seg button.active b { color: #fff; }

.filterbar { display: flex; gap: 8px; margin-top: 6px; align-items: center; }
.filterbar .input { min-height: 40px; font-size: 15px; }
.filterbar input[type="search"] { flex: 1 1 0; min-width: 0; }
.filterbar input[type="date"] { flex: 0 0 auto; max-width: 156px; }
.filterbar .btn { flex: 0 0 auto; min-height: 40px; }

/* =========================================================================
   예약 장부 — 카드 여러 장이 아니라 한 그릇 안의 행
   WHY: 하루 3~6건인 1인샵에서 저밀도 카드는 구조적으로 절대 안 채워진다.
   행으로 묶으면 같은 4건이 한 덩어리 '목록'으로 읽히고, 남는 자리가
   '빈 곳'이 아니라 '의도된 여백'이 된다.
   주의: .sheet 에 overflow:hidden 을 걸면 안쪽 .sheet-head/.daylabel 의 sticky 가 죽는다.
   ========================================================================= */
.sheet {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card);
  box-shadow: var(--sh);
}
.sheet > .summary { border-radius: 11px 11px 0 0; }
.sheet > #list > *:last-child { border-radius: 0 0 11px 11px; }

/* ---- 요약 머리줄: 테두리 박스 4개를 장부 위에 붙는 한 줄로 ----
   WHY: 기존 요약 박스 4개는 세로 89px + 여백을 먹었다. 같은 네 필터를
   하나도 없애지 않고 46px 한 줄로 접는다. 선택은 채운 면이 아니라
   근사흑색 밑줄 — 한 화면에 검은 면이 여러 개 쌓이지 않게. */
.summary {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border-bottom: 1px solid var(--line); background: var(--card);
}
.summary .box {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 46px; padding: 0 4px; cursor: pointer; text-align: center;
  border: none; border-left: 1px solid var(--line-2); background: transparent;
  border-radius: 0; color: inherit;
}
.summary .box:first-child { border-left: none; border-top-left-radius: 11px; }
.summary .box:last-child { border-top-right-radius: 11px; }
.summary .box .l { font-size: 14px; font-weight: 500; color: var(--muted); }
.summary .box .n { font-size: 17px; font-weight: 700; color: var(--text); line-height: 1.2; }
.summary .box.on { background: var(--inset); box-shadow: inset 0 -2px 0 var(--ink); }
.summary .box.on .l { color: var(--text); font-weight: 600; }
.summary .box.warn .n { color: var(--wait-ink); }

/* ---- 표머리 (컴퓨터 전용): 열 의미를 한 번만 말하면 각 행에서 라벨을 반복할 필요가 없다 ---- */
.sheet-head { display: none; }

/* ---- 예약 한 건 = 한 행 ---- */
.bk {
  position: relative; display: grid;
  grid-template-columns: var(--gut) minmax(0, 1fr) minmax(0, auto);
  /* WHY: 폰에서 메모 칩을 손님 이름 옆(오른쪽 좁은 칸)에 두면 "메모 ..." 두 글자만 남아
     읽을 수 없다. 메모는 시술 직전에 보는 정보라 못 읽으면 없는 것과 같다.
     그래서 폰에서는 칩을 자기 줄로 내려 가로 전체를 쓰게 한다(행 높이 +22px). */
  grid-template-areas:
    "time svc  badge"
    "time who  who"
    "time tags tags"
    "time acts acts";
  column-gap: 10px; row-gap: 2px;
  padding: 8px 12px 8px 14px;
}
.bk + .bk, .daylabel + .bk, .foldbody > .bk:first-child { border-top: 1px solid var(--line-2); }
/* 좌측 상태 막대: 채도를 올리지 말고 명도를 내려 보이게 만든다.
   기존 #f2d66b(1.44:1)·#6cc48f(2.11:1)·#e88a8a(2.49:1)은 비텍스트 권장 3:1 미달이라
   밝은 샵 조명 + 노안에서 사실상 표시가 없었다. 색은 2차 단서이고 1차 단서는 한글 배지 글자다. */
.bk::before {
  content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 4px;
  border-radius: 0 3px 3px 0; background: transparent;
}
.bk.pending_deposit::before { background: var(--wait); }
.bk.confirmed::before { background: var(--ok); }
.bk.no_show::before { background: var(--bad); }
/* WHY: 끝난 예약을 opacity로 흐리면 글자 대비가 같이 무너진다. 면을 한 단 눌러 말한다. */
.bk.done { background: var(--inset); }
.bk.done .svc, .bk.done .time .hm { color: var(--muted); }

.bk .time { grid-area: time; align-self: start; padding-top: 1px; }
.bk .time .ap { display: block; font-size: 14px; font-weight: 500; color: var(--muted-2); line-height: 1.3; }
.bk .time .hm { display: block; font-size: 18px; font-weight: 700; color: var(--text); line-height: 1.25; letter-spacing: var(--ls-title); }
.bk .time .dur { display: block; font-size: 14px; font-weight: 500; color: var(--muted-2); line-height: 1.3; }

.bk > .badge { grid-area: badge; align-self: start; justify-self: end; }
.bk .svc {
  grid-area: svc; font-size: 16px; font-weight: 600; line-height: 1.35; color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* WHY: 좁은 폰에서 줄이 넘치면 '이름'이 잘리게 하고 '전화번호'는 끝까지 남긴다.
   번호가 잘리면 전화를 못 건다(기능 손실), 이름은 잘려도 누군지 짐작이 된다. */
.bk .who {
  grid-area: who; display: flex; align-items: baseline; gap: 6px; min-width: 0;
  font-size: 15px; line-height: 1.35; color: var(--muted);
}
.bk .who .nm {
  flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-weight: 600; color: var(--text-2);   /* 손님 이름을 메모와 같은 회색에서 한 단 위로 */
}
.bk .who a { flex: 0 0 auto; color: var(--accent); text-decoration: none; font-weight: 600; font-variant-numeric: tabular-nums; }
.bk .who .nophone { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }

/* 조건부 4줄(메모·취소 사유·환불 기록·알림 불가 경고)을 이미 있는 줄의 빈 오른쪽에 칩으로 합친다.
   WHY: 줄을 새로 만들지 않으므로 세로가 0px 늘어난다 = 공짜 밀도.
   넘치면 칩부터 잘려 행 높이가 절대 안 늘어난다. */
/* WHY: 폰에서는 칩이 자기 줄을 통째로 쓰므로 폭을 96px로 묶어 둘 이유가 없다
   (묶어 두면 "메모 눈썹 진하..."처럼 잘린다). 넘치면 줄을 바꾸게 둔다. */
.tags {
  grid-area: tags; display: flex; gap: 4px; row-gap: 3px; justify-content: flex-start;
  align-self: center; min-width: 0; flex-wrap: wrap;
}
.tag {
  flex: 0 1 auto; min-width: 0; max-width: 100%;
  background: var(--gray-soft); color: var(--muted); border-radius: 999px;
  padding: 1px 9px; font-size: 14px; font-weight: 500; line-height: 1.5;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tag.warn { background: var(--wait-bg); color: var(--wait-ink); flex: 0 0 auto; }
/* WHY: 칩 안의 종류 표시('메모'). 폰에는 열 머리가 없어 이게 있어야 무슨 글인지 안다. */
.tag .k { font-style: normal; color: var(--muted-2); margin-right: 5px; }
/* WHY: 칩이 하나도 없으면 빈 줄이 행 높이만 늘린다. */
.bk .tags:empty { display: none; }
/* WHY: 칩이 둘이면 짧은 쪽(상태 칩)은 그대로 두고 긴 쪽(메모)이 남은 폭을 다 쓰게 한다.
   둘을 똑같이 줄이면 둘 다 못 읽는 글자가 된다. */
.tags .tag:only-child, .tags .tag:last-child:not(.warn) { flex: 1 1 auto; }
.tag.bad { background: var(--bad-bg); color: var(--bad-ink); }

/* WHY: 목록에서 버튼을 절대 숨기지 않는다. 상세 모달로 접는 순간 경쟁사 최대 약점
   ('뭐 하나하나 다 두 번 눌러야')을 그대로 복사하게 된다.
   대신 채운 파랑은 행당 1개([입금 확인])만 두고 나머지는 흰 배경 + 회색 테두리로 내린다. */
.bk .acts { grid-area: acts; display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap; }
.bk .acts .btn { min-height: var(--h-sm); padding: 0 12px; font-size: 14px; }
/* WHY: 되돌릴 수 없는 [취소]를 [입금 확인] 바로 옆 6px에 붙여 두면 장갑 낀 손에서 오조작이 난다.
   행 높이는 그대로 두고 간격만 12px로 벌린다. */
.bk .acts .btn.primary + .btn { margin-left: 6px; }

/* 날짜 구분줄 (7일 탭) — 그날 건수까지 붙여 '열기 전에 아는' 정보로 만든다 */
.daylabel {
  position: sticky; top: calc(var(--top-h) + var(--tb-h) + var(--head-h) - 2px); z-index: 6;
  background: var(--inset); border-top: 1px solid var(--line-2); border-bottom: 1px solid var(--line-2);
  padding: 7px 14px; font-size: 15px; font-weight: 700; color: var(--text-2); margin: 0;
}
.daylabel .c { font-weight: 500; color: var(--muted); }
/* WHY: 오늘 줄을 검게 채우면 선택된 탭·선택된 요약과 함께 검은 면이 세 개 쌓여 무거워진다.
   같은 강조를 밑줄 한 줄로 낸다(화면당 검은 면은 2곳까지). */
.daylabel.today { box-shadow: inset 0 -2px 0 var(--ink); color: var(--text); }

/* 완료·취소는 접어서 맨 아래로 — 오늘 10건 중 6건이 완료면 화면의 60%가 흐린 행으로 차고
   정작 급한 '입금 대기 1건'이 묻힌다 */
.foldrow {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: var(--inset); border: none; border-top: 1px solid var(--line-2);
  padding: 0 14px; min-height: var(--h-md);
  font-size: 15px; font-weight: 500; color: var(--muted); cursor: pointer;
}
.foldrow .n { font-weight: 700; color: var(--text-2); }
.foldrow .car { margin-left: auto; color: var(--muted-2); }
.foldbody { border-top: 1px solid var(--line-2); }

/* =========================================================================
   빈 상태 — 일러스트가 아니라 이미 DB에 있는 숫자로 채운다
   WHY: '심플한 것'과 '텅 빈 것'의 차이는 장식량이 아니라 정보량이다.
   기존에는 0건이어도 '0 0 0 0' 박스 4개를 그리고 목록 자리엔 회색 한 줄만 남아,
   휴무일이나 이른 아침에 보는 첫 화면이 사실상 완전히 빈 화면이었다.
   ========================================================================= */
.board { padding: 20px 16px 18px; }
.board h3 { margin: 0; font-size: 18px; font-weight: 700; color: var(--text); }
.board .lead { margin: 4px 0 0; font-size: 15px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* 장부의 '합계란': 라벨 왼쪽 / 값 오른쪽 + 괘선. 가운데 정렬 문단보다 값을 빨리 집게 한다. */
.tally { margin: 14px 0; padding: 0; border-top: 1px solid var(--line-2); }
.tally > div {
  display: flex; align-items: baseline; gap: 12px;
  padding: 9px 0; border-bottom: 1px solid var(--line-2);
}
/* WHY: 값이 길면("10:00 ~ 13:00, 14:00 ~ 20:00") 라벨이 밀려 한 글자씩 세로로 깨진다.
   라벨은 줄바꿈 금지로 폭을 지키고, 값이 길 때는 값 쪽이 줄을 바꾼다. */
.tally dt { flex: 0 0 auto; white-space: nowrap; font-size: 15px; font-weight: 500; color: var(--muted); margin: 0; }
.tally dd {
  flex: 1 1 auto; min-width: 0; margin: 0; text-align: right;
  font-size: 16px; font-weight: 700; color: var(--text); word-break: keep-all;
}
.tally dd.warn { color: var(--wait-ink); }
.board .slots { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.board .slots button {
  border: 1px solid var(--line); background: #fff; border-radius: 999px; padding: 0 12px;
  min-height: 38px; font-size: 15px; font-weight: 600; color: var(--text-2);
  font-variant-numeric: tabular-nums; cursor: pointer;
}
.empty { text-align: center; color: var(--muted); padding: 24px 12px; font-size: 15px; }
.empty .btn { margin-top: 12px; }

/* 첫 로딩만 뼈대. 갱신 때는 목록을 지우지 않는다 —
   1분마다 화면을 비우면 사용자가 말한 '텅 빔'을 우리가 직접 만드는 것이다.
   애니메이션은 넣지 않는다(화려함 금지). */
.skel > div { padding: 14px; border-bottom: 1px solid var(--line-2); }
.skel > div:last-child { border-bottom: none; }
.skel i { display: block; height: 13px; border-radius: 4px; background: #e3e7ec; }
.skel i + i { margin-top: 10px; }
.skel i.w1 { width: 32%; }
.skel i.w2 { width: 64%; }
.skel i.w3 { width: 46%; }

/* =========================================================================
   배지 — 색 배지는 '입금 대기'(노랑)와 '노쇼'(적갈) 둘뿐
   WHY: 상태 5종을 전부 색으로 구분하면 패턴이 안 잡히고 정작 급한 '입금 대기'가 묻힌다.
   단, '확정'을 무표시로 두지는 않는다. 표식이 없는 것이 '확정됨'을 뜻하게 되면
   로딩 실패·통신 오류와 구분되지 않는다. 회색 배지로 조용히 적는다.
   ========================================================================= */
.badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 14px; font-weight: 600; background: var(--gray-soft); color: var(--gray-ink);
  white-space: nowrap;
}
.badge.pending_deposit { background: var(--wait-bg); color: var(--wait-ink); }
.badge.no_show { background: var(--bad-bg); color: var(--bad-ink); }
.badge.confirmed, .badge.completed, .badge.cancelled { background: var(--gray-soft); color: var(--gray-ink); }
.badge.info { background: var(--accent-soft); color: var(--accent-ink); }

/* =========================================================================
   버튼 — 높이 두 단(44 / 48)만. 기존 .btn.sm 38px는 장갑 낀 손에서 터치 타깃 미달이었다.
   빨강은 '되돌릴 수 없는 손해'(노쇼 배지·오류·확인창)에만 남기고
   목록의 취소·노쇼 버튼은 회색 아웃라인으로 내린다.
   ========================================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  border: 1px solid var(--line); background: #fff; color: var(--text);
  border-radius: var(--r-ctl); padding: 0 16px; font-weight: 600; font-size: 15px;
  min-height: var(--h-md); cursor: pointer; white-space: nowrap;
}
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }   /* 흰 글자 6.82:1 */
.btn.soft { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--accent-ink); }
.btn.danger { background: #fff; border-color: var(--line); color: var(--bad-ink); }
.btn.danger.fill { background: var(--bad-ink); border-color: var(--bad-ink); color: #fff; }
.btn.sm { min-height: var(--h-sm); padding: 0 12px; font-size: 14px; }
.btn.block { width: 100%; }
.btn:disabled { opacity: .5; cursor: default; }
.btnrow { display: flex; gap: 8px; margin-top: 12px; }
.btnrow .btn { flex: 1; }

/* =========================================================================
   카드 / 목록 (시술·설정 화면)
   ========================================================================= */
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 14px 16px; margin-bottom: 12px; box-shadow: var(--sh);
}
.card h2 { font-size: 18px; margin: 0 0 8px; font-weight: 700; }
/* 섹션 제목 옆 건수·현재 상태 — 이미 가진 숫자로 빈 곳을 채우는 같은 수법 */
.card h2 .c { font-size: 14px; font-weight: 500; color: var(--muted); margin-left: 6px; letter-spacing: var(--ls-body); }
.card h3 { font-size: 15px; margin: 16px 0 6px; color: var(--muted); font-weight: 500; }
.muted { color: var(--muted); font-size: 15px; }
.small { font-size: 14px; }
.row { display: flex; align-items: center; gap: 10px; }
.row.between { justify-content: space-between; }
.row.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.stack > * + * { margin-top: 10px; }
.divider { border-top: 1px solid var(--line-2); margin: 14px 0; }

.item {
  display: flex; align-items: center; gap: 12px; padding: 8px 0;
  border-bottom: 1px solid var(--line-2); min-height: 52px;
}
.item:last-child { border-bottom: none; }
.item .main { flex: 1; min-width: 0; }
.item .main .t { font-size: 16px; font-weight: 600; color: var(--text-2); }
.item .main .s { font-size: 14px; color: var(--muted); }
/* 영업시간 요일 라벨: 고정 폭이라야 7줄의 시각이 한 열로 선다 */
.item .wd { flex: 0 0 40px; font-weight: 700; color: var(--text-2); }
.chip {
  display: inline-flex; align-items: center; background: var(--inset); border: 1px solid var(--line);
  border-radius: var(--r-ctl); padding: 4px 10px; font-size: 15px; margin: 2px 6px 2px 0;
  color: var(--text-2); font-variant-numeric: tabular-nums;
}
.chip button { border: none; background: transparent; color: var(--muted); padding: 0 0 0 8px; font-size: 16px; cursor: pointer; }

/* =========================================================================
   폼
   ========================================================================= */
label.field { display: block; margin-bottom: 12px; }
label.field > span { display: block; font-size: 14px; font-weight: 500; color: var(--text-2); margin-bottom: 5px; }
.input, select.input, textarea.input {
  width: 100%; border: 1px solid var(--line); border-radius: var(--r-ctl); background: #fff;
  padding: 0 12px; font-size: 16px; min-height: var(--h-md);
}
textarea.input { padding: 12px; min-height: 92px; resize: vertical; }
.input:focus { border-color: var(--accent); }
.help { font-size: 14px; color: var(--muted); margin-top: 5px; }
.err { color: var(--bad-ink); font-size: 14px; margin-top: 6px; }
.switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 4px 0; min-height: var(--h-md); }
.switch input { width: 24px; height: 24px; accent-color: var(--accent); flex: none; }
.code { font-size: 34px; letter-spacing: .12em; font-weight: 700; text-align: center; padding: 8px 0; }

/* =========================================================================
   모달 / 토스트 / 로그인
   WHY(Polaris): 목록은 고밀도, 상세 편집 화면은 저밀도. 모달 안 폼은 밀도를 올리지 않는다.
   ========================================================================= */
.modal-bg { position: fixed; inset: 0; background: rgba(20, 24, 32, .42); z-index: 50; display: flex; align-items: flex-end; justify-content: center; }
.modal {
  background: #fff; width: 100%; max-width: 560px; border-radius: 16px 16px 0 0;
  padding: 18px 16px calc(18px + env(safe-area-inset-bottom)); max-height: 90vh; overflow: auto;
}
.modal h2 { margin: 0 0 14px; font-size: 18px; font-weight: 700; }
@media (min-width: 700px) {
  .modal-bg { align-items: center; }
  .modal { border-radius: 16px; }
}
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(76px + env(safe-area-inset-bottom)); background: var(--ink); color: #fff;
  padding: 12px 18px; border-radius: var(--r-ctl); font-size: 15px; z-index: 60; max-width: 90vw;
  text-align: center; box-shadow: 0 4px 16px rgba(16, 24, 40, .18);
}
.toast.bad { background: #7f2b1c; }   /* 흰 글자 9.27:1 */

.login-wrap { max-width: 420px; margin: 11vh auto 0; padding: 0 16px; }
.login-wrap h1 { font-size: 26px; font-weight: 700; margin: 0 0 6px; letter-spacing: var(--ls-title); }
.login-wrap .sub { color: var(--muted); font-size: 15px; margin-bottom: 22px; }

/* =========================================================================
   태블릿 (640px~)
   ========================================================================= */
@media (min-width: 640px) {
  main { padding: 16px 16px calc(76px + env(safe-area-inset-bottom)); }
  .filterbar input[type="search"] { flex: 0 1 320px; }
}

/* 폰: 장부를 화면 가장자리까지 흘려 가로 24px을 목록에 돌려준다 */
@media (max-width: 639px) {
  .sheet { margin: 0 -12px; border-radius: 0; border-left: none; border-right: none; }
  .sheet > .summary { border-radius: 0; }
  .sheet > .summary .box:first-child, .sheet > .summary .box:last-child { border-radius: 0; }
  .sheet > #list > *:last-child { border-radius: 0; }
}

/* =========================================================================
   컴퓨터 (1024px~) — 예약 1건 = 한 줄. 표머리로 열 의미를 한 번만 말한다.
   WHY: 기존 3열 카드 그리드(minmax 330px)는 시간순으로 읽어야 하는 목록을
   좌→우→아래로 꺾어 놓아 순서가 헷갈리고, 예약이 1~2건이면 옆이 통째로 빈다.
   ========================================================================= */
@media (min-width: 1024px) {
  :root { --top-h: 80px; --tb-h: 56px; --head-h: 36px; --gut: 80px; }
  main { max-width: 1120px; padding: 16px 24px 48px; }
  .tabbar { display: none; }

  .topbar { padding: 12px 24px; gap: 20px; }
  .topbar .brand { flex: 0 0 auto; max-width: 360px; }
  .topbar .title { font-size: 26px; }
  .topbar .topacts { margin-left: auto; }
  /* WHY: 이름이 커진 것만으로는 부족하다. 옆 글자가 내려가야 이름이 올라와 보인다. */
  .topnav { display: flex; gap: 2px; flex: 1 1 auto; }
  .topnav a {
    text-decoration: none; color: var(--muted); font-weight: 500; font-size: 15px;
    padding: 0 14px; min-height: var(--h-sm); display: inline-flex; align-items: center;
    border-radius: var(--r-ctl);
  }
  .topnav a.active { color: #fff; background: var(--ink); font-weight: 600; }

  /* 탭·검색·추가를 한 줄로 합쳐 목록을 위로 끌어올린다 */
  .toolbar { display: flex; align-items: center; gap: 12px; padding: 10px 0; margin-bottom: 10px; }
  .seg { flex: 0 0 auto; }
  .seg button { flex: 0 0 auto; padding: 0 16px; }
  .filterbar { flex: 1 1 auto; margin-top: 0; justify-content: flex-end; }
  .filterbar input[type="search"] { flex: 0 1 260px; }

  /* WHY: 표머리와 예약 행은 서로 다른 grid 컨테이너라 열 폭이 각자 계산된다.
     한 열이라도 auto(내용에 따라 결정)면 머리줄과 행의 열이 어긋난다
     — 실측에서 4~6열이 72~181px 밀렸다. 그래서 모든 트랙을 '길이'로만 적는다
     (고정 px 또는 minmax(고정, fr)). 그러면 두 grid가 같은 폭으로 해석된다. */
  .bk, .sheet-head {
    /* WHY: 메모 열이 0.9fr이면 실측 119px까지 줄어 "메모 눈..." 처럼 두 글자만 남는다.
       메모는 시술 직전에 보는 정보라 최소 폭을 준다. 대신 시술명 열을 한 단 줄인다. */
    /* WHY: 동작 열은 [입금 확인][변경][취소][손님] 4개(약 250px)가 한 줄에 서야 한다. 실측 82+50+50+50+간격 18. */
    grid-template-columns: var(--gut) 84px minmax(120px, 1.1fr) minmax(190px, 1fr) minmax(150px, 1.3fr) 250px;
    grid-template-areas: "time badge svc who tags acts";
    align-items: center; row-gap: 0; column-gap: 12px;
    padding: 6px 16px; min-height: 52px;
  }
  .sheet-head {
    display: grid; position: sticky; top: calc(var(--top-h) + var(--tb-h) - 2px); z-index: 8;
    background: var(--inset); border-bottom: 1px solid var(--line-3);
    min-height: var(--head-h); padding-top: 0; padding-bottom: 0;
    font-size: 14px; font-weight: 600; color: var(--muted); letter-spacing: 0;
  }
  .sheet-head span { align-self: center; }
  .bk .time { align-self: center; padding-top: 0; }
  .bk .time .ap { display: inline; font-size: 14px; }
  .bk .time .hm { display: inline; font-size: 17px; margin-left: 3px; }
  .bk .time .dur { display: block; font-size: 14px; }
  .bk > .badge { align-self: center; justify-self: start; }
  .bk .svc, .bk .who { white-space: nowrap; overflow: hidden; }
  /* WHY: PC는 '메모' 열 머리가 대신 말해 주므로 칩 안의 '메모'를 지운다(약 40px 회수). */
  .tag .k { display: none; }
  /* WHY: 칩이 둘이면(예: 자동알림 X + 메모) 한 줄에 못 들어가 메모가 "메모 눈썹 ..."으로 잘렸다.
     행 높이가 20px 느는 것보다 메모가 안 보이는 쪽이 훨씬 나쁘다 — 그래서 줄바꿈을 허용한다(기본값). */
  .bk .acts { justify-content: flex-end; flex-wrap: nowrap; margin-top: 0; }
  .bk .acts .btn { min-height: 40px; padding: 0 10px; }
  .daylabel { padding: 6px 16px; }

  main.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
  main.two-col > .card { margin-bottom: 0; }
  main.two-col > .card.full { grid-column: 1 / -1; }
}
@media (max-width: 1023px) { .topnav { display: none; } }

/* =========================================================================
   넓은 화면 (1280px~) — 남는 가로를 '오늘 메모'로 쓴다
   WHY: 1366x768 노트북이 가게 컴퓨터의 대부분이다. 여기서 표 오른쪽이
   그대로 비는 것이 'PC에서 없어 보임'의 물리적 원인이었다.
   진입점을 1280으로 잡은 이유: 1200에서 사이드를 붙이면 '메모' 열이 44px까지
   눌려 칩이 못 들어간다(실측). 1280부터는 1366·1920까지 전부 여유가 있다.
   ========================================================================= */
/* WHY: 동작 열이 250px로 넓어져 1280px에서는 표(934px)+오른쪽 패널(300px)이 함께 들어가지 않는다. 1320부터 패널. */
@media (min-width: 1320px) {
  main.today-layout {
    max-width: 1320px; display: grid; grid-template-columns: minmax(0, 1fr) 280px;
    gap: 20px; align-items: start;
  }
  main.today-layout > #shopWarn, main.today-layout > .toolbar { grid-column: 1 / -1; }
  .side { position: sticky; top: calc(var(--top-h) + 16px); display: flex; flex-direction: column; gap: 12px; }
  .side .card { margin-bottom: 0; }
  .side .tally { margin-top: 10px; margin-bottom: 14px; }
}
@media (max-width: 1319px) { .side { display: none; } }

/* 아주 넓은 화면에서만 설정·시술을 3단으로 */
@media (min-width: 1440px) {
  main.two-col { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  main.two-col > .card.full { grid-column: 1 / -1; }
}

/* 작은 폰 (380px 이하) */
@media (max-width: 380px) {
  :root { --gut: 50px; }
  .topbar { padding: 10px 12px 9px; }
  .topbar .title { font-size: 22px; }
  main { padding-left: 10px; padding-right: 10px; }
  .sheet { margin: 0 -10px; }
  .seg button { font-size: 14px; padding: 0 2px; }
  .bk { padding-left: 12px; padding-right: 10px; column-gap: 8px; }
}

/* =========================================================================
   통계 (stats.html)
   WHY: 1인샵 사장님이 한 달에 한 번 보는 화면. 숫자 카드 다섯 장과 노쇼 명단이면 충분하다.
   그래프·퍼센트는 쓰지 않는다(월 20~30건에서 한 건이 3~5%p를 흔들어 '급증'처럼 보인다).
   ========================================================================= */
/* WHY: 통계는 위에서 아래로 읽는 한 줄 흐름이다. 설정처럼 두 칸으로 나누면 달 이동과 카드가 옆으로 갈라진다. */
main.stats-layout { max-width: 820px; }
.monthnav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 4px 0 10px; }
.monthnav h2 { margin: 0; font-size: 20px; font-weight: 700; color: var(--text); }
.monthnav .btn { min-width: 48px; padding: 0 12px; }
.monthnav .btn:disabled { opacity: .35; cursor: default; }
.stat-total { display: flex; align-items: baseline; gap: 6px; margin: 2px 0 10px; flex-wrap: wrap; }
.stat-total .n { font-size: 34px; font-weight: 800; letter-spacing: -0.5px; color: var(--text); font-variant-numeric: tabular-nums; }
.stat-total .l { font-size: 15px; color: var(--muted); }
.summary.stat {
  grid-template-columns: repeat(6, 1fr);
  border: 1px solid var(--line); border-bottom: 1px solid var(--line); border-radius: 10px; overflow: hidden;
}
.summary.stat .box { flex-direction: column; gap: 2px; min-height: 58px; cursor: default; padding: 6px 2px; }
.summary.stat .box .n { font-size: 18px; }
.summary.stat .box .l { font-size: 13px; }
.summary.stat .box.bad .n { color: var(--bad); }
/* WHY: 예약금 칸은 예약을 나눈 칸이 아니라 "그중 입금 확인된 건수"다. 성격이 다르다는 걸 배경으로 표시. */
.summary.stat .box.money { background: var(--accent-soft); }
.summary.stat .box.money .n { color: var(--accent-ink); }
.note { font-size: 13px; color: var(--muted-2); margin: 10px 0 0; line-height: 1.5; }
/* WHY(적대검증 S9): 통계 라벨은 길다("이 프로그램 시작 후 처음 오신 손님"). 320px 폰에서 nowrap이면 카드가 가로로 넘친다. */
.stats-layout .tally dt { white-space: normal; flex: 1 1 auto; }
.stats-layout .tally dd { flex: 0 0 auto; }
/* WHY: 하단 탭이 4칸이 되어 320px에서 "시술·시간"이 줄바꿈되면 탭바 높이가 늘어난다. */
.tabbar a { white-space: nowrap; }
@media (max-width: 340px) { .tabbar a { font-size: 13px; padding-left: 2px; padding-right: 2px; } }
.noshow-row .main .t { display: flex; align-items: center; flex-wrap: wrap; }
.noshow-row a { color: var(--accent-ink); font-weight: 600; text-decoration: none; white-space: nowrap; font-variant-numeric: tabular-nums; }
.noshow-row .nophone { color: var(--muted-2); font-size: 14px; white-space: nowrap; }
@media (max-width: 380px) {
  .summary.stat .box .l { font-size: 11px; letter-spacing: -0.3px; }
  .summary.stat .box .n { font-size: 16px; }
  .stat-total .n { font-size: 30px; }
}

/* ---- 설정 잠금(사장님 화면): 값은 보이고 저장은 못 함 ---- */
.lock-note {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding: 10px 12px; background: var(--inset); border: 1px dashed var(--line-3); border-radius: 10px;
  font-size: 14px; color: var(--muted);
}
form input:disabled, form select:disabled, form textarea:disabled { background: var(--inset); color: var(--text-2); opacity: 1; }

/* 글자처럼 보이는 버튼(로그인 화면 "비밀번호를 잊으셨나요?"). WHY: <a href="#">는 스크롤을 튀게 하고 폼 안에서 엔터 동작을 헷갈리게 한다. */
.linkbtn { background: none; border: 0; padding: 0; color: var(--accent); font: inherit; font-size: 14px; text-decoration: underline; cursor: pointer; }


/* 통계 칸 버튼(명단 열기). WHY: button 기본 스타일을 지우고 .box 모양을 그대로 쓴다. 눌리는 칸임을 밑줄 라벨로 알린다. */
.summary .box.click { font: inherit; color: inherit; cursor: pointer; appearance: none; -webkit-appearance: none; text-align: center; }
.summary .box.click .l { text-decoration: underline; text-decoration-color: var(--line-2); text-underline-offset: 3px; }
.summary .box.click:hover { background: var(--inset); }
.summary .box.click:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
.list-scroll { max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; }
