// Customer account pages: sign in, register, forgot password, reset password,
// and the account dashboard. This is the customer-facing front door for the
// features to come — logo manager, mockup approvals and order tracking — so
// the dashboard already lays out where those will live.
//
// Routing is handled by <AccountRouter sub=...> from app.jsx:
//   /account            → dashboard (signed in) or sign-in
//   /account/login      → sign-in
//   /account/register   → create account
//   /account/forgot     → request a reset link
//   /account/reset      → set a new password (reads ?token= from the hash)

// ---------- Shared shell ----------
function AccountShell({ title, subtitle, children, footer }) {
  const app = useApp();
  return (
    <main className="page section">
      <div className="stitched stitched-ink" style={{ maxWidth: 460, margin: "48px auto", padding: 36 }}>
        <div style={{ textAlign: "center", marginBottom: 18 }}>
          <div className="script" style={{ fontSize: 42, color: "var(--rose-deep)", lineHeight: 1 }}>{title}</div>
          {subtitle && <div className="smallcaps" style={{ color: "var(--ink-2)", marginTop: 4 }}>{subtitle}</div>}
        </div>
        {app.mode === "demo" && (
          <div className="mono" style={{ fontSize: 12, color: "var(--ink-3)", textAlign: "center", marginBottom: 16 }}>
            Preview mode — accounts activate on the live site.
          </div>
        )}
        {children}
        {footer && <>
          <hr className="divider-dashed" />
          <div style={{ textAlign: "center", fontSize: 14, color: "var(--ink-2)" }}>{footer}</div>
        </>}
      </div>
    </main>
  );
}

function FieldError({ msg }) {
  if (!msg) return null;
  return <div style={{ color: "var(--rose-deep)", fontSize: 14, marginTop: 6 }}>{msg}</div>;
}
function FieldOk({ msg }) {
  if (!msg) return null;
  return <div style={{ color: "var(--sage-deep)", fontSize: 14, marginTop: 6 }}>{msg}</div>;
}

// ---------- Router ----------
function AccountRouter({ sub }) {
  const app = useApp();
  const customer = app.customer;
  if (sub === "reset")    return <AccountReset />;
  if (sub === "forgot")   return <AccountForgot />;
  if (sub === "register") return customer ? <AccountDashboard /> : <AccountRegister />;
  // "" (/account) and "login"
  return customer ? <AccountDashboard /> : <AccountSignIn />;
}

// ---------- Sign in ----------
function AccountSignIn() {
  const app = useApp();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setErr(""); setBusy(true);
    try {
      await app.accountLogin(email, password);
      location.hash = "#/account";
    } catch (e) {
      setErr(e.message || "Couldn't sign in. Please try again.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <AccountShell
      title="Welcome back"
      subtitle="Sign in to your account"
      footer={<>New here? <a href="#/account/register">Create an account</a></>}
    >
      <form onSubmit={submit}>
        <div className="field">
          <label>Email</label>
          <input type="email" autoComplete="email" value={email} onChange={e => setEmail(e.target.value)} autoFocus placeholder="you@example.com" />
        </div>
        <div className="field">
          <label>Password</label>
          <input type="password" autoComplete="current-password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Your password" />
          <FieldError msg={err} />
        </div>
        <button className="btn" type="submit" disabled={busy} style={{ width: "100%", justifyContent: "center" }}>
          {busy ? "Signing in…" : "Sign in →"}
        </button>
      </form>
      <div style={{ textAlign: "center", marginTop: 14 }}>
        <a href="#/account/forgot" style={{ fontSize: 14, color: "var(--ink-2)" }}>Forgot your password?</a>
      </div>
    </AccountShell>
  );
}

// ---------- Shared address + business fields ----------
// `form` holds the profile values; `set(key, value)` updates one. Used by the
// registration form and the dashboard profile editor.
function ProfileFields({ form, set }) {
  return (
    <>
      <div className="field">
        <label>Street address</label>
        <input value={form.addressLine1 || ""} onChange={e => set("addressLine1", e.target.value)} placeholder="123 Maple St" />
      </div>
      <div className="field">
        <label>Apt / suite <span style={{ color: "var(--ink-3)" }}>(optional)</span></label>
        <input value={form.addressLine2 || ""} onChange={e => set("addressLine2", e.target.value)} placeholder="Apt 4B" />
      </div>
      <div className="field-row">
        <div className="field"><label>City</label><input value={form.city || ""} onChange={e => set("city", e.target.value)} /></div>
        <div className="field" style={{ maxWidth: 120 }}><label>State</label><input value={form.state || ""} onChange={e => set("state", e.target.value)} placeholder="FL" /></div>
        <div className="field" style={{ maxWidth: 140 }}><label>ZIP</label><input value={form.zip || ""} onChange={e => set("zip", e.target.value)} /></div>
      </div>

      <label style={{ display: "flex", alignItems: "center", gap: 8, margin: "6px 0 12px", color: "var(--ink-2)" }}>
        <input type="checkbox" checked={!!form.isBusiness} onChange={e => set("isBusiness", e.target.checked)} />
        This is a business account
      </label>

      {form.isBusiness && (
        <div className="stitched stitched-sage" style={{ marginBottom: 12 }}>
          <div className="field-hint" style={{ marginBottom: 10 }}>Orders ship to the address above. Just add your business details:</div>
          <div className="field"><label>Business name</label><input value={form.businessName || ""} onChange={e => set("businessName", e.target.value)} /></div>
          <div className="field-row">
            <div className="field"><label>Business phone</label><input value={form.businessPhone || ""} onChange={e => set("businessPhone", e.target.value)} /></div>
            <div className="field"><label>Contact person</label><input value={form.businessContact || ""} onChange={e => set("businessContact", e.target.value)} placeholder="Who to ask for" /></div>
          </div>
          <label style={{ display: "flex", gap: 8, alignItems: "flex-start", marginTop: 6, cursor: "pointer", color: "var(--ink-2)" }}>
            <input type="checkbox" checked={form.logoConsent !== false} onChange={e => set("logoConsent", e.target.checked)} style={{ marginTop: 4 }} />
            <span>Allow Hazelbelle to feature my logo and finished work on its website and social posts.
              <span className="field-hint" style={{ display: "block", marginTop: 2 }}>Optional — untick if you'd rather we didn't. You can change this anytime from your account.</span>
            </span>
          </label>
        </div>
      )}
    </>
  );
}

function emptyProfile() {
  return {
    addressLine1: "", addressLine2: "", city: "", state: "", zip: "",
    isBusiness: false, businessName: "", businessAddress: "", businessPhone: "", businessContact: "", logoConsent: true,
  };
}

// ---------- Register ----------
function AccountRegister() {
  const app = useApp();
  const [name, setName] = useState("");
  const [phone, setPhone] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirm, setConfirm] = useState("");
  const [profile, setProfile] = useState(emptyProfile());
  const [website, setWebsite] = useState(""); // [HB] honeypot
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);
  const setP = (k, v) => setProfile(p => ({ ...p, [k]: v }));

  const submit = async (e) => {
    e.preventDefault();
    setErr("");
    if (password.length < 8) { setErr("Password must be at least 8 characters."); return; }
    if (password !== confirm) { setErr("Those passwords don't match."); return; }
    if (!profile.addressLine1 || !profile.city || !profile.state || !profile.zip) {
      setErr("Please add your full delivery address — Joy ships your order to it."); return;
    }
    if (profile.isBusiness && !profile.businessName) { setErr("Please add the business name."); return; }
    setBusy(true);
    try {
      await app.accountRegister({ name, phone, email, password, website, ...profile });
      location.hash = "#/account";
    } catch (e) {
      setErr(e.message || "Couldn't create your account. Please try again.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <AccountShell
      title="Create account"
      subtitle="Join the Hazelbelle studio"
      footer={<>Already have an account? <a href="#/account/login">Sign in</a></>}
    >
      <form onSubmit={submit}>
        <Honeypot value={website} onChange={setWebsite} />
        <div className="field">
          <label>Your name</label>
          <input value={name} onChange={e => setName(e.target.value)} autoFocus placeholder="Jane Smith" />
        </div>
        <div className="field">
          <label>Phone <span style={{ color: "var(--ink-3)" }}>(optional)</span></label>
          <input value={phone} onChange={e => setPhone(e.target.value)} placeholder="So Joy can reach you about an order" />
        </div>
        <div className="field">
          <label>Email</label>
          <input type="email" autoComplete="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="you@example.com" />
        </div>
        <div className="field">
          <label>Password</label>
          <input type="password" autoComplete="new-password" value={password} onChange={e => setPassword(e.target.value)} placeholder="At least 8 characters" />
        </div>
        <div className="field">
          <label>Confirm password</label>
          <input type="password" autoComplete="new-password" value={confirm} onChange={e => setConfirm(e.target.value)} placeholder="Type it again" />
        </div>

        <hr className="divider-dashed" />
        <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 4 }}>Shipping address</div>
        <div className="field-hint" style={{ marginBottom: 10 }}>Where Joy ships your finished order — required.</div>
        <ProfileFields form={profile} set={setP} />

        <FieldError msg={err} />
        <button className="btn" type="submit" disabled={busy} style={{ width: "100%", justifyContent: "center" }}>
          {busy ? "Creating…" : "Create account →"}
        </button>
      </form>
    </AccountShell>
  );
}

