/* ==========================================================================
   SomangPay Zero-Clipping Responsive Design System (v7.0 Final)
   Ensures absolute zero visual clipping or truncation on all device viewports
   ========================================================================== */

/* Pretendard을 로컬에서 서빙(WOFF2, 외부 CDN 의존 제거 - 키오스크 네트워크 불안정/차단
   대비) - 기본 서체를 이걸로 바꾼다. Noto Sans KR은 만약을 위해 폴백으로 남겨둔다. */
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('./fonts/Pretendard-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('./fonts/Pretendard-Medium.woff2') format('woff2');
}
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('./fonts/Pretendard-SemiBold.woff2') format('woff2');
}
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('./fonts/Pretendard-Bold.woff2') format('woff2');
}
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('./fonts/Pretendard-Black.woff2') format('woff2');
}

/* Noto Sans KR을 로컬에서 서빙 (외부 CDN 의존 제거 - 키오스크 네트워크 불안정/차단 대비) -
   Pretendard 폴백용으로 유지. */
@font-face {
  font-family: 'Noto Sans KR';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('./fonts/NotoSansKR-400.ttf') format('truetype');
}
@font-face {
  font-family: 'Noto Sans KR';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('./fonts/NotoSansKR-500.ttf') format('truetype');
}
@font-face {
  font-family: 'Noto Sans KR';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('./fonts/NotoSansKR-700.ttf') format('truetype');
}
@font-face {
  font-family: 'Noto Sans KR';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('./fonts/NotoSansKR-900.ttf') format('truetype');
}

:root {
  --bg-dark: #070a12;
  --bg-card: rgba(15, 23, 42, 0.88);
  --bg-card-hover: rgba(30, 41, 59, 0.95);
  --border-glass: rgba(255, 255, 255, 0.14);
  --border-active: #06b6d4;

  --accent-cyan: #06b6d4;
  --accent-cyan-glow: rgba(6, 182, 212, 0.35);
  --accent-blue: #3b82f6;
  --accent-indigo: #6366f1;
  --accent-senior: #f59e0b;
  --accent-amber: #f59e0b; /* --accent-senior의 별칭 - kiosk.js/admin.js가 이 이름으로도 참조함 */
  --accent-emerald: #10b981; /* --accent-success의 별칭 - admin.js/admin.html이 이 이름으로도 참조함 */
  --accent-success: #10b981;
  --accent-danger: #ef4444;
  /* 차감/삭제 같은 "위험한" 액션 버튼의 옅은 배경(#redesign) - 예전엔 각 버튼마다
     rgba(239,68,68,0.15)를 인라인으로 반복해서 박아뒀다. --accent-cyan-glow와 같은
     패턴으로 토큰화 - instagram-theme.css에서 테마별로 다시 정의. */
  --accent-danger-glow: rgba(239, 68, 68, 0.15);
  --accent-rose: #f43f5e; /* kiosk.html의 로그 "비우기" 버튼이 참조하는데 정의가 빠져있었음(라이트 테마 작업 중 발견) */
  --accent-coral: #f97362; /* 앰버와 로즈 사이의 선홍/살구빛 - user.html 충전 대기 카드가 참조 */

  --text-main: #f8fafc;
  --text-muted: #94a3b8;
  --text-dim: #64748b;

  /* 카드 위에 얹는 은은한 패널(설정 하위 섹션, 비활성 탭 배경 등) / 오목하게 들어간 패널
     (입력창·표시창 배경, 표 컨테이너 등) - 지금까지 이 두 가지를 rgba(255,255,255,0.05~0.1)
     / rgba(0,0,0,0.25~0.5)로 화면마다 제각각 하드코딩해왔는데, 라이트 테마에서는 그 값들이
     그대로 못 쓰인다(흰 배경 위에 흰 반투명은 안 보이고, 검은 반투명은 톤이 다르다) - 변수로
     묶어서 테마별로 한 곳에서 바꿀 수 있게 한다. */
  --surface-1: rgba(255, 255, 255, 0.06);
  --surface-2: rgba(0, 0, 0, 0.35);
  --modal-bg: #0f172a;

  --font-family: 'Pretendard', 'Noto Sans KR', system-ui, -apple-system, sans-serif;
  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-lg: 18px;
  --radius-full: 9999px;

  --shadow-subtle: 0 8px 32px rgba(0, 0, 0, 0.4);
  --backdrop-blur: blur(16px);
}

/* 라이트/다크 테마(시스템/라이트/다크, .kiosk-wrapper[data-theme] 스위칭 - 설정 탭 > 화면
   테마, kiosk.html/kiosk.js 참고)의 실제 색 값은 여기가 아니라 src/instagram-theme.css에
   있다 - 이 사이트의 브랜드 컬러(로고 남색/블루/스카이 톤)와 인스타그램풍 스킨이 전부 그
   파일 하나에 몰려있어서, 라이트 모드도 같은 브랜드 톤으로 맞추려면 색 정의를 한 곳(그
   파일)에만 두는 게 어긋날 일이 없다. 이 파일(style.css)의 :root는 그 파일이 아예 로드
   안 됐을 때를 위한 다크 기본값(순수 청록 계열)만 갖고 있다. */

/* Global Reset & Protection against Overflow */
*, *::before, *::after {
  box-sizing: border-box !important;
  margin: 0;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  width: 100%;
  max-width: 100vw;
  min-height: 100vh;
  /* 100vh는 모바일 브라우저에서 주소창이 보이는/숨는 상태와 무관하게 "가장 큰" 뷰포트
     기준으로 고정되는 경우가 많아, 실제 보이는 화면보다 커져서 바깥 스크롤이 생기는
     원인이 된다. 100dvh(동적 뷰포트 높이)를 지원하는 브라우저에서는 이 값으로 덮어써서
     실제 보이는 높이에 맞춘다 (미지원 브라우저는 유효하지 않은 값이라 무시되고 위 100vh가 유지됨). */
  min-height: 100dvh;
  background-color: var(--bg-dark);
  color: var(--text-main);
  font-family: var(--font-family);
  /* 본문 기본 굵기는 Medium(500)만 - 제목/섹션타이틀처럼 강조가 필요한 곳은 각 클래스에서
     개별적으로 700~900을 쓴다. 본문까지 전부 Bold면 오히려 제목과 구분이 안 돼 위계가
     무너진다(참고 이미지: 큰 제목은 굵게, 본문 불릿 텍스트는 가볍게). */
  font-weight: 500;
  line-height: 1.4;
  /* overflow-x는 여기(html,body 공용)가 아니라 아래 body 전용 규칙에서만 건다 - 이유는
     그 규칙의 주석 참고(#redesign, position:sticky 버그). */
  word-break: keep-all !important; /* Keep Korean words intact */
  word-wrap: break-word !important;
}

/* html이 아니라 body에만 overflow-x:hidden을 건다(#redesign) - 예전엔 html,body 양쪽에
   같이 걸려 있었는데, CSS 스펙상 overflow-x가 visible이 아니면 overflow-y는 자동으로
   auto로 계산돼("두 축 중 하나가 visible이 아니면 나머지도 visible일 수 없음") html과
   body가 각각 별도의 스크롤 컨테이너가 됐다. 그러면 body 바로 아래 position:sticky 요소
   (.app-header, USER/ADMIN 상단 고정 바)의 "가장 가까운 스크롤 조상"이 실제 스크롤이
   일어나는 곳(html 쪽일 수도, body 쪽일 수도)과 어긋나 sticky가 전혀 안 먹는 버그가
   있었다(스크롤하면 헤더가 콘텐츠와 같이 그대로 밀려 올라감 - Playwright로 실제 스크롤
   테스트해서 발견, overflow-y를 visible로 명시해도 spec상 auto로 재승격되어 무효였음).
   body에만 걸면 body 혼자 유일한 스크롤 컨테이너가 되고 html은 스크롤과 무관해져(overflow
   기본값 visible 유지) sticky가 body 기준으로 정상 동작한다. */
body {
  overflow-x: hidden !important; /* Block horizontal scrollbars */
}

/* 위 html,body의 color: var(--text-main)는 body 시점의(=문서 루트, 다크 기본값) --text-main
   으로 한 번 계산돼서 자식 요소에 "이미 계산된 색"으로 상속된다 - 라이트 테마 스코프
   (.kiosk-wrapper[data-theme="light"] 등)가 --text-main을 262626으로 재정의해도, 그 스코프
   안의 개별 요소가 직접 color: var(--text-main)을 쓰지 않는 한(= 그냥 상속만 받는 대부분의
   <div>/<p>) 상속받은 값은 그대로 다크(#fafafa 등)라 라이트 배경 위에서 텍스트가 거의
   안 보이는 버그가 생긴다. 각 페이지의 최상위 래퍼에서 color를 다시 명시적으로 선언해
   그 스코프 안에서 var(--text-main)이 새로 계산되게 한다.
   background도 마찬가지 이유 - body { background: var(--bg-dark) }(instagram-theme.css)도
   body 시점의 값으로 고정돼서, 래퍼가 라이트로 바뀌어도 그 아래(body) 배경은 계속 다크로
   남아 카드 사이 여백/탭바 뒤로 다크가 비친다. .kiosk-wrapper는 원래도 자기 background를
   따로 갖고 있어(아래 .kiosk-wrapper 규칙) 문제 없었지만 admin-shell/mobile-wrapper는
   없었다 - 여기서 공통으로 채워준다. */
.kiosk-wrapper,
.admin-shell,
.mobile-wrapper {
  color: var(--text-main);
  background: var(--bg-dark);
  min-height: 100vh;
  min-height: 100dvh;
}

/* user.html은 kiosk/admin과 달리 상단 타이틀바가 없어(#14) 첫 카드가 화면 맨 위에 바로
   붙는다 - 노치/상태바 아래로 안전하게 내려주는 여백을 래퍼가 직접 갖는다.
   좌우 여백은 admin.html의 .admin-main과 동일하게 카드가 화면 가장자리에 바로 붙지 않도록
   준다(#17 - 이전에는 좌우 패딩이 아예 없어 카드가 화면 끝까지 꽉 찼었다). */
.mobile-wrapper {
  padding: max(1rem, env(safe-area-inset-top)) clamp(0.75rem, 4vw, 1.5rem) 1.5rem;
}

/* 키오스크 화면 잠금 - 길게 눌러 텍스트 선택/컨텍스트 메뉴 호출 차단.
   입력 필드는 정상적인 커서 배치/편집이 필요하므로 예외로 둔다. */
.kiosk-wrapper, .kiosk-wrapper * {
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  user-select: none;
}
.kiosk-wrapper input,
.kiosk-wrapper textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* Flexible App Header - Wraps gracefully on mobile */
.app-header, .kiosk-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0.4rem;
  padding: clamp(0.4rem, 1.2vh, 0.85rem) clamp(0.5rem, 1.5vw, 1.2rem);
  padding-top: max(clamp(0.4rem, 1.2vh, 0.85rem), env(safe-area-inset-top));
  background: var(--bg-card);
  backdrop-filter: var(--backdrop-blur);
  -webkit-backdrop-filter: var(--backdrop-blur);
  border-bottom: 1px solid var(--border-glass);
  position: sticky;
  top: 0;
  z-index: 100;
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
  overflow: hidden !important;
}

/* USER/ADMIN 상단 고정 바(#redesign) - 예전엔 이 자리에 있다가 각 탭 본문 맨 위 <h1>로
   옮겨가며 없어졌는데(admin-page-title 주석 참고), 스크롤하면 지금 어느 탭인지 보여줄
   기준선이 없어져 리뷰에서 지적된 부분("No sticky top app bar"). 제목을 flex
   space-between이 아니라 고정폭 3열 그리드(뒤로가기 40px / 제목 1fr / 액션 40px)로 두는
   이유 - 뒤로가기나 액션 버튼이 없는 화면(대부분의 탭)에서도 제목이 항상 바 정중앙에
   와야 해서다. flex 방식이었다면 한쪽만 비어 있을 때 제목이 반대쪽으로 쏠린다.
   admin.js의 updateAdminHeader가 뷰 전환마다 제목·뒤로가기·액션을 채운다. user.js는
   뒤로가기/액션이 필요 없는 평면 탭뿐이라 switchUserView()가 제목만 직접 채운다. */
.app-header {
  display: grid;
  grid-template-columns: 40px 1fr 40px;
}
.app-header-title {
  grid-column: 2;
  text-align: center;
  font-size: 1.05rem;
  font-weight: 700; /* #redesign - 900/800 남발 규율, 타이틀은 700까지만 */
  color: var(--text-main);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-header-back {
  grid-column: 1;
  /* 안 쓰는 화면에서는 자리만 차지하고 안 보이게(visibility) - display:none을 쓰면 그리드
     칸 자체가 무너져 제목이 다시 왼쪽으로 쏠린다. */
  visibility: hidden;
}
.app-header-back.is-visible {
  visibility: visible;
}
.app-header-actions {
  grid-column: 3;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.2rem;
  min-width: 0;
}

.brand-logo {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: clamp(1rem, 2.5vw, 1.35rem);
  font-weight: 800;
  background: linear-gradient(135deg, #38bdf8, #818cf8);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  text-decoration: none;
  white-space: nowrap;
}

.brand-logo .logo-icon {
  font-size: clamp(1.1rem, 2.8vw, 1.5rem);
  background: var(--accent-cyan-glow);
  padding: 0.15rem 0.45rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--accent-cyan);
}

.icon-line {
  display: inline-block;
  vertical-align: -0.15em;
  flex-shrink: 0;
}

.brand-logo .logo-icon-img {
  width: clamp(24px, 6vw, 32px);
  height: clamp(24px, 6vw, 32px);
  border-radius: var(--radius-sm, 8px);
  display: block;
  flex-shrink: 0;
}

.nav-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem;
  background: rgba(0, 0, 0, 0.4);
  padding: 0.2rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-glass);
}

.nav-item {
  padding: 0.35rem 0.75rem;
  font-size: clamp(0.72rem, 1.2vw, 0.82rem);
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  border-radius: var(--radius-full);
  white-space: nowrap;
}

.nav-item.active, .nav-item:hover {
  color: #fff;
  background: var(--accent-cyan);
}

/* Layout Containers */
.container {
  width: 100%;
  max-width: 1360px;
  margin: 0 auto;
  padding: clamp(0.5rem, 1.5vw, 1.2rem);
}

.glass-panel {
  background: var(--bg-card);
  backdrop-filter: var(--backdrop-blur);
  -webkit-backdrop-filter: var(--backdrop-blur);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-lg);
  padding: clamp(0.85rem, 2vw, 1.4rem);
  box-shadow: var(--shadow-subtle);
  max-width: 100%;
  overflow: hidden;
}

/* Global Mobile Touch & Button Protection against Event Blocking */
button, .btn, .btn-action, .btn-primary, .btn-senior, select, input, .nav-item {
  touch-action: manipulation !important; /* Eliminate 300ms touch delay */
  -webkit-tap-highlight-color: rgba(6, 182, 212, 0.3) !important;
  pointer-events: auto !important; /* Force click/touchability */
  cursor: pointer !important;
  position: relative;
  z-index: 20 !important;
}

button, .btn, .btn-action, .btn-primary, .btn-senior {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: clamp(0.5rem, 1.2vh, 0.75rem) clamp(0.75rem, 1.5vw, 1.1rem);
  font-size: clamp(0.8rem, 1.2vw, 0.92rem);
  font-weight: 700;
  color: #fff;
  background: linear-gradient(135deg, var(--accent-blue), var(--accent-indigo));
  border: none;
  border-radius: var(--radius-md);
  white-space: nowrap !important; /* Never break button text into lines */
  word-break: keep-all !important;
  max-width: 100%;
  min-height: 42px;
  text-decoration: none;
}

