/* ============================================================
   notihub Admin – 공통 스타일시트
   PC-only admin tool layout
   ============================================================ */

/* ── CSS Variables ──
   토큰은 여기 한 곳에만 둔다. 계층 순서(tokens → base → layout → …)의 맨 앞이다.
   페이지 CSS 는 색을 직접 적을 수 없으므로(규칙 검사기), 공통 컴포넌트가 쓰는 색은
   모두 이름을 붙여 둔다. 페이지가 같은 색을 쓰려면 이 이름을 쓴다. (docs/common/admin-ui.md) */

:root {
    --font-size-base: 1rem;

    --sidebar-width: 250px;
    --sidebar-collapsed-width: 64px;
    --topbar-height: 56px;

    --color-primary: #1e40af;
    --color-primary-light: #3b82f6;
    --color-primary-dark: #1e3a8a;
    --color-primary-bg: #eff6ff;

    --color-sidebar-bg: #0f172a;
    --color-sidebar-text: #94a3b8;
    --color-sidebar-text-active: #ffffff;
    --color-sidebar-hover: #1e293b;
    --color-sidebar-active: #1e40af;
    /* 어두운 사이드바 안의 경계선(머리·바닥)과 스크롤막대 */
    --color-sidebar-divider: rgba(255, 255, 255, 0.08);
    --color-sidebar-scrollbar: rgba(255, 255, 255, 0.22);

    --color-topbar-bg: #ffffff;
    --color-topbar-border: #e2e8f0;

    --color-surface: #ffffff;
    --color-border: #e2e8f0;
    /* 가리킬 때 짙어지는 테두리(외곽선 버튼·탭) */
    --color-border-strong: #cbd5e1;
    --color-text: #1e293b;
    /* 글자색은 놓이는 가장 어두운 바탕(본문 배경 #f1f5f9)에서도 4.5:1 을 넘게 잡는다.
       (docs/common/admin-ui.md 접근성) secondary 6.1:1 · muted 4.6:1 */
    --color-text-secondary: #4f5d71;
    --color-text-muted: #627085;
    /* 짙은 바탕(강조 버튼·고른 쪽 번호·탭) 위의 글자 */
    --color-text-inverse: #ffffff;

    --color-success: #16a34a;
    --color-warning: #d97706;
    --color-danger: #dc2626;
    --color-danger-dark: #b91c1c;
    --color-info: #0284c7;

    /* 옅은 색 바탕(배지·토스트·경고) 위에 쓰는 글자색. 위 상태색을 그대로 쓰면
       글자가 3:1 안팎이라 읽기 어렵다. 아이콘·테두리는 위 상태색을 그대로 쓴다. */
    --color-success-text: #15803d;
    --color-warning-text: #b45309;
    --color-danger-text: #b91c1c;
    --color-info-text: #0369a1;

    /* 그 옅은 색 바탕. 배지·토스트·통계 아이콘·오류 띠가 함께 쓴다. */
    --color-success-bg: #f0fdf4;
    --color-warning-bg: #fffbeb;
    --color-danger-bg: #fef2f2;
    --color-info-bg: #f0f9ff;
    /* 위험 바탕 위의 테두리(위험 외곽선 버튼·오류 토스트) */
    --color-danger-border: #fecaca;

    --color-bg-subtle: #f8fafc;
    --color-bg-muted: #f1f5f9;

    /* 툴팁. 팝업 안에서도 눈에 띄게 본문과 반대로 짙게 둔다. */
    --color-tooltip-bg: #1e293b;
    --color-tooltip-text: #f8fafc;

    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    /* 양 끝이 둥근 알약 모양(배지·스크롤막대). 높이의 절반보다 크기만 하면 된다. */
    --radius-pill: 999px;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);

    /* 위지윅 에디터 폭 — 쓰는 법은 아래 '위지윅 에디터 폭' 에 있다.
       글이 실제로 놓이는 client 본문 폭. 프로젝트가 자기 레이아웃에 맞게 바꾼다.
       예) .container(1300px) − 좌우 여백 20×2 − 본문 여백 16×2 = 1228px */
    --admin-editor-width: 1228px;

    /* 편집 영역의 좌우 여백과 테두리. 아래에서 그대로 적용해야 계산이 실제와 맞는다. */
    --admin-editor-pad-x: 12px;
    --admin-editor-border: 1px;
    /* 에디터를 담는 카드의 한쪽 여백: .admin-card 테두리 1px + .admin-card-body 여백 20px */
    --admin-editor-card-pad: 21px;
}

/* ── Font ── */

@font-face {
    font-family: "Material Icons Outlined";
    font-style: normal;
    font-weight: 400;
    font-display: block;
    src: url("../fonts/material-icons/MaterialIconsOutlined.woff2") format("woff2");
}

.material-icons-outlined {
    font-family: "Material Icons Outlined";
    font-weight: normal;
    font-style: normal;
    font-size: 24px;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    display: inline-block;
    white-space: nowrap;
    word-wrap: normal;
    direction: ltr;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-feature-settings: "liga";
}

@font-face {
    font-family: "Pretendard GOV Variable";
    font-style: normal;
    font-weight: 45 920;
    font-display: swap;
    src: url("../fonts/pretendard-gov/PretendardGOVVariable.woff2") format("woff2-variations");
}

@font-face {
    font-family: "Pretendard Std Variable";
    font-style: normal;
    font-weight: 45 920;
    font-display: swap;
    src: url("../fonts/pretendard-std/PretendardStdVariable.woff2") format("woff2-variations");
}

/* ── Reset & Base ── */

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    font-size: 100%;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    font-family: "Pretendard GOV Variable", "Pretendard Std Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-size: var(--font-size-base);
    font-weight: 400;
    line-height: 1.6;
    color: var(--color-text);
    background: var(--color-bg-muted);
}

a {
    color: inherit;
    text-decoration: none;
}

ul, ol {
    list-style: none;
}

button {
    font: inherit;
    color: inherit;
    cursor: pointer;
    border: none;
    background: none;
}

/* ── Layout: Sidebar + Main ── */

.admin-body {
    display: flex;
    min-height: 100vh;
}

/* ── Sidebar ── */

.admin-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 100;
    width: var(--sidebar-width);
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--color-sidebar-bg);
    color: var(--color-sidebar-text);
    transition: width 0.2s ease;
    overflow: hidden;
}

.admin-sidebar.is-collapsed {
    width: var(--sidebar-collapsed-width);
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    height: var(--topbar-height);
    padding: 0 16px;
    border-bottom: 1px solid var(--color-sidebar-divider);
    flex-shrink: 0;
}

.sidebar-brand-icon {
    font-size: 24px;
    color: var(--color-primary-light);
    flex-shrink: 0;
}

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

/* ── Sidebar Navigation ── */

/* ── 사이드바 스크롤막대 ──
   브라우저 기본 막대는 밝고 굵어 어두운 사이드바에서 혼자 튄다.
   폭을 줄이고 색을 사이드바에 맞춘 뒤, 사이드바에 커서가 올라올 때만
   보이게 한다. 자리는 항상 6px 로 잡아 두어 나타날 때 메뉴가 밀리지 않는다. */
.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    padding: 12px 0;
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    transition: scrollbar-color 0.2s ease;
}

.admin-sidebar:hover .sidebar-nav {
    scrollbar-color: var(--color-sidebar-scrollbar) transparent;
}

.sidebar-nav::-webkit-scrollbar {
    width: 6px;
}

.sidebar-nav::-webkit-scrollbar-track {
    background: transparent;
}

.sidebar-nav::-webkit-scrollbar-thumb {
    border-radius: var(--radius-pill);
    background: transparent;
    transition: background 0.2s ease;
}

.admin-sidebar:hover .sidebar-nav::-webkit-scrollbar-thumb {
    background: var(--color-sidebar-scrollbar);
}

.admin-sidebar:hover .sidebar-nav::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.38);
}

/* 접힌 사이드바는 폭이 좁아 막대가 아이콘 자리를 먹는다. */
.admin-sidebar.is-collapsed .sidebar-nav {
    scrollbar-width: none;
}

