/* =====================================================================
   OpenMain admin.css — 디자인 개편 (현 구조 유지 + 정제)
   어드민·광고주 양 포털이 공유한다. 기존 클래스/레이아웃은 유지하고
   디자인 토큰·타이포·여백·그림자·컴포넌트만 고급화한다.
   로고/브랜드 마크는 변경 범위에서 제외(.brand-mark 원형 유지).
   ===================================================================== */

/* Pretendard (로컬 vendor) — admin.css 를 링크하는 모든 페이지에 적용된다.
   가변 폰트 단일 파일로 400~700 굵기를 모두 커버한다. */
@font-face {
    font-family: "Pretendard Variable";
    font-weight: 45 920;
    font-style: normal;
    font-display: swap;
    src: url("/admin/assets/vendor/pretendard/PretendardVariable.woff2") format("woff2-variations");
}

:root {
    --om-sidebar-width: 256px;

    /* Neutral scale */
    --om-bg: #f4f6fa;
    --om-surface: #ffffff;
    --om-surface-2: #fafbfd;
    --om-border: #e5e9f0;
    --om-border-strong: #d3d9e3;

    /* Text */
    --om-text: #1b2436;
    --om-muted: #69748a;
    --om-faint: #97a1b3;

    /* Brand & semantic */
    --om-primary: #3a5bd9;
    --om-primary-weak: #eef2fe;
    --om-success: #15875a;
    --om-success-weak: #e4f4ec;
    --om-info: #1f7ab0;
    --om-info-weak: #e5f1f8;
    --om-warning: #b8741a;
    --om-warning-weak: #fcf0df;
    --om-danger: #c63b48;
    --om-danger-weak: #fbe9eb;

    /* Sidebar (light) */
    --om-side-bg: #ffffff;
    --om-side-bg-2: #fbfcfe;
    --om-side-text: #3d4759;
    --om-side-text-strong: #1b2436;

    /* Elevation */
    --om-radius: 14px;
    --om-radius-sm: 10px;
    --om-shadow-sm: 0 1px 2px rgba(20, 28, 46, .05), 0 1px 3px rgba(20, 28, 46, .04);
    --om-shadow: 0 1px 2px rgba(20, 28, 46, .04), 0 6px 16px rgba(20, 28, 46, .06);
    --om-shadow-lg: 0 12px 32px rgba(20, 28, 46, .12);

    /* Bootstrap link 색을 브랜드와 정렬 (컴포넌트 배경은 건드리지 않음) */
    --bs-link-color: #3a5bd9;
    --bs-link-hover-color: #2f4dc2;
}

[x-cloak] { display: none !important; }

body {
    min-height: 100vh;
    color: var(--om-text);
    background: var(--om-bg);
    font-family: "Pretendard Variable", "Pretendard", system-ui, -apple-system,
        "Apple SD Gothic Neo", "Malgun Gothic", "Segoe UI", sans-serif;
    /* 한글 자간 정제 — letter-spacing 은 상속되므로 body 스코프로 충분(전역 * 미사용) */
    letter-spacing: -.01em;
    -webkit-font-smoothing: antialiased;
}

/* ---------- Header ---------- */
.admin-header {
    position: sticky;
    top: 0;
    z-index: 1030;
    display: flex;
    align-items: center;
    gap: 16px;
    min-height: 64px;
    padding: 0 24px;
    background: rgba(255, 255, 255, .85);
    backdrop-filter: saturate(180%) blur(8px);
    border-bottom: 1px solid var(--om-border);
}

.brand-mark {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: var(--om-text);
    font-weight: 700;
    font-size: 1.02rem;
}

.brand-mark img {
    flex: 0 0 auto;
}

/* ---------- Shell / Sidebar ---------- */
.admin-shell {
    display: flex;
    min-height: 100vh;
}

.admin-sidebar,
.advertiser-sidebar {
    flex: 0 0 var(--om-sidebar-width);
    width: var(--om-sidebar-width);
    padding: 16px 14px;
    background: linear-gradient(180deg, var(--om-side-bg), var(--om-side-bg-2));
    color: var(--om-side-text);
    border-right: 1px solid var(--om-border);
    /* 본문과의 분리감 — 우측으로 옅은 그림자 */
    box-shadow: 4px 0 16px rgba(20, 28, 46, .04);
    /* 헤더를 사이드바로 통합 — 화면 최상단부터 전체 높이로 고정한다.
       메뉴(nav)만 내부 스크롤하고 브랜드/계정 블록은 상단에 고정된다.
       모바일(≤991.98px)은 아래 미디어쿼리에서 position:fixed 오프캔버스로 덮어쓴다. */
    align-self: flex-start;
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* 사이드바 상단: 브랜드 로고 */
.admin-sidebar .sidebar-brand,
.advertiser-sidebar .sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px 0;
    margin-bottom: 20px;
    color: var(--om-side-text-strong);
    font-weight: 700;
    font-size: 1rem;
    text-decoration: none;
}

/* 사이드바 하단 고정: 계정 카드(아바타 · 이메일 · 역할/대리 배지 · 로그아웃).
   메뉴(.nav)가 flex:1 로 공간을 채워 카드는 자연히 하단에 위치한다. */
.admin-sidebar .sidebar-account,
.advertiser-sidebar .sidebar-account {
    flex: 0 0 auto;
    padding: 12px;
    margin-top: 14px;
    background: var(--om-surface-2);
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
}

.admin-sidebar .sidebar-account-row,
.advertiser-sidebar .sidebar-account-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}

.admin-sidebar .sidebar-avatar,
.advertiser-sidebar .sidebar-avatar {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, var(--om-primary), #5b76e6);
    color: #fff;
    font-weight: 700;
    font-size: 1rem;
    box-shadow: 0 2px 8px rgba(58, 91, 217, .35);
}

.admin-sidebar .sidebar-account-meta,
.advertiser-sidebar .sidebar-account-meta {
    min-width: 0;
}

.admin-sidebar .sidebar-account-id,
.advertiser-sidebar .sidebar-account-id {
    color: var(--om-side-text-strong);
    font-size: .84rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.admin-sidebar .sidebar-role,
.advertiser-sidebar .sidebar-role {
    display: inline-block;
    margin-top: 3px;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--om-primary-weak);
    color: var(--om-primary);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .03em;
}

/* 메뉴 그룹 라벨(.sidebar-section) — 긴 메뉴를 의미 단위로 구획 */
.admin-sidebar > .nav > .sidebar-group:first-child > .sidebar-section {
    padding-top: 2px;
}

/* 메뉴만 내부 스크롤(브랜드/계정은 상단 고정).
   ★ .nav 는 Bootstrap 기본 flex-wrap:wrap 이라, flex-column 이어도 사이드바
   높이가 메뉴보다 짧아지면(모바일/고배율) 항목이 옆 칸으로 접혀 2열이 된다.
   flex-wrap:nowrap 로 다단을 막고 높이 부족분은 세로 스크롤로 흘린다. */
.admin-sidebar > .nav,
.advertiser-sidebar > .nav {
    flex: 1 1 auto;
    min-height: 0;
    flex-wrap: nowrap;
    overflow-y: auto;
    /* 스크롤 기능은 유지하되 스크롤바는 숨긴다. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.admin-sidebar > .nav::-webkit-scrollbar,
.advertiser-sidebar > .nav::-webkit-scrollbar {
    display: none;
}

/* 사이드바 섹션 라벨 (마크업에 .sidebar-section 추가 시 노출, 미사용 시 무해) */
.sidebar-section {
    padding: 16px 12px 6px;
    font-size: .85rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: #5d6987;
}

/* 접이식 메뉴 그룹 — 각 그룹은 독립적으로 여닫힌다(동시에 여러 개 열림 허용).
   초기 열림 그룹은 서버(sidebar.php)가 현재 경로로 판정해 open 속성을 붙인다. */
/* 그룹 머리글은 하위 메뉴보다 진하게 둬서 경계가 색 대비로 읽히게 한다.
   호버는 반대로 옅어진다 — 눌러서 여닫는 요소라는 신호를 선 없이 준다. */
.sidebar-group > .sidebar-section {
    display: flex;
    align-items: center;
    gap: 6px;
    border-radius: var(--om-radius-sm);
    color: var(--om-side-text-strong);
    cursor: pointer;
    user-select: none;
    list-style: none;
    transition: color .14s ease;
}

.sidebar-group > .sidebar-section::-webkit-details-marker {
    display: none;
}

.sidebar-group > .sidebar-section:hover {
    color: var(--om-muted);
}

.sidebar-group > .sidebar-section:focus-visible {
    outline: 2px solid var(--om-primary);
    outline-offset: 2px;
}

.sidebar-group > .sidebar-section > span:first-child {
    margin-right: auto;
}

.sidebar-caret {
    flex: 0 0 auto;
    font-size: 1rem;
    letter-spacing: normal;
    transition: transform .2s ease;
}

.sidebar-group[open] > .sidebar-section .sidebar-caret {
    transform: rotate(180deg);
}

/* 그룹이 열려 있으면 항목 자체에 배지가 보이므로 헤더 배지는 감춘다. */
.sidebar-group[open] > .sidebar-section .sidebar-group-badge {
    display: none;
}

.sidebar-group-body {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    padding-top: 2px;
}

/* 펼침/접힘 애니메이션.
   details 는 원래 즉시 토글되므로 ::details-content 를 높이 0 ↔ auto 로 전환한다.
   auto 로의 전환에는 interpolate-size 가, 닫힐 때 콘텐츠가 즉시 사라지지 않게 하려면
   content-visibility 의 allow-discrete 가 필요하다.
   미지원 브라우저는 규칙이 무시되어 기존처럼 즉시 열린다(동작 자체는 동일). */
.admin-sidebar {
    interpolate-size: allow-keywords;
}

.sidebar-group::details-content {
    interpolate-size: allow-keywords;
    block-size: 0;
    opacity: 0;
    overflow: hidden;
    transition:
        block-size .24s ease,
        opacity .16s ease,
        content-visibility .24s allow-discrete;
}

.sidebar-group[open]::details-content {
    block-size: auto;
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {

    .sidebar-group::details-content,
    .sidebar-caret {
        transition: none;
    }
}

.admin-sidebar .nav-link,
.advertiser-sidebar .nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 11px;
    min-height: 42px;
    padding: 9px 12px;
    color: var(--om-side-text);
    border-radius: 10px;
    font-size: .92rem;
    font-weight: 500;
    transition: background .14s ease, color .14s ease;
}

.admin-sidebar .nav-link i,
.advertiser-sidebar .nav-link i {
    font-size: 1.18rem;
    opacity: .95;
}

.admin-sidebar .nav-link:hover,
.advertiser-sidebar .nav-link:hover {
    color: var(--om-side-text-strong);
    background: var(--om-primary-weak);
}

.admin-sidebar .nav-link.active,
.advertiser-sidebar .nav-link.active {
    color: #fff;
    background: linear-gradient(90deg, rgba(58, 91, 217, .92), rgba(58, 91, 217, .72));
    box-shadow: 0 6px 16px rgba(58, 91, 217, .35);
}

.admin-sidebar .nav-link.active i,
.advertiser-sidebar .nav-link.active i {
    opacity: 1;
}

.admin-sidebar .nav-link.active::before,
.advertiser-sidebar .nav-link.active::before {
    content: "";
    position: absolute;
    left: -14px;
    top: 9px;
    bottom: 9px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: #8ea6ff;
}

.admin-sidebar .nav-link.disabled,
.advertiser-sidebar .nav-link.disabled {
    color: #6b7689;
    background: transparent;
}

/* ---------- Main ---------- */
.admin-main {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0 32px 30px;
}

.page-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 22px;
}

