/*
 * npcDesign.css — N커넥트 플러그인 디자인 개선안 적용 (2026-08-21)
 *
 * 원안 = `E:\WorkSolutions\사내 자료\Npay 커넥트 플러그인\디자인 개선안` (CS/마케팅 팀장 기획 초안 8종).
 *   초안은 «최종 개념»이 아니라 방향 제시라, UI·UX·색감·배치는 여기서 정돈해 구현했다(사용자 지시).
 *   - 톤: 웜 크림 배경 + 오렌지 포인트 + 화이트 카드 (초안 유지)
 *   - 정돈: 타이포 스케일·간격 리듬 8px 그리드화, 대비(WCAG AA) 확보, 터치 타깃 최소 44px,
 *           키패드는 «외곽선» 유지(사용자 지시 — 현 N커넥트 터미널 키패드 계열), 상태색 체계화
 *   - 다크모드: 단말 베젤이 검정이라 기본 대비가 좋은 웜 다크 팔레트를 별도 정의(사용자 지시).
 *              토글은 메뉴에 있고 설정은 «서버»가 정본이다(웹뷰 재호출 시 클라 저장소 전멸 — npcTheme.js).
 *
 * 로드 순서: estreUi.css → main.css → [npcDesign.css]  (마지막 로드 = 동률 특이성에서 승리)
 * ⚠ 함정: estreUi 의 `article.constraint` 쇼트핸드가 낮은 특이성 오버라이드를 밀어낸다(카드 함정 ②).
 *         레이아웃 값은 `main#staticDoc > section > .container > article` 급으로 올려서 건다.
 */

/* ══════════════════════════════════════════════════════════════
   1. 디자인 토큰 — 라이트(기본) / 다크
   ══════════════════════════════════════════════════════════════ */
html {
    /* 브랜드 오렌지 — 초안보다 채도를 약간 낮춰 장시간 응시 피로를 줄임 */
    --npc-orange: #F5851F;              --npc-orange-rgb: 245 133 31;
    --npc-orange-press: #DC7010;
    --npc-orange-soft: #FFF0DF;

    --npc-radius-card: 20px;
    --npc-radius-btn: 16px;
    --npc-radius-key: 14px;

    --npc-gap: 8px;
    --npc-pad-x: 16px;

    /* 헤더/푸터 예약 높이 — 화면마다 동일 리듬 */
    --npc-head-h: 56px;
    --npc-foot-h: 44px;

    --npc-shadow-card: 0 1px 2px rgb(60 40 20 / 6%), 0 6px 18px rgb(60 40 20 / 6%);
    --npc-shadow-pop: 0 12px 40px rgb(40 25 10 / 22%);
}

/* 라이트 */
body {
    --npc-bg: #FBF2E7;
    --npc-bg-sunken: #F3E8DA;
    --npc-surface: #FFFFFF;
    --npc-surface-2: #FFFBF5;
    --npc-line: #EFE3D3;
    --npc-line-strong: #E2D3BF;

    --npc-ink: #201C17;
    --npc-ink-2: #5F564A;
    --npc-ink-3: #938878;
    --npc-ink-on-accent: #FFFFFF;

    --npc-accent: var(--npc-orange);
    --npc-accent-rgb: var(--npc-orange-rgb);
    --npc-accent-press: var(--npc-orange-press);
    --npc-accent-soft: var(--npc-orange-soft);

    --npc-danger: #DC3B2B;
    --npc-danger-soft: #FDEBE8;
    --npc-ok: #0EAA3D;   /* 성공 그린 — 채도 상향 55%→85%(사용자 지시 2026-08-21) */

    /* 상단 네비 필(캡슐) — 라이트에선 잉크 블랙 */
    --npc-pill-bg: #191512;
    --npc-pill-ink: #FFFFFF;
    /* 네이버페이 Primary — 이 도트는 «네이버페이» 표식임이 확정됐다(사용자 확인 2026-08-21).
       main.css 의 레거시 토큰(--color-imported-naver-green: #03C75A)은 NAVER 코퍼레이트
       그린이라 여기서 덮는다. 스피너(--npc-load-1)와 같은 값이어야 두 초록이 갈려 보이지 않는다.
       ※ 이 교체로 --color-imported-naver-green 은 참조처가 0이 됐다(레거시라 main.css 는 미수정). */
    --npc-pill-dot: #00DE5A;

    background-color: var(--npc-bg);
    color: var(--npc-ink);
}

/* 다크 — 단말 베젤(검정)과 이어지도록 웜 뉴트럴.
   🔧 2026-08-21 면 밝기 상향(사용자 지적 «흰색이었던 부분이 배경과 구분이 잘 안 된다»).
      종전 surface #221E19 는 bg 와 명도차가 4.6%p 뿐이라 대비 1.13:1 — 사실상 구분 불가였다.
      라이트 배경(#FBF2E7)의 색상각 33° 를 그대로 유지한 채 명도만 올린 사다리로 교체:
        surface 1.42 · surface-2 1.70 · line 2.03 · line-strong 2.71 (모두 bg 대비)
      본문 가독은 그대로 넉넉하다 — ink 11.5:1 · ink-2 6.5:1 (surface 위).
      ⚠ 라이트는 bg/surface 대비가 1.11 뿐이지만 그림자로 면이 분리된다. 다크에선 그림자가
        안 보이므로 «명도 자체»로 벌려야 한다 — 두 모드의 수치를 맞추려 하면 안 된다. */
body[data-dark-mode="1"] {
    --npc-bg: #15120F;
    --npc-bg-sunken: #0E0C0A;
    --npc-surface: #372F25;
    --npc-surface-2: #453B2E;
    --npc-line: #514638;
    --npc-line-strong: #665847;

    --npc-ink: #F5EFE6;
    --npc-ink-2: #C0B5A6;
    --npc-ink-3: #8B8172;
    --npc-ink-on-accent: #1A1206;

    --npc-accent: #FF9A2E;
    --npc-accent-rgb: 255 154 46;
    --npc-accent-press: #E8861C;
    --npc-accent-soft: #3A2A16;

    --npc-danger: #FF7A68;
    --npc-danger-soft: #3A201C;
    --npc-ok: #21ED5E;   /* 성공 그린(다크) — 채도 51%→85% */

    --npc-pill-bg: #2E2822;
    --npc-pill-ink: #F5EFE6;

    --npc-shadow-card: 0 1px 2px rgb(0 0 0 / 40%), 0 6px 18px rgb(0 0 0 / 30%);
    --npc-shadow-pop: 0 12px 40px rgb(0 0 0 / 55%);

    /* estre 계 공용 배경 토큰도 같이 뒤집는다 — estreUi.css 가 container/article/section 에 이 토큰으로
       흰 판을 깔아(main.css 라이트 정의 white), npc 토큰만 갈아끼우면 그 판들이 다크에서 흰색으로 남는다
       (2026-08-24 실기기 computed 실측: 스플래시 내부 255,255,255). */
    --common-bg-color: var(--npc-bg);
}

/* 색 전환은 짧게 — 토글이 «깜빡»이 아니라 «전환»으로 보이게 */
body, main#staticDoc > section, .container, article { transition: background-color 0.18s ease, color 0.18s ease; }


/* ══════════════════════════════════════════════════════════════
   2. 베이스 — 전역 고정 상/하단 제거 (화면마다 자체 헤더를 갖는 구조)
   ══════════════════════════════════════════════════════════════ */
header#fixedTop { display: none; }
footer#fixedBottom { display: none; }

main#staticDoc > section { background-color: var(--npc-bg); }
main#staticDoc > section > .container { background-color: transparent; }
/* ⚠ 아티클을 transparent 로 두면 «위에 띄운» 인스턴트 화면(결제내역·메모 등)이 아래 화면과 겹쳐 보인다(실기기 확인).
   섹션과 같은 배경을 아티클에도 준다 — 스택된 화면이 아래를 확실히 가린다. */
main#staticDoc > section.root_tab_content > .container > article { background-color: var(--npc-bg); color: var(--npc-ink); }
main#splashRoot, main#splashRoot > section#splash { background-color: var(--npc-bg); }
/* 스플래시 «내부» — estre 기본(estreUi.css:457-458)이 container/article 에 --common-bg-color 판을 깐다.
   staticDoc 계는 위에서 덮었지만 splashRoot 계가 빠져 있었다(다크 첫 페인트에 흰 판 — 실기기 실측).
   스플래시는 로고 하나뿐이라 투명이 정답 — 라이트/다크 모두 섹션 배경(--npc-bg)이 그대로 보인다. */
main#splashRoot > section#splash .container, main#splashRoot > section#splash article { background-color: transparent; }

/* 망고에듀 로고 — 스프라이트의 «Edu» 워드마크만 fill:currentColor 라 이 color 를 따른다(사용자 지시 2026-08-24).
   라이트는 종전 SVG 기본 fill 과 같은 검정 유지, 다크 배경(#15120F)에선 검정이 묻히므로 흰색.
   망고 마크(옐로 그라디언트 + 흰 «Mango» 글자)는 자체 fill 이라 두 모드 모두 불변. */
.npc_logo_svg { height: auto; color: #000; }
body[data-dark-mode="1"] .npc_logo_svg { color: #FFF; }

article { --article-theme-color: transparent; }
main button, main input, main textarea, main select { font-family: 'Pretendard'; color: var(--npc-ink); }

/* 스크롤 영역이 하단 브랜드 라인을 가리지 않도록 */
main#staticDoc > section.root_tab_content > .container { padding-bottom: 0; }


/* ══════════════════════════════════════════════════════════════
   3. 공통 헤더 — [좌 아이콘] [중앙 네비 필] [우 액션]
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article > nav.head_block.npc_head {
    position: relative;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--npc-gap);
    min-height: var(--npc-head-h);
    padding: 10px var(--npc-pad-x) 2px;
    background-color: transparent;
    border-style: none;
}
main#staticDoc article > nav.head_block.npc_head > .float_area {
    position: static;
    display: flex;
    align-items: center;
    gap: 4px;
}
/* 🪤 main.css 가 이 칸에 flex-direction:row-reverse 를 주고 있어 flex-end 가 «왼쪽»으로 밀린다.
   (appbarRight 가 줄곧 비어 있어 드러나지 않았던 결함 — QR 버튼을 넣자 즉시 노출됐다) */
main#staticDoc article > nav.head_block.npc_head > .float_area.appbarRight { flex-direction: row; justify-content: flex-end; }
main#staticDoc article > nav.head_block.npc_head > .head_center { display: flex; justify-content: center; }

/* 좌상단 아이콘 버튼(홈/메뉴) — 아이콘만, 44px 터치 타깃 */
.npc_icon_btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    border-radius: 12px;
    background-color: transparent;
    color: var(--npc-ink);
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
}
.npc_icon_btn:active { background-color: rgb(var(--npc-accent-rgb) / 12%); }
/* ⚠ 여기서 fill 을 일괄로 걸면 stroke 전용 아이콘(검색·새로고침, fill="none")까지 «채워진다»
      — CSS 가 프레젠테이션 속성을 이기기 때문. 채움이 필요한 아이콘은 마크업에서 fill="currentColor" 로 지정한다. */
.npc_icon_btn > svg { width: 26px; height: 26px; }

/* 중앙 네비 필 — 반대편 서비스로 건너뛰는 지름길 */
.nav_pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 38px;
    margin: 0;
    padding: 0 16px;
    border: none;
    border-radius: 999px;
    background-color: var(--npc-pill-bg);
    color: var(--npc-pill-ink);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.02em;
    white-space: nowrap;
    cursor: pointer;
}
.nav_pill:active { transform: scale(0.97); }
.nav_pill::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: var(--npc-pill-dot);
    flex: 0 0 auto;
}
.nav_pill > .arw { opacity: 0.85; font-size: 12px; }


/* ══════════════════════════════════════════════════════════════
   4. 제목 행 / 안내 문구
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .npc_title_row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px var(--npc-pad-x) 12px;
}
main#staticDoc article .npc_title_row > .npc_title {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--npc-ink-2);
    font-size: 21px;
    font-weight: 600;
    letter-spacing: -0.03em;
}
main#staticDoc article .npc_title_row > .npc_title b { color: var(--npc-ink); font-weight: 700; }
main#staticDoc article .npc_title_row > .npc_title_actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* 원형 액션 — 뒤로(채움) / 새로고침(외곽선) */
.npc_round_btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background-color: transparent;
    color: var(--npc-accent);
    cursor: pointer;
}
.npc_round_btn > svg { width: 24px; height: 24px; }
.npc_round_btn.filled { background-color: var(--npc-accent); color: var(--npc-ink-on-accent); }
.npc_round_btn.filled:active { background-color: var(--npc-accent-press); }
.npc_round_btn:not(.filled):active { background-color: rgb(var(--npc-accent-rgb) / 14%); }

/* 홈 히어로 문구 */
main#staticDoc article .npc_hero {
    padding: 28px var(--npc-pad-x) 24px;
    color: var(--npc-ink);
    font-size: 30px;
    font-weight: 800;
    line-height: 1.35;
    letter-spacing: -0.035em;
    text-align: center;
    word-break: keep-all;
}
main#staticDoc article .npc_hero b { color: var(--npc-accent); font-weight: 800; }

/* 화면 안내(입력 유도) */
main#staticDoc article .npc_guide {
    padding: 6px var(--npc-pad-x) 14px;
    color: var(--npc-ink-2);
    font-size: 19px;
    font-weight: 500;
    letter-spacing: -0.03em;
    text-align: center;
    word-break: keep-all;
}


/* ══════════════════════════════════════════════════════════════
   5. 홈 — 서비스 선택 카드
   ══════════════════════════════════════════════════════════════ */
main#staticDoc > section#home > .container > article.home { padding: 0; background-color: transparent; }
main#staticDoc article.home .service_choose_guide { display: none; }   /* npc_hero 로 대체 */

main#staticDoc article.home .service_cards {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 8px var(--npc-pad-x) 0;
}
main#staticDoc article.home button.service_card {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    width: 100%;
    min-height: 148px;
    margin: 0;
    padding: 20px;
    border: 1px solid var(--npc-line);
    border-radius: var(--npc-radius-card);
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
    color: var(--npc-ink);
    font-size: 30px;
    font-weight: 800;
    letter-spacing: -0.03em;
    cursor: pointer;
    transition: transform 0.12s ease;
}
main#staticDoc article.home button.service_card:active { transform: scale(0.985); }
main#staticDoc article.home button.service_card.primary {
    border-color: transparent;
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
}
main#staticDoc article.home button.service_card.primary:active { background-color: var(--npc-accent-press); }
main#staticDoc article.home button.service_card > img { width: 40px; height: 40px; opacity: 0.9; }
main#staticDoc article.home button.service_card.primary > img { filter: brightness(0) invert(1); }
body[data-dark-mode="1"] main#staticDoc article.home button.service_card.primary > img { filter: brightness(0); }


/* ══════════════════════════════════════════════════════════════
   6. 하단 브랜드 라인 (학원명)
   ══════════════════════════════════════════════════════════════ */
#npcBrandFoot {
    position: fixed;
    z-index: 8;
    left: 0;
    right: 0;
    bottom: calc(var(--bottom-pad) + 10px);
    display: flex;
    justify-content: center;
    align-items: center;
    height: 30px;
    color: var(--npc-ink);
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -0.03em;
    pointer-events: none;
}
/* 로그인·대기·스플래시 위에서는 숨김 (자체 브랜딩을 가림) */
/* ⚠ splash 의 data-on-top 은 부팅 후에도 1 로 남는다(실기기 확인) — 조건에 넣으면 영구 숨김이 된다. */
body:has(main#instantDoc > section#login[data-on-top="1"]) #npcBrandFoot,
body:has(main#instantDoc > section#wait[data-on-top="1"]) #npcBrandFoot,
body:has(main#instantDoc > section#deviceWait[data-on-top="1"]) #npcBrandFoot { display: none; }


/* ══════════════════════════════════════════════════════════════
   7. 키패드 — «외곽선» 유지 (사용자 지시: 현 N커넥트 터미널 키패드처럼)
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .num_keypad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    padding: 6px var(--npc-pad-x) 12px;
    align-content: center;
}
main#staticDoc article .num_keypad > button {
    min-height: 58px;
    margin: 0;
    padding: 0;
    border: 1px solid var(--npc-line-strong);
    border-radius: var(--npc-radius-key);
    background-color: var(--npc-surface);
    box-shadow: 0 1px 0 rgb(60 40 20 / 4%);
    color: var(--npc-ink);
    font-size: 26px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.08s ease, transform 0.08s ease;
}
main#staticDoc article .num_keypad > button:active {
    background-color: var(--npc-accent-soft);
    border-color: var(--npc-accent);
    transform: scale(0.97);
}
main#staticDoc article .num_keypad > button[data-action="CLR"],
main#staticDoc article .num_keypad > button[data-action="BS"] { color: var(--npc-ink-2); font-size: 23px; }
main#staticDoc article .num_keypad > button[data-action=""] { border-color: transparent; background-color: transparent; box-shadow: none; pointer-events: none; }


/* ══════════════════════════════════════════════════════════════
   8. 입력 표시 — 흰 캡슐 / 출결 4자리 도트
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .input_display {
    display: flex;
    align-items: center;
    gap: 2px;
    margin: 4px var(--npc-pad-x) 16px;
    padding: 0 18px;
    min-height: 64px;
    border: 1px solid var(--npc-line);
    border-radius: 18px;
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
    color: var(--npc-ink);
    font-size: 30px;
    font-weight: 700;
    letter-spacing: 0.01em;
}
/* 🪤 main.css:224 의 `border-bottom: solid 2px` 가 남아 «010 -» 아래에만 밑줄이 그어졌다.
   input 쪽은 이미 border:none 을 줬는데 span 은 색만 덮어써서 놓쳤던 자리 —
   .input_display 자체가 테두리 있는 카드가 된 지금은 밑줄이 군더더기다(사용자 지적 2026-08-21). */
main#staticDoc article .input_display > span { border: none; color: var(--npc-ink); }
main#staticDoc article .input_display > input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: transparent;
    color: var(--npc-ink);
    font-size: 30px;
    font-weight: 700;
    letter-spacing: 0.01em;
}
main#staticDoc article .input_display > input::placeholder { color: var(--npc-ink-3); font-weight: 600; }
main#staticDoc article .input_display > button { color: var(--npc-ink-3); }

/* 출결번호 4자리 — 시안의 «흰 원» 인디케이터 */
main#staticDoc article .attend_dots {
    display: flex;
    justify-content: center;
    gap: 16px;
    padding: 10px 0 22px;
}
main#staticDoc article .attend_dots > i {
    width: 62px;
    height: 62px;
    border: 1px solid var(--npc-line);
    border-radius: 50%;
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
    font-style: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--npc-ink);
    font-size: 30px;
    font-weight: 700;
    transition: border-color 0.12s ease, background-color 0.12s ease;
}
main#staticDoc article .attend_dots > i.filled { border-color: var(--npc-accent); background-color: var(--npc-accent-soft); }
/* 실입력은 헤드리스 input 이 받는다(키패드 바인딩 유지) */
main#staticDoc article .attend_headless { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }


/* ══════════════════════════════════════════════════════════════
   9. 리스트 카드 (학생/청구서/항목/내역)
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article ul.blocked_list { margin: 0; padding: 0 var(--npc-pad-x); list-style: none; display: flex; flex-direction: column; gap: 12px; }
main#staticDoc article ul.blocked_list > li { margin: 0; }
main#staticDoc article ul.blocked_list > li > :is(div, button) {
    width: 100%;
    margin: 0;
    padding: 18px;
    border: 1px solid var(--npc-line);
    border-radius: var(--npc-radius-card);
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
    color: var(--npc-ink);
    text-align: left;
    cursor: pointer;
}
main#staticDoc article ul.blocked_list > li > button:active { background-color: var(--npc-accent-soft); border-color: var(--npc-accent); }
main#staticDoc article ul.blocked_list > li .item_subject {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    color: var(--npc-ink);
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.03em;
}
main#staticDoc article ul.blocked_list > li .item_details {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin-top: 8px;
    color: var(--npc-ink-2);
    font-size: 15px;
    font-weight: 500;
}
main#staticDoc article ul.blocked_list > li .item_details > span:not(:last-child)::after { content: ""; margin: 0; }
main#staticDoc article ul.blocked_list:empty::before {
    content: attr(data-placeholder);
    display: block;
    padding: 44px 12px;
    color: var(--npc-ink-3);
    font-size: 16px;
    text-align: center;
    white-space: pre-line;
}

/* 청구서 카드 — 시안의 «2026. 8 / 납부 · 미납» 2행 구성 */
.npc_bill_head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.npc_bill_month { color: var(--npc-ink); font-size: 26px; font-weight: 700; letter-spacing: -0.03em; }
.npc_bill_title { color: var(--npc-ink-3); font-size: 15px; font-weight: 500; }
.npc_bill_amounts { display: flex; align-items: baseline; gap: 10px; margin-top: 8px; font-size: 19px; font-weight: 700; }
.npc_bill_amounts .paid { color: var(--npc-ink); }
.npc_bill_amounts .sep { color: var(--npc-line-strong); font-weight: 400; }
.npc_bill_amounts .remain { color: var(--npc-danger); }
.npc_bill_amounts .label { font-size: 15px; font-weight: 600; }


/* ══════════════════════════════════════════════════════════════
   10. 결제 항목 선택 (금액 입력 + 체크)
   ══════════════════════════════════════════════════════════════ */
.npc_item_row { display: flex; align-items: center; gap: 12px; }
.npc_item_main { flex: 1 1 auto; min-width: 0; }
.npc_item_name { color: var(--npc-ink-3); font-size: 15px; font-weight: 600; }
.npc_amount_input {
    width: 116px;
    height: 42px;
    padding: 0 12px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 999px;
    background-color: var(--npc-surface-2);
    color: var(--npc-ink);
    font-size: 17px;
    font-weight: 700;
    text-align: center;
}
.npc_amount_input:focus { border-color: var(--npc-accent); outline: none; }

/* 체크 마크 — 시안의 원+체크 */
main#staticDoc article .custom_checkbox { margin: 0; }
.npc_check { width: 34px; height: 34px; flex: 0 0 auto; }
.npc_check input { display: none; }
.npc_check label {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: 2px solid var(--npc-line-strong);
    border-radius: 50%;
    color: transparent;
    cursor: pointer;
    transition: border-color 0.12s ease, background-color 0.12s ease;
}
.npc_check label::after { content: "✓"; font-size: 20px; font-weight: 800; line-height: 1; }
.npc_check input:checked + label { border-color: var(--npc-accent); background-color: transparent; color: var(--npc-accent); }

