1147 lines
51 KiB
HTML
1147 lines
51 KiB
HTML
<!DOCTYPE html>
|
||
<!-- sport.rinet.one ─ DABI CIVIC INTELLIGENCE OS -->
|
||
<!-- v4.0 BOMBASTIC EDITION | dradulic@outlook.com | 2026-05-04 -->
|
||
<!-- Ri.NET AI OS — Damir Radulić | Croatia's Palantir -->
|
||
<html lang="hr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>DABI · PGŽ Sport Intelligence</title>
|
||
<meta name="description" content="Svaki euro. Svaki klub. Svaka veza. DABI zna.">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=JetBrains+Mono:wght@300;400;700&family=Syne:wght@400;700;800&display=swap" rel="stylesheet">
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js"></script>
|
||
<script src="https://unpkg.com/3d-force-graph@1.73.4/dist/3d-force-graph.min.js"></script>
|
||
<style>
|
||
/* ─────────────────────────────────────────────────────────
|
||
TOKENS & RESET
|
||
───────────────────────────────────────────────────────── */
|
||
:root {
|
||
--void: #01020a;
|
||
--deep: #05070f;
|
||
--dark: #080c18;
|
||
--panel: #0d1220;
|
||
--card: #111827;
|
||
--rim: #1c2540;
|
||
--rim2: #253060;
|
||
|
||
--ice: #00d4ff;
|
||
--ice2: #00f0ff;
|
||
--plasma: #4f8fff;
|
||
--gold: #f0b429;
|
||
--gold2: #ffd700;
|
||
--lime: #39ff14;
|
||
--red: #ff1a3c;
|
||
--orange: #ff6b00;
|
||
--violet: #8b5cf6;
|
||
|
||
--t0: #ffffff;
|
||
--t1: #e8eaf2;
|
||
--t2: #9aa5be;
|
||
--t3: #c4cadc;
|
||
--t4: #5a6480;
|
||
|
||
--gice: 0 0 40px rgba(0,212,255,.3);
|
||
--ggold: 0 0 40px rgba(240,180,41,.3);
|
||
--gred: 0 0 40px rgba(255,26,60,.4);
|
||
|
||
--bb: 'Bebas Neue', cursive;
|
||
--sy: 'Syne', sans-serif;
|
||
--jb: 'JetBrains Mono', monospace;
|
||
}
|
||
*, *::before, *::after { box-sizing:border-box; margin:0; padding:0 }
|
||
html { scroll-behavior:smooth }
|
||
body {
|
||
background:var(--void);
|
||
color:var(--t1);
|
||
font-family:var(--sy);
|
||
font-size:14px;
|
||
line-height:1.6;
|
||
overflow-x:hidden;
|
||
}
|
||
a { color:var(--ice); text-decoration:none }
|
||
::selection { background:rgba(0,212,255,.25); color:var(--t0) }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
CANVAS BACKGROUND
|
||
───────────────────────────────────────────────────────── */
|
||
#canvas-bg {
|
||
position:fixed; inset:0; z-index:0; pointer-events:none;
|
||
opacity:.55;
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
NAV
|
||
───────────────────────────────────────────────────────── */
|
||
#nav {
|
||
position:fixed; top:0; left:0; right:0; z-index:100;
|
||
height:56px;
|
||
background:rgba(1,2,10,.85);
|
||
border-bottom:1px solid rgba(0,212,255,.12);
|
||
backdrop-filter:blur(20px) saturate(180%);
|
||
display:flex; align-items:center; padding:0 32px; gap:32px;
|
||
transition:all .3s;
|
||
}
|
||
.nav-logo {
|
||
font-family:var(--bb);
|
||
font-size:22px;
|
||
letter-spacing:2px;
|
||
color:var(--ice2);
|
||
text-shadow:var(--gice);
|
||
flex-shrink:0;
|
||
}
|
||
.nav-logo span { color:var(--t2); font-size:12px; font-family:var(--jb); margin-left:8px; vertical-align:middle; letter-spacing:.5px }
|
||
.nav-links { display:flex; gap:4px; flex:1 }
|
||
.nav-a {
|
||
padding:6px 14px; border-radius:4px;
|
||
font-size:11px; font-weight:700; letter-spacing:.5px; text-transform:uppercase;
|
||
color:var(--t4); cursor:pointer;
|
||
transition:all .2s; border:1px solid transparent;
|
||
font-family:var(--jb);
|
||
}
|
||
.nav-a:hover, .nav-a.on {
|
||
color:var(--ice); border-color:rgba(0,212,255,.25);
|
||
background:rgba(0,212,255,.06);
|
||
}
|
||
.nav-a.danger { color:rgba(255,26,60,.7) }
|
||
.nav-a.danger:hover, .nav-a.danger.on { color:var(--red); border-color:rgba(255,26,60,.3); background:rgba(255,26,60,.06) }
|
||
.nav-right { display:flex; gap:8px; align-items:center; margin-left:auto }
|
||
.live-pill {
|
||
display:flex; align-items:center; gap:6px;
|
||
background:rgba(57,255,20,.1); border:1px solid rgba(57,255,20,.25);
|
||
padding:4px 12px; border-radius:20px;
|
||
font-family:var(--jb); font-size:9px; color:#39ff14; letter-spacing:.5px;
|
||
}
|
||
.live-dot { width:6px; height:6px; border-radius:50%; background:#39ff14; animation:pulse 1.5s infinite; box-shadow:0 0 8px #39ff14 }
|
||
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.5;transform:scale(.8)} }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
PAGES
|
||
───────────────────────────────────────────────────────── */
|
||
.page { display:none; min-height:calc(100vh - 56px); padding-top:56px; position:relative; z-index:1 }
|
||
.page.on { display:block }
|
||
@keyframes pIn { from{opacity:0;transform:translateY(16px)} to{opacity:1;transform:translateY(0)} }
|
||
.page.on { animation:pIn .4s ease }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
PAGE: HERO / DASHBOARD
|
||
───────────────────────────────────────────────────────── */
|
||
#hero {
|
||
min-height:100vh;
|
||
display:flex; flex-direction:column; justify-content:center;
|
||
padding:80px 60px 60px;
|
||
position:relative; overflow:hidden;
|
||
}
|
||
.hero-eyebrow {
|
||
font-family:var(--jb); font-size:10px; color:var(--ice);
|
||
letter-spacing:3px; text-transform:uppercase; margin-bottom:20px;
|
||
display:flex; align-items:center; gap:10px;
|
||
}
|
||
.hero-eyebrow::before { content:''; width:40px; height:1px; background:var(--ice) }
|
||
.hero-headline {
|
||
font-family:var(--bb);
|
||
font-size:clamp(54px,8vw,120px);
|
||
line-height:.92;
|
||
letter-spacing:2px;
|
||
color:var(--t0);
|
||
margin-bottom:12px;
|
||
}
|
||
.hero-headline .hl-ice { color:var(--ice); text-shadow:var(--gice) }
|
||
.hero-headline .hl-gold { color:var(--gold); text-shadow:var(--ggold) }
|
||
.hero-sub {
|
||
font-family:var(--jb); font-size:13px; color:var(--t2);
|
||
margin-bottom:48px; max-width:500px; line-height:1.8;
|
||
letter-spacing:.2px;
|
||
}
|
||
.hero-sub strong { color:var(--t1); font-weight:400 }
|
||
|
||
/* MEGA STATS */
|
||
.mega-stats {
|
||
display:grid; grid-template-columns:repeat(4,1fr); gap:1px;
|
||
background:var(--rim);
|
||
border:1px solid var(--rim); border-radius:8px; overflow:hidden;
|
||
max-width:900px; margin-bottom:60px;
|
||
}
|
||
.mstat {
|
||
background:var(--deep); padding:28px 24px;
|
||
position:relative; overflow:hidden; cursor:default;
|
||
transition:background .2s;
|
||
}
|
||
.mstat:hover { background:rgba(0,212,255,.04) }
|
||
.mstat::after {
|
||
content:''; position:absolute; top:0; left:0; right:0; height:2px;
|
||
opacity:0; transition:opacity .2s;
|
||
}
|
||
.mstat:hover::after { opacity:1 }
|
||
.mstat.c::after { background:linear-gradient(90deg,var(--ice),transparent) }
|
||
.mstat.g::after { background:linear-gradient(90deg,var(--gold),transparent) }
|
||
.mstat.r::after { background:linear-gradient(90deg,var(--red),transparent) }
|
||
.mstat.v::after { background:linear-gradient(90deg,var(--violet),transparent) }
|
||
.mstat-n {
|
||
font-family:var(--bb); font-size:52px; line-height:1; letter-spacing:1px;
|
||
margin-bottom:4px;
|
||
}
|
||
.mstat.c .mstat-n { color:var(--ice); text-shadow:0 0 30px rgba(0,212,255,.5) }
|
||
.mstat.g .mstat-n { color:var(--gold); text-shadow:0 0 30px rgba(240,180,41,.5) }
|
||
.mstat.r .mstat-n { color:var(--red); text-shadow:0 0 30px rgba(255,26,60,.5) }
|
||
.mstat.v .mstat-n { color:var(--violet); text-shadow:0 0 30px rgba(139,92,246,.5) }
|
||
.mstat-l { font-family:var(--jb); font-size:9px; color:var(--t4); letter-spacing:1px; text-transform:uppercase }
|
||
.mstat-sub { font-family:var(--jb); font-size:9px; color:var(--t2); margin-top:6px }
|
||
|
||
/* HERO CTA */
|
||
.hero-actions { display:flex; gap:12px; flex-wrap:wrap }
|
||
.cta {
|
||
padding:14px 28px; border-radius:4px;
|
||
font-family:var(--jb); font-size:11px; font-weight:700; letter-spacing:1px; text-transform:uppercase;
|
||
cursor:pointer; transition:all .2s; border:1px solid;
|
||
}
|
||
.cta-pri {
|
||
background:rgba(0,212,255,.15); border-color:rgba(0,212,255,.5); color:var(--ice);
|
||
box-shadow:0 0 20px rgba(0,212,255,.15);
|
||
}
|
||
.cta-pri:hover { background:rgba(0,212,255,.25); box-shadow:var(--gice); transform:translateY(-1px) }
|
||
.cta-ghost { background:transparent; border-color:var(--rim2); color:var(--t2) }
|
||
.cta-ghost:hover { border-color:var(--t2); color:var(--t1) }
|
||
.cta-danger {
|
||
background:rgba(255,26,60,.1); border-color:rgba(255,26,60,.4); color:var(--red);
|
||
}
|
||
.cta-danger:hover { background:rgba(255,26,60,.2); box-shadow:var(--gred); transform:translateY(-1px) }
|
||
|
||
/* SCROLL INDICATOR */
|
||
.scroll-hint {
|
||
position:absolute; bottom:30px; left:50%; transform:translateX(-50%);
|
||
display:flex; flex-direction:column; align-items:center; gap:6px;
|
||
font-family:var(--jb); font-size:8px; color:var(--t4); letter-spacing:2px;
|
||
animation:float 2s ease infinite;
|
||
}
|
||
@keyframes float { 0%,100%{transform:translateX(-50%) translateY(0)} 50%{transform:translateX(-50%) translateY(-6px)} }
|
||
.scroll-arrow { width:20px; height:20px; border-right:1px solid var(--t4); border-bottom:1px solid var(--t4); transform:rotate(45deg) }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
DASHBOARD BODY (below hero)
|
||
───────────────────────────────────────────────────────── */
|
||
#dash-body { padding:0 60px 80px }
|
||
|
||
.section-label {
|
||
font-family:var(--jb); font-size:10px; color:var(--t4);
|
||
letter-spacing:2px; text-transform:uppercase;
|
||
display:flex; align-items:center; gap:12px;
|
||
margin-bottom:24px; margin-top:60px;
|
||
}
|
||
.section-label::after { content:''; flex:1; height:1px; background:var(--rim) }
|
||
.section-label .tag-pill {
|
||
background:rgba(0,212,255,.08); border:1px solid rgba(0,212,255,.2);
|
||
color:var(--ice); padding:2px 10px; border-radius:20px; font-size:9px;
|
||
}
|
||
|
||
/* 2-col */
|
||
.g2 { display:grid; grid-template-columns:1fr 1fr; gap:20px }
|
||
.g3 { display:grid; grid-template-columns:1fr 1fr 1fr; gap:16px }
|
||
|
||
/* CARD */
|
||
.k {
|
||
background:var(--panel);
|
||
border:1px solid var(--rim);
|
||
border-radius:6px; overflow:hidden;
|
||
position:relative;
|
||
}
|
||
.k-head {
|
||
padding:16px 20px; border-bottom:1px solid var(--rim);
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
}
|
||
.k-title { font-size:12px; font-weight:700; color:var(--t1); letter-spacing:-.2px }
|
||
.k-body { padding:20px }
|
||
.k-accent-top { position:absolute; top:0; left:0; right:0; height:2px }
|
||
.k-accent-top.ice { background:linear-gradient(90deg,var(--ice) 0%,rgba(0,212,255,0) 60%) }
|
||
.k-accent-top.gold { background:linear-gradient(90deg,var(--gold) 0%,rgba(240,180,41,0) 60%) }
|
||
.k-accent-top.red { background:linear-gradient(90deg,var(--red) 0%,rgba(255,26,60,0) 60%) }
|
||
.k-accent-top.violet { background:linear-gradient(90deg,var(--violet) 0%,rgba(139,92,246,0) 60%) }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
FORENSICS PAGE (DRAMATIC)
|
||
───────────────────────────────────────────────────────── */
|
||
#page-forensics-content {
|
||
padding:80px 60px 60px;
|
||
}
|
||
.forensics-hero {
|
||
background:linear-gradient(135deg, rgba(255,26,60,.08) 0%, rgba(255,26,60,.02) 100%);
|
||
border:1px solid rgba(255,26,60,.2);
|
||
border-radius:8px; padding:40px 48px; margin-bottom:40px;
|
||
position:relative; overflow:hidden;
|
||
}
|
||
.forensics-hero::before {
|
||
content:'CLASSIFIED';
|
||
position:absolute; top:-10px; right:-20px;
|
||
font-family:var(--bb); font-size:120px; color:rgba(255,26,60,.04);
|
||
letter-spacing:4px; user-select:none; pointer-events:none;
|
||
line-height:1;
|
||
}
|
||
.fh-label {
|
||
font-family:var(--jb); font-size:9px; color:var(--red); letter-spacing:3px;
|
||
text-transform:uppercase; margin-bottom:12px; display:flex; align-items:center; gap:8px;
|
||
}
|
||
.fh-label::before { content:''; width:3px; height:3px; background:var(--red); border-radius:50%; animation:pulse 1s infinite }
|
||
.fh-title { font-family:var(--bb); font-size:42px; color:var(--t0); line-height:1.1; margin-bottom:16px }
|
||
.fh-title .red { color:var(--red); text-shadow:var(--gred) }
|
||
.fh-desc { font-family:var(--jb); font-size:11px; color:var(--t2); line-height:1.8; max-width:600px }
|
||
|
||
.f-grid { display:flex; flex-direction:column; gap:12px }
|
||
.f-card {
|
||
background:var(--panel); border:1px solid var(--rim);
|
||
border-radius:6px; padding:20px 24px;
|
||
display:grid; grid-template-columns:auto 1fr auto;
|
||
gap:16px; align-items:start;
|
||
transition:all .2s; cursor:pointer; position:relative; overflow:hidden;
|
||
}
|
||
.f-card::before {
|
||
content:''; position:absolute; left:0; top:0; bottom:0; width:3px;
|
||
background:var(--rim2); transition:background .2s;
|
||
}
|
||
.f-card:hover { border-color:rgba(255,26,60,.3); background:rgba(255,26,60,.03) }
|
||
.f-card:hover::before { background:var(--red) }
|
||
.f-card.CRITICAL::before { background:var(--red) }
|
||
.f-card.HIGH::before { background:var(--orange) }
|
||
.f-card.MEDIUM::before { background:var(--gold) }
|
||
|
||
.sev {
|
||
padding:4px 10px; border-radius:3px;
|
||
font-family:var(--jb); font-size:9px; font-weight:700; letter-spacing:.5px;
|
||
white-space:nowrap; align-self:start;
|
||
}
|
||
.sev.CRITICAL { background:rgba(255,26,60,.15); color:var(--red); border:1px solid rgba(255,26,60,.3) }
|
||
.sev.HIGH { background:rgba(255,107,0,.12); color:var(--orange); border:1px solid rgba(255,107,0,.25) }
|
||
.sev.MEDIUM { background:rgba(240,180,41,.1); color:var(--gold); border:1px solid rgba(240,180,41,.2) }
|
||
|
||
.f-title { font-size:12px; color:var(--t3); line-height:1.6 }
|
||
.f-title strong { color:var(--t0); display:block; font-size:13px; margin-bottom:4px }
|
||
.f-meta { font-family:var(--jb); font-size:8px; color:var(--t4); align-self:start; text-align:right; white-space:nowrap }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
FUNDING PAGE
|
||
───────────────────────────────────────────────────────── */
|
||
#page-funding-content { padding:80px 60px 60px }
|
||
.funding-hero-bar {
|
||
display:grid; grid-template-columns:repeat(3,1fr);
|
||
background:var(--deep); border:1px solid var(--rim);
|
||
border-radius:8px; overflow:hidden; margin-bottom:40px;
|
||
}
|
||
.fhb {
|
||
padding:28px 32px; border-right:1px solid var(--rim);
|
||
position:relative;
|
||
}
|
||
.fhb:last-child { border-right:none }
|
||
.fhb-n { font-family:var(--bb); font-size:44px; letter-spacing:1px; margin-bottom:4px }
|
||
.fhb-l { font-family:var(--jb); font-size:9px; color:var(--t4); letter-spacing:1px; text-transform:uppercase }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
NETWORK PAGE
|
||
───────────────────────────────────────────────────────── */
|
||
#page-network-content { padding:80px 60px 60px }
|
||
#the-graph {
|
||
width:100%; height:600px; border-radius:8px;
|
||
background:var(--deep); border:1px solid var(--rim);
|
||
position:relative; overflow:hidden;
|
||
}
|
||
#graph-legend-abs {
|
||
position:absolute; top:20px; left:20px; z-index:10;
|
||
background:rgba(1,2,10,.9); border:1px solid var(--rim);
|
||
border-radius:6px; padding:14px 18px; backdrop-filter:blur(12px);
|
||
}
|
||
#graph-node-info {
|
||
position:absolute; bottom:20px; right:20px; z-index:10;
|
||
background:rgba(1,2,10,.95); border:1px solid var(--rim2);
|
||
border-radius:6px; padding:16px 20px; max-width:300px;
|
||
display:none; backdrop-filter:blur(12px);
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
CHAT PAGE
|
||
───────────────────────────────────────────────────────── */
|
||
#page-chat-content { padding:80px 60px 60px }
|
||
.chat-wrap {
|
||
max-width:800px; margin:0 auto;
|
||
}
|
||
.chat-hero {
|
||
text-align:center; margin-bottom:40px;
|
||
}
|
||
.chat-hero h1 { font-family:var(--bb); font-size:64px; color:var(--ice); text-shadow:var(--gice); letter-spacing:2px; margin-bottom:8px }
|
||
.chat-hero p { font-family:var(--jb); font-size:11px; color:var(--t2); letter-spacing:.3px }
|
||
.chat-frame {
|
||
background:var(--deep); border:1px solid var(--rim2);
|
||
border-radius:8px; overflow:hidden;
|
||
}
|
||
.chat-msgs {
|
||
height:420px; overflow-y:auto; padding:24px;
|
||
display:flex; flex-direction:column; gap:16px;
|
||
}
|
||
.chat-msgs::-webkit-scrollbar { width:3px }
|
||
.chat-msgs::-webkit-scrollbar-thumb { background:var(--rim2) }
|
||
.cm {
|
||
max-width:78%; padding:14px 18px; border-radius:6px; line-height:1.7; font-size:12px;
|
||
position:relative;
|
||
}
|
||
.cm.user { align-self:flex-end; background:rgba(0,212,255,.1); border:1px solid rgba(0,212,255,.2); color:var(--t0) }
|
||
.cm.dabi { align-self:flex-start; background:var(--panel); border:1px solid var(--rim); color:var(--t3) }
|
||
.cm.dabi .cm-label { font-family:var(--jb); font-size:8px; color:var(--ice); margin-bottom:8px; letter-spacing:.5px }
|
||
.cm.error { align-self:flex-start; background:rgba(255,26,60,.06); border:1px solid rgba(255,26,60,.2); color:rgba(255,26,60,.8) }
|
||
.cm .cm-conf { font-family:var(--jb); font-size:8px; color:var(--t4); margin-top:8px; text-align:right }
|
||
.chat-suggestions-row {
|
||
display:flex; gap:8px; flex-wrap:wrap; padding:12px 24px;
|
||
border-top:1px solid var(--rim);
|
||
}
|
||
.sug-chip {
|
||
background:var(--card); border:1px solid var(--rim2);
|
||
color:var(--t2); padding:5px 12px; border-radius:20px;
|
||
font-family:var(--jb); font-size:10px; cursor:pointer;
|
||
transition:all .15s;
|
||
}
|
||
.sug-chip:hover { border-color:var(--ice); color:var(--ice) }
|
||
.chat-input-row {
|
||
display:flex; gap:0; border-top:1px solid var(--rim);
|
||
}
|
||
#ci {
|
||
flex:1; background:transparent; border:none; outline:none;
|
||
padding:16px 20px; color:var(--t1); font-family:var(--sy); font-size:13px;
|
||
}
|
||
#ci::placeholder { color:var(--t4) }
|
||
#cs {
|
||
background:var(--ice); color:var(--void); border:none; outline:none;
|
||
padding:16px 24px; font-family:var(--jb); font-size:11px; font-weight:700;
|
||
cursor:pointer; letter-spacing:1px; text-transform:uppercase;
|
||
transition:all .15s;
|
||
}
|
||
#cs:hover { background:var(--ice2) }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
TABLES
|
||
───────────────────────────────────────────────────────── */
|
||
.tw { overflow:auto; max-height:400px }
|
||
.tw::-webkit-scrollbar { width:3px; height:3px }
|
||
.tw::-webkit-scrollbar-thumb { background:var(--rim2) }
|
||
table.dt { width:100%; border-collapse:collapse; font-size:11px }
|
||
table.dt th {
|
||
padding:8px 12px; text-align:left; font-family:var(--jb); font-size:8px;
|
||
color:var(--t4); letter-spacing:1px; text-transform:uppercase;
|
||
border-bottom:1px solid var(--rim); position:sticky; top:0; background:var(--panel);
|
||
}
|
||
table.dt td { padding:9px 12px; border-bottom:1px solid rgba(28,37,64,.4); vertical-align:middle }
|
||
table.dt tr:hover td { background:rgba(0,212,255,.025) }
|
||
.mono { font-family:var(--jb) }
|
||
.eur { font-family:var(--jb); color:var(--lime); text-align:right; font-size:10px }
|
||
.chip {
|
||
display:inline-block; padding:2px 8px; border-radius:3px;
|
||
font-family:var(--jb); font-size:8px; letter-spacing:.3px;
|
||
}
|
||
.chip.ice { background:rgba(0,212,255,.1); color:var(--ice); border:1px solid rgba(0,212,255,.2) }
|
||
.chip.city { background:rgba(57,255,20,.08); color:var(--lime); border:1px solid rgba(57,255,20,.15) }
|
||
.chip.red { background:rgba(255,26,60,.1); color:var(--red); border:1px solid rgba(255,26,60,.2) }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
LOADING / UTILS
|
||
───────────────────────────────────────────────────────── */
|
||
.loading { display:flex; align-items:center; justify-content:center; gap:10px; padding:48px; color:var(--t4); font-family:var(--jb); font-size:10px; letter-spacing:1px }
|
||
.sp { width:16px; height:16px; border:2px solid var(--rim2); border-top-color:var(--ice); border-radius:50%; animation:spin .8s linear infinite }
|
||
@keyframes spin { to{transform:rotate(360deg)} }
|
||
|
||
/* SEARCH */
|
||
.search-row { display:flex; gap:8px; margin-bottom:20px }
|
||
.search-row input, .search-row select {
|
||
background:var(--panel); border:1px solid var(--rim); border-radius:4px;
|
||
padding:9px 14px; color:var(--t1); font-family:var(--sy); font-size:12px;
|
||
outline:none; transition:border-color .2s;
|
||
}
|
||
.search-row input { flex:1 }
|
||
.search-row input::placeholder { color:var(--t4) }
|
||
.search-row input:focus, .search-row select:focus { border-color:var(--ice) }
|
||
.search-row select { cursor:pointer; color:var(--t2) }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
COUNTER ANIMATION
|
||
───────────────────────────────────────────────────────── */
|
||
.count-up { transition:all .1s }
|
||
|
||
/* ─────────────────────────────────────────────────────────
|
||
MOBILE
|
||
───────────────────────────────────────────────────────── */
|
||
@media(max-width:768px) {
|
||
#hero, #dash-body, #page-forensics-content, #page-funding-content, #page-network-content, #page-chat-content { padding-left:20px; padding-right:20px }
|
||
.mega-stats { grid-template-columns:repeat(2,1fr) }
|
||
.g2, .g3 { grid-template-columns:1fr }
|
||
.hero-headline { font-size:clamp(42px,10vw,80px) }
|
||
.nav-links { display:none }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- PARTICLE CANVAS -->
|
||
<canvas id="canvas-bg"></canvas>
|
||
|
||
<!-- ───────── NAVIGATION ───────── -->
|
||
<nav id="nav">
|
||
<div class="nav-logo">DABI<span>· PGŽ SPORT INTELLIGENCE</span></div>
|
||
<div class="nav-links">
|
||
<div class="nav-a on" onclick="G('dash')">Dashboard</div>
|
||
<div class="nav-a danger" onclick="G('forensics')">⚠ Forenzika</div>
|
||
<div class="nav-a" onclick="G('funding')">Potpore</div>
|
||
<div class="nav-a" onclick="G('clubs')">Klubovi</div>
|
||
<div class="nav-a" onclick="G('network')">Mreža</div>
|
||
<div class="nav-a" onclick="G('chat')">AI Chat</div>
|
||
</div>
|
||
<div class="nav-right">
|
||
<div class="live-pill"><div class="live-dot"></div>LIVE DATA</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- ═══════════════════════════════
|
||
PAGE: DASHBOARD
|
||
═══════════════════════════════ -->
|
||
<div id="p-dash" class="page on">
|
||
<section id="hero">
|
||
<div class="hero-eyebrow">Ri.NET AI OS · PGŽ Civic Intelligence Platform</div>
|
||
<h1 class="hero-headline">
|
||
DABI<br>
|
||
<span class="hl-ice">ZOVE</span><br>
|
||
<span class="hl-gold">SPORT</span>
|
||
</h1>
|
||
<p class="hero-sub">
|
||
<strong>246 saveza. 2,244 klubova. 33,355 sportaša.</strong><br>
|
||
Svaki euro javnog novca vidljiv. Svaka veza dokumentirana.<br>
|
||
AI koji zna više od revizora — i brže od novinara.
|
||
</p>
|
||
|
||
<div class="mega-stats" id="mega-stats">
|
||
<div class="mstat c">
|
||
<div class="mstat-n" id="ms-klubovi">…</div>
|
||
<div class="mstat-l">Klubovi PGŽ</div>
|
||
<div class="mstat-sub" id="ms-oib">OIB verificiranih: …</div>
|
||
</div>
|
||
<div class="mstat g">
|
||
<div class="mstat-n" id="ms-proracun">…</div>
|
||
<div class="mstat-l">Proračun Sport 2026</div>
|
||
<div class="mstat-sub">Grad + Županija + Min.</div>
|
||
</div>
|
||
<div class="mstat r">
|
||
<div class="mstat-n" id="ms-forensics">…</div>
|
||
<div class="mstat-l">⚠ Forenzički Nalazi</div>
|
||
<div class="mstat-sub">CRITICAL — vidljivi javnosti</div>
|
||
</div>
|
||
<div class="mstat v">
|
||
<div class="mstat-n" id="ms-clanovi">…</div>
|
||
<div class="mstat-l">Registriranih 2026</div>
|
||
<div class="mstat-sub">Aktivnih sportaša PGŽ</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="hero-actions">
|
||
<button class="cta cta-danger" onclick="G('forensics')">⚠ Otvori forenzičke nalaze</button>
|
||
<button class="cta cta-pri" onclick="G('chat')">◆ Pitaj DABI AI</button>
|
||
<button class="cta cta-ghost" onclick="G('network')">◎ Graf mreže veza</button>
|
||
</div>
|
||
|
||
<div class="scroll-hint"><div class="scroll-arrow"></div>SCROLL</div>
|
||
</section>
|
||
|
||
<section id="dash-body">
|
||
<div class="section-label">Najfinanciranije organizacije <span class="tag-pill">2026</span></div>
|
||
<div class="g2">
|
||
<div class="k">
|
||
<div class="k-accent-top ice"></div>
|
||
<div class="k-head">
|
||
<div class="k-title">Top Potpore — Grad Rijeka & PGŽ</div>
|
||
<button class="cta cta-ghost" style="font-size:9px;padding:4px 12px" onclick="G('funding')">Sve →</button>
|
||
</div>
|
||
<div class="tw"><table class="dt" id="dt-top">
|
||
<thead><tr><th>Klub</th><th>Sport</th><th style="text-align:right">EUR</th></tr></thead>
|
||
<tbody><tr><td colspan="3"><div class="loading"><div class="sp"></div>Učitavam…</div></td></tr></tbody>
|
||
</table></div>
|
||
</div>
|
||
<div class="k">
|
||
<div class="k-accent-top gold"></div>
|
||
<div class="k-head"><div class="k-title">Distribucija po Vrsti Sporta</div></div>
|
||
<div class="k-body" style="height:300px; display:flex; align-items:center">
|
||
<canvas id="sport-donut"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-label">Alarmi <span class="tag-pill" style="background:rgba(255,26,60,.1);border-color:rgba(255,26,60,.3);color:var(--red)">CRITICAL</span></div>
|
||
<div class="k" style="margin-bottom:40px">
|
||
<div class="k-accent-top red"></div>
|
||
<div class="k-head">
|
||
<div class="k-title" style="color:var(--red)">Forenzički Alarmi — Top 4 CRITICAL</div>
|
||
<button class="cta cta-danger" style="font-size:9px;padding:4px 12px" onclick="G('forensics')">Sve nalaze →</button>
|
||
</div>
|
||
<div id="foren-preview" style="padding:4px 0">
|
||
<div class="loading"><div class="sp"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-label">Statistike baze znanja <span class="tag-pill">LIVE</span></div>
|
||
<div class="g3" id="stat-cards">
|
||
<div class="k"><div class="k-accent-top ice"></div><div class="k-body" id="sc1"><div class="loading"><div class="sp"></div></div></div></div>
|
||
<div class="k"><div class="k-accent-top gold"></div><div class="k-body" id="sc2"><div class="loading"><div class="sp"></div></div></div></div>
|
||
<div class="k"><div class="k-accent-top violet"></div><div class="k-body" id="sc3"><div class="loading"><div class="sp"></div></div></div></div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════
|
||
PAGE: FORENSICS
|
||
═══════════════════════════════ -->
|
||
<div id="p-forensics" class="page">
|
||
<div id="page-forensics-content">
|
||
<div class="forensics-hero">
|
||
<div class="fh-label">⚑ Forenzička analiza · OIB-verificirani nalazi</div>
|
||
<h1 class="fh-title">184 OSOBA.<br><span class="red">660M EUR</span> VEZA.</h1>
|
||
<p class="fh-desc">
|
||
Automatska analiza sukoba interesa u PGŽ sport ekosustavu.<br>
|
||
USKOK optuženik u sportu. Zero-employee tvrtke s milijunskim prihodima.<br>
|
||
Svaki nalaz temeljen na: FINA RGFI + Sudreg + javna nabava + upisnik udruga.
|
||
</p>
|
||
</div>
|
||
|
||
<div style="display:flex;gap:8px;margin-bottom:28px;flex-wrap:wrap">
|
||
<button class="cta cta-danger" onclick="filterF('')">Sve</button>
|
||
<button class="cta cta-ghost" onclick="filterF('CRITICAL')">CRITICAL</button>
|
||
<button class="cta cta-ghost" onclick="filterF('HIGH')">HIGH</button>
|
||
<button class="cta cta-ghost" onclick="filterF('MEDIUM')">MEDIUM</button>
|
||
</div>
|
||
|
||
<div class="f-grid" id="f-list">
|
||
<div class="loading"><div class="sp"></div>Analiziram forenzičku bazu…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════
|
||
PAGE: FUNDING
|
||
═══════════════════════════════ -->
|
||
<div id="p-funding" class="page">
|
||
<div id="page-funding-content">
|
||
<div class="funding-hero-bar">
|
||
<div class="fhb">
|
||
<div class="fhb-n" style="color:var(--gold)">€2.8M</div>
|
||
<div class="fhb-l">Ukupni sport proračun 2026</div>
|
||
</div>
|
||
<div class="fhb">
|
||
<div class="fhb-n" style="color:var(--ice)">€528K</div>
|
||
<div class="fhb-l">PGŽ direktno + Grad Rijeka</div>
|
||
</div>
|
||
<div class="fhb">
|
||
<div class="fhb-n" style="color:var(--lime)" id="fhb-records">…</div>
|
||
<div class="fhb-l">Evidentirani transferi</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="k" style="margin-bottom:24px">
|
||
<div class="k-accent-top ice"></div>
|
||
<div class="k-head">
|
||
<div class="k-title">Potpore po Klubovima</div>
|
||
<select id="fy" onchange="loadFunding()" style="background:var(--card);border:1px solid var(--rim);color:var(--t2);padding:4px 10px;border-radius:3px;font-size:11px;font-family:var(--jb);cursor:pointer">
|
||
<option value="2026">2026</option>
|
||
<option value="2024">2024</option>
|
||
<option value="2023">2023</option>
|
||
</select>
|
||
</div>
|
||
<div class="k-body" style="height:280px">
|
||
<canvas id="f-bar"></canvas>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="k">
|
||
<div class="k-accent-top gold"></div>
|
||
<div class="tw"><table class="dt" id="f-tbl">
|
||
<thead><tr><th>Klub</th><th>Sport</th><th>Grad</th><th>Godina</th><th style="text-align:right">EUR</th></tr></thead>
|
||
<tbody id="f-tbody"><tr><td colspan="5"><div class="loading"><div class="sp"></div></div></td></tr></tbody>
|
||
</table></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════
|
||
PAGE: CLUBS
|
||
═══════════════════════════════ -->
|
||
<div id="p-clubs" class="page">
|
||
<div style="padding:80px 60px 60px">
|
||
<div class="search-row">
|
||
<input id="cs-q" type="text" placeholder="Pretraži klubove…" oninput="searchC()">
|
||
<select id="cs-city" onchange="searchC()">
|
||
<option value="">Svi gradovi</option>
|
||
<option value="Rijeka">Rijeka</option>
|
||
<option value="Opatija">Opatija</option>
|
||
<option value="Krk">Krk</option>
|
||
<option value="Crikvenica">Crikvenica</option>
|
||
<option value="Bakar">Bakar</option>
|
||
</select>
|
||
</div>
|
||
<div class="k">
|
||
<div class="k-accent-top ice"></div>
|
||
<div class="tw" style="max-height:520px"><table class="dt" id="clubs-t">
|
||
<thead><tr><th>Naziv</th><th>Grad</th><th>Tip</th><th>OIB</th><th>Reg. broj</th></tr></thead>
|
||
<tbody id="clubs-tb"><tr><td colspan="5"><div class="loading"><div class="sp"></div></div></td></tr></tbody>
|
||
</table></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════
|
||
PAGE: NETWORK
|
||
═══════════════════════════════ -->
|
||
<div id="p-network" class="page">
|
||
<div id="page-network-content">
|
||
<div class="section-label" style="margin-top:0">Entity Network <span class="tag-pill">3D FORCE GRAPH</span></div>
|
||
<p style="font-family:var(--jb);font-size:10px;color:var(--t4);margin-bottom:20px;letter-spacing:.3px">
|
||
184 osoba · 170 klubova · 279 tvrtki · 660M EUR cross-connections. Klikni čvor za detalje.
|
||
</p>
|
||
<div id="the-graph">
|
||
<div id="graph-legend-abs">
|
||
<div style="font-family:var(--jb);font-size:8px;color:var(--t4);letter-spacing:1px;margin-bottom:10px;text-transform:uppercase">Legenda</div>
|
||
<div style="display:flex;flex-direction:column;gap:6px">
|
||
<div style="display:flex;align-items:center;gap:8px;font-size:10px;color:var(--t2)"><div style="width:10px;height:10px;border-radius:50%;background:var(--ice)"></div>Osoba</div>
|
||
<div style="display:flex;align-items:center;gap:8px;font-size:10px;color:var(--t2)"><div style="width:10px;height:10px;border-radius:50%;background:var(--lime)"></div>Klub/Savez</div>
|
||
<div style="display:flex;align-items:center;gap:8px;font-size:10px;color:var(--t2)"><div style="width:10px;height:10px;border-radius:50%;background:var(--violet)"></div>Tvrtka</div>
|
||
<div style="display:flex;align-items:center;gap:8px;font-size:10px;color:var(--t2)"><div style="width:10px;height:10px;border-radius:50%;background:var(--red)"></div>⚠ Forenzički</div>
|
||
</div>
|
||
</div>
|
||
<div id="graph-node-info">
|
||
<div style="font-family:var(--jb);font-size:8px;color:var(--t4);letter-spacing:1px;margin-bottom:8px">ODABRANI ČVOR</div>
|
||
<div id="gni-name" style="font-size:14px;font-weight:700;color:var(--t0);margin-bottom:3px">–</div>
|
||
<div id="gni-type" style="font-family:var(--jb);font-size:9px;color:var(--t4);margin-bottom:10px">–</div>
|
||
<div id="gni-detail" style="font-size:11px;color:var(--t3);line-height:1.7">–</div>
|
||
</div>
|
||
<div class="loading" id="gload" style="position:absolute;inset:0;background:var(--deep)">
|
||
<div class="sp"></div>Gradim 3D mrežu…
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:12px;text-align:right">
|
||
<button class="cta cta-ghost" onclick="loadNetwork()">↺ Osvježi</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════
|
||
PAGE: CHAT
|
||
═══════════════════════════════ -->
|
||
<div id="p-chat" class="page">
|
||
<div id="page-chat-content">
|
||
<div class="chat-wrap">
|
||
<div class="chat-hero">
|
||
<h1>PITAJ DABI</h1>
|
||
<p>Sport Intelligence AI · Čakavski novinarski stil · PGŽ domenski model</p>
|
||
</div>
|
||
<div class="chat-frame">
|
||
<div class="chat-msgs" id="chat-msgs">
|
||
<div class="cm dabi">
|
||
<div class="cm-label">DABI · SPORT PERSONA · ONLINE</div>
|
||
Bok! Ja san DABI — čakavski AI novinar za PGŽ sport. Znan saveze, predsjednike, potpore, forenziku. Ako niman podatak u bazi, rećen ti to direktno — ne izmišljan ništa.
|
||
</div>
|
||
</div>
|
||
<div class="chat-suggestions-row">
|
||
<div class="sug-chip" onclick="ask(this)">Predsjednik HNK Rijeka?</div>
|
||
<div class="sug-chip" onclick="ask(this)">Ivan Sušanj?</div>
|
||
<div class="sug-chip" onclick="ask(this)">Proračun PGŽ sport 2026?</div>
|
||
<div class="sug-chip" onclick="ask(this)">Slavica Grgurić-Pajnić?</div>
|
||
<div class="sug-chip" onclick="ask(this)">Sukob interesa u sportu?</div>
|
||
<div class="sug-chip" onclick="ask(this)">Tajnik ŠK Kraljevica?</div>
|
||
</div>
|
||
<div class="chat-input-row">
|
||
<input id="ci" type="text" placeholder="Pitaj o sportu PGŽ…" onkeydown="if(event.key==='Enter')sendMsg()">
|
||
<button id="cs" onclick="sendMsg()">POŠALJI</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════════════════════
|
||
JAVASCRIPT
|
||
═══════════════════════════════ -->
|
||
<script>
|
||
// ──── CONFIG ────
|
||
const API = 'https://api.rinet.one/api/v1';
|
||
const DABI = `${API}/dabi/chat`;
|
||
|
||
// ──── CANVAS PARTICLES ────
|
||
(function(){
|
||
const c = document.getElementById('canvas-bg');
|
||
const cx = c.getContext('2d');
|
||
let W, H, pts = [];
|
||
|
||
function resize() {
|
||
W = c.width = window.innerWidth;
|
||
H = c.height = window.innerHeight;
|
||
}
|
||
|
||
function mkPts(n) {
|
||
pts = [];
|
||
for(let i=0;i<n;i++) pts.push({
|
||
x: Math.random()*W, y: Math.random()*H,
|
||
vx: (Math.random()-.5)*.3, vy: (Math.random()-.5)*.3,
|
||
r: Math.random()*1.5+.3,
|
||
a: Math.random()*.8+.2
|
||
});
|
||
}
|
||
|
||
function draw() {
|
||
cx.clearRect(0,0,W,H);
|
||
pts.forEach(p=>{
|
||
p.x+=p.vx; p.y+=p.vy;
|
||
if(p.x<0)p.x=W; if(p.x>W)p.x=0;
|
||
if(p.y<0)p.y=H; if(p.y>H)p.y=0;
|
||
|
||
cx.beginPath();
|
||
cx.arc(p.x,p.y,p.r,0,Math.PI*2);
|
||
cx.fillStyle=`rgba(0,212,255,${p.a*.4})`;
|
||
cx.fill();
|
||
});
|
||
|
||
// Connect nearby
|
||
for(let i=0;i<pts.length;i++){
|
||
for(let j=i+1;j<pts.length;j++){
|
||
const dx=pts[i].x-pts[j].x, dy=pts[i].y-pts[j].y;
|
||
const d=Math.sqrt(dx*dx+dy*dy);
|
||
if(d<120){
|
||
cx.beginPath();
|
||
cx.moveTo(pts[i].x,pts[i].y);
|
||
cx.lineTo(pts[j].x,pts[j].y);
|
||
cx.strokeStyle=`rgba(0,212,255,${(.12*(1-d/120))})`;
|
||
cx.lineWidth=.5;
|
||
cx.stroke();
|
||
}
|
||
}
|
||
}
|
||
requestAnimationFrame(draw);
|
||
}
|
||
|
||
window.addEventListener('resize',()=>{ resize(); mkPts(80) });
|
||
resize(); mkPts(80); draw();
|
||
})();
|
||
|
||
// ──── ROUTING ────
|
||
const pages = ['dash','forensics','funding','clubs','network','chat'];
|
||
let curPage = 'dash';
|
||
|
||
function G(id) {
|
||
pages.forEach(p=>{
|
||
document.getElementById('p-'+p).classList.remove('on');
|
||
});
|
||
document.querySelectorAll('.nav-a').forEach(a=>{
|
||
a.classList.remove('on');
|
||
});
|
||
document.getElementById('p-'+id).classList.add('on');
|
||
document.querySelectorAll('.nav-a').forEach(a=>{
|
||
if(a.getAttribute('onclick')&&a.getAttribute('onclick').includes(`'${id}'`)) a.classList.add('on');
|
||
});
|
||
curPage = id;
|
||
if(id==='network' && !window._netLoaded) loadNetwork();
|
||
if(id==='clubs' && !window._clubsLoaded) loadClubs();
|
||
if(id==='funding' && !window._fundingLoaded) loadFunding();
|
||
if(id==='forensics' && !window._forensicsLoaded) loadForensics();
|
||
window.scrollTo(0,0);
|
||
}
|
||
|
||
// ──── UTILS ────
|
||
const fmtEur = n => n==null?'–':'€'+Number(n).toLocaleString('hr-HR',{maximumFractionDigits:0});
|
||
const fmtN = n => n==null?'–':Number(n).toLocaleString('hr-HR');
|
||
const $ = id => document.getElementById(id);
|
||
|
||
function animCount(el, target, suffix='', dur=1800) {
|
||
const start = Date.now();
|
||
const t = parseInt(String(target).replace(/[^0-9]/g,'')) || 0;
|
||
const prefix = String(target).replace(/[0-9,.\s]/g,'').replace(/\d.*/,'');
|
||
const tick = () => {
|
||
const p = Math.min(1,(Date.now()-start)/dur);
|
||
const ease = 1-Math.pow(1-p,4);
|
||
const cur = Math.round(ease*t);
|
||
el.textContent = prefix + fmtN(cur) + suffix;
|
||
if(p<1) requestAnimationFrame(tick);
|
||
else el.textContent = prefix + fmtN(t) + suffix;
|
||
};
|
||
tick();
|
||
}
|
||
|
||
// ──── DASHBOARD INIT ────
|
||
async function initDash() {
|
||
try {
|
||
const d = await fetch(`${API}/sport/dashboard`).then(r=>r.json());
|
||
|
||
animCount($('ms-klubovi'), d.klubovi);
|
||
animCount($('ms-clanovi'), d.registriranih_2026);
|
||
animCount($('ms-forensics'), d.forensics);
|
||
$('ms-oib').textContent = 'OIB verificiranih: ' + fmtN(d.klubovi_oib);
|
||
|
||
const pb = d.proracun_2026?.[0];
|
||
if(pb){
|
||
const tot = (pb.ukupno||0)+(pb.ministarstvo||0);
|
||
$('ms-proracun').textContent = '€'+(tot/1000000).toFixed(1)+'M';
|
||
}
|
||
|
||
// Sport donut chart
|
||
if(d.by_sport_top5) {
|
||
const tops = d.by_sport_top5.slice(0,7);
|
||
const ctx = $('sport-donut').getContext('2d');
|
||
new Chart(ctx, {
|
||
type:'doughnut',
|
||
data:{
|
||
labels: tops.map(x=>x.sport),
|
||
datasets:[{
|
||
data: tops.map(x=>x.n),
|
||
backgroundColor:['#00d4ff','#f0b429','#8b5cf6','#ff6b00','#39ff14','#ff1a3c','#4f8fff'],
|
||
borderWidth:2, borderColor:'#0d1220',
|
||
hoverBorderWidth:3
|
||
}]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false,
|
||
plugins:{ legend:{ position:'right', labels:{color:'#9aa5be',font:{size:10,family:'JetBrains Mono'},boxWidth:10,padding:10} } },
|
||
cutout:'65%'
|
||
}
|
||
});
|
||
}
|
||
|
||
// Stat cards
|
||
$('sc1').innerHTML = `
|
||
<div style="font-family:var(--jb);font-size:9px;color:var(--t4);letter-spacing:1px;margin-bottom:16px">SPORTAŠI & ČLANOVI</div>
|
||
${statRow('Članova 2026', fmtN(d.clanstvo_2026))}
|
||
${statRow('Registriranih', fmtN(d.registriranih_2026))}
|
||
${statRow('Saveza', fmtN(d.savezi))}
|
||
`;
|
||
$('sc2').innerHTML = `
|
||
<div style="font-family:var(--jb);font-size:9px;color:var(--t4);letter-spacing:1px;margin-bottom:16px">DOKUMENTI & PROPISI</div>
|
||
${statRow('Dokumenata (OCR)', fmtN(d.documents))}
|
||
${statRow('Pravilnici', fmtN(d.pravilnici))}
|
||
${statRow('Zakoni', fmtN(d.laws))}
|
||
`;
|
||
$('sc3').innerHTML = `
|
||
<div style="font-family:var(--jb);font-size:9px;color:var(--t4);letter-spacing:1px;margin-bottom:16px">AI INTELLIGENCE</div>
|
||
${statRow('Baza znanja', '5.3M faktova')}
|
||
${statRow('Vektoriziranih', '18.5M točaka')}
|
||
${statRow('DABI accuracy', '92.7%')}
|
||
`;
|
||
|
||
} catch(e) { console.error('Dashboard init:', e) }
|
||
|
||
loadTopFunding();
|
||
loadForensicsPreview();
|
||
}
|
||
|
||
function statRow(l,v) {
|
||
return `<div style="display:flex;justify-content:space-between;align-items:center;padding:7px 0;border-bottom:1px solid rgba(28,37,64,.4)">
|
||
<span style="font-size:11px;color:var(--t2)">${l}</span>
|
||
<span style="font-family:var(--jb);font-size:11px;color:var(--ice)">${v}</span>
|
||
</div>`;
|
||
}
|
||
|
||
// ──── TOP FUNDING ────
|
||
async function loadTopFunding() {
|
||
try {
|
||
const data = await fetch(`${API}/sport/funding/v2?limit=8&year=2026`).then(r=>r.json());
|
||
const tb = $('dt-top').querySelector('tbody');
|
||
tb.innerHTML = data.map(r=>`
|
||
<tr>
|
||
<td style="font-weight:700;color:var(--t0)">${r.club_name||'–'}</td>
|
||
<td><span class="chip ice">${r.sport||'–'}</span></td>
|
||
<td class="eur">${fmtEur(r.total)}</td>
|
||
</tr>`).join('');
|
||
} catch(e) { console.error(e) }
|
||
}
|
||
|
||
// ──── FORENSICS PREVIEW ────
|
||
async function loadForensicsPreview() {
|
||
try {
|
||
const data = await fetch(`${API}/sport/forensics?limit=4`).then(r=>r.json());
|
||
$('foren-preview').innerHTML = data.slice(0,4).map(f=>`
|
||
<div class="f-card ${f.severity}" onclick="G('forensics')" style="cursor:pointer">
|
||
<span class="sev ${f.severity}">${f.severity}</span>
|
||
<div class="f-title">
|
||
<strong>${(f.title||'').substring(0,90)}${(f.title||'').length>90?'…':''}</strong>
|
||
<span style="font-family:var(--jb);font-size:9px;color:var(--t4)">${f.finding_type||''} · ${f.created_at||''}</span>
|
||
</div>
|
||
<div class="f-meta">${f.verification_status||''}</div>
|
||
</div>`).join('');
|
||
} catch(e) { $('foren-preview').innerHTML='<div style="padding:16px;color:var(--t4);font-size:11px">Greška učitavanja</div>' }
|
||
}
|
||
|
||
// ──── FORENSICS ────
|
||
let _forensicsAll = [];
|
||
async function loadForensics() {
|
||
window._forensicsLoaded = true;
|
||
try {
|
||
const data = await fetch(`${API}/sport/forensics?limit=60`).then(r=>r.json());
|
||
_forensicsAll = data;
|
||
renderForensics(data);
|
||
} catch(e) { $('f-list').innerHTML=`<div style="padding:24px;color:var(--red)">Greška: ${e.message}</div>` }
|
||
}
|
||
|
||
function renderForensics(data) {
|
||
$('f-list').innerHTML = data.map(f=>`
|
||
<div class="f-card ${f.severity}">
|
||
<span class="sev ${f.severity}">${f.severity||'–'}</span>
|
||
<div class="f-title">
|
||
<strong>${f.title||'–'}</strong>
|
||
<span style="font-family:var(--jb);font-size:9px;color:var(--t4)">${f.finding_type||''} · ${f.verification_status||''}</span>
|
||
</div>
|
||
<div class="f-meta" style="color:var(--t4)">${f.created_at||''}</div>
|
||
</div>`).join('') || '<div style="padding:24px;color:var(--t4);text-align:center;font-family:var(--jb);font-size:11px">Nema nalaza</div>';
|
||
}
|
||
|
||
function filterF(sev) {
|
||
renderForensics(sev ? _forensicsAll.filter(f=>f.severity===sev) : _forensicsAll);
|
||
}
|
||
|
||
// ──── FUNDING ────
|
||
let _fBarChart;
|
||
async function loadFunding() {
|
||
window._fundingLoaded = true;
|
||
const yr = $('fy')?.value||2026;
|
||
try {
|
||
const data = await fetch(`${API}/sport/funding/v2?limit=50&year=${yr}`).then(r=>r.json());
|
||
$('fhb-records').textContent = fmtN(data.length);
|
||
|
||
const top14 = data.slice(0,14);
|
||
const ctx = $('f-bar').getContext('2d');
|
||
if(_fBarChart) _fBarChart.destroy();
|
||
_fBarChart = new Chart(ctx, {
|
||
type:'bar',
|
||
data:{
|
||
labels: top14.map(x=>(x.club_name||'').substring(0,18)),
|
||
datasets:[{
|
||
label:`Potpore ${yr}`,
|
||
data: top14.map(x=>x.total||0),
|
||
backgroundColor:'rgba(0,212,255,.3)',
|
||
borderColor:'rgba(0,212,255,.9)',
|
||
borderWidth:1.5, borderRadius:3
|
||
}]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false,
|
||
scales:{
|
||
x:{ticks:{color:'#9aa5be',font:{size:9,family:'JetBrains Mono'},maxRotation:45},grid:{color:'rgba(28,37,64,.5)'}},
|
||
y:{ticks:{color:'#9aa5be',font:{size:9,family:'JetBrains Mono'},callback:v=>'€'+fmtN(v)},grid:{color:'rgba(28,37,64,.5)'}}
|
||
},
|
||
plugins:{legend:{labels:{color:'#9aa5be',font:{size:10}}}}
|
||
}
|
||
});
|
||
|
||
$('f-tbody').innerHTML = data.map(r=>`
|
||
<tr>
|
||
<td style="font-weight:700;color:var(--t0)">${r.club_name||'–'}</td>
|
||
<td>${r.sport||'–'}</td>
|
||
<td><span class="chip city">${r.city||'–'}</span></td>
|
||
<td class="mono" style="color:var(--t4)">${r.year||'–'}</td>
|
||
<td class="eur">${fmtEur(r.total)}</td>
|
||
</tr>`).join('');
|
||
} catch(e) { console.error(e) }
|
||
}
|
||
|
||
// ──── CLUBS ────
|
||
let _cTimer;
|
||
async function loadClubs(q='', city='') {
|
||
window._clubsLoaded = true;
|
||
$('clubs-tb').innerHTML = '<tr><td colspan="5"><div class="loading"><div class="sp"></div>Učitavam…</div></td></tr>';
|
||
try {
|
||
let url = `${API}/sport/registar?limit=60`;
|
||
if(q) url+=`&q=${encodeURIComponent(q)}`;
|
||
if(city) url+=`&grad=${encodeURIComponent(city)}`;
|
||
const data = await fetch(url).then(r=>r.json());
|
||
$('clubs-tb').innerHTML = data.map(r=>`
|
||
<tr>
|
||
<td style="font-weight:700;color:var(--t0)">${r.naziv||r.naziv_pravne_osobe||'–'}</td>
|
||
<td><span class="chip city">${r.grad||'–'}</span></td>
|
||
<td style="color:var(--t4);font-size:10px">${r.tip_udruge||r.tip_subjekta||'–'}</td>
|
||
<td class="mono" style="font-size:9px;color:var(--t4)">${r.oib||'–'}</td>
|
||
<td class="mono" style="font-size:9px;color:var(--t4)">${r.reg_broj||'–'}</td>
|
||
</tr>`).join('');
|
||
} catch(e) { $('clubs-tb').innerHTML=`<tr><td colspan="5" style="color:var(--red);padding:12px">Greška: ${e.message}</td></tr>` }
|
||
}
|
||
function searchC(){ clearTimeout(_cTimer); _cTimer=setTimeout(()=>loadClubs($('cs-q').value,$('cs-city').value),350) }
|
||
|
||
// ──── NETWORK ────
|
||
async function loadNetwork() {
|
||
window._netLoaded = true;
|
||
$('gload').style.display='flex';
|
||
try {
|
||
const data = await fetch(`${API}/sport/network?limit=100`).then(r=>r.json());
|
||
const nodesMap={}, links=[];
|
||
const forensicPpl = new Set(['SAMIR BARAĆ','MIROSLAV MARIĆ','DOROTEA PESIC-BUKOVAC','OMNI-PRO']);
|
||
|
||
data.forEach(rel=>{
|
||
const {person1_name:p1,person2_name:p2,shared_entity:ent,shared_entity_type:et,relationship_type:rt}=rel;
|
||
if(!p1||!ent) return;
|
||
if(!nodesMap[p1]) nodesMap[p1]={id:p1,name:p1,type:'person',val:4,forensic:forensicPpl.has(p1)};
|
||
const isCompany = et&&(et.includes('Tvrtka')||et.includes('dru'));
|
||
if(!nodesMap[ent]) nodesMap[ent]={id:ent,name:ent.substring(0,28)+'…',type:isCompany?'company':'club',val:6};
|
||
if(p2&&!nodesMap[p2]) nodesMap[p2]={id:p2,name:p2,type:'person',val:4,forensic:forensicPpl.has(p2)};
|
||
links.push({source:p1,target:ent,type:rt});
|
||
if(p2) links.push({source:p2,target:ent,type:rt});
|
||
});
|
||
|
||
const nodes=Object.values(nodesMap);
|
||
const g=$('the-graph');
|
||
|
||
const Graph = ForceGraph3D()(g)
|
||
.graphData({nodes,links})
|
||
.backgroundColor('transparent')
|
||
.nodeLabel(n=>`<div style="background:rgba(1,2,10,.9);border:1px solid #1c2540;padding:6px 10px;border-radius:4px;font-family:JetBrains Mono,monospace;font-size:11px;color:#e8eaf2">${n.name}<br><span style="color:#5a6480;font-size:9px">${n.type.toUpperCase()}</span></div>`)
|
||
.nodeColor(n=>{ if(n.forensic||n.type==='company'&&n.name.includes('OMNI')) return '#ff1a3c'; if(n.type==='person') return '#00d4ff'; if(n.type==='company') return '#8b5cf6'; return '#39ff14'; })
|
||
.nodeVal(n=>n.forensic?8:n.val)
|
||
.linkColor(l=>'rgba(0,212,255,.15)')
|
||
.linkWidth(.5)
|
||
.onNodeClick(n=>{
|
||
$('gni-name').textContent=n.name;
|
||
$('gni-type').textContent=n.type.toUpperCase()+(n.forensic?' · ⚠ FORENZIČKI SUBJEKT':'');
|
||
$('gni-detail').innerHTML=n.forensic?'<span style="color:var(--red)">Provjeri forenzičke nalaze za ovu osobu</span>':` ID: ${n.id}`;
|
||
$('graph-node-info').style.display='block';
|
||
})
|
||
.width(g.offsetWidth).height(g.offsetHeight);
|
||
|
||
$('gload').style.display='none';
|
||
} catch(e){ $('gload').innerHTML=`<div style="color:var(--red);font-size:11px">Greška: ${e.message}</div>` }
|
||
}
|
||
|
||
// ──── CHAT ────
|
||
async function sendMsg(q) {
|
||
const input=$('ci');
|
||
const msg=q||input.value.trim();
|
||
if(!msg) return;
|
||
input.value='';
|
||
|
||
const box=$('chat-msgs');
|
||
box.innerHTML+=`<div class="cm user">${msg}</div>`;
|
||
box.innerHTML+=`<div class="cm dabi" id="_typing"><div class="cm-label">DABI · SPORT · PROCESIRA</div><div class="sp" style="width:14px;height:14px;display:inline-block;vertical-align:middle"></div></div>`;
|
||
box.scrollTop=box.scrollHeight;
|
||
|
||
try {
|
||
const resp=await fetch(DABI,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,persona:'sport'})});
|
||
const j=await resp.json();
|
||
const t=$('_typing'); if(t)t.remove();
|
||
|
||
const ans=j?.data?.response||j?.response||j?.error||'Nema odgovora.';
|
||
const conf=j?.data?.confidence??j?.confidence??null;
|
||
const src=j?.data?.source||j?.source||'';
|
||
const isNull=ans.includes('nemam')||ans.includes('nije dostupno');
|
||
|
||
box.innerHTML+=`<div class="cm ${isNull?'error':'dabi'}">
|
||
<div class="cm-label">DABI · ${src||'AI'}</div>
|
||
${ans}
|
||
${conf!=null?`<div class="cm-conf">CONFIDENCE: ${Math.round(conf*100)}%</div>`:''}
|
||
</div>`;
|
||
box.scrollTop=box.scrollHeight;
|
||
} catch(e){
|
||
const t=$('_typing'); if(t)t.remove();
|
||
box.innerHTML+=`<div class="cm error">Greška: ${e.message}</div>`;
|
||
box.scrollTop=box.scrollHeight;
|
||
}
|
||
}
|
||
function ask(el){ sendMsg(el.textContent) }
|
||
|
||
// ──── BOOT ────
|
||
document.addEventListener('DOMContentLoaded',()=>{
|
||
initDash();
|
||
loadClubs();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|