/* ============================================================================
   MyTraderOS — Wave 3 pages (L0 completion)
   Auth forms · Onboarding · Connect wizard · Import & Sync · Settings · Insights · Unlock
   โหลดหลัง app.jsx (shared global scope · babel-standalone classic scripts)
   → อ้าง helper/state components/App จาก app.jsx ได้ตรง · render อยู่ท้ายไฟล์นี้
   ทุกเลข/ลิสต์มาจาก mock API · client format/วาดเท่านั้น
   ============================================================================ */

/* ------------------------------------------------------ shared bits ------ */
function StepDots({ current, total }) {
  return <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
    {Array.from({ length: total }).map((_, i) => <span key={i} style={{
      width: i === current ? 22 : 8, height: 8, borderRadius: 999,
      background: i === current ? "var(--brand-grad)" : i < current ? "var(--brand-edge)" : "var(--surface-3)",
      transition: "all var(--dur-base) var(--ease)"
    }} />)}
  </div>;
}
function FieldErr({ msg }) { return msg ? <div style={{ color: "var(--loss)", fontSize: "var(--fs-micro)", marginTop: 3 }}>{msg}</div> : null; }
function Banner({ tone, children }) {
  const c = tone === "error" ? { bg: "#fdecef", bd: "var(--loss)", ink: "var(--loss)" } : tone === "warn" ? { bg: "rgba(200,163,90,.14)", bd: "#c8a35a", ink: "#8a6d2f" } : { bg: "var(--brand-soft)", bd: "var(--brand-edge)", ink: "var(--brand-strong)" };
  return <div style={{ background: c.bg, border: "1px solid " + c.bd, color: c.ink, borderRadius: "var(--radius)", padding: "var(--sp-3) var(--sp-4)", fontSize: "var(--fs-caption)", display: "flex", gap: 8, alignItems: "flex-start" }}>
    <span aria-hidden="true">{tone === "error" ? "⚠" : tone === "warn" ? "⚠" : "🔒"}</span><span>{children}</span>
  </div>;
}
function fmtPlanPrice(cents, currency) { const sym = currency === "THB" ? "฿" : "$"; return sym + (cents / 100).toLocaleString("en-US", { maximumFractionDigits: 0 }); }

/* ===========================================================================
   AuthFormPage — /login + /register (mock · error รายฟิลด์ไทย · ?next=)
   =========================================================================== */
function AuthFormPage({ t, mode, onLogin }) {
  const isReg = mode === "register";
  const params = new URLSearchParams(window.location.search);
  const next = params.get("next") || "/app";
  const [f, setF] = useState({ email: "", password: "", confirm: "", invite: "", pdpa: false, marketing: false });
  const [err, setErr] = useState({});
  const [policyOpen, setPolicyOpen] = useState(false);
  const [forgotNote, setForgotNote] = useState(false);
  const [topErr, setTopErr] = useState(null);
  const INVITE_FLAG = true; // mock: beta invite เปิด
  const set = (k, v) => setF((o) => Object.assign({}, o, { [k]: v }));

  const validate = () => {
    const e = {};
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(f.email)) e.email = t("auth.err_email");
    if (!f.password || f.password.length < 8) e.password = t("auth.err_password");
    if (isReg) {
      if (f.confirm !== f.password) e.confirm = t("auth.err_confirm");
      if (INVITE_FLAG && f.invite.trim().toUpperCase() !== "MTOS-BETA") e.invite = t("auth.err_invite");
      if (!f.pdpa) e.pdpa = t("auth.err_pdpa");
    }
    setErr(e); return Object.keys(e).length === 0;
  };
  const submit = () => {
    setTopErr(null);
    if (!validate()) return;
    if (!isReg && f.password === "wrong") { setTopErr(t("auth.invalid_credentials")); return; }
    try { localStorage.setItem("mtos_user", JSON.stringify({ email: f.email })); } catch (x) {}
    onLogin();
    navigate(isReg ? "/app/onboarding" : next, true);
  };
  const inpErr = (k) => err[k] ? { borderColor: "var(--loss)" } : null;

  return <div style={{ minHeight: "100dvh", display: "flex", alignItems: "center", justifyContent: "center", padding: "var(--sp-5)", background: "var(--bg-grad)" }}>
    <div className="card" style={{ width: "100%", maxWidth: 400 }}>
      <div style={{ marginBottom: "var(--sp-4)" }}><Logo /></div>
      <h1 style={{ fontSize: "var(--fs-h1)", marginBottom: "var(--sp-4)" }}>{t(isReg ? "auth.register_title" : "auth.login_title")}</h1>

      {topErr && <div style={{ marginBottom: "var(--sp-3)" }}><Banner tone="error">{topErr}</Banner></div>}

      <label className="lbl" style={{ marginTop: 0 }}>{t("auth.email")}</label>
      <input className="inp" style={inpErr("email")} type="email" autoComplete="username" placeholder={t("auth.email_ph")} value={f.email} onChange={(e) => set("email", e.target.value)} data-testid="email" />
      <FieldErr msg={err.email} />

      <label className="lbl">{t("auth.password")}</label>
      <input className="inp" style={inpErr("password")} type="password" autoComplete={isReg ? "new-password" : "current-password"} placeholder={t("auth.password_ph")} value={f.password} onChange={(e) => set("password", e.target.value)} data-testid="password" />
      <FieldErr msg={err.password} />

      {isReg && <React.Fragment>
        <label className="lbl">{t("auth.confirm_password")}</label>
        <input className="inp" style={inpErr("confirm")} type="password" autoComplete="new-password" placeholder={t("auth.confirm_password_ph")} value={f.confirm} onChange={(e) => set("confirm", e.target.value)} data-testid="confirm" />
        <FieldErr msg={err.confirm} />

        <label className="lbl">{t("auth.invite_code")}</label>
        <input className="inp" style={inpErr("invite")} type="text" autoComplete="off" placeholder={t("auth.invite_ph")} value={f.invite} onChange={(e) => set("invite", e.target.value)} data-testid="invite" />
        <div style={{ fontSize: "var(--fs-micro)", color: "var(--ink-subtle)", marginTop: 3 }}>{t("auth.invite_hint")}</div>
        <FieldErr msg={err.invite} />

        <label style={{ display: "flex", gap: 8, alignItems: "flex-start", margin: "var(--sp-4) 0 0", fontSize: "var(--fs-caption)", color: "var(--ink-dim)", cursor: "pointer" }}>
          <input type="checkbox" checked={f.pdpa} onChange={(e) => set("pdpa", e.target.checked)} style={{ width: 18, height: 18, marginTop: 2, flex: "0 0 auto" }} data-testid="pdpa" />
          <span>{t("auth.pdpa_pre")} <a href="#" onClick={(e) => { e.preventDefault(); setPolicyOpen(true); }} style={{ color: "var(--brand-bright)", fontWeight: 600 }}>{t("auth.pdpa_link")}</a> {t("auth.pdpa_post")}</span>
        </label>
        <FieldErr msg={err.pdpa} />

        <label style={{ display: "flex", gap: 8, alignItems: "flex-start", margin: "var(--sp-3) 0 0", fontSize: "var(--fs-caption)", color: "var(--ink-dim)", cursor: "pointer" }}>
          <input type="checkbox" checked={f.marketing} onChange={(e) => set("marketing", e.target.checked)} style={{ width: 18, height: 18, marginTop: 2, flex: "0 0 auto" }} data-testid="marketing" />
          <span>{t("auth.marketing_consent")}</span>
        </label>
      </React.Fragment>}

      <button className="btn-brand" style={{ width: "100%", marginTop: "var(--sp-5)" }} onClick={submit} data-testid="auth-submit">{t(isReg ? "auth.register_submit" : "auth.login_submit")}</button>

      {!isReg && <div style={{ textAlign: "center", marginTop: "var(--sp-3)" }}>
        <a href="#" onClick={(e) => { e.preventDefault(); setForgotNote(true); }} style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)" }}>{t("auth.forgot")}</a>
        {forgotNote && <div style={{ fontSize: "var(--fs-micro)", color: "var(--ink-mute)", marginTop: 6 }}>{t("auth.forgot_note")}</div>}
      </div>}

      <div style={{ textAlign: "center", marginTop: "var(--sp-4)", borderTop: "1px solid var(--hairline)", paddingTop: "var(--sp-4)" }}>
        <a href={isReg ? "/login" : "/register"} onClick={(e) => { e.preventDefault(); navigate((isReg ? "/login" : "/register") + (params.get("next") ? "?next=" + encodeURIComponent(params.get("next")) : "")); }} style={{ fontSize: "var(--fs-caption)" }}>{t(isReg ? "auth.to_login" : "auth.to_register")}</a>
      </div>
    </div>

    <BottomSheet open={policyOpen} title={t("auth.policy_title")} onClose={() => setPolicyOpen(false)}>
      <div style={{ fontSize: "var(--fs-body)", color: "var(--ink-dim)", lineHeight: 1.7 }}>{t("auth.policy_body")}</div>
      <button className="btn-secondary" style={{ marginTop: "var(--sp-4)", width: "100%" }} onClick={() => setPolicyOpen(false)}>{t("common.close")}</button>
    </BottomSheet>
  </div>;
}

