/*
  app.css — 회의록 자동화 웹앱 공용 스타일 (전 화면 공용, 단일 파일)
  ------------------------------------------------------------
  "모던 SaaS" 리스킨: 밝은 배경 + 화이트 카드 + 부드러운 다층 그림자 + 큰 라운드 +
  인디고/바이올렛 포인트 컬러(그라디언트). 외부 CDN/폰트/JS 라이브러리 사용 안 함 —
  시스템 폰트 스택만 사용, 이 파일 하나로 self-contained.

  ⚠️ 주의(중요): 아래 선택자들은 record.js / status.js / meetings.js 및
  templates/*.html의 Jinja가 실제로 참조하는 "훅"입니다. 시각(색/여백/그림자/타이포)은
  자유롭게 바꾸되, 클래스/아이디/데이터 속성 이름 자체는 절대 바꾸지 마세요:
    #rec-btn #rec-title #rec-timer #rec-indicator #upload-progress #status-view
    #record-view #queue-position #status-error #view-btn #mic-warning #file-input
    #file-pick-label #file-name #file-upload-btn #meetings-list
    #meeting-card-template #meetings-empty #meetings-error #refresh-btn #minutes-frame
    [data-step], .status-badge[data-status], .is-recording/.is-current/.is-done/.is-error,
    [hidden], [aria-disabled], [aria-pressed], .meeting-card__title/__datetime/__uploader,
    .has-file

  v3(디자이너 마크업 정리) — .meeting-card__view-link("보기" 버튼) 훅을 meetings.html에서
  완전히 삭제했다(재추가 금지). 그 앵커의 ::after로 카드 전체를 덮던 stretched-link CSS도
  함께 제거하고, 대신 .meeting-card 자체에 cursor:pointer를 기본값으로 추가했다. 카드
  클릭/키보드 이동 로직은 meetings.js의 위임 클릭 + keydown 핸들러가 담당한다(카드에
  role="link"+tabindex=0을 status==="done"일 때만 부여). done이 아닌 모든 상태
  (processing/pending/failed/uploaded)의 흐림/cursor:default는 renderCard()가
  .meeting-card 자신에 미러링해둔 data-status를 직접 참조하는 속성 선택자로 구현했다
  (아래 11번 섹션 .meeting-card[data-status]:not([data-status="done"]) 참고 — :has()
  의존 제거, v4 Low 코드리뷰).

  회의록 삭제(휴지통, 관리자 전용, meetings.js/detail.js 공용) 훅 — user.role=='admin'일
  때만 서버가 렌더:
    .meeting-card__delete #meetings-status
    #delete-confirm-modal #delete-confirm-target #delete-confirm-error
    #delete-confirm-cancel-btn #delete-confirm-btn (detail.html: #btn-delete
    .detail-toolbar__divider) .modal__desc .modal__desc-target .modal__error
  카드 삭제 버튼 재배치(레이아웃 틀어짐 수정) — .meeting-card__delete는 더 이상 카드
  우상단에 절대위치로 얹혀있지 않고 .meeting-card__side 안 .meeting-card__side-actions
  (신규, hook 아님 — 순수 레이아웃용 래퍼)에 배치된다(v3: "보기" 버튼 삭제로 이제 이
  래퍼 안에는 삭제 버튼만 남는다). 이에 따라 구 .meeting-card--has-actions(삭제 버튼용
  padding-right 예약 클래스)는 완전히 제거했다.

  admin.js / account_password.js 훅 — admin.html / account_password.html /
  login.html / base.html이 참조:
    #employee-add-form #emp-email #emp-name #emp-department #emp-role #emp-add-btn
    #admin-message [data-user-id] .js-edit-emp .js-reset-pw .js-toggle-role .js-delete-emp
    .role-badge[data-role], #emp-table-body #emp-table-empty
    #edit-emp-modal .modal__backdrop[data-modal-dismiss] #employee-edit-form
    #edit-emp-email #edit-emp-name #edit-emp-department #edit-emp-role #edit-emp-cancel-btn
    #password-form #current-password #new-password #new-password-confirm #password-message
    input[name="email"] input[name="remember"] (login.html — id="username"은 유지되지만
    name은 email), .app-nav__admin-link(관리자 아이콘, user.role=='admin'일 때만 렌더)

  detail.js 훅 — detail.html이 참조(v4: 상단 탭 [회의록][스크립트] + 문서 풀블리드 뷰어 +
  인라인 편집/다운로드. detail.js 자체는 아직 v2 그대로 — activateTab() 등 탭 전환 로직은
  detail.html 상단 주석의 "detail.js가 새로 구현해야 하는 부분" 참고, 이 파일(app.css)은
  마크업/스타일만 바뀌었다):
    #detail-frame-wrap #editing-badge #detail-toolbar #toolbar-view-actions
    #toolbar-edit-actions #toolbar-script-actions #btn-edit #btn-save #btn-cancel
    #btn-revert #btn-download #pane-script #transcript-loading #transcript-empty
    #transcript-error #transcript-content #btn-transcript-download #detail-processing
    #detail-title(.visually-hidden으로 화면엔 숨기되 DOM은 유지 — 저장 title/스크립트
    파일명 폴백용), .is-editing, [data-fingerprint]
  ⚠️ v4에서 제거된 훅(재추가 금지): #btn-transcript-toggle, #transcript-panel(id — 클래스
  "transcript-panel"은 #pane-script에 그대로 재사용), .editing-badge--overlay(topbar 안
  평범한 flex 자식이 되며 불필요해짐).
  detail.html v4 전용 신규 훅(base.html hide_app_chrome과 짝) — .app-main--flush
  .detail-back .detail-topbar .detail-tabs .detail-tabs__tab #tab-minutes #tab-script

  detail.html v6 전용 신규 훅(자유 서식 툴바 — PM 설계·팀장 확정, 마크업/CSS만 존재하고
  detail.js 이벤트는 아직 없음. 상세 스펙은 detail.html 상단 "신규 훅(v6, 자유 서식
  툴바)" 주석 참고):
    .detail-toolbar__group--edit(#toolbar-edit-actions 수식자, 세로 2행 레이아웃)
    .detail-toolbar__actions(2행 — 기존 #btn-save/#btn-cancel/#btn-revert를 감싸는
    래퍼. 얘도 .detail-toolbar__group의 자손이라 기존 `.detail-toolbar__group .btn`
    규칙을 그대로 상속받는다 — 중복 정의 없음)
    #format-toolbar.format-toolbar(role=toolbar, 1행) .format-toolbar__cluster
    .format-toolbar__seg(구분선+클러스터를 한 flex 아이템으로 묶어 좁은 화면 줄바꿈 시
    구분선만 홀로 남는 것을 방지 — .detail-toolbar__danger-group과 동일 패턴)
    #btn-bold #btn-italic(.format-btn, aria-pressed로 toggle 표시)
    #select-font-size(.format-select) #btn-clear-format(.format-btn)
    .fmt-color-swatch[data-color](큐레이션 팔레트 5색 — 신규 색 토큰 없이 기존
    --ink/--danger-dark/--c-processing-text/--c-done-text/--text-light 재사용)
    #input-color(.format-color-custom, 네이티브 <input type="color">)
    선택 없음 시각 힌트(선택 사항): #format-toolbar[data-selection="empty"]

  detail.html v7 전용 신규 훅(목록 서식 — PM 설계·팀장 확정, v6 서식 툴바에 세그먼트
  추가. 신규 CSS 클래스 없이 기존 .format-btn/.format-toolbar__seg/
  .format-toolbar__cluster/.detail-toolbar__divider를 그대로 재사용해 12b 섹션 스타일이
  자동 적용된다):
    #btn-bullet-list #btn-numbered-list(둘 다 .format-btn, aria-pressed로 toggle 표시 —
    #btn-bold/#btn-italic과 동일 패턴)

  record.js 훅(v2 추가분, id/class는 변경 없음) — .record-fab-wrap #rec-btn.rec-btn--fab
  (record.js 자체는 무변경, #rec-btn 등 기존 훅을 그대로 참조)
  ⚠️ v3에서 record.html에서 삭제된 마크업(재추가 금지 아님 — 필요 시 복원 가능, CSS
  규칙 자체는 남겨둠): #record-view-title(h1), #rec-title을 담던 .form-field,
  .record-hint, .upload-divider. #rec-title 삭제는 record.js가 null-safe라 에러는
  없지만 "회의 제목 입력" 기능이 화면에서 사라졌다 — 상세는 record.html 상단 주석 참고.

  모바일 퍼스트로 작성하고 min-width 미디어쿼리로 확장한다.
------------------------------------------------------------ */

