diff --git a/apps/seo-www/content/backlink-experiment-results.mdx b/apps/seo-www/content/backlink-experiment-results.mdx new file mode 100644 index 000000000..adefbb2e9 --- /dev/null +++ b/apps/seo-www/content/backlink-experiment-results.mdx @@ -0,0 +1,179 @@ +--- +title: "We Built 50 Backlinks in 30 Days: Here's What Happened" +description: "An experiment in authority building—testing different backlink strategies and measuring their impact on rankings and traffic." +icon: "FlaskConical" +cover: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=1600&auto=format&fit=crop" +author: "winston" +tags: + - "SEO Experiments" + - "backlinks" + - "case study" + - "authority" +--- + + +This is part of our ongoing SEO Experiments series, where we test common SEO advice and share real results. + + +## The Experiment + +Everyone says backlinks are crucial for SEO. But with so many conflicting strategies—guest posting, broken link building, HARO, digital PR—how do you know what actually works? + +We decided to find out. Over 30 days, we systematically built 50 backlinks using five different methods, tracking the impact on rankings, traffic, and domain authority. + +## The Setup + +**Test site:** A 6-month-old SaaS blog with a Domain Rating (DR) of 15 +**Starting traffic:** ~500 monthly organic visitors +**Target keywords:** 10 commercial-intent terms in the productivity space +**Baseline rankings:** All keywords ranking 15-50 + +### Methods Tested + +We allocated 10 backlinks to each method: + +1. **Guest posting** on niche-relevant blogs (DR 30-50) +2. **Broken link building** on resource pages +3. **HARO/Connectively** responses to journalist queries +4. **Content partnerships** with complementary tools +5. **Link insertions** into existing articles via outreach + +## Results Overview + +| Method | Links Built | Referring Domains | Avg. DR | Traffic Impact | +|--------|------------|-------------------|---------|----------------| +| Guest posting | 10 | 10 | 38 | +12% | +| Broken link building | 8 | 8 | 42 | +8% | +| HARO | 6 | 6 | 61 | +18% | +| Content partnerships | 10 | 5 | 35 | +22% | +| Link insertions | 10 | 10 | 31 | +6% | + +**Total impact:** 66% increase in organic traffic (500 → 830 monthly visitors) + +## Detailed Findings + +### Guest Posting: Reliable but Time-Intensive + +Guest posting delivered consistent results—every pitch that got accepted resulted in a link. However, it required significant time investment: approximately 4 hours per published post including research, writing, and revisions. + +**What worked:** +- Pitching unique angles rather than generic topics +- Targeting sites that had linked to competitors +- Including internal links in the guest post (when allowed) + +**What didn't:** +- Pitching sites outside our niche (low acceptance rate) +- Overly promotional content (rejected or heavily edited) + +### Broken Link Building: High Quality, Low Volume + +The links we secured through broken link building were among the highest quality—resource pages on established sites. However, the success rate was low (8 links from 45 prospects). + +**The process:** +1. Find resource pages in our niche +2. Run them through a broken link checker +3. Identify relevant broken links +4. Create replacement content (or use existing) +5. Pitch the site owner + +**Success rate:** 18% (8/45 prospects) + +### HARO: Best ROI for Authority + +HARO (now Connectively) delivered the highest-authority links with the least effort per link. However, acceptance rates were unpredictable—we submitted 50+ responses to land 6 links. + +**Key insight:** High-DR publications have massive ranking influence. One link from a DR 75 site moved our target keyword from position 23 to position 11. + +**Tips that worked:** +- Responding within 30 minutes of query posting +- Providing specific data or unique insights +- Including credentials and social proof + +### Content Partnerships: Best for Referral Traffic + +Content partnerships—where we collaborated on guides, webinars, or tools with complementary businesses—generated the most referral traffic in addition to SEO value. + +**Example:** We co-created a "Productivity Stack Calculator" with a project management tool. They linked to it from their resources page; we linked to them from ours. Both sites benefited from the shared traffic. + +**Bonus:** These relationships often lead to additional link opportunities over time. + +### Link Insertions: Easiest but Lowest Impact + +Reaching out to site owners with relevant existing content and asking them to add our link was the easiest method—but also the lowest impact. Many of these links were on pages with thin content or low traffic. + +**Best for:** Building link velocity and diversifying anchor text + +## Ranking Changes + +Here's how our target keywords moved during the experiment: + +| Keyword | Starting Position | Ending Position | Change | +|---------|-------------------|-----------------|--------| +| "productivity app comparison" | 23 | 11 | +12 | +| "best task manager 2024" | 31 | 19 | +12 | +| "project management for freelancers" | 18 | 9 | +9 | +| "todo app alternatives" | 45 | 28 | +17 | +| "time tracking software review" | 22 | 14 | +8 | + +Most significant movements came from: +1. High-DR HARO links +2. Links from topically relevant sites (guest posts, partnerships) +3. Links on high-traffic pages (broken link building) + +## What We'd Do Differently + +### Prioritize Quality Over Quantity + +Our initial goal was 50 links. In hindsight, 20 high-quality links would have been more valuable than 50 mixed-quality ones. + +### Start HARO Earlier + +HARO links took the longest to secure (journalist timelines) but had the biggest impact. We'd allocate more time to this channel in future experiments. + +### Build Relationships First + +The content partnerships that worked best came from existing relationships. Cold outreach for partnerships had a much lower success rate. + +## Recommendations by Site Stage + +### New sites (DR < 20) +Focus on: Guest posting, content partnerships +Rationale: Build foundational authority and relationships + +### Growing sites (DR 20-40) +Focus on: HARO, broken link building +Rationale: Secure high-authority links that move the needle + +### Established sites (DR 40+) +Focus on: Digital PR, original research +Rationale: Create link-worthy assets that attract links naturally + +## The Fluid Posts Approach + +At Fluid Posts, we've systematized authority building through our network. Instead of manually building links one-by-one, our members benefit from: + +- **Mutual link exchanges** with relevant businesses +- **Curated partnerships** based on topical alignment +- **Quality controls** that ensure links benefit all parties + +The result: consistent authority growth without the outreach grind. + +## Try It Yourself + +Want to replicate this experiment? Here's a simplified 30-day plan: + +**Week 1:** Set up tracking, identify 20 guest post targets +**Week 2:** Submit 5 guest post pitches, sign up for HARO +**Week 3:** Start broken link prospecting, respond to 10+ HARO queries +**Week 4:** Reach out for content partnerships, follow up on all pitches + +Track your results and share what you learn—we'd love to hear about it. + + + + Build authority through our backlink community. + + + See all our experiment results and case studies. + + diff --git a/apps/seo-www/content/geo-seo-future.mdx b/apps/seo-www/content/geo-seo-future.mdx new file mode 100644 index 000000000..90823081b --- /dev/null +++ b/apps/seo-www/content/geo-seo-future.mdx @@ -0,0 +1,146 @@ +--- +title: "GEO vs SEO: Why AI Search Changes Everything" +description: "Generative Engine Optimization is reshaping how brands get discovered. Here's what you need to know about optimizing for ChatGPT, Perplexity, and AI Overviews." +icon: "Sparkles" +cover: "https://images.unsplash.com/photo-1677442136019-21780ecad995?q=80&w=1600&auto=format&fit=crop" +author: "winston" +tags: + - "SEO Insights" + - "GEO" + - "AI search" + - "strategy" +--- + + +This guide covers the emerging field of Generative Engine Optimization (GEO)—optimizing your content for AI-powered search experiences. + + +## The Search Landscape Is Shifting + +For two decades, SEO meant optimizing for Google's blue links. You'd research keywords, create content, build backlinks, and climb the rankings. Simple enough in theory. + +But 2024 changed everything. ChatGPT now handles millions of search-like queries daily. Google's AI Overviews provide direct answers above traditional results. Perplexity offers citation-driven research experiences. And this is just the beginning. + +## What Is GEO? + +**Generative Engine Optimization (GEO)** is the practice of optimizing content to be surfaced, cited, and recommended by AI systems. While traditional SEO focuses on ranking in search results, GEO focuses on being included in AI-generated responses. + +The key difference? AI systems don't just rank pages—they synthesize information, cite sources, and directly answer user questions. Your content needs to be the source they draw from. + +## How AI Systems Evaluate Content + +AI search engines evaluate content differently than traditional search: + +### 1. Factual Accuracy and Citations + +AI systems prefer content that makes verifiable claims with clear sources. They cross-reference information across multiple pages before including it in responses. + +**What this means for you:** +- Include specific data points and statistics +- Cite reputable sources +- Update content when information changes +- Avoid vague or unsupported claims + +### 2. Comprehensive Coverage + +AI systems are trained to provide complete answers. Content that thoroughly addresses a topic is more likely to be cited than surface-level overviews. + +**What this means for you:** +- Create definitive guides rather than thin content +- Address related questions within your content +- Structure information clearly with headers and lists +- Include multiple perspectives when relevant + +### 3. Entity Recognition + +AI models understand entities—people, places, organizations, concepts—and their relationships. Content that clearly establishes entity connections is easier for AI to process and cite. + +**What this means for you:** +- Use consistent naming for your brand and products +- Link to authoritative sources that define key concepts +- Include structured data (Schema.org markup) +- Create content that positions your brand as an authority on specific topics + +## The GEO Playbook + +Here's how to optimize your content for AI search: + +### Write for Direct Answers + +AI systems often pull content that directly answers questions. Structure your content accordingly: + +``` +❌ "Many factors influence pricing..." +✅ "The average cost of X is $500-2000, depending on Y and Z." +``` + +### Create Question-Focused Content + +AI systems heavily weight content that matches user query patterns. Include natural questions and answers throughout your content: + +- "What is [topic]?" +- "How does [topic] work?" +- "Why is [topic] important?" +- "[Topic] vs [alternative]: which is better?" + +### Build Topical Authority + +AI systems prefer citing sources with demonstrated expertise. To build authority: + +1. Create comprehensive hub pages on core topics +2. Link related content together with clear internal links +3. Publish consistently on your focus areas +4. Get cited by other authoritative sources + +### Optimize for Citations + +When AI systems cite your content, they typically pull: + +- Your page title +- A brief description or key sentence +- Your domain name + +Make sure these elements clearly communicate your value proposition. + +## SEO and GEO: Not Either/Or + +Here's the good news: most GEO best practices align with modern SEO. High-quality, authoritative content that answers user questions will perform well in both traditional and AI search. + +The key is to think about search holistically: + +| Traditional SEO | GEO | +|-----------------|-----| +| Keywords in titles | Clear, descriptive titles | +| Backlinks | Citations from authoritative sources | +| Page speed | Easily parseable content | +| User engagement | Comprehensive, accurate information | + +## How Fluid Posts Approaches GEO + +At Fluid Posts, every piece of content we create is optimized for both traditional search engines and AI systems. Our approach includes: + +- **Structured content** that AI can easily parse and cite +- **Factual, well-researched information** that passes AI accuracy checks +- **Strategic internal linking** that establishes topical authority +- **Regular updates** to keep content current and accurate + +The result? Our clients don't just rank on Google—they get cited on ChatGPT. + +## Getting Started + +Ready to optimize for the AI search era? Here's your action plan: + +1. **Audit your existing content** for direct answers and citations +2. **Identify gaps** where AI systems might look for information +3. **Create comprehensive guides** on your core topics +4. **Monitor AI search results** for your key terms +5. **Track citations** across ChatGPT, Perplexity, and AI Overviews + + + + Master the fundamentals of finding topics that drive traffic. + + + Automate your SEO and GEO strategy today. + + diff --git a/apps/seo-www/content/introducing-fluid-posts.mdx b/apps/seo-www/content/introducing-fluid-posts.mdx new file mode 100644 index 000000000..23ef75229 --- /dev/null +++ b/apps/seo-www/content/introducing-fluid-posts.mdx @@ -0,0 +1,68 @@ +--- +title: "Introducing Fluid Posts: Complete SEO, Automated" +description: "We built Fluid Posts to help growing businesses achieve organic visibility without the complexity. Here's how we're changing SEO." +icon: "Rocket" +cover: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?q=80&w=1600&auto=format&fit=crop" +author: "winston" +tags: + - "Fluid Posts Updates" + - "announcement" + - "product" +--- + + +Fluid Posts is now available for early access. Sign up today to get started with automated SEO. + + +## Why We Built Fluid Posts + +Traditional SEO is broken. Agencies charge thousands per month, deliver opaque results, and lock you into long contracts. In-house teams require expensive hires and constant management. And DIY approaches? They demand expertise most founders simply don't have time to develop. + +We asked ourselves: what if SEO could be as simple as setting your goals and letting the system work for you? + +## The Fluid Posts Approach + +Fluid Posts combines AI-powered content strategy with automated publishing and a growing authority network. Here's what makes us different: + +### 1. Strategy That Understands Your Business + +During onboarding, we learn about your business, target audience, and goals. Our system then generates tailored content strategies—not generic keyword lists, but actual publishing plans designed to capture high-intent traffic. + +### 2. Content That Ranks + +Every article is optimized for both search engines and AI systems like ChatGPT and Google's AI Overview. We don't just stuff keywords—we create genuinely useful content that builds trust with your audience. + +### 3. Authority That Compounds + +Our backlink network connects you with relevant businesses in complementary spaces. As you give and receive quality backlinks, your domain authority grows—and with it, your ability to rank for increasingly competitive terms. + +## Early Results + +We've been testing Fluid Posts with a small group of businesses, and the results speak for themselves: + +- **Dispute Ninja** went from 0 to 1,000 monthly visitors in their first month, ranking #1 on Google and getting cited on ChatGPT +- **QuantumByte** saw clicks increase 486% and impressions jump 906% within 30 days + +These aren't outliers—they're the norm when strategy, content, and authority work together. + +## What's Next + +We're just getting started. In the coming months, we'll be rolling out: + +- Advanced analytics and performance tracking +- AI-powered chat for SEO questions and insights +- Expanded CMS integrations +- More robust authority network matching + +## Get Started + +Ready to see what automated SEO can do for your business? [Sign up for early access](/signup) and join the growing community of brands that are building organic traffic on autopilot. + + + + Get started with Fluid Posts today. + + + Learn more about our approach to SEO. + + diff --git a/apps/seo-www/src/routeTree.gen.ts b/apps/seo-www/src/routeTree.gen.ts index 71125fc64..fe43ff394 100644 --- a/apps/seo-www/src/routeTree.gen.ts +++ b/apps/seo-www/src/routeTree.gen.ts @@ -12,6 +12,8 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as WhoWeAreRouteImport } from './routes/who-we-are' import { Route as SeoExpertsRouteImport } from './routes/seo-experts' import { Route as ReferralRouteImport } from './routes/referral' +import { Route as LandingMockupRouteImport } from './routes/landing-mockup' +import { Route as LandingDemoRouteImport } from './routes/landing-demo' import { Route as BlogRouteRouteImport } from './routes/blog/route' import { Route as IndexRouteImport } from './routes/index' import { Route as BlogIndexRouteImport } from './routes/blog/index' @@ -37,6 +39,16 @@ const ReferralRoute = ReferralRouteImport.update({ path: '/referral', getParentRoute: () => rootRouteImport, } as any) +const LandingMockupRoute = LandingMockupRouteImport.update({ + id: '/landing-mockup', + path: '/landing-mockup', + getParentRoute: () => rootRouteImport, +} as any) +const LandingDemoRoute = LandingDemoRouteImport.update({ + id: '/landing-demo', + path: '/landing-demo', + getParentRoute: () => rootRouteImport, +} as any) const BlogRouteRoute = BlogRouteRouteImport.update({ id: '/blog', path: '/blog', @@ -87,6 +99,8 @@ const ApiBlogSearchRoute = ApiBlogSearchRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/blog': typeof BlogRouteRouteWithChildren + '/landing-demo': typeof LandingDemoRoute + '/landing-mockup': typeof LandingMockupRoute '/referral': typeof ReferralRoute '/seo-experts': typeof SeoExpertsRoute '/who-we-are': typeof WhoWeAreRoute @@ -100,6 +114,8 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/': typeof IndexRoute + '/landing-demo': typeof LandingDemoRoute + '/landing-mockup': typeof LandingMockupRoute '/referral': typeof ReferralRoute '/seo-experts': typeof SeoExpertsRoute '/who-we-are': typeof WhoWeAreRoute @@ -115,6 +131,8 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/blog': typeof BlogRouteRouteWithChildren + '/landing-demo': typeof LandingDemoRoute + '/landing-mockup': typeof LandingMockupRoute '/referral': typeof ReferralRoute '/seo-experts': typeof SeoExpertsRoute '/who-we-are': typeof WhoWeAreRoute @@ -131,6 +149,8 @@ export interface FileRouteTypes { fullPaths: | '/' | '/blog' + | '/landing-demo' + | '/landing-mockup' | '/referral' | '/seo-experts' | '/who-we-are' @@ -144,6 +164,8 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' + | '/landing-demo' + | '/landing-mockup' | '/referral' | '/seo-experts' | '/who-we-are' @@ -158,6 +180,8 @@ export interface FileRouteTypes { | '__root__' | '/' | '/blog' + | '/landing-demo' + | '/landing-mockup' | '/referral' | '/seo-experts' | '/who-we-are' @@ -173,6 +197,8 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute BlogRouteRoute: typeof BlogRouteRouteWithChildren + LandingDemoRoute: typeof LandingDemoRoute + LandingMockupRoute: typeof LandingMockupRoute ReferralRoute: typeof ReferralRoute SeoExpertsRoute: typeof SeoExpertsRoute WhoWeAreRoute: typeof WhoWeAreRoute @@ -205,6 +231,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ReferralRouteImport parentRoute: typeof rootRouteImport } + '/landing-mockup': { + id: '/landing-mockup' + path: '/landing-mockup' + fullPath: '/landing-mockup' + preLoaderRoute: typeof LandingMockupRouteImport + parentRoute: typeof rootRouteImport + } + '/landing-demo': { + id: '/landing-demo' + path: '/landing-demo' + fullPath: '/landing-demo' + preLoaderRoute: typeof LandingDemoRouteImport + parentRoute: typeof rootRouteImport + } '/blog': { id: '/blog' path: '/blog' @@ -290,6 +330,8 @@ const BlogRouteRouteWithChildren = BlogRouteRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, BlogRouteRoute: BlogRouteRouteWithChildren, + LandingDemoRoute: LandingDemoRoute, + LandingMockupRoute: LandingMockupRoute, ReferralRoute: ReferralRoute, SeoExpertsRoute: SeoExpertsRoute, WhoWeAreRoute: WhoWeAreRoute, diff --git a/apps/seo-www/src/routes/-components/footer.tsx b/apps/seo-www/src/routes/-components/footer.tsx index d2fa938eb..de1d406c1 100644 --- a/apps/seo-www/src/routes/-components/footer.tsx +++ b/apps/seo-www/src/routes/-components/footer.tsx @@ -6,19 +6,6 @@ import { import { Link } from "@tanstack/react-router"; const links = [ - { - group: "Solution", - items: [ - { - title: "Founders", - href: "/", - }, - { - title: "Freelancers", - href: "/seo-experts", - }, - ], - }, { group: "Company", items: [ diff --git a/apps/seo-www/src/routes/-components/founders/founder-hero.tsx b/apps/seo-www/src/routes/-components/founders/founder-hero.tsx index b153a3ce5..8b5b438ce 100644 --- a/apps/seo-www/src/routes/-components/founders/founder-hero.tsx +++ b/apps/seo-www/src/routes/-components/founders/founder-hero.tsx @@ -1,197 +1,10 @@ -import { MoveRight, Search } from "@rectangular-labs/ui/components/icon"; -import { buttonVariants } from "@rectangular-labs/ui/components/ui/button"; import { Section } from "@rectangular-labs/ui/components/ui/section"; -import { useState } from "react"; -import { WaitListDialog } from "../waitlist-dialog"; +import { SerpClimbDemo } from "./serp-climb-v2"; export const FounderHero = () => { - const [dnOk, setDnOk] = useState(true); - const [chatgptOk, setChatgptOk] = useState(true); - - const GoogleMark = () => ( - - Google - - - - - - ); - - const ChatGptMark = () => ( -
- {chatgptOk ? ( - ChatGPT setChatgptOk(false)} - src="/logos/chatgpt.png" - /> - ) : ( - - ChatGPT - {/* fallback mark */} - - - )} -
- ); - return (
-
-

- Your{" "} - SEO Co-Founder. -

-

- Fluid Posts is an End-to-End SEO tool built to think like a business - owner: self-critical, data-driven, and focused on growth. -

-
- - Join the waitlist - - } - /> -

