/* ======================================================================
   cgrid.css — 관리자 목록 그리드 키트 스타일 (colon-grid 정본)

   여기에는 **그리드가 쓰는 것만** 둔다. 사이드바·로그인·대시보드처럼
   앱마다 다른 껍데기는 app.css 로 나갔다 — 섞여 있으면 키트만 떼어낼 수 없다.

   ⚠️ Tailwind 없이도 표가 제대로 보이도록 정렬/굵기 유틸을 자체 정의한다(맨 아래).
      Tailwind 가 있는 프로젝트에서는 같은 뜻이라 충돌하지 않는다.
   ====================================================================== */
/* ============================================================
   기반 토큰 — 출처: postring admin.css (canonical)
   기본 테마는 블랙앤화이트다. **쓰는 쪽에서 덮어쓰라고 열어 둔 값들**이며,
   키트 규칙은 이 토큰만 참조한다(색을 직접 박지 않는다).

   쓰는 쪽 예:
     :root { --primary: #2563eb; --gk-row-hover-bg: #eff6ff; --gk-row-hover-fg: inherit; }
   ============================================================ */
:root {
    --primary: #111827;          /* 블랙(주 강조) */
    --primary-dark: #000000;
    --primary-light: #f3f4f6;    /* 연한 회색(호버/선택 배경) */
    --primary-border: #d1d5db;
    --secondary: #64748b;
    --success: #10b981;
    --danger: #ef4444;
    --warning: #f59e0b;
    --bg: #f1f5f9;
    --card-bg: #ffffff;
    --text-primary: #1e293b;
    --text-secondary: #64748b;
    --border: #e2e8f0;
    --control-border: #f0f0f0;   /* 폼 컨트롤·버튼 공통 보더(연한 회색) — 모든 인풋/셀렉트/버튼 통일 */
    --radius: 12px;
    --radius-sm: 8px;
    --gk-combo-min: 7rem;        /* 콤보(셀렉트) 최소 폭 — 화면마다 필터 시작 위치가 들쭉날쭉하지 않게 */
    --gk-search-btn-min: 26px;   /* 검색카드 초기화/조회의 최소 높이 — 필터를 따라 줄되 여기서 멈춘다 (§2.2) */

    /* ----- 그리드 테마 (쓰는 쪽에서 덮어쓰는 지점) -----
       기본값은 위 기반 토큰에서 파생된다 → --primary 만 바꿔도 전체가 따라온다. */
    --gk-head-bg:        #f8fafc;   /* 헤더 배경 */
    --gk-row-hover-bg:   var(--primary);   /* 행 마우스오버 배경 */
    --gk-row-hover-fg:   #ffffff;          /* 행 마우스오버 글자 */
    --gk-row-edit-hover-bg: #f8fafc;       /* 수정모드에서는 반전하지 않는다 */
    --gk-row-opened-bg:  color-mix(in srgb, var(--primary) 12%, #fff);   /* 열어본 행 — 테마색의 연한 버전(노랑 고정값이 아니라 테마를 따라간다) */
    --gk-sel-bg:         rgba(17, 24, 39, .07);   /* 범위 선택 배경 */
    --gk-sel-fg:         var(--text-primary);     /* 범위 선택 글자 — hover 반전을 되돌린다 */
    --gk-sel-edge:       var(--primary);          /* 선택 테두리 */
    --gk-fill-bg:        rgba(245, 158, 11, .13); /* 채우기 미리보기 */
    --gk-fill-edge:      #f59e0b;
    --gk-zoom-base:      #94a3b8;   /* 셀 안 돋보기 — 기본 */
    --gk-zoom-invert:    #ffffff;   /* 행이 반전됐을 때 */
    --gk-editing-bg:     #fffbeb;   /* 수정모드 도구 그룹 */
    --gk-editing-border: #fcd34d;
}

/* ---------- 로딩 FOUC 방지: 향상(admin-grid.js) 전 원본 숨김 + 자리 확보 ---------- */
table[data-grid]:not(.grid-ready) { visibility: hidden; }          /* 향상 전 원본 테이블 숨김 */
[data-grid-actions] { display: none; }                              /* JS가 툴바로 이전 후 노출 */
.bg-white:has(> table[data-grid]:not(.grid-ready)),
.bg-white:has([data-grid-actions]) { position: relative; min-height: 360px; }   /* 그리드 영역 자리 확보 */
.bg-white:has(> table[data-grid]:not(.grid-ready))::after {        /* 로딩 shimmer 플레이스홀더 */
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    background: linear-gradient(100deg, #eef2f7 30%, #f7fafc 50%, #eef2f7 70%);
    background-size: 200% 100%; animation: gridShimmer 1.25s linear infinite;
}
@keyframes gridShimmer { to { background-position: -200% 0; } }
/* 상세/수정 모달이 목록 위로 함께 로드될 때: 목록은 배경이므로 FOUC shimmer·자리확보 생략(모달 뒤 스켈레톤 깜빡임 방지) */
body:has([data-admin-modal]) .bg-white:has(> table[data-grid]:not(.grid-ready))::after { content: none !important; animation: none !important; }
body:has([data-admin-modal]) .bg-white:has(> table[data-grid]:not(.grid-ready)),
body:has([data-admin-modal]) .bg-white:has([data-grid-actions]) { min-height: 0 !important; }

/* ---------- 체크박스(진담) ---------- */
input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: var(--primary);
    cursor: pointer;
    vertical-align: middle;
}

/* ---------- 검색/필터 영역 (진담 filter-bar) ---------- */
/* 검색카드 — 예전엔 Tailwind 유틸로 만들던 껍데기. 키트가 자립하도록 여기서 정의한다. */
.admin-search-card { display: flex; gap: 16px; align-items: stretch; padding: 16px; margin-bottom: 16px; }
.filter-bar { display: flex; align-items: flex-end; gap: 10px 24px; flex-wrap: wrap; flex: 1 1 auto; }   /* 가로 24px — 필터끼리 붙어 보이지 않게 */
.filter-group { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 6px; flex: 0 0 auto; }
.filter-group > label { font-size: 13px; color: var(--text-primary); font-weight: 600; margin: 0; white-space: nowrap;
    display: inline-flex; align-items: center; gap: 6px; }
/* 라벨 앞 세로 바 — 필터가 여럿이면 어디부터 어디까지가 한 칸인지 안 읽힌다.
   전체건수(.grid-count)와 같은 표식을 작게 쓴다 → 같은 언어로 읽힌다. */
.filter-group > label::before {
    content: ""; flex: 0 0 auto; display: inline-block;
    width: 3px; height: 12px; border-radius: 2px; background: var(--primary);
}
/* 검색카드 버튼열 — 필터 높이를 따라가되 **바닥이 있다**.
   두 버튼은 세로 영역을 반씩 나눠 쓴다(§2.2). 다만 min-height:0 이면 필터가 한 줄일 때
   버튼이 25px 까지 눌려 글자가 상자에 끼인다 → 최소 높이를 둬서 거기서 멈춘다.
   바닥에 걸리면 카드가 그만큼(1줄 기준 +10px) 커진다 — 버튼이 못 읽히는 것보다 낫다는 판단. */
/* 버튼 사이 간격도 좁힌다 — 바닥값과 합쳐 **한 줄 필터(58px) 안에 딱 들어가** 카드를 안 민다
   (26+4+26 = 56 ≤ 58). 필터가 두 줄 이상이면 어차피 바닥에 안 닿으니 아무 영향이 없다. */
.admin-search-card .admin-search-actions { gap: 4px; }
.admin-search-card .admin-search-actions .btn {
    min-height: var(--gk-search-btn-min);
    /* 크기는 **키트가 정한다**. 앱이 자기 `.btn-primary { padding: 9px 18px }` 로 덮으면
       같은 키트인데 검색 버튼만 7px 커졌다(velysound 실측: 카드 116 vs colon-grid 102).
       여기서 다시 못 박아 어느 앱에 얹혀도 검색카드 버튼은 같은 크기가 된다. */
    padding: 0 14px;
    font-size: 13px;
    /* 보더도 마찬가지 — 앱 `.btn-sm { border: none }`(velysound admin.css 계열 포크 3곳)에 눌려
       초기화/조회가 **테두리 없는 맨 글자**로 보였다(§2.3 컨트롤 통일 위반).
       앱이 색을 바꾸고 싶으면 같은 자리(0,3,0)에서 덮으면 된다 — velysound 가 그렇게 한다. */
    border-width: 1.5px;
    border-style: solid;
    border-color: var(--control-border);   /* `border:none` 은 색까지 currentColor 로 되돌린다 → 검은 테두리가 된다 */
}
.filter-row { display: inline-flex; align-items: center; gap: 6px; }
.filter-input {
    border: 1.5px solid var(--control-border);
    border-radius: var(--radius-sm);
    padding: 0 0.6rem;
    height: 34px;                 /* 툴바/검색 컨트롤 공통 높이 */
    font-size: 13px;
    line-height: 1.4;
    outline: none;
    background-color: #fff;
    color: var(--text-primary);
    transition: border-color .15s, box-shadow .15s;
}
.filter-input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(17, 24, 39, .12); }
/* 모달 폼 컨트롤(인풋/셀렉트/텍스트영역) 보더 = 짙은 회색 통일 (콤보 input 제외 — filter-input로 이미 통일) */
[data-admin-modal] input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not(.combobox-input),
[data-admin-modal] textarea { border-color: var(--control-border); }
/* 모달 폼 컨트롤 포커스 = 검색창과 동일 통일 */
[data-admin-modal] input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):focus,
[data-admin-modal] textarea:focus,
[data-admin-modal] select:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(17, 24, 39, .12); outline: none; }
select.filter-input {
    min-width: var(--gk-combo-min);   /* 콤보로 안 바뀐 네이티브 셀렉트도 같은 바닥을 쓴다 */
    -webkit-appearance: none; -moz-appearance: none; appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 10px center; background-size: 13px 9px; padding-right: 30px;
}