button:disabled, .btn:disabled, .btn-action:disabled, .btn-primary:disabled {
  opacity: 0.45;
}

.btn-primary {
  background: linear-gradient(135deg, #06b6d4, #3b82f6);
}

.btn-senior {
  background: linear-gradient(135deg, #f59e0b, #d97706);
}

/* #redesign(사용자 피드백: "초록색 버튼들이 안예쁘다") - 충전 관련 주 액션에만 쓰던
   그라디언트 초록(.btn-emerald)을 없애고 .btn-primary(브랜드 블루)를 그대로 재사용한다.
   Toss/Revolut/N26 등 주요 핀테크 앱들이 "성공/positive" 액션에도 브랜드 고유색 하나만
   쓰고 초록은 실제 잔액 증감 같은 상태 표시에만 남겨두는 것과 같은 방향 - 이 파일 위
   instagram-theme.css 주석("결제 성공/오류 등 상태색은 의미 전달용이라 유지")과도
   일치한다. 초록/빨강 토큰(--accent-emerald/--accent-danger) 자체는 상태 배지·금액
   증감 표시에 그대로 쓰이므로 안 건드림 - 버튼 배경으로 "장식"되던 자리만 뺐다.
   admin.html의 기존 .btn-emerald 클래스 사용처는 .btn-primary로 교체 완료. */

.form-control {
  width: 100%;
  max-width: 100%;
  padding: 0.65rem 0.85rem;
  font-size: clamp(0.85rem, 1.2vw, 0.92rem);
  color: var(--text-main);
  background: var(--surface-2);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  outline: none;
  min-height: 42px;
}

/* 포커스 표시를 별도로 정의해둔다 - 브라우저 기본 포커스 링(outline)은 요소 바깥으로
   번져 그려지는데, 모달들이 overflow: hidden이라 인풋이 모달 가장자리에 붙어 있으면
   그 바깥으로 번진 부분이 잘려서 테두리 굵기가 고르지 않아 보인다. outline-offset을
   음수로 줘서 링을 박스 안쪽으로 그리면 어떤 조상의 overflow에도 잘리지 않는다. */
.form-control:focus {
  border-color: var(--accent-cyan);
  outline: 2px solid var(--accent-cyan);
  outline-offset: -2px;
}

/* 네이티브 <select> 드롭다운의 옵션 목록은 OS가 그린다 - 이때 <option>이 상속하는
   --surface-2(반투명 검정, rgba(0,0,0,0.35))가 흰 팝업 배경에 겹쳐지면서 거의 흰색이
   되어버려, 그 위의 흰 글자(--text-main)가 보이지 않게 된다(선택/hover된 항목만 OS
   하이라이트 덕에 읽힘). 팝업 안에서는 불투명한 어두운 배경 + 밝은 글자를 명시해
   대비를 확보한다. */
.form-control option {
  color: var(--text-main);
  background-color: var(--modal-bg);
}

.form-group {
  margin-bottom: 1rem;
}

.form-label {
  display: block;
  font-size: 0.85rem;
  color: var(--text-muted);
  font-weight: 600;
  margin-bottom: 0.4rem;
}

/* ==========================================================================
   Admin Dashboard (admin.html) - glass-container/table-custom/badge-tag/
   form-group/form-label were referenced across admin.html, kiosk.html,
   user.html and index.html with no matching rules anywhere in this file,
   so those pages were rendering as bare unstyled divs/tables. Defining
   .glass-container as an alias of the existing .glass-panel card style
   fixes all of them at once; the rest are admin.html-specific.
   ========================================================================== */
.glass-container {
  background: var(--bg-card);
  backdrop-filter: var(--backdrop-blur);
  -webkit-backdrop-filter: var(--backdrop-blur);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
  max-width: 100%;
  overflow: hidden;
}

/* 설정 > 수신 로그: 항목이 아무리 많이 쌓여도(최대 50건) 패널 높이는 고정되고
   내부에서만 스크롤된다. overscroll-behavior로 끝까지 스크롤했을 때 페이지 전체가
   같이 스크롤되는(scroll chaining) 것도 막는다. */
.sms-log-scroll {
  max-height: 340px;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-right: 0.3rem;
  scrollbar-width: thin;
  scrollbar-color: var(--border-glass) transparent;
}
/* flex 컨테이너에 overflow:auto가 붙으면 자식의 "콘텐츠 크기만큼은 안 줄어드는" 기본 동작이
   사라져서(min-height:auto -> 0), 로그가 많이 쌓였을 때 각 항목이 컨테이너 높이(340px)에
   맞춰 다 같이 짜부라지며 겹쳐 보인다 - 항목마다 자기 콘텐츠 높이를 유지하고 컨테이너가
   스크롤되도록 고정한다. */
.sms-log-scroll > * {
  flex-shrink: 0;
}
.sms-log-scroll::-webkit-scrollbar {
  width: 6px;
}
.sms-log-scroll::-webkit-scrollbar-track {
  background: transparent;
}
.sms-log-scroll::-webkit-scrollbar-thumb {
  background: var(--border-glass);
  border-radius: 999px;
}

.admin-wrapper {
  max-width: 1360px;
  margin: 0 auto;
  padding: clamp(0.75rem, 2vw, 1.5rem);
}

.admin-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}

.admin-form-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.8rem;
  align-items: flex-end;
}

@media screen and (min-width: 900px) {
  .admin-grid {
    grid-template-columns: 1fr 1fr;
  }
}

@media screen and (min-width: 640px) {
  .admin-form-grid-3 {
    grid-template-columns: 2fr 1fr 1fr;
  }
  .admin-form-grid-4 {
    grid-template-columns: 2fr 1fr 1fr auto;
  }
}

/* ==========================================================================
   Admin Shell (admin.html v2) - 트위터 스타일 탭 내비게이션.
   모바일: 하단 고정 아이콘 탭바. 데스크톱(>=900px, .admin-grid와 동일 기준):
   왼쪽 sticky 사이드바로 전환. 콘텐츠는 항상 단일 컬럼 피드.
   ========================================================================== */
.admin-shell {
  display: flex;
  flex-direction: column;
  max-width: 1360px;
  margin: 0 auto;
  /* --header-h는 admin.js가 .app-header의 실제 렌더된 높이를 재서 채워준다(고정된 60px
     추정치는 safe-area-inset-top이 있는 기기 등에서 실제 헤더 높이와 어긋나 아래 admin-main이
     (flex:1로) 필요 이상 늘어나 바깥 스크롤이 생기는 원인이었다). 100dvh 우선, 미지원
     브라우저는 100vh로 폴백. */
  min-height: calc(100vh - 60px);
  min-height: calc(100dvh - var(--header-h, 60px));
}

.admin-tabbar {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 90;
  background: var(--bg-card);
  backdrop-filter: var(--backdrop-blur);
  -webkit-backdrop-filter: var(--backdrop-blur);
  border-top: 1px solid var(--border-glass);
  padding: 0.4rem 0.2rem max(0.4rem, env(safe-area-inset-bottom));
}

.admin-tab-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 600;
  padding: 0.3rem 0.6rem;
  border-radius: var(--radius-md);
  cursor: pointer;
  min-width: 56px;
}

.admin-tab-btn .tab-icon {
  font-size: 1.3rem;
  line-height: 1;
  position: relative;
}

.admin-tab-btn.active {
  color: var(--accent-cyan);
}

.admin-tab-badge {
  position: absolute;
  top: -4px;
  right: -8px;
  background: var(--accent-danger);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 800;
  min-width: 16px;
  height: 16px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 3px;
}

/* 실시간 WS 연결 상태 점(#redesign) - admin-tab-badge와 같은 모서리 배지 위치를 재사용.
   기본값(클래스 없음)은 회색(끊김/아직 연결 전)이라 페이지 로드 첫 프레임에도 어색하지
   않다 - ws-client.js의 onStatusChange가 연결되는 즉시 ws-status-open을 붙인다. */
.ws-status-dot {
  position: absolute;
  top: -1px;
  right: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-dim, var(--text-muted));
  border: 2px solid var(--bg-card);
}
.ws-status-dot.ws-status-open {
  background: var(--accent-emerald);
}
.ws-status-dot.ws-status-connecting {
  background: var(--accent-amber);
}
.ws-status-dot.ws-status-closed {
  background: var(--accent-danger);
}

.admin-main {
  flex: 1;
  padding: clamp(0.75rem, 2vw, 1.5rem);
  padding-bottom: 5.5rem; /* 하단 탭바에 안 가리게 */
}

.admin-view {
  display: none;
}
.admin-view.active {
  display: block;
}

/* 탭 본문 안 제목 행 - 제목 자체(.admin-page-title, <h1>)는 상단 고정 바로 옮겨가면서
   지워졌다(#redesign, style.css의 .app-header 참고) - 이 클래스는 이제 "기간 통계"처럼
   라벨+컨트롤 한 줄을 정렬하는 용도로만 남았다. 사용자 관리 탭처럼 라벨 옆에 액션
   버튼(+)이 필요한 경우도 마찬가지 - 예전 하단 우측 FAB(＋ 새 회원 등록)를 대신한다. */
.admin-page-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 1rem;
}
.admin-page-title-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}
.admin-page-title-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0.2rem 0.3rem;
  min-height: auto;
  color: var(--text-main);
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1;
  flex-shrink: 0;
}
.admin-page-title-action:hover {
  opacity: 0.75;
}

/* 사용자 관리 탭 검색창 - 예전엔 돋보기 버튼을 눌러야 펼쳐지는 드롭다운이었는데(#26),
   회원 목록에서 가장 자주 쓰는 조작을 한 번 더 눌러야 하는 게 어색해 제목줄 바로 아래
   상시 노출 필드로 바꿨다(#redesign). 왼쪽에 돋보기 아이콘을 얹어 검색창임을 바로
   알아보게 하고, 입력값이 있을 때만 오른쪽 끝에 X(지우기) 버튼을 겹쳐 보여준다. */
.member-search-input-wrap {
  position: relative;
  margin-bottom: 0.8rem;
}
.member-search-static {
  margin-bottom: 1rem;
}
.member-search-input-wrap .form-control {
  margin-bottom: 0;
  padding-right: 2.2rem;
}
.member-search-static .form-control {
  padding-left: 2.4rem;
}
.member-search-icon {
  position: absolute;
  top: 50%;
  left: 0.75rem;
  transform: translateY(-50%);
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1;
  pointer-events: none;
}

/* 충전함 상태 필터 칩 - 값이 3개뿐이라 드롭다운 대신 상시 노출 칩으로(#redesign, admin.js의
   renderInboxFilterSelector 참고). 각 칩 안의 건수 배지가 대기(amber)/오류(danger) 상태의
   심각도를 색으로 먼저 보여주고, 칩 자체가 눌려있으면(현재 필터) 그 색이 액센트로 바뀐다. */
.filter-chip-row {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.8rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-glass);
  background: var(--surface-1);
  color: var(--text-muted);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
}
.filter-chip-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.3em;
  padding: 0 0.4em;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 0.78em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.filter-chip-amber:not(.active) .filter-chip-count {
  background: rgba(245, 158, 11, 0.18);
  color: var(--accent-amber);
}
.filter-chip-danger:not(.active) .filter-chip-count {
  background: rgba(239, 68, 68, 0.18);
  color: var(--accent-danger);
}
.filter-chip.active {
  background: var(--accent-cyan);
  border-color: var(--accent-cyan);
  color: #001318;
}
.filter-chip.active .filter-chip-count {
  background: rgba(0, 0, 0, 0.18);
  color: inherit;
}
.member-search-clear-btn {
  display: none;
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 0.9rem;
  cursor: pointer;
}
.member-search-input-wrap.has-value .member-search-clear-btn {
  display: flex;
}

/* 필터가 걸린 상태에서 목록 맨 위에 뜨는 "필터 해제" 버튼(#26) */
.member-search-clear-filter {
  display: block;
  width: 100%;
  text-align: center;
  padding: 0.6rem;
  margin-bottom: 0.5rem;
  background: rgba(103,129,192,0.1);
  border: 1px dashed var(--accent-cyan);
  border-radius: var(--radius-md);
  color: var(--accent-cyan);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

/* admin.js의 showToast()가 #admin-toast를 만들어 .show 클래스로 토글하는데, 지금까지 이
   id에 대한 스타일이 전혀 없어서 그냥 body 맨 끝에 평범한 문서 흐름 콘텐츠로 붙어 하단
   고정 탭바와 겹쳐 보이고, 그 실제 텍스트 높이만큼 body가 늘어나 바깥(페이지) 스크롤이
   생기는 원인이 되고 있었다. position:fixed로 흐름에서 완전히 빼서 그 문제를 없앤다. */
#admin-toast {
  position: fixed;
  left: 50%;
  bottom: calc(5.2rem + env(safe-area-inset-bottom));
  transform: translate(-50%, 10px);
  max-width: min(92vw, 420px);
  background: rgba(15, 23, 42, 0.96);
  border: 1px solid var(--border-glass);
  color: var(--text-main);
  padding: 0.7rem 1rem;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-subtle);
  font-size: 0.85rem;
  line-height: 1.4;
  text-align: center;
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
#admin-toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media screen and (min-width: 900px) {
  .admin-shell {
    flex-direction: row;
    gap: 1.5rem;
  }
  .admin-tabbar {
    position: sticky;
    top: 60px;
    bottom: auto;
    left: auto;
    right: auto;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    width: 200px;
    flex-shrink: 0;
    height: fit-content;
    border-top: none;
    border-right: 1px solid var(--border-glass);
    border-radius: var(--radius-lg);
    padding: 0.8rem;
    gap: 0.3rem;
  }
  .admin-tab-btn {
    flex-direction: row;
    justify-content: flex-start;
    gap: 0.7rem;
    font-size: 0.92rem;
    padding: 0.65rem 0.9rem;
    min-width: 0;
  }
  .admin-tab-btn .tab-icon {
    font-size: 1.15rem;
  }
  .admin-tab-btn.active {
    background: var(--accent-cyan-glow);
  }
  .admin-main {
    padding: 1.5rem 1.5rem 1.5rem 0;
  }
  #admin-toast {
    bottom: 1.5rem; /* 데스크톱은 하단 탭바가 왼쪽 사이드바로 바뀌므로 여유를 둘 필요 없음 */
  }
}

/* ==========================================================================
   User Shell (user.html) - admin.html과 동일한 .admin-shell/.admin-tabbar/
   .admin-view 탭 구조를 그대로 재사용한다(레이아웃 클래스일 뿐 admin 전용 로직은
   없음 - switchUserView()는 user.js에 별도로 있다). #redesign 이전엔 상단
   .app-header가 없어 콘텐츠가 노치/상태바 바로 아래 붙는 문제가 있어 여기 padding-top에
   안전 영역을 직접 줬는데, 이제 .app-header가 생겨 그쪽에서 이미 처리하므로
   (style.css .app-header 참고) 여기서 또 더하면 안전 영역이 이중으로 잡혀 헤더 아래
   틈이 벌어진다 - admin.html의 plain .admin-main과 같은 값으로 되돌림. */
#user-shell .admin-main {
  padding-top: clamp(0.75rem, 2vw, 1.5rem);
  /* .admin-main 자신은 .admin-shell 쪽에서 flex:1로 뷰포트 높이를 채우지만, 그 값은
     flex-grow로만 정해져서(명시적 height 아님) 자식의 퍼센트 height(예: 100%)가 먹지
     않는다 - .admin-main을 flex column으로 바꿔 활성 뷰가 flex-grow로 남은 세로
     공간을 직접 채울 수 있게 한다. 다른 탭(.admin-view.active)은 그냥 display:block이라
     flex-grow:0(기본값)로 지금까지처럼 내용 높이만큼만 차지하고 나머지는 빈 공간으로
     남는다 - QR 탭만 아래에서 flex:1로 이 공간을 직접 가져간다. */
  display: flex;
  flex-direction: column;
}

