/* Preview UI — neutral operator console (Phase 1)
 *
 * Intentionally plain: white surface, hairline borders, soft drop shadow.
 * Distinct from any brand download skin (CMBG, etc.).
 *
 * 쓰는 법·규칙은 docs/preview-ui/DESIGN.md — 새 부품을 만들기 전에 읽고, 규칙을 바꾸면 같이 고친다.
 */

:root {
  --pv-bg: #f7f7f8;
  --pv-surface: #ffffff;
  --pv-border: #e6e6ea;
  --pv-text: #1a1a1f;
  --pv-text-soft: #6c6c75;
  --pv-text-mute: #9a9aa3;
  --pv-accent: #2563eb;
  --pv-accent-hover: #1d4ed8;
  --pv-danger: #dc2626;
  --pv-radius: 10px;
  --pv-radius-sm: 6px;
  --pv-shadow: 0 1px 2px rgba(20, 20, 30, 0.04), 0 2px 6px rgba(20, 20, 30, 0.04);
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

/* Pretendard 를 직접 싣는다.
   전에는 스택에 이름만 있고 **파일을 안 실었다.** 그래서 그 글꼴이 깔린 기기에서만
   Pretendard 로 보이고 나머지는 제각각이었다 — 게다가 순서가 4번째라 윈도우에서는
   Segoe UI 가 먼저 잡히고 한글은 맑은 고딕으로 떨어졌다. 화면마다 자모 모양과
   자간이 달라 보이던 것이 그 때문이다.

   가변 글꼴 하나로 굵기를 다 덮는다(400·500·600·700). 정적 파일을 굵기별로 받으면
   세 개에 2.3MB 인데 가변은 하나에 2MB 다. 운영 콘솔은 같은 사람이 반복해서 여는
   화면이라 첫 방문 뒤로는 캐시에서 나온다.

   swap 이라 글꼴이 늦게 와도 글자는 먼저 보인다 — 내려받는 동안 빈 화면이 되면
   그게 더 나쁘다. */
@font-face {
  font-family: 'Pretendard';
  src: url('/public/fonts/PretendardVariable.woff2') format('woff2-variations');
  font-weight: 45 920;
  font-style: normal;
  font-display: swap;
}

.preview-body {
  background: var(--pv-bg);
  color: var(--pv-text);
  font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, "Segoe UI",
    "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
  font-size: 14px;
  line-height: 1.45;
  min-height: 100vh;
}

/* ---------- Login ---------- */

.preview-body--login {
  display: flex;
  align-items: center;
  justify-content: center;
}

.preview-login-wrap {
  width: 100%;
  display: flex;
  justify-content: center;
  padding: 40px 16px;
}

.preview-login-card {
  width: 100%;
  max-width: 360px;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  padding: 32px 28px;
  box-shadow: var(--pv-shadow);
}

.preview-login-title {
  margin: 0 0 4px;
  font-size: 20px;
  font-weight: 600;
}

.preview-login-subtitle {
  margin: 0 0 24px;
  color: var(--pv-text-soft);
  font-size: 13px;
}

.preview-login-error {
  margin: 0 0 16px;
  padding: 10px 12px;
  background: #fff1f2;
  color: var(--pv-danger);
  border: 1px solid #fecdd3;
  border-radius: var(--pv-radius-sm);
  font-size: 13px;
}

.preview-field {
  display: block;
  margin-bottom: 14px;
}

.preview-field-label {
  display: block;
  margin-bottom: 6px;
  color: var(--pv-text-soft);
  font-size: 12px;
  font-weight: 500;
}

.preview-field input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  font-size: 14px;
  background: var(--pv-surface);
  color: var(--pv-text);
  outline: none;
  transition: border-color 0.15s;
}

.preview-field input:focus {
  border-color: var(--pv-accent);
}

/* ---------- Buttons ---------- */

.preview-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 14px;
  border: 1px solid var(--pv-border);
  background: var(--pv-surface);
  color: var(--pv-text);
  border-radius: var(--pv-radius-sm);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.preview-btn:hover { background: #fafafb; }
.preview-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.preview-btn--primary {
  background: var(--pv-accent);
  border-color: var(--pv-accent);
  color: #fff;
  width: 100%;
  padding: 10px 14px;
}

.preview-btn--primary:hover { background: var(--pv-accent-hover); border-color: var(--pv-accent-hover); }

.preview-btn--ghost {
  background: transparent;
}

/* ---------- Header ---------- */

.preview-header {
  background: var(--pv-surface);
  border-bottom: 1px solid var(--pv-border);
}

.preview-header-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.preview-header-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--pv-text);
  display: flex;
  align-items: center;
  gap: 8px;
}

.preview-back {
  color: var(--pv-text-soft);
  text-decoration: none;
  font-weight: 500;
}

.preview-back:hover { color: var(--pv-accent); }

.preview-divider {
  color: var(--pv-text-mute);
  font-weight: 300;
}

/* 헤더에서 링크가 아닌 브랜드명(뒤로가기 없는 단일지점/단일브랜드 화면). */
.preview-header-brand {
  color: var(--pv-text);
  font-weight: 600;
}

.preview-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.preview-header-user {
  color: var(--pv-text-soft);
  font-size: 13px;
}

.preview-logout-form { margin: 0; }

/* ---------- Main / sections ---------- */

.preview-main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 24px 20px 64px;
}

.preview-section + .preview-section { margin-top: 32px; }

/* 페이지 타이틀 — 헤더 백바 아래, 프린트 잔량 위. 현재 지점(또는 브랜드)명. */
.preview-page-head { margin: 0 0 16px; }
.preview-page-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--pv-text);
}

.preview-section-title {
  margin: 0 0 14px;
  font-size: 14px;
  font-weight: 600;
  color: var(--pv-text-soft);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.preview-empty {
  color: var(--pv-text-mute);
  font-size: 13px;
}

/* ---------- Brand list ---------- */

.preview-brand-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
}

.preview-brand-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  box-shadow: var(--pv-shadow);
  text-decoration: none;
  color: var(--pv-text);
  transition: border-color 0.15s, transform 0.15s;
}

.preview-brand-card:hover {
  border-color: #d4d4dc;
  transform: translateY(-1px);
}

.preview-brand-swatch {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  flex: 0 0 auto;
}

/* [색상 | 정보] — 색 띠를 카드 왼쪽 끝까지 붙여 세운다.
   28px 견본은 줄마다 똑같아 세는 데 도움이 안 됐다. 띠로 세우면 번호를 크게
   쓸 자리가 생기고, 목록을 훑을 때 왼쪽 한 줄만 따라가면 된다.

   왼쪽·위아래 여백을 카드에서 걷어 내고 그만큼을 내용 쪽에 준다. 그래야 띠가
   테두리에 닿는다 — 여백을 남기면 '띠'가 아니라 '큰 사각형'이 된다. */
.preview-brand-card--bar {
  padding: 0 14px 0 0;
  overflow: hidden;          /* 띠의 모서리를 카드 둥근 모서리에 맞춰 자른다 */
}
.preview-brand-card--bar .preview-brand-swatch {
  align-self: stretch;       /* 카드 높이를 그대로 채운다 */
  width: 46px;
  height: auto;
  min-height: 58px;
  border-radius: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  /* 옅은 테마색 위에서도 읽히게. 배경이 밝은 브랜드가 있다. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}
.preview-brand-card--bar .preview-brand-meta { padding: 14px 0; }

.preview-brand-meta { flex: 1 1 auto; min-width: 0; }
.preview-brand-name { font-weight: 600; font-size: 14px; }
/* 지점 코드는 고정폭을 쓸 이유가 없다 — 자릿수를 세거나 세로로 맞춰 보는 값이 아니다.
   본문과 같은 글꼴로 두어야 카드 안에서 두 줄이 같은 결로 읽힌다.
   (hash 처럼 한 글자씩 대조하는 값은 고정폭을 그대로 둔다) */
.preview-brand-code {
  color: var(--pv-text-soft); font-size: 12px;
  font-variant-numeric: tabular-nums; letter-spacing: .01em;
}

/* ---------- 총합 (브랜드 목록 위) ----------
   브랜드별 숫자만 있으면 "오늘 전체가 평소보다 적나"를 머리로 더해야 한다.
   통화가 섞이므로(스티커즈 국내 KRW + 중국 CNY) 합치지 않고 나란히 적는다. */
.preview-total { margin-bottom: 18px; }
.preview-total-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px; background: var(--pv-border);
  border: 1px solid var(--pv-border); border-radius: var(--pv-radius); overflow: hidden;
}
.preview-total-cell { background: var(--pv-surface); padding: 16px 18px; }
.preview-total-k {
  font-size: 11px; color: var(--pv-text-soft); letter-spacing: .03em; margin-bottom: 5px;
}
/* 통화마다 한 줄. 한 줄에 이어 붙였더니 좁은 화면에서 통째로 접혀 못 읽었다.
   국기가 어느 나라 돈인지 말해 준다 — ₩ 와 ¥ 는 서로 닮아서 기호만으로는 헷갈린다. */
