nac-presale/backup/20260309_203736/Home.tsx.bak

1379 lines
69 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// NAC XIC Token Presale — Main Page v3.0
// Features: Real on-chain data | Bilingual (EN/ZH) | TRC20 Live Feed | Wallet Connect
// Design: Dark Cyberpunk / Quantum Finance
// Colors: Amber Gold #f0b429 | Quantum Blue #00d4ff | Deep Black #0a0a0f
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { toast } from "sonner";
import { Link } from "wouter";
import { useWallet } from "@/hooks/useWallet";
import { usePresale } from "@/hooks/usePresale";
import { CONTRACTS, PRESALE_CONFIG, formatNumber, shortenAddress } from "@/lib/contracts";
import { trpc } from "@/lib/trpc";
import { type Lang, useTranslation } from "@/lib/i18n";
import { WalletSelector } from "@/components/WalletSelector";
// ─── Network Tab Types ────────────────────────────────────────────────────────
type NetworkTab = "BSC" | "ETH" | "TRON";
// ─── Assets ───────────────────────────────────────────────────────────────────
const HERO_BG = "https://d2xsxph8kpxj0f.cloudfront.net/310519663287655625/Ngki3MumDNGduV3xJt3mga/nac-hero-bg_7c6c173e.jpg";
const TOKEN_ICON = "https://d2xsxph8kpxj0f.cloudfront.net/310519663287655625/Ngki3MumDNGduV3xJt3mga/nac-token-icon_382e5c30.png";
// ─── Fallback stats while loading ─────────────────────────────────────────────
const FALLBACK_STATS = {
totalUsdtRaised: 0,
totalTokensSold: 0,
hardCap: 5_000_000,
progressPct: 0,
};
// ─── Countdown Timer ──────────────────────────────────────────────────────────
function useCountdown(targetDate: Date) {
const [timeLeft, setTimeLeft] = useState({ days: 0, hours: 0, minutes: 0, seconds: 0 });
useEffect(() => {
const tick = () => {
const diff = targetDate.getTime() - Date.now();
if (diff <= 0) { setTimeLeft({ days: 0, hours: 0, minutes: 0, seconds: 0 }); return; }
setTimeLeft({
days: Math.floor(diff / 86400000),
hours: Math.floor((diff % 86400000) / 3600000),
minutes: Math.floor((diff % 3600000) / 60000),
seconds: Math.floor((diff % 60000) / 1000),
});
};
tick();
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, [targetDate]);
return timeLeft;
}
// ─── Animated Counter ─────────────────────────────────────────────────────────
function AnimatedCounter({ value, prefix = "", suffix = "" }: { value: number; prefix?: string; suffix?: string }) {
const [display, setDisplay] = useState(0);
useEffect(() => {
if (value === 0) return;
let start = 0;
const step = value / 60;
const id = setInterval(() => {
start += step;
if (start >= value) { setDisplay(value); clearInterval(id); }
else setDisplay(Math.floor(start));
}, 16);
return () => clearInterval(id);
}, [value]);
return <span className="counter-digit">{prefix}{display.toLocaleString()}{suffix}</span>;
}
// ─── Network Icon ─────────────────────────────────────────────────────────────
function NetworkIcon({ network }: { network: NetworkTab }) {
if (network === "BSC") return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="12" fill="#F0B90B"/>
<path d="M9.4 12L12 9.4L14.6 12L12 14.6L9.4 12Z" fill="white"/>
<path d="M6 12L8.6 9.4L11.2 12L8.6 14.6L6 12Z" fill="white" opacity="0.7"/>
<path d="M12.8 12L15.4 9.4L18 12L15.4 14.6L12.8 12Z" fill="white" opacity="0.7"/>
<path d="M9.4 8.6L12 6L14.6 8.6L12 11.2L9.4 8.6Z" fill="white" opacity="0.5"/>
<path d="M9.4 15.4L12 12.8L14.6 15.4L12 18L9.4 15.4Z" fill="white" opacity="0.5"/>
</svg>
);
if (network === "ETH") return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="12" fill="#627EEA"/>
<path d="M12 4L7 12.5L12 15.5L17 12.5L12 4Z" fill="white" opacity="0.9"/>
<path d="M12 16.5L7 13.5L12 20L17 13.5L12 16.5Z" fill="white" opacity="0.7"/>
</svg>
);
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="12" fill="#FF0013"/>
<path d="M12 5L19 9.5V14.5L12 19L5 14.5V9.5L12 5Z" fill="white" opacity="0.9"/>
<path d="M12 8L16 10.5V13.5L12 16L8 13.5V10.5L12 8Z" fill="#FF0013"/>
</svg>
);
}
// ─── Step Badge ───────────────────────────────────────────────────────────────
function StepBadge({ num, text }: { num: number; text: string }) {
return (
<div className="flex items-start gap-3">
<div className="step-num">{num}</div>
<span className="text-sm text-white/70 leading-relaxed">{text}</span>
</div>
);
}
// ─── TRC20 Purchase Panel ─────────────────────────────────────────────────────
function TRC20Panel({ usdtAmount, lang, connectedAddress, onConnectWallet }: { usdtAmount: number; lang: Lang; connectedAddress?: string; onConnectWallet?: () => void }) {
const { t } = useTranslation(lang);
const tokenAmount = usdtAmount / PRESALE_CONFIG.tokenPrice;
const [copied, setCopied] = useState(false);
const [evmAddress, setEvmAddress] = useState(connectedAddress || "");
const [evmAddrError, setEvmAddrError] = useState("");
const [submitted, setSubmitted] = useState(false);
// TronLink detection state
const [tronAddress, setTronAddress] = useState<string | null>(null);
const [isTronConnecting, setIsTronConnecting] = useState(false);
const hasTronLink = typeof window !== "undefined" && (!!(window as unknown as Record<string, unknown>).tronWeb || !!(window as unknown as Record<string, unknown>).tronLink);
// Auto-detect TronLink on mount
useEffect(() => {
const detectTron = async () => {
// Wait briefly for TronLink to inject
await new Promise(resolve => setTimeout(resolve, 500));
const tronWeb = (window as unknown as Record<string, unknown>).tronWeb as { defaultAddress?: { base58?: string }; ready?: boolean } | undefined;
if (tronWeb && tronWeb.ready && tronWeb.defaultAddress?.base58) {
setTronAddress(tronWeb.defaultAddress.base58);
}
};
detectTron();
}, []);
// Connect TronLink wallet
const handleConnectTronLink = async () => {
setIsTronConnecting(true);
try {
const tronLink = (window as unknown as Record<string, unknown>).tronLink as { request?: (args: { method: string }) => Promise<{ code: number }> } | undefined;
const tronWeb = (window as unknown as Record<string, unknown>).tronWeb as { defaultAddress?: { base58?: string }; ready?: boolean } | undefined;
if (tronLink?.request) {
const result = await tronLink.request({ method: 'tron_requestAccounts' });
if (result?.code === 200 && tronWeb?.defaultAddress?.base58) {
setTronAddress(tronWeb.defaultAddress.base58);
toast.success(lang === "zh" ? "TronLink已连接" : "TronLink connected!");
}
} else if (tronWeb?.ready && tronWeb.defaultAddress?.base58) {
setTronAddress(tronWeb.defaultAddress.base58);
toast.success(lang === "zh" ? "TronLink已检测到" : "TronLink detected!");
} else {
toast.error(lang === "zh" ? "请安装TronLink钱包扩展" : "Please install TronLink wallet extension");
}
} catch {
toast.error(lang === "zh" ? "连接TronLink失败" : "Failed to connect TronLink");
} finally {
setIsTronConnecting(false);
}
};
// Auto-fill EVM address whenever wallet connects or address changes (unless user already submitted)
useEffect(() => {
if (connectedAddress && !submitted) {
setEvmAddress(connectedAddress);
}
}, [connectedAddress, submitted]);
const submitTrc20Mutation = trpc.presale.registerTrc20Intent.useMutation({
onSuccess: () => {
setSubmitted(true);
toast.success(lang === "zh" ? "XIC接收地址已保存" : "XIC receiving address saved!");
},
onError: (err: { message: string }) => {
toast.error(err.message);
},
});
const copyAddress = () => {
navigator.clipboard.writeText(CONTRACTS.TRON.receivingWallet);
setCopied(true);
toast.success(lang === "zh" ? "地址已复制到剪贴板!" : "Address copied to clipboard!");
setTimeout(() => setCopied(false), 2000);
};
const validateEvmAddress = (addr: string) => {
if (!addr) return lang === "zh" ? "请输入您的XIC接收地址" : "Please enter your XIC receiving address";
if (!/^0x[0-9a-fA-F]{40}$/.test(addr)) return lang === "zh" ? "无效的XIC接收地址格式应以0x开头入42位" : "Invalid XIC receiving address format (must start with 0x, 42 chars)";
return "";
};
const handleEvmSubmit = () => {
const err = validateEvmAddress(evmAddress);
if (err) { setEvmAddrError(err); return; }
setEvmAddrError("");
submitTrc20Mutation.mutate({ evmAddress });
};
return (
<div className="space-y-4">
{/* EVM Address Input — Required for token distribution */}
<div className="rounded-xl p-4 space-y-3" style={{ background: "rgba(240,180,41,0.06)", border: "1px solid rgba(240,180,41,0.25)" }}>
<div className="flex items-center gap-2">
<span className="text-amber-400 text-sm"></span>
<p className="text-sm font-semibold text-amber-300">
{lang === "zh" ? "必填您的XIC接收地址BSC/ETH钉包地址" : "Required: Your XIC Receiving Address (BSC/ETH wallet address)"}
</p>
</div>
<p className="text-xs text-white/50">
{lang === "zh"
? "XIC代币将发放到您的BSC/ETH钉包地址0x开头。请确保填写正确的地址否则无法收到代币。"
: "XIC tokens will be sent to your BSC/ETH wallet address (starts with 0x). Please make sure to enter the correct address."}
</p>
<div className="space-y-2">
{/* WalletSelector — shown when address not yet filled */}
{!evmAddress && !submitted && (
<WalletSelector
lang={lang}
connectedAddress={connectedAddress}
onAddressDetected={(addr) => {
setEvmAddress(addr);
setEvmAddrError("");
toast.success(lang === "zh" ? "XIC接收地址已自动填充" : "XIC receiving address auto-filled!");
if (onConnectWallet) onConnectWallet();
}}
/>
)}
<input
type="text"
value={evmAddress}
onChange={e => { setEvmAddress(e.target.value); setEvmAddrError(""); setSubmitted(false); }}
placeholder={lang === "zh" ? "0x... (您的XIC接收地址)" : "0x... (your XIC receiving address)"}
className="w-full px-4 py-3 rounded-xl text-sm font-mono"
style={{
background: "rgba(255,255,255,0.05)",
border: evmAddrError ? "1px solid rgba(255,82,82,0.5)" : submitted ? "1px solid rgba(0,230,118,0.4)" : "1px solid rgba(255,255,255,0.12)",
color: "white",
outline: "none",
}}
/>
{evmAddrError && <p className="text-xs text-red-400">{evmAddrError}</p>}
{submitted && <p className="text-xs text-green-400"> {lang === "zh" ? "XIC接收地址已保存" : "XIC receiving address saved"}</p>}
<button
onClick={handleEvmSubmit}
disabled={submitTrc20Mutation.isPending || submitted || !evmAddress}
className="w-full py-2 rounded-lg text-sm font-semibold transition-all"
style={{
background: submitted ? "rgba(0,230,118,0.15)" : "rgba(240,180,41,0.15)",
border: submitted ? "1px solid rgba(0,230,118,0.3)" : "1px solid rgba(240,180,41,0.3)",
color: submitted ? "#00e676" : "#f0b429",
opacity: !evmAddress ? 0.5 : 1,
}}
>
{submitTrc20Mutation.isPending ? (lang === "zh" ? "保存中..." : "Saving...") : submitted ? (lang === "zh" ? "✓ 已保存" : "✓ Saved") : (lang === "zh" ? "保存XIC接收地址" : "Save XIC Receiving Address")}
</button>
</div>
</div>
{/* TronLink Wallet Detection */}
<div className="rounded-xl p-4 space-y-3" style={{ background: "rgba(255,0,19,0.06)", border: "1px solid rgba(255,0,19,0.25)" }}>
<div className="flex items-center gap-2">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="12" fill="#FF0013"/>
<path d="M12 5L19 9.5V14.5L12 19L5 14.5V9.5L12 5Z" fill="white" opacity="0.9"/>
<path d="M12 8L16 10.5V13.5L12 16L8 13.5V10.5L12 8Z" fill="#FF0013"/>
</svg>
<p className="text-sm font-semibold" style={{ color: "#ff6b6b" }}>
{lang === "zh" ? "TronLink 钱包(可选)" : "TronLink Wallet (Optional)"}
</p>
</div>
{tronAddress ? (
<div className="space-y-2">
<p className="text-xs text-white/50">
{lang === "zh" ? "已连接 TronLink 地址:" : "Connected TronLink address:"}
</p>
<div
className="p-3 rounded-lg text-xs font-mono break-all"
style={{ background: "rgba(0,230,118,0.08)", border: "1px solid rgba(0,230,118,0.3)", color: "#00e676" }}
>
{tronAddress}
</div>
<p className="text-xs text-white/40">
{lang === "zh"
? "您的 TronLink 已连接。请在上方填写 XIC 接收地址,然后向下方地址发送 USDT。"
: "TronLink connected. Please fill your XIC receiving address above, then send USDT to the address below."}
</p>
</div>
) : (
<div className="space-y-2">
<p className="text-xs text-white/50">
{lang === "zh"
? "如果您使用 TronLink 钱包,可以连接后自动验证您的 TRON 地址。"
: "If you use TronLink wallet, connect to auto-verify your TRON address."}
</p>
{hasTronLink ? (
<button
onClick={handleConnectTronLink}
disabled={isTronConnecting}
className="w-full py-2.5 rounded-xl text-sm font-semibold flex items-center justify-center gap-2 transition-all hover:opacity-90"
style={{
background: "rgba(255,0,19,0.15)",
border: "1px solid rgba(255,0,19,0.4)",
color: "#ff6b6b",
}}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2"/>
<path d="M12 6v6l4 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
</svg>
{isTronConnecting
? (lang === "zh" ? "连接中..." : "Connecting...")
: (lang === "zh" ? "连接 TronLink 自动验证" : "Connect TronLink to verify")}
</button>
) : (
<a
href="https://www.tronlink.org/"
target="_blank"
rel="noopener noreferrer"
className="w-full py-2.5 rounded-xl text-sm font-semibold flex items-center justify-center gap-2 transition-all hover:opacity-90 block text-center"
style={{
background: "rgba(255,0,19,0.08)",
border: "1px solid rgba(255,0,19,0.25)",
color: "rgba(255,107,107,0.7)",
}}
>
{lang === "zh" ? "安装 TronLink 钱包 →" : "Install TronLink Wallet →"}
</a>
)}
</div>
)}
</div>
<div className="nac-card-blue rounded-xl p-4 space-y-3">
<p className="text-sm font-medium text-white/80">{t("trc20_send_to")}</p>
<div
className="trc20-address p-3 rounded-lg cursor-pointer hover:bg-white/5 transition-colors"
style={{ background: "rgba(0,212,255,0.05)", border: "1px solid rgba(0,212,255,0.2)" }}
onClick={copyAddress}
>
{CONTRACTS.TRON.receivingWallet}
</div>
<button
onClick={copyAddress}
className="w-full py-2 rounded-lg text-sm font-semibold transition-all"
style={{ background: copied ? "rgba(0,230,118,0.2)" : "rgba(0,212,255,0.15)", border: "1px solid rgba(0,212,255,0.4)", color: copied ? "#00e676" : "#00d4ff" }}
>
{copied ? t("trc20_copied") : t("trc20_copy")}
</button>
</div>
<div className="space-y-2">
<StepBadge num={1} text={
lang === "zh"
? `在上方填写您的XIC接收地址并保存`
: `Enter and save your XIC receiving address above`
} />
<StepBadge num={2} text={
lang === "zh"
? `发送 ${usdtAmount > 0 ? usdtAmount.toFixed(2) + " USDT" : "任意数量 USDT"}TRC20到上方地址`
: `${t("trc20_step1")} ${usdtAmount > 0 ? usdtAmount.toFixed(2) + " USDT" : t("trc20_step1_any")} (TRC20) ${t("trc20_step1b")}`
} />
<StepBadge num={3} text={`${t("trc20_step2")} ${PRESALE_CONFIG.trc20Memo} ${t("trc20_step2b")}`} />
<StepBadge num={4} text={
lang === "zh"
? (usdtAmount > 0 ? `${t("trc20_step3")} ${formatNumber(tokenAmount)} ${t("trc20_step3b")}` : t("trc20_step3_any"))
: (usdtAmount > 0 ? `You will receive ${formatNumber(tokenAmount)} XIC tokens after confirmation (1-24h)` : t("trc20_step3_any"))
} />
<StepBadge num={5} text={t("trc20_step4")} />
</div>
<div className="rounded-lg p-3 text-xs text-amber-300/80" style={{ background: "rgba(240,180,41,0.08)", border: "1px solid rgba(240,180,41,0.2)" }}>
{t("trc20_warning")}
</div>
</div>
);
}
// ─── EVM Purchase Panel ─────────────────────────────────────────────────────
function EVMPurchasePanel({ network, lang, wallet }: { network: "BSC" | "ETH"; lang: Lang; wallet: WalletHookReturn }) {
const { t } = useTranslation(lang);
const { purchaseState, buyWithUSDT, reset, calcTokens, getUsdtBalance } = usePresale(wallet, network);
const [usdtInput, setUsdtInput] = useState("100");
const [usdtBalance, setUsdtBalance] = useState<number | null>(null);
const targetChainId = CONTRACTS[network].chainId;
const isWrongNetwork = wallet.isConnected && wallet.chainId !== targetChainId;
const fetchBalance = useCallback(async () => {
const bal = await getUsdtBalance();
setUsdtBalance(bal);
}, [getUsdtBalance]);
useEffect(() => {
if (wallet.isConnected) fetchBalance();
}, [wallet.isConnected, fetchBalance]);
const usdtAmount = parseFloat(usdtInput) || 0;
const tokenAmount = calcTokens(usdtAmount);
const isValidAmount = usdtAmount > 0 && usdtAmount <= PRESALE_CONFIG.maxPurchaseUSDT;
const handleBuy = async () => {
if (!isValidAmount) {
toast.error(lang === "zh"
? `请输入有效金额(最大 $${PRESALE_CONFIG.maxPurchaseUSDT.toLocaleString()} USDT`
: `Please enter a valid amount (max $${PRESALE_CONFIG.maxPurchaseUSDT.toLocaleString()} USDT)`);
return;
}
await buyWithUSDT(usdtAmount);
};
useEffect(() => {
if (purchaseState.step === "success") {
toast.success(lang === "zh"
? `购买成功!获得 ${formatNumber(purchaseState.tokenAmount)} 枚 XIC 代币!`
: `Successfully purchased ${formatNumber(purchaseState.tokenAmount)} XIC tokens!`);
} else if (purchaseState.step === "error" && purchaseState.error) {
toast.error(purchaseState.error.slice(0, 120));
}
}, [purchaseState.step, purchaseState.error, purchaseState.tokenAmount, lang]);
if (!wallet.isConnected) {
return (
<div className="space-y-4">
<p className="text-sm text-white/60 text-center">{t("buy_connect_msg")}</p>
<WalletSelector
lang={lang}
connectedAddress={wallet.address ?? undefined}
onAddressDetected={async (addr) => {
// Use forceConnect to directly sync wallet state with the detected address
// (avoids triggering another eth_requestAccounts popup)
await wallet.forceConnect(addr);
setShowWalletModal(false);
toast.success(lang === "zh" ? `钱包已连接: ${addr.slice(0, 6)}...${addr.slice(-4)}` : `Wallet connected: ${addr.slice(0, 6)}...${addr.slice(-4)}`);
}}
compact
/>
<div className="text-xs text-white/40 text-center">{t("buy_connect_hint")}</div>
</div>
);
}
if (isWrongNetwork) {
return (
<div className="space-y-4">
<div className="rounded-xl p-4 text-center" style={{ background: "rgba(240,180,41,0.08)", border: "1px solid rgba(240,180,41,0.3)" }}>
<div className="text-3xl mb-2"></div>
<p className="text-amber-300 font-semibold mb-1">{t("buy_wrong_network")}</p>
<p className="text-white/60 text-sm mb-4">{t("buy_wrong_msg")} {CONTRACTS[network].chainName}</p>
<button
onClick={() => wallet.switchNetwork(targetChainId)}
className="btn-primary-nac px-6 py-2 rounded-lg text-sm font-bold"
>
{t("buy_switch")} {network === "BSC" ? "BSC" : "Ethereum"}
</button>
</div>
</div>
);
}
if (purchaseState.step === "success") {
return (
<div className="space-y-4 text-center py-4">
<div className="text-5xl mb-3">🎉</div>
<h3 className="text-xl font-bold text-green-400" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>
{t("buy_success_title")}
</h3>
<p className="text-white/70">
{t("buy_success_msg")} <span className="text-amber-400 font-bold counter-digit">{formatNumber(purchaseState.tokenAmount)}</span> {t("buy_success_tokens")}
</p>
{purchaseState.txHash && (
<a
href={`${CONTRACTS[network].explorerUrl}/tx/${purchaseState.txHash}`}
target="_blank"
rel="noopener noreferrer"
className="text-xs text-blue-400 hover:text-blue-300 underline block"
>
{t("buy_view_explorer")}
</a>
)}
<button onClick={reset} className="btn-primary-nac px-8 py-2 rounded-lg text-sm font-bold">
{t("buy_more")}
</button>
</div>
);
}
const isProcessing = ["approving", "approved", "purchasing"].includes(purchaseState.step);
return (
<div className="space-y-4">
{/* Wallet info */}
<div className="flex items-center justify-between px-3 py-2 rounded-lg" style={{ background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.08)" }}>
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-green-400" style={{ boxShadow: "0 0 6px #00e676" }} />
<span className="text-xs text-white/60 counter-digit">{shortenAddress(wallet.address || "")}</span>
</div>
{usdtBalance !== null && (
<span className="text-xs text-white/50">{t("buy_balance")} <span className="text-white/80 counter-digit">{usdtBalance.toFixed(2)} USDT</span></span>
)}
</div>
{/* USDT Amount Input */}
<div className="space-y-2">
<label className="text-sm text-white/60 font-medium">{t("buy_usdt_amount")}</label>
<div className="relative">
<input
type="number"
value={usdtInput}
onChange={e => setUsdtInput(e.target.value)}
min={0}
max={PRESALE_CONFIG.maxPurchaseUSDT}
placeholder={t("buy_placeholder")}
className="input-nac w-full px-4 py-3 rounded-xl text-lg counter-digit pr-20"
disabled={isProcessing}
/>
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-white/40 text-sm font-semibold">USDT</span>
</div>
<div className="flex gap-2">
{[100, 500, 1000, 5000].map(amt => (
<button
key={amt}
onClick={() => setUsdtInput(amt.toString())}
className="flex-1 py-1 rounded-lg text-xs font-semibold transition-all"
style={{ background: "rgba(240,180,41,0.08)", border: "1px solid rgba(240,180,41,0.2)", color: "rgba(240,180,41,0.8)" }}
disabled={isProcessing}
>
${amt}
</button>
))}
</div>
</div>
{/* Token Amount Preview */}
<div className="rounded-xl p-4" style={{ background: "rgba(240,180,41,0.06)", border: "1px solid rgba(240,180,41,0.2)" }}>
<div className="flex items-center justify-between">
<span className="text-sm text-white/60">{t("buy_you_receive")}</span>
<div className="text-right">
<span className="text-2xl font-bold amber-text-glow counter-digit" style={{ fontFamily: "'Space Grotesk', sans-serif", color: "#f0b429" }}>
{formatNumber(tokenAmount)}
</span>
<span className="text-amber-400 ml-2 font-semibold">XIC</span>
</div>
</div>
<div className="flex items-center justify-between mt-1">
<span className="text-xs text-white/40">{t("buy_price_per")}</span>
<span className="text-xs text-white/60 counter-digit">$0.02 USDT</span>
</div>
</div>
{/* Purchase Steps */}
{isProcessing && (
<div className="space-y-2 py-2">
<div className={`flex items-center gap-2 text-sm ${["approving", "approved", "purchasing", "success"].includes(purchaseState.step) ? "text-amber-400" : "text-white/40"}`}>
<div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${purchaseState.step === "approving" ? "border-amber-400 animate-spin" : "border-amber-400 bg-amber-400"}`}>
{purchaseState.step !== "approving" && <span className="text-black text-xs"></span>}
</div>
{lang === "zh" ? "第一步:授权 USDT" : `Step 1: ${t("buy_step1")}`}
</div>
<div className={`flex items-center gap-2 text-sm ${(["purchasing", "success"] as string[]).includes(purchaseState.step) ? "text-amber-400" : "text-white/40"}`}>
<div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${purchaseState.step === "purchasing" ? "border-amber-400 animate-spin" : (purchaseState.step as string) === "success" ? "border-amber-400 bg-amber-400" : "border-white/20"}`}>
{(purchaseState.step as string) === "success" && <span className="text-black text-xs"></span>}
</div>
{lang === "zh" ? "第二步:确认购买" : `Step 2: ${t("buy_step2")}`}
</div>
</div>
)}
{/* Buy Button */}
<button
onClick={handleBuy}
disabled={isProcessing || !isValidAmount}
className="btn-primary-nac w-full py-4 rounded-xl text-base font-bold"
style={{ fontFamily: "'Space Grotesk', sans-serif" }}
>
{isProcessing
? purchaseState.step === "approving" ? t("buy_approving")
: purchaseState.step === "approved" ? t("buy_approved")
: t("buy_processing")
: `${t("buy_btn")} ${formatNumber(tokenAmount)} XIC`}
</button>
<p className="text-xs text-center text-white/30">
{t("buy_no_min_max")} ${PRESALE_CONFIG.maxPurchaseUSDT.toLocaleString()} USDT
</p>
</div>
);
}
// ─── FAQ Item ─────────────────────────────────────────────────────────────────
function FAQItem({ q, a, index }: { q: string; a: string; index: number }) {
const [open, setOpen] = useState(false);
return (
<div
className="rounded-xl overflow-hidden transition-all"
style={{ background: open ? "rgba(240,180,41,0.06)" : "rgba(255,255,255,0.03)", border: open ? "1px solid rgba(240,180,41,0.25)" : "1px solid rgba(255,255,255,0.06)" }}
>
<button
onClick={() => setOpen(v => !v)}
className="w-full flex items-center justify-between px-5 py-4 text-left transition-colors hover:bg-white/5"
>
<div className="flex items-center gap-3">
<span className="text-xs font-bold counter-digit" style={{ color: "#f0b429", minWidth: "1.5rem" }}>
{String(index + 1).padStart(2, "0")}
</span>
<span className="font-semibold text-white/90 text-sm" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>{q}</span>
</div>
<span
className="text-white/40 text-lg transition-transform duration-300 flex-shrink-0 ml-4"
style={{ transform: open ? "rotate(45deg)" : "rotate(0deg)" }}
>
+
</span>
</button>
{open && (
<div className="px-5 pb-4">
<div className="pl-9">
<p className="text-sm text-white/60 leading-relaxed">{a}</p>
</div>
</div>
)}
</div>
);
}
// ─── Purchase Feed ────────────────────────────────────────────────────────────
function PurchaseFeed({ lang }: { lang: Lang }) {
const { t } = useTranslation(lang);
const feedRef = useRef<HTMLDivElement>(null);
// Fetch real TRC20 purchases from backend
const { data: trc20Records } = trpc.presale.recentPurchases.useQuery(
{ limit: 20 },
{ refetchInterval: 30_000 }
);
// Merge real TRC20 with mock EVM records for display
const [records, setRecords] = useState<Array<{
address: string;
amount: number;
usdt: number;
time: string;
chain: string;
isReal?: boolean;
}>>([
{ address: "0x3a4f...8c2d", amount: 250000, usdt: 5000, time: "2 min ago", chain: "BSC" },
{ address: "0x7b1e...f93a", amount: 50000, usdt: 1000, time: "5 min ago", chain: "ETH" },
{ address: "TRX9k...m4pQ", amount: 125000, usdt: 2500, time: "8 min ago", chain: "TRON" },
{ address: "0xd92c...1a7f", amount: 500000, usdt: 10000, time: "12 min ago", chain: "BSC" },
{ address: "0x5e8b...c3d1", amount: 25000, usdt: 500, time: "15 min ago", chain: "ETH" },
{ address: "TRX2m...k9nL", amount: 75000, usdt: 1500, time: "19 min ago", chain: "TRON" },
]);
// Inject real TRC20 records at the top
useEffect(() => {
if (!trc20Records || trc20Records.length === 0) return;
const realRecords = trc20Records.slice(0, 5).map(r => ({
address: r.fromAddress.slice(0, 6) + "..." + r.fromAddress.slice(-4),
amount: r.xicAmount,
usdt: r.usdtAmount,
time: new Date(r.createdAt).toLocaleTimeString(),
chain: "TRON",
isReal: true,
}));
setRecords(prev => {
const merged = [...realRecords, ...prev.filter(p => !p.isReal)];
return merged.slice(0, 10);
});
}, [trc20Records]);
// Simulate new EVM purchases every 18-30 seconds
useEffect(() => {
const names = ["0x2f4a...8e1c", "0x9b3d...7f2a", "TRXab...c5mN", "0x1e7c...4d9b", "0x8a2f...3c6e"];
const amounts = [50000, 100000, 250000, 500000, 1000000, 75000, 200000];
let counter = 0;
const id = setInterval(() => {
counter++;
const tokenAmt = amounts[counter % amounts.length];
const usdtAmt = tokenAmt * 0.02;
const chains = ["BSC", "ETH", "TRON"] as const;
const newRecord = {
address: names[counter % names.length],
amount: tokenAmt,
usdt: usdtAmt,
time: lang === "zh" ? "刚刚" : "just now",
chain: chains[counter % 3],
};
setRecords(prev => [newRecord, ...prev.slice(0, 9)]);
}, 18000 + Math.random() * 12000);
return () => clearInterval(id);
}, [lang]);
const chainColor = (chain: string) => {
if (chain === "BSC") return "#F0B90B";
if (chain === "ETH") return "#627EEA";
return "#FF0013";
};
return (
<div className="nac-card rounded-2xl p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="text-xs font-semibold uppercase tracking-widest text-white/40">{t("stats_live_feed")}</h3>
<div className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full bg-green-400 animate-pulse" />
<span className="text-xs text-green-400">{t("stats_live")}</span>
</div>
</div>
<div ref={feedRef} className="space-y-2 max-h-64 overflow-y-auto" style={{ scrollbarWidth: "none" }}>
{records.map((r, i) => (
<div
key={i}
className="flex items-center justify-between py-2 px-3 rounded-lg transition-all"
style={{
background: i === 0 ? "rgba(240,180,41,0.08)" : "rgba(255,255,255,0.02)",
border: i === 0 ? "1px solid rgba(240,180,41,0.2)" : "1px solid rgba(255,255,255,0.04)",
animation: i === 0 ? "fadeInDown 0.5s ease" : "none",
}}
>
<div className="flex items-center gap-2">
<span
className="text-xs font-bold px-1.5 py-0.5 rounded"
style={{ background: `${chainColor(r.chain)}20`, color: chainColor(r.chain), fontSize: "10px" }}
>
{r.chain}
</span>
<span className="text-xs text-white/50 counter-digit">{r.address}</span>
{r.isReal && <span className="text-xs text-green-400" style={{ fontSize: "9px" }}></span>}
</div>
<div className="text-right">
<div className="text-xs font-bold counter-digit" style={{ color: "#f0b429" }}>
+{formatNumber(r.amount)} XIC
</div>
<div className="text-xs text-white/30">{r.time}</div>
</div>
</div>
))}
</div>
</div>
);
}
// ─── Chat Support Widget ──────────────────────────────────────────────────────
function ChatSupport({ lang }: { lang: Lang }) {
const { t } = useTranslation(lang);
const [open, setOpen] = useState(false);
return (
<div className="fixed bottom-6 right-6 z-50 flex flex-col items-end gap-3">
{open && (
<div
className="rounded-2xl p-5 w-72 shadow-2xl"
style={{ background: "rgba(10,10,15,0.97)", border: "1px solid rgba(240,180,41,0.3)", backdropFilter: "blur(20px)" }}
>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full flex items-center justify-center" style={{ background: "rgba(240,180,41,0.15)", border: "1px solid rgba(240,180,41,0.3)" }}>
<span className="text-sm">💬</span>
</div>
<div>
<div className="text-sm font-semibold text-white" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>{t("support_title")}</div>
<div className="flex items-center gap-1">
<span className="w-1.5 h-1.5 rounded-full bg-green-400" />
<span className="text-xs text-green-400">{t("support_online")}</span>
</div>
</div>
</div>
<button onClick={() => setOpen(false)} className="text-white/40 hover:text-white/80 transition-colors text-lg">×</button>
</div>
<div className="rounded-xl p-3 mb-4" style={{ background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.06)" }}>
<p className="text-sm text-white/70 leading-relaxed">{t("support_msg")}</p>
</div>
<div className="space-y-2">
<a
href="https://t.me/newassetchain"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 w-full px-4 py-3 rounded-xl text-sm font-semibold transition-all hover:opacity-90"
style={{ background: "rgba(0,136,204,0.2)", border: "1px solid rgba(0,136,204,0.4)", color: "#29b6f6" }}
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.562 8.248l-2.04 9.61c-.15.668-.543.832-1.1.517l-3.04-2.24-1.467 1.41c-.162.162-.298.298-.61.298l.217-3.08 5.6-5.06c.243-.217-.053-.337-.376-.12L7.15 14.06l-2.97-.928c-.645-.2-.658-.645.135-.954l11.6-4.47c.537-.195 1.007.13.647.54z"/>
</svg>
{t("support_telegram")}
</a>
<a
href="mailto:support@newassetchain.io"
className="flex items-center gap-3 w-full px-4 py-3 rounded-xl text-sm font-semibold transition-all hover:opacity-90"
style={{ background: "rgba(240,180,41,0.1)", border: "1px solid rgba(240,180,41,0.25)", color: "#f0b429" }}
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
<polyline points="22,6 12,13 2,6"/>
</svg>
{t("support_email")}
</a>
</div>
<p className="text-xs text-white/30 text-center mt-3">{t("support_response")}</p>
</div>
)}
<button
onClick={() => setOpen(v => !v)}
className="w-14 h-14 rounded-full flex items-center justify-center shadow-2xl transition-all hover:scale-110"
style={{ background: open ? "rgba(240,180,41,0.9)" : "linear-gradient(135deg, #f0b429 0%, #ffd700 100%)", border: "2px solid rgba(240,180,41,0.5)", boxShadow: "0 0 24px rgba(240,180,41,0.4)" }}
title="Chat Support"
>
{open ? (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#0a0a0f" strokeWidth="2.5">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
) : (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#0a0a0f" strokeWidth="2">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
)}
</button>
</div>
);
}
// ─── Navbar Wallet Button ─────────────────────────────────────────────────────
type WalletHookReturn = ReturnType<typeof useWallet>;
function NavWalletButton({ lang, wallet }: { lang: Lang; wallet: WalletHookReturn }) {
const { t } = useTranslation(lang);
const [showMenu, setShowMenu] = useState(false);
const [showWalletModal, setShowWalletModal] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClick = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) setShowMenu(false);
};
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, []);
// Detect mobile browser
const isMobile = typeof window !== "undefined" && /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// Handle connect button click — show wallet selector modal
const handleConnectClick = async () => {
// On mobile browsers, skip direct connect attempt and show modal immediately
// (mobile browsers don't support wallet extensions)
if (isMobile) {
setShowWalletModal(true);
return;
}
// On desktop: first try direct connect (works if wallet is already set up and locked)
const result = await wallet.connect();
if (!result.success && result.error) {
// If direct connect failed, show the wallet selector modal for guided setup
setShowWalletModal(true);
toast.error(result.error, { duration: 6000 });
}
};
if (!wallet.isConnected) {
return (
<>
<button
onClick={handleConnectClick}
disabled={wallet.isConnecting}
className="flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-bold transition-all hover:opacity-90"
style={{ background: "linear-gradient(135deg, rgba(240,180,41,0.9) 0%, rgba(255,215,0,0.9) 100%)", color: "#0a0a0f", fontFamily: "'Space Grotesk', sans-serif", boxShadow: "0 0 16px rgba(240,180,41,0.3)" }}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/>
<path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/>
<path d="M18 12a2 2 0 0 0 0 4h4v-4z"/>
</svg>
{wallet.isConnecting ? t("nav_connecting") : t("nav_connect")}
</button>
{/* Wallet Connection Modal */}
{showWalletModal && (
<div
className="fixed inset-0 z-[100] flex items-end sm:items-center justify-center sm:p-4"
style={{ background: "rgba(0,0,0,0.85)", backdropFilter: "blur(8px)" }}
onClick={(e) => { if (e.target === e.currentTarget) setShowWalletModal(false); }}
>
<div
className="w-full sm:max-w-md rounded-t-2xl sm:rounded-2xl p-6 relative max-h-[85vh] overflow-y-auto"
style={{ background: "rgba(10,10,20,0.98)", border: "1px solid rgba(240,180,41,0.3)", boxShadow: "0 0 40px rgba(240,180,41,0.15)" }}
>
{/* Close button */}
<button
onClick={() => setShowWalletModal(false)}
className="absolute top-4 right-4 text-white/40 hover:text-white/80 transition-colors"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
<h3 className="text-lg font-bold text-white mb-1" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>
{lang === "zh" ? "连接钱包" : "Connect Wallet"}
</h3>
<p className="text-xs text-white/40 mb-4">
{lang === "zh"
? "选择您的钱包进行连接,或手动输入地址"
: "Select your wallet to connect, or enter address manually"}
</p>
{/* MetaMask initialization guide */}
<div
className="rounded-xl p-3 mb-4"
style={{ background: "rgba(240,180,41,0.06)", border: "1px solid rgba(240,180,41,0.2)" }}
>
<p className="text-xs text-amber-300/80 leading-relaxed">
{lang === "zh"
? "💡 首次使用 MetaMask请先打开 MetaMask 扩展完成初始化(创建或导入钱包),完成后点击下方「刷新」按钮重新检测。"
: "💡 First time using MetaMask? Open the MetaMask extension and complete setup (create or import a wallet), then click Refresh below to re-detect."}
</p>
</div>
<WalletSelector
lang={lang}
connectedAddress={wallet.address ?? undefined}
onAddressDetected={async (addr) => {
// After address detected from WalletSelector, sync wallet state
const result = await wallet.connect();
if (result.success) {
setShowWalletModal(false);
toast.success(lang === "zh" ? `钱包已连接: ${addr.slice(0, 6)}...${addr.slice(-4)}` : `Wallet connected: ${addr.slice(0, 6)}...${addr.slice(-4)}`);
} else {
// Even if connect() failed, we have the address — close modal
setShowWalletModal(false);
toast.success(lang === "zh" ? `地址已确认: ${addr.slice(0, 6)}...${addr.slice(-4)}` : `Address confirmed: ${addr.slice(0, 6)}...${addr.slice(-4)}`);
}
}}
/>
</div>
</div>
)}
</>
);
}
return (
<div className="relative" ref={menuRef}>
<button
onClick={() => setShowMenu(v => !v)}
className="flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-semibold transition-all hover:bg-white/5"
style={{ background: "rgba(0,230,118,0.1)", border: "1px solid rgba(0,230,118,0.3)", color: "#00e676" }}
>
<span className="w-2 h-2 rounded-full bg-green-400" style={{ boxShadow: "0 0 6px #00e676" }} />
<span className="counter-digit">{wallet.shortAddress}</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<polyline points="6 9 12 15 18 9"/>
</svg>
</button>
{showMenu && (
<div
className="absolute right-0 top-full mt-2 w-48 rounded-xl py-2 z-50"
style={{ background: "rgba(10,10,15,0.97)", border: "1px solid rgba(255,255,255,0.1)", backdropFilter: "blur(20px)" }}
>
<div className="px-4 py-2 border-b" style={{ borderColor: "rgba(255,255,255,0.06)" }}>
<p className="text-xs text-white/40">{t("nav_connected")}</p>
<p className="text-sm font-semibold text-white/90 counter-digit">{wallet.shortAddress}</p>
</div>
<button
onClick={() => { wallet.disconnect(); setShowMenu(false); }}
className="w-full text-left px-4 py-2 text-sm text-red-400 hover:bg-red-400/10 transition-colors"
>
{t("nav_disconnect")}
</button>
</div>
)}
</div>
);
}
// ─── Language Toggle ──────────────────────────────────────────────────────────
function LangToggle({ lang, setLang }: { lang: Lang; setLang: (l: Lang) => void }) {
return (
<div
className="flex items-center rounded-lg overflow-hidden text-xs font-semibold"
style={{ border: "1px solid rgba(255,255,255,0.12)", background: "rgba(255,255,255,0.04)" }}
>
<button
onClick={() => setLang("en")}
className="px-3 py-1.5 transition-all"
style={{
background: lang === "en" ? "rgba(240,180,41,0.2)" : "transparent",
color: lang === "en" ? "#f0b429" : "rgba(255,255,255,0.4)",
}}
>
EN
</button>
<div style={{ width: "1px", height: "16px", background: "rgba(255,255,255,0.1)" }} />
<button
onClick={() => setLang("zh")}
className="px-3 py-1.5 transition-all"
style={{
background: lang === "zh" ? "rgba(240,180,41,0.2)" : "transparent",
color: lang === "zh" ? "#f0b429" : "rgba(255,255,255,0.4)",
}}
>
</button>
</div>
);
}
// ─── Main Page ────────────────────────────────────────────────────────────────
export default function Home() {
const [lang, setLang] = useState<Lang>(() => {
// Auto-detect browser language
const browserLang = navigator.language.toLowerCase();
return browserLang.startsWith("zh") ? "zh" : "en";
});
const { t, faq } = useTranslation(lang);
const [activeNetwork, setActiveNetwork] = useState<NetworkTab>("BSC");
const [trcUsdtAmount, setTrcUsdtAmount] = useState("100");
// useMemo stabilizes the Date reference to prevent infinite re-renders in useCountdown
const presaleEndDate = useMemo(() => new Date("2026-06-30T23:59:59Z"), []);
const countdown = useCountdown(presaleEndDate);
// ── Real on-chain stats ──
const { data: onChainStats, isLoading: statsLoading } = trpc.presale.stats.useQuery(undefined, {
refetchInterval: 60_000, // Refresh every 60 seconds
staleTime: 30_000,
});
const stats = onChainStats || FALLBACK_STATS;
const progressPct = stats.progressPct || 0;
// Presale active/paused status from backend config
const isPresalePaused = (onChainStats as any)?.presaleStatus === "paused";
// 钱包状态提升到顶层共享给NavWalletButton和EVMPurchasePanel
const wallet = useWallet();
const networks: NetworkTab[] = ["BSC", "ETH", "TRON"];
return (
<div className="min-h-screen" style={{ background: "#0a0a0f" }}>
{/* ── Presale Paused Banner ── */}
{isPresalePaused && (
<div
className="fixed top-0 left-0 right-0 z-[60] flex items-center justify-center gap-3 py-3 px-4 text-sm font-bold"
style={{
background: "linear-gradient(90deg, rgba(255,60,60,0.95) 0%, rgba(200,0,0,0.95) 100%)",
color: "white",
letterSpacing: "0.05em",
}}
>
<span className="text-lg"></span>
<span>{lang === "zh" ? "预售活动已暂停,暂时无法购买。请关注官方渠道获取最新公告。" : "Presale is currently paused. Please follow our official channels for updates."}</span>
<span className="text-lg"></span>
</div>
)}
{/* ── Navigation ── */}
<nav className="fixed top-0 left-0 right-0 z-50 flex items-center justify-between px-6 py-4" style={{ background: "rgba(10,10,15,0.9)", borderBottom: "1px solid rgba(240,180,41,0.1)", backdropFilter: "blur(12px)" }}>
<div className="flex items-center gap-3">
<img src={TOKEN_ICON} alt="XIC" className="w-8 h-8 rounded-full" />
<div>
<span className="font-bold text-white" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>New AssetChain</span>
<span className="ml-2 text-xs px-2 py-0.5 rounded-full font-semibold" style={{ background: "rgba(240,180,41,0.15)", color: "#f0b429", border: "1px solid rgba(240,180,41,0.3)" }}>PRESALE</span>
</div>
</div>
<div className="flex items-center gap-3">
<a href="https://newassetchain.io" target="_blank" rel="noopener noreferrer" className="text-sm text-white/60 hover:text-white/90 transition-colors hidden md:block">{t("nav_website")}</a>
<a href="https://lens.newassetchain.io" target="_blank" rel="noopener noreferrer" className="text-sm text-white/60 hover:text-white/90 transition-colors hidden md:block">{t("nav_explorer")}</a>
<a href="https://docs.newassetchain.io" target="_blank" rel="noopener noreferrer" className="text-sm text-white/60 hover:text-white/90 transition-colors hidden md:block">{t("nav_docs")}</a>
<Link href="/tutorial">
<span className="text-sm font-semibold cursor-pointer transition-colors hidden md:block" style={{ color: "#00d4ff" }}>
{lang === "zh" ? "📖 购买教程" : "📖 Tutorial"}
</span>
</Link>
<LangToggle lang={lang} setLang={setLang} />
<NavWalletButton lang={lang} wallet={wallet} />
</div>
</nav>
{/* ── Hero Section ── */}
<section className="relative pt-20 pb-8 overflow-hidden hex-bg" style={{ minHeight: "340px" }}>
<div className="absolute inset-0 bg-cover bg-center opacity-30" style={{ backgroundImage: `url(${HERO_BG})` }} />
<div className="absolute inset-0" style={{ background: "linear-gradient(to bottom, rgba(10,10,15,0.4) 0%, rgba(10,10,15,0.95) 100%)" }} />
<div className="relative container mx-auto px-4 pt-12 pb-4 text-center">
<div className="inline-flex items-center gap-2 mb-4 px-4 py-2 rounded-full text-sm font-semibold" style={{ background: "rgba(0,230,118,0.1)", border: "1px solid rgba(0,230,118,0.3)", color: "#00e676" }}>
<span className="w-2 h-2 rounded-full bg-green-400 animate-pulse" />
{t("hero_badge")}
</div>
<h1 className="text-4xl md:text-6xl font-bold mb-4 leading-tight" style={{ fontFamily: "'Space Grotesk', sans-serif", background: "linear-gradient(135deg, #f0b429 0%, #ffd700 50%, #f0b429 100%)", WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent" }}>
{t("hero_title")}
</h1>
<p className="text-lg text-white/70 max-w-2xl mx-auto mb-6">{t("hero_subtitle")}</p>
<div className="flex flex-wrap justify-center gap-4 text-sm text-white/50">
<span className="flex items-center gap-1"><span className="text-amber-400"></span> {t("hero_price")}</span>
<span className="flex items-center gap-1"><span className="text-amber-400"></span> {t("hero_supply")}</span>
<span className="flex items-center gap-1"><span className="text-amber-400"></span> {t("hero_networks")}</span>
<span className="flex items-center gap-1"><span className="text-amber-400"></span> {t("hero_no_min")}</span>
</div>
</div>
</section>
{/* ── Main Content ── */}
<section className="container mx-auto px-4 py-8">
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6 max-w-6xl mx-auto">
{/* ── Left Panel: Stats & Info ── */}
<div className="lg:col-span-2 space-y-5 fade-in-up">
{/* Countdown */}
<div className="nac-card rounded-2xl p-5 scan-line">
<h3 className="text-xs font-semibold uppercase tracking-widest text-white/40 mb-4">{t("stats_ends_in")}</h3>
<div className="grid grid-cols-4 gap-2 text-center">
{[
{ label: t("stats_days"), value: countdown.days },
{ label: t("stats_hours"), value: countdown.hours },
{ label: t("stats_mins"), value: countdown.minutes },
{ label: t("stats_secs"), value: countdown.seconds },
].map(({ label, value }) => (
<div key={label} className="rounded-xl py-3" style={{ background: "rgba(240,180,41,0.08)", border: "1px solid rgba(240,180,41,0.15)" }}>
<div className="text-2xl font-bold counter-digit amber-text-glow" style={{ color: "#f0b429", fontFamily: "'Space Grotesk', sans-serif" }}>
{String(value).padStart(2, "0")}
</div>
<div className="text-xs text-white/40 mt-1">{label}</div>
</div>
))}
</div>
</div>
{/* Progress — Real On-Chain Data */}
<div className="nac-card rounded-2xl p-5">
<div className="flex items-center justify-between mb-3">
<h3 className="text-xs font-semibold uppercase tracking-widest text-white/40">{t("stats_raised")}</h3>
<div className="flex items-center gap-2">
{statsLoading ? (
<span className="text-xs text-white/30">{t("loading_stats")}</span>
) : (
<>
<span className="w-1.5 h-1.5 rounded-full bg-green-400 animate-pulse" />
<span className="text-xs text-green-400">{t("stats_live_data")}</span>
</>
)}
<span className="text-xs font-bold counter-digit" style={{ color: "#f0b429" }}>{progressPct.toFixed(1)}%</span>
</div>
</div>
<div className="h-3 rounded-full mb-3 overflow-hidden" style={{ background: "rgba(255,255,255,0.06)" }}>
<div className="h-full rounded-full progress-bar-animated" style={{ width: `${progressPct}%` }} />
</div>
<div className="flex justify-between text-sm">
<div>
<div className="text-xl font-bold counter-digit amber-text-glow" style={{ color: "#f0b429", fontFamily: "'Space Grotesk', sans-serif" }}>
<AnimatedCounter value={stats.totalUsdtRaised} prefix="$" />
</div>
<div className="text-xs text-white/40">{t("stats_raised_label")}</div>
</div>
<div className="text-right">
<div className="text-xl font-bold counter-digit text-white/60" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>
${formatNumber(stats.hardCap)}
</div>
<div className="text-xs text-white/40">{t("stats_hard_cap")}</div>
</div>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-2 gap-3">
{[
{ label: t("stats_tokens_sold"), value: formatNumber(stats.totalTokensSold), unit: "XIC" },
{ label: t("stats_token_price"), value: "$0.02", unit: "USDT" },
{ label: t("stats_listing"), value: "$0.10", unit: t("stats_target") },
{ label: t("hero_networks"), value: "3", unit: "BSC · ETH · TRC20" },
].map(({ label, value, unit }) => (
<div key={label} className="nac-card rounded-xl p-4">
<div className="text-lg font-bold counter-digit" style={{ fontFamily: "'Space Grotesk', sans-serif", color: "#f0b429" }}>{value}</div>
<div className="text-xs text-white/40 mt-0.5">{label}</div>
<div className="text-xs text-white/25">{unit}</div>
</div>
))}
</div>
{/* Token Info */}
<div className="nac-card rounded-2xl p-5 space-y-3">
<h3 className="text-xs font-semibold uppercase tracking-widest text-white/40">{t("token_details")}</h3>
{[
{ label: t("token_name"), value: "New AssetChain Token" },
{ label: t("token_symbol"), value: "XIC" },
{ label: t("token_network"), value: "BSC (BEP-20)" },
{ label: t("token_decimals"), value: "18" },
{ label: t("token_supply"), value: "100,000,000,000" },
].map(({ label, value }) => (
<div key={label} className="flex items-center justify-between py-1" style={{ borderBottom: "1px solid rgba(255,255,255,0.04)" }}>
<span className="text-xs text-white/40">{label}</span>
<span className="text-xs font-medium text-white/80 counter-digit">{value}</span>
</div>
))}
<a
href={`https://bscscan.com/address/${CONTRACTS.BSC.token}`}
target="_blank"
rel="noopener noreferrer"
className="block text-center text-xs py-2 rounded-lg transition-all hover:bg-white/5"
style={{ color: "#00d4ff", border: "1px solid rgba(0,212,255,0.2)" }}
>
{t("token_view_contract")}
</a>
</div>
{/* Live Purchase Feed */}
<PurchaseFeed lang={lang} />
</div>
{/* ── Right Panel: Purchase ── */}
<div className="lg:col-span-3">
<div className="nac-card rounded-2xl p-6 amber-glow">
{/* Token Icon + Title */}
<div className="flex items-center gap-4 mb-6">
<img src={TOKEN_ICON} alt="XIC" className="w-14 h-14 rounded-full" style={{ border: "2px solid rgba(240,180,41,0.4)" }} />
<div>
<h2 className="text-2xl font-bold" style={{ fontFamily: "'Space Grotesk', sans-serif", color: "#f0b429" }}>{t("buy_title")}</h2>
<p className="text-sm text-white/50">{t("buy_subtitle")} <span className="text-white/80 font-semibold">$0.02 USDT</span> · <span className="text-green-400 font-semibold">{t("buy_no_min")}</span></p>
</div>
</div>
{/* Network Selector */}
<div className="mb-6">
<p className="text-xs font-semibold uppercase tracking-widest text-white/40 mb-3">{t("buy_select_network")}</p>
<div className="grid grid-cols-3 gap-2">
{networks.map(net => (
<button
key={net}
onClick={() => setActiveNetwork(net)}
className={`network-tab rounded-xl py-3 px-2 flex flex-col items-center gap-1.5 ${activeNetwork === net ? "active" : ""}`}
>
<NetworkIcon network={net} />
<span className="text-xs font-semibold">{net === "BSC" ? "BSC" : net === "ETH" ? "Ethereum" : "TRON"}</span>
<span className="text-xs opacity-60">{net === "TRON" ? "TRC20" : "ERC20"} USDT</span>
</button>
))}
</div>
</div>
{/* Purchase Area */}
<div className="relative">
{/* Presale Paused Overlay */}
{isPresalePaused && (
<div
className="absolute inset-0 z-10 flex flex-col items-center justify-center rounded-2xl gap-3"
style={{
background: "rgba(10,10,15,0.88)",
border: "1.5px solid rgba(255,60,60,0.4)",
backdropFilter: "blur(4px)",
}}
>
<div className="text-4xl"></div>
<p className="text-base font-bold" style={{ color: "#ff6060", fontFamily: "'Space Grotesk', sans-serif" }}>
{lang === "zh" ? "预售已暂停" : "Presale Paused"}
</p>
<p className="text-xs text-white/50 text-center px-4">
{lang === "zh" ? "请关注官方 Telegram / Twitter 获取最新公告" : "Follow our official Telegram / Twitter for updates"}
</p>
</div>
)}
{activeNetwork === "BSC" && <EVMPurchasePanel network="BSC" lang={lang} wallet={wallet} />}
{activeNetwork === "ETH" && <EVMPurchasePanel network="ETH" lang={lang} wallet={wallet} />}
{activeNetwork === "TRON" && (
<div className="space-y-4">
<div className="space-y-2">
<label className="text-sm text-white/60 font-medium">{t("buy_usdt_trc20")}</label>
<div className="relative">
<input
type="number"
value={trcUsdtAmount}
onChange={e => setTrcUsdtAmount(e.target.value)}
placeholder={t("buy_placeholder")}
className="input-nac w-full px-4 py-3 rounded-xl text-lg counter-digit pr-20"
/>
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-white/40 text-sm font-semibold">USDT</span>
</div>
<div className="flex gap-2">
{[100, 500, 1000, 5000].map(amt => (
<button
key={amt}
onClick={() => setTrcUsdtAmount(amt.toString())}
className="flex-1 py-1 rounded-lg text-xs font-semibold transition-all"
style={{ background: "rgba(240,180,41,0.08)", border: "1px solid rgba(240,180,41,0.2)", color: "rgba(240,180,41,0.8)" }}
>
${amt}
</button>
))}
</div>
</div>
<TRC20Panel usdtAmount={parseFloat(trcUsdtAmount) || 0} lang={lang} connectedAddress={wallet.address || undefined} onConnectWallet={wallet.connect} />
</div>
)}
</div>
{/* Presale Contract Links */}
<div className="mt-6 pt-4" style={{ borderTop: "1px solid rgba(255,255,255,0.06)" }}>
<p className="text-xs text-white/30 mb-2">{t("buy_contracts")}</p>
<div className="flex flex-wrap gap-2">
<a href={`https://bscscan.com/address/${CONTRACTS.BSC.presale}`} target="_blank" rel="noopener noreferrer" className="text-xs px-3 py-1 rounded-full transition-all hover:bg-white/5" style={{ color: "#00d4ff", border: "1px solid rgba(0,212,255,0.2)" }}>
{t("buy_bsc_contract")}
</a>
<a href={`https://etherscan.io/address/${CONTRACTS.ETH.presale}`} target="_blank" rel="noopener noreferrer" className="text-xs px-3 py-1 rounded-full transition-all hover:bg-white/5" style={{ color: "#00d4ff", border: "1px solid rgba(0,212,255,0.2)" }}>
{t("buy_eth_contract")}
</a>
</div>
</div>
</div>
{/* Why NAC */}
<div className="mt-5 grid grid-cols-1 md:grid-cols-3 gap-4">
{[
{ icon: "🔗", title: t("why_rwa_title"), desc: t("why_rwa_desc") },
{ icon: "⚡", title: t("why_cbpp_title"), desc: t("why_cbpp_desc") },
{ icon: "🛡️", title: t("why_charter_title"), desc: t("why_charter_desc") },
].map(({ icon, title, desc }) => (
<div key={title} className="nac-card rounded-xl p-4">
<div className="text-2xl mb-2">{icon}</div>
<h4 className="font-semibold text-white/90 text-sm mb-1" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>{title}</h4>
<p className="text-xs text-white/50 leading-relaxed">{desc}</p>
</div>
))}
</div>
</div>
</div>
</section>
{/* ── FAQ Section ── */}
<section className="container mx-auto px-4 py-12 max-w-4xl">
<div className="text-center mb-10">
<h2 className="text-3xl font-bold mb-3" style={{ fontFamily: "'Space Grotesk', sans-serif", color: "#f0b429" }}>
{t("faq_title")}
</h2>
<p className="text-white/50 text-sm max-w-xl mx-auto">{t("faq_subtitle")}</p>
</div>
<div className="space-y-3">
{faq.map((item, i) => (
<FAQItem key={i} q={item.q} a={item.a} index={i} />
))}
</div>
<div className="mt-8 text-center">
<p className="text-white/40 text-sm mb-3">{t("faq_still")}</p>
<a
href="https://t.me/newassetchain"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold transition-all hover:opacity-90"
style={{ background: "rgba(240,180,41,0.1)", border: "1px solid rgba(240,180,41,0.3)", color: "#f0b429" }}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.562 8.248l-2.04 9.61c-.15.668-.543.832-1.1.517l-3.04-2.24-1.467 1.41c-.162.162-.298.298-.61.298l.217-3.08 5.6-5.06c.243-.217-.053-.337-.376-.12L7.15 14.06l-2.97-.928c-.645-.2-.658-.645.135-.954l11.6-4.47c.537-.195 1.007.13.647.54z"/>
</svg>
{t("faq_ask")}
</a>
</div>
</section>
{/* ── Footer ── */}
<footer className="mt-8 py-8 text-center" style={{ borderTop: "1px solid rgba(240,180,41,0.1)" }}>
<div className="flex items-center justify-center gap-2 mb-3">
<img src={TOKEN_ICON} alt="XIC" className="w-6 h-6 rounded-full" />
<span className="font-semibold text-white/70" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>New AssetChain</span>
</div>
<p className="text-xs text-white/30 max-w-md mx-auto">{t("footer_risk")}</p>
<div className="flex justify-center gap-6 mt-4">
{[
{ label: t("footer_website"), href: "https://newassetchain.io" },
{ label: t("footer_explorer"), href: "https://lens.newassetchain.io" },
{ label: t("footer_telegram"), href: "https://t.me/newassetchain" },
{ label: t("footer_twitter"), href: "https://twitter.com/newassetchain" },
].map(({ label, href }) => (
<a key={label} href={href} target="_blank" rel="noopener noreferrer" className="text-xs text-white/40 hover:text-white/70 transition-colors">
{label}
</a>
))}
</div>
</footer>
{/* ── Chat Support Widget ── */}
<ChatSupport lang={lang} />
<style>{`
@keyframes fadeInDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
`}</style>
</div>
);
}