/* QR 결제 페이지 - 제목("QR 결제")은 다른 탭과 같이 위에 두고, 카드 내용
   (.user-qr-view-body)만 탭 안의 남은 세로 공간 가운데로 오도록 한다(#user-update). */
#user-view-qr.active {
  flex: 1;
  display: flex;
  flex-direction: column;
}
.user-qr-view-body {
  flex: 1;
  display: flex;
  /* flex-direction을 기본값(row)으로 두면 justify-content:center가 가로축(주축)을
     센터링하면서 .glass-container를 내용 크기로 줄여버린다(카드 폭이 줄어드는
     버그였음) - column으로 바꿔 주축(세로)만 가운데 정렬하고, 교차축(가로)은
     기본 align-items:stretch로 원래처럼 꽉 채운 폭을 유지한다. */
  flex-direction: column;
  justify-content: center;
}

/* QR 결제 탭바 버튼 - 하단 탭바 5개 항목 중 가운데(3번째)에 넣는 강조 탭(#user-update).
   탭바 밖으로 튀어나오는 원형 FAB 대신, 다른 탭과 같은 높이로 바 안에 들어앉고
   아이콘 자리에만 강조색 사각 배지(chip)를 둘러 눈에 띄게 한다(요청 참고 이미지
   스타일). 레이아웃(아이콘+라벨, 모바일 라벨 숨김, 데스크톱 사이드바 행 전환, hover,
   active 등)은 다른 탭과 같은 .admin-tab-btn을 그대로 같이 붙여 쓰고, 배지만 이
   클래스로 얹는다 - active 여부와 무관하게 배지는 항상 켜져 있다. */
.user-qr-fab .tab-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.3em;
  /* 위쪽 잔액 카드(.user-card-widget)와 같은 배경으로 통일 - "같은 카드에서 온 버튼"처럼
     보이도록(#redesign - 카드가 그라디언트→플랫 남색으로 바뀌어 여기도 맞춤). */
  background: var(--user-card-bg, #2d326e);
  color: #fff;
  border-radius: var(--radius-md);
}

/* Dashboard stat tiles */
/* 홈 최상단 카드 - 잔액 보유 회원수(작게, 좌측 상단)와 총 보유 잔액 합계(크게, 우측)를
   한 카드 안에 위아래로 나눠 담는다. min-height로 카드를 세로로 넉넉하게 늘린다.
   배경/워터마크는 user.html 잔액 카드(.user-card-widget)와 같은 방식 - 플랫 남색
   (--user-card-bg) + 로고 마크를 우하단에 옅게 얹는다(#redesign, ADMIN 메인 카드를
   USER 쪽 업그레이드에 맞춤). z-index:0으로 새 스태킹 컨텍스트를 만들어 워터마크
   (z-index:-1)가 텍스트 뒤에만 깔리게 한다. */
