Files
derez.ai/operations.html
T

847 lines
33 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; }
/* ── Section ─────────────────────────── */
.section { margin-bottom: 36px; }
.section-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
cursor: pointer;
user-select: none;
}
.section-header h2 {
font-size: 15px;
font-weight: 600;
color: var(--text);
}
.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;
overflow: hidden;
max-height: 20000px;
opacity: 1;
}
.section-body.hidden { max-height: 0; opacity: 0; padding: 0; }
/* ── Goals Grid ──────────────────────── */
.goals-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 12px;
}
.goal-card {
background: var(--bg-card);
border: 1px solid var(--border);
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: 1px solid var(--border);
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: 1px solid var(--border);
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: 1px solid var(--border);
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: 1px solid var(--border);
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); }
@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; }
}
</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="sub">derez.ai &middot; Operations Dashboard</div>
<div class="badge-row">
<span class="badge live">● Site Live</span>
<span class="badge warn">● Prelaunch</span>
<span class="badge info">◉ 89 CRM Contacts</span>
</div>
<div class="updated" id="brief-updated">Last updated: &mdash;</div>
</div>
<!-- ════════ GOALS ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('goals-section', this)">
<h2>🎯 Q2/2026 Goals</h2>
<span class="section-toggle"></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"></span>
</div>
<div class="section-body" id="campaigns-section">
<div class="campaign-grid" id="campaign-grid"></div>
</div>
</div>
<!-- ════════ NEEDS (CHECKBOXES) ════════ -->
<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"></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"></span>
</div>
<div class="section-body" id="comms-section">
<div class="comms-grid" id="comms-grid"></div>
</div>
</div>
<!-- ════════ ROUTINE ════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('routine-section', this)">
<h2>⏰ Daily Routine</h2>
<span class="section-toggle"></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>
</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: 0, target: 10000, unit: '/mo', note: 'Monthly unique visitors' },
{ id: 'outreach-sent', label: 'Cold Emails Sent', current: 0, target: 100, note: 'First touch sent' },
{ id: 'crm-contacts', label: 'CRM Contacts', current: 89, target: 200, note: 'Total collected' },
];
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 },
]
}
];
/* Top 5 most important needs */
const NEEDS = [
{ id: "n1", text: "Business emails for top 20 YouTube creators — blocking cold email outreach", done: false },
{ id: "n2", text: "Influencer partnership budget (free plans? cash? affiliate %?)", done: false },
{ 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' notes for blog post", done: false },
];
/* 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', 'discount', '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: 'Jun 09 14:44', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error — cold email bounced', alert: true, spam: false },
{ id: 'i2', time: 'Jun 08 21:04', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error', alert: false, spam: false },
{ id: 'i3', time: 'Jun 10 08:12', from: 'seo@boostmyrank.xyz', subj: 'Guest post opportunity for your site', detail: 'Wants to publish sponsored content', spam: true },
{ id: 'i4', time: 'Jun 09 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 strategyCards = [
{
icon: '📢', title: 'Influencer Outreach',
body: 'Target 60 YouTube creators in AI/ML/DevOps. Offer free 3-month agent + 10% recurring affiliate commission. Pitch: "Deploy AI agents in 5 minutes — no DevOps needed."',
goal: 'Goal: 20 onboarded, 3+ social mentions'
},
{
icon: '👨‍👩‍👧‍👦', title: 'Cold Email',
body: 'Templates & coupons ready (friends950, creator950). Signed as Mark. All drafts reviewed via Telegram before sending.',
goal: 'Goal: 100 sent, 10+ replies'
},
{
icon: '🔗', title: 'LinkedIn Presence',
body: 'Zero presence. Need page, connections, and content strategy before outreach.',
goal: 'Goal: Page live, 100 connections'
},
{
icon: '📈', title: 'SEO & Analytics',
body: 'Plausible analytics live with 7 custom events. Weekly cron monitors insights. Need Search Console + directory listings.',
goal: 'Goal: 10K visitors/mo, top 5 for "AI agent hosting"'
}
];
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 fmtDate() {
return new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
}
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('hidden');
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>
<ul>${c.items.map(i => `<li><span class="dot ${i.ok ? 'green' : 'red'}"></span>${i.t}</li>`).join('')}</ul>
</div>`;
}).join('');
}
/* ── Interactive 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>`;
}
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 = fmtDate();
renderGoals();
renderCampaigns();
renderNeeds(getNeeds());
renderCommunications();
fetchLiveData();
</script>
</body>
</html>