/* ============================================================
   Groupware AI Groupware - 디자인 시스템
   참고 레퍼런스: Microsoft 365(정돈된 카드/여백), Notion(미니멀/타이포 중심),
                 카카오워크(친근한 포인트 컬러, 명확한 아이콘)
   ============================================================ */

/* 데이터분석 모듈(data-analysis/)과 공유하는 디자인 토큰.
   font-family, 폰트 크기 체계, 배경색은 tokens.css에서 관리합니다.

   ⚠️ [성능] 예전에는 여기서 @import "tokens.css"로 불러왔는데, CSS의
   @import는 브라우저가 이 파일을 "다 받아서 파싱한 뒤에야" 비로소
   tokens.css를 요청하기 시작하는 직렬(순차) 로딩이라 첫 화면이 그려지는
   시점이 그만큼 늦어집니다. index.html에서 <link> 두 줄로 나눠 걸어
   두 파일을 동시에 받도록 바꿨습니다(순서는 <link> 순서대로 보장됩니다). */

/* ---------- 1. 디자인 토큰 (CSS 변수) ----------
   색상/간격/폰트 등을 한 곳에서 관리합니다.
   나중에 다크모드나 브랜드 컬러 변경 시 이 부분만 수정하면 전체에 반영됩니다.
   ⚠️ --font-family, --fs-*, --color-bg는 데이터분석 모듈과 공유하는
   값이라 tokens.css로 옮겼습니다(위 @import 참고). */
:root {
  /* [버그 수정] 입력창 등 네이티브 폼 컨트롤이 항상 라이트로 렌더링되도록
     명시적으로 선언합니다. index.html의 <meta name="color-scheme">만
     믿으면 OS가 다크모드인 사용자의 브라우저가 그 메타 태그를 다르게
     해석할 여지가 있어(예: 메타 태그가 아직 파싱되기 전 순간), 실제로
     적용되는 값을 CSS에서도 한 번 더 못박아 둡니다. 다크 테마에서는
     아래 [data-theme="dark"] 블록의 값이 이 값보다 우선 적용됩니다. */
  color-scheme: light;

  /* 색상 - 배경/표면 */
  --color-surface: #FFFFFF;      /* 카드, 사이드바 등 표면 색 */
  --color-border: #E1E4EA;       /* 구분선/테두리 (기존보다 살짝 차분하게) */

  /* 색상 - 텍스트 */
  --color-text: #1F2937;         /* Primary — 표준 값 그대로. 배경 대비 13.81:1 */
  --color-text-secondary: #5B6472; /* Secondary — 보조 텍스트(설명, 라벨). 5.63:1 */
  --color-text-caption: #6B7280;   /* Caption — 표준의 Secondary 값. 4.55:1 */
  --color-text-disabled: #D1D5DB;  /* Disabled — 표준 값 그대로 */
  --color-text-inverse: #FFFFFF; /* 어두운 배경 위 텍스트 */

  /* [TYPOGRAPHY STANDARD v1.0 — 색상만 조정한 이유]
     표준의 Caption(#9CA3AF)은 이 앱의 배경(#F7F8FA) 위에서 대비가
     2.39:1입니다. WCAG AA가 일반 텍스트에 요구하는 4.5:1에 크게 못 미쳐서,
     캡션은 저시력 사용자에게 사실상 안 보이는 글자가 됩니다. 표준의 큰
     글자 예외(18.66px 굵게 또는 24px 이상)에도 해당하지 않습니다(13px).

     그래서 세 단계를 한 칸씩 진하게 옮겼습니다.
       Primary   #1F2937  (표준 그대로)          13.81:1
       Secondary #5B6472  (기존 프로젝트 값 유지)  5.63:1
       Caption   #6B7280  (표준의 Secondary 값)   4.55:1
     "Primary > Secondary > Caption" 3단계 위계는 그대로 유지되면서
     전부 AA를 통과합니다. 표준 원안대로 Secondary를 #6B7280으로 두면
     Caption이 그보다 연해야 하는데, 그 순간 4.5:1을 넘길 방법이 없습니다.

     Disabled(#D1D5DB, 1.39:1)는 표준 값을 그대로 씁니다 — WCAG 1.4.3이
     비활성 상태 컴포넌트를 대비 요건에서 명시적으로 제외합니다. */

  /* 색상 - 브랜드/포인트
     메인 컬러: 딥 네이비 rgb(30,42,68) — 신뢰감/전문성
     보조 컬러: 웜 골드 rgb(212,163,115) — 포인트/강조 (버튼 등 넓은 면적엔 쓰지 않고
     사이드바 active 표시줄, 배지 등 작은 요소에만 사용) */
  --color-primary: #1E2A44;
  --color-primary-hover: #15202F;
  --color-primary-soft: #EDEFF3; /* primary의 연한 배경 버전 */
  --color-accent: #D4A373;
  --color-accent-hover: #B48B62;
  --color-accent-soft: #FAF4EE;  /* accent의 연한 배경 버전 */

  /* [접근성 - 다크모드 대비 감사 중 발견] :focus-visible 링 전용 토큰.
     처음 접근성 작업 때는 --color-accent를 그대로 썼는데, 계산해보니
     라이트 배경(#F7F8FA) 위에서 대비가 2.13:1로 WCAG의 UI 컴포넌트
     최소 기준(3:1)에 못 미쳤습니다(다크 배경에서는 7.87:1로 문제 없었음).
     테마마다 어울리는 색이 반대라(라이트엔 짙은 네이비가, 다크엔 밝은
     골드가 더 잘 보임) 전용 토큰으로 분리해 각 테마에서 3:1 이상을
     확보했습니다. */
  --color-focus-ring: var(--color-primary);

  /* 상태 색상 */
  --color-success: #1E9E64;
  --color-warning: #E8A400;
  --color-danger: #E5484D;
  /* 상태 색상의 옅은 배경 버전(배지 등에서 사용).
     [다크모드 준비] 예전엔 .badge-* 규칙 안에 직접 박혀 있던 값들이라,
     라이트/다크 전환 시 여기 한 곳만 재정의하면 되도록 토큰으로 옮겼습니다. */
  --color-success-soft: #E7F7EF;
  --color-warning-soft: #FEF3D6;
  --color-danger-soft: #FDEAEA;
  /* [업무 관리 Phase 4] "보류" 상태 전용 중립 회색 (완료/진행중/대기와 구분) */
  --color-hold: #6B6B6B;
  --color-hold-soft: #EEEEEE;
  /* [29단계] "폐기" 상태 전용 회색(보류보다 짙게, 더 이상 진행하지 않음을 표시) */
  --color-discarded: #8A8A8A;
  --color-discarded-soft: #E4E4E4;

  /* 모달/사이드바 뒤 어두운 배경(오버레이).
     [다크모드 준비] 다크 테마에서는 배경 자체가 이미 어두워 더 옅은 값이
     자연스러우므로, 고정값 대신 토큰으로 분리했습니다. */
  --color-overlay: rgba(0, 0, 0, 0.35);
  --color-overlay-strong: rgba(0, 0, 0, 0.4);

  /* 레이아웃 크기 */
  --sidebar-width: 240px;
  --topbar-height: 68px;

  /* ⚠️ --radius-*와 --shadow-*는 데이터분석 모듈과 공유하는 값이라
     css/tokens.css로 옮겼습니다. 여기 값이 기준이 되어 통합됐으므로
     그룹웨어 화면은 이전과 동일합니다. */

  /* 타이포그래피
     ⚠️ --font-family는 데이터분석 모듈과 공유하는 값이라 tokens.css로
     옮겼습니다. (원래 값: 외부 웹폰트 대신 시스템 폰트를 우선으로 하되,
     한글에 최적화된 폰트를 폴백에 포함) */

  /* ---------- 폰트 크기 체계 ----------
     --fs-h1 ~ --fs-caption 중 데이터분석 모듈과 값이 같은 9개
     (h1, h2, h3, body, table, input, button, desc-sm, caption)는
     tokens.css로 옮겼습니다. 그룹웨어 전용인 --fs-menu-side,
     --fs-menu-top만 아래 남겨둡니다.
     용도별로 크기를 한 곳에서 관리합니다. 데스크톱 기준 값이며,
     768px 이하(모바일)에서는 6. 반응형 섹션에서 한 단계씩 낮춥니다.
     용도            | 크기  | 굵기
     페이지 제목(H1)  | 28px | 700  → .topbar-title
     섹션 제목(H2)    | 22px | 700  → .modal-title, 게시글/문서 제목
     카드 제목(H3)    | 18px | 600  → .card-title
     일반 본문        | 15px | 400  → body
     테이블 본문      | 14px | 400  → .data-table
     입력창(Input)    | 15px | 400  → .login-input
     버튼             | 14px | 600  → .btn
     메뉴(좌측 사이드바)| 15px | 500  → .nav-item
     상단 메뉴        | 14px | 500  → .topbar-btn, 탭류 버튼
     작은 설명        | 13px | 400  → .list-item, .empty-state 등
     캡션/날짜        | 12px | 400  → .badge, 보조 캡션 텍스트         */
  --fs-menu-side: var(--fs-body-compact); /* 표준 Sidebar Menu 15px */
  --fs-menu-top: 14px;
}

/* ============================================================
   다크 모드 - 그룹웨어 전용 토큰
   ------------------------------------------------------------
   tokens.css의 [data-theme="dark"] 블록이 --color-bg(공유 토큰)를
   담당하고, 이 블록은 그룹웨어 전용 토큰(surface/text/accent 등)을
   담당합니다. <html data-theme="dark">가 붙으면 js/core/theme.js가
   자동으로 적용/해제합니다.

   ⭐ 색 고르는 기준 ⭐
   단순히 밝기만 뒤집지 않았습니다. 예를 들어 --color-primary(딥 네이비)를
   그대로 두면 어두운 배경 위에서 거의 안 보이므로, 다크모드에서는 더
   밝은 톤으로 올렸습니다. 반대로 상태색(success/warning/danger)은
   원래도 채도가 높아 배경이 어두워지면 오히려 더 선명해 보이므로 살짝만
   밝혔습니다. 보조색(웜 골드)은 원래 값이 다크 배경 위에서도 이미 잘
   보여서 그대로 유지했습니다.
   ============================================================ */
