Reframe deck for a first-time reader (Bucket 2)

Title: "One soil, three flowers" not "3-in-1" — spells out the So·lL·unar
wordmark, labels each flower with its type + place (incl. the French
farm), and shows a "future flowers" slot. Entities slide echoes the
metaphor and names the southern-France environment.

Ask: "Choose your way in" with numbered Option 1 / Option 2, framing it
as a decision rather than a menu. Roadmap: unpacks the "blueprint is the
business" riddle. Memberships: reframed as three recurring-revenue
streams with revenue-at-target per tier. Building: literal front/back
layout. P&L: adds a "Tap to expand" affordance so the breakdown is
discoverable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-08-12 20:23:59 +02:00
commit 1edfa8c9c3
7 changed files with 66 additions and 25 deletions

View file

@ -4,15 +4,20 @@ import Icon from '../components/Icon.vue'
import { brand, investorAsk, pnl, fmtUsd } from '../data' import { brand, investorAsk, pnl, fmtUsd } from '../data'
const pct = (n: number) => Math.round(n * 100) const pct = (n: number) => Math.round(n * 100)
// annual profit share per option, on the recomputed yearly profit // annual profit share per option, on the recomputed yearly profit.
const options = investorAsk.options.map((o) => ({ ...o, annual: Math.round(pnl.yearlyProfit * o.equity) })) // Number them so the ask reads as a choice ("which way in?"), not a menu of two.
const options = investorAsk.options.map((o, i) => ({
...o,
choice: `Option ${i + 1}`,
annual: Math.round(pnl.yearlyProfit * o.equity),
}))
</script> </script>
<template> <template>
<SlideFrame center> <SlideFrame center>
<p class="text-xs font-semibold uppercase tracking-[0.3em] text-amber">The ask</p> <p class="text-xs font-semibold uppercase tracking-[0.3em] text-amber">The ask</p>
<h2 class="mt-4 font-display text-4xl font-semibold text-fg sm:text-5xl">Two ways in</h2> <h2 class="mt-4 font-display text-4xl font-semibold text-fg sm:text-5xl">Choose your way in</h2>
<p class="mt-3 text-fg-muted">Invest in SolLunar Society the first node, opening in Austin.</p> <p class="mt-3 text-fg-muted">Invest in SolLunar Society the first node, opening in Austin. Pick the entry that fits.</p>
<!-- the two options --> <!-- the two options -->
<div class="mx-auto mt-9 grid w-full max-w-2xl gap-5 sm:grid-cols-2"> <div class="mx-auto mt-9 grid w-full max-w-2xl gap-5 sm:grid-cols-2">
@ -29,9 +34,10 @@ const options = investorAsk.options.map((o) => ({ ...o, annual: Math.round(pnl.y
Full build Full build
</span> </span>
<p <p
class="text-xs font-semibold uppercase tracking-wider" class="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider"
:class="o.featured ? 'text-indigo' : 'text-fg-muted'" :class="o.featured ? 'text-indigo' : 'text-fg-muted'"
> >
<span class="rounded-full border border-current px-2 py-0.5">{{ o.choice }}</span>
{{ o.label }} {{ o.label }}
</p> </p>
<div class="mt-1 flex items-baseline gap-2"> <div class="mt-1 flex items-baseline gap-2">

View file

@ -9,8 +9,8 @@ const backbone = entityById('lab484')
<template> <template>
<SlideFrame <SlideFrame
kicker="The collaboration" kicker="The collaboration"
title="Three experiences, one backbone" title="One soil, three flowers"
subtitle="One is funded and opening now — SolLunar Society in Austin. The summit gathering and the farm follow, on the same Lab 484 stack." subtitle="One flower is funded and opening now — SolLunar Society in Austin. A regenerative farm in southern France and travelling pop-up gatherings follow — the same Lab 484 stack, proven in three very different environments."
> >
<div class="grid gap-5 md:grid-cols-3"> <div class="grid gap-5 md:grid-cols-3">
<div <div

View file

@ -3,15 +3,18 @@ import SlideFrame from '../components/SlideFrame.vue'
import Icon from '../components/Icon.vue' import Icon from '../components/Icon.vue'
import { memberships, membershipComparables, fmtUsd } from '../data' import { memberships, membershipComparables, fmtUsd } from '../data'
// display low high price // display low high price; carry each tier's monthly revenue at its member target
const tiers = [...memberships].sort((a, b) => a.price - b.price) // so the slide reads as three recurring-revenue streams, not a "buy a membership" pitch
const tiers = [...memberships]
.map((t) => ({ ...t, atTarget: t.price * t.target }))
.sort((a, b) => a.price - b.price)
</script> </script>
<template> <template>
<SlideFrame <SlideFrame
kicker="SolLunar Society · memberships" kicker="SolLunar Society · recurring revenue"
title="Three ways to belong" title="Three recurring-revenue streams"
subtitle="Recurring membership is the core of the model — priced right for the Austin market." subtitle="Memberships are the model's core revenue engine — three tiers, each a predictable monthly stream. Below each tier is its revenue at target, priced against the Austin market."
> >
<div class="grid gap-5 md:grid-cols-3"> <div class="grid gap-5 md:grid-cols-3">
<div <div
@ -30,9 +33,13 @@ const tiers = [...memberships].sort((a, b) => a.price - b.price)
<h3 class="font-display text-xl font-semibold text-fg">{{ t.name }}</h3> <h3 class="font-display text-xl font-semibold text-fg">{{ t.name }}</h3>
<div class="mt-2 flex items-baseline gap-1"> <div class="mt-2 flex items-baseline gap-1">
<span class="font-display text-4xl font-bold text-fg">{{ fmtUsd(t.price) }}</span> <span class="font-display text-4xl font-bold text-fg">{{ fmtUsd(t.price) }}</span>
<span class="text-sm text-fg-muted">/mo</span> <span class="text-sm text-fg-muted">/mo · per member</span>
</div> </div>
<p class="mt-1 text-xs text-fg-muted">{{ t.blurb }} · target {{ t.target }} members</p> <p class="mt-2 font-display text-sm font-semibold text-indigo">
{{ fmtUsd(t.atTarget) }}/mo
<span class="font-sans font-normal text-fg-muted">revenue at {{ t.target }} members</span>
</p>
<p class="mt-1 text-xs text-fg-muted">{{ t.blurb }}</p>
<ul class="mt-5 flex-1 space-y-2"> <ul class="mt-5 flex-1 space-y-2">
<li v-for="p in t.perks" :key="p" class="flex items-start gap-2 text-sm text-fg-muted"> <li v-for="p in t.perks" :key="p" class="flex items-start gap-2 text-sm text-fg-muted">

View file

@ -138,6 +138,9 @@ const revPct = (id: string) =>
<span class="shrink-0 font-display text-lg font-semibold" :class="text[g.accent]"> <span class="shrink-0 font-display text-lg font-semibold" :class="text[g.accent]">
{{ fmtUsd(groupTotal(g)) }} {{ fmtUsd(groupTotal(g)) }}
</span> </span>
<span class="hidden shrink-0 text-[10px] uppercase tracking-wider text-fg-muted/70 sm:inline">
{{ selected === g.id ? 'Hide' : 'Tap to expand' }}
</span>
<Icon name="ChevronRight" class="size-4 shrink-0 text-fg-muted transition-transform" :class="selected === g.id ? 'rotate-90' : ''" /> <Icon name="ChevronRight" class="size-4 shrink-0 text-fg-muted transition-transform" :class="selected === g.id ? 'rotate-90' : ''" />
</button> </button>
</div> </div>

View file

@ -7,7 +7,7 @@ import { roadmap } from '../data'
<SlideFrame <SlideFrame
kicker="The replication play" kicker="The replication play"
title="Prove it three ways, then grow it anywhere" title="Prove it three ways, then grow it anywhere"
subtitle="The three entities aren't the endgame — they're the seed. The blueprint is the business." subtitle="The three entities aren't the endgame — they're the proof. The real business is the replicable blueprint: prove it once, then open new locations on the same backbone at marginal cost."
> >
<ol class="relative grid gap-6 md:grid-cols-4"> <ol class="relative grid gap-6 md:grid-cols-4">
<!-- connecting line --> <!-- connecting line -->

View file

@ -10,7 +10,7 @@ const icons = ['Building2', 'BedDouble', 'Cpu']
<SlideFrame <SlideFrame
kicker="SolLunar Society · the building" kicker="SolLunar Society · the building"
title="One address, three buildings" title="One address, three buildings"
subtitle="A members' society up front, Cypherpunk Commons A & B behind — lodging, coworking and the data center that runs it all." subtitle="A members' club building in front, with Cypherpunk Commons A & B in the back — lodging, coworking and the data center that runs it all."
> >
<div class="grid gap-4 md:grid-cols-3"> <div class="grid gap-4 md:grid-cols-3">
<div <div

View file

@ -5,6 +5,14 @@ import PlaceholderBadge from '../components/PlaceholderBadge.vue'
import { Alert, AlertTitle, AlertDescription } from '@/components/ui/alert' import { Alert, AlertTitle, AlertDescription } from '@/components/ui/alert'
import { brand, surfaceEntities, accentText } from '../data' import { brand, surfaceEntities, accentText } from '../data'
import logoUrl from '@/deck/assets/logo.webp' import logoUrl from '@/deck/assets/logo.webp'
// the "flowers" that grow from the Lab 484 soil type + where each one lives,
// so a first-time reader immediately places them (incl. the French farm)
const flowerMeta: Record<string, { type: string; place: string }> = {
sollunar: { type: 'City', place: 'Austin, TX' },
summit: { type: 'Pop-up', place: 'Travelling' },
chateau: { type: 'Farm', place: 'S. France' },
}
</script> </script>
<template> <template>
@ -37,19 +45,36 @@ import logoUrl from '@/deck/assets/logo.webp'
</div> </div>
</div> </div>
<p class="mt-7 max-w-2xl text-balance text-xl text-fg sm:text-2xl"> <!-- wordmark: the mark reads "So·lL·unar" spell it out so the lL ligature isn't misread -->
<p class="mt-5 font-display text-2xl font-semibold tracking-wide text-fg">
So<span class="text-amber">lL</span>unar
</p>
<p class="mt-4 max-w-2xl text-balance text-xl text-fg sm:text-2xl">
{{ brand.tagline }} {{ brand.tagline }}
</p> </p>
<!-- the three collaborating entities --> <!-- the metaphor, made explicit: Lab 484 is the soil; three flowers grow from it -->
<div class="mt-10 flex flex-wrap items-center justify-center gap-x-3 gap-y-2 text-sm text-fg-muted"> <div class="mt-10 flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-sm text-fg-muted">
<span class="uppercase tracking-[0.2em]">Three experiences, one backbone</span> <span class="uppercase tracking-[0.2em]">Lab 484 is the soil</span>
<span class="text-fg-muted/50"> three flowers grow from it </span>
</div> </div>
<div class="mt-3 flex flex-wrap items-center justify-center gap-3"> <div class="mt-4 flex flex-wrap items-center justify-center gap-3">
<template v-for="(e, i) in surfaceEntities" :key="e.id"> <span
<span class="font-display text-lg font-semibold" :class="accentText[e.accent]">{{ e.name }}</span> v-for="e in surfaceEntities"
<span v-if="i < surfaceEntities.length - 1" class="text-fg-muted">·</span> :key="e.id"
</template> class="flex flex-col items-center rounded-xl border border-line bg-surface px-4 py-2"
>
<span class="font-display text-base font-semibold" :class="accentText[e.accent]">{{ e.name }}</span>
<span class="text-[11px] uppercase tracking-wider text-fg-muted">
{{ flowerMeta[e.id].type }} · {{ flowerMeta[e.id].place }}
</span>
</span>
<!-- room to grow: future flowers -->
<span class="flex flex-col items-center rounded-xl border border-dashed border-line-2 px-4 py-2 opacity-60">
<span class="font-display text-base font-semibold text-fg-muted">+ more</span>
<span class="text-[11px] uppercase tracking-wider text-fg-muted">Future flowers</span>
</span>
</div> </div>
<p class="mt-8 max-w-xl text-balance text-sm text-fg-muted"> <p class="mt-8 max-w-xl text-balance text-sm text-fg-muted">