// ---------- Forgot password ----------
function AccountForgot() {
  const app = useApp();
  const [email, setEmail] = useState("");
  const [sent, setSent] = useState(false);
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setErr(""); setBusy(true);
    try {
      await app.accountForgot(email);
      setSent(true);
    } catch (e) {
      setErr(e.message || "Something went wrong. Please try again.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <AccountShell
      title="Reset password"
      subtitle="We'll email you a link"
      footer={<>Remembered it? <a href="#/account/login">Back to sign in</a></>}
    >
      {sent ? (
        <div style={{ textAlign: "center" }}>
          <p style={{ color: "var(--ink-2)" }}>
            If an account exists for <strong>{email}</strong>, a reset link is on its way.
            The link works for one hour.
          </p>
          <p style={{ color: "var(--ink-3)", fontSize: 14 }}>Check your spam folder if it doesn't arrive in a few minutes.</p>
        </div>
      ) : (
        <form onSubmit={submit}>
          <div className="field">
            <label>Email</label>
            <input type="email" autoComplete="email" value={email} onChange={e => setEmail(e.target.value)} autoFocus placeholder="you@example.com" />
            <div className="field-hint">Enter the email you signed up with and we'll send a reset link.</div>
            <FieldError msg={err} />
          </div>
          <button className="btn" type="submit" disabled={busy} style={{ width: "100%", justifyContent: "center" }}>
            {busy ? "Sending…" : "Send reset link →"}
          </button>
        </form>
      )}
    </AccountShell>
  );
}

// ---------- Reset password ----------
function AccountReset() {
  const app = useApp();
  const token = (() => {
    const m = location.hash.match(/token=([^&]+)/);
    return m ? decodeURIComponent(m[1]) : "";
  })();
  const [password, setPassword] = useState("");
  const [confirm, setConfirm] = useState("");
  const [err, setErr] = useState("");
  const [done, setDone] = useState(false);
  const [busy, setBusy] = useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setErr("");
    if (password.length < 8) { setErr("Password must be at least 8 characters."); return; }
    if (password !== confirm) { setErr("Those passwords don't match."); return; }
    setBusy(true);
    try {
      await app.accountReset(token, password);
      setDone(true);
    } catch (e) {
      setErr(e.message || "Couldn't reset your password. The link may have expired.");
    } finally {
      setBusy(false);
    }
  };

  if (!token) {
    return (
      <AccountShell title="Reset password" subtitle="Link problem" footer={<a href="#/account/forgot">Request a new link</a>}>
        <p style={{ color: "var(--ink-2)", textAlign: "center" }}>
          This reset link looks incomplete. Please request a fresh one.
        </p>
      </AccountShell>
    );
  }

  return (
    <AccountShell
      title="New password"
      subtitle="Choose something memorable"
      footer={done ? <a href="#/account/login">Go to sign in →</a> : <a href="#/account/login">Back to sign in</a>}
    >
      {done ? (
        <FieldOk msg="Your password has been updated. You can sign in now." />
      ) : (
        <form onSubmit={submit}>
          <div className="field">
            <label>New password</label>
            <input type="password" autoComplete="new-password" value={password} onChange={e => setPassword(e.target.value)} autoFocus placeholder="At least 8 characters" />
          </div>
          <div className="field">
            <label>Confirm new password</label>
            <input type="password" autoComplete="new-password" value={confirm} onChange={e => setConfirm(e.target.value)} placeholder="Type it again" />
            <FieldError msg={err} />
          </div>
          <button className="btn" type="submit" disabled={busy} style={{ width: "100%", justifyContent: "center" }}>
            {busy ? "Saving…" : "Set new password →"}
          </button>
        </form>
      )}
    </AccountShell>
  );
}

// ---------- Dashboard ----------
function AccountDashboard() {
  const app = useApp();
  const c = app.customer || {};
  return (
    <main className="page section">
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", flexWrap: "wrap", gap: 10, marginBottom: 6 }}>
        <div>
          <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 6 }}>Your account</div>
          <h1>Hello, <span className="script" style={{ color: "var(--rose-deep)" }}>{c.name || "there"}</span></h1>
        </div>
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <a className="btn btn-small" href="#/upload">+ Start a new order</a>
          <button className="btn btn-ghost btn-small" onClick={() => app.accountLogout()}>Sign out</button>
        </div>
      </div>
      <p className="hero-lede" style={{ marginTop: 0 }}>
        This is your studio space. Start a new order any time, track the ones in progress,
        and find your invoices and receipts below.
      </p>

      <ThreadDivider />

      <AccountProfileCard />

      <LogoManager />

      {c.isBusiness && <ShippingAddressBook />}

      <div className="card-grid" style={{ marginTop: 18 }}>
        <FeatureTeaser live title="Proof & mockup approvals" body="Proof and mockup approvals are in your order — when a proof is ready, review it and approve or request changes right there." />
      </div>

      <OrdersSection />
      <InvoicesSection />
    </main>
  );
}