/* 헤더 줄은 스크롤해도 상단에 붙어 있는다. 긴 폼에서 저장·취소를 누르려고
   맨 아래까지 내려갔다 올 필요가 없어야 한다.
   .admin-main 의 padding 만큼 음수 마진으로 넓히지 않으면 본문이 헤더 옆으로 비쳐 지나간다.
   모바일은 아래 .page-heading-actions 가 이미 fixed 라 제외한다. */
@media (min-width: 992px) {
    .page-heading {
        position: sticky;
        top: 0;
        z-index: 1025;
        padding: 16px 0;
        background: var(--om-bg);
        border-bottom: 1px solid var(--om-border);
    }
}

/* padding-left 42px 는 고정 햄버거(top 12 / left 12 / 42px) 자리다.
   z-index 는 사이드바 서랍(1020)·햄버거(1031) 아래여야 한다 — 위에 두면 서랍을 열었을 때
   메뉴 상단이 가린다. */
@media (max-width: 991.98px) {
    .page-heading {
        position: sticky;
        top: 0;
        z-index: 1010;
        flex-wrap: wrap;
        align-items: center;
        gap: 8px 12px;
        min-height: 66px;
        margin: 0 0 16px;
        padding: 10px 0 10px 42px;
        background: var(--om-bg);
        border-bottom: 1px solid var(--om-border);
    }

    .page-heading > :first-child {
        flex: 1 1 auto;
        min-width: 0;
    }

    .page-heading > :first-child ~ * {
        flex: 0 1 auto;
        margin-left: auto;
    }

    .page-heading h1 {
        word-break: keep-all;
    }

    /* 버튼은 뷰의 Bootstrap flex-wrap 유틸대로 줄바꿈시킨다(가로 스크롤 금지).
       좁은 폭에서 라벨이 글자 단위로 쪼개지지 않도록 버튼 단위로만 넘긴다. */
    .page-heading-actions .btn {
        flex: 0 0 auto;
        padding: .38rem .6rem;
        font-size: .85rem;
        white-space: nowrap;
    }
}

.page-heading h1 {
    font-weight: 700;
    letter-spacing: -.02em;
}

.page-heading .text-secondary {
    color: var(--om-muted) !important;
    font-size: .92rem;
}

/* ---------- Cards / Panels ---------- */
.metric-card,
.content-panel,
.login-panel {
    background: var(--om-surface);
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius);
}

.metric-card {
    display: grid;
    grid-template-columns: 46px 1fr;
    column-gap: 14px;
    row-gap: 2px;
    min-height: 112px;
    padding: 18px 20px;
    transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}

.metric-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--om-shadow);
    border-color: var(--om-border-strong);
}

/* 리포트 카드: 아이콘 없는 라벨/값 2열 — 라벨 칸 넓게(1fr), 숫자 칸은 내용 너비(auto).
   min-height 112px 는 46px 아이콘을 담기 위한 값이라 아이콘이 없는 여기서는 빈 여백만 만든다. */
.metric-card.metric-card--report {
    grid-template-columns: 1fr auto;
    align-items: center;
    column-gap: 16px;
    min-height: 0;
    padding: 14px 20px;
}

.metric-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    grid-row: 1 / span 2;
    width: 46px;
    height: 46px;
    border-radius: 12px;
    font-size: 1.3rem;
}

/* 메트릭 아이콘: 솔리드(text-bg-*) → 부드러운 틴티드 톤으로 재정의.
   .metric-icon 안으로 스코프해 다른 곳의 text-bg-* 사용에는 영향 없음. */
.metric-icon.text-bg-primary   { color: var(--om-primary) !important; background: var(--om-primary-weak) !important; }
.metric-icon.text-bg-success   { color: var(--om-success) !important; background: var(--om-success-weak) !important; }
.metric-icon.text-bg-info      { color: var(--om-info) !important;    background: var(--om-info-weak) !important; }
.metric-icon.text-bg-warning   { color: var(--om-warning) !important; background: var(--om-warning-weak) !important; }
.metric-icon.text-bg-secondary { color: var(--om-muted) !important;   background: #eef1f6 !important; }
.metric-icon.text-bg-dark      { color: #2b3650 !important;           background: #e7eaf1 !important; }

.metric-label {
    align-self: end;
    color: var(--om-muted);
    font-size: .88rem;
    font-weight: 500;
}

.metric-card strong {
    align-self: start;
    font-size: 1.7rem;
    font-weight: 700;
    line-height: 1.18;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

/* 리포트 보조 지표 스트립 — 칸 사이 세로 구분선.
   줄바꿈 후 각 행의 첫 칸에는 선이 남으면 안 되므로, 브레이크포인트별 열 수에 맞춰 지운다.
   (xl 이상은 col-xl 로 한 줄에 다 들어가 :first-of-type 규칙만 걸린다)

   nth-child 가 아니라 nth-of-type 인 이유: Alpine 의 <template x-for> 는 생성한 칸을
   template 의 형제로 넣는다. nth-child 는 그 template 까지 세어 인덱스가 한 칸씩 밀린다.
   칸은 전부 div, template 는 다른 타입이므로 nth-of-type 은 칸만 센다. */
.metric-strip > div {
    border-left: 1px solid var(--om-border);
}

.metric-strip > div:first-of-type {
    border-left: 0;
}

/* 모바일은 1행 1항목 — 세로 구분선 대신 가로선으로 나눈다(라벨/값을 한 줄에 두려면 폭이 필요하다) */
@media (max-width: 767.98px) {
    .metric-strip > div { border-left: 0; }
    .metric-strip > div:not(:first-of-type) { border-top: 1px solid var(--om-border); }
}

@media (min-width: 768px) and (max-width: 1199.98px) {
    .metric-strip > div:nth-of-type(3n + 1) { border-left: 0; }
    .metric-strip > div:nth-of-type(n + 4)  { border-top: 1px solid var(--om-border); }
}

/* 리포트 지표 카드 — lg 미만에서는 칸이 좁아(2열 또는 4열) 라벨/값을 나란히 두면 넘친다.
   위아래로 쌓고, min-height 112px 를 풀어 카드 4장이 화면을 다 먹지 않게 한다. */
@media (max-width: 991.98px) {
    .metric-card.metric-card--report {
        grid-template-columns: 1fr;
        align-items: start;
        row-gap: 2px;
        min-height: 0;
        padding: 12px 14px;
    }

    .metric-card.metric-card--report .fs-4 {
        font-size: 1.25rem !important;
    }
}

/* 리포트 추이 차트 — 높이를 고정하지 않으면 Chart.js 가 canvas 기본 비율(3:1)로 늘려
   화면 하나를 통째로 먹는다. 래퍼 높이를 정하고 maintainAspectRatio:false 로 채운다. */
/* 화면이 낮을수록 차트를 줄여 일자별 표에 자리를 넘긴다. 300px 고정이면
   1366x768 같은 화면에서 표가 한 줄도 안 남는다. */
.report-chart {
    height: clamp(150px, 30vh, 420px);
}

/* 낮은 화면에서는 지표 카드를 더 줄인다 — 패널 높이가 고정이라 카드가 먹는 만큼 표가 줄어든다. */
@media (max-height: 860px) {
    .report-tabpanel .metric-card.metric-card--report {
        padding: 10px 14px;
    }

    .report-tabpanel .metric-card.metric-card--report .fs-4 {
        font-size: 1.2rem !important;
    }
}

/* 기간 필터 — 모바일에서는 날짜 2칸 + 버튼이 한 줄에 안 들어가 화면을 넘친다.
   전체 폭을 쓰고 날짜 칸이 첫 줄을 차지하게 한다. 남는 폭을 나눠 갖게 하면(flex-basis 0)
   버튼이 늘어난 만큼 날짜가 눌려 "2026-07" 이 "20:" 으로 잘린다 — 기준을 45% 로 잡아
   버튼을 다음 줄로 밀어낸다. */
@media (max-width: 575.98px) {
    .report-filter {
        width: 100%;
        flex-wrap: wrap;
    }

    .report-filter input[type="date"] {
        flex: 1 1 45%;
        min-width: 0;
        width: auto;
    }

    .report-filter .btn {
        flex: 1 1 auto;
    }
}

/* 일자별 지표는 화면 폭에 따라 표현을 바꾼다.
   데스크톱은 전체 기간 표, 모바일은 하루치 카드 1장 + 좌우 이동·날짜 선택.
   Bootstrap 의 d-none/d-lg-block 을 쓰면 .table-scroll-wrap 의 flex 표시가 덮여
   데스크톱 전체높이 레이아웃이 깨지므로 여기서 직접 가른다. */
@media (max-width: 991.98px) {
    .report-daily-table,
    .report-daily-pager {
        display: none;
    }
}

@media (min-width: 992px) {
    .report-daily-card {
        display: none;
    }
}

/* 지표 설명 말풍선 — 여닫기는 Alpine(openTip)이 x-show 로 한다.
   호버가 아니라 클릭인 이유: 같은 ⓘ 아이콘을 쓰는 스마트스토어 도움말이 클릭(모달)이라
   한 화면에서 동작이 갈리면 헷갈린다. 터치 기기에 호버가 없는 문제도 함께 사라진다.
   내용이 실제 요소인 이유: 클릭률 설명에 분수 표기가 들어가 attr() 문자열로는 못 그린다. */
.metric-tip {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--om-faint);
    font-size: 1.15rem;
    /* 굵은 제목(.fw-semibold) 안에 들어가면 브라우저가 아이콘 폰트에 가짜 볼드를 입혀
       같은 글리프가 더 두껍게 그려진다. 어디에 놓이든 같은 굵기로 고정한다. */
    font-weight: 400;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
}

.metric-tip:hover,
.metric-tip:focus-visible {
    color: var(--om-primary);
}

.metric-bubble {
    position: absolute;
    left: 50%;
    bottom: calc(100% + 10px);
    transform: translateX(-50%);
    z-index: 1050;
    width: max-content;
    max-width: 240px;
    padding: 9px 11px;
    border-radius: 10px;
    background: var(--om-text);
    color: #fff;
    font-size: .78rem;
    font-weight: 500;
    line-height: 1.45;
    letter-spacing: -.01em;
    text-align: left;
    white-space: normal;
    box-shadow: var(--om-shadow);
    pointer-events: none;
    /* 항상 배치해 두고 표시만 토글한다 — 숨겼다 켜면 폭이 그때 계산돼
       좁게 떴다가 넓어지며 가운데 정렬이 다시 잡히는(옆에서 미끄러지는) 움직임이 보인다.
       위치는 고정한 채 투명도만 바꾸므로 제자리에서 뜨고 사라진다. */
    opacity: 0;
    visibility: hidden;
    transition: opacity .12s ease, visibility 0s linear .12s;
}

.metric-bubble.is-open {
    opacity: 1;
    visibility: visible;
    transition: opacity .12s ease, visibility 0s;
}

/* 꼬리 — 사각형을 45도 돌려 말풍선 몸통과 이어 붙인다(테두리 삼각형보다 각이 또렷하다) */
.metric-bubble::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    width: 10px;
    height: 10px;
    margin: -5px 0 0 -5px;
    border-radius: 2px;
    background: inherit;
    transform: rotate(45deg);
}

/* 표 헤더용 — 위로 펴면 스크롤 영역 바깥이라 잘린다. 아래로 펴고 꼬리도 뒤집는다.
   헤더 타이포(대문자 변환)가 설명 문장까지 바꾸지 않도록 여기서 끊는다. */
.metric-bubble--below {
    top: calc(100% + 10px);
    bottom: auto;
    text-transform: none;
}

.metric-bubble--below::after {
    top: auto;
    bottom: 100%;
    margin: 0 0 -5px -5px;
}

/* 첫 열에서는 가운데 정렬하면 말풍선 왼쪽 절반이 표 바깥으로 나가 잘린다 — 왼쪽에 붙이고 꼬리만 ⓘ 위에 남긴다.
   :first-child 가 아니라 :first-of-type 인 이유: x-for 의 <template> 이 tr 의 첫 자식으로 남아 첫 th 가 first-child 가 아니다. */
.table > thead > tr > th:first-of-type .metric-bubble--below {
    left: 0;
    transform: none;
}

.table > thead > tr > th:first-of-type .metric-bubble--below::after {
    left: 8px;
}

.metric-tip[aria-expanded="true"] {
    color: var(--om-primary);
}

/* 클릭률 설명의 분수 표기 — CTR(%) = (클릭수 / 노출수) × 100 */
.metric-bubble .tip-formula {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
}

.metric-bubble .tip-formula i {
    font-style: italic;
}

.metric-bubble .tip-paren {
    font-size: 2em;
    font-weight: 300;
    line-height: .9;
}

.metric-bubble .tip-frac {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    line-height: 1.3;
}

.metric-bubble .tip-frac > span:first-child {
    padding: 0 3px 2px;
    border-bottom: 1px solid currentColor;
}

.metric-bubble .tip-frac > span:last-child {
    padding: 2px 3px 0;
}

/* 리포트 일자별 표 — 전체높이·숨김 스크롤바·하단 페이드는 목록 화면과 같은 규약
   (.panel-fill / .table-scroll-wrap / .table-scroll / .has-more)을 그대로 쓴다.
   여기서는 표 고유의 행 간격과 헤더 고정만 얹는다. */
.report-daily > .table > :not(caption) > * > * {
    padding-top: .5rem;
    padding-bottom: .5rem;
}

.report-daily > .table > thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--om-surface);
    box-shadow: inset 0 -1px 0 var(--om-border);
}

