/* 클로드비서 허브 화면 스타일 (PIN-OPS-012)
   목적: Claude Code 데스크톱 앱의 구조(좌 목록 / 중앙 대화 / 우 패널 / 하단 입력칸)를 축소 차용. 토큰은 자체 정의(외부 로고·워드마크 없음).
   테마: :root = 라이트, [data-theme=dark] = 다크, [data-theme=auto] 는 prefers-color-scheme 를 따른다.
   반응형: <760 모바일(하단 탭 + 서랍 + 시트) / 760～1099 2열 / ≥1100 3열. 폴드8 접힘(≈373px)·펼침(≈800px) 둘 다 이 구간에 든다.
   실패 지점: 없음(정적). 클래스 이름은 app.js 와 1:1 이므로 바꾸면 둘 다 바꾼다. */

/* ── 색 토큰(§37-4 · 39 회차 2026-09-09 — 37 의 전면 교체를 옛 색으로 되돌린 판) ──────────
   경위: 37 회차가 §37-1 을 따라 토큰을 통째로 갈아 끼웠으나 요청은 그것이 아니었다(§37-4).
     "화면 색을 다 바꾸라는 것이 아니라 섞어서 가독성을 높여 달라는 것" — 그래서 **옛 값이 기본**이고
     새 팔레트에서는 **넷만 남긴다**. 옛 값의 출처는 커밋 72f2021 의 이 파일이다.
   ★ 남긴 넷(아래 주석 `[39 남김]`) = ① 형광펜 배경 --accent-soft ② 상태 4색(ok·warn·plan·danger)
     ③ 카드와 바탕을 가르는 선 --line ④ 코드·경로의 보조면 --surface-2. 그 밖은 전부 옛 값이다.
   ★ 되돌리지 않은 것 = 고정색을 토큰으로 옮긴 세 자리(.badge.work · .verbar · .toast.err).
     그건 색 취향이 아니라 대비 결함 수정이라 §37-4 가 예외로 못 박았다.
   ★ §37-3 읽기 규칙(--read-w 62ch · 줄 간격 1.7 · tabular-nums · 표만 가로 스크롤)은 그대로 유지한다.
   ★ 화면 어디서도 새 색을 만들지 않는다 — 색이 필요하면 이 목록에서 고른다.
   ★ 어두운 판은 **두 벌**이다: [data-theme=dark](사람이 고른 어두움) · @media prefers-color-scheme 안의
     [data-theme=auto](시스템을 따름). 한쪽만 고치면 '시스템' 설정에서만 다른 색이 남는다 — 반드시 같이 고친다.
     (`_tools\39-test.ps1` 이 두 벌의 값이 같은지, 그리고 되돌린 값이 옛 값과 같은지 검사한다.) */
:root {
  --bg: #f7f6f3;           /* 바탕(따뜻한 회백) */
  --bg-2: #efede8;         /* 사이드바·패널 */
  --surface: #ffffff;      /* 카드·입력칸 */
  --surface-2: #ecebe6;    /* [39 남김] 코드·경로·말풍선 보조면 — 옛 #f2f0eb 보다 한 단계 눌러 코드가 면으로 보인다 */
  --ink: #1f1e1d;
  --ink-2: #4b4842;
  --muted: #7d786f;
  --line: #e2dfd8;         /* 선 — 카드/바탕을 가르는 선도 이 값 하나(따로 쓰려던 카드 선 토큰은 끝내 정의되지 않았다 · 56-ZE U9) */
  --line-2: #cfcbc2;
  --accent: #2f5bea;       /* 코발트: 행동·링크·선택 */
  --accent-ink: #ffffff;
  --accent-soft: #e5ecfb;  /* [39 남김] 강조 배경 = 형광펜(<mark>) 바탕 */
  --bubble-in: #edece8;    /* 48-5 · 상대 말풍선 회색(§50-2 새 색은 이것 하나) — 흰 카드 위에서 한 단 눌린 면 */
  /* 50-C ② · 내 말풍선 = accent 계열 배경 + 흰 글(두 판 공통). 어두운 판 --accent(#6b8cff)는 흰 글 대비 3.1 이라 한 단 짙은 값을 따로 둔다. 대비 4.5 이상은 50-C-test 가 잰다. */
  --bubble-out: #2f5bea; --bubble-out-ink: #ffffff;
  /* [39 남김] 상태 4색 — 칩·배지에만. 본문 글자색으로 쓰지 않는다(색만으로 뜻을 전하지 않기 위해 늘 낱말과 함께 둔다). */
  --ok: #2f7d4f; --ok-soft: #e2f0e7;         /* 완료 */
  --warn: #a86a12; --warn-soft: #f8ecd8;     /* 대기 */
  --plan: #6a5aa8; --plan-soft: #ece8f7;     /* 기획 */
  --danger: #b23b3b; --danger-soft: #f8e5e3; /* 오류 */
  /* 상태색 면 위 글자는 두 판 모두 어두워야 한다(밝은 판 글자와 같은 값). 판마다 덮어쓰지 않는다. */
  --warn-ink: #1f1e1d;
  --info: #2f5bea;
  --radius: 12px; --radius-s: 8px;
  /* 55-ZF 토큰(정의만 · 값은 옛 글자 그대로) */
  --fs-xs: 11px;
  --fs-s: 12.5px;
  --fs-m: 14.5px;
  --fs-l: 16px;
  --fs-xl: 20px;
  --sp-1: 4px;
  --sp-2: 6px;
  --sp-3: 8px;
  --sp-4: 12px;
  --sp-5: 16px;
  --r-xs: 4px;
  --r-pill: 999px;
  --z-head: 5;
  --z-float: 30;
  --z-panel: 40;
  --z-drawer: 45;
  --z-modal: 50;
  --z-toast: 85; /* U10 · 메뉴·시트(80)·그 어둠막(79) 위 — 시트를 연 채로 알림이 안 보이던 것(옛 60) */
  --z-bar: 70;
  --z-menu: 80;
  --tap: 40px; /* 정의만 · 쓰는 회차는 따로(값이 바뀐다) */
  --scrim: rgba(0,0,0,.35);
  /* /55-ZF 토큰 */
  /* U7 · 단추 높이 두 단 — 머리 줄 원형·아이콘 단추 = --ctl-s(36) · 입력 줄·폰 누르는 자리 = --tap(40).
     옛 38px(입력 바 원형 단추·입력칸)는 51-F 40px 에 늘 덮이던 죽은 값이라 없앴다(화면 무변경).
     글자 40px·36px 로 남은 자리 = 51-F 덩이 · 47-A 절 머리 막대 음수 여백(옛 시험이 그 글자를 붙든다 · 값은 같다). */
  --ctl-s: 36px;
  /* 읽기 폭(§37-3). ch = '0' 글자 폭이라 한글은 한 줄에 약 34자가 된다 — 좁으면 이 값 하나만 올린다. */
  --read-w: 62ch;
  --shadow: 0 1px 2px rgba(31,30,29,.05), 0 8px 24px rgba(31,30,29,.06);
  --mono: ui-monospace, "Cascadia Mono", "D2Coding", "Consolas", monospace;
  --sans: "Pretendard", -apple-system, BlinkMacSystemFont, "Malgun Gothic", "맑은 고딕", "Segoe UI", system-ui, sans-serif;
  color-scheme: light;
}
/* 어두운 판 — 아래 두 블록은 값이 **글자 하나까지 같아야** 한다(39-test 가 검사한다).
   [39 남김] 은 --surface-2 · --line · --accent-soft · 상태 4색. 그 밖은 옛 값(72f2021)이다. */
[data-theme="dark"] {
  --bg: #1a1917; --bg-2: #201f1d; --surface: #262523; --surface-2: #22262d;
  --ink: #e8e4dc; --ink-2: #c9c4ba; --muted: #8f897e; --line: #2b3038; --line-2: #45423d;
  --accent: #6b8cff; --accent-ink: #0e1224; --accent-soft: #1e2738;
  --bubble-in: #34332f;
  --bubble-out: #3a5fd9; --bubble-out-ink: #ffffff;
  --ok: #6fbf8b; --ok-soft: #1a2a21; --warn: #e0a75a; --warn-soft: #2c2419;
  --plan: #a99bdd; --plan-soft: #221f2e; --danger: #e08585; --danger-soft: #2c1d1d;
  --info: #6b8cff;
  --shadow: 0 1px 2px rgba(0,0,0,.35), 0 8px 24px rgba(0,0,0,.35);
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] {
    --bg: #1a1917; --bg-2: #201f1d; --surface: #262523; --surface-2: #22262d;
    --ink: #e8e4dc; --ink-2: #c9c4ba; --muted: #8f897e; --line: #2b3038; --line-2: #45423d;
    --accent: #6b8cff; --accent-ink: #0e1224; --accent-soft: #1e2738;
    --bubble-in: #34332f;
    --bubble-out: #3a5fd9; --bubble-out-ink: #ffffff;
    --ok: #6fbf8b; --ok-soft: #1a2a21; --warn: #e0a75a; --warn-soft: #2c2419;
    --plan: #a99bdd; --plan-soft: #221f2e; --danger: #e08585; --danger-soft: #2c1d1d;
    --info: #6b8cff;
    --shadow: 0 1px 2px rgba(0,0,0,.35), 0 8px 24px rgba(0,0,0,.35);
    color-scheme: dark;
  }
}

* { box-sizing: border-box; }
/* 6b-8: 좁은 폭(폴드8 커버)에서 긴 한 줄이 페이지 자체를 가로로 밀던 것을 막는다.
   본문 스크롤은 .content 가 맡으므로 문서 레벨 가로 스크롤은 언제나 사고다. */
html, body { margin: 0; padding: 0; height: 100%; overflow-x: hidden; }
body { background: var(--bg); color: var(--ink); font-family: var(--sans); font-size: var(--fs-m); line-height: 1.55; -webkit-text-size-adjust: 100%; overflow: hidden; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--accent); }
code, pre, .mono { font-family: var(--mono); font-size: .92em; }
[hidden] { display: none !important; }
.boot { padding: 40px; color: var(--muted); text-align: center; }
.muted { color: var(--muted); }
.small { font-size: var(--fs-s); }
.dim { opacity: .55; }
.off { opacity: .45; filter: grayscale(1); }
.spacer { flex: 1; }
.row { display: flex; align-items: center; gap: var(--sp-3); }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pre { white-space: pre-wrap; word-break: break-word; }
hr.sep { border: 0; border-top: 1px solid var(--line); margin: var(--sp-4) 0; }

/* ── 버튼·칩·입력 ── */
/* 33-9-1: 길게 누르기를 쓰는 버튼([📷])이 생겨 글자 선택·안드로이드 길게누르기 팝업을 버튼 전체에서 끈다. */
.btn { border: 1px solid var(--line-2); background: var(--surface); border-radius: var(--radius-s); padding: 7px var(--sp-4); font-size: 13.5px; font-weight: 500; color: var(--ink); display: inline-flex; align-items: center; gap: var(--sp-2); line-height: 1.2; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.btn:hover { background: var(--surface-2); }
.btn:active { transform: translateY(1px); }
.btn.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn.primary:hover { filter: brightness(1.08); background: var(--accent); }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover { background: var(--surface-2); }
.btn.danger { color: var(--danger); border-color: var(--danger-soft); }
.btn.sm { padding: var(--sp-1) 9px; font-size: var(--fs-s); }
.btn.icon { padding: var(--sp-2) var(--sp-3); }
.btn:disabled { opacity: .45; cursor: default; transform: none; }
.chip { display: inline-flex; align-items: center; gap: var(--sp-1); padding: 1px var(--sp-3); border-radius: var(--r-pill); font-size: 11.5px; font-weight: 600; background: var(--surface-2); color: var(--ink-2); white-space: nowrap; }
.chip.ok { background: var(--ok-soft); color: var(--ok); }
.chip.warn { background: var(--warn-soft); color: var(--warn); }
/* 51-B · 설정 「초기 PIN 그대로입니다」 띠 */
.pin-initial-band { background: var(--warn-soft); color: var(--warn); border-radius: var(--radius-s); padding: var(--sp-3) 10px; margin: var(--sp-3) 0; font-weight: 600; }
.chip.danger { background: var(--danger-soft); color: var(--danger); }
.chip.accent { background: var(--accent-soft); color: var(--accent); }
/* §37-1 상태 4색의 넷째(기획). 아직 쓰는 자리가 없지만 네 색이 한 벌이라 여기서 함께 정의한다. */
.chip.plan { background: var(--plan-soft); color: var(--plan); }
.badge { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; font-size: var(--fs-xs); font-weight: 700; display: inline-flex; align-items: center; justify-content: center; background: var(--accent); color: var(--accent-ink); }
.badge.work { background: var(--warn); color: var(--warn-ink); }
/* 17-D: 숫자 없는 점 배지(개인 대화 '안 읽은 새 답'). 건수가 아니라 있다/없다뿐이라 숫자를 안 쓴다. */
.badge.dot { min-width: 9px; width: 9px; height: 9px; padding: 0; border-radius: 50%; }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: var(--muted); }
.dot.on { background: var(--ok); } .dot.work { background: var(--warn); } .dot.wake { background: var(--accent); }
/* 절전·오프라인은 색이 없어 둘이 구별되지 않았다(6b-2). 점만으로는 못 읽으므로 글자(.dev-state)와 함께 본다. */
.dot.sleep { background: var(--muted); opacity: .55; } .dot.off { background: transparent; border: 1.5px solid var(--muted); }
input[type=text], input[type=search], input[type=password], input[type=time], input[type=number], select, textarea {
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); padding: 7px 10px; color: var(--ink); outline: none; min-width: 0;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
select.sm, input.sm { padding: var(--sp-1) var(--sp-3); font-size: var(--fs-s); }
.switch { position: relative; width: 38px; height: 22px; border-radius: 11px; background: var(--line-2); border: 0; padding: 0; flex: none; transition: .15s; }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: .15s; box-shadow: 0 1px 2px rgba(0,0,0,.3); }
.switch.on { background: var(--accent); }
.switch.on::after { left: 19px; }
.switch:disabled { opacity: .4; }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px var(--sp-5); }
.card + .card { margin-top: 10px; }
.card h3 { margin: 0 0 var(--sp-3); font-size: 14px; }
.card h4 { margin: var(--sp-4) 0 var(--sp-2); font-size: 13px; color: var(--ink-2); }
.kv { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-1) var(--sp-4); font-size: 13px; }
.kv dt { color: var(--muted); } .kv dd { margin: 0; }
.list { list-style: none; margin: 0; padding: 0; }
.list li { padding: var(--sp-3) 0; border-top: 1px solid var(--line); }
.list li:first-child { border-top: 0; }
.list.tight li { padding: 5px 0; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th, .tbl td { text-align: left; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); vertical-align: top; }
.tbl th { color: var(--muted); font-weight: 600; font-size: 12px; }
.tbl-wrap { overflow-x: auto; max-width: 100%; }
.nowrap { white-space: nowrap; }

/* ── 문자·통화 검수 큐(P8-2) ──
   왜 max-width 와 줄수 제한을 같이 거나: 한글 문자 본문은 공백 없이 길게 이어지는 일이 많아
   word-break 없이 두면 표 한 칸이 화면 밖까지 늘어나 버튼이 안 보인다. 3줄까지만 보이고 나머지는 잘린다. */
.tri-prev { max-width: 34em; max-height: 4.4em; overflow: hidden; line-height: 1.45; word-break: break-word; overflow-wrap: anywhere; white-space: pre-wrap; color: var(--ink-2); }
.seg { display: inline-flex; gap: var(--sp-1); flex-wrap: wrap; }

/* ── 로그인 ── */
.login-wrap { min-height: var(--app-h, 100svh); display: flex; align-items: center; justify-content: center; padding: 24px 18px calc(24px + env(safe-area-inset-bottom)); background: var(--bg); }
.login-card { width: 100%; max-width: 360px; background: var(--surface); border: 1px solid var(--line); border-radius: 18px; padding: 26px 22px 20px; box-shadow: var(--shadow); }
.login-card .brand { display: flex; align-items: center; gap: 10px; margin-bottom: var(--sp-1); }
.login-card .brand img { width: 36px; height: 36px; border-radius: 9px; }
.login-card h1 { margin: 0; font-size: var(--fs-xl); letter-spacing: -.02em; }
.login-card .sub { margin: 0 0 18px; color: var(--muted); font-size: var(--fs-s); }
.pin-display { display: flex; gap: 10px; justify-content: center; margin: var(--sp-1) 0 14px; }
.pin-dot { width: 13px; height: 13px; border-radius: 50%; background: var(--line-2); transition: .15s; }
.pin-dot.on { background: var(--accent); transform: scale(1.1); }
.keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); }
.keypad button { padding: 13px 0; font-size: 19px; font-weight: 600; border: 1px solid var(--line); background: var(--surface); border-radius: var(--radius); }
.keypad button:active { background: var(--surface-2); }
.keypad button.wide { font-size: 13px; color: var(--muted); }
.login-err { color: var(--danger); font-size: 13px; min-height: 20px; margin-top: 10px; text-align: center; }
.login-foot { margin-top: var(--sp-4); text-align: center; font-size: 11.5px; color: var(--muted); }
/* 47-C(§50-1) · 메시지 구역 앞 PIN 화면. 점·키패드는 로그인 부품 그대로, 겉 틀만 여기. 토큰만 써서 다크·라이트 두 판 공통.
   새 @media 없음 — PC 3단·폴드 네 모양 모두 가운데 한 장(max-width)이다. */
.pin-gate { max-width: 340px; margin: 8vh auto 24px; padding: 26px 22px 20px; background: var(--surface); border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow); text-align: center; }
.pin-gate.compact { margin: 0 auto; border: 0; box-shadow: none; padding: var(--sp-1) 0 0; background: transparent; }
.pin-gate .pin-lock { font-size: 34px; line-height: 1; margin-bottom: var(--sp-3); }
.pin-gate .pin-title { font-weight: 700; font-size: var(--fs-l); color: var(--ink); }
.pin-gate .pin-sub { margin: var(--sp-1) 0 var(--sp-5); }
.pin-gate .pin-msg { margin: 0 0 var(--sp-3); }
.pin-gate .pin-key.ok { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.pin-gate .pin-key.back { color: var(--muted); }
.pin-gate.busy .keypad { opacity: .5; pointer-events: none; }
.pin-gate.shake .pin-display { animation: pin-shake .3s; }
@keyframes pin-shake { 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
.lock-row .lock-left { color: var(--muted); }
.nav.sub2 .lock-count { font-size: 11.5px; }
/* 50-D · 사이드바 대화 줄 두 단 — 위 = 굵은 이름(말줄임) + 시각 · 아래 = 「🔒 잠김」 또는 미리보기(50-C ③) + 안 읽음 점.
   이름 칸 최소 4.5em(250px 에서 4글자 이상) · 아바타 머리글자 12px 굵게. 새 @media 없음 — PC 3단·폰 네 폭 공통. */
.nav.sub2.chat-row { align-items: center; padding-top: 5px; padding-bottom: 5px; }
.chat-rowbody { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.chat-row1, .chat-row2 { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; line-height: 1.3; }
.nav.sub2 .chat-row1 .chat-nm { flex: 1 1 auto; min-width: 4.5em; font-weight: 700; color: var(--ink); }
.chat-row1 .chat-when { flex: none; margin-left: auto; padding-left: 0; font-size: var(--fs-xs); }
.chat-row2 .chat-prev, .chat-row2 .lock-count { flex: 1; min-width: 0; font-size: 11.5px; min-height: 1.3em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.chat-row2 .badge.dot { flex: none; margin-left: auto; }
.chat-row .chat-ava.xs { width: 26px; height: 26px; font-size: 12px; font-weight: 700; margin-right: 0; }
.phone-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); padding: var(--sp-1) 9px var(--sp-2) 20px; }
.phone-chip { border: 1px solid var(--line); cursor: pointer; font: inherit; font-size: 11.5px; font-weight: 600; padding: 2px 10px; }
.phone-chip.on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }

/* ── 셸 레이아웃 ── */
/* 51-C · 셸 높이 = --app-h(app.js applyAppHeight 가 visualViewport.height 를 적는다). 키보드가 뜨면 셸이 보이는 칸으로 줄고
   입력 바는 그 안 바닥에 남는다. JS 가 값을 못 넣으면 종전대로 100svh. 100svh 를 새로 쓰지 말고 이 var 꼴로만 쓴다. */
.shell { display: grid; grid-template-columns: 250px 1fr; grid-template-rows: var(--app-h, 100svh); height: var(--app-h, 100svh); }
.shell.with-panel { grid-template-columns: 250px 1fr 320px; }
.sidebar { background: var(--bg-2); border-right: 1px solid var(--line); display: flex; flex-direction: column; min-height: 0; }
/* ── 안전영역 위쪽 인셋(8-18-1) ────────────────────────────────────────────
   ★ 위쪽 인셋을 쓰는 자리는 아래 다섯뿐이고, 전부 `var(--sai-top, env(safe-area-inset-top))` 한 낱말로 읽는다.
     --sai-top 은 app.js applySafeTop() 이 실측해 **화면 높이의 25% 상한**을 씌운 값이다.
     왜: 안드로이드 WebView 가 앱 복귀 직후 부풀린 인셋을 준다 — 그대로 쓰면 머리가 통째로 아래로 밀린다
     (재희님 2026-09-10 13:05 캡처). JS 가 값을 못 넣으면 종전대로 env() 로 떨어진다(폴백). */
.side-head { display: flex; align-items: center; gap: var(--sp-3); padding: calc(12px + var(--sai-top, env(safe-area-inset-top))) 14px 8px; }
.side-head img { width: 26px; height: 26px; border-radius: 7px; }
.side-head .name { font-weight: 700; letter-spacing: -.02em; font-size: 15px; }
/* 40 ①② · 아이콘+이름 전체가 단추다(홈 이동 · 홈에서는 새로고침 · 길게 누르면 강력 새로고침).
   음수 여백으로 눌리는 면만 넓히고 머리줄 높이는 그대로 둔다 — 6b-12/8-18-1 의 머리 실측이 어긋나지 않게. */
.side-home { display: flex; align-items: center; gap: var(--sp-3); padding: 3px 5px; margin: -3px -5px; border: 0; background: none; color: var(--ink); border-radius: var(--radius-s); text-align: left; }
.side-home:hover { background: var(--surface-2); }
.side-home:active { transform: scale(.98); }
/* 44-E ① · 크기 규칙은 **부품 자신**에 붙인다. 종전에는 `.side-head img` 에만 있어서 44-A 가 같은 부품을 상단 내비에
   꽂자 SVG 가 내재 크기(512×512)로 그려져 띠 밖으로 쏟아졌다(집PC 크롬 실측). 어느 부모에 꽂아도 26px 이다. */
.side-home img { width: 26px; height: 26px; flex: none; border-radius: 7px; }
.side-home .name { font-weight: 700; letter-spacing: -.02em; font-size: 15px; white-space: nowrap; }
/* 40 ④ · ↻ 는 도는 동안만 돈다(아무 반응 없이 멎은 것처럼 보이지 않게). 회전은 기존 spin 키프레임을 쓴다. */
.side-head .refresh.spinning { animation: spin .9s linear infinite; }
/* 6b-7(§8-10): 사이드바는 자기만 스크롤한다. body 는 overflow:hidden 이고 .shell 이 100svh 격자라 문서 스크롤은 없다.
   overscroll-behavior:contain = 목록 끝에서 더 굴려도 뒤(본문·폰 드로어 배경)로 스크롤이 넘어가지 않게.
   scroll-behavior:auto = 재렌더 뒤 scrollTop 을 되돌릴 때 부드럽게 미끄러지지 않고 즉시 제자리로. */
.side-scroll { flex: 1; overflow-y: auto; overscroll-behavior: contain; scroll-behavior: auto; min-height: 0; padding: 0 var(--sp-3) var(--sp-3); }
.side-label { font-size: var(--fs-xs); color: var(--muted); text-transform: uppercase; letter-spacing: .06em; padding: 10px var(--sp-3) var(--sp-1); }
/* 44-B · 묶음 탭 줄(W1). 사이드바 머리 바로 아래 한 줄이고 **스크롤 영역 밖**이다 — 목록을 내려도 탭은 제자리에 남는다.
   가운데 .gtabs-scroll 만 가로로 밀린다(묶음이 늘어나도 화살표·＋·⋯ 는 늘 같은 자리).
   폰(≤759px)에서도 같은 줄을 쓴다 — 좌우 스와이프는 app.js bindGroupSwipe 가 이 줄에 건다. */
/* 44-E ②③ · 가운데 정렬 캐러셀. 재희님 지시 = "현재를 가운데, 양쪽은 반쯤 투명·반만 보이게, 1～3개".
   ★ 폭 계산은 CSS 두 값뿐이다 — 탭 폭 = 칸의 50% · 양 끝 여백(.gtab-pad) = 칸의 50%.
     그래서 고른 탭이 가운데 서면 이웃은 칸의 25% = 제 폭의 절반만 보이고, 한 번에 보이는 것은 늘 1～3개다.
     첫·마지막 탭도 여백 덕에 가운데 설 수 있다(보이는 여백 = 칸 절반 - 탭 절반).
   ★ 넘기기는 사람 스크롤이 아니라 app.js centerGroupTabs() 의 scrollTo 한 곳이다(overflow hidden · 폰 스와이프는 44-B 것).
   ★ ◀ ▶ 는 칸 위에 겹쳐 뜨고(자리 안 먹음), ＋ ⋯ 는 탭 줄 밖 .side-tools 로 옮겼다 — 종전 실측 칸 165px 이 줄던 원인. */
.gtabs { position: relative; display: flex; align-items: center; flex: none; padding: var(--sp-2) var(--sp-2) var(--sp-1); border-bottom: 1px solid var(--line); }
.gtabs-scroll { position: relative; display: flex; align-items: center; gap: 0; flex: 1; min-width: 0; overflow: hidden; scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 78%, transparent 100%); }
.gtabs-scroll::-webkit-scrollbar { display: none; }
.gtab-pad { flex: 0 0 50%; height: 1px; }
.gtab { display: flex; align-items: center; justify-content: center; gap: var(--sp-1); flex: 0 0 50%; min-width: 0; padding: 5px var(--sp-3); border: 0; background: transparent; border-radius: var(--radius-s); color: var(--muted); font-size: 12px; font-weight: 600; white-space: nowrap; opacity: .45; transition: opacity .2s; }
.gtab.on { opacity: 1; }
.gtab .nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.gtabs > .gtab-arrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 1; }
.gtabs > .gtab-arrow.prev { left: 4px; }
.gtabs > .gtab-arrow.next { right: 4px; }
.side-tools { display: flex; align-items: center; gap: 2px; flex: none; padding: var(--sp-3) var(--sp-3) 0; }
.side-tools .newchat { flex: 1; width: auto; margin-top: 0; }
.side-tools .gtab-act { height: 34px; width: 26px; font-size: 13px; }
.gtab:hover { background: var(--surface); color: var(--ink-2); }
.gtab.on { background: var(--surface); color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); }
.gtab .cnt { font-family: var(--mono); font-size: 10px; color: var(--muted); }
.gtab:focus-visible, .gtab-arrow:focus-visible, .gtab-act:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.gtab-arrow, .gtab-act { flex: none; width: 22px; height: 24px; border: 0; background: transparent; color: var(--muted); border-radius: var(--radius-s); font-size: var(--fs-xs); }
.gtab-arrow:hover:not(:disabled), .gtab-act:hover { background: var(--surface); color: var(--ink); }
.gtab-arrow:disabled { opacity: .28; }
/* 44-H · 이웃 글자가 읽히게(PC 3단 html.topnav3 한정 — 1279px 이하는 위 규칙 그대로).
   집PC 실측: 이웃이 칸 안에 49～51% 들어와도 한 글자도 안 보였다. 까닭 셋 —
     ① mask 가 양 끝 22% 동안 투명에서 시작해 이웃 반쪽 자리를 통째로 지웠다
     ② 이웃 글자는 제 탭 가운데 = 칸 가장자리 선 위라 절반은 칸 밖, 남은 절반도 흐림 구간
     ③ muted × .45 는 어두운 판 명암비 2.0.
   고침 = 흐림은 양 끝 14px 만 · 이웃은 ink-2 × .6(어두운 판 약 4.0) · 이웃 글자를 안쪽(고른 탭 쪽)으로 붙이고
   이름은 3.5em 까지(넘으면 말줄임 = 첫 글자가 남는다) · 이웃의 숫자는 숨김(수는 title 에 그대로).
   ★ 폭·위치는 안 바꾼다 — 가운데 정렬(app.js scrollLeft 계산)은 탭 폭 50% 한 값만 본다. */
