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

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

/* ── CSS Variables ── */

: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-topbar-bg: #ffffff;
    --color-topbar-border: #e2e8f0;

    --color-surface: #ffffff;
    --color-border: #e2e8f0;
    --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-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-bg-subtle: #f8fafc;
    --color-bg-muted: #f1f5f9;

    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;

    --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);

    /* 카드 사이 간격은 이 값 하나뿐이다. 화면마다 margin 을 따로 주면
       간격이 조금씩 달라져 정돈되지 않아 보인다. */
    --gap: 20px;
}

/* ── 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 rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

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

.sidebar-brand-text {
    font-weight: 700;
    color: #ffffff;
    white-space: nowrap;
    overflow: hidden;
}

.is-collapsed .sidebar-brand-text {
    opacity: 0;
    width: 0;
}

/* ── 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: rgba(255, 255, 255, 0.22) transparent;
}

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

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

.sidebar-nav::-webkit-scrollbar-thumb {
    border-radius: 999px;
    background: transparent;
    transition: background 0.2s ease;
}

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

.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 {
    background: var(--color-sidebar-hover);
    color: var(--color-sidebar-text-active);
}

.sidebar-menu-item.is-active > .sidebar-menu-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-active > .sidebar-menu-parent {
    background: var(--color-sidebar-hover);
    color: var(--color-sidebar-text-active);
}

.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-submenu .sidebar-menu-label {
    font-weight: 500;
}

.sidebar-submenu-item.is-active .sidebar-submenu-link {
    background: var(--color-sidebar-active);
    color: var(--color-sidebar-text-active);
}

.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: 999px;
    background: var(--color-danger, #dc2626);
    color: #fff;
    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-menu-label {
    opacity: 0;
    width: 0;
}

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

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

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

/* ── Sidebar Footer ── */

.sidebar-footer {
    padding: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    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 {
    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: var(--gap);
}

.admin-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--color-border);
}

.admin-card-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-text);
}

.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: var(--gap);
    margin-bottom: var(--gap);
}

.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: #f0fdf4;
    color: var(--color-success);
}

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

.stat-card-icon.is-info {
    background: #f0f9ff;
    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; text-align: center; }

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

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

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

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

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

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

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

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

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

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

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

/* 이메일 — 길면 말줄임한다. 전체 값은 셀의 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; text-align: center; }

/* 소속/기관명 */
.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; text-align: center; }

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

/* 제목/설명 — 가변폭, 줄바꿈 허용 */
.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; text-align: center; }
/* 액션 버튼 (3~4개) */
.col-actions-md { min-width: 200px; white-space: nowrap; text-align: center; }
/* 액션 버튼 (5개 이상) */
.col-actions-lg { min-width: 300px; white-space: nowrap; text-align: center; }

/* 머리글도 같은 정렬을 따르게 한다.
   위 유틸리티는 클래스 하나(0,1,0)라 `.admin-table thead th`(0,1,2)에 진다.
   그래서 지금까지 본문 칸만 가운데로 서고 머리글은 왼쪽에 남아 어긋나 보였다.
   "<th>와 <td> 모두에 같은 클래스를 적용해 정렬을 통일한다" 는 위 주석의 뜻대로
   같은 특정도로 한 번 더 못박는다. 왼쪽 정렬 열(제목·이름·이메일)은 여기에 없으므로
   그대로 왼쪽에 남는다.
   .col-check 와 .col-actions 는 admin.css 에 기본 정의가 없어(페이지 CSS 가 폭만 준다)
   본문이 왼쪽이므로 여기에 넣지 않는다. 넣으면 머리글만 가운데로 서서 거꾸로 어긋난다. */
.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-thumb,
.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 3절) */
.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: #ffffff;
    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: #cbd5e1;
}

.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: #ffffff;
}

.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: #fecaca;
}

.btn-outline.btn-danger:hover,
.btn-tertiary.btn-danger:hover {
    background: #fef2f2;
    border-color: #fca5a5;
}

.btn-tertiary.btn-danger:hover {
    border-color: transparent;
}

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

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

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

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

.badge-info {
    background: #f0f9ff;
    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: #ffffff;
    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;
    box-sizing: border-box;
    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: #ffffff;
}

.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: #fef2f2;
    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);
}

.pagination-page.is-active {
    background: var(--color-primary);
    color: #fff;
    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;
    box-sizing: border-box;
}

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

/* ── Login Page ── */

.login-wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: #f1f5f9;
}

.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 {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: #f1f5f9;
}

.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;
    gap: 12px;
    margin-bottom: var(--gap);
}