/* ===========================================================================
   OnboardingPage — /app/onboarding (full-screen · L0 step C + completion)
   =========================================================================== */
function OnboardingPage({ t, lang, onSetLang }) {
  const [step, setStep] = useState(0); // 0 = step C profile · 1 = completion
  const [style, setStyle] = useState(null);
  const [markets, setMarkets] = useState({});
  const [exp, setExp] = useState(null);
  const toggleMarket = (k) => setMarkets((o) => Object.assign({}, o, { [k]: !o[k] }));
  const skip = () => navigate("/app", true);

  const chip = (on, onClick, label, key) => <button key={key} onClick={onClick} className={on ? "chip-on" : "chip"} style={{ minHeight: 40 }}>{label}</button>;

  return <div style={{ minHeight: "100dvh", background: "var(--bg-grad)", display: "flex", flexDirection: "column" }}>
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--sp-4) var(--sp-5)" }}>
      <Logo />
      <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
        <LangToggle lang={lang} onSet={onSetLang} />
        <button className="btn-ghost" onClick={skip} style={{ fontSize: "var(--fs-caption)" }} data-testid="onb-skip">{t("onboarding.skip")}</button>
      </div>
    </div>
    <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "var(--sp-5)" }}>
      <div className="card" style={{ width: "100%", maxWidth: 520 }}>
        <div style={{ display: "flex", justifyContent: "center", marginBottom: "var(--sp-5)" }}><StepDots current={step === 0 ? 2 : 3} total={4} /></div>

        {step === 0 && <div>
          <h1 style={{ fontSize: "var(--fs-h1)", marginBottom: 4, textAlign: "center" }}>{t("onboarding.title_c")}</h1>
          <div style={{ color: "var(--ink-dim)", fontSize: "var(--fs-body)", textAlign: "center", marginBottom: "var(--sp-5)" }}>{t("onboarding.subtitle_c")}</div>

          <div className="lbl" style={{ marginTop: 0 }}>{t("onboarding.style_label")}</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
            {[["scalp", "style_scalp"], ["day", "style_day"], ["swing", "style_swing"], ["mixed", "style_mixed"]].map(([k, lk]) => chip(style === k, () => setStyle(k), t("onboarding." + lk), k))}
          </div>

          <div className="lbl">{t("onboarding.markets_label")}</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
            {[["forex", "market_forex"], ["gold", "market_gold"], ["crypto", "market_crypto"], ["indices", "market_indices"], ["stocks", "market_stocks"]].map(([k, lk]) => chip(!!markets[k], () => toggleMarket(k), t("onboarding." + lk), k))}
          </div>

          <div className="lbl">{t("onboarding.exp_label")}</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
            {[["new", "exp_new"], ["mid", "exp_mid"], ["pro", "exp_pro"]].map(([k, lk]) => chip(exp === k, () => setExp(k), t("onboarding." + lk), k))}
          </div>

          <button className="btn-brand" style={{ width: "100%", marginTop: "var(--sp-5)" }} onClick={() => setStep(1)} data-testid="onb-next">{t("onboarding.next")}</button>
        </div>}

        {step === 1 && <div style={{ textAlign: "center" }}>
          <div style={{ fontSize: 40, marginBottom: "var(--sp-3)" }}>🚀</div>
          <h1 style={{ fontSize: "var(--fs-h1)", marginBottom: 6 }}>{t("onboarding.done_title")}</h1>
          <div style={{ color: "var(--ink-dim)", fontSize: "var(--fs-body)", marginBottom: "var(--sp-5)" }}>{t("onboarding.done_sub")}</div>
          <button className="btn-brand" style={{ width: "100%", marginBottom: "var(--sp-3)" }} onClick={() => navigate("/app/connect", true)} data-testid="onb-connect">{t("onboarding.cta_connect")}</button>
          <button className="btn-secondary" style={{ width: "100%", marginBottom: "var(--sp-4)" }} onClick={() => navigate("/app/import", true)} data-testid="onb-import">{t("onboarding.cta_import")}</button>
          <a href="#" onClick={(e) => { e.preventDefault(); navigate("/app", true); }} style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)" }}>{t("onboarding.finish")}</a>
        </div>}
      </div>
    </div>
  </div>;
}

/* ===========================================================================
   ConnectPage — /app/connect (ConnectWizard 3 step · จุด Aha)
   =========================================================================== */