/* 탭 패널은 패널(.panel-fill)과 표(.table-scroll-wrap) 사이의 높이 전달 통로다. */
@media (min-width: 992px) {
    .report-tabpanel {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }
}

.content-panel {
    padding: 22px 24px;
}

/* 패널을 연달아 놓으면 경계선만 남고 붙어 버린다(캠페인 상세의 청구 목록 ↔ 기본 정보). */
.content-panel + .content-panel {
    margin-top: 16px;
}

/* 목록 하단 페이저 — 페이지 버튼은 항상 가운데, 표시 범위는 좌측에 겹치지 않게 띄운다. */
.table-pager {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 34px;
    padding-top: 14px;
    border-top: 1px solid var(--om-border);
}

/* 표시 범위 — 목록 상단에 있던 `총 N건` 배지와 같은 형태를 재사용한다. */
.table-pager-range {
    position: absolute;
    left: 0;
    font-size: .78rem;
}

.table-pager .pagination {
    gap: 4px;
}

.table-pager .page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 34px;
    height: 34px;
    padding: 0 10px;
    border: 0;
    border-radius: var(--om-radius-sm);
    background: transparent;
    color: var(--om-muted);
    font-size: .88rem;
    font-weight: 600;
    transition: background .12s ease, color .12s ease;
}

.table-pager .page-link:hover {
    background: var(--om-surface-2);
    color: var(--om-text);
}

.table-pager .page-link:focus {
    box-shadow: none;
}

.table-pager .page-item.active .page-link {
    background: var(--om-primary);
    color: #fff;
    box-shadow: 0 2px 8px rgba(58, 91, 217, .3);
}

.table-pager .page-item.disabled .page-link {
    background: transparent;
    color: var(--om-faint);
    opacity: .45;
}

/* 좁은 화면에서는 범위 표시를 겹치지 않게 위로 쌓는다. */
@media (max-width: 991.98px) {
    .table-pager {
        flex-direction: column;
        gap: 10px;
    }

    .table-pager-range {
        position: static;
    }
}

/* 목록 전용 전체높이 레이아웃(데스크톱) — 페이지 자체는 스크롤되지 않고
   표 본문만 내부 스크롤한다. 필터/헤더/페이저는 고정 위치를 유지하며,
   행이 적어도 패널이 화면을 꽉 채운다. 모바일(≤991.98px)은 카드 레이아웃이라 제외. */
@media (min-width: 992px) {
    .admin-main:has(> .content-panel.panel-fill) {
        display: flex;
        flex-direction: column;
        height: 100vh;
        overflow: hidden;
    }

    .content-panel.panel-fill {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    .panel-fill .table-scroll-wrap {
        position: relative;
        display: flex;
        flex: 1 1 auto;
        min-height: 0;
    }

    .panel-fill .table-scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow: auto;
        /* 스크롤바는 숨기고 스크롤 동작만 남긴다. */
        scrollbar-width: none;
        -ms-overflow-style: none;
    }

    /* 아래에 남은 행이 있을 때만 켜지는 페이드 — 스크롤바 없이 "더 있음"을 알린다. */
    .panel-fill .table-scroll-wrap::after {
        content: "";
        position: absolute;
        right: 0;
        bottom: 0;
        left: 0;
        height: 34px;
        background: linear-gradient(rgba(255, 255, 255, 0), var(--om-surface));
        opacity: 0;
        transition: opacity .15s ease;
        pointer-events: none;
    }

    .panel-fill .table-scroll-wrap.has-more::after {
        opacity: 1;
    }

    .panel-fill .table-scroll::-webkit-scrollbar {
        width: 0;
        height: 0;
    }

    /* 세로 스크롤 중에도 헤더(정렬 버튼) 는 보이게 고정 */
    .panel-fill .table-scroll > .table > thead > tr > th {
        position: sticky;
        top: 0;
        z-index: 2;
    }
}

.chart-box {
    position: relative;
    min-height: 320px;
}

/* ---------- Tables ---------- */
.table {
    --bs-table-color: var(--om-text);
    margin-bottom: 0;
}

.table > thead > tr > th {
    padding: 11px 14px;
    color: var(--om-faint);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    border-bottom: 1px solid var(--om-border);
    background: var(--om-surface-2);
    white-space: nowrap;
}

/* 열 폭 고정 — 데이터 길이에 따라 칸이 흔들리지 않게 colgroup 비율을 그대로 따른다.
   넘치는 값은 말줄임 처리하고 원문은 title 로 노출한다.
   모바일은 .table-cards 카드 레이아웃이라 열 개념이 없으므로 데스크톱에만 적용한다. */