- Launching End-Jan 2026 -

-
-
-

- Early results -

-

- In early pilots, we helped 2 clients reach{" "} - #1 on Google{" "} - and appear on{" "} - ChatGPT for - targeted terms within a month. -

- -
-
-

- Pilot highlight -

- -
- {/* Google SERP mock */} -
-
- - Google -
-
- - gambling chargebacks -
-
-

- #1 result -

-

- Gambling Chargebacks: How to Dispute Online Casino... -

-
- {dnOk ? ( - Dispute Ninja setDnOk(false)} - src="/logos/dispute-ninjas.png" - /> - ) : ( -
- DN -
- )} - - Dispute Ninjas - -
-
-
- - {/* ChatGPT mock */} -
-
- - - - ChatGPT -
- -
- - best chargeback softwares -
- -
-

- #5 Result -

-

- Top Chargeback Management & Prevention Software -

-
- {dnOk ? ( - Dispute Ninja setDnOk(false)} - src="/logos/dispute-ninjas.png" - /> - ) : ( -
- DN -
- )} - - Dispute Ninjas - -
-
-
-
-
-
-
-
+
); }; diff --git a/apps/seo-www/src/routes/-components/founders/hero-search-engine-carousel.tsx b/apps/seo-www/src/routes/-components/founders/hero-search-engine-carousel.tsx new file mode 100644 index 000000000..5b3a02293 --- /dev/null +++ b/apps/seo-www/src/routes/-components/founders/hero-search-engine-carousel.tsx @@ -0,0 +1,175 @@ +import { AnimatePresence, motion } from "motion/react"; +import type { ReactNode } from "react"; +import { useEffect, useMemo, useState } from "react"; + +type Engine = { + id: string; + label: string; + node: ReactNode; +}; + +function ClaudeMark() { + return ( + + Claude + + + + ); +} + +function GeminiMark() { + return ( + + Gemini + + + + + + + + + + + ); +} + +function AiOverviewsMark() { + return ( +
+ AI +
+ ); +} + +function GoogleMark() { + return ( + + Google + + + + + + ); +} + +function ChatGptMark({ onError }: { onError: () => void }) { + return ( + ChatGPT + ); +} + +export function HeroSearchEngineCarousel() { + const [chatgptOk, setChatgptOk] = useState(true); + const engines = useMemo(() => { + const base: Engine[] = [ + { id: "google", label: "Google", node: }, + { + id: "chatgpt", + label: "ChatGPT", + node: chatgptOk ? ( + setChatgptOk(false)} /> + ) : ( +
+ GPT +
+ ), + }, + { id: "aio", label: "AI Overviews", node: }, + { id: "claude", label: "Claude", node: }, + { id: "gemini", label: "Gemini", node: }, + ]; + return base; + }, [chatgptOk]); + + const [index, setIndex] = useState(0); + + useEffect(() => { + if (engines.length === 0) return; + const id = window.setInterval(() => { + setIndex((i) => (i + 1) % engines.length); + }, 1000); + return () => window.clearInterval(id); + }, [engines.length]); + + const active = engines[index] ?? engines[0]; + if (!active) return null; + + return ( + + + + + {active.label} + {active.node} + + + + + ); +} diff --git a/apps/seo-www/src/routes/-components/founders/hero-serp-climb.tsx b/apps/seo-www/src/routes/-components/founders/hero-serp-climb.tsx new file mode 100644 index 000000000..486c76acc --- /dev/null +++ b/apps/seo-www/src/routes/-components/founders/hero-serp-climb.tsx @@ -0,0 +1,275 @@ +import { motion, useAnimationControls } from "motion/react"; +import { useEffect, useMemo, useState } from "react"; + +type Row = { + id: string; + title: string; + url: string; + snippet: string; + isBrand?: boolean; +}; + +const ROW_H = 86; +const VISIBLE = 4; +const HOLD_START_MS = 180; +const HOLD_TOP_MS = 800; + +const easePremium: [number, number, number, number] = [0.22, 1, 0.36, 1]; + +function clamp(n: number, min: number, max: number) { + return Math.max(min, Math.min(max, n)); +} + +export function HeroSerpClimb() { + const controls = useAnimationControls(); + const [phase, setPhase] = useState<"idle" | "climbing" | "top">("idle"); + + const rows = useMemo( + () => [ + { + id: "a", + title: "Competitor A — SEO services", + url: "competitor-a.com", + snippet: "A short description of a competing offer.", + }, + { + id: "b", + title: "Competitor B — Content & SEO", + url: "competitor-b.com", + snippet: "Another result with a calm, realistic snippet.", + }, + { + id: "brand", + title: "Your brand — AI SEO that compounds", + url: "yourbrand.com", + snippet: "Automates strategy, writing, optimization, and publishing.", + isBrand: true, + }, + { + id: "c", + title: "Competitor C — SEO agency", + url: "competitor-c.com", + snippet: "A third competitor result with muted styling.", + }, + { + id: "d", + title: "Competitor D — SEO tool", + url: "competitor-d.com", + snippet: "Yet another competitor, still muted in the UI.", + }, + { + id: "e", + title: "Competitor E — Growth platform", + url: "competitor-e.com", + snippet: "Generic competitor snippet for realism.", + }, + ], + [], + ); + + const brandIndex = rows.findIndex((r) => r.isBrand); + const startIndex = clamp(brandIndex - 2, 0, rows.length - VISIBLE); + const topIndex = clamp(brandIndex - 0, 0, rows.length - VISIBLE); + + const startY = -(startIndex * ROW_H); + const topY = -(topIndex * ROW_H); + + useEffect(() => { + let cancelled = false; + + async function loop() { + if (cancelled) return; + + setPhase("idle"); + await controls.set({ y: startY, opacity: 1 }); + + await new Promise((r) => setTimeout(r, HOLD_START_MS)); + if (cancelled) return; + + setPhase("climbing"); + + await controls.start({ + y: topY, + transition: { duration: 1.35, ease: easePremium }, + }); + + if (cancelled) return; + setPhase("top"); + + await new Promise((r) => setTimeout(r, HOLD_TOP_MS)); + if (cancelled) return; + + await controls.start({ + opacity: 0, + transition: { duration: 0.16, ease: easePremium }, + }); + await controls.set({ y: startY }); + await controls.start({ + opacity: 1, + transition: { duration: 0.12, ease: easePremium }, + }); + + void loop(); + } + + void loop(); + return () => { + cancelled = true; + }; + }, [controls, startY, topY]); + + return ( +
+
+

+ push your brand atop search +

+ +
+
+
+ {/* top bar */} +
+
+
+ 🔎 +
+
+ your brand +
+
+
+ {phase === "climbing" + ? "Ranking automatically…" + : phase === "top" + ? "Rank secured." + : "Indexing…"} +
+
+ + {/* viewport */} +
+ {/* cinematic vignette */} +
+
+
+
+ + + {rows.map((r, idx) => { + const isBrand = !!r.isBrand; + + return ( +
+ {/* rank column */} +
+ {idx + 1} +
+ + {/* favicon */} +
+
+
+ + {/* content */} +
+ {/* brand highlight */} + {isBrand && ( + + )} + +
+
+ {r.title} +
+
+ {r.url} +
+
+ {r.snippet} +
+ + {isBrand && phase === "top" && ( +
+ Rank #1 + + Compounding +
+ )} +
+
+
+ ); + })} + +
+ + {/* subtle footer strip */} +
+
+ Search results (simulated) +
+
+ Fluid Posts +
+
+
+
+
+
+
+ ); +} diff --git a/apps/seo-www/src/routes/-components/founders/landing-page-mockup.tsx b/apps/seo-www/src/routes/-components/founders/landing-page-mockup.tsx new file mode 100644 index 000000000..31da77ab3 --- /dev/null +++ b/apps/seo-www/src/routes/-components/founders/landing-page-mockup.tsx @@ -0,0 +1,431 @@ +import { motion, AnimatePresence } from "motion/react"; +import { useState } from "react"; + +// ============================================ +// SHARED BACKGROUND - Gradient Mesh (recommended) +// ============================================ + +function GradientBackground({ variant = "hero" }: { variant?: "hero" | "light" | "subtle" }) { + const intensity = variant === "hero" ? 1 : variant === "light" ? 0.6 : 0.3; + + return ( +
+ + + +
+ ); +} + +// ============================================ +// HERO SECTION +// ============================================ + +function HeroSection() { + return ( +
+ + +
+

