// screens.jsx — all screen components for the AnyInsu flow. Shared to window.
const { useState, useEffect, useRef } = React;
const LOGO = "https://www.anyinsu.com/wp-content/uploads/2021/03/anyinsu_logo_2-300x138.png";

/* ---------- shared chrome ---------- */
function Brand() {
  return (
    <span style={{ display: "inline-flex", flexDirection: "column", alignItems: "center", lineHeight: 1 }}>
      <b style={{ color: "var(--accent)", fontWeight: 800, fontSize: 26, letterSpacing: "-0.04em", lineHeight: 1 }}>AI</b>
      <b style={{ color: "var(--text)", fontWeight: 700, fontSize: 11, letterSpacing: "0.38em", paddingLeft: "0.38em", lineHeight: 1.5, opacity: 0.88 }}>ANYINSU</b>
    </span>
  );
}

function TopBar({ onBack, hint }) {
  return (
    <div className="topbar" style={{ position: "relative" }}>
      {onBack
        ? <button className="backbtn" onClick={onBack} aria-label="뒤로"><Icon name="arrowLeft" size={20} /></button>
        : <span style={{ width: 38, flex: "0 0 auto" }} />}
      <span style={{ flex: 1 }} />
      <Brand />
      <span style={{ flex: 1 }} />
      <span style={{ width: 38, flex: "0 0 auto" }} />
      {hint && <span className="top-hint" style={{ position: "absolute", right: 22, top: 24 }}>{hint}</span>}
    </div>
  );
}

const STEP_NAMES = ["본인확인", "보험 선택", "조건 입력", "산출 결과"];
function Stepper({ step }) {
  return (
    <>
      <div className="step-label">STEP {step}/4 · {STEP_NAMES[step - 1]}</div>
      <div className="stepper">
        {STEP_NAMES.map((_, i) => (
          <span key={i} className={"step-seg" + (i + 1 < step ? " done" : i + 1 === step ? " cur" : "")}><i /></span>
        ))}
      </div>
    </>
  );
}

