/* ============================================================
   Linekto 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: 1.5rem;
    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;
}

/* ── 글자 크기: 사람이 고른다 (화면 규칙 7) ──
   루트 값을 바꾸면 화면 전체가 같은 비율로 커진다. 상자를 rem 으로 짰기
   때문이다(화면 규칙 4). px 로 짰다면 글자만 커지고 상자는 그대로였다. */
html {
    font-size: 100%;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

html[data-text-size="small"]  { font-size: 87.5%; }
html[data-text-size="normal"] { font-size: 100%; }
html[data-text-size="large"]  { font-size: 112.5%; }
html[data-text-size="xlarge"] { font-size: 125%; }

/* 고정폭 글꼴은 숫자를 많이 보는 사람이 고른다. */
html[data-font="mono"] body { font-family: var(--font-family-mono); }

body {
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    font-weight: 400;
    line-height: var(--leading-normal);
    color: var(--color-text);
    background: var(--color-bg-muted);
    /* 숫자는 자리를 맞춘다. 금액이 세로로 줄지어 있을 때 자릿수가
       어긋나면 눈으로 비교할 수 없다(→ docs/ui-rules.md 3번). */
    font-variant-numeric: tabular-nums;
}

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

ul, ol {
    list-style: none;
}

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

/* 🔴 `button` 만 글꼴을 물려받고 있었다. 입력칸·고르개·여러줄칸은
   브라우저 기본 글꼴(13.3px)로 떨어져서, **라벨보다 입력 글자가 작았다.**
   눈에 잘 안 띄지만 화면 전체에서 계속 어긋나 보이던 원인이다. */
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

/* ── CSS Variables ── */

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

    /* ── 간격 ──────────────────────────────────────────────
       🔴 **간격 토큰이 없어서** 값이 제각각이었다. 같은 뜻의 자리에
       0.75 · 1 · 1.25 · 1.5rem 이 섞여 있었고, 화면마다 미묘하게 달랐다.
       네 칸(0.25rem) 배수 한 줄로 세운다. 이제 "카드 사이" 같은 것은
       숫자가 아니라 **이름**으로 고른다. */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-12: 3rem;

    /* ── 글자 크기 ────────────────────────────────────────
       🔴 **본문과 입력칸은 1rem 아래로 안 내린다.** 입력칸이 1rem 미만이면
       iOS 가 focus 때 화면을 확대한다(→ docs/ui-rules.md 7번), 본문은
       접근성 최소선이다(→ docs/common/admin-ui.md 접근성).
       작아지는 것은 **보조 글자**뿐이다 — 머리글·힌트·뱃지처럼
       내용이 아니라 내용을 설명하는 것들. */
    --text-xs: 0.75rem;      /* 뱃지 */
    --text-sm: 0.8125rem;    /* 표 머리글 · 힌트 */
    --text-md: 0.875rem;     /* 라벨 · 작은 단추 */
    --text-base: 0.9375rem;  /* 표 내용 · 단추 */
    --text-lg: 1.125rem;     /* 카드 제목 */
    --text-xl: 1.375rem;     /* 페이지 제목 */

    /* 줄 간격. 단추·뱃지처럼 **높이를 padding 이 정하는** 것은 1 이다.
       1.6 을 물려받으면 줄상자가 글자보다 훨씬 커지고, 그 안에서 글자가
       한쪽으로 쏠린다(단추 글자가 아래로 치우쳐 보이던 원인). */
    --leading-none: 1;
    --leading-snug: 1.4;
    --leading-normal: 1.6;

    /* 북미 사용자가 본다. 운영체제가 주는 글꼴을 그대로 쓰는 것이
       가장 빠르고, 어느 기기에서도 낯설지 않다. */
    --font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-family-mono: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Consolas", "Liberation Mono", monospace;

    --sidebar-width: 15.625rem;
    --sidebar-collapsed-width: 4rem;
    --topbar-height: 3.5rem;

    /* 🔴 어두운 남색(#1e40af)이라 단추가 배경에 묻혔다. 한 단계 밝고
       또렷한 파랑으로 올린다. 흰 글자와 5.17:1 이라 본문 기준(4.5:1)을
       넘는다 — 색만 예뻐지고 안 읽히면 안 된다. */
    --color-primary: #2563eb;
    --color-primary-light: #60a5fa;
    --color-primary-dark: #1d4ed8;
    --color-primary-bg: #eff6ff;
    /* 🔴 **강조색은 두 가지 일을 한다** — 단추의 바탕이 되거나, 바탕 위의
       글자가 된다. 한 값으로 둘 다 하면 한쪽을 맞출 때 다른 쪽이 깨진다.
       바탕일 때는 흰 글자와 견주고(위 `--color-primary`, 5.17:1),
       글자일 때는 카드 바탕과 견준다(아래, 6.8:1). */
    --color-primary-text: #1d4ed8;
    /* 초점 테두리와 선택 상태가 쓰는 옅은 파랑. 색을 한 곳에서 고른다. */
    --color-primary-ring: rgba(37, 99, 235, 0.18);

    /* 🔴 **사이드바 글자는 두 가지뿐이다** — 누를 수 있는 것과 없는 것.
       색을 셋 넷으로 나누면 어느 것이 링크인지가 흐려진다.

       `text` 는 메뉴 항목(누를 수 있는 것)이고 바탕에서 12.0:1 이다.
       `muted` 는 소제목과 아이콘(누를 수 없는 것)이고 5.9:1 이다 —
       본문 기준 4.5:1 을 넘기므로 작은 글씨에도 쓸 수 있다.
       전에는 둘이 같은 #94a3b8 이라 소제목과 항목이 구분되지 않았다. */
    --color-sidebar-bg: #0f172a;
    --color-sidebar-text: #cbd5e1;
    --color-sidebar-muted: #8296ae;
    --color-sidebar-text-active: #ffffff;
    --color-sidebar-hover: #1e293b;
    --color-sidebar-active: #2563eb;

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

    --color-surface: #ffffff;
    --color-border: #e2e8f0;
    --color-text: #1e293b;
    --color-text-secondary: #64748b;
    --color-text-muted: #94a3b8;

    /* 🔴 이 넷은 **옅은 바탕 위의 글자**로도 쓰인다(뱃지). 전에는 대비가
       3.15 였고, 뱃지를 12px 로 줄이면서 더 중요해졌다. 한 단계 진하게
       내려서 옅은 바탕 위에서도 4.5:1 을 넘긴다. 흰 글자를 얹는 자리
       (토스트 등)에서는 진해질수록 오히려 더 잘 읽힌다. */
    --color-success: #15803d;
    --color-warning: #b45309;
    --color-danger: #b91c1c;
    --color-info: #0369a1;

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

    /* 아래 넷은 컴포넌트가 색을 직접 박아 두었던 자리다. 이름을 주어
       테마가 갈아끼울 수 있게 했다(어두운 테마에서 흰 입력칸이 남던 문제). */
    --color-input-bg: #ffffff;
    --color-on-primary: #ffffff;
    --color-border-strong: #cbd5e1;

    /* 배지·토스트의 옅은 바탕. 어두운 테마에서는 반대로 짙어진다. */
    --color-success-soft: #f0fdf4;
    --color-warning-soft: #fffbeb;
    --color-danger-soft: #fef2f2;
    --color-info-soft: #f0f9ff;

    --color-tooltip-bg: #1e293b;
    --color-tooltip-text: #f8fafc;

    --radius-sm: 0.25rem;
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;

    /* 카드가 종이 한 장처럼 보이도록 테두리와 그림자를 함께 쓴다.
       그림자만 진하게 하면 탁해지고, 테두리만 있으면 납작해진다. */
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
    --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);
}

/* ── 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);
    /* 어두운 테마에서는 본문 바탕도 짙어 사이드바와의 경계가 흐려진다.
       밝은 테마에서는 바탕 차이에 묻혀 보이지 않는 정도의 선이다. */
    border-right: 1px solid rgba(255, 255, 255, 0.08);
    transition: width 0.2s ease;
    overflow: hidden;
}

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

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    height: var(--topbar-height);
    padding: 0 1rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

.sidebar-brand-icon {
    font-size: 1.5rem;
    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;
}

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

/* ── Sidebar Navigation ── */

/* ── 사이드바 스크롤막대 ──
   브라우저 기본 막대는 밝고 굵어 어두운 사이드바에서 혼자 튄다.
   폭을 줄이고 색을 사이드바에 맞춘 뒤, 사이드바에 커서가 올라올 때만
   보이게 한다. 자리는 항상 0.375rem 로 잡아 두어 나타날 때 메뉴가 밀리지 않는다. */
.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    padding: 0.75rem 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: 0.375rem;
}

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

.sidebar-nav::-webkit-scrollbar-thumb {
    border-radius: 62.4375rem;
    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: 0.25rem;
    padding: 0 0.5rem;
}

.sidebar-menu-link {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 0.75rem;
    border-radius: var(--radius-md);
    color: var(--color-sidebar-text);
    /* 최상위는 묶음의 이름이다. 하위 항목(500)보다 굵게 두어, 색을 바꾸지
       않고도 어느 줄이 윗단인지 알 수 있게 한다. */
    font-weight: 600;
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
}

/* is-active 의 왼쪽 띠가 붙을 기준. */
.sidebar-menu-link {
    position: relative;
}

.sidebar-menu-link:hover {
    background: var(--color-sidebar-hover);
    color: var(--color-sidebar-text-active);
}

/* 🔴 아이콘은 제 색을 갖고 있어 줄의 color 만 바꾸면 따라오지 않는다.
   가리킨 줄·지금 보는 줄에서는 글자와 함께 밝아져야 한 덩어리로 보인다. */
.sidebar-menu-link:hover .sidebar-menu-icon,
.sidebar-menu-link.is-active .sidebar-menu-icon,
.sidebar-menu-item.has-children.is-active > .sidebar-menu-parent .sidebar-menu-icon {
    color: inherit;
}

/* 🔴 **마크업은 링크 자신에 is-active 를 붙인다**(app·ops 레이아웃).
   전에는 부모 <li> 를 가리키고 있어 현재 화면 표시가 통째로 안 먹었다.
   셀렉터가 안 맞으면 오류가 나지 않고 **조용히 아무 일도 안 일어난다.** */
.sidebar-menu-link.is-active {
    background: var(--color-sidebar-active);
    color: var(--color-sidebar-text-active);
    font-weight: 600;
}

/* 지금 보는 곳이라는 표시를 왼쪽 띠로도 준다 — 색만으로 뜻을 전하지 않는다
   (→ docs/common/admin-ui.md 접근성). 사이드바를 접어 아이콘만 남아도
   이 띠는 그대로 보인다. */
.sidebar-menu-link.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.375rem;
    bottom: 0.375rem;
    width: 0.1875rem;
    border-radius: 0 62.4375rem 62.4375rem 0;
    background: var(--color-sidebar-text-active);
}

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

