// EzClaim — flow screens: Lookup, Results, Choice, Wizard, Review.

const { lookup: ezLookup, fmtUSD, fmtDate, PROPERTIES, homesteadStatus, buildFutureProtection } = window.EZ_MOCK;

// ─────────────────────────────────────────────────────────────────────────────
// Lookup
// ─────────────────────────────────────────────────────────────────────────────
function LookupScreen({ nav, setSession }) {
  const [q, setQ] = useState("");
  const [loading, setLoading] = useState(false);
  const [suggestions, setSuggestions] = useState([]);
  const [suggestLoading, setSuggestLoading] = useState(false);
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [activeSuggestionIndex, setActiveSuggestionIndex] = useState(-1);
  const [selectedSuggestion, setSelectedSuggestion] = useState(null);
  const [err, setErr] = useState("");
  const suggestCache = useRef(new Map());

  useEffect(() => {
    const query = q.trim();
    if (selectedSuggestion?.site_address === query) return;
    if (query.length < 4) {
      setSuggestions([]);
      setActiveSuggestionIndex(-1);
      setSuggestLoading(false);
      return;
    }

    let cancelled = false;
    const controller = new AbortController();
    const timer = setTimeout(async () => {
      const normalizedKey = query.toUpperCase().replace(/[^A-Z0-9]+/g, " ").trim();
      const cached = suggestCache.current.get(normalizedKey);
      if (cached) {
        if (!cancelled) {
          setSuggestions(cached);
          setActiveSuggestionIndex(cached.length ? 0 : -1);
          setShowSuggestions(Boolean(cached.length));
        }
        return;
      }

      const fallbackItems = window.EZ_MOCK?.suggestMockMatches ? window.EZ_MOCK.suggestMockMatches(query) : [];
      if (fallbackItems.length) {
        setSuggestions(fallbackItems);
        setActiveSuggestionIndex(0);
        setShowSuggestions(true);
      }
      setSuggestLoading(true);
      try {
        const resp = await fetch("/api/suggest?query=" + encodeURIComponent(query), { signal: controller.signal });
        const data = resp.ok ? await resp.json() : { items: [] };
        if (cancelled) return;
        let items = Array.isArray(data.items) ? data.items : [];
        if (!items.length) items = fallbackItems;
        suggestCache.current.set(normalizedKey, items);
        setSuggestions(items);
        setActiveSuggestionIndex(items.length ? 0 : -1);
        setShowSuggestions(true);
      } catch (e) {
        if (!cancelled && e.name !== "AbortError") {
          setSuggestions(fallbackItems);
          setActiveSuggestionIndex(fallbackItems.length ? 0 : -1);
          setShowSuggestions(Boolean(fallbackItems.length));
        }
      } finally {
        if (!cancelled) setSuggestLoading(false);
      }
    }, 220);

    return () => {
      cancelled = true;
      controller.abort();
      clearTimeout(timer);
    };
  }, [q, selectedSuggestion?.site_address]);

  function selectSuggestion(item) {
    setQ(item.site_address);
    setSelectedSuggestion(item);
    setSuggestions([]);
    setShowSuggestions(false);
    setActiveSuggestionIndex(-1);
    setErr("");
  }

  function onAddressKeyDown(e) {
    if (!showSuggestions || !suggestions.length) return;
    if (e.key === "ArrowDown") {
      e.preventDefault();
      setActiveSuggestionIndex((idx) => Math.min(idx + 1, suggestions.length - 1));
    } else if (e.key === "ArrowUp") {
      e.preventDefault();
      setActiveSuggestionIndex((idx) => Math.max(idx - 1, 0));
    } else if (e.key === "Enter" && activeSuggestionIndex >= 0) {
      e.preventDefault();
      selectSuggestion(suggestions[activeSuggestionIndex]);
    } else if (e.key === "Escape") {
      setShowSuggestions(false);
      setActiveSuggestionIndex(-1);
    }
  }

  async function runLookup(address, lookupOptions = {}) {
    if (!address.trim()) { setErr("Enter a Maryland street address."); return; }
    setErr(""); setLoading(true);
    const demoMode = typeof window !== "undefined" && /[?&]demo=1\b/.test(window.location.search);
    const res = await ezLookup(address, { ...lookupOptions, allowMockFallback: demoMode });
    setLoading(false);
    setSession({ query: address, lookup: res });
    if (res.state === "ambiguous") nav("/disambiguate");
    else if (res.state === "none") nav("/no-match");
    else nav("/results");
  }

  async function go(e) {
    e?.preventDefault();
    const resolvedAddress = selectedSuggestion?.site_address === q.trim() ? selectedSuggestion.site_address : q;
    await runLookup(resolvedAddress);
  }

  return (
    <div className="fade-in">
      <div className="hero">
        <div className="eyebrow">
          <span className="pulse-dot" style={{ color: "var(--good-600)" }}/>
          Maryland Homestead Tax Credit · 2026 filing window
        </div>
        <h1 className="h-display">File for the Homestead Credit on your Maryland home.</h1>
        <p className="lead">
          Look up your property to see whether the Homestead Tax Credit is on file. If it isn't, the State Department of Assessments & Taxation (SDAT) is allowed to raise your taxable assessment without a cap — and you'll pay the difference.
        </p>

        <div className="lookup-card">
          <form onSubmit={go} className="lookup-form">
            <div className="lookup-input-wrap">
              <Input
                size="lg"
                leftIcon={<I.Search size={20}/>}
                placeholder="123 Main St, Baltimore, MD 21201"
                value={q}
                onChange={(e) => {
                  setQ(e.target.value);
                  setSelectedSuggestion(null);
                }}
                onFocus={() => setShowSuggestions(true)}
                onBlur={() => setTimeout(() => setShowSuggestions(false), 120)}
                onKeyDown={onAddressKeyDown}
                aria-label="Property address"
                role="combobox"
                aria-expanded={showSuggestions && suggestions.length > 0}
                aria-controls="address-suggestions"
                aria-autocomplete="list"
                autoComplete="off"
                autoFocus
              />
              {showSuggestions && suggestions.length ? (
                <div id="address-suggestions" className="address-suggestions" role="listbox" aria-label="Address suggestions">
                  {suggestions.map((item, index) => (
                    <button
                      key={item.property_id || item.site_address}
                      type="button"
                      role="option"
                      aria-selected={index === activeSuggestionIndex}
                      className={index === activeSuggestionIndex ? "active" : ""}
                      onMouseDown={(e) => e.preventDefault()}
                      onMouseEnter={() => setActiveSuggestionIndex(index)}
                      onClick={() => selectSuggestion(item)}
                    >
                      <span className="suggestion-address">{item.site_address}</span>
                      {item.secondary ? <span className="suggestion-meta">{item.secondary}</span> : null}
                    </button>
                  ))}
                </div>
              ) : null}
            </div>
            <Button kind="primary" size="lg" type="submit" disabled={loading}>
              {loading ? <><span className="spin"/> Looking up…</> : <>Check my property <I.ArrowRight size={16}/></>}
            </Button>
          </form>
          {suggestLoading ? <div className="lookup-hint"><span className="spin"/> Resolving address…</div> : null}
          {err ? <div className="field-err" style={{ marginTop: 10 }}><I.Alert size={14}/> {err}</div> : null}
        </div>

        <div className="trust">
          <div className="trust-item">
            <span className="ti-ico"><I.Shield size={22}/></span>
            <div>
              <div className="ti-t">Not a government agency</div>
              <div className="ti-d">You can also file free directly with SDAT. We'll show you both options.</div>
            </div>
          </div>
          <div className="trust-item">
            <span className="ti-ico"><I.DollarSign size={22}/></span>
            <div>
              <div className="ti-t">Flat $39.99 if you want help</div>
              <div className="ti-d">We prepare, submit, and track your filing through to SDAT confirmation.</div>
            </div>
          </div>
          <div className="trust-item">
            <span className="ti-ico"><I.Lock size={22}/></span>
            <div>
              <div className="ti-t">Used only for filing</div>
              <div className="ti-d">Used only to prepare, review, and submit your filing. Sensitive filing materials are encrypted for internal review.</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Disambiguate — multiple matches
// ─────────────────────────────────────────────────────────────────────────────
function DisambiguateScreen({ nav, session, setSession }) {
  const matches = session?.lookup?.matches || [];
  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <Button kind="ghost" size="sm" icon={<I.ArrowLeft size={14}/>} onClick={() => nav("/")}>Back</Button>
      <div>
        <h2 className="h-page">We found a few matches</h2>
        <p className="subtle" style={{ marginTop: 6 }}>Pick the unit that's yours. We'll only look up that one parcel.</p>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {matches.map((m) => (
          <button key={m.parcel_id} className="match" onClick={async () => {
            // simulate selecting a unit -> use mock as full property
            const full = { ...PROPERTIES[0], site_address: m.site_address, county: m.county, parcel_id: m.parcel_id };
            setSession({ ...session, lookup: { matches: [full], state: "ok" } });
            nav("/results");
          }}>
            <span className="dot"/>
            <span>
              <div className="addr">{m.site_address}</div>
              <div className="sub">{m.county} County · Parcel {m.parcel_id}</div>
            </span>
            <span className="arrow"><I.ChevronRight/></span>
          </button>
        ))}
      </div>
      <Callout kind="info" icon={<I.Info size={18}/>}>
        Don't see your unit? Search again with the exact street, unit, and ZIP. Condo/co-op parcels are listed individually with SDAT.
      </Callout>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// No match
// ─────────────────────────────────────────────────────────────────────────────
function NoMatchScreen({ nav, session }) {
  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 20 }}>
      <Button kind="ghost" size="sm" icon={<I.ArrowLeft size={14}/>} onClick={() => nav("/")}>Back to lookup</Button>
      <div className="card lg">
        <div style={{ display: "flex", gap: 14, alignItems: "flex-start" }}>
          <div style={{ width: 44, height: 44, borderRadius: 12, background: "var(--warn-100)", color: "var(--warn-700)", display: "grid", placeItems: "center", flexShrink: 0 }}>
            <I.Search size={22}/>
          </div>
          <div>
            <h2 className="h-page" style={{ fontSize: 22 }}>We couldn't find that address</h2>
            <p className="subtle" style={{ marginTop: 6 }}>
              We searched the SDAT parcel index for <strong style={{ color: "var(--ink-900)" }}>"{session?.query || "—"}"</strong> and didn't find a Maryland residential parcel.
            </p>
          </div>
        </div>
        <div className="card-divider"/>
        <div style={{ display: "flex", flexDirection: "column", gap: 10, fontSize: 14, color: "var(--ink-700)" }}>
          <div>A few things to try:</div>
          <ul style={{ margin: 0, paddingLeft: 18, color: "var(--ink-600)", lineHeight: 1.6 }}>
            <li>Use the full street name (e.g., "Avenue" not "Ave") and include the ZIP.</li>
            <li>For condos and co-ops, search with the unit number — each unit is a separate parcel.</li>
            <li>If you recently moved in, the deed may not be in the public index yet — try again in 4–6 weeks.</li>
          </ul>
        </div>
        <div className="cta-bar">
          <Button kind="primary" onClick={() => nav("/")} block>Try a different address</Button>
        </div>
      </div>
      <Callout kind="info">
        Still stuck? You can file directly with SDAT for free at{" "}
        <a href="https://sdathtc.dat.maryland.gov" target="_blank" rel="noreferrer">sdathtc.dat.maryland.gov</a>.
      </Callout>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Results screen
// ─────────────────────────────────────────────────────────────────────────────
function ResultsScreen({ nav, session, setSession, tweaks }) {
  const p = session?.lookup?.matches?.[0];
  if (!p) {
    return (
      <div className="card">
        <p>No property loaded.</p>
        <Button kind="primary" onClick={() => nav("/")}>Back to lookup</Button>
      </div>
    );
  }
  const lead = p.homestead_lead || {};
  const leadStatus = lead.lead_status;
  const status = homesteadStatus(p);
  const actionable = lead.call_to_action === "file_free_or_assisted";
  const savings = lead.estimated_annual_savings || p.estimated_tax_savings || 0;
  const futureProtection = p.future_protection || buildFutureProtection?.(p);
  const shouldShowProtection = actionable && futureProtection;

  // Optional headline variant override from tweaks
  const fp = futureProtection;
  const forwardHeadline = fp && fp.headline_protection_high > 0
    ? `Next year, your property-tax bill is set to rise. With the Homestead cap on file, that increase is limited — saving you an estimated ${fmtUSD(fp.headline_protection_low)}–${fmtUSD(fp.headline_protection_high)} a year starting with your ${fp.headline_protection_year} reassessment.`
    : null;
  const headlineVariants = {
    rising_assessment_warm: lead.landing_page_headline,
    direct_savings: forwardHeadline || lead.landing_page_headline,
    new_owner_friendly: "New owner? File now for 2027+ tax bill protection.",
    already_protected: "You're already protected.",
  };
  // Forward-looking protection headline wins whenever forward protection exists,
  // so the dev-tweak default (rising_assessment_warm → backward copy) can't override it.
  const headline = forwardHeadline || headlineVariants[tweaks?.headlineVariant] || lead.landing_page_headline || "Your property is eligible for the Homestead Credit.";

  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 20 }}>
      <Button kind="ghost" size="sm" icon={<I.ArrowLeft size={14}/>} onClick={() => nav("/")}>Search a different address</Button>

      <PropertyCard p={p} right={
        <Tag kind={status.kind}>
          {status.icon === "check" ? <I.Check size={11}/> : <span className="dot"/>}
          {status.label}
        </Tag>
      } />

      {leadStatus === "needs_review"
        ? <ConfirmResidenceBlock nav={nav}/>
        : leadStatus === "not_target"
          ? <StatusBlock p={p} status={status} nav={nav}/>
          : shouldShowProtection
            ? <ProtectionBlock p={{ ...p, future_protection: futureProtection }} nav={nav} savings={savings} headline={headline}/>
            : actionable
              ? <EligibleBlock p={p} headline={headline} savings={savings} nav={nav} setSession={setSession} session={session}/>
              : <StatusBlock p={p} status={status} nav={nav}/>}
    </div>
  );
}

