mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-12 01:46:08 +00:00
refactor: eslint and prettier conflict will be resolved and formatted
This commit is contained in:
+314
-87
@@ -26,10 +26,9 @@ import {
|
||||
CheckCircle,
|
||||
Upload,
|
||||
Share2,
|
||||
Check
|
||||
Check,
|
||||
} from 'lucide-react';
|
||||
|
||||
|
||||
interface LandingPageProps {
|
||||
isLoggedIn: boolean;
|
||||
}
|
||||
@@ -55,7 +54,8 @@ const coreWorkflowFeatures = [
|
||||
},
|
||||
{
|
||||
title: 'Approval Workflow',
|
||||
description: 'Assign specific team members or clients to review and sign off on a cut. Get an exact \"Approved\" status.',
|
||||
description:
|
||||
'Assign specific team members or clients to review and sign off on a cut. Get an exact \"Approved\" status.',
|
||||
icon: CheckCircle,
|
||||
},
|
||||
];
|
||||
@@ -73,7 +73,8 @@ const workflowAcceleratorFeatures = [
|
||||
},
|
||||
{
|
||||
title: 'Real-time Webhooks',
|
||||
description: 'Get instant Telegram alerts the second a comment is dropped. More integrations coming soon.',
|
||||
description:
|
||||
'Get instant Telegram alerts the second a comment is dropped. More integrations coming soon.',
|
||||
icon: BellRing,
|
||||
},
|
||||
{
|
||||
@@ -121,7 +122,9 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
const updateNavbar = () => {
|
||||
const hasScrolled = window.scrollY > 20;
|
||||
gsap.to(nav, {
|
||||
backgroundColor: hasScrolled ? 'color-mix(in oklab, var(--background) 85%, transparent)' : 'transparent',
|
||||
backgroundColor: hasScrolled
|
||||
? 'color-mix(in oklab, var(--background) 85%, transparent)'
|
||||
: 'transparent',
|
||||
backdropFilter: hasScrolled ? 'blur(16px)' : 'blur(0px)',
|
||||
borderBottomColor: hasScrolled ? 'var(--border)' : 'transparent',
|
||||
duration: 0.3,
|
||||
@@ -141,27 +144,50 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={rootRef} className="min-h-screen overflow-x-hidden bg-background text-foreground font-sans selection:bg-primary/20">
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="min-h-screen overflow-x-hidden bg-background text-foreground font-sans selection:bg-primary/20"
|
||||
>
|
||||
{/* Header */}
|
||||
<header ref={navbarRef} className="fixed inset-x-0 top-0 z-50 border-b border-transparent bg-transparent transition-colors duration-300">
|
||||
<header
|
||||
ref={navbarRef}
|
||||
className="fixed inset-x-0 top-0 z-50 border-b border-transparent bg-transparent transition-colors duration-300"
|
||||
>
|
||||
<div className="mx-auto flex h-14 w-full max-w-[1200px] items-center justify-between px-4 sm:h-16 sm:px-6 lg:px-10">
|
||||
<Link href="/" className="group relative isolate inline-flex items-center gap-2 overflow-hidden border border-border bg-background px-3 py-2">
|
||||
<Link
|
||||
href="/"
|
||||
className="group relative isolate inline-flex items-center gap-2 overflow-hidden border border-border bg-background px-3 py-2"
|
||||
>
|
||||
<span className="pointer-events-none absolute inset-0 -translate-x-[101%] bg-primary/10 transition-transform duration-300 group-hover:translate-x-0" />
|
||||
<Video className="relative z-10 h-4 w-4 text-primary" />
|
||||
<span className="relative z-10 text-xs font-semibold tracking-[0.12em]">OPENFRAME</span>
|
||||
</Link>
|
||||
|
||||
<nav className="hidden items-center gap-6 text-[11px] font-medium uppercase tracking-[0.14em] md:flex">
|
||||
<Link className="text-muted-foreground transition-colors hover:text-foreground" href="#features">
|
||||
<Link
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
href="#features"
|
||||
>
|
||||
Features
|
||||
</Link>
|
||||
<Link className="text-muted-foreground transition-colors hover:text-foreground" href="#pricing">
|
||||
<Link
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
href="#pricing"
|
||||
>
|
||||
Pricing
|
||||
</Link>
|
||||
<a className="text-muted-foreground transition-colors hover:text-foreground" href="https://github.com/yusufipk/OpenFrame" target="_blank" rel="noreferrer">
|
||||
<a
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
href="https://github.com/yusufipk/OpenFrame"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
<a className="text-muted-foreground transition-colors hover:text-foreground" href="mailto:[email protected]">
|
||||
<a
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
href="mailto:[email protected]"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
</nav>
|
||||
@@ -177,7 +203,10 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Link href="/login" className="text-xs font-medium text-muted-foreground hover:text-foreground hidden sm:block mr-4">
|
||||
<Link
|
||||
href="/login"
|
||||
className="text-xs font-medium text-muted-foreground hover:text-foreground hidden sm:block mr-4"
|
||||
>
|
||||
Log in
|
||||
</Link>
|
||||
<Link href="/register" className={controlButtonClass}>
|
||||
@@ -196,7 +225,10 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,_var(--tw-gradient-stops))] from-primary/5 via-background to-background" />
|
||||
|
||||
<div className="relative z-10 mx-auto max-w-[1000px] space-y-8">
|
||||
<div data-hero-copy className="inline-flex items-center gap-2 border border-border/50 bg-secondary/30 px-3 py-1.5 text-xs text-muted-foreground backdrop-blur-md">
|
||||
<div
|
||||
data-hero-copy
|
||||
className="inline-flex items-center gap-2 border border-border/50 bg-secondary/30 px-3 py-1.5 text-xs text-muted-foreground backdrop-blur-md"
|
||||
>
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75"></span>
|
||||
<span className="relative inline-flex h-2 w-2 rounded-full bg-primary"></span>
|
||||
@@ -204,16 +236,26 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<span className="font-mono tracking-wide uppercase">Open Source Video Review</span>
|
||||
</div>
|
||||
|
||||
<h1 data-hero-copy className="text-4xl font-semibold leading-[0.95] tracking-[-0.03em] sm:text-5xl md:text-6xl lg:text-7xl">
|
||||
<h1
|
||||
data-hero-copy
|
||||
className="text-4xl font-semibold leading-[0.95] tracking-[-0.03em] sm:text-5xl md:text-6xl lg:text-7xl"
|
||||
>
|
||||
Cut Video Approval Time in Half. <br className="hidden md:block" />
|
||||
<span className="text-muted-foreground">Stop Chasing Timecodes.</span>
|
||||
</h1>
|
||||
|
||||
<p data-hero-copy className="mx-auto max-w-2xl text-base text-muted-foreground md:text-xl">
|
||||
OpenFrame puts comments, voice notes, and annotations on a single timeline so clients say "yes" faster and your team stops guessing.
|
||||
<p
|
||||
data-hero-copy
|
||||
className="mx-auto max-w-2xl text-base text-muted-foreground md:text-xl"
|
||||
>
|
||||
OpenFrame puts comments, voice notes, and annotations on a single timeline so clients
|
||||
say "yes" faster and your team stops guessing.
|
||||
</p>
|
||||
|
||||
<div data-hero-copy className="mx-auto flex max-w-md flex-col items-center justify-center gap-2">
|
||||
<div
|
||||
data-hero-copy
|
||||
className="mx-auto flex max-w-md flex-col items-center justify-center gap-2"
|
||||
>
|
||||
<Link
|
||||
href={hostedCtaHref}
|
||||
className="group relative isolate inline-flex h-12 min-w-max items-center justify-center overflow-hidden border border-primary bg-primary px-10 text-sm font-medium whitespace-nowrap text-primary-foreground transition-transform duration-300 hover:scale-[1.02]"
|
||||
@@ -222,7 +264,10 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<MoveRight className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1" />
|
||||
</Link>
|
||||
|
||||
<a href="#pricing" className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground">
|
||||
<a
|
||||
href="#pricing"
|
||||
className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
Prefer self-hosting? <ArrowRight className="ml-1 inline h-3.5 w-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
@@ -252,7 +297,6 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<Mic className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -261,7 +305,9 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<section className="border-y border-border bg-card/10 px-4 py-16 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto w-full max-w-[1200px]">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">Feedback chaos looks like this:</h2>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Feedback chaos looks like this:
|
||||
</h2>
|
||||
<ul className="mt-8 space-y-4 text-base text-muted-foreground md:text-lg">
|
||||
<li className="flex items-start gap-3">
|
||||
<XCircle className="mt-1 h-5 w-5 shrink-0 text-red-500/80" />
|
||||
@@ -293,7 +339,9 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<section className="border-b border-border bg-background px-4 py-20 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto w-full max-w-[1200px]">
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">From upload to approval — in one flow.</h2>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
From upload to approval — in one flow.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-4 relative">
|
||||
@@ -301,7 +349,7 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
{ label: 'Upload a cut', icon: Upload },
|
||||
{ label: 'Share a review link', icon: Share2 },
|
||||
{ label: 'Get timestamped feedback', icon: MessageSquare },
|
||||
{ label: 'Mark approved and move on', icon: Check }
|
||||
{ label: 'Mark approved and move on', icon: Check },
|
||||
].map((step, idx, arr) => (
|
||||
<div key={step.label} className="group relative flex flex-col items-center">
|
||||
<div className="flex flex-col items-center justify-center w-full min-h-[100px] md:aspect-[4/3] border border-border bg-card/20 px-4 py-6 md:p-6 text-center relative z-10 transition-all duration-300 group-hover:border-primary/40 group-hover:bg-card/40">
|
||||
@@ -334,22 +382,31 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
|
||||
{/* 4) FEATURES */}
|
||||
<section id="features" className="scroll-mt-20 border-t border-border bg-card/10">
|
||||
|
||||
{/* Feature 1 */}
|
||||
<div className="border-b border-border">
|
||||
<div className="mx-auto flex w-full max-w-[1200px] flex-col-reverse items-center justify-between gap-12 px-4 py-20 sm:px-6 lg:flex-row lg:px-8 lg:py-32">
|
||||
<div data-reveal className="w-full lg:w-1/2 relative">
|
||||
<div className="relative aspect-[16/10] w-full border border-border/50 bg-background overflow-hidden">
|
||||
<Image src="/landing/compare-v2.webp" alt="Comparison Mode" fill className="object-cover object-left-top" sizes="(min-width: 1024px) 50vw, 100vw" />
|
||||
<Image
|
||||
src="/landing/compare-v2.webp"
|
||||
alt="Comparison Mode"
|
||||
fill
|
||||
className="object-cover object-left-top"
|
||||
sizes="(min-width: 1024px) 50vw, 100vw"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-background/5" />
|
||||
</div>
|
||||
</div>
|
||||
<div data-reveal className="w-full lg:w-1/2 space-y-6">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">Compare versions side-by-side. End "which cut is this?"</h2>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Compare versions side-by-side. End "which cut is this?"
|
||||
</h2>
|
||||
<p className="text-base text-muted-foreground md:text-lg">
|
||||
See what actually changed between versions then approve with confidence.
|
||||
</p>
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-primary">Cuts revision cycles.</p>
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-primary">
|
||||
Cuts revision cycles.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -358,20 +415,29 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<div className="border-b border-border bg-background">
|
||||
<div className="mx-auto flex w-full max-w-[1200px] flex-col items-center justify-between gap-12 px-4 py-20 sm:px-6 lg:flex-row lg:px-8 lg:py-32">
|
||||
<div data-reveal className="w-full lg:w-1/2 space-y-6">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">Explain it better with Voice.</h2>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Explain it better with Voice.
|
||||
</h2>
|
||||
<p className="text-base text-muted-foreground md:text-lg">
|
||||
No more "What did you mean by this?" emails. Every note lands at the exact moment in the video.
|
||||
No more "What did you mean by this?" emails. Every note lands at the
|
||||
exact moment in the video.
|
||||
</p>
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-primary">
|
||||
Faster feedback. Fewer misunderstandings.
|
||||
</p>
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-primary">Faster feedback. Fewer misunderstandings.</p>
|
||||
</div>
|
||||
<div data-reveal className="w-full lg:w-1/2">
|
||||
<div className="border border-border bg-card p-6">
|
||||
<div className="border border-border/50 bg-background p-6">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-8 w-8 items-center justify-center bg-secondary font-mono text-xs">Y</div>
|
||||
<div className="flex h-8 w-8 items-center justify-center bg-secondary font-mono text-xs">
|
||||
Y
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p className="font-mono text-[11px] font-medium leading-none">Yusuf İpek</p>
|
||||
<p className="font-mono text-[11px] font-medium leading-none">
|
||||
Yusuf İpek
|
||||
</p>
|
||||
<p className="font-mono text-[10px] text-muted-foreground">00:03:45</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -382,7 +448,13 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<Play className="h-3 w-3" />
|
||||
</button>
|
||||
{Array.from({ length: 40 }).map((_, i) => (
|
||||
<span key={i} className="voice-bar w-full flex-1 bg-primary/60" style={{ height: `${[30, 80, 50, 90, 40, 70, 60, 45, 85, 55, 65, 35, 95, 75, 25, 40, 80, 50, 90, 30][i % 20]}%` }} />
|
||||
<span
|
||||
key={i}
|
||||
className="voice-bar w-full flex-1 bg-primary/60"
|
||||
style={{
|
||||
height: `${[30, 80, 50, 90, 40, 70, 60, 45, 85, 55, 65, 35, 95, 75, 25, 40, 80, 50, 90, 30][i % 20]}%`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -394,12 +466,37 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
{/* Feature 3 */}
|
||||
<div className="border-b border-border">
|
||||
<div className="mx-auto flex w-full max-w-[1200px] flex-col-reverse items-center justify-between gap-12 px-4 py-20 sm:px-6 lg:flex-row lg:px-8 lg:py-32">
|
||||
<div data-reveal className="w-full lg:w-1/2 relative aspect-video bg-card border border-border p-4">
|
||||
<div
|
||||
data-reveal
|
||||
className="w-full lg:w-1/2 relative aspect-video bg-card border border-border p-4"
|
||||
>
|
||||
<div className="relative h-full w-full border border-border/50 overflow-hidden bg-background">
|
||||
<Image src="https://images.unsplash.com/photo-1542204165-65bf26472b9b?auto=format&fit=crop&w=800&q=80" alt="Annotate" fill className="object-cover opacity-70" />
|
||||
<svg className="absolute inset-0 h-full w-full pointer-events-none" viewBox="0 0 800 450" fill="none">
|
||||
<circle cx="500" cy="225" r="80" stroke="#06b6d4" strokeWidth="4" className="opacity-90 drop-shadow-[0_0_8px_rgba(6,182,212,0.8)]" />
|
||||
<path d="M500 145 Q550 80 620 120" stroke="#06b6d4" strokeWidth="4" className="opacity-90" strokeLinecap="round" />
|
||||
<Image
|
||||
src="https://images.unsplash.com/photo-1542204165-65bf26472b9b?auto=format&fit=crop&w=800&q=80"
|
||||
alt="Annotate"
|
||||
fill
|
||||
className="object-cover opacity-70"
|
||||
/>
|
||||
<svg
|
||||
className="absolute inset-0 h-full w-full pointer-events-none"
|
||||
viewBox="0 0 800 450"
|
||||
fill="none"
|
||||
>
|
||||
<circle
|
||||
cx="500"
|
||||
cy="225"
|
||||
r="80"
|
||||
stroke="#06b6d4"
|
||||
strokeWidth="4"
|
||||
className="opacity-90 drop-shadow-[0_0_8px_rgba(6,182,212,0.8)]"
|
||||
/>
|
||||
<path
|
||||
d="M500 145 Q550 80 620 120"
|
||||
stroke="#06b6d4"
|
||||
strokeWidth="4"
|
||||
className="opacity-90"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Circle Editor UI mock */}
|
||||
@@ -423,11 +520,16 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
</div>
|
||||
</div>
|
||||
<div data-reveal className="w-full lg:w-1/2 space-y-6">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">Point. Draw. Done.</h2>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Point. Draw. Done.
|
||||
</h2>
|
||||
<p className="text-base text-muted-foreground md:text-lg">
|
||||
Precise feedback that leaves zero room for error. Circle, sketch, and point directly on the video frame.
|
||||
Precise feedback that leaves zero room for error. Circle, sketch, and point
|
||||
directly on the video frame.
|
||||
</p>
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-primary">
|
||||
Stops "I thought you meant..."
|
||||
</p>
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-primary">Stops "I thought you meant..."</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -437,16 +539,29 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<section className="border-b border-border px-4 py-20 sm:px-6 lg:px-8 lg:py-32 bg-background">
|
||||
<div className="mx-auto w-full max-w-[1200px]">
|
||||
<div data-reveal className="mb-12 flex flex-col items-center text-center">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">Everything a real production workflow needs.</h2>
|
||||
<p className="mt-4 max-w-2xl text-base text-muted-foreground">The core tools teams expect without the complexity that slows clients down.</p>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Everything a real production workflow needs.
|
||||
</h2>
|
||||
<p className="mt-4 max-w-2xl text-base text-muted-foreground">
|
||||
The core tools teams expect without the complexity that slows clients down.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-10">
|
||||
<div>
|
||||
<p data-reveal className="mb-4 text-xs uppercase tracking-[0.14em] text-muted-foreground">Core workflow</p>
|
||||
<p
|
||||
data-reveal
|
||||
className="mb-4 text-xs uppercase tracking-[0.14em] text-muted-foreground"
|
||||
>
|
||||
Core workflow
|
||||
</p>
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{coreWorkflowFeatures.map((feat) => (
|
||||
<div key={feat.title} data-reveal className="group border border-border bg-card p-6 transition-colors hover:border-primary/50 hover:bg-card/80">
|
||||
<div
|
||||
key={feat.title}
|
||||
data-reveal
|
||||
className="group border border-border bg-card p-6 transition-colors hover:border-primary/50 hover:bg-card/80"
|
||||
>
|
||||
<feat.icon className="mb-4 h-6 w-6 text-primary" />
|
||||
<h3 className="mb-2 text-lg font-medium">{feat.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{feat.description}</p>
|
||||
@@ -456,10 +571,19 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p data-reveal className="mb-4 text-xs uppercase tracking-[0.14em] text-muted-foreground">Workflow Accelerators</p>
|
||||
<p
|
||||
data-reveal
|
||||
className="mb-4 text-xs uppercase tracking-[0.14em] text-muted-foreground"
|
||||
>
|
||||
Workflow Accelerators
|
||||
</p>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{workflowAcceleratorFeatures.map((feat) => (
|
||||
<div key={feat.title} data-reveal className="group border border-border bg-card p-6 transition-colors hover:border-primary/50 hover:bg-card/80">
|
||||
<div
|
||||
key={feat.title}
|
||||
data-reveal
|
||||
className="group border border-border bg-card p-6 transition-colors hover:border-primary/50 hover:bg-card/80"
|
||||
>
|
||||
<feat.icon className="mb-4 h-6 w-6 text-primary" />
|
||||
<h3 className="mb-2 text-lg font-medium">{feat.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{feat.description}</p>
|
||||
@@ -475,8 +599,13 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<section className="border-b border-border bg-card/20 px-4 py-20 sm:px-6 lg:px-8 lg:py-32">
|
||||
<div className="mx-auto flex w-full max-w-[1200px] flex-col lg:flex-row gap-12 lg:items-center">
|
||||
<div data-reveal className="lg:w-1/2 space-y-6">
|
||||
<h2 className="text-4xl font-semibold tracking-[-0.02em] md:text-5xl">Clients don't need an account. They just review.</h2>
|
||||
<p className="text-lg text-muted-foreground">If clients can't adopt the tool, approvals don't happen. OpenFrame keeps it frictionless.</p>
|
||||
<h2 className="text-4xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Clients don't need an account. They just review.
|
||||
</h2>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
If clients can't adopt the tool, approvals don't happen. OpenFrame keeps
|
||||
it frictionless.
|
||||
</p>
|
||||
</div>
|
||||
<div data-reveal className="lg:w-1/2 space-y-4">
|
||||
<div className="flex items-start gap-4 border border-border bg-background p-6 transition-transform hover:-translate-y-1">
|
||||
@@ -494,7 +623,9 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<div className="flex items-start gap-4 border border-border bg-background p-6 transition-transform hover:-translate-y-1">
|
||||
<MessageSquare className="mt-1 h-6 w-6 shrink-0 text-primary" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">Timestamped notes that are impossible to miss.</h3>
|
||||
<h3 className="text-lg font-semibold">
|
||||
Timestamped notes that are impossible to miss.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -502,41 +633,73 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
</section>
|
||||
|
||||
{/* 7) PRICING */}
|
||||
<section id="pricing" className="border-b border-border bg-[#0a0a0a] px-4 py-20 sm:px-6 lg:px-8 lg:py-32">
|
||||
<section
|
||||
id="pricing"
|
||||
className="border-b border-border bg-[#0a0a0a] px-4 py-20 sm:px-6 lg:px-8 lg:py-32"
|
||||
>
|
||||
<div className="mx-auto w-full max-w-[1200px]">
|
||||
<div data-reveal className="mb-16">
|
||||
<h2 className="text-3xl font-semibold md:text-5xl text-foreground" style={{ fontFamily: 'monospace', letterSpacing: '-0.02em' }}>
|
||||
<h2
|
||||
className="text-3xl font-semibold md:text-5xl text-foreground"
|
||||
style={{ fontFamily: 'monospace', letterSpacing: '-0.02em' }}
|
||||
>
|
||||
Deploy it yourself - or let us run it for you.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{/* Card 1: Open Source */}
|
||||
<div data-reveal className="relative flex flex-col border border-border/40 bg-[#141414] p-8">
|
||||
<div
|
||||
data-reveal
|
||||
className="relative flex flex-col border border-border/40 bg-[#141414] p-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-xl font-semibold text-foreground">Open Source (Self-hosted)</h3>
|
||||
<h3 className="text-xl font-semibold text-foreground">
|
||||
Open Source (Self-hosted)
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="mb-8 flex items-baseline gap-2">
|
||||
<span className="text-3xl font-semibold text-[#06b6d4]">Free</span>
|
||||
</div>
|
||||
<p className="mb-8 text-sm text-muted-foreground">For teams who want full control and can run their own infrastructure.</p>
|
||||
<p className="mb-8 text-sm text-muted-foreground">
|
||||
For teams who want full control and can run their own infrastructure.
|
||||
</p>
|
||||
|
||||
<ul className="mb-8 flex-1 space-y-4 text-sm text-foreground/80">
|
||||
<li className="flex items-center gap-3"><CheckSquare className="h-4 w-4 text-[#06b6d4]" />Full codebase access</li>
|
||||
<li className="flex items-center gap-3"><CheckSquare className="h-4 w-4 text-[#06b6d4]" />Self-hosted infrastructure control</li>
|
||||
<li className="flex items-center gap-3"><CheckSquare className="h-4 w-4 text-[#06b6d4]" />Manual update cadence</li>
|
||||
<li className="flex items-center gap-3">
|
||||
<CheckSquare className="h-4 w-4 text-[#06b6d4]" />
|
||||
Full codebase access
|
||||
</li>
|
||||
<li className="flex items-center gap-3">
|
||||
<CheckSquare className="h-4 w-4 text-[#06b6d4]" />
|
||||
Self-hosted infrastructure control
|
||||
</li>
|
||||
<li className="flex items-center gap-3">
|
||||
<CheckSquare className="h-4 w-4 text-[#06b6d4]" />
|
||||
Manual update cadence
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href="https://github.com/yusufipk/OpenFrame" target="_blank" rel="noreferrer" className="mt-auto group relative isolate inline-flex h-12 w-full items-center justify-center overflow-hidden border border-border/50 bg-[#0a0a0a] font-medium text-foreground transition-colors hover:bg-white/5 text-sm">
|
||||
<a
|
||||
href="https://github.com/yusufipk/OpenFrame"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="mt-auto group relative isolate inline-flex h-12 w-full items-center justify-center overflow-hidden border border-border/50 bg-[#0a0a0a] font-medium text-foreground transition-colors hover:bg-white/5 text-sm"
|
||||
>
|
||||
<Github className="mr-2 h-4 w-4" /> View on GitHub
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Card 2: Hosted Cloud */}
|
||||
<div data-reveal className="relative flex flex-col border border-[#06b6d4]/40 bg-[#141414] p-8">
|
||||
<div
|
||||
data-reveal
|
||||
className="relative flex flex-col border border-[#06b6d4]/40 bg-[#141414] p-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<p className="font-mono text-[10px] uppercase font-semibold text-muted-foreground tracking-widest mb-4">Best for teams who want zero setup.</p>
|
||||
<p className="font-mono text-[10px] uppercase font-semibold text-muted-foreground tracking-widest mb-4">
|
||||
Best for teams who want zero setup.
|
||||
</p>
|
||||
<h3 className="text-xl font-semibold text-foreground">Hosted Cloud</h3>
|
||||
</div>
|
||||
|
||||
@@ -546,40 +709,77 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
</div>
|
||||
|
||||
<ul className="mb-8 flex-1 space-y-4 text-sm text-foreground/80">
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Unlimited collaborators</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Timestamped comments + voice notes</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Annotations + version compare</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Share links with permissions</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Exports (PDF/CSV)</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Unlimited YouTube Video Imports</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Download original uploaded video</span></li>
|
||||
<li className="flex items-start gap-3"><CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" /><span>Includes: 200 GB Storage</span></li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Unlimited collaborators</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Timestamped comments + voice notes</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Annotations + version compare</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Share links with permissions</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Exports (PDF/CSV)</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Unlimited YouTube Video Imports</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Download original uploaded video</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<CheckSquare className="mt-0.5 shrink-0 h-4 w-4 text-[#06b6d4]" />
|
||||
<span>Includes: 200 GB Storage</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="mb-8 text-sm text-muted-foreground">Need more storage? Add 100 GB for $5/mo.</p>
|
||||
<p className="mb-8 text-sm text-muted-foreground">
|
||||
Need more storage? Add 100 GB for $5/mo.
|
||||
</p>
|
||||
|
||||
<Link href={hostedCtaHref} className="mt-auto group relative isolate inline-flex h-12 w-full items-center justify-center overflow-hidden bg-[#06b6d4] font-medium text-black transition-colors hover:bg-[#06b6d4]/90 text-sm">
|
||||
<Link
|
||||
href={hostedCtaHref}
|
||||
className="mt-auto group relative isolate inline-flex h-12 w-full items-center justify-center overflow-hidden bg-[#06b6d4] font-medium text-black transition-colors hover:bg-[#06b6d4]/90 text-sm"
|
||||
>
|
||||
Start free
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Card 3: Studio & Agency */}
|
||||
<div data-reveal className="relative flex flex-col border border-border/40 bg-[#141414] p-8">
|
||||
<div
|
||||
data-reveal
|
||||
className="relative flex flex-col border border-border/40 bg-[#141414] p-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-xl font-semibold text-foreground">Need more than the standard limits?</h3>
|
||||
<h3 className="text-xl font-semibold text-foreground">
|
||||
Need more than the standard limits?
|
||||
</h3>
|
||||
</div>
|
||||
<div className="mb-6 flex items-baseline gap-2">
|
||||
<span className="text-3xl font-semibold text-[#06b6d4]">Let's talk</span>
|
||||
</div>
|
||||
|
||||
<p className="mb-8 text-sm text-foreground/80 leading-relaxed">
|
||||
Custom capacity and setup help for high-volume production teams. Tell us your storage, usage, and workflow - we'll recommend the right approach.
|
||||
Custom capacity and setup help for high-volume production teams. Tell us your
|
||||
storage, usage, and workflow - we'll recommend the right approach.
|
||||
</p>
|
||||
|
||||
<a href="mailto:[email protected]" className="group relative isolate inline-flex h-12 w-full items-center justify-center overflow-hidden bg-[#0a0a0a] font-medium text-foreground transition-colors hover:bg-white/5 border border-border/50 text-sm">
|
||||
<a
|
||||
href="mailto:[email protected]"
|
||||
className="group relative isolate inline-flex h-12 w-full items-center justify-center overflow-hidden bg-[#0a0a0a] font-medium text-foreground transition-colors hover:bg-white/5 border border-border/50 text-sm"
|
||||
>
|
||||
Contact us
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -588,7 +788,9 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<section className="border-b border-border bg-background px-4 py-20 sm:px-6 lg:px-8 lg:py-24">
|
||||
<div className="mx-auto w-full max-w-[1200px]">
|
||||
<div data-reveal className="mx-auto max-w-4xl">
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">Security & privacy, by design.</h2>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-5xl">
|
||||
Security & privacy, by design.
|
||||
</h2>
|
||||
<ul className="mt-8 space-y-4 text-base text-muted-foreground md:text-lg">
|
||||
<li>- Permissioned share links (control who can view/comment)</li>
|
||||
<li>- Private-by-default projects</li>
|
||||
@@ -612,7 +814,7 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
},
|
||||
{
|
||||
q: 'What happens if I exceed my storage?',
|
||||
a: 'You can add 100 GB for $5/mo. If you need much more, contact us at [email protected] and we\'ll help you choose the best setup.',
|
||||
a: "You can add 100 GB for $5/mo. If you need much more, contact us at [email protected] and we'll help you choose the best setup.",
|
||||
},
|
||||
{
|
||||
q: 'How is YouTube unlimited?',
|
||||
@@ -655,10 +857,17 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
|
||||
{/* 10) FINAL CTA STRIP */}
|
||||
<section className="border-b border-border bg-background px-4 py-16 sm:px-6 lg:px-8">
|
||||
<div data-reveal className="mx-auto flex w-full max-w-[1200px] flex-col items-center justify-between gap-4 text-center md:flex-row md:text-left">
|
||||
<div
|
||||
data-reveal
|
||||
className="mx-auto flex w-full max-w-[1200px] flex-col items-center justify-between gap-4 text-center md:flex-row md:text-left"
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-4xl">Stop chasing feedback. Start getting approvals.</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">Your first review link takes minutes.</p>
|
||||
<h2 className="text-3xl font-semibold tracking-[-0.02em] md:text-4xl">
|
||||
Stop chasing feedback. Start getting approvals.
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Your first review link takes minutes.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href={hostedCtaHref}
|
||||
@@ -674,14 +883,32 @@ export function LandingPage({ isLoggedIn }: LandingPageProps) {
|
||||
<div className="mx-auto flex max-w-[1200px] flex-col items-center justify-between gap-4 sm:flex-row">
|
||||
<div className="flex items-center gap-2">
|
||||
<Video className="h-4 w-4 text-primary" />
|
||||
<span className="font-mono text-xs text-muted-foreground">© 2026 IPEK TECH LLC. All rights reserved.</span>
|
||||
<span className="font-mono text-xs text-muted-foreground">
|
||||
© 2026 IPEK TECH LLC. All rights reserved.
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<a href="mailto:[email protected]" className="text-xs text-muted-foreground hover:text-foreground">info@open-frame.net</a>
|
||||
<a href="https://github.com/yusufipk/OpenFrame" className="text-xs text-muted-foreground hover:text-foreground">GitHub</a>
|
||||
<Link href="/terms" className="text-xs text-muted-foreground hover:text-foreground">Terms</Link>
|
||||
<Link href="/privacy" className="text-xs text-muted-foreground hover:text-foreground">Privacy</Link>
|
||||
<Link href="/refund" className="text-xs text-muted-foreground hover:text-foreground">Refund Policy</Link>
|
||||
<a
|
||||
href="mailto:info@open-frame.net"
|
||||
className="text-xs text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
info@open-frame.net
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/yusufipk/OpenFrame"
|
||||
className="text-xs text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
<Link href="/terms" className="text-xs text-muted-foreground hover:text-foreground">
|
||||
Terms
|
||||
</Link>
|
||||
<Link href="/privacy" className="text-xs text-muted-foreground hover:text-foreground">
|
||||
Privacy
|
||||
</Link>
|
||||
<Link href="/refund" className="text-xs text-muted-foreground hover:text-foreground">
|
||||
Refund Policy
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user