/* 오토젠 실사 PWA — 야외/장갑 사용 전제 큼직한 UI (R9) */
:root {
  --bg: #101418;
  --panel: #1a2028;
  --panel2: #222a34;
  --line: #303a46;
  --text: #eef2f6;
  --dim: #93a0ae;
  --accent: #3d8bfd;
  --ok: #34c77b;
  --warn: #f0a832;
  --err: #ef5350;
  --swatch-line: rgba(255, 255, 255, .35);
  --shadow: rgba(0, 0, 0, .45);
  /* ALC 배지 — 야외/장갑 전제라 '강조색 글자'로는 약하다. 불투명 칩 + 반대 명도 글자로 대비 확보
     (다크: 밝은 칩 + 어두운 글자 = 약 6.9:1, 토스트와 같은 방식) */
  --alc-bg: #4f9dff;
  --alc-fg: #071019;
  --alc-warn-bg: #f0a832;
  --alc-warn-fg: #241800;
  /* 품목 카드 윤곽 — --line 보다 한 단계 밝게. 카드가 서로 붙어 보이지 않도록 (정호님 2026-08-13) */
  --card-line: #3f4d5e;
}
:root[data-theme="light"] {
  --bg: #f2f4f7;
  --panel: #ffffff;
  --panel2: #e7ebf0;
  --line: #cfd6de;
  --text: #17202a;
  --dim: #5b6673;
  --accent: #2f6fd0;
  --ok: #1e9e5f;
  --warn: #b97d14;
  --err: #d64541;
  --swatch-line: rgba(0, 0, 0, .3);
  --shadow: rgba(0, 0, 0, .25);
  /* 라이트는 반대로 진한 칩 + 흰 글자 (약 9.2:1) — 흰 배경에서도 칩 자체가 먼저 눈에 띈다 */
  --alc-bg: #14458f;
  --alc-fg: #ffffff;
  --alc-warn-bg: #8a5200;
  --alc-warn-fg: #ffffff;
  --card-line: #bcc7d4;
}
/* 글자 크기 기준 — 전 화면 font-size 는 rem. 큰글씨 모드는 루트만 키우면 전체 확대.
   2026-08-13 정호님 지시로 한 단계씩 상향: 기본 17→20(옛 큰글씨), 큰글씨 20→23. */
html { font-size: 20px; }
html[data-font="big"] { font-size: 23px; }
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: "Malgun Gothic", "맑은 고딕", system-ui, sans-serif;
  font-size: 1rem;
  overscroll-behavior: none;
  /* ★한글은 **어절 단위로** 끊는다 (2026-08-16). 기본값은 글자 아무 데서나 끊어서
     "정명산/업", "BH로지/스" 같은 꼴이 된다 — 글자가 클수록(기본 20px·큰글씨 23px) 더 자주 난다.
     overflow-wrap:anywhere 는 안전판 — 품번처럼 띄어쓰기가 없는 긴 토큰은 넘치느니 끊는다. */
  word-break: keep-all;
  overflow-wrap: anywhere;
}
#app { max-width: 640px; margin: 0 auto; min-height: 100%; display: flex; flex-direction: column; }
.boot { padding: 40px; text-align: center; color: var(--dim); }

/* 헤더 */
.hdr {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: 10px;
  padding: calc(env(safe-area-inset-top) + 10px) 14px 10px;
  background: var(--bg); border-bottom: 1px solid var(--line);
}
.hdr .title { font-size: 1.12rem; font-weight: 700; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hdr .sub { color: var(--dim); font-size: 0.76rem; }
.hdr button { flex-shrink: 0; }

/* 공통 버튼 */
button {
  font: inherit; color: var(--text); border: 1px solid var(--line);
  background: var(--panel2); border-radius: 12px; padding: 12px 16px;
  font-size: 1rem; cursor: pointer; touch-action: manipulation;
}
button:active { filter: brightness(1.25); }
button.primary { background: var(--accent); border-color: var(--accent); font-weight: 700; }
button.big { width: 100%; padding: 18px; font-size: 1.18rem; border-radius: 14px; }
button.ghost { background: none; border: none; color: var(--dim); padding: 8px; }
button:disabled { opacity: .45; }

.screen { flex: 1; padding: 16px 14px calc(env(safe-area-inset-bottom) + 24px); display: flex; flex-direction: column; gap: 12px; }
.center { justify-content: center; }
h1 { font-size: 1.53rem; text-align: center; }
.note { color: var(--dim); font-size: 0.82rem; text-align: center; }
.err { color: var(--err); font-size: 0.88rem; text-align: center; min-height: 20px; }

input[type=text], input[type=password], input[type=tel], input[type=search] {
  font: inherit; font-size: 1.18rem; color: var(--text);
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 16px; width: 100%; outline: none;
}
input:focus { border-color: var(--accent); }

/* 위치/도메인 선택 버튼 목록 */
.choices { display: flex; flex-direction: column; gap: 10px; }
.choices button { text-align: left; padding: 18px 16px; font-size: 1.12rem; display: flex; justify-content: space-between; align-items: center; }
.choices .tag { font-size: 0.76rem; color: var(--dim); }

/* 도구 줄 — 왼쪽 진행 요약 + 오른쪽 기능 버튼. 필터(조회 조건)와 시각적으로 분리한다.
   필터는 '무엇을 볼까', 도구는 '어떻게 볼까·무엇을 할까' 로 성격이 달라 같은 줄에 두지 않는다. */
.toolbar {
  display: flex; align-items: center; gap: 10px;
  padding-top: 2px; border-top: 1px solid var(--line);
  /* ★버튼이 하나 늘면(2026-09-05 '종이') 360px 에서 줄이 넘친다 — 접히게 둔다.
     좁을 때만 두 줄이 되고 넓으면 한 줄 그대로다. 넘치면 화면 절반을 못 보게 된다. */
  flex-wrap: wrap; row-gap: 8px;
}
.toolbar .note { flex: 1; min-width: 0; text-align: left; }
.toolbar .tools { display: flex; gap: 6px; flex-shrink: 0; }
.toolbar .tools button {
  border: 1px solid var(--line); border-radius: 10px; padding: 8px 14px; font-size: 0.88rem;
}
.toolbar .tools button.danger { color: var(--err); border-color: var(--err); }
/* ─── 검색 줄 (2026-09-05) — 사내 '당사' 한 곳에 300종 넘게 매달려 있어 드롭다운으로는 못 찾는다.
   현장은 눈에 보이는 것을 적는데 앱은 목록에서 찾게 하고 있었다. 그 간극을 메우는 줄이다. */
.searchrow { display: flex; align-items: center; gap: 6px; }
.searchrow input[type=search] { flex: 1; min-width: 0; font-size: 1.06rem; padding: 11px 14px; }
.searchrow .ghost {
  flex-shrink: 0; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel2); color: var(--text); padding: 10px 14px; font-size: 1.06rem;
}