/* 할부 선택 */
.npc_select_row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    /* 좌우 margin 없음 — 감싸는 .npc_bottom_dock 이 이미 var(--npc-pad-x) 를 padding 으로 준다.
       종전엔 여기에 --npc-pad-x 를 한 번 더 얹어, 바로 아래 #pay_for_selected 보다
       좌우로 16px 씩 안쪽에 들어가 있었다(실기기 실측: 카드 32\~502, 버튼 16\~518). */
    margin: 0 0 12px;
    padding: 0 18px;
    min-height: 58px;
    border: 1px solid var(--npc-line);
    border-radius: 18px;
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
}
.npc_select_row > select {
    flex: 1 1 auto;
    border: none;
    background: transparent;
    color: var(--npc-ink);
    font-size: 18px;
    font-weight: 600;
    text-align: center;
    text-align-last: center;
}
.npc_select_row > .hint { color: var(--npc-ink-3); font-size: 14px; }


/* ══════════════════════════════════════════════════════════════
   11. 주요 CTA 버튼
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .npc_cta,
main#staticDoc article button.attend_commit,
main#staticDoc article button#pay_for_selected {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 66px;
    margin: 0;
    padding: 0 18px;
    border: none;
    border-radius: var(--npc-radius-btn);
    background-color: var(--npc-accent);
    box-shadow: none;
    color: var(--npc-ink-on-accent);
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -0.03em;
    cursor: pointer;
}
main#staticDoc article .npc_cta:active,
main#staticDoc article button.attend_commit:active,
main#staticDoc article button#pay_for_selected:active { background-color: var(--npc-accent-press); }
main#staticDoc article .npc_cta:disabled,
main#staticDoc article button.attend_commit:disabled,
main#staticDoc article button#pay_for_selected:disabled {
    background-color: var(--npc-bg-sunken);
    color: var(--npc-ink-3);
    cursor: default;
}
main#staticDoc article button.attend_commit > span { color: inherit !important; }
main#staticDoc article button.attend_commit .attend_name { color: inherit !important; font-weight: 800; }

/* CTA 를 감싸는 바닥 영역 */
main#staticDoc article .npc_bottom_dock {
    position: sticky;
    bottom: 0;
    z-index: 4;
    margin-top: auto;
    padding: 12px var(--npc-pad-x) calc(var(--npc-foot-h) + 10px);
    background-image: linear-gradient(0deg, var(--npc-bg) 78%, transparent);
}
main#staticDoc article .npc_bottom_dock > * + * { margin-top: 10px; }
/* 구 fixed_bottom 은 viewport 고정이라 브랜드 라인과 겹친다 — dock 으로 대체 */
main#staticDoc > section.root_tab_content article .fixed_bottom {
    position: static;
    margin-top: auto;
    padding: 12px var(--npc-pad-x) calc(var(--npc-foot-h) + 10px);
    background-image: none;
    background-color: transparent;
}


/* ══════════════════════════════════════════════════════════════
   12. 상단 네비바 (결제내역 계열) — [←] 제목 [검색][새로고침]
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .npc_navbar {
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: var(--npc-head-h);
    padding: 8px 10px 8px 6px;
    border-bottom: 1px solid var(--npc-line);
    background-color: var(--npc-bg);
}
main#staticDoc article .npc_navbar > .npc_navbar_title {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--npc-ink);
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.03em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
main#staticDoc article .npc_navbar > .npc_navbar_actions { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }

/* 월 네비게이션 */
main#staticDoc article .npc_month_nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 10px var(--npc-pad-x) 6px;
}
.npc_month_label {
    min-width: 132px;
    height: 44px;
    padding: 0 14px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 999px;
    background-color: var(--npc-surface);
    color: var(--npc-ink);
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -0.02em;
    cursor: pointer;
}
.npc_month_label:active { background-color: var(--npc-accent-soft); }
.npc_month_label > .caret { margin-left: 6px; color: var(--npc-ink-3); font-size: 12px; }
.npc_month_step {
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background-color: transparent;
    color: var(--npc-ink-2);
    font-size: 20px;
    font-weight: 700;
    cursor: pointer;
}
.npc_month_step:active { background-color: rgb(var(--npc-accent-rgb) / 14%); color: var(--npc-accent); }
.npc_month_step:disabled { color: var(--npc-line-strong); cursor: default; background: transparent; }

/* 검색 바 — 검색 버튼 누르면 아래에 펼쳐짐 */
main#staticDoc article .npc_search_bar {
    display: none;
    align-items: center;
    gap: 8px;
    margin: 2px var(--npc-pad-x) 8px;
    padding: 0 8px 0 16px;
    min-height: 50px;
    border: 1px solid var(--npc-accent);
    border-radius: 999px;
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
}
main#staticDoc article .npc_search_bar.on { display: flex; }
main#staticDoc article .npc_search_bar > input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: transparent;
    color: var(--npc-ink);
    font-size: 17px;
    font-weight: 500;
}
main#staticDoc article .npc_search_bar > input:focus { outline: none; }
main#staticDoc article .npc_search_bar > input::placeholder { color: var(--npc-ink-3); }
main#staticDoc article .npc_search_bar > .npc_search_clear {
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    padding: 0;
    border: none;
    border-radius: 50%;
    background-color: var(--npc-bg-sunken);
    color: var(--npc-ink-2);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
}
main#staticDoc article .npc_search_bar > .npc_search_clear:active { background-color: var(--npc-accent-soft); color: var(--npc-accent); }


/* ══════════════════════════════════════════════════════════════
   13. 월 선택 팝업 (년 네비 포함)
   ══════════════════════════════════════════════════════════════ */
.npc_pop_backdrop {
    position: fixed;
    z-index: 140;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background-color: rgb(20 12 4 / 45%);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}
.npc_pop_backdrop.on { display: flex; }
.npc_pop {
    width: min(360px, 100%);
    padding: 16px;
    border: 1px solid var(--npc-line);
    border-radius: 24px;
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-pop);
    color: var(--npc-ink);
}
.npc_pop_head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 12px; }
.npc_pop_year { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; }
.npc_pop_months { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.npc_pop_months > button {
    min-height: 52px;
    margin: 0;
    padding: 0;
    border: 1px solid var(--npc-line-strong);
    border-radius: 14px;
    background-color: var(--npc-surface-2);
    color: var(--npc-ink);
    font-size: 17px;
    font-weight: 700;
    cursor: pointer;
}
.npc_pop_months > button:active { background-color: var(--npc-accent-soft); }
.npc_pop_months > button.current { border-color: var(--npc-accent); background-color: var(--npc-accent); color: var(--npc-ink-on-accent); }
.npc_pop_months > button:disabled { color: var(--npc-ink-3); background-color: transparent; border-color: var(--npc-line); cursor: default; }
.npc_pop_foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }
.npc_pop_foot > button {
    min-height: 44px;
    padding: 0 18px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 12px;
    background-color: transparent;
    color: var(--npc-ink-2);
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
}


/* ══════════════════════════════════════════════════════════════
   14. 결제내역 목록 (터미널 · 청구서 공용)
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article ul.npc_hist_list { margin: 0; padding: 4px var(--npc-pad-x) 0; list-style: none; display: flex; flex-direction: column; gap: 10px; }
main#staticDoc article ul.npc_hist_list > li { margin: 0; }

.npc_hist_row {
    padding: 14px 16px;
    border: 1px solid var(--npc-line);
    border-radius: 18px;
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
    cursor: pointer;
}
.npc_hist_row.canceled { background-color: var(--npc-surface-2); border-color: var(--npc-line); }
.npc_hist_top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.npc_hist_amount { color: var(--npc-ink); font-size: 22px; font-weight: 800; letter-spacing: -0.03em; }
.npc_hist_row.canceled .npc_hist_amount { color: var(--npc-ink-3); text-decoration: line-through; text-decoration-thickness: 2px; }
.npc_hist_badge {
    flex: 0 0 auto;
    padding: 5px 10px;
    border-radius: 999px;
    background-color: var(--npc-accent-soft);
    color: var(--npc-accent-press);
    font-size: 13px;
    font-weight: 700;
}
body[data-dark-mode="1"] .npc_hist_badge { color: var(--npc-accent); }
/* ⚠ `body` 를 붙여 특이성을 (0,2,1) 로 올린다 — 바로 위 다크모드 규칙이 (0,2,1) 이라
   `.npc_hist_badge.canceled`(0,2,0) 로는 다크에서 취소 배지가 accent 색으로 나온다(2026-09-02) */
body .npc_hist_badge.canceled { background-color: var(--npc-danger-soft); color: var(--npc-danger); }
.npc_hist_meta { display: flex; flex-wrap: wrap; gap: 3px 10px; margin-top: 6px; color: var(--npc-ink-2); font-size: 14px; font-weight: 500; }
.npc_hist_memo { margin-top: 8px; padding: 8px 10px; border-radius: 10px; background-color: var(--npc-bg-sunken); color: var(--npc-ink-2); font-size: 14px; white-space: pre-wrap; }
.npc_hist_memo .who { color: var(--npc-ink); font-weight: 700; }

/* 확장 영역 — 승인/취소 2줄 (터치 토글) */
.npc_hist_lines { display: none; margin-top: 10px; border-top: 1px dashed var(--npc-line-strong); padding-top: 10px; }
.npc_hist_row.expanded .npc_hist_lines { display: block; }
.npc_hist_line { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: 14px; }
.npc_hist_line > .tag { flex: 0 0 auto; min-width: 46px; padding: 3px 8px; border-radius: 8px; background-color: var(--npc-accent-soft); color: var(--npc-accent-press); font-size: 12px; font-weight: 800; text-align: center; }
body[data-dark-mode="1"] .npc_hist_line > .tag { color: var(--npc-accent); }
.npc_hist_line.cancel > .tag { background-color: var(--npc-danger-soft); color: var(--npc-danger); }
.npc_hist_line > .when { color: var(--npc-ink); font-weight: 600; }
.npc_hist_line > .no { color: var(--npc-ink-3); margin-left: auto; }

.npc_hist_actions { display: flex; gap: 8px; margin-top: 12px; }
.npc_hist_actions > button {
    flex: 1 1 0;
    min-height: 46px;
    margin: 0;
    padding: 0 12px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 12px;
    background-color: transparent;
    color: var(--npc-ink-2);
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
}
.npc_hist_actions > button.danger { border-color: rgb(from var(--npc-danger) r g b / 45%); color: var(--npc-danger); }
.npc_hist_actions > button.danger.armed { background-color: var(--npc-danger); border-color: var(--npc-danger); color: #FFF; }
.npc_hist_actions > button:active { background-color: var(--npc-bg-sunken); }

.npc_hist_summary {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    margin: 12px var(--npc-pad-x) 0;
    padding: 12px 16px;
    border-radius: 14px;
    background-color: var(--npc-bg-sunken);
    color: var(--npc-ink-2);
    font-size: 14px;
    font-weight: 600;
}
.npc_hist_summary b { color: var(--npc-ink); }

.npc_hist_empty { padding: 56px 20px; color: var(--npc-ink-3); font-size: 16px; text-align: center; white-space: pre-line; }


/* ══════════════════════════════════════════════════════════════
   15. 메모 화면 (터미널 결제완료 후 — MangoPOS saveNotePaid 이식)
   ══════════════════════════════════════════════════════════════ */
.npc_memo_form { display: flex; flex-direction: column; gap: 12px; padding: 4px var(--npc-pad-x) 0; }
.npc_field { display: flex; flex-direction: column; gap: 6px; }
.npc_field > label { color: var(--npc-ink-2); font-size: 14px; font-weight: 700; padding-left: 4px; }
.npc_field > input, .npc_field > textarea {
    width: 100%;
    box-sizing: border-box;
    padding: 15px 16px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 14px;
    background-color: var(--npc-surface);
    color: var(--npc-ink);
    font-size: 18px;
    font-weight: 500;
}
.npc_field > textarea { min-height: 96px; resize: none; line-height: 1.5; }
.npc_field > input:focus, .npc_field > textarea:focus { border-color: var(--npc-accent); outline: none; }
.npc_field > input::placeholder, .npc_field > textarea::placeholder { color: var(--npc-ink-3); }

.npc_result_head { padding: 20px var(--npc-pad-x) 6px; text-align: center; }
.npc_result_head > .amt { color: var(--npc-ink); font-size: 34px; font-weight: 800; letter-spacing: -0.04em; }
.npc_result_head > .cap { margin-top: 4px; color: var(--npc-ok); font-size: 17px; font-weight: 700; }
.npc_result_head > .cap.canceled { color: var(--npc-danger); }


/* ══════════════════════════════════════════════════════════════
   16. 결과 토스트 (직전 화면 복귀 후 표시)
   ══════════════════════════════════════════════════════════════ */
#npcToast {
    position: fixed;
    z-index: 150;
    left: 50%;
    bottom: calc(var(--bottom-pad) + 56px);
    display: none;
    align-items: center;
    gap: 10px;
    max-width: min(92vw, 460px);
    padding: 14px 18px;
    border-radius: 16px;
    background-color: #201C17;
    box-shadow: var(--npc-shadow-pop);
    color: #FFF;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: -0.02em;
    transform: translate(-50%, 8px);
    opacity: 0;
    transition: opacity 0.2s ease, transform 0.2s ease;
    white-space: pre-line;
}
#npcToast.on { display: flex; opacity: 1; transform: translate(-50%, 0); }
#npcToast.ok::before { content: "✓"; color: #6BE38F; font-size: 18px; font-weight: 800; }
#npcToast.warn::before { content: "!"; color: #FFC24D; font-size: 18px; font-weight: 800; }
#npcToast.err::before { content: "×"; color: #FF8A7A; font-size: 20px; font-weight: 800; }
#npcToast > .act {
    margin-left: 6px;
    padding: 8px 12px;
    border: 1px solid rgb(255 255 255 / 30%);
    border-radius: 10px;
    background: transparent;
    color: #FFD9A8;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
}
body[data-dark-mode="1"] #npcToast { background-color: #332C24; }


/* ══════════════════════════════════════════════════════════════
   17. 메뉴 서랍 (좌측) — 시안 구성
   ══════════════════════════════════════════════════════════════ */
nav#mainMenu > section#menuArea { background-color: rgb(from var(--npc-bg) r g b / 92%); }
nav#mainMenu > section#menuArea > div.container > article { color: var(--npc-ink); }
nav#mainMenu .npc_menu_head { display: flex; justify-content: flex-end; padding: 6px 12px 10px; border-bottom: 1px solid var(--npc-line); }
nav#mainMenu .npc_menu_close {
    width: 44px; height: 44px; padding: 0;
    border: 1px solid var(--npc-line-strong);
    border-radius: 50%;
    background-color: transparent;
    color: var(--npc-ink);
    font-size: 20px;
    cursor: pointer;
}
nav#mainMenu > section#menuArea ul > li > button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 17px 18px;
    border: none;
    border-radius: 0;
    background-color: transparent;
    color: var(--npc-ink);
    font-size: 18px;
    font-weight: 600;
    text-align: left;
    text-shadow: none;
    cursor: pointer;
}
nav#mainMenu > section#menuArea ul > li > button:active { background-color: rgb(var(--npc-accent-rgb) / 12%); }
nav#mainMenu .npc_menu_row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 18px; color: var(--npc-ink); font-size: 18px; font-weight: 600; }
nav#mainMenu .npc_menu_row .sub { color: var(--npc-ink-3); font-size: 13px; font-weight: 500; }
nav#mainMenu hr { margin: 8px 0; border: none; border-top: 1px solid var(--npc-line); }
nav#mainMenu .npc_menu_foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 16px 18px; border-top: 1px solid var(--npc-line); }
nav#mainMenu .npc_menu_foot .who { color: var(--npc-ink); font-size: 18px; font-weight: 700; }
nav#mainMenu .npc_menu_foot .sign_out {
    padding: 11px 18px;
    border: none;
    border-radius: 12px;
    background-color: var(--npc-bg-sunken);
    color: var(--npc-ink-2);
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
}

/* 스위치 토글 */
.npc_switch { position: relative; width: 56px; height: 32px; flex: 0 0 auto; }
.npc_switch > input { display: none; }
.npc_switch > label {
    display: block;
    width: 56px;
    height: 32px;
    border-radius: 999px;
    background-color: var(--npc-line-strong);
    cursor: pointer;
    transition: background-color 0.18s ease;
}
.npc_switch > label::after {
    content: "";
    position: absolute;
    top: 4px;
    left: 4px;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background-color: #FFF;
    box-shadow: 0 1px 3px rgb(0 0 0 / 25%);
    transition: transform 0.18s ease;
}
.npc_switch > input:checked + label { background-color: var(--npc-accent); }
.npc_switch > input:checked + label::after { transform: translateX(24px); }


/* ══════════════════════════════════════════════════════════════
   18. 로그인 화면
   ══════════════════════════════════════════════════════════════ */
main#instantDoc > section#login { background-color: var(--npc-bg); }
main#instantDoc > section#login .npc_login_wrap { display: flex; flex-direction: column; justify-content: center; min-height: 100%; padding: 0 24px; }
main#instantDoc > section#login .npc_login_tag {
    margin-bottom: 34px;
    color: var(--npc-ink);
    font-size: 26px;
    font-weight: 800;
    line-height: 1.4;
    letter-spacing: -0.04em;
    text-align: center;
    word-break: keep-all;
}
main#instantDoc > section#login .npc_login_field {
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 12px;
    padding: 19px 20px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 16px;
    background-color: var(--npc-surface);
    color: var(--npc-ink);
    font-size: 17px;
}
main#instantDoc > section#login .npc_login_field::placeholder { color: var(--npc-ink-3); }
main#instantDoc > section#login .npc_login_field:focus { border-color: var(--npc-accent); outline: none; }
main#instantDoc > section#login .npc_login_submit {
    width: 100%;
    min-height: 62px;
    margin-top: 14px;
    border: none;
    border-radius: 16px;
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
    font-size: 19px;
    font-weight: 800;
    cursor: pointer;
}
main#instantDoc > section#login .npc_login_submit:active { background-color: var(--npc-accent-press); }
main#instantDoc > section#login .npc_login_logo { position: absolute; left: 0; right: 0; bottom: calc(var(--bottom-pad) + 40px); display: flex; justify-content: center; }
/* 로고는 <img> → 인라인 <svg>(스프라이트) 로 바뀌었다 — 두 선택자를 함께 둔다(2026-08-24) */
main#instantDoc > section#login .npc_login_logo :is(img, .npc_logo_svg) { width: 132px; }


/* ══════════════════════════════════════════════════════════════
   19. N커넥트 터미널 — 기존 레이아웃 유지, 색만 새 팔레트로
   ══════════════════════════════════════════════════════════════ */
main#staticDoc > section#terminal > .container > article.terminal { background-color: var(--npc-bg); }
.terminal_badge { background-color: var(--npc-pill-bg); color: var(--npc-pill-ink); border-radius: 999px; padding: 7px 14px; font-size: 14px; font-weight: 700; }
.terminal_side button {
    border: 1px solid var(--npc-line-strong);
    border-radius: var(--npc-radius-btn);
    background-color: var(--npc-surface);
    color: var(--npc-ink-2);
    font-weight: 700;
}
.terminal_side button.active { border-color: transparent; background-color: var(--npc-accent); color: var(--npc-ink-on-accent); }
.terminal_display { border: 1px solid var(--npc-line); border-radius: var(--npc-radius-card); background-color: var(--npc-surface); box-shadow: var(--npc-shadow-card); }
.terminal_amount_label { color: var(--npc-ink-3); }
.terminal_amount { color: var(--npc-ink); }
.terminal_amount .won { color: var(--npc-ink-2); }
.terminal_extra { color: var(--npc-ink-2); }
#terminalInstallment { border: 1px solid var(--npc-line-strong); border-radius: 10px; background-color: var(--npc-surface-2); color: var(--npc-ink); }
.terminal_msg { color: var(--npc-danger); font-weight: 600; }
.terminal_msg.info { color: var(--npc-ok); }
.terminal_actions button { border-radius: var(--npc-radius-btn); border: 1px solid var(--npc-line-strong); background-color: var(--npc-surface); color: var(--npc-ink-2); font-weight: 700; }
.terminal_actions button.pay { border-color: transparent; background-color: var(--npc-accent); color: var(--npc-ink-on-accent); }
.terminal_actions button.pay:active { background-color: var(--npc-accent-press); }
/* 터미널 키패드도 외곽선 계열로 통일 */
.terminal_keypad button {
    border: 1px solid var(--npc-line-strong);
    border-radius: var(--npc-radius-key);
    background-color: var(--npc-surface);
    color: var(--npc-ink);
    font-weight: 600;
}
.terminal_keypad button:active { background-color: var(--npc-accent-soft); border-color: var(--npc-accent); }

/* 구 터미널 내역 오버레이는 폐기(상단 네비 페이지로 승격) */
.terminal_history { display: none !important; }


/* ══════════════════════════════════════════════════════════════
   20. 출결 기록 사이드 패널 / 기타 잔여 요소 리테마
   ══════════════════════════════════════════════════════════════ */
.attend_log_panel, .paybill_guide_panel { border: 1px solid var(--npc-line); border-radius: var(--npc-radius-card); background-color: var(--npc-surface); }
.side_panel_title { color: var(--npc-ink-2); }
.attend_log_list > li { border-bottom: 1px solid var(--npc-line); color: var(--npc-ink); }
.attend_log_list > li > .log_time { color: var(--npc-ink-3); }
.attend_log_list > li > .log_type { background-color: var(--npc-accent-soft); color: var(--npc-accent-press); border-radius: 999px; padding: 2px 8px; font-size: 12px; font-weight: 700; }
body[data-dark-mode="1"] .attend_log_list > li > .log_type { color: var(--npc-accent); }
.attend_log_empty, .paybill_guide_placeholder { color: var(--npc-ink-3); }
.mango_user_handle label, .mango_user_handle .class_org_name { color: var(--npc-ink); }

/* 결과(성공/실패) 오버레이 아티클 */
main#staticDoc article[data-article-id="success"], main#staticDoc article[data-article-id="failure"],
main#staticDoc article[data-article-id="successAttend"] { background-color: var(--npc-bg); color: var(--npc-ink); }

/* 대기 화면 */
main#instantDoc > section#wait .container { background-color: rgb(20 12 4 / 55%) !important; }


