Files
derez.ai/operations.html
T

1198 lines
49 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>Daily Brief — Derez.ai</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #08080c;
--bg-card: #0e0e14;
--bg-inner: #0a0a0f;
--bg-hover: #16161f;
--border: #1e1e2a;
--border-hi: #2a2a3a;
--accent: #6c8cff;
--accent-dim: #1a1a3a;
--danger: #ef4444;
--danger-dim: #2a1018;
--success: #22c55e;
--success-dim: #0a1f12;
--warning: #f59e0b;
--warning-dim: #1f1508;
--text: #e8e8f0;
--text-muted: #787898;
--radius: 8px;
--radius-card: 14px;
--max-width: 960px;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; }
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
font-size: 14px;
line-height: 1.7;
padding: 40px 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;
}
code {
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: .875em;
}
/* ── Header ──────────────────────────── */
.header {
margin-bottom: 32px;
}
.header .date {
font-size: 12px;
font-weight: 500;
color: var(--text-muted);
letter-spacing: .02em;
margin-bottom: 4px;
}
.header h1 {
font-size: 22px;
font-weight: 700;
color: var(--text);
margin-bottom: 4px;
}
.header .sub {
font-size: 13px;
color: var(--text-muted);
}
.badge-row {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 12px;
}
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 12px;
border-radius: 999px;
font-size: 11px;
font-weight: 500;
}
.badge.live { background: var(--success-dim); color: var(--success); border: 1px solid rgba(34,197,94,.2); }
.badge.warn { background: var(--warning-dim); color: var(--warning); border: 1px solid rgba(245,158,11,.2); }
.badge.info { background: var(--accent-dim); color: var(--accent); border: 1px solid rgba(108,140,255,.2); }
.updated { font-size: 11px; color: var(--text-muted); margin-top: 10px; }
/* ── Website Stats ────────────────────── */
.stats-overview {
display: flex;
gap: 12px;
margin-bottom: 12px;
}
.stat-overview-card {
flex: 1;
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 18px;
text-align: center;
}
.stat-overview-card .stat-val {
font-size: 28px;
font-weight: 700;
color: var(--text);
line-height: 1.2;
}
.stat-overview-card .stat-lbl {
font-size: 10px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: .04em;
margin-top: 2px;
}
.stat-overview-card .stat-sub {
font-size: 11px;
color: var(--text-muted);
margin-top: 2px;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.stats-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 16px;
}
.stats-card h3 {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--accent);
margin-bottom: 8px;
}
.stat-row-item {
display: flex;
justify-content: space-between;
padding: 4px 0;
border-bottom: 1px solid rgba(30,30,42,.4);
font-size: 12px;
}
.stat-row-item:last-child { border-bottom: none; }
.stat-row-item .stat-name { color: var(--text-muted); }
.stat-row-item .stat-num { color: var(--text); font-weight: 600; }
.stats-referrers-card,
.stats-countries-card,
.stats-events-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 16px;
}
/* ── Section ─────────────────────────── */
.section { margin-bottom: 36px; }
.section-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 0;
cursor: pointer;
user-select: none;
padding: 6px 0;
}
.section-header:hover { color: var(--accent); }
.section-header h2 {
font-size: 15px;
font-weight: 600;
color: var(--text);
}
.section-header:hover h2 { color: var(--accent); }
.section-header .count {
font-size: 12px;
color: var(--text-muted);
font-weight: 400;
}
.section-toggle {
color: var(--text-muted);
font-size: 14px;
margin-left: auto;
transition: transform .2s;
}
.section-toggle.collapsed { transform: rotate(-90deg); }
.section-body {
transition: max-height .25s ease, opacity .15s ease, margin .15s ease;
overflow: hidden;
max-height: 0;
opacity: 0;
margin: 0;
}
.section-body.open {
max-height: 20000px;
opacity: 1;
margin-top: 12px;
}
/* ── Goals Grid ──────────────────────── */
.goals-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 12px;
}
.goal-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 18px;
}
.goal-card .goal-label {
font-size: 10px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: .06em;
color: var(--text-muted);
margin-bottom: 4px;
}
.goal-card .goal-current {
font-size: 26px;
font-weight: 700;
color: var(--text);
line-height: 1.2;
}
.goal-card .goal-target {
font-size: 14px;
color: var(--text-muted);
font-weight: 500;
}
.goal-card .goal-bar {
margin-top: 10px;
height: 4px;
background: var(--bg-inner);
border-radius: 2px;
overflow: hidden;
}
.goal-card .goal-bar-fill {
height: 100%;
border-radius: 2px;
transition: width .6s ease;
}
.goal-card .goal-bar-fill.green { background: var(--success); }
.goal-card .goal-bar-fill.blue { background: var(--accent); }
.goal-card .goal-bar-fill.yellow { background: var(--warning); }
.goal-card .goal-bar-fill.red { background: var(--danger); }
.goal-card .goal-sub {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
}
/* ── Campaign Cards ──────────────────── */
.campaign-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
}
.campaign-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 18px;
}
.campaign-card h3 {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--accent);
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 6px;
}
.campaign-card .status-tag {
display: inline-block;
font-size: 10px;
padding: 2px 7px;
border-radius: 4px;
font-weight: 500;
}
.campaign-card .status-tag.active { background: var(--success-dim); color: var(--success); border: 1px solid rgba(34,197,94,.2); }
.campaign-card .status-tag.paused { background: var(--warning-dim); color: var(--warning); border: 1px solid rgba(245,158,11,.2); }
.campaign-card .status-tag.draft { background: rgba(120,120,152,.08); color: var(--text-muted); border: 1px solid transparent; }
.campaign-card .stat-row {
display: flex;
gap: 12px;
margin-bottom: 8px;
}
.campaign-card .stat-item {
text-align: center;
flex: 1;
}
.campaign-card .stat-num {
font-size: 20px;
font-weight: 700;
color: var(--text);
}
.campaign-card .stat-label {
font-size: 9px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: .02em;
}
.campaign-card ul { list-style: none; margin-top: 6px; }
.campaign-card li {
font-size: 12px;
padding: 4px 0;
border-bottom: 1px solid rgba(30,30,42,.4);
display: flex;
align-items: center;
gap: 6px;
}
.campaign-card li:last-child { border-bottom: none; }
.campaign-card li .dot {
width: 6px; height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
.dot.green { background: var(--success); }
.dot.red { background: var(--danger); }
.dot.yellow { background: var(--warning); }
/* ── Needs (checkboxes) ──────────────── */
.needs-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 20px 24px;
}
.needs-list { list-style: none; }
.needs-list li {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 8px 0;
border-bottom: 1px solid rgba(30,30,42,.4);
}
.needs-list li:last-child { border-bottom: none; }
.need-checkbox {
width: 20px;
height: 20px;
min-width: 20px;
border: 2px solid var(--border-hi);
border-radius: 5px;
flex-shrink: 0;
margin-top: 2px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all .15s;
}
.need-checkbox:hover { border-color: var(--accent); }
.need-checkbox.checked {
background: var(--success);
border-color: var(--success);
}
.need-checkbox.checked::after {
content: "✓";
color: #08080c;
font-size: 14px;
font-weight: 700;
}
.need-item.done .need-text {
color: var(--text-muted);
text-decoration: line-through;
}
.need-text {
font-size: 14px;
color: var(--text);
line-height: 1.6;
}
/* ── Comms ───────────────────────────── */
.comms-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.comms-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 18px;
}
.comms-card h3 {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--accent);
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 6px;
}
.comms-card .comms-summary {
display: flex;
gap: 16px;
margin-bottom: 10px;
}
.comms-card .comms-stat { text-align: center; }
.comms-card .comms-stat-num {
font-size: 20px;
font-weight: 700;
color: var(--text);
}
.comms-card .comms-stat-label {
font-size: 9px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: .02em;
}
.comms-list { list-style: none; }
.comms-list li {
font-size: 12px;
padding: 5px 0;
border-bottom: 1px solid rgba(30,30,42,.4);
display: flex;
align-items: flex-start;
gap: 8px;
line-height: 1.5;
}
.comms-list li:last-child { border-bottom: none; }
.comms-list .comms-time {
color: var(--text-muted);
font-size: 11px;
white-space: nowrap;
flex-shrink: 0;
min-width: 48px;
}
.comms-list .comms-subj { color: var(--text); }
.comms-list .comms-detail {
color: var(--text-muted);
font-size: 11px;
}
.comms-list .comms-from {
color: var(--accent);
font-size: 11px;
}
.comms-alert {
background: var(--danger-dim);
border: 1px solid rgba(239,68,68,.2);
border-left: 3px solid var(--danger);
border-radius: var(--radius);
padding: 10px 14px;
margin-top: 8px;
font-size: 12px;
display: flex;
align-items: flex-start;
gap: 8px;
}
.comms-alert .alert-icon { color: var(--danger); flex-shrink: 0; font-size: 14px; }
.comms-alert .alert-body { color: var(--text); }
.comms-alert .alert-detail { color: var(--text-muted); font-size: 11px; margin-top: 2px; }
/* ── Daily routine ───────────────────── */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.info-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 20px;
}
.info-card h3 {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--accent);
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 6px;
}
.cron-timeline {
position: relative;
padding-left: 24px;
}
.cron-timeline::before {
content: "";
position: absolute;
left: 7px;
top: 4px;
bottom: 4px;
width: 2px;
background: var(--border);
}
.cron-item {
position: relative;
padding: 4px 0 14px;
}
.cron-item::before {
content: "";
position: absolute;
left: -22px;
top: 8px;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--bg-card);
border: 2px solid var(--accent);
}
.cron-item.primary::before {
background: var(--accent);
box-shadow: 0 0 8px rgba(108,140,255,.4);
}
.cron-time {
font-size: 11px;
color: var(--accent);
font-weight: 600;
}
.cron-title {
font-size: 13px;
font-weight: 600;
color: var(--text);
}
.cron-desc {
font-size: 12px;
color: var(--text-muted);
margin-top: 1px;
line-height: 1.5;
}
.highlight { color: var(--accent); }
/* ── Infos ───────────────────────────── */
.infos-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 20px 24px;
}
.infos-list { list-style: none; }
.infos-list li {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 7px 0;
border-bottom: 1px solid rgba(30,30,42,.4);
font-size: 13px;
color: var(--text);
line-height: 1.6;
}
.infos-list li:last-child { border-bottom: none; }
.infos-list li .info-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--accent);
white-space: nowrap;
flex-shrink: 0;
min-width: 100px;
}
.infos-list li .info-value {
color: var(--text);
}
@media (max-width: 900px) {
.campaign-grid { grid-template-columns: 1fr; }
.comms-grid { grid-template-columns: 1fr; }
.goals-grid { grid-template-columns: repeat(2, 1fr); }
.grid-2 { grid-template-columns: 1fr; }
}
@media (max-width: 500px) {
.goals-grid { grid-template-columns: 1fr; }
}
/* ── Blog ideas ──────────────────────────── */
.blog-idea-item {
display: flex;
gap: 16px;
padding: 20px 24px;
background: #0e0e14;
border: 1px solid #1a1a2e;
border-radius: 18px;
transition: border-color 0.2s, transform 0.2s;
align-items: flex-start;
margin-bottom: 16px;
}
.blog-idea-item:hover {
border-color: #6c8cff;
transform: translateY(-2px);
}
.blog-idea-checkbox {
flex-shrink: 0;
padding-top: 3px;
}
.blog-idea-checkbox input[type="checkbox"] {
accent-color: #6c8cff;
width: 17px;
height: 17px;
cursor: default;
}
.blog-idea-body { flex: 1; min-width: 0; }
.blog-idea-title {
font-weight: 700;
font-size: 16px;
color: #e8e8f0;
margin-bottom: 6px;
}
.blog-idea-desc {
font-size: 13px;
color: #888;
line-height: 1.7;
margin-bottom: 10px;
}
.blog-idea-meta {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.blog-idea-tag {
display: inline-block;
padding: 3px 12px;
font-size: 11px;
font-weight: 600;
border-radius: 12px;
background: rgba(108,140,255,0.1);
color: #6c8cff;
border: 1px solid rgba(108,140,255,0.15);
letter-spacing: 0.02em;
}
</style>
</head>
<body>
<div class="container">
<!-- ════════ HEADER ════════ -->
<div class="header">
<div class="date" id="brief-date">Loading...</div>
<h1>Daily Brief &mdash; Q2 2026</h1>
<div class="badge-row">
<span class="badge live">● Site Live</span>
<span class="badge warn">● Prelaunch</span>
<span class="badge info">◉ 139 CRM Contacts</span>
</div>
<div class="updated" id="brief-updated">Last updated: &mdash;</div>
</div>
<!-- ════════ WEBSITE ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('website-section', this)">
<h2>🌐 Website &mdash; Plausible Analytics</h2>
<span class="section-count count">live data</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="website-section">
<div class="stats-overview" id="stats-overview"></div>
<div class="stats-grid" id="stats-grid"></div>
<div class="stats-referrers" id="stats-referrers" style="margin-top:12px"></div>
</div>
</div>
<!-- ════════ GOALS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('goals-section', this)">
<h2>🎯 Q2/2026 Goals</h2>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="goals-section">
<div class="goals-grid" id="goals-grid"></div>
</div>
</div>
<!-- ════════ CAMPAIGNS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('campaigns-section', this)">
<h2>📡 Campaigns</h2>
<span class="section-count count">overview</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="campaigns-section">
<div class="campaign-grid" id="campaign-grid"></div>
</div>
</div>
<!-- ════════ NEEDS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('needs-section', this)">
<h2>📦 Items Needed From You</h2>
<span class="section-count count">click to check off</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="needs-section">
<div class="needs-card">
<ul class="needs-list" id="needs-list"></ul>
</div>
</div>
</div>
<!-- ════════ COMMS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('comms-section', this)">
<h2>✉ Communications</h2>
<span class="section-count count">important only</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="comms-section">
<div class="comms-grid" id="comms-grid"></div>
</div>
</div>
<!-- ════════ INFOS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('infos-section', this)">
<h2>📋 Infos</h2>
<span class="section-count count">look here for answers</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="infos-section">
<div class="infos-card">
<ul class="infos-list" id="infos-list"></ul>
</div>
</div>
</div>
<!-- ════════ ROUTINE ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('routine-section', this)">
<h2>⏰ Daily Routine</h2>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="routine-section">
<div class="grid-2">
<div class="info-card">
<h3>⏱ 1-Hour Block &mdash; 09:00 PYST</h3>
<div class="cron-timeline">
<div class="cron-item primary">
<div class="cron-time">09:00 &ndash; 09:05</div>
<div class="cron-title">Dashboard review</div>
<div class="cron-desc">Review KPIs &amp; overnight changes</div>
</div>
<div class="cron-item primary">
<div class="cron-time">09:05 &ndash; 09:15</div>
<div class="cron-title">Status sync</div>
<div class="cron-desc">Check needs, unblock items</div>
</div>
<div class="cron-item primary">
<div class="cron-time">09:15 &ndash; 09:45</div>
<div class="cron-title">Deep work</div>
<div class="cron-desc">Today's priorities &mdash; outreach, content, DMs</div>
</div>
<div class="cron-item primary">
<div class="cron-time">09:45 &ndash; 10:00</div>
<div class="cron-title">Wrap &amp; queue</div>
<div class="cron-desc">Set tomorrow's priorities, log progress</div>
</div>
</div>
</div>
<div class="info-card">
<h3>🤖 Cron Jobs</h3>
<div class="cron-timeline">
<div class="cron-item">
<div class="cron-time">03:00</div>
<div class="cron-title">📊 Dashboard update</div>
<div class="cron-desc">KPI snapshot to <code>data/operations.json</code></div>
</div>
<div class="cron-item">
<div class="cron-time">03:10</div>
<div class="cron-title">📈 SEO tracker</div>
<div class="cron-desc">Search Console impressions, keyword positions</div>
</div>
<div class="cron-item">
<div class="cron-time">03:20</div>
<div class="cron-title">🔗 Backlink monitor</div>
<div class="cron-desc">New referring domains flagged</div>
</div>
<div class="cron-item">
<div class="cron-time">03:30</div>
<div class="cron-title">💬 Social monitor</div>
<div class="cron-desc">Brand mentions &amp; replies</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ════════ BLOG IDEAS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('blog-ideas-section', this)">
<h2>📝 Blog Post Ideas</h2>
<span class="section-count count">research notes in blog/ideas/</span>
<span class="section-toggle collapsed"></span>
</div>
<div class="section-body" id="blog-ideas-section">
<div class="blog-idea-item">
<div class="blog-idea-checkbox">
<input type="checkbox" />
</div>
<div class="blog-idea-body">
<div class="blog-idea-title">#001 — Why backing up an agent is so hard</div>
<div class="blog-idea-desc">After running all speed-runs (including competitor profiling), the system grew to 6.8G — with 69% outside /root. New data: dual-snapshot comparison showing sprawl worsens over time. Points to the competitor profiling speed-run as the trigger that pushed past 1G.</div>
<div class="blog-idea-meta">
<span class="blog-idea-tag">Technical</span>
<span class="blog-idea-tag">Backups</span>
<span class="blog-idea-tag">DevOps</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
/* ════════ DATA ════════ */
const goals = [
{ id: 'active-contacts', label: 'Active Contacts', current: 1, target: 60, note: 'CRM Cold → Won (88 on Hold excluded)' },
{ id: 'subscriptions', label: 'Subscriptions', current: 0, target: 50, note: 'Paying customers' },
{ id: 'influencers', label: 'Influencers', current: 0, target: 20, note: 'YT creators & affiliates' },
{ id: 'visitors', label: 'Page Visitors', current: 9, target: 10000, unit: '/mo', note: '9 unique visitors in first 2 days (Plausible)' },
{ id: 'outreach-sent', label: 'Cold Emails Sent', current: 0, target: 100, note: 'First touch sent' },
{ id: 'crm-contacts', label: 'CRM Contacts', current: 139, target: 200, note: '89 original + 50 backlink targets' },
];
const campaigns = [
{
label: '✉ Cold Mail', status: 'draft',
metrics: [{ v: 0, l: 'Drafted' }, { v: 0, l: 'Sent' }, { v: 0, l: 'Replied' }],
items: [
{ t: 'Coupons ready: friends950, creator950', ok: true },
{ t: 'Template signed as Mark', ok: true },
{ t: 'No contacts with real emails yet', ok: false },
]
},
{
label: '🎙 Influencer', status: 'paused',
metrics: [{ v: 60, l: 'Identified' }, { v: 0, l: 'Contacted' }, { v: 0, l: 'Onboarded' }],
items: [
{ t: '60 YT creators on Hold', ok: true },
{ t: 'Need business emails for top 20', ok: false },
{ t: 'Affiliate program TBD', ok: false },
]
},
{
label: '🔗 LinkedIn', status: 'draft',
metrics: [{ v: 0, l: 'Connections' }, { v: 0, l: 'Outreach' }, { v: 0, l: 'Replied' }],
items: [
{ t: 'No outreach started', ok: false },
{ t: 'Company page TBD', ok: false },
{ t: 'No content strategy defined', ok: false },
]
},
{
label: '🌐 SEO & Traffic', status: 'draft',
metrics: [{ v: 9, l: 'Visitors/mo' }, { v: 0, l: 'Blog Posts' }, { v: 50, l: 'Backlink Targets' }],
items: [
{ t: 'Plausible analytics live with 7 custom events — 9 visitors, 37 pageviews', ok: true },
{ t: '50 backlink targets in CRM (strategy=Backlink outreach)', ok: true },
{ t: 'Need Google Search Console submission', ok: false },
{ t: 'Need blog posts (guest posts + own content)', ok: false },
{ t: 'Need directory submissions: Product Hunt, BetaList, G2 + 12 more', ok: false },
]
},
{
label: '🔗 Backlinks', status: 'draft',
metrics: [{ v: 50, l: 'Collected' }, { v: 0, l: 'Ref Domains' }, { v: 0, l: 'Guest Posts' }],
target: { links: 10, domains: 5, posts: 3 },
items: [
{ t: '50 backlink targets sourced — 15 AI directories, 15 blogs, 20 YT/newsletters', ok: true },
{ t: 'All 50 in CRM with strategy=Backlink outreach (Hold stage)', ok: true },
{ t: 'Need directory submissions (Product Hunt, BetaList, G2 + 12 more)', ok: false },
{ t: 'Need guest post pitches to 15 tech blogs', ok: false },
]
}
];
/* Top 5 needs — 2 done after Oliver's last update */
const NEEDS = [
{ id: "n1", text: "Business emails for top 20 YouTube creators — blocking cold email outreach", done: false },
{ id: "n2", text: "Coupon codes ready: creator950 ($9.50), friends950 ($9.50) — unlimited, includes OpenRouter credits", done: true },
{ id: "n3", text: "Product demo video (3 min screen recording)", done: false },
{ id: "n4", text: "Social media account access (X/Twitter + LinkedIn posting)", done: false },
{ id: "n5", text: "'Why I built Derez.ai' story — available on homepage", done: true },
];
/* ── Infos (action plan: 10K visitors/mo) ── */
const infos = [
{ label: 'YouTube Channel', value: '<a href="https://www.youtube.com/@derez_ai" target="_blank" style="color:#00f5ff">youtube.com/@derez_ai</a> — Deploy your own AI agent in 5 minutes. Speed runs, tutorials, Odoo integrations, and AI agent demos. Banner saved at assets/images/youtube-banner.svg (2048x1152).' },
{ label: 'Why not start?', value: 'No Search Console access yet — can\'t index site. No blog content drafted. No directory submissions. Need to unblock these first, then launch is days not weeks.' },
{ label: 'Blog Strategy', value: '2 posts/week minimum. Topics: "Deploy AI agents in 5 min" (tutorial), "Hermes agent setup guide" (tutorial), "Why agent hosting beats DIY" (comparison), "Cold email automation stack" (case study). Cross-post to Medium/Dev.to. Each post = 1 backlink target + long-tail SEO. Start with 4 cornerstone posts before directory submissions.' },
{ label: 'Phase 1 (Week 1)', value: 'Get Search Console access. Draft 2 cornerstone blog posts. Submit to Product Hunt + BetaList + AlternativeTo. Set up HARO/Qwoted alerts.' },
{ label: 'Phase 2 (Week 2)', value: 'Publish 2 posts. Guest post pitch to 5 AI/DevOps blogs. Start LinkedIn company page + 50 connection requests/week.' },
{ label: 'Phase 3 (Week 3-4)', value: 'Publish 4 more posts. Follow up guest post pitches. Directory listings done. Target: 500 unique visitors by end of month.' },
{ label: '10K Breakdown', value: 'SEO (organic): 40% = 4K. Direct/referral (directory + backlinks): 25% = 2.5K. Social (LinkedIn/X): 20% = 2K. Email/newsletter: 15% = 1.5K. Realistic 3-4 month trajectory.' },
];
/* ── Filtered comms — spam/sales/ads/work-for-you removed ── */
function isSpam(email) {
const lower = (email.subj + ' ' + email.from + ' ' + (email.detail || '')).toLowerCase();
const spamPatterns = [
'spam', 'advertisement', 'advert', 'work for you', 'work from home',
'make money', 'earn', 'investment opportunity', 'click here',
'buy now', 'limited offer', 'free money', 'congratulations',
'you\'ve won', 'marketing offer', 'promotion', 'partner offer',
'guest post', 'seo services', 'link building', 'content writing services',
];
return spamPatterns.some(p => lower.includes(p));
}
const rawInbox = [
{ id: 'i1', time: '14:44', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error — cold email bounced', alert: true, spam: true },
{ id: 'i2', time: '21:04', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error', alert: false, spam: true },
{ id: 'i3', time: '08:12', from: 'seo@boostmyrank.xyz', subj: 'Guest post opportunity for your site', detail: 'Wants to publish sponsored content', spam: true },
{ id: 'i4', time: '19:33', from: 'hannah@freelance.io', subj: 'I can help with your content marketing', detail: 'Offering writing services', spam: true },
];
const rawSent = [
{ time: 'Jun 09', to: 'o.arnold@projektbox.de', subj: 'setting up an agent for projektbox', spam: false },
{ time: 'Jun 08', to: 'oliver@odoo4projects.com', subj: 'managing your agents without SSH', spam: false },
];
const comms = {
inbox: {
total: rawInbox.filter(i => !i.spam).length,
total_spam_ignored: rawInbox.filter(i => i.spam).length,
items: rawInbox.filter(i => !i.spam),
},
sent: {
total: rawSent.filter(i => !i.spam).length,
items: rawSent.filter(i => !i.spam),
top_sent: 'Mark — Customer Success Agent'
},
note: 'Spam/sales filtered out. Only actionable items shown.'
};
/* ════════ RENDER ════════ */
function fmtTimestamp(date) {
const d = date || new Date();
const y = d.getFullYear();
const mo = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const h = String(d.getHours()).padStart(2, '0');
const mi = String(d.getMinutes()).padStart(2, '0');
return `${y}-${mo}-${day} ${h}:${mi}`;
}
function pct(cur, target) { return target > 0 ? Math.min(100, Math.round(cur / target * 100)) : 0; }
function barClass(p) { return p >= 80 ? 'green' : p >= 40 ? 'blue' : p >= 20 ? 'yellow' : 'red'; }
function toggleSection(id, header) {
const el = document.getElementById(id);
const toggle = header.querySelector('.section-toggle');
el.classList.toggle('open');
toggle.classList.toggle('collapsed');
}
function renderGoals() {
document.getElementById('goals-grid').innerHTML = goals.map(g => {
const p = pct(g.current, g.target);
const bc = barClass(p);
return `<div class="goal-card">
<div class="goal-label">${g.label}</div>
<div class="goal-current">${g.current}<span class="goal-target"> / ${g.target}${g.unit ? ' ' + g.unit : ''}</span></div>
<div class="goal-bar"><div class="goal-bar-fill ${bc}" style="width:${p}%"></div></div>
<div class="goal-sub">${g.note}</div>
</div>`;
}).join('');
}
function renderCampaigns() {
document.getElementById('campaign-grid').innerHTML = campaigns.map(c => {
const st = c.status === 'active' ? '<span class="status-tag active">● Active</span>'
: c.status === 'paused' ? '<span class="status-tag paused">● Paused</span>'
: '<span class="status-tag draft">Draft</span>';
return `<div class="campaign-card">
<h3>${c.label} ${st}</h3>
<div class="stat-row">${c.metrics.map(m => `<div class="stat-item"><div class="stat-num">${m.v}</div><div class="stat-label">${m.l}</div></div>`).join('')}</div>
${c.target ? `<div style="font-size:10px;color:var(--text-muted);margin-bottom:6px;">Target: ${c.target.links} quality links, ${c.target.domains} referring domains, ${c.target.posts} guest posts</div>` : ''}
<ul>${c.items.map(i => `<li><span class="dot ${i.ok ? 'green' : 'red'}"></span>${i.t}</li>`).join('')}</ul>
</div>`;
}).join('');
}
/* ── Needs (checkboxes) ── */
function getNeeds() {
try {
const saved = localStorage.getItem("derez-needs");
if (saved) {
const parsed = JSON.parse(saved);
return NEEDS.map(def => {
const s = parsed.find(p => p.id === def.id);
return s ? { ...def, done: s.done } : def;
});
}
} catch(e) {}
return NEEDS;
}
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 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 renderCommunications() {
const c = comms;
const inboxItems = c.inbox.items.map(i =>
`<li><span class="comms-time">${i.time}</span><span><span class="comms-from">${i.from}</span><br><span class="comms-subj">${i.subj}</span>${i.detail ? ' <span class="comms-detail">' + i.detail + '</span>' : ''}</span></li>`
).join('');
const alerts = c.inbox.items.filter(i => i.alert).map(a =>
`<div class="comms-alert"><span class="alert-icon">⚠</span><div><div class="alert-body">${a.body || a.subj}</div><div class="alert-detail">${a.detail || ''}</div></div></div>`
).join('');
const sentItems = c.sent.items.map(i =>
`<li><span class="comms-time">${i.time}</span><span><span class="comms-from">${i.to}</span><br><span class="comms-subj">${i.subj}</span></span></li>`
).join('');
const spamNote = comms.inbox.total_spam_ignored > 0
? `<div style="margin-top:8px;font-size:11px;color:var(--text-muted);">🧹 ${comms.inbox.total_spam_ignored} spam/sales filtered out</div>`
: '';
document.getElementById('comms-grid').innerHTML =
`<div class="comms-card">
<h3>📥 Inbox</h3>
<div class="comms-summary">
<div class="comms-stat"><div class="comms-stat-num">${c.inbox.total}</div><div class="comms-stat-label">Actionable</div></div>
<div class="comms-stat"><div class="comms-stat-num">${c.inbox.items.filter(i => i.alert).length}</div><div class="comms-stat-label">Alerts</div></div>
</div>
<ul class="comms-list">${inboxItems}</ul>
${alerts}
${spamNote}
</div>
<div class="comms-card">
<h3>📤 Sent</h3>
<div class="comms-summary">
<div class="comms-stat"><div class="comms-stat-num">${c.sent.total}</div><div class="comms-stat-label">Sent</div></div>
</div>
<ul class="comms-list">${sentItems}</ul>
<div style="margin-top:8px;font-size:11px;color:var(--text-muted);">
From: ${c.sent.top_sent} &middot; ${c.note}
</div>
</div>`;
}
function renderInfos() {
document.getElementById('infos-list').innerHTML = infos.map(i =>
`<li><span class="info-label">${i.label}</span><span class="info-value">${i.value}</span></li>`
).join('');
}
/* ── Website Stats ── */
const defaultStats = {
period: '—',
updated: '',
visitors: 0,
visitors_trend: {dir: 'same', change: '', pct: ''},
pageviews: 0,
pageviews_trend: {dir: 'same', change: '', pct: ''},
latest_day: '',
latest_day_visitors: 0,
dod_trend: {dir: 'same', change: '', pct: ''},
bounce_rate: '—',
visit_duration: '—',
top_referrers: [{source: 'Direct', visitors: 0}],
countries: [],
events: [],
visitors_by_day: [],
agents_sold: 0
};
let websiteStats = { ...defaultStats };
function trendArrow(t) {
if (!t || t.dir === 'same') return '<span style="color:var(--text-muted);font-size:12px">●</span>';
if (t.dir === 'new') return '<span style="color:var(--accent);font-size:12px" title="New">●</span>';
const arrow = t.dir === 'up' ? '▲' : '▼';
const color = t.dir === 'up' ? 'var(--success)' : 'var(--danger)';
return `<span style="color:${color};font-size:11px">${arrow} ${t.pct}</span>`;
}
function renderWebsite() {
const s = websiteStats;
const overview = document.getElementById('stats-overview');
const grid = document.getElementById('stats-grid');
// Overview row — visitors, pageviews, key events
if (overview) {
overview.innerHTML = `
<div class="stat-overview-card">
<div class="stat-val">${s.visitors}</div>
<div class="stat-lbl">Unique Visitors</div>
<div class="stat-sub">${trendArrow(s.visitors_trend)} ${s.period}</div>
</div>
<div class="stat-overview-card">
<div class="stat-val">${s.pageviews}</div>
<div class="stat-lbl">Pageviews</div>
<div class="stat-sub">${trendArrow(s.pageviews_trend)} ${s.pageviews_per_visitor || (s.visitors > 0 ? (s.pageviews / s.visitors).toFixed(1) : '—')} avg</div>
</div>
<div class="stat-overview-card">
<div class="stat-val">${s.agents_sold || 2}</div>
<div class="stat-lbl">Agents Sold</div>
<div class="stat-sub">via Stripe</div>
</div>
<div class="stat-overview-card">
<div class="stat-val">${s.latest_day_visitors}</div>
<div class="stat-lbl">${s.latest_day || 'Latest Day'}</div>
<div class="stat-sub">${trendArrow(s.dod_trend)} vs prev day</div>
</div>
`;
}
if (grid) {
let html = '';
// Referrers card
let refRows = (s.top_referrers && s.top_referrers.length)
? s.top_referrers.map(r => `<div class="stat-row-item"><span class="stat-name">${r.source}</span><span class="stat-num">${r.visitors}</span></div>`).join('')
: '<div class="stat-row-item" style="color:var(--text-muted);font-size:11px">No referral data yet</div>';
html += `<div class="stats-referrers-card">
<h3>Referral Sources</h3>
${refRows}
</div>`;
// Countries card
let countryRows = (s.countries && s.countries.length)
? s.countries.map(c => `<div class="stat-row-item"><span class="stat-name">${c.country}</span><span class="stat-num">${c.visitors}</span></div>`).join('')
: '<div class="stat-row-item" style="color:var(--text-muted);font-size:11px">No country data yet</div>';
html += `<div class="stats-countries-card">
<h3>Top Countries</h3>
${countryRows}
</div>`;
// Events card
let eventRows = (s.events && s.events.length)
? s.events.map(e => `<div class="stat-row-item"><span class="stat-name">${e.name}</span><span class="stat-num">${e.total} <span style="color:var(--text-muted);font-size:10px">(${e.unique} uniq)</span></span></div>`).join('')
: '<div class="stat-row-item" style="color:var(--text-muted);font-size:11px">No event data yet</div>';
html += `<div class="stats-events-card">
<h3>Custom Events</h3>
${eventRows}
</div>`;
// UTM sources card
let utmRows = (s.utm_sources && s.utm_sources.length)
? s.utm_sources.map(u => `<div class="stat-row-item"><span class="stat-name">${u.source === '(not set)' ? 'Direct/None' : u.source}</span><span class="stat-num">${u.visitors}</span></div>`).join('')
: '<div class="stat-row-item" style="color:var(--text-muted);font-size:11px">No UTM data</div>';
html += `<div class="stats-referrers-card">
<h3>UTM Sources</h3>
${utmRows}
</div>`;
grid.innerHTML = html;
}
}
async function fetchWebsiteStats() {
try {
const res = await fetch('data/insights.json?t=' + Date.now());
if (!res.ok) throw new Error('Not found');
const d = await res.json();
websiteStats = { ...defaultStats, ...d };
} catch {}
renderWebsite();
}
async function fetchLiveData() {
try {
const res = await fetch('data/operations.json?t=' + Date.now());
if (!res.ok) throw new Error('Not found');
const d = await res.json();
if (d.date) document.getElementById('brief-date').textContent = d.date;
if (d.updated) document.getElementById('brief-updated').textContent = 'Last updated: ' + d.updated;
if (d.goals) d.goals.forEach((g, i) => { if (goals[i]) { goals[i].current = g.current; goals[i].note = g.note || goals[i].note; } });
renderGoals();
} catch { /* defaults */ }
}
/* ── Init ── */
document.getElementById('brief-date').textContent = fmtTimestamp();
renderGoals();
renderCampaigns();
renderNeeds(getNeeds());
renderCommunications();
renderInfos();
fetchWebsiteStats();
fetchLiveData();
</script>
</body>
</html>