.sidebar-menu-caret {
    margin-left: auto;
    font-size: 1.125rem;
    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 0.75rem;
    padding-left: 0.625rem;
    /* 어디까지가 한 묶음인지 선으로도 보여 준다. 들여쓰기만으로는 항목이
       여럿일 때 한눈에 안 들어온다. (1px 구분선은 화면 규칙 4번의 예외) */
    border-left: 1px solid var(--color-sidebar-hover);
    display: flex;
    flex-direction: column;
    /* 항목이 붙어 있으면 어느 줄을 누르는지 헷갈린다. 벌려 두면 고른 항목의
       배경도 띠가 아니라 하나의 덩어리로 읽힌다. 전에는 0.125rem(2px) 이라
       배경이 서로 닿아 한 덩어리처럼 보였다. */
    gap: 0.375rem;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transform: translateY(-0.25rem);
    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: 0.375rem 0 0.5rem 0.75rem;
    /* 하위 항목 수는 모듈을 추가하면 늘어난다. 고정 높이로 두면 넘치는 항목이
       overflow:hidden 에 잘려 조용히 사라진다. 스크립트가 실제 내용 높이로
       덮어쓰지만, 그게 실패해도 잘리지 않도록 여유를 둔다. */
    max-height: 75rem;
    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-section {
    /* 🔴 **위는 넓게, 아래는 붙인다.** 이것이 소제목을 알아보게 하는 가장
       센 장치다 — 위로 벌어진 만큼 앞 덩어리와 끊기고, 아래로 붙은 만큼
       뒤따르는 항목들과 한 덩어리로 읽힌다. 위 1.1rem 대 아래 0.25rem. */
    margin: 0.75rem 0 -0.125rem;
    /* 항목 글자보다 한 칸 왼쪽에서 시작한다. 왼쪽 선이 같으면 목록의 한 줄로
       보이고, 어긋나 있으면 **다른 종류의 줄**로 읽힌다. */
    padding: 0 0.75rem 0 0.375rem;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    /* 항목(12.0:1)보다 흐린 5.9:1 — 누를 수 없는 줄이라는 뜻이다. 본문
       기준 4.5:1 은 넘으므로 작은 글씨여도 읽을 수 있다. */
    color: var(--color-sidebar-muted);
    /* 글자를 끌어 보려는 손을 막는다. 누를 수 없는 줄이다. */
    user-select: none;
}

/* 맨 위 소제목은 묶음 제목 바로 아래라 여백이 겹친다. */
.sidebar-submenu-section:first-child {
    margin-top: 0.125rem;
}

/* 🔴 최상위 링크와 **다른 클래스**다. `.sidebar-menu-link` 의 골격을 물려받지
   못하므로 여기서 다시 준다 — 전에는 flex 가 아니라 배지가 오른쪽 끝으로
   못 가고, hover 가 없어 누를 수 있는 줄로 안 보였다. */
.sidebar-submenu-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    /* 최상위와 같은 모서리. 좁은 항목에 각진 배경을 칠하면 띠처럼 보인다. */
    border-radius: var(--radius-md);
    color: var(--color-sidebar-text);
    /* 색은 최상위와 같다 — **둘 다 누를 수 있는 것**이기 때문이다.
       한 단 아래라는 것은 굵기와 크기가 말한다. */
    font-weight: 500;
    font-size: 0.9375rem;
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
}

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

.sidebar-submenu .sidebar-menu-icon {
    font-size: 1.125rem;
}

/* 하위 항목에는 왼쪽 띠를 붙이지 않는다 — 이미 들여쓰기로 한 단 안쪽이라
   띠까지 겹치면 줄이 두 개로 보인다. 배경과 굵기로 충분하다. */
.sidebar-submenu-link.is-active {
    background: var(--color-sidebar-active);
    color: var(--color-sidebar-text-active);
    font-weight: 600;
}

/* 아이콘은 글자를 **찾아가는 표지**이지 글자와 겨루는 것이 아니다. 같은
   밝기로 두면 줄마다 먼저 눈에 들어와 이름을 읽는 것을 늦춘다. 3:1 만
   넘으면 되는 자리라(→ docs/ui-rules.md) 한 단 흐린 5.9:1 로 둔다. */
.sidebar-menu-icon {
    color: var(--color-sidebar-muted);
    font-size: 1.25rem;
    flex-shrink: 0;
    width: 1.5rem;
    text-align: center;
}

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

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

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

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

/* 사이드바를 접으면 글자가 사라지므로 배지도 점으로 줄여 아이콘 모서리에 붙인다.
   붙일 기준이 필요해 접힌 상태에서만 링크에 position 을 준다. */
.is-collapsed .sidebar-menu-badge {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    min-width: 0.5rem;
    width: 0.5rem;
    height: 0.5rem;
    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: 0.625rem 0;
}

/* 🔴 접으면 글자가 사라져 **아이콘이 유일한 단서**가 된다. 펼쳤을 때처럼
   한 단 흐리게 두면, 정작 아이콘만 남은 상태에서 메뉴가 읽히지 않는다. */
.is-collapsed .sidebar-menu-icon {
    color: var(--color-sidebar-text);
}

/* ── 접힌 사이드바의 옆패널 ──

   🔴 전에는 여기서 끝이었다. 접으면 하위 메뉴가 사라져 **묶음 안 화면
      마흔다섯 곳에 갈 길이 없었고**, 그래서 접기 단추는 있어도 못 쓰는
      기능이었다. 지금은 아이콘에 대면 그 묶음이 옆에 펴진다.
      → docs/ui-rules.md 13번 · docs/ui-structure.md 접으면 아이콘 레일 */
.is-collapsed .sidebar-submenu {
    display: none;
}

/* 자리(left·top)는 스크립트가 인라인으로 준다 — 어느 아이콘 옆에 붙을지는
   그때그때 다르고, 아래쪽 아이콘에서는 위로 밀어 올려야 한다.
   `position: fixed` 라 사이드바의 overflow 에 잘리지 않는다. */
.admin-sidebar.is-collapsed .sidebar-menu-item.is-flyout > .sidebar-submenu {
    display: flex;
    position: fixed;
    z-index: 110;
    min-width: 13.75rem;
    max-width: 18rem;
    /* 🔴 항목이 아주 많은 묶음(입찰까지 켠 `영업` 은 열한 줄이다)을 낮은
          화면에서 열면 패널이 화면보다 커진다. 그때는 패널 안에서 스크롤한다
          — 화면 밖으로 흘러 나가면 아래쪽 항목은 누를 방법이 없다. */
    max-height: calc(100vh - 1rem);
    margin: 0;
    padding: 0.5rem;
    /* 펼친 사이드바에서 묶음을 잇는 왼쪽 선은 여기서는 뜻이 없다.
       패널 자체가 이미 한 덩어리로 떠 있다. */
    border-left: 0;
    border-radius: var(--radius-md);
    background: var(--color-sidebar-bg);
    /* 본문 위에 떠 있는 것이라 그림자로 띄운다. 선만으로는 본문 글자와
       겹쳐 읽힌다. */
    box-shadow: 0 0.625rem 1.875rem rgba(0, 0, 0, 0.35);
    opacity: 1;
    overflow-x: hidden;
    overflow-y: auto;
    scrollbar-width: thin;
    transform: none;
    /* 여닫기 애니메이션은 펼친 사이드바의 것이다. 패널은 마우스를 따라
       바로바로 떠야 해서 기다릴 시간이 없다. */
    transition: none;
}

/* 아이콘만 보고 연 패널에 제목이 없으면 여기가 어느 묶음인지 알 수 없다.
   펼친 사이드바에서는 묶음 이름이 바로 위에 있으므로 그리지 않는다. */
.sidebar-flyout-title {
    display: none;
}

.admin-sidebar.is-collapsed .sidebar-menu-item.is-flyout > .sidebar-submenu > .sidebar-flyout-title {
    display: block;
    margin: 0 0 0.375rem;
    padding: 0.25rem 0.75rem 0.5rem;
    border-bottom: 1px solid var(--color-sidebar-hover);
    color: var(--color-sidebar-text-active);
    font-size: 0.9375rem;
    font-weight: 600;
    white-space: nowrap;
    /* 누르는 줄이 아니다. 끌어 보려는 손을 막는다. */
    user-select: none;
}

/* 🔴 접힌 사이드바에서 배지는 점으로 줄여 아이콘에 붙인다. 그 규칙이
      패널 안까지 따라오면 **개수가 안 보인다** — 패널은 폭이 넉넉해
      숫자를 그대로 보여 줄 수 있고, 몇 건인지가 여기서는 정보다. */
.admin-sidebar.is-collapsed .sidebar-menu-item.is-flyout .sidebar-submenu-link .sidebar-menu-badge {
    position: static;
    width: auto;
    height: auto;
    min-width: 1.125rem;
    margin-left: auto;
    padding: 0 0.375rem;
    font-size: 0.6875rem;
    line-height: 1.125rem;
}

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

/* ── Sidebar Footer ── */

.sidebar-footer {
    padding: 0.5rem;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

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

.sidebar-logout-link:hover {
    color: var(--color-danger) !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: 0.75rem;
    height: var(--topbar-height);
    padding: 0 1.5rem;
    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: 2.25rem;
    height: 2.25rem;
    border-radius: var(--radius-sm);
    color: var(--color-text-secondary);
    transition: background 0.15s;
}

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

.topbar-title {
    min-width: 0;
    overflow: hidden;
    font-weight: 600;
    color: var(--color-text);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar-spacer {
    flex: 1;
}

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

/* ── Content Area ── */

.admin-content {
    flex: 1;
    padding: var(--space-6);
    /* 🔴 **세로 간격을 여기 한 곳에서 정한다.**
       전에는 요소마다 `margin-bottom` 으로 만들었는데, 그러면 그 값을 안 가진
       요소 뒤에 오는 것은 **그냥 붙는다.** 보고서 화면에서 거르개 카드와
       요약 칸 사이가 0px 이었던 것이 이것이다 — 카드에는 아래 여백이 없고
       요약 칸에는 위 여백이 없었다.
       `gap` 은 무엇이 오든 사이를 똑같이 벌린다. 새 화면을 만들 때 여백을
       손으로 적을 일이 없어진다(→ docs/ui-rules.md 4번). */
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    /* 넓은 모니터에서 표가 화면 끝까지 늘어나면 눈이 한 줄을 따라가지
       못한다. 그렇다고 좁게 묶으면 열 많은 표가 못 들어간다 — 이 정도가
       열두 열짜리 표가 들어가면서 눈이 따라갈 수 있는 폭이다. */
    max-width: 110rem;
    width: 100%;
    margin: 0 auto;
}

/* ── 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`
   로 붙였는데, 그러면 사이에 다른 것(요약 칸·탭 줄)이 끼는 순간 간격이
   사라졌다. 지금은 **담는 쪽**(`.admin-content` · `.card-list` · `.m-main`)이
   `gap` 으로 정한다 — 무엇이 오든 사이가 같다.

   카드를 세로로 쌓는 목록. `<li class="admin-card">` 처럼 쓴다. */
.card-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    list-style: none;
    margin: 0;
    padding: 0;
}

/* 🔴 `hidden` 속성은 저자가 지정한 `display:flex` 에 **집니다.**
   이 줄이 없으면 `el.hidden = true` 가 아무 일도 안 한다
   (→ docs/common/frontend.md). 판독 결과 목록이 그렇게 여닫힌다. */
.card-list[hidden] {
    display: none;
}

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

/* 카드 **안에** 또 머리말을 둘 때. 바깥 테두리와 여백을 물려받으면
   줄이 두 번 그어지고 여백이 겹친다. 두 화면이 `style="padding:0"` 으로
   때우고 있었는데, 그렇게 하면 padding 만 지워지고 **밑줄은 남는다.** */
.admin-card-header.is-plain {
    padding: 0;
    border-bottom: 0;
}

/* 🔴 제목에 크기가 없어서 `<h2>` 기본값(1.5em ≈ 24px)으로 나왔다.
   본문 16px 의 1.5배라 카드마다 제목이 과하게 컸고, 한 화면은
   `style="font-size:1rem"` 으로 따로 눌러 놓고 있었다. */
.admin-card-title {
    font-size: var(--text-lg);
    font-weight: 600;
    line-height: var(--leading-snug);
    letter-spacing: -0.01em;
    color: var(--color-text);
    /* 제목 옆에 붙는 한 줄 설명이 제목 크기로 따라 커지지 않게. */
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.admin-card-title .form-hint {
    margin-top: 0;
    font-weight: 400;
}

.admin-card-body {
    padding: var(--space-6);
}

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

/* Stat card */
.stat-grid {
    display: grid;
    /* 넉 줄로 못을 박지 않는다. 화면이 좁아지면 칸이 줄 바꿈으로 내려가야
       하고, 그러지 않으면 카드가 화면 밖으로 나가 가로 스크롤이 생긴다. */
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    align-items: stretch;
    gap: var(--space-5);
}

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

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

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

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

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

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

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

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

.stat-card-value {
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--color-text);
    line-height: 1.2;
}

.stat-card-sub {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    margin-top: 0.25rem;
}

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

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

/* 좌우 여백은 0.75rem 이다. 열이 열여덟인 표에서는 여백만으로 25rem 을 넘게 먹어
   그만큼이 그대로 가로 스크롤이 된다. (docs/common/admin-ui.md 1절) */
/* 🔴 머리글이 본문과 **같은 크기**라 어디까지가 이름이고 어디부터가
   자료인지 눈이 못 갈랐다. 작게·넓게·옅게 해서 머리글은 "이름표" 로,
   내용은 "읽을 것" 으로 보이게 한다. */
.admin-table thead th {
    padding: 0.625rem 0.75rem;
    text-align: left;
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    /* 🔴 여기서 한 단계 더 옅게(`--color-text-muted`) 했더니 대비가
       2.45 로 떨어졌다. 작게·자간 넓게 한 것만으로 위계는 이미 보인다 —
       **옅게 만드는 것은 위계가 아니라 읽기 어려움**이다. */
    color: var(--color-text-secondary);
    background: var(--color-bg-subtle);
    /* 머리글과 내용 사이는 줄 사이보다 진하게. 표의 "지붕" 이 보여야 한다. */
    border-bottom: 1px solid var(--color-border-strong);
    white-space: nowrap;
    overflow: hidden;
}

.admin-table tbody td {
    padding: 0.625rem 0.75rem;
    font-size: var(--text-base);
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text);
    white-space: nowrap;
}

