cyberai-1 commited on
Commit
2390a6e
Β·
1 Parent(s): 66d25ea
Files changed (2) hide show
  1. templates/index.html +179 -1
  2. templates/index_old.html +1068 -0
templates/index.html CHANGED
@@ -28,6 +28,14 @@
28
  --ff-head: 'Rajdhani', sans-serif;
29
  --ff-body: 'Exo 2', sans-serif;
30
  --r: 4px;
 
 
 
 
 
 
 
 
31
  }
32
 
33
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
@@ -42,6 +50,16 @@
42
  cursor: default;
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
45
  /* ── Grid overlay ── */
46
  body::before {
47
  content: '';
@@ -486,7 +504,9 @@
486
  </head>
487
  <body>
488
 
489
- <div class="wrapper">
 
 
490
 
491
  <!-- HEADER -->
492
  <header>
@@ -803,5 +823,163 @@
803
  // Init
804
  document.getElementById("tab-file").style.display = "block";
805
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
806
  </body>
807
  </html>
 
28
  --ff-head: 'Rajdhani', sans-serif;
29
  --ff-body: 'Exo 2', sans-serif;
30
  --r: 4px;
31
+
32
+ /*new add*/
33
+ --bg-main: #160a2f;
34
+ --bg-deep: #0e061f;
35
+ --pink: #ff4fa3;
36
+ --pink-soft: rgba(255, 79, 163, 0.18);
37
+ --pink-line: rgba(255, 79, 163, 0.28);
38
+ --white-soft: rgba(255,255,255,0.06);
39
  }
40
 
41
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
 
50
  cursor: default;
51
  }
52
 
53
+ /* ── Plexus canvas background ── */
54
+ #plexus-bg {
55
+ position: fixed;
56
+ inset: 0;
57
+ width: 100%;
58
+ height: 100%;
59
+ z-index: 0;
60
+ pointer-events: none;
61
+ }
62
+
63
  /* ── Grid overlay ── */
64
  body::before {
65
  content: '';
 
504
  </head>
505
  <body>
506
 
507
+
508
+ <!-- Animated plexus background -->
509
+ <canvas id="plexus-bg"></canvas>
510
 
511
  <!-- HEADER -->
512
  <header>
 
823
  // Init
824
  document.getElementById("tab-file").style.display = "block";
825
  </script>
826
+
827
+ <!-- ══ PLEXUS BACKGROUND ANIMATION ══ -->
828
+ <script>
829
+ (function(){
830
+ const canvas = document.getElementById('plexus-bg');
831
+ const ctx = canvas.getContext('2d');
832
+
833
+ const NODE_COLOR = 'rgba(0,255,65,';
834
+ const LINE_COLOR = 'rgba(0,255,65,';
835
+
836
+ const MAX_DIST = 160;
837
+ const MOUSE_DIST = 220;
838
+ const ATTRACT_FORCE = 0.012;
839
+ const NODE_COUNT = 110;
840
+ const SPEED = 0.4;
841
+
842
+ let W, H, nodes = [];
843
+ let mouse = { x: -9999, y: -9999 };
844
+
845
+ window.addEventListener('mousemove', e => {
846
+ mouse.x = e.clientX;
847
+ mouse.y = e.clientY;
848
+ });
849
+ window.addEventListener('mouseleave', () => {
850
+ mouse.x = -9999;
851
+ mouse.y = -9999;
852
+ });
853
+
854
+ function resize() {
855
+ W = canvas.width = window.innerWidth;
856
+ H = canvas.height = window.innerHeight;
857
+ }
858
+
859
+ function Node() {
860
+ this.x = Math.random() * W;
861
+ this.y = Math.random() * H;
862
+ this.vx = (Math.random() - 0.5) * SPEED;
863
+ this.vy = (Math.random() - 0.5) * SPEED;
864
+ this.r = Math.random() * 2 + 1.5;
865
+ }
866
+
867
+ function init() {
868
+ nodes = [];
869
+ for (let i = 0; i < NODE_COUNT; i++) nodes.push(new Node());
870
+ }
871
+
872
+ function draw() {
873
+ ctx.clearRect(0, 0, W, H);
874
+
875
+ for (const n of nodes) {
876
+ const dxM = mouse.x - n.x;
877
+ const dyM = mouse.y - n.y;
878
+ const dM = Math.sqrt(dxM * dxM + dyM * dyM);
879
+
880
+ if (dM < MOUSE_DIST && dM > 0) {
881
+ const force = (1 - dM / MOUSE_DIST) * ATTRACT_FORCE;
882
+ n.vx += dxM / dM * force;
883
+ n.vy += dyM / dM * force;
884
+ }
885
+
886
+ const spd = Math.sqrt(n.vx * n.vx + n.vy * n.vy);
887
+ const maxSpd = SPEED * 3;
888
+ if (spd > maxSpd) {
889
+ n.vx = (n.vx / spd) * maxSpd;
890
+ n.vy = (n.vy / spd) * maxSpd;
891
+ }
892
+
893
+ if (dM >= MOUSE_DIST) {
894
+ n.vx *= 0.996;
895
+ n.vy *= 0.996;
896
+ const spdNow = Math.sqrt(n.vx * n.vx + n.vy * n.vy);
897
+ if (spdNow < SPEED * 0.3) {
898
+ n.vx += (Math.random() - 0.5) * 0.06;
899
+ n.vy += (Math.random() - 0.5) * 0.06;
900
+ }
901
+ }
902
+
903
+ n.x += n.vx;
904
+ n.y += n.vy;
905
+ if (n.x < 0 || n.x > W) n.vx *= -1;
906
+ if (n.y < 0 || n.y > H) n.vy *= -1;
907
+ }
908
+
909
+ // Edges between nodes
910
+ for (let i = 0; i < nodes.length; i++) {
911
+ for (let j = i + 1; j < nodes.length; j++) {
912
+ const a = nodes[i], b = nodes[j];
913
+ const dx = a.x - b.x, dy = a.y - b.y;
914
+ const d = Math.sqrt(dx * dx + dy * dy);
915
+ if (d < MAX_DIST) {
916
+ const alpha = (1 - d / MAX_DIST) * 0.28;
917
+ ctx.beginPath();
918
+ ctx.moveTo(a.x, a.y);
919
+ ctx.lineTo(b.x, b.y);
920
+ ctx.strokeStyle = LINE_COLOR + alpha + ')';
921
+ ctx.lineWidth = 0.8;
922
+ ctx.stroke();
923
+ }
924
+ }
925
+ }
926
+
927
+ // Lines from cursor to nearby nodes
928
+ for (const n of nodes) {
929
+ const dxM = mouse.x - n.x;
930
+ const dyM = mouse.y - n.y;
931
+ const dM = Math.sqrt(dxM * dxM + dyM * dyM);
932
+ if (dM < MOUSE_DIST) {
933
+ const alpha = (1 - dM / MOUSE_DIST) * 0.35;
934
+ ctx.beginPath();
935
+ ctx.moveTo(mouse.x, mouse.y);
936
+ ctx.lineTo(n.x, n.y);
937
+ ctx.strokeStyle = LINE_COLOR + alpha + ')';
938
+ ctx.lineWidth = 0.5;
939
+ ctx.stroke();
940
+ }
941
+ }
942
+
943
+ // Cursor glow dot
944
+ if (mouse.x > 0) {
945
+ const grdC = ctx.createRadialGradient(mouse.x, mouse.y, 0, mouse.x, mouse.y, 20);
946
+ grdC.addColorStop(0, 'rgba(0,255,65,0.5)');
947
+ grdC.addColorStop(1, 'rgba(0,255,65,0)');
948
+ ctx.beginPath();
949
+ ctx.arc(mouse.x, mouse.y, 20, 0, Math.PI * 2);
950
+ ctx.fillStyle = grdC;
951
+ ctx.fill();
952
+
953
+ ctx.beginPath();
954
+ ctx.arc(mouse.x, mouse.y, 2, 0, Math.PI * 2);
955
+ ctx.fillStyle = 'rgba(0,255,65,0.95)';
956
+ ctx.fill();
957
+ }
958
+
959
+ // Nodes with glow
960
+ for (const n of nodes) {
961
+ const grd = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, n.r * 5);
962
+ grd.addColorStop(0, NODE_COLOR + '0.3)');
963
+ grd.addColorStop(1, NODE_COLOR + '0)');
964
+ ctx.beginPath();
965
+ ctx.arc(n.x, n.y, n.r * 5, 0, Math.PI * 2);
966
+ ctx.fillStyle = grd;
967
+ ctx.fill();
968
+
969
+ ctx.beginPath();
970
+ ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2);
971
+ ctx.fillStyle = NODE_COLOR + '0.85)';
972
+ ctx.fill();
973
+ }
974
+
975
+ requestAnimationFrame(draw);
976
+ }
977
+
978
+ resize();
979
+ init();
980
+ draw();
981
+ window.addEventListener('resize', () => { resize(); init(); });
982
+ })();
983
+ </script>
984
  </body>
