/* ============================================================================
   윈윈프로 웹 — 공통 컴포넌트
   ----------------------------------------------------------------------------
   모든 클래스에 ws- 접두어를 붙였습니다. 기존 CSS와 절대 충돌하지 않습니다.
   기존 화면을 한 번에 다 바꿀 필요 없습니다. 손대는 화면부터 하나씩 바꾸세요.

   설치:  <link rel="stylesheet" href="/home/css/tokens.css">
         <link rel="stylesheet" href="/home/css/ui.css">
   ============================================================================ */

/* ---------------------------------------------------------------- 기본 ---- */
/* 2026-10-01 수정 — 전에는 `.ws-app *{box-sizing:border-box}` 였습니다.
   .ws-app 을 <body> 에 붙이면 페이지의 모든 요소가 border-box 로 바뀌어
   기존 화면(width + padding 을 같이 쓴 규칙 365곳)이 전부 틀어집니다.
   이제 ws- 클래스를 직접 가진 요소에만 적용합니다. */
/* 2026-10-02 우리 수정 — 2판은 --fs-* 에 배율을 곱했는데 아래에서 또 곱해 이중 배율이었음(21곳). 토큰만 쓰도록 정리 */
[class^="ws-"],[class*=" ws-"],.ws-search input,.ws-zoom button{box-sizing:border-box}
.ws-num{font-family:var(--f-num);font-variant-numeric:tabular-nums}
.ws-muted{color:var(--ink-3)}
.ws-strong{color:var(--ink);font-weight:700}
.ws-right{text-align:right}
[class^="ws-"]:focus-visible,[class*=" ws-"]:focus-visible{outline:none;box-shadow:var(--focus)}

/* ==========================================================================
   버튼 — 네 가지만 씁니다. 한 화면에 ws-btn--primary 는 하나만.
   <button type="button" class="ws-btn ws-btn--primary">저장</button>
   ========================================================================== */
.ws-btn{
  display:inline-flex;align-items:center;gap:6px;
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line);border-radius:var(--r-1);
  padding:5px 11px;cursor:pointer;white-space:nowrap;
  font-family:var(--f-ui);font-size:var(--fs-2);
  box-shadow:var(--shadow-1);line-height:1.6;
}
.ws-btn:hover:not(:disabled){border-color:var(--brand);color:var(--brand-deep)}
.ws-btn:disabled,.ws-btn[aria-disabled="true"]{opacity:.45;cursor:not-allowed;box-shadow:none}
.ws-btn--primary{background:var(--brand-deep);border-color:var(--brand-deep);color:#fff;font-weight:700}
.ws-btn--primary:hover:not(:disabled){filter:brightness(1.1);color:#fff}
.ws-btn--danger{color:var(--danger);border-color:var(--danger-line)}
.ws-btn--danger:hover:not(:disabled){background:var(--danger-wash);border-color:var(--danger)}
.ws-btn--quiet{border-color:transparent;box-shadow:none;color:var(--ink-2)}
.ws-btn--quiet:hover:not(:disabled){background:var(--surface-3)}
.ws-btn--lg{padding:8px 16px;font-size:var(--fs-3)}
.ws-btn--block{width:100%;justify-content:center}
/* 처리 중 — WS.lock(btn) 이 자동으로 붙입니다 */
.ws-btn.is-busy{position:relative;color:transparent !important;pointer-events:none}
.ws-btn.is-busy::after{
  content:"";position:absolute;inset:0;margin:auto;width:13px;height:13px;border-radius:50%;
  border:2px solid currentColor;border-top-color:transparent;color:var(--ink-2);
  animation:ws-spin .7s linear infinite;
}
.ws-btn--primary.is-busy::after{color:#fff}
@keyframes ws-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.ws-btn.is-busy::after{animation-duration:2s}}

/* 단축키 배지 — 버튼 이름 뒤에 붙입니다. <span class="ws-kbd">F9</span> */
.ws-kbd{
  font-family:var(--f-num);font-size:calc(var(--fs-1) * .92);
  border:1px solid var(--line-strong);border-bottom-width:2px;border-radius:3px;
  padding:0 3px;color:var(--ink-3);background:var(--surface-2);line-height:1.5;
}
.ws-btn--primary .ws-kbd{border-color:rgba(255,255,255,.45);color:rgba(255,255,255,.9);background:transparent}

/* ==========================================================================
   아이콘 — 인라인 SVG 한 벌만. 이모지·Material Icons 를 쓰지 않습니다.
   <svg class="ws-i" viewBox="0 0 20 20"><path d="..."/></svg>
   ========================================================================== */
.ws-i{width:1.15em;height:1.15em;flex:0 0 auto;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}

/* ==========================================================================
   칩 / 상태 배지 — 색만으로 뜻을 전하지 않습니다. 항상 글자를 같이 씁니다.
   <span class="ws-chip ws-chip--bad"><span class="ws-dot"></span>미수</span>
   ========================================================================== */
.ws-chip{
  display:inline-flex;align-items:center;gap:4px;white-space:nowrap;
  font-size:var(--fs-1);padding:1px 7px;border-radius:100px;
  border:1px solid var(--line);color:var(--ink-2);background:var(--surface);
}
.ws-chip--ok{background:var(--ok-wash);border-color:var(--ok-line);color:var(--ok)}
.ws-chip--warn{background:var(--warn-wash);border-color:var(--warn-line);color:var(--warn)}
.ws-chip--bad{background:var(--danger-wash);border-color:var(--danger-line);color:var(--danger)}
.ws-chip--info{background:var(--info-wash);border-color:var(--info-line);color:var(--info)}
.ws-chip--brand{background:var(--brand-wash);border-color:var(--brand-line);color:var(--brand-ink)}
.ws-dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:0 0 auto}

/* ==========================================================================
   패널 / 카드
   ========================================================================== */
.ws-panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);overflow:hidden}
.ws-panel__head{
  display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;
  padding:8px 11px;background:var(--surface-3);border-bottom:1px solid var(--line);
}
.ws-panel__head h2{margin:0;font-size:var(--fs-2);font-weight:700;color:var(--ink-2)}
.ws-panel__spacer{flex:1}
.ws-panel__body{padding:var(--sp-3)}