/* 마지막 줄의 밑줄은 카드 테두리와 겹쳐 두 겹으로 보인다. */
.admin-table tbody tr:last-child td {
    border-bottom: 0;
}

.admin-table tbody tr {
    transition: background 0.1s ease;
}

.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: 3.5rem;  white-space: nowrap; text-align: center; }

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

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

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

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

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

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

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

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

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

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

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

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

/* 전화번호 */
.col-phone     { min-width: 7.5rem; white-space: nowrap; text-align: center; }

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

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

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

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

/* 미리보기/썸네일 이미지 */
.col-thumb     { min-width: 5rem;  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: 0.25rem 0.5rem;
    font-size: 0.75rem;
}

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

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    /* 🔴 **글자가 아래로 치우쳐 보이던 원인은 줄 간격이다.**
       본문의 1.6 을 물려받으면 16px 글자가 25.6px 짜리 줄상자 안에 놓이고,
       그 여백이 위아래로 고르게 안 퍼져 대문자 띠가 한가운데서 벗어난다
       (재어 보니 위 12.00 / 아래 13.59). 1 로 두고 **높이는 padding 이
       정하게** 하면 위 8.00 / 아래 8.00 으로 딱 맞는다. */
    line-height: var(--leading-none);
    /* 큰 단추와 작은 단추의 **글자 크기는 같다.** 다른 것은 여백뿐이다
       (→ docs/common/admin-ui.md "버튼은 축이 둘뿐입니다" — 크기는 축이 아니다).
       덤으로 반올림이 짝수로 떨어져 가운데가 정확히 맞는다. */
    font-size: var(--text-md);
    padding: 0.75rem var(--space-4);
    font-weight: 500;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
    white-space: nowrap;
    /* 글자를 고를 일이 없는 자리다. 두 번 눌렀을 때 파랗게 잡히면 지저분하다. */
    user-select: none;
}

/* 🔴 단추 안의 아이콘은 **글자에 맞춰** 커지고 작아진다.
   기본 아이콘은 1.5rem(24px)이라, `icon-sm` 을 빠뜨린 단추에서는
   24px 짜리 그림이 14px 글자 옆에 서서 글자가 눌려 보였다(모바일 4곳).
   `em` 이라 단추 글자를 바꾸면 아이콘이 따라온다 — 빠뜨릴 자리가 없다. */
.btn .material-icons,
.btn .material-icons-outlined {
    font-size: 1.15em;
    line-height: 1;
    /* 아이콘은 글자의 가운데 띠에 맞춘다. 밑줄에 맞추면 위로 떠 보인다. */
    flex: 0 0 auto;
}

/* 키보드로 다니는 사람에게 지금 어디인지 보여 준다. 마우스로 누를 때는
   안 뜬다(:focus-visible). 테두리 굵기라 px 예외에 해당한다. */
.btn:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

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

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

.btn-primary:active,
.btn-outline:active {
    /* 눌린 순간 살짝 가라앉는다. 0.15s 전환이 붙어 있으면 눌린 것이
       늦게 보이므로 여기서는 전환을 끈다. */
    transform: translateY(0.0625rem);
    transition: none;
}

.btn-outline {
    background: transparent;
    /* 본문 색을 쓴다. 보조 글자 색(--color-text-secondary)으로 두면 어두운
       배경에서 테두리와 글자가 함께 묻혀 눌리지 않는 버튼처럼 보인다. */
    color: var(--color-text);
    border-color: var(--color-border-strong);
}

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

.btn-sm {
    padding: 0.5rem var(--space-3);
}

/* 아이콘만 있는 단추는 **네모**여야 한다. 좌우 padding 을 그대로 두면
   글자 단추처럼 옆으로 퍼져서, 옆에 선 단추와 짝이 안 맞는다. */
.btn-icon {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
}

/* Badge */
/* 🔴 뱃지가 본문과 같은 크기(16px)라 문장만큼 커 보였다. 뱃지는 값이
   아니라 **값에 붙는 꼬리표**이므로 한 단계 작아야 한다. 줄 간격도 1.4 로
   묶어야 세로로 부풀지 않는다. */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.1875rem 0.5rem;
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
    border-radius: 624.938rem;
}

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

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

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

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

/* 🔴 **일부러 안 한 것**을 위한 중립 색. 실패(빨강)도 성공(초록)도 아니다.
   메일이 꺼져 있어 안 나간 줄이 이 색이다 — 빨강으로 칠하면 매일 빨개지고,
   그때부터 진짜 장애가 그 빨강에 묻힌다(→ libs/MailOut.php).
   색은 토큰으로만 적는다. 어두운 테마가 같은 이름을 갈아끼운다. */
.badge-muted {
    background: var(--color-bg-muted);
    color: var(--color-text-secondary);
}

/* Form controls */
.form-group {
    margin-bottom: 1rem;
}

.form-label {
    display: block;
    /* 라벨은 값이 아니라 값의 이름이다. 입력 글자보다 작아야 눈이
       값을 먼저 읽는다 — 지금까지는 반대였다(라벨 16px, 입력 13.3px). */
    font-size: var(--text-md);
    font-weight: 500;
    color: var(--color-text-secondary);
    margin-bottom: 0.375rem;
}

.form-control {
    display: block;
    width: 100%;
    padding: 0.5rem 0.75rem;
    font-family: inherit;
    /* 🔴 **1rem 아래로 안 내린다.** iOS 는 입력칸 글자가 16px 미만이면
       focus 할 때 화면을 확대해 버린다(→ docs/ui-rules.md 7번). */
    font-size: 1rem;
    line-height: var(--leading-snug);
    color: var(--color-text);
    background: var(--color-input-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

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

.form-control:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--color-primary-ring);
}

/* Filter form */
.filter-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1.25rem;
    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 11.25rem;
    min-width: 8.75rem;
}

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

.filter-input-keyword {
    width: 16.25rem;
}

.filter-form-actions {
    display: flex;
    gap: 0.5rem;
    align-items: flex-end;
    flex: 0 0 auto;
    padding-bottom: 0.0625rem;
}

.filter-form .form-control,
.filter-checkbox-group {
    height: 2.25rem;
    box-sizing: border-box;
    display: flex;
    flex-wrap: nowrap;
    gap: 0.25rem 0.875rem;
    align-items: center;
    padding: 0 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-input-bg);
}

.filter-checkbox-label {
    display: flex;
    align-items: center;
    gap: 0.3125rem;
    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-soft);
    color: var(--color-danger);
    padding: 0.625rem 0.875rem;
    border-radius: var(--radius-md);
    margin: 1rem;
}

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

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

.pagination-nav {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    /* 쪽 번호가 많으면 좁은 화면에서 줄을 바꾼다. 한 줄로 못을 박으면
       마지막 쪽 단추가 화면 밖으로 나가 누를 수 없다. */
    flex-wrap: wrap;
}

.pagination-pages {
    display: flex;
    align-items: center;
    gap: 0.125rem;
    margin: 0 0.25rem;
    flex-wrap: wrap;
}

.pagination-page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2rem;
    height: 2rem;
    padding: 0 0.375rem;
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
    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: var(--color-on-primary);
    font-weight: 600;
    pointer-events: none;
}

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

.pagination-jump-input {
    width: 4rem;
    height: 2rem;
    padding: 0 0.5rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
    text-align: center;
    box-sizing: border-box;
}

.pagination-jump-input:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 2px var(--color-primary-ring);
}

/* 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: 3rem 1.5rem;
    color: var(--color-text-muted);
}

.empty-state-icon {
    font-size: 3rem;
    margin-bottom: 0.75rem;
}

/* ── Login Page ── */

.login-wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: var(--color-bg-muted);
}

.login-box {
    width: 25rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 2.5rem 2rem;
}

.login-brand {
    text-align: center;
    margin-bottom: 2rem;
}

.login-brand-icon {
    font-size: 2.5rem;
    color: var(--color-primary);
    margin-bottom: 0.5rem;
}

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

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

.login-box .form-control {
    padding: 0.625rem 0.75rem;
}

.login-box .btn {
    width: 100%;
    padding: 0.625rem 1rem;
}

/* ── Error Page ── */

/* 레이아웃 없이 오류만 보여줄 때 감싸는 영역 */
.error-plain-wrapper {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: var(--color-bg-muted);
}

.error-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 5rem 1.5rem;
    text-align: center;
}

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

.error-title {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--color-text);
    margin-bottom: 0.5rem;
}

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

/* ── Page Header ── */

/* 제목 줄·탭 줄·카드 사이가 1 / 1.25 / 1.5rem 으로 제각각이었다.
   같은 뜻의 자리는 같은 값이어야 눈이 규칙을 읽는다. */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}

