/* 어드민 공통 스타일 — 운영자 대상 관리 화면(조밀한 표·폼 위주).
 *
 * 색·타이포·반경은 서비스 레포 DESIGN.md 의 **classic 관리 톤**(--bg/--text1/--accent)을 기준으로 한다.
 * 파스텔 home-tone(--hc-*)은 모바일 사용자 화면 전용이라 여기선 쓰지 않는다 — DESIGN.md §0 이 두 톤을
 * 섞지 말라고 못 박아 뒀고, 관리 화면은 classic 쪽이다.
 *
 * ⚠️ 서비스의 common.css 를 복사해 오지 않는다. 값만 여기 자체 정의한다 — 정본 CSS 를 복붙하면 곧 drift 한다.
 *    DESIGN.md 가 바뀌면 이 :root 블록만 따라 고치면 된다.
 *
 * 신규 값은 --ground 하나뿐이다. classic 라이트는 bg 와 bg2 가 둘 다 흰색이라 카드가 배경에서 안 뜨는데,
 * classic 다크는 이미 bg #161310 / bg2 #211C17 로 분리돼 있다. 라이트만 비어 있는 자리라 --border 계열에서 뽑았다.
 *
 * 작성 규칙은 서비스 레포 doc/ai/frontend/css/convention.md 를 따른다
 * (속성 5개 이하 한 줄 · 6개 이상 콜론 정렬 · 그룹 { } 열 정렬 · 속성 6그룹 순서 · hover 는 파일 끝 한 블록).
 */

/* ===== 토큰 ===== */

:root {
    /* ── 서비스 홈톤 통일(2026-08-28) — 사용자 페이지(common.css·tokens.css)와 색·여백·border 를 맞춘다.
       정본: 서비스 레포 tokens.css(--brand-coral #F0784F 등)·common.css --hc-* 계열. 값이 갈리면 서비스가 정본. */
    --accent      : #F0784F;  /* --brand-coral */
    --accent-hover: #E0654A;  /* 코랄 진한 단계(--grad-week-sel 끝값) */
    --accent-soft : rgba(240, 120, 79, .10);  /* --hc-active — 활성 메뉴·칩 틴트 */
    --accent-on   : #FFFFFF;
    --accent-ink  : #D2704D;  /* --brand-coral-ink — 코랄 '글자'용(솔리드 코랄은 글자로 쓰면 연하다) */

    --ground    : #FDFBF8;  /* --hc-page — 콘텐츠 캔버스(연웜 화이트) */
    --card      : #FFFFFF;  /* --hc-card */
    --card-hover: #FCF8F3;  /* --hc-row */
    --soft      : #F4EEE6;  /* --hc-soft — 칩·연한 강조 배경 */
    --soft-hover: #ECE2D5;

    --line : #F4EFF6;  /* --hc-line — 카드 테두리·구분선 */
    --line2: #ECE7DD;  /* 인풋·진한 경계용 */

    /* ★ 정본 교정(2026-09-02): 서비스 body 잉크는 --hc-text(#3A3A4A)가 아니라 --text1(#241E18)이다.
       --hc-* 는 서비스의 홈/커뮤니티 절 전용 토큰인데 그걸 전체 본문 정본으로 잘못 삼아
       어드민 전체가 흐려 보였다("전체적으로 흐려 보인다" 실피드백). 서비스 classic 톤과 동일 값. */
    --t1: #241E18;  /* 서비스 --text1 — 본문·라벨·값 */
    --t2: #615648;  /* 서비스 --text2 — 보조(시각·단위·부가) */
    --t3: #8C8073;  /* 서비스 --text3 — placeholder·비활성 */

    --ok  : #2F7A41;  --ok-bg  : #E7F4E9;
    --bad : #C32F35;  --bad-bg : #FBEAEB;
    --warn: #985A06;  --warn-bg: #FBF1DD;
    --info: #2E6FD6;  --info-bg: #E8F0FB;
    --pur : #6B45BE;  --pur-bg : #F0EAFA;
    --teal: #0C766B;  --teal-bg: #E1F3F0;

    --ink   : #241E18;  /* 인증 화면 좌측 브랜드 패널·잠긴 썸네일(웜 잉크) */
    --ink-on: #F6F1E8;

    --shadow   : 0 3px 12px rgba(74, 52, 30, .07), 0 1px 3px rgba(74, 52, 30, .04);  /* 서비스 --shadow-soft */
    --shadow-md: 0 8px 24px rgba(74, 52, 30, .08), 0 2px 4px rgba(74, 52, 30, .05);
    --shadow-accent: 0 4px 10px rgba(240, 120, 79, .30);  /* 선택된 필터 알약 글로우 */

    --radius     : 18px;  /* 서비스 리스트 카드 radius — 카드·패널·리스트 컨테이너 */
    --radius-sm  : 8px;   /* 작은 부품(썸네일·태그·세그먼트) — 서비스 아이콘 타일 9~11px 계열 */
    --radius-pill: 999px;

    /* 인증 화면 — 서비스 레포 정본을 그대로 가져온다(tokens.css · login.css). 로그인은 두 제품이 맞닿는 화면이라
       여기서만 home-tone 을 허용한다. 관리 화면 본체는 classic 톤 그대로다(§0 두 톤 분리 원칙).
       ⚠ 서비스가 라이트·다크 공통으로 두는 값이라 여기서도 다크에서 재정의하지 않는다. */
    --grad-btn       : linear-gradient(120deg, #FBB88F, #F09168);                              /* tokens.css:18 연코랄 버튼(로그인·체크칩) */
    --shadow-btn     : 0 10px 20px rgba(224, 101, 74, .18);                                    /* tokens.css:19 코랄 버튼 그림자 */
    --grad-add-btn   : linear-gradient(135deg, #FFE9DA, #F9C9AF);                              /* tokens.css:20 추가 버튼(soft 코랄) — c-button[kind=add] 공용 */
    --shadow-add-btn : 0 4px 12px rgba(230, 145, 105, .24);                                    /* tokens.css:21 추가 버튼 그림자 */
    --add-btn-ink    : #3A3A4A;                                                                /* tokens.css:34 --hc-text — kind=add 글자색(라이트 정본) */
    --radius-btn     : 14px;                                                                   /* tokens.css:44 버튼 공통 모서리 */
    --grad-auth-panel: linear-gradient(155deg, #FFE3D2 0%, #FBD4C4 52%, #F6E4D0 100%);         /* login.css:32 인증 브랜드 패널 */
    --auth-ink       : #3B2E25;                                                                /* login.css:33 패널 진한 텍스트 */
    --auth-title     : #33291F;                                                                /* login.css:35 패널 제목 */
    --brand-coral-ink: #D2704D;                                                                /* tokens.css 링크·강조 잉크 */

    /* 8단계 — 매직넘버 금지. 새 크기가 필요하면 여기에 의미명으로 먼저 추가한다. */
    --text-xs   : 12px;
    --text-sm   : 13px;
    --text-md   : 14px;
    --text-lg   : 15px;
    --text-xl   : 16px;
    --text-2xl  : 17px;
    --text-3xl  : 20px;
    --text-title: 22px;

    --font: 'Pretendard', 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, -apple-system, sans-serif;
    --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    /* 차트 막대 — 코랄 한 색의 농도 세 단(옅음 → 중간 → 진함=accent). 회색 막대에 코랄 강조만 얹던
       이전 조합은 두 색이 섞여 탁했다. 한 색의 농도면 '어디를 봐야 하냐'가 그대로 남고 톤은 하나가 된다. */
    --bar-lo : rgba(240, 120, 79, .16);
    --bar-mid: rgba(240, 120, 79, .42);

    --side-width : 244px;  /* 서비스 데스크탑 사이드바 폭 */
    --head-height: 64px;
}

/* 다크 — DESIGN.md classic 다크 값 그대로. 새 색 변수는 라이트·다크 양쪽 정의가 원칙이다.

   ★ 미디어쿼리가 아니라 data-theme 속성으로 건다. 사이드바 토글이 시스템 설정을 이길 수 있어야 하기 때문이다.
     "시스템" 선택은 head 스크립트가 matchMedia 로 해석해서 data-theme 을 심는다 — 그래야 다크 팔레트를
     여기 한 벌만 두면 된다(미디어쿼리와 속성 선택자 양쪽에 두면 색을 추가할 때 두 곳을 고쳐야 하고 곧 어긋난다). */
:root[data-theme="dark"] {
    /* 서비스 홈톤 통일(2026-08-28) — common.css [data-theme="dark"] 의 --hc-* 값 그대로 */
    --accent      : #F0A579;  /* --hc-sel — 다크 코랄 강조 */
    --accent-hover: #F5B78F;
    --accent-soft : rgba(240, 130, 100, .20);  /* --hc-active 다크 */
    --accent-on   : #231811;
    --accent-ink  : #F0A579;  /* 다크는 강조 잉크도 --hc-sel 하나로 */

    --ground    : #1A1611;  /* --hc-page */
    --card      : #211C17;  /* --hc-card */
    --card-hover: #2A241E;  /* --hc-row */
    --soft      : #2A241E;  /* --hc-soft 다크 */
    --soft-hover: #332B23;

    --line : #2E2820;  /* --hc-line */
    --line2: #342D24;

    --t1: #ECE5DC;  /* 서비스 다크 --text1 */
    --t2: #AB9E8E;  /* 서비스 다크 --text2 */
    --t3: #776B5D;  /* 서비스 다크 --text3 */

    --ok  : #54C281;  --ok-bg  : #15322A;
    --bad : #F1797E;  --bad-bg : #3A2022;
    --warn: #E5A33C;  --warn-bg: #382912;
    --info: #6BA6F0;  --info-bg: #16263F;
    --pur : #AE94EE;  --pur-bg : #272147;
    --teal: #34C8B6;  --teal-bg: #0F3834;

    --ink   : #0E0C09;
    --ink-on: #ECE5DC;

    --bar-lo : rgba(240, 165, 121, .18);  /* 다크 코랄(--accent) 기준 농도 세 단 */
    --bar-mid: rgba(240, 165, 121, .45);

    /* 서비스 다크 관례 — 카드는 그림자를 끄고 선만 남긴다. 떠야 하는 것(모달·라이트박스)만 검정 그림자 */
    --shadow   : none;
    --shadow-md: 0 8px 24px rgba(0, 0, 0, .50), 0 2px 6px rgba(0, 0, 0, .35);
    --shadow-accent: 0 4px 12px rgba(240, 165, 121, .25);
}

/* ===== 기본 ===== */

* { box-sizing: border-box; }

/* 세로 flex — 경고 띠(.warn_bar)가 있어도 띠 + .shell 이 정확히 뷰포트를 채운다.
   페이지(브라우저) 스크롤은 만들지 않는 게 원칙이고, 스크롤은 .main 안에서만 생긴다.
   ★ min-height 가 아니라 height 다. min 이면 본문이 길어질 때 body 가 내용만큼 늘어나 문서
     스크롤이 생기고 사이드바가 같이 밀려 올라간다(실측 — 대시보드에서 그랬다). height 로 가두면
     .shell 이 뷰포트에 갇혀 .main 만 스크롤한다. 셸 없는 화면(인증)은 body 를 넘치면
     문서 스크롤이 그대로 생기므로 작은 창에서도 잘리지 않는다. */
body {
    display       : flex;
    flex-direction: column;
    margin        : 0;
    height        : 100vh;
    background    : var(--ground);
    color         : var(--t1);
    font-family   : var(--font);
    font-size     : var(--text-lg);
    line-height   : 1.5;
    letter-spacing: -.1px;
}

h1, h2, h3, p, ol, ul { margin: 0; }
a                     { color: var(--accent); }
:focus-visible        { outline: 2px solid var(--accent); outline-offset: 2px; }
input                 { accent-color: var(--accent); }  /* 라디오 등 — 브라우저 기본 파랑 금지(체크박스는 아래 커스텀) */
.ph                   { line-height: 1; }  /* 아이콘은 Phosphor 정본(/plugin/phosphor) — 크기만 사용처에서 */

/* ===== 워드마크 ===== */

/* 마크는 서비스 로고 이미지다(겹친 원 + 하트). 예전엔 액센트 박스에 글자 'M' 을 넣은 자리표시자였는데,
   같은 서비스인데 어드민만 다른 마크를 쓰면 운영자가 다른 제품으로 인식한다. */
.wordmark        { display: flex; align-items: center; gap: 9px;                              }
.wordmark img    { display: block; width: 30px; height: 30px; object-fit: contain;            }
.wordmark strong { font-size: var(--text-3xl); font-weight: 700; letter-spacing: -.4px;       }

/* ===== 인증 화면 =====
   좌우 분할 — 왼쪽 잉크 패널에 접속 환경을 박는다. 운영 데이터를 직접 건드리는 화면이라
   운영/로컬을 헷갈린 채 로그인하는 것부터 막는 게 목적이다. */

.auth_wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 24px; }

.auth_split {
    display      : flex;
    width        : 100%;
    max-width    : 990px;
    min-height   : 520px;
    border       : 1px solid var(--line);
    border-radius: var(--radius);
    overflow     : hidden;
    background   : var(--card);
    box-shadow   : var(--shadow-md);
}

.brandpane {
    flex           : 0 0 452px;
    display        : flex;
    flex-direction : column;
    justify-content: space-between;
    padding        : 34px 32px;
    background     : var(--grad-auth-panel);
    color          : var(--auth-ink);
}

.brandpane .wordmark strong { color: var(--auth-ink); }

.brandpane h1 {
    margin-top    : 30px;
    color         : var(--auth-title);
    font-size     : 21px;  /* 인증 히어로 — 1회성 (스케일 밖). 한 줄에 떨어지는 크기다 */
    font-weight   : 600;
    line-height   : 1.35;
    letter-spacing: -.6px;
    white-space   : nowrap;  /* 헤드라인은 한 줄 — 두 줄로 접히면 패널 리듬이 무너진다 */
}

.brandpane p { margin-top: 10px; color: var(--auth-ink); font-size: var(--text-md); line-height: 1.6; white-space: nowrap; }  /* 리드도 한 줄 — 패널 폭을 여기에 맞춰 452px 로 잡았다 */

/* 권한 브리핑 — 로그인 전에 이 계정이 무엇을 할 수 있는지 먼저 읽힌다.
   불릿만 코랄 잉크로 빼서 글이 한 덩어리로 읽히는 걸 막는다. */
.brief    { margin-top: 26px; padding: 0; list-style: none;                                        }
.brief li { display: flex; gap: 9px; padding: 7px 0; color: var(--auth-ink); font-size: var(--text-md); }
.brief em { flex: 0 0 auto; width: 5px; height: 5px; margin-top: 7px; border-radius: var(--radius-pill); background: var(--brand-coral-ink); }

.envtag {
    display     : inline-flex;
    align-items : center;
    align-self  : flex-start;
    gap         : 7px;
    padding     : 4px 11px;
    border      : 1px solid rgba(59, 46, 37, .18);
    border-radius: var(--radius-pill);
    color       : var(--auth-ink);
    font-size   : var(--text-sm);
    font-weight : 600;
}

.envtag em      { width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--warn); font-style: normal; }
.envtag.prod em { background: var(--ok);                                                                                  }

.formpane {
    flex           : 1;
    display        : flex;
    flex-direction : column;
    justify-content: center;
    min-width      : 0;
    padding        : 32px 40px;
}

.formpane h2    { margin-bottom: 20px; font-size: var(--text-3xl); font-weight: 600;      }
.formpane .desc { margin: -14px 0 20px; color: var(--t2); font-size: var(--text-md);      }

/* 인증 CTA — 서비스 c-button kind=cta size=lg 규격. 색은 .btn.cta 가 주고 여기선 size=lg 치수만 얹는다.
   ★ 전역 .btn 은 건드리지 않는다 — 관리 화면 본체 버튼까지 그라데이션이 되면 톤이 통째로 바뀐다. */
.formpane .btn {
    height   : 50px;
    font-size: var(--text-xl);
}

.formpane .foot_note { margin-top: 18px; color: var(--t2); font-size: var(--text-sm); line-height: 1.6; }

/* 좁은 화면에선 브랜드 패널을 접는다 — 어드민은 데스크탑이 기본이지만 태블릿 대응은 해 둔다. */
@media (max-width: 720px) {
    .auth_split   { flex-direction: column; min-height: 0;                    }
    .brandpane    { flex: 0 0 auto; gap: 18px; padding: 22px 24px;            }
    .brandpane h1 { margin-top: 14px; font-size: var(--text-3xl); white-space: normal; }
    .brief        { display: none;                                            }
    .brandpane p  { display: none;                                            }
    .formpane     { padding: 24px;                                            }
}

/* ===== 폼 ===== */

.field       { margin-bottom: 14px;                                                                    }
.field label { display: block; margin-bottom: 6px; color: var(--t2); font-size: var(--text-md); font-weight: 600; }

/* number·search 도 함께 받는다 — 빠지면 그 입력만 브라우저 기본 모양으로 튄다(매출 수수료율이 그랬다) */
.field input,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
select,
textarea {
    width        : 100%;
    height       : 38px;
    padding      : 0 11px;
    border       : 1px solid var(--line2);
    border-radius: var(--radius-sm);
    background   : var(--card);
    color        : var(--t1);
    font-size    : var(--text-lg);
    font-family  : inherit;
    transition   : border-color .15s;
}

.field input::placeholder,
input[type="text"]::placeholder { color: var(--t3);                                                     }

.field input:focus,
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
select:focus,
textarea:focus { border-color: var(--accent); outline: none; }  /* 포커스 정본 = .msearch input:focus — 테두리만 코랄, 링 없음 */

/* 체크박스 — 서비스 정본 .chk_box(pregnancy/index.ftl:999) 수치 그대로 옮긴다:
   20px · 2px 테두리 · radius 6 · 11px/600 흰 ✓.
   ★ 채움 상태는 border-color: transparent 가 아니라 border: 0 이다 — 투명 테두리 위에 그라데이션을
     깔면 둥근 모서리에서 테두리 띠와 안쪽이 따로 칠해져 경계선이 비친다(서비스가 이미 겪고 고친 것).
     box-sizing 이 border-box 라 크기는 그대로다.
   ★ font-family: inherit 필수 — input 은 font-family 를 상속하지 않아 빼면 ✓ 가 브라우저 기본 폼
     글꼴로 그려져 서비스(span 이라 Pretendard 를 상속)와 모양이 갈린다.
   ★ ✓ 는 서비스가 ::before 지만 여기선 ::after 다 — .ftgl 토글이 ::after { content: "" } 로 덮어써야
     토글 손잡이에 ✓ 가 새지 않는다(::before 로 두면 토글에도 붙는다). */
input[type="checkbox"] {
    appearance     : none;
    display        : flex;
    align-items    : center;
    justify-content: center;
    flex-shrink    : 0;
    width          : 20px;
    height         : 20px;
    margin         : 0;
    border         : 2px solid var(--line2);
    border-radius  : 6px;
    background     : var(--card);
    color          : #FFF;
    font-size      : 11px;
    font-weight    : 600;
    font-family    : inherit;
    cursor         : pointer;
}

input[type="checkbox"]:checked        { border: 0; background: var(--grad-btn); }
input[type="checkbox"]:checked::after { content: "✓"; }

/* c-select — 서비스 컴포넌트(/js/select.js). ShadowDOM 변수를 어드민 토큰으로 매핑 */
c-select {
    --text1             : var(--t1);
    --text3             : var(--t3);
    --border            : var(--line2);
    --bg2               : var(--card);
    --accent-light      : var(--accent-soft);
    --select-selected-bg: var(--accent-soft);
    --select-selected-fg: var(--accent-ink);

    font-size: var(--text-md);  /* ShadowDOM 안 .button 이 font: inherit — 폼 input 과 같은 자로 내림 */
}

c-select::part(search) { display: none; }  /* 어드민 셀렉트는 항목이 적다 — 검색칸 불필요 */

.field input.code {
    height        : 52px;
    color         : var(--t1);
    font-size     : 22px;  /* OTP 6자리 — 1회성 (스케일 밖) */
    font-weight   : 700;
    font-family   : var(--mono);
    letter-spacing: 9px;
    text-align    : center;
}

/* 전역 a 는 색만 주고 밑줄은 사용처가 끈다 — a 로 만든 버튼(추가·목록·취소 등 6곳)은 여기서 끈다 */
.btn {
    display        : grid;
    place-items    : center;
    width          : 100%;
    height         : 40px;
    border         : 0;
    border-radius  : var(--radius-sm);
    background     : var(--accent);
    color          : var(--accent-on);
    font-size      : var(--text-lg);
    font-weight    : 600;
    font-family    : inherit;
    text-decoration: none;
    cursor         : pointer;
}

/* ===== 알림 ===== */

.msg_error,
.msg_ok    { margin-bottom: 14px; padding: 9px 12px; border-radius: var(--radius-sm); font-size: var(--text-md); font-weight: 500; }
.msg_error { background: var(--bad-bg); color: var(--bad);                                                                        }
.msg_ok    { background: var(--ok-bg);  color: var(--ok);                                                                         }

/* 관문 화면 탈출구 — 비밀번호 변경·2FA 관문에 갇히지 않게 항상 노출한다.
   필터는 /logout 을 상시 허용하지만 화면에 버튼이 없으면 쓸 수가 없다. */
.gate_out        { margin-top: 16px; text-align: center;                                                                                          }
.gate_out button { padding: 0; border: 0; background: none; color: var(--t3); font-size: var(--text-md); font-family: inherit; text-decoration: underline; cursor: pointer; }

/* ===== 2FA 등록 ===== */

.secret_box {
    margin-bottom : 14px;
    padding       : 11px;
    border        : 1px dashed var(--line2);
    border-radius : var(--radius-sm);
    background    : var(--card-hover);
    font-size     : var(--text-lg);
    font-family   : var(--mono);
    letter-spacing: 1px;
    word-break    : break-all;
}

.steps    { margin: 0 0 18px; padding-left: 17px; color: var(--t2); font-size: var(--text-md); }
.steps li { margin-bottom: 3px;                                                                }

/* ===== 앱 셸 ===== */

/* 뷰포트 고정 셸 — body(세로 flex)의 남은 높이를 정확히 채우고 자기는 안 넘친다(overflow hidden).
   스크롤은 .main 내부에서만 생기고, 사이드바는 본문이 아무리 길어도 항상 그대로 붙어 있다. */
.shell { flex: 1; display: flex; min-height: 0; overflow: hidden; }

/* 사이드바 — 셸이 뷰포트에 고정되므로 sticky 없이도 안 움직인다.
   메뉴가 화면보다 길어지는 예외 상황만 자체 스크롤로 받는다. */
/* 서비스와 같은 구조 — 흰 카드 사이드바 + --line 우측선. 서비스 팔레트는 카드/바닥 색차가
   미세해서(#FFF vs #FDFBF8) 경계가 면 대비가 아니라 얇은 선 하나로만 읽힌다. */
.side {
    flex          : 0 0 var(--side-width);
    display       : flex;
    flex-direction: column;
    overflow-y    : auto;
    border-right  : 1px solid var(--line);
    background    : var(--card);
}

.side_head          { display: flex; align-items: center; gap: 9px; height: var(--head-height); padding: 0 16px; }
.side_head img      { display: block; width: 28px; height: 28px; object-fit: contain;                                                                    }
.side_head strong   { font-size: var(--text-2xl); font-weight: 600; letter-spacing: -.4px;                                                             }

/* 테마 토글 — 헤더 우측 아이콘(서비스 앱과 같은 배치). 라이트↔다크만 오가고
   '시스템'은 버튼으로 노출하지 않는다 — 저장값 없으면 head 스크립트가 OS 설정을 따른다. */
.side_head button {
    display      : grid;
    place-items  : center;
    width        : 29px;
    height       : 29px;
    margin-left  : auto;
    padding      : 0;
    border       : 0;
    border-radius: var(--radius-sm);
    background   : transparent;
    color        : var(--t3);
    font-size    : var(--text-2xl);
    font-family  : inherit;
    cursor       : pointer;
}

.nav { flex: 1; display: flex; flex-direction: column; gap: 2px; padding: 11px 10px; }

.nav_lab {
    display       : flex;
    align-items   : center;
    gap           : 5px;
    padding       : 9px 8px 5px;
    color         : var(--t3);
    font-size     : var(--text-xs);
    font-weight   : 600;
    letter-spacing: .07em;
    text-transform: uppercase;
}

.nav_lab.sup { color: var(--warn);          }  /* 최고 관리자 묶음 — 자물쇠는 표시일 뿐, 차단은 SecurityConfig 매처가 한다 */
.nav_lab .ph { font-size: var(--text-md);   }

.nav a          { display: flex; align-items: center; gap: 10px; height: 40px; padding: 0 12px; border-radius: 12px; color: var(--t1); font-size: var(--text-lg); font-weight: 500; text-decoration: none; }  /* 서비스 사이드바 radius 12 — 메뉴명은 잉크·15px(가독성 실피드백) */
.nav a .ph      { width: 19px; font-size: var(--text-2xl); text-align: center;      }
.nav a.on       { background: var(--accent-soft); color: var(--accent-ink); font-weight: 600; }  /* 서비스 활성 = 코랄 틴트 + coral-ink 글자 */
.nav span.off   { display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 10px; color: var(--t3); font-size: var(--text-md); font-weight: 500; }
.nav span.off .ph { width: 19px; font-size: var(--text-2xl); text-align: center;    }
.nav .cnt       { margin-left: auto; padding: 1px 7px; border-radius: var(--radius-pill); background: var(--bad-bg); color: var(--bad); font-size: var(--text-xs); font-weight: 700; font-variant-numeric: tabular-nums; }


.side_foot               { display: flex; align-items: center; gap: 9px; padding: 12px 14px; border-top: 1px solid var(--line); }
.side_foot form          { display: inline;                                                                                    }
.side_foot form button   { display: grid; place-items: center; width: 29px; height: 29px; padding: 0; border: 1px solid var(--line2); border-radius: var(--radius-sm); background: var(--card); color: var(--t3); font-size: var(--text-2xl); font-family: inherit; cursor: pointer; }

.ava       { display: grid; place-items: center; width: 29px; height: 29px; border-radius: var(--radius-pill); background: var(--accent-soft); color: var(--accent); font-size: var(--text-md); font-weight: 700; }
.who       { flex: 1; min-width: 0;                                                                          }
.who b     { display: flex; align-items: center; gap: 5px; font-size: var(--text-md); font-weight: 600;       }
.who b .ph { color: var(--warn); font-size: var(--text-lg);                                                   }
.who span  { display: block; color: var(--t2); font-size: var(--text-xs);                                     }

/* scrollbar-gutter — 메뉴마다 스크롤바가 생겼다 없어지면 본문 폭이 변해 화면이 좌우로 흔들려 보인다.
   자리를 항상 확보해 어떤 메뉴에서든 레이아웃이 같은 자리에 선다. */
.main { flex: 1; min-width: 0; overflow-y: auto; scrollbar-gutter: stable; }

/* ===== 페이지 헤더 · 본문 ===== */

/* 서비스 헤더 바(.menu_top)와 같은 문법 — 흰 카드 + --line 하단선 */
.phead {
    display        : flex;
    align-items    : center;
    justify-content: space-between;
    gap            : 14px;
    height         : var(--head-height);
    padding        : 0 26px;
    border-bottom  : 1px solid var(--line);
    background     : var(--card);
}

.phead h2      { display: inline-flex; align-items: center; font-size: var(--text-2xl); font-weight: 600; letter-spacing: -.3px; }  /* flex 정렬 — 배지를 vertical-align 으로 맞추면 타이틀 크기 바꿀 때마다 어긋난다 */
.phead .count  { margin-left: 9px; padding: 2px 10px; border-radius: var(--radius-pill); background: var(--accent-soft); color: var(--accent-ink); font-size: var(--text-sm); font-weight: 700; font-variant-numeric: tabular-nums; }
.phead_l       { display: flex; align-items: center; gap: 8px;                                                               }
.phead_r       { display: flex; align-items: center; gap: 8px;                                                               }
.pback         { display: grid; place-items: center; width: 32px; height: 32px; margin-left: -7px; border-radius: var(--radius-pill); color: var(--t2); font-size: var(--text-2xl); text-decoration: none; }  /* 음수 마진 — 호버 원은 넉넉히, 화살표 자체는 본문 왼쪽 선에 맞춘다 */

/* 범용 flex 스페이서 — 앞뒤 형제를 컨테이너 양끝으로 민다(notice/form.ftl:14 선례).
   .phead 는 justify-content:space-between 이 이미 있어 원래도 무해했고, .fbar/.tsub 처럼
   space-between 이 없는 flex 바에서는 이 규칙이 있어야 실제로 밀린다. */
.sp { flex: 1 1 auto; }

.pbody { display: flex; flex-direction: column; gap: 12px; padding: 13px 26px 18px; }  /* 서비스 본문 좌우 26px */

.ghost {
    display      : inline-flex;
    align-items  : center;
    gap          : 6px;
    height       : 34px;
    padding      : 0 11px;
    border       : 1px solid var(--line2);
    border-radius: var(--radius-sm);
    background   : var(--card);
    color        : var(--t2);
    font-size    : var(--text-md);
    font-weight  : 600;
    font-family  : inherit;
    text-decoration: none;
    cursor       : pointer;
}

.ghost .ph { font-size: var(--text-2xl); }

/* 2차 인증 해제 경고 — 화면 맨 위 전폭. 눈에 거슬리라고 만든 것이다(되돌리기를 잊지 않게).
   body 가 세로 flex 라 띠가 있어도 페이지 스크롤은 안 생긴다 — 띠 + .shell 이 뷰포트를 정확히 나눠 갖는다. */
.warn_bar      { display: flex; align-items: center; gap: 8px; padding: 9px 20px; background: var(--bad); color: #fff; font-size: var(--text-md); }
.warn_bar b    { font-weight: 700;                                                              }
.warn_bar code { padding: 1px 6px; border-radius: var(--radius-sm); background: rgba(0, 0, 0, .22); font-family: var(--mono); font-size: var(--text-sm); }
.warn_bar .ph  { font-size: var(--text-2xl);                                                    }

/* ===== 카드 · 패널 ===== */

/* ★ 그림자를 주지 않는다. 모든 패널이 떠 있으면 위계가 사라져 화면이 '카드 나열'로 읽힌다 —
   떠 있는 것은 대시보드 히어로 하나뿐이고, 나머지는 선으로만 경계를 만든다. */
.card,
.panel      { border: 1px solid var(--line); border-radius: 10px; background: var(--card);                                    }
.card       { padding: 20px;                                                                                                  }

/* 세로 flex 로 둔다 — 나란한 두 패널의 높이를 맞추려면(.row2 stretch) 바닥의 빈 공간을
   본문(.panel_b)이 흡수해야 한다. 안 그러면 상자만 늘고 안쪽은 위에 뭉쳐 더 어색하다. */
.panel      { display: flex; flex-direction: column; overflow: hidden;                                                        }
.panel_h    { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 13px 18px; border-bottom: 1px solid var(--line); }
.panel_h h3 { font-size: var(--text-lg); font-weight: 600;                                                                    }
.panel_h .hint { color: var(--t3); font-size: var(--text-sm);                                                                 }
.panel_b    { flex: 1; padding: 16px 18px;                                                                                    }

.empty_msg         { color: var(--t3); font-size: var(--text-md);                                     }
.page_title        { margin: 0 0 16px; font-size: var(--text-3xl); font-weight: 600;                  }
.page_title .count { margin-left: 8px; padding: 2px 10px; border-radius: var(--radius-pill); background: var(--accent-soft); color: var(--accent-ink); font-size: var(--text-sm); font-weight: 700; font-variant-numeric: tabular-nums; vertical-align: 2px; }

/* ===== 대시보드 — 3층 위계 =====
   같은 카드를 반복하면 위계가 없어 어디를 볼지 안 정해진다. 층마다 모양을 달리한다.

     1층 .hero  : --card + 그림자 + 라운드 14 + 46px 숫자   → 지금 손봐야 할 것
     2층 .tile  : --card-hover + 테두리 없음 + 라운드 7      → 곁눈으로 보는 참고 수치
     3층 .panel : --card + 1px 선 + 그림자 없음              → 추이·이력 (공용 컴포넌트)

   ★ 층 라벨(.lay)에 액센트 레일·구분선을 붙이지 않는다. 층 자체가 위계를 말하므로 장식이 겹친다.
   ★ 층 라벨은 타이틀이다 — 회색 자간 스타일(eyebrow)로 낮추지 않는다(실피드백: "타이틀인데 왜 회색이야").
     라벨이 잉크(t1)로 서고 옆의 설명(b)이 한 단 낮은 회색으로 따라온다. */

.lay        { display: flex; align-items: baseline; gap: 9px; margin: 7px 0 10px; color: var(--t1); font-size: var(--text-xl); font-weight: 700; letter-spacing: -.2px; }
.lay:first-child { margin-top: 0;                                                              }
.lay b      { color: var(--t2); font-size: var(--text-sm); font-weight: 500; letter-spacing: 0; }
.lay a      { margin-left: auto; color: var(--accent); font-size: var(--text-sm); font-weight: 600; letter-spacing: 0; text-decoration: none; }

/* ── 1층 ── */

.hero       { padding: 26px 28px; border-radius: 14px; background: var(--card); box-shadow: var(--shadow-md); }

.hero_t     { display: flex; align-items: baseline; gap: 11px; flex-wrap: wrap;                }
.hero_t b   { font-size: 52px; font-weight: 700; letter-spacing: -2.4px; line-height: 1; font-variant-numeric: tabular-nums; }
.hero_t i   { color: var(--t2); font-size: var(--text-xl); font-weight: 600; font-style: normal; }
.hero_t a   { margin-left: auto; color: var(--accent); font-size: var(--text-md); font-weight: 600; text-decoration: none; }
.hero_s     { margin-top: 7px; color: var(--t2); font-size: var(--text-md);                    }
.hero_s b   { color: var(--t1); font-weight: 600;                                              }

/* 큐가 빈 날 — 큰 숫자를 세우지 않는다. '없음'은 자랑할 수치가 아니라 상태 한 줄이면 충분하고,
   그 자리는 아래로 눕는 참고 패널(.wd.calm)이 쓴다. 체크 아이콘만 ok 색이다 — 초록 52px 숫자는 과했다(실피드백). */
.calm_bar      { display: flex; align-items: center; gap: 10px; padding: 16px 20px; border: 1px solid var(--line); border-radius: 14px; background: var(--card); }
.calm_bar .ph  { color: var(--ok); font-size: var(--text-3xl);                                 }
.calm_bar b    { font-weight: 700;                                                             }
.calm_bar .sub { color: var(--t2); font-size: var(--text-md);                                  }
.calm_bar a    { margin-left: auto; color: var(--accent); font-size: var(--text-md); font-weight: 600; text-decoration: none; white-space: nowrap; }

/* 볼 게 있는 날 — 아이콘 기본이 ok 색이라 그것까지 갈아야 한 줄이 경고로 읽힌다(.sec_cell.warn 과 같은
   폭으로 색만 바꾼다 — 테두리는 건드리지 않는다) */
.calm_bar.warn .ph,
.calm_bar.warn b,
.calm_bar.warn .sub { color: var(--warn); }

/* 큐 한 줄 — 카드로 감싸지 않는다. 히어로 안에서 선으로만 나눠야 목록이 길어져도 조밀함이 유지된다 */
.q          { display: grid; grid-template-columns: 54px minmax(0, 1fr) auto; gap: 16px; align-items: start; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line); }
.q .ago     { color: var(--t2); font-size: var(--text-xs); font-variant-numeric: tabular-nums; line-height: 1.7;  }
.q .tx      { min-width: 0; font-size: var(--text-md); line-height: 1.6;                      }
.q .tx > b  { font-weight: 700;                                                                }
.q .mt      { display: flex; align-items: center; gap: 5px; margin-top: 4px; color: var(--t2); font-size: var(--text-sm); flex-wrap: wrap; }
.q .mt b    { color: var(--t1); font-weight: 600;                                              }
.q .ex      { display: block; margin-top: 4px; color: var(--t2);                               }
.q .go      { display: inline-flex; align-items: center; height: 30px; padding: 0 11px; border: 1px solid var(--line2); border-radius: var(--radius-sm); color: var(--t2); font-size: var(--text-sm); font-weight: 600; text-decoration: none; white-space: nowrap; }

/* ── 2층 ── */

/* 다섯 칸 한 줄 — 묶음 머리(회원·콘텐츠)는 없앴다. 칸이 다섯이면 한 줄이 곧 묶음이고,
   머리를 두면 회원 2칸·콘텐츠 3칸으로 갈려 줄마다 빈 칸이 생긴다(home/index.ftl 의 같은 자리 주석) */
.tiles      { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px;       }

/* 다섯 칸을 같은 모양으로 둔다 — 서로 대등한 참고 수치라 하나를 키우면 근거 없는 강조가 된다 */
.tile       { padding: 16px 18px; border-radius: 7px; background: var(--card-hover);           }
.tile .n    { font-size: 26px; font-weight: 700; letter-spacing: -.7px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.tile .n small { margin-left: 2px; color: var(--t2); font-size: var(--text-md); font-weight: 500; }
.tile .l    { margin-top: 3px; color: var(--t1); font-size: var(--text-sm);                    }

/* 스파크라인 — 큰 차트와 같은 말을 쓴다(무채로 깔고 최근·최댓값만 accent). 크기만 줄인다.
   새 색을 들이지 않으므로 차트가 여덟 개로 늘어도 화면이 시끄러워지지 않는다. */
.spark      { display: flex; align-items: flex-end; gap: 2px; height: 22px; margin-top: 9px;   }
.spark i    { flex: 1; min-height: 2px; border-radius: 1px; background: var(--bar-lo);         }  /* 큰 차트와 같은 코랄 농도 문법 */
.spark i.hi { background: var(--accent);                                                       }
.spark i.z  { background: var(--line);                                                         }
.spark_f    { display: flex; justify-content: space-between; margin-top: 5px; color: var(--t2); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }

/* ± 스파크라인 — 활성 회원 칸에만. 위아래를 같은 자로 재므로 0선이 가운데 고정이다.
   색은 신호등(ok/bad)을 쓰지 않는다 — 방향은 0선 위·아래 위치가 이미 말하고, 초록·빨강이
   옆 타일의 코랄과 세 계열로 싸웠다(실피드백). 위(가입)만 코랄, 아래(탈퇴)는 무채다. */
.spark.pm        { align-items: stretch;                                                       }
.spark.pm span   { flex: 1; display: grid; grid-template-rows: 1fr 1fr;                        }
.spark.pm u      { align-self: end;   min-height: 0; border-radius: 1px 1px 0 0; background: var(--accent); text-decoration: none; }
.spark.pm b      { align-self: start; min-height: 0; border-radius: 0 0 1px 1px; background: var(--t3);  }

.dot_ok   { background: var(--ok);   }
.dot_bad  { background: var(--bad);  }
.dot_warn { background: var(--warn); }
.dot_pur  { background: var(--pur);  }

@media (max-width: 1100px) {
    .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── 타일 = 차트 선택기 — 누르면 3층 자유 슬롯이 그 지표의 큰 차트로 바뀐다.
   주소를 바꾸지 않으므로 button 이다(감사 로그 '직접' 칩과 같은 이유). 모양은 .tile 그대로 쓰고
   버튼 기본값만 지운다. 선택 표시는 필터 세그(.seg .on)와 같은 문법 — 흰 배경 + 코랄 테두리. */
button.tile      { border: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; transition: box-shadow .12s; }
.tile.sel:hover  { box-shadow: inset 0 0 0 1px var(--line2);                                   }
.tile.sel.on     { background: var(--card); box-shadow: inset 0 0 0 1.5px var(--accent);       }

/* ===== 대시보드 0층 — 밤사이 브리핑 ===== */

/* 그날 첫 방문에만 펼쳐진다(스크립트가 localStorage 로 판별). 재방문엔 아래 슬림 바만 남는다.
   둘 다 display 를 갖는 블록이라 [hidden] 기본값이 덮인다 — 다시 꺼 준다(감사 로그 .adate 와 같은 함정). */
.dbrief          { padding: 24px 28px; border-radius: 14px; background: var(--card); box-shadow: var(--shadow-md); cursor: pointer; }  /* 카드 자체가 접기 버튼(접힌 바와 대칭). d- 접두 = 로그인 권한 브리핑(.brief)과 딴 부품 */
.dbrief[hidden]  { display: none;                                                              }
.dbrief_hi       { display: flex; align-items: baseline; gap: 10px; color: var(--t1); font-size: var(--text-sm); font-weight: 700; }
.dbrief_hi button{ margin-left: auto; border: 0; background: none; color: var(--t3); font-family: inherit; font-size: var(--text-sm); font-weight: 600; letter-spacing: 0; cursor: pointer; }
.dbrief_hi button:hover { color: var(--accent-ink);                                             }
.dbrief_tx       { margin-top: 9px; font-size: var(--text-xl); font-weight: 500; line-height: 1.62; letter-spacing: -.2px; }
.dbrief_tx b     { font-weight: 700;                                                           }
.dbrief .heads   { margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--line);     }

/* 접힌 바 — 바 자체가 버튼이다(펼치기 버튼을 따로 두지 않는다 — 실피드백). 칩 링크만 이동이 우선 */
.briefslim          { display: flex; align-items: center; gap: 10px; padding: 11px 18px; border: 1px solid var(--line); border-radius: 14px; background: var(--card); cursor: pointer; transition: background-color .12s; }
.briefslim:hover    { background: var(--card-hover);                                           }
.briefslim[hidden]  { display: none;                                                           }
.briefslim .d       { color: var(--t1); font-size: var(--text-sm); font-weight: 700; white-space: nowrap; }
.briefslim .heads   { flex: 1;                                                                 }
.briefslim .none_tx { color: var(--t1); font-size: var(--text-sm);                             }
.briefslim .more    { color: var(--t3); font-size: var(--text-sm); font-weight: 600;           }

/* 주의 칩 — '지금 눈여겨볼 것'만. 없으면 그려지지 않는다(0개를 "이상 없음"으로 채우는 건 슬림 바 몫) */
.heads      { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;                   }
.headchip   { display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border: 1px solid var(--line2); border-radius: var(--radius-pill); background: var(--card); color: var(--t2); font-size: var(--text-sm); font-weight: 600; text-decoration: none; }
.headchip b { color: var(--t1); font-weight: 700; font-variant-numeric: tabular-nums;          }
.headchip.warn   { border-color: transparent; background: var(--warn-bg); color: var(--warn);  }
.headchip.warn b { color: var(--warn);                                                         }
.headchip.bad    { border-color: transparent; background: var(--bad-bg); color: var(--bad);    }
.headchip.bad b  { color: var(--bad);                                                          }

/* ===== 대시보드 1층 — 워크데스크 (좌 큐 · 우 레일) ===== */

/* 큐가 주역이라 넓은 쪽이 큐다. 우측은 고정 높이 참고 정보라 큐가 짧은 날에도 비지 않는다. */
.wd   { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, .9fr); gap: 14px; align-items: start; }

/* 큐가 빈 날 — calm 히어로는 낮은데 레일은 길어서 2열이면 왼쪽이 뻥 뚫린다.
   레일을 옆이 아니라 아래로 눕힌다: 히어로 전폭 + 패널 한 줄(auto-fit 이라 서버 패널이 없는
   등급에서도 남는 칸 없이 나눠 앉는다). */
.wd.calm       { grid-template-columns: minmax(0, 1fr);                                        }
.wd.calm .rail { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));   }

