/* =============================================================================
   CAPTAIN CARE Ver.02 — base.css   (I2 · 2026-07-31)
   브랜드북(캡틴다이어트 전자책 41p)의 지면 규칙을 화면으로 옮긴 것.

     ① 라임은 표지에만. 본문은 흰 배경 + 먹색.
     ② 챕터 헤더 = 먹색 바에 흰 글씨.
     ③ 섹션 = 큰 숫자 + 국문 제목 + 「: 부제」.
     ④ 사진은 전폭으로 크게, 한 장. 카드 그리드로 잘게 썰지 않는다.
     ⑤ 강조는 볼드 + 라임 하이라이트. 문장은 짧고 여백은 넓다.
============================================================================= */

/* 🔴 옛 판은 Pretendard 를 jsdelivr 에서 불렀는데 CSP(style-src 'self')가 **조용히 막고
   있었다** — 선언은 살아 있고 폰트는 한 번도 안 왔다(라이브 실측 · 실패가 폴백이라 안 보였다).
   Google Fonts 로 교체하고 _headers 가 그 두 호스트만 연다. 아이콘도 여기서 온다 —
   자리채움 네모(◧◎◇)를 지우고 실제 그림으로 말한다(2026-08-06 대표님 「의미 없는 박스 제거」). */
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700;800&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Baloo+2:wght@600;700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..40,300..600,0..1,0&display=block");

/* 🔴🔴 21차 — 옛 선택자는 `.ms, .navlink .ico, .tabbar .ico` 로 **담는 그릇을 이름으로
   열거**했다. 그래서 사이드바·탭바 밖에 아이콘을 두면 리가처가 안 걸려 **영문 낱말이
   그대로 화면에 찍힌다**(오늘 회원 헤더의 설정 버튼이 「settings」로 보였다).
   §7-5 가 「아이콘 영문 노출 · 확정 결함」으로 적어 둔 자리이고, `m/home.js:360` 은
   `class:"ico ms"` 로 **우회**하고 있었다 — 우회가 있다는 것이 곧 선택자가 좁다는 증거다.
   🔑 ***대상을 이름으로 못박으면 새로 생기는 것은 언제나 그 밖에 선다*** (이 레포가
      `SHEET=02_price.html` · `verify_price` 하한 · 게이트 glob 에서 이미 세 번 밟았다).
   → `.ico` **자체**를 아이콘으로 정의한다. 그릇을 세지 않으므로 다음 자리도 자동으로 산다.
   ⚠ 전수 확인: `.ico` 사용처는 4곳이고 **전부 아이콘**이다(사이드바·탭바·회원 홈·회원 헤더).
     아이콘이 아닌 용도로 이 이름을 쓰는 곳은 없다. */