/* ---------- 0. 리셋 ---------- */
* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

/* hidden 속성은 다른 컴포넌트 클래스(display:flex 등)에 밀리지 않도록 항상 최우선으로 적용 */
[hidden] {
  display: none !important;
}

img,
svg {
  display: block;
  max-width: 100%;
}

button {
  font: inherit;
  color: inherit;
}

a {
  color: inherit;
}

/* ---------- 1. 디자인 토큰 ---------- */
:root {
  /* 브랜드 팔레트 — 인디고 → 바이올렛 그라디언트 포인트 */
  --primary: #4f46e5;       /* indigo-600 */
  --primary-dark: #4338ca;  /* indigo-700 */
  --primary-darker: #3730a3;/* indigo-800 (pressed) */
  --primary-light: #eef2ff; /* indigo-50 */
  --primary-soft: #e0e7ff;  /* indigo-100 */
  --accent: #7c3aed;        /* violet-600 */
  --accent-light: #f5f3ff;  /* violet-50 */

  --gradient-primary: linear-gradient(135deg, #6366f1 0%, #7c3aed 100%);
  --gradient-primary-hover: linear-gradient(135deg, #4f46e5 0%, #6d28d9 100%);
  --gradient-danger: linear-gradient(135deg, #f87171 0%, #dc2626 100%);

  /* 중립 / 텍스트 */
  --ink: #1a1c33;
  --text: #20233a;
  --text-light: #5c6178;
  --text-faint: #6b7085; /* 흰 배경 대비 ~4.9:1(WCAG AA) — 작은 텍스트에도 안전 */

  --bg: #f5f6fb;
  --surface: #ffffff;
  --surface-alt: #f8f9fd;
  --line: #e6e8f2;
  --line-strong: #d7d9ea;

  /* 상태 색상 (uploaded / pending / processing / done / failed) */
  --c-uploaded-text: #4338ca;
  --c-uploaded-bg: #eef2ff;
  --c-uploaded-border: #c7d2fe;

  --c-pending-text: #63677c;
  --c-pending-bg: #f1f2f7;
  --c-pending-border: #e1e3ee;

  --c-processing-text: #1d4ed8;
  --c-processing-bg: #dbeafe;
  --c-processing-border: #bfdbfe;

  --c-done-text: #15803d;
  --c-done-bg: #dcfce7;
  --c-done-border: #bbf7d0;

  --c-failed-text: #b91c1c;
  --c-failed-bg: #fee2e2;
  --c-failed-border: #fecaca;

  --danger: #dc2626;
  --danger-dark: #b91c1c;

  /* 간격 스케일 (4/8px 리듬) */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;

  --radius-sm: 10px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --radius-xl: 28px;
  --radius-full: 999px;

  /* 다층(레이어드) 그림자 — 톤은 인디고를 살짝 섞어 브랜드감을 준다 */
  --shadow-xs: 0 1px 2px rgba(23, 24, 55, 0.05);
  --shadow-sm: 0 1px 2px rgba(23, 24, 55, 0.04), 0 6px 16px rgba(23, 24, 55, 0.06);
  --shadow-md: 0 10px 28px rgba(79, 70, 229, 0.12), 0 3px 10px rgba(23, 24, 55, 0.06);
  --shadow-lg: 0 24px 56px rgba(79, 70, 229, 0.18), 0 10px 22px rgba(23, 24, 55, 0.08);

  --focus-ring: #4f46e5;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Apple SD Gothic Neo",
    "Malgun Gothic", "Noto Sans KR", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------- 2. 베이스 타이포그래피 / 배경 ---------- */
body {
  /* 아주 은은한 앰비언트 그라디언트(인디고/바이올렛) + 베이스 컬러 — 이미지 없이 CSS만 */
  background:
    radial-gradient(1100px 520px at 12% -8%, rgba(99, 102, 241, 0.08), transparent 60%),
    radial-gradient(900px 480px at 100% 0%, rgba(124, 58, 237, 0.06), transparent 55%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
  margin: 0;
  color: var(--ink);
  word-break: keep-all;
  letter-spacing: -0.01em;
}

.page-title {
  font-size: 24px;
  font-weight: 800;
}

/* ---------- 3. 접근성 유틸 ---------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--sp-3) var(--sp-4);
  background: var(--primary);
  color: #fff;
  border-radius: 0 0 var(--radius-sm) 0;
}

.skip-link:focus {
  left: 0;
}

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

:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---------- 4. 레이아웃: 헤더 / 메인 / 푸터 ---------- */
.app-header {
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 20;
}

.app-header__inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--sp-3) var(--sp-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

.app-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 800;
  font-size: 16px;
  color: var(--primary-dark);
  text-decoration: none;
  white-space: nowrap;
}

.app-logo__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  background: var(--gradient-primary);
  color: #fff;
  box-shadow: var(--shadow-xs);
  flex: none;
}

.app-logo__text {
  background: var(--gradient-primary);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--primary-dark);
}

.app-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  flex-wrap: wrap;
}

.app-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--sp-4);
  border-radius: var(--radius-full);
  color: var(--text-light);
  text-decoration: none;
  font-size: 14px;
  font-weight: 700;
  border: none;
  background: transparent;
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.app-nav__link:hover {
  background: var(--primary-light);
  color: var(--primary-dark);
}

.app-nav__link.is-active {
  background: var(--primary-light);
  color: var(--primary-dark);
}

.app-nav__logout-form {
  margin: 0;
  display: inline-flex;
}

.app-nav__logout-btn:hover {
  background: var(--c-failed-bg);
  color: var(--danger-dark);
}

/* 헤더 관리자 아이콘(admin.html로 이동) — 아이콘 전용 링크라 좌우 패딩을 좁혀 시각적으로
   버튼처럼 보이게 한다. 호버/포커스 스타일은 .app-nav__link 공통 규칙을 그대로 물려받는다. */
.app-nav__admin-link {
  padding: 0 var(--sp-3);
}

/* ── 본문 상단 탭 (녹음 / 회의록 목록) — 헤더 메뉴를 main 영역 탭으로 이동 ── */
.main-tabs {
  display: flex;
  gap: var(--sp-2);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--sp-6);
}

.main-tabs__tab {
  position: relative;
  padding: var(--sp-3) var(--sp-4);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-light);
  text-decoration: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: color 0.15s ease, background 0.15s ease;
}

.main-tabs__tab:hover {
  color: var(--text);
  background: var(--surface-alt);
}

.main-tabs__tab.is-active {
  color: var(--primary);
}

/* 활성 탭 하단 그라디언트 인디케이터 */
.main-tabs__tab.is-active::after {
  content: "";
  position: absolute;
  left: var(--sp-4);
  right: var(--sp-4);
  bottom: -1px;
  height: 3px;
  border-radius: 3px 3px 0 0;
  background: var(--gradient-primary);
}

.main-tabs__tab:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.app-main {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--sp-4) var(--sp-4) var(--sp-8);
}

/* 문서 풀블리드 뷰어(detail.html v3, base.html의 hide_app_chrome 플래그와 짝) —
   .app-main의 max-width/여백을 전부 걷어내 <main>이 뷰포트를 그대로 채우게 한다.
   .app-main과 결합 선택자(.app-main.app-main--flush, 명시도 0,2,0)로 써서 14. 반응형의
   .app-main { padding: ... } min-width 오버라이드(명시도 0,1,0, 소스 순서상 이 규칙보다
   뒤에 옴)에 밀리지 않도록 한다 — 그렇지 않으면 640px/1024px 이상에서 padding이 되살아난다. */
.app-main.app-main--flush {
  max-width: none;
  margin: 0;
  padding: 0;
}

.app-footer {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--sp-5) var(--sp-4) var(--sp-7);
  color: var(--text-faint);
  font-size: 12px;
  text-align: center;
}