/* ══════════════════════════════════════════════════════════════
   21. 세부 보강 — 신규 마크업 조각
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .npc_body_col { display: flex; flex-direction: column; justify-content: center; flex: 1 1 auto; min-height: 0; }

/* 부분납부 금액칸 (라벨 + 입력) */
.npc_pay_amount { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.npc_pay_amount > .cap { color: var(--npc-ink-3); font-size: 14px; font-weight: 700; }

/* 출석목록 오버레이 */
.npc_attend_page_list { margin: 0; padding: 0; list-style: none; max-height: 52vh; overflow-y: auto; }
.npc_attend_page_list > li { display: flex; align-items: center; gap: 10px; padding: 12px 6px; border-bottom: 1px solid var(--npc-line); color: var(--npc-ink); font-size: 16px; }
.npc_attend_page_list > li.empty { justify-content: center; color: var(--npc-ink-3); border-bottom: none; padding: 40px 0; }
.npc_attend_page_list > li > .log_time { margin-left: auto; color: var(--npc-ink-3); font-size: 14px; }
.npc_attend_page_list > li > .log_type { padding: 2px 8px; border-radius: 999px; background-color: var(--npc-accent-soft); color: var(--npc-accent-press); font-size: 12px; font-weight: 700; }
body[data-dark-mode="1"] .npc_attend_page_list > li > .log_type { color: var(--npc-accent); }

/* 출석목록 노출여부(메뉴 토글) — 끄면 좌측 패널 숨김 */
body[data-attend-log="0"] .attend_log_panel { display: none !important; }

/* 내역/메모 페이지는 세로 흐름 */
main#staticDoc article.npc_hist_page, main#staticDoc article.npc_memo_page { display: flex; flex-direction: column; min-height: 100%; }
main#staticDoc article.npc_hist_page > .linear_block { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* 로그인 입력칸 — EstreUI mango_enter_rex 를 시안 톤으로 */
main#instantDoc > section#login .mango_enter_rex {
    margin-bottom: 12px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 16px;
    background-color: var(--npc-surface);
}
main#instantDoc > section#login .mango_enter_rex > input { padding: 19px 20px; background: transparent; color: var(--npc-ink); font-size: 17px; }
main#instantDoc > section#login .mango_enter_rex > input::placeholder { color: var(--npc-ink-3); }
main#instantDoc > section#login .mango_enter_rex:focus-within { border-color: var(--npc-accent); }
main#instantDoc > section#login button#login_submit.npc_login_submit {
    width: 100%; height: 62px; margin-top: 14px;
    border: none; border-radius: 16px;
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
    font-size: 19px; font-weight: 800;
}
main#instantDoc > section#login button#login_submit.npc_login_submit:disabled { background-color: var(--npc-bg-sunken); color: var(--npc-ink-3); }

/* 홈 진입 전(로그인 유도) 화면 */
main#staticDoc article.enterence button { border: 1px solid var(--npc-line-strong) !important; border-radius: 16px !important; background-color: var(--npc-surface) !important; color: var(--npc-ink) !important; }


/* ══════════════════════════════════════════════════════════════
   22. 가로 모드 — 세로에서 «위»에 있던 것을 왼쪽으로, 키패드는 오른쪽
   ══════════════════════════════════════════════════════════════ */
@media (orientation: landscape) {
    /* 홈 — 서비스 카드 2장을 나란히 */
    main#staticDoc article.home .service_cards { flex-direction: row; }
    main#staticDoc article.home button.service_card { flex: 1 1 0; min-height: min(38vh, 210px); }
    main#staticDoc article .npc_hero { padding: 14px var(--npc-pad-x) 16px; font-size: 26px; }

    /* 출결·수납 입력 — 좌: 안내/입력/CTA · 우: 키패드 (사용자 지시) */
    main#staticDoc article .attend_main_col,
    main#staticDoc article .paybill_main_col {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-rows: auto 1fr auto;
        column-gap: 18px;
        align-content: center;
        flex: 1 1 auto;
        min-height: 0;
    }
    main#staticDoc article .attend_main_col > .npc_guide,
    main#staticDoc article .paybill_main_col > .npc_guide { grid-column: 1; grid-row: 1; align-self: end; }
    main#staticDoc article .attend_main_col > .attend_dots,
    main#staticDoc article .paybill_main_col > .input_display { grid-column: 1; grid-row: 2; align-self: start; }
    main#staticDoc article .attend_main_col > .num_keypad,
    main#staticDoc article .paybill_main_col > .num_keypad { grid-column: 2; grid-row: 1 / span 3; align-content: center; }
    main#staticDoc article .attend_main_col > .npc_bottom_dock,
    main#staticDoc article .paybill_main_col > .npc_bottom_dock { grid-column: 1; grid-row: 3; padding-bottom: 8px; background-image: none; }

    /* 가로에서만 좌측 보조 패널 노출 */
    .attend_log_panel, .paybill_guide_panel { display: flex; flex-direction: column; flex: 0 0 clamp(200px, 26vw, 300px); margin: 0 0 0 var(--npc-pad-x); padding: 14px; overflow: hidden; }
    .attend_body, .paybill_body { display: flex; flex-direction: row; align-items: stretch; gap: 14px; flex: 1 1 auto; min-height: 0; }

    /* 목록·내역은 2열로 — 가로에서 한 화면에 더 담기게 */
    main#staticDoc article ul.blocked_list,
    main#staticDoc article ul.npc_hist_list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-content: start; }
    /* 터미널 결제내역의 빈 안내(.npc_hist_empty)는 ul 안에 그려지므로 2열 격자에서 한 칸에 갇히지 않게 전폭으로 — 일반 결제내역의 안내(ul 밖)와 같은 폭 (2026-09-03) */
    main#staticDoc article ul.npc_hist_list > .npc_hist_empty { grid-column: 1 / -1; }

    /* 하단 브랜드 라인은 가로에선 자리를 많이 먹어 축소 */
    #npcBrandFoot { height: 24px; bottom: calc(var(--bottom-pad) + 4px); font-size: 16px; }
    html { --npc-foot-h: 30px; }

    /* 메모 폼도 2열 */
    .npc_memo_form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
    .npc_field:last-child { grid-column: 1 / -1; }

    /* 로그인 — 가운데 폭 제한 */
    main#instantDoc > section#login .npc_login_wrap { max-width: 560px; margin: 0 auto; }
    main#instantDoc > section#login .npc_login_tag { margin-bottom: 20px; font-size: 22px; }
    main#instantDoc > section#login .npc_login_logo { bottom: calc(var(--bottom-pad) + 14px); }
}

/* 세로에선 보조 패널을 감춘다(공간 부족) */
@media (orientation: portrait) {
    .attend_log_panel, .paybill_guide_panel { display: none; }
    .attend_body, .paybill_body { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
    main#staticDoc article .attend_main_col, main#staticDoc article .paybill_main_col { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
    main#staticDoc article .attend_main_col > .num_keypad, main#staticDoc article .paybill_main_col > .num_keypad { flex: 1 1 auto; }
}

/* 계정·로그아웃 숨김 (사용자 지시 2026-08-21) — 기기 자동 로그인이라 «로그인» 개념이 사실상 없다.
   요소는 남긴다: MangoUserHandle 이 .user_name/.class_org_name 을 바인딩하고 학원명은 하단 브랜드 라인이 쓴다. */
nav#mainMenu .npc_menu_foot { display: none; }

/* ══════════════════════════════════════════════════════════════
   23. 실기기 캡처 기반 보정 (2026-08-21)
   ══════════════════════════════════════════════════════════════ */
/* main.css 가 홈 카드를 flex:1 1 0 으로 «화면 높이만큼» 늘려서 시안보다 과하게 컸다.
   시안 비율(카드 2장 + 하단 학원명이 한 화면)에 맞춰 고정 높이 계열로 되돌린다. */
main#staticDoc article.home .service_cards { flex: 0 0 auto; margin: 0; align-items: stretch; padding: 8px var(--npc-pad-x) 0; }
main#staticDoc article.home button.service_card { flex: 0 0 auto; width: auto; max-width: none; aspect-ratio: auto; min-height: clamp(124px, 19vh, 172px); }
/* rootbar 를 감췄으므로 그 자리 예약 패딩을 브랜드 라인 몫으로 교체 */
main#staticDoc > section.root_tab_content > .container > article.home,
main#staticDoc article.home { padding-bottom: calc(var(--npc-foot-h) + var(--bottom-pad) + 8px); }
main#staticDoc article .npc_body_col { padding-bottom: 4px; }

@media (orientation: landscape) {
    main#staticDoc article.home button.service_card { min-height: clamp(110px, 30vh, 200px); }
}

/* 세로 배치 — 시안은 «안내문 상단쪽 / 카드 중앙 / 학원명 하단» 이다.
   가운데 정렬만 쓰면 전체가 아래로 밀려 상단 여백이 과하게 남는다(실기기 캡처 확인). */
main#staticDoc article.home .npc_body_col { justify-content: flex-start; }
main#staticDoc article.home .npc_hero { margin-top: clamp(16px, 7vh, 64px); padding-top: 0; padding-bottom: clamp(14px, 3vh, 26px); }
@media (orientation: landscape) {
    main#staticDoc article.home .npc_hero { margin-top: clamp(6px, 2vh, 18px); }
}

/* ══════════════════════════════════════════════════════════════
   24. 레거시 팔레트 브리지 (2026-08-21)
   ══════════════════════════════════════════════════════════════
   main.css·estreUi.css 의 기존 규칙이 토스 블루/망고 옐로 변수를 직접 쓴다.
   셀렉터를 하나씩 쫓기보다 «변수 자체»를 새 토큰으로 갈아끼워 한 번에 정합시킨다.
   (실기기 캡처에서 터미널 결제요청 버튼·금액칸이 구 옐로로 남아 있던 원인) */
body {
    --color-point: var(--npc-accent);
    --color-point-light: var(--npc-accent);
    --color-point-dark: var(--npc-accent-press);
    --color-point-text: var(--npc-ink-on-accent);
    --color-point-deactive: var(--npc-bg-sunken);
    --color-accent: var(--npc-accent);
    --cp: var(--npc-accent-rgb);
    --cpl: var(--npc-accent-rgb);
    --cpd: var(--npc-accent-rgb);
    /* 망고 옐로 계열 → 브랜드 오렌지 (터미널 강조면·활성 탭이 이 변수들을 쓴다) */
    --cmyd: var(--npc-accent-rgb);
    --cmyv: var(--npc-accent-rgb);
    --cmyl: var(--npc-accent-rgb);
    --cmyvl: var(--npc-accent-rgb);
    --color-mango-yellow-dark: var(--npc-accent);
    --color-mango-yellow-vivid: var(--npc-accent);
    --color-toss-active-blue: var(--npc-accent);
    --color-toss-deactive-blue: var(--npc-bg-sunken);
}

/* ID 특이성으로 남는 것들은 직접 지정 */
main#staticDoc #terminalReset { background-color: var(--npc-bg-sunken); border: 1px solid var(--npc-line-strong); color: var(--npc-ink-2); }
main#staticDoc #terminalPay { background-color: var(--npc-accent); border-color: transparent; color: var(--npc-ink-on-accent); }
main#staticDoc #terminalPay:active { background-color: var(--npc-accent-press); }
main#staticDoc .terminal_display { border-color: var(--npc-line); background-color: var(--npc-surface); }
main#staticDoc .terminal_amount { background-color: var(--npc-accent-soft); color: var(--npc-ink); }
main#staticDoc .terminal_amount .won { color: var(--npc-ink-2); }
main#staticDoc .terminal_amount_label { color: var(--npc-ink-3); }
main#staticDoc .terminal_side button { border-color: var(--npc-line-strong); background-color: var(--npc-surface); color: var(--npc-ink-2); }
main#staticDoc .terminal_side button.active { border-color: transparent; background-color: var(--npc-accent); color: var(--npc-ink-on-accent); }
main#staticDoc .terminal_keypad button { border-color: var(--npc-line-strong); background-color: var(--npc-surface); color: var(--npc-ink); }
main#staticDoc .terminal_keypad button[data-key="BS"] { color: var(--npc-ink-2); }
main#staticDoc #terminalInstallment { border-color: var(--npc-line-strong); background-color: var(--npc-surface-2); color: var(--npc-ink); }

/* 스택되는 인스턴트 화면은 아래를 완전히 덮는다 */
main#staticDoc article.npc_hist_page, main#staticDoc article.npc_memo_page,
main#staticDoc article[data-article-id="students"], main#staticDoc article[data-article-id="bills"],
main#staticDoc article[data-article-id="bill_details"], main#staticDoc article[data-article-id="success"],
main#staticDoc article[data-article-id="failure"], main#staticDoc article[data-article-id="successAttend"] {
    background-color: var(--npc-bg);
    min-height: 100%;
}

/* 전역 앱바를 감췄으므로 그 자리를 예약하던 상단 패딩도 없앤다 —
   남겨두면 홈을 뺀 모든 화면 위에 빈 띠가 생긴다(사용자 지적 2026-08-21). */
main#staticDoc > section.root_tab_content > .container > article { padding-top: env(safe-area-inset-top); }

/* ══════════════════════════════════════════════════════════════
   25. 우측 상단 회피 여백 — 해제 (2026-08-28, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   2026-08-21 부터 nPay 단말이 우상단에 띄우던 «커넥트 설정» 오버레이를 피해
   헤더 3종(공통 헤더·제목 행·내역 네비바)의 우측 액션을 44px×1.5 만큼 비워 왔다.
   커넥트 앱 업데이트로 그 오버레이가 사라졌고, 회피 영역 안에 놓은 프로브 버튼으로
   «표시만 숨긴 게 아니라 터치도 삼키지 않는다»를 실기기에서 확인(2026-08-28)한 뒤 걷어냈다.
   ⚠ 되살릴 일이 생기면 세 자리(.appbarRight / .npc_title_actions / .npc_navbar_actions)에
   같은 값을 한 번에 걸 것 — 개별로 걸면 이중 적용됐던 이력이 있다(§40 주석). */

/* 수납 시퀀스(원생·청구서·항목)의 뒤로·새로고침은 제목 행에서 공통 헤더 우측(화면 우상단)으로 올렸다.
   .float_area 기본 gap(4px)은 아이콘 버튼용이라 원형 버튼 쌍에는 제목 행에서 쓰던 8px 을 준다.
   🪤 main.css 의 `nav.head_block > .float_area > button { height: 100% }` 이 이 헤더(grid, align-items:center)
   안에서는 auto 로 풀려 svg 높이(24px)만 남는다 — 실측 44×24 로 원이 납작해졌다. 44px 을 명시해 되돌린다. */
main#staticDoc article > nav.head_block.npc_head > .float_area.appbarRight:has(> .npc_round_btn) { gap: 8px; }
main#staticDoc article > nav.head_block.npc_head > .float_area > .npc_round_btn { height: 44px; }

/* ══════════════════════════════════════════════════════════════
   26. 결제내역 «결제취소» 버튼 정돈 (2026-08-21, 실기기 확인)
   ══════════════════════════════════════════════════════════════
   main.css 의 구 규칙(article ul.payment_history_list > li > div button.cancel_payment)이
   height 42px + «음수 세로 마진» + 아크로매틱 다크 배경을 걸어서,
   확장 영역 안에서 위 줄을 덮고 카드 패딩 밖으로 삐져나왔다(사용자 지적).
   같은 형태의 셀렉터에 main#staticDoc 을 얹어 특이성을 이기고 새 톤으로 되돌린다. */
main#staticDoc article ul.npc_hist_list > li > div button.cancel_payment,
main#staticDoc article ul.payment_history_list > li > div button.cancel_payment {
    --height: auto;
    height: auto;
    min-height: 46px;
    margin: 0;
    padding: 0 14px;
    border: 1px solid rgb(from var(--npc-danger) r g b / 45%);
    border-radius: 12px;
    background-color: transparent;
    color: var(--npc-danger);
    font-size: 15px;
    font-weight: 700;
    line-height: 1.2;
}
main#staticDoc article ul.npc_hist_list > li > div button.cancel_payment.armed,
main#staticDoc article ul.payment_history_list > li > div button.cancel_payment.armed {
    background-color: var(--npc-danger);
    border-color: var(--npc-danger);
    color: #FFF;
}

/* 확장 영역 — 줄과 버튼 사이 숨 쉴 틈 + 카드 안쪽으로 확실히 들어오게 */
.npc_hist_lines { padding-bottom: 2px; }
.npc_hist_actions { margin-top: 12px; }
.npc_hist_row.expanded { padding-bottom: 16px; }

/* 승인번호는 접힌 상태에서도 보이게(사용자 지시) — 메타 줄 끝에 붙여 눈에 덜 튀게 */
.npc_hist_meta > .npc_hist_no { color: var(--npc-ink-3); font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════
   27. 홈 메모칸 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   서비스 버튼과 «같은 크기»의 직접 편집 칸. 글자는 상단 안내문보다 조금 크게.
   서비스 버튼을 끄면 그 자리를 대체하고, 버튼들 아래엔 상시 한 칸이 더 있다. */
main#staticDoc article.home .home_memo {
    display: block;
    flex: 0 0 auto;
    width: 100%;
    box-sizing: border-box;
    min-height: clamp(124px, 19vh, 172px);
    margin: 0;
    padding: 20px;
    border: 1px solid var(--npc-line);
    border-radius: var(--npc-radius-card);
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-card);
    color: var(--npc-ink);
    font-family: 'Pretendard';
    font-size: clamp(32px, 6.4vw, 40px);      /* 상단 안내문(30px)보다 «조금 더 크게» — 실기기에서 24px 로 클램프돼 작았다 */
    font-weight: 700;
    line-height: 1.35;
    letter-spacing: -0.03em;
    text-align: center;
    resize: none;
    overflow-y: auto;
    word-break: keep-all;
}
main#staticDoc article.home .home_memo::placeholder { color: var(--npc-ink-3); font-weight: 600; }
main#staticDoc article.home .home_memo:focus { outline: none; border-color: var(--npc-accent); }

/* 슬롯 교대 — 버튼이 켜져 있으면 대체 메모는 숨기고, 꺼져 있으면 반대로 */
body[data-svc-attend="1"] main#staticDoc article.home .home_memo[data-slot="attend"] { display: none; }
body[data-svc-attend="0"] main#staticDoc article.home button.service_card[data-svc="attend"] { display: none; }
body[data-svc-paybill="1"] main#staticDoc article.home .home_memo[data-slot="paybill"] { display: none; }
body[data-svc-paybill="0"] main#staticDoc article.home button.service_card[data-svc="paybill"] { display: none; }
/* 서비스 토글이 꺼지면 홈 카드만이 아니라 «반대편으로 건너뛰는» 상단 중앙 pill(출석 화면의 «수강료 납부», 납부 시퀀스의 «출석체크»)과
   출석완료 화면의 «수강료 내기» 도 함께 숨긴다 — 꺼 둔 서비스로 들어가는 문을 전부 닫는다 (사용자 지시 2026-09-03) */
body[data-svc-attend="0"] .head_center > .nav_pill[data-open-page="*attend"] { display: none; }
body[data-svc-paybill="0"] .head_center > .nav_pill[data-open-page="*paybill"] { display: none; }
/* 아래 규칙은 «상단 중앙» 요구보다 넓은 의도적 확장 — 출석완료 오버레이 우상단의 «수강료 내기»(현재 1곳)이고, 헤더 스코프 없이 전역이라
   같은 data-open-page 버튼이 어디에 생기든 함께 숨는다(우연이 아니라 의도). 하단 탭바(data-tab-id)는 footer 자체가 상시 숨김이라 대상 아님. */
body[data-svc-paybill="0"] .tp_tiled_btn[data-open-page="*paybill"] { display: none; }

@media (orientation: landscape) {
    main#staticDoc article.home .home_memo { flex: 1 1 0; min-height: clamp(110px, 30vh, 200px); font-size: clamp(24px, 3.4vw, 34px); }
}

/* ══════════════════════════════════════════════════════════════
   28. 실사용 피드백 반영 (2026-08-21 2차)
   ══════════════════════════════════════════════════════════════ */

/* ── 홈 메모칸 — 투명 배경 + 외곽선만, 세로 중앙, 흐린 placeholder ── */
main#staticDoc article.home .home_memo {
    display: flex;
    align-items: center;              /* 세로 중앙 정렬 */
    justify-content: center;
    flex: 0 0 auto;
    width: 100%;
    box-sizing: border-box;
    min-height: clamp(124px, 19vh, 172px);
    margin: 0;
    padding: 20px;
    border: 1px solid var(--npc-line-strong);
    border-radius: var(--npc-radius-card);
    background-color: transparent;    /* 투명 — 카드가 아니라 «칸» */
    box-shadow: none;
    color: var(--npc-ink);
    font-family: 'Pretendard';
    font-size: clamp(32px, 6.4vw, 40px);
    font-weight: 700;
    line-height: 1.35;
    letter-spacing: -0.03em;
    text-align: center;
    word-break: keep-all;
    white-space: pre-wrap;
    overflow-y: auto;
    outline: none;
}
main#staticDoc article.home .home_memo:empty::before {
    content: attr(data-placeholder);
    color: var(--npc-ink-3);
    opacity: 0.32;                    /* 거의 흐릿하게 */
    font-weight: 600;
}
main#staticDoc article.home .home_memo:focus { border-color: var(--npc-accent); }

/* 메모 토글(기본 꺼짐) — 꺼져 있으면 메모칸 자체가 안 나온다 */
body:not([data-memo-on="1"]) main#staticDoc article.home .home_memo { display: none; }

/* 서비스 버튼 글자 1.25배 */
main#staticDoc article.home button.service_card { font-size: clamp(30px, 7vw, 38px); }

/* ── 하단 학원명 1.5배 · 아래 여백 2배 ── */
#npcBrandFoot { font-size: 28px; height: 40px; bottom: calc(var(--bottom-pad) + 20px); }
html { --npc-foot-h: 66px; }
@media (orientation: landscape) {
    #npcBrandFoot { font-size: 24px; height: 30px; bottom: calc(var(--bottom-pad) + 8px); }
    html { --npc-foot-h: 42px; }
}

/* ── 출석 화면 ── */
/* 상단 메시지 1.5배 + 서브 메시지(기존 크기) */
main#staticDoc article .attend_head { padding: 2px var(--npc-pad-x) 8px; text-align: center; }
main#staticDoc article .attend_head > .npc_guide { padding: 0; font-size: clamp(26px, 5.4vw, 30px); font-weight: 700; color: var(--npc-ink); }
main#staticDoc article .attend_head > .npc_guide_sub { padding: 6px 0 0; color: var(--npc-ink-3); font-size: 19px; font-weight: 500; letter-spacing: -0.03em; word-break: keep-all; }
main#staticDoc article .attend_head > .npc_guide .who { color: var(--npc-accent); }