function BrokerLogo({ abbr, active }) {
  return <div style={{ width: 44, height: 44, borderRadius: 10, flex: "0 0 auto", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 800, fontSize: 13, letterSpacing: "-.5px", color: active ? "#fff" : "var(--brand-strong)", background: active ? "var(--brand-grad)" : "var(--brand-soft)" }}>{abbr}</div>;
}
function TrustStrip({ text }) {
  return <div style={{ display: "flex", gap: 8, alignItems: "center", background: "var(--brand-soft)", border: "1px solid var(--brand-edge)", borderRadius: "var(--radius)", padding: "var(--sp-3) var(--sp-4)", fontSize: "var(--fs-caption)", color: "var(--brand-strong)", marginBottom: "var(--sp-4)" }}>
    <span aria-hidden="true">🔒</span><span>{text}</span>
  </div>;
}
function ConnectPage({ t, toast }) {
  const [brokers, setBrokers] = useState(null);
  const [step, setStep] = useState(0);
  const [broker, setBroker] = useState(null);
  const [platform, setPlatform] = useState("mt5");
  const [f, setF] = useState({ server: "", login: "", investor: "", nickname: "" });
  const [err, setErr] = useState({});
  const [guideOpen, setGuideOpen] = useState(false);
  const [phase, setPhase] = useState("form"); // form | connecting | done
  const [connErr, setConnErr] = useState(null);
  const set = (k, v) => setF((o) => Object.assign({}, o, { [k]: v }));

  useEffect(() => { window.MTOS_API.get("/brokers").then((r) => setBrokers(r.data)).catch(() => setBrokers([])); }, []);

  const pickBroker = (b) => { setBroker(b); set("server", b.server_hint || ""); setStep(1); };
  const validate = () => {
    const e = {};
    if (!f.server.trim()) e.server = t("connect.err_server");
    if (!f.login.trim()) e.login = t("connect.err_login");
    if (!f.investor) e.investor = t("connect.err_investor");
    setErr(e); return Object.keys(e).length === 0;
  };
  const submit = () => {
    setConnErr(null); setPhase("connecting");
    window.MTOS_API.post("/accounts", { broker_name: broker.name, platform: platform, server: f.server, login: f.login, investor_password: f.investor, nickname: f.nickname })
      .then(() => { setPhase("done"); })
      .catch((er) => {
        setPhase("form");
        const code = er.envelope && er.envelope.error && er.envelope.error.code;
        if (code === "master_password_rejected") { setConnErr(t("connect.master_rejected")); set("investor", ""); setStep(1); }
        else if (code === "duplicate_account") { setConnErr(t("connect.dup_error")); setStep(1); }
        else { setConnErr(t("states.error_title")); }
      });
  };

  const isMt4 = platform === "mt4";

  // completion (first sync)
  if (phase === "done") return <div style={{ maxWidth: 560, margin: "0 auto" }}>
    <TrustStrip text={t("connect.trust_strip")} />
    <div style={{ marginBottom: "var(--sp-4)" }}><Banner tone="info">{t("connect.first_sync_started")}</Banner></div>
    <FirstSyncProgress t={t} minutes={6} progress={0.08} />
    <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-3)", marginTop: "var(--sp-4)" }}>
      <button className="btn-brand" onClick={() => navigate("/app/import")} data-testid="conn-upload-wait">{t("connect.upload_while_wait")}</button>
      <button className="btn-secondary" onClick={() => navigate("/app")}>{t("errors.go_dashboard")}</button>
    </div>
  </div>;

  return <div style={{ maxWidth: 560, margin: "0 auto" }}>
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "var(--sp-4)" }}>
      <h1 style={{ fontSize: "var(--fs-h1)" }}>{t("connect." + (step === 0 ? "step1_title" : step === 1 ? "step2_title" : "step3_title"))}</h1>
      <StepDots current={step} total={3} />
    </div>
    <TrustStrip text={t("connect.trust_strip")} />
    {connErr && <div style={{ marginBottom: "var(--sp-4)" }}><Banner tone="error">{connErr}</Banner></div>}

    {/* STEP 1 — broker */}
    {step === 0 && <div>
      {!brokers && <Skeleton variant="list" />}
      {brokers && <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: "var(--sp-2)" }}>
        {brokers.map((b) => <button key={b.key} onClick={() => pickBroker(b)} data-testid={"broker-" + b.key}
          style={{ display: "flex", alignItems: "center", gap: 12, width: "100%", textAlign: "left", border: "1px solid var(--hairline)", borderRadius: "var(--radius)", background: "var(--surface-1)", cursor: "pointer", padding: "var(--sp-3)", minHeight: 64 }}>
          <BrokerLogo abbr={b.abbr} /><span className="disp" style={{ fontSize: "var(--fs-body)", fontWeight: 700 }}>{b.name}</span>
          <span style={{ flex: 1 }} /><span style={{ color: "var(--ink-mute)" }}>›</span>
        </button>)}
      </div>}
    </div>}

    {/* STEP 2 — credentials / mt4 switch */}
    {step === 1 && broker && <div>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: "var(--sp-4)" }}>
        <BrokerLogo abbr={broker.abbr} active /><span className="disp" style={{ fontSize: "var(--fs-h3)", fontWeight: 700 }}>{broker.name}</span>
      </div>
      <div className="lbl" style={{ marginTop: 0 }}>{t("connect.platform_label")}</div>
      <div style={{ display: "flex", gap: 8, marginBottom: "var(--sp-2)" }}>
        {["mt5", "mt4"].map((p) => <button key={p} onClick={() => setPlatform(p)} className={platform === p ? "chip-on" : "chip"} style={{ minHeight: 40, flex: 1 }} data-testid={"platform-" + p}>{p.toUpperCase()}</button>)}
      </div>

      {isMt4 && <div style={{ marginTop: "var(--sp-3)" }}>
        <Banner tone="warn">{t("connect.mt4_notice")}</Banner>
        <button className="btn-brand" style={{ width: "100%", marginTop: "var(--sp-4)" }} onClick={() => navigate("/app/import")} data-testid="conn-mt4-cta">{t("connect.mt4_cta")}</button>
        <button className="btn-ghost" style={{ width: "100%", marginTop: "var(--sp-2)" }} onClick={() => setStep(0)}>{t("connect.back")}</button>
      </div>}

      {!isMt4 && <div>
        <label className="lbl">{t("connect.server_label")}</label>
        <input className="inp" style={err.server ? { borderColor: "var(--loss)" } : null} value={f.server} onChange={(e) => set("server", e.target.value)} placeholder={t("connect.server_ph")} data-testid="conn-server" />
        <FieldErr msg={err.server} />
        <label className="lbl">{t("connect.login_label")}</label>
        <input className="inp" style={err.login ? { borderColor: "var(--loss)" } : null} value={f.login} onChange={(e) => set("login", e.target.value)} placeholder={t("connect.login_ph")} inputMode="numeric" autoComplete="off" data-testid="conn-login" />
        <FieldErr msg={err.login} />
        <label className="lbl">{t("connect.investor_label")}</label>
        <input className="inp" style={err.investor ? { borderColor: "var(--loss)" } : null} type="password" autoComplete="off" name="mtos-investor-nofill" value={f.investor} onChange={(e) => set("investor", e.target.value)} placeholder={t("connect.investor_ph")} data-testid="conn-investor" />
        <FieldErr msg={err.investor} />
        <div style={{ marginTop: 6 }}><a href="#" onClick={(e) => { e.preventDefault(); setGuideOpen(true); }} style={{ fontSize: "var(--fs-caption)", color: "var(--brand-bright)" }}>{t("connect.guide_link")}</a></div>
        <label className="lbl">{t("connect.nickname_label")}</label>
        <input className="inp" value={f.nickname} onChange={(e) => set("nickname", e.target.value)} placeholder={t("connect.nickname_ph")} autoComplete="off" data-testid="conn-nickname" />

        <div style={{ display: "flex", gap: "var(--sp-3)", marginTop: "var(--sp-5)" }}>
          <button className="btn-secondary" onClick={() => setStep(0)} style={{ flex: "0 0 auto" }}>{t("connect.back")}</button>
          <button className="btn-brand" style={{ flex: 1 }} onClick={() => { if (validate()) setStep(2); }} data-testid="conn-next">{t("connect.next")}</button>
        </div>
      </div>}
    </div>}

    {/* STEP 3 — confirm */}
    {step === 2 && broker && <div>
      <div className="card" style={{ background: "var(--surface-2)" }}>
        {[[t("connect.confirm_broker"), broker.name], [t("connect.confirm_platform"), platform.toUpperCase()], [t("connect.confirm_server"), f.server], [t("connect.confirm_login"), "••" + (f.login.slice(-4) || f.login)], [t("connect.confirm_nickname"), f.nickname || "—"]].map((r, i) => <div key={i} style={{ display: "flex", justifyContent: "space-between", padding: "7px 0", fontSize: "var(--fs-caption)", borderBottom: i < 4 ? "1px solid var(--hairline)" : "none" }}>
          <span style={{ color: "var(--ink-mute)" }}>{r[0]}</span><span className="num" style={{ fontWeight: 600 }}>{r[1]}</span>
        </div>)}
      </div>
      <div style={{ display: "flex", gap: "var(--sp-3)", marginTop: "var(--sp-5)" }}>
        <button className="btn-secondary" onClick={() => setStep(1)} style={{ flex: "0 0 auto" }} disabled={phase === "connecting"}>{t("connect.back")}</button>
        <button className="btn-brand" style={{ flex: 1 }} onClick={submit} disabled={phase === "connecting"} data-testid="conn-submit">{phase === "connecting" ? t("connect.connecting") : t("connect.submit")}</button>
      </div>
      <div style={{ textAlign: "center", marginTop: "var(--sp-4)" }}>
        <a href="#" onClick={(e) => { e.preventDefault(); navigate("/app/import"); }} style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)" }}>{t("connect.fallback_upload")}</a>
      </div>
    </div>}

    <BottomSheet open={guideOpen} title={t("connect.guide_link")} onClose={() => setGuideOpen(false)}>
      <div style={{ fontSize: "var(--fs-body)", color: "var(--ink-dim)", lineHeight: 1.7 }}>{t("connect.guide_body")}</div>
      <button className="btn-secondary" style={{ marginTop: "var(--sp-4)", width: "100%" }} onClick={() => setGuideOpen(false)}>{t("common.close")}</button>
    </BottomSheet>
  </div>;
}

