diff --git a/backend/app/api/endpoints/beta_signup.py b/backend/app/api/endpoints/beta_signup.py new file mode 100644 index 00000000..d4b2c7bc --- /dev/null +++ b/backend/app/api/endpoints/beta_signup.py @@ -0,0 +1,31 @@ +"""Public beta signup endpoint — no auth required.""" + +import logging +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel, EmailStr +from app.core.email import EmailService + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/beta-signup", tags=["beta"]) + + +class BetaSignupRequest(BaseModel): + email: EmailStr + + +class BetaSignupResponse(BaseModel): + success: bool + message: str + + +@router.post("", response_model=BetaSignupResponse) +async def beta_signup(data: BetaSignupRequest): + """Collect beta interest — sends notification to beta@resolutionflow.com.""" + sent = await EmailService.send_beta_signup_notification(data.email) + if not sent: + logger.warning("Beta signup recorded (email delivery skipped): %s", data.email) + return BetaSignupResponse( + success=True, + message="Thanks! We'll be in touch with beta access details.", + ) diff --git a/backend/app/api/router.py b/backend/app/api/router.py index b5951dde..5e789ff9 100644 --- a/backend/app/api/router.py +++ b/backend/app/api/router.py @@ -15,6 +15,7 @@ from app.api.endpoints import admin_survey from app.api.endpoints import tree_transfer from app.api.endpoints import ai_suggestions from app.api.endpoints import kb_accelerator +from app.api.endpoints import beta_signup api_router = APIRouter() @@ -54,3 +55,4 @@ api_router.include_router(admin_survey.router) api_router.include_router(tree_transfer.router) api_router.include_router(ai_suggestions.router) api_router.include_router(kb_accelerator.router) +api_router.include_router(beta_signup.router) diff --git a/backend/app/core/ai_tree_validator.py b/backend/app/core/ai_tree_validator.py index 850aa219..97cc9d78 100644 --- a/backend/app/core/ai_tree_validator.py +++ b/backend/app/core/ai_tree_validator.py @@ -151,9 +151,9 @@ def validate_generated_tree(tree: dict[str, Any]) -> list[str]: errors.append( f"Tree has only {node_count} nodes. Minimum 5 required for a useful tree." ) - if node_count > 50: + if node_count > 100: errors.append( - f"Tree has {node_count} nodes. Maximum 50 allowed." + f"Tree has {node_count} nodes. Maximum 100 allowed." ) if solution_count < 2: errors.append( diff --git a/backend/app/core/email.py b/backend/app/core/email.py index bdea462f..24c13dcb 100644 --- a/backend/app/core/email.py +++ b/backend/app/core/email.py @@ -418,6 +418,72 @@ class EmailService: logger.exception("Failed to send survey copy email to %s", to_email) return False + @staticmethod + async def send_beta_signup_notification( + signup_email: str, + notify_email: str = "beta@resolutionflow.com", + ) -> bool: + """Notify beta@resolutionflow.com about a new beta signup. Fire-and-forget.""" + if not settings.email_enabled: + logger.warning("Beta signup email not sent — RESEND_API_KEY not configured") + return False + + try: + import resend + import html as html_mod + from datetime import datetime, timezone + + resend.api_key = settings.RESEND_API_KEY + + date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") + safe_email = html_mod.escape(signup_email) + subject = f"[ResolutionFlow Beta] New signup — {safe_email}" + + email_html = f""" + + + + +
+ + + + + +
+

ResolutionFlow

+

New Beta Signup

+
+

+ A new user has requested beta access: +

+
+
+

Email

+

{safe_email}

+
+
+

+ Submitted at {date_str} +