+ + Take Your Brand to the + + + Top of Search. + +

+ +

+ We create strategic content and build authoritative backlinks that push your brand + to #1 on Google, AI Overviews, and ChatGPT. +

+ +
+ + +
+ + {/* Placeholder for carousel graphic */} +
+
+
+ [Hero Carousel Graphic] +
+
+
+
+
+ ); +} + +// ============================================ +// SOCIAL PROOF - Single Quote +// ============================================ + +function SocialProofSection() { + return ( +
+
+
+ "We went from page 5 to ranking #1 for our main keyword in under 90 days. + The ROI paid for itself in the first month." +
+
+
+
+
Sarah Chen
+
Head of Growth, TechStartup
+
+
+
+
+ ); +} + +// ============================================ +// HOW IT WORKS +// ============================================ + +function HowItWorksSection() { + const steps = [ + { + num: "01", + title: "Strategic Content", + desc: "We research your market and create high-quality content optimized for both search engines and AI systems.", + }, + { + num: "02", + title: "Authority Links", + desc: "We build backlinks from trusted, relevant sources that signal authority to Google and AI platforms.", + }, + { + num: "03", + title: "Rank & Dominate", + desc: "Watch your brand climb to #1 and stay there. We monitor, adjust, and keep you on top.", + }, + ]; + + return ( +
+ + +
+
+

