Skip to content
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>60 Decibels — Weekly Sales Scorecard</title> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1"></script> <style> :root {   --teal: #4A8A90;   --dark-teal: #2D5F64;   --mint: #E4EEEB;   --cream: #F8F6F0;   --ink: #2D3B38;   --muted: #5A7A74;   --pos: #2E7D32;   --neg: #C62828;   --neutral: #6D6D6D;   --gold: #F4B942;   --amber: #EC8B5E;   --rose: #D96A9E;   --magenta: #C0448F;   --plum: #8B2B6B;   --gap: 16px;   --radius: 10px; } * { margin: 0; padding: 0; box-sizing: border-box; } body {   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;   background: var(--mint);   color: var(--ink);   line-height: 1.5;   padding: 20px; } .dashboard { max-width: 1400px; margin: 0 auto; }  .header {   background: var(--dark-teal);   color: #fff;   padding: 22px 28px;   border-radius: var(--radius);   margin-bottom: var(--gap);   display: flex;   justify-content: space-between;   align-items: center;   flex-wrap: wrap;   gap: 16px; } .header h1 { font-size: 22px; font-weight: 700; letter-spacing: -0.3px; } .header .sub { font-size: 13px; opacity: 0.8; margin-top: 2px; } .filters { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; } .filter-group { display: flex; align-items: center; gap: 8px; } .filter-group label { font-size: 12px; color: rgba(255,255,255,0.75); text-transform: uppercase; letter-spacing: 0.5px; } .filter-group select {   padding: 7px 12px;   border: 1px solid rgba(255,255,255,0.25);   border-radius: 6px;   background: rgba(255,255,255,0.12);   color: #fff;   font-size: 13px;   cursor: pointer; } .filter-group select option { background: var(--dark-teal); color: #fff; }  .kpi-row {   display: grid;   grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));   gap: var(--gap);   margin-bottom: var(--gap); } .kpi-card {   background: #fff;   border-radius: var(--radius);   padding: 20px 24px;   box-shadow: 0 1px 3px rgba(0,0,0,0.06);   border-left: 4px solid var(--teal); } .kpi-card.won { border-left-color: var(--pos); } .kpi-card.pipeline { border-left-color: var(--teal); } .kpi-card.movement { border-left-color: var(--magenta); } .kpi-card.sqls { border-left-color: var(--amber); } .kpi-label {   font-size: 11px;   color: var(--muted);   text-transform: uppercase;   letter-spacing: 0.7px;   font-weight: 600;   margin-bottom: 6px; } .kpi-value { font-size: 30px; font-weight: 700; color: var(--ink); line-height: 1.1; } .kpi-sub { font-size: 12px; color: var(--muted); margin-top: 6px; } .kpi-sub.pos { color: var(--pos); } .kpi-sub.neg { color: var(--neg); }  .chart-row {   display: grid;   grid-template-columns: 2fr 1fr;   gap: var(--gap);   margin-bottom: var(--gap); } @media (max-width: 900px) { .chart-row { grid-template-columns: 1fr; } }  .panel {   background: #fff;   border-radius: var(--radius);   padding: 20px 24px;   box-shadow: 0 1px 3px rgba(0,0,0,0.06); } .panel h3 {   font-size: 14px;   font-weight: 700;   color: var(--dark-teal);   margin-bottom: 14px;   text-transform: uppercase;   letter-spacing: 0.5px; } .panel .chart-wrap { position: relative; height: 260px; }  /* Pipeline stage bar (custom, not Chart.js) */ .stage-bar {   display: flex;   height: 46px;   border-radius: 6px;   overflow: hidden;   margin-bottom: 10px; } .stage-seg {   display: flex;   align-items: center;   justify-content: center;   color: #fff;   font-weight: 700;   font-size: 13px;   position: relative; } .stage-seg:nth-child(1) { background: var(--gold); color: var(--ink); } .stage-seg:nth-child(2) { background: var(--amber); } .stage-seg:nth-child(3) { background: var(--rose); } .stage-seg:nth-child(4) { background: var(--magenta); } .stage-seg:nth-child(5) { background: var(--plum); } .stage-legend {   display: grid;   grid-template-columns: repeat(5, 1fr);   gap: 8px;   font-size: 11px;   color: var(--muted); } .stage-legend .dot { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: middle; }  /* Pair leaderboard */ .pair-list { display: flex; flex-direction: column; gap: 10px; } .pair-row {   display: grid;   grid-template-columns: 180px 1fr 110px;   gap: 12px;   align-items: center;   font-size: 13px; } .pair-name { font-weight: 600; color: var(--ink); } .pair-bar-wrap { position: relative; height: 22px; background: #f3f3f3; border-radius: 4px; overflow: hidden; } .pair-bar {   position: absolute; top: 0; bottom: 0;   border-radius: 4px; } .pair-bar.pos { background: linear-gradient(90deg, var(--teal), var(--dark-teal)); left: 50%; } .pair-bar.neg { background: linear-gradient(90deg, #e07070, var(--neg)); right: 50%; } .pair-bar.zero { background: #ddd; left: calc(50% - 2px); width: 4px; } .pair-mid { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: #aaa; } .pair-val { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; } .pair-val.pos { color: var(--pos); } .pair-val.neg { color: var(--neg); } .pair-val.zero { color: var(--neutral); }  /* Table */ .table-wrap {   background: #fff;   border-radius: var(--radius);   padding: 20px 24px;   box-shadow: 0 1px 3px rgba(0,0,0,0.06);   overflow-x: auto; } .table-wrap h3 {   font-size: 14px;   font-weight: 700;   color: var(--dark-teal);   margin-bottom: 14px;   text-transform: uppercase;   letter-spacing: 0.5px; } table { width: 100%; border-collapse: collapse; font-size: 13px; } thead th {   text-align: left;   padding: 10px 12px;   border-bottom: 2px solid var(--mint);   color: var(--muted);   font-weight: 700;   font-size: 11px;   text-transform: uppercase;   letter-spacing: 0.5px;   cursor: pointer;   user-select: none; } thead th:hover { color: var(--dark-teal); } tbody td {   padding: 9px 12px;   border-bottom: 1px solid #f0f0f0;   vertical-align: top; } tbody tr:hover { background: #fafbfa; } tbody tr:last-child td { border-bottom: none; } .evt-tag {   display: inline-block;   padding: 2px 8px;   border-radius: 3px;   font-size: 11px;   font-weight: 700;   text-transform: uppercase;   letter-spacing: 0.3px; } .evt-new { background: #E8F5E9; color: #1B5E20; } .evt-advance { background: #E3F2FD; color: #1565C0; } .evt-won { background: #E0F7FA; color: #00695C; } .evt-lost { background: #FFEBEE; color: #B71C1C; } .num { font-variant-numeric: tabular-nums; text-align: right; } .num.pos { color: var(--pos); font-weight: 600; } .num.neg { color: var(--neg); font-weight: 600; }  /* Qualitative commentary box */ .commentary {   background: #fff;   border-radius: var(--radius);   padding: 20px 24px;   box-shadow: 0 1px 3px rgba(0,0,0,0.06);   margin-bottom: var(--gap);   display: grid;   grid-template-columns: repeat(4, 1fr);   gap: 18px; } @media (max-width: 900px) { .commentary { grid-template-columns: 1fr 1fr; } } @media (max-width: 600px) { .commentary { grid-template-columns: 1fr; } } .commentary .block { border-left: 3px solid var(--teal); padding-left: 12px; } .commentary .block.good   { border-left-color: var(--pos); } .commentary .block.behind { border-left-color: var(--neg); } .commentary .block.color  { border-left-color: var(--magenta); } .commentary .block.reco   { border-left-color: var(--amber); } .commentary h4 {   font-size: 11px;   font-weight: 700;   color: var(--dark-teal);   text-transform: uppercase;   letter-spacing: 0.7px;   margin-bottom: 6px; } .commentary p { font-size: 13px; color: var(--ink); line-height: 1.5; } .commentary strong { color: var(--dark-teal); }  .footer {   margin-top: var(--gap);   padding: 14px 20px;   font-size: 12px;   color: var(--muted);   text-align: center; } </style> </head> <body> <div class="dashboard">   <header class="header">     <div>       <h1>60 Decibels — Weekly Sales Scorecard</h1>       <div class="sub" id="week-label">Week of April 6 – April 12, 2026</div>     </div>     <div class="filters">       <div class="filter-group">         <label for="f-week">Week</label>         <select id="f-week"></select>       </div>       <div class="filter-group">         <label for="f-pair">Pair</label>         <select id="f-pair">           <option value="all">All Pairs</option>         </select>       </div>     </div>   </header>    <section class="commentary" id="commentary"></section>    <section class="kpi-row">     <div class="kpi-card pipeline">       <div class="kpi-label">Weighted Pipeline</div>       <div class="kpi-value" id="kpi-pipeline">$4.97M</div>       <div class="kpi-sub pos" id="kpi-pipeline-delta">+$0.04M (+0.8%) WoW</div>     </div>     <div class="kpi-card movement">       <div class="kpi-label">Net Movement (Week)</div>       <div class="kpi-value" id="kpi-movement">+$42.5K</div>       <div class="kpi-sub" id="kpi-movement-sub">Adds $88.7K · Removes -$46.2K</div>     </div>     <div class="kpi-card won">       <div class="kpi-label">Closed Won (Week)</div>       <div class="kpi-value" id="kpi-won">$18.0K</div>       <div class="kpi-sub" id="kpi-won-sub">3 deals · $28.8K MTD</div>     </div>     <div class="kpi-card sqls">       <div class="kpi-label">New SQLs (Week)</div>       <div class="kpi-value" id="kpi-sqls">15</div>       <div class="kpi-sub" id="kpi-sqls-sub">16 MTD · 50 to April goal</div>     </div>   </section>    <section class="chart-row">     <div class="panel">       <h3>Net Movement by Pair</h3>       <div class="pair-list" id="pair-list"></div>     </div>     <div class="panel">       <h3>Weekly Weighted Advances (Trend)</h3>       <div class="chart-wrap"><canvas id="trend-chart"></canvas></div>     </div>   </section>    <section class="panel" style="margin-bottom: var(--gap);">     <h3>Pipeline by Stage · $17.6M unweighted · ~365 deals</h3>     <div class="stage-bar" id="stage-bar"></div>     <div class="stage-legend" id="stage-legend"></div>   </section>    <section class="table-wrap">     <h3>Event Detail · <span id="event-count"></span> events</h3>     <table id="events-table">       <thead>         <tr>           <th data-sort="date">Date</th>           <th data-sort="pair">Pair</th>           <th data-sort="owner">Owner</th>           <th data-sort="event">Event</th>           <th data-sort="deal">Deal</th>           <th data-sort="value" class="num">Value</th>           <th data-sort="weighted" class="num">Weighted Impact</th>           <th data-sort="detail">Detail</th>         </tr>       </thead>       <tbody></tbody>     </table>   </section>    <footer class="footer">     Data sourced from HubSpot · Generated April 13, 2026 · Refreshed weekly every Monday   </footer> </div>  <script> // ============ DATA (weekly refresh replaces this block) ============ const DATA = {   current_week: "2026-04-06",   weeks: [     { id: "2026-04-06", label: "Apr 6 – 12, 2026" },     { id: "2026-03-30", label: "Mar 30 – Apr 5, 2026" },     { id: "2026-03-23", label: "Mar 23 – 29, 2026" },     { id: "2026-03-16", label: "Mar 16 – 22, 2026" }   ],   weekly_snapshots: {     "2026-04-06": {       kpis: {         pipeline: 4970000, pipeline_delta: 40000, pipeline_delta_pct: 0.8,         net_movement: 42450, adds: 88650, removes: -46200,         closed_won: 18000, closed_count: 3, won_mtd: 28800,         new_sqls: 15, sqls_mtd: 16, sqls_goal_remaining: 50       },       stages: [         { name: "Qualified",      weighted: 7800000, deals: 193 },         { name: "In Conversation",weighted: 5600000, deals: 81  },         { name: "Proposal",       weighted: 2980000, deals: 57  },         { name: "Negotiation",    weighted:  650000, deals: 16  },         { name: "Contract",       weighted:  320000, deals: 16  }       ],       pairs: [         { name: "Venu + Tom",             net: 44000,  sqls: 8, closed: 0,    moved: 1, designated: true },         { name: "Carla + Pranav",         net: 3800,   sqls: 4, closed: 18000,moved: 2, designated: true },         { name: "Louis + Ellie + Tripti", net: -17350, sqls: 3, closed: 1500, moved: 0, designated: true },         { name: "Pia + Sasha",            net: 0,      sqls: 0, closed: 0,    moved: 0, designated: true }       ],       commentary: {         good:   "Venu + Tom led with <strong>+$44K net</strong> on 8 new opps, including Bayer CultiBot entering at Negotiation (75%). Digital Green advanced to Contract Signing. Carla + Pranav closed 2 deals and advanced Diageo to Negotiation.",         behind: "Louis + Ellie + Tripti dropped <strong>-$17K net</strong> — SixPoint ($35K) and Climate Impact ($25K) both closed lost at Proposal stage. Pia + Sasha had no recorded activity. Overall new SQLs (15) are on pace but MTD count (16) is light 2 weeks in.",         color:  "Weighted pipeline only moved <strong>+0.8% ($40K)</strong> — adds of $88.7K were nearly offset by $46.2K in losses. Advances were concentrated in 4 deals totaling <strong>$61K weighted</strong> — well below the 4-week average of <strong>$226K</strong>. This is a <em>slow advance week</em>, not a lossy one.",         reco:   "Push Diageo and Bayer hard toward close this week. Re-engage Pia + Sasha with a pipeline review. Root-cause the two Proposal-stage losses — if pricing, adjust; if fit, qualify earlier. Target 3+ stage advances this week to rebuild weighted momentum."       },       events: [         { date:"2026-04-07", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"New Opportunity",   deal:"Bayer Crop Science (CultiBot)",                value:40000, weighted:30000, detail:"New deal, entered at In Negotiation (75%)" },         { date:"2026-04-08", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"New Opportunity",   deal:"Gates Foundation (FPO Digitization)",          value:50000, weighted: 5000, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-09", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"New Opportunity",   deal:"FMO (Babban Gona)",                            value:30000, weighted: 3000, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-07", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"New Opportunity",   deal:"FSD Africa EOI",                               value:    0, weighted:    0, detail:"New EOI, no amount recorded" },         { date:"2026-04-08", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"New Opportunity",   deal:"World Food Programme EOI",                     value:    0, weighted:    0, detail:"New EOI, no amount recorded" },         { date:"2026-04-10", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"New Opportunity",   deal:"Mercy Corps EOI",                              value:    0, weighted:    0, detail:"New EOI, no amount recorded" },         { date:"2026-04-10", pair:"Venu + Tom",              owner:"Venu Aggarwal",        event:"Advance (Forward)", deal:"Digital Green (Farmer.Chat)",                  value:40000, weighted: 6000, detail:"In Negotiation → Contract Signing" },         { date:"2026-04-09", pair:"Venu + Tom",              owner:"Tom Adams",            event:"Closed Lost",       deal:"Rio Tinto Simfer SA (Iron Ore)",               value:    0, weighted:    0, detail:"Closed lost, no amount recorded" },         { date:"2026-04-11", pair:"Venu + Tom",              owner:"Tom Adams",            event:"New Opportunity",   deal:"Lightrock",                                    value:    0, weighted:    0, detail:"New deal, no amount recorded" },         { date:"2026-04-11", pair:"Venu + Tom",              owner:"Tom Adams",            event:"New Opportunity",   deal:"On Running (Econ Security Policy RFP)",        value:    0, weighted:    0, detail:"New deal, no amount recorded" },         { date:"2026-04-08", pair:"Carla + Pranav",          owner:"Carla Grados Villamar",event:"Advance (Forward)", deal:"Diageo (Learning for Life)",                   value:39000, weighted: 9750, detail:"Proposal Submitted → In Negotiation" },         { date:"2026-04-07", pair:"Carla + Pranav",          owner:"Carla Grados Villamar",event:"New Opportunity",   deal:"SERTULL",                                      value:20000, weighted: 2000, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-09", pair:"Carla + Pranav",          owner:"Carla Grados Villamar",event:"New Opportunity",   deal:"Universidad Continental (New Assessment)",     value:15000, weighted: 1500, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-10", pair:"Carla + Pranav",          owner:"Carla Grados Villamar",event:"Closed Won",        deal:"IDB (Master Services Agreement)",              value:    0, weighted:    0, detail:"Same-day close, no amount" },         { date:"2026-04-09", pair:"Carla + Pranav",          owner:"Pranav Sridhar",       event:"Advance (Forward)", deal:"TSPI (2026 MFI)",                              value:13500, weighted: 5400, detail:"Proposal Submitted → Contract Signing" },         { date:"2026-04-11", pair:"Carla + Pranav",          owner:"Pranav Sridhar",       event:"Closed Won",        deal:"CInI (Govind Dairy Amendment)",                value:16500, weighted:-14850, detail:"Removed from Contract Signing (90%)" },         { date:"2026-04-08", pair:"Carla + Pranav",          owner:"Pranav Sridhar",       event:"New Opportunity",   deal:"Symbiotics SA (Signal Pro)",                   value:    0, weighted:    0, detail:"New deal, no amount recorded" },         { date:"2026-04-07", pair:"Louis + Ellie + Tripti",  owner:"Louis Tran Van Lieu",  event:"New Opportunity",   deal:"Draper Richards Kaplan (Signal)",              value:40000, weighted: 4000, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-09", pair:"Louis + Ellie + Tripti",  owner:"Louis Tran Van Lieu",  event:"New Opportunity",   deal:"Save the Children Global Ventures (Signal)",   value:20000, weighted: 2000, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-10", pair:"Louis + Ellie + Tripti",  owner:"Louis Tran Van Lieu",  event:"Closed Lost",       deal:"SixPoint (Signal Core Plus)",                  value:35000, weighted:-17500, detail:"Removed from Proposal Submitted (50%)" },         { date:"2026-04-11", pair:"Louis + Ellie + Tripti",  owner:"Ellie Turner",         event:"Closed Lost",       deal:"Climate Impact Partners (Carbon Afforestation)",value:25000, weighted:-12500, detail:"Removed from Proposal Submitted (50%)" },         { date:"2026-04-08", pair:"Louis + Ellie + Tripti",  owner:"Tripti Singh",         event:"New Opportunity",   deal:"Ubongo (Learning Studies)",                    value:80000, weighted: 8000, detail:"New deal, entered at Qualified (10%)" },         { date:"2026-04-12", pair:"Louis + Ellie + Tripti",  owner:"Tripti Singh",         event:"Closed Won",        deal:"EnAccess Foundation (Cost Sharing)",           value: 1500, weighted: -1350, detail:"Removed from Contract Signing (90%)" },         { date:"2026-04-09", pair:"Other",                   owner:"Atul Sukumar",         event:"Advance (Forward)", deal:"RestoringVision (Uganda)",                     value:30000, weighted:12000, detail:"Qualified → Proposal Submitted" }       ]     }     // Historical weeks would populate here on future runs.   },   weekly_advances_trend: [     { week: "2026-03-16", weighted_advances: 125930 },     { week: "2026-03-23", weighted_advances: 418157 },     { week: "2026-03-30", weighted_advances: 240813 },     { week: "2026-04-06", weighted_advances:  60950 }   ] };  // ============ RENDER ============ const STAGE_COLORS = ['#F4B942','#EC8B5E','#D96A9E','#C0448F','#8B2B6B'];  function fmt$(n) {   const a = Math.abs(n);   const sign = n < 0 ? '-' : '';   if (a >= 1e6) return sign + '$' + (a/1e6).toFixed(2) + 'M';   if (a >= 1e3) return sign + '$' + (a/1e3).toFixed(1) + 'K';   return sign + '$' + a.toFixed(0); } function fmt$signed(n) { return (n > 0 ? '+' : n < 0 ? '' : '') + fmt$(n); }  let currentWeek = DATA.current_week; let currentPair = 'all'; let sortKey = 'weighted'; let sortDir = 'desc'; let trendChart = null;  function getSnapshot() { return DATA.weekly_snapshots[currentWeek]; }  function renderKPIs() {   const s = getSnapshot(); if (!s) return;   const k = s.kpis;   document.getElementById('kpi-pipeline').textContent = fmt$(k.pipeline);   const delta = document.getElementById('kpi-pipeline-delta');   delta.textContent = fmt$signed(k.pipeline_delta) + ' (' + (k.pipeline_delta_pct>=0?'+':'') + k.pipeline_delta_pct.toFixed(1) + '%) WoW';   delta.className = 'kpi-sub ' + (k.pipeline_delta >= 0 ? 'pos' : 'neg');    document.getElementById('kpi-movement').textContent = fmt$signed(k.net_movement);   document.getElementById('kpi-movement-sub').textContent = 'Adds ' + fmt$signed(k.adds) + ' · Removes ' + fmt$signed(k.removes);    document.getElementById('kpi-won').textContent = fmt$(k.closed_won);   document.getElementById('kpi-won-sub').textContent = k.closed_count + ' deals · ' + fmt$(k.won_mtd) + ' MTD';    document.getElementById('kpi-sqls').textContent = k.new_sqls;   document.getElementById('kpi-sqls-sub').textContent = k.sqls_mtd + ' MTD · ' + k.sqls_goal_remaining + ' to April goal'; }  function renderStageBar() {   const s = getSnapshot(); if (!s) return;   const total = s.stages.reduce((a,b)=>a+b.weighted, 0);   const bar = document.getElementById('stage-bar');   const legend = document.getElementById('stage-legend');   bar.innerHTML = s.stages.map((st, i) => {     const pct = (st.weighted/total*100).toFixed(1);     return `<div class="stage-seg" style="flex:${st.weighted}" title="${st.name}: ${fmt$(st.weighted)} (${pct}%)">${st.weighted > total*0.07 ? fmt$(st.weighted) : ''}</div>`;   }).join('');   legend.innerHTML = s.stages.map((st, i) =>     `<div><span class="dot" style="background:${STAGE_COLORS[i]}"></span>${st.name} (${st.deals})</div>`   ).join(''); }  function renderPairs() {   const s = getSnapshot(); if (!s) return;   const list = document.getElementById('pair-list');   const designated = s.pairs.filter(p => p.designated);   const pairs = currentPair === 'all' ? designated : designated.filter(p => p.name === currentPair);   const maxAbs = Math.max(...designated.map(p => Math.abs(p.net)), 1);   list.innerHTML = pairs.map(p => {     const pct = Math.abs(p.net) / maxAbs * 48; // max 48% of half-width     let barHtml;     if (p.net > 0) barHtml = `<div class="pair-bar pos" style="width:${pct}%"></div>`;     else if (p.net < 0) barHtml = `<div class="pair-bar neg" style="width:${pct}%"></div>`;     else barHtml = `<div class="pair-bar zero"></div>`;     const cls = p.net > 0 ? 'pos' : p.net < 0 ? 'neg' : 'zero';     return `       <div class="pair-row">         <div class="pair-name">${p.name}</div>         <div class="pair-bar-wrap">           <div class="pair-mid"></div>           ${barHtml}         </div>         <div class="pair-val ${cls}">${fmt$signed(p.net)}</div>       </div>`;   }).join(''); }  function renderTrend() {   const ctx = document.getElementById('trend-chart').getContext('2d');   const labels = DATA.weekly_advances_trend.map(w => w.week.slice(5)); // MM-DD   const vals = DATA.weekly_advances_trend.map(w => w.weighted_advances);   if (trendChart) trendChart.destroy();   trendChart = new Chart(ctx, {     type: 'bar',     data: {       labels,       datasets: [{         label: 'Weighted Stage Advances',         data: vals,         backgroundColor: vals.map((_, i) => i === vals.length - 1 ? '#4A8A90' : '#A0C0C4'),         borderRadius: 4       }]     },     options: {       responsive: true,       maintainAspectRatio: false,       plugins: {         legend: { display: false },         tooltip: { callbacks: { label: c => fmt$(c.parsed.y) } }       },       scales: {         x: { grid: { display: false } },         y: {           beginAtZero: true,           ticks: { callback: v => fmt$(v) }         }       }     }   }); }  function renderTable() {   const s = getSnapshot(); if (!s) return;   let events = [...s.events];   if (currentPair !== 'all') events = events.filter(e => e.pair === currentPair);   events.sort((a,b) => {     const av = a[sortKey], bv = b[sortKey];     const cmp = typeof av === 'number' ? (av - bv) : String(av).localeCompare(String(bv));     return sortDir === 'asc' ? cmp : -cmp;   });   document.getElementById('event-count').textContent = events.length;   const tag = e => {     const cls = e === 'New Opportunity' ? 'evt-new' :                 e === 'Advance (Forward)' ? 'evt-advance' :                 e === 'Closed Won' ? 'evt-won' :                 e === 'Closed Lost' ? 'evt-lost' : '';     return `<span class="evt-tag ${cls}">${e}</span>`;   };   const tbody = document.querySelector('#events-table tbody');   const fmtDate = d => {     if (!d) return '—';     const [y,m,day] = d.split('-');     const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];     return months[parseInt(m)-1] + ' ' + parseInt(day);   };   tbody.innerHTML = events.map(e => `     <tr>       <td style="white-space:nowrap;color:var(--muted);font-variant-numeric:tabular-nums">${fmtDate(e.date)}</td>       <td>${e.pair}</td>       <td>${e.owner}</td>       <td>${tag(e.event)}</td>       <td>${e.deal}</td>       <td class="num">${e.value ? fmt$(e.value) : '—'}</td>       <td class="num ${e.weighted > 0 ? 'pos' : e.weighted < 0 ? 'neg' : ''}">${e.weighted ? fmt$signed(e.weighted) : '—'}</td>       <td style="color:#5A7A74">${e.detail}</td>     </tr>   `).join(''); }  function populateFilters() {   const weekSel = document.getElementById('f-week');   weekSel.innerHTML = DATA.weeks.map(w => `<option value="${w.id}" ${w.id===currentWeek?'selected':''}>${w.label}</option>`).join('');   weekSel.addEventListener('change', e => {     currentWeek = e.target.value;     const wk = DATA.weeks.find(w => w.id === currentWeek);     document.getElementById('week-label').textContent = 'Week of ' + wk.label;     renderAll();   });    const pairSel = document.getElementById('f-pair');   const s = getSnapshot();   if (s) {     s.pairs.filter(p => p.designated).forEach(p => {       const opt = document.createElement('option');       opt.value = p.name; opt.textContent = p.name;       pairSel.appendChild(opt);     });   }   pairSel.addEventListener('change', e => {     currentPair = e.target.value;     renderPairs();     renderTable();   }); }  function setupSorting() {   document.querySelectorAll('#events-table thead th').forEach(th => {     th.addEventListener('click', () => {       const k = th.dataset.sort;       if (sortKey === k) sortDir = sortDir === 'asc' ? 'desc' : 'asc';       else { sortKey = k; sortDir = 'desc'; }       renderTable();     });   }); }  function renderCommentary() {   const s = getSnapshot(); if (!s || !s.commentary) return;   const c = s.commentary;   document.getElementById('commentary').innerHTML = `     <div class="block good"><h4>✓ What Went Well</h4><p>${c.good}</p></div>     <div class="block behind"><h4>⚠ Where We're Behind</h4><p>${c.behind}</p></div>     <div class="block color"><h4>Color on the Movement</h4><p>${c.color}</p></div>     <div class="block reco"><h4>→ This Week's Focus</h4><p>${c.reco}</p></div>   `; }  function renderAll() {   renderCommentary();   renderKPIs();   renderStageBar();   renderPairs();   renderTable();   renderTrend(); }  populateFilters(); setupSorting(); renderAll(); </script> </body> </html>