diff --git a/frontend/index.html b/frontend/index.html index ab99752d..892d7a42 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,10 +1,10 @@ - + - ResolutionFlow - Decision Tree Platform + ResolutionFlow — AI-Powered Troubleshooting for MSPs diff --git a/frontend/src/components/common/PageLoader.tsx b/frontend/src/components/common/PageLoader.tsx index 04c54daa..4bb62571 100644 --- a/frontend/src/components/common/PageLoader.tsx +++ b/frontend/src/components/common/PageLoader.tsx @@ -5,7 +5,7 @@ export function PageLoader() {
-

Loading...

+

Loading…

) diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index 9ed09a69..15cb88d9 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from 'react' +import { useState, useEffect, useCallback, useRef } from 'react' import { Link } from 'react-router-dom' import { PageMeta } from '@/components/common/PageMeta' import '@/styles/landing.css' @@ -7,8 +7,10 @@ const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000' export default function LandingPage() { const [navScrolled, setNavScrolled] = useState(false) + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [betaEmail, setBetaEmail] = useState('') const [betaStatus, setBetaStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle') + const mobileMenuRef = useRef(null) // Nav scroll effect useEffect(() => { @@ -17,6 +19,22 @@ export default function LandingPage() { return () => window.removeEventListener('scroll', handleScroll) }, []) + // Close mobile menu on click outside + useEffect(() => { + function handleClickOutside(e: MouseEvent) { + if (mobileMenuRef.current && !mobileMenuRef.current.contains(e.target as Node)) { + setMobileMenuOpen(false) + } + } + if (mobileMenuOpen) { + document.addEventListener('mousedown', handleClickOutside) + return () => document.removeEventListener('mousedown', handleClickOutside) + } + }, [mobileMenuOpen]) + + // Close mobile menu on scroll to section + const handleMobileNavClick = () => setMobileMenuOpen(false) + // Scroll reveal useEffect(() => { const els = document.querySelectorAll('.landing-reveal') @@ -64,7 +82,7 @@ export default function LandingPage() {
{/* Navigation */} -
-
0
-
Ticket Notes Written by Hand
+
100%
+
Auto-Generated Documentation
@@ -198,14 +236,13 @@ export default function LandingPage() { -
{/* Problem Section */}
The Problem
-
Documentation is broken.
Everyone knows it.
+

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.
@@ -243,7 +280,7 @@ export default function LandingPage() {
How It Works
-
Three steps. Zero note-writing.
+

Three steps. Zero note-writing.

Build once, run forever. Every session generates documentation automatically.
@@ -312,7 +349,7 @@ export default function LandingPage() {
Features
-
Everything your team needs to
resolve faster and document better.
+

Everything your team needs to
resolve faster and document better.

Pricing
-
Simple pricing. No surprises.
+

Simple pricing. No surprises.

Start free. Upgrade when your team is ready.
© 2026 ResolutionFlow. All rights reserved.
diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index deffe142..7ed07844 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -79,7 +79,7 @@ export function LoginPage() { ResolutionFlow

- Decision Tree Platform + AI-Powered Troubleshooting for MSPs

Sign in to your account diff --git a/frontend/src/pages/PrivacyPage.tsx b/frontend/src/pages/PrivacyPage.tsx new file mode 100644 index 00000000..8e7662a5 --- /dev/null +++ b/frontend/src/pages/PrivacyPage.tsx @@ -0,0 +1,44 @@ +import { Link } from 'react-router-dom' +import { PageMeta } from '@/components/common/PageMeta' + +export default function PrivacyPage() { + return ( + <> + +

+
+ ← Back to home +

Privacy Policy

+

Last updated: March 21, 2026

+ +
+
+

1. Information We Collect

+

When you use ResolutionFlow, we collect information you provide directly: your name, email address, and account credentials. We also collect usage data including session logs, flow configurations, and troubleshooting documentation generated through the platform.

+
+ +
+

2. How We Use Your Information

+

We use your information to provide and improve ResolutionFlow, including: operating your account, generating documentation from troubleshooting sessions, providing AI-powered guidance through FlowPilot, and communicating with you about the service.

+
+ +
+

3. Data Storage & Security

+

Your data is stored securely using industry-standard encryption. PSA credentials (ConnectWise, Atera, Syncro) are encrypted at rest. We do not sell your data to third parties.

+
+ +
+

4. AI & Data Processing

+

FlowPilot uses AI models to provide troubleshooting guidance. Session data sent to AI providers is used only for generating responses within your session and is not used to train AI models.

+
+ +
+

5. Contact

+

Questions about this policy? Email us at hello@resolutionflow.com.

+
+
+
+
+ + ) +} diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx index 9565cffd..d3d5e9d5 100644 --- a/frontend/src/pages/RegisterPage.tsx +++ b/frontend/src/pages/RegisterPage.tsx @@ -86,15 +86,13 @@ export function RegisterPage() {
-
- -
+

ResolutionFlow

- Decision Tree Platform + AI-Powered Troubleshooting for MSPs

Create your account diff --git a/frontend/src/pages/TermsPage.tsx b/frontend/src/pages/TermsPage.tsx new file mode 100644 index 00000000..65dfba59 --- /dev/null +++ b/frontend/src/pages/TermsPage.tsx @@ -0,0 +1,49 @@ +import { Link } from 'react-router-dom' +import { PageMeta } from '@/components/common/PageMeta' + +export default function TermsPage() { + return ( + <> + +

+
+ ← Back to home +

Terms of Service

+

Last updated: March 21, 2026

+ +
+
+

1. Acceptance of Terms

+

By accessing or using ResolutionFlow, you agree to be bound by these Terms of Service. If you are using ResolutionFlow on behalf of an organization, you represent that you have authority to bind that organization to these terms.

+
+ +
+

2. Description of Service

+

ResolutionFlow provides AI-guided troubleshooting decision trees, automated documentation generation, and PSA integration tools for managed service providers (MSPs). The service includes FlowPilot AI copilot, flow editor, session management, and team collaboration features.

+
+ +
+

3. Account Responsibilities

+

You are responsible for maintaining the security of your account credentials and for all activities that occur under your account. You must notify us immediately of any unauthorized use.

+
+ +
+

4. Data Ownership

+

You retain ownership of all content you create within ResolutionFlow, including flows, session documentation, and team knowledge. We do not claim ownership of your data.

+
+ +
+

5. Service Availability

+

ResolutionFlow is currently in beta. We strive for high availability but do not guarantee uninterrupted access during the beta period. We will provide reasonable notice of planned maintenance.

+
+ +
+

6. Contact

+

Questions about these terms? Email us at hello@resolutionflow.com.

+
+
+
+
+ + ) +} diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 05fd004a..e9d0f8ee 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -18,6 +18,8 @@ const PublicTemplatesPage = lazy(() => import('@/pages/PublicTemplatesPage')) const SharedSessionPage = lazy(() => import('@/pages/SharedSessionPage')) const SurveyPage = lazy(() => import('@/pages/SurveyPage')) const SurveyThankYouPage = lazy(() => import('@/pages/SurveyThankYouPage')) +const PrivacyPage = lazy(() => import('@/pages/PrivacyPage')) +const TermsPage = lazy(() => import('@/pages/TermsPage')) // Standalone auth pages const VerifyEmailPage = lazy(() => import('@/pages/VerifyEmailPage')) @@ -100,6 +102,16 @@ export const router = sentryCreateBrowserRouter([ element: page(PublicTemplatesPage), errorElement: , }, + { + path: '/privacy', + element: page(PrivacyPage), + errorElement: , + }, + { + path: '/terms', + element: page(TermsPage), + errorElement: , + }, { path: '/login', element: , diff --git a/frontend/src/styles/landing.css b/frontend/src/styles/landing.css index 5abbc252..1ec7ef5e 100644 --- a/frontend/src/styles/landing.css +++ b/frontend/src/styles/landing.css @@ -86,7 +86,7 @@ right: 0; z-index: 100; padding: 0 2rem; - transition: all 0.3s ease; + transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease, opacity 0.3s ease; } .landing-nav.scrolled { @@ -158,6 +158,7 @@ text-decoration: none; transition: color 0.2s; letter-spacing: 0.01em; + padding: 0.75rem 0.5rem; } .landing-nav-links a:hover { @@ -177,7 +178,7 @@ text-decoration: none; padding: 0.5rem 1rem; border-radius: 8px; - transition: all 0.2s; + transition: color 0.2s, background 0.2s; } .landing-btn-ghost:hover { @@ -194,7 +195,7 @@ 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; + transition: opacity 0.25s, transform 0.25s, box-shadow 0.25s; letter-spacing: -0.01em; border: none; cursor: pointer; @@ -296,7 +297,7 @@ 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; + transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s; letter-spacing: -0.01em; border: none; cursor: pointer; @@ -322,7 +323,7 @@ border-radius: 10px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.06); - transition: all 0.3s; + transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s; } .landing-btn-hero-secondary:hover { @@ -618,7 +619,7 @@ /* ---- SECTION LAYOUT ---- */ .landing-page section { - padding: 6rem 2rem; + padding: 4rem 2rem; } .landing-section-inner { @@ -642,6 +643,7 @@ font-weight: 700; letter-spacing: -0.03em; line-height: 1.15; + margin-top: 0; margin-bottom: 1rem; } @@ -674,7 +676,7 @@ border-radius: 16px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.06); - transition: all 0.3s; + transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s; } .landing-problem-card:hover { @@ -716,7 +718,7 @@ .landing-problem-card h3 { font-family: 'Bricolage Grotesque', sans-serif; - font-size: 1.15rem; + font-size: 1.25rem; font-weight: 700; margin-bottom: 0.6rem; letter-spacing: -0.01em; @@ -731,7 +733,7 @@ /* ---- EQUATION ---- */ .landing-equation-section { text-align: center; - padding: 5rem 2rem; + padding: 3.5rem 2rem; position: relative; } @@ -804,7 +806,7 @@ border: 1px solid rgba(255, 255, 255, 0.06); position: relative; counter-increment: landing-step; - transition: all 0.3s; + transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s; } .landing-step-card:hover { @@ -967,7 +969,7 @@ border-radius: 16px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.06); - transition: all 0.3s ease; + transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease, opacity 0.3s ease; } .landing-feature-card:hover { @@ -990,7 +992,7 @@ .landing-feature-card h3 { font-family: 'Bricolage Grotesque', sans-serif; - font-size: 1.05rem; + font-size: 1.25rem; font-weight: 700; margin-bottom: 0.6rem; letter-spacing: -0.01em; @@ -1022,7 +1024,7 @@ border-radius: 16px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.06); - transition: all 0.3s; + transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s; } .landing-pricing-card:hover { @@ -1133,7 +1135,7 @@ font-size: 0.9rem; font-weight: 600; text-decoration: none; - transition: all 0.3s; + transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s; width: 100%; } @@ -1181,7 +1183,7 @@ text-align: center; max-width: 700px; margin: 0 auto; - padding: 5rem 2rem; + padding: 3.5rem 2rem; } .landing-testimonial-quote { @@ -1234,7 +1236,7 @@ /* ---- CTA ---- */ .landing-cta-section { text-align: center; - padding: 6rem 2rem; + padding: 4rem 2rem; position: relative; } @@ -1396,6 +1398,87 @@ transform: translateY(0); } +/* ---- Hamburger Button ---- */ +.landing-hamburger { + display: none; + flex-direction: column; + justify-content: center; + gap: 5px; + width: 44px; + height: 44px; + padding: 10px; + background: none; + border: none; + cursor: pointer; + z-index: 110; +} + +.landing-hamburger span { + display: block; + width: 22px; + height: 2px; + background: #a0a0b0; + border-radius: 2px; + transition: transform 0.3s ease, opacity 0.3s ease; +} + +.landing-hamburger:hover span { + background: #f0f0f5; +} + +.landing-hamburger.open span:nth-child(1) { + transform: translateY(7px) rotate(45deg); +} + +.landing-hamburger.open span:nth-child(2) { + opacity: 0; +} + +.landing-hamburger.open span:nth-child(3) { + transform: translateY(-7px) rotate(-45deg); +} + +/* ---- Mobile Menu ---- */ +.landing-mobile-menu { + display: none; + flex-direction: column; + gap: 0; + padding: 0.5rem 1.5rem 1.5rem; + background: rgba(14, 15, 24, 0.95); + backdrop-filter: blur(20px) saturate(1.2); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.landing-mobile-menu a { + display: block; + padding: 0.875rem 0; + font-size: 1rem; + font-weight: 500; + color: #a0a0b0; + text-decoration: none; + transition: color 0.2s; + border-bottom: 1px solid rgba(255, 255, 255, 0.04); +} + +.landing-mobile-menu a:hover { + color: #f0f0f5; +} + +.landing-mobile-menu a:last-child { + border-bottom: none; +} + +.landing-mobile-menu-divider { + height: 1px; + background: rgba(255, 255, 255, 0.08); + margin: 0.5rem 0; +} + +.landing-mobile-menu .landing-btn-primary { + margin-top: 0.5rem; + display: block; +} + /* ---- RESPONSIVE ---- */ @media (max-width: 900px) { @@ -1414,6 +1497,18 @@ display: none; } + .landing-nav-cta { + display: none; + } + + .landing-hamburger { + display: flex; + } + + .landing-mobile-menu { + display: flex; + } + .landing-preview-sidebar { display: none; }