.hero-stat {
  position: relative;
  z-index: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 180px;
  padding: 1.4rem 1.6rem;
  margin-bottom: 1rem;
}
.hero-stat::before {
  content: "";
  position: absolute;
  z-index: -1;
  bottom: -20%;
  right: -14%;
  width: 62%;
  aspect-ratio: 1;
  background-image: url('/icons/logo-mark-admin.svg');
  background-size: contain;
  background-repeat: no-repeat;
  opacity: 0.16;
  pointer-events: none;
}
.hero-stat-bottom {
  text-align: right;
}
.hero-stat .stat-label {
  font-size: 0.85rem;
  color: var(--text-muted);
  font-weight: 600;
}
.hero-stat .stat-value {
  font-size: clamp(2.2rem, 6vw, 3rem);
  font-weight: 800;
  color: var(--text-main);
  margin-top: 0.2rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.hero-stat .stat-value-sm {
  font-size: clamp(1.3rem, 3vw, 1.7rem);
  font-weight: 800;
  color: var(--text-main);
  margin-top: 0.2rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* 800 웨이트 통계 숫자에서 단위("원"/"명")만 작게 - 숫자 줄이 타일 폭을 넘겨 줄바꿈되던
   문제(#redesign)를 막고 숫자에 시선이 먼저 가게 한다. */
.stat-value .stat-unit,
.stat-value-sm .stat-unit {
  font-size: 0.5em;
  font-weight: 500;
  margin-left: 0.15em;
  color: var(--text-muted);
}

@media screen and (max-width: 480px) {
  .hero-stat {
    padding: 1rem 1rem;
    min-height: 150px;
  }
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.8rem;
  margin-bottom: 1rem;
}

.stat-tile {
  padding: 1rem 1.1rem;
}
.stat-tile .stat-label {
  font-size: 0.78rem;
  color: var(--text-muted);
  font-weight: 600;
}
.stat-tile .stat-value {
  font-size: clamp(1.3rem, 3vw, 1.7rem);
  font-weight: 800;
  color: var(--text-main);
  margin-top: 0.2rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* 통계 그리드 셀은 라벨/값 모두 우측 정렬. */
.stat-grid .stat-tile {
  text-align: right;
}

/* 대시보드 "처리 대기" 배너(#redesign) - 대기/오류 타일 2개를 나란히 늘 띄우던 대신,
   하나로 합쳐서 0건이면 완전히 숨긴다(display:none, admin.js의 renderDashboard). 있을 때만
   보이므로 굳이 색을 옅게/진하게 나눌 필요가 없어 늘 경고색(amber) 톤 하나로 고정. */
.pending-action-banner {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 1rem 1.1rem;
  margin-bottom: 1.2rem;
  cursor: pointer;
  border-color: var(--accent-amber);
  background: rgba(245, 158, 11, 0.08);
}
.pending-action-icon {
  font-size: 1.5rem;
  color: var(--accent-amber);
  flex-shrink: 0;
}
.pending-action-text {
  flex: 1 1 auto;
  min-width: 0;
}
.pending-action-title {
  font-weight: 800;
  font-size: 1rem;
  color: var(--text-main);
}
.pending-action-sub {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.1rem;
}
.pending-action-chevron {
  flex-shrink: 0;
  color: var(--text-muted);
}

/* 회원 관리 탭 - 검색창/스캔 버튼은 고정, 목록만 남은 공간을 차지하며 그 안에서
   스크롤된다 (홈 탭과 동일한 패턴). --search-view-h는 admin.js의
   updateFixedViewLayoutMetrics()가 채워준다. */
#admin-view-search.active {
  display: flex;
  flex-direction: column;
  height: var(--search-view-h, 70vh);
  overflow: hidden;
}
#admin-view-search.active > *:not(.member-feed) {
  flex-shrink: 0;
}

/* Member search feed (회원 관리 탭) */
.member-feed {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* flex 컨테이너 자신에 overflow:auto가 붙으면 자식의 "콘텐츠 크기 밑으로는 안 줄어드는"
   기본 동작이 사라져서(min-height:auto -> 0), 회원이 많아지면 카드가 컨테이너 높이에 맞춰
   다 같이 짜부라질 수 있다 - 카드마다 자기 콘텐츠 높이를 유지하도록 고정한다
   (설정 > 수신 로그의 .sms-log-scroll과 동일한 이슈/수정). */
.member-feed > * {
  flex-shrink: 0;
}

/* 회원 카드와 키오스크 카드(#redesign2, 키오스크 관리 탭)는 같은 모양을 쓴다 - 규칙을
   grouped selector로 공유한다. */
.member-card, .kiosk-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 1.3rem 1.1rem;
  cursor: pointer;
  transition: background 0.15s ease;
}
.member-card:hover, .kiosk-card:hover {
  background: var(--bg-card-hover);
}
.member-card .member-card-name, .kiosk-card .kiosk-card-name {
  display: flex;
  align-items: center;
  font-weight: 700;
  color: var(--text-main);
  font-size: 1.02rem;
}
.member-card .member-card-sub, .kiosk-card .kiosk-card-sub {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}
.kiosk-card .kiosk-card-name .kiosk-online-dot {
  margin-right: 0.4rem;
}
.member-card .member-card-balance {
  font-weight: 800;
  /* 잔액은 "지금 얼마 있나"라는 중립 정보 - 초록(크레딧 증가)이 아니라 잉크색으로. */
  color: var(--text-main);
  font-size: 1.05rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* 키오스크 카드 우측 오늘 매출 - 잔액과 달리 "오늘"이라는 기간이 붙는 숫자라 위에 작은
   라벨을 하나 더 둔다(회원 카드 잔액엔 라벨이 없어도 문맥상 잔액임이 분명하지만, 이건
   기간을 명시하지 않으면 오해할 수 있다). */
.kiosk-card-sales {
  flex-shrink: 0;
  text-align: right;
}
.kiosk-card-sales-label {
  font-size: 0.72rem;
  color: var(--text-muted);
}
.kiosk-card-sales-amount {
  font-weight: 800;
  color: var(--text-main);
  font-size: 1.05rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  margin-top: 0.15rem;
}
.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent-emerald);
  margin-right: 0.35rem;
}
.status-dot.suspended {
  background: var(--accent-danger);
}

/* 충전함(Inbox) - "최근 처리 내역" 피드(구 홈 탭 피드, 계좌 입금/충전 신청을 시간순으로
   합친 목록)를 보여준다. 예전엔 전용 스크롤 박스(.activity-feed-scroll)를 고정 높이로
   잘라 그 안에서만 스크롤했는데(--inbox-view-h, admin.js의 updateFixedViewLayoutMetrics),
   그 높이 계산이 어긋나면 바깥 페이지 스크롤과 안쪽 스크롤이 동시에 생기는 "2단 스크롤"이
   됐다(#redesign3, 사용자 리포트) - 다른 탭(홈/설정)이나 회원상세/키오스크상세처럼 그냥
   페이지(window) 전체가 스크롤되게 바꿔서 이중 스크롤 컨테이너 자체를 없앴다. */
#admin-view-inbox.active {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* "최근 처리 내역" 실시간 피드 - 이제 그냥 평범한 블록이고, 페이지(window) 스크롤이
   하단에 가까워지면 admin.js(setupActivityFeedInfiniteScroll)가 다음 페이지를 이어붙인다. */
.activity-feed-scroll {
  display: block;
}

.activity-feed {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  margin-bottom: 1rem;
}

/* 충전함 행 밀도 상향(#redesign) - 처리 대기가 몰릴 때 스크롤 없이 더 많이 보이도록
   기존 0.9rem 위아래 여백을 줄였다. */
.activity-row {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.7rem 1.1rem;
}
.activity-row .activity-icon {
  font-size: 1.3rem;
  flex-shrink: 0;
}
.activity-row .activity-info {
  flex: 1;
  min-width: 0;
}
/* 이름 + 대기/오류(/테스트) 배지를 한 줄에(#redesign) - 예전엔 배지가 날짜 아래 3번째
   줄에 따로 있어서 상태를 보려면 시선을 한 번 더 내려야 했다. */
.activity-row .activity-title-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.activity-row .activity-title {
  font-weight: 700;
  color: var(--text-main);
  font-size: 0.95rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.activity-row .activity-title-row .activity-status {
  margin-top: 0;
  flex-shrink: 0;
}
.activity-row .activity-sub {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}
.activity-row .activity-amount {
  font-weight: 800;
  /* 초록은 "크레딧이 실제로 반영됨(완료)"에만 - 대기·오류·기타 금액은 중립(잉크색). */
  color: var(--text-main);
  font-size: 1rem;
  white-space: nowrap;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.activity-row .activity-amount.is-credited {
  color: var(--accent-emerald);
}
.activity-row .activity-amount-col {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  flex-shrink: 0;
}

/* 심각도 우선(#redesign) - 대기/오류는 왼쪽 색 스트립으로 시선을 끌고, 처리된(완료/기타)
   행은 더 흐리고 촘촘하게 묶어 "이미 끝난 일"임을 형태로도 보여준다(색·글자로만
   구분하던 문제 개선). border-left 두께만큼 좌우 padding을 줄여 다른 행과 텍스트
   시작 위치를 맞춘다. */
.activity-row.is-pending {
  border-left: 3px solid var(--accent-amber);
  padding-left: calc(1.1rem - 3px);
}
.activity-row.is-error {
  border-left: 3px solid var(--accent-danger);
  padding-left: calc(1.1rem - 3px);
}
.activity-row.is-resolved {
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
  opacity: 0.68;
}
.activity-resolved-divider {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.03em;
  margin: 0.2rem 0.2rem -0.1rem;
}
.activity-status {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  margin-top: 0.25rem;
  white-space: nowrap;
}
.activity-status.status-done {
  background: rgba(16, 185, 129, 0.15);
  color: var(--accent-emerald);
}
.activity-status.status-pending {
  background: rgba(245, 158, 11, 0.15);
  color: var(--accent-amber);
}
.activity-status.status-rejected {
  background: rgba(239, 68, 68, 0.15);
  color: var(--accent-danger);
}
.activity-status.status-other {
  background: rgba(148, 163, 184, 0.15);
  color: var(--text-muted);
}
/* 회원 앱 지난 내역에서 "결제 완료"(잔액 차감)를 "충전 완료"(status-done, 초록)와
   구분되게 표시한다(#17) - 둘 다 초록이면 금액 부호(+/-)를 안 보고는 구분이 안 된다. */
.activity-status.status-payment {
  background: rgba(59, 130, 246, 0.15);
  color: var(--accent-blue);
}

/* user.html은 40~50대 이용자가 많아 다른 페이지보다 글자를 더 키운다(위 html 루트
   font-size 참고) - 이 목록의 상태 배지는 예외적으로 고정 rem이라 그 스케일을 못 받아서
   여전히 작다는 피드백(#17)이 있었다. .mobile-wrapper로 스코프를 걸어 admin.html 등
   다른 페이지의 동일 클래스에는 영향이 없게 한다. */
.mobile-wrapper .activity-status {
  font-size: 0.85rem;
  padding: 0.2rem 0.6rem;
}

/* 충전/결제 통합 이력 행(#19, #history-redesign) - 어드민 회원상세/키오스크상세와 유저 앱
   이용내역이 동일한 레이아웃을 쓴다: 좌측 카테고리 아이콘, 그 옆 제목/부제(2줄), 우측
   금액(+/-) + 잔액. 예전엔 감싸는 .glass-container 안에서 행마다 다시 --surface-2로
   배경을 채워 "카드 안 카드"로 겹쳐 보였다는 리뷰 지적으로, 행 자체는 배경 없이 hairline
   구분선만 넣는 flat 목록으로 바꿨다. */
.history-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 0.1rem;
  border-bottom: 1px solid var(--border-glass);
}
.history-item:last-child {
  border-bottom: 0;
}
.history-item-icon {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  color: var(--text-muted);
}
/* 아이콘 색은 상태(성공/실패)가 아니라 "누가 일으킨 이벤트인가"를 나타낸다(history-render.js
   HISTORY_CATEGORY_META 참고) - 계좌이체 자동충전만 초록(pos), 관리자 개입은 브랜드
   액센트(accent)/경고(warn)/위험(danger)으로 구분한다. 배경 rgba는 .activity-status pill과
   같은 값을 그대로 재사용해 라이트/다크 양쪽에서 이미 검증된 대비를 유지한다. */
.history-item-icon.pos {
  background: rgba(16, 185, 129, 0.15);
  color: var(--accent-emerald);
}
.history-item-icon.accent {
  background: var(--accent-cyan-glow);
  color: var(--accent-cyan);
}
.history-item-icon.warn {
  background: rgba(245, 158, 11, 0.15);
  color: var(--accent-amber);
}
.history-item-icon.danger {
  background: rgba(239, 68, 68, 0.15);
  color: var(--accent-danger);
}
.history-item-left {
  min-width: 0;
  flex: 1;
}
.history-item-title {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-main);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.history-item-subtitle {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.history-item-right {
  text-align: right;
  flex-shrink: 0;
}
/* 지출까지 빨강이면 화면이 온통 빨강이라 불안하게 읽힌다는 리뷰 지적으로, 입금
   (amount-positive)만 초록으로 두고 지출/실패/보류는 전부 기본 잉크색을 쓴다. */
.history-item-amount {
  font-weight: 800;
  font-size: 1.05rem;
  white-space: nowrap;
  color: var(--text-main);
}
.history-item-amount.amount-positive {
  color: var(--accent-emerald);
}
/* 이용내역 날짜 구분선(#33, #history-redesign) - 행을 날짜별로 묶어 이 줄로 나누고, 행
   자체에는 시간만 남긴다(historyItemHtml 참고). 예전 "2026년 8월 30일 일요일" 가운데
   정렬 풀 문장을 "오늘/어제/8월 28일 (금)"처럼 줄이고 좌측 정렬로 바꿨다. */
.history-date-divider {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--text-muted);
  margin: 1rem 0 0.4rem;
}
.history-date-divider:first-child {
  margin-top: 0;
}

.history-item-balance {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}

/* 이용내역 빈 상태(#history-redesign) - 회색 텍스트 한 줄 대신 아이콘 + 카피. */
.history-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  color: var(--text-muted);
  padding: 2rem 0;
  text-align: center;
}
.history-empty p {
  font-size: 0.85rem;
}

/* Member detail view (회원 상세 - 트위터 프로필 페이지 격) */
.detail-header {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin-bottom: 1rem;
}
.back-btn {
  /* #redesign(테두리 제거) - 원 배경/테두리를 없애고 화살표만 보이게 해서(요청) 앱바가
     차지하던 세로 공간을 줄인다. 전역 button 규칙(위쪽 "button, .btn, ..."의
     min-height: 42px, padding)이 그대로 얹히면 다시 커지므로 여기서 덮어써야 함. */
  background: transparent;
  border: none;
  /* #27: 하드코딩 흰색이라 라이트 테마(옅은 배경 위 흰 화살표)에서 거의 안 보였다 -
     테마별로 이미 재정의되는 --text-main을 따라가게 해 라이트/다크 모두에서 보이게 함. */
  color: var(--text-main);
  width: 32px;
  height: 32px;
  min-height: 0;
  padding: 0;
  font-size: 1.1rem;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 이름 + 배지/상태 칩을 두 줄로 쌓는다 - 390px에서 상태 텍스트가 헤더 오른쪽 끝으로
   잘려나가던 문제(#redesign)를 없애고 회원 목록 카드와 같은 정보 구성으로 통일. */
.detail-header-id {
  flex: 1;
  min-width: 0;
}
.detail-header-id h2 {
  margin: 0;
  font-size: 1.3rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.detail-header-tags {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.3rem;
}
.detail-status-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
}
.detail-status-chip.is-active {
  background: rgba(16, 185, 129, 0.16);
  color: var(--accent-emerald);
}
.detail-status-chip.is-suspended {
  background: rgba(239, 68, 68, 0.16);
  color: var(--accent-danger);
}

/* 세그먼트 컨트롤 - 이모지 버튼(🔁☀️🌙) 대신 쓰는 연결된 토글. ADMIN 설정 · USER
   프로필의 "화면 테마" 공용(#redesign). */
.seg-control {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  border: 1px solid var(--border-glass);
}
.seg-control button {
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius-full);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.seg-control button.is-on {
  background: var(--accent-cyan);
  color: #001318;
}

/* iOS식 토글 스위치 - 이모지 버튼(🔕/🔔) 대신(#redesign). */
.switch-toggle {
  --sw-w: 44px;
  --sw-h: 26px;
  position: relative;
  flex-shrink: 0;
  width: var(--sw-w);
  height: var(--sw-h);
  /* 전역 button 규칙(위쪽, min-height: 42px)이 이 버튼에도 걸려 26px로 지정한 높이를
     42px로 밀어 올리는 바람에 knob이 캡슐 위쪽에 붙어 초승달처럼 보이는 버그가 있었다
     (#settings-redesign, ADMIN 알림 스위치를 22px로 줄이며 발견 - USER 프로필 푸시
     스위치도 같은 문제를 안고 있었을 것). 여기서 그 하한을 무효화한다. */
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border-glass);
  border-radius: 999px;
  background: var(--surface-2);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.switch-toggle .switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: calc(var(--sw-h) - 6px);
  height: calc(var(--sw-h) - 6px);
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform 0.16s ease;
}
.switch-toggle.is-on {
  background: var(--accent-cyan);
  border-color: var(--accent-cyan);
}
.switch-toggle.is-on .switch-knob {
  transform: translateX(calc(var(--sw-w) - var(--sw-h)));
}
.switch-toggle:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.switch-status {
  margin-left: auto;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* #27: 회원 상세 카드 우측 상단의 정보수정 버튼 - 테두리/음영 없이 이름과 같은 글자색만
   써서 카드 위에 얹힌 듯 가볍게 보이도록 한다 (정지/삭제는 정보수정 모달 안으로 이동). */
.icon-plain-btn {
  width: 32px;
  height: 32px;
  border: none;
  background: none;
  box-shadow: none;
  color: var(--text-main);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  cursor: pointer;
  padding: 0;
}

/* #27: 라이트 테마에서 rgba(0,0,0,0.25) 고정값이 흰 카드 위에 너무 짙게 떠 보였다 -
   이용 내역 아이템(.history-item)과 같은 톤(var(--surface-2))으로 맞춘다. */
.card-slot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.8rem 1rem;
  background: var(--surface-2);
  border-radius: var(--radius-md);
  margin-bottom: 0.6rem;
}
.card-slot .card-slot-type {
  font-weight: 700;
  color: var(--text-main);
}
.card-slot .card-slot-uid {
  font-family: monospace;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.badge-tag {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.65rem;
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
}

.badge-senior {
  background: rgba(245, 158, 11, 0.18);
  color: var(--accent-senior);
  border: 1px solid rgba(245, 158, 11, 0.4);
}

.badge-general {
  background: rgba(59, 130, 246, 0.18);
  color: #93c5fd;
  border: 1px solid rgba(59, 130, 246, 0.4);
}

/* user.html 로그인 후 잔액 표시 카드 (회원 PWA 메인 화면, #redesign) - 예전엔 135°
   남색→블루 2톤 대각선 그라디언트였는데, 리뷰에서 "올드하고 라이트에서 탁하다"는
   지적을 받았다. 플랫 남색 + 로고 마크를 우하단에 크고 옅게 얹은 "월렛 카드" 느낌으로 -
   카카오페이/토스 카드 패턴 참고. z-index:0으로 새 스태킹 컨텍스트를 만들어 워터마크
   (z-index:-1)가 카드 밖으로 새지 않고 이름/잔액 텍스트 뒤에만 깔리게 한다.
   배경색은 --brand-navy를 그대로 안 쓰고 전용 토큰 --user-card-bg(instagram-theme.css)로
   살짝 더 어둡게 뺐다 - 로고 마크 SVG 자체가 정확히 --brand-navy 색의 사각형들을 포함하고
   있어서, 배경과 완전히 같은 색이면 그 부분 워터마크가 통째로 안 보이는 문제가 있었다
   (사용자 리포트로 발견). */
.user-card-widget {
  position: relative;
  z-index: 0;
  overflow: hidden;
  background: var(--user-card-bg, #2d326e);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  margin-bottom: 1.2rem;
  box-shadow: var(--shadow-subtle);
  color: #fff;
}
.user-card-widget::before {
  content: "";
  position: absolute;
  z-index: -1;
  bottom: -20%;
  right: -14%;
  width: 62%;
  aspect-ratio: 1;
  background-image: url('/icons/logo-mark-user.svg');
  background-size: contain;
  background-repeat: no-repeat;
  opacity: 0.16;
  pointer-events: none;
}

/* badge-general/badge-senior의 기본 색(파랑/앰버 계열 텍스트)은 이 카드의 남색
   배경 위에서는 거의 안 보인다(#14) - 이 카드 위에서만 흰 반투명 필로 덮어써
   항상 대비를 확보한다. */
.user-card-widget .badge-tag {
  background: rgba(255, 255, 255, 0.22);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.5);
}

.user-balance-big {
  font-size: 2.4rem;
  /* #redesign - 남발되던 900(Black)을 700(Bold)으로. 리뷰의 폰트 웨이트 규율("이름·잔액·
     타이틀 전부 800~900 남발 → 강조는 700까지만") 적용. */
  font-weight: 700;
  margin-top: 0.2rem;
}
/* "원" 단위는 숫자보다 가늘게 - 숫자 자체가 더 도드라지게(#redesign, user.js의
   renderUserBalance 참고). */
.user-balance-unit {
  font-size: 0.65em;
  font-weight: 500;
  margin-left: 0.1em;
}

/* 잔액 카드 아래 퀵액션 3개(#redesign) - QR 결제 / 충전 안내 / 이용 내역. */
.user-quick-actions {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 1.2rem;
}
.user-quick-actions button {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.85rem 0.4rem;
  background: var(--surface-1);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  color: var(--text-main);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}
.user-quick-actions button svg {
  color: var(--accent-cyan);
}
.user-quick-actions button:hover {
  border-color: var(--accent-cyan);
}

/* USER "충전" 탭 - 계좌 정보 카드 아래 "충전은 이렇게 진행돼요" 3단계 안내(#redesign,
   구 "문의" 탭을 대체하며 신설). 번호 배지 + 제목/설명 두 줄, 다른 곳과 마찬가지로
   원 배지는 --accent-cyan-glow 톤(#redesign 초록/빨강 정리 이후 브랜드 블루로 통일). */
.guide-steps {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.guide-step {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
}
.guide-step-num {
  flex: none;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--accent-cyan-glow);
  color: var(--accent-cyan);
  font-weight: 700;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.guide-step-body {
  min-width: 0;
}
.guide-step-title {
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--text-main);
}
.guide-step-desc {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin-top: 0.2rem;
  line-height: 1.4;
}

/* 충전 대기 카드(#18) - 메인 잔액 카드 바로 아래. 본인 앞으로 매칭된 미충전 입금 건 목록.
   #redesign2 - 예전엔 어드민 "처리 대기" 배너와 같은 앰버(경고) 톤이었는데, "들어온 돈"을
   문제처럼 보이게 한다는 피드백으로 경고색은 뺐다. 다만 대기 건은 회원이 직접 "충전하기"를
   눌러야 완료되는 필수 선행 작업이라 확실히 눈에 띄어야 해서(사용자 피드백), 주변 흰 카드들
   사이에서 옅은 에메랄드 워시 + 왼쪽 액센트 바 + "확인 필요" 뱃지 + 솔리드 행으로 대비를
   준다. 확정은 별도 모달 대신 행 안에서 "충전하기" → "확인/취소" 2단계(모달 사용성이 안
   좋다는 기존 요청 유지). 한 번에 한 행만 확인 대기 상태로 둔다. */
.pending-deposit-card {
  background: rgba(249, 115, 98, 0.10); /* 옅은 선홍/살구빛 워시 - "확인 필요"라는 대기 성격 + 따뜻한 톤, 흰 카드들 틈에서 떠 보이게 */
  border: 1px solid rgba(249, 115, 98, 0.42);
  border-left: 3px solid var(--accent-coral);
  border-radius: var(--radius-lg);
  padding: 1.2rem 1.25rem;
  margin-bottom: 1.2rem;
  box-shadow: var(--shadow-subtle);
  animation: pendingDepositIn 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes pendingDepositIn {
  from { opacity: 0; transform: translateY(-6px) scale(0.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.pending-deposit-header {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-bottom: 0.9rem;
}

/* 아이콘 - 이용 내역의 "계좌이체 충전"(카드+플러스)과 같은 모양. 선홍/살구빛 톤 칩에 담는다. */
.pending-deposit-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  color: var(--accent-coral);
  background: rgba(249, 115, 98, 0.18); /* color-mix 미지원 브라우저 폴백 */
  background: color-mix(in srgb, var(--accent-coral) 18%, transparent);
}

.pending-deposit-title {
  font-weight: 800;
  font-size: 1.02rem;
  color: var(--text-main);
}

/* "확인 필요" 뱃지 - 필수 액션임을 알리는 신호. 색은 액션 버튼(.pdrow-btn-go)과 같은
   남색으로 맞춰 "이 뱃지 = 저 버튼을 누르라는 것"으로 읽히게 한다. */
.pending-deposit-flag {
  flex-shrink: 0;
  margin-left: auto;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  color: #fff;
  background: var(--user-card-bg, #2d326e);
}

.pending-deposit-list {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

/* 행은 카드 표면색(흰/다크)으로 솔리드하게 - 선홍/살구빛 워시 배경 위에서 또렷하게 떠 보인다. */
.pending-deposit-row {
  position: relative;
  background: var(--bg-card, #fff);
  border: 1px solid var(--border-glass, rgba(0, 0, 0, 0.08));
  border-radius: var(--radius-md);
  overflow: hidden;
}

/* 기본 상태: 왼쪽에 금액(강조) + 시각, 오른쪽에 "충전" 트리거 버튼이 항상 보인다.
   (예전엔 행 아무 데나 눌러야 버튼이 나왔음) */
.pending-deposit-row-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.7rem 0.75rem 0.7rem 0.95rem;
  transition: opacity 0.15s ease;
}

.pending-deposit-row.armed .pending-deposit-row-main {
  opacity: 0;
  pointer-events: none;
}

.pending-deposit-row-info {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}

.pending-deposit-row-amount {
  font-weight: 800;
  color: var(--accent-coral);
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
}

.pending-deposit-row-date {
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* 확인 단계 - 같은 행 위에 겹쳐서(높이 흔들림 없이) 표시. "충전" 누르면 이 레이어가
   기본 레이어를 대체한다. */
.pending-deposit-confirm {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0 0.75rem 0 0.95rem;
  opacity: 0;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.pending-deposit-row.armed .pending-deposit-confirm {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.pending-deposit-confirm-text {
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--text-main);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pending-deposit-confirm-actions {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}

/* 행 내부 버튼 - 앱 전역 버튼(.btn/.btn-action)보다 한 톤 작게. "충전하기"/"확인"은
   잔액 카드·QR 탭 배지와 같은 플랫 남색(--user-card-bg), "취소"는 고스트(테두리만). */
.pdrow-btn {
  min-height: 0;
  padding: 0.4rem 0.9rem;
  font-size: 0.83rem;
  font-weight: 700;
  border-radius: var(--radius-sm, 8px);
  border: 1px solid transparent;
  white-space: nowrap;
}

.pdrow-btn-go,
.pdrow-btn-confirm {
  color: #fff;
  background: var(--user-card-bg, #2d326e);
}

.pdrow-btn-cancel {
  color: var(--text-muted);
  background: transparent;
  border-color: var(--border-glass, rgba(0, 0, 0, 0.15));
}

.pdrow-btn:disabled {
  opacity: 0.5;
}

.table-custom {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

.table-custom th,
.table-custom td {
  padding: 0.65rem 0.75rem;
  text-align: left;
  white-space: nowrap;
}

.table-custom th {
  color: var(--text-muted);
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 1px solid var(--border-glass);
}

.table-custom tbody tr {
  border-bottom: 1px solid var(--border-glass);
}

.table-custom tbody tr:hover {
  background: var(--surface-1);
}

/* Zero-Clipping Modal Dialog System */
.modal-overlay, [role="dialog"] {
  position: fixed;
  inset: 0;
  background: rgba(4, 7, 13, 0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: clamp(0.5rem, 2vw, 1rem);
}

.modal-overlay.active {
  display: flex;
  opacity: 1;
  pointer-events: auto;
}

.modal-content, .modal-box-large {
  background: var(--modal-bg);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-lg);
  padding: clamp(1rem, 2.5vw, 1.5rem);
  width: min(94vw, 480px) !important;
  max-width: 100% !important;
  max-height: 86vh !important;
  max-height: 86dvh !important;
  display: flex !important;
  flex-direction: column !important;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8);
  overflow: hidden;
}

/* 관리자 설정 모달은 기기 설정 그리드/메뉴 목록 표를 담기엔 기본 480px이 너무 좁아
   라벨이 줄바꿈되고 불필요한 스크롤이 생김 - 더 넓게 확보 (ID 특이도로 위 규칙을 이김) */
#kiosk-admin-modal.modal-overlay .modal-box-large {
  width: min(94vw, 900px) !important;
  /* 탭(메뉴 설정/로그 확인) 콘텐츠 높이가 서로 달라도 모달 크기가 탭 전환마다
     바뀌지 않도록 max-height뿐 아니라 height 자체를 고정한다. */
  height: 86vh !important;
  height: 86dvh !important;
}

/* 결제 결과 팝업은 다른 모달과 달리 내부에 .modal-body 스크롤 래퍼가 없어서, 폰 가로모드처럼
   뷰포트가 낮을 때(예: 844x390) .modal-box-large의 max-height:86vh + overflow:hidden에
   걸려 "결제 차감 금액"/안내문이 잘려서 아예 안 보이는 버그가 있었다 - 잘리는 대신
   스크롤되도록 세로 스크롤만 허용(ID 특이도로 overflow:hidden 이김, overflow-x는 그대로
   hidden 유지). */
#modal-box {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* 결제 결과 팝업(kiosk.js showModal) 텍스트/간격 - 처음엔 max-height:560px 브레이크포인트
   하나로 "폰 가로면 작게" 식으로 나눴는데, 실제 기기 높이가 390/375/360...으로 제각각이라
   한 지점에서만 줄이면 그 경계 바로 아래 높이에서 또 스크롤이 생겼다(844x390 실측:
   row 레이아웃 적용 후에도 385/333 - 52px 초과). rem(태블릿/폰 세로처럼 높이가 넉넉할 때의
   원래 크기, 상한)과 vh(부족하면 그에 비례해 계속 작아지는 하한 방향)를 clamp로 같이 써서
   특정 기기에 맞춘 게 아니라 "이 뷰포트 높이엔 얼마"가 연속적으로 나오게 했다 - 어떤 높이든
   같은 공식이 적용되니 특정 기기만 스크롤이 남는 일이 없다. */
#payment-modal .modal-result-icon {
  display: flex;
  justify-content: center;
  font-size: clamp(1.8rem, 7vh, 4rem);
  margin-bottom: clamp(0.15rem, 1vh, 0.5rem);
}

#payment-modal .modal-result-title {
  font-size: clamp(1.15rem, 5vh, 2.2rem);
  margin-bottom: clamp(0.15rem, 1vh, 0.5rem);
}

#payment-modal .modal-result-name-row {
  margin-bottom: clamp(0.4rem, 2vh, 1.2rem);
}

#payment-modal .modal-result-name {
  font-size: clamp(0.95rem, 3vh, 1.5rem);
  margin-left: 0.5rem;
}

#payment-modal .modal-result-message {
  font-size: clamp(1.05rem, 4vh, 1.4rem);
  margin: clamp(0.8rem, 3vh, 1.5rem) 0 0;
  color: var(--text-main);
}

/* 결제 금액/잔액 박스 - 기본은 위아래로 쌓아 각 값을 크게 보여주지만, 폰 가로모드처럼
   세로 공간이 부족한 뷰포트에서는 같은 두 값을 좌우로 나란히 놓아 세로 한 줄만큼 더
   절약한다(순서 결제 금액 좌 → 잔액 우 는 기본 레이아웃의 위→아래 순서와 대응). 안쪽
   패딩/글자 크기도 위와 같은 이유로 vh 비례. */
#payment-modal .modal-amount-box {
  display: flex;
  flex-direction: column;
  gap: clamp(0.3rem, 1.6vh, 0.8rem);
  padding: clamp(0.6rem, 2.6vh, 1.5rem);
  margin: clamp(0.5rem, 2vh, 1.2rem) 0;
  background: var(--surface-2);
  border-radius: 18px;
}

#payment-modal .modal-amount-label {
  color: var(--text-muted);
  font-size: clamp(0.78rem, 2.2vh, 1.1rem);
}

#payment-modal .modal-amount-value {
  font-weight: 900;
  color: var(--text-main);
  font-size: clamp(1.15rem, 4vh, 1.8rem);
}

#payment-modal .modal-amount-value-hero {
  font-weight: 900;
  color: var(--accent-emerald);
  font-size: clamp(1.3rem, 4.6vh, 2.2rem);
}

@media screen and (max-height: 560px) {
  #payment-modal .modal-amount-box {
    flex-direction: row;
  }
  #payment-modal .modal-amount-col {
    flex: 1 1 0;
    min-width: 0;
  }
}

