Skip to content

Commit adb9a09

Browse files
Launched full Ghost v2 build
X-Lovable-Edit-ID: edt-2ea27ca8-0809-4d7a-95b7-eb03fabcbc3a
2 parents 33b52ca + 558b0ed commit adb9a09

33 files changed

Lines changed: 2951 additions & 154 deletions

public/favicon.svg

Lines changed: 12 additions & 0 deletions
Loading

public/robots.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
User-agent: *
2+
Allow: /
3+
Disallow: /labs/
4+
Disallow: /academy/
5+
Disallow: /internships/
6+
Disallow: /courses/
7+
Disallow: /verify/
8+
Disallow: /blog/
9+
Disallow: /careers/
10+
Disallow: /events/
11+
Disallow: /community/

src/assets/aurora-bg.jpg

47 KB
Loading

src/assets/founder.jpg

54.9 KB
Loading

src/assets/hero-orb.jpg

52 KB
Loading

src/components/brand/logo.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { cn } from "@/lib/utils";
2+
3+
interface LogoProps {
4+
className?: string;
5+
iconOnly?: boolean;
6+
size?: number;
7+
}
8+
9+
/**
10+
* GhostCode Dynamics brand mark.
11+
* SVG-based, easy to replace with a PNG later.
12+
*/
13+
export function Logo({ className, iconOnly = false, size = 28 }: LogoProps) {
14+
return (
15+
<div className={cn("flex items-center gap-2.5", className)}>
16+
<GhostIcon size={size} />
17+
{!iconOnly && (
18+
<span className="font-display text-[1.05rem] tracking-tight leading-none">
19+
<span className="font-bold text-foreground">Ghost</span>
20+
<span className="font-medium text-muted-foreground">Code Dynamics</span>
21+
</span>
22+
)}
23+
</div>
24+
);
25+
}
26+
27+
export function GhostIcon({ size = 28, className }: { size?: number; className?: string }) {
28+
return (
29+
<svg
30+
width={size}
31+
height={size}
32+
viewBox="0 0 32 32"
33+
fill="none"
34+
xmlns="http://www.w3.org/2000/svg"
35+
className={cn("shrink-0", className)}
36+
aria-hidden="true"
37+
>
38+
<defs>
39+
<linearGradient id="ghost-grad" x1="0" y1="0" x2="1" y2="1">
40+
<stop offset="0%" stopColor="oklch(0.78 0.18 285)" />
41+
<stop offset="100%" stopColor="oklch(0.62 0.2 245)" />
42+
</linearGradient>
43+
<radialGradient id="ghost-glow" cx="0.5" cy="0.4" r="0.6">
44+
<stop offset="0%" stopColor="oklch(0.85 0.18 285 / 0.4)" />
45+
<stop offset="100%" stopColor="oklch(0.5 0.2 285 / 0)" />
46+
</radialGradient>
47+
</defs>
48+
<circle cx="16" cy="16" r="14" fill="url(#ghost-glow)" />
49+
<path
50+
d="M16 4.5c-5 0-8.5 3.6-8.5 8.5v12.2c0 1 1.1 1.6 1.9 1l1.6-1.2c.4-.3 1-.3 1.4 0l1.6 1.2c.4.3 1 .3 1.4 0l1.6-1.2c.4-.3 1-.3 1.4 0l1.6 1.2c.8.6 1.9 0 1.9-1V13c0-4.9-3.5-8.5-8.4-8.5Z"
51+
fill="url(#ghost-grad)"
52+
/>
53+
<circle cx="12.5" cy="14" r="1.4" fill="oklch(0.15 0.02 270)" />
54+
<circle cx="19.5" cy="14" r="1.4" fill="oklch(0.15 0.02 270)" />
55+
</svg>
56+
);
57+
}