function ConfirmResidenceBlock({ nav }) {
  return (
    <>
      <div className="card lg">
        <div className="row gap-3" style={{ alignItems: "flex-start" }}>
          <div style={{ width: 44, height: 44, borderRadius: 12, background: "var(--brand-50)", color: "var(--brand-700)", display: "grid", placeItems: "center", flexShrink: 0 }}>
            <I.Home size={22}/>
          </div>
          <div style={{ flex: 1 }}>
            <div className="eyebrow" style={{ marginBottom: 6 }}>Confirm your residence</div>
            <h2 className="h-page" style={{ fontSize: 22 }}>Confirm this is your primary residence to see your Homestead estimate.</h2>
            <p className="subtle" style={{ marginTop: 8 }}>
              The Homestead Credit is only for an owner's primary residence. Confirm yours and we'll show your estimated savings and prepare your filing.
            </p>
          </div>
        </div>
        <div className="cta-bar" style={{ marginTop: 16 }}>
          <Button kind="primary" iconRight={<I.ArrowRight size={14}/>} onClick={() => nav("/wizard/address")}>Confirm my primary residence</Button>
          <Button kind="secondary" iconRight={<I.ExternalLink size={14}/>} onClick={() => nav("/sdat")}>File free with SDAT</Button>
        </div>
      </div>
      <Callout kind="info">
        <strong>Why we ask:</strong> Public records for this address are ambiguous about owner-occupancy. Confirming primary residence lets us show an accurate Homestead savings estimate and prepare your filing.
      </Callout>
    </>
  );
}