@media (min-width: 992px) {
    .table-fixed-cols {
        table-layout: fixed;
        width: 100%;
    }

    .table-fixed-cols > tbody > tr > td {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .table-fixed-cols > tbody > tr > td.td-actions {
        overflow: visible;
    }

    /* td 의 text-overflow 는 칸에 바로 들어 있는 글자에만 걸린다. 캠페인명 + 대행사처럼 칸 안에
       블록 줄을 쌓은 셀은 줄마다 말줄임을 따로 걸어야 잘린 끝에 … 가 붙는다(안 걸면 그냥 잘려 보인다).
       배지 묶음(.d-flex)은 줄바꿈이 원래 동작이라 제외한다. 잘린 글자는 truncate-tip.js 가 말풍선으로 보여준다. */
    .table-fixed-cols > tbody > tr > td:not(.td-actions) :is(div, a.d-block):not(.d-flex) {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* 표 칸의 글자 링크(캠페인명 → 캠페인 관리, 금액 → 결제 상세). 평소엔 본문처럼 보이고 올리면 밑줄. */
.om-cell-link {
    color: inherit;
    text-decoration: none;
}

.om-cell-link:hover,
.om-cell-link:focus-visible {
    text-decoration: underline;
}

/* 말줄임 말풍선 — .metric-bubble 과 같은 모양. 위치는 truncate-tip.js 가 잡는다. */
.om-truncate-tip {
    position: fixed;
    z-index: 1090;
    max-width: min(360px, calc(100vw - 16px));
    padding: 7px 10px;
    border-radius: 8px;
    background: var(--om-text);
    color: #fff;
    font-size: .78rem;
    font-weight: 500;
    line-height: 1.45;
    white-space: normal;
    word-break: break-all;
    box-shadow: var(--om-shadow);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity .12s ease;
}

.om-truncate-tip.is-open {
    opacity: 1;
    visibility: visible;
}

/* 정렬 헤더 — th 타이포는 유지하고 클릭 영역만 버튼으로 대체. */
.table > thead > tr > th.th-sortable {
    padding: 0;
}

/* 라벨(또는 정렬 버튼)과 설명 ⓘ 를 한 줄에 세우는 껍데기. 설명이 없는 열에서는
   버튼이 셀 전체를 채워 이전과 똑같이 어디를 눌러도 정렬된다. */
.table > thead > tr > th > .th-head {
    display: flex;
    align-items: center;
}

.table > thead > tr > th > .th-head--tip {
    gap: 2px;
}

.table > thead > tr > th.th-sortable .th-sort {
    display: flex;
    align-items: center;
    gap: 5px;
    width: 100%;
    padding: 11px 14px;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
}

/* ⓘ 가 붙는 열은 셀 여백을 껍데기가 맡고, 버튼은 글자 폭까지만 줄인다 —
   그러지 않으면 ⓘ 가 셀 가장자리에 붙고 라벨과의 사이는 셀 왼쪽 여백만큼 벌어진다. */
.table > thead > tr > th.th-sortable > .th-head--tip {
    padding-left: 14px;
}

.table > thead > tr > th.th-sortable > .th-head--tip > .th-sort {
    width: auto;
    padding-left: 4px;
}

.table > thead > tr > th.th-sortable .th-sort:hover,
.table > thead > tr > th.th-sortable:not([aria-sort="none"]) .th-sort {
    color: var(--om-text);
}

.table > thead > tr > th .metric-tip {
    font-size: 1rem;
}

/* 세로 스크롤 중 헤더는 sticky(z-index 2)라 형제 th 끼리 겹친다 —
   말풍선을 연 열만 위로 올려 오른쪽 열 밑에 깔리지 않게 한다. */
.table > thead > tr > th:has(.metric-tip[aria-expanded="true"]) {
    z-index: 5;
}

.th-sort-icon {
    font-size: .95rem;
    opacity: .4;
}

.table > thead > tr > th.th-sortable:not([aria-sort="none"]) .th-sort-icon {
    opacity: 1;
}

.table > tbody > tr > td {
    padding: 14px;
    border-bottom: 1px solid var(--om-border);
    vertical-align: middle;
    font-size: .92rem;
}

.table > tbody > tr {
    transition: background .12s ease;
}

.table > tbody > tr:hover {
    background: var(--om-surface-2);
}

.table code {
    padding: 2px 7px;
    border-radius: 6px;
    background: #eef1f6;
    color: #34507f;
    font-size: .82rem;
}

/* ---------- Badges (soft pill) ---------- */
.badge {
    font-weight: 600;
    padding: .4em .7em;
    border-radius: 999px;
    letter-spacing: 0;
}

.badge.text-bg-primary   { color: var(--om-primary) !important; background: var(--om-primary-weak) !important; }
.badge.text-bg-success   { color: var(--om-success) !important; background: var(--om-success-weak) !important; }
.badge.text-bg-info      { color: var(--om-info) !important;    background: var(--om-info-weak) !important; }
.badge.text-bg-warning   { color: var(--om-warning) !important; background: var(--om-warning-weak) !important; }
.badge.text-bg-danger    { color: var(--om-danger) !important;  background: var(--om-danger-weak) !important; }
/* 회색 배경(#f4f6fa) 위에서는 옅은 회색 배지가 배경에 묻혀 상태가 안 보인다.
   비활성·중지처럼 눈에 띄면 안 되는 상태라도 '배지가 있다'는 것은 보여야 한다. */
.badge.text-bg-secondary { color: var(--om-side-text) !important; background: #dde3ed !important; }
.badge.text-bg-light     { color: var(--om-muted) !important;   background: var(--om-surface-2) !important; border-color: var(--om-border) !important; }

/* ---------- Forms ---------- */
.form-label {
    font-size: .82rem;
    font-weight: 600;
    color: var(--om-muted);
    margin-bottom: 5px;
}

.form-control,
.form-select {
    border-color: var(--om-border-strong);
    border-radius: var(--om-radius-sm);
    padding: .5rem .75rem;
    font-size: .92rem;
    /* 포커스 전환 시간을 여기서 한 번에 정한다. 종전에는 .form-select 만 .14s 를 갖고
       .form-control 은 부트스트랩 기본값(.15s ease-in-out)을 써서 나란히 둔 입력칸의
       반응 속도가 미세하게 달랐다. */
    transition: border-color .14s ease, box-shadow .14s ease, background-color .14s ease;
}

/* 포커스 링은 이 한 규칙만 쓴다. 컨트롤을 새로 만들면 여기에 선택자를 더한다 —
   따로 정의하면 색·두께가 조금씩 어긋난다(칩 입력이 실제로 그랬다). */
.form-control:focus,
.form-select:focus,
.om-chip-input:focus-within {
    border-color: var(--om-primary);
    box-shadow: 0 0 0 3px rgba(58, 91, 217, .15);
}

/* 커스텀 셀렉트: 네이티브 동작/접근성 유지 + 컨트롤만 디자인.
   (열린 옵션 패널은 OS 렌더 영역이라 CSS 미적용 — JS 컴포넌트 필요) */
.form-select {
    padding-right: 2.5rem;
    color: var(--om-text);
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%2369748a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .85rem center;
    background-size: 16px 16px;
}

.form-select:hover {
    border-color: var(--om-primary);
    background-color: var(--om-surface-2);
}

.form-select:focus {
    /* 포커스 시 화살표를 브랜드 색으로 (테두리/링은 위 공통 규칙 유지) */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%233a5bd9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}

.form-select:disabled {
    color: var(--om-faint);
    background-color: var(--om-surface-2);
    cursor: not-allowed;
    opacity: 1;
}

/* 옵션 가독성 (지원 브라우저 한정, 미지원 시 무해) */
.form-select option { color: var(--om-text); }
.form-select option:checked { font-weight: 600; }

/* ---------- Buttons ---------- */
.btn {
    border-radius: var(--om-radius-sm);
    font-weight: 600;
    font-size: .9rem;
}

.btn-primary {
    --bs-btn-bg: var(--om-primary);
    --bs-btn-border-color: var(--om-primary);
    --bs-btn-hover-bg: #2f4dc2;
    --bs-btn-hover-border-color: #2f4dc2;
    --bs-btn-active-bg: #2942ab;
    --bs-btn-active-border-color: #2942ab;
    box-shadow: 0 4px 12px rgba(58, 91, 217, .25);
}

.btn-outline-secondary {
    --bs-btn-color: var(--om-muted);
    --bs-btn-border-color: var(--om-border-strong);
    --bs-btn-hover-bg: var(--om-surface-2);
    --bs-btn-hover-border-color: var(--om-border-strong);
    --bs-btn-hover-color: var(--om-text);
    --bs-btn-active-bg: var(--om-surface-2);
    --bs-btn-active-color: var(--om-text);
}

.btn-outline-primary {
    --bs-btn-color: var(--om-primary);
    --bs-btn-border-color: #b9c6f4;
    --bs-btn-hover-bg: var(--om-primary);
    --bs-btn-hover-border-color: var(--om-primary);
    --bs-btn-active-bg: var(--om-primary);
    --bs-btn-active-border-color: var(--om-primary);
}

.icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--om-border-strong);
    border-radius: var(--om-radius-sm);
    color: var(--om-muted);
}

/* ---------- Login (standalone pages) ---------- */
.login-body {
    background: radial-gradient(1200px 600px at 50% -10%, #eef2fb 0%, var(--om-bg) 60%);
}

/* ---------- 광고주 포털 하단 사업자 정보 ---------- */
/* 결제가 일어나는 화면이라 통신판매업자 신원정보가 있어야 한다(전자상거래법·카드사 심사).
   구분선은 .page-heading 과 같이 본문 폭 안에서만 긋는다 — 화면 끝까지 늘이면 헤더와 어긋난다.
   아래 여백은 .admin-main 의 padding-bottom 이 이미 만든다. 여기서 또 주면 두 배가 된다. */
/* 내용이 짧아도 푸터는 화면 바닥에 붙는다. .admin-shell 이 100vh 를 잡고 .admin-main 이 그 높이를
   채우므로, 푸터에 margin-top: auto 만 주면 남는 공간이 위로 밀린다. 내용이 길면 자연히 아래로 흐른다.
   ★본문을 별도 요소로 감싸지 않는다 — .admin-main:has(> .content-panel.panel-fill) 가
   직계 자식을 보기 때문에, 감싸는 순간 표 높이 계산이 통째로 깨진다. */
.advertiser-sidebar ~ .admin-main {
    display: flex;
    flex-direction: column;
    /* 푸터가 맨 아래에 붙으므로 본문 기본 여백(30px)은 과하다. 헤더 여백과 같은 16px 로 맞춘다. */
    padding-bottom: 16px;
}

.portal-footer {
    margin-top: auto;
    padding-top: 16px;
    color: var(--om-faint);
    font-size: 0.75rem;
    line-height: 1.6;
}


/* 한 줄로 둔다 — 상호·약관은 왼쪽, 사업자정보는 오른쪽. 좁아지면 자연히 접힌다. */
.portal-footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 6px 24px;
}


.portal-footer-top {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 16px;
}


.portal-footer-brand {
    color: var(--om-text);
    font-size: 0.8125rem;
    font-weight: 600;
}

.portal-footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
}

/* 밑줄 대신 색만 바꾼다. 작은 글씨에 밑줄이 그어지면 브라우저 기본 링크처럼 보여 지저분하다. */
.portal-footer-links a {
    color: var(--om-muted);
    text-decoration: none;
    transition: color 0.12s ease;
}

.portal-footer-links a:hover,
.portal-footer-links a:focus-visible {
    color: var(--om-text);
}

/* 라벨-값 쌍이 줄바꿈으로 갈라지면 "대표전화" 와 번호가 따로 놀아 읽히지 않는다. */
.portal-footer-biz {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 18px;
    margin: 0;
}

.portal-footer-biz > div {
    display: flex;
    gap: 6px;
    white-space: nowrap;
}


.portal-footer-biz dt {
    font-weight: 400;
    color: var(--om-faint);
}

.portal-footer-biz dd {
    margin: 0;
    color: var(--om-muted);
}

/* 사이드바 맨 아래 저작권 — 푸터가 길어지지 않게 여기로 뺀다. 계정 박스 바깥에 둔다. */
.advertiser-sidebar .sidebar-copy,
.admin-sidebar .sidebar-copy {
    flex: 0 0 auto;
    margin: 10px 0 0;
    color: var(--om-faint);
    font-size: 0.6875rem;
    text-align: center;
}
.login-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 24px;
}

.login-panel {
    width: min(100%, 400px);
    padding: 32px;
    box-shadow: var(--om-shadow);
}

