diff --git a/app/public/hero.mp4 b/app/public/hero.mp4 new file mode 100644 index 0000000..4c33dc8 Binary files /dev/null and b/app/public/hero.mp4 differ diff --git a/app/public/prot-bg.png b/app/public/prot-bg.png new file mode 100644 index 0000000..994286d Binary files /dev/null and b/app/public/prot-bg.png differ diff --git a/app/src/App.tsx b/app/src/App.tsx index 09b7515..f82db85 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -7,7 +7,7 @@ import { ComplexDetailPage } from './pages/ComplexDetailPage'; import { DashboardPage } from './pages/DashboardPage'; import { MutationPage } from './pages/MutationPage'; import { Footer } from './components/layout/Footer'; -import { LandingV3 } from './v3/LandingV3'; +import { Landing } from './v3/Landing'; import { PlatformPage } from './v3/pages/PlatformPage'; import { ComplexDetailPageV3 } from './v3/pages/ComplexDetailPageV3'; @@ -35,7 +35,7 @@ function App() { } /> } /> - } /> + } /> } /> diff --git a/app/src/v3/Landing.tsx b/app/src/v3/Landing.tsx new file mode 100644 index 0000000..f42bded --- /dev/null +++ b/app/src/v3/Landing.tsx @@ -0,0 +1,447 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { NavV3 } from './components/NavV3'; +import { FooterV3 } from './components/FooterV3'; +import { ProblemV3 } from './sections/ProblemV3'; +import { PlatformV3 } from './sections/PlatformV3'; +import { WorkflowV3 } from './sections/WorkflowV3'; +import { BenchmarkV3 } from './sections/BenchmarkV3'; +import { InstallV3 } from './sections/InstallV3'; +import { CTAV3 } from './sections/CTAV3'; + + +const FONTS_HREF = + 'https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap'; + +function useFonts() { + useEffect(() => { + if (document.querySelector(`link[href="${FONTS_HREF}"]`)) return; + const pc1 = document.createElement('link'); + pc1.rel = 'preconnect'; + pc1.href = 'https://fonts.googleapis.com'; + document.head.appendChild(pc1); + const pc2 = document.createElement('link'); + pc2.rel = 'preconnect'; + pc2.href = 'https://fonts.gstatic.com'; + pc2.crossOrigin = ''; + document.head.appendChild(pc2); + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = FONTS_HREF; + document.head.appendChild(link); + }, []); +} + + +function useReveal() { + useEffect(() => { + const io = new IntersectionObserver( + (entries) => { + entries.forEach((e) => { + if (e.isIntersecting) { + e.target.classList.add('v3-in'); + io.unobserve(e.target); + } + }); + }, + { threshold: 0.12, rootMargin: '0px 0px -40px 0px' } + ); + document.querySelectorAll('.v3-reveal:not(.v3-in)').forEach((el) => io.observe(el)); + return () => io.disconnect(); + }, []); +} + +// ── font shorthands ──────────────────────────────────────────────────────────── +const SERIF = '"Instrument Serif", "Times New Roman", Times, serif'; +const SANS = '"Geist", -apple-system, BlinkMacSystemFont, system-ui, sans-serif'; +const MONO = '"JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, monospace'; + +// ── color tokens ────────────────────────────────────────────────────────────── +const INK0 = '#FFFFFF'; +const POCKET = '#C6FF3D'; +const POCKET1 = '#B3E832'; +const TEXT_DIM = 'rgba(255,255,255,0.72)'; +const TEXT_FAINT = 'rgba(255,255,255,0.50)'; +const HAIR = 'rgba(255,255,255,0.18)'; +const HAIR_STRONG = 'rgba(255,255,255,0.30)'; +const EASE = 'cubic-bezier(0.22,1,0.36,1)'; + +const NAV_LINKS = [ + { label: 'Platform', sub: 'How ProtPocket works', href: '#platform' }, + { label: 'Benchmarks', sub: 'Accuracy vs. FPocket, SiteMap', href: '#benchmarks' }, + { label: 'Research', sub: 'Papers & methods', href: '#research' }, + { label: 'Request access', sub: 'Early access programme', href: '#contact' }, +]; + +export function Landing() { + useFonts(); + useReveal(); + const [navOpen, setNavOpen] = useState(false); + const [openKey, setOpenKey] = useState(0); + const [showNav, setShowNav] = useState(false); + const sentinelRef = useRef(null); + + useEffect(() => { + const el = sentinelRef.current; + if (!el) return; + const io = new IntersectionObserver( + ([entry]) => setShowNav(!entry.isIntersecting), + { threshold: 0 } + ); + io.observe(el); + return () => io.disconnect(); + }, []); + + return ( + <> + {/* Notch nav — fixed, above everything, fades out when NavV3 takes over */} +
+
{ setNavOpen(true); setOpenKey(k => k + 1); }} + onMouseLeave={() => setNavOpen(false)} + > +
+ +   ProtPocket · Drug discovery   + +
+
+
+
+ {NAV_LINKS.map((link, i) => ( + + + {link.label} + {link.sub} + + + + ))} +
+
+
+
+
+ + + + {/* Frame — sticky so sections slide over it */} +
+ + {/* Stage */} +
+ {/* Video background */} + + + {/* Dark weight overlay */} +
+ + {/* Grain texture */} +
+ + {/* Dot grid */} +
+ + {/* Content */} +
+ + {/* Top bar — corners only */} +
+
+ © 2026 +
+
+ AlphaFold-native +
+
+ + {/* Spacer */} +
+ + {/* Hero */} +
+ {/* Left — wordmark */} +
+

+ ProtPocket + +

+
+ + {/* Right — detail */} +
+

+ Find the pocket. Cure the disease. +

+ +

+ ProtPocket reads AlphaFold structures and returns ranked binding sites — including{' '} + + cryptic + {' '} + pockets that only open in motion. Built for chemistry teams who need answers in hours, not quarters. +

+ + {/* CTA row */} + + + {/* Caption */} +
+ + cryptic pockets + · + druggability + · + hours, not quarters +
+
+
+
+
+
+ {/* Sentinel — crossing viewport top triggers NavV3 */} +
+ + {/* NavV3 — fades in once hero is scrolled past. NO transform here — transform breaks position:fixed children */} +
+ +
+ + {/* Sections — card that slides over the sticky hero */} +
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ + ); +}