/* AcquiCup — Rules, Auth / Onboarding */
const { useState, useEffect, useRef, useMemo } = React;

/* ============================== RULES ============================== */
function Rules({ go }) {
  const acqCfg = (window.ACQ && window.ACQ.CONFIG) || {};
  const exactMultiply = acqCfg.exactScoreMode !== "add"; // 'multiply' is the default
  const Section = ({ n, title, children }) => (
    <div className="row gap-20" style={{ alignItems: "flex-start" }}>
      <div className="mono" style={{ fontSize: 13, fontWeight: 600, color: "var(--accent)", width: 34, flex: "none", paddingTop: 3 }}>{n}</div>
      <div className="col gap-8 grow"><h3 style={{ fontSize: 19 }}>{title}</h3>{children}</div>
    </div>
  );
  const P = (props) => <p style={{ margin: 0, fontSize: 14.5, lineHeight: 1.6, color: "var(--ink-2)", maxWidth: 640 }} {...props} />;
  return (
    <div className="col gap-20" style={{ maxWidth: 820, margin: "0 auto" }}>
      <PageHead eyebrow="The contest" title="How AcquiCup works"
        sub="Everything you need to play, in plain language. Predict smart, swing for upsets, and climb the table." />
      <div className="card card-pad col" style={{ padding: "30px 30px", gap: 28 }}>
        <Section n="01" title="Predict before kickoff">
          <P>Predict the score of each match before it starts. You can fill in every upcoming match in advance and edit any prediction freely until that match kicks off. The moment a match begins, your prediction locks.</P>
        </Section>
        <hr className="hr" />
        <Section n="02" title="No downside">
          <P>Wrong prediction? <strong>0 points.</strong> No prediction? <strong>0 points.</strong> You never lose points. So the best strategy is simple — predict every single match.</P>
          <div className="card card-pad row gap-12" style={{ background: "color-mix(in srgb, var(--brand-yellow) 14%, var(--surface))", borderColor: "color-mix(in srgb,var(--brand-yellow) 40%,var(--line))", padding: "12px 14px", marginTop: 4 }}>
            <Icon name="flame" size={18} fill style={{ color: "var(--gold)" }} /><span style={{ fontSize: 13.5, fontWeight: 600 }}>No risk, only upside. There's never a reason to skip a match.</span>
          </div>
        </Section>
        <hr className="hr" />
        <Section n="03" title="Not all matches are equal">
          <P>Points follow a betting-odds principle. Predicting an obvious favourite earns fewer points. Predicting an unlikely upset earns many more.</P>
          <div className="card" style={{ overflow: "hidden", marginTop: 6, maxWidth: 420 }}>
            <div className="row between" style={{ padding: "10px 14px", borderBottom: "1px solid var(--line-2)" }}><span style={{ fontWeight: 700, fontSize: 14 }}>France vs Australia</span><span className="muted mono" style={{ fontSize: 12 }}>example odds</span></div>
            {[["France win", 12, false], ["Draw", 55, false], ["Australia win", 115, true]].map(([l, p, hot]) => (
              <div key={l} className="row between" style={{ padding: "10px 14px", borderTop: "1px solid var(--line-2)" }}>
                <span style={{ fontSize: 14 }}>{l}{hot && <span className="badge gold" style={{ marginLeft: 8 }}>big upset</span>}</span>
                <span className="mono" style={{ fontWeight: 700, color: hot ? "var(--accent)" : "var(--ink)" }}>{p} pts</span>
              </div>
            ))}
          </div>
        </Section>
        <hr className="hr" />
        <Section n="04" title="Bonus points">
          {exactMultiply ? (
            <P>Nail the precise scoreline — not just the winner — and your outcome points for that match are <strong>multiplied by {acqCfg.exactScoreMultiplier ?? 2}</strong>. Bigger upsets are worth even more when you call them exactly.</P>
          ) : (
            <P>On top of the outcome points, you earn a flat <strong>exact-score bonus</strong> for nailing the precise scoreline:</P>
          )}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(150px,1fr))", gap: 10, marginTop: 4 }}>
            {[["Exact score", exactMultiply ? `×${acqCfg.exactScoreMultiplier ?? 2}` : `+${acqCfg.exactScoreBonus ?? 20}`]].map(([l, v]) => (
              <div key={l} className="card card-pad col gap-2" style={{ padding: "12px 14px" }}><span className="mono" style={{ fontWeight: 700, fontSize: 18, color: "var(--accent)" }}>{v}</span><span className="muted" style={{ fontSize: 12.5 }}>{l}</span></div>
            ))}
          </div>
        </Section>
        <hr className="hr" />
        <Section n="05" title="The X2 bonus">
          <P>You get <strong>two X2 bonuses for the whole tournament</strong>. Apply each one to a match before kickoff — if that prediction earns points, they're doubled. Choose your moments: a doubled upset is how legends are made. An X2 can't be moved once its match locks, and once both are spent they're gone.</P>
        </Section>
        <hr className="hr" />
        <Section n="06" title="Two rankings, one goal">
          <P>You climb an <strong>individual</strong> ranking and contribute to your <strong>company</strong> ranking at the same time. Company score is the average of each company's top 3 players (minimum 3 to rank), so every team has a fair shot. And of course — the real goal is to beat the Acquisit team.</P>
          <div className="row gap-10" style={{ marginTop: 8 }}>
            <button className="btn btn-primary btn-sm" onClick={() => go("matches")}>Start predicting</button>
            <button className="btn btn-ghost btn-sm" onClick={() => go("leaderboard")}>View leaderboard</button>
          </div>
        </Section>
      </div>
    </div>
  );
}

/* ============================== AUTH ============================== */
// @acquisit.io players skip the "About you" step — they auto-join the Acquisit team (the
// server decides this from the verified email domain; this client helper only drives the UI).
const ACQUISIT_DOMAIN = "@acquisit.io";
const isAcquisitEmail = (e) => String(e == null ? "" : e).trim().toLowerCase().endsWith(ACQUISIT_DOMAIN);

// Mirrors the server password policy (auth.routes.passwordWeakReason) so the error shows on the
// "Your details" step, not at final submit. Policy: 6–128 chars + a number + a special character.
function passwordIssue(pw) {
  if (typeof pw !== "string" || pw.length < 6 || pw.length > 128) return "Password must be between 6 and 128 characters";
  if (!/\d/.test(pw)) return "Password must include at least one number";
  if (!/[^A-Za-z0-9]/.test(pw)) return "Password must include at least one special character";
  return null;
}