function EligibleBlock({ p, headline, savings, nav, setSession, session }) {
  const hasAssessmentDelta = Number(p.previous_assessed_value) > 0 && Number(p.current_assessed_value) > 0;
  const reassessmentText = hasAssessmentDelta
    ? `Estimate based on ${p.county} rates and your reassessed value (${fmtUSD(p.previous_assessed_value)} -> ${fmtUSD(p.current_assessed_value)}, +${Math.round(((p.current_assessed_value/p.previous_assessed_value)-1)*1000)/10}%).`
    : `Estimate based on public Homestead status, owner-occupancy indicators, and available local tax-rate data for ${p.county || "this jurisdiction"}.`;
  return (
    <>
      <div className="savings">
        <div className="s-label">Estimated Homestead savings</div>
        <div className="s-amount tabnum">{fmtUSD(savings)}<span className="per">/yr</span></div>
        <div className="s-sub" style={{ maxWidth: "60ch" }}>
          With Homestead on file, future assessment increases are capped — saving you an estimated <strong style={{ color: "#fff" }}>{fmtUSD(savings)}/year</strong> once the cap is in effect.
        </div>
        <div className="s-fine">
          {reassessmentText} Filing now is aimed at the 2027 tax bill and later, assuming SDAT approval and eligibility. This is an estimate, <em>not a guarantee</em>.
        </div>
      </div>

      <div>
        <h2 className="h-page" style={{ fontSize: 24, marginBottom: 8 }}>{headline}</h2>
        <p className="subtle" style={{ fontSize: 15 }}>{p.homestead_lead?.landing_page_body}</p>
      </div>

      <div>
        <div className="eyebrow" style={{ marginBottom: 10 }}>Two ways to file</div>
        <div className="choice-grid">
          <div className="choice">
            <div className="row between">
              <div className="choice-title">File free with SDAT</div>
              <Tag>Free</Tag>
            </div>
            <div className="choice-price"><strong>$0</strong> · paper or online</div>
            <ul>
              <li><span className="ck"><I.Check size={14}/></span>Maryland's official online portal</li>
              <li><span className="ck"><I.Check size={14}/></span>You handle the form, ID checks, and tracking</li>
              <li><span className="x"><I.X size={14}/></span>No reminders or status tracking from us</li>
            </ul>
            <Button kind="secondary" iconRight={<I.ExternalLink size={14}/>} onClick={() => nav("/sdat")}>Continue free with SDAT</Button>
          </div>
          <div className="choice recommended">
            <span className="rec-tag">Most homeowners pick this</span>
            <div className="row between">
              <div className="choice-title">EzClaim files it for you</div>
              <Tag kind="info">$39.99 flat</Tag>
            </div>
            <div className="choice-price"><strong>$39.99</strong> · one-time, no subscription</div>
            <ul>
              <li><span className="ck"><I.Check size={14}/></span>~4 minutes of plain-English questions</li>
              <li><span className="ck"><I.Check size={14}/></span>We generate the packet and queue it for internal review before SDAT submission</li>
              <li><span className="ck"><I.Check size={14}/></span>Magic-link status page — no login required</li>
              <li><span className="ck"><I.Check size={14}/></span>Sensitive filing materials encrypted for internal review</li>
            </ul>
            <Button kind="primary" iconRight={<I.ArrowRight size={14}/>} onClick={() => nav("/wizard/address")}>Start my filing — $39.99 later</Button>
          </div>
        </div>
      </div>

      <Callout kind="warn">
        <strong>Heads-up:</strong> This is an estimate based on public data. Final savings depend on your county's tax rate, your assessment trajectory, and SDAT's determination of your Homestead eligibility. We can't guarantee approval.
      </Callout>
    </>
  );
}

function MoneyLine({ low, high }) {
  const safeLow = Math.max(0, Math.round(Number(low) || 0));
  const safeHigh = Math.max(0, Math.round(Number(high) || 0));
  const isRange = safeLow !== safeHigh;
  const renderedText = isRange
    ? `${fmtUSD(safeLow)} - ${fmtUSD(safeHigh)} /yr`
    : `${fmtUSD(safeLow)} /yr`;
  const densityClass = renderedText.length >= 22 ? " is-xl" : renderedText.length >= 18 ? " is-long" : "";

  return (
    <span className={"fp-money-line" + densityClass}>
      <span className="fp-money-amount">{fmtUSD(safeLow)}</span>
      {isRange ? (
        <>
          <span className="sep">-</span>
          <span className="fp-money-amount">{fmtUSD(safeHigh)}</span>
        </>
      ) : null}
      <span className="per"> /yr</span>
    </span>
  );
}

const TAX_BILL_LABELS = {
  2027: "2027 Tax Bill",
  2028: "2028 Tax Bill",
  2029: "2029 Tax Bill",
};

