/* ============================================================
   바른셈 — 근로자 모바일 셀프서비스 (별도 채널)
   §08-B: 명세서(조회 전용) + 증명서 발급 + 연말정산 자료 제출 + 연차·휴가 신청
   계산 커널 재사용만 — runFor·yearEndSettlement·annualLeaveDays·tenure (변형 없음)
   ============================================================ */
const WORKER_ASOF = "2026-06-09";

function Worker({ ctx }) {
  const { clients, go, toast, period, workerId, setWorkerId } = ctx;
  const findEmp = (id) => {
    for (const c of clients) { const e = c.employees.find((x) => x.id === id); if (e) return { c, e }; }
    return { c: clients[0], e: clients[0].employees[0] };
  };
  const { c: client, e } = findEmp(workerId);
  const run = window.BARUNSEM.runFor(e, client, period.ym);
  const ss = ctx.ssOf(e.id);

  const [wtab, setWtab] = useState("payslip");
  useEffect(() => { setWtab("payslip"); }, [workerId]);
  // 명세서는 조회 전용 — 이의제기는 고객사 인사팀이 접수하고(addDispute 파이프라인은 그 진입점용으로 유지),
  // 교부 입증은 발송 로그(이메일·SMS)로 충분하므로 수령 확인 버튼도 두지 않는다.
  // E12 — 확정만 되고 발송 전인 명세서는 근로자에게 보이지 않는다.
  const payslipReady = ctx.statusOf(client.id, period.ym) === "distributed";

  const WTABS = [
    { key: "payslip", label: "명세서", icon: "receipt_long" },
    { key: "cert", label: "증명서", icon: "verified_user", badge: ss.certs.length },
    { key: "yearend", label: "연말정산", icon: "account_balance_wallet", dot: ss.ye && ss.ye.submitted },
    { key: "leave", label: "휴가", icon: "beach_access", badge: ss.leaveReqs.length },
  ];

  return (
    <div style={{ minHeight: "100vh", background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      <div style={{ background: "var(--ink)", color: "#fff", padding: "8px 20px", display: "flex", alignItems: "center", gap: 10, fontSize: 12.5 }}>
        <Ms name="visibility" style={{ fontSize: 16 }} />
        <span style={{ opacity: .85 }}>근로자 셀프서비스 화면 미리보기 — 근로자가 모바일로 쓰는 별도 채널입니다</span>
        <div style={{ flex: 1 }} />
        <button className="btn-ghost btn btn-sm" style={{ color: "#fff" }} onClick={() => go("dashboard")}><Ms name="arrow_back" />노무사 워크스페이스로</button>
      </div>

      <div style={{ flex: 1, display: "flex", gap: 32, justifyContent: "center", alignItems: "flex-start", padding: "36px 24px", flexWrap: "wrap" }}>
        {/* 미리보기 대상 선택 */}
        <div className="card worker-pick" style={{ width: 280, flex: "none" }}>
          <CardHead icon="smartphone" title="미리보기 대상" sub="수신할 근로자" />
          <div className="card-pad" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <div className="field"><label>고객사</label>
              <select className="select" value={client.id} onChange={(ev) => { const nc = clients.find((x) => x.id === ev.target.value); setWorkerId(nc.employees[0].id); }}>
                {clients.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
              </select>
            </div>
          </div>
          <div className="pick-list" style={{ maxHeight: 420, overflowY: "auto", borderTop: "1px solid var(--line)" }}>
            {client.employees.map((emp) => (
              <button key={emp.id} onClick={() => setWorkerId(emp.id)} style={{
                display: "flex", alignItems: "center", gap: 10, padding: "10px 16px", width: "100%", textAlign: "left",
                border: "none", borderBottom: "1px solid var(--line)", cursor: "pointer",
                background: emp.id === workerId ? "var(--accent-soft)" : "transparent",
                borderLeft: emp.id === workerId ? "3px solid var(--accent)" : "3px solid transparent",
              }}>
                <div className="avatar" style={{ width: 30, height: 30, fontSize: 12, background: "var(--bg-sunken)", color: "var(--ink-2)" }}>{emp.name[0]}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 650, fontSize: 13 }}>{emp.name}</div>
                  <div className="muted" style={{ fontSize: 11 }}>{emp.dept} · {emp.scheme}</div>
                </div>
              </button>
            ))}
          </div>
        </div>

        {/* 폰 프레임 */}
        <div className="worker-phone" style={{ flex: "none", transform: "scale(.92)", transformOrigin: "top center" }}>
          <IOSDevice width={390} height={812}>
            <div style={{ height: "100%", background: "#F2F2F7", display: "flex", flexDirection: "column", paddingTop: 50 }}>
              {/* 상단 헤더 */}
              <div style={{ padding: "8px 18px 12px", display: "flex", alignItems: "center", gap: 10, background: "#F2F2F7" }}>
                <div style={{ width: 30, height: 30, borderRadius: 8, background: "var(--accent)", color: "#fff", display: "grid", placeItems: "center", fontWeight: 800, fontSize: 15 }}>해</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 700, fontSize: 14 }}>{e.name}님 · {client.short}</div>
                  <div style={{ fontSize: 11.5, color: "var(--ink-3)" }}>한결노동법률사무소 · 근로자 포털</div>
                </div>
              </div>

              {/* 탭 콘텐츠 (스크롤) */}
              <div style={{ flex: 1, overflowY: "auto", WebkitOverflowScrolling: "touch" }}>
                {wtab === "payslip" && (payslipReady
                  ? <PayslipPanel client={client} e={e} run={run} period={period} />
                  : <div style={{ padding: "40px 24px", textAlign: "center" }}>
                      <div style={{ width: 56, height: 56, borderRadius: "50%", background: "var(--bg-sunken)", display: "grid", placeItems: "center", margin: "0 auto 14px" }}><Ms name="schedule" style={{ fontSize: 30, color: "var(--ink-3)" }} /></div>
                      <div style={{ fontWeight: 700, fontSize: 16 }}>{period.label} 명세서 준비 중</div>
                      <div style={{ fontSize: 12.5, color: "var(--ink-3)", marginTop: 8, lineHeight: 1.6 }}>급여 확정 후 발송되면 여기에서 바로 확인할 수 있습니다.<br />발송 예정일 {period.payDate}</div>
                    </div>)}
                {wtab === "cert" && <CertPanel client={client} e={e} ss={ss} onIssue={(type, doc) => {
                  ctx.addCertRequest(e.id, type);
                  const opened = doc && window.printDocument(doc.title, doc.html);
                  toast(opened ? type + "증명서를 발급했습니다 — 인쇄창에서 PDF로 저장하세요" : "팝업이 차단되어 인쇄창을 열지 못했습니다", opened ? "ok" : "warn");
                }} />}
                {wtab === "yearend" && <YearEndPanel e={e} ss={ss} onSubmit={(items) => { ctx.submitYearEnd(e.id, items); toast("연말정산 자료를 제출했습니다 — 노무사 도착", "ok"); }} />}
                {wtab === "leave" && <LeavePanel e={e} ss={ss} onRequest={(req) => { ctx.addLeaveRequest(e.id, req); toast("휴가를 신청했습니다 — 승인 대기", "ok"); }} />}
              </div>

              {/* 하단 탭바 */}
              <div style={{ display: "flex", borderTop: "1px solid #d8d8dc", background: "rgba(248,248,250,.96)", paddingBottom: 6 }}>
                {WTABS.map((tb) => {
                  const on = wtab === tb.key;
                  return (
                    <button key={tb.key} onClick={() => setWtab(tb.key)} style={{
                      flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 2, padding: "8px 0 4px", border: "none",
                      background: "none", cursor: "pointer", color: on ? "var(--accent)" : "var(--ink-3)", fontFamily: "inherit", position: "relative",
                    }}>
                      <span style={{ position: "relative" }}>
                        <Ms name={tb.icon} style={{ fontSize: 23 }} />
                        {tb.badge > 0 && <span style={{ position: "absolute", top: -3, right: -8, minWidth: 15, height: 15, padding: "0 3px", borderRadius: 99, background: "var(--accent)", color: "#fff", fontSize: 9.5, fontWeight: 800, display: "grid", placeItems: "center" }}>{tb.badge}</span>}
                        {tb.dot && !tb.badge && <span style={{ position: "absolute", top: -1, right: -4, width: 8, height: 8, borderRadius: 99, background: "var(--ok)" }} />}
                      </span>
                      <span style={{ fontSize: 10.5, fontWeight: on ? 700 : 500 }}>{tb.label}</span>
                    </button>
                  );
                })}
              </div>
            </div>
          </IOSDevice>
        </div>
      </div>

    </div>
  );
}