// Sign-up "predictions strategy" options. The id is sent to POST /api/auth/signup
// as `strategy`; the server seeds a starting scoreline per upcoming match from it
// (see server/src/strategy.js). Keep these ids in sync with SIGNUP_STRATEGIES.
// `example` is a concrete, illustrative scoreline shown in the auto-fill / auto-
// predict pop-ups so players see what each strategy actually produces. The fill
// strategies draw a RANDOM low scoreline (see server/src/strategy.js) — aggressive
// keeps a 1-goal underdog win, safe a 2-goal favourite win — so the examples list a
// couple of the possibilities rather than a single fixed score.
const STRATEGY_OPTIONS = [
  { id: "aggressive", title: "Aggressive", icon: "flame", desc: "Back the underdog to win every match by a narrow margin. Huge points when an upset lands.", example: "e.g. underdog wins 1–0, 2–1 or 3–2 (random, 1-goal margin)" },
  { id: "safe", title: "Safe", icon: "target", desc: "Back the favourite to win by a modest margin. Steady, lower-risk points.", example: "e.g. favourite wins 2–0, 3–1 or 4–2 (random, 2-goal margin)" },
  { id: "random", title: "Random", icon: "bolt", desc: "A random but plausible scoreline for every match. Let chaos pick your slate.", example: "e.g. 0–0, 1–2 or 2–1 (random low score each side)" },
  { id: "myself", title: "Let me do it myself", icon: "user", desc: "Start with a blank slate and predict each match by hand.", example: "Nothing is filled — every match stays blank for you to predict." },
];

/* GoogleButton — the OFFICIAL Google Identity Services "Sign in with Google" button.
   Google renders its own brand-compliant button inside an accounts.google.com iframe and
   returns a Google ID token via callback, which we exchange for a Firebase session with
   signInWithCredential (api.js). This first-party token flow does NOT use the cross-origin
   acquicup.firebaseapp.com popup/iframe relay, so it is immune to the third-party-cookie
   blocking (Safari ITP / Chrome) that breaks signInWithPopup when the app origin differs
   from the authDomain. While the async gsi/client script loads — or if it never loads — we
   show the legacy signInWithPopup button as a fallback, so there is always exactly one
   clickable Google control (loading → disabled placeholder; ready → official button;
   failed → popup-fallback button).
   ONE-TIME CONSOLE REQUIREMENT: the app origin must be listed under the OAuth Web client's
   "Authorized JavaScript origins" or GIS logs "origin not allowed" and the button is inert
   (see server/README.md "Enable Sign in with Google"). */
function GoogleButton({ mode, busy, onCredential, onFallbackClick }) {
  const ref = useRef(null);
  const cbRef = useRef(onCredential);
  cbRef.current = onCredential; // keep GIS's once-registered callback pointed at the latest handler
  const [state, setState] = useState("loading"); // "loading" | "ready" | "failed"
  const text = mode === "signup" ? "signup_with" : "continue_with";
  const fallbackLabel = mode === "signup" ? "Sign up with Google" : "Continue with Google";

  useEffect(() => {
    let cancelled = false;
    let tries = 0;
    function tick() {
      if (cancelled) return;
      const gid = window.google && window.google.accounts && window.google.accounts.id;
      if (gid && window.__ACQ_GOOGLE_CLIENT_ID && ref.current) {
        try {
          gid.initialize({
            client_id: window.__ACQ_GOOGLE_CLIENT_ID,
            callback: (resp) => { if (resp && resp.credential) cbRef.current(resp.credential); },
            auto_select: false,
            cancel_on_tap_outside: true,
            ux_mode: "popup",
          });
          ref.current.innerHTML = "";
          gid.renderButton(ref.current, { type: "standard", theme: "outline", size: "large", text: text, shape: "rectangular", logo_alignment: "left" });
          if (!cancelled) setState("ready");
        } catch (e) {
          if (!cancelled) setState("failed");
        }
        return;
      }
      tries += 1;
      if (tries > 50) { if (!cancelled) setState("failed"); return; } // ~5s, then offer the popup fallback
      setTimeout(tick, 100);
    }
    tick();
    return () => { cancelled = true; };
  }, [text]);

  return (
    <div className="col gap-6" style={{ alignItems: "stretch" }}>
      {/* GIS renders the official button here; kept mounted (hidden until ready) so renderButton has a target. */}
      <div ref={ref} style={{ display: state === "ready" ? "flex" : "none", justifyContent: "center" }} />
      {state !== "ready" && (
        <button className="btn btn-ghost full" style={{ justifyContent: "center" }} disabled={busy || state === "loading"} onClick={onFallbackClick} title={state === "loading" ? "Loading Google…" : undefined}>
          <span style={{ fontWeight: 700, color: "#4285F4" }}>G</span> {state === "loading" ? "Loading Google…" : fallbackLabel}
        </button>
      )}
    </div>
  );
}

