Get client sign-off from one link.
Timestamped comments, voice notes and annotations on one timeline. Clients review in the browser, no account.
Put two cuts on one timeline and scrub them together, then approve without asking which version this is.
'use client'; import Image from 'next/image'; import Link from 'next/link'; import { CtaLink } from '@/components/marketing/cta-link'; import { MarketingCompareLinks } from '@/components/marketing/marketing-compare-links'; import { useEffect, useRef } from 'react'; import { gsap } from 'gsap'; import { Video, MoveRight, Play, Mic, Users, Tag, Code, Lock, Upload, Share2, MessageSquare, Check, CheckCircle2, Copy, Link as LinkIcon, Github, ArrowRight, } from 'lucide-react'; interface LandingPageProps { isLoggedIn: boolean; } const controlButtonClass = 'group relative isolate inline-flex h-8 items-center justify-center overflow-hidden border border-border bg-background px-2.5 text-[11px] font-medium text-foreground transition-colors duration-300 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:h-9 sm:px-4 sm:text-xs'; const primaryCtaClass = 'group relative isolate inline-flex min-h-12 items-center justify-center gap-2 overflow-hidden border border-primary bg-primary px-6 py-3 text-center text-[13px] font-semibold text-primary-foreground transition-colors duration-300 hover:bg-primary/90 sm:px-8 sm:text-sm'; const labelClass = 'text-[11px] uppercase tracking-[0.14em] text-muted-foreground'; const trustSignals = [ { label: 'No client accounts', icon: Users }, { label: 'Flat $10 per month', icon: Tag }, { label: 'Fair Source, self-hostable', icon: Code }, { label: 'Private by default', icon: Lock }, ]; const steps = [ { label: 'Upload a cut', description: 'Drop a file, or import an unlisted YouTube video.', icon: Upload, }, { label: 'Share the link', description: 'Set permissions once, the client needs no account.', icon: Share2, }, { label: 'Timestamped feedback', description: 'Comments, voice notes and drawings land on the frame.', icon: MessageSquare, }, { label: 'Approve and move on', description: 'The cut gets a signed off Approved status, export the notes.', icon: Check, }, ]; const hostedFeatures = [ 'Unlimited collaborators and clients', 'Comments, voice notes, annotations', 'Version compare, history, approvals', 'Permissioned share links, PDF and CSV export', 'Unlimited unlisted YouTube imports', '200 GB storage, add 100 GB for $5/mo', ]; const selfHostedFeatures = [ 'Full source code, read it and audit it', 'Docker setup for self-hosting', 'Every release becomes Apache 2.0 two years after publication', ]; const faq = [ { q: 'Do clients need an account?', a: 'No. They can review in the browser with a share link.', }, { q: 'Is OpenFrame open source?', a: 'OpenFrame is Fair Source, licensed under the Functional Source License (FSL). You can read and audit the full source code and self-host it, and every release automatically becomes Apache 2.0 open source two years after publication.', }, { q: 'Is there a free trial?', a: 'Yes. Hosted Cloud starts with a 7-day free trial and never asks for a card to begin it. After that it is a flat $10/mo, with no per-seat or per-client fees.', }, { q: 'How is this different from a Google Drive link?', a: 'Drive does not give timestamped discussion, voice notes, annotations, or version compare, which is where approval time is actually saved.', }, { 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 info@open-frame.net and we will help you choose the best setup.', }, { q: 'Can I self-host?', a: 'Yes. The full source code is public and ships with a Docker setup for self-hosting. Hosted Cloud is for teams who want zero setup.', }, ]; // Bar heights for the voice note waveform, in percent. The first twenty read // as "played", the rest as "remaining". const waveformBars = [ 28, 52, 74, 40, 96, 62, 34, 80, 46, 90, 38, 66, 88, 30, 72, 50, 84, 42, 94, 56, 32, 68, 44, 86, 36, 60, 92, 48, 26, 70, 54, 82, 38, 64, ]; const shareOptions = [ { label: 'Can comment', on: true }, { label: 'Ask for a name before commenting', on: true }, { label: 'Allow download of the original file', on: false }, { label: 'Show earlier versions', on: false }, ]; const heroGridStyle = { backgroundImage: 'linear-gradient(to right, color-mix(in oklab, var(--foreground) 5%, transparent) 1px, transparent 1px), linear-gradient(to bottom, color-mix(in oklab, var(--foreground) 5%, transparent) 1px, transparent 1px)', backgroundSize: '48px 48px', maskImage: 'radial-gradient(ellipse 820px 640px at 50% 30%, #000 0%, rgba(0,0,0,0.6) 52%, transparent 80%)', WebkitMaskImage: 'radial-gradient(ellipse 820px 640px at 50% 30%, #000 0%, rgba(0,0,0,0.6) 52%, transparent 80%)', } as const; const heroGlowStyle = { background: 'radial-gradient(closest-side, color-mix(in oklab, var(--primary) 18%, transparent), color-mix(in oklab, var(--primary) 6%, transparent) 55%, transparent 100%)', } as const; function SectionTitle({ children }: { children: React.ReactNode }) { return (
Timestamped comments, voice notes and annotations on one timeline. Clients review in the browser, no account.
Put two cuts on one timeline and scrub them together, then approve without asking which version this is.
Record a note or circle the frame, and both land on the exact second instead of in a follow-up email.
This block needs to clear the subject, I drew where it should sit.
Decide what the link allows, send it, and get an Approved status you can point at later.
Tell us your storage, usage and workflow, we will recommend the right approach. Hosted, self-hosted, or a mix of both.
{item.a}