// NekoShop — pages: Cart, Checkout, Success

const { useState: useStateC, useEffect: useEffectC, useMemo: useMemoC, useRef: useRefC } = React;

// ──────────────────────────────────────────────────────────────────
// Helper: build a synthetic price for a total in any currency
// ──────────────────────────────────────────────────────────────────
function totalAs(total, currency) {
  if (currency === "USD")   return { idr: 0, usd: total, robux: 0 };
  if (currency === "Robux") return { idr: 0, usd: 0, robux: total };
  return { idr: total, usd: 0, robux: 0 };
}

// ──────────────────────────────────────────────────────────────────
// CART
// ──────────────────────────────────────────────────────────────────
function CartPage() {
  const { go } = useRouter();
  const { currency } = useCurrency();
  const { items, setQty, remove, clear } = useCart();
  const data = window.NEKO_DATA;

  const lines = items.map(it => {
    const p = data.products.find(x => x.id === it.id);
    return p ? { ...it, product: p } : null;
  }).filter(Boolean);

  const key = priceKey(currency);
  const subtotal = lines.reduce((a, l) => a + effectivePrice(l.product, currency).price[key] * l.qty, 0);
  const total = subtotal;

  return (
    <main className="container" style={{ paddingTop: 56, paddingBottom: 32 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 28 }}>
        <a href="#/" style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go("/");}}>NekoShop</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <span style={{ color: "var(--ink)" }}>Cart</span>
      </div>

      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 40 }}>
        <h1 style={{ fontSize: 44, fontWeight: 600, letterSpacing: "-0.025em", margin: 0 }}>Your cart</h1>
        <div style={{ fontFamily: "var(--mono)", fontSize: 12, color: "var(--muted)" }}>
          {lines.length === 0 ? "Empty" : `${lines.length} ${lines.length === 1 ? "system" : "systems"}`}
        </div>
      </div>

      {lines.length === 0 ? (
        <div style={{
          padding: "96px 32px", border: "1px dashed var(--border)", borderRadius: 8, textAlign: "center",
          background: "oklch(0.98 0.005 60)",
        }}>
          <PixelCat size={48} accentColor="var(--accent)" />
          <div style={{ fontFamily: "var(--mono)", fontSize: 12, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginTop: 18 }}>
            Nothing here yet
          </div>
          <div style={{ fontSize: 18, fontWeight: 600, marginTop: 8 }}>Your cart is empty.</div>
          <div style={{ color: "var(--muted)", fontSize: 14, marginTop: 6, marginBottom: 24 }}>Pick something out — there are 24 systems to choose from.</div>
          <Btn primary onClick={() => go("/browse")}>Browse the catalog →</Btn>
        </div>
      ) : (
        <div style={{ display: "grid", gridTemplateColumns: "1.6fr 1fr", gap: 48, alignItems: "flex-start" }}>
          <div>
            <div style={{ borderTop: "1px solid var(--border)" }}>
              {lines.map(l => (
                <CartLine key={l.id} line={l} setQty={setQty} remove={remove} currency={currency} go={go} />
              ))}
            </div>
            <button onClick={clear} style={{ marginTop: 18, background: "transparent", border: "none", color: "var(--muted)", fontFamily: "var(--mono)", fontSize: 12, letterSpacing: "0.06em", textTransform: "uppercase", cursor: "pointer" }}>
              × Clear cart
            </button>
          </div>

          <aside style={{ position: "sticky", top: 96 }}>
            <div style={{ padding: 28, border: "1px solid var(--border)", borderRadius: 6 }}>
              <h2 style={{ fontSize: 18, fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }}>Order summary</h2>
              <div style={{ marginTop: 20, display: "flex", flexDirection: "column", gap: 10, fontSize: 14 }}>
                <Row label="Subtotal" value={formatPrice(totalAs(subtotal, currency), currency)} muted />
                <Row label="Processing" value="—" muted />
                <Row label="Updates (12mo)" value="Included" muted />
              </div>
              <div style={{ marginTop: 18, paddingTop: 18, borderTop: "1px solid var(--border)" }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
                  <span style={{ fontSize: 14, fontWeight: 600 }}>Total</span>
                  <span style={{ fontFamily: "var(--mono)", fontSize: 26, fontWeight: 600 }}>
                    {formatPrice(totalAs(total, currency), currency)}
                  </span>
                </div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", textAlign: "right", marginTop: 4 }}>
                  {currency === "IDR" && "Charged in Indonesian Rupiah"}
                  {currency === "USD" && "Charged in US Dollars"}
                  {currency === "Robux" && "Pay with Robux via Gamepass"}
                </div>
              </div>
              <div style={{ marginTop: 24 }}>
                <Btn primary full onClick={() => go("/checkout")}>Continue to checkout →</Btn>
              </div>
              <div style={{ marginTop: 16, fontSize: 11, color: "var(--muted)", lineHeight: 1.55, fontFamily: "var(--mono)", textTransform: "uppercase", letterSpacing: "0.04em" }}>
                Pay with QRIS · PayPal · Robux
              </div>
            </div>
          </aside>
        </div>
      )}
    </main>
  );
}

function Row({ label, value, muted }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", color: muted ? "var(--muted)" : "var(--ink)" }}>
      <span>{label}</span>
      <span style={{ fontFamily: "var(--mono)" }}>{value}</span>
    </div>
  );
}