/* 4자리 원에 숫자 표시 */
main#staticDoc article .attend_dots > i { font-size: 34px; font-weight: 800; }

/* 하단 버튼 — 세로 1.5배, 비활성도 색은 유지하고 «글자만» 흐리게 */
main#staticDoc article button.attend_commit { min-height: 99px; font-size: 26px; }
main#staticDoc article button.attend_commit:disabled {
    background-color: var(--npc-accent);
    color: rgb(from var(--npc-ink-on-accent) r g b / 45%);
}
main#staticDoc article button.attend_commit:disabled > span { color: inherit !important; }
/* 키패드는 남은 공간에 맞춰 줄어든다 */
main#staticDoc article .attend_main_col > .num_keypad { flex: 1 1 auto; min-height: 0; }
main#staticDoc article .attend_main_col > .num_keypad > button { min-height: 0; }

/* 성공 알림 — 메시지+숫자 자리를 대신 차지(화면 전체를 덮지 않는다) */
main#staticDoc article .attend_done { display: none; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 10px var(--npc-pad-x) 18px; text-align: center; }
/* ⚠ 종전엔 성공 시 head·dots 를 display:none 하고 .attend_done 을 띄웠다 — 상단 슬롯이 168px→97px 로
   줄면서 키패드가 71px 자라 레이아웃이 튀었다(실측). 이제 성공도 «주/부 메시지»를 쓰고 도트 자리엔
   체크 뱃지가 뜬다(§42) — 슬롯 높이가 그대로라 키패드가 움직이지 않는다. .attend_done 은 미사용. */
main#staticDoc article .attend_done_name { color: var(--npc-ink); font-size: clamp(28px, 6vw, 36px); font-weight: 800; letter-spacing: -0.03em; word-break: keep-all; }
main#staticDoc article .attend_done_name b { color: var(--npc-accent); }
main#staticDoc article .attend_done_sub { color: var(--npc-ink-2); font-size: 20px; font-weight: 600; }

/* ── 수납(전화번호) 화면 — 메시지·번호·키패드 1.5배 ── */
main#staticDoc article .paybill_main_col > .npc_guide { font-size: clamp(26px, 5.4vw, 30px); font-weight: 700; color: var(--npc-ink); }
main#staticDoc article .paybill_main_col > .input_display { min-height: 84px; padding: 0 20px; font-size: clamp(34px, 7.6vw, 44px); }
main#staticDoc article .paybill_main_col > .input_display > input { font-size: inherit; }
main#staticDoc article .paybill_main_col > .num_keypad > button { font-size: clamp(30px, 6.6vw, 39px); }
/* × 버튼 — 입력란 상하 여백과 «같은 거리»를 우측에도 준다 (사용자 지시 2026-08-21).
   🪤 estreUi.css:855 의 .float_right 가 position:absolute + right:0 이라 이 버튼은 카드의
      padding-right 를 통째로 무시하고 테두리에 붙어 있었다 — 실측 우측 여백 5.5px 대 상하 20px.
      (그 5.5px 마저 원의 `margin: auto .25em`(main.css:227)에서 온 값이라 폰트 크기에 딸려 흔들렸다)
   ⇒ 버튼의 right 를 20px 로 명시하고 원의 좌우 마진·translate 를 걷어 20px 로 고정한다.
      상하 여백도 translate:0 -0.07em 탓에 18.5/21.5 로 어긋나 있었다 → 대칭 복구. */
main#staticDoc article .paybill_main_col > .input_display > button.input_clear {
    margin-left: 8px; padding: 0; right: 20px;
}
main#staticDoc article .paybill_main_col > .input_display > button.input_clear > div.circled {
    display: flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; border-radius: 50%;
    margin: 0; translate: none;
    background-color: var(--npc-bg-sunken); color: var(--npc-ink-2); font-size: 22px;
}

/* ── 목록 3종(원생·청구서·항목) — 글자 1.5배 (제목 행 액션은 2026-08-28 공통 헤더 우측으로 이동, §25) ── */
main#staticDoc article[data-article-id="students"] .npc_title,
main#staticDoc article[data-article-id="bills"] .npc_title,
main#staticDoc article[data-article-id="bill_details"] .npc_title { font-size: clamp(26px, 5.6vw, 31px); }

main#staticDoc article[data-article-id="students"] ul.blocked_list > li .item_subject { font-size: clamp(26px, 6vw, 33px); }
main#staticDoc article ul.blocked_list > li .item_details { font-size: clamp(18px, 4vw, 22px); }
.npc_bill_month { font-size: clamp(30px, 7vw, 39px); }
.npc_bill_title { font-size: clamp(18px, 4vw, 22px); }
.npc_bill_amounts { font-size: clamp(22px, 5.2vw, 28px); }
.npc_bill_amounts .label { font-size: clamp(18px, 4vw, 22px); }
.npc_item_name { font-size: clamp(18px, 4.2vw, 22px); }
.npc_amount_input { font-size: clamp(20px, 4.6vw, 25px); width: 132px; height: 52px; }
.npc_pay_amount > .cap { font-size: clamp(16px, 3.4vw, 20px); }
.npc_check, .npc_check label { width: 42px; height: 42px; }
.npc_check label::after { font-size: 24px; }
.npc_select_row > select { font-size: clamp(20px, 4.4vw, 24px); }
.npc_select_row > .hint { font-size: 17px; }

/* 🪤 카드가 뷰포트를 넘던 원인 — width:100% + padding 이 content-box 였다(포팅 함정 ⑦) */
main#staticDoc article ul.blocked_list > li > :is(div, button) { box-sizing: border-box; }
main#staticDoc article .npc_item_row { flex-wrap: wrap; row-gap: 10px; }
main#staticDoc article .npc_item_main { flex: 1 1 100%; min-width: 0; }
main#staticDoc article .npc_pay_amount { margin-left: auto; }

/* ── 결제내역 2종 — 내용 글씨 키움 ── */
.npc_hist_amount { font-size: clamp(24px, 5.4vw, 28px); }
.npc_hist_badge { font-size: 15px; padding: 6px 12px; }
.npc_hist_meta { font-size: clamp(16px, 3.6vw, 18px); gap: 4px 12px; }
.npc_hist_line { font-size: clamp(16px, 3.6vw, 18px); }
.npc_hist_line > .tag { font-size: 14px; min-width: 52px; }
.npc_hist_memo { font-size: 16px; }
.npc_hist_summary { font-size: 16px; }
main#staticDoc article .npc_navbar > .npc_navbar_title { font-size: 23px; }
.npc_month_label { font-size: 21px; min-width: 150px; }

/* ══════════════════════════════════════════════════════════════
   29. 실사용 3차 피드백 (2026-08-21)
   ══════════════════════════════════════════════════════════════ */

/* ── 목록형 화면의 상·하단 «고정 블럭» — 스크롤 항목이 밑으로 지나가도 읽히게 ── */
main#staticDoc article .npc_navbar,
main#staticDoc article .npc_month_nav,
main#staticDoc article .npc_search_bar,
main#staticDoc article .npc_hist_summary,
main#staticDoc article .npc_bottom_dock,
main#staticDoc article .npc_title_row {
    position: relative;
    z-index: 3;
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
}
main#staticDoc article .npc_navbar,
main#staticDoc article .npc_month_nav,
main#staticDoc article .npc_title_row { background-color: rgb(from var(--npc-bg) r g b / 72%); }
main#staticDoc article .npc_hist_summary { background-color: rgb(from var(--npc-bg-sunken) r g b / 72%); }
main#staticDoc article .npc_bottom_dock { background-image: none; background-color: rgb(from var(--npc-bg) r g b / 72%); }
/* 스크롤 영역이 고정 블럭 «아래로» 지나가도록 */
main#staticDoc article.npc_hist_page > .linear_block { position: relative; z-index: 1; }

/* ── 출석 화면 — 버튼과 학원명 사이 여백 축소 + 키패드가 여유를 채움 ── */
main#staticDoc article .attend_main_col > .npc_bottom_dock { padding-bottom: 10px; backdrop-filter: none; -webkit-backdrop-filter: none; background-color: transparent; }
main#staticDoc article.attend { padding-bottom: calc(var(--npc-foot-h) + var(--bottom-pad) - 12px); }
main#staticDoc article .attend_main_col { display: flex; flex-direction: column; }
main#staticDoc article .attend_main_col > .num_keypad { flex: 1 1 auto; min-height: 0; align-content: stretch; }
main#staticDoc article .attend_main_col > .num_keypad > button { height: 100%; min-height: 0; }

/* ── 결제 항목 카드 — 체크 우상단 / 금액 편집은 금액과 같은 줄 ── */
/* ⚠ 이전 절의 align-items:center 가 살아 있어 열 방향에서 자식이 가운데로 «좁아졌다»(실기기 확인).
      열 배치에선 가로로 꽉 차야 한다. */
main#staticDoc article .npc_item_row { display: flex; flex-direction: column; align-items: stretch; gap: 10px; flex-wrap: nowrap; }
main#staticDoc article .npc_item_head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
main#staticDoc article .npc_item_head > .npc_item_name { flex: 1 1 auto; min-width: 0; }
main#staticDoc article .npc_bill_amounts { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
main#staticDoc article .npc_bill_amounts > .npc_pay_amount { margin-left: auto; }

/* 체크 — 웹 기본 사각은 감추고 «원형 링»만. 체크 시 링 안에 자체 체크 표시 */
main#staticDoc article .npc_check { position: relative; display: inline-flex; width: 42px; height: 42px; flex: 0 0 auto; }
main#staticDoc article .npc_check > input[type="checkbox"] {
    position: absolute; inset: 0; width: 100%; height: 100%;
    margin: 0; padding: 0; opacity: 0; appearance: none; -webkit-appearance: none;
    border: none; background: none; pointer-events: none;
}
main#staticDoc article .npc_check > i {
    display: flex; align-items: center; justify-content: center;
    width: 42px; height: 42px;
    border: 2px solid var(--npc-line-strong); border-radius: 50%;
    color: transparent; font-style: normal; font-size: 24px; font-weight: 800; line-height: 1;
    transition: border-color 0.12s ease, color 0.12s ease;
}
main#staticDoc article .npc_check > i::after { content: "✓"; }
main#staticDoc article .npc_check > input:checked + i { border-color: var(--npc-accent); color: var(--npc-accent); }

/* ── 결제화면 하단 = 학원명 위 «고정» UI (목록만 스크롤) ── */
main#staticDoc article[data-article-id="bill_details"] { display: flex; flex-direction: column; min-height: 100%; }
main#staticDoc article[data-article-id="bill_details"] > .linear_block { flex: 1 1 auto; min-height: 0; overflow-y: auto; position: relative; z-index: 1; }
/* 🔑 결제 버튼 아래 여백 = 학원명 아래 여백 (2026-09-04 요구)
   ── 기준선을 실제 캐스케이드로 다시 풀었다. 전부 «화면 바닥(viewport bottom)» 에서 잰다.
      · article.fwvs 는 position:absolute; inset:0 이라 «컨테이너의 padding box» 를 채운다(estreUi.css:662).
        절대배치 자식은 조상의 padding 에 밀리지 않으므로 main#staticDoc(501)·section(450)·container(456)
        의 padding-bottom 은 이 계단에서 전부 0 이다 — 즉 article 의 테두리 아래끝 = 화면 끝이다.
      · 🪤 그러면 도크를 위로 띄우는 건 article «자신의» padding-bottom 이다:
        main.css:415 가 section#paybill 의 article 에 calc(56px + safe + 8px) = 64px + S 를 건다.
        이 항을 빼먹으면 「버튼을 더 내려야 한다」는 «반대 부호» 의 답이 나온다 — 조사 B 가 부딪힌 모순의 정체다.
      · 학원명(#npcBrandFoot)은 position:fixed 라 화면 바닥이 곧 기준이다: bottom = S + 20px, height = 40px (1475행).
   ── 풀이 (S = var(--bottom-pad), P = 이 도크의 padding-bottom)
        버튼 아래끝  = 64 + S + P
        학원명 윗변  = (S + 20) + 40 = S + 60
        버튼~학원명 = (64 + S + P) − (S + 60) = P + 4
        학원명~화면끝 = X = S + 20
        둘을 같게:  P + 4 = S + 20  →  P = S + 16
      종전 값은 P = --npc-foot-h + S − 14 = S + 52 였다. 그래서 36px 만큼 더 벌어져 있었다(캡처의 방향과 일치).
   ── 🪤 선택자를 bill_details 로 좁혔다. .npc_fixed_dock 은 지금 이 화면에만 붙어 있지만,
      다음 사람이 같은 클래스를 다른 화면에 재사용하면 «이 화면 전용» 계산이 말없이 번진다. */
main#staticDoc article[data-article-id="bill_details"] .npc_bottom_dock.npc_fixed_dock {
    position: sticky; bottom: 0; z-index: 4;
    margin-top: 0;
    padding: 12px var(--npc-pad-x) calc(var(--bottom-pad) + 16px);
}
/* 🪤 가로에는 «맞출 기준선이 없다» — 위 계산을 물려주지 않는다.
   🔴 정정(적대검증): 종전 주석은 「가로에선 학원명이 bottom = S+8 로 바뀐다」고 적었는데 틀렸다.
      §48(아래 @media landscape)이 홈이 아닌 화면에서 `--npc-foot-h: 0px` 로 두고 `#npcBrandFoot` 를
      `top: 0; bottom: auto` 로 «상단 앱바»에 올린다. 즉 bill_details 가 떠 있는 가로 화면의 하단에는
      학원명이 아예 없다. 「버튼이 학원명을 파고든다」는 걱정도, 거기 적힌 공식도 성립하지 않는다.
      (article 의 padding-bottom 10px 도 근거가 2574행이 아니라 §51 쪽 규칙이다 — 2574행 선택자는
       `.paybill` 클래스를 요구하는데 이 article 은 `class="fwvs"` 라 애초에 안 맞는다.)
   ⇒ 가로 여백은 «별도 요구가 생기면 실측으로» 정한다(현재 모름). 그때까지 종전 식을 그대로 둔다.
      가로에서 이 식은 --npc-foot-h 가 0 이므로 S − 14 로 평가된다. 값은 이번 변경 전과 같다(회귀 없음). */
@media (orientation: landscape) {
    main#staticDoc article[data-article-id="bill_details"] .npc_bottom_dock.npc_fixed_dock {
        padding-bottom: calc(var(--npc-foot-h) + var(--bottom-pad) - 14px);
    }
}

/* ── 메뉴 항목 숨김 (사용자 지시) ── */
nav#mainMenu li.npc_menu_hidden { display: none; }

/* 항목명은 좌측 정렬 (부모 flex 안에서 가운데로 밀려 보였다) */
main#staticDoc article .npc_item_head > .npc_item_name { text-align: left; }

/* ══════════════════════════════════════════════════════════════
   30. 메모 편집 모드 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   키보드가 올라오면 하단 메모칸이 가려진다 → 편집 중인 칸을 화면 상단으로 띄우고
   그 아래에 «비우기 / 수정완료» 를 붙인다. */
/* ⚠ 딤을 body 의사요소로 두면 메모·편집바와 «다른 스태킹 컨텍스트»라 z-index 를 올려도 뒤로 간다.
      메모와 같은 컨텍스트(홈 아티클) 안에 두어야 앞뒤가 의도대로 잡힌다(실기기 확인). */
body[data-memo-edit="1"] main#staticDoc article.home::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 118;
    background-color: rgb(20 12 4 / 38%);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

main#staticDoc article.home .home_memo.editing {
    position: fixed;
    z-index: 120;
    top: max(16px, calc(env(safe-area-inset-top) + 14px));   /* 실제 위치는 npcUi 가 시각 뷰포트로 재계산 */
    left: var(--npc-pad-x);
    right: var(--npc-pad-x);
    width: auto;
    min-height: clamp(150px, 24vh, 210px);
    max-height: 40vh;
    border-color: var(--npc-accent);
    background-color: var(--npc-surface);
    box-shadow: var(--npc-shadow-pop);
    align-items: center;              /* 편집 중에도 세로 중앙 유지 (사용자 지시 2026-08-21) */
    text-align: center;
}

#npcMemoEditBar {
    position: fixed;
    z-index: 120;
    left: var(--npc-pad-x);
    right: var(--npc-pad-x);
    top: calc(max(16px, calc(env(safe-area-inset-top) + 14px)) + clamp(150px, 24vh, 210px) + 10px);
    display: none;
    gap: 10px;
}
#npcMemoEditBar.on { display: flex; }
#npcMemoEditBar > button {
    flex: 1 1 0;
    min-height: 58px;
    margin: 0;
    padding: 0 16px;
    border-radius: var(--npc-radius-btn);
    border: 1px solid var(--npc-line-strong);
    background-color: var(--npc-surface);
    color: var(--npc-ink-2);
    font-size: 19px;
    font-weight: 700;
    cursor: pointer;
}
#npcMemoEditBar > button.done {
    border-color: transparent;
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
}
#npcMemoEditBar > button:active { filter: brightness(0.96); }

/* 메모 글자는 포인트 컬러로 (사용자 지시 2026-08-21) — 안내문과 구분되고 눈에 먼저 든다 */
main#staticDoc article.home .home_memo { color: var(--npc-accent); }
main#staticDoc article.home .home_memo:empty::before { color: var(--npc-accent); opacity: 0.28; }

/* 편집 중인 메모·편집바는 딤보다 앞 (같은 컨텍스트 안에서 순서 확정) */
main#staticDoc article.home .home_memo.editing { z-index: 120; }
#npcMemoEditBar { z-index: 121; }

/* ══════════════════════════════════════════════════════════════
   31. 메모칸 구조 분리 (2026-08-21)
   ══════════════════════════════════════════════════════════════
   🪤 contenteditable 자체에 display:flex 를 걸면 «비어 있을 때» 정렬할 flex 항목이 없어서
      캐럿이 좌상단으로 간다(사용자 지적: 포커스+빈칸일 때 세로 중앙정렬이 안 먹음).
      바깥은 중앙정렬 상자, 안쪽은 «일반 블럭» 편집 요소로 분리하면 캐럿이 정상 위치에 선다. */
main#staticDoc article.home .home_memo_input {
    display: block;
    width: 100%;
    max-height: 100%;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    line-height: inherit;
    text-align: inherit;
    word-break: keep-all;
    white-space: pre-wrap;
    overflow-y: auto;
    outline: none;
    -webkit-user-modify: read-write-plaintext-only;   /* 붙여넣기 서식 유입 차단 */
}
main#staticDoc article.home .home_memo_input:empty::before {
    content: attr(data-placeholder);
    color: var(--npc-accent);
    opacity: 0.28;
    font-weight: 600;
}
/* 바깥 상자는 «상자 역할»만 — 편집은 안쪽이 한다 */
main#staticDoc article.home .home_memo { overflow: hidden; }
main#staticDoc article.home .home_memo:empty::before { content: none; }
main#staticDoc article.home .home_memo:has(> .home_memo_input:focus) { border-color: var(--npc-accent); }

/* 메뉴 행 숨김 (li 뿐 아니라 토글 행도) */
nav#mainMenu .npc_menu_row.npc_menu_hidden { display: none; }

/* ══════════════════════════════════════════════════════════════
   32. 현금영수증 입력 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   가이드 §5 — 번호를 미리 받으면 pinData + wcc="K" 로 넘기고, 비우면 단말기가 직접 받는다. */
.cash_kind { display: inline-flex; gap: 6px; }
.cash_kind_btn {
    min-height: 38px;
    padding: 0 14px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 999px;
    background-color: var(--npc-surface);
    color: var(--npc-ink-2);
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
}
.cash_kind_btn.active { border-color: transparent; background-color: var(--npc-accent); color: var(--npc-ink-on-accent); }

.terminal_extra.cash_id_row { align-items: center; gap: 10px; }
.terminal_extra.cash_id_row > label { flex: 0 0 auto; }
#terminalCashId {
    flex: 1 1 auto;
    min-width: 0;
    height: 46px;
    padding: 0 14px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 12px;
    background-color: var(--npc-surface);
    color: var(--npc-ink);
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 0.01em;
}
#terminalCashId::placeholder { color: var(--npc-ink-3); font-size: 15px; font-weight: 500; }
#terminalCashId:focus { border-color: var(--npc-accent); outline: none; }
#terminalCashIdClear {
    flex: 0 0 auto;
    width: 38px; height: 38px; padding: 0;
    border: none; border-radius: 50%;
    background-color: var(--npc-bg-sunken);
    color: var(--npc-ink-2);
    font-size: 19px; line-height: 1;
    cursor: pointer;
}
/* CREDIT 모드에선 현금영수증 줄을 감춘다(기존 .terminal_extra.cash 규칙과 동일 축) */
article.terminal[data-tmode="CREDIT"] .terminal_extra.cash_id_row { display: none; }

/* 터미널 화면도 하단 브랜드 라인 자리를 비워둔다 (현금영수증 줄이 늘면서 키패드가 겹쳤다) */
main#staticDoc article.terminal { padding-bottom: calc(var(--npc-foot-h) + var(--bottom-pad) - 16px); }
main#staticDoc article.terminal .terminal_keypad button[data-key="BS"] { min-height: 0; }

/* 🪤 .terminal_body 가 flex-shrink:0 이라 내용이 늘면 아티클 하단 패딩을 뚫고 나간다
      (현금영수증 줄 추가 후 키패드가 학원명과 겹친 원인 — 실기기 실측 818 > 788). 축소를 허용한다. */
main#staticDoc article.terminal .terminal_body { flex: 1 1 auto; min-height: 0; }
main#staticDoc article.terminal .terminal_keypad { min-height: 0; }
/* ⚠ .terminal_main 의 min-height:0 은 §39 에서 걷어냈다 — 그게 곧 «키패드가 버튼을 덮는» 원인이었다. */

/* ══════════════════════════════════════════════════════════════
   33. 결제·취소 완료 팝업 / 터미널 블록 정돈 (2026-08-21 4차)
   ══════════════════════════════════════════════════════════════ */