/* 화면 제목 줄 */
.ws-pagehead{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;margin-bottom:10px}
.ws-pagehead h1{margin:0;font-size:var(--fs-5);font-weight:700;letter-spacing:-.015em;color:var(--ink)}
.ws-pagehead .ws-sub{font-size:var(--fs-2);color:var(--ink-3)}

/* ==========================================================================
   표 — 조회용
   ========================================================================== */
.ws-tablewrap{overflow:auto}
table.ws-table{width:100%;border-collapse:separate;border-spacing:0;
  font-size:var(--fs-3)}
table.ws-table th{
  position:sticky;top:0;z-index:2;background:var(--surface-3);color:var(--ink-2);
  font-weight:500;text-align:left;padding:7px 9px;border-bottom:1px solid var(--line);
  font-size:var(--fs-2);white-space:nowrap;
}
table.ws-table td{padding:6px 9px;border-bottom:1px solid var(--line-soft);white-space:nowrap}
table.ws-table td.ws-r,table.ws-table th.ws-r{text-align:right;font-family:var(--f-num);font-variant-numeric:tabular-nums}
table.ws-table tbody tr{cursor:pointer}
table.ws-table tbody tr:hover{background:var(--surface-2)}
table.ws-table tbody tr[aria-selected="true"]{background:var(--brand-wash)}
table.ws-table tbody tr[aria-selected="true"] td{border-bottom-color:var(--brand-line)}
/* 왼쪽 위험도 띠 */
table.ws-table tbody tr.ws-sev-bad  td:first-child{box-shadow:inset 3px 0 0 var(--danger)}
table.ws-table tbody tr.ws-sev-warn td:first-child{box-shadow:inset 3px 0 0 var(--warn)}
table.ws-table tbody tr.ws-sev-info td:first-child{box-shadow:inset 3px 0 0 var(--info)}

/* 입력용 표 (전표·견적서) */
table.ws-entry{width:100%;border-collapse:separate;border-spacing:0;
  font-size:var(--fs-3)}
table.ws-entry th{background:var(--surface-3);color:var(--ink-2);font-weight:500;text-align:left;
  padding:6px 8px;border-bottom:1px solid var(--line);
  font-size:var(--fs-2);white-space:nowrap}
table.ws-entry th.ws-r{text-align:right}
table.ws-entry td{border-bottom:1px solid var(--line-soft);padding:0;height:31px}
table.ws-entry td.ws-rownum{width:28px;text-align:center;color:var(--ink-3);
  font-family:var(--f-num);font-size:var(--fs-1);
  background:var(--surface-2);border-right:1px solid var(--line-soft)}
table.ws-entry input{width:100%;height:100%;border:0;background:transparent;padding:5px 8px;font:inherit;color:inherit}
table.ws-entry input.ws-r{text-align:right;font-family:var(--f-num)}
table.ws-entry input:focus{box-shadow:inset 0 0 0 2px var(--brand);border-radius:2px;background:var(--surface);outline:none}
table.ws-entry td.ws-calc{text-align:right;font-family:var(--f-num);padding:5px 8px;background:var(--surface-2)}
table.ws-entry tr.is-active td{background:var(--brand-wash)}
table.ws-entry tr.is-active td.ws-calc{background:var(--brand-wash)}