.admin-sidebar.is-collapsed .sidebar-nav::-webkit-scrollbar {
    width: 0;
}

.sidebar-menu {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 0 8px;
}

.sidebar-menu-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    color: var(--color-sidebar-text);
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
}

/* 가리킨 항목과, 지금 화면이 든 묶음의 부모는 같은 옅은 강조다. */
.sidebar-menu-link:hover,
.sidebar-menu-item.has-children.is-active > .sidebar-menu-parent {
    background: var(--color-sidebar-hover);
    color: var(--color-sidebar-text-active);
}

/* 지금 화면의 항목(맨 위 메뉴든 하위 메뉴든) */
.sidebar-menu-item.is-active > .sidebar-menu-link,
.sidebar-submenu-item.is-active .sidebar-submenu-link {
    background: var(--color-sidebar-active);
    color: var(--color-sidebar-text-active);
}

.sidebar-menu-parent {
    width: 100%;
    cursor: pointer;
}

.sidebar-menu-caret {
    margin-left: auto;
    font-size: 18px;
    transition: transform 0.2s ease;
}

.sidebar-menu-item.has-children.is-open > .sidebar-menu-parent .sidebar-menu-caret {
    transform: rotate(180deg);
}

.sidebar-submenu {
    margin: 0 0 0 12px;
    padding-left: 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transform: translateY(-4px);
    transition: max-height 0.22s ease, opacity 0.2s ease, transform 0.2s ease, margin 0.22s ease;
}

.sidebar-menu-item.has-children.is-open > .sidebar-submenu {
    margin: 4px 0 6px 12px;
    /* 하위 항목 수는 모듈을 추가하면 늘어난다. 고정 높이로 두면 넘치는 항목이
       overflow:hidden 에 잘려 조용히 사라진다. 스크립트가 실제 내용 높이로
       덮어쓰지만, 그게 실패해도 잘리지 않도록 여유를 둔다. */
    max-height: 1200px;
    opacity: 1;
    transform: translateY(0);
}

/* 첫 화면을 그릴 때는 여닫기 애니메이션을 쓰지 않는다. 서버가 열어 둔 묶음이
   화면에 뜨자마자 다시 펼쳐지는 것처럼 보이기 때문이다.
   admin.js 가 첫 상태를 맞춘 뒤 .sidebar-nav 에 is-nav-ready 를 붙인다. */
.sidebar-nav:not(.is-nav-ready) .sidebar-submenu,
.sidebar-nav:not(.is-nav-ready) .sidebar-menu-caret {
    transition: none;
}

.sidebar-submenu-link {
    padding: 8px 10px;
    border-radius: var(--radius-sm);
}

.sidebar-submenu .sidebar-menu-icon {
    font-size: 18px;
}

.sidebar-menu-icon {
    font-size: 20px;
    flex-shrink: 0;
    width: 24px;
    text-align: center;
}

.sidebar-menu-label {
    font-weight: 500;
    overflow: hidden;
}

/* ── 사이드바 알림 배지 ──
   처리할 것이 남은 메뉴를 알린다(승인 대기 건수 등).
   개수가 0 이면 템플릿이 아예 그리지 않으므로 빈 원이 남지 않는다.
   사이드바는 모든 화면의 공용 레이아웃이라 페이지 CSS 가 아니라 여기 둔다. */
.sidebar-menu-badge {
    margin-left: auto;
    min-width: 18px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--color-danger);
    color: var(--color-text-inverse);
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    white-space: nowrap;
    flex-shrink: 0;
}

/* 배지가 오른쪽 끝을 차지했으므로 화살표는 그 옆에 붙는다. */
.sidebar-menu-badge + .sidebar-menu-caret { margin-left: 4px; }

/* 그룹(부모)은 접혀 있어도 알 수 있게 점만 찍는다. 숫자를 크게 붙이면 시선을 뺏는다. */
.sidebar-menu-badge.is-dot {
    min-width: 8px;
    width: 8px;
    height: 8px;
    padding: 0;
    line-height: 0;
}

/* 사이드바를 접으면 글자가 사라지므로 배지도 점으로 줄여 아이콘 모서리에 붙인다.
   붙일 기준이 필요해 접힌 상태에서만 링크에 position 을 준다. */
.is-collapsed .sidebar-menu-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    min-width: 8px;
    width: 8px;
    height: 8px;
    margin: 0;
    padding: 0;
    font-size: 0;
    line-height: 0;
}

/* 접으면 글자는 폭째 감춘다. 아이콘만 남는다. */
.is-collapsed .sidebar-brand-text,
.is-collapsed .sidebar-menu-label {
    opacity: 0;
    width: 0;
}

.is-collapsed .sidebar-menu-link {
    position: relative;
    justify-content: center;
    padding: 10px 0;
}

.is-collapsed .sidebar-submenu,
.is-collapsed .sidebar-menu-caret {
    display: none;
}

/* ── Sidebar Footer ── */

.sidebar-footer {
    padding: 8px;
    border-top: 1px solid var(--color-sidebar-divider);
    flex-shrink: 0;
}

.sidebar-logout-link {
    color: var(--color-sidebar-text) !important;
}

/* 어두운 사이드바 위라 밝은 빨강을 쓴다. 상태색(#dc2626)은 여기서 3.5:1 이다. */
.sidebar-logout-link:hover {
    color: #f87171 !important;
    background: rgba(220, 38, 38, 0.1) !important;
}

/* ── Main Wrapper ── */

.admin-main {
    flex: 1;
    /* 플렉스 칸의 최소 폭은 내용 크기라, 열이 많은 목록표 하나가 이 상자를 늘려
       화면 전체에 가로 스크롤바를 만든다. 0 으로 낮춰 화면 폭 안에 묶어 두면
       넘치는 표는 .admin-table-wrap 안에서만 가로로 스크롤한다. (docs/common/admin-ui.md 1절) */
    min-width: 0;
    margin-left: var(--sidebar-width);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    transition: margin-left 0.2s ease;
}

.admin-sidebar.is-collapsed ~ .admin-main {
    margin-left: var(--sidebar-collapsed-width);
}

/* ── Topbar ── */

.admin-topbar {
    position: sticky;
    top: 0;
    z-index: 90;
    display: flex;
    align-items: center;
    gap: 12px;
    height: var(--topbar-height);
    padding: 0 24px;
    background: var(--color-topbar-bg);
    border-bottom: 1px solid var(--color-topbar-border);
    box-shadow: var(--shadow-sm);
}

.topbar-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    color: var(--color-text-secondary);
    transition: background 0.15s;
}

.topbar-toggle:hover {
    background: var(--color-bg-muted);
}

/* 상단바·카드·팝업의 제목은 같은 굵기와 색이다. */
.topbar-title,
.admin-card-title,
.modal-title {
    font-weight: 600;
    color: var(--color-text);
}

.topbar-spacer {
    flex: 1;
}

.topbar-user {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--color-text-secondary);
}

/* ── Content Area ── */

.admin-content {
    flex: 1;
    padding: 24px;
}

/* ── Common UI Components ── */

/* Card */
.admin-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.admin-card + .admin-card {
    margin-top: 16px;
}

/* 카드 머리와 팝업 머리는 같은 모양이다. 팝업 쪽만 줄어들지 않게 아래에서 더 묶는다. */
.admin-card-header,
.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--color-border);
}

.admin-card-body {
    padding: 20px;
}

.admin-card-body.no-padding {
    padding: 0;
}

/* Stat card */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    align-items: stretch;
    gap: 20px;
    margin-bottom: 24px;
}

.stat-card {
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 20px;
    box-shadow: var(--shadow-sm);
}

.stat-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}

.stat-card-label {
    font-weight: 500;
    color: var(--color-text-secondary);
}

.stat-card-icon {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
    font-size: 20px;
}

.stat-card-icon.is-primary {
    background: var(--color-primary-bg);
    color: var(--color-primary);
}

.stat-card-icon.is-success {
    background: var(--color-success-bg);
    color: var(--color-success);
}

.stat-card-icon.is-warning {
    background: var(--color-warning-bg);
    color: var(--color-warning);
}

.stat-card-icon.is-info {
    background: var(--color-info-bg);
    color: var(--color-info);
}