function LogoManager() {
  const app = useApp();
  const [logos, setLogos] = useState(null); // null = loading
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);
  const [editing, setEditing] = useState(null);
  const [editName, setEditName] = useState("");

  const load = async () => {
    try { setLogos(await app.accountLogos()); }
    catch (e) { setErr(e.message || "Couldn't load your logos right now."); setLogos([]); }
  };
  useEffect(() => { load(); }, []);

  const onPick = async (file) => {
    if (!file) return;
    setBusy(true); setErr("");
    try {
      const saved = await app.addLogo(file, file.name);
      setLogos(prev => [saved, ...(prev || [])]);
    } catch (e) { setErr(e.message || "Couldn't save that logo."); }
    finally { setBusy(false); }
  };

  const saveRename = async (id) => {
    const name = editName.trim();
    if (!name) return;
    try {
      const updated = await app.renameLogo(id, name);
      setLogos(prev => prev.map(l => l.id === id ? updated : l));
      setEditing(null);
    } catch (e) { setErr(e.message || "Couldn't rename that logo."); }
  };

  const remove = async (logo) => {
    if (!window.confirm(`Delete “${logo.name}”? Orders that already used it keep their own copy.`)) return;
    try { await app.deleteLogo(logo.id); setLogos(prev => prev.filter(l => l.id !== logo.id)); }
    catch (e) { setErr(e.message || "Couldn't delete that logo."); }
  };

  return (
    <div className="stitched stitched-sage" style={{ marginTop: 18 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", flexWrap: "wrap", gap: 10 }}>
        <div>
          <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 4 }}>Now available</div>
          <h3 style={{ margin: 0 }}>Your logos</h3>
          <p style={{ color: "var(--ink-2)", margin: "4px 0 0", fontSize: 15 }}>Save your logos here once and reuse them on any order — no re-uploading.</p>
        </div>
        <label className="btn btn-sage btn-small" style={{ cursor: busy ? "default" : "pointer", opacity: busy ? 0.6 : 1 }}>
          {busy ? "Saving…" : "+ Add a logo"}
          <input type="file" accept="image/*,.pdf,.ai,.eps,.svg" disabled={busy} style={{ display: "none" }}
            onChange={e => { const f = e.target.files && e.target.files[0]; e.target.value = ""; onPick(f); }} />
        </label>
      </div>

      {err && <div style={{ color: "var(--rose-deep)", fontSize: 13, marginTop: 10 }}>{err}</div>}

      {logos === null ? (
        <p style={{ color: "var(--ink-3)", marginTop: 14 }}>Loading your logos…</p>
      ) : logos.length === 0 ? (
        <p style={{ color: "var(--ink-3)", marginTop: 14 }}>No saved logos yet. Add one and it'll be ready to drop onto your next order.</p>
      ) : (
        <div style={{ display: "flex", flexWrap: "wrap", gap: 14, marginTop: 16 }}>
          {logos.map(logo => (
            <div key={logo.id} style={{ width: 150, border: "1px solid var(--line)", borderRadius: 8, padding: 10, background: "var(--paper-2)" }}>
              <a href={logo.viewUrl} target="_blank" rel="noopener" title="Open full size"
                style={{ height: 90, background: "#fff", borderRadius: 6, overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }}>
                {(logo.type || "").startsWith("image/")
                  ? <img src={logo.viewUrl} alt={logo.name} style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }} />
                  : <span style={{ color: "var(--ink-3)", fontSize: 12, padding: 8, textAlign: "center", wordBreak: "break-word" }}>{logo.fileName}</span>}
              </a>
              {editing === logo.id ? (
                <div style={{ marginTop: 8 }}>
                  <input value={editName} onChange={e => setEditName(e.target.value)} style={{ width: "100%", fontSize: 13 }} />
                  <div style={{ display: "flex", gap: 6, marginTop: 6 }}>
                    <button className="btn btn-small" onClick={() => saveRename(logo.id)}>Save</button>
                    <button className="btn btn-ghost btn-small" onClick={() => setEditing(null)}>Cancel</button>
                  </div>
                </div>
              ) : (
                <div style={{ marginTop: 8 }}>
                  <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-2)", wordBreak: "break-word" }}>{logo.name}</div>
                  <div style={{ display: "flex", gap: 10, marginTop: 6, fontSize: 12 }}>
                    <button onClick={() => { setEditing(logo.id); setEditName(logo.name); }} style={{ color: "var(--sage-deep)", background: "none", border: "none", padding: 0, cursor: "pointer" }}>Rename</button>
                    <button onClick={() => remove(logo)} style={{ color: "var(--rose-deep)", background: "none", border: "none", padding: 0, cursor: "pointer" }}>Delete</button>
                  </div>
                </div>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ---------- Shipping address book (business accounts) ----------
// A directory of saved ship-to profiles that sits beside the logo locker, so a
// business with several stores / recurring clients can keep them on file.
function emptyAddress() {
  return { label: "", recipient: "", company: "", addressLine1: "", addressLine2: "", city: "", state: "", zip: "", phone: "", notes: "", isDefault: false };
}
function oneLineAddress(a) {
  const l1 = [a.addressLine1, a.addressLine2].filter(Boolean).join(", ");
  const l2 = [a.city, [a.state, a.zip].filter(Boolean).join(" ")].filter(Boolean).join(", ");
  return [l1, l2].filter(Boolean).join(" · ");
}

function ShippingAddressBook() {
  const app = useApp();
  const [addresses, setAddresses] = useState(null); // null = loading
  const [err, setErr] = useState("");
  const [editing, setEditing] = useState(null);     // null | "new" | <id>
  const [form, setForm] = useState(emptyAddress());
  const [busy, setBusy] = useState(false);
  const setF = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const load = async () => {
    try { setAddresses(await app.accountAddresses()); }
    catch (e) { setErr(e.message || "Couldn't load your addresses right now."); setAddresses([]); }
  };
  useEffect(() => { load(); }, []);

  const startAdd = () => { setForm(emptyAddress()); setEditing("new"); setErr(""); };
  const startEdit = (a) => { setForm({ ...emptyAddress(), ...a }); setEditing(a.id); setErr(""); };
  const cancel = () => { setEditing(null); setErr(""); };

  const payloadFrom = (f) => ({
    label: f.label, recipient: f.recipient, company: f.company,
    addressLine1: f.addressLine1, addressLine2: f.addressLine2,
    city: f.city, state: f.state, zip: f.zip,
    phone: f.phone, notes: f.notes, isDefault: !!f.isDefault,
  });

  const save = async () => {
    if (!form.addressLine1 || !form.city || !form.state || !form.zip) {
      setErr("Please fill in the street, city, state and ZIP."); return;
    }
    setBusy(true); setErr("");
    try {
      if (editing === "new") await app.addAddress(payloadFrom(form));
      else await app.updateAddress(editing, payloadFrom(form));
      setEditing(null);
      await load();
    } catch (e) { setErr(e.message || "Couldn't save that address."); }
    finally { setBusy(false); }
  };

  const remove = async (a) => {
    if (!window.confirm(`Delete “${a.label || a.recipient || oneLineAddress(a)}”?`)) return;
    try { await app.deleteAddress(a.id); await load(); }
    catch (e) { setErr(e.message || "Couldn't delete that address."); }
  };

  const makeDefault = async (a) => {
    try { await app.updateAddress(a.id, { ...payloadFrom(a), isDefault: true }); await load(); }
    catch (e) { setErr(e.message || "Couldn't update that address."); }
  };

  return (
    <div className="stitched stitched-sage" style={{ marginTop: 18 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", flexWrap: "wrap", gap: 10 }}>
        <div>
          <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 4 }}>Now available</div>
          <h3 style={{ margin: 0 }}>Shipping addresses</h3>
          <p style={{ color: "var(--ink-2)", margin: "4px 0 0", fontSize: 15 }}>Save your ship-to locations — head office, each store, a regular client — so reorders are quick. Joy confirms shipping on every order.</p>
        </div>
        {editing === null && (
          <button className="btn btn-sage btn-small" onClick={startAdd}>+ Add address</button>
        )}
      </div>

      {err && <div style={{ color: "var(--rose-deep)", fontSize: 13, marginTop: 10 }}>{err}</div>}

      {editing !== null && (
        <div className="stitched" style={{ marginTop: 14, background: "var(--card)" }}>
          <div className="field-row">
            <div className="field"><label>Label</label><input value={form.label} onChange={e => setF("label", e.target.value)} placeholder="e.g. Downtown store" /></div>
            <div className="field"><label>Attention / contact</label><input value={form.recipient} onChange={e => setF("recipient", e.target.value)} placeholder="Who receives it (optional)" /></div>
          </div>
          <div className="field"><label>Company (optional)</label><input value={form.company} onChange={e => setF("company", e.target.value)} /></div>
          <div className="field"><label>Address line 1</label><input value={form.addressLine1} onChange={e => setF("addressLine1", e.target.value)} placeholder="Street address" /></div>
          <div className="field"><label>Address line 2 (optional)</label><input value={form.addressLine2} onChange={e => setF("addressLine2", e.target.value)} placeholder="Suite, unit, etc." /></div>
          <div className="field-row">
            <div className="field" style={{ flex: 2 }}><label>City</label><input value={form.city} onChange={e => setF("city", e.target.value)} /></div>
            <div className="field" style={{ maxWidth: 90 }}><label>State</label><input value={form.state} onChange={e => setF("state", e.target.value)} placeholder="FL" /></div>
            <div className="field" style={{ maxWidth: 130 }}><label>ZIP</label><input value={form.zip} onChange={e => setF("zip", e.target.value)} /></div>
          </div>
          <div className="field"><label>Phone (optional)</label><input value={form.phone} onChange={e => setF("phone", e.target.value)} /></div>
          <div className="field"><label>Notes (optional)</label><input value={form.notes} onChange={e => setF("notes", e.target.value)} placeholder="e.g. Deliver to loading dock" /></div>
          <label style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12, color: "var(--ink-2)" }}>
            <input type="checkbox" checked={!!form.isDefault} onChange={e => setF("isDefault", e.target.checked)} /> Make this my default shipping address
          </label>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            <button className="btn btn-small" disabled={busy} onClick={save}>{busy ? "Saving…" : (editing === "new" ? "Save address" : "Save changes")}</button>
            <button className="btn btn-ghost btn-small" disabled={busy} onClick={cancel}>Cancel</button>
          </div>
        </div>
      )}

      {addresses === null ? (
        <p style={{ color: "var(--ink-3)", marginTop: 14 }}>Loading your addresses…</p>
      ) : addresses.length === 0 ? (
        editing === null && <p style={{ color: "var(--ink-3)", marginTop: 14 }}>No saved addresses yet. Add your first ship-to location above.</p>
      ) : (
        <div style={{ display: "flex", flexWrap: "wrap", gap: 14, marginTop: 16 }}>
          {addresses.map(a => (
            <div key={a.id} style={{ width: 260, border: a.isDefault ? "2px solid var(--sage-deep)" : "1px solid var(--line)", borderRadius: 8, padding: 12, background: "var(--paper-2)" }}>
              <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 6 }}>
                <div style={{ fontWeight: 700, color: "var(--ink)", wordBreak: "break-word" }}>{a.label || a.recipient || "Address"}</div>
                {a.isDefault && <span className="badge" style={{ background: "var(--sage)" }}>default</span>}
              </div>
              {a.recipient && a.label && <div style={{ fontSize: 13, color: "var(--ink-2)" }}>{a.recipient}</div>}
              {a.company && <div style={{ fontSize: 13, color: "var(--ink-2)" }}>{a.company}</div>}
              <div style={{ fontSize: 13, color: "var(--ink-2)", marginTop: 4 }}>
                <div>{a.addressLine1}</div>
                {a.addressLine2 && <div>{a.addressLine2}</div>}
                <div>{[a.city, [a.state, a.zip].filter(Boolean).join(" ")].filter(Boolean).join(", ")}</div>
              </div>
              {a.phone && <div style={{ fontSize: 13, color: "var(--ink-3)", marginTop: 4 }}>{a.phone}</div>}
              {a.notes && <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 4, fontStyle: "italic" }}>{a.notes}</div>}
              <div style={{ display: "flex", gap: 12, marginTop: 10, fontSize: 12, flexWrap: "wrap" }}>
                <button onClick={() => startEdit(a)} style={{ color: "var(--sage-deep)", background: "none", border: "none", padding: 0, cursor: "pointer" }}>Edit</button>
                {!a.isDefault && <button onClick={() => makeDefault(a)} style={{ color: "var(--gold-deep)", background: "none", border: "none", padding: 0, cursor: "pointer" }}>Make default</button>}
                <button onClick={() => remove(a)} style={{ color: "var(--rose-deep)", background: "none", border: "none", padding: 0, cursor: "pointer" }}>Delete</button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function FeatureTeaser({ title, body, live }) {
  return (
    <div className="stitched stitched-sage" style={{ minHeight: 160, opacity: live ? 1 : 0.92 }}>
      <div className="smallcaps" style={{ color: live ? "var(--sage-deep)" : "var(--gold-deep)", marginBottom: 8 }}>{live ? "Now available" : "Coming soon"}</div>
      <h3 style={{ marginBottom: 8 }}>{title}</h3>
      <p style={{ color: "var(--ink-2)", margin: 0, fontSize: 16 }}>{body}</p>
    </div>
  );
}

function formatAddress(c) {
  const l1 = [c.addressLine1, c.addressLine2].filter(Boolean).join(", ");
  const l2 = [c.city, [c.state, c.zip].filter(Boolean).join(" ")].filter(Boolean).join(", ");
  return [l1, l2].filter(Boolean);
}

function AccountProfileCard() {
  const app = useApp();
  const c = app.customer || {};
  const [editing, setEditing] = useState(false);
  const [name, setName] = useState("");
  const [phone, setPhone] = useState("");
  const [profile, setProfile] = useState(emptyProfile());
  const [curPw, setCurPw] = useState("");
  const [newPw, setNewPw] = useState("");
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);
  const [deleting, setDeleting] = useState(false);
  const setP = (k, v) => setProfile(p => ({ ...p, [k]: v }));

  const startEdit = () => {
    setName(c.name || ""); setPhone(c.phone || "");
    setProfile({
      addressLine1: c.addressLine1 || "", addressLine2: c.addressLine2 || "",
      city: c.city || "", state: c.state || "", zip: c.zip || "",
      isBusiness: !!c.isBusiness, businessName: c.businessName || "",
      businessAddress: c.businessAddress || "", businessPhone: c.businessPhone || "", businessContact: c.businessContact || "",
      logoConsent: c.logoConsent !== false,
    });
    setErr(""); setCurPw(""); setNewPw(""); setEditing(true);
  };

  const save = async (e) => {
    e.preventDefault();
    setErr("");
    if (!profile.addressLine1 || !profile.city || !profile.state || !profile.zip) {
      setErr("Please keep your full delivery address filled in."); return;
    }
    if (profile.isBusiness && !profile.businessName) { setErr("Please add the business name."); return; }
    setBusy(true);
    try {
      const patch = { name, phone, ...profile };
      if (newPw) { patch.newPassword = newPw; patch.currentPassword = curPw; }
      await app.accountUpdate(patch);
      app.toast("Saved.");
      setEditing(false); setCurPw(""); setNewPw("");
    } catch (e) {
      setErr(e.message || "Couldn't save. Please try again.");
    } finally {
      setBusy(false);
    }
  };

  const addr = formatAddress(c);

  if (!editing) {
    return (
      <div className="stitched">
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", flexWrap: "wrap", gap: 8 }}>
          <div className="smallcaps" style={{ color: "var(--sage-deep)" }}>Your details</div>
          <button className="btn btn-ghost btn-small" onClick={startEdit}>Edit profile</button>
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 28, marginTop: 10 }}>
          <div style={{ minWidth: 200 }}>
            <div style={{ fontSize: 17, color: "var(--ink)" }}>{c.name || "—"}</div>
            <div style={{ color: "var(--ink-2)", fontSize: 15 }}>{c.email}</div>
            {c.phone && <div style={{ color: "var(--ink-2)", fontSize: 15 }}>{c.phone}</div>}
          </div>
          <div style={{ minWidth: 200 }}>
            <div className="smallcaps" style={{ color: "var(--ink-3)", fontSize: 11, marginBottom: 2 }}>Delivery address</div>
            {addr.length ? addr.map((l, i) => <div key={i} style={{ color: "var(--ink-2)", fontSize: 15 }}>{l}</div>)
              : <div style={{ color: "var(--rose-deep)", fontSize: 14 }}>Please add your address so Joy can ship your orders.</div>}
          </div>
          {c.isBusiness && (
            <div style={{ minWidth: 200 }}>
              <div className="smallcaps" style={{ color: "var(--ink-3)", fontSize: 11, marginBottom: 2 }}>Business</div>
              <div style={{ color: "var(--ink-2)", fontSize: 15 }}>{c.businessName}</div>
              {c.businessAddress && <div style={{ color: "var(--ink-2)", fontSize: 15 }}>{c.businessAddress}</div>}
              {c.businessPhone && <div style={{ color: "var(--ink-2)", fontSize: 15 }}>{c.businessPhone}</div>}
              {c.businessContact && <div style={{ color: "var(--ink-3)", fontSize: 14 }}>Contact: {c.businessContact}</div>}
              <div style={{ color: "var(--ink-3)", fontSize: 13, marginTop: 4 }}>
                Logo on our site: {c.logoConsent !== false ? "allowed" : "not shown"} <span className="field-hint">(edit profile to change)</span>
              </div>
            </div>
          )}
        </div>
        {!c.isBusiness && (
          <div className="field-hint" style={{ marginTop: 10 }}>
            Orders ship to your delivery address above. Need a one-off order sent somewhere else — a gift, perhaps? Contact Joy with the details and she'll arrange shipping.
          </div>
        )}
        <div style={{ marginTop: 16 }}>
          <button className="btn btn-ghost btn-small" onClick={() => setDeleting(true)} style={{ color: "var(--rose-deep)" }}>Delete my account</button>
        </div>
        <DeleteAccountModal open={deleting} onClose={() => setDeleting(false)} />
      </div>
    );
  }

  return (
    <div className="stitched">
      <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 8 }}>Edit your details</div>
      <form onSubmit={save}>
        <div className="field-row">
          <div className="field"><label>Name</label><input value={name} onChange={e => setName(e.target.value)} /></div>
          <div className="field"><label>Phone <span style={{ color: "var(--ink-3)" }}>(optional)</span></label><input value={phone} onChange={e => setPhone(e.target.value)} /></div>
        </div>

        <div className="smallcaps" style={{ color: "var(--sage-deep)", margin: "6px 0 8px" }}>Delivery address</div>
        <ProfileFields form={profile} set={setP} />

        <hr className="divider-dashed" />
        <div className="field-hint" style={{ marginBottom: 8 }}>Change password (leave blank to keep your current one):</div>
        <div className="field-row">
          <div className="field"><label>Current password</label><input type="password" autoComplete="current-password" value={curPw} onChange={e => setCurPw(e.target.value)} /></div>
          <div className="field"><label>New password</label><input type="password" autoComplete="new-password" value={newPw} onChange={e => setNewPw(e.target.value)} placeholder="At least 8 characters" /></div>
        </div>

        <FieldError msg={err} />
        <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
          <button className="btn btn-small" type="submit" disabled={busy}>{busy ? "Saving…" : "Save"}</button>
          <button className="btn btn-ghost btn-small" type="button" onClick={() => { setEditing(false); setErr(""); }}>Cancel</button>
        </div>
      </form>
    </div>
  );
}

// Two-step, irreversible self-delete: acknowledge the data loss, then confirm
// with the account password.
function DeleteAccountModal({ open, onClose }) {
  const app = useApp();
  const [ack, setAck] = useState(false);
  const [password, setPassword] = useState("");
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);

  useEffect(() => { if (open) { setAck(false); setPassword(""); setErr(""); } }, [open]);

  const confirm = async () => {
    setErr("");
    if (!ack) { setErr("Please tick the box to confirm you understand."); return; }
    if (!password) { setErr("Enter your password to confirm."); return; }
    setBusy(true);
    try {
      await app.accountDelete(password); // redirects home on success
    } catch (e) {
      setErr(e.message || "Couldn't delete the account.");
      setBusy(false);
    }
  };

  return (
    <Modal open={open} onClose={onClose}>
      <h2 style={{ marginTop: 0, color: "var(--rose-deep)" }}>Delete your account?</h2>
      <p style={{ color: "var(--ink-2)" }}>
        This is permanent. Once your account is deleted, your saved logos, design files, and
        order history here are gone for good — <strong>logos especially cannot be recovered.</strong>
      </p>
      <label style={{ display: "flex", alignItems: "flex-start", gap: 10, margin: "14px 0", color: "var(--ink-2)" }}>
        <input type="checkbox" checked={ack} onChange={e => setAck(e.target.checked)} style={{ marginTop: 4 }} />
        <span>I confirm I have printed or saved all of my logos and any invoices/receipts I need, and I understand they can't be retrieved after deletion.</span>
      </label>
      <div className="field">
        <label>Enter your password to confirm</label>
        <input type="password" value={password} onChange={e => setPassword(e.target.value)} />
        <FieldError msg={err} />
      </div>
      <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 8 }}>
        <button className="btn btn-ghost btn-small" onClick={onClose} disabled={busy}>Keep my account</button>
        <button className="btn btn-small" onClick={confirm} disabled={busy} style={{ background: "var(--rose-deep)", borderColor: "var(--rose-deep)" }}>
          {busy ? "Deleting…" : "Permanently delete"}
        </button>
      </div>
    </Modal>
  );
}

