3033fc0de2
- Custom Plausible events: Pricing Click, Chat Toggle, Chat Message Sent, FAQ Toggle, Speedrun Nav, Mobile Menu Toggle, Scroll Depth (25/50/75/100%) - Weekly Insights section on launch dashboard with visitor/pageview/bounce rate/visit duration/top pages/top referrers + analysis cards - data/insights.json for storing weekly analytics summaries - scripts/check-plausible-email.py — finds latest Plausible email via IMAP - Cron job: every Mon 09:00 PYST, reads Plausible email, writes insights, analyzes and implements improvements
1158 lines
52 KiB
HTML
1158 lines
52 KiB
HTML
<!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; }
|
||
|
||
/* ── Insights Section ───────────────────────────── */
|
||
.insight-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.insight-card {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-card);
|
||
padding: 20px;
|
||
}
|
||
.insight-card h3 {
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: var(--accent);
|
||
margin-bottom: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.insight-stat {
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
color: var(--text);
|
||
line-height: 1.1;
|
||
}
|
||
.insight-stat-label {
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
margin-top: 4px;
|
||
}
|
||
.insight-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
.insight-list li {
|
||
padding: 6px 0;
|
||
font-size: 13px;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
border-bottom: 1px solid rgba(10,48,96,0.3);
|
||
}
|
||
.insight-list li:last-child { border-bottom: none; }
|
||
.insight-list .val { color: var(--accent); font-weight: 600; margin-left: auto; flex-shrink: 0; }
|
||
.insight-rec {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-left: 3px solid var(--accent);
|
||
border-radius: var(--radius-card);
|
||
padding: 16px 20px;
|
||
margin-top: 12px;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
}
|
||
.insight-rec h4 {
|
||
color: var(--accent);
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
margin-bottom: 6px;
|
||
}
|
||
.insight-empty {
|
||
color: var(--text-muted);
|
||
font-size: 13px;
|
||
text-align: center;
|
||
padding: 40px 0;
|
||
}
|
||
.insight-whatworked { color: var(--success); }
|
||
.insight-whatdidnt { color: var(--danger); }
|
||
.insight-neut { color: var(--warning); }
|
||
.insight-meta {
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
margin-top: 12px;
|
||
text-align: right;
|
||
}
|
||
@media (max-width: 700px) {
|
||
.insight-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</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>
|
||
|
||
<!-- ── WEEKLY INSIGHTS ─────────────────────────── -->
|
||
<div class="section">
|
||
<div class="section-header" onclick="toggleSection('insights-section', this)">
|
||
<h2><span class="icon">📈</span> Weekly SEO & Analytics Insights</h2>
|
||
<span class="section-toggle">▼</span>
|
||
</div>
|
||
<div class="section-body" id="insights-section">
|
||
<div id="insights-content">
|
||
<div class="insight-empty">Waiting for first analytics report. Data populates weekly from Plausible.</div>
|
||
</div>
|
||
<div class="insight-meta" id="insights-meta"></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 & Family Program</h3>
|
||
<p>Invite 20–30 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 & 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 5K–50K 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 1–7):</strong> Friends & Family + SEO foundation + directory listings</li>
|
||
<li><strong>Week 2 (Days 8–14):</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 & 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 & 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 & 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 & 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 & 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 & Rankings</h3>
|
||
<ul>
|
||
<li><span class="bullet">•</span> Indexed in Google (<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 (20–30 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 20–30 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: "3–5 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 (5–10 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;
|
||
}
|
||
|
||
/* ── Render Weekly Insights ─────────────────── */
|
||
function renderInsights(insights) {
|
||
const container = document.getElementById("insights-content");
|
||
const meta = document.getElementById("insights-meta");
|
||
if (!insights || !insights.week) {
|
||
container.innerHTML = '<div class="insight-empty">Waiting for first analytics report. Data populates weekly from Plausible.</div>';
|
||
meta.textContent = "";
|
||
return;
|
||
}
|
||
|
||
const whatWorked = (insights.what_worked || []).map(function (s) {
|
||
return '<li><span class="insight-whatworked">▲</span> ' + s + '</li>';
|
||
}).join("");
|
||
const whatDidnt = (insights.what_didnt || []).map(function (s) {
|
||
return '<li><span class="insight-whatdidnt">▼</span> ' + s + '</li>';
|
||
}).join("");
|
||
const recs = (insights.recommendations || []).map(function (s) {
|
||
return '<li>→ ' + s + '</li>';
|
||
}).join("");
|
||
|
||
let topPages = "", topRefs = "";
|
||
if (insights.top_pages && insights.top_pages.length) {
|
||
topPages = insights.top_pages.map(function (p) {
|
||
return '<li>' + p.page + ' <span class="val">' + p.visits + '</span></li>';
|
||
}).join("");
|
||
}
|
||
if (insights.top_referrers && insights.top_referrers.length) {
|
||
topRefs = insights.top_referrers.map(function (r) {
|
||
return '<li>' + r.source + ' <span class="val">' + r.visits + '</span></li>';
|
||
}).join("");
|
||
}
|
||
|
||
container.innerHTML = '' +
|
||
'<div class="insight-grid">' +
|
||
'<div class="insight-card">' +
|
||
'<h3>👥 Visitors</h3>' +
|
||
'<div class="insight-stat">' + (insights.visitors || 0) + '</div>' +
|
||
'<div class="insight-stat-label">' + (insights.visitors_change || '') + '</div>' +
|
||
'</div>' +
|
||
'<div class="insight-card">' +
|
||
'<h3>📄 Pageviews</h3>' +
|
||
'<div class="insight-stat">' + (insights.pageviews || 0) + '</div>' +
|
||
'<div class="insight-stat-label">' + (insights.pageviews_change || '') + '</div>' +
|
||
'</div>' +
|
||
'<div class="insight-card">' +
|
||
'<h3>🔄 Bounce Rate</h3>' +
|
||
'<div class="insight-stat">' + (insights.bounce_rate || '—') + '</div>' +
|
||
'<div class="insight-stat-label">' + (insights.bounce_rate_change || '') + '</div>' +
|
||
'</div>' +
|
||
'<div class="insight-card">' +
|
||
'<h3>⏱ Visit Duration</h3>' +
|
||
'<div class="insight-stat">' + (insights.visit_duration || '—') + '</div>' +
|
||
'<div class="insight-stat-label">' + (insights.visit_duration_change || '') + '</div>' +
|
||
'</div>' +
|
||
'</div>' +
|
||
'<div class="insight-grid">' +
|
||
'<div class="insight-card">' +
|
||
'<h3>📑 Top Pages</h3>' +
|
||
(topPages ? '<ul class="insight-list">' + topPages + '</ul>' : '<div class="insight-empty" style="padding:20px 0">No data yet</div>') +
|
||
'</div>' +
|
||
'<div class="insight-card">' +
|
||
'<h3>🔗 Top Referrers</h3>' +
|
||
(topRefs ? '<ul class="insight-list">' + topRefs + '</ul>' : '<div class="insight-empty" style="padding:20px 0">No data yet</div>') +
|
||
'</div>' +
|
||
'</div>' +
|
||
(whatWorked ? '<div class="insight-rec"><h4>✅ What Worked</h4><ul class="insight-list">' + whatWorked + '</ul></div>' : '') +
|
||
(whatDidnt ? '<div class="insight-rec"><h4>❌ What Didn\'t</h4><ul class="insight-list">' + whatDidnt + '</ul></div>' : '') +
|
||
(recs ? '<div class="insight-rec" style="border-left-color:var(--warning)"><h4>💡 Recommendations</h4><ul class="insight-list">' + recs + '</ul></div>' : '');
|
||
|
||
meta.textContent = "Updated: " + (insights.updated || "—") + " — Week: " + insights.week;
|
||
}
|
||
|
||
/* ── 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);
|
||
}
|
||
|
||
// Also load insights
|
||
try {
|
||
const ir = await fetch("data/insights.json?_=" + Date.now());
|
||
if (ir.ok) {
|
||
renderInsights(await ir.json());
|
||
}
|
||
} catch(e) {
|
||
// No insights yet — leave placeholder
|
||
}
|
||
}
|
||
|
||
/* ── Init ────────────────────────────────────── */
|
||
renderDay();
|
||
renderNeeds(getNeeds());
|
||
loadData();
|
||
|
||
</script>
|
||
|
||
</body>
|
||
</html> |