function taxBillRowsForProtection(fp) {
  const directRows = Array.isArray(fp?.tax_bill_protection) ? fp.tax_bill_protection : [];
  if (directRows.length) {
    return directRows.map((row) => ({
      year: Number(row.year),
      label: row.label || `${row.year} Tax Bill`,
      phase: row.phase || "future",
      low: Math.max(0, Math.round(Number(row.low ?? row.moderate) || 0)),
      high: Math.max(0, Math.round(Number(row.high ?? row.high_growth ?? row.moderate) || 0)),
    }));
  }
  const fallbackRows = Array.isArray(fp?.year_detail) ? fp.year_detail : [];
  return fallbackRows
    .filter((row) => Number(row.year) >= 2027 && Number(row.year) <= 2029)
    .map((row) => {
      const low = Math.max(0, Math.round(Number(row.moderate) || 0));
      const high = Math.max(0, Math.round(Number(row.high) || low));
      return {
        year: Number(row.year),
        label: TAX_BILL_LABELS[Number(row.year)] || `${row.year} Tax Bill`,
        phase: row.phase || "future",
        low: Math.min(low, high),
        high: Math.max(low, high),
      };
    });
}

function leadTaxBillRow(rows) {
  return rows.find((row) => row.high > 0) || rows[0] || { year: 2027, label: "2027 Tax Bill", low: 0, high: 0 };
}

function ProtectionBlock({ p, nav, savings, headline }) {
  const fp = p.future_protection;
  const thisYear = new Date().getFullYear();
  const reassessYear = Number(fp.next_reassessment_year || String(fp.next_reassessment || "").match(/\d{4}/)?.[0] || thisYear + 2);
  const yearsUntil = Math.max(1, reassessYear - thisYear);
  const billRows = taxBillRowsForProtection(fp);
  const missed2026Savings = Math.max(0, Math.round(Number(fp.missed_2026_savings ?? fp.current_year_savings ?? savings ?? 0) || 0));
  const hasMissed2026Savings = missed2026Savings > 0;
  const totalModerate = billRows.reduce((sum, row) => sum + (Number(row.low) || 0), 0);
  const totalHigh = billRows.reduce((sum, row) => sum + (Number(row.high) || 0), 0);
  const fee = fmtUSD(39.99, { cents: true });
  const title = headline || "Your Homestead protection is not approved.";

  return (
    <>
      <div className="fp-decision">
        <div className="fp-decision-ico"><I.Lock size={18}/></div>
        <div>
          <div className="eyebrow" style={{ color: "var(--accent-700)" }}>Decision needed</div>
          <h2 className="h-page" style={{ fontSize: 22, marginTop: 4 }}>{title}</h2>
          <p className="subtle" style={{ marginTop: 6, fontSize: 14.5 }}>
            {fp && fp.headline_protection_high > 0 ? (
              <>
                Homestead isn't on file for this home. Filing now caps your future taxable-assessment increases — an estimated{" "}
                <strong style={{ color: "var(--ink-900)" }}>{fmtUSD(fp.headline_protection_low)}–{fmtUSD(fp.headline_protection_high)}/year</strong>{" "}
                starting with your <strong style={{ color: "var(--ink-900)" }}>{fp.headline_protection_year} reassessment</strong>.
              </>
            ) : (
              <>
                Public records do not show Homestead on file for this home. Filing now can protect your <strong style={{ color: "var(--ink-900)" }}>future tax bills</strong> once the cap is in effect.
              </>
            )}
          </p>
        </div>
      </div>

      <div className="fp-hero">
        <div className="fp-stat fp-stat-lead">
          <div className="fp-stat-k">Estimated Homestead protection</div>
          <div className="fp-stat-v fp-stat-range tabnum">
            <MoneyLine low={fp.headline_protection_low} high={fp.headline_protection_high}/>
          </div>
          <div className="fp-stat-d">
            Starting with your {fp.headline_protection_year} reassessment, modeled — not guaranteed.
          </div>
        </div>
      </div>

      <div className="fp-why">
        <h3 className="h-section" style={{ marginBottom: 6 }}>Why file now?</h3>
        <p style={{ margin: 0, color: "var(--ink-700)", fontSize: 15, lineHeight: 1.6 }}>
          {fp && fp.headline_protection_high > 0 ? (
            <>
              Filing now puts Homestead on file so your future taxable-assessment increases are capped. Based on this property's assessment trajectory, that's an estimated{" "}
              <strong>{fmtUSD(fp.headline_protection_low)}–{fmtUSD(fp.headline_protection_high)}/year</strong> starting with the <strong>{fp.headline_protection_year} reassessment</strong>.
            </>
          ) : (
            <>
              The next reassessment is expected in <strong>{fp.next_reassessment}</strong>, about {yearsUntil} year{yearsUntil > 1 ? "s" : ""} from now.
              Filing before then can protect your future tax bills by keeping the <strong>{fp.cap_rate_pct}% per year</strong> Homestead cap on file if SDAT approves eligibility.
            </>
          )}
        </p>
      </div>

      <div className="fp-year-summary" aria-label="Future tax bill protection by year">
        {billRows.map(row => (
          <div className={"fp-year-summary-item " + (row.phase === "future" ? "is-future" : "is-carried")} key={`summary-${row.year}-${row.phase}`}>
            <div className="fp-year-summary-label">{row.label}</div>
            <div className="fp-year-summary-amount tabnum">
              <MoneyLine low={row.low} high={row.high}/>
            </div>
            <div className="fp-year-summary-phase">
              Modeled reassessment range
            </div>
          </div>
        ))}
      </div>

      <details className="fp-disclosure">
        <summary>
          <span className="fp-d-label">
            <I.Info size={15}/> See how we estimated this {totalModerate > 0 ? `(${fmtUSD(totalModerate)} estimated future protection)` : ""}
          </span>
          <span className="fp-d-chev"><I.ChevronRight size={16}/></span>
        </summary>

        <div className="fp-d-body">
          <p className="fp-d-meth">
            We model future tax bill protection by applying the Homestead cap to the next reassessment window and showing the potential yearly bill impact if SDAT approves eligibility.
            {" "}A single number means the low and high estimates match; a range means the future reassessment is uncertain.
          </p>

          <div className="fp-table-wrap">
            <table className="fp-table">
              <thead>
                <tr>
                  <th>Tax bill</th>
                  <th>Low estimate<span className="th-sub">moderate reassessment scenario</span></th>
                  <th>High estimate<span className="th-sub">high-growth reassessment scenario</span></th>
                </tr>
              </thead>
              <tbody>
                {billRows.map(row => (
                  <tr key={`${row.year}-${row.phase}`} className={"phase-" + row.phase}>
                    <td className="yr">
                      <div className="yr-n">{row.year}</div>
                      <div className="yr-l">{row.label}</div>
                    </td>
                    <td className="tabnum">{row.low ? fmtUSD(row.low) : <span className="zero">$0</span>}<span className="td-sub">low</span></td>
                    <td className="tabnum">{row.high ? fmtUSD(row.high) : <span className="zero">$0</span>}<span className="td-sub">high</span></td>
                  </tr>
                ))}
              </tbody>
              <tfoot>
                <tr>
                  <td className="yr"><div className="yr-n">{billRows.length}-yr total</div></td>
                  <td className="tabnum strong">{fmtUSD(totalModerate)}</td>
                  <td className="tabnum strong">{fmtUSD(totalHigh)}</td>
                </tr>
              </tfoot>
            </table>
          </div>

          <div className="fp-year-cards" aria-hidden="true">
            {billRows.map(row => (
              <div className={"fp-yc " + (row.phase === "future" ? "fp-yc-future" : "fp-yc-current")} key={`${row.year}-${row.phase}`}>
                <div className="fp-yc-yr">
                  <span className="fp-yc-yr-n">{row.year}</span>
                  <span className="fp-yc-yr-l">{row.label}</span>
                  <span className="fp-yc-phase is-future">
                    modeled range
                  </span>
                </div>
                <div className="fp-yc-cols">
                  <div>
                    <div className="fp-yc-k">Low</div>
                    <div className="fp-yc-v tabnum">{row.low ? fmtUSD(row.low) : <span className="fp-yc-zero">$0</span>}</div>
                  </div>
                  <div>
                    <div className="fp-yc-k">High</div>
                    <div className="fp-yc-v tabnum">{row.high ? fmtUSD(row.high) : <span className="fp-yc-zero">$0</span>}</div>
                  </div>
                </div>
              </div>
            ))}
            <div className="fp-yc fp-yc-total">
              <div className="fp-yc-yr"><span className="fp-yc-yr-n">{billRows.length}-yr total</span></div>
              <div className="fp-yc-cols">
                <div>
                  <div className="fp-yc-k">Low</div>
                  <div className="fp-yc-v tabnum strong">{fmtUSD(totalModerate)}</div>
                </div>
                <div>
                  <div className="fp-yc-k">High</div>
                  <div className="fp-yc-v tabnum strong">{fmtUSD(totalHigh)}</div>
                </div>
              </div>
            </div>
          </div>

          <p className="fp-d-foot">
            Modeled, not guaranteed. Actual reassessments depend on market conditions, your neighborhood, and SDAT's determination.
            The state ceiling is 10%; {p.county} sets its local Homestead cap at {fp.cap_rate_pct}%.
          </p>
        </div>
      </details>

      <div>
        <div className="eyebrow" style={{ marginBottom: 10 }}>Two ways to file</div>
        <div className="choice-grid">
          <div className="choice">
            <div className="row between">
              <div className="choice-title">File free with SDAT</div>
              <Tag>Free</Tag>
            </div>
            <div className="choice-price"><strong>$0</strong> · paper or online</div>
            <ul>
              <li><span className="ck"><I.Check size={14}/></span>Maryland's official online portal</li>
              <li><span className="ck"><I.Check size={14}/></span>You handle the form, ID checks, and tracking</li>
              <li><span className="x"><I.X size={14}/></span>No EzClaim reminders before your {reassessYear} reassessment</li>
            </ul>
            <Button kind="secondary" iconRight={<I.ExternalLink size={14}/>} onClick={() => nav("/sdat")}>Continue free with SDAT</Button>
          </div>
          <div className="choice recommended">
            <span className="rec-tag">{hasMissed2026Savings ? "Most homeowners pick this" : "Recommended before reassessment"}</span>
            <div className="row between">
              <div className="choice-title">EzClaim files it for you</div>
              <Tag kind="info">$39.99 flat</Tag>
            </div>
            <div className="choice-price"><strong>$39.99</strong> · one-time, no subscription</div>
            <ul>
              <li><span className="ck"><I.Check size={14}/></span>~4 minutes of plain-English questions</li>
              <li><span className="ck"><I.Check size={14}/></span>We queue your packet for internal review before SDAT submission</li>
              <li><span className="ck"><I.Check size={14}/></span>Reminder before your {reassessYear} reassessment</li>
              <li><span className="ck"><I.Check size={14}/></span>Sensitive filing materials encrypted for internal review</li>
            </ul>
            <Button kind="primary" iconRight={<I.ArrowRight size={14}/>} onClick={() => nav("/wizard/address")}>
              {hasMissed2026Savings ? `Start my filing — ${fee}` : `Lock in my protection — ${fee}`}
            </Button>
          </div>
        </div>
      </div>

      <Callout kind="">
        <strong style={{ color: "var(--ink-900)" }}>Why we show a range:</strong>{" "}
        We do not know exactly what your future reassessment will be. The range keeps the promise honest while showing the yearly tax-bill impact if Homestead is approved and on file.
      </Callout>
    </>
  );
}