/* ── 완료 컨펌 레이어 (자동 닫힘 없음) ── */
.npc_result_pop { width: min(420px, 100%); padding: 26px 22px 20px; text-align: center; }
.npc_result_icon {
    display: flex; align-items: center; justify-content: center;
    width: 68px; height: 68px; margin: 0 auto 14px;
    border-radius: 50%;
    background-color: var(--npc-accent-soft);
    color: var(--npc-ok);
    font-size: 34px; font-weight: 800; line-height: 1;
}
.npc_result_icon.cancel { color: var(--npc-danger); background-color: var(--npc-danger-soft); }
.npc_result_title { color: var(--npc-ink-2); font-size: 20px; font-weight: 700; letter-spacing: -0.03em; }
.npc_result_amount { margin-top: 6px; color: var(--npc-ink); font-size: 38px; font-weight: 800; letter-spacing: -0.04em; }
.npc_result_desc { margin-top: 10px; color: var(--npc-accent-press); font-size: 18px; font-weight: 700; }
body[data-dark-mode="1"] .npc_result_desc { color: var(--npc-accent); }
/* 결제 확인 스테이지 카드 — main.css(.npc_pay_stage_box)가 라이트 하드코딩(#FFF)이라 다크에서 흰판으로 남았다(실기기 실측) */
body[data-dark-mode="1"] .npc_pay_stage_box { background-color: var(--npc-surface); }
body[data-dark-mode="1"] .npc_pay_stage_title { color: var(--npc-ink); }
body[data-dark-mode="1"] .npc_pay_stage_desc:not(:empty) { color: var(--npc-ink-2); }
.npc_result_actions { display: flex; gap: 10px; margin-top: 22px; }
.npc_result_actions > button {
    flex: 1 1 0;
    min-height: 60px;
    margin: 0; padding: 0 14px;
    border: 1px solid var(--npc-line-strong);
    border-radius: var(--npc-radius-btn);
    background-color: transparent;
    color: var(--npc-ink-2);
    font-size: 18px; font-weight: 700;
    cursor: pointer;
}
.npc_result_actions > button.npc_result_receipt {
    flex: 1.4 1 0;
    border-color: transparent;
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
}
.npc_result_actions > button:disabled { opacity: 0.6; cursor: default; }

/* ── 터미널 현금영수증 번호 — X 를 입력란 «안쪽» 우측으로 ── */
.cash_id_field { position: relative; display: flex; flex: 1 1 auto; min-width: 0; align-items: center; }
.cash_id_field > #terminalCashId { width: 100%; padding-right: 46px; }
.cash_id_field > #terminalCashIdClear {
    position: absolute;
    right: 6px; top: 50%;
    transform: translateY(-50%);
    width: 32px; height: 32px;
}

/* ── 표시 블록: 하단 공백 제거 + 두 모드 높이 통일 ── */
main#staticDoc article.terminal .terminal_display {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: 10px;
    /* 🪤 하단 공백의 정체 — 비어 있는 .terminal_msg 가 margin-top:auto 로 바닥까지 밀려
       그 위에 빈 공간(실측 63px)을 만들고 있었다. 카드는 «내용만큼» 이어야 한다(사용자 지시). */
    min-height: 0;
    height: auto;
    flex: 0 0 auto;
    padding: 14px 18px;
}
main#staticDoc article.terminal .terminal_display > .terminal_extra { margin-top: 0; }
main#staticDoc article.terminal .terminal_display > .terminal_msg { margin-top: 0; }
/* 메시지가 없을 땐 자리도 차지하지 않는다 */
main#staticDoc article.terminal .terminal_display > .terminal_msg:empty { display: none; }
main#staticDoc article.terminal .terminal_amount { margin-top: 0; }

/* ── 키패드: 남는 공간을 «균일하게» 채운다 ── */
main#staticDoc article.terminal .terminal_keypad {
    grid-auto-rows: 1fr;
    align-content: stretch;
    gap: 8px;
}
main#staticDoc article.terminal .terminal_keypad button { height: 100%; min-height: 0; }

/* ══════════════════════════════════════════════════════════════
   34. 결제 항목 카드 — 납부/미납 2줄, 미납 줄 오른쪽에 금액 편집 (2026-08-21)
   ══════════════════════════════════════════════════════════════ */
main#staticDoc article .npc_item_amounts { display: flex; flex-direction: column; gap: 6px; }
main#staticDoc article .npc_item_amounts .amt_line {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: clamp(22px, 5.2vw, 28px);
    font-weight: 700;
}
main#staticDoc article .npc_item_amounts .label { font-size: clamp(18px, 4vw, 22px); font-weight: 600; color: var(--npc-ink-2); }
main#staticDoc article .npc_item_amounts .label.danger { color: var(--npc-danger); }
main#staticDoc article .npc_item_amounts .paid { color: var(--npc-ink); }
main#staticDoc article .npc_item_amounts .remain { color: var(--npc-danger); }
/* 금액 편집은 미납 줄 «오른쪽 끝» */
main#staticDoc article .npc_item_amounts .remain_line > .npc_pay_amount { margin-left: auto; }

/* 결제내역 카드 윗줄 — 결제일시(작고 흐리게, 가운데) */
.npc_hist_when {
    display: block;
    margin-bottom: 6px;
    color: var(--npc-ink-3);
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.01em;
    text-align: center;
    opacity: 0.75;
    font-variant-numeric: tabular-nums;
}

/* ══════════════════════════════════════════════════════════════
   35. 결제내역 합계 카드 — 하단 고정 + 정렬 (2026-08-21)
   ══════════════════════════════════════════════════════════════
   🪤 목록이 길어지면 합계가 스크롤 항목 «위에 겹쳐» 떠 있었다.
      아티클 높이를 100% 로 묶어 목록만 내부 스크롤시키고, 합계는 열의 마지막에 고정한다. */
main#staticDoc article.npc_hist_page { height: 100%; min-height: 0; }
main#staticDoc article.npc_hist_page > .linear_block { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
main#staticDoc article.npc_hist_page > .npc_hist_summary { flex: 0 0 auto; }
main#staticDoc article.npc_hist_page > .bottom_pad { display: none; }

main#staticDoc article .npc_hist_summary {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 10px var(--npc-pad-x) calc(var(--npc-foot-h) + var(--bottom-pad) - 22px);
    padding: 14px 18px;
    border-radius: 16px;
    border: 1px solid var(--npc-line);
}
.npc_hist_summary .sum_line { display: flex; align-items: center; gap: 12px; font-size: 17px; font-weight: 600; }
.npc_hist_summary .sum_line > .tag {
    flex: 0 0 auto;
    min-width: 52px;
    padding: 4px 10px;
    border-radius: 999px;
    background-color: var(--npc-accent-soft);
    color: var(--npc-accent-press);
    font-size: 14px; font-weight: 800; text-align: center;
}
body[data-dark-mode="1"] .npc_hist_summary .sum_line > .tag { color: var(--npc-accent); }
.npc_hist_summary .sum_line.cancel > .tag { background-color: var(--npc-danger-soft); color: var(--npc-danger); }
.npc_hist_summary .sum_line > .cnt { flex: 0 0 auto; min-width: 62px; color: var(--npc-ink-2); }
.npc_hist_summary .sum_line > .amt { margin-left: auto; color: var(--npc-ink); font-variant-numeric: tabular-nums; }
.npc_hist_summary .sum_line b { font-weight: 800; }

/* 합계 카드도 «다른 고정 블럭과 동일하게» 투명 + 배후 블러 — 목록이 밑으로 지나가게 (사용자 지시) */
main#staticDoc article.npc_hist_page { position: relative; }
main#staticDoc article.npc_hist_page > .npc_hist_summary {
    position: absolute;
    z-index: 3;
    left: var(--npc-pad-x);
    right: var(--npc-pad-x);
    bottom: calc(var(--bottom-pad) + 68px);   /* 학원명(20~60) 위로 8px 여유 */
    margin: 0;
    border-color: rgb(from var(--npc-line) r g b / 60%);
    background-color: rgb(from var(--npc-bg) r g b / 55%);
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
}
/* 마지막 항목이 합계에 영구히 가리지 않도록 목록 아래를 비워둔다 */
main#staticDoc article.npc_hist_page > .linear_block { padding-bottom: calc(var(--bottom-pad) + 170px); }

/* ══════════════════════════════════════════════════════════════
   36. 터미널 입력 포커스 전환 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   번호칸은 readonly — 시스템 키보드 대신 «화면 키패드»로 입력하고, 칸을 눌러 대상을 바꾼다. */
main#staticDoc article.terminal .terminal_amount { position: relative; padding-right: 52px; cursor: pointer; }
main#staticDoc article.terminal #terminalAmountClear {
    position: absolute;
    right: 8px; top: 50%;
    transform: translateY(-50%);
    width: 34px; height: 34px; padding: 0;
    border: none; border-radius: 50%;
    background-color: rgb(from var(--npc-ink-3) r g b / 18%);
    color: var(--npc-ink-2);
    font-size: 19px; line-height: 1;
    cursor: pointer;
}
main#staticDoc article.terminal #terminalCashId { cursor: pointer; caret-color: transparent; }

/* 지금 입력이 들어가는 칸을 분명히 */
main#staticDoc article.terminal .focus_target { border: 2px solid transparent; transition: border-color 0.12s ease; }
main#staticDoc article.terminal .terminal_amount.focus_target { border-radius: 10px; }
main#staticDoc article.terminal .focus_target.focused { border-color: var(--npc-accent); }
main#staticDoc article.terminal #terminalCashId.focused { border-color: var(--npc-accent); }

/* ══════════════════════════════════════════════════════════════
   37. 터미널 안내 메시지 — 거래금액 줄 우측 티커 (2026-08-21)
   ══════════════════════════════════════════════════════════════
   카드 맨 아래에 붙어 카드를 늘리던 것을 «거래금액 줄 우측 끝»으로 옮기고,
   내용이 여러 줄이면 아래→위로 밀려 올라가며 한 줄씩 루프한다. */
main#staticDoc article.terminal .terminal_amount_label {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 20px;
}
main#staticDoc article.terminal .terminal_msg {
    --msg-line-h: 20px;
    flex: 1 1 auto;
    min-width: 0;
    height: var(--msg-line-h);
    overflow: hidden;
    text-align: right;
    font-size: 14px;
    font-weight: 700;
    line-height: var(--msg-line-h);
    color: var(--npc-danger);
}
main#staticDoc article.terminal .terminal_msg.info { color: var(--npc-ok); }
main#staticDoc article.terminal .terminal_msg .msg_track { display: block; will-change: transform; }
main#staticDoc article.terminal .terminal_msg .msg_line {
    display: block;
    height: var(--msg-line-h);
    line-height: var(--msg-line-h);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ══════════════════════════════════════════════════════════════
   38. 터미널 번호 입력란 — 금액칸과 같은 «칸» 으로 보이게 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   테두리가 없어 «누를 수 있는 칸»인지 구분되지 않았다. 금액칸과 동일한 테두리·라운드·연한 배경을
   주고, 포커스 시 강조는 §36 의 .focused 규칙이 그대로 이어받는다. */
main#staticDoc article.terminal #terminalCashId {
    border: 2px solid var(--npc-line);
    border-radius: 10px;
    background-color: var(--npc-accent-soft);
    color: var(--npc-ink);
    padding: 10px 46px 10px 12px;
    font-weight: 700;
}
main#staticDoc article.terminal #terminalCashId::placeholder { color: var(--npc-ink-3); font-weight: 500; }

/* ══════════════════════════════════════════════════════════════
   39. 터미널 키패드 반응형 — 상단 버튼을 가리지 않게 (2026-08-21, 사용자 지적)
   ══════════════════════════════════════════════════════════════
   🪤 겹침의 정체: .terminal_main 에 min-height:0 을 줘서 «내용보다 작게» 줄어들 수 있었다.
      그러면 초기화/결제요청 행이 박스 밖으로 삐져나오고, 다음 형제인 키패드가 불투명 배경으로
      그 위에 그려진다. 실기기 실측(CASH): main scrollH 292 > clientH 276 → actions 16px 이탈,
      키패드 겹침 4px. CREDIT 은 12px 여유가 있어 안 터졌다 — 그래서 «경우에 따라» 였다.
   ⇒ 줄어드는 몫은 전부 키패드가 흡수한다. 세로 레이아웃 한정(가로는 row 배치라 축이 다름). */
@media (max-aspect-ratio: 5/4) {
    /* 카드+버튼은 «내용만큼» 반드시 확보 — 여기서 줄이면 그 즉시 겹침이 된다 */
    main#staticDoc article.terminal .terminal_main { flex: 0 0 auto; min-height: auto; }

    /* 남는 높이는 전부 키패드 몫이고, 모자라면 키패드가 줄어든다 */
    main#staticDoc article.terminal .terminal_keypad {
        flex: 1 1 auto;
        /* 🪤 grid-auto-rows:1fr 은 minmax(auto,1fr) 이라 행이 min-content 아래로 못 줄어든다
           — 그러면 그리드가 컨테이너를 뚫고 아래(학원명 자리)로 넘친다. 최소를 0 으로 열어준다. */
        grid-auto-rows: minmax(0, 1fr);
        min-height: 260px;   /* 탭 타깃이 뭉개지기 전 바닥 — 이 아래로 밀리면 겹치지 말고 스크롤 */
    }
    /* 바닥까지 써도 안 들어가는 화면에선 «겹침» 대신 «스크롤» 로 실패한다 */
    main#staticDoc article.terminal .terminal_body { overflow-y: auto; }
}

/* ══════════════════════════════════════════════════════════════
   40. QR 출결 버튼 (2026-08-21, 사용자 지시 — 단말 scanCode §15)
   ══════════════════════════════════════════════════════════════
   (구 우상단 회피 여백은 2026-08-28 해제 — §25. 되살릴 때 여기 개별 여백을 두면 이중 적용된다.) */
main#staticDoc article.attend #beginAttendWithQR > svg { width: 26px; height: 26px; }
/* 연속 스캔 중 표시 — 스캔 UI 가 페이지를 덮으므로 «스캔 사이 틈»에만 보인다 */
main#staticDoc article.attend #beginAttendWithQR.on {
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
    border-radius: 50%;
}

/* ══════════════════════════════════════════════════════════════
   41. 출결 오류 안내 — 상단 주/부 메시지 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   하단 토스트는 키패드 위에 겹쳐 잘 안 읽혔다. 주 메시지 = 무슨 일이 있었는지,
   부 메시지 = 무엇을 해달라. 잠시 후 또는 입력이 들어오면 평상 문구로 돌아간다. */
main#staticDoc article[data-attend="error"] .attend_msg { color: var(--npc-danger); }
main#staticDoc article[data-attend="error"] .attend_sub { color: var(--npc-ink-2); }

/* ══════════════════════════════════════════════════════════════
   42. 출결 완료 체크 뱃지 + 4자리 시 숫자키 잠금 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   도트가 사라지며 키패드가 그 자리를 먹던 것을 멈춘다 — «공간은 유지»하고
   도트만 사그라들며 같은 크기의 원형 뱃지에 녹색 체크가 그려진다. */
main#staticDoc article .attend_dots { position: relative; }
main#staticDoc article .attend_dots > i {
    transition: border-color 0.12s ease, background-color 0.12s ease,
                opacity 0.18s ease, transform 0.24s ease;
}
main#staticDoc article .attend_check {
    position: absolute;
    left: 50%; top: 10px;          /* 도트와 같은 세로 위치(.attend_dots 의 padding-top) */
    margin-left: -31px;            /* 62px 뱃지의 절반 — transform 은 애니메이션 몫으로 비워둔다 */
    width: 62px; height: 62px;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--npc-ok);
    border-radius: 50%;
    background-color: rgb(from var(--npc-ok) r g b / 12%);
    color: var(--npc-ok);
    opacity: 0;
    transform: scale(0.55);
    pointer-events: none;
    transition: opacity 0.2s ease, transform 0.34s cubic-bezier(0.18, 1.5, 0.4, 1);
}
main#staticDoc article .attend_check > svg { width: 34px; height: 34px; }
main#staticDoc article .attend_check > svg path { stroke-dasharray: 21; stroke-dashoffset: 21; }

main#staticDoc article[data-attend="success"] .attend_dots > i { opacity: 0; transform: scale(0.9); }
main#staticDoc article[data-attend="success"] .attend_check { opacity: 1; transform: scale(1); }
/* 체크는 «그려지듯» 나타난다 — 뱃지가 튀어오른 직후 시작 */
main#staticDoc article[data-attend="success"] .attend_check > svg path {
    animation: npcCheckDraw 0.4s 0.12s ease forwards;
}
@keyframes npcCheckDraw { to { stroke-dashoffset: 0; } }

/* 메시지도 뱃지와 «같이» 전환된다 */
main#staticDoc article[data-attend="success"] .attend_msg,
main#staticDoc article[data-attend="success"] .attend_sub,
main#staticDoc article[data-attend="error"] .attend_msg,
main#staticDoc article[data-attend="error"] .attend_sub { animation: npcAttendMsgIn 0.28s ease; }
@keyframes npcAttendMsgIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* 4자리가 차면 숫자키를 잠근다 — 지우기(×)·되돌리기(←)는 그대로 열려 있다 */
main#staticDoc article.attend[data-code-full="1"] .num_keypad button[data-type="number"] {
    opacity: 0.32;
    pointer-events: none;
}

/* ══════════════════════════════════════════════════════════════
   43. 로딩 인디케이터 — Lottie 대체 CSS 구현 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   종전 `lotties/loading_circle_MET.json` 을 그대로 옮겼다. 원본 실측값:
     · 캔버스 800×600 을 150×150 박스에 meet 로 축소 → 배율 0.1875, 세로 오프셋 18.75px
     · 20프레임 @10fps = 2초, 재생속도 1.5배 → 실제 주기 1.334초
     · 이징 = cubic-bezier(0.785, 0.135, 0.15, 0.86) (원본 out/in 베지어 그대로)
     · 원 3개가 각자 A↔B 두 점을 왕복 (회전이 아니라 «자리바꿈»)
   🔑 바꾼 것은 «파란 원 하나»뿐이다 — 원본 #0064FF 는 토스 브랜드 블루였다.
      노란 계열 2개(#FDD105·#FFB300)는 **우리(망고에듀) 색이고 콜라보를 뜻하므로 그대로 둔다**
      (사용자 정정 2026-08-21). 파란 원만 네이버페이 그린으로 교체.
   근거 = **NAVER Pay BI 컬러 보드**(HuskyFox 제작, © NAVER FINANCIAL) 인쇄 스펙
     Primary `#00DE5A / R0 G222 B90 / Pantone 7480C / C69 M0 Y74 K0` (페어 #000000,
     그라디언트 #00DE5A→#28AAEB · #BEF5D7→#00DE5A). 공식 덱 무손실 PNG 픽셀 실측으로 교차확인 —
     「설명회 Npay_Connect 소개_251013」 #00DE5A 158,385px 대 #03C75A 48px (3,300:1).
   🪤 **#03C75A 는 네이버페이 색이 아니다** — NAVER «코퍼레이트» 아이덴티티(Pantone 2270C)로
      층위가 다르다. 한때 `npay-vertical-web-sample/index.html:17` 의 `--green:#03c75a` 를
      근거로 삼았으나, 그 파일은 **토스 디자인 토큰 세트를 그대로 쓰고 그린 한 칸만 갈아끼운
      데모 스캐폴드**다(같은 파일에 #3182f6 토스 블루·#f04452 토스 레드·#191f28·#8b95a1·#f2f4f6).
      토스 색을 걷어내는 작업에서 토스 템플릿을 근거로 삼은 셈이라 기각했다.
   좌표·지름은 전부 박스 크기(--sp) 대비 «비율»이라 크기를 바꿔도 형태가 유지된다.
   🔧 배치 확대 k=1.3 (2026-08-21 사용자 지시 «중심 기준으로 조금만 더 띄워줘»):
      좌표를 중심(0.5,0.5) 기준 1.3배 — A 위치와 이동량 Δ 를 함께 곱했다(형태 보존).
      원본 비율은 A(0.5705,0.6541)/Δ(-0.1059,-0.3197) · A(0.4522,0.3324)/Δ(-0.1837,0.3011)
      · A(0.3297,0.5599)/Δ(0.3524,0.0095) — 되돌리려면 이 값으로.
   ⚠ 확대해도 «교차 겹침»은 거의 안 준다 — d1·d3 가 주기 중간(e≈0.45)에 서로를 통과하는 것이
      원본 동작이다(k=1.0 에서 이미 24.3px 겹침, k=1.5 로 키워도 19.5px). 벌어지는 건 평소 배치뿐.
      k 상한은 상자 여백이 0 이 되는 1.5 근처(1.3 에서 여백 7.7px). */
.npc_spinner {
    --sp: 150px;
    --npc-load-1: #00DE5A;   /* 네이버페이 Primary — 여기만 교체(구 #0064FF 토스 블루) */
    --npc-load-2: #FDD105;   /* 망고에듀 옐로 — 원본 유지(콜라보 표식) */
    --npc-load-3: #FFB300;   /* 망고에듀 앰버 — 원본 유지(콜라보 표식) */
    position: relative;
    width: var(--sp);
    height: var(--sp);
}
.npc_spinner > span {
    position: absolute;
    border-radius: 50%;
    animation-duration: 1.334s;
    animation-iteration-count: infinite;
    animation-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
    will-change: transform;
}

/* 원본 Layer 3 — 토스 블루였던 자리. 여기만 네이버페이 그린으로 */
.npc_spinner > .d1 {
    width: calc(var(--sp) * 0.2637); height: calc(var(--sp) * 0.2637);
    left: calc(var(--sp) * 0.59165); top: calc(var(--sp) * 0.70033);
    margin-left: calc(var(--sp) * -0.13185); margin-top: calc(var(--sp) * -0.13185);
    background-color: var(--npc-load-1);
    animation-name: npcSpinnerA;
}
@keyframes npcSpinnerA { 50% { transform: translate(calc(var(--sp) * -0.13767), calc(var(--sp) * -0.41561)); } }

/* 원본 Layer 2 — 망고에듀 옐로, 가장 큰 원 (색 유지) */
.npc_spinner > .d2 {
    width: calc(var(--sp) * 0.2950); height: calc(var(--sp) * 0.2950);
    left: calc(var(--sp) * 0.43786); top: calc(var(--sp) * 0.28212);
    margin-left: calc(var(--sp) * -0.1475); margin-top: calc(var(--sp) * -0.1475);
    background-color: var(--npc-load-2);
    animation-name: npcSpinnerB;
}
@keyframes npcSpinnerB { 50% { transform: translate(calc(var(--sp) * -0.23881), calc(var(--sp) * 0.39143)); } }