function CartLine({ line, setQty, remove, currency, go }) {
  const data = window.NEKO_DATA;
  const cat = data.cats[line.product.cat];
  const hue = data.hue[line.product.cat];
  const key = priceKey(currency);
  return (
    <div style={{ display: "grid", gridTemplateColumns: "120px 1fr auto", gap: 24, padding: "20px 0", borderBottom: "1px solid var(--border)", alignItems: "center" }}>
      <div onClick={() => go(`/p/${line.product.id}`)} style={{ cursor: "pointer" }}>
        {line.product.video && line.product.video.kind === "youtube" ? (
          <div style={{ position: "relative", width: "100%", aspectRatio: "16/10", borderRadius: 4, overflow: "hidden", background: "var(--ink)" }}>
            <img src={`https://i.ytimg.com/vi/${line.product.video.id}/mqdefault.jpg`} alt="" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }} />
          </div>
        ) : (
          <StripedPlaceholder hue={hue} aspect="16/10" small label="" />
        )}
      </div>
      <div>
        <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>
          {cat.label}
        </div>
        <div onClick={() => go(`/p/${line.product.id}`)} style={{ fontSize: 16, fontWeight: 600, cursor: "pointer", letterSpacing: "-0.01em" }}>
          {line.product.name}
        </div>
        <div style={{ fontSize: 13, color: "var(--muted)", marginTop: 4 }}>
          v{line.product.version} · {line.product.creator}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 16, marginTop: 12 }}>
          <QtyStepper qty={line.qty} setQty={(q) => setQty(line.id, q)} />
          <button onClick={() => remove(line.id)} style={{ background: "transparent", border: "none", color: "var(--muted)", fontSize: 12, fontFamily: "var(--mono)", letterSpacing: "0.04em", textTransform: "uppercase", cursor: "pointer" }}>
            Remove
          </button>
        </div>
      </div>
      <div style={{ textAlign: "right" }}>
        <div style={{ fontFamily: "var(--mono)", fontSize: 18, fontWeight: 600 }}>
          {formatPrice(totalAs(effectivePrice(line.product, currency).price[key] * line.qty, currency), currency)}
        </div>
        {line.qty > 1 ? (
          <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", marginTop: 4 }}>
            <PriceLabel product={line.product} currency={currency} size="sm" /> × {line.qty}
          </div>
        ) : (
          effectivePrice(line.product, currency).isEvent && (
            <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", marginTop: 4, textDecoration: "line-through" }}>
              {formatPrice(line.product.price, currency)}
            </div>
          )
        )}
      </div>
    </div>
  );
}