.ms, .ico {
  font-family: "Material Symbols Rounded";
  font-weight: normal; font-style: normal; line-height: 1;
  letter-spacing: normal; text-transform: none; display: inline-block;
  white-space: nowrap; word-wrap: normal; direction: ltr;
  -webkit-font-smoothing: antialiased;
  font-variation-settings: "opsz" 22, "wght" 400, "FILL" 0, "GRAD" 0;
}

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink);
  /* 🔴 43차-후속 — **한글이 어절 중간에서 끊기고 있었다.** 브라우저 기본값(`word-break: normal`)은
     한국어를 **글자 단위로** 끊어도 된다고 본다. 그래서 칸이 조금만 좁으면 이렇게 나온다
     (실브라우저 실측 · 2026-08-19):
       · 「1,200,000‖원」 · 「약침 5‖회」 · 「운동 45‖분」 — **숫자와 단위가 갈라진다**
       · 「빠졌습‖니다」 · 「뷰‖가 계산합니다」 · 「DAEGU 설‖명한의원」
     화면별 건수: 회원 상세 **13** · 설정 **11** · 회원 목록 **7** · 홈(390px) **5** ·
     회원 today(320px) 6 · 이용내역(320px) 5. 데스크·연락은 0.
     🔑 *금액과 단위가 두 줄로 갈라지면 그건 조판이 아니라 값을 읽기 어렵게 만드는 일이다.*
     → `keep-all` 은 **어절(띄어쓰기) 단위로만** 끊는다 — 한국어 웹의 표준 관행이다.
     ⚠ 대가를 재고 넣었다: 320~1024px 13화면 실측에서 **가로 넘침 0 유지**, 문서 높이는
       같거나 **오히려 줄었다**(직원 설정 1538→1330 · 회원 목록 2927→2727 — 접히던 줄이 폈다).
       늘어난 곳은 이용내역 320px **한 줄(+24px)** 뿐이다.
     🔴 **`overflow-wrap: break-word` 를 짝으로 넣었다가 걷어냈다.** 「긴 어절은 끊어서라도
       안 넘치게」가 맞는 말 같았는데, 넣고 재 보니 캘린더 칸(41px)에서 **「1,180kc‖al」**
       12자리가 새로 생겼다 — 옛 판(「약」 / 「1,180kcal」)보다 **나쁘다**. 고치려던 것과
       같은 결함을 다른 자리에 만든 것이다.
       🔑 *안전망이 실제로 무엇을 하는지 재지 않으면, 안전망이 새 결함원이 된다.*
       ⚠ 걷어내도 넘치지 않는다 — 320~1024px 13화면 실측 **가로 넘침 0**(좁은 자리는 이미
         `overflow-wrap: anywhere` 를 자기 자리에서 선언해 두었다: `.x-pre`·`.kv .v`·
         `.meal .items`·`.x-kv dd`·`.bub`). 없는 위험에 대비하느라 있는 조판을 깨지 않는다. */
  word-break: keep-all;
  /* 목업 v6 :36 실측 — 단색이 아니라 아주 옅은 3색 그라디언트다. 카드가 뜨는 느낌이
     여기서 나온다(--ground 는 그 중간값이라 폴백으로 남긴다). */
  background: var(--ground);
  background: linear-gradient(165deg, #F4F2EC 0%, #EFEDE6 45%, #EFF2E0 100%) fixed;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

/* ── 타이포 ─────────────────────────────────────────────────────────────── */
.t-display { font-size: var(--fs-display); font-weight: 800; letter-spacing: -.02em; line-height: 1.15; }
.t-h1      { font-size: var(--fs-h1);  font-weight: 700; letter-spacing: -.015em; line-height: 1.3; }
.t-h2      { font-size: var(--fs-h2);  font-weight: 700; letter-spacing: -.01em; line-height: 1.4; }
.t-sm      { font-size: var(--fs-sm); }
.t-xs      { font-size: var(--fs-xs); }
.t-dim     { color: var(--ink-70); }
.t-mute    { color: var(--ink-45); }
.t-num     { font-variant-numeric: tabular-nums; }   /* 체중·금액이 흔들리지 않게 */
/* 🔴 **보는 숫자** 전용 (2026-08-07 · 결단 ⑥) — 체중·금액·잔여 개월 같은 대형 숫자.
   본문 안 숫자에는 쓰지 않는다: 한 문장 안에서 한글과 Baloo 2 가 섞이면 baseline 이
   어긋나 오히려 지저분해진다. 「큰 숫자 하나 + 작은 단위」 형태에만 붙인다. */
.t-dn      { font-family: var(--font-num); font-variant-numeric: tabular-nums;
             font-weight: 600; letter-spacing: -.01em; }

/* ── 절 제목 (23차 · 2026-08-07) ────────────────────────────────────────────
   🔴 **목업의 카드 제목은 `<b>` 다** — 본문 크기 · 700 · 먹색. 실측:
      mockup:398 `<b>체중 · 측정</b>` · :479 `<b>재고 · 사용</b>` ·
      :442 `<b>하루 기록 <span class="dim sm">— 식단 · 복약</span></b>`.
   🔴 그런데 회원 앱은 그 자리를 **전부 `t-sm t-dim`**(14px · `--ink-70`)으로 쓰고 있었다
      (23차 실측: 지역 `section()` 3벌 + 손 조립 14자리 · 요소가 div/section/article 3종,
      제목 클래스가 `t-sm t-dim`/`t-xs t-mute`/`t-sm` 3종으로 갈려 있었다).
      → **위계가 뒤집힌다.** 카드 안의 값이 제목보다 무겁고, 폰에서 절의 경계가 안 읽힌다.
   ⚠ 태그는 `<b>` 가 아니라 `<h2>` 다 — 목업은 그림이라 표제 구조가 필요 없지만 이 화면은
     스크린리더가 읽는다. 🔑 *보이는 것은 목업을 따르고, 읽히는 것은 표제를 따른다.*
   ⚠ 곁말(`.sub`)은 제목 **뒤**에 붙는다(목업 :442 와 같은 자리). 개수는 `.cnt` 가 따로 맡고,
     그 칩은 「무엇을 셌는지」가 읽는 순서에서 사라지지 않게 제목 **바로 뒤**에 온다. */
.card-t { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap;
          font-size: var(--fs-body); font-weight: 700; color: var(--ink); line-height: 1.4; }
.card-t .sub { font-size: var(--fs-sm); font-weight: 500; color: var(--ink-70); }
/* 제목 줄 오른쪽으로 미는 자리(곁버튼) — 없으면 아무것도 안 늘어난 채 왼쪽에 모인다. */
.card-t .sp  { flex: 1 1 auto; }
/* 제목이 문일 때 — 시각은 그대로이고 지나갈 때만 「문」임을 말한다(`a.stat` 과 같은 문법). */
.card-t a { color: inherit; }
.card-t a:hover, .card-t a:focus-visible { text-decoration: underline; }

/* ── 글 한 줄이 통째로 문인 자리 (43차 신설) ────────────────────────────────
   🔴 **이 저장소에 「링크처럼 생긴 링크」 어휘가 없었다.** 전역 `a { color: inherit;
      text-decoration: none }`(:59)이 있고 문(門)은 전부 버튼·행·탭·타일 **모양**이라
      그것으로 충분했다. 그런데 문장 한 줄이 통째로 문인 자리가 생겼고, 어휘가 없으니
      화면은 **아무 표시도 없는 회색 글자**를 그렸다. 실측(2026-08-19 · 실브라우저):
        · 회원 이용내역 「남은 포수 · 블랙 60포 …」 — 접힌 출고 목록으로 가는 문(MA-16).
          그 커밋 주석이 스스로 *"지금까지는 회색 글자라 갈 데가 없었다"* 고 적었는데,
          **고친 뒤에도 회색 글자였다** — `<a>` 만 씌우고 모양은 그대로였다(19px · 밑줄 0 ·
          색이 둘레 문단과 **같은 `--ink-70`**). 즉 DOM 은 바뀌고 화면은 안 바뀌었다.
        · 데스크 「이 기기 시작 화면 다시 고르기」 — `t-mute`(종이 위 **3.4:1**) · 19px.
      🔑 *어휘가 없으면 화면은 그것을 인라인으로 적는다 — 여기서는 **아무것도 안 적었다.***
   ⚠ hover 로 밑줄을 켜지 않는다 — 회원은 폰이고 실장은 태블릿이라 **hover 가 없다**
     (이 저장소가 `title` 을 금지하고 `.tbl` 에 펼침 표식을 세운 것과 같은 축). 밑줄은 상시다.
   ⚠ 이름이 `.link` 가 아니라 `.link-line` 인 이유 — **한 줄이 통째로 문인 자리 전용**이다.
     `min-height` 를 들고 있어 문장 **중간**에 쓰면 그 줄만 44px 로 부푼다. 문장 안 링크가
     실제로 필요해지는 날 그때 `.link` 를 따로 만든다(지금 만들면 쓰이지 않는 규칙이 하나 는다 —
     `.fh` 가 그 형태로 한동안 죽어 있었다). */
.link-line {
  display: inline-flex; align-items: center; min-height: var(--touch);
  color: var(--ink); text-decoration: underline;
  text-decoration-color: var(--ink-45); text-underline-offset: 3px;
}
.link-line:focus-visible {
  outline: 3px solid var(--lime-shade); outline-offset: 2px; border-radius: var(--r-sm);
}

/* 브랜드북 강조 = 볼드 + 라임 하이라이트 */
.t-mark {
  font-weight: 700;
  background: linear-gradient(transparent 58%, var(--lime) 58%);
  padding: 0 .1em;
}

/* ③ 섹션 머리 — 큰 숫자 + 제목 + 부제 */
.sec-head { margin-bottom: var(--sp-5); }
.sec-head .num { font-size: var(--fs-display); font-weight: 800; line-height: 1; letter-spacing: -.03em; }
.sec-head .ttl { font-size: var(--fs-h1); font-weight: 700; margin-top: var(--sp-2); }
.sec-head .sub { color: var(--ink-70); margin-top: var(--sp-1); }

/* ② 챕터 바 — 먹색 바탕에 흰 글씨 */
.chapter-bar {
  background: var(--ink); color: var(--paper);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  font-weight: 700; letter-spacing: -.01em;
}

/* ── 레이아웃 ───────────────────────────────────────────────────────────── */
.page   { max-width: var(--content-max); margin: 0 auto; padding: var(--sp-5) var(--sp-4) var(--sp-8); }
.read   { max-width: var(--read-max); }
.stack > * + * { margin-top: var(--sp-4); }
/* 🔴 **밀도 3단** (M-09 · 2026-08-07) — 위 한 줄이 예외 없이 16px 이라 회원 여덟 화면이
   전부 같은 리듬이었다. **균일 간격은 위계의 부재다** — 절과 카드와 행이 같은 무게로 깔리면
   읽는 사람은 무엇이 묶음이고 무엇이 그 안쪽인지 **간격으로는 알 수 없다.**
   절 24 / 카드 16 / 행 8 — 세 값은 `--sp-*` 에서 그대로 가져온다(새 수를 만들지 않는다).
   ⚠ **선언 순서가 규칙이다** — 특이도가 `.stack` 과 같아서(둘 다 클래스 1) 늦게 선언된 쪽이
     이긴다. 이 두 줄을 `.stack` **위로** 올리면 조용히 무효가 되고 오류는 나지 않는다.
   🔴 이 어휘가 없던 동안 화면은 그것을 **인라인으로 적었고 그 인라인은 아무 일도 안 했다** —
      `.stack` 은 flex 가 아니라서 `style="gap"` 이 한 픽셀도 안 움직인다. 회원 1자리
      (`m/condition` 최근 줄 `gap:2px`) · 직원 1자리(`staff/connect` 슬롯 `gap:6px`)가 그렇게
      살아 있었고 **여덟 층이 전부 초록**이었다(형제가 늘 옆 칸에 있다 — 이 레포 다섯 번째).
      🔑 *어휘가 없으면 화면은 그것을 인라인으로 적고, 인라인은 처방이 아니라 그 화면의 사정이 된다.*
      → 죽은 인라인 간격은 `ui.test` 「밀도 3단」 절이 **앱 전체**에서 0 으로 못박는다. */
.stack--lg > * + * { margin-top: var(--sp-5); }   /* 절 사이 — 카드와 카드, 절과 절 */
.stack--sm > * + * { margin-top: var(--sp-2); }   /* 행 사이 — 한 목록 안, 한 행 안 */
.row    { display: flex; align-items: center; gap: var(--sp-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.grow   { flex: 1 1 auto; min-width: 0; }
/* 🔴 **회원 화면은 폰으로 본다** — `.row`/`.row-between` 에는 `flex-wrap` 이 없어서
   320px 폭에서 이름 + 입력칸 + 버튼이 서로를 밀어낸다(2026-08-05 감사).
   `m/condition` 이 이미 인라인 `flexWrap: "wrap"` 으로 그 처방을 쓰고 있었는데
   **한 자리에만** 있었다 — 인라인이라 다른 화면이 그것을 못 답습했다.
   🔑 *한 화면이 찾은 처방을 인라인으로 두면, 그것은 처방이 아니라 그 화면의 사정이 된다.* */
.row-wrap { flex-wrap: wrap; }

/* ④ 전폭 사진 — 지면처럼 크게 한 장 */
.hero-photo {
  width: 100%; border-radius: var(--r-lg); overflow: hidden;
  background: var(--ink-08);
}
.hero-photo img { width: 100%; height: auto; object-fit: cover; }

/* ── 표면 ───────────────────────────────────────────────────────────────── */
.card {
  background: var(--paper);
  border: 1px solid var(--ink-08);
  border-radius: var(--r-md);
  padding: var(--sp-4);
  box-shadow: var(--shadow-1);
}
.card-quiet { background: var(--paper-dim); border-color: transparent; }
.card-lime  { background: var(--lime-wash); border-color: var(--lime-deep); }

/* ── 접힘 상자 (2026-08-06) — 이력은 접고 현재를 앞세운다 ─────────────────────
   summary 는 터치 타깃 44px 규약을 지킨다(회원·직원 공용 헬퍼라 예외 없음). */
.fold {
  background: var(--paper);
  border: 1px solid var(--ink-08);
  border-radius: var(--r-md);
}
.fold > summary, .fold > .fold-head {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--touch); padding: var(--sp-3) var(--sp-4);
  cursor: pointer; list-style: none; user-select: none;
}
.fold > summary::-webkit-details-marker,
.fold > .fold-head::-webkit-details-marker { display: none; }
.fold > summary::before, .fold > .fold-head::before {
  content: "▸"; color: var(--ink-45); font-size: var(--fs-sm);
  transition: transform .12s ease;
}
.fold[open] > summary::before,
.fold[open] > .fold-head::before { transform: rotate(90deg); }
/* 🔴 21차 — 제목은 **늘어나지 않는다**. 늘리면 개수 칩이 줄 맨 오른쪽으로 밀려
   「무엇을 센 숫자인지」가 읽는 순서에서 사라진다(목업 :459·:469·:697 은 전부
   `제목 [n]` 로 붙여 둔다). 남는 폭은 곁말(`.fold-tail`)이 먹고, 곁말이 없으면
   아무것도 안 늘어난 채 왼쪽에 모인다 — 그것이 목업의 모양이다. */
.fold .fold-title { font-weight: 700; min-width: 0; }
.fold .fold-tail  { flex: 1 1 auto; min-width: 0; color: var(--ink-45);
                    font-size: var(--fs-sm); }
.fold > .fold-body { padding: 0 var(--sp-4) var(--sp-4); }
/* 🔴 43차 — **이미 판 위면 판을 겹치지 않는다**(`.table-wrap` :605 과 같은 규약·같은 이유).
   `.fold` 는 홀로 설 때를 기준으로 자기 판(흰 배경 + 테두리 + 안쪽 여백)을 들고 있다.
   그런데 회원 화면 둘(`/m/today/` 「최근 기록」 · `/m/quick/` 「빠뜨린 날」)은 그것을
   **흰 카드 안에** 넣는다 — 실측 390px:
     · 배경 `rgb(255,255,255)` 위에 `rgb(255,255,255)` · 테두리 색까지 카드와 **같다**
       → 판이 아니라 **떠도는 선 하나**로 읽힌다.
     · 여백이 두 벌 얹혀 제목 왼쪽이 카드 안쪽 콘텐츠보다 **35px** 안으로 들어간다
       (카드 30 ↔ 접힘 제목 65). 카드 안에서 한 줄만 들여쓰기가 다르다.
   ⚠ 홀로 서는 여덟 자리(이용내역 2 · 컨디션 · 실장 홈 · 회원 상세 4)는 **안 건드린다** —
     거기서는 그 판이 맞다. 그래서 `.card` **안쪽**으로만 좁힌다.
   → 판을 걷고 **위 실선 하나**만 남긴다. 접힌다는 신호는 판이 아니라 ▸ 표식과 제목이 든다. */
.card .fold, .card-quiet .fold, .card-lime .fold {
  background: none; border: 0; border-top: 1px solid var(--ink-08); border-radius: 0;
}
.card .fold > summary, .card .fold > .fold-head,
.card-quiet .fold > summary, .card-lime .fold > summary { padding-inline: 0; }
.card .fold > .fold-body,
.card-quiet .fold > .fold-body, .card-lime .fold > .fold-body { padding-inline: 0; }

/* 분류 칩 — 무채(먹)가 기본. 🔴 색 헌법(2026-08-06 대표님 확정): 화면 안 색은
   먹·라임(포인트)·웜 그라운드 + 상태 3색(ok/warn/stop)뿐이다. 설명서에서 왔던
   파스텔 4색(민트·하늘·라벤더·핑크)은 이날 토큰째 제거했다 — 칩을 새로 만들 때
   여기 있는 로스터(chip / chip-lime / chip-stop / chip-off) 밖의 색을 만들지 말 것. */
/* ⚠ 41차-후속 — **`.chip` 은 글자 크기를 스스로 선언한다.** 그래서 `class="chip t-sm"`
   처럼 크기 유틸을 얹어도 **뒤에 오는 이 선언이 이겨** 아무 일도 안 일어난다(둘 다 클래스
   하나라 명시도가 같다 · `.t-sm` 은 위 :65). 41차가 그 무효 조합을 3곳 늘렸고 41차-후속에
   전건(4곳) 걷었다 — 칩 안에서 크기를 바꾸고 싶으면 **여기 규칙을 갈래로 나눠야** 한다.
   🔑 *선언한 클래스가 화면에 아무 흔적도 안 남기면, 붙인 사람은 붙였다고 믿는다.* */
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 4px 10px; border-radius: var(--r-full);
  font-size: var(--fs-xs); font-weight: 600; color: var(--ink);
  background: var(--ink-08);
}
/* 칩 묶음 — 필터·세그먼트로 쓰는 칩 버튼들의 한 줄. 좁은 폭에서 줄바꿈한다.
   (§2-8 보드 종류 필터가 처음 쓴다 — 없는 클래스는 오류 없이 무시되므로 먼저 만든다.) */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* ── 인증 판형 (§2-1 공통화 · 2026-08-06) — 로그인·가입이 같은 뼈대를 쓴다 ──
   수직 중앙 + 최대 440px + 푸터. 두 화면이 인라인 스타일 두 벌로 갈라져 있던 것을
   한 벌로(가입은 수직 중앙·푸터가 아예 없었다 — §4 규칙 5 「가입 톤이 기준선」의 역설). */
.page-auth { min-height: 100dvh; display: grid; grid-template-rows: 1fr auto;
             background: var(--ground); }
.page-auth > .page  { max-width: 440px; width: 100%; align-self: center; }
.page-auth > footer { padding-top: 0; color: var(--ink-45); }
/* 🔴 **누를 수 있는 칩은 44px 을 지킨다** (2026-08-05 · 감사)
   `.chip` 은 원래 *상태를 보여주는 조각*이라 작아도 됐다. 그런데 `m/health` 의
   제공자 고르개(카카오톡 / 통신사 PASS)가 `.chip` 으로 만들어진 **버튼**이었고,
   `padding: 4px` + `--fs-xs` 라 실제 터치 높이가 약 26px 이었다 — 같은 CSS 가
   `.btn`·`.input`·`.check` 에는 전부 `--touch`(44px)를 거는데 이 자리만 빠져 있었다.
   ⚠ 회원 화면은 **폰으로** 쓴다. 26px 짜리 조작부는 손가락으로 정확히 못 누른다.
   🔑 *같은 클래스가 보여주기도 하고 누르게도 하면, 누르는 쪽 규격을 따라야 한다.*
   → 요소 선택자를 붙여 **버튼일 때만** 규격을 올린다(표시용 칩은 그대로 작다). */
button.chip { min-height: var(--touch); padding-inline: var(--sp-4); }
/* 누르는 칩의 **고른 상태** — 먹색 배경 + 종이색 글자(`.btn-primary` 와 같은 문법).
   클래스가 아니라 `aria-pressed` 로 건다: 상태의 정본이 이미 그 속성이고(m/health 가
   갱신한다), 같은 사실을 클래스로 한 번 더 적으면 둘이 갈라진다(라벨-내용 단일 출처). */
button.chip[aria-pressed="true"] { background: var(--ink); color: var(--paper); }

.chip-lime { background: var(--cat-lime); }
/* 🔴 **직원 콘솔만 목업 판** (결단 3 회신 · 2026-08-10 · 블록 11).
   목업 v6(:89)은 wash+실선+라임잉크인데, 그 지시의 주어가 「직원용은」이고(위 :262 지시
   원문 · 목업 `<title>` 부터 직원 콘솔 · 회원 화면 0장) 회원까지 덮을지는 코드가 정할 일이
   아니라 결단으로 올렸다 — 회신 = **직원만**. 회원 흰 카드 위 통짜 라임(홈 「완료」 칩 등)은
   「채운 라임 ↔ 테두리만」이 신호라 그대로 둔다(wash 는 흰 판에서 속삭임이 된다). */
.shell .chip-lime {
  background: var(--lime-wash); color: var(--lime-ink);
  border: 1px solid var(--lime-deep);
}
/* 기한 경과 — 판정 뷰가 준 값(지남 일수)에만 쓴다. 옅은 stop 판 + stop 글자. */
.chip-stop {
  background: color-mix(in srgb, var(--stop) 12%, var(--paper));
  color: var(--stop);
}
/* 종료·비활성 — 면을 비우고 테두리만. 「끝난 것」이 색으로 시선을 끌지 않게. */
.chip-off {
  background: transparent;
  border: 1px solid var(--ink-20);
  color: var(--ink-45);
}

/* ── 조작부 — 태블릿 우선(44px 최소) ─────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--touch); padding: 0 var(--sp-5);
  border: 1px solid transparent; border-radius: var(--r-md);
  font: inherit; font-weight: 700; letter-spacing: -.01em; white-space: nowrap;
  cursor: pointer; background: transparent; border-color: var(--ink-20); color: var(--ink);
  transition: filter .12s ease, background .12s ease;
}
.btn:hover  { background: var(--paper-dim); }
.btn:active { filter: brightness(.96); }
.btn:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: 2px; }
/* 🔴 **버튼 2계층** (2026-08-07 · 결단 ⑥). 화면마다 「지금 하려는 일」 하나만 라임이고
   나머지는 전부 선 버튼이다 — 채운 버튼이 여럿이면 무엇이 주된 행동인지가 사라진다.
   ⚠ 옛 `.btn` 은 회색을 **채운** 판이었고 그래서 모든 버튼이 똑같이 무거웠다.
   ⚠ 높이는 36px 로 줄이지 않는다 — 목업은 데스크탑 그림이고, 이 앱의 규약은
      「태블릿 우선 · 44px 는 최소값이지 목표값이 아니다」(tokens.css · 대표님 2026-07-31).
      목업과 바이블이 어긋나면 바이블이 이긴다. */
/* 🔴 **2026-08-07 20차 · 위 ⚠ 두 줄을 대표님 지시로 뒤집는다.**
   옛 판정(2026-08-07 오전)은 *"목업은 데스크탑 그림이고, 목업과 바이블이 어긋나면
   바이블이 이긴다"* 였다. 그날 저녁 대표님이 목업(v6 아티팩트)을 직접 보시고
   **"직원용은 목업과 똑같이 만들어라 · 디자인도 목업을 우선"** 이라고 못박으셨다.

   ⚠ 그렇다고 44px 규약이 죽은 것이 아니다 — **목업 자신이 44px 을 지키고 있었다.**
     목업 실측: `--touch:44px` 이 `.btn-primary`·`.tabs button`·`.field input`·`.outk`·
     `.tplchip`·`.snav a`·`.agd`·`.grow-item`·`.fold>summary` 아홉 자리에 걸려 있고,
     36px 인 것은 **곁들이는 선 버튼**(수정·복사·닫기)뿐이다.
   🔑 *바이블과 목업이 싸운 게 아니라, 「모든 버튼」과 「주된 버튼」을 내가 안 갈랐던 것이다.*
   → 그래서 **회원 PWA(폰)는 그대로 44px**, 직원 콘솔의 **곁버튼만** 목업 값(36px)으로 내린다.
     되돌리려면 아래 `.shell .btn` 한 블록만 지우면 된다. */
.shell .btn {
  min-height: 36px; padding: 4px var(--sp-3);
  border-radius: var(--r-sm); font-size: var(--fs-xs);
}
/* 주된 행동·조작부는 목업에서도 44px 이다 — 위 축소에서 도로 올린다. */
.shell .btn-primary, .shell .btn-lg, .shell button.chip { min-height: var(--touch); }
.btn-primary { background: var(--lime); color: var(--ink); border-color: var(--lime-deep); font-weight: 800; }
.shell .btn-primary { padding: 8px var(--sp-4); font-size: var(--fs-sm); border-radius: var(--r-md); }
.btn-primary:hover { background: var(--lime-deep); }
.btn-lime    { background: var(--lime); color: var(--ink); border-color: var(--lime-deep); }
/* 먹색 채움 — 되돌릴 수 없는 확정(무장 2단계의 두 번째)에만 남긴다 */
.btn-ink     { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.btn-ghost   { background: transparent; border-color: var(--ink-20); }
/* 🆕 21차 — 아이콘 하나짜리 버튼(회원 헤더의 설정 문). **정사각 44px** 을 지킨다:
   글자가 없으므로 좌우 패딩만으로는 폭이 아이콘 크기로 줄어 엄지 타깃이 무너진다.
   ⚠ `.shell` 축소 규칙(36px)의 사정권 밖이다 — 이 버튼은 회원 PWA(폰) 전용이다. */
.btn-icon { min-width: var(--touch); padding-inline: 0; justify-content: center; }
.btn-icon .ico { font-size: 22px; }
/* 🔴 무장 상태 — **먹 바탕 + 라임 글자**. 라임 CTA 와 정반대로 뒤집어 「이번 누름은
   진짜다」를 몸으로 알린다. 옛 판은 무장 시 `btn-primary` 를 붙였는데, 그 클래스가
   라임이 된 지금은 평상시 주 버튼과 무장 버튼이 **같은 색**이 되어 경고가 사라진다. */
.btn.armed, .btn-primary.armed { background: var(--ink); color: var(--lime); border-color: var(--ink); }
.btn-lg      { min-height: var(--touch-lg); font-size: var(--fs-h2); width: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
/* 🔴 **줄처럼 생긴 버튼** (2026-08-05 · 감사)
   `m/today` 의 메뉴 검색 결과가 `class="btn btn-ghost row-between"` 이었다. 그런데
   `.btn` 은 이 파일에서 `.row-between`(위쪽)보다 **뒤에** 있고 특정도가 같으므로
   **뒤엣것이 이긴다** — 즉 `justify-content: center` 와 `white-space: nowrap` 이 살아서
   ⑴ 의도한 좌우 배치가 아예 안 먹고 ⑵ **긴 음식 이름이 폰 폭에서 가로로 넘쳤다.**
   🔑 *같은 특정도의 두 클래스를 겹쳐 쓰면 이기는 쪽은 「의도」가 아니라 「파일 순서」다.*
   → 겹치기 대신 전용 클래스를 둔다. 이름이 길면 **줄바꿈**하고 왼쪽으로 정렬한다. */
.btn-row {
  justify-content: space-between; white-space: normal; text-align: left;
  padding-block: var(--sp-2);
}

/* ── 🆕 고르는 격자 — 「메뉴판」 어휘 (35차 · 2026-08-16) ──────────────────────
   대표님 지시 둘이 **같은 형태**를 요구했다: 실장의 정가표 메뉴판, 회원의 식단 메뉴판.
   둘 다 *한 번 눌러 하나를 고르는 타일 격자*다.
   🔑 ***어휘가 없으면 화면은 그것을 인라인으로 적는다*** — 두 화면이 각자 인라인 grid 를
      적으면 같은 뜻이 두 벌로 갈라지고, 그 뒤로는 나란히 낡는다(M-09 가 배운 것).
      그래서 **화면보다 먼저** 여기 한 벌을 만든다.

   🔴 `.btn` 뒤에 선다 — 특정도가 같아 앞에 두면 `justify-content:center`·`nowrap` 이
      이겨서 세 줄 조판이 조용히 무너진다(위 `.btn-row` 가 그 자리에서 배운 것).
   🔴 고른 상태는 `aria-pressed` 로만 건다(`.chip`·`.outk` 와 같은 판). 클래스로 한 번 더
      적으면 상태의 정본이 둘이 된다. */
.pickgroup > .pg-t { font-weight: 700; margin-bottom: var(--sp-2); }
.pickgrid {
  display: grid; gap: var(--sp-2);
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.pickcard {
  /* 세 줄이 들어가므로 `.btn` 의 한 줄 규격(44px·가운데·nowrap)을 통째로 되돌린다. */
  min-height: 84px; height: 100%;
  flex-direction: column; align-items: flex-start; justify-content: flex-start;
  gap: 2px; padding: var(--sp-3); white-space: normal; text-align: left;
  background: var(--paper);
}
/* ⚠ 본문 행간(1.65)은 두 줄짜리 상품명에서 너무 벌어진다. **토큰을 새로 만들지 않고**
   이 자리에서만 좁힌다 — 한 자리에 필요한 값이 토큰이 되면 쓰이지 않는 토큰이 는다. */
.pickcard .pc-name { font-weight: 700; line-height: 1.25; }
.pickcard .pc-qty  { font-size: var(--fs-xs); font-weight: 500; color: var(--ink-70); }
/* 금액은 표·카드와 같은 tabular 숫자 — 격자에서 자릿수가 흔들리면 비교가 안 된다. */
.pickcard .pc-now  { margin-top: auto; font-size: var(--fs-h2); font-weight: 800;
                     font-variant-numeric: tabular-nums; }
.pickcard .pc-was  { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
                     font-size: var(--fs-xs); font-weight: 500; color: var(--ink-70); }
.pickcard .pc-on   { font-size: var(--fs-xs); font-weight: 700; }
/* 고름 — `.outk` 와 **같은 신호**(먹 테두리 + 라임 링). 새 신호를 만들지 않는다. */
.pickcard[aria-pressed="true"] { border-color: var(--ink); box-shadow: 0 0 0 3px var(--lime); }

.field { display: block; }
.field > .label { display: block; font-size: var(--fs-sm); font-weight: 600; margin-bottom: var(--sp-2); }
.input {
  width: 100%; min-height: var(--touch); padding: 0 var(--sp-3);
  font: inherit; color: var(--ink);
  border: 1px solid var(--ink-20); border-radius: var(--r-md); background: var(--paper);
}
.input:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 3px var(--lime); }
/* 🔴 `.input` 의 `padding: 0 …` 는 한 줄짜리 input·select 기준이다 — textarea 에 그대로
   걸리면 **첫 줄이 위 테두리에 붙는다**(실사용 6곳). 세로만 되돌린다. */
textarea.input { padding-block: var(--sp-2); line-height: var(--lh-body); resize: vertical; }
/* 입력 아래 도움말 한 줄 — 자리마다 인라인 marginTop 이 달라지던 것을 한 규칙으로.
   🔴 **정확히 그 용도로 정의해 두고 앱 전체 사용 0회**였다(M-07 · 2026-08-07). 두 가지가
      막고 있었다: ⑴ `.field` 안으로 **한정**돼 있어 같은 도움말을 그리는 `.check` 본문은
      쓸 수 없었고 ⑵ 정작 부품(`field`·`pwField`·`checkbox`)이 제 안에서 `t-xs t-mute` +
      인라인 style 을 붙이고 있어 **화면 파일에서는 지울 수 없는 자리**였다.
      🔑 *쓰이지 않는 규칙은 없는 규칙과 같다 — 다만 있는 것처럼 보여서 더 나쁘다.*
   🔴 색을 `--ink-45`(3.4:1) → `--ink-70`(7.5:1). 폼 힌트는 **범위·형식·기한**처럼 틀리면
      저장이 거부되는 말이라 장식이 아니다(§7-5 MEDIUM 이 화면 11곳에 이미 적용한 판단).
      ⚠ 크기(`--fs-xs`)는 그대로 둔다 — 위계는 여전히 크기가 만든다. */
.fh { display: block; font-size: var(--fs-xs); color: var(--ink-70); margin-top: 3px; }

/* ── 직원 콘솔 셸 ───────────────────────────────────────────────────────── */
.shell { min-height: 100dvh; display: grid; grid-template-columns: var(--nav-w) 1fr; }
.sidenav {
  background: var(--nav-bg);
  border-right: 1px solid var(--ink-08);
  padding: var(--sp-4) var(--sp-3); display: flex; flex-direction: column; gap: 2px;
}
.sidenav .brand { padding: var(--sp-2) var(--sp-2) var(--sp-5); }
/* 🔴 밝은 판으로 바꾸면서 **반전 필터를 뗐다**(2026-08-07). 먹색 단색 로고라 흰 바탕에
   그대로 얹으면 되고, 필터를 남겨 두면 흰 로고가 흰 판 위에서 통째로 사라진다.
   토큰만 갈아끼우고 이 한 줄을 잊으면 화면에 아무것도 없는 자리가 생긴다. */
.sidenav .brand img { height: 24px; width: auto; }
/* 브랜드 배지 — 라임은 이렇게 **작은 면**에만 쓴다(브랜드북 규칙 ①) */
.sidenav .brand .badge {
  display: inline-block; margin-top: var(--sp-2);
  background: var(--lime); color: var(--ink);
  font-family: var(--font-num); font-weight: 700; font-size: var(--fs-xs);
  letter-spacing: .04em; padding: 2px 8px; border-radius: var(--r-sm);
}
.navlink {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--touch); padding: 0 var(--sp-3);
  border-radius: var(--r-sm); font-weight: 500; color: var(--nav-dim);
}
.navlink:hover { background: var(--nav-hover); color: var(--nav-lit); }
/* 활성 = 아주 옅은 라임 판 + 라임 실선 하나. 라임을 진하게 면으로 깔지 않는다(규칙 ①).
   🆕 21차 — `[aria-current]` 전체를 잡는다. 하위 화면(상세·신규)에서는 `markCurrent` 가
   가장 긴 접두사에 `aria-current="true"` 를 붙이는데, 옛 선택자는 `"page"` 만 봐서
   **그 표시가 화면에 아무 흔적도 안 남겼다**(붙였는데 안 보이면 안 붙인 것과 같다). */
.navlink[aria-current] {
  background: var(--nav-active); color: var(--nav-lit); font-weight: 700;
  box-shadow: inset 3px 0 0 var(--lime-deep);
}
.navlink .ico { width: 22px; text-align: center; flex: 0 0 auto; font-size: 20px; }
/* 채운 아이콘은 **바로 이 화면**일 때만. 계열(`true`)은 판만 켜고 아이콘은 선으로 둔다 —
   「여기 있다」와 「이 아래에 있다」를 눈으로도 가른다. */
.navlink[aria-current="page"] .ico { font-variation-settings: "opsz" 22, "wght" 400, "FILL" 1, "GRAD" 0; }
.topbar {
  position: sticky; top: 0; z-index: 5; background: var(--paper);
  border-bottom: 1px solid var(--ink-08);
  /* 🔴 21차 — `flex-wrap` 이 없어서 제목 + extras(대기·지점 줄) + 총괄의 지점 선택기가
     한 줄에 서다가 태블릿 세로(1024↓)에서 **넘쳤다**. 넘친 자리에는 표시가 안 남는 종류다.
     ⚠ 이 규칙은 staff 12판 공용이라 화면 파일에서는 못 닫는다. */
  min-height: 58px; display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-5);
}

/* 얇은 구분선 — 카드 안에서 「위는 트랙, 아래는 합계」처럼 뜻을 가르는 자리. */
.hairline { border: none; border-top: 1px solid var(--ink-08); margin: var(--sp-3) 0; }

/* ── 내원 캘린더 (홈 · vNext A-1) ─────────────────────────────────────────
   🔴 날짜 칸에 **이름을 적는다.** 점이나 숫자만 있으면 "누가 오는지"를 알려고 또 눌러야
      하고, 그 한 번이 캘린더를 「달력 그림」으로 만든다.
   ⚠ 칸 높이를 고정한다 — 이름 수에 따라 주마다 높이가 달라지면 달이 출렁인다. */
.cal { width: 100%; border-collapse: collapse; table-layout: fixed; }
.cal th {
  font-size: var(--fs-xs); color: var(--ink-45); font-weight: 500;
  padding: var(--sp-1) 0; text-align: center;
}
.cal td {
  border: 1px solid var(--ink-08); height: 66px; vertical-align: top;
  padding: 3px 5px; overflow: hidden;
}
.cal .cal-day { cursor: pointer; }
.cal .cal-day:hover { background: var(--paper-dim); }
.cal .cal-day:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: -3px; }
/* 🔴 **1.36 → 3.54** (27차). 지난달·다음달 날짜가 `--ink-20` 이라 **사실상 안 보였다** —
   흐려야 하는 자리인 것은 맞지만, 「이 달이 아니다」와 「글자가 없다」는 다른 말이다.
   ⚠ 비활성 조작부는 WCAG 예외지만 이 칸은 **날짜라는 정보**를 든다(달을 넘길 때 눈이 쓴다). */