[data-theme="dark"] {
  /* [접근성/네이티브 UI] 체크박스·스크롤바 같은 브라우저 기본 컨트롤도
     다크 배경에 맞춰 자동으로 바뀌도록 합니다. index.html의
     <meta name="color-scheme">는 페이지 로드 시점 값이라 런타임 전환에는
     반응하지 않으므로, 실제 전환은 이 CSS 속성이 담당합니다. */
  color-scheme: dark;

  --color-surface: #1B212B;      /* 카드/사이드바 표면 */
  --color-border: #2B3240;

  --color-text: #E7EAF0;
  --color-text-secondary: #9AA3B2;
  /* 다크모드에서는 밝기 방향이 반대이므로, 라이트의 Caption/Disabled를
     그대로 쓰면 배경(#14181F)과 붙어버리거나 반대로 너무 튑니다.
     Caption은 Secondary보다 한 단계 어둡게, Disabled는 "눌러도 안 되는
     것"으로 읽힐 만큼만 낮춰 잡았습니다. */
  --color-text-caption: #7C8798;
  --color-text-disabled: #4A525E;
  --color-text-inverse: #FFFFFF; /* primary가 밝아졌으므로 대비는 그대로 유지됨 */

  /* 딥 네이비는 어두운 배경 위에서 거의 안 보여서, 다크모드 전용으로
     더 밝은 톤을 씁니다(라이트모드의 --color-primary-hover와 비슷한 계열). */
  --color-primary: #4A5C82;
  --color-primary-hover: #5B6E97;
  --color-primary-soft: #232A38;

  --color-accent: #D4A373;       /* 웜 골드는 그대로 — 어두운 배경에서도 잘 보임 */
  --color-accent-hover: #E0B48A;
  --color-accent-soft: #2E2620;
  /* 다크모드에서는 accent(웜 골드)가 primary보다 대비가 훨씬 좋습니다
     (7.87:1 vs 2.66:1) — 위 :root의 --color-focus-ring 설명 참고. */
  --color-focus-ring: var(--color-accent);

  --color-success: #34C77E;
  --color-warning: #F2B233;
  --color-danger: #F0656A;
  --color-success-soft: #14301F;
  --color-warning-soft: #332A12;
  --color-danger-soft: #3A1D1F;

  --color-hold: #A8AEBB;
  --color-hold-soft: #262B36;
  --color-discarded: #7B8290;
  --color-discarded-soft: #20242C;

  /* 배경 자체가 어두우므로 오버레이는 좀 더 짙게 — 카드/모달과의 경계가
     라이트모드만큼 뚜렷하게 유지되도록 합니다. */
  --color-overlay: rgba(0, 0, 0, 0.55);
  --color-overlay-strong: rgba(0, 0, 0, 0.6);

  /* ⚠️ 다크모드용 --shadow-*도 tokens.css의 [data-theme="dark"] 블록으로
     옮겼습니다(값은 그대로). */
}

/* ---------- 2. 전역 리셋 ---------- */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  height: 100%;
}

/* ---------- 2-1. 접근성 기본 레이어 ----------
   ⭐ 왜 필요한가 ⭐
   이 앱은 버튼 대부분이 `button { border:none; background:none; }`으로
   초기화되어 있어서, 키보드(Tab)로 이동할 때 "지금 어디에 있는지"를
   알려주는 브라우저 기본 표시가 사실상 지워져 있었습니다. 마우스를
   쓰는 사람에게는 문제가 없지만, 키보드만 쓰는 사용자나 스크린리더
   사용자에게는 화면을 조작할 수 없는 수준의 장벽이 됩니다.

   :focus-visible은 "키보드로 이동해서 포커스된 경우"에만 반응하는
   최신 표준 선택자라, 마우스 클릭 시에는 테두리가 나타나지 않습니다
   (예전 :focus로 하면 클릭할 때마다 테두리가 떠서 디자인이 지저분해집니다). */
:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* 마우스 클릭으로 생기는 기본 포커스 링은 숨깁니다(위 :focus-visible로 대체). */
:focus:not(:focus-visible) {
  outline: none;
}

/* [접근성] 화면에는 보이지 않지만 스크린리더는 읽어주는 텍스트용 클래스.
   아이콘만 있는 버튼의 실제 의미를 전달할 때 사용합니다. */
.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;
}

/* [접근성] OS에서 "동작 줄이기"를 켠 사용자(전정기관 장애 등으로 화면
   움직임에 어지러움을 느끼는 경우)에게는 모든 전환 효과를 사실상 끕니다.
   완전히 0으로 두면 일부 스크립트가 transitionend를 기다리다 멈출 수
   있어, 0에 가까운 아주 짧은 값으로 둡니다. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  font-family: var(--font-family);
  color: var(--color-text);
  background: var(--color-bg);
  font-size: var(--fs-body);
  /* [TYPOGRAPHY STANDARD v1.0] 전역 기본은 1.6입니다.
     표준이 Body에 지정한 1.8은 "긴 글"용이라, body 전역에 걸면 표 행·
     버튼·배지·메뉴까지 함께 늘어나 한 화면에 들어가는 정보량이 크게
     줄어듭니다. 표준도 Caption/Body Small에는 1.6을 지정하고 있으므로
     전역은 1.6, 긴 글은 .reading-body(아래)로 1.9를 적용합니다. */
  line-height: var(--lh-ui);
  /* [TYPOGRAPHY STANDARD v1.0] 숫자는 폭이 고정된 자형을 씁니다.
     근태 시각·결재 건수·용량처럼 세로로 나열되는 숫자가 자리마다
     들쭉날쭉하지 않게 정렬됩니다. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

/* [TYPOGRAPHY STANDARD v1.0] 긴 글(게시글 본문·공지·전자결재 본문) 전용.
   ⭐ 왜 전역이 아니라 클래스인가 ⭐
   표준의 "Body 최대 폭 760~860px"과 "line-height 1.8 이상"은 읽기 위한
   기준입니다. 대시보드·칸반·표처럼 넓게 훑어야 하는 화면에 그대로 걸면
   내용이 가운데 좁게 몰리고 세로로 길어져서 오히려 쓰기 나빠집니다.
   그래서 실제로 "읽는" 영역에만 붙입니다. */
.reading-body {
  font-size: var(--fs-body);
  line-height: var(--lh-reading);
  max-width: var(--content-max-width);
}

ul {
  list-style: none;
}

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

a {
  color: inherit;
  text-decoration: none;
}

/* ---------- 3. 전체 레이아웃 (사이드바 + 메인) ---------- */
#app {
  display: flex;
  height: 100vh;
  overflow: hidden;
}

/* 로그인 전에는 앱 전체를 화면에서 숨깁니다. */
#app.app-hidden {
  display: none;
}

/* ---------- 3-1. 로그인 화면 ----------
   중앙에 카드 하나만 떠 있는 아주 단순한 형태 (Notion 로그인 느낌) */
.login-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background: var(--color-bg);
  padding: var(--space-lg);
}

.login-card {
  width: 100%;
  max-width: 340px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-top: 3px solid var(--color-accent); /* 보조색(골드) 포인트 라인 */
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 32px 28px;
  display: flex;
  flex-direction: column;
}

.login-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  font-weight: 700;
  font-size: var(--fs-h2);
  margin-bottom: 28px;
}

/* 로그인 카드(최대 340px, 좌우 패딩 28px) 안에서 자연스러운 크기로 보이도록
   너비를 고정하고 높이는 원본 비율에 맞춰 자동으로 계산합니다. */
.login-logo-img {
  width: 230px;
  max-width: 100%;
  height: auto;
  display: block;
}

/* 로고 원본이 투명 배경 + 남색(#1E2A44) 글자라 라이트 모드에서는 그대로
   써도 되지만, 다크 모드(짙은 배경)에서는 남색 글자가 거의 보이지 않으므로
   흑백화(grayscale) 후 반전(invert)해서 밝은 회색/흰색으로 바꿔줍니다. */
[data-theme="dark"] .login-logo-img {
  filter: grayscale(1) invert(1) brightness(1.3);
}

.login-label {
  /* [TYPOGRAPHY STANDARD v1.0] Input Label 14 / 500 */
  font-size: var(--fs-input);
  font-weight: var(--fw-medium);
  color: var(--color-text-secondary);
  margin-bottom: 6px;
  margin-top: var(--space-md);
}

.login-input {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  /* [세로폭 축소] 이전 FHD Typography v2.0에서 글자를 14→16px로 키우며
     상하 여백도 12px까지 늘렸는데, 실사용 결과 입력창/선택창이 전반적으로
     너무 넓어 보인다는 피드백에 따라 9px/12px로 다시 줄였습니다. 글자
     크기(--fs-input)는 그대로 두고 여백만 줄여서, 가독성은 유지하면서
     세로폭만 컴팩트하게 만듭니다(이 값 하나가 앱 전체 모든
     input/select/textarea의 높이 기준이므로, 여기 한 곳만 고치면
     전체에 일괄 반영됩니다). */
  padding: 9px 12px;
  font-size: var(--fs-input);
  line-height: var(--lh-ui);
  font-family: inherit;
  outline: none;
}

/* [세로폭 축소 2단계] 위 padding 조정 이후에도 <input>/<select>(한 줄로
   끝나는 필드)가 여전히 넓어 보인다는 피드백이 있어 원인을 다시 살펴보니,
   --lh-ui(1.6)는 애초에 "Caption/Body Small처럼 여러 줄 읽는 글"을 위한
   값이라 한 줄짜리 입력창에는 과합니다(글자 위아래에 필요 이상의 여백이
   생김). 이미 있던 --lh-tight(1.2, "칩·배지처럼 한 줄로 끝나는 요소"용
   토큰)를 <input>/<select>에도 적용해 세로폭을 더 줄입니다. <textarea>는
   여러 줄을 읽어야 하므로 --lh-ui를 그대로 둡니다("내용" 등 긴 글은
   지금처럼 넉넉한 줄간격이 필요). */