/* ===========================================================================
   Account card (shared: Import list + Settings list)
   =========================================================================== */
function platformBadge(t, platform) {
  const map = { mt5: ["badge_mt5"], mt4: ["badge_mt4"], manual: ["badge_manual"] };
  const k = map[platform] ? map[platform][0] : "badge_manual";
  return t("importsync." + k);
}
function statusChip(t, s) {
  const map = { ok: ["status_ok", "var(--profit)", "#e7f8f0"], pending: ["status_pending", "#8a6d2f", "rgba(200,163,90,.16)"], manual: ["status_manual", "var(--ink-mute)", "var(--zebra)"], error: ["status_error", "var(--loss)", "#fdecef"] };
  const m = map[s] || map.manual;
  return <span style={{ fontSize: "var(--fs-micro)", fontWeight: 700, padding: "2px 8px", borderRadius: "var(--radius-full)", color: m[1], background: m[2] }}>{t("importsync." + m[0])}</span>;
}
function AccountCard({ t, a, onManage }) {
  return <div className="card" style={{ display: "flex", alignItems: "center", gap: 12, minHeight: 72 }}>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
        <span className="disp" style={{ fontSize: "var(--fs-body)", fontWeight: 700 }}>{a.nickname}</span>
        <span style={{ fontSize: "var(--fs-micro)", color: "var(--ink-mute)", fontWeight: 600 }}>{platformBadge(t, a.platform)}</span>
        {statusChip(t, a.sync_status)}
      </div>
      <div style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)", marginTop: 4 }}>{a.broker} · ••{a.last4} · <span className="num">{t("importsync.balance_label")} {fmtMoney(a.balance_cents, a.currency).replace("+", "")}</span></div>
    </div>
    {onManage && <button className="btn-secondary" onClick={onManage} style={{ minHeight: 38, padding: "0 var(--sp-3)", fontSize: "var(--fs-caption)" }} data-testid={"manage-" + a.acc_id}>{t("settings.manage")}</button>}
  </div>;
}

/* ===========================================================================
   ImportPage — /app/import (Import & Sync · AccountList + upload wizard)
   =========================================================================== */
function ImportPage({ t, toast }) {
  const [accts, setAccts] = useState(null);
  const [wiz, setWiz] = useState(false);
  const load = useCallback(() => { setAccts(null); window.MTOS_API.get("/accounts").then((r) => setAccts(r.data)).catch(() => setAccts([])); }, []);
  useEffect(() => { load(); }, [load]);

  return <div>
    <PageHeader t={t} page="import" />
    <TrustStrip text={t("importsync.trust_strip")} />

    <div style={{ display: "flex", gap: "var(--sp-3)", marginBottom: "var(--sp-4)", flexWrap: "wrap" }}>
      <button className="btn-brand" onClick={() => navigate("/app/connect")} data-testid="imp-add">{t("importsync.add_account")}</button>
      <button className="btn-secondary" onClick={() => setWiz(true)} data-testid="imp-upload" disabled={!accts || !accts.length}>{t("importsync.upload_cta")}</button>
    </div>

    {!accts && <Skeleton variant="list" />}
    {accts && accts.length === 0 && <EmptyState t={t} title={t("importsync.empty_title")} body={t("importsync.empty_body")} cta={t("importsync.empty_cta")} onCta={() => navigate("/app/connect")} />}
    {accts && accts.length > 0 && <div>
      <div className="lbl" style={{ marginTop: 0 }}>{t("importsync.accounts_title")}</div>
      <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-3)" }}>
        {accts.map((a) => <AccountCard key={a.acc_id} t={t} a={a} />)}
      </div>
    </div>}

    <BottomSheet open={wiz} title={t("importsync.wizard_title")} onClose={() => setWiz(false)}>
      {wiz && <UploadWizard t={t} toast={toast} accounts={accts || []} onClose={() => setWiz(false)} />}
    </BottomSheet>
  </div>;
}

