/* ═══ Buttons ═══ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 0 12px;
  height: 32px;
  border-radius: var(--r-sm);
  font-size: var(--fs-meta);
  font-weight: 500;
  transition: background var(--dur-fast), color var(--dur-fast), box-shadow var(--dur-fast);
  white-space: nowrap;
  flex-shrink: 0;
}
.btn:disabled { opacity: 0.4; pointer-events: none; }
.btn-primary { background: var(--explan-red); color: var(--text-on-red); }
.btn-primary:hover { background: var(--explan-red-dark); }
.btn-secondary { background: var(--surface-base); color: var(--text-primary); border: 1px solid var(--border-default); }
.btn-secondary:hover { background: var(--surface-sub); }
.btn-ghost { background: transparent; color: var(--text-secondary); }
.btn-ghost:hover { background: var(--surface-sub); color: var(--text-primary); }
.btn-danger { background: #FEE2E2; color: #991B1B; }
.btn-danger:hover { background: #FECACA; }
.btn-sm { height: 26px; padding: 0 10px; font-size: var(--fs-caption); }
.btn-xs { height: 22px; padding: 0 8px; font-size: var(--fs-tiny); }
.btn-icon { width: 32px; padding: 0; }
.btn-icon.btn-sm { width: 26px; height: 26px; }

/* ═══ CS-1163 모바일 채팅 [전송] 오탭 (2026-09-15 · ISU_CS20260914_1163) ═══
   실측(SR팀장 원인 갈래): 공용 입력창(ChatComposer)의 [전송]은 .btn-sm 26px 이고, 그 아래 8px 여백 다음이
   54px 하단 탭바 — 오른쪽 끝 칸이 «전체»(MApp.jsx 하단 탭바). 자판이 화면을 밀어 올린 상태에서 손가락이
   9px 만 아래로 치우쳐도 [전송] 대신 «전체» 탭이 눌려 대화방을 떠났다(전송 실패를 «전체»로 보내는 코드는 0).
   수리 = 터치 타깃 확대 + 여백. 모바일 셸(.m-app-frame · 실기기 셸과 PC 의 390px 미리보기 둘 다 이 클래스)에서만:
     · [전송] 높이 44px(모바일 최소 터치 타깃 관례 44×44) · 최소 너비 44px
     · 입력창 아래 여백 16px(ChatComposer 루트의 padding-bottom 이 --chat-composer-pb 를 읽는다 · 기본 8px)
   ★«입력 포커스 중 탭바 잠금»(useMModalLock 방식)은 택하지 않았다 — 자판이 뜬 상태의 실기기 거동(뷰포트
     축소·포커스 유지 여부)을 재지 않은 채 포커스 축에 잠금을 걸면 «탭을 눌렀는데 안 눌림»이 생길 수 있고,
     CSS 두 줄이 같은 오탭을 기하학으로 없앤다. PC(.m-app-frame 밖)는 값이 한 픽셀도 안 바뀐다. */
.m-app-frame { --chat-composer-pb: 16px; }
.m-app-frame .chat-composer-send.btn-sm { height: 44px; min-width: 44px; }