.page-header-left {
    display: flex;
    align-items: baseline;
    gap: 0.625rem;
}

.page-header-right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* ── Button-style radio group ──
   켜고 끄는 값처럼 선택지가 두엇뿐일 때 라디오 점 대신 붙은 버튼으로 보여 준다.
   input 은 label 안에 숨기고 바로 뒤의 span 을 버튼처럼 그린다.
   (input:checked + span 이므로 둘은 반드시 형제로 붙어 있어야 한다) */

.btn-radio-group {
    display: inline-flex;
    margin: 0.25rem 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: 5.25rem;
    padding: 0.4375rem 1rem;
    font-size: 0.8125rem;
    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: -0.0625rem;
}

.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-on-primary);
    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: var(--text-xl);
    font-weight: 700;
    line-height: var(--leading-snug);
    /* 큰 글자는 자간을 살짝 좁혀야 뭉쳐 보이지 않는다. */
    letter-spacing: -0.02em;
    color: var(--color-text);
}

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

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

/* ── Text helpers ── */

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

.icon-sm {
    font-size: 0.9375rem;
}

.icon-md {
    font-size: 1rem;
}

/* 🔴 `.btn-link-like` 는 상자가 없어서, 옆에 선 진짜 단추와 **밑줄이
   안 맞았다.** 거르개 줄(모든 목록 화면에 있는 partials/list-filter.php)
   에서 "Search" 옆의 "Clear" 가 그랬다. 테두리를 투명하게 두면 같은
   크기의 상자를 가지면서 생김새는 링크로 남는다. */
.btn-link-like {
    color: var(--color-primary-text);
    text-decoration: none;
    background: transparent;
    border-color: transparent;
}

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

/* ── Action Buttons group ── */

.action-btns {
    display: flex;
    gap: 0.25rem;
}

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

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

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

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

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

/* 표 위에 놓는 한 줄 안내 */
.table-hint {
    margin: 0;
    padding: 0.75rem 1.25rem 0;
    font-size: 0.8125rem;
    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: 0.5rem 0;
    font-weight: 600;
    color: var(--color-text);
}

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

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

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

/* 좁은 화면 — 사이드바가 본문의 자리를 가져가지 않는다.
   접힌 띠(4rem)만 자리를 차지하고, 펼치면 본문 위를 덮는다.
   접고 펴는 판정은 admin.js 가 한다(화면 폭이 바뀌면 다시 본다). */
@media (max-width: 45rem) {
    .admin-main,
    .admin-sidebar.is-collapsed ~ .admin-main {
        margin-left: var(--sidebar-collapsed-width);
    }

    /* 펼쳐진 동안에는 뒤를 어둡게 해서 무엇이 위인지 보이게 한다. */
    .admin-sidebar:not(.is-collapsed) {
        box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.45);
    }

    /* 좁은 화면에서는 여백부터 줄인다. 여백 때문에 단추가 화면 밖으로
       밀려나면 누를 수 없다. */
    .admin-topbar,
    .admin-content {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }
}

.form-hint {
    margin: 0.375rem 0 0;
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
}

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

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

/* 한 폼 안을 주제별로 나누는 작은 제목.
   카드로 나눌 만큼 크지 않은 묶음(모달 안 등)에 쓴다. */
.form-section-title {
    margin: 1.5rem 0 0.75rem;
    padding-bottom: 0.5rem;
    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: 0.5rem;
    align-items: center;
    margin: 0.375rem 0;
    cursor: pointer;
}

.form-check input {
    width: 1rem;
    height: 1rem;
    margin: 0;
    cursor: pointer;
}

.form-check-group {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

.form-check-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
    gap: 0.25rem 1rem;
    margin-top: 0.375rem;
}

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

/* 저장·취소 같은 버튼 줄.
   왼쪽 정렬해 위 입력칸과 시작점을 맞추고, 위 카드와 간격을 둔다. */
.form-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: center;
    gap: 0.5rem;
    margin: 1.25rem 0 1.5rem;
}

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

   격자·플렉스 칸은 최소 폭이 내용 크기라, 긴 값 하나가 카드를 밀어내
   화면 아래에 가로 스크롤바를 만든다. 최소 폭을 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(81.25rem) − 좌우 여백 20×2 − 본문 여백 16×2 = 76.75rem */
    --admin-editor-width: 76.75rem;

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

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

/* 팝업 크기는 담을 내용에 맞춰 등급 하나를 골라 붙인다.
   페이지 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, 30rem);
    max-width: calc(100vw - 3rem);
    max-height: calc(100vh - 5rem);
    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: 27.5rem; }
.modal-dialog-md   { --modal-width: 42.5rem; }
.modal-dialog-lg   { --modal-width: 50rem; }
.modal-dialog-xl   { --modal-width: 56.25rem; }
.modal-dialog-xxl  { --modal-width: 68.75rem; }
.modal-dialog-full { --modal-width: 87.5rem; }

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

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

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

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

.modal-close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    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: 1.25rem;
    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: 0.5rem;
    padding: 0.75rem 1.25rem;
    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: -1.25rem;
    z-index: 1;
    margin: 1.25rem -1.25rem -1.25rem;
    padding: 0.75rem 1.25rem;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
}

.modal-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 7.5rem;
    color: var(--color-text-muted);
}

/* ── 툴팁 ──

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

.admin-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 400;
    max-width: 20rem;
    padding: 0.375rem 0.625rem;
    border-radius: var(--radius-sm);
    background: var(--color-tooltip-bg);
    color: var(--color-tooltip-text);
    font-size: 0.75rem;
    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: -0.3125rem;
    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: 1.5rem;
    right: 1.5rem;
    z-index: 300;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    pointer-events: none;
}

.toast {
    pointer-events: auto;
    min-width: 17.5rem;
    max-width: 26.25rem;
    padding: 0.75rem 1rem;
    border-radius: var(--radius-md);
    font-weight: 500;
    box-shadow: var(--shadow-md);
    animation: toastSlideIn 0.25s ease;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

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

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

/* 막혔다는 안내. 성공과 같은 초록으로 두면 "됐다" 로 읽힌다. */
.toast.is-info {
    background: var(--color-info-soft);
    color: var(--color-info);
    border: 1px solid #bae6fd;
}

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

/* ── 빠른 이동(Ctrl+K) ──
   한 칸에서 기능·고객·제조사·송장 번호를 찾는다. 키보드로 훑는 자리와
   마우스가 얹힌 자리를 같게 칠한다 — 둘이 다르면 키보드만 쓰는 사람이
   지금 어디 있는지 알 수 없다(→ picker.css 의 .ts-dropdown .active). */

.jump-input {
    font-size: 1.0625rem;
}

.jump-list {
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
    max-height: 22rem;
    overflow-y: auto;
}

.jump-item {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.jump-item:hover,
.jump-item.is-current {
    color: var(--color-text);
    background: var(--color-primary-bg);
}

.jump-item-label {
    font-weight: 500;
}

.jump-item-note {
    font-size: 0.85rem;
    color: var(--color-text-secondary);
}

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

.toast-icon {
    font-size: 1.125rem;
}

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

.section-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: 0.875rem;
    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-on-primary);
    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-on-primary);
    opacity: 0.75;
}

.section-tab-off {
    font-size: 0.6875rem;
    padding: 0.0625rem 0.375rem;
    border-radius: 62.4375rem;
    background: var(--color-bg-muted);
    color: var(--color-text-secondary);
}

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

/* ============================================================
   Linekto — 이 제품이 더한 것
   공용 골조 위에 화면 규칙(docs/ui-rules.md)을 얹는다.
   계층 순서상 맨 뒤에 두어, 앞의 것을 이길 수 있게 한다.
   ============================================================ */

/* ── 표의 열 정렬 (화면 규칙 3) ──
   공용 골조는 왼쪽이 기본이지만 이 제품은 **가운데가 기본**이다.
   숫자를 세로로 훑어 읽는 일이 많아, 위아래가 맞아떨어져야 읽힌다.
   길이가 들쭉날쭉한 값(이름·설명·메모)만 왼쪽으로 내린다. */
.admin-table thead th,
.admin-table tbody td {
    text-align: center;
}

.admin-table thead th.col-title,
.admin-table tbody td.col-title,
.admin-table thead th.col-person,
.admin-table tbody td.col-person,
.admin-table thead th.col-org,
.admin-table tbody td.col-org,
.admin-table thead th.col-email,
.admin-table tbody td.col-email,
.admin-table thead th.col-author,
.admin-table tbody td.col-author,
.admin-table thead th.col-agent,
.admin-table tbody td.col-agent,
.admin-table thead th.col-name,
.admin-table tbody td.col-name,
.admin-table thead th.col-note,
.admin-table tbody td.col-note {
    text-align: left;
}

/* 금액은 오른쪽이다. 공용 규칙에 없는 이 제품의 추가로,
   1의 자리가 어긋나면 세로로 더해 읽을 수 없기 때문이다. */
.admin-table thead th.col-money,
.admin-table tbody td.col-money {
    text-align: right;
    white-space: nowrap;
}

/* 값이 아직 없는 칸. 빈칸으로 두면 자료가 없는 것인지 화면이 깨진 것인지 모른다. */
.cell-empty {
    color: var(--color-text-muted);
}

/* ── 숫자는 자릿수 폭을 고정한다 (화면 규칙 7) ──
   설정이 아니라 기본값이다. 1과 8의 폭이 다르면 금액이 세로로 어긋난다. */
.admin-table,
.form-control,
.col-money,
.page-title-count,
.pagination-count,
.stat-card-value {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}

/* ── 팝업 높이 (화면 규칙 4·10) ──
   휴대폰 주소창이 들락거리면 100vh 가 화면 밖으로 밀린다. dvh 를 함께 준다.
   머리말·버튼줄은 공용 골조가 이미 flex-shrink:0 으로 붙박이라 손대지 않는다. */
.modal-dialog {
    max-height: calc(100dvh - 5rem);
}

/* ── 저장 버튼: 고친 것이 없으면 눌리지 않는다 (화면 규칙 11) ──
   `disabled` 속성을 쓰지 않는 이유가 있다. 그렇게 하면 키보드 초점이 닿지 않아
   화면 낭독기가 버튼도, 왜 눌리지 않는지도 읽지 못한다.
   그래서 **보이고 닿이되 눌러도 저장되지 않는** 상태로 둔다.
   판정과 안내 문구는 app-ui.js 한 곳에서 한다. */