function Auth({ onAuthed, startGoogleOnboard, initialMode = "signin", onBack }) {
  const [mode, setMode] = useState(initialMode);
  const [stepIdx, setStepIdx] = useState(0);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);
  const [notice, setNotice] = useState(null); // non-error message (e.g. "verify your email")
  const [isGoogle, setIsGoogle] = useState(false);
  const [googleEmail, setGoogleEmail] = useState(""); // Google account email — drives the Acquisit shortcut on the Google path
  // Company the typed email domain auto-joins ({id,name}); null = none/unknown. When set (and not
  // Acquisit) the "About you" step is skipped and the server places the user by domain.
  const [domainCo, setDomainCo] = useState(null);

  // Post-submit confirmation when the membership is PENDING admin approval (kept for the
  // @acquisit.io-less paths the server may still hold). { companyName, joinCode? } — joinCode
  // present only when the player FOUNDED a new company. null → no screen (active).
  const [done, setDone] = useState(null);

  const grabGoogleEmail = () => { try { return (firebase.auth().currentUser && firebase.auth().currentUser.email) || ""; } catch (e) { return ""; } };

  // A Google sign-in that completed via full-page redirect (popup fallback) lands here
  // with a session but no profile; app.jsx flags it so we open onboarding directly.
  useEffect(() => {
    if (startGoogleOnboard) { setIsGoogle(true); setGoogleEmail(grabGoogleEmail()); setMode("signup"); setStepIdx(0); }
  }, [startGoogleOnboard]);

  // Controlled sign-in inputs. Prefilling has been removed to avoid exposing administrative roles.
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  // Sign-up form. `joinCode` (top of "About you") joins INSTANTLY by code. Without a code the
  // player joins the shared "Friends of Acquisit" company (category 'friend') with an optional
  // free-text `affiliation` (company name). `strategy` seeds the starting predictions (default "safe").
  const [form, setForm] = useState({
    first: "", last: "", email: "", password: "",
    joinCode: "", affiliation: "",
    strategy: "safe",
  });
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  // The email deciding the Acquisit shortcut: typed (email path) or the Google account email.
  const signupEmail = isGoogle ? googleEmail : form.email;
  const acquisit = isAcquisitEmail(signupEmail);

  // Look up whether the email domain auto-joins a curated company (debounced) so the wizard can
  // skip "About you" — mirrors the Acquisit shortcut. Acquisit addresses are handled separately,
  // so they never trigger this lookup. Errors are swallowed (the About step just stays visible).
  useEffect(() => {
    const e = String(signupEmail || "").trim();
    if (mode !== "signup" || acquisit || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(e)) { setDomainCo(null); return; }
    let live = true;
    const t = setTimeout(() => {
      API.companyByDomain(e).then((r) => { if (live) setDomainCo((r && r.company) || null); }).catch(() => { if (live) setDomainCo(null); });
    }, 350);
    return () => { live = false; clearTimeout(t); };
  }, [signupEmail, acquisit, mode]);

  // A recognised email domain (or Acquisit) auto-assigns the team, so the "About you" step is skipped.
  const autoCompany = acquisit || !!domainCo;

  // The ordered steps for THIS run. Acquisit / a mapped email domain skip "about" (the team is
  // auto-assigned); Google skips "details" (name/email come from the token). Index nav runs over this.
  const steps = [];
  if (!isGoogle) steps.push("details");
  if (!autoCompany) steps.push("about");
  steps.push("strategy");
  const idx = Math.min(stepIdx, steps.length - 1);
  const step = steps[idx];

  function switchMode(m) {
    setMode(m);
    setStepIdx(0);
    setErr(null);
    setNotice(null);
    setIsGoogle(false);
    setDone(null);
  }

  function openGoogleOnboard() {
    setIsGoogle(true);
    setGoogleEmail(grabGoogleEmail());
    setMode("signup");
    setStepIdx(0);
  }

  async function handleSignIn() {
    setErr(null);
    if (!email || !password) { setErr("Email and password are required."); return; }
    setBusy(true);
    try {
      await API.login(email, password); // Firebase sign-in on the client
      await onAuthed();                  // bootstraps; throws (and signs out) on failure
      API.track("login", { method: "email" });
    } catch (e) {
      setErr((e && e.error) || "Invalid email or password");
    } finally {
      setBusy(false);
    }
  }

  async function handleForgotPassword() {
    setErr(null);
    setNotice(null);
    const target = email.trim();
    if (!target) { setErr("Enter your email above, then tap “Forgot password?”."); return; }
    setBusy(true);
    try {
      await API.sendPasswordReset(target);
      // Neutral message either way (the API swallows user-not-found) so we never reveal
      // whether an account exists for that address.
      setNotice("If an account exists for " + target + ", we've sent a password-reset link. Check your inbox.");
      API.track("password_reset_request");
    } catch (e) {
      setErr((e && e.error) || "Could not send the reset email");
    } finally {
      setBusy(false);
    }
  }

  async function handleGoogleSignIn() {
    setErr(null);
    setNotice(null);
    setBusy(true);
    try {
      const provider = new firebase.auth.GoogleAuthProvider();
      try {
        await firebase.auth().signInWithPopup(provider);
      } catch (e) {
        const code = e && e.code;
        // Popup blocked / unsupported (some webviews, strict popup blockers): fall
        // back to a full-page redirect. The page navigates away and onboarding resumes
        // on return (app.jsx completes getRedirectResult, then opens onboarding).
        if (code === "auth/popup-blocked" || code === "auth/operation-not-supported-in-this-environment") {
          await firebase.auth().signInWithRedirect(provider);
          return;
        }
        // User aborted (closed the popup) or double-clicked (a second popup cancelled the
        // first) — NOT a reason to navigate away; just stop.
        if (code === "auth/popup-closed-by-user" || code === "auth/cancelled-popup-request") { setBusy(false); return; }
        throw e;
      }
      try {
        await onAuthed();
        API.track("login", { method: "google" }); // existing user; first-timers go to onboarding (signup)
      } catch (e) {
        if (e && (e.code === "NO_PROFILE" || (e.status === 401 && e.error === "User not found"))) {
          openGoogleOnboard(); // first-time Google user → onboarding (details skipped)
        } else {
          throw e;
        }
      }
    } catch (e) {
      await API.signOut().catch(() => {});
      setErr((e && e.error) || "Google sign-in failed");
    } finally {
      setBusy(false);
    }
  }

  // Official-button path: exchange the Google Identity Services ID token for a Firebase
  // session, then run the SAME post-auth flow as handleGoogleSignIn — bootstrap, and on
  // NO_PROFILE (a first-time Google user) open the onboarding wizard (details skipped).
  async function handleGoogleCredential(idToken) {
    setErr(null);
    setNotice(null);
    setBusy(true);
    try {
      await API.signInWithGoogleCredential(idToken);
      try {
        await onAuthed();
        API.track("login", { method: "google" }); // existing user; first-timers go to onboarding (signup)
      } catch (e) {
        if (e && (e.code === "NO_PROFILE" || (e.status === 401 && e.error === "User not found"))) {
          openGoogleOnboard();
        } else {
          throw e;
        }
      }
    } catch (e) {
      await API.signOut().catch(() => {});
      setErr((e && e.error) || (e && e.message) || "Google sign-in failed");
    } finally {
      setBusy(false);
    }
  }

  function back() {
    setErr(null);
    if (idx > 0) { setStepIdx(idx - 1); return; }
    // At the first step: for the Google path there is no "details" to fall back to, so sign out
    // and return to the sign-in screen. (Email path's first step is "details" with the tabs.)
    if (isGoogle) { API.signOut().catch(() => {}); switchMode("signin"); }
  }

  function next() {
    setErr(null);
    if (step === "details") {
      if (!form.first || !form.last || !form.email || !form.password) {
        setErr("Please fill in your name, email and password."); return;
      }
      const pwIssue = passwordIssue(form.password);
      if (pwIssue) { setErr(pwIssue); return; }
    }
    // "about" needs no validation — a join code is optional; without one the user joins as a
    // Friend of Acquisit (with an optional free-text company name).
    setStepIdx(idx + 1);
  }

  async function handleSignUp() {
    setErr(null);
    if (!isGoogle && (!form.first || !form.last || !form.email || !form.password)) {
      setErr("Please fill in your name, email and password.");
      setStepIdx(0);
      return;
    }
    const payload = { strategy: form.strategy };
    // @acquisit.io players AND mapped-email-domain players: the server assigns the team from the
    // verified email, so we send nothing company-related. Everyone else: a join code joins
    // INSTANTLY; otherwise they join the shared "Friends of Acquisit" team (category 'friend')
    // with an optional free-text `affiliation`.
    if (!autoCompany) {
      const code = form.joinCode.trim();
      if (code) {
        payload.joinCode = code;
      } else {
        payload.category = "friend";
        if (form.affiliation.trim()) payload.affiliation = form.affiliation.trim();
      }
    }
    if (!isGoogle) {
      payload.first = form.first;
      payload.last = form.last;
      payload.email = form.email;
      payload.password = form.password;
    }

    setBusy(true);
    try {
      const res = isGoogle ? await API.googleSignup(payload) : await API.signup(payload);
      // Account created server-side — log the signup once here so it captures every
      // outcome (immediate entry, pending approval, or email-verification mode).
      API.track("signup", { method: isGoogle ? "google" : "email", strategy: form.strategy });
      // Email-verification mode (opt-in): signup does NOT sign the user in and returns a
      // neutral "check your inbox" response. Show it instead of bootstrapping.
      if (res && res.pendingVerification) {
        setErr(null);
        setNotice(res.message || "Check your email to verify your account, then sign in.");
        return;
      }
      // Pending admin approval (code-less client/partner join/create) — show the confirmation
      // screen (with the minted code to share, if they founded the company) before entering.
      if (res && res.pending) {
        setDone({
          companyName: res.pendingCompany || (res.createdCompany && res.createdCompany.name) || "your team",
          joinCode: res.createdCompany && res.createdCompany.joinCode,
        });
        return;
      }
      // Mark this as a fresh signup so the app shows the one-time onboarding tour.
      try { sessionStorage.setItem("acq:newSignup", "1"); } catch (_) { /* private mode */ }
      await onAuthed();          // bootstraps; throws (and signs out) on failure
    } catch (e) {
      setErr((e && e.error) || "Could not create your account");
    } finally {
      setBusy(false);
    }
  }

  async function enterAfterCreate() {
    setErr(null);
    setBusy(true);
    try {
      // A newly approved/created member entering the contest → show the tour once.
      try { sessionStorage.setItem("acq:newSignup", "1"); } catch (_) { /* private mode */ }
      await onAuthed();
    }
    catch (e) { setErr((e && e.error) || "Could not enter the contest"); setBusy(false); }
  }

  const Brand = () => (
    <div className="col gap-16" style={{ color: "#fff", maxWidth: 360 }}>
      <img src="logos/AcquiCup_logo.png" alt="AcquiCup" style={{ height: 30, width: "auto", alignSelf: "flex-start", filter: "brightness(0) invert(1)" }} />
      <div className="badge" style={{ background: "rgba(255,255,255,.12)", color: "#cfe2ff", alignSelf: "flex-start" }}>FIFA World Cup 2026 · Prediction Contest</div>
      <h1 style={{ fontSize: 40, fontWeight: 800, lineHeight: 1.05, letterSpacing: "-.03em" }}>Predict the World Cup. Climb the leaderboard.</h1>
      <p style={{ fontSize: 15, opacity: .85, lineHeight: 1.55 }}>Call every score, swing for upsets, and climb the leaderboard with your company. No downside — wrong picks cost nothing.</p>
      <div className="row gap-20" style={{ marginTop: 6 }}>
        <div className="col"><span className="mono" style={{ fontSize: 24, fontWeight: 700 }}>104</span><span style={{ opacity: .7, fontSize: 12 }}>matches</span></div>
        <div className="col"><span className="mono" style={{ fontSize: 24, fontWeight: 700 }}>48</span><span style={{ opacity: .7, fontSize: 12 }}>teams</span></div>
        <div className="col"><span className="mono" style={{ fontSize: 24, fontWeight: 700 }}>16</span><span style={{ opacity: .7, fontSize: 12 }}>host cities</span></div>
      </div>
    </div>
  );

  const Field = ({ label, ...p }) => (<div className="col grow" style={{ minWidth: 0 }}><label className="lbl">{label}</label><input className="field" {...p} /></div>);

  const errBanner = err ? (
    <div style={{ fontSize: 13, fontWeight: 600, color: "#c0392b", background: "color-mix(in srgb, #e74c3c 12%, var(--surface))", border: "1px solid color-mix(in srgb, #e74c3c 35%, var(--line))", borderRadius: 10, padding: "9px 12px" }}>{err}</div>
  ) : null;

  const noticeBanner = notice ? (
    <div style={{ fontSize: 13, fontWeight: 600, color: "var(--accent)", background: "var(--accent-soft)", border: "1px solid color-mix(in srgb, var(--accent) 35%, var(--line))", borderRadius: 10, padding: "9px 12px" }}>{notice}</div>
  ) : null;

  return (
    <div className="acq-root row auth-split" style={{ minHeight: "100vh", alignItems: "stretch" }}>
      {/* left brand panel */}
      <div style={{ flex: "1 1 0", background: "var(--brand-navy)", padding: "56px 56px", display: "flex", flexDirection: "column", justifyContent: "center", position: "relative", overflow: "hidden" }} className="auth-brand">
        <svg style={{ position: "absolute", right: -40, bottom: -40, opacity: .12 }} width="420" height="420" viewBox="0 0 420 420" fill="none" stroke="white" strokeWidth="1.5"><circle cx="210" cy="210" r="120" /><line x1="210" y1="0" x2="210" y2="420" /><rect x="120" y="120" width="180" height="180" /></svg>
        <Brand />
      </div>
      {/* right form */}
      <div style={{ flex: "1 1 0", background: "var(--bg)", display: "flex", alignItems: "center", justifyContent: "center", padding: "40px 28px" }}>
        <div className="card card-pad col gap-16" style={{ width: 400, maxWidth: "100%", padding: "28px 26px" }}>
          {/* brand mark — only shown on phones, where the left navy panel is hidden */}
          <img className="auth-mobile-brand" src="logos/AcquiCup_logo.png" alt="AcquiCup" style={{ height: 24, width: "auto", alignSelf: "center", filter: "var(--logo-filter)" }} />
          {onBack && (
            <button onClick={onBack} className="row gap-6" style={{ alignSelf: "flex-start", background: "none", border: "none", padding: 0, cursor: "pointer", color: "var(--ink-soft)", fontSize: 13, fontWeight: 600, fontFamily: "var(--ff)", alignItems: "center" }}>
              <Icon name="chevron" size={13} style={{ transform: "rotate(180deg)" }} />Back to home
            </button>
          )}
          {!isGoogle && (
            <div className="tabs tabs-full">
              <button className={mode === "signin" ? "on" : ""} onClick={() => switchMode("signin")}>Sign in</button>
              <button className={mode === "signup" ? "on" : ""} onClick={() => switchMode("signup")}>Create account</button>
            </div>
          )}

          {mode === "signin" ? (
            <div className="col gap-16">
              <div className="col gap-4" style={{ marginTop: 2 }}><h2 style={{ fontSize: 20 }}>Welcome back</h2><span className="muted" style={{ fontSize: 13.5 }}>Sign in to manage your predictions.</span></div>
              <div className="col"><label className="lbl">Email</label><input className="field" type="email" placeholder="you@company.com" value={email} onChange={(e) => setEmail(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSignIn(); }} /></div>
              <div className="col">
                <div className="row between" style={{ alignItems: "baseline" }}>
                  <label className="lbl">Password</label>
                  <button type="button" onClick={handleForgotPassword} disabled={busy} style={{ background: "none", border: "none", padding: 0, cursor: "pointer", color: "var(--accent)", fontSize: 12, fontWeight: 600, fontFamily: "var(--ff)" }}>Forgot password?</button>
                </div>
                <input className="field" type="password" placeholder="••••••••" required value={password} onChange={(e) => setPassword(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSignIn(); }} />
              </div>
              {errBanner}
              {noticeBanner}
              <button className="btn btn-primary full" style={{ justifyContent: "center" }} disabled={busy} onClick={handleSignIn}>{busy ? "Signing in…" : "Sign in"}</button>
              <div className="row gap-8" style={{ alignItems: "center" }}>
                <div style={{ height: 1, background: "var(--line)", flex: 1 }} />
                <span className="muted" style={{ fontSize: 12 }}>or</span>
                <div style={{ height: 1, background: "var(--line)", flex: 1 }} />
              </div>
              <GoogleButton mode="signin" busy={busy} onCredential={handleGoogleCredential} onFallbackClick={handleGoogleSignIn} />
            </div>
          ) : (
            <div className="col gap-16">
              {done ? (
                /* Pending admin approval (code-less client/partner join/create). Shows the minted
                   code to share if they founded the company; then they can enter the contest. */
                <div className="col gap-12">
                  <h2 style={{ fontSize: 19 }}>Almost there — pending approval</h2>
                  <p className="muted" style={{ fontSize: 13, margin: 0, lineHeight: 1.5 }}>Your request to join <strong>{done.companyName}</strong> has been sent to an admin. You can start predicting now — you'll be added to the team once it's approved.</p>
                  {done.joinCode && (
                    <>
                      <p className="muted" style={{ fontSize: 13, margin: 0, lineHeight: 1.5 }}>You created <strong>{done.companyName}</strong>. Share this code so colleagues can join instantly:</p>
                      <div className="row" style={{ justifyContent: "center", padding: "16px", background: "var(--surface-2)", border: "1px dashed var(--line)", borderRadius: 12 }}>
                        <span className="mono" style={{ fontSize: 26, fontWeight: 800, letterSpacing: ".18em", color: "var(--ink)" }}>{done.joinCode}</span>
                      </div>
                      <p className="muted" style={{ fontSize: 12, margin: 0 }}>Keep it safe — an admin can look it up again if you lose it.</p>
                    </>
                  )}
                  {errBanner}
                  <button className="btn btn-primary full" style={{ justifyContent: "center" }} disabled={busy} onClick={enterAfterCreate}>{busy ? "Entering…" : "Enter the contest"}<Icon name="chevron" size={14} /></button>
                </div>
              ) : (
              <>
              {/* progress */}
              <div className="row gap-6">{steps.map((s, i) => <div key={s} className="meter grow"><i style={{ width: i <= idx ? "100%" : "0%" }}></i></div>)}</div>
              {step === "details" && !isGoogle && (
                <div className="col gap-12">
                  <h2 style={{ fontSize: 19 }}>Your details</h2>
                  <div className="row gap-10 auth-names">{Field({ label: "First name", value: form.first, onChange: (e) => set("first", e.target.value), placeholder: "Alex" })}{Field({ label: "Last name", value: form.last, onChange: (e) => set("last", e.target.value), placeholder: "Morgan" })}</div>
                  {Field({ label: "Company email", type: "email", value: form.email, onChange: (e) => set("email", e.target.value), placeholder: "you@company.com" })}
                  {Field({ label: "Password", type: "password", value: form.password, onChange: (e) => set("password", e.target.value), placeholder: "••••••••", required: true })}
                  <span className="muted" style={{ fontSize: 12, marginTop: -4 }}>At least 6 characters, including a number and a special character.</span>
                  {acquisit && <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.4 }}><Icon name="check" size={13} stroke={2.4} style={{ color: "var(--accent)" }} /> Recognised as Acquisit — you'll join the Acquisit team.</div>}
                  {!acquisit && domainCo && <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.4 }}><Icon name="check" size={13} stroke={2.4} style={{ color: "var(--accent)" }} /> Recognised — you'll join the <strong>{domainCo.name}</strong> team.</div>}
                  {errBanner}
                  <button className="btn btn-primary full" style={{ justifyContent: "center" }} onClick={next}>Continue<Icon name="chevron" size={14} /></button>
                  <div className="row gap-8" style={{ alignItems: "center" }}>
                    <div style={{ height: 1, background: "var(--line)", flex: 1 }} />
                    <span className="muted" style={{ fontSize: 12 }}>or</span>
                    <div style={{ height: 1, background: "var(--line)", flex: 1 }} />
                  </div>
                  <GoogleButton mode="signup" busy={busy} onCredential={handleGoogleCredential} onFallbackClick={handleGoogleSignIn} />
                </div>
              )}
              {step === "about" && (
                <div className="col gap-12">
                  <h2 style={{ fontSize: 19 }}>About you</h2>

                  {/* Path A — join instantly with a code. */}
                  <div className="col">
                    <label className="lbl">Have a join code? <span className="muted" style={{ fontWeight: 400 }}>(optional)</span></label>
                    <input className="field" placeholder="e.g. ABC123" value={form.joinCode}
                      onChange={(e) => set("joinCode", e.target.value)}
                      style={{ textTransform: "uppercase", letterSpacing: ".08em" }} />
                    <span className="muted" style={{ fontSize: 12, marginTop: 4 }}>Joins you to your team instantly. Your organiser can share it.</span>
                  </div>

                  {/* OR separator */}
                  <div className="row gap-12" style={{ alignItems: "center" }}>
                    <div style={{ height: 1, background: "var(--line)", flex: 1 }} />
                    <span className="muted" style={{ fontSize: 16, fontWeight: 700, letterSpacing: ".12em" }}>OR</span>
                    <div style={{ height: 1, background: "var(--line)", flex: 1 }} />
                  </div>

                  {/* Path B — no code → join as a Friend of Acquisit (individual, optional company). */}
                  <div className="col gap-10" style={{ opacity: form.joinCode.trim() ? 0.5 : 1 }}>
                    <div className="row gap-12" style={{ alignItems: "flex-start", padding: "12px 14px", borderRadius: 12, background: "var(--surface-2)", border: "1px solid var(--line)" }}>
                      <Icon name="star" size={20} style={{ color: "var(--ink-2)", flex: "none", marginTop: 1 }} />
                      <div className="col grow" style={{ gap: 2, minWidth: 0 }}>
                        <span style={{ fontSize: 14, fontWeight: 700, color: "var(--ink)" }}>Friend of Acquisit</span>
                        <span className="muted" style={{ fontSize: 12.5, lineHeight: 1.4 }}>No code? You'll join the "Friends of Acquisit" team — competing on the individual leaderboard and as a squad on the company board.</span>
                      </div>
                    </div>
                    <div className="col">
                      <label className="lbl">Company name <span className="muted" style={{ fontWeight: 400 }}>(optional)</span></label>
                      <input className="field" placeholder="e.g. Helios Partners" value={form.affiliation} onChange={(e) => set("affiliation", e.target.value)} disabled={!!form.joinCode.trim()} />
                    </div>
                  </div>
                  {errBanner}
                  <div className="row gap-10"><button className="btn btn-ghost" onClick={back}>Back</button><button className="btn btn-primary grow" style={{ justifyContent: "center" }} onClick={next}>Continue<Icon name="chevron" size={14} /></button></div>
                </div>
              )}
              {step === "strategy" && (
                <div className="col gap-12">
                  <h2 style={{ fontSize: 19 }}>Predictions strategy</h2>
                  <p className="muted" style={{ fontSize: 13, margin: 0, lineHeight: 1.5 }}>This sets how your predictions start. You can change any prediction later.</p>
                  {acquisit && <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.4 }}><Icon name="check" size={13} stroke={2.4} style={{ color: "var(--accent)" }} /> You'll join the Acquisit team.</div>}
                  {!acquisit && domainCo && <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.4 }}><Icon name="check" size={13} stroke={2.4} style={{ color: "var(--accent)" }} /> You'll join the <strong>{domainCo.name}</strong> team.</div>}
                  <div className="col gap-8">{STRATEGY_OPTIONS.map((o) => {
                    const on = form.strategy === o.id;
                    return (
                      <button key={o.id} onClick={() => set("strategy", o.id)} className="row gap-12" style={{ alignItems: "flex-start", padding: "12px 14px", borderRadius: 12, cursor: "pointer", textAlign: "left", background: on ? "var(--accent-soft)" : "var(--surface-2)", border: "1px solid " + (on ? "var(--accent)" : "var(--line)"), fontFamily: "var(--ff)" }}>
                        <Icon name={o.icon} size={20} style={{ color: on ? "var(--accent)" : "var(--ink-2)", flex: "none", marginTop: 1 }} />
                        <div className="col grow" style={{ gap: 2, minWidth: 0 }}>
                          <span style={{ fontSize: 14, fontWeight: 700, color: on ? "var(--accent)" : "var(--ink)" }}>{o.title}</span>
                          <span className="muted" style={{ fontSize: 12.5, lineHeight: 1.4 }}>{o.desc}</span>
                          {o.example && <span className="mono" style={{ fontSize: 11.5, lineHeight: 1.4, color: on ? "var(--accent)" : "var(--ink-soft)", marginTop: 2 }}>{o.example}</span>}
                        </div>
                        {on && <Icon name="check" size={16} stroke={2.5} style={{ color: "var(--accent)", flex: "none", marginTop: 2 }} />}
                      </button>
                    );
                  })}</div>
                  {errBanner}
                  {noticeBanner}
                  <div className="row gap-10"><button className="btn btn-ghost" disabled={busy} onClick={back}>Back</button><button className="btn btn-primary grow" style={{ justifyContent: "center" }} disabled={busy} onClick={handleSignUp}>{busy ? "Entering…" : "Enter the contest"}<Icon name="chevron" size={14} /></button></div>
                </div>
              )}
              </>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ============================== SETTINGS ============================== */
// The account page (/settings). Stacks a set of self-service cards:
//   • Profile  — edit your name, nationality, and country of residence (PUT /api/auth/me).
//   • Account  — read-only email + company/membership (changing either is an admin task).
//   • Timezone — the DEFAULT display zone saved on the account (cross-device default; the
//     header globe dropdown still overrides it for THIS device only).
//   • Password — change your password via the Firebase Web SDK (shown only for password
//     accounts; Google sign-ins have no password). Reauth happens client-side.
//   • Danger zone — permanently delete your account (hard delete; typed confirmation).
// All match dates/times across the app re-render off the active zone (window.* timezone
// helpers in components.jsx).

// Shared card wrapper: an icon chip + title/subtitle header, then arbitrary children.
function SettingsCard({ icon, title, sub, tone = "accent", children }) {
  const danger = tone === "danger";
  return (
    <div className="card card-pad col gap-12" style={danger ? { borderColor: "#E7B7B0" } : undefined}>
      <div className="row gap-10" style={{ alignItems: "center" }}>
        <div style={{ width: 38, height: 38, borderRadius: 11,
          background: danger ? "#FBE3E3" : "var(--accent-soft)",
          color: danger ? "#C0392B" : "var(--accent)",
          display: "flex", alignItems: "center", justifyContent: "center", flex: "none" }}>
          <Icon name={icon} size={20} />
        </div>
        <div className="col" style={{ lineHeight: 1.25 }}>
          <h3 style={{ fontSize: 17 }}>{title}</h3>
          {sub ? <span className="muted" style={{ fontSize: 13 }}>{sub}</span> : null}
        </div>
      </div>
      {children}
    </div>
  );
}

// One read-only label/value line (used for the email/company display rows).
function SettingsRow({ label, value }) {
  return (
    <div className="row between" style={{ padding: "9px 0", borderBottom: "1px solid var(--line)" }}>
      <span className="muted" style={{ fontSize: 13 }}>{label}</span>
      <span className="mono" style={{ fontSize: 13, fontWeight: 600 }}>{value}</span>
    </div>
  );
}

function Settings({ store, refresh, flash, onLogout }) {
  const me = (store && store.me) || (window.ACQ && window.ACQ.me) || {};
  const isPasswordAccount = useMemo(() => API.hasPasswordProvider(), []);
  return (
    <div className="col gap-20" style={{ maxWidth: 980 }}>
      <PageHead eyebrow="Account" title="Settings"
        sub="Manage your profile and security, and choose how match dates and times are shown to you." />
      {/* Two columns on wide screens (flex-wrap collapses to one on narrow). Left = the
          things you edit (profile + password); right = account facts + display prefs. */}
      <div className="row gap-20" style={{ alignItems: "flex-start", flexWrap: "wrap" }}>
        <div className="col gap-20" style={{ flex: "1 1 380px", minWidth: 0 }}>
          <ProfileCard me={me} refresh={refresh} flash={flash} />
          {isPasswordAccount ? <PasswordCard flash={flash} /> : null}
        </div>
        <div className="col gap-20" style={{ flex: "1 1 380px", minWidth: 0 }}>
          <AccountCard me={me} />
          <TimezoneCard me={me} refresh={refresh} flash={flash} />
        </div>
      </div>
      <DangerZoneCard me={me} flash={flash} onLogout={onLogout} />
    </div>
  );
}

// --- Profile: editable name + nationality + country of residence ---
function ProfileCard({ me, refresh, flash }) {
  const [first, setFirst] = useState(me.first || "");
  const [last, setLast] = useState(me.last || "");
  const [nationality, setNationality] = useState(me.nationality || "");
  const [country, setCountry] = useState(me.country || "");
  const [busy, setBusy] = useState(false);

  // Re-sync from the bootstrap if the underlying profile changes (e.g. after a refresh).
  useEffect(() => { setFirst(me.first || ""); setLast(me.last || "");
    setNationality(me.nationality || ""); setCountry(me.country || ""); },
    [me.first, me.last, me.nationality, me.country]);

  const trimmed = { first: first.trim(), last: last.trim(), nationality: nationality.trim(), country: country.trim() };
  const dirty = trimmed.first !== (me.first || "") || trimmed.last !== (me.last || "")
    || trimmed.nationality !== (me.nationality || "") || trimmed.country !== (me.country || "");
  const valid = trimmed.first.length > 0 && trimmed.last.length > 0;

  async function save() {
    if (!valid || !dirty) return;
    setBusy(true);
    try {
      // Send only the IANA-safe identity fields. Empty nationality/country clear them.
      await API.updateMe({ first: trimmed.first, last: trimmed.last,
        nationality: trimmed.nationality || null, country: trimmed.country });
      await refresh();
      if (flash) flash("Profile saved");
    } catch (e) {
      if (flash) flash((e && e.error) || "Could not save your profile");
    } finally {
      setBusy(false);
    }
  }

  return (
    <SettingsCard icon="user" title="Profile" sub="Your name and where you're from, shown on leaderboards and your public profile.">
      <div className="row gap-10 auth-names">
        <div className="col grow" style={{ minWidth: 0 }}>
          <label className="lbl">First name</label>
          <input className="field" value={first} maxLength={80} onChange={(e) => setFirst(e.target.value)} placeholder="Alex" />
        </div>
        <div className="col grow" style={{ minWidth: 0 }}>
          <label className="lbl">Last name</label>
          <input className="field" value={last} maxLength={80} onChange={(e) => setLast(e.target.value)} placeholder="Morgan" />
        </div>
      </div>
      <div className="row gap-10 auth-names">
        <div className="col grow" style={{ minWidth: 0 }}>
          <label className="lbl">Nationality</label>
          <CountryCombo value={nationality} onChange={setNationality} ariaLabel="Nationality" placeholder="Select your nationality…" />
        </div>
        <div className="col grow" style={{ minWidth: 0 }}>
          <label className="lbl">Country of residence</label>
          <CountryCombo value={country} onChange={setCountry} ariaLabel="Country of residence" placeholder="Select your country…" />
        </div>
      </div>
      {!valid ? <span className="muted" style={{ fontSize: 12, color: "#C0392B" }}>First and last name are required.</span> : null}
      <div className="row gap-10" style={{ marginTop: 4 }}>
        <button className="btn btn-primary" disabled={busy || !dirty || !valid} onClick={save} style={{ justifyContent: "center" }}>
          {busy ? "Saving…" : "Save profile"}<Icon name="check" size={14} stroke={2.4} />
        </button>
      </div>
    </SettingsCard>
  );
}

// --- Account: read-only email + company/membership ---
function AccountCard({ me }) {
  // Resolve the company display name from the bootstrap COMPANIES list (the `me` object
  // carries only the company id). Pending (unapproved) members show the requested name.
  const companies = (window.ACQ && window.ACQ.COMPANIES) || [];
  const company = companies.find((c) => c.id === me.company) || null;
  let membership;
  if (me.pending) membership = (me.pendingCompany || "—") + " · pending approval";
  else if (company) membership = company.name;
  else if (me.affiliation) membership = me.affiliation + " (individual)";
  else membership = "— none —";

  return (
    <SettingsCard icon="building" title="Account" sub="Your sign-in email and team. Contact an admin to change these.">
      <div className="col" style={{ marginTop: 2 }}>
        <SettingsRow label="Email" value={me.email || "—"} />
        <SettingsRow label="Company / team" value={membership} />
        {me.category ? <SettingsRow label="Category" value={me.category} /> : null}
      </div>
      <span className="muted" style={{ fontSize: 12 }}>
        Email is your login and can't be changed here. Need a different email or company? Ask a contest admin.
      </span>
    </SettingsCard>
  );
}

// --- Timezone: the saved cross-device default display zone ---
function TimezoneCard({ me, refresh, flash }) {
  const active = useTimezone();
  const detected = useMemo(() => detectTimezone(), []);
  const accountDefault = me.timezone || "";
  const [choice, setChoice] = useState(accountDefault || detected);
  const [busy, setBusy] = useState(false);

  // Re-sync the picker if the saved default changes (e.g. after a refresh).
  useEffect(() => { setChoice(me.timezone || detected); }, [me.timezone, detected]);

  async function saveDefault() {
    setBusy(true);
    try {
      await API.updateMe({ timezone: choice });
      setTimezone(choice); // apply to this device immediately so the UI reflects it
      await refresh();
      if (flash) flash("Default timezone saved");
    } catch (e) {
      if (flash) flash((e && e.error) || "Could not save timezone");
    } finally {
      setBusy(false);
    }
  }

  async function clearDefault() {
    setBusy(true);
    try {
      await API.updateMe({ timezone: null });
      resetTimezone(); // drop the per-device pin → back to auto-detected
      await refresh();
      if (flash) flash("Reset to auto-detected timezone");
    } catch (e) {
      if (flash) flash((e && e.error) || "Could not reset timezone");
    } finally {
      setBusy(false);
    }
  }

  const dirty = (choice || "") !== (accountDefault || "");

  return (
    <SettingsCard icon="globe" title="Timezone" sub="Your default display zone, saved to your account.">
      <div className="col" style={{ marginTop: 4 }}>
        <SettingsRow label="Currently showing" value={active.replace(/_/g, " ") + " · " + tzMetaLabel(active, Date.now())} />
        <SettingsRow label="Auto-detected (this browser)" value={detected.replace(/_/g, " ") + " · " + tzMetaLabel(detected, Date.now())} />
        <SettingsRow label="Saved account default" value={accountDefault ? accountDefault.replace(/_/g, " ") + " · " + tzMetaLabel(accountDefault, Date.now()) : "— none (auto) —"} />
      </div>

      <label className="col gap-6" style={{ marginTop: 4 }}>
        <span style={{ fontWeight: 600, fontSize: 13.5 }}>Default timezone</span>
        <TimezoneCombo value={choice} onChange={setChoice} ariaLabel="Default timezone" />
        <span className="muted" style={{ fontSize: 12 }}>
          Tip: you can switch the view on any page from the globe dropdown in the header — that change
          stays on this device. Saving here sets your default everywhere.
        </span>
      </label>

      <div className="row gap-10" style={{ marginTop: 4 }}>
        <button className="btn btn-primary" disabled={busy || !dirty} onClick={saveDefault} style={{ justifyContent: "center" }}>
          {busy ? "Saving…" : "Save default"}<Icon name="check" size={14} stroke={2.4} />
        </button>
        <button className="btn btn-ghost" disabled={busy || (!accountDefault && !choice)} onClick={clearDefault}>
          Reset to auto-detected
        </button>
      </div>
    </SettingsCard>
  );
}

// --- Password: client-side change via Firebase reauth (password accounts only) ---
function PasswordCard({ flash }) {
  const [current, setCurrent] = useState("");
  const [next, setNext] = useState("");
  const [confirm, setConfirm] = useState("");
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);

  const policyIssue = next ? passwordIssue(next) : null; // mirrors the server policy
  const mismatch = confirm.length > 0 && next !== confirm;
  const ready = current.length > 0 && next.length > 0 && confirm.length > 0 && !policyIssue && !mismatch;

  async function change() {
    setErr(null);
    if (policyIssue) { setErr(policyIssue); return; }
    if (mismatch) { setErr("The new passwords don't match."); return; }
    setBusy(true);
    try {
      await API.changePassword(current, next);
      setCurrent(""); setNext(""); setConfirm("");
      if (flash) flash("Password changed");
    } catch (e) {
      setErr((e && e.error) || "Could not change your password");
    } finally {
      setBusy(false);
    }
  }

  return (
    <SettingsCard icon="lock" title="Password" sub="Change the password you use to sign in.">
      <div className="col gap-10">
        <div className="col">
          <label className="lbl">Current password</label>
          <input className="field" type="password" autoComplete="current-password" value={current}
            onChange={(e) => setCurrent(e.target.value)} placeholder="••••••••" />
        </div>
        <div className="col">
          <label className="lbl">New password</label>
          <input className="field" type="password" autoComplete="new-password" value={next}
            onChange={(e) => setNext(e.target.value)} placeholder="••••••••" />
        </div>
        <div className="col">
          <label className="lbl">Confirm new password</label>
          <input className="field" type="password" autoComplete="new-password" value={confirm}
            onChange={(e) => setConfirm(e.target.value)} placeholder="••••••••"
            onKeyDown={(e) => { if (e.key === "Enter" && ready && !busy) change(); }} />
        </div>
        <span className="muted" style={{ fontSize: 12 }}>At least 6 characters, including a number and a special character.</span>
        {err ? <span style={{ fontSize: 12.5, color: "#C0392B", fontWeight: 600 }}>{err}</span> : null}
        <div className="row gap-10" style={{ marginTop: 2 }}>
          <button className="btn btn-primary" disabled={busy || !ready} onClick={change} style={{ justifyContent: "center" }}>
            {busy ? "Changing…" : "Change password"}<Icon name="check" size={14} stroke={2.4} />
          </button>
        </div>
      </div>
    </SettingsCard>
  );
}