/* 원본 Layer 1 — 망고에듀 앰버, 가장 작은 원 (색 유지) */
.npc_spinner > .d3 {
    width: calc(var(--sp) * 0.1877); height: calc(var(--sp) * 0.1877);
    left: calc(var(--sp) * 0.27861); top: calc(var(--sp) * 0.57787);
    margin-left: calc(var(--sp) * -0.09385); margin-top: calc(var(--sp) * -0.09385);
    background-color: var(--npc-load-3);
    animation-name: npcSpinnerC;
}
@keyframes npcSpinnerC { 50% { transform: translate(calc(var(--sp) * 0.45812), calc(var(--sp) * 0.01235)); } }

/* 움직임을 줄이도록 설정한 사용자에겐 자리바꿈 대신 깜빡임으로 */
@media (prefers-reduced-motion: reduce) {
    .npc_spinner > span { animation-name: npcSpinnerPulse !important; animation-timing-function: ease-in-out; }
    @keyframes npcSpinnerPulse { 50% { opacity: 0.3; } }
}

/* ══════════════════════════════════════════════════════════════
   44. 확인 팝업 (NpcConfirm) — 되돌릴 수 없는 요청 직전 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   결과 팝업(.npc_result_pop)의 껍데기를 그대로 쓰되, 거래 내용과 경고 문구가 더 붙는다.
   현금영수증 취소에만 뜬다 — 카드·체크는 누르면 바로 요청한다. */
.npc_confirm_detail {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 8px 16px;
    margin: 16px 0 0;
    padding: 14px 16px;
    border-radius: 12px;
    background-color: var(--npc-bg-sunken);
    text-align: left;
}
.npc_confirm_detail > dt { color: var(--npc-ink-3); font-size: 15px; font-weight: 600; white-space: nowrap; }
.npc_confirm_detail > dd { margin: 0; color: var(--npc-ink); font-size: 16px; font-weight: 700; text-align: right; word-break: break-all; }

/* 마지막 문구는 «읽히게» — 이 팝업의 존재 이유가 이 한 줄이다 */
.npc_confirm_warn {
    margin: 14px 0 0;
    padding: 12px 14px;
    border: 1px solid rgb(from var(--npc-danger) r g b / 45%);
    border-radius: 12px;
    background-color: var(--npc-danger-soft);
    color: var(--npc-danger);
    font-size: 16px;
    font-weight: 800;
    line-height: 1.45;
    letter-spacing: -0.01em;
    word-break: keep-all;
}

/* 파괴적 확정 버튼 — 액센트(주황)가 아니라 위험색으로 */
.npc_result_actions > button.npc_confirm_yes {
    flex: 1.4 1 0;
    border-color: transparent;
    background-color: var(--npc-danger);
    color: var(--npc-ink-on-accent);
}
.npc_result_actions > button.npc_confirm_yes:disabled { opacity: 0.6; cursor: default; }

/* ══════════════════════════════════════════════════════════════
   45. 터미널 메모 하단 — 안내 문구 + 2버튼 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   버튼 라벨이 «닫기»로 바뀌어도 저장은 된다 — 그 사실을 문구가 대신 말한다.
   두 번째 버튼(영수증 카톡)은 «결제 완료 직후 + 유효한 번호» 일 때만 뜬다.
   메모 수정(결제내역 진입)에는 「저장하고 닫기」 하나만 — 사용자 지시. */
.memo_dock_note {
    margin: 0 0 10px;
    color: var(--npc-ink-3);
    font-size: 15px;
    font-weight: 600;
    text-align: center;
    letter-spacing: -0.01em;
    word-break: keep-all;
}
.memo_actions { display: flex; gap: 10px; }
.memo_actions > .npc_cta { flex: 1 1 0; min-width: 0; }
/* 영수증 버튼이 함께 뜰 때 라벨이 길다 — 두 줄이 되더라도 깨지지 않게 */
.memo_actions > .memo_send { flex: 1.5 1 0; font-size: 17px; line-height: 1.25; word-break: keep-all; }

/* ══════════════════════════════════════════════════════════════
   46. 메모 화면 = 단독 화면 (2026-08-21, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   「기존 UI가 다 남아있는 상태로 뜬다」 — 아티클 자체는 전체화면 불투명이었지만
   공용 «브랜드 라인»이 아래에 그대로 남아 다른 화면 위에 얹힌 것처럼 보였다.
   ⇒ 메모가 열려 있는 동안 브랜드 라인을 덮고, 하단 독의 브랜드 자리 예약도 걷는다. */
body[data-npc-memo] .mango_user_handle { display: none !important; }

/* 🔴 진짜 원인 — 「상하단에 기존 UI가 나온다」(사용자 지적, 히트테스트로 확정 2026-08-21).
   메모 아티클은 z-index:1 로 스태킹 컨텍스트를 만들지만, 뒤의 결제내역 아티클은 z-index:auto 라
   컨텍스트를 «안» 만든다. 그래서 그 안의 sticky 자식들(.npc_navbar / .npc_month_nav /
   .npc_hist_summary, z-index 4)이 부모를 뚫고 컨테이너 레벨에서 경쟁해 memo(1)를 이긴다.
   실측 elementFromPoint: y=8·30·60·90·120 과 y=714·764 에서 art=history 가 최상단이었다.
   ⚠ 캡처 도구(DOM→canvas 페인터)는 이걸 재현하지 못해 «깨끗한» 그림을 줬다 — 화면 판정에
     페인터를 근거로 쓰면 안 된다. 판정은 elementFromPoint 히트테스트로.
   ⇒ ① 메모를 충분히 높이 올리고 ② 같은 섹션의 다른 아티클은 아예 그리지 않는다(이중 방어). */
body[data-npc-memo] main#staticDoc article.npc_memo_page { z-index: 30; }
body[data-npc-memo] main#staticDoc section article:not(.npc_memo_page) { visibility: hidden; }
/* 버튼은 «추가 메모» 바로 아래에 붙인다 — 결제 완료·메모 수정 두 모드 공통(사용자 지시).
   🪤 기존 규칙이 `main#staticDoc article .npc_bottom_dock { margin-top: auto }` 라 id 를 갖는다 —
      id 없는 선택자로는 못 이긴다(실측: margin-top 이 224px 로 남아 버튼이 아래로 밀렸다). */
main#staticDoc article.npc_memo_page .npc_bottom_dock {
    position: static;
    margin-top: 0;
    padding-top: 4px;
    padding-bottom: 14px;
    background-image: none;
}

/* 안내 문구는 «결제 완료» 모드에만 — 수정 모드는 버튼이 하나뿐이라 설명이 필요 없다. */
body[data-npc-memo="edit"] main#staticDoc article.npc_memo_page .memo_dock_note { display: none; }

/* 결제 «완료» 직후엔 X 를 내보내지 않는다(사용자 지시) — 하단 버튼이 저장을 겸하므로
   그쪽으로만 나가게 한다. 메모 «수정» 모드에만 X 로 빠져나온다. */
body[data-npc-memo="paid"] main#staticDoc article.npc_memo_page .memo_close { display: none; }

/* 🪤 .npc_cta 는 height 66px 고정이라 두 줄 이상이면 라벨이 잘린다
   (실측: 「영수증 카톡으로 보내고 닫기」에서 «닫기»가 사라졌다). 높이를 열고 글자를 줄인다. */
main#staticDoc article.npc_memo_page .memo_actions > .npc_cta {
    height: auto;
    min-height: 66px;
    padding-top: 10px;
    padding-bottom: 10px;
}
main#staticDoc article.npc_memo_page .memo_actions > .memo_send {
    font-size: 19px;
    line-height: 1.3;
}

/* 우측 상단 닫기(X) — (구 회피 여백은 2026-08-28 해제, §25) */
main#staticDoc article.npc_memo_page .npc_navbar > .npc_navbar_title { padding-left: 10px; }

/* ══════════════════════════════════════════════════════════════
   47. 가로 모드 정돈 — 실기기 실측 기반 (2026-08-25, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   854×534(8인치 가로) 실측으로 잡은 4건. §22 가 세운 원칙(«세로에서 위에 있던 걸 왼쪽으로,
   키패드는 오른쪽»)은 그대로 두고, 그 뒤에 추가된 규칙들이 덮어버린 자리만 되돌린다.
   ⚠ 이 블록이 파일 «끝»에 있는 이유 = 소스 순서. §22 는 1183행인데 홈 메모 규칙이 1416행이라
      미디어쿼리 안이어도 나중 선언이 이긴다 — 그게 이번 홈 붕괴의 원인이었다. */
@media (orientation: landscape) {

    /* ── ① 홈 — 카드가 화면 밖으로 밀려나 «출석체크 버튼이 아예 안 보였다» ──
       실측: .home_memo 가 flex:0 0 auto + width:100%(=822px) 라 형제를 밀어냈고
       출석체크 카드가 x -186..-18 로 완전히 뷰포트 밖(article overflow:hidden 이라 잘림).
       고침: 줄바꿈을 허용해 «카드 한 줄 + 메모 아래 한 줄». 세로에서 남던 아래 여백도 메모가 쓴다. */
    main#staticDoc article.home .service_cards { flex-wrap: wrap; align-content: center; gap: 12px; }
    main#staticDoc article.home button.service_card { flex: 1 1 0; min-width: 0; min-height: min(30vh, 168px); }
    main#staticDoc article.home .home_memo {
        flex: 1 1 100%;
        min-height: clamp(84px, 16vh, 128px);
        font-size: clamp(22px, 3vw, 30px);
    }

    /* ── ② 출석·수납 — CTA 버튼이 하단 학원명과 겹쳤다(실측 509×16px) ──
       본문 열이 뷰포트 바닥(522)까지 내려오는데 브랜드 라인은 496 부터다. 그 높이를 비켜준다.
       🪤 padding 으로 하면 안 된다 — 이 요소는 box-sizing:content-box 라 패딩이 높이를 «늘려»
          564 까지 삐져나갔다(실측). margin 은 박스 모델과 무관하게 flex 배분에서 빠진다. */
    .attend_body, .paybill_body { margin-bottom: var(--npc-foot-h); }

    /* ── ③ 결제내역 합계 — 2열이라 마지막 행이 합계 아래 깔렸다(실측 405×48px) ──
       합계는 «떠 있는 바»가 설계 의도(사용자 지시 2026-08-21)라 그대로 두되,
       가로에선 가로폭이 남으므로 두 줄을 «나란히» 놓아 높이를 절반으로 줄인다. */
    main#staticDoc article .npc_hist_summary {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        gap: 4px 28px;
        padding: 10px 16px;
    }
    .npc_hist_summary .sum_line { flex: 1 1 240px; }
    /* 한 줄로 낮아진 만큼 합계를 «더 아래»로 붙여 마지막 행과의 잔여 겹침(9px)까지 없앤다.
       브랜드 라인(496~526) 위 6px 에 세운다 — 그 아래로는 내릴 수 없다. */
    main#staticDoc article.npc_hist_page > .npc_hist_summary { bottom: calc(var(--npc-foot-h) + 2px); }

    /* ── ④ 수납 입력 — 안내가 3줄로 깨지고 지움(×) 버튼이 번호 위에 겹쳤다 ──
       원인은 vw 기반 clamp 다. 가로에선 vw 가 «긴 변»이라 좁은 열에 큰 글자가 들어간다
       (안내 30px / 번호 44px 이 각각 261·229px 칸에). 좌열을 넓히고 칸에 맞는 크기로 내린다. */
    .attend_log_panel, .paybill_guide_panel { flex-basis: clamp(180px, 23vw, 250px); }
    main#staticDoc article .paybill_main_col { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
    main#staticDoc article .paybill_main_col > .npc_guide { font-size: clamp(20px, 2.6vw, 25px); }
    main#staticDoc article .paybill_main_col > .input_display {
        margin-inline: 0;
        min-height: 72px;
        padding: 0 14px;
        font-size: clamp(23px, 3.3vw, 31px);
    }
    main#staticDoc article .attend_main_col > .npc_guide { font-size: clamp(21px, 2.8vw, 27px); }
}

/* ── ⑤ 기기 결제내역(숨은 디버그 메뉴) — 다크에서 흰 판이 통째로 떴다 ──
   main.css 가 라이트 하드코딩(#fff·#666·#ddd…)이라 토큰이 안 닿는다. 표면·글자·경계만 뒤집는다.
   ⚠ 가로 문제는 아니고 이번 순회 캡처에서 같이 드러난 건이다(운영자만 보는 화면). */
body[data-dark-mode="1"] .npc_devhist { background-color: var(--npc-bg); color: var(--npc-ink); }
body[data-dark-mode="1"] .npc_devhist .dh_head { border-bottom-color: var(--npc-line); }
body[data-dark-mode="1"] .npc_devhist .dh_filters { border-bottom-color: var(--npc-line); }
body[data-dark-mode="1"] .npc_devhist .dh_filters :is(input[type="date"], .dh_search) {
    border-color: var(--npc-line-strong); background-color: var(--npc-surface); color: var(--npc-ink);
}
body[data-dark-mode="1"] .npc_devhist :is(.dh_guide, .dh_empty, .dh_dt, .dh_card) { color: var(--npc-ink-3); }
body[data-dark-mode="1"] .npc_devhist .dh_row { border-bottom-color: var(--npc-line); }
body[data-dark-mode="1"] .npc_devhist .dh_state { color: var(--npc-ink-3); border-color: var(--npc-line-strong); }
body[data-dark-mode="1"] .npc_devhist .dh_detail { background-color: var(--npc-surface); color: var(--npc-ink-2); }
/* ══════════════════════════════════════════════════════════════
   48. 가로 모드 2차 — 학원명 앱바 이동 · 출결 좌우 반분 (2026-08-25, 사용자 지시)
   ══════════════════════════════════════════════════════════════ */
@media (orientation: landscape) {

    /* ── ① 학원명을 «앱바 좌측(홈/메뉴 버튼 우측)»으로 — 하단을 통째로 비운다 ──
       왜 좌측인가(실측): 좌측 버튼은 네 화면 모두 x 16..60 으로 «고정»인데, 중앙 pill 은
       라벨에 따라 344~370 으로 흔들리고 출결 화면엔 우측 QR 버튼(x 728)이 있어 자리가 좁다.
       ⚠ 결제내역(.npc_navbar)은 제외 — 좌측을 제목이 56..682 로 이미 쓴다(실측).
       ⚠ 홈도 제외 — 대기화면이라 하단 브랜드 라인을 유지한다(사용자 지시). */
    body:not(:has(main#staticDoc > section#home[data-on-top="1"])) { --npc-foot-h: 0px; }

    body:not(:has(main#staticDoc > section#home[data-on-top="1"])) #npcBrandFoot {
        top: 0;
        bottom: auto;
        left: 72px;                  /* 좌측 아이콘 버튼(16..60) 우측 12px */
        right: auto;
        height: 68px;                /* 앱바 실측 0..68 — 네 화면 동일 */
        justify-content: flex-start;
        font-size: 21px;
        color: var(--npc-ink-2);
    }

    /* ── ② 홈 — 메모칸이 아래 빈 공간을 쓰도록 키운다(사용자 지시). 카드 높이는 그대로. ── */
    main#staticDoc article.home .home_memo { min-height: clamp(110px, 26vh, 168px); }

    /* ── ③ 출결 — 화면을 좌우 반으로(사용자 지시) ──
       좌: 안내 메시지 → 숫자 디스플레이 → 그 아래 출결 기록
       우: 키패드(남는 세로를 전부 먹음) → 출석 버튼
       .attend_main_col 은 display:contents 로 «껍데기만» 지우고 자식들이 직접 격자에 앉는다
       (마크업 무변경). */
    main#staticDoc article .attend_body {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-template-rows: auto auto minmax(0, 1fr) auto;
        column-gap: 18px;
        row-gap: 10px;
        align-items: stretch;
    }
    main#staticDoc article .attend_body > .attend_main_col { display: contents; }

    main#staticDoc article .attend_body .attend_head { grid-column: 1; grid-row: 1; align-self: end; }
    main#staticDoc article .attend_body .attend_dots { grid-column: 1; grid-row: 2; }
    /* 성공 표시는 «안내+숫자» 자리를 대신 차지한다(연속 입력 중 레이아웃 유지) */
    main#staticDoc article .attend_body .attend_done { grid-column: 1; grid-row: 1 / span 2; }
    main#staticDoc article .attend_body .attend_log_panel { grid-column: 1; grid-row: 3 / span 2; margin: 0; flex: 1 1 auto; }

    main#staticDoc article .attend_body .num_keypad {
        grid-column: 2;
        grid-row: 1 / span 3;
        align-content: stretch;
        grid-auto-rows: minmax(0, 1fr);
        padding: 0 var(--npc-pad-x) 0 0;
    }
    main#staticDoc article .attend_body .num_keypad > button { min-height: 0; }
    main#staticDoc article .attend_body .npc_bottom_dock {
        grid-column: 2;
        grid-row: 4;
        margin: 0;
        padding: 0 var(--npc-pad-x) 0 0;
    }
}
/* ══════════════════════════════════════════════════════════════
   49. 가로 모드 3차 — 수납 조회도 좌우 반분 (2026-08-25)
   ══════════════════════════════════════════════════════════════
   출결(§48③)과 «같은 골격»으로 맞춘다 — 두 화면은 마크업 구조가 쌍이고 §22 부터 함께 다뤄 왔다.
     좌: 안내 → 번호 입력칸 → 그 아래 조회 방법안내
     우: 키패드(남는 세로를 전부)
   이걸로 §47④ 의 두 증상이 «칸이 좁아서» 생긴 것이었음이 같이 해소된다:
     안내 3줄 깨짐(301px 칸에 25px 글자) · 지움(×) 버튼이 번호 위에 겹침(301px 칸에 28px 번호). */
@media (orientation: landscape) {
    main#staticDoc article .paybill_body {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-template-rows: auto auto minmax(0, 1fr);
        column-gap: 18px;
        row-gap: 10px;
        align-items: stretch;
    }
    main#staticDoc article .paybill_body > .paybill_main_col { display: contents; }

    main#staticDoc article .paybill_body .npc_guide { grid-column: 1; grid-row: 1; align-self: end; }
    main#staticDoc article .paybill_body .input_display { grid-column: 1; grid-row: 2; margin: 0; }
    main#staticDoc article .paybill_body .paybill_guide_panel { grid-column: 1; grid-row: 3; margin: 0; }

    main#staticDoc article .paybill_body .num_keypad {
        grid-column: 2;
        grid-row: 1 / span 3;
        align-content: stretch;
        grid-auto-rows: minmax(0, 1fr);
        padding: 0 var(--npc-pad-x) 0 0;
    }
    main#staticDoc article .paybill_body .num_keypad > button { min-height: 0; }
    /* 칸이 넓어졌으니 글자를 다시 키운다(§47④ 에서 좁은 칸에 맞춰 줄였던 값) */
    main#staticDoc article .paybill_body .npc_guide { font-size: clamp(23px, 3vw, 29px); }
    main#staticDoc article .paybill_body .input_display { font-size: clamp(26px, 3.8vw, 36px); }
}
/* ══════════════════════════════════════════════════════════════
   50. 가로 모드 4차 — 사용자 지시 5건 (2026-08-25)
   ══════════════════════════════════════════════════════════════ */

/* ── ⑤ 텍스트 선택 차단 (가로/세로 공통) ──
   🪤 「학원명 터치 후 상단 버튼이 전부 안 먹는다」의 원인. 앱 전역이 user-select:auto 라
      키오스크에서 글자를 누르면 안드로이드 웹뷰가 «텍스트 선택»을 시작하고, 선택 핸들 +
      플로팅 메뉴(복사/공유)가 상단을 덮어 탭을 삼킨다. 합성 이벤트로는 재현되지 않는다
      (네이티브 동작이라 JS 이벤트 경로가 아니다) — 실기기 증상으로만 잡힌다.
      입력 요소만 선택을 되살린다. */
body { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
input, textarea, [contenteditable="true"], [contenteditable=""],
main#staticDoc article.home .home_memo, main#staticDoc article.home .home_memo_input {
    -webkit-user-select: text; user-select: text;
}

