Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions src/js/18-scary-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@

// ── PRD 4: Your Tab (Running) strip ──────────────────────────

// Per-item throttle timestamp for the floating +N pop animations.
let _lastTabPop = 0;

// Conversion constants shared between session totals and per-second pop deltas.
const WATER_PER_COFFEE_L = 0.2; // litres of water per cup of coffee
const KWH_PER_CHARGE = 0.015; // kWh per smartphone charge
const CO2_PER_METRE_KG = 0.000171; // kg CO₂ per metre driven (171 g/km)

function updateSessionTabStrip() {
const now = Date.now();
const elapsed = Math.max(1, (now - pageLoadTime) / 1000);
Expand All @@ -105,13 +113,13 @@
const impact = calculateEnvironmentalImpact(tokens);

// Cups of coffee (200 mL per cup, water use)
const coffees = Math.max(0, impact.waterL / 0.2);
const coffees = Math.max(0, impact.waterL / WATER_PER_COFFEE_L);
// Trees needed for a year
const trees = Math.max(0, impact.treesEquivalent);
// Smartphone charges (0.015 kWh per charge)
const charges = Math.max(0, impact.kWh / 0.015);
const charges = Math.max(0, impact.kWh / KWH_PER_CHARGE);
// Metres driven (171 g CO2/km = 0.000171 kg/m)
const metres = Math.max(0, impact.co2Kg / 0.000171);
const metres = Math.max(0, impact.co2Kg / CO2_PER_METRE_KG);

function fmtSmall(n) {
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
Expand All @@ -131,6 +139,27 @@
if (treesEl) treesEl.textContent = fmtSmall(trees);
if (chargeEl) chargeEl.textContent = fmtSmall(charges);
if (metresEl) metresEl.textContent = fmtSmall(metres);

// Floating "+N" pops — once per second, showing the per-second increment for each stat.
if (now - _lastTabPop >= 1000) {
_lastTabPop = now;
const impactPerSec = calculateEnvironmentalImpact(rate);
const coffeesPerSec = impactPerSec.waterL / WATER_PER_COFFEE_L;
const treesPerSec = impactPerSec.treesEquivalent;
const chargesPerSec = impactPerSec.kWh / KWH_PER_CHARGE;
const metresPerSec = impactPerSec.co2Kg / CO2_PER_METRE_KG;
const MIN_TAB_POP_THRESHOLD = 0.005;
[
{ el: waterEl, val: coffeesPerSec },
{ el: treesEl, val: treesPerSec },
{ el: chargeEl, val: chargesPerSec },
{ el: metresEl, val: metresPerSec },
].forEach(({ el, val }) => {
if (!el || val < MIN_TAB_POP_THRESHOLD) return;
const item = el.closest('.session-tab-item');
spawnPop(item, '+' + fmtSmall(val), 'token-pop--tab');
});
}
}

function initSessionTabStrip() {
Expand Down
7 changes: 7 additions & 0 deletions styles/counter-milestones.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@
font-size: 0.7rem;
}

/* Your Tab (Running) pop — orange, matches .sti-val colour */
.token-pop--tab {
color: var(--accent-2);
text-shadow: 0 0 8px rgba(255,136,0,0.5);
font-size: 0.7rem;
}

@keyframes token-pop-float {
0% { opacity: 0; transform: translateX(-50%) translateY(0); }
15% { opacity: 1; }
Expand Down
2 changes: 2 additions & 0 deletions styles/scary-features.css
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@
padding: 0.5rem 0.75rem;
font-size: 0.78rem;
color: var(--text-dim);
position: relative;
overflow: visible;
}

.sti-icon { font-size: 1.1rem; flex-shrink: 0; }
Expand Down
Loading