function StatusBlock({ p, status, nav }) {
  const approved = status.code === "approved";
  const pending = status.code === "pending" || status.code === "application_received";
  const title = approved ? "You're already protected" : pending ? "SDAT already has an application in progress" : "This record needs review";
  const body = approved
    ? `Your Homestead Credit is on file with SDAT${p.homestead_application_status ? ` (${p.homestead_application_status})` : ""}. Maryland caps your taxable assessment increase at 10%/year for as long as this remains your primary residence.`
    : pending
      ? "Public records show SDAT has received or is processing a Homestead application for this property. We would not start a duplicate filing unless SDAT says a corrected refiling is needed."
      : "Public records do not classify this parcel as a clean filing target. Review the SDAT record before deciding whether to file.";

  return (
    <>
      <div className="card lg">
        <div className="row gap-3" style={{ alignItems: "flex-start" }}>
          <div style={{ width: 44, height: 44, borderRadius: 12, background: approved ? "var(--good-100)" : "var(--brand-50)", color: approved ? "var(--good-700)" : "var(--brand-700)", display: "grid", placeItems: "center", flexShrink: 0 }}>
            {approved ? <I.CheckCircle size={22}/> : <I.Info size={22}/>}
          </div>
          <div style={{ flex: 1 }}>
            <h2 className="h-page" style={{ fontSize: 22 }}>{title}</h2>
            <p className="subtle" style={{ marginTop: 6 }}>
              {body} <strong style={{ color: "var(--ink-900)" }}>{approved || pending ? "No EzClaim filing is needed right now." : "Manual review recommended."}</strong>
            </p>
          </div>
        </div>
        <div className="card-divider"/>
        <div className="rcpt">
          <div className="row"><span className="k">Status</span><span className="v" style={{ marginLeft: "auto" }}><Tag kind={status.kind}>{status.icon === "check" ? <I.Check size={11}/> : <span className="dot"/>} {status.label}</Tag></span></div>
          <div className="row"><span className="k">Public record</span><span className="v" style={{ marginLeft: "auto" }}>{p.homestead_application_status || "Review"}</span></div>
          <div className="row"><span className="k">Parcel</span><span className="v" style={{ marginLeft: "auto" }}>{p.parcel_id}</span></div>
          <div className="row"><span className="k">Cap rate</span><span className="v" style={{ marginLeft: "auto" }}>10% / yr · {p.county}</span></div>
        </div>
      </div>
      <Callout kind="info">
        <strong>If you move,</strong> you'll need to refile for your new primary residence. The credit doesn't transfer between properties.
      </Callout>
      <div className="cta-bar">
        <Button kind="secondary" onClick={() => nav("/")} block>Check another address</Button>
        <Button kind="ghost" onClick={() => window.open("https://sdat.dat.maryland.gov", "_blank")} block iconRight={<I.ExternalLink size={14}/>}>View on SDAT</Button>
      </div>
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// SDAT free-path explainer
// ─────────────────────────────────────────────────────────────────────────────
function SdatScreen({ nav }) {
  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 20 }}>
      <Button kind="ghost" size="sm" icon={<I.ArrowLeft size={14}/>} onClick={() => nav("/results")}>Back</Button>
      <div>
        <h2 className="h-page">Filing free with SDAT</h2>
        <p className="subtle" style={{ marginTop: 6 }}>
          You can file free through Maryland OneStop or by paper. EzClaim prepares the packet for review before any SDAT submission step.
        </p>
      </div>
      <div className="card">
        <h3 className="h-section">What you'll need</h3>
        <ul style={{ margin: "8px 0 0", paddingLeft: 18, color: "var(--ink-700)", lineHeight: 1.7, fontSize: 14.5 }}>
          <li>A Maryland OneStop login for online filing, or the paper application if you prefer mail/fax</li>
          <li>Social Security Number(s) for all owners on the deed</li>
          <li>The property's parcel ID and address</li>
          <li>About 15–25 minutes</li>
        </ul>
      </div>
      <Callout kind="info">
        The SDAT online portal is the same form we use to prepare your packet — there's nothing we can do that you can't do yourself. The $39.99 buys handling and tracking, not access.
      </Callout>
      <div className="cta-bar">
        <Button kind="primary" iconRight={<I.ExternalLink size={14}/>} block onClick={() => window.open("https://sdathtc.dat.maryland.gov", "_blank")}>Open the SDAT portal</Button>
        <Button kind="secondary" block onClick={() => nav("/results")}>Maybe later</Button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Wizard
// ─────────────────────────────────────────────────────────────────────────────
const WIZARD_STEPS = [
  { key: "address",   label: "Confirm property",   short: "Property" },
  { key: "identity",  label: "Your contact info",  short: "You" },
  { key: "residency", label: "Primary residence",  short: "Residency" },
  { key: "owners",    label: "Other owners",       short: "Co-owners" },
  { key: "eligibility", label: "Eligibility",      short: "Eligibility" },
];

function WizardScreen({ nav, segs, session, setSession }) {
  const stepKey = segs[1] || "address";
  const idx = WIZARD_STEPS.findIndex(s => s.key === stepKey);
  const step = idx >= 0 ? idx : 0;
  const cur = WIZARD_STEPS[step];

  const filing = session.filing || {};
  function updateFiling(patch) {
    const nx = { ...filing, ...patch };
    setSession({ ...session, filing: nx });
  }
  function goNext() {
    if (step < WIZARD_STEPS.length - 1) nav("/wizard/" + WIZARD_STEPS[step + 1].key);
    else nav("/review");
  }
  function goPrev() {
    if (step > 0) nav("/wizard/" + WIZARD_STEPS[step - 1].key);
    else nav("/results");
  }

  return (
    <div className="fade-in">
      <WizardBar step={step + 1} total={WIZARD_STEPS.length} label={cur.label}/>
      <h2 className="h-page" style={{ marginTop: 6, marginBottom: 6 }}>{cur.label}</h2>
      <p className="subtle" style={{ marginBottom: 18 }}>{stepBlurb(stepKey)}</p>

      <div className="card lg">
        {stepKey === "address" && <StepAddress session={session} filing={filing} update={updateFiling}/>}
        {stepKey === "identity" && <StepIdentity filing={filing} update={updateFiling}/>}
        {stepKey === "residency" && <StepResidency filing={filing} update={updateFiling}/>}
        {stepKey === "owners" && <StepOwners filing={filing} update={updateFiling}/>}
        {stepKey === "eligibility" && <StepEligibility filing={filing} update={updateFiling}/>}
      </div>

      <Callout kind="" icon={<I.Lock size={16}/>} >
        We collect non-sensitive info now. <strong style={{ color: "var(--ink-900)" }}>SSNs and signatures are collected after payment.</strong> Used only to prepare, review, and submit your filing. Sensitive filing materials are encrypted for internal review.
      </Callout>

      <div className="cta-bar sticky">
        <Button kind="secondary" onClick={goPrev} icon={<I.ArrowLeft size={14}/>}>Back</Button>
        <Button kind="primary" onClick={goNext} iconRight={<I.ArrowRight size={14}/>} disabled={!stepValid(stepKey, filing, session)}>
          {step === WIZARD_STEPS.length - 1 ? "Review my filing" : "Continue"}
        </Button>
      </div>
    </div>
  );
}

function stepBlurb(key) {
  switch (key) {
    case "address":   return "Confirm we have the right parcel. We'll prefill from SDAT's public record where we can.";
    case "identity":  return "How should SDAT reach you if there are questions? We'll also email you your status link.";
    case "residency": return "The Homestead Credit only applies to your primary residence. A few quick yes/no questions.";
    case "owners":    return "SDAT needs every homeowner and spouse listed on the application.";
    case "eligibility": return "Final eligibility questions required on the SDAT form.";
    default: return "";
  }
}

function stepValid(key, f, session) {
  if (key === "address") return !!f.confirm_address;
  if (key === "identity") return !!(f.full_name && f.email && f.phone);
  if (key === "residency") return !!(f.is_primary && f.move_in_date);
  if (key === "owners") {
    const owners = Array.isArray(f.owners) ? f.owners : [];
    return owners.length >= 1 && owners.length <= 4 && owners.every((owner) => owner.name && owner.dob && owner.role);
  }
  if (key === "eligibility") {
    return f.no_rental === true
      && f.owner_occupant === true
      && f.tax_credit_acks === true
      && ["yes", "no", "not_applicable"].includes(f.tax_return_address)
      && ["yes", "no", "not_applicable"].includes(f.license_address)
      && ["yes", "no", "not_applicable"].includes(f.voter_registration_address);
  }
  return true;
}

// Step components ────────────────────────────────────────────────
function StepAddress({ session, filing, update }) {
  const p = session?.lookup?.matches?.[0];
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <PropertyCard p={p}/>
      <Field label="Is this the property you want to file for?">
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          <RadioCard
            selected={filing.confirm_address === "yes"}
            onClick={() => update({ confirm_address: "yes" })}
            title="Yes, this is my home"
            sub="Use this parcel for the filing."
          />
          <RadioCard
            selected={filing.confirm_address === "different_unit"}
            onClick={() => update({ confirm_address: "different_unit" })}
            title="It's the right building, but a different unit"
            sub="We'll let you pick the right parcel."
          />
        </div>
      </Field>
    </div>
  );
}

