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

/* 네이티브 hidden 속성이 항상 이기도록 강제 — .btn 등 display를 직접 지정하는 클래스와
   같은 요소에 함께 붙으면 동일 특이도(단일 attribute/class 선택자)라서 소스 순서에 따라
   hidden이 질 수 있다(.approver-list label[hidden]/.profile-image-actions [hidden]가
   이미 이 문제를 개별 override로 우회해온 이유). 전역으로 한 번 잡아 앞으로는
   el.style.display 대신 el.hidden만으로 안전하게 토글할 수 있게 한다. */
[hidden] {
    display: none !important;
}

:root {
    --color-bg: #f4f5f7;
    --color-surface: #ffffff;
    --color-border: #d9dce1;
    --color-text: #1f2328;
    --color-text-muted: #5c6472;
    --color-primary: #2f5fb3;
    --color-primary-rgb: 47, 95, 179;
    --color-primary-hover: #274d92;
    --color-error-bg: #fdeceb;
    --color-error-text: #9c2b1f;
    --color-info-bg: #eaf2ff;
    --color-info-text: #1f4d8f;
    --color-success-bg: #e8f6ed;
    --color-success-text: #1e7a3e;
    --color-warning-bg: #fff6e0;
    --color-warning-text: #8a6300;
    /* 계약 주행거리 4단계(정상/주의/위험/초과, ADR-0068)에서 "위험"만 기존 성공/경고/실패
       3색으로 표현할 수 없어 추가한 중간 등급 색(주황) — 새 컴포넌트가 아니라 badge/meter가
       이미 쓰는 색 토큰 체계에 한 쌍만 더한다. */
    --color-critical-bg: #ffe9d6;
    --color-critical-text: #9a4a0a;

    /* 팀 연차 캘린더 유형별 chip 전용 팔레트 — 전자결재 상태 의미(정보/성공/실패/대기)로
       이미 쓰이는 위 4색과 분리해 둔다(Phase 76이 재사용을 명시적으로 피한 이유). */
    --color-calendar-annual-bg: #e6f0ff;
    --color-calendar-annual-text: #1d4ed8;
    --color-calendar-half-bg: #e0f7f5;
    --color-calendar-half-text: #0f766e;
    --color-calendar-sick-bg: #fde8ec;
    --color-calendar-sick-text: #b91c4a;
    --color-calendar-family-bg: #f3e8ff;
    --color-calendar-family-text: #6d28d9;
    --color-calendar-other-bg: #f1f2f4;
    --color-calendar-other-text: #4b5563;
    --color-calendar-compensatory-bg: #fff1e0;
    --color-calendar-compensatory-text: #b45309;
    --color-calendar-working-bg: #e3f7ea;
    --color-calendar-working-text: #15803d;
    --color-calendar-leave-status-bg: #fef3c7;
    --color-calendar-leave-status-text: #92400e;
    --color-calendar-business-trip-bg: #dbeafe;
    --color-calendar-business-trip-text: #1e40af;
    --color-calendar-local-bg: #e0e7ff;
    --color-calendar-local-text: #4338ca;
    --color-calendar-holiday-bg: #fee2e2;
    --color-calendar-holiday-text: #b91c1c;
    --header-height: 56px;
    --sidebar-width: 220px;
    --transition-fast: 120ms ease;

    /* ---- Spacing scale(Phase 129) — 새 값을 발명하지 않고, 기존 app.css 전체에서 실제
       쓰이던 padding/margin/gap 값을 그대로 이름만 붙였다(시각적 변화 없음). 값 = 토큰
       이름의 숫자(px)라서 어떤 값이 어느 토큰인지 외울 필요가 없다. */
    --space-1: 1px;
    --space-2: 2px;
    --space-3: 3px;
    --space-4: 4px;
    --space-5: 5px;
    --space-6: 6px;
    --space-8: 8px;
    --space-10: 10px;
    --space-12: 12px;
    --space-14: 14px;
    --space-16: 16px;
    --space-18: 18px;
    --space-20: 20px;
    --space-22: 22px;
    --space-24: 24px;
    --space-32: 32px;
    --space-40: 40px;

    /* ---- Radius scale(Phase 129) — 기존 실측값(4/6/8/10/12px) 그대로 토큰화. 원(50%)은
       스케일 값이 아니라 그대로 둔다. */
    --radius-2: 2px;
    --radius-4: 4px;
    --radius-6: 6px;
    --radius-8: 8px;
    --radius-10: 10px;
    --radius-12: 12px;

    /* ---- Type scale(Phase 129) — 기존 실측값(10~26px) 그대로 토큰화. */
    --text-10: 10px;
    --text-11: 11px;
    --text-12: 12px;
    --text-13: 13px;
    --text-14: 14px;
    --text-15: 15px;
    --text-16: 16px;
    --text-18: 18px;
    --text-20: 20px;
    --text-26: 26px;

    /* ---- Shadow scale(Phase 129) — 기존 elevation 그림자 2곳(카드 hover/모달)을 토큰화.
       입력 focus 링(box-shadow 0 0 0 3px rgba(...))은 elevation이 아니라 별개 성격이라
       --shadow-* 스케일에는 포함하지 않는다. 색상 자체는 --color-primary-rgb(Phase
       380-A)를 따라가므로 다크모드에서도 그 시점의 primary와 같은 색조로 보인다. */
    --shadow-sm: 0 2px 8px rgba(31, 35, 40, 0.08);
    --shadow-md: 0 8px 24px rgba(31, 35, 40, 0.2);

    /* ---- Disabled state — "게시판 종류는 작성 후 변경할 수 없습니다" 류 locked-hint
       패턴(5개 화면)이 input.disabled = true만 걸고 커스텀 스타일은 없어 브라우저 기본
       회색에 맡기고 있었다. 비활성 텍스트는 --color-text-muted와 의미가 달라(입력 불가 vs
       부가 정보) 별도 토큰으로 둔다. */
    --color-disabled-bg: #eceef1;
    --color-disabled-text: #9aa1ab;

    /* ---- Z-index scale — 헤더/모달만 하드코딩값(10/100)으로 존재하던 걸 스케일로 정리하고,
       아직 컴포넌트가 없는 dropdown/toast/tooltip 레이어도 미리 자리를 잡아둔다(실제 도입 시
       값만 그대로 쓰면 됨). 값 간격을 넉넉히 둬 사이에 다른 요소가 끼어들 여지를 남긴다. */
    --z-header: 10;
    --z-dropdown: 50;
    --z-modal: 100;
    --z-toast: 150;
    --z-tooltip: 200;

    /* ---- Density tokens(ONGOFLOW UI Preference Phase 1) — 새 spacing 값을 발명하지 않고
       기존 --space-* 스케일에서 골라 이름만 붙였다. 기본값은 지금 이미 쓰이던 실제 값이라
       data-density가 없거나 "default"일 때 시각적 변화가 없다. compact/comfortable
       override는 아래 [data-density] 블록에서 이 토큰들만 재정의한다(색상/radius 등
       테마 토큰과 마찬가지로 컴포넌트 규칙 자체는 건드리지 않는다). */
    --density-control-padding-y: var(--space-8);
    --density-control-padding-x: var(--space-14);
    --density-field-padding-y: var(--space-10);
    --density-field-padding-x: var(--space-12);
    --density-table-cell-padding-y: var(--space-10);
    --density-table-cell-padding-x: var(--space-14);
    --density-card-padding-y: var(--space-20);
    --density-card-padding-x: var(--space-24);
    --density-sidebar-item-padding-y: var(--space-10);
    --density-sidebar-item-padding-x: var(--space-20);

    /* select/date input/스크롤바 같은 브라우저 네이티브 컨트롤도 테마에 맞춰 칠하도록
       지정 — 없으면 OS 설정을 따라가 버려 "선택한 테마와 무관하게" 어긋날 수 있다. */
    color-scheme: light;
}

/* ---- Dark theme ---- 위 :root의 semantic 색상 토큰만 재정의한다(spacing/radius/
   type/z-index 스케일은 테마 무관). html에 data-theme="dark"가 붙으면 적용되며,
   pageHead의 인라인 스크립트가 렌더링 전에 localStorage 값(없으면 시스템 설정)을
   읽어 이 속성을 세팅해 테마 전환 시 깜빡임(FOUC)이 없도록 한다. */
:root[data-theme="dark"] {
    --color-bg: #15171b;
    --color-surface: #1e2126;
    --color-border: #30343c;
    --color-text: #e7e9ec;
    --color-text-muted: #9aa1ac;
    --color-primary: #4c8df0;
    --color-primary-rgb: 76, 141, 240;
    --color-primary-hover: #6fa4f4;
    --color-error-bg: #3a1f1f;
    --color-error-text: #f2726c;
    --color-info-bg: #1c2b40;
    --color-info-text: #7db1f5;
    --color-success-bg: #17301f;
    --color-success-text: #6cc98a;
    --color-warning-bg: #362a10;
    --color-warning-text: #e0b64f;
    --color-critical-bg: #3a2610;
    --color-critical-text: #f0a15c;

    --color-calendar-annual-bg: #1c2b45;
    --color-calendar-annual-text: #7fa8f2;
    --color-calendar-half-bg: #123330;
    --color-calendar-half-text: #4fd9c9;
    --color-calendar-sick-bg: #3a1c26;
    --color-calendar-sick-text: #f0819e;
    --color-calendar-family-bg: #2a1c40;
    --color-calendar-family-text: #c298f5;
    --color-calendar-other-bg: #23262b;
    --color-calendar-other-text: #b3b9c2;
    --color-calendar-compensatory-bg: #3a2712;
    --color-calendar-compensatory-text: #eba75c;
    --color-calendar-working-bg: #16301f;
    --color-calendar-working-text: #6fd18f;
    --color-calendar-leave-status-bg: #3a2f0e;
    --color-calendar-leave-status-text: #e8c257;
    --color-calendar-business-trip-bg: #1a2740;
    --color-calendar-business-trip-text: #8bb0f2;
    --color-calendar-local-bg: #23234a;
    --color-calendar-local-text: #a5b4fc;
    --color-calendar-holiday-bg: #3a1c1c;
    --color-calendar-holiday-text: #f08080;

    --color-disabled-bg: #26292f;
    --color-disabled-text: #6b7280;

    --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.5);
    --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.6);

    color-scheme: dark;
}

/* ---- Density variants(ONGOFLOW UI Preference Phase 1) ---- Theme과 독립적으로 조합 가능
   (예: dark + compact) — 위 --density-* 토큰만 재정의하고 색상/radius는 건드리지 않는다.
   compact/comfortable 값도 새로 발명하지 않고 기존 --space-* 스케일에서 골랐다. */