/* 보기 전환 = 상태(세그먼트). 붙여 두면 '둘 중 하나' 로 읽히고, 떨어진 버튼은 '행동' 으로 읽힌다. */
.viewseg { display: flex; flex-shrink: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.viewseg button {
  border: 0; border-radius: 0; padding: 8px 12px; font-size: 0.88rem;
  background: none; color: var(--dim);
}
.viewseg button + button { border-left: 1px solid var(--line); }
.viewseg button.on { background: var(--accent); color: #fff; font-weight: 700; }

/* 목록 보기 행 (2026-08-14) — 한 화면에 많이. 카드보다 낮지만 터치 목표는 충분히 크게(≈52px) */
.lrow {
  display: flex; align-items: center; gap: 10px;
  background: var(--panel); border: 1px solid var(--card-line); border-radius: 10px;
  padding: 9px 12px; cursor: pointer;
}
.lrow:active { background: var(--panel2); }
.lrow .swatch { width: 22px; height: 22px; border-width: 1px; }
.lrow .la {
  flex-shrink: 0; min-width: 4.2rem;
  font-family: Consolas, "D2Coding", monospace; font-size: 0.94rem; font-weight: 800;
  color: var(--alc-bg);
}
.lrow .ln {
  flex: 1; min-width: 0; font-size: 0.88rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lrow .lq { flex-shrink: 0; font-size: 1.06rem; font-weight: 800; font-variant-numeric: tabular-nums; }
/* 미입력 = 찾아야 하는 것이므로 흐리게가 아니라 눈에 띄게 */
.lrow.todo { border-color: var(--warn); }
.lrow.todo .lq { color: var(--warn); font-size: 0.82rem; font-weight: 700; }

/* ─── 서열장 목록 행 (2026-09-03 정호님 "ALC 를 눈에 띄게 · LH/RH 표시 · 품목명 제외") ───
   품명을 뺀 만큼 폭이 남으므로 ALC 를 카드와 같은 불투명 칩으로 올린다.
   ⚠ 강조색 '글자' 로 되돌리지 말 것 — 같은 색 위에 얹히면 대비가 깎인다(2026-08-13 지적). */
.lrow.seq { gap: 8px; }
.lrow.seq .alc {
  font-size: 1.06rem; padding: 2px 8px 3px;
  min-width: 3.4rem;   /* 값 길이가 달라도 측 배지·수량 열이 행마다 어긋나지 않게 */
}
.lrow.seq .lq { margin-left: auto; }   /* 품명(flex:1)이 빠진 자리 — 수량은 계속 오른쪽 끝 */
/* 측 배지 — 글자가 1순위. 안쪽 세로줄(왼쪽/오른쪽)은 훑을 때 쓰는 보조 신호. */
.lrow .lside {
  flex-shrink: 0; min-width: 2.1rem; text-align: center;
  border: 1px solid var(--line); border-radius: 8px; padding: 2px 7px 3px;
  background: var(--panel2); color: var(--text);
  font-family: Consolas, "D2Coding", monospace; font-size: 0.94rem; font-weight: 800; letter-spacing: .5px;
}
.lrow .lside.s-lh { box-shadow: inset 3px 0 0 var(--accent); }
.lrow .lside.s-rh { box-shadow: inset -3px 0 0 var(--accent); }
/* 측을 모르는 행 — 빈 자리만 남긴다(자리를 없애면 아래 행 수량 열이 어긋난다) */
.lrow .lside.s-na { border-color: transparent; background: none; }
/* ALC 미상 행에만 붙는 품번 뒷자리 — 그 행에 남은 유일한 단서라 잘리면 뜻이 없다.
   그래서 같은 줄에 끼우지 않고 **아랫줄로 내린다**(order 로 '없음' 뒤에 놓아 줄바꿈을 강제).
   ⚠ 이 행만 두 줄이 된다 — 426 중 15건이라 목록 밀도에는 사실상 영향이 없다. */
.lrow.noalc { flex-wrap: wrap; row-gap: 2px; }
.lrow .lk {
  flex: 1 0 100%; order: 9; padding-left: 30px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--dim); font-size: 0.76rem; font-weight: 700;
  font-family: Consolas, "D2Coding", monospace; letter-spacing: .5px;
}

/* ─── 세는 종이 (2026-09-05) ───────────────────────────────────────────────────
   종이 카드는 품목 카드가 아니다 — 이름·줄 수·합계·상태만 본다. 품목 카드 골격을 쓰되
   왼쪽 색 견본이 없어 왼쪽 여백만 맞춘다. */
.sheetcard .cbody { padding-left: 2px; }
.sheetcard .pn { justify-content: space-between; }
.sheetcard .badge { margin-left: auto; }
/* 검색 결과 줄 — 누르면 키패드가 열린다는 뜻으로 오른쪽에 ＋ 를 크게 */
.lrow.hit .lq { color: var(--accent); font-size: 1.29rem; }
/* 취소된 줄 표시 (이력의 취소 표식과 같은 모양을 쓴다) */
.lrow .tagx { flex-shrink: 0; }

/* 서열장 행 = 선택 버튼 + 숨김/복원 토글 (정호님 2026-08-13 "담당 아닌 서열장 제외") */
.loc-row { display: flex; gap: 8px; align-items: stretch; }
/* ★이름 / 진행을 두 줄로 쌓는다 — 한 줄에 나란히 두면 글자 크기(20px·큰글씨 23px)에 따라
   폭을 다투다 "정명산/업" 처럼 단어 중간에서 잘린다 (2026-08-16 정호님 화면 제보). */
.loc-row .loc-pick {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left;
}
.loc-row .loc-pick .nm { font-size: 1.12rem; font-weight: 700; line-height: 1.25; }
.loc-row .loc-pick .meta { font-size: 0.76rem; color: var(--dim); font-weight: 600; }
.loc-row .loc-pick .meta b { color: var(--warn); }
.loc-row .loc-pick.dim .nm { color: var(--dim); font-weight: 600; }
.loc-row .loc-hide {
  flex-shrink: 0; width: 56px; padding: 0; font-size: 1.12rem;
  color: var(--dim); border: 1px solid var(--line);
}
.loc-row .loc-hide:disabled { opacity: .3; }

/* 품목 카드 목록 (S2) */
.cards { display: flex; flex-direction: column; gap: 10px; }
/* .card = 스와이프 시 드러나는 삭제 배경(빨강) 레이어. 테두리는 .cbody 가 갖는다 —
   양쪽에 다 주면 1px 이 두 겹으로 겹쳐 2px 이중선처럼 보인다. 반경도 .cbody 와 맞춰 둔다. */
.card {
  background: var(--err); border: 0; border-radius: 13px;
  cursor: pointer; overflow: hidden;
}
/* 슬라이딩 레이어 — 스와이프 시 손가락을 따라 이동, 뒤의 삭제 버튼이 드러남.
   ★테두리는 여기에 준다 (정호님 2026-08-13). 실제로 보이는 면이 .card 가 아니라 이 레이어라,
   .card 쪽 테두리만 두면 카드끼리 붙어 보인다. */
.cbody {
  display: flex; align-items: center; gap: 12px; padding: 14px;
  background: var(--panel); border: 1px solid var(--card-line); border-radius: 13px;
  position: relative; z-index: 1; transition: transform .18s ease;
}
.cbody:active { background: var(--panel2); }
.swatch {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  border: 2px solid var(--swatch-line);
}
.swatch.unconfirmed { border: 2px dashed var(--warn); }
.swatch.none { background: repeating-linear-gradient(45deg, #444 0 4px, #2a2a2a 4px 8px); }
.card .mid { flex: 1; min-width: 0; }
/* 제목 줄 (카드/시트 공통) = ALC 배지 + 차종·품명 을 한 행에.
   현장은 ALC 를 기준으로 실사하므로 ALC 가 항상 줄 맨 앞을 차지하고,
   폭이 모자라면 뒤의 차종·품명이 다음 줄로 넘어간다 (ALC 는 절대 잘리지 않음). */
.card .pn, .sheet .head .pn { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 8px; }
.card .pn { font-size: 1.06rem; font-weight: 700; }
.sheet .head .pn { font-size: 1.12rem; font-weight: 700; }
/* ★break-all 을 뺐다 (2026-08-16) — 차종·품명이 한글이라 글자 아무 데서나 끊겼다.
   body 의 keep-all + overflow-wrap:anywhere 가 어절 단위로 끊고, 정 넘칠 때만 강제로 끊는다. */
.pn .ttl { min-width: 0; }
.card .meta { color: var(--dim); font-size: 0.76rem; margin-top: 3px; }
.pnref { color: var(--dim); opacity: .75; font-size: 0.71rem; margin-top: 2px; font-family: Consolas, monospace; }
.card .qty { text-align: right; flex-shrink: 0; }
.card .qty .n { font-size: 1.41rem; font-weight: 800; }
.card .qty .t { color: var(--dim); font-size: 0.71rem; }
.card .qty .none { color: var(--dim); font-size: 0.88rem; }

/* ALC 배지 — 현장 실사의 1순위 대조값 (정호님 2026-08-13). 품번·품명보다 눈에 먼저 들어와야 하므로
   불투명 칩(--alc-bg) + 반대 명도 글자(--alc-fg) 로 카드에서 가장 강한 요소로 둔다.
   ⚠ 강조색 글자 + 반투명 배경으로 만들었다가 "가시성이 떨어진다" 지적 받고 교체(2026-08-13).
   값이 없으면 경고색 칩으로 '미상'. */
.alc {
  display: inline-flex; align-items: baseline; gap: 5px; flex-shrink: 0;
  background: var(--alc-bg); border: 1px solid var(--alc-bg); color: var(--alc-fg);
  border-radius: 9px; padding: 2px 9px 3px;
  font-family: Consolas, "D2Coding", monospace; font-size: 1.12rem; font-weight: 800; letter-spacing: .5px;
}
.alc.none { background: var(--alc-warn-bg); border-color: var(--alc-warn-bg); color: var(--alc-warn-fg); }
.sheet .head .alc { font-size: 1.18rem; padding: 3px 9px 4px; }

/* 입력 이력 + 취소 (키패드 시트) — 값을 넣는 자리에서 바로 되돌릴 수 있게 (정호님 2026-08-13).
   취소된 입력도 계속 보여 준다. 감추면 "왜 값이 바뀌었지" 를 현장에서 확인할 수 없다. */
.hist { display: flex; flex-direction: column; gap: 6px; }
.hist-h { color: var(--dim); font-size: 0.76rem; font-weight: 700; }
.hist-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--panel2); border: 1px solid var(--card-line);
  border-radius: 10px; padding: 8px 10px;
}
.hist-row .q { font-size: 1.12rem; font-weight: 800; min-width: 3.2rem; }
.hist-row .w { flex: 1; min-width: 0; color: var(--dim); font-size: 0.76rem; }
/* 세는 식(40+8+8+40) — 수량 옆이 아니라 시각·입력자 위에 둔다. 근거가 먼저 읽혀야 한다. */
.hist-row .hexpr { display: block; color: var(--text); font-family: Consolas, "D2Coding", monospace;
  font-size: 1.12rem; font-weight: 800; letter-spacing: .5px; overflow-wrap: anywhere; margin-bottom: 1px; }
.hist-row .undo {
  flex-shrink: 0; padding: 8px 14px; font-size: 0.88rem;
  color: var(--err); border-color: var(--err); background: none;
}
.hist-row .tagx { flex-shrink: 0; color: var(--warn); font-size: 0.76rem; font-weight: 700; }
/* 취소된 입력 — 수량에 취소선, 전체를 흐리게 */
.hist-row.off { opacity: .6; }
.hist-row.off .q { text-decoration: line-through; text-decoration-thickness: 2px; }

/* 짝 동시 저장 체크 (키패드) */
/* ─── 키패드 머리말 [LH│RH] 스위치 (2026-09-03 정호님 "키패드 입력창에서 LH/RH 를 변경해서 바로 입력") ───
   ★ALC 칩 오른쪽, 같은 줄에 둔다 — 360px 에서 머리말은 이미 두 줄로 접혀 있어(1줄=ALC+✕, 2줄=차종·품명)
     여기가 비어 있다. 별도 행으로 빼면 시트가 커져 저장 버튼이 화면 밖으로 나간다.
   ★버튼에 그 측의 현재값을 같이 적는다 — 바꾸기 전에 L=96 / R=52 를 눈으로 대조하게 된다.
   ★종전의 「반대측도 같은 수량으로 저장」 체크(.pairsave)를 이것이 대체했다 — 되살리지 말 것.
     상태가 기억되는 체크라 안 센 값이 반대측 품번에 조용히 기록될 수 있었다. */
.sideseg { display: inline-flex; flex-shrink: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.sideseg button {
  border: 0; border-radius: 0; background: var(--panel2); color: var(--dim);
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 3px 8px 4px; font-family: Consolas, "D2Coding", monospace;
  font-size: 0.82rem; font-weight: 800; letter-spacing: .5px; line-height: 1.1;
}
.sideseg button + button { border-left: 1px solid var(--line); }
.sideseg button.on { background: var(--accent); color: #fff; }
.sideseg .v {
  font-family: "Malgun Gothic", "맑은 고딕", system-ui, sans-serif;
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0; opacity: .85;
}
/* 짝이 없어 바꿀 데가 없는 품목 — 그래도 어느 측인지는 보여 준다(종전엔 머리말에 측이 아예 없었다) */
.sideone {
  flex-shrink: 0; border: 1px solid var(--line); border-radius: 10px; padding: 2px 8px 3px;
  background: var(--panel2); color: var(--text);
  font-family: Consolas, "D2Coding", monospace; font-size: 0.88rem; font-weight: 800; letter-spacing: .5px;
}

/* 키패드 (S3) */
.overlay {
  position: fixed; inset: 0; z-index: 50; background: rgba(0,0,0,.55);
  display: flex; align-items: flex-end; justify-content: center;
}
/* ★시트는 화면보다 길어질 수 있다 — 그러면 **스크롤**되어야 한다 (2026-08-16, 정호님 제보).
   종전엔 max-height·overflow 가 없었고 .overlay 가 align-items:flex-end 라, 내용이 길어지면
   시트 **위쪽이 화면 밖으로 밀려 나가고 잡을 방법이 없었다.**
   실제로 설정 시트에 스캔 항목을 더하자 맨 위의 테마·글자 크기가 통째로 잘려 "사라졌다".
   ⚠ 앞으로 시트에 항목을 더할 때 이 규칙이 없으면 같은 일이 반복된다.
   dvh 는 모바일 주소창까지 반영한다 — 지원 안 하면 앞줄 vh 가 남는다. */
.sheet {
  background: var(--panel); width: 100%; max-width: 640px;
  border-radius: 20px 20px 0 0; padding: 18px 16px calc(env(safe-area-inset-bottom) + 16px);
  display: flex; flex-direction: column; gap: 12px;
  max-height: 92vh;
  max-height: 92dvh;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;   /* 시트 끝에서 뒤 목록이 딸려 스크롤되지 않게 */
}
/* 세로 flex + overflow 면 자식이 눌려 찌그러진다 — 키패드·수량 표시가 특히 그렇다 */
.sheet > * { flex-shrink: 0; }

/* ★키패드 시트만 스크롤 방식을 달리한다 (2026-08-30 정호님 "버튼이 스크롤을 해야 보이는데 굉장히 좋지 않아").
   시트 전체가 스크롤이면 이력이 쌓이거나 짝 카드로 열 때 [저장] 이 화면 밖으로 밀린다 —
   현장에서 한 손으로 쓰는 화면이라 조작부는 <b>늘 보여야</b> 한다.
     .kscroll = 참고 영역(이력·짝 체크)만 스크롤
     .kfix    = 표시·키패드·없음(0)·[닫기][지움][저장] — 바닥 고정, 절대 안 밀림
   시트 자체의 overflow 는 끈다(스크롤 주체가 둘이면 어느 쪽이 움직일지 알 수 없다).
   2026-08-16 의 '위쪽이 잘려 사라지는' 문제는 .kscroll 이 줄어들며 흡수하므로 그대로 막힌다. */
.sheet.keypad { overflow: hidden; }
.sheet.keypad .kscroll {
  flex: 1 1 auto; min-height: 0;          /* min-height:0 이 없으면 flex 자식이 안 줄어 바닥 고정이 깨진다 */
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 12px;
}
.sheet.keypad .kscroll:empty { display: none; }   /* 이력도 짝 체크도 없으면 빈 칸을 남기지 않는다 */
.sheet.keypad .kfix {
  flex: 0 0 auto;
  display: flex; flex-direction: column; gap: 12px;
}
/* 입력 시트는 화면을 다 쓴다 — 뒤 목록을 살짝 보여 주는 92dvh 여백이 조작부를 밀어낸다.
   이 시트는 '한 품목을 세는' 전용 화면이라 전체 높이를 쓰는 편이 맞다. */
.sheet.keypad { max-height: 100vh; max-height: 100dvh; }   /* dvh 미지원이면 앞줄 vh — 원본 규약과 동일 */

/* ★작은 화면 대응 (2026-08-30) — 실측: 360×640 에서 머리말 102 + 조작부 568 = 670 이 시트 589 를 넘었다.
   버튼 크기를 줄이는 건 현장 조작성을 깎는 일이라 <b>짧은 화면에서만</b> 조인다.
   큰 폰(높이 720 초과)은 원래 크기를 그대로 쓴다 — 손가락 목표는 최대한 크게 둔다. */
@media (max-height: 720px) {
  .sheet.keypad { gap: 8px; padding-top: 12px; }
  .sheet.keypad .kfix { gap: 8px; }
  /* 두 줄(식+합계)이라 min-height 는 내용이 정하게 둔다 — 억지로 키우면 키패드가 밀린다 */
  .sheet.keypad .kdisp { min-height: 0; font-size: 1.88rem; padding: 6px 14px; }
  .sheet.keypad .kpad button { padding: 9px 0; font-size: 1.41rem; }
  .sheet.keypad .kdisp .expr { font-size: 0.94rem; }
  .sheet.keypad .kpad .kpack { font-size: 0.88rem; }
  .sheet.keypad .kpad .ksave { font-size: 1.29rem; }
  .sheet.keypad .head .pn { font-size: 1.0rem; }
  .sheet.keypad .head .alc { font-size: 1.12rem; padding: 2px 9px 3px; }
}
.sheet .head { display: flex; align-items: center; gap: 12px; }
.sheet .head .mid { flex: 1; min-width: 0; }
.sheet .prev { color: var(--dim); font-size: 0.82rem; }
/* ─── 표시줄 = 식 + 합계 + 없음(0) (2026-09-03 정호님 "입력할 때 위쪽에 20+15+20 그대로") ───
   ★식과 합계를 **두 줄로** 나눈다 (같은 날 2차: "＋ 로 더해질 때 그게 가려지지 않도록 2줄로").
     한 줄에 나란히 뒀더니 합계가 오른쪽을 고정폭으로 차지해 식이 먼저 접혔다 — 방금 누른 항이
     보이는 게 세는 사람에게는 합계만큼 중요하다.
   ★식 줄은 항상 자리를 차지한다(min-height) — 안 그러면 '＋' 를 누르는 순간 표시줄이 커지면서
     키패드가 아래로 밀린다. 빈 상태에서도 같은 높이여야 손가락 위치가 안 흔들린다.
   '없음(0)' 은 키패드 조작 열에서 여기로 옮겼다(그 칸을 ＋적입수량에 내줬다). */
.kdisprow { display: flex; align-items: stretch; gap: 8px; }
.kdisprow .kdisp {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
}
/* 식 줄 — 두 줄까지 접어 쓰고, 그보다 길면 JS(fitExpr)가 **앞쪽** 항부터 '…' 로 접는다.
   ⚠ CSS 로는 뒤만 자를 수 있어 방금 누른 항이 사라진다. direction:rtl 은 '+' 같은 중립문자를
     재배치해 식을 뒤집는다. 그래서 접기는 JS 가 한다. */
.kdisp .expr {
  text-align: left; line-height: 1.25; min-height: 1.25em; max-height: 2.5em; overflow: hidden;
  font-family: Consolas, "D2Coding", monospace; font-size: 1.06rem; font-weight: 700; color: var(--dim);
  overflow-wrap: anywhere;
}
.kdisp .sum { text-align: right; line-height: 1.1; }
.kdisp {
  background: var(--bg); border: 1px solid var(--line); border-radius: 14px;
  font-size: 2.35rem; font-weight: 800; text-align: right; padding: 12px 18px; min-height: 68px;
}
/* ★키패드 = 숫자 3열 + 조작 1열 (2026-08-30 정호님, 조작 열 내용은 2026-09-03 개편).
     1 2 3 │ ＋팔레트 40  종전의 [없음] 줄과 [닫기][지움][저장] 줄을 흡수해 약 148px 확보
     4 5 6 │ ＋
     7 8 9 │ 저장        저장은 색으로 구분(.primary) — 숫자와 섞여 보이지 않게
    00 0 ← │             백스페이스는 0 오른쪽(전화 키패드 관례)
   ★'없음(0)' 은 표시줄로 옮겼다 — 그 칸이 ＋적입수량에 필요했고, 행을 늘리면 360×640 에서
     저장 버튼이 화면 밖으로 나간다(시트가 overflow:hidden).
   ★열 너비: 조작 열을 1.25fr 로 조금 넓힌다 — 글자가 길고 오조작이 비싸다. */
.kpad {
  display: grid;
  grid-template-columns: repeat(3, 1fr) 1.25fr;
  grid-auto-rows: 1fr;
  gap: 8px;
}
.kpad button { font-size: 1.53rem; padding: 16px 0; font-weight: 700; }
/* ─── 조작 열 재배치 (2026-09-03 정호님) ───────────────────────────────────────
     1 2 3 │ ＋팔레트 40   1행 — 다 찬 무더기 하나. 무더기마다 누르므로 목표가 커야 한다
     4 5 6 │ ＋            1행 — 낱개 무더기 끊기
     7 8 9 │ 저장          2행 — 크기 유지(2026-08-30 결정)
    00 0 ← │
   좌표를 명시하므로 자동배치되는 숫자들이 1~3열만 채운다 — append 순서에 안 흔들린다.
   ★적입수량이 없는 품목은 .kpack 이 아예 안 만들어지고 .kplus.tall 이 두 행을 쓴다. */
.kpad .kpack { grid-column: 4; grid-row: 1; font-size: 1.0rem; line-height: 1.2; }
.kpad .kplus { grid-column: 4; grid-row: 2; font-size: 1.53rem; }
.kpad .kplus.tall { grid-row: 1 / span 2; }
.kpad .wide2 { grid-column: 1 / span 2; }   /* '00' 을 뺀 정수 키패드의 '0' (2026-09-03) */
.kpad .ksave { grid-column: 4; grid-row: 3 / span 2; font-size: 1.41rem; }
.kpad .ksave:disabled { opacity: .45; }
/* 표시값이 흐리면 "아직 입력 전" — 0 을 입력한 것과 구분된다 */
.kdisp.empty { color: var(--dim); }
/* [닫기][지움][저장] — 저장만 크게. 나가는 길이 액션 줄에 있어야 오입력이 안 생긴다 */
.kact { display: grid; grid-template-columns: 1fr 1fr 2fr; gap: 8px; }
.kact button { font-size: 1.18rem; padding: 16px 0; }
/* 머리말 ✕ 도 테두리를 줘서 '누를 수 있는 것' 으로 보이게 (기존엔 배경·테두리 없는 흐린 글자) */
.sheet .head button.ghost { border: 1px solid var(--line); border-radius: 10px; padding: 10px 14px; }
/* ★입력 시트에서는 ✕ 가 유일한 '나가는 길' 이다 (2026-08-30 에 [닫기] 를 없앴다).
   2026-08-13 사고 = "✕ 만으로는 나가는 길이 안 보여 빈 상태로 저장을 눌러 0 이 저장됐다".
   그래서 배경까지 줘서 버튼임을 분명히 하고 터치 목표를 키운다. 저장은 입력 전 비활성이라
   빈 상태 오저장은 그쪽에서도 막힌다. */
.sheet.keypad .head button.ghost {
  background: var(--panel2); border-color: var(--line);
  font-size: 1.29rem; padding: 12px 18px; border-radius: 12px;
}

/* 수동 등록 (S5b) */
.group-h { color: var(--dim); font-size: 0.82rem; font-weight: 700; margin-top: 8px; }
.badge { font-size: 0.71rem; border-radius: 8px; padding: 2px 8px; background: var(--panel2); color: var(--dim); flex-shrink: 0; }
.badge.hit { background: rgba(52,199,123,.15); color: var(--ok); }

/* 최근 위치 빠른 전환 칩 */
.chips { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
.chip { flex-shrink: 0; padding: 10px 16px; border-radius: 22px; font-size: 0.94rem; }
.chip.active { background: var(--accent); border-color: var(--accent); font-weight: 700; }

/* 필터 바 (차종/품목/색상) + 편집 모드 */
.filters { display: flex; gap: 6px; align-items: center; }
.filters select {
  flex: 1; min-width: 0; font: inherit; font-size: 0.82rem; color: var(--text);
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 10px 6px;
}
.filters .ghost { flex-shrink: 0; border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; }
.filters .ghost.danger { color: var(--err); border-color: var(--err); }
/* 스와이프 삭제 — pan-y: 가로 스와이프를 브라우저 스크롤에 뺏기지 않게 */
.card { position: relative; touch-action: pan-y; }
.swipe-del {
  position: absolute; top: 0; right: 0; bottom: 0; width: 84px; z-index: 0;
  background: var(--err); color: #fff; font-weight: 700; font-size: 0.94rem;
  border: none; border-radius: 0 13px 13px 0;
}

/* LH/RH 짝 카드 */
.card.pair { cursor: default; }
.pairbtns { display: flex; gap: 8px; margin-top: 8px; }
/* LH/RH 버튼은 측 이름 + 수량 한 줄. ALC 는 L/R 을 떼면 두 측이 같은 값이라 버튼에 적지 않는다
   (v31 에서 측별 배지를 달았다가 정호님 지시로 제거, 2026-08-13) */
.pairbtn {
  flex: 1; min-width: 0; display: flex; align-items: baseline; justify-content: center; gap: 8px;
  padding: 12px 8px; font-size: 0.88rem; font-weight: 700; border-radius: 10px;
}
/* ⚠ 측 이름이 글자 중간에서 쪼개지지 않게 — body 의 overflow-wrap:anywhere 를 여기서만 되돌린다.
   수량이 네 자리(1288)면 폭이 모자라 'L / H' 로 갈라져 보였다. 항목 단위로만 줄을 넘긴다. */
.pairbtn { flex-wrap: wrap; overflow-wrap: normal; }
.pairbtn .n { font-size: 1.29rem; font-weight: 800; flex-shrink: 0; }
.pairbtn .t { color: var(--dim); font-size: 0.65rem; font-weight: 400; }
.pairbtn .none { color: var(--dim); font-size: 0.82rem; font-weight: 400; }

.card .del {
  flex-shrink: 0; color: var(--err); border-color: var(--err);
  font-size: 1.18rem; padding: 10px 16px; background: none;
}

/* 박스 단위 계수 (2026-08-14) — 합계 밑에 산식을 그대로 보여 눈으로 검산하게 한다 */
.boxbreak {
  color: var(--dim); font-size: 0.82rem; text-align: center;
  padding: 2px 4px; min-height: 1.4em; word-break: keep-all;
}
.boxpad { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.boxpad button { font-size: 1.06rem; padding: 16px 0; font-weight: 700; }
.boxpad #b-plus { background: var(--accent); border-color: var(--accent); color: #fff; }

/* 오프라인 안내 — 이 상태에서는 저장이 막힌다 (대기열 폐지 2026-08-14) */
.offline {
  background: rgba(239, 83, 80, .12); border: 1px solid var(--err); color: var(--err);
  border-radius: 12px; padding: 10px 14px; font-size: 0.88rem; text-align: center; font-weight: 700;
}

/* 레거시 미전송 배지 (신규 적재 없음) */
.pending {
  background: rgba(240,168,50,.12); border: 1px solid var(--warn); color: var(--warn);
  border-radius: 12px; padding: 10px 14px; font-size: 0.88rem; text-align: center;
}

/* 플로팅 추가 버튼 (FAB) + 액션 시트 */
.fab {
  position: fixed; right: 18px; bottom: calc(env(safe-area-inset-bottom) + 22px);
  width: 62px; height: 62px; border-radius: 50%; z-index: 20;
  background: var(--accent); border: none; color: #fff;
  font-size: 34px; font-weight: 700; line-height: 1;
  box-shadow: 0 5px 16px var(--shadow);
}
.fab:active { filter: brightness(1.2); }
.sheet .big + .big, .sheet .big + .ghost { margin-top: 2px; }

/* 헤더 아이콘 버튼 (⚙ 설정) */
button.icon { padding: 10px 12px; font-size: 1.18rem; line-height: 1; }

/* 설정 시트 — 세그먼트 선택 (테마/글자 크기) */
.seg { display: flex; gap: 8px; }
.seg button { flex: 1; padding: 14px 8px; font-size: 1rem; }
.seg button.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }

/* 고정폭 — 버전·빌드처럼 그대로 읽어 전달해야 하는 값 */
.mono { font-family: Consolas, "D2Coding", monospace; }

/* 토스트 */
.toast {
  position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom) + 24px);
  transform: translateX(-50%); z-index: 99;
  background: var(--ok); color: #06281a; font-weight: 700;
  padding: 12px 22px; border-radius: 30px; font-size: 0.94rem;
  transition: opacity .3s; white-space: nowrap;
}
.toast.err { background: var(--err); color: #fff; }

/* ─── 하단 탭 바 (2026-08-15) ────────────────────────────────────────────────
   구성 = 서열장 실사 · 사내 실사 · 설정. 누구에게나 항상 셋.
   스크롤을 내리면 아래로 사라진다(B안) — .down 은 app.js 의 스크롤 감시가 붙인다.
   ★사라져도 '없어진' 게 아니라 '내려간' 것이라 위로 조금만 올리면 바로 돌아온다. */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  background: var(--bg); border-top: 1px solid var(--line);
  padding: 6px 4px calc(env(safe-area-inset-bottom) + 6px);
  transition: transform .22s ease;
}
.tabbar.down { transform: translateY(115%); }
.tabbar .tab {
  background: none; border: none; padding: 6px 2px; border-radius: 10px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: var(--dim); font-size: 0.62rem; font-weight: 600; line-height: 1.2;
}
.tabbar .tab .ic { font-size: 1.15rem; line-height: 1.1; }
.tabbar .tab.on { color: var(--accent); }
.tabbar .tab:active { background: var(--panel2); }
/* 탭바·＋버튼이 화면 끝을 가리지 않게 — 마지막 카드와 로그아웃 버튼이 바 뒤로 들어가면 누를 수 없다.
   탭바가 있는 화면에만 준다(로그인·품목등록 화면에 빈 공간이 생기지 않게). */
.screen.tabbed { padding-bottom: calc(env(safe-area-inset-bottom) + 150px); }
/* ＋ 버튼은 탭바 위에 뜨고, 바가 내려가면 같이 내려온다(겹침 방지) */
.fab { bottom: calc(env(safe-area-inset-bottom) + 84px); transition: bottom .22s ease; }
.fab.low { bottom: calc(env(safe-area-inset-bottom) + 22px); }

/* ─── 종류 칩 — 필터가 아니라 형제 간 '선택' 이라 칩 줄로 둔다 ─── */
.chips.kinds .chip { font-weight: 700; }

/* ─── 완료 구분선 — 눌러서 접고 편다 ─── */
.divi {
  width: 100%; background: none; border: none; padding: 10px 4px;
  display: flex; align-items: center; gap: 10px;
  color: var(--dim); font-size: 0.8rem; font-weight: 700; letter-spacing: .02em;
}
.divi::before, .divi::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.divi .cv { font-weight: 600; opacity: .85; }
.divi:active { color: var(--text); }

/* ─── 완료 표시 (2026-08-15) ───
   테두리 두께만으로는 햇빛 아래 장갑 낀 손에게 잘 안 보인다 — 신호를 셋으로 겹친다:
   ① 왼쪽 굵은 세로 바 ② 가라앉은 배경 ③ 수량이 굵게 찍혀 있음(미입력은 '미입력' 글자). */
.card.done, .lrow.done {
  background: var(--panel2);
  border-left: 4px solid var(--accent);
}
.card.done .cbody { opacity: .82; }
.lrow.done { opacity: .82; }

/* 방금 저장한 카드 — 잠깐 제자리에서 강조했다가 완료 묶음으로 내려간다 */
.card.flash, .lrow.flash {
  border-color: var(--ok); box-shadow: 0 0 0 2px var(--ok) inset;
  animation: flashpop .5s ease;
}
@keyframes flashpop { from { transform: scale(1.015); } to { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .tabbar, .fab, .jump { transition: none; }
  .card.flash, .lrow.flash { animation: none; }
}

/* ─── '없음(0)' 과 실사 마감 (2026-08-16) ───────────────────────────────────
   '없음' 은 자주 쓰는 정상 입력이지 위험한 동작이 아니다 — 눈에 띄되 저장(강조색)보다는 약하게.
   ⚠ 카드·행 전체가 눌리는 영역 안에 있으므로 터치 목표(≈40px)를 따로 확보해야 오탭이 안 난다. */
.zero {
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel2);
  color: var(--dim); font-size: 0.8rem; font-weight: 700;
  padding: 9px 11px; min-width: 48px; flex-shrink: 0;
}
.zero:active { background: var(--accent); border-color: var(--accent); color: #fff; }
.card .qty .zero { display: block; margin-top: 6px; margin-left: auto; }
/* 키패드의 '없음' — 저장과 같은 폭이되 색은 중립. 0 을 눌러 저장한 것과 결과가 같다. */
.zerobtn {
  width: 100%; padding: 14px 0; font-size: 1.06rem; font-weight: 700;
  border: 1px solid var(--line); border-radius: 12px; background: var(--panel2); color: var(--text);
}
.zerobtn:active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* 마감 버튼 — 목록 맨 아래. 파괴적이지 않지만 되돌아오기 번거로운 동작이라 경고색 테두리로 세운다. */
.sweepgo {
  width: 100%; margin-top: 14px; padding: 15px 12px;
  border: 2px solid var(--warn); border-radius: 14px; background: var(--panel);
  color: var(--text); font-size: 1.06rem; font-weight: 800;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.sweepgo .sub { color: var(--dim); font-size: 0.8rem; font-weight: 600; }
.sweepgo:active { background: var(--panel2); }

/* 마감 확인 시트 — 대상을 실제로 다 보여 준다. 길면 시트 안에서만 스크롤(페이지는 안 밀림). */
.sweepwarn {
  border: 1px solid var(--warn); border-radius: 12px; padding: 12px 14px;
  font-size: 0.88rem; line-height: 1.5;
}
.sweeplist {
  max-height: 42vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column; gap: 4px;
  border: 1px solid var(--line); border-radius: 12px; padding: 8px;
}
.srow { display: flex; align-items: center; gap: 9px; padding: 6px 4px; font-size: 0.82rem; }
.srow + .srow { border-top: 1px solid var(--line); }
.srow .la {
  flex-shrink: 0; min-width: 4.2rem;
  font-family: Consolas, "D2Coding", monospace; font-weight: 800; color: var(--alc-bg);
}
.srow .ln { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srow .lk { flex-shrink: 0; color: var(--dim); font-size: 0.71rem; font-family: Consolas, monospace; }
.kact2 { display: grid; grid-template-columns: 1fr 2fr; gap: 8px; }
.kact2 button { font-size: 1.12rem; padding: 16px 0; }

/* ─── 맨 위 / 맨 아래 점프 (2026-08-16, 정호님 "실사 마감 찾으러 내려가는 데 오래 걸린다") ───
   194품목이면 마감 버튼이 사실상 194행 아래다. 목록이 한 화면을 크게 넘을 때만 나온다 —
   짧은 목록에 떠 있으면 그냥 방해다.
   ★FAB(＋) 반대쪽인 **왼쪽**에 둔다. 같은 쪽에 두면 장갑 낀 손으로 오탭한다.
   ★스크롤 방향에 따라 숨기지 않는다 — 찾으러 가는 도중에 사라지는 게 제일 나쁘다.
     탭바·FAB 와 같은 규칙으로 위아래로만 움직인다(.low). */
/* ★＋ 버튼 **바로 위**에 쌓는다 (정호님 2026-09-03). 종전엔 왼쪽 아래라 목록 행 위에 겹쳐
   앉아 ALC·품번을 가렸다. 오른쪽에 세로로 세우면 가리는 건 자기 폭(52px)뿐이고
   엄지 동선도 ＋ 와 한 자리로 모인다.
   ⚠ ＋(.fab, 62px)와 같은 .low 규칙으로 움직여야 겹치지 않는다 — 아래 bottom 값이 짝이다. */
.jump {
  position: fixed; right: 23px; bottom: calc(env(safe-area-inset-bottom) + 156px);
  z-index: 20; display: flex; flex-direction: column;
  border-radius: 14px; overflow: hidden;
  border: 1px solid var(--card-line); background: var(--panel);
  box-shadow: 0 5px 16px var(--shadow);
  transition: bottom .22s ease;
}
.jump.low { bottom: calc(env(safe-area-inset-bottom) + 94px); }   /* ＋가 22px 로 내려가면 그 위 = 22+62+10 */
.jump button {
  width: 52px; height: 46px; border: none; background: none;
  color: var(--text); font-size: 1.12rem; line-height: 1;
}
.jump button + button { border-top: 1px solid var(--card-line); }
.jump button:active { background: var(--accent); color: #fff; }

/* ─── 서열장 이동 선택 (2026-08-16) — 한 줄에 [야드명][근거 배지] / [최근 실사] 두 줄짜리 버튼 ─── */
.locpick {
  text-align: left; line-height: 1.5;
  display: block; width: 100%;
  border: 1px solid var(--card-line); border-radius: 12px; padding: 13px 14px;
}
.locpick b { font-size: 1.06rem; }
.locpick .badge { margin-left: 6px; vertical-align: middle; }
.locpick .note { font-size: 0.76rem; }
.locpick:active { background: var(--panel2); }

/* 마감 되돌리기 배너 — 목록 맨 위. 되돌리기가 어느 품목의 이력 속에 숨으면 못 찾는다. */
.sweepbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  border: 1px solid var(--warn); border-radius: 12px; padding: 10px 12px; margin-bottom: 10px;
  font-size: 0.82rem;
}
.sweepbar .t { flex: 1; min-width: 10rem; }
.sweepbar button { padding: 9px 12px; font-size: 0.82rem; font-weight: 700; }