function StepIdentity({ filing, update }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <Field label="Your full legal name" help="Exactly as it appears on the deed.">
        <Input aria-label="Your full legal name" value={filing.full_name || ""} onChange={(e) => update({ full_name: e.target.value })} placeholder="Renee A. Whitlock"/>
      </Field>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        <Field label="Email" help="We'll send your status link here.">
          <Input aria-label="Email" type="email" leftIcon={<I.Mail size={16}/>} value={filing.email || ""} onChange={(e) => update({ email: e.target.value })} placeholder="you@example.com"/>
        </Field>
        <Field label="Phone" hint="optional">
          <Input aria-label="Phone" type="tel" leftIcon={<I.Phone size={16}/>} value={filing.phone || ""} onChange={(e) => update({ phone: e.target.value })} placeholder="(410) 555-0142"/>
        </Field>
      </div>
      <Field label="Mailing address" hint="if different from the property">
        <Input aria-label="Mailing address" value={filing.mailing_address || ""} onChange={(e) => update({ mailing_address: e.target.value })} placeholder="Same as property address"/>
      </Field>
    </div>
  );
}

function StepResidency({ filing, update }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <Field label="Is this your primary residence?">
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          <RadioCard selected={filing.is_primary === "yes"} onClick={() => update({ is_primary: "yes" })}
            title="Yes — I live here as my primary home"
            sub="It's where I sleep, get mail, register to vote, and file state taxes."/>
          <RadioCard selected={filing.is_primary === "no"} onClick={() => update({ is_primary: "no" })}
            title="No — I rent it out or it's a second home"
            sub="The Homestead Credit isn't available for rentals or vacation properties."/>
        </div>
      </Field>

      {filing.is_primary === "no" ? (
        <Callout kind="warn"><strong>You can't file for this property.</strong> The Homestead Credit applies only to your primary residence. If your primary home changes, come back and file for that address.</Callout>
      ) : null}

      <Field label="Move-in date" help="The date this became your primary residence.">
        <Input aria-label="Move-in date" type="date" value={filing.move_in_date || ""} onChange={(e) => update({ move_in_date: e.target.value })}/>
      </Field>

      <Field label="Are you registered to vote at this address?" hint="recommended">
        <div className="chips">
          {["Yes","No","Prefer not to say"].map(o => (
            <button key={o} className="chip" aria-pressed={filing.voter_reg === o} onClick={() => update({ voter_reg: o })}>{o}</button>
          ))}
        </div>
      </Field>
    </div>
  );
}