/* ---------- Tom Select (커스텀 옵션 패널) ----------
   bootstrap5 테마 위에 디자인 토큰을 덧입힌다. 닫힌 컨트롤은 .form-select 와
   동일한 모양, 열린 패널은 OS 기본을 대체해 라운딩/그림자/하이라이트를 입힌다. */
/* 박스(테두리/배경/화살표)는 wrapper 에 둔다 — bootstrap5 테마가 form-select wrapper 일 때
   .ts-control 을 border:none!important / background:transparent!important 로 강제하기 때문.
   (.ts-control 에 박스를 주면 보이지 않는다) */
.ts-wrapper.form-select {
    min-height: calc(1.5em + 1rem + 2px);
    border: 1px solid var(--om-border-strong);
    border-radius: var(--om-radius-sm);
    background-color: var(--om-surface);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%2369748a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .85rem center;
    background-size: 16px 16px;
}

.ts-wrapper.form-select:not(.disabled):hover {
    border-color: var(--om-primary);
    background-color: var(--om-surface-2);
}

.ts-wrapper.form-select.focus {
    border-color: var(--om-primary);
    box-shadow: 0 0 0 3px rgba(58, 91, 217, .15);
    /* 포커스 시 화살표 브랜드색 */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%233a5bd9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}

.ts-wrapper.form-select.disabled {
    background-color: var(--om-surface-2);
}

/* 컨트롤은 박스 없는 내부 영역. 우측 패딩으로 화살표와 텍스트 겹침 방지. */
.ts-wrapper.form-select .ts-control {
    padding: .5rem 2.5rem .5rem .85rem !important;
    color: var(--om-text);
    font-size: .92rem;
    cursor: pointer;
}

.ts-wrapper.form-select.disabled .ts-control {
    color: var(--om-faint);
}

.ts-control > input {
    color: var(--om-text);
    font-size: .92rem;
}

/* 열린 옵션 패널 */
.ts-dropdown {
    margin-top: 6px;
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
    box-shadow: var(--om-shadow);
    overflow: hidden;
    font-size: .92rem;
}

/* 옵션 목록 스크롤바 숨김(스크롤 기능은 유지) */
.ts-dropdown .ts-dropdown-content {
    scrollbar-width: none;          /* Firefox */
    -ms-overflow-style: none;       /* legacy Edge/IE */
}
.ts-dropdown .ts-dropdown-content::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;                  /* Chromium/WebKit */
}

.ts-dropdown .option {
    padding: 9px 14px;
    color: var(--om-text);
    border-radius: 0;
}

.ts-dropdown .option:hover,
.ts-dropdown .active {
    color: var(--om-primary);
    background: var(--om-primary-weak);
}

.ts-dropdown .option.selected,
.ts-dropdown .selected {
    font-weight: 600;
    background: var(--om-surface-2);
}

.ts-dropdown .option.selected::after {
    content: "\2713"; /* check */
    float: right;
    color: var(--om-primary);
}

.ts-dropdown .no-results {
    padding: 10px 14px;
    color: var(--om-muted);
}

.ts-dropdown .optgroup-header {
    padding: 8px 14px;
    color: var(--om-faint);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* 헤더 제거 후 모바일 사이드바 토글(좌상단 플로팅 버튼). 데스크탑에선 숨김. */
.portal-mobile-toggle {
    display: none;
    position: fixed;
    top: 12px;
    left: 12px;
    z-index: 1031;
    width: 42px;
    height: 42px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
    background: var(--om-surface);
    color: var(--om-text);
    font-size: 1.3rem;
    box-shadow: var(--om-shadow-sm);
}

/* ---------- Responsive ---------- */
/* 데이터 목록 액션 셀 — 데스크톱에서 우측 정렬(헤더 .text-end 와 일치). 모바일은 뷰의 Bootstrap 유틸리티로 좌측 정렬한다. */
.table-cards td.td-actions {
    text-align: right;
}

@media (max-width: 991.98px) {
    /* 서랍을 열어도 햄버거(top 12 / left 12 / 42px, z 1031)는 위에 떠 있는다 —
       서랍을 닫는 유일한 수단이라 가리거나 내릴 수 없다.
       로고는 아래로 내리지 않고 버튼 오른쪽으로 밀어 같은 줄에 나란히 둔다. */
    .admin-sidebar,
    .advertiser-sidebar {
        position: fixed;
        inset: 0 auto 0 0;
        z-index: 1020;
        transform: translateX(-100%);
        transition: transform 160ms ease;
    }

    /* 사이드바 좌측 padding 14 + 여기 48 = 62px — 햄버거 우측 끝(54)을 8px 여유로 비켜난다. */
    .admin-sidebar .sidebar-brand,
    .advertiser-sidebar .sidebar-brand {
        padding-left: 48px;
    }

    .admin-sidebar.is-open,
    .advertiser-sidebar.is-open {
        transform: translateX(0);
    }

    .portal-mobile-toggle {
        display: inline-flex;
    }

    .admin-main {
        padding: 0 20px 20px;
    }

    .table-cards td.td-actions .btn {
        flex: 0 0 auto;
        white-space: nowrap;
    }

    /* 액션 컨테이너 없이 셀에 직접 놓인 버튼(예: 광고주 목록)도 라벨을 한 줄 유지 → 셀에서
       쪼개지지 않고 .table-responsive 가로 스크롤로 흘린다. */
    .table .btn {
        white-space: nowrap;
    }

    /* 사이드바 메뉴 라벨이 고배율/좁은 폭에서 접히지 않게 한 줄 + 말줄임. */
    .admin-sidebar .nav-link > span,
    .advertiser-sidebar .nav-link > span {
        min-width: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }

    .admin-sidebar .nav-link > i,
    .advertiser-sidebar .nav-link > i {
        flex: 0 0 auto;
    }

    /* 데이터 많은 목록 표: 모바일에서 각 행을 카드로 스택(.table-cards 지정 시).
       thead 숨기고 td 는 라벨:값 한 줄, 첫 셀은 카드 제목, 액션 셀은 전체폭. */
    .table-cards thead {
        display: none;
    }

    /* 표를 블록으로 전환 — 데스크톱 열 폭 지정(colgroup)이 카드 폭을 좁히지 않도록
       테이블 포매팅 컨텍스트 자체를 없앤다. */
    .table-cards.table-fixed-cols {
        display: block;
    }

    .table-cards tbody,
    .table-cards > tbody > tr,
    .table-cards > tbody > tr > td {
        display: block;
        width: 100%;
    }

    .table-cards > tbody > tr {
        border: 1px solid var(--om-border);
        border-radius: var(--om-radius-sm);
        background: var(--om-surface);
        padding: 12px 14px;
        margin-bottom: 12px;
    }

    .table-cards > tbody > tr:hover {
        background: var(--om-surface);
    }

    .table-cards > tbody > tr > td {
        display: flex;
        gap: 12px;
        justify-content: space-between;
        align-items: baseline;
        padding: 5px 0;
        border: none;
        font-size: .9rem;
    }

    .table-cards > tbody > tr > td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        color: var(--om-faint);
        font-weight: 600;
        font-size: .8rem;
    }

    /* 카드 제목(첫 셀, 캠페인명) — 라벨 없이 상단 헤더 */
    .table-cards > tbody > tr > td.td-title {
        display: block;
        padding: 0 0 8px;
        margin-bottom: 6px;
        border-bottom: 1px solid var(--om-border);
        font-size: 1rem;
    }

    .table-cards > tbody > tr > td.td-title::before {
        display: none;
    }

    /* 액션 버튼 셀 — 라벨 없이 전체폭, 버튼은 좌측부터 래핑 */
    .table-cards > tbody > tr > td.td-actions {
        display: block;
        padding-top: 10px;
        text-align: left;
    }

    .table-cards > tbody > tr > td.td-actions::before {
        display: none;
    }

    /* 빈/로딩 행(colspan) — 라벨 없이 가운데 정렬 */
    .table-cards > tbody > tr > td[colspan] {
        display: block;
        justify-content: center;
        text-align: center;
    }

    .table-cards > tbody > tr > td[colspan]::before {
        display: none;
    }
}

/* ---------- 목록 액션 버튼 ---------- */
/* btn-sm 보다 한 단계 작은 크기. 액션이 3~4개씩 붙는 목록에서 행 높이를 잡아먹지 않게 한다.
   Bootstrap 5 는 btn-xs 가 없으므로 버튼 CSS 변수로 정의한다(색상 변형과 독립적으로 조합 가능). */
.btn-xs {
    --bs-btn-padding-y: 0.15rem;
    --bs-btn-padding-x: 0.4rem;
    --bs-btn-font-size: 0.75rem;
    --bs-btn-border-radius: 0.25rem;
}

/* 경고성 액션용 주황. Bootstrap 의 warning(노랑)은 흰 배경에서 대비가 낮아 잘 보이지 않는다.
   danger(빨강)와도 구분돼야 하므로 별도 변형으로 둔다. */
.btn-outline-orange {
    --bs-btn-color: #c2410c;
    --bs-btn-border-color: #fb923c;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #ea580c;
    --bs-btn-hover-border-color: #ea580c;
    --bs-btn-focus-shadow-rgb: 234, 88, 12;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #c2410c;
    --bs-btn-active-border-color: #c2410c;
    --bs-btn-disabled-color: #fb923c;
    --bs-btn-disabled-border-color: #fb923c;
}

.text-bg-orange {
    color: #fff;
    background-color: #ea580c;
}

/* 기본 confirm/prompt 대체 창(dialog.js). 안내문의 줄바꿈이 그대로 살아야 읽힌다. */
.om-dialog .om-dialog-message {
    white-space: pre-line;
}

.om-dialog .om-dialog-detail {
    white-space: pre-line;
}

.om-dialog .om-dialog-fields {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: baseline;
    gap: 8px 16px;
    margin: 16px 0 0;
    padding: 14px 16px;
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
    background: var(--om-surface-2);
}

.om-dialog .om-dialog-fields dt {
    margin: 0;
    color: var(--om-muted);
    font-size: 0.8125rem;
    font-weight: 500;
    white-space: nowrap;
}

.om-dialog .om-dialog-fields dd {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    color: var(--om-text);
    font-size: 0.9375rem;
    font-weight: 600;
    word-break: break-all;
}

.om-dialog .om-dialog-copy {
    flex: 0 0 auto;
    padding: 2px 8px;
    border: 1px solid var(--om-border);
    border-radius: 4px;
    background: var(--om-surface);
    color: var(--om-muted);
    font-size: 0.75rem;
    font-weight: 400;
    transition: color .12s ease, border-color .12s ease;
}

.om-dialog .om-dialog-copy:hover,
.om-dialog .om-dialog-copy:focus-visible {
    border-color: var(--om-primary);
    color: var(--om-primary);
}