/* 참고 레일 — 세 패널 다 내용이 고정이다(오늘 3칸 · 서버등 3개(getGaugeSummaryList 가 subList(0,3)) ·
   최근 처리 최대 5건(SQL LIMIT 6 + [0..*4])). 늘어날 일이 없으니 본문 화면(.panel 기본값)보다
   촘촘하게 잡는다 — 이 레일은 읽는 곳이 아니라 곁눈으로 확인하는 곳이다 */
.rail          { display: flex; flex-direction: column; gap: 12px;                             }
.rail .panel_h { padding: 9px 16px;                                                            }
.rail .panel_h h3 { font-size: var(--text-md);                                                 }
.rail .panel_b { padding: 10px 16px;                                                           }
.rail .panel_h a.hint { color: var(--accent); font-weight: 600; text-decoration: none;         }

/* 오늘 요약 — 빈 큐 히어로에 있던 hero_done 3칸이 상시 자리로 옮겨 온 것.
   제목 줄 없이 이 3칸이 패널 전부다(home/index.ftl 의 같은 자리 주석) */
.today    { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;                    }
.today .n { font-size: 20px; font-weight: 700; letter-spacing: -.6px; font-variant-numeric: tabular-nums; }
.today .l { margin-top: 1px; color: var(--t2); font-size: var(--text-xs);                      }

/* 서버 상태등 — 값이 아니라 '서버 메뉴를 봐야 하나'만 답한다. 임계는 ServerStatusService 가 정한다.
   등이 늘 셋이라(getGaugeSummaryList 의 subList(0,3)) 세로로 쌓지 않고 한 줄에 눕힌다 —
   바로 위 '오늘' 3칸과 같은 리듬이 되고 패널 높이가 3분의 1로 준다. 칸 사이는 세로 구분선.
   ★ 라벨만 줄어들 수 있게 min-width:0 을 준다 — 값(퍼센트)은 잘리면 안 되는 쪽이다 */
.lights         { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));              }
.light          { display: flex; align-items: center; justify-content: center; gap: 7px; padding: 3px 4px; font-size: var(--text-sm); }
.light + .light { border-left: 1px solid var(--line);                                           }
.light em       { flex: none; width: 8px; height: 8px; border-radius: var(--radius-pill); background: var(--ok); }
.light.warn em  { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-bg);               }
.light.bad em   { background: var(--bad);  box-shadow: 0 0 0 3px var(--bad-bg);                }
.light .lb      { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--t2); }
.light .v       { flex: none; color: var(--t1); font-weight: 600; font-variant-numeric: tabular-nums; }
.light.warn .v  { color: var(--warn);                                                           }
.light.bad .v   { color: var(--bad);                                                            }

/* 최근 처리 — 3건이지만 사유가 붙는 건은 두 줄이라 행 높이가 갈린다. 카드 높이를 못박고
   넘치는 만큼만 안에서 스크롤한다 — 전체는 헤더의 '감사 로그 →' 가 맡으므로 여기서 다 보일 필요가 없다 */
.rail .feed       { max-height: 122px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--line2) transparent; }
.rail .feed li    { padding: 8px 16px;                                                         }
.rail .feed .when { flex: 0 0 112px;                                                           }

/* 레일이 옆에 설 자리가 없으면 큐 아래로 내린다(추이 3종과 같은 폭 기준) */
@media (max-width: 1180px) { .wd { grid-template-columns: minmax(0, 1fr); } }

/* ===== 대시보드 3층 — 자유 슬롯 · 비중 ===== */

/* 비중 — 최근 처리가 레일로 올라가 홀로 남았다. 전폭 패널에 두 묶음을 나란히 */
.share_b { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px;         }

/* 나란히 설 때는 세로 배치용 구분선 규칙(hgroup + hgroup)을 끈다 — 옆에 선 묶음에 윗선만 남는다 */
.share_b .hgroup + .hgroup { margin-top: 0; padding-top: 0; border-top: 0;                     }

/* 두 값짜리 비중 — 한 줄을 둘이 나눠 갖는 누적 막대. 주(主)만 accent, 나머지는 무채다 */
.stackbar       { height: 14px; border-radius: 3px; background: var(--line2); overflow: hidden; }
.stackbar i     { display: block; height: 100%; border-radius: 3px 0 0 3px; background: var(--accent); }
.stack_lb       { display: flex; justify-content: space-between; gap: 10px; margin-top: 8px; font-size: var(--text-md); }
.stack_lb span  { display: inline-flex; align-items: baseline; gap: 6px;                       }
.stack_lb em    { align-self: center; width: 9px; height: 9px; border-radius: 2px; background: var(--line2); }
.stack_lb em.on { background: var(--accent);                                                   }
.stack_lb b     { font-weight: 600; font-variant-numeric: tabular-nums;                        }
.stack_lb small { color: var(--t2); font-size: var(--text-sm); font-weight: 500; font-variant-numeric: tabular-nums; }

@media (max-width: 1100px) { .share_b { grid-template-columns: minmax(0, 1fr); } }

/* ===== 대시보드 — 2단 패널 · 최근 처리 · 추이 ===== */

/* align-items 를 두지 않는다 — 그리드 기본값 stretch 로 두 패널 높이가 맞는다.
   예전엔 start 라 각 패널이 제 내용만큼만 커서 나란한 두 상자의 바닥이 어긋났다. */
.row2 { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 14px;                    }
.row3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px;                           }

/* 추이 3종은 1180px 아래에서 세로로 쌓는다 — 3열을 유지하면 7일치 막대가 핀처럼 가늘어진다 */
@media (max-width: 1180px) { .row3 { grid-template-columns: minmax(0, 1fr); } }

@media (max-width: 1100px) {
    .row2 { grid-template-columns: 1fr; }
}

