Give the top of the ladder a real prize: Team Grants + Network Intelligence
Marty's read was right — the upper tiers felt flat, and the reason is that they scaled QUANTITY, not KIND. L1->L2 was a category change (you couldn't write, now you can). Everything above L4 was a multiplier on a capability already owned at L2: sound more like you, measure it, more pages, five at once. Meanwhile the price doubles each rung — Corona costs ~17x Culmen. No batch button is worth 17x a voice profile. The fix is a change of axis. Every tool from L1 to L8 was a "me" tool, but past the first levels this business isn't about you: get two, help your two, teach them to teach. So the top now operates on the ORGANISATION. L7 Team Grants — hand Suite capacity down to anyone in your own org. Deliberately from a separate pool that the level grants, NOT out of the leader's own allowance: making someone choose between equipping their team and using their own tools means nobody ever grants anything. Recipients are verified in-org against the contract. Grants can lift a tool the recipient's level hasn't unlocked — that's the point, not a loophole. Being helped is made visible to the recipient, since half the value is knowing an upline backed you. L8 Network Intelligence — every team ad pooled and anonymised: which angles, headlines, creative and formats actually earn their impressions. No member alone has enough traffic to learn anything from display; together they do. It can't be bought, and it compounds monthly with no further work. Holds two lines: nothing identifies anyone, and nothing under 2,000 impressions gets a reported rate — under-evidenced rows are counted and disclosed rather than silently dropped. The written readout is hand-authored, not generated: these are conclusions about money, and a model paraphrasing a table is exactly where an invented number slips in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
// Team Grants client.
|
||||
(function () {
|
||||
'use strict';
|
||||
var $ = function (id) { return document.getElementById(id); };
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
||||
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
||||
});
|
||||
}
|
||||
function n(v) { return Number(v || 0).toLocaleString(); }
|
||||
|
||||
var state = { tool: null, status: null, level: 0, minLevel: 7 };
|
||||
|
||||
function gate(msg) {
|
||||
$('gGate').style.display = 'block';
|
||||
$('gGate').innerHTML = msg;
|
||||
}
|
||||
|
||||
// Shown to everyone, including people below level 7 — being helped should be
|
||||
// visible, not silent.
|
||||
function renderReceived(received) {
|
||||
var keys = Object.keys(received || {});
|
||||
if (!keys.length) { $('gGot').innerHTML = ''; return; }
|
||||
var parts = keys.map(function (k) {
|
||||
var r = received[k];
|
||||
var who = r.from.map(function (f) { return '#' + f.by; });
|
||||
var uniq = who.filter(function (v, i) { return who.indexOf(v) === i; });
|
||||
var t = (state.status && state.status.tools[k]) || {};
|
||||
return '<div>· <b style="color:var(--text)">' + n(r.total) + ' ' + esc(t.unit || k) +
|
||||
'</b> of ' + esc((t.label || k).toLowerCase()) + ', from ' + uniq.join(', ') + '</div>';
|
||||
});
|
||||
$('gGot').innerHTML = '<div class="g-got"><b style="color:var(--teal)">Someone above you topped you up this month.</b>' +
|
||||
parts.join('') + '<div style="color:var(--muted);font-size:12.5px;margin-top:6px">' +
|
||||
'It is already added to your allowances — nothing to claim.</div></div>';
|
||||
}
|
||||
|
||||
function renderPools() {
|
||||
var host = $('gPools');
|
||||
host.innerHTML = '';
|
||||
var tools = state.status.tools;
|
||||
Object.keys(tools).forEach(function (k) {
|
||||
var t = tools[k];
|
||||
var d = document.createElement('div');
|
||||
d.className = 'g-pool' + (state.tool === k ? ' on' : '');
|
||||
d.innerHTML = '<div class="big">' + n(t.remaining) + '</div>' +
|
||||
'<div class="lab">' + esc(t.label) + '</div>' +
|
||||
'<div class="of">left to give of ' + n(t.pool) + '</div>';
|
||||
d.addEventListener('click', function () {
|
||||
state.tool = k;
|
||||
renderPools();
|
||||
var el = $('gN');
|
||||
el.max = Math.min(t.max, t.remaining);
|
||||
el.step = t.step;
|
||||
el.value = Math.min(t.step, t.remaining) || '';
|
||||
$('gNHint').textContent = 'Up to ' + n(Math.min(t.max, t.remaining)) + ' ' + t.unit +
|
||||
' in one go. You have ' + n(t.remaining) + ' left to give this month.';
|
||||
});
|
||||
host.appendChild(d);
|
||||
});
|
||||
}
|
||||
|
||||
function renderHistory(hist) {
|
||||
if (!hist || !hist.length) { $('gHistWrap').style.display = 'none'; return; }
|
||||
$('gHistWrap').style.display = 'block';
|
||||
var tb = $('gHist');
|
||||
tb.innerHTML = '';
|
||||
hist.forEach(function (h) {
|
||||
var t = (state.status.tools[h.tool]) || {};
|
||||
var tr = document.createElement('tr');
|
||||
tr.innerHTML = '<td><b>#' + esc(h.to) + '</b></td>' +
|
||||
'<td>' + esc(t.label || h.tool) + '</td>' +
|
||||
'<td><b>' + n(h.n) + '</b> ' + esc(t.unit || '') + '</td>' +
|
||||
'<td>' + esc(String(h.at || '').slice(0, 10)) + '</td>';
|
||||
tb.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
async function give() {
|
||||
$('gErr').style.display = 'none';
|
||||
$('gOk').style.display = 'none';
|
||||
if (!state.tool) {
|
||||
$('gErr').textContent = 'Pick what you want to give first.';
|
||||
$('gErr').style.display = 'block';
|
||||
return;
|
||||
}
|
||||
var to = Number($('gTo').value) || 0;
|
||||
var amount = Number($('gN').value) || 0;
|
||||
if (!to) {
|
||||
$('gErr').textContent = 'Enter the position number you want to help.';
|
||||
$('gErr').style.display = 'block';
|
||||
return;
|
||||
}
|
||||
$('gGo').disabled = true;
|
||||
$('gGo').innerHTML = '<span class="spin"></span>Sending…';
|
||||
try {
|
||||
var r = await fetch('/api/public/suite-grants', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ to: to, tool: state.tool, n: amount })
|
||||
});
|
||||
var d = await r.json();
|
||||
if (!r.ok) {
|
||||
$('gErr').textContent = d.error || 'That did not go through.';
|
||||
$('gErr').style.display = 'block';
|
||||
} else {
|
||||
state.status = d.status;
|
||||
var t = state.status.tools[d.granted.tool] || {};
|
||||
$('gOk').innerHTML = '✅ <b>Sent.</b> #' + esc(d.granted.to) + ' now has ' + n(d.granted.n) + ' more ' +
|
||||
esc(t.unit || '') + ' this month. Worth telling them — it will just appear in their allowance otherwise.';
|
||||
$('gOk').style.display = 'block';
|
||||
renderPools();
|
||||
renderHistory(state.status.history);
|
||||
}
|
||||
} catch (e) {
|
||||
$('gErr').textContent = 'Connection hiccup — try again.';
|
||||
$('gErr').style.display = 'block';
|
||||
}
|
||||
$('gGo').disabled = false;
|
||||
$('gGo').innerHTML = '🎁 Give it to them';
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
try {
|
||||
var r = await fetch('/api/public/suite-grants');
|
||||
if (r.status === 401) { gate('You’re not signed in yet. <a href="/suite" style="color:var(--gold);font-weight:700">Open the Suite</a> and connect the wallet that holds your position, then come back.'); return; }
|
||||
if (r.status === 403) {
|
||||
var g = await r.json();
|
||||
gate((g.error || 'Not open for this position yet.') + ' <a href="/suite" style="color:var(--gold);font-weight:700">See your Suite</a>.');
|
||||
return;
|
||||
}
|
||||
if (!r.ok) return;
|
||||
var d = await r.json();
|
||||
state.status = d.status;
|
||||
state.level = d.level;
|
||||
state.minLevel = d.minLevel;
|
||||
|
||||
renderReceived(d.status.received);
|
||||
|
||||
if (!d.status.canGrant) {
|
||||
gate('<b>Team Grants unlock at Vertex (level 7).</b> At that point you can hand your own Suite capacity ' +
|
||||
'down to anyone in your organisation who needs it. Anything granted <em>to</em> you shows above and is already in your allowances.');
|
||||
return;
|
||||
}
|
||||
$('gBody').style.display = 'block';
|
||||
var first = Object.keys(d.status.tools)[0];
|
||||
state.tool = first;
|
||||
renderPools();
|
||||
var t = d.status.tools[first];
|
||||
$('gN').max = Math.min(t.max, t.remaining);
|
||||
$('gN').step = t.step;
|
||||
$('gN').value = Math.min(t.step, t.remaining) || '';
|
||||
$('gNHint').textContent = 'Up to ' + n(Math.min(t.max, t.remaining)) + ' ' + t.unit + ' in one go.';
|
||||
renderHistory(d.status.history);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
boot();
|
||||
$('gGo').addEventListener('click', give);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user