/* ============================================================
   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(Primary)는 데이터분석 모듈과 공유하는 값이라
     css/tokens.css의 :root로 옮겼습니다(데이터분석의 --text-primary가
     이 값을 가리키는 별칭이 됩니다 — .btn 색상 통일 목적). 여기서는
     다시 선언하지 않습니다. */
  --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의 연한 배경 버전 */

  /* [select 화살표 아이콘] appearance:none으로 <select>의 브라우저 기본
     UI를 끄면(select.login-input 참고) 화살표도
     같이 사라지므로 CSS로 직접 그립니다. data: URL 안에서는 var()로
     색을 넘길 수 없어 색상값을 직접 적었는데, 두 값 모두 새로 만든
     색이 아니라 이미 있는 --color-text-secondary의 라이트/다크 값을
     그대로 옮긴 것입니다(라이트 #5B6472 — 이 :root 블록, 다크 #9AA3B2 —
     아래 [data-theme="dark"] 블록에서 재정의). --color-text-secondary
     자체가 나중에 바뀌면 이 값도 같이 손봐야 한다는 뜻이라, 그 사실을
     여기와 다크 블록 양쪽에 남겨둡니다. */
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235B6472' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  /* [미정의 토큰 수정] 알림 팝오버(.notif-card--unread 등)가 브랜드 색을
     반투명하게 깔려고 var(--color-primary-rgb, 59,130,246) 형태로 참조하는데
     이 토큰이 어디에도 정의돼 있지 않아, **폴백인 59,130,246(일반 파란색)이
     그대로 쓰이고 있었습니다** — 네이비+골드로 통일한 브랜드 팔레트에서
     알림 목록만 파란 기가 돌던 원인입니다. rgba()에 넣어야 하므로 hex가
     아니라 쉼표로 구분한 rgb 성분으로 둡니다(#1E2A44 = 30,42,68). */
  --color-primary-rgb: 30, 42, 68;
  /* [다크모드 버그 수정] .notif-card:hover가 var(--color-hover, rgba(0,0,0,.04))로
     참조하는데 정의가 없어 폴백(검정 4%)이 쓰였습니다. 라이트모드에서는
     의도대로 보이지만 **다크모드에서는 어두운 배경 위의 검정이라 hover가
     사실상 보이지 않았습니다.** 테마별로 각각 정의해 해결합니다. */
  --color-hover: rgba(0, 0, 0, .04);
  --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;

  /* [사용자 요청 - 즐겨찾기 별 아이콘] ⭐는 "이모지 프리젠테이션" 문자라
     CSS color를 못 먹이지만(항상 정해진 노란색 그림으로 렌더링), filter는
     이모지에도 적용됩니다. 꺼진(즐겨찾기 안 된) 상태는 흑백+반투명으로
     "흐릿하게" 처리합니다. 켜진 상태는 원본 ⭐ 그대로(필터 없음)라 이
     변수가 필요 없습니다 — 꺼진 쪽만 라이트/다크 값이 다릅니다. */
  --fav-star-off-filter: grayscale(100%) opacity(45%);
  /* [업무 관리 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(Primary)는 css/tokens.css의 [data-theme="dark"]
     블록에서 이미 #E7EAF0로 재정의됩니다 — 여기서 다시 선언하지
     않습니다(위 :root 블록의 설명 참고). */
  --color-text-secondary: #9AA3B2;
  /* 다크모드에서는 밝기 방향이 반대이므로, 라이트의 Caption/Disabled를
     그대로 쓰면 배경(#14181F)과 붙어버리거나 반대로 너무 튑니다.
     Caption은 Secondary보다 한 단계 어둡게, Disabled는 "눌러도 안 되는
     것"으로 읽힐 만큼만 낮춰 잡았습니다. */
  --color-text-caption: #7C8798;
  --color-text-disabled: #4A525E;
  --color-text-inverse: #FFFFFF; /* primary가 밝아졌으므로 대비는 그대로 유지됨 */

  /* 위 :root의 --select-arrow와 짝입니다 — --color-text-secondary의
     다크 값(#9AA3B2, 바로 위)을 그대로 옮긴 것입니다. */
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%239AA3B2' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* 딥 네이비는 어두운 배경 위에서 거의 안 보여서, 다크모드 전용으로
     더 밝은 톤을 씁니다(라이트모드의 --color-primary-hover와 비슷한 계열). */
  --color-primary: #4A5C82;
  --color-primary-hover: #5B6E97;
  --color-primary-soft: #232A38;
  /* 위 --color-primary(#4A5C82)의 rgb 성분 — 라이트모드와 같은 이유로 필요합니다.
     다크에서도 알림의 '안 읽음' 배경이 그 테마의 primary를 따라가게 됩니다. */
  --color-primary-rgb: 74, 92, 130;
  /* 어두운 배경 위에서는 검정을 덧칠해도 변화가 안 보이므로, hover는 흰색을
     아주 옅게 얹는 방향으로 뒤집습니다(라이트: 검정 4% / 다크: 흰색 6%). */
  --color-hover: rgba(255, 255, 255, .06);

  --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;

  /* [사용자 요청 - 즐겨찾기 별 아이콘] 라이트모드와 같은 grayscale+opacity만
     쓰면, 어두운 카드 배경 위에서 회색별이 오히려 도드라져 보입니다
     ("꺼진" 느낌이 안 남). brightness를 더 낮춰 배경 쪽으로 가라앉힙니다
     (다른 다크모드 토큰들처럼 "눌려도 안 되는 것처럼 흐릿하게"라는
     --color-text-disabled와 같은 의도입니다). */
  --fav-star-off-filter: grayscale(100%) opacity(35%) brightness(0.7);

  --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);
  /* [읽기 모드 폐지] 전역 기본(1.6)을 화면 전체가 그대로 씁니다 — 예전에는
     게시글 본문 등 "긴 글"만 .reading-body로 1.9를 따로 적용했었는데,
     그 때문에 작성/상세보기/인라인편집을 오갈 때마다 서로 다른 서식을
     계속 맞춰야 했습니다. 별도 모드 자체를 없애 이제 어디서나 동일합니다. */
  line-height: var(--lh-ui);
  /* [TYPOGRAPHY STANDARD v1.0] 숫자는 폭이 고정된 자형을 씁니다.
     근태 시각·결재 건수·용량처럼 세로로 나열되는 숫자가 자리마다
     들쭉날쭉하지 않게 정렬됩니다. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

/* [읽기 모드 폐지 - 사용자 요청] 게시글 본문·공지·전자결재 본문(.reading-body)만
   줄간격 1.9/최대폭 860px을 따로 쓰던 규칙을 없앴습니다. 이제 이 클래스가
   붙은 요소도 body의 기본 서식(16px, line-height 1.6, 폭 제한 없음)을
   그대로 물려받습니다 — 클래스 이름 자체(및 각 모듈의 id="board-detail-body"
   같은 참조)는 그대로 남겨뒀으므로 HTML 쪽은 고칠 필요가 없습니다. */

/* [사용자 요청 - 워크스페이스 화면 최대폭 재도입] 예전(v1.6.28~) "읽기 폭"
   860px 제한을 "읽기 모드 폐지" 라운드에서 완전히 없앴는데, 다시 최대폭을
   두되(1050px로 한 번 시도했다가 860px로 다시 조정) 가운데가 아니라 좌측
   정렬로 요청받았습니다. 고정폭(width)이 아니라 최대폭(max-width)이라
   화면이 860px보다 좁으면 그만큼 줄어드는 가변형이고, margin-right만
   auto로 남는 공간을 오른쪽에만 둬서 카드가 화면 왼쪽에 붙습니다
   (v1.6.32에서 같은 이유로 썼던 `margin: 0 auto 0 0` 패턴과 동일). 새
   글쓰기 화면(board-write-card)과 상세보기 화면(board-detail-card, 이번에
   새로 추가한 클래스) 둘 다에 적용해 두 화면이 항상 같은 폭을 씁니다.

   [사용자 요청 - 전자결재 품의 작성 화면도 통일] 품의 작성 화면
   (approval-write-card)도 워크스페이스와 같은 규칙(860px, 가변형, 좌측
   정렬)을 쓰도록 여기에 함께 묶었습니다.

   [사용자 요청 - 공지사항도 워크스페이스와 통일] 공지사항 상세보기는
   기존에 720px로 따로 놀던 인라인 스타일이었고, 작성/수정 화면은 폭
   제한이 아예 없었습니다. 둘 다 여기 규칙에 합류시켜 860px/가변형/좌측
   정렬로 통일합니다(notice.js의 인라인 max-width:720px는 제거). */
/* ============================================================
   [사용자 요청] 새 글쓰기/글 수정 화면의 제목·본문을 상세보기(읽기 화면에서
   클릭해 수정하는 모습)와 동일하게 보이게 하는 규칙입니다. 본문 블록 자체의
   모양은 상세보기와 같은 마크업(.board-block-row-display 등)을 그대로 쓰므로
   여기서 따로 손댈 것이 없고, "제목 입력줄"과 "빈 본문 안내"만 이 화면에
   필요한 최소 규칙입니다.
   ============================================================ */
.board-write-title-input {
  width: 100%;
  border: none;
  background: transparent;
  padding: 0;
  margin-bottom: 10px;
  color: var(--color-text);
  /* 상세보기 제목(board-detail-title-row)과 같은 크기/굵기 */
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: 1.4;
  font-family: inherit;
}
.board-write-title-input:focus { outline: none; }
.board-write-title-input::placeholder { color: var(--color-text-secondary); opacity: 0.7; }
.board-write-body { margin-bottom: 4px; }
/* 업로드 전(등록 시 업로드) 첨부 블록의 안내 문구 */
.board-write-pending-note {
  font-size: var(--fs-caption);
  color: var(--color-text-secondary);
  margin-top: 4px;
}
.board-write-body-empty {
  color: var(--color-text-secondary);
  font-size: var(--fs-desc-sm);
  padding: 6px 0;
}

.board-write-card,
.board-detail-card,
.approval-write-card,
.notice-write-card,
.notice-detail-card {
  max-width: 860px;
  margin: 0 auto 0 0;
}

/* [읽기 모드 폐지] 게시글 본문 입력칸(작성 화면의 board-block-richtext,
   상세페이지 인라인 편집의 board-inline-richtext)을 .reading-body의 1.9/
   블록별 1.7에 맞추려고 두던 별도 line-height/최대폭 규칙도 함께
   없앴습니다 — .login-input의 기본 line-height(1.6)가 이제 어디서나
   그대로 적용됩니다. */

/* [사용자 요청 - 상세페이지 읽기/수정 여백 통일] 상세페이지 인라인 편집은
   .login-input의 안쪽 여백(9px 12px)을 없애 읽기 모드와 동일한 텍스트
   위치를 유지합니다. 체크리스트·소제목·리치텍스트(.board-inline-block-input),
   목록 항목(.board-inline-list-item-input), 표 셀(.board-inline-table-cell),
   토글 하위 항목(.board-inline-toggle-child-input) 등 인라인 편집에 쓰이는
   모든 블록 타입에 동일하게 적용합니다. 두 클래스 조합으로 특이도를
   .login-input 기본 규칙보다 높여 padding을 확실히 덮어씁니다. 인용 블록의
   왼쪽 여백(padding-left:10px, 인용 표시선 용도)은 인라인 style 속성이라
   특이도가 더 높아 그대로 유지됩니다. 작성 화면의 .board-block-*(별도 폼
   입력칸)는 이 변경 대상에서 제외합니다.

   [사용자 요청 - 읽기/수정 모드 통일] 예전엔 "지금 편집 중"임을 알려주려고
   .login-input의 테두리(border)와 기본 입력창 배경색을 일부러 유지했지만,
   편집을 시작/끝낼 때 블록 모양이 바뀌어 거슬린다는 피드백에 따라 테두리와
   배경을 모두 없애고 읽기 모드와 완전히 같은 모습으로 통일합니다. "지금
   편집 중"이라는 신호는 여전히 board-block-display-editable[data-editing="1"]의
   hover 배경 해제(위쪽 규칙)와 서식 툴바 노출만으로 충분히 전달됩니다. */
.login-input.board-inline-block-input,
.login-input.board-inline-list-item-input,
.login-input.board-inline-table-cell,
.login-input.board-inline-toggle-child-input {
  padding: 0;
}
.login-input.board-inline-block-input,
.login-input.board-inline-list-item-input,
.login-input.board-inline-table-cell,
.login-input.board-inline-toggle-child-input,
.login-input.board-inline-richtext {
  border: none;
  background: transparent;
}
.login-input.board-inline-block-input:focus,
.login-input.board-inline-list-item-input:focus,
.login-input.board-inline-table-cell:focus,
.login-input.board-inline-toggle-child-input:focus,
.login-input.board-inline-richtext:focus {
  border-color: transparent;
}
/* [버그 수정 - 새 글쓰기 화면 통일성] 표 셀만은 새 글쓰기 화면에서도
   위 상세보기 인라인 규칙과 똑같이 테두리·배경을 없앱니다 — 표 셀은
   이미 <td>/<th> 자체가 (읽기 모드와 같은 .data-table) 여백과 구분선을
   갖고 있어서, 그 안에 셀 입력창 테두리가 하나 더 있으면 이중 테두리·
   이중 여백으로 표가 불필요하게 커 보였습니다(체크리스트·소제목·리스트
   항목처럼 "그 자체로 하나의 입력 필드"인 경우와는 다름 — 그런 필드들은
   이 화면의 다른 필드와 통일성을 위해 테두리를 그대로 둡니다). */
.login-input.board-block-table-cell {
  padding: 0;
  border: none;
  background: transparent;
}
.login-input.board-block-table-cell:focus {
  border-color: transparent;
}

/* [버그 수정 - 읽기/수정 폰트 크기 불일치] 폰트 크기를 v1.0으로 되돌리면서
   --fs-body(16px)와 --fs-input(14px)이 다시 서로 다른 값이 됐습니다.
   게시글 본문 계열 편집 필드는 모두 .login-input 기본 규칙을 그대로
   물려받아 font-size:var(--fs-input)(14px)로 렌더링되는데, 정작 읽기
   모드 쪽(문단·리스트·체크리스트·표 셀·토글 등)은 폰트 크기를 따로
   지정하지 않아 body 기본값(--fs-body, 16px)을 그대로 씁니다. 그래서
   상세페이지를 수정 모드로 들어가는 순간 글자가 14px로 작아져 보이는
   문제가 다시 생겼습니다(예전엔 --fs-input이 한때 16px였던 시기에
   우연히 두 값이 같았을 뿐, 별도로 맞춰둔 게 아니었습니다). 제목(h2)과
   소제목(heading, --fs-h3)은 이미 각자 인라인 style로 명시돼 있어
   영향받지 않으므로 여기서 다시 다루지 않고, "본문 텍스트"로 취급되는
   나머지 편집 필드만 --fs-body로 맞춥니다 — 로그인/필터/담당자 검색 등
   일반 폼 입력창은 대상이 아니므로 --fs-input(14px)을 그대로 유지합니다.

   [v2.0 재적용 후] --fs-input이 다시 16px가 되어 --fs-body(16px)와
   값이 우연히 같아졌습니다 — css/tokens.css 하단 모바일 미디어쿼리의
   --fs-input 재정의와 같은 이유로, 이 규칙도 지우지 않고 그대로
   둡니다. 나중에 --fs-input이 다시 16px 미만으로 내려가면 이 규칙이
   또 필요해집니다. */
.login-input.board-inline-block-input,
.login-input.board-inline-list-item-input,
.login-input.board-inline-toggle-child-input,
.login-input.board-inline-richtext,
.login-input.board-block-text-input,
.login-input.board-block-item-input,
.login-input.board-block-richtext,
.login-input.board-block-toggle-child-input,
.login-input.board-block-toggle-title,
.login-input.comment-edit-textarea,
.login-input.comment-reply-textarea {
  font-size: var(--fs-body);
}

/* [버그 수정 - 표 셀 읽기/수정 폰트 크기 불일치] 위 규칙에 표 셀
   클래스(board-inline-table-cell / board-block-table-cell)가 함께
   있으면 클래스 2개짜리 선택자(0,2,0)가 아래쪽의
   ".board-block-table-cell, .board-inline-table-cell { font-size:
   inherit; }" 규칙(0,1,0)보다 항상 우선해서, 표 셀은 --fs-table을
   상속받지 못하고 --fs-body로 강제돼 읽기 모드와 수정 모드의 표
   글자 크기가 달라졌습니다. 표 셀은 이 규칙 대상에서 빼고, 아래
   inherit 규칙만 적용받게 합니다. */

/* [읽기 모드 폐지] 글쓰기 카드를 상세보기 읽기 폭(860px)에 맞추려고 두던
   폭 제한/좌측 정렬 규칙을 없앴습니다 — 이제 .card 기본 폭을 그대로
   씁니다(다른 화면의 카드들과 동일). */

ul {
  list-style: none;
}