// ---------- Order tracking ----------
// Joy's approved 5-stage tracker. Keys are stored on the order's `status`;
// trackerN.jpg art (in /img/tracker) matches each stage 1:1.
const ORDER_STAGES = [
  { key: "placed",     label: "Order Placed" },
  { key: "digitizing", label: "Design & Digitizing" },
  { key: "stitching",  label: "On the Hoop – Stitching" },
  { key: "qc",         label: "QC & Finishing" },
  { key: "finished",   label: "Order Finished" },
];
// Map the old 3-stage values onto the new 5-stage model so existing orders still resolve.
const STATUS_ALIASES = { new: "placed", progress: "stitching", done: "finished" };
const normStatus = (s) => STATUS_ALIASES[s] || s;

function OrdersSection() {
  const app = useApp();
  const [orders, setOrders] = useState(null); // null while loading
  const [err, setErr] = useState("");

  const load = async () => {
    try {
      const list = await app.accountOrders();
      setOrders(list || []);
    } catch (e) {
      setErr(e.message || "Couldn't load your orders right now.");
      setOrders([]);
    }
  };

  useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const list = await app.accountOrders();
        if (alive) setOrders(list || []);
      } catch (e) {
        if (alive) { setErr(e.message || "Couldn't load your orders right now."); setOrders([]); }
      }
    })();
    return () => { alive = false; };
  }, []);

  return (
    <section className="section-tight">
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", flexWrap: "wrap", gap: 10 }}>
        <div>
          <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 8 }}>Order tracking</div>
          <h2 style={{ marginBottom: 16 }}>Your <span className="script" style={{ color: "var(--rose-deep)" }}>orders</span></h2>
        </div>
        <a className="btn btn-ghost btn-small" href="#/upload">+ New order</a>
      </div>
      {orders === null ? (
        <p style={{ color: "var(--ink-3)" }}>Loading your orders…</p>
      ) : err ? (
        <p style={{ color: "var(--rose-deep)" }}>{err}</p>
      ) : orders.length === 0 ? (
        <div className="stitched stitched-sage" style={{ textAlign: "center", padding: 40 }}>
          <p style={{ color: "var(--ink-2)", margin: "0 0 14px" }}>
            No orders yet. When you upload a design, it'll appear here so you can follow its progress.
          </p>
          <a className="btn" href="#/upload">Upload a design →</a>
        </div>
      ) : (
        <div>{orders.map(o => <OrderCard key={o.id} order={o} onChanged={load} />)}</div>
      )}
    </section>
  );
}