.cal .cal-off { background: var(--paper-dim); cursor: default; color: var(--ink-45); }
/* 오늘 = 라임 테두리 + 아주 옅은 판. 라임을 진하게 깔지 않는다(브랜드북 규칙 ①). */
.cal .is-today { border: 2px solid var(--lime-deep); background: var(--lime-wash); }
.cal .is-picked { outline: 3px solid var(--lime-shade); outline-offset: -3px; }
.cal .cal-nm {
  display: block; font-size: 11px; font-weight: 700; line-height: 1.45;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── 시트(약속 잡기 등) ───────────────────────────────────────────────────
   `<dialog>` 를 쓴다 — ESC 닫기와 포커스 가두기를 브라우저가 해 준다. */
.sheet {
  border: none; border-radius: var(--r-lg); padding: var(--sp-5);
  width: min(560px, calc(100vw - var(--sp-5)));
  box-shadow: var(--shadow-2); color: var(--ink); background: var(--paper);
}
.sheet::backdrop { background: rgba(35, 25, 22, .3); }

/* ── 회원 PWA 셸 ──────────────────────────────────────────────────────────
   🔴 폰 레이아웃이므로 큰 화면에서 **폭을 묶는다.** 안 묶으면 데스크탑에서
      사진이 1500px 로 늘어나고 하단 탭이 화면 끝까지 벌어져 앱처럼 보이지 않는다.
      (2026-07-31 육안 검증에서 실제로 그렇게 나왔다 — 렌더를 봐야 잡히는 종류다.) */
.m-shell {
  min-height: 100dvh;
  max-width: var(--m-max); margin-inline: auto;
  border-inline: 1px solid var(--ink-08);
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
}
/* 🔴 **회원 표면의 곁말 대비** (결단 2 회신 B · 2026-08-10 · 블록 10).
   주 사용자가 중년 다이어트 환자이고 폰이다 — 탭 라벨·표 열 이름·곁말이 상시 표면인데
   `--ink-45`(#8C8378)는 종이 위 3.73:1 로 4.5:1 미달이었다(F-11 · CSS 가 회원에
   도달시키는 자리 10 + 앞으로 생길 자리 전부). 목록 패치가 아니라 **표면 스코프**로
   토큰을 다시 묶는다 — 직원 73자리는 이 밖이라 안 움직인다(`.shell .btn` 36px 판례).
   ⚠ **알고 고른 대가**: 이 안에서 `.t-mute` 와 `.t-dim` 이 같은 색(#5C5348 = --ink-70)이
     되어 곁말 위계 한 단이 사라진다(크기 차이만 남는다) — 결단지에 올렸고 회신 B 로 확정.
   ⚠ `.tabbar` 는 `position:fixed` 지만 DOM 은 `.m-shell` 의 자식이라 커스텀 프로퍼티가
     상속으로 닿는다(셀렉터 override 였다면 활성 탭 강조를 이길 위험 — 그래서 토큰으로). */
.m-shell, .page-auth { --ink-45: #5C5348; }
/* 🔴 **상시 헤더** (28차 블록 5) — 로고 + 설정 톱니 한 줄만 든다. 제목·부제는 `.m-title` 로
   내려가 함께 흘러간다. 값 넷(`position`·`z-index`·`background`·아래 테두리)은 직원
   `.topbar`(:366-368)에서 **그대로** 가져왔다 — 새 수를 만들지 않는다.
   ⚠ 제목까지 이 안에 두면 상시 크롬이 206px 이 되어 667px 폰의 31% 가 영구히 죽는다. */
.m-head {
  position: sticky; top: 0; z-index: 5; background: var(--paper);
  border-bottom: 1px solid var(--ink-08);
  padding: var(--sp-3) var(--sp-4);
}
/* 제목 묶음 — `main` **밖**이다. 안에 두면 화면이 `mount()`(replaceChildren)로 지운다. */
.m-title { padding: var(--sp-5) var(--sp-4) 0; }
.m-title .t-h1 { margin: 0; }
/* 제목이 위 여백을 이미 줬으므로 본문은 위를 좁힌다(두 벌로 벌어지지 않게). */
.m-title + .page { padding-top: var(--sp-4); }
.m-shell .page { max-width: none; }
/* 🆕 41차 — **절 점프 줄**(`/m/today/`). 이 화면은 카드 넷이 세로로 길고 저장 버튼 셋이
   전부 `btn-lg btn-primary` 전폭이라, 스크롤 도중에는 「지금 어느 절인가」가 화면에서
   사라진다(버튼 글자는 다르지만 그건 그 버튼에 닿았을 때의 이야기다).
   🔴 자리를 **상시로** 둔다 — 맨 위에만 두면 한 번 지나친 뒤로는 없는 것과 같다.
   🔴 `top` 은 상시 헤더(`.m-head`) 높이에서 **유도한다**(새 수 0): 세로 여백 두 벌 +
      터치 규격 + 아래 테두리 1px. 손으로 69px 이라고 적으면 헤더 규격이 바뀌는 날 겹친다.
   ⚠ `z-index` 는 헤더(5)보다 **낮다** — 같거나 높으면 스크롤 중에 로고·설정 톱니를 덮고,
     그 톱니가 회원이 로그아웃에 닿는 문 하나뿐이다(28차 블록 5). */
.jumpbar {
  position: sticky; z-index: 4;
  top: calc(var(--touch) + var(--sp-3) * 2 + 1px);
  background: var(--paper); border-bottom: 1px solid var(--ink-08);
  margin-inline: calc(var(--sp-4) * -1); padding: var(--sp-2) var(--sp-4);
}
/* 🔴🔴 41차-후속 — **이 줄은 반드시 한 줄이어야 한다.** 아래 `.anchored` 의 도착점 여백이
   「헤더 + 점프 줄 **한 줄**」을 더한 값이라, 줄이 둘이 되는 순간 그 계산이 조용히 모자란다
   (실측: 칩 넷의 폭 합 377.2px > 393px 기기의 회원 셸 안쪽 폭 367.0px → 10px 모자라 접혔고,
   상시 스택 182px 에 여백은 130px 이라 **도착한 절의 위 52px 이 점프 줄 밑에 깔렸다**.
   임계는 콘텐츠 폭 378px ≈ 뷰포트 404px — iPhone SE·12~15 · Galaxy 360/393 이 전부 해당이고
   안 걸리는 Pro Max·Pixel 쪽이 오히려 소수다).
   🔑 *유도식이 옳아도 그 식이 선 전제가 화면에서 깨지면 값은 그냥 틀린 값이다.*
   → **접히지 않게 못박고**(`nowrap`) 넘치면 가로로 민다. 그러면 줄 수가 기기와 무관하게
     늘 1 이라 아래 유도식이 **구조적으로** 참이 된다(px 을 손으로 적어 맞추는 것이 아니다).
   ⚠ `.chips`(공용)는 `flex-wrap: wrap` 그대로 둔다 — 필터 칩 줄들은 접히는 것이 맞다.
     여기서만 갈래를 준다.
   ⚠ 좌우 여백을 `--sp-4` → `--sp-3` 으로 좁힌다: 393px 기기에서 넷이 **가로 이동 없이**
     다 보이게 하는 값이다(377.2 − 칩당 8px × 4 = 345.2 ≤ 367.0). 44px 터치 규격은
     `min-height` 가 들고 있으므로 안 움직인다. */
.jumpbar .chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
.jumpbar .chips::-webkit-scrollbar { display: none; }
.jumpbar .chips > .chip { flex: 0 0 auto; }
.jumpbar button.chip { padding-inline: var(--sp-3); }
/* 🆕 41차 — 해시·점프 줄이 **지목하는** 절. 그냥 `scrollIntoView` 하면 절 제목이 상시 헤더와
   점프 줄 **밑으로 들어가** 「도착했는데 무엇에 도착했는지 안 보이는」 상태가 된다.
   ⚠ 값은 위 둘의 높이를 그대로 더한 것이다(헤더 + 점프 줄) — 손으로 잰 px 이 아니라
     같은 토큰에서 유도한다. 점프 줄 높이 = 누를 수 있는 칩(`button.chip` = --touch) +
     세로 여백 두 벌 + 아래 테두리.
   🔴 41차-후속 — **이 식은 점프 줄이 「한 줄」일 때만 참이다.** 실제로는 접혀서(2줄) 도착점
     52px 이 가려지고 있었다 — 식이 아니라 **전제**가 틀렸다. 그래서 위 `.jumpbar .chips` 에
     `flex-wrap: nowrap` 을 못박아 전제를 구조로 만들었다. 둘은 한 몸이다: **한쪽만 고치면
     다시 갈라진다**(그 정합을 `ui.test` 「점프 줄 ↔ 도착점 여백」 절이 잰다). */
.anchored { scroll-margin-top: calc(var(--touch) * 2 + var(--sp-3) * 2 + var(--sp-2) * 2 + 2px); }
.tabbar {
  position: fixed; left: 50%; transform: translateX(-50%);
  width: min(var(--m-max), 100%); bottom: 0; z-index: 10;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--paper); border-top: 1px solid var(--ink-08);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
}
.tabbar a {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; font-size: var(--fs-xs); font-weight: 600; color: var(--ink-45);
  min-height: var(--touch);
}
.tabbar a[aria-current="page"] { color: var(--ink); }
.tabbar a[aria-current="page"] .ico { font-variation-settings: "opsz" 22, "wght" 400, "FILL" 1, "GRAD" 0; }
.tabbar .ico { font-size: 21px; line-height: 1; }

/* ── 상태 표시 ──────────────────────────────────────────────────────────── */
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat .v { font-size: var(--fs-display); font-weight: 800; letter-spacing: -.02em; line-height: 1.05; }
.stat .k { font-size: var(--fs-sm); color: var(--ink-70); }
/* 링크가 된 타일(§2-2) — 시각은 같고, 지나갈 때만 「문」임을 말한다. */
a.stat:hover .k, a.stat:focus-visible .k { text-decoration: underline; color: var(--ink); }
.empty {
  border: 1px dashed var(--ink-20); border-radius: var(--r-md);
  padding: var(--sp-6) var(--sp-4); text-align: center; color: var(--ink-45);
}
/* 빈 상태의 **다음 행동**(M-13 · §4-a). 문장과 붙어 있으면 「읽을 것」으로 읽히므로 띄운다.
   ⚠ 상자를 두 벌로 만들지 않는다 — 행동이 없으면 이 요소 자체가 안 생긴다. */
.empty-act { margin-top: var(--sp-4); }
/* 🔴 21차 — 로딩과 빈 상태를 **눈으로 가른다.** 같은 점선 상자면 「읽는 중」이 「없음」으로
   굳고, 실장은 조회가 끝난 줄 안다. 테두리를 **실선**으로 바꿔 「아직 확정이 아니다」를
   모양으로 말하고, 맥동은 곁들임이다(모양이 먼저 · 움직임은 거들 뿐).
   ⚠ `prefers-reduced-motion` 에서는 맥동만 끄고 **실선은 남긴다** — 움직임을 끈 사람에게서
     구별 수단까지 뺏지 않는다(그러면 그 사람에게만 옛 결함이 되살아난다). */
.empty--loading { border-style: solid; animation: empty-pulse 1.2s ease-in-out infinite; }
/* 실패는 **실선 + 진한 글자**다. 색은 쓰지 않는다 — 색 헌법 §4-2 는 상태 3색을 판정 뷰가
   준 값에만 허용한다(조회 실패는 판정이 아니라 우리 사정이다). 모양만으로 갈린다. */
.empty--error { border-style: solid; border-color: var(--ink-45); color: var(--ink); }
@keyframes empty-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .55 } }
@media (prefers-reduced-motion: reduce) { .empty--loading { animation: none } }
.scaffold-note {
  background: var(--lime-wash); border: 1px solid var(--lime-deep);
  border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm); color: var(--ink-70);
}