/* ═══ Badge / Chip — v3.1 가독성 ↑ ═══ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 0 7px;
  height: 22px;                  /* was 20 */
  border-radius: var(--r-sm);
  font-size: var(--fs-tiny);     /* 11 — 토큰 적용 */
  font-weight: 700;              /* was 600 — 짧은 한글 라벨 또렷이 */
  white-space: nowrap;
  letter-spacing: -0.01em;
}
.badge-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
/* Status badges */
.badge-등록    { background: var(--s-reg-bg);  color: var(--s-reg-fg); }
.badge-처리중  { background: var(--s-proc-bg); color: var(--s-proc-fg); }
.badge-처리완료{ background: var(--s-done-bg); color: var(--s-done-fg); }
.badge-종결    { background: var(--s-done-bg); color: var(--s-done-fg); }
.badge-미지정  { background: var(--s-none-bg); color: var(--s-none-fg); }
/* Importance — v3.1: Priority(채움 알약)와 형태로 분리. 외곽선 + 색점. */
.badge-imp-상,
.badge-imp-중,
.badge-imp-하 {
  background: transparent;
  border: 1px solid;
  padding: 0 6px 0 5px;
  gap: 4px;
}
.badge-imp-상::before,
.badge-imp-중::before,
.badge-imp-하::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
.badge-imp-상 { color: #B91C1C; border-color: #FECACA; }
.badge-imp-상::before { background: #F43F5E; }
.badge-imp-중 { color: #B45309; border-color: #FCD34D; }
.badge-imp-중::before { background: #F59E0B; }
.badge-imp-하 { color: #4B5563; border-color: #D1D5DB; }
.badge-imp-하::before { background: #9CA3AF; }
/* Priority — v3.1: 채움 알약 (영역 점유 ↑, 채도 ↑ → 시인성) */
.badge-P1 { background: #FEE2E2; color: #991B1B; }
.badge-P2 { background: #FEF3C7; color: #92400E; }
.badge-P3 { background: #DBEAFE; color: #1E40AF; }
.badge-P4 { background: #F3F4F6; color: #6B7280; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  height: 24px;
  border-radius: var(--r-full);
  font-size: var(--fs-caption);
  font-weight: 500;
  background: var(--surface-sub);
  color: var(--text-secondary);
  border: 1px solid var(--border-default);
  cursor: pointer;
  transition: all var(--dur-fast);
  white-space: nowrap;
}
.chip:hover { background: var(--surface-sunken); color: var(--text-primary); }
.chip-active { background: var(--explan-red-light); color: var(--explan-red); border-color: #FFBDBE; }
.chip-close { display: flex; align-items: center; opacity: 0.6; }
.chip-close:hover { opacity: 1; }

/* ═══ Avatar ═══ */
.avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 700;
  flex-shrink: 0;
  background: var(--explan-red-light);
  color: var(--explan-red);
  font-size: 11px;
  width: 28px;
  height: 28px;
}
.avatar-sm { width: 22px; height: 22px; font-size: 9px; }
.avatar-lg { width: 36px; height: 36px; font-size: 14px; }
.avatar-xl { width: 48px; height: 48px; font-size: 18px; }

/* ═══ Card ═══ */
.card {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);    /* v3.1: sm→md로 통일 (모든 페이지 카드) */
  box-shadow: var(--sh-card);
}

/* ═══ Divider ═══ */
.divider { height: 1px; background: var(--border-default); margin: 0; }
.divider-v { width: 1px; background: var(--border-default); align-self: stretch; }

/* ═══ Input ═══ */
.input {
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border-default);
  border-radius: var(--r-sm);
  background: var(--surface-base);
  font-size: var(--fs-body);
  color: var(--text-primary);
  outline: none;
  transition: border-color var(--dur-fast);
  width: 100%;
}
.input:focus { border-color: var(--border-focus); box-shadow: 0 0 0 2px rgba(197,37,47,0.1); }
.input::placeholder { color: var(--text-disabled); }
.input-with-icon { position: relative; }
.input-with-icon .input { padding-left: 32px; }
.input-with-icon .input-icon { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); pointer-events: none; }

/* ═══ Side Rail Icon Item ═══ */
.rail-item {
  width: 40px;
  height: 40px;
  border-radius: var(--r-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
  cursor: pointer;
  position: relative;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.rail-item:hover { background: var(--surface-base); color: var(--text-primary); }  /* v3.1.3: 진한 레일 위 흰색 hover로 명확 */
.rail-item.active { background: var(--explan-red-light); color: var(--explan-red); }
.rail-item .notif-dot {
  position: absolute;
  top: 6px; right: 6px;
  width: 7px; height: 7px;
  background: var(--explan-red);
  border-radius: 50%;
  border: 1.5px solid var(--surface-sunken);  /* v3.1.2: 새 rail bg에 맞춰 */
}

/* ═══ Side Rail — 메뉴명 상시 표기 · 즉시 툴팁 (대표 승인 2026-09-17) ═══
   민원: 「메인메뉴(도메인)가 아이콘만 있어 처음에 어떤 메뉴인지 알기 어렵다」.
   ★라벨 원천은 RAIL_ITEM_META.label 을 shellTx() 로 옮긴 «그 문자열» 하나다 — 하드코딩 사본을
     만들지 않는다(툴팁·aria-label·라벨이 같은 값이라 언어를 바꾸면 셋이 함께 바뀐다). */
.rail-badge { position: absolute; top: 2px; right: 2px; }      /* 배지 좌표는 여기 한 곳 — 모드별로만 옮긴다 */
.rail-labeled .rail-item {
  width: 68px;                /* 레일 72 − 좌우 2 */
  height: auto;
  min-height: 44px;           /* 한 줄 라벨 = 4+20+2+11.5+3 ≈ 41 → 44 가 바닥 · 두 줄은 알아서 큰다 */
  flex-direction: column;
  gap: 2px;
  padding: 4px 1px 3px;
  /* ★칸 자체가 눌리지 않게 — 항목 칸은 세로 flex 이고 overflow:auto 다. flex 는 «스크롤을 내기 전에
     먼저 줄인다» — 두 줄 라벨이 생기는 순간 칸들이 조금씩 눌려 라벨이 잘렸다(실측). */
  flex-shrink: 0;
}
/* ★아이콘이 20px 을 지키게 — 실측에서 두 줄 라벨 칸의 아이콘이 20 → 12px 로 찌그러졌다
   (세로 flex 의 기본 flex-shrink:1 이 min-height 를 맞추려고 그림을 먼저 줄인다).
   글자만 보고 넘기면 «두 줄인 메뉴만 아이콘이 작은» 화면이 된다. */
.rail-labeled .rail-item > svg { flex: 0 0 auto; }
/* 라벨 모드의 아이콘은 68px 칸 한가운데 — 배지도 «아이콘의 오른쪽 위»로 따라 옮긴다
   (칸 오른쪽 끝에 두면 아이콘에서 24px 떨어져 어느 메뉴의 배지인지 안 읽힌다).
   68/2 = 34 가 아이콘 중심, 아이콘 20px → 오른쪽 끝 44 → 배지 왼쪽 40 → right = 68−40−14 = 14. */
.rail-labeled .rail-badge { top: 2px; right: 14px; }
.rail-label {
  font-size: 10px;
  line-height: 1.15;
  font-weight: 600;
  text-align: center;
  letter-spacing: -0.2px;
  max-width: 100%;
  /* ★말줄임을 «쓰지 않는다» — 자르는 대신 줄을 내린다. 한국어는 keep-all 로 낱말을 안 쪼개고
     (「영업·계약」 5자 = 50px < 60px 여유), 번역문(영 「Performance management」)은 빈칸에서
     접혀 두 줄이 된다. text-overflow:ellipsis 를 넣으면 언어를 바꾼 사람만 «…» 을 보게 된다. */
  word-break: keep-all;
  overflow-wrap: anywhere;
  white-space: normal;
}
/* 즉시 툴팁 — 기본 title 속성(브라우저 지연 ~1초)을 떼고 이것으로 갈음한다.
   ★position:fixed 인 이유 = 레일의 항목 칸이 overflow:auto 라, 그 안에 그리면 오른쪽으로
     삐져나온 순간 «잘린다»(overflow 한 축만 visible 로 둘 수 없다 — CSS 규격).
   ★pointer-events:none — 툴팁이 커서를 가로채면 mouseleave 가 튀어 깜빡인다. */
.rail-tip {
  position: fixed;
  z-index: 3000;
  pointer-events: none;
  transform: translateY(-50%);
  max-width: 260px;
  padding: 7px 12px;
  border-radius: var(--r-sm);
  background: var(--text-primary);
  color: var(--surface-base);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: var(--sh-drop);
}

/* ═══ Nav Item (Issue Nav) — v3.1 가독성 ↑ ═══ */
.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  height: 36px;             /* was 34 — 탭 영역 여유 */
  width: 100%;              /* v3.1.3: 항상 패널 폭 채움 — 빠른 필터 라벨 잘림 방지 */
  cursor: pointer;
  font-size: var(--fs-meta);
  color: var(--text-secondary);   /* was tertiary — 더 진하게 */
  border-radius: var(--r-sm);
  position: relative;
  transition: background var(--dur-fast), color var(--dur-fast);
  text-align: left;          /* button 기본 center 정렬 해제 */
}
.nav-item:hover { background: var(--surface-sunken); color: var(--text-primary); }   /* v3.1.3: 레일과 톤 통일 */
.nav-item.active { background: none; background-color: var(--nav-accent-bg, var(--explan-red-light)); color: var(--nav-accent, var(--explan-red)); font-weight: 700; }
.nav-item.active::before {
  content: '';
  position: absolute;
  left: 0; top: 4px; bottom: 4px;
  width: 3px;
  background: var(--nav-accent, var(--explan-red));
  border-radius: 0 2px 2px 0;
}
.nav-badge {
  margin-left: auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  background: var(--surface-sunken);
  color: var(--text-secondary);   /* was tertiary — 숫자 가독성 ↑ */
  border-radius: var(--r-full);
  font-size: var(--fs-tiny);      /* was 10 — 토큰 사용 */
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}
.nav-item.active .nav-badge { background: var(--nav-accent, var(--explan-red)); color: #fff; }

/* ═══ Section header (nav group) — v3.1 가독성 ↑ ═══ */
.nav-section-hd {
  padding: 14px 12px 6px;
  font-size: var(--fs-tiny);
  font-weight: 800;
  color: var(--text-tertiary);  /* was disabled — 그룹 헤더는 식별돼야 함 */
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.nav-section-sep {
  height: 1px;
  background: var(--border-default);
  margin: 6px 12px;
}

/* ═══ Filter bar ═══ */
.filter-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 16px;
  height: 44px;
  border-bottom: 1px solid var(--border-default);
  background: var(--surface-base);
  overflow-x: auto;
  flex-shrink: 0;
}
.filter-bar::-webkit-scrollbar { height: 0; }

/* ═══ Table / List — v3.1 헤더 대비 ↑ ═══ */
/* ═══ Table / List — v3.2 카드형 행 분리 ═══ */
.issue-table {
  width: 100%;
  min-width: 960px;            /* 고정폭 컬럼 합(~700) + 제목 최소폭 보장 → 좁아지면 컬럼 수축 대신 가로 스크롤 */
  border-collapse: separate;
  border-spacing: 0 7px;       /* 행 사이 세로 간격 */
  table-layout: fixed;
  font-size: var(--fs-meta);
  padding: 0 4px;
}
.issue-table th {
  text-align: left;
  font-weight: 700;
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  padding: 0 8px;
  height: 38px;
  background: var(--surface-base);     /* sticky 헤더 — 스크롤 시 본문 비침 방지 */
  position: sticky;
  top: 0;
  z-index: 5;
  box-shadow: inset 0 -1px 0 var(--border-default);
  white-space: nowrap;
  user-select: none;
  letter-spacing: 0.02em;
}
.issue-table th:first-child { padding-left: 16px; }
.issue-table td {
  padding: 0 10px;
  color: var(--text-primary);
  vertical-align: middle;
  background: var(--surface-base);   /* 흰 카드 행 */
  border-top: 1px solid var(--border-subtle, #ECECEE);
  border-bottom: 1px solid var(--border-subtle, #ECECEE);
  font-variant-numeric: tabular-nums;
}
/* 행 양끝 라운드 + 좌우 보더 → 카드처럼 */
.issue-table td:first-child {
  padding-left: 16px;
  border-left: 1px solid var(--border-subtle, #ECECEE);
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
}
.issue-table td:last-child {
  border-right: 1px solid var(--border-subtle, #ECECEE);
  border-top-right-radius: 10px;
  border-bottom-right-radius: 10px;
}
.issue-row { height: 54px; cursor: pointer; transition: box-shadow var(--dur-fast), transform var(--dur-fast); }
.issue-row:hover td { background: var(--surface-base); }
.issue-row:hover { box-shadow: 0 2px 10px rgba(0,0,0,0.07); }
.issue-row:hover td:first-child { box-shadow: none; }
.issue-row.selected td { background: var(--explan-red-light); border-color: #F6C8CB; }
.issue-row.selected td:first-child { box-shadow: inset 3px 0 0 var(--explan-red); }

/* ═══ Quick Preview Panel ═══ */
.qp-panel {
  width: 360px;
  flex-shrink: 0;
  border-left: 1px solid var(--border-default);
  display: flex;
  flex-direction: column;
  background: var(--surface-base);
  transition: width var(--dur-std) var(--ease-out);
}
.qp-panel.collapsed { width: 0; overflow: hidden; border-left: none; }

/* ═══ Detail Panel (slide-in) ═══ */
.detail-panel-overlay {
  position: fixed;               /* 뷰포트 고정 — 페이지 스크롤에 딸려 올라가지 않도록(구: absolute) */
  inset: 0;
  z-index: 50;
  pointer-events: none;
}
.detail-panel-overlay.open { pointer-events: all; }
.detail-panel-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.2);
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-std);
}
.detail-panel-overlay.open .detail-panel-backdrop { opacity: 1; }
.detail-panel {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 1100px;
  max-width: 90%;
  background: var(--surface-base);
  box-shadow: var(--sh-panel);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--dur-slow) var(--ease-out);
}
/* (2026-08-01) 이슈 팝업·퀵프리뷰·대화 서랍 개별 8% 확대는 전역 기본 확대(display-prefs BASE
   1.08 — 전 화면 8% 상향 지시)에 흡수돼 제거됐다. 남기면 1.08×1.08 이중 확대가 된다. */
.detail-panel-overlay.open .detail-panel { transform: translateX(0); }
/* 이슈 카드 별창(#3577 · ?pop=issue) — 창 전체가 카드다.
   레이어(슬라이드인)에서 온 폭 1100·그림자·미끄러짐은 별창에서 «창 안의 창»으로 보인다.
   ★규칙은 여기 한 곳이다 — 인라인 style 로 흩으면 다음 사람이 레이어만 고치고 별창을 놓친다. */
.detail-panel-overlay.ex-issue-popout .detail-panel {
  width: 100%;
  max-width: 100%;
  box-shadow: none;
  transform: none;
  transition: none;
}

/* ═══ Modal ═══ */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal {
  background: var(--surface-base);
  border-radius: var(--r-md);
  box-shadow: var(--sh-modal);
  display: flex;
  flex-direction: column;
}
.modal-hd {
  padding: 16px 20px 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.modal-hd h2 { font-size: var(--fs-h2); font-weight: 700; }
.modal-body { padding: 16px 20px; overflow-y: auto; }
.modal-ft { padding: 12px 20px 16px; border-top: 1px solid var(--border-default); display: flex; gap: 8px; justify-content: flex-end; }

/* ═══ ★팝업 «세로» 규격 — 화면보다 길어져도 버튼은 늘 누를 수 있다 (대표 #815 · 2026-08-20) ═══
   신고: 「작은 휴대폰에선 팝업도 스크롤 필요해 보여요」.
   실측(헤드리스 320×460 · 긴 삭제 확인문): 카드 높이 515px > 화면 460px 이라
     · 위(경고 아이콘·«삭제 확인» 제목)가 top:-28px 로 잘리고
     · «취소/삭제» 버튼 밑선이 468px — **화면(460) 밖이라 누를 수가 없었다**
   그런데 오버레이가 position:fixed 라 스크롤도 안 된다 → 확인도 취소도 못 하는 상태.
   가로(667×375)도 같다(top:-15px). 삭제 확인창이 이 모양이라 가장 위험했다.
   ☞ 규격: ①카드 높이를 뷰포트에 묶고 ②«본문만» 스크롤 ③머리말·버튼줄은 flex 0 0 auto 로 고정.
   ★100dvh 를 뒤에 적어 우선 적용하고 100vh 를 폴백으로 남긴다 — 모바일 주소창이 접혔다 펴져도
     vh 는 안 변해 버튼이 주소창 뒤로 숨는다. dvh 를 모르는 구형 웹뷰는 앞줄(vh)을 쓴다.
   ★overscroll-behavior:contain — 본문 끝까지 굴렸을 때 뒤 페이지가 따라 스크롤되지 않게.
   ★이 세 클래스가 «공용 한 벌»이다. 새 팝업을 만들 때 복붙하지 말고 이 클래스를 붙인다. */
.sys-modal-card {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
}
.sys-modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.sys-modal-fixed { flex: 0 0 auto; }

/* ═══ 공용 프로필 카드(components/shared/ProfileCard.jsx) — 모바일에서 버튼 줄이 잘리지 않게 (#3553 · 2026-09-24) ═══
   신고(관리자 · 스테이지 vc28 실기기): 대화 탭 › 프로필 카드의 하단 연락 버튼 줄(전화·문자·카카오톡·메일·저장)이
   화면 밖으로 잘린다. 지시 = 「밑이 잘려 딱 맞게 · 스크롤 안 생기게」.
   원인(코드 상수 합산): 머리 286 + 정보 줄(소속 3줄 ≈84 · 생일·휴대폰·회사전화·메일 각 ≈54) + 버튼 줄 ≈78 + 1:1 ≈46
     ≈ 722px 인데 카드 한도가 86vh(812 기기 = 698px)라 카드 «전체»가 스크롤돼 버튼 줄이 아래로 밀렸다.
   ☞ 위 .sys-modal-* 규격과 같은 원리: ①카드 높이를 «보이는 화면 − 여백 − 안전 영역»에 묶고 ②사진 머리를 화면 높이에
     맞춰 줄이고(286 상한 · 200 하한) ③그래도 넘치면 «정보 줄만» 스크롤 · 바닥 줄(연락·1:1)은 flex 0 0 auto 로 고정.
   ★머리 높이 식 = 100dvh − 440px − 위·아래 여백(각 max(20px, 안전 영역)) — 440 = 머리 밖 최대 높이(≈436) + 여유 4.
     카드 한도와 «같은 여백»을 빼므로 머리가 하한(200)에 닿기 전까지는 카드가 화면에 «딱 맞게» 들어가 어떤 스크롤도 생기지 않는다
     (375×812 → 머리 286 그대로 · 카드 ≈722 ≤ 한도 772 / 360×740 → 머리 260 · 카드 ≈696 ≤ 700).
   ★원형 사진 지름 = 머리 − 68 (머리 − 이름영역 40 − 위아래 여백 14×2) — 머리가 줄어도 위 14 = 아래 14 가 산다.
   ★PC(웹·PC 앱)는 [data-m-shell] 밖이라 기본 클래스만 탄다 = 종전 인라인 값 그대로(모양 변화 0).
   ★vh 줄을 먼저, dvh 줄을 뒤에 — dvh·clamp 를 모르는 구형 웹뷰는 앞줄(또는 기본 클래스)을 쓴다. */
.ex-pfc-overlay { padding: 20px; }
.ex-pfc-card { max-height: 86vh; overflow-y: auto; }
.ex-pfc-head { height: 286px; }
.ex-pfc-photo { width: 218px; height: 218px; }
[data-m-shell] .ex-pfc-overlay {
  padding: max(20px, env(safe-area-inset-top, 0px)) 20px max(20px, env(safe-area-inset-bottom, 0px));
}
[data-m-shell] .ex-pfc-card {
  display: flex;
  flex-direction: column;
  overflow-y: hidden;
  max-height: calc(100vh - 40px);
  max-height: calc(100dvh - max(20px, env(safe-area-inset-top, 0px)) - max(20px, env(safe-area-inset-bottom, 0px)));
}
[data-m-shell] .ex-pfc-head {
  height: clamp(200px, calc(100vh - 480px), 286px);
  height: clamp(200px, calc(100dvh - 440px - max(20px, env(safe-area-inset-top, 0px)) - max(20px, env(safe-area-inset-bottom, 0px))), 286px);
}
[data-m-shell] .ex-pfc-photo {
  width: calc(clamp(200px, calc(100vh - 480px), 286px) - 68px);
  height: calc(clamp(200px, calc(100vh - 480px), 286px) - 68px);
  width: calc(clamp(200px, calc(100dvh - 440px - max(20px, env(safe-area-inset-top, 0px)) - max(20px, env(safe-area-inset-bottom, 0px))), 286px) - 68px);
  height: calc(clamp(200px, calc(100dvh - 440px - max(20px, env(safe-area-inset-top, 0px)) - max(20px, env(safe-area-inset-bottom, 0px))), 286px) - 68px);
}
[data-m-shell] .ex-pfc-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
[data-m-shell] .ex-pfc-foot { flex: 0 0 auto; }

/* 🟥#3891 ③ — 모바일 v2 셸(MApp standalone)의 새 이슈 등록 창(PC 와 같은 NewIssueDialog 한 벌).
   ★감싸개는 상자를 만들지 않는다(display:contents) — 셸의 가로 flex 줄(가운데 정렬)에 0폭 항목도 안 끼운다.
   ★창 상자(뿌리 fixed 막의 자식)는 인라인 고정 폭(440·560·680·760)이지만 overflow:hidden 인 flex 항목이라 화면 폭까지 줄어든다.
     이 규칙은 그 전제를 «못박는» 상한 한 줄이다(인라인에 max-width 가 없어 우선순위 싸움 0) — 가로 넘침 0 · 전체 폭.
   ★PC(AppShell)는 이 감싸개 밖이라 무접촉(모양 변화 0). */
.m-newissue-host { display: contents; }
.m-newissue-host > div > div { max-width: 100vw; }
/* 🟥#3891 ③-후속(검수 🟡 · PM 1652) — 창이 375 로 줄면 인라인 N칸 격자가 칸당 약 100px(5칸은 59px)로 눌려
   «카테고리 선택…»·라벨이 글자 단위로 쪼개졌다. 감싸개 «아래»에서만 격자를 접는다:
     .nid-mfold-1 = 1열(통합 입력 3칸 넷 · 등록자&상태 2칸 · 체크리스트형 거래처 3칸)
     .nid-mfold-2 = 2열(공개 유형 5칸 — 2열이면 칸 안 약 140px 로 최장 라벨 「우리 회사 전체」·설명 한 줄이 선다)
   ★인라인 style 의 gridTemplateColumns 를 이기려면 !important 가 필요하다(선택자는 className 한 개로 겨눈다 —
     인라인 style 문자열 매칭은 브라우저 직렬화에 기대 깨지기 쉽다). minmax(0,1fr) = 긴 내용이 칸을 밀어 넓히지 않게.
   ★PC(AppShell)는 감싸개 밖이라 이 두 규칙이 닿지 않는다(className 만으로는 모양 변화 0 — 감싸개 없는 규칙 0). */
.m-newissue-host .nid-mfold-1 { grid-template-columns: minmax(0, 1fr) !important; }
.m-newissue-host .nid-mfold-2 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }

/* ═══ Spotlight (⌘K) ═══ */
.spotlight-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 200;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 15vh;
}
.spotlight-box {
  width: 560px;
  background: var(--surface-base);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-modal);
  overflow: hidden;
}
.spotlight-input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border-default);
}
.spotlight-input {
  flex: 1;
  border: none;
  outline: none;
  font-size: 15px;
  background: transparent;
  color: var(--text-primary);
}
.spotlight-result-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px;
  cursor: pointer;
  transition: background var(--dur-fast);
}
.spotlight-result-item:hover,
.spotlight-result-item.focused { background: var(--surface-sub); }

/* ═══ Treatment Card — v3.1 헤더 대비 ↑ ═══ */
.treatment-card {
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface-base);
}
.treatment-card-hd {
  padding: 11px 14px;
  background: var(--surface-sub);
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid var(--border-default);
  font-weight: 700;
  color: var(--text-primary);
}
.treatment-card-body { padding: 12px 14px; }

/* ═══ Approval Progress ═══ */
.approval-steps {
  display: flex;
  align-items: center;
  gap: 0;
  flex-wrap: nowrap;
}
.approval-step {
  display: flex;
  align-items: center;
  gap: 0;
}
.approval-step-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.approval-step-circle {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--border-default);
  background: var(--surface-base);
  position: relative;
}
.approval-step-circle.done  { background: #D1FAE5; border-color: var(--kpi-good); }
.approval-step-circle.curr  { background: var(--explan-red-light); border-color: var(--explan-red); }
.approval-step-circle.wait  { background: var(--surface-sub); border-color: var(--border-default); }
.approval-step-line {
  height: 2px;
  width: 32px;
  background: var(--border-default);
  flex-shrink: 0;
}
.approval-step-line.done { background: var(--kpi-good); }

/* ═══ Toasts ═══ */
.toast-container {
  position: fixed;
  bottom: 24px;
  right: 24px;
  /* ★모든 레이어 위에 둔다(2026-08-11 실측 사고) — 구 300 은 팝오버의 «바깥 클릭 닫기»
     레이어(zIndex 2900)·모달(9000~12000) 아래였다. 그래서 알림 팝오버를 열어 둔 채
     토스트의 «실행취소»를 누르면 클릭이 버튼이 아니라 그 배경 레이어에 맞아,
     팝오버만 닫히고 취소는 실행되지 않았다(알림 2건이 실제로 유실됐다).
     토스트는 액션 버튼을 갖는 순간 «보이는 것 = 누를 수 있는 것»이어야 한다. */
  z-index: 13000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: var(--text-primary);
  color: #fff;
  border-radius: var(--r-sm);
  font-size: var(--fs-meta);
  box-shadow: var(--sh-drop);
  pointer-events: all;
  animation: toast-in 0.2s var(--ease-out);
}
@keyframes toast-in { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
@keyframes ex-spin { to { transform: rotate(360deg); } }
.spin { animation: ex-spin 0.8s linear infinite; }
@media (prefers-reduced-motion: reduce) { .spin { animation-duration: 1.6s; } }

/* 로딩 스켈레톤(공용 Loading variant='skeleton') — shimmer */
.ex-skel {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--surface-sunken) 25%, var(--surface-sub) 37%, var(--surface-sunken) 63%);
  background-size: 400% 100%;
  animation: ex-shimmer 1.2s ease-in-out infinite;
}
@keyframes ex-shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) { .ex-skel { animation-duration: 2.4s; } }
/* 스크린리더 전용(시각적 숨김) */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ═══ Bulk action bar ═══ */
.bulk-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  height: 40px;
  background: #1A1A1A;
  color: #fff;
  flex-shrink: 0;
}
.bulk-bar .btn { color: #fff; background: rgba(255,255,255,0.12); }
.bulk-bar .btn:hover { background: rgba(255,255,255,0.2); }
.bulk-bar .btn:disabled { opacity: 0.3; }

/* ═══ KPI card — v3.1 여유 ↑ ═══ */
.kpi-card {
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);   /* was sm — 더 소프트 */
  padding: 16px 18px;           /* was 14 16 */
  background: var(--surface-card);
  cursor: pointer;
  transition: box-shadow var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
  display: flex;
  flex-direction: column;
  gap: 8px;                     /* was 6 */
  min-width: 0;
}
.kpi-card:hover { box-shadow: var(--sh-card-hover); border-color: var(--border-strong); }
.kpi-status-bar {
  height: 3px;
  border-radius: 2px;
  width: 100%;
  margin-top: 4px;
}