@media (orientation: landscape) {

    /* ── ① 학원명 세로 중앙 — 실측: 버튼 중심 y=38 인데 학원명 중심이 34 였다(4px 위) ── */
    body:not(:has(main#staticDoc > section#home[data-on-top="1"])) #npcBrandFoot { top: 4px; }

    /* ── ② 제목이 좌측을 쓰는 화면에선 학원명을 «중앙»으로 (사용자 지시) ──
       결제내역·터미널 결제내역·터미널 메모가 해당. 실측으로 이들은 인스턴트 문서가 아니라
       현재 섹션 안에 스택된 article 이라, 판별자는 섹션이 아니라 «그 article 의 on-top». */
    /* ⚠ ① 의 :not(:has(#home)) 이 id 3개라 특이성이 높다 — 같은 가드를 달아야 이긴다(실측으로 잡음) */
    /* 🪤 data-on-top 은 «섹션마다» 자기 최상위 article 을 기억한다 — 비활성 섹션의 결제내역 article 도
       1 로 남아 있어서, 섹션 조건 없이 쓰면 «모든 화면»이 중앙이 된다(실기기 실측으로 잡음).
       반드시 «지금 활성인 섹션 안의» on-top article 인지까지 본다. */
    body:not(:has(main#staticDoc > section#home[data-on-top="1"])):has(main#staticDoc > section[data-on-top="1"] > .container > article.npc_hist_page[data-on-top="1"]) #npcBrandFoot,
    body:not(:has(main#staticDoc > section#home[data-on-top="1"])):has(main#staticDoc > section[data-on-top="1"] > .container > article.npc_memo_page[data-on-top="1"]) #npcBrandFoot {
        left: 0;
        right: 0;
        justify-content: center;
    }

    /* ── ③ 하단 학원명이 비워진 만큼 본문을 아래로 확장 (사용자 지시) ──
       실측: article 하단 패딩 66px 이 그대로 남아 본문이 468 에서 끝나고 있었다(뷰포트 534). */
    main#staticDoc article:is(.attend, .paybill, .terminal) { padding-bottom: 10px; }
    main#staticDoc article.npc_hist_page > .npc_hist_summary { bottom: 10px; }
    /* 목록이 길면 항목이 합계 «밑으로» 지나가는 게 설계 의도(2026-08-21)인데, 가로에선 배경이
       옅어 글자가 비쳐 겹친 것처럼 읽힌다. 떠 있는 바로 확실히 보이게 불투명도만 올린다. */
    main#staticDoc article .npc_hist_summary { background-color: rgb(from var(--npc-bg) r g b / 88%); }

    /* ── ④ N커넥트 터미널 — 좌 버튼 폭 축소 · 중앙 디스플레이 확대 ·
             초기화/발행요청을 «키패드 위»로 (사용자 지시) ──
       실측 종전: 좌 176 · 중앙 318 · 키패드 296. 좌를 줄인 만큼 중앙이 가져간다.
       .terminal_actions 는 .terminal_main 안에 있어서, main 을 display:contents 로 풀어
       디스플레이와 액션을 각각 다른 칸에 앉힌다(마크업 무변경). */
    main#staticDoc article.terminal .terminal_body {
        display: grid;
        grid-template-columns: clamp(104px, 13.5vw, 128px) minmax(0, 1fr) minmax(0, 300px);
        grid-template-rows: auto minmax(0, 1fr);
        column-gap: 16px;
        row-gap: 12px;
        align-items: stretch;
    }
    main#staticDoc article.terminal .terminal_body > .terminal_main { display: contents; }
    main#staticDoc article.terminal .terminal_body .terminal_side { grid-column: 1; grid-row: 1 / span 2; }
    main#staticDoc article.terminal .terminal_body .terminal_display { grid-column: 2; grid-row: 1 / span 2; }
    main#staticDoc article.terminal .terminal_body .terminal_actions { grid-column: 3; grid-row: 1; margin: 0; }
    main#staticDoc article.terminal .terminal_body .terminal_keypad { grid-column: 3; grid-row: 2; }
    /* 좁아진 좌측 버튼 — 글자를 줄이고 줄바꿈을 허용한다 */
    main#staticDoc article.terminal .terminal_side button { padding: 14px 6px; font-size: clamp(14px, 1.8vw, 17px); word-break: keep-all; }
}
/* ══════════════════════════════════════════════════════════════
   51. 가로 4차 교정 — 하단 확장이 «안 먹던» 진짜 이유 (2026-08-25)
   ══════════════════════════════════════════════════════════════
   §50③ 은 무시되고 있었다. 매칭 규칙을 브라우저에게 직접 물어보니 승자는 이것이었다:
     main.css → main#staticDoc > section#attend > .container > article
                { padding-bottom: calc(var(--root-menu-height) + env(safe-area-inset-bottom) + 8px) }  = 64px
   id 가 2개(§staticDoc + §attend)라 특이성 (2,1,3) 으로 내 (1,1,2) 를 이긴다.
   내용상으로도 죽은 예약이다 — 이 앱은 §2 에서 footer#fixedBottom(rootbar)을 display:none 으로
   껐으므로 그 높이만큼 비워 둘 이유가 없다. 같은 «모양»의 선택자로 맞받는다(뒤에 오므로 승리). */
@media (orientation: landscape) {
    main#staticDoc > section:is(#attend, #paybill, #terminal) > .container > article { padding-bottom: 10px; }
}
/* ══════════════════════════════════════════════════════════════
   52. 홈 메모 슬롯 — 버튼 «자리»만큼만 차지 (2026-08-25, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   .service_cards 안에는 성격이 다른 메모가 두 종류 있다.
     .home_memo[data-slot="attend"|"paybill"]  = 그 버튼을 «끈 자리»를 대신하는 메모
     .home_memo (슬롯 없음)                     = 버튼과 무관한 상시 메모(homeMemo3)
   §48② 가 둘을 구분하지 않고 flex:1 1 100% 를 줘서, 버튼 하나만 껐는데도 메모가
   한 줄을 통째로 먹고 남은 버튼이 아래로 밀렸다. 슬롯 메모는 «카드와 같은 몫»이어야 한다. */
@media (orientation: landscape) {
    main#staticDoc article.home .home_memo[data-slot] {
        flex: 1 1 0;
        min-width: 0;
        min-height: min(30vh, 168px);   /* 카드와 같은 높이 — 한 줄에서 키가 맞아야 한다 */
    }
    /* 상시 메모만 아래 줄 전체 폭 (§48② 의 의도는 이쪽이었다) */
    main#staticDoc article.home .home_memo:not([data-slot]) {
        flex: 1 1 100%;
        min-height: clamp(110px, 26vh, 168px);
    }
}


/* ══════════════════════════════════════════════════════════════
   53. 겹친 화면의 «터치 새어나감» 차단 (2026-08-25, 사용자 지적)
   ══════════════════════════════════════════════════════════════ */
/*
   같은 컨테이너의 article 들은 서로 «겹쳐» 쌓이고, 최상단만 z-index:1 을 받는다.
   그런데 뒤에 깔린 article 은 z-index:auto 라 스태킹 컨텍스트를 만들지 않는다 —
   그래서 그 안의 자식(.npc_title_row z:3, .npc_bottom_dock z:3~4)이 부모를 뚫고 나가
   위에 있는 article(z:1)보다 «위»에 그려지고, 터치까지 가져간다.

   실측(2026-08-25, 실기기): 청구서 선택 화면에서 화면 상단 y 68~134 밴드의 최상단 요소가
   «원생 선택 화면의 상단바»였다. elementFromPoint 로 재니 보이는 새로고침 버튼과
   실제 히트 노드가 서로 다른 article 소속(같은노드=false). 즉 사용자가 청구서 화면의
   새로고침을 눌러도 실제로는 «원생 선택 화면의 새로고침»이 눌렸고, 그쪽은 이미 최상단이
   아니라 close+bring 경로를 타서 원생 선택으로 되돌아갔다 —
   「새로고침하면 이전 화면으로 돌아간다」의 정체.

   isolation:isolate 는 article 마다 스태킹 컨텍스트를 만들어 자식 z-index 를 가둔다.
   내부 순서는 그대로라 상단바·하단 dock 의 겹침 의도는 유지된다.
   같은 계열 사고: .agent/_lessons/012_a-renderer-is-not-a-screen.md (그때는 «그림», 이번엔 «입력»)
*/
main#staticDoc > section > .container > article { isolation: isolate; }

/* ══════════════════════════════════════════════════════════════
   54. 기기 등록 대기 화면 (deviceWait) — PM/018 D6·D7 (2026-08-28)
   ══════════════════════════════════════════════════════════════
   section#login 과 같은 컨테이너 구조(root container + static main article).
   색은 전부 §1 토큰 참조 — body[data-dark-mode="1"] 가 이미 뒤집어주므로
   이 섹션 자체엔 다크 전용 블록이 필요 없다(§18 로그인 화면과 동일 관례). */
main#instantDoc > section#deviceWait { background-color: var(--npc-bg); }
main#instantDoc > section#deviceWait .npc_wait_wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100%;
    /* D28 승인: 가로 모드 오버라이드(max-width:560px + padding 재정의)가 padding 을
       폭에 더하지 않고 안쪽으로 먹게 고정 — border-box 미지정 시 가로 모드에서
       24px*2 만큼 컨텐츠 폭이 밀려나는 걸 방지 */
    box-sizing: border-box;
    /* 하단 「관리자 로그인」(position:absolute, bottom: bottom-pad+24, height 50) 자리를 미리 비워
       가운데 정렬된 내용(스피너·안내문)이 화면이 낮을 때 버튼과 겹치지 않게 한다(D23⑤).
       가로 모드는 버튼이 더 아래로 붙으므로(§ landscape) 그쪽에서 값을 줄인다. */
    padding: 0 24px calc(var(--bottom-pad) + 90px);
    text-align: center;
}
main#instantDoc > section#deviceWait .npc_wait_academy {
    max-width: 100%;
    margin-bottom: 6px;
    overflow: hidden;
    color: var(--npc-ink-2);
    font-size: 16px;
    font-weight: 700;
    text-overflow: ellipsis;
    white-space: nowrap;
    word-break: keep-all;
}
/* main.js 가 .text() 로 빈 문자열을 주입할 수 있다(매칭 학원명 0건) — 그때는 margin-bottom 만 남아
   제목과의 간격이 붕 떠 보이므로 아예 접는다(D16⑤). */
main#instantDoc > section#deviceWait .npc_wait_academy:empty { display: none; }
main#instantDoc > section#deviceWait .npc_wait_title {
    margin-bottom: 28px;
    color: var(--npc-ink);
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -0.04em;
    word-break: keep-all;
}
main#instantDoc > section#deviceWait .npc_wait_spinner_holder { display: flex; justify-content: center; margin-bottom: 28px; }
/* 로그인 진입 대기(#wait)는 --sp:150px 그대로, 여긴 안내문과 나란히 놓이는 화면이라 축소 */
main#instantDoc > section#deviceWait .npc_wait_spinner_holder .npc_spinner { --sp: 96px; }
main#instantDoc > section#deviceWait .npc_wait_guide {
    max-width: 320px;
    color: var(--npc-ink-3);
    font-size: 15px;
    line-height: 1.5;
    word-break: keep-all;
}
/* 하단 고정 «관리자 로그인» — npc_login_logo(§18)와 같은 자리 계산(article.constraint 가 이미
   position:absolute 라 이 article 이 곧 컨테이닝 블록, estreUi.css:459). 1차 CTA(오렌지 채움)가
   아니라 폴백 탈출구라 아웃라인만 — main.js 의 폴링 정지 후 login 페이지로 이동(D6). */
main#instantDoc > section#deviceWait .npc_wait_admin_login {
    position: absolute;
    left: 24px;
    right: 24px;
    bottom: calc(var(--bottom-pad) + 24px);
    min-height: 50px;
    border: 1px solid var(--npc-line-strong);
    border-radius: 16px;
    background-color: transparent;
    color: var(--npc-ink-2);
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
}
main#instantDoc > section#deviceWait .npc_wait_admin_login:active { background-color: var(--npc-bg-sunken); }
/* hidden 속성을 존중 — 위 규칙이 display 를 안 건드려 UA 의 display:none 이 그대로 서지만, 나중에 display 를 주더라도 숨김이 깨지지 않게 명시(§4.12) */
main#instantDoc > section#deviceWait .npc_wait_admin_login[hidden] { display: none; }

/* 가로 모드 — §22 관례(로그인 폭 제한과 동일 값) 그대로, 세로 여백이 좁아 스피너·버튼만 축소 */
@media (orientation: landscape) {
    /* 가로 모드는 버튼이 bottom-pad+14 로 더 붙는다(아래) — 예약분도 그만큼 줄인다(D23⑤). */
    main#instantDoc > section#deviceWait .npc_wait_wrap { max-width: 560px; margin: 0 auto; padding-bottom: calc(var(--bottom-pad) + 74px); }
    main#instantDoc > section#deviceWait .npc_wait_spinner_holder { margin-bottom: 18px; }
    main#instantDoc > section#deviceWait .npc_wait_spinner_holder .npc_spinner { --sp: 72px; }
    main#instantDoc > section#deviceWait .npc_wait_title { margin-bottom: 16px; font-size: 21px; }
    main#instantDoc > section#deviceWait .npc_wait_admin_login { bottom: calc(var(--bottom-pad) + 14px); }
}


/* ══════════════════════════════════════════════════════════════
   55. 원생 선택 — 미납 청구서 0건이면 «못 고르게» (2026-08-31, 사용자 지시)
   ══════════════════════════════════════════════════════════════
   왜 — 발행된 청구서를 전부 납부한 원생을 골라도 다음 화면(청구서 선택)이 빈 목록이라
   그대로 되돌아 나와야 했다. 애초에 못 고르게 하고 «이유»를 그 자리에 적어 준다.

   상태의 출처는 단 하나 — 서버가 학생마다 실어 보내는 미납 청구서 수다. mangoPosPM 이 그걸
   진짜 boolean(NpcAllPaid)으로 환산하고, EstreUI 배열 바인딩이 disabled 와 aria-disabled 를
   «함께» 건다. 그래서 안내 문구도 별도 조건 바인딩을 두지 않고 «비활성 상태»에서만 켠다 —
   조건이 두 갈래로 갈라져 서로 어긋나는 화면(비활성인데 이유가 없다 / 활성인데 완납이라 한다)이
   원천적으로 안 생기고, 서버가 개수를 안 주면 비활성도 문구도 함께 사라진다(실패 개방).

   ⚠ blocked_list 카드에는 원래 비활성 표현이 «하나도» 없었다 — 속성만 걸면 눌리지만 않을 뿐
      겉보기가 같아서 사용자에겐 「버튼이 고장났다」로 읽힌다. §13 CTA 의 비활성 표준
      (bg-sunken + ink-3 + cursor:default)을 카드로 옮겨 온 게 아래 규칙이다.
   ⚠ 색은 전부 토큰이다. 다크는 media query 가 아니라 body[data-dark-mode="1"] 로 갈리므로
      리터럴 색을 박으면 다크에 그대로 남는다(같은 계열 사고 2건 — §1 주석, npc README ②).
   ⚠ 특이성 — §9 의 카드 규칙(main#staticDoc article ul.blocked_list …)을 이겨야 해서
      article[data-article-id="students"] 로 한 단계 올렸다(파일 머리 12-14행 규약). */

/* 활성 행의 가운데 정렬 — 원래 index.html 의 인라인 style="justify-content:center" 였다.
   비활성일 때만 좌/우로 가르려면 스타일시트가 이겨야 하는데 인라인은 못 이긴다 → 여기로 옮겼다.
   (§9 의 .item_subject 기본값이 space-between 이라 옮기지 않으면 활성 행이 좌측 정렬로 바뀐다) */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li .item_subject { justify-content: center; }

/* 안내 문구는 기본 숨김 — 아래 비활성 규칙에서만 켜진다.
   (숨어 있는 동안엔 버튼의 접근 가능한 이름에도 안 들어가므로 활성 행의 낭독이 안 길어진다) */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li .npc_student_allpaid { display: none; }

/* ── 비활성(= 발행된 청구서를 전부 납부) 상태 ──
   aria-disabled 를 함께 보는 이유: 표시 수단이 나중에 바뀌어도(네이티브 disabled 를 떼고
   aria 로만 표현하는 등) 겉모습·클릭 차단이 갈라지지 않게 하려는 것이다. */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]) {
    background-color: var(--npc-bg-sunken);
    border-color: var(--npc-line);
    box-shadow: none;
    color: var(--npc-ink-2);
    cursor: default;
}
/* 눌렀을 때 «눌린 것처럼» 오렌지로 번쩍이면 사용자를 속인다 — §9 의 :active 하이라이트를
   이 상태에서만 무력화한다. (네이티브 disabled 면 브라우저가 :active 를 안 걸어 주지만,
   aria-disabled 만 걸린 경로까지 덮어 두려는 것) */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]):active {
    background-color: var(--npc-bg-sunken);
    border-color: var(--npc-line);
}
/* .item_subject 가 color 를 직접 들고 있어(§9) 버튼의 회색이 안 내려온다 → 여기서 다시 준다.
   이름은 «계속 읽혀야» 하므로 문구와 같은 ink-3 까지 내리지 않고 ink-2 에서 멈춘다.
   정렬은 가운데 → 좌/우(space-between)로 바꿔 이름 좌 · 안내 문구 우가 되게 한다. */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]) .item_subject {
    justify-content: space-between;
    flex-wrap: wrap;
    color: var(--npc-ink-2);
}
/* 좁은 카드 대비 — 가로 모드는 목록이 2열 그리드라 카드 폭이 절반이다(§22). 이름(clamp 26\~33px)과
   21자 문구가 한 줄에 안 들어가므로 위의 flex-wrap 이 문구를 아랫줄로 접고, 이름 «혼자»도 넘치는
   극단(아주 긴 이름)에서만 말줄임으로 끊는다. min-width:0 이 없으면 flex 항목이 min-content 아래로
   안 줄어들어 말줄임이 아예 안 걸린다. */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]) .npc_student_name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* 안내 문구 — «약간 작고 살짝 연하게»(이름은 clamp 26\~33px, 여긴 17\~20px + ink-3).
   margin-left:auto 는 아랫줄로 접혔을 때도 우측에 붙어 있게 하는 장치다(space-between 은 한 줄에
   항목이 하나뿐이면 좌측으로 붙인다). word-break:keep-all 로 어절 중간이 아니라 띄어쓰기에서 접는다. */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]) .npc_student_allpaid {
    display: block;
    margin-left: auto;
    /* 🔴 ink-3 이 아니라 ink-2 다. 라이트에서 ink-3(#938878)이 이 카드 배경(bg-sunken #F3E8DA) 위에서는
       2.878:1 까지 떨어진다(실측) — 19px·weight 500 은 WCAG large text 가 아니라 4.5:1 이 요구치다.
       흰 surface 위의 다른 ink-3 관례(3.478:1)를 근거로 「같은 관례」라고 볼 수 없다. 비활성 카드는
       배경까지 함께 어두워지기 때문이다. ink-2 는 라이트 5.954:1 / 다크 9.67:1 로 양쪽 다 통과한다.
       이 문구는 «왜 못 고르는지»를 알려 주는 유일한 단서라, 안 읽히면 그냥 고장난 버튼이 된다.
       위계는 크기(19 vs 32px)와 굵기(500 vs 700)만으로도 충분히 선다. */
    color: var(--npc-ink-2);
    /* 한 단계 낮춤(2026-09-01 사용자 지시) — 20px 에서는 이름과 한 줄에 못 들어가 아랫줄로 접히고
       비활성 카드만 높이가 튀어(실측 83px → 138px) 목록에서 «고를 수 없는 행»이 가장 커 보였다. */
    font-size: clamp(15px, 3.1vw, 17px);
    font-weight: 500;
    letter-spacing: -0.02em;
    text-align: right;
    word-break: keep-all;
}

/* ══════════════════════════════════════════════════════════════
   56. 수강료 납부 — 학생 선택 이후 제목 앞 「ㅇㅇㅇ 학생 |」 (2026-09-02 사용자 지시)
   ══════════════════════════════════════════════════════════════
   왜 값이 «속성»이고 글자를 CSS 가 그리나 —
     ① EstreUI 의 텍스트 지시자(data-bind / data-bind-object-array-item)는 전부 jQuery.html() 로 끝난다.
        제목 문자열 자체에 이름을 «덧붙이는» 방식이면 제자리 갱신(refreshPageInPlace)마다
        「홍길동 학생 | 홍길동 학생 | …」로 접두사가 누적된다. 속성 대입(data-bind-attr → jQuery.attr)은 늘 치환이라 누적이 불가능하다.
     ② 이름이 비면 접두사가 «통째로» 사라져야 한다 — 결제 복귀는 새 웹뷰라 표시명을 서버에서 다시 받는데(#resumeIdentity),
        실패하면 빈 문자열이 내려온다. 그때 「 | 청구서를 …」이 남으면 안 된다. 속성 선택자면 그 판정이 CSS 안에서 끝난다.
        (data-show-on-exists 는 «숨기기»만 하고 되살리는 else 가 없어 제자리 갱신에서 한 번 숨으면 안 돌아온다 — 쓰지 않는다.)
   같은 수법의 선례 = §9·§46 의 빈 목록 안내 content: attr(data-placeholder). */
main#staticDoc article .npc_title_row > .npc_title > .npc_title_who { display: none; }
main#staticDoc article .npc_title_row > .npc_title > .npc_title_who[data-who]:not([data-who=""]) {
    display: inline;
    margin-right: 0.3em;
}
/* 이름 — 같은 제목의 <b> 강조(§4)와 같은 무게로 */
main#staticDoc article .npc_title_row > .npc_title > .npc_title_who::before {
    content: attr(data-who);
    color: var(--npc-ink);
    font-weight: 700;
}
/* 꼬리 — 「학생 |」은 본문 톤으로 한 단계 낮춰 이름이 먼저 읽히게 */
main#staticDoc article .npc_title_row > .npc_title > .npc_title_who::after {
    content: " 학생 |";
    color: var(--npc-ink-2);
    font-weight: 600;
}
/* 긴 이름이 어절 중간에서 끊기지 않게 — §55 안내 문구와 같은 관례 */
main#staticDoc article .npc_title_row > .npc_title { word-break: keep-all; }


/* ══════════════════════════════════════════════════════════════
   57. 자동으로 닫히는 확인 팝업 (NpcFlow ①②) — 2026-09-04 사용자 요구
   ══════════════════════════════════════════════════════════════
   ① 결제완료 후 「결제 가능한 청구서가 있습니다.\n계속 결제하시겠어요?」 (15초)
   ② 무조작 40초 후 「수강료 납부를 계속하시겠어요?」 (20초)
   두 문구 모두 사용자 요구 «원문 그대로»다 — 버튼 낱말도 그만하기/계속하기 · 중단하기/계속하기로 갈린다. */

/* 요구 문구가 «줄바꿈이 든 두 문장»이라 슬롯을 쪼개지 않고 원문을 그대로 살린다.
   제목은 textContent 주입이라(주입 차단이 그 이유) \n 이 그냥은 접힌다 — 여기서 살린다.
   현금영수증 취소 confirm 의 제목에는 \n 이 없으므로 그쪽 표현은 바뀌지 않는다. */
#npcConfirmPop .npc_result_title { white-space: pre-line; }

/* 확정 버튼 톤 — §44 는 이 팝업이 «되돌릴 수 없는 요청» 전용이라는 전제로 확정 버튼을 위험색(빨강)으로 뒀다.
   NpcFlow 의 두 질문은 파괴적이지 않다 — 「계속하기」가 빨강이면 «누르면 뭔가 없어진다»로 읽힌다.
   톤은 NpcConfirm.show 가 팝업 루트에 data-tone 으로 심고(아이콘 색과 같은 축), 기본값 cancel 은 종전 그대로다. */
#npcConfirmPop:not([data-tone="cancel"]) .npc_result_actions > button.npc_confirm_yes {
    background-color: var(--npc-accent);
    color: var(--npc-ink-on-accent);
}

/* 남은 초 — 무인 단말에서 «말없이 사라지는 팝업»은 버그로 읽힌다.
   결제 결과 화면의 「n초 뒤에 처음 화면으로 돌아가요」와 같은 위계로 낮게 깐다. */
.npc_confirm_count {
    margin: 12px 0 0;
    color: var(--npc-ink-3);
    font-size: 15px;
    font-weight: 600;
    line-height: 1.4;
    letter-spacing: -0.01em;
    word-break: keep-all;
}


/* ══════════════════════════════════════════════════════════════
   58. 결제 총액 게이트 — 차단 사유 표시 + 결제 대상 아닌 항목 (요구 2026-09-04)
   ══════════════════════════════════════════════════════════════ */

/* 「결제하면 남는 미납액이 마이너스」일 때 도크에 사유를 세운다.
   ⚠ 버튼은 disabled 라 click 이 오지 않는다 — 탭해서 배우는 길이 없으므로 «상시 인라인»이어야 한다. */