input.login-input,
select.login-input {
  line-height: var(--lh-tight);
}

.login-input:focus {
  border-color: var(--color-primary);
}

.login-error {
  color: var(--color-danger);
  font-size: var(--fs-caption);
  min-height: 16px;
  margin-top: 10px;
}

/* [계정 잠금 - ④] 로그인 실패 반복으로 계정이 잠겨 429가 내려온 경우,
   단순 "아이디/비밀번호 오류"(빨강)와 구분되도록 경고색으로 보여줍니다. */
.login-error.login-error-locked {
  color: var(--color-warning);
  font-weight: 600;
}

.login-submit {
  margin-top: var(--space-sm);
  justify-content: center;
  /* [광학 보정] --btn-text-nudge를 여기에도 적용합니다(높이 22px 불변). */
  padding: calc(11px + var(--btn-text-nudge)) 11px max(0px, calc(11px - var(--btn-text-nudge)));
  font-size: var(--fs-button);
}

/* ----- 사이드바 ----- */
.sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  height: 100%;
  transition: transform 0.25s ease;
  z-index: 100;
}

.sidebar-header {
  height: var(--topbar-height);
  display: flex;
  align-items: center;
  justify-content: center; /* 로고를 좌우로도 중앙 정렬 */
  padding: 0 16px;
  border-bottom: 1px solid var(--color-border);
  cursor: pointer; /* 클릭하면 대시보드로 이동 (js/core/app.js) */
}

.logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  font-weight: 700;
  font-size: 16px;
  width: 100%;
}

/* 사이드바 폭(240px, 좌우 패딩 16px)에 맞춰 폭을 고정하고 높이는 원본
   비율대로 자동 계산합니다. */
.sidebar-logo-img {
  width: 170px;
  max-width: 100%;
  height: auto;
  display: block;
}

/* 로그인 로고와 동일한 이유로 다크 모드에서 사이드바 로고도 밝게 반전합니다. */
[data-theme="dark"] .sidebar-logo-img {
  filter: grayscale(1) invert(1) brightness(1.3);
}

.nav-menu {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-md) var(--space-sm);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--fs-menu-side);
  font-weight: 500;
  cursor: pointer;
  margin-bottom: 2px;
  transition: background 0.15s ease, color 0.15s ease;
}

.nav-item:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

.nav-item.active {
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font-weight: var(--fw-semibold); /* [STANDARD] Sidebar Active 15 / 600 */
  box-shadow: inset 3px 0 0 var(--color-accent); /* 보조색(골드) 포인트 표시줄 */
}

.nav-item .nav-icon {
  font-size: 16px;
  width: 20px;
  text-align: center;
}

/* 사이드 메뉴 그룹 구분선
   - 공지사항~데이터분석(그룹웨어) / 조직도~근태(HR) / 공휴일관리~관리자(관리 기능)
     세 그룹을 시각적으로 나눠주는 얇은 선입니다. */
.nav-divider {
  height: 1px;
  margin: 10px 14px;
  background: linear-gradient(
    to right,
    transparent,
    var(--color-border) 15%,
    var(--color-border) 85%,
    transparent
  );
}

.sidebar-footer {
  border-top: 1px solid var(--color-border);
  padding: var(--space-md);
}

.user-mini {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px;
  border-radius: var(--radius-sm);
}

.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-primary);
  color: var(--color-text-inverse);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: var(--fs-desc-sm);
  flex-shrink: 0;
}

.user-mini-name {
  font-size: var(--fs-desc-sm);
  font-weight: 600;
}

.user-mini-role {
  font-size: var(--fs-caption);
  color: var(--color-text-secondary);
}

/* ----- 메인 영역(상단바 + 본문) ----- */
.main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0; /* 내부 콘텐츠가 넘칠 때 레이아웃 깨짐 방지 */
  height: 100%;
  /* [모바일 실사용 버그 수정] index.html의 viewport-fit=cover로 화면이
     노치·홈 인디케이터 영역까지 확장되는데, 이 영역을 피하는 처리가
     없었습니다. 상단바(topbar)가 이 컨테이너의 첫 자식이라 그대로 두면
     노치 기기에서 메뉴 제목/아이콘이 노치에 가려질 수 있습니다. env()는
     노치가 없는 기기에서는 0이라 대부분의 기기에는 영향이 없습니다. */
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  box-sizing: border-box;
}

.topbar {
  height: var(--topbar-height);
  flex-shrink: 0;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  display: flex;
  align-items: center;
  padding: 0 16px;
  gap: var(--space-md);
}

.topbar-title {
  font-weight: 700;
  /* [폰트 계층 교정] 상시 노출되는 68px 상단바 안의 모듈 이름표라
     "메인 제목"(--fs-h1, 30px)이 아니라 "페이지 제목"(--fs-h2, 24px)
     등급이 맞습니다. --fs-h1은 게시글 본문의 큰 표제처럼 실제
     "메인 제목"인 경우에만 씁니다(예: data-analysis 팝업 헤더). */
  font-size: var(--fs-h2);
  white-space: nowrap;
}

/* 헤더 제목 옆에 모듈이 채워 넣는 영역 (예: 대시보드의 출근/퇴근 버튼).
   flex:1로 남은 공간을 차지해서 topbar-actions는 항상 오른쪽 끝에 고정됩니다. */
.topbar-extra {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
  overflow-x: auto; /* 그래도 공간이 모자라면 줄바꿈 대신 가로 스크롤 (안전장치) */
}

/* topbar-extra 안의 버튼(대시보드의 출근/퇴근/카드관리 등) 공통 스타일.
   white-space:nowrap + flex-shrink:0으로 버튼이 눌려 찌그러지면서
   글자가 버튼 안에서 두 줄로 줄바꿈되는 것을 막습니다. 실제로 좁아서
   다 못 들어가면(아래 모바일 미디어쿼리) 버튼 안의 글자(.btn-label)를
   숨기고 아이콘만 남겨서 한 줄에 들어가게 합니다. */
.topbar-btn {
  padding: 6px 12px;
  font-size: var(--fs-menu-top);
  font-weight: 500;
  white-space: nowrap;
  flex-shrink: 0;
}

.topbar-actions {
  display: flex;
  gap: var(--space-xs);
}

.icon-btn {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  /* ⚠️ 여기에 line-height:1을 넣지 마세요 — .btn과 같은 이유로 글자(이모지)가
     위로 치우칩니다(자세한 경위는 css/tokens.css의 --btn-line-height 주석). */
  color: var(--color-text-secondary);
}

.icon-btn:hover {
  background: var(--color-bg);
}

/* [알림] 🔔 버튼 위에 표시하는 안 읽은 개수 배지(js/core/notifications.js가 동적으로 생성) */
.notif-count-badge {
  position: absolute;
  top: -2px;
  right: -2px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--color-danger);
  color: var(--color-text-inverse);
  font-size: var(--fs-micro);
  font-weight: 700;
  align-items: center;
  justify-content: center;
}

/* [알림] 알림 목록 안, 안 읽은 항목 앞에 표시하는 작은 점 */
.notif-unread-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
  flex-shrink: 0;
  margin-top: 6px;
}

/* ── 알림 팝오버 패널 (v1.37.0) ─────────────────────────────────────────
   공용 .modal-overlay 를 쓰지 않는 독립 드롭다운입니다.
   벨 버튼 하단 / 우측 정렬로 위치하며, JS가 top/left를 동적으로 계산합니다.
   ──────────────────────────────────────────────────────────────────────── */
.notif-popover {
  position: fixed;
  z-index: 300;
  width: 440px;
  max-height: calc(100vh - 80px);       /* 헤더 높이 + 여백을 제외한 최대 높이 */
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 32px rgba(0,0,0,.18);
  overflow: hidden;

  /* 열릴 때 위에서 부드럽게 내려오는 transition */
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity .15s ease, transform .15s ease;
  pointer-events: none;
}
.notif-popover--open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* 헤더 */
.notif-popover__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: 12px 16px;
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
}
.notif-popover__title {
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  margin: 0;
  flex-shrink: 0;
}
.notif-popover__header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-left: auto;
}
.notif-popover__filter {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-desc-sm);
  color: var(--color-text-secondary);
  cursor: pointer;
  white-space: nowrap;
}
.notif-popover__close {
  font-size: 14px;
  color: var(--color-text-tertiary);
  line-height: 1;
}
.notif-popover__close:hover {
  color: var(--color-text-primary);
}

/* 본문 (스크롤 영역) */
.notif-popover__body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1;
  padding: 6px 0;
}

/* 로딩 / 빈 상태 */
.notif-popover__loading,
.notif-popover__empty {
  padding: 32px 20px;
  text-align: center;
  font-size: var(--fs-desc-sm);
  color: var(--color-text-tertiary);
}

/* ── 알림 카드 행 ─────────────────────────────
   아이콘 | 제목(1줄 말줄임) + 요약(1줄 말줄임) | 날짜 + dot
   ──────────────────────────────────────────── */
.notif-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 16px;
  cursor: pointer;
  border-bottom: 1px solid var(--color-border-subtle, var(--color-border));
  transition: background .1s;
}
.notif-card:last-child { border-bottom: none; }
.notif-card:hover,
.notif-card:focus-visible {
  background: var(--color-hover, rgba(0,0,0,.04));
  outline: none;
}
.notif-card--unread {
  background: var(--color-primary-subtle, rgba(var(--color-primary-rgb, 59,130,246),.06));
}
.notif-card--unread:hover {
  background: var(--color-primary-subtle-hover, rgba(var(--color-primary-rgb, 59,130,246),.12));
}

/* 아이콘 */
.notif-card__icon {
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
  margin-top: 1px;
}

/* 텍스트 영역 */
.notif-card__body {
  flex: 1;
  min-width: 0;
}
.notif-card__title {
  margin: 0;
  font-size: var(--fs-body-compact);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}