.btn[aria-disabled="true"] {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn[aria-disabled="true"]:hover {
    filter: none;
}

/* ── 전용 화면의 버튼 줄도 바닥에 붙는다 (화면 규칙 10) ──
   공용 골조는 **팝업 안에서만** 버튼 줄을 붙박이로 만든다. 이 제품은 정산·
   임포트처럼 몇십 분씩 붙잡는 작업 화면이 있어, 전용 페이지에서도 저장·다음이
   늘 보여야 한다. 단계가 긴 작업일수록 "다음으로" 를 찾게 만들면 안 된다.

   팝업 안은 골조가 이미 처리하므로 여기서는 본문 쪽만 잡는다. */
.admin-content .form-actions {
    position: sticky;
    bottom: 0;
    z-index: 1;
    padding: 0.75rem 1.25rem;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
    border-bottom-left-radius: var(--radius-lg);
    border-bottom-right-radius: var(--radius-lg);
}

/* 카드가 아니라 화면에 바로 놓인 버튼 줄은 모서리를 둥글리지 않는다. */
.admin-content > .form-actions {
    /* 사이는 `.admin-content` 의 gap 이 정한다. 자기 여백을 함께 쓰면 더해진다.
       카드 **안**에 놓인 버튼 줄은 그대로 자기 여백을 쓴다. */
    margin-top: 0;
    margin-bottom: 0;
    border-radius: 0;
    margin-left: -1.5rem;
    margin-right: -1.5rem;
    padding-left: 1.5rem;
    padding-right: 1.5rem;
}

/* ── 설명 문단 뒤에 바로 오는 입력 격자는 한 칸 띄운다 ──
   격자는 행 간격이 0 이라(gap: 0 1.25rem) 바로 위 문단에 라벨이 달라붙는다. */
.form-hint + .form-grid,
.form-section-title + .form-grid {
    margin-top: 0.75rem;
}

/* ── 작업 화면의 합계 (설계 원칙 6) ──
   숫자를 다루는 화면은 "맞는지" 를 화면에서 보여 준다. 합계를 버튼 줄에 함께
   두면 저장을 누르기 직전에 눈이 지나가는 자리에 놓인다. */
.invoice-totals {
    margin-right: auto;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

.invoice-totals strong {
    color: var(--color-text);
    font-size: 1.05rem;
}

/* 표 바닥의 합계 줄. 본문과 섞이지 않게 바탕을 깐다. */
.admin-table tfoot th {
    padding: 0.75rem;
    background: var(--color-bg-subtle);
    border-top: 2px solid var(--color-border);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* 송장 라인 표는 칸마다 입력이 들어가 기본 여백이면 빽빽하다. */
#invoiceLines tbody td {
    padding: 0.375rem 0.5rem;
    vertical-align: top;
}

#invoiceLines .form-control {
    max-width: 100%;
}

/* 합이 100% 가 아닐 때. **색만으로 말하지 않는다** — 옆에 얼마가 남았는지
   글자로 함께 적는다(공용 접근성 최소선). */
.cell-warning {
    color: var(--color-danger);
    font-weight: 700;
}

/* 탭에 붙는 숫자 — 두 가지 뜻이다.
   전체 수는 맨 숫자로 흐리게, **아직 안 본 것**은 채워진 알약으로 강조한다.
   모양이 다르므로 색을 못 보는 사람에게도 갈라진다(→ admin-ui.md 접근성). */
.section-tab-count {
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--color-text-muted);
}

.section-tab.is-active .section-tab-count {
    color: var(--color-on-primary);
    opacity: 0.75;
}

.section-tab-badge {
    min-width: 1.125rem;
    padding: 0 0.375rem;
    border-radius: 62.4375rem;
    background: var(--color-danger);
    color: var(--color-on-primary);
    font-size: 0.6875rem;
    font-weight: 700;
    line-height: 1.125rem;
    text-align: center;
    white-space: nowrap;
}

/* 고른 탭은 바탕이 이미 진하다. 그 위에 빨강을 얹으면 둘 다 안 읽힌다. */
.section-tab.is-active .section-tab-badge {
    background: var(--color-on-primary);
    color: var(--color-primary);
}

/* ── 건너뛰기 링크 ──
   키보드로 오는 사람이 메뉴를 매번 지나지 않게. 평소에는 화면 밖에 있다가
   초점이 오면 들어온다.

   🔴 **`display: none` 이나 `visibility: hidden` 을 쓰지 않는다.** 그러면
      초점이 아예 안 가서 있으나 마나가 된다. `.sr-only` 와 같은 사정이다.

   🔴 **`admin.css` 에 둔다.** public.css 에 뒀다가 **가입 화면에서 그냥
      보이는 링크로 나왔다**(2026-09-20). 그 화면은 좁은 폭이라 public.css 를
      안 부르는데, 링크 자체는 레이아웃이 모든 화면에 그린다. 감추는 규칙이
      링크보다 좁은 자리에 있으면 언젠가 이렇게 샌다. */
.skip-link {
    position: absolute;
    left: -100vw;
    top: 0;
    z-index: 10;
    padding: 0.75rem 1.25rem;
    color: var(--color-on-primary);
    background: var(--color-primary);
    border-radius: 0 0 var(--radius-md) 0;
}

.skip-link:focus {
    left: 0;
}

/* ── 화면 낭독기에게만 ──
   눈으로는 안 보이지만 읽히기는 해야 하는 글. display:none 으로 감추면
   낭독기도 건너뛴다. */
.sr-only {
    position: absolute;
    /* 0.0625rem = 1px. 0 으로 두면 일부 낭독기가 건너뛴다. 여기서 px 를
       쓰지 않는 것은 화면 규칙 4번이 단위를 하나로 묶기 때문이다. */
    width: 0.0625rem;
    height: 0.0625rem;
    margin: -0.0625rem;
    padding: 0;
    overflow: hidden;
    white-space: nowrap;
    border: 0;
    clip-path: inset(50%);
}

/* ── 상단 띠의 공지 (docs/ops-plan.md O6) ──
   햄버거 단추 옆, 페이지 제목이 있던 자리다. 제목은 본문 <h1> 에 그대로
   있으므로 같은 글자를 두 번 보여 주는 대신 여기에 우리 말을 건다.
   flex: 1 이라 걸 것이 없으면 그냥 빈칸이고 오른쪽 단추는 제자리에 있다. */
.topbar-notice {
    display: flex;
    flex: 1;
    align-items: center;
    /* 이 한 줄이 없으면 긴 제목이 상단바를 늘려 오른쪽 단추를 밀어낸다. */
    min-width: 0;
    overflow: hidden;
}

/* 안 읽은 것을 돌려 가며 보여 준다. 한 번에 하나만 보이고 나머지는 DOM 에
   그대로 있다 — 다시 받아 오지 않으려는 것이다. */
.notice-ticker-item {
    display: none;
    align-items: center;
    gap: 0.5rem;
    /* 폭을 다 차지하지 않는다 — 그러면 닫기 단추가 화면 오른쪽 끝까지
       밀려나 무엇을 닫는지 알기 어렵고, 옆의 Jump 단추와 붙어 보인다. */
    max-width: 100%;
    min-width: 0;
    color: var(--color-text-secondary);
}

.notice-ticker-item.is-current {
    display: flex;
}

.notice-ticker-text {
    /* 짧으면 짧은 대로 둔다. 길면 아래 말줄임이 받는다. */
    flex: 0 1 auto;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
    /* 제목이 길면 말줄임한다. 줄바꿈하면 상단바 높이가 요청마다 달라진다. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.notice-ticker-text:hover {
    text-decoration: underline;
}

.notice-ticker-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
}

.notice-ticker-close:hover {
    background: var(--color-bg-subtle);
    color: var(--color-text);
}

/* ── 폼 위에 붙이는 안내 한 줄 ──
   "이 글은 영어로 씁니다", "회사 사람 누구나 읽습니다" 처럼 **쓰기 전에**
   알아야 하는 것. 나중에 알면 이미 늦은 것들이다. */
.form-notice {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: var(--space-4);
    padding: 0.625rem 0.875rem;
    border-radius: var(--radius-md);
    background: var(--color-primary-bg);
    color: var(--color-primary-text);
    font-size: 0.875rem;
}

/* ── 공지·도움말 목록 ── */
.notice-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.notice-list-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1.5rem;
    border-top: 1px solid var(--color-border);
}

.notice-list-item:first-child {
    border-top: 0;
}

.notice-list-item:hover {
    background: var(--color-bg-subtle);
}

/* 안 읽은 것은 굵게. 배지와 굵기를 함께 쓴다 — 색이나 배지 하나만으로
   뜻을 전하지 않는다(→ docs/common/admin-ui.md 접근성). */
.notice-list-item.is-unread .notice-list-title {
    font-weight: 600;
}

.notice-list-link {
    flex: 1;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.notice-list-link:hover {
    text-decoration: underline;
}

.notice-list-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 0 0 auto;
    color: var(--color-text-secondary);
    font-size: 0.8125rem;
    white-space: nowrap;
}

.notice-read-body {
    line-height: 1.7;
    word-break: break-word;
}

/* ── 주고받은 글 ──
   우리 글과 고객 글을 좌우가 아니라 **바탕색과 이름표**로 가른다. 좌우로
   나누면 좁은 화면에서 한쪽이 반 폭이 되어 긴 글이 세로로 길어진다. */
.thread {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: var(--space-6);
}

.thread-item {
    padding: 0.875rem 1rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.thread-item.is-ours {
    border-color: var(--color-primary-light);
    background: var(--color-primary-bg);
}

.thread-item-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.375rem;
}

.thread-item-body {
    line-height: 1.7;
    word-break: break-word;
}

.thread-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
    color: var(--color-text-secondary);
    font-size: 0.875rem;
}

/* 움직임을 줄여 달라고 한 사람에게는 **전환만** 끈다. 순환 자체는 정보
   전달이라 그대로 둔다 — 멈추면 뒤의 공지를 볼 길이 없어진다. */
@media (prefers-reduced-motion: reduce) {
    .notice-ticker-item,
    .notice-ticker-text {
        transition: none;
    }
}

/* ── 들여다보는 중 (docs/service-plan.md S3) ──
   우리가 고객사 화면을 읽기로 보고 있을 때의 띠. **화면 맨 위에 붙어
   따라다닌다** — 평소와 똑같이 보이면 남의 자료를 보면서도 자기 화면인 줄
   알게 된다. */
.support-banner {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 1.5rem;
    color: var(--color-on-primary);
    background: var(--color-danger);
}

.support-banner .btn {
    margin-left: auto;
    color: var(--color-on-primary);
    border-color: var(--color-on-primary);
    background: transparent;
}

.support-banner .btn:hover {
    color: var(--color-danger);
    background: var(--color-on-primary);
}

/* ── 데모 사이트 (docs/demo-site.md) ──
   들여다보는 중과 **같은 자리, 다른 색**이다. 위험을 알리는 띠(빨강)와
   구분되어야 한다 — 여기는 사고가 아니라 안내다. */
/* ── 돈이 밀렸다는 띠 ──
   닫는 단추가 없다. 공지 띠는 사람마다 닫을 수 있지만(tblNoticeRead), 돈 달라는
   말은 닫히면 안 된다 — 닫은 사람은 잠기는 날까지 아무 말도 못 듣게 된다.

   🔴 **붙박이(sticky)로 두지 않는다.** 데모 띠와 들여다보기 띠가 이미 top:0
   붙박이라, 셋째를 같은 자리에 두면 스크롤할 때 서로 겹친다. 그리고 겹쳤을 때
   가려지면 안 되는 쪽은 **"지금 남의 회사를 보고 있다"** 쪽이다.

   🔴 색은 **옅은 바탕 + 진한 글자**다. 진한 바탕에 흰 글자로 하면 어두운
   테마에서 --color-warning 이 밝은 노랑으로 뒤집혀 흰 글자가 안 읽힌다.
   -soft 짝은 두 값이 함께 뒤집혀 대비가 유지된다. */
.billing-banner {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 1.5rem;
    color: var(--color-warning);
    background: var(--color-warning-soft);
    border-bottom: 1px solid var(--color-warning);
}