src/components/coming-soon.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Link } from "@tanstack/react-router";
2+
import { ArrowLeft } from "lucide-react";
3+
import { Reveal } from "./section";
4+
5+
interface ComingSoonProps {
6+
module: string;
7+
title: string;
8+
description: string;
9+
bullets?: string[];
10+
}
11+
12+
/**
13+
* Shared "Coming soon" page used by future modules
14+
* (Internships, Courses, Cert Verify, Blog, Careers, Events, Community, Academy, Labs).
15+
* These routes are intentionally hidden from the main navigation but
16+
* scaffolded so future expansion is just a content swap.
17+
*/
18+
export function ComingSoon({ module, title, description, bullets }: ComingSoonProps) {
19+
return (
20+
<section className="relative min-h-[80vh] overflow-hidden pt-32 pb-24">
21+
<div className="absolute inset-0 bg-aurora opacity-60 pointer-events-none" aria-hidden />
22+
<div className="absolute inset-0 bg-grid pointer-events-none" aria-hidden />
23+
<div className="container-prose relative">
24+
<Reveal>
25+
<p className="font-mono text-[10px] uppercase tracking-[0.3em] text-primary">
26+
{module} · In the lab
27+
</p>
28+
<h1 className="mt-4 font-display text-4xl font-semibold leading-[1.05] tracking-tight sm:text-5xl md:text-6xl">
29+
<span className="text-gradient">{title}</span>
30+
</h1>
31+
<p className="mt-6 max-w-2xl text-lg leading-relaxed text-muted-foreground">
32+
{description}
33+
</p>
34+
35+
{bullets && (
36+
<ul className="mt-8 grid gap-3 sm:grid-cols-2 max-w-2xl">
37+
{bullets.map((b) => (
38+
<li
39+
key={b}
40+
className="flex items-start gap-3 rounded-xl border border-border bg-card/60 backdrop-blur p-4 text-sm text-muted-foreground"
41+
>
42+
<span className="mt-1 h-1.5 w-1.5 shrink-0 rounded-full bg-primary animate-pulse-glow" />
43+
{b}
44+
</li>
45+
))}
46+
</ul>
47+
)}
48+
49+
<div className="mt-10 flex flex-wrap gap-3">
50+
<Link
51+
to="/contact"
52+
className="inline-flex items-center gap-1.5 rounded-full bg-foreground px-5 py-2.5 text-sm font-medium text-background shadow-elevated hover:opacity-90 transition"
53+
>
54+
Get notified at launch
55+
</Link>
56+
<Link
57+
to="/"
58+
className="inline-flex items-center gap-1.5 rounded-full border border-border bg-surface/60 px-5 py-2.5 text-sm font-medium text-foreground hover:border-primary/40 transition"
59+
>
60+
<ArrowLeft className="h-4 w-4" /> Back home
61+
</Link>
62+
</div>
63+
</Reveal>
64+
</div>
65+
</section>
66+
);
67+
}

src/components/cta-button.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Link } from "@tanstack/react-router";
2+
import { ArrowUpRight } from "lucide-react";
3+
import { cn } from "@/lib/utils";
4+
import type { ReactNode } from "react";
5+
6+
type Variant = "primary" | "secondary" | "ghost";
7+
8+
const variants: Record<Variant, string> = {
9+
primary:
10+
"bg-foreground text-background hover:opacity-90 shadow-elevated",
11+
secondary:
12+
"border border-border bg-surface/60 backdrop-blur text-foreground hover:bg-surface hover:border-primary/40",
13+
ghost:
14+
"text-foreground hover:text-primary",
15+
};
16+
17+
interface BaseProps {
18+
children: ReactNode;
19+
variant?: Variant;
20+
className?: string;
21+
icon?: boolean;
22+
}
23+
24+
export function CtaLink({
25+
to,
26+
href,
27+
external,
28+
children,
29+
variant = "primary",
30+
className,
31+
icon = true,
32+
}: BaseProps & { to?: string; href?: string; external?: boolean }) {
33+
const classes = cn(
34+
"group inline-flex items-center gap-1.5 rounded-full px-5 py-2.5 text-sm font-medium transition-all",
35+
variants[variant],
36+
className,
37+
);
38+
39+
const content = (
40+
<>
41+
{children}
42+
{icon && (
43+
<ArrowUpRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
44+
)}
45+
</>
46+
);
47+
48+
if (external || href) {
49+
return (
50+
<a href={href} target={external ? "_blank" : undefined} rel={external ? "noopener noreferrer" : undefined} className={classes}>
51+
{content}
52+
</a>
53+
);
54+
}
55+
56+
return (
57+
<Link to={to ?? "/"} className={classes}>
58+
{content}
59+
</Link>
60+
);
61+
}