.notif-card--unread .notif-card__title {
  font-weight: var(--fw-bold);
}
.notif-card__summary {
  margin: 2px 0 0;
  font-size: var(--fs-desc-sm);
  color: var(--color-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.35;
}

/* 날짜 + 읽음 dot */
.notif-card__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  flex-shrink: 0;
  min-width: 80px;
}
.notif-card__date {
  font-size: var(--fs-micro);
  color: var(--color-text-tertiary);
  white-space: nowrap;
  line-height: 1.3;
}
.notif-card__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
  flex-shrink: 0;
}

/* 모바일: 너비를 화면에 맞춤 */
@media (max-width: 480px) {
  .notif-popover {
    width: calc(100vw - 16px);
  }
}

/* 모바일 햄버거 버튼은 데스크톱에서는 숨김 */
.menu-toggle {
  display: none;
}

.content {
  flex: 1;
  overflow-y: auto;
  padding: var(--content-padding, 24px);
}

/* 데이터분석(AutoDashboard 임베드)처럼 본문 영역을 꽉 채워야 하는 모듈에서
   사용하는 래퍼입니다. .content의 padding을 상쇄해 iframe이 남은 화면
   공간을 그대로 채우도록 합니다. */
.full-bleed-frame-wrap {
  margin: calc(var(--content-padding, 24px) * -1);
  height: calc(100% + (var(--content-padding, 24px) * 2));
}

.full-bleed-frame-wrap iframe {
  width: 100%;
  height: 100%;
  border: none;
  display: block;
}

/* ---------- 4. 공용 컴포넌트: 카드 ----------
   대시보드/게시판/HR 등 거의 모든 모듈이 이 카드 스타일을 재사용합니다. */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 20px;
  box-shadow: var(--shadow-sm);
}

/* 협업 게시판의 카드형 목록(노션 갤러리 보기 스타일)에 쓰이는 호버 효과 */
.board-card {
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.board-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--color-primary);
}

.card-title {
  font-size: var(--fs-h3);
  font-weight: 600;
  margin-bottom: var(--space-md);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.grid {
  display: grid;
  gap: var(--space-lg);
}

.grid-cols-3 {
  grid-template-columns: repeat(3, 1fr);
}

.grid-cols-2 {
  grid-template-columns: repeat(2, 1fr);
}

/* ---------- 4-1. 대시보드 카드 크기 통일 ----------
   대시보드에는 성격이 다른 카드(숫자 요약 / 문서 목록 / 안내 문구 / 커스텀 메모)가
   섞여 있어 내용 길이에 따라 카드 높이가 제각각이었습니다. 대시보드 화면에서만
   .card에 고정 높이를 주고, 내용이 넘치는 카드는 내부 목록 영역만 스크롤되도록 해
   전체 카드 크기를 통일합니다. (다른 화면의 .card는 이 클래스를 쓰지 않으므로
   영향받지 않습니다.)
   [41단계] "모든 카드 크기를 결재 대기와 동일하게" 요청에 따라, 문서 목록형/
   안내문구형/커스텀 카드의 높이도 결재 대기 카드(.dash-card-summary)와
   똑같이 맞췄습니다. 이제 .dash-card-summary는 정렬 방식만 다르고 높이는
   .dash-card와 같습니다.
   [42단계] "모든 카드 세로폭 20% 확대" 요청에 따라 161px → 193px로 높였습니다
   (161 * 1.2 ≈ 193). */
.dash-card {
  height: 193px;
  display: flex;
  flex-direction: column;
}

.dash-card .card-title {
  flex-shrink: 0;
}

.dash-card-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* 숫자 요약형 카드(결재 대기, 업무 KPI 등)는 내용이 짧으므로 세로 중앙 정렬합니다. */
.dash-card-summary {
  align-items: flex-start;
  justify-content: center;
}

/* ---------- 4-2. 대시보드 카드 드래그 정렬 ----------
   카드 "전체"가 아니라 카드 우측 상단의 작은 그립(⠿) 아이콘에만
   draggable을 걸어둡니다(js/modules/dashboard.js의 enableCardDragReorder
   참고). 그래서 카드를 클릭했을 때의 화면 이동이나, 커스텀 카드의
   수정/삭제 버튼 클릭과 절대 겹치지 않습니다.
   그립은 각 카드 콘텐츠(아이콘/제목/버튼)가 시작되는 좌측 상단이 아니라
   반대편인 우측 상단 모서리에 두어, 어떤 카드 종류든 기존 내용과
   부딪히지 않는 자리를 확보했습니다. */
#dashboard-main-grid > div,
#custom-cards-grid > div {
  position: relative;
}

.card-drag-handle {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  font-size: 14px;
  line-height: 1;
  color: var(--color-text-secondary);
  cursor: grab;
  opacity: 0.32;
  transition: opacity 0.15s ease, background 0.15s ease;
  z-index: 2;
  user-select: none;
}

#dashboard-main-grid > div:hover .card-drag-handle,
#custom-cards-grid > div:hover .card-drag-handle {
  opacity: 0.9;
  background: var(--color-bg);
}

.card-drag-handle:active {
  cursor: grabbing;
}

/* 드래그 중인 카드는 반투명하게 표시해 "지금 옮기고 있다"는 걸 보여줍니다. */
#dashboard-main-grid > div.dragging,
#custom-cards-grid > div.dragging {
  opacity: 0.4;
}

/* 마우스가 올라가 있는(드롭하면 그 자리로 들어갈) 카드에 점선 표시. */
#dashboard-main-grid > div.drag-over,
#custom-cards-grid > div.drag-over {
  outline: 2px dashed var(--color-primary);
  outline-offset: 2px;
}

/* 공용 버튼 */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* [버튼 크기 통일] padding/line-height를 공유 토큰으로 옮겨 데이터분석
     모듈의 .btn과 항상 같은 값을 갖도록 했습니다(css/tokens.css). */
  padding: var(--btn-padding);
  line-height: var(--btn-line-height);
  border-radius: var(--radius-sm);
  font-size: var(--fs-button);
  /* [굵기] Pretendard Bold. 제공된 폰트가 Regular(400)/Bold(700) 두 종뿐이라
     700이 Bold 파일에 정확히 대응합니다(중간값 500·600을 쓰면 브라우저가
     둘 중 하나로 밀어내므로 의도가 흐려집니다 — tokens.css의 @font-face
     범위 선언 주석 참고). */
  font-weight: var(--fw-bold);
  background: var(--color-bg);
  color: var(--color-text);
}

.btn:hover {
  filter: brightness(0.97);
}

/* [개선 - 공용 액션 버튼] 조직도 등에서 선택된 항목이 없을 때 상단 공용
   버튼(하위조직/수정/삭제)을 비활성화 상태로 보여주기 위한 스타일입니다. */
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: none;
}

.btn:disabled:hover {
  filter: none;
}

/* [42단계] "작성" 계열 공용 버튼(공지 작성/새 글쓰기/조직 추가/새 기안/
   근태 추가/계정 추가 등)을 다른 버튼과 확실히 구분하기 위해
   배경을 네이비(--color-primary) 대신 기존에 있던 보조색(골드, --color-accent)으로
   바꿨습니다. 골드 배경은 명도가 중간이라 흰 글자는 대비가 부족해서, 글자색은
   기존에 배경으로 쓰던 네이비(--color-primary)를 그대로 가져와 씁니다. */
.btn-primary {
  background: var(--color-accent);
  color: var(--color-primary);
}

.btn-primary:hover {
  background: var(--color-accent-hover);
}

/* [42단계] 출근 버튼은 "작성/추가" 계열 액션이 아니므로, 위에서 바꾼 골드
   배경 대상에서 제외하고 원래의 네이비 배색을 그대로 유지합니다. */
#attendance-checkin-btn.btn-primary {
  background: var(--color-primary);
  color: var(--color-text-inverse);
}

#attendance-checkin-btn.btn-primary:hover {
  background: var(--color-primary-hover);
}

/* 상태 뱃지 (예: 결재중, 완료, 대기) */
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: var(--fs-caption);
  font-weight: 700;
}

.badge-primary { background: var(--color-primary-soft); color: var(--color-primary); }
.badge-success { background: var(--color-success-soft); color: var(--color-success); }
.badge-warning { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-danger  { background: var(--color-danger-soft); color: var(--color-danger); }
/* [업무 관리 Phase 4] "보류" 상태 전용 색상 (완료/진행중/대기와 구분되는 중립 회색) */
.badge-hold    { background: var(--color-hold-soft); color: var(--color-hold); }
/* [29단계] "폐기" 상태 전용 색상 (보류보다 짙은 회색으로, 더 이상 진행하지 않음을 표시) */
.badge-discarded { background: var(--color-discarded-soft); color: var(--color-discarded); text-decoration: line-through; }

/* [레이아웃] 전자결재 "문서 유형" select — 기본 .login-input(padding
   12px/14px, 16px 글자)은 옆의 "🪄 자동 채우기" 버튼보다 훨씬 커서
   어색하므로, board.js 워크스페이스 필터바(.board-filter-bar .login-input)와
   똑같은 압축 값을 그대로 재사용해 높이를 맞춥니다. 같은 줄에 나란히
   배치되는 "휴가 종류" select 및 날짜/시각 입력 필드도 동일한 높이로
   통일합니다.
   [버그 수정] 휴가 문서의 시작일/종료일(#approval-leave-event-start/-end)이
   출장(#approval-event-start/-end) 및 시가 필드와 id가 겹치지 않도록
   별도 id로 분리되어 있었는데, 이 압축 규칙에는 그 별도 id가 빠져 있어
   휴가(시가 아닌 종류)의 시작일/종료일만 기본 크기(더 크고 폰트도 큼)로
   남아 있었습니다 — 시가의 날짜/시작/종료 선택칸과 크기가 다르게 보이는
   원인이었으므로, 두 id를 이 목록에 추가해 통일합니다. */
#approval-doctype-select,
#approval-leave-type-select {
  padding: 6px 10px;
  font-size: var(--fs-caption);
}