.billing-banner.is-locked {
    color: var(--color-danger);
    background: var(--color-danger-soft);
    border-bottom-color: var(--color-danger);
}

.billing-banner .btn {
    margin-left: auto;
    flex: none;
    color: inherit;
    border-color: currentColor;
    background: transparent;
}

.billing-banner .btn:hover {
    color: var(--color-warning-soft);
    background: var(--color-warning);
}

.billing-banner.is-locked .btn:hover {
    color: var(--color-danger-soft);
    background: var(--color-danger);
}

/* 낼 수 없는 사람에게는 단추 대신 무엇을 하면 되는지를 준다. 오른쪽 끝으로
   밀어 단추가 있던 자리에 둔다 — 줄의 모양이 사람마다 달라지지 않는다. */
.billing-banner .form-hint {
    margin-left: auto;
    flex: none;
    color: inherit;
}

.demo-banner {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 1.5rem;
    color: var(--color-on-primary);
    background: var(--color-info);
}

.demo-banner .btn {
    margin-left: auto;
    flex: none;
    color: var(--color-on-primary);
    border-color: var(--color-on-primary);
    background: transparent;
}

.demo-banner .btn:hover {
    color: var(--color-info);
    background: var(--color-on-primary);
}

/* 🔴 **데모 사이트라는 표식** — 로그인 화면 맨 위.
   들어간 뒤에는 화면마다 `.demo-banner` 가 말해 주지만, 그 전에는 주소밖에
   단서가 없다. 색과 아이콘을 그 띠와 **같게** 두어, 들어가면 보게 될 것과
   같은 것으로 읽히게 한다(→ shared/templates/login.php). */
.login-demo-flag {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
    padding: 0.625rem 0.875rem;
    font-size: 0.8125rem;
    color: var(--color-on-primary);
    background: var(--color-info);
    border-radius: var(--radius-md);
}

/* 로그인 화면의 데모 단추 — 등급마다 보이는 것이 다르다는 것을 여기서 먼저 보여 준다. */
.login-demo {
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--color-border);
}

/* 데모 사이트에서는 이것이 **유일한 길**이다 — 비밀번호 칸이 없다
   (→ libs/LoginScreen.php 의 password 옵션). 아래에 아무것도 없으므로
   밑줄을 긋지 않는다. 남겨 두면 무언가 잘려 나간 것처럼 보인다. */
.login-demo-only {
    margin: 0;
    padding: 0;
    border-top: 0;
    border-bottom: 0;
}

/* 비밀번호 칸이 함께 있을 때. 폼 위에 오고 둘 사이에 줄을 긋는다.
   아래에 두면 스크롤 밖으로 밀려 아무도 못 찾는다. */
.login-demo-first {
    margin-top: 0;
    margin-bottom: 1.5rem;
    padding-top: 0;
    padding-bottom: 1.25rem;
    border-top: 0;
    border-bottom: 1px solid var(--color-border);
}

.login-demo-title {
    margin-bottom: 0.75rem;
    font-size: 0.8125rem;
    color: var(--color-text-muted);
}

.login-demo-btn {
    display: block;
    width: 100%;
    margin-bottom: 0.5rem;
    padding: 0.625rem 0.875rem;
    text-align: left;
    cursor: pointer;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.login-demo-btn:hover {
    border-color: var(--color-primary);
    background: var(--color-bg-subtle);
}

.login-demo-name {
    display: block;
    font-weight: 600;
    color: var(--color-text);
}

.login-demo-blurb {
    display: block;
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

.login-demo-note {
    margin: 0.75rem 0 0;
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

/* 🔴 계정이 있는 고객이 데모 주소로 들어올 수 있다. 비밀번호 칸을 없앤 이상
   **갈 데를 알려 주지 않으면 그 사람은 막힌 채로 돌아간다.** 눈에 먼저 띄면
   안 되므로(데모의 주된 길은 위의 단추다) 맨 아래에 작게 둔다. */
.login-demo-elsewhere {
    margin: 1.5rem 0 0;
    padding-top: 1.25rem;
    text-align: center;
    font-size: 0.75rem;
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
}

/* 운영 화면(ops)의 브랜드 줄은 색이 다르다. 하루에 두 화면을 오가므로
   지금 어디인지 한눈에 보여야 한다. */
.is-ops .sidebar-brand {
    color: var(--color-on-primary);
    background: var(--color-danger);
}

/* ── 공개 사이트 (www) ──
   업무 화면과 **같은 CSS 한 벌**을 쓴다. 가입하고 들어간 사람이 보는 화면이
   방금 본 화면과 딴판이면 제품이 두 개인 것처럼 느낀다.
   여기서 더하는 것은 로그인 뒤 화면에 없는 것들뿐이다 — 머리줄·소개·바닥. */
.public-body {
    margin: 0;
    background: var(--color-bg-muted);
    color: var(--color-text);
}

.public-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.5rem;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}

.public-brand {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--color-text);
    text-decoration: none;
}

.public-main {
    max-width: 44rem;
    margin: 0 auto;
    padding: 2rem 1.5rem 3rem;
    /* 간격은 담는 쪽이 정한다 — 업무 화면(`.admin-content`)과 같은 규칙. */
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.public-hero h1 {
    margin: 0 0 0.5rem;
    font-size: 1.75rem;
    font-weight: 700;
}

.public-lead {
    margin: 0;
    color: var(--color-text-secondary);
}

.public-footer {
    padding: 1.5rem;
    text-align: center;
}

/* ── 첫 30일 할 일 (docs/ui-structure.md 6번) ──
   처음 온 회사에게만 보인다. 다섯 줄을 다 하면 화면에서 사라진다. */
.progress-bar {
    height: 0.5rem;
    margin: 0 0 1rem;
    overflow: hidden;
    background: var(--color-bg-subtle);
    border-radius: var(--radius-md);
}

.progress-bar-fill {
    display: block;
    height: 100%;
    background: var(--color-success);
    transition: width 0.3s;
}

.checklist {
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: checklist;
}

.checklist-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--color-border);
}

.checklist-item:first-child {
    border-top: 0;
}

.checklist-mark {
    flex: 0 0 auto;
    color: var(--color-text-muted);
}

/* 끝난 줄은 **표시가 두 개**다 — 아이콘이 채워지고, 옆에 Done 이라고 적힌다.
   색만으로 말하면 색을 구분하지 못하는 사람에게는 아무 말도 안 한 것과 같다. */
.checklist-item.is-done .checklist-mark {
    color: var(--color-success);
}

.checklist-item.is-done .checklist-body strong {
    color: var(--color-text-secondary);
    text-decoration: line-through;
}

.checklist-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0.125rem;
}

/* ── 작업 화면의 단계 줄 (docs/ui-structure.md 화면의 세 가지 성격) ──
   정산·임포트·이전 마법사는 단계가 있는 화면이다. **지금 어디인지**가 늘
   보여야 중간에 나갔다 와도 이어서 할 수 있다. */
.wizard-steps {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.wizard-step {
    flex: 1 1 14rem;
}

.wizard-step > a {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.75rem 1rem;
    color: var(--color-text-secondary);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    text-decoration: none;
}

.wizard-step > a:hover {
    background: var(--color-bg-subtle);
}

.wizard-step-no {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    font-weight: 700;
    color: var(--color-text-secondary);
    background: var(--color-bg-subtle);
    border-radius: 50%;
}

.wizard-step-label {
    flex: 1 1 auto;
    font-weight: 600;
    color: var(--color-text);
}

.wizard-step-count {
    flex: 0 0 auto;
    font-size: 0.8125rem;
    color: var(--color-text-muted);
}

/* 끝난 단계와 지금 단계는 **모양이 서로 달라야 한다** — 색만으로 가르면
   지금 어디인지 못 읽는 사람이 생긴다. 숫자가 체크로 바뀌는 것이 그 표시다. */
.wizard-step.is-done .wizard-step-no {
    color: var(--color-on-primary);
    background: var(--color-success);
}

.wizard-step.is-current > a {
    border-color: var(--color-primary);
    box-shadow: inset 0 0 0 1px var(--color-primary);
}

/* ── 금액 대사 한 줄 (docs/build-plan.md 9단계) ──
   "원본 합계 $X = 우리 합계 $X". 이 문장을 보기 전에는 아무도 옛 제품을
   끄지 못하므로, 표 위에 큰 글씨로 한 번 더 적는다. */
.reconcile-headline {
    margin: 0 0 0.5rem;
    font-size: 1.125rem;
    font-variant-numeric: tabular-nums;
}

/* ── 칸 잠그기 (docs/ui-structure.md) ──
   한 제조사의 송장을 연달아 칠 때, 잠근 칸은 다음 건에도 남는다.
   자물쇠는 **이름표 옆**에 둔다. 칸 안에 넣으면 값을 가리고, 칸 밖 먼 자리에
   두면 어느 칸의 것인지 모른다. */
.field-lock {
    margin-left: 0.375rem;
    padding: 0;
    color: var(--color-text-muted);
    background: none;
    border: 0;
    line-height: 1;
    vertical-align: middle;
    cursor: pointer;
}

.field-lock:hover {
    color: var(--color-text);
}

.field-lock:focus-visible {
    outline: 2px solid var(--color-primary-light);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* 잠긴 칸은 **색만으로 말하지 않는다** — 자물쇠 모양이 함께 바뀐다
   (lock_open → lock). 색은 거드는 것이다. */
.form-group.is-locked .field-lock {
    color: var(--color-primary-text);
}

.form-group.is-locked .form-control {
    border-color: var(--color-primary-light);
}

/* ── 목록에서 고른 줄 (docs/ui-structure.md 목록 타이핑 점프) ──
   글자를 치면 그 줄로 뛴다. F2 가 여는 것도 이 줄이라, **지금 어느 줄에
   있는지**가 보여야 한다. */
.admin-table tbody tr.is-selected > td {
    background: var(--color-bg-subtle);
    box-shadow: inset 0.1875rem 0 0 var(--color-primary);
}

/* ── 정산 바닥줄 (설계 원칙 6) ──
   **넣을 것 · 넣은 것 · 차이**가 늘 보이는 자리. 정산은 몇십 분짜리 일이라
   숫자가 화면 밖으로 나가면 사람이 스스로 세게 된다. */
.settlement-tally {
    display: flex;
    gap: 1.5rem;
    margin-right: auto;
    align-items: center;
    font-variant-numeric: tabular-nums;
}

.settlement-figure {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
}

.settlement-label {
    font-size: 0.75rem;
    color: var(--color-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.settlement-figure strong {
    font-size: 1.15rem;
}

/* 차이가 0 이면 초록, 남아 있으면 빨강. **색만으로 말하지 않는다** —
   옆에 얼마인지 숫자가 그대로 적혀 있다. */
.settlement-figure.is-settled strong { color: var(--color-success); }
.settlement-figure.is-off strong { color: var(--color-danger); }

/* 고른 줄은 바탕을 깔아 준다. 스무 줄 중 무엇을 골랐는지 한눈에 보이게. */
#paymentLines tr.is-picked td {
    background: var(--color-primary-bg);
}

#paymentLines tbody td {
    padding: 0.5rem;
    vertical-align: top;
}

/* ── themes: 어둡게 (화면 규칙 6) ──
   바꾸는 것은 토큰뿐이다. 화면마다 색을 따로 정의하지 않는다.
   '자동' 은 운영체제를 따르되, 사용자가 밝게로 고정했으면 따르지 않는다. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        /* 바탕용. 흰 글자와 4.5:1 을 넘겨야 해서 밝은 테마와 같은 값이다 —
           #3b82f6 은 예뻐 보이지만 흰 글자와 3.68 밖에 안 나온다. */
        --color-primary: #2563eb;
        --color-primary-light: #60a5fa;
        --color-primary-dark: #1d4ed8;
        /* 글자용. 어두운 바탕 위에서는 반대로 **밝아야** 읽힌다. */
        --color-primary-text: #60a5fa;
        --color-primary-ring: rgba(96, 165, 250, 0.24);
        --color-primary-bg: #1e293b;

        --color-sidebar-bg: #020617;
        --color-sidebar-text: #cbd5e1;
        --color-sidebar-muted: #8296ae;
        --color-sidebar-text-active: #ffffff;
        --color-sidebar-hover: #1e293b;
        --color-sidebar-active: #1d4ed8;

        --color-topbar-bg: #1e293b;
        --color-topbar-border: #334155;

        --color-surface: #1e293b;
        --color-border: #334155;
        --color-text: #e2e8f0;
        --color-text-secondary: #94a3b8;
        --color-text-muted: #64748b;

        --color-success: #4ade80;
        --color-warning: #fbbf24;
        --color-danger: #f87171;
        --color-info: #38bdf8;

        --color-bg-subtle: #172033;
        --color-bg-muted: #0f172a;

        --color-input-bg: #0f172a;
        --color-border-strong: #475569;
        --color-success-soft: #14321f;
        --color-warning-soft: #33280d;
        --color-danger-soft: #3a1717;
        --color-info-soft: #0c2b3d;
        --color-tooltip-bg: #e2e8f0;
        --color-tooltip-text: #0f172a;

        color-scheme: dark;
    }
}

