Compare commits

..

No commits in common. "feedback-clarity-pass" and "main" have entirely different histories.

16 changed files with 77 additions and 353 deletions

View file

@ -7,13 +7,8 @@
> **Brand note:** *SolLunar* is the cover brand (the venture / the raise).
> *Lab 484* is the shared tech backbone (open hardware + sovereign software)
> **and** a 45% operating partner. *SolLunar Punk Society (SPS)* is the other
> 45% partner (space + community). Tagline: *One living foundation. Many
> flowers.* / *Energy · Intelligence · Harmony.*
>
> **Core metaphor:** Lab 484 is the *soil* — a shared hardware + software
> ecosystem (built on decentralized protocols: Nostr + Bitcoin). Three *flowers*
> grow from it: **City** (SolLunar, Austin), **Farm** (Château du Faune, southern
> France) and **Pop-up** (travelling summit gatherings) — with room for more.
> 45% partner (space + community). Tagline: *One network. Three ways to
> experience it.* / *Energy · Intelligence · Harmony.*
---
@ -21,15 +16,13 @@
**SolLunar** — Investment prospectus (draft)
**One living foundation. Many flowers.**
**One network. Three ways to experience it.**
Lab 484 is the soil — a shared, open ecosystem of hardware + software. Three
flowers grow from it: a members' club in Austin (live and raising now), a
regenerative farm in southern France, and pop-up gatherings that travel. Prove
one, then replicate the blueprint anywhere.
A sovereign network on shared, open infrastructure. The first node — SolLunar
Society, a members' club in Austin — is live, modeled and raising now; the
summit gathering and the farm are next.
- **Lab 484 is the soil — three flowers grow from it:** SolLunar Society (City · Austin) · Pop-up Summit Gatherings (Travelling) · Château du Faune (Farm · S. France) · + future flowers
- **Wordmark:** the mark is *So·lL·unar* (the lL ligature, not a numeral).
- **Three experiences, one backbone:** SolLunar Society · Pop-up Summit Gatherings · Château du Faune
- **Raising now · SolLunar Society · Austin, TX**
---
@ -68,12 +61,9 @@ Summit Gatherings** (travelling), **Château du Faune** (farm) — plus faint
## 4 · Lab 484 · the shared backbone
**Open hardware, sovereign software.** Lab 484 is the stack under every entity —
the shared hardware + software every location runs on, owned by the network and
documented to be rebuilt anywhere. Build the stack once; every place plugs in.
*Every node self-hosts — the stack runs on-site, on the network's own hardware,
not in someone else's cloud.*
**Open hardware, sovereign software.** Lab 484 is the stack under every entity
— owned by the network, documented to be rebuilt anywhere. Build it once; every
place plugs in.
- **Decentralized mesh network** — Community-owned routers and mesh kits blanket each location; connectivity no single provider can switch off.
- **Open hardware** — Servers, routers, exchange kiosks and near-field communication (NFC) hardware from a documented, replicable bill of materials.
@ -84,20 +74,18 @@ not in someone else's cloud.*
---
## 5 · One soil, three flowers
## 5 · Three experiences, one backbone
*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.*
*One is funded and opening now — SolLunar Society in Austin. The summit
gathering and the farm follow, on the same Lab 484 stack.*
- **SolLunar Society** — City center · the first node (Members' club & community hub · Austin, TX). *The first node and the one we're raising for: a members' club in Austin where sovereignty-minded community — tenants, events, memberships and regenerative living — comes together year-round on the Lab 484 stack.* — **Live P&L · this raise.**
- **Pop-up Summit Gatherings** — Travelling gatherings (e.g. Solar Punk Summit). *A repeatable summit-gathering format that deploys the entire stack anywhere for a few days, then packs down and moves on.* — Vision · on the roadmap.
- **Château du Faune** — Farm center · southern France (off-grid regenerative farm). *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.* — Vision · on the roadmap.
- **Château du Faune** — Farm center (off-grid · regenerative farm). *An off-grid regenerative farm hosting retreats, artist & developer residencies, and the network's living lab — home to the alpacas.* — Vision · on the roadmap.
**Beneath all three — Lab 484 · Shared backbone:** 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.
entity runs on: open hardware and self-hosted software — the mycelium beneath the
whole network. Build it once, and every place plugs in.
---
@ -107,12 +95,12 @@ stack once, and every place plugs in.
we're raising for.
**What makes it unique**
- Live, financially-modeled first node — real P&L (profit & loss)
- Live, financially-modeled first node (real P&L)
- Five founding tenants + a three-tier membership
- Bitcoin terminal, vending & the full member ecosystem on site
**Spaces:** Retail space · Event space · Private lounge · Co-working · Cypherpunk Commons hostel & data center
**How it earns:** Memberships · Tenants · Events & venue rental
**How it earns:** Memberships · Tenants · Events & venue buyouts
---
@ -152,23 +140,15 @@ reason to become a member.
---
## 9 · Memberships — three recurring-revenue streams
## 9 · Memberships — three ways to belong
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. *(This is a revenue view for investors, not a
membership sales pitch.)*
Recurring membership is the core of the model — priced right for the Austin market.
| Tier | Price / mo | Target members | Revenue at target | Includes |
| --- | --- | --- | --- | --- |
| **Solarpunk Society** | $48 | 150 | ≈ $7,200/mo | Member events; amenities (cold plunge, sauna & gym); vending & Bitcoin terminal; access to tenants |
| **Lunarpunk Locals** | $148 | 100 | ≈ $14,800/mo | Coworking days at SolLunar; amenities (cold plunge, sauna & gym); vending & Bitcoin terminal; access to tenants |
| **Cypherpunk Commons** *(flagship)* | $348 | 50 | ≈ $17,400/mo | Coworking with the Lab 484 team; all locations · tech support; cold plunge, sauna, gym & coworking; vending, coffee, tea & Bitcoin terminal; full tenant & amenity access |
> ⚠️ **Reconcile before sending:** the P&L (slide 13) conservatively models
> Lunarpunk Locals at only **$1,500/mo** ("count TBD"), not the $14,800/mo its
> 100-member target implies. Decide the real Lunarpunk target and align both
> slides.
| Tier | Price / mo | Target members | Includes |
| --- | --- | --- | --- |
| **Solarpunk Society** | $48 | 150 | Member events; amenities (cold plunge, sauna & gym); vending & Bitcoin terminal; access to tenants |
| **Lunarpunk Locals** | $148 | 100 | Coworking days at SolLunar; amenities (cold plunge, sauna & gym); vending & Bitcoin terminal; access to tenants |
| **Cypherpunk Commons** *(flagship)* | $348 | 50 | Coworking with the Lab 484 team; all locations · tech support; cold plunge, sauna, gym & coworking; vending, coffee, tea & Bitcoin terminal; full tenant & amenity access |
**Austin comparables:** Soho House $350 · Bond Collective $300 · Bathe $200 · Stardust Garage $155 · Submersive $41
@ -176,7 +156,7 @@ membership sales pitch.)*
## 10 · The building — one address, three buildings
A members' club building in front, with Cypherpunk Commons A & B in the back — lodging, coworking
A members' society up front, Cypherpunk Commons A & B behind — lodging, coworking
and the data center that runs it all. *The Cypherpunk Commons data center makes
SolLunar self-hosting — the Lab 484 stack runs on-site, not in someone else's cloud.*
@ -202,7 +182,7 @@ money, your identity travel with you.
- **One membership card** — A single NFC card is your identity, access and wallet everywhere.
- **Money exchange kiosk** — On-ramp cash to Bitcoin and back at any location. The same kiosk, everywhere.
- **Pay by Lightning** — Tap to pay for drinks, tickets, rooms and experiences — instant settlement, tiny fees. *(Lightning = Bitcoin's instant-settlement layer.)*
- **Pay by Lightning** — Tap to pay for drinks, tickets, rooms and experiences — instant, low-fee settlement.
- **The Lab 484 app** — Book spaces, buy tickets, find events and manage membership from one self-hosted app.
- **Portable identity** — Your nostr identity and social graph travel with you between every node and beyond.
- **Member perks** — Access, discounts and priority booking that follow you across the whole network.
@ -233,7 +213,7 @@ stated $71.4k rev / $38.5k exp, which didn't reconcile — see the caveat below.
**Revenue — Events ($21,900/mo)**
- Lab 484 tech events — $8,000
- Lab 484 bootcamps — $6,000
- Venue rental · private hire — $6,400 ($400/hr · 4hr min · 4×/mo)
- Venue buyouts — $6,400 ($400/hr · 4hr min · 4×/mo)
- SolLunar Society events — $1,500 ($50 weekend passes)
**Revenue — Tenants ($7,000/mo)**
@ -260,8 +240,7 @@ stated $71.4k rev / $38.5k exp, which didn't reconcile — see the caveat below.
## 14 · Roadmap — the replication play
**Prove it three ways, then grow it anywhere.** 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.
endgame — they're the seed. The blueprint is the business.
- **Phase 0 · Now — Raise & open SolLunar:** Close the $400k round, sign the lease, fit out the space and onboard the founding tenants.
- **Phase 1 · ~012 mo — Operate & prove the model:** Run memberships, events and tenants in Austin; validate the P&L publicly on the Lab 484 stack.
@ -272,15 +251,14 @@ prove it once, then open new locations on the same backbone at marginal cost.
---
## 15 · The ask — choose your way in
## 15 · The ask — two ways in
Invest in SolLunar Society — the first node, opening in Austin. Pick the entry
that fits.
Invest in SolLunar Society — the first node, opening in Austin.
| Option | Amount | Equity | Annual profit share¹ | What it funds |
| --- | --- | --- | --- | --- |
| **Option 1 · Get started** | $400k | 10% | ~$37,200 / yr | Seeds the launch — sign the lease, fit out the space and open the doors. |
| **Option 2 · Full execution** *(full build)* | $900k | 25% | ~$93,000 / yr | Funds the full build-out and the first operating year end to end. |
| **Get started** | $400k | 10% | ~$37,200 / yr | Seeds the launch — sign the lease, fit out the space and open the doors. |
| **Full execution** *(full build)* | $900k | 25% | ~$93,000 / yr | Funds the full build-out and the first operating year end to end. |
¹ On SolLunar Society's estimated ~$372,000/yr node profit.

View file

@ -2,7 +2,6 @@
import SlideFrame from './SlideFrame.vue'
import Icon from './Icon.vue'
import PlaceholderBadge from './PlaceholderBadge.vue'
import GlossedText from './GlossedText.vue'
import { accentText, accentRing, accentDot, type Entity } from '../data'
const props = defineProps<{ entity: Entity }>()
@ -22,7 +21,7 @@ const a = props.entity.accent
<span v-if="entity.example" class="text-sm text-fg-muted">e.g. {{ entity.example }}</span>
<PlaceholderBadge label="name" />
</div>
<p class="mt-4 max-w-2xl text-lg text-fg-muted"><GlossedText :text="entity.blurb" /></p>
<p class="mt-4 max-w-2xl text-lg text-fg-muted">{{ entity.blurb }}</p>
<div class="mt-8 grid gap-6 lg:grid-cols-3">
<!-- unique traits -->
@ -35,7 +34,7 @@ const a = props.entity.accent
<span class="mt-0.5 grid size-5 shrink-0 place-items-center rounded-full" :class="accentRing[a]">
<Icon name="Check" class="size-3" :class="accentText[a]" />
</span>
<GlossedText :text="t" />
{{ t }}
</li>
</ul>
</div>
@ -50,7 +49,7 @@ const a = props.entity.accent
class="flex items-center gap-2 rounded-lg bg-surface px-3 py-2 text-sm text-fg"
>
<span class="size-1.5 rounded-full" :class="accentDot[a]" />
<GlossedText :text="s" />
{{ s }}
</div>
</div>
</div>
@ -70,7 +69,7 @@ const a = props.entity.accent
</div>
<div class="flex items-center gap-2 rounded-2xl border border-line bg-surface px-4 py-3 text-xs text-fg-muted">
<Icon name="Layers" class="size-4 text-amber" />
<GlossedText text="Runs on Lab 484 — same membership, kiosk & app as every node." />
Runs on Lab 484 same membership, kiosk &amp; app as every node.
</div>
</div>
</div>

View file

@ -1,96 +0,0 @@
<script setup lang="ts">
/**
* An inline glossary term: dotted-underline trigger that reveals a one-line
* definition (and an optional "learn more" link) on hover or tap.
*
* Deliberately dependency-free the deck stage uses transforms + overflow, so
* a portaled popover would escape the themed `.dark` tree and lose its tokens.
* We render the card inline instead and let it float just below the term.
*/
import { ref, onBeforeUnmount } from 'vue'
import { ExternalLink } from '@lucide/vue'
import type { GlossaryEntry } from '../data'
defineProps<{ entry: GlossaryEntry }>()
const open = ref(false)
const root = ref<HTMLElement | null>(null)
let hoverTimer: ReturnType<typeof setTimeout> | undefined
function show() {
clearTimeout(hoverTimer)
open.value = true
}
function hideSoon() {
// small grace period so the reader can travel from term to card
hoverTimer = setTimeout(() => (open.value = false), 120)
}
function toggle() {
open.value = !open.value
}
function onKey(e: KeyboardEvent) {
if (e.key === 'Escape') open.value = false
}
// close when tapping elsewhere (mobile has no hover-out)
function onDocPointer(e: PointerEvent) {
if (open.value && root.value && !root.value.contains(e.target as Node)) open.value = false
}
if (typeof document !== 'undefined') document.addEventListener('pointerdown', onDocPointer)
onBeforeUnmount(() => {
clearTimeout(hoverTimer)
if (typeof document !== 'undefined') document.removeEventListener('pointerdown', onDocPointer)
})
</script>
<template>
<span ref="root" class="relative inline-block" @mouseenter="show" @mouseleave="hideSoon">
<button
type="button"
class="cursor-help font-medium text-fg underline decoration-amber-400/70 decoration-dotted underline-offset-4 transition hover:decoration-amber-400"
:aria-expanded="open"
@click="toggle"
@keydown="onKey"
>
<slot>{{ entry.term }}</slot>
</button>
<Transition name="gloss">
<span
v-if="open"
class="absolute left-1/2 top-full z-30 mt-2 block w-64 -translate-x-1/2 cursor-auto rounded-xl border border-line-2 bg-surface-2 p-3 text-left text-sm font-normal normal-case leading-relaxed text-fg-muted shadow-xl"
@mouseenter="show"
@mouseleave="hideSoon"
>
<span class="mb-1 block font-display text-xs font-semibold uppercase tracking-wider text-amber">
{{ entry.term }}
</span>
{{ entry.short }}
<a
v-if="entry.more"
:href="entry.more.href"
target="_blank"
rel="noopener noreferrer"
class="mt-2 inline-flex items-center gap-1 text-xs font-medium text-indigo transition hover:text-indigo-300"
>
{{ entry.more.label }}
<ExternalLink class="size-3" />
</a>
</span>
</Transition>
</span>
</template>
<style scoped>
.gloss-enter-active,
.gloss-leave-active {
transition:
opacity 0.15s ease,
transform 0.15s ease;
}
.gloss-enter-from,
.gloss-leave-to {
opacity: 0;
transform: translate(-50%, -4px);
}
</style>

View file

@ -1,19 +0,0 @@
<script setup lang="ts">
/**
* Renders a string with any glossary terms turned into inline tooltips.
* Drop-in for plain interpolation: `<GlossedText :text="subtitle" />`.
*/
import GlossaryTerm from './GlossaryTerm.vue'
import { glossedParts } from '../data'
defineProps<{ text: string }>()
</script>
<template>
<span
><template v-for="p in glossedParts(text)" :key="p.id"
><GlossaryTerm v-if="p.entry" :entry="p.entry">{{ p.text }}</GlossaryTerm
><template v-else>{{ p.text }}</template></template
></span
>
</template>

View file

@ -32,7 +32,6 @@ import {
ChevronRight,
Images,
Expand,
Server,
type LucideProps,
} from '@lucide/vue'
import type { FunctionalComponent } from 'vue'
@ -69,7 +68,6 @@ const registry: Record<string, FunctionalComponent<LucideProps>> = {
ChevronRight,
Images,
Expand,
Server,
}
const props = defineProps<{ name: string }>()

View file

@ -1,7 +1,6 @@
<script setup lang="ts">
/** Shared slide chrome: consistent padding, kicker, title, subtitle + body slot. */
import { brandCaseParts } from '../data'
import GlossedText from './GlossedText.vue'
defineProps<{
kicker?: string
@ -33,7 +32,7 @@ defineProps<{
class="mt-4 max-w-2xl text-base text-fg-muted sm:text-lg"
:class="center ? 'mx-auto' : ''"
>
<GlossedText :text="subtitle" />
{{ subtitle }}
</p>
<div :class="[title || kicker ? 'mt-10' : '', 'w-full']">
<slot />

View file

@ -24,9 +24,9 @@ export const brand = {
name: 'Lab 484',
short: 'Lab 484',
// Lab 484 — the tech co / shared backbone (also the 45% operating partner).
tagline: 'One living foundation. Many flowers.',
tagline: 'One network. Three ways to experience it.',
subtagline:
'Lab 484 is the soil — a shared, open ecosystem of hardware + software. Three flowers grow from it: a members club in Austin (live and raising now), a regenerative farm in southern France, and pop-up gatherings that travel. Prove one, then replicate the blueprint anywhere.',
'A sovereign network on shared, open infrastructure. The first node — SolLunar Society, a members club in Austin — is live, modeled and raising now; the summit gathering and the farm are next.',
firstNode: 'SolLunar Society · Austin, TX',
contactEmail: 'team@l484.com',
// Seam for later: an LNbits instance + the founder's nostr npub power the
@ -79,12 +79,12 @@ export const entities: Entity[] = [
blurb:
'The first node and the one were raising for: a members club in Austin where sovereignty-minded community — tenants, events, memberships and regenerative living — comes together year-round on the Lab 484 stack.',
traits: [
'Live, financially-modeled first node — real P&L',
'Live, financially-modeled first node (real P&L)',
'Five founding tenants + a three-tier membership',
'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 rental'],
revenue: ['Memberships', 'Tenants', 'Events & venue buyouts'],
icon: 'Building2',
},
{
@ -109,12 +109,12 @@ export const entities: Entity[] = [
{
id: 'chateau',
name: 'Château du Faune',
role: 'Farm center · southern France',
kind: 'Off-grid regenerative farm · southern France',
role: 'Farm center',
kind: 'Off-grid · regenerative farm',
accent: 'clay',
layer: 'surface',
blurb:
'The rural node: an off-grid regenerative farm in southern France, hosting retreats, artist & developer residencies, and the networks living lab — home to the alpacas. Same Lab 484 stack as Austin, in a completely different environment.',
'The rural node: an off-grid regenerative farm hosting retreats, artist & developer residencies, and the networks living lab — home to the alpacas.',
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, built on decentralized protocols (Nostr + Bitcoin) — the mycelium beneath the whole network. Build the stack once, and every place plugs in.',
'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.',
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 settlement, tiny fees.',
title: 'Pay by Lightning',
body: 'Tap to pay for drinks, tickets, rooms and experiences — instant, low-fee settlement.',
icon: 'Zap',
},
{
@ -411,7 +411,7 @@ export const pnlGroups: PnlGroup[] = [
items: [
{ label: 'Lab 484 tech events', amount: 8_000 },
{ label: 'Lab 484 bootcamps', amount: 6_000 },
{ label: 'Venue rental · private hire', amount: 6_400, note: '$400/hr · 4hr min · 4×/mo' },
{ label: 'Venue buyouts', amount: 6_400, note: '$400/hr · 4hr min · 4×/mo' },
{ label: 'SolLunar Society events', amount: 1_500, note: '$50 weekend passes' },
],
},
@ -552,89 +552,6 @@ 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 484s open backbone.',
},
node: {
term: 'node',
short:
'One location running the full Lab 484 stack. SolLunar Austin is the first node.',
},
lightning: {
term: 'Lightning',
short:
'Bitcoins 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.com' },
},
cypherpunk: {
term: 'Cypherpunk',
short:
'Using cryptography to defend individual privacy and autonomy from centralized control.',
more: { label: 'whats 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.',
},
'p&l': {
term: 'P&L',
short:
'Profit & loss — a businesss real operating income measured against its expenses.',
},
}
/** 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',

View file

@ -4,20 +4,15 @@ import Icon from '../components/Icon.vue'
import { brand, investorAsk, pnl, fmtUsd } from '../data'
const pct = (n: number) => Math.round(n * 100)
// annual profit share per option, on the recomputed yearly profit.
// 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),
}))
// annual profit share per option, on the recomputed yearly profit
const options = investorAsk.options.map((o) => ({ ...o, annual: Math.round(pnl.yearlyProfit * o.equity) }))
</script>
<template>
<SlideFrame center>
<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">Choose your way in</h2>
<p class="mt-3 text-fg-muted">Invest in SolLunar Society the first node, opening in Austin. Pick the entry that fits.</p>
<h2 class="mt-4 font-display text-4xl font-semibold text-fg sm:text-5xl">Two ways in</h2>
<p class="mt-3 text-fg-muted">Invest in SolLunar Society the first node, opening in Austin.</p>
<!-- the two options -->
<div class="mx-auto mt-9 grid w-full max-w-2xl gap-5 sm:grid-cols-2">
@ -34,10 +29,9 @@ const options = investorAsk.options.map((o, i) => ({
Full build
</span>
<p
class="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider"
class="text-xs font-semibold uppercase tracking-wider"
: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 }}
</p>
<div class="mt-1 flex items-baseline gap-2">

View file

@ -1,7 +1,6 @@
<script setup lang="ts">
import SlideFrame from '../components/SlideFrame.vue'
import Icon from '../components/Icon.vue'
import GlossedText from '../components/GlossedText.vue'
import { userEcosystem, surfaceEntities as entities } from '../data'
</script>
@ -21,7 +20,7 @@ import { userEcosystem, surfaceEntities as entities } from '../data'
<Icon :name="item.icon" class="size-5" />
</span>
<h3 class="mt-4 font-display text-lg font-semibold text-fg">{{ item.title }}</h3>
<p class="mt-1.5 text-sm leading-relaxed text-fg-muted"><GlossedText :text="item.body" /></p>
<p class="mt-1.5 text-sm leading-relaxed text-fg-muted">{{ item.body }}</p>
</div>
</div>

View file

@ -9,8 +9,8 @@ const backbone = entityById('lab484')
<template>
<SlideFrame
kicker="The collaboration"
title="One soil, three flowers"
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."
title="Three experiences, one backbone"
subtitle="One is funded and opening now — SolLunar Society in Austin. The summit gathering and the farm follow, on the same Lab 484 stack."
>
<div class="grid gap-5 md:grid-cols-3">
<div

View file

@ -3,18 +3,15 @@ import SlideFrame from '../components/SlideFrame.vue'
import Icon from '../components/Icon.vue'
import { memberships, membershipComparables, fmtUsd } from '../data'
// display low high price; carry each tier's monthly revenue at its member target
// 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)
// display low high price
const tiers = [...memberships].sort((a, b) => a.price - b.price)
</script>
<template>
<SlideFrame
kicker="SolLunar Society · recurring revenue"
title="Three recurring-revenue streams"
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."
kicker="SolLunar Society · memberships"
title="Three ways to belong"
subtitle="Recurring membership is the core of the model — priced right for the Austin market."
>
<div class="grid gap-5 md:grid-cols-3">
<div
@ -33,13 +30,9 @@ const tiers = [...memberships]
<h3 class="font-display text-xl font-semibold text-fg">{{ t.name }}</h3>
<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="text-sm text-fg-muted">/mo · per member</span>
<span class="text-sm text-fg-muted">/mo</span>
</div>
<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>
<p class="mt-1 text-xs text-fg-muted">{{ t.blurb }} · target {{ t.target }} members</p>
<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">

View file

@ -138,9 +138,6 @@ const revPct = (id: string) =>
<span class="shrink-0 font-display text-lg font-semibold" :class="text[g.accent]">
{{ fmtUsd(groupTotal(g)) }}
</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' : ''" />
</button>
</div>

View file

@ -7,7 +7,7 @@ import { roadmap } from '../data'
<SlideFrame
kicker="The replication play"
title="Prove it three ways, then grow it anywhere"
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."
subtitle="The three entities aren't the endgame — they're the seed. The blueprint is the business."
>
<ol class="relative grid gap-6 md:grid-cols-4">
<!-- connecting line -->

View file

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

View file

@ -1,7 +1,6 @@
<script setup lang="ts">
import SlideFrame from '../components/SlideFrame.vue'
import Icon from '../components/Icon.vue'
import GlossedText from '../components/GlossedText.vue'
import { tech } from '../data'
</script>
@ -9,7 +8,7 @@ import { tech } from '../data'
<SlideFrame
kicker="Lab 484 · the shared backbone"
title="Open hardware, sovereign software"
subtitle="Lab 484 is the stack under every entity — the shared hardware + software every location runs on, owned by the network and documented to be rebuilt anywhere. Build the stack once; every place plugs in."
subtitle="Lab 484 is the stack under every entity — owned by the network, documented to be rebuilt anywhere. Build it once; every place plugs in."
>
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div
@ -21,17 +20,8 @@ import { tech } from '../data'
<Icon :name="t.icon" class="size-5" />
</span>
<h3 class="mt-4 font-display text-lg font-semibold text-fg">{{ t.title }}</h3>
<p class="mt-1.5 text-sm leading-relaxed text-fg-muted"><GlossedText :text="t.body" /></p>
<p class="mt-1.5 text-sm leading-relaxed text-fg-muted">{{ t.body }}</p>
</div>
</div>
<!-- self-hosting: surfaced here (reviewer found this the clearest idea, buried on the building slide) -->
<div class="mt-5 flex items-start gap-3 rounded-2xl border border-pine-500/20 bg-pine-500/5 p-5 text-sm text-fg">
<Icon name="Server" class="mt-0.5 size-5 shrink-0 text-pine" />
<span>
Every node <span class="font-semibold text-pine">self-hosts</span> the stack runs on-site,
on the networks own hardware, not in someone elses cloud.
</span>
</div>
</SlideFrame>
</template>

View file

@ -5,14 +5,6 @@ import PlaceholderBadge from '../components/PlaceholderBadge.vue'
import { Alert, AlertTitle, AlertDescription } from '@/components/ui/alert'
import { brand, surfaceEntities, accentText } from '../data'
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>
<template>
@ -45,36 +37,19 @@ const flowerMeta: Record<string, { type: string; place: string }> = {
</div>
</div>
<!-- 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">
<p class="mt-7 max-w-2xl text-balance text-xl text-fg sm:text-2xl">
{{ brand.tagline }}
</p>
<!-- 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-2 gap-y-1 text-sm text-fg-muted">
<span class="uppercase tracking-[0.2em]">Lab 484 is the soil</span>
<span class="text-fg-muted/50"> three flowers grow from it </span>
<!-- the three collaborating entities -->
<div class="mt-10 flex flex-wrap items-center justify-center gap-x-3 gap-y-2 text-sm text-fg-muted">
<span class="uppercase tracking-[0.2em]">Three experiences, one backbone</span>
</div>
<div class="mt-4 flex flex-wrap items-center justify-center gap-3">
<span
v-for="e in surfaceEntities"
:key="e.id"
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 class="mt-3 flex flex-wrap items-center justify-center gap-3">
<template v-for="(e, i) in surfaceEntities" :key="e.id">
<span class="font-display text-lg font-semibold" :class="accentText[e.accent]">{{ e.name }}</span>
<span v-if="i < surfaceEntities.length - 1" class="text-fg-muted">·</span>
</template>
</div>
<p class="mt-8 max-w-xl text-balance text-sm text-fg-muted">