Skip to content
This repository was archived by the owner on Jun 26, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copy to .env.local and fill in. .env.local is gitignored.

# Kit (ConvertKit) v4 API key — powers /api/subscribe email capture.
# Get it from Kit → Settings → Advanced → API (the "v4" key).
KIT_API_KEY=

# Optional: the Kit Form ID to subscribe new emails into (recommended).
# Find it via the `kit` skill or in the form's embed URL / dashboard.
# If unset, subscribers are created on the account without a specific form.
KIT_FORM_ID=

# Stripe — powers /api/checkout for the skills marketplace.
# Get these from Stripe → Developers → API keys. Use test keys locally.
# If STRIPE_SECRET_KEY is unset, checkout returns a friendly "not live yet"
# message instead of erroring.
STRIPE_SECRET_KEY=

# Publishable key — safe to expose to the browser (NEXT_PUBLIC_ prefix).
# Not required for the current redirect-to-Checkout flow, but handy if you
# later add Stripe.js on the client.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
24 changes: 21 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"dependencies": {
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4"
"react-dom": "19.2.4",
"stripe": "^22.2.2"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
Expand Down
248 changes: 248 additions & 0 deletions src/app/advertorial/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import type { Metadata } from "next";
import Link from "next/link";
import SignupForm from "@/components/SignupForm";

export const metadata: Metadata = {
title: "The Advertorial Engine — channel47",
description:
"The gap between your ad and your checkout is where paid traffic dies. The AI system that turns real customer research into a finished DTC advertorial — the skill, the workflow, the swipe file.",
openGraph: {
title: "The Advertorial Engine — channel47",
description:
"Turn real customer research into a finished DTC advertorial. The skill, the workflow, the swipe file. Built from real accounts.",
url: "https://channel47.dev/advertorial",
siteName: "channel47",
type: "website",
},
};

const KIT = [
{
n: "01",
t: "The advertorial skill",
d: "Turns a chosen angle into a structured, ready-to-ship presell page — the part the free plugins don’t include.",
},
{
n: "02",
t: "The research workflow",
d: "Pull real customer language from Reddit, Amazon, and Trustpilot. Build the persona. Rank the angles before you write a word.",
},
{
n: "03",
t: "The swipe file",
d: "Real DTC advertorials, annotated section by section, so you can see exactly why each one earns the click to checkout.",
},
{
n: "04",
t: "The prompt library",
d: "Generate, critique, and rewrite every section. Never start from a blank page again.",
},
{
n: "05",
t: "The workshop walkthrough",
d: "The full live build, start to finish — so you can recreate it for any product the same day.",
},
];

const STEPS = [
{ n: "1", t: "Drop your email", d: "Takes five seconds. No funnel, no quiz." },
{
n: "2",
t: "Get the goods",
d: "The recording and the kit files land in your inbox the moment they ship.",
},
{
n: "3",
t: "Build your first one",
d: "Run the system on a real product, same day.",
},
];

const FAQ = [
{
q: "Is this free?",
a: "The workshop recording and the open-source research plugins, yes. The full kit is a small one-time thing when it ships — get on the list for first dibs and the launch price.",
},
{
q: "Do I need to know how to code?",
a: "No. If you can use Claude, you can run this. The skill does the heavy lifting; you bring the judgment.",
},
{
q: "What’s the catch?",
a: "None. Get on the list, get the files. If you’d rather I build pages for you later, that door’s open — but it’s not the ask here.",
},
];

export default function Advertorial() {
return (
<main className="flex-1">
{/* Hero */}
<section className="mx-auto max-w-3xl px-6 pt-16 pb-20 sm:pt-24">
<p className="eyebrow mb-5">The Advertorial Engine</p>
<h1 className="text-balance text-4xl font-semibold leading-[1.05] tracking-tight sm:text-6xl">
Your ad did its job.
<br />
Your page killed the sale.
</h1>
<p className="mt-6 max-w-xl text-lg leading-relaxed text-muted">
Cold traffic isn’t ready to buy — it’s ready to read. I built the AI
system that turns real customer research into a finished DTC
advertorial: the skill, the workflow, the swipe file. Drop your email
and I’ll send the workshop recording and the files the moment they
ship.
</p>

<div className="mt-9 max-w-xl">
<SignupForm location="advertorial-hero" />
<p className="mt-3 font-mono text-xs leading-relaxed text-faint">
The recording + the skill files when I ship them + first dibs on
what’s next. No spam, no funnel.
</p>
</div>
</section>

{/* The problem */}
<section className="border-t border-line bg-surface/40">
<div className="mx-auto max-w-3xl px-6 py-20">
<p className="eyebrow mb-5">The gap</p>
<h2 className="text-balance text-3xl font-semibold leading-tight tracking-tight sm:text-4xl">
Between the click and the checkout, paid traffic dies.
</h2>
<p className="mt-6 max-w-xl text-lg leading-relaxed text-muted">
Your ad promised something specific. Your product page greets a
stranger with a buy button. The advertorial is the page in between —
the one that earns the sale before it asks for it.
</p>
</div>
</section>

{/* What's in the kit */}
<section className="border-t border-line">
<div className="mx-auto max-w-3xl px-6 py-20">
<p className="eyebrow mb-5">What’s in the kit</p>
<h2 className="mb-12 text-balance text-3xl font-semibold leading-tight tracking-tight sm:text-4xl">
The whole system I built live — yours to keep.
</h2>
<ul className="space-y-9">
{KIT.map((item) => (
<li key={item.n} className="flex gap-5 sm:gap-7">
<span className="pt-1 font-mono text-sm tabular-nums text-accent/80">
{item.n}
</span>
<div>
<h3 className="text-lg font-semibold tracking-tight">
{item.t}
</h3>
<p className="mt-1.5 max-w-xl leading-relaxed text-muted">
{item.d}
</p>
</div>
</li>
))}
</ul>
</div>
</section>

{/* Who built this */}
<section className="border-t border-line bg-surface/40">
<div className="mx-auto max-w-3xl px-6 py-20">
<p className="eyebrow mb-5">Who built this</p>
<h2 className="text-balance text-3xl font-semibold leading-tight tracking-tight sm:text-4xl">
I’m Jackson. I manage $6M+ in DTC ad spend.
</h2>
<p className="mt-6 max-w-xl text-lg leading-relaxed text-muted">
The research-to-creative pipeline underneath this is open-source —
anyone can grab it free. This is the part I didn’t give away: the
layer that turns that research into a page that sells. Built from
real accounts, not a course.
</p>
<div className="mt-7 flex flex-wrap gap-x-6 gap-y-2 font-mono text-sm">
<a
href="https://github.com/ctrlswing"
target="_blank"
rel="noopener noreferrer"
className="text-muted underline-offset-4 transition-colors hover:text-accent hover:underline"
>
The free plugins on GitHub →
</a>
<Link
href="/skills"
className="text-muted underline-offset-4 transition-colors hover:text-accent hover:underline"
>
Browse the paid skills →
</Link>
</div>
</div>
</section>

{/* How it works */}
<section className="border-t border-line">
<div className="mx-auto max-w-3xl px-6 py-20">
<p className="eyebrow mb-12">How this goes</p>
<div className="grid gap-10 sm:grid-cols-3">
{STEPS.map((s) => (
<div key={s.n}>
<span className="font-mono text-2xl tabular-nums text-accent">
{s.n}
</span>
<h3 className="mt-3 text-lg font-semibold tracking-tight">
{s.t}
</h3>
<p className="mt-1.5 leading-relaxed text-muted">{s.d}</p>
</div>
))}
</div>
</div>
</section>

{/* Quiet DFY footnote */}
<section className="border-t border-line">
<div className="mx-auto max-w-3xl px-6 py-14">
<div className="rounded-lg border border-line bg-surface px-6 py-6">
<p className="leading-relaxed text-muted">
<span className="font-semibold text-fg">
Run a brand and would rather skip the homework?
</span>{" "}
Reply once you’re on the list. I’m taking a couple of pilot brands
to build the first case studies — heavily discounted, in exchange
for permission to publish the before-and-after numbers.
</p>
</div>
</div>
</section>

{/* Final CTA */}
<section className="border-t border-line bg-surface/40">
<div className="mx-auto max-w-3xl px-6 py-20">
<h2 className="text-balance text-3xl font-semibold leading-tight tracking-tight sm:text-4xl">
Get the system.
</h2>
<p className="mt-4 max-w-xl text-lg leading-relaxed text-muted">
The recording, the skill files, and first dibs on the kit. One
email, no funnel.
</p>
<div className="mt-8 max-w-xl">
<SignupForm location="advertorial-footer" buttonText="Send it to me" />
</div>
</div>
</section>

{/* FAQ */}
<section className="border-t border-line">
<div className="mx-auto max-w-3xl px-6 py-20">
<p className="eyebrow mb-10">Questions</p>
<dl className="space-y-8">
{FAQ.map((f) => (
<div key={f.q}>
<dt className="text-lg font-semibold tracking-tight">{f.q}</dt>
<dd className="mt-2 max-w-xl leading-relaxed text-muted">
{f.a}
</dd>
</div>
))}
</dl>
</div>
</section>
</main>
);
}
Loading