// ===== Multi-step lead form =====
// Step 1: Lead info  → Step 2: Photo upload (Claude auto-identifies)

const US_STATES = [
  ["AL","Alabama"],["AK","Alaska"],["AZ","Arizona"],["AR","Arkansas"],["CA","California"],
  ["CO","Colorado"],["CT","Connecticut"],["DE","Delaware"],["DC","District of Columbia"],
  ["FL","Florida"],["GA","Georgia"],["HI","Hawaii"],["ID","Idaho"],["IL","Illinois"],
  ["IN","Indiana"],["IA","Iowa"],["KS","Kansas"],["KY","Kentucky"],["LA","Louisiana"],
  ["ME","Maine"],["MD","Maryland"],["MA","Massachusetts"],["MI","Michigan"],["MN","Minnesota"],
  ["MS","Mississippi"],["MO","Missouri"],["MT","Montana"],["NE","Nebraska"],["NV","Nevada"],
  ["NH","New Hampshire"],["NJ","New Jersey"],["NM","New Mexico"],["NY","New York"],
  ["NC","North Carolina"],["ND","North Dakota"],["OH","Ohio"],["OK","Oklahoma"],["OR","Oregon"],
  ["PA","Pennsylvania"],["RI","Rhode Island"],["SC","South Carolina"],["SD","South Dakota"],
  ["TN","Tennessee"],["TX","Texas"],["UT","Utah"],["VT","Vermont"],["VA","Virginia"],
  ["WA","Washington"],["WV","West Virginia"],["WI","Wisconsin"],["WY","Wyoming"],
];

