diff --git a/apps/landing/package.json b/apps/landing/package.json index 0f92f17..6b93aea 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -10,17 +10,18 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "react": "19.1.0", - "react-dom": "19.1.0", + "@banatie/database": "workspace:*", + "lucide-react": "^0.400.0", "next": "15.5.4", - "@banatie/database": "workspace:*" + "react": "19.1.0", + "react-dom": "19.1.0" }, "devDependencies": { - "typescript": "^5", + "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4" + "tailwindcss": "^4", + "typescript": "^5" } } diff --git a/apps/landing/src/app/globals.css b/apps/landing/src/app/globals.css index bafa22b..63d1204 100644 --- a/apps/landing/src/app/globals.css +++ b/apps/landing/src/app/globals.css @@ -45,6 +45,15 @@ body { } } +@keyframes gradient-rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + .animate-gradient { background-size: 200% 200%; animation: gradient-shift 3s ease infinite; diff --git a/apps/landing/src/app/homepage/components/AnimatedGradientBorder.tsx b/apps/landing/src/app/homepage/components/AnimatedGradientBorder.tsx new file mode 100644 index 0000000..3a9e12d --- /dev/null +++ b/apps/landing/src/app/homepage/components/AnimatedGradientBorder.tsx @@ -0,0 +1,55 @@ +interface AnimatedGradientBorderProps { + children: React.ReactNode; + className?: string; +} + +const gradientStyle: React.CSSProperties = { + background: `conic-gradient( + from 0deg, + rgba(99, 102, 241, 0.3), + rgba(139, 92, 246, 0.8), + rgba(236, 72, 153, 0.6), + rgba(34, 211, 238, 0.8), + rgba(99, 102, 241, 0.3) + )`, + filter: 'blur(40px)', +}; + +const glowStyle: React.CSSProperties = { + background: `conic-gradient( + from 0deg, + rgba(99, 102, 241, 0.1), + rgba(139, 92, 246, 0.4), + rgba(236, 72, 153, 0.3), + rgba(34, 211, 238, 0.4), + rgba(99, 102, 241, 0.1) + )`, + filter: 'blur(15px)', +}; + +export const AnimatedGradientBorder = ({ + children, + className = '', +}: AnimatedGradientBorderProps) => { + return ( +
+
+
+
+
+
{children}
+
+ ); +}; diff --git a/apps/landing/src/app/homepage/page.tsx b/apps/landing/src/app/homepage/page.tsx index 9d7668d..566aefc 100644 --- a/apps/landing/src/app/homepage/page.tsx +++ b/apps/landing/src/app/homepage/page.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState } from 'react'; +import { AnimatedGradientBorder } from './components/AnimatedGradientBorder'; import { Zap, Globe, @@ -42,12 +43,6 @@ import { const customStyles = ` @import url('https://fonts.googleapis.com/css2?family=Caveat:wght@500;600;700&display=swap'); - @property --form-angle { - syntax: ""; - initial-value: 0deg; - inherits: false; - } - .gradient-text { background: linear-gradient(90deg, #818cf8 0%, #c084fc 25%, #f472b6 50%, #c084fc 75%, #818cf8 100%); background-size: 200% 100%; @@ -63,47 +58,6 @@ const customStyles = ` 100% { background-position: 100% 50%; } } - .email-form-wrapper { - position: relative; - max-width: 28rem; - margin: 0 auto; - padding: 2px; - border-radius: 12px; - background: linear-gradient(#0a0612, #0a0612) padding-box, - conic-gradient(from var(--form-angle), - rgba(99, 102, 241, 0.3), - rgba(139, 92, 246, 0.8), - rgba(236, 72, 153, 0.6), - rgba(34, 211, 238, 0.8), - rgba(99, 102, 241, 0.3) - ) border-box; - animation: form-glow-rotate 4s linear infinite; - } - - .email-form-wrapper::before { - content: ''; - position: absolute; - inset: -2px; - border-radius: 14px; - background: conic-gradient(from var(--form-angle), - rgba(99, 102, 241, 0.1), - rgba(139, 92, 246, 0.4), - rgba(236, 72, 153, 0.3), - rgba(34, 211, 238, 0.4), - rgba(99, 102, 241, 0.1) - ); - filter: blur(15px); - opacity: 0.6; - z-index: -1; - animation: form-glow-rotate 4s linear infinite; - } - - @keyframes form-glow-rotate { - to { - --form-angle: 360deg; - } - } - .beta-dot { animation: beta-dot-delay 20s linear forwards, beta-dot-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) 20s infinite; } @@ -138,12 +92,30 @@ const customStyles = ` function BackgroundBlobs() { const blobs = [ - { className: 'w-[600px] h-[600px] top-[-200px] right-[-100px]', gradient: 'rgba(139, 92, 246, 0.3)' }, - { className: 'w-[500px] h-[500px] top-[800px] left-[-150px]', gradient: 'rgba(99, 102, 241, 0.25)' }, - { className: 'w-[400px] h-[400px] top-[1600px] right-[-100px]', gradient: 'rgba(236, 72, 153, 0.2)' }, - { className: 'w-[550px] h-[550px] top-[2400px] left-[-200px]', gradient: 'rgba(34, 211, 238, 0.15)' }, - { className: 'w-[450px] h-[450px] top-[3200px] right-[-150px]', gradient: 'rgba(139, 92, 246, 0.25)' }, - { className: 'w-[500px] h-[500px] top-[4000px] left-[-100px]', gradient: 'rgba(99, 102, 241, 0.2)' }, + { + className: 'w-[600px] h-[600px] top-[-200px] right-[-100px]', + gradient: 'rgba(139, 92, 246, 0.3)', + }, + { + className: 'w-[500px] h-[500px] top-[800px] left-[-150px]', + gradient: 'rgba(99, 102, 241, 0.25)', + }, + { + className: 'w-[400px] h-[400px] top-[1600px] right-[-100px]', + gradient: 'rgba(236, 72, 153, 0.2)', + }, + { + className: 'w-[550px] h-[550px] top-[2400px] left-[-200px]', + gradient: 'rgba(34, 211, 238, 0.15)', + }, + { + className: 'w-[450px] h-[450px] top-[3200px] right-[-150px]', + gradient: 'rgba(139, 92, 246, 0.25)', + }, + { + className: 'w-[500px] h-[500px] top-[4000px] left-[-100px]', + gradient: 'rgba(99, 102, 241, 0.2)', + }, ]; return ( @@ -164,7 +136,8 @@ function HeroGlow() {
); @@ -215,7 +188,7 @@ function HeroSection() {

{/* Email Form */} -
+
- + {badge.text} ))} @@ -277,13 +252,17 @@ function ApiExampleSection() {
# Generate an image
- curl -X POST https://api.banatie.app/v1/generate \ + curl{' '} + -X POST https://api.banatie.app/v1/generate \
- -H "Authorization: Bearer $API_KEY"{' '} + -H{' '} + "Authorization: Bearer $API_KEY"{' '} \
-d{' '} - '{`{"prompt": "modern office interior, natural light"}`}' + + '{`{"prompt": "modern office interior, natural light"}`}' +
@@ -291,11 +270,15 @@ function ApiExampleSection() { {'{'}
"url" - : "https://cdn.banatie.app/img/a7x2k9.png" + :{' '} + + "https://cdn.banatie.app/img/a7x2k9.png" + ,
"enhanced_prompt" - : "A photorealistic modern office..." + :{' '} + "A photorealistic modern office..." ,
"generation_time" @@ -348,8 +331,12 @@ function ProblemSolutionSection() { return (
-

Why developers choose Banatie

-

Stop fighting your image workflow. Start building.

+

+ Why developers choose Banatie +

+

+ Stop fighting your image workflow. Start building. +

{problems.map((item, i) => ( @@ -390,11 +377,14 @@ function PromptUrlsSection() {
- Unique + + Unique +

Prompt URLs — Images via HTML

- Put a prompt in your img src and get a real image. No API calls. No - JavaScript. Just HTML. + Put a prompt in your{' '} + img src and get a + real image. No API calls. No JavaScript. Just HTML.

@@ -402,15 +392,22 @@ function PromptUrlsSection() {
<!-- Write this -->
- <img src= - "https://cdn.banatie.app/gen?p=modern office interior"{' '} + <img{' '} + src= + + "https://cdn.banatie.app/gen?p=modern office interior" + {' '} />

- <!-- Get this: production-ready image, cached, CDN-delivered --> + + <!-- Get this: production-ready image, cached, CDN-delivered --> +
-

Perfect for static sites, prototypes, and AI coding agents that generate HTML.

+

+ Perfect for static sites, prototypes, and AI coding agents that generate HTML. +

@@ -438,8 +435,12 @@ function HowItWorksSection() { return (
-

Your prompt. Your control. Production-ready.

-

We handle the complexity so you can focus on building.

+

+ Your prompt. Your control. Production-ready. +

+

+ We handle the complexity so you can focus on building. +

@@ -491,42 +492,48 @@ function KeyFeaturesSection() { icon: AtSign, iconColor: 'text-pink-400', title: 'Reference Images', - description: 'Use @aliases to maintain style consistency across your project. Reference up to 3 images per generation.', + description: + 'Use @aliases to maintain style consistency across your project. Reference up to 3 images per generation.', isUnique: false, }, { icon: GitBranch, iconColor: 'text-purple-400', title: 'Flows', - description: 'Chain generations, iterate on results, build image sequences with @last and @first references.', + description: + 'Chain generations, iterate on results, build image sequences with @last and @first references.', isUnique: false, }, { icon: Palette, iconColor: 'text-yellow-400', title: '7 Style Templates', - description: 'Same prompt, different styles. Photorealistic, illustration, minimalist, product, comic, sticker, and more.', + description: + 'Same prompt, different styles. Photorealistic, illustration, minimalist, product, comic, sticker, and more.', isUnique: false, }, { icon: Globe, iconColor: 'text-green-400', title: 'Instant CDN Delivery', - description: 'Every image gets production-ready URL. No upload, no optimization, no hosting setup needed.', + description: + 'Every image gets production-ready URL. No upload, no optimization, no hosting setup needed.', isUnique: false, }, { icon: SlidersHorizontal, iconColor: 'text-blue-400', title: 'Output Control', - description: 'Control aspect ratio, dimensions, and format. From square thumbnails to ultra-wide banners.', + description: + 'Control aspect ratio, dimensions, and format. From square thumbnails to ultra-wide banners.', isUnique: false, }, { icon: Link, iconColor: 'text-cyan-400', title: 'Prompt URLs', - description: 'Generate images via URL parameters. Put prompt in img src, get real image. Built-in caching.', + description: + 'Generate images via URL parameters. Put prompt in img src, get real image. Built-in caching.', isUnique: true, }, ]; @@ -534,8 +541,12 @@ function KeyFeaturesSection() { return (
-

Built for real development workflows

-

Everything you need to integrate AI images into your projects.

+

+ Built for real development workflows +

+

+ Everything you need to integrate AI images into your projects. +

{features.map((feature, i) => ( @@ -552,7 +563,11 @@ function KeyFeaturesSection() {

{feature.title}

- {feature.isUnique && Unique} + {feature.isUnique && ( + + Unique + + )}

{feature.description}

@@ -581,7 +596,9 @@ function IntegrationsSection() {

Works with your tools

-

Use what fits your workflow. All methods, same capabilities.

+

+ Use what fits your workflow. All methods, same capabilities. +

{tools.map((tool, i) => ( @@ -599,12 +616,15 @@ function IntegrationsSection() {

- Banatie Lab — Official web interface for Banatie API. Generate images, build flows, browse your - gallery, and explore all capabilities with ready-to-use code snippets. + Banatie Lab — Official web interface for Banatie + API. Generate images, build flows, browse your gallery, and explore all capabilities + with ready-to-use code snippets.

-

Perfect for Claude Code, Cursor, and any AI-powered workflow.

+

+ Perfect for Claude Code, Cursor, and any AI-powered workflow. +

); @@ -625,11 +645,14 @@ function ShapeTheFutureSection() {
-

Shape the future of Banatie

+

+ Shape the future of Banatie +

- We're building this for developers like you. Early adopters get direct influence on our roadmap — suggest features, vote on - priorities, and help us build exactly what you need. + We're building this for developers like you. Early adopters get direct influence on + our roadmap — suggest features, vote on priorities, and help us build exactly what you + need.

@@ -666,10 +689,30 @@ function GeminiSection() { ]; const capabilities = [ - { icon: Type, title: 'Perfect Text Rendering', description: 'Legible text in images — logos, diagrams, posters. What other models still struggle with.' }, - { icon: Brain, title: 'Native Multimodal', description: 'Understands text AND images in one model. Not a text model + image model bolted together.' }, - { icon: Target, title: 'Precise Prompt Following', description: 'What you ask is what you get. No artistic "interpretation" that ignores your instructions.' }, - { icon: Image, title: 'Professional Realism', description: 'Photorealistic output that replaces stock photos. Not fantasy art — real, usable images.' }, + { + icon: Type, + title: 'Perfect Text Rendering', + description: + 'Legible text in images — logos, diagrams, posters. What other models still struggle with.', + }, + { + icon: Brain, + title: 'Native Multimodal', + description: + 'Understands text AND images in one model. Not a text model + image model bolted together.', + }, + { + icon: Target, + title: 'Precise Prompt Following', + description: + 'What you ask is what you get. No artistic "interpretation" that ignores your instructions.', + }, + { + icon: Image, + title: 'Professional Realism', + description: + 'Photorealistic output that replaces stock photos. Not fantasy art — real, usable images.', + }, ]; return ( @@ -683,8 +726,9 @@ function GeminiSection() {

Powered by Google Gemini

- We chose Gemini because it's the only model family that combines native multimodal understanding with production-grade image - generation. Two models, optimized for different needs. + We chose Gemini because it's the only model family that combines native + multimodal understanding with production-grade image generation. Two models, optimized + for different needs.

@@ -702,7 +746,8 @@ function GeminiSection() {

- Optimized for speed and iteration. Perfect for rapid prototyping and high-volume generation. + Optimized for speed and iteration. Perfect for rapid prototyping and high-volume + generation.

    {flashFeatures.map((feature, i) => ( @@ -728,7 +773,8 @@ function GeminiSection() {

- Maximum quality and creative control. For production assets and professional workflows. + Maximum quality and creative control. For production assets and professional + workflows.

    {proFeatures.map((feature, i) => ( @@ -745,7 +791,9 @@ function GeminiSection() { {/* Shared Capabilities */}
    -

    Why Gemini outperforms competitors

    +

    + Why Gemini outperforms competitors +

    {capabilities.map((cap, i) => (
    @@ -762,7 +810,8 @@ function GeminiSection() { {/* #1 Ranking Note */}

    - Gemini 2.5 Flash Image ranked #1 on LMArena for both text-to-image and image editing (August 2025) + Gemini 2.5 Flash Image ranked #1 on LMArena for both text-to-image and image editing + (August 2025)

    @@ -796,7 +845,8 @@ function FinalCtaSection() {
    @@ -804,12 +854,15 @@ function FinalCtaSection() {
    -

    Ready to build?

    +

    + Ready to build? +

    Join developers waiting for early access. We'll notify you when your spot is ready.

    @@ -822,7 +875,9 @@ function FinalCtaSection() { -

    No credit card required • Free to start • Cancel anytime

    +

    + No credit card required • Free to start • Cancel anytime +

); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86b1387..b590f78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,6 +196,9 @@ importers: '@banatie/database': specifier: workspace:* version: link:../../packages/database + lucide-react: + specifier: ^0.400.0 + version: 0.400.0(react@19.1.0) next: specifier: 15.5.4 version: 15.5.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -9192,6 +9195,10 @@ snapshots: dependencies: react: 18.3.1 + lucide-react@0.400.0(react@19.1.0): + dependencies: + react: 19.1.0 + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5