Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>RFH Intelligent Visualizer</title> | |
| <style> | |
| :root { --happiness: 0; } | |
| body { | |
| font-family: 'Segoe UI', sans-serif; | |
| display: flex; justify-content: center; align-items: center; | |
| min-height: 100vh; margin: 0; padding: 20px; | |
| transition: background 0.5s ease; | |
| } | |
| body.normal { | |
| background: linear-gradient( | |
| 135deg, | |
| hsl(calc(200 + var(--happiness) * 1.2), 80%, calc(30% + var(--happiness) * 0.3%)), | |
| hsl(calc(220 + var(--happiness) * 1.1), 70%, calc(40% + var(--happiness) * 0.4%)) | |
| ); | |
| } | |
| body.saddest { background: #444; } | |
| h1 { text-align: center; margin-bottom: 1rem; text-shadow: 0 2px 4px rgba(0,0,0,0.2); } | |
| .container { display: flex; gap: 2rem; max-width: 1000px; width: 100%; } | |
| .controls, .visuals { flex: 1; } | |
| .controls { | |
| background: rgba(255,255,255,0.15); backdrop-filter: blur(10px); | |
| border-radius: 16px; padding: 2rem; box-shadow: 0 8px 32px rgba(0,0,0,0.1); | |
| } | |
| .slider-group { margin-bottom: 1.5rem; } | |
| .label { display: flex; justify-content: space-between; margin-bottom: 0.5rem; } | |
| input[type=range] { width: 100%; height: 8px; appearance: none; background: rgba(255,255,255,0.3); border-radius: 4px; outline: none; } | |
| input[type=range]:disabled { background: rgba(200,200,200,0.3); } | |
| input[type=range]::-webkit-slider-thumb { appearance: none; width: 20px; height: 20px; background: white; border-radius: 50%; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } | |
| select { width: 100%; padding: 0.5rem; border-radius: 8px; border: none; } | |
| button { width: 100%; padding: 0.75rem; margin-top: 2rem; border: none; border-radius: 8px; background: white; font-size: 1rem; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } | |
| #canvas { width: 100%; height: 240px; margin-top: 1.5rem; } | |
| .flower-container { position: relative; width: 200px; height: 200px; margin: 1.5rem auto; } | |
| .flower { position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } | |
| .stem { position: absolute; background: linear-gradient(to bottom, #5a8f3d,#3a5e2b); width: 8px; height: 120px; bottom: 0; left: 50%; transform: translateX(-50%); transition: background 0.5s, transform 0.5s; } | |
| .flower.dead .stem { background: gray; transform: translateX(-50%) rotate(45deg); } | |
| .flower.saddest .stem { background: black; transform: translateX(-50%) rotate(60deg); } | |
| .petal { position: absolute; background: currentColor; border-radius: 50% 50% 0 50%; transform-origin: bottom center; opacity: 0; transition: opacity 0.5s ease; } | |
| .happiness-bar { width: 100%; height: 20px; background: rgba(0,0,0,0.1); border-radius: 10px; overflow: hidden; margin-top: 1rem; } | |
| .happiness-fill { height: 100%; width: 0%; background: hsl(200,80%,60%); transition: width 0.5s, background 0.5s; } | |
| </style> | |
| </head> | |
| <body class="normal"> | |
| <div class="container"> | |
| <div class="controls"> | |
| <h1>Rich, Boundary & Exposure Experiment</h1> | |
| <div class="slider-group"> | |
| <div class="label">Money: <span id="RVal">0%</span> (<span id="RAmount">€0</span>)</div> | |
| <input type="range" id="R" min="0" max="100" step="1" value="0" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="label">Fame: <span id="FVal">0%</span> (<span id="FCount">0</span> ppl)</div> | |
| <input type="range" id="F" min="0" max="100" step="1" value="0" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="label">Boundaries: <span id="BVal">0.00</span></div> | |
| <input type="range" id="B" min="0" max="1" step="0.01" value="0" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="label">Jurisdiction</div> | |
| <select id="jurisdiction"> | |
| <option value="0.6">High Privacy (Cayman)</option> | |
| <option value="0.7">Mod Privacy (Switzerland)</option> | |
| <option value="0.8">Low Privacy (EU)</option> | |
| <option value="1">High Exposure (US)</option> | |
| </select> | |
| </div> | |
| <button id="resetBtn">Reset</button> | |
| <div class="happiness-bar"><div class="happiness-fill" id="happinessFill"></div></div> | |
| <div class="flower-container"><div class="flower" id="flower"><div class="stem"></div></div></div> | |
| </div> | |
| <div class="visuals"><canvas id="canvas"></canvas></div> | |
| </div> | |
| <script> | |
| const R = document.getElementById('R'), F = document.getElementById('F'), B = document.getElementById('B'), Jur = document.getElementById('jurisdiction'); | |
| const RVal = document.getElementById('RVal'), FVal = document.getElementById('FVal'), BVal = document.getElementById('BVal'); | |
| const RAmount = document.getElementById('RAmount'), FCount = document.getElementById('FCount'); | |
| const resetBtn = document.getElementById('resetBtn'), canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'); | |
| const flower = document.getElementById('flower'), happinessFill = document.getElementById('happinessFill'), body = document.body; | |
| const maxMoney=5e9, maxPeople=8e9, basePetals=6, maxRings=5; let prevF=0; | |
| function compute(){ | |
| const s=R.value/100, Rnorm=Math.pow(s,3), money=Rnorm*maxMoney; | |
| let sliderF=+F.value; if(sliderF<prevF){sliderF=prevF; F.value=sliderF;} const moneyF=Rnorm*100; | |
| const actualF=Math.max(sliderF, moneyF); prevF=actualF; | |
| const fame=actualF/100, people=Math.round(fame*maxPeople); | |
| let Binput=+B.value; if(R.value==0){Binput=0; B.disabled=true;} else B.disabled=false; | |
| const boundaryCost=Binput*Math.pow(fame,2); | |
| const RnormEff=Math.max(0,Rnorm*(1-boundaryCost)); | |
| const effectiveB=Binput*RnormEff; | |
| const jur=+Jur.value; | |
| const Enorm=fame*(1-effectiveB)*(1-effectiveB)*jur; | |
| const U_money=Math.sqrt(RnormEff), U_bound=effectiveB, pen_exposure=Math.pow(Enorm,1.2); | |
| let Hraw=U_money+U_bound-pen_exposure; | |
| let Hnorm=Math.max(0,Math.min(1,(Hraw+0.5)/1.5)); | |
| const Hperc=Hnorm*100; | |
| // dynamic money slider max | |
| const newMax=Math.max(5, 100*(1-Hnorm*0.5)); R.max=newMax; | |
| return {RnormEff,actualF,people,effectiveB,Enorm,boundaryCost,Hperc,Hnorm}; | |
| } | |
| function draw(){ | |
| const {RnormEff,actualF,people,effectiveB,Enorm,boundaryCost,Hperc,Hnorm}=compute(); | |
| RVal.textContent=`${Math.round(R.value)}%`; | |
| FVal.textContent=`${actualF.toFixed(0)}%`; | |
| FCount.textContent=people.toLocaleString(); | |
| BVal.textContent=effectiveB.toFixed(2); | |
| RAmount.textContent=`€${(RnormEff*maxMoney).toLocaleString()}`; | |
| body.className=(R.value==0 && actualF==0)?'saddest':'normal'; | |
| body.style.setProperty('--happiness',Hperc); | |
| happinessFill.style.width=`${Hperc}%`; | |
| happinessFill.style.background=`hsl(${Hperc*1.2},80%,60%)`; | |
| flower.classList.toggle('dead',Hperc===0); | |
| flower.querySelectorAll('.petal').forEach(p=>p.remove()); | |
| const rings=Math.floor(Hnorm*maxRings); | |
| for(let r=1;r<=rings;r++){ | |
| const petalsInRing=r*basePetals, radius=30+r*20; | |
| for(let i=0;i<petalsInRing;i++){ | |
| const angle=(i/petalsInRing)*360; | |
| const petal=document.createElement('div'); petal.className='petal'; | |
| petal.style.width='15px'; petal.style.height='15px'; | |
| petal.style.color=`hsl(${Hperc*1.2+(r-1)*30},80%,60%)`; | |
| petal.style.transform=`rotate(${angle}deg) translateY(-${radius}px) rotate(-${angle}deg)`; | |
| petal.style.opacity=Hnorm; | |
| flower.appendChild(petal); | |
| } | |
| } | |
| ctx.clearRect(0,0,canvas.width,canvas.height); | |
| ctx.font='16px Arial'; ctx.fillStyle='#000'; | |
| ctx.fillText(`Money Util √R: ${Math.sqrt(RnormEff).toFixed(2)}`,20,30); | |
| ctx.fillText(`Boundary Util: ${effectiveB.toFixed(2)}`,20,55); | |
| ctx.fillText(`Boundary Cost: ${boundaryCost.toFixed(2)}`,20,80); | |
| ctx.fillText(`Exposure Penalty: ${Enorm.toFixed(2)}`,20,105); | |
| ctx.fillText(`Happiness: ${Hperc.toFixed(1)}%`,20,130); | |
| ctx.fillStyle='rgba(0,0,0,0.1)'; ctx.fillRect(20,150,460,20); | |
| ctx.fillStyle=`hsl(${Hperc*1.2},80%,60%)`; ctx.fillRect(20,150,(Hperc/100)*460,20); | |
| ctx.fillStyle='#000'; ctx.font='14px "Segoe UI"'; ctx.fillText('Low H',20,185); ctx.fillText('High H',460,185); | |
| } | |
| [R,F,B,Jur].forEach(el=>el.addEventListener('input',draw)); | |
| resetBtn.addEventListener('click',()=>{R.value=0;F.value=0;B.value=0;Jur.value='0.6';prevF=0;draw();}); | |
| draw(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=web3district/happy-combo" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> | |