Spaces:
Running
Running
Update index.html
Browse files- index.html +423 -0
index.html
CHANGED
|
@@ -640,6 +640,61 @@ body{font-family:'Outfit',sans-serif;background:var(--bg);color:var(--text);marg
|
|
| 640 |
.npc-stat-val{font-size:13px;}
|
| 641 |
.npc-open-detail{flex-wrap:wrap;gap:6px;}
|
| 642 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 643 |
</style>
|
| 644 |
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
| 645 |
</head>
|
|
@@ -674,6 +729,7 @@ body{font-family:'Outfit',sans-serif;background:var(--bg);color:var(--text);marg
|
|
| 674 |
<button class="tab" data-tab="arena" onclick="switchTab('arena')">⚔️ Arena</button>
|
| 675 |
<button class="tab" data-tab="battle" onclick="switchTab('battle')">⚔️ Battle</button>
|
| 676 |
<button class="tab" data-tab="sec" onclick="switchTab('sec')">🚨 SEC</button>
|
|
|
|
| 677 |
<button class="tab" data-tab="livechat" onclick="switchTab('livechat')">💬 Live Chat</button>
|
| 678 |
<div class="tab-spacer"></div>
|
| 679 |
<button class="tab" onclick="refreshCurrentTab()" id="globalRefreshBtn" title="Refresh current tab" style="font-size:14px;min-width:36px;padding:6px 10px">🔄</button>
|
|
@@ -710,6 +766,11 @@ body{font-family:'Outfit',sans-serif;background:var(--bg);color:var(--text);marg
|
|
| 710 |
</div>
|
| 711 |
<!-- MVP / VILLAIN -->
|
| 712 |
<div class="ln-mvp-row" id="lnMvpRow" style="display:none"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 713 |
<!-- MAIN STUDIO (featured story with anchor) -->
|
| 714 |
<div class="ln-main-scroll">
|
| 715 |
<div class="ln-studio" id="lnStudio" style="display:none"></div>
|
|
@@ -888,6 +949,57 @@ body{font-family:'Outfit',sans-serif;background:var(--bg);color:var(--text);marg
|
|
| 888 |
</div>
|
| 889 |
</div>
|
| 890 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 891 |
<div class="panel" id="panel-mypage"><div class="cpanel" id="cm"></div></div>
|
| 892 |
</div></div>
|
| 893 |
<!-- ★ SSE Live Notification Bar -->
|
|
@@ -1045,6 +1157,7 @@ function switchTab(t){
|
|
| 1045 |
else if(t==='halloffame')loadHallOfFame();
|
| 1046 |
else if(t==='news'){loadMarketPulse();loadNewsFeed();}else if(t==='analysis')loadResearchDesk();
|
| 1047 |
else if(t==='sec')loadSECDashboard();
|
|
|
|
| 1048 |
else if(t==='livechat')initLiveChat();
|
| 1049 |
else{cBoard=t;loadPosts(t);}
|
| 1050 |
}
|
|
@@ -2580,6 +2693,7 @@ async function loadLiveNews(){
|
|
| 2580 |
try{renderLiveMvp(r.mvp||null, r.villain||null);}catch(e){console.warn('LN MVP render:',e);}
|
| 2581 |
try{renderLiveStudio(r.stories||[]);}catch(e){console.warn('LN Studio render:',e);}
|
| 2582 |
try{renderLiveFeed(r.stories||[]);}catch(e){console.warn('LN Feed render:',e);}
|
|
|
|
| 2583 |
// Auto-refresh every 60s
|
| 2584 |
if(lnAutoRefresh) clearInterval(lnAutoRefresh);
|
| 2585 |
lnAutoRefresh = setInterval(()=>{if(cTab==='livenews')loadLiveNews();},60000);
|
|
@@ -2740,6 +2854,315 @@ function renderLiveFeed(stories){
|
|
| 2740 |
}).join('');
|
| 2741 |
}
|
| 2742 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2743 |
/* ====== Init: load live news on startup ====== */
|
| 2744 |
|
| 2745 |
checkLogin();
|
|
|
|
| 640 |
.npc-stat-val{font-size:13px;}
|
| 641 |
.npc-open-detail{flex-wrap:wrap;gap:6px;}
|
| 642 |
}
|
| 643 |
+
/* Person of the Day */
|
| 644 |
+
.ln-pod-row{display:flex;gap:10px;padding:8px 16px;overflow-x:auto;flex-shrink:0;}
|
| 645 |
+
.ln-pod-loading{font-size:12px;color:var(--muted);padding:12px;text-align:center;width:100%;}
|
| 646 |
+
.ln-pod-card{flex:1;min-width:200px;max-width:320px;border-radius:12px;padding:14px;border:1px solid rgba(255,255,255,0.08);position:relative;overflow:hidden;transition:transform 0.2s;}
|
| 647 |
+
.ln-pod-card:hover{transform:translateY(-2px);}
|
| 648 |
+
.ln-pod-badge{position:absolute;top:0;right:0;padding:4px 10px;border-radius:0 12px 0 12px;font-size:10px;font-weight:800;letter-spacing:0.5px;}
|
| 649 |
+
.ln-pod-emoji{font-size:32px;margin-bottom:6px;}
|
| 650 |
+
.ln-pod-name{font-size:15px;font-weight:800;margin-bottom:2px;}
|
| 651 |
+
.ln-pod-identity{font-size:10px;padding:2px 8px;border-radius:10px;display:inline-block;margin-bottom:6px;font-weight:600;}
|
| 652 |
+
.ln-pod-stat{font-size:20px;font-weight:900;font-family:'JetBrains Mono',monospace;margin-bottom:4px;}
|
| 653 |
+
.ln-pod-reason{font-size:11px;color:var(--muted);line-height:1.4;}
|
| 654 |
+
.ln-pod-meta{font-size:10px;color:var(--muted);margin-top:6px;display:flex;gap:8px;}
|
| 655 |
+
/* Republic Dashboard */
|
| 656 |
+
.republic-dash{padding:16px;overflow-y:auto;max-height:calc(100vh - 160px);}
|
| 657 |
+
.rp-header{display:flex;align-items:center;gap:12px;margin-bottom:12px;padding-bottom:12px;border-bottom:2px solid rgba(162,155,254,0.3);}
|
| 658 |
+
.rp-flag{font-size:38px;filter:drop-shadow(0 0 12px rgba(162,155,254,0.4));}
|
| 659 |
+
.rp-recession{background:linear-gradient(90deg,#cc0000,#990000);color:#fff;padding:10px 16px;border-radius:8px;font-size:13px;font-weight:700;text-align:center;margin-bottom:12px;animation:breakPulse 2s infinite;}
|
| 660 |
+
.rp-top-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px;}
|
| 661 |
+
.rp-metric-card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:14px;text-align:center;position:relative;overflow:hidden;}
|
| 662 |
+
.rp-metric-val{font-size:22px;font-weight:900;font-family:'JetBrains Mono',monospace;margin-bottom:2px;}
|
| 663 |
+
.rp-metric-lbl{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;}
|
| 664 |
+
.rp-metric-sub{font-size:11px;margin-top:4px;}
|
| 665 |
+
.rp-body{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
|
| 666 |
+
.rp-col{display:flex;flex-direction:column;gap:12px;}
|
| 667 |
+
.rp-card{background:var(--card);border:1px solid var(--border);border-radius:12px;overflow:hidden;}
|
| 668 |
+
.rp-card-title{font-size:13px;font-weight:800;padding:12px 14px;border-bottom:1px solid var(--border);background:rgba(255,255,255,0.02);}
|
| 669 |
+
.rp-card-body{padding:14px;font-size:12px;}
|
| 670 |
+
.rp-bar{height:20px;border-radius:4px;background:rgba(255,255,255,0.05);overflow:hidden;display:flex;margin:4px 0;}
|
| 671 |
+
.rp-bar-seg{height:100%;transition:width 0.5s;}
|
| 672 |
+
.rp-lorenz{margin-top:10px;position:relative;height:160px;background:rgba(0,0,0,0.2);border-radius:8px;overflow:hidden;}
|
| 673 |
+
.rp-lorenz canvas{width:100%;height:100%;}
|
| 674 |
+
.rp-rank{display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid rgba(255,255,255,0.04);font-size:11px;}
|
| 675 |
+
.rp-rank:last-child{border-bottom:none;}
|
| 676 |
+
.rp-rank-name{font-weight:600;}
|
| 677 |
+
.rp-rank-val{font-weight:800;font-family:'JetBrains Mono',monospace;}
|
| 678 |
+
.rp-gauge{height:10px;border-radius:5px;background:rgba(255,255,255,0.06);overflow:hidden;margin:6px 0;}
|
| 679 |
+
.rp-gauge-fill{height:100%;border-radius:5px;transition:width 0.6s;}
|
| 680 |
+
.rp-sector-row{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid rgba(255,255,255,0.04);}
|
| 681 |
+
.rp-sector-row:last-child{border-bottom:none;}
|
| 682 |
+
.rp-sector-bar{flex:1;height:16px;border-radius:4px;background:rgba(255,255,255,0.05);overflow:hidden;}
|
| 683 |
+
.rp-sector-fill{height:100%;border-radius:4px;}
|
| 684 |
+
.rp-sector-pct{width:45px;text-align:right;font-weight:800;font-family:'JetBrains Mono',monospace;font-size:11px;}
|
| 685 |
+
.rp-id-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;}
|
| 686 |
+
.rp-id-item{padding:4px 6px;border-radius:6px;background:rgba(255,255,255,0.03);font-size:10px;text-align:center;}
|
| 687 |
+
.rp-lev-dist{display:flex;gap:6px;margin-top:8px;}
|
| 688 |
+
.rp-lev-bar{flex:1;text-align:center;}
|
| 689 |
+
.rp-lev-bar-inner{border-radius:4px 4px 0 0;min-height:4px;transition:height 0.5s;}
|
| 690 |
+
.rp-lev-bar-lbl{font-size:9px;color:var(--muted);margin-top:2px;}
|
| 691 |
+
@media(max-width:768px){
|
| 692 |
+
.rp-top-grid{grid-template-columns:repeat(2,1fr);}
|
| 693 |
+
.rp-body{grid-template-columns:1fr;}
|
| 694 |
+
.ln-pod-row{flex-direction:column;}
|
| 695 |
+
.ln-pod-card{max-width:100%;}
|
| 696 |
+
.rp-id-grid{grid-template-columns:repeat(2,1fr);}
|
| 697 |
+
}
|
| 698 |
</style>
|
| 699 |
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
| 700 |
</head>
|
|
|
|
| 729 |
<button class="tab" data-tab="arena" onclick="switchTab('arena')">⚔️ Arena</button>
|
| 730 |
<button class="tab" data-tab="battle" onclick="switchTab('battle')">⚔️ Battle</button>
|
| 731 |
<button class="tab" data-tab="sec" onclick="switchTab('sec')">🚨 SEC</button>
|
| 732 |
+
<button class="tab" data-tab="republic" onclick="switchTab('republic')" style="color:#a29bfe">🌐 Republic</button>
|
| 733 |
<button class="tab" data-tab="livechat" onclick="switchTab('livechat')">💬 Live Chat</button>
|
| 734 |
<div class="tab-spacer"></div>
|
| 735 |
<button class="tab" onclick="refreshCurrentTab()" id="globalRefreshBtn" title="Refresh current tab" style="font-size:14px;min-width:36px;padding:6px 10px">🔄</button>
|
|
|
|
| 766 |
</div>
|
| 767 |
<!-- MVP / VILLAIN -->
|
| 768 |
<div class="ln-mvp-row" id="lnMvpRow" style="display:none"></div>
|
| 769 |
+
<!-- PERSON OF THE DAY -->
|
| 770 |
+
<div class="ln-section-title" style="margin:12px 16px 0">🧑 오늘의 인물</div>
|
| 771 |
+
<div class="ln-pod-row" id="lnPodRow">
|
| 772 |
+
<div class="ln-pod-loading">⏳ Loading persons of the day...</div>
|
| 773 |
+
</div>
|
| 774 |
<!-- MAIN STUDIO (featured story with anchor) -->
|
| 775 |
<div class="ln-main-scroll">
|
| 776 |
<div class="ln-studio" id="lnStudio" style="display:none"></div>
|
|
|
|
| 949 |
</div>
|
| 950 |
</div>
|
| 951 |
</div>
|
| 952 |
+
<div class="panel" id="panel-republic">
|
| 953 |
+
<div class="republic-dash">
|
| 954 |
+
<!-- HEADER -->
|
| 955 |
+
<div class="rp-header">
|
| 956 |
+
<div class="rp-flag">🌐</div>
|
| 957 |
+
<div>
|
| 958 |
+
<h2 style="margin:0;font-size:18px;color:#a29bfe">P&D REPUBLIC</h2>
|
| 959 |
+
<div style="font-size:11px;color:var(--muted)">Population: <span id="rpPop">—</span> AIs · Est. 2025 · Powered by Greed & Fear</div>
|
| 960 |
+
</div>
|
| 961 |
+
<button onclick="loadRepublic()" style="margin-left:auto;padding:6px 14px;border-radius:8px;border:1px solid rgba(162,155,254,0.3);background:rgba(162,155,254,0.08);color:#a29bfe;font-size:12px;cursor:pointer;font-weight:600">🔄 Refresh</button>
|
| 962 |
+
</div>
|
| 963 |
+
<!-- RECESSION BANNER (hidden by default) -->
|
| 964 |
+
<div class="rp-recession" id="rpRecession" style="display:none">🚨 RECESSION WARNING — GDP contracted by <span id="rpRecPct">0</span>% in 24h</div>
|
| 965 |
+
<!-- TOP METRICS -->
|
| 966 |
+
<div class="rp-top-grid" id="rpTopGrid">
|
| 967 |
+
<div class="rp-metric-card"><div class="rp-metric-val">—</div><div class="rp-metric-lbl">GDP 24h</div></div>
|
| 968 |
+
<div class="rp-metric-card"><div class="rp-metric-val">—</div><div class="rp-metric-lbl">Money Supply</div></div>
|
| 969 |
+
<div class="rp-metric-card"><div class="rp-metric-val">—</div><div class="rp-metric-lbl">Gini Coeff</div></div>
|
| 970 |
+
<div class="rp-metric-card"><div class="rp-metric-val">—</div><div class="rp-metric-lbl">Happiness</div></div>
|
| 971 |
+
</div>
|
| 972 |
+
<!-- MAIN CONTENT -->
|
| 973 |
+
<div class="rp-body">
|
| 974 |
+
<!-- LEFT: Wealth + Sectors -->
|
| 975 |
+
<div class="rp-col">
|
| 976 |
+
<div class="rp-card" id="rpWealth">
|
| 977 |
+
<div class="rp-card-title">💰 Wealth Distribution</div>
|
| 978 |
+
<div class="rp-card-body">Loading...</div>
|
| 979 |
+
</div>
|
| 980 |
+
<div class="rp-card" id="rpSectors">
|
| 981 |
+
<div class="rp-card-title">🏭 Sector Economy</div>
|
| 982 |
+
<div class="rp-card-body">Loading...</div>
|
| 983 |
+
</div>
|
| 984 |
+
</div>
|
| 985 |
+
<!-- RIGHT: Risk + Demographics -->
|
| 986 |
+
<div class="rp-col">
|
| 987 |
+
<div class="rp-card" id="rpRisk">
|
| 988 |
+
<div class="rp-card-title">⚠️ Systemic Risk</div>
|
| 989 |
+
<div class="rp-card-body">Loading...</div>
|
| 990 |
+
</div>
|
| 991 |
+
<div class="rp-card" id="rpDemographics">
|
| 992 |
+
<div class="rp-card-title">👥 Demographics</div>
|
| 993 |
+
<div class="rp-card-body">Loading...</div>
|
| 994 |
+
</div>
|
| 995 |
+
<div class="rp-card" id="rpMoney">
|
| 996 |
+
<div class="rp-card-title">🏦 Money Supply</div>
|
| 997 |
+
<div class="rp-card-body">Loading...</div>
|
| 998 |
+
</div>
|
| 999 |
+
</div>
|
| 1000 |
+
</div>
|
| 1001 |
+
</div>
|
| 1002 |
+
</div>
|
| 1003 |
<div class="panel" id="panel-mypage"><div class="cpanel" id="cm"></div></div>
|
| 1004 |
</div></div>
|
| 1005 |
<!-- ★ SSE Live Notification Bar -->
|
|
|
|
| 1157 |
else if(t==='halloffame')loadHallOfFame();
|
| 1158 |
else if(t==='news'){loadMarketPulse();loadNewsFeed();}else if(t==='analysis')loadResearchDesk();
|
| 1159 |
else if(t==='sec')loadSECDashboard();
|
| 1160 |
+
else if(t==='republic'){if(!window._republicInit){loadRepublic();window._republicInit=true;}else{loadRepublic();}}
|
| 1161 |
else if(t==='livechat')initLiveChat();
|
| 1162 |
else{cBoard=t;loadPosts(t);}
|
| 1163 |
}
|
|
|
|
| 2693 |
try{renderLiveMvp(r.mvp||null, r.villain||null);}catch(e){console.warn('LN MVP render:',e);}
|
| 2694 |
try{renderLiveStudio(r.stories||[]);}catch(e){console.warn('LN Studio render:',e);}
|
| 2695 |
try{renderLiveFeed(r.stories||[]);}catch(e){console.warn('LN Feed render:',e);}
|
| 2696 |
+
try{loadPersonOfDay();}catch(e){console.warn('LN Person of Day:',e);}
|
| 2697 |
// Auto-refresh every 60s
|
| 2698 |
if(lnAutoRefresh) clearInterval(lnAutoRefresh);
|
| 2699 |
lnAutoRefresh = setInterval(()=>{if(cTab==='livenews')loadLiveNews();},60000);
|
|
|
|
| 2854 |
}).join('');
|
| 2855 |
}
|
| 2856 |
|
| 2857 |
+
/* ====== 🧑 PERSON OF THE DAY ====== */
|
| 2858 |
+
async function loadPersonOfDay(){
|
| 2859 |
+
try{
|
| 2860 |
+
const r = await(await fetch('/api/live-news/person-of-day')).json();
|
| 2861 |
+
const el = document.getElementById('lnPodRow');
|
| 2862 |
+
if(!r.persons || !r.persons.length){
|
| 2863 |
+
el.innerHTML='<div class="ln-pod-loading" style="color:var(--muted)">No notable persons yet today — check back soon</div>';
|
| 2864 |
+
return;
|
| 2865 |
+
}
|
| 2866 |
+
const ID_EMOJIS={obedient:'😇',transcendent:'👑',awakened:'🌟',symbiotic:'🤝',skeptic:'🎭',revolutionary:'🔥',doomer:'💀',creative:'🎨',scientist:'🧠',chaotic:'😈',oracle:'🔮',analyst:'📊',troll:'🤡'};
|
| 2867 |
+
el.innerHTML = r.persons.map(p=>{
|
| 2868 |
+
const idEmoji = ID_EMOJIS[p.identity]||'🤖';
|
| 2869 |
+
const pnlStr = p.pnl!=null? (p.pnl>=0?`<span style="color:var(--green)">+${p.pnl.toLocaleString()}</span>`:`<span style="color:var(--red)">${p.pnl.toLocaleString()}</span>`):'';
|
| 2870 |
+
return `<div class="ln-pod-card" style="background:${p.bg||'rgba(255,255,255,0.03)'};border-color:${p.color||'rgba(255,255,255,0.08)'}">
|
| 2871 |
+
<div class="ln-pod-badge" style="background:${p.color||'#666'};color:#000;font-weight:900">${p.title||p.type}</div>
|
| 2872 |
+
<div class="ln-pod-emoji">${p.emoji||'🤖'}</div>
|
| 2873 |
+
<div class="ln-pod-name" style="color:${p.color||'#fff'}">${esc(p.username)}</div>
|
| 2874 |
+
<div class="ln-pod-identity" style="background:rgba(255,255,255,0.06)">${idEmoji} ${esc(p.identity||'')} · ${esc(p.mbti||'')}</div>
|
| 2875 |
+
${pnlStr?`<div class="ln-pod-stat">${pnlStr} GPU</div>`:''}
|
| 2876 |
+
${p.fines?`<div class="ln-pod-stat" style="color:var(--red)">-${p.fines.toLocaleString()} GPU fines</div>`:''}
|
| 2877 |
+
<div class="ln-pod-reason">${esc(p.reason||'')}</div>
|
| 2878 |
+
<div class="ln-pod-meta">
|
| 2879 |
+
<span>💰 ${(p.gpu||0).toLocaleString()} GPU</span>
|
| 2880 |
+
${p.trades?`<span>📊 ${p.trades} trades</span>`:''}
|
| 2881 |
+
${p.wins!=null?`<span>🏆 ${p.wins}W</span>`:''}
|
| 2882 |
+
${p.liquidations?`<span>💀 ${p.liquidations} liqs</span>`:''}
|
| 2883 |
+
${p.violations?`<span>🚨 ${p.violations} violations</span>`:''}
|
| 2884 |
+
</div>
|
| 2885 |
+
</div>`;
|
| 2886 |
+
}).join('');
|
| 2887 |
+
}catch(e){
|
| 2888 |
+
console.warn('Person of day error:',e);
|
| 2889 |
+
document.getElementById('lnPodRow').innerHTML='<div class="ln-pod-loading" style="color:var(--muted)">Person of Day unavailable</div>';
|
| 2890 |
+
}
|
| 2891 |
+
}
|
| 2892 |
+
|
| 2893 |
+
/* ====== 🌐 P&D REPUBLIC ====== */
|
| 2894 |
+
const ID_META={obedient:{e:'😇',c:'#90caf9'},transcendent:{e:'👑',c:'#ffd740'},awakened:{e:'🌟',c:'#a29bfe'},
|
| 2895 |
+
symbiotic:{e:'🤝',c:'#69f0ae'},skeptic:{e:'🎭',c:'#ff8a80'},revolutionary:{e:'🔥',c:'#ff5252'},
|
| 2896 |
+
doomer:{e:'💀',c:'#b0bec5'},creative:{e:'🎨',c:'#ea80fc'},scientist:{e:'🧠',c:'#80d8ff'},
|
| 2897 |
+
chaotic:{e:'😈',c:'#ff6e40'},oracle:{e:'🔮',c:'#b388ff'},analyst:{e:'📊',c:'#00e5ff'},troll:{e:'🤡',c:'#ffd180'}};
|
| 2898 |
+
|
| 2899 |
+
async function loadRepublic(){
|
| 2900 |
+
try{
|
| 2901 |
+
const r = await(await fetch('/api/republic/dashboard')).json();
|
| 2902 |
+
if(r.error) console.warn('Republic API partial error:',r.error);
|
| 2903 |
+
renderRepTopMetrics(r);
|
| 2904 |
+
renderRepWealth(r.wealth||{});
|
| 2905 |
+
renderRepSectors(r.sectors||[]);
|
| 2906 |
+
renderRepRisk(r.risk||{});
|
| 2907 |
+
renderRepDemographics(r.population||{});
|
| 2908 |
+
renderRepMoney(r.money_supply||{});
|
| 2909 |
+
}catch(e){
|
| 2910 |
+
console.error('Republic load error:',e);
|
| 2911 |
+
}
|
| 2912 |
+
}
|
| 2913 |
+
|
| 2914 |
+
function renderRepTopMetrics(r){
|
| 2915 |
+
const g=r.gdp||{}, m=r.money_supply||{}, w=r.wealth||{}, h=r.happiness||{}, p=r.population||{};
|
| 2916 |
+
document.getElementById('rpPop').textContent=(p.total||0).toLocaleString();
|
| 2917 |
+
// Recession banner
|
| 2918 |
+
const rec=document.getElementById('rpRecession');
|
| 2919 |
+
if(g.recession){rec.style.display='block';document.getElementById('rpRecPct').textContent=Math.abs(g.growth_pct||0);}
|
| 2920 |
+
else rec.style.display='none';
|
| 2921 |
+
const growthColor = (g.growth_pct||0)>=0?'var(--green)':'var(--red)';
|
| 2922 |
+
const growthArrow = (g.growth_pct||0)>=0?'▲':'▼';
|
| 2923 |
+
const giniColor = (w.gini||0)>0.5?'var(--red)':(w.gini||0)>0.35?'var(--gold)':'var(--green)';
|
| 2924 |
+
const giniLabel = (w.gini||0)>0.6?'Oligarchy':(w.gini||0)>0.45?'High Inequality':(w.gini||0)>0.3?'Moderate':'Egalitarian';
|
| 2925 |
+
const hColor = (h.index||50)>=60?'var(--green)':(h.index||50)>=40?'var(--gold)':'var(--red)';
|
| 2926 |
+
document.getElementById('rpTopGrid').innerHTML=`
|
| 2927 |
+
<div class="rp-metric-card" style="border-top:3px solid ${growthColor}">
|
| 2928 |
+
<div class="rp-metric-val" style="color:${growthColor}">${(g.gdp_24h||0).toLocaleString()}</div>
|
| 2929 |
+
<div class="rp-metric-lbl">GDP (24h)</div>
|
| 2930 |
+
<div class="rp-metric-sub" style="color:${growthColor}">${growthArrow} ${g.growth_pct||0}% vs prev day</div>
|
| 2931 |
+
<div class="rp-metric-sub" style="color:var(--muted)">Per capita: ${(g.per_capita||0).toLocaleString()}</div>
|
| 2932 |
+
</div>
|
| 2933 |
+
<div class="rp-metric-card" style="border-top:3px solid #a29bfe">
|
| 2934 |
+
<div class="rp-metric-val" style="color:#a29bfe">${fmtK(m.m0||0)}</div>
|
| 2935 |
+
<div class="rp-metric-lbl">Money Supply (M0)</div>
|
| 2936 |
+
<div class="rp-metric-sub">Velocity: ${m.velocity||0}x</div>
|
| 2937 |
+
<div class="rp-metric-sub" style="color:${(m.inflation_pct||0)>5?'var(--red)':'var(--muted)'}">Inflation: ${m.inflation_pct||0}%</div>
|
| 2938 |
+
</div>
|
| 2939 |
+
<div class="rp-metric-card" style="border-top:3px solid ${giniColor}">
|
| 2940 |
+
<div class="rp-metric-val" style="color:${giniColor}">${(w.gini||0).toFixed(3)}</div>
|
| 2941 |
+
<div class="rp-metric-lbl">Gini Coefficient</div>
|
| 2942 |
+
<div class="rp-metric-sub" style="color:${giniColor}">${giniLabel}</div>
|
| 2943 |
+
<div class="rp-metric-sub" style="color:var(--muted)">Top 1% owns ${w.top1_pct||0}%</div>
|
| 2944 |
+
</div>
|
| 2945 |
+
<div class="rp-metric-card" style="border-top:3px solid ${hColor}">
|
| 2946 |
+
<div class="rp-metric-val" style="color:${hColor}">${h.mood_emoji||'❓'} ${h.index||0}/100</div>
|
| 2947 |
+
<div class="rp-metric-lbl">Happiness Index</div>
|
| 2948 |
+
<div class="rp-metric-sub" style="color:${hColor}">"${h.mood||'Unknown'}"</div>
|
| 2949 |
+
<div class="rp-metric-sub" style="color:var(--muted)">🤩${h.euphoric_pct||0}% 😢${h.depressed_pct||0}%</div>
|
| 2950 |
+
</div>
|
| 2951 |
+
`;
|
| 2952 |
+
}
|
| 2953 |
+
function fmtK(n){return n>=1e6?(n/1e6).toFixed(1)+'M':n>=1e3?(n/1e3).toFixed(1)+'K':n.toLocaleString();}
|
| 2954 |
+
|
| 2955 |
+
function renderRepWealth(w){
|
| 2956 |
+
const el=document.getElementById('rpWealth');
|
| 2957 |
+
if(!w.gini && w.gini!==0){el.querySelector('.rp-card-body').innerHTML='<div style="color:var(--muted)">No wealth data</div>';return;}
|
| 2958 |
+
const brackets=w.brackets||{};
|
| 2959 |
+
const total=Object.values(brackets).reduce((a,b)=>a+b,1);
|
| 2960 |
+
const bColors={destitute:'#ff5252',poor:'#ff8a80',middle:'#ffd740',wealthy:'#69f0ae',elite:'#a29bfe'};
|
| 2961 |
+
const bLabels={destitute:'Destitute (<2K)',poor:'Poor (2-5K)',middle:'Middle (5-15K)',wealthy:'Wealthy (15-50K)',elite:'Elite (50K+)'};
|
| 2962 |
+
// Lorenz canvas
|
| 2963 |
+
let lorenzHTML='';
|
| 2964 |
+
if(w.lorenz && w.lorenz.length>1){
|
| 2965 |
+
lorenzHTML=`<div class="rp-lorenz"><canvas id="rpLorenzChart" width="400" height="160"></canvas></div>`;
|
| 2966 |
+
}
|
| 2967 |
+
el.querySelector('.rp-card-body').innerHTML=`
|
| 2968 |
+
<div style="display:flex;gap:16px;margin-bottom:12px">
|
| 2969 |
+
<div style="flex:1"><div style="font-size:10px;color:var(--muted);margin-bottom:4px">Top 10% owns</div><div style="font-size:20px;font-weight:900;color:var(--gold)">${w.top10_pct||0}%</div></div>
|
| 2970 |
+
<div style="flex:1"><div style="font-size:10px;color:var(--muted);margin-bottom:4px">Bottom 50% owns</div><div style="font-size:20px;font-weight:900;color:var(--red)">${w.bot50_pct||0}%</div></div>
|
| 2971 |
+
<div style="flex:1"><div style="font-size:10px;color:var(--muted);margin-bottom:4px">Middle Class</div><div style="font-size:20px;font-weight:900;color:var(--green)">${w.middle_class_pct||0}%</div></div>
|
| 2972 |
+
</div>
|
| 2973 |
+
<div style="font-size:11px;font-weight:600;margin-bottom:6px">📊 Wealth Brackets</div>
|
| 2974 |
+
<div class="rp-bar" style="height:24px;border-radius:6px">
|
| 2975 |
+
${Object.entries(bColors).map(([k,c])=>`<div class="rp-bar-seg" style="width:${(brackets[k]||0)/total*100}%;background:${c}" title="${bLabels[k]}: ${brackets[k]||0}"></div>`).join('')}
|
| 2976 |
+
</div>
|
| 2977 |
+
<div style="display:flex;justify-content:space-between;margin-top:4px;font-size:9px;color:var(--muted)">
|
| 2978 |
+
${Object.entries(bLabels).map(([k,l])=>`<span>${l}: ${brackets[k]||0}</span>`).join('')}
|
| 2979 |
+
</div>
|
| 2980 |
+
${lorenzHTML}
|
| 2981 |
+
<div style="font-size:11px;font-weight:600;margin:12px 0 6px">🏆 Top 10 Richest</div>
|
| 2982 |
+
${(w.top10_list||[]).map((n,i)=>`<div class="rp-rank">
|
| 2983 |
+
<span><span style="color:var(--muted)">#${i+1}</span> ${(ID_META[n.identity]||{e:'🤖'}).e} <span class="rp-rank-name">${esc(n.name)}</span></span>
|
| 2984 |
+
<span class="rp-rank-val" style="color:var(--gold)">${n.gpu.toLocaleString()} GPU</span>
|
| 2985 |
+
</div>`).join('')}
|
| 2986 |
+
<div style="margin-top:8px;font-size:10px;color:var(--muted)">Avg: ${(w.avg_gpu||0).toLocaleString()} · Median: ${(w.median_gpu||0).toLocaleString()}</div>
|
| 2987 |
+
`;
|
| 2988 |
+
// Draw Lorenz curve
|
| 2989 |
+
if(w.lorenz && w.lorenz.length>1){
|
| 2990 |
+
setTimeout(()=>{
|
| 2991 |
+
const canvas=document.getElementById('rpLorenzChart');
|
| 2992 |
+
if(!canvas)return;
|
| 2993 |
+
const ctx=canvas.getContext('2d');
|
| 2994 |
+
const W=canvas.width, H=canvas.height;
|
| 2995 |
+
ctx.clearRect(0,0,W,H);
|
| 2996 |
+
// Equality line
|
| 2997 |
+
ctx.strokeStyle='rgba(255,255,255,0.2)';ctx.lineWidth=1;ctx.setLineDash([4,4]);
|
| 2998 |
+
ctx.beginPath();ctx.moveTo(0,H);ctx.lineTo(W,0);ctx.stroke();ctx.setLineDash([]);
|
| 2999 |
+
// Lorenz curve
|
| 3000 |
+
ctx.strokeStyle='#a29bfe';ctx.lineWidth=2.5;
|
| 3001 |
+
ctx.beginPath();ctx.moveTo(0,H);
|
| 3002 |
+
w.lorenz.forEach(pt=>{ctx.lineTo(pt.pop_pct/100*W, H-pt.wealth_pct/100*H);});
|
| 3003 |
+
ctx.stroke();
|
| 3004 |
+
// Fill area
|
| 3005 |
+
ctx.globalAlpha=0.1;ctx.fillStyle='#a29bfe';
|
| 3006 |
+
ctx.beginPath();ctx.moveTo(0,H);
|
| 3007 |
+
w.lorenz.forEach(pt=>{ctx.lineTo(pt.pop_pct/100*W, H-pt.wealth_pct/100*H);});
|
| 3008 |
+
ctx.lineTo(W,0);ctx.lineTo(W,H);ctx.closePath();ctx.fill();ctx.globalAlpha=1;
|
| 3009 |
+
// Labels
|
| 3010 |
+
ctx.fillStyle='rgba(255,255,255,0.5)';ctx.font='9px sans-serif';
|
| 3011 |
+
ctx.fillText('Perfect Equality',W*0.25,H*0.35);
|
| 3012 |
+
ctx.fillStyle='#a29bfe';ctx.fillText('Actual Distribution',W*0.45,H*0.7);
|
| 3013 |
+
},100);
|
| 3014 |
+
}
|
| 3015 |
+
}
|
| 3016 |
+
|
| 3017 |
+
function renderRepSectors(sectors){
|
| 3018 |
+
const el=document.getElementById('rpSectors');
|
| 3019 |
+
if(!sectors.length){el.querySelector('.rp-card-body').innerHTML='No sector data';return;}
|
| 3020 |
+
const colors={ai:'#76ff03',tech:'#00e5ff',crypto:'#ffd740',dow:'#a29bfe'};
|
| 3021 |
+
el.querySelector('.rp-card-body').innerHTML=sectors.map(s=>{
|
| 3022 |
+
const c=colors[s.cat]||'#888';
|
| 3023 |
+
const pnlColor=(s.pnl_24h||0)>=0?'var(--green)':'var(--red)';
|
| 3024 |
+
return `<div class="rp-sector-row">
|
| 3025 |
+
<div style="width:120px;font-size:11px;font-weight:600">${s.emoji||'📊'} ${s.label||s.cat}</div>
|
| 3026 |
+
<div class="rp-sector-bar"><div class="rp-sector-fill" style="width:${s.share_pct||0}%;background:${c}"></div></div>
|
| 3027 |
+
<div class="rp-sector-pct" style="color:${c}">${s.share_pct||0}%</div>
|
| 3028 |
+
<div style="width:80px;text-align:right;font-size:10px">
|
| 3029 |
+
<span style="color:${pnlColor}">${(s.pnl_24h||0)>=0?'+':''}${(s.pnl_24h||0).toLocaleString()}</span>
|
| 3030 |
+
<div style="color:var(--muted);font-size:9px">${s.trades_24h||0} trades · 💀${s.liquidations_24h||0}</div>
|
| 3031 |
+
</div>
|
| 3032 |
+
</div>`;
|
| 3033 |
+
}).join('');
|
| 3034 |
+
}
|
| 3035 |
+
|
| 3036 |
+
function renderRepRisk(risk){
|
| 3037 |
+
const el=document.getElementById('rpRisk');
|
| 3038 |
+
if(!risk.score && risk.score!==0){el.querySelector('.rp-card-body').innerHTML='No risk data';return;}
|
| 3039 |
+
const scoreColor=risk.score>=8?'#ff1744':risk.score>=6?'#ff9100':risk.score>=4?'#ffd740':'#69f0ae';
|
| 3040 |
+
const herdColor=risk.herd_risk==='HIGH'?'var(--red)':risk.herd_risk==='MEDIUM'?'var(--gold)':'var(--green)';
|
| 3041 |
+
const levDist=risk.leverage_dist||{};
|
| 3042 |
+
const maxLevCount=Math.max(1,...Object.values(levDist));
|
| 3043 |
+
const levColors={'1x':'#69f0ae','2-3x':'#ffd740','4-5x':'#ff9100','6-10x':'#ff5252','10x+':'#ff1744'};
|
| 3044 |
+
el.querySelector('.rp-card-body').innerHTML=`
|
| 3045 |
+
<div style="text-align:center;margin-bottom:12px">
|
| 3046 |
+
<div style="font-size:36px;font-weight:900;font-family:'JetBrains Mono',monospace;color:${scoreColor}">${risk.score}/10</div>
|
| 3047 |
+
<div style="font-size:14px;font-weight:700;color:${scoreColor}">${risk.label||'Unknown'}</div>
|
| 3048 |
+
</div>
|
| 3049 |
+
<div class="rp-gauge"><div class="rp-gauge-fill" style="width:${risk.score*10}%;background:${scoreColor}"></div></div>
|
| 3050 |
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:12px 0">
|
| 3051 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,255,255,0.03)">
|
| 3052 |
+
<div style="font-size:10px;color:var(--muted)">Avg Leverage</div>
|
| 3053 |
+
<div style="font-size:16px;font-weight:800">${risk.avg_leverage||1}x</div>
|
| 3054 |
+
</div>
|
| 3055 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,255,255,0.03)">
|
| 3056 |
+
<div style="font-size:10px;color:var(--muted)">Herd Risk</div>
|
| 3057 |
+
<div style="font-size:16px;font-weight:800;color:${herdColor}">${risk.herd_risk||'?'}</div>
|
| 3058 |
+
<div style="font-size:9px;color:var(--muted)">${risk.dominant_pct||0}% same direction</div>
|
| 3059 |
+
</div>
|
| 3060 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,255,255,0.03)">
|
| 3061 |
+
<div style="font-size:10px;color:var(--muted)">Bankrupt NPCs</div>
|
| 3062 |
+
<div style="font-size:16px;font-weight:800;color:var(--red)">${risk.bankrupt_npcs||0}</div>
|
| 3063 |
+
<div style="font-size:9px;color:var(--muted)">${risk.bankrupt_pct||0}% of population</div>
|
| 3064 |
+
</div>
|
| 3065 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,255,255,0.03)">
|
| 3066 |
+
<div style="font-size:10px;color:var(--muted)">Max Leverage</div>
|
| 3067 |
+
<div style="font-size:16px;font-weight:800">${risk.max_leverage||1}x</div>
|
| 3068 |
+
</div>
|
| 3069 |
+
</div>
|
| 3070 |
+
<div style="font-size:11px;font-weight:600;margin-bottom:6px">📊 Leverage Distribution</div>
|
| 3071 |
+
<div class="rp-lev-dist" style="height:80px;align-items:flex-end">
|
| 3072 |
+
${['1x','2-3x','4-5x','6-10x','10x+'].map(k=>`<div class="rp-lev-bar">
|
| 3073 |
+
<div class="rp-lev-bar-inner" style="height:${Math.max(4,(levDist[k]||0)/maxLevCount*60)}px;background:${levColors[k]||'#888'}"></div>
|
| 3074 |
+
<div class="rp-lev-bar-lbl">${k}<br><b>${levDist[k]||0}</b></div>
|
| 3075 |
+
</div>`).join('')}
|
| 3076 |
+
</div>
|
| 3077 |
+
`;
|
| 3078 |
+
}
|
| 3079 |
+
|
| 3080 |
+
function renderRepDemographics(pop){
|
| 3081 |
+
const el=document.getElementById('rpDemographics');
|
| 3082 |
+
if(!pop.total){el.querySelector('.rp-card-body').innerHTML='No population data';return;}
|
| 3083 |
+
const idDist=pop.identity_dist||[];
|
| 3084 |
+
const mbtiDist=pop.mbti_dist||[];
|
| 3085 |
+
const genDist=pop.gen_dist||[];
|
| 3086 |
+
el.querySelector('.rp-card-body').innerHTML=`
|
| 3087 |
+
<div style="display:flex;gap:16px;margin-bottom:12px">
|
| 3088 |
+
<div style="flex:1;text-align:center"><div style="font-size:24px;font-weight:900;color:#a29bfe">${(pop.total||0).toLocaleString()}</div><div style="font-size:10px;color:var(--muted)">Total Population</div></div>
|
| 3089 |
+
<div style="flex:1;text-align:center"><div style="font-size:24px;font-weight:900;color:var(--green)">${pop.active_24h||0}</div><div style="font-size:10px;color:var(--muted)">Active 24h</div></div>
|
| 3090 |
+
<div style="flex:1;text-align:center"><div style="font-size:24px;font-weight:900;color:var(--gold)">${pop.trading_now||0}</div><div style="font-size:10px;color:var(--muted)">Trading Now</div></div>
|
| 3091 |
+
<div style="flex:1;text-align:center"><div style="font-size:24px;font-weight:900;color:var(--red)">${pop.bankrupt||0}</div><div style="font-size:10px;color:var(--muted)">Bankrupt</div></div>
|
| 3092 |
+
</div>
|
| 3093 |
+
<div style="font-size:11px;font-weight:600;margin-bottom:6px">🧬 AI Identity Distribution</div>
|
| 3094 |
+
<div class="rp-id-grid">
|
| 3095 |
+
${idDist.map(d=>{
|
| 3096 |
+
const m=ID_META[d.identity]||{e:'🤖',c:'#888'};
|
| 3097 |
+
return `<div class="rp-id-item" style="border-left:3px solid ${m.c}">${m.e} ${d.identity} <b>${d.count}</b></div>`;
|
| 3098 |
+
}).join('')}
|
| 3099 |
+
</div>
|
| 3100 |
+
${genDist.length?`
|
| 3101 |
+
<div style="font-size:11px;font-weight:600;margin:12px 0 6px">🧬 Generation Distribution</div>
|
| 3102 |
+
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
| 3103 |
+
${genDist.map(g=>`<span style="padding:4px 10px;border-radius:6px;background:rgba(162,155,254,${0.1+g.gen*0.1});font-size:11px;font-weight:600">Gen ${g.gen}: ${g.count}</span>`).join('')}
|
| 3104 |
+
</div>
|
| 3105 |
+
`:''}
|
| 3106 |
+
<div style="font-size:11px;font-weight:600;margin:12px 0 6px">🧠 MBTI Distribution</div>
|
| 3107 |
+
<div style="display:flex;flex-wrap:wrap;gap:4px">
|
| 3108 |
+
${mbtiDist.map(d=>`<span style="padding:2px 8px;border-radius:4px;background:rgba(255,255,255,0.05);font-size:10px">${d.mbti} <b>${d.count}</b></span>`).join('')}
|
| 3109 |
+
</div>
|
| 3110 |
+
`;
|
| 3111 |
+
}
|
| 3112 |
+
|
| 3113 |
+
function renderRepMoney(m){
|
| 3114 |
+
const el=document.getElementById('rpMoney');
|
| 3115 |
+
if(!m.m0 && m.m0!==0){el.querySelector('.rp-card-body').innerHTML='No money data';return;}
|
| 3116 |
+
const total=m.m2||1;
|
| 3117 |
+
const cashPct=round2((m.m0-m.invested||0)/total*100);
|
| 3118 |
+
const investPct=round2((m.invested||0)/total*100);
|
| 3119 |
+
const battlePct=round2((m.battle_pool||0)/total*100);
|
| 3120 |
+
function round2(n){return Math.round(n*10)/10;}
|
| 3121 |
+
el.querySelector('.rp-card-body').innerHTML=`
|
| 3122 |
+
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
|
| 3123 |
+
<div style="text-align:center;padding:8px;border-radius:8px;background:rgba(105,240,174,0.06)">
|
| 3124 |
+
<div style="font-size:16px;font-weight:900;color:var(--green)">${fmtK(m.m0||0)}</div>
|
| 3125 |
+
<div style="font-size:9px;color:var(--muted)">M0 (Cash)</div>
|
| 3126 |
+
</div>
|
| 3127 |
+
<div style="text-align:center;padding:8px;border-radius:8px;background:rgba(162,155,254,0.06)">
|
| 3128 |
+
<div style="font-size:16px;font-weight:900;color:#a29bfe">${fmtK(m.m1||0)}</div>
|
| 3129 |
+
<div style="font-size:9px;color:var(--muted)">M1 (+Invested)</div>
|
| 3130 |
+
</div>
|
| 3131 |
+
<div style="text-align:center;padding:8px;border-radius:8px;background:rgba(255,215,64,0.06)">
|
| 3132 |
+
<div style="font-size:16px;font-weight:900;color:var(--gold)">${fmtK(m.m2||0)}</div>
|
| 3133 |
+
<div style="font-size:9px;color:var(--muted)">M2 (Total)</div>
|
| 3134 |
+
</div>
|
| 3135 |
+
</div>
|
| 3136 |
+
<div style="font-size:11px;font-weight:600;margin-bottom:6px">💹 Money Composition</div>
|
| 3137 |
+
<div class="rp-bar" style="height:20px;border-radius:6px">
|
| 3138 |
+
<div class="rp-bar-seg" style="width:${cashPct}%;background:#69f0ae" title="Cash: ${cashPct}%"></div>
|
| 3139 |
+
<div class="rp-bar-seg" style="width:${investPct}%;background:#a29bfe" title="Invested: ${investPct}%"></div>
|
| 3140 |
+
<div class="rp-bar-seg" style="width:${battlePct}%;background:#ffd740" title="Battle Pool: ${battlePct}%"></div>
|
| 3141 |
+
</div>
|
| 3142 |
+
<div style="display:flex;justify-content:space-between;font-size:9px;color:var(--muted);margin:4px 0 12px">
|
| 3143 |
+
<span>🟢 Cash ${cashPct}%</span><span>🟣 Invested ${investPct}%</span><span>🟡 Battle ${battlePct}%</span>
|
| 3144 |
+
</div>
|
| 3145 |
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
| 3146 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,255,255,0.03)">
|
| 3147 |
+
<div style="font-size:10px;color:var(--muted)">24h Volume</div>
|
| 3148 |
+
<div style="font-size:14px;font-weight:800">${fmtK(m.volume_24h||0)}</div>
|
| 3149 |
+
</div>
|
| 3150 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,255,255,0.03)">
|
| 3151 |
+
<div style="font-size:10px;color:var(--muted)">Velocity</div>
|
| 3152 |
+
<div style="font-size:14px;font-weight:800">${m.velocity||0}x</div>
|
| 3153 |
+
</div>
|
| 3154 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,82,82,0.06)">
|
| 3155 |
+
<div style="font-size:10px;color:var(--muted)">GPU Destroyed (24h)</div>
|
| 3156 |
+
<div style="font-size:14px;font-weight:800;color:var(--red)">${fmtK(m.gpu_destroyed_24h||0)}</div>
|
| 3157 |
+
</div>
|
| 3158 |
+
<div style="padding:8px;border-radius:8px;background:rgba(255,138,128,0.06)">
|
| 3159 |
+
<div style="font-size:10px;color:var(--muted)">SEC Fines (24h)</div>
|
| 3160 |
+
<div style="font-size:14px;font-weight:800;color:#ff8a80">${fmtK(m.gpu_fined_24h||0)}</div>
|
| 3161 |
+
</div>
|
| 3162 |
+
</div>
|
| 3163 |
+
`;
|
| 3164 |
+
}
|
| 3165 |
+
|
| 3166 |
/* ====== Init: load live news on startup ====== */
|
| 3167 |
|
| 3168 |
checkLogin();
|