:root[data-density="compact"] {
    --density-control-padding-y: var(--space-4);
    --density-control-padding-x: var(--space-10);
    --density-field-padding-y: var(--space-6);
    --density-field-padding-x: var(--space-10);
    --density-table-cell-padding-y: var(--space-6);
    --density-table-cell-padding-x: var(--space-10);
    --density-card-padding-y: var(--space-14);
    --density-card-padding-x: var(--space-16);
    --density-sidebar-item-padding-y: var(--space-6);
    --density-sidebar-item-padding-x: var(--space-16);
}

:root[data-density="comfortable"] {
    --density-control-padding-y: var(--space-10);
    --density-control-padding-x: var(--space-18);
    --density-field-padding-y: var(--space-12);
    --density-field-padding-x: var(--space-14);
    --density-table-cell-padding-y: var(--space-12);
    --density-table-cell-padding-x: var(--space-16);
    --density-card-padding-y: var(--space-24);
    --density-card-padding-x: var(--space-32);
    --density-sidebar-item-padding-y: var(--space-12);
    --density-sidebar-item-padding-x: var(--space-22);
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    min-height: 100vh;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: "Segoe UI", "Apple SD Gothic Neo", "Malgun Gothic", -apple-system, sans-serif;
    font-size: var(--text-14);
}

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

a {
    color: var(--color-primary);
    text-decoration: none;
    transition: color var(--transition-fast);
}

a:hover {
    color: var(--color-primary-hover);
    text-decoration: underline;
}

/* ---- App shell ---- */

.app-header {
    height: var(--header-height);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 var(--space-20);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    position: sticky;
    top: 0;
    z-index: var(--z-header);
}

.app-header .brand {
    font-weight: 700;
    font-size: var(--text-16);
    color: var(--color-text);
    text-decoration: none;
}

.app-header .user-area {
    display: flex;
    align-items: center;
    gap: var(--space-12);
    font-size: var(--text-13);
    color: var(--color-text-muted);
}

.app-header .user-area strong {
    color: var(--color-text);
}

.app-header form {
    margin: 0;
}

.app-header .header-link {
    color: var(--color-text-muted);
    font-size: var(--text-13);
}

.app-header .logout-button {
    background: none;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    padding: var(--space-6) var(--space-10);
    font-size: var(--text-13);
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.app-header .logout-button:hover {
    background: var(--color-bg);
}

/* 헤더의 라이트/다크 전환 버튼 — .notification-bell과 동일한 아이콘 버튼 규격(28px,
   radius-6, hover시 --color-bg)을 따른다. 아이콘 두 개를 겹쳐 두고 :root[data-theme]로
   하나만 보이게 전환한다(app.js의 initThemeToggle이 클릭 시 속성만 바꾼다). */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: none;
    border-radius: var(--radius-6);
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.theme-toggle:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.theme-toggle-icon {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
}

.theme-toggle-icon-moon {
    display: none;
}

:root[data-theme="dark"] .theme-toggle-icon-sun {
    display: none;
}

:root[data-theme="dark"] .theme-toggle-icon-moon {
    display: block;
}

.notification-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-6);
    text-decoration: none;
    color: var(--color-text-muted);
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.notification-bell:hover {
    background: var(--color-bg);
    color: var(--color-text);
    text-decoration: none;
}

/* Phase 133 — Phase 68/69/70이 반복해서 백로그로 미뤄뒀던 항목(이모지 🔔 →
   나머지 앱 전체가 쓰는 inline stroke 아이콘 언어로 교체). 사이드바 .nav-group-icon과
   같은 규칙(stroke-width 1.8, fill:none)을 그대로 따르고, stroke는 currentColor로
   둬서 .notification-bell의 color(하버 시 진하게)를 그대로 물려받는다. */
.notification-bell-icon {
    width: 20px;
    height: 20px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
}

.notification-badge {
    position: absolute;
    top: -2px;
    right: -4px;
    min-width: 16px;
    height: 16px;
    padding: 0 var(--space-4);
    border-radius: var(--radius-8);
    background: var(--color-error-text);
    color: #fff;
    font-size: var(--text-10);
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

/* 새 알림이 도착해 배지 숫자가 늘어났을 때만(감소 시 제외) 짧게 튀어오르는 연출 —
   app.js의 refreshNotificationBellCount()가 이전 값보다 커졌을 때만 이 클래스를
   붙인다. */
@keyframes notification-badge-pulse {
    0% { transform: scale(1); }
    35% { transform: scale(1.35); }
    65% { transform: scale(0.95); }
    100% { transform: scale(1); }
}

.notification-badge.badge-pulse {
    animation: notification-badge-pulse 420ms ease;
}

@media (prefers-reduced-motion: reduce) {
    .notification-badge.badge-pulse {
        animation: none;
    }
}

.app-shell {
    display: flex;
    min-height: calc(100vh - var(--header-height));
}

/* 사이드바가 메인 콘텐츠와 스크롤을 공유하면(기존 방식), 펼쳐진 그룹이 많아 사이드바가
   길어졌을 때 활성 메뉴를 보이게 스크롤하면(app.js scrollActiveSidebarLinkIntoView) 페이지
   전체가 함께 내려가 메인 콘텐츠 상단이 화면 밖으로 밀려난다. 사이드바를 헤더 아래에
   sticky로 고정하고 자체 overflow-y를 줘서 스크롤을 분리한다 — 사이드바 쪽 스크롤이
   메인 콘텐츠 스크롤 위치에 더 이상 영향을 주지 않는다. 태블릿 아이콘 레일(641~960px)은
   overflow:visible로, 모바일 하단 시트(≤640px)는 position:fixed로 각자 완전히 재정의하므로
   이 규칙과 겹치지 않는다. */
.app-sidebar {
    width: var(--sidebar-width);
    flex-shrink: 0;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    padding: var(--space-16) 0;
    position: sticky;
    top: var(--header-height);
    height: calc(100vh - var(--header-height));
    overflow-y: auto;
}

.app-sidebar a {
    display: block;
    padding: var(--density-sidebar-item-padding-y) var(--density-sidebar-item-padding-x);
    color: var(--color-text);
    font-size: var(--text-14);
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.app-sidebar a:hover {
    background: var(--color-bg);
    text-decoration: none;
}

.app-sidebar a.active {
    background: var(--color-info-bg);
    color: var(--color-primary);
    font-weight: 600;
    border-right: 2px solid var(--color-primary);
}

/* 사이드바 브랜드 영역(Option A 컴팩트, Phase 178) — 로그인 화면의
   .login-brand-mark를 그대로 재사용한다. 크기 override는 .login-brand-mark 단독
   규칙보다 구체성이 높은 후손 선택자로 둬서, app.css 안에서 어느 쪽이 먼저
   나오는지와 무관하게 항상 이 크기가 이기게 한다. */
.app-sidebar a.sidebar-brand {
    display: flex;
    align-items: center;
    gap: var(--space-10);
    padding: var(--space-12) var(--space-20);
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.sidebar-brand .login-brand-mark {
    width: 26px;
    height: 26px;
    font-size: var(--text-13);
    border-radius: var(--radius-6);
    flex-shrink: 0;
}

.sidebar-brand-name {
    font-size: var(--text-14);
    font-weight: 700;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 사이드바 그룹(.nav-group) 일괄 펼침/닫음 컨트롤 — app.js initSidebarAccordion이
   와이어링하며, "모두 닫음"도 현재 위치한 그룹(data-active-group)은 예외로 열어둔다. */
.sidebar-toolbar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-8);
    padding: 0 var(--space-20) var(--space-10);
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.sidebar-toolbar-btn {
    background: none;
    border: none;
    padding: var(--space-3) 0;
    font-size: var(--text-11);
    color: var(--color-text-muted);
    cursor: pointer;
}

.sidebar-toolbar-btn:hover {
    color: var(--color-primary);
    text-decoration: underline;
}

.sidebar-toolbar-sep {
    width: 1px;
    height: 10px;
    background: var(--color-border);
}

.nav-group {
    margin-top: var(--space-4);
}

.nav-group summary {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    padding: var(--density-sidebar-item-padding-y) var(--density-sidebar-item-padding-x);
    font-size: var(--text-13);
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--color-text-muted);
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: background-color var(--transition-fast);
}

.nav-group summary::-webkit-details-marker {
    display: none;
}

.nav-group summary:hover {
    background: var(--color-bg);
}

.nav-group summary:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
}

.nav-group summary span {
    flex: 1;
}

.nav-group-icon {
    width: 16px;
    height: 16px;
    stroke: var(--color-text-muted);
    fill: none;
    stroke-width: 1.8;
    flex-shrink: 0;
}

.nav-chevron {
    width: 14px;
    height: 14px;
    stroke: var(--color-text-muted);
    fill: none;
    stroke-width: 2;
    transition: transform var(--transition-fast);
    flex-shrink: 0;
}

.nav-group[open] > summary .nav-chevron {
    transform: rotate(90deg);
}

.nav-group-items a {
    padding-left: var(--space-40);
}

.nav-group-admin {
    margin-top: var(--space-8);
    padding-top: var(--space-8);
    border-top: 1px solid var(--color-border);
}

.app-sidebar a.nav-standalone {
    margin-top: var(--space-8);
    padding-top: var(--space-14);
    border-top: 1px solid var(--color-border);
}

/* .nav-home/.nav-standalone(홈/알림)에 아이콘이 Phase 243(태블릿 아이콘 레일)에서
   추가됐지만, 그 미디어쿼리 안의 .nav-standalone-icon 크기 규칙만 있고 기본
   스코프(데스크톱) 규칙이 없어 SVG가 브라우저 기본 크기(폭/높이 제약 없는 replaced
   element)로 거대하게 렌더링되던 결함을 수정한다. .app-sidebar a의 기본
   display:block 때문에 아이콘(block-level svg)과 텍스트가 세로로 쌓이던 것도
   .nav-group summary와 동일하게 가로 정렬로 맞춘다. */
.nav-home,
.nav-standalone {
    display: flex;
    align-items: center;
    gap: var(--space-8);
}

.nav-standalone-icon {
    width: 16px;
    height: 16px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
    flex-shrink: 0;
}

/* 모바일 하단 탭바 + 오버플로 시트(UX 감사 TOP-36, Phase 237) — 기본은
   데스크톱이라 전부 숨김. ≤640px 미디어쿼리가 아래에서 뒤집는다.
   .app-sidebar 자체를 시트로 재사용하므로 새 컴포넌트는 탭바/닫기 버튼/
   백드롭 3개뿐이다. */
.mobile-tabbar,
.mobile-sheet-close,
.mobile-sheet-backdrop {
    display: none;
}

.mobile-tab-icon-wrap {
    position: relative;
    display: inline-flex;
}

.mobile-tab-badge {
    position: absolute;
    top: -4px;
    right: -8px;
}

.app-content {
    flex: 1;
    padding: var(--space-24) var(--space-32);
    max-width: 1100px;
    margin: 0 auto;
}

/* 로그인류 단일 카드 화면(change-password 등)에서 app-content의 기본 1100px 대신
   좁은 폭을 쓸 때 app-content와 함께 붙인다. */
.app-content-narrow {
    max-width: 420px;
}

.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-12);
    margin-bottom: var(--space-20);
}

