Clarity pass: glossary tooltips + first-time-reader reframe (Buckets 1 & 2) #1
16 changed files with 353 additions and 77 deletions
|
|
@ -7,8 +7,13 @@
|
||||||
> **Brand note:** *SolLunar* is the cover brand (the venture / the raise).
|
> **Brand note:** *SolLunar* is the cover brand (the venture / the raise).
|
||||||
> *Lab 484* is the shared tech backbone (open hardware + sovereign software)
|
> *Lab 484* is the shared tech backbone (open hardware + sovereign software)
|
||||||
> **and** a 45% operating partner. *SolLunar Punk Society (SPS)* is the other
|
> **and** a 45% operating partner. *SolLunar Punk Society (SPS)* is the other
|
||||||
> 45% partner (space + community). Tagline: *One network. Three ways to
|
> 45% partner (space + community). Tagline: *One living foundation. Many
|
||||||
> experience it.* / *Energy · Intelligence · Harmony.*
|
> 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -16,13 +21,15 @@
|
||||||
|
|
||||||
**SolLunar** — Investment prospectus (draft)
|
**SolLunar** — Investment prospectus (draft)
|
||||||
|
|
||||||
**One network. Three ways to experience it.**
|
**One living foundation. Many flowers.**
|
||||||
|
|
||||||
A sovereign network on shared, open infrastructure. The first node — SolLunar
|
Lab 484 is the soil — a shared, open ecosystem of hardware + software. Three
|
||||||
Society, a members' club in Austin — is live, modeled and raising now; the
|
flowers grow from it: a members' club in Austin (live and raising now), a
|
||||||
summit gathering and the farm are next.
|
regenerative farm in southern France, and pop-up gatherings that travel. Prove
|
||||||
|
one, then replicate the blueprint anywhere.
|
||||||
|
|
||||||
- **Three experiences, one backbone:** SolLunar Society · Pop-up Summit Gatherings · Château du Faune
|
- **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).
|
||||||
- **Raising now · SolLunar Society · Austin, TX**
|
- **Raising now · SolLunar Society · Austin, TX**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -61,9 +68,12 @@ Summit Gatherings** (travelling), **Château du Faune** (farm) — plus faint
|
||||||
|
|
||||||
## 4 · Lab 484 · the shared backbone
|
## 4 · Lab 484 · the shared backbone
|
||||||
|
|
||||||
**Open hardware, sovereign software.** Lab 484 is the stack under every entity
|
**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
|
the shared hardware + software every location runs on, owned by the network and
|
||||||
place plugs in.
|
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.*
|
||||||
|
|
||||||
- **Decentralized mesh network** — Community-owned routers and mesh kits blanket each location; connectivity no single provider can switch off.
|
- **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.
|
- **Open hardware** — Servers, routers, exchange kiosks and near-field communication (NFC) hardware from a documented, replicable bill of materials.
|
||||||
|
|
@ -74,18 +84,20 @@ place plugs in.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5 · Three experiences, one backbone
|
## 5 · One soil, three flowers
|
||||||
|
|
||||||
*One is funded and opening now — SolLunar Society in Austin. The summit
|
*One flower is funded and opening now — SolLunar Society in Austin. A
|
||||||
gathering and the farm follow, on the same Lab 484 stack.*
|
regenerative farm in southern France and travelling pop-up gatherings follow —
|
||||||
|
the same Lab 484 stack, proven in three very different environments.*
|
||||||
|
|
||||||
- **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.**
|
- **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.
|
- **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 (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.
|
- **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.
|
||||||
|
|
||||||
**Beneath all three — Lab 484 · Shared backbone:** the invisible foundation every
|
**Beneath all three — Lab 484 · Shared backbone:** the invisible foundation every
|
||||||
entity runs on: open hardware and self-hosted software — the mycelium beneath the
|
entity runs on: open hardware and self-hosted software, built on decentralized
|
||||||
whole network. Build it once, and every place plugs in.
|
protocols (Nostr + Bitcoin) — the mycelium beneath the whole network. Build the
|
||||||
|
stack once, and every place plugs in.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -95,12 +107,12 @@ whole network. Build it once, and every place plugs in.
|
||||||
we're raising for.
|
we're raising for.
|
||||||
|
|
||||||
**What makes it unique**
|
**What makes it unique**
|
||||||
- Live, financially-modeled first node (real P&L)
|
- Live, financially-modeled first node — real P&L (profit & loss)
|
||||||
- Five founding tenants + a three-tier membership
|
- Five founding tenants + a three-tier membership
|
||||||
- Bitcoin terminal, vending & the full member ecosystem on site
|
- Bitcoin terminal, vending & the full member ecosystem on site
|
||||||
|
|
||||||
**Spaces:** Retail space · Event space · Private lounge · Co-working · Cypherpunk Commons hostel & data center
|
**Spaces:** Retail space · Event space · Private lounge · Co-working · Cypherpunk Commons hostel & data center
|
||||||
**How it earns:** Memberships · Tenants · Events & venue buyouts
|
**How it earns:** Memberships · Tenants · Events & venue rental
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -140,15 +152,23 @@ reason to become a member.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9 · Memberships — three ways to belong
|
## 9 · Memberships — three recurring-revenue streams
|
||||||
|
|
||||||
Recurring membership is the core of the model — priced right for the Austin market.
|
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.)*
|
||||||
|
|
||||||
| Tier | Price / mo | Target members | Includes |
|
| Tier | Price / mo | Target members | Revenue at target | Includes |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| **Solarpunk Society** | $48 | 150 | Member events; amenities (cold plunge, sauna & gym); vending & Bitcoin terminal; access to tenants |
|
| **Solarpunk Society** | $48 | 150 | ≈ $7,200/mo | 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 |
|
| **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 | Coworking with the Lab 484 team; all locations · tech support; cold plunge, sauna, gym & coworking; vending, coffee, tea & Bitcoin terminal; full tenant & amenity access |
|
| **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.
|
||||||
|
|
||||||
**Austin comparables:** Soho House $350 · Bond Collective $300 · Bathe $200 · Stardust Garage $155 · Submersive $41
|
**Austin comparables:** Soho House $350 · Bond Collective $300 · Bathe $200 · Stardust Garage $155 · Submersive $41
|
||||||
|
|
||||||
|
|
@ -156,7 +176,7 @@ Recurring membership is the core of the model — priced right for the Austin ma
|
||||||
|
|
||||||
## 10 · The building — one address, three buildings
|
## 10 · The building — one address, three buildings
|
||||||
|
|
||||||
A members' society up front, Cypherpunk Commons A & B behind — lodging, coworking
|
A members' club building in front, with Cypherpunk Commons A & B in the back — lodging, coworking
|
||||||
and the data center that runs it all. *The Cypherpunk Commons data center makes
|
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.*
|
SolLunar self-hosting — the Lab 484 stack runs on-site, not in someone else's cloud.*
|
||||||
|
|
||||||
|
|
@ -182,7 +202,7 @@ money, your identity travel with you.
|
||||||
|
|
||||||
- **One membership card** — A single NFC card is your identity, access and wallet everywhere.
|
- **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.
|
- **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, low-fee settlement.
|
- **Pay by Lightning ⚡** — Tap to pay for drinks, tickets, rooms and experiences — instant settlement, tiny fees. *(Lightning = Bitcoin's instant-settlement layer.)*
|
||||||
- **The Lab 484 app** — Book spaces, buy tickets, find events and manage membership from one self-hosted app.
|
- **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.
|
- **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.
|
- **Member perks** — Access, discounts and priority booking that follow you across the whole network.
|
||||||
|
|
@ -213,7 +233,7 @@ stated $71.4k rev / $38.5k exp, which didn't reconcile — see the caveat below.
|
||||||
**Revenue — Events ($21,900/mo)**
|
**Revenue — Events ($21,900/mo)**
|
||||||
- Lab 484 tech events — $8,000
|
- Lab 484 tech events — $8,000
|
||||||
- Lab 484 bootcamps — $6,000
|
- Lab 484 bootcamps — $6,000
|
||||||
- Venue buyouts — $6,400 ($400/hr · 4hr min · 4×/mo)
|
- Venue rental · private hire — $6,400 ($400/hr · 4hr min · 4×/mo)
|
||||||
- SolLunar Society events — $1,500 ($50 weekend passes)
|
- SolLunar Society events — $1,500 ($50 weekend passes)
|
||||||
|
|
||||||
**Revenue — Tenants ($7,000/mo)**
|
**Revenue — Tenants ($7,000/mo)**
|
||||||
|
|
@ -240,7 +260,8 @@ stated $71.4k rev / $38.5k exp, which didn't reconcile — see the caveat below.
|
||||||
## 14 · Roadmap — the replication play
|
## 14 · Roadmap — the replication play
|
||||||
|
|
||||||
**Prove it three ways, then grow it anywhere.** The three entities aren't the
|
**Prove it three ways, then grow it anywhere.** The three entities aren't the
|
||||||
endgame — they're the seed. The blueprint is the business.
|
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.
|
||||||
|
|
||||||
- **Phase 0 · Now — Raise & open SolLunar:** Close the $400k round, sign the lease, fit out the space and onboard the founding tenants.
|
- **Phase 0 · Now — Raise & open SolLunar:** Close the $400k round, sign the lease, fit out the space and onboard the founding tenants.
|
||||||
- **Phase 1 · ~0–12 mo — Operate & prove the model:** Run memberships, events and tenants in Austin; validate the P&L publicly on the Lab 484 stack.
|
- **Phase 1 · ~0–12 mo — Operate & prove the model:** Run memberships, events and tenants in Austin; validate the P&L publicly on the Lab 484 stack.
|
||||||
|
|
@ -251,14 +272,15 @@ endgame — they're the seed. The blueprint is the business.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 15 · The ask — two ways in
|
## 15 · The ask — choose your way in
|
||||||
|
|
||||||
Invest in SolLunar Society — the first node, opening in Austin.
|
Invest in SolLunar Society — the first node, opening in Austin. Pick the entry
|
||||||
|
that fits.
|
||||||
|
|
||||||
| Option | Amount | Equity | Annual profit share¹ | What it funds |
|
| Option | Amount | Equity | Annual profit share¹ | What it funds |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| **Get started** | $400k | 10% | ~$37,200 / yr | Seeds the launch — sign the lease, fit out the space and open the doors. |
|
| **Option 1 · 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. |
|
| **Option 2 · 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.
|
¹ On SolLunar Society's estimated ~$372,000/yr node profit.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import SlideFrame from './SlideFrame.vue'
|
import SlideFrame from './SlideFrame.vue'
|
||||||
import Icon from './Icon.vue'
|
import Icon from './Icon.vue'
|
||||||
import PlaceholderBadge from './PlaceholderBadge.vue'
|
import PlaceholderBadge from './PlaceholderBadge.vue'
|
||||||
|
import GlossedText from './GlossedText.vue'
|
||||||
import { accentText, accentRing, accentDot, type Entity } from '../data'
|
import { accentText, accentRing, accentDot, type Entity } from '../data'
|
||||||
|
|
||||||
const props = defineProps<{ entity: Entity }>()
|
const props = defineProps<{ entity: Entity }>()
|
||||||
|
|
@ -21,7 +22,7 @@ const a = props.entity.accent
|
||||||
<span v-if="entity.example" class="text-sm text-fg-muted">e.g. {{ entity.example }}</span>
|
<span v-if="entity.example" class="text-sm text-fg-muted">e.g. {{ entity.example }}</span>
|
||||||
<PlaceholderBadge label="name" />
|
<PlaceholderBadge label="name" />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-4 max-w-2xl text-lg text-fg-muted">{{ entity.blurb }}</p>
|
<p class="mt-4 max-w-2xl text-lg text-fg-muted"><GlossedText :text="entity.blurb" /></p>
|
||||||
|
|
||||||
<div class="mt-8 grid gap-6 lg:grid-cols-3">
|
<div class="mt-8 grid gap-6 lg:grid-cols-3">
|
||||||
<!-- unique traits -->
|
<!-- unique traits -->
|
||||||
|
|
@ -34,7 +35,7 @@ const a = props.entity.accent
|
||||||
<span class="mt-0.5 grid size-5 shrink-0 place-items-center rounded-full" :class="accentRing[a]">
|
<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]" />
|
<Icon name="Check" class="size-3" :class="accentText[a]" />
|
||||||
</span>
|
</span>
|
||||||
{{ t }}
|
<GlossedText :text="t" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -49,7 +50,7 @@ const a = props.entity.accent
|
||||||
class="flex items-center gap-2 rounded-lg bg-surface px-3 py-2 text-sm text-fg"
|
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]" />
|
<span class="size-1.5 rounded-full" :class="accentDot[a]" />
|
||||||
{{ s }}
|
<GlossedText :text="s" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -69,7 +70,7 @@ const a = props.entity.accent
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 rounded-2xl border border-line bg-surface px-4 py-3 text-xs text-fg-muted">
|
<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" />
|
<Icon name="Layers" class="size-4 text-amber" />
|
||||||
Runs on Lab 484 — same membership, kiosk & app as every node.
|
<GlossedText text="Runs on Lab 484 — same membership, kiosk & app as every node." />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
96
src/deck/components/GlossaryTerm.vue
Normal file
96
src/deck/components/GlossaryTerm.vue
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<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>
|
||||||
19
src/deck/components/GlossedText.vue
Normal file
19
src/deck/components/GlossedText.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<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>
|
||||||
|
|
@ -32,6 +32,7 @@ import {
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Images,
|
Images,
|
||||||
Expand,
|
Expand,
|
||||||
|
Server,
|
||||||
type LucideProps,
|
type LucideProps,
|
||||||
} from '@lucide/vue'
|
} from '@lucide/vue'
|
||||||
import type { FunctionalComponent } from 'vue'
|
import type { FunctionalComponent } from 'vue'
|
||||||
|
|
@ -68,6 +69,7 @@ const registry: Record<string, FunctionalComponent<LucideProps>> = {
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Images,
|
Images,
|
||||||
Expand,
|
Expand,
|
||||||
|
Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{ name: string }>()
|
const props = defineProps<{ name: string }>()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/** Shared slide chrome: consistent padding, kicker, title, subtitle + body slot. */
|
/** Shared slide chrome: consistent padding, kicker, title, subtitle + body slot. */
|
||||||
import { brandCaseParts } from '../data'
|
import { brandCaseParts } from '../data'
|
||||||
|
import GlossedText from './GlossedText.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
kicker?: string
|
kicker?: string
|
||||||
|
|
@ -32,7 +33,7 @@ defineProps<{
|
||||||
class="mt-4 max-w-2xl text-base text-fg-muted sm:text-lg"
|
class="mt-4 max-w-2xl text-base text-fg-muted sm:text-lg"
|
||||||
:class="center ? 'mx-auto' : ''"
|
:class="center ? 'mx-auto' : ''"
|
||||||
>
|
>
|
||||||
{{ subtitle }}
|
<GlossedText :text="subtitle" />
|
||||||
</p>
|
</p>
|
||||||
<div :class="[title || kicker ? 'mt-10' : '', 'w-full']">
|
<div :class="[title || kicker ? 'mt-10' : '', 'w-full']">
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
||||||
105
src/deck/data.ts
105
src/deck/data.ts
|
|
@ -24,9 +24,9 @@ export const brand = {
|
||||||
name: 'Lab 484',
|
name: 'Lab 484',
|
||||||
short: 'Lab 484',
|
short: 'Lab 484',
|
||||||
// Lab 484 — the tech co / shared backbone (also the 45% operating partner).
|
// Lab 484 — the tech co / shared backbone (also the 45% operating partner).
|
||||||
tagline: 'One network. Three ways to experience it.',
|
tagline: 'One living foundation. Many flowers.',
|
||||||
subtagline:
|
subtagline:
|
||||||
'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 — 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.',
|
||||||
firstNode: 'SolLunar Society · Austin, TX',
|
firstNode: 'SolLunar Society · Austin, TX',
|
||||||
contactEmail: 'team@l484.com',
|
contactEmail: 'team@l484.com',
|
||||||
// Seam for later: an LNbits instance + the founder's nostr npub power the
|
// Seam for later: an LNbits instance + the founder's nostr npub power the
|
||||||
|
|
@ -79,12 +79,12 @@ export const entities: Entity[] = [
|
||||||
blurb:
|
blurb:
|
||||||
'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.',
|
'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.',
|
||||||
traits: [
|
traits: [
|
||||||
'Live, financially-modeled first node (real P&L)',
|
'Live, financially-modeled first node — real P&L',
|
||||||
'Five founding tenants + a three-tier membership',
|
'Five founding tenants + a three-tier membership',
|
||||||
'Bitcoin terminal, vending & the full member ecosystem on site',
|
'Bitcoin terminal, vending & the full member ecosystem on site',
|
||||||
],
|
],
|
||||||
spaces: ['Retail space', 'Event space', 'Private lounge', 'Co-working', 'Cypherpunk Commons hostel & data center'],
|
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',
|
icon: 'Building2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -109,12 +109,12 @@ export const entities: Entity[] = [
|
||||||
{
|
{
|
||||||
id: 'chateau',
|
id: 'chateau',
|
||||||
name: 'Château du Faune',
|
name: 'Château du Faune',
|
||||||
role: 'Farm center',
|
role: 'Farm center · southern France',
|
||||||
kind: 'Off-grid · regenerative farm',
|
kind: 'Off-grid regenerative farm · southern France',
|
||||||
accent: 'clay',
|
accent: 'clay',
|
||||||
layer: 'surface',
|
layer: 'surface',
|
||||||
blurb:
|
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: [
|
traits: [
|
||||||
'Proves the stack runs off-grid, anywhere',
|
'Proves the stack runs off-grid, anywhere',
|
||||||
'Premium revenue from retreats & residencies',
|
'Premium revenue from retreats & residencies',
|
||||||
|
|
@ -132,7 +132,7 @@ export const entities: Entity[] = [
|
||||||
accent: 'pine',
|
accent: 'pine',
|
||||||
layer: 'backbone',
|
layer: 'backbone',
|
||||||
blurb:
|
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: [
|
traits: [
|
||||||
'Powers all three entities from one shared stack',
|
'Powers all three entities from one shared stack',
|
||||||
'Open bill of materials — replicable anywhere',
|
'Open bill of materials — replicable anywhere',
|
||||||
|
|
@ -164,8 +164,8 @@ export const userEcosystem = [
|
||||||
icon: 'Coins',
|
icon: 'Coins',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Pay by Lightning',
|
title: 'Pay by Lightning ⚡',
|
||||||
body: 'Tap to pay for drinks, tickets, rooms and experiences — instant, low-fee settlement.',
|
body: 'Tap to pay for drinks, tickets, rooms and experiences — instant settlement, tiny fees.',
|
||||||
icon: 'Zap',
|
icon: 'Zap',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -411,7 +411,7 @@ export const pnlGroups: PnlGroup[] = [
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Lab 484 tech events', amount: 8_000 },
|
{ label: 'Lab 484 tech events', amount: 8_000 },
|
||||||
{ label: 'Lab 484 bootcamps', amount: 6_000 },
|
{ label: 'Lab 484 bootcamps', amount: 6_000 },
|
||||||
{ label: 'Venue buyouts', amount: 6_400, note: '$400/hr · 4hr min · 4×/mo' },
|
{ 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' },
|
{ label: 'SolLunar Society events', amount: 1_500, note: '$50 weekend passes' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -552,6 +552,89 @@ export const brandCaseParts = (text: string) =>
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((t, i) => ({ id: i, text: t, keep: t === 'SolLunar' }))
|
.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.com' },
|
||||||
|
},
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
'p&l': {
|
||||||
|
term: 'P&L',
|
||||||
|
short:
|
||||||
|
'Profit & loss — a business’s 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. */
|
/** Tailwind class helpers keyed by entity accent — keeps slides declarative. */
|
||||||
export const accentText: Record<Accent, string> = {
|
export const accentText: Record<Accent, string> = {
|
||||||
pine: 'text-pine-300',
|
pine: 'text-pine-300',
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SlideFrame from '../components/SlideFrame.vue'
|
import SlideFrame from '../components/SlideFrame.vue'
|
||||||
import Icon from '../components/Icon.vue'
|
import Icon from '../components/Icon.vue'
|
||||||
|
import GlossedText from '../components/GlossedText.vue'
|
||||||
import { userEcosystem, surfaceEntities as entities } from '../data'
|
import { userEcosystem, surfaceEntities as entities } from '../data'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ import { userEcosystem, surfaceEntities as entities } from '../data'
|
||||||
<Icon :name="item.icon" class="size-5" />
|
<Icon :name="item.icon" class="size-5" />
|
||||||
</span>
|
</span>
|
||||||
<h3 class="mt-4 font-display text-lg font-semibold text-fg">{{ item.title }}</h3>
|
<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">{{ item.body }}</p>
|
<p class="mt-1.5 text-sm leading-relaxed text-fg-muted"><GlossedText :text="item.body" /></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 -->
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SlideFrame from '../components/SlideFrame.vue'
|
import SlideFrame from '../components/SlideFrame.vue'
|
||||||
import Icon from '../components/Icon.vue'
|
import Icon from '../components/Icon.vue'
|
||||||
|
import GlossedText from '../components/GlossedText.vue'
|
||||||
import { tech } from '../data'
|
import { tech } from '../data'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -8,7 +9,7 @@ import { tech } from '../data'
|
||||||
<SlideFrame
|
<SlideFrame
|
||||||
kicker="Lab 484 · the shared backbone"
|
kicker="Lab 484 · the shared backbone"
|
||||||
title="Open hardware, sovereign software"
|
title="Open hardware, sovereign software"
|
||||||
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."
|
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."
|
||||||
>
|
>
|
||||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
<div
|
<div
|
||||||
|
|
@ -20,8 +21,17 @@ import { tech } from '../data'
|
||||||
<Icon :name="t.icon" class="size-5" />
|
<Icon :name="t.icon" class="size-5" />
|
||||||
</span>
|
</span>
|
||||||
<h3 class="mt-4 font-display text-lg font-semibold text-fg">{{ t.title }}</h3>
|
<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">{{ t.body }}</p>
|
<p class="mt-1.5 text-sm leading-relaxed text-fg-muted"><GlossedText :text="t.body" /></p>
|
||||||
</div>
|
</div>
|
||||||
</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 network’s own hardware, not in someone else’s cloud.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</SlideFrame>
|
</SlideFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue