@carrierllc/mcp 0.2.19 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/dist/chunk-ZII4CD4U.js +461 -0
- package/dist/chunk-ZII4CD4U.js.map +1 -0
- package/dist/cli.js +1294 -105
- package/dist/cli.js.map +1 -1
- package/dist/index.js +144 -315
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugin/carrier/commands/storefront.md +24 -12
- package/plugin/carrier/skills/carrier-operations/SKILL.md +4 -1
- package/templates/storefront/{next.config.ts → next.config.mjs} +2 -6
- package/templates/storefront/public/brand/logo.svg +13 -0
- package/templates/storefront/src/app/activate/[orderId]/ActivateClient.tsx +12 -12
- package/templates/storefront/src/app/checkout/[templateId]/CheckoutClient.tsx +5 -5
- package/templates/storefront/src/app/checkout/success/CheckoutSuccessClient.tsx +29 -29
- package/templates/storefront/src/app/checkout/success/page.tsx +1 -1
- package/templates/storefront/src/app/contact/page.tsx +33 -25
- package/templates/storefront/src/app/dashboard/page.tsx +7 -4
- package/templates/storefront/src/app/globals.css +72 -82
- package/templates/storefront/src/app/help/page.tsx +22 -17
- package/templates/storefront/src/app/layout.tsx +15 -4
- package/templates/storefront/src/app/page.tsx +16 -7
- package/templates/storefront/src/app/shop/ShopClient.tsx +7 -3
- package/templates/storefront/src/app/sign-in/[[...sign-in]]/page.tsx +1 -1
- package/templates/storefront/src/app/sign-up/[[...sign-up]]/StorefrontSignUpClient.tsx +16 -16
- package/templates/storefront/src/app/sign-up/[[...sign-up]]/page.tsx +1 -1
- package/templates/storefront/src/brand.config.ts +3 -3
- package/templates/storefront/src/components/faq/FaqSection.tsx +23 -26
- package/templates/storefront/src/components/footer/StorefrontFooter.tsx +37 -22
- package/templates/storefront/src/components/landing/FinalCTA.tsx +28 -0
- package/templates/storefront/src/components/landing/HeroSection.tsx +57 -24
- package/templates/storefront/src/components/landing/HowItWorks.tsx +44 -0
- package/templates/storefront/src/components/landing/PlanCard.tsx +58 -28
- package/templates/storefront/src/components/nav/StorefrontNav.tsx +59 -24
- package/templates/storefront/src/components/theme/BrandStyles.tsx +8 -0
- package/templates/storefront/src/components/theme/ThemeToggle.tsx +27 -0
- package/templates/storefront/src/components/theme/clerk-appearance.ts +33 -35
- package/templates/storefront/src/components/theme/theme-provider.tsx +25 -32
- package/templates/storefront/src/components/theme/theme-script.tsx +4 -7
- package/templates/storefront/src/lib/complete-email-sign-up.ts +3 -3
- package/templates/storefront/src/vendor/ui/brand.ts +6 -6
|
@@ -1,99 +1,89 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
-
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
3
|
+
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|
|
4
4
|
|
|
5
5
|
@theme {
|
|
6
|
-
|
|
7
|
-
--color-
|
|
8
|
-
--color-
|
|
9
|
-
--color-
|
|
10
|
-
--color-brand-accent-light: var(--brand-accent-light);
|
|
11
|
-
--color-brand-text: var(--brand-text);
|
|
12
|
-
--color-brand-text-secondary: var(--brand-text-secondary);
|
|
6
|
+
--color-leaf-50: #F0FDF4;
|
|
7
|
+
--color-leaf-500: #22C55E;
|
|
8
|
+
--color-leaf-600: #16A34A;
|
|
9
|
+
--color-leaf-700: #15803D;
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
--color-
|
|
16
|
-
--color-
|
|
17
|
-
--color-
|
|
18
|
-
--color-
|
|
19
|
-
--color-
|
|
20
|
-
--color-
|
|
21
|
-
--color-
|
|
22
|
-
--color-
|
|
23
|
-
--color-
|
|
24
|
-
--color-mango-900: #5e1e0c;
|
|
25
|
-
--color-mango-950: #3a1107;
|
|
11
|
+
--color-sand-50: #FAFAF7;
|
|
12
|
+
--color-sand-100: #F4F2EC;
|
|
13
|
+
--color-sand-200: #E8E4D8;
|
|
14
|
+
--color-sand-400: #A8A28E;
|
|
15
|
+
--color-sand-500: #78725F;
|
|
16
|
+
--color-sand-600: #5C5644;
|
|
17
|
+
--color-sand-700: #3F3B30;
|
|
18
|
+
--color-sand-800: #2C291F;
|
|
19
|
+
--color-sand-900: #1A1812;
|
|
20
|
+
--color-sand-950: #0F0E0A;
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
--
|
|
22
|
+
--font-sans: var(--font-geist-sans, "Geist", "Inter", system-ui, sans-serif);
|
|
23
|
+
--font-display: var(--font-geist-sans, "Geist", system-ui, sans-serif);
|
|
24
|
+
--font-mono: var(--font-geist-mono, ui-monospace, monospace);
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
--radius-sm: 0.25rem;
|
|
36
|
-
--radius-md: 0.5rem;
|
|
37
|
-
--radius-lg: 0.75rem;
|
|
38
|
-
--radius-xl: 1rem;
|
|
39
|
-
--radius-2xl: 1.5rem;
|
|
26
|
+
--radius-sm: 6px;
|
|
27
|
+
--radius-md: 10px;
|
|
28
|
+
--radius-lg: 14px;
|
|
29
|
+
--radius-xl: 20px;
|
|
30
|
+
--radius-2xl: 28px;
|
|
40
31
|
--radius-full: 9999px;
|
|
41
32
|
}
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
:root {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
--brand-text-secondary: #C9CCD6;
|
|
51
|
-
}
|
|
34
|
+
@layer base {
|
|
35
|
+
:root {
|
|
36
|
+
color-scheme: light;
|
|
37
|
+
--brand-accent: #FF6B35;
|
|
38
|
+
--brand-accent-dark: #D9461C;
|
|
39
|
+
--brand-accent-light: #FFB088;
|
|
40
|
+
}
|
|
52
41
|
|
|
53
|
-
[data-theme="
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
--brand-text-secondary: #6B7280;
|
|
57
|
-
}
|
|
42
|
+
html[data-theme="dark"] {
|
|
43
|
+
color-scheme: dark;
|
|
44
|
+
}
|
|
58
45
|
|
|
59
|
-
|
|
60
|
-
*,
|
|
61
|
-
*::before,
|
|
62
|
-
*::after {
|
|
63
|
-
box-sizing: border-box;
|
|
64
|
-
margin: 0;
|
|
65
|
-
padding: 0;
|
|
66
|
-
}
|
|
46
|
+
* { box-sizing: border-box; }
|
|
67
47
|
|
|
68
|
-
html {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
html {
|
|
49
|
+
scroll-behavior: smooth;
|
|
50
|
+
-webkit-font-smoothing: antialiased;
|
|
51
|
+
-moz-osx-font-smoothing: grayscale;
|
|
52
|
+
background-color: var(--color-sand-50);
|
|
53
|
+
scrollbar-gutter: stable;
|
|
54
|
+
}
|
|
72
55
|
|
|
73
|
-
body {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
56
|
+
body {
|
|
57
|
+
background-color: var(--color-sand-50);
|
|
58
|
+
color: var(--color-sand-900);
|
|
59
|
+
font-family: var(--font-sans);
|
|
60
|
+
text-rendering: optimizeLegibility;
|
|
61
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
62
|
+
}
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
color: var(--brand-text);
|
|
86
|
-
}
|
|
64
|
+
html[data-theme="dark"] {
|
|
65
|
+
background-color: var(--color-sand-950);
|
|
66
|
+
}
|
|
87
67
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
68
|
+
html[data-theme="dark"] body {
|
|
69
|
+
background-color: var(--color-sand-950);
|
|
70
|
+
color: var(--color-sand-50);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
::selection {
|
|
74
|
+
background-color: color-mix(in srgb, var(--brand-accent) 28%, transparent);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:focus-visible {
|
|
78
|
+
outline: 2px solid var(--brand-accent);
|
|
79
|
+
outline-offset: 2px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
*, *::before, *::after {
|
|
84
|
+
animation-duration: 0.01ms !important;
|
|
85
|
+
animation-iteration-count: 1 !important;
|
|
86
|
+
transition-duration: 0.01ms !important;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
99
89
|
}
|
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { HowItWorks } from "@/components/landing/HowItWorks";
|
|
2
|
+
import brand from "@/brand.config";
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
{ title: "1. Choose your plan", body: "Browse plans by region or data size and select one that suits your trip." },
|
|
5
|
-
{ title: "2. Create an account", body: "Sign up for free to manage your eSIMs in one place." },
|
|
6
|
-
{ title: "3. Complete checkout", body: "Pay securely. You will receive a QR code by email within seconds." },
|
|
7
|
-
{ title: "4. Scan & connect", body: "Open Settings on your device, scan the QR code, and you are online." },
|
|
8
|
-
];
|
|
4
|
+
export const metadata = { title: "Help" };
|
|
9
5
|
|
|
10
6
|
export default function HelpPage() {
|
|
11
7
|
return (
|
|
12
|
-
<div className="
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
<div className="pb-16">
|
|
9
|
+
<div className="mx-auto max-w-3xl px-4 py-16">
|
|
10
|
+
<h1 className="text-3xl font-semibold tracking-tight text-sand-900 dark:text-sand-50">
|
|
11
|
+
Help
|
|
12
|
+
</h1>
|
|
13
|
+
<p className="mt-2 text-sand-600 dark:text-sand-400">
|
|
14
|
+
Get connected in under five minutes. Humans on WhatsApp, 24/7.
|
|
15
|
+
</p>
|
|
16
|
+
</div>
|
|
17
|
+
<HowItWorks />
|
|
18
|
+
<div className="mx-auto max-w-3xl px-4 pt-10 text-sm text-sand-600 dark:text-sand-400">
|
|
19
|
+
Need a human?{" "}
|
|
20
|
+
<a href={`mailto:${brand.supportEmail}`} className="font-medium" style={{ color: "var(--brand-accent)" }}>
|
|
21
|
+
{brand.supportEmail}
|
|
22
|
+
</a>
|
|
23
|
+
{" · "}
|
|
24
|
+
<a href="/contact" className="font-medium" style={{ color: "var(--brand-accent)" }}>
|
|
25
|
+
All channels
|
|
26
|
+
</a>
|
|
22
27
|
</div>
|
|
23
28
|
</div>
|
|
24
29
|
);
|
|
@@ -5,6 +5,7 @@ import { GeistSans } from "geist/font/sans";
|
|
|
5
5
|
import { GeistMono } from "geist/font/mono";
|
|
6
6
|
import { Providers } from "@/components/Providers";
|
|
7
7
|
import { ThemeScript } from "@/components/theme/theme-script";
|
|
8
|
+
import { BrandStyles } from "@/components/theme/BrandStyles";
|
|
8
9
|
import { StorefrontNav } from "@/components/nav/StorefrontNav";
|
|
9
10
|
import { StorefrontFooter } from "@/components/footer/StorefrontFooter";
|
|
10
11
|
import brand from "@/brand.config";
|
|
@@ -12,7 +13,7 @@ import "./globals.css";
|
|
|
12
13
|
|
|
13
14
|
export const metadata: Metadata = {
|
|
14
15
|
title: {
|
|
15
|
-
default: brand.name
|
|
16
|
+
default: `${brand.name} — ${brand.tagline}`,
|
|
16
17
|
template: `%s — ${brand.name}`,
|
|
17
18
|
},
|
|
18
19
|
description: brand.tagline,
|
|
@@ -30,24 +31,34 @@ export const metadata: Metadata = {
|
|
|
30
31
|
|
|
31
32
|
export const viewport: Viewport = {
|
|
32
33
|
themeColor: brand.colors.accent,
|
|
33
|
-
colorScheme: "dark
|
|
34
|
+
colorScheme: "light dark",
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
37
38
|
return (
|
|
38
39
|
<html
|
|
39
40
|
lang="en"
|
|
40
|
-
data-theme="
|
|
41
|
+
data-theme="light"
|
|
41
42
|
className={`${GeistSans.variable} ${GeistMono.variable}`}
|
|
42
43
|
suppressHydrationWarning
|
|
43
44
|
>
|
|
44
45
|
<head>
|
|
45
46
|
<ThemeScript />
|
|
47
|
+
<BrandStyles />
|
|
46
48
|
</head>
|
|
47
49
|
<body>
|
|
48
50
|
<Providers>
|
|
51
|
+
<a
|
|
52
|
+
href="#main-content"
|
|
53
|
+
className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-20 focus:z-[100] focus:rounded-lg focus:px-4 focus:py-2 focus:text-sm focus:font-semibold focus:text-white"
|
|
54
|
+
style={{ background: "var(--brand-accent)" }}
|
|
55
|
+
>
|
|
56
|
+
Skip to main content
|
|
57
|
+
</a>
|
|
49
58
|
<StorefrontNav />
|
|
50
|
-
<main className="pt-16
|
|
59
|
+
<main id="main-content" className="pt-16">
|
|
60
|
+
{children}
|
|
61
|
+
</main>
|
|
51
62
|
<StorefrontFooter />
|
|
52
63
|
</Providers>
|
|
53
64
|
</body>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { HeroSection } from "@/components/landing/HeroSection";
|
|
2
|
+
import { HowItWorks } from "@/components/landing/HowItWorks";
|
|
3
|
+
import { FinalCTA } from "@/components/landing/FinalCTA";
|
|
2
4
|
import { FaqSection } from "@/components/faq/FaqSection";
|
|
3
5
|
import { fetchTemplates } from "@/vendor/carrier/client";
|
|
4
6
|
import { PlanCard } from "@/components/landing/PlanCard";
|
|
@@ -11,21 +13,28 @@ export default async function HomePage() {
|
|
|
11
13
|
<>
|
|
12
14
|
<HeroSection />
|
|
13
15
|
|
|
14
|
-
<section className="
|
|
15
|
-
<div className="mx-auto max-w-6xl
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
Instant activation. No contracts. Cancel anytime.
|
|
16
|
+
<section className="px-4 py-20">
|
|
17
|
+
<div className="mx-auto max-w-6xl">
|
|
18
|
+
<p className="text-center text-xs font-semibold uppercase tracking-[0.18em] text-sand-500">
|
|
19
|
+
Plans
|
|
19
20
|
</p>
|
|
20
|
-
<
|
|
21
|
+
<h2 className="mt-2 text-center text-3xl font-semibold tracking-tight text-sand-900 dark:text-sand-50">
|
|
22
|
+
Popular plans
|
|
23
|
+
</h2>
|
|
24
|
+
<p className="mx-auto mt-3 max-w-lg text-center text-sand-600 dark:text-sand-400">
|
|
25
|
+
Instant activation. No contracts. Price next to the limit.
|
|
26
|
+
</p>
|
|
27
|
+
<div className="mt-12 grid grid-cols-1 gap-8 md:grid-cols-3">
|
|
21
28
|
{featured.map((plan, i) => (
|
|
22
|
-
<PlanCard key={plan.id} plan={plan} featured={i === 1} />
|
|
29
|
+
<PlanCard key={plan.id} plan={plan} featured={i === 1} href={`/checkout/${plan.id}`} />
|
|
23
30
|
))}
|
|
24
31
|
</div>
|
|
25
32
|
</div>
|
|
26
33
|
</section>
|
|
27
34
|
|
|
35
|
+
<HowItWorks />
|
|
28
36
|
<FaqSection />
|
|
37
|
+
<FinalCTA />
|
|
29
38
|
</>
|
|
30
39
|
);
|
|
31
40
|
}
|
|
@@ -15,9 +15,13 @@ export function ShopClient({ plans }: { plans: SkuTemplate[] }) {
|
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<div className="mx-auto max-w-6xl px-4 py-16">
|
|
18
|
-
<h1 className="text-4xl font-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
<h1 className="text-4xl font-semibold tracking-tight text-sand-900 dark:text-sand-50">
|
|
19
|
+
Choose your plan
|
|
20
|
+
</h1>
|
|
21
|
+
<p className="mt-2 mb-12 text-sand-600 dark:text-sand-400">
|
|
22
|
+
Instant activation. No contracts. Price next to the limit.
|
|
23
|
+
</p>
|
|
24
|
+
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
|
21
25
|
{plans.map((plan, i) => (
|
|
22
26
|
<PlanCard key={plan.id} plan={plan} featured={i === 1} onSelect={handleSelect} />
|
|
23
27
|
))}
|
|
@@ -16,7 +16,7 @@ export default async function SignInPage({ searchParams }: Props) {
|
|
|
16
16
|
return (
|
|
17
17
|
<div className="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4">
|
|
18
18
|
<SignIn
|
|
19
|
-
|
|
19
|
+
fallbackRedirectUrl={redirectUrl}
|
|
20
20
|
signUpUrl="/sign-up"
|
|
21
21
|
/>
|
|
22
22
|
</div>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* useClerk() → { setActive }
|
|
11
11
|
*/
|
|
12
12
|
import { useState, FormEvent } from "react";
|
|
13
|
-
import { useSignUp } from "@clerk/nextjs
|
|
13
|
+
import { useSignUp } from "@clerk/nextjs";
|
|
14
14
|
import { useClerk } from "@clerk/nextjs";
|
|
15
15
|
import { useSearchParams, useRouter } from "next/navigation";
|
|
16
16
|
import { completeEmailSignUpAfterVerify } from "@/lib/complete-email-sign-up";
|
|
@@ -82,14 +82,14 @@ export function StorefrontSignUpClient() {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
return (
|
|
85
|
-
<div className="w-full max-w-sm rounded-2xl border border-
|
|
85
|
+
<div className="w-full max-w-sm rounded-2xl border border-sand-200 dark:border-sand-800 bg-white dark:bg-sand-900 p-8 backdrop-blur">
|
|
86
86
|
{step === "credentials" && (
|
|
87
87
|
<>
|
|
88
|
-
<h1 className="text-xl font-bold text-
|
|
89
|
-
<p className="text-xs text-
|
|
88
|
+
<h1 className="text-xl font-bold text-sand-900 dark:text-sand-50 mb-1">Create account</h1>
|
|
89
|
+
<p className="text-xs text-sand-500 mb-6">Email and password — no phone required.</p>
|
|
90
90
|
<form onSubmit={handleCredentials} className="space-y-4">
|
|
91
91
|
<div>
|
|
92
|
-
<label className="block text-xs font-medium text-
|
|
92
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="sf-email">
|
|
93
93
|
Email address
|
|
94
94
|
</label>
|
|
95
95
|
<input
|
|
@@ -99,12 +99,12 @@ export function StorefrontSignUpClient() {
|
|
|
99
99
|
required
|
|
100
100
|
value={email}
|
|
101
101
|
onChange={(e) => setEmail(e.target.value)}
|
|
102
|
-
className="w-full rounded-lg border border-
|
|
102
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
103
103
|
placeholder="you@example.com"
|
|
104
104
|
/>
|
|
105
105
|
</div>
|
|
106
106
|
<div>
|
|
107
|
-
<label className="block text-xs font-medium text-
|
|
107
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="sf-password">
|
|
108
108
|
Password
|
|
109
109
|
</label>
|
|
110
110
|
<input
|
|
@@ -115,7 +115,7 @@ export function StorefrontSignUpClient() {
|
|
|
115
115
|
minLength={8}
|
|
116
116
|
value={password}
|
|
117
117
|
onChange={(e) => setPassword(e.target.value)}
|
|
118
|
-
className="w-full rounded-lg border border-
|
|
118
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
119
119
|
placeholder="Min. 8 characters"
|
|
120
120
|
/>
|
|
121
121
|
</div>
|
|
@@ -132,7 +132,7 @@ export function StorefrontSignUpClient() {
|
|
|
132
132
|
{loading ? "Creating account…" : "Continue"}
|
|
133
133
|
</button>
|
|
134
134
|
</form>
|
|
135
|
-
<p className="mt-4 text-center text-xs text-
|
|
135
|
+
<p className="mt-4 text-center text-xs text-sand-400">
|
|
136
136
|
Already have an account?{" "}
|
|
137
137
|
<a href="/sign-in" className="text-[var(--brand-accent)] hover:underline">
|
|
138
138
|
Sign in
|
|
@@ -143,13 +143,13 @@ export function StorefrontSignUpClient() {
|
|
|
143
143
|
|
|
144
144
|
{step === "verify" && (
|
|
145
145
|
<>
|
|
146
|
-
<h1 className="text-xl font-bold text-
|
|
147
|
-
<p className="text-xs text-
|
|
148
|
-
We sent a 6-digit code to <span className="text-
|
|
146
|
+
<h1 className="text-xl font-bold text-sand-900 dark:text-sand-50 mb-1">Check your email</h1>
|
|
147
|
+
<p className="text-xs text-sand-500 mb-6">
|
|
148
|
+
We sent a 6-digit code to <span className="text-sand-600 dark:text-sand-300">{email}</span>.
|
|
149
149
|
</p>
|
|
150
150
|
<form onSubmit={handleVerify} className="space-y-4">
|
|
151
151
|
<div>
|
|
152
|
-
<label className="block text-xs font-medium text-
|
|
152
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="sf-code">
|
|
153
153
|
Verification code
|
|
154
154
|
</label>
|
|
155
155
|
<input
|
|
@@ -161,7 +161,7 @@ export function StorefrontSignUpClient() {
|
|
|
161
161
|
maxLength={6}
|
|
162
162
|
value={code}
|
|
163
163
|
onChange={(e) => setCode(e.target.value.replace(/\D/g, ""))}
|
|
164
|
-
className="w-full rounded-lg border border-
|
|
164
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-center text-lg tracking-[0.4em] text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
165
165
|
placeholder="000000"
|
|
166
166
|
/>
|
|
167
167
|
</div>
|
|
@@ -181,7 +181,7 @@ export function StorefrontSignUpClient() {
|
|
|
181
181
|
<button
|
|
182
182
|
type="button"
|
|
183
183
|
onClick={() => { setStep("credentials"); setError(null); setCode(""); }}
|
|
184
|
-
className="mt-4 w-full text-center text-xs text-
|
|
184
|
+
className="mt-4 w-full text-center text-xs text-sand-400 hover:text-sand-500"
|
|
185
185
|
>
|
|
186
186
|
← Back
|
|
187
187
|
</button>
|
|
@@ -191,7 +191,7 @@ export function StorefrontSignUpClient() {
|
|
|
191
191
|
{step === "complete" && (
|
|
192
192
|
<div className="text-center space-y-3 py-4">
|
|
193
193
|
<div className="mx-auto size-8 animate-spin rounded-full border-2 border-[var(--brand-accent)] border-t-transparent" />
|
|
194
|
-
<p className="text-sm text-
|
|
194
|
+
<p className="text-sm text-sand-600 dark:text-sand-400">Setting up your account…</p>
|
|
195
195
|
</div>
|
|
196
196
|
)}
|
|
197
197
|
</div>
|
|
@@ -17,7 +17,7 @@ export default function SignUpPage() {
|
|
|
17
17
|
fallback={
|
|
18
18
|
<div className="text-center space-y-3">
|
|
19
19
|
<div className="mx-auto size-8 animate-spin rounded-full border-2 border-[var(--brand-accent)] border-t-transparent" />
|
|
20
|
-
<p className="text-sm text-
|
|
20
|
+
<p className="text-sm text-sand-600 dark:text-sand-400">Loading…</p>
|
|
21
21
|
</div>
|
|
22
22
|
}
|
|
23
23
|
>
|
|
@@ -11,12 +11,12 @@ const brand = {
|
|
|
11
11
|
supportEmail: "support@carrier.llc",
|
|
12
12
|
supportWhatsapp: "+17864604829",
|
|
13
13
|
colors: {
|
|
14
|
-
bg: "#
|
|
14
|
+
bg: "#FAFAF7",
|
|
15
15
|
accent: "#FF6B35",
|
|
16
16
|
accentDark: "#D9461C",
|
|
17
17
|
accentLight: "#FFB088",
|
|
18
|
-
text: "#
|
|
19
|
-
textSecondary: "#
|
|
18
|
+
text: "#1A1812",
|
|
19
|
+
textSecondary: "#5C5644",
|
|
20
20
|
},
|
|
21
21
|
social: {
|
|
22
22
|
x: "@carrier_llc",
|
|
@@ -3,45 +3,41 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { motion, AnimatePresence } from "framer-motion";
|
|
5
5
|
import { cn } from "@/vendor/ui/cn";
|
|
6
|
+
import brand from "@/brand.config";
|
|
6
7
|
|
|
7
8
|
const FAQS = [
|
|
8
9
|
{
|
|
9
10
|
q: "What is an eSIM?",
|
|
10
|
-
a: "An eSIM
|
|
11
|
+
a: "An eSIM is a digital SIM. You scan a QR code — no plastic card, no store visit. Your phone is online in about 30 seconds.",
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
14
|
q: "Which devices are compatible?",
|
|
14
|
-
a: "
|
|
15
|
+
a: "iPhone XS and later, recent Google Pixels, Samsung Galaxy S21 and newer, and most modern iPads. If your phone can add a cellular plan in Settings, you are set.",
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
|
-
q: "How
|
|
18
|
-
a: "
|
|
18
|
+
q: "How fast is activation?",
|
|
19
|
+
a: "The QR code arrives by email as soon as payment clears. Scan it before you fly or after you land.",
|
|
19
20
|
},
|
|
20
21
|
{
|
|
21
|
-
q: "What happens when
|
|
22
|
-
a: "You
|
|
22
|
+
q: "What happens when data runs low?",
|
|
23
|
+
a: "You get a warning, then you can top up or buy another plan from the dashboard. We do not cut you off without telling you.",
|
|
23
24
|
},
|
|
24
25
|
{
|
|
25
|
-
q: "Can I use
|
|
26
|
-
a: "
|
|
26
|
+
q: "Can I use one plan in several countries?",
|
|
27
|
+
a: "Regional and nomad plans cover more than one country. The card lists coverage before you pay.",
|
|
27
28
|
},
|
|
28
29
|
];
|
|
29
30
|
|
|
30
31
|
function FaqItem({ q, a }: { q: string; a: string }) {
|
|
31
32
|
const [open, setOpen] = useState(false);
|
|
32
33
|
return (
|
|
33
|
-
<div className="border-b border-
|
|
34
|
+
<div className="border-b border-sand-200 dark:border-sand-800">
|
|
34
35
|
<button
|
|
35
|
-
className="flex w-full items-center justify-between py-4 text-left text-
|
|
36
|
+
className="flex w-full items-center justify-between py-4 text-left text-sand-900 transition-colors hover:text-[var(--brand-accent)] dark:text-sand-50"
|
|
36
37
|
onClick={() => setOpen((o) => !o)}
|
|
37
38
|
>
|
|
38
39
|
<span className="font-medium">{q}</span>
|
|
39
|
-
<span
|
|
40
|
-
className={cn(
|
|
41
|
-
"ml-4 shrink-0 text-[var(--brand-accent)] transition-transform",
|
|
42
|
-
open && "rotate-45",
|
|
43
|
-
)}
|
|
44
|
-
>
|
|
40
|
+
<span className={cn("ml-4 shrink-0 text-[var(--brand-accent)] transition-transform", open && "rotate-45")}>
|
|
45
41
|
+
|
|
46
42
|
</span>
|
|
47
43
|
</button>
|
|
@@ -54,7 +50,7 @@ function FaqItem({ q, a }: { q: string; a: string }) {
|
|
|
54
50
|
transition={{ duration: 0.2 }}
|
|
55
51
|
className="overflow-hidden"
|
|
56
52
|
>
|
|
57
|
-
<p className="pb-4 text-sm text-
|
|
53
|
+
<p className="pb-4 text-sm leading-relaxed text-sand-600 dark:text-sand-400">{a}</p>
|
|
58
54
|
</motion.div>
|
|
59
55
|
)}
|
|
60
56
|
</AnimatePresence>
|
|
@@ -64,16 +60,17 @@ function FaqItem({ q, a }: { q: string; a: string }) {
|
|
|
64
60
|
|
|
65
61
|
export function FaqSection() {
|
|
66
62
|
return (
|
|
67
|
-
<section className="
|
|
68
|
-
<div className="mx-auto max-w-2xl
|
|
69
|
-
<h2 className="text-3xl font-
|
|
70
|
-
|
|
63
|
+
<section className="px-4 py-20">
|
|
64
|
+
<div className="mx-auto max-w-2xl">
|
|
65
|
+
<h2 className="mb-10 text-center text-3xl font-semibold tracking-tight text-sand-900 dark:text-sand-50">
|
|
66
|
+
Questions, answered
|
|
71
67
|
</h2>
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
{FAQS.map((faq) => (
|
|
69
|
+
<FaqItem key={faq.q} q={faq.q} a={faq.a} />
|
|
70
|
+
))}
|
|
71
|
+
<p className="mt-8 text-center text-sm text-sand-500">
|
|
72
|
+
Still stuck? WhatsApp {brand.name} — real humans, 24/7.
|
|
73
|
+
</p>
|
|
77
74
|
</div>
|
|
78
75
|
</section>
|
|
79
76
|
);
|