/* The "your card" line: a measured card, how often it finds a block at the network's hash rate right now, and what its electricity costs a day in your currency. One block a second is the target, so the wait is network hashes per second over the card's rate. The rates are the bench table's measured numbers (site/miners.html); the watts are the tuned draws from the same runs (app.html's Ember table: 5090 226.8 W, 4070 75.6 W; the 9070 XT 202 W from docs/bench-log.md, 6 October 2026; Apple silicon reports no draw). The currency follows navigator.language (no IP lookup); the switch saves a choice in localStorage igneum-currency; the tariff is the public table's default for that region or currency, so the cost is arithmetic from a typical price, never a promise (currency-21, 7 October 2026). Reads /api/live every 15 s. */ (function () { var CARDS = [['NVIDIA RTX 5090', 127.7, 226.8], ['NVIDIA RTX 4070', 28.8, 75.6], ['Apple M5 Max', 26.7, null], ['AMD RX 9070 XT', 17.8, 202]]; var els = document.querySelectorAll('[data-yourcard]'); if (!els.length) return; var net = null, chosen = (function () { try { return localStorage.getItem('igneum-card'); } catch (e) { return null; } })(); var savedCur = (function () { try { return localStorage.getItem('igneum-currency') || ''; } catch (e) { return ''; } })(); function dur(sec) { if (!(sec > 0)) return 'pending'; if (sec < 90) return Math.round(sec) + ' s'; if (sec < 5400) return Math.round(sec / 60) + ' min'; if (sec < 172800) { var h = Math.floor(sec / 3600), m = Math.round((sec % 3600) / 60); return h + ' h' + (m ? ' ' + m + ' min' : ''); } return Math.round(sec / 86400) + ' days'; } function costWords(watts) { var M = window.IgneumMoney; if (!M) return ''; var ch = M.currencyChoice(savedCur, navigator.language), t = M.tariffFor(ch); if (watts === null) return 'no power reading on this card'; if (!t) return 'no typical ' + ch.code + ' tariff in the table; the app uses the price you type'; var c = M.costPerDay(watts, t.price); return 'about ' + M.fmtMoney(c, ch.code, navigator.language) + ' a day in electricity at ' + t.price + ' ' + M.minorOf(ch.code).abbr + '/kWh (' + t.region + ', typical)'; } function render() { els.forEach(function (el) { var sel = el.querySelector('select.card'), r = parseFloat(sel.value) * 1e6, eta = el.querySelector('.eta'), cost = el.querySelector('.cost'); eta.textContent = net && r ? 'every ' + dur(net / r) : 'pending'; var row = CARDS[sel.selectedIndex]; if (cost) cost.textContent = costWords(row ? row[2] : null); }); } function init() { var M = window.IgneumMoney, ch = M ? M.currencyChoice(savedCur, navigator.language) : null; els.forEach(function (el) { el.innerHTML = 'Your cardfinds a block about pending at the network\u2019s hash rate right now.' + (M ? '' : ''); el.querySelector('select.card').addEventListener('change', function (e) { var name = e.target.options[e.target.selectedIndex].text.split(' \u00b7 ')[0]; try { localStorage.setItem('igneum-card', name); } catch (x) {} document.querySelectorAll('[data-yourcard] select.card').forEach(function (s) { s.value = e.target.value; }); render(); }); var cur = el.querySelector('select.cur'); if (cur) cur.addEventListener('change', function (e) { savedCur = e.target.value; try { localStorage.setItem('igneum-currency', savedCur); } catch (x) {} document.querySelectorAll('[data-yourcard] select.cur').forEach(function (s) { s.value = savedCur; }); render(); }); }); render(); } if (window.IgneumMoney) init(); else { var sc = document.createElement('script'); sc.src = '/money.js'; sc.onload = init; sc.onerror = init; document.head.appendChild(sc); } function poll() { fetch('/api/live?window=30', { cache: 'no-store' }).then(function (r) { return r.json(); }).then(function (d) { if (d && d.ok && d.state && d.state.hashes_per_second_estimate > 0) { net = d.state.hashes_per_second_estimate; render(); } }).catch(function () {}); } poll(); setInterval(function () { if (!document.hidden) poll(); }, 15000); window.IgneumYourCard = { update: function (h) { if (h > 0) { net = h; render(); } } }; })();