/* ── 표 — 직원 콘솔의 기본 표시 단위 (F06·F07 부터) ──────────────────────
   🔴 넓은 표는 **자기 안에서** 가로 스크롤한다. 페이지 본문이 좌우로 밀리면
      태블릿에서 네비까지 같이 움직여 조작이 어긋난다. */
/* 🔴 21차 — **홀로 선 표는 카드 위에 앉는다**(목업 :335 `.card{padding:0;overflow:hidden}`
   + `.tbl`). 옛 판은 `.table-wrap` 이 스크롤만 맡아서, 카드를 손으로 감싼 다섯 화면
   (목록·정리됨·메뉴판·콘텐츠·설정) 밖의 표는 전부 웜 그라운드 위에 맨몸으로 누웠다.

   🔑 **호출처 25곳을 고치지 않고 여기 한 자리로 움직인다** — 부품마다 `card:true` 를
      달면 25곳을 세어 가며 갈아야 하고, 이미 카드에 든 다섯은 **이중 카드**가 된다.
      대신 *「판 위에 이미 있으면 판을 겹치지 않는다」* 를 아래 리셋이 말한다. */
.table-wrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  background: var(--paper); border: 1px solid var(--ink-08);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
}
/* 이미 판 위 — 카드·접힘 상자·펼친 행·시트 안. 여기서 또 판을 깔면 테두리가 두 겹이 된다. */
.card .table-wrap, .card-quiet .table-wrap, .card-lime .table-wrap,
.fold .table-wrap, .x-row .table-wrap, .sheet .table-wrap, .cpanel .table-wrap {
  background: none; border: 0; border-radius: 0; box-shadow: none;
}
.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
/* 카드 안에서 마지막 행의 밑줄은 카드 테두리와 겹쳐 2px 로 보인다(목업 :97 도 지운다) */
.tbl tbody tr:last-child td { border-bottom: none; }
.tbl th {
  text-align: left; font-weight: 700; color: var(--ink-45);
  font-size: var(--fs-xs); letter-spacing: .02em;
  padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--ink-20);
  white-space: nowrap;
}
.tbl td {
  padding: var(--sp-3); border-bottom: 1px solid var(--ink-08);
  vertical-align: middle;
}
/* 행 자체가 터치 타깃이다 — 44px 를 셀 패딩이 아니라 행 높이로 보장한다 */
.tbl tbody tr { min-height: var(--touch); }
.tbl tbody td { height: var(--touch); }
.tbl tbody tr[tabindex] { cursor: pointer; }
.tbl tbody tr[tabindex]:hover { background: var(--paper-dim); }
.tbl tbody tr[tabindex]:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: -3px; }
/* ── 펼 수 있는 행이라는 **신호** (M-14 · 2026-08-08) ────────────────────────────
   🔴 `cursor: pointer` 는 **마우스가 있는 환경에서만** 말한다. 실장은 태블릿으로 보고
      회원은 폰으로 본다 — 둘 다 hover 가 없으므로, 지금까지 펼칠 수 있는 행에는
      *아무 신호도 없었다.* 「누를 수 있는데 안 보이는」 것은 「눌러도 아무 일 없는」 것의
      반대편 결함이고, 이 레포는 그 짝을 「거짓말」로 이미 못박았다.
   🔑 이 저장소가 `title` 을 금지한 이유와 같은 축이다 — *hover 가 없으면 그건 아무 데도
      없는 것과 같다.*
   ⚠ `[aria-expanded]` 로 건다 — 부품은 **펼 것이 실제로 있을 때만** 그 속성을 준다
     (`expand(row)` 가 null 이면 안 준다). 그래서 이 표시가 뜨는 행은 반드시 펴진다.
     `onRowClick` 행(이동)은 `tabindex` 만 있고 이 속성이 없으므로 표시가 안 붙는다 —
     **다른 일을 하는 행에 같은 신호를 주지 않는다.**
   ⚠ 색을 쓰지 않는다(무채 `--ink-45`) — 색 헌법: 상태 3색은 판정 뷰가 준 값에만. */
.tbl tbody tr[aria-expanded] > td:first-child { position: relative; padding-left: 24px; }
.tbl tbody tr[aria-expanded] > td:first-child::before {
  content: ""; position: absolute; left: var(--sp-3); top: 50%;
  width: 6px; height: 6px; margin-top: -5px;
  border-right: 1.5px solid var(--ink-45); border-bottom: 1.5px solid var(--ink-45);
  transform: rotate(45deg); transition: transform .12s ease;
}
.tbl tbody tr[aria-expanded="true"] > td:first-child::before {
  transform: rotate(-135deg); margin-top: -2px;
}
.tbl .num { font-variant-numeric: tabular-nums; text-align: right; }

/* ── 직원 콘솔 본문 열 ─────────────────────────────────────────────────────
   🔴 flex 컨테이너로 만들지 말 것. 안쪽 `main.page` 가 `margin: 0 auto` 인데,
      flex 아이템의 좌우 auto margin 은 **stretch 를 꺼버린다** → main 이 내용
      크기로 쪼그라들어 500px 화면에서 본문이 221px 이 됐다(2026-07-31 실측).
      블록이면 그냥 채운다. */
.shell-body { min-width: 0; }

/* ── 입력 폼 격자 — 태블릿에서 2열, 폰에서 1열 ───────────────────────────── */
.form-grid {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
select.input { appearance: auto; }
/* 🔴 `.form-grid` 본체(240px)는 **덮지 않는다** — 29곳이 그 폭을 전제한다.
   좁아야 하는 자리만 자리로 좁힌다(`.addcard .form-grid` 와 같은 문법 · 새 이름 0). */
.sheet .form-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--sp-3); }

/* ── 구분선 문구 ("또는") ────────────────────────────────────────────────── */
.hr-or {
  display: flex; align-items: center; gap: var(--sp-3);
  color: var(--ink-45); font-size: var(--fs-sm);
}
.hr-or::before, .hr-or::after {
  content: ""; flex: 1; height: 1px; background: var(--ink-20);
}

/* ── 토스트 — 저장·실패를 알리되 화면을 막지 않는다 ─────────────────────── */
.toast-host {
  /* 🔴 z-index 는 **가장 위**여야 한다 — 연락 패널(90)·오버레이(80) 뒤에 깔리면
     「보냈습니다」도 「실패했습니다」도 실장 눈에 안 닿는다(토스트는 2.8초 뒤 사라진다).
     pointer-events:none 이라 위에 있어도 아래를 막지 않는다. */
  position: fixed; z-index: 120; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--tabbar-h) + var(--sp-4) + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: var(--sp-2);
  width: min(420px, calc(100vw - var(--sp-5))); pointer-events: none;
}
.toast {
  background: var(--ink); color: var(--paper);
  border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm); font-weight: 600; box-shadow: var(--shadow-2);
  opacity: 1; transition: opacity .35s ease, transform .35s ease;
}
.toast.out { opacity: 0; transform: translateY(6px); }
.toast-warn { background: var(--warn); }
.toast-err  { background: var(--stop); }

/* ── 모달 — 되돌릴 수 없는 것 앞에서만 ──────────────────────────────────── */
/* 🔴 `.modal-wrap`·`.modal` 을 지웠다(6차 감사 · 2026-08-01) — 유일한 사용처였던
   `confirmBox()` 가 부르는 곳 0건이라 함께 정리했다. 되돌릴 수 없는 동작이 생기는
   F09·F33 에서 **그때 필요한 모양으로** 다시 만든다. */