function OrderCard({ order, onChanged }) {
  const app = useApp();
  const current = Math.max(0, ORDER_STAGES.findIndex(s => s.key === normStatus(order.status)));
  const approval = order.approval || "pending";
  const proofStatus = order.proofStatus || "none";
  const [requesting, setRequesting] = useState(false);
  const [feedback, setFeedback] = useState("");
  const [busy, setBusy] = useState(false);
  const [pErr, setPErr] = useState("");

  const decide = async (decision) => {
    if (decision === "changes_requested" && !feedback.trim()) {
      setPErr("Please tell the studio what you'd like changed.");
      return;
    }
    setBusy(true); setPErr("");
    try {
      await app.accountProofDecision(order.id, decision, decision === "changes_requested" ? feedback.trim() : "");
      setRequesting(false); setFeedback("");
      if (onChanged) await onChanged();
    } catch (e) {
      setPErr(e.message || "Couldn't save that — please try again.");
    } finally {
      setBusy(false);
    }
  };

  const [reordering, setReordering] = useState(false);
  const [roQty, setRoQty] = useState(order.qty || 1);
  const [roDeadline, setRoDeadline] = useState("");
  const [roNotes, setRoNotes] = useState("");
  const [roBusy, setRoBusy] = useState(false);
  const [roErr, setRoErr] = useState("");

  // Optional reorder changes: a different garment (curated/full catalog) and a
  // different design (a saved logo or a freshly uploaded one). Defaults keep the
  // original behaviour — same item, original files.
  const [roBlank, setRoBlank] = useState(null);            // curated catalog pick
  const [roCatalog, setRoCatalog] = useState(null);        // full-catalog { style, color, size, service }
  const [roAdded, setRoAdded] = useState([]);              // up to 5 extra items: { blank, catalogStyle, qty }
  const [pickerTarget, setPickerTarget] = useState(null);  // null | "base" | <added index>
  const [roLogoMode, setRoLogoMode] = useState("original"); // 'original' | 'library' | 'upload'
  const [roLogoId, setRoLogoId] = useState("");
  const [roLogos, setRoLogos] = useState(null);            // null = not loaded yet
  const [roLogoBusy, setRoLogoBusy] = useState(false);
  const catalogUrl = useCatalogUrl();

  const RO_MAX_QTY = 12;       // cap pieces of any one item
  const RO_MAX_ADDED = 5;      // cap extra items added to a reorder
  const clampQty = (v) => Math.max(1, Math.min(RO_MAX_QTY, parseInt(v, 10) || 1));

  // Extra-item helpers
  const addItem = () => setRoAdded(prev => prev.length >= RO_MAX_ADDED ? prev : [...prev, { blank: null, catalogStyle: null, qty: 1 }]);
  const removeItem = (i) => setRoAdded(prev => prev.filter((_, idx) => idx !== i));
  const patchItem = (i, patch) => setRoAdded(prev => prev.map((it, idx) => idx === i ? { ...it, ...patch } : it));

  const ensureLogos = async () => {
    if (roLogos !== null) return;
    try { setRoLogos(await app.accountLogos()); }
    catch (e) { setRoLogos([]); }
  };
  const setLogoMode = (m) => {
    setRoErr("");
    setRoLogoMode(m);
    if (m === "original") setRoLogoId("");
    else ensureLogos();
  };
  const onReorderLogoUpload = async (file) => {
    if (!file) return;
    setRoLogoBusy(true); setRoErr("");
    try {
      const saved = await app.addLogo(file, file.name);
      setRoLogos(prev => [saved, ...(prev || [])]);
      setRoLogoMode("library");
      setRoLogoId(saved.id);
    } catch (e) {
      setRoErr(e.message || "Couldn't upload that logo — please try again.");
    } finally { setRoLogoBusy(false); }
  };

  const openReorder = () => {
    setRoQty(clampQty(order.qty || 1)); setRoDeadline(""); setRoNotes(order.notes || "");
    setRoBlank(null); setRoCatalog(null); setRoAdded([]); setPickerTarget(null);
    setRoLogoMode("original"); setRoLogoId("");
    setRoErr(""); setReordering(true);
  };
  const submitReorder = async () => {
    if (roLogoMode !== "original" && !roLogoId) {
      setRoErr("Pick a logo, or choose “Same files as before”.");
      return;
    }
    // Only keep extra items that actually have a garment chosen.
    const addedItems = roAdded
      .filter(it => it.blank || (it.catalogStyle && (it.catalogStyle.style || it.catalogStyle.color || it.catalogStyle.size)))
      .map(it => ({ blank: it.blank || undefined, catalogStyle: it.catalogStyle || undefined, qty: clampQty(it.qty) }));
    setRoBusy(true); setRoErr("");
    try {
      const body = {
        qty: clampQty(roQty),
        deadline: roDeadline || null,
        notes: roNotes,
        logoSource: roLogoId ? "library" : "original",
        logoId: roLogoId || undefined,
        addedItems,
      };
      if (roBlank) {
        body.blank = roBlank;
        const decode = (s) => String(s || "").replace(/&#(\d+);/g, (_, n) => String.fromCharCode(n)).replace(/&amp;/g, "&");
        body.itemType = decode([roBlank.brand, roBlank.styleNo, roBlank.title].filter(Boolean).join(" ")).replace(/\s+/g, " ").trim().slice(0, 80);
      } else if (roCatalog) { body.catalogStyle = roCatalog; }
      await app.accountReorder(order.id, body);
      setReordering(false);
      if (onChanged) await onChanged();
    } catch (e) {
      setRoErr(e.message || "Couldn't place the reorder — please try again.");
    } finally {
      setRoBusy(false);
    }
  };

  return (
    <div className="stitched" style={{ marginBottom: 16 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", flexWrap: "wrap", gap: 8 }}>
        <div>
          <div className="mono" style={{ color: "var(--gold-deep)", fontSize: 13, letterSpacing: ".04em" }}>{order.ref}</div>
          <h3 style={{ margin: "4px 0 0" }}>{order.itemType || "Custom order"}{order.qty ? ` × ${order.qty}` : ""}</h3>
        </div>
        <div style={{ textAlign: "right", color: "var(--ink-3)", fontSize: 14 }}>
          <div>Submitted {fmtDate(order.createdAt)}</div>
          {order.deadline && <div>Needed by {order.deadline}</div>}
        </div>
      </div>

      {approval === "declined" ? (
        <div style={{ marginTop: 14, padding: "12px 14px", borderRadius: 8, background: "rgba(147,78,92,0.08)", border: "1px solid rgba(147,78,92,0.3)" }}>
          <div style={{ color: "var(--rose-deep)", fontWeight: 600 }}>This order wasn't accepted as submitted.</div>
          {order.approvalReason && <div style={{ color: "var(--ink-2)", marginTop: 6, whiteSpace: "pre-wrap" }}>{order.approvalReason}</div>}
          <div style={{ color: "var(--ink-3)", fontSize: 13, marginTop: 6 }}>Reply to your confirmation email and the studio will help with next steps.</div>
        </div>
      ) : approval === "pending" ? (
        <div style={{ marginTop: 14, color: "var(--gold-deep)", fontSize: 14 }}>Awaiting review by the studio.</div>
      ) : (
        <>
          <div style={{ marginTop: 14, color: "var(--sage-deep)", fontSize: 14 }}>✓ Approved — in the studio.</div>
          <StatusStepper current={current} />
        </>
      )}

      {proofStatus !== "none" && (
        <div style={{ marginTop: 16, padding: "14px 16px", borderRadius: 8, background: "var(--paper-2)", border: "1px solid var(--line)" }}>
          <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 8, fontSize: 12 }}>Stitch proof</div>

          {order.proof && (
            <a href={order.proof.viewUrl} target="_blank" rel="noopener" title="Open full size" style={{ display: "block", marginBottom: 12 }}>
              <img src={order.proof.viewUrl} alt={`Proof for ${order.ref}`}
                style={{ width: "100%", height: "auto", maxHeight: 360, objectFit: "contain", display: "block", borderRadius: 6, background: "#fff" }} />
            </a>
          )}

          {/* [HB] Stitch count + thread colours attached to the proof. */}
          {(order.proofStitchCount > 0 || (Array.isArray(order.proofThreadColors) && order.proofThreadColors.length > 0)) && (
            <div style={{ marginBottom: 12, display: "grid", gap: 8 }}>
              {order.proofStitchCount > 0 && (
                <div style={{ fontSize: 14, color: "var(--ink-2)" }}>
                  <span className="smallcaps" style={{ color: "var(--ink-3)", fontSize: 11, marginRight: 8 }}>Stitch count</span>
                  <strong>{order.proofStitchCount.toLocaleString()}</strong> stitches
                </div>
              )}
              {Array.isArray(order.proofThreadColors) && order.proofThreadColors.length > 0 && (
                <div>
                  <div className="smallcaps" style={{ color: "var(--ink-3)", fontSize: 11, marginBottom: 4 }}>Thread colours</div>
                  <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                    {order.proofThreadColors.map((c, i) => (
                      <span key={i} style={{ padding: "3px 10px", borderRadius: 999, background: "#fff", border: "1px solid var(--line)", fontSize: 13 }}>{c}</span>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}

          {proofStatus === "approved" ? (
            <div style={{ color: "var(--sage-deep)", fontSize: 14 }}>✓ You approved this proof — thank you! Stitching can begin.</div>
          ) : proofStatus === "changes_requested" ? (
            <div>
              <div style={{ color: "var(--gold-deep)", fontWeight: 600, fontSize: 14 }}>Changes requested</div>
              {order.proofFeedback && <div style={{ color: "var(--ink-2)", marginTop: 6, whiteSpace: "pre-wrap", fontSize: 14 }}>{order.proofFeedback}</div>}
              <div style={{ color: "var(--ink-3)", fontSize: 13, marginTop: 6 }}>The studio will send a revised proof to review.</div>
            </div>
          ) : (
            // proofStatus === "sent" — awaiting the customer's decision
            <div>
              <p style={{ color: "var(--ink-2)", fontSize: 14, margin: "0 0 12px" }}>
                Please review your proof. Once you approve it, stitching begins — so check the spelling, colours and placement carefully.
              </p>
              {!requesting ? (
                <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                  <button className="btn btn-sage btn-small" disabled={busy} onClick={() => decide("approved")}>
                    {busy ? "Saving…" : "✓ Approve this proof"}
                  </button>
                  <button className="btn btn-ghost btn-small" disabled={busy} onClick={() => { setRequesting(true); setPErr(""); }}>
                    Request changes
                  </button>
                </div>
              ) : (
                <div>
                  <div className="field">
                    <label>What would you like changed?</label>
                    <textarea value={feedback} onChange={e => setFeedback(e.target.value)} rows="3"
                      placeholder="e.g. The text should read 'Hazel & Belle' — and could the leaf be a little darker green?" />
                  </div>
                  <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginTop: 8 }}>
                    <button className="btn btn-small" disabled={busy} onClick={() => decide("changes_requested")}>
                      {busy ? "Sending…" : "Send change request"}
                    </button>
                    <button className="btn btn-ghost btn-small" disabled={busy} onClick={() => { setRequesting(false); setFeedback(""); setPErr(""); }}>
                      Cancel
                    </button>
                  </div>
                </div>
              )}
              {pErr && <div style={{ color: "var(--rose-deep)", fontSize: 13, marginTop: 8 }}>{pErr}</div>}
            </div>
          )}
        </div>
      )}

      {(order.size || order.placement) && (
        <div style={{ color: "var(--ink-2)", fontSize: 14, marginTop: 12 }}>
          {order.size ? `Size: ${order.size}` : ""}{order.size && order.placement ? " · " : ""}{order.placement ? `Placement: ${order.placement}` : ""}
        </div>
      )}
      {order.notes && <p style={{ color: "var(--ink-2)", marginTop: 8, whiteSpace: "pre-wrap" }}>{order.notes}</p>}

      {order.files && order.files.length > 0 && (
        <div style={{ marginTop: 12 }}>
          <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 6, fontSize: 12 }}>Your files</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
            {order.files.map(f => (
              <a key={f.id} className="btn btn-ghost btn-small" href={f.url} target="_blank" rel="noopener">
                ↓ {f.name} <span style={{ color: "var(--ink-3)" }}>({Math.round((f.size || 0) / 1024)} KB)</span>
              </a>
            ))}
          </div>
        </div>
      )}

      <div style={{ marginTop: 14, borderTop: "1px dashed var(--line)", paddingTop: 14 }}>
        {!reordering ? (
          <button className="btn btn-ghost btn-small" onClick={openReorder}>↻ Reorder this</button>
        ) : (
          <div>
            <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 8, fontSize: 12 }}>Reorder</div>
            <p style={{ color: "var(--ink-2)", fontSize: 14, margin: "0 0 12px" }}>
              We'll start a fresh order using the same {order.itemType ? `“${order.itemType}”` : "item"}{order.size ? `, size ${order.size}` : ""}{order.placement ? `, ${order.placement} placement` : ""}, and reuse your original design files. You can change the quantity, swap the garment, or use a different logo below.
            </p>
            <div style={{ display: "flex", gap: 12, flexWrap: "wrap", alignItems: "flex-end" }}>
              <div className="field" style={{ maxWidth: 120 }}>
                <label>Quantity</label>
                <select value={roQty} onChange={e => setRoQty(Number(e.target.value))}>
                  {Array.from({ length: RO_MAX_QTY }, (_, n) => n + 1).map(n => <option key={n} value={n}>{n}</option>)}
                </select>
              </div>
              <div className="field" style={{ maxWidth: 200 }}>
                <label>Needed by (optional)</label>
                <input type="date" value={roDeadline} onChange={e => setRoDeadline(e.target.value)} />
              </div>
            </div>

            {/* Garment — keep the same, or pick from the curated/full catalog */}
            <div className="field">
              <label>Garment</label>
              {!roBlank && !roCatalog && (
                <div style={{ color: "var(--ink-2)", fontSize: 14, marginBottom: 2 }}>
                  Keeping the same item{order.itemType ? ` (${order.itemType})` : ""}.
                </div>
              )}
              <BlankLineControl
                line={{ blank: roBlank, catalogStyle: roCatalog }}
                catalogUrl={catalogUrl}
                onBrowse={() => setPickerTarget("base")}
                onRemove={() => setRoBlank(null)}
                onSetCatalog={(cs) => { setRoCatalog(cs); setRoBlank(null); }}
                onClearCatalog={() => setRoCatalog(null)}
              />
            </div>

            {/* Add up to 5 more items to this order */}
            <div className="field">
              <label>Add more items (optional)</label>
              {roAdded.length === 0 && (
                <div style={{ color: "var(--ink-3)", fontSize: 13, marginBottom: 6 }}>Want more than one kind of item? Add up to {RO_MAX_ADDED} extra garments to this order.</div>
              )}
              {roAdded.map((it, i) => (
                <div key={i} className="stitched" style={{ padding: 12, marginBottom: 8 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                    <div style={{ display: "flex", alignItems: "flex-end", gap: 10, flexWrap: "wrap" }}>
                      <div className="field" style={{ maxWidth: 110, margin: 0 }}>
                        <label style={{ fontSize: 12 }}>Quantity</label>
                        <select value={it.qty} onChange={e => patchItem(i, { qty: Number(e.target.value) })}>
                          {Array.from({ length: RO_MAX_QTY }, (_, n) => n + 1).map(n => <option key={n} value={n}>{n}</option>)}
                        </select>
                      </div>
                      <span style={{ color: "var(--ink-3)", fontSize: 13 }}>Item {i + 1}</span>
                    </div>
                    <button type="button" className="btn btn-ghost btn-small" onClick={() => removeItem(i)} style={{ color: "var(--rose-deep)" }}>Remove item</button>
                  </div>
                  <BlankLineControl
                    line={{ blank: it.blank, catalogStyle: it.catalogStyle }}
                    catalogUrl={catalogUrl}
                    onBrowse={() => setPickerTarget(i)}
                    onRemove={() => patchItem(i, { blank: null })}
                    onSetCatalog={(cs) => patchItem(i, { catalogStyle: cs, blank: null })}
                    onClearCatalog={() => patchItem(i, { catalogStyle: null })}
                  />
                </div>
              ))}
              <button type="button" className="btn btn-ghost btn-small" disabled={roAdded.length >= RO_MAX_ADDED} onClick={addItem}>
                {roAdded.length >= RO_MAX_ADDED ? `Up to ${RO_MAX_ADDED} extra items` : "+ Add another item"}
              </button>
            </div>

            {/* Design / logo — original files, a saved logo, or a new upload */}
            <div className="field">
              <label>Design / logo</label>
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 8 }}>
                <button type="button" className={"btn btn-small " + (roLogoMode === "original" ? "" : "btn-ghost")}
                  onClick={() => setLogoMode("original")}>Same files as before</button>
                <button type="button" className={"btn btn-small " + (roLogoMode === "library" ? "" : "btn-ghost")}
                  onClick={() => setLogoMode("library")}>Use a saved logo</button>
                <label className={"btn btn-small " + (roLogoMode === "upload" ? "" : "btn-ghost")} style={{ cursor: roLogoBusy ? "default" : "pointer", opacity: roLogoBusy ? 0.6 : 1 }}>
                  {roLogoBusy ? "Uploading…" : "Upload a new logo"}
                  <input type="file" accept="image/*,.pdf,.ai,.eps,.svg" disabled={roLogoBusy} style={{ display: "none" }}
                    onChange={e => { const f = e.target.files && e.target.files[0]; e.target.value = ""; setRoLogoMode("upload"); onReorderLogoUpload(f); }} />
                </label>
              </div>

              {roLogoMode === "original" && (
                <div style={{ color: "var(--ink-3)", fontSize: 13 }}>We'll reuse the artwork from your original order.</div>
              )}

              {(roLogoMode === "library" || roLogoMode === "upload") && (
                roLogos === null ? (
                  <div style={{ color: "var(--ink-3)", fontSize: 13 }}>Loading your logos…</div>
                ) : roLogos.length === 0 ? (
                  <div style={{ color: "var(--ink-3)", fontSize: 13 }}>No saved logos yet — use “Upload a new logo” above, and it'll be saved to your library too.</div>
                ) : (
                  <div style={{ display: "flex", flexWrap: "wrap", gap: 10 }}>
                    {roLogos.map(logo => {
                      const selected = roLogoId === logo.id;
                      return (
                        <button key={logo.id} type="button" onClick={() => { setRoLogoId(logo.id); setRoErr(""); }}
                          title={logo.name}
                          style={{ width: 96, padding: 6, borderRadius: 8, cursor: "pointer", background: "var(--paper-2)",
                            border: selected ? "2px solid var(--sage-deep)" : "1px solid var(--line)" }}>
                          <div style={{ height: 56, background: "#fff", borderRadius: 6, overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }}>
                            {(logo.type || "").startsWith("image/")
                              ? <img src={logo.viewUrl} alt={logo.name} style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }} />
                              : <span style={{ color: "var(--ink-3)", fontSize: 10, padding: 4, textAlign: "center", wordBreak: "break-word" }}>{logo.fileName}</span>}
                          </div>
                          <div style={{ fontSize: 11, color: selected ? "var(--sage-deep)" : "var(--ink-2)", marginTop: 4, fontWeight: selected ? 700 : 500, wordBreak: "break-word", lineHeight: 1.2 }}>
                            {selected ? "✓ " : ""}{logo.name}
                          </div>
                        </button>
                      );
                    })}
                  </div>
                )
              )}
            </div>

            <div className="field">
              <label>Changes or add-ons for this order (optional)</label>
              <textarea value={roNotes} onChange={e => setRoNotes(e.target.value)} rows="3"
                placeholder="e.g. Same as last time, but add 2 in size L and switch the thread to navy." />
            </div>
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginTop: 4 }}>
              <button className="btn btn-small" disabled={roBusy} onClick={submitReorder}>
                {roBusy ? "Placing…" : "Place reorder"}
              </button>
              <button className="btn btn-ghost btn-small" disabled={roBusy} onClick={() => { setReordering(false); setRoErr(""); }}>
                Cancel
              </button>
            </div>
            {roErr && <div style={{ color: "var(--rose-deep)", fontSize: 13, marginTop: 8 }}>{roErr}</div>}

            {pickerTarget !== null && (
              <BlankPickerModal
                onClose={() => setPickerTarget(null)}
                onPick={(p) => {
                  if (pickerTarget === "base") { setRoBlank(p); setRoCatalog(null); }
                  else { patchItem(pickerTarget, { blank: p, catalogStyle: null }); }
                  setPickerTarget(null);
                }} />
            )}
          </div>
        )}
      </div>
    </div>
  );
}