.om-dialog .modal-footer {
    border-top: 0;
    padding-top: 0;
}

a.text-bg-orange:hover,
a.text-bg-orange:focus {
    color: #fff;
    background-color: #c2410c;
}

/* ---------- 토스트 알림 ---------- */
/* 플래시 메시지는 화면 위에 떠서 사라진다. 문서 흐름에 넣으면 알림 유무에 따라
   본문이 위아래로 밀려 페이지가 흔들린다.
   흰 카드가 깔린 화면이라 토스트까지 흰색이면 또 하나의 카드로 읽힌다 —
   어두운 면으로 떨어뜨려 '떠 있는 알림'임을 색으로 먼저 알린다.
   위치가 우측 하단인 이유: 상단은 sticky 헤더의 저장·취소 버튼 자리라 토스트가 그 위를 덮는다. */
.om-toast-stack {
    position: fixed;
    bottom: 1rem;
    right: 1rem;
    z-index: 1090;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.5rem;
    max-width: calc(100vw - 2rem);
    pointer-events: none;
}

.om-toast {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    box-sizing: border-box;
    max-width: 27rem;
    padding: 0.9375rem 1rem 0.9375rem 1.0625rem;
    background: #161d2c;
    border: 1px solid rgba(255, 255, 255, .10);
    border-radius: 12px;
    box-shadow: 0 10px 28px rgba(11, 17, 29, .28);
    color: #fff;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.4;
    pointer-events: auto;
    animation: om-toast-in 0.22s cubic-bezier(.21, 1.02, .73, 1) both;
    transition: opacity 0.2s ease, transform 0.2s ease;
}

.om-toast.is-leaving {
    opacity: 0;
    transform: translateX(0.75rem);
}

.om-toast__icon {
    flex: 0 0 auto;
    font-size: 1.25rem;
    line-height: 1;
}

.om-toast--success .om-toast__icon { color: #34d399; }
.om-toast--error   .om-toast__icon { color: #fb7185; }

.om-toast__body {
    flex: 1 1 auto;
    min-width: 0;
    white-space: pre-line;
    word-break: break-word;
}

.om-toast__close {
    flex: 0 0 auto;
    margin-left: 0.375rem;
    padding: 0;
    width: 1.5rem;
    height: 1.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: rgba(255, 255, 255, .45);
    font-size: 1.0625rem;
    line-height: 1;
}

.om-toast__close:hover {
    background: rgba(255, 255, 255, .09);
    color: rgba(255, 255, 255, .85);
}

.om-toast__close:focus-visible {
    outline: 2px solid rgba(255, 255, 255, .6);
    outline-offset: 1px;
}

@keyframes om-toast-in {
    from { opacity: 0; transform: translateX(1rem) scale(.97); }
    to   { opacity: 1; transform: translateX(0) scale(1); }
}

@media (max-width: 575.98px) {
    .om-toast-stack {
        left: 1rem;
        align-items: stretch;
        max-width: none;
    }

    .om-toast { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
    .om-toast {
        animation: none;
        transition: opacity 0.2s ease;
    }

    .om-toast.is-leaving { transform: none; }
}

/* ---------- 목록 로딩 표시 ---------- */
/* "불러오는 중" 을 표 맨 아래 행에만 두면 목록이 길 때 화면 밖으로 밀려 보이지 않는다.
   바를 표 헤더 바로 아래에 붙인다 — 헤더는 스크롤 컨테이너 위에 sticky 로 고정돼 있어,
   헤더 높이(--om-thead-h)만큼 내려놓으면 목록을 스크롤해도 같은 자리에 남는다.
   헤더 높이는 글꼴·줄바꿈·화면 폭에 따라 달라지므로 list-ux.js 가 실측해 넣는다. */
.list-loading-host {
    position: relative;
}

.list-loading {
    position: absolute;
    top: var(--om-thead-h, 0px);
    left: 0;
    right: 0;
    z-index: 3;
    height: 3px;
    overflow: hidden;
    background: transparent;
    pointer-events: none;
}

.list-loading.is-active {
    background: var(--om-primary-weak);
}

.list-loading::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 40%;
    background: var(--om-primary);
    opacity: 0;
}

.list-loading.is-active::after {
    opacity: 1;
    animation: om-list-loading 1.1s ease-in-out infinite;
}

@keyframes om-list-loading {
    from { transform: translateX(-100%); }
    to   { transform: translateX(350%); }
}

@media (prefers-reduced-motion: reduce) {
    .list-loading.is-active::after {
        animation: none;
        width: 100%;
    }
}

/* ---------- 캠페인 동작 상태 패널 ---------- */
/* 항목 수는 7로 고정이다. 2칸·3칸 어느 쪽이든 마지막 하나가 혼자 남으므로 남은 폭을 전부 준다
   — 삽입 코드 확인은 URL 이 길어 좁은 칸에서 서너 줄로 접힌다. */
.om-health-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 8px;
}

@media (min-width: 768px) {
    .om-health-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
    .om-health-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.om-health-grid > .om-health-item:last-child {
    grid-column: 1 / -1;
}
/* 항목이 7개라 한 줄씩 세우면 화면을 길게 먹는다. 넓은 화면에서 두 칸으로 접되,
   칸 사이 경계가 없으면 어느 설명이 어느 항목 것인지 눈이 헷갈린다 → 옅은 배경으로 묶는다. */
.om-health-item {
    padding: 8px 10px;
    border-radius: var(--om-radius-sm);
    background: var(--om-bg);
}

.om-health-item > i {
    flex: none;
    margin-top: 2px;
    font-size: 1rem;
}

/* 막고 있는 항목만 눈에 먼저 들어와야 한다. 통과 항목까지 색을 주면 구분이 사라진다. */
.om-health-item--blocked {
    background: var(--om-danger-weak);
}

/* 긴 경로·URL 이 칸을 밀어 표가 가로로 늘어나는 것을 막는다. */
.om-health-item .min-w-0 {
    min-width: 0;
}

/* ---------- 정보 입력 폼(광고주 등록·수정) ---------- */

/* 칸 폭을 고정 열 수(3열)로 잡으면 넓은 화면에서 한 칸이 600px 까지 늘어나 값끼리 멀어진다.
   최소 폭만 정하고 들어가는 만큼 채운다 — 1900px 에서 6칸, 1400px 에서 4칸, 1000px 에서 3칸. */
.om-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 22px 26px;
}

/* 칸이 다섯 개를 넘으면 가로로 훑는 거리가 길어져 오히려 읽기 힘들다.
   넓은 화면에서는 칸을 더 넓히고 네 개로 끊는다. */
@container (min-width: 1390px) {
    .om-form-grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* 라벨과 값이 5px 붙어 있으면 한 항목이 아니라 줄무늬로 보인다.
   라벨은 뒤로 물리고(작게·옅게) 값은 앞으로 낸다(크게·진하게). */
.om-form-section .form-label {
    margin-bottom: 7px;
    font-size: .9rem;
    color: var(--om-faint);
}

.om-form-section .form-control,
.om-form-section .form-select {
    padding: .58rem .8rem;
    font-size: .95rem;
    color: var(--om-text);
    background-color: var(--om-surface);
    border-color: var(--om-border);
}

.om-form-section .form-select {
    padding-right: 2.5rem;
}

/* 읽기 전용 값(종료 시각 등)도 입력칸과 같은 자리에 놓인다 — 모양을 맞춘다. */
.om-form-section .form-control-plaintext {
    padding: .58rem .8rem;
    font-size: .95rem;
    color: var(--om-text);
    background: var(--om-surface);
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
}

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

/* 라벨 밑에 스위치만 덩그러니 놓이면 옆 칸 입력칸과 높이가 어긋나 혼자 떠 보인다.
   입력칸과 같은 판에 올려 한 줄로 정렬시킨다. */
/* om-value-box: 고칠 수 없는 항목의 현재 상태(배지 등)를 같은 자리·같은 높이로 보여준다. */
.om-form-section .om-switch-box,
.om-form-section .om-value-box {
    display: flex;
    align-items: center;
    min-height: 42px;
    padding: .3rem .8rem;
    background: var(--om-surface);
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
}

/* 켜고 끄는 스위치는 설명 오른쪽 끝에 둔다(설정 행의 통상 배치).
   Bootstrap 기본은 왼쪽 붙임이라 순서를 뒤집고 float·음수 여백을 걷어낸다. */
.om-form-section .om-switch-box .form-check {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    min-height: 0;
    margin: 0;
    padding-left: 0;
}

.om-form-section .om-switch-box .form-check-input {
    order: 2;
    flex: none;
    float: none;
    margin: 0;
    width: 2.35em;
    height: 1.2em;
}

.om-form-section .om-switch-box .form-check-label {
    order: 1;
    color: var(--om-text);
    font-size: .95rem;
}

/* 한 칸에 안 들어가는 주소·도메인만 두 칸을 쓴다. */
.om-form-grid > .om-span-2 {
    grid-column: span 2;
}

.om-file-pick {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    cursor: pointer;
}

.om-file-pick.is-empty {
    color: var(--om-faint);
}

.om-file-pick:hover:not(:disabled) {
    border-color: var(--om-border-strong);
}

/* 줄을 여기서 끊는다. 뒤 칸들을 묶어서 읽히게 할 때만 쓴다(예: 숨김 일수 3개를 한 줄에). */
.om-form-grid > .om-row-break {
    grid-column: 1 / -1;
    height: 0;
    margin: -11px 0;
}

/* 안내 문구처럼 칸에 넣을 수 없는 것은 줄 전체를 쓴다(좁아져도 유지). */
.om-form-grid > .om-span-full {
    grid-column: 1 / -1;
}

/* 열이 세 칸 이하로 접히면 두 칸짜리가 줄바꿈에서 밀려 빈 칸을 만든다.
   그 폭에서는 한 칸으로 되돌린다. 뷰포트가 아니라 박스 폭으로 재야 사이드바 접힘까지 맞는다. */
@container (max-width: 1389.98px) {
    .om-form-grid > .om-span-2 {
        grid-column: auto;
    }
}

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

    .om-form-grid > .om-span-2 {
        grid-column: auto;
    }
}

/* 흰 패널 위에 흰 박스를 얹으면 테두리가 있어도 구획이 눈에 잡히지 않는다.
   배경으로 묶고 테두리는 뺀다(입력칸이 흰색이라 대비는 그쪽에서 난다). */
.om-form-section {
    padding: 12px 0;
    container-type: inline-size;
}

/* 형제 선택자(+)로 간격을 주면 fieldset 안팎에 걸친 섹션 사이가 붙는다
   (캠페인 화면은 앞쪽만 fieldset 으로 묶여 있다). 각자 아래 여백을 갖게 한다. */
.om-form-section {
    margin-bottom: 18px;
}

/* 뒤에 아무것도 없는 마지막 섹션은 아래 여백이 필요 없다 — 패널 안쪽 여백이 이미 있다. */
.content-panel > .om-form-section:last-child,
.content-panel > fieldset:last-child > .om-form-section:last-child {
    margin-bottom: 0;
}

/* 제목이 본문과 같은 크기면 구분 역할을 못 한다. 크기 + 색 액센트로 먼저 읽히게 한다. */
.om-form-section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px;
    font-size: 1rem;
    font-weight: 600;
    color: var(--om-text);
}