985
  </html>
templates/index_old.html ADDED
@@ -0,0 +1,1068 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8"/>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>SCENEIQ β€” Intel Scene Classifier</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@400;500;600;700&family=Exo+2:wght@300;400;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ /* ══════════════════════════════════════════════════════════════════
11
+ TOKENS
12
+ ══════════════════════════════════════════════════════════════════ */
13
+ :root {
14
+ --g0: #000000;
15
+ --g1: #050d05;
16
+ --g2: #0a150a;
17
+ --g3: #0f1f0f;
18
+ --g4: #1a2e1a;
19
+ --green: #00ff41;
20
+ --green2: #00cc33;
21
+ --green3: #008f1f;
22
+ --green-dim:#004d11;
23
+ --green-glow: rgba(0,255,65,0.15);
24
+ --border: rgba(0,255,65,0.18);
25
+ --border2: rgba(0,255,65,0.35);
26
+ --muted: rgba(0,255,65,0.45);
27
+ --ff-mono: 'Share Tech Mono', monospace;
28
+ --ff-head: 'Rajdhani', sans-serif;
29
+ --ff-body: 'Exo 2', sans-serif;
30
+ --r: 4px;
31
+
32
+ /*new add*/
33
+ --bg-main: #160a2f;
34
+ --bg-deep: #0e061f;
35
+ --pink: #ff4fa3;
36
+ --pink-soft: rgba(255, 79, 163, 0.18);
37
+ --pink-line: rgba(255, 79, 163, 0.28);
38
+ --white-soft: rgba(255,255,255,0.06);
39
+ }
40
+
41
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
42
+ html { font-size: 16px; scroll-behavior: smooth; }
43
+
44
+ body {
45
+ background: var(--g0);
46
+ color: var(--green);
47
+ font-family: var(--ff-body);
48
+ min-height: 100vh;
49
+ overflow-x: hidden;
50
+ cursor: default;
51
+
52
+ /*New add 2*/
53
+ #brain-bg {
54
+ position: fixed;
55
+ inset: 0;
56
+ width: 100%;
57
+ height: 100%;
58
+ z-index: 0;
59
+ pointer-events: none;
60
+ opacity: 0.42;
61
+ }
62
+
63
+ .bg-vignette {
64
+ position: fixed;
65
+ inset: 0;
66
+ z-index: 0;
67
+ pointer-events: none;
68
+ background:
69
+ radial-gradient(circle at 20% 20%, rgba(0,255,65,0.07), transparent 30%),
70
+ radial-gradient(circle at 80% 25%, rgba(0,255,65,0.05), transparent 28%),
71
+ radial-gradient(circle at 50% 80%, rgba(0,255,65,0.04), transparent 35%),
72
+ linear-gradient(180deg, rgba(0,0,0,0.12), rgba(0,0,0,0.35));
73
+ }
74
+
75
+ .bg-orb {
76
+ position: fixed;
77
+ border-radius: 50%;
78
+ filter: blur(70px);
79
+ pointer-events: none;
80
+ z-index: 0;
81
+ opacity: 0.12;
82
+ animation: floatOrb 16s ease-in-out infinite;
83
+ }
84
+
85
+ .orb-1 {
86
+ width: 260px;
87
+ height: 260px;
88
+ top: 8%;
89
+ left: 6%;
90
+ background: rgba(0,255,65,0.35);
91
+ }
92
+
93
+ .orb-2 {
94
+ width: 320px;
95
+ height: 320px;
96
+ top: 52%;
97
+ right: 8%;
98
+ background: rgba(0,204,51,0.20);
99
+ animation-delay: -5s;
100
+ }
101
+
102
+ .orb-3 {
103
+ width: 220px;
104
+ height: 220px;
105
+ bottom: 4%;
106
+ left: 34%;
107
+ background: rgba(0,143,31,0.24);
108
+ animation-delay: -10s;
109
+ }
110
+
111
+ @keyframes floatOrb {
112
+ 0%, 100% {
113
+ transform: translate3d(0, 0, 0) scale(1);
114
+ }
115
+ 25% {
116
+ transform: translate3d(18px, -24px, 0) scale(1.04);
117
+ }
118
+ 50% {
119
+ transform: translate3d(-12px, 16px, 0) scale(0.96);
120
+ }
121
+ 75% {
122
+ transform: translate3d(22px, 8px, 0) scale(1.03);
123
+ }
124
+ }
125
+
126
+ /* rendre le contenu plus premium */
127
+ .wrapper {
128
+ position: relative;
129
+ z-index: 2;
130
+ }
131
+
132
+ .panel {
133
+ background: linear-gradient(
134
+ 180deg,
135
+ rgba(10,21,10,0.82),
136
+ rgba(5,13,5,0.86)
137
+ );
138
+ backdrop-filter: blur(8px);
139
+ -webkit-backdrop-filter: blur(8px);
140
+ border: 1px solid rgba(0,255,65,0.14);
141
+ box-shadow:
142
+ 0 10px 40px rgba(0,0,0,0.35),
143
+ inset 0 1px 0 rgba(0,255,65,0.04);
144
+ }
145
+
146
+ header {
147
+ backdrop-filter: blur(6px);
148
+ -webkit-backdrop-filter: blur(6px);
149
+ }
150
+
151
+ .hero-title {
152
+ max-width: 720px;
153
+ }
154
+
155
+ .hero-sub {
156
+ max-width: 760px;
157
+ line-height: 1.7;
158
+ }
159
+
160
+ /* petite amΓ©lioration sur les cartes */
161
+ .model-card,
162
+ .drop-zone,
163
+ .url-field,
164
+ .input-tabs,
165
+ .classes-strip,
166
+ footer {
167
+ backdrop-filter: blur(4px);
168
+ -webkit-backdrop-filter: blur(4px);
169
+ }
170
+
171
+ /* responsive */
172
+ @media (max-width: 740px) {
173
+ #brain-bg {
174
+ opacity: 0.28;
175
+ }
176
+
177
+ .bg-orb {
178
+ filter: blur(55px);
179
+ }
180
+ }
181
+ }
182
+
183
+ /* ── Grid overlay ── */
184
+ body::before {
185
+ content: '';
186
+ position: fixed; inset: 0; z-index: 0; pointer-events: none;
187
+ background-image:
188
+ linear-gradient(rgba(0,255,65,0.03) 1px, transparent 1px),
189
+ linear-gradient(90deg, rgba(0,255,65,0.03) 1px, transparent 1px);
190
+ background-size: 40px 40px;
191
+ }
192
+ /* Scanline overlay */
193
+ body::after {
194
+ content: '';
195
+ position: fixed; inset: 0; z-index: 0; pointer-events: none;
196
+ background: repeating-linear-gradient(
197
+ 0deg,
198
+ transparent,
199
+ transparent 2px,
200
+ rgba(0,0,0,0.07) 2px,
201
+ rgba(0,0,0,0.07) 4px
202
+ );
203
+ }
204
+
205
+ /* ══════════════════════════════════════════════════════════════════
206
+ LAYOUT
207
+ ══════════════════════════════════════════════════════════════════ */
208
+ .wrapper {
209
+ position: relative; z-index: 1;
210
+ max-width: 1140px;
211
+ margin: 0 auto;
212
+ padding: 0 2rem;
213
+ }
214
+
215
+ /* ── HEADER ── */
216
+ header {
217
+ border-bottom: 1px solid var(--border);
218
+ padding: 1.4rem 0;
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: space-between;
222
+ animation: fadeIn .6s ease both;
223
+ }
224
+ .logo {
225
+ display: flex; align-items: center; gap: 1rem;
226
+ }
227
+ .logo-icon {
228
+ width: 38px; height: 38px;
229
+ border: 1.5px solid var(--green);
230
+ display: grid; place-items: center;
231
+ font-size: 1.1rem;
232
+ box-shadow: 0 0 14px var(--green-glow), inset 0 0 8px var(--green-glow);
233
+ animation: pulse-box 3s ease-in-out infinite;
234
+ }
235
+ @keyframes pulse-box {
236
+ 0%,100% { box-shadow: 0 0 10px var(--green-glow), inset 0 0 6px var(--green-glow); }
237
+ 50% { box-shadow: 0 0 22px rgba(0,255,65,.3), inset 0 0 14px rgba(0,255,65,.2); }
238
+ }
239
+ .logo-text {
240
+ font-family: var(--ff-head);
241
+ font-size: 1.8rem; font-weight: 700;
242
+ letter-spacing: .15em;
243
+ text-shadow: 0 0 20px rgba(0,255,65,.6);
244
+ }
245
+ .logo-text span { color: var(--green3); }
246
+ .header-right {
247
+ display: flex; align-items: center; gap: 1.5rem;
248
+ }
249
+ .status-dot {
250
+ display: flex; align-items: center; gap: .45rem;
251
+ font-family: var(--ff-mono); font-size: .65rem; color: var(--muted);
252
+ }
253
+ .dot {
254
+ width: 6px; height: 6px; border-radius: 50%;
255
+ background: var(--green);
256
+ box-shadow: 0 0 8px var(--green);
257
+ animation: blink 2s step-end infinite;
258
+ }
259
+ @keyframes blink { 0%,100%{opacity:1} 50%{opacity:.2} }
260
+ .version {
261
+ font-family: var(--ff-mono); font-size: .6rem;
262
+ color: var(--green-dim); letter-spacing: .1em;
263
+ }
264
+
265
+ /* ── HERO ── */
266
+ .hero {
267
+ padding: 3rem 0 2rem;
268
+ animation: fadeIn .7s ease .1s both;
269
+ }
270
+ .hero-label {
271
+ font-family: var(--ff-mono); font-size: .62rem;
272
+ color: var(--green3); letter-spacing: .2em;
273
+ text-transform: uppercase; margin-bottom: .6rem;
274
+ }
275
+ .hero-title {
276
+ font-family: var(--ff-head);
277
+ font-size: clamp(2.2rem, 5vw, 3.4rem);
278
+ font-weight: 700; letter-spacing: -.01em;
279
+ line-height: 1.05;
280
+ text-shadow: 0 0 40px rgba(0,255,65,.3);
281
+ }
282
+ .hero-title em {
283
+ font-style: normal; color: var(--green2);
284
+ text-shadow: 0 0 30px rgba(0,204,51,.6);
285
+ }
286
+ .hero-sub {
287
+ margin-top: .8rem;
288
+ font-size: .9rem; color: var(--muted);
289
+ font-family: var(--ff-mono); letter-spacing: .04em;
290
+ }
291
+
292
+ /* ── MAIN GRID ── */
293
+ .main-grid {
294
+ display: grid;
295
+ grid-template-columns: 1fr 1fr;
296
+ gap: 1.5rem;
297
+ padding-bottom: 4rem;
298
+ animation: fadeIn .8s ease .2s both;
299
+ }
300
+
301
+ /* ── PANEL BASE ── */
302
+ .panel {
303
+ background: var(--g2);
304
+ border: 1px solid var(--border);
305
+ border-radius: var(--r);
306
+ padding: 1.75rem;
307
+ position: relative;
308
+ overflow: hidden;
309
+ }
310
+ .panel::before {
311
+ content: '';
312
+ position: absolute; top: 0; left: 0; right: 0;
313
+ height: 2px;
314
+ background: linear-gradient(90deg, transparent, var(--green3), transparent);
315
+ }
316
+ .panel-title {
317
+ font-family: var(--ff-mono); font-size: .6rem;
318
+ letter-spacing: .2em; color: var(--green3);
319
+ text-transform: uppercase; margin-bottom: 1.4rem;
320
+ display: flex; align-items: center; gap: .6rem;
321
+ }
322
+ .panel-title::after {
323
+ content: ''; flex: 1; height: 1px; background: var(--border);
324
+ }
325
+
326
+ /* ── MODEL SELECTOR ── */
327
+ .model-grid {
328
+ display: grid; grid-template-columns: 1fr 1fr; gap: .75rem;
329
+ margin-bottom: 1.5rem;
330
+ }
331
+ .model-card {
332
+ border: 1px solid var(--border);
333
+ border-radius: var(--r);
334
+ padding: 1rem .9rem;
335
+ cursor: pointer;
336
+ transition: all .2s;
337
+ background: var(--g1);
338
+ display: flex; flex-direction: column; gap: .3rem;
339
+ }
340
+ .model-card:hover { border-color: var(--green3); background: var(--g3); }
341
+ .model-card.active {
342
+ border-color: var(--green);
343
+ background: rgba(0,255,65,.06);
344
+ box-shadow: 0 0 16px rgba(0,255,65,.12), inset 0 0 12px rgba(0,255,65,.04);
345
+ }
346
+ .model-card.active .mc-name { color: var(--green); }
347
+ .mc-icon { font-size: 1.2rem; }
348
+ .mc-name {
349
+ font-family: var(--ff-head); font-weight: 600; font-size: 1rem;
350
+ color: var(--green2); transition: color .2s;
351
+ }
352
+ .mc-sub { font-family: var(--ff-mono); font-size: .58rem; color: var(--muted); }
353
+
354
+ /* ── INPUT TABS ── */
355
+ .input-tabs {
356
+ display: flex; gap: 0; margin-bottom: 1rem;
357
+ border: 1px solid var(--border); border-radius: var(--r); overflow: hidden;
358
+ }
359
+ .tab-btn {
360
+ flex: 1; padding: .55rem; background: var(--g1);
361
+ border: none; color: var(--muted); font-family: var(--ff-mono);
362
+ font-size: .65rem; letter-spacing: .1em; cursor: pointer;
363
+ transition: all .2s; text-transform: uppercase;
364
+ }
365
+ .tab-btn:hover { background: var(--g3); color: var(--green2); }
366
+ .tab-btn.active {
367
+ background: rgba(0,255,65,.08); color: var(--green);
368
+ box-shadow: inset 0 -2px 0 var(--green);
369
+ }
370
+
371
+ /* ── DROP ZONE ── */
372
+ .drop-zone {
373
+ border: 1.5px dashed var(--border2);
374
+ border-radius: var(--r);
375
+ min-height: 190px;
376
+ display: flex; flex-direction: column;
377
+ align-items: center; justify-content: center;
378
+ gap: .75rem; cursor: pointer;
379
+ transition: all .25s; background: var(--g1);
380
+ position: relative; overflow: hidden;
381
+ }
382
+ .drop-zone:hover, .drop-zone.drag {
383
+ border-color: var(--green);
384
+ background: rgba(0,255,65,.04);
385
+ box-shadow: 0 0 20px rgba(0,255,65,.08);
386
+ }
387
+ .drop-zone.has-img .dz-ph { display: none; }
388
+ #preview-img {
389
+ position: absolute; inset: 0;
390
+ width: 100%; height: 100%; object-fit: cover;
391
+ display: none; border-radius: calc(var(--r) - 1px);
392
+ opacity: .85;
393
+ }
394
+ .drop-zone.has-img #preview-img { display: block; }
395
+ .dz-corner {
396
+ position: absolute; width: 14px; height: 14px;
397
+ border-color: var(--green3); border-style: solid;
398
+ }
399
+ .dz-corner.tl { top: 8px; left: 8px; border-width: 1.5px 0 0 1.5px; }
400
+ .dz-corner.tr { top: 8px; right: 8px; border-width: 1.5px 1.5px 0 0; }
401
+ .dz-corner.bl { bottom: 8px; left: 8px; border-width: 0 0 1.5px 1.5px; }
402
+ .dz-corner.br { bottom: 8px; right: 8px; border-width: 0 1.5px 1.5px 0; }
403
+ .dz-ph {
404
+ display: flex; flex-direction: column; align-items: center; gap: .6rem;
405
+ pointer-events: none;
406
+ }
407
+ .dz-icon {
408
+ width: 48px; height: 48px; border: 1px solid var(--border2);
409
+ border-radius: 50%; display: grid; place-items: center;
410
+ font-size: 1.3rem; color: var(--green3);
411
+ }
412
+ .dz-ph p { font-family: var(--ff-mono); font-size: .72rem; color: var(--muted); }
413
+ .dz-ph em { font-family: var(--ff-mono); font-size: .6rem; color: var(--green-dim); font-style: normal; }
414
+ #file-input { display: none; }
415
+
416
+ /* ── URL INPUT ── */
417
+ .url-input-wrap { display: none; flex-direction: column; gap: .6rem; }
418
+ .url-input-wrap.show { display: flex; }
419
+ .url-field {
420
+ display: flex; align-items: center;
421
+ border: 1px solid var(--border2); border-radius: var(--r);
422
+ background: var(--g1); overflow: hidden;
423
+ }
424
+ .url-prefix {
425
+ font-family: var(--ff-mono); font-size: .65rem;
426
+ color: var(--green3); padding: 0 .7rem; white-space: nowrap;
427
+ border-right: 1px solid var(--border);
428
+ }
429
+ .url-input {
430
+ flex: 1; background: transparent; border: none; outline: none;
431
+ color: var(--green); font-family: var(--ff-mono); font-size: .75rem;
432
+ padding: .75rem .8rem;
433
+ caret-color: var(--green);
434
+ }
435
+ .url-input::placeholder { color: var(--green-dim); }
436
+ .url-load-btn {
437
+ padding: .65rem .9rem;
438
+ background: rgba(0,255,65,.1); border: none;
439
+ border-left: 1px solid var(--border);
440
+ color: var(--green2); font-family: var(--ff-mono); font-size: .65rem;
441
+ cursor: pointer; transition: background .2s; white-space: nowrap;
442
+ }
443
+ .url-load-btn:hover { background: rgba(0,255,65,.2); }
444
+ .url-preview {
445
+ width: 100%; height: 140px; object-fit: cover;
446
+ border-radius: var(--r); border: 1px solid var(--border);
447
+ display: none;
448
+ }
449
+ .url-preview.show { display: block; }
450
+
451
+ /* ── CLASSIFY BTN ── */
452
+ .classify-btn {
453
+ width: 100%; margin-top: 1rem;
454
+ padding: 1rem;
455
+ background: linear-gradient(135deg, var(--green3), var(--green-dim));
456
+ border: 1px solid var(--green3); border-radius: var(--r);
457
+ color: var(--green); font-family: var(--ff-head);
458
+ font-size: 1.1rem; font-weight: 700; letter-spacing: .1em;
459
+ cursor: pointer; position: relative; overflow: hidden;
460
+ transition: all .2s; text-transform: uppercase;
461
+ }
462
+ .classify-btn::before {
463
+ content: '';
464
+ position: absolute; top: -2px; left: -100%; width: 60%; height: calc(100% + 4px);
465
+ background: linear-gradient(90deg, transparent, rgba(0,255,65,.18), transparent);
466
+ transform: skewX(-15deg);
467
+ }
468
+ .classify-btn.loading::before {
469
+ animation: sweep 1.2s linear infinite;
470
+ }
471
+ @keyframes sweep { to { left: 150%; } }
472
+ .classify-btn:not(:disabled):hover {
473
+ background: linear-gradient(135deg, var(--green2), var(--green3));
474
+ box-shadow: 0 0 24px rgba(0,255,65,.3);
475
+ transform: translateY(-1px);
476
+ }
477
+ .classify-btn:disabled { opacity: .35; cursor: default; }
478
+
479
+ /* ══════════════════════════════════════════════════════════════════
480
+ RIGHT PANEL β€” RESULTS
481
+ ══════════════════════════════════════════════════════════════════ */
482
+
483
+ /* Waiting state */
484
+ .result-waiting {
485
+ display: flex; flex-direction: column;
486
+ align-items: flex-start; justify-content: center;
487
+ min-height: 200px; gap: .6rem;
488
+ padding: 1rem 0;
489
+ }
490
+ .result-waiting .rw-title {
491
+ font-family: var(--ff-head); font-size: 1rem; font-weight: 600;
492
+ color: var(--green3);
493
+ }
494
+ .result-waiting .rw-sub {
495
+ font-family: var(--ff-mono); font-size: .7rem;
496
+ color: var(--green-dim); line-height: 1.7;
497
+ }
498
+ .terminal-cursor {
499
+ display: inline-block; width: 8px; height: 14px;
500
+ background: var(--green); margin-left: 2px;
501
+ animation: blink-c .8s step-end infinite;
502
+ vertical-align: middle;
503
+ }
504
+ @keyframes blink-c { 0%,100%{opacity:1} 50%{opacity:0} }
505
+
506
+ /* Result */
507
+ .result-content { display: none; flex-direction: column; gap: 1.4rem; }
508
+ .result-content.show { display: flex; animation: scanIn .4s ease; }
509
+ @keyframes scanIn {
510
+ from { opacity: 0; clip-path: inset(0 0 100% 0); }
511
+ to { opacity: 1; clip-path: inset(0 0 0% 0); }
512
+ }
513
+
514
+ .rc-header { display: flex; flex-direction: column; gap: .25rem; }
515
+ .rc-label {
516
+ font-family: var(--ff-mono); font-size: .6rem;
517
+ letter-spacing: .2em; color: var(--green3);
518
+ }
519
+ .rc-class {
520
+ font-family: var(--ff-head);
521
+ font-size: 3rem; font-weight: 700;
522
+ letter-spacing: -.01em; line-height: 1;
523
+ color: var(--green);
524
+ text-shadow: 0 0 30px rgba(0,255,65,.5);
525
+ }
526
+ .rc-conf {
527
+ font-family: var(--ff-mono); font-size: .78rem;
528
+ color: var(--muted); margin-top: .1rem;
529
+ }
530
+ .rc-conf strong { color: var(--green2); font-size: .9rem; }
531
+
532
+ /* Main confidence bar */
533
+ .conf-track {
534
+ height: 3px; background: var(--g4);
535
+ border-radius: 99px; overflow: hidden; margin-top: .5rem;
536
+ }
537
+ .conf-fill {
538
+ height: 100%;
539
+ background: linear-gradient(90deg, var(--green3), var(--green));
540
+ border-radius: 99px; width: 0%;
541
+ transition: width 1s cubic-bezier(.4,0,.2,1);
542
+ box-shadow: 0 0 8px var(--green);
543
+ }
544
+
545
+ /* Divider */
546
+ .rc-divider {
547
+ height: 1px; background: var(--border);
548
+ }
549
+
550
+ /* Probability bars */
551
+ .prob-list { display: flex; flex-direction: column; gap: .85rem; }
552
+ .prob-row { display: flex; flex-direction: column; gap: .22rem; }
553
+ .prob-meta { display: flex; justify-content: space-between; align-items: baseline; }
554
+ .prob-name {
555
+ font-family: var(--ff-body); font-size: .8rem;
556
+ font-weight: 400; color: var(--muted);
557
+ display: flex; align-items: center; gap: .4rem; text-transform: capitalize;
558
+ }
559
+ .prob-row.top .prob-name { color: var(--green); font-weight: 600; }
560
+ .prob-pct { font-family: var(--ff-mono); font-size: .68rem; color: var(--green-dim); }
561
+ .prob-row.top .prob-pct { color: var(--green2); }
562
+ .prob-track {
563
+ height: 4px; background: var(--g4);
564
+ border-radius: 99px; overflow: hidden;
565
+ }
566
+ .prob-fill {
567
+ height: 100%; background: var(--green-dim);
568
+ border-radius: 99px; width: 0%;
569
+ transition: width .8s cubic-bezier(.4,0,.2,1);
570
+ }
571
+ .prob-row.top .prob-fill {
572
+ background: linear-gradient(90deg, var(--green3), var(--green));
573
+ box-shadow: 0 0 6px rgba(0,255,65,.4);
574
+ }
575
+
576
+ /* Error */
577
+ .result-error {
578
+ display: none; padding: 1rem;
579
+ border: 1px solid rgba(255,50,50,.3);
580
+ border-radius: var(--r); background: rgba(255,0,0,.05);
581
+ font-family: var(--ff-mono); font-size: .75rem; color: #ff4444;
582
+ line-height: 1.6;
583
+ }
584
+ .result-error.show { display: block; animation: fadeIn .3s ease; }
585
+
586
+ /* ── CLASSES STRIP ── */
587
+ .classes-strip {
588
+ border-top: 1px solid var(--border);
589
+ padding: 1rem 0;
590
+ display: flex; align-items: center; gap: 1.2rem;
591
+ flex-wrap: wrap;
592
+ animation: fadeIn .9s ease .3s both;
593
+ }
594
+ .cs-label { font-family: var(--ff-mono); font-size: .55rem; color: var(--green-dim); letter-spacing: .15em; }
595
+ .cs-pills { display: flex; gap: .5rem; flex-wrap: wrap; }
596
+ .cs-pill {
597
+ font-family: var(--ff-mono); font-size: .6rem;
598
+ padding: .25rem .65rem;
599
+ border: 1px solid var(--border); border-radius: 2px;
600
+ color: var(--green-dim);
601
+ transition: all .2s; cursor: default;
602
+ }
603
+ .cs-pill:hover { border-color: var(--green3); color: var(--green2); }
604
+
605
+ /* ── FOOTER ── */
606
+ footer {
607
+ border-top: 1px solid var(--border);
608
+ padding: 1rem 0;
609
+ display: flex; justify-content: space-between; align-items: center;
610
+ animation: fadeIn 1s ease .4s both;
611
+ }
612
+ footer p { font-family: var(--ff-mono); font-size: .6rem; color: var(--green-dim); }
613
+
614
+ /* ── ANIMATIONS ── */
615
+ @keyframes fadeIn { from{opacity:0; transform:translateY(8px)} to{opacity:1;transform:none} }
616
+
617
+ /* ── RESPONSIVE ── */
618
+ @media (max-width: 740px) {
619
+ .main-grid { grid-template-columns: 1fr; }
620
+ .hero-title { font-size: 2rem; }
621
+ .model-grid { grid-template-columns: 1fr 1fr; }
622
+ }
623
+ </style>
624
+ </head>
625
+ <body>
626
+
627
+
628
+ <!-- New add end -->
629
+ <canvas id="brain-bg"></canvas>
630
+ <div class="bg-vignette"></div>
631
+ <div class="bg-orb orb-1"></div>
632
+ <div class="bg-orb orb-2"></div>
633
+ <div class="bg-orb orb-3"></div>
634
+
635
+
636
+ <canvas id="network-bg"></canvas>
637
+ <div class="bg-overlay"></div>
638
+ <!-- New add-->
639
+ <script>
640
+ const brainCanvas = document.getElementById("brain-bg");
641
+ const brainCtx = brainCanvas.getContext("2d");
642
+
643
+ let brainW = 0;
644
+ let brainH = 0;
645
+ let brainNodes = [];
646
+ let mouse = { x: null, y: null, radius: 120 };
647
+
648
+ function resizeBrainCanvas() {
649
+ brainW = brainCanvas.width = window.innerWidth;
650
+ brainH = brainCanvas.height = window.innerHeight;
651
+ createBrainNodes();
652
+ }
653
+
654
+ function createBrainNodes() {
655
+ const count = Math.max(36, Math.floor((brainW * brainH) / 38000));
656
+ brainNodes = [];
657
+
658
+ for (let i = 0; i < count; i++) {
659
+ brainNodes.push({
660
+ x: Math.random() * brainW,
661
+ y: Math.random() * brainH,
662
+ vx: (Math.random() - 0.5) * 0.45,
663
+ vy: (Math.random() - 0.5) * 0.45,
664
+ r: Math.random() * 2.2 + 1.2,
665
+ glow: Math.random() * 0.6 + 0.4
666
+ });
667
+ }
668
+ }
669
+
670
+ function drawBrainBackground() {
671
+ brainCtx.clearRect(0, 0, brainW, brainH);
672
+
673
+ for (let i = 0; i < brainNodes.length; i++) {
674
+ const a = brainNodes[i];
675
+
676
+ a.x += a.vx;
677
+ a.y += a.vy;
678
+
679
+ if (a.x < 0 || a.x > brainW) a.vx *= -1;
680
+ if (a.y < 0 || a.y > brainH) a.vy *= -1;
681
+
682
+ for (let j = i + 1; j < brainNodes.length; j++) {
683
+ const b = brainNodes[j];
684
+ const dx = a.x - b.x;
685
+ const dy = a.y - b.y;
686
+ const dist = Math.sqrt(dx * dx + dy * dy);
687
+
688
+ if (dist < 120) {
689
+ const alpha = (1 - dist / 120) * 0.18;
690
+ brainCtx.beginPath();
691
+ brainCtx.moveTo(a.x, a.y);
692
+ brainCtx.lineTo(b.x, b.y);
693
+ brainCtx.strokeStyle = `rgba(0,255,65,${alpha})`;
694
+ brainCtx.lineWidth = 1;
695
+ brainCtx.stroke();
696
+ }
697
+ }
698
+
699
+ if (mouse.x !== null && mouse.y !== null) {
700
+ const mdx = a.x - mouse.x;
701
+ const mdy = a.y - mouse.y;
702
+ const mdist = Math.sqrt(mdx * mdx + mdy * mdy);
703
+
704
+ if (mdist < mouse.radius) {
705
+ brainCtx.beginPath();
706
+ brainCtx.moveTo(a.x, a.y);
707
+ brainCtx.lineTo(mouse.x, mouse.y);
708
+ brainCtx.strokeStyle = `rgba(0,255,65,${(1 - mdist / mouse.radius) * 0.22})`;
709
+ brainCtx.lineWidth = 1;
710
+ brainCtx.stroke();
711
+ }
712
+ }
713
+
714
+ const gradient = brainCtx.createRadialGradient(a.x, a.y, 0, a.x, a.y, a.r * 8);
715
+ gradient.addColorStop(0, `rgba(0,255,65,${0.95 * a.glow})`);
716
+ gradient.addColorStop(0.35, `rgba(0,255,65,${0.22 * a.glow})`);
717
+ gradient.addColorStop(1, "rgba(0,255,65,0)");
718
+
719
+ brainCtx.beginPath();
720
+ brainCtx.fillStyle = gradient;
721
+ brainCtx.arc(a.x, a.y, a.r * 8, 0, Math.PI * 2);
722
+ brainCtx.fill();
723
+
724
+ brainCtx.beginPath();
725
+ brainCtx.fillStyle = "rgba(170,255,195,0.9)";
726
+ brainCtx.arc(a.x, a.y, a.r, 0, Math.PI * 2);
727
+ brainCtx.fill();
728
+ }
729
+
730
+ requestAnimationFrame(drawBrainBackground);
731
+ }
732
+
733
+ window.addEventListener("resize", resizeBrainCanvas);
734
+
735
+ window.addEventListener("mousemove", (e) => {
736
+ mouse.x = e.clientX;
737
+ mouse.y = e.clientY;
738
+ });
739
+
740
+ window.addEventListener("mouseleave", () => {
741
+ mouse.x = null;
742
+ mouse.y = null;
743
+ });
744
+
745
+ resizeBrainCanvas();
746
+ drawBrainBackground();
747
+ </script>
748
+
749
+
750
+ <div class="wrapper">
751
+
752
+ <!-- HEADER -->
753
+ <header>
754
+ <div class="logo">
755
+ <div class="logo-icon">⬑</div>
756
+ <div class="logo-text">SCENE<span>IQ</span></div>
757
+ </div>
758
+ <div class="header-right">
759
+ <div class="status-dot"><div class="dot"></div> SYSTEM ONLINE</div>
760
+ <div class="version">v4.0 Β· PARFAIT</div>
761
+ </div>
762
+ </header>
763
+
764
+ <!-- HERO -->
765
+ <div class="hero">
766
+ <div class="hero-label">// Intel Image Classification Β· CNN</div>
767
+ <h1 class="hero-title">Scene Recognition<br><em>Powered by Neural Networks</em></h1>
768
+ <p class="hero-sub">Upload an image or provide a URL β€” select your model β€” get instant predictions across 6 scene categories.</p>
769
+ </div>
770
+
771
+ <!-- MAIN GRID -->
772
+ <div class="main-grid">
773
+
774
+ <!-- LEFT : inputs -->
775
+ <div class="panel">
776
+ <div class="panel-title">01 // MODEL_SELECT</div>
777
+
778
+ <div class="model-grid">
779
+ <div class="model-card active" data-fw="pytorch" onclick="selectModel(this)">
780
+ <span class="mc-icon">⚑</span>
781
+ <span class="mc-name">PyTorch</span>
782
+ <span class="mc-sub">CNN_Torch Β· .pth</span>
783
+ </div>
784
+ <div class="model-card" data-fw="tensorflow" onclick="selectModel(this)">
785
+ <span class="mc-icon">🧠</span>
786
+ <span class="mc-name">TensorFlow</span>
787
+ <span class="mc-sub">CNN_TF Β· .keras</span>
788
+ </div>
789
+ </div>
790
+
791
+ <div class="panel-title">02 // INPUT_IMAGE</div>
792
+
793
+ <!-- Tabs: File / URL -->
794
+ <div class="input-tabs">
795
+ <button class="tab-btn active" onclick="switchTab('file', this)">↑ FILE_UPLOAD</button>
796
+ <button class="tab-btn" onclick="switchTab('url', this)">⬑ IMAGE_URL</button>
797
+ </div>
798
+
799
+ <!-- File drop zone -->
800
+ <div id="tab-file">
801
+ <div class="drop-zone" id="drop-zone" onclick="document.getElementById('file-input').click()">
802
+ <div class="dz-corner tl"></div>
803
+ <div class="dz-corner tr"></div>
804
+ <div class="dz-corner bl"></div>
805
+ <div class="dz-corner br"></div>
806
+ <img id="preview-img" src="" alt="preview"/>
807
+ <div class="dz-ph">
808
+ <div class="dz-icon">↑</div>
809
+ <p>Drop image here or click</p>
810
+ <em>JPG Β· PNG Β· WEBP Β· GIF</em>
811
+ </div>
812
+ </div>
813
+ <input type="file" id="file-input" accept="image/*"/>
814
+ </div>
815
+
816
+ <!-- URL input -->
817
+ <div id="tab-url" class="url-input-wrap">
818
+ <div class="url-field">
819
+ <span class="url-prefix">URL://</span>
820
+ <input type="text" class="url-input" id="url-input"
821
+ placeholder="https://example.com/image.jpg"/>
822
+ <button class="url-load-btn" onclick="loadFromUrl()">LOAD</button>
823
+ </div>
824
+ <img id="url-preview" class="url-preview" src="" alt="URL preview"/>
825
+ </div>
826
+
827
+ <div class="panel-title" style="margin-top:1.2rem;">03 // ANALYZE</div>
828
+ <button class="classify-btn" id="classify-btn" disabled onclick="classify()">
829
+ RUN CLASSIFICATION β†’
830
+ </button>
831
+ </div>
832
+
833
+ <!-- RIGHT : results -->
834
+ <div class="panel">
835
+ <div class="panel-title">04 // SCAN_RESULTS</div>
836
+
837
+ <!-- Waiting -->
838
+ <div class="result-waiting" id="result-waiting">
839
+ <div class="rw-title">AWAITING INPUT</div>
840
+ <div class="rw-sub">
841
+ &gt; select_model()<br>
842
+ &gt; load_image() <span class="terminal-cursor"></span><br>
843
+ &gt; predict()
844
+ </div>
845
+ </div>
846
+
847
+ <!-- Results -->
848
+ <div class="result-content" id="result-content">
849
+ <div class="rc-header">
850
+ <div class="rc-label">// PREDICTED_CLASS</div>
851
+ <div class="rc-class" id="rc-class">β€”</div>
852
+ <div class="rc-conf">confidence : <strong id="rc-conf">β€”</strong></div>
853
+ <div class="conf-track"><div class="conf-fill" id="conf-fill"></div></div>
854
+ </div>
855
+ <div class="rc-divider"></div>
856
+ <div>
857
+ <div class="panel-title" style="margin-bottom:.9rem;">// CLASS_SCORES</div>
858
+ <div class="prob-list" id="prob-list"></div>
859
+ </div>
860
+ </div>
861
+
862
+ <!-- Error -->
863
+ <div class="result-error" id="result-error"></div>
864
+ </div>
865
+
866
+ </div>
867
+
868
+ <!-- CLASSES STRIP -->
869
+ <div class="classes-strip">
870
+ <span class="cs-label">CLASSES</span>
871
+ <div class="cs-pills">
872
+ <span class="cs-pill">πŸ™ buildings</span>
873
+ <span class="cs-pill">🌲 forest</span>
874
+ <span class="cs-pill">🧊 glacier</span>
875
+ <span class="cs-pill">β›° mountain</span>
876
+ <span class="cs-pill">🌊 sea</span>
877
+ <span class="cs-pill">πŸ›£ street</span>
878
+ </div>
879
+ </div>
880
+
881
+ <footer>
882
+ <p>SCENEIQ Β· Intel Image Classification Β· CNN PyTorch &amp; TensorFlow</p>
883
+ <p>by PARFAIT Β· seed=42 Β· reproducible</p>
884
+ </footer>
885
+
886
+ </div><!-- /wrapper -->
887
+
888
+ <script>
889
+ const EMOJIS = {buildings:"πŸ™",forest:"🌲",glacier:"🧊",mountain:"β›°",sea:"🌊",street:"πŸ›£"};
890
+ const CLASSES = ["buildings","forest","glacier","mountain","sea","street"];
891
+
892
+ let selectedFw = "pytorch";
893
+ let selectedFile = null;
894
+ let urlImageReady = false;
895
+ let currentTab = "file";
896
+
897
+ /* ── Model selector ── */
898
+ function selectModel(card) {
899
+ document.querySelectorAll(".model-card").forEach(c => c.classList.remove("active"));
900
+ card.classList.add("active");
901
+ selectedFw = card.dataset.fw;
902
+ }
903
+
904
+ /* ── Tab switcher ── */
905
+ function switchTab(tab, btn) {
906
+ currentTab = tab;
907
+ document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active"));
908
+ btn.classList.add("active");
909
+ document.getElementById("tab-file").style.display = tab === "file" ? "block" : "none";
910
+ const urlWrap = document.getElementById("tab-url");
911
+ urlWrap.classList.toggle("show", tab === "url");
912
+ updateBtn();
913
+ }
914
+
915
+ /* ── File drag & drop ── */
916
+ const dropZone = document.getElementById("drop-zone");
917
+ const fileInput = document.getElementById("file-input");
918
+
919
+ fileInput.addEventListener("change", () => {
920
+ if (fileInput.files[0]) loadFile(fileInput.files[0]);
921
+ });
922
+ dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("drag"); });
923
+ dropZone.addEventListener("dragleave", () => dropZone.classList.remove("drag"));
924
+ dropZone.addEventListener("drop", e => {
925
+ e.preventDefault(); dropZone.classList.remove("drag");
926
+ const f = e.dataTransfer.files[0];
927
+ if (f && f.type.startsWith("image/")) loadFile(f);
928
+ });
929
+
930
+ function loadFile(file) {
931
+ selectedFile = file;
932
+ const reader = new FileReader();
933
+ reader.onload = e => {
934
+ const img = document.getElementById("preview-img");
935
+ img.src = e.target.result;
936
+ dropZone.classList.add("has-img");
937
+ };
938
+ reader.readAsDataURL(file);
939
+ resetResult();
940
+ updateBtn();
941
+ }
942
+
943
+ /* ── URL load ── */
944
+ function loadFromUrl() {
945
+ const url = document.getElementById("url-input").value.trim();
946
+ if (!url) return;
947
+ const preview = document.getElementById("url-preview");
948
+ preview.onload = () => {
949
+ preview.classList.add("show");
950
+ urlImageReady = true;
951
+ resetResult();
952
+ updateBtn();
953
+ };
954
+ preview.onerror = () => {
955
+ preview.classList.remove("show");
956
+ urlImageReady = false;
957
+ updateBtn();
958
+ };
959
+ preview.src = url;
960
+ }
961
+
962
+ // Allow Enter key on URL input
963
+ document.getElementById("url-input").addEventListener("keydown", e => {
964
+ if (e.key === "Enter") loadFromUrl();
965
+ });
966
+
967
+ /* ── Update classify button ── */
968
+ function updateBtn() {
969
+ const ready = (currentTab === "file" && selectedFile) ||
970
+ (currentTab === "url" && urlImageReady);
971
+ document.getElementById("classify-btn").disabled = !ready;
972
+ }
973
+
974
+ /* ── Classify ── */
975
+ async function classify() {
976
+ const btn = document.getElementById("classify-btn");
977
+ btn.disabled = true;
978
+ btn.textContent = "SCANNING…";
979
+ btn.classList.add("loading");
980
+
981
+ const form = new FormData();
982
+ form.append("model", selectedFw);
983
+
984
+ if (currentTab === "file" && selectedFile) {
985
+ form.append("image", selectedFile);
986
+ } else {
987
+ form.append("image_url", document.getElementById("url-input").value.trim());
988
+ }
989
+
990
+ try {
991
+ const res = await fetch("/predict", { method: "POST", body: form });
992
+ const data = await res.json();
993
+ if (data.error) throw new Error(data.error);
994
+ showResult(data);
995
+ } catch(err) {
996
+ showError(err.message);
997
+ } finally {
998
+ btn.textContent = "RUN CLASSIFICATION β†’";
999
+ btn.classList.remove("loading");
1000
+ btn.disabled = false;
1001
+ }
1002
+ }
1003
+
1004
+ /* ── Show result ── */
1005
+ function showResult(data) {
1006
+ document.getElementById("result-waiting").style.display = "none";
1007
+ document.getElementById("result-error").classList.remove("show");
1008
+
1009
+ const pct = Math.round(data.confidence * 100);
1010
+ document.getElementById("rc-class").textContent =
1011
+ (EMOJIS[data.class] || "") + " " + data.class.charAt(0).toUpperCase() + data.class.slice(1);
1012
+ document.getElementById("rc-conf").textContent = pct + "%";
1013
+
1014
+ const content = document.getElementById("result-content");
1015
+ content.classList.remove("show");
1016
+ void content.offsetWidth; // force reflow for animation
1017
+ content.classList.add("show");
1018
+
1019
+ setTimeout(() => {
1020
+ document.getElementById("conf-fill").style.width = pct + "%";
1021
+ }, 60);
1022
+
1023
+ // Probability bars
1024
+ const list = document.getElementById("prob-list");
1025
+ list.innerHTML = "";
1026
+ const sorted = [...CLASSES].sort((a,b) => data.probabilities[b] - data.probabilities[a]);
1027
+
1028
+ sorted.forEach((cls, i) => {
1029
+ const p = Math.round(data.probabilities[cls] * 100);
1030
+ const top = cls === data.class;
1031
+ const row = document.createElement("div");
1032
+ row.className = "prob-row" + (top ? " top" : "");
1033
+ row.innerHTML = `
1034
+ <div class="prob-meta">
1035
+ <span class="prob-name">${EMOJIS[cls] || ""} ${cls}</span>
1036
+ <span class="prob-pct" id="pct-${cls}">0%</span>
1037
+ </div>
1038
+ <div class="prob-track">
1039
+ <div class="prob-fill" id="bar-${cls}"></div>
1040
+ </div>`;
1041
+ list.appendChild(row);
1042
+ setTimeout(() => {
1043
+ document.getElementById("bar-" + cls).style.width = p + "%";
1044
+ document.getElementById("pct-" + cls).textContent = p + "%";
1045
+ }, 100 + i * 50);
1046
+ });
1047
+ }
1048
+
1049
+ function showError(msg) {
1050
+ document.getElementById("result-waiting").style.display = "none";
1051
+ document.getElementById("result-content").classList.remove("show");
1052
+ const err = document.getElementById("result-error");
1053
+ err.textContent = "ERROR > " + msg;
1054
+ err.classList.add("show");
1055
+ }
1056
+
1057
+ function resetResult() {
1058
+ document.getElementById("result-waiting").style.display = "flex";
1059
+ document.getElementById("result-content").classList.remove("show");
1060
+ document.getElementById("result-error").classList.remove("show");
1061
+ document.getElementById("conf-fill").style.width = "0%";
1062
+ }
1063
+
1064
+ // Init
1065
+ document.getElementById("tab-file").style.display = "block";
1066
+ </script>
1067
+ </body>
1068
+ </html>