/* Loadpath — flow screens, part 1: eligibility → upload → classify → quote → pay */
(function () {
  const {
    Icon,
    Btn,
    Badge,
    Callout,
    ConfDot,
    SrcTag,
    Card,
    Money,
    KV,
    AddressInput,
    engineerTypeLabel,
  } = window;
  const h = React.createElement;
  const { useState, useEffect, useRef } = React;

  // ---------------- Eligibility ----------------
  function ScreenEligibility({ project, go }) {
    const initialAddr = [project && project.address, project && project.city]
      .filter(Boolean)
      .join(", ");
    const [addr, setAddr] = useState(initialAddr);
    const [phase, setPhase] = useState("idle"); // idle | checking | ok | error
    const [eligibility, setEligibility] = useState(null);
    const [err, setErr] = useState("");

    async function check() {
      const address = (addr || "").trim();
      if (!address) {
        setErr("Project address is required");
        setPhase("error");
        return;
      }
      setPhase("checking");
      setErr("");
      setEligibility(null);
      try {
        const result = await window.API.eligibility.check(address);
        setEligibility(result);
        if (!result || !result.eligible) {
          setErr(
            (result && result.message) ||
              "This address is outside the supported service area",
          );
          setPhase("error");
          return;
        }
        setPhase("ok");
      } catch (e) {
        setErr(e.message || "Unable to geocode this address");
        setPhase("error");
      }
    }

    const codeEdition = (eligibility && eligibility.code_edition) || "";
    const codeLabel = codeEdition
      ? codeEdition.split("·")[0].trim()
      : "Resolved by jurisdiction";
    const stateCode =
      (eligibility && eligibility.state) || (project && project.state) || "";
    const stateLabel =
      (eligibility &&
        (eligibility.state_name || stateName(eligibility.state))) ||
      stateName(stateCode) ||
      stateCode ||
      "project state";
    const jurisdiction =
      (eligibility && eligibility.jurisdiction) || "jurisdiction resolved";

    return h(
      "div",
      { className: "fade-in" },
      h(
        "div",
        { className: "eyebrow screen-eyebrow" },
        "Step 1 · Intake & eligibility",
      ),
      h("h1", { className: "screen-title" }, "Where is the project?"),
      h(
        "p",
        { className: "screen-sub" },
        "Eligibility is determined by the ",
        h("b", null, "project address"),
        " — never your business address. We operate in 38 states today; design criteria are pulled per address.",
      ),
      h(
        Card,
        null,
        h(
          "div",
          { className: "field" },
          h(
            "label",
            null,
            "Project address ",
            h("span", { className: "req" }, "*"),
          ),
          h(
            "div",
            { style: { display: "flex", gap: 10 } },
            h(AddressInput, {
              className: "input",
              value: addr,
              onChange: (e) => {
                setAddr(e.target.value);
                setPhase("idle");
                setErr("");
                setEligibility(null);
              },
              onFill: ({ street, city, state, zip }) => {
                setAddr(
                  [street, city, state + " " + zip].filter(Boolean).join(", "),
                );
                setPhase("idle");
                setErr("");
                setEligibility(null);
              },
              placeholder: "Street, City, ST ZIP",
            }),
            h(
              Btn,
              {
                kind: "secondary",
                icon: "map-pin",
                onClick: check,
                disabled: phase === "checking" || !(addr || "").trim(),
              },
              "Check",
            ),
          ),
        ),
        phase === "checking" &&
          h(
            "div",
            { className: "proc-log", style: { marginTop: 8 } },
            h(
              "div",
              { className: "ln active" },
              h("span", { className: "spin" }),
              "Geocoding address → resolving jurisdiction…",
            ),
          ),
        phase === "error" &&
          err &&
          h(
            "div",
            { className: "fade-in", style: { marginTop: 8 } },
            h(
              Callout,
              { kind: "danger", title: "Eligibility check failed" },
              err,
            ),
          ),
        phase === "ok" &&
          h(
            "div",
            { className: "fade-in", style: { marginTop: 8 } },
            h(
              Callout,
              { kind: "success", title: "Covered — " + stateLabel },
              "This project is inside our service area. Sealed downstream by a PE licensed in ",
              stateCode || stateLabel,
              ". ",
              "Jurisdiction: ",
              h("b", null, jurisdiction),
              ". ",
              "Code edition resolved: ",
              h("b", null, codeLabel),
              ".",
            ),
          ),
      ),
      h(
        "div",
        {
          style: {
            marginTop: 16,
            display: "flex",
            gap: 10,
            alignItems: "center",
            fontSize: 12.5,
            color: "var(--fg-subtle)",
          },
        },
        h(Icon, { name: "info", size: 14 }),
        "Outside the 38 states, we capture the lead and notify on expansion — no upload required.",
      ),
      h(
        "div",
        { className: "btn-row end", style: { marginTop: 24 } },
        h(
          Btn,
          {
            kind: "primary",
            iconRight: "arrow-right",
            disabled: phase !== "ok",
            onClick: go.next,
          },
          "Continue to upload",
        ),
      ),
    );
  }

  // ---------------- Upload ----------------
  function ScreenUpload({ project, go }) {
    const [drag, setDrag] = useState(false);
    const [file, setFile] = useState(null);
    const hasQuote = project.fee && project.status === "quoted";
    const [current, setCurrent] = useState(project);
    const initialScreen =
      project.status === "missing_info"
        ? "missing"
        : project.status === "rejected"
          ? "blocked"
          : hasQuote
            ? "pass"
            : project.drawingFileID
              ? "ready"
              : "idle";
    const [screen, setScreen] = useState(initialScreen); // idle | ready | uploading | screening | classifying | pass | blocked | missing | error
    const [err, setErr] = useState("");
    const [parseError, setParseError] = useState("");
    const [rawText, setRawText] = useState("");
    const [classifyPromptUsed, setClassifyPromptUsed] = useState("");
    const inputRef = useRef(null);
    function choose(f) {
      if (!f) return;
      setFile(f);
      setCurrent(project);
      setErr("");
      setParseError("");
      setRawText("");
      setClassifyPromptUsed("");
      uploadAndScreen(f);
    }
    function updateCurrent(p) {
      if (!p) return;
      setCurrent(p);
      if (go.updateProject) go.updateProject(p);
    }
    async function classifyUploadedDrawing() {
      setScreen("classifying");
      const classified = await window.API.projects.classify(project.id);
      const proj =
        classified && classified.project ? classified.project : classified;
      updateCurrent(proj);
      setRawText(proj.classify_raw || "");
      setClassifyPromptUsed(classified.classify_prompt || "");
      if (classified && classified.parse_error) {
        setParseError(classified.parse_error);
        setScreen("error");
        return;
      }
      if (proj.truss || proj.status === "rejected") {
        setScreen("blocked");
        return;
      }
      if (proj.status === "missing_info") {
        setScreen("missing");
        return;
      }
      if (!proj.fee) {
        setErr("Quote was not created. Please re-run drawing analysis.");
        setScreen("error");
        return;
      }
      setScreen("pass");
      setTimeout(() => {
        if (go.toQuote) go.toQuote();
        else go.next();
      }, 550);
    }
    async function analyzeExistingDrawing() {
      setErr("");
      setParseError("");
      try {
        setScreen("screening");
        const screened = await window.API.projects.preScreen(project.id);
        const screenedProject =
          screened && screened.project ? screened.project : current;
        updateCurrent(screenedProject);
        if (screenedProject.status === "rejected") {
          setScreen("blocked");
          return;
        }
        await classifyUploadedDrawing();
      } catch (e) {
        setErr(e.message || "Drawing analysis failed");
        setScreen("error");
      }
    }
    async function uploadAndScreen(selectedFile) {
      const drawing = selectedFile || file;
      if (!drawing) return;
      setErr("");
      setParseError("");
      try {
        setScreen("uploading");
        const uploaded = await window.API.projects.upload(project.id, drawing);
        updateCurrent(uploaded.project || current);
        setScreen("screening");
        const screened = await window.API.projects.preScreen(project.id);
        const screenedProject =
          screened && screened.project ? screened.project : current;
        updateCurrent(screenedProject);
        if (screenedProject.status === "rejected") {
          setScreen("blocked");
          return;
        }
        await classifyUploadedDrawing();
      } catch (e) {
        setErr(e.message || "Upload and analysis failed");
        setScreen("error");
      }
    }
    const busy =
      screen === "uploading" ||
      screen === "screening" ||
      screen === "classifying";
    const showFile = !!file || !!current.drawingFileID;
    const fileName = file ? file.name : "Uploaded drawing set";
    const fileMeta = file
      ? (file.size / 1024 / 1024).toFixed(2) + " MB"
      : "Saved PDF";
    return h(
      "div",
      { className: "fade-in" },
      h("div", { className: "eyebrow screen-eyebrow" }, "Upload"),
      h("h1", { className: "screen-title" }, "Upload the solar drawing"),
      h(
        "p",
        { className: "screen-sub" },
        "Upload one clear solar drawing PDF. We check readability, classify the project, count calculation routes, and prepare the quote before any payment.",
      ),
      !showFile
        ? h(
            "div",
            {
              className: "dropzone" + (drag ? " drag" : ""),
              onClick: () => inputRef.current && inputRef.current.click(),
              onDragOver: (e) => {
                e.preventDefault();
                setDrag(true);
              },
              onDragLeave: () => setDrag(false),
              onDrop: (e) => {
                e.preventDefault();
                setDrag(false);
                choose(e.dataTransfer.files && e.dataTransfer.files[0]);
              },
            },
            h("input", {
              ref: inputRef,
              type: "file",
              accept: "application/pdf,.pdf",
              style: { display: "none" },
              onChange: (e) => choose(e.target.files && e.target.files[0]),
            }),
            h(
              "div",
              { className: "dz-icon" },
              h(Icon, { name: "upload", size: 44, stroke: 1.25 }),
            ),
            h("h4", null, "Drop the drawing PDF, or click to select"),
            h("p", null, "Multi-sheet PDFs are welcome · max 20 MB"),
          )
        : h(
            "div",
            { className: "fade-in" },
            h(
              "div",
              { className: "filechip" },
              h("div", { className: "fi" }, "PDF"),
              h(
                "div",
                { className: "meta" },
                h("div", { className: "fn" }, fileName),
                h("div", { className: "fm" }, fileMeta),
              ),
              (screen === "idle" || screen === "ready") &&
                file &&
                h(Btn, {
                  kind: "ghost",
                  icon: "x",
                  size: "sm",
                  onClick: () => setFile(null),
                }),
              screen === "pass" &&
                h(Badge, { kind: "pass", icon: "check" }, "Quote ready"),
            ),
            screen === "ready" &&
              h(
                "div",
                { className: "btn-row", style: { marginTop: 14 } },
                h(
                  Btn,
                  {
                    kind: "secondary",
                    icon: "scan-line",
                    onClick: analyzeExistingDrawing,
                  },
                  "Analyze saved drawing",
                ),
              ),
            busy &&
              h(
                Card,
                { className: "scanline", tight: true },
                h(
                  "div",
                  { className: "proc-log" },
                  h(
                    "div",
                    {
                      className:
                        "ln " + (screen === "uploading" ? "active" : ""),
                    },
                    screen === "uploading"
                      ? h("span", { className: "spin" })
                      : h(Icon, {
                          name: "check",
                          size: 14,
                          className: "tick",
                          style: { color: "var(--signal-success)" },
                        }),
                    "Storing and processing PDF",
                  ),
                  h(
                    "div",
                    {
                      className:
                        "ln " +
                        (screen === "screening"
                          ? "active"
                          : screen === "classifying"
                            ? ""
                            : "pending"),
                    },
                    screen === "screening"
                      ? h("span", { className: "spin" })
                      : screen === "classifying"
                        ? h(Icon, {
                            name: "check",
                            size: 14,
                            className: "tick",
                            style: { color: "var(--signal-success)" },
                          })
                        : h("span", {
                            className: "tick",
                            style: { width: 14 },
                          }),
                    "Checking readability and scope",
                  ),
                  h(
                    "div",
                    {
                      className:
                        "ln " +
                        (screen === "classifying" ? "active" : "pending"),
                    },
                    screen === "classifying"
                      ? h("span", { className: "spin" })
                      : h("span", { className: "tick", style: { width: 14 } }),
                    "Classifying project and preparing quote",
                  ),
                ),
              ),
            err &&
              h(
                "div",
                {
                  style: {
                    color: "var(--signal-danger)",
                    fontSize: 13,
                    marginTop: 10,
                  },
                },
                err,
              ),
            parseError &&
              h(
                "div",
                { style: { marginTop: 12 } },
                h(
                  Callout,
                  {
                    kind: "warn",
                    title: "Response received — structured parsing failed",
                  },
                  parseError,
                ),
              ),
            screen === "pass" &&
              h(
                "div",
                { style: { marginTop: 14 } },
                h(
                  Callout,
                  { kind: "success", title: "Drawing accepted · quote ready" },
                  "The project is in scope and the price is ready. Moving to Quote.",
                ),
              ),
          ),
      screen === "blocked" &&
        h(
          "div",
          { style: { marginTop: 16 } },
          h(TrussOutOfScope, { project: current, go }),
        ),
      screen === "missing" &&
        h(
          "div",
          { style: { marginTop: 16 } },
          h(MissingInfoReport, { project: current, go }),
        ),
      rawText &&
        (screen === "error" || screen === "blocked" || screen === "missing") &&
        h(RawResponseBlock, {
          text: rawText,
          classifyPrompt: classifyPromptUsed,
        }),
      h(
        "div",
        { className: "btn-row end", style: { marginTop: 24 } },
        h(
          Btn,
          {
            kind: "primary",
            iconRight: "arrow-right",
            disabled: screen !== "pass",
            onClick: go.toQuote || go.next,
          },
          "View quote",
        ),
      ),
    );
  }

  // ---------------- Classify ----------------
  function ScreenClassify({ project, tweaks, go }) {
    const style = tweaks.extractionStyle || "log";
    const [done, setDone] = useState(false);
    const [animDone, setAnimDone] = useState(false);
    const [err, setErr] = useState("");
    const [result, setResult] = useState(null);
    const [rawText, setRawText] = useState("");
    const [parseError, setParseError] = useState("");
    const [classifyPromptUsed, setClassifyPromptUsed] = useState("");
    useEffect(() => {
      let alive = true;
      setErr("");
      setParseError("");
      setRawText("");
      setClassifyPromptUsed("");
      window.API.projects
        .classify(project.id)
        .then((d) => {
          if (!alive) return;
          // API returns {project, classify_prompt} on success, or {project, parse_error, classify_prompt} on parse failure
          const proj = d && d.project ? d.project : d;
          const pe = d && d.parse_error ? d.parse_error : "";
          setRawText(proj.classify_raw || "");
          setParseError(pe);
          setClassifyPromptUsed(d.classify_prompt || "");
          setResult(proj);
          if (go.updateProject) go.updateProject(proj);
          setDone(true);
        })
        .catch((e) => {
          if (alive) setErr(e.message || "Classification failed");
        });
      return () => {
        alive = false;
      };
    }, [project.id]);
    const current = result || project;
    const raw = rawText || current.classify_raw || "";
    return h(
      "div",
      { className: "fade-in" },
      h(
        "div",
        { className: "eyebrow screen-eyebrow" },
        "Step 3 · Classification & extraction",
      ),
      h("h1", { className: "screen-title" }, "Reading the drawing"),
      h(
        "p",
        { className: "screen-sub" },
        "Our engine extracts every project-specific value with its source sheet, classifies the project, counts distinct framing combinations, and detects rafter vs. truss. The drawing always wins over any stated type.",
      ),

      err
        ? h(Callout, { kind: "danger", title: "Classification failed" }, err)
        : !done
          ? h(
              "div",
              null,
              !animDone
                ? h(Extraction, {
                    project: current,
                    style,
                    onDone: () => setAnimDone(true),
                  })
                : h(
                    Card,
                    null,
                    h(
                      "div",
                      { className: "proc-log" },
                      h(
                        "div",
                        { className: "ln active" },
                        h("span", { className: "spin" }),
                        "Reading and analyzing the drawing — this typically takes 30–60 s…",
                      ),
                    ),
                  ),
            )
          : parseError
            ? h(
                "div",
                { className: "fade-in" },
                classifyPromptUsed
                  ? h(
                      Callout,
                      { kind: "info", title: "Custom prompt response" },
                      "Claude responded using the admin system prompt. See the response block below.",
                    )
                  : h(
                      Callout,
                      {
                        kind: "warn",
                        title: "Response received — structured parsing failed",
                      },
                      parseError,
                    ),
                h("div", null),
              )
            : h(
                "div",
                { className: "fade-in" },
                current.truss || current.status === "rejected"
                  ? h(TrussOutOfScope, { project: current, go })
                  : current.status === "missing_info"
                    ? h(MissingInfoReport, { project: current, go })
                    : h(ClassifyResult, { project: current, go }),
              ),

      done &&
        raw &&
        h(RawResponseBlock, { text: raw, classifyPrompt: classifyPromptUsed }),
    );
  }

  // ---------------- Raw Response Block ----------------
  function RawResponseBlock({ text, classifyPrompt }) {
    const [open, setOpen] = useState(true);
    const [copied, setCopied] = useState(false);
    const [promptOpen, setPromptOpen] = useState(false);

    // Detect if text is JSON or markdown
    let isJSON = false;
    let pretty = text;
    try {
      pretty = JSON.stringify(JSON.parse(text), null, 2);
      isJSON = true;
    } catch (e) {}

    // Render markdown via marked.js if available and text is not JSON
    const isMarkdown = !isJSON && typeof window.marked !== "undefined";
    const mdHtml = isMarkdown ? window.marked.parse(text || "") : null;

    const copy = () => {
      navigator.clipboard
        .writeText(text)
        .then(() => {
          setCopied(true);
          setTimeout(() => setCopied(false), 1800);
        })
        .catch(() => {});
    };
    const preStyle = {
      marginTop: 8,
      padding: 10,
      background: "var(--bg)",
      border: "1px solid var(--border)",
      borderRadius: 6,
      fontSize: 11.5,
      fontFamily: "var(--font-mono)",
      overflowX: "auto",
      whiteSpace: "pre-wrap",
      wordBreak: "break-word",
      maxHeight: 300,
      overflowY: "auto",
      color: "var(--fg)",
      lineHeight: 1.6,
    };
    const mdStyle = {
      marginTop: 8,
      padding: "12px 16px",
      background: "var(--bg)",
      border: "1px solid var(--border)",
      borderRadius: 6,
      fontSize: 13,
      lineHeight: 1.7,
      color: "var(--fg)",
      maxHeight: 600,
      overflowY: "auto",
    };
    return h(
      "div",
      {
        style: {
          marginTop: 20,
          display: "flex",
          flexDirection: "column",
          gap: 8,
        },
      },
      classifyPrompt &&
        h(
          Card,
          { tight: true },
          h(
            "div",
            { style: { display: "flex", alignItems: "center", gap: 8 } },
            h(Icon, {
              name: "settings",
              size: 13,
              style: { color: "var(--accent)" },
            }),
            h(
              "span",
              {
                className: "eyebrow",
                style: { margin: 0, flex: 1, color: "var(--accent)" },
              },
              "Admin system prompt (active)",
            ),
            h(
              "button",
              {
                style: {
                  fontSize: 12,
                  color: "var(--fg-subtle)",
                  background: "none",
                  border: "1px solid var(--border)",
                  borderRadius: 4,
                  cursor: "pointer",
                  padding: "3px 10px",
                  fontFamily: "inherit",
                },
                onClick: () => setPromptOpen((o) => !o),
              },
              promptOpen ? "▲ Collapse" : "▼ Show",
            ),
          ),
          promptOpen && h("pre", { style: preStyle }, classifyPrompt),
        ),
      h(
        Card,
        { tight: true },
        h(
          "div",
          { style: { display: "flex", alignItems: "center", gap: 8 } },
          h(Icon, {
            name: "terminal",
            size: 13,
            style: { color: "var(--fg-subtle)" },
          }),
          h(
            "span",
            { className: "eyebrow", style: { margin: 0, flex: 1 } },
            isMarkdown ? "Claude response (markdown)" : "Raw API response",
          ),
          h(
            "button",
            {
              style: {
                fontSize: 12,
                color: "var(--fg-subtle)",
                background: "none",
                border: "1px solid var(--border)",
                borderRadius: 4,
                cursor: "pointer",
                padding: "3px 10px",
                fontFamily: "inherit",
              },
              onClick: copy,
            },
            copied ? "✓ Copied" : "Copy",
          ),
          h(
            "button",
            {
              style: {
                fontSize: 12,
                color: "var(--fg-subtle)",
                background: "none",
                border: "1px solid var(--border)",
                borderRadius: 4,
                cursor: "pointer",
                padding: "3px 10px",
                fontFamily: "inherit",
                marginLeft: 4,
              },
              onClick: () => setOpen((o) => !o),
            },
            open ? "▲ Collapse" : "▼ Expand",
          ),
        ),
        open &&
          (isMarkdown
            ? h("div", {
                style: mdStyle,
                className: "md-body",
                dangerouslySetInnerHTML: { __html: mdHtml },
              })
            : h("pre", { style: { ...preStyle, maxHeight: 500 } }, pretty)),
      ),
    );
  }

  function Extraction({ project, style, onDone }) {
    const steps = [
      "Loading sheets PV1.0 … PV6.0",
      "Vision pass — reading module layout, racking, attachment grid",
      "Extracting framing: rafter size, spacing, span, pitch",
      "Cross-checking values across sheets (verification pass)",
      "Classifying type · counting framing combinations · rafter vs. truss",
      "Resolving zoning via Zoneomics (" + (project.zoning || "pending") + ")",
    ];
    const [i, setI] = useState(0);
    useEffect(() => {
      if (i >= steps.length) {
        const t = setTimeout(onDone, 350);
        return () => clearTimeout(t);
      }
      const t = setTimeout(() => setI(i + 1), 520);
      return () => clearTimeout(t);
    }, [i]);
    return h(
      Card,
      { className: style === "overlay" ? "scanline" : "" },
      h(
        "div",
        { className: "eyebrow", style: { marginBottom: 14 } },
        "Analysis · extraction",
      ),
      h(
        "div",
        { className: "proc-log" },
        steps.map((s, k) =>
          h(
            "div",
            {
              key: k,
              className: "ln " + (k < i ? "" : k === i ? "active" : "pending"),
            },
            k < i
              ? h(Icon, {
                  name: "check",
                  size: 14,
                  className: "tick",
                  style: { color: "var(--signal-success)" },
                })
              : k === i
                ? h("span", { className: "spin" })
                : h("span", { className: "tick", style: { width: 14 } }),
            s,
          ),
        ),
      ),
    );
  }

  function ClassifyResult({ project, go }) {
    const sys = project.system || {};
    const extracted = project.extracted || [];
    const combinations = project.combinations || [];
    const calcUnits = project.calcUnits || project.calc_units || [];
    const routes = engineerTypes(project);
    return h(
      "div",
      { className: "stagger" },
      h(
        "div",
        { className: "grid-2", style: { marginBottom: 16 } },
        h(
          Card,
          { tight: true, eyebrow: "Classification" },
          h(KV, { k: "Project type" }, project.type || "—"),
          h(
            KV,
            { k: "Framing combinations" },
            h("b", null, project.combosCount),
          ),
          h(
            KV,
            { k: "Engineering routes" },
            routes.length
              ? routes.map((t) =>
                  h(Badge, { key: t, kind: "neutral" }, engineerTypeLabel(t)),
                )
              : "—",
          ),
          h(
            KV,
            { k: "Framing" },
            h(Badge, { kind: "pass", icon: "check" }, "Rafter — in scope"),
          ),
          h(KV, { k: "Zoning" }, project.zoning || "—"),
        ),
        h(
          Card,
          { tight: true, eyebrow: "System" },
          h(KV, { k: "DC / AC" }, (sys.dc || "—") + " / " + (sys.ac || "—")),
          h(KV, { k: "Modules" }, sys.modules ? sys.modules + " ea" : "—"),
          h(KV, { k: "Racking" }, sys.racking || "—"),
          h(KV, { k: "Attachment" }, sys.attach || "—"),
        ),
      ),
      extracted.length > 0 &&
        h(
          Card,
          {
            eyebrow: "Extracted values · " + extracted.length + " fields",
            title: null,
            right: h(
              "span",
              {
                style: {
                  display: "flex",
                  gap: 14,
                  fontSize: 11.5,
                  color: "var(--fg-subtle)",
                  alignItems: "center",
                },
              },
              h(
                "span",
                { style: { display: "flex", gap: 5, alignItems: "center" } },
                h(ConfDot, { level: "high" }),
                "high",
              ),
              h(
                "span",
                { style: { display: "flex", gap: 5, alignItems: "center" } },
                h(ConfDot, { level: "med" }),
                "verify",
              ),
              h(
                "span",
                { style: { display: "flex", gap: 5, alignItems: "center" } },
                h(ConfDot, { level: "low" }),
                "flagged",
              ),
            ),
          },
          h(
            "table",
            { className: "dtable" },
            h(
              "thead",
              null,
              h(
                "tr",
                null,
                h("th", null, "Field"),
                h("th", null, "Value"),
                h("th", null, "Source"),
                h("th", null, "Conf"),
              ),
            ),
            h(
              "tbody",
              null,
              extracted.map((e, k) =>
                h(
                  "tr",
                  { key: k },
                  h("td", { className: "name" }, e.name),
                  h(
                    "td",
                    { className: "val" },
                    e.value,
                    " ",
                    h("span", { className: "unit" }, e.unit),
                  ),
                  h(
                    "td",
                    null,
                    h(
                      "div",
                      { className: "src" },
                      h(SrcTag, { sheet: e.sheet }),
                    ),
                  ),
                  h("td", null, h(ConfDot, { level: e.conf })),
                ),
              ),
            ),
          ),
        ),
      calcUnits.length > 0 &&
        h(
          Card,
          {
            eyebrow: "Calculation units · " + calcUnits.length + " routed",
            title: null,
          },
          h(
            "div",
            { className: "combo-list" },
            calcUnits.map((u, i) =>
              h(
                "div",
                { key: i, className: "combo" },
                h(
                  "div",
                  { className: "ci" },
                  h(Icon, { name: "calculator", size: 16 }),
                ),
                h(
                  "div",
                  null,
                  h(
                    "div",
                    { className: "ctitle" },
                    "Unit " +
                      (u.unitID || u.unit_id || i + 1) +
                      " · " +
                      (u.description || "Calculation unit"),
                  ),
                  h(
                    "div",
                    { className: "csub" },
                    [u.structure, u.mount, u.framing]
                      .filter(Boolean)
                      .join(" · ") || "Route details pending",
                  ),
                ),
                h(
                  Badge,
                  { kind: "neutral" },
                  engineerTypeLabel(u.engineerType || u.engineer_type || "—"),
                ),
              ),
            ),
          ),
        ),
      project.combosCount > 1 &&
        h(
          "div",
          { style: { marginTop: 16 } },
          h(
            Callout,
            {
              kind: "accent",
              icon: "layers",
              title:
                project.combosCount +
                " distinct framing combinations · " +
                (routes.length || 1) +
                " engineering route" +
                ((routes.length || 1) > 1 ? "s" : ""),
            },
            "Claude uses the classified project scope for the quote. Engineering runs by engineer_type route, with one calculation and one PE letter per route.",
          ),
        ),
      h(
        "div",
        { className: "btn-row end", style: { marginTop: 24 } },
        h(
          Btn,
          { kind: "primary", iconRight: "arrow-right", onClick: go.next },
          "See the quote",
        ),
      ),
    );
  }

  function TrussOutOfScope({ project, go }) {
    const isTruss = !!project.truss;
    return h(
      "div",
      { className: "fade-in" },
      isTruss
        ? h(
            Callout,
            {
              kind: "danger",
              icon: "alert-octagon",
              title: "Out of scope — engineered truss roof",
            },
            "This platform provides PV structural engineering for ",
            h("b", null, "sawn-rafter"),
            " roof framing. The drawing shows an engineered truss roof, which we do not cover — a manufactured truss cannot be modified or sistered without the truss manufacturer\u2019s engineer. Please consult the truss manufacturer\u2019s engineer for this structure.",
          )
        : h(
            Callout,
            {
              kind: "danger",
              icon: "alert-octagon",
              title: "Out of scope — declined",
            },
            "This project falls outside accepted scope for PV structural engineering. See the detailed explanation in the response block below.",
          ),
      h(
        Card,
        { tight: true, style: { marginTop: 16 }, eyebrow: "What we detected" },
        (project.extracted || []).length
          ? project.extracted.map((e, k) =>
              h(KV, { key: k, k: e.name }, e.value),
            )
          : h(
              "div",
              { style: { fontSize: 12.5, color: "var(--fg-muted)" } },
              project.outcome ||
                "The drawing is outside the current automated scope.",
            ),
      ),
      h("div", null),
    );
  }

  function MissingInfoReport({ project, go }) {
    const missing = project.missing || [];
    return h(
      "div",
      { className: "fade-in" },
      h(
        Callout,
        {
          kind: "warn",
          icon: "alert-triangle",
          title: "Missing-Information Report — specific items required",
        },
        "Your drawing passed the basic pre-screen, but some of the values required for the analysis appear to be missing or inconsistent. A quick update will have everything ready to go.",
      ),
      missing.length > 0 &&
        h(
          Card,
          { style: { marginTop: 16 }, eyebrow: "What's blocking the analysis" },
          h(
            "table",
            { className: "dtable" },
            h(
              "thead",
              null,
              h(
                "tr",
                null,
                h("th", null, "Item"),
                h("th", null, "Reason"),
                h("th", null, "Needed for"),
              ),
            ),
            h(
              "tbody",
              null,
              missing.map((m, i) =>
                h(
                  "tr",
                  { key: i },
                  h("td", { className: "name" }, m.item),
                  h("td", { style: { color: "var(--fg-muted)" } }, m.reason),
                  h("td", null, h("span", { className: "srctag" }, m.section)),
                ),
              ),
            ),
          ),
        ),
      h(
        "div",
        { style: { marginTop: 16 } },
        h(
          Callout,
          { kind: "info" },
          h(
            "b",
            null,
            "No assumptions will be made for missing project-specific data.",
          ),
          " Please provide the items above and re-upload; the analysis will then be completed.",
        ),
      ),
      h(
        "div",
        { className: "btn-row end", style: { marginTop: 24 } },
        h(
          Btn,
          { kind: "primary", icon: "upload", onClick: go.toUpload },
          "Re-upload corrected drawing",
        ),
      ),
    );
  }

  // ---------------- Quote ----------------
  // ScreenQuote is no longer used as a separate step — merged into ScreenPay below.
  function ScreenQuote({ project, tweaks, go }) {
    return h(ScreenPay, { project, tweaks, go });
  }

  function CountUp({ to, dur = 900 }) {
    const [v, setV] = useState(0);
    useEffect(() => {
      let raf,
        start,
        done = false;
      const finish = () => {
        if (!done) {
          done = true;
          setV(to);
        }
      };
      const tick = (t) => {
        if (!start) start = t;
        const p = Math.min((t - start) / dur, 1);
        setV(to * (1 - Math.pow(1 - p, 3)));
        if (p < 1) raf = requestAnimationFrame(tick);
        else finish();
      };
      raf = requestAnimationFrame(tick);
      // Fallback: rAF is throttled to 0 in background/preview iframes — guarantee the final value.
      const fb = setTimeout(finish, dur + 120);
      return () => {
        cancelAnimationFrame(raf);
        clearTimeout(fb);
      };
    }, [to]);
    const [d, c] = v.toFixed(2).split(".");
    return h(
      "span",
      { className: "quote-amount big" },
      "$",
      d,
      h("span", { className: "quote-cents" }, "." + c),
    );
  }

  function QuoteReveal({ project }) {
    return h(
      "div",
      { className: "quote-hero" },
      h(
        "div",
        { className: "eyebrow", style: { marginBottom: 10 } },
        "Project-specific quote",
      ),
      h(CountUp, { to: project.fee }),
      h("div", { className: "quote-basis" }, project.feeBasis),
      h(
        "div",
        {
          style: {
            marginTop: 16,
            fontSize: 12.5,
            color: "var(--fg-subtle)",
            display: "flex",
            gap: 8,
            justifyContent: "center",
            alignItems: "center",
          },
        },
        h(Icon, { name: "zap", size: 14, style: { color: "var(--accent)" } }),
        "Immediate turnaround on the generated package",
      ),
    );
  }

  function QuoteBreakdown({ project }) {
    return h(
      Card,
      null,
      h(
        "div",
        { className: "eyebrow", style: { marginBottom: 8 } },
        "Quote scope",
      ),
      h(
        KV,
        { k: "Project type" },
        project.type || "Solar structural engineering",
      ),
      h(KV, { k: "Use" }, project.zoning || "—"),
      h(KV, { k: "Framing combinations" }, project.combosCount || 1),
      h(
        KV,
        { k: "Calculation packages" },
        engineerTypes(project).length || project.combosCount || 1,
      ),
      h(
        "p",
        {
          style: {
            margin: "14px 0 0",
            fontSize: 12.5,
            color: "var(--fg-muted)",
            lineHeight: 1.55,
          },
        },
        project.feeBasis,
      ),
      h("div", { className: "divider" }),
      h(
        "div",
        {
          style: {
            display: "flex",
            justifyContent: "space-between",
            alignItems: "baseline",
          },
        },
        h("span", { style: { fontWeight: 600 } }, "Your fee"),
        h(Money, { amount: project.fee }),
      ),
    );
  }

  function QuoteMinimal({ project }) {
    return h(
      Card,
      null,
      h(
        "div",
        {
          style: {
            display: "flex",
            alignItems: "baseline",
            justifyContent: "space-between",
          },
        },
        h(
          "div",
          null,
          h("div", { className: "eyebrow" }, "Total"),
          h(
            "div",
            { style: { fontSize: 13, color: "var(--fg-muted)", marginTop: 6 } },
            project.feeBasis,
          ),
        ),
        h(Money, { amount: project.fee, size: "big" }),
      ),
    );
  }

  // ---------------- Payment (merged Quote + Pay) ----------------
  function ScreenPay({ project, tweaks }) {
    const [phase, setPhase] = useState("idle");
    const [errMsg, setErrMsg] = useState("");
    const fee = project.fee;
    const quoteMissing = !fee;
    const style = (tweaks && tweaks.quoteStyle) || "reveal";

    const startCheckout = async () => {
      setPhase("loading");
      setErrMsg("");
      try {
        const data = await window.API.projects.checkout(project.id);
        if (!data.checkout_url) throw new Error("No checkout URL returned");
        setPhase("redirecting");
        window.location.href = data.checkout_url;
      } catch (e) {
        setErrMsg(e.message || "Failed to start payment");
        setPhase("error");
      }
    };

    const busy = phase === "loading" || phase === "redirecting";

    if (phase === "redirecting") {
      return h(
        "div",
        {
          className: "fade-in",
          style: { textAlign: "center", padding: "60px 0" },
        },
        h("span", {
          className: "spin",
          style: {
            width: 32,
            height: 32,
            borderWidth: 3,
            borderTopColor: "var(--accent)",
            borderColor: "var(--border)",
            display: "inline-block",
            marginBottom: 20,
          },
        }),
        h("h2", null, "Redirecting to Stripe…"),
        h(
          "p",
          { style: { color: "var(--fg-muted)" } },
          "You will be returned here after payment.",
        ),
      );
    }

    const orderCard = h(
      Card,
      { tight: true, eyebrow: "Order" },
      h(
        KV,
        { k: (project.feeBasis || "").split("·")[0].trim() || "Fee" },
        fmt(fee),
      ),
      h(KV, { k: "Combinations" }, project.combosCount || 1),
      h("div", { className: "divider" }),
      h(
        "div",
        {
          style: {
            display: "flex",
            justifyContent: "space-between",
            alignItems: "baseline",
            marginBottom: 8,
          },
        },
        h("span", { style: { fontWeight: 650 } }, "Total"),
        h(Money, { amount: fee }),
      ),
      h(
        "div",
        {
          style: {
            fontSize: 12,
            color: "var(--fg-muted)",
            display: "flex",
            gap: 6,
            alignItems: "center",
            marginTop: 4,
          },
        },
        h(Icon, { name: "shield-check", size: 13 }),
        "Secured by Stripe",
      ),
      errMsg &&
        h(
          "div",
          {
            style: {
              marginTop: 8,
              color: "var(--signal-danger)",
              fontSize: 12,
            },
          },
          errMsg,
        ),
    );

    return h(
      "div",
      { className: "fade-in" },
      h("div", { className: "eyebrow screen-eyebrow" }, "Payment"),
      h("h1", { className: "screen-title" }, "Your price"),
      h(
        "p",
        { className: "screen-sub" },
        "We have determined this project-specific price. After payment you'll be brought back here automatically.",
      ),
      h(
        "div",
        { className: "grid-2-1" },
        h(
          "div",
          null,
          quoteMissing &&
            h(
              Callout,
              { kind: "danger", title: "Quote unavailable" },
              "This project needs to be classified again before it can be quoted.",
            ),
          !quoteMissing && style === "reveal" && h(QuoteReveal, { project }),
          !quoteMissing &&
            style === "breakdown" &&
            h(QuoteBreakdown, { project }),
          !quoteMissing && style === "minimal" && h(QuoteMinimal, { project }),
        ),
        orderCard,
      ),
      h(
        "div",
        { className: "btn-row end", style: { marginTop: 24 } },
        h(
          Btn,
          {
            kind: "primary",
            icon: busy ? null : "credit-card",
            onClick: startCheckout,
            disabled: busy || quoteMissing,
          },
          busy
            ? h(
                React.Fragment,
                null,
                h("span", {
                  className: "spin",
                  style: {
                    borderTopColor: "#fff",
                    borderColor: "rgba(255,255,255,.4)",
                  },
                }),
                "Loading…",
              )
            : "Pay with Stripe · " + fmt(fee),
        ),
      ),
    );
  }

  function fmt(n) {
    return n == null ? "—" : "$" + n.toFixed(2);
  }
  function engineerTypes(project) {
    const seen = {};
    (project.calcUnits || project.calc_units || []).forEach((u) => {
      const t = String(u.engineerType || u.engineer_type || "")
        .trim()
        .toUpperCase();
      if (t) seen[t] = true;
    });
    return Object.keys(seen).sort();
  }
  function stateName(s) {
    const m = { CT: "Connecticut", MA: "Massachusetts", NJ: "New Jersey" };
    return m[s] || s;
  }

  Object.assign(window, {
    ScreenEligibility,
    ScreenUpload,
    ScreenClassify,
    ScreenQuote,
    ScreenPay,
  });
})();