function QtyStepper({ qty, setQty }) {
  return (
    <div style={{ display: "inline-flex", alignItems: "center", border: "1px solid var(--border)", borderRadius: 999 }}>
      <button onClick={() => setQty(qty - 1)} style={{ width: 30, height: 30, border: "none", background: "transparent", cursor: "pointer", color: "var(--ink)", fontSize: 16 }}>−</button>
      <span style={{ minWidth: 24, textAlign: "center", fontFamily: "var(--mono)", fontSize: 13 }}>{qty}</span>
      <button onClick={() => setQty(qty + 1)} style={{ width: 30, height: 30, border: "none", background: "transparent", cursor: "pointer", color: "var(--ink)", fontSize: 16 }}>+</button>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────
// CHECKOUT
// ──────────────────────────────────────────────────────────────────
function CheckoutPage() {
  const { go } = useRouter();
  const { currency } = useCurrency();
  const { items, clear } = useCart();
  const data = window.NEKO_DATA;
  const cfg = window.NEKO_CONFIG;

  const [step, setStep] = useStateC(1); // 1: details, 2: payment
  const [name, setName] = useStateC("");
  const [robloxUser, setRobloxUser] = useStateC("");
  const [discord, setDiscord] = useStateC("");
  const [payment, setPayment] = useStateC(currency === "USD" ? "paypal" : currency === "Robux" ? "robux" : "qris");
  const [processing, setProcessing] = useStateC(false);

  // Cloudflare Turnstile refs + effect. These MUST be declared before any early
  // return so the hook count stays constant across renders (e.g. when the cart
  // empties after completing an order).
  const turnstileRef = useRefC(null);     // DOM container
  const widgetIdRef = useRefC(null);      // widget handle
  const tokenRef = useRefC(null);         // most recent solved token

  useEffectC(() => {
    const cfg = window.NEKO_CONFIG;
    if (!cfg.turnstileSiteKey || cfg.turnstileSiteKey.includes("REPLACE")) return;
    if (step !== 2) return;
    let cancelled = false;
    const tryRender = () => {
      if (cancelled) return;
      if (!window.turnstile || !turnstileRef.current) {
        setTimeout(tryRender, 200);
        return;
      }
      if (widgetIdRef.current !== null) return;
      try {
        widgetIdRef.current = window.turnstile.render(turnstileRef.current, {
          sitekey: cfg.turnstileSiteKey,
          // Managed mode — renders a normal widget that auto-solves and shows a
          // checkbox only to borderline visitors. Token arrives via callback.
          retry: "auto",
          callback: (token) => { tokenRef.current = token; },
          "expired-callback": () => { tokenRef.current = null; },
          "error-callback": () => { tokenRef.current = null; },
        });
      } catch (err) { console.warn("Turnstile render failed:", err); }
    };
    tryRender();
    return () => { cancelled = true; };
  }, [step]);

  // Payment method determines the displayed currency.
  const paymentCurrency = payment === "qris" ? "IDR" : payment === "paypal" ? "USD" : "Robux";
  const payKey = priceKey(paymentCurrency);

  const lines = items.map(it => ({ ...it, product: data.products.find(x => x.id === it.id) })).filter(l => l.product);
  const subtotal = lines.reduce((a, l) => a + effectivePrice(l.product, paymentCurrency).price[payKey] * l.qty, 0);
  const total = subtotal;

  if (lines.length === 0) {
    return (
      <main className="container" style={{ paddingTop: 96, paddingBottom: 96, textAlign: "center" }}>
        <h1 style={{ fontSize: 28, fontWeight: 600 }}>Nothing to check out.</h1>
        <p style={{ color: "var(--muted)" }}>Add a system first.</p>
        <Btn onClick={() => go("/browse")}>← Browse</Btn>
      </main>
    );
  }

  const canContinue = name.trim() && robloxUser.trim();

  // Stable per-browser fingerprint sent to the proxy Worker for spam detection.
  const getFingerprint = () => {
    try {
      let fp = localStorage.getItem("neko.fp");
      if (!fp) {
        fp = (crypto.randomUUID && crypto.randomUUID()) || (Math.random().toString(36).slice(2) + Date.now().toString(36));
        localStorage.setItem("neko.fp", fp);
      }
      return fp;
    } catch { return "unknown"; }
  };


  // Returns the current Turnstile token. In Managed mode the widget solves
  // automatically and stores the token via callback; we wait up to ~10s for it
  // to be ready (in case the buyer clicks "I've paid" before it resolves).
  const getTurnstileToken = () => new Promise((resolve) => {
    const cfg = window.NEKO_CONFIG;
    if (!cfg.turnstileSiteKey || cfg.turnstileSiteKey.includes("REPLACE")) {
      resolve(null); return;
    }
    if (tokenRef.current) { resolve(tokenRef.current); return; }
    // Poll for the token for up to 10 seconds
    let waited = 0;
    const iv = setInterval(() => {
      if (tokenRef.current) { clearInterval(iv); resolve(tokenRef.current); return; }
      waited += 250;
      if (waited >= 10000) { clearInterval(iv); resolve(null); }
    }, 250);
  });

  // Sends the order to the proxy Worker. The Worker holds the Discord webhook
  // URLs as encrypted secrets and verifies the Turnstile token before forwarding.
  const fireOrder = async (orderId) => {
    const cfg = window.NEKO_CONFIG;
    if (!cfg.apiBase) return;
    const turnstileToken = await getTurnstileToken();
    const payload = {
      orderId,
      customer: { name, robloxUser: robloxUser.replace(/^@/, ""), discord },
      payment: { method: payment, currency: paymentCurrency, total },
      lines: lines.map(l => ({ id: l.product.id, name: l.product.name, qty: l.qty })),
    };
    try {
      await fetch(cfg.apiBase.replace(/\/$/, "") + "/order", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Neko-Client": getFingerprint(),
          "X-Turnstile-Token": turnstileToken || "",
        },
        body: JSON.stringify(payload),
      });
    } catch (err) {
      console.warn("Order proxy failed:", err);
    }
    // Turnstile tokens are single-use — clear + reset so a later order re-solves.
    tokenRef.current = null;
    try { if (window.turnstile && widgetIdRef.current !== null) window.turnstile.reset(widgetIdRef.current); } catch {}
  };

  const completeOrder = () => {
    setProcessing(true);
    const orderId = "NK-" + Math.random().toString(36).slice(2, 7).toUpperCase() + "-" + Math.floor(Math.random() * 9000 + 1000);
    fireOrder(orderId);
    setTimeout(() => {
      try {
        localStorage.setItem("neko.lastOrder", JSON.stringify({
          orderId, name, robloxUser, discord, payment,
          paymentCurrency, total,
          lines: lines.map(l => ({ id: l.product.id, name: l.product.name, qty: l.qty, price: l.product.price })),
          date: new Date().toISOString(),
        }));
      } catch {}
      clear();
      go(`/success/${orderId}`);
    }, 1400);
  };

  return (
    <main className="container" style={{ paddingTop: 56, paddingBottom: 32 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 28 }}>
        <a href="#/" style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go("/");}}>NekoShop</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <a href="#/cart" style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go("/cart");}}>Cart</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <span style={{ color: "var(--ink)" }}>Checkout</span>
      </div>

      <div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 40 }}>
        <StepDot n="01" label="Details" active={step >= 1} done={step > 1} />
        <div style={{ flex: 0, width: 56, height: 1, background: step > 1 ? "var(--ink)" : "var(--border)" }} />
        <StepDot n="02" label="Payment" active={step >= 2} done={false} />
        <div style={{ flex: 1 }} />
        <h1 style={{ fontSize: 32, fontWeight: 600, letterSpacing: "-0.025em", margin: 0 }}>Checkout</h1>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 48, alignItems: "flex-start" }}>
        <div>
          {step === 1 ? (
            <div>
              <SectionHeading kicker="01 — Details" title="Your delivery info." />
              <p style={{ fontSize: 14, color: "var(--muted)", lineHeight: 1.6, marginTop: -8, marginBottom: 28 }}>
                The system is delivered <strong style={{ color: "var(--ink)" }}>directly to your Roblox toolbox</strong>. We need your Roblox username so we can send it. Discord is recommended so you can open a ticket with your receipt.
              </p>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
                <Field label="Your name *" value={name} onChange={setName} placeholder="Neko_Omen" />
                <Field label="Roblox username *" value={robloxUser} onChange={setRobloxUser} placeholder="@YourRobloxUser" prefix="@" />
                <Field label="Discord username (recommended)" value={discord} onChange={setDiscord} placeholder="yourdiscord" />
              </div>
              <p style={{ fontSize: 12, color: "var(--muted)", marginTop: 16, fontFamily: "var(--mono)", letterSpacing: "0.04em" }}>
                * Required for delivery. Seller will add <strong>Neko_0men</strong> to your Roblox friends and send the package to your toolbox.
              </p>
              <div style={{ display: "flex", gap: 12, marginTop: 32 }}>
                <Btn onClick={() => go("/cart")}>← Back to cart</Btn>
                <Btn primary onClick={() => setStep(2)} disabled={!canContinue}>Continue to payment →</Btn>
              </div>
            </div>
          ) : (
            <div>
              <SectionHeading kicker="02 — Payment" title="Choose a payment method." />
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 28 }}>
                <PayOpt active={payment==="qris"}   onClick={() => setPayment("qris")}   title="QRIS"   subtitle="GoPay · OVO · DANA · ShopeePay" icon={<QrisIcon />}   note="For Indonesia · IDR" />
                <PayOpt active={payment==="paypal"} onClick={() => setPayment("paypal")} title="PayPal" subtitle="Friends &amp; Family" icon={<PaypalIcon />} note="International · USD" />
                <PayOpt active={payment==="robux"}  onClick={() => setPayment("robux")}  title="Robux"  subtitle="Buy gamepass in-game" icon={<RobuxIcon />}  note="In-game · R$" />
              </div>

              {payment === "qris"   && <QrisPanel total={total} />}
              {payment === "paypal" && <PaypalPanel total={total} />}
              {payment === "robux"  && <RobuxPanel total={total} />}

              <div style={{ display: "flex", gap: 12, marginTop: 32 }}>
                <Btn onClick={() => setStep(1)}>← Edit details</Btn>
                <Btn primary onClick={completeOrder} disabled={processing}>
                  {processing ? "Processing…" : "I've paid — confirm"}
                </Btn>
              </div>
              {/* Cloudflare Turnstile — verifies the buyer is human before the order sends */}
              <div ref={turnstileRef} style={{ marginTop: 16 }} />
              <p style={{ fontSize: 12, color: "var(--muted)", marginTop: 16, fontFamily: "var(--mono)", letterSpacing: "0.04em", textTransform: "uppercase" }}>
                After confirming, you'll get delivery instructions (Discord ticket + Roblox handoff).
              </p>
            </div>
          )}
        </div>

        <aside style={{ position: "sticky", top: 96 }}>
          <div style={{ padding: 24, border: "1px solid var(--border)", borderRadius: 6, background: "oklch(0.985 0.003 60)" }}>
            <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 16 }}>Order summary</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              {lines.map(l => (
                <div key={l.id} style={{ display: "flex", gap: 12 }}>
                  <div style={{ width: 56, flexShrink: 0 }}>
                    {l.product.video && l.product.video.kind === "youtube" ? (
                      <div style={{ position: "relative", width: "100%", aspectRatio: "1/1", borderRadius: 4, overflow: "hidden", background: "var(--ink)" }}>
                        <img src={`https://i.ytimg.com/vi/${l.product.video.id}/mqdefault.jpg`} alt="" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }} />
                      </div>
                    ) : (
                      <StripedPlaceholder hue={data.hue[l.product.cat]} aspect="1/1" small label="" />
                    )}
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 600, lineHeight: 1.3 }}>{l.product.name}</div>
                    <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", marginTop: 2 }}>Qty {l.qty}</div>
                  </div>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 13, fontWeight: 600, whiteSpace: "nowrap" }}>
                    {step === 2
                      ? formatPrice(totalAs(effectivePrice(l.product, paymentCurrency).price[payKey] * l.qty, paymentCurrency), paymentCurrency)
                      : formatPrice(totalAs(effectivePrice(l.product, currency).price[priceKey(currency)] * l.qty, currency), currency)
                    }
                  </div>
                </div>
              ))}
            </div>
            <div style={{ marginTop: 20, paddingTop: 16, borderTop: "1px solid var(--border)", display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
              <span style={{ fontSize: 13, fontWeight: 600 }}>Total</span>
              <span style={{ fontFamily: "var(--mono)", fontSize: 22, fontWeight: 600 }}>
                {step === 2 ? formatPrice(totalAs(total, paymentCurrency), paymentCurrency) : formatPrice(totalAs(lines.reduce((a,l)=>a+effectivePrice(l.product, currency).price[priceKey(currency)]*l.qty,0), currency), currency)}
              </span>
            </div>
            {step === 2 && (
              <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", textAlign: "right", marginTop: 6, letterSpacing: "0.04em", textTransform: "uppercase" }}>
                Charged in {paymentCurrency === "Robux" ? "Robux" : paymentCurrency}
              </div>
            )}
          </div>
        </aside>
      </div>
    </main>
  );
}

