Files
hanzo.ai/app/(marketing)/blockchain/directory/page.tsx
zeekay cb6db9e659 design: every page and component outside the landing and beta paths is at zero lint
Inline style objects became gui props (Box for a div, View or Text rendering the
element for the rest, since Box styles only a div), raw colours and sizes became
the design's tokens, the brand swatches read @hanzo/design's own values, and the
project and creation hues moved to palette.json, which is data. The newsletter's
subscribe button is the standard filled Action. connect-steps.tsx, which nothing
imports, is deleted with the brand constants nobody read.

Pages compared before and after at 1440x900 and 390x844: blockchain, brand,
directory, projects, settings, unsubscribe identical; founder 0.03% of pixels;
buying specialist's title snaps from 28 to the ramp's 26px and the newsletter's
button is now filled.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-26 03:49:42 -07:00

625 lines
26 KiB
TypeScript

'use client'
/**
* /blockchain/directory — every chain we serve, and what answers on it.
*
* The type and the controls are `@hanzo/ui/marketing`; the inks, the
* tile and a card's name come from `components/blockchain/surface`; the rest
* is gui props. A chain's head and a resource link are grounds that answer a
* pointer, stated as the hover itself.
*
* It is NOT a caller of `BlockchainProductLayout` and never could be: measured
* against its two neighbours it scores 42% and 41% on class vocabulary, against
* the 83% the last fold on this site was done at, and the content says the same
* thing louder — this page is a filtered table with an expanding row, and that
* template is a hero, a card deck and a code sample.
*
* The filter buttons are the roles' Action at its two weights, which carries
* the 44px coarse-pointer floor; the search field is @hanzo/composer's `Field`
* in a bordered row, so the mark and the input are siblings.
*/
import React, { useState, type ReactNode } from "react";
import { motion } from "@/components/motion";
import {
Globe,
Search,
Server,
Wallet,
Database,
Radio,
Zap,
Check,
X,
ExternalLink,
ChevronDown,
Layers,
ArrowLeftRight,
} from "lucide-react";
import { phrase } from "@hanzo/i18n";
import { useI18n } from "@hanzo/i18n/react";
import { Box, Text, View, XStack, YStack } from '@hanzo/ui'
import { Field } from '@hanzo/composer'
import { RING } from '@/components/shell/pill'
import { Card, CardTitle, DIM, GROUND, INK, Tile, ink, white } from '@/components/blockchain/surface'
import { Action, Chip, Display, Lede } from '@hanzo/ui/marketing'
type ChainCategory = "l1" | "l2" | "rollup" | "lux" | "cosmos" | "other";
interface Chain {
name: string;
id: string;
category: ChainCategory;
chainId?: number | string;
rpcEndpoint: string;
wsEndpoint?: string;
explorer?: string;
github?: string;
faucet?: string;
docs?: string;
testnet?: boolean;
support: {
rpc: boolean;
archive: boolean;
websockets: boolean;
indexer: boolean;
tokens: boolean;
nft: boolean;
webhooks: boolean;
smartWallets: boolean;
dex: boolean;
prices: boolean;
};
}
const CHAINS: Chain[] = [
// L1s
{
name: "Ethereum",
id: "ethereum",
category: "l1",
chainId: 1,
rpcEndpoint: "https://eth.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://eth.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://etherscan.io",
github: "https://github.com/ethereum",
docs: "https://ethereum.org/developers",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Solana",
id: "solana",
category: "l1",
rpcEndpoint: "https://solana.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://solana.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://solscan.io",
github: "https://github.com/solana-labs",
docs: "https://docs.solana.com",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: false, dex: true, prices: true },
},
{
name: "Bitcoin",
id: "bitcoin",
category: "l1",
rpcEndpoint: "https://btc.hanzo.ai/v1/{API_KEY}",
explorer: "https://mempool.space",
github: "https://github.com/bitcoin/bitcoin",
docs: "https://developer.bitcoin.org",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: false, nft: false, webhooks: true, smartWallets: false, dex: false, prices: true },
},
{
name: "Avalanche C-Chain",
id: "avalanche",
category: "l1",
chainId: 43114,
rpcEndpoint: "https://avax.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://avax.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://snowtrace.io",
github: "https://github.com/ava-labs",
docs: "https://docs.avax.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "BNB Chain",
id: "bnb",
category: "l1",
chainId: 56,
rpcEndpoint: "https://bnb.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://bnb.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://bscscan.com",
github: "https://github.com/bnb-chain",
docs: "https://docs.bnbchain.org",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
// L2s & Rollups
{
name: "Arbitrum One",
id: "arbitrum",
category: "l2",
chainId: 42161,
rpcEndpoint: "https://arb.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://arb.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://arbiscan.io",
github: "https://github.com/OffchainLabs",
docs: "https://docs.arbitrum.io",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Optimism",
id: "optimism",
category: "l2",
chainId: 10,
rpcEndpoint: "https://opt.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://opt.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://optimistic.etherscan.io",
github: "https://github.com/ethereum-optimism",
docs: "https://docs.optimism.io",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Base",
id: "base",
category: "l2",
chainId: 8453,
rpcEndpoint: "https://base.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://base.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://basescan.org",
github: "https://github.com/base-org",
docs: "https://docs.base.org",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Polygon PoS",
id: "polygon",
category: "l2",
chainId: 137,
rpcEndpoint: "https://polygon.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://polygon.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://polygonscan.com",
github: "https://github.com/maticnetwork",
docs: "https://docs.polygon.technology",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "zkSync Era",
id: "zksync",
category: "rollup",
chainId: 324,
rpcEndpoint: "https://zksync.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://zksync.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://explorer.zksync.io",
github: "https://github.com/matter-labs",
docs: "https://docs.zksync.io",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Scroll",
id: "scroll",
category: "rollup",
chainId: 534352,
rpcEndpoint: "https://scroll.hanzo.ai/v1/{API_KEY}",
explorer: "https://scrollscan.com",
github: "https://github.com/scroll-tech",
docs: "https://docs.scroll.io",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Linea",
id: "linea",
category: "rollup",
chainId: 59144,
rpcEndpoint: "https://linea.hanzo.ai/v1/{API_KEY}",
explorer: "https://lineascan.build",
github: "https://github.com/Consensys/linea",
docs: "https://docs.linea.build",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
// Lux Ecosystem
{
name: "Lux P-Chain",
id: "lux-pchain",
category: "lux",
rpcEndpoint: "https://lux-p.hanzo.ai/v1/{API_KEY}",
explorer: "https://explorer.lux.network/p-chain",
github: "https://github.com/luxfi/node",
docs: "https://docs.lux.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: false, nft: false, webhooks: true, smartWallets: false, dex: false, prices: false },
},
{
name: "Lux C-Chain",
id: "lux-cchain",
category: "lux",
chainId: 96369,
rpcEndpoint: "https://lux-c.hanzo.ai/v1/{API_KEY}",
wsEndpoint: "wss://lux-c.hanzo.ai/ws/v1/{API_KEY}",
explorer: "https://explorer.lux.network",
github: "https://github.com/luxfi/node",
docs: "https://docs.lux.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Lux D-Chain (DEX)",
id: "lux-dchain",
category: "lux",
rpcEndpoint: "https://lux-d.hanzo.ai/v1/{API_KEY}",
explorer: "https://explorer.lux.network/d-chain",
github: "https://github.com/luxfi/node",
docs: "https://docs.lux.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: false, webhooks: true, smartWallets: false, dex: true, prices: true },
},
{
name: "Lux A-Chain",
id: "lux-achain",
category: "lux",
rpcEndpoint: "https://lux-a.hanzo.ai/v1/{API_KEY}",
explorer: "https://explorer.lux.network/a-chain",
github: "https://github.com/luxfi/node",
docs: "https://docs.lux.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: false, dex: false, prices: false },
},
{
name: "Lux Z-Chain",
id: "lux-zchain",
category: "lux",
rpcEndpoint: "https://lux-z.hanzo.ai/v1/{API_KEY}",
explorer: "https://explorer.lux.network/z-chain",
github: "https://github.com/luxfi/node",
docs: "https://docs.lux.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: false, nft: false, webhooks: true, smartWallets: false, dex: false, prices: false },
},
{
name: "Hanzo Network",
id: "hanzo-network",
category: "lux",
chainId: 1088,
rpcEndpoint: "https://rpc.hanzo.network/v1/{API_KEY}",
wsEndpoint: "wss://ws.hanzo.network/v1/{API_KEY}",
explorer: "https://explorer.hanzo.network",
github: "https://github.com/hanzoai",
docs: "https://docs.hanzo.ai/docs/network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Zoo Network",
id: "zoo-network",
category: "lux",
chainId: 2001,
rpcEndpoint: "https://rpc.zoo.network/v1/{API_KEY}",
wsEndpoint: "wss://ws.zoo.network/v1/{API_KEY}",
explorer: "https://explorer.zoo.network",
github: "https://github.com/zoo-labs",
docs: "https://docs.zoo.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: true, prices: true },
},
{
name: "Pars Network",
id: "pars-network",
category: "lux",
rpcEndpoint: "https://rpc.pars.network/v1/{API_KEY}",
explorer: "https://explorer.pars.network",
docs: "https://docs.pars.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: true, dex: false, prices: true },
},
// Other Networks
{
name: "Cosmos Hub",
id: "cosmos",
category: "cosmos",
rpcEndpoint: "https://cosmos.hanzo.ai/v1/{API_KEY}",
explorer: "https://mintscan.io/cosmos",
github: "https://github.com/cosmos/gaia",
docs: "https://docs.cosmos.network",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: false, webhooks: true, smartWallets: false, dex: true, prices: true },
},
{
name: "Aptos",
id: "aptos",
category: "other",
rpcEndpoint: "https://aptos.hanzo.ai/v1/{API_KEY}",
explorer: "https://explorer.aptoslabs.com",
github: "https://github.com/aptos-labs",
docs: "https://aptos.dev",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: false, dex: true, prices: true },
},
{
name: "Sui",
id: "sui",
category: "other",
rpcEndpoint: "https://sui.hanzo.ai/v1/{API_KEY}",
explorer: "https://suiexplorer.com",
github: "https://github.com/MystenLabs/sui",
docs: "https://docs.sui.io",
support: { rpc: true, archive: true, websockets: true, indexer: true, tokens: true, nft: true, webhooks: true, smartWallets: false, dex: true, prices: true },
},
];
const CATEGORIES: { id: ChainCategory; name: string; icon: any }[] = [
{ id: "l1", name: phrase("L1s"), icon: Globe },
{ id: "l2", name: phrase("L2s"), icon: Layers },
{ id: "rollup", name: phrase("Rollups"), icon: Layers },
{ id: "lux", name: phrase("Lux Ecosystem"), icon: Zap },
{ id: "cosmos", name: "Cosmos", icon: Globe },
{ id: "other", name: phrase("Other"), icon: Server },
];
const PRODUCTS = [
{ key: "rpc", name: "RPC", icon: Server },
{ key: "archive", name: phrase("Archive"), icon: Database },
{ key: "websockets", name: "WebSockets", icon: Radio },
{ key: "indexer", name: phrase("Indexer"), icon: Search },
{ key: "tokens", name: phrase("Tokens"), icon: Database },
{ key: "nft", name: "NFT", icon: Layers },
{ key: "webhooks", name: phrase("Webhooks"), icon: Radio },
{ key: "smartWallets", name: phrase("Wallets"), icon: Wallet },
{ key: "dex", name: "DEX", icon: ArrowLeftRight },
{ key: "prices", name: phrase("Prices"), icon: Database },
];
/** A support mark: lit where the product answers on this chain, dark where it does not. */
const markInk = (on: boolean) => ({
backgroundColor: on ? white(10) : 'var(--neutral-800)',
color: on ? ink(70) : 'color-mix(in oklab, var(--muted-foreground) 60%, transparent)',
})
/** A label over a half of the expanded panel. */
function Label({ children }: { children: ReactNode }) {
return (
<Text render="h4" display="block" mb={12} fontSize="$2" lineHeight="1.1607rem" fontWeight="500" color={DIM}>
{children}
</Text>
)
}
/** One endpoint, set in mono: its name dim, its address in full ink. */
function Endpoint({ name, children }: { name: string; children: ReactNode }) {
return (
<Text display="block" p={12} rounded="var(--radius-lg)" bg="var(--background)" fontFamily="$mono" fontSize="$1" lineHeight="0.9167rem" color={INK}>
<Text color={DIM}>{name}:</Text> {children}
</Text>
)
}
/** A small line of meta under a chain's name, and the legend's entries. */
const META = { fontSize: '$1', lineHeight: '0.9167rem', color: DIM } as const
const ChainDirectory = () => {
const [searchQuery, setSearchQuery] = useState("");
const [selectedCategory, setSelectedCategory] = useState<ChainCategory | "all">("all");
const [expandedChain, setExpandedChain] = useState<string | null>(null);
const { t } = useI18n();
const filteredChains = CHAINS.filter((chain) => {
const matchesSearch = chain.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
chain.id.toLowerCase().includes(searchQuery.toLowerCase());
const matchesCategory = selectedCategory === "all" || chain.category === selectedCategory;
return matchesSearch && matchesCategory;
});
return (
<Box minH="100vh" $platform-web={{ color: 'var(--foreground)' }}>
{/* Hero */}
<Box render="section" position="relative" overflow="hidden" pt={96} pb={48} px={16} $md={{ px: 32 }}>
<Box position="relative" z="var(--z-raised)" width="100%" maxW={1152} mx="auto">
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
<Box $platform-web={{ textAlign: 'center' }}>
{/* The gap under the badge rides this WRAPPER: the pill is
inline-flex, and an inline-level box's bottom margin does not
grow the line box it sits in. */}
<Box mb={24}>
<Chip px={16} py={8} borderColor={white(30)} bg={white(10)}>
<Globe width={16} height={16} color={INK} />
<Text fontSize="$2" lineHeight="1.1607rem" fontWeight="500" color={INK}>
{t('Chain Resource Directory')}
</Text>
</Chip>
</Box>
<Display mb={16}>{t('{n} Chains. One API.', { n: '100+' })}</Display>
<Lede mb={32} width="100%" maxW={672} mx="auto">
{t('RPC endpoints, chain IDs, WebSockets, explorers, and Hanzo product support for every network.')}
</Lede>
</Box>
</motion.div>
</Box>
</Box>
{/* Search & Filters */}
<Box render="section" pb={32} px={16} $md={{ px: 32 }}>
<Box width="100%" maxW={1152} mx="auto">
<YStack gap={16} mb={32} $md={{ flexDirection: 'row' }}>
{/* The mark and the field are siblings in one bordered row, so
nothing has to be positioned over anything. */}
<XStack
items="center"
gap={8}
grow={1}
shrink={1}
flexBasis="0%"
px={12}
py={12}
borderWidth={1}
borderColor="var(--border)"
rounded="var(--radius-lg)"
bg="var(--secondary)"
{...RING}
>
<Search width={20} height={20} color={DIM} />
<Field placeholder={t('Search chains...')} value={searchQuery} onChangeText={setSearchQuery} />
</XStack>
<XStack flexWrap="wrap" gap={8}>
{[{ id: 'all' as const, name: phrase('All') }, ...CATEGORIES].map((cat) => (
<Action
key={cat.id}
render="button" type="button" onClick={() => setSelectedCategory(cat.id)}
fill={selectedCategory === cat.id}
px={16}
rounded="var(--radius-lg)"
>
{t(cat.name)}
</Action>
))}
</XStack>
</YStack>
{/* Product Support Legend */}
<XStack flexWrap="wrap" gap={16} mb={32} p={16} rounded="var(--radius-lg)" borderWidth={1} borderColor="var(--border)" $platform-web={{ backgroundColor: GROUND }}>
<Text fontSize="$2" lineHeight="1.1607rem" color={DIM}>
{t('Hanzo Products:')}
</Text>
{PRODUCTS.map((product) => {
const Icon = product.icon;
return (
<XStack key={product.key} items="center" gap={6}>
<Icon width={14} height={14} color={DIM} />
<Text {...META}>{t(product.name)}</Text>
</XStack>
);
})}
</XStack>
{/* Chains List */}
<YStack gap={12}>
{filteredChains.map((chain, index) => {
const open = expandedChain === chain.id;
return (
<motion.div key={chain.id} initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3, delay: index * 0.02 }}>
<Card overflow="hidden">
{/* Chain Header: a disclosure whose ground answers a pointer, with no geometry of its own. */}
<XStack
render={<button type="button" onClick={() => setExpandedChain(open ? null : chain.id)} />}
items="center"
justify="space-between"
gap={16}
width="100%"
p={16}
borderWidth={0}
bg="transparent"
cursor="pointer"
hoverStyle={{ bg: 'var(--white-06)' }}
$platform-web={{ textAlign: 'start', color: 'inherit', font: 'inherit' }}
>
<XStack items="center" gap={16}>
<Tile size={40}>
<Globe width={20} height={20} color={INK} />
</Tile>
<Box $platform-web={{ textAlign: 'start' }}>
<CardTitle mb={0}>{chain.name}</CardTitle>
<XStack items="center" gap={12}>
{chain.chainId && <Text {...META}>{t('Chain ID: {id}', { id: chain.chainId })}</Text>}
<Text {...META}>{chain.category === "lux" ? t("Lux Ecosystem") : chain.category.toUpperCase()}</Text>
</XStack>
</Box>
</XStack>
<XStack items="center" gap={16}>
{/* Product Support Icons — the wide read of the same
table the expanded panel gives a phone. */}
<XStack display="none" $md={{ display: 'flex' }} items="center" gap={4}>
{PRODUCTS.map((product) => {
const supported = chain.support[product.key as keyof typeof chain.support];
return (
<XStack key={product.key} render={<span title={t(product.name)} />} width={24} height={24} items="center" justify="center" rounded="var(--radius)" $platform-web={markInk(supported)}>
{supported ? <Check width={14} height={14} /> : <X width={14} height={14} />}
</XStack>
);
})}
</XStack>
<XStack shrink={0} rotate={open ? '180deg' : '0deg'} $platform-web={{ transition: 'transform var(--duration-fast) var(--ease-in-out)' }}>
<ChevronDown width={20} height={20} color={DIM} />
</XStack>
</XStack>
</XStack>
{/* Expanded Details */}
{open && (
<Box px={16} pb={16} borderTopWidth={1} borderColor="var(--border)">
<YStack gap={24} pt={16} $md={{ flexDirection: 'row' }}>
{/* Endpoints */}
<Box grow={1} shrink={1} flexBasis="0%" minW={0}>
<Label>{t('Endpoints')}</Label>
<YStack gap={8}>
<Endpoint name="RPC">{chain.rpcEndpoint}</Endpoint>
{chain.wsEndpoint && <Endpoint name="WS">{chain.wsEndpoint}</Endpoint>}
</YStack>
</Box>
{/* Resources */}
<Box grow={1} shrink={1} flexBasis="0%" minW={0}>
<Label>{t('Resources')}</Label>
<XStack flexWrap="wrap" gap={8}>
{([
[phrase('Explorer'), chain.explorer],
['GitHub', chain.github],
[phrase('Docs'), chain.docs],
[phrase('Faucet'), chain.faucet],
] as const).map(([label, href]) =>
href ? (
// A link out to somebody else's page for this chain: its ground answers a pointer.
<Text
key={label}
render={<a href={href} target="_blank" rel="noopener noreferrer" />}
display="inline-flex"
items="center"
gap={6}
px={12}
py={6}
rounded="var(--radius-lg)"
bg="var(--neutral-800)"
hoverStyle={{ bg: 'var(--neutral-700)' }}
fontSize="$1"
lineHeight="0.9167rem"
textDecorationLine="none"
$platform-web={{ color: ink(80) }}
>
{t(label)} <ExternalLink width={12} height={12} />
</Text>
) : null
)}
</XStack>
</Box>
</YStack>
{/* Product Support Grid — the narrow read, where the
row above has no room for ten marks. */}
<Box mt={16} $md={{ display: 'none' }}>
<Label>{t('Product Support')}</Label>
<View display="grid" gridTemplateColumns="repeat(5, minmax(0, 1fr))" gap={8}>
{PRODUCTS.map((product) => {
const supported = chain.support[product.key as keyof typeof chain.support];
const Icon = product.icon;
return (
<YStack key={product.key} items="center" gap={4} p={8} rounded="var(--radius)" $platform-web={{ ...markInk(supported), textAlign: 'center' }}>
<Icon width={16} height={16} />
<Text color="inherit" $platform-web={{ fontSize: 'var(--text-floor)', lineHeight: '1.4' }}>
{t(product.name)}
</Text>
</YStack>
);
})}
</View>
</Box>
</Box>
)}
</Card>
</motion.div>
);
})}
</YStack>
{filteredChains.length === 0 && (
<Text display="block" py={48} text="center" color={DIM}>
{t('No chains found matching your search.')}
</Text>
)}
</Box>
</Box>
</Box>
);
};
export default ChainDirectory;