.om-form-section-title::before {
    content: "";
    flex: none;
    width: 3px;
    height: 14px;
    border-radius: 2px;
    background: var(--om-primary);
}

/* 섹션이 아래 여백을 갖고 있으므로 위 여백은 두지 않는다. */
.om-form-meta {
    margin: 0;
    color: var(--om-faint);
    font-size: .8125rem;
}

.om-form-meta strong {
    color: var(--om-muted);
    font-weight: 600;
}

/* 켜야 의미가 생기는 설정은 꺼져 있을 때 자리를 비운다. 높이를 0fr↔1fr 로 바꿔 접고 편다. */
.om-collapse {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .22s ease;
}

.om-collapse > * {
    min-height: 0;
    overflow: hidden;
}

.om-collapse.is-open {
    grid-template-rows: 1fr;
}

/* 접혀서 내용이 없을 때는 제목만 남아야 한다 — 아래 여백이 그대로면 제목이 위로 쏠려 보인다. */
.om-form-section:has(> .om-collapse:not(.is-open)) {
    padding-bottom: 18px;
}

.om-form-section:has(> .om-collapse:not(.is-open)) > .om-form-section-title {
    margin-bottom: 0;
}

/* 다 열린 뒤에는 잘림을 풀어야 포커스 링·셀렉트 화살표가 온전히 보인다. */
.om-collapse.is-open > * {
    overflow: visible;
}

/* 청구 목록 — 한 건이 기간·상태(윗줄)와 결제 내역(아랫줄) 두 단으로 읽히게 한다. */
.om-invoice-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--om-border);
}

.om-invoice-row:first-child {
    padding-top: 0;
}

.om-invoice-row:last-child {
    padding-bottom: 0;
    border-bottom: 0;
}

.om-invoice-title {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--om-text);
    font-weight: 600;
}

/* 결제가 여러 건이면 줄바꿈으로 쌓인다 — 회차별로 한 줄씩 읽는 편이 빠르다. */
.om-invoice-lines {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 4px;
    color: var(--om-muted);
    font-size: .85rem;
}

.om-invoice-lines b {
    color: var(--om-text);
    font-weight: 600;
}

.om-invoice-row.is-overdue {
    padding-left: 10px;
    border-left: 3px solid var(--om-danger);
}

.om-overdue {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 12px;
    padding: 10px 12px;
    border: 1px solid var(--om-danger);
    border-left-width: 3px;
    border-radius: 8px;
    background: var(--om-danger-weak);
    font-size: .9rem;
}

.om-overdue-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--om-danger);
    font-weight: 700;
}

.om-overdue-summary {
    color: var(--om-text);
    font-weight: 600;
}

.om-overdue-due {
    color: var(--om-muted);
}

.om-overdue-link {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-left: auto;
    color: var(--om-danger);
    font-weight: 600;
    text-decoration: none;
}

.om-overdue-link:hover {
    text-decoration: underline;
}

/* 달력 좌우 이동은 값을 바꾸는 게 아니라 보기만 옮긴다 — 읽기 전용 화면에서도 눌려야 한다.
   Bootstrap 의 `fieldset:disabled .btn` 이 클래스로 걸려 요소를 바꿔도 pointer-events 가 막힌다. */
fieldset:disabled .om-nav-btn {
    pointer-events: auto;
    opacity: 1;
    color: var(--om-muted);
    background-color: var(--om-surface);
    border-color: var(--om-border-strong);
    cursor: pointer;
}

fieldset:disabled .om-nav-btn:hover {
    background-color: var(--om-bg);
    color: var(--om-text);
}

/* 아이콘 URL 은 값보다 결과(어떤 아이콘인지)가 먼저 읽혀야 한다 — 입력칸 앞에 썸네일을 붙인다. */
.om-icon-field {
    display: flex;
    align-items: center;
    gap: 10px;
}

.om-icon-field .form-control {
    min-width: 0;
}

.om-icon-preview {
    flex: none;
    width: 42px;
    height: 42px;
    border-radius: 10px;
    border: 1px solid var(--om-border);
    background: var(--om-surface);
    overflow: hidden;
}

.om-icon-preview.is-empty {
    background: repeating-linear-gradient(45deg, var(--om-bg), var(--om-bg) 5px, var(--om-surface) 5px, var(--om-surface) 10px);
}

.om-icon-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* 제목 옆 스위치 — 섹션 전체를 켜고 끄는 것이라 라벨 없이 스위치만 둔다. */
.om-form-section-title .form-check.form-switch {
    display: inline-flex;
    align-items: center;
    min-height: 0;
    margin: 0;
    padding: 0;
}

.om-form-section-title .form-check-input {
    float: none;
    margin: 0;
    width: 2.35em;
    height: 1.2em;
}

/* 칩 입력 — 값 하나하나를 삭제 가능한 알약으로 보여 준다(숨김 파라미터 키).
   콤마 구분 문자열은 어디까지가 한 항목인지 눈으로 세어야 해서 오타를 잡기 어렵다. */
.om-chip-input {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .375rem;
    min-height: calc(1.5em + .75rem + 2px);
    height: auto;
    cursor: text;
}

.om-chip-input .om-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .15rem .25rem .15rem .5rem;
    border-radius: 999px;
    background: var(--om-primary-weak);
    color: var(--om-primary);
    font-size: .8125rem;
    line-height: 1.4;
    white-space: nowrap;
}

.om-chip-input .om-chip button {
    border: 0;
    background: transparent;
    color: inherit;
    opacity: .55;
    line-height: 1;
    padding: 0 .15rem;
    cursor: pointer;
    font-size: 1rem;
}

.om-chip-input .om-chip button:hover,
.om-chip-input .om-chip button:focus-visible {
    opacity: 1;
}

/* 칩 사이에 끼어드는 입력칸 — 테두리 없이 남은 폭을 차지한다. */
.om-chip-input input {
    flex: 1 1 8ch;
    min-width: 8ch;
    border: 0;
    outline: 0;
    padding: 0;
    background: transparent;
    font: inherit;
    color: inherit;
}

/* ── 캠페인 UI 유형 / 버튼 유형 선택 카드 ─────────────────────────────
   값만 나열된 select 로는 각 유형의 화면 배치를 알 수 없어, 축소 도식과 실제
   규격 비율을 함께 보여준다. 색은 관리자 공통 팔레트(--om-*)를 쓴다. */
.om-choice-block {
    display: block;
}

.om-choice-set {
    border: 0;
    padding: 0;
    margin: 0 0 .6rem;
}

.om-choice-set:last-child {
    margin-bottom: 0;
}

.om-choice-legend {
    float: none;
    width: auto;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--om-primary);
    margin-bottom: .45rem;
}

.om-choice-hint {
    font-size: .75rem;
    font-weight: 400;
    color: var(--om-muted);
    margin-left: .4rem;
}

.om-choice-head {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    justify-content: space-between;
    align-items: center;
    margin-bottom: .5rem;
}

.om-choice-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .75rem;
}

@media (max-width: 575.98px) {
    .om-choice-grid {
        grid-template-columns: 1fr;
    }
}

.om-choice {
    display: block;
    margin: 0;
    cursor: pointer;
}

/* 라디오는 숨기되 포커스 링은 카드가 대신 받는다(키보드 이동을 잃지 않게). */
.om-choice input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.om-choice-inner {
    display: flex;
    align-items: center;
    gap: .7rem;
    height: 100%;
    padding: .7rem .8rem;
    border: 1px solid var(--om-border);
    border-radius: .5rem;
    background: var(--om-surface);
    transition: border-color .12s, background-color .12s;
}

.om-choice:hover .om-choice-inner {
    border-color: var(--om-border-strong);
}

.om-choice input:checked + .om-choice-inner {
    border-color: var(--om-primary);
    background: var(--om-primary-weak);
}

.om-choice input:focus-visible + .om-choice-inner {
    outline: 2px solid var(--om-primary);
    outline-offset: 2px;
}

.om-choice-txt {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    min-width: 0;
}

.om-choice-txt b {
    font-size: .8125rem;
    font-weight: 600;
    color: var(--om-text);
}

.om-choice-txt small {
    font-size: .6875rem;
    color: var(--om-muted);
    line-height: 1.4;
}

/* 도식 컬럼 — UI 유형(폰)과 버튼 유형(규격 박스)의 폭을 맞춰 텍스트 시작선을 정렬한다. */
.om-fig,
.om-btnfig {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 5.5rem;
    width: 5.5rem;
    height: 62px;
}

/* UI 유형 도식 — 폰 화면 안에서 유도 UI 가 앉는 위치를 보여준다. */
.om-fig-phone {
    position: relative;
    width: 40px;
    height: 62px;
    border: 1px solid var(--om-border);
    border-radius: 6px;
    background: var(--om-surface);
    overflow: hidden;
}

/* 페이지 콘텐츠 흉내 — 유도 UI 가 페이지 위에 얹힌다는 걸 알아보게 한다. */
.om-fig-phone::before {
    content: "";
    position: absolute;
    left: 5px;
    right: 5px;
    top: 6px;
    height: 15px;
    border-radius: 3px;
    background: var(--om-bg);
}

.om-fig-phone::after {
    content: "";
    position: absolute;
    left: 5px;
    right: 12px;
    top: 25px;
    height: 4px;
    border-radius: 2px;
    background: var(--om-bg);
}

.om-fig-phone > span {
    position: absolute;
    background: var(--om-primary);
    border-radius: 3px;
    z-index: 1;
}

.om-fig-sheet {
    left: 3px;
    right: 3px;
    bottom: 3px;
    height: 18px;
}

/* .om-fig-phone > span 의 border-radius 를 이기도록 특이성을 맞춘다. */
.om-fig-phone > .om-fig-float {
    right: 5px;
    bottom: 5px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
}

/* 간단형은 안내 삽화가 없어 카드가 낮다 — 도식에서도 얇게 그린다. */
.om-fig-phone > .om-fig-sheet.is-simple {
    height: 11px;
}

