Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f9ab08b9c |
@@ -24,7 +24,7 @@ In this regard, we collect and process the following personal data from your use
|
||||
|
||||
* **IP address**: As part of such use of the Website we briefly process your IP address but we have no way of identifying you. We however have a legitimate interest in processing such IP addresses to ensure the technical functionality and enhance the security measures of the Website. This IP address is not stored by us over time.
|
||||
|
||||
* **Collection of email addresses**: We only collect your email address on our website for the purpose of sending you information in relation to the development of and the potential sale of Keycard Shell. Your email address is securely stored on our self-hosted servers, and we have implemented measures to protect such personal data by securing it with appropriate technical and organisational measures. We will not share such personal data to third parties without your consent, except as required by law. We will only collect and process your email address on the basis of you providing your consent for us to do so. We will retain your email address for as long as necessary to provide you with updates about Keycard Shell, unless you withdraw your consent or request deletion earlier
|
||||
* **Collection of email addresses**: We only collect your email address on our website for the purpose of sending you information in relation to the development of and the potential sale of Keycard Pro. Your email address is securely stored on our self-hosted servers, and we have implemented measures to protect such personal data by securing it with appropriate technical and organisational measures. We will not share such personal data to third parties without your consent, except as required by law. We will only collect and process your email address on the basis of you providing your consent for us to do so. We will retain your email address for as long as necessary to provide you with updates about Keycard Pro, unless you withdraw your consent or request deletion earlier
|
||||
|
||||
## 3) Third party processing of personal data
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 305 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 373 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 903 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 313 KiB After Width: | Height: | Size: 316 KiB |
@@ -1,118 +0,0 @@
|
||||
import { KEYCARD_PRODUCTS } from '~/app/_constants/shopify/products'
|
||||
import { formatPrice } from '~/app/_utils/format-price'
|
||||
import { Button } from '~components/button'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { BuyKeycardDialog } from '~components/buy-keycard-dialog'
|
||||
import { GetNotifiedDialog } from '~components/get-notified-dialog'
|
||||
import { Image } from '~components/image'
|
||||
import { cx } from 'cva'
|
||||
|
||||
type Props = {
|
||||
image: string
|
||||
imageClassName?: string
|
||||
name: string
|
||||
secondName?: string
|
||||
title: React.ReactNode
|
||||
description: string
|
||||
buttons: React.ReactNode
|
||||
info: string
|
||||
}
|
||||
|
||||
const Section = (props: Props) => {
|
||||
const {
|
||||
image,
|
||||
name,
|
||||
title,
|
||||
description,
|
||||
buttons,
|
||||
info,
|
||||
imageClassName,
|
||||
secondName,
|
||||
} = props
|
||||
return (
|
||||
<div className="relative w-full overflow-clip">
|
||||
<div className="absolute inset-0 z-10 h-1/2 w-full bg-gradient-to-b from-dark-100 to-[transparent]" />
|
||||
<Image
|
||||
src={image}
|
||||
alt={name}
|
||||
width={900}
|
||||
height={600}
|
||||
draggable={false}
|
||||
className={cx([
|
||||
'pointer-events-none absolute left-1/2 z-20 max-w-[549px] -translate-x-1/2 select-none lg:top-6',
|
||||
imageClassName,
|
||||
])}
|
||||
/>
|
||||
<div className="flex size-full flex-col items-center justify-end rounded-28 border border-white-8 bg-white-4 px-5 pb-20 pt-[300px] lg:px-10 lg:pb-[120px] xl:px-[98px]">
|
||||
<div className="container relative z-10 flex max-w-[434px] flex-col items-center justify-center px-2">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">
|
||||
{name} <span className="font-200">{secondName}</span>
|
||||
</p>
|
||||
<h2 className="flex pb-4 text-center font-lora text-32 font-400 lg:text-48">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="pb-8 text-center text-20 font-300 text-white-80">
|
||||
{description}
|
||||
</p>
|
||||
<div className="flex space-x-4">{buttons}</div>
|
||||
<p className="pt-6 text-16 font-300 text-white-60">{info}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const BuyCards = () => {
|
||||
return (
|
||||
<section className="grid grid-cols-1 gap-10 overflow-clip pb-2 pt-[160px] full-view-port lg:grid-cols-2 lg:flex-row lg:gap-2 lg:pt-[200px] lg:remove-full-view-port">
|
||||
<Section
|
||||
image="/assets/bottom-keycard.png"
|
||||
name="keycard"
|
||||
title={
|
||||
<>
|
||||
A card shaped <br /> hardware wallet
|
||||
</>
|
||||
}
|
||||
description="Store and trade your crypto in a simple, secure and slim hardware wallet."
|
||||
buttons={
|
||||
<>
|
||||
<BuyKeycardDialog>
|
||||
<Button variant="primary">Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
<ButtonLink href="/keycard" variant="secondary">
|
||||
Learn more
|
||||
</ButtonLink>
|
||||
</>
|
||||
}
|
||||
info={`From ${formatPrice({
|
||||
amount: KEYCARD_PRODUCTS.ONE_CARD_SET.price,
|
||||
})}`}
|
||||
/>
|
||||
<Section
|
||||
image="/assets/bottom-keycard-shell.png"
|
||||
imageClassName="pt-10 lg:pt-0 lg:-mb-20 lg:w-full"
|
||||
name="keycard"
|
||||
secondName="shell"
|
||||
title={
|
||||
<>
|
||||
One device for all <br /> your Keycards
|
||||
</>
|
||||
}
|
||||
description="Unrivalled security with an infinite number of removable Keycards, each with their own key."
|
||||
buttons={
|
||||
<>
|
||||
<GetNotifiedDialog>
|
||||
<Button>Get notified</Button>
|
||||
</GetNotifiedDialog>
|
||||
<ButtonLink href="/keycard-shell" variant="secondary">
|
||||
Learn more
|
||||
</ButtonLink>
|
||||
</>
|
||||
}
|
||||
info="Coming 2025 · Bundled with Keycard"
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { BuyCards }
|
||||
@@ -13,26 +13,24 @@ import { createElement } from 'react'
|
||||
const features: Array<Feature> = [
|
||||
{
|
||||
name: 'Competitive pricing',
|
||||
keycardShell: {
|
||||
label: 'TBD',
|
||||
},
|
||||
keycard: {
|
||||
label: formatPrice({
|
||||
amount: KEYCARD_PRODUCTS.ONE_CARD_SET.price,
|
||||
}),
|
||||
},
|
||||
tangem: {
|
||||
label: '€50',
|
||||
label: formatPrice({
|
||||
amount: 50,
|
||||
}),
|
||||
},
|
||||
ledger: {
|
||||
label: '€80',
|
||||
label: formatPrice({
|
||||
amount: 80,
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'EVM chain compatible',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
name: 'Compatible with Ethereum and EVM-chains',
|
||||
keycard: {
|
||||
featured: true,
|
||||
},
|
||||
@@ -44,39 +42,14 @@ const features: Array<Feature> = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Bitcoin compatible',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: true,
|
||||
},
|
||||
tangem: {
|
||||
featured: true,
|
||||
},
|
||||
ledger: {
|
||||
featured: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Hardware level protection',
|
||||
keycardShell: {
|
||||
badge: {
|
||||
text: 'EAL 6+',
|
||||
gradient: true,
|
||||
icon: FirmwareIcon,
|
||||
},
|
||||
},
|
||||
name: 'Hardware-level protection',
|
||||
// tooltip: 'Security certification level',
|
||||
keycard: { badge: { text: 'EAL 6+', gradient: true, icon: FirmwareIcon } },
|
||||
tangem: { badge: { text: 'EAL 6+', gradient: true, icon: FirmwareIcon } },
|
||||
ledger: { badge: { text: 'EAL 5+' } },
|
||||
},
|
||||
{
|
||||
name: 'Mobile friendly',
|
||||
tooltip: 'Compatible with mobile devices',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: true,
|
||||
},
|
||||
@@ -88,25 +61,34 @@ const features: Array<Feature> = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Unlimited master keys',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
name: 'Works on desktop',
|
||||
keycard: {
|
||||
featured: true,
|
||||
},
|
||||
tangem: {
|
||||
featured: false,
|
||||
},
|
||||
ledger: {
|
||||
featured: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Many cards, many keys',
|
||||
keycard: {
|
||||
featured: true,
|
||||
// tooltip: 'No limit on master keys',
|
||||
},
|
||||
tangem: {
|
||||
featured: true,
|
||||
// tooltip: 'No limit on master keys',
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
// tooltip: 'No limit on master keys',
|
||||
},
|
||||
{
|
||||
name: 'Easy back up of master keys',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: true,
|
||||
},
|
||||
@@ -119,10 +101,7 @@ const features: Array<Feature> = [
|
||||
},
|
||||
{
|
||||
name: 'Open ecosystem',
|
||||
tooltip: 'Part of an open ecosystem',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
// tooltip: 'Part of an open ecosystem',
|
||||
keycard: {
|
||||
featured: true,
|
||||
},
|
||||
@@ -130,16 +109,13 @@ const features: Array<Feature> = [
|
||||
featured: false,
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
featured: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Open source secure element',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
name: 'Immutable, open-source secure element',
|
||||
keycard: {
|
||||
featured: false,
|
||||
featured: true,
|
||||
},
|
||||
tangem: {
|
||||
featured: false,
|
||||
@@ -148,130 +124,6 @@ const features: Array<Feature> = [
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Secure element can't be upgraded",
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: false,
|
||||
},
|
||||
tangem: {
|
||||
featured: false,
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'QR transaction signing with any wallet',
|
||||
tooltip: 'Sign transactions using QR codes',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: false,
|
||||
},
|
||||
tangem: {
|
||||
featured: false,
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Airgap capable',
|
||||
tooltip: 'Can operate without direct connection',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: false,
|
||||
},
|
||||
tangem: {
|
||||
featured: false,
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Modular architecture',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: false,
|
||||
},
|
||||
tangem: {
|
||||
featured: false,
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Secure element has its own usages',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
tangem: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Can be used with future signing schemes',
|
||||
tooltip: 'Future-proof design',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
tangem: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Removable battery',
|
||||
tooltip: 'Battery can be replaced',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
tangem: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'USBC + Camera',
|
||||
keycardShell: {
|
||||
featured: true,
|
||||
},
|
||||
keycard: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
tangem: {
|
||||
featured: 'n/a',
|
||||
},
|
||||
ledger: {
|
||||
featured: false,
|
||||
},
|
||||
},
|
||||
] as const
|
||||
|
||||
type FeatureVariant = {
|
||||
@@ -288,7 +140,6 @@ type FeatureVariant = {
|
||||
type Feature = {
|
||||
name: string
|
||||
tooltip?: string
|
||||
keycardShell: FeatureVariant
|
||||
keycard: FeatureVariant
|
||||
tangem: FeatureVariant
|
||||
ledger: FeatureVariant
|
||||
@@ -305,7 +156,8 @@ const FeatureInfo = ({
|
||||
if (variant.featured === false)
|
||||
return <RemoveIcon className="size-4 text-white-60" />
|
||||
if (variant.featured === 'n/a') return <p className="text-white-60">n/a</p>
|
||||
if (variant.label) return <p className="text-white-95">{variant.label}</p>
|
||||
if (variant.label)
|
||||
return <p className="font-400 text-white-95">{variant.label}</p>
|
||||
if (variant.badge) {
|
||||
return (
|
||||
<Tag
|
||||
@@ -319,24 +171,36 @@ const FeatureInfo = ({
|
||||
}
|
||||
}
|
||||
|
||||
const TooltipInfo = ({
|
||||
variant,
|
||||
}: {
|
||||
variant: FeatureVariant | undefined | string
|
||||
}) => {
|
||||
if (typeof variant === 'string') return null
|
||||
|
||||
if (!variant || !variant.tooltip) return null
|
||||
|
||||
return (
|
||||
<Tooltip label={variant.tooltip || ''}>
|
||||
<div className="absolute left-[calc(50%+10px)] flex">
|
||||
<InfoIcon className="ml-2 flex-shrink-0 text-white-40 transition-colors hover:text-white-60" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const ComparisonTable = () => {
|
||||
const products: Array<keyof Feature> = [
|
||||
'keycardShell',
|
||||
'keycard',
|
||||
'tangem',
|
||||
'ledger',
|
||||
]
|
||||
const products: Array<keyof Feature> = ['keycard', 'tangem', 'ledger']
|
||||
|
||||
return (
|
||||
<section className="mx-auto max-w-[1352px] pt-[120px] text-white-95 lg:pt-[200px]">
|
||||
<h2 className="px-3 font-lora text-32 text-white-95 min-[1512px]:px-0">
|
||||
Going one step further.
|
||||
Going one step further
|
||||
</h2>
|
||||
<div className="overflow-x-auto px-3 scrollbar-none min-[1512px]:px-0">
|
||||
<div className="w-[684px] pt-14 md:w-full lg:w-full lg:pt-20">
|
||||
<div className="grid grid-cols-[1.33fr,1fr,1fr,1fr,1fr] justify-items-center text-center font-lora text-24 font-400 text-white-90 lg:grid-cols-[2fr,1fr,1fr,1fr,1fr] lg:text-24">
|
||||
<div className="grid grid-cols-[1.33fr,1fr,1fr,1fr] justify-items-center px-6 text-center font-lora text-24 font-400 text-white-90 lg:grid-cols-[2fr,1fr,1fr,1fr] lg:text-24">
|
||||
<div className="justify-self-start p-4 pb-5 pl-6">Feature</div>
|
||||
<div className="p-4 pb-5">Shell</div>
|
||||
<div className="p-4 pb-5">Keycard</div>
|
||||
<div className="p-4 pb-5">Tangem</div>
|
||||
<div className="p-4 pb-5">Ledger</div>
|
||||
@@ -347,7 +211,7 @@ const ComparisonTable = () => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="grid grid-cols-[1.33fr,1fr,1fr,1fr,1fr] border-b border-dashed border-white-12 font-300 first:pt-2 last:border-b-0 last:pb-2 lg:grid-cols-[2fr,1fr,1fr,1fr,1fr]"
|
||||
className="grid grid-cols-[1.33fr,1fr,1fr,1fr] border-b border-dashed border-white-12 font-300 first:pt-2 last:border-b-0 last:pb-2 lg:grid-cols-[2fr,1fr,1fr,1fr]"
|
||||
>
|
||||
<div className="w-full p-5 first:pl-0 lg:flex lg:items-center">
|
||||
<span className="relative inline whitespace-normal">
|
||||
@@ -368,6 +232,7 @@ const ComparisonTable = () => {
|
||||
className="relative flex items-center justify-center last:pr-0"
|
||||
>
|
||||
<FeatureInfo variant={feature[product]} />
|
||||
<TooltipInfo variant={feature[product]} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ const features = [
|
||||
{
|
||||
title: 'Water resistant',
|
||||
description:
|
||||
'Resistant to most everyday spills, give it a wipe and you’re good to go.',
|
||||
'Left Keycard in your jeans? It will survive the washing machine!',
|
||||
image: '/assets/keycard/water-resistant.png',
|
||||
},
|
||||
]
|
||||
@@ -52,11 +52,10 @@ const Faqs = () => {
|
||||
<div className="flex w-full flex-1 flex-col lg:w-auto">
|
||||
<div className="flex justify-center pt-[120px] lg:hidden">
|
||||
<Image
|
||||
src="/assets/faqs-mobile.png"
|
||||
src="/assets/faqs-v1.png"
|
||||
alt="FAQ Background"
|
||||
width={736}
|
||||
height={736}
|
||||
className="w-full"
|
||||
width={264}
|
||||
height={488}
|
||||
/>
|
||||
</div>
|
||||
<h2 className="mt-14 font-lora text-32 text-white-95 lg:mt-0">
|
||||
@@ -83,7 +82,7 @@ const Faqs = () => {
|
||||
)}
|
||||
>
|
||||
{faq.question}
|
||||
<div className="rounded-[10px] border border-white-12 bg-white-8 p-[6px] hover:border-white-20 hover:bg-white-12">
|
||||
<div className="rounded-[10px] border border-white-12 bg-white-8 p-[6px]">
|
||||
<ChevronDownIcon className="text-white-95 transition-transform duration-300 ease-[cubic-bezier(0.87,_0,_0.13,_1)] group-data-[state=open]:rotate-180" />
|
||||
</div>
|
||||
</Accordion.Trigger>
|
||||
@@ -99,15 +98,14 @@ const Faqs = () => {
|
||||
))}
|
||||
</Accordion.Root>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col lg:w-auto lg:items-end">
|
||||
<div className="flex w-full flex-col lg:w-auto lg:items-center">
|
||||
<div className="relative hidden lg:block">
|
||||
<div className="absolute right-[-245px] top-[-20px] z-10 h-[400px] w-full bg-gradient-to-tr from-[transparent] via-dark-100 via-[83%] to-dark-100" />
|
||||
<Image
|
||||
src="/assets/faqs.png"
|
||||
src="/assets/faqs-v1.png"
|
||||
alt="FAQ Background"
|
||||
height={790}
|
||||
width={1010}
|
||||
className="relative right-[-245px] top-[-40px]"
|
||||
height={488}
|
||||
width={264}
|
||||
className="relative"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -117,8 +115,7 @@ const Faqs = () => {
|
||||
Still have questions?
|
||||
</p>
|
||||
<p className="text-16 font-300 text-white-80">
|
||||
Reach out to our team or engage with our community on Discord or
|
||||
X.
|
||||
Reach out to our team or engage with us on X.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
|
||||
@@ -204,7 +204,6 @@ const FeaturesAccordion = (props: Props) => {
|
||||
alt={selected.title}
|
||||
className={imageClassName}
|
||||
quality={75}
|
||||
priority
|
||||
/>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
@@ -227,7 +226,6 @@ const FeaturesAccordion = (props: Props) => {
|
||||
alt={selected.title}
|
||||
className={imageClassName}
|
||||
quality={75}
|
||||
priority
|
||||
/>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
@@ -1,55 +1,36 @@
|
||||
import { KEYCARD_PRODUCTS } from '~/app/_constants/shopify/products'
|
||||
import { formatPrice } from '~/app/_utils/format-price'
|
||||
import { Background } from '~components/3d/background'
|
||||
import { Button } from '~components/button'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { BuyKeycardDialog } from '~components/buy-keycard-dialog'
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const Hero = () => {
|
||||
return (
|
||||
<section className="relative flex h-[calc(100svh-80px)] flex-col justify-end overflow-clip rounded-t-28 border border-white-8 bg-white-4 px-0 pt-0 full-view-port lg:h-auto lg:px-[72px] lg:py-20 lg:remove-full-view-port">
|
||||
<Image
|
||||
alt="Keycard"
|
||||
src="/assets/hero-bg.png"
|
||||
width={1600}
|
||||
height={1600}
|
||||
className="absolute bottom-0 right-0 z-0 hidden w-auto bg-cover bg-center lg:block"
|
||||
priority
|
||||
/>
|
||||
<Image
|
||||
alt="Keycard"
|
||||
src="/assets/hero-bg-mobile.png"
|
||||
width={1600}
|
||||
height={1600}
|
||||
className="absolute right-0 top-0 block w-full bg-cover bg-center lg:hidden"
|
||||
priority
|
||||
/>
|
||||
|
||||
<div className="relative z-10 flex max-w-[434px] flex-col px-5 lg:max-w-[500px] lg:pt-0">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">keycard</p>
|
||||
<h1 className="flex pb-8 font-lora text-32 font-400 lg:pb-4 lg:text-48">
|
||||
Lightweight design
|
||||
<br />
|
||||
heavyweight security
|
||||
</h1>
|
||||
<p className="pb-8 text-20 font-300 text-white-80">
|
||||
Trade swiftly and safely back up your assets with Keycard hardware
|
||||
wallets.
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<BuyKeycardDialog>
|
||||
<Button variant="primary">Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
<ButtonLink href="/keycard" variant="secondary">
|
||||
Learn more
|
||||
</ButtonLink>
|
||||
<section className="relative flex h-svh overflow-y-clip full-view-port lg:remove-full-view-port 2xl:h-[839px]">
|
||||
<Background variant="homepage" />
|
||||
<div className="pointer-events-none relative z-20 flex flex-1 flex-col justify-end px-5 pb-5 align-bottom lg:pb-[43px] xl:px-20">
|
||||
<div className="flex flex-col items-start justify-between lg:flex-row">
|
||||
<div className="pointer-events-auto flex max-w-[664px] flex-col lg:self-end">
|
||||
<h1 className="flex font-lora text-32 font-400 text-white-95 lg:text-44">
|
||||
Lightweight design, <br /> heavyweight security
|
||||
</h1>
|
||||
<p className="max-w-[435px] pb-8 pt-4 text-20 font-300 text-white-80">
|
||||
Trade swiftly and safely back up your assets with Keycard hardware
|
||||
wallets.
|
||||
</p>
|
||||
<div className="flex items-center gap-5">
|
||||
<BuyKeycardDialog>
|
||||
<Button variant="primary">Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
<p className="text-16 font-300 text-white-60">
|
||||
From{' '}
|
||||
{formatPrice({
|
||||
amount: KEYCARD_PRODUCTS.ONE_CARD_SET.price,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="pb-8 pt-6 text-16 font-300 text-white-60 lg:pb-0">
|
||||
From{' '}
|
||||
{formatPrice({
|
||||
amount: KEYCARD_PRODUCTS.ONE_CARD_SET.price,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { ExternalIcon } from '@status-im/icons/20'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { FeaturesDialog } from '~components/features-dialog'
|
||||
import { Image } from '~components/image'
|
||||
import { Link } from '~components/link'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '~components/tabs'
|
||||
@@ -18,8 +16,9 @@ const featuresMobile = [
|
||||
href="https://tokenlists.org/token-list?url=https://ipfs.io/ipns/tokens.uniswap.org"
|
||||
className="underline decoration-1 underline-offset-2 transition-colors hover:text-orange"
|
||||
>
|
||||
Uniswap Labs default token list.
|
||||
Uniswap Labs default token list
|
||||
</Link>
|
||||
.
|
||||
</>
|
||||
),
|
||||
image: '/assets/keycard/slider/status-app-mobile-send.png',
|
||||
@@ -33,7 +32,7 @@ const featuresMobile = [
|
||||
},
|
||||
{
|
||||
title: 'Bridge',
|
||||
description: 'Bridge assets across Ethereum’s most popular chains.',
|
||||
description: 'Bridge assets across Ethereum’s most popular chains. ',
|
||||
image: '/assets/keycard/slider/status-app-mobile-bridge.png',
|
||||
imageMobile: '/assets/keycard/slider/iphone-bridge.png',
|
||||
},
|
||||
@@ -56,8 +55,9 @@ const featuresDesktop = [
|
||||
href="https://tokenlists.org/token-list?url=https://ipfs.io/ipns/tokens.uniswap.org"
|
||||
className="underline decoration-1 underline-offset-2 transition-colors hover:text-orange"
|
||||
>
|
||||
Uniswap Labs default token list.
|
||||
Uniswap Labs default token list
|
||||
</Link>
|
||||
.
|
||||
</>
|
||||
),
|
||||
image: '/assets/keycard/slider/status-app-desktop-send.png',
|
||||
@@ -71,7 +71,7 @@ const featuresDesktop = [
|
||||
},
|
||||
{
|
||||
title: 'Bridge',
|
||||
description: 'Bridge assets across Ethereum’s most popular chains.',
|
||||
description: 'Bridge assets across Ethereum’s most popular chains. ',
|
||||
image: '/assets/keycard/slider/status-app-desktop-bridge.png',
|
||||
imageMobile: '/assets/keycard/slider/status-app-desktop-bridge.png',
|
||||
},
|
||||
@@ -95,30 +95,6 @@ const featuresDesktop = [
|
||||
},
|
||||
]
|
||||
|
||||
const integrations = [
|
||||
{
|
||||
name: 'Enno Wallet',
|
||||
description: 'Native Android Ethereum Wallet',
|
||||
logo: '/assets/keycard/enno.png',
|
||||
buttonText: 'Learn more',
|
||||
href: 'https://ennowallet.com/',
|
||||
},
|
||||
{
|
||||
name: 'WallETH',
|
||||
description: 'Native Android Ethereum Wallet',
|
||||
logo: '/assets/keycard/walleth.png',
|
||||
buttonText: 'Learn more',
|
||||
href: 'https://walleth.org/',
|
||||
},
|
||||
{
|
||||
name: 'Logos Operators',
|
||||
description: '5,000 Ordinals Collection',
|
||||
logo: '/assets/keycard/logos-operators.png',
|
||||
buttonText: 'Learn more',
|
||||
href: 'https://dashboard.logos.co/',
|
||||
},
|
||||
]
|
||||
|
||||
const Integration = () => {
|
||||
return (
|
||||
<section className="mt-[200px]">
|
||||
@@ -132,12 +108,12 @@ const Integration = () => {
|
||||
alt="Status"
|
||||
src="/assets/keycard/status-logo.png"
|
||||
/>
|
||||
<FeaturesDialog>
|
||||
<button className="flex items-center gap-1 rounded-16 border border-dashed border-[#FF640020] bg-[#FF640010] py-[5px] pl-4 pr-3 text-14 font-300 text-orange transition-colors group-hover:bg-[#FF640020]">
|
||||
Keycard features in beta - learn more
|
||||
<Link className="group" href="/">
|
||||
<div className="flex items-center gap-1 rounded-16 border border-dashed border-[#FF640020] bg-[#FF640010] py-[5px] pl-4 pr-3 text-14 font-300 text-orange transition-colors group-hover:bg-[#FF640020]">
|
||||
Keycard features in beta
|
||||
<ExternalIcon />
|
||||
</button>
|
||||
</FeaturesDialog>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<h2 className="pt-1 font-lora text-32 font-400 text-white-95">
|
||||
Seamless integration with Status
|
||||
@@ -172,63 +148,6 @@ const Integration = () => {
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 pt-20 md:px-12">
|
||||
<h2 className="pb-4 pl-6 text-12 font-400 text-white-80 lg:pl-8">
|
||||
ALSO INTEGRATED WITH
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-8 rounded-28 border border-white-8 bg-white-4 p-6 pt-5 text-white-95 md:grid-cols-2 lg:grid-cols-4 lg:gap-6 lg:p-8 lg:pt-9">
|
||||
{integrations.map((integration, index) => (
|
||||
<div key={index} className="flex flex-col">
|
||||
<div className="mb-3 inline-flex items-center">
|
||||
{integration.logo && (
|
||||
<Image
|
||||
src={integration.logo}
|
||||
alt={`${integration.name} logo`}
|
||||
width={24}
|
||||
height={23}
|
||||
className="mr-2"
|
||||
/>
|
||||
)}
|
||||
<h3 className="font-lora text-24 font-400">
|
||||
{integration.name}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-6 text-16 font-300 text-white-80">
|
||||
{integration.description}
|
||||
</p>
|
||||
|
||||
<ButtonLink
|
||||
href={integration.href}
|
||||
variant="secondary"
|
||||
className="group w-fit pr-2"
|
||||
>
|
||||
<span className="text-white-95">{integration.buttonText}</span>
|
||||
<ExternalIcon />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-col gap-6">
|
||||
<div>
|
||||
<h2 className="font-lora text-24 font-500 text-white-95 lg:text-32">
|
||||
Want to integrate?
|
||||
</h2>
|
||||
<p className="text-16 text-white-80">
|
||||
Read our documentation or get in touch
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<ButtonLink href="/docs">Read docs</ButtonLink>
|
||||
<ButtonLink
|
||||
href="mailto:support@keycard.tech"
|
||||
variant="secondary"
|
||||
>
|
||||
Get in touch
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import { FirmwareIcon } from '@status-im/icons/20'
|
||||
import { GithubIcon } from '@status-im/icons/social'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { Image } from '~components/image'
|
||||
import { Tag } from '~components/tag'
|
||||
import { InfinityIcon } from '~icons/infinity'
|
||||
import { cx } from 'cva'
|
||||
import { createElement } from 'react'
|
||||
|
||||
const FEATURES = [
|
||||
{
|
||||
title: 'Best-in-class security',
|
||||
description:
|
||||
'Keycard secure element has the highest level of certification provided by Common Criteria.',
|
||||
image: '/assets/feature-keycard.png',
|
||||
imageClassName: '!justify-start',
|
||||
badge: { icon: FirmwareIcon, text: 'EAL 6+', gradient: true },
|
||||
className: 'row-span-2 col-span-1',
|
||||
},
|
||||
{
|
||||
title: 'Fully open source',
|
||||
description:
|
||||
'Open-source code on an open framework, making it the most open way to design a secure element.',
|
||||
button: { icon: GithubIcon, text: 'View on GitHub' },
|
||||
className: 'col-span-1',
|
||||
},
|
||||
{
|
||||
title: 'Airgapped',
|
||||
description:
|
||||
'Through Keycard Shell’s camera or Keycard’s contactless nature, our products can be airgapped.',
|
||||
image: '/assets/feature-keycard-shell.png',
|
||||
badge: { text: '0 cables' },
|
||||
className: 'row-span-2 col-span-1 flex-col-reverse',
|
||||
},
|
||||
{
|
||||
title: 'Made to last',
|
||||
description:
|
||||
'Your keycard has a life expectancy of 25+ years, resists water and dust. It will still securely store your keys.',
|
||||
badge: { text: '25+ years' },
|
||||
className: 'col-span-1',
|
||||
},
|
||||
{
|
||||
title: 'Easy to backup',
|
||||
description:
|
||||
'Securely store your private key on multiple cards. Encrypted, instead of the typical piece of paper.',
|
||||
badge: { icon: InfinityIcon, text: 'backups' },
|
||||
className: 'col-span-1',
|
||||
},
|
||||
{
|
||||
title: 'Discreet',
|
||||
description:
|
||||
'With its light, small and discreet form factor your Keycard can go unnoticed in your wallet.',
|
||||
badge: { text: 'Credit card format' },
|
||||
className: 'col-span-1',
|
||||
},
|
||||
]
|
||||
|
||||
const UnderlinedWord = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="relative inline-block">
|
||||
<span className="relative z-10 [text-shadow:_-3px_2px_black,_4px_1px_black,_2px_0px_black,_3px_2px_black]">
|
||||
{children}
|
||||
</span>
|
||||
<div className="absolute bottom-1 left-0 -z-10 h-px w-full bg-orange" />
|
||||
</div>
|
||||
)
|
||||
|
||||
const KeycardFeatures = () => {
|
||||
return (
|
||||
<section className="max-w-[1352px] px-3 pt-[120px] lg:mx-auto lg:pt-[200px] min-[1512px]:px-0">
|
||||
<h2 className="max-w-[665px] font-lora text-32 text-white-95">
|
||||
Join the <UnderlinedWord>open source</UnderlinedWord> revolution of the
|
||||
most <UnderlinedWord>modular</UnderlinedWord> and{' '}
|
||||
<UnderlinedWord>future proof</UnderlinedWord> hardware wallet system
|
||||
ever conceived.
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-5 pt-20 lg:grid-cols-4 lg:gap-6">
|
||||
{FEATURES.map((feature, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cx([
|
||||
'relative flex flex-col justify-between overflow-clip rounded-28 bg-white-4 outline outline-1 outline-white-8',
|
||||
feature.className,
|
||||
])}
|
||||
>
|
||||
{feature.image && (
|
||||
<div
|
||||
className={cx([
|
||||
'flex items-center justify-center',
|
||||
feature.imageClassName,
|
||||
])}
|
||||
>
|
||||
<Image
|
||||
src={feature.image}
|
||||
alt={feature.title}
|
||||
className="rounded-12 object-cover"
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-6 pt-5">
|
||||
<div>
|
||||
<h3 className="mb-[6px] font-lora text-24 font-400">
|
||||
{feature.title}
|
||||
</h3>
|
||||
{feature.description && (
|
||||
<p className="mb-6 text-16 font-300 text-white-60">
|
||||
{feature.description}
|
||||
</p>
|
||||
)}
|
||||
{feature.badge && (
|
||||
<Tag
|
||||
gradient={feature.badge.gradient}
|
||||
icon={
|
||||
feature.badge.icon && createElement(feature.badge.icon)
|
||||
}
|
||||
>
|
||||
{feature.badge.text}
|
||||
</Tag>
|
||||
)}
|
||||
{feature.button && (
|
||||
<ButtonLink
|
||||
href="https://github.com/keycard-tech/keycard-pro"
|
||||
className="font-500 [&_path]:hover:fill-white-dark"
|
||||
variant="white"
|
||||
icon={<feature.button.icon />}
|
||||
>
|
||||
{feature.button.text}
|
||||
</ButtonLink>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { KeycardFeatures }
|
||||
@@ -1,56 +1,42 @@
|
||||
import { Button } from '~components/button'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { GetNotifiedDialog } from '~components/get-notified-dialog'
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const KeycardShell = () => {
|
||||
return (
|
||||
<section className="relative flex h-[calc(100svh-80px)] flex-col justify-end overflow-hidden rounded-b-28 border border-t-0 border-white-8 bg-white-4 backdrop-blur-[20px] full-view-port lg:h-auto lg:flex-row lg:items-center lg:justify-normal lg:remove-full-view-port">
|
||||
<div className="hidden max-h-[510px] flex-1 items-start justify-center overflow-hidden lg:flex">
|
||||
<Image
|
||||
src="/assets/keycard-shell.png"
|
||||
alt="Keycard Shell Hardware Wallet"
|
||||
width="758"
|
||||
height="758"
|
||||
className="relative left-0 top-[-86px] object-cover object-center xl:top-[-128px] xl:object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<section className="relative mx-0 mt-[160px] flex flex-col items-center overflow-hidden rounded-28 border border-white-12 lg:mx-6 lg:mt-[200px] lg:flex-row lg:justify-end xl:mx-20">
|
||||
<Image
|
||||
src="/assets/keycard-shell-mobile.png"
|
||||
src="/assets/bg-keycard-v1.png"
|
||||
alt="Keycard Shell Hardware Wallet"
|
||||
width="900"
|
||||
height="600"
|
||||
className="self-end lg:hidden"
|
||||
priority
|
||||
width={1400}
|
||||
height={800}
|
||||
className="absolute left-0 top-0 hidden size-full object-cover lg:block"
|
||||
/>
|
||||
|
||||
<div className="relative z-10 -mt-16 flex max-w-[506px] flex-col px-5 pb-8 pt-0 lg:mt-0 lg:py-20 lg:pl-0 lg:pr-[72px]">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">
|
||||
keycard <span className="font-200">shell</span>
|
||||
</p>
|
||||
<p className="flex pb-8 font-lora text-32 font-400 lg:pb-4 lg:text-48">
|
||||
One secure device,
|
||||
<Image
|
||||
src="/assets/bg-keycard-mobile-v1.png"
|
||||
alt="Keycard Shell Hardware Wallet"
|
||||
width={414}
|
||||
height={614}
|
||||
className="relative left-0 top-0 block aspect-[414/614] w-full lg:hidden"
|
||||
/>
|
||||
|
||||
<div className="absolute right-0 top-0 hidden h-full w-[270px] bg-gradient-to-r from-[transparent] to-dark-100 lg:block" />
|
||||
|
||||
<div className="relative z-10 mt-[-53%] flex w-full flex-col p-6 pt-0 lg:mt-0 lg:w-auto lg:py-[256px] lg:pr-[229px]">
|
||||
<p className="flex min-w-[320px] pb-4 font-lora text-32 font-400">
|
||||
Get ready for the
|
||||
<br />
|
||||
infinite backups
|
||||
future of modular
|
||||
<br />
|
||||
hardware security
|
||||
</p>
|
||||
<p className="pb-8 text-20 font-300 text-white-80">
|
||||
Unrivalled security with an infinite number of removable Keycards,
|
||||
each with their own key.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<GetNotifiedDialog>
|
||||
<Button>Get notified</Button>
|
||||
</GetNotifiedDialog>
|
||||
<ButtonLink href="/keycard-shell" variant="secondary">
|
||||
Learn more
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<p className="flex items-center gap-2 pt-6 text-16 font-300 text-white-60">
|
||||
Coming 2025 <span className="size-1 rounded-full bg-white-40" />{' '}
|
||||
Bundled with Keycard
|
||||
<p className="min-w-[320px] pb-8 text-16 font-300 text-white-60">
|
||||
Revealing soon. Coming 2025
|
||||
</p>
|
||||
<GetNotifiedDialog>
|
||||
<Button>Get notified</Button>
|
||||
</GetNotifiedDialog>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -8,10 +8,12 @@ import { cx } from 'cva'
|
||||
const Prefooter = () => {
|
||||
return (
|
||||
<section className="relative flex flex-col overflow-hidden px-6 lg:flex-row lg:px-14 lg:pt-[200px] xl:pl-[309px]">
|
||||
<div className="relative z-20 flex flex-1 flex-col items-start pb-[120px] pt-[320px] text-left lg:min-w-[450px] lg:py-[200px]">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">keycard</p>
|
||||
<div className="relative z-20 flex flex-1 flex-col items-start pb-[120px] pt-[320px] text-left lg:py-[200px]">
|
||||
<p className="pb-2 text-24 font-600 text-orange">keycard</p>
|
||||
<h2 className="flex pb-4 font-lora text-32 font-400 text-white-95 md:text-48">
|
||||
Get the card-shaped <br /> hardware wallet
|
||||
Get the card-shaped
|
||||
<br />
|
||||
hardware wallet
|
||||
</h2>
|
||||
<p className="max-w-[435px] pb-10 text-20 font-300 text-white-80">
|
||||
Store and trade your crypto in a simple, secure and slim hardware
|
||||
@@ -22,6 +24,7 @@ const Prefooter = () => {
|
||||
<Button variant="primary">Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
<p className="text-16 font-300 text-white-60">
|
||||
{' '}
|
||||
From{' '}
|
||||
{formatPrice({
|
||||
amount: KEYCARD_PRODUCTS.ONE_CARD_SET.price,
|
||||
@@ -7,9 +7,10 @@ import { Tag } from '~components/tag'
|
||||
const SecurityFeatures = () => {
|
||||
return (
|
||||
<section className="relative mt-[100px] overflow-clip px-3 lg:mt-[227px] lg:px-0">
|
||||
<div className="relative overflow-clip rounded-28 border border-white-8 bg-white-4 px-6 md:px-0">
|
||||
<div className="relative overflow-clip rounded-28 border border-white-8 bg-white-4 px-6 md:px-2">
|
||||
<div className="flex flex-col items-center justify-between lg:flex-row">
|
||||
<div className="-mx-6 flex-1 self-start lg:mx-0">
|
||||
<div className="relative -mx-6 flex-1 self-start">
|
||||
<div className="absolute inset-0 z-10 bg-gradient-to-br from-dark-100 to-30%" />
|
||||
<Image
|
||||
src="/assets/keycard/chip.png"
|
||||
alt="Keycard security"
|
||||
@@ -18,7 +19,7 @@ const SecurityFeatures = () => {
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="py-20 pb-6 md:pb-20 md:pl-6 lg:w-1/2 lg:pl-0 lg:pr-[72px]">
|
||||
<div className="py-20 pb-6 md:pb-20 md:pl-6 lg:w-[49%] lg:pl-0 lg:pr-[70px]">
|
||||
<h2 className="pb-[52px] font-lora text-32 lg:pb-14">
|
||||
Proudly building in the open
|
||||
</h2>
|
||||
@@ -3,7 +3,6 @@ import { formatPrice } from '~/app/_utils/format-price'
|
||||
import { Button } from '~components/button'
|
||||
import { BuyKeycardDialog } from '~components/buy-keycard-dialog'
|
||||
import { Image } from '~components/image'
|
||||
import { Tooltip } from '~components/tooltip'
|
||||
import { RecommendedIcon } from '~icons/recommended'
|
||||
import { cx } from 'cva'
|
||||
import { Card } from './card'
|
||||
@@ -70,9 +69,7 @@ const UseCases = () => {
|
||||
})}
|
||||
{!!set.tag && (
|
||||
<span className="flex size-5 items-center justify-center rounded-full bg-orange">
|
||||
<Tooltip label="Best deal">
|
||||
<RecommendedIcon />
|
||||
</Tooltip>
|
||||
<RecommendedIcon />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -2,10 +2,6 @@ type Props = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function HomepageLayout({ children }: Props) {
|
||||
return (
|
||||
<div className="relative m-auto mt-20 w-full max-w-[1512px] lg:mt-[92px]">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
export default function KeycardLayout({ children }: Props) {
|
||||
return <div className="m-auto w-full max-w-[1512px]">{children}</div>
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 372 KiB |
@@ -1,11 +1,15 @@
|
||||
import { Metadata } from '~/app/_metadata'
|
||||
import { AutoOpenBuyKeycardDialog } from './_components/auto-open-buy-keycard-dialog'
|
||||
import { BuyCards } from './_components/buy-cards'
|
||||
import { ComparisonTable } from './_components/comparison-table'
|
||||
import { Customisation } from './_components/customisation'
|
||||
import { Design } from './_components/design'
|
||||
import { Faqs } from './_components/faqs'
|
||||
import { Hero } from './_components/hero'
|
||||
import { KeycardFeatures } from './_components/keycard-features'
|
||||
import { Integration } from './_components/integration'
|
||||
import { KeycardShell } from './_components/keycard-shell'
|
||||
import { Prefooter } from './_components/pre-footer'
|
||||
import { SecurityFeatures } from './_components/security-features'
|
||||
import { UseCases } from './_components/use-cases'
|
||||
|
||||
export const metadata = Metadata({
|
||||
title: {
|
||||
@@ -18,11 +22,15 @@ export default function HomePage() {
|
||||
<>
|
||||
<AutoOpenBuyKeycardDialog />
|
||||
<Hero />
|
||||
<KeycardShell />
|
||||
<KeycardFeatures />
|
||||
<SecurityFeatures />
|
||||
<Design />
|
||||
<Integration />
|
||||
<UseCases />
|
||||
<ComparisonTable />
|
||||
<KeycardShell />
|
||||
<Customisation />
|
||||
<Faqs />
|
||||
<BuyCards />
|
||||
<Prefooter />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const WALLETS = [
|
||||
{
|
||||
name: 'Metamask',
|
||||
network: 'Ethereum',
|
||||
image: '/assets/keycard-shell/wallets/metamask.png',
|
||||
},
|
||||
{
|
||||
name: 'imToken',
|
||||
network: 'Ethereum',
|
||||
image: '/assets/keycard-shell/wallets/im-token.png',
|
||||
},
|
||||
{
|
||||
name: 'Rabby',
|
||||
network: 'Ethereum',
|
||||
image: '/assets/keycard-shell/wallets/rabbit.png',
|
||||
},
|
||||
{
|
||||
name: 'Backpack',
|
||||
network: 'Ethereum',
|
||||
image: '/assets/keycard-shell/wallets/back-pack.png',
|
||||
},
|
||||
{
|
||||
name: 'UniSat',
|
||||
network: 'Bitcoin',
|
||||
image: '/assets/keycard-shell/wallets/uni-sat.png',
|
||||
},
|
||||
{
|
||||
name: 'Blue',
|
||||
network: 'Bitcoin',
|
||||
image: '/assets/keycard-shell/wallets/blue.png',
|
||||
},
|
||||
{
|
||||
name: 'Sparrow',
|
||||
network: 'Bitcoin',
|
||||
image: '/assets/keycard-shell/wallets/sparrow.png',
|
||||
},
|
||||
{
|
||||
name: 'Specter',
|
||||
network: 'Bitcoin',
|
||||
image: '/assets/keycard-shell/wallets/specter.png',
|
||||
},
|
||||
]
|
||||
|
||||
const AirGapped = () => {
|
||||
return (
|
||||
<section className="relative -order-1 grid items-center overflow-y-clip px-3 pb-[498px] pt-[120px] lg:mb-[100px] lg:grid-cols-2 lg:pb-[100px]">
|
||||
<div className="relative order-last lg:order-first">
|
||||
<div className="absolute right-1/4 h-[448px] translate-x-1/3 translate-y-14 lg:right-0 lg:h-[678px] lg:-translate-y-60 lg:translate-x-0">
|
||||
<div className="absolute bottom-1 z-10 h-20 w-full bg-gradient-to-b from-[transparent] to-dark-100 lg:bottom-20 lg:h-64" />
|
||||
|
||||
<Image
|
||||
alt="Hardware wallet device with QR display"
|
||||
height={800}
|
||||
width={1600}
|
||||
className="w-[800px] max-w-[800px] lg:w-[1000px] lg:max-w-[1000px]"
|
||||
src="/assets/keycard-shell/keycard-with-macbook.png"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col justify-self-start lg:max-w-[549px] lg:pl-3 lg:pt-20">
|
||||
<div>
|
||||
<h2 className="mb-2 font-lora text-32">Fully airgapped</h2>
|
||||
<p className="text-16 font-300 text-white-60">
|
||||
Sign transactions on your favourite wallets through QR signing
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 lg:mt-14">
|
||||
<div className="mb-5 text-12 text-white-80">
|
||||
TRANSACTION SIGNING WITH
|
||||
</div>
|
||||
<div className="grid grid-cols-2">
|
||||
{WALLETS.map((wallet, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center space-x-3 border-b border-dashed border-white-20 py-3 even:border-l even:border-white-20 even:pl-3 [&:nth-child(-n+2)]:pt-0 [&:nth-last-child(-n+2)]:border-b-0 [&:nth-last-child(-n+2)]:pb-0"
|
||||
>
|
||||
<div className="flex size-10 items-center justify-center rounded-28 bg-dark-60">
|
||||
<Image
|
||||
src={wallet.image}
|
||||
alt={`${wallet.name} icon`}
|
||||
height={40}
|
||||
width={40}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-500">{wallet.name}</div>
|
||||
<div className="text-16 text-white-80">{wallet.network}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { AirGapped }
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const Comparision = () => {
|
||||
return (
|
||||
<section className="mt-[120px] grid gap-0 overflow-clip rounded-28 border border-white-12 lg:mt-[200px] lg:grid-cols-2">
|
||||
<div className="relative flex min-h-[480px] flex-col items-center justify-center lg:min-h-[840px]">
|
||||
<Image
|
||||
alt="Keycard Shell"
|
||||
className="absolute left-0 top-0 z-0 size-full object-cover"
|
||||
src="/assets/keycard-shell/comparision-left.png"
|
||||
width={1480}
|
||||
height={1680}
|
||||
/>
|
||||
|
||||
<h2 className="absolute left-1/2 top-1/2 z-20 mx-auto w-full max-w-[328px] -translate-x-1/2 -translate-y-20 text-center font-lora text-32 lg:max-w-full">
|
||||
Keep your device at home
|
||||
</h2>
|
||||
<p className="absolute bottom-8 mx-auto max-w-[423px] px-2 text-center text-16 font-300 text-white-80 lg:bottom-14">
|
||||
Stored away on a drawer, or hiding in plain sight, Keycard Shell is
|
||||
stateless without a Keycard.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative flex min-h-[480px] flex-col items-center justify-center border-t border-white-12 md:border-l md:border-t-0 lg:min-h-[840px]">
|
||||
<Image
|
||||
alt="Keycard"
|
||||
className="absolute left-0 top-0 size-full object-cover"
|
||||
src="/assets/keycard-shell/comparision-right.png"
|
||||
width={1480}
|
||||
height={1680}
|
||||
/>
|
||||
|
||||
<h2 className="absolute left-1/2 top-1/2 z-20 mx-auto w-full max-w-[328px] -translate-x-1/2 -translate-y-20 text-center font-lora text-32 lg:max-w-full">
|
||||
Take your keys with you
|
||||
</h2>
|
||||
<p className="absolute bottom-8 mx-auto max-w-[434px] px-2 text-center text-16 font-300 text-white-80 lg:bottom-14">
|
||||
Discreet and light, your Keycard fits perfectly in your wallet ready
|
||||
to go, everywhere!
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { Comparision }
|
||||
@@ -1,77 +0,0 @@
|
||||
import { FirmwareIcon } from '@status-im/icons/20'
|
||||
import { GithubIcon } from '@status-im/icons/social'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { Tag } from '~components/tag'
|
||||
|
||||
const FEATURES = [
|
||||
{
|
||||
name: 'Unparalleled security',
|
||||
description: `Keycard's secure element has passed Common Criteria EAL6+ certification.`,
|
||||
badge: 'EAL 6+',
|
||||
},
|
||||
{
|
||||
name: 'Stateless',
|
||||
description:
|
||||
'Your information and private keys are never stored on the device.',
|
||||
tags: ['Keys stored on Keycard'],
|
||||
},
|
||||
{
|
||||
name: 'Clear security model',
|
||||
description: `Your private keys will never leave Keycard's secure Element.`,
|
||||
tags: ['bip-32', 'bip-39', 'bip-44'],
|
||||
},
|
||||
]
|
||||
|
||||
const FeaturesCard = () => {
|
||||
return (
|
||||
<section className="mx-9 px-3 pt-[144px] lg:pt-[48px]">
|
||||
<div className="grid grid-cols-1 gap-8 rounded-28 border border-white-8 bg-white-4 p-6 pt-5 text-white-95 lg:grid-cols-2 lg:gap-6 lg:p-8 lg:pt-7 xl:grid-cols-4">
|
||||
{FEATURES.map((feature, index) => (
|
||||
<div key={index} className="flex flex-col">
|
||||
<h3 className="pb-[6px] font-lora text-24 font-400">
|
||||
{feature.name}
|
||||
</h3>
|
||||
|
||||
<p className="pb-6 text-16 font-300 text-white-60">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{Boolean(feature.badge) && (
|
||||
<Tag gradient icon={<FirmwareIcon />}>
|
||||
{feature.badge}
|
||||
</Tag>
|
||||
)}
|
||||
{feature.tags && (
|
||||
<div className="flex gap-2">
|
||||
{feature.tags.map((tag, index) => (
|
||||
<Tag key={index}>{tag}</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-col gap-6">
|
||||
<div>
|
||||
<h2 className="pb-[6px] font-lora text-24 font-400 text-white-95">
|
||||
Fully open source
|
||||
</h2>
|
||||
<p className="text-16 font-300 text-white-60">
|
||||
Our software, hardware and construction are fully open source.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ButtonLink
|
||||
href="https://github.com/keycard-tech/keycard-pro"
|
||||
className="font-500 [&_path]:hover:fill-white-dark"
|
||||
variant="white"
|
||||
icon={<GithubIcon />}
|
||||
>
|
||||
View on Github
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { FeaturesCard }
|
||||
@@ -1,145 +0,0 @@
|
||||
import { Image } from '~components/image'
|
||||
import { Tag } from '~components/tag'
|
||||
import { Tooltip } from '~components/tooltip'
|
||||
import { cx } from 'cva'
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Runs for ever',
|
||||
description:
|
||||
'With the use of a very common replaceable Nokia BL-4C battery.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-1.png',
|
||||
badge: { text: '18 hours+' },
|
||||
className: 'row-span-2 col-span-1 [&_img]:w-fit gap-0',
|
||||
},
|
||||
{
|
||||
title: 'Ethereum support',
|
||||
description: 'Works out of the box with your favourite Ethereum wallets.',
|
||||
className: 'col-span-1',
|
||||
icons: [
|
||||
{
|
||||
name: 'Metamask',
|
||||
image: '/assets/keycard-shell/wallets/metamask.png',
|
||||
},
|
||||
{
|
||||
name: 'Rabby',
|
||||
image: '/assets/keycard-shell/wallets/rabbit.png',
|
||||
},
|
||||
{
|
||||
name: 'imToken',
|
||||
image: '/assets/keycard-shell/wallets/im-token.png',
|
||||
},
|
||||
{
|
||||
name: 'Backpack',
|
||||
image: '/assets/keycard-shell/wallets/back-pack.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Large display',
|
||||
description:
|
||||
'Verify the correctness of transaction data on the secure device.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-2.png',
|
||||
badge: { text: '2 inches' },
|
||||
className: 'row-span-2 col-span-1 flex-col-reverse gap-0 [&_img]:w-fit',
|
||||
},
|
||||
{
|
||||
title: 'Air-gapped',
|
||||
description:
|
||||
'Send transactions through the air via QR code standard ERC-4527.',
|
||||
badge: { text: 'ERC-4527' },
|
||||
className: 'col-span-1',
|
||||
},
|
||||
{
|
||||
title: 'Easy to backup',
|
||||
description:
|
||||
'Use multiple cards as backups, each protected by a unique PIN.',
|
||||
badge: { text: '2 cards included with device' },
|
||||
className: 'col-span-1',
|
||||
},
|
||||
{
|
||||
title: 'Bitcoin support',
|
||||
description:
|
||||
'Works out of the box with your favourite Bitcoin wallets.',
|
||||
className: 'col-span-1',
|
||||
icons: [
|
||||
{
|
||||
name: 'Unisat',
|
||||
image: '/assets/keycard-shell/wallets/uni-sat.png',
|
||||
},
|
||||
{
|
||||
name: 'Sparrow',
|
||||
image: '/assets/keycard-shell/wallets/sparrow.png',
|
||||
},
|
||||
{
|
||||
name: 'Specter',
|
||||
image: '/assets/keycard-shell/wallets/specter.png',
|
||||
},
|
||||
{
|
||||
name: 'Blue',
|
||||
image: '/assets/keycard-shell/wallets/blue.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const FeaturesGrid = () => {
|
||||
return (
|
||||
<section className="max-w-[1352px] px-3 pt-[120px] lg:mx-auto lg:pt-[200px] min-[1512px]:px-0">
|
||||
<h2 className="max-w-[665px] font-lora text-32 text-white-95">
|
||||
Convenient, secure and made to last
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-5 pt-14 lg:grid-cols-4 lg:gap-6">
|
||||
{features.map((feature, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={cx([
|
||||
'relative flex flex-col justify-between overflow-clip rounded-28 bg-white-4 outline outline-1 outline-white-8',
|
||||
feature.className,
|
||||
])}
|
||||
>
|
||||
{feature.image && (
|
||||
<Image
|
||||
src={feature.image}
|
||||
alt={feature.title}
|
||||
className="w-full rounded-12"
|
||||
width={640}
|
||||
height={640}
|
||||
/>
|
||||
)}
|
||||
<div className="p-6 pt-5">
|
||||
<div>
|
||||
<h3 className="mb-[6px] font-lora text-24 font-400">
|
||||
{feature.title}
|
||||
</h3>
|
||||
|
||||
<p className="mb-6 text-16 font-300 text-white-60">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{feature.badge && <Tag>{feature.badge.text}</Tag>}
|
||||
{feature.icons && (
|
||||
<div className="flex items-center gap-3">
|
||||
{feature.icons?.map((icon, index) => (
|
||||
<Tooltip key={index} label={icon.name}>
|
||||
<Image
|
||||
src={icon.image}
|
||||
alt={icon.name}
|
||||
width={40}
|
||||
height={40}
|
||||
className="transition-opacity duration-200 hover:opacity-[80%]"
|
||||
/>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { FeaturesGrid }
|
||||
@@ -1,144 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from '@status-im/icons/20'
|
||||
import { Image } from '~components/image'
|
||||
import { EmblaOptionsType } from 'embla-carousel'
|
||||
import useEmblaCarousel from 'embla-carousel-react'
|
||||
import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Full visibility of seed phrase',
|
||||
description:
|
||||
'All the words clearly numbered and visible ready for you to write them down.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-card-1.png',
|
||||
},
|
||||
{
|
||||
name: 'Human readable tx data',
|
||||
description: 'Get a clear understanding of what you’re actually signing.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-card-2.png',
|
||||
},
|
||||
{
|
||||
name: 'Secure PIN for each card',
|
||||
description:
|
||||
'Switch your card, enter your pin, and you’re ready to sign a transaction.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-card-3.png',
|
||||
},
|
||||
{
|
||||
name: 'Compatible QR wallets',
|
||||
description:
|
||||
'Its embedded camera allows Keycard Shell to be used fully airgapped.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-card-4.png',
|
||||
},
|
||||
{
|
||||
name: 'Block USB data transfer',
|
||||
description:
|
||||
'Add an extra layer of safety every time you connect your cable to charge.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-card-5.png',
|
||||
},
|
||||
{
|
||||
name: 'Name your cards',
|
||||
description: 'Easily identify your cards by giving them a unique name.',
|
||||
image: '/assets/keycard-shell/keycard-shell-feature-card-6.png',
|
||||
},
|
||||
]
|
||||
|
||||
const FeaturesSlider = () => {
|
||||
const options: EmblaOptionsType = {
|
||||
align: 'start',
|
||||
containScroll: 'trimSnaps',
|
||||
dragFree: false,
|
||||
}
|
||||
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel(options, [
|
||||
WheelGesturesPlugin(),
|
||||
])
|
||||
const [prevBtnEnabled, setPrevBtnEnabled] = useState(false)
|
||||
const [nextBtnEnabled, setNextBtnEnabled] = useState(false)
|
||||
|
||||
const scrollPrev = useCallback(
|
||||
() => emblaApi && emblaApi.scrollPrev(),
|
||||
[emblaApi],
|
||||
)
|
||||
const scrollNext = useCallback(
|
||||
() => emblaApi && emblaApi.scrollNext(),
|
||||
[emblaApi],
|
||||
)
|
||||
|
||||
const onSelect = useCallback(() => {
|
||||
if (!emblaApi) return
|
||||
setPrevBtnEnabled(emblaApi.canScrollPrev())
|
||||
setNextBtnEnabled(emblaApi.canScrollNext())
|
||||
}, [emblaApi])
|
||||
|
||||
useEffect(() => {
|
||||
if (!emblaApi) return
|
||||
onSelect()
|
||||
emblaApi.on('select', onSelect)
|
||||
emblaApi.on('reInit', onSelect)
|
||||
|
||||
return () => {
|
||||
emblaApi.off('select', onSelect)
|
||||
emblaApi.off('reInit', onSelect)
|
||||
}
|
||||
}, [emblaApi, onSelect])
|
||||
|
||||
return (
|
||||
<div className="relative w-full pt-[130px] lg:pt-[200px]">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="max-w-[665px] px-3 font-lora text-32 text-white-95 lg:px-20">
|
||||
Running on open source software{' '}
|
||||
</h2>
|
||||
<div className="hidden items-center gap-3 pr-20 lg:flex">
|
||||
<button
|
||||
className="rounded-12 border border-white-12 bg-white-8 p-[10px] text-white-95 opacity-[100%] transition-all hover:bg-white-12 disabled:opacity-[40%]"
|
||||
disabled={!prevBtnEnabled}
|
||||
onClick={scrollPrev}
|
||||
aria-label="Previous item"
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
</button>
|
||||
<button
|
||||
className="rounded-12 border border-white-12 bg-white-8 p-[10px] text-white-95 opacity-[100%] transition-all hover:bg-white-12 disabled:opacity-[40%]"
|
||||
disabled={!nextBtnEnabled}
|
||||
onClick={scrollNext}
|
||||
aria-label="Next item"
|
||||
>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative w-full px-3 pt-14 lg:px-20" ref={emblaRef}>
|
||||
<div className="flex justify-start gap-6">
|
||||
{features.map((feature, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="min-w-0 flex-[0_0_90%] select-none sm:flex-[0_0_320px]"
|
||||
>
|
||||
<div className="rounded-28 border border-white-8 bg-white-4 p-10">
|
||||
<Image
|
||||
src={feature.image}
|
||||
alt={feature.name}
|
||||
width={500}
|
||||
height={500}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-8">
|
||||
<p className="pb-[6px] text-left font-lora text-24 font-500 text-white-95">
|
||||
{feature.name}
|
||||
</p>
|
||||
<p className="text-16 font-300 text-white-60">
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { FeaturesSlider }
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Button } from '~components/button'
|
||||
import { GetNotifiedDialog } from '~components/get-notified-dialog'
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const Hero = () => {
|
||||
return (
|
||||
<section className="relative flex h-svh overflow-y-clip full-view-port lg:remove-full-view-port 2xl:h-[839px]">
|
||||
<Image
|
||||
alt="Keycard Shell"
|
||||
src="/assets/keycard-shell/bg-hero-keycard-shell.png"
|
||||
width={986}
|
||||
height={986}
|
||||
priority
|
||||
className="absolute left-10 top-[43%] z-0 h-auto w-fit -translate-y-1/2 scale-[120%] bg-contain bg-top bg-no-repeat md:scale-100 lg:left-[56%] lg:-translate-x-1/2"
|
||||
/>
|
||||
<div className="pointer-events-none relative z-20 flex flex-1 flex-col justify-end px-5 pb-5 align-bottom lg:pb-20 xl:px-20">
|
||||
<div className="flex flex-col items-start justify-between lg:flex-row">
|
||||
<div className="pointer-events-auto flex max-w-[664px] flex-col lg:self-end">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">
|
||||
keycard <span className="font-200">shell</span>
|
||||
</p>
|
||||
<h1 className="flex font-lora text-32 font-400 text-white-95 lg:text-44">
|
||||
One secure device,
|
||||
<br />
|
||||
infinite backups
|
||||
</h1>
|
||||
<p className="max-w-[435px] pb-8 pt-4 text-20 font-300 text-white-80">
|
||||
Unrivalled security with an infinite number of removable Keycards,
|
||||
each with their own key.
|
||||
</p>
|
||||
<div className="flex flex-col items-start gap-5 md:flex-row md:items-center">
|
||||
<GetNotifiedDialog>
|
||||
<Button variant="primary">Get notified</Button>
|
||||
</GetNotifiedDialog>
|
||||
<p className="flex items-center gap-2 text-16 font-300 text-white-60">
|
||||
Coming 2025 <span className="size-1 rounded-full bg-white-40" />{' '}
|
||||
Bundled with Keycard
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-x-0 bottom-0 z-10 h-1/2 bg-gradient-to-t from-[#010101] to-[transparent] lg:h-1/3" />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { Hero }
|
||||
@@ -1,37 +0,0 @@
|
||||
import { Button } from '~components/button'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { BuyKeycardDialog } from '~components/buy-keycard-dialog'
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const Keycards = () => {
|
||||
return (
|
||||
<section className="relative mx-auto mt-[120px] min-h-[490px] max-w-[1352px] overflow-clip rounded-28 border border-white-12 lg:mt-0 lg:min-h-[840px]">
|
||||
<Image
|
||||
src="/assets/keycard-shell/bg-keycards.png"
|
||||
alt="Keycards scattered around"
|
||||
className="absolute left-0 top-0 size-full object-cover"
|
||||
width={2704}
|
||||
height={1680}
|
||||
/>
|
||||
|
||||
<div className="relative z-10 pt-12 text-center lg:pt-20">
|
||||
<p className="font-lora text-32 text-white-95">
|
||||
Many use cases,
|
||||
<br />
|
||||
multiple Keycards.
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-center gap-4 pt-8">
|
||||
<BuyKeycardDialog>
|
||||
<Button variant="primary">Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
<ButtonLink href="/keycard" variant="secondary">
|
||||
Learn more
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { Keycards }
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Button } from '~components/button'
|
||||
import { GetNotifiedDialog } from '~components/get-notified-dialog'
|
||||
import { Image } from '~components/image'
|
||||
import { cx } from 'cva'
|
||||
|
||||
const Prefooter = () => {
|
||||
return (
|
||||
<section className="relative mb-2 mt-28 flex flex-col px-5 pb-20 pt-0 full-view-port lg:my-0 lg:mb-[160px] lg:flex-row lg:pb-0 lg:pt-[100px] lg:remove-full-view-port xl:gap-8 2xl:gap-16">
|
||||
<div className="relative z-20 order-2 flex flex-col items-start pt-[365px] text-left lg:order-1 lg:pt-[100px] xl:pl-[290px]">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">
|
||||
keycard <span className="font-200">shell</span>
|
||||
</p>
|
||||
<h2 className="flex pb-4 font-lora text-32 font-400 text-white-95 md:text-48">
|
||||
One device for all
|
||||
<br /> your Keycards
|
||||
</h2>
|
||||
<p className="max-w-[435px] pb-10 text-20 font-300 text-white-80">
|
||||
Unrivalled security with an infinite number of removable Keycards,
|
||||
each with their own key.
|
||||
</p>
|
||||
<div className="flex flex-col items-start gap-5 sm:min-w-[410px] sm:flex-row sm:items-center">
|
||||
<GetNotifiedDialog>
|
||||
<Button variant="primary">Get notified</Button>
|
||||
</GetNotifiedDialog>
|
||||
<p className="flex items-center gap-2 text-16 font-300 text-white-60 md:gap-2">
|
||||
Coming 2025 <span className="size-1 rounded-full bg-white-40" />{' '}
|
||||
Bundled with Keycard
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-1 flex lg:right-[-20px] lg:order-2">
|
||||
<div className="absolute z-20 block h-[360px] w-full bg-gradient-to-b from-dark-100 to-[transparent] lg:h-[220px]" />
|
||||
<Image
|
||||
src="/assets/faqs.png"
|
||||
alt="Keycard Shell"
|
||||
width={791}
|
||||
height={521}
|
||||
draggable={false}
|
||||
className={cx([
|
||||
'pointer-events-none absolute left-[80%] top-0 z-0 h-auto max-w-[549px] -translate-x-1/2 select-none md:left-1/2 lg:relative lg:left-1/4 lg:top-12 lg:translate-x-0',
|
||||
'aspect-[791/521] object-contain lg:scale-125',
|
||||
])}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { Prefooter }
|
||||
@@ -1,7 +0,0 @@
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function KeycardShellLayout({ children }: Props) {
|
||||
return <div className="m-auto w-full max-w-[1512px]">{children}</div>
|
||||
}
|
||||
|
Before Width: | Height: | Size: 322 KiB |
@@ -1,30 +0,0 @@
|
||||
import { Metadata } from '~/app/_metadata'
|
||||
import { AirGapped } from './_components/air-gapped'
|
||||
import { Comparision } from './_components/comparision'
|
||||
import { FeaturesCard } from './_components/features-card'
|
||||
import { FeaturesGrid } from './_components/features-grid'
|
||||
import { FeaturesSlider } from './_components/features-slider'
|
||||
import { Hero } from './_components/hero'
|
||||
import { Keycards } from './_components/keycards'
|
||||
import { Prefooter } from './_components/pre-footer'
|
||||
|
||||
export const metadata = Metadata({
|
||||
title: 'Keycard Shell',
|
||||
description:
|
||||
'A modular hardware wallet that relies on Keycard as the secure element.',
|
||||
})
|
||||
|
||||
export default async function KeycardShellPage() {
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<FeaturesCard />
|
||||
<FeaturesGrid />
|
||||
<FeaturesSlider />
|
||||
<Comparision />
|
||||
<AirGapped />
|
||||
<Keycards />
|
||||
<Prefooter />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { KEYCARD_PRODUCTS } from '~/app/_constants/shopify/products'
|
||||
import { formatPrice } from '~/app/_utils/format-price'
|
||||
import { Background } from '~components/3d/background'
|
||||
import { Button } from '~components/button'
|
||||
import { BuyKeycardDialog } from '~components/buy-keycard-dialog'
|
||||
|
||||
const Hero = () => {
|
||||
return (
|
||||
<section className="relative flex h-svh overflow-y-clip full-view-port lg:remove-full-view-port 2xl:h-[839px]">
|
||||
<Background variant="homepage" />
|
||||
<div className="pointer-events-none relative z-20 flex flex-1 flex-col justify-end px-5 pb-5 align-bottom lg:pb-[43px] xl:px-20">
|
||||
<div className="flex flex-col items-start justify-between lg:flex-row">
|
||||
<div className="pointer-events-auto flex max-w-[664px] flex-col lg:self-end">
|
||||
<p className="pb-2 text-24 font-600 text-white-95">keycard</p>
|
||||
<h1 className="flex font-lora text-32 font-400 text-white-95 lg:text-44">
|
||||
Lightweight design, <br /> heavyweight security
|
||||
</h1>
|
||||
<p className="max-w-[435px] pb-8 pt-4 text-20 font-300 text-white-80">
|
||||
Trade swiftly and safely back up your assets with Keycard hardware
|
||||
wallets.
|
||||
</p>
|
||||
<div className="flex items-center gap-5">
|
||||
<BuyKeycardDialog>
|
||||
<Button variant="primary">Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
<p className="text-16 font-300 text-white-60">
|
||||
From{' '}
|
||||
{formatPrice({
|
||||
amount: KEYCARD_PRODUCTS.ONE_CARD_SET.price,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { Hero }
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Button } from '~components/button'
|
||||
import { ButtonLink } from '~components/button-link'
|
||||
import { GetNotifiedDialog } from '~components/get-notified-dialog'
|
||||
import { Image } from '~components/image'
|
||||
|
||||
const KeycardShell = () => {
|
||||
return (
|
||||
<section className="relative mx-0 mt-[160px] flex items-center overflow-hidden rounded-28 border border-white-12 lg:mx-6 lg:mt-[200px] xl:mx-20">
|
||||
<Image
|
||||
src="/assets/keycard/bg-keycard-shell.png"
|
||||
alt="Keycard Shell Hardware Wallet"
|
||||
width={1400}
|
||||
height={800}
|
||||
className="absolute left-0 top-0 hidden size-full object-cover md:block"
|
||||
/>
|
||||
|
||||
<Image
|
||||
src="/assets/keycard/bg-keycard-shell-mobile.png"
|
||||
alt="Keycard Shell Hardware Wallet"
|
||||
width={828}
|
||||
height={1452}
|
||||
className="absolute left-0 top-0 block size-full scale-[101%] object-cover md:hidden"
|
||||
/>
|
||||
|
||||
<div className="relative z-10 flex flex-col p-6 pt-[400px] lg:py-[200px] lg:pl-[229px]">
|
||||
<p className="pb-3 text-24 font-600 text-white-95">
|
||||
keycard <span className="font-200">shell</span>
|
||||
</p>
|
||||
<p className="flex pb-2 font-lora text-32 font-400">
|
||||
Multiple Keycards.
|
||||
<br />
|
||||
One device.
|
||||
</p>
|
||||
<p className="max-w-[320px] pb-6 text-16 font-300 text-white-80">
|
||||
Use the same device for all your Keycards. It’s airgapped, stateless
|
||||
and modular.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<GetNotifiedDialog>
|
||||
<Button>Get notified</Button>
|
||||
</GetNotifiedDialog>
|
||||
<ButtonLink href="/keycard-shell" variant="secondary">
|
||||
Learn more
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<p className="flex items-center gap-2 pt-6 text-16 font-300 text-white-60 md:gap-2">
|
||||
Coming 2025 <span className="size-1 rounded-full bg-white-40" />{' '}
|
||||
Bundled with Keycard
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export { KeycardShell }
|
||||
@@ -1,7 +0,0 @@
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function KeycardLayout({ children }: Props) {
|
||||
return <div className="m-auto w-full max-w-[1512px]">{children}</div>
|
||||
}
|
||||
|
Before Width: | Height: | Size: 173 KiB |
@@ -1,30 +0,0 @@
|
||||
import { Metadata } from '~/app/_metadata'
|
||||
import { Customisation } from './_components/customisation'
|
||||
import { Design } from './_components/design'
|
||||
import { Hero } from './_components/hero'
|
||||
import { Integration } from './_components/integration'
|
||||
import { KeycardShell } from './_components/keycard-shell'
|
||||
import { Prefooter } from './_components/pre-footer'
|
||||
import { SecurityFeatures } from './_components/security-features'
|
||||
import { UseCases } from './_components/use-cases'
|
||||
|
||||
export const metadata = Metadata({
|
||||
title: 'Keycard',
|
||||
description:
|
||||
'Store and trade your crypto with a simple, secure and slim hardware wallet.',
|
||||
})
|
||||
|
||||
export default async function KeycardPage() {
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<SecurityFeatures />
|
||||
<Design />
|
||||
<Integration />
|
||||
<UseCases />
|
||||
<KeycardShell />
|
||||
<Customisation />
|
||||
<Prefooter />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export default function StartPage() {
|
||||
Learn how to create and store your keys directly on Keycard
|
||||
</p>
|
||||
<ButtonLink
|
||||
href="https://status.app/help/keycard/create-a-status-profile-using-keycard"
|
||||
href="https://status.app/help/profile/create-a-status-profile-using-keycard"
|
||||
variant="secondary"
|
||||
icon={<ExternalIcon />}
|
||||
className="mt-auto"
|
||||
@@ -63,7 +63,7 @@ export default function StartPage() {
|
||||
Learn how to move your keys to Keycard
|
||||
</p>
|
||||
<ButtonLink
|
||||
href="https://status.app/help/keycard/secure-your-status-profile-or-wallet-with-keycard"
|
||||
href="https://status.app/help/profile/secure-your-status-profile-or-wallet-with-keycard"
|
||||
variant="secondary"
|
||||
icon={<ExternalIcon />}
|
||||
className="mt-auto"
|
||||
|
||||
@@ -164,12 +164,14 @@ const BackgroundWebGL = (props: Props) => {
|
||||
return (
|
||||
<div className="absolute size-full">
|
||||
<motion.div
|
||||
animate={{ opacity: isMounted ? 0 : 1 }}
|
||||
animate={{
|
||||
opacity: isMounted ? 0 : 1,
|
||||
}}
|
||||
transition={{
|
||||
duration: 0.3,
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
className="absolute left-0 top-0 z-0 size-full translate-y-[20px]"
|
||||
className="absolute left-0 top-0 z-0 size-full translate-y-px scale-[0.99]"
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
@@ -181,14 +183,13 @@ const BackgroundWebGL = (props: Props) => {
|
||||
alt="Keycard Placeholder"
|
||||
width={5478}
|
||||
height={2166}
|
||||
className="aspect-[5478/2166] h-[74.5%] w-full object-cover blur-2xl"
|
||||
className="aspect-[5478/2166] h-[74.5%] w-full object-cover"
|
||||
/>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
ref={wrapperRef}
|
||||
animate={{
|
||||
opacity: isMounted ? 1 : 0,
|
||||
y: isMounted ? 0 : 20,
|
||||
}}
|
||||
transition={{
|
||||
duration: 0.3,
|
||||
@@ -243,7 +244,7 @@ const BackgroundWebGL = (props: Props) => {
|
||||
const BackgroundImage = (props: Props) => {
|
||||
const { variant } = props
|
||||
return (
|
||||
<div className="absolute size-full translate-y-[20px]">
|
||||
<div className="absolute size-full">
|
||||
<Image
|
||||
src={
|
||||
variant === 'homepage'
|
||||
@@ -253,7 +254,7 @@ const BackgroundImage = (props: Props) => {
|
||||
alt="Keycard"
|
||||
width={5478}
|
||||
height={2166}
|
||||
className="aspect-[5478/2166] h-[74.5%] w-full object-cover"
|
||||
className="aspect-[5478/2166] h-[74.5%] w-full translate-y-[2px] scale-[0.99] object-cover"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,11 +14,11 @@ const Footer = () => {
|
||||
</Link>
|
||||
</div>
|
||||
<div className="m-auto max-w-[1512px]">
|
||||
<div className="grid grid-cols-2 divide-x divide-dashed divide-white-20 lg:grid-cols-5 lg:pl-[152px] lg:pr-0 xl:pl-[282px]">
|
||||
<div className="grid grid-cols-2 divide-x divide-dashed divide-white-20 lg:grid-cols-5 lg:pl-[152px] lg:pr-[72px] xl:pl-[282px]">
|
||||
<div
|
||||
key="empty"
|
||||
title="empty"
|
||||
className="col-span-2 h-[72px] border-b border-dashed border-white-20 lg:col-span-1 lg:border-0"
|
||||
className="border-b border-dashed border-white-20 lg:hidden"
|
||||
/>
|
||||
|
||||
{Object.entries(ROUTES).map(([title, links]) => (
|
||||
|
||||
@@ -11,7 +11,7 @@ const Section = (props: Props) => {
|
||||
const { title, routes } = props
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col gap-6 border-b border-dashed border-white-20 p-6 lg:border-0 lg:pb-12 lg:pt-0 lg:[&:nth-child(1)]:col-start-2">
|
||||
<div className="relative flex flex-col gap-6 border-b border-dashed border-white-20 p-6 lg:border-0 lg:pb-12 lg:pt-0 [&:nth-child(2)]:pt-0 lg:[&:nth-child(2)]:col-start-3">
|
||||
<p className="text-12 uppercase text-white-80">{title}</p>
|
||||
<ul className="grid gap-2">
|
||||
{routes.map(route => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { Logo } from '../logo'
|
||||
import { MenuIcon } from './menu-icon'
|
||||
// import { MenuIcon } from './menu-icon'
|
||||
import { Section } from './section'
|
||||
|
||||
const NAV_BAR_HEIGHT = 80
|
||||
@@ -105,14 +105,14 @@ const NavBarMobile = () => {
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<button
|
||||
{/* <button
|
||||
key={isOpen ? 'menu-open' : 'menu-closed'}
|
||||
className="rounded-12 border border-white-12 bg-white-8 text-white-95 transition-colors hover:bg-white-12"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<MenuIcon isOpen={isOpen} />
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
@@ -140,7 +140,7 @@ const NavBarMobile = () => {
|
||||
className="flex h-full flex-col items-center justify-center pt-[200px]"
|
||||
>
|
||||
<ul className="flex flex-1 flex-col gap-4">
|
||||
{ROUTES.Products.map((item, index) => (
|
||||
{/* {ROUTES.Products.map((item, index) => (
|
||||
<motion.li
|
||||
key={item.name}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@@ -152,7 +152,7 @@ const NavBarMobile = () => {
|
||||
{item.name}
|
||||
</Link>
|
||||
</motion.li>
|
||||
))}
|
||||
))} */}
|
||||
<motion.li
|
||||
key="button-keycard"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import { cva } from 'cva'
|
||||
import {
|
||||
motion,
|
||||
useMotionValueEvent,
|
||||
@@ -8,7 +7,6 @@ import {
|
||||
useTransform,
|
||||
} from 'framer-motion'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
import { Button } from './button'
|
||||
import { BuyKeycardDialog } from './buy-keycard-dialog'
|
||||
@@ -16,26 +14,7 @@ import { Logo } from './logo'
|
||||
|
||||
const NAV_BAR_HEIGHT = 92
|
||||
|
||||
const internalLinkStyles = cva({
|
||||
base: 'rounded-12 border border-[transparent] px-[14px] py-[7px] transition-colors hover:bg-white-8',
|
||||
variants: {
|
||||
isActive: {
|
||||
true: 'bg-white-12',
|
||||
false: '',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const links = [
|
||||
{ href: '/keycard', label: 'Keycard' },
|
||||
{
|
||||
href: '/keycard-shell',
|
||||
label: 'Keycard Shell',
|
||||
},
|
||||
]
|
||||
|
||||
const NavBar = () => {
|
||||
const pathname = usePathname()
|
||||
const [variant, setVariant] = useState<'primary' | 'secondary'>('secondary')
|
||||
|
||||
const { scrollY } = useScroll()
|
||||
@@ -71,21 +50,9 @@ const NavBar = () => {
|
||||
<Logo />
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
{links.map(({ href, label }) => (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={internalLinkStyles({ isActive: pathname === href })}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
<BuyKeycardDialog>
|
||||
<Button variant={variant}>Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
</div>
|
||||
<BuyKeycardDialog>
|
||||
<Button variant={variant}>Buy Keycard</Button>
|
||||
</BuyKeycardDialog>
|
||||
</motion.nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export const ROUTES = {
|
||||
Products: [
|
||||
{ name: 'Keycard', href: '/keycard' },
|
||||
{ name: 'Keycard Shell', href: '/keycard-shell' },
|
||||
],
|
||||
// Products: [
|
||||
// { name: 'Keycard', href: '/keycard' },
|
||||
// { name: 'Keycard Shell', href: '/keycard-shell' },
|
||||
// ],
|
||||
Info: [
|
||||
{ name: 'Get started', href: '/start' },
|
||||
{ name: 'Documentation', href: '/docs/overview' },
|
||||
|
||||