#approval-event-start,
#approval-event-end,
#approval-leave-event-start,
#approval-leave-event-end,
#approval-event-hourly-date,
#approval-event-hourly-start,
#approval-event-hourly-end {
  padding: 6px 10px;
  font-size: var(--fs-caption);
  width: auto;
}

/* [시각적 결재선] 결재선 편집기 — 단계(step)별로 왼쪽→오른쪽 칸으로
   배치하고, 칸 사이는 화살표로 잇습니다. 같은 칸 안에 결재자가 여러 명
   (다인 결재)이면 위아래로 쌓입니다. */
.approval-line-flow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.approval-line-step-col {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.approval-line-arrow {
  flex: none;
  align-self: center;
  color: var(--color-text-secondary);
  font-size: 18px;
  line-height: 1;
}

/* 결재자 한 명(칸 안의 한 줄) 카드 — 예전에는 .list-item(위아래로 쌓이는
   구분선 목록)이었지만, 이제는 좌우로 배치되는 독립된 칸이라 카드처럼
   사방 테두리를 두릅니다. */
.approval-line-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}

.approval-line-row .approval-line-search-input {
  width: 150px;
}

/* [다인 결재] 결재선 편집기의 "N단계" 배지 — 클릭할 때마다 단계가 순환하는
   버튼이지만 겉모습은 기존 .badge와 똑같이 보이도록 브라우저 기본 버튼
   스타일(테두리/배경/폰트)만 리셋합니다. */
.approval-line-step-badge {
  font-family: inherit;
  border: none;
  cursor: pointer;
  flex: none;
  transition: filter 0.1s ease;
}
.approval-line-step-badge:hover {
  filter: brightness(0.95);
}

/* 리스트 형태의 항목 (공지, 결재함, 일정 등에서 재사용) */
.list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--fs-desc-sm);
}

.list-item:last-child {
  border-bottom: none;
}

/* [폰트 위계 조정] 리스트 아이템 "제목"만 본문과 같은 단(16px)으로 한 단계
   올립니다. .list-item 자체(--fs-desc-sm, 15px)는 그대로 두고, 제목
   요소에만 이 클래스를 얹어 부제(메타/캡션) 텍스트와의 위계를 유지합니다. */
.list-item-title {
  font-size: var(--fs-body-compact);
}

/* 빈 상태(데이터 없음) 안내 */
.empty-state {
  text-align: center;
  color: var(--color-text-secondary);
  padding: 40px 0;
  font-size: var(--fs-desc-sm);
}

/* ---------- 4-1b. 업무 검색/필터 바 (Phase 4) ----------
   워크스페이스 탭·보기 탭 아래, 목록(갤러리/보드/테이블) 위에 표시되는
   검색어 입력 + 담당자/상태/우선순위 필터 한 줄 */
.board-filter-bar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
  margin-top: 10px;
}

.board-filter-bar .login-input {
  padding: 6px 10px;
  font-size: var(--fs-caption);
}

/* ---------- 4-1. 데이터 테이블 (테이블 보기) ----------
   협업 게시판의 "테이블 보기" 등 여러 곳에서 재사용할 수 있는 공용 표 스타일 */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-table);
}

.data-table th {
  text-align: left;
  /* [GBDP FHD Desktop Typography v2.0] 표 글자가 커진 만큼(14→15px) 행
     높이도 함께 늘려, FHD 화면에서 행이 서로 붙어 보이지 않게 합니다. */
  padding: 12px 14px;
  color: var(--color-text-secondary);
  font-weight: var(--fw-semibold);
  /* Table Header는 본문과 같은 크기(--fs-table)에 굵기로만 구분합니다
     — 캡션보다 작은 크기를 쓰면 머리글이 오히려 덜 읽힙니다. */
  font-size: var(--fs-table);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.data-table td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

.data-table tbody tr {
  cursor: pointer;
}

.data-table tbody tr:hover {
  background: var(--color-bg);
}

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

/* ---------- 4-2. 보드(칸반) 보기 ----------
   상태별로 칼럼을 나눠 카드를 드래그로 옮기는 보기 */
.board-columns {
  display: flex;
  gap: var(--space-lg);
  overflow-x: auto;
  padding-bottom: var(--space-sm);
}

.board-column {
  flex: 1;
  min-width: 240px;
  background: var(--color-bg);
  border-radius: var(--radius-md);
  padding: var(--space-md);
  transition: background 0.15s ease;
}

.board-column.drag-over {
  background: var(--color-primary-soft);
}

.board-column-header {
  font-weight: 700;
  font-size: var(--fs-desc-sm);
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.board-mini-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-bottom: var(--space-sm);
  cursor: grab;
  font-size: var(--fs-desc-sm);
}

.board-mini-card:active {
  cursor: grabbing;
}

.board-mini-card.dragging {
  opacity: 0.4;
}

/* [30단계] 워크스페이스 탭 드래그 순서 변경(관리자 전용) */
.board-workspace-tab-btn[draggable="true"] {
  cursor: grab;
}

.board-workspace-tab-btn.dragging {
  opacity: 0.4;
}

.board-workspace-tab-btn.drag-over {
  outline: 2px dashed var(--color-primary);
  outline-offset: 2px;
}

/* [드래그앤드롭] 일정(Schedule) 월간 달력 - 일정 칩을 드래그해 날짜를 옮기기 */
/* [보기 전환] 월/주/일 탭 버튼 — "오늘" 버튼(#schedule-today-btn, .btn 기본
   패딩만 사용)과 세로 폭을 맞추기 위해 padding만 별도로 고정합니다.
   ⚠️ line-height는 예전에 여기서 1.2로 따로 지정했었는데, 그게 바로
   "이모지 섞인 버튼 글자가 위로 쏠려 보이는" 문제의 원인 중 하나였습니다
   — .btn의 공유 토큰(--btn-line-height, 지금은 1)이 이후에 새로 생겼는데
   이 규칙이 소스 순서상 더 뒤에 있어서 계속 덮어쓰고 있었습니다. 이제는
   지정하지 않고 .btn 쪽 값을 그대로 물려받게 둡니다. */
.schedule-mode-tab-btn {
  padding: 8px 14px;
}

/* [월 보기 레이아웃] 일정 칩이 셀 경계를 넘어가지 않도록 셀에 overflow:hidden을
   설정합니다. 칩 자체의 text-overflow:ellipsis는 칩이 부모 너비로 제한될 때만
   동작하므로, 이 두 규칙이 함께 있어야 긴 제목이 "..."으로 잘립니다. */
.schedule-day-cell {
  overflow: hidden;
}

/* [월 보기 칩] block으로 두어야 폭이 부모(셀) 100%에 맞춰지고
   text-overflow:ellipsis가 실제로 작동합니다. */
.schedule-event-chip {
  display: block;
  width: 100%;
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.schedule-event-chip[draggable="true"] {
  cursor: grab;
}

.schedule-event-chip.dragging {
  opacity: 0.4;
}

.schedule-day-cell.drag-over {
  background: var(--color-primary-soft);
}

/* [크기 조절 엔진] 시간표(주간/일간 보기) 일정 블록 하단의 크기 조절 손잡이.
   기존 드래그(.schedule-event-chip[draggable="true"]) 커서 규칙과 마찬가지로,
   이 손잡이 위에서는 "위아래로 늘이기" 커서로 바꿔 무엇을 할 수 있는지 알려줍니다. */
.schedule-resize-handle {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 8px;
  cursor: ns-resize;
  z-index: 2;
}

/* 크기를 조절하는 동안(마우스/터치 모두) 지금 조절 중인 블록을 강조합니다. */
.schedule-event-chip.resizing {
  outline: 2px solid var(--color-primary);
  outline-offset: -1px;
}

/* 크기 조절을 마쳤지만 아직 서버에 저장하지 않은 미리보기 상태 표시(점선 테두리).
   다음 새로고침/재조회 시 서버 값으로 되돌아간다는 것을 은은하게 알려줍니다. */
.schedule-event-chip.preview-unsaved {
  outline: 2px dashed var(--color-primary);
  outline-offset: -1px;
}

/* [드래그앤드롭] 워크스페이스 글쓰기/수정 화면 - 블록 손잡이로 순서 옮기기 */
.board-block-row.dragging {
  opacity: 0.4;
}

.board-block-row.drag-over {
  background: var(--color-primary-soft);
}

/* [터치 드래그 지원] js/core/touchDragSupport.js가 터치 드래그를 확정하는
   순간 <html>에 이 클래스를 붙여, 손가락으로 끄는 동안 페이지 전체가
   함께 스크롤되지 않도록 막습니다(놓으면/취소되면 바로 제거됩니다). */
html.touch-dragging {
  touch-action: none;
  overscroll-behavior: none;
}

/* ---------- 첨부파일 드래그앤드롭 영역 (워크스페이스/전자결재 공용) ---------- */
.file-dropzone {
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-lg) var(--space-md);
  text-align: center;
  font-size: var(--fs-caption);
  color: var(--color-text-secondary);
  margin-top: 6px;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.file-dropzone.drag-over {
  border-color: var(--color-primary);
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

/* ---------- 5. 사이드바 오버레이(모바일 전용) ---------- */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--color-overlay);
  z-index: 90;
}

.sidebar-overlay.visible {
  display: block;
}

/* ---------- 5-1. 범용 모달(팝업창) ---------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: var(--color-overlay-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  padding: var(--space-lg);
}

/* [Resize/터치 대응] 뷰포트가 좁거나 낮아지면(모바일 회전, 창 크기 조절 등)
   내용이 긴 모달(예: 일정 등록/수정)이 화면 밖으로 넘쳐 저장 버튼을 누를 수
   없게 되는 문제를 막기 위해, 모달 자체의 높이를 뷰포트의 90%로 제한하고
   헤더(제목/닫기 버튼)는 고정한 채 본문(.modal-body)만 스크롤되게 합니다. */
.modal-box {
  width: 100%;
  max-width: 380px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0; /* 본문이 스크롤되어도 헤더는 항상 보이도록 고정합니다. */
}

.modal-title {
  font-weight: 700;
  font-size: var(--fs-h2);
}

.modal-body {
  padding: 18px;
  overflow-y: auto;
  /* 관성 스크롤(터치 기기에서 손을 뗀 뒤에도 자연스럽게 미끄러지는 스크롤)을 켭니다. */
  -webkit-overflow-scrolling: touch;
}

/* ---------- 5-1. 협업 기능 강화 (댓글 수정/삭제, @멘션, 즐겨찾기, 수정 이력,
   편집 잠금/저장 충돌 감지는 브라우저 기본 confirm()/alert()로 안내하므로
   별도 CSS가 필요 없습니다) ---------- */

/* 댓글 "수정" 모드 — 기본 .list-item은 가로 배치라, 이 줄만 세로로 바꿔서
   textarea가 전체 너비를 쓰고 그 아래 저장/취소 버튼이 오도록 합니다. */
.comment-row-editing {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-sm);
}