/* page-header 오른쪽에 버튼을 여러 개 묶어야 할 때(단일 버튼은 page-header의
   justify-content: space-between만으로 충분하다). */
.header-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8);
}

.page-header h1 {
    font-size: var(--text-20);
    margin: 0;
}

.breadcrumb {
    font-size: var(--text-13);
    color: var(--color-text-muted);
    margin-bottom: var(--space-8);
}

/* ---- Buttons ---- */

.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-6);
    padding: var(--density-control-padding-y) var(--density-control-padding-x);
    font-size: var(--text-13);
    font-weight: 600;
    border-radius: var(--radius-4);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast);
}

.btn:hover {
    background: var(--color-bg);
    text-decoration: none;
}

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

.btn-primary {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #fff;
}

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

.btn-danger {
    color: var(--color-error-text);
    border-color: var(--color-error-text);
}

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

/* Secondary — 기본 .btn 외형과 동일하다(border+surface 배경 자체가 이미 secondary
   룩이라 별도 재정의가 없다). maintenance-mode/edit.html처럼 "주 액션 옆의 보조
   액션"이라는 의도를 마크업에서 명시하고 싶을 때 .btn-primary/.btn-danger와 나란히
   쓴다. */
.btn-secondary {
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text);
}

.btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* 브랜딩 테마 색상 프리셋 스와치(branding/edit.html) */
.preset-swatch {
    width: var(--space-24);
    height: var(--space-24);
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-6);
    cursor: pointer;
}

.preset-swatch:hover {
    transform: scale(1.1);
}

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

/* ---- Tabs ---- */

.tabs {
    display: flex;
    gap: var(--space-4);
    border-bottom: 1px solid var(--color-border);
    margin-bottom: var(--space-16);
}

.tabs button {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    padding: var(--space-10) var(--space-14);
    font-size: var(--text-14);
    color: var(--color-text-muted);
    cursor: pointer;
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.tabs button:hover {
    color: var(--color-text);
}

.tabs button.active {
    color: var(--color-primary);
    border-bottom-color: var(--color-primary);
    font-weight: 600;
}

/* ---- Sub-tabs(Phase 130) — 페이지 레벨 .tabs 아래에 딸린 하위 필터 탭(예: 프로젝트
   상세의 "업무/마일스톤/..." 안에 있는 "전체/할 일/진행중/완료"). .tabs와 똑같은 밑줄
   스타일을 쓰면 두 레벨이 한 줄의 tabs처럼 보여 계층이 흐려지므로, 밑줄 대신 pill
   형태로 시각적 무게를 낮춘다. */
.tabs-sub {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin: 0 0 var(--space-16);
}

.tabs-sub button {
    background: var(--color-bg);
    border: none;
    border-radius: var(--radius-4);
    padding: var(--space-4) var(--space-10);
    font-size: var(--text-12);
    font-weight: 600;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.tabs-sub button:hover {
    color: var(--color-text);
}

.tabs-sub button.active {
    background: var(--color-info-bg);
    color: var(--color-primary);
}

/* ---- Table ---- */

.data-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-6);
    overflow: hidden;
}

.data-table th,
.data-table td {
    padding: var(--density-table-cell-padding-y) var(--density-table-cell-padding-x);
    text-align: left;
    font-size: var(--text-13);
    border-bottom: 1px solid var(--color-border);
}

.data-table th {
    background: var(--color-bg);
    color: var(--color-text-muted);
    font-weight: 600;
}

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

/* 근태 상세 구간 그룹화(2026-09-06 UX 진단 UX-1) — CLOCK_IN 행 위에 구분선을 더해 근무
   구간 경계를 표에서 바로 알아볼 수 있게 한다. */
.data-table tbody tr.segment-divider td {
    border-top: 2px solid var(--color-border);
}

.data-table tbody tr.clickable {
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

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

/* outline은 <tr>에서 Chromium 테이블 레이아웃 특성상 그려지지 않는 게 실측 확인돼(키보드
   tabindex 포커스 자체는 정상 동작하지만 시각적 표시가 안 보임), 대신 hover보다 진한
   background로 포커스를 표시한다 — hover와 확실히 구분되면서 <tr>에서 신뢰성 있게
   렌더링되는 유일한 방법이다. */
.data-table tbody tr.clickable:focus-visible {
    outline: none;
    background: var(--color-info-bg);
}

/* ---- Status badge ---- */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-8);
    border-radius: var(--radius-10);
    font-size: var(--text-12);
    font-weight: 600;
}

.badge-draft {
    background: var(--color-bg);
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
}

.badge-in-progress {
    background: var(--color-info-bg);
    color: var(--color-info-text);
}

.badge-approved {
    background: var(--color-success-bg);
    color: var(--color-success-text);
}

.badge-rejected {
    background: var(--color-error-bg);
    color: var(--color-error-text);
}

.badge-pending {
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
}

/* ONGOFLOW PHASE 368 — Maintenance Mode/Deployment 상태 배지. 새 색상 값을 만들지 않고
   기존 success/warning/info 토큰만 재사용한다(groupware-design-system Skill 원칙). */
.badge-normal {
    background: var(--color-success-bg);
    color: var(--color-success-text);
}

.badge-maintenance {
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
}

.badge-deploying {
    background: var(--color-info-bg);
    color: var(--color-info-text);
}

.maintenance-status-row {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    margin-bottom: var(--space-16);
}

/* 계약 주행거리 "위험" 등급 전용(ADR-0068) — 주의(pending)와 초과(rejected) 사이 중간 색. */
.badge-critical {
    background: var(--color-critical-bg);
    color: var(--color-critical-text);
}

/* ---- States ---- */

.state-message {
    padding: var(--space-32);
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--text-14);
}

.state-message.error {
    color: var(--color-error-text);
}

/* ---- Pagination ---- */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-12);
    margin-top: var(--space-16);
    font-size: var(--text-13);
    color: var(--color-text-muted);
}

/* ---- Form ---- */

.field {
    margin-bottom: var(--space-16);
}

.field label {
    display: block;
    font-size: var(--text-13);
    font-weight: 600;
    margin-bottom: var(--space-6);
}

.hint {
    font-size: var(--text-12);
    color: var(--color-text-muted);
}

.field .hint {
    font-weight: 400;
}

/* "OO는 작성 후 변경할 수 없습니다" 류 잠금 안내 — 6개 화면(assets/boards/
   cloud-subscriptions×2/weekly-reports)에 동일한 인라인 style(display:none;
   margin-top:6px)로 반복되던 것을 통합. */
.field .hint.locked-hint {
    margin-top: var(--space-6);
}

.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="number"],
.field input[type="date"],
.field input[type="datetime-local"],
.field input[type="time"],
.field input[type="month"],
.field input[type="search"],
.field input[type="tel"],
.field input[type="url"],
.field select,
.field textarea {
    width: 100%;
    padding: var(--density-field-padding-y) var(--density-field-padding-x);
    font-size: var(--text-14);
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    font-family: inherit;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.field input[type="text"]:focus,
.field input[type="email"]:focus,
.field input[type="password"]:focus,
.field input[type="number"]:focus,
.field input[type="date"]:focus,
.field input[type="datetime-local"]:focus,
.field input[type="time"]:focus,
.field input[type="month"]:focus,
.field input[type="search"]:focus,
.field input[type="tel"]:focus,
.field input[type="url"]:focus,
.field select:focus,
.field textarea:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.15);
}

.field input:disabled,
.field select:disabled,
.field textarea:disabled {
    background: var(--color-disabled-bg);
    color: var(--color-disabled-text);
    cursor: not-allowed;
}

.field textarea {
    min-height: 120px;
    resize: vertical;
}

.field input[type="checkbox"] {
    accent-color: var(--color-primary);
    vertical-align: middle;
}

/* input/select + 버튼을 한 줄에 놓는 반복 패턴 — 첫 번째 자식(보통 input 또는
   .field)이 남는 폭을 채우고, 버튼은 자기 크기만 차지한다. */
.field-row {
    display: flex;
    gap: var(--space-8);
    align-items: flex-end;
}

.field-row > :first-child {
    flex: 1;
}

/* 체크박스 하나 + 설명 텍스트를 한 label로 묶는 반복 패턴(필터 등). */
.checkbox-label {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

/* 라디오 버튼 여러 개를 한 줄에 늘어놓는 반복 패턴. */
.radio-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-16);
}

.radio-inline {
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
}

/* 화면 한 곳에서만 필요한 미세 여백 조정용 — 일반 규칙(margin-bottom)이 지나칠 때만
   쓴다. 새 값이 필요하면 여기 추가하기보다 인라인 style을 먼저 의심할 것. */
.mb-8 {
    margin-bottom: var(--space-8);
}

.mb-12 {
    margin-bottom: var(--space-12);
}

/* ---- File input(Phase 132) — 첨부파일 업로드 폼(11개 화면)의 <input type="file">가
   유일하게 스타일 안 된 네이티브 컨트롤로 남아 옆의 .btn "업로드" 버튼과 이질감이
   있었다. ::file-selector-button만 .btn과 같은 값으로 맞추면 JS/마크업을 전혀 건드리지
   않고도(네이티브 input 그대로 — 접근성/드래그앤드롭 유지) 시각적으로 통일된다.
   프로필 이미지 input(employees-detail.js)은 이미 .sr-only로 완전히 숨겨 커스텀 버튼을
   쓰고 있어 이 규칙과 무관하다(숨김 상태라 영향 없음). ---- */
input[type="file"] {
    font-size: var(--text-13);
    color: var(--color-text-muted);
}

