Initial commit: site code, layouts, content (images added separately)

Fixes duplicate placeholder cover image on the 3 newest blog posts,
broken aspect-ratio classes on the albums listing page, and a
non-responsive fixed sidebar on the chat page.
This commit is contained in:
Dr. Jones
2026-07-08 06:18:17 +00:00
commit d9961a76ad
80 changed files with 9438 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
{{ define "main" }}
<div class="bg-gray-900 min-h-screen py-16 px-4 sm:px-6 lg:px-8" x-data="membershipPage()">
<div class="max-w-4xl mx-auto">
<div class="text-center mb-12">
<h1 class="text-4xl font-extrabold text-gray-100 tracking-tight sm:text-5xl mb-4">Become a Member</h1>
<p class="text-xl text-gray-300 max-w-2xl mx-auto">
Unlock exclusive access to premium content, unlimited downloads, and direct communication with Dr. Jones.
</p>
</div>
<!-- Benefits Section -->
<div class="bg-gray-800 rounded-lg p-8 mb-8 border border-gray-700">
<h2 class="text-2xl font-bold text-gray-100 mb-6">Member Benefits</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 bg-primary rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-100 mb-2">Unlimited Downloads</h3>
<p class="text-gray-300">Access and download all resources from the library without limits.</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 bg-primary rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-100 mb-2">Direct Email Access</h3>
<p class="text-gray-300">Get my direct email address and submit contact forms for personalized help.</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 bg-primary rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-100 mb-2">Member Testimonials</h3>
<p class="text-gray-300">Read and share success stories from other growers using the sterile method.</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 bg-primary rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-100 mb-2">Priority Support</h3>
<p class="text-gray-300">Members get priority responses and access to exclusive content updates.</p>
</div>
</div>
</div>
</div>
<!-- Buy Me a Coffee Section -->
<div class="bg-gradient-to-r from-[#FFDD00] to-[#FFCC00] rounded-lg p-8 mb-8 text-center">
<h2 class="text-3xl font-bold text-gray-900 mb-4">Support the Method</h2>
<p class="text-lg text-gray-800 mb-6">
Membership requires a $5 minimum donation via Buy Me a Coffee. This helps support continued development of sterile hydroponic protocols and resources.
</p>
<a href="https://www.buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener noreferrer"
class="inline-flex items-center px-8 py-4 bg-gray-900 hover:bg-gray-800 text-white rounded-lg font-bold text-lg transition-colors shadow-lg">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 24 24">
<path d="M18.5 3H6c-1.1 0-2 .9-2 2v5.71c0 3.83 2.95 7.18 6.78 7.29 3.96.12 7.22-3.06 7.22-7v-1h.5c1.38 0 2.5-1.12 2.5-2.5S19.88 3 18.5 3zM16 5v3H6V5h10zm2.5 5H18V5h.5c.28 0 .5.22.5.5s-.22.5-.5.5zM4 19h16v2H4v-2z"/>
</svg>
Donate $5 Minimum
</a>
<p class="text-sm text-gray-700 mt-4 italic">After completing your donation, return here to sign up for membership.</p>
</div>
<!-- Login/Register Tabs -->
<div class="bg-gray-800 rounded-lg border border-gray-700 overflow-hidden">
<div class="border-b border-gray-700 flex">
<button @click="activeTab = 'login'"
:class="activeTab === 'login' ? 'bg-primary text-white' : 'text-gray-300 hover:text-gray-100'"
class="flex-1 px-6 py-4 font-medium transition-colors">
Login
</button>
<button @click="activeTab = 'register'"
:class="activeTab === 'register' ? 'bg-primary text-white' : 'text-gray-300 hover:text-gray-100'"
class="flex-1 px-6 py-4 font-medium transition-colors">
Sign Up
</button>
</div>
<div class="p-8">
<!-- Login Form -->
<div x-show="activeTab === 'login'" x-transition>
<form @submit.prevent="handleLogin()" class="space-y-6">
<div>
<label for="login-email" class="block text-sm font-medium text-gray-300 mb-2">Email</label>
<input type="email" id="login-email" x-model="loginEmail" required
class="w-full px-4 py-3 bg-gray-900 border border-gray-700 rounded-lg text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent">
</div>
<div>
<label for="login-password" class="block text-sm font-medium text-gray-300 mb-2">Password</label>
<input type="password" id="login-password" x-model="loginPassword" required
class="w-full px-4 py-3 bg-gray-900 border border-gray-700 rounded-lg text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent">
</div>
<div x-show="loginError" class="bg-red-900/50 border border-red-700 rounded-lg p-4 text-red-200">
<p x-text="loginError"></p>
</div>
<button type="submit"
:disabled="loginLoading"
class="w-full px-6 py-3 bg-primary hover:bg-emerald-700 text-white font-semibold rounded-lg transition-colors disabled:opacity-50">
<span x-show="!loginLoading">Login</span>
<span x-show="loginLoading">Logging in...</span>
</button>
</form>
</div>
<!-- Register Form -->
<div x-show="activeTab === 'register'" x-transition>
<form @submit.prevent="handleRegister()" class="space-y-6">
<div>
<label for="register-name" class="block text-sm font-medium text-gray-300 mb-2">Full Name</label>
<input type="text" id="register-name" x-model="registerName" required
class="w-full px-4 py-3 bg-gray-900 border border-gray-700 rounded-lg text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent">
</div>
<div>
<label for="register-email" class="block text-sm font-medium text-gray-300 mb-2">Email</label>
<input type="email" id="register-email" x-model="registerEmail" required
class="w-full px-4 py-3 bg-gray-900 border border-gray-700 rounded-lg text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent">
</div>
<div>
<label for="register-password" class="block text-sm font-medium text-gray-300 mb-2">Password</label>
<input type="password" id="register-password" x-model="registerPassword" required minlength="8"
class="w-full px-4 py-3 bg-gray-900 border border-gray-700 rounded-lg text-gray-100 focus:ring-2 focus:ring-primary focus:border-transparent">
<p class="mt-1 text-sm text-gray-400">Must be at least 8 characters</p>
</div>
<div x-show="registerError" class="bg-red-900/50 border border-red-700 rounded-lg p-4 text-red-200">
<p x-text="registerError"></p>
</div>
<div x-show="registerSuccess" class="bg-green-900/50 border border-green-700 rounded-lg p-4 text-green-200">
<p x-text="registerSuccess"></p>
</div>
<button type="submit"
:disabled="registerLoading"
class="w-full px-6 py-3 bg-primary hover:bg-emerald-700 text-white font-semibold rounded-lg transition-colors disabled:opacity-50">
<span x-show="!registerLoading">Sign Up</span>
<span x-show="registerLoading">Creating account...</span>
</button>
</form>
</div>
</div>
</div>
<!-- Status Message -->
<div x-show="statusMessage" class="mt-6 p-4 rounded-lg"
:class="statusMessageType === 'success' ? 'bg-green-900/50 border border-green-700 text-green-200' : 'bg-blue-900/50 border border-blue-700 text-blue-200'">
<p x-text="statusMessage"></p>
</div>
</div>
</div>
<script>
function membershipPage() {
return {
activeTab: 'login',
loginEmail: '',
loginPassword: '',
loginError: '',
loginLoading: false,
registerName: '',
registerEmail: '',
registerPassword: '',
registerError: '',
registerSuccess: '',
registerLoading: false,
statusMessage: '',
statusMessageType: 'info',
async handleLogin() {
this.loginError = '';
this.loginLoading = true;
const result = await window.MembershipAPI.login(this.loginEmail, this.loginPassword);
this.loginLoading = false;
if (result.success) {
this.statusMessage = 'Login successful! Redirecting...';
this.statusMessageType = 'success';
setTimeout(() => {
window.location.href = '/';
}, 1500);
} else {
this.loginError = result.error || 'Login failed';
}
},
async handleRegister() {
this.registerError = '';
this.registerSuccess = '';
this.registerLoading = true;
const result = await window.MembershipAPI.register(this.registerEmail, this.registerPassword, this.registerName);
this.registerLoading = false;
if (result.success) {
this.registerSuccess = result.message || 'Registration successful! Your membership is pending approval. You will receive an email when approved.';
this.registerEmail = '';
this.registerPassword = '';
this.registerName = '';
} else {
this.registerError = result.error || 'Registration failed';
}
}
}
}
</script>
{{ end }}