.comment-edit-textarea {
  width: 100%;
  resize: vertical;
  font-family: inherit;
  line-height: 1.5;
}

.comment-edit-actions {
  display: flex;
  gap: 6px;
}

.comment-edit-actions .btn {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--fs-caption);
}

/* 댓글 목록의 수정/삭제 버튼, 상세보기 상단의 즐겨찾기/이력 버튼처럼
   본문보다 눈에 덜 띄어야 하는 보조 버튼들을 위한 공용 사이즈 */
.comment-edit-btn,
.comment-delete-btn {
  padding: 3px 8px;
  font-size: var(--fs-caption);
}

.comment-edited-tag {
  color: var(--color-text-secondary);
  font-style: italic;
}

/* [댓글 답글] 답글 목록 전체를 오른쪽으로 들여써서, 원 댓글에 딸린
   답글이라는 것을 한눈에 알 수 있게 합니다. 왼쪽의 얇은 선은 답글이
   여러 개일 때 하나의 스레드로 묶여 보이도록 돕습니다. */
.comment-replies {
  margin-left: 24px;
  padding-left: 12px;
  border-left: 2px solid var(--color-border);
}

/* 답글 한 줄(comment-reply-row)은 위 들여쓰기 컨테이너 안에서 다시
   .list-item의 기본 좌우 패딩을 그대로 쓰되, 구분선 없이 촘촘하게
   붙습니다(들여쓰기 자체로 이미 원 댓글과 구분되므로). */
.comment-reply-row {
  border-bottom: none;
}
.comment-replies .comment-reply-row:not(:last-child) {
  border-bottom: 1px solid var(--color-border);
}

/* "답글" 버튼도 수정/삭제와 같은 보조 버튼 크기를 씁니다. */
.comment-reply-btn {
  padding: 3px 8px;
  font-size: var(--fs-caption);
}

/* @멘션 강조 표시 (댓글, 블록 본문) */
.mention-tag {
  color: var(--color-primary);
  background: var(--color-primary-soft);
  border-radius: 4px;
  padding: 0 4px;
  font-weight: 600;
}

/* @멘션 자동완성 드롭다운 — position:fixed로 body에 직접 붙기 때문에
   부모 요소의 overflow/position에 영향을 받지 않습니다. */
.mention-dropdown {
  position: fixed;
  z-index: 300;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  max-height: 220px;
  overflow-y: auto;
}

.mention-dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 8px 10px;
  font-size: var(--fs-desc-sm);
  cursor: pointer;
}

.mention-dropdown-item:hover {
  background: var(--color-primary-soft);
}

.mention-dropdown-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font-size: var(--fs-caption);
  font-weight: 700;
  flex-shrink: 0;
}

.mention-dropdown-position {
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
}

/* 상세보기 상단의 즐겨찾기/수정 이력 버튼 — 다른 헤더 버튼과 나란히 놓일 때
   줄바꿈이 되어도 자연스럽도록 살짝 여백만 조정합니다. */
#board-favorite-btn,
#board-history-btn {
  padding: var(--space-sm) var(--space-md);
  font-size: var(--fs-desc-sm);
}

/* ---------- 5-2. 대시보드 "최근 작성/수정/즐겨찾기/담당 문서" 카드 ---------- */
/* 첨부파일 목록 — 행 전체가 클릭 가능합니다 (누르면 새 탭에서 열리거나
   다운로드됩니다. js/core/utils.js의 openAttachment() 참고). data: URL로의
   직접 이동을 최신 브라우저가 막기 때문에 <a href="data:...">가 아니라
   클릭 이벤트로 Blob을 열어주는 방식이라, 여기서는 포인터 커서와
   호버 효과만 지정합니다. */
.attachment-row {
  cursor: pointer;
}

.attachment-row:hover {
  background: var(--color-bg);
}

.attachment-row:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

.dashboard-post-row span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-right: var(--space-sm);
}

/* [대시보드 카드 밀도] 알림/상신한 문서/공지/일정/게시글 등 대시보드
   고정·요약 카드의 한 줄(.list-item 재사용)은 카드 높이가 제한적인 만큼
   더 많은 항목을 한눈에 보여주는 것이 유용합니다. .list-item의 기본
   padding(10px 0)은 board.js 댓글 목록·admin.js 감사 로그처럼 화면
   전체를 목록에 쓰는 곳 기준이라 대시보드 카드에는 다소 여유롭습니다.
   여기서만 상하 padding과 줄간격을 줄여 더 촘촘하게 쌓이도록 합니다
   (.list-item 자체는 다른 화면에 영향이 가므로 건드리지 않고, 이
   선택자들에만 덮어씁니다 — .comment-reply-row가 border-bottom만
   덮어쓰는 것과 같은 방식). */
.dashboard-alarm-row,
.dashboard-submitted-row,
.dashboard-notice-row,
.dashboard-schedule-row,
.dashboard-post-row {
  padding: 6px 0;
  line-height: 1.3;
}

/* ---------- 6. 반응형 (모바일 대응) ----------
   768px 이하에서는 사이드바를 화면 밖으로 숨기고,
   햄버거 버튼으로 슬라이드 인/아웃 시킵니다. */
@media (max-width: 768px) {
  :root {
    --content-padding: var(--space-lg);
    /* 좁은 화면에서는 제목류 크기를 한 단계씩 낮춰 상단바/모달이 여유 있게 보이도록 합니다. */
    --topbar-height: 56px;
    /* [GBDP FHD Desktop Typography v2.0] 이 앱은 PC 16:9 FHD를 기본
       전제로 설계되어 있고, 이 미디어쿼리는 그 전제를 벗어난 좁은 화면
       (태블릿·모바일)에서만 예외적으로 적용되는 축소판입니다. 데스크톱
       계단(30/24/20)을 그대로 축소 비율로 내리지 않고, 좁은 화면에서
       상단바·모달이 과하게 눌리지 않을 만큼만 단계를 낮췄습니다. */
    --fs-h1: 22px;
    --fs-h2: 18px;
    --fs-h3: 16px;
    /* [모바일 실사용 버그 수정] iOS Safari는 포커스된 입력창의 글자 크기가
       16px보다 작으면 "확대해서 잘 보이게" 페이지 전체를 강제로 확대합니다.
       로그인 화면부터 글쓰기·댓글까지 이 앱의 거의 모든 입력창이 공유하는
       .login-input이 16px보다 작았을 때(구 버전 --fs-input 14px) 모바일
       사파리에서 아무 입력창이나 탭할 때마다 화면이 훅 확대됐다가 초점을
       옮기면 다시 축소되는 현상이 있었습니다.

       [GBDP FHD Desktop Typography v2.0 적용 후] 데스크톱 --fs-input도
       16px가 되어 이 모바일 재정의와 값이 같아졌습니다 — 더 이상 "예외"가
       아니라 "우연히 일치"이지만, 그렇다고 이 규칙을 지워도 되는 건
       아닙니다. 나중에 데스크톱 입력 폰트가 다시 16px 미만으로 내려가는
       변경이 있어도, 모바일은 이 줄 덕분에 iOS 확대 버그에서 계속
       안전합니다. */
    --fs-input: 16px;
  }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    /* [모바일 실사용 버그 수정] index.html의 viewport-fit=cover 때문에
       화면이 노치·다이내믹 아일랜드·홈 인디케이터 영역까지 확장되는데,
       정작 그 영역을 피해가는 처리가 어디에도 없었습니다. 사이드바가
       position:fixed로 화면 맨 위/아래까지 붙기 때문에, 그대로 두면
       맨 위 로고나 맨 아래 메뉴 항목이 노치나 홈 인디케이터에 가려질 수
       있습니다. env()는 노치가 없는 기기(대부분의 안드로이드, 구형
       아이폰)에서는 0으로 계산되므로 그런 기기에는 아무 변화가 없습니다. */
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    box-sizing: border-box;
    transform: translateX(-100%);
    box-shadow: var(--shadow-md);
  }

  .sidebar.open {
    transform: translateX(0);
  }

  .menu-toggle {
    display: flex;
  }

  .content {
    padding: var(--space-lg);
  }

  .grid-cols-3,
  .grid-cols-2 {
    grid-template-columns: 1fr;
  }

  /* 좁은 화면에서는 헤더의 "출근/퇴근 시간" 텍스트를 숨기고 버튼만 남겨서
     제목·버튼과 겹치지 않게 합니다. */
  .topbar-extra {
    gap: var(--space-xs);
  }

  #attendance-time-status {
    display: none;
  }

  /* 출근/퇴근/카드관리 버튼도 여백과 글자 크기를 한 단계 줄입니다. */
  .topbar-btn {
    padding: 6px 8px;
    font-size: var(--fs-caption);
  }

  /* 모바일에서는 로그인 화면/사이드바 로고가 과도하게 커 보이지 않도록
     데스크톱보다 작은 크기로 줄입니다. */
  .login-logo-img {
    width: 170px;
  }

  .sidebar-logo-img {
    width: 130px;
  }
}