html.topnav3 .gtabs-scroll {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%); }
html.topnav3 .gtab { opacity: .6; color: var(--ink-2); }
html.topnav3 .gtab.on { opacity: 1; color: var(--ink); }
html.topnav3 .gtab:has(+ .gtab.on) { justify-content: flex-end; padding-right: 2px; }
html.topnav3 .gtab.on + .gtab { justify-content: flex-start; padding-left: 2px; }
html.topnav3 .gtab:has(+ .gtab.on) .nm { max-width: 3.5em; }
html.topnav3 .gtab.on + .gtab .nm { max-width: 3.5em; }
html.topnav3 .gtab:has(+ .gtab.on) .cnt { display: none; }
html.topnav3 .gtab.on + .gtab .cnt { display: none; }
/* 44-B · 사업 태그 칩(`#08`·`#개인`·`#21`). 사이드바 번호 자리·파일 목록·검색 결과가 **같은 모양**을 쓴다. */
.tagchip { font-family: var(--mono); font-size: 10.5px; color: var(--muted); background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 1px 5px; flex: none; white-space: nowrap; }
.biz .num.tag { width: auto; min-width: 22px; }
/* 새 대화 — 사업 목록 맨 위. 왜: 첫 화면에서 "그냥 물어보기" 가 갈 곳. */
.newchat { display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left; margin-top: var(--sp-3); padding: var(--sp-3) 9px; border: 1px solid var(--line); background: var(--surface); border-radius: var(--radius-s); color: var(--ink); font-size: 13.5px; font-weight: 600; }
.newchat:hover { border-color: var(--accent); color: var(--accent); }
.newchat .ico { width: 18px; text-align: center; font-size: 13px; color: var(--accent); }
/* 그룹 머리(사업·개인·클로드비서 개발) — 눌러서 접고 편다. 애니메이션 없이 즉시(목록이 길어 스크롤이 튀는 것을 피한다). */
.side-group { display: flex; align-items: center; gap: var(--sp-2); width: 100%; text-align: left; margin-top: 10px; padding: var(--sp-2) var(--sp-3); border: 0; background: transparent; border-radius: var(--radius-s); color: var(--muted); font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
.side-group:hover { background: var(--surface); color: var(--ink-2); }
.side-group:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.side-group.open { color: var(--ink-2); }
.side-group .caret { width: 12px; flex: none; font-size: 10px; }
.side-group .nm { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.side-group .cnt { font-family: var(--mono); font-size: 10.5px; color: var(--muted); flex: none; }
.side-group-body { display: flex; flex-direction: column; }
/* 그룹 머리 줄: 접기 버튼 + (사업 그룹만) 새 사업 ＋. 버튼 안에 버튼을 넣을 수 없어 한 줄로 감쌌다. */
.side-group-head { display: flex; align-items: center; }
.side-group-head .side-group { flex: 1; min-width: 0; }
.side-group-add { flex: none; margin-top: 10px; padding: 5px 7px; border: 0; background: transparent; color: var(--muted); border-radius: var(--radius-s); font-size: 13px; line-height: 1; }
.side-group-add:hover { background: var(--surface); color: var(--ink); }
.side-group-add:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* 개인 절 — 사업과 다른 색으로 구분(업무 대화와 헷갈리지 않게). */
.biz.personal .num { color: var(--accent); }
/* §17-B 개인 대화 스레드 — 개인 줄 아래에 한 단 들여 붙는다(사업 줄과 층이 다르다는 것을 들여쓰기로만 표시). */
.biz.thread { padding-left: 20px; font-size: 13px; color: var(--muted); }
/* 17-C ④: 지금 열려 있는 대화는 한눈에 보여야 한다(메인 화면이 이 한 줄에 종속된다). 왼쪽 굵은 선 + 배경. */
.biz.thread.on { color: var(--ink); background: var(--surface); box-shadow: inset 2px 0 0 var(--accent); font-weight: 600; }
.biz.thread .num { width: 14px; font-size: 10px; }
/* §24-2 알림에서 들어온 카드 강조. 링크로 왔는데 카드가 여러 장이면 어느 것을 보라는 건지 알 수 없다.
   2.5초 뒤 클래스가 떨어지므로 테두리만 잠깐 켠다(레이아웃은 건드리지 않는다 — 켜질 때 화면이 밀리면 안 된다). */
.hit { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-s); }
/* 스레드 이력(개인 탭 메인 대화의 앞부분) — 답변 카드와 구분되게 왼쪽 선 하나로만 묶는다. */
.threadhist:not(:empty) { border-left: 2px solid var(--line); padding: var(--sp-1) 0 var(--sp-1) 10px; margin-bottom: var(--sp-3); display: flex; flex-direction: column; gap: 10px; }
/* 대화 머리(제목·건수·닫기) — 어느 대화를 보고 있는지 본문 위에서도 읽히게. */
.threadhist .thead { align-items: center; gap: var(--sp-2); }
.threadhist .turn { display: flex; flex-direction: column; gap: var(--sp-1); }
.threadhist .turn .q { font-weight: 600; color: var(--ink); white-space: pre-wrap; }
.threadhist .turn .a { color: var(--ink-2); font-size: 14px; }
.biz { display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left; padding: 7px 9px; border: 0; background: transparent; border-radius: var(--radius-s); color: var(--ink-2); font-size: 13.5px; }
.biz:hover { background: var(--surface); }
.biz.on { background: var(--surface); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
.biz .num { font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted); width: 22px; flex: none; align-self: flex-start; margin-top: 1px; }
/* 기획 §2-1: 사업명은 두 줄까지 접고 그래도 넘치면 말줄임. 전체 이름은 title 툴팁에 있다.
   -webkit-line-clamp 는 크롬·사파리·최신 파폭이 모두 지원한다(줄바꿈만 되고 말줄임이 없어도 읽기에는 문제 없음). */
.biz .nm { flex: 1; min-width: 0; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; white-space: normal; overflow: hidden; overflow-wrap: anywhere; line-height: 1.32; }
.biz .st { font-size: 10.5px; color: var(--muted); flex: none; }
/* 17-D: 개인 대화의 주고받은 건수. 알림이 아니라 사실이므로 회색 작은 글자로만 둔다(동그라미 금지). */
.biz .turns { font-family: var(--mono); font-size: 10.5px; color: var(--muted); flex: none; align-self: flex-start; margin-top: 2px; }
.biz .badge { align-self: flex-start; }
.biz .badge.dot { margin-top: 5px; }
/* 사업 한 줄 = [본문 버튼][⋯]. 버튼 안에 버튼을 넣을 수 없어 감쌌다. ⋯ 는 호버·포커스·열림일 때만 보인다. */
.bizrow { position: relative; display: flex; align-items: stretch; }
.bizrow .biz { align-items: flex-start; padding-right: 28px; } /* ⋯ 자리를 늘 비워 둔다 — 나타날 때 이름이 밀리지 않게 */
.bizrow .dots { flex: none; width: 24px; margin-left: -26px; padding: 0; border: 0; background: transparent; color: var(--muted); border-radius: var(--radius-s); font-size: 14px; line-height: 1; opacity: 0; }
.bizrow:hover .dots, .bizrow .dots:focus-visible, .bizrow .dots[aria-expanded=true] { opacity: 1; background: var(--surface-2); color: var(--ink); }
.bizrow .dots:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* 손가락에는 호버가 없다 — 폰 서랍에서는 항상 보인다.
   U14(2026-10-08) · 같은 까닭으로 파일 행 단추(평소 .55)와 폰 대화 말풍선 ⋯(평소 숨김)도 손가락 기기에서는 늘 보인다.
   두 규칙은 아래(파일 탐색기 절 · 48-1 절)보다 앞이라 클래스 하나를 더 붙여 세기로 이긴다(.row.fx-acts · .msg .msg-more).
   새 미디어 조건 줄은 만들지 않는다(tests/hub tap-targets 「줄머리 조건 수 그대로」) — 이미 있는 이 한 덩이에 더한다. */
@media (hover: none) { .bizrow .dots { opacity: .7; } .fx-row .row.fx-acts { opacity: 1; } .pchat .msg .msg-more { visibility: visible; opacity: .6; } }
/* 제자리 이름 편집 — 줄이 그대로 입력칸이 된다(미리보기 시트 없음). */
.bizrename { align-items: center; gap: var(--sp-3); padding: var(--sp-1) 9px; }
.bizrename input { flex: 1; min-width: 0; font-size: 13.5px; padding: var(--sp-1) var(--sp-2); }
/* ⋯ 메뉴 — 사이드바 스크롤에 잘리지 않게 body 에 붙이고 위치를 계산해 넣는다.
   z-index 는 80: ≤759px 에서 사이드바가 fixed·45(서랍 배경 44), 패널 40, 모달 50, 버전 막대 70이라(토스트는 U10 부터 85 = 이 메뉴 위)
   30이던 예전 값은 폰 폭에서 서랍 뒤로 깔렸다(재희님 "메뉴가 뒤로 가려짐", 6b-4). */
.bizmenu { position: fixed; z-index: var(--z-menu); min-width: 150px; padding: var(--sp-1); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); box-shadow: var(--shadow); }
.bizmenu button { display: block; width: 100%; text-align: left; padding: 7px 10px; border: 0; background: transparent; border-radius: var(--radius-s); color: var(--ink-2); font-size: 13px; }
.bizmenu button:hover { background: var(--bg-2); color: var(--ink); }
.bizmenu button.danger { color: var(--danger); }
/* 좁은 폭(≤430, 폴드8 접힌 화면·폰 서랍)은 드롭다운 대신 하단 시트 — 손가락 타깃을 키우고 화면 밖으로 나갈 여지를 없앤다. */
.bizmenu-bg { position: fixed; inset: 0; background: var(--scrim); z-index: 79; }
.bizmenu.sheet { top: auto; left: 0; right: 0; bottom: 0; width: auto; min-width: 0; padding: 6px 10px calc(10px + env(safe-area-inset-bottom));
  border-width: 1px 0 0; border-radius: 14px 14px 0 0; }