/* [버그 수정 - 리스트 블록 글머리 기호(·) 사라짐] 위 전역
   ul { list-style:none } 리셋은 사이드바/카드 등 앱 전반의 "글머리 없는"
   목록을 위한 것인데, "리스트"/"순서리스트" 블록(읽기 모드·상세보기 인라인
   편집·새 글쓰기 편집기 모두 동일하게 실제 <ol>/<ul>을 씁니다 — 위
   inlineEditor.js/editor.js 참고)도 같은 <ul>을 그대로 써서 함께 지워지고
   있었습니다. 세 화면 어디서 렌더링되든(래퍼 클래스가 제각각이라 래퍼
   기준으로 걸면 하나씩 빠지기 쉬우므로) <ol>/<ul> 자체에 붙는 전용 클래스
   (board-content-list)로 걸어, 브라우저 기본 글머리(ul=disc, ol=decimal)를
   되살립니다. */
.board-content-list {
  list-style: revert;
}

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;
}

/* 로고를 라이트/다크 모드용으로 별도 제작된 두 개의 이미지(bmp_logo_L2,
   bmp_logo_D2)로 교체하면서, 필터로 반전시키던 기존 방식 대신 테마에 맞는
   이미지를 각각 보여주고 나머지는 숨기는 방식으로 변경합니다.
   ⚠️ [버그 수정] 아래 선택자들은 클래스 1개짜리(.logo-img-dark 등)라
   .sidebar-logo-img { display: block; } 같은, 스타일시트 뒤쪽에 나오는
   동일 우선순위(0,1,0) 규칙에게 소스 순서상 밀려 덮어써졌습니다. 그
   결과 라이트 모드에서도 다크 로고 이미지가 display:block으로 다시
   보이면서, 라이트/다크 로고 두 장이 사이드바에 겹쳐 보이는 문제가
   있었습니다. img 요소 선택자를 더해 우선순위를 (0,1,1)로 올려, 로딩
   순서와 무관하게 항상 이 규칙이 이기도록 고정합니다. */
img.logo-img-dark {
  display: none;
}

[data-theme="dark"] img.logo-img-light {
  display: none;
}

[data-theme="dark"] img.logo-img-dark {
  display: block;
}

.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(input/select/textarea 공통 규칙) 전체에 --color-surface
   (카드 표면색)를 명시했더니, 원래 <input>/<textarea>가 쓰던 브라우저
   기본 배경(색상 스킴에 맞춰 자동으로 렌더링되던 값)보다 못하다는
   평가를 받아 되돌립니다. <input>/<textarea>는 다시 배경/글자색을
   지정하지 않고 브라우저 기본 렌더링에 맡깁니다 — appearance를 건드린
   적이 없어 원래 방식 그대로 잘 동작합니다.

   <select>만은 예외입니다 — appearance:none(아래 select.login-input)
   때문에 배경을 지정하지 않으면 다크 모드에서 흰 배경으로 튑니다.
   대신 임의의 색(--color-surface 등)을 고르는 대신 CSS 시스템
   색상 키워드 Field/FieldText를 씁니다 — 브라우저가 실제로 "폼
   입력창"에 쓰는 배경/글자색이라, <input>이 브라우저 기본으로
   렌더링되는 것과 사실상 같은 색이 되고 라이트/다크 전환도 자동으로
   따라옵니다(select.login-input 규칙 참고). */
.login-input {
  border: var(--control-border-width) solid var(--color-border);
  border-radius: var(--radius-sm);
  /* [세로폭 축소] 이전 FHD Typography v2.0에서 글자를 14→16px로 키우며
     상하 여백도 12px까지 늘렸는데, 실사용 결과 입력창/선택창이 전반적으로
     너무 넓어 보인다는 피드백에 따라 9px/12px로 다시 줄였습니다. 글자
     크기(--fs-input)는 그대로 두고 여백만 줄여서, 가독성은 유지하면서
     세로폭만 컴팩트하게 만듭니다(이 값 하나가 앱 전체 모든
     input/select/textarea의 높이 기준이므로, 여기 한 곳만 고치면
     전체에 일괄 반영됩니다). 값 자체는 css/tokens.css의
     --input-padding-y/--input-padding-x 토큰입니다 — select.login-input의
     height calc()가 아래에서 같은 토큰을 가져다 쓰므로, 두 규칙이
     서로 다른 값을 갖는 사고가 나지 않습니다. */
  padding: var(--input-padding-y) var(--input-padding-x);
  font-size: var(--fs-input);
  line-height: var(--lh-ui);
  font-family: inherit;
  outline: none;
}

/* [사용자 요청 - "버튼/select/입력창 세로폭을 전부 이미지처럼 통일해줘
   ('+ 새 글쓰기' 버튼이나 갤러리 탭 크기가 지금 딱 좋다, 이 기준으로
   맞추고 텍스트도 버튼(15px)로 통일)"] 한 줄짜리 <input>/<select>(검색창,
   진행상태·중요도·진행률 선택, 담당자 검색, 마감일 등)를 .btn과 완전히
   같은 세로폭·글자 크기로 맞춥니다.

   지금까지 두 단계를 거쳤습니다:
   1차 --lh-ui(1.6, 여러 줄 읽기용)가 한 줄짜리 필드에는 과해 --lh-tight
      (1.2)로 줄임 → 그래도 <select>는 브라우저 네이티브 UI 여백이 남아
      <input>과도 안 맞았고, 무엇보다 버튼(15px)과는 애초에 글자 크기부터
      달라 나란히 두면 세로폭이 어긋났습니다.
   2차(이번) 아예 버튼과 같은 토큰(--btn-padding-y/--fs-button/
      --btn-line-height)으로 font-size·line-height·height를 전부 다시
      정의합니다 — 계산식이 .btn과 완전히 동일하므로 버튼·검색창·select가
      항상 같은 세로폭을 갖고, 이 토큰들만 바꾸면 셋 다 함께 따라옵니다.

   <textarea>/블록 편집기의 여러 줄 입력(.login-input.board-inline-*,
   .login-input.board-block-* 등, 이 규칙보다 더 구체적인 선택자라 아래
   규칙의 영향을 받지 않음)과 라벨은 대상이 아닙니다 — 여러 줄 읽는
   본문은 --fs-input(16)/--lh-ui(1.6)가 그대로 필요합니다. */
/* [사용자 지적 - "선택 창 텍스트도 잘려져 있고"] .login-input의 상하
   padding(9px, 위 --input-padding-y)과 테두리(1px 위+아래, 아래
   --control-border-width)를 합치면 이미 위 height 계산식(패딩*2+글자
   크기)과 같은 값이 되어, border 2px이 들어갈 자리가 없어 글자 아래가
   잘렸습니다. 상하 padding을 0으로 두고 height 안에서 남는 여유
   (테두리를 빼고도 넉넉히 남는 공간)로 글자를 세로 중앙에 놓습니다 —
   좌우 padding(--input-padding-x)은 그대로 유지합니다. */
input.login-input,
select.login-input {
  font-size: var(--fs-button);
  line-height: var(--btn-line-height);
  padding-top: 0;
  padding-bottom: 0;
  height: calc(var(--btn-padding-y) * 2 + var(--fs-button) * var(--btn-line-height));
}

/* select.login-input은 appearance:none + 직접 그린 화살표를 씁니다
   (브라우저 기본 <select> UI가 author의 height/padding을 그대로
   따르지 않을 수 있어, 상자 모델을 완전히 통제하기 위함). <input>은
   브라우저 기본 UI가 별도로 없어 대상이 아닙니다.

   [사용자 지적 - "원래 배경색 있던게 더 좋은 거 같은데?"] appearance:none
   을 쓰면 배경/글자색의 브라우저 기본 렌더링도 같이 꺼지므로(안 그러면
   다크 모드에서 흰 배경으로 튐) 뭔가는 지정해야 하는데, --color-surface
   같은 임의의 색 대신 CSS 시스템 색상 키워드 Field/FieldText를 씁니다.
   이 값은 브라우저가 실제로 폼 입력창에 쓰는 배경/글자색이라, 옆의
   <input>(브라우저 기본 렌더링 그대로 유지)과 사실상 같은 색이 되고
   라이트/다크 전환도 OS/브라우저 설정에 따라 자동으로 맞습니다 —
   원래 있던 배경색을 되도록 그대로 복원하는 방법입니다. */
select.login-input {
  padding-right: calc(var(--input-padding-x) + 16px);
  background-color: Field;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right var(--input-padding-x) center;
  background-size: 10px 6px;
  color: FieldText;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  cursor: pointer;
}

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

/* [버그 수정 - 브라우저 자동완성 오입력] 주민등록번호/계좌번호처럼 화면
   표시만 가리면 되는 민감정보 입력란은 type="password"가 아니라
   type="text"에 이 클래스를 붙여 CSS로만 마스킹합니다(브라우저가
   "로그인 비밀번호 입력란"으로 오인해 저장된 관리자 로그인 자격증명을
   자동으로 채우는 것을 막기 위함). 👁 버튼을 누르면 이 클래스를 토글해
   실제 값을 보여줍니다. */
.hr-masked-input {
  -webkit-text-security: disc;
  text-security: disc;
}

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

/* [로그인 유지] 로그인 폼의 "로그인 유지" 체크박스 줄. */
.login-remember-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-size: var(--fs-desc-sm);
  color: var(--color-text-secondary);
  cursor: pointer;
  user-select: none;
}

.login-remember-row input[type="checkbox"] {
  width: 15px;
  height: 15px;
  cursor: pointer;
  accent-color: var(--color-primary);
}

/* [계정 잠금 - ④] 로그인 실패 반복으로 계정이 잠겨 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;
}

.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;
}

/* ── 알림 팝오버 패널 (v1.37.0) ─────────────────────────────────────────
   공용 .modal-overlay 를 쓰지 않는 독립 드롭다운입니다.
   벨 버튼 하단 / 우측 정렬로 위치하며, JS가 top/left를 동적으로 계산합니다.
   ──────────────────────────────────────────────────────────────────────── */
.notif-popover {
  position: fixed;
  z-index: 300;
  width: min(440px, calc(100vw - 16px));
  max-width: calc(100vw - 16px);
  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-caption);
  line-height: 1;
}
.notif-popover__close:hover {
  color: var(--color-text);
}

/* 본문 (스크롤 영역) */
.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-caption);
}

/* 푸터: "전체보기" — 전체 알림함 모듈로 이동 */
.notif-popover__footer {
  flex-shrink: 0;
  padding: 8px 16px;
  border-top: 1px solid var(--color-border);
  display: flex;
  justify-content: center;
}
.notif-popover__footer .btn {
  width: 100%;
}

/* ── 알림 카드 행 ─────────────────────────────
   아이콘 | 제목(1줄 말줄임) + 요약(1줄 말줄임) | 날짜 + dot
   ──────────────────────────────────────────── */
.notif-card {
  display: flex;
  /* [세로 중앙 정렬] 예전엔 flex-start라 제목+요약(본문)과 우측 날짜/시간이
     서로 다른 높이에서 시작해 위쪽만 맞고 전체적으로는 어긋나 보였습니다.
     아이콘·본문·메타(날짜) 세 블록을 행 전체 기준으로 세로 중앙 정렬합니다. */
  align-items: center;
  gap: 12px;
  /* [사용자 요청 - 목록 간격 좁힘] 10px → 8px, .list-item과 동일한 값으로
     맞췄습니다(대시보드 알림카드/알림벨 팝오버/전체 알림함이 전부 이
     클래스를 공유합니다). */
  padding: 8px 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;
  /* 행이 이제 세로 중앙 정렬(align-items:center)이라, flex-start 시절
     상단 보정용이던 margin-top은 더 이상 필요 없어 제거했습니다. */
}

/* 텍스트 영역 */
.notif-card__body {
  flex: 1;
  min-width: 0;
}
.notif-card__title {
  margin: 0;
  /* 벨 팝오버와 전체 알림함이 공유하는 알림 본문 글꼴. 읽음/안읽음은
     배경과 dot으로 구분하고 텍스트 굵기는 모두 Regular로 유지합니다. */
  font-size: var(--fs-desc-sm);
  font-weight: var(--fw-regular);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}
.notif-card--unread .notif-card__title {
  font-weight: var(--fw-regular);
}
.notif-card__summary {
  margin: 2px 0 0;
  font-size: var(--fs-caption);
  font-weight: var(--fw-regular);
  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 {
  /* [폰트 조화] --fs-micro(11px)는 제목(15px)과 대비가 너무 커서
     따로 노는 느낌이었습니다. 요약과 같은 --fs-caption(14px)으로 올려
     "제목이 살짝 크고 나머지는 나란한" 균형으로 맞췄습니다. */
  font-size: var(--fs-caption);
  color: var(--color-text-caption);
  white-space: nowrap;
  line-height: 1.3;
}
.notif-card__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
  flex-shrink: 0;
}

/* [벨 팝오버 전용 축소] 팝오버는 최대 5건만 미리 보여주는 좁은(최대 440px)
   드롭다운이라, 전체 알림함(위 기본값 — 제목 15px/요약·시간 14px)과 같은
   크기면 상대적으로 커 보입니다. .notif-popover 안에서만 한 단계씩 더
   낮춥니다(제목/요약/시간의 내부 위계 비율은 그대로 유지 — 제목이 1px
   더 큼). 13px은 디자인 토큰 스케일에 없는 값이지만, 420px 이하 좁은
   화면의 타임라인 눈금(9px)처럼 이 프로젝트가 이미 써온 "좁은 자리
   전용 예외" 패턴과 같습니다. */
.notif-popover .notif-card__title {
  font-size: var(--fs-caption);
}
.notif-popover .notif-card__summary,
.notif-popover .notif-card__date {
  font-size: 13px;
}

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

/* [대시보드] 카드 제목 우측의 "전체보기" 링크(알림카드 등). .card-title이
   justify-content:space-between인 flex 컨테이너지만, 두 자식 중 하나가
   순수 텍스트라 브라우저마다 익명 flex 아이템 처리 방식이 미묘하게
   달라 space-between만으로는 우측 끝에 딱 붙지 않는 경우가 있었습니다.
   margin-left:auto를 함께 줘 항상 명시적으로 우측 정렬되도록 합니다. */
.card-title-action {
  font-size: var(--fs-desc-sm);
  font-weight: var(--fw-regular);
  color: var(--color-text-secondary);
  text-decoration: none;
  flex-shrink: 0;
  white-space: nowrap;
  margin-left: auto;
}
.card-title-action:hover {
  color: var(--color-primary);
  text-decoration: underline;
}

.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)은
   숫자 하나만 있는 경우가 많아 193px 그대로 두고, 목록형인 공지사항/
   알림 두 카드만(#dashboard-pinned-grid) 왼쪽 사이드바의 "데이터분석"
   아래 카테고리 분리선 높이에 맞춰 키웠습니다. 계산 근거:
   .nav-item(패딩 10px×2 + 텍스트 줄높이 21px + margin-bottom 2px ≈ 43px)
   여섯 개(공지사항~데이터분석) + 메뉴 상단 여백(12px) + 분리선 여백(10px)
   ≈ 280px. */
#dashboard-pinned-grid .dash-card {
  height: 280px;
}

/* [43단계] 카드 가로폭이 좁아져도(반응형/드래그 재배치 등) 제목이 두 줄로
   줄바꿈되지 않고 한 줄로 말줄임(...) 처리되도록 합니다. 기본 카드/커스텀
   카드 제목 모두 .card-title 하나만 쓰므로 여기서 함께 적용됩니다.

   [중복 정리] 예전에는 이 셀렉터가 바로 위에 flex-shrink만 가진 블록으로
   한 번 더 선언돼 있었습니다. 뒤쪽 선언이 이기는 구조라 동작에는 문제가
   없었지만, 둘 중 하나만 고치고 다른 하나를 깜빡하면 조용히 어긋나는
   유지보수 부채라(.sr-only 중복을 정리한 것과 같은 이유) 하나로 합쳤습니다. */
.dash-card .card-title {
  flex-shrink: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}

/* 커스텀 카드는 제목이 아이콘 버튼과 가로로 나란히 놓인 별도의 flex 행
   안에 있어, 그 행에서 실제로 줄어들 수 있어야 말줄임이 동작합니다. */
.custom-dash-card .card-title {
  flex: 1 1 auto;
  min-width: 0;
}

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

/* [대시보드] 카드 하단 우측의 "전체보기"(알림카드 등). .dash-card가 이미
   flex-direction:column이라, 이 자리에 오면 스크롤되는 .dash-card-body
   아래 항상 고정된 한 줄로 붙습니다(스크롤 대상에서 제외 — flex-shrink:0). */
.dash-card-footer {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  padding-top: 6px;
}
.dash-card-footer .card-title-action {
  margin-left: 0;
}

/* 숫자 요약형 카드(결재 대기, 업무 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;
}

/* [43단계 수정] 그리드 트랙(1fr)의 자동 최소 너비는 기본적으로 콘텐츠
   크기를 따라가서(그리드 아이템 기본 min-width:auto), 안의 텍스트에
   overflow/ellipsis를 걸어도 카드 자체가 줄어들지 않아 말줄임이 동작하지
   않았습니다. 대시보드 카드가 실제로 좁아질 수 있도록 0으로 재설정합니다. */
#dashboard-pinned-grid > div,
#dashboard-main-grid > div,
#custom-cards-grid > div {
  min-width: 0;
}

