/* ======================================================================
   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;
    --gk-mcard-border:   #dde2ec;   /* 모바일 행 카드 테두리(v4.17.2, §2.19 ⑦) — 표 칸 선(--border)보다 또렷하게 */
    --gk-mcard-shadow:   0 2px 8px rgba(26, 26, 46, .05);   /* 모바일 행 카드 옅은 그림자 */
}

/* ---------- 로딩 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; }
/* ---------- 바로 반영(data-search-live) · 상세 필터 접기(data-filter-more) · 걸린 조건 칩 — v4.12.0 ---------- */
/* 바로 반영 화면은 [조회]가 기간이 있을 때만 남는다 — 초기화 하나면 세로로 늘리지 않고 컨트롤 높이에 맞춘다 */
.admin-search-card .admin-search-actions.is-live { justify-content: flex-end; }
.admin-search-card .admin-search-actions.is-live .btn { flex: 0 0 auto; height: 34px; }
/* 상세 필터 버튼 — 기본 줄 끝. 숫자 = 상세 칸에 걸린 조건 수 */
.filter-more-btn { height: 34px; display: inline-flex; align-items: center; gap: 6px; padding: 0 12px; font-size: 13px; font-weight: 700; }
.filter-more-btn.on { border-color: var(--primary); }
.filter-more-n:empty { display: none; }
.filter-more-n { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--primary); color: #fff;
    font-size: 11px; line-height: 18px; text-align: center; }
/* 상세 칸 — 기본 줄 아래 한 줄을 통째로(접힘이 기본, 열림은 화면별로 기억) */
.filter-more { display: none; flex: 1 1 100%; flex-wrap: wrap; align-items: flex-end; gap: 10px 24px;
    padding-top: 12px; border-top: 1px dashed var(--control-border); grid-column: 1 / -1; }
.filter-more.is-open { display: flex; }
/* v4.21.0 — 펼친 상세 칸은 **기본 줄에 이어 붙는다**(윗줄 빈자리부터 채우고 넘치면 다음 줄). 상세 칸을 따로 한 줄로 두면
   윗줄이 비어도 두 줄이 됐다. [상세 필터] 단추는 늘 줄 끝(order). 좁은 화면은 아래 @media 그대로(한 줄에 하나). */