+
+
+""" + + resend.Emails.send({ + "from": settings.FROM_EMAIL, + "to": [notify_email], + "reply_to": signup_email, + "subject": subject, + "html": email_html, + }) + logger.info("Beta signup notification sent for %s", signup_email) + return True + + except Exception: + logger.exception("Failed to send beta signup notification for %s", signup_email) + return False + @staticmethod async def send_survey_invite_email( to_email: str, diff --git a/backend/app/core/kb_conversion_service.py b/backend/app/core/kb_conversion_service.py index e1655411..ba28fdd2 100644 --- a/backend/app/core/kb_conversion_service.py +++ b/backend/app/core/kb_conversion_service.py @@ -139,7 +139,7 @@ Return a JSON object with this structure: 4. The first node is the root of the decision tree. 5. All `next_node_id` and option `next_node_id` references must point to existing node IDs. 6. Detect implicit branching logic (e.g., "If X, do Y; otherwise Z") and create decision nodes. -7. Produce at least 3 nodes. Maximum 50 nodes. +7. Produce at least 3 nodes. Maximum 100 nodes. 8. Use high confidence (0.9+) for directly stated steps, medium (0.7-0.89) for reasonable inferences, low (<0.7) for significant interpretation. 9. Return ONLY valid JSON — no markdown fences, no explanation text.""" diff --git a/frontend/src/components/layout/ProtectedRoute.tsx b/frontend/src/components/layout/ProtectedRoute.tsx index cc8bf2cf..1c5d6140 100644 --- a/frontend/src/components/layout/ProtectedRoute.tsx +++ b/frontend/src/components/layout/ProtectedRoute.tsx @@ -22,7 +22,7 @@ export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps) } if (!isAuthenticated) { - return + return } // Enforce must_change_password — redirect unless already on /change-password diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx new file mode 100644 index 00000000..9ed09a69 --- /dev/null +++ b/frontend/src/pages/LandingPage.tsx @@ -0,0 +1,514 @@ +import { useState, useEffect, useCallback } from 'react' +import { Link } from 'react-router-dom' +import { PageMeta } from '@/components/common/PageMeta' +import '@/styles/landing.css' + +const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000' + +export default function LandingPage() { + const [navScrolled, setNavScrolled] = useState(false) + const [betaEmail, setBetaEmail] = useState('') + const [betaStatus, setBetaStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle') + + // Nav scroll effect + useEffect(() => { + const handleScroll = () => setNavScrolled(window.scrollY > 40) + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, []) + + // Scroll reveal + useEffect(() => { + const els = document.querySelectorAll('.landing-reveal') + const observer = new IntersectionObserver( + (entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) entry.target.classList.add('visible') + }) + }, + { threshold: 0.15 } + ) + els.forEach(el => observer.observe(el)) + return () => observer.disconnect() + }, []) + + const handleBetaSubmit = useCallback(async (e: React.FormEvent) => { + e.preventDefault() + if (!betaEmail.trim() || betaStatus === 'sending') return + setBetaStatus('sending') + try { + const resp = await fetch(`${API_URL}/api/v1/beta-signup`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: betaEmail }), + }) + if (!resp.ok) throw new Error('Signup failed') + setBetaStatus('sent') + setBetaEmail('') + } catch { + setBetaStatus('error') + setTimeout(() => setBetaStatus('idle'), 3000) + } + }, [betaEmail, betaStatus]) + + return ( + <> + + +
+
+
+ +
+ {/* Navigation */} + + + {/* Hero */} +
+
Now in Beta — Join early access
+

+ Stop writing ticket notes.
+ Start generating them. +

+

+ AI-guided decision trees that walk your engineers through troubleshooting — and automatically document every step, ready for your PSA ticket. +

+
+ Start Free + See How It Works +
+
+ + {/* Social Proof Bar */} +
+

Built by MSP engineers, for MSP engineers

+
+
+
15+
+
Years MSP Experience
+
+
+
70%
+
Less Time on Documentation
+
+
+
0
+
Ticket Notes Written by Hand
+
+
+
+ + {/* App Preview */} +
+
+
+
+
+ ResolutionFlow + × +
+
+
+ 🔒 + app.resolutionflow.com/editor +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ Flow Editor +
+
+
+ Session Runner +
+
+
+ Flow Library +
+
+
+ Session History +
+
+
+ Team Analytics +
+
+
+
+
Outlook Not Syncing
+
+
+
+
Yes
+
+
Check profile config
+
+
+
No
+
+
Verify credentials
+
+
+
+
+
+
+
+ +
+
+ + {/* Problem Section */} +
+
+
The Problem
+
Documentation is broken.
Everyone knows it.
+
+ Engineers don't want to write it. Managers hate chasing it. Clients never see it. The same issues get solved from scratch every time. +
+
+ + + + +
+
+
+ +
+ + {/* Brand Equation */} +
+
The Answer
+
+ Resolution + + + Documentation + + Time + = + ResolutionFlow +
+

+ What if documentation was a byproduct of solving the issue — not a separate task? What if your engineers never had to write another ticket note? +

+
+ +
+ + {/* How It Works */} +
+
+
How It Works
+
Three steps. Zero note-writing.
+
+ Build once, run forever. Every session generates documentation automatically. +
+
+
+

Build a Flow

+

Use the visual Flow Editor to create branching decision trees for any troubleshooting scenario. Drag, connect, and enrich steps with commands, notes, and AI suggestions.

+
+
+
▶ Start
+
+
Check DNS
+
+
Yes / No?
+
+
✓ Resolved
+
+
+
+ +
+

Run a Session

+

An engineer launches the flow on a live ticket. FlowPilot — your AI copilot — acts as a virtual senior engineer, guiding decisions and capturing every action in real time.

+
+
+
+ FlowPilot: + Is the user on VPN? +
+
+ Engineer: + Yes, Cisco AnyConnect +
+
+ FlowPilot: + Check split tunnel config → +
+
+ Auto-doc: + Step captured ✓ +
+
+
+
+ +
+

Export to Ticket

+

When the session ends, full documentation is generated — formatted for your PSA. Paste it directly into ConnectWise, Atera, or Syncro. Done.