input[type="file"]::file-selector-button {
    font-family: inherit;
    font-size: var(--text-13);
    font-weight: 600;
    padding: var(--space-8) var(--space-14);
    margin-right: var(--space-10);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
    transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

input[type="file"]::file-selector-button:hover {
    background: var(--color-bg);
}

/* ---- Form grid(Phase 130) — 필드가 5개 이상인 폼을 2열로 묶고 섹션 제목을 붙인다.
   .field 자체의 margin-bottom은 그대로 두고(세로 간격), 열 사이 가로 간격만 gap의
   column 값으로 준다. 폭이 좁아야 하는 폼(필드 4개 이하)은 지금처럼 .form-grid 없이
   1열 그대로 둔다 — 모든 폼을 일괄 2열로 바꾸지 않는다. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0 var(--space-24);
}

.form-grid .field-full,
.form-grid .action-bar {
    grid-column: 1 / -1;
}

.form-section {
    grid-column: 1 / -1;
    font-size: var(--text-12);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    padding-bottom: var(--space-6);
    margin: var(--space-8) 0 var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.form-grid .form-section:first-child {
    margin-top: 0;
}

@media (max-width: 640px) {
    .form-grid {
        grid-template-columns: 1fr;
    }
}

/* 상세 화면 카드 안에서 하위 목록(댓글/담당자/거래 이력 등)을 여는 소제목 — 7개 JS
   파일 11곳에 동일한 인라인 style(font-size:15px; margin:16px 0 8px)로 반복되던 것을
   통합. */
.subsection-title {
    font-size: var(--text-15);
    margin: var(--space-16) 0 var(--space-8);
}

/* ---- Card / detail sections ---- */

.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-6);
    padding: var(--density-card-padding-y) var(--density-card-padding-x);
    margin-bottom: var(--space-16);
}

.card h2 {
    font-size: var(--text-15);
    margin: 0 0 var(--space-12);
}

.detail-row {
    display: flex;
    padding: var(--space-6) 0;
    font-size: var(--text-13);
    border-bottom: 1px solid var(--color-bg);
}

.detail-row:last-child {
    border-bottom: none;
}

/* 동승자 각 행(NEXT-C 프론트엔드, §3) — .detail-row와 같은 리듬으로 여러 명을 세로로
   나열한다. */
.participant-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-8);
    padding: var(--space-6) 0;
    font-size: var(--text-13);
    border-bottom: 1px solid var(--color-bg);
}

.participant-row:last-child {
    border-bottom: none;
}

.detail-row .label {
    width: 120px;
    flex-shrink: 0;
    color: var(--color-text-muted);
}

.detail-row .value {
    flex: 1;
    white-space: pre-wrap;
}

.attachment-row {
    align-items: center;
    gap: var(--space-10);
}

/* PHASE 391-C — Board WYSIWYG(Quill) 편집 영역 기본 높이. Quill 기본값은 내용이 없으면
   한두 줄 높이로 보여 첫 작성 화면이 빈약해 보인다는 피드백(2026-09-20)에 따라 최소
   높이를 늘렸다 — 이 게시판 작성/수정 화면에만 적용되고 다른 화면에는 영향 없다. */
#content-editor .ql-editor {
    min-height: 280px;
}

/* PHASE 391-C — Board WYSIWYG(Quill) 게시글 본문 렌더링. 서버(BoardContentSanitizer)가
   이미 허용 목록만 통과시킨 안전한 HTML이라 innerHTML로 그대로 꽂아 넣는다(boards-detail.js) —
   이 블록은 그 결과물의 시각 스타일만 담당하고 보안과는 무관하다. Quill 자체 CSS는 편집
   화면(quill.snow.css)에만 로드하고, 읽기 전용 렌더링은 Design Token만으로 새로 정의한다. */
.rich-content {
    font-size: var(--text-14);
    line-height: 1.6;
    word-break: break-word;
}

.rich-content > *:first-child {
    margin-top: 0;
}

.rich-content > *:last-child {
    margin-bottom: 0;
}

.rich-content p,
.rich-content ul,
.rich-content ol,
.rich-content blockquote,
.rich-content pre,
.rich-content table {
    margin: 0 0 var(--space-12);
}

.rich-content h1,
.rich-content h2,
.rich-content h3 {
    margin: var(--space-20) 0 var(--space-10);
    line-height: 1.3;
}

.rich-content h1 { font-size: var(--text-20); }
.rich-content h2 { font-size: var(--text-18); }
.rich-content h3 { font-size: var(--text-16); }

.rich-content ul,
.rich-content ol {
    padding-left: var(--space-24);
}

.rich-content blockquote {
    margin-left: 0;
    padding: var(--space-6) var(--space-16);
    border-left: 3px solid var(--color-border);
    color: var(--color-text-muted);
}

.rich-content pre {
    padding: var(--space-12);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: var(--text-13);
    white-space: pre-wrap;
    word-break: break-word;
}

.rich-content code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
}

.rich-content table {
    border-collapse: collapse;
    width: 100%;
}

.rich-content th,
.rich-content td {
    border: 1px solid var(--color-border);
    padding: var(--space-6) var(--space-10);
    text-align: left;
}

.rich-content th {
    background: var(--color-bg);
}

.rich-content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius-4);
}

.rich-content a {
    color: var(--color-primary);
}

.rich-content hr {
    border: none;
    border-top: 1px solid var(--color-border);
    margin: var(--space-16) 0;
}

.comment-author-row {
    align-items: center;
    gap: var(--space-8);
}

.attachment-thumb {
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: var(--radius-6);
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    flex-shrink: 0;
}

.json-block {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-6);
    padding: var(--space-12);
    margin: var(--space-4) 0 0;
    font-family: monospace;
    font-size: var(--text-12);
    white-space: pre-wrap;
    word-break: break-all;
    max-height: 400px;
    overflow-y: auto;
}

.action-bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8);
    margin-bottom: var(--space-16);
}

/* ---- Filter bar(Phase 131) — 목록 화면 상단 검색/필터 폼의 반복 패턴(13개 화면에
   동일한 인라인 style로 흩어져 있던 것)을 클래스로 뽑았다. */
.filter-bar {
    display: flex;
    gap: var(--space-8);
    flex-wrap: wrap;
    align-items: flex-end;
}

.filter-bar .field {
    margin: 0;
}

/* ---- Inline option(Phase 131) — 체크박스/라디오 + 라벨을 가로로 나열할 때 쓰는
   반복 패턴(관리자 설정 카드의 체크박스 그룹 등). ---- */
.inline-option {
    display: inline-block;
    margin-right: var(--space-16);
    font-weight: 400;
}

/* ---- Settings panel(Phase 131) — 목록/통계 화면 안의 ADMIN 전용 설정을 본문
   목록보다 아래로 내리고 기본 접힘 상태로 두기 위한 접이식 패널. 사이드바
   .nav-group과 같은 <details> 패턴을 메인 콘텐츠 영역에 재사용한다. */
.settings-panel {
    margin-top: var(--space-24);
}

.settings-panel > summary {
    cursor: pointer;
    font-size: var(--text-13);
    font-weight: 600;
    color: var(--color-text-muted);
    padding: var(--space-8) 0;
    list-style: none;
    user-select: none;
}

.settings-panel > summary::-webkit-details-marker {
    display: none;
}

.settings-panel > summary::before {
    content: "▸";
    display: inline-block;
    width: 1em;
    transition: transform var(--transition-fast);
}

.settings-panel[open] > summary::before {
    transform: rotate(90deg);
}

.settings-panel > summary:hover {
    color: var(--color-text);
}

.settings-panel > .card:first-of-type {
    margin-top: var(--space-8);
}

/* ---- Approver picker ---- */

.approver-search {
    margin-bottom: var(--space-8);
}

.approver-list {
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    padding: var(--space-8);
}

.approver-list label {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    padding: var(--space-6) var(--space-4);
    font-size: var(--text-13);
    font-weight: 400;
    cursor: pointer;
    border-radius: var(--radius-4);
    transition: background-color var(--transition-fast);
}

.approver-list label:hover {
    background: var(--color-bg);
}

.approver-list label[hidden] {
    display: none;
}

.approver-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
    margin-top: var(--space-8);
}

.approver-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-6);
    background: var(--color-info-bg);
    color: var(--color-info-text);
    padding: var(--space-4) var(--space-8);
    border-radius: var(--radius-12);
    font-size: var(--text-12);
}

.approver-chip .seq {
    font-weight: 700;
}

.approver-chip .final-decision-toggle {
    border: 1px solid var(--color-warning-text);
    background: transparent;
    color: var(--color-warning-text);
    border-radius: var(--radius-8);
    font-size: var(--text-11);
    padding: var(--space-1) var(--space-6);
    cursor: pointer;
}

.approver-chip .final-decision-toggle.active {
    background: var(--color-warning-bg);
    font-weight: 700;
}

.badge-final {
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
}

.notice {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    font-size: var(--text-13);
    padding: var(--space-10) var(--space-12);
    border-radius: var(--radius-4);
    margin-bottom: var(--space-16);
}

/* ---- Org chart ---- */

.org-chart {
    display: flex;
    gap: var(--space-16);
    align-items: flex-start;
}

.org-chart-tree {
    width: 360px;
    flex-shrink: 0;
}

.org-chart-detail {
    flex: 1;
    min-width: 0;
}

.org-search-results {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    margin-top: var(--space-4);
    max-height: 240px;
    overflow-y: auto;
}

.org-search-results .result-group-label {
    padding: var(--space-6) var(--space-10);
    font-size: var(--text-11);
    font-weight: 700;
    color: var(--color-text-muted);
    background: var(--color-bg);
}

.org-search-results .result-item {
    padding: var(--space-8) var(--space-10);
    font-size: var(--text-13);
    cursor: pointer;
    border-top: 1px solid var(--color-border);
    transition: background-color var(--transition-fast);
}

.org-search-results .result-item:hover {
    background: var(--color-info-bg);
}

.org-tree {
    max-height: 65vh;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    padding: var(--space-8);
}

.org-tree-node {
    display: flex;
    align-items: center;
    gap: var(--space-6);
    padding: var(--space-4) var(--space-6);
    border-radius: var(--radius-4);
    cursor: pointer;
    font-size: var(--text-13);
    transition: background-color var(--transition-fast);
}

.org-tree-node:hover {
    background: var(--color-info-bg);
}

.org-tree-node.selected {
    background: var(--color-info-bg);
    font-weight: 700;
}

.org-tree-toggle {
    width: 16px;
    text-align: center;
    color: var(--color-text-muted);
    flex-shrink: 0;
    user-select: none;
}

