Files
derez.ai/launch-dashboard.html
T
Oliver 3d1c9be1d0 prelaunch: create launch dashboard + daily cron updates
- New launch-dashboard.html: KPI grid, 14-day action plan, strategy
  overview, items-needed checklist, daily routine, cron schedule
- Nav link '🚀 Launch' added to main site header
- data/launch-status.json: data file updated daily at 03:00 by cron
- scripts/update-dashboard.py: advances day, probes KPIs, marks
  today's actions
- cron jobs configured:
  • 03:00 daily — dashboard update script + git commit/push
  • 09:00 daily — 1-hour working session reminder
2026-06-09 14:03:44 -03:00

977 lines
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex, nofollow" />
<title>Prelaunch Dashboard — Derez.ai</title>
<style>
/* ═══════════════════════════════════════════════════════════════
DESIGN TOKENS (matches derez.ai design system)
═══════════════════════════════════════════════════════════════ */
:root {
--bg: #000810;
--bg-card: #010f20;
--bg-inner: #000d1a;
--bg-hover: #001a30;
--bg-input: #000813;
--border: #0a3060;
--border-hi: rgba(0,245,255,0.45);
--accent: #00f5ff;
--accent-dim: #002535;
--danger: #ff3b3b;
--danger-dim: #3b0000;
--success: #00ff88;
--warning: #ffaa00;
--text: #c8f0ff;
--text-muted: #2e6a80;
--radius: 8px;
--radius-card: 18px;
--shadow: 0 0 0 1px rgba(0,245,255,0.25), 0 0 60px rgba(0,245,255,0.12), 0 28px 72px rgba(0,0,0,0.9);
--max-width: 1200px;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; }
body {
font-family: "Courier New", Courier, monospace;
background: var(--bg);
color: var(--text);
font-size: 14px;
line-height: 1.6;
overflow-x: hidden;
padding-top: 70px;
}
body::before {
content: "";
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 25% 20%, rgba(0,245,255,0.06) 0%, transparent 70%),
radial-gradient(ellipse 50% 60% at 75% 80%, rgba(0,200,255,0.04) 0%, transparent 70%);
pointer-events: none;
z-index: 0;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.container {
max-width: var(--max-width);
margin: 0 auto;
padding: 0 24px;
position: relative;
z-index: 1;
}
/* ── Header ──────────────────────────────── */
.site-header {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
background: rgba(0,8,16,0.85);
backdrop-filter: blur(14px);
border-bottom: 1px solid var(--border-hi);
height: 60px;
display: flex;
align-items: center;
}
.site-header .container {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.site-logo { display: flex; align-items: center; flex-shrink: 0; }
.site-logo-img { display: block; height: 30px; width: auto; }
.nav-links {
display: flex;
align-items: center;
gap: 8px;
list-style: none;
}
.nav-links a {
padding: 6px 14px;
border-radius: var(--radius);
color: var(--text-muted);
font-size: 13px;
font-weight: 500;
transition: color 0.15s, background 0.15s;
}
.nav-links a:hover {
color: var(--text);
background: var(--bg-hover);
text-decoration: none;
}
.nav-links a.active {
color: var(--accent);
background: var(--accent-dim);
}
/* ── Status Banner ───────────────────────── */
.status-banner {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 28px 32px;
margin-bottom: 28px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 16px;
}
.status-banner h1 {
font-size: 22px;
color: var(--accent);
font-weight: 700;
}
.status-banner .day-indicator {
font-size: 13px;
color: var(--text-muted);
margin-top: 4px;
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 16px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
}
.status-badge.live {
background: rgba(0,255,136,0.12);
color: var(--success);
border: 1px solid rgba(0,255,136,0.3);
}
.status-badge.paused {
background: rgba(255,170,0,0.12);
color: var(--warning);
border: 1px solid rgba(255,170,0,0.3);
}
.last-updated {
font-size: 11px;
color: var(--text-muted);
}
/* ── Section ─────────────────────────────── */
.section {
margin-bottom: 28px;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
cursor: pointer;
user-select: none;
}
.section-header h2 {
font-size: 16px;
font-weight: 700;
color: var(--text);
display: flex;
align-items: center;
gap: 10px;
}
.section-header h2 .icon { color: var(--accent); }
.section-toggle {
color: var(--text-muted);
font-size: 18px;
transition: transform 0.2s;
}
.section-toggle.collapsed {
transform: rotate(-90deg);
}
.section-body {
transition: max-height 0.3s ease, opacity 0.2s ease;
overflow: hidden;
max-height: 20000px;
opacity: 1;
}
.section-body.hidden {
max-height: 0;
opacity: 0;
padding: 0;
}
/* ── KPI Grid ────────────────────────────── */
.kpi-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
.kpi-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 20px;
}
.kpi-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
margin-bottom: 6px;
}
.kpi-value {
font-size: 28px;
font-weight: 700;
color: var(--text);
}
.kpi-value .target {
font-size: 16px;
color: var(--text-muted);
font-weight: 400;
}
.kpi-bar {
margin-top: 10px;
height: 4px;
background: var(--bg-inner);
border-radius: 2px;
overflow: hidden;
}
.kpi-bar-fill {
height: 100%;
border-radius: 2px;
transition: width 0.6s ease;
}
.kpi-bar-fill.green { background: var(--success); }
.kpi-bar-fill.blue { background: var(--accent); }
.kpi-bar-fill.yellow { background: var(--warning); }
.kpi-bar-fill.red { background: var(--danger); }
.kpi-sub {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
}
/* ── Action Plan Table ───────────────────── */
.action-table {
width: 100%;
border-collapse: collapse;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
overflow: hidden;
font-size: 13px;
}
.action-table th {
background: var(--bg-inner);
padding: 12px 16px;
text-align: left;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
border-bottom: 1px solid var(--border);
}
.action-table td {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
vertical-align: top;
}
.action-table tr:last-child td { border-bottom: none; }
.action-table td.day-col {
color: var(--accent);
font-weight: 600;
white-space: nowrap;
width: 60px;
}
.action-table td.status-col {
white-space: nowrap;
width: 110px;
}
.action-table td.owner-col { white-space: nowrap; width: 80px; }
.status-tag {
display: inline-block;
padding: 2px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
}
.status-tag.done { background: rgba(0,255,136,0.12); color: var(--success); border: 1px solid rgba(0,255,136,0.25); }
.status-tag.in-progress { background: rgba(0,245,255,0.12); color: var(--accent); border: 1px solid rgba(0,245,255,0.25); }
.status-tag.pending { background: rgba(46,106,128,0.15); color: var(--text-muted); border: 1px solid transparent; }
.status-tag.blocked { background: rgba(255,59,59,0.12); color: var(--danger); border: 1px solid rgba(255,59,59,0.25); }
.status-tag.today { background: rgba(255,170,0,0.15); color: var(--warning); border: 1px solid rgba(255,170,0,0.3); }
/* ── Card Layout ─────────────────────────── */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.grid-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
}
.info-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 24px;
}
.info-card h3 {
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--accent);
margin-bottom: 14px;
display: flex;
align-items: center;
gap: 8px;
}
.info-card ul {
list-style: none;
padding: 0;
}
.info-card li {
padding: 8px 0;
border-bottom: 1px solid rgba(10,48,96,0.4);
font-size: 13px;
display: flex;
align-items: flex-start;
gap: 8px;
}
.info-card li:last-child { border-bottom: none; }
.info-card li .bullet { color: var(--accent); flex-shrink: 0; }
.need-checkbox {
width: 16px;
height: 16px;
border: 2px solid var(--border);
border-radius: 4px;
flex-shrink: 0;
margin-top: 2px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.15s;
}
.need-checkbox.checked {
background: var(--success);
border-color: var(--success);
}
.need-checkbox.checked::after {
content: "✓";
color: var(--bg);
font-size: 12px;
font-weight: 700;
}
.need-item.done .need-text {
color: var(--text-muted);
text-decoration: line-through;
}
/* ── Strategy Section ────────────────────── */
.strategy-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 24px;
margin-bottom: 16px;
}
.strategy-card h3 {
font-size: 14px;
color: var(--accent);
margin-bottom: 10px;
}
.strategy-card p, .strategy-card ul {
font-size: 13px;
color: var(--text);
line-height: 1.7;
}
.strategy-card ul {
padding-left: 20px;
list-style: none;
}
.strategy-card ul li::before {
content: " ";
color: var(--accent);
}
/* ── Cron / Daily Routine ────────────────── */
.cron-timeline {
position: relative;
padding-left: 28px;
}
.cron-timeline::before {
content: "";
position: absolute;
left: 8px;
top: 4px;
bottom: 4px;
width: 2px;
background: var(--border);
}
.cron-item {
position: relative;
padding: 6px 0 16px;
}
.cron-item::before {
content: "";
position: absolute;
left: -24px;
top: 10px;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--bg-card);
border: 2px solid var(--accent);
}
.cron-item.daily-hour::before {
background: var(--accent);
box-shadow: 0 0 12px rgba(0,245,255,0.5);
}
.cron-time {
font-size: 12px;
color: var(--accent);
font-weight: 600;
}
.cron-title {
font-size: 14px;
font-weight: 600;
color: var(--text);
}
.cron-desc {
font-size: 12px;
color: var(--text-muted);
margin-top: 2px;
}
/* ── Responsive ──────────────────────────── */
@media (max-width: 900px) {
.grid-2, .grid-3 { grid-template-columns: 1fr; }
.kpi-grid { grid-template-columns: repeat(2, 1fr); }
.status-banner { flex-direction: column; align-items: flex-start; }
.action-table { font-size: 12px; }
.action-table th, .action-table td { padding: 8px 10px; }
}
@media (max-width: 500px) {
.kpi-grid { grid-template-columns: 1fr; }
.nav-links { display: none; }
}
.tag { color: var(--text-muted); font-size: 12px; }
.highlight { color: var(--accent); }
.success-text { color: var(--success); }
.warn-text { color: var(--warning); }
.danger-text { color: var(--danger); }
.btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 20px;
border-radius: var(--radius);
font-size: 13px;
font-weight: 600;
cursor: pointer;
border: none;
transition: all 0.15s;
font-family: inherit;
}
.btn-primary {
background: var(--accent);
color: var(--bg);
}
.btn-primary:hover { opacity: 0.85; text-decoration: none; }
.btn-ghost {
background: transparent;
color: var(--text);
border: 1px solid var(--border);
}
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
</style>
</head>
<body>
<!-- ── HEADER ──────────────────────────────────── -->
<header class="site-header">
<div class="container">
<a href="/" class="site-logo">
<img src="assets/images/logo.svg" alt="Derez.ai" width="132" height="34" class="site-logo-img" />
</a>
<ul class="nav-links" id="nav-links">
<li><a href="/">Home</a></li>
<li><a href="/#pricing">Pricing</a></li>
<li><a href="/#faq">FAQ</a></li>
<li><a href="launch-dashboard.html" class="active">🚀 Launch Dashboard</a></li>
</ul>
</div>
</header>
<div class="container">
<!-- ── STATUS BANNER ─────────────────────────── -->
<div class="status-banner" id="status-banner">
<div>
<h1>🚀 Prelaunch Dashboard</h1>
<div class="day-indicator" id="day-indicator">Day 1 of 14 — Counting down to launch</div>
</div>
<div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap;">
<span class="status-badge live" id="campaign-status">● Campaign Active</span>
<span class="last-updated" id="last-updated">Updated: just now</span>
</div>
</div>
<!-- ── KPI DASHBOARD ─────────────────────────── -->
<div class="section">
<div class="section-header" onclick="toggleSection('kpi-section', this)">
<h2><span class="icon">📊</span> Key Performance Indicators</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="kpi-section">
<div class="kpi-grid" id="kpi-grid">
<!-- populated by JS -->
</div>
</div>
</div>
<!-- ── ACTION PLAN ───────────────────────────── -->
<div class="section">
<div class="section-header" onclick="toggleSection('actions-section', this)">
<h2><span class="icon">📋</span> 14-Day Action Plan</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="actions-section">
<table class="action-table">
<thead>
<tr>
<th>Day</th>
<th>Action</th>
<th>Owner</th>
<th>Status</th>
</tr>
</thead>
<tbody id="action-rows">
<!-- populated by JS -->
</tbody>
</table>
</div>
</div>
<!-- ── STRATEGY SUMMARY ──────────────────────── -->
<div class="section">
<div class="section-header" onclick="toggleSection('strategy-section', this)">
<h2><span class="icon">🎯</span> Strategy Overview</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="strategy-section">
<div class="grid-2">
<div>
<div class="strategy-card">
<h3>👨‍👩‍👧‍👦 Friends &amp; Family Program</h3>
<p>Invite 2030 trusted contacts to beta test at discounted rates. Each gets:</p>
<ul>
<li>50% off first month ($4.75/mo instead of $9.50)</li>
<li>$5 OpenRouter credits with referral coupon code</li>
<li>Priority support via chat</li>
<li>Direct feedback channel (Telegram group)</li>
<li>NPS survey after 7 days</li>
</ul>
<p style="margin-top:8px;"><span class="highlight">Goal:</span> 20 signups, 5 detailed testimonials</p>
</div>
<div class="strategy-card">
<h3>🔗 Backlinks &amp; SEO</h3>
<p>Three-pronged approach:</p>
<ul>
<li><strong>Product Hunt / directories:</strong> List on Product Hunt, BetaList, AlternativeTo, G2</li>
<li><strong>Guest posts:</strong> Pitch "How to deploy AI agents in 5 minutes" to AI/DevOps blogs</li>
<li><strong>HARO / Qwoted:</strong> Respond to journalist queries about AI hosting</li>
<li><strong>Open source:</strong> Link back from Hermes Agent docs / GitHub (if accepted)</li>
</ul>
<p style="margin-top:8px;"><span class="highlight">Goal:</span> 10 quality backlinks, rank in top 5 for "AI agent hosting"</p>
</div>
</div>
<div>
<div class="strategy-card">
<h3>📢 Influencer Outreach</h3>
<p>Target micro-influencers in AI/ML/DevOps spaces:</p>
<ul>
<li><strong>Where to find them:</strong> YouTube (AI channels 5K50K subs), X/Twitter (AI builders), LinkedIn (DevOps thought leaders), Discord communities (OpenRouter, Nous Research, local LLM)</li>
<li><strong>Offer:</strong> Free 3-month agent instance + affiliate commission (10% recurring)</li>
<li><strong>Pitch angle:</strong> "Your followers can deploy their own AI agent in 5 minutes — no DevOps needed"</li>
<li><strong>Toolkit:</strong> One-pager, demo video, coupon codes, pre-written tweets</li>
</ul>
<p style="margin-top:8px;"><span class="highlight">Goal:</span> 5 confirmed partnerships, 3 social mentions</p>
</div>
<div class="strategy-card">
<h3>📈 Launch Strategy</h3>
<ul>
<li><strong>Week 1 (Days 17):</strong> Friends &amp; Family + SEO foundation + directory listings</li>
<li><strong>Week 2 (Days 814):</strong> Influencer push + guest posts + social content sprint</li>
<li><strong>Launch day:</strong> Product Hunt + email blast + social storm + all influencers post</li>
<li><strong>Tracking:</strong> UTM on every link, Google Search Console, weekly KPI review</li>
</ul>
<p style="margin-top:8px;"><span class="highlight">Goal:</span> 500 unique visitors, 50 email captures, 20 paid signups by launch</p>
</div>
</div>
</div>
</div>
</div>
<!-- ── DAILY ROUTINE & CRON ──────────────────── -->
<div class="section">
<div class="section-header" onclick="toggleSection('routine-section', this)">
<h2><span class="icon"></span> Daily Routine &amp; Cron Schedule</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="routine-section">
<div class="grid-2">
<div class="info-card">
<h3>⏱ Our Daily 1-Hour Block</h3>
<p style="font-size:13px;margin-bottom:12px;">Every day at <strong class="highlight">09:00 PYST</strong> (America/Asuncion) — 60 minutes.</p>
<div class="cron-timeline">
<div class="cron-item daily-hour">
<div class="cron-time">09:00 09:05</div>
<div class="cron-title">Dashboard review</div>
<div class="cron-desc">Read the 03:00 auto-update, review KPIs, see what changed overnight</div>
</div>
<div class="cron-item daily-hour">
<div class="cron-time">09:05 09:15</div>
<div class="cron-title">Status sync</div>
<div class="cron-desc">Reply to dashboard comments, update action statuses, unblock items</div>
</div>
<div class="cron-item daily-hour">
<div class="cron-time">09:15 09:45</div>
<div class="cron-title">Deep work</div>
<div class="cron-desc">Execute today's priority actions (outreach emails, content drafts, influencer DMs)</div>
</div>
<div class="cron-item daily-hour">
<div class="cron-time">09:45 10:00</div>
<div class="cron-title">Wrap &amp; queue</div>
<div class="cron-desc">Set tomorrow's priorities, queue tasks for the cron, log progress</div>
</div>
</div>
</div>
<div class="info-card">
<h3>🤖 Automated Cron Jobs</h3>
<div class="cron-timeline">
<div class="cron-item">
<div class="cron-time">03:00 PYST</div>
<div class="cron-title">📊 Dashboard Update</div>
<div class="cron-desc">Auto-generates KPI snapshot: signups, traffic, backlinks, influencer status. Writes to <code>data/launch-status.json</code> for Oliver to review at 09:00.</div>
</div>
<div class="cron-item">
<div class="cron-time">03:10 PYST</div>
<div class="cron-title">📈 SEO &amp; Rank Tracker</div>
<div class="cron-desc">Check Google Search Console for new impressions/clicks. Log keyword positions for target terms. Alert on +50% change.</div>
</div>
<div class="cron-item">
<div class="cron-time">03:20 PYST</div>
<div class="cron-title">🔗 Backlink Monitor</div>
<div class="cron-desc">Check Ahrefs/Moz API for new backlinks. Update backlink KPI count. Flag new domains referring.</div>
</div>
<div class="cron-item">
<div class="cron-time">03:30 PYST</div>
<div class="cron-title">💬 Social Monitor</div>
<div class="cron-desc">Check X/Twitter for brand mentions, reply to inquiries, log sentiment.</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ── ITEMS NEEDED ──────────────────────────── -->
<div class="section">
<div class="section-header" onclick="toggleSection('items-section', this)">
<h2><span class="icon">📦</span> Items Needed From You</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="items-section">
<div class="info-card">
<p style="margin-bottom:16px;font-size:13px;color:var(--text-muted);">
Check off items as you deliver them. This list is saved in your browser.
</p>
<ul id="needs-list">
<!-- populated by JS -->
</ul>
</div>
</div>
</div>
<!-- ── OUTCOME TARGETS ───────────────────────── -->
<div class="section">
<div class="section-header" onclick="toggleSection('targets-section', this)">
<h2><span class="icon">🏆</span> 14-Day Targets</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="targets-section">
<div class="grid-3">
<div class="info-card">
<h3>👥 Friends &amp; Family</h3>
<ul>
<li><span class="bullet"></span> 20 beta signups</li>
<li><span class="bullet"></span> 5 video testimonials</li>
<li><span class="bullet"></span> 8 NPS responses (target: 8+)</li>
<li><span class="bullet"></span> 3 bug/UX improvement reports</li>
</ul>
</div>
<div class="info-card">
<h3>📣 Reach &amp; Visibility</h3>
<ul>
<li><span class="bullet"></span> 5 influencer partnerships</li>
<li><span class="bullet"></span> 10 quality backlinks</li>
<li><span class="bullet"></span> 500 unique landing page visitors</li>
<li><span class="bullet"></span> 50 email list signups</li>
</ul>
</div>
<div class="info-card">
<h3>📈 SEO &amp; Rankings</h3>
<ul>
<li><span class="bullet"></span> Indexed in Google (&lt;7 days)</li>
<li><span class="bullet"></span> Top 5 for "AI agent hosting"</li>
<li><span class="bullet"></span> 3 directory listings live</li>
<li><span class="bullet"></span> 2 guest posts published</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<br><br>
<script>
/* ═══════════════════════════════════════════════════════════════════
DATA — strategy & action plan
═══════════════════════════════════════════════════════════════════ */
const DAY_ONE = new Date("2026-06-09T00:00:00-04:00"); // today
const KPIS = [
{ id: "signups", label: "Friends & Family Signups", current: 0, target: 20, unit: "", color: "green" },
{ id: "visitors", label: "Landing Page Visitors", current: 0, target: 500, unit: "", color: "blue" },
{ id: "emails", label: "Email Captures", current: 0, target: 50, unit: "", color: "blue" },
{ id: "backlinks", label: "Quality Backlinks", current: 0, target: 10, unit: "", color: "yellow" },
{ id: "influencers", label: "Influencer Partnerships", current: 0, target: 5, unit: "", color: "green" },
{ id: "testimonials", label: "Testimonials Collected", current: 0, target: 5, unit: "", color: "green" },
{ id: "impressions", label: "Google Impressions (7d)", current: 0, target: 2000, unit: "", color: "blue" },
{ id: "directory", label: "Directory Listings", current: 0, target: 3, unit: "", color: "yellow" },
];
const ACTIONS = [
// ── Day 1 ──
{ day: 1, action: "Set up friends & family invite list (2030 contacts)", owner: "Oliver", status: "today" },
{ day: 1, action: "Generate & configure referral coupon codes in Stripe", owner: "Mint", status: "today" },
{ day: 1, action: "Create prelaunch landing page / launch-dashboard", owner: "Mint", status: "done" },
{ day: 1, action: "Submit to Google Search Console + Bing Webmaster Tools", owner: "Mint", status: "pending" },
{ day: 1, action: "Set up UTM tracking for all campaign links", owner: "Mint", status: "pending" },
// ── Day 2 ──
{ day: 2, action: "Send friends & family invitation emails", owner: "Oliver", status: "pending" },
{ day: 2, action: "Create F&F onboarding docs (1-pager + video)", owner: "Mint", status: "pending" },
{ day: 2, action: "Set up dedicated F&F Telegram/Discord group", owner: "Mint", status: "pending" },
{ day: 2, action: "Submit to Product Hunt — create draft listing", owner: "Oliver", status: "pending" },
// ── Day 3 ──
{ day: 3, action: "Start influencer research: compile list of 30 targets", owner: "Oliver", status: "pending" },
{ day: 3, action: "Write influencer outreach email template", owner: "Mint", status: "pending" },
{ day: 3, action: "Submit to BetaList, AlternativeTo, G2 directories", owner: "Mint", status: "pending" },
// ── Day 4 ──
{ day: 4, action: "Send first batch of influencer DMs/emails (top 10)", owner: "Oliver", status: "pending" },
{ day: 4, action: "Pitch guest post: 'Deploy AI agents in 5 min' to 5 blogs", owner: "Mint", status: "pending" },
{ day: 4, action: "Set up HARO / Qwoted / Connectively alerts", owner: "Mint", status: "pending" },
{ day: 4, action: "First friends & family onboardings go live", owner: "Oliver", status: "pending" },
// ── Day 5 ──
{ day: 5, action: "Follow up on influencer DMs (non-responders)", owner: "Oliver", status: "pending" },
{ day: 5, action: "Create 3 social posts (X/LinkedIn) about launch", owner: "Mint", status: "pending" },
{ day: 5, action: "Send F&F NPS survey #1", owner: "Mint", status: "pending" },
// ── Day 6 ──
{ day: 6, action: "Publish first social proof tweet: F&F testimonial", owner: "Oliver", status: "pending" },
{ day: 6, action: "Write blog post: 'Why I built Derez.ai'", owner: "Oliver", status: "pending" },
{ day: 6, action: "Check Google indexing status — escalate if not indexed", owner: "Mint", status: "pending" },
// ── Day 7 ──
{ day: 7, action: "Mid-campaign review: KPI check vs targets", owner: "Both", status: "pending" },
{ day: 7, action: "Second batch influencer outreach (next 10 targets)", owner: "Oliver", status: "pending" },
{ day: 7, action: "Polish Product Hunt listing with F&F feedback quotes", owner: "Mint", status: "pending" },
// ── Day 8 ──
{ day: 8, action: "Guest post #1 published — monitor backlinks", owner: "Mint", status: "pending" },
{ day: 8, action: "Send affiliate program details to confirmed influencers", owner: "Oliver", status: "pending" },
{ day: 8, action: "Content sprint: write 5 more social posts", owner: "Mint", status: "pending" },
// ── Day 9 ──
{ day: 9, action: "Follow up guest post pitches — pitch 5 more blogs", owner: "Mint", status: "pending" },
{ day: 9, action: "Record demo video (screen recording 3 min)", owner: "Oliver", status: "pending" },
{ day: 9, action: "Email list: send first newsletter teaser", owner: "Mint", status: "pending" },
// ── Day 10 ──
{ day: 10, action: "Launch day prep: schedule Product Hunt live", owner: "Oliver", status: "pending" },
{ day: 10, action: "Coordinate influencer posts for launch day", owner: "Both", status: "pending" },
{ day: 10, action: "Prepare launch day email blast", owner: "Mint", status: "pending" },
// ── Day 11 ──
{ day: 11, action: "🚀 LAUNCH DAY — Product Hunt go-live", owner: "Both", status: "pending" },
{ day: 11, action: "Social storm: all platforms post simultaneously", owner: "Oliver", status: "pending" },
{ day: 11, action: "Influencers publish their content", owner: "Influencers", status: "pending" },
// ── Day 12 ──
{ day: 12, action: "Post-launch: reply to every Product Hunt comment", owner: "Oliver", status: "pending" },
{ day: 12, action: "Monitor signups & traffic spike — adjust messaging", owner: "Mint", status: "pending" },
// ── Day 13 ──
{ day: 13, action: "Follow up on all pending leads from launch", owner: "Oliver", status: "pending" },
{ day: 13, action: "Send thank-you emails to F&F + influencers", owner: "Mint", status: "pending" },
// ── Day 14 ──
{ day: 14, action: "Final KPI review & retrospective", owner: "Both", status: "pending" },
{ day: 14, action: "Compile lessons learned doc", owner: "Mint", status: "pending" },
{ day: 14, action: "Decide: full public launch or iterate based on data", owner: "Oliver", status: "pending" },
];
const NEEDS = [
{ id: "n1", text: "List of 2030 friends & family contacts (name + email)", done: false },
{ id: "n2", text: "Target influencer list (30 names, handles/emails, niche, follower count)", done: false },
{ id: "n3", text: "Influencer partnership budget (free plans? cash? affiliate %?)", done: false },
{ id: "n4", text: "OpenRouter coupon codes for referral program (5 needed)", done: false },
{ id: "n5", text: "Product demo video (3 min screen recording) or go-ahead to record one", done: false },
{ id: "n6", text: "35 screenshots of dashboard / agent setup flow", done: false },
{ id: "n7", text: "Social media account access (X/Twitter + LinkedIn posting access)", done: false },
{ id: "n8", text: "Personal story / 'Why I built Derez.ai' notes for blog post", done: false },
{ id: "n9", text: "Competitor list (510 names) for backlink gap analysis", done: false },
{ id: "n10", text: "Price anchoring: confirm if F&F get special discount or free month", done: false },
{ id: "n11", text: "HARO / Qwoted / Connectively account (or credentials to set up)", done: false },
{ id: "n12", text: "Google Search Console access for derez.ai", done: true },
];
/* ═══════════════════════════════════════════════════════════════════
RENDER
═══════════════════════════════════════════════════════════════════ */
function getDayNumber() {
const now = new Date();
const diff = Math.floor((now - DAY_ONE) / (1000 * 60 * 60 * 24));
return Math.max(1, Math.min(14, diff + 1));
}
function renderDay() {
const day = getDayNumber();
document.getElementById("day-indicator").textContent =
`Day ${day} of 14 — ${14 - day} days until launch`;
}
function renderKPIs(kpis) {
const grid = document.getElementById("kpi-grid");
grid.innerHTML = kpis.map(k => {
const pct = k.target > 0 ? Math.min(100, Math.round((k.current / k.target) * 100)) : 0;
const barColor = k.color || "blue";
return `
<div class="kpi-card">
<div class="kpi-label">${k.label}</div>
<div class="kpi-value">${k.current} <span class="target">/ ${k.target}${k.unit}</span></div>
<div class="kpi-bar"><div class="kpi-bar-fill ${barColor}" style="width:${pct}%"></div></div>
<div class="kpi-sub">${pct}% of target</div>
</div>
`;
}).join("");
}
function renderActions(actions) {
const tbody = document.getElementById("action-rows");
const day = getDayNumber();
tbody.innerHTML = actions.map(a => {
let statusClass = a.status;
let statusLabel = a.status.replace("-", " ").replace(/\b\w/g, c => c.toUpperCase());
return `
<tr>
<td class="day-col">Day ${a.day}</td>
<td>${a.action}</td>
<td class="owner-col">${a.owner}</td>
<td class="status-col"><span class="status-tag ${statusClass}">${statusLabel}</span></td>
</tr>
`;
}).join("");
}
function renderNeeds(needs) {
const list = document.getElementById("needs-list");
list.innerHTML = needs.map(n => `
<li class="need-item${n.done ? ' done' : ''}">
<span class="need-checkbox${n.done ? ' checked' : ''}" data-id="${n.id}" onclick="toggleNeed('${n.id}')"></span>
<span class="need-text">${n.text}</span>
</li>
`).join("");
}
function toggleNeed(id) {
const needs = getNeeds();
const idx = needs.findIndex(n => n.id === id);
if (idx === -1) return;
needs[idx].done = !needs[idx].done;
localStorage.setItem("derez-needs", JSON.stringify(needs));
renderNeeds(needs);
}
function getNeeds() {
try {
const saved = localStorage.getItem("derez-needs");
if (saved) {
const parsed = JSON.parse(saved);
// Merge with defaults — keep done states
return NEEDS.map(def => {
const s = parsed.find(p => p.id === def.id);
return s ? { ...def, done: s.done } : def;
});
}
} catch(e) {}
return NEEDS;
}
/* ── Toggle sections ─────────────────────────── */
function toggleSection(id, header) {
const body = document.getElementById(id);
const toggle = header.querySelector(".section-toggle");
body.classList.toggle("hidden");
toggle.classList.toggle("collapsed");
}
/* ── Load data from JSON (if available) ───────── */
async function loadData() {
try {
const res = await fetch("data/launch-status.json?_=" + Date.now());
if (!res.ok) throw new Error("No data file yet");
const data = await res.json();
if (data.kpis) {
// Merge server KPIs with our defaults
const merged = KPIS.map(k => {
const s = data.kpis.find(d => d.id === k.id);
return s ? { ...k, current: s.current } : k;
});
renderKPIs(merged);
}
if (data.actions) {
const merged = ACTIONS.map(a => {
const s = data.actions.find(d => d.day === a.day && d.action === a.action);
return s ? { ...a, status: s.status } : a;
});
renderActions(merged);
}
if (data.updated) {
document.getElementById("last-updated").textContent =
"Updated: " + new Date(data.updated).toLocaleString("en-US", {
month: "short", day: "numeric", hour: "2-digit", minute: "2-digit"
});
}
} catch(e) {
// No data file yet — render defaults
renderKPIs(KPIS);
renderActions(ACTIONS);
}
}
/* ── Init ────────────────────────────────────── */
renderDay();
renderNeeds(getNeeds());
loadData();
</script>
</body>
</html>