Import source: cshare-app/src/pages/LandingPage

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:14:57 +03:30
parent 09fe6187b4
commit 7320cd43c6
+366
View File
@@ -0,0 +1,366 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { ShieldIcon, ZapIcon, GlobeIcon, ArrowDownIcon, EyeIcon, LinkIcon, ClockIcon, ChartIcon, CheckIcon, CopyIcon } from '../components/Icons';
import Footer from '../components/Footer';
import { useScrollReveal } from '../hooks/useScrollReveal';
const DEMO_FILES = [
{ id: 'a', name: 'app-prototype-v2.fig', type: 'FILE', color: '#5E9EFF', size: '15.0 MB', initProgress: 100, initLink: 'cshare.app/preview/x7k2m' },
{ id: 'b', name: 'Q4-report.pdf', type: 'PDF', color: '#E84848', size: '2.4 MB', initProgress: 62 },
{ id: 'c', name: 'brand-assets.zip', type: 'ZIP', color: '#4A90FF', size: '52.0 MB', initProgress: 21 },
];
function HeroPreview() {
const [files, setFiles] = useState(() =>
DEMO_FILES.map((f) => ({ ...f, progress: f.initProgress, link: f.initLink || null }))
);
useEffect(() => {
const id = setInterval(() => {
setFiles((prev) =>
prev.map((f) => {
if (f.progress >= 100) return f;
const np = Math.min(100, f.progress + Math.random() * 8 + 3);
return {
...f,
progress: np,
link: np >= 100
? `cshare.app/preview/${Math.random().toString(36).slice(2, 7)}`
: f.link,
};
})
);
}, 380);
return () => clearInterval(id);
}, []);
const doneCount = files.filter((f) => f.progress >= 100).length;
return (
<div style={{
background: 'var(--bg-elevated)',
border: '1px solid var(--border)',
borderRadius: 'var(--radius-xl)',
padding: '20px',
boxShadow: '0 32px 80px rgba(0,0,0,0.55), 0 0 0 1px rgba(74,144,255,0.07)',
animation: 'fadeInUp 0.55s 0.35s ease both',
}}>
{/* Panel header */}
<div style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
marginBottom: '14px',
paddingBottom: '14px',
borderBottom: '1px solid var(--border-subtle)',
}}>
<div style={{
width: '8px',
height: '8px',
borderRadius: '50%',
background: doneCount < 3 ? 'var(--accent)' : 'var(--success)',
boxShadow: `0 0 10px ${doneCount < 3 ? 'rgba(74,144,255,0.7)' : 'rgba(56,200,122,0.7)'}`,
animation: 'pulseGlow 2s ease infinite',
flexShrink: 0,
}} />
<span style={{ fontFamily: 'var(--font-heading)', fontSize: '0.8125rem', fontWeight: 600, color: 'var(--text-secondary)' }}>
{doneCount < 3 ? `${doneCount} of 3 ready to share` : 'All files ready'}
</span>
<span style={{ marginLeft: 'auto', fontSize: '0.6875rem', color: 'var(--text-muted)', fontFamily: 'var(--font-heading)' }}>
just now
</span>
</div>
{/* File rows */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{files.map((file) => (
<div
key={file.id}
style={{
padding: '12px 14px',
background: 'var(--bg-surface)',
borderRadius: 'var(--radius-md)',
border: `1px solid ${file.link ? 'var(--accent-border)' : 'var(--border-subtle)'}`,
transition: 'border-color 0.4s ease',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<div style={{
width: '34px', height: '34px', borderRadius: 'var(--radius-sm)',
background: 'var(--bg-primary)', display: 'flex', alignItems: 'center', justifyContent: 'center',
fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '0.5rem',
color: file.color, letterSpacing: '0.05em', flexShrink: 0, border: '1px solid var(--border-subtle)',
}}>
{file.type}
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontFamily: 'var(--font-heading)', fontWeight: 600, fontSize: '0.8125rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{file.name}
</div>
<div style={{ fontSize: '0.6875rem', color: 'var(--text-muted)', marginTop: '2px' }}>
{file.size}
</div>
</div>
{file.progress >= 100 ? (
<div style={{ width: '24px', height: '24px', borderRadius: '50%', background: 'var(--success-muted)', display: 'flex', alignItems: 'center', justifyContent: 'center', animation: 'scaleIn 0.3s ease', flexShrink: 0 }}>
<CheckIcon size={11} style={{ color: 'var(--success)' }} />
</div>
) : (
<span style={{ fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '0.75rem', color: 'var(--accent)', minWidth: '32px', textAlign: 'right', flexShrink: 0 }}>
{Math.round(file.progress)}%
</span>
)}
</div>
{file.progress < 100 && (
<div style={{ marginTop: '10px', height: '3px', borderRadius: '2px', background: 'var(--bg-primary)', overflow: 'hidden' }}>
<div style={{
width: `${file.progress}%`, height: '100%', borderRadius: '2px',
background: 'linear-gradient(90deg, var(--accent), var(--accent-hover))',
transition: 'width 0.38s ease',
boxShadow: '0 0 8px rgba(74,144,255,0.45)',
}} />
</div>
)}
{file.link && (
<div style={{
marginTop: '10px', display: 'flex', alignItems: 'center', gap: '8px',
padding: '7px 10px', background: 'var(--bg-primary)', borderRadius: 'var(--radius-sm)',
border: '1px solid var(--accent-border)', animation: 'fadeIn 0.4s ease',
}}>
<span style={{ flex: 1, fontSize: '0.6875rem', color: 'var(--accent)', fontFamily: 'monospace', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{file.link}
</span>
<CopyIcon size={11} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />
</div>
)}
</div>
))}
</div>
</div>
);
}
export default function LandingPage() {
const navigate = useNavigate();
const sectionRef = useScrollReveal(0.1);
return (
<div className="page-enter" style={{ minHeight: '100vh' }}>
{/* ── Hero ──────────────────────────────────────────────── */}
<section style={{ position: 'relative', overflow: 'hidden', padding: 'clamp(72px, 10vh, 120px) 32px clamp(72px, 10vh, 120px)' }}>
{/* Dot-grid background */}
<div style={{
position: 'absolute', inset: 0,
backgroundImage: 'radial-gradient(circle, rgba(42,42,50,0.85) 1px, transparent 1px)',
backgroundSize: '32px 32px', pointerEvents: 'none',
maskImage: 'radial-gradient(ellipse 90% 90% at 50% 40%, black 30%, transparent 100%)',
WebkitMaskImage: 'radial-gradient(ellipse 90% 90% at 50% 40%, black 30%, transparent 100%)',
}} />
<div style={{ position: 'absolute', top: '-20%', right: '-8%', width: '650px', height: '650px', background: 'radial-gradient(circle, rgba(74,144,255,0.065) 0%, transparent 65%)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', bottom: '-15%', left: '-5%', width: '420px', height: '420px', background: 'radial-gradient(circle, rgba(74,144,255,0.03) 0%, transparent 65%)', pointerEvents: 'none' }} />
<div className="hero-split">
{/* Left — headline + CTA */}
<div style={{ position: 'relative', zIndex: 1 }}>
{/* Hero heading — Playfair Display italic */}
<h1 style={{
fontFamily: 'var(--font-display)',
fontStyle: 'italic',
fontWeight: 700,
fontSize: 'clamp(2.75rem, 4.5vw, 5rem)',
letterSpacing: '-0.02em',
lineHeight: 1.08,
marginBottom: '24px',
animation: 'fadeInUp 0.5s 0.08s ease both',
}}>
Share files
<br />
without the
<br />
<span style={{
background: 'linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 50%, var(--accent) 100%)',
backgroundSize: '200% 200%',
animation: 'gradientMove 4s ease infinite',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
}}>
friction.
</span>
</h1>
<p style={{
fontSize: '1.0625rem', color: 'var(--text-secondary)',
maxWidth: '460px', lineHeight: 1.75, marginBottom: '36px',
animation: 'fadeInUp 0.5s 0.16s ease both',
}}>
Upload, share, and track your files with links that expire.
No sign-up walls, no bloated UIs, no files living forever on someone else's server.
</p>
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap', marginBottom: '32px', animation: 'fadeInUp 0.5s 0.24s ease both' }}>
<button className="btn btn-primary btn-lg" onClick={() => navigate('/upload')}>
Start Sharing
<ArrowDownIcon size={16} style={{ transform: 'rotate(-90deg)' }} />
</button>
<button className="btn btn-secondary btn-lg" onClick={() => navigate('/dashboard')}>
View Dashboard
</button>
</div>
{/* Honest feature pills */}
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', animation: 'fadeInUp 0.5s 0.32s ease both' }}>
{['No sign-up required', 'Auto-expiring links', 'Up to 500 MB/file'].map((tag) => (
<span key={tag} style={{
display: 'inline-flex', alignItems: 'center', gap: '6px',
padding: '5px 12px', background: 'var(--bg-elevated)', border: '1px solid var(--border-subtle)',
borderRadius: '999px', fontSize: '0.8125rem', color: 'var(--text-secondary)',
fontFamily: 'var(--font-body)', fontWeight: 500,
}}>
<CheckIcon size={12} style={{ color: 'var(--success)' }} />
{tag}
</span>
))}
</div>
</div>
{/* Right — live upload preview */}
<div className="hero-preview-col" style={{ position: 'relative', zIndex: 1 }}>
<HeroPreview />
</div>
</div>
</section>
{/* ── Features ─────────────────────────────────────────────── */}
<section ref={sectionRef} id="features" style={{ padding: 'clamp(60px, 8vh, 100px) 24px', maxWidth: '1120px', margin: '0 auto' }}>
<div className="scroll-reveal" style={{ textAlign: 'center', marginBottom: '64px' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', marginBottom: '16px' }}>
<span style={{ color: 'var(--accent)', fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '0.875rem' }}>//</span>
<span style={{ color: 'var(--text-muted)', fontFamily: 'var(--font-heading)', fontWeight: 600, fontSize: '0.75rem', letterSpacing: '0.12em', textTransform: 'uppercase' }}>Features</span>
</div>
<h2 className="heading-md" style={{ marginBottom: '16px' }}>Built different, by design</h2>
<p style={{ color: 'var(--text-secondary)', maxWidth: '480px', margin: '0 auto' }}>
Every decision made to reduce friction and keep your files under your control.
</p>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '20px' }}>
{[
{ icon: <ZapIcon size={24} />, title: 'Instant uploads', desc: 'Drag, drop, share. Your link is ready before you finish reading this sentence.' },
{ icon: <ShieldIcon size={24} />, title: 'Auto-expiring links', desc: 'Every link has a shelf life. Set it and forget it — files disappear when you want them to.' },
{ icon: <GlobeIcon size={24} />, title: 'No account needed', desc: "Share files without creating an account. Recipients don't need one either." },
{ icon: <EyeIcon size={24} />, title: 'View tracking', desc: 'Know exactly who opened your link and when. Real-time analytics on every file you share.' },
{ icon: <LinkIcon size={24} />, title: 'One-click sharing', desc: 'Copy a share link the instant your file is ready. No extra steps, no friction.' },
{ icon: <ClockIcon size={24} />, title: 'Custom expiry', desc: 'Set links to expire in an hour, a day, a week — you decide when access ends.' },
{ icon: <ChartIcon size={24} />, title: 'Download analytics', desc: 'See how many times your file has been downloaded, right from your dashboard.' },
{ icon: <ShieldIcon size={24} />, title: 'End-to-end encryption', desc: 'Your files are encrypted in transit and at rest. Only people with the link can access them.' },
{ icon: <ZapIcon size={24} />, title: '500MB file limit', desc: 'Share presentations, design files, datasets — up to half a gigabyte per upload.' },
].map((feature, i) => (
<div key={i} className="card card-interactive scroll-reveal" style={{ padding: '28px', transitionDelay: `${i * 0.05}s`, position: 'relative', overflow: 'hidden' }}>
{/* Faint ordinal */}
<span style={{
position: 'absolute', top: '16px', right: '20px',
fontFamily: 'var(--font-heading)', fontWeight: 800, fontSize: '0.6875rem',
color: 'var(--border)', letterSpacing: '0.06em', userSelect: 'none',
}}>
{String(i + 1).padStart(2, '0')}
</span>
<div style={{
width: '44px', height: '44px', borderRadius: 'var(--radius-md)',
background: 'var(--accent-muted)', border: '1px solid var(--accent-border)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: 'var(--accent)', marginBottom: '16px', transition: 'all var(--transition-base)',
}}>
{feature.icon}
</div>
<h3 className="heading-sm" style={{ marginBottom: '6px' }}>{feature.title}</h3>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.875rem', lineHeight: 1.65 }}>{feature.desc}</p>
</div>
))}
</div>
</section>
{/* ── How it Works ─────────────────────────────────────────── */}
<section id="how-it-works" style={{
padding: 'clamp(60px, 8vh, 100px) 24px',
background: 'var(--bg-elevated)',
borderTop: '1px solid var(--border-subtle)',
borderBottom: '1px solid var(--border-subtle)',
position: 'relative',
}}>
<div style={{ maxWidth: '1120px', margin: '0 auto', position: 'relative', zIndex: 1 }}>
<div style={{ textAlign: 'center', marginBottom: '64px' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', marginBottom: '16px' }}>
<span style={{ color: 'var(--accent)', fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '0.875rem' }}>//</span>
<span style={{ color: 'var(--text-muted)', fontFamily: 'var(--font-heading)', fontWeight: 600, fontSize: '0.75rem', letterSpacing: '0.12em', textTransform: 'uppercase' }}>How it Works</span>
</div>
<h2 className="heading-md" style={{ marginBottom: '16px' }}>Three steps. Zero friction.</h2>
<p style={{ color: 'var(--text-secondary)', maxWidth: '480px', margin: '0 auto' }}>
From file to shared link in under five seconds.
</p>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '48px', position: 'relative' }}>
<div className="steps-connector" style={{ position: 'absolute', top: '28px', left: 'calc(33.33% / 2)', right: 'calc(33.33% / 2)', height: '2px', background: 'linear-gradient(90deg, var(--accent-border), var(--accent), var(--accent-border))', opacity: 0.4, zIndex: 0 }} />
{[
{ title: 'Drop your file', desc: 'Drag and drop any file up to 500MB into the upload zone.' },
{ title: 'Get a link', desc: 'A unique, time-limited share link is generated instantly.' },
{ title: 'Share and track', desc: 'Send the link. See who viewed and downloaded it in your dashboard.' },
].map((step, i) => (
<div key={i} style={{ textAlign: 'center', position: 'relative', zIndex: 1 }}>
{/* Giant decorative background number */}
<div style={{
position: 'absolute', top: '-24px', left: '50%', transform: 'translateX(-50%)',
fontFamily: 'var(--font-heading)', fontWeight: 800, fontSize: '8rem',
lineHeight: 1, color: 'var(--bg-surface)',
userSelect: 'none', pointerEvents: 'none', zIndex: 0,
letterSpacing: '-0.06em',
}}>
{i + 1}
</div>
<div style={{ position: 'relative', zIndex: 1 }}>
<div style={{
width: '56px', height: '56px', margin: '0 auto 20px', borderRadius: '50%',
border: '2px solid var(--accent-border)', display: 'flex', alignItems: 'center', justifyContent: 'center',
fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '1.25rem',
color: 'var(--accent)', background: 'var(--bg-elevated)',
animation: `pulseGlow 3s ease-in-out infinite ${i * 0.5}s`,
}}>
{i + 1}
</div>
<h3 className="heading-sm" style={{ marginBottom: '8px' }}>{step.title}</h3>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.9375rem', lineHeight: 1.65 }}>{step.desc}</p>
</div>
</div>
))}
</div>
</div>
</section>
{/* ── CTA ──────────────────────────────────────────────────── */}
<section style={{ padding: 'clamp(80px, 12vh, 140px) 24px', textAlign: 'center', position: 'relative', overflow: 'hidden' }}>
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse 60% 50% at 50% 100%, rgba(74,144,255,0.06) 0%, transparent 70%)', pointerEvents: 'none' }} />
<div style={{ position: 'relative', zIndex: 1, maxWidth: '560px', margin: '0 auto' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', marginBottom: '16px' }}>
<span style={{ color: 'var(--accent)', fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '0.875rem' }}>//</span>
<span style={{ color: 'var(--text-muted)', fontFamily: 'var(--font-heading)', fontWeight: 600, fontSize: '0.75rem', letterSpacing: '0.12em', textTransform: 'uppercase' }}>Get Started</span>
</div>
<h2 className="heading-md" style={{ marginBottom: '16px' }}>Ready to share smarter?</h2>
<p style={{ color: 'var(--text-secondary)', marginBottom: '32px', lineHeight: 1.7 }}>
No sign-up required. Upload your first file and see how simple file sharing should be.
</p>
<button className="btn btn-primary btn-lg" onClick={() => navigate('/upload')}>
Start Sharing Now
<ArrowDownIcon size={16} style={{ transform: 'rotate(-90deg)' }} />
</button>
</div>
</section>
<Footer />
</div>
);
}