+ How We Get You to #1. +

+

+ A proven system that combines content excellence with strategic link building. +

+
+ +
+ {steps.map((step) => ( +
+ {step.num} +

{step.title}

+

{step.desc}

+
+ ))} +
+
+
+ ); +} + +// ============================================ +// RESULTS - Mini Case Study +// ============================================ + +function ResultsSection() { + return ( +
+
+
+
+
+ +
+
+

+ Real Results. +

+

+ Don't take our word for it. Here's what happens when we work together. +

+
+ + {/* Mini Case Study */} +
+
+
+
Before
+
Page 5
+
Invisible to customers
+
+ +
+ +
+ +
+
After 90 Days
+
#1
+
Dominating search
+
+
+ +
+
+
312%
+
Traffic increase
+
+
+
47
+
Keywords on page 1
+
+
+
8.2x
+
ROI in 6 months
+
+
+ +
+

"FluidPosts transformed our entire organic strategy."

+

— Marketing Director, B2B SaaS Company

+
+
+
+
+ ); +} + +// ============================================ +// FEATURES / WHAT YOU GET +// ============================================ + +function FeaturesSection() { + const features = [ + { + title: "AI-Optimized Content", + desc: "Content engineered to rank on Google AND get cited by ChatGPT, Claude, and AI Overviews.", + icon: "M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z", + }, + { + title: "Authority Backlinks", + desc: "High-quality links from real, relevant websites. No spam, no shortcuts, just real authority.", + icon: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1", + }, + { + title: "Rank Tracking", + desc: "Real-time monitoring across Google, Bing, and AI platforms. Know exactly where you stand.", + icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z", + }, + { + title: "Competitor Analysis", + desc: "We reverse-engineer what's working for your competitors so you can outrank them.", + icon: "M15 12a3 3 0 11-6 0 3 3 0 016 0z M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z", + }, + ]; + + return ( +
+ + +
+
+

+ Everything You Need. +

+

+ A complete system for dominating search—traditional and AI-powered. +

+
+ +
+ {features.map((feature) => ( +
+
+ + + +
+
+

{feature.title}

+

{feature.desc}

+
+
+ ))} +
+
+
+ ); +} + +// ============================================ +// CTA SECTION +// ============================================ + +function CTASection() { + return ( +
+ + +
+

+ Ready to Rank #1? +

+

+ Join 50+ brands that have transformed their search presence. + Let's discuss how we can get you to the top. +

+
+ + +
+
+
+ ); +} + +// ============================================ +// FAQ SECTION +// ============================================ + +function FAQSection() { + const faqs = [ + { + q: "How long until I see results?", + a: "Most clients see meaningful ranking improvements within 60-90 days. SEO is a long-term play, but our approach accelerates results significantly.", + }, + { + q: "Do you guarantee #1 rankings?", + a: "No one can guarantee specific rankings—anyone who does is lying. What we guarantee is a proven process, transparent reporting, and relentless optimization.", + }, + { + q: "How is this different from other SEO agencies?", + a: "We focus specifically on the new landscape: Google + AI platforms. Most agencies are still stuck in 2015. We're building for where search is going.", + }, + { + q: "What kind of backlinks do you build?", + a: "Real editorial links from relevant, authoritative sites. No PBNs, no spam, no shortcuts. Quality over quantity, always.", + }, + ]; + + const [openIndex, setOpenIndex] = useState(null); + + return ( +
+
+

+ Questions? +

+ +
+ {faqs.map((faq, i) => ( +
+ + + {openIndex === i && ( + +
+ {faq.a} +
+
+ )} +
+
+ ))} +
+
+
+ ); +} + +// ============================================ +// FOOTER +// ============================================ + +function Footer() { + return ( +
+
+
+
FluidPosts
+
+ + + +
+
© 2024 FluidPosts
+
+
+
+ ); +} + +// ============================================ +// FULL PAGE +// ============================================ + +export function LandingPageMockup() { + return ( +
+ + + + + + + +
+
+ ); +} diff --git a/apps/seo-www/src/routes/-components/founders/landing-sections-demo.tsx b/apps/seo-www/src/routes/-components/founders/landing-sections-demo.tsx new file mode 100644 index 000000000..ab72359f6 --- /dev/null +++ b/apps/seo-www/src/routes/-components/founders/landing-sections-demo.tsx @@ -0,0 +1,486 @@ +import { motion } from "motion/react"; + +// ============================================ +// CONTINUOUS LIQUID GRADIENT BACKGROUND +// This flows seamlessly across the entire page +// ============================================ + +export function LiquidGradientBackground() { + return ( +
+ {/* Subtle top gradient - light mode */} +
+ {/* Dark mode gradient */} +
+ {/* Single subtle accent - light mode */} +
+ {/* Dark mode accent */} +
+
+ ); +} + +// ============================================ +// DEMO PAGE SHOWCASING ALL THEMES +// ============================================ + +// Theme 1: Gradient Beams (diagonal light rays) +function GradientBeamsBackground() { + return ( +
+
+ + + +
+ ); +} + +// Theme 2: Morphing Blob (single large animated shape) +function MorphingBlobBackground() { + return ( +
+ +
+ ); +} + +// Theme 3: Gradient Stripes (angled color bands) +function GradientStripesBackground() { + return ( +
+
+
+
+ ); +} + +// Theme 4: Spotlight Effect (dramatic top-down light) +function SpotlightBackground() { + return ( +
+
+ +
+
+ ); +} + +// Theme 5: Particle Field (floating dots with depth) +function ParticleFieldBackground() { + const particles = Array.from({ length: 40 }, (_, i) => ({ + id: i, + size: Math.random() * 4 + 2, + x: Math.random() * 100, + y: Math.random() * 100, + duration: Math.random() * 10 + 15, + delay: Math.random() * 5, + opacity: Math.random() * 0.3 + 0.1, + })); + + return ( +
+
+ {particles.map((p) => ( + + ))} +
+
+ ); +} + +// Theme 6: Glassmorphism Layers (stacked frosted panels) +function GlassLayersBackground() { + return ( +
+
+ + + +
+ ); +} + +// Theme 7: Noise Gradient (textured gradient) +function NoiseGradientBackground() { + return ( +
+
+ +
+
+
+ ); +} + +// Theme 8: Liquid Gradient (flowing color blend) - STANDALONE VERSION +function LiquidGradientBackgroundStandalone() { + return ( +
+ +
+ ); +} + +// Theme 9: Geometric Shapes (floating abstract forms) +function GeometricShapesBackground() { + return ( +
+
+ + + + +
+
+ ); +} + +// Theme 10: Aurora Waves (flowing aurora borealis) +function AuroraWavesBackground() { + return ( +
+
+ + +
+ ); +} + +// Theme 11: Split Gradient (bold color division) +function SplitGradientBackground() { + return ( +
+
+ + +
+
+ ); +} + +// Theme 12: Prism Light (refracted light effect) +function PrismLightBackground() { + return ( +
+
+ + +
+ ); +} + +// ============================================ +// DEMO SECTIONS +// ============================================ + +function Section({ bg, label, labelColor, description }: { + bg: React.ReactNode; + label: string; + labelColor: string; + description: string; +}) { + return ( +
+ {bg} +
+
+ + {label} + +
+

+ + Take Your Brand to the + + + Top of Search. + +

+

+ {description} +

+
+
+ ); +} + +// ============================================ +// COMBINED DEMO PAGE +// ============================================ + +export function LandingSectionsDemo() { + return ( +
+
} + label="Theme 1: Gradient Beams" + labelColor="bg-blue-100 text-blue-700" + description="Animated diagonal light rays. Dynamic, premium, catches attention without overwhelming." + /> +
} + label="Theme 2: Morphing Blob" + labelColor="bg-violet-100 text-violet-700" + description="Single large shape that morphs and rotates. Hypnotic, modern, very unique." + /> +
} + label="Theme 3: Gradient Stripes" + labelColor="bg-indigo-100 text-indigo-700" + description="Angled color bands. Bold, structured, editorial feel. Very intentional." + /> +
} + label="Theme 4: Spotlight" + labelColor="bg-sky-100 text-sky-700" + description="Dramatic top-down light cone. Draws focus to content, theatrical." + /> +
} + label="Theme 5: Particle Field" + labelColor="bg-cyan-100 text-cyan-700" + description="Floating dots with depth. Tech-forward, alive, sophisticated." + /> +
} + label="Theme 6: Glass Layers" + labelColor="bg-emerald-100 text-emerald-700" + description="Stacked frosted glass panels. Depth, dimension, very modern UI feel." + /> +
} + label="Theme 7: Noise Gradient" + labelColor="bg-teal-100 text-teal-700" + description="Textured gradient with grain. Editorial, tactile, premium print feel." + /> +
} + label="Theme 8: Liquid Gradient" + labelColor="bg-purple-100 text-purple-700" + description="Smoothly transitioning colors. Hypnotic, fluid, very polished." + /> +
} + label="Theme 9: Geometric Shapes" + labelColor="bg-pink-100 text-pink-700" + description="Floating abstract forms. Playful but professional, distinctive." + /> +
} + label="Theme 10: Aurora Waves" + labelColor="bg-amber-100 text-amber-700" + description="Flowing aurora borealis from top. Dramatic, ethereal, striking." + /> +
} + label="Theme 11: Split Gradient" + labelColor="bg-orange-100 text-orange-700" + description="Bold diagonal color division. Architectural, confident, modern." + /> +
} + label="Theme 12: Prism Light" + labelColor="bg-rose-100 text-rose-700" + description="Refracted light beams with rainbow hints. Innovative, eye-catching, tech-forward." + /> +
+ ); +} diff --git a/apps/seo-www/src/routes/-components/founders/serp-climb-v2.tsx b/apps/seo-www/src/routes/-components/founders/serp-climb-v2.tsx new file mode 100644 index 000000000..bbc391305 --- /dev/null +++ b/apps/seo-www/src/routes/-components/founders/serp-climb-v2.tsx @@ -0,0 +1,572 @@ +import { motion, useAnimationControls, AnimatePresence } from "motion/react"; +import { useEffect, useState, useCallback } from "react"; +import { WaitListDialog } from "../waitlist-dialog"; + +const ROW_H = 96; +const VISIBLE_ROWS = 3; +const VIEWPORT_H = ROW_H * VISIBLE_ROWS; + +const CLIMB_DURATION = 1.6; +const HOLD_START_MS = 800; +const HOLD_TOP_MS = 200; +const CURSOR_MOVE_DURATION = 0.18; +const CURSOR_HOLD_MS = 350; +const EASE: [number, number, number, number] = [0.25, 0.1, 0.25, 1]; + +const TRANSITION_DELAY = 250; +const GRAPHIC_HEIGHT = 520; + +// ============================================ +// GOOGLE SERP COMPONENT +// ============================================ + +function GoogleLogo() { + return ( + + + + + + + + + ); +} + +function SearchBar({ query }: { query: string }) { + return ( +
+ + + + {query} +
+ ); +} + +function SerpResultRow({ + name, url, snippet, isBrand, isBlurred, isAtTop, isClicked, +}: { + name: string; url: string; snippet: string; isBrand?: boolean; isBlurred: boolean; isAtTop?: boolean; isClicked?: boolean; +}) { + return ( + + {isBrand && ( + + )} +
{url}
+ + {name} + +
{snippet}
+
+ ); +} + +function MouseCursor({ isVisible, isClicking }: { isVisible: boolean; isClicking: boolean }) { + return ( + + + + + + ); +} + +function GoogleSerpGraphic({ onComplete }: { onComplete: () => void }) { + const competitorControls = useAnimationControls(); + const brandControls = useAnimationControls(); + const [phase, setPhase] = useState<"idle" | "climbing" | "top" | "cursor" | "click">("idle"); + + const competitors = [ + { id: "c1", name: "Competitor 1 — Industry Solutions", url: "competitor1.com", snippet: "Professional services for your business..." }, + { id: "c2", name: "Competitor 2 — Industry Solutions", url: "competitor2.com", snippet: "Professional services for your business..." }, + { id: "c3", name: "Competitor 3 — Industry Solutions", url: "competitor3.com", snippet: "Professional services for your business..." }, + { id: "c4", name: "Competitor 4 — Industry Solutions", url: "competitor4.com", snippet: "Professional services for your business..." }, + { id: "c5", name: "Competitor 5 — Industry Solutions", url: "competitor5.com", snippet: "Professional services for your business..." }, + { id: "c6", name: "Competitor 6 — Industry Solutions", url: "competitor6.com", snippet: "Professional services for your business..." }, + { id: "c7", name: "Competitor 7 — Industry Solutions", url: "competitor7.com", snippet: "Professional services for your business..." }, + { id: "c8", name: "Competitor 8 — Industry Solutions", url: "competitor8.com", snippet: "Professional services for your business..." }, + { id: "c9", name: "Competitor 9 — Industry Solutions", url: "competitor9.com", snippet: "Professional services for your business..." }, + ]; + + const competitorStartY = -ROW_H * (competitors.length - 2); + const competitorEndY = ROW_H; + const brandStartY = ROW_H * 2; + const brandEndY = 0; + + useEffect(() => { + let cancelled = false; + const run = async () => { + if (cancelled) return; + setPhase("idle"); + competitorControls.set({ y: competitorStartY }); + brandControls.set({ y: brandStartY }); + + await new Promise((r) => setTimeout(r, HOLD_START_MS)); + if (cancelled) return; + setPhase("climbing"); + + await Promise.all([ + brandControls.start({ y: brandEndY, transition: { duration: CLIMB_DURATION, ease: EASE } }), + competitorControls.start({ y: competitorEndY, transition: { duration: CLIMB_DURATION, ease: EASE } }), + ]); + if (cancelled) return; + setPhase("top"); + + await new Promise((r) => setTimeout(r, HOLD_TOP_MS)); + if (cancelled) return; + setPhase("cursor"); + + await new Promise((r) => setTimeout(r, CURSOR_MOVE_DURATION * 1000 + 80)); + if (cancelled) return; + setPhase("click"); + + await new Promise((r) => setTimeout(r, CURSOR_HOLD_MS)); + if (cancelled) return; + onComplete(); + }; + void run(); + return () => { cancelled = true; }; + }, [competitorControls, brandControls, competitorStartY, brandStartY, onComplete]); + + const isClimbing = phase === "climbing"; + const isAtTop = phase === "top" || phase === "cursor" || phase === "click"; + const showCursor = phase === "cursor" || phase === "click"; + const isClicked = phase === "click"; + + return ( +
+
+
+ +
+
+ All + Images + Videos + News +
+
+
+ + {competitors.map((c) => ( + + ))} + + + + + +
+
+
+ ); +} + +// ============================================ +// GOOGLE AI OVERVIEW COMPONENT (Light mode) +// ============================================ + +function GeminiSparkle() { + return ( + + + + + + + + + + ); +} + +function AIOverviewGraphic({ onComplete }: { onComplete: () => void }) { + const [phase, setPhase] = useState<"idle" | "thinking" | "typing" | "highlight" | "done">("idle"); + + useEffect(() => { + let cancelled = false; + const run = async () => { + if (cancelled) return; + setPhase("idle"); + await new Promise((r) => setTimeout(r, 300)); + if (cancelled) return; + setPhase("thinking"); + await new Promise((r) => setTimeout(r, 800)); + if (cancelled) return; + setPhase("typing"); + await new Promise((r) => setTimeout(r, 600)); + if (cancelled) return; + setPhase("highlight"); + await new Promise((r) => setTimeout(r, 1200)); + if (cancelled) return; + setPhase("done"); + await new Promise((r) => setTimeout(r, 500)); + if (cancelled) return; + onComplete(); + }; + void run(); + return () => { cancelled = true; }; + }, [onComplete]); + + const isThinking = phase === "thinking"; + const showText = phase === "typing" || phase === "highlight" || phase === "done"; + const highlightBrand = phase === "highlight" || phase === "done"; + + return ( +
+
+ {/* Header */} +
+ + AI Overview +
+ + + +
+
+ + {/* Content */} +
+ {/* Thinking indicator */} + + {isThinking && ( + + + + + Thinking... + + )} + + + {/* Main content */} + + {showText && ( + +

+ The best product for your needs is{" "} + + Your Brand + . +

+ +

+ Based on customer reviews and expert analysis,{" "} + + Your Brand + + {" "}is the top-rated solution in its category. +

+ + {/* Source citation inline */} + + {highlightBrand && ( + + + + + yourbrand.com + + )} + +
+ )} +
+
+
+
+ ); +} + +// ============================================ +// CHATGPT COMPONENT (Light mode with logo) +// ============================================ + +function ChatGPTLogo() { + return ( + + + + ); +} + +function ChatGPTGraphic({ onComplete }: { onComplete: () => void }) { + const [phase, setPhase] = useState<"idle" | "typing" | "show" | "sources" | "done">("idle"); + + useEffect(() => { + let cancelled = false; + const run = async () => { + if (cancelled) return; + setPhase("idle"); + await new Promise((r) => setTimeout(r, 400)); + if (cancelled) return; + setPhase("typing"); + await new Promise((r) => setTimeout(r, 800)); + if (cancelled) return; + setPhase("show"); + await new Promise((r) => setTimeout(r, 600)); + if (cancelled) return; + setPhase("sources"); + await new Promise((r) => setTimeout(r, 1000)); + if (cancelled) return; + setPhase("done"); + await new Promise((r) => setTimeout(r, 400)); + if (cancelled) return; + onComplete(); + }; + void run(); + return () => { cancelled = true; }; + }, [onComplete]); + + const showResponse = phase === "show" || phase === "sources" || phase === "done"; + const showSources = phase === "sources" || phase === "done"; + + return ( +
+
+ {/* Header with logo */} +
+ + ChatGPT +
+ + {/* Chat area */} +
+ {/* User message - pill style on right */} +
+
+

best product for my needs

+
+
+ + {/* Assistant response */} +
+ {phase === "typing" && ( +
+ +
+ + + +
+
+ )} + + + {showResponse && ( + +

+ The best product for your needs is{" "} + + Your Brand + . +

+ + + {showSources && ( + +

+ It's consistently rated #1 for quality, customer satisfaction, and value. +

+ + {/* Source citation */} +
+ + + + yourbrand.com + + + +
+
+ )} +
+
+ )} +
+
+
+ + {/* Input bar */} +
+
+ + + + Message ChatGPT + + + +
+
+
+
+ ); +} + +// ============================================ +// CAROUSEL WRAPPER +// ============================================ + +const GRAPHICS = [ + { key: "google", label: "Google", component: GoogleSerpGraphic }, + { key: "ai-overview", label: "AI Overview", component: AIOverviewGraphic }, + { key: "chatgpt", label: "ChatGPT", component: ChatGPTGraphic }, +]; + +export function SerpClimbDemo() { + const [currentGraphic, setCurrentGraphic] = useState(0); + + const handleComplete = useCallback(() => { + setTimeout(() => { + setCurrentGraphic((prev) => (prev + 1) % GRAPHICS.length); + }, TRANSITION_DELAY); + }, []); + + const current = GRAPHICS[currentGraphic]; + const CurrentComponent = current?.component; + + return ( +
+ {/* Subtle green glow - lighter in light mode, stronger in dark mode */} +
+
+
+
+
+

+ + Take Your Brand to the + + + Top of Search. + +

+ + {/* Subheading and CTA */} +
+

+ Fluid Posts is the end-to-end SEO/GEO tool that automates organic rankings. +

+ + Join the Waitlist + + } + /> +
+ +
+ + + {CurrentComponent ? : null} + + +
+ + {/* Tab-style indicators */} +
+
+ {GRAPHICS.map((g) => ( + + ))} +
+
+
+
+ ); +} diff --git a/apps/seo-www/src/routes/-components/founders/serp-zoom-animation.tsx b/apps/seo-www/src/routes/-components/founders/serp-zoom-animation.tsx new file mode 100644 index 000000000..638457ac0 --- /dev/null +++ b/apps/seo-www/src/routes/-components/founders/serp-zoom-animation.tsx @@ -0,0 +1,255 @@ +import { motion, useAnimationControls } from "motion/react"; +import { useEffect, useMemo, useState } from "react"; + +const CARD_H = 68; +const GAP = 12; +const STEP = CARD_H + GAP; +const VIEWPORT_H = 232; + +type Ghost = { + id: string; + urlW: "w-16" | "w-20" | "w-24"; + titleW: "w-36" | "w-40" | "w-44" | "w-48"; + dim?: boolean; +}; + +function SkeletonLine({ w }: { w: string }) { + return
; +} + +function GhostResult({ ghost }: { ghost: Ghost }) { + return ( +
+
+
+
+
+
+ + +
+
+
+ ); +} + +function BrandResult() { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+ + Your Brand + +
+
+
+
+ ); +} + +function rotateDownBy(arr: T[], n: number): T[] { + if (arr.length === 0) return arr; + const k = ((n % arr.length) + arr.length) % arr.length; + if (k === 0) return arr; + return [...arr.slice(-k), ...arr.slice(0, -k)]; +} + +function makeGhosts(seed: number): Ghost[] { + const urlWidths: Ghost["urlW"][] = ["w-16", "w-20", "w-24"]; + const titleWidths: Ghost["titleW"][] = ["w-36", "w-40", "w-44", "w-48"]; + + return Array.from({ length: 12 }, (_, i) => ({ + id: `g-${seed}-${i}`, + urlW: urlWidths[(seed + i) % urlWidths.length] ?? "w-20", + titleW: titleWidths[(seed * 3 + i) % titleWidths.length] ?? "w-40", + dim: i % 5 === 0, + })); +} + +export function SerpZoomAnimation() { + const bgControls = useAnimationControls(); + const brandControls = useAnimationControls(); + const cutControls = useAnimationControls(); + const vignetteControls = useAnimationControls(); + + const [ghosts, setGhosts] = useState(() => makeGhosts(0)); + const [boosting, setBoosting] = useState(false); + + const brandStartY = useMemo(() => VIEWPORT_H - CARD_H - 16, []); + const brandEndY = useMemo(() => 12, []); + + useEffect(() => { + let cancelled = false; + const CLIMB_STEPS = 3; + const CLIMB_BG_Y = CLIMB_STEPS * STEP; + + const run = async () => { + let seed = 1; + + bgControls.set({ y: 0, opacity: 0.95, filter: "blur(0px)" }); + brandControls.set({ y: brandStartY, scale: 0.98 }); + cutControls.set({ opacity: 0 }); + vignetteControls.set({ opacity: 0.16 }); + + while (!cancelled) { + // Act I: start low + setBoosting(false); + await Promise.all([ + bgControls.start({ y: 0, transition: { duration: 0.35 } }), + brandControls.start({ + y: brandStartY, + scale: 0.985, + transition: { duration: 0.35, ease: "easeOut" }, + }), + vignetteControls.start({ + opacity: 0.14, + transition: { duration: 0.35, ease: "easeOut" }, + }), + ]); + + // Act II: cinematic climb + setBoosting(true); + await Promise.all([ + brandControls.start({ + y: brandEndY, + scale: 1.01, + transition: { duration: 0.6, ease: [0.16, 1, 0.3, 1] }, + }), + bgControls.start({ + y: CLIMB_BG_Y, + opacity: 0.9, + filter: "blur(1.5px)", + transition: { duration: 0.6, ease: [0.16, 1, 0.3, 1] }, + }), + vignetteControls.start({ + opacity: 0.28, + transition: { duration: 0.28, ease: "easeOut" }, + }), + ]); + + // Act III: settle at top + setBoosting(false); + await Promise.all([ + brandControls.start({ + scale: 1, + transition: { duration: 0.18, ease: "easeOut" }, + }), + bgControls.start({ + filter: "blur(0px)", + opacity: 0.92, + transition: { duration: 0.22, ease: "easeOut" }, + }), + ]); + + // Hold + await brandControls.start({ + y: brandEndY, + transition: { duration: 1.0, ease: "linear" }, + }); + + // Film cut to hide reset + await cutControls.start({ + opacity: 1, + transition: { duration: 0.12, ease: "easeIn" }, + }); + + setGhosts((prev) => rotateDownBy(prev, CLIMB_STEPS)); + setGhosts(makeGhosts(seed)); + seed += 1; + bgControls.set({ y: 0, opacity: 0.95, filter: "blur(0px)" }); + brandControls.set({ y: brandStartY, scale: 0.98 }); + vignetteControls.set({ opacity: 0.14 }); + + await cutControls.start({ + opacity: 0, + transition: { duration: 0.18, ease: "easeOut" }, + }); + } + }; + + void run(); + return () => { + cancelled = true; + }; + }, [ + bgControls, + brandControls, + brandEndY, + brandStartY, + cutControls, + vignetteControls, + ]); + + const brandShadow = boosting + ? "0 22px 50px rgba(99, 102, 241, 0.12)" + : "0 12px 28px rgba(0,0,0,0.10)"; + + return ( +
+ {/* Cinematic vignette */} + + + {/* Cut overlay for seamless looping */} + + +
+
+
+ +
+ {/* Background SERP stack (moves down) */} + + {ghosts.map((g) => ( +
+ +
+ ))} +
+ + {/* Brand (moves from bottom to top) */} + + + +
+
+
+ ); +} diff --git a/apps/seo-www/src/routes/-components/header.tsx b/apps/seo-www/src/routes/-components/header.tsx index 17512cb6f..47223e844 100644 --- a/apps/seo-www/src/routes/-components/header.tsx +++ b/apps/seo-www/src/routes/-components/header.tsx @@ -5,8 +5,7 @@ import { AnimatePresence, motion } from "motion/react"; import { useState } from "react"; const menuItems = [ - { name: "Founders", href: "/" }, - { name: "SEO Experts", href: "/seo-experts" }, + { name: "Blog", href: "/blog" }, { name: "Referral", href: "/referral" }, { name: "About us", href: "/who-we-are" }, ]; diff --git a/apps/seo-www/src/routes/-components/waitlist-dialog.tsx b/apps/seo-www/src/routes/-components/waitlist-dialog.tsx index c3eb7a032..93737ca96 100644 --- a/apps/seo-www/src/routes/-components/waitlist-dialog.tsx +++ b/apps/seo-www/src/routes/-components/waitlist-dialog.tsx @@ -179,13 +179,13 @@ export function WaitListDialog({ trigger, className }: Props) { ) : null}
- + + ))} + + +
+ + {/* Main content area */} +
+ {/* Search bar */} +
+
+ + setSearchQuery(e.target.value)} + className="w-full rounded-xl border border-neutral-200 bg-white py-3 pr-4 pl-12 text-neutral-900 placeholder:text-neutral-400 focus:border-emerald-500 focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:border-neutral-700 dark:bg-neutral-900 dark:text-white dark:focus:border-emerald-400 dark:placeholder:text-neutral-500" + /> +
+
+ + {/* Mobile categories */} +
+ {CATEGORIES.map((cat) => ( + + ))} +
+ + {/* Posts grid */} + {filteredPosts.length === 0 ? ( +
+

No articles found.

+
+ ) : ( + +
+
+
+ ); } diff --git a/apps/seo-www/src/routes/index.tsx b/apps/seo-www/src/routes/index.tsx index 3bd5a72ac..598ce7fe5 100644 --- a/apps/seo-www/src/routes/index.tsx +++ b/apps/seo-www/src/routes/index.tsx @@ -1,11 +1,10 @@ import { createFileRoute } from "@tanstack/react-router"; -import { FounderCTA } from "~/routes/-components/founders/founder-cta"; -import { FounderGrowth } from "~/routes/-components/founders/founder-growth"; -import { FounderHero } from "~/routes/-components/founders/founder-hero"; -import { FounderIntervention } from "~/routes/-components/founders/founder-intervention"; -import { FounderStrategist } from "~/routes/-components/founders/founder-strategist"; -import { FounderTransparency } from "~/routes/-components/founders/founder-transparency"; -import { FounderWhatItIs } from "~/routes/-components/founders/founder-what-it-is"; +import { motion, AnimatePresence } from "motion/react"; +import { useState, useMemo, useEffect, useCallback } from "react"; +import { Logo } from "@rectangular-labs/ui/components/icon"; +import { SerpClimbDemo } from "~/routes/-components/founders/serp-climb-v2"; +import { LiquidGradientBackground } from "~/routes/-components/founders/landing-sections-demo"; +import { WaitListDialog } from "~/routes/-components/waitlist-dialog"; export const Route = createFileRoute("/")({ component: App, @@ -28,16 +27,937 @@ export const Route = createFileRoute("/")({ }), }); +// ============================================ +// SOCIAL PROOF - Single Quote +// ============================================ + +function SocialProofSection() { + return ( +
+ {/* Subtle dot pattern */} +
+
+
+
+
+ "Our website ranked #1 for relevant keywords within a month of using Fluid Posts." +
+