.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;
  /* [버튼 텍스트 중앙 정렬] 버튼이 컨텐츠보다 넓어지는 경우(w-full 등)
     기본값(flex-start)이면 텍스트가 왼쪽으로 쏠려 보였습니다. 폭이
     컨텐츠와 같은 일반 버튼은 이 값의 영향을 받지 않으므로 전체
     버튼에 안전하게 적용합니다. */
  justify-content: 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);
  /* [사용자 요청 - 버튼 디자인 통일, 2026-09-22] 예전엔 배경이 페이지
     배경(--color-bg)과 같아서, 카드(.card) 밖에 놓인 버튼(예: 사건관리
     빠른조회 "당월/올해/전체")은 테두리도 배경도 안 보여 버튼처럼
     안 보였습니다. 특정 모듈에 개별로 테두리를 하드코딩하는 대신
     공용 .btn 기준을 바꿔 앱 전체(카드 안/밖 어디서든) 모든 버튼이
     항상 같은 방식으로 보이도록 통일합니다. 활성 탭(U.tabButtonHTML의
     선택된 항목 등)은 인라인 style로 배경/테두리를 덮어써 계속
     구분됩니다. */
  background: var(--color-surface);
  border: var(--control-border-width) solid var(--color-border);
  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;
  /* [사용자 요청 - 모든 배지 한 줄 유지] display:inline-block인 배지도
     기본값(white-space: normal)에서는 폭이 좁아지면(플렉스/그리드
     컨테이너 안에서 눌리거나, 좁은 화면으로 줄어들 때) 안의 글자가
     중간에서 줄바꿈될 수 있습니다. 배지는 짧은 상태 라벨이므로 항상
     한 줄로 유지되어야 모양이 깨지지 않습니다. */
  white-space: nowrap;
}

.badge-primary { background: var(--color-primary-soft); color: var(--color-primary); }
/* [사용자 요청 - 다크모드 가시성] 다크모드의 --color-primary(#4A5C82)는
   --color-primary-soft 배경 위에서 대비가 낮아 담당자·진행률·마감일·
   검토·낮음 배지(전부 .badge-primary 재사용)가 흐리게 보였습니다.
   --color-primary 토큰 자체를 밝히면 버튼(출근 버튼, 아바타 등 흰
   글자가 얹히는 배경)·알림 점·구분선 hover 등 다른 곳까지 영향이
   번지므로, .badge-primary의 글자색만 다크모드에서 사이드바 미선택
   메뉴·작성자/수정자 표시와 같은 색(--color-text-secondary, 대비
   5.66:1)으로 바꿉니다. 배경은 그대로 --color-primary-soft(남색 틴트)를
   유지해 .badge-muted(중립/테두리)와는 여전히 구분됩니다. */
[data-theme="dark"] .badge-primary { color: var(--color-text-secondary); }

.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; }

/* [사용자 요청 - 다크모드 배지 색상 통일] 다크모드에서는 .badge-primary/
   .badge-hold/.badge-discarded의 배경(--color-primary-soft #232A38,
   --color-hold-soft #262B36, --color-discarded-soft #20242C)이 .badge-muted
   배경(--color-bg #14181F)과 색상 계열이 거의 같아 보여, 서로 다른 배지처럼
   보이려던 미세한 색상차가 오히려 산만하게 흩어져 보였습니다. "공개"/
   "마감일 없음"(.badge-muted) 배경을 기준으로 통일해 담당자·진행률·검토·
   낮음(.badge-primary)과 보류(.badge-hold)·폐기(.badge-discarded) 배지가
   전부 같은 어두운 배경을 씁니다. 글자색도 담당자·진행률(.badge-primary)에
   이미 쓰던 --color-text-secondary를 기준으로 보류·폐기 배지에 동일하게
   맞춰 톤을 통일합니다. 비공개(.badge-warning)·높음(.badge-danger)처럼
   뚜렷하게 구분돼야 하는 강조색 배지는 그대로 둡니다.
   [후속 요청 - 테두리 완전 제거] "공개"(.badge-muted)에만 있던 테두리 때문에
   같은 배경색인 진행률·보류·폐기 배지와 겉모습이 달라 보인다는 지적이
   있었습니다. 다른 배지에 테두리를 맞춰 추가하는 대신, 요청대로 .badge-muted
   쪽 테두리를 완전히 없애 전부 테두리 없는 모습으로 통일합니다(아래
   .badge-muted 규칙에서 제거). */
[data-theme="dark"] .badge-primary,
[data-theme="dark"] .badge-hold,
[data-theme="dark"] .badge-discarded {
  background: var(--color-bg);
}
[data-theme="dark"] .badge-hold,
[data-theme="dark"] .badge-discarded {
  color: var(--color-text-secondary);
}
/* [사용자 요청 - 진행률/빈 값 배지] 값이 없는 필드("담당자 없음" 등)와,
   값이 채워져 있는 다른 배지(파랑/초록 등)를 시각적으로 구분하기 위한
   중립 색상 배지입니다.
   [후속 요청 - 테두리 제거] 예전엔 다크모드 가시성을 위해 얇은 테두리를
   추가했었지만, 이 테두리가 같은 배경의 다른 배지(진행률·보류·폐기)와
   겉모습이 달라 보이는 원인이 됐습니다. 모든 모드에서 테두리 없이
   배경색만으로 구분하도록 되돌립니다. */
.badge-muted   { background: var(--color-bg); color: var(--color-text-secondary); }

/* [사용자 요청 - 라이트모드 배지 색상 통일] 라이트모드에서 "공개"/"마감일
   없음" 배지(둘 다 .badge-muted 재사용, "담당자 없음"·"진행률 없음"·조직도
   인원수 배지도 같은 클래스라 함께 적용됨)가 진행률(.badge-primary)과 다른
   회색 계열이라 위계가 흩어져 보였습니다. 라이트모드에서만 .badge-muted를
   .badge-primary와 완전히 동일한 배경/글자색(--color-primary-soft/
   --color-primary)으로 통일합니다.
   [후속 요청 - 비슷한 배지도 함께 통일] .badge-muted와 같은 계열의 회색
   중립 배지인 보류(.badge-hold, #EEEEEE/#6B6B6B)·폐기(.badge-discarded,
   #E4E4E4/#8A8A8A)도 라이트모드에서 같은 값으로 맞춥니다. 폐기 배지의
   취소선(text-decoration)은 "더 이상 진행하지 않음"이라는 의미 표시라
   색상과 무관하므로 그대로 둡니다.
   다크모드는 기존 방식(.badge-primary/.badge-hold/.badge-discarded를
   .badge-muted 배경 기준으로 통일)을 그대로 유지하므로 이 규칙 대상에서
   제외합니다. */
html:not([data-theme="dark"]) .badge-muted,
html:not([data-theme="dark"]) .badge-hold,
html:not([data-theme="dark"]) .badge-discarded {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

/* [사용자 요청 - 배지 클릭 수정] 상세보기의 상태/중요도/담당자/마감일/진행률
   배지를 클릭해서 바로 수정할 수 있게 만든 버튼 — 겉모습은 기존 .badge와
   똑같이 보이도록 브라우저 기본 버튼 스타일(테두리/배경/폰트)만 리셋합니다.
   (.approval-line-step-badge와 동일한 방식) */
.board-detail-badge-btn {
  padding: 0;
  border: none;
  background: none;
  font-family: inherit;
  cursor: pointer;
}
.board-detail-badge-btn:hover .badge {
  filter: brightness(0.95);
}

/* [레이아웃] 전자결재 "문서 유형" 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;
}

/* [버그 수정 - select 화살표와 텍스트 겹침] 위 두 규칙은 id 선택자라
   select.login-input(아래 "select.login-input은 appearance:none..." 규칙)이
   화살표를 그릴 공간으로 비워둔 오른쪽 여백(padding-right: 입력창
   기본 여백 + 16px)까지 padding shorthand로 함께 덮어씁니다. id
   선택자가 항상 이기므로(특이도상 클래스 선택자로는 되돌릴 수 없음),
   <select>인 대상만 골라 오른쪽 여백을 다시 확보합니다(<input
   type="date">인 나머지 대상은 화살표가 없어 영향 없음). 같은
   문제가 워크스페이스 필터바에도 있어(.board-filter-bar .login-input
   규칙 참고) 함께 고칩니다. */
#approval-doctype-select,
#approval-leave-type-select,
#approval-event-hourly-start,
#approval-event-hourly-end {
  padding-right: calc(var(--input-padding-x) + 16px);
}

/* [시각적 결재선] 결재선 편집기 — 단계(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;
}

/* [버그 수정 - 구조 감사 7라운드 - 고아 드롭다운] 검색 드롭다운을
   document.body에 붙이던 것을 이 입력창 바로 옆(같은 행 안)의 평범한 DOM
   요소로 바꾸면서, 시각적으로는 예전과 똑같이 입력창 바로 아래 겹쳐
   보이도록 래퍼를 position:relative로 두고 드롭다운을 그 기준
   position:absolute; top:100%로 띄웁니다(getBoundingClientRect()로 매번
   좌표를 계산하던 방식 대신 CSS가 처리). 입력창은 150px로 좁게
   고정돼 있지만 드롭다운은 내용(이름·직급·직책·부서)에 맞춰 넓게
   펼쳐져야 하므로 폭은 .mention-dropdown이 이미 갖고 있는 min-width를
   그대로 따릅니다.
   attendanceAdmin.js/org.js처럼 "항상 화면에 하나뿐인 검색창"이라면
   position:static(레이아웃을 밀어내는 완전한 인라인)로 충분했지만, 이
   화면은 결재선 단계마다 카드가 여러 개 나란히 배치되는 flex 행이라
   그 안에 static 블록을 끼워 넣으면 옆 카드들의 가로 정렬이 흐트러집니다
   — 그래서 여기만 absolute로 겹쳐 보이게 하되, 여전히 document.body가
   아니라 이 행(editorEl의 자손) 안에 머무르므로 라우팅 시 자연스럽게
   함께 사라진다는 핵심은 attendanceAdmin.js/org.js와 동일합니다. */
.approval-line-search-wrap {
  position: relative;
}

/* [명시도 주의] .mention-dropdown 기본 규칙(아래쪽, position:fixed)과
   같은 파일 안에서 순서상 나중에 나오는 클래스이므로, 이 요소에 붙은
   두 클래스(.mention-dropdown .approval-line-search-dropdown) 중
   하나만으로 셀렉터를 만들면 소스 순서상 .mention-dropdown이 이겨
   position:fixed로 되돌아갑니다. 자손 결합자로 명시도를 하나 더
   높여(.approval-line-search-wrap .approval-line-search-dropdown)
   순서와 무관하게 항상 이 규칙이 이기도록 합니다. */
.approval-line-search-wrap .approval-line-search-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  margin-top: 4px;
  width: max-content;
  min-width: 220px;
}

/* [다인 결재] 결재선 편집기의 "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;
  /* [사용자 요청 - 목록 간격 좁힘] 10px → 8px. 이 클래스는 공지사항/전자결재/
     조직도/근태/댓글 등 앱 전체의 범용 목록이 공유하므로, 여기 한 곳만
     고치면 전부 한 번에 좁아집니다. 8px는 이미 이 앱의 멘션 자동완성
     (.mention-dropdown-item)·커맨드 팔레트(.cmdk-item, var(--space-sm))가
     쓰고 있는 값이라 새로 만든 게 아니라 기존 패턴을 따른 것이고,
     글자 크기(15px, --fs-desc-sm)를 생각하면 가시성에는 영향 없는
     수준입니다.
  */
  padding: 8px 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);
}

/* ---------- 조직관리(org.js) 드래그앤드롭 순서변경 ----------
   대시보드 카드 그립(.card-drag-handle)과 같은 원칙 — 행 전체가 아니라
   행 앞의 작은 그립(⠿) 아이콘에만 draggable을 걸어, 체크박스 클릭/선택
   동작과 절대 겹치지 않게 합니다. */
.org-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  color: var(--color-text-secondary);
  cursor: grab;
  opacity: 0.5;
  user-select: none;
}
.org-node:hover .org-drag-handle {
  opacity: 0.9;
}
.org-drag-handle:active {
  cursor: grabbing;
}
/* 드래그 중인 행은 반투명하게 표시해 "지금 옮기고 있다"는 걸 보여줍니다. */
.org-node.dragging {
  opacity: 0.4;
}
/* 마우스가 올라가 있는(드롭하면 그 앞으로 들어갈) 행에 점선 표시. 같은
   레벨(parent_id 동일)의 행에만 붙습니다 — 다른 레벨 위에서는 org.js가
   dragover에서 preventDefault()를 하지 않아 애초에 이 클래스가 붙지 않습니다. */
.org-node.drag-over {
  outline: 2px dashed var(--color-primary);
  outline-offset: -2px;
}

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

/* [상세보기 하위 섹션 전용] 하위 페이지/연결된 일정/댓글처럼 상세보기
   안에 여러 섹션이 이어지는 곳은 기본 .empty-state(40px 0)의 세로폭이
   과도하게 넓어 안내 문구만 있어도 섹션이 길어 보입니다. 문구가 잘
   보일 정도로만 세로 여백을 줄인 보조 클래스입니다(기본 .empty-state는
   다른 화면들 — 워크스페이스 목록, 즐겨찾기 등 —에서 그대로 재사용되므로
   건드리지 않습니다). */
.empty-state-compact {
  padding: 14px 0;
}

/* ---------- 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);
}

/* [버그 수정 - 필터 select 화살표와 텍스트 겹침, 사용자 지적] 위 규칙은
   클래스 2개짜리 선택자(특이도 0,2,0)라, select.login-input이 화살표를
   그릴 공간으로 비워둔 오른쪽 여백(padding-right: 입력창 기본 여백 +
   16px, 특이도 0,1,1 — class 개수가 더 적어 위 규칙에 밀림)까지
   padding shorthand로 함께 덮어써, "공개여부 전체"/"상태 전체"/
   "우선순위 전체"의 마지막 글자가 화살표(▼)와 겹쳐 보였습니다. 이
   필터바 안의 <select>만 다시 선택해(class 2개 + 타입 선택자로 특이도
   0,2,1 확보) 오른쪽 여백을 되살립니다. */
.board-filter-bar select.login-input {
  padding-right: calc(var(--input-padding-x) + 16px);
}

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

.data-table th {
  text-align: left;
  /* [bmp FHD Desktop Typography v2.0] 표 글자가 커진 만큼(14→15px) 행
     높이도 함께 늘려, FHD 화면에서 행이 서로 붙어 보이지 않게 했었습니다.
     [사용자 요청 - 목록 간격 좁힘] 12px → 10px로 살짝 줄였습니다(다른
     목록류처럼 8px까지는 아님 — 위 결정을 완전히 되돌리지 않고 절충). */
  padding: 10px 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: 10px 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는 칩이 부모 너비로 제한될 때만
   동작하므로, 이 두 규칙이 함께 있어야 긴 제목이 "..."으로 잘립니다.
   [레이아웃 통일] .holiday-day-cell(공휴일 캘린더)도 같은 그리드 구조를
   공유하므로 여기 함께 선택자로 묶습니다 — CSS Grid 항목은 overflow가
   visible(기본값)이면 자동 최소 너비가 내용물의 min-content로 계산되어,
   공백 없이 긴 공휴일 이름 하나가 그리드 열 자체를 넓혀 달력 전체
   레이아웃이 깨지는 원인이 됩니다(칩의 text-overflow:ellipsis는 이 문제와
   무관하게 그리드 트랙이 먼저 넓어져 버린 뒤라 소용이 없었습니다). */
.schedule-day-cell,
.holiday-day-cell {
  overflow: hidden;
}

/* [월 보기 칩] block으로 두어야 폭이 부모(셀) 100%에 맞춰지고
   text-overflow:ellipsis가 실제로 작동합니다.
   [레이아웃 통일] .holiday-chip(공휴일 칩)도 같은 이유로 함께 묶습니다 —
   기존에는 holiday.js가 이 규칙 없이 인라인 스타일만으로 흉내 내고
   있었는데, 위 .holiday-day-cell의 overflow:hidden이 빠져 있던 탓에
   그리드 트랙 확장 자체를 막지 못해 실효가 없었습니다. */
.schedule-event-chip,
.holiday-chip {
  display: block;
  width: 100%;
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

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

.schedule-event-chip.dragging,
.schedule-event-bar.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);
}

/* [사용자 요청 - UX 개선] 새 글쓰기 화면의 블록 타입 전환 select. 평소엔
   글을 쓰는 데 시선을 뺏지 않도록 테두리 없는 옅은 글자 라벨처럼 보이다가,
   마우스를 올리거나 클릭(포커스)했을 때만 실제 입력 요소처럼 테두리/배경이
   드러납니다. 기능(타입 전환)은 전혀 바뀌지 않았고 순전히 시각적 변경입니다. */
.board-block-type-select-compact {
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-tight);
  padding: 4px 6px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  max-width: 84px;
}
.board-block-type-select-compact:hover,
.board-block-type-select-compact:focus {
  border-color: var(--color-border);
  background: var(--color-bg);
  color: var(--color-text);
  outline: none;
}

/* [터치 드래그 지원] 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;
}

/* [워크스페이스 모듈 - 모바일 댓글 입력] 좁은 화면에서 입력창과
   등록 버튼이 한 줄에서 과도하게 압축되어 레이아웃을 깨뜨리지 않도록
   모바일에서는 입력창을 한 줄 전체로 쓰고 등록 버튼을 다음 줄 우측에 배치합니다. */
.board-comment-compose {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
}
.board-comment-compose .login-input {
  flex: 1 1 auto;
  min-width: 0;
}
.board-comment-compose .btn {
  flex: 0 0 auto;
}