function StepDot({ n, label, active, done }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
      <div style={{
        width: 26, height: 26, borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center",
        background: active ? "var(--ink)" : "var(--bg)", color: active ? "var(--bg)" : "var(--muted)",
        border: "1px solid", borderColor: active ? "var(--ink)" : "var(--border)",
        fontFamily: "var(--mono)", fontSize: 10, fontWeight: 600, letterSpacing: "0.04em",
      }}>
        {done ? "✓" : n}
      </div>
      <span style={{ fontSize: 13, fontWeight: 500, color: active ? "var(--ink)" : "var(--muted)" }}>{label}</span>
    </div>
  );
}

function Field({ label, value, onChange, placeholder, type = "text", prefix }) {
  return (
    <label style={{ display: "block" }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 8 }}>{label}</div>
      <div style={{
        display: "flex", alignItems: "center", border: "1px solid var(--border)", borderRadius: 6,
        background: "var(--bg)",
      }}>
        {prefix && <span style={{ paddingLeft: 14, fontFamily: "var(--mono)", color: "var(--muted)", fontSize: 14 }}>{prefix}</span>}
        <input type={type} value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} style={{
          flex: 1, padding: "12px 16px", border: "none",
          fontFamily: "var(--sans)", fontSize: 14, outline: "none", background: "transparent", color: "var(--ink)",
          paddingLeft: prefix ? 4 : 16,
        }} />
      </div>
    </label>
  );
}