.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 이므로 둘은 반드시 형제로 붙어 있어야 한다) */

.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: #ffffff;
    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-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;
}

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

/* 한 폼 안을 주제별로 나누는 작은 제목.
   카드로 나눌 만큼 크지 않은 묶음(모달 안 등)에 쓴다. */
.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 6절) */
.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-width 만 바꾼다.
   폭을 더하는 계산은 값을 쓰는 요소에서 한다. :root 에서 미리 더해 두면
   페이지가 바꾼 --admin-editor-width 가 반영되지 않고 그 시점의 값으로 굳는다. */

:root {
    /* 글이 실제로 놓이는 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;
}

/* 폭이 아니라 상한이다. 놓인 자리(카드·팝업)가 이보다 좁으면 그만큼 줄어들고,
   넓어도 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);
}

/* 팝업 크기는 담을 내용에 맞춰 등급 하나를 골라 붙인다.
   페이지 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-xl   { --modal-width: 900px; }
.modal-dialog-xxl  { --modal-width: 1100px; }
.modal-dialog-full { --modal-width: 1400px; }

/* 예전 이름. 새 화면에는 위 등급을 쓴다. */
.modal-dialog-wide { --modal-width: 900px; }

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

.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
}

.modal-title {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--color-text);
}

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

/* 불러오는 중 표시는 곧바로 띄우지 않는다. 대개 금방 끝나는 요청에 글자가 깜빡이고
   지나가면 오히려 느려 보인다. 자리는 처음부터 차지해(visibility) 팝업이 뛰지 않게 하고,
   글자만 잠시 뒤에 보인다. (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);
    animation: modalLoadingDelay 0s linear 300ms both;
}

@keyframes modalLoadingDelay {
    from { visibility: hidden; }
    to   { visibility: visible; }
}

/* ── 툴팁 ──

   네이티브 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: #1e293b;
    color: #f8fafc;
    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: #1e293b;
    top: 100%;
}

.admin-tooltip.is-below::after {
    border-top-color: transparent;
    border-bottom-color: #1e293b;
    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: #f0fdf4;
    color: var(--color-success-text);
    border: 1px solid #bbf7d0;
}

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

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

/* ── Disabled button state ── */

.btn:disabled,
.btn.is-loading {
    opacity: 0.6;
    cursor: not-allowed;
    pointer-events: none;
}

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

.is-hidden {
    display: none !important;
}

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

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

/* ── 탭 ──
   학술대회 수정 화면의 섹션 탭에서 시작해 회비 현황도 함께 쓴다.
   화면이 둘이 되었으므로 페이지 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: #fff;
    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: #cbd5e1;
    text-decoration: none;
}

.section-tab.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #ffffff;
    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: #ffffff;
    opacity: 0.75;
}

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

.section-tab.is-active .section-tab-off {
    background: rgba(255, 255, 255, 0.25);
    color: #fff;
}

/* ============================================================
   akierp 운영 화면에서 더한 공통 컴포넌트
   ============================================================ */

/* ── 카드 배치 ──
   카드를 세로로 쌓을 때는 .stack, 나란히 둘 때는 .grid-2 · .grid-2-1 을 쓴다.
   간격은 모두 --gap 하나다. .stack 안에서는 형제 카드의 margin 을 쓰지 않는다. */
.stack {
    display: flex;
    flex-direction: column;
    gap: var(--gap);
}

.stack > .admin-card + .admin-card,
.grid-2 > .admin-card + .admin-card,
.grid-2-1 > .admin-card + .admin-card {
    margin-top: 0;
}

.grid-2,
.grid-2-1 {
    display: grid;
    gap: var(--gap);
    align-items: stretch;
}

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

.stat-grid > *,
.grid-2 > *,
.grid-2-1 > * {
    min-width: 0;
}

/* 포인트 줄처럼 카드가 다섯인 통계 줄. 넷 기준 격자에 다섯을 넣으면 하나가 혼자 다음 줄로 내려간다.
   칸이 좁아 제목이 두 줄이 되면 낱말 중간("매/출")이 아니라 낱말 사이에서 꺾이게 한다. */
.stat-grid.cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.stat-grid.cols-5 .stat-card-label { word-break: keep-all; }

@media (max-width: 1100px) {
    .stat-grid,
    .stat-grid.cols-5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .grid-2,
    .grid-2-1 { grid-template-columns: minmax(0, 1fr); }
}

.admin-card-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

.admin-card-footer {
    padding: 12px 20px;
    border-top: 1px solid var(--color-border);
}