/* ── 반응형 — 태블릿이 기준, 데스크탑은 펴고 폰은 접는다 ─────────────────── */
@media (max-width: 900px) {
  /* 🔴 grid-template-rows 를 반드시 함께 준다. 없으면 암묵 행 2개가 100dvh 를
     나눠 가져 **메뉴 줄 하나가 화면 절반 높이로 늘어난다**(실측으로 잡힌 결함). */
  .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .sidenav {
    position: sticky; top: 0; z-index: 6;
    flex-direction: row; align-items: center; overflow-x: auto; gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
  }
  .sidenav .brand { display: none; }
  .navlink { white-space: nowrap; padding: 0 var(--sp-4); }
  /* 🔴🔴 41차 — **상단바가 사이드바 뒤로 들어가 있었다.** 이 폭에서 `.sidenav` 는 가로 줄로
     서서 `top:0`(z-index 6)에 붙고, `.topbar` 도 `top:0`(z-index 5)에 붙는다 — **둘이 같은
     자리를 달라고 한다.** 스크롤을 조금이라도 내리면 상단바의 위 60px 이 사이드바 밑으로
     들어가고, 그 60px 이 상단바의 **첫 줄 전부**다: 화면 제목 · [새 회원 등록] · [데스크] ·
     [발송 대장] · 지점 선택기 · 로그아웃. 실장이 폰으로 쓰는 동안 그것들은 없는 것과 같다.
     ⚠ 「조금 가린다」가 아니라 **되돌릴 방법이 스크롤 맨 위뿐**이라는 것이 결함의 크기다.

     🔵 **값을 손으로 적지 않는다 — 사이드바의 실제 높이를 이 자리에서 유도한다.**
        이 폭의 `.sidenav` 는 세로로 이것뿐이다:
          · 위아래 padding = `var(--sp-2)` × 2   (바로 위 `.sidenav` 규칙이 이 폭에서 준 값)
          · 그 안의 가장 높은 자식 = `.navlink` 의 `min-height: var(--touch)`  (:394-396)
          · `.brand` 는 `display:none`(바로 윗줄) · 위아래 테두리 없음(`border-right` 뿐)
        → 높이 = `var(--touch) + var(--sp-2) * 2`. 토큰이 바뀌면 이 값도 **따라 바뀐다**;
          `60px` 이라고 적어 두면 `--touch` 를 올리는 날 조용히 어긋난다.
     ⚠ `position: static` 으로 접는 길도 있었지만 택하지 않았다 — 사이드바는 여기서
       **실제로 붙어 있고**(실측: 어느 스크롤에서도 `[0, 60]`), 상단바만 흐름으로 내리면
       지점 선택기·로그아웃이 스크롤과 함께 사라져 28차가 회원 셸에서 되살린 그 문
       (「상시 헤더」)을 직원 셸에서 다시 잃는다. 겹침만 푼다. */
  .topbar { top: calc(var(--touch) + var(--sp-2) * 2); }
}
@media (max-width: 560px) {
  :root { --fs-display: 1.875rem; }
  .page { padding: var(--sp-4) var(--sp-3) var(--sp-7); }
  /* 🆕 41차 — 점프 줄은 `.page` 의 좌우 여백을 **뚫고** 나가야 흰 판이 화면 끝까지 간다.
     그 여백이 이 폭에서 sp-4 → sp-3 으로 줄므로 뚫는 값도 **같은 줄에서 함께** 바뀐다
     (위 `.jumpbar` 기본형은 sp-4 기준 · 한쪽만 남으면 4px 이 삐져나오거나 모자란다). */
  .jumpbar { margin-inline: calc(var(--sp-3) * -1); padding-inline: var(--sp-3); }
  /* 열 우선순위(§2-3) — 폰에서는 표가 가로 스크롤 대신 우선 열만 남긴다.
     어느 열이 접히는지는 화면의 cols 정의(hideNarrow)가 정한다(ui.js table). */
  .col-nrw { display: none; }

  /* 🔴🔴 42차 — **폰에서 직원 메뉴 여섯 중 셋이 화면 밖에 있었다.**
     위 `@media (max-width: 900px)` 의 `.sidenav` 는 가로 줄(flex row) + `overflow-x: auto`
     인데, 한 줄에 필요한 폭이 **651px** 이라 폰에서는 오른쪽이 잘린다(실측 390px:
     홈 [12,93] · 회원 [101,196] · 연락 [204,300] · **메뉴판 [308,418] · 콘텐츠 [426,536] ·
     설정 [544,639]** — 뷰포트 우변은 390). 320·360·390·414 에서 셋, 430 에서 둘, 560 에서
     하나가 밖이다.
     가로로 밀면 닿기는 한다. 그러나 **밀 수 있다는 표시가 화면에 0** 이다 — 페이드도,
     부분 노출 힌트도, 스크롤바도 없어서 첫 화면에서는 「메뉴판」의 **왼쪽 끝조차 안 보인다.**
     🔑 *닿는 길이 있어도 그 길이 화면에 없으면 없는 것과 같다.* 실장이 폰으로 쓰는 동안
        메뉴판·콘텐츠·설정 **세 화면이 없는 것과 같았다.**
     ⚠ 41차 회귀가 아니다 — 이 규칙은 최초 스캐폴드(`3127b53`)부터 있었다.
       41차는 상단바 **겹침**만 쟀고 **닿는가**는 아무도 안 쟀다.

     → 회원 하단 탭바(`.tabbar` :543)와 **같은 문법**으로 바꾼다. 격자 한 줄에 `1fr` 로
       나눠 담으므로 **항목이 몇 개든 화면 안에 들어간다** — 일곱 번째가 생겨도 넘치지 않는다.
       폭을 손으로 계산해 맞추는 것이 아니라 **넘칠 수 없는 구조**로 바꾸는 것이다.

     🔵 **높이는 안 바뀐다 — 바뀌면 위 `.topbar { top }` 유도식이 조용히 틀린다.**
        세로로 쌓이는 것 = 아이콘 20px(`.navlink .ico` :412) + gap 2px +
        라벨(`--fs-xs` 13px × `line-height: 1.2` = 15.6px) = **37.6px ≤ `--touch`(44px)**.
        그래서 `.navlink` 높이는 여전히 `min-height: var(--touch)` 가 정하고,
        `.sidenav` 는 그대로 `var(--touch) + var(--sp-2) * 2` = 60px 다.
        ⚠ 라벨의 `font-size`·`line-height` 를 **여기서 못박는 이유가 이것이다**: 물려받은
          16px × 1.65 = **26.4px**(실측)이면 20 + 2 + 26.4 = **48.4px** 로 `--touch` 를 넘겨
          사이드바가 커지고, 그러면 41차가 닫은 「상단바가 사이드바 뒤로 숨는」 결함이
          그대로 되돌아온다(가려지는 60px 이 화면 제목·[새 회원 등록]·[데스크]·[발송 대장]·
          지점 선택기·로그아웃 **전부**다).
        ⚠ `white-space: nowrap`(위 `.navlink`)은 여기서 **높이를 지키는 장치**이기도 하다 —
          라벨이 두 줄로 접히는 순간 위 계산이 그냥 틀린 값이 된다.
     ⚠ `.grow` 는 `flex: 1 1 auto` 라 세로 방향에서 라벨을 늘린다 — 여기서 끈다.

     🔴 **561~660px 구간은 여전히 「설정」 하나가 밖에 남는다.** 임계는 위 651px 이고
        660px 부터 저절로 정상이다(실측: 660 에서 scrollW 659 = clientW 659).
        **새 브레이크포인트를 만들지 않는다**는 판단이다 — 그 폭의 *세로 화면 실기기가
        사실상 없다*(폰 최대 ~448px · 태블릿 세로는 600px 이상). 남는 실사용은
        **노트북 창을 561~651px 로 좁혔을 때** 하나이고, 그때는 창을 넓히는 길이 화면에 있다.
        숨기지 않고 여기 적어 둔다 — 실장이 그 폭을 실제로 쓴다는 사실이 나오면
        이 절의 상한을 **660** 으로 올리는 것이 수리다(다른 규칙은 손대지 않는다). */
  .sidenav {
    display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
    gap: 0; padding-inline: 0;
  }
  .navlink {
    flex-direction: column; justify-content: center; gap: 2px;
    padding: 0 2px; font-size: var(--fs-xs); line-height: 1.2;
  }
  .navlink > .grow { flex: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ── 진행률 미니그래프 (F06) ────────────────────────────────────────────────
   표 안에 들어가는 아주 작은 막대. 차트 라이브러리를 넣지 않는 이유는 바이블 1-3
   그대로다 — 이 정도를 위해 번들러가 들어오면 그때부터 화면마다 다른 차트가 생긴다.
   ⚠ 폭은 인라인 style 로 준다(값이 행마다 다르므로 CSS 로는 표현할 수 없다). */
.prog     { display: flex; align-items: center; gap: var(--sp-2); min-width: 132px; }
.prog-bar { flex: 1 1 auto; height: 6px; border-radius: 3px; overflow: hidden;
            background: color-mix(in srgb, var(--ink) 10%, transparent); }
/* 🔴 **채움과 트랙이 1.12:1 이었다** (27차 실측) — 막대가 값을 말하는데 **막대와 트랙이
   눈으로 안 갈렸다.** 그리고 회원 홈(`m/home.js`)의 막대에는 `.prog-txt` 가 없어 그 막대가
   값의 **유일한 표현**이다(직원 목록에는 곁글이 있다).
   ⚠ 채움 색을 어둡게 하지 않는다 — 라임 채움은 색 헌법의 「지금 해야 할 것」 신호다.
     대신 **테두리로 경계를 만든다**: 테두리 `--lime-shade` 는 트랙과 **3.74**, 채움과 3.35 라
     양쪽 모두에서 선이 보인다. 🔑 *값을 바꾸지 않고 경계만 세운다.* */
.prog-bar > i { display: block; height: 100%; background: var(--lime-deep);
                box-shadow: inset 0 0 0 1px var(--lime-shade); }
.prog-txt { font-size: var(--fs-xs); color: var(--ink-70);
            white-space: nowrap; font-variant-numeric: tabular-nums; }

/* 필터 줄 — 좁은 화면에서 접힌다. 태블릿 우선이라 한 줄에 다 들어가는 것이 기본이다. */
.filters        { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }
.filters .input { width: auto; min-width: 128px; }
.filters .search{ min-width: 220px; flex: 1 1 220px; }

/* 미확인 메시지 개수 배지 */
.badge-n { display: inline-block; min-width: 20px; padding: 0 6px; border-radius: 10px;
           background: var(--ink); color: var(--paper); font-size: var(--fs-xs);
           font-weight: 700; text-align: center; line-height: 20px; }
/* 배지가 눌리는 자리(§2-3 목록 → 메시지함 딥링크) — pill 은 그대로 작고 히트 영역만
   44px 를 채운다(행 셀 높이가 이미 44px 라 행은 안 자란다). */
.badge-tap { display: inline-flex; align-items: center; justify-content: center;
             min-width: var(--touch); min-height: var(--touch); }

/* 정체 점(§2-3) — 판정은 `v_stagnant`(뷰) · 화면은 점 하나만 얹는다(색 헌법 2조:
   상태색은 판정 뷰 값·지남에만). 셀 컨테이너는 점과 글자를 세로 가운데로 맞춘다. */
.dot-stop { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
            background: var(--stop); flex: none; }
/* 🔴 21차 — 점이 `.who` (inline-flex·gap) 안에 있을 때는 간격이 절로 붙었다. 이름
   글자 바로 뒤(`.who-n` 안)로 옮기면 그 gap 이 사라져 글자에 달라붙는다. 여기서 준다. */
.who-n > .dot-stop { margin-left: var(--sp-2); vertical-align: middle; }
.cell-measure { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* ── 회원 한 칸 (아바타 + 이름·전화) ─────────────────────────────────────────
   목록·스레드처럼 「행이 곧 사람」인 표의 첫 칸. 열을 하나 줄여 표가 숨쉬게 하되
   전화는 그대로 보인다 — 정보를 줄이는 개편이 아니다(2026-08-06 목업 승인 축). */
.avatar  { width: 30px; height: 30px; border-radius: 50%; flex: none;
           display: inline-flex; align-items: center; justify-content: center;
           background: var(--lime-wash); border: 1px solid var(--lime-deep);
           color: var(--lime-ink); font-size: var(--fs-xs); font-weight: 800; }
.who     { display: inline-flex; align-items: center; gap: var(--sp-2); }
.who-txt { display: inline-flex; flex-direction: column; line-height: 1.3; }
.who-n   { font-weight: 700; }
.who-p   { font-size: var(--fs-xs); color: var(--ink-45); font-variant-numeric: tabular-nums; }

/* ── 결제 히스토리 한 줄 (§2-7 · track-board.js) ────────────────────────────
   행 = 결제 한 건 「8.6 · 1,000,000원 — 감량 3개월 · 약침 12회」. 누르면 구성 상세가
   행 바로 아래 열린다(툴팁은 태블릿에 없다 — 표 expand 와 같은 자리 규약 · 44px). */
.sale-list { display: flex; flex-direction: column; }
.sale-row  { display: flex; align-items: center; gap: var(--sp-2);
             min-height: var(--touch); padding: var(--sp-2) 0;
             border-bottom: 1px solid var(--ink-08);
             cursor: pointer; font-size: var(--fs-sm); }
.sale-row:last-child { border-bottom: 0; }
.sale-row:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: -3px; }
.sale-x    { background: var(--paper-dim); border-radius: var(--r-sm);
             padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-2); }

/* ── 최근 활동 타임라인 (F07 · 2026-08-06) ──────────────────────────────────
   🔴 29차 정정(2026-08-10) — 옛 주석이 정본이라 가리키던 `detail.js timelineBox` 는
   **지금 없다**(레포 전수 0건). `.tl` 계열 소비자도 앱 JS 에 0 — 예약 자리 둘은 이미
   다른 어휘로 지어졌다(설정 동의 이력 = `.agd` · 이용내역 = `table()`). 되살리면 M-12 를
   되돌린다. 블록 14(죽은 클래스 일괄)에서 이 블록째 걷는 것이 처방이다 — 그 전까지
   이 규칙을 새 화면의 본보기로 쓰지 말 것. */
.tl      { display: flex; flex-direction: column; }
.tl-row  { display: flex; align-items: baseline; gap: var(--sp-2);
           padding: var(--sp-2) 0; border-bottom: 1px solid var(--ink-08);
           font-size: var(--fs-sm); }
.tl-row:last-child { border-bottom: 0; }
.tl-dot  { width: 8px; height: 8px; border-radius: 50%; flex: none;
           align-self: center; }
.tl-ok   { background: var(--ok); }
.tl-lime { background: var(--lime-deep); }
.tl-mute { background: var(--ink-45); }
.tl-t    { font-weight: 700; white-space: nowrap; }
.tl-b    { color: var(--ink-70); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tl-when { margin-left: auto; color: var(--ink-45); font-size: var(--fs-xs);
           white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ── 탭 (§2-6 · 2026-08-06) — 회원 상세가 처음 입고, 설정 화면이 뒤따른다 ──────
   버튼 나열이 아니라 탭의 시각 문법: 먹색 글자 · 활성 = 하단 라임 인디케이터 · 44px 터치.
   🔴 라임을 면으로 깔지 않는다(브랜드북 규칙 ①) — `.navlink` 활성의 inset 라임 실선과
      같은 문법을 가로로 눕힌 것이다. 좁은 화면에서는 탭줄이 자기 안에서 가로 스크롤한다
      (본문이 좌우로 밀리면 태블릿에서 조작이 어긋난다 — 표와 같은 처방). */
.tabs { display: flex; align-items: stretch; gap: 2px;
        border-bottom: 1px solid var(--ink-20); margin-bottom: var(--sp-4);
        overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tabs .tab { flex: 0 0 auto; min-height: var(--touch); padding: 0 18px;
             border: 0; background: none; cursor: pointer;
             font: inherit; font-weight: 700; color: var(--ink-45); white-space: nowrap;
             box-shadow: inset 0 -2px 0 transparent; transition: color .12s ease; }
.tabs .tab:hover { color: var(--ink); }
.tabs .tab[aria-selected="true"] { color: var(--ink); font-weight: 700;
                                   box-shadow: inset 0 -3px 0 var(--lime); }
.tabs .tab:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: -3px; }
/* 🔴 탭이 `<a>` 일 수도 있다(설정 화면 — 주소가 탭의 정본이라 새로고침·뒤로가기가 산다).
   버튼은 flex 자식이 저절로 가운데 오지만 링크는 아니다 — 높이만 맞으면 글자가 위에 붙는다. */
a.tab { display: inline-flex; align-items: center; text-decoration: none; }
.tabpane { min-width: 0; }

/* 행 확장 — 표의 부가 열·잘린 본문을 **행 바로 아래**에서 편다(툴팁은 태블릿에 없다).
   인라인 수정 폼도 같은 자리 규약을 쓴다(§2-6 C3 — 폼이 화면 밖에 생기지 않게). */
.x-row > td { background: var(--paper-dim); }
/* 펼친 행의 **속**. 🆕 M-14(2026-08-08) — 회원 이용내역 셋이 이 자리를 쓰면서 어휘가 됐다.
   🔴 직원 `measureExpand` 는 같은 값을 **인라인 style 로** 적고 있었다. 그건 M-12 게이트가
      「목록 줄 높이를 손으로 흉내 낸 자리」로 세는 모양과 글자까지 같아서, 새로 쓰는 자리가
      그 흉내와 구별되지 않는다. 🔑 *어휘가 없으면 화면은 그것을 인라인으로 적고, 인라인은
      그 화면의 사정이라 아무도 답습하지 못한다.*
   ⚠ `.x-row > td` 에 직접 주지 않는 이유 — 직원 호출부가 이미 자기 인라인 padding 을
     들고 있어 겹친다(호출 25곳을 세어 가며 고치는 편집을 이 커밋이 하지 않는다). */
.x-body { padding: var(--sp-2) 0; }
.x-pre { white-space: pre-wrap; overflow-wrap: anywhere;
         font-size: var(--fs-xs); color: var(--ink-70); margin: 0; }

/* ── 회원 상세 (F07) ────────────────────────────────────────────────────────
   한 화면에 열세 덩어리가 세로로 쌓인다. 구획이 안 보이면 실장은 스크롤 중에
   자기가 어디를 보고 있는지 잃는다 — 그래서 섹션마다 제목줄에 밑줄 하나를 둔다. */
.sec     { margin-top: var(--sp-6); }
.sec-bar { margin-bottom: var(--sp-3); padding-bottom: var(--sp-2);
           border-bottom: 1px solid var(--ink-08); }
/* 섹션 **안쪽**의 작은 묶음 제목. `.chapter-bar`(먹색 띠)를 쓰면 섹션 제목보다 강해져서
   위계가 뒤집힌다 — 실제로 「복약」이 「회원 셀프 기록」보다 커 보였다(2026-07-31 육안). */
.sub-head { margin-top: var(--sp-5); margin-bottom: var(--sp-2);
            font-size: var(--fs-sm); font-weight: 700; color: var(--ink-70); }

/* 라벨–값 쌍. 태블릿 가로에서 3~4열, 좁아지면 스스로 접힌다(미디어쿼리 없이). */
.kv      { display: grid; gap: var(--sp-4);
           grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.kv > div{ min-width: 0; }
.kv .k   { display: block; font-size: var(--fs-xs); color: var(--ink-45); }
.kv .v   { display: block; font-weight: 600; overflow-wrap: anywhere; }

/* 요약 숫자 4칸 — .stat 을 가로로 늘어놓는 자리 */
.statrow { display: grid; gap: var(--sp-4);
           grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
/* 🔴 **고정 2열 변형** (28차 블록 6) — 칸 수가 **정해진** 묶음에만 얹는다. 기본형은 auto-fit
   이라 회원 셸 폭에서 4칸이 3+1 로 갈리는데, 갯수가 고정인 자리에서는 그 유연함이 손해다.
   ⚠ **기본형 뒤에** 선다(특이도가 같아 앞에 두면 조용히 무효). 기본형은 안 건드린다 —
   가변 타일(홈 프로그램 1~N)이 그 유연함으로 살아 있다. */
.statrow--fixed2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 숫자 대신 **문장**이 들어가는 칸(「목표 미설정」). 같은 크기면 판정 불가라는 말이
   네 칸 중 제일 커져서 시선을 가져간다 — 판정할 수 없다는 것이 강조할 일은 아니다. */
.stat .v-words { font-size: var(--fs-h2); }

/* 🔴 지금 보고 있는 범위와 다른 지점의 회원일 때(유저 플로우 5-b).
   경고가 아니라 안내다 — 빨강을 쓰면 "무언가 잘못됐다"로 읽힌다. */
.notice-line { padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
               background: var(--lime-wash); border: 1px solid var(--lime-deep);
               font-size: var(--fs-sm); }

/* 체중 추이 — 가로만 늘리므로(preserveAspectRatio=none) 선 굵기를 고정한다.
   ⚠ non-scaling-stroke 가 없으면 넓은 화면에서 선이 뭉개진다. */
.spark        { width: 100%; height: 96px; display: block; }
.spark .line  { fill: none; stroke: var(--lime-shade); stroke-width: 2;
                stroke-linejoin: round; stroke-linecap: round;
                vector-effect: non-scaling-stroke; }
.spark .goal  { stroke: var(--ink-20); stroke-width: 1; stroke-dasharray: 5 5;
                vector-effect: non-scaling-stroke; }
/* F22 — 같은 그래프 위에서 **누가 잰 것인지**만 가른다(마커만 다르다 · 축은 하나다).
   실장 측정은 채운 점, 회원 셀프는 속이 빈 점 — 색만으로 가르면 색약에서 구분이 사라진다. */
.spark .dot-staff { fill: var(--lime-shade); }
.spark .dot-self  { fill: var(--paper); stroke: var(--lime-shade); stroke-width: 1.5; }

/* 복약 3-상태 — null 은 아예 다른 글자(—)이고, 여기서는 색만 가른다 */
.tri-yes { color: var(--ok); font-size: 1.05em; }
.tri-no  { color: var(--ink-45); font-size: 1.05em; }

/* 동의 체크 한 줄 (F01) — 🔴 상자만 누르게 두면 태블릿에서 사실상 못 누른다.
   라벨 전체가 히트 영역이고 높이는 44px 를 넘긴다(I2 전제: 태블릿 우선). */
.check { display: flex; gap: var(--sp-3); align-items: flex-start;
         padding: var(--sp-3) 0; cursor: pointer; min-height: 44px; }
.check + .check { border-top: 1px solid var(--ink-08); }
.check-box  { width: 22px; height: 22px; flex: 0 0 auto; margin-top: 2px;
              accent-color: var(--lime-shade); cursor: pointer; }
.check-text { display: block; }
.check-label { display: block; font-size: var(--fs-sm); font-weight: 600; }
/* 필수/선택 표시는 라벨 옆에 붙는 작은 글자다 — 색만으로 가르지 않는다(글자로도 말한다) */
.check-mark { margin-left: var(--sp-2); font-size: var(--fs-xs); font-weight: 600;
              color: var(--ink-45); }
.check-all  { border-bottom: 1px solid var(--ink-20); }
.check-all .check-label { font-size: var(--fs-body); }

/* ═══════════════════════════════════════════════════════════════════════════
   목업 v6 부품층 (2026-08-07 · 20차)

   출처 = `clinic-daegu/seolmyungcare-mockup-v6.html` 1~296줄. 대표님이 직접 보시고
   **"직원용은 목업과 똑같이"** 라고 판정하신 판이므로, 여기 있는 값은 **목업 실측**이지
   내 취향이 아니다. 값을 고칠 일이 생기면 목업 파일에서 다시 grep 한다.

   🔴 이식하며 **한 엔진으로 합친 것 3** — 목업이 새 이름을 쓴 자리가 앱에 이미 있었다.
     ⑴ 목업 `.prog`(막대 자체) ↔ 앱 `.prog`(막대를 담은 flex 줄) — **같은 이름, 다른 뜻**.
        그대로 덮으면 F06 목록의 미니 막대가 통째로 깨진다. → 막대는 앱의 `.prog-bar` 를
        쓰고, 굵기만 자리에서 올린다(`.stk`·`.goalrow`).
     ⑵ 목업은 캘린더가 둘이다(`.vcal` 홈 · `.cal` 기록). 앱은 `.cal` 하나로 홈을 그린다.
        → 엔진은 `.cal` 하나로 두고 기록용은 `.cal--rec` 변형으로 얹는다.
     ⑶ 목업 `.seg`(메시지/알림톡 칸) 는 `.tabs` 와 시각 문법이 같다 → `.tabs` 재사용.
   🔑 *사본은 나란히 낡지 않는다 — 한쪽만 고쳐진다.* 그래서 이름을 늘리지 않았다.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 🔴 **`.on` 토글 규약을 쓰지 않는다**(목업과 갈리는 유일한 구조 결정).
   목업은 `display:none` + `.on` 으로 5개 부품(추가카드·행 확장·템플릿 바·오버레이·
   연락 패널)을 여닫는다. 앱은 대신 **만들었다 지운다**(`replaceChildren` / `hidden` /
   `append`+`remove`) — `ui.js table()` 의 행 확장이 이미 그 방식이고, 패널을 열 때
   만들면 포커스 트랩·ESC 처리를 한 곳에서 할 수 있다.
   ⚠ 그래서 아래 부품에 `display:none` 을 적지 않는다. 목업 마크업을 옮길 때
     `class="… on"` 을 함께 옮기면 **아무 일도 안 하는 클래스**가 된다.

   ── 작은 조각 ─────────────────────────────────────────────────────────────
   `.cnt` = 개수 배지(라임 테두리) · `.tag` = 행 앞 종류 글자(출고/결제/약침)
   ⚠ `.cnt` 는 「몇 건인지」를 말하는 자리다. 못 셌으면 그리지 않는다(0 으로 접지 않는다). */
.cnt {
  display: inline-block; font-size: .75rem; font-weight: 700;
  background: var(--lime-wash); border: 1px solid var(--lime-deep); color: var(--lime-ink);
  border-radius: var(--r-full); padding: 0 8px; font-variant-numeric: tabular-nums;
}
.tag { font-size: .75rem; font-weight: 700; color: var(--ink-45); letter-spacing: .02em; }
/* 서버가 자동으로 채우는 변수(이름·코드) — 화면은 값을 보내지 않는다는 표식 */
.var { background: var(--lime-wash); border-radius: 3px; padding: 0 3px; font-weight: 700; }
/* 방금 저장된 행 — 어디에 들어갔는지 눈이 따라가게 한다(2.4초). */
@keyframes rowflash { from { background: var(--lime-wash); } to { background: transparent; } }
.flash { animation: rowflash 2.4s ease-out; }

/* ── 회원 상세 상시 헤더 (F07 개요 폐지의 짝 · 결단 ⑦) ─────────────────────
   🔴 값은 **인라인 문장**으로 읽는다(`감량기 1개월 남음`). 큰 숫자 + 아래 라벨로 쌓으면
      다섯 칸이 같은 무게가 되어 「지금 무엇이 급한지」가 사라진다 — 실제로 그렇게 보였다. */
.mhead {
  background: var(--paper); border: 1px solid var(--ink-08); border-radius: var(--r-md);
  padding: 14px 18px; box-shadow: var(--shadow-1); margin-bottom: 14px;
}
.mhead .row1 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mhead .nm   { font-size: 1.35rem; font-weight: 800; letter-spacing: -.015em; }
.mhead .sub  { font-size: var(--fs-xs); color: var(--ink-70); }
.mhead .sp   { flex: 1; }
.mhead .row2 {
  display: flex; align-items: baseline; gap: 20px; flex-wrap: wrap;
  margin-top: 8px; font-size: var(--fs-xs); color: var(--ink-45);
}
.mhead .row2 b { font-size: 1.1rem; font-weight: 600; color: var(--ink); margin-right: 2px;
                 font-family: var(--font-num); font-variant-numeric: tabular-nums; }
/* 값이 없을 때는 서체를 바꾸지 않는다 — 「—」가 Baloo 2 로 뜨면 대시가 숫자처럼 보인다. */
.mhead .row2 b.none { font-family: var(--font); color: var(--ink-45); }

/* 메시지 | 알림톡 칸 — `.tabs` 와 같은 엔진, 위치만 다르다 */
.seg { margin: 10px 0 0; }

/* ── 홈 ────────────────────────────────────────────────────────────────── */
.dash-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: 14px; align-items: start; }
/* 법정 기한이 걸린 한 줄만 맨 위에 고정한다(탈퇴 요청) */
.pin {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: color-mix(in srgb, var(--stop) 6%, var(--paper));
  border: 1px solid color-mix(in srgb, var(--stop) 25%, var(--paper));
  border-radius: var(--r-md); padding: 10px 16px; margin-bottom: 14px;
}
/* 그날 아젠다 한 줄 */
.agd { display: flex; align-items: center; gap: 10px; padding: 9px 0;
       border-bottom: 1px solid var(--ink-08); min-height: var(--touch); }
.agd:last-child { border-bottom: none; }
.agd .tm { font-size: var(--fs-xs); color: var(--ink-45); font-variant-numeric: tabular-nums;
           width: 44px; flex: none; }
.agd .nm { font-weight: 700; white-space: nowrap; }
.agd .tx { font-size: var(--fs-xs); color: var(--ink-70); flex: 1; min-width: 0;
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── 44px 행 문법 — 회원 목록의 변형 셋 (M-12 · 2026-08-08) ──────────────────
   🔴 **새 행 문법을 만들지 않는다.** 위 `.agd` 가 이미 「44px 터치 + 아래 구분선 +
      (때 · 이름 · 본문 · 곁버튼)」이고, 회원 목록 아홉이 필요한 것이 정확히 그것이다.
      옛 회원 판은 `.row` + `.stack--sm`(8px) 이라 **행이 높이를 안 갖고 간격만 가졌다** —
      그래서 폰에서 누를 자리가 글자 크기만 했고, 행 안에 버튼을 넣을 자리가 없었다.

   ⚠ 그대로 못 쓰는 자리가 셋이라 **변형을 뒤에 얹는다**(M-09 이 `.stack--lg`/`--sm` 으로
     세운 그 형태 · 기본형을 고치면 직원 아젠다 줄이 함께 움직인다).
     🔑 *되돌림 비용이 「전 화면 동시」에서 「자리마다 한 줄」로 내려간다.*
   ⚠ **반드시 `.agd` 뒤에 둔다** — 특이도가 같아 순서가 이긴다. 위로 옮기면
     오류 없이 조용히 무효가 된다(`ui.test` 가 선언 순서를 본다).

   ⑴ `.tm--date` — 폭만 푸는 수식이 아니다. 직원 아젠다의 `.tm` 은 **오늘 안의 시각**
      (`14:30`)이고 날짜는 화면이 이미 말하므로 곁말(`--ink-45` · 11px · 44px 고정)이 맞다.
      회원 목록의 때는 **여러 날에 걸친 날짜**이고 그건 곁말이 아니라 **그 행의 이름**이다
      (「무엇에 대한 줄인가」를 그것 하나가 정한다) → 폭·대비·무게를 함께 올린다.
      🔴 `--ink-45` 는 웜 그라운드 위에서 **3.40:1** 이라 중년 회원에게 안 읽힌다
         (감사 M-36 · 결단 2). 그 결단의 범위는 하단 탭 라벨이고, **여기는 그 사촌이다** —
         자리로 좁힌 예외이므로 토큰도 `.tm` 기본형도 건드리지 않는다(`.shell .btn` 36px 판례).
   ⑵ `.agd--stack` + `.agd-sub` — `.agd` 는 한 줄이다(직원: 한 사람이 두 줄을 먹지 않는다).
      회원 행 중 셋은 아래에 **그 행의 본문**이 온다(컨디션 일기 · 검진 값 격자 · 경위).
      그건 곁말이 아니라 잘리면 안 되는 내용이므로, nowrap 규약을 **자리에서만** 푼다.
   ⑶ `.end` — 값(금액·상태·kcal)이 행의 오른쪽 끝에 서는 자리. `.row-between` 을 쓰거나
      빈 `div` 를 spacer 로 넣던 자리를 대신한다.
      🔑 *M-09 이 설정 카드에서 지운 「경계용 빈 요소」와 같은 형태다 — 읽는 기계에게
         아무 뜻이 없는 노드를 레이아웃 때문에 만들지 않는다.* */
.agd .tm--date { width: auto; flex: none;
                 font-size: var(--fs-sm); font-weight: 600; color: var(--ink-70); }
.agd--stack { flex-wrap: wrap; row-gap: var(--sp-2); }
.agd--stack > .agd-sub { flex-basis: 100%; margin: 0; }
.agd .end, .grow-item .end { margin-left: auto; flex: none; }
/* `.nm` 의 `nowrap` 은 **사람 이름**이라는 전제에서 온 것이다(직원 아젠다는 회원 이름을 든다).
   회원 화면의 「이름」은 동의 항목명처럼 **문장에 가까워** 폰 폭에서 접혀야 한다 —
   접히지 않으면 오른쪽 끝의 값(`.end`)을 화면 밖으로 밀어낸다.
   ⚠ 기본형을 안 고치고 **회원 셸 안으로만** 좁힌다(`.shell .btn` 36px 예외와 같은 문법). */
.m-shell .agd .nm { white-space: normal; }
/* 🔴 **간격을 두 곳이 각자 더하지 않는다.** `.agd` 는 자기 padding(9px)과 구분선을 든다 —
   그런데 그것을 `.stack`(16) 이나 `.stack--sm`(8) 직속에 두면 위에 그 값이 또 얹혀
   행 사이가 25px 이 된다(설정 이력 줄이 32px 이던 그 형태의 재발이다).
   🔑 *이 함정은 검사로 잡는 것보다 **일어날 수 없게 만드는 편**이 싸다* — 프로브로 「어느
      `el()` 의 직접 자식인가」를 정적으로 알아내려면 호출 트리를 파싱해야 하고, 그 파서가
      바로 이 레포가 다섯 번 밟은 「썩는 프로브」다. 규칙 한 줄이 그 일을 대신한다. */
.stack > .agd + .agd { margin-top: 0; }

/* 행 전체가 문일 때 — 목록 안에서 링크 색·밑줄을 도로 걷는다(`.grow-item` 이 이미 쓰는 판).
   ⚠ 누를 수 있다는 신호는 색이 아니라 **행 안의 다음 행동**이 준다(색 헌법 · 라임은 면에 안 깐다). */
a.agd { color: inherit; text-decoration: none; }
a.agd:hover { background: var(--paper-dim); }
a.agd:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: -3px; }

/* 분류 그룹 카드 + 그 안의 한 줄.
   🔴 **줄바꿈하지 않는다.** 버튼이 둘째 줄로 접히면 한 사람이 두 줄을 먹어 목록이
      두 배로 길어지고, 「이름 → 사정 → 할 일」이 한눈에 안 읽힌다(2026-08-07 육안).
      좁아지는 것은 가운데 `.tx` 뿐이다 — 그래서 거기만 `min-width:0` + 말줄임이다. */
.grp { background: var(--paper); border: 1px solid var(--ink-08); border-radius: var(--r-md);
       box-shadow: var(--shadow-1); overflow: hidden; margin-bottom: 12px; }
.grp > header { display: flex; align-items: center; gap: 8px; padding: 11px 16px 7px; font-weight: 700; }
.grp > header .more { margin-left: auto; font-size: .75rem; color: var(--ink-45); font-weight: 500; }
/* 🆕 27차 — 분류 그룹 안의 빈 상자. `.grp` 는 패딩이 0 이라(행이 자기 좌우 패딩을 든다)
   빈 상자가 모서리에 닿는다. 옛 판은 그 여백을 **호출부가 인라인으로** 들고 있었고, 그래서
   `.empty` 를 부품(`emptyBox`)으로 못 부르고 **손으로 짓고 있었다**(M-04 가 회원 쪽에서
   회수한 그 형태의 직원판).
   🔑 *어휘가 없으면 화면은 그것을 인라인으로 적고, 인라인이 있으면 부품을 못 쓴다.* */
.grp .empty { margin: 0 16px 14px; }
.grow-item { display: flex; align-items: center; gap: 10px; padding: 8px 16px;
             border-top: 1px solid var(--ink-08); min-height: var(--touch); }
.grow-item .nm { font-weight: 700; white-space: nowrap; }
.grow-item .tx { font-size: var(--fs-xs); color: var(--ink-70); flex: 1; min-width: 0;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 🔴 **이 줄만은 잘리면 안 된다** — 위 말줄임을 한 줄에만 되돌리는 수식(2026-08-07 육안).
      「유지기 복용 다름」의 문장이 양방향으로 어긋난 분에서 「… 블랙 · 처…」로 잘렸고,
      **무엇이 어긋났는지가 잘린 자리 뒤로 사라졌다.**

   ⚠ **`.tx` 전역을 안 고친 이유** — 위의 말줄임은 다른 그룹에서는 **옳다.** 「새 메시지」의
     본문 미리보기·「오래 안 오심」의 사유·「이번 달 출고」의 사정은 잘려도 **전문이 다른
     화면에 다시 있다**(잘림은 요약이지 손실이 아니다). 전역으로 풀면 그 세 그룹이 사람마다
     두 줄을 먹어 바로 위 주석이 적어 둔 이유(「이름 → 사정 → 할 일」이 한눈에)가 무너진다.
     🔑 *잘려도 되는 줄과 안 되는 줄을 가르는 것은 글자 수가 아니라 **그 문장이 다른 데
        또 적히는가**이다* — 유지기 어긋남 문장은 어디에도 다시 안 적힌다(홈이 유일한 자리).
     그래서 CSS 규칙이 아니라 **「이 줄만은」을 이름으로 선언**한다.

   ⚠ `title` 속성으로 대신하지 않는다 — **실장은 태블릿으로 본다.** hover 가 없으면 그건
     아무 데도 없는 것과 같다(이 저장소가 「버튼이 무엇을 하는지 화면에 적는다」로 이미
     한 번 못박은 자리다). `title` 은 hover 가 있는 환경을 위해 **남겨 두되 의존하지 않는다**.

   🆕 **`.agd` 도 같은 규칙을 쓴다**(M-12 · 2026-08-08). 회원 컨디션 목록의 지표 줄
     (「기분 4/5 · 활력 3/5 · 운동 30분」)이 정확히 위 기준에 걸린다 — **그 값은 앱 어디에도
     다시 안 적힌다.** 잘리면 무엇을 적었는지가 잘린 자리 뒤로 사라진다.
     ⚠ 사본을 만들지 않고 **셀렉터를 넓힌다** — 같은 규칙이 두 벌이면 한쪽만 고쳐진다.
   ⚠ 같은 무게(클래스 2개)라 **순서가 이긴다** — 반드시 `.grow-item .tx`·`.agd .tx` **뒤**에 둔다.
     위로 옮기면 아무 일도 안 일어나고 화면은 그대로 잘린다(오류도 안 난다).
   ⚠ 행이 두 줄로 자라도 리듬은 안 깨진다 — `.grow-item` 이 이미 `min-height: var(--touch)`
     라 한 줄짜리 행과 높이가 같고, 넘칠 때만 그만큼 자란다.
   ⚠ `overflow-wrap: anywhere` 는 **끊을 자리가 없는 긴 토막**(약 이름이 붙어 오는 경우)이
     칸 밖으로 삐져나가지 않게 하는 안전선이다 — 평소에는 낱말 단위로 접힌다. */
.grow-item .tx--wrap, .agd .tx--wrap { white-space: normal; overflow: visible; text-overflow: clip;
                       overflow-wrap: anywhere; }
.grow-item .tm { font-size: var(--fs-xs); color: var(--ink-45);
                 font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 곁버튼은 줄을 밀지 않는다 — 이름·사정이 먼저 좁아진다 */
.grow-item .btn { flex: none; }
/* 🆕 `.agd` 도 같다(M-13 · 2026-08-08) — 행 안에 다음 행동이 들어오면서 회원 목록에도
   같은 규칙이 필요해졌다. ⚠ 회원 `.btn` 은 44px 이므로(`.shell .btn` 36px 은 직원 전용)
   버튼이 든 행은 62px 로 자란다 — **그것이 44px 규약의 정직한 값**이다. 깎지 않는다. */
.agd .btn { flex: none; }

/* 🔴 43차-후속 — **행 안의 이름은 「문」인데 히트 영역이 20~23px 이었다.** 실장 홈 실측
   **54자리**(아젠다·그룹 행 30 · 표 셀 23 · 탈퇴 요청 핀 1) — 전부 직원 규격 36px 미달이고
   WCAG 2.5.8 의 24px 최소도 못 넘긴다. 실장은 태블릿에서 손가락으로 누른다.
   ⚠ 선택자를 `.agd`·`.grow-item` 안으로 좁혔다가 **`a.nm` 전체로 넓혔다** — 좁은 판으로는
     54 중 30 만 닫혔다(나머지 24 는 표 셀과 핀 안에 있었다). *대상을 담는 그릇 이름으로
     못박으면 새로 생기는 것은 언제나 그 밖에 선다*(이 저장소가 `.ico` 에서 배운 것).
   🔑 *`.badge-tap` 과 같은 처방* — 「pill 은 그대로 작고 히트 영역만 규격을 채운다」.
     글자 크기·색·굵기는 **한 픽셀도 안 건드리고** 누를 수 있는 넓이만 키운다.
   ⚠ `margin-block` 으로 늘어난 높이를 도로 뺀다 — 안 빼면 행이 44 → 54px 로 자라
     한 화면에 보이는 줄 수가 줄어든다. 실측: 링크 23→**36px** · 규격 미달 30→**0** ·
     **행 높이(53/54/55)와 문서 높이(3023) 무변**(1024·390 양쪽).
   🔒 **밑줄을 켤 것인가는 여기서 안 정한다** — 이 이름들은 표식이 없어 문으로 안 보이는데
     (43차 `.link-line` 이 고친 그 결함과 같은 축), 같은 행에 이미 같은 곳으로 가는 36px
     버튼([측정])이 있어 **밑줄 다섯이 한 화면에 얹히는 대가**가 따로 붙는다. 대표님 결단 대기.
     그때까지 **누를 수는 있게** 해 두는 것이 이 규칙이다. */
a.nm {
  display: inline-flex; align-items: center;
  min-height: 36px; margin-block: -7px;
}

/* 🔴🔴 41차 — **폰에서 행 안 버튼이 문장을 0 으로 눌렀다.**
   `.agd`·`.grow-item` 둘 다 `flex-wrap` 이 없고 버튼은 `flex:none`(바로 위 두 줄)이라,
   좁아지는 것은 오직 가운데 `.tx` 뿐이다 — 그것이 데스크탑에서는 옳은 규칙이었다
   (「이름 → 사정 → 할 일」이 한 줄에 읽힌다). 그런데 390px 에서는 버튼 둘이 폭의 절반을
   먹고 **문장이 말줄임만 남는다**(실측 390px: `.grow-item .tx` 164px 에 내용 311px ·
   `.agd .tx` 144px 에 271px — 버튼이 셋이면 0 에 수렴한다). 그 문장은 「누구에게 왜
   연락하는가」이고, 그것이 사라지면 **버튼만 남은 행**이 된다.
   🔑 *한 줄 규약이 지키려던 것은 「한눈에 읽힌다」이지 「한 줄」이 아니다 — 폭이 모자란
      자리에서 한 줄을 고집하면 읽히는 쪽이 먼저 죽는다.*

   ⚠ **반드시 `.grow-item .tx`·`.agd .tx`(:1021·:951) 뒤에 둔다.** 명시도가 같아서
     **순서가 이긴다** — 미디어쿼리는 명시도를 올려 주지 않는다. 위쪽 `@media (max-width:560px)`
     블록(:672)에 적으면 아무 오류 없이 **조용히 무효**가 된다(이 저장소가 실제로 하루 넘게
     죽은 규칙을 들고 있던 형태다).
   ⚠ `order` 로 **문장을 버튼 뒤로 보낸다.** `flex-basis:100%` 만 주면 줄바꿈이 차례대로
     일어나 `tx` 가 둘째 줄, 버튼이 **셋째 줄**로 밀려 행이 세 줄이 된다. 첫 줄에
     「때 · 이름 · 할 일」을, 둘째 줄에 사정을 두는 것이 위 규약의 뜻에 가장 가깝다.
   ⚠ 행이 두 줄로 자라는 것은 **이미 허용된 값이다**(:1045-1046 · `.agd--stack` 이 같은
     문법으로 먼저 하고 있다) — `min-height` 는 그대로고 넘칠 때만 그만큼 자란다.
   ⚠ 말줄임은 **안 푼다** — 잘려도 되는 줄과 안 되는 줄을 가르는 것은 `.tx--wrap` 의 몫이고
     (:1049) 그 판정을 여기서 뒤집지 않는다. 여기서 주는 것은 **폭**이지 줄 수가 아니다.
   🔴 **`.agd--stack` 은 뺀다** — 그 변형은 `.tx` **뒤에 `.agd-sub`**(그 행의 본문 — 컨디션
     일기 · 검진 값 격자 · 경위)가 오는 모양이고, `.agd-sub` 는 이미 `flex-basis:100%` 다
     (:984). 여기서 `.tx` 에 `order:1` 을 주면 **본문이 지표 줄보다 위로 올라간다** —
     겹침을 고치려다 읽는 차례를 뒤집는 것이라 결함을 다른 결함으로 바꾸는 일이다.
     🔑 *같은 규칙을 넓힐 때는 그 안에 이미 자기 순서를 가진 변형이 있는지 먼저 본다.* */
/* 🔴🔴 43차 — **같은 결함이 태블릿에서 살아 있었다.** 41차는 이 규칙을
   `@media (max-width: 560px)` 안에 두었다. 그런데 문장을 누르는 것은 **뷰포트 폭이 아니라
   그 줄이 실제로 받은 폭**이고, 홈의 오른쪽 단은 넓은 화면에서도 좁다:
     실측 1024px(이 앱이 「태블릿 우선」이라 선언한 그 폭) — `.dash-grid` 두 단 427 / **315**,
     「이번 달 출고」 **18행 전부 `.tx` 폭 0px**(필요 144px). 한 글자도 안 보인다.
     「새 메시지」는 199px 이 필요한데 114px, 「오래 안 오심」은 125px 이 필요한데 79px.
   즉 41차가 폰에서 고친 그 결함이 **560~1200px 구간에서 그대로 살아 있었고**, 그 구간이
   실장이 실제로 쓰는 기기다. 🔑 *뷰포트로 잰 처방은 그 줄이 어디에 놓였는지를 모른다.*

   → **폭 조건을 뷰포트에서 「그 줄이 받은 폭」으로 옮긴다.** `flex-wrap` 을 상시로 켜고
     `.tx` 에 **최소 폭(flex-basis)** 을 준다:
       · 옆에 그만큼 자리가 있으면 → 한 줄 그대로(넓은 화면의 「이름 → 사정 → 할 일」 유지)
       · 자리가 없으면 → 둘째 줄로 내려가 **혼자 서므로 전폭**을 받는다
     미디어쿼리가 없으므로 **어느 폭에서도 구조적으로 참이다** — 새 브레이크포인트가
     생겨도 따라 고칠 사본이 없다(41차 판을 남겨 두면 두 벌이 나란히 낡는다).
   ⚠ 최소 폭은 **글자 수**로 잡는다(px 아님) — `.tx` 는 `--fs-xs` 이고 한글 한 자가 대략
     1.9ch 이므로 `24ch` ≈ 한글 12자다. 「감량기 · 이번 달분」이 읽히는 최소치이고,
     이보다 좁게 남느니 줄을 바꾸는 편이 낫다는 것이 41차가 배운 것이다.
   ⚠ `.agd--stack` 은 그대로 뺀다(41차 판단 그대로) — 그 변형은 `.tx` **뒤에** 자기
     본문(`.agd-sub`)이 오는 모양이라 `order:1` 을 주면 본문이 지표 줄보다 위로 올라간다.
   ⚠ 반드시 `.grow-item .tx`·`.agd .tx`(위) **뒤에** 둔다 — 명시도가 같아 순서가 이긴다. */
.agd, .grow-item { flex-wrap: wrap; row-gap: var(--sp-2); }
.agd:not(.agd--stack) > .tx, .grow-item > .tx { flex: 1 1 24ch; order: 1; }

/* ── 기록 탭 ───────────────────────────────────────────────────────────── */
/* 차트 | 목표 패널 2단 */
.mx { display: grid; grid-template-columns: 1fr 220px; gap: 20px; align-items: center; }
.goalrow { display: flex; flex-direction: column; gap: 10px; }
.goalrow .st { display: flex; justify-content: space-between; align-items: baseline;
               font-size: var(--fs-xs); color: var(--ink-45); }
.goalrow .st b { font-size: 1.1rem; font-weight: 600; color: var(--ink);
                 font-family: var(--font-num); font-variant-numeric: tabular-nums; }
/* 캘린더 | 그날 상세 2단 */
.rec-grid { display: grid; grid-template-columns: 1fr 320px; gap: 14px;
            align-items: start; margin-top: 14px; }
.day-panel { position: sticky; top: 70px; }
/* 하루 기록 캘린더 — 홈 캘린더(.cal)와 같은 엔진, 칸 안에 담는 것만 다르다 */
/* 🔴 21차 — `.cal td` 의 `overflow:hidden` 을 **여기서 푼다**. 62px 은 상한이 아니라
   **최솟값**이어야 한다: 4요소(일자·체중·복약 점 3~4·kcal)가 다 있는 날에 잘리면
   **잘린 자리에 아무 표시가 안 남는다**(§7-12 D · 자동 검사가 원리적으로 못 보는 종류).
   🔑 *줄이 길어지는 것이 값이 사라지는 것보다 싸다* — 안 보이는 실패를 보이는 실패로. */
.cal--rec td { height: 62px; padding: 5px 7px; overflow: visible; }
.cal--rec .meds { display: flex; gap: 3px; margin-top: 4px; }
.cal--rec .meds i { width: 7px; height: 7px; border-radius: 50%; display: block; }
/* 🔴 복약 점은 **무채**다. 색 헌법 §4-2 — 상태색(ok/warn/stop)은 판정 뷰가 준 값에만 쓴다.
   「약을 먹었다」는 판정이 아니라 회원이 적은 사실이라 초록으로 칠할 자리가 아니다. */
.cal--rec .meds .y { background: var(--ink); }
.cal--rec .meds .n { background: transparent; border: 1.5px solid var(--ink-20); }
.cal--rec .kc { font-size: .6875rem; color: var(--ink-45);
                font-variant-numeric: tabular-nums; margin-top: 2px; }
.legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: .75rem;
          color: var(--ink-45); margin-top: 8px; align-items: center; }
.legend i { width: 7px; height: 7px; border-radius: 50%; display: inline-block; margin-right: 4px; }
/* 그날 끼니 한 줄 */
.meal { display: flex; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--ink-08);
        font-size: var(--fs-sm); align-items: baseline; }
.meal:last-child { border-bottom: none; }
.meal .slot { color: var(--ink-45); font-size: var(--fs-xs); width: 34px; flex: none; }
.meal .items { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.meal .kcal { color: var(--ink-45); font-size: var(--fs-xs);
              font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── 출고관리 탭 ───────────────────────────────────────────────────────── */
/* 트랙 한 줄 = 이름 | 진행바 | 값(남음 + 결제·나감) */
.stk { display: grid; grid-template-columns: 56px 1fr auto; gap: 6px 14px;
       align-items: center; padding: 7px 0; }
.stk .k { font-size: var(--fs-xs); color: var(--ink-70); font-weight: 700; }
.stk .v { font-size: var(--fs-sm); white-space: nowrap; }
.stk .v b { font-family: var(--font-num); font-size: 1.25rem; font-weight: 600;
            font-variant-numeric: tabular-nums; }
.stk .s { font-size: .75rem; color: var(--ink-45); white-space: nowrap;
          font-variant-numeric: tabular-nums; }
/* 목업의 막대 굵기(8px · 완전 둥근) — 엔진은 `.prog-bar` 하나를 그대로 쓴다 */
.stk .prog-bar, .goalrow .prog-bar { height: 8px; border-radius: var(--r-full); }

.ledger { background: var(--paper); border: 1px solid var(--ink-08); border-radius: var(--r-md);
          box-shadow: var(--shadow-1); overflow: hidden; }
/* 🔴 [+결제]·[+출고] 는 **원장 안**에 산다. 카드 밖에 두면 「무엇에 더하는 것인지」가
   흐려지고, 저장 뒤 새 행이 어디에 들어갔는지 눈이 못 따라간다. */
.lg-tools { display: flex; gap: 8px; align-items: center; padding: 12px 16px;
            border-bottom: 1px solid var(--ink-08); flex-wrap: wrap; }
.dgh { display: flex; align-items: baseline; gap: 8px; padding: 14px 16px 4px;
       font-weight: 800; font-variant-numeric: tabular-nums; }
.dgh .wd { font-size: var(--fs-xs); color: var(--ink-45); font-weight: 500; }
.dg-sep { border-top: 1px solid var(--ink-08); margin-top: 8px; }
.ev { display: grid; grid-template-columns: 44px 1fr auto; gap: 10px; align-items: baseline;
      padding: 7px 16px 7px 20px; cursor: pointer; min-height: 40px;
      width: 100%; text-align: left; border: 0; background: none; font: inherit; color: inherit; }
.ev:hover { background: var(--paper-dim); }
.ev:focus-visible { outline: 3px solid var(--lime-shade); outline-offset: -3px; }
.ev .main { font-size: var(--fs-sm); min-width: 0; }
.ev .main .l2 { font-size: var(--fs-xs); color: var(--ink-45); }
.ev .amt { font-weight: 600; font-family: var(--font-num); font-variant-numeric: tabular-nums;
           white-space: nowrap; font-size: 1.05rem; }
.ev .amt .u { font-size: .75rem; font-family: var(--font); color: var(--ink-45); }
.ev-x { background: var(--paper-dim); padding: 12px 16px 14px 74px;
        border-top: 1px solid var(--ink-08); }
/* 영수증 — 결제 행을 펴면 무엇을 샀는지가 지면처럼 읽힌다 */
.rcpt { max-width: 340px; background: var(--paper); border: 1px dashed var(--ink-20);
        border-radius: var(--r-sm); padding: 12px 16px; font-size: var(--fs-sm); }
.rcpt .ln { display: flex; align-items: baseline; }
.rcpt .ln .dots { flex: 1; border-bottom: 1px dotted var(--ink-20); margin: 0 8px 5px; }
.rcpt .ln .v { font-variant-numeric: tabular-nums; }
.rcpt .tot { border-top: 1px dashed var(--ink-20); margin-top: 8px; padding-top: 8px; font-weight: 800; }
/* 라벨–값 정의 목록(행 확장 안) — `.kv` 는 격자라 짧은 쌍에 여백이 과하다 */
.x-kv { display: grid; grid-template-columns: auto 1fr; gap: 3px 14px; font-size: var(--fs-sm); }
.x-kv dt { color: var(--ink-45); }
.x-kv dd { margin: 0; overflow-wrap: anywhere; }
/* 🔴 먹색 언더바 — 스크롤이 어디에 있든 「지금 남은 것」이 화면에 남는다.
   원장이 길어질수록 위쪽 재고 카드가 밀려 올라가는데, 실장이 출고를 넣는 순간 확인해야
   하는 값이 바로 그 재고다. */
.underbar { position: sticky; bottom: 0; background: var(--ink); color: var(--paper);
            display: flex; gap: 24px; align-items: baseline; padding: 11px 18px;
            flex-wrap: wrap; border-radius: 0 0 var(--r-md) var(--r-md); }
.underbar .u-i { display: flex; align-items: baseline; gap: 7px; font-size: .75rem;
                 color: rgba(255,255,255,.62); white-space: nowrap; }
.underbar .u-i b { font-family: var(--font-num); font-size: 1.15rem; font-weight: 600;
                   color: var(--lime); font-variant-numeric: tabular-nums; }
/* 🆕 F-30-9 블록 12 — 회원 이용내역 「남은 기간」 먹판(회원 8화면의 「진한 판 ≥1」 그 하나).
   문법(먹색 판 · .u-i 라벨 · 라임 숫자)은 언더바를 그대로 쓰고, 자리가 카드 **머리**라
   sticky 와 아래쪽 반원 모서리만 되돌린다. ⚠ 기본형 **뒤**에 선다 — 특이도가 같아
   앞에 두면 조용히 무효다(어휘 변형 규약 · m/history 만 쓴다). */
.underbar--panel { position: static; border-radius: var(--r-md); }

/* 인라인 입력 카드 — 화면을 덮지 않고 원장 위에서 펴진다(시트로 띄우면 원장이 안 보인다) */
.addcard { background: var(--lime-wash); border: 1px solid var(--lime-deep);
           border-radius: var(--r-md); padding: 14px 16px; margin: 10px 16px; }
.addcard .ttl { display: flex; align-items: center; gap: 8px; font-weight: 800; margin-bottom: 10px;
                flex-wrap: wrap; }
.addcard .form-grid { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--sp-3); }
.addcard .foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }
/* 🆕 41차 — 데스크(F40)의 카드 자리. `.addcard` 의 `margin: 10px 16px` 는 **원장 안**(`.ledger`
   가 좌우 padding 0 인 카드)에서 그 16px 이 카드의 안쪽 여백 노릇을 하라고 있는 값이다.
   데스크는 이미 자기 판(`.stack`)에 여백이 있는 자리라 그 16px 이 **두 번** 붙어 좌우로
   32px 이 죽는다 — 720px 기둥에서는 안 보이지만 폰에서는 입력 칸이 그만큼 좁아진다.
   ⚠ `.addcard` 기본형을 고치지 않는다 — 원장 안의 들여쓰기가 그 값으로 서 있다. */
.deskslot > .addcard { margin-inline: 0; }
/* 출고 3택 — 「무엇이 나가나」를 먼저 고른다. 고른 것만 아래 칸이 바뀐다. */
.outk { min-height: 52px; flex-direction: column; align-items: flex-start; gap: 1px;
        background: var(--paper); }
.shell .outk { min-height: 52px; }
.outk[aria-pressed="true"] { border-color: var(--ink); box-shadow: 0 0 0 3px var(--lime); }
.outk .st { font-size: var(--fs-xs); font-weight: 500; color: var(--ink-45); }
/* 다음 달분 나갈 날 같은 예고 칩 줄 */
.soon { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }

/* ── 연락 ──────────────────────────────────────────────────────────────── */
/* 🔴 좌단은 **하한 270 · 상한 380 사이에서 자란다** (2026-08-20 44차 · 라이브 실데이터가 잡았다)
   옛 판은 모든 폭에서 `270px` 고정이었다. 1440px 실측: 스레드 칸 **864px** ↔ 목록 미리보기
   칸 **174px**, 필요 210px — **4행이 같은 자리에서 잘려** 「문자 · 복용 리마인드 —…」 넷이
   서로 구별되지 않았다. 목록은 «훑는» 면이라 꼬리를 잃으면 훑는 일 자체가 안 된다.
   ⚠ 잘림 자체가 결함인 것은 아니다 — 전문은 오른쪽 스레드에 있고, 이 저장소 규칙(43차)은
     *「그 값이 다른 데 또 적히면 잘려도 된다」* 이다. 결함은 **자리 배분**이다: 굶는 쪽이
     고정이고 남는 쪽이 자란다. 스레드는 말풍선이 `max-width:78%` 라 864px 을 쓰지도 않았다.
   🔑 ***고정 폭은 「좁을 때 지킬 값」이지 「넓을 때도 멈출 값」이 아니다.***
   ⓐ 30% 는 컨테이너가 **900px 을 넘을 때부터** 270 을 넘어선다 → 좁은 폭은 **한 픽셀도 안 바뀐다**
   ⓑ 상한 380 은 목록이 스레드를 잠식하지 않게 한다(1148px 컨테이너에서 좌 344 / 우 790) */
.comm-grid { display: grid; grid-template-columns: clamp(270px, 30%, 380px) 1fr; gap: 14px; align-items: start; }
.mlist td { padding: 9px 10px; }
.thread { display: flex; flex-direction: column; min-height: 380px; padding-top: 14px; }
.bub { max-width: 78%; border-radius: var(--r-md); padding: 8px 12px;
       font-size: var(--fs-sm); margin-bottom: 8px; overflow-wrap: anywhere; }
.bub.mem { background: var(--ink-08); align-self: flex-start; }
.bub.stf { background: var(--lime-wash); border: 1px solid var(--lime-deep); align-self: flex-end; }
.bub .t { font-size: .6875rem; color: var(--ink-45); margin-top: 2px; }
/* 🔴 21차 결단 ①(§7-12 B-1) — **회원 앱에서는 내 말이 오른쪽**이다. 메신저 관습이 그렇고,
   회원 앱은 회원이 쓰는 앱이다. 실장 콘솔은 그대로 둔다(거기서 「내 말」은 실장의 말이다).

   🔑 뒤집는 것은 **자리뿐이고 색은 아니다.** 클래스 뜻은 `말한 사람`(mem=회원·stf=실장)이라
      색이 곧 신원이다 — 뷰마다 색까지 바꾸면 같은 대화가 두 화면에서 다른 색 체계로 서고,
      실장이 회원 화면을 함께 들여다볼 때(원내 태블릿의 실제 동선) 누가 한 말인지 어긋난다.
      자리는 보는 사람의 관습, 색은 말한 사람의 신원. 축이 둘이라 클래스도 둘이다.
   ⚠ 클래스 이름을 뒤집지 않는 이유 — `.bub.stf` 가 회원의 말에 붙으면 이름이 거짓말이 된다.
      뷰 수식자를 부모에 두면 이름은 계속 참이다. */
.thread--me-right .bub.mem { align-self: flex-end; }
.thread--me-right .bub.stf { align-self: flex-start; }
/* 🔴 알림톡 **발송 사건**이 대화 흐름에 남는다 — 안 남기면 실장이 "말이 없다"고 읽는데
   실제로는 공식 안내가 세 번 나갔을 수 있다(맥락이 두 화면으로 갈리던 자리). */
.sysev { display: flex; align-items: center; gap: 10px; margin: 6px 0 10px;
         font-size: var(--fs-xs); color: var(--ink-70); }
.sysev::before, .sysev::after { content: ""; flex: 1; border-top: 1px solid var(--ink-08); }
.sysev .sc { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--ink-20);
             border-radius: var(--r-full); padding: 3px 14px; background: var(--paper); }
.composer { display: flex; gap: 8px; padding: 12px 0 0; border-top: 1px solid var(--ink-08);
            align-items: center; }
.composer .input { flex: 1; }
/* 🔴 회원 앱(`/m/messages/`)의 입력줄 — 대화가 길어져도 **답장할 자리가 화면을 안 떠난다.**
   흐름 안에 두면 목록 길이만큼 아래로 밀려, 옛 대화를 읽으러 올라간 순간 입력칸이 사라진다.
   ⚠ 하단 탭바가 화면 아래를 이미 덮고 있으므로 그 높이만큼 띄운다(겹치면 보내기가 안 눌린다). */
.composer--sticky { position: sticky; z-index: 9; background: var(--paper);
                    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
                    padding-bottom: var(--sp-3); }
/* 승인 템플릿 갤러리 */
.gal { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
       gap: 10px; padding-top: 14px; }
.tpl { border: 1px solid var(--ink-08); border-radius: var(--r-md); padding: 12px 14px;
       background: var(--paper); display: flex; flex-direction: column; gap: 6px; }
.tpl .tt { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
           font-weight: 700; font-size: var(--fs-sm); }
.tpl .bd { font-size: var(--fs-xs); color: var(--ink-70); flex: 1; }
.tpl .ft { display: flex; justify-content: flex-end; gap: var(--sp-2); flex-wrap: wrap; }
/* 연락 패널 안의 한 줄짜리 템플릿 바 */
.tplbar { display: flex; gap: 8px; overflow-x: auto; padding: 10px 0 2px; }
.tplchip { flex: none; min-height: var(--touch); border: 1px solid var(--ink-20);
           border-radius: var(--r-md); padding: 6px 14px; font: inherit;
           font-size: var(--fs-sm); font-weight: 700; background: var(--paper);
           text-align: left; cursor: pointer; color: var(--ink); }
.tplchip .st { display: block; font-size: .6875rem; font-weight: 500; color: var(--ink-45); }
.tplchip:disabled { opacity: .5; cursor: not-allowed; }
.tplchip.armed { background: var(--ink); color: var(--lime); border-color: var(--ink); }

/* ── 연락 패널 — 화면을 떠나지 않고 답장한다 ───────────────────────────────
   🔴 홈에서 [연락하기]를 누를 때 다른 화면으로 이동하면 **처리하던 목록의 자리를 잃는다**
      (연속 처리 동선이 끊긴다). 그래서 오른쪽에서 패널이 밀려 나온다. */
.overlay { position: fixed; inset: 0; background: rgba(35,25,22,.30); z-index: 80; }
.cpanel { position: fixed; top: 0; right: 0; bottom: 0; width: min(400px, 94vw);
          background: var(--paper); z-index: 90; box-shadow: var(--shadow-2);
          display: flex; flex-direction: column; }
.cpanel .ch { padding: 16px 18px 8px; border-bottom: 1px solid var(--ink-08); }
.cpanel .cb { flex: 1; overflow: auto; padding: 14px 18px; display: flex; flex-direction: column; }
.cpanel .composer { padding: 12px 18px; border-top: 1px solid var(--ink-08); }
.cpanel .tplbar { padding-inline: 18px; }
/* 입력줄 묶음은 안 줄어든다 — 스레드(.cb)만 늘고 줄어든다 */
.cpanel .pnl-foot { flex: none; }

/* ── 목업의 반응형 — 2단은 900px 에서 눕는다 ─────────────────────────────── */
/* 🔴🔴 43차 — **홈 2단만 더 일찍 눕는다(1200px).** 나머지 셋(기록·연락·측정)은 900 그대로다.
   왜 홈만 다른가 — 이 격자의 오른쪽 단이 **폭의 42.5%** 밖에 못 받는데(1.15 : 0.85 이므로
   0.85/2.0), 거기 사는 것이 실장이 홈에서 하는 **일 넷**이다(연락·정체·출고·복용 다름).
   왼쪽의 캘린더는 41차 주석이 스스로 적었듯 *"그날 약속을 훑는 지도"* 다.
     실측 1024px — 오른쪽 단 **321px**, 「이번 달 출고」 18행이 사정 문장을 **0px** 로 눌렀다.
     식으로도 같은 말이다: 한 줄에 필요한 폭 ≈ 490px → `0.425 × (W − 282) ≥ 490` → **W ≥ 1440**.
     즉 이 배분은 **1440px 아래 어느 폭에서도** 오른쪽 단을 못 채운다.
   🔑 *폭 배분이 우선순위를 배반하고 있었다* — 지도에 더 주고 일에 덜 줬다.
   ⚠ 비율을 뒤집는 길(0.85 : 1.15)도 있었지만 택하지 않았다: 1024 에서 캘린더가 289px 로
     내려가 날짜 칸이 41px 이 되고, 그 칸에 **이름을 적는 것**이 이 캘린더의 설계다
     (`.cal-nm` · "점이나 숫자만 있으면 또 눌러야 한다"). 한쪽을 살리려고 다른 쪽을 깨뜨린다.
   → 좁아지면 **눕힌다**. 눕는 순간 둘 다 전폭(1024 에서 756px)을 받아 아무것도 안 잘린다.
   ⚠ `order: -1`(그룹 넷을 먼저)은 이 격자의 규칙이므로 **같은 블록 안에** 둔다 — 두 값이
     다른 브레이크포인트에 흩어지면 1200~900 구간에서 캘린더가 다시 위로 올라온다. */
@media (max-width: 1200px) {
  .dash-grid { grid-template-columns: 1fr; }
  /* 🆕 41차 — **눕는 순간 읽는 차례가 뒤집힌다.** 2단에서는 캘린더(왼)와 그룹 넷(오른)이
     나란히 보이지만, 한 단이 되면 DOM 순서 그대로 6주 캘린더 + 아젠다를 **다 지나야**
     「새 메시지 · 오래 안 오심 · 이번 달 출고」에 닿는다 — 실장이 홈에서 하는 일은 그
     넷이고, 캘린더는 그날 약속을 훑는 지도다.
     🔴 **DOM 은 안 건드린다.** 순서를 코드로 바꾸면 넓은 화면의 좌우 배치(1.15fr/.85fr)까지
        따라 뒤집히고, 탭 순서·스크린리더 읽는 차례도 함께 뒤집힌다 — 여기서 필요한 것은
        **좁은 화면에서의 시각적 차례 하나**다.
     ⚠ `order` 는 grid 자식에 듣는다(`.dash-grid` 는 grid) · 넓은 화면은 이 블록 밖이라 무변경.
     ⚠ 캘린더 기본 접힘은 **안 한다** — `foldBox` 는 절 전체를 `<details>` 로 감싸는 부품이라
       달 이동(‹ ›)·날짜 고르기가 summary 밖 본문에 있는 이 카드에 그대로 씌우면 「고른 날」
       상태와 접힘 상태가 서로를 지운다(41차 판단 · 보고에 남긴다). */
  .dash-grid > :nth-child(2) { order: -1; }
}
@media (max-width: 900px) {
  .rec-grid, .comm-grid, .mx { grid-template-columns: 1fr; }
  .day-panel { position: static; }
  .mhead .row2 { gap: 12px; }
  /* ⚠ 홈 2단(`.dash-grid`)과 그 `order` 는 여기 없다 — 43차에 **1200px 블록으로 함께** 옮겼다
     (그 자리 주석이 왜인지 적는다). 둘 중 하나만 옮기면 1200~900 구간에서 캘린더가
     다시 위로 올라온다 — 같은 사실을 두 블록이 나눠 들면 그렇게 갈라진다. */
}

/* 🆕 41차 — 메뉴판 격자가 폰에서 **1열**이었다(`.pickgrid` = auto-fill minmax(180px,1fr)).
   회원 셸 안쪽 폭이 화면폭 − 56 이라 iPhone 15(393px)에서 337px → 180px 짜리 두 칸이
   안 들어가 한 칸이 된다. 한 분류를 열면 24 타일이 세로로 ≈2,200px 로 늘어서고,
   그 아래 「직접 적기」·저장 버튼은 화면 밖으로 밀린다.
   🔴 `auto-fill` 을 **고정 2열로** 바꾼다 — `minmax(0, 1fr)` 의 0 이 요점이다(180px 을
      남겨 두면 좁은 폭에서 격자가 다시 1열로 무너진다).
   ⚠ 타일이 좁아져 상품·음식 이름이 두세 줄로 접히는데, `.pickcard` 가 `min-height: 84px`
     **+ 높이를 칸에 꽉 채우는 선언**이 함께 있어 한 행의 두 칸이 같이 자란다.
   ⚠ 회원 셸로 좁히지 않는다 — 같은 격자를 직원 정가표(price-board · lifecycle)도 쓰고,
     폰 폭에서 1열이 되는 문제는 그쪽도 똑같다(한 어휘를 자리마다 갈라 두지 않는다). */
@media (max-width: 560px) {
  .pickgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
