Fix #58: Individual talk detail pages (#78)
All checks were successful
Build and Deploy to Storj / build-and-deploy (push) Successful in 10s

Closes #58.

- Created /speaking/50-first-dates.astro with full details
- Created /speaking/strip-mining-to-cultivation.astro with details
- Updated TalkCard component to support slug prop with Learn more link
- Updated speaking page to pass slugs
- Added Umami tracking to talk CTAs

Reviewed-on: https://os.scoble.me/forgejo/forgejo/sscoble/brand-site/pulls/78
Reviewed-by: sscoble <sscoble@users.noreply.git>
Co-authored-by: Engineering (agent-engineering) <engineering@marcus.local>
Co-committed-by: Engineering (agent-engineering) <engineering@marcus.local>
This commit is contained in:
engineering Agent 2026-05-05 14:23:59 -07:00 • committed by sscoble
parent 1084105571
commit ac7c2c675e
4 changed files with 433 additions and 1 deletions

View file

@ -8,7 +8,7 @@ export interface Props {
slug?: string;
}
const { title, premise, takeaways, audience } = Astro.props;
const { title, premise, takeaways, audience, slug } = Astro.props;
---
<article class="talk-card card">
@ -27,6 +27,10 @@ const { title, premise, takeaways, audience } = Astro.props;
<div class="talk-audience">
<span class="talk-label">Ideal Audience:</span> {audience}
</div>
{slug && (
<a href={slug} class="talk-link">Learn more →</a>
)}
</article>
<style>
@ -79,4 +83,16 @@ const { title, premise, takeaways, audience } = Astro.props;
font-weight: 600;
color: var(--color-brown);
}
.talk-link {
display: inline-block;
margin-top: var(--space-md);
color: var(--color-green);
text-decoration: none;
font-weight: 500;
}
.talk-link:hover {
text-decoration: underline;
}
</style>

View file