function UploadWizard({ t, toast, accounts, onClose }) {
  const [step, setStep] = useState(0); // 0 account · 1 file · 2 tz · 3 preview
  const [acct, setAcct] = useState(accounts[0] ? accounts[0].acc_id : "");
  const [fileName, setFileName] = useState("");
  const [tz, setTz] = useState("Asia/Bangkok");
  const [err, setErr] = useState(null);
  const [preview, setPreview] = useState(null);
  const [busy, setBusy] = useState(false);

  const goPreview = () => {
    setBusy(true);
    window.MTOS_API.post("/imports", { account_id: acct, filename: fileName, tz: tz }).then((r) => { setPreview(r.data); setStep(3); setBusy(false); }).catch(() => { setBusy(false); setErr(t("states.error_title")); });
  };
  const commit = () => {
    setBusy(true);
    window.MTOS_API.post("/imports/" + preview.imp_id + "/commit").then((r) => { setBusy(false); onClose(); toast(t("importsync.committed_toast", { n: r.data.inserted })); }).catch(() => { setBusy(false); setErr(t("states.error_title")); });
  };

  const tzOpts = ["Asia/Bangkok", "UTC", "Europe/London", "America/New_York"];

  return <div>
    <div style={{ display: "flex", justifyContent: "center", marginBottom: "var(--sp-4)" }}><StepDots current={Math.min(step, 2)} total={3} /></div>
    {err && <div style={{ marginBottom: "var(--sp-3)" }}><Banner tone="error">{err}</Banner></div>}

    {step === 0 && <div>
      <div className="lbl" style={{ marginTop: 0 }}>{t("importsync.w_pick_account")}</div>
      <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-2)" }}>
        {accounts.map((a) => <button key={a.acc_id} onClick={() => setAcct(a.acc_id)} className={acct === a.acc_id ? "chip-on" : "chip"} style={{ minHeight: 48, textAlign: "left", justifyContent: "flex-start", padding: "0 var(--sp-3)" }} data-testid={"wiz-acct-" + a.acc_id}>{a.nickname} · {a.broker} ••{a.last4}</button>)}
      </div>
      <button className="btn-brand" style={{ width: "100%", marginTop: "var(--sp-4)" }} onClick={() => setStep(1)} data-testid="wiz-next-0">{t("importsync.next")}</button>
    </div>}

    {step === 1 && <div>
      <div className="lbl" style={{ marginTop: 0 }}>{t("importsync.w_file_label")}</div>
      <div style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)", marginBottom: "var(--sp-3)" }}>{t("importsync.w_file_hint")}</div>
      <label className="btn-secondary" style={{ display: "inline-flex", cursor: "pointer" }}>
        {t("importsync.w_file_pick")}
        <input type="file" style={{ display: "none" }} onChange={(e) => setFileName(e.target.files && e.target.files[0] ? e.target.files[0].name : "")} data-testid="wiz-file" />
      </label>
      <div style={{ marginTop: "var(--sp-2)", fontSize: "var(--fs-caption)", color: fileName ? "var(--ink)" : "var(--ink-subtle)" }}>{fileName || t("importsync.w_file_none")}</div>
      <div style={{ fontSize: "var(--fs-micro)", color: "var(--ink-subtle)", marginTop: "var(--sp-3)" }}>{t("importsync.desktop_mapping_hint")}</div>
      <div style={{ display: "flex", gap: "var(--sp-3)", marginTop: "var(--sp-4)" }}>
        <button className="btn-secondary" onClick={() => setStep(0)} style={{ flex: "0 0 auto" }}>{t("importsync.back")}</button>
        <button className="btn-brand" style={{ flex: 1 }} onClick={() => { if (!fileName) { setErr(t("importsync.err_no_file")); return; } setErr(null); setStep(2); }} data-testid="wiz-next-1">{t("importsync.next")}</button>
      </div>
    </div>}

    {step === 2 && <div>
      <div className="lbl" style={{ marginTop: 0 }}>{t("importsync.w_tz_label")}</div>
      <div style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)", marginBottom: "var(--sp-3)" }}>{t("importsync.w_tz_hint")}</div>
      <select className="inp" value={tz} onChange={(e) => setTz(e.target.value)} data-testid="wiz-tz">
        {tzOpts.map((z) => <option key={z} value={z}>{z}</option>)}
      </select>
      <div style={{ display: "flex", gap: "var(--sp-3)", marginTop: "var(--sp-4)" }}>
        <button className="btn-secondary" onClick={() => setStep(1)} style={{ flex: "0 0 auto" }}>{t("importsync.back")}</button>
        <button className="btn-brand" style={{ flex: 1 }} onClick={goPreview} disabled={busy} data-testid="wiz-preview">{busy ? t("common.loading") : t("importsync.preview_title")}</button>
      </div>
    </div>}

    {step === 3 && preview && <div>
      <div className="disp" style={{ fontSize: "var(--fs-h3)", marginBottom: "var(--sp-3)" }}>{t("importsync.preview_title")}</div>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginBottom: "var(--sp-3)" }}>
        <span className="chip-on" style={{ minHeight: 30 }}>{t("importsync.chip_ok", { n: preview.summary.ok })}</span>
        <span className="chip" style={{ minHeight: 30, color: "var(--loss)" }}>{t("importsync.chip_fail", { n: preview.summary.fail })}</span>
        <span className="chip" style={{ minHeight: 30 }}>{t("importsync.chip_skip", { n: preview.summary.skip })}</span>
        <span className="chip" style={{ minHeight: 30 }}>{t("importsync.chip_dup", { n: preview.summary.dup })}</span>
      </div>
      {preview.overlap_warning && <div style={{ marginBottom: "var(--sp-3)" }}><Banner tone="warn">{t("importsync.warn_overlap")}</Banner></div>}
      <div style={{ overflowX: "auto" }}>
        <table className="trades-table"><thead><tr>
          <th>{t("importsync.col_time")}</th><th>{t("importsync.col_symbol")}</th><th>{t("importsync.col_dir")}</th>
          <th style={{ textAlign: "right" }}>{t("importsync.col_lots")}</th><th style={{ textAlign: "right" }}>{t("importsync.col_pnl")}</th><th></th>
        </tr></thead><tbody>
          {preview.rows.map((r) => <tr key={r.row}>
            <td className="num" style={{ color: "var(--ink-mute)" }}>{r.time ? fmtTime(r.time) : "—"}</td>
            <td><span className="disp" style={{ fontWeight: 700 }}>{r.symbol}</span></td>
            <td>{r.direction ? <DirText t={t} dir={r.direction} /> : "—"}</td>
            <td className="num" style={{ textAlign: "right" }}>{r.lots == null ? "—" : r.lots.toFixed(2)}</td>
            <td style={{ textAlign: "right" }}>{r.net_cents == null ? "—" : <MoneyText cents={r.net_cents} currency="USD" />}</td>
            <td style={{ textAlign: "right" }}><span style={{ fontSize: "var(--fs-micro)", color: r.status === "ok" ? "var(--profit)" : r.status === "dup" ? "#8a6d2f" : "var(--loss)", fontWeight: 700 }}>{t("importsync.row_status_" + r.status)}</span></td>
          </tr>)}
        </tbody></table>
      </div>
      <div style={{ display: "flex", gap: "var(--sp-3)", marginTop: "var(--sp-4)" }}>
        <button className="btn-secondary" onClick={() => setStep(2)} style={{ flex: "0 0 auto" }}>{t("importsync.back")}</button>
        <button className="btn-brand" style={{ flex: 1 }} onClick={commit} disabled={busy} data-testid="wiz-commit">{t("importsync.commit_cta", { n: preview.summary.ok })}</button>
      </div>
    </div>}
  </div>;
}

/* ===========================================================================
   SettingsPage — /app/settings
   =========================================================================== */