:root[data-theme="dark"] {
    /* 바탕용. 흰 글자와 4.5:1 을 넘겨야 해서 밝은 테마와 같은 값이다 —
       #3b82f6 은 예뻐 보이지만 흰 글자와 3.68 밖에 안 나온다. */
    --color-primary: #2563eb;
    --color-primary-light: #60a5fa;
    --color-primary-dark: #1d4ed8;
    /* 글자용. 어두운 바탕 위에서는 반대로 **밝아야** 읽힌다. */
    --color-primary-text: #60a5fa;
    --color-primary-ring: rgba(96, 165, 250, 0.24);
    --color-primary-bg: #1e293b;

    --color-sidebar-bg: #020617;
    --color-sidebar-text: #cbd5e1;
    --color-sidebar-muted: #8296ae;
    --color-sidebar-text-active: #ffffff;
    --color-sidebar-hover: #1e293b;
    --color-sidebar-active: #1d4ed8;

    --color-topbar-bg: #1e293b;
    --color-topbar-border: #334155;

    --color-surface: #1e293b;
    --color-border: #334155;
    --color-text: #e2e8f0;
    --color-text-secondary: #94a3b8;
    --color-text-muted: #64748b;

    --color-success: #4ade80;
    --color-warning: #fbbf24;
    --color-danger: #f87171;
    --color-info: #38bdf8;

    --color-bg-subtle: #172033;
    --color-bg-muted: #0f172a;

    --color-input-bg: #0f172a;
    --color-border-strong: #475569;
    --color-success-soft: #14321f;
    --color-warning-soft: #33280d;
    --color-danger-soft: #3a1717;
    --color-info-soft: #0c2b3d;
    --color-tooltip-bg: #e2e8f0;
    --color-tooltip-text: #0f172a;

    color-scheme: dark;
}

/* 색을 직접 박아 둔 몇 곳은 어두운 배경에서 대비가 무너진다. 여기서 되돌린다. */
:root[data-theme="dark"] .section-tab-off,
:root:not([data-theme="light"]) .section-tab-off {
    background: var(--color-bg-muted);
    color: var(--color-text-secondary);
}

/* 인쇄와 PDF 는 늘 밝게 낸다 (화면 규칙 6). */
@media print {
    :root {
        --color-surface: #ffffff;
        --color-bg-subtle: #ffffff;
        --color-bg-muted: #ffffff;
        --color-text: #000000;
        --color-text-secondary: #333333;
        --color-border: #cccccc;
        --color-input-bg: #ffffff;
        --color-success-soft: #ffffff;
        --color-warning-soft: #ffffff;
        --color-danger-soft: #ffffff;
        --color-info-soft: #ffffff;
        color-scheme: light;
    }

    .admin-sidebar,
    .admin-topbar,
    .btn,
    .pagination-nav {
        display: none !important;
    }

    .admin-main {
        margin-left: 0;
    }
}

/* 움직임을 줄여 달라는 설정을 존중한다 (공용 접근성 최소선). */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* ────────────────────────────────────────────────────────────
   보고서의 그림

   서버가 SVG 를 글자로 그려서 넘깁니다(libs/ReportChart.php). 그림
   라이브러리가 없으므로 **색은 여기서만** 정하고, 테마를 바꾸면 그림이
   따라 바뀝니다. 인쇄에도 같은 모양으로 나갑니다.

   올해와 작년은 **같은 파랑의 두 단계**입니다. 서로 다른 무엇이 아니라
   같은 것의 다른 때라, 다른 색을 주면 둘이 별개처럼 읽힙니다. 작년 막대에는
   테두리를 둬서 색을 못 가리는 사람도 형태로 가릅니다.
   ──────────────────────────────────────────────────────────── */
:root {
    --chart-now: #2563eb;
    --chart-before: #bfdbfe;
    --chart-before-edge: #60a5fa;
    --chart-up: #15803d;
    --chart-down: #b91c1c;
    --chart-grid: #e2e8f0;

    /* 선 둘째·셋째. 첫째는 --chart-now 다. 색만으로 가르지 않으려고
       코드가 점선도 함께 씁니다(libs/ReportChart.php). */
    --chart-line-2: #b45309;
    --chart-line-3: #6d28d9;

    /* 쌓는 층 다섯. **명도를 서로 다르게** 잡아 흑백 복사에서도 갈라집니다. */
    --chart-series-1: #1d4ed8;
    --chart-series-2: #0891b2;
    --chart-series-3: #b45309;
    --chart-series-4: #6d28d9;
    --chart-series-5: #64748b;

    /* 히트맵 다섯 단계. 옅음 → 진함. 🔴 가장 진한 칸도 글자(--color-text)가
       4.5:1 을 넘게 둡니다 — 칸 색에 따라 글자색을 바꾸면 검증할 조합이
       열 개가 되고, 그중 하나가 반드시 어긋납니다. */
    --chart-heat-0: transparent;
    --chart-heat-1: #eff6ff;
    --chart-heat-2: #dbeafe;
    --chart-heat-3: #bfdbfe;
    --chart-heat-4: #93c5fd;

    /* 목표 눈금과 그 뒤 홈. */
    --chart-target: #0f172a;
    --chart-target-track: #e2e8f0;

    /* 확실도가 다른 층에 얹는 빗금. 색이 아니라 **무늬**로 가릅니다. */
    --chart-hatch: #ffffff;
}

