/* ======================================================================
   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: #d5dbe3;   /* 폼 컨트롤·버튼 공통 보더 — 모든 인풋/셀렉트/버튼 통일. v4.23.0 #f0f0f0 → #d5dbe3(입력칸이 바탕과 구분이 안 됐다, §1.4.2) */
    --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);   /* 모바일 행 카드 옅은 그림자 */
}

/* ---------- [hidden] 은 늘 숨김 (v4.23.0, §1.4.2) ----------
   JS 가 el.hidden = true 로 숨겨도 같은 요소의 표시 클래스(Tailwind flex · block · grid, 사이트 .row 등)가 이겨서 보였다
   (postring 쿠폰 '기간 지정 / 발급 후 N일' 이 둘 다 보이던 것). 조건부 칸(data-show-if)도 이것에 기댄다. */
[hidden] { display: none !important; }

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

/* ---------- 체크박스 · 라디오 (v4.23.0, §1.4.2) ----------
   브라우저 기본 모양 대신 키트 모양 — 18px · 테마색(--primary) · 라벨과 세로 가운데. 그리드 행 선택·폼 선택지에 같이 쓰인다.
   (예전 v4.22 까지는 accent-color 만 — 브라우저마다 모양·크기가 달랐다) */
input[type="checkbox"], input[type="radio"] {
    -webkit-appearance: none; appearance: none;
    width: 18px; height: 18px; flex: 0 0 18px; margin: 0; box-sizing: border-box;
    display: inline-grid; place-content: center; vertical-align: middle;
    border: 1.5px solid #b6bfcc; background: #fff; cursor: pointer;
    transition: background-color .12s, border-color .12s, box-shadow .12s;
}
input[type="checkbox"] { border-radius: 5px; }
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:hover, input[type="radio"]:hover { border-color: var(--primary); }
input[type="checkbox"]:checked { background: var(--primary); border-color: var(--primary); }
input[type="checkbox"]:checked::after {
    content: ""; width: 10px; height: 5px; border: 2px solid #fff; border-top: 0; border-right: 0;
    transform: translateY(-1px) rotate(-45deg);
}
input[type="checkbox"]:indeterminate { background: var(--primary); border-color: var(--primary); }
input[type="checkbox"]:indeterminate::after { content: ""; width: 9px; height: 2px; background: #fff; border-radius: 1px; border: 0; transform: none; }
input[type="radio"]:checked { border-color: var(--primary); background: var(--primary); box-shadow: inset 0 0 0 4px #fff; }
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(17, 24, 39, .18); }
input[type="radio"]:checked:focus-visible { box-shadow: inset 0 0 0 4px #fff, 0 0 0 3px rgba(17, 24, 39, .18); }
input[type="checkbox"]:disabled, input[type="radio"]:disabled { opacity: .45; cursor: not-allowed; }

/* ---------- 검색/필터 영역 (진담 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%; }
/* v4.21.1 — 버튼열이 아래 줄(.fp-bar)로 내려가 오른쪽 기둥 자리가 비는데, 필터 칸은 제 너비 그대로라 줄 끝에 빈칸이 남았다
   (colon-work 실측). 켜진 카드에서는 칸이 그 줄 남는 너비를 나눠 갖고 안의 컨트롤도 칸을 꽉 채운다.
   v4.21.2 — 칸 기본 너비를 **내용 폭이 아니라 정해진 값(flex-basis)** 으로 — 내용 폭이면 검색어 줄 인라인 28rem·콤보 인라인 폭
   (옵션 글자가 길면 더 넓다)이 그대로 줄바꿈 기준이 돼, 카드 안쪽 1250px 에 4칸(검색어 720px)밖에 안 들어갔다.
   보통 칸(콤보·셀렉트) 10rem · 기간 18rem · 검색어(검색유형+입력) 19.5rem(입력 ≈ 12rem) → 1250px 에 검색어 + 고르기 5칸.
   줄바꿈은 이 값으로 하고, 남는 폭은 칸들이 나눠 가져 줄 끝까지 찬다. 안의 인라인 폭은 !important 로 눌러 칸을 따라가게 한다.
   v4.21.3 — **늘어나는 건 검색어 칸뿐**. 고르기(콤보·셀렉트·다중선택)·기간 칸까지 남는 폭을 나눠 가지니 검색어 없는 줄
   (colon-work 세금계산서 둘째 줄 진행상태·작업·귀속년월)에서 '작업' 콤보가 약 280px·귀속년월이 약 400px 로 쓸데없이 컸다
   (상한 22rem·30rem 은 걸려 있었지만 그 아래라 안 막았다). 고르기 10rem · 기간 18rem 은 제 기본 폭 그대로(flex 0 0),
   검색어(검색유형+입력)만 19.5rem 에서 줄 남는 폭을 흡수 → 검색어 있는 줄은 꽉, 없는 줄은 기본 폭으로 왼쪽부터(남는 자리는 빈다).
   배지 칩 줄(.badge-filter)은 칩 개수만큼 폭이 필요해 내용 폭 그대로. 좁은 화면은 아래 @media(grid) 그대로.
   v4.22.1 — 피커(v4.22.0)가 기간 칸에 붙이는 보이는 글자 칸(input.cg-pk-input, type=text)이 '검색어 칸' 판정에 걸려
   귀속년월 기간이 줄 남는 폭을 다 먹었다(colon-work 세금계산서 실측) → 검색어 판정에서 .cg-pk-input 을 뺀다. */
@media (min-width: 769px) {
    .admin-search-card.has-fp .filter-bar .filter-group:not(.filter-more-toggle) { flex: 0 0 10rem; min-width: 0; align-items: stretch; }
    .admin-search-card.has-fp .filter-bar .filter-group:has(> .filter-row > input[type="month"]),
    .admin-search-card.has-fp .filter-bar .filter-group:has(> .filter-row > input[type="date"]) { flex: 0 0 18rem; }
    .admin-search-card.has-fp .filter-bar .filter-group:has(> .filter-row > input[type="text"]:not(.cg-pk-input)) { flex: 1 1 19.5rem; }
    .admin-search-card.has-fp .filter-bar .filter-group:has(> .filter-row > .badge-filter) { flex: 0 0 auto; min-width: auto; }
    /* 묶음 — 상자는 안 칸 폭의 합(안 칸 width 로 정한다, flex-basis 는 상자 기본 너비에 안 먹는다). 안에 검색어 칸이 있을 때만 상자째 늘고 그 칸이 흡수 */
    .admin-search-card.has-fp .filter-bar .filter-set { flex: 0 0 auto; min-width: 0; }
    .admin-search-card.has-fp .filter-bar .filter-set:has(> .filter-group > .filter-row > input[type="text"]:not(.cg-pk-input)) { flex: 1 1 auto; }
    .admin-search-card.has-fp .filter-bar .filter-set > .filter-group { width: 10rem; }
    .admin-search-card.has-fp .filter-bar .filter-set > .filter-group:has(> .filter-row > input[type="month"]),
    .admin-search-card.has-fp .filter-bar .filter-set > .filter-group:has(> .filter-row > input[type="date"]) { width: 18rem; }
    .admin-search-card.has-fp .filter-bar .filter-set > .filter-group:has(> .filter-row > input[type="text"]:not(.cg-pk-input)) { width: 19.5rem; }
    /* 안의 컨트롤 — 인라인 폭(콤보 138px 등)을 눌러 칸을 따라가게(줄바꿈 기준은 위 flex-basis 라 칸이 커지지 않는다) */
    .admin-search-card.has-fp .filter-bar .filter-group > .combobox,
    .admin-search-card.has-fp .filter-bar .filter-group > select,
    .admin-search-card.has-fp .filter-bar .filter-group > input:not([type="checkbox"]):not([type="radio"]) { width: 100% !important; min-width: 0; max-width: 100%; }
    .admin-search-card.has-fp .filter-bar .filter-group .combobox input { width: 100%; }
    /* 한 줄 칸(검색어 = 검색유형+검색어 · 기간 · 표 밖 필터): 줄 인라인 28rem 을 누르고 칸 너비를 따른다.
       검색유형 콤보는 제 폭 그대로·검색어 입력이 흡수, 기간 두 칸은 반씩, 표 밖 select 하나면 꽉 */
    .admin-search-card.has-fp .filter-bar .filter-group > .filter-row { display: flex; width: 100% !important; min-width: 0; max-width: 100%; }
    .admin-search-card.has-fp .filter-bar .filter-row > input[type="text"] { flex: 1 1 auto; min-width: 0; }
    .admin-search-card.has-fp .filter-bar .filter-row > input[type="month"],
    .admin-search-card.has-fp .filter-bar .filter-row > input[type="date"],
    .admin-search-card.has-fp .filter-bar .filter-row > input.cg-pk-input { flex: 1 1 0; width: auto; min-width: 0; }
    .admin-search-card.has-fp .filter-bar .filter-row > .combobox:only-child,
    .admin-search-card.has-fp .filter-bar .filter-row > select:only-child { flex: 1 1 auto; width: 100% !important; min-width: 0; }
}
.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 = 색상반전(검흰↔흰검, 파흰↔흰파)으로 통일 + 1px 올라옴·옅은 그림자(v4.29.0 §2.21 움직임 — 맨 아래 블록) */
/* 기본 버튼 = 흰 바탕·검은 글씨. 보더는 전부 짙은 회색(--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; }

/* ---------- 공통 로딩 부품 (v4.25.0, cgrid-loading.js · ADMIN_PAGE_RULE §1.2.1) ----------
   자리 먼저 + 그 모양의 스켈레톤. is-wait = 아직 200ms 안 지남 → 자리(크기)는 차지하되 안 보인다(짧은 응답 깜빡임 방지). */
.cg-sk.is-wait, .grid-skeleton.is-wait { visibility: hidden; }
.cg-sk { display: flex; flex-direction: column; gap: 12px; padding: 4px 0; box-sizing: border-box; width: 100%; }
/* 영역이 grid·flex 그릇이어도(카드 줄) 스켈레톤은 한 칸이 아니라 그릇 전체를 쓴다 — 안 그러면 블록 4개가 첫 칸에 세로로 쌓인다 */
.cg-sk, .cg-load-err { grid-column: 1 / -1; flex: 1 1 100%; }
.cg-sk-over { position: absolute; inset: 0; z-index: 5; background: var(--card-bg, #fff); padding: 14px 16px; border-radius: inherit; overflow: hidden; }
.cg-sk-row { display: flex; gap: 16px; align-items: center; padding: 6px 0; border-bottom: 1px solid #eef1f5; }
.cg-sk-row .skeleton-bar { flex: 1 1 0; }
.cg-sk-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.cg-sk-card { display: flex; flex-direction: column; gap: 10px; padding: 16px; min-height: 96px; box-sizing: border-box;
    border: 1px solid #e6eaf0; border-radius: 10px; background: var(--card-bg, #fff); }
.cg-sk-field { display: flex; flex-direction: column; gap: 7px; }
.cg-sk-input, .cg-sk-btn { display: block; height: 38px; border-radius: 6px; border: 1px solid #e6eaf0; background: #f6f8fb; }
.cg-sk-li { padding: 7px 12px; }
.cg-sk-box { display: block; width: 100%; height: 100%; min-height: 200px; border-radius: 8px;
    background: #eef1f5 linear-gradient(90deg, #eef1f5 0, #f6f8fb 80px, #eef1f5 160px) no-repeat; background-size: 240px 100%;
    animation: skel-shimmer 1.4s ease-in-out infinite; }
.cg-sk-box:only-child { flex: 1 1 auto; }
.cg-sk-search { display: flex; gap: 12px; align-items: flex-end; padding: 14px; border: 1px solid #e6eaf0; border-radius: 10px; background: var(--card-bg, #fff); }
.cg-sk-pair { flex: 1 1 0; display: flex; flex-direction: column; gap: 7px; }
.cg-sk-btn { flex: 0 0 72px; background: #e9edf2; }
.cg-sk-tool { display: flex; gap: 8px; align-items: center; padding: 4px 2px 0; }
.cg-sk-tool .skeleton-bar { width: auto; flex: 0 0 auto; }
.cg-sk-gap { flex: 1 1 auto; }
.cg-sk-thead { display: flex; gap: 16px; padding: 10px 0; border-bottom: 1px solid #e2e8f0; }
.cg-sk-thead .skeleton-bar { background: #e2e7ee; }
/* 실패 — 영역 안에 오류 + [다시 시도] */
.cg-load-err { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
    min-height: 120px; padding: 24px 16px; box-sizing: border-box; color: #475569; font-size: 14px; text-align: center; word-break: keep-all; }
.cg-load-err.cg-sk-over { background: var(--card-bg, #fff); }
.cg-load-err-msg { display: inline-flex; align-items: center; gap: 6px; }
.cg-load-err-msg svg { color: #ef4444; flex: 0 0 auto; }
.cg-load-retry { display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 14px; cursor: pointer;
    border: 1px solid var(--control-border, #d5dbe3); border-radius: 6px; background: #fff; color: #1e293b; font: inherit; font-size: 13px; font-weight: 600; }
.cg-load-retry:hover { background: var(--primary); border-color: var(--primary); color: #fff; }
/* 다시 받는 표 — 누르기는 바로 막고(grid-busy), 흐리게(grid-loading)는 200ms 뒤 */
.data-table.grid-busy { pointer-events: none; }
/* 탭 셸 새 탭 첫 로드 뼈대(cgrid-shell.js) — iframe 위 덮개 */
.cs-pane { position: relative; }
.cs-frame-skel { position: absolute; inset: 0; z-index: 2; background: var(--bg, #f1f5f9); overflow: hidden; }
.cs-ws.mode-split .cs-frame-skel { top: 28px; }
.cs-frame-skel-in { display: flex; flex-direction: column; gap: 12px; padding: 20px 24px; }
.cs-frame-skel.is-err { display: flex; align-items: center; justify-content: center; }
/* 움직임 줄이기 — shimmer 멈춤(자리·모양은 그대로) */
@media (prefers-reduced-motion: reduce) {
    .skeleton-bar, .cg-sk-box { animation: none !important; }
    .bg-white:has(> table[data-grid]:not(.grid-ready))::after { animation: none !important; }
    .data-table.grid-loading tbody { transition: none; }
}

/* ---------- 콤보박스 (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;
}

/* 모달 기본 크기 (v4.23.0, §1.4.2) — 768px(max-w-3xl) 은 큰 입력 폼(쿠폰·상품)이 답답했다 → min(1040px, 94vw).
   특정도(0,2,0)로 Tailwind max-w-3xl(0,1,0)을 이긴다. 넓은 모달(아래 cg-modal-wide 90vw)·전체화면은 그대로 */
.cg-modal-overlay > .cg-modal-card { max-width: min(1040px, 94vw); }
/* 넓은 모달 — 컨텐츠에 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: 320px; padding: 2px 0; }
.cg-modal-skel .skeleton-bar:first-child { margin-bottom: 5px; }
.cg-modal-err { min-height: 220px; }

/* ============================================================
   §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"], .filter-row > input.cg-pk-input { 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"], .filter-row > input.cg-pk-input { 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; }

/* ---------- 그리드 위 줄 (v4.23.0, §2.1 바뀜) ----------
   [전체 N건 ……… + 추가 · 엑셀 · 개수] 를 **한 줄 세로 가운데**로. 예전(v4.22 까지)엔 도구 묶음(엑셀·개수)이 카드 위에 붙은
   '탭'(상단 라운드·하단 보더 없음·1px 겹침)이라, 탭 밖에 뜬 화면 액션(+ 추가)과 높이·위치가 따로 놀아 보였다(postring 사용자
   "그리드와 위 버튼이 이상하게 떨어져 있다"). 탭 모양을 걷고 줄 아래 여백 10px 로 카드와 띄운다.
   도구 묶음(.grid-tools)은 그대로 한 덩어리 — 수정모드일 땐 옅은 노랑 테두리로 감싼다(자리는 안 바뀌게 그림자로). */
.grid-toolbar { margin-bottom: 10px; align-items: center; position: relative; }   /* relative = 줌 알약의 중앙 기준 */
.grid-toolbar > div { align-items: center; }
.grid-toolbar .grid-count, .grid-toolbar .grid-actions { margin-bottom: 0; }
.grid-tools {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 0; margin: 0; background: transparent; border: 0; border-radius: 8px;
    position: relative; z-index: 1;
}
.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); box-shadow: 0 0 0 4px var(--gk-editing-bg), 0 0 0 5px 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; }
    .grid-toolbar { row-gap: 6px; }   /* 줄바꿈되면 줄 사이 6px(v4.23.0 — 예전엔 칸마다 아래 여백) */
    .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; }
}

/* ============================================================
   날짜·시간 선택기 (v4.22.0, cgrid-picker.js)
   원래 input(.cg-pk-src)은 숨겨 두고 값·이벤트·제출을 맡는다. 보이는 칸은 .cg-pk-input(글자 칸),
   선택기는 body 에 붙는 .cg-pk(fixed) — 카드·모달 overflow 에 잘리지 않는다. 색은 키트 토큰만.
   ============================================================ */
:root {
    --gk-pk-sun: #e11d48;                                              /* 일요일 */
    --gk-pk-sat: #2563eb;                                              /* 토요일 */
    --gk-pk-range: color-mix(in srgb, var(--primary) 9%, #fff);        /* 기간 사이 */
    --gk-pk-hover: color-mix(in srgb, var(--primary) 7%, #fff);
    --gk-pk-edge: color-mix(in srgb, var(--primary) 20%, #fff);       /* 기간 반대쪽 끝 */
}
input.cg-pk-src {
    position: absolute !important; width: 1px !important; height: 1px !important; min-width: 0 !important; max-width: 1px !important;
    padding: 0 !important; margin: 0 !important; border: 0 !important; opacity: 0 !important; pointer-events: none !important;
    overflow: hidden !important; clip-path: inset(50%) !important; flex: none !important;
}
input.cg-pk-input {
    padding-right: 30px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M16 3v4M8 3v4M3 10h18'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 9px center; background-size: 15px 15px;
    font-variant-numeric: tabular-nums; cursor: pointer; text-overflow: ellipsis;
}
input.cg-pk-input.cg-pk-t-time {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");
}
input.cg-pk-input::placeholder { color: #b6bfcc; }
input.cg-pk-input[readonly], input.cg-pk-input:disabled { cursor: default; }
input.cg-pk-input.is-open { border-color: var(--primary); }

.cg-pk {
    position: fixed; z-index: 1250; left: 0; top: 0; box-sizing: border-box;
    background: #fff; color: var(--text-primary); border: 1px solid var(--border); border-radius: 14px;
    box-shadow: 0 16px 40px rgba(15, 23, 42, .16), 0 2px 8px rgba(15, 23, 42, .06);
    padding: 10px 12px 10px; font-size: 13px; line-height: 1.2; user-select: none; -webkit-user-select: none;
    opacity: 0; transform: translateY(-4px); transition: opacity .12s ease-out, transform .12s ease-out;
    font-variant-numeric: tabular-nums;
}
.cg-pk.is-up { transform: translateY(4px); }
.cg-pk.is-on { opacity: 1; transform: none; }
.cg-pk button { font: inherit; color: inherit; margin: 0; -webkit-tap-highlight-color: transparent; }
.cg-pk-sh { display: none; }
.cg-pk-main { display: flex; align-items: stretch; }
.cg-pk-cal { width: 252px; }
.cg-pk-cal.is-flash { animation: cgPkFlash .5s; }
@keyframes cgPkFlash { 0%, 100% { background: transparent; } 30% { background: var(--gk-pk-hover); } }

/* 머리 — ‹ 2026년 ▾ 11월 ▾ › */
.cg-pk-hd { display: flex; align-items: center; justify-content: space-between; height: 34px; margin-bottom: 4px; }
.cg-pk-arw {
    width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
    border: 0; border-radius: 8px; background: transparent; color: var(--text-secondary); cursor: pointer;
}
.cg-pk-arw:hover { background: var(--primary-light); color: var(--text-primary); }
.cg-pk-ttl { display: flex; align-items: center; gap: 2px; }
.cg-pk-ttl button, .cg-pk-tr {
    display: inline-flex; align-items: center; gap: 4px; height: 30px; padding: 0 8px;
    border: 0; border-radius: 8px; background: transparent; font-size: 14.5px; font-weight: 700; letter-spacing: -.01em; cursor: pointer;
}
.cg-pk-tr { cursor: default; color: var(--text-primary); }
.cg-pk-ttl button:hover { background: var(--primary-light); }
.cg-pk-dn { color: var(--text-secondary); opacity: .8; }

/* 요일 머리 + 날짜 격자 (6줄 고정 — 달마다 높이가 출렁이지 않게) */
.cg-pk-wk { display: grid; grid-template-columns: repeat(7, 1fr); height: 26px; align-items: center; text-align: center;
    font-size: 11.5px; font-weight: 600; color: var(--text-secondary); }
.cg-pk-wk .is-sun { color: var(--gk-pk-sun); }
.cg-pk-wk .is-sat { color: var(--gk-pk-sat); }
.cg-pk-days { display: grid; grid-template-columns: repeat(7, 1fr); grid-auto-rows: 34px; row-gap: 2px; }
.cg-pk-grid4 { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 48px; gap: 6px; padding: 4px 0 2px; }
.cg-pk-c {
    position: relative; border: 0; border-radius: 9px; background: transparent; cursor: pointer;
    font-size: 13px; transition: background-color .1s;
}
.cg-pk-c:hover:not(:disabled) { background: var(--gk-pk-hover); }
.cg-pk-c.is-sun { color: var(--gk-pk-sun); }
.cg-pk-c.is-sat { color: var(--gk-pk-sat); }
.cg-pk-c.is-out { opacity: .38; }
.cg-pk-c:disabled { color: #cbd2dc !important; opacity: 1; cursor: not-allowed; background: transparent; }
.cg-pk-c.is-today { font-weight: 700; }
.cg-pk-c.is-today::after {
    content: ''; position: absolute; left: 50%; bottom: 4px; width: 4px; height: 4px; margin-left: -2px;
    border-radius: 50%; background: var(--primary);
}
.cg-pk-grid4 .cg-pk-c.is-today::after { bottom: 7px; }
/* 기간 — 사이는 띠로, 양 끝은 둥글게 */
.cg-pk-c.is-in, .cg-pk-c.is-pv { background: var(--gk-pk-range); border-radius: 0; }
.cg-pk-days .cg-pk-c.is-rs, .cg-pk-days .cg-pk-c.is-pvs { border-radius: 9px 0 0 9px; }
.cg-pk-days .cg-pk-c.is-re, .cg-pk-days .cg-pk-c.is-pve { border-radius: 0 9px 9px 0; }
.cg-pk-days .cg-pk-c.is-rs, .cg-pk-days .cg-pk-c.is-re { background: var(--gk-pk-range); }
/* 기간의 다른 쪽 끝(지금 고르는 칸이 아닌 쪽) — 옅게 채운 둥근 칸 */
.cg-pk-c.is-edge { background: var(--gk-pk-edge); color: var(--text-primary); font-weight: 700; }
.cg-pk .cg-pk-c.is-sel, .cg-pk .cg-pk-days .cg-pk-c.is-sel, .cg-pk .cg-pk-c.is-sel:hover:not(:disabled) {
    background: var(--primary); color: #fff !important; font-weight: 700; opacity: 1;
    box-shadow: 0 2px 6px color-mix(in srgb, var(--primary) 35%, transparent);
}
.cg-pk-c.is-sel::after { background: #fff; }
.cg-pk.is-kb .cg-pk-c.is-cur:not(.is-sel) { box-shadow: inset 0 0 0 1.5px var(--primary); }
.cg-pk.is-kb .cg-pk-c.is-cur.is-sel { box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px var(--primary); }

/* 시·분 목록 — 일시는 달력 오른쪽, 시각만이면 단독 */
.cg-pk-time { display: flex; gap: 6px; }
.cg-pk-t-datetime .cg-pk-time { margin-left: 12px; padding-left: 12px; border-left: 1px solid var(--border); }
.cg-pk-col { width: 56px; display: flex; flex-direction: column; }
.cg-pk-col-hd { height: 34px; margin-bottom: 4px; display: flex; align-items: center; justify-content: center;
    font-size: 12px; font-weight: 700; color: var(--text-secondary); }
.cg-pk-list {
    height: 240px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; padding: 0 2px;
    scrollbar-width: thin; scrollbar-color: #d8dee6 transparent; overscroll-behavior: contain;
}
.cg-pk-list .cg-pk-c { flex: 0 0 30px; font-size: 13px; }
.cg-pk-t-time .cg-pk-list { height: 236px; }

/* 아래 단추 줄 */
.cg-pk-ft { display: flex; align-items: center; gap: 6px; margin-top: 8px; padding-top: 9px; border-top: 1px solid var(--border); }
.cg-pk-sp { flex: 1 1 auto; }
.cg-pk-b {
    height: 28px; padding: 0 11px; border: 1px solid var(--border); border-radius: 8px; background: #fff;
    font-size: 12.5px; font-weight: 600; color: var(--text-secondary); cursor: pointer; white-space: nowrap;
}
.cg-pk-b:hover:not(:disabled) { background: var(--primary-light); color: var(--text-primary); }
.cg-pk-b:disabled { opacity: .45; cursor: not-allowed; }
.cg-pk-b.cg-pk-now { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 30%, #fff); }
.cg-pk-b.is-main { background: var(--primary); border-color: var(--primary); color: #fff; padding: 0 16px; }
.cg-pk-b.is-main:hover:not(:disabled) { background: var(--primary-dark); color: #fff; }

/* 좁은 화면 — 아래 시트(cgrid-picker.js 가 .is-sheet 을 붙인다, ≤768px). 손가락 크기 44px */
.cg-pk-dim { position: fixed; inset: 0; z-index: 1249; background: rgba(15, 23, 42, .35); opacity: 0; transition: opacity .2s; }
.cg-pk-dim.is-on { opacity: 1; }
.cg-pk.is-sheet {
    left: 0 !important; right: 0; top: auto !important; bottom: 0; width: auto; max-width: 560px; margin: 0 auto;
    border-radius: 18px 18px 0 0; border-bottom: 0; padding: 0 16px calc(14px + env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -10px 32px rgba(15, 23, 42, .2); transform: translateY(100%); opacity: 1;
    transition: transform .22s cubic-bezier(.2, .8, .2, 1); max-height: 92vh; overflow-y: auto; font-size: 15px;
}
.cg-pk.is-sheet.is-on { transform: none; }
.cg-pk.is-sheet .cg-pk-sh { display: flex; align-items: center; justify-content: space-between; position: relative; height: 54px; }
.cg-pk-grab { position: absolute; top: 7px; left: 50%; width: 36px; height: 4px; margin-left: -18px; border-radius: 4px; background: #d8dee6; }
.cg-pk-sh-t { font-size: 16px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cg-pk-x { width: 44px; height: 44px; margin-right: -10px; display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; color: var(--text-secondary); border-radius: 10px; cursor: pointer; }
.cg-pk-x svg { width: 20px; height: 20px; }
.cg-pk.is-sheet .cg-pk-main { flex-direction: column; }
.cg-pk.is-sheet .cg-pk-cal { width: auto; }
.cg-pk.is-sheet .cg-pk-hd { height: 44px; margin-bottom: 6px; }
.cg-pk.is-sheet .cg-pk-arw { width: 44px; height: 44px; }
.cg-pk.is-sheet .cg-pk-arw svg { width: 20px; height: 20px; }
.cg-pk.is-sheet .cg-pk-ttl button, .cg-pk.is-sheet .cg-pk-tr { height: 44px; font-size: 17px; padding: 0 10px; }
.cg-pk.is-sheet .cg-pk-wk { height: 30px; font-size: 13px; }
.cg-pk.is-sheet .cg-pk-days { grid-auto-rows: 44px; }
.cg-pk.is-sheet .cg-pk-c { font-size: 15px; border-radius: 12px; }
.cg-pk.is-sheet .cg-pk-days .cg-pk-c.is-rs, .cg-pk.is-sheet .cg-pk-days .cg-pk-c.is-pvs { border-radius: 12px 0 0 12px; }
.cg-pk.is-sheet .cg-pk-days .cg-pk-c.is-re, .cg-pk.is-sheet .cg-pk-days .cg-pk-c.is-pve { border-radius: 0 12px 12px 0; }
.cg-pk.is-sheet .cg-pk-c.is-in, .cg-pk.is-sheet .cg-pk-c.is-pv { border-radius: 0; }
.cg-pk.is-sheet .cg-pk-grid4 { grid-auto-rows: 56px; gap: 8px; }
.cg-pk.is-sheet .cg-pk-time { flex: 1 1 auto; gap: 10px; }
.cg-pk.is-sheet.cg-pk-t-datetime .cg-pk-time { margin: 10px 0 0; padding: 10px 0 0; border-left: 0; border-top: 1px solid var(--border); }
.cg-pk.is-sheet .cg-pk-col { flex: 1 1 0; width: auto; }
.cg-pk.is-sheet .cg-pk-list { height: 184px; }
.cg-pk.is-sheet.cg-pk-t-time .cg-pk-list { height: 276px; }
.cg-pk.is-sheet .cg-pk-list { align-items: center; }
.cg-pk.is-sheet .cg-pk-list .cg-pk-c { flex: 0 0 44px; width: 100%; max-width: 132px; }
.cg-pk.is-sheet .cg-pk-ft { margin-top: 12px; padding-top: 12px; gap: 8px; }
.cg-pk.is-sheet .cg-pk-b { height: 46px; font-size: 15px; flex: 1 1 0; border-radius: 12px; }
.cg-pk.is-sheet .cg-pk-sp { display: none; }
@media (prefers-reduced-motion: reduce) { .cg-pk, .cg-pk.is-sheet, .cg-pk-dim { transition: none; } }

/* ======================================================================
   모달 폼 사용성 (v4.23.0, ADMIN_PAGE_RULE §1.4.2 · cgrid-formux.js)
   기준 구현 = postring v2.40~v2.42 app.css 블록을 키트로 옮김. 범위는 모달 안(.cg-modal-body · [data-admin-modal]).
   ====================================================================== */

/* ---------- 입력칸 구분 — 꺼진 칸 · 읽기 전용 · 아직 안 채운 필수칸 ---------- */
:is([data-admin-modal], .cg-modal-body) :is(input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), select, textarea):disabled,
:is([data-admin-modal], .cg-modal-body) :is(input:not(.combobox-input), textarea)[readonly] { background: #f1f5f9 !important; color: #94a3b8; cursor: not-allowed; }
/* 필수칸 비었음 = 옅은 노랑. 날짜 선택기 칸(v4.22.0 .cg-pk-input)은 원래 칸이 숨어 있으니 aria-required + 빈 칸 표식으로 */
:is([data-admin-modal], .cg-modal-body) :is(input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), textarea)[required]:invalid:not(:focus),
:is([data-admin-modal], .cg-modal-body) input.cg-pk-input.is-empty[aria-required="true"]:not(:focus) {
    background: #fffbeb; border-color: #fcd34d !important; }
/* 메모·사유·설명 같은 글 칸 = 기본 5줄. rows 를 직접 준 칸·리치 에디터 원본은 그대로 */
:is([data-admin-modal], .cg-modal-body) textarea:not([rows]):not([data-editor]) { min-height: calc(5 * 1.5em + 18px); line-height: 1.5; }

/* ---------- 줄 맞춤 — 모달 칸 안의 콤보 = 옆 입력칸과 같은 너비 ----------
   키트가 콤보에 글자 폭만큼 style.width 를 넣는 경우(.field 밖)가 있어 !important. 바깥 여백 유틸(mt-1 등)은 v4.23.0 부터
   감싸개로 옮겨 붙는다(cgrid-combobox.js splitCls) — 안쪽 입력칸에 한 번 더 붙어 4px 처지던 것 */
:is([data-admin-modal], .cg-modal-body) :is(label, .block, .field) > .combobox:not(.cb-compact) { display: flex; width: 100% !important; }
:is([data-admin-modal], .cg-modal-body) :is(label, .block, .field) > .combobox:not(.cb-compact) > .combobox-input { flex: 1 1 auto; width: 100%; margin-top: 0; }

/* ---------- 체크 칩 — 옆 입력칸과 같은 높이의 선택지. <label class="ck-chip"><input type=checkbox|radio> 글</label> ----------
   높이 = --gk-chip-h: 기본 42px(Tailwind 폼 입력칸, postring 기준) · 키트 .form-grid 안은 .filter-input 높이(34px)에 맞춘다 */
.form-grid { --gk-chip-h: 34px; }
.ck-chip { display: inline-flex; align-items: center; gap: 8px; box-sizing: border-box; height: var(--gk-chip-h, 42px); padding: 0 14px; width: max-content; max-width: 100%;
    border: 1.5px solid var(--control-border); border-radius: 8px; background: #fff; font-size: 14px; color: #334155; cursor: pointer; white-space: nowrap; }
.ck-chip:has(input:checked) { border-color: var(--primary); background: #f8fafc; color: #0f172a; font-weight: 600; }
.ck-chip:has(input:disabled) { opacity: .55; cursor: not-allowed; }
.ck-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.ck-field { display: flex; flex-direction: column; gap: 6px; }   /* 라벨 없는 칸 — 위 여백만 라벨 한 줄만큼(.field 와 같은 간격 6px) */
.ck-field > .ck-spacer { display: block; font-size: 13px; font-weight: 600; visibility: hidden; }   /* .field-label 과 같은 글자 한 줄 높이 */
.ck-field > .ck-spacer::before { content: "\00a0"; }
@media (max-width: 768px) { .form-grid .ck-field > .ck-spacer { display: none; } }   /* 한 줄 한 칸이면 맞출 옆 칸이 없다 — 빈 줄만 남았다 */

/* ---------- 묶음(.fsec) = 상자 대신 구분선 + 제목은 영역 띠(.form-section) ----------
   <section class="fsec"><h3 class="form-section">기본 정보</h3> …칸… </section> */
.fsec { display: flex; flex-direction: column; gap: 14px; scroll-margin-top: 12px; }
.fsec + .fsec { border-top: 1px solid var(--border); padding-top: 20px; margin-top: 6px; }
.fsec > .form-section { margin: 0 0 2px; }
form:has(> .fsec) { display: flex; flex-direction: column; gap: 14px; }
/* 묶음으로 시작하는 모달 — 첫 띠 위 여백은 본문 위쪽 16px 하나만. Tailwind space-y-* 는 앞에 hidden 칸이 있으면
   첫 묶음에도 16px 를 더 붙인다(첫째 자식이 아니라서 — postring 41px 실측) */
.cg-modal-body:has(.fsec) { padding-top: 16px; }
.fsec:not(.fsec ~ .fsec) { margin-top: 0 !important; }

/* ---------- 묶음 바로 가기 (.cg-fnav) — 모달 머리 [전체화면] 왼쪽 · 키트 입력 콤보 ---------- */
.cg-fnav { display: flex; align-items: center; gap: 8px; }
.cg-fnav.is-head { margin-left: auto; margin-right: 4px; }
.cg-modal-head > .cg-fnav.is-head + .cg-modal-fullbtn, .cg-modal-head > .cg-fnav.is-head + .cg-modal-close { margin-left: 0; }
.cg-fnav .combobox { width: 200px !important; }
.cg-fnav .combobox .combobox-input { box-sizing: border-box; height: 34px; font-size: 13px; padding: 0 30px 0 12px;
    border: 1.5px solid var(--control-border) !important; border-radius: 8px; background-color: #fff; }   /* background 통째로 쓰면 ▾ 그림이 지워진다 */
.cg-fnav .combobox:hover .combobox-input, .cg-fnav .combobox.combobox-open .combobox-input { border-color: var(--primary) !important; }
.cg-fnav.is-sticky { position: sticky; top: -1px; z-index: 5; padding: 8px 0; background: #fff; border-bottom: 1px solid var(--border); }
@media (max-width: 640px) { .cg-fnav .combobox { width: 150px !important; } }

/* ---------- 파일 선택 공통 부품 (.cg-fd) — 끌어다 놓기 · 미리보기 · 삭제 ---------- */
.cg-fd { display: flex; flex-direction: column; gap: 8px; width: 100%; min-width: 0; box-sizing: border-box; }
.cg-fd-input, .cg-fd-picker { display: none !important; }
.cg-fd-zone { display: flex; align-items: center; gap: 12px; box-sizing: border-box; padding: 14px 16px; border: 1.5px dashed #b6bfcc; border-radius: 10px;
    background: #f8fafc; cursor: pointer; transition: border-color .12s, background .12s; }
.cg-fd-zone:hover, .cg-fd.is-drag .cg-fd-zone { border-color: var(--primary); background: #f1f5f9; }
.cg-fd-zone:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(17, 24, 39, .18); }
.cg-fd.is-missing .cg-fd-zone { border-color: #fcd34d; background: #fffbeb; }
.cg-fd:has(.cg-fd-input:disabled) .cg-fd-zone { opacity: .5; cursor: not-allowed; }
.cg-fd-ico { width: 36px; height: 36px; flex: 0 0 36px; display: grid; place-items: center; border-radius: 50%; background: #fff; color: #334155; border: 1px solid var(--border); }
.cg-fd-ico svg { width: 18px; height: 18px; }
.cg-fd-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cg-fd-text b { font-size: 13px; color: #1e293b; font-weight: 600; }
.cg-fd-text small { font-size: 12px; color: #94a3b8; }
.cg-fd-list { display: flex; flex-direction: column; gap: 6px; }
.cg-fd-item { display: flex; align-items: center; gap: 10px; box-sizing: border-box; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: #fff; min-width: 0; }
.cg-fd-item.is-current { background: #f8fafc; }
.cg-fd-thumb { width: 48px; height: 48px; flex: 0 0 48px; border-radius: 8px; overflow: hidden; background: #f1f5f9; display: grid; place-items: center;
    font-size: 11px; font-weight: 700; color: #64748b; border: 1px solid var(--border); }
.cg-fd-thumb img { width: 100%; height: 100%; object-fit: cover; }
.cg-fd-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.cg-fd-meta b { font-size: 13px; color: #1e293b; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cg-fd-meta small { font-size: 12px; color: #94a3b8; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cg-fd-del { flex: 0 0 auto; font-size: 12px; padding: 5px 10px; border-radius: 6px; border: 1px solid var(--border); background: #fff; color: #475569; cursor: pointer; }
.cg-fd-del:hover { border-color: #f43f5e; color: #e11d48; }
.cg-fd-msg { font-size: 12px; color: #e11d48; margin: 0; }
.cg-fd-msg:empty { display: none; }
/* 한 개짜리(썸네일 등) = 한 줄: [지금 파일 ……][끌어다 놓기] — 줄마다 크게 차지하던 것(postring 사용자 "줄이자") */
.cg-fd.cg-fd-single { flex-direction: row; flex-wrap: wrap; align-items: stretch; }
.cg-fd.cg-fd-single .cg-fd-list { order: -1; flex: 0 1 340px; min-width: 0; }
.cg-fd.cg-fd-single .cg-fd-zone { flex: 1 1 240px; padding: 8px 12px; min-height: 62px; }
.cg-fd.cg-fd-single .cg-fd-msg { flex-basis: 100%; }
.cg-fd.cg-fd-single .cg-fd-item { height: 100%; padding: 6px 10px; }
.cg-fd.cg-fd-single .cg-fd-thumb { width: 46px; height: 46px; flex-basis: 46px; }
.cg-fd.cg-fd-single .cg-fd-ico { width: 32px; height: 32px; flex-basis: 32px; }

/* ---------- 사진 크게 보기 — 누를 수 있는 사진에 돋보기 커서 ---------- */
:is(.cg-fd-thumb, .cg-modal-body, [data-admin-modal], .data-table) img:not([data-no-zoom]):not(a img):not(button img),
img[data-zoom], [data-zoom] img { cursor: zoom-in; }
[data-no-zoom] img { cursor: auto; }

/* ===== v4.24.0 사이드바 즐겨찾기 메뉴 (cgrid-favmenu.js · ADMIN_PAGE_RULE §2.8 ⑨) =====
   사이드바는 사이트 마크업 — 색은 사이드바 글자색(currentColor)을 따르고, 키트는 자리·모양만 잡는다.
   별 = 리프 줄 오른쪽 끝. 미등록 = 호버 때만 빈 별, 등록 = 채운 별 상시, 휴대폰 서랍 = 빈 별도 옅게 상시. */
.cg-fav-host { position: relative; }
.cg-fav-host.cg-fav-host { padding-right: 30px; }          /* 별 자리 — 긴 이름의 말줄임이 별 밑으로 안 들어가게(클래스 두 번 = 사이트 패딩보다 우선) */
.cg-fav-star {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
    border-radius: 6px; color: inherit; opacity: 0; cursor: pointer; transition: opacity .12s ease;
}
.cg-fav-star svg { width: 14px; height: 14px; display: block; }
.cg-fav-host:hover > .cg-fav-star, .cg-fav-host:focus-visible > .cg-fav-star { opacity: .6; }
.cg-fav-star:hover { opacity: 1 !important; background: rgba(148, 163, 184, .2); }
.cg-fav-host.is-fav > .cg-fav-star, .cg-fav-link > .cg-fav-star { opacity: 1; color: #f59e0b; }
@media (hover: none), (max-width: 768px) {
    .cg-fav-host > .cg-fav-star { opacity: .35; width: 32px; height: 32px; right: 2px; }
    .cg-fav-host.is-fav > .cg-fav-star, .cg-fav-link > .cg-fav-star { opacity: 1; }
}
/* 모음 — nav 맨 위, 머리 눌러 접기(v4.25.0 — 기본 펼침·기억), 비면 [hidden] */
.cg-fav { margin: 0 0 6px; padding-bottom: 6px; border-bottom: 1px solid rgba(148, 163, 184, .2); }
.cg-fav > .cg-fav-head { padding: 8px 16px 4px; font-size: 12px; font-weight: 700; opacity: .75; cursor: default; user-select: none; }
.cg-fav-link { cursor: pointer; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.cg-fav-link.cg-fav-cur { font-weight: 700; box-shadow: inset 3px 0 0 0 currentColor; }
/* v4.25.0 머리 = 접기 단추(우측 끝 꺾쇠 — 펼침 = 아래, 접힘 = 오른쪽). 사이트 그룹 머리 클래스(headClass)를 주면 그쪽 모양 */
.cg-fav { position: relative; }
button.cg-fav-head { display: flex; align-items: center; gap: 6px; width: 100%; border: 0; background: transparent; color: inherit;
    font: inherit; text-align: left; cursor: pointer; }
.cg-fav > button.cg-fav-head { cursor: pointer; }
.cg-fav > button.cg-fav-head:hover { opacity: 1; }
.cg-fav-caret { margin-left: auto; flex: 0 0 13px; transform: rotate(90deg); transition: transform .15s ease; }
.cg-fav.is-closed .cg-fav-caret { transform: rotate(0deg); }
.cg-fav.is-closed { padding-bottom: 2px; }
.cg-fav.is-closed.has-cur > .cg-fav-head { opacity: 1; }            /* 접힌 채 지금 화면이 모음 안 — 머리만 활성 톤(사이트가 색을 덮는다) */
/* 불러오는 동안 줄(§1.2.1) — 사이드바는 어두운 바탕이 많아 막대를 옅은 반투명으로 */
.cg-fav-sk { padding: 9px 16px 9px 26px; }
.cg-fav-sk .skeleton-bar { height: 10px; background-color: rgba(148, 163, 184, .22);
    background-image: linear-gradient(90deg, rgba(148, 163, 184, .22) 0, rgba(148, 163, 184, .38) 60px, rgba(148, 163, 184, .22) 120px); }
/* 정렬 손잡이 — 줄 왼쪽 점 6개. PC = 줄에 올리면 옅게, 휴대폰 = 늘 옅게 */
.cg-fav-link { position: relative; }
.cg-fav-grip { position: absolute; left: 6px; top: 50%; transform: translateY(-50%); width: 16px; height: 22px;
    display: inline-flex; align-items: center; justify-content: center; border-radius: 4px; color: inherit;
    opacity: 0; cursor: grab; touch-action: none; transition: opacity .12s ease; }
.cg-fav-grip svg { display: block; }
.cg-fav-link:hover > .cg-fav-grip, .cg-fav-grip:focus-visible { opacity: .55; }
.cg-fav-grip:hover { opacity: 1 !important; background: rgba(148, 163, 184, .2); }
.cg-fav-grip:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; opacity: 1; }
@media (hover: none), (max-width: 768px) { .cg-fav-grip { opacity: .35; width: 22px; height: 30px; left: 2px; } }
/* 끄는 동안 — 고스트(반투명·띄운 그림자)가 커서를 따라오고, 원래 줄은 빈 칸 자리(점선) */
.cg-fav.is-dragging, .cg-fav.is-dragging .cg-fav-link, .cg-fav.is-dragging .cg-fav-grip { cursor: grabbing; }
.cg-fav-link.cg-fav-drag { background: rgba(148, 163, 184, .22) !important; outline: 1px dashed rgba(148, 163, 184, .6); outline-offset: -1px; }
.cg-fav-link.cg-fav-slot { color: transparent !important; background: rgba(148, 163, 184, .10) !important;
    outline: 2px dashed var(--primary, #38bdf8); outline-offset: -2px; border-radius: 6px; box-shadow: none !important; }
.cg-fav-link.cg-fav-slot > * { visibility: hidden; }
.cg-fav-link.cg-fav-out { outline: 0; opacity: .45; background: transparent !important; }
.cg-fav-ghost { z-index: 30; pointer-events: none; margin: 0; opacity: .92; border-radius: 6px;
    background: var(--gk-fav-ghost-bg, #1e293b) !important; color: var(--gk-fav-ghost-fg, #fff) !important;   /* 밝은 사이드바는 토큰으로 덮는다 */
    box-shadow: 0 10px 24px rgba(0, 0, 0, .35), 0 2px 6px rgba(0, 0, 0, .25); transform: scale(1.02) rotate(-.6deg); }
.cg-fav-ghost > .cg-fav-grip { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .cg-fav-caret, .cg-fav-grip { transition: none; }
    .cg-fav-ghost { transform: none; }
}

/* ===== 화면 안 탭 (v4.28.0, cgrid-tabs.js · §2.20) — 목록·검색이 따로인 영역이 둘 이상이면 탭으로 ===== */
.cg-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin: 0 0 14px; overflow-x: auto; overflow-y: hidden;
    white-space: nowrap; scrollbar-width: thin; -webkit-overflow-scrolling: touch; }
.cg-tab { appearance: none; background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
    padding: 9px 14px; font: inherit; font-size: 14px; font-weight: 600; color: var(--text-secondary); cursor: pointer;
    display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; word-break: keep-all; border-radius: 6px 6px 0 0; }
.cg-tab:hover { color: var(--text-primary); background: var(--primary-light); }
.cg-tab[aria-selected="true"] { color: var(--text-primary); border-bottom-color: var(--primary); }
.cg-tab:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.cg-tab[disabled] { opacity: .45; cursor: default; }
.cg-tab-count { min-width: 20px; padding: 0 6px; border-radius: 999px; background: var(--primary-light); color: var(--text-secondary);
    font-size: 12px; font-weight: 600; line-height: 20px; text-align: center; font-variant-numeric: tabular-nums; }
.cg-tab[aria-selected="true"] .cg-tab-count { background: var(--primary); color: #fff; }
.cg-tab-count[hidden] { display: none; }
[data-tab-panel][hidden] { display: none !important; }   /* 사이트 CSS 가 display 를 줘도 숨긴 패널은 숨는다 */
@media (max-width: 768px) { .cg-tabs { margin-left: -4px; margin-right: -4px; } .cg-tab { padding: 9px 12px; } }

/* ===== 공통 움직임 (v4.29.0, ADMIN_PAGE_RULE §2.21 — colon-hub 값 그대로, 사용자 2026-11-04 "colon-hub 와 동일하게") =====
   반응·전환에만 쓴다(장식으로 계속 움직이는 것 X). 움직임 줄이기면 전부 끔 · 끄기 data-no-motion(요소·부모·html). */
:root { --cg-ease-pop: cubic-bezier(.3, 1.6, .5, 1); --cg-ease-spring: cubic-bezier(.2, 1.15, .4, 1); --cg-ease-out: cubic-bezier(.2, .8, .2, 1); }
@keyframes cg-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes cg-fade-out { to { opacity: 0; } }
@keyframes cg-pop { from { opacity: 0; transform: translateY(4px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes cg-pop-out { to { opacity: 0; transform: translateY(6px) scale(.97); } }
@keyframes cg-rip { to { transform: scale(2.6); opacity: 0; } }
/* 버튼 — 올라오고, 누르면 톡 · 아이콘 반응 · 누른 자리 물결(cgrid-motion.js) */
.btn { transition: transform .15s var(--cg-ease-pop), background-color .15s, border-color .15s, color .15s, box-shadow .2s; }
.btn:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 6px 14px -8px rgba(26, 26, 46, .45); }
.btn:active:not(:disabled) { transform: scale(.94); box-shadow: none; }
.btn > svg { transition: transform .3s var(--cg-ease-pop); }
.btn:hover:not(:disabled) > svg { transform: scale(1.18) rotate(-8deg); }
.cg-rip-host { position: relative; overflow: hidden; }
.cg-rip { position: absolute; border-radius: 9999px; pointer-events: none; background: currentColor; opacity: .18; transform: scale(0); animation: cg-rip .55s ease-out forwards; }
/* 키트 모달 — 열릴 때 배경 페이드·카드는 살짝 튕기며, 닫힐 때 가라앉으며(cgrid-modal 이 .is-closing 뒤 걷음) */
.cg-modal-overlay.flex { animation: cg-fade .18s var(--cg-ease-out) both; }
.cg-modal-overlay.flex > .cg-modal-card { animation: cg-pop .26s var(--cg-ease-spring) both; }
.cg-modal-overlay.is-closing { animation: cg-fade-out .16s var(--cg-ease-out) both; }
.cg-modal-overlay.is-closing > .cg-modal-card { animation: cg-pop-out .16s var(--cg-ease-out) both; }
/* 콤보 목록 — 열릴 때 살짝 내려오며 */
.combobox-open .combobox-dropdown, .combobox-dropdown-portal { animation: cg-pop .16s var(--cg-ease-out) both; transform-origin: top center; }
/* 표 행 — 누르는 순간 반응 */
.data-table tbody td { transition: background-color .12s ease, color .12s ease; }
.data-table tbody tr.row-clickable:active td { filter: brightness(.92); }
/* 탭 — 화면 안 탭 패널·셸 탭 본문이 바뀔 때 부드럽게 */
.cg-tab-panel:not([hidden]) { animation: cg-fade .2s var(--cg-ease-out); }
.cg-tab { transition: background-color .18s var(--cg-ease-out), color .18s, border-color .18s; }
.cs-frame { animation: cg-fade .2s var(--cg-ease-out); }
/* 사이드바 즐겨찾기 모음 줄 — 사이트 메뉴와 같은 밀림(§2.8 ⑩: +7px · .2s · 지금 화면은 제자리) */
.cg-fav-link { transition: background .2s ease, color .2s ease, padding-left .2s var(--cg-ease-out), transform .1s ease; }
.cg-fav-link:active { transform: scale(.985); }
[data-no-motion], [data-no-motion] * { animation: none !important; transition: none !important; }
@media (prefers-reduced-motion: reduce) {
    .btn, .btn > svg, .cg-tab, .cg-fav-link, .data-table tbody td { transition: none !important; }
    .btn:hover:not(:disabled), .btn:active:not(:disabled), .btn:hover:not(:disabled) > svg { transform: none !important; }
    .cg-rip, .cg-modal-overlay.flex, .cg-modal-overlay.flex > .cg-modal-card, .cg-modal-overlay.is-closing, .cg-modal-overlay.is-closing > .cg-modal-card,
    .combobox-open .combobox-dropdown, .combobox-dropdown-portal, .cg-tab-panel:not([hidden]), .cs-frame { animation: none !important; }
}
