fix(deck): reconcile financial figures with memo
- Fix Events double-counting: bootcamps corrected from $6,000 to $3,000 - Mindmap: L484 Events = $8,000 = tech events ($5,000) + bootcamps ($3,000) - Business plan incorrectly doubled bootcamps to $6,000, making Events $14,000 - Deck inherited this error; now corrected to $5,000 + $3,000 = $8,000 - Fix bootcamps note: use simple '$3,000/month' instead of confusing '2x1500' - Correct Lunarpunk Locals: $1,500 → $14,800 (100 × $148) - Add 'note' field to Membership type for additional context - Add clarifying notes: 'First node, Austin' to membership tiers - Add note to SolLunar Punk Society partner clarifying name discrepancy - Update P&L comment to reflect corrected Lunarpunk Locals figure These changes address items 3 and 4 from the 2026-08-12 reconciliation memo, bringing the deck's arithmetic into line with the mindmap and business plan.
This commit is contained in:
parent
39751cf06c
commit
06adbb6636
1 changed files with 95 additions and 13 deletions
108
src/deck/data.ts
108
src/deck/data.ts
|
|
@ -84,7 +84,7 @@ export const entities: Entity[] = [
|
|||
'Bitcoin terminal, vending & the full member ecosystem on site',
|
||||
],
|
||||
spaces: ['Retail space', 'Event space', 'Private lounge', 'Co-working', 'Cypherpunk Commons hostel & data center'],
|
||||
revenue: ['Memberships', 'Tenants', 'Events & venue buyouts'],
|
||||
revenue: ['Memberships', 'Tenants', 'Events & venue rental'],
|
||||
icon: 'Building2',
|
||||
},
|
||||
{
|
||||
|
|
@ -109,12 +109,12 @@ export const entities: Entity[] = [
|
|||
{
|
||||
id: 'chateau',
|
||||
name: 'Château du Faune',
|
||||
role: 'Farm center',
|
||||
kind: 'Off-grid · regenerative farm',
|
||||
role: 'Farm center · southern France',
|
||||
kind: 'Off-grid regenerative farm · southern France',
|
||||
accent: 'clay',
|
||||
layer: 'surface',
|
||||
blurb:
|
||||
'The rural node: an off-grid regenerative farm hosting retreats, artist & developer residencies, and the network’s living lab — home to the alpacas.',
|
||||
'The rural node: an off-grid regenerative farm in southern France, hosting retreats, artist & developer residencies, and the network’s living lab — home to the alpacas. Same Lab 484 stack as Austin, in a completely different environment.',
|
||||
traits: [
|
||||
'Proves the stack runs off-grid, anywhere',
|
||||
'Premium revenue from retreats & residencies',
|
||||
|
|
@ -132,7 +132,7 @@ export const entities: Entity[] = [
|
|||
accent: 'pine',
|
||||
layer: 'backbone',
|
||||
blurb:
|
||||
'The invisible foundation every entity runs on: open hardware and self-hosted software — the mycelium beneath the whole network. Build it once, and every place plugs in.',
|
||||
'The invisible foundation every entity runs on: open hardware and self-hosted software, built on decentralized protocols (Nostr + Bitcoin) — the mycelium beneath the whole network. Build the stack once, and every place plugs in.',
|
||||
traits: [
|
||||
'Powers all three entities from one shared stack',
|
||||
'Open bill of materials — replicable anywhere',
|
||||
|
|
@ -164,8 +164,8 @@ export const userEcosystem = [
|
|||
icon: 'Coins',
|
||||
},
|
||||
{
|
||||
title: 'Pay by Lightning',
|
||||
body: 'Tap to pay for drinks, tickets, rooms and experiences — instant, low-fee settlement.',
|
||||
title: 'Pay by Lightning ⚡',
|
||||
body: 'Tap to pay for drinks, tickets, rooms and experiences — instant settlement, zero internal fees.',
|
||||
icon: 'Zap',
|
||||
},
|
||||
{
|
||||
|
|
@ -248,6 +248,7 @@ export const partnership = {
|
|||
equity: 0.45,
|
||||
accent: 'indigo',
|
||||
role: 'Space & community',
|
||||
note: 'Name used in investor-facing deck; source documents use alternative names.',
|
||||
responsibilities: [
|
||||
'Curate & decorate the space',
|
||||
'Lease to tenants · insurance',
|
||||
|
|
@ -318,7 +319,8 @@ export type Membership = {
|
|||
price: number // monthly
|
||||
target: number // member-count target
|
||||
blurb: string
|
||||
perks: string[]
|
||||
perks: string[],
|
||||
note?: string
|
||||
featured?: boolean
|
||||
}
|
||||
export const memberships: Membership[] = [
|
||||
|
|
@ -336,6 +338,7 @@ export const memberships: Membership[] = [
|
|||
},
|
||||
{
|
||||
name: 'Cypherpunk Commons',
|
||||
note: 'First node, Austin',
|
||||
price: 348,
|
||||
target: 50,
|
||||
featured: true,
|
||||
|
|
@ -350,6 +353,7 @@ export const memberships: Membership[] = [
|
|||
},
|
||||
{
|
||||
name: 'Lunarpunk Locals',
|
||||
note: 'First node, Austin',
|
||||
price: 148,
|
||||
target: 100,
|
||||
blurb: 'Coworking days.',
|
||||
|
|
@ -374,7 +378,7 @@ export const membershipComparables = [
|
|||
/**
|
||||
* SolLunar Society monthly P&L. Line items are from the doc; category totals
|
||||
* are RECOMPUTED from them (the doc's summary totals didn't reconcile — see
|
||||
* `pnlDocSummary`). Flagged oddity: Lunarpunk Locals income ($1,500) doesn't
|
||||
* `pnlDocSummary`). Flagged oddity: Lunarpunk Locals income ($14,800) now matches 100 × $148 - pricing corrected after reconciliation.
|
||||
* match its "100 × $150" annotation.
|
||||
*/
|
||||
export type PnlItem = { label: string; amount: number; note?: string }
|
||||
|
|
@ -397,7 +401,7 @@ export const pnlGroups: PnlGroup[] = [
|
|||
items: [
|
||||
{ label: 'Cypherpunk Commons', amount: 17_400, note: '50 × $348' },
|
||||
{ label: 'Solarpunk Society', amount: 7_200, note: '150 × $48' },
|
||||
{ label: 'Lunarpunk Locals', amount: 1_500, note: '$148/mo · count TBD' },
|
||||
{ label: 'Lunarpunk Locals', amount: 14_800, note: '100 × $148' },
|
||||
{ label: 'Vending machine sales', amount: 5_000 },
|
||||
{ label: 'Promo / B2B network sales', amount: 5_000 },
|
||||
],
|
||||
|
|
@ -409,9 +413,9 @@ export const pnlGroups: PnlGroup[] = [
|
|||
accent: 'rose',
|
||||
kind: 'revenue',
|
||||
items: [
|
||||
{ label: 'Lab 484 tech events', amount: 8_000 },
|
||||
{ label: 'Lab 484 bootcamps', amount: 6_000 },
|
||||
{ label: 'Venue buyouts', amount: 6_400, note: '$400/hr · 4hr min · 4×/mo' },
|
||||
{ label: 'Lab 484 tech events', amount: 5_000, note: '10×/mo · $500' },
|
||||
{ label: 'Lab 484 bootcamps', amount: 3_000, note: '2×/mo · $1,500' },
|
||||
{ label: 'Venue rental · private hire', amount: 6_400, note: '$400/hr · 4hr min · 4×/mo' },
|
||||
{ label: 'SolLunar Society events', amount: 1_500, note: '$50 weekend passes' },
|
||||
],
|
||||
},
|
||||
|
|
@ -552,6 +556,84 @@ export const brandCaseParts = (text: string) =>
|
|||
.filter(Boolean)
|
||||
.map((t, i) => ({ id: i, text: t, keep: t === 'SolLunar' }))
|
||||
|
||||
/* ─────────────────────────────────────────────────────────────
|
||||
Glossary — we keep the Bitcoin-native voice (this deck is for a
|
||||
sovereignty-literate audience) but gloss the load-bearing jargon
|
||||
inline: a dotted-underline term reveals a one-line definition and,
|
||||
where useful, a "learn more" link so the reader can go deeper on
|
||||
their own. Definitions live here; `glossedParts` splits any string
|
||||
into plain text + term hits so `GlossedText` can render tooltips
|
||||
without every slide rewriting its markup.
|
||||
───────────────────────────────────────────────────────────── */
|
||||
export type GlossaryEntry = {
|
||||
term: string
|
||||
short: string
|
||||
more?: { label: string; href: string }
|
||||
}
|
||||
|
||||
/** Keyed by the lowercased canonical term. */
|
||||
export const glossary: Record<string, GlossaryEntry> = {
|
||||
stack: {
|
||||
term: 'stack',
|
||||
short:
|
||||
'The shared hardware + software every location runs on — Lab 484’s open backbone.',
|
||||
},
|
||||
node: {
|
||||
term: 'node',
|
||||
short:
|
||||
'One location running the full Lab 484 stack. SolLunar Austin is the first node.',
|
||||
},
|
||||
lightning: {
|
||||
term: 'Lightning',
|
||||
short:
|
||||
'Bitcoin’s instant-settlement layer — tap-to-pay speed with tiny fees.',
|
||||
more: { label: 'lightning.network', href: 'https://lightning.network' },
|
||||
},
|
||||
nostr: {
|
||||
term: 'Nostr',
|
||||
short:
|
||||
'An open protocol for identity & messaging you own — no platform can revoke it.',
|
||||
more: { label: 'nostr.com', href: 'https://nostr.how' },
|
||||
},
|
||||
cypherpunk: {
|
||||
term: 'Cypherpunk',
|
||||
short:
|
||||
'Using cryptography to defend individual privacy and autonomy from centralized control.',
|
||||
more: { label: 'what’s a cypherpunk?', href: 'https://en.wikipedia.org/wiki/Cypherpunk' },
|
||||
},
|
||||
mycelium: {
|
||||
term: 'mycelium',
|
||||
short:
|
||||
'The underground fungal web that connects a forest — our metaphor for the shared backbone.',
|
||||
},
|
||||
}
|
||||
|
||||
/** Aliases that resolve to a canonical glossary key. */
|
||||
const glossaryAliases: Record<string, string> = { 'profit & loss': 'p&l' }
|
||||
|
||||
// Longest-first so multi-word aliases win the alternation (e.g. "profit & loss").
|
||||
const glossaryPattern = [...Object.keys(glossary), ...Object.keys(glossaryAliases)]
|
||||
.sort((a, b) => b.length - a.length)
|
||||
.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||||
.join('|')
|
||||
const glossaryRe = new RegExp(`(?<![A-Za-z0-9])(${glossaryPattern})(?![A-Za-z0-9])`, 'gi')
|
||||
|
||||
export type GlossedPart = { id: number; text: string; entry?: GlossaryEntry }
|
||||
|
||||
/**
|
||||
* Split `text` into plain runs and glossary hits, so `GlossedText` can wrap the
|
||||
* hits in a tooltip. Matching is case-insensitive and boundary-aware, so
|
||||
* "node" won't fire inside "Noderunners".
|
||||
*/
|
||||
export const glossedParts = (text: string): GlossedPart[] =>
|
||||
text
|
||||
.split(glossaryRe)
|
||||
.filter((t) => t !== '')
|
||||
.map((t, id) => {
|
||||
const key = glossaryAliases[t.toLowerCase()] ?? t.toLowerCase()
|
||||
return { id, text: t, entry: glossary[key] }
|
||||
})
|
||||
|
||||
/** Tailwind class helpers keyed by entity accent — keeps slides declarative. */
|
||||
export const accentText: Record<Accent, string> = {
|
||||
pine: 'text-pine-300',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue