chenemii commited on
Commit
7a0d98b
·
1 Parent(s): f3b5cd6

Remove all Gradio files and dependencies

Browse files

- Delete app/gradio_app.py (Gradio implementation)
- Delete app.py (Gradio entry point)
- Delete app/styles.css (Gradio CSS file)
- Clean up repository to use only Streamlit implementation

Files changed (3) hide show
  1. app.py +0 -26
  2. app/gradio_app.py +0 -492
  3. app/styles.css +0 -122
app.py DELETED
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Golf Swing Analysis - Hugging Face Spaces Entry Point (Gradio Interface)
4
- """
5
-
6
- import os
7
- import sys
8
-
9
- # Add the app directory to the path for imports
10
- sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
11
-
12
- # Import and run the new Gradio app
13
- try:
14
- from app.gradio_app import create_interface
15
-
16
- if __name__ == "__main__":
17
- demo = create_interface()
18
- demo.launch(share=True)
19
- except ImportError as e:
20
- # Fallback import method
21
- sys.path.insert(0, 'app')
22
- import gradio_app
23
-
24
- if __name__ == "__main__":
25
- demo = gradio_app.create_interface()
26
- demo.launch(share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/gradio_app.py DELETED
@@ -1,492 +0,0 @@
1
- """
2
- Gradio web UI for Golf Swing Analysis with Step-based Flow
3
- """
4
-
5
- import os
6
- import sys
7
- import tempfile
8
- import gradio as gr
9
- from dotenv import load_dotenv
10
- import base64
11
- from pathlib import Path
12
- import shutil
13
- import cv2
14
- from PIL import Image
15
- from datetime import datetime
16
-
17
- # Load environment variables
18
- load_dotenv()
19
-
20
- # Add the app directory to the path
21
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
22
-
23
- # Import analysis modules
24
- from utils.video_downloader import download_youtube_video, download_pro_reference, cleanup_video_file, cleanup_downloads_directory
25
- from utils.video_processor import process_video
26
- from models.pose_estimator import analyze_pose
27
- from models.swing_analyzer import segment_swing, analyze_trajectory
28
- from models.llm_analyzer import generate_swing_analysis, create_llm_prompt, prepare_data_for_llm, check_llm_services, parse_and_format_analysis
29
-
30
- # Import RAG functionality
31
- try:
32
- from golf_swing_rag import GolfSwingRAG
33
- RAG_AVAILABLE = True
34
- except ImportError:
35
- RAG_AVAILABLE = False
36
-
37
- def validate_youtube_url(url):
38
- """Validate if the URL is a YouTube URL"""
39
- return "youtube.com" in url or "youtu.be" in url
40
-
41
- def start_analysis(input_text, video_file):
42
- """Start video analysis process"""
43
- try:
44
- video_path = None
45
-
46
- # Handle video input
47
- if video_file is not None:
48
- # Save uploaded file
49
- os.makedirs("downloads", exist_ok=True)
50
- video_path = os.path.join("downloads", f"uploaded_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4")
51
- shutil.copy(video_file.name, video_path)
52
- elif input_text and validate_youtube_url(input_text):
53
- # Download from YouTube
54
- video_path = download_youtube_video(input_text)
55
- else:
56
- return ("### Step 1: Upload Your Video",
57
- "Please provide either a valid YouTube URL or upload a video file.",
58
- 1, None)
59
-
60
- if not video_path or not os.path.exists(video_path):
61
- return ("### Step 1: Upload Your Video",
62
- "Failed to process video. Please try again.",
63
- 1, None)
64
-
65
- # Move to analysis step
66
- return ("### Step 2: Analyzing Video and Pose",
67
- "🔄 **Processing your swing video...**\n\nPlease wait while we analyze your golf swing.",
68
- 2, {'video_path': video_path})
69
-
70
- except Exception as e:
71
- error_message = f"Error starting analysis: {str(e)}"
72
- return ("### Step 1: Upload Your Video",
73
- error_message, 1, None)
74
-
75
- def process_analysis(analysis_state):
76
- """Process the actual video analysis"""
77
- if not analysis_state or 'video_path' not in analysis_state:
78
- return ("### Step 1: Upload Your Video",
79
- "No video to analyze. Please upload a video first.",
80
- 1, None)
81
-
82
- try:
83
- video_path = analysis_state['video_path']
84
-
85
- # Process video analysis
86
- frames, detections = process_video(video_path, sample_rate=1)
87
- pose_data = analyze_pose(frames)
88
- swing_phases = segment_swing(pose_data, detections, sample_rate=1)
89
- trajectory_data = analyze_trajectory(frames, detections, swing_phases, sample_rate=1)
90
- analysis_data = prepare_data_for_llm(pose_data, swing_phases, trajectory_data)
91
-
92
- # Store complete results
93
- results = {
94
- 'video_path': video_path,
95
- 'pose_data': pose_data,
96
- 'swing_phases': swing_phases,
97
- 'trajectory_data': trajectory_data,
98
- 'analysis_data': analysis_data
99
- }
100
-
101
- # Generate results summary
102
- results_content = f"""
103
- ## 🏌️ Swing Analysis Results
104
-
105
- **Video:** {os.path.basename(video_path)}
106
-
107
- ### 📊 **Analysis Summary**
108
- - **Swing Phases Detected:** {len(swing_phases) if swing_phases else 0}
109
- - **Pose Data Points:** {len(pose_data) if pose_data else 0}
110
- - **Trajectory Analysis:** {'✅ Complete' if trajectory_data else '❌ Failed'}
111
-
112
- ### 🎯 **Key Findings**
113
- - Swing timing and rhythm patterns identified
114
- - Pose alignment and form analyzed
115
- - Ball trajectory and impact mechanics evaluated
116
-
117
- **Ready to dive deeper?** Choose how you'd like to explore your swing analysis:
118
- """
119
-
120
- return ("### Step 3: See Your Results",
121
- results_content, 3, results)
122
-
123
- except Exception as e:
124
- error_message = f"Error during analysis: {str(e)}"
125
- return ("### Step 2: Analyzing Video and Pose",
126
- f"❌ **Analysis Failed**\n\n{error_message}\n\nPlease try again.",
127
- 2, analysis_state)
128
-
129
-
130
-
131
- def go_to_improvements(analysis_results):
132
- """Navigate to improvements step"""
133
- return ("### Step 4: AI-Powered Improvements",
134
- "🎯 **Generating personalized swing improvements...**",
135
- 4, analysis_results)
136
-
137
- def go_to_chatbot(analysis_results):
138
- """Navigate to chatbot step"""
139
- return ("### Step 5: Ask the Golf Expert",
140
- "💬 **Ready to answer your swing questions!**",
141
- 5, analysis_results)
142
-
143
- def go_back_to_results(analysis_results):
144
- """Go back to results step"""
145
- if not analysis_results:
146
- return ("### Step 1: Upload Your Video",
147
- "No analysis data available. Please start over.",
148
- 1, None)
149
-
150
- results_content = f"""
151
- ## 🏌️ Swing Analysis Results
152
-
153
- **Video:** {os.path.basename(analysis_results.get('video_path', 'Unknown'))}
154
-
155
- ### 📊 **Analysis Summary**
156
- - **Swing Phases Detected:** {len(analysis_results.get('swing_phases', [])) if analysis_results.get('swing_phases') else 0}
157
- - **Pose Data Points:** {len(analysis_results.get('pose_data', [])) if analysis_results.get('pose_data') else 0}
158
- - **Trajectory Analysis:** {'✅ Complete' if analysis_results.get('trajectory_data') else '❌ Failed'}
159
-
160
- ### 🎯 **Key Findings**
161
- - Swing timing and rhythm patterns identified
162
- - Pose alignment and form analyzed
163
- - Ball trajectory and impact mechanics evaluated
164
-
165
- **Ready to dive deeper?** Choose how you'd like to explore your swing analysis:
166
- """
167
-
168
- return ("### Step 3: See Your Results",
169
- results_content, 3, analysis_results)
170
-
171
- def start_over():
172
- """Reset to beginning"""
173
- return ("### Step 1: Upload Your Video",
174
- "**Choose your input method:**",
175
- 1, None)
176
-
177
- def handle_next_step(current_step, youtube_input, video_upload, analysis_results):
178
- """Handle moving to next step"""
179
- if current_step == 1:
180
- # Start analysis
181
- header, content, step, results = start_analysis(youtube_input, video_upload)
182
- return (header, content, step, results,
183
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
184
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=False),
185
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
186
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False))
187
- elif current_step == 2:
188
- # Process analysis
189
- header, content, step, results = process_analysis(analysis_results)
190
- if step == 3: # Analysis complete
191
- return (header, content, step, results,
192
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
193
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
194
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
195
- gr.update(visible=True), gr.update(visible=True), gr.update(visible=True))
196
- else:
197
- return (header, content, step, results,
198
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
199
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=False),
200
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
201
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False))
202
- else:
203
- # Should not happen with current flow
204
- return (gr.update(), gr.update(), current_step, analysis_results,
205
- gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
206
- gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
207
-
208
- def handle_back_step(current_step, analysis_results):
209
- """Handle going back one step"""
210
- header, content, step, results = go_back_step(current_step, analysis_results)
211
-
212
- if step == 1:
213
- return (header, content, step,
214
- gr.update(value="🏌️ Start Analysis"), gr.update(visible=False), gr.update(visible=False),
215
- gr.update(visible=True), gr.update(visible=False), gr.update(visible=False),
216
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
217
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False))
218
- elif step == 3:
219
- return (header, content, step,
220
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=True),
221
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
222
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
223
- gr.update(visible=True), gr.update(visible=True), gr.update(visible=True))
224
- else:
225
- return (header, content, step,
226
- gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
227
- gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
228
-
229
- def handle_go_to_step(target_step, analysis_results):
230
- """Handle navigation to specific step"""
231
- if target_step == 3:
232
- header, content, step, results = go_back_to_results(analysis_results)
233
- return (header, content, step,
234
- gr.update(visible=False), gr.update(visible=True),
235
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
236
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
237
- gr.update(visible=True), gr.update(visible=True), gr.update(visible=True))
238
- elif target_step == 4:
239
- header, content, step, results = go_to_improvements(analysis_results)
240
- improvements_content = generate_improvements(analysis_results)
241
- return (header, content, step,
242
- gr.update(visible=False), gr.update(visible=True),
243
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
244
- gr.update(visible=True), gr.update(visible=False), gr.update(visible=False),
245
- gr.update(value=improvements_content), gr.update(visible=True), gr.update(visible=False), gr.update(visible=True))
246
- elif target_step == 5:
247
- header, content, step, results = go_to_chatbot(analysis_results)
248
- return (header, content, step,
249
- gr.update(visible=False), gr.update(visible=True),
250
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
251
- gr.update(visible=False), gr.update(visible=True), gr.update(visible=False),
252
- gr.update(visible=True), gr.update(visible=False), gr.update(visible=True))
253
- else:
254
- return (gr.update(), gr.update(), target_step,
255
- gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
256
- gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
257
-
258
- def handle_start_over():
259
- """Handle starting over"""
260
- header, content, step, results = start_over()
261
- return (header, content, step, results,
262
- gr.update(value="🏌️ Start Analysis"), gr.update(visible=False), gr.update(visible=False),
263
- gr.update(visible=True), gr.update(visible=False), gr.update(visible=False),
264
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
265
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False))
266
-
267
- def generate_improvements(analysis_results):
268
- """Generate AI-powered swing improvements"""
269
- if not analysis_results:
270
- return "No analysis data available. Please analyze a video first."
271
-
272
- try:
273
- # Generate detailed analysis with recommendations
274
- analysis = generate_swing_analysis(
275
- analysis_results['pose_data'],
276
- analysis_results['swing_phases'],
277
- analysis_results['trajectory_data']
278
- )
279
-
280
- return f"""
281
- ## 🎯 Personalized Swing Improvements
282
-
283
- {analysis}
284
-
285
- ---
286
- *Analysis generated using AI-powered swing analysis technology*
287
- """
288
-
289
- except Exception as e:
290
- return f"Error generating improvements: {str(e)}"
291
-
292
- def get_chatbot_response(question, analysis_results, chat_history):
293
- """Get response from golf swing chatbot"""
294
- if not RAG_AVAILABLE:
295
- return chat_history + [("System", "Chatbot is not available due to missing dependencies.")], ""
296
-
297
- try:
298
- # Initialize RAG system if needed
299
- if not hasattr(get_chatbot_response, 'rag_system'):
300
- get_chatbot_response.rag_system = GolfSwingRAG()
301
- get_chatbot_response.rag_system.load_and_process_data()
302
- get_chatbot_response.rag_system.create_embeddings()
303
-
304
- # Search for relevant information
305
- relevant_chunks = get_chatbot_response.rag_system.search_similar_chunks(question, top_k=3)
306
-
307
- # Generate response (simplified version)
308
- if relevant_chunks:
309
- best_chunk = relevant_chunks[0]
310
- response = f"Based on {best_chunk['metadata']['title']}: {best_chunk['chunk'][:300]}..."
311
- else:
312
- response = "I couldn't find specific information about that topic. Could you try rephrasing your question?"
313
-
314
- # Add to chat history
315
- chat_history.append((question, response))
316
-
317
- return chat_history, ""
318
-
319
- except Exception as e:
320
- chat_history.append((question, f"Error: {str(e)}"))
321
- return chat_history, ""
322
-
323
- def go_back_step(current_step, analysis_results):
324
- """Go back one step"""
325
- if current_step <= 1:
326
- return start_over()
327
- elif current_step == 2:
328
- return start_over()
329
- elif current_step >= 3:
330
- return go_back_to_results(analysis_results)
331
- else:
332
- return start_over()
333
-
334
- # Create the Gradio interface
335
- def create_interface():
336
- with gr.Blocks(css="styles.css", title="Par-ity Project: Golf Swing Analysis") as demo:
337
- # States
338
- current_step = gr.State(1)
339
- analysis_results = gr.State(None)
340
-
341
- with gr.Row():
342
- # Floating sidebar (initially hidden)
343
- with gr.Column(scale=1, visible=False, elem_id="sidebar") as sidebar:
344
- gr.Markdown("### Navigation", elem_classes="sidebar-header")
345
- results_btn = gr.Button("📊 Results", variant="secondary", size="sm", visible=False)
346
- improvements_btn = gr.Button("🎯 Improvements", variant="secondary", size="sm", visible=False)
347
- chatbot_btn = gr.Button("💬 Chatbot", variant="secondary", size="sm", visible=False)
348
- start_over_btn = gr.Button("🔄 Start Over", variant="secondary", size="sm")
349
-
350
- # Main content area
351
- with gr.Column(scale=4, elem_classes="centered"):
352
- # Logo
353
- logo = gr.Image("1.png", show_label=False, width=150, height=75, container=False)
354
-
355
- # Step header with zigzag effect
356
- step_header = gr.Markdown("### Step 1: Upload Your Video", elem_id="step-header")
357
-
358
- # Main content area
359
- content_box = gr.Markdown("**Choose your input method:**")
360
-
361
- # Step 1: Upload section
362
- with gr.Group(visible=True) as step1_content:
363
- with gr.Tab("YouTube URL"):
364
- youtube_input = gr.Textbox(
365
- lines=2,
366
- placeholder="Paste YouTube URL of golf swing here...",
367
- label="YouTube URL",
368
- show_label=False
369
- )
370
-
371
- with gr.Tab("Upload Video"):
372
- video_upload = gr.File(
373
- file_types=["video"],
374
- label="Upload Video File",
375
- show_label=False
376
- )
377
-
378
- # Step 2: Analysis in progress (hidden initially)
379
- with gr.Group(visible=False) as step2_content:
380
- gr.Markdown("### 🔄 Processing Your Video")
381
- gr.Markdown("Please wait while we analyze your golf swing...")
382
- analysis_progress = gr.HTML("⏳ Starting analysis...")
383
-
384
- # Step 3: Results (hidden initially)
385
- with gr.Group(visible=False) as step3_content:
386
- results_display = gr.Markdown()
387
-
388
- # Step 4: Improvements (hidden initially)
389
- with gr.Group(visible=False) as step4_content:
390
- improvements_output = gr.Markdown()
391
-
392
- # Step 5: Chatbot (hidden initially)
393
- with gr.Group(visible=False) as step5_content:
394
- chatbot = gr.Chatbot(height=400)
395
- chatbot_input = gr.Textbox(placeholder="Ask about golf swing technique...", show_label=False)
396
- ask_btn = gr.Button("Ask Question")
397
-
398
- # Navigation buttons
399
- with gr.Row():
400
- back_btn = gr.Button("← Back", variant="secondary", visible=False)
401
- next_btn = gr.Button("🏌️ Start Analysis", variant="primary", size="lg")
402
-
403
- # Step 3 action buttons (shown only on results step)
404
- with gr.Row(visible=False) as step3_actions:
405
- step3_improvements_btn = gr.Button("🎯 Get Improvements", variant="primary", size="lg")
406
- step3_chatbot_btn = gr.Button("💬 Ask Questions", variant="secondary", size="lg")
407
-
408
- # Event handlers for step navigation
409
- next_btn.click(
410
- fn=handle_next_step,
411
- inputs=[current_step, youtube_input, video_upload, analysis_results],
412
- outputs=[step_header, content_box, current_step, analysis_results,
413
- next_btn, back_btn, sidebar, step1_content, step2_content, step3_content,
414
- step4_content, step5_content, step3_actions, results_btn, improvements_btn, chatbot_btn]
415
- )
416
-
417
- back_btn.click(
418
- fn=handle_back_step,
419
- inputs=[current_step, analysis_results],
420
- outputs=[step_header, content_box, current_step, next_btn, back_btn,
421
- sidebar, step1_content, step2_content, step3_content, step4_content,
422
- step5_content, step3_actions, results_btn, improvements_btn, chatbot_btn]
423
- )
424
-
425
- # Step 3 action buttons
426
- step3_improvements_btn.click(
427
- fn=lambda analysis_results: handle_go_to_step(4, analysis_results),
428
- inputs=[analysis_results],
429
- outputs=[step_header, content_box, current_step, next_btn, back_btn,
430
- step1_content, step2_content, step3_content, step4_content, step5_content,
431
- step3_actions, improvements_output, results_btn, improvements_btn, chatbot_btn]
432
- )
433
-
434
- step3_chatbot_btn.click(
435
- fn=lambda analysis_results: handle_go_to_step(5, analysis_results),
436
- inputs=[analysis_results],
437
- outputs=[step_header, content_box, current_step, next_btn, back_btn,
438
- step1_content, step2_content, step3_content, step4_content, step5_content,
439
- step3_actions, results_btn, improvements_btn, chatbot_btn]
440
- )
441
-
442
- # Sidebar navigation
443
- results_btn.click(
444
- fn=lambda analysis_results: handle_go_to_step(3, analysis_results),
445
- inputs=[analysis_results],
446
- outputs=[step_header, content_box, current_step, next_btn, back_btn,
447
- step1_content, step2_content, step3_content, step4_content, step5_content,
448
- step3_actions, results_btn, improvements_btn, chatbot_btn]
449
- )
450
-
451
- improvements_btn.click(
452
- fn=lambda analysis_results: handle_go_to_step(4, analysis_results),
453
- inputs=[analysis_results],
454
- outputs=[step_header, content_box, current_step, next_btn, back_btn,
455
- step1_content, step2_content, step3_content, step4_content, step5_content,
456
- step3_actions, improvements_output, results_btn, improvements_btn, chatbot_btn]
457
- )
458
-
459
- chatbot_btn.click(
460
- fn=lambda analysis_results: handle_go_to_step(5, analysis_results),
461
- inputs=[analysis_results],
462
- outputs=[step_header, content_box, current_step, next_btn, back_btn,
463
- step1_content, step2_content, step3_content, step4_content, step5_content,
464
- step3_actions, results_btn, improvements_btn, chatbot_btn]
465
- )
466
-
467
- start_over_btn.click(
468
- fn=handle_start_over,
469
- outputs=[step_header, content_box, current_step, analysis_results,
470
- next_btn, back_btn, sidebar, step1_content, step2_content, step3_content,
471
- step4_content, step5_content, step3_actions, results_btn, improvements_btn, chatbot_btn]
472
- )
473
-
474
- # Chatbot functionality
475
- ask_btn.click(
476
- fn=get_chatbot_response,
477
- inputs=[chatbot_input, analysis_results, chatbot],
478
- outputs=[chatbot, chatbot_input]
479
- )
480
-
481
- chatbot_input.submit(
482
- fn=get_chatbot_response,
483
- inputs=[chatbot_input, analysis_results, chatbot],
484
- outputs=[chatbot, chatbot_input]
485
- )
486
-
487
- return demo
488
-
489
- # Launch the app
490
- if __name__ == "__main__":
491
- demo = create_interface()
492
- demo.launch(share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/styles.css DELETED
@@ -1,122 +0,0 @@
1
- body {
2
- background-color: #FFFFFF;
3
- font-family: 'Georgia', serif;
4
- color: #0B3B0B;
5
- }
6
-
7
- .centered {
8
- min-height: 100vh;
9
- display: flex;
10
- flex-direction: column;
11
- justify-content: center;
12
- align-items: center;
13
- gap: 20px;
14
- }
15
-
16
- button, .gr-button {
17
- background-color: #0B3B0B !important;
18
- color: #FFFFFF !important;
19
- border-radius: 25px !important;
20
- padding: 12px 28px !important;
21
- font-weight: bold !important;
22
- font-size: 16px !important;
23
- transition: all 0.3s ease !important;
24
- border: none !important;
25
- box-shadow: 0 2px 8px rgba(11, 59, 11, 0.2) !important;
26
- }
27
-
28
- button:hover, .gr-button:hover {
29
- background-color: #4CAF50 !important;
30
- transform: translateY(-1px) !important;
31
- box-shadow: 0 4px 12px rgba(11, 59, 11, 0.3) !important;
32
- }
33
-
34
- textarea, input, .gr-textbox {
35
- border: 2px solid #4CAF50 !important;
36
- border-radius: 10px;
37
- }
38
-
39
- #step-header::after {
40
- content: "";
41
- display: block;
42
- height: 10px;
43
- background-image: repeating-linear-gradient(
44
- 135deg,
45
- #1D6F42,
46
- #1D6F42 5px,
47
- #A9DFBF 5px,
48
- #A9DFBF 10px
49
- );
50
- margin-top: 8px;
51
- }
52
-
53
- /* Sidebar styling */
54
- #sidebar {
55
- background-color: rgba(76, 175, 80, 0.05) !important;
56
- border-right: 2px solid rgba(76, 175, 80, 0.2) !important;
57
- padding: 20px !important;
58
- position: sticky !important;
59
- top: 20px !important;
60
- height: fit-content !important;
61
- border-radius: 15px !important;
62
- margin-right: 20px !important;
63
- }
64
-
65
- .sidebar-header {
66
- color: #0B3B0B !important;
67
- font-weight: bold !important;
68
- text-align: center !important;
69
- margin-bottom: 15px !important;
70
- font-size: 16px !important;
71
- }
72
-
73
- #sidebar button {
74
- width: 100% !important;
75
- margin-bottom: 10px !important;
76
- font-size: 12px !important;
77
- padding: 8px 12px !important;
78
- text-align: left !important;
79
- }
80
-
81
- /* Mobile responsive styles */
82
- @media (max-width: 768px) {
83
- .centered {
84
- padding: 20px;
85
- gap: 15px;
86
- }
87
-
88
- button, .gr-button {
89
- padding: 10px 20px !important;
90
- font-size: 14px !important;
91
- width: 100% !important;
92
- max-width: 280px !important;
93
- }
94
-
95
- #sidebar {
96
- padding: 15px !important;
97
- margin-right: 10px !important;
98
- position: relative !important;
99
- }
100
-
101
- .sidebar-header {
102
- font-size: 14px !important;
103
- margin-bottom: 10px !important;
104
- }
105
-
106
- #sidebar button {
107
- font-size: 11px !important;
108
- padding: 6px 10px !important;
109
- margin-bottom: 8px !important;
110
- }
111
- }
112
-
113
- @media (max-width: 480px) {
114
- #sidebar {
115
- display: none !important;
116
- }
117
-
118
- .centered {
119
- padding: 10px;
120
- gap: 12px;
121
- }
122
- }