/* ═══ Role Select ═══ */
.role-select-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--surface-sub);
  padding: 40px 20px;
}
.role-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  max-width: 760px;
  width: 100%;
}
.role-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 22px 16px;
  background: var(--surface-base);
  border: 1.5px solid var(--border-default);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: all var(--dur-std);
  text-align: center;
  box-shadow: var(--sh-card);
}
.role-card:hover { border-color: var(--explan-red); box-shadow: 0 4px 16px rgba(197,37,47,0.12); transform: translateY(-2px); }
.role-card-icon {
  width: 52px; height: 52px;
  border-radius: 50%;
  background: var(--explan-red-light);
  display: flex; align-items: center; justify-content: center;
  color: var(--explan-red);
}
.role-card-name { font-size: var(--fs-h3); font-weight: 700; }
.role-card-desc {
  font-size: var(--fs-caption);   /* was tiny — 2단계 ↑ */
  color: var(--text-tertiary);
  line-height: 1.6;
  word-break: keep-all;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  max-width: 100%;
  min-height: 38px;               /* 2줄 공간 확보 */
}

/* ═══ Login (진입 화면) — premium ═══ */
.login-page {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  min-height: 100vh;
  background: var(--surface-base);
}
/* 좌 — 브랜드 패널 (딥 차콜 + 레드 글로우) */
.login-brand {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 80% at 100% 0%, #1c1d24 0%, #121317 55%, #0d0e11 100%);
}
.login-brand-glow {
  position: absolute;
  left: -16%; bottom: -24%;
  width: 62%; height: 68%;
  background: radial-gradient(circle, color-mix(in srgb, var(--brand-primary, var(--explan-red)) 38%, transparent) 0%, transparent 70%);
  filter: blur(6px);
  pointer-events: none;
}
.login-brand-dots {
  position: absolute; inset: 0;
  background-image: radial-gradient(rgba(255,255,255,0.055) 1.1px, transparent 1.1px);
  background-size: 24px 24px;
  -webkit-mask-image: linear-gradient(155deg, #000 0%, transparent 70%);
          mask-image: linear-gradient(155deg, #000 0%, transparent 70%);
  pointer-events: none;
}
.login-brand-inner {
  position: relative;
  z-index: 1;
  height: 100%;
  min-height: 100vh;
  box-sizing: border-box;
  padding: 54px 60px;
  display: flex;
  flex-direction: column;
}
.login-logo-mark {
  width: 48px; height: 48px;
  border-radius: 13px;
  background: var(--brand-primary, var(--explan-red));
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 22px color-mix(in srgb, var(--brand-primary, var(--explan-red)) 40%, transparent), inset 0 1px 0 rgba(255,255,255,0.18);
}
.login-submit .login-logo-tag, .login-logo-tag {
  font-size: 12px; font-weight: 800; letter-spacing: 0.14em; color: #fff;
  background: color-mix(in srgb, var(--brand-primary, var(--explan-red)) 30%, transparent);
  border: 1px solid rgba(255,255,255,0.12);
  padding: 3px 9px; border-radius: 6px;
}
.login-eyebrow {
  font-size: 11px; font-weight: 800; letter-spacing: 0.22em;
  color: rgba(255,255,255,0.42);
  margin-bottom: 18px;
}
.login-headline {
  font-size: 34px; font-weight: 800; color: #fff;
  line-height: 1.3; letter-spacing: -0.025em; margin: 0;
  word-break: keep-all;
}
.login-sub {
  margin: 18px 0 0; font-size: 14.5px; line-height: 1.7;
  color: rgba(255,255,255,0.6); max-width: 360px; word-break: keep-all;
}
.login-features {
  margin-top: 34px; display: flex; flex-direction: column; gap: 16px;
}
.login-feature { display: flex; align-items: center; gap: 13px; }
.login-feature > span:last-child { min-width: 0; }
.login-feature-ic {
  width: 38px; height: 38px; flex-shrink: 0;
  border-radius: 10px;
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.10);
  display: flex; align-items: center; justify-content: center;
}
.login-feature-t { display: block; font-size: 13.5px; font-weight: 700; color: rgba(255,255,255,0.92); word-break: keep-all; }
.login-feature-d { display: block; font-size: 12px; color: rgba(255,255,255,0.5); margin-top: 1px; word-break: keep-all; }
.login-copy { font-size: 11.5px; color: rgba(255,255,255,0.34); margin-top: 40px; }

/* 우 — 폼 영역 */
.login-form-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 28px;
  background: var(--surface-sub);
}
.login-card {
  width: 100%;
  max-width: 380px;
}
.login-card-logo { display: none; }
.login-label {
  display: block;
  font-size: var(--fs-tiny);
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 7px;
  letter-spacing: 0.01em;
}
/* 입력 필드 */
.login-field { position: relative; }
.login-field-ic {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--text-disabled); display: flex; pointer-events: none;
  transition: color var(--dur-fast);
}
.login-input {
  width: 100%; height: 48px; box-sizing: border-box;
  padding: 0 14px 0 40px;
  border: 1.5px solid var(--border-default);
  border-radius: var(--r-md);
  background: var(--surface-base);
  font-family: inherit; font-size: 14.5px; color: var(--text-primary);
  outline: none;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.login-input::placeholder { color: var(--text-disabled); }
.login-input:hover { border-color: var(--border-strong); }
.login-input:focus {
  border-color: var(--brand-primary, var(--explan-red));
  box-shadow: 0 0 0 3.5px color-mix(in srgb, var(--brand-primary, var(--explan-red)) 14%, transparent);
}
.login-field:focus-within .login-field-ic { color: var(--brand-primary, var(--explan-red)); }
.login-eye {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  border: none; background: none; cursor: pointer; padding: 6px;
  color: var(--text-tertiary); display: flex; border-radius: 6px;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.login-eye:hover { color: var(--text-secondary); background: var(--surface-sub); }
/* 옵션 행 */
.login-remember {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: var(--fs-caption); color: var(--text-secondary); white-space: nowrap;
}
.login-remember input { accent-color: var(--explan-red); width: 16px; height: 16px; cursor: pointer; }
.login-secure span { white-space: nowrap; }

/* 빠른 로그인 (개발·데모) */
.login-quick {
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--border-default);
}
.login-quick-hd {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px; margin-bottom: 11px;
}
.login-quick-hd > span:first-child {
  font-size: var(--fs-tiny); font-weight: 800; color: var(--text-secondary);
  letter-spacing: 0.02em; white-space: nowrap;
}
.login-quick-note { font-size: 10px; color: var(--text-disabled); white-space: nowrap; }
.login-quick-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 7px;
}
.login-quick-btn {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 9px 4px 8px;
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
  background: var(--surface-base);
  cursor: pointer; font-family: inherit;
  transition: border-color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast), box-shadow var(--dur-fast);
}
.login-quick-btn:hover {
  border-color: var(--border-strong);
  background: var(--surface-sub);
  transform: translateY(-1px);
  box-shadow: 0 3px 10px -4px rgba(20,22,26,0.18);
}
.login-quick-btn:active { transform: translateY(0); }
.login-quick-tier {
  width: 22px; height: 22px; border-radius: 7px;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 800; font-size: 11px;
  font-family: var(--kpi-num-font, inherit);
}
.login-quick-label {
  font-size: 10.5px; font-weight: 600; color: var(--text-secondary);
  white-space: nowrap;
}
/* 실계정 버튼 — 샘플과 눈으로 구분한다(아이디만 채우고 비밀번호는 직접 입력). */
.login-quick-real {
  font-size: 9px; font-weight: 700; letter-spacing: .02em;
  color: #B45309; background: rgba(180,83,9,.10);
  border-radius: 999px; padding: 1px 6px; margin-top: -1px;
}
@media (max-width: 460px) {
  .login-quick-grid { grid-template-columns: repeat(3, 1fr); }
}
/* ★아주 좁은 폰(320)에서 «페이지가 통째로 넓어지던» 원인 (대표 #803 · 2026-08-20).
   실측: 320 기기인데 레이아웃 뷰포트가 348 로 벌어졌다(=8% 축소). 범인은 이 그리드 하나 —
   .login-quick-label 이 white-space:nowrap 이라 3칸의 min-content 가 270px 로 굳는데,
   320 폰이 카드에 줄 수 있는 폭은 242px(폼 패딩 32 + 카드 패딩 44 + 테두리 2 를 뺀 값)뿐이다.
   ☞ 두 칸으로 내리면 칸당 117px 라 nowrap 라벨(85px)이 그대로 들어간다.
     nowrap 을 푸는 대신 칸 수를 줄인 이유: 375·414 에서 라벨이 두 줄로 접혀 «PC 는 그대로»가
     깨진다(그 폭에서는 지금도 한 줄로 딱 맞는다). */
@media (max-width: 360px) {
  .login-quick-grid { grid-template-columns: repeat(2, 1fr); }
}
/* 버튼 */
.login-submit {
  width: 100%; height: 50px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--brand-primary, var(--explan-red));
  color: var(--brand-on, #fff); border: none; border-radius: var(--r-md);
  font-size: 15px; font-weight: 700; cursor: pointer;
  font-family: inherit; letter-spacing: -0.01em; white-space: nowrap;
  box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--brand-primary, var(--explan-red)) 50%, transparent);
  transition: background var(--dur-fast), transform var(--dur-fast), box-shadow var(--dur-fast);
}
.login-submit:hover { background: var(--brand-primary-dark, var(--explan-red-dark)); box-shadow: 0 10px 26px -6px color-mix(in srgb, var(--brand-primary, var(--explan-red)) 60%, transparent); }
.login-submit:active { transform: translateY(1px); box-shadow: 0 4px 12px -4px color-mix(in srgb, var(--brand-primary, var(--explan-red)) 50%, transparent); }
/* 보안 안내 */
.login-secure {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin-top: 22px;
  font-size: var(--fs-tiny); color: var(--text-disabled);
}
/* 대시보드 카드 내 클릭 가능한 행/셀 개별 하이라이트 */
.dash-hit { margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: 7px; transition: background var(--dur-fast); }
.dash-hit:hover { background: var(--surface-sub); }
.dash-cell { transition: outline var(--dur-fast); }
.dash-cell:hover { outline: 2px solid var(--brand-primary, var(--explan-red)); outline-offset: -2px; }
/* ═══ 분리된 채팅 창(.dm-popout) — 좁은 초기 폭(480px)에서 헤더가 깔끔하게 ═══
   제목이 두 줄로 꺾이고 «멤버 N명»이 줄바꿈되던 것을 정리(2026-08-13 사용자 지시).
   주 창(넓은 화면)은 건드리지 않는다 — 전부 .dm-popout 스코프. */
.dm-popout .dm-room-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* 제목을 감싼 flex 줄도 줄어들 수 있어야 ellipsis 가 걸린다(min-width:auto 기본값 해제) */
.dm-popout div:has(> .dm-room-title) { min-width: 0; }
/* 긴 방 이름 — 마우스를 대면 끝까지 흐른다(«…»로 잘려 확인할 길이 없던 것. title 툴팁과 병행).
   ★안쪽 span 을 움직인다 — 바깥이 overflow:hidden 이라 창이 아니라 «글자»가 이동해야 한다.
   ★hover 가 없는 터치 기기에는 애니메이션이 안 걸린다 — 그쪽은 title/탭 제목으로 확인한다. */
.dm-popout .dm-room-title-in { display: inline-block; will-change: transform; }
.dm-popout .dm-room-title:hover .dm-room-title-in { animation: dm-title-marquee 6s linear 0.4s infinite; }
@keyframes dm-title-marquee {
  0%, 12%   { transform: translateX(0); }
  88%, 100% { transform: translateX(calc(-100% + 180px)); }   /* 헤더 가시폭만큼 남기고 끝까지 */
}
/* ★가시폭을 «재서» 흐른다(2026-08-14) — 위 180px 은 제목이 아이콘과 한 줄을 나눠 쓰던 시절의
   폭이다. 제목이 한 줄을 다 쓰게 된 뒤로 그 값이면 글자가 왼쪽으로 지나쳐 오른쪽이 빈다.
   100cqw = 제목 칸의 실제 폭이라 창을 넓혀도 맞고, min(0px,…) 이라 짧은 이름은 아예 안 움직인다
   (구현은 짧은 이름에도 hover 하면 덜컥거렸다). 컨테이너 쿼리 미지원 브라우저는 위 규칙 그대로. */
@supports (container-type: inline-size) {
  .dm-popout .dm-room-title { container-type: inline-size; }
  @keyframes dm-title-marquee {
    0%, 12%   { transform: translateX(0); }
    88%, 100% { transform: translateX(min(0px, calc(100cqw - 100%))); }
  }
}
.dm-popout .dm-head-acts { gap: 3px !important; }
/* 좁은 폭에서 부가 기능은 접는다 — 이름 변경(연필)은 주 창에서 한다.
   ★!important — 버튼에 인라인 display:flex 가 있어 일반 규칙으로는 못 이긴다. */
.dm-popout [aria-label="이름 변경"] { display: none !important; }

/* ── 분리 창 헤더 = 2행: 제목이 첫 줄을 통째로 쓴다 (2026-08-14 사용자 지시) ──
   구현은 제목·멤버수·아이콘 7개가 480px 한 줄을 나눠 써서 방 이름이 «#SA-내부-공지테스…»로
   잘렸다. 제목에 한 줄을 다 주면 대부분의 방 이름이 잘리지 않고 끝까지 보인다.
   ★핵심은 display:contents — 제목을 감싼 두 겹(dm-head-title/titlerow)을 레이아웃에서
     «투명하게» 만들어, 안쪽 제목·배지·멤버수가 바깥 헤더 행의 직접 항목이 되게 한다.
     그래야 제목만 100% 폭을 갖고 나머지는 둘째 줄로 흐른다(주 창은 이 규칙이 없어 종전 그대로).
   ★높이는 CSS 가 정하고 JS 가 «잰다»(ChatArea 의 ResizeObserver) — 오버레이 패널 top 을
     숫자로 박으면 줄 수가 바뀔 때마다 어긋난다. 여기서 높이를 바꿔도 JS 를 고칠 필요가 없다. */
.dm-popout .dm-chat-head { height: auto !important; padding: 8px 16px !important; }
.dm-popout .dm-head-row { flex-wrap: wrap; row-gap: 6px; }
/* ★!important 필수 — dm-head-titlerow 에는 인라인 display:flex 가 있어 일반 규칙으로는 못 이긴다
   (위 «이름 변경» 버튼과 같은 함정. 안 붙이면 제목이 57px 로 쪼그라들고 줄바꿈도 안 된다 — 실측). */
.dm-popout .dm-head-title,
.dm-popout .dm-head-titlerow { display: contents !important; }
/* ★자식 결합자(>)를 쓰지 말 것 — display:contents 는 «레이아웃»에서만 투명해지고 DOM 은 그대로라
   .dm-head-row > .dm-room-title 은 영영 안 맞는다. 안 맞으면 제목이 flex 아이템으로서 폭 0 이
   돼 통째로 사라진다(실측: w=0). 아래처럼 후손 선택자로 잡는다. */
.dm-popout .dm-head-titlerow .dm-room-title { flex: 0 0 100%; }
/* 둘째 줄 — 왼쪽에 멤버 수·배지, 아이콘 묶음은 오른쪽 끝으로 */
.dm-popout .dm-head-acts { margin-left: auto; }

/* 메모 카드 삭제 버튼 — 평소엔 흐리게(내용을 가리지 않게), 카드에 올리면 드러나고 자신에 올리면 빨갛게.
   ★터치 기기에는 hover 가 없다 — opacity 0 으로 «숨기지» 않는다(그러면 폰에서 영영 못 지운다). */
