diff --git a/apps/web/app/(landing)/layout.tsx b/apps/web/app/(landing)/layout.tsx new file mode 100644 index 00000000..c68a032a --- /dev/null +++ b/apps/web/app/(landing)/layout.tsx @@ -0,0 +1,29 @@ +import { Geist_Mono, Schibsted_Grotesk } from "next/font/google"; +import LaunchBanner from "@/components/launch-banner"; + +const schibstedGrotesk = Schibsted_Grotesk({ + variable: "--font-schibsted-grotesk", + subsets: ["latin"], + display: "swap", +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], + display: "swap", +}); + +export default function MarketingLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( +
+ + {children} +
+ ); +} diff --git a/apps/web/app/page.tsx b/apps/web/app/(landing)/page.tsx similarity index 100% rename from apps/web/app/page.tsx rename to apps/web/app/(landing)/page.tsx diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index b1f02a6d..3849c209 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -92,9 +92,6 @@ --sidebar-accent-foreground: oklch(0.8109 0 0); --sidebar-border: oklch(0.2520 0 0); --sidebar-ring: oklch(0.7214 0.1337 49.9802); - --font-sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; - --font-serif: serif; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace; --radius: 0.75rem; --shadow-x: 0px; --shadow-y: 1px; @@ -172,6 +169,22 @@ --tracking-widest: calc(var(--tracking-normal) + 0.1em); } +/* Landing page typography (the `(marketing)` route group, i.e. `/` only). + Schibsted Grotesk for text, Geist Mono for code, nav and labels. The + variables are rebound here rather than in :root so every other route keeps + the system stack. System fonts stay on as fallbacks. + Do not re-declare --font-* on any ancestor inside this subtree (e.g. on + `.dark`): a local re-declaration shadows the inherited value for + `font-mono` / `font-sans` utilities even though `font-family` still + inherits. */ +.landing-fonts { + --font-sans: var(--font-schibsted-grotesk), ui-sans-serif, system-ui, + -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + --font-mono: var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, + Monaco, Consolas, "Liberation Mono", monospace; + font-family: var(--font-sans); +} + body { letter-spacing: var(--tracking-normal); } diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 6c822434..79f51b26 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -5,7 +5,6 @@ import { QueryProvider } from "@/components/providers/query-provider"; import { Analytics } from "@vercel/analytics/next"; import { SpeedInsights } from "@vercel/speed-insights/next"; import { Toaster } from "sonner"; -import LaunchBanner from "@/components/launch-banner"; export const metadata: Metadata = { title: "Supercode - The open source SWE agent", @@ -43,7 +42,6 @@ export default function RootLayout({ return ( - - + Beta Beta Live | - + T-MINUS - + jul 10 @@ -121,7 +121,7 @@ export default function BetaCountdownBanner({ : - + remain @@ -152,7 +152,7 @@ function DigitPairMobile({ value }: { value: string }) { {value.split("").map((digit, i) => ( {digit} diff --git a/apps/web/components/homepage/changelog-card.tsx b/apps/web/components/homepage/changelog-card.tsx index f4491845..76640592 100644 --- a/apps/web/components/homepage/changelog-card.tsx +++ b/apps/web/components/homepage/changelog-card.tsx @@ -22,34 +22,34 @@ export default function ChangelogCard() {
{/* Left — version info */} -
-

+
+

// SHIP

-

+

Changelog

- + v0.1.90 -

+

56 releases shipped

{/* Right — recent entries */} -
+
{changelogEntries.map((entry, i) => { const style = typeStyles[entry.type]; return (
{style.label} -

+

{entry.text}

@@ -61,7 +61,7 @@ export default function ChangelogCard() {
READ FULL CHANGELOG diff --git a/apps/web/components/homepage/faq-section.tsx b/apps/web/components/homepage/faq-section.tsx index 3d337ac5..0b5ca241 100644 --- a/apps/web/components/homepage/faq-section.tsx +++ b/apps/web/components/homepage/faq-section.tsx @@ -53,10 +53,10 @@ const FAQSection = () => {
-

+

// faq

-

+

frequently asked questions

@@ -68,11 +68,11 @@ const FAQSection = () => { value={`item-${i}`} className="border border-border rounded-lg bg-card/20 data-[state=open]:bg-card/40 transition-colors duration-200" > - + {item.q} -

+

{item.a}

diff --git a/apps/web/components/homepage/footer.tsx b/apps/web/components/homepage/footer.tsx index e7a1ed52..fe748901 100644 --- a/apps/web/components/homepage/footer.tsx +++ b/apps/web/components/homepage/footer.tsx @@ -83,7 +83,6 @@ const PIXEL_FONT: Record = { ], } -const PIXEL_SIZE = 8 const PIXEL_GAP = 1 const PixelLogo = () => { @@ -93,9 +92,15 @@ const PixelLogo = () => {
setHoverTick((t) => t + 1)} - style={{ - filter: "drop-shadow(0 0 6px hsl(var(--primary) / 0.3))", - }} + style={ + { + filter: "drop-shadow(0 0 6px hsl(var(--primary) / 0.3))", + // Fluid pixel size so the wordmark can never overflow a narrow screen. + // Width = 9 glyphs x (5 pixels + 4 gaps) + 8 x 3px gaps + // = 45 x pixelSize + 60, so keep 45 x px under the inner width. + "--px": "min(8px, calc((100vw - 120px) / 45))", + } as React.CSSProperties + } > {/* Scanline overlay */}
{ style={{ backgroundImage: "repeating-linear-gradient(0deg, transparent, transparent 1px, rgba(0,0,0,0.3) 1px, rgba(0,0,0,0.3) 2px)", - backgroundSize: `100% ${PIXEL_SIZE + PIXEL_GAP}px`, + backgroundSize: `100% calc(var(--px) + ${PIXEL_GAP}px)`, }} />
@@ -112,7 +117,7 @@ const PixelLogo = () => { key={`${hoverTick}-${ci}`} className="grid" style={{ - gridTemplateRows: `repeat(7, ${PIXEL_SIZE}px)`, + gridTemplateRows: "repeat(7, var(--px))", gap: `${PIXEL_GAP}px`, }} > @@ -127,8 +132,8 @@ const PixelLogo = () => { key={`${ri}-${pi}`} className={`${pixel ? "bg-primary" : "bg-transparent"}`} style={{ - width: PIXEL_SIZE, - height: PIXEL_SIZE, + width: "var(--px)", + height: "var(--px)", opacity: pixel ? undefined : 0, borderRadius: "1px", animation: pixel ? `pixelFadeIn 0.4s ease-out ${(ri * 5 + pi) * 20 + ci * 120}ms both` : undefined, @@ -176,8 +181,8 @@ interface LinkGroupProps { const LinkGroup = ({ title, links }: LinkGroupProps) => (
-

- $ {title} +

+ {title}

    {links.map((link, i) => ( @@ -194,7 +199,7 @@ const LinkGroup = ({ title, links }: LinkGroupProps) => ( href={link.href} target="_blank" rel="noopener noreferrer" - className="group inline-flex items-center gap-2 text-[13px] font-mono text-muted-foreground hover:text-foreground transition-colors duration-200" + className="group inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors duration-200" > {link.label} @@ -203,7 +208,7 @@ const LinkGroup = ({ title, links }: LinkGroupProps) => ( ) : ( {link.label} @@ -295,47 +300,37 @@ const Footer = () => { />
- {/* Social links */} - - - - - - - - - - - - {/* Bottom bar */} -
-

+

+

© 2026 SUPERCODE INC.

-
- - v0.1.83-beta - + + v0.1.83-beta + +
diff --git a/apps/web/components/homepage/get-started.tsx b/apps/web/components/homepage/get-started.tsx index bca6f15d..ba78c1cd 100644 --- a/apps/web/components/homepage/get-started.tsx +++ b/apps/web/components/homepage/get-started.tsx @@ -19,16 +19,18 @@ const commands: CommandLine[] = [ { type: "info", text: "start shipping with supercode" }, ] -const reduceMotion = - typeof window !== "undefined" - ? window.matchMedia("(prefers-reduced-motion: reduce)").matches - : false - const GetStartedSection = () => { const [visible, setVisible] = useState(false) + const [reduceMotion, setReduceMotion] = useState(false) const [copiedIndex, setCopiedIndex] = useState(null) const sectionRef = useRef(null) + useEffect(() => { + setReduceMotion( + window.matchMedia("(prefers-reduced-motion: reduce)").matches, + ) + }, []) + useEffect(() => { const el = sectionRef.current if (!el) return @@ -84,7 +86,7 @@ const GetStartedSection = () => { `}
-

+

$ Get started with Supercode

@@ -131,7 +133,7 @@ const GetStartedSection = () => { - ))} + {(Object.keys(installCommands) as InstallMethod[]).map( + (method) => ( + + ), + )}
{/* Command display */} -
- +
+ - {installCommands[activeMethod].command.split(installCommands[activeMethod].highlight)[0]} + { + installCommands[activeMethod].command.split( + installCommands[activeMethod].highlight, + )[0] + } {installCommands[activeMethod].highlight} - {activeMethod === 'curl' && ( + {activeMethod === "curl" && ( <> | bash )} -
+

+ Or read the{" "} + + documentation + +

- - {/* Documentation link */} -

- Or read the documentation -

- - {/* Beta Section */} - {/*
-
-

- The Open Source Software Engineer -

-

- Use supercode with your free models. Free models are available,
- including GPT, Gemini, Minimax, Deepseek and more. -

-
- currently in beta -
-
-
*/} ); }; -export default HeroSection; \ No newline at end of file +export default HeroSection; diff --git a/apps/web/components/homepage/navbar.tsx b/apps/web/components/homepage/navbar.tsx index 13cf39d3..c621f566 100644 --- a/apps/web/components/homepage/navbar.tsx +++ b/apps/web/components/homepage/navbar.tsx @@ -1,36 +1,33 @@ -"use client" +"use client"; -import React, { useEffect, useState, useCallback } from "react" -import Link from "next/link" -import { usePathname } from "next/navigation" -import BetaCountdownBanner from "./beta-countdown-banner" -import ProductsDropdown from "./products-dropdown" -import { Button } from "../ui/button" +import Link from "next/link"; +import { Button } from "../ui/button"; +import { usePathname } from "next/navigation"; +import ProductsDropdown from "./products-dropdown"; +import { useEffect, useState } from "react"; const DOCS_URL = - process.env.NEXT_PUBLIC_DOCS_URL || "http://localhost:3001/docs/intro" + process.env.NEXT_PUBLIC_DOCS_URL || "http://localhost:3001/docs/intro"; function GithubStars() { - const [stars, setStars] = useState(null) + const [stars, setStars] = useState(null); useEffect(() => { fetch("https://api.github.com/repos/yashdev9274/superCli") .then((res) => res.json()) .then((data) => setStars(data.stargazers_count)) - .catch(() => {}) - }, []) + .catch(() => {}); + }, []); - if (stars === null) return null + if (stars === null) return null; return ( - + [{stars >= 1000 ? `${(stars / 1000).toFixed(1)}k` : stars}] - ) + ); } -const EASE = "cubic-bezier(0.23,1,0.32,1)" - const PixelLogo = () => ( ( -) +); const mobileProducts = [ - { label: "Supercode Agent", description: "AI pair programmer that ships code with you", href: "/download" }, - { label: "Supercode Review", description: "Automated code review on every pull request", href: "/code-review" }, - { label: "Supercode Voice Agent", description: "Control your system with your voice", href: "/download" }, - { label: "Cortex SDK", description: "Build custom AI tooling on top of Supercode", href: "https://github.com/yashdev9274/superCli", external: true }, -] + { + label: "Supercode Agent", + description: "AI pair programmer that ships code with you", + href: "/download", + }, + { + label: "Supercode Review", + description: "Automated code review on every pull request", + href: "/code-review", + }, + { + label: "Supercode Voice Agent", + description: "Control your system with your voice", + href: "/download", + }, + { + label: "Cortex SDK", + description: "Build custom AI tooling on top of Supercode", + href: "https://github.com/yashdev9274/superCli", + external: true, + }, +]; const Navbar = () => { - const [menuOpen, setMenuOpen] = useState(false) - const [bannerVisible, setBannerVisible] = useState(false) - const pathname = usePathname() - const hideBanner = pathname === "/code-review" - - const handleBannerChange = useCallback((visible: boolean) => { - setBannerVisible(visible) - }, []) + const [menuOpen, setMenuOpen] = useState(false); + const pathname = usePathname(); + const onLandingPage = pathname === "/"; useEffect(() => { if (menuOpen) { - document.body.style.overflow = "hidden" + document.body.style.overflow = "hidden"; } else { - document.body.style.overflow = "" + document.body.style.overflow = ""; } return () => { - document.body.style.overflow = "" - } - }, [menuOpen]) + document.body.style.overflow = ""; + }; + }, [menuOpen]); - const navItems: Array<{ label: string; href: string; external?: boolean; accent?: boolean }> = [ - { label: "GitHub", href: "https://github.com/yashdev9274/superCli", external: true }, + const navItems: Array<{ + label: string; + href: string; + external?: boolean; + accent?: boolean; + }> = [ + { + label: "GitHub", + href: "https://github.com/yashdev9274/superCli", + external: true, + }, // { label: "Partnerships", href: "/partnerships" }, { label: "Compare", href: "/compare" }, { label: "Docs", href: DOCS_URL, external: true }, { label: "Changelog", href: "/changelog" }, { label: "Blog", href: "/blog" }, // { label: "Waitlist", href: "/waitlist", accent: true }, - ] + ]; return ( <> -
- -
-
-
- - - - -
- - - -
- - - - - Talk to founder - - - - - -
-
-
- -
-
- - -
+
+ + + + + Talk to founder + + + + + +
+
+
+ +
+
- {navItems.map((item, i) => ( + -
-
- {/*
+ {mobileProducts.map((item, i) => ( +
+ {item.external ? ( + setMenuOpen(false)} + className="flex items-center gap-3 px-5 py-3 rounded-xl text-[15px] text-foreground/80 hover:text-foreground hover:bg-accent/30 transition-[background-color] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)]" + > + + {String(i + 1).padStart(2, "0")} + +
+
{item.label}
+
+ {item.description} +
+
+ + ↗ + +
+ ) : ( + setMenuOpen(false)} + className="flex items-center gap-3 px-5 py-3 rounded-xl text-[15px] text-foreground/80 hover:text-foreground hover:bg-accent/30 transition-[background-color] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)]" + > + + {String(i + 1).padStart(2, "0")} + +
+
{item.label}
+
+ {item.description} +
+
+ + )} +
+ ))} + +
+ + {navItems.map((item, i) => ( +
+ {item.external ? ( + setMenuOpen(false)} + className="flex items-center gap-3 px-5 py-4 rounded-xl text-[17px] text-foreground/80 hover:text-foreground hover:bg-accent/30 transition-[background-color] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)]" + > + + {String(i + 1).padStart(2, "0")} + + {item.label} + + ↗ + + + ) : ( + setMenuOpen(false)} + className={`flex items-center gap-3 px-5 py-4 rounded-xl text-[17px] transition-[background-color] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] ${ + item.accent + ? "text-primary bg-primary/5 hover:bg-primary/10" + : "text-foreground/80 hover:text-foreground hover:bg-accent/30" + }`} + > + + {String(i + 1).padStart(2, "0")} + + {item.label} + + )} +
+ ))} +
+ +
+ + + {/*
@@ -402,7 +458,7 @@ const Navbar = () => {
*/} - ) -} + ); +}; -export default Navbar +export default Navbar; diff --git a/apps/web/components/homepage/partnerships-section.tsx b/apps/web/components/homepage/partnerships-section.tsx index 3ed5c650..9468f8de 100644 --- a/apps/web/components/homepage/partnerships-section.tsx +++ b/apps/web/components/homepage/partnerships-section.tsx @@ -13,16 +13,16 @@ const PartnershipsSection = () => {
-

+

$ Partnerships

-

+

Built together

View all @@ -36,26 +36,27 @@ const PartnershipsSection = () => { href={`/partnerships/${partner.slug}`} className="group border text-[#A1A1AA] border-border rounded-lg p-6 hover:border-primary/40 transition-colors duration-200 bg-card/30 hover:bg-card/60" > -
+
{partner.logoSrc ? ( + // eslint-disable-next-line @next/next/no-img-element {partner.name} ) : ( partner.logo )}
-

+

{partner.name}

-

+

{partner.description}

-
+
{partner.stat.value}
-
+
{partner.stat.label}
@@ -66,7 +67,7 @@ const PartnershipsSection = () => {
View all partnerships diff --git a/apps/web/components/homepage/products-dropdown.tsx b/apps/web/components/homepage/products-dropdown.tsx index 15d290a7..aea1f65c 100644 --- a/apps/web/components/homepage/products-dropdown.tsx +++ b/apps/web/components/homepage/products-dropdown.tsx @@ -1,9 +1,7 @@ "use client" -import React, { useState, useRef, useCallback } from "react" import Link from "next/link" - -const EASE = "cubic-bezier(0.23,1,0.32,1)" +import React, { useState, useRef, useCallback } from "react" interface ProductItem { label: string @@ -77,14 +75,14 @@ function ProductRow({ item, index, isVisible }: { item: ProductItem; index: numb }`} style={{ transitionDelay: isVisible ? `${index * 40}ms` : "0ms" }} > -
+
{item.icon}
-
+
{item.label}
-
+
{item.description}
@@ -166,7 +164,7 @@ export default function ProductsDropdown() { onMouseLeave={handleMouseLeave} >