.stat-card-value {
    font-size: 28px;
    font-weight: 700;
    color: var(--color-text);
    line-height: 1.2;
    /* 숫자 타일은 고정폭 숫자로 그린다. (docs/common/admin-ui.md 숫자 보여주기) */
    font-variant-numeric: tabular-nums;
}

.stat-card-sub {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 4px;
}

/* Table */
.admin-table-wrap {
    overflow-x: auto;
}

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

/* 좌우 여백은 12px 이다. 열이 열여덟인 표에서는 여백만으로 400px 을 넘게 먹어
   그만큼이 그대로 가로 스크롤이 된다. (docs/common/admin-ui.md 1절) */
.admin-table thead th {
    padding: 10px 12px;
    text-align: left;
    font-weight: 600;
    color: var(--color-text-secondary);
    background: var(--color-bg-subtle);
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
    overflow: hidden;
}

.admin-table tbody td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text);
    white-space: nowrap;
}

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

/* ── Table Column Width Utilities ──
   항목 속성에 따른 min-width 유틸리티 클래스.
   <th>와 <td> 모두에 동일한 클래스를 적용하여 정렬을 통일한다.
   table-layout: auto 이므로 브라우저가 내용에 맞게 폭을 자동 계산하고,
   min-width로 최소 폭만 보장한다. white-space: nowrap으로 줄바꿈 방지.
   가운데 정렬은 아래 '가운데에 세우는 열' 한 곳에서 정한다.
   ─────────────────────────────────── */

/* 번호/순서 (No, #, 순번) */
.col-no        { min-width: 56px;  white-space: nowrap; }

/* 고유 ID (DB primary key 등) */
.col-id        { min-width: 72px;  white-space: nowrap; }

/* 상태 (활성/비활성, 공개/비공개 뱃지) */
.col-status    { min-width: 88px;  white-space: nowrap; }

/* 카테고리/유형 (짧은 라벨) */
.col-category  { min-width: 92px;  white-space: nowrap; }

/* 언어 (EN/KO 뱃지) */
.col-lang      { min-width: 80px;  white-space: nowrap; }

/* 조회수, 정렬 순서 등 숫자 */
.col-count     { min-width: 72px;  white-space: nowrap; }

/* 작성자/담당자 (이름) */
.col-author    { min-width: 100px; white-space: nowrap; }

/* 날짜 (YYYY-MM-DD) */
.col-date      { min-width: 110px; white-space: nowrap; }

/* 날짜+시간 (YYYY-MM-DD HH:MM) */
.col-datetime  { min-width: 160px; white-space: nowrap; }

/* 기간 (YYYY-MM-DD ~ YYYY-MM-DD). 한 줄로 두면 240px 을 넘으므로 줄을 나눠 넣는다.
   나누는 자리는 마크업이 <br> 로 정한다. 브라우저에 맡기면 '2027-12-' / '10' 처럼
   날짜 가운데가 끊어지므로 칸 자체는 nowrap 으로 둔다. */
.col-period    { min-width: 118px; white-space: nowrap; }

/* 코드값 (접수번호·상품코드처럼 자리수가 정해진 값) */
.col-code      { min-width: 110px; white-space: nowrap; }

/* IP 주소 */
.col-ip        { min-width: 110px; white-space: nowrap; }