.feed          { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none;            }
.feed li       { display: flex; gap: 11px; padding: 12px 18px; border-bottom: 1px solid var(--line); font-size: var(--text-md); }
.feed li:last-child { border-bottom: 0;                                                                     }
.feed .when    { flex: 0 0 118px; white-space: nowrap; color: var(--t2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.feed .what    { flex: 1; min-width: 0; color: var(--t1);                                                   }
.feed .what b  { color: var(--t1); font-weight: 600;                                                        }

/* ===== 차트 =====
   막대·라인·가로바가 한 벌이다. 대시보드 추이 3종과 매출 4종이 같은 규칙을 쓴다.

   구조: .chart > .chart_h(요약) + .plot(좌표계) + .days(축)
   ★ 막대는 .plot 안에서 absolute 로 깔고 값 라벨은 막대에 absolute 로 붙인다. 예전처럼 라벨이
     flex 흐름에 끼면 막대가 (플롯 높이 − 라벨 높이) 기준으로 그려져 높이가 조용히 어긋난다.
   ★ 플롯 높이는 4의 배수로 둔다(128·150). 보조선을 25% 간격으로 깔기 때문에 안 나눠지면
     선이 소수 px 에 떨어져 흐려진다. box-sizing: border-box + 아래 테두리 1px 이므로
     배경 영역은 실제로 127/149px 이다 — 0.25px 오차는 눈에 보이지 않는다. */

/* ★ 색은 '무슨 지표냐'가 아니라 '어디를 봐야 하냐'를 말한다.
   차트가 셋이라고 색을 셋 쓰면 화면에 유채색이 넷~다섯이 되고(accent 포함) 색이 정보를 잃는다.
   차트는 패널 제목으로 이미 구분되므로 막대는 무채로 깔고, 최근 칸과 최댓값 칸에만 accent 를 쓴다.

   색이 의미를 갖는 자리는 두 곳만 남긴다:
     · 방향 — 회원 유입(ok) / 이탈(bad). 위아래로도 구분되지만 관습이 강해 유지한다.
     · 상태 — 태그(.tag.*)의 숨김·정지 등.
   '정체성 색'(이 차트는 초록, 저 차트는 파랑)은 쓰지 않는다. */
.chart      { display: flex; flex-direction: column; --chart: var(--accent);                    }
/* 가입/탈퇴 — 상태 토큰을 시안 D 값(80%/65%)으로 누른 것. 막대(.pm_fill)와 범례(.sw_*)가 같은 변수를 봐야
   한쪽만 바뀌는 일이 없다(2026-09-05 chorong: 막대만 눌리고 범례는 100% 로 남았다). opacity 가 아니라
   color-mix 인 이유 — 숫자 라벨이 막대 요소 안에 있어 opacity 는 글자까지 흐리게 한다. */
.chart      { --join:  color-mix(in srgb, var(--ok)  80%, transparent);
              --leave: color-mix(in srgb, var(--bad) 65%, transparent);                          }

.chart_h    { display: flex; align-items: baseline; gap: 9px; margin-bottom: 12px;               }
.chart_h b  { font-size: 22px; font-weight: 700; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }  /* 요약 수치 — 1회성 (스케일 밖) */
.chart_h .up { color: var(--ok);  font-size: var(--text-sm); font-weight: 600;                   }
.chart_h .dn { color: var(--bad); font-size: var(--text-sm); font-weight: 600;                   }
.chart_h i  { margin-left: auto; color: var(--t2); font-size: var(--text-xs); font-style: normal; font-variant-numeric: tabular-nums; }

/* flex: none 은 예방책이다. .plot 의 자식(.bars/.pm_bars)이 전부 absolute 라 콘텐츠 높이가 0 인데,
   .plot 은 .chart(column flex)의 항목이라 flex-shrink 가 1 이다 — .chart 에 확정 높이가 생기는 날
   150px 이 0 으로 줄어든다. 지금은 .chart 높이가 auto 라 실제로 줄어들지는 않는다. */
.plot       { position: relative; flex: none; height: 128px; border-bottom: 1px solid var(--line2); }

.bars       { position: absolute; inset: 0; display: flex; align-items: flex-end; gap: 4px;      }
.bar_col    { position: relative; flex: 1; display: flex; align-items: flex-end; justify-content: center; height: 100%; }

/* max-width 로 두께를 잡는다 — flex: 1 만으로는 칸 폭이 그대로 막대 폭이 되어 7일치에서 슬래브가 된다 */
/* 한 색 안에서 세 단계로 나눈다 — 색을 늘리지 않고 밋밋함만 없앤다.
   무채(평균 이하) → 옅은 면 + 액센트 실선(평균 초과) → 꽉 찬 액센트(최근·최댓값).
   기준은 이미 그리고 있는 평균선과 같은 값이라 선과 색이 서로 다른 말을 하지 않는다.
   ★ .hi 는 .mid 뒤에 둔다. 둘 다 (0,2,0) 이라 순서로만 이긴다. */
/* 코랄 농도 세 단(--bar-lo → --bar-mid → accent) — 회색 막대 + 코랄 강조 + 테두리 박스 조합은
   세 문법이 섞여 탁했다. 한 색의 농도면 '어디를 봐야 하냐'는 그대로 남고 톤은 하나가 된다. */
.bar        { position: relative; width: 100%; max-width: 30px; min-height: 2px; border-radius: 3px 3px 0 0; background: var(--bar-lo); }
.bar.mid    { background: var(--bar-mid);                                                        }  /* 평균 초과 — 한 단 진하게 */
.bar.hi     { background: var(--chart);                                                          }  /* 최근 칸 · 최댓값 칸 — 눈이 먼저 가야 할 자리 */
.bar.zero   { background: var(--line);                                                           }  /* 0 인 날 — 값이 있는 옅은 코랄보다 더 옅은 무채 */

/* 값은 늘 막대 밖(위)에 둔다. 막대 안에 넣으면 배경색마다 대비를 다시 맞춰야 하고,
   무채 막대 위에서는 어떤 잉크를 써도 11px AA 를 넘기 어렵다. */
.bar b      { position: absolute; bottom: 100%; left: 0; right: 0; margin-bottom: 3px; color: var(--t1); font-size: var(--text-xs); font-weight: 600; font-variant-numeric: tabular-nums; text-align: center; }
.bar.hi b   { color: var(--t1); font-weight: 700;                                                }

/* 직전 값 눈금 — 반드시 .bar_col 의 자식이다(.bar 안이 아니라). bottom % 는 기준 상자 높이로 재는데
   .bar 는 자기 높이가 heightPct 라, 그 안에 두면 눈금이 늘 그 배율만큼 눌리고 급감한 날은 방향이 뒤집힌다.
   폭은 막대(30px)에 좌우 2px 씩 물린 34px — 칸 전체로 늘리면 이웃 칸까지 잇는 선으로 읽힌다.
   z-index 는 막대보다 앞에 세우려고 — 안 세우면 트리 순서상 막대가 위에 그려진다. */
.bar_prev   { position: absolute; left: 50%; z-index: 2; width: 100%; max-width: 34px; height: 2px; background: var(--t2); opacity: .5; transform: translateX(-50%); }

/* 기간 평균선 — DOM 상 .bars 보다 앞이라 z-index 없이는 막대 뒤로 깔린다 */
.chart_avg  { position: absolute; left: 0; right: 0; z-index: 3; border-top: 1px dashed var(--t3); }
.chart_avg b { position: absolute; top: -8px; right: 0; padding: 0 4px; background: var(--card); color: var(--t2); font-size: var(--text-xs); font-weight: 600; }

.days       { display: flex; gap: 4px; margin-top: 8px; color: var(--t1); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.days span  { flex: 1; min-width: 0; overflow: hidden; text-align: center; white-space: nowrap;  }

/* ===== 필터 탭 ===== */

.tabs        { display: flex; flex-wrap: wrap; gap: 6px;                                                                     }
.tabs a      { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border: 1px solid var(--line2); border-radius: var(--radius-pill); background: var(--card); color: var(--t2); font-size: var(--text-md); font-weight: 600; text-decoration: none; }
.tabs a.on   { border-color: var(--accent); background: var(--accent); color: var(--accent-on);                               }
.tabs .badge { font-weight: 700; font-variant-numeric: tabular-nums; opacity: .8;                                             }

/* ===== 표 ===== */

.table_scroll  { overflow-x: auto;                                                              }  /* 열이 많아 좁은 화면에서 표만 가로 스크롤 — 페이지는 안 넘침 */
/* 표 본문은 한 크기·한 굵기다 — 셀마다 크기를 달리하면 어느 값이 중요한지가 톤이 아니라 크기로 말해져
   한 행을 읽는 눈이 여러 번 멈춘다. 위계는 헤더 굵기(600)와 색으로만 준다(2026-09-10 확정). */
.grid          { width: 100%; border-collapse: collapse; font-size: var(--text-sm); font-weight: 500;   }
.grid th,
.grid td       { padding: 10px 11px; border-bottom: 1px solid var(--line); vertical-align: middle;                  }
/* 헤더 배경 --soft(칩 배경) — --ground·--card-hover 는 흰 카드와 색차가 미세해 헤더로 안 읽힌다.
   sticky 헤더(감사로그·공지·모달 표)는 배경이 불투명해야 행이 비쳐 보이지 않는데 --soft 가 그 조건도 만족 */
.grid th       { background: var(--soft); border-bottom-color: var(--line2); color: var(--t1); font-weight: 600; text-align: center; white-space: nowrap;                     }

.nowrap        { white-space: nowrap;                                                           }
.content_cell  { min-width: 210px; max-width: 270px; color: var(--t2); word-break: break-word;  }
.dim           { color: var(--t2);                                                              }
.muted         { color: var(--t3);                                                              }
.small         { font-size: var(--text-sm);                                                     }
/* 가운데 정렬 — 표 헤더는 공용으로 이미 가운데다(.grid th). 본문은 이 클래스를 붙인 열만 가운데다.
   제목처럼 읽어 내려가는 텍스트는 왼쪽이 기본이다 — 눈이 시작점을 찾는 비용이 없어야 한다 */
.center        { text-align: center;                                                            }
.num           { font-variant-numeric: tabular-nums;                                            }

/* ===== 상태 태그 ===== */
/* 의미가 서비스와 같은 것은 DESIGN.md 시맨틱 색을 그대로 쓴다 — 임의 hex 금지 */

.tag              { display: inline-block; padding: 2px 7px; border-radius: var(--radius-sm); background: var(--card-hover); color: var(--t3); font-size: var(--text-xs); font-weight: 600; white-space: nowrap; vertical-align: 1px; }
.tag.live         { background: var(--ok-bg);      color: var(--ok);   }
.tag.hide         { background: var(--warn-bg);    color: var(--warn); }
.tag.blind        { background: var(--pur-bg);     color: var(--pur);  }  /* 운영 숨김 — 자동숨김과 눈으로 구분 */
.tag.del          { background: var(--bad-bg);     color: var(--bad);  }
.tag.pend         { background: var(--info-bg);    color: var(--info); }  /* 신고 처리 — 되돌릴 수 있는 상태 변경이라 정지·숨김과 다른 계열 */
.tag.st_PENDING   { background: var(--warn-bg);    color: var(--warn); }
.tag.st_REVIEWED  { background: var(--info-bg);    color: var(--info); }
.tag.st_ACTIONED  { background: var(--ok-bg);      color: var(--ok);   }
.tag.st_DISMISSED { background: var(--card-hover); color: var(--t3);   }
/* 회원 상태 — 신고 상태와 같은 st_ 규칙을 쓴다(코드값을 그대로 클래스로 붙인다) */
.tag.st_ACTIVE    { background: var(--ok-bg);      color: var(--ok);   }
.tag.st_SUSPENDED { background: var(--bad-bg);     color: var(--bad);  }
.tag.st_DELETED   { background: var(--card-hover); color: var(--t3);   }
.tag.comm         { background: var(--teal-bg);    color: var(--teal); }  /* 임신·육아 커뮤니티 — 그룹 피드와 구분 */
.tag.grp          { background: var(--card-hover); color: var(--t2);   }
/* 회원 목록 — 무료/결제 구분 배지(검증 I-1). 무료는 목업(프리미엄 관리 모달과 같은 --pur 톤)과 맞춘다 */
.tag.prem_free    { background: var(--pur-bg);      color: var(--pur);  }
.tag.prem_paid    { background: var(--info-bg);     color: var(--info); }
.tag.exp          { background: var(--bad-bg);      color: var(--bad);  }  /* 만료 처리 전 잔존(검증 M-2) */

.subject { margin-bottom: 3px; color: var(--t1); font-weight: 600; }  /* 커뮤니티 글 제목 — 본문 위 */

/* ===== 행별 처리 폼 =====
   기본은 접어 둔다 — 한 화면에 30건이 뜨는데 폼을 다 펼치면 목록을 훑을 수가 없다. */

.act summary       { display: inline-flex; align-items: center; height: 26px; padding: 0 10px; border: 1px solid var(--line2); border-radius: var(--radius-sm); background: var(--card); color: var(--t2); font-size: var(--text-sm); font-weight: 600; cursor: pointer; user-select: none; }
.act[open] summary { margin-bottom: 8px; border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }

.act_form          { width: 260px; padding: 12px; border: 1px solid var(--line2); border-radius: var(--radius); background: var(--card-hover); white-space: normal; }
.act_label         { display: block; margin-bottom: 5px; color: var(--t2); font-size: var(--text-sm); font-weight: 600; }
.act_label input   { height: 31px; margin-top: 5px; font-size: var(--text-md);                                         }
.act_check         { display: flex; align-items: center; gap: 6px; margin: 9px 0; color: var(--t2); font-size: var(--text-sm); }
.act_btns          { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px;                   }
/* width: auto 를 명시한다 — select 는 위 폼 기본값(width: 100%)을 상속받아 줄을 통째로 먹는다 */
.act_btns button,
.act_btns select   { display: inline-flex; align-items: center; width: auto; height: 27px; padding: 0 10px; border: 1px solid var(--line2); border-radius: var(--radius-sm); background: var(--card); color: var(--t2); font-size: var(--text-sm); font-weight: 600; font-family: inherit; cursor: pointer; }
.act_btns c-select { width: auto; min-width: 96px; height: 27px; font-size: var(--text-sm); font-weight: 600;         }  /* 테두리·배경은 ShadowDOM(part) 몫 */
.act_btns button.danger { border-color: var(--bad); color: var(--bad);                                                 }  /* 정지는 되돌리는 데 사람 손이 필요해 색으로 구분 */

/* ===== 모달(공용 dialog) =====
   매출 화면에서 처음 만들었지만 지금은 회원·관리자 계정·공지가 함께 쓴다 — 메뉴 블록 밖에 둔다.
   <dialog> 라 포커스 가둠·Esc 닫기는 브라우저가 해 준다. */
.rmodal     { width: min(600px, calc(100vw - 40px)); padding: 0; border: 1px solid var(--line); border-radius: 18px; background: var(--card); color: var(--t1); box-shadow: 0 18px 48px rgba(24, 20, 17, .22); }
.rmodal::backdrop { background: rgba(24, 20, 17, .5);                                              }

.rmodal_h   { display: flex; align-items: center; gap: 12px; padding: 17px 20px; border-bottom: 1px solid var(--line); }
.rmodal_ic  { display: grid; place-items: center; width: 34px; height: 34px; border-radius: var(--radius-pill); background: var(--accent-soft); color: var(--accent); font-size: var(--text-xl); }
.rmodal_t   { display: flex; flex-direction: column; gap: 1px;                                     }
.rmodal_t h3 { font-size: var(--text-3xl); font-weight: 700;                                        }
.rmodal_t .hint { color: var(--t1); font-size: var(--text-xs);                                     }
.rmodal_x   { display: grid; place-items: center; width: 30px; height: 30px; margin-left: auto; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--t3); font-size: var(--text-xl); font-family: inherit; cursor: pointer; }
.rmodal_b   { padding: 18px 20px;                                                             }

/* ===== 매출 =====
   배치 정본 = doc/preview/revenue-d-perfect.html (D 안, 2026-09-04 chorong 확정) 에서 출발해
   예상·파이프라인을 걷어냈다(2026-09-05). 지금은 기간 탭 → 실수입 | KPI 여섯 칸 → 2×2(왼쪽 '돈' ·
   오른쪽 '사람') → 정산 기준 접기. 데이터가 없어도 카드는 남는다(빈 상태는 카드 안에서 말한다). */

/* ── 상단 — 실수입 한 덩어리 | 나머지 여섯 칸 ──
   ★ 전부 같은 크기로 늘어놓으면 결과(실수입)가 과정(결제−환불−수수료)과 동급으로 읽힌다.
     그 칸에만 틴트를 깔아 봤더니 깨끗한 스트립 가운데 색 블록이 생겨 더 시끄러웠다 —
     자리(왼쪽 전용 칸)와 크기로만 위계를 준다(2026-09-05 chorong).
   ⚠️ 이 주석이 한동안 깨져 있었다(닫는 별표가 중간에 끼어 아래 .kpilead 규칙이 통째로 무효) —
     브라우저는 조용히 버려서 실수입이 스트립 위로 올라간 채 며칠 떴다. CssCommentBalanceCheck 가 지킨다. */
/* ★ 왼쪽 칸을 auto 로 두면 안 된다 — 내용 크기라 실수입이 "0원" 인 단계에서 칸이 최소로 쪼그라들어
     "왜 이렇게 작지" 가 된다(실측 2026-09-05). 최소 폭을 박고, 숫자가 길어지면 그만큼만 늘린다. */
.kpilead    { display: grid; grid-template-columns: minmax(272px, auto) minmax(0, 1fr);            }
.kl         { display: flex; flex-direction: column; justify-content: center; gap: 4px; padding: 18px 24px; border-right: 1px solid var(--line); }
.kl .l      { color: var(--t1); font-size: var(--text-sm); font-weight: 700;                       }
.kl .n      { color: var(--accent-ink); font-size: 34px; font-weight: 800; letter-spacing: -1.2px; line-height: 1.1; white-space: nowrap; font-variant-numeric: tabular-nums; }  /* 헤드라인 — 1회성 (스케일 밖) */
.kl .n em   { margin-left: 3px; color: var(--t2); font-size: var(--text-lg); font-style: normal; font-weight: 700; }

.kpi        { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));                     }
.kpi6       { grid-template-columns: repeat(6, minmax(0, 1fr));                                    }
.kc         { display: flex; flex-direction: column; gap: 2px; padding: 13px 16px;                 }
.kc + .kc   { border-left: 1px solid var(--line);                                                  }
.kc .l      { color: var(--t2); font-size: var(--text-xs); font-weight: 600;                       }
.kc .n      { font-size: var(--text-xl); font-weight: 700; letter-spacing: -.3px; font-variant-numeric: tabular-nums; }
.kc .s      { color: var(--t2); font-size: var(--text-xs); font-variant-numeric: tabular-nums;     }
.kc.minus .n { color: var(--bad);                                                                  }

/* ── 본문 2×2 — 행마다 두 카드를 같은 높이로 세운다.
   ★ 세 번째 방식이다. ① grid 2열 + 열마다 카드 쌓기 → 왼쪽 3장·오른쪽 4장이라 왼쪽 아래가 통째로
     비었다(2026-09-04). ② column-count → 카드가 순서대로 흘러 (1·2)|(3·4) 조합으로 굳는데
     채널·요금제가 유독 길어 오른쪽 아래가 190px 비었다(실측 캡처 2026-09-05). ③ 지금 — 카드가
     넷이라 2×2 행 정렬이 되고, 행 안에서 짝보다 낮은 카드는 stretch 된다. 남는 높이는 카드 밖 여백이
     아니라 차트(.grow) 안으로 들어간다. 카드 수가 넷이 아니게 되면 이 전제가 깨진다. */
.rgrid2     { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px;          }

/* 차트 카드 본문 — 짝 카드가 더 크면 차트가 그 높이를 채운다(막대는 % 높이라 같이 자란다).
   빈 상태(.rbox)도 같이 늘어나 문구가 카드 세로 중앙에 선다. 표 카드(채널·해지)는 안 준다 —
   표는 늘릴 게 없고 해지의 눕힌 선(.churnrow)이 커지면 방금 줄인 걸 되돌리는 셈이다. */
.grow        { display: flex; flex-direction: column;                                              }
.grow .chart { flex: 1;                                                                            }
.grow .rbox  { flex: 1;                                                                            }


/* 카드 밑 각주 — 값이 아니라 그 값을 어떻게 읽어야 하는지 */
.rfoot      { margin-top: 12px; color: var(--t2); font-size: var(--text-sm); line-height: 1.6;     }
.rfoot b    { color: var(--t1); font-weight: 700;                                                  }

/* 빈 상태 — 카드를 없애지 않고 안에서 말한다(2026-09-04 chorong). 차트가 서던 높이를 그대로
   차지해야 기간을 바꿔도 2열의 좌우 높이가 안 흔들린다. .plot.tall(150) + 축(8+16) 자리다.
   회원 목록 erow(한 페이지 높이 고정 + 세로 중앙)와 같은 방침이다. */
.rbox       { display: grid; place-items: center; min-height: 174px; margin: 0; text-align: center; }

/* 해지만 하한이 낮다 — 그래프를 눕히며(2026-09-05) 실내용이 174(막대 차트 자리)보다 낮아졌는데
   빈 상태만 174 를 쓰니 해지가 없을 때 카드가 오히려 부풀었다. 빈·실을 같은 하한에 세운다. */
.chbox       { min-height: 146px;                                                                  }
.chbox .rbox { min-height: inherit;                                                                }

/* ── 정산 기준 — 접혀 있는 게 기본. 매일 읽는 값이 아니라 위 숫자의 근거다 ── */
.fset summary        { display: flex; align-items: center; gap: 9px; padding: 13px 20px; color: var(--t1); font-size: var(--text-lg); font-weight: 700; cursor: pointer; list-style: none; }
.fset summary::-webkit-details-marker { display: none;                                             }
.fset summary .ph    { color: var(--t2); font-size: var(--text-xl); transition: transform .15s ease; }
.fset summary .hint  { margin-left: auto; color: var(--t2); font-size: var(--text-xs); font-weight: 600; }
.fset[open] summary  { border-bottom: 1px solid var(--line);                                       }
.fset[open] summary .ph { transform: rotate(90deg);                                                }

/* 채널 한 장 — 요율이 2.9% 와 15% 로 크게 갈려 한 줄에 나란히 두면 어느 쪽 얘긴지 헷갈린다 */
.fcard      { margin-bottom: 12px; padding: 13px 14px 12px; border: 1px solid var(--line); border-radius: 10px; }
.fcard_h    { display: flex; align-items: center; gap: 8px; margin-bottom: 11px;                   }
.fcard_ic   { display: grid; place-items: center; width: 26px; height: 26px; border-radius: var(--radius-sm); background: var(--card-hover); color: var(--t2); font-size: var(--text-lg); }
.fcard_h b  { font-size: var(--text-md); font-weight: 700;                                         }
.fcard_at   { margin-left: auto; color: var(--t2); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }

.fcard_row  { display: flex; align-items: flex-end; gap: 10px;                                     }
.fcard_rate { flex: 0 0 auto; display: flex; flex-direction: column; gap: 5px;                     }
.fcard_memo { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 5px;       }
.fcard_row label > span { color: var(--t1); font-size: var(--text-xs); font-weight: 600;           }

/* % 를 입력 안에 붙여 둔다 — 밖에 두면 저장 버튼과의 간격이 들쭉날쭉해진다 */
.fcard_in   { position: relative; display: inline-flex; align-items: center;                       }
.fcard_in i { position: absolute; right: 11px; color: var(--t3); font-size: var(--text-md); font-style: normal; pointer-events: none; }

/* ★ width: auto 가 필요하다 — .btn 이 width: 100% 라 flex 기준폭이 부모 전체가 되어
     옆의 근거 칸을 0 까지 밀어냈다(라벨 '근거'가 두 줄로 접혔다). */
.fcard_save { flex: none; white-space: nowrap;                                                   }  /* 크기·색은 .btn.wide.cta — 다른 화면 저장·추가 버튼과 같은 것(2026-09-05). 여기선 줄 안에서 안 줄어들게만 */