.page {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

/* ---------- 5. 버튼 ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-5);
  border-radius: var(--radius-md);
  border: 1.5px solid transparent;
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease, opacity 150ms ease,
    box-shadow 150ms ease, transform 150ms ease;
  touch-action: manipulation;
}

.btn--primary {
  background: var(--gradient-primary);
  color: #fff;
  box-shadow: 0 1px 2px rgba(79, 70, 229, 0.25), 0 8px 18px rgba(79, 70, 229, 0.22);
}

.btn--primary:hover {
  background: var(--gradient-primary-hover);
  box-shadow: 0 1px 2px rgba(79, 70, 229, 0.3), 0 10px 22px rgba(79, 70, 229, 0.3);
}

.btn--primary:active {
  transform: translateY(1px);
}

.btn--secondary {
  background: var(--surface);
  color: var(--primary-dark);
  border-color: var(--line-strong);
  box-shadow: var(--shadow-xs);
}

.btn--secondary:hover {
  background: var(--primary-light);
  border-color: var(--primary-soft);
}

.btn--ghost {
  background: transparent;
  color: var(--text-light);
  padding: 0 var(--sp-3);
}

.btn--ghost:hover {
  background: var(--primary-light);
  color: var(--primary-dark);
}

/* 파괴적 액션(삭제 등) 버튼 — .btn--secondary와 동일한 셸이되 위험 색상 톤만 다르게.
   새 색 토큰 없이 기존 --danger* / --c-failed* 재사용. */
.btn--danger {
  background: var(--surface);
  color: var(--danger-dark);
  border-color: var(--c-failed-border);
  box-shadow: var(--shadow-xs);
}

.btn--danger:hover {
  background: var(--c-failed-bg);
  border-color: var(--danger);
}

@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover,
  .btn--secondary:hover {
    transform: translateY(-1px);
  }
}

.btn--block {
  width: 100%;
}

.btn--sm {
  min-height: 36px;
  padding: 0 var(--sp-4);
  font-size: 13px;
}

.btn:disabled,
.btn[aria-disabled="true"],
.detail-tabs__tab:disabled,
.detail-tabs__tab[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
  transform: none;
}

.btn__icon {
  display: inline-flex;
}

/* ---------- 6. 폼 ---------- */
.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}

.form-label {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
}

.form-label__optional {
  font-weight: 400;
  color: var(--text-faint);
}

.form-input {
  min-height: 48px;
  padding: 0 var(--sp-4);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 16px;
  color: var(--text);
  background: var(--surface);
  transition: border-color 150ms ease, box-shadow 150ms ease, background-color 150ms ease;
}

.form-input:hover {
  border-color: var(--line-strong);
}

.form-input:focus-visible {
  border-color: var(--primary);
  background: var(--surface);
  box-shadow: 0 0 0 4px var(--primary-light);
}

/* 체크박스 + 라벨 한 줄 배치(로그인 "로그인 정보 저장" 등) */
.form-check {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-5);
}

.form-check__input {
  width: 18px;
  height: 18px;
  accent-color: var(--primary);
  flex: none;
}

.form-check__label {
  font-size: 13.5px;
  color: var(--text-light);
  cursor: pointer;
}

/* ---------- 7. 알림 / 배너 ---------- */
.alert {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--c-failed-border);
  border-left: 4px solid var(--c-failed-text);
  background: var(--c-failed-bg);
  color: var(--c-failed-text);
  font-size: 14px;
  line-height: 1.6;
}

.alert--error {
  border-color: var(--c-failed-border);
  border-left-color: var(--c-failed-text);
  background: var(--c-failed-bg);
  color: var(--c-failed-text);
}

/* 성공 메시지(직원 추가/삭제/권한변경/비밀번호 변경 결과 등) — 기존 done 상태 토큰 재사용 */
.alert--success {
  border-color: var(--c-done-border);
  border-left-color: var(--c-done-text);
  background: var(--c-done-bg);
  color: var(--c-done-text);
}

.banner {
  padding: var(--sp-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--c-failed-border);
  border-left: 4px solid var(--c-failed-text);
  background: var(--c-failed-bg);
  color: var(--c-failed-text);
  font-size: 13.5px;
  line-height: 1.7;
}

.banner--warning {
  border-color: #f2d98c;
  border-left-color: #b8860b;
  background: #fff8e6;
  color: #7a5b00;
}

.banner__title {
  display: block;
  margin-bottom: var(--sp-1);
}

.banner__desc {
  color: inherit;
}

/* ---------- 8. 로그인 화면 ---------- */
.auth-wrap {
  position: relative;
  min-height: calc(100vh - 220px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6) 0;
}

/* account_password.html처럼 헤더/탭이 있는 일반 페이지 안에 auth-card를 배치할 때 쓰는
   변형 — 로그인 화면 전용의 전체높이 중앙정렬(min-height/padding)을 없애고 카드만
   가로 중앙 정렬한다. */
.auth-wrap--inline {
  min-height: 0;
  padding: 0;
  display: block;
}

.auth-wrap--inline .auth-card {
  margin: 0 auto;
}

.auth-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-7) var(--sp-6);
  overflow: hidden;
}

.auth-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 5px;
  background: var(--gradient-primary);
}

.auth-card__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: var(--gradient-primary);
  color: #fff;
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--sp-4);
}

.auth-card__eyebrow {
  margin: 0 0 var(--sp-2);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--primary);
}

.auth-card__title {
  font-size: 24px;
  margin-bottom: var(--sp-2);
}

.auth-card__desc {
  margin: 0 0 var(--sp-5);
  color: var(--text-light);
  font-size: 13.5px;
}

.auth-form {
  display: flex;
  flex-direction: column;
}

.auth-card .alert {
  margin-bottom: var(--sp-4);
}

/* 로그인 카드 하단의 최초 로그인 안내 문구 */
.auth-card__note {
  margin: var(--sp-5) 0 0;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
  color: var(--text-faint);
  font-size: 12.5px;
  line-height: 1.6;
  text-align: center;
}

/* ---------- 9. 녹음 화면 ---------- */
.record-view,
.status-view {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
}

/* v2 재설계로 하단 고정 플로팅 버튼(.record-fab-wrap)이 뷰포트 바닥 근처를 차지하므로,
   본문 콘텐츠(파일 업로드 패널 등)와 겹치지 않도록 여유 공간을 확보한다.
   v3: 상단 h1/제목입력/안내문구가 삭제되어 .file-upload-panel이 화면 맨 위로 바로
   붙으면서 여백이 허전해 보여, 상단 여백을 한 단계(--sp-3) 추가로 확보했다. */
.page--record {
  padding-top: var(--sp-3);
  padding-bottom: calc(var(--sp-8) + var(--sp-7));
}

/* 녹음 안내 문구(v2: 구 .record-panel 카드 제거에 따라 본문 상단으로 이동) */
.record-hint {
  margin: 0;
  color: var(--text-light);
  font-size: 13px;
  text-align: center;
}

/* .record-panel__status(카드 없이도 그대로 재사용 — record-fab-wrap 안에서 pill 형태로
   재배치된다, 아래 .record-fab-wrap 참고) */
.record-panel__status {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 28px;
}

.rec-indicator {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--danger);
  font-weight: 700;
  font-size: 13px;
}

.rec-indicator__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--danger);
  animation: rec-pulse 1.4s ease-in-out infinite;
}

@keyframes rec-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.rec-timer {
  font-variant-numeric: tabular-nums;
  font-size: 22px;
  font-weight: 800;
  color: var(--ink);
  letter-spacing: 0.02em;
}

.rec-btn {
  width: clamp(128px, 34vw, 176px);
  height: clamp(128px, 34vw, 176px);
  border-radius: 50%;
  border: none;
  background: var(--gradient-primary);
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  cursor: pointer;
  box-shadow: 0 2px 4px rgba(79, 70, 229, 0.3), var(--shadow-lg);
  transition: background 150ms ease, transform 150ms ease, box-shadow 150ms ease;
  touch-action: manipulation;
}

@media (hover: hover) and (pointer: fine) {
  .rec-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 10px rgba(79, 70, 229, 0.34), var(--shadow-lg);
  }
}

.rec-btn:active {
  transform: scale(0.97);
}

.rec-btn__label {
  font-size: 15px;
  font-weight: 700;
}

.rec-btn.is-recording {
  background: var(--gradient-danger);
  box-shadow: 0 0 0 10px rgba(220, 38, 38, 0.14), var(--shadow-lg);
  animation: rec-btn-ring 1.6s ease-out infinite;
}

.rec-btn.is-recording:hover {
  background: var(--gradient-danger);
}

@keyframes rec-btn-ring {
  0% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.28), var(--shadow-lg); }
  100% { box-shadow: 0 0 0 16px rgba(220, 38, 38, 0), var(--shadow-lg); }
}