.modal-body {
  overflow-y: auto !important;
  -webkit-overflow-scrolling: touch;
  flex: 1 1 auto;
  padding-right: 0.25rem;
}

.modal-footer {
  flex-shrink: 0;
  padding-top: 0.75rem;
  margin-top: 0.5rem;
  border-top: 1px solid var(--border-glass);
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
}

/* Responsive Grid Layouts */
.grid-menu {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 0.75rem;
}

/* Screen Size Media Queries */
@media screen and (max-width: 480px) {
  .grid-menu {
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
  }
  .nav-pills {
    display: none; /* Hide top pills on tiny portrait screens to save header space */
  }
  .btn-action {
    width: 100%;
  }
}

@media screen and (min-width: 768px) {
  .kiosk-body {
    display: grid !important;
    grid-template-columns: 1.6fr 1fr !important;
    height: calc(100vh - 56px) !important;
    overflow: hidden !important;
    gap: 0.85rem !important;
    padding: 0.85rem !important;
  }
  .kiosk-menu-section, .kiosk-checkout-section {
    height: 100% !important;
    overflow-y: auto !important;
    margin-bottom: 0 !important;
  }
}

/* Galaxy S25 Ultra Landscape Mode (Low Height) Optimization */
@media screen and (min-width: 768px) and (max-height: 500px) {
  .kiosk-body {
    height: calc(100vh - 46px) !important;
    padding: 0.5rem !important;
    gap: 0.6rem !important;
  }
  .kiosk-checkout-section {
    padding: 0.75rem !important;
    overflow-y: hidden !important;
  }
  .section-title {
    font-size: 0.95rem !important;
    margin-bottom: 0.4rem !important;
  }
  /* 결제 금액 타이틀은 초기화 버튼과 한 줄 정렬돼야 하므로 예외적으로 margin-bottom 0 유지 */
  .kiosk-checkout-section .section-title {
    margin-bottom: 0 !important;
  }

  /* 결제 금액 / 최근 결제 내역 블록이 각자 콘텐츠 높이만큼만 차지하고 남는 공간을 그냥
     비워두던 것을, 아래 min-height:700px 블록과 동일한 방식(2분할 flex)으로 바꾼다 -
     이 저해상도 가로 모드는 세로 공간이 워낙 빠듯해서(#35) 콘텐츠 실높이 합이 섹션 높이에
     거의 딱 맞아떨어져 "최근 결제 내역" 카드 아래 여백이 사실상 0으로 보였다. flex로
     남는 공간을 나눠주고 내부 리스트가 스크롤되게 하면 카드 자체는 항상 여유 있는 높이를
     유지한다. 1:1로 반씩 나눴더니 최근 결제 내역은 항목이 몇 줄 없어도 늘 크게 비어 보이고
     정작 "선택된 메뉴" 목록은 좁아 보인다는 피드백이 있어, 결제 금액 쪽에 더 많은 비중을 줌. */
  .kiosk-checkout-section > #kiosk-checkout-amount-block {
    flex: 2 1 0 !important;
    min-height: 0 !important;
    margin-top: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }
  .kiosk-checkout-section > #kiosk-checkout-recent-block {
    flex: 1 1 0 !important;
    min-height: 0 !important;
    margin-top: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }
  #kiosk-checkout-amount-grid {
    flex: 1;
    min-height: 0;
    padding: 0.5rem !important;
    gap: 0.4rem !important;
  }
  /* "선택된 메뉴" 라벨 + 카트 목록을 담은 위쪽 서브패널 - 예전 HTML의 justify-content:center는
     카트가 비었을 때 안내 문구를 세로 중앙에 두려던 의도인데, flex:1 min-height:0인 이
     서브패널 자체가 grid 안에서 남는 공간을 받아도 justify-content:center 때문에 카트 목록이
     그 공간을 채우지 못하고 라벨 바로 아래 붙어버린다. flex-start로 바꿔 카트 목록이 남는
     세로 공간을 실제로 차지하게 한다. */
  #kiosk-checkout-amount-grid > div:first-child {
    justify-content: flex-start !important;
  }
  #cart-items-container {
    max-height: none !important;
    flex: 1;
    min-height: 2.4rem;
  }
  #kiosk-checkout-recent-block {
    display: flex !important;
  }
  #recent-payments-container {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  #recent-payments-list {
    max-height: none !important;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
  }
}

/* Payment Success Screen Edge Green Glow Animation */
@keyframes greenEdgePulse {
  0% { box-shadow: inset 0 0 0px rgba(16, 185, 129, 0); }
  35% { box-shadow: inset 0 0 80px rgba(16, 185, 129, 0.95), 0 0 40px rgba(16, 185, 129, 0.8); }
  100% { box-shadow: inset 0 0 0px rgba(16, 185, 129, 0); }
}
.success-glow-active {
  animation: greenEdgePulse 1.4s ease-out forwards;
}

/* Unregistered Card / Error Edge Red Glow Animation */
@keyframes redEdgePulse {
  0% { box-shadow: inset 0 0 0px rgba(239, 68, 68, 0); }
  35% { box-shadow: inset 0 0 80px rgba(239, 68, 68, 0.95), 0 0 40px rgba(239, 68, 68, 0.8); }
  100% { box-shadow: inset 0 0 0px rgba(239, 68, 68, 0); }
}
.error-glow-active {
  animation: redEdgePulse 1.4s ease-out forwards;
}

/* Insufficient Balance Edge Yellow Glow Animation */
@keyframes yellowEdgePulse {
  0% { box-shadow: inset 0 0 0px rgba(245, 158, 11, 0); }
  35% { box-shadow: inset 0 0 80px rgba(245, 158, 11, 0.95), 0 0 40px rgba(245, 158, 11, 0.8); }
  100% { box-shadow: inset 0 0 0px rgba(245, 158, 11, 0); }
}
.warning-glow-active {
  animation: yellowEdgePulse 1.4s ease-out forwards;
}

.kiosk-wrapper {
  display: flex;
  flex-direction: column;
  height: 100vh;
  /* 모바일에서 100vh는 주소창이 숨은 "가장 큰" 뷰포트라, 최초 렌더 때 실제 보이는
     영역보다 커서 wrapper 하단(결제 금액 섹션·메뉴 목록 밑부분)이 화면 밖으로 밀려
     "하단이 잘려 보이다가 회전/스크롤 후 정상"이 되는 원인이 된다. 실제 보이는 높이를
     추적하는 100dvh로 덮어쓴다(미지원 브라우저는 무시하고 위 100vh 유지). */
  height: 100dvh;
  overflow: hidden;
  background-color: var(--bg-dark);
}

.kiosk-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.75rem;
  overflow-y: auto;
}

/* Redesigned Section Title Wording Style */
.section-title {
  font-size: 1.15rem !important;
  font-weight: 800;
  color: var(--accent-cyan);
  margin-bottom: 0.75rem;
  border-left: 3px solid var(--accent-cyan);
  padding-left: 0.5rem;
  line-height: 1.2;
}

/* Checkout cart and scanning boxes layout styling */
.kiosk-checkout-section {
  display: flex !important;
  flex-direction: column !important;
  gap: 0.85rem !important;
  padding: 1rem !important;
  justify-content: flex-start !important;
}

/* 모바일(768px 미만) 세로 스택(#redesign, 사용자 피드백: "결제 금액 섹션이 뷰포트 하단에
   위치하고, 위쪽은 메뉴 선택으로 채워지면 좋겠다") - 두 섹션 다 콘텐츠 높이만큼만
   차지해서, 메뉴/결제 내역이 짧으면 .kiosk-body(flex:1, 뷰포트 남는 높이 전부)의 빈
   공간이 결제 금액 카드 "아래"에 그냥 남아 있었다. 메뉴 선택 쪽에 flex:1을 줘서 남는
   공간을 흡수하게 하면(넘치면 이 섹션 안에서만 스크롤), 결제 금액 카드는 자기 콘텐츠
   높이만큼만 차지하며 자연히 뷰포트 하단에 붙는다. 태블릿 그리드 레이아웃
   (min-width:768px, 아래 규칙)은 이미 각 섹션에 height:100%를 줘서 영향 없음. */
@media screen and (max-width: 767.98px) {
  /* 세로 스택에서 메뉴 목록이 자기 영역 안에서만 스크롤되도록:
       .kiosk-body(flex column) > .kiosk-menu-section(flex column) >
       #kiosk-menu-content-wrap(flex column) > #kiosk-menu-grid(스크롤)
     체인을 전부 flex:1 1 auto + min-height:0 으로만 잇는다. height:100% 는
     쓰지 않는다 - 부모 높이가 정해지기 전(첫 렌더/데이터 로드 전)에는 안 잡혀서
     한 번 회전(리플로우)해야 제대로 보이는 증상이 난다. 이전엔 absolute + inset
     으로 래퍼 높이를 확정했는데, 그 방식도 containing block(섹션) 높이가 flex 로
     늦게 정해지면 첫 렌더에서 stale 해지는 같은 타이밍 버그가 있었다.
     순수 flex 체인은 콘텐츠가 언제 들어와도 레이아웃 시점에 다시 계산된다. */
  .kiosk-menu-section {
    display: flex !important;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    /* 인라인 overflow:hidden 유지 - 섹션 자체는 스크롤 컨테이너로 만들지 않는다
       (QR 카메라 오버레이 #kiosk-camera-viewport-container 가 스크롤에 안 딸려가도록). */
  }
  #kiosk-menu-content-wrap {
    /* 인라인 style="display:block; height:100%" 덮어쓰기 - height:100% 제거하고
       flex 자식으로서 남는 세로 공간을 받는다. */
    height: auto !important;
    display: flex !important;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
  }
  #kiosk-menu-content-wrap > div:first-child {
    flex: 0 0 auto;   /* "메뉴 선택" 제목 줄은 고정 높이 */
  }
  #kiosk-menu-content-wrap > .menu-grid {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    /* 위쪽 여백은 스크롤 컨테이너 padding-top으로. 아래 여백은 padding-bottom이 일부
       안드로이드 WebView에서 scrollHeight에 안 잡혀서 마지막 카드가 잘려 보이므로
       마지막 카드 margin-bottom으로 준다(모든 브라우저에서 스크롤 범위에 포함). */
    padding-top: 0.25rem;
  }
  #kiosk-menu-grid > .menu-card:last-child {
    margin-bottom: 0.9rem;
  }
  /* 메뉴 카드를 2행(이름 / 가격+수량)으로 바꾸는 규칙은 style.css 뒤쪽,
     base ".menu-grid .menu-card"(height:88px 등) 선언보다 나중에 와야 소스 순서로
     이겨서 아래 별도 미디어 블록에 둔다. */
}