.org-tree-employee {
    display: flex;
    align-items: center;
    gap: var(--space-6);
    padding: var(--space-4) var(--space-6);
    border-radius: var(--radius-4);
    cursor: pointer;
    font-size: var(--text-13);
    color: var(--color-text-muted);
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.org-tree-employee:hover {
    background: var(--color-info-bg);
}

.org-tree-employee.selected {
    background: var(--color-info-bg);
    font-weight: 700;
    color: var(--color-text);
}

.notice-error {
    background: var(--color-error-bg);
    color: var(--color-error-text);
}

.value-danger {
    color: var(--color-error-text);
    font-weight: 700;
}

.notice-info {
    background: var(--color-info-bg);
    color: var(--color-info-text);
}

.notice-warning {
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
}

.source-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-6);
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
    padding: var(--space-8) var(--space-12);
    border-radius: var(--radius-4);
    font-size: var(--text-13);
    margin-bottom: var(--space-16);
}

/* ---- Confirm modal ---- */

.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(31, 35, 40, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: var(--z-modal);
}

.modal-dialog {
    background: var(--color-surface);
    border-radius: var(--radius-6);
    padding: var(--space-24);
    width: 360px;
    max-width: calc(100vw - 32px);
    box-shadow: var(--shadow-md);
}

/* 외근/출장 연결 Modal(NEXT-C 프론트엔드, §5)의 후보 목록 — 개수가 많아져도 .modal-dialog
   자체(confirmModal과 공유)는 건드리지 않고 이 목록만 스크롤되게 한다. */
.trip-candidate-list {
    max-height: 50vh;
    overflow-y: auto;
    margin-bottom: var(--space-16);
}

.modal-message {
    margin: 0 0 var(--space-20);
    font-size: var(--text-14);
    white-space: pre-wrap;
}

.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-8);
}

/* ---- Toast(Phase 145) — 화면을 이동하지 않는 액션(고정/첨부파일 업로드/삭제 등)의
   성공·실패를 알리는 일시적 알림. 기존 .notice는 카드 안에 계속 박혀 있는 형태라
   다음 렌더링까지 남아있었는데, 그중 "액션 결과 통지" 용도로 쓰이던 곳만 이걸로
   대체한다(페이지 자체를 못 그릴 때의 상태 메시지는 .state-message.error를 그대로 씀). */
.toast-container {
    position: fixed;
    top: var(--space-16);
    right: var(--space-16);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
    max-width: 360px;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-8);
    padding: var(--space-10) var(--space-12);
    border-radius: var(--radius-4);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
    font-size: var(--text-13);
    animation: toast-in var(--transition-fast);
}

.toast-success {
    border-color: var(--color-success-text);
    background: var(--color-success-bg);
    color: var(--color-success-text);
}

.toast-error {
    border-color: var(--color-error-text);
    background: var(--color-error-bg);
    color: var(--color-error-text);
}

.toast-info {
    border-color: var(--color-info-text);
    background: var(--color-info-bg);
    color: var(--color-info-text);
}

.toast-message {
    flex: 1;
}

.toast-close {
    background: none;
    border: none;
    padding: 0;
    line-height: 1;
    font-size: var(--text-14);
    color: inherit;
    cursor: pointer;
}

@keyframes toast-in {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ---- Home dashboard ---- */

.dashboard-greeting {
    display: flex;
    align-items: center;
    gap: var(--space-12);
    margin-bottom: var(--space-20);
}

.dashboard-greeting h1 {
    font-size: var(--text-20);
    margin: 0 0 var(--space-4);
}

.dashboard-greeting p {
    margin: 0;
    font-size: var(--text-13);
    color: var(--color-text-muted);
}

.summary-row {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: var(--space-16);
    margin-bottom: var(--space-16);
}

.summary-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-6);
    padding: var(--space-18) var(--space-20);
    color: inherit;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.summary-card:hover {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
}

.summary-card-title {
    font-size: var(--text-13);
    color: var(--color-text-muted);
    font-weight: 600;
}

.summary-card-value {
    font-size: var(--text-26);
    font-weight: 700;
}

.summary-card-detail {
    display: block;
    font-size: var(--text-12);
}

/* 계약 주행거리 사용률 바(NEXT-C 프론트엔드, §15/§16) — 값은 서버가 계산한 usagePercentage를
   그대로 그리기만 한다. */
.meter {
    display: inline-block;
    width: 100px;
    height: 8px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-10);
    overflow: hidden;
    vertical-align: middle;
    margin-right: var(--space-8);
}

.meter-fill {
    height: 100%;
}

.meter-fill-approved { background: var(--color-success-text); }
.meter-fill-pending { background: var(--color-warning-text); }
.meter-fill-critical { background: var(--color-critical-text); }
.meter-fill-rejected { background: var(--color-error-text); }

/* ---- Brand mark (login 화면 + 브랜딩 관리 화면 미리보기 공용, Phase 176) ---- */

.login-brand {
    display: flex;
    align-items: center;
    gap: var(--space-12);
    margin: 0 0 var(--space-24);
}

.login-brand-mark {
    position: relative;
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-primary);
    color: #fff;
    font-weight: 700;
    font-size: var(--text-18);
    border-radius: var(--radius-10);
    overflow: hidden;
}

/* 로고 이미지 로드에 성공하면 문자마크 위를 덮어 대체한다(실패 시 JS가 이 img 자체를
   제거해 문자마크로 자연 폴백 — 새 조회 없이 <img onerror>만으로 처리). */
.login-brand-mark img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* 다크 로고(2026-09-09) — 라이트 이미지 위에 겹쳐두고 다크 테마에서만 보여준다.
   .theme-toggle-icon-sun/-moon(아래 참고)과 동일한 토글 관례. 로드 실패(다크 로고
   미등록)면 onerror가 이 img를 제거해 아래 라이트 이미지로 자동 폴백한다. */
.login-brand-mark-img-dark {
    display: none;
}

:root[data-theme="dark"] .login-brand-mark-img-dark {
    display: block;
}

/* 브랜딩 관리 화면의 "다크로 보기" 미리보기(Phase 브랜딩 미리보기 이미지 반영)는
   실제 페이지 테마(:root[data-theme])가 아니라 .branding-preview-dark 스코프로만
   제어되므로, 같은 토글을 이 스코프에도 별도로 걸어준다. */
.branding-preview-dark .login-brand-mark-img-dark {
    display: block;
}

.login-title {
    font-size: var(--text-18);
    font-weight: 700;
    margin: 0;
}

/* 워드마크(서비스명을 이미지로 표현한 문자 로고, 2026-09-09) — 심볼 마크와 달리
   가변폭 텍스트 자리(.login-title/.app-header .brand/.sidebar-brand-name)를
   대체해야 해서 position:absolute 겹침 대신, JS(app.js의 applyWordmarkImages /
   pageHead 인라인 스크립트)가 프리로드 확인 후 .brand-wordmark-text를 hidden
   처리하고 이 이미지를 노출하는 방식을 쓴다. height:1em으로 부모의 font-size를
   그대로 따라가므로 세 위치 각각의 텍스트 크기에 맞춰 자동으로 스케일된다 —
   위치별 override가 필요 없다. */
.brand-wordmark-img {
    height: 1em;
    width: auto;
    max-width: 100%;
    vertical-align: middle;
}

.login-subtitle {
    font-size: var(--text-13);
    color: var(--color-text-muted);
    margin: 2px 0 0;
}

/* 브랜딩 관리 화면의 "다크로 보기" 미리보기 전용(Phase 브랜딩 P0) — 실제 페이지
   테마(:root[data-theme])와 무관하게 이 스코프 안의 --color-* 변수만 다크 값으로
   덮어써서, 그 안의 .login-brand/.sidebar-brand/.app-header .brand가 자동으로
   다크 배경에서의 모습을 보여준다. 값은 :root[data-theme="dark"]의 기존 리터럴을
   그대로 가져왔다(새 팔레트를 만들지 않음).*/
.branding-preview-dark {
    --color-bg: #15171b;
    --color-surface: #1e2126;
    --color-border: #30343c;
    --color-text: #e7e9ec;
    --color-text-muted: #9aa1ac;
    background: var(--color-bg);
    padding: var(--space-16);
    border-radius: var(--radius-10);
}

.quick-access {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: var(--space-8);
}

.quick-access-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-8);
    padding: var(--space-14) var(--space-8);
    border-radius: var(--radius-6);
    color: var(--color-text);
    text-align: center;
    font-size: var(--text-12);
    transition: background-color var(--transition-fast);
}

.quick-access-item:hover {
    background: var(--color-bg);
    text-decoration: none;
}

.quick-access-item svg {
    width: 22px;
    height: 22px;
    stroke: var(--color-primary);
    fill: none;
    stroke-width: 1.6;
}

/* 홈 대시보드 Widget 개인화(순서 변경/Drag&Drop) — 위젯들은 #dashboard-widgets 안에
   세로 1열로 쌓인다(예전 .dashboard-grid의 2단 배치를 통일해 자유로운 순서 변경을
   단순하게 만든다). 편집 모드가 아닐 때는 .dashboard-widget-toolbar가 숨어 있고
   home.js의 initDashboardWidgetOrder()가 [data-editing] 토글로 노출한다. */
#dashboard-widgets {
    display: flex;
    flex-direction: column;
    gap: var(--space-16);
}

.dashboard-layout-controls {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-8);
    margin-bottom: var(--space-16);
}

.dashboard-layout-edit-actions {
    display: flex;
    gap: var(--space-8);
}

.dashboard-widget {
    position: relative;
}

.dashboard-widget[data-editing] {
    outline: 1px dashed var(--color-border);
    outline-offset: 4px;
    border-radius: var(--radius-6);
}

.dashboard-widget.drag-over {
    outline: 2px dashed var(--color-primary);
    outline-offset: 4px;
}

.dashboard-widget-toolbar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-4);
    margin-bottom: var(--space-8);
}

.widget-move-btn,
.widget-drag-handle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: none;
    border-radius: var(--radius-4);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
}

.widget-drag-handle {
    cursor: grab;
}

.widget-move-btn:hover,
.widget-drag-handle:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.widget-move-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.widget-move-btn svg,
.widget-drag-handle svg {
    width: 16px;
    height: 16px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
}

.widget-drag-handle svg {
    fill: currentColor;
    stroke: none;
}

.dashboard-widget-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-12);
}

.dashboard-widget-header h2 {
    margin: 0;
}

.dashboard-widget-header a {
    font-size: var(--text-13);
}

.dashboard-subsection {
    margin-bottom: var(--space-16);
}

.dashboard-subsection:last-child {
    margin-bottom: 0;
}

.dashboard-subsection h3 {
    font-size: var(--text-13);
    color: var(--color-text-muted);
    margin: 0 0 var(--space-8);
}