/* 플로팅 녹음 버튼 래퍼(#rec-btn을 감싼다, v2 재설계) — 뷰포트 하단 중앙 고정.
   .refresh-fab와 같은 "하단 고정 액션" 계열 톤이다(.detail-toolbar는 v5부터 더 이상
   고정이 아니라 흐름 안 요소 — 12번 섹션 참고). */
.record-fab-wrap {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
}

/* record-panel__status(기존 클래스 그대로 재사용)를 카드 chrome 없이 작은 pill로 */
.record-fab-wrap .record-panel__status {
  min-height: 0;
  padding: 4px var(--sp-3);
  border-radius: var(--radius-full);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-xs);
  gap: var(--sp-3);
}

/* 기존 .rec-btn(128~176px 원형) 위에 추가되는 보조 클래스 — 컴팩트한 알약형으로 축소.
   .rec-btn 자체(및 .is-recording 등 기존 상태 클래스)는 record.js가 그대로 토글하므로
   변경하지 않는다. */
.rec-btn--fab {
  width: auto;
  height: auto;
  min-height: 56px;
  padding: 0 var(--sp-5);
  border-radius: var(--radius-full);
  flex-direction: row;
  gap: var(--sp-2);
  box-shadow: 0 2px 4px rgba(79, 70, 229, 0.3), var(--shadow-md);
}

.rec-btn--fab .rec-btn__icon svg {
  width: 20px;
  height: 20px;
}

.rec-btn--fab.is-recording {
  box-shadow: 0 0 0 8px rgba(220, 38, 38, 0.14), var(--shadow-md);
}

.upload-progress-wrap {
  width: 100%;
  max-width: 320px;
  margin: 0 auto;
}

.upload-progress {
  width: 100%;
  height: 8px;
  border-radius: var(--radius-full);
  border: none;
  overflow: hidden;
  accent-color: var(--primary);
}

.upload-progress::-webkit-progress-bar {
  background: var(--primary-light);
  border-radius: var(--radius-full);
}

.upload-progress::-webkit-progress-value {
  background: var(--gradient-primary);
  border-radius: var(--radius-full);
  transition: width 200ms ease;
}

/* ---------- 9b. 파일 업로드(기존 오디오 파일 선택) ---------- */
.upload-divider {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  color: var(--text-faint);
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.upload-divider::before,
.upload-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.file-upload-panel {
  background: var(--surface);
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  transition: border-color 150ms ease, background-color 150ms ease;
}

.file-upload-panel:hover {
  border-color: var(--primary-soft);
  background: var(--surface-alt);
}

.file-upload-panel__hint {
  margin: 0;
  color: var(--text-light);
  font-size: 13px;
  line-height: 1.6;
}

.file-upload-panel__controls {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

.file-upload-panel__filename {
  font-size: 13.5px;
  color: var(--text-light);
  overflow-wrap: anywhere;
}

.file-upload-panel__filename.has-file {
  color: var(--primary-dark);
  font-weight: 700;
}

/* 접근성(코드리뷰): 실제 <input type=file>은 .visually-hidden으로 시각적으로만 숨겨져
   있고(포커스는 가능) label 안에 중첩되어 있다. input이 키보드 포커스를 받으면
   :focus-within으로 이 label(버튼처럼 보이는 요소)에 포커스 링을 보여준다 — 전역
   :focus-visible 규칙(위 3번 섹션)과 동일한 값으로 통일. */
.file-upload-panel__pick-btn:focus-within {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ---------- 10. 처리 상태판(스테퍼) ---------- */
.step-track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-5);
}

.step {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  position: relative;
}

.step__marker {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid var(--line-strong);
  background: var(--surface);
  color: var(--text-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 800;
  transition: background-color 200ms ease, border-color 200ms ease, color 200ms ease,
    box-shadow 200ms ease;
}

.step__check {
  display: none;
}

.step__check::before {
  content: "\2713";
}

.step__label {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-light);
  transition: color 200ms ease;
}

.step.is-current .step__marker {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-light);
  box-shadow: 0 0 0 4px var(--primary-light);
  animation: step-current-pulse 1.8s ease-in-out infinite;
}

.step.is-current .step__label {
  color: var(--primary-dark);
  font-weight: 700;
}

@keyframes step-current-pulse {
  0%, 100% { box-shadow: 0 0 0 4px var(--primary-light); }
  50% { box-shadow: 0 0 0 7px var(--primary-light); }
}

.step.is-done .step__marker {
  border-color: transparent;
  background: var(--gradient-primary);
  color: #fff;
  box-shadow: none;
  animation: none;
}

.step.is-done .step__num {
  display: none;
}

.step.is-done .step__check {
  display: inline;
}

.step.is-done .step__label {
  color: var(--text);
}

.step.is-error .step__marker {
  border-color: var(--danger);
  background: var(--c-failed-bg);
  color: var(--danger);
  box-shadow: none;
  animation: none;
}

.step.is-error .step__label {
  color: var(--danger);
  font-weight: 700;
}

.queue-position {
  margin: 0;
  color: var(--text-light);
  font-size: 13.5px;
  text-align: center;
}

/* ---------- 11. 회의록 목록(카드 그리드) ---------- */
.meetings-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
}

.meetings-list__loading {
  padding: var(--sp-6);
  text-align: center;
  color: var(--text-light);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  animation: skeleton-pulse 1.6s ease-in-out infinite;
}

@keyframes skeleton-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* 카드 전체가 클릭 가능(상세로 이동)함을 알려주는 커서 — 실제 이동 가능 여부(done 상태)는
   meetings.js가 판단하며, 여기서는 시각적 어포던스만 통일해서 준다(정적 preview 포함).
   기본값은 done을 가정한 pointer이고, done이 아닌 상태는 아래
   .meeting-card[data-status]:not([data-status="done"]) 규칙이 default로 되돌린다.
   v3: "보기" 버튼(.meeting-card__view-link)을 마크업에서 완전히 제거하면서, 그 앵커의
   ::after로 카드 전체를 덮던 stretched-link 기법도 함께 제거했다(대상 앵커 자체가 이제
   DOM에 없다). 카드 클릭/키보드 이동은 meetings.js의 위임 클릭 + keydown 핸들러가
   담당한다. position:relative는 삭제 버튼(.meeting-card__delete,
   position:relative;z-index:2)의 스택 기준으로 여전히 필요해 유지한다. */
.meeting-card {
  position: relative;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease,
    opacity 200ms ease, background-color 200ms ease;
}

@media (hover: hover) and (pointer: fine) {
  /* transform: translateY 로 카드를 띄우면 hover 순간 카드가 커서 밑에서 벗어났다
     다시 들어오는 왕복이 생겨 커서가 손가락↔화살표로 깜빡인다. 레이아웃을 움직이지
     않는 그림자/테두리로만 피드백. */
  .meeting-card:hover {
    box-shadow: var(--shadow-md);
    border-color: var(--line-strong);
  }
}

/* done이 아닌 모든 카드(processing/pending/failed/uploaded) 시각 구분(v3 사용자 요청,
   v4 Low 코드리뷰로 범위 수정). done 카드와 확실히 다른 "손댈 수 없는" 느낌을 주기 위해
   저채도 배경 tint + 약한 투명도로 흐리게 하고, cursor:default로 클릭 불가 어포던스를
   준다. hover의 그림자/테두리 강조도 억제해 "떠 있는" 느낌을 없앤다 — done 카드만 hover
   시 반응하도록.
   v4(Low, 코드리뷰): 기존 :has(.status-badge[data-status="processing"|"pending"]) 선택자는
   failed/uploaded 카드를 놓쳤다 — JS(meetings.js)는 done 카드만 이동시키는데 failed 카드는
   여전히 pointer 커서로 보여 "클릭 가능해 보이는데 아무 일도 안 일어나는" 혼란스러운 UX
   버그가 있었다. renderCard()가 이제 .meeting-card 자신에도 data-status를 미러링해두므로
   (meetings.js 참고), :has()로 자식 배지를 참조하는 대신 카드 자신의 data-status를 직접
   보는 속성 선택자 `:not([data-status="done"])`로 바꿨다 — :has() 미지원 구형 브라우저에서도
   동일하게 동작하고, done이 아닌 모든 상태(향후 추가되는 상태 포함)를 자동으로 커버한다. */
.meeting-card[data-status]:not([data-status="done"]) {
  cursor: default;
  background: var(--surface-alt);
  opacity: 0.72;
}

.meeting-card[data-status]:not([data-status="done"]):hover {
  box-shadow: var(--shadow-sm);
  border-color: var(--line);
}

