/* sema ikutoke Collections — single-file React prototype */
const { useState, useEffect, useRef, useMemo, useCallback } = React;
/* ─── tweak defaults ─── */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"palette": "cream",
"pair": "editorial",
"hero": "split",
"pricing": "trio",
"checkout": "drawer",
"canvas": "dots"
}/*EDITMODE-END*/;
/* ─── data ─── */
const COVER_STYLES = [
// 8 distinct cover gradients for templates / tiles
{ bg: "linear-gradient(135deg, #E14118 0%, #1B1410 70%)", tag: "Vinyl Co." },
{ bg: "linear-gradient(160deg, #1B8B53 0%, #F1E8D1 100%)", tag: "Pasture" },
{ bg: "radial-gradient(60% 60% at 30% 30%, #F1582E, #14110D 70%)", tag: "Embers" },
{ bg: "linear-gradient(135deg, #2D261F 0%, #C2421A 100%)", tag: "Atelier 03" },
{ bg: "linear-gradient(45deg, #5D2BD0 0%, #F4DCD2 90%)", tag: "Stayed" },
{ bg: "radial-gradient(circle at 30% 30%, #FFC83D, #C2421A 60%, #14110D 100%)", tag: "Sun&Co" },
{ bg: "linear-gradient(135deg, #0E2740 0%, #E14118 95%)", tag: "Nightline" },
{ bg: "linear-gradient(180deg, #F8F1DD 0%, #1B8B53 100%)", tag: "Greenroom" },
];
const TEMPLATES = [
{ id: "t1", name: "Quiet Pages", cat: "Writers", price: "KSh 0", note: "Free template" },
{ id: "t2", name: "Pasture & Light", cat: "Photographers", price: "KSh 2,400" },
{ id: "t3", name: "Atelier 03", cat: "Designers", price: "KSh 1,800" },
{ id: "t4", name: "Field Notes", cat: "Writers", price: "KSh 1,200" },
{ id: "t5", name: "Embers", cat: "Musicians", price: "KSh 2,000" },
{ id: "t6", name: "Stayed", cat: "Course creators", price: "KSh 3,200" },
{ id: "t7", name: "Sun & Co.", cat: "Illustrators", price: "KSh 1,500" },
{ id: "t8", name: "Nightline", cat: "Podcasters", price: "KSh 2,000" },
{ id: "t9", name: "Greenroom", cat: "Filmmakers", price: "KSh 2,800" },
];
const TICKER = [
"M-PESA Live · 12,847 collections sold today",
"Nairobi · Mombasa · Kisumu · Eldoret · Lagos · Kigali · Cape Town · Accra",
"Avg. payout 11s after STK approval",
"Built in Nairobi, made for makers everywhere",
"Bridging KSh, USD, GHS, NGN, ZAR — settled in your wallet",
];
const HOW_STEPS = [
{ n: "01", title: "Compose.", body: "Pick a shell, drag in your work. sema ikutoke becomes the single source of truth for your collection.", line: "▸ builder · blocks · drag & drop" },
{ n: "02", title: "Distribute.", body: "One click syncs your catalog to WhatsApp, Instagram, Facebook, TikTok — and your own site.", line: "▸ WA · IG · FB · TT · Jumia" },
{ n: "03", title: "Get paid.", body: "Buyer tap. M-PESA STK pushes to their phone. Money in your wallet in seconds, every channel.", line: "▸ M-PESA · settle in 11s" },
];
/* ─── view router stage names ─── */
const VIEWS = [
["landing", "Landing"],
["signup", "Sign up"],
["templates", "Templates"],
["builder", "Builder"],
["channels", "Channels"],
["store", "Storefront"],
["checkout", "Checkout"],
["dashboard", "Dashboard"],
];
/* ═══════════════════════════════════════════════════════════════════════
Reusable bits
═══════════════════════════════════════════════════════════════════════ */
function Brand() {
return (
R
sema ikutoke · Collections
);
}
function TopNav({ goto, view }) {
return (
);
}
function Ticker() {
const items = [...TICKER, ...TICKER, ...TICKER];
return (
{items.map((t, i) => (
{t}
))}
);
}
function ProtoNav({ view, goto }) {
return (
{VIEWS.map(([v, label], i) => (
goto(v)}
>
{String(i+1).padStart(2,"0")} {label}
))}
);
}
/* ═══════════════════════════════════════════════════════════════════════
LANDING
═══════════════════════════════════════════════════════════════════════ */
function HeroSplit({ goto }) {
return (
Make a place
for what you make .
sema ikutoke is a small, opinionated builder for selling digital things —
ebooks, presets, courses, lookbooks, beats — directly to anyone with
a phone. Built around M-PESA. Loved by clean designers everywhere.
goto("signup")}>
Start a collection
goto("templates")}>
Browse templates
No card. KSh 0 / mo while you build.
);
}
function HeroCentered({ goto }) {
return (
LIVE
Now in 14 markets across East & West Africa
A quieter way to sell what you make.
sema ikutoke Collections is a small builder for digital makers. Compose a page.
Take payment via M-PESA. Get on with making things.
goto("signup")}>
Start free
goto("templates")}>
Templates
);
}
function HeroEditorial({ goto }) {
return (
Issue 14 · 2026 Nairobi
sema ikutoke Collections · A builder for digital makers
Sell what you made .
ESTABLISHED in Nairobi · Settles in 11 seconds · For writers, photographers,
designers, course creators, musicians, illustrators, podcasters & filmmakers.
A small, opinionated tool for putting your digital work online & getting
paid for it via M-PESA. No theme store. No upsells. No 30-step funnel.
goto("signup")}>
Begin
goto("templates")}>
See templates
M-PESA Daraja · STK push · Card support · USD payouts · No-code editor ·
Drag-and-drop · Cmd-K everywhere · Open transparently.
);
}
function HeroCardStack() {
return (
kamau.sema-ikutoke.so
Field Notes Vol. 03
KSh 1,200
23 photographs · 14 essays · 42 MB
★ 4.9 / 312
M-PESA
STK push sent to +254 7•• ••• 421
);
}
function HowItWorks() {
return (
§ 01 / Method
Three steps. No fuss.
Average creator ships in 38 min
{HOW_STEPS.map((s) => (
{s.n}
{s.title}
{s.body}
{s.line}
))}
);
}
function ChannelsStrip({ goto }) {
const items = [
{ mark: "WA", name: "WhatsApp", color: "#1B8B53", note: "STK push from chat" },
{ mark: "IG", name: "Instagram", color: "#E14118", note: "Shoppable posts" },
{ mark: "FB", name: "Facebook", color: "#0E2740", note: "FB Shop sync" },
{ mark: "TT", name: "TikTok", color: "#161210", note: "Live shopping" },
{ mark: "JU", name: "Jumia", color: "#F1582E", note: "Digital catalog" },
{ mark: "RI", name: "Your site", color: "#5D2BD0", note: ".sema-ikutoke.so" },
];
return (
§ 02 / Distribution
Built once. Sold everywhere.
goto("channels")} style={{cursor:"pointer", textDecoration:"underline"}}>
See all channels →
{items.map((c) => (
goto("channels")} className="ch-strip-card">
{c.mark}
))}
§ THE PROMISE
One catalog. One inbox. One M-PESA wallet. No spreadsheet.
goto("channels")}>
See how it works
);
}
function TemplatesStrip({ goto }) {
const featured = TEMPLATES.slice(0, 5);
return (
§ 03 / Collection
Pick a shell .
goto("templates")} style={{cursor:"pointer", textDecoration:"underline"}}>
See all 64 templates →
goto("builder")}>
Quiet Pages
Editor's pick
Writers · Essays · Free
Quiet Pages
By sema ikutoke Studio Free
{featured.slice(1, 5).map((t, i) => (
goto("builder")}>
{t.cat}
{t.name}
23 blocks {t.price}
))}
);
}
/* Pricing — varies by tweak */
const PLANS_TRIO = [
{ name: "Quiet", price: "0", suffix: "KSh / mo", tag: "Build at your own pace.",
features: ["1 collection", "Unlimited blocks", "M-PESA checkout (5% per sale)", "sema-ikutoke.so subdomain"], cta: "Start free" },
{ name: "Studio", price: "990", suffix: "KSh / mo", tag: "For makers who ship.", featured: true,
features: ["10 collections", "Custom domain", "M-PESA + Card (2.5% per sale)", "Email capture & list", "Analytics that's actually useful"], cta: "Start free trial" },
{ name: "Atelier", price: "2,490", suffix: "KSh / mo", tag: "For teams & agencies.",
features: ["Unlimited collections", "Team members (5)", "All payment rails (1.9%)", "API & webhooks", "Priority support"], cta: "Talk to us" },
];
const PLANS_DUO = [
{ name: "Solo", price: "0", suffix: "KSh / mo", tag: "Free forever.",
features: ["3 collections", "M-PESA checkout", "5% per sale", "sema ikutoke sub-domain", "Community support"], cta: "Start free" },
{ name: "Pro", price: "1,490", suffix: "KSh / mo", tag: "Everything, simply.", featured: true,
features: ["Unlimited collections", "Custom domain", "M-PESA + Card (1.9%)", "Team seats", "API access"], cta: "Begin trial" },
];
const PLANS_QUAD = [
{ name: "Free", price: "0", suffix: "KSh / mo", tag: "Try it.",
features: ["1 collection", "sema ikutoke sub-domain", "5% per sale"], cta: "Start free" },
{ name: "Maker", price: "490", suffix: "KSh / mo", tag: "Hobby projects.",
features: ["3 collections", "Custom subdomain", "M-PESA (3.5% per sale)"], cta: "Start" },
{ name: "Studio", price: "990", suffix: "KSh / mo", tag: "For makers.", featured: true,
features: ["10 collections", "Custom domain", "M-PESA + Card (2.5%)", "Analytics"], cta: "Begin trial" },
{ name: "Atelier", price: "2,490", suffix: "KSh / mo", tag: "Teams.",
features: ["Unlimited", "All rails (1.9%)", "API · 5 seats", "Priority"], cta: "Talk to us" },
];
function Pricing({ variant, goto }) {
const plans = variant === "duo" ? PLANS_DUO : variant === "quad" ? PLANS_QUAD : PLANS_TRIO;
return (
§ 04 / Pricing
Honest pricing.
All plans · 14-day trial · Cancel anytime
{plans.map((p) => (
{p.name}
{p.price}{p.suffix}
{p.tag}
{p.features.map((f, i) => {f} )}
goto("signup")}>{p.cta}
))}
);
}
function Footer({ goto }) {
return (
);
}
function Landing({ tweaks, goto }) {
const Hero = tweaks.hero === "centered" ? HeroCentered : tweaks.hero === "editorial" ? HeroEditorial : HeroSplit;
return (
);
}
/* ═══════════════════════════════════════════════════════════════════════
SIGNUP
═══════════════════════════════════════════════════════════════════════ */
function Signup({ goto }) {
const [form, setForm] = useState({ name: "", email: "", phone: "", code: "+254", role: "writer" });
const [stage, setStage] = useState("form");
const submit = (e) => {
e.preventDefault();
setStage("verifying");
setTimeout(() => goto("templates"), 1400);
};
return (
§ FROM THE MANIFESTO
A small, quieter way to sell what you make — on your terms.
"I switched from three other platforms. sema ikutoke lets me focus on the work,
not the funnel. STK push, sale done, 4 seconds."
— Wambui K. , photographer · Nairobi
);
}
/* ═══════════════════════════════════════════════════════════════════════
TEMPLATES
═══════════════════════════════════════════════════════════════════════ */
const CATEGORIES = ["All", "Writers", "Photographers", "Designers", "Musicians", "Course creators", "Illustrators", "Podcasters", "Filmmakers"];
function Templates({ goto }) {
const [cat, setCat] = useState("All");
const list = useMemo(() =>
cat === "All" ? TEMPLATES : TEMPLATES.filter(t => t.cat === cat),
[cat]
);
return (
§ 01 / Collection
Sixty-four shells . One that's yours.
Every template is a starting point — designed to be made yours. Strip
it back, add what's missing, push it somewhere unexpected.
{CATEGORIES.map((c) => (
setCat(c)}
>{c}
))}
{list.map((t, i) => {
const sp = i === 0 ? "tile-span-6" : (i % 5 === 2 ? "tile-span-3" : "tile-span-3");
const cover = COVER_STYLES[i % COVER_STYLES.length];
return (
goto("builder")}>
{t.name}
{t.note &&
{t.note} }
{t.cat}
{t.name}
{18 + i*2} blocks
{t.price}
);
})}
);
}
Object.assign(window, {
Brand, TopNav, Ticker, ProtoNav,
Landing, Signup, Templates,
COVER_STYLES, TEMPLATES,
});