// --- Danger zone: permanent account deletion (hard delete, typed confirmation) ---
function DangerZoneCard({ me, flash, onLogout }) {
  const [open, setOpen] = useState(false);
  const [confirm, setConfirm] = useState("");
  const [busy, setBusy] = useState(false);
  const ready = confirm.trim().toUpperCase() === "DELETE";

  async function del() {
    if (!ready) return;
    setBusy(true);
    try {
      await API.deleteAccount();
      API.track("delete_account"); // log before onLogout tears the session down
      // The Firebase Auth user is gone server-side; tear the local session down and
      // return to the marketing gate. onLogout best-effort-revokes + signs out + resets UI.
      if (onLogout) await onLogout();
      else { await API.clearToken().catch(() => {}); window.location.assign("/"); }
    } catch (e) {
      if (flash) flash((e && e.error) || "Could not delete your account");
      setBusy(false); // leave the panel open so the user can read the error / retry
    }
  }

  return (
    <SettingsCard icon="trash" tone="danger" title="Delete account"
      sub="Permanently remove your account, predictions, and history. This cannot be undone.">
      {!open ? (
        <div className="row">
          <button className="btn btn-ghost" style={{ color: "#C0392B", borderColor: "#E7B7B0" }} onClick={() => setOpen(true)}>
            Delete my account
          </button>
        </div>
      ) : (
        <div className="col gap-10">
          <span style={{ fontSize: 13 }}>
            This will delete <strong>{me.first} {me.last}</strong> and all of your predictions — for good.
            Type <strong>DELETE</strong> to confirm.
          </span>
          <input className="field" value={confirm} onChange={(e) => setConfirm(e.target.value)}
            placeholder="DELETE" autoComplete="off"
            onKeyDown={(e) => { if (e.key === "Enter" && ready && !busy) del(); }} />
          <div className="row gap-10">
            <button className="btn" disabled={busy || !ready} onClick={del}
              style={{ justifyContent: "center", background: "#C0392B", color: "#fff", opacity: (busy || !ready) ? 0.55 : 1 }}>
              {busy ? "Deleting…" : "Permanently delete"}<Icon name="trash" size={14} stroke={2.2} />
            </button>
            <button className="btn btn-ghost" disabled={busy} onClick={() => { setOpen(false); setConfirm(""); }}>
              Cancel
            </button>
          </div>
        </div>
      )}
    </SettingsCard>
  );
}

// STRATEGY_OPTIONS is shared with the in-app bulk "auto-fill" action (see
// PredictionBulkActions in screens-core.jsx), which reuses the same titles/icons
// minus 'myself' (an empty-slate choice that fills nothing).
Object.assign(window, { Rules, Auth, GoogleButton, STRATEGY_OPTIONS, Settings });