/* 이메일 — 길면 말줄임한다. 전체 값은 셀의 title 로 보여 준다 */
.col-email     { min-width: 160px; max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 전화번호 */
.col-phone     { min-width: 120px; white-space: nowrap; }

/* 소속/기관명 */
.col-org       { min-width: 140px; max-width: 220px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 값 아래에 보조 설명이 한 줄 더 붙는 칸(뱃지 + '희망 구연' 같은 것).
   한 줄로 두면 표가 그 길이만큼 넓어지므로 접는다. 다른 col-* 와 함께 쓴다. */
.col-stacked   { max-width: 160px; }
.col-stacked,
.admin-table tbody td.col-stacked { white-space: normal; }

/* 이름 + 소속처럼 두 줄로 놓이는 사람 정보 */
.col-person    { min-width: 120px; max-width: 200px; }
.col-person,
.admin-table tbody td.col-person { white-space: normal; word-break: break-word; }

/* 브라우저 정보처럼 아주 긴 기술 문자열. 말줄임하고 전체 값은 셀의 title 로 보여 준다 */
.col-agent     { min-width: 160px; max-width: 260px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 미리보기/썸네일 이미지 */
.col-thumb     { min-width: 80px;  white-space: nowrap; }

/* 표 안에서 바로 고치는 select.
   칸 폭을 px 로 정해 두면 select 는 width:100% 라 선택된 값이 잘리고,
   퍼센트 폭은 칸 폭 계산에 잡히지 않아 칸이 스스로 늘어나지도 않는다.
   그래서 select 는 가장 긴 항목에 맞춰 늘리고, 칸은 그만큼만 쓰게 한다.
   글자 크기는 .form-control 의 1rem 을 그대로 받는다(표 안이라도 내리지 않는다). */
.col-select    { width: 1%; white-space: nowrap; }
.col-select .form-control {
    display: inline-block;
    width: auto;
    padding: 4px 8px;
}

/* 제목/설명 — 가변폭, 줄바꿈 허용 */
.col-title     { width: auto; }
.col-title,
.admin-table tbody td.col-title {
    white-space: normal;
    /* anywhere 여야 한다. break-word 는 줄은 접어 주지만 표가 폭을 계산할 때 쓰는
       최소 폭(min-content)은 줄이지 못해, 긴 값 하나가 표를 그만큼 늘려 버린다. */
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* 액션 버튼 (2개 이하) */
.col-actions-sm { min-width: 120px; white-space: nowrap; }
/* 액션 버튼 (3~4개) */
.col-actions-md { min-width: 200px; white-space: nowrap; }
/* 액션 버튼 (5개 이상) */
.col-actions-lg { min-width: 300px; white-space: nowrap; }

/* ── 가운데에 세우는 열 ──
   값의 길이가 대체로 일정한 열이다(번호·날짜·기간·상태·코드·IP·전화번호 …).
   왼쪽 정렬 열(제목·이름·이메일·소속)은 여기에 없으므로 그대로 왼쪽에 남는다.
   (docs/common/admin-ui.md 9절)

   본문 칸(<td>)과 머리글(<th>)을 이 규칙 하나가 함께 정한다. 머리글은 클래스 하나(0,1,0)로는
   `.admin-table thead th`(0,1,2)에 지므로 같은 열을 그 특정도(0,2,2)로 한 번 더 적는다.
   **열을 더하면 위아래 두 묶음에 함께 더한다.** 따로 두었을 때 기간·코드·IP·전화번호·select 가
   머리글 쪽에서 빠져, 본문만 가운데로 서고 머리글은 왼쪽에 남았다.
   .col-check 와 .col-actions 는 admin.css 에 기본 정의가 없어(페이지 CSS 가 폭만 준다)
   본문이 왼쪽이므로 여기에 넣지 않는다. 넣으면 머리글만 가운데로 서서 거꾸로 어긋난다. */
.col-no,
.col-id,
.col-status,
.col-category,
.col-lang,
.col-count,
.col-date,
.col-datetime,
.col-period,
.col-code,
.col-ip,
.col-phone,
.col-thumb,
.col-select,
.col-actions-sm,
.col-actions-md,
.col-actions-lg,
.admin-table thead th.col-no,
.admin-table thead th.col-id,
.admin-table thead th.col-status,
.admin-table thead th.col-category,
.admin-table thead th.col-lang,
.admin-table thead th.col-count,
.admin-table thead th.col-date,
.admin-table thead th.col-datetime,
.admin-table thead th.col-period,
.admin-table thead th.col-code,
.admin-table thead th.col-ip,
.admin-table thead th.col-phone,
.admin-table thead th.col-thumb,
.admin-table thead th.col-select,
.admin-table thead th.col-actions-sm,
.admin-table thead th.col-actions-md,
.admin-table thead th.col-actions-lg {
    text-align: center;
}

/* 숫자 열은 고정폭 숫자로 그린다. 본문 글꼴은 비례폭이라 '1' 이 좁아,
   가운데로 맞춰도 자릿수가 위아래로 어긋난다. (docs/common/admin-ui.md 9절 · 「숫자 보여주기」) */
.col-no,
.col-id,
.col-count,
.col-date,
.col-datetime,
.col-period,
.col-code,
.col-ip,
.col-phone,
.pagination-count,
.pagination-page {
    font-variant-numeric: tabular-nums;
}

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 16px;
    font-weight: 500;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    white-space: nowrap;
}

.btn-primary {
    background: var(--color-primary);
    color: var(--color-text-inverse);
    border-color: var(--color-primary);
}

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

.btn-outline {
    background: transparent;
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}

.btn-outline:hover {
    background: var(--color-bg-subtle);
    border-color: var(--color-border-strong);
}

.btn-sm {
    padding: 5px 10px;
}

/* ── 버튼의 두 축 (docs/common/admin-ui.md 버튼은 축이 둘뿐입니다) ──
   강조(emphasis): .btn-primary · .btn-outline(secondary) · .btn-tertiary 중 하나만 고른다.
   어조(tone): .btn-danger 를 위 셋 어느 것에든 더한다. 없으면 neutral 이다.
   크기는 축이 아니다. .btn-sm 은 치수만 바꾼다. */

/* 가장 낮은 강조. 테두리·바탕 없이 글자만 둔다. 보조 동작(초기화·더 보기)에 쓴다. */
.btn-tertiary {
    background: transparent;
    color: var(--color-primary);
    border-color: transparent;
}

.btn-tertiary:hover {
    background: var(--color-bg-subtle);
}

/* 위험한 동작(삭제·반려). 강조 등급은 그대로 두고 색만 바꾼다. */
.btn-danger {
    color: var(--color-danger-text);
}

.btn-primary.btn-danger {
    background: var(--color-danger);
    border-color: var(--color-danger);
    color: var(--color-text-inverse);
}

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

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

/* 가리키면 위험 바탕을 깐다. 외곽선 버튼은 테두리도 짙어지고, 글자만 있는 버튼은 테두리 없이 둔다. */
.btn-outline.btn-danger:hover {
    background: var(--color-danger-bg);
    border-color: #fca5a5;
}

.btn-tertiary.btn-danger:hover {
    background: var(--color-danger-bg);
    border-color: transparent;
}

/* Badge */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    font-weight: 600;
    border-radius: var(--radius-pill);
}

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

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

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

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

/* Form controls */
.form-group {
    margin-bottom: 16px;
}

.form-label {
    display: block;
    font-weight: 500;
    color: var(--color-text);
    margin-bottom: 6px;
}

/* 글자 크기를 반드시 준다. 입력 요소는 본문 글자 크기를 물려받지 않아, 비워 두면
   브라우저 기본값(13.33px)이 된다. 1rem 아래면 iOS 가 입력할 때 화면을 확대한다.
   (docs/common/admin-ui.md 접근성) */
.form-control {
    display: block;
    width: 100%;
    padding: 8px 12px;
    font-family: inherit;
    font-size: 1rem;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    transition: border-color 0.15s, box-shadow 0.15s;
}

.form-control:focus {
    outline: none;
    border-color: var(--color-primary-light);
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

/* Filter form */
.filter-form {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 20px;
    align-items: flex-end;
}

/* Legacy: pages using direct > div children (non filter-field) */
.filter-form > div:not(.filter-field):not(.filter-form-actions) {
    flex: 1 1 180px;
    min-width: 140px;
}

.filter-field {
    flex: 0 0 auto;
}

.filter-input-keyword {
    width: 260px;
}

.filter-form-actions {
    display: flex;
    gap: 8px;
    align-items: flex-end;
    flex: 0 0 auto;
    padding-bottom: 1px;
}

.filter-form .form-control,
.filter-checkbox-group {
    height: 36px;
    display: flex;
    flex-wrap: nowrap;
    gap: 4px 14px;
    align-items: center;
    padding: 0 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.filter-checkbox-label {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: inherit;
    color: var(--color-text);
    cursor: pointer;
    white-space: nowrap;
}

.filter-checkbox-label input[type="checkbox"] {
    margin: 0;
    cursor: pointer;
}

/* Alert */
.alert-error {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
    padding: 10px 14px;
    border-radius: var(--radius-md);
    margin: 16px;
}

/* Pagination */
.pagination {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

/* 왼쪽에 건수, 가운데에 쪽 버튼, 오른쪽에 쪽 이동.
   건수와 쪽 이동이 같은 몫을 차지해야 가운데 버튼 줄이 실제로 가운데 선다. */
.pagination-count {
    flex: 1 1 0;
    min-width: 0;
    white-space: nowrap;
}

.pagination-nav {
    display: flex;
    align-items: center;
    gap: 4px;
}

.pagination-pages {
    display: flex;
    align-items: center;
    gap: 2px;
    margin: 0 4px;
}

.pagination-page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 32px;
    padding: 0 6px;
    border-radius: var(--radius-md);
    font-size: 13px;
    color: var(--color-text-secondary);
    text-decoration: none;
    transition: background 0.15s, color 0.15s;
}

.pagination-page:hover {
    background: var(--color-bg-subtle);
    color: var(--color-text);
}

/* 처음·앞 쪽에서 앞으로, 끝 쪽에서 뒤로 가는 단추. 자리는 지키되 누를 수 없게 흐리게 둔다(admin-ui.md 10). */
.pagination-page.is-disabled {
    opacity: 0.4;
    pointer-events: none;
}

.pagination-page.is-active {
    background: var(--color-primary);
    color: var(--color-text-inverse);
    font-weight: 600;
    pointer-events: none;
}

.pagination-jump {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    flex: 1 1 0;
    min-width: 0;
}

.pagination-jump-input {
    width: 64px;
    height: 32px;
    padding: 0 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    /* 입력칸이므로 1rem 아래로 내리지 않는다(.form-control 주석). */
    font-size: 1rem;
    text-align: center;
}

.pagination-jump-input:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

/* Hide number input spinners */
.pagination-jump-input::-webkit-outer-spin-button,
.pagination-jump-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.pagination-jump-input[type=number] {
    -moz-appearance: textfield;
}

/* Empty state */
.empty-state {
    text-align: center;
    padding: 48px 24px;
    color: var(--color-text-muted);
}

.empty-state-icon {
    font-size: 48px;
    margin-bottom: 12px;
}

/* ── 파일 올리기 (Uploader) ──
   uploader.js 가 그리는 위젯 — 서버 설정 경고 · 끌어다 놓는 자리 · 안내 · 목록 · 버튼 줄.
   경로는 길어도 줄을 넘기지 않고 말줄임한다. 가로 스크롤바를 만들지 않는다. (docs/common/upload.md) */
.uploader {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.uploader-warning {
    padding: 12px 14px;
    border: 1px solid var(--color-warning);
    border-radius: var(--radius-md);
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
}

.uploader-warning ul {
    margin: 6px 0;
    padding-left: 20px;
}

.uploader-warning p {
    margin: 0;
}

.uploader-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 28px 20px;
    border: 2px dashed var(--color-border-strong);
    border-radius: var(--radius-lg);
    background: var(--color-bg-subtle);
    text-align: center;
    transition: border-color 0.15s, background 0.15s;
}

.uploader-drop.is-dragover {
    border-color: var(--color-primary);
    background: var(--color-primary-bg);
}

.uploader-drop-title {
    margin: 0;
    font-weight: 600;
    color: var(--color-text);
}

.uploader-hint {
    margin: 0;
    color: var(--color-text-muted);
}

.uploader-pick {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-top: 4px;
}

.uploader-message {
    margin: 0;
    padding: 8px 12px;
    border-radius: var(--radius-md);
    background: var(--color-info-bg);
    color: var(--color-info-text);
}

.uploader-message[data-kind="error"] {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

.uploader-message[data-kind="success"] {
    background: var(--color-success-bg);
    color: var(--color-success-text);
}

.uploader-summary {
    display: flex;
    align-items: center;
    gap: 12px;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

.uploader-summary-progress {
    flex: 1;
    min-width: 0;
    accent-color: var(--color-primary);
}

.uploader-list {
    max-height: 360px;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    list-style: none;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.uploader-list:empty {
    display: none;
}

.uploader-item {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto auto;
    align-items: center;
    gap: 4px 12px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--color-border);
}

.uploader-item:last-child {
    border-bottom: 0;
}

.uploader-item-path {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.uploader-item-size {
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

/* 서버가 준 실패 문구는 길 수 있다. 줄을 바꿔 경로 칸을 밀어내지 않게 폭을 묶는다. */
.uploader-item-status {
    max-width: 22em;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

.uploader-item[data-state="done"] .uploader-item-status {
    color: var(--color-success-text);
}

.uploader-item[data-state="failed"] .uploader-item-status,
.uploader-item[data-state="rejected"] .uploader-item-status {
    color: var(--color-danger-text);
}

.uploader-item-progress {
    grid-column: 1 / -1;
    width: 100%;
    height: 6px;
    accent-color: var(--color-primary);
}

.uploader-item[data-state="rejected"] .uploader-item-progress,
.uploader-item[data-state="ready"] .uploader-item-progress {
    display: none;
}

/* ── Login Page ── */

/* 로그인 화면과, 레이아웃 없이 오류만 보여 줄 때(error-plain) 감싸는 영역.
   둘 다 사이드바 없이 화면 가운데에 상자 하나를 띄운다. */
.login-wrapper,
.error-plain-wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: var(--color-bg-muted);
}

.login-box {
    width: 400px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 40px 32px;
}

.login-brand {
    text-align: center;
    margin-bottom: 32px;
}

.login-brand-icon {
    font-size: 40px;
    color: var(--color-primary);
    margin-bottom: 8px;
}

.login-brand-name {
    font-size: 20px;
    font-weight: 700;
    color: var(--color-text);
}

.login-brand-sub {
    color: var(--color-text-muted);
    margin-top: 4px;
}

.login-box .form-control {
    padding: 10px 12px;
}

.login-box .btn {
    width: 100%;
    padding: 10px 16px;
}

/* ── Error Page ── */

/* 레이아웃 없이 오류만 보여줄 때 감싸는 영역(.error-plain-wrapper)은 위 로그인 화면과 함께 정한다. */

.error-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 80px 24px;
    text-align: center;
}

.error-code {
    font-size: 72px;
    font-weight: 700;
    color: var(--color-primary-light);
    line-height: 1;
    margin-bottom: 16px;
}

.error-title {
    font-size: 20px;
    font-weight: 600;
    color: var(--color-text);
    margin-bottom: 8px;
}

.error-message {
    color: var(--color-text-secondary);
    margin-bottom: 24px;
}

/* ── Page Header ── */

.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}

.page-header-left {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.page-header-right {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ── Button-style radio group ──
   켜고 끄는 값처럼 선택지가 두엇뿐일 때 라디오 점 대신 붙은 버튼으로 보여 준다.
   input 은 label 안에 숨기고 바로 뒤의 span 을 버튼처럼 그린다.
   (input:checked + span 이므로 둘은 반드시 형제로 붙어 있어야 한다)
   고른 칸은 강조색으로 채워 한눈에 가른다 — 바탕을 조금 밝게만 하면 어두운 테마에서 구분이 안 된다
   (docs/common/admin-ui.md 버튼형 라디오는 고른 것이 한눈에 보입니다) */

.btn-radio-group {
    display: inline-flex;
    margin: 4px 0;
}

.btn-radio {
    position: relative;
    margin: 0;
}

.btn-radio input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.btn-radio > span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 84px;
    padding: 7px 16px;
    font-size: 13px;
    font-weight: 500;
    color: var(--color-text-secondary);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

/* 버튼끼리 테두리를 겹쳐 한 덩어리로 보이게 한다 */
.btn-radio + .btn-radio > span {
    margin-left: -1px;
}

.btn-radio:first-child > span {
    border-radius: var(--radius-md) 0 0 var(--radius-md);
}

.btn-radio:last-child > span {
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.btn-radio:only-child > span {
    border-radius: var(--radius-md);
}

.btn-radio > span:hover {
    background: var(--color-bg-subtle);
    color: var(--color-text);
}

.btn-radio input:checked + span {
    position: relative;
    z-index: 1;
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-text-inverse);
    font-weight: 600;
}

/* 끈 쪽이 파랗게 강조되면 켠 것처럼 읽힌다. 회색으로 눌러 둔다. */
.btn-radio-off input:checked + span {
    background: var(--color-text-muted);
    border-color: var(--color-text-muted);
}

.btn-radio input:focus-visible + span {
    outline: 2px solid var(--color-primary-light);
    outline-offset: 1px;
}

.btn-radio input:disabled + span {
    opacity: 0.5;
    cursor: not-allowed;
}

.page-title {
    font-size: 20px;
    font-weight: 700;
    color: var(--color-text);
}

.page-title-count {
    font-weight: 400;
    color: var(--color-text-muted);
}

.page-meta {
    color: var(--color-text-secondary);
    font-size: 14px;
}

/* ── Text helpers ── */

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

.icon-sm {
    font-size: 15px;
}

.icon-md {
    font-size: 16px;
}

.btn-link-like {
    color: var(--color-primary);
    text-decoration: none;
}

.btn-link-like:hover {
    color: var(--color-primary-dark);
    text-decoration: underline;
}

/* ── Action Buttons group ── */

.action-btns {
    display: flex;
    gap: 4px;
}

.btn-toggle-off:hover {
    color: var(--color-danger);
    border-color: var(--color-danger);
}

.btn-toggle-on:hover {
    color: var(--color-success-text);
    border-color: var(--color-success);
}

/* ── 순서 변경 표 (AdminOrderTable) ── */

.drag-handle {
    cursor: grab;
    vertical-align: middle;
    font-size: 18px;
    color: var(--color-text-muted);
}

.admin-table tr.is-dragging {
    opacity: 0.4;
}

/* 표 위에 놓는 한 줄 안내 */
.table-hint {
    margin: 0;
    padding: 12px 20px 0;
    font-size: 13px;
    color: var(--color-text-secondary);
}

/* ── Form helpers ──

   아래 규칙은 모든 관리 화면에 그대로 쓰인다.
   pages/*.css 에 같은 이름을 다시 정의하지 않는다. (docs/common/admin-ui.md)
   ── */

.form-required {
    color: var(--color-danger);
    font-weight: 600;
}

.form-static {
    padding: 8px 0;
    font-weight: 600;
    color: var(--color-text);
}

/* 입력 여러 개를 나란히 놓는 격자 */
.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 0 20px;
}

/* 제목·주소처럼 한 줄을 다 쓰는 항목 */
.form-group-wide {
    grid-column: 1 / -1;
}

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

/* 입력칸 바로 오른쪽에 붙는 버튼(인증번호 받기·중복 확인). 줄을 바꾸지 않는다 — 좁으면 입력칸이 줄고 버튼은 그대로다. */
.form-inline-action {
    display: flex;
    align-items: center;
    gap: 8px;
}

.form-inline-action > .form-control {
    flex: 1 1 auto;
    min-width: 0;
}

.form-inline-action > .btn {
    flex: none;
}

/* 항목이 많은 고르기 칸 — 입력해서 찾는다(admin.js 의 AdminSearchSelect, <select data-search-select>).
   목록은 입력칸 아래에 펼쳐 둔다. 떠 있는 목록은 팝업 본문의 스크롤 상자에 잘린다. */
.search-select-list {
    max-height: 240px;
    margin-top: 4px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.search-select-option {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 12px;
    cursor: pointer;
}

.search-select-option:hover,
.search-select-option[aria-selected="true"] {
    background: var(--color-primary-bg);
}

.search-select-group,
.search-select-empty {
    color: var(--color-text-muted);
    font-size: 0.85rem;
}

.search-select-empty {
    padding: 8px 12px;
}

.form-hint {
    margin: 6px 0 0;
    color: var(--color-text-secondary);
    font-size: 0.85rem;
}

/* 설명 안에서 표기법을 그대로 보여 줄 때 */
.form-hint code {
    padding: 1px 4px;
    border-radius: 3px;
    background: var(--color-bg-muted);
    font-size: 0.9em;
}

/* 고객사가 알리고 사이트에서 해 둘 일(client/templates/_aligo-guide.php) — admin·client 의 팝업·카드·실패 자리에 함께 쓴다. */
.aligo-guide {
    margin-top: 10px;
    padding: 12px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-text-secondary);
    font-size: 0.85rem;
    text-align: left;
    /* 좁은 팝업에서 낱말 가운데를 끊지 않는다 */
    word-break: keep-all;
}

.aligo-guide-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.aligo-guide-title {
    color: var(--color-text);
}

.aligo-guide .material-icons-outlined {
    font-size: 16px;
}

.aligo-guide-steps {
    margin: 10px 0 0;
    padding: 0;
    list-style: none;
    counter-reset: aligo-step;
}

.aligo-guide-steps > li {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-left: 28px;
    counter-increment: aligo-step;
}

.aligo-guide-steps > li + li {
    margin-top: 10px;
}

/* 단계 번호 */
.aligo-guide-steps > li::before {
    content: counter(aligo-step);
    position: absolute;
    top: 0;
    left: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: var(--radius-pill);
    background: var(--color-primary);
    color: var(--color-text-inverse);
    font-size: 0.75rem;
    font-weight: 600;
}

.aligo-guide-label {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    min-height: 20px;
    color: var(--color-text);
    font-weight: 600;
}

/* 알리고 화면에서 차례로 누르는 메뉴 */
.aligo-guide-path {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
}

.aligo-guide-path > .material-icons-outlined {
    color: var(--color-text-muted);
}

.aligo-guide-menu {
    padding: 2px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
    white-space: nowrap;
}

/* 등록할 IP — 한 번 누르면 전체가 골라져 바로 복사한다 */
.aligo-guide-ip {
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    background: var(--color-primary-bg);
    color: var(--color-primary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.95em;
    user-select: all;
}

/* 실패 안내(가운데 정렬) 안에서는 글 폭만큼만 */
.empty-state .aligo-guide {
    max-width: 560px;
    margin: 16px auto 0;
}

.form-error {
    margin: 6px 0 0;
    color: var(--color-danger);
    font-size: 0.85rem;
}

/* 서버가 돌려준 칸별 오류(admin.js 의 AdminActions 가 그 칸 아래에 붙인다). 색만으로 알리지 않도록 문구가 함께 붙는다. */
.form-control.is-invalid {
    border-color: var(--color-danger);
}

/* 한 폼 안을 주제별로 나누는 작은 제목.
   카드로 나눌 만큼 크지 않은 묶음(모달 안 등)에 쓴다. */
.form-section-title {
    margin: 24px 0 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--color-border);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-text);
}

.form-section-title:first-of-type {
    margin-top: 0;
}

.form-check {
    display: flex;
    gap: 8px;
    align-items: center;
    margin: 6px 0;
    cursor: pointer;
}

.form-check input {
    width: 16px;
    height: 16px;
    margin: 0;
    cursor: pointer;
}

.form-check-group {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
}

.form-check-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 4px 16px;
    margin-top: 6px;
}

.form-group-indent {
    margin-left: 24px;
    padding-left: 12px;
    border-left: 2px solid var(--color-border);
}

/* 저장·취소 같은 버튼 줄. 기본은 오른쪽 정렬이다. 위에서 아래로 읽고 나면 시선이
   오른쪽 끝에 있고, 마무리 버튼이 그 자리에 있어야 곧바로 누른다. 위 카드와 간격을 둔다.
   상자 안에서 가로로 밀리는 넓은 표 화면은 오른쪽 끝이 화면 밖이므로
   마크업에서 .is-start 를 붙여 왼쪽에 둔다. (docs/common/admin-ui.md 4절) */
.form-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 8px;
    margin: 20px 0 24px;
}

.form-actions.is-start {
    justify-content: flex-start;
}

/* ── 카드 안에서 가로 스크롤바가 생기지 않게 ──

   격자·플렉스 칸은 최소 폭이 내용 크기라, 긴 값 하나가 카드를 밀어내
   화면 아래에 가로 스크롤바를 만든다. 최소 폭을 0 으로 낮추고
   내용은 카드 폭을 넘지 못하게 막는다.
   (열이 많은 목록표는 .admin-table-wrap 안에서만 따로 스크롤한다.) */

.admin-card-body > *,
.form-grid > *,
.form-group {
    min-width: 0;
}

.form-control,
.admin-card-body img,
.admin-card-body pre {
    max-width: 100%;
}

textarea.form-control {
    resize: vertical;
}

/* ── 위지윅 에디터 폭 ──

   --admin-editor-width 는 '글이 실제로 놓이는 폭' 이다.
   client 에서 그 글이 보이는 본문 폭(바깥 상자에서 좌우 여백을 뺀 값)과 같게 잡아,
   관리자가 편집할 때의 줄바꿈·표 크기가 방문자 화면과 어긋나지 않게 한다.

   편집 영역만 늘리면 에디터 상자와 카드를 뚫고 나가므로 바깥부터 맞춘다.
   에디터 상자는 이 폭에 편집 영역의 여백·테두리를 더한 만큼,
   에디터를 담은 폼·카드(.admin-editor-fit)는 거기에 카드의 여백·테두리를 더한 만큼 넓다.
   화면이 좁으면 그 아래로 그대로 줄어든다(가로 스크롤 없음).

   화면별로 다른 폭이 필요하면 그 화면의 에디터를 담은 요소(.admin-editor-fit)에
   style="--admin-editor-width: …" 로 값만 준다. 페이지 CSS 에서 토큰을 다시 정의하지
   않는다 — 규칙 검사기가 막는다(docs/common/admin-ui.md 의 CSS 계층 순서).
   폭을 더하는 계산은 값을 쓰는 요소에서 한다. :root 에서 미리 더해 두면
   화면이 준 --admin-editor-width 가 반영되지 않고 그 시점의 값으로 굳는다.
   토큰 값(--admin-editor-*)은 맨 위 :root 에 있다. */

/* 폭이 아니라 상한이다. 놓인 자리(카드·팝업)가 이보다 좁으면 그만큼 줄어들고,
   넓어도 client 본문 폭을 넘지 않는다. 고정 폭으로 두면 화면이 넓을 때
   에디터만 유난히 길어 보이고, 팝업 안에서는 상자를 뚫고 나간다. */
.ck.ck-editor {
    width: 100%;
    max-width: calc(
        var(--admin-editor-width) + (var(--admin-editor-pad-x) + var(--admin-editor-border)) * 2
    );
}

/* 편집 영역의 좌우 여백을 고정한다. CKEditor 기본값은 글자 크기에 딸린 em 이라
   위 계산과 어긋난다. (자기 폭을 따로 잡는 팝업 편집기는 그쪽 규칙이 이긴다) */
.ck.ck-editor .ck.ck-editor__editable_inline {
    padding-left: var(--admin-editor-pad-x);
    padding-right: var(--admin-editor-pad-x);
}

/* 에디터가 든 카드(또는 그 카드를 담은 폼)에 붙인다.
   카드 안쪽 폭이 에디터 상자와 딱 맞아, 글이 놓이는 폭이 client 본문 폭과 같아진다. */
.admin-editor-fit {
    max-width: calc(
        var(--admin-editor-width)
        + (var(--admin-editor-pad-x) + var(--admin-editor-border) + var(--admin-editor-card-pad)) * 2
    );
}

/* ── Modal ── */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 42, 0.5);
    backdrop-filter: blur(2px);
}

/* 팝업 위에 쌓인 층(admin.js 의 AdminModal). 배경을 한 겹 더 짙게 깔면 뒤가 까맣게 가려진다.
   아래 창이 비쳐 보여야 그 창이 그대로 있다는 것이 보인다. 흐림(blur)도 한 번이면 충분하다.
   층마다 z-index 를 한 칸씩 올리는 것은 admin.js 가 한다. (docs/common/admin-ui.md 2절) */
.modal-backdrop.is-stacked {
    background: rgba(15, 23, 42, 0.28);
    backdrop-filter: none;
}

/* 확인창(admin.js 의 AdminActions.ask)의 전용 층. 쌓인 팝업(한 칸씩 올라 최대 +50)보다 늘 위이고, 알림(toast)보다는 아래다. */
.modal-backdrop.is-confirm {
    z-index: 260;
}

/* 확인창 본문은 적힌 줄바꿈 그대로 보인다 — 「무엇이 일어나는지」 와 「되돌릴 수 있는지」 를 두 줄로 적는다. */
.confirm-message {
    white-space: pre-line;
}

/* 팝업 크기는 담을 내용에 맞춰 등급 하나를 골라 붙인다.
   페이지 CSS 에서 .modal-dialog 를 다시 정의하지 않는다. (docs/common/admin-ui.md)

     (없음) 480  입력 두세 개짜리 짧은 폼
     sm     440  확인·비밀번호 변경
     md     680  파일 입력·안내가 함께 있는 폼
     lg     800  두 열 격자, 결과 표, 본문 에디터
     xl     900  프리셋 격자, 실제 크기 미리보기, 내역 표 + 폼
     xxl   1100  항목이 많은 상세·수정 폼, 본문 에디터가 든 폼
     full  1400  화면을 거의 다 쓰는 편집 폼 */
.modal-dialog {
    width: var(--modal-width, 480px);
    max-width: calc(100vw - 48px);
    max-height: calc(100vh - 80px);
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: modalSlideIn 0.2s ease;
}

.modal-dialog-sm   { --modal-width: 440px; }
.modal-dialog-md   { --modal-width: 680px; }
.modal-dialog-lg   { --modal-width: 800px; }
/* .modal-dialog-wide 는 xl 의 예전 이름이다. 새 화면에는 xl 을 쓴다. */
.modal-dialog-xl,
.modal-dialog-wide { --modal-width: 900px; }
.modal-dialog-xxl  { --modal-width: 1100px; }
.modal-dialog-full { --modal-width: 1400px; }

@keyframes modalSlideIn {
    from { opacity: 0; transform: translateY(-16px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* 머리 모양은 카드 머리(.admin-card-header)와 함께 정한다. 내용이 길어도 머리는 줄지 않는다. */
.modal-header {
    flex-shrink: 0;
}

.modal-close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    transition: background 0.15s, color 0.15s;
}

.modal-close:hover {
    background: var(--color-bg-muted);
    color: var(--color-text);
}

.modal-body {
    padding: 20px;
    overflow-y: auto;
    flex: 1;
}

/* .modal-body 의 flex·overflow 는 부모가 flex 일 때만 듣는다.
   폼이 .modal-dialog 와 .modal-body 사이에 끼면 그 고리가 끊겨
   내용이 길 때 모달이 화면 밖으로 넘친다. 폼에도 같은 방향을 준다. */
.modal-form {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

.modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 20px;
    border-top: 1px solid var(--color-border);
    flex-shrink: 0;
}

/* 불러온 화면이 자기 스크롤 영역을 가진 경우. admin.js 가 is-passthrough 를 붙인다.

       <form class="modal-form">
         <div class="modal-body">   ← 여기만 스크롤한다
         <div class="modal-footer"> ← 스크롤 밖. 늘 보인다

   상자의 본문까지 함께 스크롤하면 스크롤바가 둘 생기고 바닥 줄이 스크롤에 묻힌다. 상자는 스크롤을 양보한다. */
.modal-body.is-passthrough {
    padding: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* ── 목록 위 팝업으로 띄운 페이지 ──

   등록·수정·상세는 화면을 옮기지 않고 목록 위 팝업에서 처리한다.
   내용은 그 화면의 템플릿을 그대로 받아 오므로, 팝업에서 뜻이 겹치는 것만 여기서 감춘다.
   (admin.js 의 AdminModal, docs/common/admin-ui.md) */

/* 제목과 '목록' 버튼은 팝업 머리글과 닫기 버튼이 대신한다. */
.modal-body > .page-header {
    display: none;
}

/* 긴 폼에서 저장 버튼을 찾아 끝까지 내려가지 않게 바닥에 붙인다.
   좌우 여백을 상쇄해 팝업 폭을 가득 채우고, 위 내용과 경계선으로 나눈다. */
.modal-body > form > .form-actions,
.modal-body > .form-actions {
    position: sticky;
    bottom: -20px;
    z-index: 1;
    margin: 20px -20px -20px;
    padding: 12px 20px;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
}

/* 불러오는 중 표시는 곧바로 띄우지 않는다. 대개 금방 끝나는 요청에 글자가 깜빡이고
   지나가면 오히려 느려 보인다. 자리는 처음부터 차지해 팝업이 들썩이지 않게 하고,
   글자만 잠시 뒤에 보인다(admin.js 가 300ms 뒤에 글자의 hidden 을 뗀다). (docs/common/admin-ui.md 13절) */
.modal-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 120px;
    color: var(--color-text-muted);
}

/* 못 불러온 팝업 — 같은 자리, 같은 높이에 이유와 「다시 시도」. (docs/common/admin-ui.md 13절) */
.modal-load-error {
    flex-direction: column;
    padding: 16px;
    text-align: center;
}

.modal-load-error-text {
    margin: 0;
    color: var(--color-text-secondary);
    word-break: keep-all;
}

.modal-load-error-actions {
    display: flex;
    gap: 8px;
    justify-content: center;
}

/* ── 툴팁 ──

   네이티브 title 툴팁은 커서 아래에 뜬다. 버튼이 촘촘한 목록표에서는 아래 줄을 가려
   무엇을 누르는지 보이지 않으므로, 커서 위에 직접 그린다.
   (admin.js 의 AdminTooltip 이 title 을 data-admin-tip 으로 옮겨 준다) */

.admin-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 400;
    max-width: 320px;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    background: var(--color-tooltip-bg);
    color: var(--color-tooltip-text);
    font-size: 12px;
    line-height: 1.5;
    white-space: pre-line;
    box-shadow: var(--shadow-md);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.12s;
}

.admin-tooltip.is-on {
    opacity: 1;
}

/* 가리키는 곳을 향한 꼬리. 아래로 내려 그릴 때만 방향이 뒤집힌다. */
.admin-tooltip::after {
    content: '';
    position: absolute;
    left: var(--tooltip-arrow-x, 50%);
    margin-left: -5px;
    border: 5px solid transparent;
    border-top-color: var(--color-tooltip-bg);
    top: 100%;
}

.admin-tooltip.is-below::after {
    border-top-color: transparent;
    border-bottom-color: var(--color-tooltip-bg);
    top: auto;
    bottom: 100%;
}

/* ── Toast notification ── */

.toast-container {
    position: fixed;
    top: 24px;
    right: 24px;
    z-index: 300;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}

.toast {
    pointer-events: auto;
    min-width: 280px;
    max-width: 420px;
    padding: 12px 16px;
    border-radius: var(--radius-md);
    font-weight: 500;
    box-shadow: var(--shadow-md);
    animation: toastSlideIn 0.25s ease;
    display: flex;
    align-items: center;
    gap: 8px;
}

.toast.is-success {
    background: var(--color-success-bg);
    color: var(--color-success-text);
    border: 1px solid #bbf7d0;
}

.toast.is-error {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
    border: 1px solid var(--color-danger-border);
}

/* 사라질 때. admin.js 의 showToast 가 3초 뒤에 붙이고, 0.3초 뒤에 지운다. */
.toast.is-leaving {
    opacity: 0;
    transition: opacity 0.3s;
}

@keyframes toastSlideIn {
    from { opacity: 0; transform: translateX(24px); }
    to   { opacity: 1; transform: translateX(0); }
}

/* ── Disabled button state ── */

/* pointer-events: none 이라 커서 모양(cursor)은 주지 않는다. 버튼이 커서를 받지 않아 보이지 않는다. */
.btn:disabled,
.btn.is-loading {
    opacity: 0.6;
    pointer-events: none;
}

.btn.is-disabled {
    opacity: 0.5;
    pointer-events: none;
}

/* 숨기는 표시 둘. .is-hidden 은 스크립트가 붙이는 클래스(팝업 등)다.
   hidden 속성은 저자가 준 display(flex·grid·block)에 진다. .btn·.form-actions·.form-control
   처럼 display 를 가진 공통 클래스에 el.hidden = true 를 주면 아무 일도 일어나지 않는다.
   페이지 CSS 는 공통 클래스를 다시 정의하지 않으므로 여기서 한 번에 이기게 한다.
   (docs/common/frontend.md 이벤트) */
.is-hidden,
[hidden] {
    display: none !important;
}

.toast-icon {
    font-size: 18px;
}

/* 알림 옆 버튼 — 줄을 지운 뒤의 「되돌리기」 같은 것(AdminUtils.showToast 의 세 번째 인자).
   글자색을 따라 알림과 같은 색으로 그린다. */
.toast-action {
    margin-left: 12px;
    padding: 2px 8px;
    border: 1px solid currentColor;
    border-radius: var(--radius-sm);
    background: transparent;
    color: inherit;
    font: inherit;
    font-weight: 700;
    cursor: pointer;
}

/* ── 탭 ──
   학술대회 수정 화면의 섹션 탭에서 시작해 회비 현황도 함께 쓴다.
   화면이 둘이 되었으므로 페이지 CSS 가 아니라 여기에 둔다.
   모서리·색·전환은 .pagination-page 를 그대로 따랐다. */
.section-tabs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin-bottom: 20px;
}

.section-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: 14px;
    text-decoration: none;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.section-tab:hover {
    background: var(--color-bg-subtle);
    border-color: var(--color-border-strong);
    text-decoration: none;
}

.section-tab.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-text-inverse);
    font-weight: 600;
}