+
+
+
ConnectWise Ticket #48291
+
10:04Verified VPN connection active
+
10:06Split tunnel misconfigured — fixed
+
10:08Confirmed Outlook sync restored
+
10:09Resolution: VPN split tunnel updated
+
+
+
+
+
+
+ +
+ + {/* Features */} +
+
+
Features
+
Everything your team needs to
resolve faster and document better.
+
+ } + title="FlowPilot — Your AI Copilot" + description="Like having a senior engineer on every call. FlowPilot suggests next steps, provides context-aware guidance, and automatically captures documentation as a byproduct of the troubleshooting session." + /> + } + title="Visual Flow Editor" + description="Build branching decision trees with a drag-and-drop canvas. Add steps, conditions, commands, and notes — no code required." + /> + } + title="Auto-Documentation" + description="Every session generates timestamped, detailed notes — formatted for your PSA. Engineers never write another ticket note." + /> + } + title="Team Knowledge Sharing" + description="Share flows across your team. When one engineer solves a new problem, the whole team benefits from that path — instantly." + /> + } + title="Session History & Analytics" + description="Track which flows are used most, identify bottlenecks, and see how your team resolves issues over time." + /> + } + title="PSA Integration" + description="Connect directly to ConnectWise, Atera, and Syncro. Export session docs straight to tickets — no copy-paste needed." + /> +
+
+
+ +
+ + {/* Pricing */} +
+
+
Pricing
+
Simple pricing. No surprises.
+
Start free. Upgrade when your team is ready.
+
+ + + +
+

+ Need Enterprise (25+ techs, SSO, custom branding)?{' '} + Contact us +

+
+
+ +
+ + {/* Testimonial */} +
+
+ We used to spend more time writing ticket notes than solving the actual issue. Now it just… happens. The documentation writes itself while we work. +
+
+ Beta Tester — MSP Engineer, Southeast US +
+
+ +
+ + {/* CTA */} +
+

Ready to stop writing ticket notes?

+

Join the beta and see what happens when documentation becomes automatic.

+
+ setBetaEmail(e.target.value)} + required + /> + +
+ {betaStatus === 'sent' && ( +

Thanks! We'll be in touch with beta access details.

+ )} + {betaStatus === 'error' && ( +

Something went wrong. Please try again.

+ )} +

Free to start. No credit card required.