@media (max-width: 560px) {
  .board-comment-compose {
    flex-wrap: wrap;
  }
  .board-comment-compose .login-input {
    flex: 1 1 100%;
    width: 100%;
  }
  .board-comment-compose .btn {
    margin-left: auto;
  }
}

/* [댓글 답글] 답글 목록 전체를 오른쪽으로 들여써서, 원 댓글에 딸린
   답글이라는 것을 한눈에 알 수 있게 합니다. 왼쪽의 얇은 선은 답글이
   여러 개일 때 하나의 스레드로 묶여 보이도록 돕습니다. */
.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;
}

/* [워크스페이스 리치텍스트 에디터 - 2단계] 텍스트 블록 서식 툴바 + contenteditable
   입력창. .login-input을 그대로 재사용해 border/padding/font-size는 기존
   textarea와 동일하고, 내용이 비어 있을 때만 안내 문구를 보여줍니다. */
.board-block-richtext:empty:before {
  content: attr(data-placeholder);
  color: var(--color-text-secondary);
  pointer-events: none;
}
/* [버그 수정 - 새 글쓰기 화면 통일성] 리스트 항목·토글 하위 항목도
   <input>에서 contenteditable div로 바뀌면서 같은 빈 칸 안내 문구
   패턴이 필요합니다(표 셀은 원래도 placeholder가 없었으므로 대상에서
   제외 — 상세보기 인라인 편집과 동일한 기준). */
.board-block-item-input:empty:before,
.board-block-toggle-child-input:empty:before {
  content: attr(data-placeholder);
  color: var(--color-text-secondary);
  pointer-events: none;
}
/* [버그 수정 - 폰트 통일성] 표 셀 입력창은 .login-input의 기본
   font-size(--fs-input)를 그대로 물려받아 상세보기 표(.data-table,
   --fs-table)와 크기가 어긋날 수 있었습니다. 표 안에서는 부모
   (.data-table)의 크기를 그대로 따르게 합니다. */
.board-block-table-cell,
.board-inline-table-cell {
  font-size: inherit;
}

/* [사용자 요청 - 서식 툴바 제거] 굵게/기울임/밑줄/취소선 툴바 버튼을 없애고
   단축키로만 쓰도록 하면서, 그 툴바 전용 스타일(board-richtext-toolbar 등)도
   더 이상 어디서도 만들어지지 않아 제거했습니다. */

/* [사용자 요청 - 인용 블록 작성자 표시] "(이름) " 접두사 — 인용문 자체는
   기울임(italic)이지만 작성자 이름표는 일반 글자로 구분되게 둡니다.
   flex-shrink:0/white-space:nowrap은 편집 화면(입력창과 나란히 flex로
   배치)에서 이름이 줄바꿈되지 않게 합니다 — 읽기 화면은 flex가 아니라
   그냥 인라인 텍스트라 영향이 없습니다.
   */
.board-quote-author-prefix {
  font-style: normal;
  font-weight: normal;
  color: var(--color-text-secondary);
  white-space: nowrap;
  flex-shrink: 0;
}

/* [워크스페이스 "노션 스타일" 확장 - 세션 2/6] 상세보기에서 클릭해 바로
   편집할 수 있는 블록의 hover 힌트입니다. 레이아웃에 영향을 주지 않도록
   배경색/커서만 바꾸고 여백·크기는 건드리지 않습니다(margin/padding을
   추가하면 기존 블록 간 세로 간격이 미세하게 달라질 수 있어 피했습니다).
   [버그 수정 - 커서 방향이 거꾸로였음] 읽기 모드(아직 클릭 전)는 화살표,
   실제로 입력 중인 편집 모드는 I자로 보여야 자연스러운데, 예전엔 반대로
   읽기 모드가 cursor:text, 편집 모드가 cursor:default였습니다. 편집 모드에
   들어가면 이 wrapper 내부가 실제 입력창(contenteditable div,
   .board-inline-block-input 등)으로 바뀌는데 그 입력창 자체엔 cursor
   속성이 없어 부모 값을 그대로 물려받으므로, 편집 중에도 정작 글자를
   치는 칸 위에서 화살표가 보였습니다. */
.board-block-display-editable {
  cursor: default;
  border-radius: var(--radius-sm);
  transition: background-color 0.12s ease;
}
.board-block-display-editable:hover {
  background: var(--color-bg);
}
.board-block-display-editable[data-editing="1"] {
  background: transparent;
  cursor: text;
}
/* [워크스페이스 "노션 스타일" 확장 - 세션 3/6] 블록마다 hover 시에만
   나타나는 드래그 손잡이·"+"·✕. 평소엔 opacity:0 + pointer-events:none으로
   완전히 숨겨(자리는 차지하되 보이지도, 클릭되지도 않음), 그 블록 행에
   마우스를 올렸을 때만 드러납니다. 터치 환경(hover 없음)에서의 대안 노출
   방식은 4단계(모바일 폴리싱) 몫으로 남겨뒀습니다. */
.board-block-row-display {
  display: flex;
  align-items: flex-start;
  gap: 2px;
}
.board-block-row-display > .board-block-display,
.board-block-row-display > .board-block-display-editable {
  flex: 1;
  min-width: 0;
}
.board-block-hover-rail {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  width: 20px;
  padding-top: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
.board-block-inline-drag-handle {
  cursor: grab;
  color: var(--color-text-secondary);
  user-select: none;
  font-size: var(--fs-desc-sm);
}
/* [사용자 요청 - "블록 추가" 진입점 개편] 블록마다 hover 시 뜨던 "+"는
   없앴습니다. 대신 (C) 블록 "사이" 틈에서만 나타나는 얇은 "+" 줄과,
   (B) 본문 맨 아래 항상 보이는 "+ 블록 추가" 버튼 두 가지를 씁니다.

   (C) board-block-divider: 실제 마우스 좌표를 JS로 추적하는 대신, 블록
   사이마다 얇은 실제 DOM 요소를 두고 그 요소 자체에 CSS :hover를 거는
   방식입니다 — 좌표 계산 없이 "그 틈에 마우스가 있을 때만" 보이는 같은
   효과를 냅니다. margin을 음수로 줘 위아래 블록과 겹치게 해, 마우스가
   블록에서 틈으로 넘어갈 때 끊기지 않게 합니다.
   hover 자체가 없는 터치 환경(@media (hover:hover) and (pointer:fine)
   밖)에서는 이 틈을 아예 노출하지 않습니다 — 터치에서 "여기 누르면
   되나?" 하고 헤매게 하느니, 처음부터 안 보이게 하고 아래 (B) 버튼 하나로
   안내하는 편이 낫습니다. */
.board-block-divider {
  position: relative;
  height: 15px;
  margin: -8px 0;
  z-index: 2;
}
.board-block-divider-add-btn {
  display: none;
}
@media (hover: hover) and (pointer: fine) {
  .board-block-divider::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 2px;
    background: var(--color-primary);
    opacity: 0;
    transition: opacity 0.12s ease;
    pointer-events: none;
  }
  .board-block-divider:hover::before {
    opacity: 0.4;
  }
  .board-block-divider-add-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    /* [사용자 요청] 예전엔 왼쪽(left:6px)에 붙어 있었는데, 줄 중앙으로
       옮겨서 노션처럼 "+"가 항상 선 가운데에 오도록 맞췄습니다. */
    left: 50%;
    top: 50%;
    width: 20px;
    height: 20px;
    transform: translate(-50%, -50%) scale(0.7);
    border-radius: 50%;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    cursor: pointer;
    font-size: var(--fs-desc-sm);
    line-height: 1;
    padding: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease, transform 0.12s ease;
  }
  .board-block-divider:hover .board-block-divider-add-btn {
    opacity: 1;
    pointer-events: auto;
    transform: translate(-50%, -50%) scale(1);
  }
  .board-block-divider-add-btn:hover {
    background: var(--color-bg);
    color: var(--color-text);
    border-color: var(--color-primary);
  }
}

/* (B) board-block-add-footer-row / board-block-add-footer-btn: 본문 맨
   끝에 항상 고정으로 보이는 블록 추가 버튼들. [사용자 요청 - 개편] 예전엔
   "+ 블록 추가" 버튼 하나만 두고 눌러야 타입 메뉴가 떴는데, 이제 새
   글쓰기 화면과 똑같이 10종 타입 버튼을 한 줄에 바로 늘어놓아 클릭 한
   번으로 원하는 타입을 바로 추가합니다. 버튼 낱개의 디자인(점선 테두리)은
   기존 "+ 블록 추가" 버튼 스타일을 그대로 유지하고, width:100%였던 부분만
   여러 개가 한 줄에 나란히 흐르도록(inline-flex + 줄바꿈 컨테이너) 바꿨습니다.
   hover가 필요 없어 데스크톱·터치 모두 동일하게 동작하고, 특히 (C)가
   꺼져 있는 터치 환경에서는 블록을 추가하는 사실상 유일한 진입점입니다. */
.board-block-add-footer-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
}
.board-block-add-footer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px 10px;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  font-size: var(--fs-desc-sm);
  white-space: nowrap;
}
.board-block-add-footer-btn:hover {
  background: var(--color-bg);
  color: var(--color-text);
  border-color: var(--color-primary);
}
.board-block-inline-delete-btn {
  flex-shrink: 0;
  margin-top: 2px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
.board-block-inline-delete-btn:hover {
  color: var(--color-danger);
}
.board-block-row-display:hover .board-block-hover-rail,
.board-block-row-display:hover > .board-block-inline-delete-btn {
  opacity: 1;
  pointer-events: auto;
}
.board-block-row-display.dragging {
  opacity: 0.4;
}
.board-block-row-display.drag-over {
  background: var(--color-primary-soft);
  border-radius: var(--radius-sm);
}

/* [워크스페이스 "노션 스타일" 확장 - 세션 5/6] 표(table) 블록.
   표시는 기존 .data-table을 그대로 재사용하되, 그 클래스가 원래
   "클릭해서 이동하는 목록 행"을 전제로 한 tbody tr { cursor:pointer }를
   갖고 있어(관리자 목록 등) 여기서는 오해를 주지 않도록 되돌립니다. */
.board-block-display .data-table tbody tr,
.board-inline-table tbody tr {
  cursor: default;
}
.board-block-display .data-table tbody tr:hover {
  background: transparent;
}
.board-inline-table-editor {
  margin: 8px 0;
}
.board-inline-table {
  width: 100%;
  border-collapse: collapse;
}
/* [버그 수정 - 열 추가/삭제가 반응 없는 것처럼 보임] .data-table은 원래 가로
   구분선(border-bottom)만 있고 세로 구분선이 없어서, 행을 추가하면 새
   가로줄이 뚜렷이 보이는 반면 열을 추가/삭제해도 눈에 띄는 변화가 없어
   "버튼이 반응 안 한다"는 오해를 샀습니다. 표 블록(.board-inline-table)에만
   세로 구분선을 추가해 열의 변화도 행처럼 바로 보이게 합니다(다른 곳의
   .data-table은 원래 모양 그대로 유지). 편집 화면 맨 끝 "행 삭제" 칸은
   실제 열이 아니므로 구분선에서 제외합니다. */
.board-inline-table th,
.board-inline-table td {
  border-right: 1px solid var(--color-border);
}
.board-inline-table th:last-child,
.board-inline-table td:last-child,
.board-inline-table .board-inline-table-row-actions {
  border-right: none;
}
/* [버그 수정 - 읽기/수정 크기 불일치] 예전엔 셀 padding을 4px 6px로 따로
   좁혀서 읽기 모드(.data-table td, 10px 14px)보다 표가 눈에 띄게 작아
   보였습니다. 이 표는 항상 .data-table 클래스를 함께 쓰므로(위 마크업
   참고), 별도 규칙 없이 .data-table td/th의 padding을 그대로 물려받게
   두면 저절로 읽기 모드와 같은 크기가 됩니다. */
.board-inline-table-cell {
  width: 100%;
  min-width: 60px;
}
.board-inline-table-row-actions {
  width: 28px;
  padding: 0 !important;
  border-bottom: none !important;
}
.board-inline-table-row-delete-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  padding: 2px 4px;
}
.board-inline-table-row-delete-btn:hover {
  color: var(--color-danger);
}
/* [사용자 요청 - 열 삭제 기능] 행 삭제 버튼과 동일한 모양으로, 표 맨 아래
   열마다 하나씩 두는 삭제 버튼 전용 행입니다. */
.board-inline-table-col-actions td {
  text-align: center;
  padding: 2px 0 !important;
}
.board-block-table-col-delete-btn,
.board-inline-table-col-delete-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  padding: 2px 4px;
}
.board-block-table-col-delete-btn:hover,
.board-inline-table-col-delete-btn:hover {
  color: var(--color-danger);
}
/* [사용자 요청 - 열 폭 드래그 조정] 첫 행 셀 오른쪽 끝의 가는 드래그 손잡이.
   평소엔 거의 안 보이다가(투명) 마우스를 올리면 강조색으로 드러납니다.
   셀(th/td)에는 인라인 style="position:relative;"가 이미 붙어있어(마크업
   참고) 이 손잡이가 그 셀 오른쪽 끝에 절대위치로 붙습니다. */
.board-block-table-col-resize-handle,
.board-inline-table-col-resize-handle {
  position: absolute;
  top: 0;
  right: -3px;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 2;
  background: transparent;
}
.board-block-table-col-resize-handle:hover,
.board-inline-table-col-resize-handle:hover {
  background: var(--color-primary);
  opacity: 0.5;
}
/* [사용자 요청 - 셀 병합] 마우스로 끌어서 고른 칸 범위를 표시합니다. */
.board-block-table-cell-selected,
.board-inline-table-cell-selected {
  background: var(--color-primary-soft) !important;
  outline: 1px solid var(--color-primary);
  outline-offset: -1px;
}
.board-inline-table-controls {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-top: 6px;
  flex-wrap: wrap;
}
.board-inline-table-header-label {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-desc-sm);
  color: var(--color-text-secondary);
}

/* [세션 5/6] 토글(toggle) 블록. 제목 옆 화살표(▸/▾)를 누르면 펼침/접힘만
   즉시 저장되고(체크리스트 체크박스와 동일한 방식), 하위 항목은 들여쓰기로
   구분해 보여줍니다. 무한 중첩은 지원하지 않으므로(1단계 깊이만) 하위
   항목 안에는 또 다른 토글 화살표가 나타나지 않습니다. */
.board-toggle-block {
  margin: 6px 0;
}
.board-toggle-header {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}
.board-toggle-expand-btn {
  flex-shrink: 0;
  width: 20px;
  height: 24px;
  line-height: 24px;
  text-align: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  font-size: var(--fs-desc-sm);
}
.board-toggle-expand-btn:hover {
  color: var(--color-text);
}
.board-toggle-title {
  flex: 1;
  line-height: var(--lh-ui);
  font-weight: var(--fw-semibold);
  padding-top: 1px;
}
.board-toggle-children {
  margin: 4px 0 4px 26px;
  padding-left: 10px;
  border-left: 2px solid var(--color-border);
}
.board-toggle-child {
  margin: 4px 0;
}
/* [사용자 요청 - 읽기/수정 위치 일치] 읽기 모드 .board-toggle-children의
   margin(4px 0 4px 26px)과 정확히 맞춥니다(이전엔 상하 6px로 어긋나
   있었음). */
.board-inline-toggle-children-editor {
  margin: 4px 0 4px 26px;
}
.board-inline-toggle-child-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 4px 0;
}
.board-inline-toggle-child-input {
  flex: 1;
}
.board-inline-toggle-child-delete-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  flex-shrink: 0;
}
.board-inline-toggle-child-delete-btn:hover {
  color: var(--color-danger);
}
.board-inline-toggle-child-add-btn {
  margin: 4px 0 4px 26px;
}
/* [워크스페이스 리치텍스트 에디터 - 2단계]가 만든 .board-block-richtext의
   빈 칸 안내 문구 패턴을 토글 제목 입력창에도 재사용합니다(클래스만 다름). */
.board-inline-richtext:empty:before {
  content: attr(data-placeholder);
  color: var(--color-text-secondary);
  pointer-events: none;
}
/* [사용자 요청 - 새 글쓰기/수정 통일] 새 글쓰기 화면(전체 폼)의 토글 블록
   제목 입력창도 같은 빈 칸 안내 문구 패턴을 씁니다. */
.board-block-toggle-title:empty:before {
  content: attr(data-placeholder);
  color: var(--color-text-secondary);
  pointer-events: none;
}
/* [버그 수정 - 읽기/수정 크기 불일치] 체크리스트 항목의 contenteditable
   입력창도 같은 빈 칸 안내 문구 패턴을 씁니다. */
.board-inline-checklist-text:empty:before {
  content: attr(data-placeholder);
  color: var(--color-text-secondary);
  pointer-events: none;
}
/* [버그 수정 - 읽기/수정 크기 불일치] 소제목·리스트 항목·토글 하위 항목도
   <input>에서 contenteditable div로 바뀌면서 같은 빈 칸 안내 문구 패턴이
   필요합니다(표 셀은 원래도 placeholder가 없었으므로 대상에서 제외). */
.board-inline-heading-text:empty:before,
.board-inline-list-item-input:empty:before,
.board-inline-toggle-child-input:empty:before {
  content: attr(data-placeholder);
  color: var(--color-text-secondary);
  pointer-events: none;
}