.checkout-cart-box {
  background: rgba(0, 0, 0, 0.25);
  border-radius: var(--radius-md);
  padding: 0.85rem;
  border: 1px solid var(--border-glass);
}

/* "선택된 메뉴" 아래 담긴 항목 목록(kiosk.js updateCheckoutSummary가 .cart-item으로 생성) -
   색 지정이 없어 상속에 맡겨져 있었는데 실제로는 잘 안 보인다는 피드백이 있어 위 "선택된
   메뉴" 레이블과 동일한 색으로 명시. 글자 크기/들여쓰기(#redesign, 사용자 피드백)는
   "선택된 메뉴" 레이블 아래 딸린 하위 항목이라는 위계가 보이도록 살짝 작고 안으로. */
.cart-item {
  color: var(--text-muted);
  padding: 0.2rem 0;
  font-size: 0.88rem;
  padding-left: 0.5rem;
}
/* 비었을 때 안내 문구("선택된 메뉴가 없습니다.")도 담긴 항목과 같은 크기·행 높이로,
   위쪽 여백 없이. 정렬만 가운데 - 왼쪽 들여쓰기를 취소해야 진짜 가운데로 보인다. */
.cart-item--empty {
  padding-left: 0;
  text-align: center;
}

/* "카드를 대주세요" 결제 유도 패널(#redesign, kiosk.html의 kiosk-tap-card-panel) - 메뉴를
   담아도 다음에 뭘 해야 하는지 안내가 없다는 게 실제 셀프 키오스크 대조 리뷰의 가장 큰
   지적이었다. 장바구니가 비어있으면 눈에 덜 띄게 죽여두고(.is-active 없음), 담기는 순간
   kiosk.js updateCheckoutSummary가 .is-active를 얹어 강조한다. */
.kiosk-tap-card-panel {
  /* 사용자 요청으로 일단 숨김(#redesign) - 아래 flex 레이아웃/.is-active 강조 규칙은
     되살릴 때 바로 쓸 수 있게 그대로 둔다. */
  display: none;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
  padding: 0.65rem;
  border-radius: 12px;
  border: 1px solid var(--border-glass);
  background: var(--surface-1);
  color: var(--text-muted);
  font-size: 0.95rem;
  font-weight: 700;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.kiosk-tap-card-panel .kiosk-tap-card-icon {
  display: inline-flex;
  font-size: 1.15em;
}
.kiosk-tap-card-panel.is-active {
  background: var(--accent-cyan-glow);
  border-color: var(--accent-cyan);
  color: var(--accent-cyan);
  animation: kioskTapCardPulse 1.8s ease-in-out infinite;
}
@keyframes kioskTapCardPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}

/* 메뉴 미선택 상태로 카드를 대려 할 때 잠깐 보였다 사라지는 인라인 힌트
   (kiosk.html의 kiosk-no-menu-hint, kiosk.js의 flashNoMenuHint 참고). */
@keyframes kioskHintPop {
  0% { opacity: 0; transform: translateY(-2px); }
  15%, 85% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; }
}
#kiosk-no-menu-hint.is-active {
  display: flex !important;
  animation: kioskHintPop 2.4s ease;
}

.checkout-scan-box {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.desc-text-senior {
  font-size: 0.82rem !important;
  color: var(--accent-senior) !important;
  margin-top: 0.35rem;
  text-align: right;
  font-weight: 500;
}

.scanner-title {
  font-size: 1.18rem !important;
  color: #fff !important;
  font-weight: 800;
  margin-bottom: 0.25rem;
}

.desc-text {
  color: var(--text-muted) !important;
  font-size: 0.85rem !important;
  margin-bottom: 0.75rem;
}

.camera-scanner-panel {
  background: rgba(15, 23, 42, 0.6) !important;
  border: 1px solid rgba(6, 182, 212, 0.25) !important;
  border-radius: var(--radius-md) !important;
  padding: 0.65rem !important;
  text-align: center;
}

.scanner-live-badge {
  font-size: 0.78rem;
  color: #67e8f9;
  font-weight: 700;
  background: rgba(6, 182, 212, 0.12);
  padding: 0.15rem 0.45rem;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(6, 182, 212, 0.2);
}

.status-msg-text {
  font-size: 0.78rem !important;
  color: var(--text-dim) !important;
  margin-top: 0.35rem;
}

/* Admin Scan Grid - Vertical stack on mobile to prevent touch element overlap */
.admin-scan-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media screen and (min-width: 768px) {
  .admin-scan-grid {
    grid-template-columns: 1fr 1fr;
    gap: 1.2rem;
  }
}

/* 전면 카메라 미러 모드 - .mirror-mode 클래스가 붙은 video 요소만 좌우반전 */
.mirror-mode {
  transform: scaleX(-1);
  -webkit-transform: scaleX(-1);
}

/* 관리자 "키오스크 관리" 탭 - 제목/카드 헤더 옆 선택기(누르면 바로 아래로 목록이 펼쳐지는
   드롭다운 트리거). 키오스크 선택기와 메뉴별 매출 기간 선택기가 같은 패턴을 공유한다 -
   트리거를 담은 행에 position: relative를 주고, 목록을 그 아래에 절대배치한다. */
.kiosk-title-row {
  position: relative;
}
.kiosk-selector-btn {
  display: flex !important;
  align-items: center;
  gap: 0.3rem;
  background: none;
  border: none;
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.82rem;
  padding: 0.2rem 0.3rem;
  min-height: auto;
  text-align: right;
}
.kiosk-selector-arrow-icon {
  display: inline-flex;
  font-size: 0.85em;
  transition: transform 0.2s ease;
  color: var(--text-muted);
  flex-shrink: 0;
}
.kiosk-selector-list, .kiosk-sales-period-list {
  display: none;
  flex-direction: column;
  gap: 0.3rem;
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 0.4rem;
  background: var(--bg-card);
  backdrop-filter: var(--backdrop-blur);
  -webkit-backdrop-filter: var(--backdrop-blur);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  padding: 0.4rem;
  box-shadow: var(--shadow-subtle);
  /* 하단 고정 탭바(.admin-tabbar, z-index:90)보다 위에 오도록 - 화면 아래쪽에서 열려
     JS로 미처 위쪽으로 뒤집기 전 한 프레임 동안 가려지는 것까지 막는 안전장치. */
  z-index: 95;
}
.kiosk-selector-list {
  min-width: 12rem;
  max-width: min(22rem, 90vw);
  max-height: 60vh;
  overflow-y: auto;
}
.kiosk-sales-period-list {
  min-width: 7rem;
}
/* 트리거 아래쪽에 펼칠 공간이 부족하면(fitDropdownVertically) 위로 뒤집어 연다. */
.kiosk-selector-list.drop-up, .kiosk-sales-period-list.drop-up {
  top: auto;
  bottom: 100%;
  margin-top: 0;
  margin-bottom: 0.4rem;
}
.kiosk-selector-empty {
  padding: 0.6rem 0.2rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.kiosk-selector-item-main {
  display: flex !important;
  align-items: center;
  width: 100%;
  min-height: auto;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.8rem;
  text-align: left;
}
.kiosk-selector-item-name {
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--text-main);
}
.kiosk-selector-item-main.active {
  border-color: var(--accent-cyan);
  background: rgba(103, 129, 192, 0.1);
}

/* 키오스크 상세 정보 - 이름 아래 UUID(키오스크 ID). 눌러서 복사할 수 있는 칩으로
   바꿨다(#redesign) - 등록/문의 시 단말기 UUID를 손으로 옮겨 적던 걸 대신한다. */
.kiosk-detail-uuid {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  width: 100%;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-family: monospace;
  background: var(--surface-2);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  padding: 0.55rem 0.8rem;
  cursor: pointer;
  text-align: left;
}
.kiosk-detail-uuid-text {
  /* 전역 button 리셋(위 "Global Mobile Touch & Button Protection")이 button 태그 자체에
     white-space:nowrap/word-break:keep-all을 !important로 강제한다 - 자식인 이 span은
     상속으로 그 값을 그대로 물려받으므로, 여기서 직접 다시 선언해 override해야 UUID가
     좁은 화면에서 줄바꿈된다(부모의 !important는 부모 자신에게만 적용되고, 자식이 값을
     직접 선언하면 상속보다 그 선언이 우선한다). */
  white-space: normal;
  word-break: break-all;
  overflow-wrap: anywhere;
  min-width: 0;
}
.kiosk-detail-uuid-copy-icon {
  flex-shrink: 0;
  font-size: 1rem;
  color: var(--text-muted);
}

/* 키오스크 관리 탭 - 카드 목록. 예전엔 상단에 칩을 늘어놓고 그 아래 상세를 같이 보여줬는데
   (#redesign), 대수가 늘어나면 칩 줄이 계속 옆으로 밀려 알아보기 힘들었다 - 카드를 누르면
   상세 화면으로 들어가게 바꿨다(#redesign2, admin.js의 renderKioskFeed/openKioskDetail
   참고). 목록 자체는 한때 전용 스크롤 박스를 고정 높이로 잘라 그 안에서만 스크롤했는데
   (--kiosk-view-h), 회원 관리 탭과 달리 이 탭은 그 높이 계산이 어긋나 바깥 페이지 스크롤과
   같이 "2단 스크롤"이 되는 문제가 있었다(#redesign3, 사용자 리포트) - 전용 스크롤 박스
   없이 그냥 페이지(window) 전체가 스크롤되게 바꿨다. */
.kiosk-feed {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* 온라인 상태 점 - 회색(오프라인)/초록(온라인, is-online). 선택 목록 행과 상세 패널
   양쪽에서 재사용한다(#redesign). */
.kiosk-online-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-dim, var(--text-muted));
  flex-shrink: 0;
}
.kiosk-online-dot.is-online {
  background: var(--accent-emerald);
  box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
}
.kiosk-online-status {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.82rem;
  color: var(--text-muted);
}

/* 메뉴별 매출 막대 리스트(#redesign) - 표 대신 이 기간 최고 매출 대비 비율로 막대를
   그려서 뭐가 제일 잘 팔리는지 숫자를 안 읽어도 한눈에 들어오게 한다. */
.kiosk-sales-bar-row {
  margin-bottom: 0.9rem;
}
.kiosk-sales-bar-row:last-child {
  margin-bottom: 0;
}
.kiosk-sales-bar-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.3rem;
}
.kiosk-sales-bar-name {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--text-main);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kiosk-sales-bar-amount {
  flex-shrink: 0;
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--accent-emerald);
  font-variant-numeric: tabular-nums;
}
.kiosk-sales-bar-track {
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  overflow: hidden;
}
.kiosk-sales-bar-fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--accent-cyan);
}
.kiosk-sales-bar-qty {
  margin-top: 0.25rem;
  font-size: 0.75rem;
  color: var(--text-muted);
}

/* 키오스크에 배정할 메뉴 그리드 - 실제 키오스크 화면(.menu-card)과 같은 모양으로 2열,
   카드를 누르면 배정 활성/비활성이 토글된다(체크박스 대신 버튼 느낌). */
.kiosk-menu-assign-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin-top: 0.5rem;
}
.kiosk-menu-assign-grid .menu-card {
  cursor: pointer;
  min-height: auto !important;
  opacity: 0.5;
  filter: grayscale(0.4);
  position: relative;
  transition: opacity 0.15s ease, filter 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.kiosk-menu-assign-grid .menu-card.assigned {
  opacity: 1;
  filter: none;
  border-color: var(--accent-cyan);
  box-shadow: 0 0 0 1.5px var(--accent-cyan);
}
.kiosk-menu-assign-grid .menu-card .menu-toggle-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-cyan);
  color: #fff;
  font-size: 0.65rem;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.kiosk-menu-assign-grid .menu-card.assigned .menu-toggle-badge {
  opacity: 1;
  transform: scale(1);
}

/* 어드민 "메뉴 관리" 모달 - 각 카드 우측 상단에 삭제 버튼, 마지막 칸은 "+" 추가 카드
   (.kiosk-menu-assign-grid와 같은 2열 카드 그리드를 재사용하되 배정 토글이 아니라
   삭제/추가 액션이라 별도 클래스로 분리). */
.menu-manage-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin-top: 0.5rem;
}
.menu-manage-grid .menu-card {
  min-height: auto !important;
  position: relative;
}
.menu-manage-grid .menu-card .menu-delete-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 28px;
  height: 28px;
  min-height: auto !important;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  color: var(--icon-danger, #fca5a5);
  border: none;
  cursor: pointer;
  font-size: 0.85rem;
}
.menu-manage-grid .menu-card-add {
  cursor: pointer;
  display: flex !important;
  flex-direction: row !important;
  align-items: center !important;
  justify-content: center !important;
  border: 1.5px dashed var(--border-glass);
  box-shadow: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 1.6rem;
  line-height: 1;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.menu-manage-grid .menu-card-add:hover {
  border-color: var(--accent-emerald);
  color: var(--accent-emerald);
}

/* Kiosk Menu Card Aspect Ratio & Compact Button Fix */
.menu-grid {
  display: grid !important;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)) !important;
  gap: 0.75rem !important;
}

.menu-card {
  background: var(--bg-card);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  padding: 0.85rem !important;
  display: flex !important;
  flex-direction: column !important;
  justify-content: space-between !important;
  height: auto !important;
  min-height: 145px !important;
  max-height: 200px !important;
  box-shadow: var(--shadow-subtle);
}

.menu-card .menu-name {
  font-size: 0.95rem !important;
  font-weight: 800;
  margin-bottom: 0.3rem;
  color: var(--text-menu-name, var(--text-main));
}

.menu-card .menu-price {
  font-size: 0.82rem !important;
  color: var(--text-muted);
}

.menu-card .menu-price-senior {
  font-size: 0.82rem !important;
  color: var(--accent-senior);
  font-weight: 500;
}

/* 실제 키오스크 고객 화면(#kiosk-menu-grid)만 - 2열 카드 그리드 대신 메뉴 하나가 가로
   한 줄을 통째로 차지하도록 바꾸고(단일 열), "메뉴 이름 - 금액 - 개수" 3단 가로 배치로
   바꾼다(이름 아래에 가격을 붙이지 않고 가격을 별도 칸으로 오른쪽 정렬). 글씨도 전체적으로
   키운다. admin.js의 단말기별 메뉴 배정 화면(.kiosk-menu-assign-grid)은 여전히 2열 카드
   그리드+세로 스택을 그대로 쓰므로, 그쪽 .menu-card까지 같이 바뀌지 않도록 .menu-grid로
   범위를 좁혀 덮어쓴다.
   (Kiosk Menu Card Aspect Ratio & Compact Button Fix 블록의 기본값을 그대로 상속받고
   여기서 필요한 것만 다시 덮어쓴다.) */