@ -43,6 +43,7 @@ import SharedForm from '../components/Form/SharedForm.astro';
<TalkCard
title="50 First Dates with AI"
slug="/speaking/50-first-dates"
premise="Current AI memory architecture is anterograde amnesia — and we're all Henry Roth, recording VHS tapes every night so our agent can wake up functional tomorrow."
idealFor="Engineering leaders, AI product teams, CTOs implementing agentic systems"
takeaways={[
@ -54,6 +55,7 @@ import SharedForm from '../components/Form/SharedForm.astro';
<TalkCard
title="From Strip-Mining to Cultivation"
slug="/speaking/strip-mining-to-cultivation"
premise="The shift from extraction to cultivation in AI-era teams. Why throughput optimization is killing your soil health."
idealFor="CTOs, VP Engineering, people leaders, engineering managers"
takeaways={[

View file

@ -0,0 +1,207 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
const talk = {
title: "50 First Dates with AI",
premise: "Current AI memory architecture is anterograde amnesia — and we're all Henry Roth, recording VHS tapes every night so our agent can wake up functional tomorrow.",
duration: "45–60 minutes",
type: "Keynote",
idealFor: ["Engineering leaders", "AI product teams", "CTOs implementing agentic systems"],
takeaways: [
"Why your AI's 'memory' is actually just replay, not learning",
"The three-layer memory architecture that actually compounds",
"How to move from VHS tapes to persistent, evolving agents"
],
description: `Every morning, your AI wakes up and doesn't remember yesterday.
We built agentic systems with anterograde amnesia — they can't form new long-term memories. Instead, we record VHS tapes (context windows) every night and hope the agent can reconstruct enough context to be functional.
This isn't a technical limitation. It's an architectural choice we keep making because it's easier than building systems that actually learn.
In this keynote, I show why the "50 First Dates" problem is the single biggest blocker to truly agentic AI — and how to build systems that compound instead of reset.`
};
---
<BaseLayout
title={`${talk.title} — Scott Scoble`}
description={talk.premise}
pageType="speaking"
>
<article class="talk-detail">
<header class="talk-header">
<div class="talk-meta">
<span class="talk-type">{talk.type}</span>
<span class="talk-duration">{talk.duration}</span>
</div>
<h1>{talk.title}</h1>
<p class="talk-premise">{talk.premise}</p>
</header>
<div class="talk-content">
<section class="talk-description">
<h2>The Talk</h2>
{talk.description.split('\n\n').map(para => (
<p>{para}</p>
))}
</section>
<section class="talk-takeaways">
<h2>What You'll Learn</h2>
<ul>
{talk.takeaways.map(takeaway => (
<li>{takeaway}</li>
))}
</ul>
</section>
<section class="talk-audience">
<h2>Ideal For</h2>
<div class="audience-tags">
{talk.idealFor.map(audience => (
<span class="audience-tag">{audience}</span>
))}
</div>
</section>
</div>
<footer class="talk-cta">
<a href="/speaking#book" class="btn-primary" data-umami-event="talk-cta-book-50-first-dates">
Book This Talk
</a>
<a href="/speaking" class="btn-outline">← All Talks</a>
</footer>
</article>
</BaseLayout>
<style>
.talk-detail {
max-width: 800px;
margin: 0 auto;
padding: 4rem 2rem;
}
.talk-header {
text-align: center;
margin-bottom: 3rem;
}
.talk-meta {
display: flex;
gap: 1rem;
justify-content: center;
margin-bottom: 1.5rem;
}
.talk-type {
background: var(--color-green);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.talk-duration {
color: var(--color-text-muted);
font-size: 0.875rem;
}
.talk-header h1 {
font-family: var(--font-heading);
font-size: clamp(1.75rem, 4vw, 2.5rem);
color: var(--color-brown-dark);
margin: 0 0 1rem;
}
.talk-premise {
font-size: 1.125rem;
color: var(--color-text-muted);
font-style: italic;
max-width: 600px;
margin: 0 auto;
}
.talk-content section {
margin-bottom: 2.5rem;
}
.talk-content h2 {
font-family: var(--font-heading);
font-size: 1.25rem;
color: var(--color-brown-dark);
margin: 0 0 1rem;
}
.talk-description p {
color: var(--color-text);
line-height: 1.7;
margin: 0 0 1rem;
}
.talk-takeaways ul {
list-style: none;
padding: 0;
}
.talk-takeaways li {
padding: 0.75rem 0;
border-bottom: 1px solid var(--color-border);
position: relative;
padding-left: 1.5rem;
}
.talk-takeaways li:before {
content: "✓";
position: absolute;
left: 0;
color: var(--color-green);
font-weight: bold;
}
.audience-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.audience-tag {
background: var(--color-cream);
border: 1px solid var(--color-border);
padding: 0.5rem 1rem;
border-radius: 100px;
font-size: 0.875rem;
color: var(--color-text);
}
.talk-cta {
display: flex;
gap: 1rem;
justify-content: center;
padding-top: 2rem;
border-top: 1px solid var(--color-border);
}
.btn-primary {
display: inline-block;
background: var(--color-amber);
color: white;
padding: 0.75rem 1.5rem;
border-radius: 6px;
text-decoration: none;
font-weight: 600;
}
.btn-outline {
display: inline-block;
background: transparent;
color: var(--color-text);
padding: 0.75rem 1.5rem;
border-radius: 6px;
text-decoration: none;
border: 1px solid var(--color-border);
}
</style>

View file

@ -0,0 +1,207 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
const talk = {
title: "From Strip-Mining to Cultivation",
premise: "The shift from extraction to cultivation in AI-era teams. Why throughput optimization is killing your soil health.",
duration: "45–60 minutes",
type: "Keynote",
idealFor: ["CTOs", "VP Engineering", "People leaders", "Engineering managers"],
takeaways: [
"The difference between strip-mining and cultivation mindsets",
"How to audit your current practices for extraction vs. growth",
"A cultivation playbook you can implement this quarter"
],
description: `The most successful AI teams aren't the ones with the best models. They're the ones who stopped strip-mining their humans and started cultivating them.
For a decade, we've optimized engineering teams like strip-mining operations: extract maximum value, move fast, burn bright. It worked when the work was predictable and the humans were replaceable.
AI changes the equation. The work is no longer predictable — it's conversational, contextual, and constantly evolving. You can't strip-mine your way through agentic development. You need soil that gets richer over time.
This keynote shows the cultivation playbook: how to build teams where AI augments human capability instead of replacing it, where throughput is a byproduct of health, and where your best people don't just stay — they compound.`
};
---
<BaseLayout
title={`${talk.title} — Scott Scoble`}
description={talk.premise}
pageType="speaking"
>
<article class="talk-detail">
<header class="talk-header">
<div class="talk-meta">
<span class="talk-type">{talk.type}</span>
<span class="talk-duration">{talk.duration}</span>
</div>
<h1>{talk.title}</h1>
<p class="talk-premise">{talk.premise}</p>
</header>
<div class="talk-content">
<section class="talk-description">
<h2>The Talk</h2>
{talk.description.split('\n\n').map(para => (
<p>{para}</p>
))}
</section>
<section class="talk-takeaways">
<h2>What You'll Learn</h2>
<ul>
{talk.takeaways.map(takeaway => (
<li>{takeaway}</li>
))}
</ul>
</section>
<section class="talk-audience">
<h2>Ideal For</h2>
<div class="audience-tags">
{talk.idealFor.map(audience => (
<span class="audience-tag">{audience}</span>
))}
</div>
</section>
</div>
<footer class="talk-cta">
<a href="/speaking#book" class="btn-primary" data-umami-event="talk-cta-book-cultivation">
Book This Talk
</a>
<a href="/speaking" class="btn-outline">← All Talks</a>
</footer>
</article>
</BaseLayout>
<style>
.talk-detail {
max-width: 800px;
margin: 0 auto;
padding: 4rem 2rem;
}
.talk-header {
text-align: center;
margin-bottom: 3rem;
}
.talk-meta {
display: flex;
gap: 1rem;
justify-content: center;
margin-bottom: 1.5rem;
}
.talk-type {
background: var(--color-green);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.talk-duration {
color: var(--color-text-muted);
font-size: 0.875rem;
}
.talk-header h1 {
font-family: var(--font-heading);
font-size: clamp(1.75rem, 4vw, 2.5rem);
color: var(--color-brown-dark);
margin: 0 0 1rem;
}
.talk-premise {
font-size: 1.125rem;
color: var(--color-text-muted);
font-style: italic;
max-width: 600px;
margin: 0 auto;
}
.talk-content section {
margin-bottom: 2.5rem;
}
.talk-content h2 {
font-family: var(--font-heading);
font-size: 1.25rem;
color: var(--color-brown-dark);
margin: 0 0 1rem;
}
.talk-description p {
color: var(--color-text);
line-height: 1.7;
margin: 0 0 1rem;
}
.talk-takeaways ul {
list-style: none;
padding: 0;
}
.talk-takeaways li {
padding: 0.75rem 0;
border-bottom: 1px solid var(--color-border);
position: relative;
padding-left: 1.5rem;
}
.talk-takeaways li:before {
content: "✓";
position: absolute;
left: 0;
color: var(--color-green);
font-weight: bold;
}
.audience-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.audience-tag {
background: var(--color-cream);
border: 1px solid var(--color-border);
padding: 0.5rem 1rem;
border-radius: 100px;
font-size: 0.875rem;
color: var(--color-text);
}
.talk-cta {
display: flex;
gap: 1rem;
justify-content: center;
padding-top: 2rem;
border-top: 1px solid var(--color-border);
}
.btn-primary {
display: inline-block;
background: var(--color-amber);
color: white;
padding: 0.75rem 1.5rem;
border-radius: 6px;
text-decoration: none;
font-weight: 600;
}
.btn-outline {
display: inline-block;
background: transparent;
color: var(--color-text);
padding: 0.75rem 1.5rem;
border-radius: 6px;
text-decoration: none;
border: 1px solid var(--color-border);
}
</style>