function StatusStepper({ current }) {
  const i = Math.min(Math.max(0, current), ORDER_STAGES.length - 1);
  const stage = ORDER_STAGES[i];
  return (
    <div style={{ marginTop: 16 }}>
      <img
        src={`/img/tracker/tracker${i + 1}.jpg`}
        alt={`Order status: ${stage.label} — step ${i + 1} of ${ORDER_STAGES.length}`}
        style={{ width: "100%", height: "auto", display: "block", borderRadius: 8 }}
        loading="lazy"
      />
      <div className="smallcaps" style={{ textAlign: "center", color: "var(--sage-deep)", marginTop: 8, fontSize: 12 }}>
        {stage.label} — step {i + 1} of {ORDER_STAGES.length}
      </div>
    </div>
  );
}

// ---------- Invoices & receipts ----------
function InvoiceStatusBadge({ status }) {
  const map = {
    paid:  { label: "Paid",  bg: "var(--sage)", fg: "#fff" },
    open:  { label: "Due",   bg: "var(--gold)", fg: "#3a2a26" },
    void:  { label: "Void",  bg: "var(--line)", fg: "var(--ink-3)" },
    draft: { label: "Draft", bg: "var(--line)", fg: "var(--ink-3)" },
  };
  const s = map[status] || map.open;
  return <span style={{ background: s.bg, color: s.fg, fontSize: 12, padding: "2px 10px", borderRadius: 999, letterSpacing: ".03em" }}>{s.label}</span>;
}

