Files
derez.ai/operations.html
T

769 lines
31 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex, nofollow" />
<title>Daily Brief — Derez.ai Operations</title>
<style>
: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: 1100px;
}
*, *::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: 32px 0;
}
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;
}
/* ── Brief Header ────────────────────────── */
.brief-header {
text-align: center;
margin-bottom: 32px;
padding: 32px 24px 28px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
box-shadow: var(--shadow);
}
.brief-header .date {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--text-muted);
margin-bottom: 6px;
}
.brief-header h1 {
font-size: 26px;
font-weight: 700;
color: var(--accent);
}
.brief-header .sub {
font-size: 13px;
color: var(--text-muted);
margin-top: 6px;
}
.brief-header .badge-row {
display: flex;
justify-content: center;
gap: 10px;
flex-wrap: wrap;
margin-top: 14px;
}
.brief-header .badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 14px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
}
.badge.live { background: rgba(0,255,136,0.12); color: var(--success); border: 1px solid rgba(0,255,136,0.3); }
.badge.warn { background: rgba(255,170,0,0.12); color: var(--warning); border: 1px solid rgba(255,170,0,0.3); }
.badge.info { background: rgba(0,245,255,0.1); color: var(--accent); border: 1px solid rgba(0,245,255,0.2); }
.updated { font-size: 11px; color: var(--text-muted); margin-top: 12px; opacity: 0.6; }
/* ── Section ─────────────────────────────── */
.section { margin-bottom: 32px; }
.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); font-size: 18px; }
.section-header .count {
font-size: 11px;
color: var(--text-muted);
font-weight: 400;
}
.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; }
/* ── Q2 Goals Grid ───────────────────────── */
.goals-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
gap: 14px;
}
.goal-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 20px;
}
.goal-card .goal-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
margin-bottom: 4px;
}
.goal-card .goal-current {
font-size: 28px;
font-weight: 700;
color: var(--text);
}
.goal-card .goal-target {
font-size: 14px;
color: var(--text-muted);
}
.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 0.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: 14px;
}
.campaign-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 20px;
}
.campaign-card h3 {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--accent);
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}
.campaign-card .status-tag {
display: inline-block;
font-size: 10px;
padding: 2px 8px;
border-radius: 4px;
font-weight: 600;
}
.campaign-card .status-tag.active { background: rgba(0,255,136,0.12); color: var(--success); border: 1px solid rgba(0,255,136,0.25); }
.campaign-card .status-tag.paused { background: rgba(255,170,0,0.12); color: var(--warning); border: 1px solid rgba(255,170,0,0.3); }
.campaign-card .status-tag.draft { background: rgba(46,106,128,0.15); color: var(--text-muted); border: 1px solid transparent; }
.campaign-card .stat-row {
display: flex;
gap: 16px;
margin-bottom: 10px;
}
.campaign-card .stat-item {
text-align: center;
flex: 1;
}
.campaign-card .stat-num {
font-size: 24px;
font-weight: 700;
color: var(--text);
}
.campaign-card .stat-label {
font-size: 10px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.campaign-card ul {
list-style: none;
margin-top: 6px;
}
.campaign-card li {
font-size: 12px;
padding: 5px 0;
border-bottom: 1px solid rgba(10,48,96,0.3);
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); }
/* ── Action Items ────────────────────────── */
.action-list { list-style: none; }
.action-item {
background: var(--bg-card);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: var(--radius-card);
padding: 20px 24px;
margin-bottom: 12px;
display: flex;
align-items: flex-start;
gap: 14px;
}
.action-item .action-num {
width: 28px; height: 28px;
border-radius: 50%;
background: var(--accent-dim);
border: 1px solid var(--accent);
color: var(--accent);
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
}
.action-item .action-body h4 {
font-size: 14px;
color: var(--text);
margin-bottom: 3px;
}
.action-item .action-body p {
font-size: 12px;
color: var(--text-muted);
}
.action-item .action-owner {
font-size: 11px;
color: var(--accent);
margin-top: 4px;
}
/* ── Strategy ────────────────────────────── */
.strategy-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.strategy-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 22px;
}
.strategy-card h3 {
font-size: 13px;
font-weight: 700;
color: var(--accent);
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.strategy-card p, .strategy-card li {
font-size: 13px;
color: var(--text);
line-height: 1.7;
}
.strategy-card ul {
list-style: none;
padding: 0;
}
.strategy-card ul li {
padding: 4px 0;
}
.strategy-card ul li::before {
content: " ";
color: var(--accent);
}
.strategy-card .highlight { color: var(--accent); }
/* ── Recommendations ─────────────────────── */
.reco-list { list-style: none; }
.reco-list li {
padding: 10px 16px;
margin-bottom: 8px;
background: var(--bg-card);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: var(--radius-card);
font-size: 13px;
display: flex;
align-items: flex-start;
gap: 10px;
}
.reco-list li .reco-icon {
color: var(--accent);
flex-shrink: 0;
font-weight: 700;
}
/* ── Communications ──────────────────────── */
.comms-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.comms-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-card);
padding: 20px;
}
.comms-card h3 {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--accent);
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.comms-card .comms-summary {
display: flex;
gap: 20px;
margin-bottom: 12px;
}
.comms-card .comms-stat {
text-align: center;
}
.comms-card .comms-stat-num {
font-size: 24px;
font-weight: 700;
color: var(--text);
}
.comms-card .comms-stat-label {
font-size: 10px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.comms-list { list-style: none; }
.comms-list li {
font-size: 12px;
padding: 6px 0;
border-bottom: 1px solid rgba(10,48,96,0.3);
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: 60px;
}
.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(--bg-card);
border: 1px solid rgba(255,59,59,0.3);
border-left: 3px solid var(--danger);
border-radius: var(--radius-card);
padding: 12px 16px;
margin-top: 10px;
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; }
@media (max-width: 900px) {
.campaign-grid { grid-template-columns: 1fr; }
.comms-grid { grid-template-columns: 1fr; }
.goals-grid { grid-template-columns: repeat(2, 1fr); }
.strategy-grid { grid-template-columns: 1fr; }
}
@media (max-width: 500px) {
.goals-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="container">
<!-- ═══════════════ BRIEF HEADER ═══════════════ -->
<div class="brief-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>
<!-- ═══════════════ Q2 GOALS ═══════════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('goals-section', this)">
<h2><span class="icon">🎯</span> 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><span class="icon">📡</span> Campaigns <span class="count">overview</span></h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="campaigns-section">
<div class="campaign-grid" id="campaign-grid"></div>
</div>
</div>
<!-- ═══════════════ ACTION ITEMS ═══════════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('actions-section', this)">
<h2><span class="icon"></span> Action Items <span class="count">needs your attention</span></h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="actions-section">
<ul class="action-list" id="action-list"></ul>
</div>
</div>
<!-- ═══════════════ COMMUNICATIONS ═══════════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('comms-section', this)">
<h2><span class="icon"></span> Communications <span class="count">last 35 hours</span></h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="comms-section">
<div class="comms-grid" id="comms-grid"></div>
</div>
</div>
<!-- ═══════════════ STRATEGY & RECOMMENDATIONS ═══════════════ -->
<div class="section">
<div class="section-header" onclick="toggleSection('strategy-section', this)">
<h2><span class="icon">🧠</span> Strategy Overview &amp; Recommendations</h2>
<span class="section-toggle"></span>
</div>
<div class="section-body" id="strategy-section">
<div class="strategy-grid" id="strategy-grid"></div>
<br>
<ul class="reco-list" id="reco-list"></ul>
</div>
</div>
</div>
<script>
/* ═══════════════ DATA ═══════════════════════ */
const goals = [
{ id: 'active-contacts', label: 'Active Contacts', current: 1, target: 60, note: 'CRM: Cold to 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 on board' },
{ 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 emails' },
{ id: 'crm-contacts', label: 'CRM Contacts', current: 89, target: 200, note: 'Total contacts collected' },
];
const campaigns = [
{
label: '✉ Cold Mail', status: 'draft',
metrics: [{ v: 0, l: 'Drafted' }, { v: 0, l: 'Sent' }, { v: 0, l: 'Replied' }],
items: [
{ t: 'Coupon codes: friends950, creator950', ok: true },
{ t: 'Template signed as Mark — Customer Agent', ok: true },
{ t: 'No Cold 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 YouTube 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 yet', ok: false },
{ t: 'Company page TBD', ok: false },
{ t: 'No content strategy defined', ok: false },
]
}
];
const actions = [
{ num: 1, title: 'Prioritise top 20 YouTube creators for outreach', desc: 'From the 60 YT creators on Hold — pick the most relevant 20 for first-wave influencer outreach.', owner: 'Oliver' },
{ num: 2, title: 'Collect business emails for top 20 creators', desc: 'Research and collect real business emails from the prioritised creators\' websites or social bios.', owner: 'Mint' },
{ num: 3, title: 'Send first cold email batch', desc: 'Using friends950 / creator950 coupons. Draft goes to Telegram for confirmation first.', owner: 'Oliver + Mint' },
];
const strategyCards = [
{
icon: '📢', title: 'Influencer Outreach',
body: 'Target 60 YouTube creators in AI/ML/DevOps. Offer free 3-month agent + affiliate commission (10% recurring). Pitch: "Your followers can deploy AI agents in 5 minutes — no DevOps needed."',
goal: 'Goal: 20 onboarded, 3+ social mentions'
},
{
icon: '👨‍👩‍👧‍👦', title: 'Cold Email',
body: 'Templates and coupons ready (friends950, creator950). Signed as Mark — Customer Success Agent. All drafts reviewed via Telegram before sending.',
goal: 'Goal: 100 emails sent, 10+ replies'
},
{
icon: '🔗', title: 'LinkedIn Presence',
body: 'Zero LinkedIn presence currently. Need company page, connection building, and content strategy before outreach can begin.',
goal: 'Goal: Company page live, 100 connections'
},
{
icon: '📈', title: 'SEO & Analytics',
body: 'Plausible analytics live on derez.ai with 7 custom events tracked. Weekly cron monitors insights. Need Search Console submission and directory listings.',
goal: 'Goal: 10K monthly visitors, top 5 for "AI agent hosting"'
}
];
const comms = {
inbox: {
total: 4,
last35h: 2,
items: [
{ time: 'Jun 09 14:44', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error — cold email bounced', alert: true },
{ time: 'Jun 08 21:04', from: 'Mail Delivery System', subj: 'Undelivered — the@123.de', detail: '421 Downstream server error', alert: false },
],
alerts: [
{ icon: '⚠', body: 'Cold email to the@123.de bouncing (421 Downstream server error)', detail: 'Check recipient email validity or retry later.' }
]
},
sent: {
total: 8,
last35h: 2,
items: [
{ time: 'Jun 09', to: 'o.arnold@projektbox.de', subj: 'setting up an agent for projektbox' },
{ time: 'Jun 08', to: 'oliver@odoo4projects.com', subj: 'managing your agents without SSH' },
],
top_sent: 'Mark — Customer Success Agent'
},
note: '2 bounce alerts need review. Sending from support@derez.ai as Mark.'
};
const recommendations = [
'Prioritise the top 20 YouTube creators from the 60 collected — these are your most winnable influencer channel.',
'Unlock cold email outreach by populating real business emails for those 20 creators. Templates and coupons are ready.',
'Set up a LinkedIn company page and start building connections — zero presence there currently.',
'Email capturing (newsletter signup or lead magnet) is needed before 10K visitors/mo becomes realistic.',
];
/* ═══════════════ 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 || ''}</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('');
}
function renderActions() {
document.getElementById('action-list').innerHTML = actions.map(a =>
`<li class="action-item">
<div class="action-num">${a.num}</div>
<div class="action-body">
<h4>${a.title}</h4>
<p>${a.desc}</p>
<div class="action-owner">Owner: ${a.owner}</div>
</div>
</li>`
).join('');
}
function renderStrategy() {
document.getElementById('strategy-grid').innerHTML = strategyCards.map(s =>
`<div class="strategy-card">
<h3>${s.icon} ${s.title}</h3>
<p>${s.body}</p>
<p style="margin-top:8px;"><span class="highlight">${s.goal}</span></p>
</div>`
).join('');
document.getElementById('reco-list').innerHTML = recommendations.map(r =>
`<li><span class="reco-icon">→</span>${r}</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> <span class="comms-detail">${i.detail}</span></span></li>`
).join('');
const alerts = c.inbox.alerts.map(a =>
`<div class="comms-alert"><span class="alert-icon">${a.icon}</span><div><div class="alert-body">${a.body}</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('');
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">Total</div></div>
<div class="comms-stat"><div class="comms-stat-num">${c.inbox.last35h}</div><div class="comms-stat-label">Last 35h</div></div>
<div class="comms-stat"><div class="comms-stat-num">${c.inbox.alerts.length}</div><div class="comms-stat-label">Alerts</div></div>
</div>
<ul class="comms-list">${inboxItems}</ul>
${alerts}
</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">Total Sent</div></div>
<div class="comms-stat"><div class="comms-stat-num">${c.sent.last35h}</div><div class="comms-stat-label">Last 35h</div></div>
<div class="comms-stat"><div class="comms-stat-num">${c.sent.items.length}</div><div class="comms-stat-label">Recent</div></div>
</div>
<ul class="comms-list">${sentItems}</ul>
<div style="margin-top:8px;font-size:11px;color:var(--text-muted);">
Sender: ${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 { /* use defaults */ }
}
document.getElementById('brief-date').textContent = fmtDate();
renderGoals();
renderCampaigns();
renderActions();
renderCommunications();
renderStrategy();
fetchLiveData();
</script>
</body>
</html>