.meeting-card__main {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.meeting-card__title {
  font-size: 17px;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
}

.meeting-card__meta {
  margin: 0;
  display: flex;
  gap: var(--sp-3);
  flex-wrap: wrap;
  color: var(--text-light);
  font-size: 13px;
}

.meeting-card__side {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}

/* .role-badge(관리자 화면의 권한 배지)는 시각적으로 같은 pill 셸을 쓰므로 선택자를
   공유한다 — data-status(회의 상태)와 data-role(권한)은 의미가 다르니 값/색상 규칙은
   아래에서 각자 따로 정의한다. */
.status-badge,
.role-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 var(--sp-3);
  border-radius: var(--radius-full);
  border: 1px solid var(--line);
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
}

.status-badge::before,
.role-badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.status-badge[data-status="uploaded"] {
  color: var(--c-uploaded-text);
  background: var(--c-uploaded-bg);
  border-color: var(--c-uploaded-border);
}

.status-badge[data-status="pending"] {
  color: var(--c-pending-text);
  background: var(--c-pending-bg);
  border-color: var(--c-pending-border);
}

.status-badge[data-status="processing"] {
  color: var(--c-processing-text);
  background: var(--c-processing-bg);
  border-color: var(--c-processing-border);
}

.status-badge[data-status="done"] {
  color: var(--c-done-text);
  background: var(--c-done-bg);
  border-color: var(--c-done-border);
}

.status-badge[data-status="failed"] {
  color: var(--c-failed-text);
  background: var(--c-failed-bg);
  border-color: var(--c-failed-border);
}

/* 권한 배지(관리자 화면 전용) — uploaded/pending 토큰을 그대로 재사용해 새 색을 만들지 않는다 */
.role-badge[data-role="admin"] {
  color: var(--c-uploaded-text);
  background: var(--c-uploaded-bg);
  border-color: var(--c-uploaded-border);
}

.role-badge[data-role="staff"] {
  color: var(--c-pending-text);
  background: var(--c-pending-bg);
  border-color: var(--c-pending-border);
}

/* .meeting-card__side 우측 그룹(삭제, 관리자 전용) — v3(디자이너 정리).
   ------------------------------------------------------------
   v2에서는 삭제 버튼이 "보기" 버튼 옆에 나란히 있었으나, v3에서 "보기" 버튼 자체를
   제거하면서 이 그룹엔 이제 관리자 삭제 버튼 하나만 남는다(비관리자에게는 빈 래퍼).
   레이아웃(카드 우측 끝 정렬)엔 영향 없음 — .meeting-card__side가 여전히
   space-between으로 좌측 상태배지/우측 이 그룹을 분리한다. */
.meeting-card__side-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* .meeting-card__delete(목록 카드) + #btn-delete(v3부터 상세 툴바, detail.html 참고)가
   같은 "아이콘-only 위험색 원형 버튼" 톤을 공유한다. #btn-delete는 목록 카드 밖(상세
   페이지 하단 툴바)에서 쓰이므로 z-index:2/position:relative(stretched-link/카드 클릭
   레이어링 대비)는 무해하지만 실질적으로 의미 없다 — 두 컨텍스트에서 동일 스타일을
   한 곳에서 관리하기 위해 규칙만 공유한다. */
.meeting-card__delete,
#btn-delete {
  /* stretched-link가 사라진 v3에서도 카드 전체 클릭(개발자가 meetings.js에 새로
     구현할 예정, meetings.html 상단 주석 참고)보다 이 버튼이 우선 처리돼야 하므로
     position:relative + z-index:2를 유지한다 — 카드 자신(.meeting-card,
     position:relative)보다 위 레이어에 놓여 DOM 순서와 무관하게 클릭을 가로챈다. */
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--c-failed-border);
  border-radius: var(--radius-full);
  /* v3(사용자 요청): 기존에는 idle=저채도 회색(--text-faint)이라 눈에 잘 안 띄었다.
     항상 또렷하게 보이도록 기존 hover 모양(위험색 계열)을 idle 기본값으로 승격했다.
     목록/상세 모두 과하지 않도록 배경은 연한 tint(--c-failed-bg)만 쓰고, .btn--danger
     처럼 진한 배경/그림자는 쓰지 않아 톤 균형을 유지한다. */
  background: var(--c-failed-bg);
  color: var(--danger-dark);
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
}

.meeting-card__delete:hover,
.meeting-card__delete:focus-visible,
#btn-delete:hover,
#btn-delete:focus-visible {
  /* idle보다 한 단계만 더 진하게 — border만 더 또렷한 --danger로 올려 hover/focus
     피드백을 살짝 더 준다(배경/텍스트는 idle과 동일 톤 유지, 새 색 토큰 추가 없음). */
  background: var(--c-failed-bg);
  color: var(--danger-dark);
  border-color: var(--danger);
}

.empty-state {
  padding: var(--sp-7) var(--sp-5);
  text-align: center;
  background: var(--surface-alt);
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--text-light);
}

.empty-state__title {
  margin: 0 0 var(--sp-2);
  font-weight: 700;
  color: var(--text);
}

.empty-state__desc {
  margin: 0;
  font-size: 13.5px;
}

.empty-state a {
  color: var(--primary);
  font-weight: 700;
}

/* 페이지 하단에 떠 있는 새로고침 버튼과 카드 목록이 겹치지 않도록 여유 공간 확보 */
.page--meetings {
  padding-bottom: calc(var(--sp-8) + var(--sp-6));
}

/* #refresh-btn(meetings.js가 참조하는 hook, id는 유지)을 화면 하단 중앙에 떠 있는
   pill 버튼으로 재배치. 버튼·pill류는 --radius-full 사용 대상이라 1-c의 카드 라운드
   축소 규칙에서 제외된다. */
.refresh-fab {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 30;
  padding-left: var(--sp-6);
  padding-right: var(--sp-6);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-lg);
}

.refresh-fab:hover {
  transform: translateX(-50%) translateY(-2px);
}

.refresh-fab:active {
  transform: translateX(-50%) translateY(0);
}

/* ---------- 12. 상세 보기(v5: 상단 탭 [회의록][스크립트] + 문서 풀블리드 뷰어 + 흐름 툴바) ---------- */
/* 상세 페이지는 앱 헤더/푸터 없이(base.html hide_app_chrome) 상단 슬림 탭 바
   (.detail-topbar) → #detail-frame-wrap([회의록] 탭) 또는 #pane-script([스크립트] 탭) →
   .detail-toolbar(하단 버튼 바) 순으로 쌓이는 flex-column 한 장짜리 "문서 뷰어"
   레이아웃이다. 구 .detail-header(제목/날짜/업로더 메타 블록)는 v3에서 이미 제거됐고,
   v4에서는 좌상단에 떠 있던 .detail-back 오버레이 pill이 .detail-topbar 안 평범한 flex
   자식으로 편입됐다(더 이상 position:fixed 아님) — topbar가 실제 flex-column 흐름을
   차지하므로 아래 패널들은 자동으로 남은 높이만 받는다(별도 top reserve 변수 불필요).
   화면엔 숨긴 #detail-title(.visually-hidden — DOM/텍스트는 detail.js·서버 폴백용으로 유지)은
   .page--detail의 첫 자식으로 남아 있다(레이아웃엔 영향 없음).
   v5(모바일 하단 죽은 공간 버그 수정): .detail-toolbar를 마지막까지 position:fixed로
   화면 하단에 띄우고 --detail-toolbar-reserve라는 "추측 예약값"(모바일 176px)만큼
   .page--detail에 padding-bottom을 미리 깔아뒀었다. 실제 툴바 높이(버튼 1줄 ≈ 90px)가
   예약값보다 훨씬 작아 그 차이만큼 하단에 빈 회색 밴드가 생기는 버그였다. 이제
   .detail-toolbar는 .page--detail의 평범한 마지막 flex 자식(아래 참고)이라 자기 실제
   높이만 차지하고, 이 규칙에는 reserve 변수/padding-bottom이 더 이상 없다 — 하단
   safe-area 여백도 .detail-toolbar 자신이 책임진다(아래 .detail-toolbar 규칙 참고). */