function InvoicesSection() {
  const app = useApp();
  const [invoices, setInvoices] = useState(null); // null while loading
  const [err, setErr] = useState("");
  const [viewing, setViewing] = useState(null);

  useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const list = await app.accountInvoices();
        if (alive) setInvoices(list || []);
      } catch (e) {
        if (alive) { setErr(e.message || "Couldn't load your invoices right now."); setInvoices([]); }
      }
    })();
    return () => { alive = false; };
  }, []);

  return (
    <section className="section-tight">
      <div className="smallcaps" style={{ color: "var(--sage-deep)", marginBottom: 8 }}>Invoices &amp; receipts</div>
      <h2 style={{ marginBottom: 16 }}>Your <span className="script" style={{ color: "var(--rose-deep)" }}>invoices</span></h2>
      {invoices === null ? (
        <p style={{ color: "var(--ink-3)" }}>Loading your invoices…</p>
      ) : err ? (
        <p style={{ color: "var(--rose-deep)" }}>{err}</p>
      ) : invoices.length === 0 ? (
        <div className="stitched stitched-sage" style={{ textAlign: "center", padding: 40 }}>
          <p style={{ color: "var(--ink-2)", margin: 0 }}>No invoices yet. Receipts and invoices for your orders will appear here once issued.</p>
        </div>
      ) : (
        <div>{invoices.map(inv => <InvoiceRow key={inv.id} invoice={inv} onView={() => setViewing(inv)} />)}</div>
      )}

      <Modal open={!!viewing} onClose={() => setViewing(null)}>
        {viewing && (
          <div>
            <div className="no-print" style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginBottom: 12 }}>
              <button className="btn btn-small" onClick={() => window.print()}>Print / Save as PDF</button>
            </div>
            <InvoiceDocument invoice={viewing} tweaks={app.tweaks} />
            <div className="no-print" style={{ color: "var(--ink-3)", fontSize: 13, marginTop: 12, textAlign: "center" }}>
              Tip: choose “Save as PDF” as the destination in the print dialog to keep a copy.
            </div>
          </div>
        )}
      </Modal>
    </section>
  );
}