function Section({ title, children }) {
  return <div className="card" style={{ marginBottom: "var(--sp-4)" }}>
    <div className="disp" style={{ fontSize: "var(--fs-h3)", marginBottom: "var(--sp-3)" }}>{title}</div>
    {children}
  </div>;
}
function SettingsPage({ t, toast, onLogout, lang, onSetLang }) {
  const [me, setMe] = useState(null);
  const [name, setName] = useState("");
  const [tzv, setTzv] = useState("");
  const [marketing, setMarketing] = useState(false);
  const [pw, setPw] = useState({ cur: "", nw: "", cf: "" });
  const [pwErr, setPwErr] = useState({});
  const [accts, setAccts] = useState(null);
  const [sheetAcct, setSheetAcct] = useState(null);
  const [delOpen, setDelOpen] = useState(false);
  const [delText, setDelText] = useState("");
  const [delErr, setDelErr] = useState(null);

  const loadMe = useCallback(() => { window.MTOS_API.get("/me").then((r) => { setMe(r.data); setName(r.data.display_name); setTzv(r.data.timezone); setMarketing(!!(r.data.consents && r.data.consents.marketing)); }).catch(() => {}); }, []);
  const loadAccts = useCallback(() => { window.MTOS_API.get("/accounts").then((r) => setAccts(r.data)).catch(() => setAccts([])); }, []);
  useEffect(() => { loadMe(); loadAccts(); }, [loadMe, loadAccts]);

  const saveProfile = () => {
    window.MTOS_API.patch("/me", { display_name: name, timezone: tzv, consents: { marketing: marketing } })
      .then(() => toast(t("settings.profile_saved"))).catch(() => toast(t("states.error_title")));
  };
  const changePw = () => {
    const e = {};
    if (!pw.cur) e.cur = t("settings.err_current");
    if (!pw.nw || pw.nw.length < 8) e.nw = t("auth.err_password");
    if (pw.cf !== pw.nw) e.cf = t("auth.err_confirm");
    setPwErr(e); if (Object.keys(e).length) return;
    window.MTOS_API.post("/me/password", { current_password: pw.cur, new_password: pw.nw }).then(() => { setPw({ cur: "", nw: "", cf: "" }); toast(t("settings.password_changed")); }).catch(() => toast(t("states.error_title")));
  };
  const exportData = () => {
    window.MTOS_API.get("/me/export").then((r) => {
      try {
        const blob = new Blob([JSON.stringify(r.data, null, 2)], { type: "application/json" });
        const url = URL.createObjectURL(blob); const a = document.createElement("a");
        a.href = url; a.download = "mytraderos-export.json"; document.body.appendChild(a); a.click();
        document.body.removeChild(a); setTimeout(() => URL.revokeObjectURL(url), 1000);
      } catch (x) {}
      toast(t("settings.exported"));
    }).catch(() => toast(t("states.error_title")));
  };
  const deleteAccount = () => {
    if (!me || delText.trim() !== me.email) { setDelErr(t("settings.confirm_type_mismatch")); return; }
    window.MTOS_API.del("/me").then(() => { toast(t("settings.account_deleted")); setTimeout(() => onLogout(), 600); }).catch(() => setDelErr(t("states.error_title")));
  };

  const moreLinks = [
    ["/app/insights", "◎", t("app.nav.insights"), false],
    ["/app/import", "⇪", t("app.nav.import"), false],
    ["/app/connect", "◈", t("app.nav.connect"), false],
    ["/app/unlock", "★", t("app.nav.unlock"), true],
  ];

  return <div style={{ maxWidth: 640, margin: "0 auto" }}>
    <PageHeader t={t} page="settings" />

    {/* more links — เข้าถึง Insights/Import/Connect/Unlock จากมือถือ (IA ทางเลือก B · P1-2) */}
    <Section title={t("settings.section_more")}>
      <div style={{ display: "flex", flexDirection: "column" }}>
        {moreLinks.map(([to, icon, label, purple], i) => <a key={to} href={to}
          onClick={(e) => { e.preventDefault(); navigate(to); }} data-testid={"more-" + to.split("/").pop()}
          style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 2px", minHeight: 48, borderTop: i ? "1px solid var(--hairline)" : "none", color: purple ? "var(--brand-strong)" : "var(--ink)", fontWeight: 600, textDecoration: "none" }}>
          <span style={{ width: 24, textAlign: "center", fontSize: 16, color: purple ? "var(--brand-strong)" : "var(--ink-dim)" }}>{icon}</span>
          <span style={{ flex: 1 }}>{label}</span>
          {purple && <span style={{ fontSize: "var(--fs-micro)", fontWeight: 800, color: "#fff", background: "var(--brand-grad)", padding: "2px 9px", borderRadius: "var(--radius-full)" }}>{t("settings.more_unlock_hint")}</span>}
          <span style={{ color: "var(--ink-subtle)", fontSize: 18 }}>›</span>
        </a>)}
      </div>
    </Section>

    {/* profile */}
    <Section title={t("settings.section_profile")}>
      {!me && <Skeleton variant="list" />}
      {me && <div>
        <label className="lbl" style={{ marginTop: 0 }}>{t("settings.display_name")}</label>
        <input className="inp" value={name} onChange={(e) => setName(e.target.value)} data-testid="set-name" />
        <label className="lbl">{t("settings.timezone")}</label>
        <select className="inp" value={tzv} onChange={(e) => setTzv(e.target.value)} data-testid="set-tz">
          {["Asia/Bangkok", "UTC", "Europe/London", "America/New_York", "Asia/Tokyo"].map((z) => <option key={z} value={z}>{z}</option>)}
        </select>
        <label className="lbl">{t("settings.language")}</label>
        <div><LangToggle lang={lang} onSet={onSetLang} /></div>
        <label style={{ display: "flex", gap: 8, alignItems: "center", margin: "var(--sp-4) 0 0", fontSize: "var(--fs-caption)", color: "var(--ink-dim)", cursor: "pointer" }}>
          <input type="checkbox" checked={marketing} onChange={(e) => setMarketing(e.target.checked)} style={{ width: 18, height: 18 }} />{t("settings.consent_marketing")}
        </label>
        <button className="btn-brand" style={{ marginTop: "var(--sp-4)" }} onClick={saveProfile} data-testid="set-save-profile">{t("settings.save_profile")}</button>
      </div>}
    </Section>

    {/* password */}
    <Section title={t("settings.section_password")}>
      <label className="lbl" style={{ marginTop: 0 }}>{t("settings.current_password")}</label>
      <input className="inp" type="password" autoComplete="current-password" value={pw.cur} onChange={(e) => setPw(Object.assign({}, pw, { cur: e.target.value }))} />
      <FieldErr msg={pwErr.cur} />
      <label className="lbl">{t("settings.new_password")}</label>
      <input className="inp" type="password" autoComplete="new-password" value={pw.nw} onChange={(e) => setPw(Object.assign({}, pw, { nw: e.target.value }))} />
      <FieldErr msg={pwErr.nw} />
      <label className="lbl">{t("settings.confirm_new")}</label>
      <input className="inp" type="password" autoComplete="new-password" value={pw.cf} onChange={(e) => setPw(Object.assign({}, pw, { cf: e.target.value }))} />
      <FieldErr msg={pwErr.cf} />
      <button className="btn-secondary" style={{ marginTop: "var(--sp-4)" }} onClick={changePw}>{t("settings.change_password")}</button>
    </Section>

    {/* trading accounts */}
    <Section title={t("settings.section_accounts")}>
      {!accts && <Skeleton variant="list" />}
      {accts && accts.length === 0 && <div style={{ color: "var(--ink-mute)", fontSize: "var(--fs-caption)" }}>{t("importsync.empty_title")}</div>}
      {accts && accts.length > 0 && <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-3)" }}>
        {accts.map((a) => <AccountCard key={a.acc_id} t={t} a={a} onManage={() => setSheetAcct(a)} />)}
      </div>}
    </Section>

    {/* PDPA / danger */}
    <Section title={t("settings.section_pdpa")}>
      <button className="btn-secondary" onClick={exportData} data-testid="set-export">{t("settings.export")}</button>
      <div style={{ fontSize: "var(--fs-micro)", color: "var(--ink-subtle)", marginTop: 6 }}>{t("settings.export_hint")}</div>
      <div style={{ marginTop: "var(--sp-5)", borderTop: "1px solid var(--loss)", paddingTop: "var(--sp-4)" }}>
        <div style={{ color: "var(--loss)", fontWeight: 700, fontSize: "var(--fs-caption)", marginBottom: 6 }}>{t("settings.danger_zone")}</div>
        <div style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)", marginBottom: "var(--sp-3)" }}>{t("settings.delete_account_warn")}</div>
        <button className="btn-danger" onClick={() => { setDelOpen(true); setDelText(""); setDelErr(null); }} data-testid="set-delete-user">{t("settings.delete_account")}</button>
      </div>
    </Section>

    {/* logout */}
    <Section title={t("settings.section_logout")}>
      <button className="btn-secondary" onClick={onLogout} data-testid="set-logout">{t("settings.logout")}</button>
    </Section>

    {/* account settings sheet */}
    <BottomSheet open={!!sheetAcct} title={t("settings.acct_sheet_title")} onClose={() => setSheetAcct(null)}>
      {sheetAcct && <AccountSettingsSheet t={t} toast={toast} acct={sheetAcct} onClose={() => setSheetAcct(null)} onChanged={() => { setSheetAcct(null); loadAccts(); }} />}
    </BottomSheet>

    {/* delete user confirm */}
    <BottomSheet open={delOpen} title={t("settings.delete_account")} onClose={() => setDelOpen(false)}>
      <Banner tone="error">{t("settings.delete_account_warn")}</Banner>
      {me && <React.Fragment>
        <label className="lbl">{t("settings.delete_account_confirm_label", { email: me.email })}</label>
        <input className="inp" value={delText} onChange={(e) => setDelText(e.target.value)} placeholder={me.email} autoComplete="off" data-testid="del-user-input" />
      </React.Fragment>}
      {delErr && <div style={{ marginTop: 6 }}><FieldErr msg={delErr} /></div>}
      <button className="btn-danger" style={{ width: "100%", marginTop: "var(--sp-4)" }} onClick={deleteAccount} data-testid="del-user-confirm">{t("settings.delete_account_cta")}</button>
    </BottomSheet>
  </div>;
}