@media (min-width: 769px) {
    .filter-more.is-open { display: contents; }
    .filter-bar > .filter-more-toggle { order: 1; }
    .filter-bar > .filter-foot { order: 2; }
}
/* 한 줄에 다 들어감(.is-more-fit, grid 가 잰다) — 접지 않는다: 상세 칸을 펴고 단추를 숨긴다 */
.admin-search-card.is-more-fit .filter-more { display: contents; }
.admin-search-card.is-more-fit .filter-more-toggle { display: none; }
/* 묶음(data-filter-set) — 같은 이름끼리 한 덩어리로 줄바꿈 */
.filter-set { display: inline-flex; align-items: flex-end; gap: 10px 24px; flex: 0 0 auto; flex-wrap: nowrap; max-width: 100%; }
@media (max-width: 768px) { .filter-set { flex-direction: column; align-items: stretch; width: 100%; gap: 10px; grid-column: 1 / -1; } }
/* 기간을 바꾸고 아직 [조회]를 안 눌렀다 — 칸과 버튼에 표시(놓치지 않게) */
.filter-group.is-pending .filter-input { border-color: #f3b14b; background: #fffbf2; }
.admin-search-card .admin-search-actions .btn.is-pending { position: relative; }
.admin-search-card .admin-search-actions .btn.is-pending::after { content: ""; position: absolute; top: 4px; right: 4px;
    width: 7px; height: 7px; border-radius: 50%; background: #f59e0b; }
/* 걸린 조건 칩 — ✕ 로 하나씩 푼다. 없으면 자리도 차지하지 않는다 */
.filter-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: -6px 0 12px; }
.filter-chips.is-empty { display: none; }
.filter-chip { display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 4px 0 10px; border-radius: 999px;
    background: #eef2ff; color: #2a3a8f; font-size: 12.5px; font-weight: 600; max-width: 100%; }
.filter-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-chip-x { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border: 0;
    border-radius: 50%; background: transparent; color: #6c78c9; cursor: pointer; padding: 0; }
.filter-chip-x:hover { background: #dfe5ff; color: #2a3a8f; }
.filter-chip-x .gk-icon { width: 12px; height: 12px; }
/* ---------- 자주 쓰는 필터 (v4.20.0, cgrid-presets.js · §1.7 data-presets) ----------
   켜진 검색카드만(.has-fp) — 위 = 필터 칸이 카드 너비를 다 쓰고, 맨 아랫줄(.fp-bar) = 왼쪽 칩 · 오른쪽 [상세 필터][초기화][조회] 가로.
   선언 없는 표는 이 규칙에 하나도 안 걸린다(예전 배치 그대로). */
.admin-search-card.has-fp { flex-direction: column; gap: 14px; }
.admin-search-card.has-fp > .filter-bar { width: 100%; }
.admin-search-card .fp-bar { display: flex; align-items: center; gap: 12px; min-width: 0; padding-top: 12px; border-top: 1px dashed var(--control-border, #e2e8f0); }
.admin-search-card .fp-left { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px; }
.admin-search-card .fp-label { flex: 0 0 auto; font-size: 13px; font-weight: 700; color: var(--text-primary, #334155); white-space: nowrap; word-break: keep-all; }
.admin-search-card .fp-chips { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.admin-search-card .fp-chip { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 12px; margin: 0; border: 1px solid #e2e8f0; border-radius: 999px;
    background: #fff; color: #475569; font: inherit; font-size: 13px; font-weight: 600; line-height: 1; white-space: nowrap; word-break: keep-all; cursor: pointer;
    box-sizing: border-box; transition: background .15s, border-color .15s, color .15s; }
.admin-search-card .fp-chip:hover { border-color: #94a3b8; color: #0f172a; }
.admin-search-card .fp-chip.is-on { background: #0f172a; border-color: #0f172a; color: #fff; }
.admin-search-card .fp-chip .fp-count { font-weight: 700; color: #e11d48; }
.admin-search-card .fp-chip.is-on .fp-count { color: #fda4af; }
.admin-search-card .fp-mine { padding: 0 4px 0 0; cursor: default; }
.admin-search-card .fp-mine > button { display: inline-flex; align-items: center; gap: 5px; height: 100%; margin: 0; border: 0; background: none;
    color: inherit; font: inherit; cursor: pointer; }
.admin-search-card .fp-mine .fp-pick { padding: 0 4px 0 12px; }
.admin-search-card .fp-mine .fp-star { display: inline-flex; color: #f59e0b; }
.admin-search-card .fp-mine .fp-del { width: 22px; height: 22px; padding: 0; justify-content: center; border-radius: 999px; color: #94a3b8; }
.admin-search-card .fp-mine .fp-del:hover { background: rgba(148, 163, 184, .18); color: #e11d48; }
.admin-search-card .fp-mine.is-on .fp-del { color: #cbd5e1; }
.admin-search-card .fp-save { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px; height: 30px; padding: 0 11px 0 9px; margin: 0;
    border: 1px dashed #cbd5e1; border-radius: 999px; background: #fff; color: #64748b; font: inherit; font-size: 12.5px; font-weight: 600;
    white-space: nowrap; word-break: keep-all; cursor: pointer; box-sizing: border-box; }
.admin-search-card .fp-save:hover { border-color: #64748b; color: #0f172a; }
/* 오른쪽 버튼 — 키트 세로 기둥(초기화/조회)을 가로 한 줄로 */
.admin-search-card .fp-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; }
.admin-search-card .fp-actions .filter-more-toggle { width: auto; margin: 0; padding: 0; flex: 0 0 auto; }
.admin-search-card .fp-actions .filter-more-toggle > label { display: none; }
.admin-search-card .fp-actions .filter-more-btn { width: auto; }
.admin-search-card .fp-actions .admin-search-actions { flex-direction: row !important; align-self: center; gap: 6px; }
.admin-search-card .fp-actions .admin-search-actions .btn { flex: 0 0 auto; width: auto; height: 34px; min-height: 0; }
.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%; }
/* 페이지당 개수 — **숫자 세 자리 + 화살표 = 4.5rem** 고정(§2.4·§2.5). 키트는 이 콤보에 cb-block 을 안 붙이지만(v4.17.1),
   붙어 있어도 사이트의 `.combobox.cb-block { width:100% }` 를 이기게 특정도를 한 칸 올린 선택자를 같이 둔다(postring 265px 실측). */
.combobox.cb-compact, .combobox.cb-block.cb-compact { width: 4.5rem; flex: 0 0 auto; }
.combobox.cb-compact .combobox-input, .combobox.cb-block.cb-compact .combobox-input { width: 100%; min-width: 0; }
.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; }
/* 위로 펼치기(v4.17.1, §2.4 목록 = 다 보이게) — 아래 공간(모달 본문·창)이 모자라면 cgrid-combobox.js fitDrop 이 붙인다 */
.combobox-dropdown.combobox-drop-up { top: auto; bottom: calc(100% + 4px); }
.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; }

/* ---------- 다중선택 콤보 (체크박스+인풋+드롭다운, cgrid-combobox.js enhanceMulti) ----------
   검색카드 코드 필터의 기본(§2.2). 모양은 단일 콤보 그대로 + 항목 앞 체크박스 + 상단 [전체 선택][선택 해제].
   목록만 스크롤된다 — 상단 바는 늘 보인다. */
.cb-multi-dropdown { padding: 0; overflow: hidden; display: none; flex-direction: column; max-height: 320px; }
.combobox.combobox-open .cb-multi-dropdown, .cb-multi-dropdown.combobox-dropdown-portal { display: flex !important; }
.cb-multi-bar { display: flex; align-items: center; gap: 2px; padding: 4px 6px; border-bottom: 1px solid var(--border); flex: 0 0 auto; background: #fff; }
.cb-multi-act {
    border: 0; background: none; padding: 4px 8px; border-radius: 6px; cursor: pointer;
    font-size: 12px; font-weight: 600; color: var(--text-secondary); white-space: nowrap; font-family: inherit;
}
.cb-multi-act:hover { background: var(--primary-light); color: var(--primary); }
.cb-multi-done { display: none; margin-left: auto; color: var(--primary); }
.cb-multi-list { overflow-y: auto; padding: 4px 0; flex: 1 1 auto; min-height: 0; }
.cb-multi-item { display: flex; align-items: center; gap: 8px; }
/* 체크 표시가 선택 상태를 말한다 — 단일 콤보의 굵은 글씨·배경은 여기선 겹쳐서 시끄럽다 */
.cb-multi-item.combobox-item-selected { background: transparent; font-weight: 500; color: var(--text-primary); }
.cb-multi-item.combobox-item-selected.combobox-item-active, .cb-multi-item.combobox-item-selected:hover { background: var(--primary-light); }
.cb-check {
    flex: 0 0 auto; width: 16px; height: 16px; box-sizing: border-box; border-radius: 4px;
    border: 1.5px solid var(--primary-border); background: #fff; color: transparent;
    display: inline-flex; align-items: center; justify-content: center;
}
.cb-check svg { width: 12px; height: 12px; display: block; }
.combobox-item-selected .cb-check { background: var(--primary); border-color: var(--primary); color: #fff; }
.cb-multi-text { overflow: hidden; text-overflow: ellipsis; }
/* 하나라도 골랐으면 트리거 글자를 강조 — '전체' 와 걸러진 상태가 한눈에 갈린다 */
.combobox.cb-multi.cb-multi-has:not(.combobox-open) .combobox-input { color: var(--primary); font-weight: 600; }
.combobox.cb-multi .combobox-input { text-overflow: ellipsis; }
/* 좁은 화면 — 아래에서 올라오는 시트(body 로 띄운다). 손가락 크기 항목 + [완료] */
.cb-multi-backdrop { position: fixed; inset: 0; background: rgba(15, 23, 42, .35); z-index: 1190; }
.cb-multi-dropdown.cb-multi-sheet {
    position: fixed !important; left: 0 !important; right: 0 !important; bottom: 0 !important; top: auto !important;
    width: auto !important; min-width: 0 !important; max-height: 70vh;
    border-radius: 14px 14px 0 0; border-bottom: 0; box-shadow: 0 -8px 28px rgba(0, 0, 0, .18);
    padding-bottom: env(safe-area-inset-bottom, 0);
}
/* 시트 맨 위 거르기 칸(v4.14.0) — 시트가 열리면 배경막이 원래 인풋을 덮으므로 "다시 탭해서 타자"는 여기서 한다.
   16px — iOS 는 16px 보다 작은 칸에 포커스가 가면 화면을 확대한다 */
.cb-multi-find { display: none; }
.cb-multi-sheet .cb-multi-find {
    display: block; flex: 0 0 auto; box-sizing: border-box; width: calc(100% - 20px); height: 40px; margin: 10px 10px 2px;
    padding: 0 12px; border: 1.5px solid var(--control-border); border-radius: 8px; font-size: 16px; font-family: inherit; background: #fff;
}
.cb-multi-sheet .cb-multi-find:focus { border-color: var(--primary); outline: none; }
.cb-multi-sheet .cb-multi-bar { padding: 8px 10px; }
.cb-multi-sheet .cb-multi-act { font-size: 14px; padding: 8px 10px; }
.cb-multi-sheet .cb-multi-done { display: inline-block; }
.cb-multi-sheet .cb-multi-item { padding: 12px 16px; font-size: 15px; }
.cb-multi-sheet .cb-check { width: 20px; height: 20px; }
.cb-multi-sheet .cb-check svg { width: 14px; height: 14px; }

/* ---------- 키트 표식 = 선 SVG (v4.16.1, cgrid-icon.js · cgrid-shell.js) ----------
   글자 기호(↕ ▲▼ ＋ ✕ ⌂ ▾)는 iOS 에서 컬러 이모지로 바뀌어 그려졌다(§2 머리). 크기 1em · 색 currentColor 라
   사이트가 `.sort-ar { font-size:10px; color:… }` 처럼 글자에 준 스타일이 그대로 먹는다.
   Tailwind preflight 의 `svg { display:block }` 을 이겨야 글자처럼 줄 안에 선다. */
.cg-sym { display: inline-block; flex: 0 0 auto; vertical-align: -0.125em; overflow: visible; }
.sort-ind .sort-ar { display: inline-flex; align-items: center; }
.row-move button { display: inline-flex; align-items: center; justify-content: center; }

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

/* 모달 겹·카드 바탕 (v4.19.1, ADR-005) — 키트 마크업이 Tailwind 유틸(fixed inset-0 · bg-black/40 · max-w-3xl · max-h-[90vh] …)에만 기대서
   Tailwind 없는 사이트(velysound)·프리컴파일에 그 클래스가 빠진 사이트에선 [자세히] 팝업이 스타일 없이 화면 아래에 붙었다.
   → 키트 표식(cg-modal-*)에 **Tailwind 와 같은 값**을 준다(Tailwind 있는 사이트는 모양이 그대로). 크기·여백 덮기는 아래 규칙들. */
.cg-modal-overlay {
    position: fixed; inset: 0; display: none; justify-content: center; align-items: center;
    background-color: rgb(0 0 0 / 0.4); padding: 1rem; overflow-y: auto; box-sizing: border-box;
}
.cg-modal-overlay.flex { display: flex; }
.cg-modal-overlay.hidden { display: none; }
.cg-modal-card {
    display: flex; flex-direction: column; box-sizing: border-box; width: 100%; max-width: 48rem; max-height: 90vh;
    margin-top: auto; margin-bottom: auto; background-color: #fff; border-radius: 0.75rem;
    box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
}
.cg-modal-head {
    display: flex; align-items: center; justify-content: space-between; box-sizing: border-box; flex-shrink: 0;
    height: 3rem; padding-left: 1.25rem; padding-right: 1.25rem; border-bottom: 1px solid #e2e8f0;
}
.cg-modal-card > .cg-modal-head > .cg-modal-title {   /* Tailwind preflight(h3 크기·여백 상속) + font-bold · text-slate-900 · truncate */
    margin: 0; font-size: inherit; font-weight: 700; color: #0f172a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cg-modal-body { box-sizing: border-box; flex: 1 1 0%; padding: 1.5rem; overflow-y: auto; }
.cg-modal-card > .cg-modal-footer { box-sizing: border-box; flex-shrink: 0; padding: 0.75rem 1.5rem; border-top: 1px solid #e2e8f0; }

/* 확인·알림·입력 창(cgrid.confirm·alert·prompt, cgrid-form.js) — 같은 이유로 키트 표식(cg-dlg-*)에 같은 값 (v4.19.1, 본문·단추는 v4.19.2) */
.cg-dlg {
    position: fixed; inset: 0; z-index: 300; display: none; justify-content: center; align-items: center;
    background-color: rgb(0 0 0 / 0.4); padding: 1rem; box-sizing: border-box;
}
.cg-dlg.flex { display: flex; }
.cg-dlg.hidden { display: none; }
.cg-dlg-card {
    display: flex; flex-direction: column; box-sizing: border-box; width: 100%; max-width: 24rem;
    background-color: #fff; border-radius: 0.75rem; box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
}
.cg-dlg-head {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; box-sizing: border-box; flex-shrink: 0;
    height: 3rem; padding-left: 1.25rem; padding-right: 1.25rem; border-bottom: 1px solid #f1f5f9;
}
.cg-dlg-head > .cg-dlg-title { margin: 0; font-size: inherit; font-weight: 700; color: #0f172a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cg-dlg-head > .cg-dlg-x {
    padding: 0; margin: 0; border: 0; background: transparent; cursor: pointer; font-family: inherit;
    color: #94a3b8; font-size: 1.875rem; line-height: 1;
}
.cg-dlg-head > .cg-dlg-x:hover { color: #0f172a; }
/* v4.19.2 — 본문을 읽기 좋게: 14px 회색 → 15px 진한 글색 · 줄간격 1.6 · 한글은 낱자에서 안 끊기게(keep-all, 너무 긴 낱말만 anywhere).
   `.cg-dlg .cg-dlg-body`(0,2,0) — Tailwind `text-sm`·사이트 `p { … }` 보다 이기게. 첫 줄 = 굵은 질문(cg-dlg-q), 나머지 = 설명(cg-dlg-detail). */
.cg-dlg .cg-dlg-body {
    padding: 1.25rem 1.25rem 1.375rem; font-size: 15px; line-height: 1.6; color: #1e293b; white-space: pre-line;
    word-break: keep-all; overflow-wrap: anywhere; text-align: left; max-height: 60vh; overflow-y: auto; box-sizing: border-box;
}
.cg-dlg .cg-dlg-body > p { margin: 0; font-size: inherit; line-height: inherit; color: inherit; }
.cg-dlg .cg-dlg-body > .cg-dlg-q { font-size: 16px; line-height: 1.5; font-weight: 700; color: #0f172a; }
.cg-dlg .cg-dlg-body > .cg-dlg-q + .cg-dlg-detail { margin-top: 0.5rem; }
.cg-dlg .cg-dlg-body > p[hidden] { display: none; }
.cg-dlg-input { padding: 0 1.25rem 1.25rem; box-sizing: border-box; }
.cg-dlg-input.hidden { display: none; }
.cg-dlg-foot {
    display: flex; gap: 0.5rem; justify-content: flex-end; box-sizing: border-box; flex-shrink: 0;
    padding: 0.75rem 1.25rem; border-top: 1px solid #f1f5f9;
}
/* v4.19.2 — 단추 역할을 모양으로: [확인] = 채운 주 단추(검정, 위험 동작은 빨강) · [취소] = 흰 보조 단추.
   예전엔 둘 다 흰 바탕이고 hover 가 검정 반전이라, 마우스가 [취소] 위에 있으면 **취소가 채워진 주 단추처럼** 보였다(colon-hub 실측).
   특정도 (0,3,1) — 사이트 `.btn-primary`·`.btn-light:hover`·`.btn:focus` 가 덮지 못하게. 초점은 **링만**(채움·색 반전 없음). */
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes,
.cg-dlg .cg-dlg-foot > button.cg-dlg-no {
    min-width: 76px; height: 40px; padding: 0 18px; justify-content: center; font-size: 14px; font-weight: 700;
    border-radius: var(--radius-sm, 8px); border-width: 1.5px; border-style: solid; box-shadow: none; outline: none;
}
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes { background: var(--primary, #111827); border-color: var(--primary, #111827); color: #fff; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes:hover { background: var(--primary-dark, #000); border-color: var(--primary-dark, #000); color: #fff; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes.is-danger { background: #dc2626; border-color: #dc2626; color: #fff; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes.is-danger:hover { background: #b91c1c; border-color: #b91c1c; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-no { background: #fff; border-color: #cbd5e1; color: #1e293b; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-no:hover { background: #f1f5f9; border-color: #94a3b8; color: #0f172a; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes:focus,
.cg-dlg .cg-dlg-foot > button.cg-dlg-no:focus { outline: none; box-shadow: none; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes:focus-visible,
.cg-dlg .cg-dlg-foot > button.cg-dlg-no:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
.cg-dlg .cg-dlg-foot > button.cg-dlg-yes.is-danger:focus-visible { outline-color: #dc2626; }
.cg-dlg .cg-dlg-head > .cg-dlg-x:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; border-radius: 4px; }
/* 휴대폰 폭 — 창은 화면 폭 - 32px, 단추는 반씩 나눠 크게(엄지로 누르기) */
@media (max-width: 480px) {
    .cg-dlg .cg-dlg-foot > button.cg-dlg-yes,
    .cg-dlg .cg-dlg-foot > button.cg-dlg-no { flex: 1 1 0; height: 44px; font-size: 15px; }
}

/* 모달 푸터 버튼 정렬 — **오른쪽부터, 삭제만 왼쪽**(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; }
/* v4.16.1 — 모달은 화면의 `[data-modal-footer]` **감싸개째** 푸터로 옮긴다. 감싸개가 그냥 div(블록)거나
   내용 폭만큼만 서는 flex 면 위 정렬이 안 먹었다 — 단추가 감싸개 안에 갇혀 삭제가 왼쪽으로 못 가고 간격도 제각각.
   → 감싸개를 푸터와 같은 줄(flex · 오른쪽 정렬 · 간격 8px)로 만들고 푸터 폭을 다 쓰게 한다.
   `:where()` 로 특정도 0 — 사이트가 감싸개에 준 클래스(flex · justify-* · gap-*)·인라인 style 이 항상 이긴다(마크업 안 바꿔도 됨). */
:where(.cg-modal-footer > [data-modal-footer]) {
    display: flex; align-items: center; gap: 8px; justify-content: flex-end; flex: 1 1 auto; min-width: 0;
}

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

/* 모달 전체화면 (v4.15.0, §1.4) — 헤더 X 옆 단추. 겹(overlay)에 cg-modal-full 이 붙으면
   카드가 창(셸 iframe 이면 그 iframe — 셸이 iframe 을 창 전체로 키운다)을 꽉 채운다.
   Tailwind 의 p-4 · max-w-3xl · max-h-[90vh] · rounded-xl · my-auto 를 이겨야 해서 !important. */
/* 모달 머리 단추 (v4.17.1, §1.4) — 전체화면 · X 는 **같은 32px 상자 · 같은 세로 중심선 · 간격 4px**, 제목줄(48px) 세로 가운데.
   🚨 예전: X 가 Tailwind `text-3xl leading-none` 글자 줄(19.5×30) 안의 인라인 SVG 라 아이콘이 5px 아래로 처지고,
      전체화면(32×32)과 상자 크기·간격(12px)이 달라 "전체화면이 X 보다 위에 붙어" 보였다.
   → 머리 줄 정렬도 키트가 맡는다(Tailwind 없는 사이트도 같게). 특정도를 올려 사이트 Tailwind 의 gap-3 을 이긴다. */
.cg-modal-card > .cg-modal-head { display: flex; align-items: center; gap: 4px; }
.cg-modal-card > .cg-modal-head > .cg-modal-title { min-width: 0; margin-right: 8px; }
.cg-modal-fullbtn, .cg-modal-close {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
    width: 32px; height: 32px; padding: 0; margin: 0; border: 0; border-radius: 6px;
    background: transparent; color: #94a3b8; cursor: pointer; font-size: 22px; line-height: 1;
}
.cg-modal-fullbtn { margin-left: auto; }
.cg-modal-fullbtn[hidden] + .cg-modal-close { margin-left: auto; }   /* 전체화면을 끈 화면(data-modal-nofull)도 X 는 오른쪽 끝 */
.cg-modal-fullbtn:hover, .cg-modal-close:hover { color: #0f172a; background: #f1f5f9; }
.cg-modal-fullbtn:focus-visible, .cg-modal-close:focus-visible { outline: 2px solid var(--primary, #111827); outline-offset: 1px; }
.cg-modal-fullbtn[hidden] { display: none; }
.cg-modal-fullbtn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.cg-modal-close svg { display: block; width: 20px; height: 20px; vertical-align: 0; }
.cg-modal-fullbtn .cg-ic-min { display: none; }
.cg-modal-full .cg-modal-fullbtn .cg-ic-max { display: none; }
.cg-modal-full .cg-modal-fullbtn .cg-ic-min { display: block; }
.cg-modal-overlay.cg-modal-full { padding: 0 !important; overflow: hidden !important; }
.cg-modal-overlay.cg-modal-full > .cg-modal-card {
    width: 100% !important; max-width: none !important; height: 100% !important; max-height: none !important;
    margin: 0 !important; border-radius: 0 !important; box-shadow: none !important;
}

/* 모바일 모달 여백(v4.17.2, §2.19 ⑦) — 바깥 16px(p-4) + 몸통 24px(p-6) + 화면 폼 상자 여백이 겹쳐 빈 곳이 컸다(postring 이 덮던 것).
   마크업이 Tailwind 패딩이라 !important. 전체화면(cg-modal-full)은 바깥 여백 0 그대로 */
@media (max-width: 768px) {
    .cg-modal-overlay:not(.cg-modal-full) { padding: 8px !important; }
    .cg-modal-body { padding: 14px 14px 16px !important; }
    .cg-modal-head { padding-left: 14px !important; padding-right: 8px !important; }
    .cg-modal-footer { padding: 10px 14px !important; }
}

/* 모달 로딩 — 클릭 즉시 열고 내용은 뒤따라 채운다(빈 시간에 사용자가 또 누르지 않게) */
.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; }
    /* 행 카드(v4.17.2, §2.19 ⑦) — 카드마다 또렷한 테두리·12px 모서리·12px 간격·옅은 그림자(colon-hub·postring 이 각자 덮던 것) */
    .data-table tbody tr { border: 1px solid var(--gk-mcard-border); border-radius: 12px; margin-bottom: 12px; box-shadow: var(--gk-mcard-shadow); 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) */
    .filter-more.is-open { flex-direction: column; align-items: stretch; }   /* 상세 칸도 한 줄에 하나 */
    .filter-more-toggle, .filter-more-btn { width: 100%; justify-content: center; }
    /* 버튼 줄(v4.14.0) — 좁을 때 키트가 [상세 필터]와 버튼열을 filter-bar 맨 끝 한 줄로 옮긴다(cgrid-grid.js layoutFoot).
       위 필터와의 간격 = filter-bar 의 줄 간격 그대로. 상세 필터는 왼쪽, [초기화][조회]는 오른쪽 */
    .filter-foot { display: flex; align-items: center; gap: 8px; width: 100%; grid-column: 1 / -1; }   /* 앱이 filter-bar 를 grid 로 바꿔도 한 줄 통째로 */
    .filter-foot .filter-more-toggle { width: auto; flex: 0 0 auto; }
    .filter-foot .filter-more-btn { width: auto; }
    .admin-search-card .filter-foot .admin-search-actions { flex: 1 1 auto; justify-content: flex-end; align-self: auto; gap: 8px; }
    .admin-search-card .filter-foot .admin-search-actions .btn { flex: 0 0 auto; height: 34px; min-height: 0; }
    /* 상세 필터가 없고 [조회]도 있으면 예전처럼 둘이 반씩 */
    .admin-search-card .filter-foot.no-more .admin-search-actions:not(.is-live) .btn { flex: 1 1 0%; }
    /* 검색카드 필터 격자(v4.17.2, §2.19 ⑦) — 한 줄에 하나씩 좁게 서서 오른쪽이 텅 비었다(colon-hub·postring 이 각자 덮던 것).
       화면 폭에 따라 한 줄 2~3칸(칸 최소 120px), 검색어(검색유형+검색값) 줄·상세 필터 펼친 칸·버튼 줄만 한 줄 통째, 칸 안 컨트롤은 칸 폭을 꽉 채운다 */
    .admin-search-card .filter-bar { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
    .admin-search-card .filter-group { width: auto; min-width: 0; align-items: stretch; }
    .admin-search-card .filter-group:has(.filter-row) { grid-column: 1 / -1; }
    .admin-search-card .filter-group > .combobox, .admin-search-card .filter-group > select, .admin-search-card .filter-group > input { width: 100% !important; min-width: 0; }
    .admin-search-card .filter-group .combobox input { width: 100%; }
    .admin-search-card .filter-more-toggle { align-self: end; }
    .admin-search-card .filter-more { grid-column: 1 / -1; }
    .admin-search-card .filter-more.is-open { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
    .admin-search-card .filter-foot { grid-column: 1 / -1; }

    /* ----- 촘촘한 카드(opt-in, 표 data-card="compact") — 라벨:값 한 줄씩 대신
       [키] / [제목] / [나머지를 한 줄에 이어서]. 열 data-card: key·title(맨 위 한 줄씩) · bare(라벨 없이 값만) · hide(카드에서 뺌). 순번은 뺀다 ----- */
    .data-table[data-card="compact"] tbody tr { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; padding: 12px 14px; }
    .data-table[data-card="compact"] td {
        display: inline-flex; justify-content: flex-start; gap: 5px; text-align: left !important;
        padding: 0 !important; border: none !important; font-size: 12px; color: var(--text-secondary);   /* 기본 칸의 !important 패딩·밑줄을 이긴다 */
    }
    .data-table[data-card="compact"] td::before { margin-right: 0; font-weight: 500; font-size: 11px; color: #94a3b8; }
    .data-table[data-card="compact"] tbody tr:last-child td { border-bottom: none !important; }   /* 합계행용 '마지막 행 밑줄 유지'(아래 규칙)를 카드에선 끈다 */
    .data-table[data-card="compact"] td[data-blank],
    .data-table[data-card="compact"] td.seq-cell,
    .data-table[data-card="compact"] td[data-card="hide"] { display: none; }
    .data-table[data-card="compact"] td[data-card="key"] { order: -2; flex: 1 1 100%; font-size: 11px; }
    .data-table[data-card="compact"] td[data-card="title"] { order: -1; flex: 1 1 100%; font-size: 14px; font-weight: 600; color: var(--text-primary); }
    .data-table[data-card="compact"] td[data-card="key"]::before,
    .data-table[data-card="compact"] td[data-card="title"]::before,
    .data-table[data-card="compact"] td[data-card="bare"]::before { content: none; }
    .data-table[data-card="compact"] td.grid-empty-cell { flex: 1 1 100%; justify-content: center; }

}

/* ---------- 기간(부터~까지) 필터 (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; }
/* v4.17.3 — 좁은 화면에선 9.5rem 두 칸(304px)이 검색카드(390 폭에서 266px)를 넘어 오른쪽으로 삐져나갔다.
   두 칸이 줄 폭을 반씩 나눠 쓴다. iOS 날짜 칸은 값이 가운데로 몰려 왼쪽 정렬로 맞춘다 */
@media (max-width: 768px) {
    .filter-row > input[type="month"], .filter-row > input[type="date"] { flex: 1 1 0; width: auto; min-width: 0; }
    .filter-row > input[type="month"]::-webkit-date-and-time-value,
    .filter-row > input[type="date"]::-webkit-date-and-time-value { text-align: left; }
}

/* ---------- 그리드 합계행 (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; }
/* ↻ 글자 대신 선 SVG(v4.16.1, §2 이모지 금지) — 마스크라 글자색(currentColor)을 그대로 따른다 */
.data-table td.cell-stale::before {
    content: ""; display: inline-block; width: 1em; height: 1em; margin-right: .25em; vertical-align: -0.125em;
    font-style: normal; background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8'/%3E%3Cpath d='M21 3v5h-5'/%3E%3C/svg%3E") center / contain no-repeat;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8'/%3E%3Cpath d='M21 3v5h-5'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* 상태 컬럼 — 맨 왼쪽, 수정모드에서만 (§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; }
    .grid-toolbar > .gk-row, .grid-actions { flex-wrap: wrap; min-width: 0; }   /* v4.17.2 §2.12 — 화면 단추가 많아도 옆으로 넘치지 않게 줄바꿈 */
    /* v4.17.2 §2.19 ⑦ — 휴대폰에선 엑셀 내려받기·페이지 크기를 뺀다(받아도 열 데가 마땅찮고 도구 줄만 두 줄로 는다, 개수는 기본값).
       남는 도구(컬럼·수정 등 단추)가 없으면 도구 묶음째 숨긴다. 통째로 숨기려면 사이트가 .grid-tools { display:none } */
    .grid-excel, .grid-tools .combobox.cb-compact { display: none !important; }
    .grid-tools:not(:has(> .btn:not(.grid-excel))) { display: none !important; }
    .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-work 계산서·작업)
   (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-work 계산서에서 그랬다). */
.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; }

/* ===== 탭 도구 캡슐 · 접기 (v4.18.0, §2.13) — cgrid-shell.js tabTools 가 짓는다 =====
   .cs-tools-host(사이트 컨테이너) > .cs-tools(캡슐) > [.cs-tools-fold ‹/›, .cs-tools-body > 단추 ┊ 단추] (+ .cs-tools-div 캡슐 뒤 세로선)
   색은 탭 바(다크) 기준. 밝은 탭 바면 변수만 바꾼다: --cs-tools-bg · --cs-tools-line · --cs-tools-fg · --cs-tools-hover · --cs-tools-h */
.cs-tools {
    flex: 0 0 auto; display: inline-flex; align-items: center; box-sizing: border-box;
    height: var(--cs-tools-h, 38px); padding: 0 3px; margin: 0;
    border-radius: 12px; background: var(--cs-tools-bg, rgba(255, 255, 255, .07));
    border: 1px solid var(--cs-tools-line, rgba(255, 255, 255, .16));
}
/* 사이트 단추 클래스(.tc-btn · .ws-controls button …)보다 세게 — 크기·색을 캡슐 표준으로 */
.cs-tools .cs-tools-fold, .cs-tools .cs-tools-body > button {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
    width: 30px; min-width: 30px; height: 30px; padding: 0; margin: 0;
    border: 0; border-radius: 8px; background: transparent; box-shadow: none;
    color: var(--cs-tools-fg, rgba(255, 255, 255, .82)); font-size: 16px; line-height: 1; cursor: pointer;
    transition: background .15s, color .15s;
}
.cs-tools .cs-tools-fold:hover, .cs-tools .cs-tools-body > button:hover:not(:disabled) { background: var(--cs-tools-hover, rgba(255, 255, 255, .14)); color: #fff; }
.cs-tools .cs-tools-body > button:disabled { opacity: .35; cursor: default; }
.cs-tools .cs-tools-fold:focus-visible, .cs-tools .cs-tools-body > button:focus-visible { outline: 2px solid rgba(255, 255, 255, .6); outline-offset: -2px; }
/* 아이콘 = 선 18px (마크업의 width 속성 14·16 을 CSS 가 이긴다) */
.cs-tools .cs-tools-body > button svg, .cs-tools .cs-tools-fold svg {
    display: block; width: 18px; height: 18px; fill: none; stroke: currentColor;
    stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.cs-tools .cs-ico { display: inline-flex; }
.cs-tools .cs-tools-fold svg { transition: transform .25s ease; }
.cs-tools.is-open .cs-tools-fold svg { transform: rotate(180deg); }      /* 접힘 ‹ · 펼침 › */
/* 옆으로 미끄러지듯 — max-width + opacity. 접히면 캡슐 안에 접기 단추 하나 */
.cs-tools-body {
    display: flex; align-items: center; gap: 2px; overflow: hidden; margin: 0; padding: 0; border: 0;
    max-width: 420px; opacity: 1; transition: max-width .28s ease, opacity .2s;
}
.cs-tools-body.is-folded { max-width: 0; opacity: 0; pointer-events: none; }
.cs-tools-sep { flex: 0 0 auto; width: 1px; height: 18px; margin: 0 4px; background: var(--cs-tools-sep, rgba(255, 255, 255, .2)); }
/* 처음 그림·상태 복원은 움직이지 않는다 */
.cs-tools-still, .cs-tools-still * { transition: none !important; }
/* 캡슐 뒤 세로선 — 탭 도구와 앱 단추(채팅·알림 등)가 한 덩어리로 보이지 않게 */
.cs-tools-div { flex: 0 0 auto; align-self: center; width: 1px; height: 26px; margin: 0 0 0 10px; background: var(--cs-tools-sep, rgba(255, 255, 255, .2)); }
.cs-tools-host { align-items: center; }
/* 키트 표준 탭 바(.cs-bar 는 바닥 맞춤·위 여백 6px) — 캡슐은 막대 전체의 세로 가운데로.
   ⚠️ transform 으로 올리지 않는다 — 쌓임 맥락이 생겨 떠 있는 상자가 탭 화면(iframe) 밑으로 깔린다. top 으로 */
.cs-bar > .cs-tools-host { align-self: center; padding-top: 0; padding-bottom: 0; position: relative; top: var(--cs-tools-lift, -3px); }
/* 첫 그림 깜빡임 막기 — 컨테이너에 data-cs-tools 를 달면 셸이 캡슐을 지을 때까지 안을 숨긴다 */
div[data-cs-tools]:not(.cs-tools-host):not(.cs-tools-none) > * { display: none !important; }
/* 모바일 셸(§2.19)에선 탭 도구가 드롭다운 안에 있다 — 캡슐·세로선은 숨긴다 */
@media (max-width: 768px) {
    html.cgm-on .cs-tools, html.cgm-on .cs-tools-div { display: none !important; }
}

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

/* ============================================================
   모바일 셸 (v4.16.0, ADMIN_PAGE_RULE §2.19) — cgrid-shell.js 의 mobileShell 과 짝.
   사이트 클래스가 아니라 **JS 가 붙이는 표식(cgm-*)만** 본다 — 셸 마크업이 사이트마다 달라서.
   전부 `html.cgm-on`(셸 화면) + 768px 이하에서만 — PC 는 아무것도 바뀌지 않는다.
   색: --cgm-head(헤더 색, 안 정하면 JS 가 탭 줄 바탕색으로 채운다) · 상자 글자색 = --text-primary.
   ⚠️ 셸 높이를 늘리지 않는다(100dvh 그대로) — iOS 26 홈 화면 앱에서 탭 줄이 사라졌다. 아래 여백만 뺀다(vp-short).
   ⚠️ 이 블록은 사이트 기본 규칙보다 **뒤에서 이겨야** 한다 — 서랍 위치는 !important(사이트 사이드바가 id·transform 으로 잡혀 있어도),
      나머지는 `html.cgm-on .cgm-*` 특이성으로 이긴다. 사이트가 크기만 바꾸고 싶으면 같은 특이성 이상으로 뒤에 쓴다.
   ============================================================ */
.cgm-menu, .cgm-pick, .cgm-pick-menu, .cgm-dim, .cgm-backdrop, .cgm-edge { display: none; }
@keyframes cgm-pop { from { opacity: 0; transform: translateY(4px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes cgm-fade { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 768px) {
    /* ④ 셸 바탕 = 헤더 색 — 사파리는 주소창 둘레를 페이지 바탕색으로 칠한다(theme-color 는 요즘 안 쓴다). 본문만 밝게 */
    html.cgm-on, html.cgm-on body { background: var(--cgm-head, #0f172a); }
    html.cgm-on .cgm-hide { display: none !important; }
    /* 셸 높이 = 100dvh(앞은 대체값) — 사파리 아래 도구줄 밑으로 탭 줄이 숨지 않게. 사이드바는 서랍(fixed)이라 흐름에서 빠진다 */
    html.cgm-on .cgm-root { display: block; height: 100vh; height: 100dvh; overflow: hidden; animation: none; }
    html.cgm-on .cgm-main {
        display: flex; flex-direction: column; box-sizing: border-box; min-height: 0;
        height: 100vh; height: 100dvh;
        padding-top: env(safe-area-inset-top, 0px);           /* 위 안전 영역 = 헤더 색 띠(상태줄 자리) */
        background: var(--cgm-head, #0f172a);
    }
    /* ① 본문 위 · 탭 줄 아래 — 같은 마크업에 순서만 바꾼다 */
    html.cgm-on .cgm-wsbox { order: 1; flex: 1 1 auto; min-height: 0; }
    html.cgm-on .cgm-bar {
        order: 2; flex: 0 0 auto; position: relative; display: flex; flex-wrap: nowrap; align-items: center; gap: 4px;
        height: auto; min-height: 0; box-sizing: border-box; overflow: visible;
        /* 아래 = 6px + 홈 막대(34pt)의 절반 — 통째로 띄우면 94pt 가 되어 화면 아래를 너무 먹었다 */
        padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px) / 2);
        box-shadow: 0 -1px 0 rgba(255, 255, 255, .06), 0 -6px 18px rgba(15, 18, 38, .18);
    }
    html.cgm-on.vp-short .cgm-bar { padding-bottom: 6px; }   /* iOS 26 홈 화면 앱 — 홈 막대가 이미 그리는 영역 밖 */
    /* 표식(data-m) 없는 탭 줄 자식은 숨긴다 — 가로 탭 줄·탭 도구 캡슐·이름 글자 등. 묶음은 풀어 단추마다 order */
    html.cgm-on .cgm-bar > :not(.cgm-keep), html.cgm-on .cgm-flat > :not(.cgm-keep) { display: none !important; }
    html.cgm-on .cgm-flat { display: contents !important; }
    html.cgm-on .cgm-flat::before, html.cgm-on .cgm-flat::after { display: none !important; }   /* 묶음 앞 세로선 등 */
    html.cgm-on .cgm-bar .cgm-keep:not(.cgm-flat) { align-self: center; margin-top: 0; margin-bottom: 0; }
    /* 순서 고정: 프로필 · 도움말 · [지금 탭 ▾] · 앱 단추들 · ☰ */
    html.cgm-on .cgm-r-profile { order: 1; }
    html.cgm-on .cgm-r-help { order: 2; }
    html.cgm-on .cgm-pick { order: 4; }
    html.cgm-on .cgm-r-app { order: 5; }
    html.cgm-on .cgm-r-keep { order: 6; }
    html.cgm-on .cgm-menu { order: 9; }

    /* ☰ — 사이드바 열기 전용(다른 메뉴에 햄버거 모양 금지) */
    html.cgm-on .cgm-menu {
        display: inline-flex; flex: 0 0 auto; flex-direction: column; justify-content: center; align-items: center; gap: 4px;
        width: 40px; height: 40px; margin: 0; padding: 0; border: 0; border-radius: 10px; background: transparent; cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }
    html.cgm-on .cgm-menu:active { background: rgba(255, 255, 255, .12); }
    html.cgm-on .cgm-menu span { display: block; width: 16px; height: 2px; background: #fff; border-radius: 2px; }

    /* ② 탭 드롭다운 단추 — 남은 폭 전부, 긴 이름은 말줄임. 화살표 = 펼쳐질 방향(닫힘 ▴ · 열림 ▾) */
    html.cgm-on .cgm-pick { display: flex; flex: 1 1 auto; min-width: 0; position: relative; margin: 0 2px; }
    .cgm-pick-btn {
        flex: 1 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 8px; height: 42px; padding: 0 10px 0 14px;
        border: 1px solid rgba(255, 255, 255, .18); border-radius: 12px; background: rgba(255, 255, 255, .08); color: #fff;
        font-family: inherit; font-size: 15px; font-weight: 700; cursor: pointer; transition: background .15s, border-color .15s;
    }
    .cgm-pick-btn[aria-expanded="true"] { background: rgba(255, 255, 255, .16); border-color: rgba(255, 255, 255, .36); }
    .cgm-pick-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .cgm-pick-n { flex: 0 0 auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 9999px; background: rgba(255, 255, 255, .18); font-size: 11.5px; line-height: 20px; text-align: center; box-sizing: border-box; }
    .cgm-pick-n[hidden] { display: none; }
    .cgm-pick-caret { flex: 0 0 auto; width: 18px; height: 18px; margin-left: auto; fill: none; stroke: rgba(255, 255, 255, .75); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; transform: rotate(180deg); transition: transform .2s; }
    .cgm-pick-btn[aria-expanded="true"] .cgm-pick-caret { transform: none; }
    /* 상자 = 화면 너비(좌우 8px), 막대 바로 위(위치는 JS place — 막대의 실제 자리). 뒤는 딤드, 연 단추는 딤드 위 */
    html.cgm-on .cgm-pick-menu {
        display: block; position: fixed; left: 8px; right: 8px; bottom: calc(62px + env(safe-area-inset-bottom, 0px) / 2); z-index: 92;
        max-height: calc(100dvh - 90px - env(safe-area-inset-top, 0px)); overflow-y: auto; box-sizing: border-box;
        padding: 6px; border-radius: 14px; background: #fff; color: var(--text-primary, #1e293b);
        box-shadow: 0 18px 44px rgba(0, 0, 0, .3); transform-origin: bottom left; animation: cgm-pop .2s cubic-bezier(.2, 1.15, .4, 1) both;
    }
    html.cgm-on .cgm-pick-menu[hidden] { display: none; }
    html.cgm-on.cgm-pick-open .cgm-dim { display: block; position: fixed; inset: 0; z-index: 85; background: rgba(15, 18, 38, .42); animation: cgm-fade .18s both; }
    html.cgm-on.cgm-pick-open .cgm-pick { z-index: 91; }
    .cgm-pick-h { padding: 8px 10px 4px; font-size: 11.5px; font-weight: 700; color: #94a3b8; }
    .cgm-pick-h:not(:first-child) { margin-top: 4px; border-top: 1px solid #eef0f5; padding-top: 10px; }
    .cgm-pick-row {
        display: flex; align-items: center; gap: 12px; width: 100%; min-height: 46px; padding: 0 6px 0 12px; box-sizing: border-box;
        border: 0; border-radius: 10px; background: transparent; color: inherit;
        font-family: inherit; font-size: 15px; font-weight: 600; text-align: left; cursor: pointer;
    }
    .cgm-pick-row:active, .cgm-pick-row:hover { background: #f3f5f9; }
    .cgm-pick-row.is-on { background: var(--primary-light, #eef0f6); font-weight: 800; }
    .cgm-pick-row:disabled { opacity: .4; cursor: default; background: transparent; }
    .cgm-ico { flex: 0 0 22px; display: inline-flex; color: #475569; }
    .cgm-ico svg { display: block; width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
    .cgm-pick-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: #cbd5e1; }
    .cgm-pick-row.is-on .cgm-pick-dot { background: var(--cgm-accent, #2563eb); }
    .cgm-pick-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .cgm-pick-x { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; padding: 0; border: 0; border-radius: 9px; background: transparent; color: #94a3b8; cursor: pointer; }
    .cgm-pick-x:active, .cgm-pick-x:hover { background: #e2e8f0; color: var(--text-primary, #1e293b); }
    .cgm-pick-x svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }

    /* ② 사이트의 떠 있는 상자(프로필 메뉴·알림 상자 등)도 위로 — data-m-pop(왼쪽 기준) / data-m-pop="wide"(화면 너비) */
    html.cgm-on [data-m-pop] { top: auto; bottom: calc(100% + 12px); left: 0; right: auto; transform-origin: bottom left; }
    html.cgm-on [data-m-pop="wide"] {
        position: fixed; left: 8px; right: 8px; width: auto; top: auto;
        bottom: calc(62px + env(safe-area-inset-bottom, 0px) / 2);
        max-height: calc(100dvh - 90px - env(safe-area-inset-top, 0px)); transform-origin: bottom center;
    }

    /* ③ 사이드바 = 오른쪽 서랍. PC 접힘(is-collapsed 등)이 따라와도 같은 규칙으로 덮는다 */
    html.cgm-on .cgm-sb {
        position: fixed !important; z-index: 70 !important; top: 0 !important; bottom: 0 !important; left: auto !important; right: 0 !important;
        width: min(72vw, 264px) !important; min-width: 0 !important; max-width: none !important; height: auto !important; flex: none !important;
        margin: 0 -320px 0 0 !important; transform: none !important; translate: none !important; visibility: visible !important;
        box-sizing: border-box; padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px);
        box-shadow: -8px 0 24px rgba(0, 0, 0, .25); transition: margin-right .2s cubic-bezier(.2, .8, .2, 1) !important;
    }
    html.cgm-on.cgm-open .cgm-sb { margin-right: 0 !important; }
    html.cgm-on.cgm-open .cgm-backdrop, html.cgm-on .cgm-backdrop.is-drag {
        display: block; position: fixed; inset: 0; z-index: 65; background: rgba(0, 0, 0, .5);
        -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
    }
    /* 오른쪽 가장자리 14px 띠 — 왼쪽으로 밀면 서랍. 높이는 탭 줄 위까지(☰ 를 덮지 않게), 세로 스크롤은 그대로 */
    html.cgm-on .cgm-edge { display: block; position: fixed; z-index: 60; top: 0; right: 0; bottom: calc(64px + env(safe-area-inset-bottom, 0px)); width: 14px; touch-action: pan-y; }
    html.cgm-on.cgm-open .cgm-edge { display: none; }
    /* 모달 열림 중엔 떠 있는 셸 요소가 모달 위로 올라오지 않게(§2.12) */
    body.modal-open .cgm-edge { display: none; }
}
@media (max-width: 768px) and (prefers-reduced-motion: reduce) {
    html.cgm-on .cgm-sb { transition: none !important; }
    html.cgm-on .cgm-pick-menu, html.cgm-on.cgm-pick-open .cgm-dim { animation: none; }
}

/* ---------- 도움말(?) — 설명 글 접기 (v4.17.0, cgrid-help.js · ADMIN_PAGE_RULE §2.18) ----------
   키트는 **끈 동안 숨기기만** 한다. 켜면 사이트가 준 원래 display(flex·block 등)로 돌아온다 —
   켤 때 display 를 키트가 정하면 사이트의 안내 상자(flex) 모양이 깨진다.
   색·크기는 토큰으로 열어 둔다: --cg-help-ring(켤 때 테두리 강조) · --cg-help-on-bg/-fg(켜진 단추). */
:root {
    --cg-help-ring: 0 0 0 3px rgba(59, 130, 246, .12);
    --cg-help-on-bg: var(--primary);
    --cg-help-on-fg: #fff;
}
html:not(.cg-help-on) .cg-help { display: none !important; }
html.cg-help-on .cg-help { animation: cg-help-in .32s cubic-bezier(.22, 1, .36, 1) both; box-shadow: var(--cg-help-ring); }
@keyframes cg-help-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.cg-help-bar { display: flex; align-items: center; justify-content: flex-start; gap: 6px; margin: 0 0 10px; }
.cg-help-btn { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 11px 0 9px; border: 1px solid var(--border); border-radius: 9999px;
    background: var(--card-bg); color: var(--text-secondary); font-size: 12.5px; font-weight: 700; line-height: 1; cursor: pointer; white-space: nowrap;
    transition: background .15s, color .15s, border-color .15s, transform .15s; }
.cg-help-btn .cg-sym { width: 16px; height: 16px; }
.cg-help-btn:hover { border-color: var(--primary-border); color: var(--text-primary); transform: translateY(-1px); }
.cg-help-btn.is-on { background: var(--cg-help-on-bg); border-color: var(--cg-help-on-bg); color: var(--cg-help-on-fg); }
/* 팝업 제목 옆 = 아이콘만 작게 */
.cg-help-btn-modal { height: 28px; margin-left: 0; padding: 0 7px; flex: 0 0 auto; align-self: center; }   /* 제목 옆 — 제목과 간격은 제목 margin-right 8px(v4.17.1) */
.cg-help-btn-modal span { display: none; }
.cg-modal-head .cg-modal-title + .cg-help-btn-modal { margin-right: auto; }
@media (max-width: 640px) { .cg-help-btn span { display: none; } .cg-help-btn { padding: 0 8px; } }
@media (prefers-reduced-motion: reduce) { html.cg-help-on .cg-help { animation: none; } .cg-help-btn { transition: none; } }

/* ---------- 터치 기기 확대 막기 (v4.17.2, ADMIN_PAGE_RULE §2.19 ⑦) ----------
   아이폰은 글자 16px 미만 입력칸을 누르면 화면을 확대한다. viewport 에 maximum-scale 을 걸면 손가락 확대까지 막히니(§2.19 ④),
   터치 기기에서 입력칸 글자만 16px 로 올린다. touch-action: manipulation = 두 번 톡 확대만 끄고 손가락 벌려 확대는 남긴다 */
@media (hover: none) and (pointer: coarse) {
    input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea, .combobox input { font-size: 16px !important; }
}
html, body { touch-action: manipulation; }

/* ---------- 도움말 글 항목형 · [자세히] 팝업 (v4.19.0, cgrid-help.js [data-help-doc] · ADMIN_PAGE_RULE §2.18) ----------
   colon-hub 0148(help.css)과 같은 모양 — 굵은 이름 — 요약 / 점 · 굵은 소제목 · 한 줄 설명 / 팝업은 항목마다 소제목 줄 + 설명 줄, 줄마다 구분선.
   상자(.note 등) 모양·숨김은 사이트·키트 .cg-help 그대로 — 여기선 안쪽만. 색은 토큰. */
:root {
    --cg-hd-strong: var(--text-primary);
    --cg-hd-dim: #8a96ad;              /* 대시·점 */
    --cg-hd-dot-modal: #4a5a80;
    --cg-hd-more-border: #c9d3ea;
    --cg-hd-line: #eef1f6;
}
.cg-hd-row { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1; }
.cg-hd:not(.is-pop) .cg-hd-row { align-items: flex-start; }
.cg-hd-main { flex: 1; min-width: 0; }
.cg-hd-sum { margin: 0; }
.cg-hd-sum > b { color: var(--cg-hd-strong); }
.cg-hd-dash { color: var(--cg-hd-dim); }
.cg-hd-list { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 4px; }
.cg-hd-list li { position: relative; padding-left: 13px; line-height: 1.55; }
.cg-hd-list li::before { content: ''; position: absolute; left: 2px; top: .68em; width: 4px; height: 4px; border-radius: 9999px; background: var(--cg-hd-dim); }
.cg-hd-list li > b { font-weight: 700; color: var(--cg-hd-strong); margin-right: 4px; }
.cg-hd-more { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 2px; align-self: center; height: 28px; padding: 0 8px 0 11px;
    border: 1px solid var(--cg-hd-more-border); border-radius: 9999px; background: var(--card-bg, #fff); color: var(--text-secondary); font-size: 12.5px; font-weight: 600;
    white-space: nowrap; cursor: pointer; }
.cg-hd-more:hover { border-color: var(--text-secondary); color: var(--cg-hd-strong); }
.cg-hd-more .cg-sym { width: 14px; height: 14px; }
.cg-hd-modal { font-size: 14px; line-height: 1.65; color: var(--text-secondary); }
.cg-hd-modal .cg-hd-lead { margin: 0 0 12px; color: var(--cg-hd-strong); }
.cg-hd-list.is-modal { margin: 0; gap: 0; border-top: 1px solid var(--border); }
.cg-hd-list.is-modal li { padding: 10px 2px 10px 15px; border-bottom: 1px solid var(--cg-hd-line); }
.cg-hd-list.is-modal li::before { top: calc(10px + .78em); width: 5px; height: 5px; background: var(--cg-hd-dot-modal); }
.cg-hd-list.is-modal li > b { display: block; margin: 0 0 1px; font-size: 13.5px; }
@media (max-width: 768px) {
    .cg-hd-sum, .cg-hd-list li { line-height: 1.6; }
    .cg-hd-modal { font-size: 14.5px; }
}

/* 자주 쓰는 필터 — 휴대폰(v4.20.0): 칩 한 줄(옆으로 밀기, 라벨 숨김) 위 · 단추 줄 아래([상세 필터] 왼쪽 ……… [초기화][조회] 오른쪽) */
@media (max-width: 768px) {
    .admin-search-card .fp-bar { flex-wrap: wrap; gap: 10px; padding-top: 10px; }
    .admin-search-card .fp-left { width: 100%; flex-basis: 100%; }
    .admin-search-card .fp-label { display: none; }
    .admin-search-card .fp-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
    .admin-search-card .fp-chips::-webkit-scrollbar { display: none; }
    .admin-search-card .fp-actions { width: 100%; }
    .admin-search-card .fp-actions .filter-more-toggle { margin-right: auto; width: auto; }
    .admin-search-card .fp-actions .filter-more-btn { width: auto; }
    .admin-search-card .fp-actions .admin-search-actions { margin-left: auto; flex: 0 0 auto; }
}
