BladeSzaSza commited on
Commit
8fa76f7
·
1 Parent(s): 0892623

Revert "Cleanup: Remove unused files, Svelte frontend, old UI, and update docs for Streamlit-only architecture"

Browse files

This reverts commit 08926230de657ac0fb0ec6eba09839ca0552edb3.

Files changed (3) hide show
  1. ui/__init__.py +1 -0
  2. ui/interfaces.py +458 -0
  3. ui/themes.py +428 -0
ui/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # UI module initialization
ui/interfaces.py ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from typing import Callable, Any, Optional
3
+ import numpy as np
4
+
5
+ def create_voice_interface(pipeline: Any, game_mechanics: Any) -> gr.Column:
6
+ """Create voice-controlled interface for monster generation"""
7
+
8
+ with gr.Column() as voice_interface:
9
+ gr.Markdown("""
10
+ ### 🎙️ Voice Control Interface
11
+ Speak your monster description or use the microphone to create your digital companion!
12
+ """)
13
+
14
+ with gr.Row():
15
+ with gr.Column(scale=1):
16
+ # Voice input
17
+ voice_input = gr.Audio(
18
+ label="🎤 Voice Description",
19
+ sources=["microphone", "upload"],
20
+ type="filepath",
21
+ elem_classes=["cyber-input"],
22
+ info="Describe your monster using voice"
23
+ )
24
+
25
+ # Voice control buttons
26
+ with gr.Row():
27
+ start_recording = gr.Button(
28
+ "🔴 Start Recording",
29
+ elem_classes=["cyber-button"],
30
+ size="sm"
31
+ )
32
+ stop_recording = gr.Button(
33
+ "⏹️ Stop Recording",
34
+ elem_classes=["cyber-button"],
35
+ size="sm"
36
+ )
37
+
38
+ # Real-time transcription display
39
+ transcription_display = gr.Textbox(
40
+ label="📝 Transcription",
41
+ placeholder="Your voice will be transcribed here...",
42
+ interactive=False,
43
+ lines=3,
44
+ elem_classes=["cyber-output"]
45
+ )
46
+
47
+ # Voice commands
48
+ gr.Markdown("""
49
+ **Voice Commands:**
50
+ - "Create a [type] monster" - Generate specific type
51
+ - "Make it [color/trait]" - Add characteristics
52
+ - "Give it [ability]" - Add special abilities
53
+ """)
54
+
55
+ with gr.Column(scale=1):
56
+ # Preview and generation status
57
+ generation_status = gr.Markdown(
58
+ value="🟢 Ready for voice input",
59
+ elem_classes=["cyber-message"]
60
+ )
61
+
62
+ # Audio visualization
63
+ audio_viz = gr.HTML(
64
+ value="""
65
+ <div class="audio-visualizer">
66
+ <div class="bar"></div>
67
+ <div class="bar"></div>
68
+ <div class="bar"></div>
69
+ <div class="bar"></div>
70
+ <div class="bar"></div>
71
+ </div>
72
+ """,
73
+ elem_classes=["cyber-container"]
74
+ )
75
+
76
+ # Quick voice templates
77
+ gr.Markdown("**Quick Templates:**")
78
+ template_buttons = []
79
+ templates = [
80
+ ("🔥 Fire Type", "Create a fierce fire-breathing dragon monster"),
81
+ ("💧 Water Type", "Create a graceful aquatic monster"),
82
+ ("⚡ Electric Type", "Create a sparking electric monster"),
83
+ ("🌿 Nature Type", "Create a peaceful nature guardian monster")
84
+ ]
85
+
86
+ for label, prompt in templates:
87
+ btn = gr.Button(label, size="sm", elem_classes=["cyber-button"])
88
+ template_buttons.append((btn, prompt))
89
+
90
+ # Voice interface specific styling
91
+ gr.HTML("""
92
+ <style>
93
+ .audio-visualizer {
94
+ display: flex;
95
+ justify-content: center;
96
+ align-items: center;
97
+ height: 100px;
98
+ gap: 5px;
99
+ }
100
+
101
+ .audio-visualizer .bar {
102
+ width: 10px;
103
+ height: 30px;
104
+ background: linear-gradient(to top, #00ff41, #8A2BE2);
105
+ animation: audio-wave 1s ease-in-out infinite;
106
+ border-radius: 5px;
107
+ }
108
+
109
+ .audio-visualizer .bar:nth-child(1) { animation-delay: 0s; }
110
+ .audio-visualizer .bar:nth-child(2) { animation-delay: 0.1s; }
111
+ .audio-visualizer .bar:nth-child(3) { animation-delay: 0.2s; }
112
+ .audio-visualizer .bar:nth-child(4) { animation-delay: 0.3s; }
113
+ .audio-visualizer .bar:nth-child(5) { animation-delay: 0.4s; }
114
+
115
+ @keyframes audio-wave {
116
+ 0%, 100% { height: 30px; }
117
+ 50% { height: 60px; }
118
+ }
119
+ </style>
120
+ """)
121
+
122
+ return voice_interface
123
+
124
+ def create_visual_interface(pipeline: Any, game_mechanics: Any) -> gr.Column:
125
+ """Create visual/camera-based interface for monster generation"""
126
+
127
+ with gr.Column() as visual_interface:
128
+ gr.Markdown("""
129
+ ### 👁️ Visual Control Interface
130
+ Use images, drawings, or camera input to inspire your monster creation!
131
+ """)
132
+
133
+ with gr.Row():
134
+ with gr.Column(scale=1):
135
+ # Image input options
136
+ with gr.Tabs():
137
+ with gr.TabItem("📷 Camera"):
138
+ camera_input = gr.Image(
139
+ label="Camera Capture",
140
+ sources=["webcam"],
141
+ type="pil",
142
+ elem_classes=["cyber-input", "camera-feed"]
143
+ )
144
+
145
+ with gr.TabItem("🖼️ Upload"):
146
+ image_upload = gr.File(
147
+ label="Upload Reference Images",
148
+ file_count="multiple",
149
+ file_types=["image"],
150
+ elem_classes=["cyber-input"]
151
+ )
152
+
153
+ uploaded_gallery = gr.Gallery(
154
+ label="Uploaded References",
155
+ columns=3,
156
+ rows=1,
157
+ height="150px",
158
+ elem_classes=["cyber-container"]
159
+ )
160
+
161
+ with gr.TabItem("✏️ Draw"):
162
+ sketch_pad = gr.Sketchpad(
163
+ label="Draw Your Monster",
164
+ type="pil",
165
+ elem_classes=["cyber-input", "sketch-pad"]
166
+ )
167
+
168
+ # Visual style options
169
+ gr.Markdown("**Visual Style Options:**")
170
+
171
+ style_modifier = gr.CheckboxGroup(
172
+ choices=[
173
+ "🎨 Artistic",
174
+ "🤖 Mechanical",
175
+ "✨ Magical",
176
+ "🌊 Organic",
177
+ "💎 Crystalline"
178
+ ],
179
+ label="Style Modifiers",
180
+ elem_classes=["cyber-checkbox"]
181
+ )
182
+
183
+ color_palette = gr.Radio(
184
+ choices=[
185
+ "🌈 Vibrant",
186
+ "🌑 Dark",
187
+ "❄️ Cool",
188
+ "🔥 Warm",
189
+ "🎨 Custom"
190
+ ],
191
+ label="Color Palette",
192
+ value="🌈 Vibrant",
193
+ elem_classes=["cyber-radio"]
194
+ )
195
+
196
+ with gr.Column(scale=1):
197
+ # Image analysis display
198
+ image_analysis = gr.Markdown(
199
+ value="📊 Image Analysis Results",
200
+ elem_classes=["cyber-message"]
201
+ )
202
+
203
+ # Detected features
204
+ detected_features = gr.JSON(
205
+ label="🔍 Detected Features",
206
+ elem_classes=["cyber-stats"]
207
+ )
208
+
209
+ # Generation preview
210
+ preview_placeholder = gr.HTML(
211
+ value="""
212
+ <div class="preview-container">
213
+ <div class="scanning-overlay">
214
+ <div class="scan-line"></div>
215
+ </div>
216
+ <p>Preview will appear here...</p>
217
+ </div>
218
+ """,
219
+ elem_classes=["cyber-container"]
220
+ )
221
+
222
+ # Confidence meter
223
+ confidence_display = gr.HTML(
224
+ value="""
225
+ <div class="confidence-meter">
226
+ <label>Generation Confidence:</label>
227
+ <div class="meter-bar">
228
+ <div class="meter-fill" style="width: 0%"></div>
229
+ </div>
230
+ <span class="meter-value">0%</span>
231
+ </div>
232
+ """,
233
+ elem_classes=["cyber-container"]
234
+ )
235
+
236
+ # Visual interface specific styling
237
+ gr.HTML("""
238
+ <style>
239
+ .camera-feed {
240
+ border: 3px solid #00ff41;
241
+ border-radius: 10px;
242
+ overflow: hidden;
243
+ position: relative;
244
+ }
245
+
246
+ .camera-feed::after {
247
+ content: 'LIVE';
248
+ position: absolute;
249
+ top: 10px;
250
+ right: 10px;
251
+ background: red;
252
+ color: white;
253
+ padding: 5px 10px;
254
+ border-radius: 5px;
255
+ font-size: 12px;
256
+ animation: blink 1s infinite;
257
+ }
258
+
259
+ @keyframes blink {
260
+ 0%, 100% { opacity: 1; }
261
+ 50% { opacity: 0.5; }
262
+ }
263
+
264
+ .sketch-pad {
265
+ background: rgba(0, 0, 0, 0.9);
266
+ border: 2px solid #8A2BE2;
267
+ }
268
+
269
+ .preview-container {
270
+ position: relative;
271
+ height: 200px;
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ overflow: hidden;
276
+ }
277
+
278
+ .scanning-overlay {
279
+ position: absolute;
280
+ top: 0;
281
+ left: 0;
282
+ right: 0;
283
+ bottom: 0;
284
+ pointer-events: none;
285
+ }
286
+
287
+ .scan-line {
288
+ position: absolute;
289
+ top: 0;
290
+ left: 0;
291
+ right: 0;
292
+ height: 2px;
293
+ background: linear-gradient(90deg, transparent, #00ff41, transparent);
294
+ animation: scan-vertical 2s linear infinite;
295
+ }
296
+
297
+ @keyframes scan-vertical {
298
+ 0% { top: 0; }
299
+ 100% { top: 100%; }
300
+ }
301
+
302
+ .confidence-meter {
303
+ padding: 15px;
304
+ }
305
+
306
+ .confidence-meter label {
307
+ color: #8A2BE2;
308
+ font-size: 14px;
309
+ margin-bottom: 5px;
310
+ display: block;
311
+ }
312
+
313
+ .meter-bar {
314
+ background: rgba(0, 0, 0, 0.5);
315
+ border: 1px solid #00ff41;
316
+ height: 20px;
317
+ border-radius: 10px;
318
+ overflow: hidden;
319
+ margin: 10px 0;
320
+ }
321
+
322
+ .meter-fill {
323
+ height: 100%;
324
+ background: linear-gradient(90deg, #00ff41, #8A2BE2);
325
+ transition: width 0.5s ease;
326
+ }
327
+
328
+ .meter-value {
329
+ color: #00ff41;
330
+ font-weight: bold;
331
+ font-size: 18px;
332
+ }
333
+ </style>
334
+ """)
335
+
336
+ return visual_interface
337
+
338
+ def create_monster_status_display() -> gr.Column:
339
+ """Create monster status display component"""
340
+
341
+ with gr.Column() as status_display:
342
+ # 3D Model viewer
343
+ model_viewer = gr.Model3D(
344
+ label="Your Digital Monster",
345
+ height=400,
346
+ elem_classes=["monster-display"]
347
+ )
348
+
349
+ # Status indicators
350
+ with gr.Row():
351
+ hp_bar = gr.HTML(
352
+ value=create_status_bar("HP", 100, 100, "#ff4444"),
353
+ elem_classes=["status-bar"]
354
+ )
355
+
356
+ hunger_bar = gr.HTML(
357
+ value=create_status_bar("Hunger", 80, 100, "#ffaa44"),
358
+ elem_classes=["status-bar"]
359
+ )
360
+
361
+ happiness_bar = gr.HTML(
362
+ value=create_status_bar("Happiness", 90, 100, "#44ff44"),
363
+ elem_classes=["status-bar"]
364
+ )
365
+
366
+ # Communication display
367
+ communication = gr.Textbox(
368
+ label="Monster Says",
369
+ value="🤖💚9️⃣0️⃣",
370
+ interactive=False,
371
+ elem_classes=["cyber-dialogue"]
372
+ )
373
+
374
+ # Evolution progress
375
+ evolution_display = gr.HTML(
376
+ value="""
377
+ <div class="evolution-progress">
378
+ <h4>Evolution Progress</h4>
379
+ <div class="progress-ring">
380
+ <svg width="120" height="120">
381
+ <circle cx="60" cy="60" r="50" stroke="#333" stroke-width="10" fill="none"/>
382
+ <circle cx="60" cy="60" r="50" stroke="#8A2BE2" stroke-width="10" fill="none"
383
+ stroke-dasharray="314" stroke-dashoffset="157"
384
+ transform="rotate(-90 60 60)"/>
385
+ </svg>
386
+ <div class="progress-text">50%</div>
387
+ </div>
388
+ </div>
389
+ """,
390
+ elem_classes=["evolution-display"]
391
+ )
392
+
393
+ return status_display
394
+
395
+ def create_status_bar(label: str, current: int, max_val: int, color: str) -> str:
396
+ """Create HTML status bar"""
397
+ percentage = (current / max_val) * 100
398
+
399
+ return f"""
400
+ <div class="status-bar-container">
401
+ <label>{label}</label>
402
+ <div class="status-bar-bg">
403
+ <div class="status-bar-fill" style="width: {percentage}%; background: {color};"></div>
404
+ </div>
405
+ <span class="status-value">{current}/{max_val}</span>
406
+ </div>
407
+ """
408
+
409
+ def create_training_interface() -> gr.Column:
410
+ """Create training interface component"""
411
+
412
+ with gr.Column() as training_interface:
413
+ gr.Markdown("""
414
+ ### 💪 Training Center
415
+ Train your monster to improve its stats and prepare for evolution!
416
+ """)
417
+
418
+ # Training schedule
419
+ with gr.Row():
420
+ with gr.Column():
421
+ training_schedule = gr.DataFrame(
422
+ headers=["Time", "Activity", "Stat Focus", "Intensity"],
423
+ datatype=["str", "str", "str", "number"],
424
+ value=[
425
+ ["Morning", "Strength Training", "Attack", 7],
426
+ ["Afternoon", "Agility Course", "Speed", 5],
427
+ ["Evening", "Meditation", "Special", 3]
428
+ ],
429
+ elem_classes=["cyber-table"]
430
+ )
431
+
432
+ with gr.Column():
433
+ # Training mini-game
434
+ training_game = gr.HTML(
435
+ value="""
436
+ <div class="training-game">
437
+ <h4>Quick Training</h4>
438
+ <div class="game-area">
439
+ <div class="target" id="training-target"></div>
440
+ </div>
441
+ <p>Click the targets to train!</p>
442
+ </div>
443
+ """,
444
+ elem_classes=["cyber-container"]
445
+ )
446
+
447
+ # Training rewards
448
+ rewards_display = gr.Markdown(
449
+ """
450
+ **Today's Rewards:**
451
+ - 🏆 +15 Attack
452
+ - 🛡️ +10 Defense
453
+ - ⚡ +5 Speed
454
+ """,
455
+ elem_classes=["cyber-message"]
456
+ )
457
+
458
+ return training_interface
ui/themes.py ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from gradio.themes.base import Base
3
+ from gradio.themes.utils import colors, fonts, sizes
4
+
5
+ def get_cyberpunk_theme():
6
+ """Create cyberpunk-themed Gradio theme"""
7
+
8
+ theme = gr.themes.Base(
9
+ primary_hue=colors.green,
10
+ secondary_hue=colors.purple,
11
+ neutral_hue=colors.gray,
12
+ font=[fonts.GoogleFont("Orbitron"), "monospace", "sans-serif"],
13
+ ).set(
14
+ # Background colors
15
+ body_background_fill="linear-gradient(135deg, #0a0a0a, #1a0033, #001122)",
16
+ body_background_fill_dark="linear-gradient(135deg, #050505, #0d001a, #000811)",
17
+
18
+ # Text colors
19
+ body_text_color="#00ff41",
20
+ body_text_color_dark="#00ff41",
21
+ body_text_color_subdued="#8A2BE2",
22
+ body_text_color_subdued_dark="#8A2BE2",
23
+
24
+ # Component colors
25
+ background_fill_primary="rgba(0, 255, 65, 0.1)",
26
+ background_fill_primary_dark="rgba(0, 255, 65, 0.05)",
27
+ background_fill_secondary="rgba(138, 43, 226, 0.1)",
28
+ background_fill_secondary_dark="rgba(138, 43, 226, 0.05)",
29
+
30
+ # Borders
31
+ border_color_primary="#00ff41",
32
+ border_color_primary_dark="#00ff41",
33
+ border_color_accent="#8A2BE2",
34
+ border_color_accent_dark="#8A2BE2",
35
+
36
+ # Shadows
37
+ shadow_drop="0 0 20px rgba(0, 255, 65, 0.5)",
38
+ shadow_drop_lg="0 0 30px rgba(138, 43, 226, 0.5)",
39
+
40
+ # Buttons
41
+ button_primary_background_fill="linear-gradient(45deg, #00ff41, #8A2BE2)",
42
+ button_primary_background_fill_hover="linear-gradient(45deg, #00ff41, #9932CC)",
43
+ button_primary_background_fill_dark="linear-gradient(45deg, #00cc33, #7B68EE)",
44
+ button_primary_text_color="#000000",
45
+ button_primary_text_color_dark="#000000",
46
+
47
+ # Inputs
48
+ input_background_fill="rgba(0, 0, 0, 0.8)",
49
+ input_background_fill_dark="rgba(0, 0, 0, 0.9)",
50
+ input_border_color="#00ff41",
51
+ input_border_color_dark="#00ff41",
52
+ input_border_color_focus="#8A2BE2",
53
+ input_border_color_focus_dark="#8A2BE2",
54
+
55
+ # Other elements
56
+ block_background_fill="rgba(0, 0, 0, 0.6)",
57
+ block_background_fill_dark="rgba(0, 0, 0, 0.8)",
58
+ block_border_color="#00ff41",
59
+ block_border_color_dark="#00ff41",
60
+ block_border_width="2px",
61
+ block_shadow="0 0 15px rgba(0, 255, 65, 0.3)",
62
+
63
+ # Radius
64
+ radius_lg="8px",
65
+ radius_md="6px",
66
+ radius_sm="4px",
67
+ )
68
+
69
+ return theme
70
+
71
+ # Custom CSS for additional cyberpunk styling
72
+ CYBERPUNK_CSS = """
73
+ /* Import cyberpunk font */
74
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
75
+
76
+ /* Global styles */
77
+ * {
78
+ font-family: 'Orbitron', monospace !important;
79
+ }
80
+
81
+ /* Animated background */
82
+ .gradio-container {
83
+ background: linear-gradient(135deg, #0a0a0a, #1a0033, #001122);
84
+ background-size: 400% 400%;
85
+ animation: gradient-shift 15s ease infinite;
86
+ }
87
+
88
+ @keyframes gradient-shift {
89
+ 0% { background-position: 0% 50%; }
90
+ 50% { background-position: 100% 50%; }
91
+ 100% { background-position: 0% 50%; }
92
+ }
93
+
94
+ /* Cyber header */
95
+ .cyber-header {
96
+ background: linear-gradient(135deg, rgba(0,0,0,0.9), rgba(26,0,51,0.9));
97
+ border: 2px solid #00ff41;
98
+ border-radius: 10px;
99
+ padding: 30px;
100
+ margin-bottom: 30px;
101
+ position: relative;
102
+ overflow: hidden;
103
+ box-shadow: 0 0 30px rgba(0,255,65,0.5), inset 0 0 30px rgba(0,255,65,0.1);
104
+ }
105
+
106
+ .cyber-header::before {
107
+ content: '';
108
+ position: absolute;
109
+ top: -2px;
110
+ left: -2px;
111
+ right: -2px;
112
+ bottom: -2px;
113
+ background: linear-gradient(45deg, #00ff41, #8A2BE2, #00ff41);
114
+ z-index: -1;
115
+ animation: border-glow 3s linear infinite;
116
+ filter: blur(5px);
117
+ }
118
+
119
+ @keyframes border-glow {
120
+ 0%, 100% { opacity: 1; }
121
+ 50% { opacity: 0.5; }
122
+ }
123
+
124
+ /* Glitch text effect */
125
+ .glitch-text {
126
+ position: relative;
127
+ color: #00ff41;
128
+ font-size: 3em;
129
+ font-weight: 900;
130
+ text-transform: uppercase;
131
+ text-shadow:
132
+ 0 0 10px #00ff41,
133
+ 0 0 20px #00ff41,
134
+ 0 0 40px #00ff41;
135
+ animation: text-glow 2s ease-in-out infinite alternate;
136
+ }
137
+
138
+ @keyframes text-glow {
139
+ from { text-shadow: 0 0 10px #00ff41, 0 0 20px #00ff41, 0 0 40px #00ff41; }
140
+ to { text-shadow: 0 0 20px #00ff41, 0 0 30px #00ff41, 0 0 50px #00ff41; }
141
+ }
142
+
143
+ .glitch-text::before,
144
+ .glitch-text::after {
145
+ content: attr(data-text);
146
+ position: absolute;
147
+ top: 0;
148
+ left: 0;
149
+ width: 100%;
150
+ height: 100%;
151
+ }
152
+
153
+ .glitch-text::before {
154
+ animation: glitch-1 0.5s infinite;
155
+ color: #8A2BE2;
156
+ z-index: -1;
157
+ }
158
+
159
+ .glitch-text::after {
160
+ animation: glitch-2 0.5s infinite;
161
+ color: #00ff41;
162
+ z-index: -2;
163
+ }
164
+
165
+ @keyframes glitch-1 {
166
+ 0% { clip: rect(44px, 450px, 56px, 0); transform: translate(0); }
167
+ 20% { clip: rect(20px, 450px, 30px, 0); transform: translate(-2px, 2px); }
168
+ 40% { clip: rect(85px, 450px, 95px, 0); transform: translate(2px, -2px); }
169
+ 60% { clip: rect(10px, 450px, 20px, 0); transform: translate(-1px, 1px); }
170
+ 80% { clip: rect(60px, 450px, 70px, 0); transform: translate(1px, -1px); }
171
+ 100% { clip: rect(44px, 450px, 56px, 0); transform: translate(0); }
172
+ }
173
+
174
+ @keyframes glitch-2 {
175
+ 0% { clip: rect(65px, 450px, 75px, 0); transform: translate(0); }
176
+ 20% { clip: rect(30px, 450px, 40px, 0); transform: translate(2px, -2px); }
177
+ 40% { clip: rect(90px, 450px, 100px, 0); transform: translate(-2px, 2px); }
178
+ 60% { clip: rect(15px, 450px, 25px, 0); transform: translate(1px, -1px); }
179
+ 80% { clip: rect(70px, 450px, 80px, 0); transform: translate(-1px, 1px); }
180
+ 100% { clip: rect(65px, 450px, 75px, 0); transform: translate(0); }
181
+ }
182
+
183
+ /* Cyber subtitle */
184
+ .cyber-subtitle {
185
+ color: #8A2BE2;
186
+ font-size: 1.2em;
187
+ text-transform: uppercase;
188
+ letter-spacing: 2px;
189
+ text-shadow: 0 0 10px #8A2BE2;
190
+ }
191
+
192
+ /* Pulse line */
193
+ .pulse-line {
194
+ height: 2px;
195
+ background: linear-gradient(90deg, transparent, #00ff41, transparent);
196
+ margin: 20px 0;
197
+ animation: pulse-width 2s ease-in-out infinite;
198
+ }
199
+
200
+ @keyframes pulse-width {
201
+ 0%, 100% { transform: scaleX(0.5); opacity: 0.5; }
202
+ 50% { transform: scaleX(1); opacity: 1; }
203
+ }
204
+
205
+ /* Cyber containers */
206
+ .cyber-container {
207
+ background: rgba(0, 0, 0, 0.8);
208
+ border: 2px solid #00ff41;
209
+ border-radius: 8px;
210
+ padding: 20px;
211
+ position: relative;
212
+ box-shadow:
213
+ 0 0 20px rgba(0, 255, 65, 0.3),
214
+ inset 0 0 20px rgba(0, 255, 65, 0.1);
215
+ animation: container-pulse 4s ease-in-out infinite;
216
+ }
217
+
218
+ @keyframes container-pulse {
219
+ 0%, 100% {
220
+ border-color: #00ff41;
221
+ box-shadow: 0 0 20px rgba(0, 255, 65, 0.3), inset 0 0 20px rgba(0, 255, 65, 0.1);
222
+ }
223
+ 50% {
224
+ border-color: #8A2BE2;
225
+ box-shadow: 0 0 30px rgba(138, 43, 226, 0.5), inset 0 0 20px rgba(138, 43, 226, 0.1);
226
+ }
227
+ }
228
+
229
+ /* Cyber buttons */
230
+ .cyber-button {
231
+ background: linear-gradient(45deg, #00ff41, #8A2BE2);
232
+ border: none;
233
+ color: #000;
234
+ font-weight: bold;
235
+ text-transform: uppercase;
236
+ letter-spacing: 1px;
237
+ padding: 12px 24px;
238
+ border-radius: 6px;
239
+ position: relative;
240
+ overflow: hidden;
241
+ transition: all 0.3s ease;
242
+ box-shadow: 0 0 20px rgba(0, 255, 65, 0.5);
243
+ }
244
+
245
+ .cyber-button::before {
246
+ content: '';
247
+ position: absolute;
248
+ top: 50%;
249
+ left: 50%;
250
+ width: 0;
251
+ height: 0;
252
+ background: rgba(255, 255, 255, 0.3);
253
+ border-radius: 50%;
254
+ transform: translate(-50%, -50%);
255
+ transition: width 0.6s, height 0.6s;
256
+ }
257
+
258
+ .cyber-button:hover::before {
259
+ width: 300px;
260
+ height: 300px;
261
+ }
262
+
263
+ .cyber-button:hover {
264
+ transform: translateY(-2px);
265
+ box-shadow: 0 5px 30px rgba(0, 255, 65, 0.7);
266
+ }
267
+
268
+ /* Generate button special */
269
+ .generate-button {
270
+ font-size: 1.2em;
271
+ animation: generate-pulse 2s ease-in-out infinite;
272
+ }
273
+
274
+ @keyframes generate-pulse {
275
+ 0%, 100% { box-shadow: 0 0 20px rgba(0, 255, 65, 0.5); }
276
+ 50% { box-shadow: 0 0 40px rgba(0, 255, 65, 0.8), 0 0 60px rgba(138, 43, 226, 0.5); }
277
+ }
278
+
279
+ /* Cyber inputs */
280
+ .cyber-input input,
281
+ .cyber-input textarea {
282
+ background: rgba(0, 0, 0, 0.9) !important;
283
+ border: 2px solid #00ff41 !important;
284
+ color: #00ff41 !important;
285
+ font-family: 'Orbitron', monospace !important;
286
+ transition: all 0.3s ease;
287
+ }
288
+
289
+ .cyber-input input:focus,
290
+ .cyber-input textarea:focus {
291
+ border-color: #8A2BE2 !important;
292
+ box-shadow: 0 0 20px rgba(138, 43, 226, 0.5) !important;
293
+ outline: none !important;
294
+ }
295
+
296
+ /* Monster display */
297
+ .monster-display {
298
+ background: rgba(0, 0, 0, 0.9);
299
+ border: 2px solid #00ff41;
300
+ border-radius: 10px;
301
+ padding: 20px;
302
+ position: relative;
303
+ overflow: hidden;
304
+ }
305
+
306
+ .monster-display::before {
307
+ content: '';
308
+ position: absolute;
309
+ top: 0;
310
+ left: -100%;
311
+ width: 100%;
312
+ height: 100%;
313
+ background: linear-gradient(90deg, transparent, rgba(0, 255, 65, 0.2), transparent);
314
+ animation: scan-line 3s linear infinite;
315
+ }
316
+
317
+ @keyframes scan-line {
318
+ 0% { left: -100%; }
319
+ 100% { left: 100%; }
320
+ }
321
+
322
+ /* Evolution display */
323
+ .evolution-display {
324
+ background: linear-gradient(135deg, rgba(138, 43, 226, 0.2), rgba(0, 255, 65, 0.2));
325
+ border: 2px solid #8A2BE2;
326
+ border-radius: 8px;
327
+ padding: 15px;
328
+ margin: 10px 0;
329
+ animation: evolution-glow 2s ease-in-out infinite;
330
+ }
331
+
332
+ @keyframes evolution-glow {
333
+ 0%, 100% { box-shadow: 0 0 20px rgba(138, 43, 226, 0.5); }
334
+ 50% { box-shadow: 0 0 40px rgba(138, 43, 226, 0.8); }
335
+ }
336
+
337
+ /* Cyber dialogue */
338
+ .cyber-dialogue {
339
+ font-size: 1.5em;
340
+ text-align: center;
341
+ color: #00ff41;
342
+ text-shadow: 0 0 10px currentColor;
343
+ }
344
+
345
+ /* Cyber stats */
346
+ .cyber-stats {
347
+ font-family: 'Orbitron', monospace;
348
+ color: #00ff41;
349
+ }
350
+
351
+ .cyber-stats .label {
352
+ color: #8A2BE2;
353
+ }
354
+
355
+ /* Tab styling */
356
+ .cyber-tabs .tab-nav {
357
+ background: rgba(0, 0, 0, 0.8);
358
+ border-bottom: 2px solid #00ff41;
359
+ }
360
+
361
+ .cyber-tabs .tab-nav button {
362
+ color: #00ff41 !important;
363
+ background: transparent !important;
364
+ border: none !important;
365
+ transition: all 0.3s ease;
366
+ }
367
+
368
+ .cyber-tabs .tab-nav button:hover {
369
+ background: rgba(0, 255, 65, 0.1) !important;
370
+ text-shadow: 0 0 10px #00ff41;
371
+ }
372
+
373
+ .cyber-tabs .tab-nav button.selected {
374
+ background: rgba(0, 255, 65, 0.2) !important;
375
+ border-bottom: 3px solid #00ff41 !important;
376
+ }
377
+
378
+ /* Loading animation */
379
+ .loading {
380
+ display: inline-block;
381
+ width: 20px;
382
+ height: 20px;
383
+ border: 3px solid rgba(0, 255, 65, 0.3);
384
+ border-radius: 50%;
385
+ border-top-color: #00ff41;
386
+ animation: spin 1s ease-in-out infinite;
387
+ }
388
+
389
+ @keyframes spin {
390
+ to { transform: rotate(360deg); }
391
+ }
392
+
393
+ /* Scrollbar styling */
394
+ ::-webkit-scrollbar {
395
+ width: 10px;
396
+ height: 10px;
397
+ }
398
+
399
+ ::-webkit-scrollbar-track {
400
+ background: rgba(0, 0, 0, 0.5);
401
+ border: 1px solid #00ff41;
402
+ }
403
+
404
+ ::-webkit-scrollbar-thumb {
405
+ background: linear-gradient(45deg, #00ff41, #8A2BE2);
406
+ border-radius: 5px;
407
+ }
408
+
409
+ ::-webkit-scrollbar-thumb:hover {
410
+ background: linear-gradient(45deg, #00ff41, #9932CC);
411
+ }
412
+
413
+ /* Mobile responsiveness */
414
+ @media (max-width: 768px) {
415
+ .glitch-text {
416
+ font-size: 2em;
417
+ }
418
+
419
+ .cyber-container {
420
+ padding: 15px;
421
+ }
422
+
423
+ .cyber-button {
424
+ padding: 10px 20px;
425
+ font-size: 0.9em;
426
+ }
427
+ }
428
+ """