.page--detail {
  display: flex;
  flex-direction: column;
  /* 뷰포트 높이에 '고정'한다(min-height가 아니라 height + overflow:hidden). 이렇게 해야
     페이지 자체는 절대 뷰포트를 넘지 않아 브라우저 창 스크롤바(메인 본문)가 생기지 않고,
     스크롤은 활성 패널(iframe 또는 #pane-script) 내부에서만 발생한다 — 스크롤바 이중
     노출 방지. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  padding: 0;
  gap: 0;
}

/* 상단 슬림 탭 바 — "← 목록" + [회의록][스크립트] 탭 + (편집 중일 때만) 편집중 배지를
   한 줄에 담는다. .page--detail의 평범한 flex 자식(flex:none, 고정 높이가 아니라 내용
   높이만큼만 차지)이라, v3의 .detail-back처럼 position:fixed 오버레이일 필요가 없다 —
   자체 배경/블러만 얹으면 상단에 자연스럽게 고정된 것처럼 보인다. */
.detail-topbar {
  flex: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  padding-top: calc(var(--sp-2) + env(safe-area-inset-top, 0px));
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
}

/* "← 목록" — v3에서는 position:fixed 오버레이 pill이었으나 v4는 .detail-topbar 안
   평범한 flex 자식이라 위치 지정이 필요 없다(모양/톤은 그대로 유지). */
.detail-back {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 var(--sp-4);
  border-radius: var(--radius-full);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-xs);
  color: var(--text-light);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  transition: background-color 150ms ease, color 150ms ease, box-shadow 150ms ease,
    border-color 150ms ease;
}

.detail-back:hover,
.detail-back:focus-visible {
  background: var(--primary-light);
  color: var(--primary-dark);
  border-color: var(--primary-soft);
}

/* 탭 바([role=tablist]) — 세그먼트(알약) 컨트롤 톤. 앱 전역 .main-tabs(밑줄 인디케이터,
   4번 섹션)와는 시각적으로 다른 컴포넌트다 — 이 페이지 전용 탭임을 클래스로도 분리한다.
   새 색 토큰은 추가하지 않고 기존 --surface-alt/--line/--primary* 토큰만 재사용한다. */
.detail-tabs {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-full);
}

.detail-tabs__tab {
  appearance: none;
  border: none;
  background: transparent;
  min-height: 34px;
  padding: 0 var(--sp-4);
  border-radius: var(--radius-full);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text-light);
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease, box-shadow 150ms ease;
}

.detail-tabs__tab:hover {
  color: var(--text);
}

/* 활성 탭 — aria-selected가 유일한 상태 소스(별도 .is-active 클래스 없음). detail.js는
   #tab-minutes/#tab-script의 aria-selected 속성만 토글하면 시각 스타일이 자동으로
   따라온다. */
.detail-tabs__tab[aria-selected="true"] {
  background: var(--surface);
  color: var(--primary-dark);
  box-shadow: var(--shadow-xs);
}

/* .detail-tabs__tab:disabled/[aria-disabled="true"] 흐림 스타일은 5번 섹션(버튼)의
   .btn:disabled 공용 규칙에 이미 포함돼 있다(선택자만 추가) — 여기서 중복 정의하지
   않는다. 편집 중 [스크립트] 탭 비활성화 정책은 detail.html 상단 주석 참고. */

.detail-frame-wrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  background: var(--surface);
}

.minutes-frame {
  display: block;
  width: 100%;
  height: 100%;
  /* min-height 고정을 두지 않는다 — 부모(.detail-frame-wrap, flex:1)가 확보한 높이(=뷰포트
     - topbar - 툴바여백)를 height:100%로 그대로 채우고, 회의록 본문이 그보다 길면 iframe
     내부에서만 스크롤된다. min-height:480px가 있으면 짧은 화면에서 페이지가 뷰포트를 넘어
     창 스크롤이 생겼다(스크롤바 2개 원인). */
  border: 0;
  background: #fff;
}

/* #btn-edit / #btn-download / #btn-save / #btn-cancel / #btn-revert /
   #btn-transcript-download(hook id 유지)를 담는 하단 툴바. v5부터는 .page--detail의
   마지막 flex 자식(흐름 안 요소)이라 더 이상 position:fixed가 아니다 — flex-shrink:0으로
   내용(버튼 줄바꿈 등)이 아무리 길어져도 찌그러지지 않고 자기 높이를 그대로 유지하며,
   위 형제(#detail-frame-wrap/#pane-script, 둘 다 flex:1; min-height:0)가 남은 높이를
   자동으로 채운다(구 .detail-actions-bar를 대체 — #detail-open-btn/#detail-download-btn은
   더 이상 쓰지 않는다). 배경은 이제 콘텐츠 위를 덮는 오버레이가 아니라 흐름상 아래에
   나란히 놓이므로 반투명+블러 대신 불투명 흰색으로 단순화했다(시각적으로 거의 동일).
   safe-area 하단 여백은 예전처럼 이 컴포넌트가 padding-bottom으로 직접 책임진다(.page--
   detail 쪽엔 더 이상 --detail-toolbar-reserve/padding-bottom이 없음). */
.detail-toolbar {
  flex-shrink: 0;
  /* position:relative + z-index로 positioned 승격 — 그렇지 않으면 형제 #detail-frame-wrap
     (position:relative)이 페인트 순서상 위에 그려져 툴바 상단 그림자(0 -8px 24px)가 흰
     iframe 배경에 덮여 [회의록] 탭에서만 안 보인다(스크립트 탭과 시각 불일치). 1이면 충분. */
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--line);
  box-shadow: 0 -8px 24px rgba(23, 24, 55, 0.06);
}

.detail-toolbar__group {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  justify-content: center;
  width: 100%;
  max-width: 560px;
}

.detail-toolbar__group .btn {
  flex: 1 1 0;
  min-width: 96px;
}

/* 관리자 전용 삭제 버튼을 일반 액션(편집/다운로드)과 시각적으로 구분하는 세로 구분선.
   .btn이 아니므로 위 flex:1 규칙 대상에서 자연히 제외된다(flex:none 고정폭). */
.detail-toolbar__divider {
  align-self: stretch;
  flex: none;
  width: 1px;
  min-height: 28px;
  background: var(--line);
}

/* divider + #btn-delete를 하나의 그룹으로 묶는다(정돈, v2). 이유 둘:
   1) 좁은 화면에서 #toolbar-view-actions가 flex-wrap될 때, divider가 .btn이 아니라서
      독립된 flex 아이템으로 남아 있으면 줄바꿈 경계에서 divider만 한 줄에 홀로 남고
      #btn-delete가 다음 줄로 넘어가는 "잘린 구분선" 모양이 생길 수 있었다. 이 그룹을
      하나의 flex 아이템으로 묶으면 래핑 시 항상 divider+버튼이 통째로 다음 줄로
      넘어간다(가장 좁은 320px 폭에서도 어색하지 않음).
   2) flex:none — 위 .detail-toolbar__group .btn 규칙(flex:1 1 0)이 편집/다운로드에
      적용해 두 버튼을 넓게 늘리는 것과 달리, 이 그룹(및 안의 #btn-delete)은 늘어나지
      않고 내용 크기만큼만 차지한다 — 파괴적 액션이 주요 액션과 동일한 시각적 비중을
      갖지 않도록 자연스럽게 위계를 분리한다. */
.detail-toolbar__danger-group {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: none;
}

/* v3: #btn-delete는 더 이상 .btn 클래스를 쓰지 않는다(detail.html에서
   class="meeting-card__delete"로 교체 — 목록 카드와 동일한 아이콘-only 원형 버튼).
   그래서 위 .detail-toolbar__group .btn / 아래 있었던 .btn 대상 규칙들이 이제 자동으로
   #btn-delete에는 적용되지 않는다(선택자가 .btn을 요구하므로) — 별도 flex:none 보정이
   필요 없어 옛 .detail-toolbar__danger-group .btn 규칙과, idle=회색/hover=위험색이던
   #toolbar-view-actions #btn-delete id 한정 override는 둘 다 제거했다. 톤 자체는 이제
   .meeting-card__delete, #btn-delete 공용 규칙(11번 섹션)이 항상 위험색으로 정의한다. */