.bizmenu.sheet .sheet-title { padding: var(--sp-3) 10px var(--sp-2); font-size: 11.5px; color: var(--muted); overflow-wrap: anywhere; }
.bizmenu.sheet button { padding: 13px var(--sp-4); font-size: 15px; min-height: 46px; }
.bizmenu.sheet button.sheet-cancel { margin-top: var(--sp-1); text-align: center; color: var(--muted); background: var(--bg-2); }
.nav { display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left; padding: 7px 9px; border: 0; background: transparent; border-radius: var(--radius-s); color: var(--ink-2); font-size: 13.5px; }
.nav:hover { background: var(--surface); }
.nav.on { background: var(--surface); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
.nav .ico { width: 18px; text-align: center; font-size: 13px; color: var(--muted); }
/* 21-A 휴대폰 묶음 안의 줄(주소록·문자·통화·메세지). 사업 줄과 같은 들여쓰기로 "묶음에 속한 것"으로 읽히게 한다. */
.nav.sub { padding-left: 20px; }
.side-foot { padding: 8px 14px calc(10px + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); font-size: var(--fs-xs); color: var(--muted); display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
/* 51-O · 버전은 조각(관리코드·허브·앱·폰연결)마다 한 span — 좁은 서랍에서는 조각 단위로 아랫줄로 접힌다.
   조각이 그래도 넘치면(아주 좁은 폭) 잘리지 않고 말줄임으로 끝낸다. column-gap 은 조각 사이 간격, row-gap 은 접힌 줄 사이. */
.side-foot { column-gap: var(--sp-3); row-gap: 2px; }
.side-foot .ver { white-space: nowrap; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.side-foot .devs { display: flex; gap: var(--sp-2); align-items: center; }

.main { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--bg); }
/* ── 머리줄(.topbar) 한 자리 규칙 ──────────────────────────────────────────
   ★ 화면 조건 우선순위(6b-12에서 한 자리로 모음. app.js MQS 와 순서·문자열이 같아야 한다):
       ① 낮은 화면 LOW    '(max-height: 600px), (orientation: landscape) and (max-width: 1100px)'  — 머리 한 줄(제목+⋯)
       ② 좁은 폭  NARROW  '(max-width: 420px)'   — 버튼 셋을 ⋯ 로 접는다(JS 몫)
       ③ 두 줄    TWOROW  '(max-width: 600px)'   — .acts 를 둘째 줄로 내린다(CSS 몫)
     ①이 ②·③보다 세다. CSS 는 아래쪽 블록이 이기므로 LOW 블록(파일 끝쪽)이 TWOROW 블록보다 뒤에 있어야 하고,
     LOW 블록은 flex-wrap 을 nowrap 으로 되돌려 ③의 두 줄 배치를 명시적으로 끈다(둘 다 참인 가로 폰이 있다).
   ★ 6b-12 · 머리는 본문(.content) 보다 위 층에 둔다.
     왜: .content 는 스크롤 컨테이너라 브라우저가 하나의 층으로 늦게 그린다. 머리 글자가 조금이라도 상자 밖으로
     흘러 있으면 스크롤된 카드의 불투명 배경이 그 글자를 덮는다(스크롤 0 에서는 카드가 아직 아래에 있어 멀쩡해 보인다).
     z-index 5 = 본문 안 sticky(.outpins·.brf-head 3) 위, 패널 40·서랍 45·모달 50·버전막대 70·토스트 85 아래.
   ★ flex:none — .main 의 세로가 모자랄 때 머리가 먼저 찌그러지는 것을 막는다(머리는 늘 온전해야 한다). */
.topbar { --head-h: 44px; position: relative; z-index: var(--z-head); flex: none;
  display: flex; align-items: center; gap: var(--sp-3); padding: calc(8px + var(--sai-top, env(safe-area-inset-top))) 14px 8px; border-bottom: 1px solid var(--line); background: var(--bg); min-height: 48px; }
/* 실측 안전망(6b-12) — app.js measureHead() 가 "내용이 상자보다 높다" 고 판단하면 상한을 푼다.
   회전·안전영역 변화로 계산이 어긋나도 글자가 잘리지 않게 하는 마지막 방어선이다. */
.topbar.tall { max-height: none; }
.topbar .title { font-weight: 700; font-size: 15px; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar .sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ── 머리줄 버튼 묶음(6b-11) ────────────────────────────────────────────
   증상(재희님 2026-09-09 16:02 캡처): 사업 머리 버튼이 21-B-1 로 하나 늘어 폭이 모자라자
   버튼 묶음이 아래로 밀려 'PIN-OPS-012' 칩 줄 위에 겹쳤다.
   원인: 버튼이 .topbar 의 형제로 늘어서 있어 각자 줄어들며(기본 flex-shrink:1) 글자가 접히고,
     .topbar 가 한 줄(nowrap)이라 넘친 만큼이 제목·칩 상자를 덮었다.
   고침: 버튼은 app.js 가 .acts 한 상자로 묶고(넓은 화면은 종전과 같은 한 줄),
     좁은 폭에서는 그 상자만 통째로 둘째 줄로 내려 겹칠 자리를 없앤다.
   ★ 아래 420px 조건은 app.js 의 NARROW_MQ 와 문자열이 같아야 한다(그쪽이 버튼 셋을 ⋯ 로 접는다). */
.topbar .acts { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.topbar .acts > * { flex: none; white-space: nowrap; }

@media (max-width: 600px) {
  /* 두 줄 고정 — 1줄 [☰ 제목·칩] / 2줄 [버튼 묶음]. 버튼 줄은 접지 않고 넘치면 그 상자 안에서만 가로로 굴린다(6b-8 규칙). */
  .topbar { flex-wrap: wrap; row-gap: var(--sp-1); padding-bottom: var(--sp-2); }
  .topbar > .grow { flex: 1 1 auto; min-width: 0; }
  .topbar .acts { flex: 1 0 100%; width: 100%; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .topbar .acts::-webkit-scrollbar { width: 0; height: 0; }
}
/* 6b-7: 메인은 사이드바와 별개의 스크롤 컨테이너다(라우트가 바뀌면 여기만 맨 위로 돌아간다). */
/* 6b-8 · overflow-y:auto 는 overflow-x 를 auto 로 만든다 — 실제 가로 스크롤은 여기서 났다. 명시적으로 막는다.
   가로로 넘칠 수 있는 것(표·코드 블록)은 자기 상자 안에서만 스크롤한다(.md pre · .md table · .tbl-wrap). */
.content { flex: 1; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; min-height: 0; min-width: 0; padding: 14px var(--sp-5) 24px; }
.content.narrow { max-width: 900px; width: 100%; margin: 0 auto; }
.panel { background: var(--bg-2); border-left: 1px solid var(--line); overflow-y: auto; min-height: 0; padding: 12px 14px calc(14px + env(safe-area-inset-bottom)); font-size: 13px; }
.panel h3 { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; margin: 14px 0 var(--sp-2); }
.panel h3:first-child { margin-top: 0; }
.panel .box { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--sp-3) 10px; }
.panel .box + .box { margin-top: var(--sp-2); }
/* 왜: 상자 높이를 줄 높이의 배수로 맞춰 마지막 줄이 반쯤 잘리지 않게 하고, 넘칠 때만 아래를 흐리게 해 이어짐을 알린다. */
.panel .clamp { --lh: 1.5; line-height: var(--lh); max-height: calc(var(--lh) * 1em * 8 + 16px); overflow: hidden; position: relative; }
.panel .clamp.overflow::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2.2em; background: linear-gradient(to bottom, transparent, var(--surface)); pointer-events: none; }
.panel .clamp.open { max-height: none; }
.panel .clamp.open::after { display: none; }
.panel .clamp.md h1, .panel .clamp.md h2, .panel .clamp.md h3 { font-size: var(--fs-s); margin: var(--sp-2) 0 3px; color: var(--ink-2); }
.panel .clamp.md h1:first-child, .panel .clamp.md h2:first-child { margin-top: 0; }
.panel .clamp.md blockquote { margin: 0 0 var(--sp-2); padding-left: var(--sp-3); border-left: 2px solid var(--line-2); color: var(--muted); }
.panel .clamp.md ul { margin: 0 0 var(--sp-2); padding-left: var(--sp-5); }
.panel .clamp.md p { margin: 0 0 var(--sp-2); }
.panel .more { display: block; margin-top: var(--sp-1); }
.panel .recent button { background: transparent; border: 0; color: var(--ink-2); text-align: left; padding: 3px 0; width: 100%; font-size: var(--fs-s); }
.panel .recent button:hover { color: var(--accent); }
.mobile-only { display: none; }

/* ── 대화 ── */
.chat { display: flex; flex-direction: column; gap: 14px; max-width: 900px; margin: 0 auto; }
.tip { border: 1px dashed var(--line-2); background: var(--surface); border-radius: var(--radius); padding: 10px 14px; display: flex; gap: 10px; align-items: flex-start; font-size: 13px; }
.tip .t { font-weight: 600; margin-right: var(--sp-2); }
.tip .x { margin-left: auto; }
.msg { display: flex; flex-direction: column; gap: var(--sp-3); }
.q { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 10px 14px; white-space: pre-wrap; word-break: break-word; }
.q .meta { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: center; font-size: 11.5px; color: var(--muted); margin-top: var(--sp-2); }
/* §41 · 고쳐 보낸 원 질문. **지우지 않고 접는다** — 무엇을 왜 멈췄는지 기록이 남아야 한다.
   접는 것은 높이뿐이고 글자는 그대로 있다(찾기·복사가 죽지 않게). [펼치기]가 .folded 만 떼면 원래대로 돌아온다.
   ★ 왼쪽 굵은 선은 "이건 지나간 판" 이라는 표시다 — 색은 §37-1 토큰만 쓴다(새 색을 만들지 않는다). */
.q.edited { border-left: 3px solid var(--line-2); opacity: .82; }
.q.edited.folded .qtext { max-height: 3.4em; overflow: hidden; -webkit-mask-image: linear-gradient(180deg, #000 55%, transparent); mask-image: linear-gradient(180deg, #000 55%, transparent); }
.q .qmore { padding: 0 var(--sp-2); font-size: 11.5px; }
.q .editfrom { margin-bottom: var(--sp-2); }
.prog { border-left: 2px solid var(--line-2); margin-left: var(--sp-2); padding-left: 10px; }
.prog summary { cursor: pointer; font-size: var(--fs-s); color: var(--muted); list-style: none; display: flex; align-items: center; gap: var(--sp-2); }
.prog summary::-webkit-details-marker { display: none; }
.prog summary::before { content: "▸"; font-size: var(--fs-xs); }
.prog[open] summary::before { content: "▾"; }
.prog .lines { margin: var(--sp-2) 0 0; padding: 0; list-style: none; font-family: var(--mono); font-size: 12px; color: var(--ink-2); }
.prog .lines li { padding: 1px 0; white-space: pre-wrap; word-break: break-word; }
.prog .lines li .ts { color: var(--muted); margin-right: var(--sp-3); }
.spin { display: inline-block; width: 10px; height: 10px; border: 2px solid var(--line-2); border-top-color: var(--accent); border-radius: 50%; animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.a { padding: 2px var(--sp-1); }
.md h1, .md h2, .md h3, .md h4 { margin: 14px 0 var(--sp-2); line-height: 1.3; }
.md h1 { font-size: 18px; } .md h2 { font-size: var(--fs-l); } .md h3 { font-size: var(--fs-m); } .md h4 { font-size: 14px; }
.md p { margin: var(--sp-2) 0; } .md ul, .md ol { margin: var(--sp-2) 0; padding-left: 22px; } .md li { margin: 2px 0; }
/* 6b-8 · 본문 규칙(브리핑 본문·사업 답변 공통).
   왜: 인라인 코드(`01 신용보증재단 대출 (우리은행)` — 화면에서는 칩으로 보인다)가 줄바꿈되지 않아 좁은 폭에서
   한 줄이 화면 밖으로 나갔다. 인라인 코드·본문 칩은 줄바꿈을 허용하고, 원문 그대로 읽어야 하는
   코드 블록·표만 자기 상자 안에서 가로 스크롤한다. */
.md { min-width: 0; overflow-wrap: anywhere; }
.md pre { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-s); padding: 10px var(--sp-4); overflow-x: auto; max-width: 100%; }
.md code { background: var(--surface-2); padding: 1px 5px; border-radius: var(--r-xs); white-space: normal; overflow-wrap: anywhere; }
.md pre code { background: transparent; padding: 0; white-space: pre; overflow-wrap: normal; }
.md .chip { white-space: normal; }
.md blockquote { margin: var(--sp-2) 0; padding: 2px var(--sp-4); border-left: 3px solid var(--line-2); color: var(--ink-2); }
.md table { border-collapse: collapse; margin: var(--sp-3) 0; font-size: 13px; display: block; overflow-x: auto; max-width: 100%; }
.md th, .md td { border: 1px solid var(--line); padding: var(--sp-1) var(--sp-3); }
.md hr { border: 0; border-top: 1px solid var(--line); margin: var(--sp-4) 0; }

/* ── 형광펜(§37-2 · 37 회차) ──────────────────────────────────────────────
   한 규칙을 둘이 쓴다: ① 세션이 직접 고른 `==강조==`(md() 가 <mark> 로 바꾼다)
   ② 화면이 자동으로 칠하는 세 자리(app.js autoMark — 답 첫 문장 · 저장 경로 · 결정/확인 필요 항목 제목, class="hl").
   노란 형광을 안 쓰는 이유: 어두운 판에서 눈이 아프고 상태 4색(완료·대기·기획·오류)과 섞여 뜻이 흐려진다.
   글자색은 건드리지 않는다 — 대비는 (글자 vs 강조 배경) 한 쌍만 보면 된다(밝은 14.2:1 · 어두운 12.3:1).
   box-decoration-break:clone = 두 줄로 접힌 강조의 끝줄에도 둥근 모서리·좌우 여백이 그대로 붙는다. */
mark, .md mark { background: var(--accent-soft); color: inherit; border-radius: 3px; padding: 0 2px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone; }

/* ── 읽기 규칙(§37-3) ─────────────────────────────────────────────────────
   ★ 줄 길이는 --read-w 한 값에서만 나온다. 본문(문단·목록·인용)에만 걸고 표·코드 블록은 뺀다 —
     그 둘은 자기 상자 안에서 가로로 굴러야 한다(6b-8 규칙). max-width 라 좁은 폭에서는 아무 일도 안 한다.
   ★ 제목 text-wrap:balance = 두 줄로 접힐 때 아랫줄에 한 낱말만 남는 것을 막는다(미지원 브라우저는 무시). */
.md p, .md li, .md blockquote { max-width: var(--read-w); }
.md p, .md li { line-height: 1.7; }
.md h1, .md h2, .md h3, .md h4, .card h3, .modal h2, .topbar .title { text-wrap: balance; }
/* 숫자가 줄지어 나오는 자리(표·사용량·시각). 자릿수가 흔들리면 세로로 비교해 읽을 수 없다. */
.mono, .tbl, .md table, .kv dd, .stat .n, .lim-row, .uptime-lab, .fbar .when, .prog .lines { font-variant-numeric: tabular-nums; }

/* ── 카드 위계(§37-3) ─────────────────────────────────────────────────────
   규칙 셋: 답 카드는 '면'(테두리·그림자 없음) · 상태는 '칩 색' · 구분은 '선 하나'.
   그림자는 **떠 있는 것**(모달·메뉴·팝오버·토스트·버전 막대)에만, 그것도 --shadow 한 겹만.
   문서 흐름에 놓인 상자(.card·.brf-head·.outpins)는 테두리만 갖는다 — 셋을 다 주면 위계가 사라진다. */
.mem { border: 1px solid var(--line); border-left: 3px solid var(--accent); background: var(--surface); border-radius: var(--radius-s); padding: var(--sp-3) var(--sp-4); font-size: 13px; }
.mem .h { font-size: 11.5px; color: var(--muted); font-weight: 600; margin-bottom: var(--sp-1); }
.mem ul { margin: 0; padding-left: 18px; }
.att { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.att a { display: inline-flex; align-items: center; gap: var(--sp-2); border: 1px solid var(--line-2); border-radius: var(--radius-s); padding: var(--sp-1) 10px; font-size: var(--fs-s); text-decoration: none; color: var(--ink); background: var(--surface); }
.actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; font-size: 12px; color: var(--muted); }
/* §41 · [✏ 수정]은 넓은 화면에서는 제 단추로, 폰(좁은 폭)에서는 카드 ⋯ 메뉴 **첫 줄**로 간다(§41 ⑦).
   ★ 폭 판정을 CSS 에 맡긴 이유: 카드는 한 번만 그려지므로 JS 로 정하면 화면을 돌렸을 때 옛 판이 그대로 남는다.
   ★ 아래 600px 조건은 이 파일의 다른 두 줄 머리(.topbar) 조건과 같은 값이다 — 한쪽만 고치면 어긋난다. */
.actions .card-more { display: none; }
@media (max-width: 600px) {
  .actions .edit-wide { display: none; }
  .actions .card-more { display: inline-flex; }
}
.err { border: 1px solid var(--danger-soft); background: var(--danger-soft); border-radius: var(--radius-s); padding: 10px var(--sp-4); font-size: 13px; }
.err .h { font-weight: 700; color: var(--danger); margin-bottom: var(--sp-1); }
.err .dev { font-family: var(--mono); font-size: 12px; color: var(--ink-2); white-space: pre-wrap; word-break: break-all; margin-top: var(--sp-1); }
.empty { color: var(--muted); text-align: center; padding: 40px 10px; font-size: 13.5px; }
/* 대화 위 안내 줄(개인 모드 등). */
.note { border: 1px solid var(--line); background: var(--bg-2); border-radius: var(--radius-s); padding: var(--sp-3) 11px; font-size: var(--fs-s); color: var(--ink-2); margin-bottom: var(--sp-1); }
/* 새 대화 시트 — 개인 버튼 + 검색 + 목록. */
.newchat-sheet input[type=search] { width: 100%; margin: 10px 0 2px; }
.newchat-sheet .picklist { max-height: 46svh; overflow-y: auto; }
.pick { display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left; padding: 9px 10px; border: 0; background: transparent; border-radius: var(--radius-s); color: var(--ink-2); font-size: 13.5px; }
.pick:hover { background: var(--bg-2); color: var(--ink); }
.pick .num { font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted); width: 22px; flex: none; }
.pick .nm { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pick.personal { border: 1px solid var(--line); background: var(--bg-2); font-weight: 600; color: var(--ink); }
.pick.personal:hover { border-color: var(--accent); }
.pick.personal .num { color: var(--accent); }
.newchat-sheet .pick.personal + .pick.personal { margin-top: var(--sp-2); }

/* 사업 추가 마법사 — 한 번에 한 질문(1/10) → 요약 → 만들기. */
.wiz-bar { height: 4px; border-radius: 2px; background: var(--bg-2); overflow: hidden; margin-bottom: 10px; }
.wiz-bar i { display: block; height: 100%; background: var(--accent); transition: width .15s ease; }
.wiz-head .row { gap: var(--sp-3); }
.wiz-body { min-height: 128px; margin-top: var(--sp-4); }
.wiz-body label { display: block; font-size: var(--fs-s); color: var(--muted); margin: 10px 0 3px; }
.wiz-body input[type=text], .wiz-body textarea { width: 100%; }
.wiz-pick { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.wiz-rows .row { margin-bottom: var(--sp-2); }
.wiz-rows input[type=date] { flex: none; }
.wiz-err { color: var(--danger); font-size: var(--fs-s); min-height: 17px; margin-top: var(--sp-3); }
.wiz-sum { max-height: 44svh; overflow-y: auto; }
.wiz-sum-row { display: flex; gap: 10px; padding: var(--sp-2) 0; border-top: 1px solid var(--line); font-size: 13px; }
.wiz-sum-row:first-child { border-top: 0; }
.wiz-sum-row .k { flex: none; width: 78px; color: var(--muted); }
.wiz-sum-row .v { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.wiz-sum-row .v ul { padding-left: 0; }
.wiz-sum-row .v .list li { border-top: 0; padding: 2px 0; }

/* ── 입력칸 ── */
.composer { border-top: 1px solid var(--line); background: var(--bg); padding: 8px 12px calc(10px + env(safe-area-inset-bottom)); }
.composer .inner { max-width: 900px; margin: 0 auto; background: var(--surface); border: 1px solid var(--line-2); border-radius: 14px; padding: var(--sp-3) 10px; position: relative; }
.composer .inner:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.composer textarea { width: 100%; border: 0; background: transparent; resize: none; padding: var(--sp-1) var(--sp-1) 2px; min-height: 40px; max-height: calc(var(--app-h, 100svh) * .4); outline: none; box-shadow: none; }
.composer .bar { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-1); }
.composer .bar select { font-size: 12px; padding: 3px var(--sp-2); border-radius: 6px; background: var(--surface-2); border-color: transparent; }
.composer .send { margin-left: auto; }
/* 입력칸 사업 드롭다운(기획 §3) — 버튼 + 위로 열리는 목록(새 대화 시트와 같은 .picklist 부품). */
/* 왜 위로 여나: 입력칸이 화면 맨 아래라 아래로 열면 목록이 잘린다(.slash 와 같은 방식). */
.composer .bizwrap { position: relative; }
/* 왜 nowrap 인가(6b-2): 사업명이 길면 "사업:" 라벨과 ▾ 가 따로 줄바꿈되어 버튼이 두 줄이 되고 입력칸 높이가 흔들렸다.
   라벨·화살표는 접지 않고, 줄어드는 것은 사업명(.v) 하나뿐이다(말줄임). 좁은 화면에서는 .v 를 더 줄인다. */
.composer .bizsel { font-size: 12px; padding: 3px var(--sp-3); border-radius: 6px; background: var(--surface-2); border-color: transparent; max-width: 190px; white-space: nowrap; flex: 0 1 auto; min-width: 0; }
.composer .bizsel .v { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: bottom; font-weight: 600; }
@media (max-width: 520px) { .composer .bizsel { max-width: 150px; } .composer .bizsel .v { max-width: 92px; } }
.bizpop { position: absolute; left: 0; bottom: calc(100% + 6px); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); box-shadow: var(--shadow); width: 320px; max-width: 78vw; z-index: 6; padding: var(--sp-3); }
.bizpop input[type=search] { width: 100%; margin-bottom: var(--sp-1); }
.bizpop .picklist { max-height: 44svh; overflow-y: auto; }
.composer .files { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.composer .files .chip button { border: 0; background: transparent; padding: 0 0 0 var(--sp-1); color: inherit; }
/* 첨부 칩(기획 §6) — 붙여넣기·드래그·📎 가 모두 같은 칩으로 모인다. 이미지는 썸네일, 그 밖은 문서 아이콘. */
/* 폰(폴드8 웹뷰)에서도 ✕ 가 눌려야 하므로 최소 터치 크기 28px 을 지킨다. */
.composer .files .chip.att { padding: 3px 4px 3px 5px; gap: 6px; max-width: 240px; }
.composer .files .chip.att .th { width: 28px; height: 28px; object-fit: cover; border-radius: 5px; display: block; }
.composer .files .chip.att .ico { width: 28px; text-align: center; font-size: 15px; }
.composer .files .chip.att .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 130px; }
.composer .files .chip.att .x { min-width: 28px; min-height: 28px; padding: 0; border-radius: var(--r-pill); font-size: 13px; }
.composer .files .chip.att .x:hover { background: var(--surface); }
/* 파일을 끌어다 놓는 동안의 표시. dragover 를 막지 않으면 브라우저가 파일을 새 탭으로 연다(app.js 쪽에서 막는다). */
.composer .inner.dropping { border-color: var(--accent); border-style: dashed; background: var(--accent-soft); }

/* ── 35-C · 파일 서랍 + 입력창 위 띠(§8-21) ──────────────────────────────────
   왜 .composer 안인가: 입력창 바로 위가 재희님이 지목한 자리이고(사용량 경고 띠와 같은 층),
   구조적으로 하단 탭바·입력창 아래로 내려갈 수 없어 겹침 계산이 아예 필요 없다.
   본문 위로 뜨는 것은 펼친 목록(.fd-pop) 하나뿐이다 — 그것만 absolute 다.
   폭은 입력창(.inner)과 같은 900px 로 맞춘다(가운데 정렬이 어긋나면 손잡이가 화면 끝으로 달아난다). */
.fdock { max-width: 900px; margin: 0 auto 4px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.fbar { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; flex-wrap: wrap;
  background: var(--accent-soft); border: 1px solid var(--line-2); border-radius: 10px; padding: var(--sp-1) var(--sp-2) var(--sp-1) 9px; font-size: var(--fs-s); }
.fbar .ico { flex: 0 0 auto; }
.fbar .nm { flex: 1 1 120px; min-width: 0; border: 0; background: transparent; padding: 0; font: inherit; font-weight: 600; color: var(--accent); text-align: left; cursor: pointer; }
.fbar .nm:hover { text-decoration: underline; }
.fbar .when { flex: 0 0 auto; font-size: 11.5px; }
.fbar .btn { min-height: 26px; padding: 2px var(--sp-3); }
.fbar .btn.x { min-width: 28px; padding: 2px var(--sp-2); }
/* 서랍 — 손잡이는 오른쪽 아래(입력창 바로 위 오른쪽), 목록은 손잡이 위로 펼쳐진다. */
.fdrawer { position: relative; align-self: flex-end; max-width: 100%; }
.fd-handle { border-radius: var(--r-pill); background: var(--surface-2); border-color: transparent; font-size: 12px; padding: 3px 10px; }
.fd-handle.on { background: var(--accent-soft); color: var(--accent); }
.fd-pop { position: absolute; right: 0; bottom: calc(100% + 6px); z-index: var(--z-float); width: 320px; max-width: min(92vw, 360px);
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); box-shadow: var(--shadow); padding: var(--sp-2); }
.fd-pop[hidden] { display: none; }
.fd-head { display: flex; align-items: center; gap: var(--sp-1); font-size: 12px; font-weight: 600; color: var(--muted); padding: 2px var(--sp-1) var(--sp-2); }
.fd-head .btn { min-height: 26px; padding: 2px 7px; }
/* 목록만 굴린다. 행 ⋯ 메뉴는 이 상자 안에 있어 같이 굴러간다(허공에 뜬 메뉴가 생기지 않는다). */
.fd-listwrap { position: relative; max-height: min(46svh, 340px); overflow-y: auto; overflow-x: hidden; }
.fd-row { display: flex; align-items: center; gap: 7px; padding: 5px var(--sp-1); border-radius: var(--radius-s); cursor: pointer; min-width: 0; }
.fd-row:hover { background: var(--surface-2); }
.fd-row .ico { flex: 0 0 auto; font-size: 15px; }
.fd-row .nm { font-size: var(--fs-s); font-weight: 600; }
.fd-row .dots { flex: 0 0 auto; min-width: 28px; min-height: 28px; padding: 0; }
.fd-empty { padding: var(--sp-4) var(--sp-2); font-size: var(--fs-s); text-align: center; }
.fd-menu { position: absolute; right: 6px; z-index: 2; display: flex; flex-direction: column; align-items: stretch; gap: 2px;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); box-shadow: var(--shadow); padding: var(--sp-1); }
.fd-menu[hidden] { display: none; }
.fd-menu .btn { justify-content: flex-start; white-space: nowrap; font-size: 12px; min-height: 30px; }
/* 폰(§8-21) — 접은 앞화면·좁은 폭에서는 띠만 두고 서랍은 [파일] 패널이 갈음한다. 손잡이 한 줄만큼도 아깝다. */
@media (max-width: 420px) { .fdrawer { display: none; } .fbar { font-size: 12px; padding: 3px 5px 3px 7px; } }
/* 분할 화면·버블(compact=1)은 대화와 입력창만 그리는 자리다 — 여기에도 두지 않는다. */
.shell.compact .fdock { display: none; }
/* §33-1 · 본문 어디나 드롭. 창 전체에 테두리를 둘러 "여기 놓아도 된다" 를 알린다(입력창 위가 아니어도 된다).
   pointer-events:none 이 없으면 이 겹이 drop 이벤트를 가로채 파일이 어디에도 안 붙는다. */
html.dropping-page::after { content: '여기에 놓으면 던지기'; position: fixed; inset: 8px; z-index: 90; pointer-events: none;
  border: 2px dashed var(--accent); border-radius: 10px; background: var(--accent-soft);
  display: flex; align-items: flex-end; justify-content: center; padding-bottom: 18px; font-size: 13px; color: var(--accent); opacity: .85; }
/* §33-3 · 답 카드 맨 위 "저장:" 줄. 경로가 길어도 버튼이 밀려나지 않게 줄바꿈을 허용한다. */
.msg .saved { display: flex; flex-direction: column; gap: var(--sp-1); margin: 0 0 var(--sp-3); }
.msg .saved .row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
/* 53-Y: .chip 기본 white-space:nowrap 이 overflow-wrap 을 막아 긴 저장 경로가 카드 밖으로 삐져나왔다 → 줄바꿈 허용 */
.msg .saved .chip.ok { max-width: 100%; overflow-wrap: anywhere; white-space: normal; }
/* 35-B(§8-19) · "저장:" 칩 자체가 파일을 여는 단추다. 칩 모양은 그대로 두고 단추 티(테두리·기본 글꼴)만 지운다. */
.chip.as-link { border: 0; cursor: pointer; font: inherit; font-size: 11.5px; font-weight: 600; text-align: left; }
.chip.as-link:hover { text-decoration: underline; }
.slash { position: absolute; left: 10px; bottom: calc(100% + 6px); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); box-shadow: var(--shadow); min-width: 260px; max-width: 90%; z-index: var(--z-head); overflow: hidden; }
.slash button { display: flex; gap: 10px; width: 100%; text-align: left; padding: 7px var(--sp-4); border: 0; background: transparent; font-size: 13px; }
.slash button.on, .slash button:hover { background: var(--accent-soft); }
.slash .c { font-family: var(--mono); color: var(--accent); min-width: 70px; }
.slash .d { color: var(--muted); }

/* ── 브리핑·목록 화면 ── */
.two { display: grid; grid-template-columns: 220px 1fr; gap: 14px; }
.datelist button { display: block; width: 100%; text-align: left; border: 0; background: transparent; padding: 7px 10px; border-radius: var(--radius-s); color: var(--ink-2); font-size: 13px; }
.datelist button.on { background: var(--surface); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
/* 6b-8 · 미리보기는 2줄 말줄임. 옛 nowrap+ellipsis 는 부모가 좁아지지 않으면 잘리지 않고 그대로 밀어냈다.
   마크다운 기호 제거는 서버(routes/briefings.js briefingPreview)가 한다. */
.datelist .pv { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; white-space: normal; font-size: var(--fs-xs); line-height: 1.4; color: var(--muted); }
.datelist button { min-width: 0; }

/* 6b-9 · 브리핑 탭 재배치 — 머리줄 고정 → 날짜 칩 한 줄 → (접힘) 전체 목록 → 본문.
   왜: 2단(.two)에서는 좁은 폭에서 왼쪽 날짜 목록이 위로 접혀 첫 화면을 통째로 차지했다.
   머리줄은 sticky 라 본문을 내려도 날짜와 [지금 만들기]가 늘 보인다(파란 띠 버튼 제거). */
.brf-head { position: sticky; top: 0; z-index: 3; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  padding: 7px 10px; margin-bottom: var(--sp-3); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); }
.brf-head .ttl { min-width: 0; font-weight: 700; font-size: 14px; overflow-wrap: anywhere; }
.brf-head .act { flex: none; margin-left: auto; display: flex; gap: var(--sp-2); }
/* 칩 줄은 자기 상자 안에서만 좌우로 굴러간다(6b-8 규칙 — 화면 전체는 안 밀린다). */
.brf-chips { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); overflow-x: auto; padding-bottom: 2px; }
.brf-chips .dchip { flex: none; padding: 3px 10px; border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface); color: var(--ink-2); font-size: var(--fs-s); white-space: nowrap; cursor: pointer; }
.brf-chips .dchip.on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); font-weight: 600; }
.brf-list { margin-bottom: var(--sp-3); }

/* ── 파일 ── */
.tree { font-size: 13px; }
.tree .node { display: flex; align-items: center; gap: var(--sp-2); padding: 3px var(--sp-2); border-radius: 6px; cursor: pointer; user-select: none; }
.tree .node:hover { background: var(--surface-2); }
.tree .node.on { background: var(--accent-soft); }
.tree .node .ico { width: 16px; text-align: center; color: var(--muted); font-size: 12px; }
.tree .kids { margin-left: var(--sp-5); border-left: 1px dashed var(--line); padding-left: var(--sp-1); }
.files-grid { display: grid; grid-template-columns: 300px 1fr; gap: var(--sp-4); min-height: 60vh; }
.viewer { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); min-height: 60vh; overflow: hidden; display: flex; flex-direction: column; }
.viewer .vhead { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); display: flex; gap: var(--sp-3); align-items: center; font-size: 13px; }
.viewer iframe { flex: 1; border: 0; width: 100%; min-height: 55vh; background: #fff; }
.viewer textarea { flex: 1; border: 0; border-radius: 0; min-height: 55vh; font-family: var(--mono); font-size: 13px; }
.viewer .vbody { flex: 1; overflow: auto; padding: var(--sp-4) 14px; }
.viewer img { max-width: 100%; }

/* ── PDF 뷰어(8-19-1) — pdf.js 가 캔버스로 그린다. 폰 WebView 에는 내장 PDF 뷰어가 없다 ── */
.pdfv { display: flex; flex-direction: column; gap: var(--sp-3); }
.pdfv-bar { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
/* position:relative — 쪽 번호·[◀▶] 가 offsetTop 을 이 상자 기준으로 잰다(기준이 바뀌면 엉뚱한 쪽으로 뛴다). */
/* touch-action — 브라우저 기본 핀치를 끄고 뷰어가 직접 배율을 잡는다(끄지 않으면 화면 전체가 확대된다). */
.pdfv-pages { position: relative; overflow: auto; max-height: 70vh; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--sp-3); -webkit-overflow-scrolling: touch; touch-action: pan-x pan-y; }
/* min-width:fit-content — 확대해 캔버스가 상자보다 넓어져도 가운데 정렬이 왼쪽을 잘라먹지 않는다. */
.pdfv-page { display: flex; justify-content: center; min-width: fit-content; margin-bottom: 10px; }
.pdfv-page:last-child { margin-bottom: 0; }
/* 배경은 흰색 고정 — 어두운 판에서도 종이는 종이다(투명 PDF 가 검게 뭉개지는 것을 막는다). max-width 는 두지 않는다(확대가 막힌다). */
.pdfv-cv { background: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .18); display: block; }