.menu-grid {
  grid-template-columns: 1fr !important;
  /* CSS 그리드는 기본값(align-content: normal, 실질적으로 stretch)이 행 높이를 컨테이너의
     남는 세로 공간에 맞춰 늘린다 - 메뉴가 몇 개 없을 때 카드가 쓸데없이 커 보이는 원인이라,
     항목 수와 무관하게 카드 높이가 항상 고정값(아래 .menu-card height)을 유지하도록 위쪽부터
     제 크기로 쌓고 나머지는 그냥 빈 공간으로 남긴다. */
  align-content: start !important;
}
.menu-grid .menu-card {
  flex-direction: row !important;
  align-items: center !important;
  gap: 1.2rem;
  /* min/max가 아니라 고정 height - 메뉴 개수가 적어서 그리드에 남는 공간이 있어도(위
     align-content: start와 함께) 카드가 늘어나지 않고, 많아서 스크롤이 생겨도 줄어들지
     않는다. 위쪽 bare .menu-card 규칙(admin의 2열 카드 그리드용)이 min-height: 145px
     !important / max-height: 200px !important를 걸어두는데, height만 덮어쓰고 이 둘을
     그대로 두면 min-height가 height보다 커서 실제로는 145px로 렌더링된다(#35) - 여기서도
     같이 초기화해야 의도한 88px 고정 높이가 실제로 적용된다. */
  height: 88px !important;
  min-height: 88px !important;
  max-height: none !important;
}
/* 데스크톱/태블릿에서는 래퍼를 없애서(display:contents) 이름/가격/수량이 그대로 카드의
   직속 flex 자식으로 한 줄에 놓이게 한다. 모바일(<=767px)에서만 아래 블록이 이 래퍼를
   실제 flex 컬럼으로 되살려 이름 위 / 가격 아래로 쌓는다. */
.menu-grid .menu-card .menu-card-info {
  display: contents;
}
.menu-grid .menu-card .menu-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 1.4rem !important;
  margin-bottom: 0 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.menu-grid .menu-card .menu-price-block {
  flex-shrink: 0;
  text-align: right;
}
.menu-grid .menu-card .menu-price,
.menu-grid .menu-card .menu-price-senior {
  font-size: 0.95rem !important;
}
.menu-grid .menu-card .touch-qty-box {
  margin-top: 0 !important;
  flex-shrink: 0;
}
.menu-grid .touch-qty-btn {
  width: 40px !important;
  height: 40px !important;
  min-height: 40px !important;
  font-size: 1.3rem !important;
}
.menu-grid .touch-qty-val {
  font-size: 1.15rem !important;
  font-weight: 800;
  padding: 0 0.6rem;
  color: var(--text-muted);
  /* 최대 99(updateQty의 상한)까지는 자릿수가 늘어나도(1자리→2자리) 폭이 안 바뀌도록
     "99" 기준으로 고정 - ch 단위는 폰트별 "0" 글리프 폭 기준이라 Pretendard Black(800)
     굵기의 실제 두 자리 숫자 폭보다 작게 잡혀서(2ch, 2.6ch 둘 다 부족했음, 실측 결과
     2자리일 때 41px 필요) 폰트에 안 흔들리는 rem 고정값으로 교체. */
  min-width: 2.8rem;
  text-align: center;
}

.touch-qty-box {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  background: var(--surface-2) !important;
  border-radius: var(--radius-sm) !important;
  padding: 0.2rem !important;
  margin-top: 0.6rem !important;
}

.touch-qty-btn {
  width: 32px !important;
  height: 32px !important;
  min-height: 32px !important;
  padding: 0 !important;
  font-size: 1.1rem !important;
  font-weight: bold !important;
  border-radius: 6px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
}

/* 좁은 세로 화면(모바일)에서는 "이름 · 가격 · 수량"을 한 줄에 못 담아 메뉴 이름이 2글자에서
   잘렸다(사용자 피드백). 카드를 flex 두 칸으로:
     - 왼쪽(.menu-card-info, flex 컬럼): 이름 위 / 가격(일반·시니어 세로) 아래
     - 오른쪽(.touch-qty-box): 수량 버튼 - 카드 세로 중앙
   base ".menu-grid .menu-card"(height:88px, gap:1.2rem 등)보다 뒤에 있어야 소스 순서로 이긴다. */
@media screen and (max-width: 767.98px) {
  /* 그리드 행을 카드 실제 높이에 맞춤 - 기본 auto 행이라면 카드(그리드 아이템)가
     align-self:stretch로 88px짜리 행에 늘려 맞춰지면서 height:auto가 무력화돼 이름+가격
     2줄이 세로로 찌부러진다(위쪽 패딩이 잘려 보이는 원인). */
  .menu-grid {
    grid-auto-rows: max-content !important;
  }
  .menu-grid .menu-card {
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    align-self: start !important;    /* 행에 억지로 늘어나지 않고 제 콘텐츠 높이를 유지 */
    gap: 0.85rem;
    height: auto !important;         /* 두 줄(이름+가격)이 들어갈 만큼 카드가 늘어나야 함 */
    min-height: 88px !important;
    max-height: none !important;
    padding: 0.85rem 1rem !important;
  }
  .menu-grid .menu-card .menu-card-info {
    display: flex !important;        /* 데스크톱의 display:contents를 되살려 실제 컬럼으로 */
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    gap: 0.3rem;
  }
  .menu-grid .menu-card .menu-name {
    /* 이름을 왼쪽 열 폭만큼 다 보여준다 - 말줄임(overflow:hidden/nowrap) 대신 줄바꿈 허용.
       overflow:hidden을 그대로 두면 flex 아이템 최소 크기가 0이 돼 세로로도 찌부러진다. */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: keep-all;            /* 한글은 어절 단위로 끊기 */
    line-height: 1.25;
  }
  .menu-grid .menu-card .menu-price-block {
    text-align: left;
  }
  .menu-grid .menu-card .touch-qty-box {
    flex: 0 0 auto;
    margin-top: 0 !important;
  }
}

/* Large Kiosk Tablet Landscape Optimization (e.g. Galaxy Tab A11+ SM-X236, 1920x1200)
   Mobile/short-landscape layouts above are untouched by this block (min-height gate excludes them).
   Placed at end of file so it wins cascade ties (same specificity + !important) against the
   unconditional base rules defined earlier (e.g. .kiosk-checkout-section's justify-content).
   Spreads checkout content top-to-bottom and enlarges touch targets/typography instead of
   leaving large stretches of empty space below the compact mobile-sized layout. */
@media screen and (min-width: 768px) and (min-height: 700px) {
  /* 스타일시트 전반이 rem/clamp(rem,vw,rem) 단위를 쓰고 있어서, 루트 폰트 크기를 키우면
     개별 요소를 일일이 손보지 않아도 전체 글씨가 비례적으로 커진다.
     22px 고정값은 1920px 폭 태블릿 기준으로 잡은 값이라 1280px처럼 더 좁은 가로 태블릿에서는
     글자가 상대적으로 너무 커서 잘리거나 줄바꿈이 생긴다 - 폭에 비례해 17~22px 사이로 스케일. */
  html {
    font-size: clamp(17px, 1.15vw, 22px);
  }

  .kiosk-body {
    grid-template-columns: 1.5fr 1fr !important;
    gap: 1.5rem !important;
    padding: 1.75rem !important;
  }

  .kiosk-menu-section .section-title,
  .kiosk-checkout-section .section-title {
    font-size: 1.75rem !important;
    margin-bottom: 1.25rem !important;
  }

  /* 결제 금액 타이틀만 예외 - 옆에 초기화 버튼과 한 줄로 정렬돼야 해서(kiosk.html의
     inline margin:0 + align-items:center) 위 공통 규칙의 margin-bottom을 다시 0으로
     되돌린다. 안 그러면 !important끼리 부딪혀 이 규칙(더 뒤에 선언됨)이 이기면서
     inline margin:0가 무력화되고 타이틀만 아래로 처져 보인다. */
  .kiosk-checkout-section .section-title {
    margin-bottom: 0 !important;
  }

  /* 타이틀 아랫줄(메뉴 목록 시작 / "선택된 메뉴" 시작)의 위치를 서로 맞춤 - 원래 메뉴 목록
     쪽이 더 아래(margin-bottom 0.6rem)에서 시작하고 "선택된 메뉴" 쪽은 더 위(카드 패딩
     0.7rem만)에서 시작해 어긋나 있었다. 둘 사이 간격의 중간 지점에서 만나도록 메뉴 쪽은
     줄이고 결제 쪽은 늘림(각각 ~6px, 이 브레이크포인트의 루트 폰트 17px 기준 0.36rem). */
  #kiosk-menu-content-wrap > div:first-child {
    margin-bottom: 0.24rem !important;
  }
  #kiosk-checkout-amount-grid {
    margin-top: 0.73rem;
  }

  /* 초기화 버튼 높이를 결제 금액 타이틀의 줄박스 높이(font-size 1.75rem * line-height 1.2)와
     맞춰 위/아래 선이 둘 다 일치하게 함 - 기존엔 버튼 자체 패딩으로 높이가 결정돼 타이틀보다
     더 컸다. */
  #kiosk-checkout-amount-block button {
    height: 2.1rem !important;
    min-height: 2.1rem !important;
  }

  /* 메뉴 그리드 영역 자체가 타이틀 아래 남는 세로 공간을 전부 차지하도록 늘림 - 단일 열이라
     행 수는 항상 메뉴 개수만큼이고(grid-auto-rows 기본값 auto), 다 못 담으면 이 블록
     .kiosk-menu-section의 overflow-y:auto(768px 미디어 쿼리)로 스크롤된다. */
  #kiosk-menu-content-wrap {
    display: flex !important;
    flex-direction: column !important;
    height: 100% !important;
  }

  .menu-grid {
    gap: 1.25rem !important;
    flex: 1;
  }

  /* 카드가 그리드 셀 전체(가로/세로)를 꽉 채우도록 - 기존 height:auto/max-height 캡을 해제
     (admin.js의 단말기별 메뉴 배정 화면(.kiosk-menu-assign-grid)에서 쓰는 2열 카드 그리드용). */
  .menu-card {
    height: 100% !important;
    max-height: none !important;
    padding: 1.4rem !important;
  }

  /* 실제 키오스크 고객 화면만 - 위 height:100%(그리드 셀 전체를 채움)를 고정값으로 덮어써서,
     메뉴가 몇 개 없어 그리드에 남는 공간이 있어도(.menu-grid의 align-content: start와 함께)
     카드가 늘어나지 않게 한다. .menu-grid .menu-card(0,2,0)가 바로 위 bare .menu-card(0,1,0)
     보다 특정성이 높아 이긴다. */
  .menu-grid .menu-card {
    height: 108px !important;
  }

  /* .menu-grid .menu-card ...(specificity 0,3,0)로 잡는 이유: 위쪽 "실제 키오스크 고객
     화면만" 블록의 기본(모바일) 폰트 크기 규칙과 동일한 형태의 선택자라 특정성이 같고, 이
     미디어 쿼리가 더 뒤에 선언돼 있어(그리고 매치될 때만) 태블릿에서 더 크게 이긴다 -
     특정성이 낮은 bare .menu-card 선택자를 썼다면 반대로 모바일 쪽 규칙에 져서 태블릿에서도
     작은 글씨가 나온다. */
  .menu-grid .menu-card .menu-name {
    font-size: 1.8rem !important;
    margin-bottom: 0 !important;
  }

  .menu-grid .menu-card .menu-price,
  .menu-grid .menu-card .menu-price-senior {
    font-size: 1.1rem !important;
  }

  .touch-qty-box {
    padding: 0.4rem !important;
  }

  .menu-grid .touch-qty-btn {
    width: 68px !important;
    height: 68px !important;
    min-height: 68px !important;
    font-size: 2.1rem !important;
  }

  .menu-grid .touch-qty-val {
    font-size: 1.7rem !important;
    font-weight: 800;
    min-width: 3.8rem;
    text-align: center;
  }

  /* 결제 금액 / 최근 결제 내역 2분할 — 카메라는 화면에 표시되지 않으므로 두 블록이 균등 배분.
     기존 .kiosk-checkout-section 규칙의 justify-content:flex-start는 더 이상 필요 없음
     (flex:1 자식들이 이미 전체 높이를 나눠 채움). */
  .kiosk-checkout-section {
    justify-content: flex-start !important;
    padding: 1.75rem !important;
    gap: 1.25rem !important;
  }

  /* 메뉴 선택 섹션의 가로 여백도 결제 금액 섹션(위)과 동일하게 - 안 맞추면 두 섹션의
     제목/목록이 태블릿에서 좌우로 서로 다른 폭만큼 들어가 보인다. */
  .kiosk-menu-section {
    padding: 1.75rem !important;
  }

  .kiosk-checkout-section > #kiosk-checkout-amount-block,
  .kiosk-checkout-section > #kiosk-checkout-recent-block {
    flex: 1 1 0 !important;
    min-height: 0 !important;
    margin-top: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }

  .checkout-cart-box {
    padding: 1.5rem !important;
  }

  #kiosk-total-price {
    font-size: 3rem !important;
  }

  /* 결제 금액 블록: 제목 아래 그리드 영역이 남는 세로 공간을 채우도록 */
  #kiosk-checkout-amount-grid {
    flex: 1;
    min-height: 0;
  }

  #cart-items-container {
    max-height: none !important;
    flex: 1;
    min-height: 0;
  }

  /* 최근 결제 내역 블록: 컨테이너 자체를 flex:1로 늘리고 내부 리스트가 그 공간을 채움 */
  #kiosk-checkout-recent-block {
    display: flex !important;
  }

  #recent-payments-container {
    padding: 1.2rem 1.4rem !important;
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  #recent-payments-list {
    max-height: none !important;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
  }

  .recent-payments-title {
    font-size: 1.25rem !important;
  }

  .recent-payments-subtitle {
    font-size: 1rem !important;
  }

  .recent-payment-row {
    font-size: 1.15rem !important;
    padding: 0.6rem 0.8rem !important;
    gap: 0.6rem;
  }

  .recent-payment-time {
    font-size: 1rem !important;
  }

  .recent-payment-name {
    font-size: 1.2rem !important;
  }

  .recent-payment-badge {
    font-size: 1rem !important;
    padding: 0.15rem 0.6rem !important;
  }

  .recent-payment-amount {
    font-size: 1.4rem !important;
  }

  .recent-payment-empty {
    font-size: 1.2rem !important;
  }
}

/* Tablet Portrait Layout (세로로 긴 태블릿, 예: 800x1280, 810x1080 이상)
   세로로 긴 화면은 폭 조건만 보는 위 min-height:700px 블록에도 걸려 좌우 반쪽 분할이 그대로
   적용되는데, 폭이 좁고 높이가 긴 화면에서는 이 가로 2분할이 부자연스럽다.
   대신 위(메뉴 선택 전체 폭) / 아래(좌: 결제 금액, 우: 최근 결제 내역) 구조로 재배치.
   이 블록이 캐스케이드상 위 min-height:700px 블록보다 뒤에 있으므로, 동일 특이도(!important)
   조건에서 겹치는 속성은 이 블록이 이긴다. */
@media screen and (min-width: 768px) and (orientation: portrait) {
  .kiosk-body {
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: none !important;
  }

  .kiosk-menu-section {
    flex: 1.4 1 0 !important;
    min-height: 0 !important;
  }

  .kiosk-checkout-section {
    flex: 1 1 0 !important;
    min-height: 0 !important;
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    grid-template-rows: minmax(0, 1fr) auto !important;
    gap: 1.25rem !important;
  }

  /* 좌: 결제 금액, 우: 최근 결제 내역 - 카메라가 화면에 표시되지 않으므로 그 자리를
     결제 금액이 대신 차지하고, 두 블록 모두 이 행의 높이 전체를 채운다. */
  .kiosk-checkout-section > #kiosk-checkout-amount-block {
    grid-column: 1 !important;
    grid-row: 1 !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }

  .kiosk-checkout-section > #kiosk-checkout-recent-block {
    grid-column: 2 !important;
    grid-row: 1 !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }

  /* 카드 대리 발급 패널이 열리면 하단에 좌우 폭 전체로 별도 행 추가 (평소엔 숨김 상태라 공간 차지 없음) */
  #admin-tab-card {
    grid-column: 1 / -1 !important;
    grid-row: 2 !important;
  }

  #kiosk-checkout-amount-grid {
    flex: 1;
    min-height: 0;
  }

  #cart-items-container {
    max-height: none !important;
    flex: 1;
    min-height: 0;
  }

  #recent-payments-container {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  #recent-payments-list {
    max-height: none !important;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
  }

  /* 최근 결제 내역 한 줄 항목도 좁은 컬럼 기준으로 살짝 축소 (가로 모드 700px+ 확대 폰트 기준 대비) */
  .recent-payment-row {
    font-size: 0.95rem !important;
  }

  .recent-payment-amount {
    font-size: 1.05rem !important;
  }
}