/* 비어 있을 때 — 빈 표를 그냥 두지 않습니다 */
.ws-empty{color:var(--ink-3);font-size:var(--fs-2);text-align:center;padding:40px 14px;line-height:1.8}

/* ==========================================================================
   폼 — 라벨과 입력칸을 <label for> 로 묶습니다. th/td 표 폼을 쓰지 않습니다.
   ========================================================================== */
.ws-field{margin-bottom:11px}
.ws-field > label{display:block;font-size:var(--fs-2);color:var(--ink-2);
  margin-bottom:4px;font-weight:500}
.ws-field .ws-req{color:var(--danger)}
.ws-field .ws-opt{color:var(--ink-3);font-weight:400;font-size:var(--fs-1)}
.ws-input,.ws-select,.ws-textarea{
  width:100%;border:1px solid var(--line);border-radius:var(--r-1);
  padding:6px 9px;background:var(--surface);color:var(--ink);
  font-family:var(--f-ui);font-size:var(--fs-3);
}
.ws-input.ws-num,.ws-input[inputmode="numeric"]{font-family:var(--f-num);text-align:right}
.ws-textarea{min-height:96px;resize:vertical;line-height:1.7}
.ws-input:focus,.ws-select:focus,.ws-textarea:focus{outline:none;border-color:var(--brand);box-shadow:var(--focus)}
.ws-field .ws-help{font-size:var(--fs-1);color:var(--ink-3);margin-top:4px;line-height:1.55}
/* 입력 오류 — 팝업 대신 칸 아래 한 줄 */
.ws-input.is-error,.ws-select.is-error,.ws-textarea.is-error{border-color:var(--danger);background:var(--danger-wash)}
.ws-field .ws-error{display:none;font-size:var(--fs-1);color:var(--danger);margin-top:4px}
.ws-field.is-error .ws-error{display:block}

/* 검색 상자 */
.ws-search{position:relative;display:inline-flex;align-items:center}
.ws-search .ws-i{position:absolute;left:8px;color:var(--ink-3);pointer-events:none}
/* 2026-10-02 우리 보정 — 기존 .st-toolbar input[type="text"]{padding:0 8px} (0,2,1) 이
   키트 왼쪽 여백(29px)을 이겨 돋보기가 글자를 덮었음. 같은 특이도로, 뒤에 로드되므로 이긴다 */
.st-toolbar .ws-search input[type="text"],.filter-bar .ws-search input[type="text"]{padding-left:29px}
.ws-search input{border:1px solid var(--line);border-radius:var(--r-1);padding:6px 10px 6px 29px;
  background:var(--surface);color:var(--ink);font-size:var(--fs-3);font-family:var(--f-ui)}

/* ==========================================================================
   토스트 — alert() 대신. WS.toast() 가 자동으로 만듭니다.
   ========================================================================== */
.ws-toasts{position:fixed;top:calc(env(safe-area-inset-top,0px) + 56px);right:18px;z-index:2000;
  display:flex;flex-direction:column;gap:8px;pointer-events:none}
.ws-toast{
  pointer-events:auto;background:var(--surface);border:1px solid var(--line);
  border-left:3px solid var(--ok);border-radius:var(--r-2);box-shadow:var(--shadow-2);
  padding:9px 13px;min-width:248px;max-width:340px;font-family:var(--f-ui);
  animation:ws-pop .18s ease-out;
}
.ws-toast--warn{border-left-color:var(--warn)}
.ws-toast--bad{border-left-color:var(--danger)}
.ws-toast__t{font-weight:700;font-size:var(--fs-3);color:var(--ink)}
.ws-toast__s{color:var(--ink-3);font-size:var(--fs-2)}
@keyframes ws-pop{from{opacity:0;transform:translateY(-6px)}}
@media (prefers-reduced-motion:reduce){.ws-toast{animation:none}}

/* ==========================================================================
   모달 — 모든 모달이 같은 모양입니다. 위험한 작업만 위쪽에 빨간 띠.
   ========================================================================== */
.ws-scrim{position:fixed;inset:0;background:rgba(10,16,15,.44);z-index:1900;
  display:grid;place-items:center;padding:20px}
.ws-modal{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-3);
  box-shadow:var(--shadow-2);width:min(460px,100%);max-height:86vh;
  display:flex;flex-direction:column;overflow:hidden;font-family:var(--f-ui)}
