diff --git a/index.html b/index.html index edd4762..381d0bd 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Boilerplate Website + Archipelago — Investment Prospectus
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..dbc8714 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +# Approve dependencies allowed to run install scripts (pnpm 11). +# vue-demi's postinstall selects its Vue 2/3 entrypoint; without this, +# `pnpm dev` / `pnpm build` fail the pre-run ignored-builds check. +allowBuilds: + vue-demi: true diff --git a/src/deck/Deck.vue b/src/deck/Deck.vue new file mode 100644 index 0000000..bd36b45 --- /dev/null +++ b/src/deck/Deck.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/deck/components/EntityDetail.vue b/src/deck/components/EntityDetail.vue new file mode 100644 index 0000000..746afc4 --- /dev/null +++ b/src/deck/components/EntityDetail.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/deck/components/Icon.vue b/src/deck/components/Icon.vue new file mode 100644 index 0000000..e46ad45 --- /dev/null +++ b/src/deck/components/Icon.vue @@ -0,0 +1,78 @@ + + + diff --git a/src/deck/components/Lightbox.vue b/src/deck/components/Lightbox.vue new file mode 100644 index 0000000..ce78a9e --- /dev/null +++ b/src/deck/components/Lightbox.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/src/deck/components/MyceliumNetwork.vue b/src/deck/components/MyceliumNetwork.vue new file mode 100644 index 0000000..b78771d --- /dev/null +++ b/src/deck/components/MyceliumNetwork.vue @@ -0,0 +1,408 @@ + + + + + diff --git a/src/deck/components/PlaceholderBadge.vue b/src/deck/components/PlaceholderBadge.vue new file mode 100644 index 0000000..49f1a42 --- /dev/null +++ b/src/deck/components/PlaceholderBadge.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/deck/components/SlideFrame.vue b/src/deck/components/SlideFrame.vue new file mode 100644 index 0000000..5816b0d --- /dev/null +++ b/src/deck/components/SlideFrame.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/deck/data.ts b/src/deck/data.ts new file mode 100644 index 0000000..24c3645 --- /dev/null +++ b/src/deck/data.ts @@ -0,0 +1,530 @@ +/** + * ARCHIPELAGO — pitch deck content & figures. + * + * Single source of truth for the deck — no slide hard-codes a number or name. + * + * The financials are REAL, from SolLunarSociety.md, and belong exclusively to + * the first node — SolLunar Society (Austin). Archipelago (Lab 484), Pop-up + * Festivals and Chateau du Faune are narrative/vision only and have no + * standalone numbers. P&L totals are recomputed from line items; the doc's + * own summary totals didn't reconcile (kept in `pnlDocSummary`). + * + * Archipelago is the shared backbone — open hardware + sovereign software (the + * "mycelium"). Three surface entities run on top of it: + * • SolLunar Society — city members' club, Austin (the funded first node) + * • Pop-up Festivals — travelling gatherings (e.g. Solarpunk Summit) + * • Chateau du Faune — regenerative farm center + * + * Currency: all figures are USD unless noted. + */ + +export const PLACEHOLDER = true + +export const brand = { + name: 'Archipelago', + short: 'Archipelago', + // a connected network of islands — one backbone, many places. + tagline: 'One network. Three ways to experience it.', + subtagline: + 'A sovereign network on the Archipelago backbone. The first node — SolLunar Society, a members’ club in Austin — is live, modeled and raising now; the festival and the farm are next.', + firstNode: 'SolLunar Society · Austin, TX', + contactEmail: 'invest@sollunar.example', // PLACEHOLDER + // Seam for later: an LNbits instance + the founder's nostr npub power the + // "contribute" tiers. Left null until wired (see src/features/{lnbits,nostr}). + lnbitsBaseUrl: null as string | null, + nostrNpub: null as string | null, +} + +/** Big-picture framing for the vision slide (from the SolLunar Society plan). */ +export const problem = { + headline: 'Transformation fades without the infrastructure to sustain it.', + points: [ + 'People leave transformational events inspired — then return to environments that don’t support the change.', + 'There’s no lasting home for sovereignty-minded community: dreamers, creatives, technologists, regenerative living.', + 'Peak experiences don’t compound without ongoing relationships, education, and a place to practice.', + ], + thesis: + 'SolLunar Society is the missing infrastructure — a community hub where the work begun at transformational events continues through membership, tenants, events and regenerative living.', +} + +export type Accent = 'pine' | 'indigo' | 'rose' | 'clay' + +export type Entity = { + id: string + name: string + role: string // short label, e.g. "City center" + kind: string + accent: Accent + layer: 'backbone' | 'surface' + example?: string // for the generic festival entity + blurb: string + traits: string[] // what makes THIS entity unique + spaces: string[] + revenue: string[] // how this entity earns + icon: string +} + +/** + * The entities. Archipelago is the backbone; the other three are the + * experiences that run on it. PLACEHOLDER names & details. + */ +export const entities: Entity[] = [ + { + id: 'sollunar', + name: 'SolLunar Society', + role: 'City center · the first node', + kind: 'Members’ club & community hub · Austin, TX', + accent: 'indigo', + layer: 'surface', + 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 Archipelago stack.', + traits: [ + 'Live, financially-modeled first node (real P&L)', + 'Five founding tenants + a three-tier membership', + 'Bitcoin ATM, vending & the full member ecosystem on site', + ], + spaces: ['Retail space', 'Event space', 'Private lounge', 'Co-working', 'Yellow House hostel & data center'], + revenue: ['Memberships', 'Tenants', 'Events & venue buyouts'], + icon: 'Building2', + }, + { + id: 'festivals', + name: 'Pop-up Festivals', + role: 'Travelling gatherings', + kind: 'Travelling · ephemeral', + accent: 'rose', + layer: 'surface', + example: 'Solar Punk Summit', + blurb: + 'The travelling node: a repeatable festival format — like Solar Punk Summit — that deploys the entire stack anywhere for a few days, then packs down and moves on.', + traits: [ + 'Deploys the full stack from a mobile kit — anywhere', + 'Proves the network is portable, not tied to a building', + 'Turns every festival into a member-onboarding event', + ], + spaces: ['Main stage', 'Mobile exchange kiosk', 'Mesh & power rig', 'Camp & vendors', 'Workshop tents'], + revenue: ['Ticket sales', 'Vendor & bar revenue', 'Sponsorships'], + icon: 'Tent', + }, + { + id: 'chateau', + name: 'Chateau du Faune', + role: 'Farm center', + kind: 'Off-grid · regenerative farm', + 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.', + traits: [ + 'Proves the stack runs off-grid, anywhere', + 'Premium revenue from retreats & residencies', + 'Sponsor-an-alpaca connects small backers to the land', + ], + spaces: ['Retreat lodging', 'Residency studios', 'Event barn', 'Alpaca paddocks', 'Regenerative gardens'], + revenue: ['Lodging & retreats', 'Residencies', 'Experiences & venue hire'], + icon: 'Trees', + }, + { + id: 'archipelago', + name: 'Archipelago', + role: 'Shared backbone', + kind: 'Open hardware & sovereign software', + 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.', + traits: [ + 'Powers all three entities from one shared stack', + 'Open bill of materials — replicable anywhere', + 'Forkable and sovereign — no platform lock-in', + ], + spaces: ['Edge servers', 'Mesh routers', 'Exchange kiosks', 'NFC / membership', 'Self-hosted apps'], + revenue: ['Network & kiosk fees', 'Payment fees', 'Replication licensing'], + icon: 'Cpu', + }, +] + +export const entityById = (id: string) => entities.find((e) => e.id === id)! +/** The three experiences (everything except the backbone). */ +export const surfaceEntities = entities.filter((e) => e.layer === 'surface') + +/** + * The user ecosystem every location shares — the "full experience regardless + * of location" promise. This is what a member touches at any node. + */ +export const userEcosystem = [ + { + title: 'One membership card', + body: 'A single NFC card is your identity, access and wallet everywhere — the society, a festival, the farm.', + icon: 'CreditCard', + }, + { + title: 'Money exchange kiosk', + body: 'On-ramp cash to Bitcoin and back at any location. The same kiosk, everywhere.', + icon: 'Coins', + }, + { + title: 'Pay by Lightning', + body: 'Tap to pay for drinks, tickets, rooms and experiences — instant, low-fee settlement.', + icon: 'Zap', + }, + { + title: 'The Archipelago app', + body: 'Book spaces, buy tickets, find events and manage membership from one self-hosted app.', + icon: 'Sparkles', + }, + { + title: 'Portable identity', + body: 'Your nostr identity and social graph travel with you between every node and beyond.', + icon: 'KeyRound', + }, + { + title: 'Member perks', + body: 'Access, discounts and priority booking that follow you across the whole network.', + icon: 'HeartHandshake', + }, +] + +/** The Archipelago backbone / core stack (the infrastructure pillars). */ +export const tech = [ + { + title: 'Decentralized mesh network', + body: 'Community-owned routers and mesh kits blanket each location — connectivity no single provider can switch off.', + icon: 'Wifi', + }, + { + title: 'Open hardware', + body: 'Servers, routers, exchange kiosks and NFC hardware from a documented, replicable bill of materials.', + icon: 'Cpu', + }, + { + title: 'Sovereign software', + body: 'Self-hosted services on a stack we control — no platform lock-in, forkable by any future node.', + icon: 'Code', + }, + { + title: 'Bitcoin & Lightning', + body: 'Instant, low-fee settlement at kiosks and NFC points. Value stays in the network, not the middleman.', + icon: 'Zap', + }, + { + title: 'Nostr identity & comms', + body: 'Portable identity, messaging and social layer that travels with members across every node.', + icon: 'KeyRound', + }, + { + title: 'Replicable blueprint', + body: 'Every node ships its build docs. Proven once, the whole stack redeploys anywhere in the world.', + icon: 'Globe', + }, +] + +/* ───────────────────────────────────────────────────────────── + SolLunar Society — the first node's REAL business (from + SolLunarSociety.md). All figures below are SolLunar Society's + only; Archipelago (Lab 484), Festivals and Chateau have no + standalone numbers yet. Lab 484's economics appear here as + revenue lines INTO SolLunar (~$16k/mo) — don't double-count. + ───────────────────────────────────────────────────────────── */ + +/** Ownership & partnership of the SolLunar Society JV. */ +export type Partner = { + name: string + short?: string + equity: number // 0..1 + accent: Accent | 'amber' + role: string + responsibilities: string[] + note?: string +} +export const partnership = { + valuation: 4_000_000, + raiseAmount: 400_000, + equityOffered: 0.1, + partners: [ + { + name: 'SolLunar Punk Society', + short: 'SPS', + equity: 0.45, + accent: 'indigo', + role: 'Space & community', + responsibilities: [ + 'Curate & decorate the space', + 'Lease to tenants · insurance', + 'Outbound payments · SOPs', + 'Marketing & community management', + ], + }, + { + name: 'Lab 484', + equity: 0.45, + accent: 'pine', + role: 'Tech & operations', + responsibilities: [ + 'Operate the spaces & staff', + 'All IT / infrastructure (Archipelago)', + 'Inbound payments · bootcamps', + 'Tenant & investor relations', + ], + note: 'Builds & operates the Archipelago backbone — and is itself a tenant + event driver (~$16k/mo of the revenue below).', + }, + { + name: 'Investors', + equity: 0.1, + accent: 'amber', + role: 'Capital', + responsibilities: ['$400,000 for 10% equity'], + }, + ] as Partner[], +} + +/** The five founding tenants inside SolLunar Society. */ +export const tenants = [ + { + name: 'Lab 484', + icon: 'Cpu', + blurb: + 'Research lab & startup cluster building a “second internet” — Bitcoin, mesh networks, Nostr, e-cash and P2P. User-owned hardware, software and data.', + }, + { + name: 'Solarpunk Summit', + icon: 'Tent', + blurb: + 'Transformational platform & community builder — immersive experiences, educational programming and regenerative culture.', + }, + { + name: 'Noderunners Shop', + icon: 'Router', + blurb: + 'Retail for AI computers, mesh gear, Bitcoin nodes and repair. Open-source, secure laptops, phones and servers.', + }, + { + name: 'Divine Provisions', + icon: 'Coins', + blurb: + 'A food co-op for low-cost, organic, local groceries — offered as a member service at the building entrance.', + }, + { + name: 'Kitchen 484', + icon: 'Sparkles', + blurb: + 'Community food truck — organic, seed-oil-free, locally sourced. Runs even when disconnected from the internet.', + }, +] + +/** Membership tiers (customer product / recurring revenue). */ +export type Membership = { + name: string + price: number // monthly + target: number // member-count target + blurb: string + perks: string[] + featured?: boolean +} +export const memberships: Membership[] = [ + { + name: 'Solarpunk Society', + price: 50, + target: 150, + blurb: 'Events access.', + perks: ['Member events', 'Amenities, vending & Bitcoin ATM', 'Access to tenants'], + }, + { + name: 'Cypherpunk Commons', + price: 350, + target: 50, + featured: true, + blurb: 'Full access, all locations.', + perks: [ + 'Coworking with the Lab 484 team', + 'All locations · tech support', + 'Bitcoin ATM, vending, coffee & tea', + 'Full tenant & amenity access', + ], + }, + { + name: 'Lunarpunk Locals', + price: 150, + target: 100, + blurb: 'Coworking days.', + perks: ['Coworking days at SolLunar', 'Access to tenants', 'Amenities, vending & Bitcoin ATM'], + }, +] + +/** Comparable Austin memberships, for pricing context. */ +export const membershipComparables = [ + { name: 'Soho House', price: 350 }, + { name: 'Bond Collective', price: 300 }, + { name: 'Bathe', price: 200 }, + { name: 'Stardust Garage', price: 155 }, + { name: 'Submersive', price: 41 }, +] + +/** + * 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 + * match its "100 × $150" annotation. + */ +export type PnlItem = { label: string; amount: number; note?: string } +export type PnlGroup = { + id: string + title: string + icon: string + accent: Accent + kind: 'revenue' | 'expense' + items: PnlItem[] +} + +export const pnlGroups: PnlGroup[] = [ + { + id: 'memberships', + title: 'Memberships', + icon: 'CreditCard', + accent: 'indigo', + kind: 'revenue', + items: [ + { label: 'Cypherpunk Commons', amount: 17_500, note: '50 × $350' }, + { label: 'Solarpunk Society', amount: 7_500, note: '150 × $50' }, + { label: 'Lunarpunk Locals', amount: 1_500, note: '$150/mo · count TBD' }, + { label: 'Vending machine sales', amount: 5_000 }, + { label: 'Promo / B2B network sales', amount: 5_000 }, + ], + }, + { + id: 'events', + title: 'Events', + icon: 'PartyPopper', + 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: 'SolLunar Society events', amount: 1_500, note: '$50 weekend passes' }, + ], + }, + { + id: 'tenants', + title: 'Tenants', + icon: 'Building2', + accent: 'pine', + kind: 'revenue', + items: [ + { label: 'Solarpunk Summit', amount: 2_000 }, + { label: 'Lab 484', amount: 2_000 }, + { label: 'Divine Provisions', amount: 1_500 }, + { label: 'Noderunners Shop', amount: 1_500 }, + ], + }, + { + id: 'expenses', + title: 'Expenses', + icon: 'Coins', + accent: 'clay', + kind: 'expense', + items: [ + { label: 'Staff', amount: 22_500, note: '4 staff' }, + { label: 'Rent', amount: 6_500, note: '$4,500 + triple net' }, + { label: 'Overhead', amount: 3_000 }, + { label: 'Utilities', amount: 2_000, note: 'electric, water, trash' }, + ], + }, +] + +export const groupTotal = (g: PnlGroup) => g.items.reduce((s, i) => s + i.amount, 0) +const sumKind = (k: 'revenue' | 'expense') => + pnlGroups.filter((g) => g.kind === k).reduce((s, g) => s + groupTotal(g), 0) + +/** Recomputed monthly/yearly economics for SolLunar Society. */ +export const pnl = { + monthlyRevenue: sumKind('revenue'), + monthlyExpenses: sumKind('expense'), + monthlyProfit: sumKind('revenue') - sumKind('expense'), + yearlyProfit: (sumKind('revenue') - sumKind('expense')) * 12, +} +/** What the source doc's summary table stated (did NOT reconcile with line items). */ +export const pnlDocSummary = { revenue: 71_400, expenses: 38_500, profit: 32_900, yearlyProfit: 394_800 } + +/** The building & spaces. */ +export const buildings = [ + { + name: 'SolLunar Society', + rooms: ['Retail space', 'Event space', 'Private office', 'Private lounge'], + }, + { + name: 'Lab 484 · Yellow House 1', + rooms: ['Hostel room · $40/night', 'Healer room', 'Coworking', 'Meeting room', 'Outdoor'], + }, + { + name: 'Lab 484 · Yellow House 2', + rooms: ['VIP guest room', 'Office', 'Data center', 'Outdoor'], + }, +] + +/** The investor ask. */ +export const investorAsk = { + amount: 400_000, + equity: 0.1, + valuation: 4_000_000, + scenarios: [0.1, 0.25], // profit-share scenarios on the recomputed yearly profit +} + +/** Roadmap / replication story. SolLunar Society first, then the network. */ +export const roadmap = [ + { + phase: 'Phase 0', + title: 'Raise & open SolLunar', + when: 'Now', + body: 'Close the $400k round, sign the lease, fit out the space and onboard the founding tenants.', + }, + { + phase: 'Phase 1', + title: 'Operate & prove the model', + when: '~0–12 mo', + body: 'Run memberships, events and tenants in Austin; validate the P&L publicly on the Archipelago stack.', + }, + { + phase: 'Phase 2', + title: 'Add the festival', + when: '~12–18 mo', + body: 'Deploy the same stack to a travelling Solarpunk Summit — proving the network is portable, not tied to a building.', + }, + { + phase: 'Phase 3', + title: 'Add the farm & replicate', + when: '18 mo+', + body: 'Open Chateau du Faune and publish the replication kit — new nodes plug into the same backbone anywhere.', + }, +] + +/** Formatting helpers used across slides. Millions render as $X.XXMM. */ +export const fmtUsd = (n: number) => { + if (n >= 1_000_000) + return `$${(n / 1_000_000).toLocaleString('en-US', { maximumFractionDigits: 2 })}MM` + if (n >= 1000) + return `$${(n / 1000).toLocaleString('en-US', { maximumFractionDigits: n % 1000 === 0 ? 0 : 1 })}k` + return `$${n.toLocaleString('en-US')}` +} + +export const fmtUsdFull = (n: number) => `$${n.toLocaleString('en-US')}` + +/** Tailwind class helpers keyed by entity accent — keeps slides declarative. */ +export const accentText: Record = { + pine: 'text-pine-300', + indigo: 'text-indigo-300', + rose: 'text-rose-300', + clay: 'text-clay-400', +} +export const accentDot: Record = { + pine: 'bg-pine-400', + indigo: 'bg-indigo-400', + rose: 'bg-rose-400', + clay: 'bg-clay-400', +} +export const accentRing: Record = { + pine: 'border-pine-500/30 bg-pine-500/5', + indigo: 'border-indigo-500/30 bg-indigo-500/5', + rose: 'border-rose-500/30 bg-rose-500/5', + clay: 'border-clay-400/30 bg-clay-400/5', +} diff --git a/src/deck/gallery.ts b/src/deck/gallery.ts new file mode 100644 index 0000000..ba34758 --- /dev/null +++ b/src/deck/gallery.ts @@ -0,0 +1,82 @@ +/** + * Gallery auto-population. + * + * Drop image files into these folders and they appear in the deck gallery + * automatically — no code changes, no manifest to edit: + * + * src/deck/gallery/city/ → City (SolLunar Punk Society) + * src/deck/gallery/farm/ → Farm (Chateau du Faune) + * src/deck/gallery/festival/ → Festival (Pop-up Festivals) + * + * Supported: jpg jpeg png webp avif gif svg. Images show sorted by filename, + * so prefix with 01-, 02-, … to control order. The seeded *-placeholder.svg + * files are just demo art — delete them once you add real photos. + * + * Under the hood this uses Vite's import.meta.glob, which must take a literal + * pattern (no variables) — hence one call per folder. + */ +import { entityById, type Accent } from './data' + +type GlobMap = Record + +// NB: import.meta.glob is statically analysed by Vite — its options must be an +// inline object literal (not a shared const), so the object is repeated below. +const city = import.meta.glob('./gallery/city/*.{jpg,jpeg,png,webp,avif,gif,svg}', { + eager: true, + query: '?url', + import: 'default', +}) as GlobMap +const farm = import.meta.glob('./gallery/farm/*.{jpg,jpeg,png,webp,avif,gif,svg}', { + eager: true, + query: '?url', + import: 'default', +}) as GlobMap +const festival = import.meta.glob('./gallery/festival/*.{jpg,jpeg,png,webp,avif,gif,svg}', { + eager: true, + query: '?url', + import: 'default', +}) as GlobMap + +export type GalleryImage = { src: string; name: string } + +function toImages(map: GlobMap): GalleryImage[] { + return Object.entries(map) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([path, src]) => ({ src, name: path.split('/').pop()!.replace(/\.[^.]+$/, '') })) +} + +export type GallerySection = { + id: 'city' | 'farm' | 'festival' + label: string + entityName: string + accent: Accent + folder: string + images: GalleryImage[] +} + +export const gallerySections: GallerySection[] = [ + { + id: 'city', + label: 'City', + entityName: entityById('sollunar').name, + accent: 'indigo', + folder: 'src/deck/gallery/city/', + images: toImages(city), + }, + { + id: 'farm', + label: 'Farm', + entityName: entityById('chateau').name, + accent: 'clay', + folder: 'src/deck/gallery/farm/', + images: toImages(farm), + }, + { + id: 'festival', + label: 'Festival', + entityName: entityById('festivals').name, + accent: 'rose', + folder: 'src/deck/gallery/festival/', + images: toImages(festival), + }, +] diff --git a/src/deck/gallery/README.md b/src/deck/gallery/README.md new file mode 100644 index 0000000..e5a79b4 --- /dev/null +++ b/src/deck/gallery/README.md @@ -0,0 +1,18 @@ +# Deck gallery — just drop images in + +Each subfolder feeds one section of the Gallery slide. **Drop image files in and +they appear automatically** — no code to touch. + +| Folder | Section | Entity | +| ----------- | -------- | --------------------- | +| `city/` | City | SolLunar Punk Society | +| `farm/` | Farm | Chateau du Faune | +| `festival/` | Festival | Pop-up Festivals | + +- Supported formats: `jpg` `jpeg` `png` `webp` `avif` `gif` `svg`. +- Images display **sorted by filename** — prefix `01-`, `02-`, … to order them. +- The `*-placeholder.svg` files are demo art. **Delete them** once you add real photos. +- Wiring lives in `src/deck/gallery.ts` (Vite `import.meta.glob`). If you add a + brand-new format, extend the glob patterns there. + +Recommended: web-optimized `.webp` under ~500 KB each for fast loading. diff --git a/src/deck/gallery/city/1024-580.webp b/src/deck/gallery/city/1024-580.webp new file mode 100644 index 0000000..e656aaf Binary files /dev/null and b/src/deck/gallery/city/1024-580.webp differ diff --git a/src/deck/gallery/city/1024-581.webp b/src/deck/gallery/city/1024-581.webp new file mode 100644 index 0000000..427ba88 Binary files /dev/null and b/src/deck/gallery/city/1024-581.webp differ diff --git a/src/deck/gallery/city/1024-768.webp b/src/deck/gallery/city/1024-768.webp new file mode 100644 index 0000000..2939d2b Binary files /dev/null and b/src/deck/gallery/city/1024-768.webp differ diff --git a/src/deck/gallery/city/1024-769.webp b/src/deck/gallery/city/1024-769.webp new file mode 100644 index 0000000..98bde8b Binary files /dev/null and b/src/deck/gallery/city/1024-769.webp differ diff --git a/src/deck/gallery/city/1024-770.webp b/src/deck/gallery/city/1024-770.webp new file mode 100644 index 0000000..f98c87e Binary files /dev/null and b/src/deck/gallery/city/1024-770.webp differ diff --git a/src/deck/gallery/farm/1536-2048-max.webp b/src/deck/gallery/farm/1536-2048-max.webp new file mode 100644 index 0000000..925a489 Binary files /dev/null and b/src/deck/gallery/farm/1536-2048-max.webp differ diff --git a/src/deck/gallery/farm/2048-1536-max (1).webp b/src/deck/gallery/farm/2048-1536-max (1).webp new file mode 100644 index 0000000..66ab743 Binary files /dev/null and b/src/deck/gallery/farm/2048-1536-max (1).webp differ diff --git a/src/deck/gallery/farm/2048-1536-max (2).webp b/src/deck/gallery/farm/2048-1536-max (2).webp new file mode 100644 index 0000000..6e2d1d0 Binary files /dev/null and b/src/deck/gallery/farm/2048-1536-max (2).webp differ diff --git a/src/deck/gallery/farm/2048-1536-max (3).webp b/src/deck/gallery/farm/2048-1536-max (3).webp new file mode 100644 index 0000000..40353d9 Binary files /dev/null and b/src/deck/gallery/farm/2048-1536-max (3).webp differ diff --git a/src/deck/gallery/farm/2048-1536-max.webp b/src/deck/gallery/farm/2048-1536-max.webp new file mode 100644 index 0000000..dcb165a Binary files /dev/null and b/src/deck/gallery/farm/2048-1536-max.webp differ diff --git a/src/deck/gallery/farm/PXL_20250907_134308735.webp b/src/deck/gallery/farm/PXL_20250907_134308735.webp new file mode 100644 index 0000000..0b17b7d Binary files /dev/null and b/src/deck/gallery/farm/PXL_20250907_134308735.webp differ diff --git a/src/deck/gallery/farm/PXL_20251121_064432019.webp b/src/deck/gallery/farm/PXL_20251121_064432019.webp new file mode 100644 index 0000000..0397f6a Binary files /dev/null and b/src/deck/gallery/farm/PXL_20251121_064432019.webp differ diff --git a/src/deck/gallery/farm/garden.webp b/src/deck/gallery/farm/garden.webp new file mode 100644 index 0000000..3acaf33 Binary files /dev/null and b/src/deck/gallery/farm/garden.webp differ diff --git a/src/deck/gallery/festival/01-placeholder.svg b/src/deck/gallery/festival/01-placeholder.svg new file mode 100644 index 0000000..73a7b1f --- /dev/null +++ b/src/deck/gallery/festival/01-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 01 + PLACEHOLDER · 800×800 + diff --git a/src/deck/gallery/festival/02-placeholder.svg b/src/deck/gallery/festival/02-placeholder.svg new file mode 100644 index 0000000..b6c7456 --- /dev/null +++ b/src/deck/gallery/festival/02-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 02 + PLACEHOLDER · 800×560 + diff --git a/src/deck/gallery/festival/03-placeholder.svg b/src/deck/gallery/festival/03-placeholder.svg new file mode 100644 index 0000000..3951739 --- /dev/null +++ b/src/deck/gallery/festival/03-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 03 + PLACEHOLDER · 800×1040 + diff --git a/src/deck/gallery/festival/04-placeholder.svg b/src/deck/gallery/festival/04-placeholder.svg new file mode 100644 index 0000000..46ef5c8 --- /dev/null +++ b/src/deck/gallery/festival/04-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 04 + PLACEHOLDER · 800×700 + diff --git a/src/deck/gallery/festival/05-placeholder.svg b/src/deck/gallery/festival/05-placeholder.svg new file mode 100644 index 0000000..e9ce0cc --- /dev/null +++ b/src/deck/gallery/festival/05-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 05 + PLACEHOLDER · 800×600 + diff --git a/src/deck/slides/AskSlide.vue b/src/deck/slides/AskSlide.vue new file mode 100644 index 0000000..1a00ade --- /dev/null +++ b/src/deck/slides/AskSlide.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/deck/slides/EcosystemSlide.vue b/src/deck/slides/EcosystemSlide.vue new file mode 100644 index 0000000..be2b577 --- /dev/null +++ b/src/deck/slides/EcosystemSlide.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/deck/slides/EntitiesSlide.vue b/src/deck/slides/EntitiesSlide.vue new file mode 100644 index 0000000..639212b --- /dev/null +++ b/src/deck/slides/EntitiesSlide.vue @@ -0,0 +1,63 @@ + + + diff --git a/src/deck/slides/GallerySlide.vue b/src/deck/slides/GallerySlide.vue new file mode 100644 index 0000000..b059a62 --- /dev/null +++ b/src/deck/slides/GallerySlide.vue @@ -0,0 +1,92 @@ + + + diff --git a/src/deck/slides/MembershipSlide.vue b/src/deck/slides/MembershipSlide.vue new file mode 100644 index 0000000..259b847 --- /dev/null +++ b/src/deck/slides/MembershipSlide.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/deck/slides/NetworkSlide.vue b/src/deck/slides/NetworkSlide.vue new file mode 100644 index 0000000..a2901e7 --- /dev/null +++ b/src/deck/slides/NetworkSlide.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/deck/slides/PartnershipSlide.vue b/src/deck/slides/PartnershipSlide.vue new file mode 100644 index 0000000..045ea4b --- /dev/null +++ b/src/deck/slides/PartnershipSlide.vue @@ -0,0 +1,78 @@ + + + diff --git a/src/deck/slides/PnlSlide.vue b/src/deck/slides/PnlSlide.vue new file mode 100644 index 0000000..b5f0622 --- /dev/null +++ b/src/deck/slides/PnlSlide.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/deck/slides/RoadmapSlide.vue b/src/deck/slides/RoadmapSlide.vue new file mode 100644 index 0000000..caf691a --- /dev/null +++ b/src/deck/slides/RoadmapSlide.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/deck/slides/SolLunarSlide.vue b/src/deck/slides/SolLunarSlide.vue new file mode 100644 index 0000000..a91aba3 --- /dev/null +++ b/src/deck/slides/SolLunarSlide.vue @@ -0,0 +1,8 @@ + + + diff --git a/src/deck/slides/SpacesSlide.vue b/src/deck/slides/SpacesSlide.vue new file mode 100644 index 0000000..048fd15 --- /dev/null +++ b/src/deck/slides/SpacesSlide.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/deck/slides/TechSlide.vue b/src/deck/slides/TechSlide.vue new file mode 100644 index 0000000..db7f7d4 --- /dev/null +++ b/src/deck/slides/TechSlide.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/deck/slides/TenantsSlide.vue b/src/deck/slides/TenantsSlide.vue new file mode 100644 index 0000000..8049d3a --- /dev/null +++ b/src/deck/slides/TenantsSlide.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/deck/slides/TitleSlide.vue b/src/deck/slides/TitleSlide.vue new file mode 100644 index 0000000..b8fef18 --- /dev/null +++ b/src/deck/slides/TitleSlide.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/deck/slides/VisionSlide.vue b/src/deck/slides/VisionSlide.vue new file mode 100644 index 0000000..97f4e42 --- /dev/null +++ b/src/deck/slides/VisionSlide.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/deck/useDeck.ts b/src/deck/useDeck.ts new file mode 100644 index 0000000..6cf2e17 --- /dev/null +++ b/src/deck/useDeck.ts @@ -0,0 +1,66 @@ +import { onMounted, onUnmounted, ref, computed } from 'vue' + +/** + * Deck navigation: current slide index, next/prev/goto, keyboard control, + * and URL-hash sync so any slide is a shareable deep link (…/#/7). + */ +export function useDeck(total: number) { + const clamp = (n: number) => Math.min(Math.max(n, 0), total - 1) + + const readHash = () => { + const m = window.location.hash.match(/#\/(\d+)/) + return m ? clamp(parseInt(m[1], 10) - 1) : 0 + } + + // seed from the hash synchronously so deep links (…/#/7) land on the right + // slide at first paint — no flash of slide 1 and no on-load transition. + const index = ref(readHash()) + const direction = ref<1 | -1>(1) + + function goto(n: number) { + const next = clamp(n) + direction.value = next >= index.value ? 1 : -1 + index.value = next + window.location.hash = `#/${next + 1}` + } + const next = () => goto(index.value + 1) + const prev = () => goto(index.value - 1) + + function onKey(e: KeyboardEvent) { + if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { + e.preventDefault() + next() + } else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { + e.preventDefault() + prev() + } else if (e.key === 'Home') { + goto(0) + } else if (e.key === 'End') { + goto(total - 1) + } + } + + const onHash = () => { + index.value = readHash() + } + + onMounted(() => { + if (!window.location.hash) window.location.hash = '#/1' + window.addEventListener('keydown', onKey) + window.addEventListener('hashchange', onHash) + }) + onUnmounted(() => { + window.removeEventListener('keydown', onKey) + window.removeEventListener('hashchange', onHash) + }) + + return { + index, + direction, + goto, + next, + prev, + isFirst: computed(() => index.value === 0), + isLast: computed(() => index.value === total - 1), + } +} diff --git a/src/router/index.ts b/src/router/index.ts index 4f315f4..1537b8b 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -3,8 +3,8 @@ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' const routes: RouteRecordRaw[] = [ { path: '/', - name: 'home', - component: () => import('@/views/HomeView.vue'), + name: 'deck', + component: () => import('@/views/DeckView.vue'), }, ] diff --git a/src/style.css b/src/style.css index 3ebb69e..f10e40b 100644 --- a/src/style.css +++ b/src/style.css @@ -73,6 +73,43 @@ --radius-sm: calc(var(--radius) - 4px); } +/* ───────────────────────────────────────────────────────────── + ARCHIPELAGO pitch-deck brand palette (PLACEHOLDER — swap when real + brand assets land). Regenerative-tech: pine green + warm amber + on a deep near-black ground. Used by the self-contained dark + deck; the shadcn tokens above are left intact for other pages. + ───────────────────────────────────────────────────────────── */ +@theme { + --color-ink: hsl(160 28% 6%); /* deck ground */ + --color-ink-2: hsl(160 24% 9%); /* raised panels */ + --color-ink-3: hsl(158 20% 13%); /* cards / borders */ + + --color-pine-300: hsl(158 42% 62%); + --color-pine-400: hsl(159 45% 50%); + --color-pine-500: hsl(160 60% 40%); + --color-pine-600: hsl(162 66% 30%); + + --color-amber-300: hsl(38 96% 70%); + --color-amber-400: hsl(36 95% 60%); + --color-amber-500: hsl(32 92% 52%); + + /* SolLunar Punk Society (city) accent — lunarpunk indigo */ + --color-indigo-300: hsl(255 85% 78%); + --color-indigo-400: hsl(256 78% 68%); + --color-indigo-500: hsl(258 70% 58%); + + /* Pop-up Festivals accent — vibrant rave rose */ + --color-rose-300: hsl(342 95% 76%); + --color-rose-400: hsl(344 85% 66%); + --color-rose-500: hsl(346 78% 56%); + + --color-clay-400: hsl(14 72% 62%); /* farm / warmth accent */ + --color-sand: hsl(44 30% 90%); /* primary deck text */ + --color-sand-dim: hsl(150 8% 62%); /* muted deck text */ + + --font-display: 'Georgia', 'Times New Roman', serif; +} + @layer base { * { @apply border-border; @@ -80,4 +117,13 @@ body { @apply bg-background text-foreground; } + + /* Deck root: self-contained dark canvas independent of shadcn tokens */ + .deck-root { + background: + radial-gradient(120% 80% at 80% -10%, hsl(162 60% 14% / 0.55), transparent 60%), + radial-gradient(90% 70% at -10% 110%, hsl(32 80% 18% / 0.35), transparent 55%), + var(--color-ink); + color: var(--color-sand); + } } diff --git a/src/views/DeckView.vue b/src/views/DeckView.vue new file mode 100644 index 0000000..6133717 --- /dev/null +++ b/src/views/DeckView.vue @@ -0,0 +1,7 @@ + + +