function PayOpt({ active, onClick, title, subtitle, icon, note }) {
  return (
    <button onClick={onClick} style={{
      padding: 18, border: "1px solid", borderColor: active ? "var(--ink)" : "var(--border)",
      borderRadius: 6, background: "var(--bg)", cursor: "pointer", textAlign: "left",
      position: "relative", color: "var(--ink)",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <div style={{ width: 40, height: 40, border: "1px solid var(--border)", borderRadius: 6, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>{icon}</div>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: "-0.01em" }}>{title}</div>
          <div style={{ fontSize: 11, color: "var(--muted)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} dangerouslySetInnerHTML={{ __html: subtitle }} />
        </div>
        <div style={{
          width: 18, height: 18, borderRadius: "50%", border: "1px solid", borderColor: active ? "var(--ink)" : "var(--border)",
          display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
        }}>
          {active && <div style={{ width: 9, height: 9, borderRadius: "50%", background: "var(--ink)" }} />}
        </div>
      </div>
      <div style={{ marginTop: 12, paddingTop: 10, borderTop: "1px solid var(--border)", fontSize: 10, color: "var(--muted)", fontFamily: "var(--mono)", letterSpacing: "0.06em", textTransform: "uppercase" }}>
        {note}
      </div>
    </button>
  );
}

// ──────────────────────────────────────────────────────────────────
// QRIS panel — uses the real merchant QR image
// ──────────────────────────────────────────────────────────────────
function QrisPanel({ total }) {
  const cfg = window.NEKO_CONFIG;
  return (
    <div style={{ padding: 28, border: "1px solid var(--border)", borderRadius: 6, display: "grid", gridTemplateColumns: "auto 1fr", gap: 36, alignItems: "flex-start" }}>
      <div style={{ width: 248, padding: 10, background: "var(--bg)", border: "1px solid var(--border)", borderRadius: 6 }}>
        <img src={cfg.qris.image} alt="QRIS — HOLLOWXNEKO_OMEN, GAMING" style={{ width: "100%", display: "block", borderRadius: 4 }} />
      </div>
      <div>
        <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 8 }}>
          Step 1 — Scan
        </div>
        <h3 style={{ fontSize: 22, fontWeight: 600, margin: 0, letterSpacing: "-0.015em" }}>Scan with any e-wallet</h3>
        <p style={{ fontSize: 14, color: "var(--muted)", marginTop: 10, lineHeight: 1.55, marginBottom: 0 }}>
          Buka aplikasi GoPay, OVO, DANA, ShopeePay, LinkAja, atau mobile banking yang mendukung QRIS, lalu scan kode di samping.
        </p>
        <div style={{ marginTop: 20, padding: 16, border: "1px dashed var(--border)", borderRadius: 6, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, fontFamily: "var(--mono)", fontSize: 12 }}>
          <div>
            <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>Merchant</div>
            <div style={{ fontSize: 13 }}>{cfg.qris.merchant}</div>
          </div>
          <div>
            <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>Amount</div>
            <div style={{ fontSize: 13 }}>{formatPrice(totalAs(total, "IDR"), "IDR")}</div>
          </div>
          <div>
            <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>NMID</div>
            <div style={{ fontSize: 13 }}>{cfg.qris.nmid}</div>
          </div>
          <div>
            <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>Type</div>
            <div style={{ fontSize: 13 }}>{cfg.qris.code} · Static</div>
          </div>
        </div>
        <p style={{ fontSize: 12, color: "var(--muted)", marginTop: 16, lineHeight: 1.55, marginBottom: 0 }}>
          <strong style={{ color: "var(--ink)" }}>Penting:</strong> simpan screenshot bukti pembayaran. Kamu butuh untuk klaim di Discord ticket setelah ini.
        </p>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────
// PayPal panel — always shows USD
// ──────────────────────────────────────────────────────────────────
function PaypalPanel({ total }) {
  const cfg = window.NEKO_CONFIG;
  return (
    <div style={{ padding: 28, border: "1px solid var(--border)", borderRadius: 6 }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 36 }}>
        <div>
          <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 8 }}>Send to</div>
          <div style={{ fontSize: 22, fontWeight: 600, fontFamily: "var(--mono)", wordBreak: "break-all" }}>{cfg.paypal.email}</div>
          <div style={{ fontSize: 13, color: "var(--muted)", marginTop: 4 }}>{cfg.paypal.name} · {cfg.paypal.handle}</div>
          <div style={{ marginTop: 20, padding: 16, border: "1px dashed var(--border)", borderRadius: 6, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, fontFamily: "var(--mono)", fontSize: 12 }}>
            <div>
              <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>Amount</div>
              <div style={{ fontSize: 13 }}>{formatPrice(totalAs(total, "USD"), "USD")}</div>
            </div>
            <div>
              <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>Type</div>
              <div style={{ fontSize: 13 }}>Friends &amp; Family</div>
            </div>
            <div style={{ gridColumn: "1 / -1" }}>
              <div style={{ color: "var(--muted)", fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>Reference / note</div>
              <div style={{ fontSize: 13 }}>NekoShop order</div>
            </div>
          </div>
        </div>
        <div>
          <h3 style={{ fontSize: 14, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--muted)", margin: 0, marginBottom: 14, fontFamily: "var(--mono)" }}>How to pay</h3>
          <ol style={{ paddingLeft: 18, margin: 0, fontSize: 13, color: "var(--ink)", lineHeight: 1.7 }}>
            <li>Open the PayPal app or paypal.com.</li>
            <li>Send <strong>${total} USD</strong> to <strong style={{ wordBreak: "break-all" }}>{cfg.paypal.email}</strong>.</li>
            <li>Choose <strong>Friends &amp; Family</strong> if you can.</li>
            <li>Screenshot the receipt.</li>
            <li>Click <strong>I've paid</strong> — you'll get Discord steps to claim it.</li>
          </ol>
        </div>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────
// Robux panel — always shows R$ amount
// ──────────────────────────────────────────────────────────────────
function RobuxPanel({ total }) {
  const cfg = window.NEKO_CONFIG;
  return (
    <div style={{ padding: 28, border: "1px solid var(--border)", borderRadius: 6 }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 36 }}>
        <div>
          <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 8 }}>Pay in-game</div>
          <div style={{ fontSize: 32, fontWeight: 700, fontFamily: "var(--mono)", letterSpacing: "-0.01em" }}>R$ {Number(total).toLocaleString("en-US")}</div>
          <div style={{ fontSize: 13, color: "var(--muted)", marginTop: 6 }}>via gamepasses on the NekoShop store</div>
          <a href={cfg.robloxStoreUrl} target="_blank" rel="noreferrer" style={{
            display: "inline-flex", alignItems: "center", gap: 10, marginTop: 20,
            padding: "12px 20px", borderRadius: 999,
            background: "var(--ink)", color: "var(--bg)", textDecoration: "none",
            fontFamily: "var(--sans)", fontSize: 14, fontWeight: 500,
          }}>
            <RobuxIcon inverted />
            Open store on Roblox ↗
          </a>
          <div style={{ marginTop: 20, padding: 14, border: "1px dashed var(--border)", borderRadius: 6, fontFamily: "var(--mono)", fontSize: 12, color: "var(--muted)", lineHeight: 1.6 }}>
            Tip: the seller (<strong style={{ color: "var(--ink)" }}>{cfg.robloxSeller}</strong>) will verify the gamepass purchase by Roblox username, so make sure yours matches the one you typed in step 1.
          </div>
        </div>
        <div>
          <h3 style={{ fontSize: 14, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--muted)", margin: 0, marginBottom: 14, fontFamily: "var(--mono)" }}>How to pay</h3>
          <ol style={{ paddingLeft: 18, margin: 0, fontSize: 13, color: "var(--ink)", lineHeight: 1.7 }}>
            <li>Click <strong>Open store on Roblox</strong> →</li>
            <li>Find the gamepass that matches <strong>R$ {Number(total).toLocaleString("en-US")}</strong>.</li>
            <li>Purchase it on your Roblox account.</li>
            <li>Screenshot the receipt.</li>
            <li>Click <strong>I've paid</strong> below.</li>
          </ol>
        </div>
      </div>
    </div>
  );
}

function QrisIcon() {
  return (
    <svg width="22" height="22" viewBox="0 0 14 14" shapeRendering="crispEdges">
      {[[0,0,3,3],[11,0,3,3],[0,11,3,3],[5,5,1,1],[7,5,2,2],[5,8,2,2],[8,8,1,1],[10,8,1,1],[10,10,1,1]].map((r,i) => <rect key={i} x={r[0]} y={r[1]} width={r[2]} height={r[3]} fill="var(--ink)" />)}
      {[[1,1,1,1],[12,1,1,1],[1,12,1,1]].map((r,i) => <rect key={"i"+i} x={r[0]} y={r[1]} width={r[2]} height={r[3]} fill="var(--bg)" />)}
    </svg>
  );
}
function PaypalIcon() {
  return (
    <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
      <path d="M7 19l1.5-10h5c2 0 3.5 1.5 3 3.5C16 15 14 16 12 16h-2l-.5 3H7z" fill="var(--ink)"/>
      <path d="M9 17l1.5-10h5c2 0 3.5 1.5 3 3.5C18 13 16 14 14 14h-2l-.5 3H9z" fill="var(--accent)" opacity="0.75"/>
    </svg>
  );
}

function RobuxIcon({ inverted }) {
  const fg = inverted ? "var(--bg)" : "var(--ink)";
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <rect x="2.5" y="2.5" width="17" height="17" rx="2" stroke={fg} strokeWidth="1.6" transform="rotate(8 11 11)" />
      <text x="11" y="14.5" textAnchor="middle" fontFamily="JetBrains Mono, monospace" fontSize="10" fontWeight="700" fill={fg}>R$</text>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────────
// SUCCESS — Discord delivery instructions + install guide
// ──────────────────────────────────────────────────────────────────
function SuccessPage({ id }) {
  const { go } = useRouter();
  const cfg = window.NEKO_CONFIG;
  const data = window.NEKO_DATA;
  const order = useMemoC(() => {
    try { return JSON.parse(localStorage.getItem("neko.lastOrder") || "null"); } catch { return null; }
  }, [id]);

  const paymentLabel = order?.payment === "paypal" ? "PayPal" : order?.payment === "robux" ? "Robux" : "QRIS";
  const totalDisplay = order ? formatPrice(totalAs(order.total, order.paymentCurrency || "IDR"), order.paymentCurrency || "IDR") : "—";

  return (
    <main className="container" style={{ paddingTop: 56, paddingBottom: 32, maxWidth: 960 }}>
      {/* Hero */}
      <div style={{ textAlign: "center" }}>
        <div style={{
          width: 64, height: 64, borderRadius: "50%", background: "var(--accent)",
          margin: "0 auto", display: "flex", alignItems: "center", justifyContent: "center",
        }}>
          <svg width="28" height="28" viewBox="0 0 24 24"><path d="M5 12.5l4.5 4.5L19 7" fill="none" stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
        <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.12em", textTransform: "uppercase", marginTop: 24 }}>
          Payment marked as sent
        </div>
        <h1 style={{ fontSize: 44, fontWeight: 600, letterSpacing: "-0.025em", marginTop: 14, marginBottom: 14 }}>
          Thanks{order?.name ? `, ${order.name.split(" ")[0]}` : ""}.
        </h1>
        <p style={{ fontSize: 16, color: "var(--muted)", lineHeight: 1.6, maxWidth: 580, margin: "0 auto" }}>
          One more step — claim your package on Discord and we'll send it straight to your Roblox toolbox.
        </p>
      </div>

      {/* Order receipt */}
      <div style={{ marginTop: 48, padding: 28, border: "1px solid var(--border)", borderRadius: 6 }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 24, paddingBottom: 20, borderBottom: "1px solid var(--border)" }}>
          <div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)" }}>Order ID</div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 16, fontWeight: 600, marginTop: 4 }}>{id || order?.orderId || "NK-—"}</div>
          </div>
          <div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)" }}>Paid via</div>
            <div style={{ fontSize: 14, fontWeight: 600, marginTop: 4 }}>{paymentLabel}</div>
          </div>
          <div style={{ textAlign: "right" }}>
            <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)" }}>Total</div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 18, fontWeight: 600, marginTop: 4 }}>{totalDisplay}</div>
          </div>
        </div>

        {order?.lines?.length > 0 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 14, marginTop: 20 }}>
            {order.lines.map(l => {
              const p = data.products.find(x => x.id === l.id);
              const hue = p ? data.hue[p.cat] : 22;
              return (
                <div key={l.id} style={{ display: "grid", gridTemplateColumns: "56px 1fr auto", gap: 14, alignItems: "center" }}>
                  <div style={{ width: 56 }}>
                    {p && p.video && p.video.kind === "youtube" ? (
                      <div style={{ position: "relative", width: "100%", aspectRatio: "1/1", borderRadius: 4, overflow: "hidden", background: "var(--ink)" }}>
                        <img src={`https://i.ytimg.com/vi/${p.video.id}/mqdefault.jpg`} alt="" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }} />
                      </div>
                    ) : (
                      <StripedPlaceholder hue={hue} aspect="1/1" small label="" />
                    )}
                  </div>
                  <div>
                    <div style={{ fontSize: 14, fontWeight: 600 }}>{l.name}</div>
                    <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", marginTop: 2 }}>Qty {l.qty} · Pending delivery</div>
                  </div>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase" }}>Awaiting claim</div>
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* Delivery flow */}
      <section style={{ marginTop: 56 }}>
        <SectionHeading kicker="Next — Claim & Delivery" title="How to get your package." />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <DeliveryStep n="01" title="Join the Discord" body={<span>Click below to join <strong>nomenfams</strong>. We coordinate every delivery from there.</span>} cta={<a href={cfg.discordInvite} target="_blank" rel="noreferrer" style={discordBtn}><DiscordIcon /> Open Discord invite ↗</a>} />
          <DeliveryStep n="02" title="Open a ticket" body={<span>In the <strong>#ticket</strong> channel, open a new ticket so a staff member can verify your order.</span>} cta={<a href={cfg.discordTicketUrl} target="_blank" rel="noreferrer" style={discordBtn}><DiscordIcon /> Open ticket channel ↗</a>} />
          <DeliveryStep n="03" title="Send receipt + Roblox username" body={<span>In the ticket, paste your <strong>payment receipt screenshot</strong> and your Roblox username{order?.robloxUser ? <> (<strong style={{ fontFamily: "var(--mono)" }}>@{order.robloxUser.replace(/^@/, "")}</strong>)</> : ""}.</span>} />
          <DeliveryStep n="04" title={<>Add <strong style={{ fontFamily: "var(--mono)" }}>{cfg.robloxSeller}</strong> on Roblox</>} body={<span>Send the seller a friend request on Roblox. The package will be delivered to your toolbox.</span>} />
        </div>
      </section>

      {/* Install guide */}
      <section style={{ marginTop: 64 }}>
        <SectionHeading kicker="How to install" title="Add the system to your Roblox Studio." />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12 }}>
          <InstallStep n="A" title="Open your Toolbox" body="In Roblox Studio, open the Toolbox panel. The model will appear in your Inventory once the seller delivers it." />
          <InstallStep n="B" title="Insert the model" body="Right-click the model and choose Insert. It drops into Workspace, ready to configure." />
          <InstallStep n="C" title="Turn on HTTP + API access" body={<span>Open <strong>Game Settings → Security</strong>. Enable <strong>HTTP Requests</strong> and <strong>API Services</strong>. Most systems require this.</span>} />
        </div>
        <div style={{
          marginTop: 16, padding: "20px 24px", background: "var(--ink)", color: "var(--bg)",
          borderRadius: 6, display: "flex", alignItems: "center", gap: 20,
        }}>
          <YoutubeIcon />
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 600, fontSize: 15 }}>Watch the configuration tutorial</div>
            <div style={{ fontSize: 13, color: "oklch(0.75 0.005 60)", marginTop: 2 }}>
              Each system has a step-by-step video on the <strong style={{ color: "var(--bg)" }}>Neko_Omen YouTube</strong> channel.
            </div>
          </div>
          <a href={cfg.youtubeChannel} target="_blank" rel="noreferrer" style={{
            padding: "10px 18px", background: "var(--accent)", color: "white", borderRadius: 999,
            textDecoration: "none", fontSize: 13, fontWeight: 500,
          }}>
            Open YouTube ↗
          </a>
        </div>
      </section>

      <div style={{ marginTop: 56, textAlign: "center", display: "flex", justifyContent: "center", gap: 12 }}>
        <Btn onClick={() => go("/")}>← Home</Btn>
        <Btn primary onClick={() => go("/browse")}>Keep shopping</Btn>
      </div>
    </main>
  );
}