.ws-modal--wide{width:min(720px,100%)}
.ws-modal--danger{border-top:3px solid var(--danger)}
.ws-modal__head{padding:13px 16px 0}
.ws-modal__head h3{margin:0;font-size:var(--fs-4);font-weight:700;color:var(--ink)}
.ws-modal--danger .ws-modal__head h3{color:var(--danger)}
.ws-modal__body{padding:8px 16px 14px;color:var(--ink-2);font-size:var(--fs-3);overflow:auto}
.ws-modal__hl{background:var(--danger-wash);border:1px solid var(--danger-line);border-radius:var(--r-1);
  padding:8px 11px;color:var(--ink);margin-top:9px;font-size:var(--fs-2);line-height:1.65}
.ws-modal__foot{display:flex;justify-content:flex-end;gap:var(--sp-2);padding:11px 16px;
  background:var(--surface-2);border-top:1px solid var(--line-soft)}

/* ==========================================================================
   기다림 — 조회 중에 빈 화면을 두지 않습니다.
   ========================================================================== */
/* 상단 진행 막대. 기존 header.html 의 <div id="pageProgress"> 에 붙여 쓰세요 */
.ws-progress{position:fixed;top:0;left:0;width:100%;z-index:9999;pointer-events:none;
  height:2px;background:transparent;overflow:hidden}
.ws-progress.is-on::after{content:"";display:block;height:2px;width:40%;background:var(--brand);
  animation:ws-slide 1s ease-in-out infinite}
@keyframes ws-slide{0%{transform:translateX(-100%)}100%{transform:translateX(320%)}}
@media (prefers-reduced-motion:reduce){.ws-progress.is-on::after{animation:none;width:100%;opacity:.5}}

/* 표 뼈대 */
.ws-sk{height:10px;border-radius:3px;
  background:linear-gradient(90deg,var(--line-soft) 25%,var(--surface-3) 50%,var(--line-soft) 75%);
  background-size:200% 100%;animation:ws-shine 1.2s linear infinite}
@keyframes ws-shine{to{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.ws-sk{animation:none}}
.ws-sk-row{display:grid;grid-template-columns:1.6fr 1fr .8fr .8fr;gap:7px;margin-bottom:8px}

/* ==========================================================================
   버튼에 붙는 메뉴 — "영수증 ▾", "더보기 ▾" 처럼 버튼 하나에 선택지를 겁니다.
   WS.menu(버튼, [...]) 가 이 마크업을 만듭니다. 직접 쓸 일은 거의 없습니다.
   ========================================================================== */
.ws-menu{
  position:absolute;z-index:1800;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-2);box-shadow:var(--shadow-2);padding:5px;min-width:196px;max-width:320px;
  font-family:var(--f-ui);
}
.ws-menu__item{
  display:flex;align-items:flex-start;gap:9px;width:100%;background:none;border:0;text-align:left;
  padding:7px 9px;border-radius:var(--r-1);cursor:pointer;color:var(--ink);
  font-family:var(--f-ui);font-size:var(--fs-3);line-height:1.5;
}
.ws-menu__item:hover,.ws-menu__item:focus-visible{background:var(--brand-wash);color:var(--brand-ink);outline:none}
.ws-menu__item .ws-i{margin-top:2px;color:var(--ink-3)}
.ws-menu__item:hover .ws-i{color:var(--brand)}
.ws-menu__item b{display:block;font-weight:500}
.ws-menu__item small{display:block;font-size:var(--fs-1);color:var(--ink-3);line-height:1.45;margin-top:1px}
.ws-menu__item:hover small{color:var(--brand-ink);opacity:.8}
.ws-menu__item .ws-kbd{margin-left:auto;align-self:center}
.ws-menu__item--danger{color:var(--danger)}
.ws-menu__item--danger:hover{background:var(--danger-wash);color:var(--danger)}
.ws-menu__item--danger .ws-i{color:var(--danger)}
.ws-menu hr{border:0;border-top:1px solid var(--line-soft);margin:4px 2px}

/* ==========================================================================
   상단 보기 배율 — 노안 대응. 세 단계만.
   ========================================================================== */
.ws-zoom{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-1);overflow:hidden}
.ws-zoom button{background:var(--surface);border:0;border-left:1px solid var(--line);
  padding:4px 9px;cursor:pointer;white-space:nowrap;font-family:var(--f-ui);
  font-size:var(--fs-1);color:var(--ink-2)}
.ws-zoom button:first-child{border-left:0}
.ws-zoom button[aria-pressed="true"]{background:var(--brand-deep);color:#fff;font-weight:700}

/* ==========================================================================
   인쇄 — 화면용 장식은 종이에서 뺍니다.
   ========================================================================== */
@media print{
  .ws-toasts,.ws-scrim,.ws-zoom,.ws-progress,.ws-btn{display:none !important}
  .ws-panel{border:0;box-shadow:none}
  .ws-panel__head{background:none;border-bottom:1px solid #000}
}