.preview-total-v {
  display: flex; align-items: center; gap: 7px;
  font-size: 20px; font-weight: 700; color: var(--pv-text); line-height: 1.3;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.preview-total-v + .preview-total-v { margin-top: 2px; }
/* 국기는 흰 바탕(태극기·싱가포르)이 카드 흰색에 묻힌다. 옅은 그림자로 띄운다 —
   테두리만 두면 선이 하나 더 생겨 도안이 답답해 보인다. */
.preview-flag {
  display: block; flex-shrink: 0; border-radius: 2px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .16);
}
.preview-total-unit { font-size: 14px; font-weight: 500; color: var(--pv-text-soft); margin-left: 2px; }
.preview-total-n { margin-top: 3px; font-size: 12px; color: var(--pv-text-soft); }
.preview-total-n .is-bad { color: #b91c1c; font-weight: 600; }

/* ---------- 지점 목록 ----------
   브랜드 목록(preview-brand-grid)은 여러 칸으로 나누지만 지점 목록은 그러면 안 된다.
   220px 칸에 [번호][이름][매출][→] 을 밀어 넣었더니 이름이 한 글자씩 세로로 접혔다
   (2026-09-09 Funny 화면). 지점은 세로로 한 줄씩 쌓는다. */
.preview-store-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* 카드 높이를 맞춘다. 내용은 이름·코드·매출·상태 네 줄이 기준이고, 상태가 없어도
   그 자리는 비워 둔다. 높이가 제각각이면 목록을 훑을 때 눈이 멈추지 못한다. */
.preview-store-row { width: 100%; min-height: 116px; }
.preview-store-row .preview-brand-meta {
  display: flex; flex-direction: column; gap: 6px; padding: 16px 0;
}
.preview-store-row .preview-brand-name { font-size: 15px; line-height: 1.3; }
.preview-store-row .preview-brand-code { line-height: 1.3; }

/* 아래 한 줄 — 왼쪽 매출, 오른쪽 끝 기기 이상.
   이 화면은 지점 이름과 코드만 보여줬다. 어느 지점이 오늘 한 건도 못 팔았는지 보려면
   지점을 하나씩 눌러 들어가야 했고(스티커즈 52곳이면 52번), 그래서 아무도 안 봤다. */
.preview-store-bottom { margin-top: 4px; }
.preview-store-figs { display: flex; gap: 22px; align-items: baseline; flex-wrap: wrap; }
/* 통화가 여럿이면 값이 여러 개다. 세로로 쌓되 라벨은 첫 줄에 맞춘다. */
.preview-store-fig { display: flex; align-items: baseline; gap: 6px; min-height: 21px; }
.preview-store-fig .preview-store-fig-v { display: inline-flex; align-items: center; gap: 5px; }
.preview-store-fig .preview-flag { position: relative; top: 1px; }
.preview-store-fig-k { font-size: 11px; color: var(--pv-text-soft); letter-spacing: .02em; }
.preview-store-fig-v {
  font-size: 16px; font-weight: 600; color: var(--pv-text);
  font-variant-numeric: tabular-nums;
}
.preview-store-fig-n { font-size: 11px; color: var(--pv-text-soft); font-variant-numeric: tabular-nums; }
/* 오늘은 이번달 옆에 붙는 참고값이라 한 단계 죽인다 — 두 숫자가 같은 무게면
   눈이 어디를 볼지 모른다. */
.preview-store-fig--soft .preview-store-fig-v { font-size: 14px; font-weight: 500; color: var(--pv-text-soft); }
/* 오늘 0건에 경고색을 쓰지 않는다. 개점 전이거나 영업시간 밖일 수 있고,
   매일 빨간 화면은 아무도 안 본다. 판단은 옆의 이번달 값이 거든다. */
.preview-store-fig.is-zero .preview-store-fig-v { color: var(--pv-text-soft); }

/* 상태 줄 — 카드 맨 아래.
   매출 옆에 아이콘만 두었더니 무슨 이상인지 알려면 hover 해야 했고, 목록을 훑는
   화면에서는 아무도 hover 하지 않는다. 아이콘과 문구를 같이 낸다.

   여전히 **나쁠 때만** 뜬다. 정상까지 그리면 모든 카드에 한 줄이 더 붙어서
   정작 이상한 지점이 안 보인다. */
/* 상태 줄은 비어 있어도 자리를 지킨다. 있을 때만 그리면 카드 높이가 줄마다 달라져
   목록이 들쭉날쭉해지고, 그러면 어디를 봐야 할지 눈이 멈추지 못한다. */
.preview-store-marks {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  /* 30px — 프린터 고장 칩은 코드를 아래 줄에 달아 한 줄 칩보다 높다. 그 칩이 있는 카드만
     커지면 목록이 다시 들쭉날쭉해지므로 자리를 미리 잡는다. */
  margin-top: 8px; min-height: 30px;
}
.preview-store-mark {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px 4px 7px; border-radius: 999px;
  font-size: 12px; font-weight: 500; line-height: 1.2; white-space: nowrap;
}
.preview-store-mark[title] { cursor: help; }
/* 정상은 중립색이다. 초록으로 칠하면 다섯 칩이 다 초록이라 아무것도 안 도드라진다 —
   색은 "여기를 봐라"는 뜻이어야 한다. 나쁜 것만 색을 쓴다. */
.preview-store-mark.is-ok    { color: var(--pv-text-soft); background: #f1f3f5; }
.preview-store-mark.is-warn  { color: #92400e; background: #fef3c7; }
.preview-store-mark.is-fault { color: #991b1b; background: #fee2e2; }
/* 값을 못 받은 것(DEV·미연동)은 정상과 다르다. 점선으로 "모른다"를 표시한다. */
.preview-store-mark.is-mute  {
  color: var(--pv-text-soft); background: transparent;
  box-shadow: inset 0 0 0 1px var(--pv-border);
}
/* 미연동은 장애가 아니라 아직 안 붙은 것이다. 같은 붉은색으로 두면 배포가 안 끝난
   브랜드가 매일 경보로 뜨고, 그러면 아무도 안 본다. */
.preview-store-mark.is-mute { color: var(--pv-text-soft); background: #f1f3f5; }
/* 문구만 있는 알약(브랜드 목록)은 아이콘 자리를 비워 둘 필요가 없다. */
.preview-store-mark:not(:has(.preview-store-ico)) { padding: 4px 10px; }
.preview-store-ico { display: block; flex-shrink: 0; }
.preview-store-mark-t { display: inline-block; }
/* 프린터 고장 칩 — 원인은 색과 글자로, 제조사 코드는 아래 줄에 작게. 코드는 앱·AS 와
   대화할 때 보는 값이라 원인만큼 크게 둘 이유가 없다. */
.preview-store-mark-code {
  display: block; font-size: 10px; font-weight: 400; line-height: 1.1;
  opacity: .75; font-variant-numeric: tabular-nums;
}
.preview-store-mark:has(.preview-store-mark-code) {
  border-radius: 10px; padding-top: 2px; padding-bottom: 2px;
}

@media (max-width: 560px) {
  .preview-store-figs { gap: 14px; }
  .preview-store-mark { font-size: 11px; padding: 3px 8px 3px 6px; }
}

.preview-brand-cta {
  color: var(--pv-text-soft);
  font-size: 28px;
  line-height: 1;
  font-weight: 300;
  flex-shrink: 0;
  padding-left: 4px;
  transition: transform 0.15s ease, color 0.15s ease;
}
.preview-brand-card:hover .preview-brand-cta {
  color: var(--pv-text);
  transform: translateX(4px);
}

/* ---------- Grid toolbar ---------- */

.preview-filter-card {
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  box-shadow: var(--pv-shadow);
  padding: 14px;
  margin-bottom: 16px;
}

.preview-date-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 10px;
  margin-bottom: 12px;
}

.preview-date-actions {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

.preview-date-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  color: var(--pv-text-soft);
  font-size: 12px;
  font-weight: 600;
}

.preview-date-field input {
  min-width: 150px;
  padding: 8px 10px;
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  background: var(--pv-surface);
  color: var(--pv-text);
  font-size: 13px;
}

.preview-booth-filter {
  display: grid;
  gap: 8px;
  margin: 0 0 12px;
}
/* 부스 2개 미만이면 JS가 hidden 처리 — display:grid가 [hidden]을 덮지 않게 명시. */
.preview-booth-filter[hidden] { display: none; }

.preview-booth-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.preview-booth-card {
  position: relative;
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  background: var(--pv-surface);
  color: var(--pv-text);
  padding: 12px 14px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  min-width: 0;
  text-align: left;
  font: inherit;
  box-shadow: var(--pv-shadow);
  transition: border-color .15s, background .15s;
}
.preview-booth-card:hover { border-color: #c9c9d1; }
.preview-booth-card--all { width: 100%; padding: 14px 16px; }

.preview-booth-name {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.3;
  word-break: keep-all;
  min-width: 0;
}
/* 부스 번호는 위 줄에 작고 옅게, 이름은 아래 줄에 다 보이게(잔량 카드와 같은 결) */
.preview-booth-name .preview-booth-code { display: block; margin: 0 0 1px; }
.preview-booth-code {
  margin-right: 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--pv-text-mute);
}

/* 금액이 주인공, 건수·비중은 한 줄 아래 작게 */
.preview-booth-revenue {
  margin-top: 4px;
  font-size: 17px;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.preview-booth-sub {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 12px;
  font-weight: 500;
  color: var(--pv-text-soft);
}
.preview-booth-share { font-weight: 700; color: var(--pv-text); }

/* 전체 매출 중 이 부스 비중 */
.preview-booth-bar {
  display: block;
  height: 4px;
  margin-top: 8px;
  border-radius: 2px;
  background: var(--pv-bg);
  overflow: hidden;
}
.preview-booth-bar i { display: block; height: 100%; border-radius: 2px; background: var(--pv-accent); }

.preview-booth-card--all .preview-booth-name { font-size: 14px; }
.preview-booth-card--all .preview-booth-revenue { font-size: 24px; }

/* 선택된 카드 — 검은 박스 */
.preview-booth-card.is-active { background: var(--pv-text); border-color: var(--pv-text); color: #fff; }
.preview-booth-card.is-active .preview-booth-code { color: rgba(255, 255, 255, 0.55); }
.preview-booth-card.is-active .preview-booth-sub { color: rgba(255, 255, 255, 0.7); }
.preview-booth-card.is-active .preview-booth-share { color: #fff; }
.preview-booth-card.is-active .preview-booth-bar { background: rgba(255, 255, 255, 0.18); }
.preview-booth-card.is-active .preview-booth-bar i { background: #fff; }

@media (max-width: 640px) {
  .preview-date-actions {
    width: 100%;
    margin-left: 0;
    justify-content: flex-end;
  }
  .preview-booth-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.preview-summary {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
@media (min-width: 720px) {
  .preview-summary {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.preview-summary-metric {
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  padding: 10px 12px;
}

.preview-summary-label {
  display: block;
  color: var(--pv-text-soft);
  font-size: 11px;
  margin-bottom: 4px;
}

.preview-summary-metric strong {
  display: block;
  color: var(--pv-text);
  font-size: 20px;
  font-weight: 700;
}

.preview-summary-metric--revenue strong {
  color: var(--pv-accent);
}

.preview-summary-frames {
  display: grid;
  gap: 8px;
  margin-top: 12px;
}

.preview-frame-summary {
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  padding: 8px 10px;
}

.preview-frame-summary-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.preview-frame-summary-labels {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.preview-frame-tag {
  display: inline-flex;
  align-items: center;
  padding: 4px 8px;
  border-radius: 999px;
  background: var(--pv-bg);
  border: 1px solid var(--pv-border);
  color: var(--pv-text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
}

.preview-frame-summary-labels span {
  color: var(--pv-text-soft);
  font-size: 12px;
}

.preview-frame-total,
.preview-frame-revenue {
  color: var(--pv-text);
  font-size: 15px;
  font-weight: 700;
  white-space: nowrap;
}

.preview-summary-empty,
.preview-summary-note {
  color: var(--pv-text-mute);
  font-size: 12px;
}

.preview-summary-note {
  margin: 10px 0 0;
}

/* Kiosk supply strip — sits above the grid toolbar. One chip per deviceCode
   showing the latest paperRemain. The wrapper auto-hides (including the
   "프린트 잔량" heading) when the strip has no rows. */
.preview-kiosks-wrap {
  margin-bottom: 14px;
}
.preview-kiosks-wrap:has(.preview-kiosks-strip:empty) {
  display: none;
}
.preview-kiosks-title {
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--pv-text-soft);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.preview-kiosks-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.preview-kiosk-chip {
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  padding: 8px 10px;
  text-align: center;
  box-shadow: var(--pv-shadow);
}
.preview-kiosk-chip-id {
  font-size: 11px;
  color: var(--pv-text-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  margin-bottom: 2px;
}
.preview-kiosk-chip-remain {
  font-size: 18px;
  font-weight: 600;
  color: var(--pv-text);
  line-height: 1.1;
}
/* 기기 상태 — AOBSC 하트비트. 접이식 한 줄 표.
   격자 카드로 두었더니 기기가 많은 브랜드(gkpb24 46대)에서 화면 절반을 먹었다.
   평소엔 접어 두고 요약만 보이게 하고, 펼치면 한 기기가 한 줄이다. */
.preview-devices-wrap { margin-bottom: 14px; }
.preview-devices-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: var(--pv-surface); border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm); padding: 8px 12px;
  font: inherit; color: var(--pv-text); cursor: pointer; text-align: left;
}
.preview-devices-title {
  font-size: 12px; font-weight: 600; color: var(--pv-text-soft);
  letter-spacing: 0.02em; text-transform: uppercase;
}
.preview-devices-sum { font-size: 12px; color: var(--pv-text-soft); }
.preview-devices-sum.is-warn { color: #b45309; font-weight: 600; }
.preview-devices-chev { margin-left: auto; color: var(--pv-text-soft); font-size: 11px; }
.preview-devices-head[aria-expanded="true"] .preview-devices-chev { transform: rotate(180deg); }
.preview-devices-body { margin-top: 6px; }
/* 컬럼이 여덟이라 좁은 화면에서는 가로로 굴린다. 페이지 자체가 밀리면 안 된다. */
.preview-dev-scroll { overflow-x: auto; }
.preview-dev-table {
  width: 100%; border-collapse: collapse; font-size: 12px;
  background: var(--pv-surface); border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
}
.preview-dev-table th, .preview-dev-table td {
  padding: 5px 8px; text-align: center; white-space: nowrap;
  border-bottom: 1px solid var(--pv-border);
}
.preview-dev-table thead th {
  font-size: 11px; font-weight: 500; color: var(--pv-text-soft);
  background: var(--pv-bg, #fafafa); cursor: help; padding: 6px 8px;
}
/* 아이콘 머리 — 이모지는 OS 마다 크기가 달라 정렬이 흔들렸다. SVG 로 못박는다. */
.preview-dev-ico { display: block; margin: 0 auto; }
.preview-dev-table tbody tr:last-child td { border-bottom: 0; }
.preview-dev-table td.preview-dev-id, .preview-dev-table th.preview-dev-id {
  text-align: left; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
}
.preview-dev-table td.preview-dev-name, .preview-dev-table th.preview-dev-name {
  text-align: left; max-width: 130px; overflow: hidden;
  text-overflow: ellipsis; font-size: 11px;
}
/* 생사는 색 점 하나로. 줄 전체를 물들이면 여덟 칸이 다 붉어져 정작 어느 값이
   문제인지 안 보인다. */
.preview-dev-dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  margin-right: 5px; vertical-align: middle; background: #9ca3af;
}
.preview-dev-row--online .preview-dev-dot { background: #16a34a; }
.preview-dev-row--stale  .preview-dev-dot { background: #f59e0b; }
.preview-dev-row--stale  { background: #fffbf5; }
/* 한 번도 안 보낸 기기는 장애가 아니라 아직 안 붙은 것이라 조용히 둔다. */
.preview-dev-row--never  { opacity: 0.5; }
.preview-dev-row--never .preview-dev-dot { background: transparent; box-shadow: inset 0 0 0 1px #9ca3af; }
/* 영업시간 밖에 꺼진 기기 — 장애가 아니라 퇴근이다. 조용히 흐리게만 둔다. */
.preview-dev-row--closed { opacity: 0.6; }
.preview-dev-row--closed .preview-dev-dot { background: #cbd5e1; }
/* 지점 카드 코드 옆 영업시간. 지금 영업시간 밖이면 더 흐리게. */
.preview-store-hours { color: var(--pv-text-soft); cursor: help; }
.preview-store-hours.is-closed { opacity: .6; }
/* 끊긴 줄의 '마지막 신호'는 그 자체가 장애 시간이라 그 칸만 강조한다. */
.preview-dev-late { color: #b45309; font-weight: 600; }
/* 프린터 칸 — 고장 원인은 색으로 가르고 제조사 코드는 아래 줄에 작게 둔다.
   고장(인쇄가 멈춤)은 빨강, 주의(용지·리본 부족, 냉각)는 호박. 지점 카드 칩과 같은 색이다. */
.preview-dev-state { font-weight: 600; }
.preview-dev-state.is-fault { color: #b91c1c; }
.preview-dev-state.is-warn  { color: #b45309; }
.preview-dev-code {
  display: block; font-size: 10px; line-height: 1.1; margin-top: 1px;
  color: var(--pv-text-soft); font-variant-numeric: tabular-nums;
}
.preview-devices-note { margin-top: 6px; font-size: 11px; color: var(--pv-text-soft); }
.preview-devices-note:empty { display: none; }

.preview-kiosk-chip--low {
  border-color: #fecdd3;
  background: #fff8f8;
}
.preview-kiosk-chip--low .preview-kiosk-chip-remain {
  color: var(--pv-danger);
}

.preview-grid-toolbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.preview-grid-toolbar > #preview-load-new {
  grid-column: 2;
  justify-self: center;
}

.preview-grid-status {
  color: var(--pv-text-soft);
  font-size: 13px;
}

#preview-count {
  color: var(--pv-text);
  font-weight: 600;
}

/* ---------- Cards grid ---------- */

.preview-grid {
  display: grid;
  /* Mobile-first: lock to 2 columns so phones don't fall back to a single
     wide card that wastes vertical space. minmax(0, 1fr) keeps card content
     from blowing the column out at narrow widths. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
@media (min-width: 640px) {
  .preview-grid {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 14px;
  }
}

.preview-card {
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  box-shadow: var(--pv-shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.preview-card-thumb {
  aspect-ratio: 1 / 1;
  background: #f0f0f3;
  overflow: hidden;
  position: relative;
}

.preview-card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.preview-card-badge {
  position: absolute;
  top: 8px;
  left: 8px;
  background: rgba(20, 20, 30, 0.7);
  color: #fff;
  font-size: 11px;
  padding: 2px 7px;
  border-radius: 999px;
}

.preview-card-booth-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(20, 20, 30, 0.78);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 8px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

.preview-card-body {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* 카드 본문은 한 줄에 하나씩 세운다. 두 값을 좌우로 밀어 넣으면 좁은
   카드에서 접혀 카드 높이가 들쭉날쭉해진다(실측: "삭제까지 21시간 남음"이
   두 줄로 꺾였다). */
.preview-card-line {
  font-size: 12px;
  line-height: 1.4;
  color: var(--pv-text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.preview-card-hash {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--pv-text);
}

/* 촬영 시각 — 상대시간만으로는 언제 찍었는지 알 수 없다. */
.preview-card-shot { color: var(--pv-text); }
.preview-card-ago { color: var(--pv-text-mute); font-size: 11px; }

.preview-card-expires {
  font-size: 11px;
  color: var(--pv-text-mute);
}

.preview-card-actions {
  display: flex;
  gap: 6px;
  margin-top: 2px;
}
.preview-card-actions .preview-card-button { flex: 1 1 auto; }
.preview-card-more {
  flex: 0 0 auto;
  padding: 8px 10px;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  color: var(--pv-text-soft);
  font-size: 12px;
  cursor: pointer;
}
.preview-card-more:hover { background: #fafafb; border-color: #d4d4dc; }
.preview-card-more:disabled { opacity: .5; cursor: default; }

.preview-card-print {
  margin: 0 0 8px;
  padding: 4px 8px;
  background: var(--pv-bg);
  border-radius: var(--pv-radius-sm);
  font-size: 11px;
  color: var(--pv-text-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: center;
}

.preview-card-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8px 10px;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  color: var(--pv-text);
  font-size: 12px;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.15s, border-color 0.15s;
}

.preview-card-button:hover {
  background: #fafafb;
  border-color: #d4d4dc;
}

/* ---------- Grid footer ---------- */

.preview-grid-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 24px;
}

.preview-grid-footer .preview-btn--primary {
  width: auto;
  min-width: 200px;
}

.preview-status {
  margin: 0;
  color: var(--pv-text-mute);
  font-size: 12px;
}

/* ---------- Admin (Phase 2) ---------- */

.preview-flash {
  margin: 0 0 16px;
  padding: 10px 14px;
  border-radius: var(--pv-radius-sm);
  font-size: 13px;
  border: 1px solid var(--pv-border);
}
.preview-flash--ok {
  background: #ecfdf5;
  border-color: #a7f3d0;
  color: #047857;
}
.preview-flash--err {
  background: #fff1f2;
  border-color: #fecdd3;
  color: var(--pv-danger);
}

.preview-admin-table-wrap {
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  box-shadow: var(--pv-shadow);
  overflow-x: auto;
}

.preview-admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.preview-admin-table th,
.preview-admin-table td {
  padding: 10px 12px;
  text-align: left;
  border-bottom: 1px solid var(--pv-border);
  vertical-align: middle;
}

.preview-admin-table thead th {
  background: #fafafb;
  color: var(--pv-text-soft);
  font-weight: 600;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.preview-admin-table tbody tr:last-child td { border-bottom: none; }

.preview-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--pv-text);
}

.preview-admin-ts { color: var(--pv-text-soft); font-size: 12px; }

.preview-admin-self {
  color: var(--pv-text-mute);
  font-size: 12px;
  font-style: italic;
}

.preview-admin-inline-form {
  display: flex;
  gap: 6px;
  align-items: center;
  margin: 0;
}

.preview-admin-inline-form input[type="password"] {
  padding: 6px 8px;
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  font-size: 12px;
  width: 140px;
  outline: none;
}
.preview-admin-inline-form input[type="password"]:focus {
  border-color: var(--pv-accent);
}

.preview-btn--danger {
  color: var(--pv-danger);
  border-color: #fecdd3;
}
.preview-btn--danger:hover {
  background: #fff1f2;
  border-color: var(--pv-danger);
}

.preview-admin-create {
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  box-shadow: var(--pv-shadow);
  padding: 18px 18px 20px;
  max-width: 520px;
}

.preview-admin-create .preview-field input,
.preview-admin-create .preview-field select {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
  font-size: 14px;
  background: var(--pv-surface);
  color: var(--pv-text);
  outline: none;
  transition: border-color 0.15s;
}
.preview-admin-create .preview-field input:focus,
.preview-admin-create .preview-field select:focus {
  border-color: var(--pv-accent);
}

.preview-admin-create .preview-btn--primary {
  width: auto;
  min-width: 160px;
  margin-top: 4px;
}

.preview-admin-hint {
  margin: 4px 0 16px;
  color: var(--pv-text-mute);
  font-size: 12px;
}

.preview-checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 8px;
  padding: 10px 12px;
  background: var(--pv-bg);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm);
}

.preview-checkbox-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--pv-text);
  cursor: pointer;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.preview-checkbox-item input {
  margin: 0;
  cursor: pointer;
}

/* ===== 상단 탭 (매출/현황 ↔ 프리뷰) ===== */
.preview-tabs {
  display: flex;
  gap: 4px;
  margin: 16px 0 0;
  border-bottom: 1px solid var(--pv-border);
}
.preview-tab {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 10px 18px;
  font-size: 15px;
  color: var(--pv-text-soft);
  cursor: pointer;
}
.preview-tab.is-active {
  color: var(--pv-accent);
  border-bottom-color: var(--pv-accent);
  font-weight: 600;
}
.preview-panel { margin-top: 16px; }
.preview-panel[hidden] { display: none; }

/* ===== 매출 탭 달력 ===== */
.preview-calendar-card {
  margin-top: 18px;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  padding: 14px;
}
.preview-cal-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.preview-cal-label { font-size: 15px; font-weight: 600; }
.preview-cal-total { margin-left: auto; font-size: 13px; color: var(--pv-text-soft); }
.preview-cal-clear {
  appearance: none; cursor: pointer;
  border: 1px solid var(--pv-accent); background: var(--pv-accent); color: #fff;
  border-radius: 999px; padding: 5px 12px; font-size: 12px; font-weight: 600;
}
.preview-cal-clear:hover { background: var(--pv-accent-hover); border-color: var(--pv-accent-hover); }
.preview-calendar {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
}
.preview-cal-dow {
  text-align: center;
  font-size: 12px;
  color: var(--pv-text-mute);
  padding: 4px 0;
}
.preview-cal-cell {
  appearance: none;
  text-align: left;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: 8px;
  min-height: 62px;
  padding: 6px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* 셀 너비 기준 유동 폰트(cqw) — 금액이 셀 폭에 맞춰 자동 축소 */
  container-type: inline-size;
  overflow: hidden;
}
.preview-cal-cell--empty { background: transparent; border: 0; cursor: default; }
.preview-cal-cell:not(.preview-cal-cell--empty):hover { border-color: var(--pv-accent); }
.preview-cal-cell.has-sales { background: #f6f9ff; }
.preview-cal-cell.is-selected { border-color: var(--pv-accent); box-shadow: 0 0 0 1px var(--pv-accent) inset; }
/* 날짜·금액·건수: 셀 너비(cqw)에 비례해 자동 축소 — 7자리 금액도 항상 셀 안에 맞음.
   17cqw ≈ 셀 폭의 17%, 데스크탑은 12px 상한, 초소형은 5px 하한으로 클램프 */
.preview-cal-dnum { font-size: 9px; font-size: clamp(5px, 17cqw, 12px); color: var(--pv-text-soft); line-height: 1.1; }
.preview-cal-amt { font-size: 8px; font-size: clamp(5px, 17cqw, 12px); font-weight: 700; color: var(--pv-accent); margin-top: auto; white-space: nowrap; font-variant-numeric: tabular-nums; letter-spacing: -0.2px; line-height: 1.1; }
/* 축약(141만) 대신 정확한 값(1,407,000)을 쓰므로 자릿수가 길다. 값을 뭉개는 대신
   글자를 줄인다 — 자릿수 구간별로 셀 폭 대비 비율(cqw)과 상·하한을 낮춘다.
   JS(grid.js calAmountClass)가 문자열 길이를 보고 클래스를 붙인다. */
.preview-cal-amt.is-w9  { font-size: clamp(4px, 11.5cqw, 9px); letter-spacing: -0.3px; }
.preview-cal-amt.is-w12 { font-size: clamp(3.5px, 9cqw, 8px); letter-spacing: -0.4px; }
/* 건수는 금액보다 한 단계 작게 — 금액이 축약을 버리고 길어진 만큼, 셀 안에서
   읽는 순서(금액 먼저)가 드러나도록 위계를 준다. */
.preview-cal-cnt { font-size: 8px; font-size: clamp(4.5px, 14cqw, 10px); color: var(--pv-text-mute); line-height: 1.1; }

@media (max-width: 520px) {
  .preview-calendar { gap: 3px; }
  .preview-cal-dow { font-size: 10px; padding: 2px 0; }
  .preview-cal-cell { min-height: 46px; padding: 3px; border-radius: 6px; }
  .preview-cal-label { font-size: 14px; }
  .preview-cal-total { font-size: 11px; }
}
@media (max-width: 380px) {
  .preview-cal-cell { min-height: 40px; padding: 2px; }
}

/* ===== 매출 탭 KPI (이번달/오늘 매출) ===== */
.preview-kpi {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}
.preview-kpi-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--pv-surface);
  border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius);
  padding: 14px 18px;
}
.preview-kpi-label { font-size: 14px; color: var(--pv-text-soft); }
.preview-kpi-value { font-size: 22px; font-weight: 700; color: var(--pv-accent); font-variant-numeric: tabular-nums; }

/* ===== 날짜 3모드 + 커스텀 범위 ===== */
.preview-date-modes { display: flex; gap: 6px; margin-bottom: 10px; }
.preview-mode-btn {
  appearance: none; border: 1px solid var(--pv-border); background: var(--pv-surface);
  border-radius: 999px; padding: 7px 16px; font-size: 14px; color: var(--pv-text-soft); cursor: pointer;
}
.preview-mode-btn.is-active { background: var(--pv-accent); border-color: var(--pv-accent); color: #fff; font-weight: 600; }
.preview-date-custom {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 12px;
  padding: 12px; background: var(--pv-bg); border: 1px solid var(--pv-border); border-radius: var(--pv-radius-sm);
}
.preview-date-custom[hidden] { display: none; }
.preview-date-custom input[type="date"] {
  flex: 1 1 auto; min-width: 130px; padding: 8px 10px;
  border: 1px solid var(--pv-border); border-radius: var(--pv-radius-sm);
  background: var(--pv-surface); color: var(--pv-text); font-size: 13px;
}
.preview-date-custom input[type="date"]:focus { border-color: var(--pv-accent); outline: none; }
.preview-date-custom .preview-btn--primary { width: auto; flex: 0 0 auto; padding: 8px 20px; }
.preview-date-tilde { color: var(--pv-text-mute); flex: 0 0 auto; }

/* ===== 결제수단 4탭 ===== */
.preview-pay { margin-top: 16px; background: var(--pv-surface); border: 1px solid var(--pv-border); border-radius: var(--pv-radius); padding: 12px; }
.preview-pay-tabs { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--pv-border); padding-bottom: 8px; }
.preview-pay-tab {
  appearance: none; border: 0; background: transparent; border-radius: 8px;
  padding: 6px 12px; font-size: 13px; color: var(--pv-text-soft); cursor: pointer;
}
.preview-pay-tab.is-active { background: var(--pv-accent); color: #fff; font-weight: 600; }
.preview-pay-tab-cnt { opacity: .8; font-size: 12px; }
.preview-pay-body { display: flex; gap: 24px; padding: 12px 4px 2px; }
.preview-pay-metric { display: flex; flex-direction: column; gap: 2px; }
.preview-pay-metric span { font-size: 12px; color: var(--pv-text-soft); }
.preview-pay-metric strong { font-size: 18px; font-variant-numeric: tabular-nums; }

/* ===== 시간대별 (접이식) ===== */
.preview-hourly-card { margin-top: 16px; background: var(--pv-surface); border: 1px solid var(--pv-border); border-radius: var(--pv-radius); padding: 12px; }
.preview-hourly-title { font-size: 14px; margin: 0 0 8px; color: var(--pv-text); }
.preview-hour-row { border-top: 1px solid var(--pv-border); }
.preview-hour-row:first-child { border-top: 0; }
.preview-hour-head {
  width: 100%; appearance: none; border: 0; background: transparent; cursor: pointer;
  display: flex; align-items: center; gap: 10px; padding: 10px 4px; font-size: 14px; text-align: left;
}
.preview-hour-label { font-weight: 600; min-width: 44px; }
.preview-hour-sum { color: var(--pv-text-soft); }
.preview-hour-caret { margin-left: auto; color: var(--pv-text-mute); transition: transform .12s; }
.preview-hour-head.is-open .preview-hour-caret { transform: rotate(180deg); }
.preview-hour-items { padding: 0 4px 10px; }
.preview-hour-items[hidden] { display: none; }
.preview-hour-item { display: flex; align-items: center; gap: 12px; padding: 5px 8px; font-size: 13px; border-radius: 6px; }
.preview-hour-item:nth-child(odd) { background: var(--pv-bg); }
.preview-hour-time { font-variant-numeric: tabular-nums; color: var(--pv-text-soft); min-width: 72px; }
.preview-hour-frame { flex: 1; color: var(--pv-text); }
.preview-hour-amt { font-variant-numeric: tabular-nums; font-weight: 600; }
.preview-hour-more { font-size: 12px; color: var(--pv-text-mute); padding: 6px 8px; }

/* ===== 결제 내역 리스트 (최신순) ===== */
.preview-txn-card { margin-top: 16px; background: var(--pv-surface); border: 1px solid var(--pv-border); border-radius: var(--pv-radius); padding: 12px; }
.preview-txn-title { font-size: 14px; margin: 0 0 8px; color: var(--pv-text); }
.preview-txn-list { display: flex; flex-direction: column; }
.preview-txn-row {
  /* 칸 수와 셀 수가 어긋나면 마지막 셀이 다음 줄로 떨어져 행이 두 겹이 된다.
     시각·부스·프레임·결제수단·금액·세로점 여섯을 그대로 센다. */
  display: grid;
  /* 시각 칸은 글자 폭에 딱 맞춘다. 넉넉히 잡아 두면 남는 자리가 그대로 빈
     여백이 되어, 정작 길어질 수 있는 프레임·조치 칸이 먼저 잘린다.
     행마다 grid 라 auto 를 쓰면 열이 안 맞아 고정폭이어야 한다. */
  grid-template-columns: 100px 40px minmax(0, 1fr) auto auto 20px;
  align-items: center; gap: 6px;
  padding: 8px; font-size: 13px; border-top: 1px solid var(--pv-border);
}
/* 시각은 절대 접히면 안 된다. 접히는 순간 행 높이가 두 배가 된다. */
.preview-txn-time { white-space: nowrap; }
.preview-txn-row:first-child { border-top: 0; }
.preview-txn-row:nth-child(even) { background: var(--pv-bg); }

/* 취소 행 — 금액이 음수라 눈에 띄지만, 목록을 훑을 때 색으로 먼저 잡히게 한다.
   짝수행 배경(nth-child)보다 뒤에 와야 덮인다. */
.preview-txn-row.is-cancel,
.preview-txn-row.is-cancel:nth-child(even) { background: #fef2f2; }
.preview-txn-row.is-cancel .preview-txn-amt,
.preview-txn-row.is-cancel .preview-txn-method { color: #b91c1c; font-weight: 600; }
/* 취소 행의 프레임 칸에는 원거래 참조가 들어간다. 빨강까지 입히면 그것도
   경고로 읽히는데, 그건 안내지 경고가 아니다. */
.preview-txn-row.is-cancel .preview-txn-frame {
  color: #9a6b6b; font-size: 11px;
}

/* 사진이 아직 없는 행 — 세 단계다. 색은 "지금 손봐야 하는가"만 말한다.
     촬영중     0~7분   손님이 아직 부스에 있다. 정상이므로 **색을 주지 않는다**
     업로드 대기 7~30분  촬영은 끝났을 시간인데 안 온다. 옅은 노랑
     사진 지연   30분~   손볼 일. 진한 노랑 (배너가 세는 구간)
   촬영중까지 노랑으로 칠하면 하루 종일 노란 행이 깔려, 정작 볼 것이 묻힌다.
   짝수행 배경(nth-child)보다 뒤에 와야 덮인다. */
.preview-txn-row.is-photo-shooting .preview-txn-frame {
  color: var(--pv-text-mute); font-size: 11px;
}
.preview-txn-row.is-photo-pending,
.preview-txn-row.is-photo-pending:nth-child(even) { background: #fefce8; }
.preview-txn-row.is-photo-pending .preview-txn-frame { color: #a16207; font-weight: 600; }
.preview-txn-row.is-photo-late,
.preview-txn-row.is-photo-late:nth-child(even) { background: #fef3c7; }
.preview-txn-row.is-photo-late .preview-txn-frame { color: #b45309; font-weight: 700; }

/* 조치완료 — 노랑을 내리고 옅은 회색으로 가라앉힌다. 사진은 여전히 안 왔지만
   사람이 끝냈으니 더 붙잡을 일이 아니다. 흔적은 체크로 남는다. */
.preview-txn-row.is-photo-done,
.preview-txn-row.is-photo-done:nth-child(even) { background: var(--pv-bg); }
.preview-txn-row.is-photo-done .preview-txn-time,
.preview-txn-row.is-photo-done .preview-txn-booth,
.preview-txn-row.is-photo-done .preview-txn-frame { color: var(--pv-text-mute); }

/* 정상 건에 조치만 붙은 경우 — 배경은 건드리지 않는다. 색을 주면 멀쩡한 거래가
   문제처럼 보인다. 체크 아이콘만으로 충분하다. */
.preview-txn-row.has-action .preview-txn-frame { color: var(--pv-text); }

/* 조치 행은 '오류 › 조치' 두 마디가 들어가 길다. 글자를 줄여 한 줄을 지킨다 —
   접히면 행 높이가 두 배가 되고, 그러면 목록을 훑는 눈이 끊긴다. */
.preview-txn-row.has-action .preview-txn-frame,
.preview-txn-row.is-photo-done .preview-txn-frame {
  font-size: 11px; letter-spacing: -0.2px;
}

/* 체크 아이콘 — 색을 갖지 않는다. 글자색을 그대로 따라가므로 회색 행에서는
   회색으로 가라앉고, 일반 행에서는 본문색이 된다. */
.preview-txn-check {
  display: inline-block; vertical-align: -1px; margin-right: 2px; flex: none;
}

/* 사진 대기 행은 행 전체가 눌린다 — 작은 세로점을 겨냥하지 않아도 되게. */
.preview-txn-row.is-photo-pending,
.preview-txn-row.is-photo-late,
.preview-txn-row.is-photo-done { cursor: pointer; }

/* ── 상세 탭 ───────────────────────────────────────────────────────
   사진·내역·조치를 한 화면에 세로로 쌓으면 스크롤이 길어져 아래 것이 안 보인다.
   탭으로 갈라 한 번에 하나만 보이게 한다. */
.preview-ovl-tabs {
  display: flex; gap: 2px; margin-bottom: 12px;
  border-bottom: 1px solid var(--pv-border);
}
.preview-ovl-tab {
  flex: 1 1 0; padding: 8px 4px; border: 0; background: none;
  color: var(--pv-text-mute); font-size: 13px; font-weight: 600; cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.preview-ovl-tab.is-on { color: var(--pv-text); border-bottom-color: #1f2937; }
/* 탭 내용은 높이가 제각각이다. 최소 높이를 줘야 탭을 옮길 때 상자가 덜 튄다. */
.preview-ovl-panel { min-height: 150px; }

/* ── 조치 기록 ─────────────────────────────────────────────────────── */
.preview-ovl-action {
  margin-top: 16px; padding: 12px; border: 1px solid var(--pv-border);
  border-radius: 8px; background: var(--pv-bg);
}
.preview-ovl-action.is-done { background: #f0fdf4; border-color: #bbf7d0; }
.preview-ovl-action-head {
  font-size: 12px; font-weight: 700; color: var(--pv-text); margin-bottom: 8px;
}
.preview-ovl-action-label {
  font-size: 11px; font-weight: 700; color: var(--pv-text-mute);
  margin: 0 0 6px; letter-spacing: .02em;
}
.preview-ovl-tags + .preview-ovl-action-label { margin-top: 12px; }
.preview-ovl-action-sum {
  display: grid; grid-template-columns: 44px 1fr; gap: 4px 10px;
  margin: 0 0 8px; font-size: 13px;
}
.preview-ovl-action-sum dt { color: var(--pv-text-mute); }
.preview-ovl-action-sum dd { margin: 0; color: var(--pv-text); font-weight: 600; }
.preview-ovl-action.is-done .preview-ovl-action-head { color: #15803d; }
.preview-ovl-action-head .preview-txn-check { vertical-align: -1px; margin-right: 3px; }
.preview-ovl-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.preview-ovl-tag {
  padding: 6px 10px; border: 1px solid var(--pv-border); border-radius: 999px;
  background: #fff; color: var(--pv-text); font-size: 12px; cursor: pointer;
}
.preview-ovl-tag.is-on {
  background: #1f2937; border-color: #1f2937; color: #fff; font-weight: 600;
}
.preview-ovl-note {
  width: 100%; padding: 8px; border: 1px solid var(--pv-border); border-radius: 6px;
  font-size: 13px; margin-bottom: 8px; background: #fff; color: var(--pv-text);
}
.preview-ovl-action-save {
  width: 100%; padding: 9px; border: 1px solid #1f2937; border-radius: 8px;
  background: #1f2937; color: #fff; font-size: 13px; font-weight: 600; cursor: pointer;
}
.preview-ovl-action-save:disabled {
  background: var(--pv-bg); border-color: var(--pv-border);
  color: var(--pv-text-mute); cursor: default;
}
.preview-ovl-action-note { margin: 0 0 6px; font-size: 13px; color: var(--pv-text); }
.preview-ovl-action-by { margin: 0 0 8px; font-size: 11px; color: var(--pv-text-mute); }
.preview-ovl-action-clear {
  padding: 5px 10px; border: 1px solid var(--pv-border); border-radius: 6px;
  background: #fff; color: var(--pv-text-mute); font-size: 12px; cursor: pointer;
}

/* 취소된 결제 — 짝이 되는 음수 행이 어딘가에 있어 합계는 0이다.
   금액만 줄을 긋는다. 행 전체를 흐리면 목록을 훑을 때 존재 자체가 사라진다. */
.preview-txn-row.is-voided .preview-txn-amt {
  text-decoration: line-through; color: var(--pv-text-mute); font-weight: 600;
}

/* 짝으로 건너뛰는 버튼. 값 자리에 들어가므로 링크처럼 보이되 줄바꿈은 막는다. */
.preview-ovl-mate {
  padding: 0; border: 0; background: none; cursor: pointer;
  color: var(--pv-accent, #2563eb); font: inherit; text-align: left;
  text-decoration: underline; white-space: nowrap;
}
.preview-ovl-mate:disabled { opacity: .5; cursor: default; }

/* 취소 버튼 — 눈에 띄되 먼저 잡히지는 않게. 실수로 누르는 자리가 아니다. */
.preview-txn-cancel {
  margin-left: 8px; padding: 2px 8px; font-size: 12px; line-height: 18px;
  border: 1px solid #fca5a5; border-radius: 6px; background: #fff; color: #b91c1c;
  cursor: pointer; white-space: nowrap;
}
.preview-txn-cancel:hover { background: #fef2f2; }
.preview-txn-cancel:disabled { opacity: .5; cursor: default; }
/* 행 끝 세로점 — 자리를 거의 안 차지해야 목록이 한 줄로 유지된다. */
.preview-txn-menu {
  width: 22px; flex: 0 0 22px; padding: 0; margin-left: 4px;
  border: 0; background: none; color: var(--pv-muted, #9ca3af);
  font-size: 16px; line-height: 1; cursor: pointer; text-align: center;
}
.preview-txn-menu:hover { color: var(--pv-text, #111827); }
.preview-txn-menu.is-empty { cursor: default; }

/* 거래 상세 */
.preview-ovl {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(0, 0, 0, .35);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.preview-ovl[hidden] { display: none; }
.preview-ovl-box {
  position: relative; width: 100%; max-width: 360px;
  background: #fff; border-radius: 12px; padding: 20px 18px 18px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
  /* 사진이 들어가면 세로가 길어진다. 화면 밖으로 밀리면 취소 버튼에 닿지 못한다. */
  max-height: calc(100vh - 32px); overflow-y: auto;
}
.preview-ovl-close {
  position: absolute; top: 6px; right: 8px; width: 32px; height: 32px;
  border: 0; background: none; font-size: 22px; line-height: 1;
  color: var(--pv-muted, #9ca3af); cursor: pointer;
}
.preview-ovl-title { margin: 0 0 12px; font-size: 15px; font-weight: 700; }
.preview-ovl-list {
  display: grid; grid-template-columns: 84px 1fr; gap: 6px 10px;
  margin: 0; font-size: 13px;
}
.preview-ovl-list dt { color: var(--pv-muted, #6b7280); }
.preview-ovl-list dd { margin: 0; word-break: break-all; }
/* 결제 정보 — 승인번호·카드 상세. 거래 내역과 한 칸 띄워 구분한다.
   무료 건은 통째로 빈 문자열이라 :empty 로 여백까지 지운다. */
.preview-ovl-pay { margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--pv-line, #e5e7eb); }
.preview-ovl-pay:empty { display: none; }
.preview-ovl-pay-head {
  margin: 0 0 8px; font-size: 12px; font-weight: 700;
  color: var(--pv-muted, #6b7280); letter-spacing: .02em;
}
/* 카드번호·가맹점번호는 자릿수를 눈으로 세는 값이라 고정폭으로 둔다. */
.preview-ovl-pay-list dd { font-variant-numeric: tabular-nums; }
.preview-ovl-pay-empty { font-size: 12px; color: var(--pv-muted, #9ca3af); }
/* 사진·링크 — 비어 있으면 margin 도 남기지 않는다(:empty). */
.preview-ovl-media { margin: 0 0 12px; }
.preview-ovl-media:empty { display: none; }
/* 높이를 고정한다. 프레임마다 비율이 제각각이라(가로 띠부터 2x6 세로 띠까지)
   원본 비율대로 두면 세로 띠 한 장이 오버레이를 화면 밖까지 밀어낸다.
   contain 이라 잘리지 않는다 — 확인용이니 전체가 보이는 쪽이 맞다. */
.preview-ovl-thumb {
  display: block; height: 168px; border-radius: 8px;
  overflow: hidden; background: var(--pv-bg);
}
.preview-ovl-thumb img {
  display: block; width: 100%; height: 100%; object-fit: contain;
  opacity: 1; transition: opacity .18s ease;
}
/* 사진이 오기 전에는 자리만 잡아 둔다. 도착하는 순간 나타나게 하면 아래
   내용이 밀리지 않는다 — 밀리면 누르려던 버튼이 손가락 밑에서 움직인다. */
.preview-ovl-thumb.is-loading img { opacity: 0; }
.preview-ovl-skel,
.preview-ovl-thumb.is-loading {
  background: linear-gradient(100deg,
    var(--pv-bg) 30%, rgba(255, 255, 255, .75) 50%, var(--pv-bg) 70%);
  background-size: 220% 100%;
  animation: preview-ovl-shimmer 1.1s linear infinite;
}
.preview-ovl-skel { display: block; height: 168px; border-radius: 8px; }
@keyframes preview-ovl-shimmer {
  from { background-position: 180% 0; }
  to { background-position: -80% 0; }
}
/* 움직임을 끈 사용자에게는 흐르지 않는 회색 자리만 남긴다. */
@media (prefers-reduced-motion: reduce) {
  .preview-ovl-skel,
  .preview-ovl-thumb.is-loading { animation: none; background: var(--pv-bg); }
}

/* 사진이 없을 때 — 이미지 자리를 통째로 비우지 않고 한 줄로 대신한다.
   빈 자리는 "못 불러왔다"로 읽히고, 한 줄은 "아직 없다"로 읽힌다. */
/* 사진 칸과 **같은 높이**를 쓴다. 로딩 자리가 168px 였다가 안내 문구에서
   갑자기 줄면 그 순간 아래가 통째로 올라붙어 눈에 거슬린다 — 자리를 미리
   잡아 둔 이유가 사라진다. */
.preview-ovl-nophoto {
  margin: 0; height: 168px; padding: 12px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  background: var(--pv-bg); color: var(--pv-text-mute);
  font-size: 13px; line-height: 1.4;
}
.preview-ovl-nophoto.is-pending {
  background: #fefce8; color: #a16207; font-weight: 600;
}
.preview-ovl-link {
  display: block; margin-top: 8px; padding: 9px; text-align: center;
  border: 1px solid var(--pv-border); border-radius: 8px; background: #fff;
  color: var(--pv-text); font-size: 13px; font-weight: 600; text-decoration: none;
}
/* 주소를 아직(혹은 영영) 모를 때. 자리는 지키고 누를 수 없게만 한다 —
   버튼이 나중에 생겨나면 그만큼 아래가 튄다. */
.preview-ovl-link.is-disabled {
  background: var(--pv-bg); color: var(--pv-text-mute);
  cursor: default; pointer-events: none; opacity: .6;
}
.preview-ovl-actions { margin-top: 16px; text-align: center; }
.preview-ovl-cancel {
  width: 100%; padding: 10px; border: 1px solid #fca5a5; border-radius: 8px;
  background: #fff; color: #b91c1c; font-size: 14px; font-weight: 600; cursor: pointer;
}
.preview-ovl-cancel:hover { background: #fef2f2; }
.preview-ovl-cancel:disabled { opacity: .5; cursor: default; }
.preview-ovl-warn {
  margin: 8px 0 0; font-size: 11px; line-height: 1.5; color: var(--pv-muted, #6b7280);
}
.preview-ovl-note { font-size: 13px; color: var(--pv-muted, #6b7280); }
.preview-ovl-release { display: block; margin-top: 8px; }
.preview-txn-time { font-variant-numeric: tabular-nums; color: var(--pv-text-soft); }
.preview-txn-booth { color: var(--pv-text-mute); }
.preview-txn-frame { color: var(--pv-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.preview-txn-method { font-size: 11px; color: var(--pv-text-soft); padding: 2px 6px; border: 1px solid var(--pv-border); border-radius: 999px; }
.preview-txn-amt { font-variant-numeric: tabular-nums; font-weight: 700; min-width: 70px; text-align: right; }
.preview-txn-more { display: block; width: 100%; margin-top: 10px; }
@media (max-width: 520px) {
  /* 모바일에서도 결제수단은 유지(컬럼 제거 금지), 부스만 숨김.
     세로점 칸을 빼먹으면 그것이 다음 줄로 떨어져 행이 두 겹이 된다. */
  .preview-txn-row {
    /* 초를 감추므로 "08-27 15:47" 11자면 된다. 남는 폭은 프레임 칸에 준다. */
    grid-template-columns: 68px minmax(0, 1fr) auto auto 18px;
    gap: 5px; font-size: 12px; padding: 8px 6px;
  }
  .preview-txn-booth { display: none; }
  .preview-txn-frame { min-width: 0; }
  /* 좁은 폭에서 "08-28 12:34:55" 는 한 줄에 안 들어간다. 초를 버린다 —
     같은 초에 두 건이 겹치는 일은 없고, 접히는 것보다 낫다. */
  .preview-txn-time { font-size: 11px; letter-spacing: -0.2px; }
  /* 좁은 폭에서는 더 줄인다. '중복결제 › 사진 전달' 이 최장 조합이다. */
  .preview-txn-row.has-action .preview-txn-frame,
  .preview-txn-row.is-photo-done .preview-txn-frame { font-size: 10px; }
  .preview-txn-time .sec { display: none; }
  .preview-txn-method { font-size: 10px; padding: 1px 5px; }
  .preview-txn-amt { min-width: 0; font-size: 12px; }
}

/* 모바일: 부스 카드 2열로(정확한 금액 수용) */
@media (max-width: 520px) {
  .preview-booth-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .preview-booth-revenue { font-size: 15px; }
  .preview-booth-card--all .preview-booth-revenue { font-size: 20px; }
}

/* ===== 하단 footer (안내 문구) ===== */
.preview-footer {
  margin-top: 28px;
  padding: 16px 4px 0;
  border-top: 1px solid var(--pv-border);
  color: var(--pv-text-mute);
  font-size: 12px;
  line-height: 1.6;
  text-align: center;
}

/* ── 사진 촬영중인 결제 ──────────────────────────────────────────────────
 * 결제 신호는 왔는데 사진이 오지 않은 건. 0건이면 통째로 숨는다(뷰에서 hidden).
 * 대부분의 날이 0건이라 자리를 늘 차지하면 눈에 안 들어오고, 그러면 정작 있을 때도
 * 안 보인다. 그래서 있을 때만 나타나고, 나타나면 눈에 띄어야 한다. */
.preview-pending {
  margin: 0 0 14px;
  border: 1px solid #f0c36d;
  background: #fff8e8;
  border-radius: 10px;
  overflow: hidden;
}

.preview-pending-head {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 11px 14px;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 14px;
  color: #7a5a12;
  text-align: left;
  cursor: pointer;
}

.preview-pending-head:hover {
  background: rgba(240, 195, 109, 0.14);
}

.preview-pending-mark {
  flex: none;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background: #e0a52a;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  line-height: 19px;
  text-align: center;
}

.preview-pending-text { flex: 1 1 auto; }
.preview-pending-text b { font-weight: 700; }

.preview-pending-chev {
  flex: none;
  font-size: 11px;
  transition: transform 0.15s ease;
}

.preview-pending.is-open .preview-pending-chev { transform: rotate(180deg); }

.preview-pending-list {
  border-top: 1px solid #f0c36d;
  max-height: 260px;
  overflow-y: auto;
}

.preview-pending-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  font-size: 13px;
  color: #5c4a20;
  border-bottom: 1px solid rgba(240, 195, 109, 0.4);
}

.preview-pending-row:last-child { border-bottom: 0; }

/* "08-27 14:04" 11자면 된다. 넉넉히 잡으면 그만큼 기기 코드가 잘리는데,
   어느 기기인지가 원인 추적의 출발점이라 그쪽이 더 중요하다. */
.preview-pending-when { flex: none; width: 76px; font-variant-numeric: tabular-nums; }
.preview-pending-store { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 어느 기기인지가 원인 추적의 출발점이라 잘리지 않게 둔다. */
.preview-pending-dev { flex: none; color: #8a7440; font-size: 12px; }
.preview-pending-amt { flex: none; text-align: right; font-variant-numeric: tabular-nums; }

/* 건마다 고르는 체크. 라벨 전체가 눌리므로 작은 상자를 겨냥하지 않아도 된다. */
.preview-pending-row { cursor: pointer; }
.preview-pending-pick { flex: none; margin: 0; accent-color: #b45309; }

/* 펼친 목록 맨 아래의 일괄 조치. 목록과 색을 맞추되 경계는 분명히 둔다 —
   위는 "무엇이 남았나"이고 여기는 "무엇을 할까"라 성격이 다르다. */
.preview-pending-bulk {
  padding: 12px 14px 14px;
  border-top: 2px solid #f0c36d;
  background: #fffdf6;
}
.preview-pending-bulk-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px; font-size: 12px; color: #5c4a20;
}
.preview-pending-all { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.preview-pending-all input { margin: 0; accent-color: #b45309; }
.preview-pending-picked { font-weight: 700; }
.preview-pending-bulk .preview-ovl-action-label { color: #8a7440; }

.preview-pending-more {
  padding: 8px 14px;
  font-size: 12px;
  color: #8a7440;
  text-align: center;
}

@media (max-width: 560px) {
  .preview-pending-when { width: auto; }
  .preview-pending-dev { display: none; }
}

/* 용지 표 — 잔량은 막대와 숫자를 같이 둔다. 숫자만 있으면 500과 90의 차이가
   한눈에 안 들어오고, 막대만 있으면 정확한 값을 못 읽는다. */
.preview-paper-table td { vertical-align: middle; }
.preview-paper-level { white-space: nowrap; }
.preview-paper-bar {
  display: inline-block; width: 54px; height: 6px; margin-right: 6px;
  background: var(--pv-border); border-radius: 999px; overflow: hidden; vertical-align: middle;
}
.preview-paper-bar i { display: block; height: 100%; background: #0d9488; }
.preview-paper-num { font-variant-numeric: tabular-nums; }
.preview-paper-act { white-space: nowrap; }

/* ── 용지 종합 보고 (/preview/:brand/paper) ───────────────────────────────── */
.preview-header-sub { margin-left: 8px; font-size: 13px; color: var(--pv-text-soft); font-weight: 500; }
.preview-paper-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; margin-bottom: 10px;
}
.preview-paper-filters { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.preview-paper-sort {
  padding: 4px 10px; border: 1px solid var(--pv-border); border-radius: 999px;
  background: var(--pv-surface); color: var(--pv-text-soft); font-size: 12px; cursor: pointer;
}
.preview-paper-sort.is-on { background: var(--pv-text); color: var(--pv-surface); border-color: var(--pv-text); }
.preview-paper-check { display: inline-flex; align-items: center; gap: 4px; color: var(--pv-text-soft); cursor: pointer; }

/* 지점 한 덩이 — 사람은 기기가 아니라 지점 단위로 움직인다. */
.preview-paper-store {
  margin-bottom: 14px; padding: 10px 12px; background: var(--pv-surface);
  border: 1px solid var(--pv-border); border-radius: var(--pv-radius-sm);
}
.preview-paper-store.is-issue { border-color: #fca5a5; background: #fffafa; }
.preview-paper-store-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.preview-paper-store-name { font-size: 14px; font-weight: 700; }
.preview-paper-store-code { font-size: 11px; color: var(--pv-text-mute); font-family: ui-monospace, Menlo, monospace; }
.preview-paper-store-sum { margin-left: auto; font-size: 11px; color: var(--pv-text-soft); }

.preview-paper-table td b { font-weight: 700; }
.preview-paper-table .is-ok { color: var(--pv-text); }
.preview-paper-table .is-warn { color: #b45309; }
.preview-paper-table .is-fault { color: #b91c1c; }
.preview-paper-dev { text-align: left !important; }
.preview-paper-link {
  padding: 0; border: 0; background: none; cursor: pointer; text-decoration: underline dotted;
  font-family: ui-monospace, Menlo, monospace; font-size: 11px; color: var(--pv-text);
}
.preview-paper-warn { display: block; font-size: 10px; color: #b91c1c; }
.preview-paper-bar i.is-warn { background: #d97706; }
.preview-paper-bar i.is-fault { background: #b91c1c; }
.preview-paper-hist td { background: var(--pv-bg, #fafafa); text-align: left; }
.preview-paper-hist-box { display: flex; gap: 20px; flex-wrap: wrap; padding: 6px 2px; }
.preview-paper-hist-box > div { flex: 1 1 320px; min-width: 0; }
.preview-paper-hist-box h4 { margin: 0 0 6px; font-size: 12px; color: var(--pv-text-soft); }
.preview-total-v.is-warn { color: #b45309; }
.preview-total-v.is-fault { color: #b91c1c; }

@media (max-width: 560px) {
  .preview-paper-store-sum { margin-left: 0; }
  .preview-paper-hist-box { gap: 10px; }
}

/* ── 용지 보고 — 모바일 ─────────────────────────────────────────────────────
   표를 가로로 밀어 보게 하면 한 기기의 값을 끝까지 따라가지 못한다(2026-09-21 잘림 제보).
   좁은 화면에서는 기기 하나를 카드 한 장으로 편다: 칸 이름을 값 위에 작게 달고 두 줄로 놓는다. */
@media (max-width: 640px) {
  .preview-header-inner { flex-wrap: wrap; gap: 6px; }
  .preview-header-actions { flex-wrap: wrap; gap: 6px; }
  .preview-total-grid { grid-template-columns: repeat(2, 1fr); }
  .preview-total-cell { padding: 10px 12px; }

  .preview-paper-store { padding: 10px; }
  .preview-paper-store .preview-dev-scroll { overflow: visible; }
  .preview-paper-table, .preview-paper-table tbody,
  .preview-paper-mini, .preview-paper-mini tbody { display: block; width: 100%; border: 0; }
  .preview-paper-table thead, .preview-paper-mini thead { display: none; }

  .preview-paper-row {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px;
    padding: 12px 0; border-bottom: 1px solid var(--pv-border);
  }
  .preview-paper-row:last-of-type { border-bottom: 0; }
  .preview-paper-row td {
    display: block; padding: 0; border: 0; text-align: left; white-space: normal; font-size: 13px;
  }
  .preview-paper-row td[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 2px;
    font-size: 10px; color: var(--pv-text-soft); font-weight: 400;
  }
  .preview-paper-dev, .preview-paper-act { grid-column: 1 / -1; }
  .preview-paper-link { font-size: 13px; }
  .preview-paper-bar { width: 70px; }

  .preview-paper-hist:not([hidden]) { display: block; }
  .preview-paper-hist td { display: block; padding: 8px 0; }
  .preview-paper-mini tr {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px 10px;
    padding: 8px 0; border-bottom: 1px dashed var(--pv-border);
  }
  .preview-paper-mini td { display: block; padding: 0; border: 0; text-align: left; font-size: 12px; }
  .preview-paper-mini td[data-label]::before {
    content: attr(data-label); display: block; font-size: 10px; color: var(--pv-text-soft);
  }
  .preview-paper-filters { width: 100%; flex-wrap: wrap; }
}

/* 상단 접근 메뉴 — 헤더 바로 아래 한 줄. 좁은 화면에서는 가로로 밀어 본다. */
.preview-subnav {
  display: flex; gap: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch;
  max-width: 1200px; margin: 0 auto; padding: 14px 20px 0;
  scrollbar-width: none;
}
.preview-subnav::-webkit-scrollbar { display: none; }
.preview-subnav-item {
  flex: 0 0 auto; padding: 5px 12px; border-radius: 999px; font-size: 12px; font-weight: 500;
  color: var(--pv-text-soft); background: var(--pv-surface); border: 1px solid var(--pv-border);
  text-decoration: none; white-space: nowrap;
}
.preview-subnav-item:hover { color: var(--pv-text); }
.preview-subnav-item.is-on { color: var(--pv-surface); background: var(--pv-text); border-color: var(--pv-text); }

/* ── 톤 맞춤(2026-09-21) — 용지 화면이 다른 화면과 따로 놀았다 ──────────────
   메뉴가 본문보다 4px 안쪽에서 시작했고, 표 글자·버튼이 다른 화면보다 한 단계 작았다. */
.preview-subnav + .preview-main { padding-top: 16px; }
.preview-subnav-item, .preview-paper-sort { padding: 6px 14px; font-size: 13px; }
.preview-paper-table { font-size: 13px; }
.preview-paper-table th { font-size: 12px; }
.preview-paper-link { text-decoration: none; text-align: left; font-family: inherit; }
.preview-paper-link:hover .preview-paper-devname { text-decoration: underline; }
.preview-paper-devname { display: block; font-size: 13px; font-weight: 600; color: var(--pv-text); }
.preview-paper-devcode {
  display: block; margin-top: 1px; font-size: 11px; color: var(--pv-text-mute);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.preview-paper-store-name { font-size: 15px; }
.preview-paper-store-sum { font-size: 12px; }
@media (max-width: 640px) {
  .preview-subnav { padding: 10px 16px 0; }
}

/* 조작 칸 — 이력(왼쪽)·재고(오른쪽) 두 개. 넓은 화면에서는 칸 오른쪽에 붙이고,
   좁은 화면에서는 반반으로 나눠 손가락으로 누르기 좋게 한다. */
.preview-paper-act { text-align: right; white-space: nowrap; }
@media (max-width: 640px) {
}

/* ── 버튼 체계 ────────────────────────────────────────────────────────────────
   기본 .preview-btn(외곽선) 하나에 크기와 성격만 덧붙인다. 화면마다 따로 만들었더니
   같은 「재고」 버튼이 화면마다 크기·여백이 달랐다(2026-09-21).
     크기   --sm        표 안 조작
     성격   (기본)      외곽선 — 보기·이동(이력 등)
            --solid     진한 채움 — 값을 바꾸는 조작(재고 입력 등)
            --primary   강조색 — 화면의 주 행동(로그인·적용)
            --ghost     배경 없음 — 머리글 보조(새로고침·로그아웃) */
.preview-btn--sm { padding: 6px 12px; font-size: 12px; line-height: 1.2; border-radius: 8px; }
.preview-btn--solid { background: var(--pv-text); border-color: var(--pv-text); color: var(--pv-surface); }
.preview-btn--solid:hover { background: #2b2b2b; border-color: #2b2b2b; }
.preview-paper-act .preview-btn + .preview-btn { margin-left: 6px; }
@media (max-width: 640px) {
  /* 카드 칸 규칙(.preview-paper-row td { display:block })보다 구체적이어야 이긴다. */
  .preview-paper-row td.preview-paper-act { display: flex; gap: 8px; }
  .preview-paper-row td.preview-paper-act .preview-btn { flex: 1 1 0; margin: 0; padding: 11px 12px; font-size: 14px; }
}

/* ── 이력 창(용지) ───────────────────────────────────────────────────────────
   넓은 창 하나. 좁은 화면에서는 아래에서 올라오는 전체 폭 시트로 편다. */
body.is-modal-open { overflow: hidden; }
.preview-modal { position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center; }
.preview-modal[hidden] { display: none; }
.preview-modal-backdrop { position: absolute; inset: 0; background: rgba(15, 17, 21, .45); }
.preview-modal-box {
  position: relative; display: flex; flex-direction: column;
  width: min(760px, 92vw); max-height: 86vh;
  background: var(--pv-surface); border-radius: 14px; box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
}
.preview-modal-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 18px 20px 10px;
}
.preview-modal-title { margin: 0; font-size: 17px; font-weight: 700; }
.preview-modal-sub { margin: 4px 0 0; font-size: 12px; color: var(--pv-text-soft); }
.preview-modal-tabs { display: flex; gap: 4px; padding: 0 20px; border-bottom: 1px solid var(--pv-border); }
.preview-modal-tab {
  padding: 9px 14px; border: 0; background: none; font-size: 14px; font-weight: 500;
  color: var(--pv-text-soft); border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer;
}
.preview-modal-tab.is-on { color: var(--pv-text); border-bottom-color: var(--pv-text); font-weight: 700; }
.preview-modal-body { flex: 1 1 auto; overflow-y: auto; padding: 8px 20px; }
.preview-modal-foot { padding: 10px 20px 18px; text-align: center; }
.preview-modal-foot .preview-btn { min-width: 160px; }

.preview-hist-list { list-style: none; margin: 0; padding: 0; }
.preview-hist-item { padding: 12px 0; border-bottom: 1px solid var(--pv-border); }
.preview-hist-item:last-child { border-bottom: 0; }
.preview-hist-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; }
.preview-hist-main { margin-top: 4px; font-size: 14px; font-variant-numeric: tabular-nums; }
.preview-hist-soft { margin-top: 2px; font-size: 12px; color: var(--pv-text-soft); }
.preview-hist-tag {
  flex: 0 0 auto; padding: 2px 9px; border-radius: 999px; font-size: 11px; font-weight: 600;
  background: #f1f3f5; color: var(--pv-text-soft);
}
.preview-hist-tag.is-up { background: #dcfce7; color: #166534; }
.preview-hist-tag.is-down { background: #fee2e2; color: #991b1b; }
.preview-hist-empty { padding: 28px 0; text-align: center; font-size: 13px; color: var(--pv-text-soft); }

@media (max-width: 640px) {
  .preview-modal { align-items: flex-end; }
  .preview-modal-box { width: 100vw; max-height: 88vh; border-radius: 16px 16px 0 0; }
  .preview-modal-head, .preview-modal-body, .preview-modal-foot { padding-left: 16px; padding-right: 16px; }
  .preview-modal-tabs { padding: 0 16px; }
  .preview-modal-foot .preview-btn { width: 100%; }
}

/* 프린트 잔량 카드 — 기기 수만큼 나눈다(열 수는 grid.js 가 정한다). 용지 관리 브랜드는
   기기 이름·재고·「관리」를 카드 안에 둔다 — 지점 화면에서 따로 표를 펼칠 필요가 없게. */
.preview-kiosks-strip .preview-kiosk-chip {
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
  padding: 12px 14px; border-radius: var(--pv-radius); text-align: left;
}
.preview-kiosk-chip-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; min-width: 0; }
/* 번호는 위 줄, 이름은 아래 줄에 다 보이게(말줄임 없이) */
.preview-kiosks-strip .preview-kiosk-chip-id {
  margin: 0; min-width: 0; font-family: inherit; font-size: 14px; font-weight: 700; color: var(--pv-text);
  line-height: 1.3; word-break: keep-all;
}
.preview-kiosk-chip-id .preview-booth-code { display: block; margin: 0 0 1px; }
.preview-kiosk-chip-body { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.preview-kiosks-strip .preview-kiosk-chip-remain { font-size: 22px; font-weight: 800; letter-spacing: -0.01em; }
.preview-kiosk-chip-remain small { margin-left: 2px; font-size: 12px; font-weight: 500; color: var(--pv-text-soft); }
.preview-kiosk-chip-stock { font-size: 12px; color: var(--pv-text-soft); white-space: nowrap; }
.preview-kiosk-chip-stock b { color: var(--pv-text); }
.preview-kiosk-chip-bar { margin-top: 2px; }
.preview-kiosk-chip--low .preview-kiosk-chip-bar i { background: var(--pv-danger); }
/* 「관리」— 카드 머리 오른쪽의 작은 글자 버튼 */
.preview-kiosk-manage {
  flex: none; padding: 3px 9px; border: 1px solid var(--pv-border); border-radius: 999px;
  background: var(--pv-surface); color: var(--pv-text-soft); font: inherit; font-size: 11px; font-weight: 600; cursor: pointer;
}
.preview-kiosk-manage:hover { color: var(--pv-text); border-color: #c9c9d1; }

/* 관리 창 다듬기(2026-09-21) — 좁은 폭에서 머리 버튼 둘이 세로로 쌓이고, 탭에 클릭 포커스
   테두리가 남고, 한 건이 세 줄을 먹었다. 닫기는 ✕ 하나로, 손이 가는 버튼은 아래로 내린다. */
.preview-modal-headtext { min-width: 0; flex: 1 1 auto; }
.preview-modal-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.preview-modal-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.preview-modal-close {
  flex: 0 0 auto; width: 34px; height: 34px; margin: -4px -6px 0 0;
  border: 0; border-radius: 8px; background: none; font-size: 18px; line-height: 1;
  color: var(--pv-text-soft); cursor: pointer;
}
.preview-modal-close:hover { background: var(--pv-bg, #f4f5f7); color: var(--pv-text); }
.preview-modal-tab:focus { outline: none; }
.preview-modal-tab:focus-visible { outline: 2px solid var(--pv-accent); outline-offset: -2px; border-radius: 6px; }
.preview-modal-foot { display: flex; gap: 8px; }
.preview-modal-foot .preview-btn { flex: 1 1 0; min-width: 0; padding: 11px 12px; font-size: 14px; }

.preview-hist-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 0; }
.preview-hist-left { min-width: 0; }
.preview-hist-date { font-size: 12px; color: var(--pv-text-soft); font-variant-numeric: tabular-nums; }
.preview-hist-item .preview-hist-main { margin-top: 2px; font-size: 14px; font-weight: 600; }

/* 프린트 잔량 자리 잡기 — 불러오는 동안 카드 한 장 크기의 회색 틀을 둔다. */
.preview-kiosk-skel {
  grid-column: 1 / -1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; min-height: 64px; padding: 10px; border: 1px solid var(--pv-border);
  border-radius: var(--pv-radius-sm); background: var(--pv-surface);
}
.preview-kiosk-skel.is-tall { min-height: 96px; }
.preview-kiosk-skel i {
  display: block; height: 10px; width: 40%; border-radius: 6px;
  background: linear-gradient(90deg, #eef0f3 0%, #f7f8fa 50%, #eef0f3 100%);
  background-size: 200% 100%; animation: pv-skel 1.2s ease-in-out infinite;
}
.preview-kiosk-skel i:nth-child(2) { width: 25%; height: 16px; }
.preview-kiosk-skel i:nth-child(3) { width: 55%; }
@keyframes pv-skel { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }

/* 재고 변동 태그 — 누가 바꿨나. 자동(교체 감지)은 파랑, 사용자 입력은 호박, PB24 이관은 회색. */
.preview-hist-tag.is-auto { background: #dbeafe; color: #1e40af; }
.preview-hist-tag.is-manual { background: #fef3c7; color: #92400e; }
.preview-hist-tag.is-pb24 { background: #f1f3f5; color: var(--pv-text-soft); }

/* 태그 두 줄 — 위: 자동 차감(파랑)/사용자 입력(호박), 아래: 무엇이 얼마나(중립). */
.preview-hist-tags { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex: 0 0 auto; }