/* 명세서 패널 (실지급 히어로 + 지급/공제 + 계산방법) — 조회 전용 */
function PayslipPanel({ client, e, run, period }) {
  const accent = "var(--accent)";
  const sec = (title, color) => <div style={{ fontSize: 12, fontWeight: 700, color, margin: "0 0 6px" }}>{title}</div>;
  // React key 경고가 나던 자리 — 리스트 항목이므로 key가 필요하다(E9).
  const line = (key, k, v, opt) => (
    <div key={key} style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 8, padding: "5px 0", fontSize: 13.5, color: opt && opt.minus ? "var(--danger)" : "var(--ink-2)" }}>
      <span style={{ minWidth: 0 }}>{k}{opt && opt.note && <span style={{ display: "block", fontSize: 10, color: "var(--ink-4)", lineHeight: 1.4 }}>{opt.note}</span>}</span>
      <span style={{ fontVariantNumeric: "tabular-nums", fontWeight: 600, color: "var(--ink)", flex: "none" }}>{KRW(v)}</span>
    </div>
  );
  // 근기법 §48② 계산방법 — 종이 명세서와 같은 커널 산식(payslipFormula)을 그대로 쓴다.
  const formula = (x) => window.BARUNSEM.payslipFormula(x, run, e);
  return (
    <div style={{ padding: "2px 0 16px" }}>
      <div style={{ margin: "0 16px 14px", background: accent, borderRadius: 18, padding: "20px 20px 22px", color: "#fff", boxShadow: "0 8px 24px -8px rgba(40,40,80,.4)" }}>
        <div style={{ fontSize: 12.5, opacity: .85 }}>{period.label} 실지급액</div>
        <div style={{ fontSize: 34, fontWeight: 800, letterSpacing: "-0.03em", fontVariantNumeric: "tabular-nums", marginTop: 2 }}>{KRW(run.net)}<span style={{ fontSize: 16, fontWeight: 600 }}>원</span></div>
        <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
          <div style={{ flex: 1, background: "rgba(255,255,255,.16)", borderRadius: 10, padding: "8px 10px" }}>
            <div style={{ fontSize: 10.5, opacity: .8 }}>지급계</div>
            <div style={{ fontSize: 14, fontWeight: 700, fontVariantNumeric: "tabular-nums" }}>{KRW(run.gross)}</div>
          </div>
          <div style={{ flex: 1, background: "rgba(255,255,255,.16)", borderRadius: 10, padding: "8px 10px" }}>
            <div style={{ fontSize: 10.5, opacity: .8 }}>공제계</div>
            <div style={{ fontSize: 14, fontWeight: 700, fontVariantNumeric: "tabular-nums" }}>−{KRW(run.deductTotal)}</div>
          </div>
        </div>
      </div>

      <div style={{ margin: "0 16px 12px", background: "var(--surface)", borderRadius: 16, padding: "12px 16px", fontSize: 13.5 }}>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "3px 0" }}><span style={{ color: "var(--ink-3)" }}>부서·직급</span><span>{e.dept} · {e.title}</span></div>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "3px 0" }}><span style={{ color: "var(--ink-3)" }}>급여체계</span><span>{e.scheme}</span></div>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "3px 0" }}><span style={{ color: "var(--ink-3)" }}>지급일</span><span>{period.payDate}</span></div>
      </div>

      <div style={{ margin: "0 16px 12px", background: "var(--surface)", borderRadius: 16, padding: "14px 16px" }}>
        {sec("지급 내역", accent)}
        {run.earnings.map((x) => line(x.key, x.name, x.amount, { minus: x.amount < 0, note: formula(x) }))}
        <div style={{ height: 1, background: "var(--line)", margin: "8px 0" }} />
        {sec("공제 내역", "var(--danger)")}
        {run.deductions.map((x) => line(x.key, x.name, -x.amount, { minus: true, note: formula(x) }))}
      </div>

      <div style={{ margin: "0 16px 14px", fontSize: 10.5, color: "var(--ink-4)", lineHeight: 1.5 }}>
        통상시급 {KRW(run.ordinaryHourly)}원 · 비과세 {KRW(run.nonTax)}원 · 근로기준법 §48 임금명세서
      </div>

    </div>
  );
}