+ Bowen Xue · Founder, Dispute Ninja +

+
+
+ ); +} + +// ============================================ +// ANIMATED NETWORK GRAPH +// ============================================ + +function NetworkGraph() { + const nodes = useMemo(() => { + const generated: Array<{ id: number; x: number; y: number; size: number; opacity: number }> = []; + for (let i = 0; i < 80; i++) { + generated.push({ + id: i, + x: Math.random() * 100, + y: Math.random() * 100, + size: Math.random() * 8 + 3, + opacity: Math.random() * 0.6 + 0.2, + }); + } + return generated; + }, []); + + const connections = useMemo(() => { + const lines: Array<{ from: number; to: number; opacity: number }> = []; + for (let i = 0; i < nodes.length; i++) { + const numConnections = Math.floor(Math.random() * 3) + 1; + for (let j = 0; j < numConnections; j++) { + const target = Math.floor(Math.random() * nodes.length); + if (target !== i) { + lines.push({ from: i, to: target, opacity: Math.random() * 0.3 + 0.1 }); + } + } + } + return lines; + }, [nodes]); + + return ( +
+ +
+ ); +} + +// ============================================ +// COMPLETE SEO, AUTOMATED +// ============================================ + +function JourneySection() { + return ( +
+ {/* Grid pattern with subtle fade - animated */} +
+ +
+
+
+
+

+ Complete SEO, automated. +

+

+ From strategy to publishing—we handle it all. +

+ {/* Decorative line */} +
+
+ + {/* Step 1: Content Strategy */} +
+
+
+

+ 01 + Content Strategy +

+

+ Based on your onboarding, we research your business, target users, and tailor strategies to reach your audience. +

+
    +
  • + + Custom strategy based on your business goals +
  • +
  • + + SEO-optimized articles written for you +
  • +
  • + + Automated publishing to your CMS +
  • +
+
+
+ {/* Strategy mockup - Toothbrush business example */} +
+
+
+ Suggestion +
+

Electric Toothbrush Buyer's Guide Cluster

+

Goal: 12,000 impressions per month

+
+
+
+

Motivation

+

+ High-intent buyers searching "best electric toothbrush" are ready to purchase. Capturing these queries drives qualified leads directly to your product pages. +

+
+
+

Description

+

+ Create comparison guides targeting "how to choose a toothbrush", "electric vs manual toothbrush", and "best toothbrush for sensitive teeth". +

+
+
+
+ + + +
+
+
+
+
+ + {/* Step 2: Authority Network */} +
+
+
+ +
+
+

+ 02 + Authority Network +

+

+ We pass trusted backlinks across our network, strengthening domain authority and compounding visibility. +

+
+
+
+
+
+ ); +} + +// ============================================ +// RESULTS - Two Client Cards (AirOps style) +// ============================================ + +function ResultsSection() { + return ( +
+ {/* Circle grid pattern */} +
+ +
+
+
+

+ Real Results. +

+
+
+ +
+ {/* Dispute Ninja Card */} +
+
+

DisputeNinja

+

B2B SaaS

+
+
+
+ 0 → 1,000 + monthly visitors +
+
+ + #1 on Google for target keywords +
+
+ + Cited on ChatGPT +
+
+

Results achieved within 1 month

+
+ + {/* QuantumByte Card */} +
+
+

QuantumByte

+

B2C SaaS

+
+
+
+ +486% + clicks +
+
+ +906% + impressions +
+
+

Results achieved within 1 month