/* @멘션 자동완성 드롭다운 — 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;
}

/* [사용자 요청 - 검색 결과 한 줄 표시] 이름 옆에 "직급 · 직책 · 부서"까지
   붙다 보니, 드롭다운 폭이 좁을 때(예: 전자결재 결재선처럼 입력창 자체가
   150px로 좁은 경우) 이 줄이 두 줄로 줄바꿈됐습니다. 한 사람 정보는
   항상 한 줄로 보이는 게 자연스러우므로 줄바꿈을 막고, 대신 드롭다운
   폭이 내용에 맞춰 늘어나도록 아래 JS의 width 계산과 함께 동작합니다. */
.mention-dropdown-item span {
  white-space: nowrap;
}

.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-submitted-row,
.dashboard-notice-row,
.dashboard-schedule-row,
.dashboard-post-row {
  padding: 6px 0;
  line-height: 1.3;
}

/* [43단계] 대시보드 카드 가로폭이 좁아지면 목록 한 줄(제목 + 뱃지/시간)이
   두 줄로 줄바꿈되지 않도록, 제목은 남는 공간을 차지하며 한 줄로
   말줄임(...) 처리하고 뱃지/시간처럼 짧은 요소는 줄어들지 않게 고정합니다. */
.dashboard-submitted-row .list-item-title,
.dashboard-notice-row .list-item-title,
.dashboard-schedule-row .list-item-title,
.dashboard-post-row .list-item-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dashboard-submitted-row > *:not(.list-item-title),
.dashboard-notice-row > *:not(.list-item-title),
.dashboard-schedule-row > *:not(.list-item-title),
.dashboard-post-row > *:not(.list-item-title) {
  flex-shrink: 0;
}

/* ---------- 6. 반응형 (모바일 대응) ----------
   768px 이하에서는 사이드바를 화면 밖으로 숨기고,
   햄버거 버튼으로 슬라이드 인/아웃 시킵니다. */
@media (max-width: 768px) {
  :root {
    --content-padding: var(--space-lg);
    /* 좁은 화면에서는 제목류 크기를 한 단계씩 낮춰 상단바/모달이 여유 있게 보이도록 합니다. */
    --topbar-height: 56px;
    /* [bmp 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) 모바일
       사파리에서 아무 입력창이나 탭할 때마다 화면이 훅 확대됐다가 초점을
       옮기면 다시 축소되는 현상이 있었습니다.

       [bmp 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;
  }

  /* [워크스페이스 리치텍스트 에디터 - 5단계 폴리싱] 블록 편집기 한 줄(드래그 손잡이 +
     타입 select + 입력창 + ▲▼ + 삭제)이 전부 inline-flex(줄바꿈 없음)로 붙어 있어,
     좁은 화면에서는 입력창만 계속 눌려 작아지다가 실질적으로 입력이 불편해지는
     문제가 있었습니다(실사용 점검 중 발견). 이 너비부터는 줄바꿈을 허용해
     "손잡이+타입+▲▼+삭제"가 먼저 한 줄을 채우고 입력창이 그 아래 전체 폭을
     쓰도록 합니다. */
  .board-block-row {
    flex-wrap: wrap;
  }

  /* 입력창/필드 영역만 다음 줄로 내려 전체 폭을 쓰게 하고, 손잡이·타입 select·
     ▲▼·삭제 같은 작은 조작 버튼들은 원래처럼 한 줄에 모여 있게 둡니다.
     체크리스트 입력창(.board-block-text-input이지만 체크박스 바로 옆에 있어야
     하는 경우)은 제외합니다 — heading 입력창만 별도 클래스로 구분해 지정. */
  .board-block-row > input.board-block-heading-input,
  .board-block-row > div[style*="flex:1"] {
    flex-basis: 100%;
    margin-top: 6px !important;
  }

  /* 이미지/파일 블록(attachmentBlockEditorRowHTML)은 입력창이 없는 미리보기+
     파일명 구조라 줄바꿈 대상에서 제외하고 그대로 한 줄 레이아웃을 유지합니다
     (미리보기 72px 정사각형이라 좁은 화면에서도 이미 잘 맞음). */
  .board-block-row.board-block-row-attachment {
    flex-wrap: nowrap;
  }
}

@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가 부여하는 클래스).
   모든 화면 크기에서 적용됩니다 — 화면 폭과 무관하게 스크롤 손맛 자체는
   항상 좋아야 하기 때문입니다.
   [스크롤바 숨김] 스크롤 "동작"(overflow-y:auto, 렌더러의 인라인 style)은
   그대로 두고 스크롤바 "표시"만 숨깁니다 — .orgchart-card-wrapper에 쓴 것과
   동일한 3종 규칙(Firefox/구버전 Edge·IE/Chrome·Safari·신버전 Edge)입니다.
   예전에는 scrollbar-width:thin + 8px짜리 ::-webkit-scrollbar만 있어 실제로는
   숨겨지지 않았는데, 이 스크롤바 폭이 요일 헤더/종일 띠 줄(스크롤바 없음)과
   시간표 줄(스크롤바 있음) 사이의 실제 콘텐츠 폭을 다르게 만들어 주간 보기에서
   두 줄의 세로 칸(열)이 서로 어긋나 보이는 원인이기도 했습니다. width/height:0을
   쓰는 이유(= display:none 대신): 일부 WebKit 엔진에서 display:none은 스크롤
   동작 자체를 막을 수 있기 때문입니다. */
.schedule-timeline-viewport {
  -webkit-overflow-scrolling: touch; /* iOS 관성 스크롤 */
  overscroll-behavior: contain; /* 끝까지 스크롤해도 뒤의 페이지가 함께 스크롤되지 않도록 */
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* 구버전 Edge/IE */
}

.schedule-timeline-viewport::-webkit-scrollbar {
  width: 0;
  height: 0;
  background: transparent;
}

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

  /* [반응형 이벤트 레이아웃] 좁은 화면에서는 일정 칩 글자가 너무 작아지지
     않도록 최소 크기를 보장합니다. 칩 크기 자체(폭/높이)는 각 렌더러가
     이미 화면 폭에 맞춰 넘겨준 값(인라인 style)을 그대로 따릅니다 — 여기서는
     본문 글자 크기만 살짝 올려 가독성을 보정합니다. */
  .schedule-event-chip,
  .schedule-event-bar {
    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); }
/* [사용자 요청 - 조직도 "겸"/"조직장" 배지 간격] 기존 ml-8(8px)의 50%인 4px.
   조직도의 겸직 조직장 표시에서 "겸"↔"조직장" 배지 사이 간격 전용으로 씁니다. */
.ml-4  { margin-left: var(--space-xs); }

/* ---------- 타이포그래피 / 텍스트 색 ---------- */
.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; }
/* [버그 수정 - 구조 감사 7라운드] admin/auditLog.js와 admin/fileStorage.js의
   페이지 이동 버튼이 "d-flex justify-center gap-6"을 쓰고 있었는데,
   justify-center 클래스 자체가 정의돼 있지 않아 display:flex + gap만
   적용되고 가운데 정렬은 전혀 되지 않고 있었습니다(approval/listView.js의
   같은 용도 페이지네이션이 이 문제를 피하려고 인라인 스타일로 직접
   중앙 정렬한 이유를 남긴 주석 참고). 새 클래스를 만드는 대신 지금
   실제로 쓰이고 있던 이름(justify-center) 그대로 정의를 채워 넣어,
   기존 마크업을 건드리지 않고 실제로 가운데 정렬되게 고칩니다.
   align-items-center도 같은 이유로 함께 채워둡니다(아직 쓰는 곳은
   없지만, 앞으로 justify-center와 짝으로 또 없는 클래스인 채 쓰이는
   사고를 막기 위함). */
.justify-center { justify-content: center; }
.align-items-center { align-items: center; }
.gap-6        { gap: 6px; }
.gap-8        { gap: var(--space-sm); }

/* [탭 바 공용 컴포넌트] key/label 탭 버튼 여러 개를 감싸는 컨테이너.
   admin.js·approval.js·board.js·leaveManagement.js·schedule.js가 각자
   거의 같은 인라인 스타일(display:flex + flex-wrap:wrap + gap)을
   따로 반복해서 갖고 있었고, 그중 하나(approval.js)만 flex-wrap이
   빠져 있어 라벨이 긴 탭이 좁은 화면에서 줄바꿈되지 못하고 넘쳐
   깨지는 사고가 있었습니다(README 121번). 다섯 곳 모두 이 클래스
   하나로 통일해, 앞으로 탭 바를 새로 만들 때도 flex-wrap을 깜빡할
   수 없게 합니다. 바깥 여백(margin-bottom 등)은 화면마다 배치가
   달라 이 클래스에 포함하지 않고, 필요한 곳에서 style로 따로 줍니다. */
.tab-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

/* [사용자 지적 - "부서 캘린더를 클릭하면 나타나는 선택창의 디자인이 왜
   다른(예: 워크스페이스 새글쓰기 선택창)과 다르지?"] .tab-bar 안에
   .btn 버튼들과 나란히 놓이는 <select>(일정의 "부서 캘린더" 선택창)를
   위해 한때 전용 클래스(.tab-bar-select)를 따로 두고, 그 안에서
   .login-input과 같은 값을 손으로 다시 맞추려 했습니다. 값 하나(패딩,
   테두리 굵기, 배경색 등)를 나중에 고칠 때마다 두 규칙 중 한쪽만
   고치는 사고가 반복됐고, 그때마다 "왜 다르지?"라는 지적으로
   이어졌습니다.

   이제 그 select(js/modules/schedule/toolbar.js)는 전용 클래스 없이
   class="login-input"만 그대로 씁니다 — 워크스페이스의 진행상태/
   중요도/담당자/검색창과 완전히 같은 CSS 규칙 하나를 공유하므로,
   두 곳을 따로 관리하다 어긋나는 사고 자체가 구조적으로 불가능합니다.
   .tab-bar 클래스 자체(아래)는 계속 씁니다 — flex 정렬/줄바꿈만
   담당하고 안에 놓이는 버튼/select의 생김새에는 관여하지 않습니다. */

/* ---------- 버튼 크기/색상 변형 ----------
   기존 .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. 브레드크럼 ----------
   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;
}

/* ---------- 11. 표 정렬 ----------
   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);
}


/* ---------- 12. 터치 기기용 클릭 타깃 확대 ----------
   [접근성] 위 크기들은 마우스를 쓰는 데스크톱(이 앱의 기준 환경)에
   맞춘 값입니다. 손가락은 마우스 포인터보다 훨씬 뭉툭해서, 같은
   크기라도 터치에서는 빗나가기 쉽습니다.
   `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)));
  }

  /* [워크스페이스 "노션 스타일" 확장 - 세션 4/6 폴리싱] 3단계에서 만든
     드래그 손잡이·+·✕는 마우스 hover에서만 나타나게 설계했는데, 터치
     환경(pointer: coarse — 이 프로젝트가 이미 버튼 padding 보정에 쓰고
     있는 것과 같은 기준)에는 hover 자체가 없어 이 버튼들에 영영 닿을
     방법이 없었습니다. 손가락으로 탭해서 펼치는 방식(추가 상태 관리 필요)
     대신, 터치 환경에서는 처음부터 옅게 항상 보이도록 해 최소한 "여기
     누르면 된다"는 것을 알 수 있고 실제로 누를 수 있게 합니다. */
  .board-block-hover-rail,
  .board-block-inline-delete-btn {
    opacity: 0.55;
    pointer-events: auto;
  }
}

/* [당일 수정사항 표시] 워크스페이스에서 오늘 실제로 바뀐 글자만 강조 */
.board-today-change {
  color: #D97706;
}

/* [신규 글 배지] 작성된 지 24시간이 안 지난 글의 제목 끝에 붙는 작은 "new" 배지.
   .board-today-change(수정 강조, 앰버)와는 별개 표시라 겹칠 수 있습니다. */
.board-new-badge {
  display: inline-block;
  vertical-align: middle;
  background: #E53935;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 2px 5px;
  border-radius: 999px;
  margin-left: 2px;
}

/* 체크리스트는 본문 색을 바꾸지 않고, 오늘 완료 상태가 바뀐 체크박스만 강조합니다.
   체크됨은 accent-color로, 체크 해제됨은 네이티브 빈 체크박스가 색을 잃지 않도록
   얇은 outline으로 같은 #D97706을 보여줍니다. */
.board-checklist-toggle.board-today-check-change,
.board-toggle-child-checkbox.board-today-check-change {
  accent-color: #D97706;
}

.board-checklist-toggle.board-today-check-change:not(:checked),
.board-toggle-child-checkbox.board-today-check-change:not(:checked) {
  outline: 2px solid #D97706;
  outline-offset: 1px;
  border-radius: 2px;
}


/* 시스템관리 계정 목록 */
.admin-tabs { margin-bottom: 12px; }
.admin-toolbar { display: flex; justify-content: flex-start; margin-bottom: 12px; }
.admin-user-row .list-item-title { font-weight: 600; display: flex; align-items: center; flex-wrap: wrap; }
.admin-user-row .admin-role-btn { margin-left: 8px; border: 0; cursor: pointer; }
.admin-user-row .admin-pw-btn { width: 24px; height: 24px; font-size: var(--fs-desc-sm); margin-left: 4px; }
.admin-user-row .admin-unlock-btn { background: var(--color-warning); color: #fff; }
/* [사용자 요청] 삭제 버튼을 사각형 "btn"(빨간 배경 + 흰 글자)에서
   🔑(admin-pw-btn)와 같은 자리(이름 줄)에 어울리는 작은 원형 아이콘
   버튼으로 바꿨습니다 - 배경을 꽉 채우는 대신, 평소엔 위험 색 아이콘만
   보이다가 hover 시에만 옅은 위험색 배경(--color-danger-soft, 다른
   곳의 .badge-danger와 같은 톤)이 뜨는 절제된 스타일입니다. */
.admin-user-row .admin-delete-btn { width: 24px; height: 24px; font-size: var(--fs-desc-sm); margin-left: 2px; color: var(--color-danger); }
.admin-user-row .admin-delete-btn:hover { background: var(--color-danger-soft); }
.admin-group-option.is-locked { opacity: .55; cursor: not-allowed; }

/* 시스템관리 모듈의 정적 인라인 스타일을 CSS로 통합 */
.admin-section-spacer { margin-top:14px; }
.admin-muted-box { padding:6px 8px;color:var(--color-text-secondary); }
.admin-stat-card { flex:1;min-width:120px;padding:12px;border:1px solid var(--color-border);border-radius:var(--radius-sm); }
.admin-stat-label { font-size:var(--fs-caption);color:var(--color-text-secondary);margin-bottom:4px; }
.admin-stat-value { font-size:var(--fs-h2);font-weight:700; }
.admin-action-row { display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px; }
.admin-stat-caption { color:var(--color-text-secondary);font-size:var(--fs-caption);margin-top:2px; }
.admin-section-desc { font-size:var(--fs-desc-sm);color:var(--color-text-secondary);margin-bottom:12px; }
.admin-section-gap-lg { margin-bottom:28px; }
.admin-error-desc { font-size:var(--fs-desc-sm);color:var(--color-danger);margin-bottom:12px; }
.admin-hint-text { margin-top:10px;font-size:var(--fs-desc-sm); }
.admin-summary-card { flex:1;min-width:140px;max-width:100%;box-sizing:border-box;padding:12px;border:1px solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden; }
.admin-summary-title { font-size:var(--fs-h3);font-weight:700;overflow-wrap:break-word;word-break:break-word; }
.admin-summary-subtitle { font-size:14px;color:var(--color-text-secondary);margin-top:2px;overflow-wrap:break-word;word-break:break-word; }
.admin-danger-title { color:var(--color-danger);margin-bottom:4px; }
.admin-desc-relaxed { font-size:var(--fs-desc-sm);line-height:1.5; }
.admin-detail-row { display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--color-border);font-size:var(--fs-desc-sm); }
/* [레이아웃 개선 - 유지보수 화면] 예전엔 max-width:360px로 폭을 직접
   고정했지만, 지금은 이 목록이 2단 카드 레이아웃(admin-card-grid) 안의
   .card 하나를 그대로 채우므로 카드 폭이 곧 목록 폭이 됩니다. 목록이
   그 카드의 마지막 요소라 아래쪽 여백도 카드 자체 padding으로 충분해
   margin-bottom을 0으로 뺐습니다. */
.admin-no-gap { margin-bottom:0; }

/* [시스템설정 > 디자인설정] admin/designSettings.js 전용 - 위 admin-inline-XX와
   같은 이유(정적 인라인 스타일을 CSS로 통합)로 이름 있는 클래스를 씁니다. */
.admin-inline-ds-banner {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px 14px;
  background: var(--color-warning-soft);
  border: 1px solid var(--color-warning);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--fs-desc-sm);
  margin-bottom: var(--space-md);
}
.admin-inline-ds-card { margin-bottom: var(--space-md); }
.admin-inline-ds-subtitle { font-size: var(--fs-desc-sm); margin-bottom: var(--space-md); }
.admin-inline-ds-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 10px 0;
  border-bottom: 1px solid var(--color-border);
}
/* [핵심] :last-child가 아니라 :last-of-type을 씁니다 - 앱 아이콘 카드는
   이 행(파일 선택 행) 바로 다음에 형제 요소로 .admin-inline-ds-icon-previews가
   와서 이 행이 카드의 "마지막 자식"은 아니지만, admin-inline-ds-row로서는
   "마지막"이라 여기서 구분선을 지워야 합니다. :last-child로는 이 조건을
   못 잡아 구분선이 남아있었고, 그 아래 여백 없는 아이콘 미리보기 상자와
   바로 붙어 보이는 문제가 있었습니다. */