/* ---------- 12b. 서식 도구모음(자유 서식 툴바, PM 설계·팀장 확정) ----------
   편집 모드(#toolbar-edit-actions)에서만 노출되는 서식 컨트롤 6종(굵게/기울임/
   글머리기호 목록/번호 매기기 목록/글자크기/글자색) + 서식 지우기(선택). 저장/취소/
   되돌리기와 시각적으로 구분하기 위해 #toolbar-edit-actions(.detail-toolbar__group--edit)를
   세로 2행으로 바꾼다:
     1행 .format-toolbar(role="toolbar") — 서식 컨트롤. 자체적으로도 flex-wrap이라
         좁은 화면에서는 이 1행 안에서 클러스터 단위(굵게+기울임 / 목록(불릿+번호) / 크기 /
         색 / 지우기)로 또 줄바꿈된다 — 각 클러스터의 실측 최대 폭(팔레트+커스텀피커 ≈
         180px, 목록 클러스터는 34px 버튼 2개 ≈ 74px)이 가장 좁은 지원 폭(320px, 좌우
         패딩 제외 시 ≈ 288px)보다 작으므로 별도 가로 스크롤(overflow-x) 없이 항상 한
         줄 안에 들어간다. 목록 버튼(#btn-bullet-list/#btn-numbered-list)은 신규 CSS
         클래스 없이 기존 .format-btn/.format-toolbar__seg/.format-toolbar__cluster를
         그대로 재사용한다(아이콘만 다름) — 이 섹션 스타일이 자동으로 적용되므로 별도
         규칙을 추가하지 않았다.
     2행 .detail-toolbar__actions — 기존 #btn-save/#btn-cancel/#btn-revert(로직/스타일
         변경 없음). 1행과는 상단 구분선(border-top)으로만 분리한다.
   새 색 토큰은 추가하지 않는다 — 팔레트 스와치는 인라인 style로 기존
   --ink/--danger-dark/--c-processing-text/--c-done-text/--text-light를 그대로
   재사용한다(:root에 새 --token 없음). */
.detail-toolbar__group--edit {
  flex-direction: column;
  gap: var(--sp-3);
  max-width: 680px;
}

.format-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  /* 버튼 라벨(B/I) 텍스트가 더블클릭 등으로 선택되는 것을 방지 — iframe 쪽 선택과
     헷갈리지 않도록. */
  user-select: none;
}

/* 선택 없음 시각 힌트(선택 사항, 개발자 몫): 편집 영역에 선택된 텍스트가 없을 때 JS가
   #format-toolbar[data-selection="empty"]를 세팅하면 컨트롤을 절반 투명도로 흐리게
   한다. pointer-events는 막지 않는다 — 완전 비활성화할지, 클릭 시 안내만 줄지는
   개발자 로직이 정한다. */
/* 목록(불릿/번호) 버튼은 블록 레벨 명령이라 텍스트 선택이 없어도(캐럿만 있어도) 정상
   동작하므로 흐림 대상에서 제외한다 — 흐리면 "비활성"으로 오해되는 미끼 어포던스가 됨. */
.format-toolbar[data-selection="empty"] .format-btn:not(#btn-bullet-list):not(#btn-numbered-list),
.format-toolbar[data-selection="empty"] .format-select,
.format-toolbar[data-selection="empty"] .fmt-color-swatch,
.format-toolbar[data-selection="empty"] .format-color-custom {
  opacity: 0.5;
}

.format-toolbar__cluster {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

/* 구분선(.detail-toolbar__divider 재사용) + 뒤따르는 클러스터를 하나의 flex 아이템으로
   묶는다 — .detail-toolbar__danger-group과 같은 이유(좁은 화면 줄바꿈 시 구분선만
   홀로 남는 "잘린 구분선" 방지). */
.format-toolbar__seg {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  flex: none;
}

/* 아이콘/글자 전용 소형 버튼(굵게 B / 기울임 I / 서식 지우기 아이콘). toggle 상태는
   aria-pressed로만 표현한다 — .detail-tabs__tab[aria-selected="true"]와 동일하게 별도
   .is-active 클래스 없이 속성 선택자로 스타일링. */
.format-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-light);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease,
    box-shadow 150ms ease;
}

.format-btn:hover {
  background: var(--primary-light);
  border-color: var(--primary-soft);
  color: var(--primary-dark);
}

.format-btn[aria-pressed="true"] {
  background: var(--primary-light);
  border-color: var(--primary);
  color: var(--primary-dark);
  box-shadow: inset 0 0 0 1px var(--primary);
}

.format-btn--bold {
  font-weight: 800;
}

.format-btn--italic {
  font-style: italic;
  font-family: Georgia, "Times New Roman", serif;
}

/* 글자 크기 셀렉트 — .form-input과 같은 톤이되 툴바에 맞춰 컴팩트(34px). 프리셋 값은
   detail.html 옵션 참고(12~72px, 상한 72px은 서버 정화가 최종 방어하지만 이 select
   자체가 그 밖의 값을 만들 수 없게 프리셋으로 제한한다). */
.format-select {
  min-height: 34px;
  max-width: 132px;
  padding: 0 var(--sp-2);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.format-select:hover {
  border-color: var(--primary-soft);
}

.format-select:focus-visible {
  border-color: var(--primary);
  box-shadow: 0 0 0 4px var(--primary-light);
}

/* 글자색 팔레트 — 클릭 즉시 적용(토글 아님, 그래서 aria-pressed 없음). 5색은 신규 색
   토큰이 아니라 detail.html에서 인라인 style로 기존 상태색 토큰을 재사용한다. */
.fmt-color-swatch {
  width: 20px;
  height: 20px;
  padding: 0;
  flex: none;
  border: 2px solid var(--surface);
  outline: 1px solid var(--line-strong);
  outline-offset: 0;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: transform 150ms ease, outline-color 150ms ease;
}

.fmt-color-swatch:hover {
  transform: scale(1.15);
}

.fmt-color-swatch:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* 자유 선택(네이티브 <input type="color">) — 점선 테두리로 실선 팔레트 스와치와
   "직접 고르기"임을 구분한다. */
.format-color-custom {
  width: 24px;
  height: 24px;
  padding: 2px;
  flex: none;
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
}

.format-color-custom:hover {
  border-color: var(--primary);
}

.format-color-custom:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

.format-color-custom::-webkit-color-swatch-wrapper {
  padding: 0;
}

.format-color-custom::-webkit-color-swatch {
  border: none;
  border-radius: 3px;
}

.format-color-custom::-moz-color-swatch {
  border: none;
  border-radius: 3px;
}

/* 2행(저장/취소/되돌리기) — 1행(.format-toolbar)과 구분되는 별도 그룹임을 상단
   구분선으로 표시한다. 버튼 자체의 flex:1/min-width 배치는 기존
   `.detail-toolbar__group .btn` 규칙(자손 선택자라 중첩 depth와 무관하게 적용됨)을
   그대로 상속받으므로 여기서 중복 정의하지 않는다. */
.detail-toolbar__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}

/* 좁은 화면(≤479px): 팔레트/셀렉트 간 간격을 한 단계 더 좁혀 한 줄에 더 잘 들어맞게
   한다(래핑 자체는 이미 모든 폭에서 동작 — 위 .format-toolbar 주석 참고). */
@media (max-width: 479px) {
  .format-toolbar {
    gap: var(--sp-1) var(--sp-2);
  }
  .format-select {
    max-width: 112px;
  }
}

.detail-toolbar__notice {
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  border: 1px dashed var(--line-strong);
  color: var(--text-light);
  font-size: 13px;
  text-align: center;
}

/* 편집 중 배지(#editing-badge) — v4에서는 .detail-topbar 안 평범한 flex 자식이다
   (margin-left:auto로 우측 정렬). v3의 .editing-badge--overlay(position:fixed 오버레이)는
   제거됐다 — topbar 자체가 이미 상단에 고정 배치돼 있어 배지만 따로 띄울 필요가 없다.
   색/모양은 원본 그대로. rec-pulse keyframe(9. 녹음 화면 섹션에서 정의)을 재사용한다 —
   새 keyframe 추가 없음. */
.editing-badge {
  flex: none;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 var(--sp-3);
  border-radius: var(--radius-full);
  background: var(--primary-light);
  color: var(--primary-dark);
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
}

.editing-badge__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary);
  animation: rec-pulse 1.4s ease-in-out infinite;
}

/* 편집 모드: iframe wrap 강조. 카드 테두리 대신 inset 링을 써서 레이아웃 크기(폭/높이)에
   영향을 주지 않는다 — .detail-frame-wrap은 flex:1로 뷰포트를 꽉 채우므로 border 두께가
   달라지면 리플로우가 생긴다. */
.detail-frame-wrap.is-editing {
  box-shadow: inset 0 0 0 3px var(--primary), inset 0 0 0 4px var(--primary-light);
}

/* 처리중 placeholder(#detail-processing, iframe 대신 표시) */
.detail-processing {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-6);
  color: var(--text-light);
  text-align: center;
}