main#staticDoc article .npc_pay_note {
    display: none;
    padding: 12px 14px;
    border: 1px solid var(--npc-danger);
    border-radius: 16px;
    background-color: rgb(from var(--npc-danger) r g b / 10%);
    color: var(--npc-danger);
    font-size: clamp(15px, 3.4vw, 19px);
    font-weight: 700;
    line-height: 1.35;
    text-align: center;
    word-break: keep-all;
}
main#staticDoc article .npc_pay_note.on { display: block; }

/* 결제 대상이 아닌 항목(할인·완납) — 편집칸을 감추고 그 자리에 «왜 못 고르는지»를 쓴다 */
main#staticDoc article .npc_pay_amount.npc_not_payable > input { display: none; }
main#staticDoc article .npc_pay_amount.npc_not_payable > .cap {
    padding: 6px 14px;
    border: 1px dashed var(--npc-line-strong);
    border-radius: 999px;
    color: var(--npc-ink-3);
    font-weight: 700;
}
/* 체크 링 — 고를 수 없다는 걸 «비어 있는 점선»으로 (숨기면 왜 없는지 알 수 없다) */
main#staticDoc article .npc_check.npc_check_off { opacity: 0.34; }
main#staticDoc article .npc_check.npc_check_off > i { border-style: dashed; }
/* 할인 줄은 «미납»이 아니다 — 빨간 미납색을 쓰면 청구가 남은 것처럼 읽힌다 */
main#staticDoc article .npc_item_amounts .amt_line.npc_discount_line .remain { color: var(--npc-accent); }


/* ══════════════════════════════════════════════════════════════
   59. 할인 항목 = «잠긴 채 켜져 있다» (사용자 요구 2026-09-04)
   ══════════════════════════════════════════════════════════════ */

/* 할인은 항상 선택돼 합계에 자동으로 들어간다 — 해제할 수 없다는 걸 «채워진 채 흐린 링»으로 보인다.
   ⚠ 완납의 npc_check_off(빈 점선)와 달라야 한다. 「고를 수 없다」와 「끌 수 없다」는 다른 말이다. */
main#staticDoc article .npc_check.npc_check_locked > i { opacity: 0.55; border-style: dashed; }

/* 잠긴 금액칸 — 값을 «보여주되» 못 고친다(readOnly). 눌러 보기 전에 잠겼다는 걸 알 수 있어야 한다.
   ⚠ 완납·미납없음은 §58 이 input 을 display:none 으로 감추므로 이 규칙이 보이지 않는다 — 할인 전용에 가깝다. */
main#staticDoc article .npc_pay_amount.npc_amount_locked > input {
    border-style: dashed;
    background-color: transparent;
    color: var(--npc-ink-3);
    cursor: default;
}
main#staticDoc article .npc_pay_amount.npc_amount_locked > input:focus { border-color: var(--npc-line-strong); }

/* 할인칸은 «빼는 돈»이다 — 미납 빨강도 회색도 아닌 accent 로, 합계에 그대로 들어가는 수임을 보인다.
   ⚠ npc_amount_locked 규칙보다 «뒤»에 와야 이긴다(같은 특정도). */
main#staticDoc article .npc_pay_amount.npc_discount_amount > input { border-color: var(--npc-accent); color: var(--npc-accent); }
main#staticDoc article .npc_pay_amount.npc_discount_amount > .cap { color: var(--npc-accent); font-weight: 800; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   §59 관리자 UI 숨김 — 학원웹 «N커넥트설정» 탭(PM/024, 2026-09-14)
   NpcTheme 이 body.dataset 에 data-hide-* 를 «1/0» 으로 비추고(마스터 adminHideAll 이 켜지면 전부 1),
   여기서는 «요소+속성» 규칙만 둔다(선례 §? body[data-svc-*] :1413-1420). 앵커는 index.html 의 npc_admin_* 클래스.
   마스터 예외(사용자 결정 §4-3): 메뉴 «업데이트» 항목과 메뉴 호출(햄버거) 버튼은 남긴다 — 그래서 마스터용 규칙을
   따로 두지 않고 NpcTheme 가 개별 키를 전부 1 로 비추는 것으로 끝낸다(업데이트 li 엔 앵커가 없다). 로그아웃은 숨긴다.
   ⚠ 통계는 «결제내역 확인» 페이지만(section#paymentHistory) — 터미널 결제내역(.th_summary)은 대상이 아니다(§4-6). */
body[data-hide-pay-history="1"] nav#mainMenu li.npc_admin_pay_history { display: none; }
body[data-hide-attend-log="1"] nav#mainMenu li.npc_admin_attend_log { display: none; }
body[data-hide-svc-attend-toggle="1"] nav#mainMenu .npc_menu_row.npc_admin_svc_attend_toggle { display: none; }
body[data-hide-svc-paybill-toggle="1"] nav#mainMenu .npc_menu_row.npc_admin_svc_paybill_toggle { display: none; }
body[data-hide-attend-log-toggle="1"] nav#mainMenu .npc_menu_row.npc_admin_attend_log_toggle { display: none; }
body[data-hide-dark-toggle="1"] nav#mainMenu .npc_menu_row.npc_admin_dark_toggle { display: none; }
body[data-hide-hist-summary="1"] main#staticDoc section#paymentHistory .npc_hist_summary { display: none; }
body[data-hide-terminal="1"] .head_center > .nav_pill.npc_admin_terminal,
body[data-hide-terminal="1"] #appbarRight > #terminalBtn.npc_admin_terminal { display: none; }
/* [2차 피드백 2026-09-14] «터미널 결제내역 숨기기»(버튼) → «터미널 결제내역 통계 표시»(통계 숨김)로 기능 변경 — 결제내역 확인 페이지의 통계 규칙과 짝.
   구 규칙 보존(주석): body[data-hide-terminal-history="1"] main#staticDoc article.terminal .terminal_side > #terminalHistoryBtn.npc_admin_terminal_history { display: none; } */
body[data-hide-terminal-hist-summary="1"] main#staticDoc article.npc_hist_page > .npc_hist_summary.th_summary { display: none; }   /* 결제내역 article 은 비정적(열 때 생성) — 위치가 아니라 클래스로 짚는다 */
/* 로그아웃은 마스터에서만 숨긴다(개별 행이 없다 — 사용자 결정 §4-3) */
body[data-admin-hide-all="1"] nav#mainMenu .npc_menu_foot > .sign_out.npc_admin_sign_out { display: none; }

/* ══════════════════════════════════════════════════════════════
   §60 홈 메모 «최종 표시» — 학원웹 메모 편집·표시 토글(PM/024 2차, 2026-09-14)
   ══════════════════════════════════════════════════════════════
   NpcTheme.#apply 가 칸마다 data-memo1|2|3 = 켜짐 ∧ 내용 있음(또는 편집 중) ∧ (슬롯 메모는 그 버튼이 꺼져 있을 때) 로 비춘다.
   종전 규칙(§1413-1416 슬롯 교대 · §1469 body:not([data-memo-on]) 전체 숨김)은 그대로 두고, 여기서 «같거나 높은 특이성의 뒤 규칙»으로 최종 판정만 덮는다:
   비어 있는 메모는 켜져 있어도 칸이 안 나온다(placeholder 도 없다 — 사용자 지시). 편집은 학원웹(N커넥트설정 탭 미리보기)이 정본이고 기기 편집은 내용이 있는 칸에서만. */
main#staticDoc article.home .home_memo[data-slot="attend"], main#staticDoc article.home .home_memo[data-slot="paybill"], main#staticDoc article.home .home_memo:not([data-slot]) { display: none; }
body[data-memo1="1"] main#staticDoc article.home .home_memo[data-slot="attend"] { display: flex; }
body[data-memo2="1"] main#staticDoc article.home .home_memo[data-slot="paybill"] { display: flex; }
body[data-memo3="1"] main#staticDoc article.home .home_memo:not([data-slot]) { display: flex; }
main#staticDoc article.home .home_memo_input:empty::before { content: none; }
/* [3차] 잠긴 메모 — 편집 커서·선택 없음(웹 전용 키 homeMemoNLock, 기본 잠금) */
main#staticDoc article.home .home_memo_input[contenteditable="false"] { cursor: default; user-select: none; -webkit-user-select: none; }

/* ══════════════════════════════════════════════════════════════
   §61 홈 카드 «자리 보존» — 버튼을 숨겨도 다음 카드가 그 자리로 올라오지 않는다 (PM/024 6차, 사용자 지시 2026-09-15)
   ══════════════════════════════════════════════════════════════
   §1413-1416 슬롯 교대는 버튼을 display:none 으로 빼서 «수강료 납부»가 «출석체크» 자리로 당겨졌다(세로: 위로, 가로: 왼쪽으로).
   슬롯 메모(§60 data-memoN)가 그 자리를 채울 때만 버튼을 빼고, 채우지 않을 때는 버튼을 «보이지 않는 채로 자리만» 남긴다.
   높이·폭은 §1255/§2379 의 카드 규칙을 그대로 받으므로 빈 슬롯도 다른 카드와 같은 크기다. */
body[data-svc-attend="0"]:not([data-memo1="1"]) main#staticDoc article.home button.service_card[data-svc="attend"] { display: flex; visibility: hidden; pointer-events: none; }
body[data-svc-paybill="0"]:not([data-memo2="1"]) main#staticDoc article.home button.service_card[data-svc="paybill"] { display: flex; visibility: hidden; pointer-events: none; }

/* ══════════════════════════════════════════════════════════════════════════
   62. 출결 기록 — «이미 처리»(2000) 거절 줄 (2026-09-15 사용자 지시)
   정상 줄은 «이름 | 구분 알약 | 시각» 3열이고, 거절 줄은 «문장 | 시각» 2열이다.
   좌측 패널(.attend_log_list)과 메뉴 › 출석목록 팝업(.npc_attend_page_list)이 같은 li 를 쓰므로 둘 다 건다.
   ══════════════════════════════════════════════════════════════════════════ */
.attend_log_list > li.log_notice > .log_msg,
.npc_attend_page_list > li.log_notice > .log_msg {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--npc-ink-3); font-weight: 500;
}
.attend_log_list > li.log_notice > .log_msg > .log_msg_name,
.npc_attend_page_list > li.log_notice > .log_msg > .log_msg_name {
    color: var(--npc-ink); font-weight: 700;
}
/* 시각은 정상 줄과 같은 자리 — 팝업 쪽은 margin-left:auto 규칙이 이미 있어 그대로 맞는다 */
.attend_log_list > li.log_notice > .log_time { flex-shrink: 0; }

/* ══════════════════════════════════════════════════════════════════════════
   63. 가로 모드 목록 카드 재배치 (2026-09-15 사용자 지시)

   왜: 854×534 가로에서 §22 의 2열 격자는 «좌우 분할»로 보이고, 카드마다 내용이 2~4줄로 쌓여
       한 화면에 서너 건밖에 안 들어왔다. 가로는 «폭»이 남고 «높이»가 모자란 축이므로,
       카드를 전폭 1장으로 눕혀 내용을 가로로 펴면 같은 높이에 훨씬 많이 담긴다.

   범위: 이 절은 전부 @media (orientation: landscape) 안이다 — 세로 레이아웃은 한 줄도 건드리지 않는다.
        DOM 템플릿(index.html)과 데이터 바인딩은 무변경이라 estreUi 배열 바인더·npcUi.syncItemRows 와 무관하다.
   ══════════════════════════════════════════════════════════════════════════ */
@media (orientation: landscape) {

    /* ── ① 청구서 목록 — 전폭 1줄 ─────────────────────────────────────────
       「2026. 09  9월 수강료 외 1건 ……… 납부 0원 | 미납 450,000원」 */
    main#staticDoc article ul.blocked_list[data-bind-array="billList"] { grid-template-columns: 1fr; gap: 10px; }
    main#staticDoc article ul.blocked_list[data-bind-array="billList"] > li > button {
        flex-flow: row nowrap; align-items: center; gap: 18px; padding: 14px 22px;
    }
    /* 제목 묶음은 줄바꿈 없이 왼쪽에, 남는 폭은 이 블록이 먹는다(긴 제목은 말줄임) */
    main#staticDoc article ul.blocked_list[data-bind-array="billList"] .npc_bill_head {
        flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; margin: 0; gap: 12px;
    }
    main#staticDoc article ul.blocked_list[data-bind-array="billList"] .npc_bill_title {
        min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    /* 금액은 오른쪽 끝에 한 줄로 — margin-top 은 세로 배치용이라 가로에선 0 */
    main#staticDoc article ul.blocked_list[data-bind-array="billList"] .npc_bill_amounts {
        flex: 0 0 auto; margin: 0; flex-wrap: nowrap; white-space: nowrap;
    }

    /* ── ② 청구 항목 내역 — 전폭 1줄 · 체크박스 최우측 ────────────────────
       「9월 수강료 ……… 납부 0원   미납 380,000원   결제[380,000]   (✓)」
       🔑 체크는 마크업상 .npc_item_head 안(이름 옆)에 있다. DOM 을 건드리지 않고 «행 전체의 마지막»으로
          보내려고 head 를 display:contents 로 풀어 자식(이름·체크)을 행의 직접 항목으로 만들고 order 로 세운다.
          (head 는 레이아웃 전용이라 배경·테두리가 없어 contents 로 풀어도 잃는 표현이 없다) */
    main#staticDoc article ul.blocked_list[data-bind-array="detailItems"] { grid-template-columns: 1fr; gap: 10px; }
    main#staticDoc article ul.blocked_list[data-bind-array="detailItems"] > li > div { padding: 12px 20px; }
    main#staticDoc article .npc_item_row {
        flex-direction: row; align-items: center; flex-wrap: nowrap; gap: 18px;
    }
    main#staticDoc article .npc_item_head { display: contents; }
    main#staticDoc article .npc_item_head > .npc_item_name {
        order: 1; flex: 1 1 auto; min-width: 0;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    main#staticDoc article .npc_item_amounts {
        order: 2; flex: 0 0 auto; flex-direction: row; align-items: center; gap: 18px;
    }
    /* 납부·미납 두 줄을 나란히 — 미납줄의 결제 입력칸은 margin-left:auto 를 풀어 미납액 바로 옆에 붙인다 */
    main#staticDoc article .npc_item_amounts .amt_line { white-space: nowrap; }
    main#staticDoc article .npc_item_amounts .remain_line { display: flex; align-items: center; gap: 12px; }
    main#staticDoc article .npc_item_amounts .remain_line > .npc_pay_amount { margin-left: 0; }
    main#staticDoc article .npc_item_head > .npc_check { order: 3; margin-left: 4px; }

    /* ── ③④ 결제내역 · 터미널 결제내역 — 전폭 카드 + 세로 압축 ──────────
       두 목록은 클래스가 완전 공용(.npc_hist_list > li > .npc_hist_row)이라 한 벌로 둘 다 걸린다
       = 사용자 지시 「양쪽 카드 구성은 비슷하게」가 구조적으로 보장된다.
       접힌 카드: 「금액 [뱃지]  월·카드사·승인번호  [학생명 · 전화]  ……… 날짜」 한 줄(넘치면 두 줄) */
    main#staticDoc article ul.npc_hist_list { grid-template-columns: 1fr; gap: 8px; }
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row {
        display: flex; flex-flow: row wrap; align-items: center;
        column-gap: 16px; row-gap: 4px; padding: 10px 16px;
    }
    /* 🔑 둘째 줄 강제 — 메모(학생명·전화 / 터미널 메모)를 «수납일시와 같은 줄»에 두라는 지시(2026-09-15)라
       윗줄(금액·뱃지·메타)과 아랫줄(메모·일시)을 가르는 «폭 100% 높이 0» 칸을 order 로 끼워 넣는다.
       (DOM 을 늘리지 않고 flex-wrap 의 줄바꿈을 만드는 방법 — .npc_hist_row 에 다른 의사요소는 없다) */
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row::before {
        content: ""; order: 3; flex: 0 0 100%; height: 0; margin: 0;
    }
    /* 날짜는 그 둘째 줄의 «오른쪽 끝» — 메모가 왼쪽, 일시가 오른쪽 */
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row > .npc_hist_when {
        order: 5; margin: 0 0 0 auto; flex: 0 0 auto; text-align: right;
    }
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row > .npc_hist_top {
        order: 1; flex: 0 0 auto; gap: 8px;
    }
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row > .npc_hist_meta {
        order: 2; margin: 0; flex: 0 1 auto; flex-wrap: nowrap; white-space: nowrap;
    }
    /* 메모 = 둘째 줄 «왼쪽 정렬» (사용자 지시 2026-09-15) — 결제내역의 「학생명 · 전화번호」와
       터미널 결제내역의 메모가 같은 클래스라 한 벌로 둘 다 걸린다 */
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row > .npc_hist_memo {
        order: 4; margin: 0; flex: 0 1 auto; min-width: 0; margin-right: auto;
        padding: 4px 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    /* 확장 영역은 언제나 «새 줄 전폭» — 좌측 승인/취소, 우측 버튼 한 줄 (사용자 지시) */
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row > .npc_hist_lines {
        order: 9; flex: 1 1 100%; margin-top: 8px; padding-top: 8px;
    }
    main#staticDoc article ul.npc_hist_list > li > .npc_hist_row.expanded > .npc_hist_lines {
        display: flex; flex-flow: row wrap; align-items: center; column-gap: 20px; row-gap: 6px;
    }
    main#staticDoc article ul.npc_hist_list .npc_hist_line { flex: 0 1 auto; padding: 0; }
    main#staticDoc article ul.npc_hist_list .npc_hist_line > .no { margin-left: 8px; }
    main#staticDoc article ul.npc_hist_list .npc_hist_actions {
        margin: 0 0 0 auto; flex: 0 0 auto; gap: 8px;
    }
    main#staticDoc article ul.npc_hist_list .npc_hist_actions > button {
        flex: 0 0 auto; min-width: 128px; min-height: 40px;
    }
}

/* ══════════════════════════════════════════════════════════════
   64. 가로 모드 «원생 선택» 목록 — 한 줄 한 장 · 가운데 정렬 (사용자 지시 2026-09-16, PM/024 §4.11)
   ══════════════════════════════════════════════════════════════ */
/* §22 가 가로에서 blocked_list 를 2열 격자로 가른다(한 화면에 더 담기게). 원생 선택만 예외로 되돌린다 —
   이름 하나뿐인 카드라 반 폭이면 「둘 중 하나를 고르는 표」처럼 읽히고, 완납 안내가 아랫줄로 접힌다(§55 주석의 실측).
   ① 격자 열을 1열로(격자 자체는 유지 — §22 의 gap·align-content 그대로) ② 이름·안내 문구를 가운데로.
   ⚠ 비활성(완납) 카드는 §55 가 space-between(이름 좌 · 안내 우)이다 — 가로에선 그것도 가운데로 모은다.
      §55 와 «같은 선택자»를 미디어 블록 안 «뒤»에 두어 순서로 이긴다(특이성 동률). 안내의 margin-left:auto 도 0 으로 —
      auto 가 남으면 가운데 정렬이어도 안내 문구가 오른쪽 끝으로 밀린다.
   ⚠ 세로는 손대지 않는다(요구가 «가로모드일 때 한정») — §55 의 세로 모습(이름 좌·안내 우)은 그대로다.
   판정(로컬 헤드리스 854x534 실측, r11/probe-students.js): 카드 폭 = 목록 폭, 이름 중심 = 카드 중심(오프셋 0).

   🔴 11차-b(2026-09-16, 사용자 「52번 기기 가로에서 가운데가 아니다」 → 실기기 DOM 실측): 위 justify-content:center 는 기기에서
   «헛돌고» 있었다. 카드 버튼이 main.css(`article ul.blocked_list > li > :is(div, button)`)로 display:flex; flex-direction:column 인데
   Android WebView(Chrome 130)의 UA 스타일시트가 button 에 align-items:flex-start 를 주어 .item_subject 가 «내용 폭»(182px)으로
   줄어 왼쪽에 붙었다 — 그 안에서 가운데 정렬해 봐야 182px 안의 가운데다. 로컬 Chrome(132) 은 같은 DOM 이 전폭(465px)이라
   가운데로 «보였고», 그 차이가 판정을 가렸다(UA 기본값은 관측 대상이지 전제가 아니다). 교정 = 교차축을 명시:
   버튼 align-items:stretch + .item_subject align-self:stretch(전폭). 방향 무관하게 건다 — §55 가 문서화한 의도(활성 행 가운데,
   완납 행 좌/우)가 세로에서도 같은 이유로 안 서 있었다(기기 실측 subjW 182 → 465, 이름 중심 오프셋 -141 → 0). */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button { align-items: stretch; }
main#staticDoc article[data-article-id="students"] ul.blocked_list > li .item_subject { align-self: stretch; width: 100%; box-sizing: border-box; }
/* 11차-d(2026-09-16, 사용자 지적 「아랫줄이 기존 줄 높이를 그대로 가져간다」): main.css 가 .item_subject 에 line-height:1.4em 을 주는데
   em 단위 line-height 는 «계산된 px»(33px 글자 → 46.2px)로 상속된다. 그래서 17px 안내 문구도 46px 줄 상자를 갖고 있었다
   (기기 실측 noticeH 46). 단위 없는 값으로 자기 글자 기준으로 되돌린다 — 세로에서도 긴 이름이 접혀 안내가 제 줄로 내려올 때 같은 문제라 방향 무관. */
main#staticDoc article[data-article-id="students"] ul.blocked_list > li .npc_student_allpaid { line-height: 1.3; }
@media (orientation: landscape) {
    main#staticDoc article[data-article-id="students"] ul.blocked_list { grid-template-columns: minmax(0, 1fr); }
    main#staticDoc article[data-article-id="students"] ul.blocked_list > li .item_subject { justify-content: center; text-align: center; }
    /* 완납 카드(11차-c, 사용자 지시 2026-09-16): 안내 문구는 이름 «아랫줄» 가운데 — 한 줄에 나란히 두면 전폭 카드에서 이름과 문구가
       멀찍이 떨어져 한 덩어리로 안 읽힌다. 열 방향 flex 로 세로 쌓고 교차축 가운데. 안내의 margin-left:auto(§55) 는 열 방향에선
       «오른쪽으로 밀기»가 되므로 0 으로. 세로(§55 좌/우 한 줄)는 그대로. */
    main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]) .item_subject { flex-direction: column; align-items: center; justify-content: center; gap: 4px; }
    main#staticDoc article[data-article-id="students"] ul.blocked_list > li > button:is(:disabled, [aria-disabled="true"]) .npc_student_allpaid { margin-left: 0; }
}