.dashboard-list a.dashboard-list-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-8);
    padding: var(--space-8) 0;
    border-bottom: 1px solid var(--color-bg);
    color: var(--color-text);
    font-size: var(--text-13);
    transition: color var(--transition-fast);
}

.dashboard-list a.dashboard-list-row:last-child {
    border-bottom: none;
}

.dashboard-list a.dashboard-list-row:hover {
    color: var(--color-primary);
    text-decoration: none;
}

.dashboard-list-row .row-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-list-row .row-meta {
    flex-shrink: 0;
    color: var(--color-text-muted);
    font-size: var(--text-12);
}

.dashboard-list .state-message {
    padding: var(--space-12) 0;
    text-align: left;
    font-size: var(--text-13);
}

@media (max-width: 960px) {
    .summary-row {
        grid-template-columns: 1fr 1fr;
    }

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

@media (max-width: 640px) {
    .summary-row {
        grid-template-columns: 1fr;
    }

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

/* ---- Detail 2단 레이아웃(본문 좌측 / 프로필 이미지 우측) ---- */

.detail-layout {
    display: flex;
    align-items: flex-start;
    gap: var(--space-24);
}

.detail-layout-main {
    flex: 1;
    min-width: 0;
}

.detail-layout-aside {
    flex: 0 0 340px;
    width: 340px;
}

@media (max-width: 960px) {
    .detail-layout {
        flex-direction: column;
    }

    .detail-layout-aside {
        flex-basis: auto;
        width: 100%;
    }
}

/* ---- Avatar / 프로필 이미지 ---- */

/* Phase 132 — 8px 라운드 사각형에서 완전한 원형으로. Jdenticon 폴백/실제 사진 모두
   "이것은 아바타다"를 한눈에 읽히게 하려는 목적(사각형 라운드는 28px 같은 작은
   크기에서 깨진 이미지 아이콘처럼 보인다는 실사 지적). Cropper.js가 업로드 시
   aspectRatio:1로 항상 정사각형으로 잘라두므로(employees-detail.js) 원형 마스킹이
   실제 사진을 어색하게 자르지 않는다. */
.avatar {
    display: inline-block;
    border-radius: 50%;
    object-fit: cover;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    vertical-align: middle;
}

.profile-image-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-10);
    width: 100%;
    margin: 0 0 var(--space-16);
    text-align: center;
}

.profile-image-card h2 {
    margin: 0;
}

.profile-image-card .avatar {
    width: 160px;
    height: 160px;
    max-width: 100%;
}

.profile-image-actions {
    display: flex;
    flex-direction: row;
    gap: var(--space-8);
    width: 100%;
}

.profile-image-actions[hidden] {
    display: none;
}

.profile-image-actions .btn {
    flex: 1;
    justify-content: center;
}

.profile-image-actions .btn[hidden] {
    display: none;
}

/* 자르기 박스가 열려 있는 동안만 노출되는 확대/축소/원래대로 컨트롤(Phase 165 후속) —
   휠 확대만으로는 어느 정도까지 확대됐는지 가늠하기 어렵고, 과하게 확대하면 이미지가
   프레임 밖으로 벗어나 일부만 보이는 문제가 있어 추가했다. */
.profile-image-zoom-controls {
    display: flex;
    flex-direction: row;
    gap: var(--space-8);
    width: 100%;
}

.profile-image-zoom-controls[hidden] {
    display: none;
}

.profile-image-zoom-controls .btn {
    flex: 1;
    justify-content: center;
}

/* 자르기 편집 모달(Phase 165 후속) — 카드 안에서 폭을 키우던 이전 방식이 "내 정보
   수정" 등 옆 카드를 눌러서 불편하다는 지적을 받아, 페이지 레이아웃과 무관하게 크게
   쓸 수 있는 모달로 옮겼다. 기본 .modal-dialog(360px, 확인/취소류 전용)보다 훨씬
   넓게 쓴다. */
.profile-image-modal-dialog {
    width: 700px;
    max-width: calc(100vw - 32px);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-10);
    text-align: center;
}

.profile-image-modal-dialog h2 {
    margin: 0;
}

.profile-image-modal-dialog .hint {
    color: var(--color-text-muted);
    font-size: var(--text-13);
    margin: 0;
}

.profile-image-modal-dialog .crop-target {
    display: block;
    width: 100%;
    height: auto;
    /* 세로로 극단적으로 긴 원본(예: 스크린샷)이 프레임을 한없이 늘리지 않도록 상한만
       둔다 — width:100%와 함께라면 비율은 그대로 유지된 채 축소된다(찌그러지지 않음). */
    max-height: 700px;
}

/* 최종 아바타가 원형(.avatar)이라, 자르기 선택 박스도 정사각형이 아니라 원형
   뷰파인더로 보여줘야 "이 안이 실제로 보이는 부분"이라는 게 직관적이다. 잘라내는
   좌표 자체는 Cropper.js 특성상 여전히 정사각형이고(getCroppedCanvas는 그대로
   정사각형을 반환), 여기서는 화면에 보이는 모양만 CSS로 원형으로 감싼다(Cropper.js
   공식 원형 자르기 예제와 동일한 방식). */
.profile-image-modal-dialog .cropper-view-box,
.profile-image-modal-dialog .cropper-face {
    border-radius: 50%;
}

.profile-image-modal-dialog .cropper-view-box {
    outline: 1px solid var(--color-primary);
    overflow: hidden;
}

/* WorkLog → OvertimeRecord 프리필 참고연계(§23 연계1) 선택 목록 모달 — 확인/취소류
   360px 기본 폭으로는 시각+제목을 한 줄에 담기 어려워 프로필 편집 모달과 같은 방식으로
   폭만 넓힌다. */
.work-log-picker-dialog {
    width: 480px;
    max-width: calc(100vw - 32px);
    text-align: left;
}

.work-log-picker-dialog h2 {
    margin: 0 0 var(--space-12);
}

.work-log-picker-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
    max-height: 320px;
    overflow-y: auto;
    margin-bottom: var(--space-16);
}

.work-log-picker-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
    gap: var(--space-4);
    text-align: left;
    white-space: normal;
}

.work-log-picker-time {
    font-size: var(--text-13);
    color: var(--color-text-muted);
}

.work-log-picker-title {
    font-size: var(--text-14);
}

/* ---- Responsive (app shell) ---- */

/* 태블릿 폭 사이드바(641~960px, UX 감사 UX-5 잔여 항목, 2026-09-08) — 이 구간에서
   데스크톱과 같은 220px 고정폭 사이드바를 그대로 쓰면 콘텐츠 영역이 좁아진다.
   새 컴포넌트를 만들지 않고 기존 아코디언(.nav-group/.nav-group-items) 구조를
   그대로 두되, 그룹 라벨 텍스트만 숨기고(접근성 이름은 summary/링크의 aria-label로
   유지, layout.html 참고) 아이콘 72px 레일로 좁힌다. .nav-group-items는 네이티브
   <details open> 상태와 무관하게 항상 hover/focus-within에서만 플라이아웃(절대배치)으로
   뜨도록 강제한다 — details가 열려 있어도(서버가 현재 활성 그룹을 open으로 렌더링)
   레이아웃을 밀어내지 않기 위함이다. 모바일(≤640px, Phase 237 하단 시트)과는
   완전히 분리된 별개 구간이라 겹치지 않는다. */
@media (min-width: 641px) and (max-width: 960px) {
    .app-sidebar {
        width: 72px;
        overflow: visible;
    }

    .sidebar-brand-name,
    .sidebar-toolbar {
        display: none;
    }

    .app-sidebar a.sidebar-brand {
        justify-content: center;
        padding: var(--space-12) 0;
    }

    .nav-home,
    .nav-standalone {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-2);
        padding: var(--space-10) var(--space-4);
        font-size: var(--text-11);
        text-align: center;
    }

    .nav-home span,
    .nav-standalone span {
        display: none;
    }

    .nav-standalone-icon {
        width: 20px;
        height: 20px;
        stroke: currentColor;
        fill: none;
        stroke-width: 1.8;
        flex-shrink: 0;
    }

    .nav-group summary {
        justify-content: center;
        padding: var(--space-10) var(--space-4);
    }

    .nav-group summary span,
    .nav-group summary .nav-chevron {
        display: none;
    }

    .nav-group {
        position: relative;
    }

    .nav-group-items {
        display: none;
        position: absolute;
        left: 100%;
        top: 0;
        min-width: 200px;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-8);
        box-shadow: var(--shadow-md);
        padding: var(--space-6) 0;
        z-index: var(--z-dropdown);
    }

    /* 마우스 호버는 즉시 미리보기(open 여부 무관), 키보드는 summary에 포커스 후
       Enter/Space로 네이티브 [open]을 토글해야 목록이 tab 순서에 들어간다 —
       :focus-within만으로 보이게 하면 포커스만으로는 열려 보이는데 실제 Tab은
       그 안으로 들어가지 않는(닫힌 <details> 자손은 tab 순서 밖) 불일치가
       생겨서 [open]으로 통일했다(2026-09-08 실측 확인). */
    .nav-group:hover .nav-group-items,
    .nav-group[open] .nav-group-items {
        display: block;
    }

    .nav-group-items a {
        padding-left: var(--space-20);
    }
}

/* 테이블 → 카드 스택 전환 기준 640px→900px 상향(UX 감사 UX-5 잔여 항목,
   2026-09-08). 폭이 넓은 .data-table이 641~900px(태블릿 포함)에서도 가로
   스크롤 없이 카드형으로 보이도록 Phase 238 규칙의 breakpoint만 넓힌다 —
   규칙 내용/JS(app.js의 initResponsiveTableLabels(), width 무관하게 항상
   data-label을 붙임)는 그대로 재사용하고 breakpoint만 별도 블록으로 분리했다. */
@media (max-width: 900px) {
    .data-table {
        display: block;
        border: none;
        background: transparent;
    }

    .data-table thead {
        display: none;
    }

    .data-table tbody {
        display: block;
    }

    .data-table tbody tr {
        display: block;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-6);
        padding: var(--space-8) var(--space-12);
        margin-bottom: var(--space-8);
    }

    .data-table tbody tr:last-child {
        margin-bottom: 0;
    }

    .data-table td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: var(--space-12);
        padding: var(--space-4) 0;
        border-bottom: none;
        white-space: normal;
    }

    .data-table td[data-label]::before {
        content: attr(data-label);
        flex-shrink: 0;
        color: var(--color-text-muted);
        font-weight: 600;
        text-align: left;
    }

    .data-table td:not([data-label]) {
        justify-content: flex-start;
    }
}