/* ---------- 0. HOME ---------- */
function FlipDigit({ d }) {
  const [cur, setCur] = useState(d);
  const [prev, setPrev] = useState(d);
  const [roll, setRoll] = useState(false);
  const liveRef = useRef(d);
  const timerRef = useRef(null);

  useEffect(() => {
    const target = d;
    if (liveRef.current === target) return;
    const step = () => {
      const from = liveRef.current;
      const next = (from + 1) % 10;
      liveRef.current = next;
      setPrev(from);
      setCur(next);
      setRoll(true);
      timerRef.current = setTimeout(() => {
        setRoll(false);
        if (next !== target) {
          timerRef.current = setTimeout(step, 12);
        }
      }, 165);
    };
    clearTimeout(timerRef.current);
    step();
    return () => clearTimeout(timerRef.current);
  }, [d]);

  return (
    <div className={`flip${roll ? " roll" : ""}`}>
      <div className="flip-half top"><span className="n">{cur}</span></div>
      <div className="flip-half bottom"><span className="n">{roll ? prev : cur}</span></div>
      {roll && <div className="leaf top"><span className="n">{prev}</span></div>}
      {roll && <div className="leaf bottom"><span className="n">{cur}</span></div>}
    </div>
  );
}
function FlipTile({ ch }) {
  return (
    <div className="flip">
      <div className="flip-half top"><span className="n">{ch}</span></div>
      <div className="flip-half bottom"><span className="n">{ch}</span></div>
    </div>
  );
}
function FlipAmount() {
  const rand = () => (Math.floor(Math.random() * 5001) + 1000) * 100; // 100,000 ~ 600,000 (100원 단위)
  const [amt, setAmt] = useState(rand);
  useEffect(() => {
    const id = setInterval(() => {
      setAmt((p) => { let n; do { n = rand(); } while (n === p); return n; });
    }, 3000);
    return () => clearInterval(id);
  }, []);
  const digits = String(amt).padStart(6, "0").split("");
  return (
    <div className="flipclock">
      <div className="fc-cap"><Icon name="spark" size={14} fill="currentColor" stroke={0} /> 연 보험료</div>
      <div className="fc-row">
        <FlipTile ch="₩" />
        {digits.map((ch, i) => (
          <React.Fragment key={i}>
            {i === 3 && <span className="fc-comma">,</span>}
            <FlipDigit d={Number(ch)} />
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

function Home({ onStart }) {
  return (
    <div className="screen enter">
      <TopBar hint="무료 산출" />
      <div className="screen-body" style={{ display: "flex", flexDirection: "column" }}>
        <div style={{ paddingTop: 26 }}>
          <span className="eyebrow">AI 실시간 보험료 산출</span>
          <h1 className="h1">AI 분석으로<br />나에게 맞는 최적 보험,<br /><span className="mint">지금 바로 확인하세요.</span></h1>
        </div>

        <FlipAmount />

        <div className="trustrow" style={{ marginTop: 26 }}>
          <span className="tchip"><Icon name="check" size={14} /> 회원가입 없음</span>
          <span className="tchip"><Icon name="clock" size={14} /> 1분 소요</span>
          <span className="tchip"><Icon name="shield" size={14} /> 본인확인 안심</span>
        </div>

        <div style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", paddingBottom: 24 }}>
          <button className="btn btn-primary" style={{ width: "78%", padding: "34px 18px", fontSize: 20, borderRadius: 18 }} onClick={onStart}>보험료 산출</button>
        </div>
      </div>
      <div className="screen-foot" style={{ background: "transparent" }}>
        <p className="social" style={{ color: "var(--accent)", fontWeight: 700, fontSize: 13.5 }}>에니인슈는 믿고 맡기는 안전의 문화를 만들어 갑니다.</p>
      </div>
    </div>
  );
}

/* ---------- 1. 본인확인 ---------- */
function formatPhone(v) {
  const d = v.replace(/\D/g, "").slice(0, 11);
  if (d.length < 4) return d;
  if (d.length < 8) return d.slice(0, 3) + "-" + d.slice(3);
  return d.slice(0, 3) + "-" + d.slice(3, 7) + "-" + d.slice(7);
}
function Verify({ name, setName, phone, setPhone, onVerify, onBack }) {
  const digits = phone.replace(/\D/g, "");
  const valid = name.trim().length >= 2 && digits.length >= 10;
  return (
    <div className="screen enter">
      <TopBar onBack={onBack} />
      <Stepper step={1} />
      <div className="screen-body">
        <h2 className="h2">본인확인이 필요해요</h2>
        <p className="sub">이름, 전화번호, 구글 계정으로<br />쉽고 빠르게 인증을 완료합니다.</p>

        <div className="field">
          <label>이름</label>
          <input className="input" value={name} placeholder="이름을 입력하세요"
            onChange={(e) => setName(e.target.value)} />
        </div>
        <div className="field">
          <label>휴대폰 번호</label>
          <input className="input" value={phone} placeholder="010-0000-0000" inputMode="numeric"
            onChange={(e) => setPhone(formatPhone(e.target.value))} />
        </div>

        <div style={{ display: "flex", alignItems: "flex-start", gap: 13, background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 14, padding: "14px 15px", marginTop: 20 }}>
          <svg width="28" height="28" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" style={{ flex: "0 0 auto", marginTop: 2 }}>
            <path fill="#EA4335" d="M24 9.5c3.1 0 5.8 1.1 8 2.9l6-6C34.4 3.1 29.5 1 24 1 14.7 1 6.8 6.7 3.4 14.9l7 5.4C12.2 13.8 17.6 9.5 24 9.5z"/>
            <path fill="#4285F4" d="M46.5 24.5c0-1.6-.1-3.1-.4-4.5H24v8.5h12.7c-.6 3-2.3 5.5-4.9 7.2l7.6 5.9c4.4-4.1 7.1-10.2 7.1-17.1z"/>
            <path fill="#FBBC05" d="M10.4 28.5A14.5 14.5 0 0 1 9.5 24c0-1.6.3-3.1.7-4.6l-7-5.4A23 23 0 0 0 1 24c0 3.8.9 7.4 2.4 10.7l7-5.2z"/>
            <path fill="#34A853" d="M24 47c5.5 0 10.1-1.8 13.5-4.9l-7.6-5.9c-1.9 1.3-4.2 2-5.9 2-6.4 0-11.8-4.3-13.6-10.1l-7 5.2C6.8 41.3 14.7 47 24 47z"/>
          </svg>
          <div>
            <div style={{ fontSize: 13.5, fontWeight: 700, color: "var(--text)" }}>Google 계정으로 안전하게 인증</div>
            <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 4, lineHeight: 1.55, fontWeight: 500 }}>
              입력하신 정보와 Google 계정을 통해 본인확인이 진행됩니다. 수집된 정보는 보험료 산출 및 상담 연결 목적으로만 사용됩니다.
            </div>
          </div>
        </div>
      </div>
      <div className="screen-foot">
        <button className="btn btn-primary" disabled={!valid} onClick={onVerify}
          style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 10 }}>
          <svg width="20" height="20" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
            <path fill="#fff" d="M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4z"/>
          </svg>
          Google 계정으로 계속하기
        </button>
      </div>
    </div>
  );
}

/* ---------- 2. 보험 종류 선택 ---------- */
function SelectType({ name, onSelect, onConsult, onBack }) {
  return (
    <div className="screen enter">
      <TopBar onBack={onBack} />
      <Stepper step={2} />
      <div className="screen-body">
        <span className="vchip"><span className="dot" />{name}님 본인확인 완료</span>
        <h2 className="h2" style={{ marginTop: 14 }}>어떤 보험이 필요하세요?</h2>
        <p className="sub">산출을 원하는 보험을 선택해 주세요.</p>

        <div style={{ display: "grid", gridAutoRows: "1fr", gap: 12, marginTop: 22 }}>
          <button className="pick" onClick={() => onSelect("fire")}>
            <span className="pick-ico"><Icon name="flame" size={24} /></span>
            <span style={{ flex: 1 }}>
              <span className="pick-t" style={{ display: "block" }}>화재보험</span>
              <span className="pick-d" style={{ display: "block" }}>사업장의 화재·폭발·재산 피해를 보장합니다. 업종·면적만 입력하면 예상 보험료를 바로 확인할 수 있어요.</span>
            </span>
            <span className="chev"><Icon name="chevronRight" size={20} /></span>
          </button>
          <button className="pick" onClick={() => onSelect("liability")}>
            <span className="pick-ico"><Icon name="handshake" size={24} /></span>
            <span style={{ flex: 1 }}>
              <span className="pick-t" style={{ display: "block" }}>영업배상책임보험</span>
              <span className="pick-d" style={{ display: "block" }}>영업 중 타인의 신체·재산에 입힌 손해를 배상합니다. 담당자가 맞춤 상담을 도와드려요.</span>
            </span>
            <span className="chev"><Icon name="chevronRight" size={20} /></span>
          </button>
        </div>

        <div className="or-div"><span>또는</span></div>

        <button className="pick consult-pick" onClick={onConsult}>
          <span className="pick-ico"><Icon name="phone" size={22} /></span>
          <span style={{ flex: 1 }}>
            <span className="pick-t" style={{ display: "block" }}>상담 요청</span>
            <span className="pick-d" style={{ display: "block" }}>어떤 보험이 필요한지 모르겠다면, 전문 상담사가 맞춤으로 안내해 드려요.</span>
          </span>
          <span className="chev"><Icon name="chevronRight" size={20} /></span>
        </button>
      </div>
    </div>
  );
}

/* ---------- 3a. 화재보험 조건 ---------- */
const QUICK_PYEONG = [10, 20, 30, 50];
function FireForm({ industry, setIndustry, pyeong, setPyeong, onSubmit, onBack }) {
  const [customIndustry, setCustomIndustry] = useState("");
  const valid = industry && Number(pyeong) > 0 && (industry !== "etc" || customIndustry.trim().length > 0);
  const m2 = Number(pyeong) > 0 ? (Number(pyeong) * PYEONG_TO_M2).toFixed(1) : null;
  return (
    <div className="screen enter">
      <TopBar onBack={onBack} />
      <Stepper step={3} />
      <div className="screen-body">
        <h2 className="h2">화재보험 산출 조건</h2>
        <p className="sub">업종과 사업장 면적을 입력하면<br />AI가 유사 계약을 분석해 예상 보험료를 산출해요.</p>

        <div className="field">
          <label>업종 선택</label>
          <div className="chipgrid">
            {INDUSTRIES.map((ind) => (
              <button key={ind.id} className={"chip" + (industry === ind.id ? " sel" : "")} onClick={() => setIndustry(ind.id)}>
                <span className="chip-ico"><Icon name={ind.icon} size={18} /></span>
                <span className="chip-t">{ind.name}</span>
              </button>
            ))}
          </div>
        </div>

          {industry === "etc" && (
            <div className="field" style={{ animation: "scn .3s both", marginTop: 12 }}>
              <label>상세 업종 입력</label>
              <input className="input" value={customIndustry} placeholder="예: 노래방, 볼링장, 세탁소 등"
                autoFocus onChange={(e) => setCustomIndustry(e.target.value)} />
            </div>
          )}

        <div className="field">
          <label>사업장 면적</label>
          <div className="field-wrap">
            <input className="input has-suffix" value={pyeong} placeholder="0" inputMode="numeric"
              onChange={(e) => setPyeong(e.target.value.replace(/[^0-9]/g, "").slice(0, 4))} />
            <span className="suffix">평</span>
          </div>
          <div className="quick">
            {QUICK_PYEONG.map((p) => (
              <button key={p} className={"qbtn" + (String(p) === String(pyeong) ? " sel" : "")} onClick={() => setPyeong(String(p))}>{p}평</button>
            ))}
          </div>
          {m2 && <div className="helper"><Icon name="check" size={13} /> 약 {m2}㎡</div>}
        </div>
      </div>
      <div className="screen-foot">
        <button className="btn btn-primary" disabled={!valid} onClick={() => onSubmit(customIndustry)}>예상 보험료 확인</button>
      </div>
    </div>
  );
}

/* ---------- 3b. 영업배상 ---------- */
const LIAB_OPTIONS = [
  { id: "docs", icon: "doc", title: "구비서류 안내 요청", desc: "가입에 필요한 서류를 안내해 드려요. 담당자가 확인 후 연락드립니다." },
  { id: "call", icon: "phone", title: "연락 바랍니다", desc: "담당자가 직접 연락드려 사업장에 맞는 보장을 상담해 드립니다." },
];
function LiabilityForm({ choice, setChoice, onSubmit, onBack }) {
  return (
    <div className="screen enter">
      <TopBar onBack={onBack} />
      <Stepper step={3} />
      <div className="screen-body">
        <span className="vchip" style={{ background: "var(--surface)", border: "1px solid var(--border)", color: "var(--text-muted)" }}>
          <span style={{ color: "var(--accent)", display: "flex" }}><Icon name="handshake" size={15} /></span> 영업배상책임보험
        </span>
        <h2 className="h2" style={{ marginTop: 14 }}>어떻게 도와드릴까요?</h2>
        <p className="sub">영업배상은 조건이 다양해 담당자 상담으로 진행돼요.<br />원하시는 방식을 선택해 주세요.</p>

        <div style={{ display: "flex", flexDirection: "column", gap: 12, marginTop: 22 }}>
          {LIAB_OPTIONS.map((o) => (
            <button key={o.id} className={"pick" + (choice === o.id ? " sel" : "")} onClick={() => setChoice(o.id)}>
              <span className="pick-ico"><Icon name={o.icon} size={22} /></span>
              <span style={{ flex: 1 }}>
                <span className="pick-t" style={{ display: "block" }}>{o.title}</span>
                <span className="pick-d" style={{ display: "block" }}>{o.desc}</span>
              </span>
              <span className="opt-radio" />
            </button>
          ))}
        </div>
      </div>
      <div className="screen-foot">
        <button className="btn btn-primary" disabled={!choice} onClick={onSubmit}>산출 의뢰 접수하기</button>
      </div>
    </div>
  );
}

/* ---------- 4a. 화재보험 결과 ---------- */
function FireResult({ result, name, onConsult, onRedo, onBack }) {
  const r = result;
  const [memo, setMemo] = useState("");
  return (
    <div className="screen enter">
      <TopBar onBack={onBack} hint={`${r.industryLabel || r.industry.name} · ${r.pyeong}평`} />
      <Stepper step={4} />
      <div className="screen-body">
        <h2 className="h2" style={{ marginBottom: 16 }}>{name}님의 예상 보험료</h2>
        <div className="amount-card">
          <div className="amt-cap">연 예상 보험료{r.premiumBasis === "similar" ? " · 실제 청약 기준" : ""}</div>
          <div className="amt-big"><span className="won">₩</span>{r.yearly.toLocaleString("ko-KR")}<span className="per">/년</span></div>
          <div className="amt-year">월 환산 <b>{wonPlain(r.monthly)}</b>{r.premiumBasis === "similar" ? "" : " · 연 1.2% 할인 적용"}</div>
          <span className="ai-tag"><Icon name="spark" size={13} fill="currentColor" stroke={0} /> {r.premiumBasis === "similar" ? "유사 청약 1건 기준 · 실제 보험료" : "AI 분석 · 유사 계약 기준 예상치"}</span>
        </div>

        {r.similar ? (
        <>
        <div className="sec-title"><span className="badge1">유사 계약 1건</span> 비슷한 사업장은 이렇게 가입했어요</div>
        <div className="similar">
          <div className="srow">
            <span className="sk">업종</span>
            <span className="sv">{r.similar.industryName}</span>
          </div>
          {r.similar.pyeong ? (
          <div className="srow">
            <span className="sk">면적</span>
            <span className="sv">{r.similar.pyeong}평<span className="small">약 {Math.round(r.similar.m2)}㎡</span></span>
          </div>) : null}
          {r.similar.coverage ? (
          <div className="srow">
            <span className="sk">가입금액</span>
            <span className="sv">{wonPlain(r.similar.coverage)}</span>
          </div>) : null}
          {r.similar.monthly ? (
          <div className="srow">
            <span className="sk">실제 가입 보험료</span>
            <span className="sv" style={{ color: "var(--accent)" }}>{wonPlain(r.similar.yearly)} /년<span className="small">월 {wonPlain(r.similar.monthly)}</span></span>
          </div>) : null}
          {r.similar.applyDate ? (
          <div className="srow">
            <span className="sk">청약일</span>
            <span className="sv">{r.similar.applyDate}</span>
          </div>) : null}
        </div>
        </>
        ) : (
        <div className="sec-title" style={{ color: "var(--text-muted)" }}>비슷한 청약 사례를 찾지 못했어요. 담당자가 직접 정확히 산출해 드립니다.</div>
        )}
        <div className="note">
          <span style={{ color: "var(--accent)", flex: "0 0 auto", marginTop: 1 }}><Icon name="shield" size={16} /></span>
          <p>개인정보가 제거된 익명 유사 청약 사례이며, 실제 보험료는 가입 조건·보장 설계에 따라 달라질 수 있어요.</p>
        </div>

        <div className="field" style={{ marginTop: 22 }}>
          <label>메모 (선택)</label>
          <textarea className="input" rows={3}
            style={{ resize: "none", lineHeight: 1.6, fontSize: 15 }}
            placeholder="담당자에게 전달할 내용을 입력해 주세요. (보장 조건, 문의사항 등)"
            value={memo} onChange={(e) => setMemo(e.target.value)} />
        </div>
      </div>
      <div className="screen-foot">
        <button className="btn btn-primary" onClick={() => onConsult(memo)}>이 조건으로 전문 설계사 상담받기</button>
        <button className="btn btn-ghost" style={{ marginTop: 10 }} onClick={onRedo}>조건 다시 입력</button>
      </div>
    </div>
  );
}

/* ---------- 사업자등록증 자동 스캔(크롭) : OpenCV.js + jscanify 지연 로드 ---------- */
let _scannerPromise = null;
function loadScanner() {
  if (window.jscanify && window.cv && window.cv.Mat) return Promise.resolve();
  if (_scannerPromise) return _scannerPromise;
  const loadScript = (src) => new Promise((res, rej) => {
    const s = document.createElement("script");
    s.src = src; s.async = true; s.onload = res; s.onerror = () => rej(new Error("load:" + src));
    document.head.appendChild(s);
  });
  // emscripten MODULARIZE 빌드: window.cv 가 Promise 이거나 init 후 Mat 보유 → 모두 대응 + 타임아웃
  const cvReady = (timeoutMs) => new Promise((resolve, reject) => {
    const t0 = Date.now(); let thened = false, fin = false;
    const ok = () => { if (!fin) { fin = true; resolve(); } };
    const tick = () => {
      if (fin) return;
      const c = window.cv;
      if (c && c.Mat && typeof c.Mat === "function") return ok();
      if (c && !thened && typeof c.then === "function") {
        thened = true;
        c.then((m) => { if (m && m.Mat) window.cv = m; ok(); }, () => {});
      }
      if (Date.now() - t0 > timeoutMs) { fin = true; return reject(new Error("cv-timeout")); }
      setTimeout(tick, 80);
    };
    tick();
  });
  _scannerPromise = loadScript("/assets/vendor/opencv.js")
    .then(() => cvReady(25000))
    .then(() => (window.jscanify ? null : loadScript("/assets/vendor/jscanify.min.js")))
    .catch((e) => { _scannerPromise = null; throw e; });
  return _scannerPromise;
}
function _dist(a, b) { return Math.hypot(a.x - b.x, a.y - b.y); }
function _clamp(v, lo, hi) { return v < lo ? lo : (v > hi ? hi : v); }
// 자연좌표 4꼭지 → 원근보정 크롭 캔버스
function warpCorners(imgEl, c) {
  const cv = window.cv;
  const src = cv.imread(imgEl);
  let outW = Math.round(Math.max(_dist(c.tl, c.tr), _dist(c.bl, c.br)));
  let outH = Math.round(Math.max(_dist(c.tl, c.bl), _dist(c.tr, c.br)));
  const cap = 1600;
  if (outW > cap) { outH = Math.round(outH * cap / outW); outW = cap; }
  outW = Math.max(80, outW); outH = Math.max(80, outH);
  const srcTri = cv.matFromArray(4, 1, cv.CV_32FC2, [c.tl.x, c.tl.y, c.tr.x, c.tr.y, c.br.x, c.br.y, c.bl.x, c.bl.y]);
  const dstTri = cv.matFromArray(4, 1, cv.CV_32FC2, [0, 0, outW, 0, outW, outH, 0, outH]);
  const M = cv.getPerspectiveTransform(srcTri, dstTri);
  const dst = new cv.Mat();
  cv.warpPerspective(src, dst, M, new cv.Size(outW, outH), cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar());
  const out = document.createElement("canvas"); out.width = outW; out.height = outH;
  cv.imshow(out, dst);
  src.delete(); dst.delete(); srcTri.delete(); dstTri.delete(); M.delete();
  return out;
}
// 자동 문서 외곽 검출 → 자연좌표 4꼭지 (실패 시 null)
function detectCorners(imgEl) {
  let mat = null, contour = null;
  try {
    const scanner = new window.jscanify();
    mat = window.cv.imread(imgEl);
    contour = scanner.findPaperContour(mat);
    if (!contour) return null;
    const p = scanner.getCornerPoints(contour);
    if (!p || !p.topLeftCorner || !p.bottomRightCorner) return null;
    return {
      tl: { x: p.topLeftCorner.x, y: p.topLeftCorner.y },
      tr: { x: p.topRightCorner.x, y: p.topRightCorner.y },
      br: { x: p.bottomRightCorner.x, y: p.bottomRightCorner.y },
      bl: { x: p.bottomLeftCorner.x, y: p.bottomLeftCorner.y },
    };
  } catch (e) { return null; }
  finally { if (mat && mat.delete) mat.delete(); if (contour && contour.delete) contour.delete(); }
}

/* ---------- 사업자등록증 크롭 편집기 (촬영 직후) ---------- */
function BizCertCropper({ file, onCropped, onCancel }) {
  const [phase, setPhase] = useState("load");   // load | edit | error
  const [note, setNote] = useState("스캐너 준비 중… (최초 1회 다운로드)");
  const [disp, setDisp] = useState({ w: 0, h: 0 });
  const [corners, setCorners] = useState(null);
  const [busy, setBusy] = useState(false);
  const [scan, setScan] = useState("");   // "" | ok | nodetect | noscanner
  const boxRef = useRef(null);
  const imgRef = useRef(null);
  const urlRef = useRef("");
  const scaleRef = useRef(1);

  useEffect(() => {
    const url = URL.createObjectURL(file); urlRef.current = url;
    const img = new Image();
    img.onload = async () => {
      imgRef.current = img;
      const maxW = (boxRef.current && boxRef.current.clientWidth) || 300;
      const w = Math.min(maxW, img.naturalWidth);
      const sc = w / img.naturalWidth; scaleRef.current = sc;
      setDisp({ w: Math.round(w), h: Math.round(img.naturalHeight * sc) });
      let auto = false;
      try { await loadScanner(); auto = true; }
      catch (e) { setScan("noscanner"); }
      let nat = auto ? detectCorners(img) : null;
      if (auto) setScan(nat ? "ok" : "nodetect");
      if (!nat) {
        const ix = img.naturalWidth, iy = img.naturalHeight, mx = ix * 0.08, my = iy * 0.08;
        nat = { tl: { x: mx, y: my }, tr: { x: ix - mx, y: my }, br: { x: ix - mx, y: iy - my }, bl: { x: mx, y: iy - my } };
      }
      const m = (p) => ({ x: p.x * sc, y: p.y * sc });
      setCorners({ tl: m(nat.tl), tr: m(nat.tr), br: m(nat.br), bl: m(nat.bl) });
      setPhase("edit");
    };
    img.onerror = () => { setNote("이미지를 읽지 못했어요."); setPhase("error"); };
    img.src = url;
    return () => { if (urlRef.current) URL.revokeObjectURL(urlRef.current); };
  }, []);

  const startDrag = (key) => (e) => {
    e.preventDefault();
    const move = (ev) => {
      const rect = boxRef.current.getBoundingClientRect();
      const px = (ev.touches ? ev.touches[0].clientX : ev.clientX) - rect.left;
      const py = (ev.touches ? ev.touches[0].clientY : ev.clientY) - rect.top;
      setCorners((c) => ({ ...c, [key]: { x: _clamp(px, 0, disp.w), y: _clamp(py, 0, disp.h) } }));
    };
    const up = () => { window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); };
    window.addEventListener("pointermove", move); window.addEventListener("pointerup", up);
  };

  const useOriginal = () => { onCropped(file, URL.createObjectURL(file)); };

  const apply = () => {
    if (busy || !corners || !imgRef.current) return;
    setBusy(true);
    try {
      const sc = scaleRef.current;
      const nat = {
        tl: { x: corners.tl.x / sc, y: corners.tl.y / sc }, tr: { x: corners.tr.x / sc, y: corners.tr.y / sc },
        br: { x: corners.br.x / sc, y: corners.br.y / sc }, bl: { x: corners.bl.x / sc, y: corners.bl.y / sc },
      };
      const canvas = warpCorners(imgRef.current, nat);
      canvas.toBlob((blob) => {
        if (!blob) { setBusy(false); return useOriginal(); }
        const base = (file.name || "scan").replace(/\.[^.]+$/, "");
        const cf = new File([blob], base + "_scan.jpg", { type: "image/jpeg" });
        onCropped(cf, URL.createObjectURL(blob));
      }, "image/jpeg", 0.92);
    } catch (e) { setBusy(false); useOriginal(); }
  };

  const HANDLE = { position: "absolute", width: 26, height: 26, marginLeft: -13, marginTop: -13, borderRadius: "50%", border: "2.5px solid var(--accent)", background: "rgba(47,224,192,.25)", touchAction: "none", cursor: "grab", boxShadow: "0 1px 4px rgba(0,0,0,.4)" };
  const poly = corners ? `${corners.tl.x},${corners.tl.y} ${corners.tr.x},${corners.tr.y} ${corners.br.x},${corners.br.y} ${corners.bl.x},${corners.bl.y}` : "";

  return (
    <div className="card" style={{ width: "100%", marginTop: 18, textAlign: "left" }}>
      <div style={{ fontWeight: 700, fontSize: 14.5, color: "var(--text)", marginBottom: 4 }}>사업자등록증 영역 확인</div>
      <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginBottom: 12, lineHeight: 1.5 }}>
        파란 모서리를 드래그해 등록증 네 귀퉁이에 맞춘 뒤 [반듯하게 크롭]을 눌러 주세요.
      </div>
      <div ref={boxRef} style={{ position: "relative", width: "100%", borderRadius: 12, overflow: "hidden", background: "#000", touchAction: "none" }}>
        {phase !== "load" && (
          <img src={urlRef.current} alt="원본" draggable={false}
            style={{ display: "block", width: disp.w, height: disp.h, userSelect: "none", pointerEvents: "none" }} />
        )}
        {phase === "edit" && corners && (
          <svg width={disp.w} height={disp.h} style={{ position: "absolute", left: 0, top: 0, pointerEvents: "none" }}>
            <polygon points={poly} fill="rgba(47,224,192,.14)" stroke="var(--accent)" strokeWidth="2" />
          </svg>
        )}
        {phase === "edit" && corners && ["tl", "tr", "br", "bl"].map((k) => (
          <div key={k} onPointerDown={startDrag(k)} onTouchStart={startDrag(k)}
            style={{ ...HANDLE, left: corners[k].x, top: corners[k].y }} />
        ))}
        {phase === "load" && (
          <div style={{ padding: "44px 16px", textAlign: "center", color: "var(--text-muted)", fontSize: 13 }}>{note}</div>
        )}
      </div>
      {phase === "edit" && scan && (
        <div style={{ fontSize: 12.5, marginTop: 9, fontWeight: 600,
          color: scan === "ok" ? "var(--accent)" : "#ffcf8a" }}>
          {scan === "ok" ? "✓ 문서를 자동으로 찾았어요. 필요하면 모서리를 조정하세요."
            : scan === "nodetect" ? "⚠ 자동 검출 실패 — 네 모서리를 직접 맞춰 주세요."
            : "⚠ 자동 검출 모듈 로드 실패 — 모서리를 직접 맞추거나 [원본 사용]."}
        </div>
      )}
      <div style={{ display: "flex", gap: 8, marginTop: 13 }}>
        <button className="btn btn-primary" style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 7 }} disabled={phase !== "edit" || busy} onClick={apply}>
          <Icon name="crop" size={17} /> {busy ? "처리 중…" : "반듯하게 크롭"}
        </button>
        <button className="btn btn-ghost" style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 7 }} disabled={busy} onClick={useOriginal}>
          <Icon name="image" size={17} /> 원본 사용
        </button>
      </div>
      <button className="btn btn-ghost" style={{ marginTop: 8, width: "100%" }} disabled={busy} onClick={onCancel}>다시 촬영 / 취소</button>
    </div>
  );
}