function InvoiceRow({ invoice, onView }) {
  const isReceipt = invoice.status === "paid";
  return (
    <div className="stitched" style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap", marginBottom: 12 }}>
      <div>
        <div className="mono" style={{ color: "var(--gold-deep)", fontSize: 13 }}>{invoice.number}</div>
        <div style={{ fontSize: 16, marginTop: 2 }}>{isReceipt ? "Receipt" : "Invoice"} · {fmtPrice(invoice.total)}</div>
        <div style={{ color: "var(--ink-3)", fontSize: 14 }}>
          Issued {fmtDate(invoice.issuedAt)}{invoice.orderRef ? ` · order ${invoice.orderRef}` : ""}
        </div>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <InvoiceStatusBadge status={invoice.status} />
        <button className="btn btn-ghost btn-small" onClick={onView}>View / Print</button>
      </div>
    </div>
  );
}

function InvoiceDocument({ invoice, tweaks }) {
  const isReceipt = invoice.status === "paid";
  const loc = (tweaks && tweaks.location) || "North Fort Myers, Florida";
  const email = (tweaks && tweaks.contactEmail) || "hazelbelleemb@gmail.com";
  const balance = Math.max(0, (invoice.total || 0) - (invoice.amountPaid || 0));
  const rows = invoice.items && invoice.items.length
    ? invoice.items
    : [{ id: "_", description: invoice.notes ? "Order" : "Custom order", qty: 1, unitPrice: invoice.total, amount: invoice.total }];

  return (
    <div className="invoice-doc">
      <div className="invoice-head">
        <div className="invoice-brand">
          <img src="assets/hazelbelle-logo.jpg" alt="Hazelbelle logo" />
          <div>
            <div className="invoice-brand-name script">Hazelbelle</div>
            <div className="invoice-brand-sub smallcaps">Embroidery &amp; Print</div>
            <div className="invoice-brand-meta">{loc}<br />{email}</div>
          </div>
        </div>
        <div className="invoice-meta">
          <div className="invoice-title">{isReceipt ? "Receipt" : "Invoice"}</div>
          <div className="invoice-number mono">{invoice.number}</div>
          <table><tbody>
            <tr><td>Issued</td><td>{fmtDate(invoice.issuedAt)}</td></tr>
            {invoice.dueAt && <tr><td>Due</td><td>{fmtDate(invoice.dueAt)}</td></tr>}
            {isReceipt && invoice.paidAt && <tr><td>Paid</td><td>{fmtDate(invoice.paidAt)}</td></tr>}
            {invoice.orderRef && <tr><td>Order</td><td>{invoice.orderRef}</td></tr>}
          </tbody></table>
        </div>
      </div>

      <div className="invoice-billto">
        <div className="smallcaps">Billed to</div>
        <div style={{ fontSize: 16 }}>{invoice.customerName || "—"}</div>
        <div style={{ color: "var(--ink-2)" }}>{invoice.customerEmail}</div>
        {invoice.shipTo && (
          <div style={{ marginTop: 10 }}>
            <div className="smallcaps">Ship to</div>
            <div style={{ color: "var(--ink-2)", whiteSpace: "pre-line" }}>{invoice.shipTo}</div>
          </div>
        )}
      </div>

      <table className="invoice-items">
        <thead>
          <tr><th>Description</th><th className="num">Qty</th><th className="num">Unit</th><th className="num">Amount</th></tr>
        </thead>
        <tbody>
          {rows.map(it => (
            <tr key={it.id}>
              <td>{it.description}</td>
              <td className="num">{it.qty}</td>
              <td className="num">{fmtPrice(it.unitPrice)}</td>
              <td className="num">{fmtPrice(it.amount)}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <div className="invoice-totals">
        <table><tbody>
          <tr><td>Subtotal</td><td className="num">{fmtPrice(invoice.subtotal)}</td></tr>
          {invoice.tax > 0 && <tr><td>Tax</td><td className="num">{fmtPrice(invoice.tax)}</td></tr>}
          <tr className="invoice-total-row"><td>Total</td><td className="num">{fmtPrice(invoice.total)}</td></tr>
          {invoice.amountPaid > 0 && <tr><td>Paid</td><td className="num">{fmtPrice(invoice.amountPaid)}</td></tr>}
          {!isReceipt && balance > 0 && <tr className="invoice-due-row"><td>Balance due</td><td className="num">{fmtPrice(balance)}</td></tr>}
        </tbody></table>
      </div>

      {isReceipt && <div className="invoice-paid-stamp">Paid — thank you!</div>}
      {invoice.status === "void" && <div className="invoice-void">This invoice has been voided.</div>}
      {invoice.notes && (
        <div className="invoice-notes">
          <div className="smallcaps">Notes</div>
          <div style={{ whiteSpace: "pre-wrap" }}>{invoice.notes}</div>
        </div>
      )}

      <div className="invoice-foot">Hazelbelle Embroidery &amp; Print · made with love, one stitch at a time</div>
    </div>
  );
}

window.AccountRouter = AccountRouter;