function StepOwners({ filing, update }) {
  const owners = normalizeOwnersWithIds(
    filing.owners?.length ? filing.owners : [{ name: filing.full_name || "", dob: filing.dob || "", role: "homeowner" }]
  );
  useEffect(() => {
    if (!filing.owners?.length || filing.owners.some((owner) => !owner.owner_id)) {
      update({ owners });
    }
  }, [filing.owners]);

  function setOwner(index, patch) {
    update({ owners: owners.map((owner, i) => i === index ? { ...owner, ...patch } : owner) });
  }
  function addOwner() {
    update({ owners: owners.length >= 4 ? owners : [...owners, { owner_id: makeOwnerId(), name: "", dob: "", role: "coowner" }] });
  }
  function removeOwner(index) {
    update({ owners: owners.filter((_, i) => i !== index) });
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <Callout kind="info">
        SDAT requires every homeowner and spouse on the application, even if a spouse is not listed on the deed.
      </Callout>
      {owners.slice(0, 4).map((owner, index) => (
        <div className="card sm" key={owner.owner_id}>
          <Field label={index === 0 ? "Primary homeowner legal name" : "Owner or spouse legal name"}>
            <Input aria-label={`Owner ${index + 1} name`} value={owner.name || ""} onChange={(e) => setOwner(index, { name: e.target.value })}/>
          </Field>
          <Field label="Date of birth">
            <Input aria-label={`Owner ${index + 1} date of birth`} type="date" value={owner.dob || ""} onChange={(e) => setOwner(index, { dob: e.target.value })}/>
          </Field>
          <Field label="Role">
            <div className="chips">
              {["homeowner", "spouse", "coowner"].map((role) => (
                <button key={role} className="chip" aria-pressed={owner.role === role} onClick={() => setOwner(index, { role })}>
                  {role === "homeowner" ? "Homeowner" : role === "spouse" ? "Spouse" : "Co-owner"}
                </button>
              ))}
            </div>
          </Field>
          {index > 0 ? <Button kind="link" size="sm" onClick={() => removeOwner(index)}>Remove</Button> : null}
        </div>
      ))}
      <Button kind="secondary" onClick={addOwner} disabled={owners.length >= 4}>Add owner or spouse</Button>
      {owners.length >= 4 ? <p className="subtle">The SDAT form supports four rows. If there are more than four owners, contact us and we will route the filing to support.</p> : null}
    </div>
  );
}

function normalizeOwnersWithIds(owners) {
  return owners.map((owner) => ({
    ...owner,
    owner_id: owner.owner_id || makeOwnerId(),
  }));
}