/* ---------- 5a. 사업자등록증 업로드 (완료 화면 내) ---------- */
function BizCertUpload({ onUpload }) {
  const [file, setFile] = useState(null);
  const [preview, setPreview] = useState("");
  const [status, setStatus] = useState("idle"); // idle | uploading | done | error
  const [err, setErr] = useState("");
  const [cropFile, setCropFile] = useState(null); // 크롭 대상 원본 이미지 (있으면 크롭 화면)
  const inputRef = useRef(null);

  const pick = (e) => {
    const f = e.target.files && e.target.files[0];
    if (e.target) e.target.value = "";   // 동일 파일 재선택 허용
    if (!f) return;
    setErr("");
    if (f.size > 12 * 1024 * 1024) { setErr("파일 용량은 12MB 이하만 가능해요."); return; }
    if (f.type && f.type.indexOf("image/") === 0) {
      setCropFile(f);                    // 이미지 → 자동 크롭 편집기로
    } else {
      setFile(f); setStatus("idle"); setPreview("");   // PDF 등 → 바로 업로드 단계
    }
  };

  if (cropFile) {
    return (
      <BizCertCropper file={cropFile}
        onCropped={(cf, url) => { setFile(cf); setPreview(url); setStatus("idle"); setCropFile(null); }}
        onCancel={() => setCropFile(null)} />
    );
  }

  const submit = async () => {
    if (!file || status === "uploading") return;
    setStatus("uploading"); setErr("");
    const r = await onUpload(file);
    if (r && r.ok) { setStatus("done"); }
    else { setStatus("error"); setErr("업로드에 실패했어요. 잠시 후 다시 시도해 주세요."); }
  };

  if (status === "done") {
    return (
      <div className="card" style={{ width: "100%", marginTop: 18, textAlign: "left", display: "flex", alignItems: "center", gap: 11 }}>
        <span style={{ color: "var(--accent)", display: "flex" }}><Icon name="check" size={20} /></span>
        <div>
          <div style={{ fontWeight: 700, fontSize: 14.5, color: "var(--text)" }}>사업자등록증이 접수되었어요</div>
          <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 2 }}>담당자가 함께 확인 후 상담을 도와드릴게요.</div>
        </div>
      </div>
    );
  }

  return (
    <div className="card" style={{ width: "100%", marginTop: 18, textAlign: "left" }}>
      <div style={{ display: "flex", alignItems: "flex-start", gap: 10 }}>
        <span style={{ color: "var(--accent)", flex: "0 0 auto", marginTop: 1, display: "flex" }}><Icon name="doc" size={18} /></span>
        <p style={{ fontSize: 13, color: "var(--text-muted)", lineHeight: 1.55, fontWeight: 500, margin: 0 }}>
          사업자등록증을 첨부해 주시면 보다 원활한 상담이 가능합니다.
        </p>
      </div>

      <input ref={inputRef} type="file" accept="image/*,application/pdf" capture="environment"
        style={{ display: "none" }} onChange={pick} />

      <button className="btn btn-ghost" style={{ marginTop: 13, display: "flex", alignItems: "center", justifyContent: "center", gap: 8 }}
        onClick={() => inputRef.current && inputRef.current.click()}>
        <Icon name="camera" size={18} /> {file ? "다시 선택 / 촬영" : "촬영 또는 파일 선택"}
      </button>

      {file && (
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 11, background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 12, padding: "9px 11px" }}>
          {preview
            ? <img src={preview} alt="미리보기" style={{ width: 42, height: 42, objectFit: "cover", borderRadius: 8, flex: "0 0 auto" }} />
            : <span style={{ color: "var(--accent)", display: "flex" }}><Icon name="doc" size={22} /></span>}
          <span style={{ flex: 1, fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{file.name}</span>
        </div>
      )}

      {err && <div style={{ color: "#ff8a80", fontSize: 12.5, marginTop: 9 }}>{err}</div>}

      <button className="btn btn-primary" style={{ marginTop: 12 }} disabled={!file || status === "uploading"} onClick={submit}>
        {status === "uploading" ? "접수 중…" : "사업자 등록증 접수"}
      </button>
    </div>
  );
}

