← back to Trademarks Copyright
src/app/drops/page.tsx
262 lines
"use client";
import { Suspense, useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
export default function DropsLandingPage() {
return (
<div>
<Suspense fallback={<div className="card p-8 mb-6" />}>
<Hero />
</Suspense>
<SampleDrop />
<Proof />
<WhoFor />
<Plans />
<HowItWorks />
<Fine />
</div>
);
}
function SampleDrop() {
const [drop, setDrop] = useState<{
id: number;
date: string;
subject: string;
intro: string | null;
items: { position: number; headline: string; blurb: string }[];
hidden_count: number;
} | null>(null);
useEffect(() => {
fetch("/api/drops/latest").then((r) => r.json()).then((j) => setDrop(j.drop));
}, []);
if (!drop) return null;
const date = new Date(drop.date).toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric" });
return (
<section className="card mb-6 overflow-hidden">
<div className="bg-ledger text-parchment px-6 py-4">
<div className="text-xs tracking-widest text-brass">SAMPLE · {date.toUpperCase()}</div>
<h2 className="mt-1 text-2xl font-semibold">{drop.subject}</h2>
{drop.intro && <p className="mt-2 text-sm italic text-parchment/75">{drop.intro}</p>}
</div>
<div>
{drop.items.map((it) => (
<div key={it.position} className="border-t border-ink/10 p-5">
<div className="text-xs text-ink/50">#{it.position}</div>
<h3 className="mt-1 text-lg font-semibold">{it.headline}</h3>
<p className="mt-1 text-sm text-ink/80">{it.blurb}</p>
</div>
))}
{drop.hidden_count > 0 && (
<div className="relative border-t border-ink/10 p-6 bg-brass/5 text-center">
<div className="absolute inset-0 pointer-events-none" style={{
background: "linear-gradient(180deg, transparent, #f7f4ec 80%)",
}} />
<div className="relative">
<div className="text-xs uppercase tracking-wide text-brass">+ {drop.hidden_count} more in today's drop</div>
<p className="mt-1 text-sm text-ink/70">
Subscribers got the rest in their inbox at 9am PT.
</p>
</div>
</div>
)}
</div>
</section>
);
}
function Hero() {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [loading, setLoading] = useState(false);
const [err, setErr] = useState<string | null>(null);
const sp = useSearchParams();
const ref = sp.get("ref") || "";
async function submit(e: React.FormEvent) {
e.preventDefault();
setLoading(true); setErr(null);
try {
const r = await fetch("/api/drops/signup", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email, name, ref: ref || undefined }),
});
const j = await r.json();
if (!r.ok) throw new Error(j.error || `HTTP ${r.status}`);
// Redirect to the private portal instead of showing the token inline.
window.location.href = `/drops/view/${j.subscriber.token}?welcomed=1`;
} catch (e) { setErr((e as Error).message); setLoading(false); }
}
return (
<div className="card p-8 mb-6 border-brass/30 bg-gradient-to-br from-parchment to-white">
<div className="text-xs uppercase tracking-widest text-brass">Daily Drops</div>
<h1 className="mt-2 text-4xl font-semibold tracking-tight">
Trademark & domain opportunities in your inbox. <span className="text-brass">Every day at 9am.</span>
</h1>
<p className="mt-3 max-w-2xl text-lg text-ink/70">
Abandoned US trademarks, public-domain works entering their 95th year, expired fashion-brand domains,
and unregistered names sitting on million-dollar businesses — curated, scored, and delivered fresh.
</p>
<form onSubmit={submit} className="mt-6 flex flex-wrap items-center gap-2">
<input
required
type="email"
placeholder="you@business.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-72"
/>
<input
placeholder="your name (optional)"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-52"
/>
<button type="submit" className="btn" disabled={loading}>
{loading ? "Starting…" : "Start free trial (3 drops)"}
</button>
</form>
{loading && <div className="mt-3 rounded bg-ink/5 p-3 text-sm text-ink/70">Setting up your account…</div>}
{err && <div className="mt-3 rounded bg-red-50 p-3 text-sm text-red-700">{err}</div>}
{ref && <div className="mt-2 text-xs text-brass">✓ referral applied — you're helping a friend too</div>}
<div className="mt-2 text-xs text-ink/50">No credit card. Unsubscribe anytime from any email.</div>
</div>
);
}
function Plans() {
const [loading, setLoading] = useState<string | null>(null);
const [err, setErr] = useState<string | null>(null);
const [email, setEmail] = useState("");
async function upgrade(tier: "standard" | "pro") {
if (!email) { setErr("Enter the email you signed up with, then click upgrade."); return; }
setLoading(tier); setErr(null);
try {
const r = await fetch("/api/drops/checkout", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email, tier }),
});
const j = await r.json();
if (!r.ok) throw new Error(j.error || `HTTP ${r.status}`);
if (j.url) window.location.href = j.url;
else setErr(j.message || "Upgraded (mock).");
} catch (e) { setErr((e as Error).message); }
finally { setLoading(null); }
}
return (
<div className="mb-6 card p-6">
<h2 className="text-2xl font-semibold">Pick a tier</h2>
<div className="mt-2 mb-4 flex flex-wrap items-center gap-2">
<label className="text-sm text-ink/70">Upgrade as:</label>
<input type="email" placeholder="you@business.com" value={email} onChange={(e) => setEmail(e.target.value)} className="w-64" />
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<Plan name="Trial"
price="Free · 3 drops"
features={["3 items per drop", "basic edition", "no credit card"]}
cta={null} />
<Plan name="Standard"
price="$29/mo"
features={["3 items per drop", "curated by editor + Qwen", "full archive access"]}
cta={<button onClick={() => upgrade("standard")} disabled={loading !== null} className="btn">{loading === "standard" ? "…" : "Upgrade"}</button>} />
<Plan name="Pro"
price="$99/mo"
highlight
features={["10 items per drop", "Pro-only tactical hints", "SWOT preview per item", "full archive + API access"]}
cta={<button onClick={() => upgrade("pro")} disabled={loading !== null} className="btn">{loading === "pro" ? "…" : "Upgrade"}</button>} />
</div>
{err && <div className="mt-3 rounded bg-red-50 p-3 text-sm text-red-700">{err}</div>}
</div>
);
}
function Plan({ name, price, features, cta, highlight }: { name: string; price: string; features: string[]; cta: React.ReactNode; highlight?: boolean }) {
return (
<div className={`card p-5 ${highlight ? "border-brass/60 ring-1 ring-brass/30" : ""}`}>
<div className="flex items-baseline justify-between">
<h3 className="text-lg font-semibold">{name}</h3>
<span className="text-sm text-brass">{price}</span>
</div>
<ul className="mt-3 space-y-1.5 text-sm text-ink/70">
{features.map((f, i) => <li key={i}>· {f}</li>)}
</ul>
{cta && <div className="mt-4">{cta}</div>}
</div>
);
}
function HowItWorks() {
return (
<div className="mb-6 card p-6">
<h2 className="text-2xl font-semibold">How we build each drop</h2>
<ol className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-4 text-sm">
<li><b>1. Crawl.</b> USPTO TSDR for cancelled marks. US Copyright Office for 95-year clocks. Fashion/DTC sites for unregistered brands + domain expiries.</li>
<li><b>2. Score.</b> Distinctiveness, age-fit, domain availability, common-law risk. Top candidates only.</li>
<li><b>3. Write.</b> Local LLM drafts editorial blurbs; human editor reviews before send.</li>
<li><b>4. Deliver.</b> Inbox at 9am PT. Full archive on your portal. Unsubscribe anytime.</li>
</ol>
</div>
);
}
function Proof() {
const stats = [
{ k: "30+", v: "abandoned trademarks in the curated catalog" },
{ k: "1929 cohort", v: "most recent public-domain batch" },
{ k: "4 labels", v: "domain snipe · abandonment watch · licensing · avoid" },
{ k: "9am PT", v: "delivered every single day" },
];
return (
<div className="mb-6 card p-6">
<h2 className="text-2xl font-semibold">What you get, in numbers</h2>
<dl className="mt-4 grid grid-cols-2 gap-4 md:grid-cols-4">
{stats.map((s, i) => (
<div key={i}>
<dt className="text-2xl font-semibold text-brass">{s.k}</dt>
<dd className="text-sm text-ink/70">{s.v}</dd>
</div>
))}
</dl>
</div>
);
}
function WhoFor() {
return (
<div className="mb-6 card p-6">
<h2 className="text-2xl font-semibold">Who reads Drops</h2>
<div className="mt-3 grid grid-cols-1 gap-4 md:grid-cols-3 text-sm">
<div>
<div className="font-semibold text-brass">Brand builders</div>
<p className="mt-1 text-ink/80">Looking for a distinctive name with built-in recall? Drops surfaces marks nobody's using. You file; you build.</p>
</div>
<div>
<div className="font-semibold text-brass">Domain investors</div>
<p className="mt-1 text-ink/80">The domain-snipe label flags expiring domains paired with brand recognition. Wholesale prices at drop, retail on sellback.</p>
</div>
<div>
<div className="font-semibold text-brass">IP attorneys</div>
<p className="mt-1 text-ink/80">Client lookups, FTO reconnaissance, and the licensing-outreach list as a client-development feed.</p>
</div>
</div>
</div>
);
}
function Fine() {
return (
<div className="text-xs text-ink/60">
Nothing in Drops is legal advice. Before registering any mark or buying any domain, talk to a USPTO-registered attorney.
Drops flags <em>opportunities</em>, not guarantees — common-law rights survive trademark abandonment and the ACPA applies to bad-faith domain buys.
We surface the low-risk side of the ledger and tell you why.
</div>
);
}