// Atlas · App shell — wired to FastAPI backend
const { useState: useStateApp, useEffect: useEffectApp, useRef: useRefApp } = React;

class ErrorBoundary extends React.Component {
  constructor(props) { super(props); this.state = { error: null }; }
  static getDerivedStateFromError(e) { return { error: e }; }
  render() {
    if (this.state.error) {
      return (
        <div style={{ padding: 40, fontFamily: "var(--mono)", fontSize: 13, color: "#c0392b", background: "#fff5f5", minHeight: "100vh" }}>
          <strong>Render error (check console for full trace):</strong>
          <pre style={{ marginTop: 12, whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{String(this.state.error)}</pre>
          <button onClick={() => { sessionStorage.clear(); window.location.reload(); }} style={{ marginTop: 20, padding: "8px 16px", cursor: "pointer" }}>Reset &amp; Reload</button>
        </div>
      );
    }
    return this.props.children;
  }
}

// ── Theme helper ─────────────────────────────────────────────────────────────
function applyTheme(t) {
  document.documentElement.setAttribute("data-theme", t === "dark" ? "dark" : "light");
  localStorage.setItem("atlas-theme", t);
}

// ── User dropdown ─────────────────────────────────────────────────────────────
function UserMenu({ user, onSettings, onSignOut }) {
  const [open, setOpen] = useStateApp(false);
  const ref = useRefApp(null);

  useEffectApp(() => {
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  const initials = user?.initials || "?";
  const name = user?.name || "Account";

  return (
    <div className="user-menu" ref={ref}>
      <button className={`user-menu-btn ${open ? "open" : ""}`} onClick={() => setOpen((v) => !v)}>
        <span className="av">{initials}</span>
        <span>{name}</span>
        <span className="chevron"><Icon name="chevronDown" size={12} /></span>
      </button>
      {open && (
        <div className="user-dropdown">
          <div className="user-dropdown-header">
            <div className="name">{name}</div>
            <div className="email">{user?.email}</div>
          </div>
          <button className="user-dropdown-item" onClick={() => { setOpen(false); onSettings(); }}>
            <Icon name="settings" size={14} /> Settings
          </button>
          <div className="user-dropdown-divider" />
          <button className="user-dropdown-item danger" onClick={onSignOut}>
            <Icon name="logOut" size={14} /> Sign out
          </button>
        </div>
      )}
    </div>
  );
}

// ── Helpers ───────────────────────────────────────────────────────────────────
const stripHtml = (html) => (html || "").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").trim();

// ── API helpers ───────────────────────────────────────────────────────────────
async function apiPost(path, body) {
  const res = await fetch(path, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({ detail: res.statusText }));
    throw new Error(err.detail || "Request failed");
  }
  return res.json();
}

async function apiGet(path) {
  const res = await fetch(path);
  if (!res.ok) {
    const err = await res.json().catch(() => ({ detail: res.statusText }));
    throw new Error(err.detail || "Request failed");
  }
  return res.json();
}

// ── App ───────────────────────────────────────────────────────────────────────
function App() {
  // Auth
  const [user, setUser] = useStateApp(null);
  const [authLoading, setAuthLoading] = useStateApp(true);

  // UI state
  const [showSettings, setShowSettings] = useStateApp(false);
  const [showUpgrade, setShowUpgrade] = useStateApp(false);
  const [theme, setThemeState] = useStateApp(() => localStorage.getItem("atlas-theme") || "light");

  const setTheme = (t) => { setThemeState(t); applyTheme(t); };

  // Apply theme on mount
  useEffectApp(() => { applyTheme(theme); }, []);

  // Step navigation: "home" | 0..5
  const [step, setStep] = useStateApp("home");

  // Loading / error for async transitions
  const [loading, setLoading] = useStateApp(false);
  const [loadingStatus, setLoadingStatus] = useStateApp("");
  const [error, setError] = useStateApp(null);

  // Report state (populated progressively as the pipeline runs)
  const [reportId, setReportId] = useStateApp(null);
  const [parsedScope, setParsedScope] = useStateApp(null);
  const [outline, setOutline] = useStateApp([]);
  const [draft, setDraft] = useStateApp(null);
  const [exportFiles, setExportFiles] = useStateApp([]);
  const [reportResearched, setReportResearched] = useStateApp(false);
  const [refreshedFrom, setRefreshedFrom] = useStateApp(null);

  // Intake form state
  const [intake, setIntake] = useStateApp({
    title: "",
    reportType: "tech",
    format: "pdf",
    brief: "",
    attachments: [],
    audience: "Internal R&D / science team",
    dateRange: "Last 10 years (2016–2026)",
    geo: "Global",
    anchorEntity: "",
  });

  // Past reports list
  const [reports, setReports] = useStateApp([]);

  // ── Persist navigation state across reloads ─────────────────────────────
  useEffectApp(() => {
    if (step !== "home" && reportId) {
      try {
        sessionStorage.setItem("atlas_step", String(step));
        sessionStorage.setItem("atlas_reportId", reportId);
      } catch {}
    }
    try {
      if (window.posthog && typeof posthog.capture === 'function') {
        posthog.capture('navigation', { step: step, report_id: reportId });
      }
    } catch (err) {
      console.warn("PostHog capture failed:", err);
    }
  }, [step, reportId]);

  // ── Load report list on home ─────────────────────────────────────────────
  useEffectApp(() => {
    if (step === "home" && user) {
      apiGet("/api/reports")
        .then((d) => setReports(d.reports || []))
        .catch(() => {});
    }
  }, [step, user]);

  // ── Poll for background research completion ──────────────────────────────
  useEffectApp(() => {
    if (step !== "home" || !user) return;
    const hasResearching = reports.some((r) => r.isResearching);
    if (!hasResearching) return;
    const interval = setInterval(() => {
      apiGet("/api/reports")
        .then((d) => setReports(d.reports || []))
        .catch(() => {});
    }, 5000);
    return () => clearInterval(interval);
  }, [step, user, reports]);

  // ── Step transitions ─────────────────────────────────────────────────────

  // Step 0 → 1: submit brief, parse scope
  const handleSubmitBrief = async () => {
    setLoading(true);
    setLoadingStatus("Defining scope...");
    setError(null);
    try {
      const result = await apiPost("/api/intake", { ...intake, brief: stripHtml(intake.brief), existingReportId: reportId || null });
      
      // Check for report limit reached (apiPost throws on non-ok, but we handle 403 specifically here)
      // Actually apiPost currently throws, so we'll catch it below. 
      // But let's check if we can handle the 403 more gracefully if needed.
      
      setReportId(result.reportId);
      setParsedScope(result.scope);
      setStep(1);
    } catch (e) {
      if (e.message === "report_limit_reached") {
        setShowUpgrade(true);
      } else {
        setError(e.message);
      }
    } finally {
      setLoading(false);
      setLoadingStatus("");
    }
  };

  // Step 2 → 3: generate outline (acceptedType passed when user accepts a type recommendation)
  const handleGenerateOutline = async (acceptedType) => {
    setLoading(true);
    setError(null);
    try {
      const body = acceptedType ? { reportType: acceptedType } : {};
      if (acceptedType) setIntake(prev => ({ ...prev, reportType: acceptedType }));
      const result = await apiPost(`/api/outline/${reportId}`, body);
      setOutline(result.outline);
      setStep(2);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  // Step 3 → 4: approve outline, go to research console
  const handleApproveOutline = () => {
    setReportResearched(true);
    setStep(3);
  };

  // Step 4 → 5: research + draft done (auto-generated in background), just fetch the draft
  const handleResearchDone = async () => {
    setLoading(true);
    setError(null);
    try {
      const draftData = await apiGet(`/api/report/${reportId}/draft`);
      setDraft(draftData.markdown);
      setStep(4);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  // Re-export after a post-approve revision
  const handleReExport = async () => {
    if (!reportId || !intake.format || !exportFiles.length) return;
    const formats = [intake.format, "md"].filter(Boolean);
    try {
      const result = await apiPost(`/api/export/${reportId}`, { formats });
      setExportFiles(result.files || []);
    } catch (e) {
      // non-fatal — user can re-download on next approval
    }
  };

  // Step 5 → 6: approve draft, export files
  const handleApproveDraft = async () => {
    setLoading(true);
    setError(null);
    try {
      // Mark approved first — this is what makes the report "Complete"
      await apiPost(`/api/report/${reportId}/approve`, {});
      const formats = [intake.format, "md"].filter(Boolean);
      const result = await apiPost(`/api/export/${reportId}`, { formats });
      setExportFiles(result.files || []);
      setStep(5);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  const goBack = () => setStep((s) => Math.max((typeof s === "number" ? s : 0) - 1, 0));
  const goHome = () => { sessionStorage.removeItem("atlas_step"); sessionStorage.removeItem("atlas_reportId"); setStep("home"); setError(null); setReportResearched(false); setRefreshedFrom(null); fetch("/api/me").then(r => r.json()).then(d => { if (d.authenticated) setUser(d); }).catch(() => {}); };
  const goNew  = () => { sessionStorage.removeItem("atlas_step"); sessionStorage.removeItem("atlas_reportId"); setStep(0); setReportId(null); setParsedScope(null); setDraft(null); setOutline([]); setExportFiles([]); setReportResearched(false); setRefreshedFrom(null); setIntake({ title: "", reportType: "tech", format: "pdf", brief: "", attachments: [], audience: "Internal R&D / science team", dateRange: "Last 10 years (2016–2026)", geo: "Global", anchorEntity: "" }); setError(null); };
  const goView = async (r) => {
    const id = r.id || r;
    const stage = typeof r.stage === "number" ? r.stage : null;
    setReportId(id);
    setRefreshedFrom(r.refreshedFrom || null);
    setDraft(null); setOutline([]); setParsedScope(null);
    try {
      if (stage === 5 || stage === 4) {
        const fetches = [
          apiGet(`/api/report/${id}/draft`),
          apiGet(`/api/report/${id}/scope`).catch(() => null),
          apiGet(`/api/report/${id}/outline`).catch(() => null),
        ];
        const [draftData, scopeData, outlineData] = await Promise.all(fetches);
        setDraft(draftData.markdown);
        if (scopeData) {
          setParsedScope(scopeData.scope);
          if (scopeData.intake) setIntake(prev => ({ ...prev, ...scopeData.intake, title: scopeData.scope?.title || prev.title, attachments: scopeData.scope?.attachment_names !== undefined ? scopeData.scope.attachment_names.map(n => n.startsWith("http") ? { type: "url", url: n } : { type: "file", name: n }) : prev.attachments }));
        }
        if (outlineData) { setOutline(outlineData.outline); }
        setReportResearched(true);
        setStep(stage);
      } else if (stage === 3 || r.isResearching) {
        const [scopeData, outlineData] = await Promise.all([
          apiGet(`/api/report/${id}/scope`).catch(() => null),
          apiGet(`/api/report/${id}/outline`).catch(() => null),
        ]);
        if (scopeData) {
          setParsedScope(scopeData.scope);
          if (scopeData.intake) setIntake(prev => ({ ...prev, ...scopeData.intake, title: scopeData.scope?.title || prev.title, attachments: scopeData.scope?.attachment_names !== undefined ? scopeData.scope.attachment_names.map(n => n.startsWith("http") ? { type: "url", url: n } : { type: "file", name: n }) : prev.attachments }));
        }
        if (outlineData) { setOutline(outlineData.outline); }
        setReportResearched(true);
        setStep(3);
      } else if (stage === 2) {
        const [scopeData, outlineData] = await Promise.all([
          apiGet(`/api/report/${id}/scope`),
          apiGet(`/api/report/${id}/outline`),
        ]);
        setParsedScope(scopeData.scope);
        if (scopeData.intake) setIntake(prev => ({ ...prev, ...scopeData.intake, title: scopeData.scope?.title || prev.title, attachments: scopeData.scope?.attachment_names !== undefined ? scopeData.scope.attachment_names.map(n => n.startsWith("http") ? { type: "url", url: n } : { type: "file", name: n }) : prev.attachments }));
        setOutline(outlineData.outline);
        setStep(2);
      } else if (stage === 1) {
        const scopeData = await apiGet(`/api/report/${id}/scope`);
        setParsedScope(scopeData.scope);
        if (scopeData.intake) setIntake(prev => ({ ...prev, ...scopeData.intake, title: scopeData.scope?.title || prev.title, attachments: scopeData.scope?.attachment_names !== undefined ? scopeData.scope.attachment_names.map(n => n.startsWith("http") ? { type: "url", url: n } : { type: "file", name: n }) : prev.attachments }));
        setStep(1);
      } else {
        // Fallback for bare string id (no stage info, e.g. after refresh) — load scope/outline then check for draft
        const [scopeData, outlineData] = await Promise.all([
          apiGet(`/api/report/${id}/scope`).catch(() => null),
          apiGet(`/api/report/${id}/outline`).catch(() => null),
        ]);
        if (!scopeData && !outlineData) {
          // No report data at all — bail to home and clear stale session
          sessionStorage.removeItem("atlas_step");
          sessionStorage.removeItem("atlas_reportId");
          setStep("home");
          return;
        }
        if (scopeData) {
          setParsedScope(scopeData.scope);
          if (scopeData.intake) setIntake(prev => ({ ...prev, ...scopeData.intake, title: scopeData.scope?.title || prev.title, attachments: scopeData.scope?.attachment_names !== undefined ? scopeData.scope.attachment_names.map(n => n.startsWith("http") ? { type: "url", url: n } : { type: "file", name: n }) : prev.attachments }));
        }
        if (outlineData) setOutline(outlineData.outline);
        setReportResearched(true);
        try {
          const d = await apiGet(`/api/report/${id}/draft`);
          setDraft(d.markdown); setStep(4);
        } catch { setStep(3); }
      }
    } catch (e) { setError(e.message); }
  };

  // ── Auth check on mount (after goView is defined for session restore) ────
  useEffectApp(() => {
    fetch("/api/me")
      .then((r) => r.json())
      .then(async (data) => {
        // Initialize Sentry if DSN is provided
        if (data.sentry_dsn && window.Sentry) {
          window.Sentry.init({
            dsn: data.sentry_dsn,
            integrations: [
              window.Sentry.browserTracingIntegration(),
              window.Sentry.replayIntegration(),
            ],
            tracesSampleRate: 1.0,
            replaysSessionSampleRate: 0.1,
            replaysOnErrorSampleRate: 1.0,
          });
          if (data.authenticated) {
            window.Sentry.setUser({ email: data.email, id: data.email, username: data.name });
          }
        }

        if (data.authenticated) {
          setUser(data);
          try {
            if (window.posthog && typeof posthog.identify === 'function') {
              posthog.identify(data.email, {
                email: data.email,
                name: data.name,
                ...data.profile
              });
            }
          } catch (err) {
            console.warn("PostHog identify failed:", err);
          }
          const savedTheme = data.profile?.theme;
          if (savedTheme) setTheme(savedTheme);

          // Restore navigation state from previous session
          const savedReportId = sessionStorage.getItem("atlas_reportId");
          const savedStep = parseInt(sessionStorage.getItem("atlas_step"), 10);
          if (savedReportId) {
            try {
              const result = await apiGet("/api/reports");
              const match = (result.reports || []).find((r) => r.id === savedReportId);
              if (match) {
                // Use the saved UI step rather than server stage so the user
                // lands back on exactly the page they were on before refresh.
                const stageOverride = !isNaN(savedStep) ? savedStep : match.stage;
                await goView({ ...match, stage: stageOverride });
                if (match.stage >= 3) setReportResearched(true);
              } else {
                sessionStorage.removeItem("atlas_step");
                sessionStorage.removeItem("atlas_reportId");
              }
            } catch {}
          }
        }
      })
      .catch(() => {})
      .finally(() => setAuthLoading(false));
  }, []);

  // ── Render ───────────────────────────────────────────────────────────────
  const STEPS = [
    { num: "01", lbl: "Brief" },
    { num: "02", lbl: "Scope" },
    { num: "03", lbl: "Outline" },
    { num: "04", lbl: "Research" },
    { num: "05", lbl: "Draft" },
    { num: "06", lbl: "Delivery" },
  ];

  const stepHeadlines = [
    { eye: "STEP 01 · BRIEF",    h: "Tell Atlas what to investigate.",       sub: "The richer the brief, the sharper the report." },
    { eye: "STEP 02 · SCOPE",    h: intake.title || "Atlas parsed your brief.", sub: "Confirm the structured scope before research begins." },
    { eye: "STEP 03 · OUTLINE",  h: "Approve the report's blueprint.",       sub: "Chapters drafted by the Outline Agent." },
    { eye: "STEP 04 · RESEARCH", h: "The pipeline is running.",              sub: "Agents working in parallel. Feel free to step away." },
    { eye: "STEP 05 · DRAFT",    h: "Review the working draft.",             sub: "Comment any section; the Writing Agent replies in-place." },
    { eye: "STEP 06 · DELIVERY", h: "Your report is ready.",                 sub: "Generated in every format the brief requested." },
  ];
  const homeHeadline = { eye: "REPORTS", h: "Reports.", sub: "Every landscape, literature review, and competitive analysis you've run." };
  const headline = step === "home" ? homeHeadline : stepHeadlines[step];

  const userName = user ? user.name : "Guest";

  const renderStep = () => {
    if (step === "home") {
      return (
        <StepHome
          goNew={goNew}
          goView={goView}
          reports={reports}
          setReports={setReports}
          user={user}
          onUpgrade={() => setShowUpgrade(true)}
        />
      );
    }
    switch (step) {
      case 0:
        return (
          <StepIntake
            state={intake}
            setState={setIntake}
            goNext={handleSubmitBrief}
            goHome={goHome}
            goForward={() => setStep(1)}
            loading={loading}
            error={error}
            readOnly={reportResearched && !!reportId}
          />
        );
      case 1:
        return (
          <StepScope
            state={intake}
            reportId={reportId}
            parsedScope={parsedScope}
            goBack={goBack}
            goNext={handleGenerateOutline}
            goForward={() => setStep(2)}
            loading={loading}
            error={error}
            readOnly={reportResearched && !!reportId}
          />
        );
      case 2:
        return (
          <StepOutline
            outline={outline}
            setOutline={setOutline}
            reportId={reportId}
            parsedScope={parsedScope}
            intake={intake}
            goBack={goBack}
            goNext={handleApproveOutline}
            goForward={() => setStep(3)}
            readOnly={reportResearched && !!reportId}
          />
        );
      case 3:
        return (
          <StepResearch
            reportId={reportId}
            goBack={goBack}
            goHome={goHome}
            goNext={handleResearchDone}
            loading={loading}
            error={error}
            refreshedFrom={refreshedFrom}
          />
        );
      case 4:
        return (
          <StepDraft
            reportId={reportId}
            draft={draft}
            setDraft={setDraft}
            goBack={goBack}
            goNext={handleApproveDraft}
            onRevisionComplete={handleReExport}
            loading={loading}
            error={error}
          />
        );
      case 5:
        return (
          <StepDelivery
            state={intake}
            reportId={reportId}
            files={exportFiles}
            user={user}
            goBack={goBack}
            goRestart={goHome}
            goHome={goHome}
          />
        );
      default:
        return null;
    }
  };

  // Loading spinner overlay
  const LoadingOverlay = () => (
    <div style={{
      position: "fixed", inset: 0, background: "rgba(251,251,253,0.85)",
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
      zIndex: 9999, backdropFilter: "blur(4px)",
    }}>
      <div style={{
        width: 36, height: 36, border: "2px solid var(--line-2)",
        borderTopColor: "var(--ink)", borderRadius: "50%",
        animation: "spin 0.8s linear infinite",
      }} />
      <div style={{ marginTop: 16, fontFamily: "var(--mono)", fontSize: 12, color: "var(--ink-3)", letterSpacing: "0.08em" }}>
        {loadingStatus || "Atlas is thinking…"}
      </div>
      <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
    </div>
  );

  // Auth gate
  if (authLoading) {
    return (
      <div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: "var(--bg)" }}>
        <div style={{ fontFamily: "var(--mono)", fontSize: 12, color: "var(--ink-3)", letterSpacing: "0.1em" }}>Loading…</div>
      </div>
    );
  }

  if (!user) {
    return <AuthPage />;
  }

  if (!user.profileComplete) {
    return (
      <Onboarding
        user={user}
        onComplete={(updatedUser) => setUser({ ...user, ...updatedUser, profileComplete: true })}
      />
    );
  }

  return (
    <div className="app">
      {loading && <LoadingOverlay />}

      {/* Impersonation Banner */}
      {user.impersonated && (
        <div style={{
          background: "#1a1a1a", color: "#fff", padding: "8px 24px",
          display: "flex", alignItems: "center", justifyContent: "space-between",
          fontSize: 12, fontWeight: 500, zIndex: 10000,
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
            <span style={{ color: "#fbbf24" }}>● ADMIN MODE</span>
            <span>You are impersonating <strong>{user.name} ({user.email})</strong></span>
          </div>
          <button 
            onClick={async () => {
              const res = await fetch("/api/admin/unimpersonate", { method: "POST" });
              const data = await res.json();
              if (data.ok) window.location.href = data.redirect;
            }}
            style={{
              background: "#333", border: "none", color: "#fff",
              padding: "4px 12px", borderRadius: "4px", cursor: "pointer",
              fontSize: 11, fontWeight: 600,
            }}
          >
            End Session
          </button>
        </div>
      )}

      {/* Topbar */}
      <div className="topbar">
        <div className="brand" onClick={goHome} style={{ cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 7 }}>
          <img src="/static/Atlas-mark.svg" width="18" height="18" alt="" style={{ display: "block", flexShrink: 0 }} />
          <span className="name">Atlas</span>
        </div>
        <div className="spacer" />
        <div className="topbar-actions">
          {step !== "home" && reportId && (
            <>
              <span style={{ fontFamily: "var(--mono)", letterSpacing: "0.04em", fontSize: 11, color: "var(--ink-3)" }}>
                {reportId}
              </span>
              <span className="divider" />
            </>
          )}
          <UserMenu
            user={user}
            onSettings={() => setShowSettings(true)}
            onSignOut={() => window.location.href = "/auth/logout"}
          />
        </div>
      </div>

      {/* Upgrade / report-limit modal */}
      {showUpgrade && (
        <UpgradeModal
          user={user}
          onClose={() => setShowUpgrade(false)}
        />
      )}

      {/* Settings modal */}
      {showSettings && (
        <SettingsModal
          user={user}
          theme={theme}
          setTheme={(t) => { setTheme(t); }}
          onClose={() => setShowSettings(false)}
          onSave={(form) => {
            setUser((u) => ({ ...u, name: form.name, profile: { ...u.profile, ...form } }));
            if (form.theme) setTheme(form.theme);
          }}
        />
      )}

      {/* Headline */}
      {headline && (
        <div className="headline">
          <div>
            <div className="eyebrow">{headline.eye}</div>
            <h1>{headline.h}</h1>
            <div className="subtitle">{headline.sub}</div>
          </div>
          {step !== "home" && reportId && (
            <div className="doc-meta">
              <div className="id">{reportId}</div>
              <div>Owner · {userName}</div>
              <div>Started · {new Date().toLocaleDateString("en-US", { month: "short", day: "numeric" })}</div>
            </div>
          )}
        </div>
      )}

      {/* Progress strip */}
      {step !== "home" && (
        <div className="progress">
          {STEPS.map((s, i) => {
            const cls = i < step ? "done" : i === step ? "active" : "locked";
            return (
              <div
                key={s.num}
                className={`seg ${cls}`}
                onClick={() => i < step && setStep(i)}
              >
                <span className="ix">{s.num}</span>
                <span className="ttl">{s.lbl}</span>
                {i < step && (
                  <span className="check"><Icon name="checkSmall" size={12} strokeWidth={2.4} /></span>
                )}
              </div>
            );
          })}
        </div>
      )}

      {/* Error banner */}
      {error && (
        <div style={{
          background: "var(--rose-tint)", borderBottom: "1px solid var(--rose)",
          padding: "10px 64px", fontSize: 13, color: "var(--rose)",
          display: "flex", alignItems: "center", justifyContent: "space-between",
        }}>
          <span>Error: {error}</span>
          <button onClick={() => setError(null)} style={{ background: "none", border: "none", color: "var(--rose)", cursor: "pointer", fontSize: 18, lineHeight: 1 }}>×</button>
        </div>
      )}

      {/* Canvas */}
      <div className="canvas">
        {renderStep()}
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<ErrorBoundary><App /></ErrorBoundary>);