src/components/section.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { motion, type Variants } from "framer-motion";
2+
import { cn } from "@/lib/utils";
3+
import type { ReactNode } from "react";
4+
5+
export const fadeUp: Variants = {
6+
hidden: { opacity: 0, y: 20 },
7+
show: { opacity: 1, y: 0, transition: { duration: 0.55, ease: [0.22, 1, 0.36, 1] } },
8+
};
9+
10+
export const stagger: Variants = {
11+
hidden: {},
12+
show: { transition: { staggerChildren: 0.08, delayChildren: 0.05 } },
13+
};
14+
15+
export function Reveal({
16+
children,
17+
className,
18+
delay = 0,
19+
as: As = "div",
20+
}: {
21+
children: ReactNode;
22+
className?: string;
23+
delay?: number;
24+
as?: React.ElementType;
25+
}) {
26+
const MotionTag = motion(As as any);
27+
return (
28+
<MotionTag
29+
initial="hidden"
30+
whileInView="show"
31+
viewport={{ once: true, margin: "-80px" }}
32+
variants={fadeUp}
33+
transition={{ delay }}
34+
className={className}
35+
>
36+
{children}
37+
</MotionTag>
38+
);
39+
}
40+
41+
export function SectionHeader({
42+
eyebrow,
43+
title,
44+
description,
45+
className,
46+
align = "left",
47+
}: {
48+
eyebrow?: string;
49+
title: ReactNode;
50+
description?: ReactNode;
51+
className?: string;
52+
align?: "left" | "center";
53+
}) {
54+
return (
55+
<Reveal className={cn(align === "center" && "text-center mx-auto max-w-2xl", className)}>
56+
{eyebrow && (
57+
<div
58+
className={cn(
59+
"inline-flex items-center gap-2 rounded-full border border-border bg-surface/60 px-3 py-1 text-xs font-medium text-muted-foreground",
60+
align === "center" && "mx-auto",
61+
)}
62+
>
63+
<span className="h-1.5 w-1.5 rounded-full bg-primary animate-pulse-glow" />
64+
{eyebrow}
65+
</div>
66+
)}
67+
<h2 className="mt-5 font-display text-3xl font-semibold tracking-tight text-foreground sm:text-4xl md:text-5xl">
68+
{title}
69+
</h2>
70+
{description && (
71+
<p className="mt-4 max-w-2xl text-base leading-relaxed text-muted-foreground sm:text-lg">
72+
{description}
73+
</p>
74+
)}
75+
</Reveal>
76+
);
77+
}
78+
79+
export function PageHero({
80+
eyebrow,
81+
title,
82+
description,
83+
children,
84+
}: {
85+
eyebrow?: string;
86+
title: ReactNode;
87+
description?: ReactNode;
88+
children?: ReactNode;
89+
}) {
90+
return (
91+
<section className="relative overflow-hidden pt-32 pb-16 md:pt-40 md:pb-24">
92+
<div className="absolute inset-0 bg-aurora opacity-70 pointer-events-none" aria-hidden />
93+
<div className="absolute inset-0 bg-grid pointer-events-none" aria-hidden />
94+
<div className="container-prose relative">
95+
<Reveal>
96+
{eyebrow && (
97+
<div className="inline-flex items-center gap-2 rounded-full border border-border glass px-3 py-1 text-xs font-medium text-muted-foreground">
98+
<span className="h-1.5 w-1.5 rounded-full bg-primary animate-pulse-glow" />
99+
{eyebrow}
100+
</div>
101+
)}
102+
<h1 className="mt-5 font-display text-4xl font-semibold leading-[1.05] tracking-tight text-foreground sm:text-5xl md:text-6xl lg:text-7xl">
103+
<span className="text-gradient">{title}</span>
104+
</h1>
105+
{description && (
106+
<p className="mt-6 max-w-2xl text-lg leading-relaxed text-muted-foreground">
107+
{description}
108+
</p>
109+
)}
110+
{children && <div className="mt-8">{children}</div>}
111+
</Reveal>
112+
</div>
113+
</section>
114+
);
115+
}

0 commit comments

Comments
 (0)