const { useState, useEffect, useCallback } = React;

// ─────────────────────────────────────────────────────────────
// 상수 정의
// ─────────────────────────────────────────────────────────────
const API_BASE = ""; // 동일 오리진. 필요 시 절대 URL로 교체.

const CURRENT_YEAR = new Date().getFullYear();
// 올해(부가세·원천세 당해 신고건) ~ 6년 전. 종소세는 보통 전년(귀속) 기본.
const YEAR_OPTIONS = Array.from({ length: 7 }, (_, i) => CURRENT_YEAR - i);

const AUTH_LABEL = (r) => (r.simple_auth ? "간편인증" : "ID/PW");

const SA_PROVIDERS = [
  { key: "kakao", label: "카카오톡" },
  { key: "pass", label: "통신사PASS" },
  { key: "toss", label: "토스" },
];

const SERVICES = [
  { key: "jongso", label: "종합소득세", desc: "신고서·계산·신고안내자료 등", ready: true },
  { key: "vat", label: "부가가치세", desc: "신고서·매출매입 등", ready: false },
  { key: "corp", label: "법인세", desc: "준비 중", ready: false },
];

const SERVICE_LABEL = { ...Object.fromEntries(SERVICES.map((s) => [s.key, s.label])), quote: "단순 견적" };

const STATUS_META = {
  pending: { label: "대기", cls: "bg-slate-100 text-slate-600 ring-slate-200" },
  running: { label: "진행중", cls: "bg-blue-50 text-blue-700 ring-blue-200" },
  needs_auth: { label: "인증대기", cls: "bg-amber-50 text-amber-700 ring-amber-200" },
  done: { label: "완료", cls: "bg-emerald-50 text-emerald-700 ring-emerald-200" },
  failed: { label: "실패", cls: "bg-rose-50 text-rose-700 ring-rose-200" },
};

// ─────────────────────────────────────────────────────────────
// 동선 지도 — Leaflet 폴리라인(좌표를 선으로 연결). API키 불필요(OSM 타일).
// points: [{lat,lng,label?,time?}] (시간순). 마지막 점=현재위치(빨강).
// ─────────────────────────────────────────────────────────────
function RouteMap({ points, height = 300 }) {
  const ref = React.useRef(null);
  const mapRef = React.useRef(null);
  const layerRef = React.useRef(null);
  useEffect(() => {
    if (!ref.current || typeof L === "undefined") return;
    if (!mapRef.current) {
      mapRef.current = L.map(ref.current, { scrollWheelZoom: false }).setView([37.55, 126.98], 11);
      L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: "© OpenStreetMap", maxZoom: 19 }).addTo(mapRef.current);
      layerRef.current = L.layerGroup().addTo(mapRef.current);
    }
    const map = mapRef.current, layer = layerRef.current;
    layer.clearLayers();
    const pts = (points || []).filter((p) => p.lat && p.lng);
    const latlngs = pts.map((p) => [p.lat, p.lng]);
    if (latlngs.length > 1) L.polyline(latlngs, { color: "#2563eb", weight: 3, opacity: 0.85 }).addTo(layer);
    latlngs.forEach((ll, i) => {
      const isLast = i === latlngs.length - 1;
      const bg = isLast ? "#dc2626" : "#2563eb";
      // 번호 배지 마커(divIcon) — 타임라인 번호와 1:1 대조
      const icon = L.divIcon({
        className: "",
        html: `<div style="background:${bg};color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;border:2px solid #fff;box-shadow:0 1px 3px rgba(0,0,0,.4)">${i + 1}</div>`,
        iconSize: [22, 22], iconAnchor: [11, 11],
      });
      L.marker(ll, { icon }).bindPopup(`<b>#${i + 1}</b> ${pts[i].label || ""}${pts[i].time ? "<br>" + pts[i].time : ""}`).addTo(layer);
    });
    if (latlngs.length === 1) map.setView(latlngs[0], 15);
    else if (latlngs.length > 1) map.fitBounds(latlngs, { padding: [25, 25], maxZoom: 16 });
    setTimeout(() => map.invalidateSize(), 60);
  }, [points]);
  useEffect(() => () => { if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; } }, []);
  return <div ref={ref} style={{ height, width: "100%", zIndex: 0 }} className="rounded-xl border border-slate-200" />;
}

// ─────────────────────────────────────────────────────────────
// 하이픈 자동 포맷 (표시용). 전송 시엔 숫자만 보냄.
// ─────────────────────────────────────────────────────────────
const onlyDigits = (s) => (s || "").replace(/\D/g, "");

function formatPhone(s) {
  const d = onlyDigits(s).slice(0, 11);
  if (d.length < 4) return d;
  if (d.length < 7) return `${d.slice(0, 3)}-${d.slice(3)}`;
  if (d.length === 10) return `${d.slice(0, 3)}-${d.slice(3, 6)}-${d.slice(6)}`; // 3-3-4
  return `${d.slice(0, 3)}-${d.slice(3, 7)}-${d.slice(7)}`; // 3-4-4 (11자리)
}

function formatJumin(s) {
  const d = onlyDigits(s).slice(0, 13);
  if (d.length < 7) return d;
  return `${d.slice(0, 6)}-${d.slice(6)}`; // 6-7
}

// 단순 견적용: 앞 6자리 + 뒤 1자리까지만 (XXXXXX-X)
function formatJuminShort(s) {
  const d = onlyDigits(s).slice(0, 7);
  if (d.length < 7) return d;
  return `${d.slice(0, 6)}-${d.slice(6)}`; // 6-1
}

function formatBizNo(s) {
  const d = onlyDigits(s).slice(0, 10);
  if (d.length < 4) return d;
  if (d.length < 6) return `${d.slice(0, 3)}-${d.slice(3)}`;
  return `${d.slice(0, 3)}-${d.slice(3, 5)}-${d.slice(5)}`; // 3-2-5
}

// 법인등록번호 13자리 (6-7)
function formatCorpNo(s) {
  const d = onlyDigits(s).slice(0, 13);
  if (d.length < 7) return d;
  return `${d.slice(0, 6)}-${d.slice(6)}`; // 6-7
}

// 데모(목업) — API 미연결 시 폴백용 가상 데이터.
// ⚠️ 전원 가상 인물 — 실제 고객/영업사원 실명·실주민·실연락처를 쓰지 않습니다.
//    실제 customers.json 구조(simple_auth/sa_provider/biz_nos/jumin 등)만 참조.
const MOCK_REQUESTS = [
  {
    request_id: "REQ-20260610-001",
    name: "홍길동",
    platform: "크몽",
    cust_label: "종소세-2024",
    phone: "010-0000-0001",
    jumin: "8203**-*******",
    hometax_id: "demo_hong",
    hometax_pw: "demo-pw-0001",
    biz_nos: ["111-22-33344"],
    simple_auth: true,
    sa_provider: "kakao",
    year: CURRENT_YEAR - 1,
    service: "jongso",
    status: "needs_auth",
    created_at: "2026-06-10 09:12",
    memo: "오전 중 연락 가능, 사업장 1곳만 우선 처리 희망합니다.",
    logs: ["요청 접수", "워커 큐 등록", "홈택스 로그인 시도", "간편인증(카카오) 승인 대기 중…"],
    files: [],
  },
  {
    request_id: "REQ-20260610-002",
    name: "김민준",
    platform: "네이버 블로그",
    cust_label: "신규",
    phone: "010-0000-0002",
    jumin: "9105**-*******",
    biz_nos: [],
    simple_auth: false,
    year: CURRENT_YEAR - 1,
    service: "jongso",
    status: "done",
    created_at: "2026-06-10 08:45",
    logs: ["요청 접수", "로그인 성공", "신고안내자료 다운로드", "종합소득세 신고서 다운로드", "완료"],
    files: [
      { name: "신고안내자료.xlsx", url: "#" },
      { name: "종합소득세_신고서.pdf", url: "#" },
    ],
  },
  {
    request_id: "REQ-20260609-014",
    name: "이서연",
    platform: "당근",
    cust_label: "VIP",
    phone: "010-0000-0003",
    jumin: "7608**-*******",
    biz_nos: ["222-33-44455", "666-77-88899"],
    simple_auth: false,
    year: CURRENT_YEAR - 1,
    service: "vat",
    status: "running",
    created_at: "2026-06-09 17:30",
    logs: ["요청 접수", "로그인 성공", "부가가치세 자료 수집 중…"],
    files: [],
  },
  {
    request_id: "REQ-20260609-009",
    name: "박지후",
    platform: "크몽",
    cust_label: "재방문",
    phone: "010-0000-0004",
    jumin: "8810**-*******",
    biz_nos: [],
    simple_auth: true,
    sa_provider: "toss",
    year: CURRENT_YEAR - 1,
    service: "jongso",
    status: "failed",
    created_at: "2026-06-09 14:02",
    logs: ["요청 접수", "간편인증(토스) 승인 대기", "시간 초과 — 실패"],
    files: [],
  },
  {
    request_id: "REQ-20260610-101",
    name: "최유나",
    platform: "김영업",
    cust_label: "견적문의",
    mode: "quote",
    jumin: "920305-2",
    hometax_id: "demo_choi",
    hometax_pw: "demo-pw-0101",
    biz_nos: [],
    simple_auth: false,
    service: "jongso",
    status: "pending",
    created_at: "2026-06-10 10:30",
    memo: "종소세 견적 먼저 받아보고 싶습니다.",
    logs: ["견적 문의 접수"],
    files: [],
  },
  {
    request_id: "REQ-20260610-102",
    name: "정해성",
    platform: "네이버 블로그",
    cust_label: "견적문의",
    mode: "quote",
    jumin: "880712-1",
    hometax_id: "demo_jung",
    hometax_pw: "demo-pw-0102",
    biz_nos: [],
    simple_auth: false,
    service: "vat",
    status: "pending",
    created_at: "2026-06-10 11:05",
    memo: "부가세 신고 대행 견적 부탁드립니다.",
    logs: ["견적 문의 접수"],
    files: [],
  },
  {
    request_id: "REQ-20260610-103",
    name: "주식회사 가나다",
    platform: "직접문의",
    cust_label: "법인",
    mode: "corp",
    corp_name: "주식회사 가나다",
    corp_no: "110111-7654321",
    biz_no: "777-81-99001",
    corp_hometax_id: "demo_ganada",
    corp_hometax_pw: "demo-corp-0103",
    ceo_name: "김대표",
    ceo_jumin: "7011**-*******",
    personal_hometax_id: "demo_ceo",
    personal_hometax_pw: "demo-ceo-0103",
    biz_nos: ["777-81-99001"],
    simple_auth: false,
    service: "corp",
    status: "running",
    created_at: "2026-06-10 09:50",
    logs: ["요청 접수", "로그인 성공", "법인세 자료 수집 중…"],
    files: [],
  },
  {
    request_id: "REQ-20260610-104",
    name: "주식회사 라마바",
    platform: "박영업",
    cust_label: "법인 견적",
    mode: "corp",
    corp_name: "주식회사 라마바",
    biz_no: "888-81-22002",
    biz_nos: ["888-81-22002"],
    simple_auth: false,
    service: "corp",
    status: "pending",
    created_at: "2026-06-10 12:10",
    memo: "법인 기장·신고 보수료 견적 요청.",
    logs: ["법인 견적 문의 접수"],
    files: [],
  },
  {
    request_id: "REQ-20260609-201",
    name: "강도윤",
    platform: "크몽",
    cust_label: "완료",
    biz_nos: ["123-81-55667"],
    simple_auth: false,
    service: "vat",
    status: "done",
    created_at: "2026-06-09 13:20",
    logs: ["요청 접수", "로그인 성공", "부가세 신고서 다운로드", "완료"],
    files: [{ name: "부가가치세_신고서.pdf", url: "#" }],
  },
];

// ─────────────────────────────────────────────────────────────
// API 래퍼 (try/catch + 목업 폴백)
// ─────────────────────────────────────────────────────────────
// 세션 만료/미로그인(401) → 역할 비우고 로그인 화면으로(1회 리로드, 루프 방지).
function _handle401() {
  try { localStorage.removeItem("site_role"); localStorage.removeItem("site_auth"); } catch (e) {}
  try {
    if (!sessionStorage.getItem("_relogin")) {
      sessionStorage.setItem("_relogin", "1");
      window.location.reload();
    }
  } catch (e) {}
}
async function apiGet(path, fallback) {
  try {
    const res = await fetch(`${API_BASE}${path}`, { credentials: "same-origin" });
    if (res.status === 401) { _handle401(); throw new Error(401); }
    if (!res.ok) throw new Error(res.status);
    try { sessionStorage.removeItem("_relogin"); } catch (e) {}
    return await res.json();
  } catch (e) {
    return fallback;
  }
}
async function apiPost(path, body, fallback) {
  try {
    const res = await fetch(`${API_BASE}${path}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "same-origin",
      body: JSON.stringify(body),
    });
    // 로그인 요청의 401 은 '비번 틀림' → 화면 리로드 말고 호출부에서 처리.
    if (res.status === 401 && !path.includes("/api/login")) { _handle401(); throw new Error(401); }
    if (!res.ok) throw new Error(res.status);
    try { sessionStorage.removeItem("_relogin"); } catch (e) {}
    return await res.json();
  } catch (e) {
    return fallback;
  }
}

// ─────────────────────────────────────────────────────────────
// 공통 UI 조각
// ─────────────────────────────────────────────────────────────
function StatusBadge({ status }) {
  const m = STATUS_META[status] || STATUS_META.pending;
  return (
    <span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ring-1 ring-inset ${m.cls}`}>
      {m.label}
    </span>
  );
}

// 법인/개인 구분 배지 — r.mode === "corp" 면 법인, 그 외 개인.
function KindBadge({ r }) {
  const corp = r && r.mode === "corp";
  return (
    <span className={`inline-flex shrink-0 items-center rounded px-1.5 py-0.5 text-[10px] font-bold ${corp ? "bg-violet-100 text-violet-700" : "bg-sky-100 text-sky-700"}`}>
      {corp ? "법인" : "개인"}
    </span>
  );
}

function Field({ label, required, hint, children }) {
  return (
    <label className="block">
      <span className="mb-1.5 flex items-center gap-1 text-sm font-medium text-slate-700">
        {label}
        {required && <span className="text-rose-500">*</span>}
      </span>
      {children}
      {hint && <span className="mt-1 block text-xs text-slate-400">{hint}</span>}
    </label>
  );
}

const inputCls =
  "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 placeholder:text-slate-400 focus:border-orange-500 focus:outline-none focus:ring-2 focus:ring-orange-100";

// 인셋 라벨 셀 — 라벨(박힌 글자)이 칸 안쪽 위에, 아래는 입력 영역
function Cell({ label, required, children }) {
  return (
    <label className="block bg-slate-100 px-4 pb-2 pt-2 transition focus-within:bg-orange-50/40">
      <span className="mb-1 flex items-center gap-1 text-[11px] font-medium uppercase tracking-wide text-slate-400">
        {label}
        {required && <span className="text-orange-400">*</span>}
      </span>
      <div className="rounded-md bg-white px-2.5 py-1.5 ring-1 ring-inset ring-slate-200 focus-within:ring-orange-300">
        {children}
      </div>
    </label>
  );
}

// 셀들을 묶는 그룹 카드 (얇은 구분선으로 셀 분리)
function CellGroup({ title, children }) {
  return (
    <div>
      {title && <span className="mb-2 block px-1 text-xs font-semibold uppercase tracking-wide text-orange-700">{title}</span>}
      <div className="overflow-hidden rounded-xl ring-1 ring-slate-200 [&>label]:border-b [&>label]:border-slate-200 [&>label:last-child]:border-b-0 [&>div>label]:border-b [&>div>label]:border-slate-200">
        {children}
      </div>
    </div>
  );
}

// 셀 내부 입력 (흰 입력박스 안, 보더 없음 — 박스가 외곽 담당)
const cellInput =
  "w-full border-0 bg-transparent p-0 text-[15px] font-medium text-slate-900 placeholder:font-normal placeholder:text-slate-300 focus:outline-none focus:ring-0";

// ─────────────────────────────────────────────────────────────
// 공통 완료 화면
// ─────────────────────────────────────────────────────────────
function SuccessScreen({ requestId, message, onReset, event = false }) {
  const [evClosed, setEvClosed] = useState(false);
  return (
   <>
    {event && PA_EVENT_LIVE() && !evClosed && <EventPopup variant="thanks" onClose={() => setEvClosed(true)} />}
    <div className="mx-auto max-w-lg px-4 py-16 text-center">
      <div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-50 ring-1 ring-emerald-200">
        <svg className="h-8 w-8 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
          <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
        </svg>
      </div>
      <h2 className="text-xl font-bold text-slate-800">접수되었습니다</h2>
      <p className="mt-2 text-sm text-slate-500">{message}</p>
      <div className="mt-6 inline-block rounded-lg bg-slate-50 px-5 py-3 ring-1 ring-slate-200">
        <span className="text-xs text-slate-400">접수번호</span>
        <div className="font-mono text-lg font-semibold text-slate-800">{requestId}</div>
      </div>
      <div>
        <button onClick={onReset} className="mt-8 text-sm font-medium text-orange-600 hover:text-orange-700">
          처음으로
        </button>
      </div>
    </div>
   </>
  );
}

// 정중한 마무리 인사 — 로그인 확인 후 고객에게(가치선언 + 창 닫기 안내). 파이프라인은 서버에서 진행.
// 브랜드 로고 — 이미지 대신 코드로 재현(해상도 깨짐 방지). 주황 'PA' 박스 + 상호 2줄.
function BrandLockup({ size = "lg" }) {
  const s = size === "lg"
    ? { box: "h-14 w-14 rounded-2xl text-2xl", name: "text-2xl", sub: "text-xs tracking-[0.2em]" }
    : { box: "h-9 w-9 rounded-lg text-base", name: "text-base", sub: "text-[10px] tracking-[0.15em]" };
  return (
    <div className="flex items-center gap-3">
      <div className={`flex shrink-0 items-center justify-center bg-orange-600 font-extrabold tracking-tight text-white ${s.box}`}>PA</div>
      <div className="text-left leading-tight">
        <div className={`font-bold text-gray-800 ${s.name}`}>피에이세무회계</div>
        <div className={`text-gray-400 ${s.sub}`}>Private Accountant</div>
      </div>
    </div>
  );
}

// 고객 폼 제출 후 '접수 완료' 확인 화면 (브랜드 헤더 + 체크 + 정중한 마무리). 디자인 스펙 고정.
function SubmitSuccess({ requestId, event = false }) {
  const [shown, setShown] = useState(false);
  const [evClosed, setEvClosed] = useState(false);
  useEffect(() => { const t = setTimeout(() => setShown(true), 20); return () => clearTimeout(t); }, []);
  return (
   <>
    {event && PA_EVENT_LIVE() && !evClosed && <EventPopup variant="thanks" onClose={() => setEvClosed(true)} />}
    <div className="flex min-h-screen items-center justify-center bg-gray-50 px-4"
         style={{ fontFamily: "'Pretendard', -apple-system, 'Malgun Gothic', sans-serif" }}>
      <div className="w-full max-w-md rounded-2xl bg-white px-6 py-12 shadow-md">
        {/* 0. 브랜드 헤더 — 코드로 재현한 로고(이미지 X). */}
        <div className="mb-10 flex justify-center">
          <BrandLockup size="lg" />
        </div>

        {/* 1. 체크 아이콘 (w-20, 마운트 시 scale/opacity 등장) */}
        <div className={`mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-emerald-50 transition-all duration-300 ${shown ? "scale-100 opacity-100" : "scale-90 opacity-0"}`}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" className="h-10 w-10 text-emerald-500">
            <path d="M5 13l4 4L19 7" />
          </svg>
        </div>

        {/* 2. 제목 */}
        <h2 className="mt-5 text-center text-2xl font-bold tracking-tight text-gray-900">접수되었습니다</h2>
        {/* 3. 부제 — 체크 원색(emerald)으로 확인 안내 */}
        <p className="mt-1 text-center text-sm font-medium text-emerald-500">입력하신 정보가 정상 확인되었습니다.</p>

        {/* 4. 접수번호 배지 (작고 연하게 — 핵심카피보다 약하게) */}
        {requestId != null && requestId !== "" && (
          <div className="mt-4 text-center">
            <span className="inline-block rounded-full bg-gray-50 px-2.5 py-1 text-xs text-gray-400">접수번호 #{requestId}</span>
          </div>
        )}

        {/* 5. 핵심 카피 (정중한 다짐, PA 브랜드 주황) */}
        <div className="mb-2 mt-10 text-center">
          <div className="space-y-1 text-lg font-semibold leading-loose text-gray-900">
            <p>신뢰와 믿음을 지키고</p>
            <p>고객님의 소중한 재산을 보호하는</p>
            <p><span className="font-bold text-orange-600">세무전문가</span>가 되겠습니다.</p>
          </div>
        </div>

        {/* 6. 구분선 (연하게 + 좁게) */}
        <hr className="mx-auto my-8 w-2/3 border-gray-100" />

        {/* 7. 강조 줄 */}
        <p className="text-center text-base font-bold text-gray-700">창을 닫으셔도 됩니다.</p>
        {/* 8. 마지막 줄 */}
        <p className="mt-2 text-center text-sm text-gray-400">진행되는 대로 다시 연락드리겠습니다.</p>
      </div>
    </div>
   </>
  );
}

// 고객 ID/PW 폼 제출 후 — 로그인(아이디·비번·주민 확인)까지 기다렸다 마무리 안내. 토큰스코프 폴링.
function ClientCloseScreen({ token, requestId, onRetry, event = false }) {
  const [st, setSt] = useState({ status: null, logged_in: false });
  const [evClosed, setEvClosed] = useState(false);
  useEffect(() => {
    if (!token) return;
    const t = setInterval(async () => {
      const s = await apiGet(`/api/client-status/${token}`, null);
      if (s && s.ok) setSt(s);
    }, 3000);
    return () => clearInterval(t);
  }, [token]);
  const failed = st.status === "failed";
  if (st.logged_in) return <SubmitSuccess requestId={requestId} event={false} />;   // 로그인 확인 후 접수완료 화면(B는 아래서 이미 노출)
  return (
   <>
    {event && PA_EVENT_LIVE() && !evClosed && <EventPopup variant="thanks" onClose={() => setEvClosed(true)} />}
    <div className="mx-auto max-w-md px-4 py-12 text-center">
      {!failed && (
        <>
          <div className="mx-auto mb-4 h-10 w-10 animate-spin rounded-full border-4 border-slate-200 border-t-orange-500" />
          <h2 className="text-lg font-bold text-slate-800">입력 정보를 확인하는 중입니다…</h2>
          <p className="mt-2 text-sm leading-relaxed text-slate-500">홈택스 로그인으로 아이디·비밀번호·주민번호를 확인하고 있어요.<br />잠시만 기다려 주세요. (창을 닫지 말아 주세요)</p>
          {requestId && <p className="mt-3 text-[11px] text-slate-400">접수번호 {requestId}</p>}
        </>
      )}
      {failed && (
        <>
          <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-rose-50 text-2xl">⚠️</div>
          <h2 className="text-lg font-bold text-slate-800">로그인 정보를 다시 확인해 주세요</h2>
          <p className="mt-2 text-sm leading-relaxed text-slate-500">입력하신 <b>아이디·비밀번호 또는 주민번호</b>가 맞지 않는 것 같습니다.<br />정보를 고쳐 다시 입력해 주세요.</p>
          {onRetry && <button onClick={onRetry} className="mt-5 w-full rounded-lg bg-orange-600 py-3 text-sm font-bold text-white hover:bg-orange-700">정보 다시 입력하기</button>}
        </>
      )}
    </div>
   </>
  );
}

// ─────────────────────────────────────────────────────────────
// 입력자 페이지
// ─────────────────────────────────────────────────────────────
function CustomerForm({ prefill = null, startMode = null, fromQuoteId = null, lockMode = false, handoffToken = null }) {
  const [mode, setMode] = useState(startMode); // null=유형선택, "full"=전체, "quote"=단순견적
  const [form, setForm] = useState({
    name: prefill?.name || "",
    phone: prefill?.phone || "",
    hometax_id: prefill?.hometax_id || "",
    hometax_pw: prefill?.hometax_pw || "",
    jumin: prefill?.jumin || "",
    biz_nos: prefill?.biz_nos?.length ? prefill.biz_nos : [""],
    simple_auth: prefill?.simple_auth || false,
    sa_provider: prefill?.sa_provider || "kakao",
    service: prefill?.service || "jongso",
    memo: prefill?.memo || "",
    joint: prefill?.joint || false,
    joint_biz_no: prefill?.joint_biz_no || "",
    joint_rep_name: prefill?.joint_rep_name || "",
    joint_rep_jumin: prefill?.joint_rep_jumin || "",
    joint_share: prefill?.joint_share || "",
    agree: false,
  });
  const [showPw, setShowPw] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [result, setResult] = useState(null);
  const [retry, setRetry] = useState(null);   // 로그인 실패 후 재시도 {token, rid} — 기존요청 덮어쓰기
  const [evPop, setEvPop] = useState("");      // 제출 즉시 이벤트 팝업(A) — 접수 후 B는 성공화면이 담당

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const setBizNo = (idx, v) =>
    setForm((f) => {
      const arr = [...f.biz_nos];
      arr[idx] = v;
      return { ...f, biz_nos: arr };
    });
  const addBizNo = () => setForm((f) => ({ ...f, biz_nos: [...f.biz_nos, ""] }));
  const removeBizNo = (idx) =>
    setForm((f) => ({ ...f, biz_nos: f.biz_nos.filter((_, i) => i !== idx) }));

  const canSubmit =
    form.name &&
    form.phone &&
    form.jumin &&
    form.hometax_id &&
    form.hometax_pw &&
    form.service &&
    form.agree &&
    (!form.joint || (form.joint_biz_no && form.joint_rep_name && form.joint_rep_jumin && form.joint_share)) &&
    !submitting;

  const handleSubmit = async () => {
    setSubmitting(true);
    if (PA_EVENT_LIVE()) setEvPop("entry");   // 보낼 때 = A
    // ★재시도(로그인 실패 후) — 1회용 토큰 소진됐으므로 신규 제출 대신 기존요청 덮어쓰기·재실행.
    if (retry) {
      const rr = await apiPost(`/api/client-retry/${retry.token}`,
        { name: form.name, phone: onlyDigits(form.phone), hometax_id: form.hometax_id, hometax_pw: form.hometax_pw, jumin: onlyDigits(form.jumin) }, null);
      setSubmitting(false);
      if (!rr || !rr.ok) { setEvPop(""); alert("재시도에 실패했습니다. 잠시 후 다시 시도해 주세요."); return; }
      setEvPop(""); setResult({ request_id: retry.rid, client_token: retry.token });
      return;
    }
    const payload = {
      mode: "full",
      name: form.name,
      phone: onlyDigits(form.phone),
      hometax_id: form.hometax_id,
      hometax_pw: form.hometax_pw,
      jumin: onlyDigits(form.jumin),
      service: form.service,
      memo: form.memo || undefined,
      agree: form.agree,
      biz_nos: form.biz_nos.map(onlyDigits).filter(Boolean),
      simple_auth: form.simple_auth,
      sa_provider: form.simple_auth ? form.sa_provider : undefined,
      from_quote_id: fromQuoteId || undefined,
      token: handoffToken || undefined,
      joint: form.joint,
      joint_biz_no: form.joint ? onlyDigits(form.joint_biz_no) : undefined,
      joint_rep_name: form.joint ? form.joint_rep_name : undefined,
      joint_rep_jumin: form.joint ? onlyDigits(form.joint_rep_jumin) : undefined,
      joint_share: form.joint ? form.joint_share : undefined,
    };
    const res = await apiPost("/api/requests", payload, {
      request_id: `REQ-${Date.now().toString().slice(-8)}`,
      status: "pending",
    });
    setEvPop("");   // A 닫고 → 성공화면이 B(접수 후)를 띄움
    setResult(res);
    setSubmitting(false);
  };

  if (result) {
    if (result.client_token) return <ClientCloseScreen token={result.client_token} requestId={result.request_id} event onRetry={() => { setRetry({ token: result.client_token, rid: result.request_id }); setResult(null); }} />;
    return (
      <SuccessScreen
        requestId={result.request_id}
        message="담당 직원이 확인 후 자료를 수집해 드립니다."
        event
        onReset={() => { setResult(null); setMode(null); }}
      />
    );
  }

  // 견적 문의는 별도 페이지
  if (mode === "quote") {
    return <QuoteForm onBack={() => setMode(null)} />;
  }

  // 법인은 별도 페이지
  if (mode === "corp") {
    return <CorpForm onBack={() => setMode(null)} />;
  }

  // 유형 선택 화면
  if (mode === null) {
    return (
      <div className="mx-auto max-w-lg px-4 py-8">
        <div className="mb-6 text-center">
          <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-orange-100">
            <svg className="h-7 w-7 text-orange-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
              <path d="M16 7a4 4 0 11-8 0 4 4 0 018 0z" />
              <path d="M12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
            </svg>
          </div>
          <h2 className="text-2xl font-bold text-slate-800">고객정보 입력 요청</h2>
          <p className="mt-1 text-sm text-slate-500">신청하실 유형을 선택해주세요.</p>
        </div>

        <div className="space-y-3">
          <button
            type="button"
            onClick={() => setMode("full")}
            className="group flex w-full items-start gap-3 rounded-2xl border border-slate-200 bg-white p-5 text-left shadow-sm transition hover:border-orange-300 hover:bg-orange-50/30"
          >
            <span className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-orange-100 text-orange-600">
              <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
                <path d="M9 12h6M9 16h6M9 8h6M5 4h14v16H5z" />
              </svg>
            </span>
            <span>
              <span className="block font-semibold text-slate-800">세무 신고·자료수집 (개인)</span>
              <span className="mt-1 block text-sm leading-relaxed text-slate-500">
                개인·개인사업자의 신고나 자료 수집.
                <br />
                사업자번호·인증·신청 서비스까지 입력합니다.
              </span>
            </span>
          </button>

          <button
            type="button"
            onClick={() => setMode("corp")}
            className="group flex w-full items-start gap-3 rounded-2xl border border-slate-200 bg-white p-5 text-left shadow-sm transition hover:border-orange-300 hover:bg-orange-50/30"
          >
            <span className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-orange-100 text-orange-600">
              <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
                <path d="M5 21V5a1 1 0 011-1h8a1 1 0 011 1v16M9 8h.01M12 8h.01M9 12h.01M12 12h.01M15 21v-8h4v8" />
              </svg>
            </span>
            <span>
              <span className="block font-semibold text-slate-800">법인 신고·자료수집</span>
              <span className="mt-1 block text-sm leading-relaxed text-slate-500">
                법인사업자의 신고나 자료 수집.
                <br />
                법인명·법인등록번호·법인 홈택스 계정을 입력합니다.
              </span>
            </span>
          </button>

          <button
            type="button"
            onClick={() => setMode("quote")}
            className="group flex w-full items-start gap-3 rounded-2xl border border-slate-200 bg-white p-5 text-left shadow-sm transition hover:border-orange-300 hover:bg-orange-50/30"
          >
            <span className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-orange-100 text-orange-600">
              <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
                <path d="M9 7h6M9 11h6M9 15h4M6 3h12v18l-6-3-6 3z" />
              </svg>
            </span>
            <span>
              <span className="block font-semibold text-slate-800">단순 견적 문의</span>
              <span className="mt-1 block text-sm leading-relaxed text-slate-500">
                먼저 견적만 받아보실 경우 선택해주세요.
                <br />
                최소한의 정보만 입력합니다.
              </span>
            </span>
          </button>
        </div>
      </div>
    );
  }

  return (
    <div className="mx-auto max-w-2xl px-4 py-8">
      {evPop && <EventPopup variant="entry" onClose={() => setEvPop("")} />}
      {!lockMode && (
        <button
          type="button"
          onClick={() => setMode(null)}
          className="mb-3 flex items-center gap-1 text-sm text-slate-400 hover:text-slate-600"
        >
          <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
            <path d="M15 19l-7-7 7-7" />
          </svg>
          유형 다시 선택
        </button>
      )}
      <div className="mb-6 text-center">
        <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-orange-100">
          <svg className="h-7 w-7 text-orange-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
            <path d="M16 7a4 4 0 11-8 0 4 4 0 018 0z" />
            <path d="M12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
          </svg>
        </div>
        <h2 className="text-2xl font-bold text-slate-800">고객정보 입력 요청</h2>
        <p className="mt-1 text-sm text-slate-500">
          {fromQuoteId
            ? "견적 시 받은 정보는 미리 채워 두었습니다. 나머지 정보만 입력해주세요."
            : "정확한 세무 서비스 제공을 위해 정보를 입력해주세요."}
        </p>
      </div>

      {fromQuoteId && (
        <div className="mb-4 flex items-center gap-2 rounded-lg bg-emerald-50 p-3 text-xs text-emerald-700 ring-1 ring-emerald-100">
          <svg className="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
            <path d="M5 13l4 4L19 7" />
          </svg>
          이전 견적 문의({fromQuoteId})에서 이어집니다. 채워진 항목은 그대로 두셔도 됩니다.
        </div>
      )}

      <div className="space-y-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:p-6">
        {/* 신청인 정보 */}
        <CellGroup title="신청인 정보">
          <Cell label="이름" required>
            <input className={cellInput} value={form.name} onChange={(e) => set("name", e.target.value)} placeholder="홍길동" />
          </Cell>
          <Cell label="휴대폰번호" required>
            <input className={cellInput} value={form.phone} onChange={(e) => set("phone", formatPhone(e.target.value))} inputMode="numeric" placeholder="010-0000-0000" />
          </Cell>
          <Cell label="주민등록번호" required>
            <input
              className={cellInput}
              value={form.jumin}
              onChange={(e) => set("jumin", formatJumin(e.target.value))}
              inputMode="numeric"
              placeholder="000000-0000000"
            />
          </Cell>
        </CellGroup>

        {/* 홈택스 계정 */}
        <CellGroup title="홈택스 계정">
          <Cell label="아이디" required>
            <input className={cellInput} value={form.hometax_id} onChange={(e) => set("hometax_id", e.target.value)} autoComplete="off" placeholder="hometax_id" />
          </Cell>
          <Cell label="비밀번호" required>
            <div className="flex items-center gap-2">
              <input
                className={cellInput}
                type={showPw ? "text" : "password"}
                value={form.hometax_pw}
                onChange={(e) => set("hometax_pw", e.target.value)}
                autoComplete="new-password"
                placeholder="••••••••"
              />
              <button
                type="button"
                onClick={() => setShowPw((s) => !s)}
                className="shrink-0 rounded px-2 py-0.5 text-xs text-slate-400 hover:bg-slate-100"
              >
                {showPw ? "가림" : "표시"}
              </button>
            </div>
          </Cell>
        </CellGroup>

        {/* 사업장 */}
        <CellGroup title="사업장">
          {form.biz_nos.map((b, idx) => (
            <Cell key={idx} label={form.biz_nos.length > 1 ? `사업자등록번호 ${idx + 1}` : "사업자등록번호 (선택)"}>
              <div className="flex items-center gap-2">
                <input
                  className={cellInput}
                  value={b}
                  onChange={(e) => setBizNo(idx, formatBizNo(e.target.value))}
                  inputMode="numeric"
                  placeholder="000-00-00000"
                />
                {form.biz_nos.length > 1 && (
                  <button
                    type="button"
                    onClick={() => removeBizNo(idx)}
                    className="shrink-0 rounded px-2 py-0.5 text-xs text-slate-400 hover:bg-slate-100"
                  >
                    삭제
                  </button>
                )}
              </div>
            </Cell>
          ))}
          <button
            type="button"
            onClick={addBizNo}
            className="block w-full bg-slate-100 px-4 py-2.5 text-left text-sm font-medium text-orange-600 transition hover:bg-slate-50"
          >
            + 사업자 추가
          </button>
        </CellGroup>
        <p className="-mt-4 px-1 text-xs text-slate-400">프리랜서·일반 개인은 사업자등록번호를 비워 두세요.</p>

        {/* 공동사업자 */}
        <div>
          <button
            type="button"
            onClick={() => set("joint", !form.joint)}
            className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
              form.joint ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
            }`}
          >
            <span className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${form.joint ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"}`}>
              {form.joint && (
                <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                </svg>
              )}
            </span>
            <span>
              <span className="block text-sm font-medium text-slate-800">공동사업자입니다</span>
              <span className="block text-xs text-slate-400">둘 이상이 함께 운영하는 사업장이면 선택하세요.</span>
            </span>
          </button>
          {form.joint && (
            <div className="mt-3">
              <CellGroup title="공동사업장 정보">
                <Cell label="공동사업장 사업자등록번호" required>
                  <input className={cellInput} value={form.joint_biz_no} onChange={(e) => set("joint_biz_no", formatBizNo(e.target.value))} inputMode="numeric" placeholder="000-00-00000" />
                </Cell>
                <Cell label="공동사업장 대표자 성함" required>
                  <input className={cellInput} value={form.joint_rep_name} onChange={(e) => set("joint_rep_name", e.target.value)} placeholder="대표자 이름" />
                </Cell>
                <Cell label="공동사업장 대표자 주민등록번호" required>
                  <input className={cellInput} value={form.joint_rep_jumin} onChange={(e) => set("joint_rep_jumin", formatJumin(e.target.value))} inputMode="numeric" placeholder="000000-0000000" />
                </Cell>
                <Cell label="본인 손익배분율 (%)" required>
                  <input
                    className={cellInput}
                    value={form.joint_share}
                    onChange={(e) => set("joint_share", e.target.value.replace(/[^0-9.]/g, "").slice(0, 5))}
                    inputMode="decimal"
                    placeholder="예: 50"
                  />
                </Cell>
              </CellGroup>
              <p className="mt-1 px-1 text-[11px] text-slate-400">대표자 성함·주민번호는 공동사업장 대표 기준으로 적어주세요.</p>
            </div>
          )}
        </div>

        {/* 인증 — ID/PW 기본, 간편인증 가능 여부 선택 */}
        <div>
          <span className="mb-1.5 block text-sm font-medium text-slate-700">인증 방식</span>
          <p className="mb-2 text-xs text-slate-400">기본은 홈택스 ID/PW 로그인입니다. 현재 간편인증이 가능하면 아래에서 선택하세요.</p>
          <button
            type="button"
            onClick={() => set("simple_auth", !form.simple_auth)}
            className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
              form.simple_auth ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
            }`}
          >
            <span
              className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${
                form.simple_auth ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"
              }`}
            >
              {form.simple_auth && (
                <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                </svg>
              )}
            </span>
            <span>
              <span className="block text-sm font-medium text-slate-800">지금 간편인증이 가능합니다</span>
              <span className="block text-xs text-slate-400">카카오톡·PASS·토스 등으로 본인인증 승인이 즉시 가능한 경우 선택하세요.</span>
            </span>
          </button>
          {form.simple_auth && (
            <div className="mt-3">
              <Field label="간편인증 수단">
                <select className={inputCls} value={form.sa_provider} onChange={(e) => set("sa_provider", e.target.value)}>
                  {SA_PROVIDERS.map((p) => (
                    <option key={p.key} value={p.key}>
                      {p.label}
                    </option>
                  ))}
                </select>
              </Field>
            </div>
          )}
        </div>

        {/* 신청 서비스 (하나만 선택) */}
        <div>
          <span className="mb-2 block text-sm font-medium text-slate-700">
            신청 서비스 <span className="text-rose-500">*</span>
          </span>
          <div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
            {SERVICES.map((s) => {
              const on = form.service === s.key;
              return (
                <button
                  key={s.key}
                  type="button"
                  onClick={() => set("service", s.key)}
                  className={`rounded-lg border p-3 text-left transition ${
                    on ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
                  }`}
                >
                  <span className="flex items-center gap-1.5">
                    <span className="text-sm font-semibold text-slate-800">{s.label}</span>
                    {!s.ready && (
                      <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-500">준비중</span>
                    )}
                  </span>
                  <span className="mt-0.5 block text-xs text-slate-400">{s.desc}</span>
                </button>
              );
            })}
          </div>
        </div>

        {/* 메모 */}
        <div>
          <span className="mb-1.5 flex items-center gap-1.5 text-sm font-medium text-slate-700">
            <svg className="h-4 w-4 text-orange-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
              <path d="M8 10h8M8 14h5M21 12a8 8 0 01-8 8H7l-4 3 1-5a8 8 0 1117-6z" />
            </svg>
            메모
            <span className="font-normal text-slate-400">(선택)</span>
          </span>
          <div className="relative rounded-xl bg-orange-50 p-3 ring-1 ring-orange-100">
            <textarea
              rows={3}
              value={form.memo}
              onChange={(e) => set("memo", e.target.value)}
              placeholder="요청사항이나 특이사항을 자유롭게 적어주세요."
              className="w-full resize-none border-0 bg-transparent text-sm text-slate-800 placeholder:text-orange-300 focus:outline-none focus:ring-0"
            />
          </div>
        </div>

        {/* 동의 */}
        <button
          type="button"
          onClick={() => set("agree", !form.agree)}
          className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
            form.agree ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
          }`}
        >
          <span
            className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${
              form.agree ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"
            }`}
          >
            {form.agree && (
              <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
              </svg>
            )}
          </span>
          <span className="text-sm text-slate-700">
            <span className="font-medium">[필수]</span> 본인은 위 정보를 바탕으로 세무전문가가 홈택스에 접속하여 세무 신고·자료 수집을 대행하는 것에 동의합니다.
          </span>
        </button>

        <button
          onClick={handleSubmit}
          disabled={!canSubmit}
          className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:cursor-not-allowed disabled:bg-slate-300"
        >
          {submitting ? "접수 중…" : "요청 제출"}
        </button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 단순 견적 문의 페이지 (독립)
// ─────────────────────────────────────────────────────────────
function QuoteForm({ onBack = null, handoffToken = null }) {
  const [form, setForm] = useState({
    name: "",
    jumin: "",
    hometax_id: "",
    hometax_pw: "",
    service: "jongso",
    memo: "",
    joint: false,
    joint_biz_no: "",
    joint_share: "",
    agree: false,
  });
  const [showPw, setShowPw] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [result, setResult] = useState(null);
  const [evPop, setEvPop] = useState("");   // 제출 즉시 이벤트 팝업(A)

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const [retry, setRetry] = useState(null);   // 로그인 실패 후 재시도 {token, rid} — 기존요청 덮어쓰기

  const canSubmit =
    form.name && form.jumin && form.hometax_id && form.hometax_pw && form.service && form.agree &&
    (!form.joint || (form.joint_biz_no && form.joint_share)) && !submitting;

  const handleSubmit = async () => {
    setSubmitting(true);
    if (PA_EVENT_LIVE()) setEvPop("entry");   // 보낼 때 = A
    // ★재시도(로그인 실패 후) — 1회용 토큰 소진됐으므로 신규 제출 대신 기존요청 덮어쓰기·재실행.
    if (retry) {
      const rr = await apiPost(`/api/client-retry/${retry.token}`,
        { name: form.name, hometax_id: form.hometax_id, hometax_pw: form.hometax_pw, jumin: onlyDigits(form.jumin) }, null);
      setSubmitting(false);
      if (!rr || !rr.ok) { setEvPop(""); alert("재시도에 실패했습니다. 잠시 후 다시 시도해 주세요."); return; }
      setEvPop(""); setResult({ request_id: retry.rid, client_token: retry.token });
      return;
    }
    const payload = {
      mode: "quote",
      name: form.name,
      jumin: onlyDigits(form.jumin),
      hometax_id: form.hometax_id,
      hometax_pw: form.hometax_pw,
      service: form.service,
      memo: form.memo || undefined,
      agree: form.agree,
      token: handoffToken || undefined,
      joint: form.joint,
      joint_biz_no: form.joint ? onlyDigits(form.joint_biz_no) : undefined,
      joint_share: form.joint ? form.joint_share : undefined,
    };
    const res = await apiPost("/api/requests", payload, {
      request_id: `REQ-${Date.now().toString().slice(-8)}`,
      status: "pending",
    });
    setEvPop(""); setResult(res);
    setSubmitting(false);
  };

  if (result) {
    if (result.client_token) return <ClientCloseScreen token={result.client_token} requestId={result.request_id} event onRetry={() => { setRetry({ token: result.client_token, rid: result.request_id }); setResult(null); }} />;
    return (
      <SuccessScreen
        requestId={result.request_id}
        message="담당 직원이 확인 후 견적을 안내해 드립니다."
        event
        onReset={onBack}
      />
    );
  }

  return (
    <div className="mx-auto max-w-2xl px-4 py-8">
      {evPop && <EventPopup variant="entry" onClose={() => setEvPop("")} />}
      {onBack && (
        <button
          type="button"
          onClick={onBack}
          className="mb-3 flex items-center gap-1 text-sm text-slate-400 hover:text-slate-600"
        >
          <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
            <path d="M15 19l-7-7 7-7" />
          </svg>
          유형 다시 선택
        </button>
      )}
      <div className="mb-6 text-center">
        <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-orange-100">
          <svg className="h-7 w-7 text-orange-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
            <path d="M9 7h6M9 11h6M9 15h4M6 3h12v18l-6-3-6 3z" />
          </svg>
        </div>
        <h2 className="text-2xl font-bold text-slate-800">단순 견적 문의</h2>
        <p className="mt-1 text-sm text-slate-500">견적 산출에 필요한 최소 정보만 입력해주세요.</p>
      </div>

      <div className="space-y-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:p-6">
        {/* 신청인 정보 */}
        <CellGroup title="신청인 정보">
          <Cell label="이름" required>
            <input className={cellInput} value={form.name} onChange={(e) => set("name", e.target.value)} placeholder="홍길동" />
          </Cell>
          <Cell label="주민등록번호 (앞 7자리)" required>
            <input
              className={cellInput}
              value={form.jumin}
              onChange={(e) => set("jumin", formatJuminShort(e.target.value))}
              inputMode="numeric"
              placeholder="000000-0"
            />
          </Cell>
        </CellGroup>

        {/* 홈택스 계정 */}
        <CellGroup title="홈택스 계정">
          <Cell label="아이디" required>
            <input className={cellInput} value={form.hometax_id} onChange={(e) => set("hometax_id", e.target.value)} autoComplete="off" placeholder="hometax_id" />
          </Cell>
          <Cell label="비밀번호" required>
            <div className="flex items-center gap-2">
              <input
                className={cellInput}
                type={showPw ? "text" : "password"}
                value={form.hometax_pw}
                onChange={(e) => set("hometax_pw", e.target.value)}
                autoComplete="new-password"
                placeholder="••••••••"
              />
              <button
                type="button"
                onClick={() => setShowPw((s) => !s)}
                className="shrink-0 rounded px-2 py-0.5 text-xs text-slate-400 hover:bg-slate-100"
              >
                {showPw ? "가림" : "표시"}
              </button>
            </div>
          </Cell>
        </CellGroup>

        {/* 공동사업자 */}
        <div>
          <button
            type="button"
            onClick={() => set("joint", !form.joint)}
            className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
              form.joint ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
            }`}
          >
            <span className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${form.joint ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"}`}>
              {form.joint && (
                <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                </svg>
              )}
            </span>
            <span>
              <span className="block text-sm font-medium text-slate-800">공동사업자입니다</span>
              <span className="block text-xs text-slate-400">둘 이상이 함께 운영하는 사업장이면 선택하세요.</span>
            </span>
          </button>
          {form.joint && (
            <div className="mt-3">
              <CellGroup title="공동사업장 정보">
                <Cell label="공동사업장 사업자등록번호" required>
                  <input className={cellInput} value={form.joint_biz_no} onChange={(e) => set("joint_biz_no", formatBizNo(e.target.value))} inputMode="numeric" placeholder="000-00-00000" />
                </Cell>
                <Cell label="본인 손익배분율 (%)" required>
                  <input
                    className={cellInput}
                    value={form.joint_share}
                    onChange={(e) => set("joint_share", e.target.value.replace(/[^0-9.]/g, "").slice(0, 5))}
                    inputMode="decimal"
                    placeholder="예: 50"
                  />
                </Cell>
              </CellGroup>
            </div>
          )}
        </div>

        {/* 신청 서비스 */}
        <div>
          <span className="mb-2 block text-sm font-medium text-slate-700">
            견적 항목 <span className="text-rose-500">*</span>
          </span>
          <div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
            {SERVICES.map((s) => {
              const on = form.service === s.key;
              return (
                <button
                  key={s.key}
                  type="button"
                  onClick={() => set("service", s.key)}
                  className={`rounded-lg border p-3 text-left transition ${
                    on ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
                  }`}
                >
                  <span className="flex items-center gap-1.5">
                    <span className="text-sm font-semibold text-slate-800">{s.label}</span>
                    {!s.ready && (
                      <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-500">준비중</span>
                    )}
                  </span>
                  <span className="mt-0.5 block text-xs text-slate-400">{s.desc}</span>
                </button>
              );
            })}
          </div>
        </div>

        {/* 메모 */}
        <div>
          <span className="mb-1.5 flex items-center gap-1.5 text-sm font-medium text-slate-700">
            <svg className="h-4 w-4 text-orange-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
              <path d="M8 10h8M8 14h5M21 12a8 8 0 01-8 8H7l-4 3 1-5a8 8 0 1117-6z" />
            </svg>
            메모
            <span className="font-normal text-slate-400">(선택)</span>
          </span>
          <div className="rounded-xl bg-orange-50 p-3 ring-1 ring-orange-100">
            <textarea
              rows={3}
              value={form.memo}
              onChange={(e) => set("memo", e.target.value)}
              placeholder="요청사항이나 특이사항을 자유롭게 적어주세요."
              className="w-full resize-none border-0 bg-transparent text-sm text-slate-800 placeholder:text-orange-300 focus:outline-none focus:ring-0"
            />
          </div>
        </div>

        {/* 동의 */}
        <button
          type="button"
          onClick={() => set("agree", !form.agree)}
          className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
            form.agree ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
          }`}
        >
          <span
            className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${
              form.agree ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"
            }`}
          >
            {form.agree && (
              <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
              </svg>
            )}
          </span>
          <span className="text-sm text-slate-700">
            <span className="font-medium">[필수]</span> 본인은 견적 산출을 위해 위 정보를 세무전문가에게 제공하는 것에 동의합니다.
          </span>
        </button>

        <button
          onClick={handleSubmit}
          disabled={!canSubmit}
          className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:cursor-not-allowed disabled:bg-slate-300"
        >
          {submitting ? "접수 중…" : "견적 문의 제출"}
        </button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 간편인증 고객 입력 폼 (1회용 링크 kind=sa) — ID/PW 없이 휴대폰 인증
// ─────────────────────────────────────────────────────────────
function SAForm({ handoffToken = null, prefill = null }) {
  const PROVIDERS = ["카카오톡", "통신사PASS", "토스"];
  const TELCOS = ["SKT", "KT", "LGU+"];
  const [form, setForm] = useState({
    name: prefill?.name || "",
    jumin: "",
    sa_provider: "카카오톡",
    telco: "SKT",
    sa_phone: prefill?.phone || "",
    service: prefill?.service || "jongso",
    memo: "",
    agree: false,
  });
  const [submitting, setSubmitting] = useState(false);
  const [result, setResult] = useState(null);
  const [saStatus, setSaStatus] = useState(null);   // 고객 폰에서 진행상태 폴링
  const [saLoggedIn, setSaLoggedIn] = useState(false);
  const [completing, setCompleting] = useState(false);
  const [evPop, setEvPop] = useState("");           // 제출 즉시 이벤트 팝업(A)
  const [saEvClosed, setSaEvClosed] = useState(false);   // 접수 후 B 팝업 닫음 여부
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const ctok = result && result.sa_complete_token;
  useEffect(() => {
    if (!ctok) return;
    const t = setInterval(async () => {
      const s = await apiGet(`/api/sa-status/${ctok}`, null);
      if (s && s.ok) { setSaStatus(s.status); setSaLoggedIn(!!s.logged_in); }
    }, 3000);
    return () => clearInterval(t);
  }, [ctok]);

  const canSubmit =
    form.name && form.jumin && form.sa_phone && form.agree &&
    (form.sa_provider !== "통신사PASS" || form.telco) && !submitting;

  const handleSubmit = async () => {
    setSubmitting(true);
    if (PA_EVENT_LIVE()) setEvPop("entry");   // 보낼 때 = A
    const payload = {
      mode: "quote",
      service: form.service,
      simple_auth: true,
      name: form.name,
      jumin: onlyDigits(form.jumin),
      sa_provider: form.sa_provider,
      telco: form.sa_provider === "통신사PASS" ? form.telco : undefined,
      sa_phone: onlyDigits(form.sa_phone),
      memo: form.memo || undefined,
      agree: form.agree,
      token: handoffToken || undefined,
    };
    const res = await apiPost("/api/requests", payload, {
      request_id: `REQ-${Date.now().toString().slice(-8)}`,
      status: "pending",
    });
    setEvPop("");   // A 닫고 → 접수 후 B는 아래 화면들이 띄움
    setResult(res);
    setSubmitting(false);
  };

  // 접수 후(결과 화면) 공용 B 팝업 — 닫기 전까지 1회 노출
  const saThanks = result && PA_EVENT_LIVE() && !saEvClosed
    ? <EventPopup variant="thanks" onClose={() => setSaEvClosed(true)} />
    : null;

  if (result) {
    if (result.sa_complete_token) {
      const failed = saStatus === "failed";
      const needsAuth = saStatus === "needs_auth" || saStatus == null;   // 인증 대기(완료 전·또는 재대기)
      // ★실제 홈택스 로그인 확인된 뒤에만 마무리 인사. 완료 눌러 로그인 진행중이면 '확인 중'.
      if (saLoggedIn || saStatus === "done") {
        return <>{saThanks}<SubmitSuccess requestId={result.request_id} /></>;
      }
      if (!needsAuth && !failed) {
        return (
         <>{saThanks}
          <div className="mx-auto max-w-md px-4 py-12 text-center">
            <div className="mx-auto mb-4 h-10 w-10 animate-spin rounded-full border-4 border-slate-200 border-t-orange-500" />
            <h2 className="text-lg font-bold text-slate-800">간편인증 확인 중입니다…</h2>
            <p className="mt-2 text-sm leading-relaxed text-slate-500">홈택스 로그인을 진행하고 있어요.<br />잠시만 기다려 주세요. (창을 닫지 말아 주세요)</p>
          </div>
         </>
        );
      }
      return (
       <>{saThanks}
        <div className="mx-auto max-w-md px-4 py-10 text-center">
          <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-orange-100 text-2xl">📲</div>
          <h2 className="text-xl font-bold text-slate-800">간편인증 요청을 보냈습니다</h2>
          <p className="mt-2 text-sm leading-relaxed text-slate-500">휴대폰 알림(카카오톡·PASS·토스)을 <b>승인</b>한 뒤,<br />아래 <b>‘간편인증 완료’</b>를 눌러주세요.</p>
          <p className="mt-1 text-[11px] text-slate-400">접수번호 {result.request_id}</p>
          {!failed && (
            <button
              disabled={completing}
              onClick={async () => {
                setCompleting(true);
                const r = await apiPost(`/api/sa-complete/${result.sa_complete_token}`, {}, null);
                if (!r || !r.ok) alert("아직 완료할 수 없습니다.\n휴대폰에서 인증을 승인한 뒤 잠시 후 다시 눌러주세요.");
                setCompleting(false);
              }}
              className="mt-5 w-full rounded-lg bg-orange-600 py-3 text-sm font-bold text-white transition hover:bg-orange-700 disabled:bg-slate-300">
              {completing ? "처리 중…" : "✅ 간편인증 완료"}
            </button>
          )}
          {failed && <p className="mt-5 rounded-lg bg-rose-50 p-3 text-sm font-medium text-rose-700">처리 중 문제가 발생했습니다. 담당자에게 문의해 주세요.</p>}
          <p className="mt-4 text-[11px] leading-relaxed text-slate-400">창을 닫아도 괜찮습니다 — 담당 영업사원이 대신 완료할 수 있습니다.</p>
        </div>
       </>
      );
    }
    return (
     <>{saThanks}
      <SuccessScreen
        requestId={result.request_id}
        message="담당 직원이 간편인증 요청을 보내드립니다. 휴대폰 알림(카카오톡·PASS·토스)을 확인해 승인해 주세요."
        onReset={null}
      />
     </>
    );
  }

  return (
    <div className="mx-auto max-w-2xl px-4 py-8">
      {evPop && <EventPopup variant="entry" onClose={() => setEvPop("")} />}
      <div className="mb-6 text-center">
        <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-orange-100">
          <svg className="h-7 w-7 text-orange-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 11c0-1.66 1.34-3 3-3s3 1.34 3 3M5 11h14a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2z" />
          </svg>
        </div>
        <h2 className="text-2xl font-bold text-slate-800">간편인증 신청</h2>
        <p className="mt-1 text-sm text-slate-500">휴대폰 간편인증으로 본인확인을 진행합니다. <b>비밀번호는 입력하지 않습니다.</b></p>
      </div>

      <div className="space-y-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:p-6">
        <CellGroup title="신청인 정보">
          <Cell label="이름" required>
            <input className={cellInput} value={form.name} onChange={(e) => set("name", e.target.value)} placeholder="홍길동" />
          </Cell>
          <Cell label="주민등록번호 (앞 7자리)" required>
            <input className={cellInput} value={form.jumin} onChange={(e) => set("jumin", formatJuminShort(e.target.value))} inputMode="numeric" placeholder="000000-0" />
          </Cell>
        </CellGroup>

        <CellGroup title="간편인증 수단">
          <Cell label="인증 수단" required>
            <select className={cellInput} value={form.sa_provider} onChange={(e) => set("sa_provider", e.target.value)}>
              {PROVIDERS.map((p) => <option key={p} value={p}>{p}</option>)}
            </select>
          </Cell>
          {form.sa_provider === "통신사PASS" && (
            <Cell label="통신사" required>
              <select className={cellInput} value={form.telco} onChange={(e) => set("telco", e.target.value)}>
                {TELCOS.map((t) => <option key={t} value={t}>{t}</option>)}
              </select>
            </Cell>
          )}
          <Cell label="휴대폰 번호 (인증 알림 받을 번호)" required>
            <input className={cellInput} value={form.sa_phone} onChange={(e) => set("sa_phone", formatPhone(e.target.value))} inputMode="numeric" placeholder="010-0000-0000" />
          </Cell>
        </CellGroup>

        {/* 신청 서비스 */}
        <div>
          <span className="mb-2 block text-sm font-medium text-slate-700">견적 항목 <span className="text-rose-500">*</span></span>
          <div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
            {SERVICES.map((s) => {
              const on = form.service === s.key;
              return (
                <button key={s.key} type="button" onClick={() => set("service", s.key)}
                  className={`rounded-lg border p-3 text-left transition ${on ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"}`}>
                  <span className="flex items-center gap-1.5">
                    <span className="text-sm font-semibold text-slate-800">{s.label}</span>
                    {!s.ready && <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-500">준비중</span>}
                  </span>
                  <span className="mt-0.5 block text-xs text-slate-400">{s.desc}</span>
                </button>
              );
            })}
          </div>
        </div>

        {/* 메모 */}
        <div>
          <span className="mb-1.5 block text-sm font-medium text-slate-700">메모 <span className="font-normal text-slate-400">(선택)</span></span>
          <div className="rounded-xl bg-orange-50 p-3 ring-1 ring-orange-100">
            <textarea rows={3} value={form.memo} onChange={(e) => set("memo", e.target.value)}
              placeholder="요청사항이나 특이사항을 자유롭게 적어주세요."
              className="w-full resize-none border-0 bg-transparent text-sm text-slate-800 placeholder:text-orange-300 focus:outline-none focus:ring-0" />
          </div>
        </div>

        {/* 동의 */}
        <button type="button" onClick={() => set("agree", !form.agree)}
          className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${form.agree ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"}`}>
          <span className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${form.agree ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"}`}>
            {form.agree && (
              <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
              </svg>
            )}
          </span>
          <span className="text-sm text-slate-700"><span className="font-medium">[필수]</span> 본인은 견적 산출을 위해 위 정보를 세무전문가에게 제공하는 것에 동의합니다.</span>
        </button>

        <button onClick={handleSubmit} disabled={!canSubmit}
          className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:cursor-not-allowed disabled:bg-slate-300">
          {submitting ? "접수 중…" : "간편인증 신청"}
        </button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 법인 신고·자료수집 페이지 (독립)
// ─────────────────────────────────────────────────────────────
function CorpForm({ onBack = null, handoffToken = null }) {
  const [form, setForm] = useState({
    corp_name: "",
    corp_no: "",
    biz_no: "",
    corp_hometax_id: "",
    corp_hometax_pw: "",
    ceo_name: "",
    ceo_jumin: "",
    ceo_phone: "",
    mgr_name: "",
    mgr_phone: "",
    use_personal: false,
    personal_hometax_id: "",
    personal_hometax_pw: "",
    personal_phone: "",
    service: "corp",
    memo: "",
    agree: false,
  });
  const [showCorpPw, setShowCorpPw] = useState(false);
  const [showPersPw, setShowPersPw] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [result, setResult] = useState(null);
  const [evPop, setEvPop] = useState("");   // 제출 즉시 이벤트 팝업(A)

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const canSubmit =
    form.corp_name &&
    form.corp_no &&
    form.biz_no &&
    form.corp_hometax_id &&
    form.corp_hometax_pw &&
    form.ceo_jumin &&
    form.service &&
    form.agree &&
    !submitting;

  const handleSubmit = async () => {
    setSubmitting(true);
    if (PA_EVENT_LIVE()) setEvPop("entry");   // 보낼 때 = A
    const payload = {
      mode: "corp",
      corp_name: form.corp_name,
      corp_no: onlyDigits(form.corp_no),
      biz_no: onlyDigits(form.biz_no),
      corp_hometax_id: form.corp_hometax_id,
      corp_hometax_pw: form.corp_hometax_pw,
      ceo_name: form.ceo_name || undefined,
      ceo_jumin: onlyDigits(form.ceo_jumin),
      mgr_name: form.mgr_name || undefined,
      mgr_phone: form.mgr_phone ? onlyDigits(form.mgr_phone) : undefined,
      personal_hometax_id: form.use_personal ? form.personal_hometax_id : undefined,
      personal_hometax_pw: form.use_personal ? form.personal_hometax_pw : undefined,
      personal_phone: form.use_personal && form.personal_phone ? onlyDigits(form.personal_phone) : undefined,
      service: form.service,
      memo: form.memo || undefined,
      agree: form.agree,
      token: handoffToken || undefined,
    };
    const res = await apiPost("/api/requests", payload, {
      request_id: `REQ-${Date.now().toString().slice(-8)}`,
      status: "pending",
    });
    setEvPop(""); setResult(res);
    setSubmitting(false);
  };

  if (result) {
    return (
      <SuccessScreen
        requestId={result.request_id}
        message="담당 직원이 확인 후 자료를 수집해 드립니다."
        event
        onReset={onBack}
      />
    );
  }

  return (
    <div className="mx-auto max-w-2xl px-4 py-8">
      {evPop && <EventPopup variant="entry" onClose={() => setEvPop("")} />}
      {onBack && (
        <button
          type="button"
          onClick={onBack}
          className="mb-3 flex items-center gap-1 text-sm text-slate-400 hover:text-slate-600"
        >
          <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
            <path d="M15 19l-7-7 7-7" />
          </svg>
          유형 다시 선택
        </button>
      )}
      <div className="mb-6 text-center">
        <div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-orange-100">
          <svg className="h-7 w-7 text-orange-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
            <path d="M5 21V5a1 1 0 011-1h8a1 1 0 011 1v16M9 8h.01M12 8h.01M9 12h.01M12 12h.01M15 21v-8h4v8" />
          </svg>
        </div>
        <h2 className="text-2xl font-bold text-slate-800">법인 정보 입력</h2>
        <p className="mt-1 text-sm text-slate-500">법인 세무 서비스 제공을 위해 정보를 입력해주세요.</p>
      </div>

      <div className="space-y-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:p-6">
        {/* 법인 정보 */}
        <CellGroup title="법인 정보">
          <Cell label="법인명" required>
            <input className={cellInput} value={form.corp_name} onChange={(e) => set("corp_name", e.target.value)} placeholder="주식회사 OOO" />
          </Cell>
          <Cell label="법인등록번호" required>
            <input className={cellInput} value={form.corp_no} onChange={(e) => set("corp_no", formatCorpNo(e.target.value))} inputMode="numeric" placeholder="000000-0000000" />
          </Cell>
          <Cell label="사업자등록번호" required>
            <input className={cellInput} value={form.biz_no} onChange={(e) => set("biz_no", formatBizNo(e.target.value))} inputMode="numeric" placeholder="000-00-00000" />
          </Cell>
        </CellGroup>

        {/* 법인 홈택스 계정 */}
        <CellGroup title="법인 홈택스 계정">
          <Cell label="아이디" required>
            <input className={cellInput} value={form.corp_hometax_id} onChange={(e) => set("corp_hometax_id", e.target.value)} autoComplete="off" placeholder="법인 홈택스 아이디" />
          </Cell>
          <Cell label="비밀번호" required>
            <div className="flex items-center gap-2">
              <input
                className={cellInput}
                type={showCorpPw ? "text" : "password"}
                value={form.corp_hometax_pw}
                onChange={(e) => set("corp_hometax_pw", e.target.value)}
                autoComplete="new-password"
                placeholder="••••••••"
              />
              <button type="button" onClick={() => setShowCorpPw((s) => !s)} className="shrink-0 rounded px-2 py-0.5 text-xs text-slate-400 hover:bg-slate-100">
                {showCorpPw ? "가림" : "표시"}
              </button>
            </div>
          </Cell>
        </CellGroup>

        {/* 대표자 정보 */}
        <CellGroup title="대표자 정보">
          <Cell label="대표자 이름">
            <input className={cellInput} value={form.ceo_name} onChange={(e) => set("ceo_name", e.target.value)} placeholder="홍길동" />
          </Cell>
          <Cell label="대표자 주민등록번호" required>
            <input className={cellInput} value={form.ceo_jumin} onChange={(e) => set("ceo_jumin", formatJumin(e.target.value))} inputMode="numeric" placeholder="000000-0000000" />
          </Cell>
        </CellGroup>
        <p className="-mt-4 px-1 text-xs text-slate-400">
          공동대표·각자대표인 경우, <span className="font-medium text-slate-500">홈택스 로그인에 실제 사용하는 대표자</span>의 주민등록번호를 적어주세요.
        </p>

        {/* 담당자 정보 (선택) */}
        <CellGroup title="담당자 정보 (선택)">
          <Cell label="담당자 이름">
            <input className={cellInput} value={form.mgr_name} onChange={(e) => set("mgr_name", e.target.value)} placeholder="경리·담당 직원 등" />
          </Cell>
          <Cell label="담당자 연락처">
            <input className={cellInput} value={form.mgr_phone} onChange={(e) => set("mgr_phone", formatPhone(e.target.value))} inputMode="numeric" placeholder="010-0000-0000" />
          </Cell>
        </CellGroup>
        <p className="-mt-4 px-1 text-xs text-slate-400">대표자와 연락 담당자가 다르면 적어주세요.</p>

        {/* 개인 홈택스 계정 (선택) */}
        <div>
          <button
            type="button"
            onClick={() => set("use_personal", !form.use_personal)}
            className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
              form.use_personal ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
            }`}
          >
            <span className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${form.use_personal ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"}`}>
              {form.use_personal && (
                <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                </svg>
              )}
            </span>
            <span>
              <span className="block text-sm font-medium text-slate-800">대표자 개인 홈택스 계정도 함께 제공</span>
              <span className="block text-xs text-slate-400">대표자 개인 자료(연말정산 등)도 필요하면 선택하세요. (선택)</span>
            </span>
          </button>
          {form.use_personal && (
            <div className="mt-3">
              <CellGroup title="개인 홈택스 계정 (선택)">
                <Cell label="아이디">
                  <input className={cellInput} value={form.personal_hometax_id} onChange={(e) => set("personal_hometax_id", e.target.value)} autoComplete="off" placeholder="개인 홈택스 아이디" />
                </Cell>
                <Cell label="비밀번호">
                  <div className="flex items-center gap-2">
                    <input
                      className={cellInput}
                      type={showPersPw ? "text" : "password"}
                      value={form.personal_hometax_pw}
                      onChange={(e) => set("personal_hometax_pw", e.target.value)}
                      autoComplete="new-password"
                      placeholder="••••••••"
                    />
                    <button type="button" onClick={() => setShowPersPw((s) => !s)} className="shrink-0 rounded px-2 py-0.5 text-xs text-slate-400 hover:bg-slate-100">
                      {showPersPw ? "가림" : "표시"}
                    </button>
                  </div>
                </Cell>
                <Cell label="전화번호">
                  <input className={cellInput} value={form.personal_phone} onChange={(e) => set("personal_phone", formatPhone(e.target.value))} inputMode="numeric" placeholder="010-0000-0000" />
                </Cell>
              </CellGroup>
            </div>
          )}
        </div>

        {/* 신청 서비스 */}
        <div>
          <span className="mb-2 block text-sm font-medium text-slate-700">
            신청 서비스 <span className="text-rose-500">*</span>
          </span>
          <div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
            {SERVICES.map((s) => {
              const on = form.service === s.key;
              return (
                <button
                  key={s.key}
                  type="button"
                  onClick={() => set("service", s.key)}
                  className={`rounded-lg border p-3 text-left transition ${
                    on ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
                  }`}
                >
                  <span className="flex items-center gap-1.5">
                    <span className="text-sm font-semibold text-slate-800">{s.label}</span>
                    {!s.ready && (
                      <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-500">준비중</span>
                    )}
                  </span>
                  <span className="mt-0.5 block text-xs text-slate-400">{s.desc}</span>
                </button>
              );
            })}
          </div>
        </div>

        {/* 메모 */}
        <div>
          <span className="mb-1.5 flex items-center gap-1.5 text-sm font-medium text-slate-700">
            <svg className="h-4 w-4 text-orange-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
              <path d="M8 10h8M8 14h5M21 12a8 8 0 01-8 8H7l-4 3 1-5a8 8 0 1117-6z" />
            </svg>
            메모
            <span className="font-normal text-slate-400">(선택)</span>
          </span>
          <div className="rounded-xl bg-orange-50 p-3 ring-1 ring-orange-100">
            <textarea
              rows={3}
              value={form.memo}
              onChange={(e) => set("memo", e.target.value)}
              placeholder="요청사항이나 특이사항을 자유롭게 적어주세요."
              className="w-full resize-none border-0 bg-transparent text-sm text-slate-800 placeholder:text-orange-300 focus:outline-none focus:ring-0"
            />
          </div>
        </div>

        {/* 동의 */}
        <button
          type="button"
          onClick={() => set("agree", !form.agree)}
          className={`flex w-full items-start gap-2.5 rounded-lg border p-3 text-left transition ${
            form.agree ? "border-orange-500 bg-orange-50" : "border-slate-200 bg-white hover:bg-slate-50"
          }`}
        >
          <span className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${form.agree ? "border-orange-500 bg-orange-500" : "border-slate-300 bg-white"}`}>
            {form.agree && (
              <svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
              </svg>
            )}
          </span>
          <span className="text-sm text-slate-700">
            <span className="font-medium">[필수]</span> 본인은 위 정보를 바탕으로 세무전문가가 홈택스에 접속하여 세무 신고·자료 수집을 대행하는 것에 동의합니다.
          </span>
        </button>

        <button
          onClick={handleSubmit}
          disabled={!canSubmit}
          className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:cursor-not-allowed disabled:bg-slate-300"
        >
          {submitting ? "접수 중…" : "요청 제출"}
        </button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 영업사원 페이지 (현장 견적 — 모바일/태블릿)
// ─────────────────────────────────────────────────────────────
// 기준금액 안내 (개략 — 사장님 확인 후 조정). 업종/연도별 상이.
const STD_INFO = [
  "복식부기 의무(직전연도 수입금액): 도·소매/부동산매매 3억↑ · 제조/음식/숙박/건설 1.5억↑ · 서비스/임대 7,500만↑ (미만=간편장부 대상)",
  "간이과세자 기준: 직전연도 공급대가 8,000만원 미만 (부동산임대·과세유흥 4,800만원) ※기준 변동 가능, 확인요망",
  "기장 보수료(월): 일반 소매·음식 등 상점 8만원 / 규모 1억 언저리~초과 10만원↑ 재량",
];

// 빈 폼(현장고객 입력) — 초기값·리셋 공용. 활동기록/접수 후 페이지 초기화에 사용.
const EMPTY_Q = {
  name: "", revenue: "", book_type: "simple", biz_count: 1, extra_count: 0,
  financial: false, rental: false,
  surcharge: "", surcharge_reason: "", discount: "", discount_reason: "", memo: "",
  tax_credit: "",   // 법인: 세액공제·감면액(5% 가산, 천원절사)
  phone: "", address: "", biz_name: "",   // 현장고객 — 순서 무관, 선택 입력
  rate_table: "kmong",   // 요율표: kmong(크몽 기장용) / 세무사회
};

function SalesForm() {
  const ORIGIN = typeof window !== "undefined" ? window.location.origin : "https://피에이세무회계.kr";
  const [calcMode, setCalcMode] = useState("sa");  // sa=간편인증 / auto=ID·PW 자동산출 / manual=수동 견적
  const [salesList, setSalesList] = useState(["김영업"]);
  const [sales, setSales] = useState("김영업");   // 영업사원 (default 김영업)
  useEffect(() => { apiGet("/api/salespeople", ["김영업"]).then((l) => { if (Array.isArray(l) && l.length) setSalesList(l); }); }, []);
  const [q, setQ] = useState({ ...EMPTY_Q });
  const [calc, setCalc] = useState(null);
  const [busy, setBusy] = useState(false);
  const [saved, setSaved] = useState(null);
  const [manualRid, setManualRid] = useState(null);   // 수동견적 세션 요청 ID(재산출 시 같은 요청 갱신)
  const [copied, setCopied] = useState("");
  const setF = (k, v) => setQ((s) => ({ ...s, [k]: v }));
  const won = (n) => (n == null ? "—" : Number(n).toLocaleString("ko-KR") + "원");
  const copy = (t, key) => { try { navigator.clipboard?.writeText(t); } catch (e) {} setCopied(key); setTimeout(() => setCopied(""), 1500); };

  // 예상세액(소득세계산 '납부세액 계') 표시 — 음수=환급, 양수=납부, 0=없음
  const estBlock = (r) => {
    if (r == null || r.est_tax == null) return null;
    const v = Number(r.est_tax);
    const label = v < 0 ? "예상 환급액" : v > 0 ? "예상 납부세액" : "납부할 세액 없음";
    const cls = v < 0 ? "text-emerald-600" : v > 0 ? "text-rose-600" : "text-slate-500";
    return (
      <div className="my-2 rounded-lg border border-orange-200 bg-orange-50 p-3 text-center">
        <div className="text-[11px] font-medium text-orange-500">💰 예상세액 (소득세계산 · 납부세액 계)</div>
        <div className={`text-2xl font-bold ${cls}`}>{v === 0 ? "없음" : won(Math.abs(v))}</div>
        <div className="text-[11px] text-slate-500">{label}{v < 0 ? " · 환급 예상" : v > 0 ? " · 납부 예상" : ""}</div>
      </div>
    );
  };

  // ── 방문 위치(GPS) — 접수 시 좌표 기록 ──
  const [geo, setGeo] = useState(null);       // {lat,lng} — 마지막 좌표(핀+동선추적이 갱신). 동선기록·지도용.
  const [geoBusy, setGeoBusy] = useState(false);
  // 핀 게이트 — geo와 분리. 핀 버튼만 true로 만들고 동선추적(watchPosition)은 건드리지 않음.
  // 활동기록/접수 후 false=업무잠금 → 핀을 다시 찍어야 열림(동선추적은 계속 ON 유지).
  const [pinned, setPinned] = useState(false);
  // 견적/파이프라인 실행 후 '접수하기' 마감 대기 플래그 — 마감 전엔 다음 고객 업무 차단(전 모드 공통).
  const [pendingFinalize, setPendingFinalize] = useState(false);
  const captureGeo = () => {
    if (!navigator.geolocation) { alert("이 기기는 위치를 지원하지 않습니다"); return; }
    setGeoBusy(true);
    navigator.geolocation.getCurrentPosition(
      (p) => { setGeo({ lat: +p.coords.latitude.toFixed(6), lng: +p.coords.longitude.toFixed(6) }); setPinned(true); setGeoBusy(false); },
      (e) => { setGeoBusy(false); alert("위치 가져오기 실패: " + e.message + "\n(HTTPS·위치권한 허용 필요)"); },
      { enableHighAccuracy: true, timeout: 10000 }
    );
  };
  const geoMemo = () => (geo ? `📍방문위치 ${geo.lat},${geo.lng} https://maps.google.com/?q=${geo.lat},${geo.lng}` : "");
  const withGeo = (memo) => [memo, geoMemo()].filter(Boolean).join("\n") || undefined;

  // ── 영업 활동 기록(동선·업무) — 견적 외 활동(위치갱신·정보못받음 등) ──
  const ACT_KINDS = ["위치정보 갱신", "정보 받지못함", "부재중(사장 부재)", "재방문 예정", "상담 거절", "기타 메모"];
  const [actKind, setActKind] = useState("위치정보 갱신");
  const [actMsg, setActMsg] = useState("");
  const logActivity = async (kind, extra = {}) =>
    apiPost("/api/sales/activity", {
      salesperson: sales || undefined, kind,
      biz_name: q.biz_name || undefined, phone: q.phone ? onlyDigits(q.phone) : undefined,
      memo: q.memo || undefined, geo: geo || undefined, ...extra,
    }, null);
  const recordActivity = async () => {
    if (actKind === "위치정보 갱신" && !geo) { alert("위치정보 갱신은 먼저 ‘📍 현재 위치 기록’을 눌러주세요"); return; }
    await logActivity(actKind);
    const t = new Date().toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit" });
    // 활동기록 = 한 건의 방문/업무 종료 → 서버 반영 후 페이지 초기화(위치 해제 → 업무 잠금).
    // 다음 업무를 시작하려면 ‘📍 현재 위치 기록’을 다시 눌러야 함.
    setActMsg(`✓ ‘${actKind}’ 서버 기록됨 ${t}${geo ? " · 위치 포함" : ""} — 페이지 초기화됨(동선추적 유지). 새 업무는 ‘📍 현재 위치 기록’부터.`);
    setPinned(false);   // 핀만 해제(업무잠금). 동선추적(watchPosition)·geo는 그대로 유지.
    setQ({ ...EMPTY_Q });
    setCalc(null);
    setManualRid(null);   // 다음 고객은 새 수동견적 요청으로
    setTimeout(() => setActMsg(""), 8000);
  };

  // ── 동선 실시간 추적 (watchPosition → 주기적 위치 활동기록, 운영자 탭에 실시간 반영) ──
  const _today = (() => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; })();
  const [tracking, setTracking] = useState(false);
  const watchRef = React.useRef(null);
  const lastPostRef = React.useRef(0);
  const [myRoute, setMyRoute] = useState([]);
  const loadMyRoute = async () => {
    if (!sales) return;
    const r = await apiGet(`/api/sales/activity?salesperson=${encodeURIComponent(sales)}&date=${_today}`, []);
    setMyRoute(Array.isArray(r) ? r.filter((a) => a.lat && a.lng) : []);
  };
  useEffect(() => { loadMyRoute(); /* eslint-disable-next-line */ }, [sales]);
  const startTracking = () => {
    if (!navigator.geolocation) { alert("이 기기는 위치를 지원하지 않습니다"); return; }
    setTracking(true);
    watchRef.current = navigator.geolocation.watchPosition(async (p) => {
      const g = { lat: +p.coords.latitude.toFixed(6), lng: +p.coords.longitude.toFixed(6) };
      setGeo(g);
      const now = Date.now();
      if (now - lastPostRef.current < 1800000) return;   // 최소 30분 간격(과다기록 방지)
      lastPostRef.current = now;
      await logActivity("위치정보 갱신", { geo: g, memo: "자동 동선 추적" });
      loadMyRoute();
    }, () => {}, { enableHighAccuracy: true, maximumAge: 30000, timeout: 20000 });
  };
  const stopTracking = () => {
    if (watchRef.current != null && navigator.geolocation) navigator.geolocation.clearWatch(watchRef.current);
    watchRef.current = null; setTracking(false);
  };
  useEffect(() => () => { if (watchRef.current != null && navigator.geolocation) navigator.geolocation.clearWatch(watchRef.current); }, []);
  const myRouteUrl = myRoute.length ? `https://www.google.com/maps/dir/${myRoute.map((a) => `${a.lat},${a.lng}`).join("/")}` : null;
  const [showMyMap, setShowMyMap] = useState(false);
  // 업무 가능 조건: 동선 추적 ON + 현재 위치 기록됨. 둘 중 하나라도 없으면 업무 차단.
  const canWork = tracking && pinned;
  const blockWork = () => {
    if (pendingFinalize) { alert("이전 고객 ‘접수하기’를 먼저 누르세요 — 마감해야 다음 위치기록·업무가 가능합니다."); return true; }
    if (!tracking) { alert("동선 추적을 먼저 켜세요 (상단). 추적 중에만 영업 업무가 가능합니다."); return true; }
    if (!pinned) { alert("현재 위치 핀을 먼저 찍으세요 (상단 ‘📍 현재 위치 기록’). 새 업무는 핀부터입니다."); return true; }
    return false;
  };
  // ‘접수하기’ — 현재 고객 업무 마감: 업무잠금(핀해제) + 폼 초기화. (파이프라인 결과는 이미 고객목록에 등록됨)
  const finalizeIntake = () => {
    setPendingFinalize(false);
    setPinned(false);              // 다음 고객은 ‘📍 현재 위치 기록’부터
    setQ({ ...EMPTY_Q }); setCalc(null); setManualRid(null);
    setAuto({ name: "", hometax_id: "", hometax_pw: "", jumin: "" }); setAutoRes(null); setAutoBusy(false);
    stopSaPoll(); setSaStage("idle"); setSaRid(null); setSaRes(null);
    setSa({ name: "", jumin: "", phone: "", provider: "카카오톡", telco: "SKT" });
    setActMsg("✓ 접수 완료 — 다음 고객은 ‘📍 현재 위치 기록’부터 시작하세요.");
    setTimeout(() => setActMsg(""), 8000);
  };

  // ── 상단 뷰 전환: 견적 작성 / 내 금일 고객 조회 ──
  const [view, setView] = useState("form");   // form | mine

  // 귀속연도 — 파이프라인 자료다운로드·계산 기준(연도별로 결과 달라짐). auto·sa 공용.
  const YEARS = [2025, 2024, 2023];
  const [quoteYear, setQuoteYear] = useState(2025);
  // ── 자동 산출 (홈택스 로그인 → 신고안내자료 → 계산 → 견적) ──
  const [auto, setAuto] = useState({ name: "", hometax_id: "", hometax_pw: "", jumin: "" });
  const [autoShowPw, setAutoShowPw] = useState(false);
  const [autoBusy, setAutoBusy] = useState(false);
  const [autoRes, setAutoRes] = useState(null);   // 폴링된 요청
  const setA = (k, v) => setAuto((s) => ({ ...s, [k]: v }));
  const runAuto = async () => {
    if (blockWork()) return;
    if (!(auto.name && auto.hometax_id && auto.hometax_pw && onlyDigits(auto.jumin).length >= 7)) {
      alert("성명·아이디·비번·주민 7자리를 입력하세요"); return;
    }
    setAutoBusy(true); setAutoRes({ status: "running", logs: ["자동 산출 시작 — 로그인→신고안내자료→계산"] });
    const r = await apiPost("/api/sales/quote", {
      name: auto.name, hometax_id: auto.hometax_id, hometax_pw: auto.hometax_pw,
      jumin: onlyDigits(auto.jumin), year: quoteYear, salesperson: sales || undefined,
      phone: q.phone ? onlyDigits(q.phone) : undefined, address: q.address || undefined,
      biz_name: q.biz_name || undefined, memo: withGeo(q.memo), geo: geo || undefined,
    }, null);
    if (!r?.request_id) { setAutoBusy(false); setAutoRes({ status: "failed", logs: ["요청 생성 실패"] }); return; }
    const id = r.request_id;
    setPendingFinalize(true);   // 고객목록 등록됨 → ‘접수하기’ 마감 필요(다음 고객 위치기록 전)
    logActivity("견적-자동산출", { request_id: id, biz_name: q.biz_name || auto.name || undefined });
    const t = setInterval(async () => {
      const d = await apiGet(`/api/sales/request/${id}`, null);   // 영업사원=staff 허용(민감필드 제거)
      if (!d) return;
      setAutoRes(d);
      if (d.status !== "running") { clearInterval(t); setAutoBusy(false); }
    }, 3000);
  };

  // ── 간편인증 산출 (홈택스 간편인증 → 로그인 → 안내문 → 소득세계산 → 견적) ──
  const SA_PROVIDERS = ["카카오톡", "통신사PASS", "토스"];
  const TELCOS = ["SKT", "KT", "LGU+"];
  const [sa, setSa] = useState({ name: "", jumin: "", phone: "", provider: "카카오톡", telco: "SKT" });
  const [saRid, setSaRid] = useState(null);
  const [saStage, setSaStage] = useState("idle");   // idle|sent|running|done|failed
  const [saRes, setSaRes] = useState(null);          // 폴링된 요청
  const setS = (k, v) => setSa((s) => ({ ...s, [k]: v }));
  const saPollRef = React.useRef(null);
  const stopSaPoll = () => { if (saPollRef.current) { clearInterval(saPollRef.current); saPollRef.current = null; } };
  useEffect(() => () => stopSaPoll(), []);   // 언마운트 시 폴링 정리

  const startSaPoll = (id) => {
    stopSaPoll();
    saPollRef.current = setInterval(async () => {
      const d = await apiGet(`/api/sales/request/${id}`, null);   // 영업사원=staff 허용(민감필드 제거)
      if (!d) return;
      setSaRes(d);
      if (d.status === "needs_auth") setSaStage("sent");        // 인증요청 발송됨 — 고객 승인 대기
      else if (d.status === "running") setSaStage("running");   // 인증완료 클릭 후 모듈 실행 중
      else { stopSaPoll(); setSaStage(d.status === "failed" ? "failed" : "done"); }
    }, 3000);
  };

  const sendSa = async () => {
    if (blockWork()) return;
    if (!(sa.name && onlyDigits(sa.jumin).length >= 7 && onlyDigits(sa.phone).length >= 10)) {
      alert("성명·주민 7자리·휴대폰번호를 입력하세요"); return;
    }
    setSaStage("sent"); setSaRid(null);
    setSaRes({ status: "needs_auth", logs: [`간편인증(${sa.provider}) 요청 전송 중…`] });
    const r = await apiPost("/api/sales/quote-sa", {
      name: sa.name, jumin: onlyDigits(sa.jumin),
      sa_phone: onlyDigits(sa.phone),                              // 간편인증 휴대폰(푸시)
      phone: q.phone ? onlyDigits(q.phone) : undefined,           // 연락/매장 전화(표시)
      sa_provider: sa.provider, telco: sa.provider === "통신사PASS" ? sa.telco : undefined,
      salesperson: sales || undefined, year: quoteYear,
      address: q.address || undefined, biz_name: q.biz_name || undefined,
      memo: withGeo(q.memo), geo: geo || undefined,
    }, null);
    if (!r?.request_id) { setSaStage("failed"); setSaRes({ status: "failed", logs: ["요청 생성 실패 — 서버 확인"] }); return; }
    setSaRid(r.request_id);
    setPendingFinalize(true);   // 고객목록 등록됨 → 인증완료 후 ‘접수하기’ 마감 필요
    logActivity("견적-간편인증", { request_id: r.request_id, biz_name: q.biz_name || sa.name || undefined });
    startSaPoll(r.request_id);
  };

  const completeSa = async () => {
    if (!saRid) return;
    setSaStage("running");
    setSaRes((s) => ({ ...(s || {}), status: "running", logs: [...((s && s.logs) || []), "인증완료 클릭 → 홈택스 인증완료 → 로그인 시도"] }));
    await apiPost(`/api/requests/${saRid}/simple-auth/complete`, { modules: ["jongso_calc", "invoice"] }, null);
    startSaPoll(saRid);
  };

  const resetSa = () => { stopSaPoll(); setSaStage("idle"); setSaRid(null); setSaRes(null); setSa({ name: "", jumin: "", phone: "", provider: "카카오톡", telco: "SKT" }); };

  // ── 영업 고객목록: 일자별 멀티탭 + 페이지네이션 ──
  const [browseAll, setBrowseAll] = useState(false);        // 전체 영업사원 보기(운영자용)
  const [dates, setDates] = useState([]);                    // [{date,count}]
  const [openTabs, setOpenTabs] = useState([]);              // 열린 일자탭 [date]
  const [activeTab, setActiveTab] = useState(null);          // 현재 일자탭
  const [tabData, setTabData] = useState({});                // {date: [requests]}
  const [tabPage, setTabPage] = useState({});                // {date: page(1-base)}
  const [mineQ, setMineQ] = useState("");                    // 활성탭 내 검색
  const PER_PAGE = 10;
  const _spParam = () => (browseAll ? "" : (sales || ""));
  const loadDates = async () => {
    const r = await apiGet(`/api/sales/dates?salesperson=${encodeURIComponent(_spParam())}`, []);
    setDates(Array.isArray(r) ? r : []);
  };
  const loadTab = async (date) => {
    const p = new URLSearchParams({ salesperson: _spParam(), date });
    const r = await apiGet(`/api/sales/my-requests?${p.toString()}`, []);
    setTabData((m) => ({ ...m, [date]: Array.isArray(r) ? r : [] }));
  };
  const openTab = (date) => {
    setOpenTabs((t) => (t.includes(date) ? t : [...t, date]));
    setActiveTab(date);
    setTabPage((m) => ({ ...m, [date]: m[date] || 1 }));
    loadTab(date);
  };
  const closeTab = (date) => {
    setOpenTabs((t) => {
      const nt = t.filter((d) => d !== date);
      setActiveTab((a) => (a === date ? (nt[nt.length - 1] || null) : a));
      return nt;
    });
  };
  useEffect(() => {
    if (view !== "mine") return;
    loadDates();
    const today = (() => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; })();
    if (!activeTab) openTab(today);   // 진입 시 오늘 탭 자동 오픈(방금 돌린 고객 바로 보이게)
    else loadTab(activeTab);
    /* eslint-disable-next-line */
  }, [view, sales, browseAll]);
  // 활성탭 자동 폴링(진행중 고객 상태·동선 반영)
  useEffect(() => {
    if (view !== "mine" || !activeTab) return;
    const t = setInterval(() => loadTab(activeTab), 5000);
    return () => clearInterval(t);
    /* eslint-disable-next-line */
  }, [view, activeTab, browseAll, sales]);

  // ── 금일 고객 상세탭 (수정 / 파일다운로드 / 삭제요청 / 파이프라인 재실행) ──
  const [detail, setDetail] = useState(null);        // 폴링된 상세(sanitized)
  const [draft, setDraft] = useState(null);          // 수정 입력 버퍼
  const [runMethod, setRunMethod] = useState("sa");  // 재실행 방식: sa | idpw
  const [detailMsg, setDetailMsg] = useState("");
  const [detailBusy, setDetailBusy] = useState(false);
  const detailPollRef = React.useRef(null);
  const stopDetailPoll = () => { if (detailPollRef.current) { clearInterval(detailPollRef.current); detailPollRef.current = null; } };
  useEffect(() => () => stopDetailPoll(), []);
  const setD = (k, v) => setDraft((s) => ({ ...s, [k]: v }));

  const openDetail = async (r) => {
    stopDetailPoll();
    setView("detail"); setDetailMsg("");
    setDetail(r);
    setRunMethod(r.simple_auth ? "sa" : "idpw");
    setDraft({ biz_name: r.biz_name || "", phone: r.phone || "", name: r.name || "", address: r.address || "",
               memo: r.memo || "", jumin: "", sa_phone: "", sa_provider: r.sa_provider || "카카오톡", telco: r.telco || "SKT",
               hometax_id: r.hometax_id || "", hometax_pw: "", year: r.year || "" });
    const fresh = await apiGet(`/api/sales/request/${r.request_id}`, null);
    if (fresh) { setDetail(fresh); setRunMethod(fresh.simple_auth ? "sa" : "idpw"); }
    startDetailPoll(r.request_id);
  };
  const startDetailPoll = (rid) => {
    stopDetailPoll();
    detailPollRef.current = setInterval(async () => {
      const d = await apiGet(`/api/sales/request/${rid}`, null);
      if (d) setDetail(d);
    }, 3000);
  };
  const closeDetail = () => { stopDetailPoll(); setDetail(null); setDraft(null); setView("mine"); if (activeTab) loadTab(activeTab); };

  const saveDetail = async () => {
    if (!detail) return;
    setDetailBusy(true);
    const r = await apiPost(`/api/requests/${detail.request_id}/edit`, draft, null);
    setDetailBusy(false);
    if (r?.ok) { setDetail(r.request); setDetailMsg("✓ 저장됨"); setTimeout(() => setDetailMsg(""), 2500); }
    else setDetailMsg("저장 실패");
    return r?.ok;
  };
  const requestDelete = async () => {
    if (!detail) return;
    const reason = window.prompt("운영자에게 삭제요청 사유를 입력하세요 (선택)", "");
    if (reason === null) return;
    await apiPost(`/api/requests/${detail.request_id}/delete-request`, { reason, by: sales || undefined }, null);
    setDetailMsg("🗑️ 운영자에게 삭제요청 전송됨");
    const d = await apiGet(`/api/sales/request/${detail.request_id}`, null);
    if (d) setDetail(d);
  };
  // 수정→파이프라인 재실행 (단일 런버튼, 개별런 아님). method=sa|idpw
  const rerunDetail = async () => {
    if (!detail) return;
    if (runMethod === "idpw" && !(detail.has_idpw || (draft.hometax_id && draft.hometax_pw))) {
      alert("ID/PW 방식으로 로그인할 수 없습니다.\n\n이유: 홈택스 아이디·비밀번호가 등록되지 않았습니다.\nID/PW를 입력해 저장하거나, 간편인증 방식을 사용하세요.");
      return;
    }
    await saveDetail();   // 변경분 먼저 저장
    setDetailMsg(`▶ 파이프라인 재실행 요청… (${runMethod === "sa" ? "간편인증" : "ID/PW"})`);
    const r = await apiPost(`/api/requests/${detail.request_id}/rerun`, { year: draft.year || undefined, method: runMethod }, null);
    if (r?.error) { setDetailMsg("❌ " + r.error); return; }
    if (r?.mode === "simple_auth") setDetailMsg("📲 간편인증 재발송됨 — 고객 승인 후 아래 '인증완료'를 누르세요");
    else setDetailMsg("▶ 파이프라인 실행 중… (1~2분)");
    startDetailPoll(detail.request_id);
  };
  const cancelDetail = async () => {
    if (!detail) return;
    if (!window.confirm("이 작업을 중단할까요? (브라우저 종료)")) return;
    await apiPost(`/api/requests/${detail.request_id}/cancel`, {}, null);
    setDetailMsg("⛔ 작업 중단 요청 — 브라우저 정리");
    const d = await apiGet(`/api/sales/request/${detail.request_id}`, null);
    if (d) setDetail(d);
  };
  const completeDetailSa = async () => {
    if (!detail) return;
    setDetailMsg("✅ 인증완료 → 로그인 → 안내문 → 계산 → 견적 진행…");
    await apiPost(`/api/requests/${detail.request_id}/simple-auth/complete`, { modules: ["jongso_calc", "invoice"] }, null);
    startDetailPoll(detail.request_id);
  };

  // ── 고객 휴대폰 직접입력 QR (1회용 링크) ──
  const [qrUrl, setQrUrl] = useState("");
  const [qrKind, setQrKind] = useState("");   // 생성한 QR의 폼종류 라벨(표시용)
  const [qrSel, setQrSel] = useState("sa");   // QR 생성 드롭다운 선택값
  const QR_KINDS = [
    { k: "sa", l: "간편인증" },
    { k: "quote", l: "단순견적(ID·PW)" },
    { k: "full", l: "정식등록" },
    { k: "corp", l: "법인" },
  ];
  const genHandoff = async (kind, label) => {
    const r = await apiPost("/api/sales/handoff", { kind, salesperson: sales || undefined, created_by: "sales" }, null);
    if (r?.token) { setQrUrl(`${ORIGIN}/?token=${r.token}`); setQrKind(label); }
    else alert("QR 생성 실패 — 서버 확인");
  };

  // 정식등록(전환) 1회용 링크 — 견적 건에서 고객에게 전체정보 입력 폼 보내기.
  const [regUrl, setRegUrl] = useState("");
  const genReg = async () => {
    const r = await apiPost("/api/sales/handoff",
      { kind: "full", from_id: saved?.request_id, salesperson: sales || undefined, created_by: "sales" }, null);
    if (r?.token) { const u = `${ORIGIN}/?token=${r.token}`; setRegUrl(u); copy(u, "reg"); }
    else alert("링크 생성 실패 — 서버 확인");
  };

  // 수동견적 → 고객정보 요약(quote_info) 구성. 자동/간편인증의 [고객 정보] 블록과 동일 형태.
  const buildQuoteInfo = (cc) => {
    const L = ["[고객 정보]"];
    if (q.biz_name) L.push(`· 상호: ${q.biz_name}`);
    if (q.name) L.push(`· 성명: ${q.name}`);
    if (q.phone) L.push(`· 연락처: ${formatPhone(q.phone)}`);
    if (q.address) L.push(`· 주소: ${q.address}`);
    L.push(`· 사업자 구분: ${cc?.book_type_text || (q.book_type === "double" ? "복식부기 대상자" : "간편장부 대상자")}`);
    if (q.revenue) L.push(`· 수입금액: ${won(Number(q.revenue))}`);
    if (Number(q.biz_count) > 1) L.push(`· 사업장 수: ${q.biz_count}곳`);
    if (cc) {
      // 산출 내역 — 어떻게 계산됐는지 고객정보에서 바로 확인 가능하게.
      L.push("");
      L.push("[종합소득세 세무조정료 — 산출 내역]");
      if (cc.bracket_label) L.push(`· 적용 구간: ${cc.bracket_label}`);
      L.push(`· 기본 보수료: ${won(cc.base_fee)}`);
      if (cc.fin_fee > 0) L.push(`· 금융/임대 추가: ${won(cc.fin_fee)}`);
      if (cc.extra_fee > 0) L.push(`· 근·연·기 추가 (×${cc.extra_count}): ${won(cc.extra_fee)}`);
      if (cc.biz_fee > 0) L.push(`· 사업장 추가 (×${cc.extra_biz}, 총 ${cc.biz_count}개): ${won(cc.biz_fee)}`);
      if (cc.credit_fee > 0) L.push(`· 세액공제·감면 가산 (감면액 ${won(cc.tax_credit)} × 5%): +${won(cc.credit_fee)}`);
      if (cc.surcharge > 0) L.push(`· 가산${cc.surcharge_reason ? ` (${cc.surcharge_reason})` : ""}: +${won(cc.surcharge)}`);
      L.push(`· 공급가: ${won(cc.supply)}`);
      L.push(`· 부가세(10%): ${won(cc.vat)}`);
      if (cc.total_trim > 0) {
        L.push(`· 합계: ${won(cc.total_raw)} → 만원단위 버림 -${won(cc.total_trim)}`);
        L.push(`· 입금액(부가세 포함): ${won(cc.total)}`);
      } else {
        L.push(`· 합계(부가세 포함): ${won(cc.total)}`);
      }
    }
    return L.join("\n");
  };

  // 수동견적 요청 payload — 생성/갱신 공용. quote_text(청구서)·quote_info(고객정보)도 함께 저장.
  const manualPayload = (cc) => ({
    mode: calcMode === "corp" ? "corp" : "quote", source: "sales", agree: true, service: "jongso",
    salesperson: sales || undefined,
    cust_label: (calcMode === "corp" ? "법인 · " : "") + (sales ? `${sales}고객` : "현장견적"),
    name: q.name || "현장고객",
    phone: q.phone ? onlyDigits(q.phone) : undefined,
    address: q.address || undefined,
    biz_name: q.biz_name || undefined,
    memo: withGeo(q.memo),
    geo: geo || undefined,
    quote: cc || undefined,
    quote_text: (cc && cc.text) || undefined,   // 청구 발송문
    quote_info: buildQuoteInfo(cc),             // 고객정보 요약
  });

  // 산출 즉시 일자별 리스트에 반영(자동/간편인증과 동일). 재산출이면 같은 요청 갱신.
  const autoSaveManual = async (cc) => {
    if (manualRid) {
      await apiPost(`/api/requests/${manualRid}/quote`, manualPayload(cc), null);
    } else {
      const res = await apiPost("/api/requests", manualPayload(cc), null);
      if (res?.request_id) { setManualRid(res.request_id); logActivity("견적-수동", { request_id: res.request_id }); }
    }
  };

  const runCalc = async () => {
    if (blockWork()) return;
    setBusy(true);
    const payload = {
      client_name: q.name,
      revenue: Number(onlyDigits(q.revenue) || 0),
      book_type: q.book_type,
      biz_count: Number(q.biz_count) || 1,
      extra_count: Number(q.extra_count) || 0,
      financial: q.financial, rental: q.rental,
      surcharge: Number(onlyDigits(q.surcharge) || 0), surcharge_reason: q.surcharge_reason,
      discount: Number(onlyDigits(q.discount) || 0), discount_reason: q.discount_reason,
      tax_credit: calcMode === "corp" ? Number(onlyDigits(q.tax_credit) || 0) : 0,
      rate_table: calcMode === "corp" ? "법인" : q.rate_table,
    };
    const res = await apiPost("/api/sales/calc", payload, null);
    setCalc(res); setBusy(false);
    if (res) await autoSaveManual(res);   // 산출 즉시 서버 반영(리스트 + 고객정보/청구서)
  };

  const saveReq = async () => {
    if (blockWork()) return;
    // 산출 시 이미 자동 접수됨 → 완료화면(정식등록 링크)만 표시.
    if (manualRid) { setSaved({ request_id: manualRid }); return; }
    // (산출 없이 바로 저장하는 폴백) — 생성.
    const res = await apiPost("/api/requests", manualPayload(calc), { request_id: `REQ-${Date.now().toString().slice(-8)}` });
    if (res?.request_id) { setManualRid(res.request_id); logActivity("견적-수동", { request_id: res.request_id }); }
    setSaved(res);
  };

  const numInput = "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-orange-500 focus:outline-none focus:ring-2 focus:ring-orange-100";

  if (saved) {
    const resetAll = () => { setSaved(null); setCalc(null); setPinned(false); setManualRid(null); setRegUrl(""); setQ({ ...EMPTY_Q }); };
    if (saved.visited) {
      return (
        <div className="mx-auto max-w-md px-4 py-10 text-center">
          <h2 className="text-xl font-bold text-slate-800">방문 기록 완료</h2>
          <p className="mt-2 text-sm text-slate-500">접수번호 <span className="font-mono font-semibold">{saved.request_id}</span> {sales && `· ${sales}`}</p>
          <div className="mt-4 rounded-lg border border-rose-200 bg-rose-50 p-3 text-sm text-rose-600">견적은 제공되지 않았지만 <b>매장 방문확인 고객</b>으로 기록되었습니다. {geo ? "(위치 포함)" : "(위치 미기록)"}<br />운영자 ‘영업사원(금일)’ 탭에서 확인됩니다.</div>
          <button onClick={resetAll} className="mt-6 text-sm font-medium text-orange-600 hover:text-orange-700">새 접수</button>
        </div>
      );
    }
    return (
      <div className="mx-auto max-w-md px-4 py-10 text-center">
        <h2 className="text-xl font-bold text-slate-800">현장 접수 완료</h2>
        <p className="mt-2 text-sm text-slate-500">접수번호 <span className="font-mono font-semibold">{saved.request_id}</span> {sales && `· ${sales}고객`}</p>
        <div className="mt-5 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-left">
          <span className="mb-1 block text-xs font-medium text-emerald-700">고객 정식등록 링크 (1회용)</span>
          <p className="mb-2 text-[11px] text-emerald-600">고객이 진행 원하면 생성해 보내세요. 전체정보 입력 폼이 열리고, 1회 제출 시 만료됩니다.</p>
          {regUrl ? (
            <div className="flex items-center gap-2">
              <input readOnly value={regUrl} className="w-full truncate rounded-lg border border-emerald-200 bg-white px-2.5 py-1.5 text-xs text-slate-600" />
              <button onClick={() => copy(regUrl, "reg")} className="shrink-0 rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-700">{copied === "reg" ? "복사됨" : "복사"}</button>
            </div>
          ) : (
            <button onClick={genReg} className="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-700">정식등록 링크 생성</button>
          )}
        </div>
        <button onClick={resetAll} className="mt-6 text-sm font-medium text-orange-600 hover:text-orange-700">새 견적</button>
      </div>
    );
  }

  return (
    <div className="mx-auto max-w-lg px-4 py-6">
      <div className="mb-4 text-center">
        <h2 className="text-xl font-bold text-slate-800">영업사원</h2>
        <p className="mt-1 text-sm text-slate-500">간편인증·자동·수동 견적 + 일자별 고객목록</p>
      </div>

      {/* 상단 뷰 전환: 견적 작성 / 고객목록(일자별) */}
      <div className="mb-3 flex gap-1 rounded-lg bg-slate-100 p-1">
        {[{ k: "form", l: "견적 작성" }, { k: "mine", l: "고객목록 📋" }, { k: "route", l: "내 동선 🗺️" }].map((v) => (
          <button key={v.k} onClick={() => setView(v.k)} className={`flex-1 rounded-md py-2 text-sm font-semibold transition ${view === v.k ? "bg-white text-slate-800 shadow-sm" : "text-slate-500 hover:text-slate-700"}`}>{v.l}</button>
        ))}
      </div>

      {/* ★ 최상단 — 위치 기록 + 동선 추적 (업무 전 필수). 꺼져있으면 업무 불가 */}
      <div className={`mb-3 rounded-2xl border-2 p-3 shadow-sm transition ${canWork ? "border-emerald-300 bg-emerald-50" : "border-rose-300 bg-rose-50"}`}>
        <div className="flex items-center justify-between">
          <span className="text-xs font-bold text-slate-700">📍 위치·동선 {canWork ? <span className="text-emerald-600">· 업무 가능</span> : <span className="text-rose-600">· 업무 불가 (켜세요)</span>}</span>
          <button onClick={tracking ? stopTracking : startTracking} className={`rounded-lg px-3 py-1.5 text-xs font-bold transition ${tracking ? "bg-rose-600 text-white hover:bg-rose-700" : "bg-emerald-600 text-white hover:bg-emerald-700"}`}>
            {tracking ? "⏹ 동선 추적 끄기" : "▶ 동선 추적 켜기"}
          </button>
        </div>
        <div className="mt-2 flex flex-wrap items-center gap-2">
          <button type="button" onClick={captureGeo} disabled={geoBusy} className={`rounded-lg border px-2.5 py-1 text-xs font-medium transition ${pinned ? "border-emerald-300 bg-white text-emerald-700" : "border-rose-300 bg-white text-rose-600"} disabled:opacity-50`}>
            {geoBusy ? "위치 확인중…" : pinned ? "📍 위치 기록됨 ✓" : "📍 현재 위치 기록 (필수)"}
          </button>
          {tracking && <span className="flex items-center gap-1 rounded-full bg-rose-100 px-2 py-0.5 text-[11px] font-medium text-rose-600"><span className="h-2 w-2 animate-pulse rounded-full bg-rose-500" />추적중</span>}
          <span className="text-[11px] text-slate-500">오늘 동선 {myRoute.length}곳</span>
          {myRoute.length > 0 && <button onClick={() => setShowMyMap((v) => !v)} className="text-[11px] text-blue-600 underline">🗺️ 내 동선 지도 {showMyMap ? "닫기" : "보기"}</button>}
          {geo && <a href={`https://maps.google.com/?q=${geo.lat},${geo.lng}`} target="_blank" rel="noreferrer" className="text-[11px] text-blue-600 underline">{geo.lat},{geo.lng}</a>}
        </div>
        {showMyMap && myRoute.length > 0 && (
          <div className="mt-2">
            <RouteMap points={myRoute.map((a) => ({ lat: a.lat, lng: a.lng, label: a.kind || "", time: String(a.created_at || "").slice(11) }))} height={240} />
          </div>
        )}
        {!canWork && <p className="mt-1.5 text-[11px] font-medium text-rose-500">⚠️ 동선 추적을 켜고 현재 위치를 기록해야 견적·간편인증·고객등록이 가능합니다.</p>}
      </div>

      {/* 영업사원 (공통) — 드롭다운(운영자가 목록 관리, default 김영업) */}
      <div className="mb-3">
        <label className="mb-1 block text-xs font-medium text-slate-500">영업사원</label>
        <select className={numInput} value={sales} onChange={(e) => setSales(e.target.value)}>
          {salesList.map((n) => <option key={n} value={n}>{n}</option>)}
        </select>
      </div>

      {/* ─────────── 영업 고객목록 (일자별 멀티탭 · 10개 페이지네이션) ─────────── */}
      {view === "mine" && (() => {
        const custCard = (r) => {
          const st = STATUS_META[r.status] || { label: r.status, cls: "bg-slate-100 text-slate-600" };
          return (
            <div key={r.request_id} onClick={() => openDetail(r)} className="cursor-pointer rounded-2xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-orange-300 hover:shadow">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-1.5 text-sm font-semibold text-slate-800"><KindBadge r={r} /><span>{r.biz_name || r.name || "고객"}</span>{r.phone && <span className="font-normal text-slate-500">· {formatPhone(r.phone)}</span>}</div>
                <div className="flex items-center gap-1.5">
                  {r.delete_request?.requested && <span className="rounded-full bg-rose-100 px-2 py-0.5 text-[10px] font-medium text-rose-600">삭제요청</span>}
                  <span className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${st.cls || "bg-slate-100 text-slate-600"}`}>{st.label}</span>
                </div>
              </div>
              <div className="mt-0.5 text-[11px] text-slate-400">{r.name && `${r.name} · `}{r.simple_auth ? "간편인증" : "ID/PW"} · {r.created_at} {r.salesperson && `· ${r.salesperson}`} <span className="text-orange-500">· 클릭하여 열람·파이프라인 ›</span></div>
              {estBlock(r)}
              {r.quote_info && <pre className="mt-2 max-h-32 overflow-y-auto whitespace-pre-wrap rounded bg-blue-50/50 p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-blue-100">{r.quote_info}</pre>}
              {r.quote_text && (
                <div className="mt-2">
                  <button onClick={(e) => { e.stopPropagation(); copy(r.quote_text, "ci" + r.request_id); }} className="mb-1 rounded border border-slate-300 px-2 py-0.5 text-[11px] text-slate-500 hover:bg-slate-50">{copied === "ci" + r.request_id ? "복사됨" : "청구 발송문 복사"}</button>
                  <pre className="max-h-32 overflow-y-auto whitespace-pre-wrap rounded bg-slate-50 p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-slate-100">{r.quote_text}</pre>
                </div>
              )}
              {(r.status === "running" || r.status === "needs_auth") && (
                <div className="mt-2 space-y-0.5 font-mono text-[10px] text-slate-500">{(r.logs || []).slice(-4).map((l, i) => <div key={i}>{l}</div>)}</div>
              )}
            </div>
          );
        };
        const all = tabData[activeTab] || [];
        const needle = mineQ.trim().toLowerCase();
        const rows = needle ? all.filter((r) => `${r.name || ""} ${r.biz_name || ""} ${r.phone || ""}`.toLowerCase().includes(needle)) : all;
        const page = tabPage[activeTab] || 1;
        const pages = Math.max(1, Math.ceil(rows.length / PER_PAGE));
        const slice = rows.slice((page - 1) * PER_PAGE, page * PER_PAGE);
        const setPage = (p) => setTabPage((m) => ({ ...m, [activeTab]: p }));
        return (
          <div className="space-y-3">
            {/* 조회 대상(운영자=전체 가능) */}
            <div className="flex items-center justify-between rounded-xl border border-slate-200 bg-white px-3 py-2">
              <span className="text-xs font-semibold text-slate-500">고객목록 — 일자 선택</span>
              <label className="flex items-center gap-1.5 text-[11px] text-slate-500"><input type="checkbox" checked={browseAll} onChange={(e) => { setBrowseAll(e.target.checked); setOpenTabs([]); setActiveTab(null); }} />전체 영업사원</label>
            </div>

            {/* 일자 목록 */}
            <div className="rounded-2xl border border-slate-200 bg-white p-3 shadow-sm">
              <span className="mb-2 block text-[11px] font-semibold text-slate-400">일자별 ({dates.length}일) — 클릭하면 탭 열림</span>
              {dates.length === 0 ? <p className="py-3 text-center text-xs text-slate-400">기록이 없습니다.</p> : (
                <div className="flex flex-wrap gap-1.5">
                  {dates.map((d) => (
                    <button key={d.date} onClick={() => openTab(d.date)} className={`rounded-lg border px-2.5 py-1.5 text-xs font-medium transition ${openTabs.includes(d.date) ? "border-orange-400 bg-orange-50 text-orange-700" : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50"}`}>{d.date} <span className="text-slate-400">({d.count})</span></button>
                  ))}
                </div>
              )}
            </div>

            {/* 열린 일자 탭 바 (여러 개) */}
            {openTabs.length > 0 && (
              <div className="flex flex-wrap gap-1 border-b border-slate-200 pb-1">
                {openTabs.map((d) => (
                  <span key={d} className={`flex items-center gap-1 rounded-t-lg border-x border-t px-2.5 py-1.5 text-xs font-medium transition ${activeTab === d ? "border-slate-300 bg-white text-slate-800" : "border-transparent bg-slate-100 text-slate-500"}`}>
                    <button onClick={() => setActiveTab(d)}>{d}</button>
                    <button onClick={() => closeTab(d)} className="text-slate-400 hover:text-rose-500">✕</button>
                  </span>
                ))}
              </div>
            )}

            {/* 활성 탭 내용 — 그날 고객 목록 (검색 + 10개 페이지네이션) */}
            {activeTab ? (
              <div className="space-y-3">
                <div className="flex items-center gap-2">
                  <input className={numInput + " text-sm"} value={mineQ} onChange={(e) => setMineQ(e.target.value)} placeholder="이 날짜 내 고객명·상호·전화 검색" />
                  <button onClick={() => loadTab(activeTab)} className="shrink-0 rounded-lg border border-slate-300 px-3 py-2 text-xs font-medium text-slate-600 hover:bg-slate-50">새로고침</button>
                </div>
                <div className="text-[11px] text-slate-400">{activeTab} · 총 {rows.length}명 (페이지 {page}/{pages})</div>
                {slice.length === 0 ? <p className="py-8 text-center text-sm text-slate-400">고객이 없습니다.</p> : slice.map(custCard)}
                {pages > 1 && (
                  <div className="flex items-center justify-center gap-1">
                    <button onClick={() => setPage(Math.max(1, page - 1))} disabled={page <= 1} className="rounded border border-slate-300 px-2 py-1 text-xs text-slate-600 disabled:opacity-40">‹</button>
                    {Array.from({ length: pages }, (_, i) => i + 1).map((p) => (
                      <button key={p} onClick={() => setPage(p)} className={`rounded px-2.5 py-1 text-xs font-medium ${p === page ? "bg-slate-800 text-white" : "border border-slate-300 text-slate-600 hover:bg-slate-50"}`}>{p}</button>
                    ))}
                    <button onClick={() => setPage(Math.min(pages, page + 1))} disabled={page >= pages} className="rounded border border-slate-300 px-2 py-1 text-xs text-slate-600 disabled:opacity-40">›</button>
                  </div>
                )}
              </div>
            ) : <p className="py-8 text-center text-sm text-slate-400">위에서 일자를 선택하세요.</p>}
          </div>
        );
      })()}

      {/* ─────────── 금일 고객 상세탭 (수정·다운로드·삭제요청·재실행) ─────────── */}
      {view === "detail" && detail && (() => {
        const st = STATUS_META[detail.status] || { label: detail.status, cls: "bg-slate-100 text-slate-600" };
        const isSa = !!detail.simple_auth;
        return (
          <div className="space-y-3">
            <button onClick={closeDetail} className="text-sm font-medium text-orange-600 hover:text-orange-700">‹ 목록으로</button>

            <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
              <div className="flex items-center justify-between">
                <div className="text-base font-bold text-slate-800">{detail.biz_name || detail.name || "고객"} <span className="text-xs font-normal text-slate-400">{detail.request_id}</span></div>
                <span className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${st.cls || "bg-slate-100 text-slate-600"}`}>{st.label}</span>
              </div>
              <div className="mt-0.5 text-[11px] text-slate-400">{detail.phone ? formatPhone(detail.phone) : "전화 없음"} · {detail.name || "성명 미상"} · {detail.created_at} · {isSa ? `간편인증(${detail.sa_provider || "카카오톡"})` : "ID/PW"}{detail.salesperson && ` · ${detail.salesperson}`}</div>
              {detail.delete_request?.requested && (
                <div className="mt-2 rounded-lg border border-rose-200 bg-rose-50 p-2 text-[11px] text-rose-600">🗑️ 운영자에게 <b>삭제요청됨</b> ({detail.delete_request.at}) {detail.delete_request.reason && `· 사유: ${detail.delete_request.reason}`}</div>
              )}
              {estBlock(detail)}
              {detail.quote_info && (
                <div className="mt-2">
                  <div className="mb-1 flex items-center gap-2">
                    <span className="text-[11px] font-semibold text-slate-500">고객 정보 요약</span>
                    <button onClick={() => copy(detail.quote_info, "dinfo")} className="rounded border border-slate-300 px-2 py-0.5 text-[11px] text-slate-500 hover:bg-slate-50">{copied === "dinfo" ? "복사됨" : "복사"}</button>
                  </div>
                  <pre className="max-h-48 overflow-y-auto whitespace-pre-wrap rounded bg-blue-50/50 p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-blue-100">{detail.quote_info}</pre>
                </div>
              )}
              {detail.quote_text && (
                <div className="mt-2">
                  <div className="mb-1 flex items-center gap-2">
                    <span className="text-[11px] font-semibold text-slate-500">청구 발송문</span>
                    <button onClick={() => copy(detail.quote_text, "dtext")} className="rounded border border-slate-300 px-2 py-0.5 text-[11px] text-slate-500 hover:bg-slate-50">{copied === "dtext" ? "복사됨" : "복사"}</button>
                  </div>
                  <pre className="max-h-40 overflow-y-auto whitespace-pre-wrap rounded bg-slate-50 p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-slate-100">{detail.quote_text}</pre>
                </div>
              )}
            </div>

            {/* 고객정보 수정 — 처음부터 다시 입력 없이 고침 (간편인증·ID/PW 양쪽 대칭) */}
            <div className="space-y-2 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
              <span className="block text-xs font-semibold text-slate-500">고객정보 수정 (오기재·에러 시 여기서 고치고 재실행)</span>
              {/* 자격증명 표시 규칙 안내 — 마스킹 때문에 빈칸이라 헷갈리는 것 방지 */}
              <div className="rounded-lg border border-sky-200 bg-sky-50 p-2 text-[11px] leading-relaxed text-sky-700">
                🔒 보안상 <b>등록된 비밀번호·주민번호는 화면에 표시되지 않습니다.</b> 아래 <b>초록 ‘등록됨’</b> 배지가 있으면 이미 입력된 상태예요.<br />
                · <b>빈칸으로 두면 기존 값이 그대로 유지</b>됩니다 (다시 입력 안 해도 됨).<br />
                · <b>새로 입력하면 그 값으로 변경</b>됩니다. (‘미등록’ 표시면 입력이 필요합니다)
              </div>
              {/* 공통: 상호 → 연락전화 → 성명 */}
              <div className="grid grid-cols-2 gap-2">
                <div><label className="mb-0.5 block text-[11px] text-slate-400">상호명</label><input className={numInput + " text-sm"} value={draft.biz_name} onChange={(e) => setD("biz_name", e.target.value)} /></div>
                <div><label className="mb-0.5 block text-[11px] text-slate-400">연락/매장 전화</label><input className={numInput + " text-sm"} value={draft.phone} onChange={(e) => setD("phone", formatPhone(e.target.value))} inputMode="numeric" /></div>
              </div>
              <div className="grid grid-cols-2 gap-2">
                <div><label className="mb-0.5 block text-[11px] text-slate-400">고객 성명</label><input className={numInput + " text-sm"} value={draft.name} onChange={(e) => setD("name", e.target.value)} /></div>
                <div><label className="mb-0.5 block text-[11px] text-slate-400">귀속연도</label><input className={numInput + " text-sm"} value={draft.year} onChange={(e) => setD("year", onlyDigits(e.target.value).slice(0, 4))} inputMode="numeric" placeholder="2025" /></div>
              </div>
              <div><label className="mb-0.5 block text-[11px] text-slate-400">주소</label><input className={numInput + " text-sm"} value={draft.address} onChange={(e) => setD("address", e.target.value)} /></div>

              {/* 주민번호 — 7자리=로그인만 / 13자리=정식등록·full pipeline */}
              <div>
                <label className="mb-0.5 flex flex-wrap items-center gap-1.5 text-[11px] text-slate-400">
                  주민번호 (비우면 기존 유지)
                  {(() => {
                    const len = onlyDigits(draft.jumin).length || detail.jumin_len || 0;
                    if (len >= 13) return <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] font-medium text-emerald-700">✓ 13자리 등록됨</span>;
                    if (len >= 7) return <span className="rounded bg-amber-100 px-1.5 py-0.5 text-[10px] font-medium text-amber-700">△ 7자리만 등록</span>;
                    return <span className="rounded bg-rose-100 px-1.5 py-0.5 text-[10px] font-medium text-rose-600">⚠ 미등록 — 입력 필요</span>;
                  })()}
                </label>
                <input className={numInput + " text-sm"} value={draft.jumin} onChange={(e) => setD("jumin", formatJumin(e.target.value))} placeholder="000000-0000000" inputMode="numeric" />
                {(() => {
                  const len = onlyDigits(draft.jumin).length || detail.jumin_len || 0;
                  if (len >= 13) return <p className="mt-0.5 text-[10px] text-emerald-600">정식 고객등록·full pipeline 가능</p>;
                  if (len >= 7) return <p className="mt-0.5 text-[10px] text-amber-600">로그인·간편인증은 가능 — full pipeline(부가세·지급명세서 등)엔 13자리 권장. 가능하면 보완하세요.</p>;
                  return <p className="mt-0.5 text-[10px] text-rose-600">주민번호가 등록돼 있지 않습니다 — 13자리(최소 7자리) 입력이 필요합니다.</p>;
                })()}
              </div>

              {/* 간편인증용 */}
              <div className="rounded-lg border border-slate-100 bg-slate-50 p-2">
                <div className="mb-1 flex items-center gap-2 text-[11px] font-semibold text-slate-500">간편인증용 {detail.sa_phone ? <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] text-emerald-700">✓ 등록됨</span> : <span className="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] text-slate-500">미등록</span>}</div>
                <div className="grid grid-cols-2 gap-2">
                  <div><label className="mb-0.5 block text-[11px] text-slate-400">간편인증 휴대폰</label><input className={numInput + " text-sm"} value={draft.sa_phone} onChange={(e) => setD("sa_phone", formatPhone(e.target.value))} inputMode="numeric" placeholder={detail.sa_phone ? "비우면 기존 유지" : "010-0000-0000"} /></div>
                  <div><label className="mb-0.5 block text-[11px] text-slate-400">인증수단</label>
                    <select className={numInput + " text-sm"} value={draft.sa_provider} onChange={(e) => setD("sa_provider", e.target.value)}>{SA_PROVIDERS.map((p) => <option key={p} value={p}>{p}</option>)}</select></div>
                </div>
                {draft.sa_provider === "통신사PASS" && <div className="mt-1"><label className="mb-0.5 block text-[11px] text-slate-400">통신사</label>
                  <select className={numInput + " text-sm"} value={draft.telco} onChange={(e) => setD("telco", e.target.value)}>{TELCOS.map((t) => <option key={t} value={t}>{t}</option>)}</select></div>}
              </div>

              {/* ID/PW용 */}
              <div className="rounded-lg border border-slate-100 bg-slate-50 p-2">
                <div className="mb-1 flex items-center gap-2 text-[11px] font-semibold text-slate-500">홈택스 ID/PW용 {detail.has_idpw ? <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] text-emerald-700">등록됨</span> : <span className="rounded bg-rose-100 px-1.5 py-0.5 text-[10px] text-rose-600">미등록</span>}</div>
                <div className="grid grid-cols-2 gap-2">
                  <div><label className="mb-0.5 block text-[11px] text-slate-400">홈택스 아이디</label><input className={numInput + " text-sm"} value={draft.hometax_id} onChange={(e) => setD("hometax_id", e.target.value)} autoComplete="off" placeholder={detail.has_idpw ? "비우면 기존 유지" : "입력 필요"} /></div>
                  <div><label className="mb-0.5 block text-[11px] text-slate-400">비밀번호</label><input type="password" className={numInput + " text-sm"} value={draft.hometax_pw} onChange={(e) => setD("hometax_pw", e.target.value)} autoComplete="new-password" placeholder={detail.has_idpw ? "비우면 기존 유지" : "입력 필요"} /></div>
                </div>
              </div>

              <textarea rows={2} value={draft.memo} onChange={(e) => setD("memo", e.target.value)} placeholder="메모" className="w-full resize-none rounded-lg border border-slate-200 bg-white p-2 text-xs text-slate-700 focus:outline-none" />
              <div className="flex items-center gap-2">
                <button onClick={saveDetail} disabled={detailBusy} className="rounded-lg border border-slate-300 px-3 py-2 text-xs font-semibold text-slate-700 hover:bg-slate-50 disabled:opacity-50">{detailBusy ? "저장중…" : "수정 저장"}</button>
                {detailMsg && <span className="text-[11px] font-medium text-emerald-600">{detailMsg}</span>}
              </div>
            </div>

            {/* 파이프라인 재실행 (단일 런버튼 — 개별런 아님) — 방식 선택 */}
            <div className="space-y-2 rounded-2xl border border-orange-200 bg-orange-50 p-4 shadow-sm">
              <span className="block text-xs font-semibold text-orange-600">파이프라인 재실행 — 로그인 방식 선택</span>
              {/* 방식 토글: 간편인증 / ID·PW (ID·PW는 자격증명 없으면 비활성) */}
              <div className="flex gap-2">
                <button onClick={() => setRunMethod("sa")} className={`flex-1 rounded-lg border p-2 text-xs font-semibold transition ${runMethod === "sa" ? "border-orange-500 bg-white text-orange-700" : "border-slate-200 bg-white text-slate-500"}`}>간편인증</button>
                <button onClick={() => { if (detail.has_idpw || (draft.hometax_id && draft.hometax_pw)) setRunMethod("idpw"); else alert("홈택스 아이디·비밀번호가 등록되지 않아 ID/PW 방식을 쓸 수 없습니다.\n\n이유: ID/PW 로그인은 홈택스 계정 정보가 필요합니다.\n→ 위 '홈택스 ID/PW용'에 입력 후 저장하거나, 간편인증을 사용하세요."); }}
                  className={`flex-1 rounded-lg border p-2 text-xs font-semibold transition ${runMethod === "idpw" ? "border-orange-500 bg-white text-orange-700" : "border-slate-200 bg-white text-slate-500"} ${!(detail.has_idpw || (draft.hometax_id && draft.hometax_pw)) ? "opacity-50" : ""}`}>
                  ID·PW {!(detail.has_idpw || (draft.hometax_id && draft.hometax_pw)) && "🔒"}
                </button>
              </div>
              {/* 경고: ID/PW 미등록 → 간편인증만 가능 */}
              {!(detail.has_idpw || (draft.hometax_id && draft.hometax_pw)) && (
                <div className="rounded-lg border border-amber-200 bg-amber-50 p-2 text-[11px] text-amber-700">
                  ⚠️ <b>홈택스 ID/PW 미등록 → 간편인증만 사용 가능.</b><br />이유: ID/PW 방식 로그인은 홈택스 아이디·비밀번호가 있어야 합니다. (위 ‘홈택스 ID/PW용’에 입력·저장하면 ID/PW 방식이 열립니다.)
                </div>
              )}
              {/* 경고: 주민 13자리 아님 → full pipeline 제약 */}
              {!detail.has_jumin13 && onlyDigits(draft.jumin).length < 13 && (
                <div className="rounded-lg border border-amber-200 bg-amber-50 p-2 text-[11px] text-amber-700">
                  ⚠️ 주민 7자리만 등록 — 지금 로그인·견적은 가능하나, <b>추후 정식 고객등록 후 full pipeline에서 주민 13자리가 필요</b>할 수 있습니다.
                </div>
              )}
              <button onClick={rerunDetail} className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white hover:bg-orange-700">▶ 수정 저장 + 파이프라인 다시 실행 ({runMethod === "sa" ? "간편인증" : "ID·PW"})</button>
              {runMethod === "sa" && detail.status === "needs_auth" && (
                <button onClick={completeDetailSa} className="w-full rounded-lg bg-emerald-600 py-3 text-sm font-semibold text-white hover:bg-emerald-700">✅ 인증완료 (고객 승인 후 클릭)</button>
              )}
              {(detail.status === "running" || detail.status === "needs_auth") && (
                <button onClick={cancelDetail} className="w-full rounded-lg border border-rose-300 bg-rose-50 py-2 text-sm font-semibold text-rose-700 hover:bg-rose-100">⛔ 작업 중단 (브라우저 종료)</button>
              )}
              {(detail.status === "running" || detail.status === "needs_auth") && (
                <div className="space-y-0.5 rounded bg-white/70 p-2 font-mono text-[10px] text-slate-500">{(detail.logs || []).slice(-7).map((l, i) => <div key={i}>{l}</div>)}</div>
              )}
            </div>

            {/* 산출 파일 다운로드 */}
            <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
              <span className="mb-2 block text-xs font-semibold text-slate-500">산출 파일 ({(detail.files || []).length})</span>
              {(detail.files || []).length === 0 ? <p className="text-[11px] text-slate-400">아직 산출 파일이 없습니다.</p> : (
                <div className="space-y-1">
                  {detail.files.map((f, i) => (
                    <a key={i} href={f.url} target="_blank" rel="noreferrer" download className="flex items-center justify-between rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-blue-600 hover:bg-white">
                      <span>{f.module || "파일"}: {f.name}</span><span className="text-[10px] text-slate-400">⬇ 다운로드</span>
                    </a>
                  ))}
                </div>
              )}
            </div>

            {/* 삭제요청 (운영자 확인 후 실제 삭제) */}
            <div className="rounded-2xl border border-slate-200 bg-white p-3 text-center shadow-sm">
              <button onClick={requestDelete} disabled={detail.delete_request?.requested} className="text-xs font-medium text-rose-500 hover:text-rose-600 disabled:opacity-40">{detail.delete_request?.requested ? "삭제요청됨 (운영자 확인 대기)" : "🗑️ 운영자에게 삭제요청"}</button>
              <p className="mt-1 text-[10px] text-slate-400">영업사원은 직접 삭제할 수 없습니다. 운영자가 확인 후 삭제합니다.</p>
            </div>
          </div>
        );
      })()}

      {/* ─────────── 내 동선 탭 — 운영자가 보는 것과 동일한 동선/업무 자료(본인 고정) ─────────── */}
      {view === "route" && (
        <div className="rounded-2xl border border-slate-200 bg-white p-3 shadow-sm">
          <p className="mb-2 text-[11px] text-slate-400">운영자가 보는 것과 동일한 내 동선·업무 자료입니다. 날짜를 바꿔 과거 기록도 볼 수 있어요.</p>
          <SalesDashboard lockSp={sales} />
        </div>
      )}

      {/* 동선추적 OFF·위치 미기록 → 업무화면 잠금(상단 위치·동선 바만 노출) */}
      {view === "form" && !canWork && (
        <div className="rounded-2xl border border-dashed border-rose-300 bg-white p-8 text-center">
          <div className="text-3xl">📍</div>
          <p className="mt-2 text-sm font-semibold text-slate-700">동선 추적을 켜고 현재 위치를 기록하세요</p>
          <p className="mt-1 text-xs text-slate-500">{!tracking ? "동선 추적이 꺼져 있습니다." : "새 업무를 시작하려면 현재 위치 핀을 찍으세요."}<br />업무(견적·간편인증·고객등록)는 동선 추적 + 위치 핀 후에만 가능합니다. {tracking && <span className="text-emerald-600">동선 추적은 켜져 있습니다.</span>}</p>
          {actMsg && <p className="mt-2 text-xs font-medium text-emerald-600">{actMsg}</p>}
          <div className="mt-4 flex justify-center gap-2">
            {!tracking && <button onClick={startTracking} className="rounded-lg bg-emerald-600 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-700">▶ 동선 추적 켜기</button>}
            {!pinned && <button onClick={captureGeo} disabled={geoBusy} className="rounded-lg bg-orange-600 px-4 py-2 text-sm font-semibold text-white hover:bg-orange-700 disabled:opacity-50">{geoBusy ? "위치 확인중…" : "📍 현재 위치 기록"}</button>}
          </div>
        </div>
      )}

      {view === "form" && canWork && (<>

      {/* ★ 영업 활동 기록 — 맨 위. 견적 외 업무(정보못받음·부재중·재방문 등) 기록 후 페이지 초기화 */}
      <div className="mb-3 rounded-2xl border border-slate-300 bg-slate-50 p-3 shadow-sm">
        <span className="mb-1.5 block text-xs font-semibold text-slate-600">영업 활동 기록 <span className="font-normal text-slate-400">(견적 외 — 운영자 ‘영업사원’ 탭에 동선·업무로 남음)</span></span>
        <div className="flex gap-2">
          <select className={numInput + " text-xs"} value={actKind} onChange={(e) => setActKind(e.target.value)}>
            {ACT_KINDS.map((k) => <option key={k} value={k}>{k}</option>)}
          </select>
          <button onClick={recordActivity} className="shrink-0 rounded-lg bg-slate-800 px-3 py-2 text-xs font-semibold text-white hover:bg-slate-900">기록</button>
        </div>
        <p className="mt-1 text-[11px] text-slate-400">상호·전화·메모·위치가 함께 저장됩니다. ‘정보 받지못함’도 여기서 기록(매장 방문 확인). <b className="text-slate-500">기록 시 서버 반영 후 페이지가 초기화</b>됩니다.</p>
        {actMsg && <p className="mt-1 text-[11px] font-medium text-emerald-600">{actMsg}</p>}
      </div>

      {/* 고객 메모(공통·상시) — 전화·주소·상호·메모, 견적 산출 후 발송 전까지 계속 작성 */}
      <div className="mb-3 rounded-2xl border border-slate-200 bg-white p-3 shadow-sm">
        <span className="mb-1.5 block text-xs font-semibold text-slate-500">고객 메모 (상시 작성)</span>
        <div className="grid grid-cols-2 gap-2">
          <input className={numInput + " text-xs"} value={q.biz_name} onChange={(e) => setF("biz_name", e.target.value)} placeholder="상호명" />
          <input className={numInput + " text-xs"} value={q.phone} onChange={(e) => setF("phone", formatPhone(e.target.value))} inputMode="numeric" placeholder="연락/매장 전화" />
        </div>
        <input className={numInput + " mt-2 text-xs"} value={q.address} onChange={(e) => setF("address", e.target.value)} placeholder="주소" />
        <textarea rows={2} value={q.memo} onChange={(e) => setF("memo", e.target.value)} placeholder="요청사항·특이사항 메모" className="mt-2 w-full resize-none rounded-lg border border-slate-200 bg-white p-2 text-xs text-slate-700 focus:outline-none" />
      </div>


      {/* 고객 휴대폰 직접입력 QR (1회용) — 폼종류 선택 후 생성 */}
      <div className="mb-3 rounded-2xl border border-slate-200 bg-white p-4 text-center shadow-sm">
        <p className="mb-2 text-sm font-semibold text-slate-700">📱 고객 입력 QR 생성 <span className="font-normal text-slate-400">(폼 선택 · 1회용)</span></p>
        <div className="flex items-center gap-2">
          <select value={qrSel} onChange={(e) => setQrSel(e.target.value)} className="flex-1 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-orange-500 focus:outline-none focus:ring-2 focus:ring-orange-100">
            {QR_KINDS.map((k) => <option key={k.k} value={k.k}>{k.l}</option>)}
          </select>
          <button onClick={() => genHandoff(qrSel, (QR_KINDS.find((k) => k.k === qrSel) || {}).l)} className="shrink-0 rounded-lg bg-slate-800 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-900">QR 생성</button>
        </div>
        <p className="mt-1.5 text-[11px] text-slate-400">고객이 본인 폰으로 스캔 → 직접 입력(비번 영업사원이 안 받음). 1회용 링크.</p>
        {qrUrl && (
          <div className="mt-3">
            {qrKind && <p className="mb-1 text-xs font-semibold text-emerald-700">{qrKind} 링크 (생성됨)</p>}
            <img src={`https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=${encodeURIComponent(qrUrl)}`} alt="고객 입력 QR" className="mx-auto h-44 w-44 rounded-lg border border-slate-200" />
            <div className="mx-auto mt-2 flex max-w-xs items-center gap-2">
              <input readOnly value={qrUrl} className="w-full truncate rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-[11px] text-slate-500" />
              <button onClick={() => copy(qrUrl, "qr")} className="shrink-0 rounded-lg border border-slate-300 px-2 py-1 text-xs text-slate-600 hover:bg-slate-50">{copied === "qr" ? "복사됨" : "복사"}</button>
            </div>
          </div>
        )}
      </div>

      {/* 간편인증/자동/수동/법인 토글 — 한 줄 */}
      <div className="mb-3 flex gap-1 rounded-lg bg-slate-100 p-1">
        {[{ k: "sa", l: "간편인증" }, { k: "auto", l: "자동(ID/PW)" }, { k: "manual", l: "수동견적" }, { k: "corp", l: "법인견적" }].map((m) => (
          <button key={m.k} onClick={() => setCalcMode(m.k)} className={`flex-1 rounded-md px-0.5 py-1.5 text-[11px] font-medium transition ${calcMode === m.k ? "bg-white text-slate-800 shadow-sm" : "text-slate-500 hover:text-slate-700"}`}>{m.l}</button>
        ))}
      </div>

      {/* 자동 산출 (홈택스 로그인→신고안내자료→계산) */}
      {calcMode === "auto" && (
        <div className="space-y-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
          <p className="text-[11px] leading-relaxed text-slate-400">홈택스 로그인 → 신고안내자료 → 소득세계산 (1~2분 소요). ID/PW + 주민 7자리 필요.</p>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">고객 성명</label><input className={numInput} value={auto.name} onChange={(e) => setA("name", e.target.value)} placeholder="홍길동" /></div>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">홈택스 아이디</label><input className={numInput} value={auto.hometax_id} onChange={(e) => setA("hometax_id", e.target.value)} autoComplete="off" placeholder="hometax_id" /></div>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">비밀번호</label>
            <div className="flex items-center gap-2">
              <input className={numInput} type={autoShowPw ? "text" : "password"} value={auto.hometax_pw} onChange={(e) => setA("hometax_pw", e.target.value)} autoComplete="new-password" placeholder="••••••••" />
              <button type="button" onClick={() => setAutoShowPw((s) => !s)} className="shrink-0 rounded px-2 py-0.5 text-xs text-slate-400 hover:bg-slate-100">{autoShowPw ? "가림" : "표시"}</button>
            </div>
          </div>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">주민번호 (앞 7자리)</label><input className={numInput} inputMode="numeric" value={auto.jumin} onChange={(e) => setA("jumin", formatJuminShort(e.target.value))} placeholder="000000-0" /></div>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">귀속연도 <span className="font-normal text-slate-400">(연도별로 자료·계산이 달라집니다)</span></label>
            <select className={numInput} value={quoteYear} onChange={(e) => setQuoteYear(Number(e.target.value))}>
              {YEARS.map((y) => <option key={y} value={y}>{y}년 귀속</option>)}
            </select></div>
          <button onClick={runAuto} disabled={autoBusy} className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white hover:bg-orange-700 disabled:bg-slate-300">{autoBusy ? "산출 중… (1~2분)" : "자동 산출"}</button>
          {autoRes && (
            <div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs">
              <div className="mb-1 font-medium text-slate-600">상태: {STATUS_META[autoRes.status]?.label || autoRes.status}</div>
              {estBlock(autoRes)}
              {autoRes.quote_info && <pre className="mb-2 whitespace-pre-wrap rounded bg-white p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-slate-100">{autoRes.quote_info}</pre>}
              {autoRes.quote_text && (
                <div>
                  <button onClick={() => copy(autoRes.quote_text, "autotext")} className="mb-1 rounded border border-slate-300 px-2 py-0.5 text-[11px] text-slate-500 hover:bg-white">{copied === "autotext" ? "복사됨" : "청구 발송문 복사"}</button>
                  <pre className="max-h-40 overflow-y-auto whitespace-pre-wrap rounded bg-white p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-slate-100">{autoRes.quote_text}</pre>
                </div>
              )}
              {!autoRes.quote_info && autoRes.status === "running" && (
                <div className="space-y-0.5 font-mono text-[10px] text-slate-500">{(autoRes.logs || []).slice(-6).map((l, i) => <div key={i}>{l}</div>)}</div>
              )}
            </div>
          )}
        </div>
      )}

      {/* 간편인증 산출 (간편인증 → 로그인 → 안내문 → 소득세계산 → 견적) */}
      {calcMode === "sa" && (
        <div className="space-y-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
          <p className="text-[11px] leading-relaxed text-slate-400">
            인증수단 선택 + 성명·주민7·휴대폰 입력 → <b>간편인증 보내기</b> → 고객 폰 승인 →
            <b> 인증완료</b> 누르면 홈택스 로그인 → 신고안내자료 → 소득세계산 → 견적까지 자동.
            <span className="text-slate-300"> (ID/PW 불필요)</span>
          </p>

          {/* 인증수단 */}
          <div>
            <label className="mb-1 block text-xs font-medium text-slate-500">인증수단</label>
            <div className="grid grid-cols-3 gap-2">
              {SA_PROVIDERS.map((p) => (
                <button key={p} type="button" disabled={saStage !== "idle"} onClick={() => setS("provider", p)} className={`rounded-lg border p-2 text-xs font-medium transition disabled:opacity-50 ${sa.provider === p ? "border-orange-500 bg-orange-50 text-orange-700" : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50"}`}>{p}</button>
              ))}
            </div>
          </div>

          <div><label className="mb-1 block text-xs font-medium text-slate-500">고객 성명</label>
            <input className={numInput} value={sa.name} disabled={saStage !== "idle"} onChange={(e) => setS("name", e.target.value)} placeholder="홍길동" /></div>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">주민번호 (앞 7자리 = 생년월일6+뒤1)</label>
            <input className={numInput} inputMode="numeric" value={sa.jumin} disabled={saStage !== "idle"} onChange={(e) => setS("jumin", formatJuminShort(e.target.value))} placeholder="000000-0" /></div>
          <div><label className="mb-1 block text-xs font-medium text-slate-500">간편인증 휴대폰 <span className="text-[10px] font-normal text-slate-400">(인증 푸시 받을 본인 번호 — 매장전화 아님)</span></label>
            <input className={numInput} inputMode="numeric" value={sa.phone} disabled={saStage !== "idle"} onChange={(e) => setS("phone", formatPhone(e.target.value))} placeholder="010-0000-0000" /></div>
          {sa.provider === "통신사PASS" && (
            <div><label className="mb-1 block text-xs font-medium text-slate-500">통신사</label>
              <select className={numInput} value={sa.telco} disabled={saStage !== "idle"} onChange={(e) => setS("telco", e.target.value)}>
                {TELCOS.map((t) => <option key={t} value={t}>{t}</option>)}
              </select></div>
          )}
          <div><label className="mb-1 block text-xs font-medium text-slate-500">귀속연도 <span className="font-normal text-slate-400">(연도별로 자료·계산이 달라집니다)</span></label>
            <select className={numInput} value={quoteYear} disabled={saStage !== "idle"} onChange={(e) => setQuoteYear(Number(e.target.value))}>
              {YEARS.map((y) => <option key={y} value={y}>{y}년 귀속</option>)}
            </select></div>

          {/* 단계별 액션 */}
          {saStage === "idle" && (
            <button onClick={sendSa} className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white hover:bg-orange-700">📲 간편인증 보내기</button>
          )}
          {saStage === "sent" && (
            <div className="space-y-2">
              <div className="rounded-lg border border-amber-200 bg-amber-50 p-3 text-xs text-amber-700">
                📲 <b>고객 휴대폰({sa.provider})으로 인증요청 발송됨.</b><br />고객이 폰에서 승인하면 아래 <b>인증완료</b>를 누르세요.
              </div>
              <button onClick={completeSa} className="w-full rounded-lg bg-emerald-600 py-3 text-sm font-semibold text-white hover:bg-emerald-700">✅ 인증완료 (고객 승인 후 클릭)</button>
              <button onClick={resetSa} className="w-full rounded-lg border border-slate-300 py-2 text-xs font-medium text-slate-500 hover:bg-slate-50">취소 / 다시</button>
            </div>
          )}
          {saStage === "running" && (
            <div className="rounded-lg border border-blue-200 bg-blue-50 p-3 text-xs text-blue-700">⏳ 인증완료 → 로그인 → 신고안내자료 → 소득세계산 → 견적 진행 중… (1~2분)</div>
          )}
          {(saStage === "done" || saStage === "failed") && (
            <button onClick={resetSa} className="w-full rounded-lg border border-slate-300 py-2 text-sm font-medium text-slate-600 hover:bg-slate-50">새 간편인증 산출</button>
          )}

          {/* 진행 로그 / 결과 */}
          {saRes && (
            <div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs">
              <div className="mb-1 font-medium text-slate-600">상태: {STATUS_META[saRes.status]?.label || saRes.status}</div>
              {estBlock(saRes)}
              {saRes.quote_info && <pre className="mb-2 whitespace-pre-wrap rounded bg-white p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-slate-100">{saRes.quote_info}</pre>}
              {saRes.quote_text && (
                <div>
                  <button onClick={() => copy(saRes.quote_text, "satext")} className="mb-1 rounded border border-slate-300 px-2 py-0.5 text-[11px] text-slate-500 hover:bg-white">{copied === "satext" ? "복사됨" : "청구 발송문 복사"}</button>
                  <pre className="max-h-40 overflow-y-auto whitespace-pre-wrap rounded bg-white p-2 text-[11px] leading-relaxed text-slate-700 ring-1 ring-slate-100">{saRes.quote_text}</pre>
                </div>
              )}
              {!saRes.quote_info && (saRes.logs || []).length > 0 && (
                <div className="space-y-0.5 font-mono text-[10px] text-slate-500">{(saRes.logs || []).slice(-7).map((l, i) => <div key={i}>{l}</div>)}</div>
              )}
            </div>
          )}
        </div>
      )}

      {/* 수동 견적 / 법인 견적 (같은 입력폼, 법인은 법인으로 기록) */}
      {(calcMode === "manual" || calcMode === "corp") && (<>
      <div className="space-y-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
        {calcMode === "corp" && (
          <div className="rounded-lg border border-violet-200 bg-violet-50 p-2.5 text-[11px] leading-relaxed text-violet-700">🏢 <b>법인 견적</b> — 법인 고객으로 기록됩니다. 보수료는 아래 수동 입력(매출·할증·할인)으로 산정합니다.</div>
        )}
        {/* 고객명 */}
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">고객명</label>
          <input className={numInput} value={q.name} onChange={(e) => setF("name", e.target.value)} placeholder="홍길동" />
        </div>

        {/* 수입금액 + 기장유형 */}
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">연 매출(수입금액)</label>
          <input className={numInput} inputMode="numeric" value={q.revenue} onChange={(e) => setF("revenue", onlyDigits(e.target.value))} placeholder="예: 29628429" />
          {q.revenue && <p className="mt-0.5 text-[11px] text-slate-400">{won(Number(q.revenue))}</p>}
        </div>
        {calcMode === "corp" && (
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">세액공제·감면액 <span className="font-normal text-slate-400">(이 금액의 5% 가산)</span></label>
          <input className={numInput} inputMode="numeric" value={q.tax_credit} onChange={(e) => setF("tax_credit", onlyDigits(e.target.value))} placeholder="예: 12000000" />
          {q.tax_credit && (() => { const c = Math.round(Number(onlyDigits(q.tax_credit)) * 0.05); return <p className="mt-0.5 text-[11px] text-violet-600">{won(Number(q.tax_credit))} × 5% = 가산 <b>{won(c)}</b></p>; })()}
        </div>
        )}
        {calcMode !== "corp" && (
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">기장 유형</label>
          <div className="grid grid-cols-2 gap-2">
            {[{ k: "simple", l: "간편장부" }, { k: "double", l: "복식부기" }].map((b) => (
              <button key={b.k} type="button" onClick={() => setF("book_type", b.k)} className={`rounded-lg border p-2 text-sm font-medium transition ${q.book_type === b.k ? "border-orange-500 bg-orange-50 text-orange-700" : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50"}`}>{b.l}</button>
            ))}
          </div>
        </div>
        )}

        {/* 요율표 선택 — 법인은 법인 요율표 고정(선택 숨김) */}
        {calcMode === "corp" ? (
          <p className="text-[11px] text-violet-600">적용 요율표: <b>세무사회 권고기준</b> (법인사업자 · 수입금액 구간별)</p>
        ) : (
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">적용 요율표</label>
          <select className={numInput} value={q.rate_table} onChange={(e) => setF("rate_table", e.target.value)}>
            <option value="kmong">크몽 기장용 (현재)</option>
            <option value="세무사회">세무사회 권고기준</option>
          </select>
          {q.rate_table === "세무사회" && <p className="mt-0.5 text-[11px] text-amber-600">※ 세무사회 요율표 값 입력 전 — 임시로 크몽 요율 적용됨</p>}
        </div>
        )}

        {/* 사업장 수 / 근연기 — 개인 전용 */}
        {calcMode !== "corp" && (
        <div className="grid grid-cols-2 gap-2">
          <div>
            <label className="mb-1 block text-xs font-medium text-slate-500">사업장 수</label>
            <input className={numInput} inputMode="numeric" value={q.biz_count} onChange={(e) => setF("biz_count", onlyDigits(e.target.value) || 1)} />
          </div>
          <div>
            <label className="mb-1 block text-xs font-medium text-slate-500">근·연·기 추가</label>
            <input className={numInput} inputMode="numeric" value={q.extra_count} onChange={(e) => setF("extra_count", onlyDigits(e.target.value) || 0)} />
          </div>
        </div>
        )}

        {/* 금융/임대 — 개인 전용 */}
        {calcMode !== "corp" && (
        <div className="flex gap-2">
          {[{ k: "financial", l: "금융소득" }, { k: "rental", l: "주택임대소득" }].map((o) => (
            <button key={o.k} type="button" onClick={() => setF(o.k, !q[o.k])} className={`flex-1 rounded-lg border p-2 text-xs font-medium transition ${q[o.k] ? "border-orange-500 bg-orange-50 text-orange-700" : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50"}`}>{o.l} {q[o.k] ? "✓" : ""}</button>
          ))}
        </div>
        )}

        {/* 할증 / 할인 */}
        <div className="grid grid-cols-2 gap-2">
          <div>
            <label className="mb-1 block text-xs font-medium text-slate-500">할증(원)</label>
            <input className={numInput} inputMode="numeric" value={q.surcharge} onChange={(e) => setF("surcharge", onlyDigits(e.target.value))} placeholder="0" />
            <input className={numInput + " mt-1 text-xs"} value={q.surcharge_reason} onChange={(e) => setF("surcharge_reason", e.target.value)} placeholder="할증 사유" />
          </div>
          <div>
            <label className="mb-1 block text-xs font-medium text-slate-500">할인(원)</label>
            <input className={numInput} inputMode="numeric" value={q.discount} onChange={(e) => setF("discount", onlyDigits(e.target.value))} placeholder="0" />
            <input className={numInput + " mt-1 text-xs"} value={q.discount_reason} onChange={(e) => setF("discount_reason", e.target.value)} placeholder="할인 사유" />
          </div>
        </div>

        <button onClick={runCalc} disabled={busy} className="w-full rounded-lg bg-orange-600 py-3 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:bg-slate-300">{busy ? "산출 중…" : "견적 산출"}</button>
      </div>

      {/* 견적 결과 */}
      {calc && (
        <div className="mt-4 rounded-2xl border border-orange-200 bg-gradient-to-b from-orange-50 to-white p-5 shadow-sm">
          <div className="mb-1 text-center text-xs text-slate-500">{q.name || "고객"} · {calc.book_type_text} · {calc.bracket_label}</div>
          <div className="mb-3 text-center">
            <div className="text-xs text-slate-400">{calc.is_corp ? "법인 세무대행 보수료" : "종합소득세 세무조정료"} (부가세 포함){calc.total_trim > 0 ? " · 입금액" : ""}</div>
            <div className="text-3xl font-bold text-orange-600">{won(calc.total)}</div>
            <div className="mt-0.5 text-xs text-slate-400">공급가 {won(calc.supply)} + 부가세 {won(calc.vat)}{calc.total_trim > 0 ? ` = ${won(calc.total_raw)}` : ""}</div>
            {calc.total_trim > 0 && <div className="mt-0.5 text-[11px] text-rose-500">만원단위 버림 −{won(calc.total_trim)}</div>}
          </div>
          <dl className="space-y-1 rounded-lg bg-white p-3 text-sm ring-1 ring-slate-100">
            <div className="flex justify-between"><dt className="text-slate-500">기본 보수료</dt><dd>{won(calc.base_fee)}</dd></div>
            {calc.fin_fee > 0 && <div className="flex justify-between"><dt className="text-slate-500">금융/임대 추가</dt><dd>{won(calc.fin_fee)}</dd></div>}
            {calc.extra_fee > 0 && <div className="flex justify-between"><dt className="text-slate-500">근·연·기 추가 ({calc.extra_count}건)</dt><dd>{won(calc.extra_fee)}</dd></div>}
            {calc.biz_fee > 0 && <div className="flex justify-between"><dt className="text-slate-500">사업장 추가 ({calc.extra_biz}곳)</dt><dd>{won(calc.biz_fee)}</dd></div>}
            {calc.credit_fee > 0 && <div className="flex justify-between"><dt className="text-slate-500">세액공제·감면 가산 ({won(calc.tax_credit)} × 5%)</dt><dd>+{won(calc.credit_fee)}</dd></div>}
            {calc.surcharge > 0 && <div className="flex justify-between"><dt className="text-slate-500">할증 {calc.surcharge_reason && `(${calc.surcharge_reason})`}</dt><dd>+{won(calc.surcharge)}</dd></div>}
            {calc.discount > 0 && <div className="flex justify-between"><dt className="text-slate-500">할인 {calc.discount_reason && `(${calc.discount_reason})`}</dt><dd className="text-rose-500">-{won(calc.discount)}</dd></div>}
          </dl>
          {calc.text && (
            <div className="mt-3">
              <button onClick={() => copy(calc.text, "calctext")} className="mb-1 rounded border border-slate-300 px-2 py-0.5 text-[11px] text-slate-500 hover:bg-white">{copied === "calctext" ? "복사됨" : "청구 발송문 복사"}</button>
              <pre className="max-h-40 overflow-y-auto whitespace-pre-wrap rounded-lg bg-white p-2.5 text-[11px] leading-relaxed text-slate-600 ring-1 ring-slate-100">{calc.text}</pre>
            </div>
          )}
          {/* 접수 완료 — 산출 시 이미 일자별 리스트에 자동 접수됨(고객정보·청구서 포함) */}
          <div className="mt-3">
            {manualRid && <p className="mb-1.5 text-center text-[11px] font-medium text-emerald-600">✓ 일자별 고객목록에 자동 접수됨 (고객정보·청구서 포함) — 운영자도 즉시 확인</p>}
            <button onClick={saveReq} className="w-full rounded-lg bg-slate-800 py-2.5 text-sm font-semibold text-white hover:bg-slate-900">{manualRid ? "접수 완료 · 정식등록 링크 만들기" : "접수 저장 (운영자에게 전달 + 정식등록 링크)"}</button>
          </div>
        </div>
      )}
      </>)}

      {/* 접수하기 — 현재 고객 마감(다음 고객 위치기록을 위한 필수 단계) */}
      {pendingFinalize && (
        <div className="mt-3 rounded-2xl border-2 border-orange-300 bg-orange-50 p-4 text-center shadow-sm">
          <p className="text-sm font-semibold text-orange-700">이 고객 작업이 고객목록에 등록되었습니다.</p>
          <p className="mb-3 mt-1 text-[11px] leading-relaxed text-orange-500">다음 고객으로 넘어가려면 ‘접수하기’로 마감하세요.<br />마감 후 새 위치 기록(📍)을 찍어야 다음 업무가 열립니다.</p>
          <button onClick={finalizeIntake} className="w-full rounded-lg bg-orange-600 py-3 text-sm font-bold text-white hover:bg-orange-700">접수하기 (완료 · 다음 고객)</button>
        </div>
      )}

      {/* 기준 안내 (#3 #4) */}
      <div className="mt-4 rounded-xl border border-slate-200 bg-white p-4 text-xs text-slate-600 shadow-sm">
        <span className="mb-2 block text-sm font-semibold text-slate-700">기준 안내 (고객 설명용)</span>
        <ul className="space-y-1.5">
          {STD_INFO.map((s, i) => <li key={i} className="flex gap-1.5"><span className="text-orange-400">•</span><span className="leading-relaxed">{s}</span></li>)}
        </ul>
        {calc?.brackets && (() => {
          const isRate = calc.brackets[0] && calc.brackets[0].base !== undefined;
          return (
          <div className="mt-3">
            <span className="mb-1 block text-[11px] font-medium text-slate-400">{isRate ? (calcMode === "corp" ? "법인사업자 보수료 구간표" : "세무사회 권고 보수료 구간표 (개인사업자)") : "종합소득세 세무조정료 구간표"}</span>
            <table className="w-full text-[11px]">
              {isRate ? (
                <thead className="text-slate-400"><tr><th className="py-1 text-left font-medium">수입금액</th><th className="text-right font-medium">기본료</th><th className="text-right font-medium">초과액 요율</th></tr></thead>
              ) : (
                <thead className="text-slate-400"><tr><th className="py-1 text-left font-medium">수입금액</th><th className="text-right font-medium">간편</th><th className="text-right font-medium">복식</th></tr></thead>
              )}
              <tbody className="divide-y divide-slate-100">
                {calc.brackets.map((b, i) => (
                  isRate ? (
                    <tr key={i}><td className="py-1 text-slate-600">{b.label}</td><td className="text-right">{won(b.base)}</td><td className="text-right">{b.rate10k ? `+${b.rate10k}/만` : "—"}</td></tr>
                  ) : (
                    <tr key={i}><td className="py-1 text-slate-600">{b.label}</td><td className="text-right">{won(b.simple)}</td><td className="text-right">{won(b.double)}</td></tr>
                  )
                ))}
              </tbody>
            </table>
            <p className="mt-1 text-[10px] text-slate-400">{isRate ? "※ 기본료 + (수입금액 − 구간하한) × 요율(만분율). 간편·복식 동일." : "⚠️ 부가세·법인세 세무조정료 견적은 미구현(종합소득세 세무조정료 기준)"}</p>
          </div>
          );
        })()}
      </div>
      </>)}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 운영자 페이지
// ─────────────────────────────────────────────────────────────
// 영업사원 대시보드 — 인원별 하루 동선 + 업무 정리.
// 운영자: 인원 자유선택. 영업사원 본인탭: lockSp로 본인 고정(드롭다운 대신 고정표시).
function SalesDashboard({ lockSp }) {
  const todayStr = (() => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; })();
  const [salesList, setSalesList] = useState(["김영업"]);
  const [sp, setSp] = useState(lockSp || "김영업");
  const [date, setDate] = useState(todayStr);
  const [acts, setActs] = useState([]);
  const [loading, setLoading] = useState(false);
  useEffect(() => { if (lockSp) { setSp(lockSp); return; } apiGet("/api/salespeople", ["김영업"]).then((l) => { if (Array.isArray(l) && l.length) { setSalesList(l); setSp((cur) => l.includes(cur) ? cur : l[0]); } }); }, [lockSp]);
  const load = useCallback(async () => {
    if (!sp) return;
    setLoading(true);
    const r = await apiGet(`/api/sales/activity?salesperson=${encodeURIComponent(sp)}&date=${date}`, []);
    setActs(Array.isArray(r) ? r : []);
    setLoading(false);
  }, [sp, date]);
  useEffect(() => { load(); }, [load]);
  // 오늘 날짜면 자동 갱신(영업사원 동선·이동 실시간 반영)
  const [live, setLive] = useState(true);
  useEffect(() => {
    if (date !== todayStr || !live) return;
    const t = setInterval(load, 15000);
    return () => clearInterval(t);
  }, [date, live, load, todayStr]);

  const geoPts = acts.filter((a) => a.lat && a.lng);
  const geoNum = {};   // 활동 id → 지도 번호(타임라인 대조용)
  geoPts.forEach((a, i) => { geoNum[a.id] = i + 1; });
  const lastPt = geoPts.length ? geoPts[geoPts.length - 1] : null;
  const cnt = (pfx) => acts.filter((a) => String(a.kind || "").startsWith(pfx)).length;
  const summary = [
    { l: "견적", n: cnt("견적"), c: "bg-orange-100 text-orange-700" },
    { l: "위치갱신", n: cnt("위치정보 갱신"), c: "bg-blue-100 text-blue-700" },
    { l: "정보못받음", n: cnt("정보 받지못함"), c: "bg-rose-100 text-rose-700" },
    { l: "부재중", n: cnt("부재중"), c: "bg-slate-100 text-slate-600" },
    { l: "재방문", n: cnt("재방문"), c: "bg-amber-100 text-amber-700" },
    { l: "거절", n: cnt("상담 거절"), c: "bg-slate-100 text-slate-600" },
  ];
  const kindColor = (k) => k?.startsWith("견적") ? "bg-orange-500" : k?.startsWith("위치") ? "bg-blue-500" : k?.startsWith("정보") ? "bg-rose-500" : k?.startsWith("부재") ? "bg-slate-400" : k?.startsWith("재방문") ? "bg-amber-500" : k?.startsWith("상담 거절") ? "bg-slate-500" : "bg-slate-400";

  return (
    <div>
      {/* 인원·날짜 선택 */}
      <div className="mb-4 flex flex-wrap items-end gap-2">
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">영업사원</label>
          {lockSp ? (
            <div className="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm font-semibold text-slate-700">{lockSp}</div>
          ) : (
            <select value={sp} onChange={(e) => setSp(e.target.value)} className="rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-orange-400 focus:outline-none">
              {salesList.map((n) => <option key={n} value={n}>{n}</option>)}
            </select>
          )}
        </div>
        <div>
          <label className="mb-1 block text-xs font-medium text-slate-500">날짜</label>
          <input type="date" value={date} onChange={(e) => setDate(e.target.value)} className="rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-orange-400 focus:outline-none" />
        </div>
        <button onClick={() => setDate(todayStr)} className="rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-600 hover:bg-slate-50">오늘</button>
        <button onClick={load} className="rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-600 hover:bg-slate-50">새로고침</button>
      </div>

      {/* 실시간 동선 상태 (오늘 자동 갱신) */}
      <div className="mb-3 flex flex-wrap items-center gap-2 rounded-lg border border-slate-200 bg-white px-3 py-2 text-xs">
        {date === todayStr ? (
          <button onClick={() => setLive((v) => !v)} className={`flex items-center gap-1 rounded-full px-2 py-0.5 font-medium ${live ? "bg-rose-50 text-rose-600" : "bg-slate-100 text-slate-500"}`}>
            <span className={`h-2 w-2 rounded-full ${live ? "bg-rose-500 animate-pulse" : "bg-slate-400"}`} />{live ? "실시간 갱신중 (15초)" : "실시간 꺼짐"}
          </button>
        ) : <span className="text-slate-400">과거 기록 (실시간 아님)</span>}
        {lastPt ? (
          <span className="text-slate-600">현재 위치: <a href={`https://maps.google.com/?q=${lastPt.lat},${lastPt.lng}`} target="_blank" rel="noreferrer" className="text-blue-600 underline">{lastPt.lat},{lastPt.lng}</a> <span className="text-slate-400">({String(lastPt.created_at || "").slice(11)} 기준)</span></span>
        ) : <span className="text-slate-400">위치 기록 없음</span>}
      </div>

      {/* 업무 요약 */}
      <div className="mb-4 flex flex-wrap gap-2">
        <span className="rounded-full bg-slate-800 px-3 py-1 text-sm font-semibold text-white">총 활동 {acts.length}</span>
        {summary.map((s) => <span key={s.l} className={`rounded-full px-3 py-1 text-sm font-medium ${s.c}`}>{s.l} {s.n}</span>)}
      </div>

      {/* 동선 지도 (좌표를 선으로 연결) */}
      {geoPts.length > 0 && (
        <div className="mb-4 rounded-xl border border-slate-200 bg-white p-3 shadow-sm">
          <span className="mb-2 block text-xs font-semibold text-slate-500">{sp} · {date} 동선 지도 ({geoPts.length}곳) <span className="font-normal text-slate-400">— ①②③ 번호 = 아래 동선/업무 타임라인과 대조 · 빨강=현재위치</span></span>
          <RouteMap points={geoPts.map((a) => ({ lat: a.lat, lng: a.lng, label: `${a.kind || ""}${a.biz_name ? " " + a.biz_name : ""}`, time: String(a.created_at || "").slice(11) }))} height={320} />
        </div>
      )}

      {/* 동선 타임라인 */}
      <div className="rounded-xl border border-slate-200 bg-white p-3 shadow-sm">
        <span className="mb-2 block text-xs font-semibold text-slate-500">{sp} · {date} 동선 / 업무</span>
        {loading ? (
          <div className="py-10 text-center text-sm text-slate-400">불러오는 중…</div>
        ) : acts.length === 0 ? (
          <div className="py-10 text-center text-sm text-slate-400">이 날짜의 활동 기록이 없습니다.</div>
        ) : (
          <ol className="relative ml-2 border-l border-slate-200">
            {acts.map((a, i) => (
              <li key={a.id || i} className="mb-3 ml-4">
                <span className={`absolute -left-[7px] mt-1 h-3 w-3 rounded-full ${kindColor(a.kind)}`} />
                <div className="flex flex-wrap items-center gap-2">
                  {geoNum[a.id] && <span className={`flex h-5 w-5 items-center justify-center rounded-full text-[10px] font-bold text-white ${geoNum[a.id] === geoPts.length ? "bg-rose-600" : "bg-blue-600"}`} title="지도 번호">{geoNum[a.id]}</span>}
                  <span className="font-mono text-xs text-slate-400">{String(a.created_at || "").slice(11)}</span>
                  <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[11px] font-medium text-slate-700">{a.kind}</span>
                  {a.biz_name && <span className="text-sm font-medium text-slate-800">{a.biz_name}</span>}
                  {a.phone && <span className="text-xs text-slate-500">{a.phone}</span>}
                  {a.lat && <a href={`https://maps.google.com/?q=${a.lat},${a.lng}`} target="_blank" rel="noreferrer" className="text-[11px] text-blue-600 underline">📍</a>}
                  {a.request_id && <span className="font-mono text-[10px] text-slate-400">{a.request_id}</span>}
                </div>
                {a.memo && <p className="mt-0.5 whitespace-pre-wrap text-xs text-slate-500">{a.memo}</p>}
              </li>
            ))}
          </ol>
        )}
      </div>
    </div>
  );
}

function OperatorPage({ canDelete = true, isClerk = false }) {
  const won = (n) => (n == null ? "—" : Number(n).toLocaleString("ko-KR") + "원");
  const [salesView, setSalesView] = useState(false);   // 영업사원 최상위 탭
  const [requests, setRequests] = useState([]);
  const [taxType, setTaxType] = useState("all"); // jongso|vat|corp|all
  const [stage, setStage] = useState("quote"); // quote|progress|done
  const [selected, setSelected] = useState(null);
  const [detail, setDetail] = useState(null);
  const [loading, setLoading] = useState(true);
  const [meta, setMeta] = useState({ platform: "", cust_label: "", op_memo: "" });
  const [savingMeta, setSavingMeta] = useState(false);
  const [savedMeta, setSavedMeta] = useState(false);
  const [copied, setCopied] = useState("");
  const [showCreds, setShowCreds] = useState(false);
  const [opEdit, setOpEdit] = useState(false);       // 고객정보 수정 패널 토글
  const [opDraft, setOpDraft] = useState(null);      // 수정 입력 버퍼
  const [opSaving, setOpSaving] = useState(false);
  const [search, setSearch] = useState("");
  const [searchCol, setSearchCol] = useState("all");
  // 실행 패널: 모듈 선택 / 사업자 선택 / 귀속연도
  const [runModules, setRunModules] = useState({});
  const [runBiz, setRunBiz] = useState("all");
  const [runYear, setRunYear] = useState(CURRENT_YEAR - 1);
  const [page, setPage] = useState(1);
  const PAGE_SIZE = 10;
  const [view, setView] = useState("list");   // list=목록 / detail=고객 상세 탭
  const [openTabs, setOpenTabs] = useState([]);   // 동시 오픈 고객 탭 [{request_id,name}]
  const closeTab = (id) => {
    setOpenTabs((ts) => ts.filter((t) => t.request_id !== id));
    if (detail?.request_id === id) { setDetail(null); setSelected(null); setView("list"); }
  };
  const [spText, setSpText] = useState("김영업");   // 영업사원 목록(쉼표구분, 운영자 편집)
  const [genLink, setGenLink] = useState(null);     // 방금 생성한 1회용 링크 {url,label}
  const [genKind, setGenKind] = useState("quote");  // 고객 안내 링크 종류 선택(드롭다운)
  useEffect(() => { apiGet("/api/salespeople", ["김영업"]).then((l) => setSpText((l || ["김영업"]).join(", "))); }, []);
  const saveSalespeople = async () => {
    const names = spText.split(",").map((s) => s.trim()).filter(Boolean);
    const r = await apiPost("/api/salespeople", { names }, null);
    if (r?.names) { setSpText(r.names.join(", ")); alert("영업사원 목록 저장됨"); }
  };

  // 실행 가능한 모듈 — 자료수집(개인/사업자) + 계산·산출
  const MODULES_PERSONAL = [   // 개인 자료수집 (ID/PW 가능)
    { key: "jongso_calc", label: "신고안내자료+소득세계산" },
    { key: "invoice", label: "견적·청구서" },
    { key: "jongso_prev", label: "전기 종소세 신고서" },
    { key: "jongso_current", label: "당기 종소세 신고서" },
  ];
  const MODULES_SIMPLEAUTH = [ // 간편인증 있어야 조회됨
    { key: "yearend", label: "연말정산 간소화" },
    { key: "jungmyung", label: "근로지급명세서" },
  ];
  const MODULES_BIZ = [        // 사업자 자료수집
    { key: "vat", label: "부가세 신고서" },
    { key: "wht", label: "원천세 신고서" },
    { key: "maemae", label: "매출매입 일괄(5종)" },
    { key: "detail", label: "매출매입 상세" },
  ];
  const MODULES_CALC = [       // 계산·산출 (가공물)
    { key: "yearend_convert", label: "연말정산 엑셀변환" },
    { key: "tonghap", label: "통합지출요약" },
  ];
  // 산출물 완전성 체크리스트 — 자료 종류(모듈) 별 그룹
  const DATA_TYPES = [
    { mod: "계산", label: "신고안내자료·소득세계산" },
    { mod: "청구서", label: "견적·청구서" },
    { mod: "종소세", label: "종소세 신고서" },
    { mod: "연말정산", label: "연말정산 간소화" },
    { mod: "연말정산변환", label: "연말정산 엑셀변환" },
    { mod: "근로지급명세서", label: "근로지급명세서" },
    { mod: "부가세", label: "부가세 신고서" },
    { mod: "원천세", label: "원천세 신고서" },
    { mod: "매출매입일괄", label: "매출매입 일괄(5종)" },
    { mod: "매출매입상세", label: "매출매입 상세" },
    { mod: "전자세금계산서", label: "전자세금계산서" },
    { mod: "지방소득세", label: "지방소득세 접수증" },
    { mod: "통합지출요약", label: "통합지출요약" },
  ];
  const ALL_MODULES = [...MODULES_PERSONAL, ...MODULES_SIMPLEAUTH, ...MODULES_BIZ, ...MODULES_CALC];

  // 검색 가능한 열 정의 (각 행에서 값 추출)
  const SEARCH_COLS = [
    { key: "all", label: "전체 열" },
    { key: "request_id", label: "접수번호", get: (r) => r.request_id },
    { key: "name", label: "고객명", get: (r) => r.name },
    { key: "platform", label: "플랫폼", get: (r) => r.platform },
    { key: "cust_label", label: "고객 라벨", get: (r) => r.cust_label },
    { key: "service", label: "서비스", get: (r) => SERVICE_LABEL[r.service] || r.service },
    { key: "status", label: "상태", get: (r) => STATUS_META[r.status]?.label || r.status },
    { key: "memo", label: "메모", get: (r) => r.memo },
    { key: "op_memo", label: "운영자 메모", get: (r) => r.op_memo },
    { key: "salesperson", label: "영업사원", get: (r) => r.salesperson },
  ];

  const ORIGIN = typeof window !== "undefined" ? window.location.origin : "https://피에이세무회계.kr";

  const copyLink = (url, key) => {
    try {
      navigator.clipboard?.writeText(url);
    } catch (e) {}
    setCopied(key);
    setTimeout(() => setCopied(""), 1500);
  };

  // 1회용 링크 생성 — kind(sa|quote|full), from_id(견적→정식 전환 원본). 생성 후 복사 + 표시.
  const makeOneTimeLink = async (kind, from_id, key, label) => {
    const r = await apiPost("/api/sales/handoff",
      { kind, from_id, created_by: "operator" }, null);
    if (!r?.token) { alert("링크 생성 실패 — 로그인/서버를 확인하세요"); return; }
    const url = `${ORIGIN}/?token=${r.token}`;
    copyLink(url, key);
    setGenLink({ url, label: label || kind });
  };

  // 요청의 단계 분류: 견적문의 / 진행중 / 완료
  const stageOf = (r) => {
    if (r.mode === "quote") return "quote";
    if (r.status === "done") return "done";
    return "progress"; // pending | running | needs_auth | failed
  };
  const inStage = (r, st) => stageOf(r) === st;

  const load = useCallback(async () => {
    setLoading(true);
    const data = await apiGet(`/api/requests`, MOCK_REQUESTS);
    setRequests(data);
    setLoading(false);
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const matchSearch = (r) => {
    const q = search.trim().toLowerCase();
    if (!q) return true;
    const cols = searchCol === "all" ? SEARCH_COLS.filter((c) => c.get) : SEARCH_COLS.filter((c) => c.key === searchCol);
    return cols.some((c) => String(c.get(r) || "").toLowerCase().includes(q));
  };

  const visible = requests.filter(
    (r) => (taxType === "all" || r.service === taxType) && inStage(r, stage) && matchSearch(r)
  );
  const pageCount = Math.max(1, Math.ceil(visible.length / PAGE_SIZE));
  const paged = visible.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
  useEffect(() => { setPage(1); }, [taxType, stage, search, searchCol, requests.length]);

  const countBy = (tType, st) =>
    requests.filter((r) => (tType === "all" || r.service === tType) && inStage(r, st)).length;

  const openDetail = async (req) => {
    setSelected(req.request_id);
    setView("detail");
    setOpenTabs((ts) => ts.some((t) => t.request_id === req.request_id) ? ts : [...ts, { request_id: req.request_id, name: req.name }]);
    const d = await apiGet(`/api/requests/${req.request_id}`, req);
    setDetail(d);
    setMeta({ platform: d.platform || "", cust_label: d.cust_label || "", op_memo: d.op_memo || "" });
    setSavedMeta(false);
    setShowCreds(false);
    setOpEdit(false);
    setOpDraft(buildOpDraft(d));
  };

  // 상세 → 편집 버퍼. 민감필드(비번·주민)는 표시 안 하므로 빈칸으로 시작(빈칸=기존 유지).
  const buildOpDraft = (d) => ({
    name: d.name || "", phone: d.phone || "", year: d.year || "",
    biz_no: (d.biz_nos && d.biz_nos[0]) || d.biz_no || "",
    biz_name: d.biz_name || "", address: d.address || "",
    jumin: "", hometax_id: "", hometax_pw: "",
    sa_phone: d.sa_phone || "", sa_provider: d.sa_provider || "kakao",
    // 법인
    corp_name: d.corp_name || "", corp_no: d.corp_no || "",
    corp_hometax_id: "", corp_hometax_pw: "",
    ceo_name: d.ceo_name || "", ceo_jumin: "",
    personal_hometax_id: "", personal_hometax_pw: "",
    memo: d.op_memo || d.memo || "",
  });

  const saveOpEdit = async () => {
    if (!detail || !opDraft) return;
    setOpSaving(true);
    const isCorp = detail.mode === "corp";
    const payload = {};
    const put = (k, v) => { if (v !== undefined && v !== null && String(v).trim() !== "") payload[k] = v; };
    // 공통
    put("name", opDraft.name);
    put("phone", onlyDigits(opDraft.phone));
    put("year", opDraft.year ? (Number(opDraft.year) || opDraft.year) : "");
    put("biz_no", onlyDigits(opDraft.biz_no));
    if (isCorp) {
      // 법인
      put("corp_name", opDraft.corp_name);
      put("corp_no", onlyDigits(opDraft.corp_no));
      put("corp_hometax_id", opDraft.corp_hometax_id);
      put("corp_hometax_pw", opDraft.corp_hometax_pw);
      put("ceo_name", opDraft.ceo_name);
      put("ceo_jumin", onlyDigits(opDraft.ceo_jumin));
      put("personal_hometax_id", opDraft.personal_hometax_id);
      put("personal_hometax_pw", opDraft.personal_hometax_pw);
    } else {
      // 개인
      put("biz_name", opDraft.biz_name);
      put("address", opDraft.address);
      put("jumin", onlyDigits(opDraft.jumin));
      put("hometax_id", opDraft.hometax_id);
      put("hometax_pw", opDraft.hometax_pw);
      put("sa_phone", onlyDigits(opDraft.sa_phone));
      if (opDraft.sa_phone) put("sa_provider", opDraft.sa_provider);
    }
    const r = await apiPost(`/api/requests/${detail.request_id}/edit`, payload, null);
    setOpSaving(false);
    if (r && r.ok) {
      const d = await apiGet(`/api/requests/${detail.request_id}`, detail);
      setDetail(d);
      setOpDraft(buildOpDraft(d));
      alert("고객정보 저장됨");
    } else {
      alert("저장 실패 — 다시 시도하세요");
    }
  };

  const saveMeta = async () => {
    if (!detail) return;
    setSavingMeta(true);
    await apiPost(`/api/requests/${detail.request_id}/meta`, meta, { ok: true });
    setDetail((d) => ({ ...d, ...meta }));
    setRequests((rs) => rs.map((r) => (r.request_id === detail.request_id ? { ...r, ...meta } : r)));
    setSavingMeta(false);
    setSavedMeta(true);
  };

  const handleRun = async () => {
    if (!detail) return;
    const mods = Object.keys(runModules).filter((k) => runModules[k]);
    if (!mods.length) { alert("실행할 모듈을 1개 이상 선택하세요."); return; }
    const res = await apiPost(
      `/api/requests/${detail.request_id}/run`,
      { modules: mods, biz_no: runBiz, year: Number(runYear) },
      { status: "running" }
    );
    setDetail((d) => ({ ...d, status: res.status, logs: [...(d.logs || []), `실행 지시 전송 (${mods.join(", ")})`] }));
    setRequests((rs) => rs.map((r) => (r.request_id === detail.request_id ? { ...r, status: res.status } : r)));
  };

  // 진행중이면 상세를 폴링해 로그·산출물·상태 갱신
  useEffect(() => {
    if (!detail || detail.status !== "running") return;
    const id = detail.request_id;
    const t = setInterval(async () => {
      const d = await apiGet(`/api/requests/${id}`, null);
      if (!d) return;
      setDetail((cur) => (cur && cur.request_id === id ? { ...cur, ...d } : cur));
      setRequests((rs) => rs.map((r) => (r.request_id === id ? { ...r, status: d.status } : r)));
      if (d.status !== "running") clearInterval(t);
    }, 3000);
    return () => clearInterval(t);
  }, [detail?.request_id, detail?.status]);

  // 운영자 — 진행 중 작업 '중단'. 전체 '모든 창 닫기'와 달리 이 작업의 브라우저만 종료.
  const handleCancel = async () => {
    if (!detail) return;
    if (!window.confirm("이 작업을 중단하고 브라우저를 닫을까요?")) return;
    const id = detail.request_id;
    const res = await apiPost(`/api/requests/${id}/cancel`, {}, null);
    setDetail((d) => ({ ...d, status: (res && res.status) || "failed", logs: [...(d.logs || []), "⛔ 중단 요청 — 브라우저 정리"] }));
    setRequests((rs) => rs.map((r) => (r.request_id === id ? { ...r, status: "failed" } : r)));
  };

  // 누적 진행 로그 정리 — 새 작업 전 깨끗한 화면.
  const handleClearLogs = async () => {
    if (!detail) return;
    if (!window.confirm("이 고객의 누적 진행 로그를 모두 정리할까요? (실행에는 영향 없음)")) return;
    const id = detail.request_id;
    await apiPost(`/api/requests/${id}/logs/clear`, {}, null);
    setDetail((d) => ({ ...d, logs: [] }));
  };

  // 간편인증 — 보내기 / 인증완료(선택 모듈 실행)
  const handleSAuthSend = async () => {
    if (!detail) return;
    const mods = Object.keys(runModules).filter((k) => runModules[k]);
    if (!mods.length) { alert("간편인증으로 실행할 모듈을 먼저 체크하세요 (연말정산·근로지급명세서 등)"); return; }
    const res = await apiPost(
      `/api/requests/${detail.request_id}/simple-auth/send`,
      { modules: mods, biz_no: runBiz, year: Number(runYear) },
      { status: "needs_auth" }
    );
    setDetail((d) => ({ ...d, status: res.status, logs: [...(d.logs || []), "간편인증 인증요청 — 폰 승인 대기(자동 진행)"] }));
    setRequests((rs) => rs.map((r) => (r.request_id === detail.request_id ? { ...r, status: res.status } : r)));
  };
  const handleSAuthComplete = async () => {
    if (!detail) return;
    const mods = Object.keys(runModules).filter((k) => runModules[k]);
    const res = await apiPost(
      `/api/requests/${detail.request_id}/simple-auth/complete`,
      { modules: mods, biz_no: runBiz, year: Number(runYear) },
      { status: "running" }
    );
    setDetail((d) => ({ ...d, status: res.status, logs: [...(d.logs || []), "인증완료 — 선택 모듈 실행"] }));
  };

  const handleDeleteFile = async (url) => {
    if (!detail || !url) return;
    if (!window.confirm("이 첨부파일을 삭제할까요?")) return;
    await apiPost(`/api/requests/${detail.request_id}/files/delete`, { url }, { ok: true });
    setDetail((d) => ({ ...d, files: (d.files || []).filter((f) => f.url !== url) }));
  };
  const handleDeleteRequest = async () => {
    if (!detail) return;
    if (!window.confirm(`요청 ${detail.request_id} (${detail.name})을(를) 삭제할까요? 되돌릴 수 없습니다.`)) return;
    try {
      await fetch(`${API_BASE}/api/requests/${detail.request_id}`, { method: "DELETE" });
    } catch (e) {}
    setRequests((rs) => rs.filter((r) => r.request_id !== detail.request_id));
    setDetail(null);
    setSelected(null);
  };
  const handleSetStatus = async (status) => {
    if (!detail) return;
    await apiPost(`/api/requests/${detail.request_id}/status`, { status }, { ok: true });
    setDetail((d) => ({ ...d, status }));
    setRequests((rs) => rs.map((r) => (r.request_id === detail.request_id ? { ...r, status } : r)));
  };
  const handleUpload = async (file, mod = "지방소득세") => {
    if (!detail || !file) return;
    const fd = new FormData();
    fd.append("file", file); fd.append("module", mod);
    try {
      const res = await fetch(`${API_BASE}/api/requests/${detail.request_id}/files/upload`, { method: "POST", body: fd });
      const d = await res.json();
      if (d?.url) setDetail((cur) => ({ ...cur, files: [...(cur.files || []), { name: d.name, url: d.url, module: d.module }] }));
    } catch (e) { alert("업로드 실패"); }
  };
  const [ni, setNi] = useState({ income_tax: "", local_tax: "", tax_base: "", case: "납부", deadline: "", refund_date: "", extra_note: "", is_cash_receipt_penalty: false, is_amendment: false, previous_amount: "", info_only: false });
  const setNiF = (k, v) => setNi((s) => ({ ...s, [k]: v }));
  const [niDocType, setNiDocType] = useState("접수증");   // 자동생성 문서유형
  const [niAuto, setNiAuto] = useState(null);             // 마지막 자동결과(매칭파일·금액)
  const [showManual, setShowManual] = useState(false);    // 수동입력 펼침(2순위)
  const handleFinalNotice = async () => {
    if (!detail) return;
    const body = { case: ni.case, extra_note: ni.extra_note, is_cash_receipt_penalty: ni.is_cash_receipt_penalty, is_amendment: ni.is_amendment, info_only: ni.info_only };
    if (ni.income_tax) body.income_tax = Number(onlyDigits(ni.income_tax));
    if (ni.local_tax) body.local_tax = Number(onlyDigits(ni.local_tax));
    if (ni.tax_base) body.tax_base = Number(onlyDigits(ni.tax_base));
    if (ni.deadline) body.deadline = ni.deadline;
    if (ni.refund_date) body.refund_date = ni.refund_date;
    if (ni.previous_amount) body.previous_amount = Number(onlyDigits(ni.previous_amount));
    const r = await apiPost(`/api/requests/${detail.request_id}/final-notice`, body, null);
    if (r?.text) setDetail((cur) => ({ ...cur, final_notice: r.text }));
  };
  const handleFinalNoticeAuto = async () => {
    if (!detail) return;
    const r = await apiPost(`/api/requests/${detail.request_id}/final-notice/auto`, { doc_type: niDocType, extra_note: ni.extra_note }, null);
    if (r?.text) {
      setDetail((cur) => ({ ...cur, final_notice: r.text }));
      setNi((s) => ({ ...s, income_tax: r.income_tax ? String(r.income_tax) : s.income_tax, local_tax: r.local_tax ? String(r.local_tax) : s.local_tax, case: r.case || s.case }));
      setNiAuto(r);
    } else { alert("자동 생성 실패"); }
  };
  const [dragOver, setDragOver] = useState(false);
  const [uploadMod, setUploadMod] = useState("지방소득세");

  const TAX_TABS = [
    { key: "jongso", label: "종합소득세" },
    { key: "vat", label: "부가가치세" },
    { key: "corp", label: "법인세" },
    { key: "all", label: "전체" },
  ];
  const STAGES = [
    { key: "quote", label: "견적문의" },
    { key: "progress", label: "진행중" },
    { key: "done", label: "완료" },
  ];

  return (
    <div className="mx-auto max-w-3xl px-4 py-8">
      <div className="mb-4 flex flex-wrap items-center justify-between gap-3">
        <div>
          <h2 className="text-xl font-bold text-slate-800">요청 관리</h2>
          <p className="mt-1 text-sm text-slate-500">세목을 선택하고 단계별로 고객을 관리하세요.</p>
        </div>
        <button
          onClick={load}
          className="rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-600 hover:bg-slate-50"
        >
          새로고침
        </button>
      </div>

      {/* 고객 안내 1회용 링크 — 생성 시마다 새 토큰, 고객이 1회 제출하면 만료 */}
      <div className={`mb-4 rounded-lg border border-slate-200 bg-white p-3 ${view === "detail" || salesView ? "hidden" : ""}`}>
        <div className="mb-2 flex items-center justify-between">
          <span className="text-sm font-medium text-slate-700">고객 안내 링크 (1회용)</span>
          <span className="text-[11px] text-slate-400">생성 후 고객에게 전달 · 1회 제출 시 만료</span>
        </div>
        {(() => {
          const KINDS = [
            { kind: "sa", title: "간편인증", desc: "휴대폰 인증(ID/PW 없이)" },
            { kind: "quote", title: "단순견적", desc: "견적만 문의하는 고객용(ID/PW)" },
            { kind: "full", title: "정식등록(신규)", desc: "처음부터 전체정보 입력" },
            { kind: "corp", title: "법인", desc: "법인 고객 전체정보 입력" },
          ];
          const sel = KINDS.find((k) => k.kind === genKind) || KINDS[0];
          return (
            <>
              <div className="flex flex-col gap-2 sm:flex-row sm:items-center">
                <select value={genKind} onChange={(e) => setGenKind(e.target.value)} className={inputCls + " sm:flex-1"}>
                  {KINDS.map((k) => <option key={k.kind} value={k.kind}>{k.title} — {k.desc}</option>)}
                </select>
                <button onClick={() => makeOneTimeLink(sel.kind, null, "gen", sel.title)}
                  className="shrink-0 rounded-lg bg-orange-600 px-4 py-2 text-sm font-semibold text-white hover:bg-orange-700">
                  {copied === "gen" ? "복사됨 ✓" : "링크 생성"}
                </button>
              </div>
              <p className="mt-1 text-[11px] text-slate-400">{sel.title}: {sel.desc}</p>
            </>
          );
        })()}
        {genLink && (
          <div className="mt-3 flex flex-col items-center gap-2 rounded-lg bg-slate-50 p-3 sm:flex-row sm:items-start">
            <img src={`https://api.qrserver.com/v1/create-qr-code/?size=140x140&data=${encodeURIComponent(genLink.url)}`} alt="고객 입력 QR" className="h-32 w-32 rounded-lg border border-slate-200 bg-white" />
            <div className="min-w-0 flex-1">
              <div className="mb-1 text-xs font-medium text-emerald-700">{genLink.label} 1회용 링크 (복사됨)</div>
              <div className="flex gap-1">
                <input readOnly value={genLink.url} className="w-full truncate rounded-lg border border-slate-200 bg-white px-2 py-1 text-[11px] text-slate-500" />
                <button onClick={() => copyLink(genLink.url, "genbox")} className="shrink-0 rounded-lg border border-slate-300 px-2 py-1 text-xs text-slate-600 hover:bg-slate-50">{copied === "genbox" ? "복사됨" : "복사"}</button>
              </div>
              <p className="mt-1 text-[11px] text-slate-400">고객에게 이 링크/QR를 보내세요. 고객이 한 번 제출하면 만료됩니다.</p>
            </div>
          </div>
        )}
      </div>

      {/* 영업사원 목록 관리 */}
      <div className={`mb-4 rounded-lg border border-slate-200 bg-white p-3 ${view === "detail" || salesView ? "hidden" : ""}`}>
        <label className="mb-1 block text-xs font-medium text-slate-500">영업사원 목록 (쉼표 구분 — 영업사원 페이지 드롭다운에 반영)</label>
        <div className="flex gap-2">
          <input className={inputCls} value={spText} onChange={(e) => setSpText(e.target.value)} placeholder="김영업, 홍길동" />
          <button onClick={saveSalespeople} className="shrink-0 rounded-lg border border-slate-300 px-3 py-2 text-xs font-medium text-slate-600 hover:bg-slate-50">저장</button>
        </div>
      </div>

      {/* 긴급 복구 — 모든 브라우저 닫기(로그아웃) */}
      <div className={`mb-4 rounded-lg border border-rose-200 bg-rose-50 p-3 ${view === "detail" || salesView ? "hidden" : ""}`}>
        <div className="flex items-center justify-between gap-2">
          <div>
            <div className="text-xs font-semibold text-rose-700">🧹 모든 창 닫기 (로그아웃)</div>
            <p className="mt-0.5 text-[11px] text-rose-500">간편인증 등이 창 점유로 막혔을 때. 이 PC의 크롬·엣지 전부 닫고 홈택스 세션 정리.</p>
          </div>
          <button
            onClick={async () => {
              if (!window.confirm("이 PC의 모든 크롬·엣지 창을 닫고 홈택스 세션을 정리합니다.\n진행할까요?")) return;
              const r = await apiPost("/api/admin/close-browsers", {}, null);
              if (r?.ok) alert(`완료 — 크롬:${r.killed?.["chrome.exe"] || "-"} / 엣지:${r.killed?.["msedge.exe"] || "-"} / 상태리셋:${r.status_reset || 0}건`);
              else alert("실패 — 다시 시도하거나 서버 확인");
            }}
            className="shrink-0 rounded-lg bg-rose-600 px-3 py-2 text-xs font-semibold text-white hover:bg-rose-700">모든 창 닫기</button>
        </div>
      </div>

      {/* 세목 탭 + 고객 상세 탭(● 이름 ✕) — 항상 표시 */}
      <div className="mb-3 flex flex-wrap items-center gap-1 border-b border-slate-200">
        {TAX_TABS.map((t) => (
          <button
            key={t.key}
            onClick={() => { setTaxType(t.key); setView("list"); setSalesView(false); }}
            className={`relative -mb-px border-b-2 px-4 py-2 text-sm font-medium transition ${
              view === "list" && !salesView && taxType === t.key
                ? "border-orange-500 text-orange-600"
                : "border-transparent text-slate-500 hover:text-slate-700"
            }`}
          >
            {t.label}
          </button>
        ))}
        {!isClerk && (
        <button
          onClick={() => { setSalesView(true); setView("list"); setSelected(null); setDetail(null); }}
          className={`relative -mb-px border-b-2 px-4 py-2 text-sm font-medium transition ${
            salesView ? "border-blue-500 text-blue-600" : "border-transparent text-slate-500 hover:text-slate-700"
          }`}
        >
          👟 영업사원
        </button>
        )}
        {openTabs.map((t) => {
          const active = view === "detail" && detail && detail.request_id === t.request_id;
          return (
            <button
              key={t.request_id}
              onClick={() => openDetail(t)}
              className={`relative -mb-px ml-1 flex items-center gap-1.5 rounded-t-md border-b-2 px-3 py-2 text-sm font-medium transition ${
                active ? "border-orange-500 bg-orange-50 text-orange-600" : "border-transparent text-slate-500 hover:text-slate-700"
              }`}
            >
              <span className="text-orange-500">●</span>{t.name}
              <span onClick={(e) => { e.stopPropagation(); closeTab(t.request_id); }} className="ml-0.5 rounded px-1 text-slate-400 hover:bg-slate-200 hover:text-slate-600">✕</span>
            </button>
          );
        })}
      </div>

      {/* 영업사원 대시보드 (최상위 탭) */}
      {salesView && <SalesDashboard />}

      {/* 단계 칩 */}
      <div className={`mb-4 flex flex-wrap gap-2 ${view === "detail" || salesView ? "hidden" : ""}`}>
        {STAGES.map((s) => {
          const n = countBy(taxType, s.key);
          const on = stage === s.key;
          return (
            <button
              key={s.key}
              onClick={() => { setStage(s.key); setSelected(null); setDetail(null); }}
              className={`flex items-center gap-1.5 rounded-full px-3.5 py-1.5 text-sm font-medium transition ${
                on ? "bg-slate-800 text-white" : "bg-white text-slate-600 ring-1 ring-slate-200 hover:bg-slate-50"
              }`}
            >
              {s.label}
              <span className={`rounded-full px-1.5 text-xs ${on ? "bg-white/20" : "bg-slate-100 text-slate-500"}`}>{n}</span>
            </button>
          );
        })}
      </div>

      {/* 검색 + 열 선택 */}
      <div className={`mb-4 flex flex-wrap items-center gap-2 ${view === "detail" || salesView ? "hidden" : ""}`}>
        <div className="relative min-w-0 flex-1">
          <svg className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
            <path d="M21 21l-4.3-4.3M11 19a8 8 0 100-16 8 8 0 000 16z" />
          </svg>
          <input
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            placeholder={`검색 (${SEARCH_COLS.find((c) => c.key === searchCol)?.label})`}
            className="w-full rounded-lg border border-slate-300 bg-white py-2 pl-9 pr-3 text-sm text-slate-800 placeholder:text-slate-400 focus:border-orange-400 focus:outline-none focus:ring-2 focus:ring-orange-100"
          />
          {search && (
            <button
              onClick={() => setSearch("")}
              className="absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 text-slate-400 hover:bg-slate-100"
            >
              <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
                <path d="M6 6l12 12M18 6L6 18" />
              </svg>
            </button>
          )}
        </div>
        <select
          value={searchCol}
          onChange={(e) => setSearchCol(e.target.value)}
          className="shrink-0 rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-700 focus:border-orange-400 focus:outline-none focus:ring-2 focus:ring-orange-100"
        >
          {SEARCH_COLS.map((c) => (
            <option key={c.key} value={c.key}>
              {c.label}
            </option>
          ))}
        </select>
      </div>

      <div className="grid grid-cols-1 gap-4 lg:grid-cols-5">
        {/* 목록 테이블 — 상세 탭 보는 중엔 숨김 */}
        <div className={view === "detail" || salesView ? "hidden" : "lg:col-span-5"}>
          {/* 고객목록 — 카드(풍선)형. 한 명씩 축약 표시, 카드 선택해 입장. */}
          <div className="space-y-2">
            {loading ? (
              <div className="rounded-xl border border-slate-200 bg-white p-10 text-center text-sm text-slate-400 shadow-sm">불러오는 중…</div>
            ) : visible.length === 0 ? (
              <div className="rounded-xl border border-slate-200 bg-white p-10 text-center text-sm text-slate-400 shadow-sm">해당 단계의 고객이 없습니다.</div>
            ) : (
              paged.map((r) => (
                <div
                  key={r.request_id}
                  role="button"
                  onClick={() => openDetail(r)}
                  className={`cursor-pointer rounded-xl border bg-white p-3.5 shadow-sm transition hover:border-orange-300 hover:shadow ${selected === r.request_id ? "border-orange-300 ring-1 ring-orange-200" : "border-slate-200"}`}
                >
                  {/* 1줄: 고객명(법인/개인) + 상태 */}
                  <div className="flex items-start justify-between gap-2">
                    <div className="flex min-w-0 items-center gap-1.5">
                      <KindBadge r={r} />
                      <span className="truncate font-bold text-slate-800">{r.name || "고객"}</span>
                    </div>
                    <StatusBadge status={r.status} />
                  </div>
                  {/* 2줄: 접수번호 · 일시 */}
                  <div className="mt-1 font-mono text-[11px] text-slate-400">{r.request_id} · {r.created_at}</div>
                  {/* 3줄: 플랫폼/영업 · 서비스 · 라벨 칩 */}
                  <div className="mt-2 flex flex-wrap items-center gap-1.5">
                    {(r.platform || r.salesperson) && (
                      <span className={`rounded px-1.5 py-0.5 text-[11px] ${r.platform ? "bg-slate-100 text-slate-600" : "bg-blue-50 text-blue-600"}`}>{r.platform || r.salesperson}</span>
                    )}
                    <span className="rounded bg-orange-50 px-1.5 py-0.5 text-[11px] text-orange-700">{SERVICE_LABEL[r.service] || r.service}</span>
                    {r.cust_label && <span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-500">{r.cust_label}</span>}
                    {r.delete_request?.requested && <span className="rounded bg-rose-100 px-1.5 py-0.5 text-[10px] font-medium text-rose-600">🗑 삭제요청</span>}
                  </div>
                  {/* 4줄(견적단계): 전체정보 링크 생성 */}
                  {stage === "quote" && (
                    <div className="mt-2.5 border-t border-slate-100 pt-2.5">
                      <button
                        onClick={(e) => { e.stopPropagation(); makeOneTimeLink("full", r.request_id, `row-${r.request_id}`, "정식등록(전환)"); }}
                        className="rounded-lg bg-emerald-600 px-2.5 py-1 text-xs font-medium text-white hover:bg-emerald-700"
                      >
                        {copied === `row-${r.request_id}` ? "복사됨 ✓" : "전체정보 링크 생성"}
                      </button>
                    </div>
                  )}
                </div>
              ))
            )}
          </div>
          {/* 페이지네이션 (10개/페이지) */}
          {view !== "detail" && pageCount > 1 && (
            <div className="mt-3 flex items-center justify-center gap-1">
              <button onClick={() => setPage((p) => Math.max(1, p - 1))} disabled={page <= 1} className="rounded border border-slate-300 px-2 py-1 text-xs text-slate-600 disabled:opacity-40 hover:bg-slate-50">이전</button>
              {Array.from({ length: pageCount }, (_, i) => i + 1).map((n) => (
                <button key={n} onClick={() => setPage(n)} className={`rounded px-2.5 py-1 text-xs ${n === page ? "bg-slate-800 text-white" : "border border-slate-300 text-slate-600 hover:bg-slate-50"}`}>{n}</button>
              ))}
              <button onClick={() => setPage((p) => Math.min(pageCount, p + 1))} disabled={page >= pageCount} className="rounded border border-slate-300 px-2 py-1 text-xs text-slate-600 disabled:opacity-40 hover:bg-slate-50">다음</button>
            </div>
          )}
        </div>

        {/* 상세 패널 — 상세 탭 활성일 때만 (세목 탭 바 아래로 들어감) */}
        {view === "detail" && detail && (
        <div className="lg:col-span-5">
          {detail ? (
            <div className="rounded-xl border border-slate-200 bg-white p-5 shadow-sm">
              <button
                onClick={() => setView("list")}
                className="mb-3 flex items-center gap-1 text-sm font-medium text-slate-500 hover:text-slate-700"
              >
                <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M15 19l-7-7 7-7" /></svg>
                목록으로
              </button>
              <div className="mb-4 flex items-start justify-between">
                <div>
                  <div className="font-mono text-xs text-slate-400">{detail.request_id}</div>
                  <h3 className="flex items-center gap-1.5 text-lg font-bold text-slate-800"><KindBadge r={detail} /><span>{detail.mode === "corp" ? (detail.corp_name || detail.name) : detail.name}</span>
                    {detail.salesperson && <span className="ml-1 rounded bg-blue-50 px-1.5 py-0.5 text-xs text-blue-600">{detail.salesperson}고객</span>}
                  </h3>
                </div>
                <StatusBadge status={detail.status} />
              </div>

              {/* 고객정보 요약 */}
              <dl className="mb-4 grid grid-cols-2 gap-x-4 gap-y-2 rounded-lg bg-slate-50 p-3 text-xs">
                {detail.mode === "corp" && (
                  <>
                    <div>
                      <dt className="text-slate-400">법인명</dt>
                      <dd className="text-slate-700">{detail.corp_name || "—"}</dd>
                    </div>
                    <div>
                      <dt className="text-slate-400">법인등록번호</dt>
                      <dd className="text-slate-700">{detail.corp_no || "—"}</dd>
                    </div>
                  </>
                )}
                <div>
                  <dt className="text-slate-400">연락처</dt>
                  <dd className="text-slate-700">{detail.phone || "—"}</dd>
                </div>
                <div>
                  <dt className="text-slate-400">사업자번호</dt>
                  <dd className="text-slate-700">
                    {detail.biz_nos?.length ? detail.biz_nos.join(", ") : detail.biz_no || "—"}
                  </dd>
                </div>
                <div>
                  <dt className="text-slate-400">인증</dt>
                  <dd className="text-slate-700">
                    {AUTH_LABEL(detail)}
                    {detail.simple_auth && detail.sa_provider &&
                      ` (${SA_PROVIDERS.find((p) => p.key === detail.sa_provider)?.label})`}
                  </dd>
                </div>
                <div>
                  <dt className="text-slate-400">귀속연도</dt>
                  <dd className="text-slate-700">{detail.year ? `${detail.year}년` : `${CURRENT_YEAR - 1}년 (자동)`}</dd>
                </div>
              </dl>

              {/* 홈택스 로그인 정보 (직접 로그인용 복사) */}
              <div className="mb-4 rounded-lg border border-slate-200 bg-white p-3">
                <div className="mb-2 flex items-center justify-between">
                  <span className="text-xs font-medium text-slate-500">홈택스 로그인 정보</span>
                  <button
                    onClick={() => setShowCreds((s) => !s)}
                    className="text-xs text-slate-400 hover:text-slate-600"
                  >
                    {showCreds ? "가리기" : "보기"}
                  </button>
                </div>
                <div className="space-y-1.5">
                  {(detail.mode === "corp"
                    ? [
                        { label: "법인ID", value: detail.corp_hometax_id, key: "cid", mask: false },
                        { label: "법인PW", value: detail.corp_hometax_pw, key: "cpw", mask: true },
                        { label: "법인번호", value: detail.corp_no, key: "cno", mask: false },
                        { label: "대표주민", value: detail.ceo_jumin, key: "ceoj", mask: true },
                        { label: "개인ID", value: detail.personal_hometax_id, key: "pid", mask: false },
                        { label: "개인PW", value: detail.personal_hometax_pw, key: "ppw", mask: true },
                      ]
                    : [
                        { label: "아이디", value: detail.hometax_id, key: "id", mask: false },
                        { label: "비밀번호", value: detail.hometax_pw, key: "pw", mask: true },
                        { label: "주민번호", value: detail.jumin, key: "jumin", mask: true },
                      ]
                  ).map((f) => (
                    <div key={f.key} className="flex items-center gap-2">
                      <span className="w-16 shrink-0 text-[11px] text-slate-400">{f.label}</span>
                      <span className="min-w-0 flex-1 truncate font-mono text-sm text-slate-700">
                        {!f.value ? "—" : showCreds || !f.mask ? f.value : "••••••••"}
                      </span>
                      {f.value && (
                        <button
                          onClick={() => copyLink(f.value, `cred-${f.key}`)}
                          className="shrink-0 rounded-lg border border-slate-300 px-2 py-1 text-xs font-medium text-slate-600 hover:bg-slate-50"
                        >
                          {copied === `cred-${f.key}` ? "복사됨" : "복사"}
                        </button>
                      )}
                    </div>
                  ))}
                </div>
                <p className="mt-2 text-[11px] text-slate-400">
                  운영자가 홈택스에 직접 로그인할 때 복사해서 사용하세요.
                </p>
              </div>

              {/* 고객정보 수정 (운영자) — 개인/법인 전 필드 + 등록상태 배지 */}
              <div className="mb-4 rounded-lg border border-slate-200 bg-white p-3">
                <div className="flex items-center justify-between">
                  <span className="text-xs font-semibold text-slate-500">고객정보 수정 {detail.mode === "corp" && <span className="ml-1 rounded bg-violet-100 px-1.5 py-0.5 text-[10px] text-violet-700">법인</span>}</span>
                  <button onClick={() => setOpEdit((o) => !o)} className="rounded-lg border border-slate-300 px-2.5 py-1 text-xs text-slate-600 hover:bg-slate-50">{opEdit ? "닫기" : "✏️ 수정"}</button>
                </div>
                {opEdit && opDraft && (() => {
                  const isCorp = detail.mode === "corp";
                  const jlen = onlyDigits(opDraft.jumin).length || detail.jumin_len || 0;
                  const cjlen = onlyDigits(opDraft.ceo_jumin).length || 0;
                  const okB = (t) => <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] font-medium text-emerald-700">{t || "✓ 등록됨"}</span>;
                  const warnB = (t) => <span className="rounded bg-amber-100 px-1.5 py-0.5 text-[10px] font-medium text-amber-700">{t}</span>;
                  const noB = (t) => <span className="rounded bg-rose-100 px-1.5 py-0.5 text-[10px] font-medium text-rose-600">{t || "⚠ 미등록"}</span>;
                  const upd = (k, v) => setOpDraft((d) => ({ ...d, [k]: v }));
                  return (
                    <div className="mt-2 space-y-2">
                      <div className="rounded-lg border border-sky-200 bg-sky-50 p-2 text-[11px] leading-relaxed text-sky-700">🔒 등록된 비번·주민번호는 보안상 표시되지 않습니다. <b>빈칸=기존 유지, 새로 입력=변경.</b></div>

                      {/* 공통: 명칭 + 연락처 + 귀속연도 */}
                      <div className="grid grid-cols-2 gap-2">
                        <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">{isCorp ? "법인명" : "성명"} {(isCorp ? detail.corp_name : detail.name) ? okB() : noB()}</span><input className={inputCls} value={isCorp ? opDraft.corp_name : opDraft.name} onChange={(e) => upd(isCorp ? "corp_name" : "name", e.target.value)} placeholder={isCorp ? "주식회사 OOO" : "홍길동"} /></label>
                        <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">연락처 {detail.phone ? okB() : noB()}</span><input className={inputCls} value={opDraft.phone} onChange={(e) => upd("phone", formatPhone(e.target.value))} inputMode="numeric" placeholder={detail.phone ? "비우면 기존 유지" : "010-0000-0000"} /></label>
                      </div>
                      <div className="grid grid-cols-2 gap-2">
                        <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">사업자번호 {(detail.biz_nos?.length || detail.biz_no) ? okB() : noB()}</span><input className={inputCls} value={opDraft.biz_no} onChange={(e) => upd("biz_no", formatBizNo(e.target.value))} inputMode="numeric" placeholder="000-00-00000" /></label>
                        <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">귀속연도</span><input className={inputCls} value={opDraft.year} onChange={(e) => upd("year", onlyDigits(e.target.value).slice(0, 4))} inputMode="numeric" placeholder={`${CURRENT_YEAR - 1}`} /></label>
                      </div>

                      {isCorp ? (
                        <>
                          <div className="grid grid-cols-2 gap-2">
                            <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">대표자명 {detail.ceo_name ? okB() : noB()}</span><input className={inputCls} value={opDraft.ceo_name} onChange={(e) => upd("ceo_name", e.target.value)} placeholder="대표자 성명" /></label>
                            <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">법인등록번호 {detail.corp_no ? okB() : noB()}</span><input className={inputCls} value={opDraft.corp_no} onChange={(e) => upd("corp_no", formatCorpNo(e.target.value))} inputMode="numeric" placeholder="000000-0000000" /></label>
                          </div>
                          <div className="rounded-lg border border-slate-100 bg-slate-50 p-2">
                            <div className="mb-1 flex items-center gap-2 text-[11px] font-semibold text-slate-500">법인 홈택스 ID/PW {detail.corp_hometax_id ? okB() : noB()}</div>
                            <div className="grid grid-cols-2 gap-2">
                              <input className={inputCls} value={opDraft.corp_hometax_id} onChange={(e) => upd("corp_hometax_id", e.target.value)} autoComplete="off" placeholder={detail.corp_hometax_id ? "비우면 기존 유지" : "법인 아이디"} />
                              <input type="password" className={inputCls} value={opDraft.corp_hometax_pw} onChange={(e) => upd("corp_hometax_pw", e.target.value)} autoComplete="new-password" placeholder={detail.corp_hometax_id ? "비우면 기존 유지" : "법인 비밀번호"} />
                            </div>
                          </div>
                          <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">대표 주민번호 {cjlen >= 13 ? okB("✓ 13자리") : detail.ceo_jumin ? okB() : noB()}</span><input className={inputCls} value={opDraft.ceo_jumin} onChange={(e) => upd("ceo_jumin", formatJumin(e.target.value))} inputMode="numeric" placeholder="000000-0000000 (비우면 유지)" /></label>
                          <div className="rounded-lg border border-slate-100 bg-slate-50 p-2">
                            <div className="mb-1 flex items-center gap-2 text-[11px] font-semibold text-slate-500">대표 개인 홈택스 ID/PW {detail.personal_hometax_id ? okB() : noB()}</div>
                            <div className="grid grid-cols-2 gap-2">
                              <input className={inputCls} value={opDraft.personal_hometax_id} onChange={(e) => upd("personal_hometax_id", e.target.value)} autoComplete="off" placeholder={detail.personal_hometax_id ? "비우면 기존 유지" : "개인 아이디"} />
                              <input type="password" className={inputCls} value={opDraft.personal_hometax_pw} onChange={(e) => upd("personal_hometax_pw", e.target.value)} autoComplete="new-password" placeholder={detail.personal_hometax_id ? "비우면 기존 유지" : "개인 비밀번호"} />
                            </div>
                          </div>
                        </>
                      ) : (
                        <>
                          <div className="grid grid-cols-2 gap-2">
                            <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">상호명</span><input className={inputCls} value={opDraft.biz_name} onChange={(e) => upd("biz_name", e.target.value)} placeholder="상호(선택)" /></label>
                            <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">주소</span><input className={inputCls} value={opDraft.address} onChange={(e) => upd("address", e.target.value)} placeholder="주소(선택)" /></label>
                          </div>
                          <label className="block"><span className="mb-0.5 flex flex-wrap items-center gap-1 text-[11px] text-slate-400">주민번호 {jlen >= 13 ? okB("✓ 13자리") : jlen >= 7 ? warnB("△ 7자리만") : detail.has_jumin13 ? okB() : noB()}</span><input className={inputCls} value={opDraft.jumin} onChange={(e) => upd("jumin", formatJumin(e.target.value))} inputMode="numeric" placeholder="000000-0000000 (비우면 유지)" /></label>
                          <div className="rounded-lg border border-slate-100 bg-slate-50 p-2">
                            <div className="mb-1 flex items-center gap-2 text-[11px] font-semibold text-slate-500">홈택스 ID/PW {detail.has_idpw ? okB() : noB()}</div>
                            <div className="grid grid-cols-2 gap-2">
                              <input className={inputCls} value={opDraft.hometax_id} onChange={(e) => upd("hometax_id", e.target.value)} autoComplete="off" placeholder={detail.has_idpw ? "비우면 기존 유지" : "아이디 입력"} />
                              <input type="password" className={inputCls} value={opDraft.hometax_pw} onChange={(e) => upd("hometax_pw", e.target.value)} autoComplete="new-password" placeholder={detail.has_idpw ? "비우면 기존 유지" : "비밀번호 입력"} />
                            </div>
                          </div>
                          <div className="rounded-lg border border-slate-100 bg-slate-50 p-2">
                            <div className="mb-1 flex items-center gap-2 text-[11px] font-semibold text-slate-500">간편인증 {detail.sa_phone ? okB() : noB()}</div>
                            <div className="grid grid-cols-2 gap-2">
                              <input className={inputCls} value={opDraft.sa_phone} onChange={(e) => upd("sa_phone", formatPhone(e.target.value))} inputMode="numeric" placeholder={detail.sa_phone ? "비우면 기존 유지" : "인증 휴대폰"} />
                              <select className={inputCls} value={opDraft.sa_provider} onChange={(e) => upd("sa_provider", e.target.value)}>{SA_PROVIDERS.map((p) => <option key={p.key} value={p.key}>{p.label}</option>)}</select>
                            </div>
                          </div>
                        </>
                      )}

                      <button onClick={saveOpEdit} disabled={opSaving} className="w-full rounded-lg bg-orange-600 py-2.5 text-sm font-semibold text-white hover:bg-orange-700 disabled:bg-slate-300">{opSaving ? "저장 중…" : "저장"}</button>
                    </div>
                  );
                })()}
              </div>

              {/* 신청 서비스 */}
              <div className="mb-4">
                <span className="mb-1.5 block text-xs font-medium text-slate-500">신청 서비스</span>
                <span className="inline-flex items-center gap-1.5">
                  <span className="rounded-md bg-orange-50 px-2 py-1 text-xs text-orange-700 ring-1 ring-orange-100">
                    {SERVICE_LABEL[detail.service] || detail.service}
                  </span>
                  {detail.mode === "quote" && (
                    <span className="rounded-md bg-slate-100 px-2 py-1 text-xs text-slate-600">단순 견적</span>
                  )}
                </span>
              </div>

              {/* 공동사업자 */}
              {detail.joint && (
                <div className="mb-4">
                  <span className="mb-1.5 block text-xs font-medium text-slate-500">공동사업자</span>
                  <div className="rounded-lg bg-slate-50 p-3 text-xs text-slate-700 ring-1 ring-slate-200">
                    {detail.joint_biz_no && <div>사업자번호: {detail.joint_biz_no}</div>}
                    {detail.joint_rep_name && <div>대표자: {detail.joint_rep_name}</div>}
                    {detail.joint_rep_jumin && <div>대표자 주민번호: {detail.joint_rep_jumin}</div>}
                    <div>본인 배분율: {detail.joint_share ? `${detail.joint_share}%` : "—"}</div>
                  </div>
                </div>
              )}

              {/* 방문위치(GPS) — 영업사원이 기록한 좌표 */}
              {detail.geo?.lat && (
                <div className="mb-4">
                  <span className="mb-1.5 block text-xs font-medium text-slate-500">방문 위치 (영업사원 GPS)</span>
                  <a href={`https://maps.google.com/?q=${detail.geo.lat},${detail.geo.lng}`} target="_blank" rel="noreferrer"
                    className="inline-flex items-center gap-1 rounded-lg border border-blue-200 bg-blue-50 px-2.5 py-1.5 text-xs font-medium text-blue-700 hover:bg-blue-100">
                    📍 {detail.geo.lat}, {detail.geo.lng} — 지도 열기
                  </a>
                </div>
              )}

              {/* 고객 메모 */}
              {detail.memo && (
                <div className="mb-4">
                  <span className="mb-1.5 block text-xs font-medium text-slate-500">고객 메모</span>
                  <div className="whitespace-pre-wrap rounded-lg bg-orange-50 p-3 text-sm text-slate-700 ring-1 ring-orange-100">
                    {detail.memo}
                  </div>
                </div>
              )}

              {/* 운영자 메타 입력: 플랫폼 사이트 / 고객 라벨 */}
              <div className="mb-4 rounded-lg border border-slate-200 bg-slate-50 p-3">
                <span className="mb-2 block text-xs font-medium text-slate-500">관리 정보</span>
                <div className="space-y-2">
                  <div>
                    <label className="mb-1 block text-[11px] text-slate-400">플랫폼 사이트 이름</label>
                    <input
                      className={inputCls}
                      value={meta.platform}
                      onChange={(e) => { setMeta((m) => ({ ...m, platform: e.target.value })); setSavedMeta(false); }}
                      placeholder="예: 크몽, 네이버, 직접문의"
                    />
                  </div>
                  <div>
                    <label className="mb-1 block text-[11px] text-slate-400">고객 라벨</label>
                    <input
                      className={inputCls}
                      value={meta.cust_label}
                      onChange={(e) => { setMeta((m) => ({ ...m, cust_label: e.target.value })); setSavedMeta(false); }}
                      placeholder="예: 종소세-2024, VIP, 재방문"
                    />
                  </div>
                  <div>
                    <label className="mb-1 block text-[11px] text-slate-400">운영자 메모</label>
                    <textarea
                      rows={3}
                      className={inputCls + " resize-none"}
                      value={meta.op_memo}
                      onChange={(e) => { setMeta((m) => ({ ...m, op_memo: e.target.value })); setSavedMeta(false); }}
                      placeholder="처리 내역·특이사항을 기록하세요. (고객에게 보이지 않음)"
                    />
                  </div>
                </div>
                <div className="mt-2 flex items-center gap-2">
                  <button
                    onClick={saveMeta}
                    disabled={savingMeta}
                    className="rounded-lg border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50 disabled:opacity-50"
                  >
                    {savingMeta ? "저장 중…" : "저장"}
                  </button>
                  {savedMeta && <span className="text-xs text-emerald-600">저장되었습니다</span>}
                </div>
              </div>

              {/* 견적 건 → 본입력 전환 1회용 링크 */}
              {detail.mode === "quote" && (
                <div className="mb-4 rounded-lg border border-emerald-200 bg-emerald-50 p-3">
                  <span className="mb-1 block text-xs font-medium text-emerald-700">본입력 전환 링크 (1회용)</span>
                  <p className="mb-2 text-[11px] text-emerald-600">
                    고객이 진행을 원하면 생성해 보내세요. 견적 시 받은 정보(자격증명 제외)가 채워진 전체 입력 폼이 열립니다. 1회 제출 시 만료.
                  </p>
                  <button
                    onClick={() => makeOneTimeLink("full", detail.request_id, "convert", "정식등록(전환)")}
                    className="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-700"
                  >
                    {copied === "convert" ? "링크 복사됨 ✓" : "전환 링크 생성"}
                  </button>
                </div>
              )}

              {/* 견적 건 전용 — 종소세 계산 + 견적 청구서 생성 */}
              {detail.mode === "quote" && (
                <div className="mb-4 rounded-lg border border-orange-200 bg-orange-50/40 p-3">
                  <span className="mb-2 block text-xs font-semibold text-orange-700">견적 산출 — 종소세 계산 · 청구서 생성</span>
                  <div className="mb-2 grid grid-cols-2 gap-1">
                    {[{ key: "jongso_calc", label: "신고안내자료+소득세계산" }, { key: "invoice", label: "견적·청구서" }].map((m) => (
                      <label key={m.key} className={`flex items-center gap-1.5 rounded px-2 py-1 text-xs cursor-pointer ${runModules[m.key] ? "bg-orange-100 text-orange-800" : "bg-white text-slate-600"}`}>
                        <input type="checkbox" checked={!!runModules[m.key]} onChange={(e) => setRunModules((s) => ({ ...s, [m.key]: e.target.checked }))} />
                        {m.label}
                      </label>
                    ))}
                  </div>
                  <div className="mb-2 flex items-center gap-2">
                    <span className="text-[11px] text-slate-400">귀속연도</span>
                    <select value={runYear} onChange={(e) => setRunYear(e.target.value)} className="rounded-md border border-slate-300 bg-white px-2 py-1 text-xs text-slate-700">
                      {YEAR_OPTIONS.map((y) => <option key={y} value={y}>{y}년</option>)}
                    </select>
                    <button type="button" onClick={() => setRunModules({ jongso_calc: true, invoice: true })} className="ml-auto rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-500 hover:bg-white">둘 다 선택</button>
                  </div>
                  <button onClick={handleRun} disabled={detail.status === "running"} className="w-full rounded-lg bg-orange-600 py-2.5 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:cursor-not-allowed disabled:bg-slate-300">
                    {detail.status === "running" ? "실행 중…" : "선택 실행 (계산·청구서)"}
                  </button>
                  <p className="mt-1 text-[11px] text-slate-400">※ 견적 문의 고객은 홈택스 ID/PW/주민을 제출함 → 로그인해 신고안내자료를 받고 계산·청구서를 생성합니다. <b>청구서는 계산 결과(CSV)가 있어야 생성</b>되니 <b>둘 다 체크</b> 권장(계산 먼저 → 청구서).</p>
                </div>
              )}

              {/* 실행 패널 (견적 건 제외) — 모듈 체크박스 + 사업자선택 + 연도 */}
              {detail.mode !== "quote" && (
                <div className="mb-4 rounded-lg border border-orange-200 bg-orange-50/40 p-3">
                  <span className="mb-2 block text-xs font-semibold text-orange-700">실행할 모듈 선택</span>

                  {/* 개인 자료수집 */}
                  <div className="mb-2">
                    <span className="mb-1 block text-[11px] text-slate-400">개인 자료수집</span>
                    <div className="grid grid-cols-2 gap-1">
                      {MODULES_PERSONAL.map((m) => (
                        <label key={m.key} className={`flex items-center gap-1.5 rounded px-2 py-1 text-xs cursor-pointer ${runModules[m.key] ? "bg-orange-100 text-orange-800" : "bg-white text-slate-600"}`}>
                          <input type="checkbox" checked={!!runModules[m.key]} onChange={(e) => setRunModules((s) => ({ ...s, [m.key]: e.target.checked }))} />
                          {m.label}
                        </label>
                      ))}
                    </div>
                  </div>

                  {/* 간편인증 필요 (연말정산·지급명세서) */}
                  <div className="mb-2">
                    <span className="mb-1 block text-[11px] text-amber-600">🔐 간편인증 필요 (②인증완료로 실행)</span>
                    <div className="grid grid-cols-2 gap-1">
                      {MODULES_SIMPLEAUTH.map((m) => (
                        <label key={m.key} className={`flex items-center gap-1.5 rounded px-2 py-1 text-xs cursor-pointer ${runModules[m.key] ? "bg-amber-100 text-amber-800" : "bg-white text-slate-600"}`}>
                          <input type="checkbox" checked={!!runModules[m.key]} onChange={(e) => setRunModules((s) => ({ ...s, [m.key]: e.target.checked }))} />
                          {m.label}
                        </label>
                      ))}
                    </div>
                  </div>

                  {/* 사업자 자료수집 */}
                  <div className="mb-2">
                    <span className="mb-1 block text-[11px] text-slate-400">사업자 자료수집</span>
                    <div className="grid grid-cols-2 gap-1">
                      {MODULES_BIZ.map((m) => (
                        <label key={m.key} className={`flex items-center gap-1.5 rounded px-2 py-1 text-xs cursor-pointer ${runModules[m.key] ? "bg-orange-100 text-orange-800" : "bg-white text-slate-600"}`}>
                          <input type="checkbox" checked={!!runModules[m.key]} onChange={(e) => setRunModules((s) => ({ ...s, [m.key]: e.target.checked }))} />
                          {m.label}
                        </label>
                      ))}
                    </div>
                    {/* 사업자 선택 */}
                    {(detail.biz_nos?.length > 0) && (
                      <select value={runBiz} onChange={(e) => setRunBiz(e.target.value)} className="mt-1.5 w-full rounded-md border border-slate-300 bg-white px-2 py-1 text-xs text-slate-700">
                        <option value="all">전체 사업장 ({detail.biz_nos.length}개)</option>
                        {detail.biz_nos.map((b) => <option key={b} value={b}>{b}</option>)}
                      </select>
                    )}
                  </div>

                  {/* 계산·산출 */}
                  <div className="mb-2">
                    <span className="mb-1 block text-[11px] text-slate-400">계산·산출 (가공물)</span>
                    <div className="grid grid-cols-2 gap-1">
                      {MODULES_CALC.map((m) => (
                        <label key={m.key} className={`flex items-center gap-1.5 rounded px-2 py-1 text-xs cursor-pointer ${runModules[m.key] ? "bg-orange-100 text-orange-800" : "bg-white text-slate-600"}`}>
                          <input type="checkbox" checked={!!runModules[m.key]} onChange={(e) => setRunModules((s) => ({ ...s, [m.key]: e.target.checked }))} />
                          {m.label}
                        </label>
                      ))}
                    </div>
                  </div>

                  {/* 연도 + 단축 */}
                  <div className="mb-2 flex items-center gap-2">
                    <span className="text-[11px] text-slate-400">귀속연도</span>
                    <select value={runYear} onChange={(e) => setRunYear(e.target.value)} className="rounded-md border border-slate-300 bg-white px-2 py-1 text-xs text-slate-700">
                      {YEAR_OPTIONS.map((y) => <option key={y} value={y}>{y}년</option>)}
                    </select>
                    <button type="button" onClick={() => setRunModules(Object.fromEntries(ALL_MODULES.map((m) => [m.key, true])))} className="ml-auto rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-500 hover:bg-white">전체선택</button>
                    <button type="button" onClick={() => setRunModules({})} className="rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-500 hover:bg-white">해제</button>
                  </div>

                  <button
                    onClick={handleRun}
                    disabled={detail.status === "running"}
                    className="w-full rounded-lg bg-orange-600 py-2.5 text-sm font-semibold text-white transition hover:bg-orange-700 disabled:cursor-not-allowed disabled:bg-slate-300"
                  >
                    {detail.status === "running" ? "실행 중…" : "선택 모듈 실행"}
                  </button>

                  {(detail.status === "running" || detail.status === "needs_auth") && (
                    <button
                      onClick={handleCancel}
                      className="mt-2 w-full rounded-lg border border-rose-300 bg-rose-50 py-2 text-sm font-semibold text-rose-700 transition hover:bg-rose-100"
                    >
                      ⛔ 작업 중단 (브라우저 종료)
                    </button>
                  )}

                  {/* 기타 — 간편인증 2단계 */}
                  <div className="mt-3 border-t border-orange-200 pt-3">
                    <span className="mb-1.5 block text-[11px] font-semibold text-slate-500">기타 — 간편인증</span>
                    <div className="flex gap-2">
                      <button
                        onClick={handleSAuthSend}
                        disabled={detail.status === "running"}
                        className="flex-1 rounded-lg border border-slate-300 bg-white py-2 text-xs font-medium text-slate-700 hover:bg-slate-50 disabled:opacity-50"
                      >
                        ① 간편인증 보내기
                      </button>
                      <button
                        onClick={handleSAuthComplete}
                        disabled={detail.status !== "needs_auth"}
                        className="flex-1 rounded-lg bg-amber-500 py-2 text-xs font-semibold text-white hover:bg-amber-600 disabled:cursor-not-allowed disabled:bg-slate-300"
                      >
                        ② 인증완료 → 실행
                      </button>
                    </div>
                    <p className="mt-1 text-[11px] text-slate-400">🔐모듈 체크 → ①보내기(인증요청, 세션유지) → 고객 폰 카톡 승인 → 고객이 "됐다"하면 ②인증완료(봇이 홈택스 완료버튼 클릭 + 모듈 실행)</p>
                  </div>
                </div>
              )}

              {/* 고객 정보 요약 (invoice 모듈 참조용) */}
              {detail.quote_info && (
                <div className="mb-4">
                  <div className="mb-1.5 flex items-center justify-between">
                    <span className="text-xs font-medium text-slate-500">고객 정보 요약 (참조용)</span>
                    <button
                      onClick={() => copyLink(detail.quote_info, "qinfo")}
                      className="rounded-lg border border-slate-300 px-2.5 py-1 text-xs font-medium text-slate-600 hover:bg-slate-50"
                    >
                      {copied === "qinfo" ? "복사됨" : "복사"}
                    </button>
                  </div>
                  <pre className="w-full overflow-x-auto whitespace-pre-wrap rounded-lg border border-slate-200 bg-slate-50 p-2.5 text-[11px] leading-relaxed text-slate-700">{detail.quote_info}</pre>
                </div>
              )}

              {/* 청구 발송문 (invoice 모듈 결과) */}
              {detail.quote_text && (
                <div className="mb-4">
                  <div className="mb-1.5 flex items-center justify-between">
                    <span className="text-xs font-medium text-slate-500">청구 발송문 (고객에게 복사)</span>
                    <button
                      onClick={() => copyLink(detail.quote_text, "quote")}
                      className="rounded-lg border border-slate-300 px-2.5 py-1 text-xs font-medium text-slate-600 hover:bg-slate-50"
                    >
                      {copied === "quote" ? "복사됨" : "전체 복사"}
                    </button>
                  </div>
                  <textarea
                    readOnly
                    value={detail.quote_text}
                    rows={7}
                    className="w-full resize-y rounded-lg border border-orange-200 bg-orange-50/40 p-2.5 text-xs leading-relaxed text-slate-700 focus:outline-none"
                  />
                </div>
              )}

              {/* 진행 로그 */}
              <div className="mb-4">
                <div className="mb-1.5 flex items-center justify-between gap-2">
                  <span className="text-xs font-medium text-slate-500">진행 로그</span>
                  <div className="flex gap-1.5">
                    {(detail.status === "running" || detail.status === "needs_auth") && (
                      <button
                        onClick={handleCancel}
                        className="rounded border border-rose-300 bg-rose-50 px-2 py-1 text-[11px] font-semibold text-rose-700 hover:bg-rose-100"
                      >
                        ⛔ 작업 중단
                      </button>
                    )}
                    {(detail.logs || []).length > 0 && (
                      <button
                        onClick={handleClearLogs}
                        className="rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-500 hover:bg-slate-50"
                      >
                        🧹 로그 정리
                      </button>
                    )}
                  </div>
                </div>
                <div className="max-h-40 space-y-1 overflow-y-auto rounded-lg bg-slate-900 p-3 font-mono text-xs text-slate-300">
                  {(detail.logs || []).map((log, i) => (
                    <div key={i}>
                      <span className="text-slate-500">›</span> {log}
                    </div>
                  ))}
                </div>
              </div>

              {/* 가공 문서 생성 현황 */}
              <div className="mb-3 flex flex-wrap gap-4 rounded-lg bg-slate-50 px-3 py-2 text-xs">
                <span>견적·청구 문구: {(detail.quote_text || detail.quote_info) ? <b className="text-emerald-600">생성됨 ✔</b> : <span className="text-slate-400">미생성</span>}</span>
                <span>최종안내문: {detail.final_notice ? <b className="text-emerald-600">생성됨 ✔</b> : <span className="text-slate-400">미생성</span>}</span>
              </div>

              {/* 산출물 — 자료 종류별 완전성 체크리스트 (왼쪽 종류 / 오른쪽 첨부) */}
              <div>
                <span className="mb-1.5 block text-xs font-medium text-slate-500">자료 첨부 현황</span>
                <div className="overflow-hidden rounded-lg border border-slate-200">
                  {DATA_TYPES.map((dt) => {
                    const fs = (detail.files || []).filter((f) => f.module === dt.mod);
                    return (
                      <div key={dt.mod} className="flex items-start gap-2 border-b border-slate-100 px-3 py-2 last:border-b-0">
                        <div className="flex w-32 shrink-0 items-center gap-1.5 pt-0.5">
                          <span className={`h-2 w-2 shrink-0 rounded-full ${fs.length ? "bg-emerald-500" : "bg-slate-300"}`}></span>
                          <span className="text-xs text-slate-600">{dt.label}</span>
                        </div>
                        <div className="min-w-0 flex-1 space-y-1">
                          {fs.length === 0 ? (
                            <span className="text-[11px] text-slate-300">미첨부</span>
                          ) : fs.map((f) => (
                            <div key={f.url || f.name} className="flex items-center gap-1.5">
                              <a href={f.url} className="min-w-0 flex-1 truncate text-xs text-orange-700 hover:underline">{f.name}</a>
                              {canDelete && <button onClick={(e) => { e.preventDefault(); handleDeleteFile(f.url); }} className="shrink-0 rounded px-1.5 text-[11px] text-rose-500 hover:bg-rose-50">삭제</button>}
                            </div>
                          ))}
                        </div>
                      </div>
                    );
                  })}
                </div>
                {/* 자료 드래그 업로드 — 원하는 그룹 선택해서 첨부 */}
                <div className="mt-2">
                  <div className="mb-1 flex items-center gap-2">
                    <span className="text-[11px] text-slate-400">첨부 그룹:</span>
                    <select value={uploadMod} onChange={(e) => setUploadMod(e.target.value)} className="rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-700">
                      {DATA_TYPES.map((dt) => <option key={dt.mod} value={dt.mod}>{dt.label}</option>)}
                    </select>
                  </div>
                  <div
                    onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
                    onDragLeave={() => setDragOver(false)}
                    onDrop={(e) => { e.preventDefault(); setDragOver(false); const f = e.dataTransfer.files?.[0]; if (f) handleUpload(f, uploadMod); }}
                    className={`rounded-lg border-2 border-dashed p-3 text-center text-xs ${dragOver ? "border-orange-400 bg-orange-50" : "border-slate-300 bg-slate-50 text-slate-400"}`}
                  >
                    「{DATA_TYPES.find((d) => d.mod === uploadMod)?.label}」 그룹으로 드래그 또는
                    <label className="ml-1 cursor-pointer font-medium text-orange-600 hover:underline">
                      파일 선택
                      <input type="file" className="hidden" onChange={(e) => { const f = e.target.files?.[0]; if (f) handleUpload(f, uploadMod); e.target.value = ""; }} />
                    </label>
                  </div>
                </div>
              </div>

              {/* 최종안내문 — 자동(1순위) / 수동(2순위) */}
              <div className="mb-4">
                <span className="mb-1.5 block text-xs font-medium text-slate-500">최종안내문 (종소세+지방소득세)</span>

                {/* ① 자동생성 (1순위) */}
                <div className="rounded-xl border border-emerald-200 bg-emerald-50/60 p-3">
                  <div className="mb-2 flex items-center gap-2">
                    <span className="rounded-md bg-emerald-600 px-1.5 py-0.5 text-[10px] font-bold text-white">자동</span>
                    <span className="text-xs text-slate-600">문서유형 선택 → 종소세 폴더 + 업로드한 지방소득세 합쳐 자동 생성</span>
                  </div>
                  <div className="mb-2 grid grid-cols-2 gap-1.5">
                    {[{ k: "접수증", l: "접수증 (보통 환급)" }, { k: "납부서", l: "납부서 (보통 납부)" }].map((d) => (
                      <button key={d.k} type="button" onClick={() => setNiDocType(d.k)}
                        className={`rounded-lg border px-2 py-1.5 text-xs font-medium transition ${niDocType === d.k ? "border-emerald-500 bg-white text-emerald-700 shadow-sm" : "border-emerald-200 bg-emerald-50 text-slate-500 hover:bg-white"}`}>{d.l}</button>
                    ))}
                  </div>
                  <button onClick={handleFinalNoticeAuto} className="w-full rounded-lg bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-700">📄 {niDocType} 2건으로 자동생성</button>
                  {niAuto && (
                    <div className="mt-2 space-y-0.5 text-[11px]">
                      <div className="font-semibold text-slate-700">판정: <span className={niAuto.case === "환급" ? "text-blue-600" : niAuto.case === "납부" ? "text-rose-600" : "text-slate-500"}>{niAuto.case}</span> (부호 자동판정)</div>
                      <div className={niAuto.found_jongso ? "text-slate-600" : "text-rose-500"}>
                        {niAuto.found_jongso ? "✔" : "✕"} 종소세 {niAuto.doc_type}: {niAuto.jongso_file || "파일 없음"} {niAuto.found_jongso && `→ ${won(niAuto.income_tax)}`}
                      </div>
                      <div className={niAuto.found_local ? "text-slate-600" : "text-amber-600"}>
                        {niAuto.found_local ? "✔" : "△"} 지방세 {niAuto.doc_type}: {niAuto.local_file || "업로드 없음(종소세의 10%로 추정)"} {niAuto.found_local && `→ ${won(niAuto.local_tax)}`}
                      </div>
                      {niAuto.doc_type === "납부서" && <div className="text-slate-600">📅 납부기한: {niAuto.deadline || "추출 실패(직접 입력 보정)"}</div>}
                      {!niAuto.found_jongso && <div className="text-rose-500">※ 종소세 금액 추출 실패 — 아래 ‘직접 입력’에서 보정 후 생성하세요.</div>}
                    </div>
                  )}
                </div>

                {/* ② 직접 입력 (2순위, 접힘) */}
                <button onClick={() => setShowManual((v) => !v)} className="mt-2 text-[11px] font-medium text-slate-400 hover:text-slate-600">
                  {showManual ? "▾ 직접 입력 닫기" : "▸ 직접 입력 (수동)"}
                </button>
                {showManual && (
                  <div className="mt-1.5 space-y-2 rounded-xl border border-slate-200 bg-slate-50/60 p-3">
                    <div className="grid grid-cols-3 gap-2">
                      <select className={inputCls + " text-xs"} value={ni.case} onChange={(e) => setNiF("case", e.target.value)}>
                        <option value="납부">납부</option><option value="환급">환급</option><option value="세액없음">세액없음</option>
                      </select>
                      <input className={inputCls + " text-xs"} inputMode="numeric" value={ni.income_tax} onChange={(e) => setNiF("income_tax", e.target.value)} placeholder="종소세액(납부서기준)" />
                      <input className={inputCls + " text-xs"} inputMode="numeric" value={ni.local_tax} onChange={(e) => setNiF("local_tax", e.target.value)} placeholder="지방세액(빈칸=10%)" />
                    </div>
                    <div className="grid grid-cols-2 gap-2">
                      <input className={inputCls + " text-xs"} inputMode="numeric" value={ni.tax_base} onChange={(e) => setNiF("tax_base", e.target.value)} placeholder="과세표준(선택)" />
                      {ni.case === "환급"
                        ? <input className={inputCls + " text-xs"} value={ni.refund_date} onChange={(e) => setNiF("refund_date", e.target.value)} placeholder="환급예정일 예:2026년 6월 말" />
                        : <input className={inputCls + " text-xs"} value={ni.deadline} onChange={(e) => setNiF("deadline", e.target.value)} placeholder="납부기한 예:2026년 6월 1일" />}
                    </div>
                    <div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-slate-600">
                      <label className="flex items-center gap-1"><input type="checkbox" checked={ni.is_cash_receipt_penalty} onChange={(e) => setNiF("is_cash_receipt_penalty", e.target.checked)} />현금영수증 가산세</label>
                      <label className="flex items-center gap-1"><input type="checkbox" checked={ni.is_amendment} onChange={(e) => setNiF("is_amendment", e.target.checked)} />정정신고</label>
                      {ni.is_amendment && <input className={inputCls + " w-28 text-xs"} inputMode="numeric" value={ni.previous_amount} onChange={(e) => setNiF("previous_amount", e.target.value)} placeholder="정정전 금액" />}
                      <label className="flex items-center gap-1"><input type="checkbox" checked={ni.info_only} onChange={(e) => setNiF("info_only", e.target.checked)} />정보전달전용</label>
                    </div>
                    <input className={inputCls + " text-xs"} value={ni.extra_note} onChange={(e) => setNiF("extra_note", e.target.value)} placeholder="추가 안내(작년대비 변동 사유 등, 선택)" />
                    <button onClick={handleFinalNotice} className="rounded-lg bg-slate-800 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-slate-900">직접 입력값으로 생성</button>
                  </div>
                )}

                {detail.final_notice && (
                  <div className="mt-2">
                    <div className="mb-1 flex justify-end">
                      <button onClick={() => copyLink(detail.final_notice, "final")} className="rounded-lg border border-slate-300 px-2.5 py-1 text-xs text-slate-600 hover:bg-slate-50">{copied === "final" ? "복사됨" : "복사"}</button>
                    </div>
                    <textarea value={detail.final_notice} onChange={(e) => setDetail((d) => ({ ...d, final_notice: e.target.value }))} rows={11} className="w-full resize-y rounded-lg border border-slate-200 bg-slate-50 p-2.5 text-[11px] leading-relaxed text-slate-700 focus:outline-none" />
                  </div>
                )}
              </div>

              {/* 영업사원 삭제요청 반영 — 운영자 확인 후 실제 삭제 (직원은 삭제 불가) */}
              {canDelete && detail.delete_request?.requested && (
                <div className="mt-4 flex flex-wrap items-center justify-between gap-2 rounded-lg border border-rose-300 bg-rose-50 p-3">
                  <div className="text-xs text-rose-700">🗑️ <b>영업사원 삭제요청</b> · {detail.delete_request.by || "?"} ({detail.delete_request.at}){detail.delete_request.reason && <span className="block text-[11px] text-rose-500">사유: {detail.delete_request.reason}</span>}</div>
                  <button onClick={handleDeleteRequest} className="shrink-0 rounded-lg bg-rose-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-rose-700">확인 후 삭제</button>
                </div>
              )}

              {/* 완료/진행중 (운영자 결정) + 요청 삭제 */}
              {detail.mode !== "quote" && (
                <div className="mt-4 flex items-center justify-between border-t border-slate-200 pt-3">
                  {detail.status === "done" ? (
                    <button onClick={() => handleSetStatus("pending")} className="rounded-lg border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-600 hover:bg-slate-50">↩ 진행중으로</button>
                  ) : (
                    <button onClick={() => handleSetStatus("done")} className="rounded-lg bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700">✓ 완료 처리</button>
                  )}
                  {canDelete && <button onClick={handleDeleteRequest} className="rounded-lg border border-rose-200 px-3 py-1.5 text-xs font-medium text-rose-600 hover:bg-rose-50">요청 삭제</button>}
                </div>
              )}
              {detail.mode === "quote" && canDelete && (
                <div className="mt-4 border-t border-slate-200 pt-3 text-right">
                  <button onClick={handleDeleteRequest} className="rounded-lg border border-rose-200 px-3 py-1.5 text-xs font-medium text-rose-600 hover:bg-rose-50">요청 삭제</button>
                </div>
              )}
            </div>
          ) : (
            <div className="flex h-full min-h-[200px] items-center justify-center rounded-xl border border-dashed border-slate-300 bg-white text-sm text-slate-400">
              요청을 선택하면 상세 정보가 표시됩니다.
            </div>
          )}
        </div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 루트 (탭 라우팅)
// ─────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────
// 위장용 공개 홈페이지 (첫 페이지) — 평범한 세무사무소 소개. 로그인 노출 없음.
// ─────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────
// 부가세 1기 포켓몬 카드 이벤트 팝업 (공용) — 랜딩 진입 + 고객 접수폼(제출/접수후)에서 재사용.
//   variant: 'entry'(A, 진입·제출 즉시) | 'thanks'(B, 접수 후 감사)
//   ★ 2026-07-31(KST)까지만. 이후엔 PA_EVENT_LIVE() 가 false → 어디서도 안 뜸.
// ─────────────────────────────────────────────────────────────
const PA_EVENT_END = new Date("2026-07-31T23:59:59+09:00").getTime();
const PA_EVENT_LIVE = () => Date.now() <= PA_EVENT_END;
const PA_KAKAO = "https://pf.kakao.com/_JgqxjX/chat";
const PA_PRIZES = [
  { img: "/event/prize_brg10.png", rank: "1등 · 1명", name: "잉어킹 AR 트리플렛비트 (한글판) BRG 10", val: "20만원 상당" },
  { img: "/event/prize_psa9.png", rank: "2등 · 1명", name: "리피아 P 유나가바 SV 프로모 (일어판) PSA 9", val: "12만원 상당" },
  { img: "/event/prize_brg9.png", rank: "3등 · 4명", name: "잉어킹 AR 트리플렛비트 (한글판) BRG 9", val: "각 9만원 상당" },
];
const PA_STEPS_ENTRY = [
  "피에이세무회계에 1기 확정 부가세 신고를 의뢰합니다.",
  "신고가 완료됩니다. (~ 7월 25일)",
  "카카오톡 채널(@피에이세무회계)에서 부가세 신고 인증을 해주시면 추첨 대상에 포함됩니다.",
  "7월 31일 추첨 후 당첨자에게 카카오톡 채널 메시지로 안내드립니다.",
];
const PA_STEPS_THANKS = [
  "신고가 완료되면 (~ 7월 25일) 카카오톡 채널(@피에이세무회계)에서 부가세 신고 인증을 해주세요.",
  "인증하신 분은 모두 추첨 대상에 포함됩니다.",
  "7월 31일 추첨 후 당첨자에게 카카오톡 채널 메시지로 안내드립니다.",
];

function EventPopup({ variant = "entry", onClose, onHideToday }) {
  const thanks = variant === "thanks";
  const X = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" className={p.className}><path d="M6 6l12 12" /><path d="M18 6L6 18" /></svg>);
  const Kk = (p) => (<svg viewBox="0 0 24 24" fill="currentColor" className={p.className} aria-hidden="true"><path d="M12 3C6.8 3 2.5 6.4 2.5 10.6c0 2.7 1.8 5 4.5 6.4-.2.7-.7 2.5-.8 2.9 0 .3.2.4.4.2.2-.1 2.8-1.9 3.9-2.7.6.1 1.1.1 1.6.1 5.2 0 9.5-3.4 9.5-7.6S17.2 3 12 3z" /></svg>);
  const steps = thanks ? PA_STEPS_THANKS : PA_STEPS_ENTRY;
  return (
    <div className="fixed inset-0 z-[90] flex items-center justify-center bg-black/50 p-4 font-sans" onClick={onClose} role="dialog" aria-modal="true" style={{ wordBreak: "keep-all" }}>
      <div className="relative flex max-h-[90vh] w-full max-w-md flex-col overflow-hidden rounded-2xl bg-[#FFF6EF] text-slate-800 shadow-2xl md:max-w-2xl" style={{ borderTop: "5px solid #F26A21" }} onClick={(e) => e.stopPropagation()}>
        <button type="button" onClick={onClose} aria-label="닫기" className="absolute right-3 top-3 z-10 flex h-9 w-9 items-center justify-center rounded-full bg-white/85 text-slate-500 shadow"><X className="h-5 w-5" /></button>
        <div className="overflow-y-auto px-5 py-6 text-center md:px-8">
          <div className="mb-4 flex items-center justify-center gap-2.5">
            <span className="flex h-10 w-10 items-center justify-center rounded-[11px] bg-orange-500 text-base font-extrabold text-white">PA</span>
            <span className="text-left leading-tight"><b className="block text-[15px] font-extrabold text-slate-800">피에이세무회계</b><span className="text-[10px] font-semibold tracking-wide text-orange-500">PRIVATE ACCOUNTANT</span></span>
          </div>
          {thanks ? (
            <>
              <span className="mb-3 inline-block rounded-full bg-orange-500 px-3.5 py-1.5 text-xs font-bold text-white">🎁 부가세 신고 이벤트 안내</span>
              <h3 className="text-xl font-extrabold leading-snug text-slate-800 md:text-2xl">신청해 주셔서 감사합니다!</h3>
              <p className="mt-2 text-sm leading-relaxed text-slate-500">포켓몬 카드 이벤트가 진행 중이에요. 신고 완료 후 카카오톡 채널에서 인증하시면 추첨을 통해 총 6분께 포켓몬 TCG 카드를 드립니다.</p>
            </>
          ) : (
            <>
              <span className="mb-3 inline-block rounded-full bg-orange-500 px-3.5 py-1.5 text-xs font-bold text-white">7/25까지 · 1기 확정 부가세 신고 이벤트</span>
              <h3 className="text-xl font-extrabold leading-snug text-slate-800 md:text-2xl">부가세 신고하고<br /><span className="text-orange-600">포켓몬 카드</span> 받아가세요!</h3>
              <p className="mt-2 text-sm leading-relaxed text-slate-500">추첨을 통해 총 6분께 포켓몬 TCG 카드를 드립니다.</p>
            </>
          )}
          <div className="my-5 grid grid-cols-3 gap-2.5">
            {PA_PRIZES.map((p) => (
              <div key={p.rank} className="flex flex-col items-center rounded-xl border border-[#f0d9c8] bg-white p-2.5">
                <img src={p.img} alt={p.name} loading="lazy" className="mb-2 h-[120px] w-full rounded-md bg-[#faf3ec] object-contain md:h-[160px]" />
                <span className="mb-1 inline-block rounded-full bg-orange-500 px-2.5 py-0.5 text-[11px] font-bold text-white">{p.rank}</span>
                <span className="text-[11px] font-semibold leading-snug text-slate-700">{p.name}</span>
                <span className="mt-1 text-[11px] font-bold text-orange-700">{p.val}</span>
              </div>
            ))}
          </div>
          <div className="rounded-xl bg-white p-4 text-left">
            <h4 className="mb-3 text-sm font-extrabold text-orange-700">응모 방법</h4>
            <ol className="space-y-2.5">
              {steps.map((s, i) => (
                <li key={i} className="flex gap-2.5 text-[13px] leading-relaxed text-slate-600">
                  <span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-orange-100 text-[11px] font-bold text-orange-600">{i + 1}</span>
                  <span className="min-w-0">{s}</span>
                </li>
              ))}
            </ol>
            <p className="mt-3 rounded-lg bg-orange-50 p-2.5 text-[12px] leading-relaxed text-orange-700">⚠️ 응모(신고 인증)와 당첨 안내 모두 카카오톡 채널에서 진행됩니다. 꼭 채널을 추가해 주세요!</p>
          </div>
          <div className="mt-4 flex flex-col items-center gap-2.5">
            <img src="/event/kakao_qr.png" alt="피에이세무회계 카카오톡 채널 QR" loading="lazy" className="h-28 w-28 rounded-lg bg-white p-1.5 ring-1 ring-orange-100" />
            <p className="text-[12px] text-slate-500">카카오톡 채널 <b className="text-slate-700">@피에이세무회계</b></p>
            <a href={PA_KAKAO} target="_blank" rel="noopener" className="flex w-full max-w-xs items-center justify-center gap-2 rounded-xl py-3.5 text-sm font-extrabold text-slate-900 shadow-sm" style={{ backgroundColor: "#FEE500" }}><Kk className="h-5 w-5" /> 채팅하기</a>
          </div>
        </div>
        <div className="flex items-center justify-between border-t border-orange-100 bg-white px-5 py-3">
          {onHideToday
            ? <button type="button" onClick={onHideToday} className="text-xs font-medium text-slate-400 transition hover:text-slate-600">오늘 하루 보지 않기</button>
            : <span className="text-xs text-slate-300">피에이세무회계</span>}
          <button type="button" onClick={onClose} className="text-xs font-bold text-orange-600">닫기</button>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 위장 랜딩(고객용 홈페이지). 시즌 배너 문구만 바꾸려면 아래 LandingPage 안의
// "시즌 배너" 주석 블록을 찾아 헤드라인/서브카피/버튼 3줄만 교체하세요.
// 시즌별 문구 모음(복사용):
//   종소세(5월)  : "세금은 PA가 챙기겠습니다" / "5월은 평소처럼 보내세요" / "종소세 상담하기"
//   부가세(1·7월): "복잡한 부가세, PA가 챙기겠습니다" / "25일까지 걱정 말고 맡겨주세요" / "부가세 상담하기"
//   법인세(3월)  : "법인세 신고, PA에 맡기세요" / "결산부터 신고까지 한 번에" / "법인세 상담하기"
//   기본(상시)   : "복잡한 세무는 PA에서 단순해집니다" / "한 건 한 건 직접 챙기겠습니다" / "무료 상담하기"
// '문의하기' 버튼은 모두 우하단 채널톡 메신저(openChat)로 연결한다.
// ─────────────────────────────────────────────────────────────
function LandingPage() {
  const KAKAO = "https://pf.kakao.com/_JgqxjX/chat";   // (현재 미사용) 카카오 채널 링크 — 이벤트/폴백용 보관
  const NAVER_PLACE = "https://naver.me/FEgNknSC";
  const KMONG = "https://kmong.com/@%ED%94%BC%EC%97%90%EC%9D%B4%EC%84%B8%EB%AC%B4%ED%9A%8C%EA%B3%84";
  const KAKAO_PLACE = "https://place.map.kakao.com/660348689";
  const TEL = "031-432-8615";
  const EMAIL = "Kicpa-pa@naver.com";
  const CHANNELTALK_KEY = "304bbac4-0d88-4252-8787-6debcb2ffe1a";   // 채널톡 플러그인 키
  // ★ 손키: 카카오맵 실제 렌더용 JavaScript 키(developers.kakao.com → 앱 → JS키, 플랫폼에 pa-tax.co.kr 등록).
  //   키 넣기 전엔 '카카오맵에서 보기' 버튼 폴백이 뜬다. (카카오 채널/채널톡 키와 별개)
  const KAKAO_JS_KEY = "XXXX";
  const KAKAO_PLACE_COORD = { lat: 37.31987, lng: 126.80920 };       // ★ 정확 좌표는 손키가 보정 권장
  // ★ 손키: 무장벽 상담폼 수신처. Formspree(formspree.io) 가입 → 폼 생성(수신메일 Kicpa-pa@naver.com)
  //   → 발급된 폼 ID 로 아래 XXXX 교체. 교체 전엔 제출 시 '카카오/전화로 안내' 폴백이 뜬다.
  const FORMSPREE = "https://formspree.io/f/XXXX";

  const [menuOpen, setMenuOpen] = useState(false);
  const [procTab, setProcTab] = useState("online");
  const [scrolled, setScrolled] = useState(false);     // 헤더 축소
  const [activeSec, setActiveSec] = useState("");       // 스크롤 스파이
  const [toast, setToast] = useState("");               // 복사 알림
  const [showEvent, setShowEvent] = useState(false);    // 진입 이벤트 팝업
  const [showChatTip, setShowChatTip] = useState(false);// 우하단 채널톡 안내 풍선
  const [showKakaoTip, setShowKakaoTip] = useState(false);// 좌하단 카카오톡 안내 풍선
  const [mapReady, setMapReady] = useState(false);      // 카카오맵 렌더 성공 여부
  const [evPop, setEvPop] = useState("");               // 상담폼 제출 팝업: ''|entry(A)|thanks(B)
  const [form, setForm] = useState({ name: "", phone: "", type: "", msg: "", agree: false });
  const [formStatus, setFormStatus] = useState("");     // ''|need|agree|sending|ok|err
  const setF = (k, v) => setForm((p) => ({ ...p, [k]: v }));
  const submitConsult = async (e) => {
    if (e && e.preventDefault) e.preventDefault();
    if (!form.name.trim() || !form.phone.trim()) { setFormStatus("need"); return; }
    if (!form.agree) { setFormStatus("agree"); return; }
    setFormStatus("sending");
    if (PA_EVENT_LIVE()) setEvPop("entry");   // 보낼 때 = A(이벤트 안내)
    try {
      const res = await fetch(FORMSPREE, {
        method: "POST",
        headers: { Accept: "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({ 이름: form.name, 연락처: form.phone, 문의유형: form.type || "미선택", 문의내용: form.msg }),
      });
      if (res.ok) { setFormStatus("ok"); setForm({ name: "", phone: "", type: "", msg: "", agree: false }); setEvPop(PA_EVENT_LIVE() ? "thanks" : ""); }
      else { setFormStatus("err"); setEvPop(""); }
    } catch (e2) { setFormStatus("err"); setEvPop(""); }
  };

  const NAVS = [
    { id: "services", label: "서비스" },
    { id: "why", label: "강점" },
    { id: "process", label: "이용절차" },
    { id: "contact", label: "오시는길" },
  ];

  // 스크롤 시 헤더 축소 + 현재 섹션 하이라이트(스크롤 스파이)
  useEffect(() => {
    const ids = NAVS.map((n) => n.id);
    const onScroll = () => {
      setScrolled(window.scrollY > 8);
      let cur = "";
      for (const id of ids) {
        const el = document.getElementById(id);
        if (el && el.getBoundingClientRect().top <= 130) cur = id;
      }
      setActiveSec(cur);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  // 최초 진입 이벤트 팝업 ('오늘 하루 보지 않기' = 24시간) — ★ 2026-07-31(KST)까지만 노출
  const EVENT_END = new Date("2026-07-31T23:59:59+09:00").getTime();
  useEffect(() => {
    if (Date.now() > EVENT_END) return;   // 이벤트 종료 후 진입 팝업 미노출
    try {
      const until = Number(localStorage.getItem("pa_event_hide") || 0);
      if (!until || Date.now() > until) setShowEvent(true);
    } catch (e) { setShowEvent(true); }
  }, []);
  const hideEventToday = () => {
    try { localStorage.setItem("pa_event_hide", String(Date.now() + 24 * 3600 * 1000)); } catch (e) {}
    setShowEvent(false);
  };

  // 채널톡(channel.io) 실시간 채팅 위젯 — 랜딩에서만 boot, 떠날 때 shutdown(운영자 페이지엔 안 뜨게).
  useEffect(() => {
    const w = window;
    if (!w.ChannelIO) {
      const ch = function () { ch.c(arguments); };
      ch.q = []; ch.c = function (args) { ch.q.push(args); };
      w.ChannelIO = ch;
      const loader = function () {
        if (w.ChannelIOInitialized) return;
        w.ChannelIOInitialized = true;
        const s = document.createElement("script");
        s.type = "text/javascript"; s.async = true;
        s.src = "https://cdn.channel.io/plugin/ch-plugin-web.js";
        const x = document.getElementsByTagName("script")[0];
        if (x && x.parentNode) x.parentNode.insertBefore(s, x);
      };
      if (document.readyState === "complete") loader();
      else { w.addEventListener("DOMContentLoaded", loader); w.addEventListener("load", loader); }
    }
    try { w.ChannelIO("boot", { pluginKey: CHANNELTALK_KEY }); } catch (e) {}
    return () => { try { w.ChannelIO && w.ChannelIO("shutdown"); } catch (e) {} };
  }, []);

  // 채널톡 메신저 열기 — 모든 '문의하기' 버튼이 우하단 채널톡 채팅을 띄운다.
  const openChat = () => {
    try {
      if (window.ChannelIO) { window.ChannelIO("showMessenger"); return; }
    } catch (e) {}
    // 채널톡 미로딩 시 폴백(전화 안내)
    onPhone();
  };

  // 우하단 채널톡 안내 풍선 — 진입 후 잠깐 뒤 등장(하루 1회, 닫으면 24h 숨김)
  useEffect(() => {
    let until = 0;
    try { until = Number(localStorage.getItem("pa_chattip_hide") || 0); } catch (e) {}
    if (until && Date.now() < until) return;
    const t = window.setTimeout(() => setShowChatTip(true), 1600);
    return () => window.clearTimeout(t);
  }, []);
  const dismissChatTip = () => {
    try { localStorage.setItem("pa_chattip_hide", String(Date.now() + 24 * 3600 * 1000)); } catch (e) {}
    setShowChatTip(false);
  };

  // 좌하단 카카오톡 안내 풍선 — 우측과 동일한 등장/숨김 규칙
  useEffect(() => {
    let until = 0;
    try { until = Number(localStorage.getItem("pa_kakaotip_hide") || 0); } catch (e) {}
    if (until && Date.now() < until) return;
    const t = window.setTimeout(() => setShowKakaoTip(true), 1600);
    return () => window.clearTimeout(t);
  }, []);
  const dismissKakaoTip = () => {
    try { localStorage.setItem("pa_kakaotip_hide", String(Date.now() + 24 * 3600 * 1000)); } catch (e) {}
    setShowKakaoTip(false);
  };

  // 카카오맵 — JS 키가 있으면 실제 인터랙티브 지도 렌더(마커+클릭시 장소). 없으면 폴백 버튼 유지.
  useEffect(() => {
    if (!KAKAO_JS_KEY || KAKAO_JS_KEY === "XXXX") return;
    const init = () => {
      const kakao = window.kakao;
      if (!kakao || !kakao.maps) return;
      kakao.maps.load(() => {
        const c = document.getElementById("kakao-map");
        if (!c) return;
        const pos = new kakao.maps.LatLng(KAKAO_PLACE_COORD.lat, KAKAO_PLACE_COORD.lng);
        const map = new kakao.maps.Map(c, { center: pos, level: 3 });
        const marker = new kakao.maps.Marker({ position: pos });
        marker.setMap(map);
        kakao.maps.event.addListener(marker, "click", () => window.open(KAKAO_PLACE, "_blank", "noopener"));
        setMapReady(true);
      });
    };
    if (window.kakao && window.kakao.maps) { init(); return; }
    const s = document.createElement("script");
    s.async = true;
    s.src = "//dapi.kakao.com/v2/maps/sdk.js?appkey=" + KAKAO_JS_KEY + "&autoload=false";
    s.onload = init;
    document.head.appendChild(s);
  }, []);

  const showToast = (msg) => {
    setToast(msg);
    window.clearTimeout(showToast._t);
    showToast._t = window.setTimeout(() => setToast(""), 2300);
  };
  const copyText = (text, label) => {
    const done = () => showToast(`${label} 복사되었습니다 (${text})`);
    try {
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(text).then(done, () => showToast(`${label}: ${text}`));
      } else {
        const ta = document.createElement("textarea");
        ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0";
        document.body.appendChild(ta); ta.focus(); ta.select();
        document.execCommand("copy"); document.body.removeChild(ta); done();
      }
    } catch (e) { showToast(`${label}: ${text}`); }
  };
  const isMobile = () => (typeof window !== "undefined" && window.matchMedia && window.matchMedia("(max-width: 767px)").matches);
  // 모바일: tel: 로 통화 / PC: tel 미작동 → 번호 표시+클립보드 복사
  const onPhone = () => { if (isMobile()) { window.location.href = "tel:" + TEL; } else { copyText(TEL, "전화번호가"); } };

  // 통일된 오렌지 라인(아웃라인) 아이콘. 여러 path 는 '|' 로 구분.
  const Icon = ({ d, className = "h-6 w-6" }) => (
    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor"
         strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      {String(d).split("|").map((p, i) => <path key={i} d={p} />)}
    </svg>
  );
  const KakaoIcon = ({ className = "h-5 w-5" }) => (
    <svg className={className} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
      <path d="M12 3C6.8 3 2.5 6.4 2.5 10.6c0 2.7 1.8 5 4.5 6.4-.2.7-.7 2.5-.8 2.9 0 .3.2.4.4.2.2-.1 2.8-1.9 3.9-2.7.6.1 1.1.1 1.6.1 5.2 0 9.5-3.4 9.5-7.6S17.2 3 12 3z" />
    </svg>
  );

  const PRIZES = [
    { img: "/event/prize_brg10.png", rank: "1등 · 1명", name: "잉어킹 AR 트리플렛비트 (한글판) BRG 10", val: "20만원 상당" },
    { img: "/event/prize_psa9.png", rank: "2등 · 1명", name: "리피아 P 유나가바 SV 프로모 (일어판) PSA 9", val: "12만원 상당" },
    { img: "/event/prize_brg9.png", rank: "3등 · 4명", name: "잉어킹 AR 트리플렛비트 (한글판) BRG 9", val: "각 9만원 상당" },
  ];
  const ENTRY = [
    "피에이세무회계에 1기 확정 부가세 신고를 의뢰합니다.",
    "신고가 완료됩니다. (~ 7월 25일)",
    "카카오톡 채널(@피에이세무회계)에서 부가세 신고 인증을 해주시면 추첨 대상에 포함됩니다.",
    "7월 31일 추첨 후 당첨자에게 카카오톡 채널 메시지로 안내드립니다.",
  ];

  const STRENGTHS = [
    { d: "M12 2l8 4v6c0 5-3.4 8-8 10-4.6-2-8-5-8-10V6l8-4z|M9 12l2 2 4-4", t: "회계법인 출신 전문성", desc: "삼일회계법인 8년, 상장사 다수 감사·자문 경력" },
    { d: "M16 8a4 4 0 11-8 0 4 4 0 018 0z|M4 21c0-3.9 3.6-6 8-6s8 2.1 8 6", t: "전담 밀착 관리", desc: "시즌·비시즌 없이 전담 사원이 직접 챙깁니다", badge: "비시즌에도 연락 OK" },
    { d: "M6 2h12v20l-2-1.5L12 22l-2-1.5L6 22V2z|M9 7h6|M9 11h6|M9 15h4", t: "투명한 가격정책", desc: "가격 산정 내역 전액 공개, 바가지 없는 정직한 견적", extra: "불합리한 비용은 청구하지 않습니다.", highlight: true },
    { d: "M3 4h18v12H3z|M3 16h18|M10 20h4|M9 8l-2 2 2 2|M15 8l2 2-2 2", t: "개발하는 회계사", desc: "자체 전자결재·세무 자동화 시스템 제공" },
    { d: "M13 2L4 14h7l-1 8 9-12h-7l1-8z", t: "빠른 응대", desc: "막힘없는 문의 대응과 피드백" },
  ];

  const CAREER = [
    ["외부감사", "대기업·상장사 다수 (PCAOB·K-SOX 통합감사)"],
    ["내부회계관리", "대형 상장사·중견기업 구축 다수"],
    ["연결재무제표", "상장사 20개 이상 작성"],
    ["가치평가", "주식·옵션가치평가 검토 다수"],
    ["특수", "표시통화·기능통화 상이 대형 상장사 감사, 사업부문 매각 Carve-out 용역, 거래구조 재설계, 기술특례상장 감사"],
    ["現", "피에이세무회계 대표(2022~), 정비사업위원회 위원"],
  ];

  const SERVICES = [
    { d: "M7 3h7l5 5v13H7V3z|M14 3v5h5|M9 14l2 2 4-4", t: "종합소득세 신고", desc: "사업소득·프리랜서·N잡러·크리에이터" },
    { d: "M6 3h12v18H6z|M9 7h6|M8 11h.01|M12 11h.01|M16 11h.01|M8 15h.01|M12 15h.01|M16 15h.01", t: "부가가치세 신고", desc: "매입·매출 정리부터 예정·확정 신고" },
    { d: "M3 21h18|M5 21V5l7-2v18|M12 21V8l7 3v10|M8 8h.01|M8 12h.01|M8 16h.01|M16 13h.01|M16 17h.01", t: "법인세 신고", desc: "결산·세무조정·법인세 신고" },
    { d: "M5 4a2 2 0 012-2h11v18a2 2 0 01-2 2H7a2 2 0 01-2-2V4z|M9 7h6|M9 11h6|M9 15h3", t: "기장대행", desc: "월 기장 + 원천세(일용직·프리랜서·정규직) + 부가세 신고 포함" },
  ];

  const PROC = {
    online: [
      { d: "M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z", t: "채팅으로 문의" },
      { d: "M7 3h7l5 5v13H7V3z|M14 3v5h5|M9 13h6|M9 16h4", t: "필수 자료 입력 요청" },
      { d: "M6 3h12v18H6z|M9 7h6|M8 11h.01|M12 11h.01|M16 11h.01|M8 15h.01", t: "견적 산정·상담" },
      { d: "M4 13l4 4L20 5|M4 19h16", t: "계약·업무 착수" },
    ],
    offline: [
      { d: "M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z", t: "채팅으로 문의" },
      { d: "M12 21s-6-5.3-6-10a6 6 0 1112 0c0 4.7-6 10-6 10z|M12 11a2 2 0 100-4 2 2 0 000 4z", t: "영업사원·담당 회계사 방문" },
      { d: "M6 3h12v18H6z|M9 7h6|M8 11h.01|M12 11h.01|M16 11h.01|M8 15h.01", t: "견적 산정·상담" },
      { d: "M4 13l4 4L20 5|M4 19h16", t: "계약·업무 착수" },
    ],
  };

  const PROMISES = [
    { d: "M6 2h12v20l-2-1.5L12 22l-2-1.5L6 22V2z|M9 7h6|M9 11h6|M9 15h4", t: "산정 내역 전액 공개", desc: "무엇에 얼마인지 명확히" },
    { d: "M12 2l8 4v6c0 5-3.4 8-8 10-4.6-2-8-5-8-10V6l8-4z", t: "불필요한 비용 청구 없음", desc: "바가지 없는 정직한 견적" },
    { d: "M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z|M8 11h.01|M12 11h.01|M16 11h.01", t: "견적 후 부담 없이 결정", desc: "비용 먼저 확인하고 진행" },
  ];

  return (
    <div className="min-h-screen overflow-x-hidden break-keep bg-white font-sans text-slate-800 [overflow-wrap:break-word]">

      {/* ── 헤더 (sticky, 스크롤 시 축소+그림자, 스크롤 스파이) ── */}
      <header className={"sticky top-0 z-40 border-b bg-white/90 backdrop-blur transition-all " + (scrolled ? "border-orange-100 shadow-sm" : "border-transparent")}>
        <div className={"mx-auto flex max-w-[1080px] items-center justify-between px-6 transition-all " + (scrolled ? "py-2" : "py-3.5")}>
          <a href="/" className="flex min-w-0 items-center gap-2.5">
            <span className={"flex shrink-0 items-center justify-center rounded-lg bg-orange-500 font-extrabold text-white transition-all " + (scrolled ? "h-8 w-8 text-xs" : "h-9 w-9 text-sm")}>PA</span>
            <span className="min-w-0 leading-tight">
              <span className="block text-[15px] font-extrabold text-slate-800">피에이세무회계</span>
              <span className="block text-[9px] font-semibold uppercase tracking-[0.18em] text-orange-500">Private Accountant</span>
            </span>
          </a>
          <nav className="hidden items-center gap-6 text-sm font-semibold text-slate-600 md:flex">
            {NAVS.map((n) => (
              <a key={n.id} href={"#" + n.id} className={"transition hover:text-orange-600 " + (activeSec === n.id ? "text-orange-600" : "")}>{n.label}</a>
            ))}
            <button type="button" onClick={openChat} className="inline-flex items-center gap-1.5 rounded-lg bg-orange-500 px-3.5 py-2 text-sm font-bold text-white shadow-sm transition hover:bg-orange-600"><Icon className="h-4 w-4" d="M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z" />문의하기</button>
          </nav>
          <button type="button" onClick={() => setMenuOpen(true)} aria-label="메뉴 열기"
            className="flex h-11 w-11 items-center justify-center rounded-lg text-slate-700 md:hidden">
            <Icon d="M4 7h16|M4 12h16|M4 17h16" />
          </button>
        </div>
      </header>

      {/* ── 모바일 풀스크린 메뉴 ── */}
      {menuOpen && (
        <div className="fixed inset-0 z-50 flex flex-col bg-white md:hidden">
          <div className="flex items-center justify-between border-b border-orange-100 px-6 py-3.5">
            <span className="text-[15px] font-extrabold text-slate-800">피에이세무회계</span>
            <button type="button" onClick={() => setMenuOpen(false)} aria-label="메뉴 닫기" className="flex h-11 w-11 items-center justify-center text-slate-700">
              <Icon d="M6 6l12 12|M18 6L6 18" />
            </button>
          </div>
          <nav className="flex flex-1 flex-col px-6 py-4 text-lg font-bold text-slate-800">
            {NAVS.map((n) => (
              <a key={n.id} href={"#" + n.id} onClick={() => setMenuOpen(false)} className="border-b border-slate-100 py-4">{n.label}</a>
            ))}
            <button type="button" onClick={() => { setMenuOpen(false); openChat(); }} className="mt-5 inline-flex items-center justify-center gap-2 rounded-xl bg-orange-500 px-5 py-4 font-bold text-white shadow-sm transition hover:bg-orange-600"><Icon className="h-5 w-5" d="M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z" />문의하기</button>
          </nav>
        </div>
      )}

      {/* ── 히어로 ── */}
      <section className="relative overflow-hidden bg-gradient-to-b from-orange-50 via-[#FFF6EE] to-white">
        {/* 배경 추상 도형(blob) */}
        <svg className="pointer-events-none absolute -right-28 -top-28 h-[460px] w-[460px] text-orange-100" viewBox="0 0 200 200" fill="currentColor" aria-hidden="true">
          <path d="M44 -64C58 -54 70 -42 74 -28C78 -14 74 2 67 16C60 30 50 42 37 51C24 60 8 66 -8 67C-24 68 -41 64 -54 53C-67 42 -76 24 -78 5C-80 -14 -75 -34 -63 -47C-51 -60 -32 -66 -14 -70C4 -74 30 -74 44 -64Z" transform="translate(100 100)" />
        </svg>
        <div className="relative mx-auto grid max-w-[1080px] grid-cols-1 items-center gap-10 px-6 py-16 sm:py-20 lg:grid-cols-2">
          <div className="min-w-0">
            <p className="mb-3 text-sm font-bold tracking-wide text-orange-600">믿을 수 있는 세무 파트너</p>
            <h1 className="text-[30px] font-extrabold leading-snug text-slate-900 sm:text-5xl">
              복잡한 세무는<br />PA에서 단순해집니다.
            </h1>
            <p className="mt-4 max-w-[600px] text-[15px] leading-relaxed text-slate-600 sm:text-lg">
              종합소득세·부가가치세·법인세 신고부터 기장대행까지, 회계법인 출신 전문성으로 정확하고 정직하게 처리해 드립니다.
            </p>
            <div className="mt-7 flex flex-wrap gap-3">
              <button type="button" onClick={openChat} className="inline-flex min-h-[48px] items-center gap-2 rounded-xl bg-orange-500 px-6 py-3.5 text-sm font-bold text-white shadow-sm transition hover:bg-orange-600"><Icon className="h-5 w-5" d="M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z" />채팅으로 문의하기</button>
              <a href="#services" className="inline-flex min-h-[48px] items-center rounded-xl border border-orange-200 bg-white px-6 py-3.5 text-sm font-bold text-slate-700 transition hover:border-orange-400">서비스 보기</a>
            </div>
          </div>
          {/* 우측 오렌지 라인 일러스트(세무/성장/서류 추상) */}
          <div className="relative hidden justify-self-center lg:block">
            <svg className="h-auto w-[380px] text-orange-500" viewBox="0 0 380 320" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <rect x="56" y="38" width="178" height="234" rx="14" fill="#ffffff" />
              <path d="M90 80h110M90 110h110M90 140h66" opacity="0.45" />
              <rect x="150" y="150" width="174" height="132" rx="14" fill="#FFF1E6" />
              <path d="M178 252v-26M208 252v-52M238 252v-36M268 252v-66M298 252v-46" stroke="#FB923C" strokeWidth="9" />
              <path d="M168 260h142" opacity="0.5" />
              <path d="M70 300l44-34 30 18 64-54" opacity="0.7" />
              <path d="M196 226l14-4-2 14" opacity="0.7" />
              <circle cx="300" cy="72" r="27" fill="#FFE7D1" stroke="currentColor" />
              <path d="M300 58v28M293 66h11a4 4 0 010 8h-7a4 4 0 000 8h11" strokeWidth="2" />
            </svg>
          </div>
        </div>
      </section>

      {/* ==================================================
          시즌 광고 배너 — 시즌 바뀌면 아래 헤드라인/서브카피/버튼 3줄만 교체하세요
          종소세 / 부가세 / 법인세 / 기본 중 택1 (문구 모음은 LandingPage 위 주석 참고)
         ================================================== */}
      <section className="bg-orange-500">
        <div className="mx-auto flex max-w-[1080px] flex-col items-start gap-3 px-6 py-5 sm:flex-row sm:items-center sm:justify-between sm:py-6">
          <div className="flex min-w-0 items-center gap-3">
            <Icon className="hidden h-9 w-9 shrink-0 text-white/80 sm:block" d="M3 11l9-7 9 7|M5 10v10h14V10|M9 20v-6h6v6" />
            <div className="min-w-0">
              <h2 className="text-lg font-extrabold leading-snug text-white sm:text-2xl">복잡한 세무는 PA에서 단순해집니다</h2>{/* ← 헤드라인 */}
              <p className="mt-1 text-sm text-orange-50 sm:text-base">한 건 한 건 직접 챙기겠습니다</p>{/* ← 서브카피 */}
            </div>
          </div>
          <button type="button" onClick={openChat}
             className="min-h-[48px] w-full shrink-0 rounded-xl bg-white px-6 py-3 text-center text-sm font-bold text-orange-600 shadow-sm transition hover:bg-orange-50 sm:w-auto">무료 상담하기</button>{/* ← 버튼 문구 */}
        </div>
      </section>
      {/* =============== 시즌 배너 끝 =============== */}

      {/* ── 강점 (Why Us) — PC 3+2 배치(여백 있게) ── */}
      <section id="why" className="relative overflow-hidden bg-[#FFF9F4] py-16 sm:py-20">
        <svg className="pointer-events-none absolute -left-24 bottom-0 h-72 w-72 text-orange-100/70" viewBox="0 0 200 200" fill="currentColor" aria-hidden="true">
          <path d="M44 -64C58 -54 70 -42 74 -28C78 -14 74 2 67 16C60 30 50 42 37 51C24 60 8 66 -8 67C-24 68 -41 64 -54 53C-67 42 -76 24 -78 5C-80 -14 -75 -34 -63 -47C-51 -60 -32 -66 -14 -70C4 -74 30 -74 44 -64Z" transform="translate(100 100)" />
        </svg>
        <div className="relative mx-auto max-w-[1080px] px-6">
          <div className="mb-9 text-center">
            <h2 className="text-2xl font-extrabold text-slate-900 sm:text-3xl">왜 피에이세무회계인가</h2>
          </div>
          <div className="flex flex-wrap justify-center gap-5">
            {STRENGTHS.map((s) => (
              <div key={s.t} className={"flex min-h-[200px] w-full flex-col rounded-2xl border bg-white p-6 shadow-sm sm:w-[calc(50%-10px)] lg:w-[calc(33.333%-14px)] " + (s.highlight ? "border-orange-300 ring-1 ring-orange-200" : "border-orange-100")}>
                <span className="mb-3.5 flex h-12 w-12 items-center justify-center rounded-xl bg-orange-100 text-orange-600"><Icon d={s.d} /></span>
                <h3 className="text-base font-bold leading-snug text-slate-800">{s.t}</h3>
                <p className="mt-2 text-[13.5px] leading-relaxed text-slate-500">{s.desc}</p>
                {s.extra && <p className="mt-2 text-[13px] font-semibold text-orange-600">{s.extra}</p>}
                {s.badge && <span className="mt-auto pt-3"><span className="inline-flex w-fit rounded-full bg-orange-500 px-2.5 py-1 text-[11px] font-bold text-white">{s.badge}</span></span>}
              </div>
            ))}
          </div>

          {/* 경력 더보기 (아코디언) */}
          <details className="group mx-auto mt-7 max-w-3xl rounded-2xl border border-orange-100 bg-white">
            <summary className="flex cursor-pointer list-none items-center justify-between px-5 py-4 text-sm font-bold text-slate-700">
              <span>대표 회계사 경력 더보기</span>
              <Icon className="h-5 w-5 shrink-0 text-orange-500 transition group-open:rotate-180" d="M6 9l6 6 6-6" />
            </summary>
            <div className="px-2 pb-3">
              <dl className="divide-y divide-orange-50 border-t border-orange-50">
                {CAREER.map(([k, v]) => (
                  <div key={k} className="grid grid-cols-1 gap-1 px-3 py-3 sm:grid-cols-[110px_1fr] sm:gap-4">
                    <dt className="min-w-0 text-[13px] font-bold text-orange-600">{k}</dt>
                    <dd className="min-w-0 text-[13px] leading-relaxed text-slate-600">{v}</dd>
                  </div>
                ))}
              </dl>
              <p className="px-3 pb-1 pt-2 text-[11px] text-slate-400">※ 기업명은 비밀유지 원칙에 따라 표기하지 않습니다.</p>
            </div>
          </details>
        </div>
      </section>

      {/* ── 서비스 안내 ── */}
      <section id="services" className="mx-auto max-w-[1080px] px-6 py-16 sm:py-20">
        <div className="mb-9 text-center">
          <h2 className="text-2xl font-extrabold text-slate-900 sm:text-3xl">서비스 안내</h2>
          <p className="mt-2 text-sm text-slate-500">사업의 시작부터 성장까지, 필요한 세무를 한 곳에서.</p>
        </div>
        <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
          {SERVICES.map((s) => (
            <div key={s.t} className="flex min-h-[208px] min-w-0 flex-col rounded-2xl border border-orange-100 bg-white p-6 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md">
              <span className="mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-orange-100 text-orange-600"><Icon d={s.d} /></span>
              <h3 className="text-base font-bold text-slate-800">{s.t}</h3>
              <p className="mt-2 text-[13.5px] leading-relaxed text-slate-500">{s.desc}</p>
            </div>
          ))}
        </div>
        <p className="mt-7 text-center text-sm text-slate-500">필요한 서비스만 단건으로도, 묶어서도 의뢰 가능합니다.</p>
      </section>

      {/* ── 이용 절차 (온/오프라인 탭) ── */}
      <section id="process" className="bg-[#FFF9F4] py-16 sm:py-20">
        <div className="mx-auto max-w-[1080px] px-6">
          <div className="mb-7 text-center">
            <h2 className="text-2xl font-extrabold text-slate-900 sm:text-3xl">이용 절차</h2>
          </div>
          <div className="mx-auto mb-8 flex w-full max-w-xs rounded-xl bg-orange-100 p-1">
            {[["online", "온라인"], ["offline", "오프라인"]].map(([k, label]) => (
              <button key={k} type="button" onClick={() => setProcTab(k)}
                className={"min-h-[44px] flex-1 rounded-lg text-sm font-bold transition " + (procTab === k ? "bg-orange-500 text-white shadow-sm" : "text-orange-700 hover:text-orange-800")}>{label}</button>
            ))}
          </div>
          <div className="flex flex-col gap-3 sm:flex-row sm:items-stretch">
            {PROC[procTab].map((step, i) => (
              <React.Fragment key={i}>
                <div className="flex min-w-0 flex-1 items-center gap-3 rounded-2xl border border-orange-100 bg-white p-4 sm:flex-col sm:items-start sm:gap-3">
                  <span className="relative flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-orange-100 text-orange-600">
                    <Icon className="h-5 w-5" d={step.d} />
                    <span className="absolute -right-1.5 -top-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-orange-500 text-[11px] font-bold text-white ring-2 ring-[#FFF9F4]">{i + 1}</span>
                  </span>
                  <span className="min-w-0 text-sm font-semibold leading-snug text-slate-700">{step.t}</span>
                </div>
                {i < PROC[procTab].length - 1 && (
                  <div className="hidden items-center justify-center text-orange-300 sm:flex"><Icon className="h-5 w-5" d="M5 12h14|M13 6l6 6-6 6" /></div>
                )}
              </React.Fragment>
            ))}
          </div>
        </div>
      </section>

      {/* ── 비용 안내 (맞춤 견적) ── */}
      <section className="mx-auto max-w-[1080px] px-6 py-16 sm:py-20">
        <div className="mx-auto max-w-3xl text-center">
          <h2 className="text-2xl font-extrabold text-slate-900 sm:text-3xl">투명한 맞춤 견적</h2>
          <p className="mt-2 text-sm font-semibold text-orange-600">똑같은 신고는 없습니다. 상황에 딱 맞는 정직한 견적을 드립니다.</p>
          <p className="mt-4 text-[14px] leading-relaxed text-slate-600">
            세무 비용은 소득 규모·거래 건수·업종·신고 난이도에 따라 달라집니다. 일률적인 정찰가는 누군가에겐 비싸고 누군가에겐 부실합니다. 그래서 PA는 상담을 통해 꼭 필요한 항목만 담은 맞춤 견적을 드립니다.
          </p>
        </div>
        <div className="mt-9 grid grid-cols-1 gap-5 sm:grid-cols-3">
          {PROMISES.map((p, i) => (
            <div key={p.t} className={"min-w-0 rounded-2xl border bg-white p-6 text-center shadow-sm " + (i === 0 ? "border-orange-300 ring-1 ring-orange-200" : "border-orange-100")}>
              <span className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-orange-100 text-orange-600"><Icon d={p.d} /></span>
              <h3 className="text-[15px] font-bold leading-snug text-slate-800">{p.t}</h3>
              <p className="mt-1.5 text-[13px] leading-relaxed text-slate-500">{p.desc}</p>
            </div>
          ))}
        </div>
        <div className="mt-9 text-center">
          <button type="button" onClick={openChat} className="inline-flex min-h-[48px] items-center justify-center gap-2 rounded-xl bg-orange-500 px-7 py-3.5 text-sm font-bold text-white shadow-sm transition hover:bg-orange-600"><Icon className="h-5 w-5" d="M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z" />채팅으로 견적 문의</button>
          <p className="mt-2 text-xs text-slate-400">1분이면 견적 받으실 수 있습니다.</p>
        </div>
      </section>

      {/* ── 상담 신청 (무장벽 — 로그인/앱 불필요) ── */}
      <section id="apply" className="mx-auto max-w-[1080px] px-6 py-16 sm:py-20">
        <div className="mx-auto max-w-2xl">
          <div className="mb-7 text-center">
            <h2 className="text-2xl font-extrabold text-slate-900 sm:text-3xl">상담 신청</h2>
            <p className="mt-2 text-sm text-slate-500">로그인·앱 없이 바로 신청하세요. 빠르게 연락드리겠습니다.</p>
          </div>
          {formStatus === "ok" ? (
            <div className="rounded-2xl border border-orange-200 bg-white p-8 text-center shadow-sm">
              <span className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-orange-100 text-orange-600"><Icon d="M20 6L9 17l-5-5" /></span>
              <p className="text-base font-bold text-slate-800">상담 신청이 접수되었습니다.</p>
              <p className="mt-1.5 text-sm text-slate-500">빠르게 연락드리겠습니다. 부가세 1기 이벤트도 진행 중이에요!</p>
              <button type="button" onClick={() => setFormStatus("")} className="mt-4 text-sm font-bold text-orange-600">다시 작성하기</button>
            </div>
          ) : (
            <form onSubmit={submitConsult} className="rounded-2xl border border-orange-100 bg-white p-6 shadow-sm sm:p-8">
              <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
                <label className="block">
                  <span className="mb-1.5 block text-[13px] font-bold text-slate-600">이름 <span className="text-orange-500">*</span></span>
                  <input type="text" value={form.name} onChange={(e) => setF("name", e.target.value)} placeholder="홍길동"
                    className="min-h-[46px] w-full rounded-xl border border-slate-200 px-4 text-sm outline-none focus:border-orange-400" />
                </label>
                <label className="block">
                  <span className="mb-1.5 block text-[13px] font-bold text-slate-600">연락처 <span className="text-orange-500">*</span></span>
                  <input type="tel" inputMode="tel" value={form.phone} onChange={(e) => setF("phone", e.target.value)} placeholder="010-0000-0000"
                    className="min-h-[46px] w-full rounded-xl border border-slate-200 px-4 text-sm outline-none focus:border-orange-400" />
                </label>
              </div>
              <label className="mt-4 block">
                <span className="mb-1.5 block text-[13px] font-bold text-slate-600">문의 유형</span>
                <select value={form.type} onChange={(e) => setF("type", e.target.value)}
                  className="min-h-[46px] w-full rounded-xl border border-slate-200 bg-white px-4 text-sm outline-none focus:border-orange-400">
                  <option value="">선택 안 함</option>
                  <option>종합소득세</option>
                  <option>부가가치세</option>
                  <option>법인세</option>
                  <option>기장대행</option>
                  <option>기타</option>
                </select>
              </label>
              <label className="mt-4 block">
                <span className="mb-1.5 block text-[13px] font-bold text-slate-600">문의 내용</span>
                <textarea value={form.msg} onChange={(e) => setF("msg", e.target.value)} rows={3} placeholder="간단히 적어주세요. (선택)"
                  className="w-full rounded-xl border border-slate-200 p-4 text-sm outline-none focus:border-orange-400"></textarea>
              </label>
              <label className="mt-4 flex items-start gap-2.5 text-[13px] text-slate-600">
                <input type="checkbox" checked={form.agree} onChange={(e) => setF("agree", e.target.checked)} className="mt-0.5 h-4 w-4 shrink-0 accent-orange-500" />
                <span className="min-w-0">개인정보 수집·이용(상담 응대 목적)에 동의합니다. <span className="text-orange-500">*</span></span>
              </label>
              {formStatus === "need" && <p className="mt-3 text-[13px] font-semibold text-red-500">이름과 연락처를 입력해 주세요.</p>}
              {formStatus === "agree" && <p className="mt-3 text-[13px] font-semibold text-red-500">개인정보 수집·이용에 동의해 주세요.</p>}
              {formStatus === "err" && <p className="mt-3 text-[13px] font-semibold text-red-500">온라인 접수가 일시적으로 어렵습니다. 채팅 또는 전화(031-432-8615)로 문의해 주세요.</p>}
              <button type="submit" disabled={formStatus === "sending"}
                className="mt-5 flex min-h-[50px] w-full items-center justify-center rounded-xl bg-orange-500 text-sm font-bold text-white shadow-sm transition hover:bg-orange-600 disabled:opacity-60">
                {formStatus === "sending" ? "접수 중…" : "상담 신청하기"}
              </button>
            </form>
          )}
        </div>
      </section>

      {/* ── 오시는 길 / 연락처 (PC도 상하 배치) ── */}
      <section id="contact" className="bg-[#FFF9F4] py-16 sm:py-20">
        <div className="mx-auto max-w-[1080px] px-6">
          <div className="mb-8 text-center">
            <h2 className="text-2xl font-extrabold text-slate-900 sm:text-3xl">오시는 길 · 연락처</h2>
          </div>

          {/* 연락처 카드 (위) */}
          <div className="mx-auto max-w-3xl rounded-2xl border border-orange-100 bg-white p-6 sm:p-8">
            <dl className="space-y-4 text-sm">
              <div className="flex gap-3"><dt className="w-14 shrink-0 font-bold text-orange-600">주소</dt><dd className="min-w-0 leading-relaxed text-slate-700">경기도 안산시 단원구 지원로 107 관리동 1층 119-1호</dd></div>
              <div className="flex items-start gap-3"><dt className="w-14 shrink-0 font-bold text-orange-600">전화</dt><dd className="min-w-0 text-slate-700">
                {/* 모바일: 터치 시 통화 / PC: 클릭 시 번호 복사(tel 미작동) */}
                <a href={"tel:" + TEL} className="font-semibold text-slate-800 md:hidden">{TEL}</a>
                <button type="button" onClick={() => copyText(TEL, "전화번호가")} className="hidden items-center gap-1.5 font-semibold text-slate-800 transition hover:text-orange-600 md:inline-flex">{TEL}<Icon className="h-3.5 w-3.5 text-slate-400" d="M9 9h10v10H9z|M5 15V5h10" /></button>
              </dd></div>
              <div className="flex items-start gap-3"><dt className="w-14 shrink-0 font-bold text-orange-600">이메일</dt><dd className="min-w-0 text-slate-700">
                <button type="button" onClick={() => copyText(EMAIL, "이메일이")} className="inline-flex items-center gap-1.5 break-all text-left font-semibold text-slate-800 transition hover:text-orange-600">{EMAIL}<Icon className="h-3.5 w-3.5 shrink-0 text-slate-400" d="M9 9h10v10H9z|M5 15V5h10" /></button>
              </dd></div>
            </dl>
            <div className="mt-6 grid grid-cols-1 gap-3 sm:grid-cols-3">
              <button type="button" onClick={openChat} className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-orange-500 px-4 py-3 text-sm font-bold text-white transition hover:bg-orange-600"><Icon className="h-5 w-5" d="M21 12a8 8 0 01-11.3 7.3L4 21l1.7-5.7A8 8 0 1121 12z" />채팅으로 문의하기</button>
              <a href={NAVER_PLACE} target="_blank" rel="noopener" className="flex min-h-[48px] items-center justify-center rounded-xl px-4 py-3 text-sm font-bold text-white transition hover:brightness-95" style={{ backgroundColor: "#03C75A" }}>네이버 플레이스</a>
              <a href={KMONG} target="_blank" rel="noopener" className="flex min-h-[48px] items-center justify-center rounded-xl px-4 py-3 text-sm font-bold text-white transition hover:brightness-95" style={{ backgroundColor: "#14B8A6" }}>크몽에서 문의하기</a>
            </div>
            <div className="mt-3 text-center text-[12px] font-semibold text-slate-500">
              <span className="text-amber-500">★ 5.0</span> · 크몽 리뷰 39 — 검증된 전문가 페이지
            </div>
            {/* (B) 네이버 톡톡 버튼 자리 — 링크 확보 후 위 그리드에 추가 예정 */}
          </div>

          {/* 지도 (아래) — 카카오맵. JS키 있으면 실제 지도, 없으면 '카카오맵에서 보기' 폴백 */}
          <div className="relative mx-auto mt-6 max-w-3xl overflow-hidden rounded-xl border border-orange-100" style={{ height: "340px" }}>
            <div id="kakao-map" className="absolute inset-0 h-full w-full"></div>
            {!mapReady && (
              <a href={KAKAO_PLACE} target="_blank" rel="noopener" className="group absolute inset-0 flex flex-col items-center justify-center gap-3 bg-slate-50 p-6 text-center">
                <span className="flex h-12 w-12 items-center justify-center rounded-full bg-orange-500 text-white shadow-lg">
                  <Icon className="h-6 w-6" d="M12 21s-6-5.3-6-10a6 6 0 1112 0c0 4.7-6 10-6 10z|M12 11a2 2 0 100-4 2 2 0 000 4z" />
                </span>
                <p className="max-w-xs font-bold leading-relaxed text-slate-700">경기도 안산시 단원구 지원로 107 관리동 1층 119-1호</p>
                <span className="inline-flex items-center gap-2 rounded-lg px-5 py-2.5 text-sm font-bold text-slate-900 shadow transition group-hover:brightness-95" style={{ backgroundColor: "#FEE500" }}>카카오맵에서 보기<Icon className="h-4 w-4" d="M5 12h14|M13 6l6 6-6 6" /></span>
              </a>
            )}
            {mapReady && (
              <a href={KAKAO_PLACE} target="_blank" rel="noopener" className="absolute bottom-3 right-3 z-10 inline-flex items-center gap-1.5 rounded-lg bg-white/95 px-3 py-2 text-xs font-bold text-slate-700 shadow ring-1 ring-slate-200">카카오맵에서 보기<Icon className="h-3.5 w-3.5" d="M5 12h14|M13 6l6 6-6 6" /></a>
            )}
          </div>
        </div>
      </section>

      {/* ── 푸터 ── */}
      <footer className="border-t border-orange-100 bg-white py-10">
        <div className="mx-auto max-w-[1080px] px-6 text-left text-[13px] leading-[1.95] text-slate-500">
          <div className="text-sm font-extrabold text-slate-700">피에이세무회계</div>
          <div>대표 손성걸</div>
          <div>사업자등록번호 281-45-00895</div>
          <div>경기도 안산시 단원구 지원로 107 관리동 1층 119-1호</div>
          <div>전화 031-432-8615&nbsp;&nbsp;|&nbsp;&nbsp;이메일 Kicpa-pa@naver.com</div>
          <div className="mt-3 text-xs text-slate-400">© 2026 피에이세무회계 (PA Tax Accounting). All rights reserved.</div>
        </div>
      </footer>

      {/* ── 우하단 채널톡 안내 풍선 — 채팅 위젯(채팅 상담하기)으로 시선 유도 ── */}
      {showChatTip && (
        <div className="pa-chattip fixed bottom-[88px] right-5 z-[45] flex items-start gap-1.5 md:bottom-24">
          <button type="button" onClick={() => { dismissChatTip(); openChat(); }}
            className="relative max-w-[230px] rounded-2xl bg-white px-4 py-3 text-left shadow-xl ring-1 ring-orange-100 transition hover:ring-orange-300">
            <span className="block text-[13.5px] font-bold text-slate-800">채팅으로 문의하세요</span>
            <span className="mt-0.5 block text-[11px] text-slate-400">로그인 없는 실시간 상담이 열려 있어요</span>
            {/* 꼬리 — 우하단 채널톡 버튼 방향 */}
            <span className="absolute -bottom-1 right-7 h-3 w-3 rotate-45 bg-white"></span>
          </button>
          <button type="button" onClick={dismissChatTip} aria-label="안내 닫기"
            className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-slate-700/85 text-white shadow-md transition hover:bg-slate-800">
            <Icon className="h-3.5 w-3.5" d="M6 6l12 12|M18 6L6 18" />
          </button>
        </div>
      )}

      {/* ── 좌하단 카카오톡 문의 — 아이콘 버튼 + 안내 풍선 (PC·모바일 공통 고정) ── */}
      <div className="fixed bottom-5 left-4 z-40 flex flex-col items-start gap-2.5">
        {showKakaoTip && (
          <div className="pa-chattip flex items-start gap-1.5">
            <a href={KAKAO} target="_blank" rel="noopener"
              className="relative max-w-[230px] rounded-2xl bg-white px-4 py-3 text-left shadow-xl ring-1 ring-amber-100 transition hover:ring-amber-300 break-keep">
              <span className="block text-[13.5px] font-bold text-slate-800">카카오톡으로 문의하세요</span>
              <span className="mt-0.5 block text-[11px] text-slate-400">카카오 채널로 바로 연결돼요</span>
              {/* 꼬리 — 좌하단 카카오 버튼 방향 */}
              <span className="absolute -bottom-1 left-7 h-3 w-3 rotate-45 bg-white"></span>
            </a>
            <button type="button" onClick={dismissKakaoTip} aria-label="안내 닫기"
              className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-slate-700/85 text-white shadow-md transition hover:bg-slate-800">
              <Icon className="h-3.5 w-3.5" d="M6 6l12 12|M18 6L6 18" />
            </button>
          </div>
        )}
        <a href={KAKAO} target="_blank" rel="noopener" aria-label="카카오톡 문의하기"
          className="flex h-14 w-14 items-center justify-center rounded-full text-slate-900 shadow-lg ring-1 ring-black/5 transition hover:brightness-95"
          style={{ backgroundColor: "#FEE500" }}>
          <KakaoIcon className="h-7 w-7" />
        </a>
      </div>

      {/* ── 복사 토스트 ── */}
      {toast && (
        <div className="fixed left-1/2 top-5 z-[80] -translate-x-1/2 rounded-full bg-slate-900/90 px-5 py-2.5 text-center text-sm font-semibold text-white shadow-lg">{toast}</div>
      )}

      {/* ── 이벤트 팝업: 진입(A) / 상담폼 제출 즉시(A) → 접수 후(B) ── */}
      {showEvent && <EventPopup variant="entry" onClose={() => setShowEvent(false)} onHideToday={hideEventToday} />}
      {evPop && <EventPopup variant={evPop} onClose={() => setEvPop("")} />}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 비밀 주소(/about/philosophy) 진입 — 위장 '경영철학' 페이지. 주소를 아는 직원만 들어옴.
// 별도 '임직원 로그인' 페이지/라벨 없이, 페이지 하단의 수수한 두 버튼으로 바로 접속.
// ─────────────────────────────────────────────────────────────
function AdminGate({ chooseRole }) {
  const VALUES = [
    { t: "정직과 신뢰", d: "모든 신고와 상담은 사실에 근거합니다. 고객과 쌓은 신뢰가 가장 큰 자산이라 믿습니다." },
    { t: "고객 중심", d: "고객의 사업을 내 일처럼 생각합니다. 작은 문의에도 끝까지 책임지고 응대합니다." },
    { t: "전문성과 책임", d: "끊임없이 학습하며 변화하는 세법에 한발 앞서 대응합니다. 결과에 책임집니다." },
    { t: "동반 성장", d: "고객의 성장이 곧 우리의 성장입니다. 단기 이익보다 오래가는 관계를 우선합니다." },
  ];
  return (
    <div className="min-h-screen bg-white font-sans text-slate-900">
      <header className="border-b border-slate-200">
        <div className="mx-auto flex max-w-3xl items-center gap-2.5 px-5 py-4">
          <div className="flex h-8 w-8 select-none items-center justify-center rounded-lg bg-slate-700 text-xs font-bold text-white">PA</div>
          <div className="text-sm font-bold text-slate-700">피에이세무회계 — 경영철학</div>
        </div>
      </header>
      <main className="mx-auto max-w-3xl px-5 py-12">
        <h1 className="text-2xl font-bold text-slate-800">우리가 일하는 방식</h1>
        <p className="mt-3 text-sm leading-7 text-slate-500">
          피에이세무회계는 숫자 너머의 사람을 봅니다. 세무는 단순한 신고 업무가 아니라,
          고객이 안심하고 사업에 집중할 수 있도록 돕는 일이라고 믿습니다. 우리는 다음의 가치를 지킵니다.
        </p>
        <div className="mt-8 space-y-6">
          {VALUES.map((v, i) => (
            <div key={v.t} className="border-l-2 border-slate-200 pl-4">
              <h2 className="text-base font-semibold text-slate-700">{i + 1}. {v.t}</h2>
              <p className="mt-1 text-sm leading-7 text-slate-500">{v.d}</p>
            </div>
          ))}
        </div>
        <p className="mt-10 text-xs leading-6 text-slate-400">본 페이지는 임직원 내부 안내용입니다. 무단 열람을 금합니다.</p>

        <div className="mt-8 flex gap-2 border-t border-slate-100 pt-6">
          <button onClick={() => chooseRole("operator")} className="rounded-lg border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-600 hover:border-orange-300">운영자</button>
          <button onClick={() => chooseRole("clerk")} className="rounded-lg border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-600 hover:border-orange-300">직원</button>
          <button onClick={() => chooseRole("sales")} className="rounded-lg border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-600 hover:border-orange-300">영업사원</button>
        </div>
      </main>
    </div>
  );
}

// 엔진(SSD 서버) OFF 시 노출 — 위장 랜딩은 그대로 두고, 실제 기능 진입(직원 로그인/고객 접수)만 이 화면으로.
// 정적 프론트는 Cloudflare 에 상시 떠 있고, 엔진이 꺼지면 /health 가 실패 → '영업시간 외'.
function ClosedNotice() {
  return (
    <div className="flex min-h-screen items-center justify-center bg-slate-50 px-6 font-sans text-slate-900">
      <div className="max-w-md text-center">
        <div className="mx-auto mb-5 flex h-14 w-14 items-center justify-center rounded-2xl bg-orange-600 text-lg font-bold tracking-tight text-white">PA</div>
        <h1 className="text-xl font-bold text-slate-800">지금은 운영시간이 아닙니다</h1>
        <p className="mt-3 text-sm leading-relaxed text-slate-500">
          현재 접수·상담 업무가 운영되지 않는 시간입니다.<br />
          운영시간에 다시 방문해 주시면 도와드리겠습니다.
        </p>
        <p className="mt-6 text-xs text-slate-400">피에이세무회계</p>
      </div>
    </div>
  );
}

function App() {
  const params = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : new URLSearchParams();
  const formParam = params.get("form");
  const fromId = params.get("from");
  const handoffToken = params.get("token");   // 1회용 링크(영업사원·운영자 생성) → 고객 직접입력
  const urlRole = params.get("role");
  // 직원 진입은 오직 비밀 주소에서만. 그 외 첫 진입은 위장 홈페이지.
  const _path = typeof window !== "undefined" ? window.location.pathname : "/";
  const loginMode = _path === "/about/philosophy";
  // 1회용 링크 종류 조회(비소모): undefined=로딩 / {ok:true,kind,...} / {ok:false,reason}
  const [handoffInfo, setHandoffInfo] = useState(undefined);
  useEffect(() => {
    if (!handoffToken) return;
    apiGet(`/api/handoff/${handoffToken}`, { ok: false, reason: "error" }).then(setHandoffInfo);
  }, []);

  // 엔진(SSD 서버) 가동 여부 — null=확인중 / true=ON / false=OFF(영업시간 외).
  // /health 를 20초마다 폴링. 엔진이 켜지면 자동으로 풀기능 복귀(별도 새로고침 불필요).
  const [engineUp, setEngineUp] = useState(null);
  useEffect(() => {
    let alive = true;
    const ping = async () => {
      try {
        const res = await fetch(`${API_BASE}/health`, { credentials: "same-origin", cache: "no-store" });
        if (alive) setEngineUp(res.ok);
      } catch (e) {
        if (alive) setEngineUp(false);
      }
    };
    ping();
    const t = setInterval(ping, 20000);
    return () => { alive = false; clearInterval(t); };
  }, []);

  // ── 역할 접근제어: 고객=고객페이지 / 영업사원=영업페이지 / 운영자=전체 ──
  // 서버 인증(세션 쿠키)이 진짜 게이트. role 은 '보여줄 화면', authTier 는 '실제 로그인 등급'.
  // authTier==='operator' 면 운영자 쿠키가 모든 권한(영업 포함)을 열어주므로 비번 없이 화면 전환 허용.
  const [authTier, setAuthTier] = useState(() => {
    try { return localStorage.getItem("site_auth") || null; } catch (e) { return null; }
  });
  const setTier = (t) => { try { t ? localStorage.setItem("site_auth", t) : localStorage.removeItem("site_auth"); } catch (e) {} setAuthTier(t); };
  const [role, setRole] = useState(() => {
    if (handoffToken) return "customer";
    if (["customer", "sales", "operator"].includes(urlRole)) return urlRole;
    try { return localStorage.getItem("site_role") || null; } catch (e) { return null; }
  });
  const persistRole = (r) => { try { localStorage.setItem("site_role", r); } catch (e) {} setRole(r); };
  // 고객은 비번 없음. 영업사원/운영자는 서버 비밀번호로 로그인 → 세션 쿠키(HttpOnly) 발급.
  const chooseRole = async (r) => {
    if (r === "customer") { persistRole(r); return; }
    const roleName = { sales: "영업사원", clerk: "직원", operator: "운영자" }[r] || "운영자";
    const pw = window.prompt(`${roleName} 비밀번호를 입력하세요`);
    if (pw == null) return;
    const res = await apiPost("/api/login", { role: r, password: pw }, null);
    if (res && res.ok) { setTier(r); persistRole(r); }
    else alert("비밀번호가 올바르지 않습니다");
  };
  const logoutRole = () => {
    apiPost("/api/logout", {}, null);   // 서버 세션 쿠키도 제거
    setTier(null);
    try { localStorage.removeItem("site_role"); } catch (e) {}
    setRole(null);
  };
  // 운영자 화면 전환 — 비번 없이 role(보여줄 화면)만 바꿈. role 변경 후 본인 화면으로 못 돌아오는 것 방지.
  const switchView = (r) => persistRole(r);
  const roleBadge = ({ customer: "고객", sales: "영업사원", clerk: "직원", operator: "운영자" }[authTier === "clerk" ? "clerk" : role]) || "";
  // 운영자 전용 빠른 화면 전환 칩 — authTier==='operator' 일 때 모든 헤더에 노출.
  const opViewSwitch = authTier === "operator" ? (
    <div className="flex items-center gap-1 rounded-lg bg-emerald-50 p-0.5 ring-1 ring-emerald-200">
      <span className="px-1 text-[10px] font-bold text-emerald-600">보기</span>
      {[["customer", "고객"], ["sales", "영업"], ["operator", "운영자"]].map(([k, l]) => (
        <button key={k} onClick={() => switchView(k)} className={`rounded px-1.5 py-0.5 text-[10px] font-medium transition ${role === k ? "bg-emerald-500 text-white" : "text-emerald-700 hover:bg-emerald-100"}`}>{l}</button>
      ))}
    </div>
  ) : null;

  const quoteSource = fromId ? MOCK_REQUESTS.find((r) => r.request_id === fromId) : null;
  const prefill = quoteSource
    ? {
        name: quoteSource.name,
        jumin: quoteSource.jumin,
        hometax_id: quoteSource.hometax_id,
        hometax_pw: quoteSource.hometax_pw,
        service: quoteSource.service,
        memo: quoteSource.memo,
      }
    : null;

  const [tab, setTab] = useState(formParam === "quote" ? "t_quote" : formParam === "full" ? "t_full" : "operator");

  // 데모용 prefill: 첫 견적 목업을 본입력에 채워 띄움
  const demoQuote = MOCK_REQUESTS.find((r) => r.mode === "quote");
  const demoPrefill = demoQuote
    ? {
        name: demoQuote.name,
        jumin: demoQuote.jumin,
        hometax_id: demoQuote.hometax_id,
        hometax_pw: demoQuote.hometax_pw,
        service: demoQuote.service,
        memo: demoQuote.memo,
      }
    : null;

  // 고객화면 미리보기(데모) — 각 폼의 [입력]/[완료] 화면. 평상시 접어둠(운영자 default).
  const [menuOpen, setMenuOpen] = useState(false);   // 운영자 ⋮ 메뉴(데모·보기전환·로그아웃)
  const DEMO_TABS = [
    { key: "t_quote", label: "단순견적 · 입력" },
    { key: "t_quote_done", label: "단순견적 · 완료" },
    { key: "t_sa", label: "간편인증 · 입력" },
    { key: "t_sa_done", label: "간편인증 · 완료" },
    { key: "t_full", label: "개인전체 · 입력" },
    { key: "t_full_done", label: "개인전체 · 완료" },
    { key: "t_corp", label: "법인 · 입력" },
    { key: "t_corp_done", label: "법인 · 완료" },
    { key: "t_prefill", label: "견적→전체 · 입력" },
    { key: "t_prefill_done", label: "견적→전체 · 완료" },
  ];

  const renderTab = () => {
    switch (tab) {
      case "t_quote":
        return <QuoteForm onBack={() => {}} />;
      case "t_full":
        return <CustomerForm startMode="full" lockMode />;
      case "t_corp":
        return <CorpForm onBack={() => {}} />;
      case "t_prefill":
        return <CustomerForm startMode="full" lockMode prefill={demoPrefill} fromQuoteId={demoQuote?.request_id} />;
      case "t_sa":
        return <SAForm />;
      // 입력후(완료) 화면 미리보기 — 고객이 제출/인증 후 보는 화면
      case "t_quote_done":
      case "t_sa_done":
      case "t_full_done":
      case "t_prefill_done":
        return <SubmitSuccess requestId="REQ-샘플-001" />;
      case "t_corp_done":
        return <SuccessScreen requestId="REQ-샘플-001" message="담당 직원이 확인 후 자료를 수집해 드립니다." onReset={() => {}} />;
      case "operator":
      default:
        return <OperatorPage canDelete={authTier !== "clerk"} isClerk={authTier === "clerk"} />;
    }
  };

  // 영업사원 QR 링크로 들어온 고객 — 깔끔한 입력 폼만(데모 탭 숨김)
  if (handoffToken) {
    const titles = { sa: "간편인증 신청", quote: "고객정보 입력", full: "정식 고객등록", corp: "법인 고객등록" };
    const info = handoffInfo;
    const kind = info && info.ok ? (info.kind || "quote") : "quote";
    let body;
    if (info === undefined) {
      body = <div className="mx-auto max-w-lg px-4 py-16 text-center text-sm text-slate-400">불러오는 중…</div>;
    } else if (!info.ok) {
      const msg = info.reason === "used"
        ? "이미 사용된 링크입니다. 1회용 링크라 다시 사용할 수 없어요 — 담당자에게 새 링크를 요청하세요."
        : "유효하지 않은 링크입니다. 주소를 다시 확인하거나 담당자에게 새 링크를 요청하세요.";
      body = (
        <div className="mx-auto max-w-lg px-4 py-16 text-center">
          <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-rose-50 text-2xl">⚠️</div>
          <p className="text-sm text-slate-600">{msg}</p>
        </div>
      );
    } else if (kind === "sa") {
      body = <SAForm handoffToken={handoffToken} prefill={info.prefill} />;
    } else if (kind === "full") {
      body = <CustomerForm startMode="full" lockMode handoffToken={handoffToken} prefill={info.prefill} />;
    } else if (kind === "corp") {
      body = <CorpForm handoffToken={handoffToken} />;
    } else {
      body = <QuoteForm handoffToken={handoffToken} />;
    }
    return (
      <div className="min-h-screen bg-slate-50 font-sans text-slate-900">
        <header className="border-b border-slate-200 bg-white">
          <div className="mx-auto flex max-w-lg items-center justify-between gap-2.5 px-4 py-3">
            <BrandLockup size="sm" />
            <span className="shrink-0 text-xs font-medium text-slate-400">{titles[kind] || "고객정보 입력"}</span>
          </div>
        </header>
        <main>{body}</main>
      </div>
    );
  }

  // 공통 헤더 (역할 표시 + 역할변경)
  const roleHeader = (sub) => (
    <header className="sticky top-0 z-10 border-b border-slate-200 bg-white/80 backdrop-blur">
      <div className="mx-auto flex max-w-6xl items-center justify-between gap-2 px-4 py-3">
        <div className="flex items-center gap-2.5">
          <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-orange-600 text-xs font-bold tracking-tight text-white">PA</div>
          <div>
            <div className="text-sm font-bold text-slate-800">피에이세무회계</div>
            <div className="text-[11px] text-slate-400">{sub}</div>
          </div>
        </div>
        <button onClick={() => setMenuOpen((o) => !o)} aria-label="메뉴" className="flex h-9 w-9 items-center justify-center rounded-lg text-slate-500 hover:bg-slate-100">
          <svg className="h-5 w-5" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.8" /><circle cx="12" cy="12" r="1.8" /><circle cx="12" cy="19" r="1.8" /></svg>
        </button>
      </div>
      {menuOpen && (
        <div className="border-t border-slate-100 bg-white shadow-sm">
          <div className="mx-auto max-w-6xl space-y-3 px-4 py-3">
            <div className="flex items-center justify-between">
              <span className="rounded-full bg-slate-100 px-2.5 py-1 text-[11px] font-medium text-slate-600">{roleBadge}</span>
              <button onClick={logoutRole} className="rounded-lg border border-slate-300 px-3 py-1 text-[11px] text-slate-500 hover:bg-slate-50">로그아웃</button>
            </div>
            {opViewSwitch && <div>{opViewSwitch}</div>}
          </div>
        </div>
      )}
    </header>
  );

  // 미로그인 진입: 별도 URL(/admin)이면 직원 로그인, 아니면 위장 홈페이지.
  if (!role) {
    if (!loginMode) return <LandingPage />;   // 위장 홈페이지는 엔진 가동과 무관하게 항상 노출(위장 유지).
    // 비밀 주소(/about/philosophy)로 들어온 직원 — 엔진 OFF 면 로그인 무의미 → 영업시간 외.
    if (engineUp === false) return <ClosedNotice />;
    return <AdminGate chooseRole={chooseRole} />;
  }

  // 고객 → 고객 페이지만 (엔진 OFF 면 접수 불가 → 영업시간 외)
  if (role === "customer") {
    if (engineUp === false) return <ClosedNotice />;
    return (
      <div className="min-h-screen bg-slate-50 font-sans text-slate-900">
        {roleHeader("고객 — 견적 문의·정보 입력")}
        <main><QuoteForm /></main>
        <footer className="border-t border-slate-200 py-6 text-center text-xs text-slate-400">피에이세무회계</footer>
      </div>
    );
  }

  // 영업사원 → 영업사원 페이지만
  if (role === "sales") {
    return (
      <div className="min-h-screen bg-slate-50 font-sans text-slate-900">
        {roleHeader("영업사원 · 전산등록")}
        <main><SalesForm /></main>
        <footer className="border-t border-slate-200 py-6 text-center text-xs text-slate-400">피에이세무회계</footer>
      </div>
    );
  }

  // 운영자 → 전체 접근
  return (
    <div className="min-h-screen bg-slate-50 font-sans text-slate-900">
      <header className="sticky top-0 z-10 border-b border-slate-200 bg-white/80 backdrop-blur">
        <div className="mx-auto flex max-w-6xl items-center justify-between gap-2 px-4 py-3">
          <div className="flex items-center gap-2.5">
            <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-orange-600 text-xs font-bold tracking-tight text-white">PA</div>
            <div>
              <div className="text-sm font-bold text-slate-800">피에이세무회계</div>
              <div className="text-[11px] text-slate-400">{authTier === "clerk" ? "직원 — 업무" : "운영자 — 전체 관리"}</div>
            </div>
          </div>
          <div className="flex items-center gap-1.5">
            {tab !== "operator" && (
              <button onClick={() => setTab("operator")} className="rounded-md bg-slate-100 px-2.5 py-1.5 text-xs font-medium text-slate-600 hover:text-slate-800">← 운영자</button>
            )}
            <button onClick={() => setMenuOpen((o) => !o)} aria-label="메뉴" className="flex h-9 w-9 items-center justify-center rounded-lg text-slate-500 hover:bg-slate-100">
              <svg className="h-5 w-5" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.8" /><circle cx="12" cy="12" r="1.8" /><circle cx="12" cy="19" r="1.8" /></svg>
            </button>
          </div>
        </div>
        {menuOpen && (
          <div className="border-t border-slate-100 bg-white shadow-sm">
            <div className="mx-auto max-w-6xl space-y-3 px-4 py-3">
              <div className="flex items-center justify-between">
                <span className="rounded-full bg-slate-100 px-2.5 py-1 text-[11px] font-medium text-slate-600">{roleBadge}</span>
                <button onClick={logoutRole} className="rounded-lg border border-slate-300 px-3 py-1 text-[11px] text-slate-500 hover:bg-slate-50">로그아웃</button>
              </div>
              {opViewSwitch && (
                <div>
                  <div className="mb-1.5 text-[11px] font-semibold text-slate-400">보기 전환</div>
                  {opViewSwitch}
                </div>
              )}
              {/* 데모(고객화면 미리보기)는 운영자 전용 — 직원은 숨김 */}
              {authTier === "operator" && (
              <div>
                <div className="mb-1.5 text-[11px] font-semibold text-slate-400">고객화면 미리보기 (입력/완료)</div>
                <div className="grid grid-cols-2 gap-1.5">
                  {DEMO_TABS.map((t) => (
                    <button key={t.key} onClick={() => { setTab(t.key); setMenuOpen(false); }} className={`rounded-lg px-2.5 py-1.5 text-left text-xs font-medium transition ${tab === t.key ? "bg-orange-500 text-white" : "bg-slate-50 text-slate-600 hover:bg-slate-100"}`}>{t.label}</button>
                  ))}
                </div>
              </div>
              )}
            </div>
          </div>
        )}
      </header>

      <main>{renderTab()}</main>

      <footer className="border-t border-slate-200 py-6 text-center text-xs text-slate-400">
        피에이세무회계
      </footer>
    </div>
  );
}

// ── 마운트 (빌드 없는 CDN 환경) ──
const _root = ReactDOM.createRoot(document.getElementById("root"));
_root.render(<App />);