/* ---------------- 관리자 PIN 잠금 화면(#redesign) ----------------
   예전엔 그냥 텍스트 입력칸 + 버튼 하나뿐이라 실제 잠금 화면 느낌이 없었다. 원형 배지에
   자물쇠 아이콘을 얹고, 제목/설명을 가운데 정렬하고, 숫자 키패드를 더했다 - 키패드는
   위 입력칸(admin-pin-input)에 값을 그대로 채워 넣으므로 물리 키보드 타이핑과 병행해도
   충돌하지 않는다(admin.js의 appendAdminPinDigit 참고). */
/* PIN 잠금 화면(#redesign) - 사용자 피드백 두 차례 반영:
   1차("화면 상하 크기를 고려해야 할듯", "스크롤 하는건 별로야") 때는 원 지름을 뷰포트
   높이(vh)에 비례해 계속 줄이는 clamp()를 썼는데, 가로가 넓고 세로만 짧은 화면(랜드스케이프
   태블릿 등)에서 "이건 너무 작아"라는 지적을 받았다. 2차 피드백: "번호 자체 크기는
   세로방향일 때 정도를 최소 크기로 하고, 넘어가면 5열로 바꾸자" - 즉 원 크기를 계속
   줄이는 대신, 안 맞으면 아예 3열→5열로 배열을 바꿔 줄 수를 4줄→3줄로 줄이는 이진 전환
   방식으로 바꿨다. 기본(3열)은 원래 크기(모달 너비 기준 1fr, 세로 길이와 무관 - "포트레이트
   일 때 정도"가 최소 크기가 되도록 아예 안 줄임)로 되돌리고, 세로가 부족할 때만 아래
   @media (max-height)에서 5열 배열로 전환한다. */
.pin-lock-box {
  text-align: center;
}
/* .pin-lock-box는 .modal-box-large와 같은 요소에 같이 걸려서(class="modal-box-large
   pin-lock-box"), 그 flex-direction:column 컨테이너의 자식들이다 - flex-shrink 기본값
   1이라 컨테이너가 비좁으면 브라우저가 아이콘 등을 내용과 무관하게 짜부라뜨린다(버그,
   Playwright로 발견 - 아이콘 실측 높이가 지정한 64px보다 훨씬 작은 17px로 나옴).
   flex-shrink:0으로 꺼서 지정한 크기가 실제 최종 크기가 되게 한다. */
.pin-lock-box > * {
  flex-shrink: 0;
}
.pin-lock-icon {
  width: 48px;
  height: 48px;
  margin: 0 auto 0.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--accent-cyan);
  font-size: 1.4rem;
}
/* 원 지름을 84px로 캡 - 모달 자체는 최대 480px까지 넓어질 수 있어(.modal-box-large),
   폭 기준 1fr 그대로 두면 넓은 화면에서 원이 134px까지 커지면서 정작 3열 배열에
   필요한 세로 공간도 그만큼 늘어 아래 6열 전환 기준(max-height)을 무의미하게 크게
   잡아야 했다. (사용자 피드백: "세로모드는 좀 줄이고 가로모드는 더 키워서" 두 방향
   버튼/글씨 크기를 비슷하게 - 세로 100→84px, 아래 랜드스케이프 6열은 반대로 키움.) */
.pin-keypad {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 84px));
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
}
.pin-key {
  /* 전역 button { min-height: 42px } 규칙(위쪽)이 이 버튼에도 걸려서 aspect-ratio로
     정한 높이(원 지름)를 42px 밑으로는 못 내려가게 밀어 올려, 아래 5열 좁은 화면
     모드에서 원이 아니라 찌그러진 알약 모양이 됐다(.switch-toggle이 겪었던 것과 같은
     버그, #settings-redesign 참고). 여기서 그 하한을 0으로 무효화한다 - 기본(3열) 모드는
     원래도 42px보다 훨씬 크니 실질적인 변화 없음. */
  min-height: 0;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  border: 1px solid var(--border-glass);
  background: var(--surface-1);
  color: var(--text-main);
  /* #redesign(사용자 피드백: "숫자버튼이랑 핀 입력 창 폰트가 너무 큰 것 같애") -
     1.3rem → 1.1rem. */
  font-size: 1.1rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pin-key:active {
  background: var(--surface-2);
}
.pin-key-spacer {
  background: none;
  border-color: transparent;
  cursor: default;
}
.pin-key-backspace {
  background: none;
  color: var(--text-muted);
  font-size: 1.1rem;
}

/* PIN 입력칸 공용 자간(#redesign) - 값이 있을 때만 자릿수를 벌려 보이게. :placeholder-shown일
   때도 자간을 그대로 두면 짧은 플레이스홀더("PIN 입력" 등)까지 같이 벌어져 "P I N   입 력"
   처럼 깨져 보인다(버그, 키오스크 등록/PIN 화면 실기기 리뷰로 발견) - ADMIN PIN 인증
   입력칸도 같은 버그라 이 공용 클래스로 함께 고친다. */
.pin-code-input {
  text-align: center;
  /* #redesign(사용자 피드백: "숫자버튼이랑 핀 입력 창 폰트가 너무 큰 것 같애") -
     1.8rem → 1.5rem, 이후 세로모드 축소 요청으로 1.5 → 1.4rem. */
  font-size: 1.4rem;
  letter-spacing: 0.4rem;
}
.pin-code-input:placeholder-shown {
  letter-spacing: normal;
}
/* 키패드와 아래 취소/인증하기 버튼 사이 여백(#redesign, 사용자 피드백: "그 아래 인증/
   취소 버튼이랑 여백도 좀 넣고") - .pin-lock-actions를 각 모달 인라인 margin-top 대신
   공용 클래스로 뺐을 때 기본(3열) 모드용 값을 안 챙겨서 여백이 아예 없어졌었다. */
.pin-lock-actions {
  margin-top: 0.9rem;
}

/* 가로 방향(랜드스케이프)이면서 세로 공간이 부족한 화면 전용 - 원 크기를 줄이는 대신
   3열→6열로 바꿔서 4줄이던 키패드를 2줄(1-5 / 6-0+지우기)로 줄인다.
   사용자 피드백: "세로모드는 원래대로 둬. 가로모드만 번호가 너무 작아지면 5열로" -
   처음엔 높이만으로 판단했더니 세로로 긴 화면(포트레이트 폰/태블릿)도 높이가 960px
   밑이면 여기 걸려 원래(3열, 100px 원) 모습을 잃었다. orientation:landscape를 같이
   걸어서 세로 방향 화면은 높이가 아무리 낮아도(안전망 overflow-y:auto만 남고) 이
   전환 자체가 절대 안 걸리게 한다 - 세로 방향에서 어떻게 보일지는 이 미디어 쿼리
   손대기 전과 100% 동일.
   960px 기준: 위 3열 모드는 원 지름을 100px로 캡해뒀는데도(.pin-keypad 참고), 모달이
   최대 너비(480px, .modal-box-large)까지 넓어지는 화면에서 3열 배열이 다 들어가려면
   실측(Playwright)상 뷰포트 높이가 kiosk-pin-modal은 883px, 상태 문구까지 있는 더 무거운
   kiosk-register-modal은 942px 이상 필요했다(86vh 예산 기준) - 두 모달 다 안전하게 6열로
   전환되도록 더 무거운 쪽 기준으로 960px를 잡았다. */
@media screen and (orientation: landscape) and (max-height: 960px) {
  /* #redesign(사용자 피드백: "최대한 크게 해 / 가로·세로 버튼·글씨 크기 비슷하게 /
     세로는 좀 줄이고 가로는 더 키워 / 스크롤은 생기면 안돼") - 랜드스케이프에선
     모든 치수를 dvh 기반 clamp()로 잡아, 화면이 충분히 높으면 세로(3열, 84px 원)와
     같은 크기까지 커지고, 낮으면 스크롤 없이 딱 맞을 때까지 연속적으로 줄어들게 한다.
     상한값 = 세로모드와 동일(원 84px / 아이콘 44px / 입력칸 46px / 버튼 46px). */
  .pin-lock-box {
    overflow-y: auto !important; /* 진짜 극단값용 최후 안전망 (정상 범위에선 안 걸림) */
    width: min(94vw, 620px) !important;
    max-height: 95dvh !important;
    padding: clamp(0.3rem, 1.4dvh, 0.85rem) clamp(0.8rem, 3vw, 1.4rem) !important;
  }
  .pin-lock-icon {
    width: clamp(24px, 6.5dvh, 44px);
    height: clamp(24px, 6.5dvh, 44px);
    margin-bottom: clamp(0.12rem, 0.9dvh, 0.4rem);
    font-size: clamp(0.8rem, 3dvh, 1.3rem);
  }
  .pin-lock-box h2 {
    font-size: clamp(0.95rem, 4.2dvh, 1.4rem) !important;
    margin-bottom: clamp(0.05rem, 0.6dvh, 0.2rem) !important;
  }
  .pin-lock-box p {
    font-size: clamp(0.66rem, 2.5dvh, 0.88rem) !important;
    line-height: 1.25;
    margin-bottom: clamp(0.15rem, 1.1dvh, 0.5rem) !important;
  }
  .pin-lock-box .form-group {
    margin-bottom: 0 !important;
  }
  .pin-lock-box .pin-code-input {
    font-size: clamp(1rem, 4.5dvh, 1.4rem) !important;
    padding-top: clamp(0.1rem, 0.9dvh, 0.4rem) !important;
    padding-bottom: clamp(0.1rem, 0.9dvh, 0.4rem) !important;
    min-height: clamp(30px, 8dvh, 46px) !important;
  }
  .pin-keypad {
    /* 키 지름을 dvh로 캡(clamp 30~84px)하고, 키패드 폭을 "키*6 + gap*5"로 역산해
       잡는다 - grid의 1fr 트랙이 이 폭을 6등분하므로 키가 정확히 그 지름이 된다.
       화면이 좁으면 min(100%, …)로 폭이 먼저 걸린다.
       주의: .pin-lock-box는 flex column이라 자식이 기본 stretch로 늘어나는데, 여기에
       margin-inline:auto를 주면 cross축 auto 마진이 stretch를 무력화해서 키패드가
       content 폭으로 쪼그라든다(실측). 그래서 auto 마진 대신 width를 명시하고
       align-self:center로 가운데 정렬한다. */
    width: min(100%, calc(clamp(30px, 13.5dvh, 84px) * 6 + clamp(0.25rem, 1.3dvh, 0.55rem) * 5));
    align-self: center;
    /* 숫자 1-5 / 6-0 두 줄(5개씩 정렬) + 지우기는 6번째 칸에 따로 배치(요청: "지우기
       버튼도 둘째줄 오른쪽에, 번호는 5개 5개 줄 맞추고 삭제는 그 오른쪽에") - 그래서
       6열이지만 1번째 줄의 6번째 칸은 비워둔다. 아래 각 키를 data-digit/클래스로
       명시적 grid-column/row에 꽂아서, DOM 순서(1..9,spacer,0,backspace)나
       .pin-key-spacer의 display:none 여부와 무관하게 항상 이 배치가 된다. */
    grid-template-columns: repeat(6, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: clamp(0.25rem, 1.3dvh, 0.55rem);
    margin-top: clamp(0.2rem, 1.3dvh, 0.8rem);
  }
  .pin-key {
    font-size: clamp(0.9rem, 5.5dvh, 1.25rem);
  }
  .pin-key-spacer {
    display: none;
  }
  .pin-key[data-digit="1"] { grid-column: 1; grid-row: 1; }
  .pin-key[data-digit="2"] { grid-column: 2; grid-row: 1; }
  .pin-key[data-digit="3"] { grid-column: 3; grid-row: 1; }
  .pin-key[data-digit="4"] { grid-column: 4; grid-row: 1; }
  .pin-key[data-digit="5"] { grid-column: 5; grid-row: 1; }
  .pin-key[data-digit="6"] { grid-column: 1; grid-row: 2; }
  .pin-key[data-digit="7"] { grid-column: 2; grid-row: 2; }
  .pin-key[data-digit="8"] { grid-column: 3; grid-row: 2; }
  .pin-key[data-digit="9"] { grid-column: 4; grid-row: 2; }
  .pin-key[data-digit="0"] { grid-column: 5; grid-row: 2; }
  .pin-key-backspace {
    grid-column: 6;
    grid-row: 2;
  }
  .pin-lock-actions {
    margin-top: clamp(0.2rem, 1.3dvh, 0.7rem) !important;
  }
  .pin-lock-actions .btn-action {
    min-height: clamp(32px, 7.5dvh, 46px) !important;
  }
  #kiosk-register-status {
    margin-top: clamp(0.1rem, 0.8dvh, 0.35rem) !important;
    min-height: 0.9em !important;
    font-size: clamp(0.62rem, 2dvh, 0.8rem) !important;
  }
}

/* 설정 탭 그룹 캡션(#redesign) - iOS 설정 앱처럼 각 카드 그룹 위에 작은 대문자 라벨을 둬서
   "앱/화면/알림/고급" 네 묶음의 경계가 카드 사이 여백만으로도 아니라 눈에 바로 들어오게. */
.settings-group-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin: 0 0.3rem 0.4rem;
}

/* 설정 탭 공용 행(#settings-redesign) - 좌측 라벨 + 우측 컨트롤(스위치 등), 화면 테마
   세그먼트 행과 같은 가로 정렬을 클래스로 뽑았다. */
.settings-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
/* 알림 항목 3개처럼 마스터 토글 아래 딸린 하위 행 그룹 - 위쪽 hairline으로 한 묶음임을
   보여준다(#settings-redesign, 리뷰 지적: "이모지 푸시 버튼 + 맨 체크박스 3개 → 스위치
   행 그룹"). */
.settings-subrows {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--border-glass);
}
.settings-subrow {
  font-size: 0.8rem;
  color: var(--text-main);
}
.settings-subrow .switch-toggle {
  margin-left: auto;
  --sw-w: 38px;
  --sw-h: 22px;
}

/* 앱 다운로드 리스트(#settings-redesign) - 풀폭 pill 버튼 3개 대신 플랫폼 아이콘 + "버전 ·
   용량" + 우측 다운로드 아이콘 한 줄(리뷰 지적: "APK 다운로드 풀폭 pill 3개 → 플랫폼 ·
   버전 · 용량 리스트"). 버전/용량 텍스트는 admin.js hydrateApkDownloadMeta()가 채운다. */
.settings-dl-list {
  display: flex;
  flex-direction: column;
}
.settings-dl-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.6rem 0.2rem;
  border-bottom: 1px solid var(--border-glass);
  color: var(--text-main);
  text-decoration: none;
}
.settings-dl-row:last-child {
  border-bottom: 0;
}
.settings-dl-icon {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 1rem;
}
.settings-dl-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}
.settings-dl-label {
  font-size: 0.88rem;
  font-weight: 600;
}
.settings-dl-meta {
  font-size: 0.76rem;
  color: var(--text-muted);
  margin-top: 0.1rem;
}
.settings-dl-arrow {
  flex: none;
  color: var(--accent-cyan);
  font-size: 1.1rem;
}