function LeadForm({ slotsLeft, onSlotClaim }) {
  const [step, setStep] = React.useState(1);
  const [lead, setLead] = React.useState({
    first: "", last: "", email: "", phone: "",
    street: "", city: "", state: "", zip: ""
  });
  const [errors, setErrors] = React.useState({});
  const [photos, setPhotos] = React.useState([]); // {id, url, name, status, items, total}
  const [dragOver, setDragOver] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [submitError, setSubmitError] = React.useState(null);
  const fileInput = React.useRef(null);

  // Send the lead to the backend. Called once on step 1 submit (so we capture
  // the lead even if the user bails on photos) and again on step 3 with the
  // AI-identified items + estimate range.
  const sendLead = React.useCallback(async (payload) => {
    try {
      const r = await fetch("/api/lead", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });
      if (!r.ok) {
        const t = await r.text().catch(() => "");
        throw new Error(t || `HTTP ${r.status}`);
      }
      return await r.json();
    } catch (err) {
      console.error("[lead] submit failed:", err);
      return { ok: false, error: String(err && err.message || err) };
    }
  }, []);

  // ----- Partial-lead capture: fires while user is still filling step 1 -----
  // We only post a partial once we have a valid email + at least one other
  // field, and we dedupe so we don't re-send identical state. A 3-second
  // debounce prevents hammering the API while typing.
  const partialFlightRef = React.useRef({
    lastSignature: null,
    completed: false,    // once they submit, stop sending partials
    sending: false,
  });
  const sendPartialLead = React.useCallback(async (snapshot) => {
    if (partialFlightRef.current.completed) return;
    if (partialFlightRef.current.sending) return;
    if (!/^\S+@\S+\.\S+$/.test(snapshot.email || "")) return;

    // De-dupe, only post if the content actually changed
    const sig = JSON.stringify(snapshot);
    if (sig === partialFlightRef.current.lastSignature) return;
    partialFlightRef.current.lastSignature = sig;
    partialFlightRef.current.sending = true;

    try {
      await fetch("/api/partial-lead", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...snapshot, source: "partial_step1" }),
        keepalive: true,
      });
      // Fire a Pixel custom event so you can build a 'PartialLead' audience
      // for retargeting later. Distinct from 'Lead' so your conversion
      // optimization isn't polluted.
      if (typeof window !== "undefined" && typeof window.fbq === "function") {
        try {
          window.fbq("trackCustom", "PartialLead", {
            content_name: "Free Appraisal Kit",
            content_category: "partial",
          });
        } catch (err) { /* ignore */ }
      }
    } catch (err) {
      console.warn("[partial-lead] send failed:", err);
    } finally {
      partialFlightRef.current.sending = false;
    }
  }, []);

  // We no longer fire partial-leads on every keystroke. Instead, partials
  // are flushed ONCE per session, on tab close, if the user has a valid
  // email + 1 other field and HASN'T submitted step 1.

  // Best-effort flush on tab close, if user leaves with unsent partial state
  React.useEffect(() => {
    const flush = () => {
      if (partialFlightRef.current.completed) return;
      if (!/^\S+@\S+\.\S+$/.test(lead.email || "")) return;
      const filled = [lead.first, lead.last, lead.email, lead.phone, lead.street, lead.city, lead.state, lead.zip].filter((v) => v && v.toString().trim()).length;
      if (filled < 2) return;
      const data = JSON.stringify({ ...lead, source: "partial_exit" });
      const sig = data;
      if (sig === partialFlightRef.current.lastSignature) return;
      partialFlightRef.current.lastSignature = sig;
      // Mark completed so other handlers don't also fire
      partialFlightRef.current.completed = true;
      // sendBeacon works during page-unload where fetch may be cancelled
      if (navigator.sendBeacon) {
        const blob = new Blob([data], { type: "application/json" });
        navigator.sendBeacon("/api/partial-lead", blob);
      } else {
        fetch("/api/partial-lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: data, keepalive: true });
      }
    };
    window.addEventListener("beforeunload", flush);
    window.addEventListener("pagehide", flush);
    return () => {
      window.removeEventListener("beforeunload", flush);
      window.removeEventListener("pagehide", flush);
    };
  }, [lead]);

  const update = (k, v) => setLead((s) => ({ ...s, [k]: v }));

  const validateStep1 = () => {
    const e = {};
    if (!lead.first.trim()) e.first = true;
    if (!lead.last.trim()) e.last = true;
    if (!/^\S+@\S+\.\S+$/.test(lead.email)) e.email = true;
    if (lead.phone.replace(/\D/g, "").length < 10) e.phone = true;
    if (!lead.street.trim()) e.street = true;
    if (!lead.city.trim()) e.city = true;
    if (!lead.state.trim()) e.state = true;
    if (!/^\d{5}(-?\d{4})?$/.test(lead.zip.trim())) e.zip = true;
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  const fullAddress = () => {
    const parts = [lead.street, lead.city, [lead.state, lead.zip].filter(Boolean).join(" ")].filter(Boolean);
    return parts.join(", ");
  };

  // Per-session lead event_id — shared between browser fbq('track', 'Lead', ..., {eventID})
  // and the server-side Conversions API call so Meta deduplicates them. Generated
  // once per page load, so a refresh = new event_id = new lead, which is correct.
  const leadEventIdRef = React.useRef(
    `lead_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`
  );

  // Final-lead flush ref, guarantees exactly ONE /api/lead email per session.
  // Once set true, neither the step-3 finisher, the step-1 fallback timer, nor
  // the beforeunload sendBeacon will re-fire.
  const leadFlushedRef = React.useRef(false);
  const step1FallbackTimerRef = React.useRef(null);

  const flushLeadOnce = React.useCallback((payload) => {
    if (leadFlushedRef.current) return Promise.resolve({ ok: true, dedup: true });
    leadFlushedRef.current = true;
    if (step1FallbackTimerRef.current) {
      clearTimeout(step1FallbackTimerRef.current);
      step1FallbackTimerRef.current = null;
    }
    return sendLead({ ...payload, event_id: leadEventIdRef.current });
  }, [sendLead]);

  // sendBeacon fallback for "user submitted step 1 then closed the tab".
  React.useEffect(() => {
    const flush = () => {
      if (leadFlushedRef.current) return;
      if (!step1FallbackTimerRef.current) return; // step 1 wasn't submitted
      leadFlushedRef.current = true;
      const data = JSON.stringify({ ...lead, address: fullAddress(), photoCount: 0, items: [], source: "step1_abandoned", event_id: leadEventIdRef.current });
      if (navigator.sendBeacon) {
        const blob = new Blob([data], { type: "application/json" });
        navigator.sendBeacon("/api/lead", blob);
      } else {
        fetch("/api/lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: data, keepalive: true });
      }
    };
    window.addEventListener("beforeunload", flush);
    window.addEventListener("pagehide", flush);
    return () => {
      window.removeEventListener("beforeunload", flush);
      window.removeEventListener("pagehide", flush);
    };
  }, [lead]);

  const submitStep1 = async (ev) => {
    ev.preventDefault();
    if (!validateStep1()) return;
    if (submitting) return;
    setSubmitting(true);
    setSubmitError(null);

    // Tell the partial-lead system this user has converted, stop sending
    // partials for the same session.
    partialFlightRef.current.completed = true;

    // NOTE: we deliberately DO NOT email yet. Step 3 (or a fallback timer / tab
    // close beacon) will fire ONE consolidated email with all data. Keeps the
    // operator's inbox to a single message per lead.
    //
    // Fallback timer: if user submits step 1 but never reaches step 3 within
    // 5 minutes, send a "step1 only" lead email so we don't lose them.
    if (step1FallbackTimerRef.current) clearTimeout(step1FallbackTimerRef.current);
    step1FallbackTimerRef.current = setTimeout(() => {
      flushLeadOnce({ ...lead, address: fullAddress(), photoCount: 0, items: [], source: "step1_timeout" });
    }, 5 * 60 * 1000);

    // Meta Pixel: primary conversion event, fires the moment a complete
    // lead form is submitted (before optional photo step).
    //
    // Advanced Matching: re-init the Pixel with the user's PII so Meta can
    // hash + match the conversion to a real FB profile even when ITP/ATT
    // blocks third-party cookies. Meta SDK hashes these client-side; the
    // raw values never leave the browser. Improves match quality 10-20%.
    if (typeof window !== "undefined" && typeof window.fbq === "function") {
      try {
        window.fbq("init", "27809644531957665", {
          em: lead.email.toLowerCase().trim(),
          ph: lead.phone.replace(/\D/g, ""),
          fn: lead.first.toLowerCase().trim(),
          ln: lead.last.toLowerCase().trim(),
          ct: lead.city.toLowerCase().trim(),
          st: lead.state.toLowerCase().trim(),
          zp: lead.zip.trim(),
          country: "us",
        });
        window.fbq("track", "Lead", {
          content_name: "Free Appraisal Kit",
          content_category: "lead_form",
        }, { eventID: leadEventIdRef.current });
      } catch (err) { console.warn("[fbq] Lead event error:", err); }
    }

    // Fire Meta Conversions API server-side in parallel with the same event_id.
    // Recovers leads from users blocking the browser pixel (ad blockers / ITP).
    // capi_only=true means "no email yet" — the consolidated email still fires
    // at step 3 or via the 5-min fallback timer. Meta dedupes the two CAPI hits
    // by event_id within 48h.
    fetch("/api/lead", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        ...lead, address: fullAddress(),
        capi_only: true,
        event_id: leadEventIdRef.current,
        source: "step1_capi",
      }),
      keepalive: true,
    }).catch((err) => console.warn("[capi] step1 fire failed:", err));

    onSlotClaim && onSlotClaim();
    setStep(2);
    setSubmitting(false);
    setTimeout(() => {
      const el = document.getElementById("form-card");
      if (el) window.scrollTo({ top: el.offsetTop - 80, behavior: "smooth" });
    }, 50);
  };

  const finishStep3 = async () => {
    if (submitting) return;
    setSubmitting(true);
    setSubmitError(null);

    const allItemsNow = photos.flatMap((p) => p.items || []);
    // Compute a rough indicator of lead value (for Meta optimization) based on
    // detected markings. NOT shown to the customer.
    const marksNow = allItemsNow.flatMap((it) => it.visible_markings || []);
    const hiKaratHit = ["14K", "18K", "22K", "24K"].some((k) => marksNow.includes(k));
    const indicativeValue = hiKaratHit ? 800 : marksNow.length ? 200 : 100;

    const r = await flushLeadOnce({
      ...lead,
      address: fullAddress(),
      items: allItemsNow,
      photoCount: photos.length,
      photoUrls: photos.map((p) => p.blobUrl).filter(Boolean),
      source: photos.length ? "step2_with_photos" : "step2_no_photos",
    });

    if (!r.ok) setSubmitError("We couldn't reach our servers, but your info is saved locally. Please email hello@goldbuyingexperts.com if you don't hear from us within an hour.");

    // Meta Pixel: full funnel completion. We pass an indicative value derived
    // from detected karat marks so Meta can optimize toward higher-quality
    // leads, this is NOT a customer-facing valuation.
    if (typeof window !== "undefined" && typeof window.fbq === "function") {
      try {
        window.fbq("track", "CompleteRegistration", {
          content_name: "Free Appraisal Kit",
          status: photos.length ? "with_photos" : "no_photos",
          value: indicativeValue,
          currency: "USD",
        });
      } catch (err) { console.warn("[fbq] CompleteRegistration event error:", err); }
    }

    setSubmitting(false);
    setStep(3);
  };

  // Photo slot definitions, labeled tiles for higher data quality
  const PHOTO_SLOTS = [
    { key: "front",    label: "Front of item",       required: true,  hint: "Primary face." },
    { key: "back",     label: "Back of item",        required: false, hint: "Reverse side, where stamps usually live." },
    { key: "marking",  label: "Close-up of stamp",   required: false, hint: "10K / 14K / 18K / 22K / 24K / 925 / GF / GP." },
    { key: "scale",    label: "On a scale (optional)", required: false, hint: "Helps with weight estimate." },
    { key: "multi",    label: "Multiple items (optional)", required: false, hint: "Several pieces in one shot." },
  ];

  const handleSlotFile = (slotKey, file) => {
    if (!file || !file.type.startsWith("image/")) return;
    // Replace any existing photo in this slot
    const existing = photos.find((p) => p.slotKey === slotKey);
    if (existing) {
      setPhotos((s) => s.filter((p) => p.id !== existing.id));
    }
    const id = Math.random().toString(36).slice(2);
    const url = URL.createObjectURL(file);
    const newPhoto = { id, slotKey, url, name: file.name, status: "analyzing", items: [], blobUrl: null };
    setPhotos((s) => [...s, newPhoto]);
    processPhoto(file, id, slotKey);
  };

  // Upload the raw image bytes to Vercel Blob and return its public URL.
  const uploadPhoto = async (file) => {
    const r = await fetch("/api/upload-photo", {
      method: "POST",
      headers: { "Content-Type": file.type || "image/jpeg" },
      body: file,
    });
    if (!r.ok) {
      const t = await r.text().catch(() => "");
      throw new Error(`upload ${r.status}: ${t.slice(0, 120)}`);
    }
    const json = await r.json();
    if (!json.url) throw new Error("upload: no url in response");
    return json.url;
  };

  // 1. Upload to Blob. 2. Identify by URL (smaller payload than base64).
  // 3. Update photo state with both Blob URL and AI items.
  const processPhoto = async (file, id, slotKey) => {
    let blobUrl = null;
    try {
      blobUrl = await uploadPhoto(file);
      setPhotos((s) => s.map((p) => p.id === id ? { ...p, blobUrl } : p));
    } catch (err) {
      console.warn("[upload-photo] failed, will identify via base64 instead:", err && err.message);
    }
    await identifyPhoto(file, id, blobUrl, slotKey);
  };

  const identifyPhoto = async (file, id, blobUrl, slotKey) => {
    try {
      let parsed = null;
      try {
        let bodyObj;
        if (blobUrl) {
          bodyObj = { imageUrl: blobUrl, photoSlot: slotKey };
        } else {
          const reader = new FileReader();
          const dataUrl = await new Promise((resolve, reject) => {
            reader.onload = (e) => resolve(e.target.result);
            reader.onerror = () => reject(new Error("Could not read file"));
            reader.readAsDataURL(file);
          });
          bodyObj = { image: dataUrl, photoSlot: slotKey };
        }
        const r = await fetch("/api/identify", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(bodyObj),
        });
        if (r.ok) parsed = await r.json();
      } catch (err) {
        console.warn("[identify] network error, using fallback:", err);
      }

      if (!parsed || !parsed.items || !parsed.items.length) {
        // Soft fallback: a single low-confidence item with a friendly summary.
        parsed = { items: [{
          item_type: "unknown",
          visible_markings: [],
          likely_material: "unknown",
          condition: "unknown",
          confidence: "low",
          red_flags: ["AI pre-screen unavailable"],
          needs_human_review: true,
          recommended_followup: "Manual review needed.",
          customer_facing_summary: "Thanks. Our team will review this photo and follow up shortly.",
        }] };
      }

      setPhotos((s) => s.map((p) => p.id === id ? { ...p, status: "done", items: parsed.items } : p));
    } catch (err) {
      console.error("[identify] unexpected error:", err);
      setPhotos((s) => s.map((p) => p.id === id ? { ...p, status: "done", items: [{
        item_type: "unknown",
        visible_markings: [],
        likely_material: "unknown",
        condition: "unknown",
        confidence: "low",
        red_flags: ["error during pre-screening"],
        needs_human_review: true,
        recommended_followup: "Manual review needed.",
        customer_facing_summary: "Thanks. Our appraisers will take a closer look at this photo.",
      }] } : p));
    }
  };

  const removePhoto = (id) => setPhotos((s) => s.filter((p) => p.id !== id));

  const allItems = photos.flatMap((p) => p.items || []);
  const anyAnalyzing = photos.some((p) => p.status === "analyzing");

  // Aggregate customer-facing summary across all uploaded photos
  const aggregatedSummary = (() => {
    const summaries = allItems.map((it) => it.customer_facing_summary).filter(Boolean);
    if (!summaries.length) return null;
    // De-dupe similar sentences
    const unique = [...new Set(summaries)];
    return unique.slice(0, 3).join(" ");
  })();
  // Collect all detected markings for the headline pill
  const allMarkings = [...new Set(allItems.flatMap((it) => it.visible_markings || []).filter((m) => m && m !== "unknown"))];

  // === Render ===
  return (
    <div className="form-card" id="form-card" data-step={step}>
      <div className="ribbon">Free Appraisal Kit · No Obligation · Insured Shipping</div>

      {step === 1 && (
        <div className="body">
          <h2>Get your <em>free appraisal kit</em></h2>
          <p className="form-sub">Takes 60 seconds. We'll text and email you a prepaid, insured shipping label, no obligation, ever.</p>

          <div className="scarcity-strip">
            <I.Flame size={16} />
            <span><strong>{slotsLeft} same-day appraisal slots</strong> remaining today, claims close at midnight.</span>
          </div>

          <form onSubmit={submitStep1} noValidate>
            <div className="field-row">
              <div className="field">
                <label>First name</label>
                <input type="text" value={lead.first} onChange={(e) => update("first", e.target.value)}
                  style={errors.first ? { borderColor: "var(--red)" } : null} placeholder="Jane" autoComplete="given-name" />
              </div>
              <div className="field">
                <label>Last name</label>
                <input type="text" value={lead.last} onChange={(e) => update("last", e.target.value)}
                  style={errors.last ? { borderColor: "var(--red)" } : null} placeholder="Doe" autoComplete="family-name" />
              </div>
            </div>
            <div className="field">
              <label>Email address</label>
              <input type="email" value={lead.email} onChange={(e) => update("email", e.target.value)}
                style={errors.email ? { borderColor: "var(--red)" } : null} placeholder="you@email.com" autoComplete="email" />
            </div>
            <div className="field">
              <label>Phone number</label>
              <input type="tel" inputMode="tel" value={lead.phone} onChange={(e) => update("phone", e.target.value)}
                style={errors.phone ? { borderColor: "var(--red)" } : null} placeholder="(555) 555-5555" autoComplete="tel" />
            </div>
            <div className="field">
              <label>Street address</label>
              <input type="text" value={lead.street} onChange={(e) => update("street", e.target.value)}
                style={errors.street ? { borderColor: "var(--red)" } : null} placeholder="123 Main Street" autoComplete="address-line1" />
            </div>
            <div className="field">
              <label>City</label>
              <input type="text" value={lead.city} onChange={(e) => update("city", e.target.value)}
                style={errors.city ? { borderColor: "var(--red)" } : null} placeholder="Atlanta" autoComplete="address-level2" />
            </div>
            <div className="field-grid-address">
              <div className="field field-state">
                <label>State</label>
                <select value={lead.state} onChange={(e) => update("state", e.target.value)}
                  style={errors.state ? { borderColor: "var(--red)" } : null} autoComplete="address-level1">
                  <option value="">Select…</option>
                  {US_STATES.map(([abbr, name]) => (
                    <option key={abbr} value={abbr}>{name}</option>
                  ))}
                </select>
              </div>
              <div className="field">
                <label>ZIP code</label>
                <input type="text" inputMode="numeric" pattern="[0-9]*" value={lead.zip}
                  onChange={(e) => update("zip", e.target.value.replace(/[^\d-]/g, "").slice(0, 10))}
                  style={errors.zip ? { borderColor: "var(--red)" } : null} placeholder="30318" autoComplete="postal-code" />
              </div>
            </div>

            <button type="submit" className="cta-btn">
              Claim My Free Kit<span className="arrow">→</span>
            </button>

            <div className="form-foot">
              <span className="lock"><I.Lock size={13} stroke={2} /> 256-bit secure</span>
              <span>· We never sell your info ·</span>
              <span>No credit card</span>
            </div>

            <div className="step-dots">
              <span className="active"></span>
              <span></span>
            </div>
          </form>
        </div>
      )}

      {step === 2 && (
        <div className="body">
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
            <div style={{ width: 28, height: 28, borderRadius: "50%", background: "rgba(63,157,112,0.15)", color: "var(--green)", display: "grid", placeItems: "center" }}>
              <I.Check size={16} stroke={2.4} />
            </div>
            <div style={{ fontSize: 12, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--green)", fontWeight: 700 }}>
              Step 1 of 2 complete
            </div>
          </div>
          <h2>Upload photos for a <em>faster estimate</em></h2>
          <p className="form-sub">
            Add a few clear photos of your gold, jewelry, coins, or watches so our team can review your items faster. Optional, but takes 30 seconds.
          </p>

          <div className="upload-instructions">
            <strong>For best results:</strong> bright lighting, avoid blur, and include any visible markings like <span className="mark-list">10K, 14K, 18K, 22K, 24K, 925, GF, GP</span>.
          </div>

          <div className="slot-grid">
            {PHOTO_SLOTS.map((slot) => {
              const photo = photos.find((p) => p.slotKey === slot.key);
              const inputId = `slot-input-${slot.key}`;
              return (
                <label key={slot.key} className={"slot " + (photo ? "filled" : "")} htmlFor={inputId}>
                  <input
                    id={inputId}
                    type="file"
                    accept="image/*"
                    capture="environment"
                    style={{ display: "none" }}
                    onChange={(e) => {
                      const f = e.target.files && e.target.files[0];
                      if (f) handleSlotFile(slot.key, f);
                      e.target.value = "";
                    }}
                  />
                  {photo ? (
                    <React.Fragment>
                      <img src={photo.url} alt="" className="slot-thumb" />
                      <button type="button" className="slot-x" aria-label="Remove" onClick={(e) => { e.preventDefault(); e.stopPropagation(); removePhoto(photo.id); }}>
                        <I.X size={12} stroke={2.4} />
                      </button>
                      <span className={"slot-badge " + (photo.status === "analyzing" ? "analyzing" : "done")}>
                        {photo.status === "analyzing" ? "Analyzing…" : "Reviewed"}
                      </span>
                    </React.Fragment>
                  ) : (
                    <React.Fragment>
                      <I.Camera size={28} stroke={1.6} />
                      <span className="slot-label">{slot.label}</span>
                      <span className="slot-hint">{slot.hint}</span>
                    </React.Fragment>
                  )}
                </label>
              );
            })}
          </div>

          {allItems.length > 0 && (
            <div className="identified-list">
              <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 10 }}>
                <I.Sparkles size={14} />
                <strong style={{ fontSize: 11, letterSpacing: "0.14em", textTransform: "uppercase" }}>Preliminary review</strong>
              </div>
              {allItems.map((it, idx) => {
                const marks = (it.visible_markings || []).filter((m) => m && m !== "unknown");
                return (
                  <div key={idx} className="row">
                    <span className="check"><I.Check size={14} stroke={2.4} /></span>
                    <span>
                      <div><strong style={{ textTransform: "capitalize" }}>{it.item_type}</strong>{marks.length ? <span style={{ color: "var(--gold-3)", marginLeft: 8, fontFamily: "var(--mono)", fontSize: 12 }}>{marks.join(", ")}</span> : null}</div>
                      <div style={{ fontSize: 12, color: "var(--muted-dark)" }}>{it.customer_facing_summary}</div>
                    </span>
                    <span className="est" style={{ textTransform: "capitalize", fontSize: 11 }}>{it.confidence}</span>
                  </div>
                );
              })}
              <div style={{ fontSize: 11, color: "var(--muted-dark)", marginTop: 10, lineHeight: 1.5 }}>
                <strong>This is a preliminary review only.</strong> Final offers require verification of weight, purity, condition, and current market pricing.
              </div>
            </div>
          )}

          <button type="button" className="cta-btn" style={{ marginTop: 16 }}
            disabled={anyAnalyzing || submitting}
            onClick={finishStep3}>
            {submitting ? "Submitting…" : anyAnalyzing ? "Reviewing photos…" : (photos.length === 0 ? "Ship My Kit Without Photos" : "Lock In My Free Kit")}
            <span className="arrow">→</span>
          </button>

          {submitError && (
            <div style={{ marginTop: 10, fontSize: 12, color: "var(--red)", textAlign: "center" }}>{submitError}</div>
          )}

          {photos.length === 0 && (
            <button type="button" className="skip-link" onClick={finishStep3} style={{ display: "block", margin: "8px auto 0" }} disabled={submitting}>
              Skip. I'll show items when I pack the kit
            </button>
          )}

          <div className="step-dots">
            <span className="done"></span>
            <span className="active"></span>
          </div>
        </div>
      )}

      {step === 3 && (
        <div className="success-card">
          <div className="icon"><I.Check size={32} stroke={2.4} /></div>
          <h2>You're all set, {lead.first || "friend"}.</h2>
          <p style={{ marginTop: 10 }}>Your prepaid <strong>insured shipping kit</strong> ships today. Watch your inbox at <strong style={{ color: "var(--ink)" }}>{lead.email}</strong> for tracking, a senior appraiser will email you with next steps within 1 business hour.</p>

          {aggregatedSummary && (
            <div style={{ marginTop: 20, padding: "16px 18px", background: "rgba(201,162,75,0.08)", borderRadius: 6, border: "1px solid #d9cdb2", textAlign: "left" }}>
              <div style={{ fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--gold-3)", fontWeight: 700, marginBottom: 8 }}>
                Preliminary review
              </div>
              <p style={{ fontFamily: "var(--serif)", fontSize: 18, fontStyle: "italic", color: "var(--ink)", margin: 0, lineHeight: 1.5 }}>
                "{aggregatedSummary}"
              </p>
              {allMarkings.length > 0 && (
                <div style={{ marginTop: 10, display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {allMarkings.map((m) => (
                    <span key={m} style={{ display: "inline-flex", alignItems: "center", gap: 4, background: "var(--ink)", color: "var(--gold-2)", padding: "4px 10px", borderRadius: 999, fontSize: 11, fontFamily: "var(--mono)", fontWeight: 600, letterSpacing: "0.05em" }}>
                      <I.Check size={11} stroke={2.6} /> {m} visible
                    </span>
                  ))}
                </div>
              )}
              <div style={{ marginTop: 12, fontSize: 11, color: "var(--muted-dark)", lineHeight: 1.5 }}>
                Final offer requires verification of weight, purity, and condition by our GIA-certified appraisers.
              </div>
            </div>
          )}

          <div style={{ marginTop: 24, fontSize: 12, color: "var(--muted-dark)" }}>Questions? Email <strong style={{ color: "var(--ink)" }}>hello@goldbuyingexperts.com</strong> · we reply within the hour, 7 days a week</div>
        </div>
      )}
    </div>
  );
}

window.LeadForm = LeadForm;