/* ── 버튼 — 축은 둘뿐이다 (docs/common/admin-ui.md "버튼은 축이 둘뿐입니다")
   emphasis: primary · secondary · tertiary  (하나만 고른다)
   tone:     neutral(기본) · danger           (emphasis 와 조합한다)
   크기는 축이 아니다. .btn-sm 은 치수만 바꾼다. */
.btn-secondary {
    background: var(--color-surface);
    color: var(--color-text);
    border-color: var(--color-border);
}

.btn-secondary:hover {
    background: var(--color-bg-subtle);
    border-color: #cbd5e1;
}

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

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

.btn-primary.tone-danger {
    background: var(--color-danger);
    border-color: var(--color-danger);
    color: #ffffff;
}

.btn-primary.tone-danger:hover {
    background: #b91c1c;
    border-color: #b91c1c;
}

.btn-secondary.tone-danger,
.btn-tertiary.tone-danger {
    color: var(--color-danger);
}

.btn-secondary.tone-danger:hover {
    background: #fef2f2;
    border-color: #fecaca;
}

.btn-tertiary.tone-danger:hover {
    background: #fef2f2;
}

/* 아이콘만 있는 버튼. aria-label 을 반드시 단다 */
.btn-icon {
    padding: 6px;
}

.btn .material-icons-outlined {
    font-size: 18px;
}

/* ── 배지 색 — App\Erp\View::badge() 가 붙이는 tone-* ──
   색만으로 뜻을 전하지 않도록 배지에는 늘 글자가 함께 있다. */
.badge {
    font-size: 12px;
    line-height: 1.6;
    white-space: nowrap;
}

