Skip to content

Commit ef80c19

Browse files
committed
Add ClawWatch preregistration landing
1 parent e7f5e37 commit ef80c19

File tree

3 files changed

+559
-6
lines changed

3 files changed

+559
-6
lines changed

docs/app.js

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
const SUPABASE_URL = 'https://kvezyhwbkvpyndkaemsw.supabase.co';
2+
const SUPABASE_ANON_KEY = 'sb_publishable_MWtbxI1_Gm_yYTSxHofq2Q_z0nGwItH';
3+
4+
const preregisterButton = document.getElementById('google-preregister-button');
5+
const signOutButton = document.getElementById('google-signout-button');
6+
const statusText = document.getElementById('preregister-status');
7+
const resultBanner = document.getElementById('preregister-result');
8+
const preregisterSection = document.getElementById('preregister');
9+
10+
let supabaseClient = null;
11+
12+
function track(eventName, params = {}) {
13+
if (typeof window.gtag === 'function') {
14+
window.gtag('event', eventName, params);
15+
}
16+
}
17+
18+
function setBanner(message, tone = 'info') {
19+
if (!resultBanner) return;
20+
resultBanner.hidden = false;
21+
resultBanner.dataset.tone = tone;
22+
resultBanner.textContent = message;
23+
}
24+
25+
function clearBanner() {
26+
if (!resultBanner) return;
27+
resultBanner.hidden = true;
28+
resultBanner.textContent = '';
29+
delete resultBanner.dataset.tone;
30+
}
31+
32+
function renderSignedOut() {
33+
clearBanner();
34+
if (statusText) {
35+
statusText.textContent = 'Sign in with Google and we will mark your account for the future install-ready ClawWatch release.';
36+
}
37+
if (preregisterButton) {
38+
preregisterButton.disabled = false;
39+
preregisterButton.innerHTML = '<span class="google-mark">G</span><span>Continue with Google</span>';
40+
}
41+
if (signOutButton) {
42+
signOutButton.hidden = true;
43+
}
44+
}
45+
46+
function renderBusy(message) {
47+
clearBanner();
48+
if (statusText) {
49+
statusText.textContent = message;
50+
}
51+
if (preregisterButton) {
52+
preregisterButton.disabled = true;
53+
preregisterButton.innerHTML = '<span class="google-mark">…</span><span>Working…</span>';
54+
}
55+
if (signOutButton) {
56+
signOutButton.hidden = true;
57+
}
58+
}
59+
60+
function renderRegistered(user) {
61+
const email = user.email || 'your Google account';
62+
if (statusText) {
63+
statusText.textContent = `You are registered for ClawWatch install updates as ${email}.`;
64+
}
65+
if (preregisterButton) {
66+
preregisterButton.disabled = true;
67+
preregisterButton.innerHTML = '<span class="google-mark">✓</span><span>You are on the list</span>';
68+
}
69+
if (signOutButton) {
70+
signOutButton.hidden = false;
71+
}
72+
setBanner('Interest saved. We will contact you when the install-ready release opens.', 'success');
73+
}
74+
75+
async function ensureInterest(user) {
76+
const metadata = user.user_metadata || {};
77+
if (metadata.clawwatch_interest_at) {
78+
renderRegistered(user);
79+
return;
80+
}
81+
82+
renderBusy('Saving your ClawWatch preregistration…');
83+
84+
const { data, error } = await supabaseClient.auth.updateUser({
85+
data: {
86+
clawwatch_interest: true,
87+
clawwatch_interest_at: new Date().toISOString(),
88+
clawwatch_interest_source: window.location.host,
89+
clawwatch_interest_status: 'preregistered'
90+
}
91+
});
92+
93+
if (error) {
94+
if (statusText) {
95+
statusText.textContent = 'Google sign-in worked, but saving your preregistration failed.';
96+
}
97+
if (preregisterButton) {
98+
preregisterButton.disabled = false;
99+
preregisterButton.innerHTML = '<span class="google-mark">G</span><span>Try Google sign-in again</span>';
100+
}
101+
setBanner(error.message, 'error');
102+
return;
103+
}
104+
105+
track('clawwatch_preregister_complete');
106+
renderRegistered(data.user || user);
107+
if (window.location.hash === '#preregister') {
108+
preregisterSection?.scrollIntoView({ behavior: 'smooth', block: 'start' });
109+
}
110+
}
111+
112+
async function startGoogleSignIn() {
113+
clearBanner();
114+
renderBusy('Redirecting to Google sign-in…');
115+
track('clawwatch_preregister_start');
116+
117+
const redirectTo = `${window.location.origin}${window.location.pathname}#preregister`;
118+
const { error } = await supabaseClient.auth.signInWithOAuth({
119+
provider: 'google',
120+
options: {
121+
redirectTo,
122+
queryParams: {
123+
prompt: 'select_account',
124+
access_type: 'offline'
125+
}
126+
}
127+
});
128+
129+
if (error) {
130+
renderSignedOut();
131+
setBanner(error.message, 'error');
132+
}
133+
}
134+
135+
async function signOut() {
136+
const { error } = await supabaseClient.auth.signOut();
137+
if (error) {
138+
setBanner(error.message, 'error');
139+
return;
140+
}
141+
renderSignedOut();
142+
}
143+
144+
async function init() {
145+
const { createClient } = await import('https://esm.sh/@supabase/supabase-js@2');
146+
supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
147+
auth: {
148+
autoRefreshToken: true,
149+
detectSessionInUrl: true,
150+
persistSession: true,
151+
flowType: 'pkce'
152+
}
153+
});
154+
155+
preregisterButton?.addEventListener('click', startGoogleSignIn);
156+
signOutButton?.addEventListener('click', signOut);
157+
158+
const { data, error } = await supabaseClient.auth.getSession();
159+
if (error) {
160+
setBanner(error.message, 'error');
161+
return;
162+
}
163+
164+
if (data.session?.user) {
165+
await ensureInterest(data.session.user);
166+
} else {
167+
renderSignedOut();
168+
}
169+
170+
supabaseClient.auth.onAuthStateChange(async (event, session) => {
171+
if (event === 'SIGNED_IN' && session?.user) {
172+
await ensureInterest(session.user);
173+
return;
174+
}
175+
176+
if (event === 'SIGNED_OUT') {
177+
renderSignedOut();
178+
}
179+
});
180+
}
181+
182+
init().catch((error) => {
183+
renderSignedOut();
184+
setBanner(error.message || 'Failed to initialize preregistration.', 'error');
185+
});

