fix(landing): design audit fixes — hamburger menu, dead links, branding, spacing (#117)

* fix(landing): design audit fixes — hamburger menu, dead links, branding, spacing

- Add mobile hamburger menu with animated open/close and click-outside dismiss
- Create Privacy and Terms pages (footer links were dead # anchors)
- Change "Decision Tree Platform" to "AI-Powered Troubleshooting for MSPs" on login, register, and HTML title
- Fix register page icon color (was red/coral via CSS invert, now uses BrandLogo directly)
- Replace "0 Ticket Notes Written by Hand" stat with "100% Auto-Generated Documentation"
- Increase nav link touch targets to 44px minimum
- Fix heading hierarchy: section titles are now <h2>, standardize H3 to 1.25rem
- Tighten section spacing (6rem → 4rem padding, remove extra 5rem spacer)
- Add color-scheme: dark to HTML element
- Replace all transition: all with specific properties (10 occurrences)
- Fix loading ellipsis (... → …)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mobile): responsive modals + landing preview overflow

- PrepareSessionModal: bottom-sheet on mobile, centered on desktop
- IntakeFormModal: bottom-sheet on mobile, responsive padding
- ShareTreeModal: bottom-sheet on mobile, full-width on small screens
- Landing preview: hide URL bar and window controls on mobile (<900px)
  to prevent 189px horizontal overflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mobile): collapse search bar to icon on mobile

On mobile (<640px), the full search bar with placeholder text and ⌘K
badge was taking too much space in the topbar. Now shows just a
magnifying glass icon that opens the same command palette on tap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: chihlasm <michael@resolutionflow.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #117.
This commit is contained in:
chihlasm
2026-03-21 09:55:37 -04:00
committed by GitHub
parent 9298f5a272
commit 0c51198556
13 changed files with 299 additions and 45 deletions

View File

@@ -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<HTMLDivElement>(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() {
<div className="landing-page-content">
{/* Navigation */}
<nav className={`landing-nav ${navScrolled ? 'scrolled' : ''}`}>
<nav className={`landing-nav ${navScrolled ? 'scrolled' : ''}`} ref={mobileMenuRef}>
<div className="landing-nav-inner">
<a href="#" className="landing-nav-logo">
<div className="landing-nav-logo-icon">
@@ -88,7 +106,27 @@ export default function LandingPage() {
<Link to="/login" className="landing-btn-ghost">Sign In</Link>
<Link to="/register" className="landing-btn-primary">Get Started Free</Link>
</div>
<button
className={`landing-hamburger ${mobileMenuOpen ? 'open' : ''}`}
onClick={() => setMobileMenuOpen(v => !v)}
aria-label="Toggle menu"
aria-expanded={mobileMenuOpen}
>
<span />
<span />
<span />
</button>
</div>
{mobileMenuOpen && (
<div className="landing-mobile-menu">
<a href="#features" onClick={handleMobileNavClick}>Features</a>
<a href="#how-it-works" onClick={handleMobileNavClick}>How It Works</a>
<a href="#pricing" onClick={handleMobileNavClick}>Pricing</a>
<div className="landing-mobile-menu-divider" />
<Link to="/login" onClick={handleMobileNavClick}>Sign In</Link>
<Link to="/register" className="landing-btn-primary" onClick={handleMobileNavClick} style={{ textAlign: 'center' }}>Get Started Free</Link>
</div>
)}
</nav>
{/* Hero */}
@@ -120,8 +158,8 @@ export default function LandingPage() {
<div className="label">Less Time on Documentation</div>
</div>
<div className="landing-proof-stat">
<div className="number">0</div>
<div className="label">Ticket Notes Written by Hand</div>
<div className="number">100%</div>
<div className="label">Auto-Generated Documentation</div>
</div>
</div>
</div>
@@ -198,14 +236,13 @@ export default function LandingPage() {
</div>
</div>
<div style={{ height: '5rem' }} />
<div className="landing-section-divider" />
{/* Problem Section */}
<section id="problem" className="landing-reveal">
<div className="landing-section-inner">
<div className="landing-section-label">The Problem</div>
<div className="landing-section-title">Documentation is broken.<br />Everyone knows it.</div>
<h2 className="landing-section-title">Documentation is broken.<br />Everyone knows it.</h2>
<div className="landing-section-desc">
Engineers don&apos;t want to write it. Managers hate chasing it. Clients never see it. The same issues get solved from scratch every time.
</div>
@@ -243,7 +280,7 @@ export default function LandingPage() {
<section id="how-it-works" className="landing-reveal">
<div className="landing-section-inner">
<div className="landing-section-label">How It Works</div>
<div className="landing-section-title">Three steps. Zero note-writing.</div>
<h2 className="landing-section-title">Three steps. Zero note-writing.</h2>
<div className="landing-section-desc">
Build once, run forever. Every session generates documentation automatically.
</div>
@@ -312,7 +349,7 @@ export default function LandingPage() {
<section id="features" className="landing-reveal">
<div className="landing-section-inner">
<div className="landing-section-label">Features</div>
<div className="landing-section-title">Everything your team needs to<br />resolve faster and document better.</div>
<h2 className="landing-section-title">Everything your team needs to<br />resolve faster and document better.</h2>
<div className="landing-features-grid">
<FeatureCard
highlight
@@ -355,7 +392,7 @@ export default function LandingPage() {
<section id="pricing" className="landing-reveal">
<div className="landing-section-inner">
<div className="landing-section-label">Pricing</div>
<div className="landing-section-title">Simple pricing. No surprises.</div>
<h2 className="landing-section-title">Simple pricing. No surprises.</h2>
<div className="landing-section-desc">Start free. Upgrade when your team is ready.</div>
<div className="landing-pricing-grid">
<PricingCard
@@ -453,8 +490,8 @@ export default function LandingPage() {
<span className="landing-footer-copy">&copy; 2026 ResolutionFlow. All rights reserved.</span>
</div>
<ul className="landing-footer-links">
<li><a href="#">Privacy</a></li>
<li><a href="#">Terms</a></li>
<li><Link to="/privacy">Privacy</Link></li>
<li><Link to="/terms">Terms</Link></li>
<li><a href="mailto:hello@resolutionflow.com">Contact</a></li>
</ul>
</div>