@media (max-width: 960px) {
    .app-content {
        padding: var(--space-20);
    }

    .org-chart {
        flex-direction: column;
    }

    .org-chart-tree {
        width: 100%;
    }
}

@media (max-width: 640px) {
    .app-header {
        padding: 0 var(--space-12);
        gap: var(--space-8);
    }

    .app-header .user-area {
        gap: var(--space-8);
        font-size: var(--text-12);
    }

    .app-shell {
        flex-direction: column;
        min-height: 0;
    }

    /* 사이드바 자체를 별도 컴포넌트로 새로 만들지 않고, 하단에서 올라오는
       오버플로 시트로 재사용한다(UX 감사 TOP-36, Phase 237 — Phase 71의
       "항상 펼쳐진 가로 스크롤 스트립"을 대체). 기본은 화면 밖(translateY)에
       숨어 있다가 .mobile-sheet-open이 붙으면 올라온다. 내부 레이아웃은
       데스크톱과 동일한 세로 아코디언 그대로 둔다 — 그래서 .sidebar-toolbar/
       .nav-chevron/그룹 세로 레이아웃을 강제로 바꾸던 Phase 71 규칙들이
       전부 필요 없어졌다(이 미디어쿼리에서 없어짐). */
    .app-sidebar {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        max-height: 75vh;
        overflow-y: auto;
        border-right: none;
        border-top: 1px solid var(--color-border);
        border-radius: var(--radius-12) var(--radius-12) 0 0;
        padding-top: var(--space-40);
        padding-bottom: calc(var(--space-16) + env(safe-area-inset-bottom));
        transform: translateY(100%);
        transition: transform var(--transition-fast);
        z-index: calc(var(--z-modal) + 1);
    }

    .app-sidebar.mobile-sheet-open {
        transform: translateY(0);
    }

    .mobile-sheet-close {
        display: flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        top: var(--space-8);
        right: var(--space-8);
        width: 32px;
        height: 32px;
        border: none;
        border-radius: var(--radius-8);
        background: var(--color-bg);
        color: var(--color-text-muted);
        cursor: pointer;
    }

    .mobile-sheet-close svg {
        width: 16px;
        height: 16px;
        stroke: currentColor;
        fill: none;
        stroke-width: 2;
    }

    .mobile-sheet-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.4);
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--transition-fast);
        z-index: var(--z-modal);
    }

    .mobile-sheet-backdrop.open {
        opacity: 1;
        pointer-events: auto;
    }

    /* 하단 탭바 — 홈/결재/알림은 직접 링크, 근태/더보기는 위 시트를 여는
       버튼(data-mobile-sheet-open, app.js initMobileSheet). */
    .mobile-tabbar {
        display: flex;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        background: var(--color-surface);
        border-top: 1px solid var(--color-border);
        padding: var(--space-4) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom));
        z-index: var(--z-dropdown);
    }

    .mobile-tab {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-2);
        padding: var(--space-4) 0;
        border: none;
        background: none;
        color: var(--color-text-muted);
        font-size: var(--text-11);
        font-family: inherit;
        cursor: pointer;
    }

    .mobile-tab svg {
        width: 20px;
        height: 20px;
        stroke: currentColor;
        fill: none;
        stroke-width: 1.8;
    }

    .mobile-tab.active {
        color: var(--color-primary);
        font-weight: 600;
    }

    .app-content {
        padding: var(--space-16);
        padding-bottom: calc(64px + env(safe-area-inset-bottom));
        margin: 0;
        max-width: none;
        min-width: 0;
    }

    .detail-row {
        flex-direction: column;
        gap: var(--space-2);
    }

    .detail-row .label {
        width: auto;
    }
}

/* ---- Timeline (부품 설치 이력, Phase 93) ---- */

.timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.timeline-item {
    position: relative;
    padding: 0 0 var(--space-20) var(--space-22);
    border-left: 2px solid var(--color-border);
    font-size: var(--text-13);
}

.timeline-item:last-child {
    border-left-color: transparent;
    padding-bottom: 0;
}

.timeline-item::before {
    content: "";
    position: absolute;
    left: -6px;
    top: 2px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--color-border);
    border: 2px solid var(--color-surface);
}

.timeline-item.current::before {
    background: var(--color-primary);
}

.timeline-item .timeline-date {
    color: var(--color-text-muted);
    font-size: var(--text-12);
}

.timeline-item .timeline-title {
    font-weight: 600;
    margin-top: var(--space-2);
}

/* ---- 부품 교체 전/후 시각화(Phase 93) ---- */

.change-visual {
    display: flex;
    align-items: center;
    gap: var(--space-12);
    margin: var(--space-8) 0;
}

.change-visual-card {
    flex: 1;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    padding: var(--space-8) var(--space-10);
    font-size: var(--text-12);
}

.change-visual-card .change-visual-label {
    color: var(--color-text-muted);
    font-weight: 600;
    margin-bottom: var(--space-2);
}

.change-visual-card.change-visual-old {
    background: var(--color-error-bg);
}

.change-visual-card.change-visual-new {
    background: var(--color-success-bg);
}

.change-visual-arrow {
    flex-shrink: 0;
    color: var(--color-text-muted);
    font-size: var(--text-16);
}

/* ---- 서식형 결재 문서(휴가 신청 Phase 93/95/96, 경비 정산 Phase 97) — 이름은 "leave-doc"
   에서 시작했지만 구조 자체가 문서 종류에 무관해 Phase 97에서 "formal-doc"으로 일반화했다. ---- */

.formal-doc-title {
    text-align: center;
    font-size: var(--text-18);
    letter-spacing: 0.15em;
    margin: 0 0 var(--space-16);
}

.formal-doc-header {
    display: flex;
    gap: 0;
    margin-bottom: -1px;
}

.formal-doc-info-table,
.formal-doc-approval-table,
.formal-doc-body-table {
    border-collapse: collapse;
    width: 100%;
    font-size: var(--text-13);
}

.formal-doc-info-table {
    flex: 1;
}

.formal-doc-approval-table {
    flex: 1.3;
}

.formal-doc-info-table th,
.formal-doc-info-table td,
.formal-doc-approval-table th,
.formal-doc-approval-table td,
.formal-doc-body-table th,
.formal-doc-body-table td {
    border: 1px solid var(--color-border);
    padding: var(--space-8) var(--space-12);
}

.formal-doc-info-table th,
.formal-doc-approval-table th,
.formal-doc-body-table th {
    background: var(--color-bg);
    color: var(--color-text-muted);
    font-weight: 600;
    text-align: left;
    white-space: nowrap;
}

.formal-doc-approval-table th,
.formal-doc-approval-table td {
    text-align: center;
    vertical-align: top;
}

.formal-doc-approver-cell {
    min-width: 84px;
}

.formal-doc-approver-name {
    font-weight: 600;
    margin-bottom: var(--space-4);
}

.formal-doc-approver-date {
    color: var(--color-text-muted);
    font-size: var(--text-11);
    margin-top: var(--space-4);
}

.formal-doc-closing {
    text-align: center;
    margin: var(--space-16) 0 0;
    font-size: var(--text-13);
    white-space: pre-wrap;
}

/* ---- 서식형 결재란 내 결재자 선택(Phase 96, Phase 97에서 경비 정산까지 확장) — 별도
   "결재선 선택" 카드 대신 결재란 표 자체에서 추천/추가/제거를 처리한다. .formal-doc-approval-table
   은 이 박스 안에서 flex item이 아니라 일반 블록으로 쌓이므로 flex:1.3은 래퍼
   (.formal-doc-approval-box)가 대신 가진다. */
.formal-doc-approval-box {
    flex: 1.3;
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
}

.formal-doc-approver-remove {
    margin-left: var(--space-6);
    background: none;
    border: none;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: var(--text-13);
    line-height: 1;
    padding: 0;
}

.formal-doc-approver-remove:hover {
    color: var(--color-error-text);
}

@media (max-width: 640px) {
    .formal-doc-header {
        flex-direction: column;
        margin-bottom: 0;
    }

    .formal-doc-info-table,
    .formal-doc-approval-table,
    .formal-doc-approval-box {
        width: 100%;
    }
}

/* ---- Team Leave Calendar ---- */

.calendar-grid-wrapper {
    overflow-x: auto;
}

.calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(120px, 1fr));
    gap: var(--space-1);
    background: var(--color-border);
    border: 1px solid var(--color-border);
    min-width: 700px;
}

.calendar-weekday {
    background: var(--color-bg);
    padding: var(--space-8);
    text-align: center;
    font-weight: 600;
    font-size: var(--text-13);
    color: var(--color-text-muted);
}