.admin-inline-ds-row:last-of-type { border-bottom: none; }
.admin-inline-ds-row-body { flex: 1; min-width: 0; }
.admin-inline-ds-hint { font-size: var(--fs-caption); margin-top: 2px; }
.admin-inline-ds-logo-preview {
  width: 64px;
  height: 40px;
  object-fit: contain;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  flex-shrink: 0;
}
/* [레이아웃 개선] 위 파일 선택 행과 최소한의 간격을 두어, 구분선이 없어진
   뒤에도 미리보기 상자들이 행에 바로 들러붙어 보이지 않게 합니다. */
.admin-inline-ds-icon-previews {
  display: flex;
  gap: var(--space-lg);
  flex-wrap: wrap;
  margin-top: var(--space-md);
}
.admin-inline-ds-icon-preview-item { text-align: center; }
.admin-inline-ds-icon-preview-img,
.admin-inline-ds-icon-preview-empty {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  margin: 0 auto 6px;
}
.admin-inline-ds-icon-preview-img { object-fit: contain; background: var(--color-bg); }
.admin-inline-ds-icon-preview-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-caption);
  color: var(--color-text-secondary);
  background: var(--color-bg);
}
/* ============================================================
   [시스템관리 > 공개자료] QR로 배포하는 공개 PDF 목록/등록 화면
   ------------------------------------------------------------
   등록 폼은 "제목 + 주소" 한 줄, 그 아래 넓은 파일 드롭존, 맨 아래
   오른쪽 정렬 등록 버튼의 3단 구조입니다. 처음에는 세 항목을 모두 같은
   auto-fit 그리드에 넣었는데, 파일 선택 버튼이 입력칸 옆 좁은 칸에
   끼면서 어디를 눌러야 파일을 고르는지 알아보기 어려웠습니다 — 성격이
   다른 입력(텍스트 2개 vs 파일 1개)을 같은 줄에 억지로 맞추지 않고
   분리했습니다.
   ============================================================ */
.pf-upload-card { margin-bottom: var(--space-md); }
.pf-card-head { margin-bottom: var(--space-md); }
.pf-card-desc { font-size: var(--fs-desc-sm); margin-top: 2px; }

/* 제목/주소 2단 - 좁아지면 자동으로 세로로 쌓입니다. */
.pf-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}
.pf-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.pf-label { font-size: var(--fs-desc-sm); color: var(--color-text-secondary); }
.pf-label-optional { font-size: var(--fs-caption); opacity: 0.8; }
.pf-input { width: 100%; box-sizing: border-box; }

/* 주소 입력칸: 접두 "/"를 입력칸 안에 넣어 이 값이 URL 조각임을 드러냅니다.
   (예전에는 입력칸 바깥에 "/"만 떠 있어서 앞 칸에 붙은 기호처럼 보였습니다) */
.pf-slug-input {
  display: flex;
  align-items: center;
  min-width: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  padding-left: 10px;
  gap: 2px;
}
.pf-slug-input:focus-within { border-color: var(--color-primary); }
.pf-slug-prefix { color: var(--color-text-secondary); font-family: var(--font-mono, monospace); flex-shrink: 0; }
/* 안쪽 input은 테두리를 없애 바깥 상자와 하나처럼 보이게 합니다. */
.pf-slug-input .pf-input { flex: 1; min-width: 0; border: none; background: transparent; padding-left: 4px; }
.pf-slug-input .pf-input:focus { border-color: transparent; box-shadow: none; outline: none; }