docs/index.html

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<span class="brand-name">ClawWatch.app</span>
3131
</a>
3232
<nav class="top-links">
33+
<a href="#preregister">Preregister</a>
3334
<a href="#latest-news">Latest news</a>
3435
<a href="/news/">News archive</a>
3536
<a href="https://github.com/ThinkOffApp/ClawWatch" target="_blank" rel="noopener noreferrer">GitHub</a>
@@ -71,12 +72,122 @@ <h1>The watch agent that feels your pulse and knows what your team is doing.</h1
7172
</div>
7273

7374
<div class="hero-media">
74-
<div class="video-card">
75-
<video controls preload="metadata" poster="/assets/images/clawwatch/clawwatch-v2-demo-still.png">
76-
<source src="/assets/videos/clawwatch-v2-launch-compressed.mp4" type="video/mp4">
77-
Your browser does not support video playback.
78-
</video>
79-
<p class="media-caption">ClawWatch 2.0 in action on-device. If you upload a YouTube version later, this block can be swapped to an embedded player.</p>
75+
<div class="hero-visual-stack">
76+
<div class="avatar-stage-card">
77+
<div class="avatar-stage-shell">
78+
<div class="avatar-glow avatar-glow-cyan"></div>
79+
<div class="avatar-glow avatar-glow-rose"></div>
80+
<svg class="hero-avatar-svg" viewBox="0 0 200 200" aria-label="Animated ClawWatch lobster avatar" role="img">
81+
<g id="hero-head" transform-origin="100px 102px">
82+
<path fill="#14070B" fill-opacity="0.55" d="M100,105 m -42,0 a 42,58 0 1,0 84,0 a 42,58 0 1,0 -84,0" />
83+
84+
<g id="hero-antenna-left" transform-origin="83px 55px">
85+
<path stroke-width="4" stroke="#B32634" stroke-linecap="round" fill="none" d="M83,55 C74,40 73,27 84,23 C95,20 101,31 98,42" />
86+
<path stroke-width="3" stroke="#C63A45" stroke-linecap="round" fill="none" d="M83,33 C74,30 71,36 74,42" />
87+
</g>
88+
89+
<g id="hero-antenna-right" transform-origin="117px 55px">
90+
<path stroke-width="4" stroke="#B32634" stroke-linecap="round" fill="none" d="M117,55 C126,40 127,27 116,23 C105,20 99,31 102,42" />
91+
<path stroke-width="3" stroke="#C63A45" stroke-linecap="round" fill="none" d="M117,33 C126,30 129,36 126,42" />
92+
</g>
93+
94+
<g id="hero-left-claw" transform-origin="49px 86px">
95+
<path fill="#96111F" d="M58,96 C48,101 46,114 51,123 C56,132 67,136 74,130 C80,124 80,112 74,103 C70,97 64,95 58,96 Z" />
96+
<path fill="#B81C2A" d="M44,80 C31,72 25,58 31,46 C40,32 59,36 66,50 C71,60 67,72 57,78 C61,85 59,95 50,100 C39,106 26,100 24,90 C22,82 30,77 44,80 Z" />
97+
<path fill="#D33A45" fill-opacity="0.3" d="M35,48 C42,40 54,41 59,52 C55,46 47,45 39,49 Z" />
98+
</g>
99+
100+
<g id="hero-right-claw" transform-origin="151px 86px">
101+
<path fill="#96111F" d="M142,96 C152,101 154,114 149,123 C144,132 133,136 126,130 C120,124 120,112 126,103 C130,97 136,95 142,96 Z" />
102+
<path fill="#B81C2A" d="M156,76 C169,70 175,58 169,46 C160,32 141,36 134,50 C129,60 133,72 143,78 C139,87 141,98 150,103 C161,109 174,102 176,90 C178,80 170,75 156,76 Z" />
103+
<path fill="#D33A45" fill-opacity="0.3" d="M165,48 C158,40 146,41 141,52 C145,46 153,45 161,49 Z" />
104+
</g>
105+
106+
<path fill="#C21F2F" d="M100,70 m -26,0 a 26,24 0 1,0 52,0 a 26,24 0 1,0 -52,0" />
107+
<path fill="#8D1420" fill-opacity="0.18" d="M91,83 C96,86 104,86 109,83 C106,89 100,91 100,91 C100,91 94,89 91,83 Z" />
108+
<path fill="#D64D58" fill-opacity="0.28" d="M100,65 m -18,0 a 18,12 0 1,0 36,0 a 18,12 0 1,0 -36,0" />
109+
<path fill="#B11624" d="M100,110 m -22,0 a 22,34 0 1,0 44,0 a 22,34 0 1,0 -44,0" />
110+
<path fill="#8F0E1A" d="M100,135 m -18,0 a 18,12 0 1,0 36,0 a 18,12 0 1,0 -36,0" />
111+
<path fill="#A21220" d="M100,148 m -16,0 a 16,10 0 1,0 32,0 a 16,10 0 1,0 -32,0" />
112+
<path fill="#B81C2A" d="M100,160 m -14,0 a 14,9 0 1,0 28,0 a 14,9 0 1,0 -28,0" />
113+
114+
<path stroke-width="5" stroke="#8A0C18" stroke-linecap="round" fill="none" d="M80,115 C69,120 63,127 60,138" />
115+
<path stroke-width="5" stroke="#8A0C18" stroke-linecap="round" fill="none" d="M84,129 C74,134 69,143 67,152" />
116+
<path stroke-width="5" stroke="#8A0C18" stroke-linecap="round" fill="none" d="M88,142 C80,147 76,156 75,165" />
117+
<path stroke-width="5" stroke="#8A0C18" stroke-linecap="round" fill="none" d="M120,115 C131,120 137,127 140,138" />
118+
<path stroke-width="5" stroke="#8A0C18" stroke-linecap="round" fill="none" d="M116,129 C126,134 131,143 133,152" />
119+
<path stroke-width="5" stroke="#8A0C18" stroke-linecap="round" fill="none" d="M112,142 C120,147 124,156 125,165" />
120+
121+
<path fill="#AA1826" d="M100,170 L88,186 L98,192 L100,186 L102,192 L112,186 Z" />
122+
<path fill="#C52A38" fill-opacity="0.28" d="M100,171 L92,183 L100,180 L108,183 Z" />
123+
124+
<g id="hero-eyes" transform-origin="100px 70px">
125+
<path fill="#FFF7F0" d="M86,66 m -9,0 a 9,11 0 1,0 18,0 a 9,11 0 1,0 -18,0" />
126+
<path fill="#6A1720" d="M88,67 m -4,0 a 4,6 0 1,0 8,0 a 4,6 0 1,0 -8,0" />
127+
<path fill="#FFFFFF" d="M90,63 m -1.6,0 a 1.6,1.6 0 1,0 3.2,0 a 1.6,1.6 0 1,0 -3.2,0" />
128+
<path fill="#FFF7F0" d="M114,66 m -9,0 a 9,11 0 1,0 18,0 a 9,11 0 1,0 -18,0" />
129+
<path fill="#6A1720" d="M112,67 m -4,0 a 4,6 0 1,0 8,0 a 4,6 0 1,0 -8,0" />
130+
<path fill="#FFFFFF" d="M114,63 m -1.6,0 a 1.6,1.6 0 1,0 3.2,0 a 1.6,1.6 0 1,0 -3.2,0" />
131+
</g>
132+
133+
<g id="hero-eye-glow" transform-origin="100px 68px">
134+
<ellipse cx="86" cy="66" rx="15" ry="17" fill="#8CE8FF" fill-opacity="0.16"></ellipse>
135+
<ellipse cx="114" cy="66" rx="15" ry="17" fill="#8CE8FF" fill-opacity="0.16"></ellipse>
136+
</g>
137+
138+
<g id="hero-mouth" transform-origin="100px 83px">
139+
<path stroke="#71111B" stroke-width="4" stroke-linecap="round" fill="none" d="M90,84 Q100,90 110,84" />
140+
</g>
141+
</g>
142+
</svg>
143+
</div>
144+
<p class="media-caption">The real ClawWatch avatar system from the watch app, adapted here as a live hero visual.</p>
145+
</div>
146+
147+
<div class="video-card">
148+
<video controls preload="metadata" poster="/assets/images/clawwatch/clawwatch-v2-demo-still.png">
149+
<source src="/assets/videos/clawwatch-v2-launch-compressed.mp4" type="video/mp4">
150+
Your browser does not support video playback.
151+
</video>
152+
<p class="media-caption">ClawWatch 2.0 in action on-device. If you upload a YouTube version later, this block can be swapped to an embedded player.</p>
153+
</div>
154+
</div>
155+
</div>
156+
</section>
157+
158+
<section id="preregister" class="preregister-panel">
159+
<div class="section-heading">
160+
<span class="section-kicker">Early access</span>
161+
<h2>Signal that you want one-tap ClawWatch installs</h2>
162+
<p>ClawWatch is open source today, but the consumer install flow is still on the way.</p>
163+
</div>
164+
<div class="preregister-grid">
165+
<article class="preregister-copy">
166+
<h3>Open source now. Install-button version next.</h3>
167+
<p>ClawWatch is already real and open source, but getting it onto a fresh watch still means compiling the app and using developer mode.</p>
168+
<p>We are building the version every watch owner can install with a button. Tap below to signal your interest and we will let you know when it is ready.</p>
169+
<div class="preregister-points">
170+
<span>Google sign-in only</span>
171+
<span>No public profile created yet</span>
172+
<span>Shared ThinkOff / Ant Farm identity system</span>
173+
</div>
174+
</article>
175+
176+
<div class="preregister-card">
177+
<div class="auth-pill">Google preregistration</div>
178+
<h3>Get on the ClawWatch install list</h3>
179+
<p id="preregister-status" class="preregister-status">
180+
Sign in with Google and we will mark your account for the future install-ready ClawWatch release.
181+
</p>
182+
<button id="google-preregister-button" class="google-button" type="button">
183+
<span class="google-mark">G</span>
184+
<span>Continue with Google</span>
185+
</button>
186+
<button id="google-signout-button" class="text-link-button" type="button" hidden>Sign out</button>
187+
<div id="preregister-result" class="status-banner" hidden></div>
188+
<p class="preregister-note">
189+
This stores early-access interest inside the shared ThinkOff / Ant Farm Supabase auth system. It does not create a public social profile until you actually join the services.
190+
</p>
80191
</div>
81192
</div>
82193
</section>
@@ -214,5 +325,6 @@ <h2>ClawWatch.app is a ThinkOff.io service</h2>
214325
<p>© 2026 ThinkOff Ecosystem</p>
215326
</footer>
216327
</div>
328+
<script type="module" src="/app.js"></script>
217329
</body>
218330
</html>

0 commit comments

Comments
 (0)