Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
29f0854
docs: forbid AI attribution in commits, PRs and files
CodeWithEugene Sep 28, 2026
85e8d9a
feat(ui): animate the hero ribbon and run it behind the header
CodeWithEugene Sep 28, 2026
9823cad
feat(events): redesign the hackathons listing as a searchable directory
CodeWithEugene Sep 28, 2026
33ea076
feat(events): lay out the hackathon page like a customer story
CodeWithEugene Sep 28, 2026
ec1ebb7
feat(events): scroll the listing hero's cover wall in alternate direc…
CodeWithEugene Sep 28, 2026
72e0c2c
feat(events): keep the directory filters in place while results scroll
CodeWithEugene Sep 28, 2026
22f87d9
feat(events): open the Status and Category filters by default
CodeWithEugene Sep 28, 2026
e849d90
refactor(ui): drop the dashboard card, photo banner and navy band fro…
CodeWithEugene Sep 28, 2026
dcdf2c7
refactor(ui): trim the audience sections and closing CTA from the hom…
CodeWithEugene Sep 28, 2026
3c78a26
style(ui): tighten the gap above the trust band on the home page
CodeWithEugene Sep 28, 2026
07c2897
fix(ui): give the 0% trust stat a natural fan with an outward ripple
CodeWithEugene Sep 28, 2026
dc03bee
style(ui): tighten the gap between the escrow quote and the blog section
CodeWithEugene Sep 28, 2026
49d9d86
feat(ui): list five recent posts beside the featured one on the home …
CodeWithEugene Sep 28, 2026
a87ef28
style(ui): size the home page product cards to their content
CodeWithEugene Sep 28, 2026
dbd58e7
refactor(ui): drop the open-source and first-hackathon promo cards fr…
CodeWithEugene Sep 28, 2026
8cb02a8
feat(events): fill the space under the listing's verify heading with …
CodeWithEugene Sep 28, 2026
eb3c65f
feat(ui): restyle the document pages on the ruled frame
CodeWithEugene Sep 28, 2026
49b9569
feat(ui): restyle How It Works on the ruled frame
CodeWithEugene Sep 28, 2026
7e8b9e2
feat(ui): put the header in the ruled frame on every page and restyle…
CodeWithEugene Sep 28, 2026
fa79283
feat(ui): restyle the public ledger page on the ruled frame
CodeWithEugene Sep 28, 2026
eed811d
feat(ui): restyle developer profiles on the ruled frame
CodeWithEugene Sep 28, 2026
6922d55
feat(ui): restyle organizer profiles on the ruled frame
CodeWithEugene Sep 28, 2026
898f54d
feat(ui): put the unsubscribe pages and the 404 page in the ruled frame
CodeWithEugene Sep 28, 2026
21a4208
style(ui): let document text fill the column beside the contents rail
CodeWithEugene Sep 28, 2026
d85eb5f
style(ui): centre the title block on document pages
CodeWithEugene Sep 28, 2026
a97de9d
style(ui): let the ruled frame grow on large monitors
CodeWithEugene Sep 28, 2026
bbada93
style(ui): let headings keep growing on large monitors
CodeWithEugene Sep 28, 2026
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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ docs/
- Branches: `main` (protected, production), `dev` (integration), `feature/`, `fix/`
- Never commit directly to `main`; always PR
- Never commit `.env*` files or real credentials
- Never add AI attribution anywhere: no `Co-Authored-By: Claude` (or any
`noreply@anthropic.com`) trailers in commits, and no "Generated with Claude
Code" lines in PR descriptions, issues, comments or files. This overrides
any tool default that suggests them.

## Package Manager
- This project uses **pnpm** exclusively — never `npm` or `yarn`
Expand Down
52 changes: 25 additions & 27 deletions app/(marketing)/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ArrowLeft } from "lucide-react";

import { KeepReading } from "@/components/patterns/keep-reading";
import { LegalToc } from "@/components/patterns/legal-toc";
import { PageHero } from "@/components/patterns/page-hero";
import { JsonLd } from "@/components/seo/json-ld";
import { allPosts, formatPostDate, getPost, morePosts } from "@/lib/blog";
import { articleSchema, breadcrumbSchema } from "@/lib/seo/schema";
Expand Down Expand Up @@ -66,41 +67,38 @@ export default async function BlogPostPage({ params }: PageProps) {
]),
]}
/>
<article className="site-container py-12">
<Link
href="/blog"
className="inline-flex items-center gap-1.5 text-sm font-semibold text-ink-soft hover:text-ink"
<article className="lp">
<PageHero
kicker={meta.category}
bar={
<Link href="/blog" className="blog-back">
<ArrowLeft aria-hidden className="size-4" /> All Posts
</Link>
}
title={meta.title}
lead={meta.excerpt}
>
<ArrowLeft aria-hidden className="size-4" /> All Posts
</Link>