@media (max-width: 420px) {
  /* 아주 좁은 화면(구형/소형 스마트폰)에서는 한 번 더 축소합니다. */
  .login-logo-img {
    width: 150px;
  }

  .sidebar-logo-img {
    width: 115px;
  }

  /* 출근/퇴근/카드관리 세 버튼이 한 줄에 다 들어가지 않는 화면 너비이므로,
     버튼 글자는 숨기고 이모지 아이콘만 남깁니다 (title 속성으로 접근성 유지). */
  .topbar-btn .btn-label {
    display: none;
  }

  .topbar-btn {
    padding: 6px;
  }
}

/* ---------- 7. 일정(Schedule) 달력 - 반응형/스크롤 최적화 ----------
   레이아웃 크기(칸 높이, 시간당 픽셀 등)는 js/modules/schedule.js의
   responsiveCalendarConfig()가 화면 폭에 맞춰 js/modules/calendar/
   {month,week,day}GridRenderer.js에 값을 넘겨서 이미 조절합니다(인라인
   style). 여기서는 그 크기 조절만으로는 안 되는, 순수하게 CSS의 몫인
   부분(스크롤 관성/스크롤바, 터치 손잡이 크기, 작은 화면 가독성)만
   다룹니다 — 데스크톱(FHD) 레이아웃은 이 규칙들의 영향을 받지 않습니다. */

/* [스크롤 최적화] 주간/일간 보기의 0~23시 시간표 스크롤 영역(js/modules/
   calendar/weekGridRenderer.js, dayGridRenderer.js가 부여하는 클래스).
   모든 화면 크기에서 적용됩니다 — 화면 폭과 무관하게 스크롤 손맛 자체는
   항상 좋아야 하기 때문입니다. */
.schedule-timeline-viewport {
  -webkit-overflow-scrolling: touch; /* iOS 관성 스크롤 */
  overscroll-behavior: contain; /* 끝까지 스크롤해도 뒤의 페이지가 함께 스크롤되지 않도록 */
  scrollbar-width: thin;
}

.schedule-timeline-viewport::-webkit-scrollbar {
  width: 8px;
}

.schedule-timeline-viewport::-webkit-scrollbar-thumb {
  background: var(--color-border);
  border-radius: var(--radius-sm);
}

@media (max-width: 768px) {
  /* [터치 상호작용] 크기 조절 손잡이는 데스크톱 마우스 기준(8px)으로는
     손가락으로 정확히 짚기 어려우므로, 태블릿/모바일에서는 여유 있게 키웁니다.
     레이아웃(top/left/right)은 그대로 두고 높이만 키워 아래로 살짝 더
     내려오게 합니다. */
  .schedule-resize-handle {
    height: 16px;
    bottom: -8px;
  }

  /* [반응형 이벤트 레이아웃] 좁은 화면에서는 일정 칩 글자가 너무 작아지지
     않도록 최소 크기를 보장합니다. 칩 크기 자체(폭/높이)는 각 렌더러가
     이미 화면 폭에 맞춰 넘겨준 값(인라인 style)을 그대로 따릅니다 — 여기서는
     본문 글자 크기만 살짝 올려 가독성을 보정합니다. */
  .schedule-event-chip {
    font-size: var(--fs-caption) !important;
  }

  /* [터치 상호작용] 달력 보기 전환/회사·부서 필터 탭이 좁은 화면에서
     줄바꿈되어도 각 버튼이 손가락으로 누르기 충분한 높이를 갖도록 합니다. */
  .schedule-mode-tab-btn,
  .schedule-view-tab-btn {
    padding: var(--space-sm) var(--space-md);
  }
}

@media (max-width: 420px) {
  /* [반응형 레이아웃] 아주 좁은 화면에서는 시간 라벨 열 폭도 이미 렌더러가
     줄여서 넘기지만(예: 32px), 눈금 숫자 글자 크기까지 CSS로 한 번 더
     보정합니다(인라인 스타일이라 !important로 재정의합니다). */
  .schedule-timeline-viewport {
    /* [토큰 예외] 타입 스케일의 최소 단계는 --fs-micro(11px)지만, 여기만
       420px 이하에서 9px를 유지합니다. 32px까지 줄어든 시간 라벨 열에
       "13:00"이 한 줄로 들어가야 해서 11px로는 넘칩니다. 스케일 밖 값을
       의도적으로 남긴 유일한 지점이니, 토큰 정리 시 실수로 치환하지
       마세요. */
    font-size: 9px;
  }
}


/* ============================================================
   유틸리티 클래스 (인라인 스타일 정리 - 1단계)
   ------------------------------------------------------------
   ⭐ 왜 추가했나 ⭐
   모듈 코드 전반에 style="width:100%;" 같은 짧은 인라인 스타일이
   500회 넘게 반복되고 있었습니다. 디자인이 바뀌면(예: 기본 여백 값
   조정) 수백 곳을 일일이 찾아 고쳐야 하는 구조라, 가장 많이 반복되던
   상위 패턴만 이름 있는 클래스로 옮겼습니다.

   ⭐ 범위를 의도적으로 제한한 이유 ⭐
   전체 인라인 스타일(235종) 중 1~2회만 쓰이는 패턴까지 전부 클래스로
   빼면 오히려 CSS가 비대해지고 "이 클래스가 정확히 어떤 값인지"를
   찾아보는 비용이 인라인 스타일을 읽는 것보다 커집니다. 그래서 자주
   반복되는 상위 패턴(대략 상위 30종, 전체 발생의 약 45%)만 다루고,
   나머지는 자연스럽게 남겨뒀습니다. 새 코드를 작성할 때는 아래 클래스가
   먼저 맞는지 확인하고, 맞지 않으면 인라인 스타일을 그대로 써도 됩니다
   (강제 규칙이 아니라 "있으면 우선 재사용" 정도의 관례입니다).

   이름 규칙은 Tailwind 등 널리 쓰이는 유틸리티 프레임워크의 축약 표기를
   그대로 따랐습니다(w-full, mb-10, mt-16 등) — 그룹웨어 코드베이스에
   Tailwind가 설치되어 있진 않지만, 이 축약 표기 자체는 프론트엔드
   개발자에게 이미 익숙한 관례라 별도 학습 비용이 거의 없습니다.
   ============================================================ */

/* ---------- 너비/유연 박스 ---------- */
.w-full  { width: 100%; }
.w-auto  { width: auto; }
.flex-1  { flex: 1; }

/* ---------- 여백 (margin) ----------
   값은 새로 만든 스케일이 아니라, 지금까지 인라인으로 실제 쓰이던
   픽셀 값을 그대로 옮긴 것입니다(화면이 달라지지 않도록). */
.m-0  { margin: 0; }
.mb-8  { margin-bottom: var(--space-sm); }
.mb-10 { margin-bottom: 10px; }
.mb-14 { margin-bottom: 14px; }
.mt-4  { margin-top: var(--space-xs); }
.mt-12 { margin-top: var(--space-md); }
.mt-16 { margin-top: var(--space-lg); }
.ml-8  { margin-left: var(--space-sm); }

/* ---------- 타이포그래피 / 텍스트 색 ---------- */
.fw-600      { font-weight: 600; }
.text-muted  { color: var(--color-text-secondary); }
/* [자주 반복] "작은 크기 + 보조색" 조합이 12회 등장해 하나로 묶었습니다.
   margin-top 등 추가 여백이 붙는 변형은 옆에 mt-4 등을 함께 붙여 씁니다
   (예: class="text-xs-muted mt-4"). */
.text-xs-muted { font-size: var(--fs-caption); color: var(--color-text-secondary); }
.text-right  { text-align: right; }
.nowrap      { white-space: nowrap; }

/* ---------- 레이아웃 ---------- */
.d-flex       { display: flex; }
.hidden       { display: none; }
.align-start  { align-items: flex-start; }
.gap-6        { gap: 6px; }
.gap-8        { gap: var(--space-sm); }

/* ---------- 버튼 크기/색상 변형 ----------
   기존 .btn / .btn-primary와 같은 자리에 두는 컴포넌트 클래스입니다
   (인라인 스타일이 아니라 의미 있는 이름을 가진 진짜 변형이라, 위
   범용 유틸리티와 구분해서 아래에 뒀습니다). */
/* [버튼 크기 통일] "작은 버튼"이 코드 곳곳에 인라인 style로 흩어져 있었고
   그마저 padding이 4px 10px / 4px 8px / 6px 10px / 2px 6px로 제각각이었습니다
   (여기에 캡션 크기를 쓰는지 여부도 들쭉날쭉). 실제 용도를 보면 성격이
   다른 세 그룹이라, 하나로 합치는 대신 이름 붙은 클래스 세 개로
   공식화했습니다 — 앞으로는 숫자를 다시 타이핑하지 않고 클래스만
   붙이면 됩니다.

   .btn-sm   4px 10px / caption(14px) — 삭제·제거·항목추가 같은 보조 액션.
             파일 제거, 속성 삭제, 블록 삭제, 카테고리 편집, 필터 초기화 등.
   .btn-xs   2px 6px  / caption(14px) — 아이콘 한 글자만 담는 초소형 버튼.
             체크리스트/블록 편집기의 ▲▼ 순서 이동처럼 좁은 세로 공간에
             쌓이는 자리 전용입니다. .btn-sm보다 한 단계 더 작습니다.
   .btn-compact 6px 10px / --fs-button(15px, 폰트 크기는 줄이지 않음) —
             주 버튼(＋ 새 글쓰기 등) 옆에 나란히 서는 보조 툴바 버튼.
             폰트는 주 버튼과 맞추되 자리만 덜 차지해야 하는 경우입니다.
             (워크스페이스 추가/이름변경/삭제, 즐겨찾기) */