/* 꺼 둔 섹션도 들어갈 수는 있어야 한다. 다시 켜려면 들어가야 하기 때문이다. */
.section-tab.is-off {
    color: var(--color-text-muted);
    background: var(--color-bg-subtle);
}

.section-tab.is-off.is-active {
    background: var(--color-primary);
    color: var(--color-text-inverse);
    opacity: 0.75;
}

.section-tab-off {
    font-size: 11px;
    padding: 1px 6px;
    border-radius: var(--radius-pill);
    background: #e5e7eb;
    color: var(--color-text-secondary);
}

.section-tab.is-active .section-tab-off {
    background: rgba(255, 255, 255, 0.25);
    color: var(--color-text-inverse);
}

/* ── 카드 탭 ──
   한 건에 이력이 딸린 폼(거래처·직원 등)의 위쪽 탭 줄. 첫 탭이 편집 폼이고 나머지는 읽기 표다.
   여닫기와 키보드(Ctrl+PgUp/PgDn)는 admin.js 의 data-card-tabs 가 맡는다. 바닥 선은 그림자로 긋는다 —
   탭의 굵은 밑줄이 이 선 위에 겹쳐야 하는데 테두리로 그으면 탭을 음수 여백으로 끌어내려야 한다. */
.card-tabs-nav {
    display: flex;
    gap: 4px;
    margin: 0 0 20px;
    box-shadow: inset 0 -1px 0 var(--color-border-strong);
}

