diff --git a/AGENTS.md b/AGENTS.md
index 586624b..65df783 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -59,6 +59,8 @@ No install needed — `open-location-code` is already available at `pcd-website/
- **Astro** owns routing, layouts, metadata, and static content pages. `src/layouts/BaseLayout.astro` provides the document shell; `MapLayout.astro`, `SiteLayout.astro`, and `DocsLayout.astro` provide the map, standard content, and Organizer Kit shells respectively.
- The Organizer Kit introduction lives in `src/content/organizer-kit/getting-started/introduction.md`; `/organize/` redirects to its canonical `/organize/getting-started/introduction/` route.
+- The Organizer Kit's `partners.md` supplies the “Discover our Partners” overview introduction. `PartnerCards.astro` renders published Organizer Kit entries with `partnerCard` frontmatter, sorted by `order` then title. Each partner detail page in `src/content/organizer-kit/partner-pages/` owns its card's `logo` (relative image path validated by Astro), `summary` (one paragraph leading with the participant or organizer benefit, followed by a short partner description); its title and route supply the card name and link. The same data supplies the overview's shared Markdown and the detail-page logo. Detail pages use `hideFromNav: true` to stay published while being omitted from both shared navigation trees and prev/next links; `draft: true` still excludes a page from build output and cards. Partner card styles live in `src/styles/prose.css`.
+- Partner detail routes pass their logo to `DocsLayout` through its optional `headingLogo` prop; the image stays inside the h1 with the page title as alt text. Its optional `backLink` prop renders the shared `BackButton` above the heading in place of the eyebrow; partner pages link back to `/organize/partners/`.
- **Vue** handles the interactive map UI as `client:only="vue"` island components. Map-specific interactive features belong in Vue; static site and Organizer Kit pages belong in Astro and Markdown content collections. The map filter drawer in `MapView.vue` filters clustered markers by date (future, past, or all), format, and activity type; checkbox choices are ORed within a group and filter groups are ANDed together.
### Event list
diff --git a/pcd-website/src/components/Footer.astro b/pcd-website/src/components/Footer.astro
index 6501a78..3310ad4 100644
--- a/pcd-website/src/components/Footer.astro
+++ b/pcd-website/src/components/Footer.astro
@@ -179,22 +179,19 @@ const UTILITY_LINKS = [
.sponsor-logo {
display: inline-flex;
align-items: center;
- padding: 0.5rem 0.625rem;
- border: 1px solid var(--color-border);
- border-radius: 4px;
- background: #fff;
+ padding: 0.5rem 0;
}
.sponsor-logo img {
display: block;
- width: 200px;
+ width: auto;
max-width: 100%;
- height: auto;
+ height: 30px;
+ object-fit: contain;
}
- .sponsor-logo:hover {
- border-color: var(--color-primary);
- color: var(--color-primary);
+ .sponsor-logo:hover img {
+ opacity: 0.8;
}
.social-row {
diff --git a/pcd-website/src/components/PartnerCards.astro b/pcd-website/src/components/PartnerCards.astro
new file mode 100644
index 0000000..448ec4c
--- /dev/null
+++ b/pcd-website/src/components/PartnerCards.astro
@@ -0,0 +1,24 @@
+---
+import { Image } from 'astro:assets';
+import { kitHref, type KitEntry } from '../config/organizer-kit-nav';
+
+interface Props {
+ partners: KitEntry[];
+}
+
+const { partners } = Astro.props;
+---
+
+
diff --git a/pcd-website/src/config/organizer-kit-nav.ts b/pcd-website/src/config/organizer-kit-nav.ts
index 67a633f..ef0a60e 100644
--- a/pcd-website/src/config/organizer-kit-nav.ts
+++ b/pcd-website/src/config/organizer-kit-nav.ts
@@ -32,6 +32,7 @@ const TOP_LEVEL: ReadonlyArray = [
{ page: 'peer-support-sessions' },
{ page: 'code-of-conduct' },
{ page: 'faq' },
+ { page: 'partners' },
{ page: 'about-processing-foundation' },
{ page: 'contact' },
{ page: 'documents' },
@@ -49,13 +50,14 @@ function toPage(entry: KitEntry): KitPage {
/**
* Builds the sidebar tree by walking TOP_LEVEL and pulling matching entries out
- * of the collection. Throws if an entry exists that TOP_LEVEL never places, so
+ * of the collection. Pages with `hideFromNav` remain published but are omitted.
+ * Throws if any other entry exists that TOP_LEVEL never places, so
* adding a Markdown file without listing it here fails the build rather than
* silently producing an unreachable page.
*/
export async function getKitNav(): Promise {
const allEntries = await getCollection('organizerKit');
- const entries = allEntries.filter((entry) => !entry.data.draft);
+ const entries = allEntries.filter((entry) => !entry.data.draft && !entry.data.hideFromNav);
const allIds = new Set(allEntries.map((entry) => entry.id));
const byId = new Map(entries.map((entry) => [entry.id, entry]));
diff --git a/pcd-website/src/config/sponsors.ts b/pcd-website/src/config/sponsors.ts
index d219cce..2fbd121 100644
--- a/pcd-website/src/config/sponsors.ts
+++ b/pcd-website/src/config/sponsors.ts
@@ -1,5 +1,6 @@
import type { ImageMetadata } from 'astro';
import openProcessingLogo from '../images/openprocessing_logo.svg';
+import butterLogo from '../images/butter_logo.svg';
export interface Sponsor {
name: string;
@@ -13,4 +14,9 @@ export const SPONSORS: Sponsor[] = [
href: 'https://openprocessing.org',
logo: openProcessingLogo,
},
+ {
+ name: 'Butter',
+ href: 'https://www.butter.video/',
+ logo: butterLogo,
+ },
];
diff --git a/pcd-website/src/content.config.ts b/pcd-website/src/content.config.ts
index a998561..17f926c 100644
--- a/pcd-website/src/content.config.ts
+++ b/pcd-website/src/content.config.ts
@@ -31,7 +31,7 @@ const pages = defineCollection({
const organizerKit = defineCollection({
loader: glob({ base: './src/content/organizer-kit', pattern: '**/*.md' }),
- schema: z.object({
+ schema: ({ image }) => z.object({
title: z.string(),
// Sidebar grouping. `section` is the parent group's title; standalone
// top-level pages omit it. `order` sorts a page within its section — the
@@ -39,9 +39,15 @@ const organizerKit = defineCollection({
section: z.string().optional(),
order: z.number().default(0),
description: z.string().optional(),
+ partnerCard: z.object({
+ logo: image(),
+ summary: z.string().min(1),
+ }).optional(),
// Excludes the page from the sidebar and from build output entirely, for
// TBD pages that shouldn't be publicly reachable yet.
draft: z.boolean().default(false),
+ // Keeps a page published while omitting it from navigation and prev/next links.
+ hideFromNav: z.boolean().default(false),
// Suppresses the "On this page" table of contents, for short pages whose
// headings aren't worth navigating.
hideToc: z.boolean().default(false),
diff --git a/pcd-website/src/content/organizer-kit/partner-pages/butter.md b/pcd-website/src/content/organizer-kit/partner-pages/butter.md
new file mode 100644
index 0000000..5114975
--- /dev/null
+++ b/pcd-website/src/content/organizer-kit/partner-pages/butter.md
@@ -0,0 +1,21 @@
+---
+title: Butter
+hideFromNav: true
+order: 1
+description: Free Butter Pro for PCD participants in October 2026
+draft: false
+hideToc: true
+partnerCard:
+ logo: ../../../images/butter_logo.svg
+ summary: Get free access to Butter Pro as a PCD participant during October 2026 and build a tool to get a chance to win a cash prize.
+---
+
+[Butter](https://www.butter.video/) is an online video editor with customizable blocks for motion graphics, animated type, and visual effects.
+
+## Butter Pro in October
+
+Butter is offering PCD participants access to Butter Pro during October 2026. Details on how to claim access will be shared here when available.
+
+## Creative Coding Challenge
+
+Details TBD
diff --git a/pcd-website/src/content/organizer-kit/partner-pages/openprocessing.md b/pcd-website/src/content/organizer-kit/partner-pages/openprocessing.md
new file mode 100644
index 0000000..3afd168
--- /dev/null
+++ b/pcd-website/src/content/organizer-kit/partner-pages/openprocessing.md
@@ -0,0 +1,23 @@
+---
+title: OpenProcessing
+hideFromNav: true
+order: 2
+description: One free month of OpenProcessing ProfessorPlus
+draft: false
+hideToc: true
+partnerCard:
+ logo: ../../../images/openprocessing_logo.svg
+ summary: Get one free month of ProfessorPlus membership for your workshop during October 2026.
+---
+
+[OpenProcessing](https://openprocessing.org/) is a creative coding platform and community for creating, sharing, and exploring sketches in the browser.
+
+## Free ProfessorPlus for your workshop
+
+Sinan from OpenProcessing is offering PCD organizers one free month of ProfessorPlus membership for their workshop during October 2026.
+
+You can use the membership to try features such as collaborative code editing in your workshop.
+
+## How to request access
+
+Contact Sinan at OpenProcessing directly to request the one-month ProfessorPlus membership for your PCD workshop.
diff --git a/pcd-website/src/content/organizer-kit/partners.md b/pcd-website/src/content/organizer-kit/partners.md
new file mode 100644
index 0000000..93d2086
--- /dev/null
+++ b/pcd-website/src/content/organizer-kit/partners.md
@@ -0,0 +1,7 @@
+---
+title: Discover our Partners
+description: Meet our partners and explore what they offer PCD organizers.
+hideToc: true
+---
+
+Meet the platforms supporting our community and discover what they offer PCD organizers.
diff --git a/pcd-website/src/images/butter_logo.svg b/pcd-website/src/images/butter_logo.svg
new file mode 100644
index 0000000..3e66d58
--- /dev/null
+++ b/pcd-website/src/images/butter_logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/pcd-website/src/layouts/DocsLayout.astro b/pcd-website/src/layouts/DocsLayout.astro
index 8c42406..153980b 100644
--- a/pcd-website/src/layouts/DocsLayout.astro
+++ b/pcd-website/src/layouts/DocsLayout.astro
@@ -1,11 +1,13 @@
---
-import type { MarkdownHeading } from 'astro';
+import type { ImageMetadata, MarkdownHeading } from 'astro';
+import { Image } from 'astro:assets';
import BaseLayout from './BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import DocsSidebar from '../components/DocsSidebar.astro';
import DocsTableOfContents from '../components/DocsTableOfContents.astro';
import DocsPageActions from '../components/DocsPageActions.astro';
+import BackButton from '../components/BackButton.astro';
import { getKitNav } from '../config/organizer-kit-nav';
import '../styles/base.css';
import '../styles/docs.css';
@@ -15,8 +17,12 @@ interface Props {
description: string;
/** Rendered above the h1 as the section label. */
eyebrow?: string;
+ /** Parent navigation shown in place of the eyebrow. */
+ backLink?: { href: string; label: string };
/** Page heading. Falls back to `title` when omitted. */
heading?: string;
+ /** Optional logo displayed within the h1, using the heading as alt text. */
+ headingLogo?: ImageMetadata;
headings?: MarkdownHeading[];
/** Suppresses the "On this page" table of contents. */
hideToc?: boolean;
@@ -26,7 +32,7 @@ interface Props {
editHref?: string;
}
-const { title, description, eyebrow, heading, headings = [], hideToc = false, markdown, editHref } = Astro.props;
+const { title, description, eyebrow, backLink, heading, headingLogo, headings = [], hideToc = false, markdown, editHref } = Astro.props;
const nav = await getKitNav();
const permalink = new URL(Astro.url.pathname, Astro.site ?? Astro.url.origin).href;
@@ -49,9 +55,13 @@ const permalink = new URL(Astro.url.pathname, Astro.site ?? Astro.url.origin).hr
- {eyebrow &&