.btn-sm {
  /* [접근성] 예전 padding 4px일 때 높이가 22px로, WCAG 2.2 AA가 요구하는
     최소 클릭 타깃 24×24px에 못 미쳤습니다. 이 클래스는 삭제·제거처럼
     되돌릴 수 없는 동작에 쓰이므로 빗나감/오클릭 비용이 큽니다.
     6px로 올려 26px를 확보합니다. */
  padding: calc(6px + var(--btn-text-nudge)) 10px max(0px, calc(6px - var(--btn-text-nudge)));
  font-size: var(--fs-caption);
}

.btn-xs {
  /* [접근성] 예전 padding 2px일 때 높이 18px로 기준에 크게 못 미쳤습니다.
     ▲▼ 순서이동처럼 세로로 붙여 쌓는 자리라 무한정 키울 수는 없어,
     최소 기준(24px)에 맞추는 선에서 5px로 올립니다. */
  padding: calc(5px + var(--btn-text-nudge)) 6px max(0px, calc(5px - var(--btn-text-nudge)));
  font-size: var(--fs-caption);
}

.btn-compact {
  padding: calc(6px + var(--btn-text-nudge)) 10px max(0px, calc(6px - var(--btn-text-nudge)));
}
.btn-danger {
  background: var(--color-danger);
  color: #fff;
}

/* ---------- 7. 토스트 알림 ----------
   js/core/toast.js가 동적으로 만드는 요소들의 스타일입니다.
   브라우저 기본 alert()을 대체합니다(왜 걷어냈는지는 toast.js 상단 참고).

   z-index 400: 이 앱의 레이어 순서는 사이드바 오버레이(90) < 모달(200) <
   멘션 드롭다운(300) 순인데, 토스트는 그 무엇보다 위에 있어야 합니다 —
   모달 안에서 저장에 실패했을 때 그 알림이 모달에 가리면 안 되니까요. */
.toast-container {
  position: fixed;
  right: var(--space-lg);
  bottom: var(--space-lg);
  z-index: 400;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  /* 컨테이너 자체는 클릭을 통과시켜, 토스트가 없는 빈 영역이 화면을
     막지 않도록 합니다. 토스트 본체만 pointer-events를 되살립니다. */
  pointer-events: none;
  max-width: min(420px, calc(100vw - var(--space-xl)));
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-text-secondary);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-desc-sm);
  line-height: var(--lh-ui);
  /* 메시지에 들어오는 줄바꿈(\n)을 그대로 살립니다. 기존 alert() 문구
     상당수가 여러 줄이라 이게 없으면 한 줄로 뭉칩니다. */
  white-space: pre-line;
  /* 들어올 때/나갈 때 애니메이션의 시작 상태입니다. */
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.toast-visible {
  opacity: 1;
  transform: translateY(0);
}

.toast-leaving {
  opacity: 0;
  transform: translateY(8px);
}

.toast-icon {
  flex-shrink: 0;
  line-height: inherit;
}

.toast-message {
  flex: 1;
  min-width: 0;
  /* 긴 파일명이나 URL이 들어와도 컨테이너를 밀어내지 않도록 합니다. */
  overflow-wrap: anywhere;
}

.toast-close {
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  line-height: 1;
  padding: 2px;
  border-radius: var(--radius-sm);
}

.toast-close:hover {
  color: var(--color-text);
}

/* 유형별 강조선. 배경 전체를 물들이지 않고 왼쪽 선과 아이콘으로만
   구분해, 다크모드에서도 대비 문제가 생기지 않게 합니다. */
.toast-success { border-left-color: var(--color-success); }
.toast-error   { border-left-color: var(--color-danger); }
.toast-warning { border-left-color: var(--color-accent); }
.toast-info    { border-left-color: var(--color-primary); }

/* 좁은 화면에서는 좌우로 꽉 채웁니다. */
@media (max-width: 768px) {
  .toast-container {
    left: var(--space-md);
    right: var(--space-md);
    bottom: var(--space-md);
    max-width: none;
  }
}

/* [접근성] 움직임을 줄이도록 설정한 사용자에게는 애니메이션 없이
   바로 나타나고 사라지게 합니다. */
@media (prefers-reduced-motion: reduce) {
  .toast {
    transition: none;
    opacity: 1;
    transform: none;
  }
  .toast-leaving {
    opacity: 0;
  }
}

/* ---------- 8. 확인(confirm) UI ----------
   js/core/app.js의 confirmDanger()/confirmInline()이 만드는 요소들입니다.
   confirmDanger는 모달 자체(.modal-box 등)를 재사용하므로 별도 스타일이
   거의 필요 없고, 여기는 confirmInline 전용입니다. */
.confirm-inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  /* [폴백] color-mix()는 이 프로젝트에서 처음 쓰는 함수라, 미지원 구형
     브라우저(2023년 이전 배포분)에서도 최소한의 대비가 나오도록 먼저
     불투명 색을 지정해둡니다. 지원 브라우저는 뒤의 color-mix 선언으로
     덮어써 살짝 반투명한 버전이 적용됩니다. */
  background: #FDECEC;
  background: color-mix(in srgb, var(--color-danger) 8%, transparent);
  border: 1px solid #F3B4B4;
  border: 1px solid color-mix(in srgb, var(--color-danger) 30%, transparent);
  border-radius: var(--radius-sm);
}

.confirm-inline-msg {
  font-size: var(--fs-caption);
  color: var(--color-text);
  white-space: nowrap;
}

/* ---------- 9. 스켈레톤 로딩 ----------
   js/core/utils.js의 skeletonHTML()이 만드는 요소들입니다.
   (왜 텍스트 "불러오는 중..." 대신 이걸 쓰는지는 그 함수 상단 주석 참고) */
.skeleton-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
}

.skeleton-line {
  height: 14px;
  border-radius: var(--radius-sm);
  /* 은은하게 좌→우로 흐르는 반짝임(shimmer). 세 지점 그라데이션을
     가로로 200% 늘려두고, 그 배경 자체를 옆으로 움직여서 표현합니다. */
  background: linear-gradient(
    90deg,
    var(--color-border) 25%,
    var(--color-bg) 50%,
    var(--color-border) 75%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
}

@keyframes skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* [접근성] .sr-only은 위(파일 상단, 접근성 섹션)에 이미 정의되어 있습니다
   (skeletonHTML()의 "불러오는 중" 안내 문구도 그 정의를 그대로 씁니다) —
   여기 있던 완전히 동일한 중복 정의는 제거했습니다. */

@media (prefers-reduced-motion: reduce) {
  .skeleton-line {
    animation: none;
    background: var(--color-border);
  }
}

/* ---------- 10. Command Palette ----------
   js/core/app.js의 openCommandPalette()가 만드는 요소들입니다.
   확인 모달과 마찬가지로 .modal-box 자체는 재사용하고, 여기는
   검색 결과 목록(cmdk-*) 전용입니다. */
#cmdk-results {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 320px;
  overflow-y: auto;
}

.cmdk-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text);
  font-size: var(--fs-input);
  cursor: pointer;
}

.cmdk-item-icon {
  flex-shrink: 0;
}

/* [키보드 탐색] ↑/↓로 고른 항목의 강조 표시. hover가 아니라 클래스로
   직접 관리하는 이유: 마우스를 안 움직여도(키보드만 써도) 강조가
   보여야 합니다. */
.cmdk-item-active,
.cmdk-item:hover {
  background: var(--color-primary-soft);
}

/* ---------- 11. 브레드크럼 ----------
   js/core/app.js의 setBreadcrumb()가 #topbar-title 안에 만드는
   요소들입니다. 기본 상태(브레드크럼 없이 제목 하나)는 이 규칙들의
   영향을 받지 않습니다 — .breadcrumb 클래스가 붙었을 때만 적용됩니다. */
.topbar-title.breadcrumb {
  display: flex;
  align-items: center;
  gap: 6px;
  /* [FHD Desktop] 브레드크럼이 길어질 수 있으니, 상단바를 벗어나지
     않도록 넘치는 부분은 줄임표로 처리합니다. */
  overflow: hidden;
  white-space: nowrap;
}

.breadcrumb-link {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  font-size: inherit;
  font-weight: var(--fw-regular);
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: transparent;
  transition: text-decoration-color 0.15s ease;
}

.breadcrumb-link:hover {
  text-decoration-color: currentColor;
}

.breadcrumb-segment {
  color: var(--color-text-secondary);
  font-weight: var(--fw-regular);
}

.breadcrumb-current {
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
}

.breadcrumb-sep {
  color: var(--color-text-secondary);
  flex-shrink: 0;
}

/* ---------- 12. 표 정렬 ----------
   js/core/utils.js의 enableTableSort()가 붙이는 클래스/속성입니다. */
.data-table th.sortable-th {
  cursor: pointer;
  user-select: none;
}

.data-table th.sortable-th:hover {
  color: var(--color-text);
  background: var(--color-primary-soft);
}

.data-table th.sortable-th:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

.sort-indicator {
  display: inline-block;
  min-width: 1em;
  color: var(--color-primary);
  font-size: var(--fs-micro);
}


/* ---------- 13. 터치 기기용 클릭 타깃 확대 ----------
   [접근성] 위 크기들은 마우스를 쓰는 데스크톱(이 앱의 기준 환경)에
   맞춘 값입니다. 손가락은 마우스 포인터보다 훨씬 뭉툭해서, 같은
   크기라도 터치에서는 빗나가기 쉽습니다.
   `pointer: coarse`는 "정밀하지 않은 입력장치(주로 손가락)"를 뜻하며,
   화면 폭이 아니라 입력 방식으로 판별하므로 태블릿 가로 모드처럼
   화면은 넓지만 터치로 쓰는 경우까지 정확히 잡아냅니다. */
@media (pointer: coarse) {
  .btn-sm {
    padding: calc(9px + var(--btn-text-nudge)) 12px max(0px, calc(9px - var(--btn-text-nudge)));
  }
  .btn-xs {
    padding: calc(8px + var(--btn-text-nudge)) 10px max(0px, calc(8px - var(--btn-text-nudge)));
  }
  .btn-compact {
    padding: calc(9px + var(--btn-text-nudge)) 12px max(0px, calc(9px - var(--btn-text-nudge)));
  }
}