/* 증명서 발급 패널 — 재직·경력·원천징수영수증 */
function CertPanel({ client, e, ss, onIssue }) {
  const B = window.BARUNSEM;
  const ten = B.tenure(e.joined, WORKER_ASOF);
  const [sel, setSel] = useState("재직");
  const TYPES = [
    { k: "재직", icon: "badge", desc: "재직 사실 증명" },
    { k: "경력", icon: "work_history", desc: "근속·담당 업무 증명" },
    { k: "원천징수", icon: "receipt", desc: "전년 총급여·결정세액" },
  ];
  const ye = e.yearEnd || {};
  const yeRun = ye.totalPay ? B.yearEndSettlement(ye, B.RULEBOOK.RB_DED) : null;
  // F4 — 실제 파일을 만든다. 이력 배열에 한 줄 넣는 것으로는 근로자 손에 아무것도 남지 않는다.
  const certHtml = () => {
    const title = sel === "원천징수" ? "근로소득 원천징수영수증" : sel + "증명서";
    const rows = [["성명", e.name], ["부서 · 직급", e.dept + " · " + e.title], ["입사일", e.joined || "—"], ["근속기간", ten ? ten.label : "—"]];
    if (sel === "재직") rows.push(["용도", "제출용 (재직 사실 증명)"]);
    if (sel === "경력") rows.push(["담당업무", e.title]);
    if (sel === "원천징수" && yeRun) rows.push(["전년 총급여", KRW(ye.totalPay) + "원"], ["결정세액", KRW(yeRun.determined) + "원"], ["기납부세액", KRW(ye.prepaidTax) + "원"]);
    const esc = (v) => String(v).replace(/[&<>]/g, (m) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[m]));
    return { title, html: `<h1>${esc(title)}</h1><div class="sub">${esc(client.name)}</div>`
      + `<table>${rows.map((r) => `<tr><th>${esc(r[0])}</th><td>${esc(r[1])}</td></tr>`).join("")}</table>`
      + `<div class="foot">위와 같이 증명합니다.<br>발급일 ${WORKER_ASOF} · 한결노동법률사무소</div>` };
  };
  return (
    <div style={{ padding: "4px 16px 16px" }}>
      <PanelHd icon="verified_user" title="증명서 발급" sub="모바일에서 바로 발급 — 노무사 핑퐁 없이" />
      <div style={{ display: "flex", gap: 7, marginBottom: 12 }}>
        {TYPES.map((t) => (
          <button key={t.k} onClick={() => setSel(t.k)} style={{
            flex: 1, padding: "10px 4px", borderRadius: 12, border: "1px solid " + (sel === t.k ? "var(--accent)" : "var(--line-2)"),
            background: sel === t.k ? "var(--accent-soft)" : "var(--surface)", cursor: "pointer", fontFamily: "inherit",
            display: "flex", flexDirection: "column", alignItems: "center", gap: 4, color: sel === t.k ? "var(--accent-ink)" : "var(--ink-2)",
          }}>
            <Ms name={t.icon} style={{ fontSize: 20 }} />
            <span style={{ fontSize: 11, fontWeight: 700 }}>{t.k}{t.k === "원천징수" ? "영수증" : "증명서"}</span>
          </button>
        ))}
      </div>

      {/* 양식 미리보기 */}
      <div style={{ background: "var(--surface)", borderRadius: 14, padding: "18px 18px", border: "1px solid var(--line-2)" }}>
        <div style={{ textAlign: "center", paddingBottom: 12, borderBottom: "2px solid var(--ink)" }}>
          <div style={{ fontWeight: 800, fontSize: 17, letterSpacing: "-0.02em" }}>{sel === "원천징수" ? "근로소득 원천징수영수증" : sel + "증명서"}</div>
          <div style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 2 }}>{client.name}</div>
        </div>
        <div style={{ padding: "12px 0", fontSize: 13 }}>
          <CertRow k="성명" v={e.name} />
          <CertRow k="부서 · 직급" v={e.dept + " · " + e.title} />
          <CertRow k="입사일" v={e.joined || "—"} />
          <CertRow k="근속기간" v={ten ? ten.label : "—"} />
          {sel === "재직" && <CertRow k="용도" v="제출용 (재직 사실 증명)" />}
          {sel === "경력" && <CertRow k="담당업무" v={e.title} />}
          {sel === "원천징수" && yeRun && <>
            <CertRow k="전년 총급여" v={KRW(ye.totalPay) + "원"} />
            <CertRow k="결정세액" v={KRW(yeRun.determined) + "원"} />
            <CertRow k="기납부세액" v={KRW(ye.prepaidTax) + "원"} />
          </>}
        </div>
        <div style={{ fontSize: 10, color: "var(--ink-4)", textAlign: "center", borderTop: "1px solid var(--line)", paddingTop: 8 }}>발급일 {WORKER_ASOF} · 한결노동법률사무소 직인</div>
      </div>

      <button style={{ ...mobileBtn(true), marginTop: 14, width: "100%" }} onClick={() => onIssue(sel, certHtml())}><Ms name="download" style={{ fontSize: 18 }} />{sel}{sel === "원천징수" ? "영수증" : "증명서"} 발급 (PDF)</button>

      {ss.certs.length > 0 && (
        <div style={{ marginTop: 16 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: "var(--ink-2)", marginBottom: 6 }}>발급 이력</div>
          {ss.certs.slice().reverse().map((c) => (
            <div key={c.id} style={{ display: "flex", alignItems: "center", gap: 8, background: "var(--surface)", borderRadius: 10, padding: "9px 12px", marginBottom: 6, fontSize: 12.5 }}>
              <Ms name="task_alt" style={{ color: "var(--ok)", fontSize: 17 }} />
              <span style={{ flex: 1, fontWeight: 600 }}>{c.type}{c.type === "원천징수" ? "영수증" : "증명서"}</span>
              <span style={{ color: "var(--ink-4)", fontSize: 11 }}>{c.at}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
function CertRow({ k, v }) {
  return <div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0" }}><span style={{ color: "var(--ink-3)" }}>{k}</span><span style={{ fontWeight: 600 }}>{v}</span></div>;
}

/* 연말정산 자료 제출 패널 */
function YearEndPanel({ e, ss, onSubmit }) {
  const submitted = ss.ye && ss.ye.submitted;
  // B12 수집항목 일치 — 노무사 폼(RB-DED)이 쓰는 항목과 1:1로 맞춘다. 빠진 칸은 노무사가 다시 물어야 한다.
  const [f, setF] = useState({ spouse: false, dependents: 0, elderly: 0, disabled: 0,
    creditCard: "", housing: "", medical: "", education: "", donation: "", rent: "", insurance: "", pensionSavings: "" });
  const upd = (k, v) => setF((p) => ({ ...p, [k]: v }));
  const num = (v) => { const n = Number(String(v).replace(/[^\d]/g, "")); return isFinite(n) ? n : 0; };
  const items = {
    family: { spouse: !!f.spouse, dependents: num(f.dependents), elderly: num(f.elderly), disabled: num(f.disabled) },
    incomeItems: { creditCard: num(f.creditCard), housing: num(f.housing) },
    creditItems: { medical: num(f.medical), education: num(f.education), donation: num(f.donation), rent: num(f.rent),
      insurance: num(f.insurance), pensionSavings: num(f.pensionSavings) },
  };

  if (submitted) {
    return (
      <div style={{ padding: "4px 16px 16px" }}>
        <PanelHd icon="account_balance_wallet" title="연말정산 자료 제출" sub="간소화 자료 제출" />
        <div style={{ background: "var(--ok-soft)", border: "1px solid var(--ok-line)", borderRadius: 14, padding: "22px 18px", textAlign: "center" }}>
          <div style={{ width: 52, height: 52, borderRadius: "50%", background: "var(--surface)", display: "grid", placeItems: "center", margin: "0 auto 12px" }}><Ms name="task_alt" style={{ color: "var(--ok)", fontSize: 30 }} /></div>
          <div style={{ fontWeight: 750, fontSize: 16 }}>제출 완료</div>
          <div style={{ fontSize: 12.5, color: "color-mix(in oklab, var(--ok) 55%, black)", marginTop: 6, lineHeight: 1.55 }}>연말정산 자료가 노무사에게 도착했습니다.<br />검토 후 정산 결과를 명세서로 받습니다.</div>
          <div style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 10 }}>제출 {ss.ye.at}</div>
        </div>
      </div>
    );
  }
  return (
    <div style={{ padding: "4px 16px 16px" }}>
      <PanelHd icon="account_balance_wallet" title="연말정산 자료 제출" sub="부양가족·지출 자료를 입력해 제출하세요" />
      <div style={{ background: "var(--surface)", borderRadius: 14, padding: "14px 16px", display: "flex", flexDirection: "column", gap: 12 }}>
        <label style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, fontSize: 13 }}>
          <span>배우자 공제 대상</span>
          <input type="checkbox" checked={!!f.spouse} onChange={(ev) => upd("spouse", ev.target.checked)} style={{ width: 20, height: 20 }} />
        </label>
        <WField label="부양가족 수 (본인·배우자 외)" unit="명" value={f.dependents} onChange={(v) => upd("dependents", v)} />
        <WField label="경로우대 (만 70세 이상)" unit="명" value={f.elderly} onChange={(v) => upd("elderly", v)} />
        <WField label="장애인" unit="명" value={f.disabled} onChange={(v) => upd("disabled", v)} />
        <WField label="신용카드 등 사용액" unit="원" value={f.creditCard} onChange={(v) => upd("creditCard", v)} won />
        <WField label="주택자금 (원리금·차입금)" unit="원" value={f.housing} onChange={(v) => upd("housing", v)} won />
        <WField label="보장성보험료" unit="원" value={f.insurance} onChange={(v) => upd("insurance", v)} won />
        <WField label="연금계좌 납입액" unit="원" value={f.pensionSavings} onChange={(v) => upd("pensionSavings", v)} won />
        <WField label="의료비" unit="원" value={f.medical} onChange={(v) => upd("medical", v)} won />
        <WField label="교육비" unit="원" value={f.education} onChange={(v) => upd("education", v)} won />
        <WField label="기부금" unit="원" value={f.donation} onChange={(v) => upd("donation", v)} won />
        <WField label="월세액" unit="원" value={f.rent} onChange={(v) => upd("rent", v)} won />
      </div>
      <div style={{ fontSize: 10.5, color: "var(--ink-4)", margin: "10px 2px", lineHeight: 1.5 }}>입력 자료는 노무사 검토용 초안입니다. 한도·요건 적용과 최종 반영은 노무사가 확정합니다.</div>
      <button style={{ ...mobileBtn(true), width: "100%" }} onClick={() => onSubmit(items)}><Ms name="send" style={{ fontSize: 18 }} />노무사에게 제출</button>
    </div>
  );
}
function WField({ label, unit, value, onChange, won }) {
  return (
    <div>
      <div style={{ fontSize: 11.5, color: "var(--ink-3)", fontWeight: 600, marginBottom: 4 }}>{label}</div>
      <div style={{ display: "flex", alignItems: "center", gap: 6, border: "1px solid var(--line-2)", borderRadius: 10, padding: "0 12px", height: 42, background: "#fafafb" }}>
        <input value={value} onChange={(e) => onChange(won ? e.target.value.replace(/[^\d]/g, "") : e.target.value.replace(/[^\d]/g, ""))} placeholder="0" inputMode="numeric"
          style={{ flex: 1, border: "none", background: "none", fontSize: 15, fontFamily: "inherit", fontVariantNumeric: "tabular-nums", textAlign: "right", outline: "none", minWidth: 0 }} />
        <span style={{ fontSize: 12.5, color: "var(--ink-3)" }}>{unit}</span>
      </div>
    </div>
  );
}

/* 연차 잔여 조회 + 휴가 신청 패널 */
function LeavePanel({ e, ss, onRequest }) {
  const B = window.BARUNSEM;
  const total = B.annualLeaveDays(e.joined, WORKER_ASOF);
  const approvedDays = ss.leaveReqs.filter((r) => r.status === "approved").reduce((s, r) => s + r.days, 0);
  const usedBase = e.leaveUsed || 0;
  const used = usedBase + approvedDays;
  const remain = Math.max(0, total - used);
  const pct = total > 0 ? Math.round((used / total) * 100) : 0;
  const [from, setFrom] = useState("");
  const [to, setTo] = useState("");
  const [reason, setReason] = useState("연차");
  const dayCount = (a, b) => { if (!a || !b) return 0; const d = Math.round((new Date(b) - new Date(a)) / 86400000) + 1; return d > 0 ? d : 0; };
  const days = dayCount(from, to);
  const valid = from && to && days > 0;

  return (
    <div style={{ padding: "4px 16px 16px" }}>
      <PanelHd icon="beach_access" title="연차 · 휴가" sub="잔여 연차 조회 및 휴가 신청" />
      {/* 잔여 게이지 */}
      <div style={{ background: "var(--surface)", borderRadius: 16, padding: "16px 18px", marginBottom: 12 }}>
        <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between" }}>
          <span style={{ fontSize: 12.5, color: "var(--ink-3)", fontWeight: 600 }}>잔여 연차</span>
          <span style={{ fontVariantNumeric: "tabular-nums" }}><b style={{ fontSize: 28, color: "var(--accent)" }}>{remain}</b><span style={{ fontSize: 13, color: "var(--ink-3)" }}> / {total}일</span></span>
        </div>
        <div style={{ height: 8, borderRadius: 99, background: "#ececf1", marginTop: 10, overflow: "hidden" }}>
          <div style={{ width: pct + "%", height: "100%", background: "var(--accent)", borderRadius: 99 }} />
        </div>
        <div style={{ display: "flex", justifyContent: "space-between", marginTop: 8, fontSize: 11.5, color: "var(--ink-3)" }}>
          <span>발생 {total}일 (근속 기준)</span><span>사용 {used}일</span>
        </div>
      </div>

      {/* 휴가 신청 */}
      <div style={{ background: "var(--surface)", borderRadius: 16, padding: "14px 16px" }}>
        <div style={{ fontSize: 12.5, fontWeight: 700, marginBottom: 10 }}>휴가 신청</div>
        <div style={{ display: "flex", gap: 8, marginBottom: 10 }}>
          <div style={{ flex: 1 }}><div style={{ fontSize: 11, color: "var(--ink-3)", marginBottom: 3 }}>시작일</div><input type="date" value={from} onChange={(e) => setFrom(e.target.value)} style={leaveInput} /></div>
          <div style={{ flex: 1 }}><div style={{ fontSize: 11, color: "var(--ink-3)", marginBottom: 3 }}>종료일</div><input type="date" value={to} onChange={(e) => setTo(e.target.value)} style={leaveInput} /></div>
        </div>
        <div style={{ display: "flex", gap: 7, marginBottom: 12 }}>
          {["연차", "반차", "경조", "병가"].map((r) => (
            <button key={r} onClick={() => setReason(r)} style={{ flex: 1, height: 32, borderRadius: 8, border: "1px solid " + (reason === r ? "var(--accent)" : "var(--line-2)"), background: reason === r ? "var(--accent-soft)" : "var(--surface)", color: reason === r ? "var(--accent-ink)" : "var(--ink-2)", fontSize: 12, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" }}>{r}</button>
          ))}
        </div>
        {valid && <div style={{ fontSize: 12, color: "var(--ink-2)", marginBottom: 10, textAlign: "center" }}>신청 일수 <b style={{ color: "var(--accent-ink)" }}>{days}일</b> · 신청 후 잔여 {Math.max(0, remain - days)}일</div>}
        <button disabled={!valid} style={{ ...mobileBtn(true), width: "100%", opacity: valid ? 1 : .5 }} onClick={() => { if (!valid) return; onRequest({ from, to, days, reason }); setFrom(""); setTo(""); }}><Ms name="send" style={{ fontSize: 18 }} />휴가 신청</button>
      </div>

      {ss.leaveReqs.length > 0 && (
        <div style={{ marginTop: 16 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: "var(--ink-2)", marginBottom: 6 }}>신청 내역</div>
          {ss.leaveReqs.slice().reverse().map((r) => (
            <div key={r.id} style={{ display: "flex", alignItems: "center", gap: 8, background: "var(--surface)", borderRadius: 10, padding: "10px 12px", marginBottom: 6, fontSize: 12.5 }}>
              <Ms name={r.status === "approved" ? "event_available" : "hourglass_top"} style={{ color: r.status === "approved" ? "var(--ok)" : "var(--warn)", fontSize: 18 }} />
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 600 }}>{r.reason} · {r.days}일</div>
                <div style={{ color: "var(--ink-4)", fontSize: 11 }}>{r.from} ~ {r.to}</div>
              </div>
              <span style={{ fontSize: 10.5, fontWeight: 700, padding: "3px 8px", borderRadius: 99, background: r.status === "approved" ? "var(--ok-soft)" : "var(--warn-soft)", color: r.status === "approved" ? "color-mix(in oklab, var(--ok) 60%, black)" : "color-mix(in oklab, var(--warn) 55%, black)" }}>{r.status === "approved" ? "승인" : "승인 대기"}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
const leaveInput = { width: "100%", border: "1px solid var(--line-2)", borderRadius: 8, padding: "0 10px", height: 38, fontSize: 13, fontFamily: "inherit", background: "#fafafb", outline: "none", boxSizing: "border-box" };

function PanelHd({ icon, title, sub }) {
  return (
    <div style={{ padding: "8px 2px 14px" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}><Ms name={icon} style={{ color: "var(--accent)", fontSize: 20 }} /><span style={{ fontWeight: 750, fontSize: 16, letterSpacing: "-0.02em" }}>{title}</span></div>
      {sub && <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 3 }}>{sub}</div>}
    </div>
  );
}

function mobileBtn(primary) {
  return {
    flex: 1, height: 50, borderRadius: 14, border: "none", cursor: "pointer",
    display: "flex", alignItems: "center", justifyContent: "center", gap: 7,
    fontSize: 15.5, fontWeight: 700, fontFamily: "inherit",
    background: primary ? "var(--accent)" : "var(--surface)",
    color: primary ? "#fff" : "var(--ink-2)",
    boxShadow: primary ? "0 6px 16px -6px rgba(40,40,80,.5)" : "inset 0 0 0 1px var(--line-2)",
  };
}

window.Worker = Worker;