+
+ + {/* Footer */} +
+
+
+
+ + + + + + + + +
+ © 2026 ResolutionFlow. All rights reserved. +
+ +
+
+
+
+ + ) +} + + +/* ---- Sub-components ---- */ + +function ProblemCard({ icon, color, title, description }: { + icon: string; color: string; title: string; description: string +}) { + return ( +
+
{icon}
+

{title}

+

{description}

+
+ ) +} + +function FeatureCard({ icon, title, description, highlight }: { + icon: React.ReactNode; title: string; description: string; highlight?: boolean +}) { + return ( +
+
{icon}
+

{title}

+

{description}

+
+ ) +} + +function PricingCard({ name, target, amount, period, note, features, btnLabel, btnStyle, featured }: { + name: string; target: string; amount: string; period?: string; note: string + features: string[]; btnLabel: string; btnStyle: 'outline' | 'filled'; featured?: boolean +}) { + return ( +
+
{name}
+
{target}
+
+ {amount} + {period && {period}} +
+
{note}
+
    + {features.map(f =>
  • {f}
  • )} +
+ {btnLabel} +
+ ) +} diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index a5d3f190..10b5ad12 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -13,6 +13,7 @@ import { } from '@/pages' // Public pages +const LandingPage = lazy(() => import('@/pages/LandingPage')) const SharedSessionPage = lazy(() => import('@/pages/SharedSessionPage')) const SurveyPage = lazy(() => import('@/pages/SurveyPage')) const SurveyThankYouPage = lazy(() => import('@/pages/SurveyThankYouPage')) @@ -78,6 +79,11 @@ function page(Component: React.LazyExoticComponent) { } export const router = sentryCreateBrowserRouter([ + { + path: '/landing', + element: page(LandingPage), + errorElement: , + }, { path: '/login', element: , diff --git a/frontend/src/styles/landing.css b/frontend/src/styles/landing.css new file mode 100644 index 00000000..73509b18 --- /dev/null +++ b/frontend/src/styles/landing.css @@ -0,0 +1,1133 @@ +/* ============================================ + RESOLUTIONFLOW LANDING PAGE + All classes prefixed with landing- to avoid + collisions with the main app styles. + ============================================ */ + +.landing-page { + font-family: 'IBM Plex Sans', sans-serif; + background: hsl(228, 12%, 7%); + color: #f0f0f5; + line-height: 1.6; + overflow-x: hidden; + -webkit-font-smoothing: antialiased; + min-height: 100vh; +} + +/* ---- AMBIENT BACKGROUND ---- */ +.landing-ambient-glow { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; + overflow: hidden; +} +.landing-ambient-glow::before { + content: ''; + position: absolute; + top: -30%; + left: 50%; + transform: translateX(-50%); + width: 900px; + height: 900px; + background: radial-gradient(circle, rgba(6,182,212,0.06) 0%, transparent 70%); + animation: landingAmbientPulse 12s ease-in-out infinite; +} +.landing-ambient-glow::after { + content: ''; + position: absolute; + bottom: -20%; + right: -10%; + width: 600px; + height: 600px; + background: radial-gradient(circle, rgba(34,211,238,0.04) 0%, transparent 70%); + animation: landingAmbientPulse 16s ease-in-out infinite reverse; +} +@keyframes landingAmbientPulse { + 0%, 100% { opacity: 0.6; transform: translateX(-50%) scale(1); } + 50% { opacity: 1; transform: translateX(-50%) scale(1.15); } +} + +/* ---- GRID PATTERN ---- */ +.landing-grid-pattern { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; + background-image: + linear-gradient(rgba(255,255,255,0.015) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,0.015) 1px, transparent 1px); + background-size: 64px 64px; + mask-image: radial-gradient(ellipse at 50% 30%, black 20%, transparent 70%); +} + +.landing-page-content { position: relative; z-index: 1; } + +/* ---- NAVIGATION ---- */ +.landing-nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + padding: 0 2rem; + transition: all 0.3s ease; +} +.landing-nav.scrolled { + background: rgba(14, 15, 24, 0.85); + backdrop-filter: blur(20px) saturate(1.2); + border-bottom: 1px solid rgba(255,255,255,0.06); +} +.landing-nav-inner { + max-width: 1200px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + height: 72px; +} +.landing-nav-logo { + display: flex; + align-items: center; + gap: 10px; + text-decoration: none; +} +.landing-nav-logo-icon { + width: 36px; + height: 36px; + background: linear-gradient(135deg, #06b6d4, #22d3ee); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 20px rgba(6,182,212,0.2); +} +.landing-nav-logo-icon svg { width: 20px; height: 20px; } +.landing-nav-wordmark { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.25rem; + font-weight: 700; + color: #f0f0f5; + letter-spacing: -0.02em; +} +.landing-nav-wordmark span { + background: linear-gradient(135deg, #22d3ee, #67e8f9); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.landing-nav-links { + display: flex; + align-items: center; + gap: 2rem; + list-style: none; + margin: 0; + padding: 0; +} +.landing-nav-links a { + font-size: 0.875rem; + font-weight: 500; + color: #a0a0b0; + text-decoration: none; + transition: color 0.2s; + letter-spacing: 0.01em; +} +.landing-nav-links a:hover { color: #f0f0f5; } +.landing-nav-cta { + display: flex; + align-items: center; + gap: 1rem; +} +.landing-btn-ghost { + font-size: 0.875rem; + font-weight: 500; + color: #a0a0b0; + text-decoration: none; + padding: 0.5rem 1rem; + border-radius: 8px; + transition: all 0.2s; +} +.landing-btn-ghost:hover { color: #f0f0f5; background: rgba(255,255,255,0.06); } +.landing-btn-primary { + font-size: 0.875rem; + font-weight: 600; + color: #000; + text-decoration: none; + padding: 0.55rem 1.25rem; + border-radius: 8px; + background: linear-gradient(135deg, #06b6d4, #22d3ee); + box-shadow: 0 0 20px rgba(6,182,212,0.15), inset 0 1px 0 rgba(255,255,255,0.15); + transition: all 0.25s; + letter-spacing: -0.01em; + border: none; + cursor: pointer; +} +.landing-btn-primary:hover { + box-shadow: 0 0 30px rgba(6,182,212,0.3), inset 0 1px 0 rgba(255,255,255,0.2); + transform: translateY(-1px); +} + +/* ---- HERO ---- */ +.landing-hero { + padding: 10rem 2rem 6rem; + text-align: center; + max-width: 900px; + margin: 0 auto; +} +.landing-hero-badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 16px; + border-radius: 100px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + font-family: 'JetBrains Mono', monospace; + font-size: 0.75rem; + font-weight: 500; + color: #22d3ee; + margin-bottom: 2rem; + letter-spacing: 0.03em; + animation: landingFadeInDown 0.8s ease-out; +} +.landing-hero-badge::before { + content: ''; + width: 6px; + height: 6px; + border-radius: 50%; + background: #22d3ee; + animation: landingPulse 2s ease-in-out infinite; +} +@keyframes landingPulse { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; box-shadow: 0 0 8px #22d3ee; } +} +.landing-hero h1 { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: clamp(2.75rem, 6vw, 4.25rem); + font-weight: 800; + line-height: 1.08; + letter-spacing: -0.035em; + margin-bottom: 1.5rem; + animation: landingFadeInUp 0.8s ease-out 0.15s both; +} +.landing-gradient-text { + background: linear-gradient(135deg, #22d3ee, #67e8f9); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.landing-hero-sub { + font-size: 1.2rem; + font-weight: 400; + color: #a0a0b0; + line-height: 1.7; + max-width: 600px; + margin: 0 auto 2.5rem; + animation: landingFadeInUp 0.8s ease-out 0.3s both; +} +.landing-hero-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + animation: landingFadeInUp 0.8s ease-out 0.45s both; +} +.landing-btn-hero-primary { + font-size: 1rem; + font-weight: 600; + color: #000; + text-decoration: none; + padding: 0.85rem 2rem; + border-radius: 10px; + background: linear-gradient(135deg, #06b6d4, #22d3ee); + box-shadow: 0 4px 30px rgba(6,182,212,0.2), inset 0 1px 0 rgba(255,255,255,0.15); + transition: all 0.3s; + letter-spacing: -0.01em; + border: none; + cursor: pointer; +} +.landing-btn-hero-primary:hover { + box-shadow: 0 4px 40px rgba(6,182,212,0.35), inset 0 1px 0 rgba(255,255,255,0.2); + transform: translateY(-2px); +} +.landing-btn-hero-primary:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} +.landing-btn-hero-secondary { + font-size: 1rem; + font-weight: 500; + color: #a0a0b0; + text-decoration: none; + padding: 0.85rem 2rem; + border-radius: 10px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + transition: all 0.3s; +} +.landing-btn-hero-secondary:hover { + background: rgba(255,255,255,0.06); + border-color: rgba(255,255,255,0.1); + color: #f0f0f5; +} + +/* ---- SOCIAL PROOF BAR ---- */ +.landing-social-proof-bar { + text-align: center; + padding: 3rem 2rem 1rem; + animation: landingFadeInUp 0.8s ease-out 0.6s both; +} +.landing-social-proof-bar > p { + font-family: 'JetBrains Mono', monospace; + font-size: 0.75rem; + color: #5a5a6e; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 1.5rem; +} +.landing-proof-stats { + display: flex; + justify-content: center; + gap: 3rem; + flex-wrap: wrap; +} +.landing-proof-stat { text-align: center; } +.landing-proof-stat .number { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.75rem; + font-weight: 700; + background: linear-gradient(135deg, #22d3ee, #67e8f9); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.landing-proof-stat .label { + font-size: 0.8rem; + color: #5a5a6e; + margin-top: 0.25rem; +} + +/* ---- APP PREVIEW ---- */ +.landing-app-preview { + max-width: 1000px; + margin: 3rem auto 0; + padding: 0 2rem; + animation: landingFadeInUp 1s ease-out 0.75s both; +} +.landing-preview-window { + border-radius: 12px; + border: 1px solid hsl(228, 8%, 16%); + background: hsl(228, 10%, 10%); + overflow: hidden; + box-shadow: 0 20px 80px rgba(0,0,0,0.4), 0 0 60px rgba(6,182,212,0.05); +} +.landing-preview-titlebar { + display: flex; + align-items: center; + padding: 0 0 0 14px; + height: 40px; + background: rgba(255,255,255,0.02); + border-bottom: 1px solid rgba(255,255,255,0.06); +} +.landing-preview-tab { + display: flex; + align-items: center; + gap: 8px; + padding: 0 14px; + height: 100%; + font-family: 'JetBrains Mono', monospace; + font-size: 0.7rem; + color: #a0a0b0; + border-right: 1px solid rgba(255,255,255,0.06); + background: rgba(255,255,255,0.02); + position: relative; +} +.landing-preview-tab::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(135deg, #06b6d4, #22d3ee); +} +.landing-tab-icon { + width: 14px; + height: 14px; + border-radius: 3px; + background: linear-gradient(135deg, #06b6d4, #22d3ee); + flex-shrink: 0; +} +.landing-tab-close { + font-size: 0.65rem; + color: #5a5a6e; + margin-left: 4px; +} +.landing-preview-url-bar { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.landing-preview-url { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 12px; + border-radius: 6px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + color: #5a5a6e; +} +.landing-lock-icon { color: #22c55e; font-size: 0.6rem; } +.landing-preview-window-controls { + display: flex; + align-items: center; + margin-left: auto; + height: 100%; +} +.landing-win-btn { + width: 46px; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: #5a5a6e; + font-size: 0.7rem; + transition: background 0.15s; +} +.landing-win-btn:hover { background: rgba(255,255,255,0.06); } +.landing-win-btn.close:hover { background: #c42b1c; color: white; } +.landing-win-btn svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 1.5; } +.landing-preview-body { + padding: 2rem; + min-height: 340px; + display: flex; + gap: 1.5rem; +} +.landing-preview-sidebar { + width: 200px; + flex-shrink: 0; + display: flex; + flex-direction: column; + gap: 0.5rem; +} +.landing-preview-sidebar-item { + padding: 8px 12px; + border-radius: 8px; + font-size: 0.8rem; + color: #a0a0b0; + display: flex; + align-items: center; + gap: 8px; +} +.landing-preview-sidebar-item.active { + background: rgba(6,182,212,0.08); + color: #22d3ee; + border-left: 3px solid #22d3ee; +} +.landing-preview-sidebar-item .dot { + width: 8px; + height: 8px; + border-radius: 50%; +} +.landing-preview-canvas { + flex: 1; + background: rgba(255,255,255,0.01); + border-radius: 12px; + border: 1px dashed rgba(255,255,255,0.06); + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; +} + +/* Mini tree */ +.landing-mini-tree { + display: flex; + flex-direction: column; + align-items: center; +} +.landing-tree-node { + padding: 10px 20px; + border-radius: 10px; + font-size: 0.75rem; + font-weight: 600; + white-space: nowrap; +} +.landing-tree-node.root { + background: linear-gradient(135deg, #06b6d4, #22d3ee); + color: #000; + box-shadow: 0 0 20px rgba(6,182,212,0.2); +} +.landing-tree-node.decision { + background: hsl(228, 10%, 10%); + border: 1px solid hsl(228, 8%, 16%); + color: #f0f0f5; + font-size: 0.7rem; +} +.landing-tree-connector { + width: 2px; + height: 24px; + background: rgba(6,182,212,0.2); +} +.landing-tree-branch { + display: flex; + gap: 2rem; + position: relative; +} +.landing-tree-branch::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + width: calc(100% - 100px); + transform: translateX(-50%); + height: 2px; + background: rgba(6,182,212,0.15); +} +.landing-tree-branch-arm { + display: flex; + flex-direction: column; + align-items: center; +} +.landing-tree-branch-arm .landing-tree-connector { height: 18px; } +.landing-tree-label { + font-family: 'JetBrains Mono', monospace; + font-size: 0.6rem; + color: #22d3ee; + margin-bottom: 4px; + opacity: 0.7; +} + +/* ---- SECTION LAYOUT ---- */ +.landing-page section { padding: 6rem 2rem; } +.landing-section-inner { max-width: 1100px; margin: 0 auto; } +.landing-section-label { + font-family: 'JetBrains Mono', monospace; + font-size: 0.7rem; + font-weight: 600; + color: #22d3ee; + letter-spacing: 0.12em; + text-transform: uppercase; + margin-bottom: 1rem; +} +.landing-section-title { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: clamp(2rem, 4vw, 2.75rem); + font-weight: 700; + letter-spacing: -0.03em; + line-height: 1.15; + margin-bottom: 1rem; +} +.landing-section-desc { + font-size: 1.1rem; + color: #a0a0b0; + line-height: 1.7; + max-width: 580px; +} + +/* ---- SECTION DIVIDER ---- */ +.landing-section-divider { + max-width: 1100px; + margin: 0 auto; + height: 1px; + background: rgba(255,255,255,0.06); +} + +/* ---- PROBLEM SECTION ---- */ +.landing-problem-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + margin-top: 3.5rem; + align-items: start; +} +.landing-problem-card { + padding: 2rem; + border-radius: 16px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + transition: all 0.3s; +} +.landing-problem-card:hover { + background: rgba(255,255,255,0.06); + border-color: rgba(255,255,255,0.08); + transform: translateY(-2px); +} +.landing-problem-icon { + width: 44px; + height: 44px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.25rem; + font-size: 1.25rem; +} +.landing-problem-icon.red { background: rgba(239,68,68,0.1); color: #ef4444; } +.landing-problem-icon.amber { background: rgba(245,158,11,0.1); color: #f59e0b; } +.landing-problem-icon.slate { background: rgba(148,163,184,0.1); color: #94a3b8; } +.landing-problem-icon.violet { background: rgba(139,92,246,0.1); color: #8b5cf6; } +.landing-problem-card h3 { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.15rem; + font-weight: 700; + margin-bottom: 0.6rem; + letter-spacing: -0.01em; +} +.landing-problem-card p { + font-size: 0.9rem; + color: #a0a0b0; + line-height: 1.65; +} + +/* ---- EQUATION ---- */ +.landing-equation-section { + text-align: center; + padding: 5rem 2rem; + position: relative; +} +.landing-equation-section::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 500px; + height: 100%; + background: radial-gradient(ellipse, rgba(6,182,212,0.04) 0%, transparent 70%); + pointer-events: none; +} +.landing-brand-equation { + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + flex-wrap: wrap; + font-family: 'Bricolage Grotesque', sans-serif; + font-size: clamp(1.5rem, 3.5vw, 2.5rem); + font-weight: 700; + letter-spacing: -0.02em; + margin-bottom: 1.5rem; +} +.landing-eq-item { + padding: 0.5rem 1.25rem; + border-radius: 12px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); +} +.landing-eq-operator { color: #22d3ee; font-size: 2rem; } +.landing-eq-result { + background: linear-gradient(135deg, #06b6d4, #22d3ee); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + padding: 0; +} +.landing-equation-desc { + font-size: 1.1rem; + color: #a0a0b0; + max-width: 550px; + margin: 0 auto; + line-height: 1.7; +} + +/* ---- HOW IT WORKS ---- */ +.landing-steps-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + margin-top: 3.5rem; + counter-reset: landing-step; +} +.landing-step-card { + padding: 2rem; + border-radius: 16px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + position: relative; + counter-increment: landing-step; + transition: all 0.3s; +} +.landing-step-card:hover { + background: rgba(255,255,255,0.06); + border-color: rgba(6,182,212,0.15); +} +.landing-step-card::before { + content: counter(landing-step, decimal-leading-zero); + font-family: 'JetBrains Mono', monospace; + font-size: 0.7rem; + font-weight: 600; + color: #22d3ee; + opacity: 0.6; + letter-spacing: 0.05em; +} +.landing-step-card h3 { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.25rem; + font-weight: 700; + margin: 1rem 0 0.75rem; + letter-spacing: -0.01em; +} +.landing-step-card p { + font-size: 0.9rem; + color: #a0a0b0; + line-height: 1.65; +} +.landing-step-visual { + width: 100%; + height: 120px; + border-radius: 10px; + margin-top: 1.25rem; + background: hsl(228, 10%, 10%); + border: 1px solid hsl(228, 8%, 16%); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +/* Mock editor */ +.landing-mock-editor { + display: flex; + gap: 0.5rem; + padding: 0.75rem; + width: 100%; + align-items: center; +} +.landing-mock-node { + padding: 6px 10px; + border-radius: 6px; + font-size: 0.6rem; + font-family: 'JetBrains Mono', monospace; + white-space: nowrap; +} +.landing-mock-node.start { background: rgba(6,182,212,0.15); color: #22d3ee; border: 1px solid rgba(6,182,212,0.2); } +.landing-mock-node.step { background: rgba(255,255,255,0.03); color: #a0a0b0; border: 1px solid rgba(255,255,255,0.06); } +.landing-mock-connector { width: 20px; display: flex; align-items: center; justify-content: center; color: #5a5a6e; font-size: 0.7rem; } + +/* Mock session */ +.landing-mock-session { + width: 100%; + padding: 0.75rem; + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.landing-mock-chat-line { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.6rem; + font-family: 'JetBrains Mono', monospace; +} +.landing-mock-chat-line .label { + color: #22d3ee; + font-weight: 600; + min-width: 55px; +} +.landing-mock-chat-line .text { color: #a0a0b0; } +.landing-mock-chat-line.doc .label { color: #22c55e; } + +/* Mock ticket */ +.landing-mock-ticket { + width: 100%; + padding: 0.75rem; + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.landing-mock-ticket-header { + font-family: 'JetBrains Mono', monospace; + font-size: 0.6rem; + color: #22d3ee; + font-weight: 600; + padding-bottom: 0.35rem; + border-bottom: 1px solid rgba(255,255,255,0.06); + margin-bottom: 0.15rem; +} +.landing-mock-ticket-line { + font-size: 0.55rem; + font-family: 'JetBrains Mono', monospace; + color: #a0a0b0; + display: flex; + gap: 6px; +} +.landing-mock-ticket-line .time { color: #5a5a6e; min-width: 35px; } +.landing-mock-ticket-line .check { color: #22c55e; } + +/* ---- FEATURES GRID ---- */ +.landing-features-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-top: 3.5rem; +} +.landing-feature-card { + padding: 2rem; + border-radius: 16px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + transition: all 0.3s ease; +} +.landing-feature-card:hover { + background: rgba(255,255,255,0.06); + border-color: rgba(6,182,212,0.15); + transform: translateY(-3px); +} +.landing-feature-icon { + width: 40px; + height: 40px; + border-radius: 10px; + background: rgba(6,182,212,0.08); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.25rem; + color: #22d3ee; +} +.landing-feature-card h3 { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.05rem; + font-weight: 700; + margin-bottom: 0.6rem; + letter-spacing: -0.01em; +} +.landing-feature-card p { + font-size: 0.875rem; + color: #a0a0b0; + line-height: 1.6; +} +.landing-feature-card.highlight { + border-color: rgba(6,182,212,0.15); + background: rgba(6,182,212,0.03); + grid-column: span 2; +} + +/* ---- PRICING ---- */ +.landing-pricing-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-top: 3.5rem; + align-items: start; +} +.landing-pricing-card { + padding: 2.5rem 2rem; + border-radius: 16px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.06); + transition: all 0.3s; +} +.landing-pricing-card:hover { border-color: rgba(255,255,255,0.08); } +.landing-pricing-card.featured { + border-color: rgba(6,182,212,0.2); + background: rgba(6,182,212,0.03); + position: relative; + box-shadow: 0 0 40px rgba(6,182,212,0.06); +} +.landing-pricing-card.featured::before { + content: 'Most Popular'; + position: absolute; + top: -12px; + left: 50%; + transform: translateX(-50%); + padding: 4px 14px; + border-radius: 100px; + background: linear-gradient(135deg, #06b6d4, #22d3ee); + font-family: 'JetBrains Mono', monospace; + font-size: 0.65rem; + font-weight: 600; + color: #000; + letter-spacing: 0.05em; + text-transform: uppercase; +} +.landing-pricing-plan-name { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.15rem; + font-weight: 700; + margin-bottom: 0.5rem; + letter-spacing: -0.01em; +} +.landing-pricing-target { + font-size: 0.8rem; + color: #5a5a6e; + margin-bottom: 1.5rem; +} +.landing-pricing-price { + display: flex; + align-items: baseline; + gap: 0.25rem; + margin-bottom: 0.25rem; +} +.landing-pricing-price .amount { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 2.75rem; + font-weight: 800; + letter-spacing: -0.04em; +} +.landing-pricing-card.featured .landing-pricing-price .amount { + background: linear-gradient(135deg, #22d3ee, #67e8f9); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} +.landing-pricing-price .period { + font-size: 0.85rem; + color: #5a5a6e; +} +.landing-pricing-note { + font-size: 0.8rem; + color: #5a5a6e; + margin-bottom: 2rem; +} +.landing-pricing-features { + list-style: none; + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-bottom: 2rem; + padding: 0; +} +.landing-pricing-features li { + font-size: 0.875rem; + color: #a0a0b0; + display: flex; + align-items: flex-start; + gap: 0.6rem; +} +.landing-pricing-features li::before { + content: '\2713'; + color: #22d3ee; + font-weight: 700; + font-size: 0.8rem; + margin-top: 2px; + flex-shrink: 0; +} +.landing-pricing-btn { + display: block; + text-align: center; + padding: 0.75rem 1.5rem; + border-radius: 10px; + font-size: 0.9rem; + font-weight: 600; + text-decoration: none; + transition: all 0.3s; + width: 100%; +} +.landing-pricing-btn.outline { + color: #f0f0f5; + border: 1px solid hsl(228, 8%, 16%); + background: transparent; +} +.landing-pricing-btn.outline:hover { + background: rgba(255,255,255,0.06); + border-color: rgba(255,255,255,0.12); +} +.landing-pricing-btn.filled { + color: #000; + background: linear-gradient(135deg, #06b6d4, #22d3ee); + box-shadow: 0 0 20px rgba(6,182,212,0.15); + border: none; +} +.landing-pricing-btn.filled:hover { + box-shadow: 0 0 30px rgba(6,182,212,0.3); + transform: translateY(-1px); +} +.landing-pricing-enterprise { + text-align: center; + margin-top: 2rem; + font-size: 0.85rem; + color: #5a5a6e; +} +.landing-pricing-enterprise a { color: #22d3ee; text-decoration: none; } +.landing-pricing-enterprise a:hover { text-decoration: underline; } + +/* ---- TESTIMONIAL ---- */ +.landing-testimonial-section { + text-align: center; + max-width: 700px; + margin: 0 auto; + padding: 5rem 2rem; +} +.landing-testimonial-quote { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: 1.5rem; + font-weight: 600; + line-height: 1.5; + letter-spacing: -0.01em; + color: #f0f0f5; + font-style: italic; + margin-bottom: 1.5rem; + position: relative; +} +.landing-testimonial-quote::before { + content: '\201C'; + font-size: 4rem; + color: #22d3ee; + opacity: 0.3; + position: absolute; + top: -1rem; + left: -0.5rem; + font-family: 'Bricolage Grotesque', sans-serif; + line-height: 1; +} +.landing-testimonial-author { + font-size: 0.9rem; + color: #5a5a6e; +} +.landing-testimonial-author strong { color: #a0a0b0; font-weight: 600; } + +/* ---- CTA ---- */ +.landing-cta-section { + text-align: center; + padding: 6rem 2rem; + position: relative; +} +.landing-cta-section::before { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 700px; + height: 400px; + background: radial-gradient(ellipse, rgba(6,182,212,0.05) 0%, transparent 70%); + pointer-events: none; +} +.landing-cta-section h2 { + font-family: 'Bricolage Grotesque', sans-serif; + font-size: clamp(2rem, 4vw, 2.75rem); + font-weight: 700; + letter-spacing: -0.03em; + margin-bottom: 1rem; +} +.landing-cta-section > p { + font-size: 1.1rem; + color: #a0a0b0; + max-width: 500px; + margin: 0 auto 2rem; + line-height: 1.7; +} +.landing-cta-email-form { + display: flex; + gap: 0.75rem; + max-width: 440px; + margin: 0 auto; +} +.landing-cta-email-input { + flex: 1; + padding: 0.85rem 1.25rem; + border-radius: 10px; + border: 1px solid hsl(228, 8%, 16%); + background: hsl(228, 10%, 10%); + color: #f0f0f5; + font-family: 'IBM Plex Sans', sans-serif; + font-size: 0.9rem; + outline: none; + transition: border-color 0.3s; +} +.landing-cta-email-input::placeholder { color: #5a5a6e; } +.landing-cta-email-input:focus { + border-color: #06b6d4; + box-shadow: 0 0 0 3px rgba(6,182,212,0.1); +} +.landing-cta-fine-print { + font-size: 0.75rem; + color: #5a5a6e; + margin-top: 1rem; +} +.landing-cta-success { + font-size: 0.85rem; + color: #22c55e; + margin-top: 0.75rem; +} +.landing-cta-error { + font-size: 0.85rem; + color: #ef4444; + margin-top: 0.75rem; +} + +/* ---- FOOTER ---- */ +.landing-footer { + border-top: 1px solid rgba(255,255,255,0.06); + padding: 3rem 2rem; +} +.landing-footer-inner { + max-width: 1100px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; +} +.landing-footer-left { + display: flex; + align-items: center; + gap: 0.75rem; +} +.landing-footer-copy { + font-size: 0.8rem; + color: #5a5a6e; +} +.landing-footer-links { + display: flex; + gap: 1.5rem; + list-style: none; + margin: 0; + padding: 0; +} +.landing-footer-links a { + font-size: 0.8rem; + color: #5a5a6e; + text-decoration: none; + transition: color 0.2s; +} +.landing-footer-links a:hover { color: #a0a0b0; } + +/* ---- ANIMATIONS ---- */ +@keyframes landingFadeInUp { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} +@keyframes landingFadeInDown { + from { opacity: 0; transform: translateY(-12px); } + to { opacity: 1; transform: translateY(0); } +} + +/* ---- SCROLL REVEAL ---- */ +.landing-reveal { + opacity: 0; + transform: translateY(24px); + transition: opacity 0.7s ease, transform 0.7s ease; +} +.landing-reveal.visible { + opacity: 1; + transform: translateY(0); +} + +/* ---- RESPONSIVE ---- */ +@media (max-width: 900px) { + .landing-problem-grid, + .landing-steps-container, + .landing-features-grid, + .landing-pricing-grid { + grid-template-columns: 1fr; + } + .landing-feature-card.highlight { grid-column: span 1; } + .landing-nav-links { display: none; } + .landing-preview-sidebar { display: none; } + .landing-tree-branch { gap: 1rem; } + .landing-hero { padding: 8rem 1.5rem 4rem; } + .landing-brand-equation { font-size: 1.25rem; gap: 0.5rem; } + .landing-eq-item { padding: 0.35rem 0.75rem; } + .landing-cta-email-form { flex-direction: column; } +}