.calendar-cell {
    background: var(--color-surface);
    min-height: 96px;
    padding: var(--space-6);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.calendar-cell.outside-month {
    background: var(--color-bg);
}

/* Week 뷰(P1, 2026-09-11 근무현황/업무이동 백로그) — 7칸뿐이라 Month 뷰(42칸)보다 셀당
   여유 공간이 많아, 더 키워서 같은 날의 여러 상태 칩을 한눈에 보기 쉽게 한다. */
.calendar-cell-week {
    min-height: 160px;
}

.calendar-cell .calendar-date {
    font-size: var(--text-13);
    font-weight: 600;
    color: var(--color-text);
}

/* .calendar-grid는 요일 헤더 7개 + 날짜 셀 42개를 한 grid 컨테이너의 형제로
   그대로 나열하므로(7열 grid), nth-child(7n+1)/(7n)이 헤더·셀 모두에서
   일요일/토요일 열을 그대로 가리킨다. */
.calendar-weekday:nth-child(7n+1),
.calendar-cell:nth-child(7n+1) .calendar-date {
    color: var(--color-error-text);
}

.calendar-weekday:nth-child(7n),
.calendar-cell:nth-child(7n) .calendar-date {
    color: var(--color-primary);
}

.calendar-cell.outside-month .calendar-date {
    color: var(--color-text-muted);
}

.calendar-entry {
    font-size: var(--text-12);
    background: var(--color-info-bg);
    color: var(--color-info-text);
    border-radius: var(--radius-4);
    padding: var(--space-2) var(--space-6);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
}

/* 유형별 chip 색상 구분(Phase 76 백로그) — 색상만으로 상태를 구분하지 않는다는 디자인 원칙에
   따라 chip 텍스트(이름 · 유형 라벨)는 그대로 유지하고 배경/글자색만 유형별로 다르게 준다. */
.calendar-entry-annual {
    background: var(--color-calendar-annual-bg);
    color: var(--color-calendar-annual-text);
}

.calendar-entry-half {
    background: var(--color-calendar-half-bg);
    color: var(--color-calendar-half-text);
}

.calendar-entry-sick {
    background: var(--color-calendar-sick-bg);
    color: var(--color-calendar-sick-text);
}

.calendar-entry-family {
    background: var(--color-calendar-family-bg);
    color: var(--color-calendar-family-text);
}

.calendar-entry-other {
    background: var(--color-calendar-other-bg);
    color: var(--color-calendar-other-text);
}

.calendar-entry-compensatory {
    background: var(--color-calendar-compensatory-bg);
    color: var(--color-calendar-compensatory-text);
}

/* 전사 근무 현황 캘린더(work-calendar) 전용 4개 상태 — 색상만으로 구분하지 않고 텍스트(상태명 +
   건수)를 항상 함께 표시한다. */
.calendar-entry-working {
    background: var(--color-calendar-working-bg);
    color: var(--color-calendar-working-text);
}

.calendar-entry-leave-status {
    background: var(--color-calendar-leave-status-bg);
    color: var(--color-calendar-leave-status-text);
}

.calendar-entry-business-trip {
    background: var(--color-calendar-business-trip-bg);
    color: var(--color-calendar-business-trip-text);
}

/* 팀 외근/출장 캘린더(조직장 View) 전용 2종 — 출장은 위 work-calendar 토큰을 그대로
   재사용(같은 개념), 외근만 새 토큰(local)을 쓴다. */
.calendar-entry-overnight {
    background: var(--color-calendar-business-trip-bg);
    color: var(--color-calendar-business-trip-text);
}

.calendar-entry-local {
    background: var(--color-calendar-local-bg);
    color: var(--color-calendar-local-text);
}

.calendar-entry-holiday {
    background: var(--color-calendar-holiday-bg);
    color: var(--color-calendar-holiday-text);
    font-weight: 600;
    cursor: default;
}

.work-calendar-detail-group {
    margin-bottom: var(--space-16);
}

.work-calendar-detail-group h3 {
    font-size: var(--text-14);
    margin: 0 0 var(--space-8);
}

.work-calendar-detail-employee {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    padding: var(--space-6) 0;
    border-bottom: 1px solid var(--color-border);
}

.work-calendar-detail-employee:last-child {
    border-bottom: none;
}

.work-calendar-detail-name {
    font-weight: 600;
}

.work-calendar-detail-department {
    color: var(--color-text-muted);
    font-size: var(--text-12);
}

.calendar-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-10) var(--space-16);
    padding: var(--space-8) 0 var(--space-4);
    margin: 0;
    border: none;
    font-size: var(--text-12);
    color: var(--color-text-muted);
}

.calendar-legend-item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-5);
}

.calendar-legend-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-2);
}

/* work-calendar(전사 근무 현황) 범례 전용 — calendar-entry-*의 -bg 토큰(배지 배경용
   옅은 색)과 달리, 작은 점으로도 잘 보이도록 더 진한 -text 토큰을 그대로 배경색으로 쓴다. */
.calendar-legend-swatch-working {
    background: var(--color-calendar-working-text);
}

.calendar-legend-swatch-leave-status {
    background: var(--color-calendar-leave-status-text);
}

.calendar-legend-swatch-compensatory {
    background: var(--color-calendar-compensatory-text);
}

.calendar-legend-swatch-business-trip {
    background: var(--color-calendar-business-trip-text);
}

/* ---- Project Gantt Chart (Phase D) ---- */
/* 뷰 전용(읽기 전용) — 편집은 업무 수정 폼에서 한다. 날짜 축은 CSS grid 대신 절대 위치로
   막대/마커를 배치한다(하루 단위로 grid 컬럼을 나누면 프로젝트 기간이 길 때 DOM 노드가
   과도하게 늘어나기 때문). */

.gantt-wrapper {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-8);
    background: var(--color-surface);
    overflow: hidden;
}

.gantt-layout {
    display: flex;
}

.gantt-labels {
    flex: 0 0 220px;
    border-right: 1px solid var(--color-border);
}

.gantt-timeline {
    flex: 1 1 auto;
    overflow-x: auto;
    position: relative;
}

.gantt-label-header,
.gantt-timeline-header {
    height: 28px;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
}

.gantt-label-header {
    display: flex;
    align-items: center;
    padding: 0 var(--space-8);
    font-size: var(--text-12);
    font-weight: 600;
    color: var(--color-text-muted);
}

.gantt-label-row {
    height: 32px;
    display: flex;
    align-items: center;
    padding: 0 var(--space-8);
    font-size: var(--text-13);
    color: var(--color-text);
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gantt-label-milestone {
    color: var(--color-text-muted);
}

.gantt-timeline-header {
    position: relative;
}

.gantt-week-label {
    position: absolute;
    top: 0;
    height: 100%;
    display: flex;
    align-items: center;
    padding-left: var(--space-4);
    border-left: 1px solid var(--color-border);
    font-size: var(--text-12);
    color: var(--color-text-muted);
    white-space: nowrap;
}

.gantt-timeline-body {
    position: relative;
}

.gantt-row {
    position: relative;
    height: 32px;
    border-bottom: 1px solid var(--color-border);
}

.gantt-bar {
    position: absolute;
    top: 6px;
    height: 20px;
    border-radius: var(--radius-4);
    background: var(--color-text-muted);
}

.gantt-bar-in-progress {
    background: var(--color-info-text);
}

.gantt-bar-done {
    background: var(--color-success-text);
}

.gantt-marker {
    position: absolute;
    top: 8px;
    width: 10px;
    height: 10px;
    transform: rotate(45deg);
    background: var(--color-text-muted);
}

.gantt-marker-task {
    background: var(--color-warning-text);
}

.gantt-marker-milestone {
    background: var(--color-primary);
}

.gantt-marker-milestone.achieved {
    background: var(--color-success-text);
}

.gantt-today-line {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--color-error-text);
}

/* ---- Gantt 후속: 드래그 편집 + 선후행 관계 화살표 ---- */

.gantt-bar-draggable {
    cursor: grab;
}

.gantt-bar-draggable.dragging {
    cursor: grabbing;
    opacity: 0.75;
}

.gantt-bar-handle {
    position: absolute;
    top: 0;
    width: 6px;
    height: 100%;
    cursor: ew-resize;
}

.gantt-bar-handle-start {
    left: 0;
}

.gantt-bar-handle-end {
    right: 0;
}

.gantt-dependency-overlay {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
}

.gantt-dependency-arrow {
    fill: none;
    stroke: var(--color-text-muted);
    stroke-width: 1.5;
}

.gantt-arrowhead-fill {
    fill: var(--color-text-muted);
}

/* ---- Working Time Policy — Rule Builder 조건 그룹 트리 ---- */

.condition-group {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-6);
    padding: var(--space-14);
    margin-bottom: var(--space-12);
    background: var(--color-bg);
}

.condition-group .child-groups {
    margin-left: var(--space-20);
    padding-left: var(--space-14);
    border-left: 2px solid var(--color-border);
    margin-top: var(--space-10);
}

.condition-group-header {
    display: flex;
    align-items: center;
    gap: var(--space-10);
    margin-bottom: var(--space-10);
}

.condition-group-header select {
    font-weight: 600;
    width: auto;
}

.condition-row {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    margin-bottom: var(--space-8);
    flex-wrap: wrap;
}

.condition-row select,
.condition-row input {
    width: auto;
}

.condition-row .condition-value {
    min-width: 140px;
    flex: 1 1 140px;
}

.condition-group-footer {
    display: flex;
    gap: var(--space-8);
    margin-top: var(--space-8);
}

/* Context Help(P1, help-context.js) — 화면 헤더 옆 "?" 위젯. 매칭되는 PUBLISHED
   도움말 문서가 있을 때만 렌더링된다(help-context.js가 빈 결과면 아무것도 그리지
   않음). */
.help-context-wrap {
    position: relative;
    display: inline-block;
    margin-left: var(--space-8);
    vertical-align: middle;
}

.help-context-btn {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-text-muted);
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.help-context-btn:hover,
.help-context-btn:focus-visible {
    background: var(--color-info-bg);
    color: var(--color-info-text);
    border-color: var(--color-primary);
}

.help-context-popover {
    position: absolute;
    top: 28px;
    left: 0;
    z-index: var(--z-tooltip);
    width: 260px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-8);
    box-shadow: var(--shadow-md);
    padding: var(--space-12);
    font-size: 13px;
}

.help-context-popover-title {
    font-weight: 700;
    margin-bottom: var(--space-4);
    color: var(--color-text);
}

.help-context-popover-summary {
    color: var(--color-text-muted);
    margin-bottom: var(--space-8);
}

.help-context-popover-link {
    font-weight: 600;
}

/* Tag(P1, help-article-form.js) 삭제 버튼 — .badge 안에 인라인으로 들어간다. */
.tag-remove {
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    padding: 0 0 0 var(--space-4);
    font-size: inherit;
    line-height: 1;
}

/* OCR-6 — expense-reports-detail.js/approvals-detail.js가 공유하는 OCR 검토 패널.
   .card를 그대로 재사용하고, 후보 그룹/원문 표시용 최소 스타일만 추가한다. */
.ocr-review-panel h3 {
    margin-top: 0;
    font-size: 14px;
}

.ocr-field-group {
    border-top: 1px solid var(--color-border);
    padding: var(--space-8) 0;
}

.ocr-field-group:first-of-type {
    border-top: none;
}

.ocr-candidate {
    cursor: pointer;
}

.ocr-raw-text {
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 240px;
    overflow-y: auto;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-4);
    padding: var(--space-8);
    font-size: 12px;
}

/* 차량운행일지 조회/출력(2026-09-17 신규, 9-A-9 브라우저 인쇄) — 화면에서는 숨기고
   인쇄에서만 보이는 문서 머리말. 프로젝트 전체에서 재사용 가능한 일반 유틸리티라
   특정 화면 이름을 붙이지 않았다. */
.print-only {
    display: none;
}

@media print {
    .no-print,
    .app-header,
    .app-sidebar,
    .mobile-tabbar,
    .mobile-sheet-backdrop {
        display: none !important;
    }
    .print-only {
        display: block;
    }
    .app-shell {
        display: block;
    }
    .app-content {
        margin: 0;
        padding: 0;
        width: 100%;
    }
    body {
        background: #fff;
    }
}

.usage-log-print-header {
    margin-bottom: var(--space-16);
}

.usage-log-print-header h1 {
    margin-bottom: var(--space-4);
}