.detail-processing__spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid var(--primary-light);
  border-top-color: var(--primary);
  animation: dp-spin 0.8s linear infinite;
}

@keyframes dp-spin {
  to {
    transform: rotate(360deg);
  }
}

.detail-processing__title {
  margin: 0;
  font-weight: 700;
  color: var(--text);
}

.detail-processing__desc {
  margin: 0;
  font-size: 13px;
  max-width: 380px;
}

/* [스크립트] 탭 패널(#pane-script, class="transcript-panel" 재사용) — v3에서는 문서
   아래로 펼치는 카드형 패널(margin+border+shadow)이었지만, v4는 탭 전환 시 #detail-
   frame-wrap과 자리를 통째로 맞바꾸는 형제 패널이라 동일하게 flex:1로 남은 세로 공간을
   그대로 채운다(카드 여백/테두리 제거 — 문서 iframe과 동급의 "풀블리드 패널"). 스크롤도
   이 패널 자체가 담당한다(내부 <pre>에 별도 max-height를 주지 않음 — 스크롤 컨텍스트를
   패널 하나로 통일해 이중 스크롤바를 피한다, .minutes-frame과 동일 원칙). 다운로드
   버튼(#btn-transcript-download)은 하단 툴바(#toolbar-script-actions)로 이동했고 탭
   라벨이 이미 "스크립트"를 표시하므로, 구 .transcript-panel__header/__title(패널 내부
   헤더 행)은 v4에서 제거했다 — 대신 h2#script-panel-title은 .visually-hidden으로만
   유지(스크린리더 전용). */
.transcript-panel {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  padding: var(--sp-5) var(--sp-4);
}

.transcript-panel__content {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.8;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}

/* 로딩/빈/에러 상태 — 패널 전체가 이미 풀블리드라 v3처럼 점선 카드로 감쌀 필요 없이
   #detail-processing과 같은 톤으로 패널 남은 높이 전체에서 중앙 정렬한다
   (.transcript-panel이 flex column이라 flex:1이 정상 동작). */
.transcript-panel__state {
  flex: 1;
  min-height: 240px;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-light);
  font-size: 13.5px;
  padding: var(--sp-6);
}

.transcript-panel__state--error {
  color: var(--c-failed-text);
}

/* ---------- 13. 관리자 화면 ---------- */
.page-desc {
  margin: var(--sp-1) 0 0;
  color: var(--text-light);
  font-size: 13.5px;
}

.admin-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.admin-panel__title {
  font-size: 17px;
  font-weight: 800;
  color: var(--ink);
}

/* 직원 추가 폼: 모바일은 세로 1열, 넓은 화면에서 반응형 규칙(14번 섹션)으로 다열 그리드로 확장 */
.emp-add-form {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin: 0;
}

.emp-add-form .form-field {
  margin-bottom: 0;
}

.emp-add-form__submit {
  align-self: end;
}

/* 직원 목록 테이블 — 모바일에서는 가로 스크롤로 대응(래퍼에 overflow-x), 데스크톱에서는
   전체 폭에 자연스럽게 맞춰진다. */
.emp-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
}

.emp-table {
  width: 100%;
  /* 액션 열이 4개 버튼(수정/비번초기화/권한변경/삭제)을 한 줄로 담아야 하므로 720px보다
     넉넉히 잡는다 — table-layout이 auto라 실제로는 콘텐츠가 더 넓으면 이 값을 넘어
     자연스럽게 커지고, 그 초과분은 .emp-table-wrap의 overflow-x(가로 스크롤)가 흡수한다. */
  min-width: 880px;
  border-collapse: collapse;
  font-size: 14px;
}

.emp-table th,
.emp-table td {
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.emp-table th {
  background: var(--surface-alt);
  color: var(--text-light);
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.emp-table tbody tr {
  transition: background-color 150ms ease;
}

.emp-table tbody tr:hover {
  background: var(--primary-light);
}

.emp-table tbody tr:last-child td {
  border-bottom: none;
}

/* 이메일 컬럼만 줄바꿈 허용(긴 이메일이 테이블 폭을 과하게 늘리지 않도록) */
.emp-table td[data-label="이메일"] {
  white-space: normal;
  overflow-wrap: anywhere;
  min-width: 200px;
}

/* 액션 열 버튼(수정/비번초기화/권한변경/삭제) — 사용자 지적: 4개가 되면서 줄바꿈되던
   문제를 고친다. flex-wrap을 wrap -> nowrap으로 바꿔 항상 한 줄에 유지하고(넘치는 폭은
   래퍼의 가로 스크롤이 흡수 — 위 .emp-table min-width 주석 참고), 버튼 자체도
   .btn--sm보다 한 단계 더 컴팩트하게(좌우 패딩/폰트 축소) 줄여 4개가 실제로 한 줄에
   들어오게 한다. white-space: normal을 더 이상 주지 않으므로(제거) 부모 .emp-table td의
   기본 nowrap을 그대로 물려받아 각 버튼 라벨 자체도 줄바꿈되지 않는다. */
.emp-table__actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: nowrap;
}

.emp-table__actions .btn {
  padding: 0 var(--sp-3);
  font-size: 12.5px;
}

/* ---------- 13b. 직원 수정 모달 ----------
   행마다 편집 폼을 인라인 전개하지 않고(33명+ 실 운영 목록에서 표 레이아웃이 깨지기
   쉽고 모바일 폭도 부족함) 페이지에 모달 하나만 두고 admin.js가 열 때마다 값을 채운다.
   .form-field/.form-input/.btn은 기존 직원 추가 폼과 동일한 톤을 그대로 재사용한다. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
}

.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(23, 24, 55, 0.45);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  border: none;
  cursor: pointer;
}

.modal__dialog {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - var(--sp-8));
  overflow-y: auto;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-6);
}

.modal__title {
  font-size: 18px;
  font-weight: 800;
  margin: 0 0 var(--sp-4);
  color: var(--ink);
}

/* 삭제 확인 모달(meetings.html/detail.html #delete-confirm-modal) 본문 — 재사용 가능한
   일반 설명 문단 + 대상 강조 + 인라인 오류. 새 색 토큰 없이 기존 --danger*/--c-failed*
   재사용(.alert--error/.btn--danger와 동일 톤). */
.modal__desc {
  margin: 0 0 var(--sp-5);
  color: var(--text-light);
  font-size: 14px;
  line-height: 1.6;
}

.modal__desc-target {
  display: block;
  margin-bottom: var(--sp-2);
  color: var(--ink);
  font-size: 15px;
}

.modal__error {
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--c-failed-border);
  background: var(--c-failed-bg);
  color: var(--c-failed-text);
  font-size: 13.5px;
  line-height: 1.5;
}

.emp-edit-form .form-field {
  margin-bottom: var(--sp-4);
}

.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}

/* ---------- 14. 반응형 (min-width 확장) ---------- */
@media (min-width: 640px) {
  .app-main {
    padding: var(--sp-5) var(--sp-5) var(--sp-8);
  }

  /* 스크립트 탭 패널: 풀블리드 폭이 넓어지는 만큼 좌우 여백을 넉넉히 넓혀준다
     (.transcript-panel__content의 max-width:760px 중앙 정렬과 함께 작동) */
  .transcript-panel {
    padding: var(--sp-6);
  }

  /* 직원 추가 폼: 2열 그리드로 확장 */
  .emp-add-form {
    grid-template-columns: repeat(2, 1fr);
    align-items: end;
  }
}

@media (min-width: 768px) {
  /* 처리 상태 스텝을 가로 트랙으로 전환 */
  .step-track {
    flex-direction: row;
    align-items: flex-start;
  }

  .step {
    flex: 1;
    flex-direction: column;
    gap: var(--sp-2);
    text-align: center;
  }

  .step:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 16px;
    left: calc(50% + 24px);
    width: calc(100% - 48px);
    height: 2px;
    background: var(--line);
    z-index: 0;
    transition: background-color 200ms ease;
  }

  .step.is-done:not(:last-child)::after {
    background: var(--primary);
  }

  .step__marker {
    position: relative;
    z-index: 1;
  }

  /* 회의록 목록: 카드 그리드로 전환 */
  .meetings-list {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  }
}

@media (min-width: 1024px) {
  .app-main {
    padding-left: var(--sp-6);
    padding-right: var(--sp-6);
  }

  /* 직원 추가 폼: 필드 4열 + 제출 버튼 한 줄 */
  .emp-add-form {
    grid-template-columns: repeat(4, 1fr) auto;
  }
}