/* ---------- 5. 접수 완료 ---------- */
function Done({ name, phone, summary, onHome, canUpload, onUpload }) {
  return (
    <div className="screen enter">
      <TopBar />
      <div className="screen-body" style={{ display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", justifyContent: "center" }}>
        <div className="check">
          <svg viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7" /></svg>
        </div>
        <h2 className="h2" style={{ marginTop: 24 }}>상담요청이 접수됐어요</h2>
        <p className="sub" style={{ textAlign: "center" }}>영업일 기준 <b style={{ color: "var(--text)" }}>1일 이내</b>에<br />담당 설계사가 연락드릴게요.</p>

        <div className="card" style={{ width: "100%", marginTop: 26, textAlign: "left" }}>
          <div className="srow" style={{ padding: "10px 0", borderColor: "var(--border)" }}><span className="sk">신청자</span><span className="sv">{name}</span></div>
          <div className="srow" style={{ padding: "10px 0", borderColor: "var(--border)" }}><span className="sk">연락처</span><span className="sv">{phone}</span></div>
          <div className="srow" style={{ padding: "10px 0", borderBottom: "none" }}><span className="sk">요청 내용</span><span className="sv" style={{ color: "var(--accent)" }}>{summary}</span></div>
        </div>

        {canUpload && <BizCertUpload onUpload={onUpload} />}
      </div>
      <div className="screen-foot">
        <button className="btn btn-soft" onClick={onHome}>처음으로</button>
      </div>
    </div>
  );
}

/* ---------- overlay (verifying / analyzing) ---------- */
function Overlay({ title, sub }) {
  return (
    <div className="overlay">
      <div className="ring" />
      <div className="ov-t">{title}</div>
      <div className="ov-s">{sub}</div>
      <div className="scan"><i /><i /><i /></div>
    </div>
  );
}

Object.assign(window, { Home, Verify, SelectType, FireForm, LiabilityForm, FireResult, Done, Overlay, LIAB_OPTIONS });