/* ── 파일 탐색기(§8-1·§8-2·§8-3, 6b-1) ── */
.fx .fx-list { margin-top: 10px; }
.fx .list-flat { padding: var(--sp-2); }
.fx-row { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-3); border-radius: var(--radius-s); cursor: pointer; }
.fx-row + .fx-row { border-top: 1px solid var(--line); }
.fx-row:hover { background: var(--surface-2); }
.fx-row.on { background: var(--accent-soft); }
.fx-row .ico { width: 22px; text-align: center; font-size: 15px; flex: none; }
.fx-row .nm { font-size: 13.5px; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 오늘 클로드가 만진 줄(§8-4 [클로드 작업]·"방금 만든 파일" 묶음). 색을 더 쓰면 대표에게 보임·잠김 칩과 다툰다. */
.fx-row.today .nm { font-weight: 700; }
/* 파일 종류 3분류 배지(§8-4). 산출물=파랑 · 원본=회색 · 작업용=옅은 노랑 */
.chip.k-out { background: var(--accent-soft); color: var(--accent); }
.chip.k-src { background: var(--surface-2); color: var(--ink-2); }
.chip.k-work { background: var(--warn-soft); color: var(--warn); }
.fx-row .fx-acts { flex: none; opacity: 0.55; }
.fx-row:hover .fx-acts, .fx-row.on .fx-acts { opacity: 1; }
.fx-row .fx-acts .btn { padding: 2px var(--sp-2); }
/* 8-13b · 다중 선택. 체크는 평소 흐리게 두고 마우스를 올리거나 고른 줄에서만 또렷해진다(줄마다 체크가 보이면 목록이 시끄럽다). */
.fx-row .fx-ck { flex: none; width: 16px; height: 16px; margin: 0; opacity: .35; cursor: pointer; }
.fx-row:hover .fx-ck, .fx-row.picked .fx-ck { opacity: 1; }
.fx-row.picked { background: var(--accent-soft); }
/* 선택 바는 버튼이 다섯이라 좁은 폭에서 반드시 접혀야 한다(6b-8: 가로로 굴러가지 않게). */
.fx .selbar { flex-wrap: wrap; border-radius: 14px; }
/* 8-13 · 파일 탭 루트의 사업 카드. 좁은 폭에서는 자동으로 1단이 된다(6b-8: 가로로 굴러가지 않게 minmax 하한을 0 으로). */
.fx-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); gap: var(--sp-3); }
.fx-cards .fx-biz { cursor: pointer; min-width: 0; margin: 0; }
.fx-cards .fx-biz:hover { background: var(--surface-2); }
/* 답변·기억 줄의 파일 경로 링크 */
a.fpath { text-decoration: underline dotted; word-break: break-all; }
/* 답변 속 파일 링크 옆 [핀](§8 · 6b-2). 글자 흐름을 밀지 않도록 아주 작게 두고, 카드를 인쇄·복사할 땐 빠진다. */
.pinbtn { border: 0; background: transparent; padding: 0 2px; font-size: var(--fs-xs); line-height: 1; cursor: pointer; opacity: .45; }
.pinbtn:hover, .pinbtn.on { opacity: 1; }
/* 사업 탭 머리 아래 고정 산출물 목록. 대화가 쌓여도 위에 붙어 있어야 "아까 그 문서" 에 바로 닿는다. */
.outpins { position: sticky; top: 0; z-index: 3; display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center;
  padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-3); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius-s); }
.outpins .chip.pin { max-width: 260px; gap: var(--sp-1); }
.outpins .chip.pin a { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 210px; display: inline-block; vertical-align: bottom; }
.outpins .chip.pin button.x { border: 0; background: transparent; padding: 0 0 0 2px; color: inherit; cursor: pointer; }
/* 입력칸의 PC 파일 첨부 칩(경로 참조) */
.composer .pins:empty { display: none; }

/* ── 기기 ── */
.dev-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--sp-4); align-items: stretch; }
/* 왜 flex column + margin-top:auto(6b-2): 그램·집PC 는 담는 항목 수가 다르다(집PC 는 사무실 Z: 백업이 없고,
   구 실행기는 세션 슬롯 줄이 없다). 그대로 두면 카드 높이와 가동 막대의 바닥 선이 어긋나 두 PC 를 나란히 못 읽는다.
   격자는 카드를 같은 높이로 늘이고, 가동 막대는 남는 자리를 밀어 카드 맨 아래에 붙는다. */
.dev-cards > .card { display: flex; flex-direction: column; }
/* ★ 6b-6 높이 불일치의 실제 원인: 전역 `.card + .card { margin-top: 10px }` 가 격자 안에서도 먹는다.
   격자는 stretch 로 카드를 행 높이에 맞추지만 그 높이에서 margin 을 빼므로, 첫 장을 뺀 모든 카드가
   10px 아래로 밀리고 10px 짧아진다(재희님 01:03 스크린샷의 확장 카드·앱 카드 4장). 격자 안에서는 여백을 gap 이 맡는다. */
.dev-cards > .card + .card { margin-top: 0; }
/* 카드마다 담는 항목 수가 다르다(집PC 는 Z: 백업 없음, 확장은 가동 막대 없음, 앱 카드는 안내문 길이가 제각각).
   맨 아래에 붙일 요소에 .card-foot(또는 기기 카드의 .uptime)을 주면 남는 자리를 밀어 바닥선이 맞는다. */
.dev-cards > .card .uptime, .dev-cards > .card .card-foot { margin-top: auto; }
/* 카드 안이 한 겹 더 감싸인 경우(APK 카드는 비동기로 통째 교체된다) 그 겹도 세로로 늘여야 바닥 정렬이 통한다. */
.dev-cards > .card > .col-fill { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* 확장 카드의 '최근 전송 이력 6칸'(6b-6). 가동 막대 자리를 대신해 카드 구조를 다른 카드와 같게 만든다. */
.ext-hist { display: flex; gap: var(--sp-1); margin-top: var(--sp-3); }
.ext-hist .h { flex: 1; min-width: 0; text-align: center; border-radius: var(--r-xs); background: var(--accent-soft); padding: var(--sp-1) 0 3px; }
.ext-hist .h.empty { background: transparent; border: 1px dashed var(--line-2); }
.ext-hist .h.bad { background: var(--danger-soft); }
.ext-hist .h i { display: block; width: 6px; height: 6px; border-radius: 50%; margin: 0 auto 3px; background: var(--accent); }
.ext-hist .h.novalue i { background: var(--muted); }
.ext-hist .h.bad i { background: var(--danger); }
.ext-hist .h.empty i { background: transparent; border: 1px solid var(--line-2); }
.ext-hist .h span { font-size: 9.5px; color: var(--muted); }
/* 긴 안내문(앱 카드 설치 방법 등)은 3줄까지만 — 카드 하나가 길어지면 같은 줄 전체가 그 높이로 늘어난다. */
.clamp3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.more-btn { border: 0; background: transparent; color: var(--accent); padding: 2px 0 0; font-size: 12px; cursor: pointer; }
.dev-state { font-weight: 700; display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
/* Z: 백업 경고는 실행기가 올리는 한 줄 문장이라 칩 안에 넣으면 카드 폭을 밀어냈다. 칩은 낱말만, 문장은 아래 두 줄까지. */
.zbk { display: flex; flex-direction: column; gap: 2px; align-items: flex-start; min-width: 0; }
.zbk .chip { white-space: nowrap; }
.clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.uptime { display: flex; gap: var(--sp-1); align-items: flex-end; height: 42px; margin-top: var(--sp-2); }
.uptime .bar { flex: 1; background: var(--accent-soft); border-radius: 3px 3px 0 0; position: relative; min-height: 2px; }
.uptime .bar i { position: absolute; left: 0; right: 0; bottom: 0; background: var(--accent); border-radius: 3px 3px 0 0; }
.uptime-lab { display: flex; gap: var(--sp-1); font-size: 10px; color: var(--muted); }
.uptime-lab span { flex: 1; text-align: center; }

/* ── 사용 한도(§1 · 1-A) ──
   막대는 기기 탭 카드, .limit-hint 는 입력창 상태줄. 색만으로 알리지 않고 % 숫자를 항상 함께 둔다. */
.lim { display: grid; gap: var(--sp-3); }
.lim-row { display: grid; grid-template-columns: 76px 1fr 46px auto; align-items: center; gap: var(--sp-3); }
.lim-lab { font-size: 12px; color: var(--muted); }
.lim-bar { height: 8px; border-radius: var(--r-xs); background: var(--accent-soft); overflow: hidden; }
.lim-bar i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-xs); }
.lim-bar.warn i { background: var(--warn); }
.lim-bar.danger i { background: var(--danger); }
.lim-pct { font-size: 12px; font-variant-numeric: tabular-nums; text-align: right; }
.lim-pct.warn { color: var(--warn); font-weight: 600; }
.lim-pct.danger { color: var(--danger); font-weight: 600; }
.lim-res { white-space: nowrap; }
.limit-hint { white-space: nowrap; color: var(--muted); }
/* 22-1 · 70% 이상은 색만, 90%(보류임계) 이상은 색+굵기, 100% 는 빨강. 색만으로 알리지 않으려고 % 숫자는 늘 함께 둔다. */
.limit-hint.caution { color: var(--warn); }
.limit-hint.warn { color: var(--warn); font-weight: 600; }
.limit-hint.danger { color: var(--danger); font-weight: 600; }
/* 23-4 · 큐 계획 카드에서도 같은 '주의' 색을 쓴다(.limit-hint 안이 아니라 일반 줄이라 규칙이 따로 필요하다). */
.caution { color: var(--warn); }
/* 22-1 · 입력창 상태줄 슬롯. 폴링이 이 안만 다시 그린다 — display:contents 라 도구 줄 배치는 슬롯이 없던 때와 같다. */
.cstat { display: contents; }
@media (max-width: 480px) { .lim-row { grid-template-columns: 64px 1fr 42px; } .lim-res { grid-column: 2 / -1; } }

/* ── 사용 패턴 히트맵(§20-4 · 20-A) ──
   24칸 격자 2장 + 창 띠. 진하기(opacity)로만 값을 그리고 정확한 값은 마우스 올림(title)에 둔다 —
   색 단계로 숫자를 읽게 만들면 색약·다크 테마에서 틀리게 읽힌다. 폰(≤480)에서도 24칸이 한 줄에 들어가야
   요일 비교가 가능하므로 칸 높이만 줄이고 칸 수는 줄이지 않는다. */
.heat, .wband { display: grid; grid-template-columns: 20px repeat(24, 1fr); gap: 2px; align-items: center; margin-top: var(--sp-1); }
.heat b, .wband b { font: 10px/12px var(--mono); color: var(--muted); font-weight: 400; text-align: right; padding-right: 2px; }
.heat i { display: block; height: 12px; border-radius: 2px; background: var(--accent); }
.heat.burn i { background: var(--warn); }
.wband { margin-top: var(--sp-2); }
.wband i { display: block; height: 8px; border-radius: 2px; background: var(--line); }
.wband i.wsleep { background: var(--accent-soft); }
.wband i.wfocus { background: var(--accent); }
.wband i.wfree { background: var(--ok); }
.chip.wsleep { background: var(--accent-soft); color: var(--ink); }
.chip.wfocus { background: var(--accent); color: var(--accent-ink); }
.chip.wfree { background: var(--ok-soft); color: var(--ink); }
@media (max-width: 480px) { .heat i { height: 9px; } .heat, .wband { gap: 1px; } }

/* ── 설정 ── */
.set-row { display: flex; align-items: center; gap: 10px; padding: var(--sp-3) 0; border-top: 1px solid var(--line); }
.set-row:first-of-type { border-top: 0; }
.set-row .lab { flex: 1; }
.set-row .lab small { display: block; color: var(--muted); font-size: 11.5px; }
.set-row.sub { padding-left: 18px; }

/* ── 상태 ── */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--sp-3); }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--sp-3) var(--sp-4); }
.stat .n { font-size: var(--fs-xl); font-weight: 700; } .stat .l { font-size: 11.5px; color: var(--muted); }
.logx { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--sp-3) 10px; font-family: var(--mono); font-size: 11.5px; white-space: pre-wrap; word-break: break-all; max-height: 50vh; overflow: auto; }

/* ── 모달·토스트 ── */
.modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: var(--z-modal); display: flex; align-items: center; justify-content: center; padding: var(--sp-5); }
.modal { background: var(--surface); border: 1px solid var(--line); border-radius: 16px; width: 100%; max-width: 560px; max-height: 90svh; overflow: auto; box-shadow: var(--shadow); padding: 18px 20px; }
.modal h2 { margin: 0 0 var(--sp-4); font-size: var(--fs-l); }
.modal .foot { display: flex; gap: var(--sp-3); justify-content: flex-end; margin-top: var(--sp-5); }
.modal label { display: block; font-size: var(--fs-s); color: var(--muted); margin: 10px 0 3px; }
.modal input[type=text], .modal select, .modal textarea { width: 100%; }
.toast { position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom)); transform: translateX(-50%); background: var(--ink); color: var(--bg); padding: 9px var(--sp-5); border-radius: 10px; font-size: 13px; z-index: var(--z-toast); box-shadow: var(--shadow); max-width: 90vw; pointer-events: none; }
/* §37-1 · 오류 토스트 글자를 흰색 고정에서 바탕 토큰으로 바꿨다. 어두운 판의 오류색(#e08585)은 밝은 면이라
   흰 글자가 2.3:1 밖에 안 나왔다(밝은 판은 반대). 바탕색을 쓰면 두 판 모두 6:1 위로 올라간다. */
.toast.err { background: var(--danger); color: var(--bg); }

/* ── 새 버전 배너(기획 §3-1) ── */
/* 왜 위쪽 고정인가: 배포 뒤에도 열어 둔 화면이 옛 코드를 쓰고 있는 것을 사람이 알아채는 유일한 자리다. */
.verbar { position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-bar); display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: calc(7px + var(--sai-top, env(safe-area-inset-top))) 12px 7px; background: var(--accent); color: var(--accent-ink); font-size: 13px; font-weight: 600; box-shadow: var(--shadow); }
.verbar .btn { min-height: 28px; padding: 3px 10px; }
/* §37-1 · 흰색 고정을 강조면 글자 토큰으로. 어두운 판의 강조색(#7aa2f7)은 밝아서 흰 글자가 안 읽힌다. */
.verbar .btn.ghost { color: inherit; border-color: currentColor; background: transparent; }
.verbar .btn.x { min-width: 28px; padding: 3px var(--sp-3); }

/* ── 개발 판 띠(§42-B, 2026-09-13) ──────────────────────────────────────────
   왜 위쪽 고정 + 아래 내용을 그만큼 내리는가: 새 버전 배너(.verbar)는 fixed 로 머리를 **덮는다**(6b-5 미완 ③).
     그건 잠깐 뜨는 배너라 참았지만 이 띠는 **늘 떠 있다** — 덮으면 머리줄을 영구히 못 쓴다.
     그래서 띠 높이(--devbar-h)만큼 .app 을 내리고 .shell 의 100svh 에서 같은 값을 뺀다(문서 스크롤이 생기지 않게).
   ★ 높이는 --devbar-h **한 값**이다. 띠 두께를 바꾸려면 아래 첫 줄만 고친다.
   ★ html.devmode .app 에서 --sai-top 을 0 으로 덮는 이유: 위쪽 안전영역 인셋을 **띠가 대신 먹는다**.
     안 덮으면 머리줄이 인셋을 한 번 더 밀어 폰에서 빈 띠가 두 겹으로 생긴다.
     ★ 이 블록은 `var(--sai-top, env(safe-area-inset-top))` 꼴을 **새로 쓰지 않는다** — 8-18-1 단위 시험이
       그 낱말이 style.css 에 몇 번 나오는지(여섯) 세어 회귀를 막기 때문이다. 띠 자신은 env() 를 직접 읽는다
       (dev 전용 장식이라 25% 상한 밖에 둔 것이고, 부풀어도 띠와 본문이 같은 값으로 함께 움직인다).
   ★ 운영(claude.aisend.co.kr)에서는 이 블록의 어느 줄도 걸리지 않는다 — html.devmode 가 붙지 않는다. */
html.devmode { --devbar-h: 24px; }
.devbar { position: fixed; top: 0; left: 0; right: 0; z-index: 71; overflow: hidden;
  display: flex; align-items: center; justify-content: center; gap: 10px; white-space: nowrap;
  height: calc(var(--devbar-h) + env(safe-area-inset-top)); padding: env(safe-area-inset-top) 12px 0;
  background: var(--danger); color: var(--bg); font-size: var(--fs-s); font-weight: 600; }
.devbar .b { letter-spacing: .04em; border: 1px solid currentColor; border-radius: 5px; padding: 0 5px; }
.devbar .go { color: inherit; text-decoration: underline; }
html.devmode .app { --sai-top: 0px; padding-top: calc(var(--devbar-h) + env(safe-area-inset-top)); }
html.devmode .shell { grid-template-rows: calc(var(--app-h, 100svh) - var(--devbar-h) - env(safe-area-inset-top));
  height: calc(var(--app-h, 100svh) - var(--devbar-h) - env(safe-area-inset-top)); }
html.devmode .login-wrap { min-height: calc(var(--app-h, 100svh) - var(--devbar-h) - env(safe-area-inset-top)); }
/* 새 버전 배너는 띠 아래로 내린다 — 둘이 같은 top:0 에서 겹치면 뒤엣것이 통째로 가려진다. */
html.devmode .verbar { top: calc(var(--devbar-h) + env(safe-area-inset-top)); }
/* 좁은 폭(폴드8 커버·세로)에서는 긴 문구를 접고 `개발 판` 딱지와 링크만 남긴다(띠가 두 줄로 늘지 않게). */
@media (max-width: 600px) { .devbar span:not(.b) { display: none; } }

/* ── 하단 탭·서랍(모바일) ── */
.tabbar { display: none; }
.drawer-bg { display: none; }

/* U12 · 이 조건은 app.js MQS.desktop '(min-width: 1100px)' 의 짝이다 — 숫자를 바꾸면 그쪽도 같이(1100 이상 = 패널 상설). */
@media (max-width: 1099px) {
  .shell, .shell.with-panel { grid-template-columns: 250px 1fr; }
  .panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(360px, 92vw); z-index: var(--z-panel); transform: translateX(100%); transition: transform .2s; box-shadow: var(--shadow); }
  .panel.open { transform: none; }
  .mobile-only { display: inline-flex; }
}
@media (max-width: 759px) {
  .shell, .shell.with-panel { grid-template-columns: 1fr; grid-template-rows: 1fr auto; }
  .sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: min(300px, 85vw); z-index: var(--z-drawer); transform: translateX(-100%); transition: transform .2s; box-shadow: var(--shadow); }
  .sidebar.open { transform: none; }
  .drawer-bg { display: block; position: fixed; inset: 0; background: var(--scrim); z-index: 44; }
  .drawer-bg[hidden] { display: none; }
  .content { padding: 10px var(--sp-4) 20px; }
  .tabbar { display: flex; border-top: 1px solid var(--line); background: var(--bg-2); padding: 4px 4px calc(4px + env(safe-area-inset-bottom)); }
  .tabbar button { flex: 1; border: 0; background: transparent; padding: var(--sp-2) 2px; font-size: var(--fs-xs); color: var(--muted); display: flex; flex-direction: column; align-items: center; gap: 2px; border-radius: var(--radius-s); }
  .tabbar button .ico { font-size: var(--fs-l); }
  .tabbar button.on { color: var(--accent); background: var(--surface); }
  .two { grid-template-columns: 1fr; }
  .files-grid { grid-template-columns: 1fr; }
  .composer .bar select { max-width: 96px; }
}

/* 왜: 그리드 안 카드가 긴 해시·URL 때문에 min-content 폭으로 커져 화면 밖으로 잘리던 문제(2026-09-06 기기 탭). 카드는 줄어들 수 있게, 긴 문자열은 아무 데서나 줄바꿈. */
.card, .dev-cards > *, .kv dd { min-width: 0; }
.mono, .kv dd, .card .small { overflow-wrap: anywhere; word-break: break-word; }
.dev-cards { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }

/* 왜: 좁은 폭(폴드 접힘)에서 하단 탭 글자가 두 줄로 넘치던 문제. 한 줄 고정·말줄임, 버튼 최소폭 0. */
@media (max-width: 900px) { .tabbar button { min-width: 0; white-space: nowrap; overflow: hidden; } .tabbar button span:last-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; } }

/* ── 좁은 폭(폴드8 앞면 커버 세로 ≈ 360px) — 6b-8 ──────────────────────────
   6b-5 의 '낮은 화면' 조건(세로 600px 이하·가로 모드)과는 별개다. 커버 세로는 높이가 아니라 폭이 문제였다.
   ① 넘칠 수 있는 상자를 전부 min-width:0 으로 눌러 그리드·플렉스 자식이 내용 폭으로 부풀지 않게 하고
   ② 여백·글자를 한 단계 줄여 같은 카드가 한 화면에 들어오게 한다. 표·코드 블록만 자기 상자 안에서 가로 스크롤.
   조건을 420px 로 잡은 이유: 커버(≈360)와 접힌 상태의 일반 폰(≈390～412)까지 함께 걸린다. */
@media (max-width: 420px) {
  body { font-size: 13.5px; }
  .content { padding: var(--sp-3) 10px 18px; }
  .card { padding: 10px 11px; }
  .card h3 { font-size: 13.5px; }
  /* 넘침의 뿌리 — 플렉스·그리드 자식은 기본 min-width:auto 라 내용이 길면 부모를 밀어낸다. */
  .card, .row, .row > *, .list li, .kv dd, .set-row, .set-row .lab, .tri-row, .dev-cards > *, .stat, .datelist, .two > * { min-width: 0; }
  .row { flex-wrap: wrap; }
  .list li, .set-row .lab, .tri-row, .bizrow { overflow-wrap: anywhere; word-break: break-word; }
  /* 검수 큐 카드 미리보기는 em 고정폭 대신 카드 폭을 따른다. */
  .tri-prev { max-width: 100%; }
  .chip { padding: 1px var(--sp-2); font-size: var(--fs-xs); }
  .tbl th, .tbl td { padding: 5px 5px; }
  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: var(--sp-2); }
  .md h1 { font-size: 16.5px; } .md h2 { font-size: 15px; } .md h3 { font-size: 14px; }
  .md ul, .md ol { padding-left: 18px; }
  .two { gap: 10px; }
  .modal-bg { padding: var(--sp-3); }
  .modal { padding: 13px 14px; border-radius: var(--radius); }
  /* 6b-9 · 좁은 폭(폴드 앞면·접힌 폰)에서는 머리줄이 두 줄이 된다. 버튼 줄은 아래 왼쪽에 붙인다. */
  .brf-head { padding: var(--sp-2) var(--sp-3); }
  .brf-head .act { margin-left: 0; }
  .brf-head .ttl { font-size: 13.5px; }
}

/* 왜: compact=1(앱 분할 화면·팝업·버블) — 대화와 입력창만. */
.shell.compact { grid-template-columns: 1fr; }
.shell.compact .sidebar:not(.open), .shell.compact .tabbar, .shell.compact .panel, .shell.compact .topbar .sub, .shell.compact .drawer-bg[hidden] { display: none !important; }
/* 53-O · 버블에서 ☰ 가 안 눌리던 것 — 종전엔 서랍(.sidebar.open)·덮개까지 늘 숨겨 ☰ 를 눌러도 아무것도 안 떴다.
   이제 서랍을 열었을 때만 폭과 상관없이 왼쪽 겹침 서랍으로 띄운다(평소에는 종전처럼 대화만). */
.shell.compact .sidebar.open { display: flex; position: fixed; top: 0; left: 0; bottom: 0; width: min(300px, 85vw); z-index: 45; transform: none; box-shadow: var(--shadow); }
.shell.compact .drawer-bg:not([hidden]) { display: block; position: fixed; inset: 0; background: var(--scrim); z-index: 44; }
.shell.compact .topbar { padding: var(--sp-1) var(--sp-3); min-height: 0; flex-wrap: nowrap; }
/* 6b-11 · 분할 화면·버블은 세로가 더 귀하다. 두 줄로 늘리지 않고 버튼 줄만 가로로 굴린다. */
.shell.compact .topbar .acts { flex: 0 1 auto; width: auto; overflow-x: auto; }
.shell.compact .content { padding: var(--sp-2) var(--sp-3); }
.shell.compact .composer .inner { padding: var(--sp-2) var(--sp-3); }
/* G1 · 자기개발 앱 「익힘」(?app=growth) — 06-shell.js render 가 compact 셸 + 루트 클래스 app-growth 를 붙인다.
   compact 가 목록·탭바·패널을 숨기고, 여기서 남은 문 셋(서랍 · 머리 ☰ · 어둠막)을 닫는다 = 익힘 안에서 사업·받은함으로 못 나간다. */
html.app-growth .sidebar,
html.app-growth .topbar .side-tgl,
html.app-growth .drawer-bg { display: none !important; }
/* M1-a · 메시지 앱 「한통」(?app=link) — 같은 06-shell.js render 가 compact 셸 + 루트 클래스 app-link 를 붙인다.
   익힘과 같은 문 셋을 닫는다 = 한통 안에서는 휴대폰 구역(받은함·대화·주소록)만. */