.badge.tone-primary { background: var(--color-primary-bg); color: var(--color-primary); }
.badge.tone-success { background: #f0fdf4; color: #15803d; }
.badge.tone-warning { background: #fffbeb; color: #b45309; }
.badge.tone-danger  { background: #fef2f2; color: var(--color-danger); }
.badge.tone-info    { background: #f0f9ff; color: #0369a1; }
.badge.tone-muted   { background: var(--color-bg-muted); color: var(--color-text-secondary); }

/* ── 숫자 ──
   금액·개수는 고정폭 숫자로 그려 자릿수가 위아래로 맞게 한다.
   금액 열은 끝자리를 맞춰야 크기를 비교할 수 있어 오른쪽 정렬이다. */
.num,
.stat-card-value,
.col-count,
.col-money {
    font-variant-numeric: tabular-nums;
}

.col-money { min-width: 96px; white-space: nowrap; text-align: right; }
.admin-table thead th.col-money { text-align: right; }

/* 이름 + 보조 글 두 줄(회사명 + 대표자 등) */
.cell-sub {
    display: block;
    font-size: 12px;
    color: var(--color-text-muted);
}

/* 값과 단위는 2:1 이다. 단위가 값만큼 시선을 가져가지 않게 한다. */
.stat-card-unit {
    margin-left: 2px;
    font-size: 14px;
    font-weight: 600;
    color: var(--color-text-secondary);
}

.stat-card-link {
    color: inherit;
}

.stat-card-link:hover .stat-card-label {
    color: var(--color-primary);
}

/* ── 쪽 번호 줄 — App\Erp\View::pager() 의 마크업 ──
   쪽이 하나뿐이어도 그린다(docs/common/admin-ui.md 12번). */
.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 20px;
    border-top: 1px solid var(--color-border);
}

.pager-count,
.pager-jump {
    flex: 1 1 0;
    min-width: 0;
    white-space: nowrap;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

.pager-count strong {
    color: var(--color-text);
    font-weight: 600;
}

.pager-nav {
    display: flex;
    align-items: center;
    gap: 2px;
}

.pager-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);
    font-variant-numeric: tabular-nums;
    transition: background 0.15s, color 0.15s;
}

.pager-page .material-icons-outlined {
    font-size: 18px;
}

.pager-page:hover {
    background: var(--color-bg-muted);
    color: var(--color-text);
}

.pager-page.is-active {
    background: var(--color-primary);
    color: #ffffff;
    font-weight: 600;
}

.pager-page.is-disabled {
    opacity: 0.35;
    pointer-events: none;
}

.pager-jump {
    display: flex;
    justify-content: flex-end;
}

.pager-jump form {
    display: flex;
    align-items: center;
    gap: 8px;
}

.pager-jump .input {
    width: 72px;
    height: 32px;
    padding: 0 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    font-size: 1rem;
    text-align: center;
}

/* ── 비어 있음 — App\Erp\View::empty() ──
   왜 비었는지와 다음에 할 일을 함께 적는다. */
.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 200px;
    padding: 40px 24px;
    text-align: center;
    color: var(--color-text-muted);
}

.empty .material-icons-outlined {
    font-size: 40px;
}

.empty-title {
    font-weight: 600;
    color: var(--color-text-secondary);
}

.empty-desc {
    font-size: 14px;
}

.empty.is-compact {
    min-height: 120px;
    padding: 24px 16px;
}

/* ── 안내 상자 — 화면이 왜 이렇게 동작하는지 한 줄로 알린다 ── */
.admin-callout {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 16px;
    border: 1px solid #bae6fd;
    border-radius: var(--radius-lg);
    background: #f0f9ff;
    color: #075985;
}

.admin-callout .material-icons-outlined {
    font-size: 20px;
    flex-shrink: 0;
}

.admin-callout.tone-warning {
    border-color: #fde68a;
    background: #fffbeb;
    color: #92400e;
}

/* ── 정보 목록 (상세 팝업의 읽기 전용 값) ──
   라벨 열 폭을 하나로 두어 값의 시작선이 위아래로 맞게 한다. */
.info-list {
    display: grid;
    grid-template-columns: 110px minmax(0, 1fr);
    gap: 8px 16px;
}

.info-list dt {
    color: var(--color-text-secondary);
    font-weight: 500;
}

.info-list dd {
    min-width: 0;
    overflow-wrap: anywhere;
}

.info-list.cols-2 {
    grid-template-columns: 110px minmax(0, 1fr) 110px minmax(0, 1fr);
}

@media (max-width: 720px) {
    .info-list.cols-2 { grid-template-columns: 110px minmax(0, 1fr); }
}

/* 긴 본문(문의 내용·공지 본문)을 적힌 줄바꿈 그대로 보여 준다. */
.text-block {
    padding: 12px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ── 수정 화면을 전체 화면으로 열었을 때 ──
   같은 템플릿이 팝업에서는 팝업 상자 안에, 전체 화면에서는 카드 안에 놓인다.
   팝업 안에서는 테두리를 겹치지 않도록 전체 화면일 때만 카드 모양을 준다. */
.admin-content > .page-form {
    padding: 20px 20px 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.page-form > section + section,
.page-form > section + .form-section-title {
    margin-top: 24px;
}

/* ── 칸별 오류 ── 서버가 준 errors 를 admin.js 가 그 칸 아래에 붙인다. */
.form-control.is-invalid {
    border-color: var(--color-danger);
}

/* 필터 줄의 입력칸은 높이 36px 로 맞춰져 있다. 버튼도 같은 높이로 둔다. */
.filter-form .btn {
    height: 36px;
}

.filter-form .filter-input-month {
    width: 170px;
}

/* 목록 표 위의 요약 줄(상태별 건수·합계 등) */
.list-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 24px;
    padding: 12px 20px;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-bg-subtle);
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

.list-summary strong {
    color: var(--color-text);
}

/* ── 상태별 비율 막대 (대시보드) ── 막대 폭은 값이 곧 데이터라 인라인 style 로 준다. */
.ratio-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ratio-row {
    display: grid;
    grid-template-columns: 96px minmax(0, 1fr) 56px;
    align-items: center;
    gap: 12px;
}

.ratio-track {
    height: 8px;
    border-radius: 999px;
    background: var(--color-bg-muted);
    overflow: hidden;
}

.ratio-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--color-primary-light);
}

.ratio-fill.tone-success { background: var(--color-success); }
.ratio-fill.tone-info { background: var(--color-info); }
.ratio-fill.tone-warning { background: var(--color-warning); }
.ratio-fill.tone-muted { background: var(--color-text-muted); }

.ratio-value {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* ── 로그인 화면 오류 줄 ── */
.login-error {
    margin-bottom: 16px;
    padding: 10px 14px;
    border-radius: var(--radius-md);
    background: #fef2f2;
    color: var(--color-danger);
}

.login-error[hidden] {
    display: none;
}

/* ── 확인 창 본문 ── */
.confirm-message {
    white-space: pre-line;
}

/* ── 유틸리티 ── 한 가지만 한다. 마지막이라 앞의 것을 이긴다. */
.text-right { text-align: right; }
.text-danger { color: var(--color-danger); }
.text-secondary { color: var(--color-text-secondary); }
.nowrap { white-space: nowrap; }
.mt-0 { margin-top: 0; }
.mt-sm { margin-top: 12px; }

/* ── 손가락으로 누르는 기기 ──
   관리 화면은 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,
    .pager-page {
        min-width: 44px;
        min-height: 44px;
    }

    .pagination-jump-input,
    .pager-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;
    }
}