/* 파일 드롭존: 클릭도 되고 드래그&드롭도 되는 넓은 영역. */
.pf-dropzone {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 14px 16px;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  cursor: pointer;
  margin-bottom: var(--space-md);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.pf-dropzone:hover,
.pf-dropzone:focus-visible { border-color: var(--color-primary); }
.pf-dropzone.is-dragover { border-color: var(--color-primary); background: var(--color-primary-soft); }
.pf-dropzone.is-selected { border-style: solid; }
.pf-dropzone-icon { font-size: 22px; line-height: 1; flex-shrink: 0; }
.pf-dropzone-body { flex: 1; min-width: 0; }
.pf-dropzone-title { font-size: var(--fs-desc-sm); overflow-wrap: anywhere; }
.pf-dropzone-hint { font-size: var(--fs-caption); color: var(--color-text-secondary); margin-top: 2px; }
/* 실제 클릭은 드롭존 전체가 받으므로, 안쪽 '파일 선택'은 버튼 모양의 표시일 뿐입니다. */
.pf-dropzone-btn { flex-shrink: 0; pointer-events: none; }

.pf-submit-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.pf-submit-hint { font-size: var(--fs-caption); }

/* 여러 줄 안내문 배너: 공용 배너가 display:flex라 <b> 등 인라인 강조가
   flex 아이템으로 쪼개집니다. 이 화면 배너는 문장이므로 일반 흐름으로 둡니다. */
.pf-banner { display: block; line-height: 1.6; }

/* 접속 통계 칸: 숫자가 오른쪽 정렬되면 자릿수 비교가 쉽습니다. */
.pf-hits-col { text-align: right; white-space: nowrap; }
.pf-hits-value { font-variant-numeric: tabular-nums; cursor: help; }
.pf-url-cell span { font-family: var(--font-mono, monospace); word-break: break-all; }
/* 작업 버튼이 여러 개라 좁은 화면에서는 줄바꿈되어야 표가 옆으로 밀리지 않습니다. */
.pf-actions { display: flex; flex-wrap: wrap; gap: 4px; }
.pf-qr-modal { display: flex; flex-direction: column; align-items: center; gap: var(--space-md); text-align: center; }
/* QR은 픽셀 단위 그림이라 확대 시 보간되면 스캔이 실패할 수 있어 픽셀을 그대로 키웁니다. */
.pf-qr-canvas {
  image-rendering: pixelated;
  max-width: 100%;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: #fff;
}
/* QR 주소 종류 선택(안내 페이지 / PDF 직접 / 다운로드) - 인쇄 전에 고릅니다. */
.pf-qr-modes { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.pf-qr-mode-btn.is-active {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-text-inverse);
}
.pf-qr-url { font-size: var(--fs-caption); word-break: break-all; color: var(--color-text-secondary); }
.pf-qr-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.pf-edit-modal { display: flex; flex-direction: column; gap: var(--space-md); }
.pf-edit-warning {
  padding: 8px 10px;
  border-left: 3px solid var(--color-warning);
  background: var(--color-warning-soft);
  border-radius: var(--radius-sm);
  font-size: var(--fs-desc-sm);
  color: var(--color-text-secondary);
}
@media (max-width: 560px) {
  /* 모바일에서는 드롭존 안의 버튼 표시가 자리를 많이 먹어 감춥니다
     (영역 전체가 이미 눌리는 버튼입니다). */
  .pf-dropzone-btn { display: none; }
  .pf-submit-row { justify-content: stretch; }
  .pf-submit-row .btn { width: 100%; }
}
/* 클립보드 API를 못 쓰는 http 환경의 복사 폴백용 - 화면에 보이면 안 됩니다. */
.pf-copy-helper { position: fixed; top: -1000px; left: -1000px; opacity: 0; }

/* [레이아웃 개선 - 유지보수 화면] 예전엔 밑줄 하나로만 구분되는 밋밋한
   행이라 "최근 오류" 여러 건이 나열되면 서로 잘 구분되지 않았습니다.
   빨간 왼쪽 강조선 + 옅은 배경의 카드형 항목으로 바꿔 오류 목록이라는
   성격이 한눈에 드러나게 했습니다. */
.admin-danger-callout { padding:8px 10px;border-left:3px solid var(--color-danger);background:var(--color-danger-soft);border-radius:var(--radius-sm);margin-bottom:8px; }
.admin-log-row-header { display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:2px; }
.admin-no-shrink { flex-shrink:0; }
.admin-log-time { font-size:var(--fs-caption);color:var(--color-text-secondary);white-space:nowrap; }
.admin-log-detail { font-size:var(--fs-desc-sm);overflow-wrap:break-word;word-break:break-word; }
/* [레이아웃 개선 - 유지보수 화면] max-width:560px는 예전에 이 목록이 전체
   폭 카드 안에서 옆으로 너무 넓게 퍼지지 않게 하려던 제약이었지만, 지금은
   2단 카드(admin-card-grid) 안의 .card 하나를 그대로 채우므로 카드 폭이
   곧 목록 폭입니다. 카드의 마지막 요소라 margin-bottom도 0으로 뺐습니다. */
.admin-scroll-panel { margin-bottom:0;max-height:360px;overflow-y:auto; }
.admin-scroll-row { display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid var(--color-border);font-size:var(--fs-desc-sm);gap:8px; }
.admin-wrap-anywhere { overflow-wrap:break-word;word-break:break-word; }
/* [레이아웃 버그 수정] 자동 백업 카드가 admin-inline-28/29/30/32/33을 각각
   "행 컨테이너"·"카드 틀"·"라벨"·"컨트롤" 여러 용도로 뒤섞어 재사용하다가,
   같은 클래스가 카드와 그 안의 라벨에 동시에 적용되며 이중 테두리/여백이
   생기는 등 레이아웃이 깨져 있었습니다. 이미 검증된 진단 카드 그리드
   (admin-stat-grid/13/60/14)로 통일하면서 이 5개 클래스는 더 이상 쓰이지
   않아 제거했습니다(admin-picker-row만 "선택상자+버튼 한 줄" 래퍼로 계속 씁니다). */
.admin-picker-row { display:flex;gap:6px;align-items:center; }
.admin-backup-auto-header { display:flex;align-items:center;justify-content:flex-start;gap:8px;margin-bottom:12px; }
.admin-backup-manual-download-btn { margin-bottom:12px;white-space:nowrap; }
.admin-backup-auto-header .card-title { margin-bottom:0; }
.admin-auto-backup-now-btn { flex-shrink:0;white-space:nowrap; }
.admin-backup-restore-desc { font-size:var(--fs-desc-sm);color:var(--color-text-secondary);margin-bottom:12px; }
.admin-auto-backup-files { display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;max-width:none;margin-bottom:8px; }
.admin-auto-backup-file { min-width:0;padding:10px;border:1px solid var(--color-border);border-radius:var(--radius-sm); }
.admin-auto-backup-file-meta { font-size:var(--fs-desc-sm);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:8px; }
.admin-auto-backup-file-actions { display:flex;gap:6px; }
.admin-auto-backup-file-actions .btn { flex:1;min-width:0;white-space:nowrap; }
.admin-block-gap { margin-bottom:12px; }
.admin-block-desc { font-size:var(--fs-desc-sm);margin-bottom:12px; }
.admin-narrow-field { max-width:480px;margin-bottom:24px; }
.admin-toolbar-row { display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px; }
.admin-plain-desc { font-size:var(--fs-desc-sm); }
.admin-stat-grid { display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:16px; }
.admin-section-gap { margin-bottom:16px; }
.admin-chip-row { display:flex;flex-wrap:wrap;gap:8px; }
.admin-chip { font-size:var(--fs-desc-sm);padding:6px 10px; }
.admin-card-grid { display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px;margin-bottom:16px; }
.admin-panel-header { display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:12px; }
.admin-flush { margin-bottom:0; }
.admin-select-input { padding:6px 10px;font-size:var(--fs-caption);min-width:200px; }
.admin-table-scroll { overflow-x:auto; }
.admin-center-text { text-align:center; }
.admin-field-hint { color:var(--color-text-secondary);font-size:var(--fs-desc-sm);margin-bottom:6px; }
/* [레이아웃 개선 - 유지보수 화면] 통계 카드 그룹 안의 소제목("환경 정보"/
   "자동화 상태" 등). 대문자+자간을 살짝 줘 본문 텍스트와 확실히 구분되는
   레이블처럼 보이게 합니다(카드 제목보다 한 단계 아래 위계). */
.admin-group-heading { font-size:var(--fs-caption);font-weight:var(--fw-semibold);color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.03em;margin:0 0 8px; }
/* [레이아웃 개선 - 유지보수 화면] 카드 제목 바로 아래 붙는 한 줄 설명(보존
   정책 카드, 유지보수 작업 카드가 공용으로 사용). */
.admin-group-desc { font-size:var(--fs-desc-sm);color:var(--color-text-secondary);margin-bottom:12px;line-height:1.5; }
/* [버그 수정 - 유지보수 화면] statCardHTML()의 라벨("PHP 버전" 등)이
   카드 바깥 컨테이너와 같은 클래스(admin-summary-card, border+padding 있음)를
   그대로 재사용하고 있어 라벨만 카드 안에 또 다른 테두리 박스로 겹쳐
   보이는 문제가 있었습니다("PHP 버전"과 "8.2.12"가 서로 다른 테두리에
   갇힌 것처럼 분리돼 보임). 라벨 전용의 테두리 없는 캡션 스타일로
   분리했습니다(admin-stat-label와 동일한 "값 위 라벨" 패턴).*/
.admin-value-label { font-size:var(--fs-caption);color:var(--color-text-secondary);margin-bottom:4px; }
/* [레이아웃 균형 - 자동 백업 카드, 사용자 피드백] "백업 주기"/"시작 시각"
   select가 기본 .login-input 세로폭(9px 12px 패딩, 로그인 폼 기준)을
   그대로 쓰고 있어, 버튼/텍스트 하나만 있는 "상태"/"마지막 자동 백업"
   카드보다 눈에 띄게 높고, 그래서 grid 한 행 안의 다른(내용이 짧은)
   카드들 아래에 불균형한 여백이 남았습니다. 워크스페이스 필터바
   (.board-filter-bar .login-input)가 이미 같은 문제를 겪어 6px 10px +
   caption 크기로 줄여둔 값을 그대로 재사용해, 이 카드의 select 두 개도
   같은 컴팩트한 세로폭으로 맞춥니다. */
.admin-compact-select { padding:6px 10px;font-size:var(--fs-caption); }
/* [레이아웃 버그 수정 - 자동 백업 카드, 사용자 피드백] "시작 시각" 옆
   "해제" 버튼이 select와 같은 flex 행(admin-picker-row)에서 남은 공간을
   두고 select와 경쟁하다가 폭이 눌려, 텍스트가 버튼 안에서 세로로
   줄바꿈(해\n제)되는 문제가 있었습니다. topbar-btn이 이미 같은 문제를
   white-space:nowrap + flex-shrink:0으로 해결해 둔 것과 동일한 처방을
   적용합니다(그룹웨어 전체에 세로로 줄바꿈되는 버튼 디자인은 없습니다).
   select 쪽은 flex-shrink 기본값(1)을 그대로 둬 남는 공간을 계속
   차지하도록 합니다. */
.admin-picker-row > .btn {
  white-space: nowrap;
  flex-shrink: 0;
}
.admin-row-pad { padding:6px 0; }
.admin-inline-note { color:var(--color-text-secondary);font-size:var(--fs-caption); }
.admin-th-cell { text-align:left;padding:8px 0; }
.admin-modal-narrow { max-width:520px; }
.admin-full-field { width:100%;margin-bottom:14px; }
.admin-full-field-lg { width:100%;margin-bottom:18px; }
.admin-divider-block { border-top:1px solid var(--color-border);padding-top:14px; }
.admin-full-width { width:100%; }


.admin-danger-card { border: 1px solid var(--color-danger); background: var(--color-danger-soft); border-radius: var(--radius-sm); padding: 12px 14px; margin-bottom: 12px; }
.admin-backup-status-toggle { font-size: var(--fs-h3); font-weight: 700; padding: 2px 8px; background: transparent; border: none; cursor: pointer; color: var(--color-success); }
.admin-backup-status-toggle.is-disabled { color: var(--color-text-secondary); }
.admin-nowrap-cell { white-space: nowrap; }

/* 시스템관리 계정 권한 행 */
.admin-user-row {
  display: grid !important;
  /* [사용자 요청] 권한 항목을 가로 스크롤 없이 한 줄에 다 보이게 해달라는
     요청에 따라, 예전에는 권한 열을 520px로 고정하고 정보 열이 남는
     공간을 전부 가져가던(minmax(280px, 1fr)) 것을 뒤집었습니다. 이름/
     아이디/직급 등은 380px 넘게 늘어나도 딱히 더 읽기 좋아지지 않으므로
     정보 열은 380px에서 멈추게 하고, 남는 공간을 전부 권한 열(1fr)로
     몰아줍니다 - 화면이 넓을수록 권한 열도 함께 넓어져 항목이 한 줄에
     들어갈 여지가 커집니다. 최소 560px는 보장해 화면이 좁아도 어느 정도
     항목은 한 줄에 보이고, 그래도 다 못 들어가면 .admin-user-permissions의
     overflow-x:auto가 가로 스크롤로 나머지를 보여줍니다. */
  grid-template-columns: minmax(260px, 380px) minmax(560px, 1fr) max-content;
  gap: 16px;
  align-items: center;
}
.admin-user-info {
  min-width: 0;
}
.admin-user-meta {
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  margin-top: 4px;
  overflow-wrap: anywhere;
  line-height: 1.5;
}
/* [레이아웃 버그 수정 + 견고화] 그룹 체크박스(그룹웨어/HR/HR관리/시스템관리)를
   CSS Grid(4열 고정: repeat(4, ...))로 배치하고 있었는데, 예전에는 삭제/
   잠금해제 버튼(.admin-user-actions)도 이 grid 안의 "5번째 아이템"으로
   함께 들어 있었습니다. 4개 열만 정의된 grid에 자식이 5개면 5번째는
   자동으로 다음 줄 1번째 칸(그룹웨어 칸 바로 아래)으로 밀려나, 삭제
   버튼이 그룹웨어 체크박스 아래에 겹쳐 보이는 원인이었습니다.
   .admin-user-actions는 이제 js/modules/admin.js에서 이 grid 밖으로
   옮겨져 .admin-user-row의 3번째(max-content) 칸을 채우는 독립된
   형제 요소이므로 더는 이 문제가 없습니다.

   그룹 체크박스 배치 자체도 grid(고정 열 개수 전제) 대신 flex로
   바꿨습니다 - 그룹 이름이 길어지거나 그룹 개수가 늘어나도(코드 수정
   없이) 각 항목(.admin-group-option)이 자기 내용(white-space:nowrap)만큼만
   너비를 차지합니다. 다음 줄로 넘어가는 대신 한 줄을 유지하고(아래
   .admin-user-permissions의 nowrap 참고) 다 못 들어가면 가로 스크롤로
   닿게 했습니다 - grid처럼 정해둔 열 개수를 넘는 순간 엉뚱한 칸으로
   끼어드는 일 자체가 구조적으로 생기지 않습니다. */
.admin-user-permissions {
  display: flex;
  /* [사용자 요청] 권한 모듈이 10개로 늘면서 기존 flex-wrap이 두 줄로
     넘어가던 것을, 우선은 한 줄로 표기해도 괜찮다는 요청에 따라
     nowrap으로 바꿨습니다. 520px 열 폭에 10개 항목이 다 안 들어갈 수
     있어 overflow-x:auto로 가로 스크롤을 허용하고, justify-content도
     flex-end 대신 flex-start로 바꿔 넘치는 항목이 왼쪽(스크롤 시작
     지점) 밖으로 가려지지 않고 항상 스크롤로 닿을 수 있게 했습니다. */
  flex-wrap: nowrap;
  overflow-x: auto;
  align-items: center;
  justify-content: flex-start;
  column-gap: 14px;
  min-width: 0;
}
.admin-group-option {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: none;
  white-space: nowrap;
  font-size: var(--fs-caption);
  cursor: pointer;
}
.admin-group-option input {
  margin: 0;
  width: 15px;
  height: 15px;
}
.admin-user-actions {
  display: flex;
  gap: 4px;
  justify-content: flex-end;
  flex-wrap: wrap;
}
.admin-role-btn {
  font: inherit;
}
@media (max-width: 900px) {
  .admin-user-row {
    grid-template-columns: 1fr;
  }
  /* .admin-user-row가 한 칸으로 접히면 .admin-user-permissions/
     .admin-user-actions가 정보 아래에 혼자 있는 줄이 되므로, 오른쪽
     정렬 대신 다른 내용과 같은 왼쪽 기준선에 맞춥니다. */
  .admin-user-permissions {
    justify-content: flex-start;
  }
  .admin-user-actions {
    justify-content: flex-start;
  }
}
@media (max-width: 560px) {
  .admin-user-permissions {
    column-gap: 10px;
  }
}

/* ---------- 조직도 조회 화면 (js/modules/orgChart.js) ----------
   ⭐ 능동형 · 적응형 조직도 ⭐
   ul/li로 만든 트리 하나로 두 가지 모드를 모두 지원합니다.
   - 데스크톱(기본): 위→아래로 뻗는 가로형 트리. 세대(depth)마다
     가로로 나열되고, li::before/li::after로 그린 접속선이 부모-자식을
     이어줍니다(전통적인 조직도 느낌).
   - 900px 이하(모바일 등): 같은 마크업을 세로 들여쓰기 목록으로
     전환합니다(가로 스크롤 없이 위→아래로만 읽으면 되도록). */
/* [가시성] 툴바 아래 요약 줄 — "N개 부서 · M명"(+검색 중이면 결과 건수). */
.orgchart-summary {
  margin-top: 8px;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
}
/* [사용자 요청 - 전체 펼치기/접기 배지] 예전엔 "모두 펼치기"/"모두 접기"
   텍스트 버튼 2개가 검색창 옆 툴바 줄에 나란히 있었지만, +/- 배지로 바꿔
   조직도 카드(.orgchart-toolbar-card) 우측상단 모서리에 올립니다.
   카드 자체를 position:relative로 만들고 배지 묶음을 absolute로 그 안
   우측상단에 배치합니다. */
.orgchart-toolbar-card {
  position: relative;
}
/* [화면 최대 활용 - 사용자 요청] 조직도 카드(트리가 그려지는 카드)의
   세로 크기를 "화면상 보이는 최대 높이"까지 키웁니다(16:9 FHD 데스크톱
   기준으로 확인). .orgchart-page는 orgChart.js가 렌더 시 툴바 카드 +
   트리 카드를 감싸는 세로 flex 래퍼로, .content(#main-content)가 이미
   뷰포트 높이만큼(상단바 68px 제외) 확보하고 있는 세로 공간을 그대로
   물려받습니다(height:100%). 툴바 카드는 내용만큼만 차지하고
   (flex-shrink:0), 남는 세로 공간은 전부 트리 카드(.orgchart-card-wrapper)
   몫입니다(flex:1) — 화면이 커질수록 트리 카드도 함께 커집니다.
   min-height:0은 flex 자식이 내용 높이 때문에 줄어들지 못하는 기본
   동작을 풀어줘, 트리 카드가 지정된 높이로 실제로 줄어들 수 있게 합니다. */
.orgchart-page {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  /* [사용자 요청 - 확대/축소 컨트롤] 우측하단 확대율 배지(.orgchart-zoom-controls)의
     기준 컨테이너. 스크롤되는 .orgchart-card-wrapper가 아니라 스크롤되지 않는
     이 래퍼를 기준으로 앵커해야, 조직도를 이동/스크롤해도 배지가 항상 같은
     화면 위치(카드 우측하단)에 떠 있습니다. */
  position: relative;
}
/* [사용자 요청 - 배지 위치 재조정] 카드 모서리에 겹쳐 보이던 배치(top/right
   음수 오프셋)에서, 카드 우측 "바깥쪽"으로 완전히 벗어나(left:100%) 카드의
   위쪽 라인(top:0)을 기준으로 +/- 배지가 세로로 위아래 쌓이도록 바꿨습니다.
   left:100% + margin-left를 쓰는 이유는 배지 폭이 얼마든(아이콘/호버 상태
   무관) 항상 카드 우측 테두리 바로 바깥에서 시작하게 하기 위함입니다
   (right:-Npx 방식은 배지 자체 폭만큼을 미리 알아야 정확히 맞습니다). */
.orgchart-corner-actions {
  position: absolute;
  top: 0;
  left: 100%;
  margin-left: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  z-index: 2;
}
/* 배지를 눌러 전체를 펼치고/접을 수 있게 하되, 겉모습은 다른 조직도 배지들과
   같은 톤(.badge-primary)을 유지해 "N명"/"조직장" 배지들과 자연스럽게
   어울리도록 합니다. 뜻은 아이콘만으론 바로 안 보이므로 title(호버 메모)로
   "모두 펼치기"/"모두 접기"를 설명합니다. */
.orgchart-corner-badge {
  cursor: pointer;
  user-select: none;
  min-width: 20px;
  text-align: center;
  line-height: 1.3;
}
.orgchart-corner-badge:hover,
.orgchart-corner-badge:focus-visible {
  background: var(--color-primary);
  color: var(--color-surface);
  outline: none;
}
.orgchart, .orgchart ul {
  display: flex;
  padding-top: 20px;
  position: relative;
  list-style: none;
}
.orgchart {
  padding: 20px 0 0 0;
  /* [사용자 요청 - 가로 중앙정렬, 버그 수정] 최상위 목록(.orgchart)은 다른
     block 요소와 마찬가지로 부모(#orgchart-tree) 폭을 그대로 채우는
     flex 컨테이너라, 실제 카드 내용이 그보다 좁아도 박스 자체는 항상
     꽉 찹니다. 그 결과 JS(fitToView)가 재는 scrollWidth가 실제 콘텐츠
     폭이 아니라 이 박스의 꽉 찬 폭과 같아져 panX 중앙정렬 계산이
     사실상 0으로만 나와 왼쪽 정렬처럼 보였습니다. 여기 justify-content:
     center를 직접 주면 박스 폭과 무관하게 그 안의 최상위 조직(들)이
     항상 가운데 오므로, JS 계산값과 상관없이 확실히 중앙정렬됩니다.
     중첩된 하위 목록(.orgchart ul)에는 이 규칙을 주지 않습니다 — 하위
     목록은 부모 카드 밑에서 연결선(::before) 기준으로 이미 정렬되고
     있어, 별도로 가운데 정렬할 대상이 아닙니다. 좁은 화면(세로 목록
     모드, @container orgchart)에서는 .orgchart가 display:block으로
     바뀌므로 justify-content는 그때 자동으로 무시됩니다. */
  justify-content: center;
}
.orgchart ul {
  padding-left: 0;
}
.orgchart-li {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 10px 0 10px;
  position: relative;
}
/* 부모→자식 접속선 (가로형 모드 전용) */
.orgchart-li::before,
.orgchart-li::after {
  content: "";
  position: absolute;
  top: 0;
  right: 50%;
  border-top: 1px solid var(--color-border);
  width: 50%;
  height: 20px;
}
.orgchart-li::after {
  right: auto;
  left: 50%;
  border-left: 1px solid var(--color-border);
}
.orgchart-li:only-child::after,
.orgchart-li:only-child::before {
  display: none;
}
/* [버그 수정 - 외동 자식 연결선 끊김] 위 규칙이 가로 버스 라인(::before/
   ::after 전체)을 지워, 외동 자식일 때는 가로선이 필요 없다는 의도까지는
   맞았습니다. 하지만 부모→자식 세로 연결선의 "카드 바로 위 구간"도 이
   li 자신의 ::after(border-left)가 그리고 있어서(마지막 자식은 ::before의
   border-right가 대신 그림 - 둘 다 바로 아래 :last-child::before 규칙 참고),
   그 구간까지 함께 지워지며 부모 카드 바로 아래 세로선(.orgchart-li > ul::before,
   부모 쪽 padding-top 20px 구간)과 이 카드 사이에 20px짜리 빈 틈이
   생겼습니다 - 형제가 있을 때는 안 보이던 문제로, 외동 자식일 때만
   나타납니다. 이 li 자신의 padding-top을 0으로 없애 애초에 "카드 위
   구간" 자체를 없애면, 부모 쪽 세로선이 끝나는 지점에 카드가 곧바로
   맞닿아 틈 없이 이어집니다(가로 버스가 필요 없는 것과 같은 이유로,
   세로선도 부모 쪽 한 구간만 있으면 충분합니다). */
.orgchart-li:only-child {
  padding-top: 0;
}
.orgchart-li:first-child::before,
.orgchart-li:last-child::after {
  border: 0 none;
}
.orgchart-li:last-child::before {
  border-right: 1px solid var(--color-border);
  border-radius: 0 6px 0 0;
}
.orgchart-li:first-child::after {
  border-radius: 6px 0 0 0;
}
/* 부모 박스에서 아래로 내려오는 세로선 */
.orgchart-li > ul::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  border-left: 1px solid var(--color-border);
  width: 0;
  height: 20px;
}
.orgchart-node {
  display: inline-flex;
  flex-direction: column;
  gap: 6px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 10px 14px;
  min-width: 200px;
  box-sizing: border-box;
  /* [전체 펼치기/접기] 최상위 카드에만 붙는 .orgchart-corner-actions(+/- 배지)를
     이 카드 기준 절대위치로 앵커하기 위한 기준 컨테이너. */
  position: relative;
  /* [카드 폭 통일] zoomPan.js의 equalizeNodeWidths()가 렌더 시마다 가로형
     (데스크톱) 모드에 보이는 조직도 전체 카드 중 가장 넓은 카드 하나를
     기준으로 모든 카드의 width를 인라인으로 맞춰줍니다(형제 그룹 폭
     통일의 부수 효과로 부모→자식 세로 연결선도 자동으로 카드 중앙과
     일치). 이 규칙(min-width)은 그 인라인 값이 없을 때(=JS 실행 전 순간,
     또는 JS가 실패했을 때)의 기본값 역할만 합니다.
     세로 목록 모드(@container 좁음)에서는 아래에서 width:100%로 덮어씁니다. */
  transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
/* [심미성] 카드가 살짝 떠오르는 느낌으로 조직 단위 경계를 더 또렷하게 합니다. */
.orgchart-node:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.orgchart-node-match {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary-soft);
}
.orgchart-node-head {
  display: flex;
  align-items: center;
  gap: 4px;
}
.orgchart-node-title {
  font-weight: 600;
  font-size: var(--fs-desc-sm);
}
.orgchart-toggle {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  padding: 0 2px;
  line-height: 1;
}
.orgchart-toggle:hover {
  color: var(--color-primary);
}
.orgchart-head-empty {
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
}
/* [사용자 요청 - 배지 줄바꿈 방지] "조직장"/"겸"/"나"/"N명" 배지는
   .orgchart-emp / .orgchart-node-head가 flex 아이템이라, 카드 폭이
   좁아지면 flex 기본 동작(줄어들기)으로 배지 텍스트 자체가 두 줄로
   꺾여 보일 수 있었습니다. 배지는 항상 한 줄로 유지하고(white-space),
   다른 flex 아이템에 밀려 찌그러지지 않도록(flex-shrink) 고정합니다. */
.orgchart-node-head .badge,
.orgchart-emp .badge {
  white-space: nowrap;
  flex-shrink: 0;
}
.orgchart-emp {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  border-radius: var(--radius-sm);
  padding: 4px 6px;
  font-size: var(--fs-caption);
}
.orgchart-emp:hover,
.orgchart-emp:focus-visible {
  background: var(--color-hover);
  outline: none;
}
.orgchart-emp-match {
  background: var(--color-warning-soft);
}
/* [표시 범위] 조직도 카드/구성원 목록의 이름 옆 "직급" 텍스트.
   .mention-dropdown-position과 같은 톤(중립 보조색)을 쓰되, 조직도 전용
   클래스로 분리해 검색 드롭다운(직급·직책·부서 모두 표시)과는 독립적으로
   유지합니다 — 조직도는 직급만 보여주면 충분합니다. */
.orgchart-emp-position {
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  white-space: nowrap;
}
/* [사용자 요청 - 겸/조직장 배지 우측정렬, 근본 수정] 조직장 카드 행
   (.orgchart-head-emp, flex row)에서 아바타+이름은 좌측에 그대로 두고,
   "겸"/"조직장" 배지만 행의 우측 끝으로 붙입니다.
   ⚠️ 예전에는 .orgchart-head-emp 바로 아래 첫 번째 badge를 :first-of-type으로
   골라 margin-left:auto를 주려 했지만 전혀 매치되지 않았습니다 —
   :first-of-type은 "badge 클래스를 가진 것 중 첫 번째"가 아니라 "같은
   태그(span) 형제 중 몇 번째인가"만 보는데,
   이 행의 실제 첫 span은 badge가 아닌 아바타(.orgchart-emp-avatar)라서
   badge에는 이 조건이 한 번도 성립하지 않았습니다(우측정렬 미적용
   상태로 계속 남아있던 원인). "겸"/"조직장" 배지를 js/modules/orgChart/
   treeRenderer.js에서 별도 wrapper(.orgchart-head-badges)로 감싸고,
   그 wrapper 자체에 margin-left:auto를 줘서 flex의 남는 공간을 전부
   배지 앞으로 밀어내는 방식으로 바꿨습니다 — 클래스가 아니라 실제
   DOM 구조(wrapper 하나)로 우측정렬 대상을 지정하므로 겸직 여부와
   무관하게 항상 정확히 매치됩니다. "겸"-"조직장" 두 배지 사이
   간격(ml-4/ml-8)은 wrapper 안에서 그대로 유지됩니다. */
.orgchart-head-badges {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
}
.orgchart-emp-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font-size: var(--fs-micro);
  font-weight: 600;
  flex-shrink: 0;
}
.orgchart-emp-avatar-lg {
  width: 40px;
  height: 40px;
  font-size: var(--fs-h3);
}
/* [심미성] 조직장 아바타에 얇은 링을 둘러 일반 구성원과 한눈에 구분되게 합니다. */
.orgchart-emp-avatar-head {
  box-shadow: 0 0 0 2px var(--color-primary);
}
/* [내 위치 찾기] 내 카드에는 항상 은은한 좌측 악센트를 둡니다("나" 배지와 함께
   이중으로 표시 — 트리가 길어져 배지를 놓쳐도 라인만으로 알아볼 수 있도록). */