/* 입력한 요율이 실제로 얼마를 떼는지 — 퍼센트만으로는 2.9 와 15 의 무게 차이가 안 잡힌다 */
.fcard_eff     { margin: 10px 0 0; padding-top: 10px; border-top: 1px dashed var(--line2); color: var(--t2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.fcard_eff b   { color: var(--t1); font-weight: 700;                                               }
.fcard_eff.bad { color: var(--bad);                                                                }

/* ★ 선택자를 input.xxx 로 쓴다 — 전역 input[type="number"](0,1,1)가 .fee_input(0,1,0)을 이겨
     width 가 100% 로 남는다. 실제로 요율 칸이 넓게 퍼지고 근거 칸이 찌그러졌다. */
input.fee_input { width: 112px; height: 36px; padding-right: 30px; font-size: var(--text-lg); font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }
input.rfee_memo { width: 100%; min-width: 0; height: 36px; font-size: var(--text-md);               }

/* 좁아지면 상단이 먼저 무너진다 — 실수입 칸이 위로 올라서고 나머지는 3칸씩 두 줄 */
@media (max-width: 1240px) {
    .kpilead { grid-template-columns: minmax(0, 1fr);                                              }
    .kl      { border-right: 0; border-bottom: 1px solid var(--line);                              }
    .kpi6    { grid-template-columns: repeat(3, minmax(0, 1fr));                                   }
    .kpi6 .kc:nth-child(3n+1) { border-left: 0;                                                    }
    .kpi6 .kc:nth-child(n+4)  { border-top: 1px solid var(--line);                                 }
}

@media (max-width: 1100px) {
    .rgrid { grid-template-columns: minmax(0, 1fr);                                                }
}

/* ===== 차트 공통 ===== */

.legend2      { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 10px; color: var(--t2); font-size: var(--text-sm); }
.legend2 span { display: inline-flex; align-items: center; gap: 6px;                           }
.legend2 em   { width: 11px; height: 11px; border-radius: 2px; font-style: normal;             }
.legend2 b    { color: var(--t2); font-weight: 600;                                            }

.sw_net { background: var(--accent);                                                     }
.sw_gap { background: var(--bar-mid);                                                     }  /* 막대의 '평균 초과' 단과 같은 농도 */
.sw_ok  { background: var(--join);                                                        }  /* 회원 유입·탈퇴 범례 — 막대와 같은 변수 */
.sw_bad { background: var(--leave);                                                       }

/* ★ 선 모양 스와치는 .legend2 em(특정도 0,1,1)을 이겨야 한다. .sw_dash 단독은 (0,1,0) 이라
     파일 순서와 무관하게 width/height 11px 이 그대로 남아 점으로 보인다. em 을 붙여 (0,2,1) 로 올린다. */
.legend2 em.sw_dash { width: 14px; height: 0; border-top: 1px dashed var(--t3);           }
.legend2 em.sw_prev { width: 14px; height: 0; border-top: 2px solid var(--t2); opacity: .5; }

.dot_info { background: var(--info); }

.mini_kpi          { display: flex; border-bottom: 1px solid var(--line);                      }
.mini_kpi > div    { flex: 1; padding: 11px 15px; border-right: 1px solid var(--line);          }
.mini_kpi > div:last-child { border-right: 0;                                                   }
.mini_kpi .k       { display: flex; align-items: center; gap: 6px; color: var(--t1); font-size: var(--text-sm); font-weight: 600; }
.mini_kpi .k em    { width: 7px; height: 7px; border-radius: var(--radius-pill); font-style: normal; }
.mini_kpi .v       { margin-top: 3px; font-size: var(--text-3xl); font-weight: 700; letter-spacing: -.3px; font-variant-numeric: tabular-nums; }
.mini_kpi .v small { margin-left: 3px; color: var(--t3); font-size: var(--text-sm); font-weight: 500; }

/* 쌓기 막대 — 전체 높이가 총 결제액, 안쪽 진한 부분이 실수입.
   ★ 겉면에 opacity 를 쓰지 않는다. --accent 30% 는 다크 카드(#211C17) 위에서 탁한 갈색이 되고
     라이트에선 반대로 흐려 '빠지는 몫'이 안 읽힌다. 면은 --accent-soft, 경계는 --accent 헤어라인으로
     두면 라이트(#EE5239 on #FDEDE9)·다크(#E88A5C on #34241B) 양쪽에서 카드와 분리된다. */
.plot.tall  { height: 150px;                                                                   }
.bar_stack  {
    position     : relative;
    display      : flex;
    align-items  : flex-end;
    width        : 100%;
    max-width    : 34px;
    min-height   : 2px;
    border-radius: 2px 2px 0 0;
    background   : var(--accent-soft);
    box-shadow   : inset 0 0 0 1px var(--accent);
}
.bar_net    { position: relative; width: 100%; min-height: 2px; border-radius: 2px 2px 0 0; background: var(--chart); }
/* 라벨은 실수입 조각의 것이다 — 겉면 꼭대기에 두면 전액 환불된 달(조각 0)에 옅은 막대 위 "0" 이 떠서
   '빠지는 몫이 0' 으로 읽힌다(2026-09-05 chorong). 기본은 조각 바로 위, 조각이 글자 높이면 안(.lin). */
.bar_net b     { position: absolute; bottom: 100%; left: 0; right: 0; z-index: 2; margin-bottom: 3px; color: var(--accent-ink); font-size: var(--text-xs); font-weight: 700; font-variant-numeric: tabular-nums; text-align: center; white-space: nowrap; }
.bar_net.lin b { top: 4px; bottom: auto; margin-bottom: 0; color: var(--accent-on);                }

/* 회원 유입·탈퇴 — 0선을 데이터 비율(--up)에 둔다.
   ★ 예전엔 위 96px / 아래 44px 하드코딩이라 이탈이 유입보다 많은 달도 아래쪽이 항상 작게 그려졌다.
     지금은 위·아래가 같은 자(px/명)를 쓰고 0선이 실제 비율 위치로 움직인다. */
/* ★ 안쪽 트랙 이름은 pm 이 아니라 pm_bars 다. 바깥 래퍼가 class="plot pm" 이라 이름이 겹치면
   .pm(0,1,0) 이 .plot(0,1,0) 보다 뒤에 있다는 이유만으로 position 을 이겨 바깥 상자가
   absolute 가 된다 — 위치지정 조상이 없어 플롯이 통째로 페이지 좌상단으로 날아간다.
   수식자(.plot.tall / .plot.pm)로 쓰는 이름을 단독 클래스로 재사용하지 않는다. */
/* ★ 숫자 라벨은 막대 바깥에 붙는다(위 = bottom: 100%, 아래 = top: 100%) — 최댓값 막대는 좌표계 끝까지
     차므로 라벨이 밖으로 나간다. 아래는 월 축과('07월'과 '9'가 포개졌다), 위는 범례와('탈퇴'와 '26',
     2026-09-05) 겹쳤다. 양쪽 다 라벨 한 줄만큼 비워 둔다 — 범례의 10px 만으론 모자란다. */
.plot.pm    { height: 150px; margin: 15px 0; border-bottom: 0;                                 }
.grow .plot { flex: 1; height: auto; min-height: 150px;                                        }  /* 고정 150 은 하한으로 — .plot.tall/.pm 뒤에 와야 같은 특정도에서 이긴다 */
.pm_bars    { position: absolute; inset: 0; display: flex; gap: 4px;                            }
.pm_col     { flex: 1; min-width: 0; display: grid; grid-template-rows: var(--up, 50%) 1fr;      }
.pm_up      { display: flex; align-items: flex-end;   justify-content: center;                  }
.pm_dn      { display: flex; align-items: flex-start; justify-content: center;                  }
.pm_fill    { position: relative; width: 100%; max-width: 26px; font-style: normal;             }  /* <i> 라 기본 이탤릭 — 안의 숫자 라벨이 기울었다(2026-09-05) */
/* 색은 .chart 의 --join/--leave(시안 D 값) — 위/아래가 이미 들고 나감을 말하니 색은 거들 뿐이다.
   100% 로 두었더니 코랄 하나로 가는 이 화면에서 이 카드만 상태 배지처럼 튀었다(2026-09-05 chorong). */
.pm_fill.up   { border-radius: 2px 2px 0 0; background: var(--join);                             }
.pm_fill.down { border-radius: 0 0 2px 2px; background: var(--leave);                            }
.pm_fill span { position: absolute; left: 0; right: 0; color: var(--t2); font-size: var(--text-xs); font-weight: 600; font-variant-numeric: tabular-nums; text-align: center; }
.pm_fill.up   span { bottom: 100%; margin-bottom: 2px;                                          }
.pm_fill.down span { top: 100%; margin-top: 2px;                                                }
.plot_zero  { position: absolute; top: var(--up, 50%); left: 0; right: 0; z-index: 2; height: 1px; background: var(--line2); }

/* 취소율 라인 — 고정비 viewBox + aspect-ratio + 기본 preserveAspectRatio.
   ★ 예전 preserveAspectRatio="none" 은 패널이 세로보다 3배 넓어 기울기가 가로로 3배 늘어났다.
     좌표는 Java 가 만들어 넘긴다(FreeMarker 에서 ?index 나눗셈을 하지 않는다). */
.chart_line { display: block; width: 100%; aspect-ratio: 5 / 1; overflow: visible; }  /* 2열이라 폭이 넓다 — 3:1 이면 3점짜리 선에 200px 을 쓴다(2026-09-05) */
/* ★ overflow: visible — 최댓값 점은 y=0, 0 은 y=60 에 정확히 놓여(RevenueService.churnLineOf) 점 반지름 3.5 와 선 굵기가
     좌표계 밖으로 나간다. SVG 기본은 hidden 이라 꼭대기 점이 반 잘렸다(2026-09-05 chorong). 좌표계에 여백을 넣는 대신
     넘치게 둔다 — 비율(5:1)을 건드리면 aspect-ratio 와 어긋나 기울기 왜곡이 되살아난다. */

/* 취소율 — 값 한 줄 + 오른쪽에 작은 선. 6개월치 점 몇 개짜리 추세라 큰 차트가 필요 없다(2026-09-05).
   ★ .chart 를 세로 flex 에서 가로로 되돌린다 — .chart.churnrow(0,2,0) 가 .chart(0,1,0) 를 이긴다. */
.churnrow       { flex-direction: row; align-items: center; gap: 16px;                             }
.cmeta          { flex: none; display: inline-flex; align-items: baseline; gap: 8px; margin-right: 12px; }
.cmeta b        { font-size: var(--text-3xl); font-weight: 700; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
.cmeta .up      { color: var(--ok);  font-size: var(--text-sm); font-weight: 700;                  }
.cmeta .dn      { color: var(--bad); font-size: var(--text-sm); font-weight: 700;                  }
.cmeta i        { color: var(--t2); font-size: var(--text-xs); font-style: normal;                 }
.churnrow .chart_line { flex: 1 1 300px; max-width: 380px; margin-left: auto;                      }  /* 비율이 폭↔높이를 묶는다 — 폭이 곧 크기 손잡이다(300~380px → 60~76px) */
.line_grid  { stroke: var(--line);  stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.line_base  { stroke: var(--line2); stroke-width: 1; vector-effect: non-scaling-stroke;          }
.line_avg   { stroke: var(--t3);    stroke-width: 1; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }
.line_stroke {
    fill              : none;
    stroke            : var(--chart);
    stroke-width      : 2;
    stroke-linejoin   : round;
    stroke-linecap    : round;
    vector-effect     : non-scaling-stroke;
}
.line_pt    { fill: var(--chart); stroke: var(--card); stroke-width: 1.5; vector-effect: non-scaling-stroke; }


/* 가로 막대 — 누적 눈금(u)을 얹어 상위 몇 개가 전체의 얼마인지 바로 읽는다 */
/* 패널 본문 위 한 줄짜리 보조 수치. 범례(.legend2)와 구분한다 — 스와치가 붙으면 아래 차트의
   범례로 읽히는데, 실제로는 다른 지표라 색도 내용도 안 맞는다. */
.panel_meta        { margin-bottom: 11px; color: var(--t2); font-size: var(--text-sm);           }
.panel_meta b      { color: var(--t1); font-weight: 600; font-variant-numeric: tabular-nums;     }
.panel_meta span   { margin: 0 4px; color: var(--line2);                                         }

/* 한 패널에 비중 묶음이 둘 이상일 때 — 분모가 다르므로 선으로 끊고 기준을 적는다.
   기준을 안 적으면 100% 가 무엇의 100% 인지 알 수 없다. */
.hgroup + .hgroup { margin-top: 15px; padding-top: 14px; border-top: 1px solid var(--line);     }
.hgroup_t   { margin-bottom: 9px; color: var(--t1); font-size: var(--text-sm); font-weight: 700; }

/* 카드 안 소제목 — 한 카드에 두 덩어리를 둘 때 경계를 만든다(해지: 취소율 위 · 사유 아래) */
.hsub       { display: flex; align-items: baseline; gap: 7px; margin: 16px 0 9px; padding-top: 14px; border-top: 1px solid var(--line); color: var(--t1); font-size: var(--text-sm); font-weight: 700; }
.hsub span  { color: var(--t2); font-size: var(--text-xs); font-weight: 500; font-variant-numeric: tabular-nums; }
.hsub.chsub { margin-top: 0; padding-top: 0; border-top: 0;                                        }  /* 카드 첫 소제목 — 위 구분선이 카드 머리와 겹치고, 여백은 본문 패딩이 이미 준다 */

.hbars      { display: flex; flex-direction: column; gap: 8px;                                  }
.hbar       { display: grid; grid-template-columns: 92px minmax(0, 1fr) 76px; align-items: center; gap: 10px; font-size: var(--text-md); }
.hbar .lb   { color: var(--t1); font-weight: 600; white-space: nowrap;                          }
.hbar .tr   { position: relative; height: 14px; border-radius: 2px; background: var(--card-hover); }
/* 기본은 무채다. 대비되는 두 줄에서 '주(主)'가 되는 쪽에만 .lead 로 accent 를 준다 —
   둘 다 색을 주면 무엇을 먼저 봐야 할지가 사라진다. */
.hbar .tr i      { display: block; height: 100%; border-radius: 2px 0 0 2px; background: var(--t3); }
.hbar.lead .tr i { background: var(--accent);                                                   }
/* 누적 위치 눈금. --accent-soft 를 --card-hover 위에 깔면 라이트 1.00:1 · 다크 1.04:1 로 안 보인다 */
.hbar .tr u { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--t3); text-decoration: none; }
.hbar .pc   { color: var(--t1); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.hbar .pc small { color: var(--t2); font-weight: 500;                                           }

.hint_box       { display: flex; align-items: flex-start; gap: 7px; margin-top: 14px; color: var(--t3); font-size: var(--text-sm); }
.hint_box .ph   { color: var(--warn); font-size: var(--text-2xl);                               }
.hint_box code  { color: var(--accent); font-family: var(--mono);                               }

.grid tfoot td { border-top: 1px solid var(--line2); border-bottom: 0; background: var(--soft); color: var(--t1); font-weight: 700; }

/* ===== 미디어 =====
   D 조합안(2026-09-03, doc/preview/media-d-perfect.html) 을 실피드백으로 다듬은 판 — 필터 카드(신고 큐 .fbar 문법, 판정 스트립·칩은 여기로 흡수)
   → 10열 × 4줄 그리드 | 검토 서랍(420px, 고르기 전에도 빈 상태로 서 있다 — 폭이 변하지 않아야 타일을 누를 때 그리드가 안 튄다).
   서랍은 헤더·처리를 위아래에 고정하고 무대·글 정보(.dr_body)만 안에서 스크롤한다 — 사유·확정·이전/다음이 어느 높이에서도 보인다.
   묶음 배지는 좌하단 — 서랍이 차지한 폭에서 타일이 ≈105px 라 좌상단이 우상단 상태 태그와 겹친다(시안 실측). */

/* 필터 카드는 신고 큐 .fbar.slim 그대로. 토글(신고 많은 순·초기화)만 오른쪽 끝으로 — slim 은 flex 라 grid-column 이 안 먹는다 */
.media_body .fbar_r { margin-left: auto; margin-top: 0;                                            }

/* 한 화면 — 스크롤 없이 페이징까지 들어온다(chorong 2026-09-03). 본문(.media_body)이 뷰포트 높이를 채우고, 그리드가 남는 높이를
   4줄로 나눈다(10열 × 4줄 = 한 페이지 40장). 타일은 줄 높이를 따라 줄어들고 사진은 cover 로 채운다 — 창이 낮아도 스크롤이 안 생긴다. */
.media_body  { height: calc(100% - var(--head-height)); min-height: 0;                              }
.deck        { flex: 1; min-height: 0; display: flex; align-items: stretch; gap: 14px;              }
.deck .list  { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column;            }
.pgrid       { flex: 1; min-height: 0; display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); grid-template-rows: repeat(4, minmax(0, 1fr)); grid-auto-rows: minmax(0, 1fr); gap: 10px; }
@media (max-width: 1500px) { .pgrid { grid-template-columns: repeat(8, minmax(0, 1fr)); } }  /* 좁은 창 — 열이 줄고 줄이 늘어도 높이는 나눠 쓴다 */
@media (max-width: 1200px) { .pgrid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.mempty      { flex: 1; display: grid; place-items: center; color: var(--t3); font-size: var(--text-md); }  /* 0건 — 그리드 자리를 그대로 채운다 */

/* 타일 — 썸네일 + 묶음 배지 + 상태 태그 / 닉네임 · 날짜 · 출처. 처리 버튼은 없다(서랍이 처리) */
.ptile       { position: relative; display: flex; flex-direction: column; min-height: 0; margin: 0; padding: 5px; border: 1.5px solid var(--line); border-radius: 12px; background: var(--card); box-shadow: var(--shadow); cursor: pointer; }
.ptile:hover { background: var(--card-hover);                                                       }
.ptile.need  { border-color: var(--warn);                                                           }  /* 처리 필요 — 스캔의 첫 단서 */
.ptile.kin   { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft);               }
.ptile.sel   { padding: 4.5px; border-width: 2px; border-color: var(--accent);                      }  /* 링을 그림자 목록에 섞지 않는다 — 다크 --shadow: none 과 합치면 선언 전체가 무효 */
.pgrid.focus .ptile:not(.kin) { opacity: .35;                                                       }

.pth         { position: relative; flex: 1; min-height: 0;                                          }
.ptile_open  { position: relative; display: block; width: 100%; height: 100%; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; font-family: inherit; overflow: hidden; cursor: pointer; }
.ptile.sel .ptile_open { cursor: zoom-in;                                                           }  /* 열린 타일 재클릭 = 확대 */
.ptile_img   { display: block; width: 100%; height: 100%; object-fit: cover;                       }  /* 줄 높이에 맞춰 cover — 비율은 확대 보기가 살린다 */
.ptile_img.off { opacity: .35; filter: grayscale(1);                                                }  /* 이미 가려진 사진 — 처리 완료를 한눈에 */
.ptile_ph    { display: grid; place-items: center; height: 100%; background: var(--card-hover); color: var(--t3); font-size: 26px; }  /* 썸네일 서명 실패 자리 — 1회성 (스케일 밖) */
/* 영상 타일 — 썸네일이 정지 이미지라 표식이 없으면 사진과 구분이 안 된다 */
.ptile_play  { position: absolute; left: 50%; top: 50%; display: grid; place-items: center; width: 38px; height: 38px; transform: translate(-50%, -50%); border-radius: var(--radius-pill); background: rgba(0, 0, 0, .5); color: #fff; font-size: var(--text-xl); }
.pstack      { position: absolute; left: 5px; bottom: 5px; display: inline-flex; align-items: center; gap: 2px; height: 22px; padding: 0 5px; border: 0; border-radius: var(--radius-sm); background: var(--ink); color: var(--ink-on); font-family: inherit; font-size: var(--text-xs); font-weight: 600; opacity: .88; cursor: pointer; }
.pstack .ph  { font-size: var(--text-sm);                                                           }
.ptag        { position: absolute; right: 5px; top: 5px; padding: 2px 6px; box-shadow: 0 0 0 1px var(--card); }
.tag.rep     { background: var(--warn-bg); color: var(--warn);                                      }  /* 신고·노출중 = 처리 필요. 신고 큐의 .tag.pend(info, 처리 상태)와 뜻이 달라 따로 둔다 */

.pft         { flex: none; display: flex; flex-direction: column; gap: 3px; margin: 0; padding: 6px 1px 2px; }
.pnick       { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--t1); font-size: var(--text-sm); font-weight: 600; }
.pft_meta    { display: flex; align-items: center; gap: 5px; min-height: 20px;                      }
.pft_meta time { flex: none; color: var(--t2); font-size: var(--text-xs); font-weight: 500; font-variant-numeric: tabular-nums; }
.pft_meta .tag { min-width: 0; overflow: hidden; text-overflow: ellipsis;                           }

/* 검토 서랍 — 헤더(고정) · .dr_body(무대 + 글 정보, 유일한 스크롤 영역) · .dr_act(사유 · 확정 · 이전/다음, 하단 고정) */
.drawer         { flex: 0 0 420px; display: flex; flex-direction: column; padding: 0; overflow: hidden; }  /* .deck 높이(= 그리드)를 그대로 채운다 — 페이징은 .deck 밖이라 서랍이 그리드보다 아래로 안 내려간다 */
/* 빈 상태(고르기 전) — 서랍을 치우지 않는다. 폭이 늘 같아야 타일을 누른 순간 그리드가 튀지 않는다.
   해제·확대는 고른 사진이 있을 때만 뜻이 있어 표식을 감춘다(잠금은 JS 가 버튼 disabled 로 건다). */
.drawer.empty .dr_x,
.drawer.empty .ink_zoom { display: none;                                                            }
.drawer.empty .big      { cursor: default;                                                          }
.dr_hd          { display: flex; align-items: center; gap: 8px; padding: 11px 12px 11px 17px; border-bottom: 1px solid var(--line); background: var(--card); }
.dr_hd b        { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--t1); font-size: var(--text-lg); font-weight: 700; }
.dr_hd .tag     { flex: none;                                                                       }
.dr_hd .prog    { flex: none; color: var(--t1); font-size: var(--text-xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.dr_x           { flex: none; display: grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--t1); font-size: var(--text-xl); font-family: inherit; cursor: pointer; }
.dr_body        { flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--line2) transparent; }

/* 무대 — 신고 큐 잉크 무대와 같은 톤. 이름은 다르다: .ink 는 열람 확인 절차가 있는 신고 큐 것이고 미디어엔 그 절차가 없다 */
.dr_stage       { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 16px; background: var(--ink); color: var(--ink-on); }
.big            { position: relative; display: block; max-width: 100%; padding: 0; border: 0; background: none; font-family: inherit; cursor: zoom-in; }
.big img        { display: block; max-width: 100%; max-height: 180px; border-radius: 10px; object-fit: contain; }  /* 무대 높이 180 통일 — 820 에서 처리 폼까지 한 화면에. 자세히는 확대 보기 */
.big img.off    { opacity: .5; filter: grayscale(1);                                                }
.big .big_ph    { display: grid; width: 180px; height: 180px; border-radius: 10px; background: rgba(255, 255, 255, .06); color: var(--ink-on); }
.big .ptile_play { width: 56px; height: 56px; font-size: var(--text-title);                         }
.big .ink_zoom  { pointer-events: none;                                                             }  /* 버튼 안의 표식 — 클릭은 .big(data-zoom) 이 받는다 */
.big > [hidden] { display: none;                                                                    }  /* display 를 명시한 자식(.ptile_play grid · img block)은 hidden 속성만으론 안 숨는다 */

/* 같은 글 필름 — 체크하면 함께 처리 */
.dr_stage .ink_film { display: flex; flex-wrap: wrap; align-items: center; align-self: flex-start; gap: 8px; }
.fitem          { position: relative; flex: none;                                                   }
.fitem button   { display: block; width: 44px; height: 44px; padding: 0; border: 2px solid transparent; border-radius: 10px; background: none; overflow: hidden; cursor: pointer; }
.fitem button.on { border-color: var(--accent);                                                     }
.fitem img,
.fitem .ptile_ph { display: block; width: 100%; height: 100%; border-radius: 6px; object-fit: cover; font-size: var(--text-md); }
.fitem img.off  { opacity: .35; filter: grayscale(1);                                               }
.fchk           { position: absolute; left: 5px; top: 5px; width: 16px; height: 16px; margin: 0; accent-color: var(--accent); cursor: pointer; }
.ink_film .ink_cnt { margin-left: 2px;                                                              }
.dr_stage .ink_film[hidden] { display: none;                                                        }

/* 정보 · 처리 */
.dr_sec         { padding: 14px 17px 16px; border-top: 1px solid var(--line);                       }
.dr_sec h4      { margin: 0 0 10px; color: var(--t1); font-size: var(--text-md); font-weight: 700;  }
.dr_sec .tri_txt { color: var(--t1); font-weight: 500;                                              }
.dr_sec .tri_dl  { grid-template-columns: 56px minmax(0, 1fr); margin-top: 12px;                    }
.dr_sec .tri_dl dd { font-weight: 500;                                                              }
.dr_sec .tri_dl dd .dnick,
.dr_sec .tri_dl dd .tag { max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }  /* 긴 닉네임·그룹명 한 줄 말줄임 */
.qlink          { display: inline-flex; align-items: center; gap: 5px; margin-top: 12px; color: var(--accent-ink); font-size: var(--text-sm); font-weight: 600; text-decoration: none; }
.qlink[hidden]  { display: none;                                                                    }
.qlink .ph      { font-size: var(--text-lg);                                                        }
.dr_none        { margin: 0; color: var(--t1); font-size: var(--text-sm); font-weight: 500;         }  /* 처리 불가 사유 — 읽고 판단하는 안내문이라 t1 */

/* 사유 폼 — 셀렉트 + '기타' 맨 밑 + 확정(필수값 차기 전 비활성) */
.rform          { display: flex; flex-direction: column; gap: 8px;                                  }
.rform[hidden]  { display: none;                                                                    }
.rform_lab      { display: flex; align-items: baseline; gap: 6px; color: var(--t1); font-size: var(--text-sm); font-weight: 600; }
.rform_lab span { color: var(--t2); font-size: var(--text-xs); font-weight: 500;                    }
.rform c-select { width: 100%;                                                                      }
.rform c-select::part(button) { height: 36px; border-radius: 10px;                                  }
.rform_etc      { width: 100%; height: 36px; padding: 0 11px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-family: inherit; font-size: var(--text-md); }
.rform_etc::placeholder { color: var(--t3);                                                         }
.rform_btns     { display: flex; gap: 7px;                                                          }
.rform_btns button { flex: 1; height: 36px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-family: inherit; font-size: var(--text-md); font-weight: 600; cursor: pointer; }
.rform_btns .rform_ok { border-color: transparent; background: var(--accent); color: var(--accent-on); }
.rform_btns .rform_ok.danger { background: var(--bad-bg); color: var(--bad);                        }  /* 숨김은 되돌릴 수 있지만 사진이 사라지는 동작 — 복원(코랄)과 색으로 구분 */
.rform_btns .rform_ok:disabled { opacity: .45; cursor: not-allowed;                                 }

.dr_nav         { display: flex; gap: 7px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.dr_nav button  { flex: 1; height: 36px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-family: inherit; font-size: var(--text-md); font-weight: 600; cursor: pointer; }
.dr_nav button:disabled { color: var(--t3); cursor: not-allowed;                                    }

.note_box      { padding: 13px 15px; border: 1px solid var(--line); border-left: 3px solid var(--warn); border-radius: var(--radius-sm); background: var(--card); color: var(--t2); font-size: var(--text-md); }
.note_box b    { color: var(--t1); font-weight: 600;                                          }
.note_box code { color: var(--accent); font-family: var(--mono); font-size: var(--text-sm);    }

/* ===== 신고 큐 =====
   목록에서 훑다가 한 건을 눌러 트리아지로 들어간다. 두 모드가 한 화면에 있다.

   ★ 실제 큐가 이 배치를 정했다. 신고 대상이 사실상 '사진'이고(본문이 없거나 몇 글자),
     판단 재료가 사진이라 무대에 제일 넓은 칸을 준다 — 그래서 트리아지가 3열이다.
     비율은 가정하지 않는다. 캡처·촬영본이 섞여 가로도 세로도 온다(contain 으로 받는다). */

/* 필터 — 세 줄이면 화면 위쪽을 다 먹으니 한 줄에 모으되, 축마다 세그먼트로 묶는다.
   알약을 죽 늘어놓으면 같은 무게가 아홉 개라 상태·출처·대상 경계가 안 보인다. */
/* 필터 카드 — 축이 넷 + 토글 둘이라 캔버스에 흩어놓으면 덩어리가 안 잡힌다. 리스트와 같은 흰 카드로 묶는다.
   ★ flex-wrap 이 아니라 2열 그리드다 — 넓은 화면에서 전부 한 줄로 늘어서면 축 경계가 안 보인다.
     상태·출처 / 대상·사유 / 토글 줄, 이 배치가 화면 폭과 무관하게 유지된다. */
/* margin-bottom 을 두지 않는다 — 아래 간격은 .pbody 의 gap 이 만든다(겹치면 이중 여백) */
.fbar      { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: center; gap: 12px 28px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); }
.fg        { display: flex; align-items: center; gap: 9px; min-width: 0;                           }
.fg b      { flex: 0 0 auto; color: var(--t1); font-size: var(--text-md); font-weight: 700;        }  /* 축 이름은 구조 라벨 — 흐린 글씨는 부수 정보에만 쓴다 */

/* 다중선택 칩 — 서비스 .hc_chip 문법. 코랄 솔리드는 단일선택(.hc_filter_tab) 문법이라 쓰지 않는다 */
.seg       { display: inline-flex; gap: 6px; flex-wrap: wrap;                                      }
.seg a,
.seg button { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 13px; border-radius: 10px; background: var(--soft); color: var(--t2); font-size: var(--text-sm); font-weight: 600; text-decoration: none; transition: background-color .12s; }
/* 주소를 바꾸지 않는 칩은 button 이다 — 링크가 아니면서 링크처럼 보이면 새 탭·뒤로가기가 거짓말을 한다 */
.seg button { border: 0; font-family: inherit; cursor: pointer;                                    }
.seg a i,
.seg button i { font-style: normal; font-size: var(--text-xs); font-variant-numeric: tabular-nums; opacity: .6; }

/* 선택된 칩 — 서비스 .hc_chip 선택 문법(흰 배경 + 코랄 테두리 + 코랄잉크).
   '전체'도 선택이면 똑같이 표시한다 — 필터 미적용도 "전체를 골랐다"로 읽게 한다. */
.seg a.on,
.seg button.on   { background: var(--card); color: var(--accent-ink); box-shadow: inset 0 0 0 1.5px var(--accent); }
.seg a.on i,
.seg button.on i { opacity: .85;                                                                   }

/* 0 건인 축 — 지우지 않고 무게만 낮춘다. 걸 수 있는 축과 같은 굵기로 서 있으면 훑을 것이 더 있어 보인다 */
.seg a.zero { color: var(--t3); background: transparent; box-shadow: inset 0 0 0 1px var(--line);   }

/* 배타 선택이 아니라 켜고 끄는 토글 — 점선으로 모양부터 구분한다 */
/* 보기 토글 — 필터 칩(무엇을 볼지)과 성질이 달라 자기 줄로 뗀다. 서비스 스위치 문법 축소판. */
.fbar_r    { grid-column: 1 / -1; display: flex; align-items: center; gap: 22px; margin-top: 2px;  }

/* slim 변형 — 필터를 한 줄에 세우는 화면(회원 관리·공지). 그리드 2열 대신 한 줄 flex, 상하도 얇게 */
.fbar.slim { display: flex; flex-wrap: wrap; gap: 12px 22px; padding-top: 8px; padding-bottom: 8px; }

@media (max-width: 1100px) {
    .fbar { grid-template-columns: minmax(0, 1fr);                                                 }
}
.ftog      { display: inline-flex; align-items: center; gap: 8px; color: var(--t2); font-size: var(--text-md); font-weight: 600; text-decoration: none; }
.ftog em   { position: relative; width: 34px; height: 20px; border-radius: var(--radius-pill); background: var(--line2); transition: background-color .15s; font-style: normal; }
.ftog em::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: var(--radius-pill); background: var(--card); box-shadow: 0 1px 2px rgba(0, 0, 0, .18); transition: left .15s; }
.ftog.on        { color: var(--t1);                                                                }
.ftog.on em     { background: var(--accent);                                                       }
.ftog.on em::after { left: 16px;                                                                   }

/* 필터 초기화 — 걸린 필터가 있을 때만 fbar 오른쪽 끝에 나온다.
   밑줄은 글자에만 준다 — 링크라는 신호는 남기되 아이콘까지 그어지면 아이콘이 글자처럼 보인다 */
.freset      { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; color: var(--t2); font-size: var(--text-sm); font-weight: 600; text-decoration: none; }
.freset .ph  { font-size: var(--text-md);                                                          }

/* 경과 배지 — 지금 큐의 진짜 신호다. 하루라도 넘기면 색이 붙는다 */
.age       { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--card-hover); color: var(--t2); font-size: var(--text-xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.age.old   { background: var(--warn-bg); color: var(--warn);                                       }
/* 우측 시간 칸 — 접수일 위, 경과 배지 아래. flex-end 라 배지는 글자만큼만 차지한다 */
.qwhen        { display: flex; flex-direction: column; align-items: flex-end; gap: 3px;            }
.qwhen .qdate { color: var(--t1); font-size: var(--text-xs); font-variant-numeric: tabular-nums;   }

/* ── 목록 ── */

/* C안(리치 2줄) — 배지는 본문 아래 메타 줄로, 오른쪽엔 경과·셰브론만. 서비스 리스트 카드처럼 soft 그림자(다크는 토큰이 끔) */
.qlist     { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--card); box-shadow: var(--shadow); }
.qrow      { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto 20px; gap: 15px; align-items: center; padding: 13px 15px 13px 19px; border-bottom: 1px solid var(--line); color: inherit; text-decoration: none; transition: background-color .12s; }
.qrow:last-child { border-bottom: 0;                                                               }

/* A2 — 행 왼쪽 상태 스트립. 배지를 읽기 전에 색으로 대기/조치가 갈린다(스트립은 훑기용, 정보 정본은 배지) */
.qrow::before              { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }
.qrow.st_PENDING::before   { background: var(--warn);                                              }
.qrow.st_REVIEWED::before  { background: var(--info);                                              }
.qrow.st_ACTIONED::before  { background: var(--ok);                                                }
.qrow.st_DISMISSED::before { background: var(--line2);                                             }

/* 썸네일은 잠긴 상태다 — 목록에서 사진을 부르면 큐를 열 때마다 열람 감사로그가 쌓인다 */
/* 사진 장수 칩 — 실물 썸네일은 열람 기록 때문에 목록에 두지 않는다(상세에서 연다) */
.qpic      { display: inline-flex; align-items: center; gap: 3px; padding: 2px 7px; border-radius: var(--radius-pill); background: var(--card-hover); color: var(--t2); font-size: var(--text-xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.qpic i    { font-size: var(--text-sm);                                                            }

.qbody     { min-width: 0;                                                                         }
.t1row     { display: flex; align-items: center; gap: 8px;                                         }
.t1row b   { font-size: var(--text-lg); font-weight: 600; letter-spacing: -.2px;                   }
.qbody .x  { display: block; margin: 3px 0 7px; color: var(--t2); font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qbody .x.none { color: var(--t3); font-style: italic;                                             }

.metarow   { display: flex; align-items: center; gap: 5px; flex-wrap: wrap;                        }
.metarow .nick   { margin-right: 4px; color: var(--t2); font-size: var(--text-sm); font-weight: 600; }
.metarow .nick i { color: var(--t2); font-style: normal; font-weight: 500;                         }
.qgo       { color: var(--t3); font-size: var(--text-2xl);                                         }

/* ── 트리아지 ── */

/* B+C 조합(2026-08-28) — 케이스 헤더 + 2열(무대 | 정보·처리). 큐 레일은 뺐다:
   판단 재료가 사진이라 무대 폭이 곧 처리 품질이고, '오래된 순 하나씩' 소화는 헤더의
   이전/다음(←→)이 대신한다. 건너뛰기는 리치 2줄 목록이 한 클릭 거리라 레일과 역할이 겹쳤다. */
.tri       { display: grid; grid-template-columns: minmax(0, 1fr) 396px; gap: 14px; align-items: start; }

/* 케이스 헤더 — 사진을 보기 전에 '무엇에 대한 몇 번째 건인지'부터 잡는다. 왼쪽 요약 · 오른쪽 진행 */
.tri_top     { display: flex; align-items: center; gap: 9px; padding: 13px 18px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); flex-wrap: wrap; }
.tri_top > b { font-size: var(--text-xl); font-weight: 700; letter-spacing: -.2px;                 }
.tri_top .no { color: var(--t2); font-size: var(--text-sm); font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums; }
.tri_top .r  { margin-left: auto; display: flex; align-items: center; gap: 13px;                   }

/* ★ tri_nav 다 — 사이드바가 이미 .nav 를 쓴다. 여기서 .nav 를 쓰면 `.nav { flex-direction: column }`
     이 새 나가 앞뒤 버튼이 세로로 쌓이고, `.nav a { height: 42px; padding: 0 12px }` 까지 따라온다. */
.tri_nav   { display: flex; gap: 5px;                                                              }
.tri_nav a,
.tri_nav .off { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t2); font-size: var(--text-xl); text-decoration: none; }
.tri_nav .off { color: var(--line2);                                                               }

.prog      { display: flex; align-items: center; gap: 9px; color: var(--t2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.prog em   { display: block; width: 130px; height: 4px; border-radius: var(--radius-pill); overflow: hidden; background: var(--soft); }
.prog em i { display: block; height: 100%; background: var(--accent);                              }

/* 잉크 무대 — 밝은 카드 위에서는 사진이 배경과 섞이고 잠금 상태가 '빈 회색 상자'로 보인다.
   인증 화면이 이미 쓰는 --ink 라 새 색이 아니다. */
/* 무대가 단독 칸이다(헤더는 tri_top 으로 나갔다). min-height 는 잠금·사진 없음 상태의 바닥값 —
   오른쪽 칸(정보+처리)과 어깨가 맞을 만큼. 사진이 크면 내용대로 더 자란다. */
.ink       { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 13px; min-height: 620px; padding: 18px; border-radius: var(--radius); background: var(--ink); color: var(--ink-on); box-shadow: var(--shadow); }
.ink_lock  { display: grid; justify-items: center; gap: 12px; margin: 0; text-align: center;       }
.ink_lock .ic { display: grid; place-items: center; width: 52px; height: 52px; border-radius: var(--radius-pill); background: rgba(255, 255, 255, .09); font-size: 24px; }
.ink_lock p   { margin: 0; max-width: 400px; opacity: .66; font-size: var(--text-sm);              }

/* 서명이 한 장도 안 온 상태 — 안내문을 흐리게 두면 안 된다. 여기서 흘려보면 사진을 못 본 채로 처리된다.
   ★ 글자는 붉히지 않는다. 어두운 잉크 위에서 --bad 는 대비가 떨어져 오히려 덜 읽힌다 —
     경고는 아이콘 칩이 달고, 문구는 무대에서 제일 밝게 둔다. */
.ink_lock.fail .ic { background: var(--bad-bg); color: var(--bad);                                 }
.ink_lock.fail p   { font-size: var(--text-md); font-weight: 600; opacity: 1;                      }
.ink_btn   { display: inline-flex; align-items: center; gap: 7px; height: 36px; padding: 0 18px; border: 0; border-radius: var(--radius-pill); background: var(--accent); color: var(--accent-on); font-size: var(--text-md); font-weight: 700; font-family: inherit; cursor: pointer; }
/* ★ 무대 위에 겹쳐 놓지 않는다 — 사진이 무대를 꽉 채우는 게 정상이라 겹치면 글자가 사진에 묻힌다 */
.ink_cnt   { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: var(--radius-pill); background: rgba(255, 255, 255, .1); font-size: var(--text-xs); font-weight: 600; }

/* 사진은 contain 으로 넣는다 — 올라오는 비율이 제각각(캡처·촬영본)이라 cover 로 채우면
   증거의 가장자리가 잘린다. 판단 재료를 자르는 건 여기서 제일 하면 안 되는 일이다. */
.ink_row   { display: flex; align-items: center; justify-content: center; gap: 12px; width: 100%; flex-wrap: wrap; }
.ink_one   { position: relative; margin: 0; max-width: 340px; border-radius: 6px; overflow: hidden; background: rgba(255, 255, 255, .06); }
.ink_one.off { opacity: .45;                                                                       }
.ink_one img { display: block; width: 100%; max-height: 520px; object-fit: contain;                }
.ink_one figcaption { display: flex; align-items: center; gap: 6px; padding: 7px 9px; color: var(--ink-on); font-size: var(--text-xs); opacity: .85; flex-wrap: wrap; }
.ink_fail  { display: grid; place-items: center; gap: 5px; width: 200px; height: 260px; color: var(--ink-on); font-size: var(--text-sm); opacity: .7; }
.ink_zoom  { position: absolute; right: 7px; top: 7px; display: grid; place-items: center; width: 30px; height: 30px; padding: 0; border: 0; border-radius: var(--radius-sm); background: rgba(0, 0, 0, .45); color: #fff; font-size: var(--text-lg); font-family: inherit; cursor: zoom-in; }

/* 오른쪽 — 정보 · 처리 */
.tri_side  { display: flex; flex-direction: column; gap: 14px;                                     }
.tri_side .card { padding: 16px 18px;                                                              }
/* h4 는 눈썹 라벨로 낮춘다 — 카드의 주인공은 제목·본문이지 '신고 내용'이라는 구획명이 아니다 */
.tri_side .card h4 { margin: 0 0 12px; color: var(--t1); font-size: var(--text-md); font-weight: 700; }  /* 계정·구독(h5)과 같은 타이틀 톤 */

.ptitle    { margin-bottom: 5px; color: var(--t1); font-size: var(--text-xl); font-weight: 700;    }
.tri_txt   { color: var(--t2); font-size: var(--text-md); line-height: 1.65; white-space: pre-wrap; word-break: break-word; }
.tri_txt.none { color: var(--t3); font-style: italic;                                              }

.rdetail   { margin-top: 11px; padding: 10px 12px; border-left: 3px solid var(--line2); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; background: var(--card-hover); color: var(--t2); font-size: var(--text-sm); }
.rdetail b { display: block; margin-bottom: 3px; color: var(--t1); font-size: var(--text-xs);      }

.tri_dl    { display: grid; grid-template-columns: 62px minmax(0, 1fr); gap: 7px 12px; margin: 14px 0 0; padding-top: 13px; border-top: 1px solid var(--line); font-size: var(--text-md); }
.tri_dl dt { color: var(--t1);                                                                     }
.tri_dl dd { display: flex; align-items: center; gap: 5px; margin: 0; flex-wrap: wrap;             }

/* 2열 그리드 — 세로 한 줄 스택은 회색 벽처럼 단조롭고, 반폭(≈180px)에도 다섯 글자 라벨이 넉넉하다.
   주 조치(pri)만 전폭으로 눈에 먼저 들어오게 한다. */
.tri_btns  { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 11px;            }
.tri_btns button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; width: 100%; height: 38px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-size: var(--text-md); font-weight: 600; font-family: inherit; cursor: pointer; }
.tri_btns button.pri    { grid-column: 1 / -1; border-color: transparent; background: var(--accent-soft); color: var(--accent-ink); }  /* 솔리드 코랄은 벽처럼 진하다 — 틴트 + 잉크 */
.tri_btns button.danger { border-color: transparent; background: var(--bad-bg); color: var(--bad); }
.tri_btns button:only-child { grid-column: 1 / -1;                                                 }  /* 버튼이 하나뿐이면(회원 정지/해제) 반폭으로 뜨지 않게 */
.tri_btns button[hidden] { display: none;                                                          }  /* display 를 갖는 버튼이라 [hidden] 기본값이 덮인다 — 다시 꺼 준다(.dbrief·.qlink 와 같은 함정) */

/* 처리 폼 — 매출·미디어(.ptile_foot)가 쓰는 .act_form 회색 박스와 달리 여기는 카드가 곧 폼이다 */
.tri_side .act_form { width: 100%; padding: 0; border: 0; background: transparent;                 }
.tri_side .rfoot    { margin: 11px 0 0;                                                            }
.tri_side .act_form > input[type="text"] { width: 100%; height: 38px; padding: 0 12px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-size: var(--text-md); font-family: inherit; }
.tri_side .act_btns  { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line);     }
.tri_side .act_btns select,
.tri_side .act_btns c-select { flex: 1; height: 34px;                                              }
.tri_side .act_btns button { height: 34px;                                                         }

.audit_note     { display: flex; align-items: center; gap: 6px; margin-top: 11px; color: var(--t3); font-size: var(--text-xs); }
.audit_note .ph { font-size: var(--text-md);                                                       }

.mini      { flex: 0 0 auto; height: 23px; padding: 0 8px; border: 1px solid rgba(255, 255, 255, .3); border-radius: var(--radius-sm); background: rgba(255, 255, 255, .1); color: var(--ink-on); font-size: var(--text-xs); font-weight: 600; font-family: inherit; white-space: nowrap; cursor: pointer; }

/* 2열이 안 들어가면 세로로 쌓인다. 이때 처리 칸은 화면 너비를 다 쓰므로 버튼 그리드도
   고정 2열 대신 폭만큼 칸을 늘린다(auto-fill) — 전폭에 반쪽짜리 버튼 두 개만 떠 있으면 허전하다. */
@media (max-width: 1180px) {
    .tri  { grid-template-columns: minmax(0, 1fr);                                                 }
    .qrow { grid-template-columns: minmax(0, 1fr) auto;                                            }
    .qgo  { display: none;                                                                         }

    .tri_btns        { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));               }

    /* 감사 로그 — 열을 쌓으면 높이를 못박을 기준이 사라진다. 패널만 제 상한을 갖는다 */
    .alog_split      { grid-template-columns: minmax(0, 1fr); height: auto;                        }
    .alog_pane       { max-height: 60vh;                                                           }
}

/* ===== 페이징 ===== */

/* 알약 버튼 — 글자 링크는 목록 아래에서 흘러가는 텍스트처럼 보인다. 세그·고스트 버튼과 같은 톤 */
.paging   { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 16px 0 2px; color: var(--t2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.paging a { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 14px; border: 1px solid var(--line2); border-radius: var(--radius-pill); background: var(--card); color: var(--t2); font-weight: 600; text-decoration: none; }
.paging span { min-width: 54px; text-align: center; color: var(--t2); font-weight: 600;            }

/* ===== 확대 보기 (사진·영상 원본) =====
   미디어 메뉴와 신고 큐가 같이 쓴다. 마크업·스크립트는 _layout.ftl 의 lightbox 매크로에 있다. */

.lbx        { position: fixed; inset: 0; z-index: 100; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 34px 24px 24px; background: rgba(24, 20, 17, .88); }
.lbx[hidden] { display: none;                                                                      }

/* 원본은 4000px 짜리도 온다. 화면 밖으로 나가면 확대한 의미가 없으니 뷰포트에 맞춘다 */
.lbx_stage  { display: flex; align-items: center; justify-content: center; min-height: 0; flex: 1;  }
.lbx_stage img,
.lbx_stage video { max-width: 100%; max-height: 100%; border-radius: 6px; object-fit: contain;      }
.lbx_msg    { margin: 0; color: #fff; font-size: var(--text-md); opacity: .75;                      }

.lbx_cap    { flex: 0 0 auto; margin: 0; color: #fff; font-size: var(--text-sm); opacity: .8; text-align: center; }

.lbx_x      { position: absolute; right: 18px; top: 18px; display: grid; place-items: center; width: 38px; height: 38px; border: 0; border-radius: var(--radius-pill); background: rgba(255, 255, 255, .14); color: #fff; font-size: var(--text-2xl); font-family: inherit; cursor: pointer; }


/* ===== 회원 관리 =====
   신고 큐와 같은 두 모드(목록 ↔ 상세)다. 3열 배치도 그대로 재사용한다 — 화면끼리 손이 통일된다. */

/* 검색 필드 — 서비스 일정 검색(.st_search/.st_search_x) 정본 그대로: 아이콘 없는 필드 + 원형 × */
.msearch       { position: relative; display: inline-flex; align-items: center; flex: 0 0 320px;   }
.msearch input { width: 100%; height: 38px; padding: 0 44px 0 14px; border: 1.5px solid var(--line2); border-radius: 14px; background: var(--card-hover); color: var(--t1); font-size: var(--text-md); font-weight: 600; outline: none; transition: border-color .15s; }
.msearch input::placeholder { color: var(--t3); font-weight: 500;                                  }
.msearch_x {
    position     : absolute;
    right        : 8px;
    top          : 50%;
    transform    : translateY(-50%);
    width        : 26px;
    height       : 26px;
    padding      : 0;
    border       : none;
    border-radius: 50%;
    display      : grid;
    place-items  : center;
    background   : var(--soft);
    color        : var(--t2);
    font-size    : var(--text-lg);
    line-height  : 1;
    cursor       : pointer;
}
.msearch input:placeholder-shown ~ .msearch_x { display: none; }
.msearch input::-webkit-search-cancel-button  { display: none; }  /* 브라우저 기본 × — 우리 × 와 겹친다 */

/* 스위치 토글 — 서비스 c-toggle 치수 그대로(36×20 트랙 · 16px 노브 · 켜짐 코랄) */
.ftgl { display: inline-flex; align-items: center; gap: 7px; color: var(--t2); font-size: var(--text-sm); font-weight: 600; cursor: pointer; user-select: none; }
.ftgl input[type="checkbox"] {
    position     : relative;
    width        : 36px;
    height       : 20px;
    border       : none;
    border-radius: var(--radius-pill);
    background   : var(--line2);
    transition   : background .18s ease;
}
.ftgl input[type="checkbox"]::after {
    content      : "";
    position     : absolute;
    left         : 2px;
    top          : 2px;
    width        : 16px;
    height       : 16px;
    border-radius: 50%;
    background   : #fff;
    box-shadow   : 0 1px 3px rgba(0, 0, 0, .18);
    transition   : transform .18s ease;
}
.ftgl input[type="checkbox"]:checked        { background: var(--accent);      }
.ftgl input[type="checkbox"]:checked::after { transform: translateX(16px);    }

.mava      { display: grid; place-items: center; width: 34px; height: 34px; border-radius: var(--radius-pill); background: var(--accent-soft); color: var(--accent); font-size: var(--text-md); font-weight: 700; }

/* 목록 테이블 — 조회·대조 화면이라 컬럼 스캔이 우선이다(처리 큐는 리치 2줄, 여긴 테이블).
   베이스는 공통 .grid, 여기선 행 링크·회원 셀만 얹는다 */
/* padding 0 — 테이블이 카드 모서리에 딱 붙어야 헤더 배경이 굴곡을 같이 탄다(기본 .card 는 20px) */
.mtbl_wrap { padding: 0; overflow-x: auto;                                                         }
/* 고정폭 — 내용 따라 컬럼이 늘줄면 정렬·페이지 이동 때마다 표가 움직인다. 회원 칸이 남는 폭을 다 가진다 */
.mtbl      { table-layout: fixed; min-width: 1352px;                                               }
/* 남는 폭은 이메일이 갖는다(내용이 제일 긴 칸) — 회원이 가지면 넓은 모니터에서 옆이 통째로 빈다.
   ⚠️ 그래서 **폭을 비워 두는 열은 이메일 하나뿐이어야 한다.** 열을 더할 때 여기 nth-child 를 같이 안 주면
      fixed 레이아웃이 남는 폭을 미지정 열끼리 **반씩 나눠** 새 열이 이메일만큼 넓어진다(2026-09-22 '최근 접속' 추가 때 실제로 그랬다).
      min-width 도 함께 올린다 — 지정폭 합(1196) + 이메일 최소폭(156) = 1352. */
.mtbl th:nth-child(1), .mtbl td:nth-child(1)   { width: 230px; }  /* 회원         */
.mtbl th:nth-child(3), .mtbl td:nth-child(3)   { width: 76px;  }  /* 전화 뒷4     */
.mtbl th:nth-child(4), .mtbl td:nth-child(4)   { width: 84px;  }  /* 상태         */
.mtbl th:nth-child(5), .mtbl td:nth-child(5)   { width: 120px; }  /* 구독         */
.mtbl th:nth-child(6), .mtbl td:nth-child(6)   { width: 82px;  }  /* 본인인증     */
.mtbl th:nth-child(7), .mtbl td:nth-child(7)   { width: 66px;  }  /* 글           */
.mtbl th:nth-child(8), .mtbl td:nth-child(8)   { width: 82px;  }  /* 피신고       */
.mtbl th:nth-child(9), .mtbl td:nth-child(9)   { width: 152px; }  /* 가입         */
.mtbl th:nth-child(10), .mtbl td:nth-child(10) { width: 152px; }  /* 최근 로그인 — 가입과 함께 'YYYY-MM-DD HH:mm' 이 안 잘리는 폭 */
.mtbl th:nth-child(11), .mtbl td:nth-child(11) { width: 152px; }  /* 최근 접속   — 같은 형식이라 같은 폭 */
.mtbl td   { overflow: hidden; text-overflow: ellipsis;                                            }
.mtbl th a { display: inline-flex; align-items: center; gap: 3px; color: inherit; text-decoration: none; }
.mtbl th a.on { color: var(--accent-ink);                                                          }
.mtbl th a .ph.off { visibility: hidden; }  /* 자리만 차지 — 정렬 전환 때 컬럼 폭이 안 변한다 */
.mtbl td   { white-space: nowrap;                                                                  }
.mtbl tbody tr           { cursor: pointer;                                                        }
/* 한 줄 고정 — 커뮤니티닉이 아랫줄로 떨어지면 행 높이가 데이터에 따라 달라져 페이지마다 목록 길이가 들쭉날쭉해진다 */
.mtbl .nick        { display: flex; align-items: center; gap: 7px; max-width: 280px;               }
.mtbl .nick .nm    { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;               }
.mtbl .nick .dim   { flex: none; font-size: var(--text-xs);                                        }
.mtbl .num       { font-variant-numeric: tabular-nums;                                             }
.mtbl td.num.bad { color: var(--bad);                                                              }

/* 상세 본문 — 계정 · 활동 · 구독을 구획선으로 나눈다 */
.mpane     { padding: 6px 20px;                                                                    }
.mblock    { padding: 16px 0; border-bottom: 1px solid var(--line);                                }
.mblock:last-child { border-bottom: 0;                                                             }
.mblock h5 { display: flex; align-items: baseline; gap: 8px; margin: 0 0 12px; color: var(--t1); font-size: var(--text-md); font-weight: 700; }
.mblock .tri_dl { margin: 0; padding-top: 0; border-top: 0; grid-template-columns: 96px minmax(0, 1fr); row-gap: 10px; }
.mblock .muted  { font-size: var(--text-xs);                                                       }
/* 권한 안내 — 본문 정보와 톤 분리(구분선 위 잔글씨). 카드 안에서 문단처럼 흐르면 지저분하다 */
.mpane > .rfoot { margin: 0; padding: 12px 0 16px; border-top: 1px solid var(--line);              }

/* 활동 스탯 스트립 — 케이스 헤더 아래 한 줄 6칸. 칸 사이 1px 은 배경(--line)이 만든다 */
.mstrip      { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px; margin: 14px 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--line); overflow: hidden; box-shadow: var(--shadow); }
.mstrip span { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 12px 6px; background: var(--card); }
.mstrip b    { font-size: var(--text-3xl); font-weight: 700; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
.mstrip i    { color: var(--t2); font-style: normal; font-size: var(--text-xs); font-weight: 600;  }
.mstrip .bad b { color: var(--bad);                                                                }

.mpane_card  { padding: 0;                                                                         }  /* 안쪽 여백은 .mpane 몫 — 겹치면 이중 패딩 */

/* 두 열 바닥선 정렬 — 행 높이는 왼쪽(계정·구독)이 정하고, 기록 카드는 딱 그 높이만 갖는다.
   height:0 + min-height:100% — 기록 내용이 행 높이에 기여하지 않게 하는 그리드 트릭.
   기록(최근 8건)이 더 길면 목록만 카드 안에서 스크롤된다 */
.mtri        { align-items: stretch;                                                               }
.mtri .tri_side { height: 0; min-height: 100%;                                                     }
/* 첫 카드(기록·신고 내용)가 남는 높이를 흡수하고 넘치면 스크롤 — 뒤 카드(처리)는 제 높이로 바닥에 붙는다 */
.mtri .tri_side .card:first-child { flex: 1; min-height: 0; overflow-y: auto;                      }

/* 처리 — 전폭 카드, 사유 입력 + 버튼 한 줄 */
.act_card    { margin-top: 8px; padding: 13px 18px 14px;                                           }
.act_card h4 { margin: 0 0 9px; color: var(--t1); font-size: var(--text-md); font-weight: 700;     }
.act_card .act_form { display: flex; align-items: center; gap: 8px; width: 100%; padding: 0; border: 0; background: transparent; }
.act_card .act_form > .msearch { flex: 1;                                                          }
.act_card .act_form > .btn     { flex: none;                                                       }
.act_card .rfoot { margin: 10px 0 0;                                                               }

.mlog      { margin: 0; padding: 0; list-style: none;                                              }
.mlog li   { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 3px 8px; padding: 10px 0; border-bottom: 1px solid var(--line); font-size: var(--text-md); }
.mlog li:first-child { padding-top: 2px;                                                           }
.mlog li:last-child  { padding-bottom: 0; border-bottom: 0;                                        }
.mlog li b { font-weight: 700;                                                                     }
.mlog li span { color: var(--t2); font-size: var(--text-xs); text-align: right; white-space: nowrap; }
.mlog li em { grid-column: 1 / -1; color: var(--t2); font-size: var(--text-sm); font-style: normal; }

/* ── PII 열람 모달 ──
   ★ 막는 대신 보여 준다. 횟수 제한은 스크린샷으로 우회되고, 자동 가림 타이머는 받아적는 중에
     사라져 재열람만 늘린다 — 최근 열람 이력을 띄우는 쪽이 억제력이 정직하다(설계 §13). */
.rmodal_ic.warn { background: var(--warn-bg); color: var(--warn);                                  }

.mprev     { display: flex; align-items: center; gap: 7px; margin: 0 0 14px; padding: 9px 11px; border-radius: 8px; background: var(--warn-bg); color: var(--warn); font-size: var(--text-sm); }
.mprev b   { font-weight: 700;                                                                     }
.mprev.quiet { background: var(--card-hover); color: var(--t3);                                    }

/* 사유 입력 — 이 문장이 영구 기록에 남는다. 칸의 무게를 그에 맞춘다 */
.rz        { display: flex; flex-direction: column; gap: 8px;                                      }
.rz_top    { display: flex; align-items: center; gap: 7px;                                         }
.rz_top b  { font-size: var(--text-md); font-weight: 700;                                          }
.rz_req    { padding: 1px 7px; border-radius: var(--radius-pill); background: var(--bad-bg); color: var(--bad); font-size: var(--text-xs); font-weight: 700; }

/* 칩 — CS 사유는 반복적이다. 로그 문구가 통일돼 나중에 검색되는 게 본래 값어치다 */
.rz_chips  { display: flex; gap: 5px; flex-wrap: wrap;                                             }
.rz_chip   { height: 26px; padding: 0 11px; border: 1px dashed var(--line2); border-radius: var(--radius-pill); background: none; color: var(--t2); font-size: var(--text-sm); font-family: inherit; cursor: pointer; }

.rz_field  { position: relative; display: flex; align-items: center;                               }
.rz_field i { position: absolute; left: 12px; color: var(--t3); font-size: var(--text-lg);         }
.rz_field.bad i { color: var(--bad);                                                               }
.rz_field.bad input { border-color: var(--bad); background: var(--bad-bg);                         }

/* ★ input#id 로 쓴다 — 전역 input[type="text"](0,1,1)가 클래스만으로는 안 밀린다(모달 요율 칸에서 겪었다).
   모양은 목록 검색(.msearch input)과 같은 문법 — 1.5px 테두리 · radius 14 · 포커스 코랄 */
input#pii_reason { width: 100%; height: 42px; padding: 0 13px 0 38px; border: 1.5px solid var(--line2); border-radius: 14px; background: var(--card-hover); color: var(--t1); font-size: var(--text-md); font-weight: 600; outline: none; transition: border-color .15s; }
input#pii_reason::placeholder { color: var(--t3); font-weight: 500;                                }

.rz_help   { display: flex; align-items: flex-start; gap: 10px; color: var(--t3); font-size: var(--text-xs); line-height: 1.5; }
.rz_help b { color: var(--t2); font-weight: 700;                                                   }
.rz_help em { margin-left: auto; flex: 0 0 auto; font-style: normal; font-variant-numeric: tabular-nums; }
.rz_help.bad,
.rz_help.bad b { color: var(--bad);                                                                }

/* PII 2단계 위저드 — 진행바 · 결과 값 크게 · 기록 확인 배너 */
.pii_steps   { display: flex; gap: 5px; margin: 0 0 14px;                                          }
.pii_steps i { flex: 1; height: 4px; border-radius: var(--radius-pill); background: var(--soft);   }
.pii_steps i.on { background: var(--grad-btn);                                                     }

.pii_kv       { margin: 2px 0 0;                                                                   }
.pii_kv > div { display: flex; align-items: center; justify-content: space-between; padding: 12px 2px; border-bottom: 1px solid var(--line); }
.pii_kv > div:last-child { border-bottom: 0;                                                       }
.pii_kv dt    { color: var(--t1); font-size: var(--text-sm); font-weight: 600;                     }
.pii_kv dd    { margin: 0; font-size: var(--text-xl); font-weight: 700; letter-spacing: .2px;      }

.pii_logged   { display: flex; align-items: center; gap: 7px; margin: 12px 0 0; padding: 9px 12px; border-radius: 10px; background: var(--ok-bg); color: var(--ok); font-size: var(--text-sm); font-weight: 600; }

.mpii_out  { margin-top: 14px;                                                                     }
/* 열람 버튼 — 서비스 저장 버튼 문법(연코랄 그라데이션 · radius 14 · 전폭). 2단계에선 같은 버튼이 '닫기' */
.mpii_btns      { margin-top: 16px;                                                                }
.mpii_btns .btn { display: flex; align-items: center; justify-content: center; width: 100%; height: 46px; border: none; font-size: var(--text-lg); font-weight: 700; }
.mpii_btns .btn:disabled { opacity: .6; cursor: default;                                           }

/* ── 프리미엄 관리 모달(#prem_modal) · 공용 구독 부여/회수 모달(#sub_modal) ──
   목업(doc/preview/2026-09-03-admin-premium-manage.html) 구조를 따르되 부품은 기존 것을 그대로 쓴다 —
   .btn.sm.bad(행 버튼) · .grid(표) · .afield/.rpick(입력행) · c-select(사유).
   #sub_modal 은 상세 화면과 한 벌이라 기본 폭(600px)을 그대로 쓴다 — 후보표가 붙어도 4열이라 넉넉하다. */
.rmodal.w1 { width: min(840px, calc(100vw - 40px)); }  /* 무료 프리미엄 명단 — 표 6열 */

/* 구독 모달(#sub_modal)이 프리미엄 관리 모달 위에 겹칠 때 아래를 뒤로 보낸다 — 목업(mk_stack .under)과 같은 표현.
   ⚠️ 아래 모달을 close() 하지 않고 열어 둔 채 이 클래스만 얹는다 — close() 했다가 위 모달에서 Esc 하면
   아래까지 통째로 사라진다(실측 결함, 검증 I-2). pointer-events:none 이라 뒤에서 클릭도 안 먹는다.
   ⚠️ opacity 를 쓰지 않는다 — 카드가 반투명해지면 그 아래 회원 목록 표가 통째로 비쳐 글자가 겹친다
     (실측 결함). 어둡게 하는 일은 위 모달의 ::backdrop 이 이미 한다 — 두 dialog 는 top layer 에
     '아래 backdrop → 아래 카드 → 위 backdrop → 위 카드' 순으로 쌓여, 위 backdrop 이 아래 카드를 덮는다.
     여기선 물러난 느낌(축소·흐림)만 준다. */
.rmodal.under { transform: scale(.96); filter: blur(.4px); pointer-events: none; }

/* 타이틀 행에 처리 버튼을 얹는다(모달 A) — 기존 .tsub 는 그대로 두고 레이아웃만 이 조합 클래스가 얹는다.
   ⚠️ 반드시 .tsub.tsub_row 로 적는다 — .tsub_row 단독이면 명시성이 .tsub(파일 뒤쪽 — 소스 순서로도
   이긴다)와 동률이라 .tsub 의 margin(26px 0 8px)에 덮인다(실측 결함, 검증 M-4). */
.tsub.tsub_row { display: flex; align-items: center; gap: 8px; margin: 0 2px 14px; }

/* 부여·회수 대상 명단 — 모달 안에서만 스크롤한다(모달 자체는 늘어나지 않는다).
   ★ 높이를 못박는다(max 가 아니라 height) — 검색 결과 건수에 따라 상자가 줄면 아래 기간·사유 칸과
     버튼이 위아래로 튄다. 한 건이든 50건이든 이 자리 크기는 늘 같다. */
.plist               { height: 320px; margin-bottom: 16px; overflow-y: auto; border: 1px solid var(--line); border-radius: 12px; }
.plist table.grid th { position: sticky; top: 0; z-index: 1;                                       }
.plist .empty        { display: grid; place-items: center; height: 100%; color: var(--t3); font-size: var(--text-md); }

/* 표 아래 각주 — 상자가 아니라 잔글씨 한 덩어리다(.notice 를 쓰면 아이콘 자리 들여쓰기와
   .rmodal_b .notice 의 아래 여백만 남아 표와 모달 바닥 사이가 텅 빈다).
   margin 이 0 인 건 위 .plist 의 margin-bottom 16px 이 이미 간격이기 때문이다 */
.mnote   { margin: 0; color: var(--t2); font-size: var(--text-xs); line-height: 1.6;               }
.mnote b { color: var(--t1); font-weight: 700;                                                     }

/* 모달 표 — A 명단(.ptbl) · B 후보(.ctbl). 한 행 한 줄이고 고정폭이라 닉네임·이메일이 길어도
   행 높이·컬럼 폭이 안 변한다(내용 따라 늘줄면 부여·회수·검색으로 목록이 바뀔 때마다 표가
   움찔한다 — 회원 목록 .mtbl 과 같은 이유). 남는 폭은 이메일이 갖는다(내용이 제일 긴 칸) */
.ptbl,
.ctbl    { table-layout: fixed;                                                                    }
.ptbl td,
.ctbl td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;                         }

.ptbl th:nth-child(1), .ptbl td:nth-child(1) { width: 130px; }  /* 회원   */
.ptbl th:nth-child(3), .ptbl td:nth-child(3) { width: 90px;  }  /* 부여자 */
.ptbl th:nth-child(4), .ptbl td:nth-child(4) { width: 140px; }  /* 부여일 */
.ptbl th:nth-child(5), .ptbl td:nth-child(5) { width: 170px; }  /* 만료일 — '만료됨' 배지가 날짜 옆에 붙는다 */
.ptbl th:nth-child(6), .ptbl td:nth-child(6) { width: 78px;  }  /* 회수   */
.ptbl .tag { margin-left: 7px;                                                                     }

.ctbl th:nth-child(1), .ctbl td:nth-child(1) { width: 48px;  }  /* 체크   */
.ctbl th:nth-child(2), .ctbl td:nth-child(2) { width: 130px; }  /* 회원   */
.ctbl th:nth-child(4), .ctbl td:nth-child(4) { width: 140px; }  /* 가입일 */

/* 프리미엄 톤 — 결제가 아니라 '특별 부여'라는 뜻을 --pur 로 낸다(.tag.role_SUPER 와 같은 팔레트) */
.btn.prem { background: var(--pur-bg); color: var(--pur); border-color: transparent;                }

/* ===== 감사 로그 ===== */

/* 좌: 훑기(표) · 우: 파고들기(패널). 패널 폭은 고정 — 표가 줄어드는 쪽이 낫다(사유가 잘려도 패널에 전문이 있다).
   ★ 높이도 못박는다. 패널이 사유·JSON 길이만큼 늘어나면 행을 고를 때마다 화면이 튀고,
     표가 길어지면 페이지 자체가 늘어나 아래 페이징이 화면 밖으로 밀렸다. 두 열이 같은 높이를
     갖고 각자 안에서 스크롤하면 필터·페이징이 항상 제자리에 남는다. */
.alog_split { display: grid; grid-template-columns: minmax(0, 1fr) 372px; gap: 14px; align-items: stretch; height: calc(100vh - 252px); min-height: 420px; }
/* padding 0 — 표가 카드 모서리에 딱 붙어야 헤더 배경이 굴곡을 같이 탄다(회원 목록 .mtbl_wrap 과 같은 이유) */
.alog_wrap  { display: flex; min-width: 0; padding: 0; overflow: hidden;                           }
.alog_wrap .table_scroll { flex: 1; min-height: 0; overflow: auto;                                 }

/* 직접 기간 — 프리셋으로는 못 잡는 '그 사건이 난 날'을 위한 축이다. 전역 input 규칙이 date 를 안 받아 여기서 준다.
   ★ 기본은 접혀 있다(기간 세그의 '직접' 칩이 연다). 늘 펴 두면 값이 안 든 빈 칸 두 개가 필터바
     오른쪽을 먹는데, 칩보다 크면서 정보량은 0 이라 화면에서 가장 시끄러운 자리를 차지했다. */
.adate       { display: inline-flex; align-items: center; gap: 6px;                                }
.adate[hidden] { display: none;                                                                    }  /* .adate 의 display 가 [hidden] 기본값을 덮으므로 다시 끈다 */
/* 모양은 어드민 공통(datepicker.css 어드민 절)이 정본 — 여기서는 필터바에 앉을 크기만 */
.adate .dtp_trig { width: 116px; height: 30px; padding: 0 9px;                                     }
/* 사본은 아이콘을 오른쪽 끝으로 민다(margin-left: auto) — 칸을 넓게 쓰는 폼 기준이다.
   필터바에서는 글자 바로 옆에 붙어야 네이티브 date 입력처럼 보이지 않는다. */
.adate .dtp_trig .pk_ico { margin-left: 5px; padding-left: 0;                                      }
.adate .to   { color: var(--t3);                                                                   }
.adate[data-on] .dtp_trig { border-color: var(--accent); background: var(--accent-soft);           }

/* 헤더는 붙여 둔다 — 표가 안에서 스크롤되므로 안 붙이면 아래로 내려간 순간 무슨 컬럼인지 사라진다 */
.alog thead th { position: sticky; top: 0; z-index: 1;                                             }

/* 고정폭 — 내용 따라 컬럼이 늘줄면 페이지를 넘길 때마다 표가 움직인다(회원 목록에서 같은 이유로 박았다).
   남는 폭은 사유가 갖는다. 잘려도 우측 패널에 전문이 있고, 다른 칸은 길이가 데이터마다 안 변한다. */
.alog      { table-layout: fixed; min-width: 680px;                                                }
.alog th:nth-child(1), .alog td:nth-child(1) { width: 148px; }  /* 시각 2026-09-01 17:30          */
.alog th:nth-child(2), .alog td:nth-child(2) { width: 88px;  }  /* 관리자                         */
.alog th:nth-child(3), .alog td:nth-child(3) { width: 124px; }  /* 행위 — 신고 상태 조치됨이 최장 */
.alog th:nth-child(4), .alog td:nth-child(4) { width: 116px; }  /* 대상 COMMENT #55218            */
.alog th:nth-child(5), .alog td:nth-child(5) { width: 204px; }  /* 사유 — 앞 네 열을 뺀 값        */

/* 한 줄 고정 — 사유가 아랫줄로 접히면 행 높이가 데이터마다 달라져 페이지마다 목록 길이가 들쭉날쭉해진다.
   높이를 못박은 화면이라 이게 곧 한 화면에 몇 행이 보이느냐를 흔든다. */
.alog td   { white-space: nowrap; overflow: hidden; text-overflow: ellipsis;                       }
.alog .tm  { font-variant-numeric: tabular-nums;                                                   }
.alog .tgt { font-variant-numeric: tabular-nums;                                                   }


/* 고른 행 — 우측 패널이 무엇을 보여 주고 있는지 표에서도 보여야 한다 */
.alog tbody tr[aria-current] td { background: var(--accent-soft);                                  }

/* PII — 행 전체를 warn 으로 칠하면 행위 태그(열람)도 같은 계열이라 서로 뭉갠다. 왼쪽 세로선으로 바꿨다 */
.alog tbody tr.pii_row td:first-child { box-shadow: inset 3px 0 0 var(--warn);                     }

/* ── 우측 상세 패널 ── */
.alog_pane   { display: flex; flex-direction: column; padding: 0; overflow: hidden;                }
.apane_h     { flex: none; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 14px 16px; border-bottom: 1px solid var(--line); }
.apane_ic    { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 12px; background: var(--card-hover); color: var(--t2); font-size: var(--text-3xl); flex: none; }
.apane_ic.live  { background: var(--ok-bg);   color: var(--ok);                                    }
.apane_ic.hide  { background: var(--warn-bg); color: var(--warn);                                  }
.apane_ic.blind { background: var(--pur-bg);  color: var(--pur);                                   }
.apane_ic.del   { background: var(--bad-bg);  color: var(--bad);                                   }
.apane_ic.pend  { background: var(--info-bg); color: var(--info);                                  }
.apane_t h4  { margin: 0; font-size: var(--text-xl); font-weight: 700; letter-spacing: -.3px;      }
.apane_t .hint { display: block; color: var(--t2); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }

.apane_b     { flex: 1; min-height: 0; padding: 14px 16px 16px; overflow-y: auto;                  }
.apane_b.center { display: grid; place-items: center; }  /* 빈 상태 문구는 패널 정중앙 */
.apane_dl    { margin: 0; grid-template-columns: 58px minmax(0, 1fr);                              }
.apane_why   { display: block; color: var(--t1); line-height: 1.6;                                 }

.apane_sec   { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line);             }
.apane_sec h5 { margin: 0 0 10px; color: var(--t1); font-size: var(--text-md); font-weight: 700;    }  /* 회원 상세 계정·구독(.mblock h5)과 같은 타이틀 톤 — 아이콘 없이 검정 */
.apane_none  { margin: 0; color: var(--t3); font-size: var(--text-sm);                             }

/* 인접 행동 — 한 건만 보면 단발인지 연쇄인지 알 수 없다 */
.anear       { margin: 0; padding: 0; list-style: none;                                            }
.anear li    { border-bottom: 1px solid var(--line);                                               }
.anear li:last-child { border-bottom: 0;                                                           }
.anear a     { display: grid; grid-template-columns: minmax(0, 1fr); gap: 3px; padding: 7px 0; color: inherit; text-decoration: none; }
.anear .t    { color: var(--t2); font-size: var(--text-xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.anear .d    { display: flex; align-items: center; gap: 7px; min-width: 0; font-size: var(--text-sm); }
.anear .d .tgt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--t2); font-weight: 600; }

/* 개인정보 열람 표식 — 태그가 아니라 경고다. 태그 계열과 섞이지 않게 알약으로 둔다 */
.pii_mark    { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; padding: 2px 9px; border-radius: var(--radius-pill); background: var(--warn-bg); color: var(--warn); font-size: var(--text-xs); font-weight: 700; }

.astate_pre { margin: 0; max-height: 50vh; padding: 13px 14px; border-radius: 9px; overflow: auto; background: var(--ink); color: var(--ink-on); font-size: var(--text-sm); line-height: 1.55; white-space: pre-wrap; word-break: break-all; }

/* ===== 관리자 계정 ===== */

/* 버튼 변형 — 지금까지 .btn 은 width:100% 짜리 주황 하나뿐이었다. 행·모달 안에서 쓸 수 있는 게 없어
   여기서 연다.
   ★ display 를 반드시 덮는다 — .btn 은 grid+place-items 라 아이콘과 글자가 세로로 쌓인다.
   ★ 수식자를 .ink 로 짓지 말 것 — 신고 큐 사진 무대(.ink{min-height:620px})와 이름이 겹쳐
     배경만 덮이고 크기를 상속받아 거대한 검정 판이 된다(목업에서 실제로 그랬다). */
.btn.sm,
.btn.wide  { display: inline-flex; align-items: center; justify-content: center; gap: 5px; width: auto; }
.btn.sm    { height: 30px; padding: 0 11px; border: 1px solid var(--line2); background: var(--card); color: var(--t2); font-size: var(--text-sm); font-weight: 600; }
.btn.sm i  { font-size: var(--text-md);                                                            }
.btn.sm.bad { border-color: #E9C4C6; color: var(--bad);                                            }
.btn.sm:disabled { border-color: var(--line); background: var(--card-hover); color: var(--t3); cursor: not-allowed; }
.btn.wide  { height: 36px; padding: 0 16px; font-size: var(--text-md);                             }
.btn.wide:disabled { opacity: .6; cursor: default;                                                 }
.btn.plain { border: 1px solid var(--line2); background: var(--card); color: var(--t2);            }
/* CTA — 서비스 c-button kind=cta 색(grad-btn / 흰 글자 / shadow-btn / radius14). 화면 안에서 가장 무거운 한 개에만 붙인다 */
.btn.cta   { border-radius: var(--radius-btn); background: var(--grad-btn); box-shadow: var(--shadow-btn); color: #FFF; }
.btn.add   { border-radius: var(--radius-btn); background: var(--grad-add-btn); box-shadow: var(--shadow-add-btn); color: var(--add-btn-ink); }  /* 서비스 c-button kind=add — 폼을 여는 '추가·진입' 버튼. 확정·제출은 cta */
.btn.danger { background: var(--bad); color: #FFF;                                                 }  /* 되돌릴 수 없는 확정 — accent 면 '추가'와 같은 색이라 손이 먼저 간다 */

/* 등급 배지 — 상태 배지(.st_*)와 다른 계열로 둔다. 같은 칸에 나란히 서므로 색이 같으면 한 덩어리로 읽힌다 */
.tag.role_SUPER    { background: var(--pur-bg);     color: var(--pur);                             }
.tag.role_OPERATOR { background: var(--info-bg);    color: var(--info);                            }
.tag.role_SUPPORT  { background: var(--card-hover); color: var(--t2);                              }
.tag.linked        { background: var(--teal-bg);    color: var(--teal);                            }

/* 알림 띠 — .msg_error 는 좁은 인증 화면용이라 아이콘·여러 줄을 못 받는다 */
.notice      { display: flex; gap: 10px; padding: 12px 14px; border-radius: var(--radius); font-size: var(--text-md); line-height: 1.6; }
.notice[hidden] { display: none; }  /* display:flex 가 hidden 기본값을 덮는다 — 다시 끈다(.adate 와 같은 함정) */
.notice i    { flex: 0 0 auto; font-size: 18px;                                                    }
.notice.warn { background: var(--warn-bg); color: var(--warn);                                     }
/* 경고가 아니라 '알아 둘 것' 톤 — 알림 화면이 "이건 공지가 아니다"를 말하는 자리에 쓴다. warn 을
   빌려 쓰지 않는 이유는 같은 화면에 진짜 경고(방해금지·되돌릴 수 없음)가 함께 서기 때문이다 —
   둘이 같은 색이면 정의를 설명하는 문장과 위험 경고가 한 덩어리로 읽힌다. 색은 있는 토큰 그대로다. */
.notice.info { background: var(--info-bg); color: var(--info);                                     }
.notice.slim { padding: 10px 13px; border-radius: 10px; font-size: var(--text-xs); line-height: 1.55; }  /* 모달 각주용 — 버튼 직전 한 줄 */

/* 추가 모달(시안 A, 2026-09-02) — 이름·이메일 한 줄, 필수/선택 구획 */
.frow2   { display: grid; grid-template-columns: 1fr 1.4fr; gap: 10px;                             }
.opt_sec { margin-top: 3px; margin-bottom: 13px; padding-top: 13px; border-top: 1px dashed var(--line2); }
.opt_t   { margin-bottom: 10px; color: var(--t1); font-size: var(--text-sm); font-weight: 700;     }
.opt_t em { color: var(--t3); font-style: normal; font-weight: 500;                                }

/* ── D 조합안(2026-09-02) — 카드 그리드(.agrp/.acards/.acard*)는 표로 대체되며 은퇴했다. ──
   0층 보안 스트립: 값 + 판정. 이상 없는 칸은 조용하고, 누를 수 있는 칸만 button/a 다. */
.sec_strip { display: grid; grid-template-columns: repeat(4, 1fr); border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); overflow: hidden; }
.sec_cell  { display: block; padding: 14px 18px; border: 0; background: none; color: inherit; font-family: inherit; text-align: left; text-decoration: none; }
.sec_cell + .sec_cell { border-left: 1px solid var(--line);                                        }
button.sec_cell,
a.sec_cell { cursor: pointer; transition: background-color .12s;                                   }
button.sec_cell:hover,
a.sec_cell:hover { background: var(--card-hover);                                                  }
.sec_cell .n  { font-size: 22px; font-weight: 700; letter-spacing: -.5px; line-height: 1.2; font-variant-numeric: tabular-nums; }
.sec_cell .d2 { color: var(--t2); font-size: var(--text-md); font-weight: 500;                     }
.sec_cell .l  { margin-top: 1px; color: var(--t1); font-size: var(--text-sm); font-weight: 600;    }
.sec_cell .j  { margin-top: 4px; color: var(--t1); font-size: var(--text-xs); line-height: 1.5;    }
.sec_cell.warn .n,
.sec_cell.warn .j { color: var(--warn); font-weight: 600;                                          }

/* 재직 표 — 행 클릭 = 확장(최근 활동). 케밥 메뉴가 뜰 자리라 wrap 의 overflow 를 열어 둔다 */
.atbl_wrap { margin-top: 2px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.atbl th:first-child { border-top-left-radius: var(--radius);                                      }
.atbl th:last-child  { border-top-right-radius: var(--radius);                                     }
.atbl td.last { padding-top: 5px; padding-bottom: 5px;                                             }  /* 케밥 30 + 패딩 10 = 다른 표와 같은 41px 행 */

.arow          { cursor: pointer;                                                                  }
.arow:hover td { background: var(--card-hover);                                                    }

.arow.hit td   { box-shadow: inset 0 2px 0 var(--warn), inset 0 -2px 0 var(--warn);                }  /* 스트립 점프 착지 표시 */
.atbl .nm      { white-space: nowrap;                                                              }
.arow .tm      { font-variant-numeric: tabular-nums; white-space: nowrap;                          }


/* ⋯ 메뉴 — 드문 동작(등급·2FA·연동·퇴사)은 접어 둔다. 잠긴 항목은 이유(title)와 함께 흐려진다 */
.atbl .last              { position: relative; text-align: right; white-space: nowrap; }
.kebab                   { width: 30px; height: 30px; border: 1px solid var(--line2); border-radius: var(--radius-sm); background: var(--card); color: var(--t1); font-family: inherit; font-size: var(--text-lg); font-weight: 700; line-height: 1; cursor: pointer; }
.kebab:hover             { background: var(--card-hover);                            }


.kmenu          { position: absolute; right: 14px; top: 44px; z-index: 10; display: flex; flex-direction: column; min-width: 176px; padding: 6px; border: 1px solid var(--line2); border-radius: 12px; background: var(--card); box-shadow: var(--shadow-md); }
.kmenu[hidden]  { display: none;                                                                   }
.kmenu button   { height: 34px; padding: 0 12px; border: 0; border-radius: var(--radius-sm); background: none; color: var(--t1); font-family: inherit; font-size: var(--text-sm); font-weight: 600; text-align: left; cursor: pointer; }
.kmenu button:hover { background: var(--card-hover);                                               }
.kmenu button.bad   { color: var(--bad);                                                           }
.kmenu button:disabled       { color: var(--t3); cursor: not-allowed;                              }
.kmenu button:disabled:hover { background: none;                                                   }

/* 확장 행 — 최근 활동(감사로그 앞 몇 건). 전체는 감사 로그 메뉴가 맡는다 */
.xrow[hidden] { display: none;                                                                     }
.xrow td      { padding: 0 16px 14px; background: var(--card-hover);                               }
.xrow_in      { padding: 12px 14px 0;                                                              }
.xrow_in h5   { display: flex; align-items: baseline; gap: 8px; margin: 0 0 8px; color: var(--t1); font-size: var(--text-sm); font-weight: 700; }
.xrow_in h5 a { margin-left: auto; color: var(--accent); font-size: var(--text-xs); font-weight: 600; text-decoration: none; }
.xrow_in ul   { margin: 0; padding: 0; list-style: none;                                           }
.xrow_in li   { display: flex; gap: 10px; padding: 6px 0; border-top: 1px solid var(--line); font-size: var(--text-sm); }
.xrow_in li:first-child { border-top: 0;                                                           }
.xrow_in .t   { flex: 0 0 128px; color: var(--t2); font-size: var(--text-xs); font-variant-numeric: tabular-nums; line-height: 1.6; }
.xrow_in .none { margin: 0; color: var(--t3); font-size: var(--text-sm);                           }

.none_tx { color: var(--t3); font-size: var(--text-sm); font-weight: 500;                          }

/* 퇴사 — 상태 필터(세그)의 탭으로 분리됐다(details 접기 은퇴, 2026-09-02) */

/* 모달 폼 */
.afield        { display: flex; flex-direction: column; gap: 5px; margin-bottom: 14px;             }
.afield[hidden] { display: none; }  /* display:flex 가 hidden 기본값을 덮는다 — 등급 선택·회원 검색이 전 동작에서 보이던 원인 */
.afield > span { color: var(--t1); font-size: var(--text-md); font-weight: 700;                    }
.afield input:not([type="checkbox"]),
.afield textarea { padding: 0 12px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-size: var(--text-md); font-family: inherit; }
.afield input:not([type="checkbox"]) { height: 38px; }
.afield .hint  { margin: 2px 0 0; color: var(--t1); font-size: var(--text-xs); line-height: 1.55;  }

/* 폼 필드 안의 × — 목록 검색(.msearch)의 원형 × 만 빌린다. 모양·크기는 .afield 규격 그대로 두고
   (.afield input 이 뒤에 있어 테두리·배경·radius 를 이긴다) × 가 앉을 오른쪽 여백만 얹는다.
   .msearch 의 flex: 0 0 320px 은 세로 flex 인 .afield 안에서 '높이 320px'이 되므로 끈다. */
.afield .msearch       { flex: none; width: 100%;                                                  }
.afield .msearch input { padding-right: 44px;                                                      }

/* 자주 쓰는 사유 — 빨라지는 것보다 로그 문구가 통일돼 검색된다는 게 본래 값어치다(PII 열람과 같은 장치) */
/* 사유 셀렉트 — c-select 가 afield 폭을 채운다. 칩(.chips)은 셀렉트 전환으로 은퇴했다(2026-09-02) */
.afield c-select { width: 100%; margin-bottom: 4px; }  /* 기타 상세 입력과 숨 쉴 간격(gap 5 + 4) */
/* 사본 버튼 기본은 32px — 모달 입력칸(38px · radius 10)과 같은 자로 맞춘다(사본 본문은 손대지 않는다) */
.afield c-select::part(button) { height: 38px; border-radius: 10px; }

/* 등급 고르기 — 이름만으로는 권한 범위가 안 읽힌다. 목록이 아니라 여기에 설명을 둔다:
   훑을 때가 아니라 고를 때 필요한 정보다. */
.rpick       { display: flex; flex-direction: column; gap: 7px;                                     }
.rpick label { display: flex; align-items: flex-start; gap: 10px; padding: 11px 13px; border: 1px solid var(--line2); border-radius: 12px; cursor: pointer; }
.rpick label.on { border-color: var(--accent); background: var(--accent-soft);                      }
.rpick input { display: none;                                                                       }  /* 동그라미를 숨긴다 — 카드 테두리(.on)가 선택 표시다(시안 A) */
.rpick b     { display: block; font-size: var(--text-md);                                           }
.rpick span  { display: block; margin-top: 1px; color: var(--t1); font-size: var(--text-xs); line-height: 1.5; }  /* 결정 정보 — 크기만 낮추고 색은 낮추지 않는다 */

/* 기간 5칸 — 라벨이 짧아 세로로 쌓으면 모달만 길어진다(후보표까지 붙는 모드가 있다). 한 줄에 눕힌다.
   ⚠️ 반드시 .rpick.rpick_row 로 적는다 — 단독이면 .rpick 과 명시성이 동률이라 소스 순서에 기댄다(.tsub_row 선례) */
.rpick.rpick_row       { flex-direction: row; flex-wrap: wrap;                                      }
.rpick.rpick_row label { flex: 1 1 92px; justify-content: center;                                   }

/* 연동 검색 결과 */
.acand       { margin-top: 7px; border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; }
.acand button { display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 12px; border: 0; border-bottom: 1px solid var(--line); background: var(--card); color: var(--t1); font-size: var(--text-md); font-family: inherit; text-align: left; cursor: pointer; }
.acand button:last-child { border-bottom: 0;                                                        }
.acand .e    { margin-left: auto; color: var(--t2); font-size: var(--text-sm);                      }

/* 임시 비밀번호 — 한 번만 보이는 값이라 눈에 띄어야 한다 */
.pw      { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 15px; border-radius: var(--radius); background: var(--ink); color: var(--ink-on); }
.pw code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 17px; letter-spacing: .06em; }

.rmodal_f { display: flex; gap: 8px; justify-content: flex-end; padding: 14px 20px; border-top: 1px solid var(--line); background: var(--ground); border-radius: 0 0 18px 18px; }
.rmodal_f .btn.wide { flex: 1; height: 40px; border-radius: 12px; }  /* 푸터는 전폭 분할(시안 톤) — 헤더의 .btn.wide 는 그대로 */

/* 표 공통 빈 상태 — 결과 0건이어도 헤더는 남고 tbody 가 한 줄로 말한다(전 표 화면 통일 규칙, 2026-09-02) */
.erow td { height: 820px; padding: 0 16px; color: var(--t3); font-size: var(--text-md); text-align: center; vertical-align: middle; border-bottom: 0; }  /* 한 페이지(20행 × 41px) 분량 — 0건이어도 표 높이가 평소와 같다 */

/* 행이 적어도 표 영역은 한 페이지 분량을 지킨다 — erow 와 같은 자(헤더 41 + 20행 820). 감사로그는 컨테이너가 이미 뷰포트 고정이라 제외 */
.atbl_wrap, .mtbl_wrap { min-height: 861px; }
.atbl_wrap.slim { min-height: 0; }  /* 전체 탭의 퇴사 섹션 — 페이지 높이는 위의 재직 표가 이미 채운다 */
.mblock > .btn.sm { margin-top: 10px; }  /* 회원 상세 구독 카드의 부여/회수 버튼 */
.rmodal_b .notice { margin-bottom: 14px; }  /* 경고와 다음 입력 구획 사이 숨 — 붙어 있으면 경고가 기간의 설명처럼 읽힌다 */
.tsub        { margin: 26px 0 8px; color: var(--t1); font-size: var(--text-lg); font-weight: 600; }
.tsub .count { margin-left: 8px; padding: 2px 10px; border-radius: var(--radius-pill); background: var(--accent-soft); color: var(--accent-ink); font-size: var(--text-sm); font-weight: 700; font-variant-numeric: tabular-nums; vertical-align: 1px; }

/* 체크박스 + 문구 한 줄 — 공지 폼(고정)과 사유 모달(푸시)이 함께 쓴다. 스코프를 좁히지 않는다.
   vertical-align 은 inline 문맥에서만 유효 — inline-flex 의 baseline 이 체크박스 박스 하단이라
   라벨 옆(공지 폼 '무기한')에 놓으면 글자가 위로 뜸. flex 아이템으로 쓰일 때는 무시.  */
.chk                    { display: inline-flex; align-items: center; gap: 7px; font-size: var(--text-sm); font-weight: 600; vertical-align: middle; }

/* 공지 작성 폼 — .afield 는 모달 부품이지만 카드 안에서도 그대로 쓴다. 본문만 여러 줄이라 높이를 따로 준다.
   트리거 모양은 어드민 공통(datepicker.css 어드민 절)이 정본 — 여기서는 폼에 앉을 치수만.
   ★ 2열이다(2026-09-09 확정, D안) — 왼쪽이 입력, 오른쪽이 앱 미리보기. 공지는 줄바꿈 한 줄이 앱의 한
     줄이 되는 평문이라(설계 §4-2) 결과를 눈으로 봐야 하는데, 전에는 저장 후 preview.sgn 을 새 창으로
     열어야만 확인됐다. 폭이 모자라면 한 컬럼으로 접고 미리보기를 입력 아래로 내린다. */
/* 폭을 못박고 가운데 세운다 — 전폭으로 두면 화면이 넓어질수록 왼쪽 입력만 늘어나 분류 칩 오른쪽이
   빈다. 1180 은 미리보기 420 을 뺀 왼쪽이 744px 가 되는 값이다(한 컬럼 시절 760px 와 거의 같아
   본문 입력 감각이 유지된다).
   두 카드는 같은 높이로 세운다(align-items 기본 stretch) — 오른쪽 미리보기가 왼쪽 입력과 같은 기둥에
   서야 눈이 좌우로만 오간다. 그래서 본문 textarea 는 resize 를 끄고 고정 높이 + 스크롤이다:
   늘어나면 오른쪽이 따라 늘어 균형이 깨진다. */
.nform,
.nfmsg                  { width: 100%; max-width: 1180px; margin: 0 auto; }  /* 띠는 .pbody flex 아이템 — auto margin 이 stretch 를 깨서 width 명시 */
.nform_split            { display: flex; align-items: stretch; }
.nprev                  { flex: 0 1 420px; min-width: 300px; display: flex; flex-direction: column; padding-left: 20px; border-left: 1px solid var(--line); }
.nform textarea         { height: 250px; padding: 10px 12px; line-height: 1.7; resize: none; }
.nfrange                { display: flex; align-items: center; gap: 8px; }
.nftil                  { flex: 0 0 auto; color: var(--t3); font-weight: 600; }
.nfrange .dtp_trig      { flex: 1; min-width: 0; }
.nfrange .dtp_trig .pk_ico { padding-left: 6px; }
/* 시각 칸은 좁힌다 — '09:00' 다섯 자가 '2026-09-10' 열 자와 같은 폭을 먹으면 빈 칸이 커 보인다 */
.nfrange .dtp_trig[data-dtp="time"] { flex: 0 0 82px; }

.nform .afield em.hint  { font-style: normal; font-weight: 500; }
.nform .afield > span .chk { margin-left: 8px; }
/* 간격은 gap 하나로 둔다 — 공용 .afield 의 margin-bottom 에 맡기면 margin 이 없는 .notice·details 와
   섞여 칸 사이가 제각각이 된다(그래서 .afield 쪽 margin 은 이 폼 안에서만 끈다). */
.nform_pane             { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 16px; padding-right: 20px; }
.nform_pane .afield,
.nform_pane .chk        { margin-bottom: 0; }

/* 노출 시작·종료는 나란히 둔다 — 한 기간의 두 끝이라 위아래로 떨어지면 짝이 안 읽힌다.
   접기(details)를 쓰지 않는다: 접으면 노출 기간을 못 본 채 저장해 예약 발행을 빼먹는다. */
.nfcat                  { display: flex; align-items: center; gap: 12px; }
.nfcat .chk             { margin-left: auto; white-space: nowrap; }
.nform_pane > .hint     { margin: -6px 0 0; color: var(--t1); font-size: var(--text-xs); line-height: 1.55; }

/* 글자수 — 세 자리를 오갈 때 뒤따르는 "/ 4,000" 이 안 흔들린다(.ntfy_cnt 와 같은 이유) */
.nfcnt                  { margin: 3px 0 0; color: var(--t3); font-size: var(--text-xs); font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }

/* ── 앱 미리보기 ── 저장 전 값이라 서비스 preview 엔드포인트를 못 쓰고 여기서 근사치를 그린다.
   ★ SYNC:notice-app-preview — 값·구조 정본은 서비스 레포다:
     web/menu/mypage/_notice_style.ftl(색 토큰 --c-* · 치수) · _notice_items.ftl(마크업).
     정본이 바뀌면 아래 값도 같이 옮긴다. 사본이라 앵커를 박아 둔다(SYNC:notice-push-lock 과 같은 기법).
   ★ 값은 전부 .text() 로 넣는다 — 저장 전 원문이 그대로 실행되는 자리를 만들지 않는다. */
.nprev                  { --npfg: #4E78C4; --npbg: #DCE9FF; --nptint: #EEF4FF; }
.nprev [data-cat="FIX"]    { --npfg: #C46A4E; --npbg: #FFE4D6; --nptint: #FFF3EC; }
.nprev [data-cat="EVENT"]  { --npfg: #C45E84; --npbg: #FDEDE9; --nptint: #FFF1F6; }
.nprev [data-cat="UPDATE"] { --npfg: #7C5DB8; --npbg: #ECE4FB; --nptint: #F6F1FE; }
.nprev [data-cat="NOTICE"] { --npfg: #4E78C4; --npbg: #DCE9FF; --nptint: #EEF4FF; }

/* 카드를 꽉 채운다 — 앱 화면 자체가 이 칸의 내용이라 카드 안에 또 액자를 두지 않는다 */
.npitem                 { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.nphd                   { padding: 0 0 13px; }
.npmeta                 { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.npbadge                { display: inline-block; padding: 3px 9px; border-radius: var(--radius-pill); background: var(--npbg); color: var(--npfg); font-size: 11px; font-weight: 600; letter-spacing: -.2px; }
.npnew                  { flex: 0 0 auto; padding: 2px 6px; border-radius: 6px; background: linear-gradient(120deg, #F5A87F, var(--accent)); color: #fff; font-size: 9.5px; font-weight: 700; letter-spacing: .3px; }
.npsubject              { color: var(--t1); font-size: 14.5px; font-weight: 600; letter-spacing: -.3px; line-height: 1.4; }
.npdate                 { margin-top: 5px; color: var(--t2); font-size: 11.5px; font-weight: 600; }

.npbody                 { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 0 0; border-top: 1px solid var(--line); }
/* 줄 하나가 앱의 .notice_line 하나다 — 간격이 2px 이라 빈 줄이 곧 단락 사이가 된다 */
.nptext                 { color: var(--t1); font-size: 13.5px; line-height: 1.7; letter-spacing: -.2px; }
.npline                 { display: block; margin-top: 2px; }

.npinfo                 { display: flex; align-items: center; gap: 9px; margin-top: 13px; padding: 11px 13px; border-radius: 12px; background: var(--nptint); color: var(--npfg); font-size: 13px; font-weight: 600; letter-spacing: -.2px; line-height: 1.4; }
.npinfo[hidden]         { display: none; }
.npinfo .ph             { flex: 0 0 auto; font-size: 15px; }

:root[data-theme="dark"] .nprev                  { --npfg: #9DBDF5; --npbg: #233351; --nptint: #1E2533; }
:root[data-theme="dark"] .nprev [data-cat="FIX"]    { --npfg: #F0A98E; --npbg: #4A2F22; --nptint: #2F261E; }
:root[data-theme="dark"] .nprev [data-cat="EVENT"]  { --npfg: #F09FBE; --npbg: #4A2533; --nptint: #2F222A; }
:root[data-theme="dark"] .nprev [data-cat="UPDATE"] { --npfg: #C2A7F0; --npbg: #322547; --nptint: #261F33; }
:root[data-theme="dark"] .nprev [data-cat="NOTICE"] { --npfg: #9DBDF5; --npbg: #233351; --nptint: #1E2533; }

@media (max-width: 1100px) {
    .nform         { max-width: 760px; }
    .nform_split   { flex-direction: column; }
    .nform_pane    { padding-right: 0; }
    .nprev         { flex: none; margin-top: 18px; padding: 18px 0 0; border-left: 0; border-top: 1px solid var(--line); }
}

/* ══════════════════════════════════════════════════════════════════════════
   알림 — 대상 지정 메시지 발송 화면(/notify/**, 2026-09-05)

   ★ 이 절은 어제(2026-09-04) 공지 발송 모달에 얹었던 "대상 선택 2단"의 후신이다. 규칙을 새로 쓴
     것이 아니라 자리를 옮겼다 — 대상 산정은 공지가 아니라 알림의 것이기 때문이다. 근거: 앱 공지
     목록은 liveFilter 넷만 보고 대상 컬럼을 보지 않아, **대상을 좁혀도 문서는 전 회원이 봤다.**
     공지 모달이 1단으로 돌아가면서 그 클래스들이 그쪽에 남았다면 전부 죽은 CSS 다. 이름 대응:
       .nask_steps*      → 삭제. 여기는 모달 위저드가 아니라 페이지 폼이라 진행바가 없다
       #nask_target      → 삭제. 숨겼다 여는 블록이 아니라 화면 본문이다(.hint 여백만 .ntfy_form 이 승계)
       .nask_aud*        → .ntfy_aud*
       .nask_target_sum  → .ntfy_sum
       .tag.target       → .tag.ntfy_tgt
       .rpick.rpick_grid → 그대로. .rpick 의 변형이지 이 기능 전용 이름이 아니다

   ★ 새 색·새 반경을 만들지 않는다 — 있는 토큰만 쓴다(DESIGN.md §2-1).
   ★ 플랫폼(단일선택)·메뉴(다중선택) 카드도 새로 만들지 않는다 — 계정 화면의 등급 고르기(.rpick,
     common.css:1967)·회원 화면의 기간 고르기(.rpick.rpick_row)를 그대로 쓴다(DESIGN.md §2-1-7
     "배지는 신규 생성보다 재사용"). 메뉴만 다중 선택이라 그리드 변형(.rpick_grid) 한 벌을 얹는다 —
     선택 표시(.on)·input 숨김은 .rpick 에서 그대로 상속되므로(라디오든 체크박스든 input 은 안
     보이고 카드 테두리가 선택을 말한다) 새로 정의하지 않는다.
   ══════════════════════════════════════════════════════════════════════════ */

/* 발송 폼 — 공지 작성 폼(.nform)과 같은 폭·같은 문법이다. 두 화면이 사이드바에서 나란히 붙어 있어
   폭이 다르면 메뉴를 오갈 때마다 본문 기둥이 흔들린다. 본문은 알림함 둘째 줄 한 줄짜리라 .nform 의
   textarea(300px)보다 훨씬 낮다 — 칸 크기가 곧 "얼마나 쓸 수 있는가"의 안내다. */
.ntfy_form                 { max-width: 760px;                                                      }
.ntfy_form textarea        { height: 86px; padding: 11px; border: 1px solid var(--line2); border-radius: 10px; background: var(--card); color: var(--t1); font-size: var(--text-md); font-family: inherit; line-height: 1.7; resize: vertical; }
.ntfy_form > .hint         { margin: 2px 0 0; color: var(--t1); font-size: var(--text-xs); line-height: 1.55; }  /* 예상 인원 아래 안내 두 줄은 .afield 밖에 있다 — .afield .hint(common.css:1951) 규칙을 그대로 한 번 더 */
.ntfy_form .afield em.hint { font-style: normal; font-weight: 500;                                  }
.ntfy_cnt                  { font-variant-numeric: tabular-nums;                                    }  /* 한 자리 ↔ 세 자리를 오갈 때 뒤따르는 "/ 100자" 가 안 흔들린다 */

/* 보내기 — 되돌릴 수 없는 유일한 동작이라 .btn.danger 다. 폼 하나에 버튼 하나뿐이라 전폭으로 둔다
   (공지 폼은 버튼이 둘이고 폼이 넓어 .btn.wide 기본 자연 폭을 쓴다 — 전폭 분할은 여기와 모달 푸터뿐이다). */
.ntfy_actions              { display: flex; margin-top: 16px;                                       }
.ntfy_actions .btn.wide    { flex: 1; height: 40px; border-radius: 12px;                            }

/* 메뉴 다중 선택 그리드 — 설명이 있는 셋(육아·임신·그룹, 칩 이름만으로 안 드러나는 것들)과 없는
   둘(가계부·일정)이 섞여 카드 높이가 들쭉날쭉하므로 행 높이를 고정하지 않는다(grid-auto-rows
   기본값 = 내용 크기).
   ⚠️ 반드시 .rpick.rpick_grid 로 적는다 — 단독이면 .rpick 과 명시성이 동률이라 소스 순서에 기댄다
      (.rpick_row·.tsub_row 선례). */
.rpick.rpick_grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px;                 }

/* 예상 대상자 수 — 조건을 바꿀 때마다(디바운스 400ms) 숫자가 바뀌는 게 보여야 한다. 로딩 중엔 값을
   지우지 않고 흐려서 "갱신 중"만 알린다 — 지우면 그 사이 아무 정보도 없어 체감 지연이 더 커진다.
   0명은 서버의 발송 거부(400)를 미리 알리는 것이라 경고 톤(--bad)이다.
   숫자 22px 은 매직넘버가 아니라 다른 화면의 '큰 숫자'(.sec_cell .n, common.css:1840)와 같은 값이다. */
.ntfy_aud                     { display: flex; align-items: baseline; gap: 8px; margin: 4px 0 12px; padding: 12px 14px; border-radius: var(--radius); background: var(--card-hover); }
.ntfy_aud_n                   { color: var(--t1); font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.4px; }
.ntfy_aud_l                   { color: var(--t2); font-size: var(--text-sm); font-weight: 600;      }
.ntfy_aud.loading .ntfy_aud_n { animation: ntfy_aud_pulse 1.1s ease-in-out infinite;                }  /* prefers-reduced-motion 은 파일 끝 전역 규칙(*,*::before,*::after{animation:none!important})이 끈다 */
.ntfy_aud.zero                { background: var(--bad-bg);                                          }
.ntfy_aud.zero .ntfy_aud_n,
.ntfy_aud.zero .ntfy_aud_l    { color: var(--bad);                                                  }
/* 0명 안내 — 숫자만 빨갛게 뜨면 운영자가 고장으로 읽는다(설계 §11-1). 왜 0인지를 바로 아래에서 말한다.
   ⚠️ 경고 톤이되 --bad 는 안 쓴다: 위 카운터가 이미 --bad 라 같은 색을 두 번 쓰면 "막혔다"로 읽히는데,
   0명은 조건을 넓히면 풀리는 상태이지 오류가 아니다. 한 단계 눅인 --t2 로 설명 톤을 유지한다. */
.ntfy_zero_hint               { margin: -6px 0 12px; color: var(--t2); line-height: 1.55;           }

@keyframes ntfy_aud_pulse { 0%, 100% { opacity: .4; } 50% { opacity: .85; } }

/* 확인 모달의 대상 요약 한 줄 — 조건 배지 + 예상 인원. 인원을 오른쪽 끝으로 미는 것은 "누구에게 ·
   몇 명"이 한 줄에서 읽히게 하기 위해서다. */
.ntfy_sum   { display: flex; align-items: center; gap: 6px; margin: 0 0 10px; color: var(--t1); font-size: var(--text-sm); font-weight: 600; }
.ntfy_sum_n { margin-left: auto; color: var(--t2); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* 대상 조건 배지(전체 / 안드로이드 · 가계부) — .tag 계열이되, 메뉴를 다 고르면 "iOS · 가계부 ·
   육아 · 임신 · 일정 · 그룹"까지 길어질 수 있어 기본 .tag 의 nowrap(common.css:864)을 풀고 줄바꿈을
   허용한다 — 잘라서 안 보이게 하느니 두 줄로 보이는 편이 낫다. 되돌릴 수 없는 발송의 마지막 확인이라
   조건이 다 보이는 것이 이 배지의 존재 이유다. */
.tag.ntfy_tgt { max-width: 100%; overflow-wrap: break-word; white-space: normal; text-align: left; background: var(--card-hover); color: var(--t2); }

/* 확인 모달의 문구 미리보기 — 알림함에 실제로 뜰 두 줄을 그대로 보여 준다. 이 알림은 눌러서 볼
   전문이 없어(딥링크 없음) 여기 두 줄이 받는 사람이 보게 될 전부다. */
.ntfy_prev            { margin: 0 0 14px; padding: 12px 14px; border-radius: var(--radius); background: var(--card-hover); }
.ntfy_prev b          { display: block; color: var(--t1); font-size: var(--text-md); font-weight: 700; word-break: break-word; }
.ntfy_prev span       { display: block; margin-top: 3px; color: var(--t2); font-size: var(--text-sm); line-height: 1.55; word-break: break-word; }
.ntfy_prev span:empty { display: none;                                                              }  /* 본문을 비우면 알림함에서도 그 줄이 없다 — 미리보기도 같아야 한다 */

/* 좁은 화면(이 앱의 모바일 분기 기준, common.css:266) — 2열이 비좁아지면 메뉴 카드를 세로로 편다 */
@media (max-width: 720px) {
    .rpick.rpick_grid { grid-template-columns: 1fr;                                                 }
}



/* ===== 공지사항 목록 ===== */

/* 열 폭을 style 로 못박은 표라 fixed 로 가둔다 — auto 면 내용에 따라 컬럼이 늘줄어 페이지를
   넘길 때마다 표가 좌우로 움직인다. */
.ntbl                        { table-layout: fixed;                }
.ntbl tbody tr:last-child td { border-bottom: 0;                   }

/* 상태 칩은 폭을 맞춘다 — 글자수(게시 2자 ~ 예약 대기 4자+공백)에 따라 알약이 들쭉날쭉하면
   한 열을 훑는 눈이 왼쪽 경계를 매번 다시 찾는다. 최장값 기준이고 공용 .tag 는 건드리지 않는다 */
.nl_st .tag { min-width: 68px; }

/* 값에 딸리는 부가 한 줄 — 푸시 발송 열의 인원·발송 대기·발송 예약됨이 이 톤을 쓴다 */
.nl_sub {
    display    : inline-flex;
    align-items: center;
    gap        : 3px;
    color      : var(--t1);
    font-weight: 500;
}
.nl_sub .ph  { font-size: var(--text-sm); }
.nl_sub.warn { color: var(--warn);        }
.nl_sub.bad  { color: var(--bad);         }
.nl_sub.go   { color: var(--accent-ink);  }

/* 값에 딸리는 부가 한 줄 — 푸시 발송 인원·강조 문구가 이 톤을 쓴다 */

/* 시각·날짜 값 — 노출 기간·등록일·푸시 발송이 같은 톤을 쓴다. tabular-nums 로 자리가 흔들리지 않게 */
.nl_pd     { color: var(--t1); white-space: nowrap; font-variant-numeric: tabular-nums; }


/* 아래쪽 행은 위로 열린다 — 표 컨테이너가 overflow-x:auto 라 넘친 메뉴가 잘린다 */
.kmenu.up { top: auto; bottom: calc(100% - 40px); }

/* 임시저장 — 상태 배지 톤이 게시·숨김·삭제 셋뿐이라 DRAFT 가 무채로 떨어진다 */
.tag.st_DRAFT { background: var(--pur-bg);     color: var(--pur);  }
.tag.sched    { background: var(--info-bg);    color: var(--info); }  /* 예약 대기 — 아직 안 뜬 것 */
.tag.past     { background: var(--card-hover); color: var(--t3);   }  /* 만료 — 지난 것은 조용히. 삭제(bad)와 색이 겹치지 않게 회색이다 */


/* ── 목록 2열 — 왼쪽 표 · 오른쪽 드로어 (2026-09-10 확정, D안) ──
   표에서 뺀 푸시 발송·최종 수정·삭제 사유·삭제 시각과 처리 여섯이 전부 드로어로 갔다.
   삭제 행도 본표에 섞여 오므로(매퍼 statusFilter 의 ALL 갈래) 부표가 없다.
   ★ 드로어는 고르기 전에도 자리를 지킨다(.empty) — 나중에 끼어들면 표 폭이 그때 튄다.
   ★ .drawer · .dr_hd · .dr_sec · .tri_dl · .tri_btns · .rdetail · .qlink 는 미디어·신고 큐가 이미 쓰는
     공용 부품이다. 공지 쪽 치수 조정은 전부 .nl_deck 안으로 좁혀 그 화면들에 닿지 않게 한다. */
.nl_deck   { display: flex; align-items: stretch; gap: 14px; height: calc(100vh - 292px); min-height: 430px; }
/* 표가 남는 폭을 먹는다. 열은 전부 폭을 갖고(가변 열 금지) table-layout: fixed 가 그 폭을 비율로 써서
   넓혀 준다 — 한 열만 늘어나지 않고 일곱이 함께 커진다. 지정값(상태 90 · 분류 78 · 제목 250 ·
   등록일 102 · 노출 기간 146 · 올린 사람 90 · 푸시 발송 110)은 그 비율의 기준이다.
   행이 많아 세로 스크롤바가 생기면 헤더·본문이 함께 좁아진다(thead 가 sticky 라 같이 줄어든다) —
   자리를 미리 비워 두면 그 15px 이 표 오른쪽에 흰 띠로 남아 헤더가 끊겨 보인다.
   드로어는 300 으로 못박는다 — 서랍이 넓어지면 앱 미리보기가 실제 폰보다 커져 "앱에서 이렇게 보인다"가 거짓이 된다. */
.nl_list   { flex: 1; min-width: 0; display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.nl_scroll { flex: 1; min-height: 0; overflow-y: auto; }

/* 헤더는 붙여 둔다 — 표가 안에서 스크롤되므로 내려간 순간 무슨 컬럼인지 사라진다 */
.ntbl thead th { position: sticky; top: 0; z-index: 1; height: 41px; }
.nl_row td     { height: 62px; }  /* 데이터 행만 — erow 는 공통 820 을 따른다 */

/* 고른 행 — 배경 강조만으로 드로어와 짝을 보인다(왼쪽 막대는 띠처럼 읽혀 뺐다). 만료 행은 흐리되 고른 순간은 또렷하게 */
.nl_row                              { cursor: pointer; }
.nl_row[aria-current] td             { background: var(--accent-soft); }
.ntbl tr.exp td                      { opacity: .55; }
.ntbl tr.exp[aria-current] td        { opacity: 1;   }

/* 제목 칸 — 두 줄까지 보이고 그 아래 한 줄에 강조 문구·고정 표시가 붙는다 */
.nl_t        { min-width: 0; }
.nl_tt       { display: flex; align-items: flex-start; gap: 5px; min-width: 0; color: var(--t1); line-height: 1.45; }
.nl_tt b     { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; min-width: 0; overflow: hidden; }
.nl_tt .ph   { flex: none; margin-top: 2px; color: var(--t2); font-size: var(--text-sm); }
.nl_meta     { display: flex; align-items: center; gap: 8px; margin-top: 3px; color: var(--t3); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.nl_meta .ph { font-size: var(--text-sm); }
.nl_meta .hl { display: inline-flex; align-items: center; gap: 3px; color: var(--accent-ink); }
/* 드로어 무대 — 앱 미리보기(.np*)를 공지 폼과 같은 부품으로 앉힌다.
   .nprev 는 폼 2열용 치수(flex 0 1 420px · min-width 300 · padding-left 20 · border-left)를 갖고 있어
   그 넷을 여기서 덮는다. 무대는 서랍 폭을 꽉 채운다 — 좌우 패딩을 두면 미리보기가 실제 폰보다
   좁아져 "앱에서 이렇게 보인다"가 흐려진다. display:flex column 은 남긴다(.npitem 의 flex:1 이 전제)
   ★ 카드 안 여백은 .nprev 가 아니라 .nphd·.npbody 가 갖는다 — 부모에 몰면 .npbody 의 구분선이 같이
     들여써져 앱과 달라진다(앱은 카드 폭 전체, SYNC:notice-app-preview — 정본 .notice_hd·.notice_body) */
.nl_stage         { border-bottom: 1px solid var(--line); background: var(--soft); }
.nl_stage .nprev  { flex: none; min-width: 0; padding: 0; border-left: 0; background: var(--card); }
.nl_stage .nphd   { padding: 15px 16px; }
.nl_stage .npbody { overflow: visible; padding: 14px 16px 16px; }

.nl_none   { display: grid; place-items: center; place-content: center; height: 100%; padding: 0 34px; color: var(--t1); font-size: var(--text-md); line-height: 1.65; text-align: center; }
.nl_none i { display: block; margin-bottom: 9px; color: var(--t3); font-size: var(--text-title); }

/* 공용 드로어 부품을 공지 치수로 — 미디어·신고 큐 드로어에 닿지 않게 .nl_deck 안으로 좁힌다 */
.nl_deck .dr_body > .dr_sec:first-child { border-top: 0; }
/* 서랍 처리 버튼 — 수정·발행·숨김·삭제 넷이 한 줄에 선다(버튼당 95px). .pri 의 전폭은 풀고
   복구만 단독으로 뜨므로 전폭으로 되돌린다 */
.nl_deck .dr_act .tri_btns            { grid-template-columns: repeat(4, 1fr); }
.nl_deck .dr_act .tri_btns button     { height: 34px;        }
.nl_deck .dr_act .tri_btns button.pri { grid-column: auto;   }
.nl_deck .dr_act #dr_restore          { grid-column: 1 / -1; }
.nl_deck .drawer.empty .dr_sec,
.nl_deck .drawer.empty .nl_stage      { display: none; }  /* 고르기 전에는 빈 미리보기 무대를 띄우지 않는다 */
.nl_deck .drawer:not(.empty) .nl_none { display: none; }  /* 고른 뒤에는 안내를 치운다 — 값이 그 자리를 채운다 */
.nl_deck .drawer { flex: 0 0 440px; }     /* 앱 미리보기가 실제 폰 폭(360)에 가깝게 — 무대·카드 패딩 66 을 빼면 372 */
/* 서랍 헤더는 왼쪽 표 헤더와 한 줄에 선다 — 높이·글자·배경을 같은 값으로 맞춘다(41px).
   .dr_hd 는 공용이라 .nl_deck 안에서만 덮는다(미디어·신고 큐 서랍은 그대로) */
.nl_deck .dr_hd   { height: 41px; padding: 0 12px 0 17px; background: var(--soft); }
.nl_deck .dr_hd b { font-size: var(--text-sm); font-weight: 600; }

/* 전 회원 발송 — 되돌릴 수 없는 유일한 동작이라 구분선 아래 따로 둔다. 케밥 시절 .nl_send 와
   이름이 겹치지 않게 dr_ 접두를 쓴다. 서버 엔드포인트가 살아 있고 이 서랍이 유일한 진입점이다 */
.dr_send      { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.dr_send p    { margin: 0 0 8px; color: var(--bad); font-size: var(--text-xs); font-weight: 600; line-height: 1.55; }
.dr_send .btn { width: 100%; }
/* ===== hover — 마우스 기기 전용 (이 파일의 모든 :hover 를 여기 한 곳에 모음) ===== */

@media (hover: hover) {
    .btn:not(:disabled):hover     { background: var(--accent-hover);                                    }
    .btn.cta:not(:disabled):hover { background: var(--grad-btn);                                       }  /* 그라데이션 유지 — 호버로 솔리드가 되면 다른 버튼처럼 보인다 */
    .btn.add:not(:disabled):hover { background: var(--grad-add-btn);                                   }
    .ghost:hover                  { border-color: var(--accent); color: var(--accent);                  }
    .gate_out button:hover        { color: var(--t1);                                                   }
    .nav a:not(.on):hover         { background: var(--card-hover);                                      }  /* .on 상태를 덮지 않게 제외 */
    .side_head button:hover       { background: var(--card-hover); color: var(--t1);                    }
    .side_foot form button:hover  { border-color: var(--accent); color: var(--accent);                  }
    .grid tbody tr:hover          { background: var(--card-hover);                                      }
    .paging a:hover               { border-color: var(--accent); color: var(--accent-ink);              }
    .mtbl th a:hover              { color: var(--accent-ink);                                           }
    .act_btns button:hover        { border-color: var(--accent); color: var(--accent);                  }
    .act_btns button.danger:hover { border-color: var(--bad); background: var(--bad); color: #fff;      }
    /* 막대 색을 바꾸지 않고 칸을 강조한다 — 톤(ok/warn/info/accent)마다 hover 색을 따로 두면
       토큰이 네 벌로 늘고, --card-hover 는 라이트/다크에서 방향이 알아서 뒤집힌다. */
    .bar_col:hover                { background: var(--card-hover);                                      }
    .bar_col:hover .bar.zero      { background: var(--t3);                                              }  /* 0 인 날도 어느 칸인지는 보여야 한다 */
    /* 신고 큐 — 목록 줄·케이스 헤더·잉크 무대·처리 버튼. */
    .qrow:hover                   { background: var(--card-hover);                                      }
    .pback:hover                  { background: var(--card-hover); color: var(--t1);                    }
    .tri_nav a:hover              { border-color: var(--accent); color: var(--accent-ink);              }
    .mini:hover                   { border-color: var(--accent); color: var(--accent);                  }
    .ink_btn:hover                { background: var(--accent-hover);                                    }
    .ink_zoom:hover               { background: rgba(0, 0, 0, .68);                                     }
    /* 회원 관리 · 감사 로그 */
    .rz_chip:hover                { border-style: solid; border-color: var(--accent); color: var(--accent); }
    .alog tbody tr[data-href]:hover td { background: var(--card-hover);                                  }
    .alog tbody tr[aria-current]:hover td { background: var(--accent-soft);                                      }  /* 고른 행은 덮지 않는다 — 지금 뭘 보고 있는지 흐려진다 */
    .anear a:hover .d .tgt        { color: var(--accent-ink);                                            }
    /* 필터 세그먼트 — 선택된 칸(.on)은 덮지 않는다. 덮으면 지금 뭐가 걸렸는지 흐려진다 */
    .seg a:not(.on):hover,
    .seg button:not(.on):hover    { background: var(--soft-hover); color: var(--t1);                 }
    .ftog:not(.on):hover          { border-color: var(--accent); color: var(--accent);               }
    .freset:hover                 { color: var(--accent);                                            }
    /* 확대 보기 — 누르면 커진다는 걸 커서로 먼저 알린다 */
    .lbx_x:hover                  { background: rgba(255, 255, 255, .26);                               }
    [data-zoom]:hover             { cursor: zoom-in;                                                    }
    /* 관리자 계정 — .btn:hover 가 accent-hover 로 덮으므로 변형마다 다시 적는다.
       :disabled 는 제외한다. 잠긴 버튼이 색을 바꾸면 눌리는 줄 안다 */
    .btn.sm:not(:disabled):hover  { border-color: var(--accent); color: var(--accent); background: var(--card); }
    .btn.sm.bad:not(:disabled):hover { border-color: var(--bad); color: var(--bad);                     }
    .btn.plain:not(:disabled):hover { border-color: var(--accent); color: var(--accent); background: var(--card); }
    .btn.danger:not(:disabled):hover { background: #A82A2F;                                             }
    .rpick label:not(.on):hover   { border-color: var(--line2); background: var(--card-hover);           }
    .acand button:hover           { background: var(--card-hover);                                      }
}

/* ★ 가상요소를 따로 적는다 — * 는 ::before/::after 에 걸리지 않아, 가상요소에 준 transition 은
     이 규칙만으로는 죽지 않는다. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after { transition: none !important; animation: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   서버 상태 (SUPER 전용, /server/**)

   ★ 수식자를 단독 이름(.ok/.warn/.bad/.gap/.new)으로 두지 않고 전부 srv_ 안에서만 쓴다 —
     단독 수식자는 다른 화면과 특정도 동률로 부딪혀 서로를 덮는다(.pm 이 실제로 그렇게 났다).
   ══════════════════════════════════════════════════════════════════════════ */

/* 레벨 색은 글자에만 준다. 판정·신선도·AWS 가 저마다 배경을 칠하면 화면이 신호등이 되고,
   정작 봐야 할 '오늘 신규' 태그가 그 안에 묻힌다. */
.srv_lv.ok        { color: var(--ok);                                                            }
.srv_lv.warn      { color: var(--warn);                                                          }
.srv_lv.bad       { color: var(--bad);                                                           }
.srv_lv.dead      { color: var(--bad);                                                           }

/* 상태 헤더 카드 — 왼쪽 "지금 봐야 하나"(판정·신선도) · 오른쪽 "지금 얼마인가"(시계·현재값 4칸) */
.srv_head         { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 9fr);         }
.srv_hl           { display: flex; flex-direction: column; justify-content: center; gap: 12px; min-width: 0; padding: 16px 18px; border-right: 1px solid var(--line); word-break: keep-all; }
.srv_verdict b    { display: block; font-size: var(--text-title); font-weight: 700; letter-spacing: -.4px; line-height: 1.25; }
/* 근거는 2줄에서 자른다 — partList 는 게이지·컨테이너·AWS·신선도·신규 예외를 전부 잇는 문자열이라
   길이 상한이 없다. 안 자르면 헤더 왼쪽만 늘어나 오른쪽 현재값 4칸이 세로로 벌어진다(전문은 title). */
.srv_verdict span { display: -webkit-box; overflow: hidden; margin-top: 3px; color: var(--t1); font-size: var(--text-sm); line-height: 1.45; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

/* 신선도 두 줄 — 근거(span)까지 판단 정보라 회색으로 낮추지 않는다. 색은 부모에서 한 번만 준다. */
.srv_hfresh       { display: flex; align-items: flex-start; gap: 8px; min-width: 0; color: var(--t1); }
.srv_hfresh > i.ph { flex: none; margin-top: 2px; font-size: var(--text-2xl);                    }
.srv_hfresh div   { min-width: 0;                                                                }
.srv_hfresh b     { display: block; color: inherit; font-size: var(--text-md); font-weight: 600; line-height: 1.4; }
.srv_hfresh span  { display: block; margin-top: 2px; color: inherit; font-size: var(--text-sm); line-height: 1.4; font-variant-numeric: tabular-nums; }
.srv_hfresh.old,
.srv_hfresh.parse { color: var(--warn);                                                          }
.srv_hfresh.dead  { color: var(--bad);                                                           }
/* 정상일 때도 아이콘만은 색을 준다 — 장식 아이콘을 다 뺀 뒤 남은 하나라 무채색이면 아무 신호가 없다 */
.srv_hfresh.ok > i.ph { color: var(--ok);                                                        }

.srv_hr           { display: flex; flex-direction: column; min-width: 0;                         }
.srv_hclock       { display: flex; align-items: baseline; justify-content: flex-end; gap: 4px; padding: 10px 16px 0; }
.srv_scope        { color: var(--t2); font-size: var(--text-sm); white-space: nowrap;            }
.srv_clock        { flex: none; color: var(--t2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.srv_clock a      { color: inherit; text-decoration: underline;                                  }

/* 현재값 4칸 — 직접 재는 둘(CPU·메모리)과 수집기만 아는 둘(디스크·컨테이너)을 묶음으로 가른다.
   수집기가 멎으면 도장(.srv_stale)이 뒤 묶음에만 찍혀야 해서 DOM 도 그렇게 갈라 둔다. */
.srv_strip        { flex: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) minmax(0, 2fr); }
.srv_strip_g      { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); min-width: 0; }
.srv_strip > * + *,
.srv_strip_g > * + * { border-left: 1px solid var(--line);                                       }
.srv_cell         { display: flex; flex-direction: column; gap: 5px; min-width: 0; padding: 8px 12px 14px; }
.srv_cell_l       { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-height: 18px; }
.srv_cell_l b     { color: var(--t1); font-size: var(--text-sm); font-weight: 600;               }
.srv_cell_n       { color: var(--t2); font-size: var(--text-xs); line-height: 1.4; word-break: keep-all; font-variant-numeric: tabular-nums; }
/* 값은 왼쪽 종합 판정(.srv_verdict b)과 같은 급이다 — 여기만 키우면 "판정 = 값" 위계가 오른쪽으로 기운다 */
.srv_kv           { display: inline-flex; align-items: baseline; gap: 3px; color: var(--t1); font-size: var(--text-title); font-weight: 700; letter-spacing: -.5px; line-height: 1.15; font-variant-numeric: tabular-nums; }
.srv_kv em        { color: var(--t2); font-size: var(--text-sm); font-weight: 600; font-style: normal; letter-spacing: 0; }

/* 본문 2열 — 왼쪽 자원(추이·컨테이너·AWS) · 오른쪽 오류. 오른쪽이 표라서 넓다.
   ★ align-items: start 를 쓰지 않는다 — 오른쪽 카드가 예외 건수를 따라 자라, 탭·날짜를 고를 때마다
     바닥선이 왼쪽과 어긋난다. 오른쪽 열은 카드를 절대배치(.srv_cr)해 행 높이에 아무 말도 못 하게 둔다:
     행 높이 = 왼쪽 열 높이가 되고 카드가 그 높이를 꽉 채운다(넘치는 표만 안에서 스크롤).
   ★ 높이를 px 로 못 박지 않는다 — 왼쪽 높이의 큰 몫이 추이 밴드(aspect-ratio 5:1)라 창 폭을 탄다.
     한 폭에서 맞춘 숫자는 다른 폭에서 그만큼 어긋난다. */
.srv_grid2        { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 15px; }
.srv_col          { display: flex; flex-direction: column; gap: 15px; min-width: 0;              }
.srv_cr           { position: relative;                                                          }
.srv_cr > .panel  { position: absolute; inset: 0;                                                }

/* 게이지 — 색 단계는 View 가 정한다(임계값을 CSS 에 박지 않는다) */
.srv_gauge        { position: relative; height: 6px; border-radius: var(--radius-pill); background: var(--card-hover); overflow: hidden; }
.srv_gauge i      { position: absolute; inset: 0 auto 0 0; border-radius: var(--radius-pill); background: var(--ok); transition: width .3s ease; }
.srv_gauge.warn i { background: var(--warn);                                                     }
.srv_gauge.bad  i { background: var(--bad);                                                      }

/* 추이 밴드 — 고정비 5:1 좌표계(ServerTrendView.VIEW_W:VIEW_H = 500:100). CPU 와 메모리를 밴드
   둘로 나눠 각자 자기 축에 그린다. aspect-ratio 와 viewBox 비가 같아야 등척 배율이 왜곡을 안 만들므로
   셋을 늘 함께 고친다 — Java 의 VIEW_W · server/index.ftl 의 viewBox 와 격자 x2 · 여기의 aspect-ratio.
   ★ 폭 상한(max-width)으로 높이를 누르지 않는다. 전체 폭에 3:1 이면 1600px 화면에서 높이가 533px 까지
     자라 "추이밖에 안 보인다"가 됐고, 660px 상한을 걸었더니 이번엔 차트만 660px 에서 끊겨 상한이 없는
     .srv_xlab · .srv_legend 와 좌우가 어긋났다. 높이는 폭 상한이 아니라 비로 잡는다.
   ★ 높이를 px 로 못 박거나 max-height 로 자르지 않는다 — 등척 배율은 min(폭/vbW, 높이/vbH) 라
     상자가 비보다 넓어지는 순간 좌우 레터박스, 즉 여기서 고친 그 증상이 그대로 되돌아온다.
     preserveAspectRatio="none" 으로 늘리는 것도 금지다(TemplateInvariantCheck 가 막는다) —
     기울기가 왜곡되고 non-scaling-stroke 는 선 두께만 균질하게 할 뿐 좌표를 되돌리지 못한다.
   ★ 선에 non-scaling-stroke 가 필요한 건 폭 상한을 걷었기 때문이다. 배율이 뷰포트를 타서
     그냥 두면 900px 1.8px ↔ 1920px 5.0px 로 두께가 흔들린다. */
.srv_band         { position: relative; aspect-ratio: 5 / 1;                                     }
.srv_band + .srv_band { margin-top: 10px;                                                        }
.srv_band svg     { display: block; width: 100%; height: 100%; overflow: visible;                }
.srv_line         { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.srv_area         { fill: color-mix(in srgb, var(--accent) 12%, transparent); stroke: none;      }
.srv_band.mem .srv_line { stroke: var(--info);                                                   }
.srv_band.mem .srv_area { fill: color-mix(in srgb, var(--info) 12%, transparent);                }
.srv_grid         { stroke: var(--line2); stroke-width: 1; vector-effect: non-scaling-stroke;    }

/* 밴드 라벨 — 왼쪽 이름·단위, 오른쪽 축 범위. 카드색 배경을 깔아 선 위에 겹쳐도 읽힌다.
   B 안처럼 왼쪽에 라벨 열을 따로 두면 이 열(≈430px)에서 플롯이 300px 로 준다. */
.srv_bl,
.srv_br           { position: absolute; top: 0; z-index: 2; background: var(--card); font-size: var(--text-xs); line-height: 16px; white-space: nowrap; pointer-events: none; }
.srv_bl           { left: 0; display: inline-flex; align-items: center; gap: 6px; padding-right: 6px; color: var(--t1); font-weight: 600; }
.srv_bl em        { display: inline-block; width: 14px; height: 3px; border-radius: 2px; font-style: normal; }
.srv_bl .u        { margin-left: -2px; color: var(--t2); font-weight: 500;                       }
.srv_br           { right: 0; padding-left: 6px; color: var(--t2); font-weight: 500; font-variant-numeric: tabular-nums; }

/* 오류 틱 — 예외가 난 시각. CPU·메모리와 같은 x축이라 "그때 무슨 일이 있었나"가 한 줄로 읽힌다.
   SVG 가 아니라 HTML 이라 위 5:1 비 규약 대상이 아니다(높이 고정). */
.srv_ticks        { position: relative; height: 26px; margin-top: 10px; border-top: 1px solid var(--line2); border-bottom: 1px solid var(--line2); }
.srv_ticks .srv_bl { top: 4px;                                                                   }
/* z-index 는 왼쪽 '오류' 라벨(.srv_bl, z-index 2)보다 높아야 한다 — 라벨 폭 안에 든 틱, 즉 창 맨 앞
   1~3% 에 난 예외가 통째로 가려진다. 밴드에서 라벨이 선을 덮는 건 선이 이어져 읽히므로 다른 얘기다. */
.srv_tk           { position: absolute; top: 5px; bottom: 5px; z-index: 3; width: 2px; margin-left: -1px; border-radius: 1px; background: var(--bad); }
/* 폭 2px 을 마우스로 짚어야 title 이 읽힌다 — 투명 패드로 히트영역만 넓힌다(그림은 그대로) */
.srv_tk::after    { content: ""; position: absolute; inset: -4px -5px;                           }
.srv_tk.new       { background: var(--warn);                                                     }

.srv_xlab         { display: flex; justify-content: space-between; margin-top: 6px; color: var(--t2); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
/* 색의 뜻을 말하는 자리라 읽고 판단하는 정보다 — 회색으로 낮추지 않는다 */
.srv_legend       { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; color: var(--t1); font-size: var(--text-sm); }
.srv_legend em    { display: inline-block; width: 14px; height: 3px; margin-right: 5px; border-radius: 2px; vertical-align: 2px; font-style: normal; }
.srv_legend .sw_cpu,
.srv_bl .sw_cpu   { background: var(--accent);                                                   }
.srv_legend .sw_mem,
.srv_bl .sw_mem   { background: var(--info);                                                     }
.srv_legend .sw_tk { background: var(--bad);                                                     }
.srv_legend .sw_tk.new { background: var(--warn);                                                }

/* 컨테이너 — 1줄 이름(+활성/대기) · 2줄 게이지 | 메모리·CPU */
.srv_conts        { display: flex; flex-direction: column;                                       }
.srv_ct           { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px 12px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.srv_ct:first-child { padding-top: 0;                                                            }
.srv_ct:last-child { padding-bottom: 0; border-bottom: 0;                                        }
.srv_ct_n         { grid-column: 1 / -1; display: flex; align-items: center; gap: 7px; min-width: 0; }
.srv_ct_n b       { min-width: 0; overflow: hidden; color: var(--t1); font-size: var(--text-md); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.srv_ct_n .tag    { flex: none;                                                                  }
.srv_ct_v         { color: var(--t1); font-size: var(--text-sm); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* AWS 3칸 한 줄 */
.srv_aws          { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));             }
.srv_aws_i        { min-width: 0; padding: 0 12px;                                               }
.srv_aws_i:first-child { padding-left: 0;                                                        }
.srv_aws_i:last-child { padding-right: 0;                                                        }
.srv_aws_i + .srv_aws_i { border-left: 1px solid var(--line);                                    }
.srv_aws_i .nm    { display: flex; align-items: center; gap: 6px; min-width: 0;                  }
.srv_aws_i .nm b  { color: var(--t1); font-size: var(--text-sm); font-weight: 700; letter-spacing: .3px; }
.srv_aws_i .st    { display: block; margin-top: 2px; overflow: hidden; font-size: var(--text-md); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
/* min-height 는 근거가 빈 칸(S3 등) 때문이다 — 세 칸이 border-left 로 나뉜 한 줄이라 마지막만
   한 줄 짧으면 줄이 오른쪽에서 잘린 것처럼 보인다. */
.srv_aws_i .n     { margin-top: 3px; color: var(--t2); font-size: var(--text-xs); line-height: 1.4; min-height: 1.4em; word-break: keep-all; font-variant-numeric: tabular-nums; }
/* AWS 일부 조회 실패 — 이 화면에서 그 사실을 말하는 유일한 문장이라 톤을 준다(카드 거터 안으로) */
.srv_awserr       { margin: 0; padding: 0 18px 16px; color: var(--bad); font-size: var(--text-sm); }

/* 오류 파도 — 14일 막대. 0건과 자료없음을 반드시 다르게 그리고, 자료 없는 날은 고를 수도 없다. */
.srv_wave_box     { padding: 16px 18px 8px;                                                      }
.srv_wave         { display: flex; align-items: flex-end; gap: 3px; height: 56px; padding-top: 4px; }
.srv_wave_d       { position: relative; flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; min-width: 0; }
.srv_wave_d i     { display: block; min-height: 2px; border-radius: 2px 2px 0 0; background: var(--bad); opacity: .82; }
.srv_wave_d.zero i{ height: 2px; background: var(--line2); opacity: 1;                           }
.srv_wave_d.gap i { height: 100%; border-radius: 2px; opacity: 1;
                    background: repeating-linear-gradient(135deg, var(--line2) 0 3px, transparent 3px 6px); }
.srv_wave_d:not(.gap) { cursor: pointer;                                                         }
.srv_wave_d.on    { border-radius: 3px; background: var(--accent-soft);                          }
.srv_wave_d.on i  { background: var(--accent); opacity: 1;                                       }
.srv_wave_x       { display: flex; gap: 3px; margin-top: 5px;                                    }
.srv_wave_x span  { flex: 1; min-width: 0; overflow: hidden; color: var(--t2); font-size: var(--text-xs); text-align: center; font-variant-numeric: tabular-nums; }

.srv_tabs_row     { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 4px 18px 12px; }
.srv_day          { color: var(--accent-ink); font-size: var(--text-sm); font-weight: 600; text-decoration: none; white-space: nowrap; }
/* 패널 머리의 안내문 — 읽고 판단하는 문장이라 .hint(t3) 가 아니라 t1 이다 */
.srv_note         { min-width: 0; overflow: hidden; color: var(--t1); font-size: var(--text-sm); font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }

/* 예외 표 — 원문이 아니라 묶음. 행을 누르면 아래 확장행이 열린다(스택은 늘 펼치면 한 화면을 다 먹는다).
   ★ 표만 카드 안에서 스크롤한다 — 카드 높이는 왼쪽 열이 정하므로(.srv_cr) 건수가 늘어도 카드는 안 자란다.
     헤더는 붙박이인데, border-collapse 표라 sticky 셀의 테두리는 같이 따라오지 않는다 —
     위 선은 상자가, 아래 선은 inset 그림자가 대신 그린다(안 그러면 행이 헤더 밑으로 선 없이 미끄러진다). */
.srv_tbl_box      { flex: 1; min-height: 0; overflow-y: auto; border-top: 1px solid var(--line);  }
.srv_tbl_box th   { position: sticky; top: 0; z-index: 1; box-shadow: inset 0 -1px 0 var(--line); }
.srv_tbl          { table-layout: fixed;                                                         }
.srv_tbl th,
.srv_tbl td       { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;              }
.srv_tbl td       { padding: 8px 11px;                                                           }  /* 2줄 셀(클래스명 + 태그·메시지) 42 + 패딩 16 이 62 안에 드는 값 */
.srv_erow td      { height: 62px;                                                                }  /* 공지 목록(.ntbl td)과 같은 2줄 표 높이 — 확장행(.srv_xrow)은 제외 */
/* 표에서 유일한 수량 열 — 우측 정렬이 세로 비교를 만든다(최근 열은 시각이라 대상이 아니다) */
.srv_tbl .srv_cnt { text-align: right;                                                           }
.srv_cls .nm      { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srv_hd           { display: flex; align-items: center; gap: 6px; min-width: 0; margin-top: 1px; }
.srv_hd .tag      { flex: none;                                                                  }
.srv_hm           { min-width: 0; overflow: hidden; color: var(--t1); font-size: var(--text-sm); text-overflow: ellipsis; white-space: nowrap; }
.srv_car          { padding-left: 0; color: var(--t2); text-align: center;                       }
.srv_car i        { display: inline-block; font-size: var(--text-md); transition: transform .12s; }
.srv_erow         { cursor: pointer;                                                             }
.srv_erow:hover td { background: var(--card-hover);                                              }
/* 오늘 신규 — 왼쪽 3px 바. 펼친 본문까지 이어져야 어느 묶음의 스택인지 눈이 안 잃는다 */
.srv_erow.new td:first-child,
.srv_erow.new + .srv_xrow td { box-shadow: inset 3px 0 0 var(--warn);                            }
.srv_erow.on .srv_car i { transform: rotate(90deg);                                              }
.srv_erow.on td   { border-bottom-color: transparent;                                            }
.srv_xrow         { display: none;                                                               }
.srv_erow.on + .srv_xrow:not(.srv_off) { display: table-row;                                     }
.srv_xrow td      { padding: 0 11px 10px; white-space: normal;                                   }
.srv_xmeta        { margin-bottom: 6px; color: var(--t1); font-size: var(--text-sm); word-break: break-all; font-variant-numeric: tabular-nums; }
.srv_trace        { margin: 0; padding: 11px 13px; border-radius: var(--radius-sm); background: var(--ground); color: var(--t1); font-family: var(--mono); font-size: var(--text-sm); line-height: 1.75; white-space: pre-wrap; word-break: break-all; }

.tag.srv_new      { background: var(--warn-bg); color: var(--warn);                              }
.tag.srv_gone     { background: var(--ok-bg);  color: var(--ok);                                 }

/* 필터·토글로 감춘 것 — 인라인 display 를 쓰지 않는다(확장행 규칙과 부딪힌다) */
.srv_off          { display: none;                                                               }

/* 빈 상태 — 표 안에서 말한다. 높이를 고정해 두어야 날짜를 골라 0건이 돼도 2열 높이가 안 흔들린다. */
.srv_none         { display: grid; place-items: center; min-height: 96px; padding: 12px; color: var(--t3); font-size: var(--text-md); text-align: center; white-space: normal; }
.srv_none small   { color: var(--t3);                                                            }
/* 같은 이유로 빈 카드도 자리를 지킨다 — 수집기가 멎으면 왼쪽 세 카드가 동시에 한 줄로 줄어
   2열의 좌우 높이가 크게 어긋나고 "여기 원래 무엇이 있었나"의 자리 감각이 사라진다.
   전역 .empty_msg 는 다른 메뉴가 쓰므로 이 화면 안에서만 높이를 준다. */
.srv_col .empty_msg { display: grid; place-items: center; min-height: 120px;                     }

/* 옛 값 도장 — 수집기가 멎으면 산출물 영역(추이·컨테이너·AWS·오류·헤더의 산출물 묶음)에 씌운다.
   ⚠️ 직접 재는 CPU·메모리 칸과 헤더 왼쪽(판정·신선도)에는 절대 씌우지 않는다 — 그 둘은 살아 있다.
   흐림은 ::before, 라벨 글자는 ::after 가 data-stale 속성에서 읽는다(문구는 Java 가 만든다). */
.srv_stale        { position: relative;                                                          }
.srv_stale::before { content: ""; position: absolute; inset: 0; z-index: 2; border-radius: inherit; background: color-mix(in srgb, var(--card) 62%, transparent); pointer-events: none; }
.srv_stale::after { content: attr(data-stale); position: absolute; top: 8px; right: 10px; z-index: 3; padding: 2px 8px; border-radius: var(--radius-sm); background: var(--bad-bg); color: var(--bad); font-size: var(--text-xs); font-weight: 700; white-space: nowrap; pointer-events: none; }
.srv_stale .panel_h .hint,
.srv_stale .srv_note { visibility: hidden;                                                       }  /* 우상단은 도장 자리 */
/* 헤더 스트립의 산출물 묶음은 카드 안이라 라벨을 바닥 줄에 둔다 — 위에 얹으면 게이지를 가린다 */
.srv_strip_g.srv_stale::after { top: auto; bottom: 12px; right: 50%; transform: translateX(50%); }

@media (max-width: 1100px) {
    .srv_grid2    { grid-template-columns: minmax(0, 1fr);                                       }
    .srv_head     { grid-template-columns: minmax(0, 1fr);                                       }
    .srv_hl       { border-right: 0; border-bottom: 1px solid var(--line);                       }
    /* 한 열로 쌓이면 맞출 왼쪽이 없다 — 카드를 흐름으로 되돌려 표가 제 길이대로 눕게 둔다 */
    .srv_cr > .panel { position: static;                                                         }
}

@media (max-width: 900px) {
    .srv_strip    { grid-template-columns: repeat(2, minmax(0, 1fr));                            }
    .srv_strip_g  { grid-column: 1 / -1; border-top: 1px solid var(--line); border-left: 0;      }
    .srv_aws      { grid-template-columns: minmax(0, 1fr);                                       }
    .srv_aws_i    { padding: 0;                                                                  }
    .srv_aws_i + .srv_aws_i { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); border-left: 0; }
}