.chart {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

.chart-grid {
    stroke: var(--chart-grid);
    stroke-width: 1;
}

.chart-grid.is-zero {
    stroke: var(--color-border-strong);
}

/* 글자는 **글자 색**을 씁니다. 막대 색을 글자에 쓰면 색을 못 가리는 사람에게
   그 줄은 아무 말도 안 한 것이 됩니다. */
.chart-axis,
.chart-label,
.chart-value {
    fill: var(--color-text-secondary);
    font-size: 0.75rem;
    font-family: inherit;
}

.chart-label,
.chart-value {
    fill: var(--color-text);
    font-size: 0.8125rem;
}

.chart-value {
    font-variant-numeric: tabular-nums;
}

.chart-value.is-empty {
    fill: var(--color-text-muted);
}

.chart-bar-now {
    fill: var(--chart-now);
}

.chart-bar-before {
    fill: var(--chart-before);
    stroke: var(--chart-before-edge);
    stroke-width: 1;
}

.chart-bar-up {
    fill: var(--chart-up);
}

.chart-bar-down {
    fill: var(--chart-down);
}

/* 고른 기간 밖의 달. 빼지 않고 흐리게 둔다 — 아예 빼면 "그 달에 아무것도
   없었다" 로 읽히는데, 사실은 안 보기로 한 것이다. */
.is-outside {
    opacity: 0.32;
}

.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
    align-items: center;
    margin-bottom: var(--space-3);
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

.chart-legend-item {
    display: inline-flex;
    gap: var(--space-2);
    align-items: center;
}

.chart-legend-swatch {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0.1875rem;
    background: var(--chart-now);
}

.chart-legend-swatch.is-before {
    background: var(--chart-before);
    border: 1px solid var(--chart-before-edge);
}

.chart-legend-swatch.is-up {
    background: var(--chart-up);
}

.chart-legend-swatch.is-down {
    background: var(--chart-down);
}

.chart-legend-swatch.is-series-1 { background: var(--chart-series-1); }
.chart-legend-swatch.is-series-2 { background: var(--chart-series-2); }
.chart-legend-swatch.is-series-3 { background: var(--chart-series-3); }
.chart-legend-swatch.is-series-4 { background: var(--chart-series-4); }
.chart-legend-swatch.is-series-5 { background: var(--chart-series-5); }

.chart-legend-swatch.is-line-1 { background: var(--chart-now); }
.chart-legend-swatch.is-line-2 { background: var(--chart-line-2); }
.chart-legend-swatch.is-line-3 { background: var(--chart-line-3); }

/* 빗금이 얹힌 층. 색 견본만으로는 "무늬가 있는 쪽" 을 못 가리킵니다. */
.chart-legend-swatch.is-hatch {
    background: var(--chart-series-3);
    background-image: repeating-linear-gradient(
        45deg,
        var(--chart-hatch) 0,
        var(--chart-hatch) 0.07em,
        transparent 0.07em,
        transparent 0.28em
    );
}

/* ── 선 ──────────────────────────────────────────────────────
   🔴 둘째·셋째 선은 **색과 함께 점선**입니다. 색만으로 가르면 색을 구분
   하지 못하는 사람에게 세 선이 한 덩어리가 됩니다. */
.chart-line-1,
.chart-line-2,
.chart-line-3 {
    fill: none;
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.chart-line-1 { stroke: var(--chart-now); }

.chart-line-2 {
    stroke: var(--chart-line-2);
    stroke-dasharray: 7 4;
}

.chart-line-3 {
    stroke: var(--chart-line-3);
    stroke-dasharray: 2 3;
}

/* 숫자가 붙는 점만 찍습니다. 점마다 찍으면 선이 안 보입니다. */
.chart-point {
    stroke: none;
}

.chart-point.chart-line-1 { fill: var(--chart-now); }
.chart-point.chart-line-2 { fill: var(--chart-line-2); }
.chart-point.chart-line-3 { fill: var(--chart-line-3); }

/* ── 쌓는 층 ────────────────────────────────────────────── */
.chart-fill-1 { fill: var(--chart-series-1); }
.chart-fill-2 { fill: var(--chart-series-2); }
.chart-fill-3 { fill: var(--chart-series-3); }
.chart-fill-4 { fill: var(--chart-series-4); }
.chart-fill-5 { fill: var(--chart-series-5); }

.chart-hatch-line {
    stroke: var(--chart-hatch);
    stroke-width: 1.5;
}

/* ── 목표 대비 ──────────────────────────────────────────── */
.chart-bullet-track {
    fill: var(--chart-target-track);
}

.chart-bullet-bar {
    fill: var(--chart-now);
}

/* 목표는 색이 아니라 **형태**입니다 — 흑백 인쇄에서도 남습니다. */
.chart-bullet-target {
    stroke: var(--chart-target);
    stroke-width: 2.5;
}

/* ── 타일 안 미니 추이 ──────────────────────────────────── */
.chart-spark-box {
    width: 7.5rem;
    height: 1.75rem;
    overflow: visible;
}

.chart-spark {
    fill: none;
    stroke: var(--chart-now);
    stroke-width: 1.5;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

/* ── 히트맵 — 표에 얹는다 ───────────────────────────────────
   🔴 격자를 따로 그리지 않습니다. 칸 배경만 정하고 **숫자는 칸 안에**
   그대로 둡니다(libs/ReportChart.php heatStep). 색을 못 가리는 사람도
   값을 읽을 수 있어야 합니다. */
.cell-heat-0 { background: var(--chart-heat-0); }
.cell-heat-1 { background: var(--chart-heat-1); }
.cell-heat-2 { background: var(--chart-heat-2); }
.cell-heat-3 { background: var(--chart-heat-3); }
.cell-heat-4 { background: var(--chart-heat-4); }

/* 격자 표 — 가로로 길어진다. 🔴 **첫 열은 붙박이**다. 옆으로 밀면 그 줄이
   누구 줄인지 모르게 되고, 그러면 표를 세로로 읽을 수 없다. */
.matrix-wrap {
    max-height: 70vh;
}

.matrix-table th.matrix-head {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--color-surface);
    text-align: left;
    font-weight: 500;
    white-space: nowrap;
}

.matrix-table tr.is-total th.matrix-head,
.matrix-table tr.is-muted th.matrix-head {
    background: var(--color-bg-subtle);
}

/* 칸 진하기는 글자 뒤에 깔린다. 칸 전체를 칠하면 옆 칸과 붙어 보인다. */
.matrix-table td .cell-heat-1,
.matrix-table td .cell-heat-2,
.matrix-table td .cell-heat-3,
.matrix-table td .cell-heat-4 {
    display: inline-block;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-sm);
}

/* 작년엔 있었고 이번엔 없는 칸. 한 번도 없는 칸(빈칸)과 **달라야** 한다. */
.cell-was {
    color: var(--color-text-muted);
    font-style: italic;
    white-space: nowrap;
}

/* 목표 표 — 한 칸에 실적 · 목표 · 달성률 셋이 세로로 쌓인다. */
.goal-cell {
    /* 목표 칸에는 백만 단위가 들어간다. 좁게 두면 사람이 방금 친 숫자의
       끝자리를 못 본다. */
    min-width: 8.5rem;
    vertical-align: top;
}

.goal-actual {
    display: block;
    font-weight: 600;
}

.goal-table .goal-target {
    width: 100%;
    margin-top: 0.15rem;
    text-align: right;
}

.goal-target-read {
    display: block;
    color: var(--color-text-secondary);
}

.goal-share {
    display: block;
    margin-top: 0.15rem;
}

/* 층마다 얼마나 확실한가 — 표 아래 한 덩이. 머리글에 넣으면 열이 넓어져
   금액 칸이 화면 밖으로 밀려난다. */
.layer-notes {
    display: grid;
    grid-template-columns: minmax(9rem, max-content) 1fr;
    gap: 0.35rem 1rem;
    margin: 0;
}

.layer-notes dt {
    font-weight: 600;
    color: var(--color-text);
}

.layer-notes dd {
    margin: 0;
    color: var(--color-text-secondary);
}

/* "안 봐도 된다" 고 표시하는 자리. 이유 칸과 단추가 한 줄에 붙는다. */
.put-away {
    display: flex;
    gap: 0.35rem;
    align-items: center;
    min-width: 14rem;
}

/* 잘려서 한 칸으로 묶인 열. 진짜 열과 구분되어야 한다. */
.matrix-table th.is-muted-head,
.matrix-table td.is-muted-cell {
    color: var(--color-text-secondary);
    background: var(--color-bg-subtle);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        /* 어두운 바탕에서는 **밝은 것이 앞으로** 옵니다. 밝기를 그대로 두면
           올해 막대가 바탕에 묻힙니다.

           작년 막대의 채움은 어두운 바탕과 2:1 남짓입니다. 더 밝히면 올해
           막대와 구분이 흐려지므로, **형태를 테두리가 집니다** — 테두리는
           바탕과 4.8:1 이라 채움이 흐려도 막대가 거기 있다는 것이 보입니다.
           정확한 숫자는 언제나 표에 있습니다. */
        --chart-now: #60a5fa;
        --chart-before: #33517f;
        --chart-before-edge: #6f96cc;
        --chart-up: #4ade80;
        --chart-down: #f87171;
        --chart-grid: #334155;
        --chart-line-2: #fbbf24;
        --chart-line-3: #c4b5fd;
        --chart-series-1: #60a5fa;
        --chart-series-2: #22d3ee;
        --chart-series-3: #fbbf24;
        --chart-series-4: #c4b5fd;
        --chart-series-5: #94a3b8;
        /* 🔴 **방향을 안 뒤집습니다.** 뒤집으면 같은 화면을 두 사람이 반대로
           읽습니다. 밝기 범위만 옮깁니다. */
        --chart-heat-0: transparent;
        --chart-heat-1: #1e293b;
        --chart-heat-2: #253449;
        --chart-heat-3: #2c4160;
        --chart-heat-4: #334e77;
        --chart-target: #e2e8f0;
        --chart-target-track: #334155;
        --chart-hatch: #0f172a;
    }
}

:root[data-theme="dark"] {
    --chart-now: #60a5fa;
    --chart-before: #33517f;
    --chart-before-edge: #6f96cc;
    --chart-up: #4ade80;
    --chart-down: #f87171;
    --chart-grid: #334155;
    --chart-line-2: #fbbf24;
    --chart-line-3: #c4b5fd;
    --chart-series-1: #60a5fa;
    --chart-series-2: #22d3ee;
    --chart-series-3: #fbbf24;
    --chart-series-4: #c4b5fd;
    --chart-series-5: #94a3b8;
    /* 🔴 **방향을 안 뒤집습니다.** 뒤집으면 같은 화면을 두 사람이 반대로
       읽습니다. 밝기 범위만 옮깁니다. */
    --chart-heat-0: transparent;
    --chart-heat-1: #1e293b;
    --chart-heat-2: #253449;
    --chart-heat-3: #2c4160;
    --chart-heat-4: #334e77;
    --chart-target: #e2e8f0;
    --chart-target-track: #334155;
    --chart-hatch: #0f172a;
}

@media print {
    :root {
        /* 종이는 흑백일 수 있습니다. 그래서 작년 막대는 **테두리로도**
           구분되게 두고, 채움은 옅게 둡니다. */
        --chart-now: #1d4ed8;
        --chart-before: #dbeafe;
        --chart-before-edge: #64748b;
        --chart-up: #15803d;
        --chart-down: #b91c1c;
        --chart-grid: #cbd5e1;

        /* 종이에서는 **명도 차이를 키웁니다** — 흑백 복사에서 회색조로
           갈라져야 합니다. 히트맵은 반대로 옅게 둡니다: 진한 칸에 잉크를
           부으면 그 칸의 숫자를 못 읽습니다. */
        --chart-line-2: #92400e;
        --chart-line-3: #4c1d95;
        --chart-series-1: #1e3a8a;
        --chart-series-2: #0e7490;
        --chart-series-3: #92400e;
        --chart-series-4: #6d28d9;
        --chart-series-5: #94a3b8;
        --chart-heat-0: transparent;
        --chart-heat-1: #f8fafc;
        --chart-heat-2: #f1f5f9;
        --chart-heat-3: #e2e8f0;
        --chart-heat-4: #cbd5e1;
        --chart-target: #0f172a;
        --chart-target-track: #f1f5f9;
        --chart-hatch: #ffffff;
    }

    /* 그림이 쪽 사이에서 잘리면 읽을 수 없습니다. */
    .chart-frame,
    .admin-table {
        break-inside: avoid;
    }
}

/* ────────────────────────────────────────────────────────────
   보고서의 표와 양식 고르기
   ──────────────────────────────────────────────────────────── */

/* 합계 줄. 눈에 안 띄면 사람이 표를 끝까지 훑고도 못 찾습니다. */
.admin-table tbody tr.is-total > td {
    border-top: 2px solid var(--color-border-strong);
    background: var(--color-bg-subtle);
}

/* 잘려서 한 줄로 묶인 나머지. 진짜 줄과 **구분되어야** 합니다. */
.admin-table tbody tr.is-muted > td {
    color: var(--color-text-secondary);
    background: var(--color-bg-subtle);
}

.preset-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: var(--space-4);
}

.preset-card {
    display: block;
    padding: var(--space-5);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: inherit;
    text-decoration: none;
}

.preset-card:hover,
.preset-card:focus-visible {
    border-color: var(--color-primary);
    background: var(--color-primary-bg);
}

.preset-card-name {
    display: block;
    margin-bottom: var(--space-2);
    font-weight: 600;
    color: var(--color-text);
}

.preset-card-lead {
    display: block;
    margin-bottom: var(--space-2);
    font-size: var(--text-md);
    color: var(--color-text-secondary);
    line-height: var(--leading-snug);
}

/* 왜 이 보고서를 보는가. 이름만으로는 고를 수 없습니다. */
.preset-card-why {
    display: block;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    line-height: var(--leading-snug);
}

/* 맨 아래 줄은 아래 여백이 필요 없습니다. 앞세운 카드는 설명 한 줄뿐이라
   여백이 남으면 카드가 이유 없이 커 보입니다. */
.preset-card > :last-child {
    margin-bottom: 0;
}

/* 화면에 내놓는 넷. **왜 보는 표인지**는 팝업에서 읽습니다 — 열세 장의 이유를
   한 화면에 늘어놓으면 보고서 화면이 양식 고르는 화면이 됩니다. */
.preset-grid.is-compact {
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
}

.preset-section {
    margin-bottom: var(--space-7);
}

.preset-section:last-child {
    margin-bottom: 0;
}

.preset-section-head {
    margin-bottom: var(--space-3);
}

.preset-section-head strong {
    display: block;
    color: var(--color-text);
}

/* 지금 무엇으로 좁혀 놓았는지. 종이에도 나갑니다. */
.report-scope {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

.report-scope .badge {
    font-variant-numeric: tabular-nums;
}

/* 보고서 표지 — 이 종이가 무엇인지. 종이 한 장이 홀로 돌아다니므로
   무엇을 언제 어떤 기준으로 뽑았는지가 그 장 안에 있어야 합니다. */
.report-meta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-3) var(--space-7);
    margin: 0;
}

.report-meta > div {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
}

.report-meta dt {
    flex: 0 0 9rem;
    margin: 0;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

.report-meta dd {
    margin: 0;
    color: var(--color-text);
}