.card-tab {
    padding: 8px 16px;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    color: var(--color-text-secondary);
    font: inherit;
    cursor: pointer;
}

.card-tab:hover {
    color: var(--color-text);
}

.card-tab.is-active {
    color: var(--color-primary);
    border-bottom-color: var(--color-primary);
    font-weight: 600;
}

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

/* 탭에 붙는 개수. 열어 보기 전에 무엇이 몇 개인지 보인다. */
.card-tab-count {
    margin-left: 4px;
    color: var(--color-text-muted);
    font-size: 12px;
}

.card-tab.is-active .card-tab-count {
    color: inherit;
}

/* ── 고르는 칸 + 「새 ○○」 ──
   선행 업무(admin.js 의 AdminPick, docs/common/frontend.md). 칸이 남은 폭을 쓰고 버튼은 글자 폭만큼이다. */
.pick-pair {
    display: flex;
    align-items: center;
    gap: 8px;
}

.pick-pair.is-select select {
    flex: 1 1 auto;
    min-width: 0;
}

.pick-pair.is-select .btn {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* ── 좁은 화면(720px 이하) — 사이드바를 본문 위로 꺼냈다 넣는다 ──
   바탕 규칙(사이드바·상단바·통계 카드·쪽 이동)보다 뒤에 있어야 덮는다. 이 파일 끝쪽에 둔다.
   본문 옆에 붙여 두면 390px 화면에서 사이드바가 250px 를 차지해 본문이 140px 만 남고
   화면 전체가 가로로 밀린다(docs/common/admin-ui.md 1절). 좁은 화면에서는 사이드바를 화면 밖에 두고
   상단바의 메뉴 단추로 본문 위에 펼친다. 여닫기와 접기(is-collapsed)를 넓은 화면에서만 쓰는 판단은 admin.js 의 Sidebar toggle 이 한다. */
@media (max-width: 720px) {
    .admin-sidebar,
    .admin-sidebar.is-collapsed {
        width: var(--sidebar-width);
        transform: translateX(-100%);
        /* 닫혀 있을 때 Tab 으로 화면 밖 메뉴에 들어가지 않게 숨긴다. 닫히는 움직임이 끝난 뒤에 숨긴다. */
        visibility: hidden;
        transition: transform 0.2s ease, visibility 0s linear 0.2s;
    }

    .admin-sidebar.is-open {
        transform: none;
        visibility: visible;
        box-shadow: var(--shadow-lg);
        transition: transform 0.2s ease;
    }

    .admin-main,
    .admin-sidebar.is-collapsed ~ .admin-main {
        margin-left: 0;
    }

    /* 펼친 사이드바 뒤를 가린다. 누르면 닫힌다(admin.js).
       z-index 는 상단바와 같은 90 — 문서에서 뒤에 오므로 상단바를 덮고, 사이드바(100) 아래에 깔린다(z-index 계층 표). */
    .admin-body.is-sidebar-open::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 90;
        background: rgba(15, 23, 42, 0.5);
    }

    .admin-topbar {
        padding: 0 16px;
    }

    .topbar-title {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .topbar-user {
        flex-shrink: 0;
        white-space: nowrap;
    }

    .admin-content {
        padding: 16px;
    }

    .page-header {
        flex-wrap: wrap;
        gap: 8px;
    }

    .stat-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 12px;
    }

    /* 건수 옆에 붙는 안내(client 의 앞뒤 이동 목록)가 한 줄로 늘어서 화면을 밀지 않게 */
    .pagination-count {
        white-space: normal;
    }

    /* 붙은 라디오 단추(btn-radio-group)는 칸 폭 안에서 나눠 가진다. 단추 하나에 84px 를 고집하면
       넷만 돼도 팝업 폭(342px)을 넘는다. */
    .btn-radio-group {
        display: flex;
        max-width: 100%;
    }

    .btn-radio {
        flex: 1 1 auto;
        min-width: 0;
    }

    .btn-radio > span {
        width: 100%;
        min-width: 0;
        padding: 7px 8px;
    }
}

/* ── 손가락으로 누르는 기기 ──
   관리 화면은 PC 가 기본이라 PC 치수는 그대로 두고, 터치 화면에서만 누를 자리를
   44×44 이상으로 넓힌다. 작으면 옆 버튼을 잘못 누른다. (docs/common/admin-ui.md 접근성) */
@media (pointer: coarse) {
    .btn,
    .btn-sm,
    .form-control,
    .section-tab,
    .btn-radio > span,
    .sidebar-menu-link,
    .form-check {
        min-height: 44px;
    }

    .modal-close,
    .topbar-toggle,
    .pagination-page {
        min-width: 44px;
        min-height: 44px;
    }

    .pagination-jump-input,
    .filter-form .form-control,
    .filter-checkbox-group {
        height: 44px;
    }
}

/* ── 움직임 줄이기 ──
   사용자가 운영체제에서 움직임을 줄여 달라고 했으면 팝업·토스트의 미끄러짐과
   전환 효과를 끈다. 어지럼을 느끼는 사람이 있다. 지연(불러오는 중 표시)은 그대로 둔다.
   (docs/common/admin-ui.md 접근성) */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
