diff --git a/apps/blogs/.vercel/project.json b/apps/blogs/.vercel/project.json new file mode 100644 index 0000000..576f6a3 --- /dev/null +++ b/apps/blogs/.vercel/project.json @@ -0,0 +1,16 @@ +{ + "projectId": "prj_NgfV08gylZB0IMTiK3SBS9eG7BFa", + "orgId": "team_fRT3JqS9WXaErZ3MPOJqCcdg", + "projectName": "vectorless-blog", + "settings": { + "createdAt": 1781973251317, + "framework": "nextjs", + "devCommand": null, + "installCommand": null, + "buildCommand": null, + "outputDirectory": null, + "rootDirectory": "apps/blogs", + "directoryListing": false, + "nodeVersion": "24.x" + } +} diff --git a/apps/blogs/app/VectorlessIcon.tsx b/apps/blogs/app/VectorlessIcon.tsx new file mode 100644 index 0000000..977e1d2 --- /dev/null +++ b/apps/blogs/app/VectorlessIcon.tsx @@ -0,0 +1,50 @@ +import { cn } from "@/lib/utils"; + +interface VectorlessIconProps { + size?: number; + className?: string; +} + +export function VectorlessIcon({ size = 32, className }: VectorlessIconProps) { + return ( + + + + + + + + + ); +} + +export function VectorlessDot({ size = 16, className }: VectorlessIconProps) { + return ( + + + + + ); +} + +export default VectorlessIcon; diff --git a/apps/blogs/app/globals.css b/apps/blogs/app/globals.css index c9a35af..475d2e5 100644 --- a/apps/blogs/app/globals.css +++ b/apps/blogs/app/globals.css @@ -1,40 +1,115 @@ @import "tailwindcss"; @import "tw-animate-css"; +@custom-variant dark (&:is(.dark *)); + @theme { --font-sans: var(--font-geist), ui-sans-serif, system-ui, sans-serif; - --font-display: var(--font-plus-jakarta), var(--font-geist), ui-sans-serif, system-ui, sans-serif; - --font-mono: var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace; + --font-display: var(--font-geist), ui-sans-serif, system-ui, sans-serif; + --font-mid: var(--font-geist), ui-sans-serif, system-ui, sans-serif; + --font-data: var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace; --font-serif: var(--font-instrument-serif), ui-serif, Georgia, serif; - --color-bg-base: #FAF6EE; - --color-bg-paper: #FAF8F5; - --color-text-base: #0F0F0F; - --color-text-secondary: #3F3F3E; - --color-text-muted: #70706F; + --color-bg-base: #ffffff; + --color-bg-dark: #0a0a0a; + --color-bg-deep: #050505; + + --color-text-base: #0a0a0a; + --color-text-dark: #0a0a0a; + --color-text-secondary: #3f3f36; + --color-text-muted: #71717a; + --color-brand-blue: #1456f0; --color-brand-pink: #ea5ec1; - --color-border-light: #F0EDE6; - --color-border-gray: #E3DFD5; + --color-primary-500: #3b82f6; + --color-primary-600: #2563eb; + + --color-border-light: #f2f3f5; + --color-border-gray: #e5e7eb; + + /* shadcn/ui color mappings for Tailwind v4 */ + --color-background: hsl(var(--background)); + --color-foreground: hsl(var(--foreground)); + --color-card: hsl(var(--card)); + --color-card-foreground: hsl(var(--card-foreground)); + --color-popover: hsl(var(--popover)); + --color-popover-foreground: hsl(var(--popover-foreground)); + --color-primary: hsl(var(--primary)); + --color-primary-foreground: hsl(var(--primary-foreground)); + --color-secondary: hsl(var(--secondary)); + --color-secondary-foreground: hsl(var(--secondary-foreground)); + --color-muted: hsl(var(--muted)); + --color-muted-foreground: hsl(var(--muted-foreground)); + --color-accent: hsl(var(--accent)); + --color-accent-foreground: hsl(var(--accent-foreground)); + --color-destructive: hsl(var(--destructive)); + --color-destructive-foreground: hsl(var(--destructive-foreground)); + --color-border: hsl(var(--border)); + --color-input: hsl(var(--input)); + --color-ring: hsl(var(--ring)); + + --animate-gradient: gradient 8s linear infinite; + @keyframes gradient { + to { background-position: 200% center; } + } +} + +/* ─── shadcn/ui Design Tokens ─── */ +:root { + --background: 0 0% 100%; + --foreground: 0 0% 13.3%; + --card: 0 0% 100%; + --card-foreground: 0 0% 13.3%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 13.3%; + --primary: 222 89% 51%; + --primary-foreground: 0 0% 100%; + --secondary: 210 15% 95%; + --secondary-foreground: 210 15% 30%; + --muted: 210 15% 96%; + --muted-foreground: 210 10% 45%; + --accent: 210 15% 95%; + --accent-foreground: 210 15% 30%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 100%; + --border: 220 13% 91%; + --input: 220 13% 91%; + --ring: 222 89% 51%; + --radius: 0.625rem; } @layer base { html, body { - background-color: #FAF6EE !important; - color: #0F0F0F; + background-color: #ffffff !important; + color: #0a0a0a; margin: 0; padding: 0; width: 100%; height: 100%; } + + * { + border-color: hsl(var(--border)); + } } @layer utilities { + .hide-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; + } + .hide-scrollbar::-webkit-scrollbar { + display: none; + } + .grid-paper { background-image: - linear-gradient(to right, rgba(20, 86, 240, 0.03) 1px, transparent 1px), - linear-gradient(to bottom, rgba(20, 86, 240, 0.03) 1px, transparent 1px); + linear-gradient(to right, rgba(20, 86, 240, 0.05) 1px, transparent 1px), + linear-gradient(to bottom, rgba(20, 86, 240, 0.05) 1px, transparent 1px); background-size: 56px 56px; } -} + .text-balance { + text-wrap: balance; + } +} diff --git a/apps/blogs/app/layout.tsx b/apps/blogs/app/layout.tsx index 52204ab..e2df046 100644 --- a/apps/blogs/app/layout.tsx +++ b/apps/blogs/app/layout.tsx @@ -1,10 +1,9 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono, Instrument_Serif, Plus_Jakarta_Sans } from "next/font/google"; +import { Geist, Geist_Mono, Instrument_Serif } from "next/font/google"; import "./globals.css"; const geist = Geist({ subsets: ["latin"], variable: "--font-geist" }); const geistMono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" }); -const plusJakartaSans = Plus_Jakarta_Sans({ subsets: ["latin"], variable: "--font-plus-jakarta" }); const instrumentSerif = Instrument_Serif({ weight: "400", subsets: ["latin"], @@ -24,10 +23,10 @@ export default function RootLayout({ return ( {children} diff --git a/apps/blogs/app/page.tsx b/apps/blogs/app/page.tsx index 031874f..f680adb 100644 --- a/apps/blogs/app/page.tsx +++ b/apps/blogs/app/page.tsx @@ -1,24 +1,20 @@ 'use client'; -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'motion/react'; +import Link from 'next/link'; import { ArrowRight, X, Calendar, Clock, - Check, - Sparkles, - Search, - FileText, - Database, - Cpu, - Play, + Search, + BookOpen, Terminal, - ArrowUpRight, - BookOpen, - ChevronRight + ChevronRight, + Menu } from 'lucide-react'; +import { VectorlessIcon, VectorlessDot } from './VectorlessIcon'; // Categories matching our blog categories type Category = 'All' | 'Product Updates' | 'Engineering' | 'Guides' | 'Features'; @@ -155,50 +151,22 @@ const BLOG_POSTS: BlogPost[] = [ } ]; -function VectorlessIcon({ size = 32, className }: { size?: number; className?: string }) { - return ( - - - - - - - - - ); -} - -// Highly stylized schematic vectors +// Technical blueprint vectors function BlueprintIllustration({ type }: { type: BlogPost['imageType'] }) { if (type === 'architecture') { return ( - {/* Coordinates grid back layer */} - - {/* Section nodes tree representation */} - - - - - {/* Abstract border paths */} @@ -208,13 +176,10 @@ function BlueprintIllustration({ type }: { type: BlogPost['imageType'] }) { return ( - - {/* Neon database cylinders in technical drawing format */} - @@ -225,7 +190,6 @@ function BlueprintIllustration({ type }: { type: BlogPost['imageType'] }) { return ( - {/* Abstract syntax-highlighted code blocks */} @@ -248,6 +212,8 @@ function BlueprintIllustration({ type }: { type: BlogPost['imageType'] }) { export default function BlogPage() { const [selectedCategory, setSelectedCategory] = useState('All'); const [activeArticle, setActiveArticle] = useState(null); + const [scrolled, setScrolled] = useState(false); + const [isNavOpen, setIsNavOpen] = useState(false); // Interactive Simulation state const [showDemoModal, setShowDemoModal] = useState(false); @@ -258,24 +224,38 @@ export default function BlogPage() { const [retrievedResult, setRetrievedResult] = useState(null); const [highlightedSection, setHighlightedSection] = useState(null); - // Auto processing steps on mock document load + // Monitor scroll for glass navbar effect + useEffect(() => { + const handleScroll = () => setScrolled(window.scrollY > 24); + window.addEventListener('scroll', handleScroll, { passive: true }); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + const openSandbox = () => { + setIngestionStep(0); + setRetrievedResult(null); + setHighlightedSection(null); + setShowDemoModal(true); + }; + + const selectDoc = (docId: typeof selectedDoc) => { + setIngestionStep(0); + setRetrievedResult(null); + setHighlightedSection(null); + setSelectedDoc(docId); + }; + + // Ingestion simulation steps useEffect(() => { if (showDemoModal) { - // eslint-disable-next-line react-hooks/set-state-in-effect - setIngestionStep(0); - // eslint-disable-next-line react-hooks/set-state-in-effect - setRetrievedResult(null); - // eslint-disable-next-line react-hooks/set-state-in-effect - setHighlightedSection(null); - - const timer1 = setTimeout(() => setIngestionStep(1), 700); - const timer2 = setTimeout(() => setIngestionStep(2), 1400); - const timer3 = setTimeout(() => setIngestionStep(3), 2100); + const t1 = setTimeout(() => setIngestionStep(1), 700); + const t2 = setTimeout(() => setIngestionStep(2), 1400); + const t3 = setTimeout(() => setIngestionStep(3), 2100); return () => { - clearTimeout(timer1); - clearTimeout(timer2); - clearTimeout(timer3); + clearTimeout(t1); + clearTimeout(t2); + clearTimeout(t3); }; } }, [showDemoModal, selectedDoc]); @@ -331,71 +311,88 @@ export default function BlogPage() { return post.category === selectedCategory; }); - // Editorial varying card layouts: - // Post 1 -> Featured Hero const featuredPost = BLOG_POSTS.find(p => p.id === 'retrieval-reasoning-era'); - // Grid layout split: - // If category is All, show Hero at top, and others in grid. const displayPosts = selectedCategory === 'All' ? BLOG_POSTS.filter(p => p.id !== 'retrieval-reasoning-era') : filteredPosts; return ( -
+
- {/* Editorial top accent line */} -
+ {/* Decorative gradient overlay matching landing page */} +
- {/* Subtle blueprint grid backdrop - hidden when reading article */} + {/* Grid Backdrop (Only visible on main index page) */} {!activeArticle && ( -
+
)} - {/* Main Content Wrapper */} -
- - {/* Header Block */} -
-
setActiveArticle(null)} - className="flex items-center gap-3 cursor-pointer group" + {/* FLOATING GLASS PILL NAVBAR */} +
+
+ + {/* Mobile menu container */} + {isNavOpen && ( +
+ setIsNavOpen(false)} className="text-[15px] font-medium text-[#0A0A0A] p-2 rounded-lg hover:bg-black/5">How it works + setIsNavOpen(false)} className="text-[15px] font-medium text-[#0A0A0A] p-2 rounded-lg hover:bg-black/5">Docs + setIsNavOpen(false)} className="text-[15px] font-medium text-[#0A0A0A] p-2 rounded-lg hover:bg-black/5">Whitepaper + Blog + setIsNavOpen(false)} className="text-[15px] font-medium text-[#0A0A0A] p-2 rounded-lg hover:bg-black/5">Pricing +
+ setIsNavOpen(false)} className="text-[15px] font-medium text-[#0A0A0A] p-2 rounded-lg hover:bg-black/5">Login + setIsNavOpen(false)} className="bg-bg-dark text-white px-4 py-3 rounded-full text-[14px] font-medium hover:bg-black transition-colors flex items-center justify-center mt-1"> + Start free → + +
+ )} + + {/* Main Container */} +
+ {activeArticle ? ( - /* FULL PAGE ARTICLE READER VIEW (Warm cream background, flat, no grid lines) */ + /* FULL PAGE ARTICLE READER VIEW (Sleek Clean Canvas, Minimal Grid) */ -
- {activeArticle.category} +
+ {activeArticle.category} {activeArticle.date} {activeArticle.readTime}
-

+

{activeArticle.title}

- {/* Author Card */} -
-
+ {/* Author Row */} +
+
{activeArticle.author.name}
- {activeArticle.author.name} - {activeArticle.author.role} + {activeArticle.author.name} + {activeArticle.author.role}
- {/* Large Accent Illustration */} -
+ {/* Blueprint Illustration Card */} +
- {/* Article Content with Premium Typography */} -
+ {/* Article Content */} +
{activeArticle.content.map((paragraph, pIdx) => { - // If it starts with a number list item like "1. " if (/^\d+\.\s/.test(paragraph)) { const parts = paragraph.split(/^\d+\.\s/); const titleAndText = parts[1].split(/:\s/); return ( -
+
{titleAndText.length > 1 ? ( <> -

{titleAndText[0]}

-

{titleAndText[1]}

+

{titleAndText[0]}

+

{titleAndText[1]}

) : ( -

{parts[1]}

+

{parts[1]}

)}
); } return ( -

+

{paragraph}

); })}
- {/* Back button at the bottom */} -
+ {/* Back to Library Index */} +
- + File: {activeArticle.id}.log
@@ -491,23 +487,41 @@ export default function BlogPage() { exit={{ opacity: 0 }} transition={{ duration: 0.3 }} > - {/* Hero Section */} -
-
- - Volume IV · Technical Library - -

- Document retrieval for the reasoning era. + {/* Hero Header matching landing page style */} +
+
+
+ + + + + + Vectorless Intelligence Log + +
+ +

+ Document retrieval for the + + + reasoning era. + + {/* Underline vector */} + + + + +

-

- Deep dives, implementation specifications, and performance analyses of structure-preserving retrieval architectures. + +

+ Deep dives, engineering specifications, and core performance analyses of structure-preserving retrieval architectures.

- {/* Categories Bar */} -
+ {/* Categories Navigation Bar */} +
{categories.map((cat) => { const isActive = selectedCategory === cat; @@ -515,10 +529,10 @@ export default function BlogPage() {
-
- - Format: Structural / Open-Source Spec -
+
- {/* Featured Editorial Hero Article */} + {/* Featured Article Layout */} {selectedCategory === 'All' && featuredPost && ( setActiveArticle(featuredPost)} - className="py-12 border-b border-[#E3DFD5] cursor-pointer group" + className="py-12 border-b border-[#E5E7EB] cursor-pointer group" > -
+
-
- {featuredPost.category} +
+ {featuredPost.category} {featuredPost.date} {featuredPost.readTime}
-

+

{featuredPost.title}

-

+

{featuredPost.snippet}

-
+
Analyze Specification - +
-
+
@@ -578,11 +595,9 @@ export default function BlogPage() { )} - {/* Magazine Grid List */} + {/* Post Grid List */}
-
+
{displayPosts.map((post, idx) => ( setActiveArticle(post)} - className="flex flex-col justify-between cursor-pointer group h-full pb-8 border-b border-[#E3DFD5]/80" + className="flex flex-col justify-between cursor-pointer group h-full pb-8 border-b border-[#E5E7EB]" >
- {/* Header meta */} -
- {post.category} +
+ {post.category} {post.date}
- {/* Title */} -

+

{post.title}

- {/* Snippet */} -

+

{post.snippet}

- {/* Card Illustration */} -
+
- {/* Author */}
-
+
{post.author.name}
- {post.author.name} - {post.author.role} + {post.author.name} + {post.author.role}
))} - {/* Empty State */} {displayPosts.length === 0 && ( -
- No matching logs found +
+ No matching logs found @@ -653,18 +662,18 @@ export default function BlogPage() {
{/* Footer */} -