feat(docs): streamline product and privacy pages

This commit is contained in:
2026-07-16 19:49:17 +02:00
parent 0a185077af
commit 526a5da444
16 changed files with 869 additions and 1755 deletions

View File

@@ -1,48 +1,54 @@
import Image from "next/image";
import Link from "next/link";
import { ApprovalDemo } from "@/components/approval-demo";
import { HeroIllustration } from "@/components/hero-illustration";
import { Icon, type IconName } from "@/components/icons";
import { InvitationShowcase } from "@/components/invitation-showcase";
import { NetworkIllustration } from "@/components/network-illustration";
import { PlatformOrbit } from "@/components/platform-orbit";
import { PlatformIcon, supportedPlatforms } from "@/components/platform-icon";
import { Reveal } from "@/components/reveal";
import { TransferFlow } from "@/components/transfer-flow";
const trustItems = [
{ value: "No account", detail: "Open the app and start sharing" },
{ value: "No hosted copy", detail: "Your transfer is not parked in our cloud" },
{ value: "No silent overwrite", detail: "Existing files stay untouched" },
];
const privacyPoints: Array<{
const steps: Array<{
icon: IconName;
number: string;
title: string;
text: string;
}> = [
{
icon: "lock",
icon: "folder",
number: "01",
title: "Encrypted in transit",
text: "Each connection is authenticated and end-to-end encrypted, including through a relay.",
title: "Choose what to send",
text: "Pick files, a batch, or a complete folder. VniDrop preserves the folder structure.",
},
{
icon: "qr",
number: "02",
title: "Share an invitation",
text: "Introduce the devices with a QR code, NFC tap, or portable .vnd invitation.",
},
{
icon: "shield",
number: "02",
title: "Approval by default",
text: "An invitation helps devices meet. It is not automatic permission to download.",
number: "03",
title: "Approve and transfer",
text: "The receiver asks first. Once approved, the files move over an authenticated encrypted connection.",
},
];
const trustItems: Array<{
icon: IconName;
title: string;
text: string;
}> = [
{
icon: "devices",
title: "Direct when possible",
text: "Devices connect directly when they can. An encrypted relay forwards traffic when they cannot.",
},
{
icon: "lock",
title: "No hosted copy",
text: "A relay is a route, not storage. Your transfer is never turned into a cloud download.",
},
{
icon: "verified",
number: "03",
title: "Verified on arrival",
text: "Content addressing checks that incoming bytes match exactly what was offered.",
},
{
icon: "stop",
number: "04",
title: "Revocable access",
text: "Stop a share at any time and the invitation can no longer be used.",
text: "Content addressing checks that the received bytes match exactly what you sent.",
},
];
@@ -67,224 +73,95 @@ export default function HomePage() {
between devices.
</h1>
<p className="hero-lead">
Send files and folders directly when possible, with an end-to-end encrypted relay
when needed. No account. No hosted transfer copy. You decide who receives them.
Send files and folders across your devicesdirect when possible, private by design,
and always under your control.
</p>
<div className="hero-actions">
<a className="button button-primary" href="#how-it-works">
See how it works
<Icon name="arrow" />
</a>
<a
className="button button-ghost"
href="https://github.com/vnidrop/vnidrop"
target="_blank"
rel="noreferrer"
>
<Icon name="github" />
Explore the project
</a>
</div>
<div className="hero-platforms" aria-label="Supported platforms">
<span>Available across</span>
<ul>
<li>Android</li>
<li>iOS</li>
<li>macOS</li>
<li>Windows</li>
<li>Linux</li>
{supportedPlatforms.map((platform) => (
<li key={platform} aria-label={platform} title={platform}>
<PlatformIcon className="platform-glyph" platform={platform} />
<span className="sr-only">{platform}</span>
</li>
))}
</ul>
</div>
</Reveal>
</div>
<Reveal className="hero-art-reveal" delay={120}>
<HeroIllustration />
<figure className="hero-app-preview">
<Image
className="hero-app-preview-image"
src="/App.png"
width={2338}
height={1873}
sizes="(max-width: 920px) calc(100vw - 32px), 56vw"
alt="VniDrop running on desktop and iPhone, showing transfer review, receiver permissions, and invitation options."
priority
unoptimized
/>
</figure>
</Reveal>
</div>
</section>
<section className="trust-section" aria-label="VniDrop principles">
<div className="page-shell trust-grid">
{trustItems.map((item, index) => (
<Reveal key={item.value} className="trust-item" delay={index * 70}>
<span className="trust-number">0{index + 1}</span>
<div>
<strong>{item.value}</strong>
<p>{item.detail}</p>
</div>
</Reveal>
))}
</div>
</section>
<div className="word-stream" aria-hidden="true">
<div>
<span>FILES</span><i></i><span>FOLDERS</span><i></i><span>PHOTOS</span><i></i>
<span>PROJECTS</span><i></i><span>FILES</span><i></i><span>FOLDERS</span><i></i>
<span>PHOTOS</span><i></i><span>PROJECTS</span><i></i>
</div>
</div>
<section id="how-it-works" className="section flow-section">
<section id="how-it-works" className="simple-section simple-flow-section">
<div className="page-shell">
<Reveal className="section-heading section-heading-centered">
<span className="kicker">ONE DROP, FIVE MOMENTS</span>
<h2>A handoff that stays in your hands.</h2>
<Reveal className="simple-heading">
<span className="kicker">HOW IT WORKS</span>
<h2>Three steps. No account.</h2>
<p>
VniDrop keeps the transfer understandable from first selection to final verified file.
Choose the files, introduce the devices, and approve the handoff. VniDrop handles the
secure route.
</p>
</Reveal>
<Reveal delay={100}>
<TransferFlow />
</Reveal>
</div>
</section>
<section id="invitations" className="section invitation-section">
<div className="page-shell">
<Reveal className="section-heading section-heading-split">
<div>
<span className="kicker">MAKE THE INTRODUCTION</span>
<h2>One transfer. Three ways to meet.</h2>
</div>
<p>
The invitation is small enough to scan, tap, or send. Your files stay where they are
until a receiver connects and you allow the transfer.
</p>
</Reveal>
<Reveal delay={100}>
<InvitationShowcase />
</Reveal>
</div>
</section>
<section className="section approval-section">
<div className="page-shell approval-layout">
<Reveal className="approval-copy">
<span className="kicker">CONTROL IS BUILT IN</span>
<h2>A link is not permission.</h2>
<p className="section-lead">
Every new receiver knocks by default. Approve people you recognize, refuse the rest,
and follow progress for each device.
</p>
<ul className="check-list">
<li><Icon name="check" /> See the receiver before anything moves</li>
<li><Icon name="check" /> Cancel a transfer already in progress</li>
<li><Icon name="check" /> Stop sharing to revoke future access</li>
</ul>
<p className="approval-note">
Need an open handoff? An optional Anyone with this transfer mode is available for
non-sensitive files.
</p>
</Reveal>
<Reveal className="approval-demo-wrap" delay={120}>
<ApprovalDemo />
<span className="demo-hint"><Icon name="spark" /> Try the controls</span>
</Reveal>
</div>
</section>
<section id="privacy" className="privacy-principles-section">
<div className="page-shell">
<Reveal className="privacy-intro">
<span className="kicker kicker-light">PRIVACY BY DESIGN</span>
<h2>The file never becomes our file.</h2>
<p>
VniDrop coordinates a secure handoff. It does not turn your transfer into a hosted
download sitting on someone elses storage.
</p>
</Reveal>
<div className="privacy-point-grid">
{privacyPoints.map((point, index) => (
<Reveal key={point.number} className="privacy-point" delay={index * 70}>
<div className="privacy-point-top">
<span><Icon name={point.icon} /></span>
<small>{point.number}</small>
<div className="simple-steps">
{steps.map((step) => (
<article key={step.number} className="simple-step">
<div className="simple-step-top">
<span>{step.number}</span>
<Icon name={step.icon} />
</div>
<h3>{point.title}</h3>
<p>{point.text}</p>
</Reveal>
<h3>{step.title}</h3>
<p>{step.text}</p>
</article>
))}
</div>
<Reveal className="privacy-policy-link" delay={120}>
<p>Diagnostics are optional and exclude invitations, file paths, and transfer contents.</p>
<Link href="/privacy/">
Read the full privacy policy
</div>
</section>
<section id="privacy" className="trust-summary-section">
<div className="page-shell trust-summary">
<Reveal className="trust-summary-copy">
<span className="kicker kicker-light">PRIVACY BY DESIGN</span>
<h2>What VniDrop doesand doesnt do.</h2>
<p>
The transfer happens between devices. These are the details that matter when you
decide what to share and who can receive it.
</p>
<Link className="simple-text-link" href="/privacy/">
Read the privacy policy
<Icon name="arrow" />
</Link>
</Reveal>
</div>
</section>
<section className="network-section">
<div className="page-shell network-layout">
<Reveal className="network-copy">
<span className="kicker kicker-light">DIRECT WHEN POSSIBLE</span>
<h2>The shortest safe path, chosen automatically.</h2>
<p>
VniDrop tries to connect devices directlyeven across home routers and mobile
networks. If that path is not available, a relay forwards the same encrypted
connection without becoming a file store.
</p>
<blockquote>
<span></span>
The relay can move the envelope. It cannot open it.
</blockquote>
</Reveal>
<Reveal delay={120}>
<NetworkIllustration />
</Reveal>
</div>
</section>
<section id="platforms" className="section platforms-section">
<div className="page-shell platforms-layout">
<Reveal className="platforms-copy">
<span className="kicker">ONE FAMILIAR HANDOFF</span>
<h2>At home on every screen.</h2>
<p className="section-lead">
Android, iOS, macOS, Windows, and Linux share one clear transfer experience while each
platform keeps control of its own file picker and save destination.
</p>
<div className="platform-feature-list">
<span><Icon name="devices" /> Cross-platform by design</span>
<span><Icon name="folder" /> Native file destinations</span>
<span><Icon name="code" /> Open-source core</span>
</div>
</Reveal>
<Reveal className="orbit-wrap" delay={100}>
<PlatformOrbit />
</Reveal>
</div>
</section>
<section className="final-cta-section">
<div className="page-shell">
<Reveal className="final-cta">
<div className="final-cta-orb final-cta-orb-one" aria-hidden="true" />
<div className="final-cta-orb final-cta-orb-two" aria-hidden="true" />
<span className="kicker kicker-light">OPEN SOURCE · APACHE 2.0</span>
<h2>Drop the cloud holding pen. Keep the control.</h2>
<p>
VniDrop is in early development. Explore the code, build the app, and help shape a
more direct way to share.
</p>
<div className="final-cta-actions">
<a
className="button button-light"
href="https://github.com/vnidrop/vnidrop"
target="_blank"
rel="noreferrer"
>
<Icon name="github" />
View the source
</a>
<Link className="button button-dark-ghost" href="/privacy/">
Privacy policy
<Icon name="arrow" />
</Link>
</div>
</Reveal>
<div className="trust-summary-list">
{trustItems.map((item) => (
<article key={item.title} className="trust-summary-item">
<span><Icon name={item.icon} /></span>
<div>
<h3>{item.title}</h3>
<p>{item.text}</p>
</div>
</article>
))}
</div>
</div>
</section>
</main>