.orgchart-emp-me {
  box-shadow: inset 3px 0 0 var(--color-success);
  border-radius: var(--radius-sm);
}
/* [내 위치 찾기] "내 위치 찾기" 클릭 시 도착한 카드를 잠깐 두드러지게 합니다. */
.orgchart-emp-flash {
  animation: orgchart-flash 1.2s ease-out;
}
@keyframes orgchart-flash {
  0% { box-shadow: 0 0 0 4px var(--color-primary-soft); }
  100% { box-shadow: inset 3px 0 0 var(--color-success); }
}
.orgchart-members summary {
  cursor: pointer;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
}
.orgchart-emp-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 4px;
}
.orgchart-hidden {
  display: none !important;
}
.orgchart-unassigned {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px dashed var(--color-border);
}
.orgchart-unassigned-title {
  font-weight: 600;
  color: var(--color-text-secondary);
  margin-bottom: 8px;
  font-size: var(--fs-desc-sm);
}

/* 직원 정보 모달 안 레이아웃 */
.orgchart-modal-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.orgchart-modal-section {
  border-top: 1px solid var(--color-border);
  padding-top: 12px;
  margin-top: 12px;
}
.orgchart-modal-section-title {
  font-weight: 600;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  margin-bottom: 6px;
}
.orgchart-modal-row {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  font-size: var(--fs-desc-sm);
}
.orgchart-modal-row > span:first-child {
  color: var(--color-text-secondary);
}

/* [적응형] 조직도가 실제로 그려지는 영역(카드) 폭이 좁아지면, 가로형
   트리(접속선 포함) 대신 같은 ul/li 마크업을 세로 들여쓰기 목록으로
   바꿉니다 — 좁은 폭에서 가로형 트리는 옆으로 계속 넓어져 카드 배경
   밖으로 넘치기 때문입니다.
   ⚠️ 예전에는 `@media (max-width: 900px)`(브라우저 창 전체 폭 기준)를
   썼습니다. 그런데 조직도가 그려지는 실제 영역은 사이드바 등을 빼면
   창 폭보다 훨씬 좁을 수 있어서, 카드 밖으로 넘쳐 보여도 "창 폭"은
   아직 900px 밑으로 안 내려가 전환이 안 되는 문제가 있었습니다. 그래서
   창 폭이 아니라 조직도 카드 자신의 폭을 재는 컨테이너 쿼리
   (@container)로 바꿨습니다. 기준 컨테이너는 .orgchart-card-wrapper
   (orgChart.js가 렌더 시 조직도 카드에 붙이는 클래스)이고, 700px는
   형제 카드 3개(각 min-width 200px + 좌우 여백)가 최소한으로 나란히
   들어갈 수 있는 폭을 감안한 값입니다 — 필요하면 조정 가능합니다. */
/* [사용자 요청 - 근본적 레이아웃 수정] v1.4.171에서 700px 기준(@container)을
   들여왔지만, 그 값은 어디까지나 "형제 카드 3개가 최소한으로 들어가는 폭"을
   가정한 값이라 형제가 4개 이상이거나, 하위 트리가 깊어 폭이 누적되면
   (한 조직의 렌더 폭은 결국 그 하위 트리 전체 폭의 합으로 결정됨) 카드가
   700px보다 넓은 채로 유지되고 있어도 실제 트리 내용은 그보다 훨씬
   넓어질 수 있습니다. `container-type: inline-size`는 이 요소의 인라인
   크기를 내용과 무관하게 고정시키므로(컨테이너 쿼리가 순환 없이 동작하기
   위한 필수 조건), 내용이 더 넓어지면 자동으로 줄어들지 않고 카드 밖으로
   그대로 넘쳐(overflow) 화면이 깨져 보였습니다.
   특정 형제 수 기준의 매직 넘버에 계속 기대는 대신, 트리가 자기 폭을
   넘어서면 항상 카드 안에서 가로 스크롤로 담기도록 만들어 형제가 몇
   개든(4개든 10개든) 다시는 카드 밖으로 깨져 보이지 않게 합니다. 세로
   목록 모드(@container 좁음)에서는 폭이 100%로 고정되므로 이 규칙은
   영향이 없습니다. */
.orgchart-card-wrapper {
  container-type: inline-size;
  container-name: orgchart;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* [화면 최대 활용] .orgchart-page 안에서 남는 세로 공간을 전부 차지하고
     (flex:1 + min-height:0), 트리가 그 높이보다 커지면 카드 자신이
     세로 스크롤을 담당합니다 — 전체 페이지가 아니라 이 카드만 스크롤되어
     툴바(검색창 등)는 항상 화면에 고정되어 보입니다. */
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* 기본 커서는 손모양 없이 일반 화살표. 마우스 우측버튼을 누르고 있는
     동안에만(.orgchart-panning) 잡은 손 모양으로 바뀝니다(아래 참고). */
  cursor: default;
  /* ⚠️ [사용자 리포트 - 우클릭 드래그(캔버스 팬) 중 스크롤바가 나타나 어색함]
     이 카드는 (a) 위 overflow-x/y: auto가 원래 담당하던 "형제가 많아 트리가
     카드보다 넓어질 때 잘리지 않고 스크롤로 담기게" 하는 레이아웃 안전망과,
     (b) #orgchart-tree에 얹는 확대/축소·우클릭 드래그 이동(translate+scale
     transform, zoomPan.js) 두 가지를 동시에 담당합니다. 브라우저는 transform
     으로 밀려난(팬 이동된) 콘텐츠의 화면상 시각적 범위도 "스크롤 가능
     영역"에 포함시키므로, 우클릭 드래그로 캔버스를 이리저리 옮기다 보면
     실제로는 아무 문제 없이 잘 따라오고 있는데도 카드 스크롤바가 함께
     나타났다 사라졌다 합니다. 기능상 문제는 없지만(오히려 그대로 두면
     scrollIntoView 기반 "내 위치 찾기"(orgChartCore.js findMe)가 계속
     정상 동작해야 하므로 overflow: auto 자체는 유지합니다), 스크롤은
     오직 우클릭 드래그·shift+휠·버튼(zoomPan.js)으로만 이루어져야 하는
     "일러스트레이터 캔버스" 느낌의 UI라 네이티브 스크롤바가 화면에 같이
     보이는 건 시각적으로 어색합니다. 그래서 스크롤 동작(overflow: auto,
     scrollIntoView 포함)은 그대로 남기고 스크롤바 "표시"만 숨깁니다 —
     스크롤 자체를 막는 overflow: hidden으로 바꾸면 findMe의
     scrollIntoView가 못 쓰게 될 수 있어(카드 자체가 접근성 트리에서
     스크롤 불가능한 것으로 취급될 위험) 쓰지 않았습니다. */
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* 구버전 Edge/IE */
}
.orgchart-card-wrapper::-webkit-scrollbar {
  /* Chrome/Safari/신버전 Edge: 스크롤 "동작"은 그대로 두고 스크롤바
     "그리기"만 없앱니다(display:none이 아니라 width/height:0 — 일부
     WebKit 엔진에서 display:none은 스크롤 자체를 막아버릴 수 있어
     더 안전한 방식을 씁니다). */
  width: 0;
  height: 0;
  background: transparent;
}
/* [사용자 요청 - 어도비 일러스트 캔버스 느낌] 마우스 우측버튼을 누른 채
   이동하는 동안 커서를 "잡고 끄는 손" 모양으로 바꿔, 지금 화면을
   드래그로 이동시키고 있다는 걸 시각적으로 알려줍니다. */
.orgchart-card-wrapper.orgchart-panning {
  cursor: grabbing;
}
/* [사용자 요청 - 확대/축소·드래그 이동] #orgchart-tree 자체에 JS가
   transform(translate + scale)을 직접 얹어 확대/축소·이동을 구현합니다.
   transform-origin을 좌상단(0 0)으로 고정해야 scale 계산이 항상 같은
   기준점을 쓰므로(그렇지 않으면 브라우저 기본값인 중앙 기준으로 계산이
   꼬입니다) panX/panY 계산식과 어긋나지 않습니다. */
#orgchart-tree {
  transform-origin: 0 0;
  will-change: transform;
}
/* [사용자 요청 - 확대/축소 컨트롤] shift+휠·우클릭 드래그를 모르는 사용자를
   위한 보조 버튼(확대/축소/원래크기)과, 지금 확대 비율을 알 수 있는
   표시입니다. 조직도 카드 우측하단에 떠 있는 작은 캡슐 형태로 배치해
   기존 우측상단의 모두펼치기/접기 배지(.orgchart-corner-actions, 그건
   툴바 카드 소속)와 겹치지 않습니다. .orgchart-card-wrapper(스크롤 컨테이너)가
   아니라 .orgchart-page 기준으로 절대위치를 잡아, 조직도를 드래그로
   이동시켜도 이 배지는 화면의 같은 자리에 계속 떠 있습니다. */
.orgchart-zoom-controls {
  position: absolute;
  right: 14px;
  bottom: 14px;
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 4px 6px;
  font-size: var(--fs-caption);
  color: var(--color-text-secondary);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  z-index: 5;
  user-select: none;
}
.orgchart-zoom-btn {
  cursor: pointer;
  width: 22px;
  height: 22px;
  line-height: 22px;
  text-align: center;
  border-radius: 50%;
  font-weight: 600;
}
.orgchart-zoom-btn:hover,
.orgchart-zoom-btn:focus-visible {
  background: var(--color-primary);
  color: var(--color-surface);
  outline: none;
}
.orgchart-zoom-level {
  min-width: 40px;
  text-align: center;
}
.orgchart-zoom-reset {
  cursor: pointer;
  padding: 2px 8px;
  border-radius: 999px;
  border-left: 1px solid var(--color-border);
  margin-left: 2px;
  white-space: nowrap;
}
.orgchart-zoom-reset:hover,
.orgchart-zoom-reset:focus-visible {
  background: var(--color-primary);
  color: var(--color-surface);
  outline: none;
}
@container orgchart (max-width: 700px) {
  .orgchart, .orgchart ul {
    display: block;
    padding-top: 0;
  }
  .orgchart-li {
    align-items: stretch;
    padding: 8px 0 0 18px;
    border-left: 1px solid var(--color-border);
  }
  .orgchart-li:last-child {
    padding-bottom: 4px;
  }
  .orgchart-li::before,
  .orgchart-li::after,
  .orgchart-li > ul::before {
    display: none;
  }
  .orgchart-node {
    min-width: 0;
    width: 100% !important;
  }
}
/* [구형 브라우저 대비] 컨테이너 쿼리(@container)를 지원하지 않는
   브라우저에서는 예전 방식(창 전체 폭 기준)으로라도 세로 목록으로
   전환되도록 폴백을 남겨둡니다. */
@supports not (container-type: inline-size) {
  @media (max-width: 900px) {
    .orgchart, .orgchart ul {
      display: block;
      padding-top: 0;
    }
    .orgchart-li {
      align-items: stretch;
      padding: 8px 0 0 18px;
      border-left: 1px solid var(--color-border);
    }
    .orgchart-li:last-child {
      padding-bottom: 4px;
    }
    .orgchart-li::before,
    .orgchart-li::after,
    .orgchart-li > ul::before {
      display: none;
    }
    .orgchart-node {
      min-width: 0;
      width: 100%;
    }
  }
}

/* ---------- 사건관리 --------------------------------------------------
   기존 그룹웨어의 card / admin-toolbar / login-input / data-table / modal-box
   컴포넌트를 그대로 사용합니다. 사건 전용 스타일은 간격·정렬처럼
   도메인 화면에 필요한 최소한의 보정만 담당합니다. */
.case-toolbar {
  margin-bottom: 12px;
}

.case-filter-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  min-width: 0;
}

.case-filter-bar > .login-input {
  flex: 0 0 auto;
}

.case-filter-bar > select.login-input {
  width: auto;
}

.case-filter-bar .case-quick {
  flex: 0 0 auto;
}

.case-filter-bar .case-date-range {
  flex: 0 0 auto;
}

.case-date-range {
  display: grid;
  grid-template-columns: 140px 14px 140px;
  gap: 6px;
  align-items: center;
}

.case-date-input {
  width: 140px;
  min-width: 140px;
}

.case-date-range > span {
  color: var(--color-text-secondary);
  text-align: center;
}

.case-filter-search-input {
  flex: 1 1 220px !important;
  min-width: 180px;
}

.case-quick {
  min-width: 64px;
  /* [디자인 통일] 배경/테두리는 공용 .btn 기준(css/styles.css의 .btn 규칙)을
     그대로 물려받습니다 — 카드 밖에서도 항상 보이도록 특정 모듈에
     하드코딩하지 않습니다. 선택된 버튼은 전자결재 "내 상신함/내 결재함"
     탭과 동일하게 U.tabButtonHTML의 인라인 style이 남색 채움을 적용합니다
     (src/features/case/caseCore.js 참고). */
}


.case-summary-grid {
  margin-bottom: 16px;
}

.case-stat-card {
  padding: 16px 18px;
}

.case-stat-value {
  margin-top: 4px;
  font-size: 20px;
  font-weight: var(--fw-bold);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}

.case-list-card {
  min-width: 0;
}

.case-list-title {
  margin-bottom: 12px;
}

.case-count {
  margin-left: 6px;
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  font-weight: var(--fw-regular);
}

.case-report-actions {
  display: inline-flex;
  gap: 6px;
  margin-left: auto;
}

/* 목록은 사건관리 전용 표를 새로 그리지 않고 그룹웨어 공용 .data-table의
   폭·패딩·선·hover 규칙을 그대로 사용합니다. 수평 스크롤만 보조합니다. */
.case-table-scroll {
  overflow-x: auto;
}

.case-table {
  min-width: 1445px;
  table-layout: fixed;
}

.case-table th,
.case-table td {
  white-space: nowrap;
}

/* 사건 목록의 열 폭은 내용 역할에 맞춰 고정합니다. 금액은 100억 단위까지
   표시해도 충분한 폭만 확보하고, 사건명은 한글 기준 최소 30자 수준을
   수용할 수 있도록 넉넉하게 잡아 불필요한 수평 압축을 막습니다. */
.case-col-number { width: 140px; }
.case-col-title { width: 380px; }
.case-col-client { width: 120px; }
.case-col-date { width: 110px; }
.case-col-money { width: 115px; }
.case-col-status { width: 90px; }
.case-col-author { width: 100px; }
.case-col-note { width: 160px; }

.case-table th {
  text-align: center;
}


.case-sort {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 100%;
  min-height: 20px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: inherit;
  text-align: inherit;
  cursor: pointer;
}

.case-sort:hover {
  color: var(--color-primary);
}

.case-title-cell {
  display: inline-block;
  width: 100%;
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}

.case-note-cell {
  overflow: hidden;
  text-overflow: ellipsis;
}

.case-note-cell span {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}

.case-number-cell {
  font-variant-numeric: tabular-nums;
}

.case-money-cell {
  text-align: right;
}


/* 본문 정렬은 컬럼 역할별로 한 번만 정의합니다. */
.case-table td.case-client-cell,
.case-table td.case-date-cell,
.case-table td.case-status-cell,
.case-table td.case-author-cell {
  text-align: center;
  vertical-align: middle;
}

.case-row {
  cursor: pointer;
}

.case-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 16px;
}

.case-pagination span {
  min-width: 70px;
  text-align: center;
}

.case-modal {
  max-width: 700px;
}

.modal-subtitle {
  margin-top: 2px;
  font-size: var(--fs-caption);
  color: var(--color-text-secondary);
}

.case-form {
  display: grid;
  gap: 16px;
}

.case-form-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 14px 16px;
}

.case-form-field {
  min-width: 0;
}

.case-field-half {
  grid-column: span 3;
}

.case-field-third {
  grid-column: span 2;
}

.case-field-full {
  grid-column: 1 / -1;
}

.case-form-field .login-label {
  display: block;
  margin-bottom: 6px;
}

.case-form-field .login-input {
  width: 100%;
  min-width: 0;
}

.case-form-field input[inputmode="numeric"] {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.case-form-field input:disabled,
.case-form-field select:disabled {
  opacity: 1;
  color: var(--color-text);
  background: var(--color-surface);
  cursor: default;
}

.case-modal-audit {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 0 0;
  border-top: 1px solid var(--color-border);
  color: var(--color-text-secondary);
  font-size: var(--fs-caption);
  white-space: nowrap;
}

.case-modal-audit strong {
  color: var(--color-text);
  font-weight: var(--fw-semibold);
}

.case-form-actions {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

@media (max-width: 640px) {
  .case-modal {
    max-width: calc(100vw - 24px);
  }

  .case-form-grid {
    grid-template-columns: 1fr;
  }

  .case-form-field {
    grid-column: 1 / -1;
  }

  .case-modal-audit {
    align-items: flex-start;
    flex-direction: column;
    gap: 4px;
  }
}

@media (max-width: 900px) {
  .case-filter-bar {
    flex-wrap: wrap;
  }

  .case-filter-search-input {
    flex-basis: 220px !important;
  }

  .case-summary-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 600px) {
  .case-filter-bar {
    align-items: stretch;
  }

  .case-filter-bar > .login-input,
  .case-filter-search-input {
    width: 100%;
    flex-basis: 100% !important;
  }

  .case-date-range {
    width: 100%;
    grid-template-columns: minmax(0, 1fr) 14px minmax(0, 1fr);
  }

  .case-date-input {
    width: 100%;
    min-width: 0;
  }

  .case-summary-grid {
    grid-template-columns: 1fr;
  }
}


@media (max-width: 720px) {
  .admin-backup-auto-header { align-items:flex-start; }
  .admin-auto-backup-files { grid-template-columns:1fr; }
}