.memo-del-btn { opacity: 0.35; transition: opacity var(--dur-fast), background var(--dur-fast); }
[class*="memo-card"]:hover .memo-del-btn, .memo-del-btn:focus-visible { opacity: 1; }
.memo-del-btn:hover { opacity: 1; background: rgba(0,0,0,0.07); }
.memo-del-btn:hover svg { stroke: var(--kpi-alert, #DC2626); }
/* 칸반 히스토리 화살표 — 3개 순차 흐름 */
@keyframes kanban-arrow-flow { 0%, 70%, 100% { opacity: .25; } 35% { opacity: 1; } }
.kanban-wf-arrow .kwa1, .kanban-wf-arrow .kwa2, .kanban-wf-arrow .kwa3, .kanban-wf-arrow .kwa4, .kanban-wf-arrow .kwa5 { display:inline-block; animation: kanban-arrow-flow 1.2s ease-in-out infinite; }
.kanban-wf-arrow .kwa2 { animation-delay: .15s; }
.kanban-wf-arrow .kwa3 { animation-delay: .3s; }
.kanban-wf-arrow .kwa4 { animation-delay: .45s; }
.kanban-wf-arrow .kwa5 { animation-delay: .6s; }
@media (prefers-reduced-motion: reduce) { .kanban-wf-arrow .kwa1, .kanban-wf-arrow .kwa2, .kanban-wf-arrow .kwa3, .kanban-wf-arrow .kwa4, .kanban-wf-arrow .kwa5 { animation: none; opacity: 1; } }
/* 미확인 알림 배지 — 맥동(pulse) 효과 */
@keyframes issue-alert-pulse { 0%,100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--explan-red) 55%, transparent); } 70% { box-shadow: 0 0 0 5px transparent; } }
.issue-alert-pulse { animation: issue-alert-pulse 1.4s ease-out infinite; }
@media (prefers-reduced-motion: reduce) { .issue-alert-pulse { animation: none; } }
/* 되돌아가기 칩 — 등장 + 화살표 슬라이드 */
@keyframes return-chip-in { 0% { opacity: 0; transform: translateY(-10px); } 100% { opacity: 1; transform: translateY(0); } }
@keyframes return-chip-arrow { 0%,100% { transform: translateX(0); } 50% { transform: translateX(-3px); } }
.return-chip-anim { animation: return-chip-in .28s ease-out; transition: transform .12s, box-shadow .12s; }
.return-chip-anim .return-chip-arrow { animation: return-chip-arrow 1.1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .return-chip-anim, .return-chip-anim .return-chip-arrow { animation: none; } }
/* 홈 차트 동적 — 스파크라인 그리기·끝점 맥동·도넛 채우기 */
@keyframes spark-draw { from { stroke-dashoffset: 240; } to { stroke-dashoffset: 0; } }
.spark-line-draw { stroke-dasharray: 240; animation: spark-draw 2.4s ease-out forwards; }
@keyframes spark-dot { 0%,100% { r: 2.6; opacity: 1; } 50% { r: 3.6; opacity: .65; } }
.spark-dot-pulse { animation: spark-dot 1.6s ease-in-out infinite; }
@keyframes donut-fill { from { stroke-dashoffset: var(--donut-c); } to { stroke-dashoffset: var(--donut-off); } }
.donut-draw { animation: donut-fill 2.2s ease-out forwards; }
@media (prefers-reduced-motion: reduce) { .spark-line-draw, .spark-dot-pulse, .donut-draw { animation: none; stroke-dashoffset: var(--donut-off, 0); } }
/* 인사 헤더 감성 아이콘 — 부드러운 등장 + 은은한 부유 */
@keyframes greet-pop { 0% { transform: scale(.6) rotate(-12deg); opacity:0; } 100% { transform: scale(1) rotate(0); opacity:1; } }
@keyframes greet-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
.greet-icon { animation: greet-pop .4s ease-out, greet-float 3s ease-in-out infinite .4s; }
@media (prefers-reduced-motion: reduce) { .greet-icon { animation: none; } }
@keyframes login-shake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-7px); }
  40%, 80% { transform: translateX(7px); }
}
.login-shake { animation: login-shake 0.42s; }

/* 좁은 화면 — 브랜드 패널 숨기고 폼만 + 카드화
   ★모바일 폭 확장(2026-09-02 대표 지시 「좌우 여백 안 생기게 · 반응형」) — 380px 고정이
   폰(뷰포트 390~430px)에서 좌우 회색 띠를 만들었다. 좁은 화면에선 카드가 화면을 쓴다. */
@media (max-width: 860px) {
  .login-page { grid-template-columns: 1fr; }
  .login-brand { display: none; }
  .login-card-logo {
    display: flex; align-items: center; gap: 10px;
    margin-bottom: 26px; justify-content: center;
  }
  .login-card {
    background: var(--surface-base);
    border: 1px solid var(--border-default);
    border-radius: var(--r-lg, 16px);
    box-shadow: 0 16px 48px rgba(20,22,26,0.10);
    padding: 36px 30px;
    max-width: 560px;               /* 태블릿·큰 폰 — 380 고정 해제 */
  }
}
@media (max-width: 460px) {
  .login-form-wrap { padding: 14px 10px; }
  .login-card { padding: 26px 18px; max-width: 100%; }
}

/* ── 채팅 메시지 액션(리액션·답장·수정·삭제) ──────────────────────────────
   ★기본은 숨김, 그 메시지에 마우스를 올렸을 때만 보인다(2026-07-28 지시).
     상시 노출이면 메시지마다 아이콘 4개가 늘 떠 있어 대화 자체가 안 읽힌다.
   ★리액션 피커를 연 동안(.chat-actions-open)은 마우스가 벗어나도 유지한다 —
     안 그러면 피커를 고르러 가는 순간 액션이 사라져 클릭이 취소된다.
   ★터치 기기는 hover 가 없다 — coarse 포인터에서는 항상 보이게 둔다. */
.chat-actions { opacity: 0; transition: opacity .12s ease; }
.bubble-line:hover .chat-actions,
.chat-actions.chat-actions-open,
.chat-actions:focus-within { opacity: 1; }
@media (hover: none), (pointer: coarse) {
  .chat-actions { opacity: 1; }
}