const discordBtn = {
  display: "inline-flex", alignItems: "center", gap: 10, marginTop: 14,
  padding: "10px 16px", borderRadius: 999,
  background: "var(--accent)", color: "white", textDecoration: "none",
  fontFamily: "var(--sans)", fontSize: 13, fontWeight: 500,
};

function DeliveryStep({ n, title, body, cta }) {
  return (
    <div style={{ padding: 24, border: "1px solid var(--border)", borderRadius: 6 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 10 }}>
        <div style={{
          width: 28, height: 28, borderRadius: "50%", background: "var(--accent)", color: "white",
          display: "flex", alignItems: "center", justifyContent: "center",
          fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: "0.04em",
        }}>{n}</div>
        <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: "-0.01em" }}>{title}</div>
      </div>
      <div style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.6 }}>{body}</div>
      {cta && cta}
    </div>
  );
}

function InstallStep({ n, title, body }) {
  return (
    <div style={{ padding: 20, border: "1px solid var(--border)", borderRadius: 6 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--accent)", letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 8 }}>
        Step {n}
      </div>
      <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: "-0.01em", marginBottom: 6 }}>{title}</div>
      <div style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.55 }}>{body}</div>
    </div>
  );
}

function DiscordIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="white">
      <path d="M13.5 3.5A12 12 0 0 0 10.4 2.6l-.2.4a10.6 10.6 0 0 0-4.4 0l-.2-.4A12 12 0 0 0 2.5 3.5C1 6 .5 8.5 1 11c1.4 1 2.7 1.6 4 2l.6-.9a8 8 0 0 1-1.4-.7l.3-.2a8.4 8.4 0 0 0 7 0l.3.2a8 8 0 0 1-1.4.7l.6.9c1.3-.4 2.6-1 4-2 .6-3-.2-5.4-1.5-7.5zM6 9.5c-.6 0-1.2-.6-1.2-1.4S5.4 6.7 6 6.7c.7 0 1.2.6 1.2 1.4S6.7 9.5 6 9.5zm4 0c-.6 0-1.2-.6-1.2-1.4s.6-1.4 1.2-1.4 1.2.6 1.2 1.4-.5 1.4-1.2 1.4z"/>
    </svg>
  );
}
function YoutubeIcon() {
  return (
    <div style={{
      width: 44, height: 44, borderRadius: 6, background: "var(--accent)",
      display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
    }}>
      <svg width="20" height="20" viewBox="0 0 22 22"><polygon points="7,4 7,20 21,12" fill="white"/></svg>
    </div>
  );
}

Object.assign(window, { CartPage, CheckoutPage, SuccessPage });