function makeOwnerId() {
  if (window.crypto?.randomUUID) return window.crypto.randomUUID();
  return `owner_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
}

function StepEligibility({ filing, update }) {
  function setOwnerOccupant(v) {
    update({ owner_occupant: v, principal_residence: v ? "yes" : "no" });
  }
  function setNoRental(v) {
    update({ no_rental: v, portion_rented: v ? "no" : "yes" });
  }
  function setAcknowledgement(v) {
    update({
      tax_credit_acks: v,
      acknowledgements: { not_government: v, sdat_decides: v },
    });
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
      <Checkbox
        checked={!!filing.owner_occupant}
        onChange={setOwnerOccupant}
        label="I occupy this property as my principal residence."
        sub="I live here for more than 6 months of the calendar year."
      />
      <Checkbox
        checked={!!filing.no_rental}
        onChange={setNoRental}
        label="No portion of this property is rented or used for business."
        sub="If a portion is rented, we can still help — but the credit is prorated."
      />
      <Checkbox
        checked={!!filing.no_other_homestead}
        onChange={(v) => update({ no_other_homestead: v })}
        label="I do not have a Homestead Credit on any other property."
        sub="In Maryland or any other state."
      />
      <div className="card-divider"/>
      <ExactAnswerField
        label="Is the property address shown on your Maryland income tax return?"
        value={filing.tax_return_address}
        onChange={(tax_return_address) => update({ tax_return_address })}
      />
      <ExactAnswerField
        label="Is the property address shown on your driver's license or state ID?"
        value={filing.license_address}
        onChange={(license_address) => update({ license_address })}
      />
      <ExactAnswerField
        label="Is the property address shown on your voter registration?"
        value={filing.voter_registration_address}
        onChange={(voter_registration_address) => update({ voter_registration_address })}
      />
      <div className="card-divider"/>
      <Checkbox
        checked={!!filing.tax_credit_acks}
        onChange={setAcknowledgement}
        label="I understand EzClaim is not a government agency, the $39.99 is a service fee, and approval is determined by SDAT."
        sub="You can also file free directly with SDAT at sdathtc.dat.maryland.gov."
      />
    </div>
  );
}

function ExactAnswerField({ label, value, onChange }) {
  return (
    <Field label={label}>
      <div className="chips">
        {[
          ["yes", "Yes"],
          ["no", "No"],
          ["not_applicable", "Not applicable"],
        ].map(([key, text]) => (
          <button key={key} className="chip" aria-pressed={value === key} onClick={() => onChange(key)}>{text}</button>
        ))}
      </div>
    </Field>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Review
// ─────────────────────────────────────────────────────────────────────────────
function ReviewScreen({ nav, session, setSession }) {
  const p = session?.lookup?.matches?.[0];
  const f = session?.filing || {};
  const [startingCheckout, setStartingCheckout] = useState(false);
  const [checkoutError, setCheckoutError] = useState("");
  const savings = p?.homestead_lead?.estimated_annual_savings ?? p?.estimated_tax_savings ?? 0;

  async function startCheckout() {
    setStartingCheckout(true);
    setCheckoutError("");
    try {
      const resp = await fetch("/api/filings", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          property_id: p?.property_id,
          parcel_id: p?.parcel_id,
          property: p,
          lookup: session.lookup,
          filing: f,
        }),
      });
      if (!resp.ok) {
        const body = await resp.json().catch(() => ({}));
        const missingOwners = Array.isArray(body.details) && body.details.some((detail) => String(detail).startsWith("owner_") || detail === "owner_required");
        setCheckoutError(missingOwners
          ? "We need to fix a few filing details before checkout. Review the owner information and try again."
          : "We need to fix a few filing details before checkout.");
        setStartingCheckout(false);
        return;
      }
      const draft = await resp.json();
      setSession({
        ...session,
        payment_status: "pending",
        filing_id: draft.filing_id || session.filing_id,
        status_token: draft.status_token || session.status_token,
        status_url: draft.status_url || session.status_url,
        draft_filing: draft,
      });
      const checkoutResp = await fetch("/api/checkout", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ filing_id: draft.filing_id }),
      });
      if (!checkoutResp.ok) {
        setCheckoutError("We could not start secure checkout. Check your connection and try again.");
        setStartingCheckout(false);
        return;
      }
      const checkout = await checkoutResp.json().catch(() => ({}));
      if (checkout.checkout_url) {
        window.location.href = checkout.checkout_url;
        return;
      }
      setStartingCheckout(false);
      nav("/checkout");
    } catch (e) {
      setCheckoutError("We could not create your filing draft. Check your connection and try again.");
      setStartingCheckout(false);
    }
  }

  return (
    <div className="fade-in" style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      <Button kind="ghost" size="sm" icon={<I.ArrowLeft size={14}/>} onClick={() => nav("/wizard/eligibility")}>Back</Button>
      <div>
        <h2 className="h-page">Review your filing</h2>
        <p className="subtle" style={{ marginTop: 6 }}>Here's what we'll prepare. After payment we'll ask for SSN or ITIN and signature — used only to prepare, review, and submit your filing.</p>
      </div>

      <PropertyCard p={p}/>

      {checkoutError ? (
        <Callout kind="err" icon={<I.Alert size={16}/>} title="Checkout paused">
          {checkoutError}
        </Callout>
      ) : null}

      <ReviewBlock title="Your contact info" onEdit={() => nav("/wizard/identity")}>
        <Kv label="Name" v={f.full_name || "—"}/>
        <Kv label="Email" v={f.email || "—"}/>
        <Kv label="Phone" v={f.phone || "—"}/>
        <Kv label="Mailing address" v={f.mailing_address || "Same as property"}/>
      </ReviewBlock>

      <ReviewBlock title="Residency" onEdit={() => nav("/wizard/residency")}>
        <Kv label="Primary residence" v={f.is_primary === "yes" ? "Yes" : "No"}/>
        <Kv label="Move-in date" v={f.move_in_date ? fmtDate(f.move_in_date) : "—"}/>
        <Kv label="Voter registration here" v={f.voter_reg || "—"}/>
      </ReviewBlock>

      <ReviewBlock title="Other owners" onEdit={() => nav("/wizard/owners")}>
        {(f.owners?.length ? f.owners : [{ name: f.full_name, role: "homeowner" }]).slice(0, 4).map((owner, index) => (
          <Kv key={index} label={index === 0 ? "Primary owner" : `Owner ${index + 1}`} v={`${owner.name || "—"} · ${owner.role || "—"}`}/>
        ))}
      </ReviewBlock>

      <ReviewBlock title="Eligibility" onEdit={() => nav("/wizard/eligibility")}>
        <Kv label="Owner-occupant" v={f.owner_occupant ? "Yes" : "—"}/>
        <Kv label="No rental / business use" v={f.no_rental ? "Yes" : "—"}/>
        <Kv label="No other Homestead claim" v={f.no_other_homestead ? "Yes" : "—"}/>
        <Kv label="Tax return address" v={formatAnswer(f.tax_return_address)}/>
        <Kv label="License address" v={formatAnswer(f.license_address)}/>
        <Kv label="Voter registration address" v={formatAnswer(f.voter_registration_address)}/>
      </ReviewBlock>

      <div className="card lg">
        <div className="row between" style={{ alignItems: "flex-start" }}>
          <div>
            <div className="eyebrow">Order summary</div>
            <div className="h-section" style={{ marginTop: 6, marginBottom: 4 }}>EzClaim filing service</div>
            <div className="subtle">Prepare · review · track SDAT submission</div>
          </div>
          <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: "-0.01em" }}>$39.99</div>
        </div>
        <div className="card-divider"/>
        <Callout kind="" icon={<I.Sparkle size={16}/>} title="What happens after you pay">
          <ol style={{ margin: "6px 0 0 18px", padding: 0, color: "var(--ink-700)", lineHeight: 1.6 }}>
            <li>We collect your SSN and digital signature on a secure page.</li>
            <li>We assemble the SDAT Homestead application and queue it for internal review before SDAT submission.</li>
            <li>We email you a unique status link — no login required.</li>
            <li>Sensitive filing materials are encrypted for internal review and deleted after the eventual SDAT submission step, except when briefly needed for retry, support, refund, dispute, legal, fraud, or abuse handling.</li>
          </ol>
        </Callout>
      </div>

      <div className="cta-bar sticky">
        <Button kind="secondary" onClick={() => nav("/wizard/eligibility")} icon={<I.ArrowLeft size={14}/>}>Back</Button>
        <Button kind="primary" onClick={startCheckout} iconRight={<I.Lock size={14}/>} disabled={startingCheckout}>
          {startingCheckout ? "Starting secure checkout..." : "Pay $39.99 and continue"}
        </Button>
      </div>

      <p className="subtle text-center" style={{ fontSize: 12, color: "var(--ink-500)" }}>
        Payments processed securely by Stripe. SDAT decides approval; filing now targets the 2027 tax bill and later.
      </p>
    </div>
  );
}

function ReviewBlock({ title, onEdit, children }) {
  return (
    <div className="card">
      <div className="row between" style={{ marginBottom: 10 }}>
        <div className="h-section" style={{ margin: 0 }}>{title}</div>
        <Button kind="link" onClick={onEdit}>Edit</Button>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 8, fontSize: 14 }}>
        {children}
      </div>
    </div>
  );
}
function Kv({ label, v }) {
  return <div className="kv"><span className="k">{label}</span><span className="v">{v}</span></div>;
}

function formatAnswer(value) {
  if (value === "yes") return "Yes";
  if (value === "no") return "No";
  if (value === "not_applicable") return "Not applicable";
  return "—";
}

Object.assign(window, {
  LookupScreen, DisambiguateScreen, NoMatchScreen,
  ResultsScreen, SdatScreen, WizardScreen, ReviewScreen,
});
