feat: reskin public pages to zine direction

This commit is contained in:
Jennie Robinson Faber 2026-04-02 21:29:52 +01:00
parent 8b3daadadd
commit 88caca94c7
8 changed files with 2663 additions and 3577 deletions

View file

@ -1,186 +1,270 @@
<template>
<div class="max-w-6xl mx-auto px-6 md:px-8">
<!-- Hero Section -->
<section class="py-16 md:py-24 ink-grain">
<div class="max-w-2xl">
<h1
class="text-display-xl font-light text-guild-100 leading-tight mb-2"
>
Build your co-op studio
</h1>
<p
class="text-display-xl font-light text-guild-500 leading-tight mb-8"
>
with people who get it.
</p>
<div>
<!-- HERO -->
<div class="hero">
<h1>Ghost Guild is where game developers practice cooperative business models.</h1>
<p>Resources, events, and a community of people figuring it out. Three circles, no hierarchy. $050/mo, pay what you can.</p>
<div class="hero-links">
<NuxtLink to="/join" class="hero-link primary">Become a member</NuxtLink>
<NuxtLink to="/wiki" class="hero-link">Read the wiki</NuxtLink>
<NuxtLink to="/about" class="hero-link">What is this?</NuxtLink>
</div>
</div>
<p class="text-lg text-guild-400 leading-relaxed mb-8 max-w-xl">
Ghost Guild is a peer community for game developers exploring
cooperative models. Find support, share knowledge, grow together.
</p>
<!-- THREE CIRCLES -->
<div class="content-row">
<div v-for="circle in circleData" :key="circle.value" class="content-block">
<div class="label" :style="{ color: `var(--c-${circle.value})` }">{{ circle.label }}</div>
<h2>{{ circle.metaphor }}</h2>
<p>{{ circle.blurb }}</p>
<details>
<summary>What's included?</summary>
<p>{{ circle.included }}</p>
</details>
</div>
</div>
<!-- Signup Form -->
<form @submit.prevent="handleJoinSubmit" class="mb-4">
<div class="flex flex-col sm:flex-row gap-3">
<UInput
v-model="joinEmail"
type="email"
placeholder="your.email@example.com"
size="lg"
class="flex-1"
:disabled="isSubmitting"
/>
<UButton
type="submit"
size="lg"
:loading="isSubmitting"
:disabled="!isEmailValid"
>
Join Us
</UButton>
<!-- UPCOMING EVENTS + WIKI -->
<div class="content-row two-col">
<div class="content-block">
<div class="label">Upcoming Events</div>
<div v-if="events?.length" class="event-list">
<div v-for="event in events" :key="event._id" class="event-item">
<span class="event-date">{{ formatDate(event.date) }}</span>
<span class="event-title">
<NuxtLink :to="`/events/${event._id}`">{{ event.title }}</NuxtLink>
</span>
<CircleBadge v-if="event.circle" :circle="event.circle" />
</div>
</form>
<p class="text-sm text-guild-600">Free to join. Pay what you can.</p>
<!-- Success/Error Messages -->
<div
v-if="submitSuccess"
class="mt-4 p-3 bg-primary-500/10 border border-primary-500/30 rounded-lg"
>
<p class="text-primary-400 text-sm">
Check your email to complete signup!
</p>
</div>
<div
v-if="submitError"
class="mt-4 p-3 bg-ember-900/20 border border-ember-500/30 rounded-lg"
>
<p class="text-ember-400 text-sm">{{ submitError }}</p>
<p v-else class="empty">No upcoming events</p>
</div>
<div class="content-block">
<div class="label">Recently in the Wiki</div>
<div class="wiki-list">
<div class="wiki-item">
<a href="/wiki">Revenue sharing models</a>
</div>
<div class="wiki-item">
<a href="/wiki">What is a cooperative studio?</a>
</div>
<div class="wiki-item">
<a href="/wiki">Governance structures</a>
</div>
<div class="wiki-item">
<a href="/wiki">Legal incorporation guide</a>
</div>
</div>
</div>
</section>
</div>
<GuildDivider variant="woodcut" />
<!-- Value Props Section -->
<section class="py-16">
<div class="grid md:grid-cols-3 gap-8 md:gap-12">
<div>
<p class="text-ui-label text-candlelight-400 mb-3">Peer Support</p>
<p class="text-guild-400 leading-relaxed">
Connect with founders at your stage and practitioners who've been
there. Real conversations, real help.
</p>
</div>
<div>
<p class="text-ui-label text-candlelight-400 mb-3">
Shared Knowledge
</p>
<p class="text-guild-400 leading-relaxed">
Templates, governance docs, financial modelstools built by co-ops,
for co-ops. All members get full access.
</p>
</div>
<div>
<p class="text-ui-label text-candlelight-400 mb-3">
Solidarity Economics
</p>
<p class="text-guild-400 leading-relaxed">
Those who can, support those who can't. No tiers, no gatekeeping.
Everyone gets everything.
</p>
</div>
</div>
</section>
<GuildDivider variant="woodcut" />
<!-- Circles Section -->
<section class="py-16">
<p class="text-ui-label text-guild-600 mb-8">Find your people</p>
<div class="space-y-4 mb-8">
<NuxtLink
v-for="circle in circles"
:key="circle.value"
to="/about/circles"
class="flex items-baseline gap-8 group py-2"
>
<span
class="text-guild-300 group-hover:text-guild-100 transition-colors w-32 md:w-40"
>
{{ circle.label }}
</span>
<span class="text-guild-600">
{{ circle.shortDescription }}
</span>
</NuxtLink>
</div>
<p class="text-sm text-guild-600 italic">
These reflect your journey, not your status. Move between them as you
grow.
</p>
</section>
<GuildDivider variant="woodcut" />
<!-- Bottom CTA Section -->
<section class="py-24 text-center">
<p class="text-ui-label text-guild-600 mb-4">Part of the Baby Ghosts family</p>
<h2 class="text-display font-light text-guild-200 mb-8">
Ready to find your people?
</h2>
<UButton
to="/join"
variant="outline"
size="lg"
class="hover:bg-primary-500/10"
>
Become a Ghostie
</UButton>
</section>
<!-- PARCHMENT INSET -->
<ParchmentInset>
<div class="label" style="color: var(--candle-faint); opacity: 0.6; margin-bottom: 12px;">From the Wiki</div>
<h2>What is a cooperative studio?</h2>
<p>A cooperative studio is a game development company owned and governed by the people who work there. Decisions are made collectively. Profits are shared according to contribution, not ownership stake.</p>
<p>The games industry is full of stories about crunch, layoffs, and studios that extract value from workers. Cooperatives are one alternative not the only one, but one worth <a href="/wiki">practicing together</a>.</p>
<p><a href="/wiki">Read more in the wiki &rarr;</a></p>
</ParchmentInset>
</div>
</template>
<script setup>
import { getCircleOptions } from "~/config/circles";
definePageMeta({
layout: "default",
});
})
const circles = getCircleOptions();
const { data: events } = await useFetch('/api/events', {
query: { limit: 4, upcoming: true },
default: () => [],
})
// Join form state
const joinEmail = ref("");
const isSubmitting = ref(false);
const submitSuccess = ref(false);
const submitError = ref("");
const circleData = [
{
value: 'community',
label: 'Community',
metaphor: 'The open hall',
blurb: 'Arrival, curiosity, orientation. For anyone exploring cooperative models in game development. Access the wiki, public events, and Slack.',
included: 'Wiki access, public events, Slack community, monthly guild meetings. Free or pay-what-you-can.',
},
{
value: 'founder',
label: 'Founder',
metaphor: 'The workshop',
blurb: 'For people actively building cooperatives. Structured practice, peer support, templates, and hands-on resources.',
included: 'Everything in Community plus the peer accelerator, 1:1 mentorship matching, and Founder-only workshops.',
},
{
value: 'practitioner',
label: 'Practitioner',
metaphor: 'The alcove',
blurb: 'Where experience is shared and knowledge given back. Teaching, advising, shaping the program itself.',
included: 'Everything in Founder plus the ability to mentor, propose events, contribute to the wiki, and help govern the Guild.',
},
]
const isEmailValid = computed(() => {
return joinEmail.value && joinEmail.value.includes("@");
});
const handleJoinSubmit = async () => {
if (!isEmailValid.value || isSubmitting.value) return;
isSubmitting.value = true;
submitSuccess.value = false;
submitError.value = "";
try {
// Redirect to join page with email pre-filled
await navigateTo({
path: "/join",
query: { email: joinEmail.value },
});
} catch (err) {
console.error("Join error:", err);
submitError.value = "Something went wrong. Please try again.";
} finally {
isSubmitting.value = false;
}
};
const formatDate = (dateStr) => {
if (!dateStr) return ''
const d = new Date(dateStr)
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
}
</script>
<style scoped>
/* ---- HERO ---- */
.hero {
padding: 48px 32px;
border-bottom: 1px dashed var(--border);
}
.hero h1 {
font-family: 'Brygada 1918', serif;
font-size: 36px;
font-weight: 600;
color: var(--text-bright);
line-height: 1.15;
letter-spacing: -0.01em;
margin-bottom: 16px;
max-width: 540px;
}
.hero p {
color: var(--text-dim);
max-width: 460px;
line-height: 1.7;
margin-bottom: 20px;
}
.hero-links {
display: flex;
gap: 16px;
font-size: 13px;
}
.hero-link {
color: var(--candle);
padding: 6px 16px;
border: 1px dashed var(--candle-faint);
transition: all 0.2s;
text-decoration: none;
}
.hero-link:hover {
border-color: var(--candle);
border-style: solid;
text-decoration: none;
}
.hero-link.primary {
background: var(--candle);
color: var(--bg);
border-color: var(--candle);
border-style: solid;
}
.hero-link.primary:hover {
background: var(--candle-dim);
border-color: var(--candle-dim);
}
/* ---- CONTENT GRID ---- */
.content-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
border-bottom: 1px dashed var(--border);
}
.content-row.two-col {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.content-block {
padding: 24px 28px;
border-right: 1px dashed var(--border);
min-width: 0;
overflow-wrap: break-word;
}
.content-block:last-child { border-right: none; }
.content-block h2 {
font-family: 'Brygada 1918', serif;
font-size: 18px;
font-weight: 500;
color: var(--text-bright);
margin-bottom: 8px;
}
.content-block p {
color: var(--text-dim);
font-size: 12px;
line-height: 1.65;
}
.content-block .label {
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-faint);
margin-bottom: 8px;
}
/* ---- DETAILS ---- */
details {
margin-top: 12px;
}
details summary {
font-size: 12px;
color: var(--candle-dim);
cursor: pointer;
list-style: none;
}
details summary::before {
content: '+ ';
}
details[open] summary::before {
content: ' ';
}
details p {
margin-top: 8px;
}
/* ---- EVENT LIST ---- */
.event-item {
display: grid;
grid-template-columns: 80px 1fr auto;
gap: 16px;
align-items: baseline;
padding: 10px 0;
border-bottom: 1px dashed var(--border);
transition: padding-left 0.2s;
}
.event-item:last-child { border-bottom: none; }
.event-item:hover { padding-left: 4px; }
.event-date { color: var(--text-faint); font-size: 12px; }
.event-title { color: var(--text); font-size: 13px; }
.event-title a { color: var(--text); text-decoration: none; }
.event-title a:hover { color: var(--candle); }
/* ---- WIKI LIST ---- */
.wiki-item {
padding: 8px 0;
border-bottom: 1px dashed var(--border);
font-size: 13px;
}
.wiki-item:last-child { border-bottom: none; }
.wiki-item a { color: var(--text); text-decoration: none; }
.wiki-item a:hover { color: var(--candle); }
.empty {
color: var(--text-faint);
font-size: 12px;
}
/* ---- RESPONSIVE ---- */
@media (max-width: 768px) {
.content-row,
.content-row.two-col {
grid-template-columns: 1fr;
}
.content-block {
border-right: none;
border-bottom: 1px dashed var(--border);
}
.content-block:last-child { border-bottom: none; }
.hero-links {
flex-direction: column;
gap: 8px;
}
.hero-link {
text-align: center;
}
}
</style>