html.app-link .sidebar,
html.app-link .topbar .side-tgl,
html.app-link .drawer-bg { display: none !important; }

/* ── 낮은 화면(폴드8 커버 가로 등) — 6b-5 ────────────────────────────────
   왜: 커버 화면 가로에서 머리 3줄(제목·칩·설명) + 입력창 묶음(입력 2줄·도구줄·상태문구·보내기) + 큰 하단 탭바가
       세로를 거의 다 먹어 대화 본문이 2줄만 남았다(재희님 2026-09-08 23:07 스크린샷).
   조건 둘: 세로 600px 이하 · 가로 모드이면서 폭 1100px 이하. 데스크톱 창은 세로가 더 높아 걸리지 않고,
       세로로 돌리면(폴드 펼침·폰 세로) 두 조건 모두 벗어나 원래 레이아웃으로 돌아온다.
   ★ app.js 의 LOWH_MQ 가 이 조건 문자열을 그대로 복사해 쓴다 — 한쪽만 고치면 머리 ⋯ 와 CSS 가 어긋난다. */
.lowh-only { display: none; }
.composer .tools { display: contents; }   /* 넓은 화면: 묶음이 없는 것처럼 도구가 .bar 에 그대로 늘어선다 */

@media (max-height: 600px), (orientation: landscape) and (max-width: 1100px) {
  /* 1. 머리 한 줄 — 칩·설명 줄은 app.js 가 아예 그리지 않고(⋯ 안으로 옮김) 높이만 여기서 조인다.
     ★ 6b-12 · 상한에 반드시 안전영역 인셋을 더한다(재희님 2026-09-09 17:17 가로 캡처의 원인).
       box-sizing:border-box 라 max-height 안에 padding-top(3px + 위쪽 인셋)이 들어간다.
       ★ 8-18-1 · 인셋은 --sai-top(실측 + 25% 상한)으로 읽는다. 파일 위 '안전영역 위쪽 인셋' 절 참조.
       가로·edge-to-edge(viewport-fit=cover)에서 상태바 인셋이 40~50px 이므로 옛 44px 상한은 안쪽 높이를 0 으로 만들었고,
       제목 줄이 상자 밖(본문 위)으로 흘러 스크롤한 카드에 위 절반이 덮였다.
       이제 안쪽 높이는 어느 인셋에서도 --head-h(44px) 로 지켜진다.
     ★ flex-wrap:nowrap — 위 TWOROW(≤600px) 블록과 동시에 걸리는 가로 폰에서 ①이 이기게 명시적으로 되돌린다. */
  .topbar { min-height: 0; max-height: calc(var(--head-h) + var(--sai-top, env(safe-area-inset-top))); gap: var(--sp-2);
    padding: calc(3px + var(--sai-top, env(safe-area-inset-top))) 10px 3px; flex-wrap: nowrap; }
  .topbar > .grow { min-width: 0; }
  /* 새 버전 배너는 fixed 라 낮은 화면에서는 머리를 통째로 가린다 — 높이만이라도 줄인다(자리 확보는 U13 절 · 낮은 높이일수록 머리가 덜 내려간다). */
  .verbar { padding: calc(3px + var(--sai-top, env(safe-area-inset-top))) 10px 3px; font-size: 12px; }
  .topbar .title { font-size: 14px; }
  .topbar .sub { display: none; }
  /* 2. 입력칸 한 줄(포커스하면 두 줄). 도구줄은 ⚙ 로 접고, 상태문구는 점 하나로 줄인다. */
  .content { padding: var(--sp-3) 10px 10px; }
  /* 본문이 남은 높이를 다 갖되 바닥 아래로는 안 내려간다.
     54-V(2026-09-29) · 바닥이 55% 였을 때는 머리(44+인셋)까지 빼고 나면 입력창 몫이 137px 밖에 안 남아,
     한도 띠 한 줄과 첨부 칩 두 줄만 붙어도 [보내기] 줄이 셸 밖으로 흘러 잘렸다. 40% 로 낮춘다 —
     이 값은 **바닥**이지 몫이 아니다(평소에는 본문이 남은 자리를 다 먹으므로 훨씬 크다).
     입력창이 무한히 자라지 않는 것은 54-V 절의 칩 줄 상한이 따로 지킨다. */
  .main > .content { min-height: 40svh; }
  .composer { padding: 4px 8px calc(4px + env(safe-area-inset-bottom)); }
  .composer .inner { padding: var(--sp-1) var(--sp-2); border-radius: 10px; }
  .composer textarea { min-height: 24px; max-height: 26px; padding: 2px var(--sp-1); }
  .composer .inner:focus-within textarea { max-height: 54px; }
  .composer .bar { margin-top: 0; flex-wrap: wrap; }
  .composer .lowh-only { display: inline-flex; }
  .composer .tools { display: none; }
  .composer .inner.tools-open .tools { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); flex-basis: 100%; margin-top: var(--sp-1); }
  .composer .send { margin-left: auto; }
  /* 54-V · 첨부 칩은 한 줄만 두고 나머지는 굴린다 — 이 블록은 본문에 최소 높이(988줄)를 주므로
     칩 줄이 자라면 입력창 아래쪽([보내기] 줄)이 셸 밖으로 흘러 잘렸다. 상한 규칙 본체는 54-V 절. */
  .composer .files, .composer .pins { max-height: 56px; }
  /* 35-C · 낮은 화면(폴드 커버 가로·폰 가로)은 본문이 두세 줄뿐이라 서랍이 펼쳐질 자리가 없다.
     띠 한 줄만 남기고 손잡이는 숨긴다 — [외 N개]를 누르면 app.js fdNarrow() 가 [파일] 패널로 갈음한다.
     ★ 조건 문자열은 이 블록(= MQS.low)과 max-width:420px(= MQS.narrow) 두 개뿐이고 app.js 가 같은 둘을 본다. */
  .fdrawer { display: none; }
  .fdock { margin-bottom: 3px; }
  .fbar { font-size: 12px; padding: 2px 5px 2px 7px; }
  .fbar .btn { min-height: 24px; }
  /* 3. 하단 탭바 — 아이콘만, 본문을 내리면 감춘다(.hid 는 app.js bindTabbarAutoHide 가 붙인다). */
  /* 6b-12 · 머리줄과 같은 병(상한에 안전영역이 안 들어가 아이콘 아래가 잘린다). 여기도 인셋을 더해 준다. */
  .tabbar { max-height: calc(40px + env(safe-area-inset-bottom)); padding: 2px 4px calc(2px + env(safe-area-inset-bottom)); overflow: hidden; transition: max-height .15s ease, padding .15s ease; }
  .tabbar button { font-size: 0; gap: 0; padding: var(--sp-1) 2px; }
  .tabbar button .ico { font-size: 17px; }
  .tabbar.hid { max-height: 0; padding-top: 0; padding-bottom: 0; border-top-width: 0; }
}

/* 운영 절 제목(상태 탭 안). 왜: 헬스·오류·알림과 운영(P11) 카드를 시각적으로 갈라 놓는다. */
.sec-title { margin: 20px 0 var(--sp-3); padding-top: 14px; border-top: 1px solid var(--line); font-size: 15px; color: var(--ink-2); }

/* ── 28-B 휴대폰 받은함 ────────────────────────────────────────────────
   ① 사이드바 트리: 종(문자·통화·메세지) 아래 연락처 줄이 한 단 더 들어간다.
   ② 대화 화면: 수신 좌·발신 우 말풍선. 폭은 카드 안에서만 자라고(6b-8 규칙) 긴 낱말은 끊어 접는다.
   ③ 선택 바: 화면 아래에 떠서 발췌 보내기로 이어진다(입력창이 없는 탭이라 자리가 비어 있다). */
.nav.sub .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.nav.sub.kindhead .caret { width: 12px; flex: none; font-size: 10px; }
.side-kind { display: flex; flex-direction: column; }
.nav.sub2 { padding-left: 40px; font-size: var(--fs-s); }
.nav.sub2 .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }

/* ── 44-A · 2.0 PC 3단 배치(≥1280px) ──────────────────────────────────────
   기획 정본 `기획_클로드비서_2.0_v0.1.html` PC 3단·W 절 · 근거 §43·§43-1·§43-2.
   ★ 1279px 이하에서는 이 절의 어느 줄도 걸리지 않는다 — 폰·태블릿 배치(6b-12 네 뷰포트)는 한 줄도 바뀌지 않는다.
     `html.topnav3` 는 app.js render() 가 `wide3()`(= MQS.wide3, 아래 @media 와 같은 문자열)일 때만 붙인다.
     클래스와 요소는 반드시 함께 켜고 끈다 — 클래스만 남으면 .shell 이 48px 짧은 채로 굳는다.
   ★ 높이는 --topnav-h **한 값**이다. 내비 두께를 바꾸려면 아래 첫 줄만 고친다(devbar 와 같은 방식).
   ★ 위쪽 안전영역: 이 폭은 PC 창이라 인셋이 0 이다. 내비는 인셋을 읽지 않고, 대신 그 아래 .topbar 의
     --sai-top 을 0 으로 눌러 둔다(안 누르면 노치 있는 큰 화면에서 빈 띠가 두 겹으로 생긴다).
     ※ 여기에 `var(--sai-top, env(safe-area-inset-top))` 꼴을 **새로 쓰지 않는다** — 8-18-1 단위 시험이
       그 낱말이 style.css 에 몇 번 나오는지(여섯) 세어 회귀를 막는다. */
html.topnav3 { --topnav-h: 48px; }
.topnav { display: flex; align-items: center; gap: 10px; height: var(--topnav-h, 48px); padding: 0 14px;
  border-bottom: 1px solid var(--line); background: var(--bg-2); }
html.topnav3 .shell { grid-template-rows: calc(var(--app-h, 100svh) - var(--topnav-h)); height: calc(var(--app-h, 100svh) - var(--topnav-h)); }
/* 개발 판 띠와 겹칠 때 — 띠 높이·인셋은 .app 의 padding-top 이 이미 먹었고 여기서 같은 값을 한 번 더 뺀다. */
html.topnav3.devmode .shell { grid-template-rows: calc(var(--app-h, 100svh) - var(--topnav-h) - var(--devbar-h) - env(safe-area-inset-top));
  height: calc(var(--app-h, 100svh) - var(--topnav-h) - var(--devbar-h) - env(safe-area-inset-top)); }
html.topnav3 .topbar { --sai-top: 0px; }

/* ── U13 · 새 판 배너가 머리를 덮지 않게(2026-10-08 · 6b-5 미완 ③) ─────────────────
   왜: .verbar 는 fixed 라 떠 있는 동안 머리(제목·☰·막대)를 통째로 가렸다.
   app.js verBarSpace() 가 배너가 **실제로 보일 때만** html.verbar-on 과 두 값을 적는다
     --verbar-h = 배너 높이 · --verbar-b = 배너 아래 끝(개발 판 띠 아래에 뜨면 띠 높이까지 포함).
   ① 1279 이하 = 머리 위쪽 여백 변수(.app 안 --sai-top)를 배너 높이로 바꾼다. 배너가 안전영역을 이미 먹었으므로
      인셋을 더하지 않는다(개발 판 .app 의 0px 규칙 뒤에 두어 그 판에서도 같은 값). 셸 높이는 그대로 · 머리만 그만큼 커진다.
   ② 1280 이상 = 상단 내비를 배너 높이만큼 내리고 셸을 배너 아래 끝만큼 줄인다(개발 판 셸 규칙 뒤라 이것이 이긴다).
   ③ 작은 셸(.shell.compact)의 머리는 인셋 변수를 안 읽으므로 따로 위 여백을 더한다.
   ★ 이 절은 위쪽 인셋 낱말·env() 를 새로 쓰지 않는다(8-18-1 E1·E7). 배너가 없으면 클래스가 없어 한 줄도 안 걸린다. */
html.verbar-on .app { --sai-top: var(--verbar-h, 0px); }
html.verbar-on .shell.compact .topbar { padding-top: calc(var(--sp-1) + var(--verbar-h, 0px)); }
html.topnav3.verbar-on .topnav { margin-top: var(--verbar-h, 0px); }
html.topnav3.verbar-on .shell { grid-template-rows: calc(var(--app-h, 100svh) - var(--topnav-h) - var(--verbar-b, 0px));
  height: calc(var(--app-h, 100svh) - var(--topnav-h) - var(--verbar-b, 0px)); }
.topnav .side-home { flex: none; }
.topnav .tn-here { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; max-width: 320px; }
.topnav .tn-name { font-size: 13.5px; font-weight: 600; color: var(--ink-2); min-width: 0; }
/* 진행 점 자리(44-C). 지금은 빈 상자지만 폭을 잡아 둔다 — 44-C 가 점을 채울 때 머리가 밀리지 않게. */
.topnav .tn-dots { flex: none; width: 34px; height: 8px; }
.topnav .tn-lims { display: flex; align-items: center; gap: 10px; flex: none; border: 0; background: transparent; padding: 2px var(--sp-2); border-radius: var(--radius-s); }
.topnav .tn-lims:hover { background: var(--surface); }
.topnav .tn-lim { display: flex; align-items: center; gap: 5px; }
.topnav .tn-lim .lim-bar { width: 64px; height: 5px; }
.topnav .tn-lab, .topnav .tn-pct { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.topnav .tn-pct { font-family: var(--mono); }
.topnav .tn-pct.warn { color: var(--warn); }
.topnav .tn-pct.danger { color: var(--danger); }
.topnav .tn-q { flex: none; width: 220px; font-size: var(--fs-s); padding: var(--sp-1) var(--sp-3); }
/* 44-E ① 마지막 방어선 — 다음에 또 부품을 가져와 크기 규칙이 빠져도 그림이 띠(48px) 밖으로 못 나간다. */
.topnav img, .topnav svg { max-height: calc(var(--topnav-h, 48px) - 12px); max-width: 64px; }
/* 44-E ① 같은 함정 두 번째 — ↻ 회전이 `.side-head` 에만 걸려 상단 내비의 ↻ 는 돌지 않았다. */
.topnav .refresh.spinning { animation: spin .9s linear infinite; }
/* 51-I · 44-A 미완 ② — ≥1280 에서 사이드바 머리(아이콘·「클로드비서」·테마·↻)가 상단 내비와 겹쳐 두 번 보였다.
   상단 내비가 같은 네 부품(homeHeadBtn·테마·headRefreshBtn)을 이미 들고 있으므로 사이드바 쪽만 숨긴다.
   ★ html.topnav3 한정 — 1279px 이하(폰·태블릿·드로어)는 사이드바 머리가 유일한 머리라 그대로 둔다. */
html.topnav3 .sidebar .side-head { display: none; }

/* ── 44-C · 진행 점 · 티커 · 최신 작업 · 알림 점 ─────────────────────────────
   점 셋의 자리는 고정(왼쪽 대기 회색 · 가운데 진행 노랑 · 오른쪽 완료 파랑)이고 켜짐만 바뀐다(이름 폭 무변).
   깜빡임은 이름 한 자리(.unseen-blink)이고 멈춤도 맨 아래 prefers-reduced-motion 한 자리다(정지 · 색만).
   ★ 이 절은 넓은 폭 전용 블록 밖이다 — 점·티커는 폰에서도 뜬다(넓은 폭 블록은 파일에 여전히 하나뿐). */
.pdots { display: inline-flex; align-items: center; gap: 3px; flex: none; }
.pdots i { width: 6px; height: 6px; border-radius: 50%; background: transparent; }
.pdots i.pd-q.on { background: var(--muted); }
.pdots i.pd-r.on { background: var(--warn); }
.pdots i.pd-d.on { background: var(--accent); }
.biz .pdots { align-self: center; margin-left: 2px; }
.topnav .tn-dots { justify-content: center; }
@keyframes csUnseenBlink { 0%, 100% { background-color: transparent; } 50% { background-color: color-mix(in srgb, var(--warn) 30%, transparent); } }
.unseen-blink { border-radius: var(--r-xs); animation: csUnseenBlink 2s ease-in-out infinite; }
/* 입력창 위 티커 — .fbar 클래스를 같이 달아 모양이 같다. 층도 같은 .fdock. composer 가 파일 띠보다 먼저 붙인다. */
.fdock.tdock { margin-bottom: 0; }
.tdock > .ticker { margin-bottom: var(--sp-1); }
.ticker .tagchip { flex: 0 0 auto; }
.ticker.flow { animation: csTickerIn .35s ease-out; }
@keyframes csTickerIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* 최신 작업 줄(오른쪽 패널 칸 · 좁은 폭 시트 공용) */
.lt-list { display: flex; flex-direction: column; gap: 1px; }
.lt-row { display: flex; align-items: center; gap: var(--sp-2); width: 100%; min-width: 0; border: 0; background: transparent; padding: var(--sp-1) 3px;
  border-radius: 6px; font: inherit; font-size: var(--fs-s); text-align: left; cursor: pointer; color: inherit; }
.lt-row:hover { background: var(--surface-2); }
.lt-row .lt-what { flex: 1 1 auto; min-width: 0; }
.lt-row .lt-when { flex: none; font-size: var(--fs-xs); }
.lt-row .lt-st { flex: none; width: 16px; text-align: center; }
.lt-row.st-running .lt-st { color: var(--warn); }
.lt-row.st-error .lt-st { color: var(--danger); }
.lt-row.unseen .lt-what { font-weight: 700; }
.lt-row.unseen .lt-st { color: var(--accent); }
/* 좁은 폭 머리 알림 점. ≥1280(html.topnav3)은 오른쪽 패널 칸이 같은 목록이라 숨긴다. */
.nbell { position: relative; }
.nbell.has-new::after { content: ''; position: absolute; top: 4px; right: 4px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
html.topnav3 .nbell { display: none; }
@media (prefers-reduced-motion: reduce) {
  .unseen-blink { animation: none; background-color: color-mix(in srgb, var(--warn) 24%, transparent); }
  .ticker.flow { animation: none; }
  .gtab { transition: none; }   /* 44-E · 미끄럼(scrollTo smooth)은 app.js 가 같은 조건으로 끈다 */
}
/* ── 44-D · 홈에서 물은 질문 — 카드 머리 한 줄 · 되묻기 상자 ───────────────
   폭 조건 없음(폰·PC 공통). 되묻기 버튼 줄은 접혀 내려간다(row wrap). */
.routefrom { margin-bottom: 3px; }
.routefrom a { font-weight: 600; }
.routeask { margin-top: var(--sp-3); padding: var(--sp-3) 10px; border: 1px dashed var(--warn); border-radius: var(--radius-s); background: var(--surface); }
.routeask.done { border-style: solid; border-color: var(--line); }
.home-ul .routeask { margin: var(--sp-1) 0 var(--sp-2); }
.routeask select.btn { max-width: 180px; }

@media (min-width: 1280px) {
  /* 오른쪽 패널 상설 300px(지시서 44-A 2). 접기 없음 — .panel 의 ✕ 는 .mobile-only 라 이 폭에서 애초에 안 뜬다. */
  .shell.with-panel { grid-template-columns: 250px 1fr 300px; }
  /* 파일 서랍이 패널 둘째 칸으로 올라갔으므로 입력창 오른쪽 아래 손잡이는 이 폭에서만 숨긴다(<1280 은 종전 그대로).
     띠(.fbar)는 그대로 둔다 — 방금 만든 파일 한 줄은 입력창 바로 위가 제자리다(§8-21). */
  .shell.with-panel .composer .fdrawer { display: none; }
}
/* 패널 두 층 — [세 칸 받침대(고정)] + [본문(스크롤)]. 받침대가 없는 폭에서는 .panel 이 종전처럼 통째로 구른다. */
.panel.has-dock { display: flex; flex-direction: column; gap: 10px; overflow: hidden; }
.panel.has-dock .panel-body { flex: 1; min-height: 0; overflow-y: auto; }
.pdock { flex: none; display: flex; flex-direction: column; gap: var(--sp-3); }
.pdock-sec { border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--surface); overflow: hidden; }
.pdock-sec > h4 { margin: 0; padding: 5px 9px; border-bottom: 1px solid var(--line); background: var(--bg-2);
  font-size: var(--fs-xs); font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.pdock-sec .pd-body { padding: 7px 9px; max-height: 30svh; overflow-y: auto; overscroll-behavior: contain; }
/* 패널 안 파일 서랍 — 같은 fileDrawer() 를 띄우지 않고 제자리에 눕힌다(그림자·고정폭·절대위치를 끈다). */
.fdrawer.in-panel { position: static; align-self: stretch; }
.fdrawer.in-panel .fd-pop { position: static; width: auto; max-width: none; border: 0; box-shadow: none; background: transparent; padding: 0; }
.fdrawer.in-panel .fd-listwrap { max-height: none; }
/* 45-A · 임시폴더 칸(패널 셋째 칸 · 파일 탭 [임시] 창 공용). 폭 조건 없음 — 창은 좁은 폭에서도 뜬다. */
.inbox { display: flex; flex-direction: column; gap: var(--sp-2); }
.inbox-list { display: flex; flex-direction: column; gap: 2px; }
.inbox-row { display: grid; grid-template-columns: auto auto minmax(0, 1fr); column-gap: var(--sp-2); align-items: center; padding: 3px 2px; border-radius: 6px; }
.inbox-row.on { background: var(--bg-2); }
.inbox-row .ib-name { background: none; border: 0; padding: 0; text-align: left; color: inherit; cursor: pointer; font: inherit; min-width: 0; }
.inbox-row .ib-name:hover { text-decoration: underline; }
.inbox-row .ib-meta, .inbox-row .ib-why { grid-column: 3; }
.inbox-row .ib-why.err { color: var(--danger, #c0392b); }
/* 53-R · 올리기 대기열 상태 줄(파일 탭 「임시」 칸 📷 줄 아래). 올린 적이 없으면 비어 있어 높이 0. */
.ib-ups { margin: -4px 0 var(--sp-3); display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ib-ups:empty { display: none; }
.ib-up-row { display: flex; gap: var(--sp-1); min-width: 0; white-space: nowrap; overflow: hidden; }
.ib-up-row > .ellipsis { min-width: 0; flex: 0 1 auto; }
.ib-up-row.err > .muted { color: var(--danger, #c0392b); }
.inbox-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); border-top: 1px solid var(--line); padding-top: var(--sp-2); }
/* 45-B · 오피스 보기 — xlsx 는 PDF 대신 표(extract.mjs 시트 텍스트) */
.otable-wrap { overflow: auto; max-height: 60vh; border: 1px solid var(--line); border-radius: 6px; margin: var(--sp-1) 0 10px; }
.otable { border-collapse: collapse; font-size: 12px; }
.otable td { border: 1px solid var(--line); padding: 2px var(--sp-2); white-space: nowrap; vertical-align: top; }

.chat { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.msg { max-width: 84%; min-width: 0; padding: 7px 9px; border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--line); }
.msg.in { align-self: flex-start; border-bottom-left-radius: var(--r-xs); }
.msg.out { align-self: flex-end; border-bottom-right-radius: 4px; background: var(--accent-soft); }
.msg.pick { outline: 2px solid var(--accent); outline-offset: 1px; }
.msg-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; min-width: 0; }
.msg-body { margin-top: 3px; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; line-height: 1.45; }
.msg-acts { margin-top: 5px; gap: var(--sp-1); }
/* §48-1 · 대화창(§49-1 B·C · §49-2 말풍선). 새 @media 없음(47-A 네 모양 그대로 따른다). */
.msg-sender { font-size: 12px; font-weight: 600; color: var(--accent); }
.msg-fwd { font-size: var(--fs-xs); color: var(--muted); font-style: italic; }
.msg-quote { margin: 2px 0 var(--sp-1); padding: 3px var(--sp-3); border-left: 3px solid var(--line); background: var(--surface); border-radius: 6px; font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-status { align-self: flex-end; font-size: var(--fs-xs); color: var(--muted); margin-top: -2px; }
.msg-status.read { font-weight: 700; color: var(--accent); }
.msg-status.failed { color: var(--danger); font-weight: 700; }
.unread-sep { align-self: stretch; text-align: center; font-size: 12px; color: var(--accent); border-top: 1px solid var(--accent); padding-top: 3px; margin: var(--sp-2) 0; }
.chat-compose { position: sticky; bottom: 0; z-index: var(--z-head); margin-top: var(--sp-3); padding: var(--sp-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.chat-bar { gap: var(--sp-2); align-items: flex-end; flex-wrap: nowrap; }
.chat-input { flex: 1; min-width: 0; resize: none; max-height: 140px; }
.stage-strip, .quote-band { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-bottom: var(--sp-2); align-items: center; }
.to-bottom { position: fixed; right: 16px; bottom: 96px; z-index: var(--z-float); width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line); background: var(--surface); box-shadow: 0 2px 8px rgba(0, 0, 0, .18); font-size: 18px; }
.send-fail { color: var(--danger); font-weight: 800; }
.chat.drop { outline: 2px dashed var(--accent); outline-offset: 4px; }
/* 48-1b · 말풍선 글자 선택 끔(긴 누름 메뉴와 겹침 ⑪ — 복사는 ⋯ [복사]) · 첨부 2～9장 격자 · 보내기 진행 한 줄. 새 @media 없음. */
.chat .msg { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.msg-more { padding: 0 var(--sp-2); line-height: 1.2; }
.att-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px; margin-top: 5px; max-width: 264px; }
.att-grid.n2, .att-grid.n4 { grid-template-columns: repeat(2, 1fr); }
.att-grid .attthumb { max-width: none; padding: 0; border: 0; background: none; }
.att-grid .attthumb img { width: 100%; height: auto; aspect-ratio: 1 / 1; }
.att-grid .attthumb .small { display: none; }
.send-prog { font-size: 12px; color: var(--muted); }
/* 48-1c · 집PC 실측 결함. ① 대화 화면은 입력칸 하나(사업 입력창 숨김) ② 바 높이만큼 스크롤 여유 ④ 말풍선 = 본문·첨부만,
   시각은 밑 작은 줄, 내 것 오른쪽 accent 계열 · 상대 왼쪽 회색. .pchat = 폰 대화(사업 대화 .chat .msg 는 그대로). 새 @media 없음. */
html.chat-thread .main > .composer { display: none; }
/* 50-C ① · PIN 문(msgGate)이 선 동안에도 사업 입력창을 숨긴다(받은함·휴지통 문 포함). */
html.msg-gate .main > .composer { display: none; }
/* 51-S ⑤ · 휴대폰 대화 목록·휴지통(문이 열린 뒤)도 사업 입력창 숨김. 표식은 app.js viewPhone 이 붙이고 render 가 뗀다. */
html.msg-list .main > .composer { display: none; }
html.chat-thread #content { scroll-padding-bottom: calc(var(--chat-bar-h, 64px) + 8px); }
.pchat .msg { display: flex; align-items: flex-end; gap: var(--sp-2); max-width: 84%; padding: 0; background: none; border: 0; border-radius: 0; }
.pchat .msg.out { flex-direction: row-reverse; }
.pchat .msg.pick { outline: none; }
.pchat .msg-col { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; max-width: 100%; }
.pchat .msg.out .msg-col { align-items: flex-end; }
.pchat .bubble { min-width: 0; max-width: 100%; padding: 7px 10px; border-radius: 14px; background: var(--surface-2); color: var(--ink); }
.pchat .msg.in .bubble { border-bottom-left-radius: var(--r-xs); }
.pchat .msg.out .bubble { background: var(--accent-soft); border-bottom-right-radius: var(--r-xs); }
.pchat .msg.pick .bubble { outline: 2px solid var(--accent); outline-offset: 1px; }
.pchat .msg-body { margin-top: 0; }
.pchat .msg-body.masked { filter: blur(2.5px); color: var(--muted); }
.pchat .msg-meta { display: flex; align-items: center; gap: 6px; margin-top: 2px; font-size: 11px; color: var(--muted); }
.pchat .msg-check { align-self: center; margin: 0; }
/* 48-5 · 채팅다운 채팅(§50-2). 위 48-1c 절을 덮는다. 새 @media 없음 — 폭은 min/max 식으로(PC 65% · 좁은 폭 84%),
   폴드 네 모양(47-A)·PC 3단(44-A)이 같은 부품을 쓴다. 색 = 내 것 --accent/--accent-ink · 상대 --bubble-in(신설 하나). */
.pchat { gap: 2px; }
.pchat .msg { max-width: min(84%, max(65%, 420px)); gap: var(--sp-2); align-items: flex-start; }
.pchat .msg.in { align-self: flex-start; }
.pchat .msg.out { align-self: flex-end; }
.pchat .msg.first { margin-top: 8px; }
.pchat .bub-row { display: flex; align-items: flex-end; gap: var(--sp-1); min-width: 0; max-width: 100%; }
.pchat .msg.out .bub-row { flex-direction: row-reverse; }
.pchat .bubble { position: relative; padding: var(--sp-3) var(--sp-4); border-radius: 18px; background: var(--bubble-in); color: var(--ink); }
.pchat .msg.in .bubble { border-bottom-left-radius: 18px; }
.pchat .msg.out .bubble { background: var(--bubble-out); color: var(--bubble-out-ink); border-bottom-right-radius: 18px; }
/* 꼬리 — 묶음 첫 말풍선만(카톡: 위쪽 모서리). */
.pchat .msg.in.first .bubble { border-top-left-radius: var(--r-xs); }
.pchat .msg.out.first .bubble { border-top-right-radius: var(--r-xs); }
.pchat .msg.in.first .bubble::before, .pchat .msg.out.first .bubble::before { content: ""; position: absolute; top: 0; width: 8px; height: 10px; background: inherit; }
.pchat .msg.in.first .bubble::before { left: -5px; clip-path: polygon(100% 0, 0 0, 100% 100%); }
.pchat .msg.out.first .bubble::before { right: -5px; clip-path: polygon(0 0, 100% 0, 0 100%); }
.pchat .msg.out .msg-quote { background: rgba(255, 255, 255, .18); color: inherit; border-left-color: currentColor; }
.pchat .msg.out .msg-body.masked { color: inherit; }
.pchat .msg-meta { flex-direction: column; align-items: flex-start; gap: 0; margin: 0; flex: none; }
.pchat .msg.out .msg-meta { align-items: flex-end; }
.pchat .msg-time { font-size: 10.5px; color: var(--muted); white-space: nowrap; line-height: 1.2; }
.pchat .msg-more { visibility: hidden; font-size: 12px; padding: 0 var(--sp-1); }
.pchat .msg:hover .msg-more, .pchat .msg-more:focus-visible { visibility: visible; }
.pchat .msg-sender { color: var(--ink-2); margin: 0 0 2px 2px; }
.pchat .msg-status { margin-top: 2px; }
.pchat .msg-status.failed::before { content: "! "; }
.chat-ava { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 36px; height: 36px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); font-weight: 700; font-size: 15px; line-height: 1; overflow: hidden; }
.chat-ava.sm { width: 32px; height: 32px; font-size: 13px; }
.chat-ava.xs { width: 22px; height: 22px; font-size: var(--fs-xs); margin-right: var(--sp-2); }
.chat-ava.ghost { visibility: hidden; }
.day-chip { align-self: center; margin: var(--sp-4) 0 var(--sp-1); }
.day-chip span { display: inline-block; padding: 3px var(--sp-4); border-radius: var(--r-pill); background: var(--bg-2); color: var(--ink-2); font-size: 12px; }
.chat-head { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.chat-back, .chat-more { flex: none; width: var(--ctl-s); height: var(--ctl-s); border: 0; border-radius: 50%; background: none; color: var(--ink); font-size: 24px; line-height: 1; cursor: pointer; }
.chat-more { font-size: var(--fs-xl); }
.chat-back:hover, .chat-more:hover { background: var(--bg-2); }
.chat-head-nm { flex: 1; min-width: 0; }
.chat-head-title { font-weight: 700; font-size: var(--fs-l); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-head-sub { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 입력 바 — 원형 ＋ · 둥근 입력칸 · 원형 accent 보내기(글 없으면 흐림) · 하단 안전영역. 위치는 48-1c sticky 그대로(8-18-1 키보드 규칙). */
.chat-compose { border-radius: 24px; padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px)); }
.chat-compose .chat-bar { align-items: flex-end; gap: var(--sp-2); }
.chat-plus, .chat-send { flex: none; width: var(--tap); height: var(--tap); border-radius: 50%; border: 0; font-size: var(--fs-xl); line-height: 1; cursor: pointer; }
.chat-plus { background: var(--bg-2); color: var(--ink-2); }
.chat-send { background: var(--accent); color: var(--accent-ink); font-weight: 700; }
.chat-send.empty { opacity: .4; }
.chat-send:disabled { opacity: .4; cursor: default; }
.chat-compose .chat-input { border-radius: 20px; padding: 9px 14px; min-height: var(--tap); line-height: 1.35; border: 1px solid var(--line); background: var(--bg); }
.kcard { max-width: min(84%, max(65%, 420px)); border-radius: 18px; }
.chat-files { display: flex; flex-direction: column; gap: 10px; }
.chat-nm { font-weight: 600; }
.chat-when { margin-left: auto; padding-left: var(--sp-2); white-space: nowrap; }
/* §28-6 · 받은함 카드 안 첨부 줄. 이름만 보이던 자리에 썸네일·종류·크기를 넣는다. */
.msg-atts { margin-top: 5px; gap: var(--sp-2); align-items: flex-start; }
.attthumb { display: flex; flex-direction: column; align-items: center; gap: 2px; max-width: 132px; padding: var(--sp-1); border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--surface); cursor: pointer; }
.attthumb img { display: block; width: 120px; height: 90px; object-fit: cover; border-radius: 6px; background: var(--surface-2); }
.attthumb .small { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attlock { display: flex; align-items: center; justify-content: center; width: 120px; height: 90px; border-radius: 6px; background: var(--surface-2); font-size: 22px; }

/* §37-3 · 그림자 한 겹 규칙에 맞춰 두 겹짜리 고정값을 --shadow 로 통일했다(떠 있는 막대라 그림자 자체는 남긴다). */
.selbar { position: sticky; bottom: 8px; z-index: var(--z-float); display: flex; align-items: center; gap: var(--sp-3); margin-top: 10px; padding: var(--sp-3) 10px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface); box-shadow: var(--shadow); }
.selbar[hidden] { display: none; }

/* 좁은 폭(6b-8 규칙과 같은 조건) — 말풍선이 카드를 넘지 않게 하고 버튼 줄은 접는다. */
@media (max-width: 420px) {
  .msg { max-width: 100%; }
  .selbar { flex-wrap: wrap; border-radius: var(--radius); }
  .nav.sub2 { padding-left: 32px; }
}

/* ── 40 홈 대시보드(#/home) ─────────────────────────────────────────────
   폰은 한 줄에 카드 하나, 760px 이상이면 2열(§40 ③). 카드 껍데기·칩·막대는 다른 탭과 같은 부품을 쓴다. */
.home-head { margin-bottom: 10px; }
.home-head h2 { margin: 0; font-size: 17px; letter-spacing: -.02em; }
.home-grid { display: grid; grid-template-columns: 1fr; gap: 12px; align-items: start; }
@media (min-width: 760px) { .home-grid { grid-template-columns: 1fr 1fr; } }
.home-card { margin: 0; }
/* 51-R · 홈 카드 윗선·높이(재희님 09-17 「홈에서 물은 질문」 top 146 vs 「오늘」 top 156 · 높이 117 vs 618).
   원인 ① 전역 `.card + .card { margin-top: 10px }`(0,2,0)가 `.home-card { margin: 0 }`(0,1,0)을 이겨 둘째 카드부터 10px 내려갔다
   (6b-6 dev-cards 와 같은 원인) ② align-items:start 라 같은 줄 카드 높이가 제각각.
   고침 = 격자 안 카드 margin 0(간격은 gap 한 가지) · 2열에서 stretch + 카드 최대 420px · 몸통(.home-body)만 안쪽 스크롤 ·
   넘치면 아래 「더 보기」(app.js homeFitMore 가 넘침을 재서 켠다). 1열(폰·759 이하)은 최대 높이 없음 · 간격만 gap. */
.home-grid > .card, .home-grid > .card + .card { margin: 0; }
.home-card { display: flex; flex-direction: column; min-height: 0; }
.home-card > .home-body { flex: 1 1 auto; min-height: 0; }
.home-card > .home-more { align-self: flex-start; margin-top: var(--sp-2); }
.home-card > .home-more[hidden] { display: none; }
@media (min-width: 760px) {
  .home-grid { align-items: stretch; }
  .home-grid > .home-card { max-height: 420px; }
  .home-grid > .home-card > .home-body { overflow-y: auto; overscroll-behavior: contain; }
}
/* 다른 격자 칸도 같은 형제 간격이 윗선을 밀지 않게(간격은 각 격자 gap). dev-cards 는 6b-6 에서 이미 막았다. */
.two > .card + .card, .files-grid > .card + .card, .fx-cards > .card + .card, .stat-grid > .card + .card { margin-top: 0; }
.home-card .home-ul { margin: var(--sp-2) 0 0; padding-left: 17px; font-size: 13px; }
.home-card .home-ul li { margin: 3px 0; }
.home-card .home-ul.plain { list-style: none; padding-left: 0; }
/* 파일 한 줄 — 누르면 §8-19-1 뷰어. 이름은 길면 말줄임, 시각은 오른쪽 끝. */
.home-file { display: flex; align-items: center; gap: var(--sp-2); width: 100%; padding: 2px 0; border: 0; background: none; color: var(--ink); font-size: 13px; text-align: left; }
.home-file .nm { flex: 1; min-width: 0; }
.home-file:hover .nm { color: var(--accent); text-decoration: underline; }

/* 51-F ① · 원형 보내기(↑)·＋ 단추가 세로로 늘어나던 것(재희님 폰 캡처 · 운영 1.100.0 펼침 세로).
   flex 줄의 stretch·전역 버튼 규칙에 끌려 높이가 입력칸을 따라갔다 — 크기를 네 칸(width·height·min·max)으로 못 박고
   줄 아래(입력칸이 여러 줄로 자라도 바닥)에 선다. 모든 모양(PC·폴드 넷) 공통이라 미디어 조건 없이 둔다.
   자리: 47-A 절 앞(47-A·44-K 시험이 그 절 뒤 선택자를 모양 클래스로만 받는다). 옛 38px 규칙보다 선택자가 세다. */
.chat-compose .chat-bar { align-items: flex-end; }
.chat-compose .chat-send, .chat-compose .chat-plus { flex: none; align-self: flex-end; box-sizing: border-box;
  width: 40px; height: 40px; min-width: 40px; min-height: 40px; max-width: 40px; max-height: 40px; aspect-ratio: 1 / 1; padding: 0;
  display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; }
.chat-compose .chat-input { min-height: 40px; }

/* 51-H · [인쇄하기](PC·브라우저 갈래). app.js printInPage 가 .print-root 에 쪽 그림을 붙이고 html.printing 을 켠다.
   화면에서는 늘 숨김 · 인쇄 때만 그것만 찍는다. 셸 높이 고정(--app-h)·overflow 가 여러 쪽을 한 쪽으로 자르지 않게 풀어 준다.
   자리: 47-A 절 앞(47-A·44-K 시험이 그 절 뒤 선택자를 모양 클래스로만 받는다) · 고정색 없음(37·39 시험). */
.print-root { display: none; }
@media print {
  html.printing, html.printing body { height: auto !important; min-height: 0 !important; overflow: visible !important; }
  html.printing body > :not(.print-root) { display: none !important; }
  html.printing .print-root { display: block !important; }
  html.printing .print-page { display: block; max-width: 100%; max-height: 100vh; margin: 0 auto; object-fit: contain; break-after: page; page-break-after: always; }
  html.printing .print-page:last-child { break-after: auto; page-break-after: auto; }
}

/* 51-L ① · 머리 ☰ 는 늘 맨 위 층·더블탭 확대 지연 없음(옆 사용량 막대 층은 47-A 절 안 막대 규칙 뒤).
   누르기 처리는 app.js onMenuTap(문서 capture 위임) — 단추에 onclick 이 없다. 자리: 47-A 절 앞(그 절 앞에는 모양 이름을 안 쓴다 — 47-A 시험). */
.topbar .side-tgl { position: relative; z-index: 2; touch-action: manipulation; flex: none; min-width: var(--ctl-s); min-height: var(--ctl-s); }

/* P-19 · 기기별 화면 잠금 덮개(app.js screenLockShow · body 바로 아래). 층 84 = 메뉴·시트(80) 위 · 토스트(85) 아래
   (U10 규약 「토스트 이상인 층 0」 · 토스트는 글자뿐이라 풀기 실패 안내가 보이는 편이 낫다). 불투명 — 뒤 화면이 비치지 않는다.
   자리: 47-A 절 앞(모양 이름 안 씀 · 새 @media 없음). */
.screen-lock { position: fixed; inset: 0; z-index: 84; background: var(--bg); display: flex; align-items: center; justify-content: center; padding: 24px; }
.screen-lock-box { width: 100%; max-width: 320px; display: flex; flex-direction: column; gap: 10px; text-align: center; }
.screen-lock-pin { font-size: 22px; letter-spacing: 8px; text-align: center; min-height: var(--tap); }

/* 51-T ③ · 주소록 표(app.js viewContacts drawTable · contactRow). 같은 DOM 을 폭으로 셋: PC 표(머리 고정) · 1279 이하 2줄 카드 · 폰(≤600) 1열 카드.
   자리: 47-A 절 앞(47-A·44-K 시험이 그 절부터 파일 끝까지를 모양 클래스 전용으로 본다). */
.ct-table { display: grid; gap: 0; }
.ct-row { display: grid; grid-template-columns: minmax(120px, 1.2fr) minmax(90px, 1fr) 128px 190px minmax(100px, 1fr) 112px 40px 66px; align-items: center; column-gap: var(--sp-3); padding: var(--sp-2) var(--sp-1); border-bottom: 1px solid var(--line); }
.ct-head { position: sticky; top: 0; z-index: 2; background: var(--bg); font-size: 12px; color: var(--muted); padding: var(--sp-1); }
.ct-c { min-width: 0; }
.ct-name .ct-link { background: none; border: 0; padding: 0; font: inherit; font-weight: 600; color: var(--ink); cursor: pointer; text-align: left; }
.ct-name .ct-link:hover { color: var(--accent); text-decoration: underline; }
.ct-memo { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ct-gone { opacity: .6; }
.ct-sort { background: none; border: 0; padding: 0; font: inherit; color: inherit; cursor: pointer; }
.ct-sort.on { color: var(--accent); font-weight: 600; }
.ct-chan { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.ct-tags { background: none; border: 0; padding: 0; cursor: pointer; display: flex; flex-wrap: wrap; gap: 3px; text-align: left; }
.ct-auto { font-size: var(--fs-xs); }
.ct-send { text-align: right; }
/* 51-T ② · 채널 토글 칩 — 켜짐 = accent 채움 + ✓ · 꺼짐 = 흐린 테두리. 상태의 진실은 aria-pressed(모양은 거기에만 붙는다). */
.chip-tg { font: inherit; font-size: 12px; line-height: 18px; padding: 1px var(--sp-3); border-radius: var(--r-pill); border: 1px solid var(--line); background: transparent; color: var(--muted); cursor: pointer; white-space: nowrap; }
.chip-tg[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink, #fff); font-weight: 600; }
.chip-tg:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (max-width: 1279px) {
  .ct-row { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) auto 40px 66px; grid-template-areas: "name num last more send" "org chan biz biz biz"; row-gap: var(--sp-1); }
  .ct-name { grid-area: name; } .ct-org { grid-area: org; } .ct-num { grid-area: num; } .ct-chan { grid-area: chan; }
  .ct-biz { grid-area: biz; } .ct-last { grid-area: last; } .ct-more { grid-area: more; } .ct-send { grid-area: send; }
  .ct-head { grid-template-areas: "name last"; grid-template-columns: 1fr auto; }
  .ct-head > .ct-org, .ct-head > .ct-num, .ct-head > .ct-chan, .ct-head > .ct-biz, .ct-head > .ct-more, .ct-head > .ct-send { display: none; }
}
@media (max-width: 600px) {
  .ct-row:not(.ct-head) { grid-template-columns: minmax(0, 1fr) 40px 66px; grid-template-areas: "name more send" "num num num" "org org org" "chan chan chan" "biz biz biz" "last last last"; border: 1px solid var(--line); border-radius: 10px; margin-bottom: var(--sp-3); padding: var(--sp-3); }
}
/* 51-V ⑤ · 휴지통 갈래 칩(이름 앞) · 필터 칩 줄 · 같은 방 묶음(펼친 하위 줄 들여쓰기). app.js viewPhoneTrash · trashChanTag · trashRuns.
   자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.trash-chips { margin-top: var(--sp-2); gap: var(--sp-1); }
.trash-chan { flex: 0 0 auto; white-space: nowrap; }
.trash-run > .row > strong { min-width: 0; }
.list li.trash-sub { padding-left: 18px; border-left: 2px solid var(--line); }

/* 53-C · 모바일 입력창 내리기/올리기(재희님 2026-09-21 지시 「키보드처럼 화면 아래로 내린다」).
   app.js cmpApply() → 루트 클래스 **두 개**: html.cmp-on(= 폰 네 모양 · mobileShape() 판정) · html.cmp-down(= 지금 내려가 있음).
   왜 여기서 폴드 네 모양 클래스를 안 쓰나: 그 이름들은 47-A 절 전용이고(그 절 규약 · 시험이 절 앞에 없는지 본다),
   내림은 「모양 + cmp-down」 두 클래스를 겹쳐야 해 그 절의 선택자 모양(한 클래스 + 공백)에도 안 맞는다.
   그래서 모양 판정을 app.js 한 자리(mobileShape)로 올리고 여기서는 cmp-on 만 본다.
   cmp-on 이 안 붙는 PC·마우스 창·compact 는 손잡이가 display:none 이고 입력창이 한 픽셀도 안 바뀐다.
   왜 max-height 로 접나: transform 으로 밀면 그 자리가 빈 채 남아 본문이 넓어지지 않는다. 자리를 실제로 비워야
   .main 의 flex 계산(.composer)과 스크롤 여유(--chat-bar-h · .chat-compose)가 같이 따라온다.
   왜 overflow:hidden 을 내렸을 때만 주나: 평소에도 주면 입력창 안에서 **위로** 열리는 사업 드롭다운(.bizpop)·슬래시 목록이 잘린다.
   자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). 새 @media 없음. */
.cmp-grip { display: none; }
html.cmp-on .cmp-grip {
  display: flex; align-items: center; justify-content: center; height: 22px; margin: -2px 0 2px;
  cursor: pointer; touch-action: pan-x; -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.cmp-grip .cmp-grip-bar { display: block; width: 44px; height: 4px; border-radius: var(--r-pill); background: var(--line-2); transition: width .15s ease, background .15s ease; }
.cmp-grip:active .cmp-grip-bar, .cmp-grip:focus-visible .cmp-grip-bar { background: var(--accent); }
html.cmp-down .cmp-grip .cmp-grip-bar { width: 64px; background: var(--accent); }
html.cmp-on .composer,
html.cmp-on .chat-compose { max-height: calc(var(--app-h, 100svh) * .8); transition: max-height .18s ease; }
/* 내림 — 손잡이 한 줄(22px)만 남긴다. box-sizing:border-box(81줄) 라 max-height 안에 패딩이 들어간다:
   위 2 + 손잡이 22 + 아래 2 + 안전영역 = 26px + inset. 값을 바꾸면 손잡이 높이·패딩도 같이 본다. */
html.cmp-down .composer,
html.cmp-down .chat-compose { overflow: hidden; }
html.cmp-down .cmp-grip { margin: 0; }
/* 53-ZP · 내렸을 때는 손잡이 말고 전부 숨긴다(재희님 캡처 2026-09-22 12:36).
   왜: max-height + overflow:hidden 만으로 접으면 티커·파일 띠·파일 서랍(.fd-pop 은 absolute 로 위로 뜬다)이
   입력창 상자 안에 그대로 살아 있어, 서랍 손잡이를 누르면 브라우저가 초점을 보이려 그 상자를 **안에서 굴려**
   손잡이 줄이 밀려나고 파일 줄 반쪽이 본문 위에 겹쳐 보였다. 숨기면 굴릴 것이 없다(올리면 그대로 돌아온다). */
html.cmp-down .composer > :not(.cmp-grip),
html.cmp-down .chat-compose > :not(.cmp-grip) { display: none; }
/* 좌우 패딩은 건드리지 않는다 — 접힘 가로(47-A 절)가 오른쪽에 메뉴 자리 56px 를 따로 비워 두기 때문이다. */
html.cmp-down .composer {
  max-height: calc(26px + env(safe-area-inset-bottom)); padding-top: 2px; padding-bottom: calc(2px + env(safe-area-inset-bottom));
}
html.cmp-down .chat-compose {
  max-height: calc(26px + env(safe-area-inset-bottom, 0px)); padding-top: 2px; padding-bottom: calc(2px + env(safe-area-inset-bottom, 0px));
}
/* .to-bottom 은 바 높이를 96px 로 가정한 고정값(1165줄) — 바를 내리면 같이 내려온다. */
html.cmp-down .to-bottom { bottom: 56px; }

/* 54-C · 가이드(#/guide · app.js viewGuide). 항목 접기·검색은 53-K 부품(.db-item·.db-sec)을 그대로 쓰고
   여기서는 그 위에 얹는 것만 정한다 — 탭 줄·검색 줄·따라 하기 단계·기능 칩 묶음·용어 두 단.
   자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). **새 @media 없음** — 용어 두 단은 auto-fit 으로 낸다
   (이 자리가 53-C 절 범위 안이라 @media 를 쓰면 그 회차 시험 E10 이 깨진다). 새 색 토큰 없음. */
.gd-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0 var(--sp-1); }
.gd-search { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.gd-search input[type=text] { flex: 1 1 200px; min-width: 0; }
.gd-steps { margin: var(--sp-1) 0 var(--sp-2); padding-left: 20px; display: flex; flex-direction: column; gap: 5px; }
.gd-steps li { line-height: 1.5; }
.gd-stuck { margin: 2px 0 var(--sp-1); padding: 7px 9px; border-left: 3px solid var(--accent); background: var(--bg-2); border-radius: 0 var(--radius-s) var(--radius-s) 0; line-height: 1.5; }
.gd-top { margin: var(--sp-1) 0 var(--sp-2); padding-left: 18px; display: flex; flex-direction: column; gap: var(--sp-1); }
.gd-feats { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin: 3px 0 var(--sp-1); }
.gd-vpick { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-bottom: 2px; }
.gd-vpick select { min-width: 96px; }
.gd-terms { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: var(--sp-2); }
.gd-term { display: flex; gap: var(--sp-3); padding: 7px 9px; border: 1px solid var(--line); border-radius: 10px; line-height: 1.45; }
.gd-term b { flex: 0 0 96px; }

/* 53-K · 개발 현황판(#/devboard · app.js viewDevBoard). 왼쪽 = 남은 일(버그·수정 필요·남은 기획·결정·기획 절),
   오른쪽 = 적용 완료(날짜별 회차). ≥900px 두 칸 · 그 아래는 [남은 일 | 적용 완료] 칩으로 한 칸씩.
   두 칸은 각자 스크롤한다(한쪽이 길어도 다른 쪽 머리가 안 밀린다). 자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.db-wrap { display: flex; flex-direction: column; gap: 10px; min-height: 0; }
.db-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); }
.db-head .db-title { font-weight: 700; font-size: var(--fs-l); }
.db-head .db-q { flex: 1 1 180px; min-width: 140px; max-width: 360px; }
.db-tabs { display: none; gap: var(--sp-2); }
.db-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-4); align-items: start; }
.db-col { min-width: 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface, var(--bg)); padding: 10px var(--sp-4); }
.db-col > h3 { margin: 0 0 var(--sp-2); font-size: 14px; display: flex; align-items: center; gap: var(--sp-2); }
.db-sec { margin-top: var(--sp-3); }
.db-sec > summary { cursor: pointer; font-weight: 600; font-size: 13px; padding: var(--sp-1) 0; list-style-position: outside; }
.db-sec > summary .chip { margin-left: var(--sp-1); }
.db-item { border-top: 1px solid var(--line); padding: var(--sp-2) 0; font-size: 13px; line-height: 1.45; }
.db-item:first-of-type { border-top: 0; }
.db-item > summary { cursor: pointer; list-style: none; display: flex; gap: var(--sp-2); align-items: baseline; }
.db-item > summary::-webkit-details-marker { display: none; }
.db-item .db-t { min-width: 0; flex: 1 1 auto; overflow-wrap: anywhere; }
.db-item .db-meta { flex: 0 0 auto; white-space: nowrap; color: var(--muted); font-size: 12px; }
.db-item .db-sum { color: var(--muted); font-size: 12px; margin-top: 2px; overflow-wrap: anywhere; }
.db-item .db-body { margin-top: var(--sp-2); padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-s); background: var(--bg-2); font-size: 12px; white-space: pre-wrap; overflow-wrap: anywhere; }
.db-item .db-body b { display: block; margin-top: var(--sp-1); }
.db-day { margin: 10px 0 2px; font-size: 12px; font-weight: 700; color: var(--muted); position: sticky; top: 0; background: var(--surface, var(--bg)); padding: 2px 0; z-index: 1; }
.db-round { flex: 0 0 auto; font-weight: 700; color: var(--accent); min-width: 44px; }
.db-bug { color: var(--danger); }
.db-plan-done { color: var(--ok); }
.db-plan-none { color: var(--warn); }
/* 사이드바 개발 묶음 · 21 줄 바로 밑 한 줄(탭처럼 들여 씀) */
.side-group-body .nav.db-sub { padding-left: 30px; font-size: 13px; }
@media (max-width: 899px) {
  .db-tabs { display: flex; }
  .db-cols { grid-template-columns: minmax(0, 1fr); }
  .db-cols.show-left .db-col.db-right, .db-cols.show-right .db-col.db-left { display: none; }
}

/* 53-M · 질문 칸을 답과 한눈에 가른다(재희님 2026-09-21 「질문 박스 색을 차별적으로」). 종전 .q 는 surface-2 + 회색 테두리라
   다크 모드에서 답 본문과 거의 같은 판으로 보였다. 바탕 = accent-soft · 왼쪽 굵은 줄 = accent(토큰만 · 고정색 없음 — 37·39 시험).
   고쳐 보낸 원 카드(.q.edited)는 아래 줄로 종전 회색 판·회색 줄로 되돌린다(선택자 세기가 같아 뒤에 둔다). 자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.msg > .q { background: var(--accent-soft); border: 1px solid var(--line); border-left: 4px solid var(--accent); }
.msg > .q .qtext { font-weight: 600; color: var(--ink); }
.msg > .q.edited { background: var(--surface-2); border-left-color: var(--line-2); }

/* 53-S · 화면 E 파일 모아보기(대화 머리 ⋯ [파일 모아보기] · app.js openThreadFiles). 사진 = 정사각 격자(폭에 맞춰 칸 수),
   파일 = 한 줄 목록. 날짜 머리로 묶는다. 새 @media 없음 · 토큰만. 자리: 47-A 절 앞. */
.tfiles { display: flex; flex-direction: column; gap: var(--sp-3); }
.tfiles-tabs { gap: var(--sp-2); align-items: center; }
.tfiles-body { display: flex; flex-direction: column; gap: 12px; max-height: calc(var(--app-h, 100svh) * .65); overflow-y: auto; }
.tfiles-dayhead { margin-bottom: var(--sp-1); }
.tfiles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 3px; }
.tfiles-ph { padding: 0; border: 0; background: var(--surface-2); border-radius: var(--r-xs); overflow: hidden; cursor: pointer; aspect-ratio: 1 / 1; }
.tfiles-ph img { display: block; width: 100%; height: 100%; object-fit: cover; }
.tfiles-ph .attlock { width: 100%; height: 100%; }
.tfiles-list { display: flex; flex-direction: column; gap: 2px; }
.tfiles-row { display: flex; align-items: center; gap: var(--sp-3); width: 100%; padding: 7px var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--surface); color: var(--ink); text-align: left; cursor: pointer; }
.tfiles-ico { flex: none; }
.tfiles-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tfiles-meta { flex: none; white-space: nowrap; }

/* 53-T · 결정할 것(#/decide · app.js viewDecide). 카드 한 장 = 질문 + 선택지 단추(설문). 고른 단추 = accent 테두리·바탕.
   아래 [보내기] 줄은 화면 바닥에 붙는다(sticky). 새 @media 없음 · 토큰만. 자리: 47-A 절 앞. */
.dc-wrap { display: flex; flex-direction: column; gap: 10px; padding-bottom: var(--sp-3); }
.dc-group > h3 { margin: var(--sp-2) 0; font-size: 14px; display: flex; align-items: center; gap: var(--sp-2); }
.dc-card { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); padding: 10px var(--sp-4); margin-bottom: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); }
.dc-card.picked { border-color: var(--accent); }
.dc-card.sent, .dc-card.answered { opacity: .75; }
.dc-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2); }
.dc-title { font-weight: 700; flex: 1 1 200px; min-width: 0; overflow-wrap: anywhere; }
.dc-why { font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
.dc-opts { display: flex; flex-direction: column; gap: var(--sp-2); }
.dc-opt { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 2px var(--sp-3); align-items: baseline; width: 100%; text-align: left; padding: 9px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg-2); color: var(--ink); cursor: pointer; font-size: 14px; }
.dc-opt:disabled { cursor: default; }
.dc-opt.on { border-color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.dc-opt.more { color: var(--muted); }
.dc-key { grid-row: span 2; font-weight: 700; color: var(--accent); }
.dc-lab { overflow-wrap: anywhere; }
.dc-note { grid-column: 2; font-size: 12px; color: var(--muted); font-weight: 400; overflow-wrap: anywhere; }
.dc-rec { margin-left: var(--sp-2); font-size: var(--fs-xs); padding: 0 var(--sp-2); border-radius: var(--radius-s); background: var(--accent); color: var(--accent-ink); }
.dc-memo { width: 100%; }
.dc-sent { color: var(--warn); }
.dc-done { color: var(--ok); }
.dc-bar { position: sticky; bottom: 0; display: flex; gap: var(--sp-3); padding: var(--sp-3) 0; background: var(--bg); border-top: 1px solid var(--line); z-index: 2; }
.dc-bar .btn.primary { flex: 1 1 auto; }

/* 53-W · PC 에서 답 카드 글자를 마우스로 고를 수 있게(재희님 2026-09-21 「알려준 사이트 주소가 복사가 안 된다」).
   48-1b 가 긴 누름 메뉴와 겹치지 않게 .chat .msg 선택을 늘 껐는데, 그 규칙이 PC 답 카드까지 막았다.
   폰 네 모양(html.cmp-on · app.js cmpApply 가 mobileShape 로 켬)은 그대로 끔 · 그 밖(PC)은 다시 켬. 새 @media 없음. 자리: 47-A 절 앞. */
html:not(.cmp-on) .chat .msg { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; }

/* 53-X · 폰에서 눌러서 복사(재희님 2026-09-21 「모바일은 추천한 사이트·복사할 내용을 링크처럼 눌러서 복사」).
   폰 네 모양은 53-W 대로 글자 선택이 꺼져 있다 → 복사할 것만 누르는 자리로 만든다. 부품 = app.js tapCopy(답 카드 본문).
   ① 줄 안 코드(`…`) = 폰(html.cmp-on)에서만 점선 밑줄 · 누르면 복사 ② 주소 옆 📋 = 폰에서만 보임
   ③ 코드 블록 [복사] = PC·폰 모두. 새 @media 없음. 자리: 47-A 절 앞. */
html.cmp-on .msg code.cp-code { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; color: var(--accent); }
html.cmp-on .msg code.cp-code:active { background: var(--accent-soft); }
.msg .cp-btn { border: 0; background: transparent; padding: 0 var(--sp-1); margin-left: 2px; font-size: 13px; line-height: 1; cursor: pointer; vertical-align: baseline; }
html:not(.cmp-on) .msg .cp-url-btn { display: none; }
.msg pre.cp-pre { position: relative; }
.msg pre.cp-pre > .cp-pre-btn { position: absolute; top: 4px; right: 4px; padding: 2px var(--sp-3); font-size: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--bg); color: inherit; }

/* 53-ZH · 53-ZI · 답 카드 시각화(D-025) — 표 다듬기 · 상태 칩 · 숫자 타일 · 진행 막대 · 막대/꺾은선 · 일정표.
   만드는 곳은 app.js md() 한 자리. 색은 토큰만(상태 = .chip ok/warn/danger/plan · 계열 = --accent·--ok·--warn·--plan).
   폰(html.cmp-on · app.js cmpApply)에서 표는 줄마다 카드로 접힌다. 새 @media 없음. 자리: 47-A 절 앞. */
.md table.md-tbl .md-r { text-align: right; font-variant-numeric: tabular-nums; }
.md table.md-tbl .md-c { text-align: center; }
.md table.md-tbl th { background: var(--surface-2); }
.md .chip.md-st { vertical-align: 1px; }
html.cmp-on .md table.md-tbl, html.cmp-on .md table.md-tbl tbody, html.cmp-on .md table.md-tbl tr, html.cmp-on .md table.md-tbl td { display: block; }
html.cmp-on .md table.md-tbl { border: 0; overflow: visible; }
html.cmp-on .md table.md-tbl thead { display: none; }
html.cmp-on .md table.md-tbl tr { border: 1px solid var(--line); border-radius: var(--radius-s); margin: var(--sp-2) 0; padding: var(--sp-1) var(--sp-3); background: var(--surface); }
html.cmp-on .md table.md-tbl td { border: 0; padding: 2px 0; text-align: left; }
html.cmp-on .md table.md-tbl td::before { content: attr(data-l); display: inline-block; min-width: 5.5em; margin-right: var(--sp-3); color: var(--muted); font-size: 12px; }
.md .md-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: var(--sp-3); margin: var(--sp-3) 0; }
.md .md-tile { border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--sp-3) 10px; background: var(--surface); }
.md .md-tile-v { font-size: var(--fs-xl); font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; }
.md .md-tile-l { font-size: 12px; color: var(--ink-2); margin-top: 2px; }
.md .md-tile-s { font-size: 11.5px; color: var(--muted); }
.md .md-progs { margin: var(--sp-3) 0; display: grid; gap: var(--sp-2); }
.md .md-prog-h { display: flex; justify-content: space-between; gap: var(--sp-3); font-size: 13px; }
.md .md-prog-n { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.md .md-prog-bar { height: 8px; border-radius: var(--r-pill); background: var(--surface-2); overflow: hidden; }
.md .md-prog-bar > i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill); }
.md .md-chart { margin: var(--sp-3) 0; max-width: 100%; overflow-x: auto; }
.md .md-chart figcaption { font-size: 13px; font-weight: 600; margin-bottom: var(--sp-1); }
.md .md-chart svg { display: block; width: 100%; min-width: 300px; height: auto; }
.md .md-chart .md-ax { stroke: var(--line-2); stroke-width: 1; }
.md .md-chart .md-tx { fill: var(--muted); font-size: var(--fs-xs); }
.md .md-chart .md-ln { fill: none; stroke-width: 2; }
.md .md-s0 { fill: var(--accent); stroke: var(--accent); }
.md .md-s1 { fill: var(--ok); stroke: var(--ok); }
.md .md-s2 { fill: var(--warn); stroke: var(--warn); }
.md .md-s3 { fill: var(--plan); stroke: var(--plan); }
.md .md-legend { display: flex; flex-wrap: wrap; gap: 10px; font-size: 12px; color: var(--ink-2); margin-top: var(--sp-1); }
.md .md-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: var(--sp-1); vertical-align: -1px; background: var(--accent); }
.md .md-legend i.md-s1 { background: var(--ok); } .md .md-legend i.md-s2 { background: var(--warn); } .md .md-legend i.md-s3 { background: var(--plan); }
.md .md-tl { margin: var(--sp-3) 0; border-left: 2px solid var(--line); padding-left: var(--sp-4); }
.md .md-tl-day { margin: 0 0 var(--sp-3); }
.md .md-tl-d { font-weight: 700; font-size: 13px; position: relative; }
.md .md-tl-d::before { content: ''; position: absolute; left: -18px; top: 5px; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
.md .md-tl ul { margin: 2px 0 0; padding-left: var(--sp-5); }
.md .md-tl-t { color: var(--ink-2); font-variant-numeric: tabular-nums; }

/* 53-ZG · 개선 P-04 — D-003 PC 대화 오른쪽 [파일 칸](app.js viewPhoneThread · 판정 threadPaneAllowed · 부품 threadFilesPanel = 53-S 창과 같은 것).
   .tfp-wrap 은 평소 한 줄 흐름(종전과 같음). app.js 가 PC 에서 칸을 켤 때만 .on 을 붙여 두 기둥 격자가 된다 — 폰 네 모양은 안 붙인다(모양 클래스 안 씀).
   오른쪽 칸은 sticky + 자체 스크롤(스크롤 기준은 여전히 #content · 입력 바 sticky 는 왼쪽 기둥 안). D-002 폰 홈 입력창은 CSS 변경 없음(render 가 붙일 뿐).
   새 @media 없음 · 토큰만. 자리: 47-A 절 앞. */
.tfp-wrap.on { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--sp-4); align-items: start; }
.tfp-wrap.on > .tfp-main { min-width: 0; }
.tfp-pane { position: sticky; top: 0; max-height: calc(var(--app-h, 100svh) - 160px); overflow-y: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); padding: 10px; }
.tfp-pane[hidden] { display: none; }
.tfp-pane .tfiles-body { max-height: none; overflow: visible; }
.tfp-pane .tfiles-grid { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); }
.tfp-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); font-weight: 600; }
.tfp-head .grow { flex: 1; min-width: 0; }
.tfp-btn[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* 53-ZZ · 질문 바로 밑 「지금 도는 모델」 칩(app.js runModelChip). 칩 줄에서 한눈에 걸리도록 테두리·글자만 강조하고
   바탕은 --accent-soft 하나뿐이다(새 색 토큰 0 · 새 미디어쿼리 0). 도는 중(.live)은 같은 자리에서 점 하나만 깜빡인다 —
   자리가 움직이면 사람이 눈으로 다시 찾아야 한다. 자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.q .meta .chip.model { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.q .meta .chip.model.live::before { content: '●'; margin-right: var(--sp-1); animation: modelPulse 1.4s ease-in-out infinite; }
@keyframes modelPulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* 54-E · 자기개발 화면(app.js viewGrowth). 영어 문장이 주인공이라 영어 줄만 크고 진하게, 한국어는 작고 흐리게 둔다.
   새 색 토큰 0 · 새 미디어쿼리 0(53-C·53-ZZ 절 범위 안이라 미디어쿼리를 쓰면 그 두 시험이 깨진다 — 54-C 와 같은 규칙).
   자리: 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.gw-top .gw-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0 var(--sp-2); }
.gw-top .gw-tabs .btn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.gw-top .gw-find { width: 100%; max-width: 320px; }
.gw-head { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: space-between; align-items: flex-start; margin-top: var(--sp-2); }
.gw-bar { height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; margin: var(--sp-2) 0 10px; }
.gw-bar > span { display: block; height: 100%; background: var(--accent); }
.gw-card { border: 1px solid var(--line); border-radius: var(--radius-s); padding: 10px var(--sp-4); }
.gw-card .gw-k { display: block; margin: 10px 0 var(--sp-1); }
.gw-card .gw-k:first-child { margin-top: 0; }
.gw-rule { padding: var(--sp-2) 0; border-bottom: 1px dashed var(--line); }
.gw-rule:last-child { border-bottom: 0; }
.gw-ex { padding: var(--sp-2) 0; border-bottom: 1px dashed var(--line); }
.gw-ex:last-child { border-bottom: 0; }
.gw-en { font-size: 15px; font-weight: 600; line-height: 1.45; overflow-wrap: anywhere; }
.gw-ko { font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
.gw-tag { margin-right: var(--sp-2); }
.gw-tag.ai { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.gw-q { padding: var(--sp-2) 0; border-bottom: 1px dashed var(--line); }
.gw-q > summary { cursor: pointer; font-size: 14px; }
.gw-a { font-weight: 600; margin-top: var(--sp-1); }
.gw-term { padding: var(--sp-3) 0; border-bottom: 1px dashed var(--line); }
.gw-term:last-child { border-bottom: 0; }
.gw-term .gw-ko { display: inline; margin-left: var(--sp-2); }

/* 54-F · 발음 단추 · 채점 칩 · 숫자 타일 · 그래프. 색은 이미 있는 토큰과 마크다운 그래프 클래스(md-s0·md-s1)를
   그대로 쓴다(새 색 토큰 0 · 미디어쿼리 0 — 53-C·53-ZZ 시험 범위 안이라 그 두 가지를 쓰면 회귀가 깨진다).
   자리: 54-E 절 바로 뒤 · 47-A 절 앞. */
.gw-say { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-top: var(--sp-2); }
.gw-en .gw-say { display: inline-flex; margin: 0 0 0 var(--sp-2); vertical-align: middle; }
.gw-say .btn.sm { padding: 2px var(--sp-3); }
.gw-say .btn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.gw-score { margin-top: var(--sp-2); display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.gw-score > b { font-size: var(--fs-l); }
.gw-w { font-family: inherit; }
.gw-w.ok { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.gw-w.bad { opacity: .55; text-decoration: none; }
.gw-q.ok > summary { color: var(--accent); }
.gw-q.bad > summary { opacity: .7; }
.gw-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--sp-3); margin: var(--sp-3) 0 var(--sp-4); }
.gw-tile { border: 1px solid var(--line); border-radius: var(--radius-s); padding: var(--sp-3) 10px; display: flex; flex-direction: column; gap: 2px; }
.gw-tile > b { font-size: 18px; line-height: 1.2; }
.gw-chart { margin: var(--sp-2) 0; }
.gw-chart svg { width: 100%; height: auto; max-height: 130px; display: block; }

/* ── 54-G · 가이드 「구조」 탭(app.js guideMap) ───────────────────────────────
   자리: 54-F 절 바로 뒤 · 47-A 절 앞. 새 색 토큰 0(상태 4색·accent·line 만 쓴다) · 미디어쿼리 0.
   그림은 SVG 한 장이고 viewBox 로 줄어들므로 폰·PC 가 같은 그림이다 — 폭 규칙을 따로 두지 않는다.
   선은 상자보다 먼저 그려 상자가 덮는다(app.js 가 그 순서로 문자열을 만든다). */
.gm-wrap { border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--bg-2); padding: var(--sp-2); overflow-x: auto; }
.gm-svg { width: 100%; min-width: 520px; height: auto; display: block; }
.gm-line { stroke: var(--line-2); stroke-width: 1.4; }
.gm-line.on { stroke: var(--accent); stroke-width: 2.2; }
.gm-line.off { opacity: .25; }
.gm-arh { fill: var(--line-2); }
.gm-arh.on { fill: var(--accent); }
.gm-lbl { font-size: 10px; fill: var(--muted); text-anchor: middle; paint-order: stroke; stroke: var(--bg-2); stroke-width: 3px; stroke-linejoin: round; }
.gm-lbl.on { fill: var(--accent); font-weight: 600; }
.gm-lbl.off { opacity: .2; }
.gm-node { cursor: pointer; }
.gm-node rect { fill: var(--surface); stroke: var(--line-2); stroke-width: 1.4; }
.gm-node .gm-n { font-size: 15px; font-weight: 700; fill: var(--ink); }
.gm-node .gm-s { font-size: var(--fs-xs); fill: var(--muted); }
.gm-node.off { opacity: .35; }
.gm-node.sel rect { stroke-width: 2.6; }
.gm-node:hover rect, .gm-node:focus rect { stroke-width: 2.2; }
.gm-k-phone rect { fill: var(--plan-soft); stroke: var(--plan); }
.gm-k-hub rect { fill: var(--accent-soft); stroke: var(--accent); }
.gm-k-pc rect { fill: var(--ok-soft); stroke: var(--ok); }
.gm-k-out rect { fill: var(--warn-soft); stroke: var(--warn); }
.gm-info { margin-top: 10px; border: 1px solid var(--line); border-radius: var(--radius-s); padding: 10px var(--sp-4); background: var(--surface); }
.gm-info.sel { border-color: var(--accent); }
.gm-info h4 { margin: 0 0 var(--sp-2); display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; }
.gm-info p { margin: 0 0 var(--sp-3); }
.gm-line-kv { margin-top: var(--sp-2); font-size: 13px; }
.gm-line-kv.warn { color: var(--warn); }
.gm-flow { margin-top: var(--sp-3); font-size: 13px; display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; }
.gm-fl { border: 1px solid var(--line); border-radius: var(--r-pill); padding: 1px var(--sp-3); }
.gm-fl > i { color: var(--muted); font-style: normal; }
.gm-legend { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.gm-key { border: 1px solid var(--line-2); border-radius: var(--r-pill); padding: 2px 10px; font-size: 12px; }
.gm-key.gm-k-phone { background: var(--plan-soft); border-color: var(--plan); }
.gm-key.gm-k-hub { background: var(--accent-soft); border-color: var(--accent); }
.gm-key.gm-k-pc { background: var(--ok-soft); border-color: var(--ok); }
.gm-key.gm-k-out { background: var(--warn-soft); border-color: var(--warn); }

/* ── 54-H · 소리·마이크가 안 되는 자리 알림 줄(app.js growthEnvNote) ─────────
   자리: 54-G 절 뒤 · 47-A 절 앞. 새 색 토큰 0(warn 계열 재사용) · 미디어쿼리 0
   (규칙을 적는 낱말 자체가 시험에 걸리므로 여기서는 그 글자를 안 쓴다 — 54-E 교훈).
   왜 띠인가: 종전에는 되는 환경이 아니면 단추를 **조용히 안 그렸다** — 폰에서는 「스피커 모양이 안 보인다」 로만
   보였다(2026-09-28). 이유와 나가는 길([폰 크롬으로 열기])을 같은 줄에 둔다. */
.gw-env { margin: var(--sp-2) 0; display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; border: 1px solid var(--warn); background: var(--warn-soft); border-radius: var(--radius-s); padding: var(--sp-2) 10px; }
.gw-env > .small { flex: 1 1 220px; }

/* ── 54-V · 첨부 칩 줄은 아무리 늘어도 입력창을 밀어내지 않는다 ──────────────
   재희님 캡처 2026-09-29 「파일첨부하면 아래로 짤려」 — 파일 셋을 붙이니 [보내기] 줄이 화면 밖으로 나갔다.
   원인: 칩을 담는 두 줄(.composer .files · .composer .pins)에 높이 상한이 없어, 칩이 늘어난 만큼
   입력창이 통째로 세로로 자랐다. 본문(.content)은 남은 자리를 받는 쪽이라 평소에는 그만큼 줄어 주지만,
   낮은 화면에서는 본문에 최소 높이가 걸려 있어(988줄) 더는 못 줄고 입력창 아래쪽이 셸 밖으로 흘렀다.
   하필 흘러 나가는 것이 [보내기] 줄이라 「보낼 수가 없다」 가 된다.
   고침: 칩 줄에만 상한과 스크롤을 준다 — 넘치면 칩 줄 안에서 굴리고 입력창 높이는 그대로다.
   왜 입력창 전체가 아니라 칩 줄인가: .composer 에 overflow 를 주면 **위로** 열리는 사업 드롭다운(.bizpop)·
   슬래시 목록이 잘린다(53-C 절이 같은 이유로 내렸을 때만 준다). 칩 줄 안에는 위로 뜨는 것이 없다.
   값이 왜 비율인가: 화면 높이에 비례시키면 가장 낮은 화면에서도 한 줄은 보이고 큰 화면에서는 서너 줄이 보여,
   조건문을 새로 만들지 않고 한 규칙으로 둘 다 받는다(낮은 화면 한 줄은 그 블록 안에 같이 적었다).
   자리: 54-H 절 뒤 · 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.composer .files,
.composer .pins { max-height: calc(var(--app-h, 100svh) * .16); overflow-y: auto; overscroll-behavior: contain; }

/* ── 54-Y(P-37) · 답 카드에 새어 든 말풍선 모양 벗기기 ────────────────────────
   재희님 2026-09-28 17:1x 발견 → D-061 = (a) 되돌리기. 사업 답 카드(app.js:5284 class:'msg')와
   폰 대화 말풍선이 같은 이름을 쓰는데, 48-1 이 말풍선에 준 폭·상자 규칙(1165줄 .msg)에 울타리가 없어
   **답 카드가 화면 폭 84% 로 좁아지고 테두리·둥근 모서리·바탕이 한 겹 더** 입혀졌다
   (안쪽 질문 칸 .q 가 이미 제 상자를 갖는다 · 1482줄 53-M). 09-10 부터 그대로였다.
   폰 대화 쪽은 .pchat 울타리가 이미 있어 그 규칙들이 보이는 데 쓰이지 않는다 → 답 카드만 되돌린다.
   왜 1165줄을 안 고치나: 그 줄은 좁은 폭 되돌림(1284줄 .msg max-width:100%)과 짝이라 한쪽만 고치면
   폭이 두 갈래로 갈린다. 세기가 더 센 한 줄이 그 둘을 한꺼번에 이긴다(파일 순서와 무관).
   왜 .chat > .msg 가 아닌가: 폰 대화 칸도 class 에 chat 을 같이 달고 있다(.chat .pchat) — :not 으로 가른다.
   목록 모양(1805줄 언저리)은 선택자가 더 세므로 그대로다. 새 조건문 없음 · 새 색 토큰 없음.
   자리: 54-V 절 뒤 · 47-A 절 앞(그 절 뒤는 모양 클래스 전용). */
.chat:not(.pchat) > .msg { max-width: 100%; padding: 0; background: none; border: 0; border-radius: 0; }

/* ── 55-B(2.0 안정판 U5) · 답 카드 사이 간격 되돌리기 ───────────────────────────
   2.0 UI/UX 감사(2026-10-01 · req_20261001_110458_c055)가 찾은 54-Y 의 나머지 절반. 사업 대화(app.js:5078
   class:'chat')의 카드 간격은 444줄 14px 인데, 48-1 이 폰 대화 칸에 준 1164줄 .chat 규칙(같은 세기 · 뒤)이
   울타리 없이 덮어 09-10 부터 답 카드 사이가 8px 로 붙어 있었다. 폰 대화(.chat .pchat)는 8px 그대로 둔다.
   1164줄을 안 고치는 까닭은 54-Y 와 같다 — 그 줄의 min-width:0 은 폰·사업 둘 다 쓰고 있어, 세기가 더 센
   한 줄로 간격만 되돌린다. 새 조건문 없음 · 새 색 토큰 없음. 자리: 54-Y 절 뒤 · 47-A 절 앞. */
.chat:not(.pchat) { gap: 14px; }

/* ── 55-R(2.0 안정판 U1 나머지 · U2) · 읽는 중 · 주소록 채널 칩 표적 ─────────────────
   U1 · 여섯 화면(사업·브리핑·사용량·기기·설정·상태)이 첫 await 전에 아무것도 안 그려 느린 망에서 본문이 하얗게 비었다.
   본문 자식이 0 일 때만 「읽는 중…」 을 보인다. JS 로 자리표시를 넣지 않는 까닭 = 55-C viewFailed 가 「자식 0」 을
   실패 판정 근거로 쓴다 — 자식을 넣으면 그 판정이 깨진다. 0.3초 안에 그려지면 안 보인다(깜빡임 방지).
   U2 · 주소록 채널 칩(높이 22px · 셋이 붙음)은 누르면 바로 저장된다 → 폰에서만 표적을 32px 로.
   폰 판정은 53-C 의 html.cmp-on 한 클래스(app.js cmpApply 가 mobileShape 로 켬)를 그대로 쓴다 — 새 조건문 0
   (조건문을 하나 더 두면 옛 시험 열다섯 벌이 「이 절 뒤 새 조건문 없음」·「조건문 23개 그대로」 로 깨진다).
   새 색 토큰 0 · 화면 모양 클래스 안 씀. 자리: 55-B 절 뒤 · 47-A 절 앞. */
.content:empty::before { content: '읽는 중…'; display: block; padding: 32px var(--sp-5); text-align: center; color: var(--muted); font-size: 13px; opacity: 0; animation: v55r-in .2s .3s forwards; }
@keyframes v55r-in { to { opacity: 1; } }
html.cmp-on .ct-chan { gap: var(--sp-3); }
html.cmp-on .ct-chan .chip-tg { min-height: 32px; padding: var(--sp-2) var(--sp-4); }

/* ── 56-N(U6 표적) · 폰에서 누르는 자리 40px(--tap) ─────────────────────────────
   화면 촬영 계측(docs/화면결함_v0.1.md) D2·D6·D7·D10 — 폰 네 모양에서 40px 미만이던 네 자리:
   D2 머리 사용량 막대 단추(40×4 · 막대 두께 4px 가 곧 단추 높이) · D6 머리 ☰ 등 ghost 단추(36) ·
   D7 입력창 막대(보내기 32 · 📎·📷·⚙ 25 · 모델·허용 select 25 · 사업 고르기 22) ·
   D10 답 카드 행동 줄(삭제·로그·보내기·취소 25) · 진행 접기 줄(19).
   단추 상자만 키운다(min-height·min-width) — 글자 크기·색·여백·막대 두께는 그대로. 값은 55-ZF 토큰 --tap 하나.
   머리 두 줄의 음수 margin-block = 단추는 40 이 되어도 줄 안에서 차지하는 높이는 종전 ☰ 36px 그대로 —
   머리가 4px 자라면 본문 칸이 그만큼 줄어 설정 화면 맨 아래 스위치가 하단 탭 밑으로 밀렸다(촬영 계측 tap 1건).
   폰 판정은 53-C 의 html.cmp-on(app.js cmpApply · mobileShape) — PC·마우스 창은 한 픽셀도 안 바뀐다.
   새 미디어 쿼리 없음. 자리: 55-R 절 뒤 · 47-A 절 앞. D2 막대 단추 규칙 한 줄만 47-A 절 끝(그 절이 그 선택자를 허용한다 · 47-A 시험). */
html.cmp-on .topbar > .btn.ghost { min-height: var(--tap); min-width: var(--tap); justify-content: center; margin-block: calc((var(--ctl-s) - var(--tap)) / 2); }
html.cmp-on .composer .bar .btn,
html.cmp-on .composer .bar select { min-height: var(--tap); min-width: var(--tap); justify-content: center; }
html.cmp-on .msg > .actions .btn { min-height: var(--tap); min-width: var(--tap); justify-content: center; }
html.cmp-on .msg > details.prog > summary { min-height: var(--tap); }
/* 56-ZE(U6 둘째) · 같은 촬영 계측(docs/화면결함_v0.1.md)의 D5·D8·D9·D11·D13·D17 — 56-N 이 못 덮은 폰 자리.
   D5 사이드바 머리·도구 단추(31×29 · 26×34 · 새 대화 39) · D8 설정 숫자 칸(31) · D9 파일 탭 위 단추(25) ·
   D11 홈 카드 → · 브리핑 만들기(29 · 25) · D17 오른쪽 판 최신 작업 줄(28) ·
   D18 일부 = 카드 안 줄의 작은 단추(사용량 화면 74×25).
   같은 원칙 = 상자만 키운다(글자·색·막대 두께 그대로) · 값은 --tap 하나 · 폰 판정 html.cmp-on.
   일부러 안 한 것 = D13 머리 둘째 줄 단추(56-N 처럼 음수 margin-block 으로 줄 높이를 지키면 단추 상자가 머리 선 아래 본문 위로
   7px 삐져나온다 — 사진 확인 · 키우려면 머리가 15px 자라야 해서 본문 칸 몫과 같이 정할 일) · D15 사이드바 탭·사업 줄 높이(기준을 사람이 정할 몫 · 문서 §6) · D3 탭 띠 화살표(넓히면 이웃 탭의 보이는 반쪽을 덮는다) ·
   D4 사업 줄 ⋯(사업 이름 단추 오른쪽 여백 위에 얹힌 설계) · D12 아래 탭 · D14 홈 두 단추(그 선택자는 47-A 절 앞에 못 쓴다) · D16 입력창 손잡이 · D18 스위치. */
html.cmp-on .side-head .btn.ghost,
html.cmp-on .side-tools .gtab-act { min-height: var(--tap); min-width: var(--tap); justify-content: center; }
html.cmp-on .side-head .side-home,
html.cmp-on .side-tools .newchat { min-height: var(--tap); }
html.cmp-on .set-row input.sm { min-height: var(--tap); }
html.cmp-on #content > .row.wrap > .btn.sm { min-height: var(--tap); }
html.cmp-on .home-card > .row > .btn.ghost { min-height: var(--tap); min-width: var(--tap); justify-content: center; }
html.cmp-on .home-card > .home-body > .btn.sm { min-height: var(--tap); }
html.cmp-on #content > .card > .row > .btn.sm { min-height: var(--tap); }
html.cmp-on #pd-latest .lt-row { min-height: var(--tap); }

/* ── 47-A · 3.0 폴드8 네 모양(기획 §43 M) ────────────────────────────────────
   app.js MQS.mFoldV·mFoldH·mOpenV·mOpenH(폭·높이·pointer:coarse) → 루트 클래스 하나(html.m-fold-v / m-fold-h / m-open-v / m-open-h).
   ★ 이 절에는 @media 가 하나도 없다. 전부 네 클래스 선택자로만 건다 — 클래스가 안 붙는 PC·마우스 창과 ≥1280 은
     종전 격자·머리·탭이 한 픽셀도 안 바뀐다(47-A 시험이 종전 격자값을 함께 대조한다).
   ★ 위쪽 안전영역은 여기서 새로 읽지 않는다. 접힘 가로의 머리 한 줄은 종전 낮은 화면 블록(8-18-1 규칙)이 그대로 맡는다. */

/* 하단 5칸(브리핑·홈·파일·검색·더보기) — 접힘 가로는 오른쪽 세로 막대라 아래에서 따로 */
html.m-fold-v .tabbar.m5, html.m-open-v .tabbar.m5, html.m-open-h .tabbar.m5 { display: flex; border-top: 1px solid var(--line); background: var(--bg-2); padding: 4px 4px calc(4px + env(safe-area-inset-bottom)); }
html.m-fold-v .tabbar.m5 button, html.m-open-v .tabbar.m5 button, html.m-open-h .tabbar.m5 button { flex: 1; min-width: 0; border: 0; background: transparent; padding: 5px 2px; color: var(--muted); display: flex; flex-direction: column; align-items: center; gap: 2px; border-radius: 8px; white-space: nowrap; overflow: hidden; }
.tabbar.m5 button.on { color: var(--accent); background: var(--surface); }
.tabbar.m5 .ico { font-size: 17px; }
.tabbar.m5 .lb { font-size: 11px; }
html.lowh .tabbar.m5 .lb { display: none; }   /* 낮은 화면은 종전처럼 아이콘만 */

/* 머리: 메뉴(☰) 옆 사용량 막대 둘 + 알림 점은 안 본 완료가 있을 때만 */
/* 51-M · 막대 셋(5시간·주간·주간 Fable) — 줄 간격 2px · 막대 4px · 퍼센트 글 줄높이 10px 로 세 줄 합 34px(머리 44px 안) */
.m-bars { display: flex; flex-direction: column; justify-content: center; gap: 2px; flex: none; border: 0; background: transparent; padding: 0 2px; }
.m-bars .tn-lim { display: flex; align-items: center; gap: 3px; }
.m-bars .tn-lab { display: none; }
.m-bars .lim-bar { width: 36px; height: 4px; }
.m-bars .tn-pct { font-size: 10px; line-height: 10px; min-width: 2.4em; }
html.m-fold-v .m-bars .tn-pct { display: none; }
/* 51-L ① · 막대가 넘쳐도 ☰(z 2) 를 덮지 않게 */
html.m-fold-v .topbar .m-bars, html.m-fold-h .topbar .m-bars, html.m-open-v .topbar .m-bars, html.m-open-h .topbar .m-bars { position: relative; z-index: 1; overflow: hidden; }
html.m-fold-v .nbell:not(.has-new), html.m-fold-h .nbell:not(.has-new), html.m-open-v .nbell:not(.has-new), html.m-open-h .nbell:not(.has-new) { display: none; }

/* 홈 맨 위 「새 대화」「사업」(하단 탭에서 옮겨 옴) */
.m-home-acts { gap: 8px; margin: 0 0 10px; }
.m-home-acts .btn { flex: 1; justify-content: center; }

/* 접힘 세로 — 목록만. 대화·파일은 한 줄(말줄임) */
html.m-fold-v .home-file .nm, html.m-fold-v .lt-what, html.m-fold-v .fbar .nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 접힘 가로 — 머리 한 줄 + 본문, 메뉴는 오른쪽 세로. 목록은 서랍. */
html.m-fold-h .shell, html.m-fold-h .shell.with-panel { grid-template-columns: minmax(0, 1fr); }
html.m-fold-h .sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: min(300px, 85vw); z-index: 45; transform: translateX(-100%); transition: transform .2s; box-shadow: var(--shadow); }
html.m-fold-h .sidebar.open { transform: none; }
html.m-fold-h .drawer-bg { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.35); z-index: 44; }
html.m-fold-h .drawer-bg[hidden] { display: none; }
html.m-fold-h .mobile-only { display: inline-flex; }
/* 44-K · 머리는 폭 전체를 쓰고(종전 .main 48px 비킴 → 머리까지 좁아짐), 본문·입력창만 메뉴 폭 + 오른쪽 인셋만큼 비킨다.
   메뉴는 머리 아래(app.js mFoldHeadVar 가 --m-head-b 에 머리 바닥 px)에서 시작한다(종전 top 0 이 머리 오른쪽을 덮음). */
html.m-fold-h .main { padding-right: 0; }
html.m-fold-h .main > .content { padding-right: calc(58px + env(safe-area-inset-right)); padding-left: calc(10px + env(safe-area-inset-left)); }
html.m-fold-h .main > .composer { padding-right: calc(56px + env(safe-area-inset-right)); padding-left: calc(8px + env(safe-area-inset-left)); }
html.m-fold-h .topbar { padding-right: calc(10px + env(safe-area-inset-right)); padding-left: calc(10px + env(safe-area-inset-left)); }
html.m-fold-h .sidebar { padding-left: env(safe-area-inset-left); }
html.m-fold-h .tabbar.m5, html.m-fold-h .tabbar.m5.hid { display: flex; flex-direction: column; position: fixed; top: var(--m-head-b, 50px); right: 0; bottom: 0; width: calc(48px + env(safe-area-inset-right)); max-height: none; z-index: 30;
  padding: 4px calc(2px + env(safe-area-inset-right)) calc(4px + env(safe-area-inset-bottom)) 2px; border-top: 0; border-left: 1px solid var(--line); background: var(--bg-2); overflow: hidden; }
html.m-fold-h .tabbar.m5 button { flex: 1; min-height: 0; border: 0; background: transparent; padding: 0; color: var(--muted); display: flex; align-items: center; justify-content: center; border-radius: 8px; }
html.m-fold-h .tabbar.m5 .lb { display: none; }
html.m-fold-h .to-bottom { right: calc(56px + env(safe-area-inset-right)); }   /* 55-R(U4) · [↓ 맨 아래](오른쪽 16～56px)가 세로 탭 칸(0～48px+인셋)과 32px 겹치던 것 · 탭 칸 왼쪽으로 8px 띄운다 */

/* 펼침 세로 — 두 칸(목록 · 내용). 패널은 종전처럼 떠 있는 서랍. */
html.m-open-v .shell, html.m-open-v .shell.with-panel { grid-template-columns: minmax(200px, 38%) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
html.m-open-v .sidebar { position: static; transform: none; width: auto; box-shadow: none; z-index: auto; }
html.m-open-v .drawer-bg { display: none; }

/* 펼침 가로 — 세 칸(목록 · 내용 · 파일/작업). 셋째 칸은 44-A 받침대(최신 작업·파일 서랍·임시폴더). */
html.m-open-h .shell { grid-template-columns: 220px minmax(0, 1fr); }
html.m-open-h .shell.with-panel { grid-template-columns: 220px minmax(0, 1fr) 280px; }
html.m-open-h .panel { position: static; transform: none; width: auto; box-shadow: none; z-index: auto; }
html.m-open-h .panel .mobile-only { display: none; }
/* 53-H(50-A ㉒) · 펼침 가로에도 서랍 배경을 끈다 — 1455 의 펼침 세로 짝. 이 모양은 사이드바가 상설 칸이라 덮개가 필요 없다.
   지금 당장 어두워지지는 않는다(덮개를 켜는 규칙이 `@media (max-width: 759px)` 안이고 이 모양은 ≥901px).
   그래도 두는 이유: 그 상한(759)이나 이 모양의 하한(901)이 한 번이라도 바뀌면 그 순간 화면이 덮인다 — 세로 쪽만 막혀 있던 비대칭을 없앤다. */
html.m-open-h .drawer-bg { display: none; }
/* 44-K · 펼침 가로는 셋째 칸 받침대에 파일 서랍이 이미 누워 있다 — 입력창 오른쪽 아래 손잡이(📁 파일 N)는 중복이라 숨긴다(≥1280 규칙과 같은 뜻). 띠(.fbar)는 그대로. */
html.m-open-h .composer .fdrawer { display: none; }

/* 51-F ② · 펼침 세로·가로 사이드바 접기(app.js sideApply → 셸 클래스 .side-hide). 접히면 본문이 전체 폭.
   접힘 두 모양·PC 는 이 클래스가 안 붙는다. 펼침 가로 1100～1279 는 ☰ 가 숨던 폭이라 머리에서 다시 보이게 한다. */
html.m-open-v .shell.side-hide > .sidebar, html.m-open-h .shell.side-hide > .sidebar { display: none; }
html.m-open-v .shell.side-hide, html.m-open-v .shell.side-hide.with-panel { grid-template-columns: minmax(0, 1fr); }
html.m-open-h .shell.side-hide { grid-template-columns: minmax(0, 1fr); }
html.m-open-h .shell.side-hide.with-panel { grid-template-columns: minmax(0, 1fr) 280px; }
/* 보-272 · 익힘 앱(?app=growth = compact 셸 · 사이드바 숨김)이 폴드 펼침에서 첫 칸(38% · 220px)에 갇혀 나머지가 빈 화면이던 것.
   `.shell.compact`(한 칸)보다 위 펼침 두 칸 규칙이 세서 이겼다 — 접힘·PC 는 그 규칙이 없어 멀쩡했다. compact 셸은 늘 한 칸(패널 없음). */
html.m-open-v .shell.compact, html.m-open-h .shell.compact { grid-template-columns: minmax(0, 1fr); }
html.m-open-v .topbar .side-tgl, html.m-open-h .topbar .side-tgl { display: inline-flex; }
/* 53-V · 펼침 두 모양은 머리를 두 줄로(1줄 ☰·막대·제목·칩 / 2줄 버튼 묶음 .acts).
   왜(재희님 2026-09-21 16:40 폴드 펼침 캡처): 폭 판정(6b-11 NARROW ≤420 · TWOROW ≤600)이 **화면 폭**을 봐서, 사이드바가 칸을 먹은
   펼침 모양은 본문 칸이 좁아도 한 줄 배치가 됐다. 버튼 다섯(53-K [▦ 개발 현황] 이후)이 한 줄을 차지하자 제목 칸이 0 으로
   찌그러져 칩이 세로로 쌓이고 머리가 수백 px 로 늘어 내용이 아래로 밀렸다. 버튼 줄은 접지 않고 그 상자 안에서 가로로 굴린다(TWOROW 와 같은 규칙).
   제목 칸 flex-basis 0 = 낮은 화면(버튼 없는 한 줄 머리)에서는 줄바꿈이 일어나지 않는다. */
html.m-open-v .topbar, html.m-open-h .topbar { flex-wrap: wrap; row-gap: 4px; }
html.m-open-v .topbar > .grow, html.m-open-h .topbar > .grow { flex: 1 1 0; min-width: 0; }
html.m-open-v .topbar .acts, html.m-open-h .topbar .acts { flex: 1 0 100%; width: 100%; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
html.m-open-v .topbar .acts::-webkit-scrollbar, html.m-open-h .topbar .acts::-webkit-scrollbar { width: 0; height: 0; }

/* 44-K · 접힘 세로 전용 목록(app.js mFoldList). 목록 = 카드마다 질문 한 줄 + 시각, 상세 = 누른 카드 하나 전체. */
html.m-fold-v #chat.m-list:not(.m-detail) > .msg { cursor: pointer; margin: 0; padding: 6px 2px; border-bottom: 1px solid var(--line); }
html.m-fold-v #chat.m-list:not(.m-detail) > .msg > :not(.q) { display: none; }
html.m-fold-v #chat.m-list:not(.m-detail) > .msg > .q { padding: 6px 10px; max-height: none; }
html.m-fold-v #chat.m-list:not(.m-detail) > .msg > .q > :not(.qtext):not(.meta) { display: none; }
html.m-fold-v #chat.m-list:not(.m-detail) > .msg .qtext { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
html.m-fold-v #chat.m-list:not(.m-detail) > .msg .meta .btn { display: none; }
html.m-fold-v #chat.m-detail > :not(.m-sel):not(.m-back) { display: none; }
html.m-fold-v #chat.m-detail > .m-back { position: sticky; top: 0; z-index: 2; margin-bottom: 6px; background: var(--bg); }

/* 56-N(U6 표적) D2 · 머리 사용량 막대 단추 — 47-A 시험이 이 절에서 .m-bars 선택자를 허용한다. 폰 머리에만 있는 요소라 PC 무변경. */
html.m-fold-v .m-bars, html.m-fold-h .m-bars, html.m-open-v .m-bars, html.m-open-h .m-bars { min-height: var(--tap); margin-block: calc((36px - var(--tap)) / 2); }