/* ---------- 버튼 (진담 btn) ---------- */
.btn {
    border-radius: var(--radius-sm); font-size: 13px; font-weight: 600; padding: 8px 16px;
    transition: all .15s; border: 1.5px solid var(--control-border); cursor: pointer;   /* 공통 보더(짙은 회색) */
    display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;   /* 좁은 폭에서 글자 세로 쪼개짐 방지(§2.12) */
    user-select: none;   /* 그리드 드래그 중 버튼 글자가 파랗게 잡히지 않게 */
}
.btn svg { flex-shrink: 0; }
/* hover = 색상반전(검흰↔흰검, 파흰↔흰파)으로 통일 — 그림자 없음 */
/* 기본 버튼 = 흰 바탕·검은 글씨. 보더는 전부 짙은 회색(--control-border) 통일. hover 시 색상반전 */
.btn-primary { background: #fff; border-color: var(--control-border); color: var(--primary); }
.btn-primary:hover { background: var(--primary); color: #fff; border-color: var(--primary); }   /* hover 반전: 흰검 → 검흰 */
.btn-light { background: #fff; border-color: var(--control-border); color: var(--text-primary); }
.btn-light:hover { background: var(--primary); color: #fff; border-color: var(--primary); }       /* 흰검 → 검흰 */
.btn-outline { background: #fff; border-color: var(--control-border); color: var(--primary); }   /* §2.3 초기화 */
.btn-outline:hover { background: var(--primary); color: #fff; border-color: var(--primary); }     /* 흰검 → 검흰 */
.btn-danger-outline { background: #fff; border-color: var(--control-border); color: var(--danger); }
.btn-danger-outline:hover { background: var(--danger); color: #fff; border-color: var(--danger); } /* 흰빨 → 빨흰 */
.btn-sm { padding: 0 14px; height: 34px; font-size: 13px; }   /* 셀렉트/툴바 컨트롤과 높이 통일 */

/* ---------- 데이터 테이블 (진담 data-table) ---------- */
/* 가로 오버플로우(컬럼 합이 넓어질 때)는 카드 안에서 스크롤 — 밖으로 안 나가게 */
.table-wrap { overflow-x: auto; }
/* §2.5 table-layout: auto — 고정폭이 텍스트 컬럼을 뭉개지 않게 내용 사이징(컬럼 리사이즈는 드래그 시 px 고정으로 동작). */
.data-table { width: 100%; border-collapse: collapse; table-layout: auto; }
.data-table thead th { text-align: center; }   /* §450 헤더 전부 중앙정렬 */
.data-table th {
    background: #f8fafc !important; border-bottom: 2px solid var(--border) !important;
    border-right: 1px solid var(--border);   /* 컬럼 구분선(리사이즈 위치 가시화) */
    padding: 10px 14px !important; font-size: 12px !important; font-weight: 600 !important;
    color: var(--text-secondary) !important; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; position: relative;
}
.data-table td {
    padding: 11px 14px !important; border-bottom: 1px solid var(--border) !important;
    border-right: 1px solid var(--border);   /* 컬럼 구분선 */
    font-size: 13px; color: var(--text-primary); vertical-align: middle;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;   /* 넘치면 ... */
}
.data-table th:last-child, .data-table td:last-child { border-right: none; }
.data-table td.seq-cell { font-size: 11px; color: #64748b; }   /* §2.10 순번=보조정보(작은 폰트+흐린 slate-500, 데이터처럼 안 읽히게) */
.data-table td .truncate, .data-table td > div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 컬럼 너비 조절 핸들(임시) — 우측 경계 위 */
.col-resizer { position: absolute; top: 0; right: -1px; width: 7px; height: 100%; cursor: col-resize; user-select: none; z-index: 2; }
.col-resizer:hover { background: var(--primary); opacity: .5; }
/* 컬럼 위치 변경(임시, 드래그) 피드백 */
.data-table th.col-dragging { opacity: .5; }
.data-table th.col-dragover { box-shadow: inset 3px 0 0 0 var(--primary); }
/* 빈 상태(데이터 0건) — 고정 높이 200px. (.data-table td 의 padding !important 를 이기려 특정도↑) */
.data-table td.grid-empty-cell { height: 200px; padding: 0 !important; text-align: center; vertical-align: middle; color: var(--text-secondary); font-size: 14px; border-right: none !important; }
.data-table tbody tr:last-child td { border-bottom: none !important; }
.data-table tbody tr:hover td { background: var(--primary-light); }
.data-table tbody tr.row-clickable { cursor: pointer; }
/* 열어본 행 — 상세를 열었던 행을 닫은 뒤에도 가리킨다(§2.5).
   ⚠️ hover 규칙보다 **먼저** 와야 한다. 뒤에 두면 마우스를 올려도 이 색이 이겨 반전이 죽는다. */
.data-table tbody tr.row-opened > td { background: var(--gk-row-opened-bg); }
/* 로우 hover = 색상반전(흰 배경 → 검정, 글자 흰색). 배지(bg-* 보유)는 자체 색 유지 */
.data-table tbody tr.row-clickable:hover td { background: var(--gk-row-hover-bg); color: var(--gk-row-hover-fg); }
/* ⚠️ 드래그로 범위를 고르는 동안에는 행 반전을 끈다.
   반전(어두운 배경 + 흰 글자)과 선택(밝은 배경)이 겹치면 **흰 글자 + 밝은 배경**이 되어 글자가 사라진다.
   선택은 셀 단위인데 반전은 행 단위라, 같은 행의 고른 칸과 안 고른 칸이 서로 다른 색이 되는 문제도 있다. */
body.grid-dragging .data-table tbody tr.row-clickable:hover td { background: inherit; color: inherit; }
/* 드래그가 끝나도 **선택이 남아 있는 동안**에는 마찬가지다 — 반전이 켜지면 고른 범위가 묻힌다.
   (엑셀도 범위를 고른 상태에서 행 강조를 하지 않는다) */
.data-table:has(td.cell-sel) tbody tr.row-clickable:hover td { background: inherit; color: inherit; }

/* 필수 열 표시 — 선언만 하면 헤더에 점이 붙는다(화면마다 별표를 손으로 달지 않게) */
/* 필수 표시 = 헤더 **왼쪽**의 빨간 별표. 수정모드에서만 보인다(보기만 할 땐 입력 얘기가 불필요) */
.data-table.grid-edit-mode th[data-required]::before {
    content: '*'; color: var(--danger); font-weight: 700;
    margin-right: 3px; line-height: 1;
}

/* ---------- 정렬 헤더 (다중정렬: 1개=화살표만, 2개+=순서 숫자) ---------- */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--primary) !important; }
.sort-ind { display: inline-flex; align-items: center; gap: 2px; margin-left: 4px; vertical-align: middle; }
.sort-ind .sort-ar { font-size: 10px; opacity: .3; }
th.sortable.sorted .sort-ind .sort-ar { opacity: 1; color: var(--primary); }
.sort-ind .sort-pri {
    display: none; min-width: 15px; height: 15px; line-height: 15px; text-align: center;
    font-size: 9px; font-weight: 700; color: #fff; background: var(--primary); border-radius: 9999px; padding: 0 3px;
}
.sort-ind .sort-pri:not(:empty) { display: inline-block; }   /* 번호 있을 때(2개+)만 표시 */

/* 전체건수 (진담: 세로 바 + "전체 N건") */
.grid-count { font-size: 13px; color: var(--text-primary); display: inline-flex; align-items: center; gap: 8px; }
.grid-count::before { content: ""; width: 5px; height: 16px; border-radius: 2px; background: var(--primary); display: inline-block; }
.grid-count b { color: var(--primary); }

/* ---------- 페이징 (진담 pagination) ---------- */
.grid-pager-area { margin-top: 16px; }   /* 그리드 카드와 별개 하위 영역 */
.pagination-wrapper { display: flex; justify-content: center; align-items: center; gap: 6px; }
.grid-page-status { margin-left: 10px; font-size: 13px; color: var(--text-secondary); }
.page-gap { padding: 0 2px; color: var(--text-secondary); font-size: 13px; user-select: none; align-self: center; }

/* 컬럼 표시/숨김 팝오버(§2.11) — 버튼 기준 툴팁식 팝업(모달 아님) */
.col-pop { position: fixed; z-index: 250; width: 300px; max-width: calc(100vw - 16px); padding: 12px;
    background: #fff; border: 1px solid var(--control-border); border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, .16); }
.col-pop-title { font-size: 12px; font-weight: 700; color: var(--text-secondary); margin-bottom: 8px; }
.col-pop-list { display: flex; flex-wrap: wrap; gap: 6px; }
.col-badge { font-size: 12px; font-weight: 600; padding: 5px 11px; border-radius: 999px; cursor: pointer; transition: all .12s;
    background: var(--primary); border: 1px solid var(--primary); color: #fff; }                 /* 활성(표시) */
.col-badge:hover { opacity: .85; }
.col-badge.off { background: #f1f5f9; border-color: var(--border); color: #94a3b8; }            /* 비활성(숨김) */
.page-btn {
    min-width: 34px; height: 34px; padding: 0 8px; border: 1.5px solid var(--control-border); border-radius: var(--radius-sm);
    background: #f1f5f9; display: inline-flex; align-items: center; justify-content: center; font-size: 13px;
    cursor: pointer; color: var(--text-primary); transition: all .15s;
}
.page-btn:hover { background: var(--primary); border-color: var(--primary); color: #fff; }   /* 색상반전 통일 */
.page-btn.active { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.page-btn:disabled { opacity: .45; cursor: not-allowed; }

/* ---------- 로딩 스켈레톤 ---------- */
@keyframes skel-shimmer { 0% { background-position: -180px 0; } 100% { background-position: calc(180px + 100%) 0; } }
.skeleton-bar {
    display: inline-block; height: 12px; border-radius: 6px; width: 100%;
    background: #e9edf2;
    background-image: linear-gradient(90deg, #e9edf2 0, #f5f7fa 60px, #e9edf2 120px);
    background-size: 180px 100%; background-repeat: no-repeat;
    animation: skel-shimmer 1.15s ease-in-out infinite;
}
.grid-skeleton td { padding: 11px 14px !important; border-bottom: 1px solid var(--border) !important; }

/* ---------- 콤보박스 (input + dropdown, 전체목록 노출) ---------- */
.combobox { position: relative; display: inline-flex; align-items: center; }
.combobox.cb-block { display: flex; width: 100%; }
.combobox.cb-block .combobox-input { flex: 1 1 auto; width: 100%; }
.combobox.cb-compact { width: 4.5rem; flex: 0 0 auto; }   /* 페이지당 개수 — 숫자 네 자리 + 화살표에 맞춘 폭 */
.combobox.cb-compact .combobox-input { width: 100%; }
.combobox-input {
    cursor: pointer; padding-right: 30px !important;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 10px center; background-size: 13px 9px;
}
.combobox.combobox-open .combobox-input { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(17, 24, 39, .12); outline: none; }
.combobox-dropdown {
    display: none; position: absolute; top: calc(100% + 4px); left: 0; right: 0;
    /* 목록 폭 = **트리거와 항상 같다.** 트리거 쪽이 이미 `max(--gk-combo-min, 가장 긴 항목)` 으로
       잡히므로(cgrid-combobox.js syncWidth) 여기서 따로 늘릴 필요가 없다.
       ⚠️ 한쪽만 max-content 로 두면 둘이 어긋난다 — 폭은 **한 곳에서만** 정한다. */
    width: auto; min-width: 100%; box-sizing: border-box;   /* 트리거와 폭이 1px 도 어긋나지 않게 */
    background: #fff; border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, .10);
    max-height: 280px; overflow-y: auto; z-index: 1100; padding: 4px 0;
}
.combobox.combobox-open .combobox-dropdown { display: block; }
.combobox-item { padding: 8px 12px; font-size: 14px; cursor: pointer; color: #334155; white-space: nowrap; line-height: 1.4; }
.combobox-item:hover, .combobox-item.combobox-item-active { background: var(--primary-light); }
.combobox-item.combobox-item-selected { background: rgba(17, 24, 39, .08); color: var(--primary); font-weight: 600; }
.combobox-empty { padding: 10px 12px; font-size: 13px; color: #94a3b8; text-align: center; }

/* ---------- 모달 ---------- */
body.modal-open { overflow: hidden; }

/* 모달 푸터 버튼 정렬 — **오른쪽부터, 삭제만 왼쪽**(ADMIN_PAGE_RULE §1.4).
   화면마다 푸터 div 에 flex/ml-auto 를 직접 주다 보니 어떤 팝업은 버튼이 왼쪽에 붙었다
   (선택 팝업 실측). 키트가 정렬을 맡고 화면은 버튼만 나열한다. */
.cg-modal-footer { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.cg-modal-footer.hidden { display: none; }
/* 삭제는 되돌릴 수 없는 액션이라 **저장/취소와 멀리** 둔다 — 손이 미끄러져 눌리지 않게 */
.cg-modal-footer [data-modal-delete],
.cg-modal-footer .btn-danger-outline,
.cg-modal-footer .btn-danger { margin-right: auto; }

/* 넓은 모달 — 컨텐츠에 data-modal-wide 가 있으면 화면 폭에 가깝게 넓힌다(긴 텍스트 편집 화면).
   Tailwind 의 max-w-3xl(0,1,0)을 이기도록 특정도를 올린다. */
.cg-modal-overlay > .cg-modal-wide { max-width: 90vw; }

/* 모달 로딩 — 클릭 즉시 열고 내용은 뒤따라 채운다(빈 시간에 사용자가 또 누르지 않게) */
.cg-modal-skel { display: flex; flex-direction: column; gap: 13px; min-height: 150px; padding: 2px 0; }
.cg-modal-skel .skeleton-bar:first-child { margin-bottom: 5px; }

/* ============================================================
   §2.12 반응형 / 모바일 (≤768px) — 공통 CSS가 자동 처리(화면별 마크업 X)
   ============================================================ */

@media (max-width: 768px) {
    /* ----- 그리드 = 카드형(행=카드, 라벨 좌 / 값 우) ----- */
    /* 그리드 카드 래퍼는 투명하게 → 카드 사이 간격에 흰 배경 안 보이고 페이지 배경 노출 */
    .bg-white:has(> .table-wrap) { background: transparent !important; border: none !important; border-radius: 0 !important; overflow: visible !important; }
    .data-table, .data-table tbody, .data-table tr { display: block; width: 100%; }
    .data-table thead { display: none; }
    .data-table tbody tr { border: 1px solid var(--border); border-radius: 10px; margin-bottom: 10px; overflow: hidden; background: #fff; }
    .data-table td {
        display: flex; justify-content: flex-end; align-items: center; gap: 6px;     /* 값(들)은 우측에 모음 */
        width: auto !important; max-width: none;
        text-align: right !important;                                  /* 값 우측(컬럼 inline align 덮기) */
        white-space: normal; overflow: visible; text-overflow: clip;
        border: none; border-bottom: 1px solid var(--border); padding: 9px 14px;
    }
    .data-table td:last-child { border-bottom: none; }
    .data-table td::before {
        content: attr(data-label); flex: 0 0 auto; margin-right: auto;             /* 라벨은 좌측으로 밀고 값은 우측 그룹 */
        font-size: 12px; font-weight: 600; color: var(--text-secondary); text-align: left; white-space: nowrap;
    }
    .data-table td:empty { display: none; }                            /* 값 없는 셀은 카드에서 숨김 */
    .data-table td:has(img) { justify-content: flex-end; }
    .data-table td.grid-empty-cell { display: flex !important; align-items: center; justify-content: center; height: 100px; min-height: 100px; text-align: center !important; }   /* 빈상태 모바일=100px·세로중앙 */
    .data-table td.grid-empty-cell::before { content: none; }
    .data-table .col-resizer { display: none; }
    .table-wrap { overflow-x: visible; }
    .data-table tbody tr.row-clickable:hover td { background: transparent; color: inherit; }   /* 카드에선 로우 반전 완화 */

    /* ----- 검색영역 세로화 ----- */
    .admin-search-card { flex-direction: column; }
    .filter-bar { flex-direction: column; align-items: stretch; }
    .filter-group { width: 100%; }
    .filter-row { width: 100% !important; }                            /* 검색유형(7rem 고정)+검색값(흡수) 한 줄 유지 */
    .admin-search-actions { flex-direction: row !important; align-self: auto; }   /* 초기화/조회 가로 한 줄(둘 다 flex:1) */

}

/* ---------- 기간(부터~까지) 필터 (admin-grid.js data-range) ---------- */
.filter-dash { color: var(--text-secondary); font-size: 12px; padding: 0 2px; user-select: none; }
.filter-row > input[type="month"], .filter-row > input[type="date"] { width: 9.5rem; }

/* ---------- 그리드 합계행 (admin-grid.js data-sum) ---------- */
.data-table tfoot .grid-foot-row td {
    background: #f8fafc !important; border-top: 2px solid var(--border) !important; border-bottom: none !important;
    font-size: 13px; font-weight: 700; color: var(--primary); text-align: right; white-space: nowrap;
}
.data-table tfoot .grid-foot-row td.foot-label { text-align: center; font-size: 11px; color: var(--text-secondary); }
.data-table tbody tr:last-child td { border-bottom: 1px solid var(--border) !important; }   /* 합계행이 붙으므로 마지막 행 선 유지 */

/* ---------- 콤보 자유입력(creatable) 항목 ---------- */
.combobox-item-create { color: var(--primary); font-weight: 600; border-top: 1px solid var(--border); }

/* ---------- 폼 필드 (molecule: 라벨 + 컨트롤) — 모달 폼 공통 ---------- */
.form-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 16px; }
/* 폼 소제목 — 항목이 열 개를 넘으면 한 덩어리로 읽히지 않는다. **묶어서 읽는 단위**를 만든다.
   한 줄을 통째로 차지하는 **영역 띠**다 — 옅은 배경 + 왼쪽 굵은 막대. 제목 뒤에 얇은 선을 끌던
   방식은 표·카드 테두리와 같은 색이라 '구간 머리'로 안 읽혔다(선 하나 더로 보였다). */
.form-grid .form-section {
    grid-column: 1 / -1;
    display: flex; align-items: center; gap: 8px;
    margin: 16px 0 2px;
    padding: 7px 12px;
    background: var(--primary-light);
    border-left: 4px solid var(--primary);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: 14px; font-weight: 700; color: var(--text-primary); letter-spacing: .02em;
}
.form-grid .form-section:first-child { margin-top: 0; }
.form-grid .col-2 { grid-column: span 2; }
.form-grid .col-full { grid-column: 1 / -1; }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* footgun(§2.4): 콤보를 <label> 안에 넣으면 항목 클릭이 label 기본동작으로 드롭다운을 다시 연다 → 라벨은 형제 span 으로 */
.field-label { font-size: 13px; font-weight: 600; color: var(--text-primary); white-space: nowrap;
    display: inline-flex; align-items: center; gap: 6px; }
/* 라벨 앞 세로 바 — 검색카드 라벨(.filter-group > label)·전체건수와 **같은 표식**을 쓴다.
   폼과 검색카드가 다른 언어로 보이면 같은 화면인데 두 벌처럼 읽힌다. */
.field-label::before {
    content: ""; flex: 0 0 auto; display: inline-block;
    width: 3px; height: 12px; border-radius: 2px; background: var(--primary);
}
.field-label .req { color: var(--danger); margin-left: 2px; }
/* 필드 안 컨트롤은 **칸을 다 쓴다** — 콤보(.combobox)는 기본이 inline-flex(내용 폭)라
   같은 줄의 인풋보다 좁게 서서 폼이 들쭉날쭉해 보였다. 필드 안에서는 블록으로 편다. */
.field .filter-input, .field textarea, .field select, .field .combobox { width: 100%; }
.field .combobox { display: flex; }
.field .combobox .combobox-input { flex: 1 1 auto; width: 100%; }
.field textarea { padding: 8px 10px; height: auto; min-height: 72px; resize: vertical; font-family: inherit; }
.field-hint { font-size: 11px; color: var(--text-secondary); }
/* 자동계산 결과 칸 — 읽기전용임을 색으로 알린다(수정하려면 직접 입력 가능) */
.field .calc-out { background: #f8fafc; font-weight: 600; }
.form-error { background: #fef2f2; border: 1px solid #fecaca; color: #b91c1c; font-size: 13px; padding: 9px 12px; border-radius: 8px; }
/* .form-grid 밖에 놓인 소제목도 같은 띠로 보이게(폼이 두 벌처럼 읽히지 않도록) */
.form-section {
    padding: 7px 12px; margin: 16px 0 2px;
    background: var(--primary-light); border-left: 4px solid var(--primary);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: 14px; font-weight: 700; color: var(--text-primary); letter-spacing: .02em;
}

@media (max-width: 768px) {
    .form-grid { grid-template-columns: 1fr; }
    .form-grid .col-2, .form-grid .col-full { grid-column: auto; }
}

/* ---------- 상태 배지 ---------- */
.badge { display: inline-block; font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; white-space: nowrap; }
.badge-done { background: #111827; color: #fff; }
.badge-wait { background: #f1f5f9; color: #64748b; }
.badge-mid { background: #e2e8f0; color: #1e293b; }

/* ---------- 그리드 수정모드 (ADMIN_PAGE_RULE §2.15) ---------- */
/* 체크박스 컬럼: 항상 존재하고 수정모드에서만 보인다(컬럼 수를 흔들지 않으려고) */
.data-table .col-check { display: none; width: 40px; }
.data-table.grid-edit-mode .col-check { display: table-cell; }
.data-table.grid-edit-mode { --edit-accent: #111827; }

/* 수정모드에선 행 클릭(상세 모달)이 막히므로 커서/호버 반전도 끈다 */
.data-table.grid-edit-mode tbody tr.row-clickable { cursor: default; }
.data-table.grid-edit-mode tbody tr.row-clickable:hover td { background: var(--gk-row-edit-hover-bg); color: inherit; }

/* 편집 가능한 칸 — 수정모드에서만 점선으로 알린다(보기모드에선 아무 표시 없음) */
.data-table.grid-edit-mode td.cell-editable { cursor: text; position: relative; }
/* 편집 가능 표식 = **헤더에 컬럼 유형 아이콘**.
   연필 하나로 통일하면 "고칠 수 있다"만 알 뿐, 무엇으로 고치는지(텍스트/숫자/선택/날짜) 를 모른다.
   셀마다 찍지 않는 이유: 표 전체가 표식으로 뒤덮여 정작 데이터가 안 읽힌다. */
.data-table.grid-edit-mode th.col-editable::before {
    content: ""; display: inline-block; width: 11px; height: 11px; margin-right: 5px;
    vertical-align: middle; opacity: .6; background-color: currentColor;
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
}
.data-table.grid-edit-mode th.col-editable[data-edit="text"]::before {
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 6h14'/%3E%3Cpath d='M12 6v13'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 6h14'/%3E%3Cpath d='M12 6v13'/%3E%3C/svg%3E");
}
.data-table.grid-edit-mode th.col-editable[data-edit="number"]::before {
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 4l-1.5 16M16.5 4L15 20M4 9.5h16M3.5 15h16'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 4l-1.5 16M16.5 4L15 20M4 9.5h16M3.5 15h16'/%3E%3C/svg%3E");
}
.data-table.grid-edit-mode th.col-editable[data-edit="select"]::before {
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='2'/%3E%3Cpath d='M8.5 11l3.5 3.5 3.5-3.5'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='2'/%3E%3Cpath d='M8.5 11l3.5 3.5 3.5-3.5'/%3E%3C/svg%3E");
}
.data-table.grid-edit-mode th.col-editable[data-edit="date"]::before {
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
}
.data-table.grid-edit-mode th.col-editable[data-edit="month"]::before {
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4M7.5 15h9'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4M7.5 15h9'/%3E%3C/svg%3E");
}
.data-table.grid-edit-mode td.cell-editable:hover { background: #fff !important; box-shadow: inset 0 0 0 2px var(--primary); }
/* 잠긴 행(tr[data-locked]) — 수정모드에서도 편집 대상이 아니다.
   막아만 두고 표식을 그대로 두면 "눌러도 안 열리는 칸"이 되어 고장으로 읽힌다 → 손이 가지 않게 되돌린다. */
.data-table.grid-edit-mode tr[data-locked] td.cell-editable { cursor: default; }
.data-table.grid-edit-mode tr[data-locked] td.cell-editable:hover { background: transparent !important; box-shadow: none; }
.data-table.grid-edit-mode tr[data-locked] > td { color: #64748b; }
/* 편집 중인 셀 = 엑셀처럼 "셀이 선택된" 모양. 셀 안에 인풋 UI(보더·배경·라운드)를 그리지 않는다.
   패딩도 일반 셀과 같게 둬서 편집을 시작해도 값이 튀지 않는다. */
.data-table td.cell-editing { overflow: visible; background: #fff !important; box-shadow: inset 0 0 0 2px var(--primary); }
.data-table td.cell-editing::after { content: none; }
.data-table td .cell-editor {
    width: 100%; min-width: 0; height: auto; line-height: inherit;
    border: none !important; background: transparent !important; box-shadow: none !important;
    border-radius: 0; padding: 0; margin: 0; outline: none;
    font: inherit; color: inherit; text-align: inherit;   /* 금액 우측정렬 등 컬럼 정렬을 그대로 따른다 */
}
.data-table td .combobox { width: 100%; }
/* 날짜/년월 = 텍스트 입력(숫자만 쳐도 자동 정형) + 우측 달력 버튼.
   네이티브 date/month 는 로케일 표기(2026년 08월)를 강제해 목록 표기와 어긋나고 타이핑도 느리다.
   달력이 필요할 때만 숨겨둔 네이티브 입력의 피커를 띄운다. */
.data-table td.cell-picker { position: relative; }
.data-table td.cell-picker .cell-editor { padding-right: 20px; }
.cell-picker-src { position: absolute; right: 4px; bottom: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; border: 0; padding: 0; }
.cell-cal {
    position: absolute; right: 3px; top: 50%; transform: translateY(-50%);
    width: 18px; height: 18px; padding: 0; border: 0; background: transparent; cursor: pointer;
    color: #64748b; display: inline-flex; align-items: center; justify-content: center;
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
    -webkit-mask-size: contain; mask-size: contain; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    background-color: currentColor;
}
.cell-cal:hover { color: var(--primary); }
/* 잘못된 날짜 — 셀 강조 + 말풍선 */
.data-table td.cell-invalid { box-shadow: inset 0 0 0 2px var(--danger) !important; }
.cell-error-tip {
    position: fixed; z-index: 300; background: #fff; color: var(--danger);
    border: 1px solid var(--danger); border-radius: 6px; padding: 5px 10px;
    font-size: 12px; font-weight: 600; white-space: nowrap; pointer-events: none;
    opacity: 0; transition: opacity .1s; box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
}
.cell-error-tip.show { opacity: 1; }
/* 셀 안 날짜 입력의 네이티브 달력 버튼 — 나타났다 사라지며 열 너비를 흔든다. 숨기고 JS 가 showPicker() 로 연다 */
.data-table td .cell-editor::-webkit-calendar-picker-indicator { display: none; }
.data-table td .cell-editor::-webkit-inner-spin-button { display: none; }
/* 콤보는 무엇을 고를 수 있는지 알아야 하므로 꺽쇠만 작게 남긴다 */
.data-table td .combobox-input { padding-right: 14px !important; background-size: 9px 6px; background-position: right 0 center; }
/* 편집 중에는 셀 툴팁이 값 위를 덮는다 → 숨김 */
body.cell-editing-open .admin-celltip { display: none !important; }

/* 변경된 칸 / 저장 시 재계산될 파생 칸 */
.data-table td.cell-changed { background: #fef9c3 !important; font-weight: 600; }
.data-table td.cell-stale { color: #94a3b8; font-style: italic; }
.data-table td.cell-stale::before { content: "↻ "; font-style: normal; }

/* 상태 컬럼 — 맨 왼쪽, 수정모드에서만 (§2.15) */
.data-table .col-state { display: none; width: 56px; }
.data-table.grid-edit-mode .col-state { display: table-cell; }

/* 행 상태 — 수정/삭제 표시 (§2.15) */
.data-table tbody tr.row-edited > td { background: #fffbeb; }
.data-table tbody tr.row-edited > td:first-child { box-shadow: inset 3px 0 0 0 #f59e0b; }
.data-table tbody tr.row-removed > td { background: #fef2f2; color: #b91c1c; text-decoration: line-through; opacity: .8; }
.data-table tbody tr.row-removed > td:first-child { box-shadow: inset 3px 0 0 0 var(--danger); text-decoration: none; }
.data-table tbody tr.row-removed > td.col-state, .data-table tbody tr.row-removed > td.col-check { text-decoration: none; }
.row-state {
    display: inline-flex; align-items: center; justify-content: center; gap: 3px;
    padding: 2px 7px; border-radius: 999px; font-size: 10px; font-weight: 700; line-height: 1.6;
    cursor: pointer; text-decoration: none; vertical-align: middle; transition: opacity .12s;
}
.row-state.is-edited { background: #f59e0b; color: #fff; }
/* 삭제 = 휴지통 아이콘 (행 자체는 빨간 표시 유지) */
.row-state.is-removed { background: var(--danger); color: #fff; width: 24px; height: 24px; padding: 0; border-radius: 7px; }
.row-state.is-removed svg { width: 13px; height: 13px; }
.row-state:hover { opacity: .75; }
.seq-num { vertical-align: middle; }

/* ---------- 그리드 도구 그룹 (이 표에 딸린 조작이라는 걸 시각적으로 묶는다) ---------- */
/* 도구 그룹 = 그리드 상단에 맞붙은 "탭" (§2.13 탭 바가 본문과 이어지는 것과 같은 방식).
   상단 모서리만 라운드 + 하단 보더 제거 + 카드와 1px 겹침 → 표에 속한 도구로 읽힌다. */
.grid-toolbar { margin-bottom: 0; align-items: flex-end; position: relative; }   /* relative = 줌 알약의 중앙 기준 */
/* ⚠️ .grid-tools 는 툴바 직속이 아니라 우측 컨테이너 안에 있다.
   우측 컨테이너에 아래 여백을 주면 탭이 통째로 떠서 이음새가 깨진다 → 여백은 개별 요소에만. */
.grid-toolbar > div { align-items: flex-end; }
.grid-toolbar .grid-count { margin-bottom: 10px; }                /* 카드에 붙지 않게 */
.grid-toolbar .grid-actions { margin-bottom: 10px; }              /* 화면 액션(등록 등) */
.grid-tools {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 8px 7px;
    background: #fff;
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: 10px 10px 0 0;
    margin-bottom: -1px;          /* 카드 상단 보더와 겹쳐 이음새를 지운다 */
    position: relative; z-index: 1;
}
.grid-card { border-top-right-radius: 0 !important; }             /* 탭이 붙는 쪽 모서리를 편다 */
.grid-tools-icon { display: inline-flex; align-items: center; color: #94a3b8; padding: 0 2px; }
.grid-tools-icon svg { width: 15px; height: 15px; }
/* 수정모드 = 탭과 표가 함께 편집 상태임을 알린다 */
/* 수정모드 강조는 은은하게 — 밑줄이 도드라지면 표보다 버튼이 먼저 읽힌다 */
.grid-tools.is-editing { background: var(--gk-editing-bg); border-color: var(--gk-editing-border); }

/* ---------- 범위 선택 · 채우기 (엑셀식, §2.15) ---------- */
/* 선택 셀은 배경과 **글자색을 함께** 되돌린다 — 배경만 덮으면 hover 의 흰 글자가 남아 안 보인다 */
.data-table td.cell-sel { background: var(--gk-sel-bg) !important; color: var(--gk-sel-fg) !important; }
.data-table tbody tr.row-clickable:hover td.cell-sel { color: var(--gk-sel-fg) !important; }
.data-table td.cell-sel-edge { position: relative; box-shadow: inset 0 0 0 2px var(--gk-sel-edge); }
.data-table td.cell-fill { background: var(--gk-fill-bg) !important; color: var(--gk-sel-fg) !important; outline: 1px dashed var(--gk-fill-edge); outline-offset: -1px; }
/* 채우기 핸들 = 선택 범위 우하단 작은 사각형(엑셀과 같은 위치) */
.fill-handle {
    position: absolute; right: -4px; bottom: -4px; width: 8px; height: 8px;
    background: var(--gk-sel-edge); border: 1.5px solid #fff; border-radius: 2px;
    cursor: crosshair; z-index: 3;
}
/* 드래그 중엔 텍스트 선택이 따라붙지 않게 */
body.grid-dragging, body.grid-dragging .data-table { user-select: none; -webkit-user-select: none; }
body.grid-dragging { cursor: cell; }

/* 콤보 드롭다운 portal — 셀 overflow 에 잘리지 않게 body 로 띄운 상태 */
.combobox-dropdown-portal { display: block !important; z-index: 1200; }

@media (max-width: 768px) {
    .data-table.grid-edit-mode .col-check, .data-table.grid-edit-mode .col-state { display: flex; justify-content: flex-end; }
    .grid-tools { flex-wrap: wrap; border-bottom: 1px solid var(--border); border-radius: 10px; margin-bottom: 10px; }
    .grid-card { border-top-right-radius: 12px !important; }
    .grid-toolbar .grid-count, .grid-toolbar .grid-actions { margin-bottom: 6px; }
    .data-table td.cell-editing { display: flex; }
    .data-table td .cell-editor { max-width: 60%; }
}

/* ---------- 셀 안 상세 열기(돋보기) — 연결된 다른 화면으로 (§2.13 탭으로 열림) ---------- */
/* ===== 토스트(§1.4) — 가벼운 결과 알림. 다이얼로그와 달리 아무것도 막지 않는다 ===== */
.toast-host {
    position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
    z-index: 300;                                   /* 모달(200) 위 — 모달 안에서 한 조작도 보여야 한다 */
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    pointer-events: none;                           /* 토스트가 없는 자리는 아래 화면을 그대로 쓴다 */
    width: max-content; max-width: min(92vw, 520px);
}
.toast {
    pointer-events: auto; cursor: pointer;
    background: #0f172a; color: #fff;
    border-radius: 10px; padding: 10px 16px;
    font-size: 13px; line-height: 1.5; text-align: center;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .28);
    opacity: 0; transform: translateY(8px);
    transition: opacity .18s ease, transform .18s ease;
}
.toast-in { opacity: 1; transform: translateY(0); }
.toast-out { opacity: 0; transform: translateY(8px); }
.toast-warn { background: #7c2d12; }
.toast-line + .toast-line { color: #cbd5e1; font-size: 12px; }
@media (prefers-reduced-motion: reduce) {
    .toast { transition: none; opacity: 1; transform: none; }
}
@media (max-width: 768px) { .toast-host { bottom: 16px; } }

/* 다른 화면으로 넘어가는 링크 — **자기 크기만** 차지한다.
   ⚠️ 예전엔 음수 마진으로 셀 전체를 덮었다. 그런데 행 클릭이 상세를 여는 표에서는
   "셀을 누르면 다른 화면, 행을 누르면 상세" 가 한 칸 안에서 충돌한다 —
   값을 읽으려고 누른 건데 화면이 넘어가 버린다.
   → 이동은 **돋보기(.cell-zoom)** 로만 한다. 셀의 나머지는 행 클릭(상세)에 넘긴다. */
.cell-goto {
    display: inline-flex; align-items: center; gap: 5px;
    text-decoration: none; cursor: pointer;
}
/* 돋보기는 **셀 오른쪽 끝에 고정**한다. 값 옆에 붙여 두면 값 길이에 따라 좌우로 흔들려
   열마다 아이콘 위치가 들쭉날쭉해진다.
   ⚠️ td 를 flex 로 바꾸지 않는다 — 그러면 text-center/text-right 같은 열 정렬이 깨진다.
   대신 아이콘만 절대배치로 띄우고, 글자와 겹치지 않게 오른쪽 여백을 확보한다. */
/* 링크 모양이 두 가지다 —
   (a) 돋보기만 링크: 값은 셀에 그대로 두고 아이콘만 칸 오른쪽에 고정 (jindam 결제화면)
   (b) 값까지 링크: 셀 전체가 클릭 영역 (colon-money 계산서·작업)
   (b) 를 절대배치하면 **값까지 흐름에서 빠져** 칸이 헤더 폭으로 굳고 배지가 오른쪽에 붙는다.
   그래서 고정은 (a) 에만 건다. */
.data-table td:has(> a.cell-goto) { position: relative; padding-right: 32px; }
/* (a) 돋보기만 링크 — 링크째 오른쪽 끝에 고정 */
.data-table td > a.cell-goto:has(> .cell-zoom:only-child) {
    position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
}
/* (b) 값까지 링크 — **아이콘만** 오른쪽 끝에 고정한다. 링크째 옮기면 값이 흐름에서 빠져
   칸이 헤더 폭으로 굳고 값이 오른쪽에 붙는다(colon-money 계산서에서 그랬다). */
.data-table td > a.cell-goto:not(:has(> .cell-zoom:only-child)) > .cell-zoom {
    position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
}
/* 가운데정렬 칸은 **왼쪽에도 같은 자리**를 비운다 — 오른쪽만 비우면 값이 왼쪽으로 밀려
   가운데정렬로 안 보인다(진담 예약번호가 그랬다) */
.data-table td.text-center:has(> a.cell-goto) { padding-left: 32px; }
/* 돋보기가 들어가는 칸은 값 + 아이콘이 함께 들어가야 한다 — 헤더가 짧으면('작업') 좁게 굳는다 */
.data-table th.col-goto, .data-table td:has(> a.cell-goto) { min-width: 120px; }
.cell-goto:hover .cell-zoom { background-color: var(--primary); }
.cell-zoom {
    width: 16px; height: 16px; flex-shrink: 0; cursor: pointer; display: inline-block;
    background-color: var(--gk-zoom-fg, var(--gk-zoom-base));
    -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M21 21l-4.3-4.3'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M21 21l-4.3-4.3'/%3E%3C/svg%3E");
    -webkit-mask-size: contain; mask-size: contain; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
/* ⚠️ 아이콘 색은 **행 반전과 반드시 같이** 움직여야 한다.
   반전만 끄고 아이콘을 흰색으로 두면 흰 배경에 흰 아이콘 → 버튼이 사라진 것처럼 보인다.
   그래서 상속 변수 하나로 묶어, 반전을 끄는 선택자가 아이콘 색도 함께 되돌리게 한다. */
.data-table tbody tr.row-clickable:hover { --gk-zoom-fg: var(--gk-zoom-invert); }
body.grid-dragging .data-table tbody tr.row-clickable:hover,
.data-table:has(td.cell-sel) tbody tr.row-clickable:hover { --gk-zoom-fg: var(--gk-zoom-base); }
.data-table tbody tr.row-clickable:hover .cell-goto:hover .cell-zoom { background-color: #cbd5e1; }
/* ⚠️ 링크 **글자색**도 반전을 따라야 한다. 앱 링크색(파랑)이 반전 배경(파랑)과 같으면
   글자가 통째로 사라진다(진담 결제 목록에서 실제로 안 보였다). 아이콘만 챙기고 글자를 빠뜨린 자리다.
   행이 **실제로 반전되는 동안에만** 적용한다 — 드래그·범위선택 중엔 반전이 꺼지므로 제외. */
body:not(.grid-dragging) .data-table:not(:has(td.cell-sel)) tbody tr.row-clickable:hover td a {
    color: var(--gk-row-hover-fg);
}

/* ======================================================================
   Tailwind 없이도 성립하게 — 키트 마크업이 쓰는 최소 유틸만 자체 정의한다.
   Tailwind 가 있는 프로젝트에서는 같은 뜻이라 충돌하지 않는다.
   (여기 없는 유틸을 키트 JS 가 쓰기 시작하면 그 순간 Tailwind 의존이 되살아난다)
   ====================================================================== */
.gk-row      { display: flex; align-items: center; gap: 8px; }
.gk-row-wrap { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.gk-col      { display: flex; flex-direction: column; gap: 8px; align-items: stretch; align-self: stretch; flex-shrink: 0; }
.gk-grow     { flex: 1 1 0%; min-height: 0; justify-content: center; }
.gk-push     { margin-left: auto; }
.gk-icon     { width: 1rem; height: 1rem; }
.gk-card     { background: #fff; border: 1px solid var(--border); border-radius: 12px; }

/* 표 정렬·강조 — Tailwind 의 text- / font- 계열과 같은 값.
   ⚠️ 주석 안에 `*` 와 `/` 를 붙여 쓰지 말 것 — 거기서 주석이 끝나고 뒤가 CSS 로 파싱된다.
      그러면 **바로 다음 규칙이 통째로 무효**가 된다(선택자 목록 중 하나라도 잘못되면 규칙 전체가 버려진다).
      실제로 이 줄 때문에 아래 text-left 세 개가 오랫동안 죽어 있었다. */
.data-table .text-left,   td.text-left,   th.text-left   { text-align: left; }
.data-table .text-center, td.text-center, th.text-center { text-align: center; }
.data-table .text-right,  td.text-right,  th.text-right  { text-align: right; }
.font-medium   { font-weight: 500; }
.font-semibold { font-weight: 600; }

/* 조회 중 — 표를 지우지 않고 흐리게만 한다(데이터가 사라진 것처럼 보이지 않게) */
.data-table.grid-loading tbody { opacity: .45; transition: opacity .12s ease; }
.data-table.grid-loading { pointer-events: none; }

/* more(더보기) — 페이저 자리에 진행 상태와 하단 감시자 */
.more-status { font-size: 12.5px; color: var(--text-secondary); }
.more-sentinel {
    display: block; margin: 6px auto 0; padding: 7px 22px;
    border: 1px solid var(--border); background: #fff; border-radius: 999px;
    font-size: 12.5px; color: var(--text-secondary); cursor: pointer;
}
.more-sentinel:hover { border-color: var(--primary); color: var(--primary); }

/* 신규 행 — 아직 저장되지 않았음을 알린다 */
.data-table tbody tr.row-new > td { background: #f0fdf4; }
.row-badge.badge-new { background: #dcfce7; color: #166534; border: 1px solid #86efac; }

/* 전역 커스텀 툴팁 — **키트 것이다**(admin-tooltip.js 가 만든다).
   ⚠️ CSS 를 분리할 때 이게 app.css 로 새어나가, 스타일 없는 div 가 문서 맨 아래에 쌓여 보였다. */
/* 껍데기는 **위치만** 잡는다(투명). 텍스트는 .admin-tip-text 가, 이미지는 img 가 각자 자기 상자를 갖는다.
   ⚠️ v4.8.2~4.9.0 에서 껍데기에도 배경·보더·그림자가 남아 있어 텍스트 툴팁이 **상자 두 겹**으로 떴다
   (사이드바 메뉴·페이지크기 콤보 등 모든 텍스트 툴팁). 껍데기에 다시 배경을 주지 말 것. */
.admin-tip { position: fixed; z-index: 250; transform: translateY(-50%); background: none; border: 0; padding: 0; box-shadow: none;
    color: var(--primary); font-size: 12px; max-width: 50vw; white-space: nowrap; pointer-events: none; opacity: 0;
    transition: opacity .07s; }
.admin-tip.show { opacity: 1; }

/* 말줄임 셀 툴팁 — 키트 것이다(initCellTip). app.css 로 새면 스타일 없는 div 가 문서 끝에 쌓인다 */
.admin-celltip { position: fixed; z-index: 200; transform: translateY(-50%); background: #fff; color: var(--primary); border: 1px solid var(--primary); font-size: 12px;
    padding: 5px 9px; border-radius: 6px; max-width: 60vw; white-space: normal; word-break: break-word; line-height: 1.4;
    pointer-events: none; opacity: 0; transition: opacity .07s; box-shadow: 0 4px 14px rgba(0, 0, 0, .35); }
.admin-celltip.show { opacity: 1; }

/* ===== 영역 고정 + 내부 스크롤 (data-height) =====
   표가 문서를 늘리지 않고 정해진 높이 안에서만 스크롤한다.
   ⚠️ 헤더·합계행은 sticky 로 붙여야 스크롤 중에도 기준이 보인다. */
.table-wrap-scroll { overflow-y: auto; }
.table-wrap-fill { max-height: 100%; height: 100%; }
.table-wrap-scroll .data-table thead th {
    position: sticky; top: 0; z-index: 2;
}
.table-wrap-scroll .data-table tfoot td {
    position: sticky; bottom: 0; z-index: 2; background: #f8fafc;
    border-top: 2px solid var(--border);
}
/* ⚠️ sticky 헤더·합계행은 표 위아래를 **덮는다**. 그대로 두면 키보드로 첫/마지막 행에 갔을 때
   그 행이 가려져 "안 보이는 칸을 고치는" 상태가 된다.
   scrollIntoView 가 그만큼 여유를 두도록 셀에 scroll-margin 을 준다(값 = 헤더/합계행 높이). */
.table-wrap-scroll .data-table tbody td {
    scroll-margin-top: 44px;
    scroll-margin-bottom: 48px;
}

/* 합계행을 맨 위에 둘 때(thead 안) — 헤더 아래에 붙는다 */
.table-wrap-scroll .data-table thead tr.grid-foot-row td { position: sticky; top: 40px; z-index: 2; }

/* ===== 행 높이 (data-row-height) ===== */
.data-table.rows-compact td { padding-top: 5px !important; padding-bottom: 5px !important; }
.data-table.rows-comfortable td { padding-top: 17px !important; padding-bottom: 17px !important; }

/* 선택 범위 숫자 통계 — 페이저 영역 우측(엑셀 상태 표시줄과 같은 자리) */
.grid-pager-area { position: relative; }
.grid-selstat {
    position: absolute; right: 0; top: 50%; transform: translateY(-50%);
    display: inline-flex; align-items: stretch; gap: 0;
    border: 1px solid var(--border); background: #fff; border-radius: 8px; overflow: hidden;
    white-space: nowrap;
}
.ss-item {
    display: inline-flex; align-items: baseline; gap: 6px;
    padding: 6px 12px; border: 0; background: transparent; font: inherit;
    border-left: 1px solid var(--border);
}
.ss-item:first-child { border-left: 0; }
.ss-label { font-size: 11px; color: var(--text-secondary); }      /* 라벨 = 작고 흐리게 */
.ss-val   { font-size: 13px; font-weight: 700; color: var(--text-primary);
            font-variant-numeric: tabular-nums; }                  /* 값 = 크고 진하게 */
.ss-copy  { cursor: pointer; }
.ss-copy:hover { background: var(--primary-light); }
.ss-copy:hover .ss-label { color: var(--primary); }
.ss-copy:active { background: #e5e7eb; }

@media (max-width: 768px) { .grid-selstat { position: static; transform: none; margin: 8px auto 0; } }

/* 수정모드 하단 버튼 그룹 — 표가 길 때 위로 스크롤하지 않아도 되게 */
.grid-tools-bottom {
    display: flex; justify-content: flex-end; gap: 6px;
    margin: -1px 0 0; padding: 8px 10px;
    background: var(--gk-editing-bg);
    border: 1px solid var(--gk-editing-border); border-top: 0;
    border-radius: 0 0 10px 10px;                 /* 표 아래에 탭처럼 맞물린다(위쪽 도구의 거울) */
}
@media (max-width: 768px) { .grid-tools-bottom { justify-content: center; flex-wrap: wrap; } }

/* ===== 이미지 툴팁 (열 `data-tip-img`) — 썸네일 미리보기 =====
   텍스트만일 때의 말풍선 스타일이 이미지에는 어울리지 않아, 이미지가 있을 때는
   껍데기 배경·테두리를 걷고 안쪽 요소가 각자 자기 상자를 갖는다. */
.admin-tip.tip-img { max-width: 360px; }
.admin-tip .admin-tip-text { width: fit-content; max-width: 360px; background: #fff; color: var(--primary);
    border: 1px solid var(--primary); border-radius: 8px; padding: 7px 12px; font-size: 12px; line-height: 1.4;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .16); }
.admin-tip img { display: block; max-width: 220px; max-height: 300px; padding: 6px; background: #fff;
    border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 28px rgba(0, 0, 0, .18); }
.admin-tip .admin-tip-text + img { margin-top: 9px; }

/* ===== §2.10 행 드래그 정렬 (`data-reorder-url`) ===== */
.data-table td.col-drag { text-align: center; cursor: grab; user-select: none; }
.data-table td.col-drag:active { cursor: grabbing; }
.data-table tr.row-dragging { opacity: .45; background: var(--gk-head-bg); }
.grip-9 { display: inline-block; width: 10px; height: 14px; vertical-align: middle;
    background-image: radial-gradient(currentColor 1px, transparent 1.2px);
    background-size: 5px 5px; color: #94a3b8; }
.row-move { display: none; gap: 6px; }
.row-move button { border: 1px solid var(--control-border); background: #fff; border-radius: 6px;
    width: 26px; height: 26px; font-size: 11px; line-height: 1; color: var(--primary); cursor: pointer; }
.row-move button:hover { background: var(--gk-head-bg); }
@media (max-width: 767px) {
    /* 모바일엔 마우스가 없다 — 손잡이를 감추고 ▲▼ 버튼으로 */
    .data-table td.col-drag .grip-9 { display: none; }
    .data-table td.col-drag .row-move { display: inline-flex; }
}

/* ===== 다중선택 칩 필터 (표 밖 필터 type:'chips') ===== */
.badge-filter { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.badge-chip { position: relative; display: inline-flex; align-items: center; cursor: pointer; user-select: none; }
.badge-chip input[type="checkbox"] { position: absolute; opacity: 0; pointer-events: none; }
.badge-chip span { display: inline-block; padding: 5px 11px; border-radius: 999px; font-size: 12px; font-weight: 700;
    border: 1px solid var(--control-border); background: #fff; color: #64748b; transition: all .12s; }
.badge-chip:hover span { border-color: var(--primary); color: var(--primary); }
/* 켜진 칩 — 눌린 상태가 한눈에 보여야 여러 개 고른 걸 안다 */
.badge-chip.on span { background: var(--primary); border-color: var(--primary); color: #fff; }

/* ===== 열 표시 유형 배지 (`data-cell-type="yn|use|display"`) =====
   사용처(velysound)에 있던 표기를 그대로 옮겼다 — 합치면서 모양을 바꾸면
   그건 이관이 아니라 표기 변경이라 화면이 흔들린다. 색만 앱이 덮을 수 있게 둔다. */
.badge-y, .badge-n, .badge-use-y, .badge-use-n, .badge-display-y, .badge-display-n {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 600;
}
.badge-y { background: #e8f8ef; color: #27ae60; }
.badge-use-y { background: #f0f0f0; color: #444; }
.badge-display-y { background: #e8f5e9; color: #2e7d32; }
.badge-n, .badge-use-n, .badge-display-n { background: #f5f5f5; color: #bbb; }


/* ===== 표 줌 알약 (§2.17) — 그리드 툴바 가운데 =====
   표(.table-wrap 안의 table)에만 zoom 을 걸어 컬럼이 더 들어오게 한다. 알약은 표의 도구라
   표 바로 위 툴바에 산다(§2.1). 화면 전체 줌은 쓰지 않는다.
   ⚠️ 알약은 툴바(줌 밖)에 있으므로 표가 줄어도 같이 줄지 않는다 — 역보정이 필요 없다. */
.grid-zoom {
    display: inline-flex;
    /* ⚠️ stretch 여야 한다. center 로 두면 버튼이 알약보다 낮아서, 호버 배경이
       위아래 흰 띠를 남기고 **일부만 칠해진 것처럼** 보인다. */
    align-items: stretch;
    /* ⚠️ 흐름 안에서 margin:auto 로 두면 **남은 공간의 가운데**라, 좌(건수)·우(도구) 폭이
       다르면 실제 중앙이 아니다. 툴바 전체 폭 기준 중앙에 놓으려면 절대배치여야 한다.
       흐름 밖이라 알약이 떠도 건수·도구가 밀리지 않는 이점도 같이 얻는다. */
    position: absolute; left: 50%; transform: translateX(-50%);
    bottom: 8px;
    margin: 0;
    border: 1px solid var(--border-color, #d1d5db); border-radius: 999px;
    background: #fff; overflow: hidden;
    /* 평소엔 숨어 있다가 줌이 바뀌거나 그리드에 손이 올라오면 나타난다. */
    opacity: 0; visibility: hidden;
    transition: opacity .16s ease, visibility .16s;
}
.grid-zoom.is-on { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) { .grid-zoom { transition: none; } }

.grid-zoom button {
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; cursor: pointer;
    font: inherit; line-height: 1; color: var(--text-primary, #111827);
    padding: 8px 14px; min-width: 38px; font-size: 15px;
}
.grid-zoom .grid-zoom-val {
    min-width: 66px; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
    border-left: 1px solid var(--border-color, #d1d5db);
    border-right: 1px solid var(--border-color, #d1d5db);
}
/* 양 끝 버튼은 알약 곡률을 따라간다 — 안 그러면 호버 배경 모서리가 알약 밖으로 각지게 비어 보인다 */
.grid-zoom button:first-child { border-top-left-radius: 999px; border-bottom-left-radius: 999px; padding-left: 16px; }
.grid-zoom button:last-child  { border-top-right-radius: 999px; border-bottom-right-radius: 999px; padding-right: 16px; }
.grid-zoom button:hover { background: var(--primary, #2563eb); color: #fff; }   /* §2.3 호버=색상반전 */
.grid-zoom button:focus-visible { outline: 2px solid var(--primary, #2563eb); outline-offset: -2px; }
@media (max-width: 768px) { .grid-zoom { display: none; } }   /* §2.17 모바일은 끈다 */

/* ============================================================
   탭 작업공간 셸 (§2.13) — cgrid-shell.js 와 짝. 정본 컴포넌트다.
   ============================================================ */
/* 부모(셸)가 높이를 정한다 — 100vh 를 또 박으면 셸 높이와 어긋나 바깥 스크롤이 생긴다 */
.cs-main { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; height: 100%; }
/* 탭 바는 본문(밝음)이 아니라 **사이드바(다크) 쪽**에 속한다 — 헤더가 사이드바의 연장처럼 보이게 */
.cs-bar {
    flex: 0 0 auto; display: flex; align-items: flex-end; gap: 8px;
    background: #0f172a; padding: 6px 8px 0 0;
}
/* 첫 탭의 좌측 모서리 = 컨텐츠 영역 시작선. 좌측 패딩을 두지 않는다 */
.cs-tabs { flex: 1 1 auto; min-width: 0; display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; }
.cs-tabs::-webkit-scrollbar { display: none; }
/* 탭바 가로 드래그 중 — 커서를 손바닥으로. 자식까지 덮어야 탭 위에서도 바뀐다 */
.cs-dragging, .cs-dragging * { cursor: grabbing !important; }
.cs-tab {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 8px 7px 14px;                 /* 우측은 ✕ 자리 */
    border-radius: 8px 8px 0 0;                /* 상단만 라운드 — 아래는 본문과 이어진다 */
    background: transparent; color: #94a3b8; font-size: 13px; cursor: pointer;
    max-width: 220px; white-space: nowrap;
}
/* ✕ 가 없는 탭(마지막 1개)은 좌우 대칭 — 아니면 제목이 한쪽으로 쏠려 보인다 */
.cs-tab.solo { padding-right: 14px; }
.cs-tab:hover { color: #e2e8f0; }
.cs-tab.active { background: #fff; color: #0f172a; font-weight: 700; }   /* 활성 = 본문과 이어짐 */
.cs-tab.inpanel { box-shadow: inset 0 -3px 0 #38bdf8; }
.cs-tab-title { overflow: hidden; text-overflow: ellipsis; }
.cs-home { font-size: 12px; }
.cs-tab-x { border: 0; background: transparent; color: inherit; cursor: pointer; padding: 0 2px; font-size: 11px; opacity: .6; }
.cs-tab-x:hover { opacity: 1; }

.cs-ctl { flex: 0 0 auto; display: flex; align-items: center; gap: 2px; padding-bottom: 6px; }
.cs-ctl-sep { width: 1px; height: 14px; background: rgba(148, 163, 184, .3); margin: 0 3px; }
.cs-ctl-b {
    border: 0; background: transparent; color: #94a3b8; cursor: pointer;
    padding: 3px 6px; border-radius: 5px; font-size: 13px; line-height: 1;
}
.cs-ctl-b:hover:not(:disabled) { background: rgba(148, 163, 184, .18); color: #fff; }
.cs-ctl-b:disabled { opacity: .35; cursor: default; }

.cs-ws { flex: 1 1 auto; position: relative; min-height: 0; background: #fff; }

/* ===== 배치 = CSS (layout:'panes') =====
   JS 는 역할 클래스(mode- · dir- · is-pane 계열)만 붙인다. 어디에 얼마나 놓일지는 여기서 정한다.
   비율을 바꾸려면 아래 flex 한 줄, 3분할도 flex 라 자연스럽다 — JS 좌표 계산이 없다.
   그래서 창 크기가 바뀌어도 resize 리스너가 필요 없다. */
.cs-ws.mode-single, .cs-ws.mode-split { display: flex; }
.cs-ws.dir-lr { flex-direction: row; }
.cs-ws.dir-tb { flex-direction: column; }
.cs-pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; flex: 1 1 50%; }
.cs-ws.mode-single .cs-pane.is-single { flex: 1 1 100%; }
.cs-pane .cs-frame {
    /* ⚠️ height:100% 가 없으면 iframe 이 기본 높이(150px)로 쪼그라든다 — flex:1 1 auto 만으로는
       iframe 의 내재 높이를 못 이긴다. 탭 컨텐츠가 위로 쏠려 보이는 원인. */
    flex: 1 1 auto; width: 100%; height: 100%; min-height: 0; border: 0; background: #fff; display: block;
}
/* 패널 머리말은 분할일 때만 보인다 — 단일 모드에서 자리를 먹으면 화면이 좁아진다 */
.cs-pane-head { display: none; }
.cs-ws.mode-split .cs-pane-head {
    display: flex; align-items: center; height: 28px; flex: 0 0 28px;
    padding: 0 10px; background: #f1f5f9; border-bottom: 1px solid #e2e8f0;
}
.cs-pane-title {
    display: inline-flex; align-items: center; gap: 6px;
    border: 0; background: transparent; cursor: pointer;
    font: inherit; font-size: 12px; font-weight: 700; color: #334155; padding: 0;
}
/* 분할 중 어느 쪽이 포커스인지 보이게 — 없으면 새 탭이 어디로 열릴지 알 수 없다 */
.cs-ws.mode-split .cs-pane.is-focus > .cs-pane-head { background: #e0f2fe; box-shadow: inset 2px 0 0 #38bdf8; }
.cs-ws.dir-tb .cs-pane.is-paneB { border-top: 1px solid #cbd5e1; }
.cs-ws.dir-lr .cs-pane.is-paneB { border-left: 1px solid #cbd5e1; }

/* layout:'absolute' (옛 마크업 호환) — JS 가 좌표를 계산해 붙인다 */
.cs-ws:not(.mode-single):not(.mode-split) .cs-frame { position: absolute; border: 0; background: #fff; }
.cs-ptitle.is-focus { background: #e0f2fe; box-shadow: inset 2px 0 0 #38bdf8; }
.cs-ptitle {
    position: absolute; display: none; align-items: center; justify-content: space-between;
    padding: 0 10px; font-size: 12px; font-weight: 700; color: #334155;
    background: #f1f5f9; border-bottom: 1px solid #e2e8f0; cursor: pointer; z-index: 2;
}
.cs-ptitle.show { display: flex; }

.cs-list {
    position: fixed; z-index: 300; display: none; min-width: 200px; max-width: 320px;
    background: #0f172a; color: #e2e8f0; border-radius: 8px; padding: 4px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .3);
}
.cs-list.show { display: block; }
.cs-li { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 6px; font-size: 13px; cursor: pointer; }
.cs-li span:first-of-type { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-li:hover { background: rgba(148, 163, 184, .18); }
.cs-li.active { background: rgba(56, 189, 248, .18); font-weight: 700; }
.cs-li-x { opacity: .6; }
.cs-li-x:hover { opacity: 1; }

/* 탭 우클릭 컨텍스트 메뉴 (§2.13 — 닫기류를 우클릭으로도 제공) */
.cs-ctx {
    position: fixed; z-index: 320; min-width: 190px; padding: 4px;
    background: #0f172a; color: #e2e8f0; border-radius: 8px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .32);
}
.cs-ctx-row { padding: 8px 12px; border-radius: 6px; font-size: 13px; cursor: pointer; white-space: nowrap; }
.cs-ctx-row:hover { background: rgba(148, 163, 184, .18); }
.cs-ctx-row.is-disabled { opacity: .4; cursor: default; }
.cs-ctx-row.is-disabled:hover { background: transparent; }