/* To-Do 행 삭제 버튼 — 마우스를 올렸을 때만 보인다(평소 목록은 조용해야 한다) */
.dash-todo-row:hover .todo-del { opacity: 1 !important; }
.todo-del:hover { color: var(--kpi-alert, #BE123C) !important; }
/* ★터치 기기에는 hover 가 없다 — 항상 보인다(2026-08-06 신고).
   구: 버튼이 opacity:0 인 채로 «자리와 클릭»만 살아 있어, 모바일에서 안 보이는데 눌리면
   삭제가 되는 상태였다(보이지 않는 파괴 버튼). 위 .chat-actions 와 같은 처방. */
@media (hover: none), (pointer: coarse) {
  .dash-todo-row .todo-del { opacity: 1 !important; }
  [id^="todo-menu-"] { opacity: 1 !important; }   /* TodoWidget 행 «더보기»(삭제 포함) — 같은 결함 */
}

/* ── 할 일 행 «메타 ↔ 액션» 자리 교대 (v7 리디자인 · 2026-08-13) ─────────────────
   평소엔 태그·우선순위·날짜(메타)를 보여 주고, 마우스를 올리면 그 자리에 긴급·더보기(액션)가 뜬다.
   ★JS 의 onMouseEnter 상태로 하지 않고 CSS 로 두는 이유는 «터치» 때문이다 —
     터치 기기엔 hover 가 없어 상태가 영영 false 로 남고, 액션이 보이지도 눌리지도 않는다
     (위 .todo-del 이 2026-08-06 에 똑같이 당한 결함). 아래 coarse 분기에서 «둘 다» 보여 준다. */
.todo-row .todo-acts { opacity: 0; pointer-events: none; transition: opacity .12s; }
.todo-row .todo-meta { opacity: 1; transition: opacity .12s; }
.todo-row:hover .todo-acts,
.todo-row.is-menu-open .todo-acts { opacity: 1; pointer-events: auto; }
.todo-row:hover .todo-meta,
.todo-row.is-menu-open .todo-meta { opacity: 0; pointer-events: none; }
.todo-row:hover, .todo-row.is-menu-open { background: var(--surface-hover); }
@media (hover: none), (pointer: coarse) {
  /* 터치 — 겹쳐 놓지 않고 «나란히» 흐르게 둔다(absolute 해제). 둘 다 항상 보인다. */
  .todo-row .todo-meta { opacity: 1 !important; pointer-events: auto !important; }
  .todo-row .todo-acts { opacity: 1 !important; pointer-events: auto !important;
    position: static !important; transform: none !important; }
}

/* ── 시스템관리 보기 모드 잠금(SysEditGuard, 2026-08-01) ─────────────────
   ★잠그는 것은 '설정을 바꾸는 것'이지 '보는 것'이 아니다(2026-08-02 수정).
     구 규칙은 button/input/select 를 통째로 죽여 검색·필터·탭·목록 선택까지 막았고,
     보기 모드에서 화면이 통째로 먹통이라는 신고가 나왔다(로그인 사용자 화면: 검색 1 + 필터 버튼 9 사망).
     조회 전용 컨트롤은 `data-sys-view` 로 표시해 잠금에서 제외한다 — 표시는 SysEditGuard 규약. */
[data-sys-lock="1"] button:not([data-sys-view]),
[data-sys-lock="1"] input:not([data-sys-view]),
[data-sys-lock="1"] select:not([data-sys-view]),
[data-sys-lock="1"] textarea:not([data-sys-view]),
[data-sys-lock="1"] a:not([data-sys-view]),
[data-sys-lock="1"] [role="button"]:not([data-sys-view]),
[data-sys-lock="1"] [contenteditable]:not([data-sys-view]),
[data-sys-lock="1"] [draggable="true"]:not([data-sys-view]) {
  pointer-events: none !important;
}
/* 조회 전용 영역 — 검색·필터·목록 선택 등 '읽기'는 보기 모드에서도 살아 있어야 한다.
   컨테이너에 한 번만 표시하면 그 안의 컨트롤이 전부 풀린다. */
[data-sys-lock="1"] [data-sys-view],
[data-sys-lock="1"] [data-sys-view] button,
[data-sys-lock="1"] [data-sys-view] input,
[data-sys-lock="1"] [data-sys-view] select,
[data-sys-lock="1"] [data-sys-view] textarea,
[data-sys-lock="1"] [data-sys-view] a,
[data-sys-lock="1"] [data-sys-view] [role="button"] {
  pointer-events: auto !important;
}
/* ★모달 닫기는 절대 잠그지 않는다(2026-08-03 신고: 카탈로그 상세를 열면 닫을 수가 없었다).
   잠글 대상은 '설정을 바꾸는 것'이다 — 닫기·취소는 아무것도 바꾸지 않는데, 잠기면 사용자가
   모달에 갇힌다(배경 클릭을 모르면 새로고침밖에 없다). 닫기·취소 컨트롤은 data-sys-close 로 표시한다. */
/*   ★선택자 구체성 주의 — 잠금 규칙이 `[data-sys-lock] button:not([data-sys-view])` 라
     `[data-sys-lock] [data-sys-close]` 만으로는 진다(같은 !important, 잠금 쪽이 더 구체적).
     그래서 요소 이름까지 붙여 확실히 이긴다. */
[data-sys-lock="1"] button[data-sys-close]:not([data-sys-view]),
[data-sys-lock="1"] a[data-sys-close]:not([data-sys-view]),
[data-sys-lock="1"] [role="button"][data-sys-close]:not([data-sys-view]),
[data-sys-lock="1"] [data-sys-close] button:not([data-sys-view]),
[data-sys-lock="1"] [data-sys-close] [role="button"]:not([data-sys-view]) {
  pointer-events: auto !important;
}
/* ★탭 전환은 조회다(2026-09-21 관리자 신고 — 자격 화면 S-4: [수정] 전에는 탭·목록이 통째로 죽었다).
   보기 모드의 원칙 = «읽기 전부 허용 · 쓰기만 잠금» — 탭은 화면 상태만 바꾸고 아무것도 저장하지 않는다.
   role="tab"(또는 tablist 안 버튼)을 쓴 표준 탭은 표시 없이도 풀어 준다.
   선택자 구체성 = 위 close 와 같은 이유로 요소 이름까지 붙여 잠금 규칙과 동률을 만들고, 뒤에 둬서 이긴다. */
[data-sys-lock="1"] button[role="tab"],
[data-sys-lock="1"] a[role="tab"],
[data-sys-lock="1"] [role="tablist"] button {
  pointer-events: auto !important;
}
/* 검색창 자동 예외 — 표시자를 붙이는 걸 잊어도 검색은 살아 있게 한다.
   검색 입력은 화면 상태만 바꾸고 설정을 저장하지 않으므로 잠글 이유가 없다.
   (전수 점검 2026-08-02: 화면마다 자체 구현한 검색창이 6곳 더 있었다 — 하나씩 붙이면 반드시 빠진다) */
[data-sys-lock="1"] input[type="search"],
[data-sys-lock="1"] input[placeholder*="검색"] {
  pointer-events: auto !important;
}

/* ── 본문 HTML 표시(BodyHtml) — 이슈 내용·처리 내용 ─────────────────────────
   ★전역에 둔다: 예전엔 RichBodyEditor 안의 <style> 에만 있어서, 에디터가 없는
     읽기 전용 화면(모바일 퀵뷰·이슈 카드)에서는 규칙이 통째로 빠졌다. */
.body-html { white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; }
.body-html .mention-tag { background: #EEF2FF; color: #4338CA; border-radius: 4px; padding: 0 4px; font-weight: 600; }
.body-html img.body-img {
  max-width: 100%; max-height: 320px; border-radius: var(--r-sm);
  border: 1px solid var(--border-default); display: inline-block; margin: 4px 0; cursor: zoom-in;
}
/* 레거시 본문의 표 — 원본 style 은 새니타이즈에서 제거되므로 여기서 최소 서식을 준다.
   넓은 표는 잘라내지 않고 «가로 스크롤» 시킨다(모바일에서 페이지가 밀리지 않게). */
.body-html table {
  border-collapse: collapse; margin: 6px 0; max-width: 100%;
  font-size: 0.95em; white-space: normal;
}
.body-html th, .body-html td {
  border: 1px solid var(--border-default); padding: 5px 8px; text-align: left; vertical-align: top;
}
.body-html th { background: var(--surface-sunken, #F1F5F9); font-weight: 700; }
/* 본문 목록 — base.css 의 리셋(ul,ol{list-style:none})을 여기서만 되돌린다.
   되돌리지 않으면 글머리 기호·번호가 통째로 사라져 «그냥 들여쓴 문단»으로 보인다. */
.body-html ul, .body-html ol { padding-left: 22px; margin: 4px 0; white-space: normal; }
.body-html ul { list-style: disc; }
.body-html ol { list-style: decimal; }
.body-html li { margin: 2px 0; }
.body-html .body-table-wrap { max-width: 100%; overflow-x: auto; }
/* 본문 링크 — 자동 링크(평문 URL)와 레거시 <a href> 모두 이 규칙을 탄다. 새 창으로 열린다. */
.body-html a { color: var(--kpi-info, #2563EB); text-decoration: underline; word-break: break-all; }
.body-html a:hover { opacity: .8; }

/* ══ 🟥#3619ⓑ 메모 «카드» 본문 — 줄이 서로 겹쳐 그려지던 것을 고친다 ═══════════════════════
   근거 = 관리자 PC 스크린샷(2026-09-16 · 「저장된 메모 22개」 카드형): 긴 본문(사업자번호·대표자·
   주소) 카드에서 마지막 본문 줄과 그 다음 URL 줄이 **같은 세로 자리에 포개져** 읽을 수 없었다.
   짧은 본문 카드는 멀쩡했다.

   ★무엇이 겹쳤나 = 서로 다른 «컴포넌트» 둘이 아니다. 한 상자(.body-html) 안의 **이웃한 줄 블록
     둘**이다 — 편집기(contentEditable · execCommand)는 줄마다 <div> 로 감싸 저장하고
     (RichBodyEditor.jsx:107-124 의 실측 주석), 평문 URL 은 그 줄 «안»에서 <a> 가 된다
     (EX_LINKIFY.autolinkDom · RichBodyEditor.jsx:103-105). 따로 덧그리는 링크 줄은 없다.

   ★왜 겹쳤나 — 세 겹이다(하나만 고치면 다른 판에서 또 난다):
     ⒜ `-webkit-line-clamp` 를 **자식이 블록인 상자**에 걸었다. 그 속성은 상자 «자신의» 인라인
        서식 문맥(line box)만 자른다 — 자식이 줄마다 <div> 면 자를 대상이 없어, 클램프가 사실상
        안 걸린 채 상자 높이만 바깥(flex)이 정하게 된다.
     ⒝ 그 상자는 카드(세로 flex)의 `flex:1` 아이템인데 **min-height 가 auto** 였다 — flex 아이템은
        내용보다 작게 줄지 않는 것이 기본이라, 줄일 수 없는 높이와 `overflow:hidden` 이 맞붙었다.
     ⒞ 그래서 «몇 줄까지»를 말하는 **결정적인 높이 상한이 어디에도 없었다**.
   ★어떻게 고치나 — 셋을 다 막는다:
     ① 줄 블록을 «한 인라인 흐름»으로 되돌린다(아래 > div/> p 규칙) ⇒ 클램프가 제 일을 하고
        마지막 줄 끝에 «…» 가 붙는다.
     ② `min-height: 0` ⇒ flex 아이템이 실제로 줄어든다.
     ③ `max-height` 기하 상한 ⇒ ①이 못 먹는 판(표·목록이 섞인 본문, 구형 웹뷰)에서도 상자가
        N 줄 너머로는 **그려질 수 없다**. 잘리되 겹치지는 않는다.
   ★줄 수는 화면이 정한다 — `--memo-card-clamp`(기본 3)를 카드가 인라인으로 넘긴다.
     ★목록형·카드형·자유형·대시보드 위젯·모바일 홈이 **같은 이 조각 한 벌**을 쓴다(복붙 0). */
.memo-card-body {
  display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;
  -webkit-line-clamp: var(--memo-card-clamp, 3);
  line-height: var(--memo-card-lh, 1.6);
  max-height: calc(var(--memo-card-clamp, 3) * var(--memo-card-lh, 1.6) * 1em);
  min-height: 0;
}
/* 줄마다 <div>(또는 <p>) 인 본문을 인라인으로 되돌리고, 줄바꿈은 ::after 의 개행 글자가 맡는다.
   ★`.body-html` 이 이미 `white-space: pre-wrap` 이라 '\A' 가 그대로 «줄바꿈»으로 산다.
   ★마지막 줄 뒤에는 개행을 붙이지 않는다 — 빈 줄 하나가 클램프 한 줄을 통째로 먹는다. */
.memo-card-body > div, .memo-card-body > p { display: inline; margin: 0; }
/*   ★white-space 를 ::after 에 «직접» 준다 — 물려받기에 기대지 않는다. HTML 본문은 `.body-html`
     이 pre-wrap 을 주지만, 구 평문(마크다운) 갈래에는 그 class 가 없다. 안 주면 그 갈래에서
     개행 글자가 «공백 한 칸»으로 접혀 줄이 통째로 이어 붙는다. */
.memo-card-body > div::after, .memo-card-body > p::after { content: '\A'; white-space: pre-wrap; }
.memo-card-body > div:last-child::after, .memo-card-body > p:last-child::after { content: none; }
/* 카드 미리보기의 이미지는 «글줄 높이»로 눕힌다 — 320px 짜리가 들어오면 카드가 통째로 터진다. */
.memo-card-body img.body-img { max-height: 1.4em; margin: 0; vertical-align: text-bottom; }
/* 표·목록은 인라인으로 되돌리지 않는다(뜻이 깨진다) — 위 ③ 기하 상한이 잘라 준다. */
.memo-card-body > ul, .memo-card-body > ol, .memo-card-body > table, .memo-card-body > blockquote { margin: 0; }

/* ── 평문 속 자동 링크(window.exLinkText / EX_LINKIFY.render) ─────────────────
   대화 말풍선·댓글·메모·결재 의견·AI 답변처럼 «평문을 그대로 찍던» 자리에서 쓴다(대표 #528).
   ★긴 URL 이 말풍선·카드를 밀어 레이아웃을 깨지 않게 여기서 줄바꿈을 보장한다 —
     부모가 white-space:pre-wrap 이어도 URL 은 공백이 없어 한 줄로 뻗는다. */
.ex-link {
  color: var(--kpi-info, #2563EB); text-decoration: underline;
  overflow-wrap: anywhere; word-break: break-all;
}
.ex-link:hover { opacity: .8; }
/* 배경색이 있는 자리(말풍선 등) — 파란 링크색은 진한 배경에서 읽히지 않는다.
   글자색을 물려받고 밑줄·굵기로 «링크»를 알린다. */
.ex-link--inherit { color: inherit; font-weight: 600; text-decoration: underline; }

/* ═══ 이력의 «이동» 화살표 (2026-08-08 지시 — 우측으로 가는 동적인 느낌) ═══ */
/*   사람 → 사람(전달·배정)처럼 «옮겨 갔다»를 말하는 자리에 쓴다. 정지한 → 는 그냥 기호로 읽힌다.
     살짝 오른쪽으로 밀었다 돌아오게 해 방향이 먼저 눈에 들어오게 한다.
     ★폭·자리는 그대로다(transform 만 움직인다) — 글자가 밀리면 읽기가 더 나빠진다. */
.ex-hist-arrow {
  display: inline-block;
  animation: exHistArrow 1.5s ease-in-out infinite;
  will-change: transform;
}
@keyframes exHistArrow {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(3px); }
}
/* 움직임을 줄여 달라는 설정을 존중한다 — 이 표시는 장식이지 정보가 아니다. */
@media (prefers-reduced-motion: reduce) {
  .ex-hist-arrow { animation: none; }
}

/* ── iOS 입력창 자동 확대 방지 (D20 · 2026-08-18 · T7) ──────────────────────────
   iOS Safari 는 **입력 요소의 폰트가 16px 미만이면** 포커스 시 페이지를 확대하고
   되돌리지 않는다. 모바일 셸은 그 조건에 정면으로 걸려 있었다(정적 실측):
     · .input 기본이 --fs-body=15px 로 이미 16px 미만
     · 모바일 화면들이 인라인으로 12.5~13px 을 21곳 더 지정
     · base.css 의 `input,textarea,select{font-size:inherit}` 라 16px 로 올리는 규칙이 없음
     · viewport 에 maximum-scale·user-scalable 없음(접근성상 옳다 — 그래서 확대가 실제로 난다)
   ☞ 결과: 검색창·입력칸을 탭할 때마다 화면이 확대된다.

   ★«입력 요소만» 16px 로 올린다 — 표시 텍스트·타이포는 건드리지 않는다.
   ★!important 인 이유: 화면들이 인라인 style 로 12.5~13px 을 주므로 그것을 이겨야 한다.
   ★.chat-editor(contenteditable) 도 넣는다 — 대화·이슈채팅 입력창이고 --fs-caption(13.8px)
     이라 같은 조건에 걸린다. contenteditable 도 iOS 확대 대상이다.
   ★범위는 모바일 셸(.m-app-frame) 안으로 한정 — 데스크톱 콘솔의 조밀한 폼은 그대로 둔다.
     실기기와 «모바일 미리보기»가 같은 프레임을 쓰므로 미리보기도 실기기와 같게 보인다.
   ※ /sv/ · /owner/ 스탠드얼론 앱은 이 규칙 밖 — app-shell.css 에 같은 모양의 규칙이 따로 있다(D52 · .oa-frame·.sv-frame). */
.m-app-frame input,
.m-app-frame select,
.m-app-frame textarea,
.m-app-frame .chat-editor { font-size: 16px !important; }

/* ═══ 🟥#3578 메모 하단 도구 재설계 — 미니바 + 아코디언 (2026-09-16) ═══════════════════════════
   근거 = 관리자 확정 «★추가 확정 3» 여섯 요건 + 승인 시안(airepo/시안_3578_하단도구_2026-09-15.*).
   부품 = components/common/MemoToolbar.jsx (자리) · 버튼의 손은 MemoWidget 이 그대로 쥔다.

   ★치수는 여기 한 곳에 산다 — JSX 에 인라인 치수를 두지 않는다. 폰/PC 는 `.m-app-frame`
     하나로만 갈린다(폰 = 모바일 셸 · 실기기와 390px 미리보기가 같은 클래스를 쓴다).
     시안 §3 «새 크기 (폰 / PC)»: 평소 줄 48/36 · Aa 56x48/36 · 미니바 44/36 · 아코디언 44/32.
     ☞ MemoToolbar.jsx 의 MEMO_TOOL_MIN 표와 **같은 값**이어야 한다(자 ⓓ가 둘을 맞대 본다).

   ★색은 토큰 여섯 + 상태 둘만 쓴다(시안 ★보정). 이 블록에 hex 리터럴은 0 이다.
     평소 = surface-base · surface-sunken · border-default · text-tertiary · text-secondary · text-primary
     상태 = border-strong(선택·활성) · kpi-alert(삭제 · 녹음 중)  ← 브랜드색(--explan-red)은 쓰지 않는다.

   ★«켜짐»은 색이 아니라 «면»으로 말한다 — surface-sunken 칠 + border-strong 테두리 +
     text-primary 글자(대비 17.72:1). 회색 칸 위에 회색 글자(4.12:1)를 얹는 조합은 만들지 않는다. */

/* 고정 요소(안내 더미 · 아코디언)가 본문에서 가져갈 수 있는 높이의 «몫» — 한 곳에서 정한다.
   #3578 가시성 ③ 이 안내 더미에 둔 30vh 와 **같은 축**이고, 아코디언이 그 축에 얹혔다.
   숫자를 두 곳에 적지 않으려고 변수로 뽑았다(MemoRecorder.jsx noteStack 도 이 변수를 읽는다). */
:root { --memo-fixed-maxh: 30vh; }

/* ── 평소 노출 줄 ─────────────────────────────────────────────────────────────── */
.memo-tool-row {
  display: flex; align-items: center; gap: 10px; row-gap: 10px; flex-wrap: wrap;
  padding: 10px 16px; border-top: 1px solid var(--border-default);
  background: var(--surface-base); flex-shrink: 0;
}
.memo-tool-row-end { margin-left: auto; display: flex; align-items: center; gap: 6px; }

/* ═══ 🟥#3595 컴팩트 — 첨부·녹음을 «Aa 급» 한 칸으로 (2026-09-16) ══════════════════════════
   근거 = 관리자 지시(전략기획실 #597): 「하단 첨부 녹음 영역이 너무 크잖아 버튼은 두개뿐인데
   눌렀을때 뭐가나오는 구조라면 Aa 버튼처럼 해도되는거고 레이아웃 정리좀 잘해보라고해」.

   ★고친 것은 «자리»지 «손»이 아니다 — 첨부는 눌러서 파일 고르기(at.openPicker), 녹음은 눌러서
     녹음 시작(pickMode)이 그대로다. 줄어든 것은 **가로 글자와 세로 줄 수**뿐이다.

   ★무엇이 컸나(실측 · 폰 기준) — 평소 줄이 «세 줄»이었다:
       ①[첨부] 48  ②MemoVoiceButton 뿌리(width:100% 라 제 줄을 통째로 차지) 48+4
       ③[Aa] 48   + 줄 간격 6×2 + 세로 여백 6×2  =  172px
     세 버튼이 한 줄에 들어가지 못한 이유는 버튼 크기가 아니라 **뿌리 div 의 width:100%** 였다.
     그래서 여기서 그 뿌리를 «펼친다»(display: contents) — 자식(녹음 버튼·안내 칸)이 이 줄의
     직계가 되어, 한 줄짜리 버튼들은 한 줄에 서고 가로 한 줄짜리 안내만 아래로 내려간다.
       ⇒ 기본 상태 = 48 + 여백 12 = 60px (172 → 60 · **112px 회수** ≈ 본문 4줄)
       ⇒ PC 는 36 + 12 = 48px (136 → 48 · 88px 회수)
   ★구형 웹뷰 폴백 = @supports 밖의 선언(display:flex + width:100%)이 종전 모양 그대로다.
     새 규칙이 안 먹는 브라우저는 «오늘과 똑같이» 보인다(깨지지 않는다). */
.memo-voice-flat { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; row-gap: 4px; width: 100%; min-width: 0; }
/* 받아쓰기·녹음 «묶음»(memo-tool-pair)도 같은 이유로 펼친다 — 폴백(구형 웹뷰)은 두 몫짜리 flex 상자. */
.memo-tool-row .memo-tool-pair { display: inline-flex; align-items: center; gap: 8px; flex: 2 1 0; min-width: 0; }
@supports (display: contents) {
  .memo-tool-row > .memo-voice-flat { display: contents; }
  .memo-tool-row .memo-tool-pair { display: contents; }
}
/* 가로 한 줄짜리(안내 더미 · 연장 안내)는 컨트롤 줄 «아래»로 보낸다 — 그래야 [Aa]가 늘 첫 줄
   오른쪽 끝에 남는다. 값이 같은 것끼리는 DOM 차례대로다(첨부 → 녹음 → Aa).
   ★🟥선택자가 «손자»를 가리켜야 한다 — display:contents 는 **상자**를 없앨 뿐 DOM 을 바꾸지 않는다.
     그래서 `.memo-tool-row > [data-memo-notes]` 는 영원히 안 맞는다(뿌리가 DOM 에 그대로 있다).
     «누가 flex 아이템인가»(레이아웃)와 «누가 자식인가»(선택자)는 다른 물음이다.
   ★[role="status"] 는 **뿌리의 직계만** 집는다 — 안내 칸 «안»에도 role=status 가 있어서
     범위를 안 좁히면 그 칸 내부 차례(경고 순서)까지 뒤섞인다. */
.memo-tool-row > .memo-voice-flat > [data-memo-notes],
.memo-tool-row > .memo-voice-flat > [role="status"] { order: 1; }
/* 안내가 하나도 없으면 빈 칸이 줄을 하나 먹는다(높이 0 + 줄 간격) — 비었으면 아예 없앤다. */
.memo-tool-row > .memo-voice-flat > [data-memo-notes]:empty { display: none; }

/* 컴팩트 버튼 — 평소 도구 줄의 «세 칸»(첨부 · 받아쓰기 · 녹음)이 이 옷을 입는다.
   ★세로는 그 줄의 버튼 규칙(.memo-tool-row button)이 주고, 가로만 여기서 준다 — 값이 두 곳이
     되지 않게 하는 종전 규약 그대로다(자 memo-toolbar-redesign ⓓ 가 그것을 지킨다). */
.memo-tool-compact {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 2px; padding: 0 2px;
}

/* ══ 🟥#3619ⓐ 세로 배치 — «아이콘 위 · 글씨 아래» (관리자 실물 확인 2026-09-16 21:0x) ═══════
   관리자 원문: 「아이콘 하단 네모박스 안에 첨부·받아쓰기·녹음 글씨」.
   ★1차(#3595·#3619④)는 «아이콘만»이었다 — 이름이 aria-label·title 에만 살아 있어서, 마우스를
     얹지 않는 폰에서는 어느 것이 무엇인지 볼 방법이 없었다. 이제 라벨이 화면에 선다.
   ★«공용 클래스 하나»다 — 첨부(MemoWidget)·받아쓰기·녹음(MemoRecorder) 셋이 같은 이 규칙을
     탄다(복붙 0). 셋 중 하나만 모양이 달라지는 날이 오지 않게 하는 자리다.
   ★치수는 여기에 적지 않는다 — 가로는 .memo-tool-compact, 세로는 .memo-tool-row button 이
     준다(위 규약). 이 블록은 «모양»(방향·간격·라벨 글자)만 말한다. */
.memo-tool-stack {
  flex-direction: column; gap: 3px; padding: 4px 6px; line-height: 1.15;
}
/* 🟥#3619 디자인 정돈(관리자 실물 대조 2026-09-17 08:5x 「시안과 많이 다르다」) — 관리자 확정 시안 §1 ③ 의
   «네모 상자» 꼴: 테두리 1px(강조 경계) · 모서리 8 · 남는 폭을 칸들이 «고르게» 나눠 가진다(flex 1).
   ★ghost 버튼(테두리 0 · 작은 글자)을 그대로 입고 있던 것이 «시안과 다른» 뿌리였다 — 손·이름·구조는
     그대로고 «옷»만 시안 것으로 갈아입는다. 치수 바닥(min-width/height)은 종전 규약대로 위·아래 규칙이 준다. */
.memo-tool-row .memo-tool-stack {
  /* flex-basis 0 = 칸 폭이 «고르다»(시안 flex:1) — 라벨 길이 차이로 첨부만 넓어지던 것을 없앤다.
     바닥(min-width 60/64)이 라벨보다 넓어 basis 0 이어도 「받아쓰기」가 잘리지 않는다. */
  flex: 1 1 0; max-width: 140px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface-base); color: var(--text-primary);
}
.memo-tool-row .memo-tool-stack:hover { background: var(--surface-sunken); }
/* 넷째 [요약](조건부 · MemoMinutesButton)은 시안 §1 ③ 대로 바닥을 살짝 가라앉힌다 — «AI 가 하는 일»을
   손으로 하는 셋과 눈으로 가른다. 그 버튼만 aria-busy 를 가진다(진행 표시) — 그 속성이 곧 표식이다. */
.memo-tool-row .memo-tool-stack[aria-busy] { background: var(--surface-sunken); }
.memo-tool-row .memo-tool-stack:disabled { opacity: 0.45; }
/* 🟥#3619 재작업(검수 #99 · PM #101 ②) — 녹음·받아쓰기 «중»에는 시안 §2·§3 대로 [끝내기]+[취소] 둘만 선다.
   판정 손잡이 = 그 줄 안의 [끝내기](.memo-tool-stop) — 상태를 부모로 올리지 않고 CSS :has 로 형제(첨부·요약)를 접는다.
   :has 가 없는 구형 웹뷰는 종전(넷 다 보임)으로 남는다 — 깨지지 않는 폴백. */
.memo-tool-row:has(.memo-tool-stop) .memo-tool-stack { display: none; }
/* 진행 배지(녹음 중 · 받아쓰는 중 · 전사 중)의 «인라인» 자리 — 서랍(panelSlot)에서는 화면 고정 층이 아니라
   도구 줄 «맨 위 한 줄»에 선다(order -1 · width 100%). 종전 고정 배지가 넓어진 도구 줄을 덮어 [취소]·[요약] 클릭을
   가로챈 🟥(검수 #99)의 수리 — 시안 §2·§3 «배지는 본문 위(도구 위)». */
.memo-tool-row .memo-rec-badge-inline {
  flex: 3 1 160px; min-width: 0; max-width: 100%; overflow: hidden; white-space: nowrap;   /* [끝내기] 왼쪽 같은 줄 · 남는 폭의 3몫 · 넘치면 뒤가 잘린다 */
}
/* 4차(검수 #121) — 배지는 role="status" 라 위 «안내 줄은 아래로(order 1)» 규칙을 함께 받아 오른쪽 끝에 섰다.
   배지 전용 선택자(같은 깊이 · 더 높은 특이성)로 «맨 앞(order -1)»을 되돌린다 — 안내 줄 규칙은 그대로다. */
.memo-tool-row > .memo-voice-flat > .memo-rec-badge-inline[role="status"],
.memo-tool-row > .memo-rec-badge-inline[role="status"] { order: -1; }
/* [끝내기]는 글자 폭만(남는 폭은 배지 몫) — 종전 flex 1 1 auto 가 배지와 폭을 나눠 배지 글이 잘렸다 */
.memo-tool-row .memo-tool-stop { flex: 0 1 auto; }
/* 녹음 중 = [끝내기](채움 · 남는 폭 전부) + [취소](테두리 상자) — 시안 §2·§3 */
.memo-tool-row .memo-tool-stop {
  flex: 1 1 auto; border-radius: var(--r-sm); gap: 6px; font-size: var(--fs-caption);
}
.memo-tool-row .memo-tool-cancel {
  border: 1px solid var(--border-strong); border-radius: var(--r-sm); padding: 0 12px;
  color: var(--text-secondary); font-size: var(--fs-caption);
}
/* 라벨 = 토큰 최소치(--fs-tiny) — «11px 급»의 정본 값이다(tokens.css:167 · 라벨/배지 최소치).
   ★nowrap — 「받아쓰기」가 두 줄로 접히면 아이콘이 위로 밀려 네 칸의 높이가 갈린다.
     상태말(«마이크 여는 중…»·«저장 중…»·«정리 중…»)이 들어오면 «가로로만» 넓어진다(높이 불변). */
.memo-tool-stack-label {
  font-size: var(--fs-tiny); font-weight: 500; white-space: nowrap; letter-spacing: -0.03em;
  color: var(--text-secondary);   /* 시안 «글씨 #444» 급 — 아이콘(진한색)보다 한 단 연하게 */
}
/* 🟥#3619ⓔ — 가로 한 줄짜리 안내(회의록 잠금·진행·결과·실패)는 컨트롤 «아래»로 보낸다.
   ★형제 규칙(.memo-voice-flat > [role="status"] { order: 1 })과 같은 뜻이다 — 그쪽은 뿌리를
     한 겹 거치고, 이쪽은 줄의 직계라 선택자만 다르다.
   ★그 안내 문장 «속»의 [자세히]·[그만두기]·[회의록 열기]는 표적 확대에서 되돌린다 —
     문장 안의 낱말을 40px 상자로 키우면 줄이 깨진다(아래 [data-memo-notes] 규칙과 같은 이유). */
.memo-tool-row > p { order: 1; }
.memo-tool-row > p button { min-height: 0; min-width: 0; }

/* ── 도구 버튼 ────────────────────────────────────────────────────────────────── */
.memo-tool-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  padding: 0 8px; border: 1px solid var(--border-default); border-radius: var(--r-sm);
  background: transparent; color: var(--text-secondary);
  font-size: var(--fs-caption); font-family: inherit; font-weight: 500;
  cursor: pointer; white-space: nowrap; flex-shrink: 0;
}
.memo-tool-btn:disabled { opacity: 0.4; pointer-events: none; }
.memo-tool-btn[aria-pressed="true"] {
  background: var(--surface-sunken); border-color: var(--border-strong);
  color: var(--text-primary); font-weight: 700;
}
.memo-tool-btn.memo-tool-danger { color: var(--kpi-alert); }
.memo-tool-glyph { font-weight: 700; }
.memo-tool-aa-glyph { font-size: var(--fs-meta); font-weight: 700; letter-spacing: -0.02em; }

/* ── 치수 (PC 기본) — 종전 부품(.btn-xs 22px 등)도 이 칸 안에서는 표적이 커진다 ─────────
   min-height 는 .btn-xs 의 height 를 이긴다(CSS 상자 규칙) — 그래서 그 부품들의 JSX 를
   한 글자도 고치지 않고 표적만 키울 수 있다. 「옮김이지 재작성이 아니다」의 실행이다.
   ★안내 더미([data-memo-notes]) 안의 [자세히]·링크 꼴 버튼은 제외한다 — 글줄 안의 낱말이라
     44px 상자로 키우면 문장이 깨진다(아래 되돌림 규칙). */
/* 🟥#3619ⓐ — 평소 줄의 세로가 36 → 40 으로 올랐다(라벨 한 줄이 아이콘 «아래»에 서기 때문).
   ★이 값은 «그 줄의 모든 버튼»에 걸린다 — 녹음 중에만 보이는 [녹음 끝내기]·[취소]도 같은 세로다.
     그래야 평소 ↔ 녹음 중을 오갈 때 줄 높이가 튀지 않는다(관리자 지시 ⓐ «높이가 튀지 않게»). */
.memo-tool-row button { min-height: 54px; min-width: 36px; }
.memo-tool-acc button { min-height: 32px; min-width: 32px; }
.memo-tool-mini button { min-height: 36px; min-width: 36px; }
.memo-tool-row .memo-tool-aa { min-width: 36px; }
/* 🟥#3619ⓐ — 첨부·받아쓰기·녹음(+ⓔ [요약])의 가로. 종전 «Aa 급»(36)에서 라벨을 품는 칸으로 넓혔다.
   ★60 의 근거 = 가장 긴 라벨 「받아쓰기」 네 글자 × --fs-tiny(12.7px) ≈ 50.8px + 좌우 여백 8px → 59.
     min-width 는 «바닥»이라 실제 폭은 라벨이 정한다 — 바닥이 라벨보다 좁으면 칸마다 폭이 달라져
     들쭉날쭉해진다. 그래서 바닥을 «가장 긴 라벨»에 맞춘다(자가 토큰에서 계산해 대조한다). */
.memo-tool-row .memo-tool-compact { min-width: 60px; }
.memo-tool-row [data-memo-notes] button,
.memo-tool-acc [data-memo-notes] button,
.memo-tool-row .memo-tool-inline,
.memo-tool-acc .memo-tool-inline { min-height: 0; min-width: 0; }
/* 🟥#3619④ — [Aa]가 칩 줄(.memo-fold-bar)로 옮겨 왔다. 표적 크기는 «그 줄의 칩 급»으로 준다.
   ★도구 줄(48/36)보다 낮은 이유 = 그 줄의 이웃이 접힘 칩(높이 ~22px)이라, 도구 줄 크기를 그대로
     가져오면 칩 줄 혼자 두 배로 부풀어 «레이아웃 정돈»(#3619⑤)의 반대가 된다.
   ★위 `.memo-tool-row .memo-tool-aa` 규칙은 그대로 둔다 — 아코디언 없는 화면·옛 배치가 아직 그
     선택자를 탄다(죽은 규칙이 아니라 «다른 자리»다). */
.memo-fold-bar .memo-tool-aa { min-height: 30px; min-width: 36px; padding: 0 10px; border-color: var(--text-tertiary); font-weight: 700; }   /* 시안 «진한 테두리»(#9ca3af 급) — 도구 상자(border-strong)보다 한 단 진하게 */
.memo-fold-bar .memo-tool-aa .memo-tool-aa-glyph { font-size: var(--fs-tiny); }   /* 시안 «Aa 12px 굵게» */

/* ── 치수 (폰 = 모바일 셸) ────────────────────────────────────────────────────── */
/* 🟥#3619ⓐ 폰 — 48 → 54(아이콘 14 + 간격 1 + 라벨 한 줄 ~15 + 위아래 여백 4 ≈ 34 가 «속»이고,
   손가락 표적(44)을 넉넉히 넘기려 54 로 둔다. PC 40 보다 크다 = «폰이 더 큰 표적» 규약 유지). */
.m-app-frame .memo-tool-row button { min-height: 58px; min-width: 48px; }
.m-app-frame .memo-tool-acc button { min-height: 44px; min-width: 44px; }
.m-app-frame .memo-tool-mini button { min-height: 44px; min-width: 44px; }
.m-app-frame .memo-tool-row .memo-tool-aa { min-width: 56px; }
/* 🟥#3619ⓐ 폰 — 라벨 칸(52) 위에 손가락 표적 폭을 얹어 64.
   ★한 줄에 서나(실측 계산) — 폰 폭 360 · 줄 좌우 여백 16×2 = 쓸 수 있는 폭 328px.
       세 칸(첨부·받아쓰기·녹음) = 64×3 + 간격 6×2 = 204px
       🟥#3619ⓔ 넷째 [요약]까지 = 64×4 + 6×3 = 274px  → 328 > 274 이라 넷도 한 줄이다. */
.m-app-frame .memo-tool-row .memo-tool-compact { min-width: 64px; }
/* 🟥#3619ⓔ 폰 — 남는 폭(328−274 = 54px)을 네 칸이 «고르게» 나눠 가진다.
   ★flex-basis 를 0 으로 두지 않는다(1 1 auto) — 0 으로 두면 라벨 길이를 무시하고 똑같이 나눠,
     「받아쓰기」만 글자가 잘린다. 바닥(64)은 지키고 남는 것만 고르게 편다.
   ★max-width — 버튼이 셋으로 줄어든 상태(요약 조건 불충족)에서 한 칸이 과하게 넓어지지 않게. */
.m-app-frame .memo-tool-row .memo-tool-stack { flex: 1 1 0; max-width: 120px; }
.m-app-frame .memo-tool-row [data-memo-notes] button,
.m-app-frame .memo-tool-acc [data-memo-notes] button,
.m-app-frame .memo-tool-row .memo-tool-inline,
.m-app-frame .memo-tool-acc .memo-tool-inline { min-height: 0; min-width: 0; }
/* 🟥#3619④ 폰 — 칩 줄의 [Aa]도 손가락 표적(44)은 지킨다(그 줄에서 유일한 «누르는 버튼»이다). */
.m-app-frame .memo-fold-bar .memo-tool-aa { min-height: 44px; min-width: 56px; }

/* ── 미니바 — 고른 글자 «그 자리»에 뜬다(자리 계산 = memoMiniBarPos) ───────────────── */
.memo-tool-mini {
  position: fixed; z-index: 5450;
  display: flex; align-items: center; gap: 2px; padding: 3px;
  background: var(--surface-base); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); box-shadow: var(--sh-drop);
}
.memo-tool-mini .memo-tool-btn { border-color: transparent; padding: 0 6px; }
.memo-tool-mini .memo-tool-btn:hover { background: var(--surface-sunken); }

/* ── 아코디언 — 도구줄 «위»에 펼쳐진다. 넘치면 지우지 않고 이 칸 안에서 스크롤한다 ──────
   PC 는 세 칸 가로(문단 / 음성·AI / 메모), 좁아지면 스스로 한 칸으로 접힌다.
   ★서랍(480px)에서는 자연히 한 칸이 된다 — 폭이 190px 짝을 못 만들기 때문이다. */
.memo-tool-acc {
  border-top: 1px solid var(--border-default);
  background: var(--surface-base);
  padding: 8px 16px; overflow-y: auto; flex-shrink: 0;
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.m-app-frame .memo-tool-acc { grid-template-columns: 1fr; }
/* 접힘 = «숨김»이지 «없앰»이 아니다 — 이 칸에서 도는 일(회의록 정리 요청)이 끊기지 않게 마운트는 유지한다.
   display:grid 가 hidden 의 기본 display:none 을 이기므로 여기서 한 번 더 못 박는다. */
.memo-tool-acc[hidden] { display: none !important; }
.memo-tool-group { min-width: 0; }
.memo-tool-group-name {
  margin: 0 0 4px; font-size: var(--fs-tiny); font-weight: 700;
  color: var(--text-tertiary);          /* 흰 바탕 위 4.83:1 — 회색 칸 위에는 쓰지 않는다 */
}
.memo-tool-group-items { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; row-gap: 4px; }

/* ══ 🟥#3619 디자인 회차 2 — 칩 줄 = «작은 알약»(관리자 확정 2026-09-18 시안 v3 · 그림3 꼴) ═══════════════
   종전(09-17 시안 A «칩 = 도구 버튼 한 벌» · 세로 54)은 관리자 확정으로 접었다(「그림3 처럼 작게」).
   ①칩(첨부·참석자) = 알약 한 줄: 아이콘 + 글자 + 요약 + 배지 · 높이 26(PC)/32(폰) · 모서리 999 · 흰 면 · 1px 테두리.
   ②연 칩 = 진한 면 · 흰 글자 · 글자 뒤 ✕ — 배지(보관)는 숨긴다(보관 설정은 펼친 패널 맨 위에 있다).
   ③[Aa] = 오른쪽 끝 작은 사각(28×34 · 폰 36×44) · 글자 줄(「서식」) 없음.
   ④펼친 패널 = 칩들 «뒤» 줄(order 1 · basis 100%) · 머리 [닫기 ✕] · 바닥 [▲ 접기] — 닫는 길 셋은 그대로.
   ★줄바꿈 0 — 칩은 늘리지 않는다(flex 0 1 auto) · 글자 nowrap+ellipsis. 폭 실측(340px 폰 · 내부 316) = 셋 286 · 첨부 열림 240.
   ★PC 넓은 화면·전체화면 — 칩은 왼쪽에 고정 크기로 서고 [Aa]만 오른쪽(margin-left:auto). 늘어나는 요소가 없어 폭이 커져도 모양이 같다. */
.memo-fold-bar { display: flex; flex-wrap: wrap; gap: 6px; row-gap: 8px; align-items: center; }
.memo-fold-bar > .memo-fold-chip { flex: 0 1 auto; min-width: 0; max-width: 62%; order: 0; }
.memo-fold-bar > .memo-tool-aa { flex: 0 0 auto; margin-left: auto; order: 0; }
.memo-fold-chip {
  position: relative; display: inline-flex; flex-direction: row; align-items: center; justify-content: center;
  gap: 4px; min-height: 26px; height: 26px; padding: 0 10px; box-sizing: border-box;
  border: 1px solid var(--border-strong); border-radius: 999px;
  background: var(--surface-base); color: var(--text-primary);
  font-family: inherit; line-height: 1; cursor: pointer;
}
.memo-fold-chip:hover { background: var(--surface-sunken); }
.memo-fold-ic { font-size: 13px; line-height: 1; }
.memo-fold-label { font-size: var(--fs-tiny); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.memo-fold-sum { font-weight: 500; color: var(--text-secondary); }
.memo-fold-badge {
  font-size: 10px; line-height: 1.4; padding: 0 6px; border-radius: 999px;
  border: 1px solid var(--border-default); background: var(--surface-sunken); color: var(--text-secondary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.memo-fold-chip[aria-expanded="true"] { background: var(--text-primary); border-color: var(--text-primary); color: #fff; }
.memo-fold-chip[aria-expanded="true"]:hover { background: var(--text-primary); }
.memo-fold-chip[aria-expanded="true"] .memo-fold-sum { color: #fff; }
.memo-fold-chip[aria-expanded="true"] .memo-fold-badge { display: none; }
.memo-fold-x { position: static; font-size: 11px; line-height: 1; opacity: .9; margin-left: 2px; }
/* 🟥#3727 ②(관리자 확정 2026-09-19) — [메모삭제] 칩: 이웃 칩(참석자·첨부)과 같은 테두리 알약(.memo-fold-chip) 그대로 ·
   위험색은 «글자·아이콘만»(#3578 「빨강은 둘만 — 녹음 중 알약과 삭제」의 그 삭제 축 · 빨간 테두리 상자 꼴 금지). */
.memo-del-chip .memo-fold-label { color: var(--kpi-alert); }
/* #3727 정돈(검수 #657) — 휴지통 아이콘도 위험색: SVG «속성» stroke 의 var() 는 브라우저가 무시해 검정으로 남았다 → CSS 로 겨눈다. */
.memo-del-chip .memo-fold-ic svg { stroke: var(--kpi-alert); }
/* 🟥#3891 ① — 칩 줄 오른쪽 묶음에 [이슈로 만들기]·[내용복사]가 더해져 칩이 넷([Aa] 포함)이 됐다. 폰(내부 316px)에서는
   한 줄에 다 못 선다 → 이 묶음이 «줄을 바꾼다»(가로 넘침 0 · 오른쪽 정렬 유지). 인라인 style(display·gap·margin-left)은 그대로이고
   이 규칙은 인라인에 없는 칸만 준다. min-width:0 + max-width:100% = 칩 줄(.memo-fold-bar) 폭을 넘지 않는다.
   ★[이슈로 만들기]는 요점정리 중(aiBusy)·빈 메모에서 disabled — 옷은 이웃 칩 그대로, 흐림만 준다(누를 수 없음을 보이게). */
.memo-chip-row-end { flex-wrap: wrap; justify-content: flex-end; row-gap: 6px; min-width: 0; max-width: 100%; }
.memo-fold-chip:disabled { opacity: .45; cursor: default; }
.memo-fold-chip:disabled:hover { background: var(--surface-base); }
.memo-fold-bar > .memo-fold-open {
  flex-basis: 100%; width: 100%; order: 1; box-sizing: border-box; padding: 8px 10px;
  background: var(--surface-sunken); border: 1px solid var(--border-default); border-radius: var(--r-sm);
}
.memo-fold-head { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; font-size: var(--fs-tiny); color: var(--text-secondary); }
.memo-fold-close {
  margin-left: auto; font: inherit; font-size: var(--fs-tiny); font-weight: 600; padding: 3px 9px;
  border: 1px solid var(--border-default); border-radius: 8px; background: var(--surface-base); color: var(--text-primary);
  cursor: pointer; white-space: nowrap; flex-shrink: 0;
}
.memo-fold-foot {
  display: block; width: 100%; margin-top: 8px; padding: 7px 0; box-sizing: border-box;
  border: 1px solid var(--border-default); border-radius: 8px; background: var(--surface-base); color: var(--text-primary);
  font: inherit; font-size: var(--fs-tiny); font-weight: 700; cursor: pointer; white-space: nowrap;
}
/* [Aa] — 칩 줄 오른쪽 끝 작은 사각(위 «칩 급 30px» 규칙을 이 뒤 규칙이 덮는다 · 다른 자리의 Aa 는 무접촉) */
.memo-fold-bar .memo-tool-aa {
  display: inline-flex; flex-direction: row; align-items: center; justify-content: center; gap: 0;
  min-height: 28px; height: 28px; min-width: 34px; padding: 0 8px; box-sizing: border-box;
  border: 1px solid var(--border-strong); border-radius: 8px;
  background: var(--surface-base); color: var(--text-primary); font-weight: 700; line-height: 1;
}
.memo-fold-bar .memo-tool-aa:hover { background: var(--surface-sunken); }
.memo-fold-bar .memo-tool-aa .memo-tool-aa-glyph { font-size: 13px; font-weight: 700; }
.memo-fold-bar .memo-tool-aa .memo-tool-aa-label { display: none; }
.memo-fold-bar .memo-tool-aa[aria-expanded="true"] { background: var(--text-primary); border-color: var(--text-primary); color: #fff; }
/* 폰 — 손가락 표적: 칩 32 · [Aa] 36×44(그 줄에서 누르는 것들) */
.m-app-frame .memo-fold-chip { min-height: 32px; height: 32px; padding: 0 12px; }
.m-app-frame .memo-fold-bar .memo-tool-aa { min-height: 36px; height: 36px; min-width: 44px; }

/* ══ 제목 입력칸 — «제목 칸»으로 읽히게(시안 v3 수정 5·6 · 「제목 칸이라는 인지가 되도록」) ═══════════
   흰 면 + 얇은 테두리 + 왼쪽 진한 세로줄 4px + 굵은 큰 글자 + 오른쪽 ✎ — 본문(반투명 연한 면)과 다른 층. 「제목」 표지 글자는 뺐다(수정 6). */
.memo-title-wrap { position: relative; }
.memo-title-input.input {
  width: 100%; box-sizing: border-box; height: 40px; padding: 0 30px 0 12px;
  font-weight: 700; font-size: var(--fs-h3, 15px); color: var(--text-primary);
  background: rgba(255,255,255,.75); border: 1px solid rgba(0,0,0,.12); border-left: 4px solid var(--text-primary);
  border-radius: 8px; outline: none; box-shadow: 0 1px 2px rgba(0,0,0,.06);
}
.memo-title-input.input:focus { border-color: var(--border-focus, rgba(0,0,0,.3)); border-left-color: var(--text-primary); }
.memo-title-input.input::placeholder { color: var(--text-tertiary); font-weight: 600; }
.memo-title-pen { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); font-size: 13px; pointer-events: none; }

/* ══ [원문 | 요약] 세그먼트 탭 — 가로 꽉 참(폰) · PC 는 상한 420 · 선택 칸 = 흰 카드(입체)(시안 v3 수정 4) ═══════
   ★PC·전체화면에서 1,800px 를 다 차지하면 이상하므로 상한을 둔다(왼쪽 정렬) · 폰(.m-app-frame)은 상한 없음 = 가로 꽉 참. */
.memo-tabs {
  display: flex; gap: 0; padding: 3px; margin: 8px 16px 0; max-width: 420px; box-sizing: border-box;
  background: rgba(0,0,0,.08); border-radius: 12px; box-shadow: inset 0 1px 2px rgba(0,0,0,.12); flex-shrink: 0;
}
.m-app-frame .memo-tabs { max-width: none; }
.memo-seg-btn {
  flex: 1 1 0; min-width: 0; text-align: center; padding: 7px 0; font-family: inherit; font-size: var(--fs-caption);
  font-weight: 500; color: var(--text-secondary); background: transparent; border: none; border-radius: 9px; cursor: pointer;
}
.memo-seg-btn[aria-selected="true"] {
  background: var(--surface-base); color: var(--text-primary); font-weight: 700;
  box-shadow: 0 1px 3px rgba(0,0,0,.18), 0 0 0 1px rgba(0,0,0,.04);
}
/* 요약 탭 머리 오른쪽 — [↻ 재정리] · [✈ 보내기] 작은 알약 둘(서식 패널 «음성·AI» 줄에서 옮겨 왔다 · 확정 ⓐ) */
.memo-summary-acts { display: flex; gap: 6px; justify-content: flex-end; align-items: center; padding: 6px 16px 0; flex-shrink: 0; flex-wrap: wrap; }
.memo-summary-acts .btn { border: 1px solid var(--border-default); border-radius: 999px; padding: 2px 9px; background: var(--surface-base); min-height: 24px; }
/* 🟥#3891 둘째 컷 — 요약 «그 자리 편집»(메모 서랍 한 벌 · PC·모바일·분리창 공통) */
.memo-summary-acts .memo-sum-edited { margin-right: auto; font-size: var(--fs-tiny); color: var(--text-secondary); cursor: help; white-space: nowrap; }
.memo-summary-acts.is-editing { justify-content: flex-end; }
.memo-summary-acts.is-editing .memo-sum-save { background: var(--explan-red, #e0004d); color: #fff; border-color: transparent; }
.memo-summary-acts.is-editing .memo-sum-save:disabled { opacity: .45; cursor: not-allowed; }
.memo-summary-panel .memo-sum-editor { display: flex; flex-direction: column; min-height: 120px; border: 1px dashed var(--border-default); border-radius: var(--r-sm); padding: 4px; }
.memo-drawer-root.memo-sum-editing .memo-tool-row { position: relative; }
.memo-drawer-root.memo-sum-editing .memo-tool-row > :not(.memo-tool-lock) { opacity: .45; }
.memo-drawer-root.memo-sum-editing .memo-tool-extra { opacity: .45; pointer-events: none; }
.memo-tool-row .memo-tool-lock { position: absolute; inset: 0; z-index: 2; background: transparent; border: 0; padding: 0; margin: 0; cursor: not-allowed; }
.memo-sum-edited-note { font-size: var(--fs-tiny); color: var(--text-secondary); margin: 0 0 6px; }
.memo-ask .memo-ask-warn { font-size: var(--fs-tiny); color: var(--status-warn, #b45309); margin: 2px 0 0 26px; }

/* ══ [Aa] 서식 패널 — 28px 미니바 한 줄 + 메모 줄 한 줄(시안 v3 ③ · 「버튼이 너무 크다 · 작게」) ═══════════
   «글자·문단» 소제목 줄 없음(아이콘 + 툴팁) · «음성·AI» 줄 없음(스위치·회의록으로 정리·🔒 안내 삭제) ·
   🟥#3891 ①(PM 승인) — 메모 줄([🔗 이슈로 만들기] [📋 내용복사])은 칩 줄 오른쪽(항시 노출 · .memo-chip-row-end)으로 옮겼다 —
   이 패널은 이제 서식 미니바 한 줄이다. 메모 줄 규칙(.memo-memo-row)도 함께 걷었다(두 벌 금지).
   🟥#3727 ②(관리자 확정 2026-09-19) — [🗑 메모삭제]는 칩 줄 오른쪽(항시 노출 · .memo-del-chip)으로 옮겼다 —
   이 줄의 삭제 전용 규칙(.memo-memo-del)도 함께 걷었다(두 벌 금지). */
.memo-tool-acc.memo-acc-slim { display: flex; flex-direction: column; gap: 8px; }
.memo-fmt-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.memo-fmt-bar .memo-fmt-sep { width: 1px; height: 18px; background: var(--border-default); margin: 0 3px; }
.memo-fmt-bar > button { width: 28px !important; height: 28px !important; border-radius: 6px !important; }
.memo-tool-acc.memo-acc-slim button { min-height: 28px; min-width: 28px; }
.m-app-frame .memo-tool-acc.memo-acc-slim button { min-height: 34px; min-width: 34px; }
.m-app-frame .memo-fmt-bar > button { width: 34px !important; height: 34px !important; }

/* ══ [요약] 잠금 툴팁 — 🔒 안내 줄 대신(시안 v3 ① · 「설명은 자리 많이 차지하니 툴팁으로」) ═══════════
   비활성처럼 보이되(aria-disabled) 누를 수는 있다 — 터치 화면엔 title 툴팁이 없어 «누르면 한 번» 뜬다(PC 는 title 도 산다). */
.memo-tool-row .memo-tool-stack { position: relative; }
.memo-tool-row .memo-tool-stack[aria-disabled="true"] { opacity: .5; cursor: default; }
.memo-tool-tip {
  position: absolute; bottom: calc(100% + 8px); right: 0; z-index: 30; width: 220px; max-width: 70vw;
  padding: 7px 10px; border-radius: 8px; background: var(--text-primary); color: #fff;
  font-size: var(--fs-tiny); font-weight: 400; line-height: 1.45; text-align: left; white-space: normal;
  box-shadow: 0 4px 12px rgba(0,0,0,.2);
}
.memo-tool-tip::after { content: ""; position: absolute; bottom: -6px; right: 22px; border: 6px solid transparent; border-top-color: var(--text-primary); border-bottom: 0; }
.memo-tool-tip button { color: #93C5FD; text-decoration: underline; background: none; border: none; font: inherit; padding: 0; min-height: 0 !important; min-width: 0 !important; cursor: pointer; }
/* 녹음기의 부수 줄(글로 옮기기 · AI 교정 단추) — 서식 패널 «음성·AI» 줄이 사라져 도구 줄 «아래 한 줄»로 옮겼다 · 비면 사라진다 */
.memo-tool-row > .memo-tool-extra { width: 100%; flex-basis: 100%; order: 2; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.memo-tool-row > .memo-tool-extra:empty { display: none; }
.memo-tool-row > .memo-tool-extra button { min-height: 0; min-width: 0; }

/* ══ 팔레트 줄 오른쪽 «스타일 유지» 토글(시안 v3 수정 1~2 · 칩 줄 넷은 340px 에 안 들어가 팔레트 줄로) ═══ */
.memo-color-bar .memo-keepstyle {
  display: inline-flex; align-items: center; gap: 3px; height: 22px; padding: 0 8px; margin-left: 6px;
  border: 1px solid var(--border-strong); border-radius: 999px; background: var(--surface-base); color: var(--text-secondary);
  font-family: inherit; font-size: var(--fs-tiny); white-space: nowrap; cursor: pointer;
}
.memo-color-bar .memo-keepstyle[aria-pressed="true"] { background: var(--text-primary); border-color: var(--text-primary); color: #fff; font-weight: 700; }
.m-app-frame .memo-color-bar .memo-keepstyle { height: 26px; }
/* 🟥#3619 회차 4-1(관리자 실기기 2026-09-18 ①) — 팔레트 줄 오른쪽 끝 «저장 중… / 저장됨» 칸은 고정 폭.
   문구가 바뀌어도 폭이 같아야 flex-wrap 줄바꿈 판정이 같다 — 종전엔 «저장 중…»(긴 문구)일 때만 375px 에서 다음 줄로 밀려
   줄이 두 줄↔한 줄로 흔들렸다. min-width = 긴 문구(«저장 중…» 5글자 · 영문 «Saving…») 기준 · nowrap · 오른쪽 정렬. */
.memo-color-bar .memo-save-state {
  margin-left: auto; flex: 0 0 auto; min-width: 5.5em; white-space: nowrap; text-align: right;
  font-size: var(--fs-tiny); color: var(--text-tertiary);
}

/* ══ 녹음 종료 확인창 — 체크 셋(원문 고정 🔒 · 첨부 · 요약) + [취소][확인](시안 v3 ④ · 관리자 확정 2026-09-18) ═══ */
.memo-ask-dim { position: fixed; inset: 0; z-index: 5500; background: rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; padding: 16px; }
.memo-ask { width: 320px; max-width: 100%; background: var(--surface-base); border-radius: 14px; padding: 16px 16px 12px; box-shadow: 0 10px 30px rgba(0,0,0,.25); box-sizing: border-box; }
.memo-ask h3 { margin: 0 0 8px; font-size: var(--fs-body); font-weight: 700; color: var(--text-primary); }
.memo-ask-list { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 12px; }
.memo-ask-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-default); border-radius: 10px; background: var(--surface-sunken); cursor: pointer; }
.memo-ask-row.is-lock { cursor: default; opacity: .85; }
.memo-ask-row input { width: 18px; height: 18px; margin: 0; accent-color: var(--text-primary); flex-shrink: 0; }
.memo-ask-row .memo-ask-lb { flex: 1; min-width: 0; font-size: var(--fs-caption); font-weight: 700; color: var(--text-primary); line-height: 1.25; }
.memo-ask-row .memo-ask-lb small { display: block; font-weight: 400; font-size: var(--fs-tiny); color: var(--text-secondary); margin-top: 1px; }
.memo-ask-btns { display: flex; gap: 6px; }
.memo-ask-btns button { flex: 1; padding: 8px 0; border: 1px solid var(--border-strong); border-radius: 8px; background: var(--surface-base); color: var(--text-primary); font: inherit; font-size: var(--fs-caption); font-weight: 600; cursor: pointer; }
.memo-ask-btns .is-pri { background: var(--text-primary); color: #fff; border-color: var(--text-primary); }
.memo-ask-ft { margin-top: 8px; font-size: 10.5px; color: var(--text-tertiary); text-align: center; }

/* ══ 첨부 미리보기 «그 창 안»(지시 ⓐ) — 새 탭 대신 덮개 + iframe(PC·iOS) · 앱 웹뷰는 종전 EX_DL(외부 브라우저) ═══ */
.memo-prev-dim { position: fixed; inset: 0; z-index: 5400; background: rgba(0,0,0,.6); display: flex; flex-direction: column; }
.memo-prev-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--surface-base); color: var(--text-primary); font-size: var(--fs-caption); flex-shrink: 0; }
.memo-prev-head .memo-prev-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.memo-prev-frame { flex: 1; width: 100%; border: 0; background: #fff; }

/* ═══ 🟥메모 회차 3 (관리자 확정 2026-09-18 · 시안 시안_3619_받아쓰기ⓑ_실시간삽입) ═══════════════════════ */
/* ⓑ 실시간 받아쓰기 — 아직 확정 안 된 말(흐린 점선) · 멈춘 뒤 AI 교정으로 바뀐 낱말(초록 · 5초 뒤 표시만 사라짐) */
.memo-stt-interim { color: #9CA3AF; border-bottom: 1.5px dotted #9CA3AF; }
.memo-stt-interim:empty { display: inline-block; width: 0; border: 0; }
@keyframes memoSttFixFade { 0% { background: #DCFCE7; } 80% { background: #DCFCE7; } 100% { background: transparent; } }
.memo-stt-fix { border-bottom: 2px solid #16A34A; border-radius: 2px; animation: memoSttFixFade 5s ease-out forwards; }
/* ⑤ 키보드가 올라온 동안(.memo-kb-open · 판정은 MemoDrawer) — 본문 + [Aa] 서식 미니바만 · 나머지는 «감춤»(떼지 않는다) */
.memo-drawer-root.memo-kb-open .memo-meta-region,
.memo-drawer-root.memo-kb-open .memo-color-bar,
.memo-drawer-root.memo-kb-open .memo-tool-row,
.memo-drawer-root.memo-kb-open .memo-tool-extra,
.memo-drawer-root.memo-kb-open .memo-summary-acts { display: none !important; }
/* 🟥#3891 — 요약 «편집 중» 머리 줄([편집 취소][요약 저장])은 키보드가 떠도 감추지 않는다(감춤 목록의 예외 한 줄 · 구체성 4 > 3) */
.memo-drawer-root.memo-kb-open .memo-summary-acts.is-editing { display: flex !important; }
.memo-drawer-root.memo-kb-open .memo-title-wrap { padding-top: 4px; padding-bottom: 4px; }
.memo-drawer-root.memo-kb-open .memo-body-first { margin-bottom: 4px; }
/* 커서(관리자 확정 2026-09-18) — 본문 상자(.memo-body-first · 세로 flex)를 편집기가 끝까지 채운다.
   RichBodyEditor 는 편집기를 position:relative 감싸개 안에 두므로 flex 자식은 그 감싸개다 —
   감싸개도 세로 flex 로 늘리고 그 안 편집기가 남은 높이를 가진다. 빈 자리 탭 = 편집기 탭(캐럿·폰 키보드는 브라우저 몫). */
.memo-body-first > div { flex: 1 0 auto; display: flex; flex-direction: column; min-height: 0; }
.memo-body-first .rich-body-editor { flex: 1 0 auto; }
.memo-drawer-root.memo-kb-open .memo-tool-acc.memo-acc-slim { padding-top: 4px; padding-bottom: 4px; }
/* 키보드 동안 서식 줄 오른쪽 끝 [받아쓰기] — 도구 줄의 그 버튼을 대신 누른다(관리자 2026-09-18) */
/* ★.memo-fmt-bar > button 의 정사각 강제(28/34px !important)에서 이 버튼만 뺀다 — 글자가 있는 알약이다 */
.memo-fmt-bar > button.memo-kb-mic { width: auto !important; height: 28px !important; border-radius: 999px !important; min-width: 0; }
.m-app-frame .memo-fmt-bar > button.memo-kb-mic { height: 34px !important; }
.memo-kb-mic { margin-left: auto; height: 28px; padding: 0 10px; display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto;
  border: 1px solid var(--border-default); border-radius: 999px; background: var(--surface-base);
  font-size: var(--fs-caption); font-weight: 600; color: var(--text-primary); cursor: pointer; white-space: nowrap; }
/* 🟥#3619 회차 4-1(관리자 실기기 2026-09-18 ③) — 녹음·받아쓰기가 «도는 중»(aria-pressed)이면 이 알약은 [■ 끝내기] —
   도구 줄 [끝내기](.memo-tool-stop)와 같은 빨강(kpi-alert 토큰 · 브랜드색 0 · #3578 「빨강은 녹음 중 알약과 삭제 둘뿐」의 그 알약). */
.memo-kb-mic.is-stop, .memo-kb-mic[aria-pressed="true"] { background: var(--kpi-alert); border-color: var(--kpi-alert); color: var(--surface-base); font-weight: 700; }
/* 접혀 있던 서식 아코디언을 키보드 동안만 편다 — hidden 속성(UA display:none)을 이 한 줄이 이긴다 */
.memo-drawer-root.memo-kb-open .memo-tool-acc.memo-acc-slim[hidden] { display: flex !important; }

/* ═══ 🟥메모 회차 4 (관리자 확정 2026-09-18 · A 문단 · B 교정 안내 · C 원문/교정본 선택 · D 녹음 뒤 안내 · E 부품 하나) ═══ */
/* E 진행 안내 부품(MemoProgressNotice) — 자리 = 서랍 본문 «바로 위» 칸(.memo-progress-host · portal) · 모양은 memo-ask-*·잔류 줄과 한 벌.
   ★키보드 동안(.memo-kb-open) 감춤 목록에 넣지 않는다 — 교정 중·요약 중 안내는 키보드가 떠도 보인다. */
.memo-progress-host { flex-shrink: 0; padding: 0 16px; }
.memo-progress-host:empty { display: none; }
.memo-progress-notice { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; padding: 7px 10px; min-width: 0;
  border: 1px solid var(--border-default); border-radius: var(--r-sm, 6px); background: var(--surface-sunken);
  font-size: var(--fs-tiny); line-height: 1.5; color: var(--text-secondary); word-break: keep-all; }
.memo-progress-notice.tone-ok { background: var(--surface-info, #EFF6FF); border-color: var(--border-info, #BFDBFE); }
.memo-progress-notice.tone-warn { background: var(--surface-warn, #FFF7ED); border-color: var(--border-warn, #FED7AA); }
.memo-progress-body { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.memo-progress-stage { color: var(--text-primary); font-weight: 700; }
.memo-progress-clock { font-variant-numeric: tabular-nums; color: var(--text-tertiary); }
.memo-progress-text { flex-basis: 100%; }
.memo-progress-spin { width: 12px; height: 12px; flex-shrink: 0; border-radius: 50%; border: 2px solid var(--border-strong, #D1D5DB);
  border-top-color: var(--text-primary); animation: memoProgressRot .9s linear infinite; }
.memo-progress-dot { width: 8px; height: 8px; flex-shrink: 0; border-radius: 50%; background: var(--text-tertiary); }
.tone-ok .memo-progress-dot { background: #16A34A; }
.tone-warn .memo-progress-dot { background: var(--kpi-warn, #B45309); }
@keyframes memoProgressRot { to { transform: rotate(360deg); } }
.memo-progress-btn { flex: 0 0 auto; height: 24px; padding: 0 10px; border: 1px solid var(--border-strong, #D1D5DB); border-radius: 999px;
  background: var(--surface-base); color: var(--text-primary); font: inherit; font-size: var(--fs-tiny); font-weight: 600; cursor: pointer; white-space: nowrap; }
.memo-progress-btn.is-pri { background: var(--text-primary); color: #fff; border-color: var(--text-primary); }
/* 인라인 폴백(진행 안내 칸이 없는 화면) — 도구 줄 안에서 한 줄을 통째로 쓴다 */
.memo-tool-row .memo-progress-notice, .memo-voice-flat > .memo-progress-notice { width: 100%; flex-basis: 100%; order: -2; }
/* C 원문/교정본 선택 화면 — 확인창(.memo-ask)의 옷을 넓힌 것 · PC = 좌·우 두 칸 · 폰(.is-narrow) = 위·아래 */
.memo-choice { width: 720px; max-width: 100%; max-height: min(88vh, 720px); display: flex; flex-direction: column; }
.memo-choice-head { display: flex; align-items: flex-start; gap: 8px; }
.memo-choice-head h3 { flex: 1; min-width: 0; }
.memo-choice-x { flex: 0 0 auto; width: 28px; height: 28px; border: 0; border-radius: 999px; background: transparent; color: var(--text-secondary);
  font-size: 18px; line-height: 1; cursor: pointer; }
.memo-choice-cols { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 4px 0 12px; }
.memo-choice.is-narrow { width: 100%; }
.memo-choice.is-narrow .memo-choice-cols { grid-template-columns: 1fr; }
.memo-choice-col { min-height: 0; display: flex; flex-direction: column; border: 1px solid var(--border-default); border-radius: 10px; background: var(--surface-sunken); overflow: hidden; }
.memo-choice-col.is-fixed { border-color: #86EFAC; }
.memo-choice-lb { flex: 0 0 auto; padding: 6px 10px; font-size: var(--fs-caption); font-weight: 700; color: var(--text-primary); border-bottom: 1px solid var(--border-default); }
.memo-choice-lb small { font-weight: 400; color: var(--text-secondary); }
.memo-choice-body { flex: 1; min-height: 64px; max-height: 42vh; overflow: auto; padding: 8px 10px; font-size: var(--fs-caption); line-height: 1.6; color: var(--text-primary); word-break: keep-all; }
.memo-choice.is-narrow .memo-choice-body { max-height: 24vh; }
.memo-choice-warn { flex: 0 0 auto; margin: 6px 10px 0; padding: 5px 8px; font-size: var(--fs-caption); color: var(--text-warn, #9A3412); background: var(--surface-warn, #FFF7ED); border: 1px solid var(--border-warn, #FED7AA); border-radius: 8px; }   /* 검수 4차 — 줄 나눔 못 옮김 안내 */
.memo-choice-body p { margin: 0 0 8px; white-space: pre-line; }
.memo-choice-body p:last-child { margin-bottom: 0; }
/* 교정본 쪽 «바뀐 낱말» = 회차 3 의 초록 표식(.memo-stt-fix)을 그대로 재사용 — 선택 화면 안에서는 사라지지 않고 남는다 */
.memo-choice-body .memo-stt-fix { animation: none; background: #DCFCE7; }
.memo-choice-append { margin: 0 0 4px; padding: 8px 10px; border: 1px solid var(--border-warn, #FED7AA); border-radius: 10px; background: var(--surface-warn, #FFF7ED);
  font-size: var(--fs-caption); color: var(--text-primary); line-height: 1.5; }

/* ── SysEditGuard 모드 바 손가락 표적(CS-20260922-2210 후속 · #3827r · 2026-09-24) ─────────────
   대표가 폰(PC 화면 축소 보기)에서 [수정](표시 30px → 실측 ~8px)을 못 눌렀다. AppShell.jsx 의
   BarBtn(보기 모드 [수정] · 편집 모드 [편집 취소]·[저장] · 리포트 [계속 편집]·[승인·적용] 전부 공용)
   에 className 만 얹었다 — 인라인 height:30 은 한 글자도 안 고친다.
   ★#3619 와 같은 상자 규칙 — min-height/min-width 는 별 selector 라 인라인 height 와 다투지 않고
     «used height = max(height, min-height)»로 상자 계산에서 이긴다(옮김이지 재작성이 아니다).
   ★로그인 화면과 같은 문턱(860 · components.css 기존 관례, 실측 폰 뷰포트 390~430 포함) — 그 안쪽만
     44px 급으로 올리고, PC 데스크톱 폭에서는 30px 그대로 두어 띠 높이가 과하게 커지지 않게 한다.
   ★(pointer: coarse) 갈래 추가(팀장 09-24 재지시) — 대표의 «PC 화면 축소 보기»는 layout viewport 가
     데스크톱 폭(≥980)으로 잡혀 위 max-width:860 이 안 걸릴 공산이 크다. 손가락(coarse) 입력이면
     «폭과 무관하게» 44px 이 걸리게 한다 — PC 마우스는 fine 이라 데스크톱은 무영향. 같은 파일의
     .chat-actions·.todo-del·.todo-row 가 쓰는 (hover: none), (pointer: coarse) 관례와 같은 모양. */
@media (max-width: 860px), (pointer: coarse) {
  .sys-guard-bar-btn {
    min-height: 44px; min-width: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }
}
.memo-choice-append .memo-ask-btns { margin-top: 8px; }