/* 인라인 배너는 실제로 문서 흐름의 맨 끝에 붙는다 — 도식도 화면 밖 아래로 걸치게 둔다. */
.om-fig-inline {
    left: 3px;
    right: 3px;
    bottom: -11px;
    height: 18px;
    opacity: .5;
}

/* 버튼 유형 도식 — 네이버가 그리는 버튼 모양을 규격 비율(0.38배)로 줄여 그린다.
   빈 사각형만으로는 무엇의 크기인지 읽히지 않아, 초록 N 과 문구 자리를 함께 넣는다. */
.om-btnfig-box {
    display: flex;
    align-items: center;
    gap: 3px;
    padding: 0 3px;
    border: 1px solid var(--om-border);
    border-radius: 3px;
    background: var(--om-surface);
}

.om-btnfig-box.is-w2 {
    width: 84px;
    height: 16px;
}

.om-btnfig-box.is-w1 {
    width: 34px;
    height: 10px;
    padding: 0 2px;
}

/* T1 은 W1 과 크기가 같고 배경만 회색이다(네이버 가이드 원본 기준). */
.om-btnfig-box.is-t1 {
    width: 34px;
    height: 10px;
    padding: 0 2px;
    background: #ededed;
    border-color: #ededed;
}

/* 네이버 초록은 브랜드 색이라 관리자 팔레트로 바꾸지 않는다. */
.om-btnfig-n {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: #03c75a;
    color: #fff;
    font-size: 7px;
    font-weight: 800;
    font-style: normal;
    line-height: 1;
}

.om-btnfig-box.is-w1 .om-btnfig-n,
.om-btnfig-box.is-t1 .om-btnfig-n {
    width: 7px;
    height: 7px;
    font-size: 4px;
    border-radius: 1px;
}

/* 버튼 안 문구 자리. */
.om-btnfig-line {
    flex: 1 1 auto;
    height: 4px;
    border-radius: 2px;
    background: var(--om-bg);
}

.om-choice input:checked + .om-choice-inner .om-btnfig-box {
    border-color: var(--om-primary);
}

/* ── 실물 미리보기 모달 ──────────────────────────────────────────────
   위에서 아래로 읽는 순서: 무엇을 볼지 고르고(컨트롤) → 결과를 보고(폰) → 설명을 읽는다. */
.om-preview-controls {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    margin-bottom: 1rem;
}

.om-preview-group {
    flex: 1 1 15rem;
    min-width: 0;
}

.om-preview-group .form-label {
    margin-bottom: .3rem;
}

/* 버튼을 붙이지 않고 띄운다 — 맞붙이면 hover/선택 시 인접 테두리가 겹쳐 지저분해진다. */
.om-preview-seg {
    display: flex;
    gap: .25rem;
}

.om-seg-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: .3rem .5rem;
    border: 1px solid var(--om-border);
    border-radius: .4rem;
    background: var(--om-surface);
    color: var(--om-muted);
    font: inherit;
    font-size: .78rem;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color .12s, background-color .12s, color .12s;
}

.om-seg-btn:hover {
    border-color: var(--om-border-strong);
    color: var(--om-text);
}

.om-seg-btn.active {
    border-color: var(--om-primary);
    background: var(--om-primary-weak);
    color: var(--om-primary);
    font-weight: 600;
}

.om-seg-btn:focus-visible {
    outline: 2px solid var(--om-primary);
    outline-offset: 1px;
}

.om-preview-stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
}

/* 임베드 스타일은 fixed 를 쓰므로 iframe 뷰포트 안에서만 실제와 같게 앉는다. */
.om-preview-phone {
    width: 360px;
    max-width: 100%;
    height: 520px;
    border: 8px solid var(--om-text);
    border-radius: 22px;
    overflow: hidden;
    background: var(--om-surface);
}

.om-preview-phone iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

.om-preview-caption {
    font-size: .75rem;
    color: var(--om-muted);
}

.om-preview-note {
    margin: 1rem 0 0;
    text-align: center;
    color: var(--om-muted);
}

/* 좁은 창에서는 폰을 줄여 모달이 화면을 넘기지 않게 한다. */
@media (max-width: 767.98px) {
    .om-preview-group {
        flex: 1 1 100%;
    }

    .om-preview-phone {
        height: 440px;
    }
}

/* 긴 안내 문구를 문장 단위로 끊는다. 줄이 모자라면 문장이 통째로 다음 줄로 넘어가고,
   남으면 다음 문장이 같은 줄에 이어 붙는다(문장 중간에서 끊기지 않게). */
.om-sentences > span {
    display: inline-block;
}

/* 요일·시간대 노출 스케줄 그리드(7×24).
   칸이 168개라 테두리를 칸마다 그리면 선이 겹쳐 두꺼워진다 — collapse 로 한 겹만 남긴다. */
.om-schedule-grid {
    border-collapse: collapse;
    width: 100%;
    min-width: 420px;
    table-layout: fixed;
    user-select: none;
}

.om-schedule-grid th,
.om-schedule-grid td {
    border: 1px solid var(--om-border);
    text-align: center;
}

.om-schedule-grid thead th {
    padding: .4rem .25rem;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--om-text);
    background: var(--om-surface-2);
    cursor: pointer;
}

.om-schedule-grid thead th.is-sat { color: #2563eb; }
.om-schedule-grid thead th.is-sun { color: #dc2626; }
.om-schedule-grid thead th:hover { background: var(--om-primary-weak); }

.om-schedule-corner {
    width: 4.5rem;
    cursor: default !important;
}

.om-schedule-hour {
    width: 4.5rem;
    padding: 0 .35rem;
    font-size: .75rem;
    font-weight: 500;
    color: var(--om-faint);
    background: var(--om-surface-2);
    text-align: right;
    cursor: pointer;
}

.om-schedule-hour:hover { background: var(--om-primary-weak); color: var(--om-primary); }

/* 칸 높이는 24행이 한 화면에 들어오도록 낮게 잡는다. */
.om-schedule-grid tbody td {
    height: 1.35rem;
    cursor: pointer;
    background: var(--om-surface);
    transition: background-color .08s ease;
}

.om-schedule-grid tbody td:hover { background: var(--om-primary-weak); }
.om-schedule-grid tbody td.is-on { background: var(--om-primary); }
.om-schedule-grid tbody td.is-on:hover { background: #2f49ad; }

.om-schedule-legend {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: .8125rem;
    color: var(--om-faint);
}

.om-schedule-legend i {
    width: .85rem;
    height: .85rem;
    border: 1px solid var(--om-border);
    background: var(--om-surface);
    display: inline-block;
}

.om-schedule-legend i.is-on { background: var(--om-primary); border-color: var(--om-primary); }

/* 폼에는 요약만 둔다 — 그리드를 펼쳐 두면 24행이 화면 절반을 차지한다. */
.om-schedule-preview {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    min-height: calc(1.5em + .75rem + 2px);
    height: auto;
    background: var(--om-surface);
    cursor: default;
}

.om-schedule-slotchip {
    display: inline-flex;
    align-items: baseline;
    gap: .3rem;
    padding: .1rem .5rem;
    border-radius: 999px;
    font-size: .8125rem;
    line-height: 1.5;
    color: var(--om-primary);
    background: var(--om-primary-weak);
}

.om-schedule-slotchip b { font-weight: 600; }


/* 상세(읽기 전용) 화면에서도 「실물 미리보기」·「달력 보기」·「시간대 설정」은 열려야 한다 —
   값을 바꾸는 버튼이 아니라 보기만 여는 버튼이고, 애초에 눈으로 확인하려고 만든 화면이다.
   fieldset disabled 가 button 요소를 통째로 비활성화하므로 마크업은 span + role=button 을 쓰고,
   Bootstrap 의 `fieldset:disabled .btn` 이 클래스로 pointer-events 를 막는 것은 여기서 되살린다. */
fieldset:disabled .om-preview-open,
fieldset:disabled .om-viewer-open {
    pointer-events: auto;
    opacity: 1;
    cursor: pointer;
}

fieldset:disabled .om-preview-open {
    color: var(--om-muted);
    background-color: var(--om-surface);
    border-color: var(--om-border-strong);
}

fieldset:disabled .om-preview-open:hover {
    background-color: var(--om-bg);
    color: var(--om-text);
}

/* span 은 button 과 달리 flex item 으로 늘어나도 내부 텍스트가 위에 붙는다(실측: 위 6px / 아래 15px).
   input-group 안에서 옆 입력칸과 글자 높이가 어긋나 보이므로 중앙에 맞춘다. */
.om-preview-open,
.om-viewer-open {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* 읽기 전용 그리드는 눌러도 아무 일이 없으므로 커서·호버로 그렇게 보이게 한다. */
.om-schedule-readonly .om-schedule-grid th,
.om-schedule-readonly .om-schedule-grid td { cursor: default; }

.om-schedule-readonly .om-schedule-grid tbody td:hover { background: var(--om-surface); }
.om-schedule-readonly .om-schedule-grid tbody td.is-on:hover { background: var(--om-primary); }
.om-schedule-readonly .om-schedule-grid thead th:hover { background: var(--om-surface-2); }
.om-schedule-readonly .om-schedule-hour:hover { background: var(--om-surface-2); color: var(--om-faint); }

/* 파라미터 조건의 제외/포함 선택. 칩 입력이 남은 폭을 다 쓰도록 셀렉트는 고정 폭으로 둔다. */
.om-param-mode {
    flex: 0 0 auto;
    width: auto;
    min-width: 6.5rem;
}

/* ---------- 상품 · 요금 안내 모달 ---------- */
/* 카드사 심사가 「결제되는 금액이 어떤 금액인지」를 결제 화면 안에서 확인한다.
   소개 사이트의 요금 카드와 같은 구성으로 둔다 — 금액을 고칠 때는 양쪽을 함께 고친다. */
.product-plan {
    padding: 20px;
    border: 1px solid var(--om-border);
    border-radius: var(--om-radius-sm);
    background: var(--om-surface-2);
}

.product-plan-name {
    margin-bottom: 4px;
    color: var(--om-primary);
    font-size: 0.8125rem;
    font-weight: 600;
}

.product-plan-price {
    margin-bottom: 2px;
    color: var(--om-text);
    font-size: 1.5rem;
    font-weight: 700;
}

.product-plan-detail {
    margin-bottom: 14px;
    color: var(--om-faint);
    font-size: 0.8125rem;
}

.product-plan-points {
    margin: 0;
    padding: 14px 0 0;
    border-top: 1px solid var(--om-border);
    list-style: none;
    color: var(--om-muted);
    font-size: 0.8125rem;
}

.product-plan-points li + li {
    margin-top: 6px;
}