function AccountSettingsSheet({ t, toast, acct, onClose, onChanged }) {
  const [nick, setNick] = useState(acct.nickname);
  const [mode, setMode] = useState("main"); // main | archive | delete
  const [delText, setDelText] = useState("");
  const [err, setErr] = useState(null);

  const saveNick = () => { window.MTOS_API.patch("/accounts/" + acct.acc_id, { nickname: nick }).then(() => { toast(t("settings.acct_saved")); onChanged(); }).catch(() => toast(t("states.error_title"))); };
  const archive = () => { window.MTOS_API.patch("/accounts/" + acct.acc_id, { archived: true }).then(() => { toast(t("settings.acct_archived")); onChanged(); }).catch(() => toast(t("states.error_title"))); };
  const del = () => {
    if (delText.trim() !== acct.last4) { setErr(t("settings.confirm_type_mismatch")); return; }
    window.MTOS_API.del("/accounts/" + acct.acc_id).then(() => { toast(t("settings.acct_deleted")); onChanged(); }).catch(() => setErr(t("states.error_title")));
  };

  if (mode === "delete") return <div>
    <Banner tone="error">{t("settings.acct_delete_warn")}</Banner>
    <label className="lbl">{t("settings.acct_delete_confirm_label", { last4: acct.last4 })}</label>
    <input className="inp" value={delText} onChange={(e) => setDelText(e.target.value)} placeholder={acct.last4} autoComplete="off" data-testid="del-acct-input" />
    {err && <div style={{ marginTop: 6 }}><FieldErr msg={err} /></div>}
    <div style={{ display: "flex", gap: "var(--sp-3)", marginTop: "var(--sp-4)" }}>
      <button className="btn-secondary" onClick={() => setMode("main")} style={{ flex: "0 0 auto" }}>{t("common.cancel")}</button>
      <button className="btn-danger" style={{ flex: 1 }} onClick={del} data-testid="del-acct-confirm">{t("settings.acct_delete_cta")}</button>
    </div>
  </div>;

  if (mode === "archive") return <div>
    <div style={{ fontSize: "var(--fs-body)", color: "var(--ink-dim)", marginBottom: "var(--sp-4)" }}>{t("settings.acct_archive_confirm")}</div>
    <div style={{ display: "flex", gap: "var(--sp-3)" }}>
      <button className="btn-secondary" onClick={() => setMode("main")} style={{ flex: "0 0 auto" }}>{t("common.cancel")}</button>
      <button className="btn-brand" style={{ flex: 1 }} onClick={archive}>{t("settings.acct_archive")}</button>
    </div>
  </div>;

  return <div>
    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: "var(--sp-4)" }}>
      <span className="disp" style={{ fontSize: "var(--fs-h3)", fontWeight: 700 }}>{acct.broker} ••{acct.last4}</span>{statusChip(t, acct.sync_status)}
    </div>
    <label className="lbl" style={{ marginTop: 0 }}>{t("settings.acct_nickname")}</label>
    <input className="inp" value={nick} onChange={(e) => setNick(e.target.value)} data-testid="acct-nick" />
    <button className="btn-brand" style={{ marginTop: "var(--sp-3)" }} onClick={saveNick} data-testid="acct-save-nick">{t("settings.acct_save")}</button>

    <div style={{ borderTop: "1px solid var(--hairline)", margin: "var(--sp-5) 0 var(--sp-4)" }} />
    <div style={{ display: "flex", flexDirection: "column", gap: "var(--sp-2)" }}>
      <button className="btn-secondary" onClick={() => setMode("archive")}>{t("settings.acct_archive")}</button>
      <button className="btn-ghost" onClick={() => toast(t("settings.acct_revoke"))} style={{ textAlign: "left" }}>{t("settings.acct_revoke")}</button>
      <button className="btn-danger" onClick={() => { setMode("delete"); setDelText(""); setErr(null); }} data-testid="acct-delete-open">{t("settings.acct_delete")}</button>
    </div>
  </div>;
}

/* ===========================================================================
   InsightsPage — /app/insights (dimension chips → breakdown → bucket→trades)
   =========================================================================== */