<header className="mx-auto mt-6 max-w-3xl text-center">
<p className="text-[11px] font-semibold tracking-[0.18em] text-ink-soft uppercase">
{meta.category}
</p>
<h1 className="mt-3 font-display text-3xl leading-tight font-bold text-ink sm:text-4xl">
{meta.title}
</h1>
<p className="mt-3 text-lg text-muted">{meta.excerpt}</p>
<p className="mt-4 text-sm text-muted">
<p className="blog-byline">
By {meta.author} ·{" "}
<time dateTime={meta.publishedAt}>{formatPostDate(meta.publishedAt)}</time> ·{" "}
{meta.readingMinutes} min read
</p>
</header>
</PageHero>

<div className="relative mt-10 aspect-[21/9] overflow-hidden rounded-card bg-brand/10 shadow-card">
<Image
src={meta.cover}
alt={meta.coverAlt}
fill
priority
sizes="100vw"
className="object-cover"
/>
<div className="lp-frame lp-divided blog-cover-frame">
<div className="blog-cover">
<Image
src={meta.cover}
alt={meta.coverAlt}
fill
priority
sizes="(min-width: 1440px) 1392px, 100vw"
className="object-cover"
/>
</div>
</div>

<div className="legal-layout mt-12">
<div className="lp-frame legal-layout">
<aside className="legal-aside">
<LegalToc containerId={CONTENT_ID} />
</aside>
Expand Down
42 changes: 21 additions & 21 deletions app/(marketing)/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Metadata } from "next";

import { BlogCard } from "@/components/patterns/blog-card";
import { PageHero } from "@/components/patterns/page-hero";
import { Pagination } from "@/components/patterns/pagination";
import { JsonLd } from "@/components/seo/json-ld";
import { allPosts } from "@/lib/blog";
Expand All @@ -14,7 +15,10 @@ export async function generateMetadata({ searchParams }: PageProps): Promise<Met
const page = parsePage((await searchParams).page, count);
return {
// Later pages say so in the title, so they aren't duplicates of page 1.
title: page === 1 ? "Blog: Hackathon Guides And Stories" : `Blog: Hackathon Guides And Stories, Page ${page}`,
title:
page === 1
? "Blog: Hackathon Guides And Stories"
: `Blog: Hackathon Guides And Stories, Page ${page}`,
description:
"Guides and stories from HackVillage: how escrowed prizes work, how winners get paid instantly, and how to run hackathons builders trust in Kenya and beyond.",
// Self-canonical per pagination page; page 1 canonicalizes to the clean URL.
Expand Down Expand Up @@ -44,26 +48,22 @@ export default async function BlogPage({ searchParams }: PageProps) {
visible.map(({ meta }) => ({ title: meta.title, path: `/blog/${meta.slug}` })),
)}
/>
<div className="site-container py-16">
<header className="mb-10 text-center">
<p className="text-[11px] font-semibold tracking-[0.18em] text-ink-soft uppercase">
Blog
</p>
<h1 className="mt-3 font-display text-3xl font-bold text-ink sm:text-4xl">
Stories And Guides From HackVillage
</h1>
<p className="mx-auto mt-3 max-w-xl text-muted">
How prizes stay safe, how winners get paid, and how to run and win hackathons in Kenya
and beyond.
</p>
</header>

<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
{visible.map((post) => (
<BlogCard key={post.meta.slug} post={post.meta} />
))}
</div>
<Pagination page={page} pageCount={count} label="Blog pages" hrefFor={blogPageHref} />
<div className="lp">
<PageHero
kicker="Blog"
title="Stories And Guides From HackVillage"
lead="How prizes stay safe, how winners get paid, and how to run and win hackathons in Kenya and beyond."
/>
<section className="lp-section" aria-label="Posts">
<div className="lp-frame lp-block lp-divided">
<div className="blog-grid">
{visible.map((post) => (
<BlogCard key={post.meta.slug} post={post.meta} />
))}
</div>
<Pagination page={page} pageCount={count} label="Blog pages" hrefFor={blogPageHref} />
</div>
</section>
</div>
</>
);
Expand Down
Loading
Loading