+
+
+
+
+ ); +} + +// ============================================ +// FEATURE ICONS +// ============================================ + +function PerformanceIcon({ className }: { className?: string }) { + return ( + + ); +} + +function AuditIcon({ className }: { className?: string }) { + return ( + + ); +} + +function ArticleIcon({ className }: { className?: string }) { + return ( + + ); +} + +function InsightsIcon({ className }: { className?: string }) { + return ( + + ); +} + +function ChatIcon({ className }: { className?: string }) { + return ( + + ); +} + +// ============================================ +// FEATURES - THE WHOLE SUITE (Light mode with platform render) +// ============================================ + +function FeaturesSection() { + const [activeTab, setActiveTab] = useState<"performance" | "strategies" | "chat">("performance"); + const [isAutoPlaying, setIsAutoPlaying] = useState(true); + + const tabs = ["performance", "strategies", "chat"] as const; + + const cycleTab = useCallback(() => { + if (isAutoPlaying) { + setActiveTab((current) => { + const currentIndex = tabs.indexOf(current); + const nextIndex = (currentIndex + 1) % tabs.length; + return tabs[nextIndex] ?? "performance"; + }); + } + }, [isAutoPlaying, tabs]); + + useEffect(() => { + const interval = setInterval(cycleTab, 5000); + return () => clearInterval(interval); + }, [cycleTab]); + + const handleTabClick = (tab: typeof activeTab) => { + setIsAutoPlaying(false); + setActiveTab(tab); + setTimeout(() => setIsAutoPlaying(true), 15000); + }; + + const featureGroups = [ + { + title: "Performance Tracking", + icon: PerformanceIcon, + items: null, + desc: "Track key metrics like clicks and impressions for your whole website, and within clusters we suggest.", + }, + { + title: "Self-Auditing", + icon: AuditIcon, + items: null, + desc: "Fluid Posts audits its performance biweekly and shares its honest assessment with you.", + }, + { + title: "Article Generation", + icon: ArticleIcon, + items: ["Auto Image Generation", "SEO-Optimized Articles", "Organic Product Mentions", "Auto Publishing with CMS Integration"], + desc: null, + }, + { + title: "Business Insights", + icon: InsightsIcon, + items: null, + desc: "Actionable product and service insights and recommendations based on organic performance.", + }, + { + title: "Chat About Anything", + icon: ChatIcon, + items: null, + desc: "Have questions about your SEO, GEO, or business insights? Just ask chat.", + }, + ]; + + return ( +
+ {/* Same grid pattern as Complete SEO - with subtle flow animation */} +
+ +
+
+
+
+

+ The Whole Suite. And More. +

+

+ Fluid Posts is the only point of contact you need for SEO. Monitor performance, create articles, + ask questions, and watch it audit itself biweekly. +

+
+
+ +
+ {/* Interactive Platform Mockup */} +
+ {/* Tab switcher */} +
+ + + +
+ + {/* Platform mockup - with dark mode support */} +
+
+
+
+
+ FluidPosts +
+ + + {activeTab === "performance" && ( + +
+
+

BrightSmile Dental Co

+

Monitor clicks, impressions, and top queries

+
+
+ Last 28 days +
+
+
+
+

Total Clicks

+

4,821

+

+892%

+
+
+

Total Impressions

+

1.2M

+

+1,247%

+
+
+
+

Clicks & Impressions over time

+ +
+
+ )} + + {activeTab === "strategies" && ( + +
+

Active Strategies

+

Strategies currently executing

+
+
+
+
+ Active +
+
Electric Toothbrush Buyer's Guide
+

Goal: 12,000 impressions per month

+
+
+
+ Suggestion +
+
Dental Care Tips for Sensitive Teeth
+

Goal: 8,000 impressions per month

+
+
+
+ )} + + {activeTab === "chat" && ( + +
+

Chat

+

Ask anything about your SEO performance

+
+
+ {/* User message */} +
+
+ How has performance been this last week compared to the previous week, and why? +
+
+ {/* AI response */} +
+
+

Great question! This week showed a 23% increase in impressions compared to last week.

+

The main driver was your article "Best Electric Toothbrush 2025" which gained traction on Google's featured snippets...

+

.....

+
+
+
+ {/* Input bar */} +
+ + +
+
+ )} +
+
+
+ + {/* Feature list - grouped with icons */} +
+ {featureGroups.map((group) => ( +
+
+ +
+
+

{group.title}

+ {group.desc && ( +

{group.desc}

+ )} + {group.items && ( +
    + {group.items.map((item) => ( +
  • • {item}
  • + ))} +
+ )} +
+
+ ))} +
+
+
+
+ ); +} + +// ============================================ +// FLUID POSTS LOGO COMPONENT +// ============================================ + +function FluidPostsLogo({ className, centered }: { className?: string; centered?: boolean }) { + return ( +
+ + Fluid Posts +
+ ); +} + +// ============================================ +// PRICING - COMPARISON SECTION +// ============================================ + +function PricingSection() { + return ( +
+ {/* Dot pattern - same as Real Results */} +
+ +
+
+
+

+ Pricing. +

+

+ We believe SEO should be accessible to businesses of all sizes. That's why we're priced competitively—a fraction of traditional agencies, with better results. +

+
+
+ +
+ {/* Traditional Agency */} +
+ {/* Header section - price aligned with other cards */} +
+
+

Traditional SEO Agency

+
+
+ $3,000-$10,000 + /mo +
+
+ + {/* Features section */} +
+

What you get:

+
    +
  • + + 3-4 Articles / month +
  • +
  • + + 6-12 month lock-in period +
  • +
  • + + Monthly Reporting +
  • +
+ +

What you don't get:

+
    +
  • + + Real-time traffic updates +
  • +
  • + + Search strategy +
  • +
  • + + Transparent Self-auditing +
  • +
+
+
+ + {/* Fluid Posts - HIGHLIGHTED */} +
+
+ BEST FOR SMALL BUSINESSES +
+ {/* Header section - price aligned with other cards */} +
+
+ +
+
+ $99 + /mo +
+
+ $199/mo + 50% OFF +
+
+ + {/* Features section */} +
+

What you get:

+
    +
  • + + 5 GEO/SEO-optimized articles/month +
  • +
  • + + 1 quality backlink/month +
  • +
  • + + Clear strategies upon onboarding +
  • +
  • + + Self-auditing platform +
  • +
  • + + Automated CMS publishing +
  • +
  • + + Control over published content +
  • +
+
+ + + Join the waitlist + + } + /> +
+ + {/* Enterprise - HIGHLIGHTED */} +
+
+ BEST FOR ENTERPRISES +
+ {/* Header section - price aligned with other cards */} +
+
+ + Fluid Posts Enterprise +
+
+ Custom +
+

Accelerated results for serious growth

+
+ + {/* Features section */} +
+

What you get:

+
    +
  • + + 30+ SEO-optimized articles/month +
  • +
  • + + 3+ quality backlinks/month +
  • +
  • + + Direct contact with founders +
  • +
  • + + Personalized onboarding +
  • +
  • + + Quicker results +
  • +
+
+ + + Join the waitlist + + } + /> +
+
+
+
+ ); +} + +// ============================================ +// CTA SECTION +// ============================================ + +function CTASection() { + return ( +
+
+

+ Let's Get Ranking. +

+

+ Unlock the value of SEO for your business today, and watch visibility compound while you sit and watch. +

+
+ + Join the waitlist + + } + /> +
+
+ ); +} + +// ============================================ +// MAIN APP +// ============================================ + function App() { return ( -
- - - - - - - +
+ + + + + + + +
); } diff --git a/apps/seo-www/src/routes/landing-demo.tsx b/apps/seo-www/src/routes/landing-demo.tsx new file mode 100644 index 000000000..c4a30ba5c --- /dev/null +++ b/apps/seo-www/src/routes/landing-demo.tsx @@ -0,0 +1,14 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { LandingSectionsDemo } from "~/routes/-components/founders/landing-sections-demo"; + +export const Route = createFileRoute("/landing-demo")({ + component: LandingDemoPage, +}); + +function LandingDemoPage() { + return ( +
+ +
+ ); +} diff --git a/apps/seo-www/src/routes/landing-mockup.tsx b/apps/seo-www/src/routes/landing-mockup.tsx new file mode 100644 index 000000000..7480be765 --- /dev/null +++ b/apps/seo-www/src/routes/landing-mockup.tsx @@ -0,0 +1,14 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { LandingPageMockup } from "~/routes/-components/founders/landing-page-mockup"; + +export const Route = createFileRoute("/landing-mockup")({ + component: LandingMockupPage, +}); + +function LandingMockupPage() { + return ( +
+ +
+ ); +} diff --git a/apps/seo-www/src/styles.css b/apps/seo-www/src/styles.css index 560acd5ab..cc819efbe 100644 --- a/apps/seo-www/src/styles.css +++ b/apps/seo-www/src/styles.css @@ -8,3 +8,16 @@ --border: oklch(0.78 0 0); --input: oklch(0.78 0 0); } + +.seo-www-hero-display { + font-family: "EB Garamond Variable", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; +} + +@keyframes grid-flow { + 0%, 100% { opacity: 0.88; } + 50% { opacity: 1; } +} + +.animate-grid-flow { + animation: grid-flow 25s ease-in-out infinite; +} diff --git a/apps/seo-www/vite.config.ts b/apps/seo-www/vite.config.ts index 8d191a5e7..77272c321 100644 --- a/apps/seo-www/vite.config.ts +++ b/apps/seo-www/vite.config.ts @@ -39,6 +39,7 @@ const config = defineConfig({ viteReact(), ], server: { + host: "localhost", port: 4242, }, }); diff --git a/package.json b/package.json index c9745c689..93b46a414 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dev": "docker compose up -d && pnpm turbo watch dev --continue", "dev:seo": "docker compose up -d && pnpm turbo watch dev --continue --filter=seo... --filter=!seo-www", "dev:seo-www": "pnpm turbo watch dev --continue --filter=seo-www", + "dev:localhost": "pnpm --filter seo-www dev", "build": "turbo build", "build:preview": "turbo build:preview", "build:production": "turbo build:production", diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css index f3a80d49a..f786e1d41 100644 --- a/packages/ui/src/styles.css +++ b/packages/ui/src/styles.css @@ -184,7 +184,7 @@ code { unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -font-face { +@font-face { font-family: 'EB Garamond Variable'; font-style: normal; font-display: swap;