function InsightsPage({ t, toast }) {
  const [phase, setPhase] = useState("loading"); // loading | ready | error | empty
  const [data, setData] = useState(null);
  const [errCode, setErrCode] = useState(null);
  const q = getQuery();
  const dimension = q.get("dimension") || "symbol";
  const acct = q.get("account_id") || "";
  const range = q.get("range") || "30d";
  const DIMS = [["symbol", "dim_symbol"], ["session", "dim_session"], ["weekday", "dim_weekday"]];

  const load = useCallback(() => {
    setPhase("loading");
    const sp = new URLSearchParams(analyticsQS(acct, range).replace(/^\?/, ""));
    sp.set("dimension", dimension);
    window.MTOS_API.get("/analytics/breakdown?" + sp.toString())
      .then((r) => { setData(r.data); setPhase((r.data.buckets && r.data.buckets.length) ? "ready" : "empty"); })
      .catch((e) => { setErrCode((e.envelope && e.envelope.error && e.envelope.error.code) || "error"); setPhase("error"); });
  }, [dimension, acct, range]);
  useEffect(() => { load(); }, [load]);

  // ใช้ dimension ของ payload (กัน transient: URL เปลี่ยนก่อน data refetch → label ผิดชุด)
  const dataDim = data ? data.dimension : dimension;
  const labelOf = (b) => {
    if (dataDim === "session") return t("session." + b.key);
    if (dataDim === "weekday") { const w = t("insights.weekdays_full"); return w[parseInt(b.key, 10)] || b.key; }
    return b.key;
  };
  const bucketLink = (b) => {
    if (dataDim === "symbol") return "/app/trades?symbol=" + encodeURIComponent(b.key);
    if (dataDim === "session") return "/app/trades?session=" + encodeURIComponent(b.key);
    return "/app/trades";
  };
  const maxAbs = data && data.buckets ? Math.max.apply(null, data.buckets.map((b) => Math.abs(b.net_pnl_cents)).concat([1])) : 1;

  return <div>
    <PageHeader t={t} page="insights" />
    <PageFilterBar t={t} />

    {/* dimension chips scroll แนวนอน */}
    <div style={{ display: "flex", gap: 8, overflowX: "auto", paddingBottom: 6, marginBottom: "var(--sp-4)", WebkitOverflowScrolling: "touch" }}>
      {DIMS.map(([k, lk]) => <button key={k} onClick={() => patchQuery({ dimension: k === "symbol" ? null : k })} className={dimension === k ? "chip-on" : "chip"} style={{ minHeight: 38, flex: "0 0 auto" }} data-testid={"dim-" + k}>{t("insights." + lk)}</button>)}
    </div>

    {phase === "loading" && <Skeleton variant="list" />}
    {phase === "error" && <ErrorState t={t} code={errCode} onRetry={load} />}
    {phase === "empty" && <EmptyState t={t} title={t("insights.empty")} body="" />}

    {phase === "ready" && data && <div className="card" style={{ marginBottom: "var(--sp-4)", padding: 0, overflow: "hidden" }}>
      <table className="trades-table" style={{ margin: 0 }}><thead><tr>
        <th style={{ paddingLeft: "var(--sp-4)" }}>{t("insights.col_bucket")}</th><th style={{ textAlign: "right" }}>{t("insights.col_trades")}</th>
        <th style={{ textAlign: "right" }}>{t("insights.col_net")}</th><th style={{ textAlign: "right", paddingRight: "var(--sp-4)" }}>{t("insights.col_winrate")}</th>
      </tr></thead><tbody>
        {data.buckets.map((b) => <tr key={b.key} onClick={() => navigate(bucketLink(b))} data-testid={"bucket-" + b.key}>
          <td style={{ paddingLeft: "var(--sp-4)" }}>
            <div className="disp" style={{ fontWeight: 700, marginBottom: 4 }}>{labelOf(b)}</div>
            <div style={{ height: 6, borderRadius: 999, background: "var(--surface-3)", overflow: "hidden", maxWidth: 220 }}>
              <div style={{ height: "100%", width: (Math.abs(b.net_pnl_cents) / maxAbs * 100) + "%", background: b.net_pnl_cents >= 0 ? "var(--profit)" : "var(--loss)" }} />
            </div>
          </td>
          <td className="num" style={{ textAlign: "right" }}>{b.trades}</td>
          <td style={{ textAlign: "right" }}><MoneyText cents={b.net_pnl_cents} currency="USD" /></td>
          <td className="num" style={{ textAlign: "right", paddingRight: "var(--sp-4)" }}>{fmtPct(b.win_rate)}</td>
        </tr>)}
      </tbody></table>
    </div>}

    {/* AI insight — เร็วๆ นี้ (Locked sample) */}
    <LockedSampleCard t={t}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <span style={{ fontSize: 22 }}>✦</span>
        <div>
          <div className="disp" style={{ fontWeight: 700 }}>{t("insights.ai_card_title")}</div>
          <div style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)" }}>{t("insights.ai_card_body")}</div>
        </div>
        <span style={{ flex: 1 }} />
        <span style={{ fontSize: "var(--fs-micro)", fontWeight: 700, color: "var(--brand-bright)", padding: "2px 8px", borderRadius: "var(--radius-full)", background: "var(--brand-soft)" }}>{t("insights.coming_soon")}</span>
      </div>
    </LockedSampleCard>
  </div>;
}

/* ===========================================================================
   UnlockPage — /app/unlock (pricing จาก mock · ปุ่มซื้อ disabled)
   =========================================================================== */
function UnlockPage({ t, toast }) {
  const [plan, setPlan] = useState(null);
  const [phase, setPhase] = useState("loading");
  useEffect(() => { window.MTOS_API.get("/plans").then((r) => { setPlan(r.data[0]); setPhase("ready"); }).catch(() => setPhase("error")); }, []);
  const feats = t("unlock.features");

  return <div style={{ maxWidth: 520, margin: "0 auto" }}>
    <div style={{ textAlign: "center", marginBottom: "var(--sp-5)" }}>
      <h1 style={{ fontSize: "var(--fs-h1)", marginBottom: 6 }}>{t("unlock.headline")}</h1>
      <div style={{ color: "var(--ink-dim)", fontSize: "var(--fs-body)" }}>{t("unlock.sub")}</div>
    </div>

    {phase === "loading" && <div className="card"><div style={{ height: 260 }}><Skeleton /></div></div>}
    {phase === "error" && <ErrorState t={t} code="plans" onRetry={() => window.location.reload()} />}
    {phase === "ready" && plan && <div className="card" style={{ position: "relative", border: "1px solid var(--brand-edge)", textAlign: "center" }}>
      <span style={{ position: "absolute", top: 12, right: 12, fontSize: "var(--fs-micro)", fontWeight: 800, padding: "3px 10px", borderRadius: "var(--radius-full)", background: "var(--brand-grad)", color: "#fff" }}>{t("unlock.founder_badge")}</span>
      <div className="disp" style={{ fontSize: 40, fontWeight: 800, letterSpacing: "-1px", marginTop: "var(--sp-3)" }} data-testid="plan-price">{fmtPlanPrice(plan.price_cents, plan.currency)}</div>
      <div style={{ fontSize: "var(--fs-caption)", color: "var(--ink-mute)", marginBottom: "var(--sp-2)" }}>{t("unlock.price_lifetime")}</div>
      <div style={{ display: "inline-flex", gap: 6, alignItems: "center", fontSize: "var(--fs-caption)", color: "var(--brand-strong)", fontWeight: 700, marginBottom: "var(--sp-4)" }}>
        <span>{t("unlock.seats_left", { n: plan.seats_left })}</span><span style={{ color: "var(--ink-subtle)", fontWeight: 500 }}>· {t("unlock.seats_note")}</span>
      </div>

      <div style={{ textAlign: "left", borderTop: "1px solid var(--hairline)", paddingTop: "var(--sp-4)", marginBottom: "var(--sp-4)" }}>
        <div className="lbl" style={{ marginTop: 0 }}>{t("unlock.features_title")}</div>
        {feats.map((ft, i) => <div key={i} style={{ display: "flex", gap: 8, alignItems: "flex-start", padding: "4px 0", fontSize: "var(--fs-caption)", color: "var(--ink-dim)" }}>
          <span style={{ color: "var(--profit)" }}>✓</span><span>{ft}</span>
        </div>)}
      </div>

      <button className="btn-brand" style={{ width: "100%", opacity: .55, cursor: "not-allowed" }} disabled data-testid="buy-btn">{t("unlock.buy_cta")} · {t("unlock.coming_soon")}</button>
      <div style={{ fontSize: "var(--fs-micro)", color: "var(--ink-subtle)", marginTop: "var(--sp-3)" }}>{t("unlock.beta_note")}</div>
      <div style={{ marginTop: "var(--sp-2)" }}><a href="#" onClick={(e) => e.preventDefault()} style={{ fontSize: "var(--fs-micro)", color: "var(--ink-mute)" }}>{t("unlock.tos")}</a></div>
    </div>}
  </div>;
}

/* ===========================================================================
   Render (ท้ายไฟล์ — หลัง app.jsx โหลดครบ · App + pages ทั้งหมดพร้อมแล้ว)
   =========================================================================== */
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
