yash202 commited on
Commit
b7a676b
·
1 Parent(s): 9d7fa73

Initial upload of Flask app with LFS and full project

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.jpg filter=lfs diff=lfs merge=lfs -text
37
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
38
+ *.png filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python image
2
+ FROM python:3.9-slim
3
+
4
+ # Set working directory
5
+ WORKDIR /app
6
+
7
+ # Install dependencies
8
+ COPY requirements.txt .
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ # Copy all files into the container
12
+ COPY . .
13
+
14
+ # Expose the port Flask will run on
15
+ EXPOSE 7860
16
+
17
+ # Run your Flask app
18
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ from PIL import Image
5
+ import pickle
6
+ import io
7
+ import os
8
+ import zipfile
9
+ import matplotlib.pyplot as plt
10
+ import logging
11
+
12
+ # Configure logging
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
+ logger = logging.getLogger(__name__)
15
+
16
+ app = Flask(__name__)
17
+
18
+ MODEL_ZIP = "skin_lesions_model.zip"
19
+ MODEL_PATH = "skin_lesion_model.h5"
20
+ HISTORY_PATH = "training_history.pkl"
21
+
22
+ # Extract model if not already extracted
23
+ if not os.path.exists(MODEL_PATH):
24
+ try:
25
+ logger.info("Extracting model from zip: %s", MODEL_ZIP)
26
+ with zipfile.ZipFile(MODEL_ZIP, 'r') as zip_ref:
27
+ zip_ref.extractall()
28
+ logger.info("Model extracted successfully.")
29
+ except Exception as e:
30
+ logger.error("Failed to extract model: %s", str(e))
31
+ raise
32
+
33
+ # Load model
34
+ try:
35
+ logger.info("Loading model from %s", MODEL_PATH)
36
+ model = tf.keras.models.load_model(MODEL_PATH)
37
+ except Exception as e:
38
+ logger.error("Failed to load model: %s", str(e))
39
+ raise
40
+
41
+ # Load training history for plotting
42
+ if os.path.exists(HISTORY_PATH):
43
+ try:
44
+ with open(HISTORY_PATH, "rb") as f:
45
+ history_dict = pickle.load(f)
46
+ logger.info("Loaded training history from %s", HISTORY_PATH)
47
+ except Exception as e:
48
+ logger.error("Failed to load training history: %s", str(e))
49
+ history_dict = {}
50
+ else:
51
+ logger.warning("Training history file %s not found", HISTORY_PATH)
52
+ history_dict = {}
53
+
54
+ # Plot accuracy history
55
+ if "accuracy" in history_dict and "val_accuracy" in history_dict:
56
+ try:
57
+ plt.plot(history_dict['accuracy'], label='Train Accuracy')
58
+ plt.plot(history_dict['val_accuracy'], label='Val Accuracy')
59
+ plt.xlabel('Epochs')
60
+ plt.ylabel('Accuracy')
61
+ plt.title('Training History')
62
+ plt.legend()
63
+ plt.grid(True)
64
+ os.makedirs("static", exist_ok=True)
65
+ plt.savefig("static/training_plot.png")
66
+ plt.close()
67
+ logger.info("Generated training history plot at static/training_plot.png")
68
+ except Exception as e:
69
+ logger.error("Failed to generate training plot: %s", str(e))
70
+
71
+ IMG_SIZE = (224, 224)
72
+ CONFIDENCE_THRESHOLD = 0.30
73
+
74
+ label_map = {
75
+ 0: "Melanoma",
76
+ 1: "Melanocytic nevus",
77
+ 2: "Basal cell carcinoma",
78
+ 3: "Actinic keratosis",
79
+ 4: "Benign keratosis",
80
+ 5: "Dermatofibroma",
81
+ 6: "Vascular lesion",
82
+ 7: "Squamous cell carcinoma"
83
+ }
84
+
85
+ def preprocess_image(image_bytes):
86
+ try:
87
+ image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
88
+ image = image.resize(IMG_SIZE)
89
+ image_array = tf.keras.utils.img_to_array(image)
90
+ image_array = np.expand_dims(image_array, axis=0)
91
+ return image_array / 255.0
92
+ except Exception as e:
93
+ logger.error("Failed to preprocess image: %s", str(e))
94
+ raise
95
+
96
+ @app.route("/form", methods=["GET"])
97
+ def form():
98
+ logger.info("Serving form page at /form")
99
+ return render_template("form.html", history_plot="/static/training_plot.png")
100
+
101
+ @app.route("/predict", methods=["POST"])
102
+ def predict():
103
+ logger.info("Received prediction request")
104
+ if "image" not in request.files:
105
+ logger.warning("No image uploaded in request")
106
+ return jsonify({"error": "No image uploaded"}), 400
107
+
108
+ try:
109
+ image = request.files["image"].read()
110
+ img_array = preprocess_image(image)
111
+
112
+ logger.info("Running model prediction")
113
+ prediction = model.predict(img_array)[0]
114
+ predicted_index = int(np.argmax(prediction))
115
+ confidence = float(prediction[predicted_index])
116
+
117
+ if confidence < CONFIDENCE_THRESHOLD:
118
+ result = {
119
+ "prediction": "Low confidence",
120
+ "confidence": f"{confidence * 100:.2f}%",
121
+ "message": "⚠ This image is not confidently recognized. Please upload a clearer image."
122
+ }
123
+ logger.info("Prediction: Low confidence (%.2f%%)", confidence * 100)
124
+ else:
125
+ result = {
126
+ "prediction": label_map.get(predicted_index, "Unknown"),
127
+ "confidence": f"{confidence * 100:.2f}%"
128
+ }
129
+ logger.info("Prediction: %s (Confidence: %.2f%%)", result["prediction"], confidence * 100)
130
+
131
+ return jsonify(result), 200
132
+ except Exception as e:
133
+ logger.error("Error processing image: %s", str(e))
134
+ return jsonify({"error": f"Error processing image: {str(e)}"}), 500
135
+
136
+ # Use fixed port for Hugging Face
137
+ if __name__ == "__main__":
138
+ app.run(host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ flask
2
+ tensorflow
3
+ numpy
4
+ Pillow
5
+ matplotlib
skin_lesion_model.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:91579e4e16ced55f3211edfc60b330b88c35730ee815038dc20272620328cd4c
3
+ size 186018725
static/cursor-effect.js ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Custom cursor effect
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ // Use default browser cursor as fallback
4
+ document.body.classList.remove('custom-cursor');
5
+
6
+ // Create cursor elements
7
+ const cursorGlow = document.createElement('div');
8
+ cursorGlow.classList.add('cursor-glow');
9
+ document.body.appendChild(cursorGlow);
10
+
11
+ // Create delayed follow circle
12
+ const cursorFollow = document.createElement('div');
13
+ cursorFollow.classList.add('cursor-follow');
14
+ document.body.appendChild(cursorFollow);
15
+
16
+ // Variables for storing mouse position
17
+ let mouseX = 0;
18
+ let mouseY = 0;
19
+ let cursorX = 0;
20
+ let cursorY = 0;
21
+
22
+ // Update mouse position on mouse move
23
+ document.addEventListener('mousemove', function(e) {
24
+ mouseX = e.clientX;
25
+ mouseY = e.clientY;
26
+
27
+ // Show the cursor glow and position it at the mouse coordinates
28
+ cursorGlow.style.opacity = '1';
29
+ cursorGlow.style.left = mouseX + 'px';
30
+ cursorGlow.style.top = mouseY + 'px';
31
+ });
32
+
33
+ // Animate the follow cursor with delay
34
+ function animateFollowCursor() {
35
+ // Calculate the distance between current position and target position
36
+ const dx = mouseX - cursorX;
37
+ const dy = mouseY - cursorY;
38
+
39
+ // Move the cursor a fraction of the distance (creates delay/easing effect)
40
+ cursorX += dx * 0.15;
41
+ cursorY += dy * 0.15;
42
+
43
+ // Apply the position
44
+ cursorFollow.style.left = cursorX + 'px';
45
+ cursorFollow.style.top = cursorY + 'px';
46
+
47
+ // Continue animation
48
+ requestAnimationFrame(animateFollowCursor);
49
+ }
50
+
51
+ // Start the animation
52
+ animateFollowCursor();
53
+
54
+ // Make cursor visible when mouse moves
55
+ document.addEventListener('mousemove', function() {
56
+ cursorFollow.style.opacity = '1';
57
+ cursorGlow.style.opacity = '0.9';
58
+ });
59
+
60
+ // Increase cursor glow size when hovering clickable elements
61
+ const clickableElements = document.querySelectorAll('a, button, input[type="submit"], .cta-button, .submit-button, .feature-card, .testimonial-card, .step-number, nav ul li a, .footer-links ul li a, input[type="checkbox"], input[type="radio"], select, .color-label, .checkbox-option');
62
+
63
+ clickableElements.forEach(element => {
64
+ element.addEventListener('mouseenter', function() {
65
+ cursorGlow.style.width = '70px';
66
+ cursorGlow.style.height = '70px';
67
+ cursorGlow.style.background = 'radial-gradient(circle, rgba(255, 143, 48, 0.6) 0%, rgba(255, 143, 48, 0) 70%)';
68
+ cursorFollow.style.borderColor = 'rgba(255, 143, 48, 1)';
69
+ cursorFollow.style.backgroundColor = 'rgba(255, 143, 48, 0.3)';
70
+ cursorFollow.style.width = '35px';
71
+ cursorFollow.style.height = '35px';
72
+ });
73
+
74
+ element.addEventListener('mouseleave', function() {
75
+ cursorGlow.style.width = '50px';
76
+ cursorGlow.style.height = '50px';
77
+ cursorGlow.style.background = 'radial-gradient(circle, rgba(55, 199, 255, 0.5) 0%, rgba(55, 199, 255, 0) 70%)';
78
+ cursorFollow.style.borderColor = 'rgba(55, 199, 255, 0.9)';
79
+ cursorFollow.style.backgroundColor = 'rgba(55, 199, 255, 0.2)';
80
+ cursorFollow.style.width = '25px';
81
+ cursorFollow.style.height = '25px';
82
+ });
83
+ });
84
+
85
+ // Hide cursor elements when mouse leaves the window
86
+ document.addEventListener('mouseout', function(e) {
87
+ if (e.relatedTarget === null) {
88
+ cursorGlow.style.opacity = '0';
89
+ cursorFollow.style.opacity = '0';
90
+ }
91
+ });
92
+
93
+ // Show cursor elements when mouse enters the window
94
+ document.addEventListener('mouseover', function() {
95
+ cursorGlow.style.opacity = '1';
96
+ cursorFollow.style.opacity = '1';
97
+ });
98
+
99
+ // Add cursor click effect
100
+ document.addEventListener('mousedown', function() {
101
+ cursorGlow.style.transform = 'translate(-50%, -50%) scale(0.9)';
102
+ cursorFollow.style.transform = 'translate(-50%, -50%) scale(0.5)';
103
+ });
104
+
105
+ document.addEventListener('mouseup', function() {
106
+ cursorGlow.style.transform = 'translate(-50%, -50%) scale(1)';
107
+ cursorFollow.style.transform = 'translate(-50%, -50%) scale(1)';
108
+ });
109
+ });
static/cursor.svg ADDED
static/dna-cursor-pointer.svg ADDED
static/form-handler.js ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const fileInput = document.getElementById('image');
3
+ const fileUploadText = document.querySelector('.file-upload-text');
4
+ const uploadMessage = document.getElementById('upload-message');
5
+ const form = document.getElementById('patient-form');
6
+ const submitBtn = document.getElementById('submit-btn');
7
+ const resultSection = document.getElementById('result-section');
8
+ const resultContent = document.getElementById('result-content');
9
+
10
+ // Handle file input change
11
+ fileInput.addEventListener('change', () => {
12
+ if (fileInput.files && fileInput.files.length > 0) {
13
+ const file = fileInput.files[0];
14
+ const validTypes = ['image/jpeg', 'image/jpg', 'image/png'];
15
+ const maxSize = 20 * 1024 * 1024; // 20MB in bytes
16
+
17
+ // Validate file type and size
18
+ if (!validTypes.includes(file.type)) {
19
+ alert('Please upload an image in JPG, JPEG, or PNG format.');
20
+ fileInput.value = '';
21
+ fileUploadText.textContent = 'Choose Image';
22
+ uploadMessage.style.display = 'none';
23
+ return;
24
+ }
25
+
26
+ if (file.size > maxSize) {
27
+ alert('File size exceeds 20MB. Please upload a smaller image.');
28
+ fileInput.value = '';
29
+ fileUploadText.textContent = 'Choose Image';
30
+ uploadMessage.style.display = 'none';
31
+ return;
32
+ }
33
+
34
+ // Update file upload text with file name
35
+ fileUploadText.textContent = file.name;
36
+
37
+ // Show upload successful message
38
+ uploadMessage.innerHTML = `
39
+ <span class="message-text">Upload Successful: ${file.name}</span>
40
+ <button class="close-btn" aria-label="Close message">×</button>
41
+ `;
42
+ uploadMessage.style.display = 'flex';
43
+ uploadMessage.classList.add('show');
44
+
45
+ // Add event listener to close button
46
+ const closeBtn = uploadMessage.querySelector('.close-btn');
47
+ closeBtn.addEventListener('click', () => {
48
+ uploadMessage.style.display = 'none';
49
+ uploadMessage.classList.remove('show');
50
+ });
51
+ } else {
52
+ fileUploadText.textContent = 'Choose Image';
53
+ uploadMessage.style.display = 'none';
54
+ uploadMessage.classList.remove('show');
55
+ }
56
+ });
57
+
58
+ // Handle form submission
59
+ form.addEventListener('submit', async (e) => {
60
+ e.preventDefault(); // Prevent default form submission
61
+
62
+ // Disable submit button and show loading state
63
+ submitBtn.disabled = true;
64
+ submitBtn.textContent = 'Processing...';
65
+ resultSection.style.display = 'none';
66
+ resultContent.innerHTML = '';
67
+
68
+ try {
69
+ const formData = new FormData(form);
70
+
71
+ const response = await fetch('/predict', {
72
+ method: 'POST',
73
+ body: formData
74
+ });
75
+
76
+ const data = await response.json();
77
+
78
+ // Display result
79
+ resultSection.style.display = 'block';
80
+ if (data.error) {
81
+ resultContent.innerHTML = `
82
+ <div class="result-error">
83
+ <p>${data.error}</p>
84
+ </div>
85
+ `;
86
+ } else if (data.prediction === 'Low confidence') {
87
+ resultContent.innerHTML = `
88
+ <div class="result-warning">
89
+ <p><strong>Warning:</strong> ${data.message}</p>
90
+ <p><strong>Confidence:</strong> ${data.confidence}</p>
91
+ </div>
92
+ `;
93
+ } else {
94
+ resultContent.innerHTML = `
95
+ <div class="result-success">
96
+ <p><strong>Prediction:</strong> ${data.prediction}</p>
97
+ <p><strong>Confidence:</strong> ${data.confidence}</p>
98
+ </div>
99
+ `;
100
+ }
101
+ } catch (error) {
102
+ console.error('Error:', error);
103
+ resultSection.style.display = 'block';
104
+ resultContent.innerHTML = `
105
+ <div class="result-error">
106
+ <p>An error occurred while processing your request. Please try again.</p>
107
+ </div>
108
+ `;
109
+ } finally {
110
+ // Re-enable submit button
111
+ submitBtn.disabled = false;
112
+ submitBtn.textContent = 'Submit Details';
113
+ }
114
+ });
115
+ });
static/form-styles.css ADDED
@@ -0,0 +1,855 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Global Styles */
2
+ :root {
3
+ --primary-color: #7e54ff;
4
+ --secondary-color: #6c43ff;
5
+ --accent-color: #ff54b1;
6
+ --background-color: #0a0a14;
7
+ --dark-color: #050510;
8
+ --light-color: #151525;
9
+ --gray-color: #1a1a2a;
10
+ --text-color: #e0e0e0;
11
+ --border-color: #333333;
12
+ --success-color: #4caf50;
13
+ --error-color: #f44336;
14
+ }
15
+
16
+ * {
17
+ margin: 0;
18
+ padding: 0;
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ .custom-cursor, .custom-cursor * {
23
+ cursor: none !important;
24
+ }
25
+
26
+ body:not(.custom-cursor) {
27
+ cursor: url('dna-cursor.svg'), auto;
28
+ }
29
+
30
+ html {
31
+ scroll-behavior: smooth;
32
+ }
33
+
34
+ body {
35
+ font-family: 'Roboto', sans-serif;
36
+ line-height: 1.6;
37
+ color: var(--text-color);
38
+ background-color: var(--background-color);
39
+ position: relative;
40
+ }
41
+
42
+ /* Custom Cursor Styles */
43
+ body:not(.custom-cursor) a,
44
+ body:not(.custom-cursor) button,
45
+ body:not(.custom-cursor) input[type="submit"],
46
+ body:not(.custom-cursor) .submit-button,
47
+ body:not(.custom-cursor) input[type="checkbox"],
48
+ body:not(.custom-cursor) select,
49
+ body:not(.custom-cursor) .step-icon,
50
+ body:not(.custom-cursor) nav ul li a,
51
+ body:not(.custom-cursor) .footer-links ul li a {
52
+ cursor: url('dna-cursor-pointer.svg'), pointer;
53
+ }
54
+
55
+ /* Custom Cursor Effects */
56
+ .cursor-glow {
57
+ position: fixed;
58
+ width: 50px;
59
+ height: 50px;
60
+ border-radius: 50%;
61
+ pointer-events: none;
62
+ z-index: 9999;
63
+ opacity: 0.6;
64
+ background: radial-gradient(circle, rgba(55, 199, 255, 0.5) 0%, rgba(55, 199, 255, 0) 70%);
65
+ transform: translate(-50%, -50%);
66
+ transition: opacity 0.3s ease, width 0.3s ease, height 0.3s ease;
67
+ }
68
+
69
+ .cursor-follow {
70
+ position: fixed;
71
+ width: 25px;
72
+ height: 25px;
73
+ border-radius: 50%;
74
+ pointer-events: none;
75
+ z-index: 9998;
76
+ opacity: 0.8;
77
+ border: 3px solid rgba(55, 199, 255, 0.9);
78
+ background-color: rgba(55, 199, 255, 0.2);
79
+ transform: translate(-50%, -50%);
80
+ transition: width 0.3s ease, height 0.3s ease, border-color 0.3s ease, transform 0.2s ease;
81
+ }
82
+
83
+ /* Preloader */
84
+ .preloader {
85
+ position: fixed;
86
+ top: 0;
87
+ left: 0;
88
+ width: 100%;
89
+ height: 100%;
90
+ background-color: var(--dark-color);
91
+ display: flex;
92
+ justify-content: center;
93
+ align-items: center;
94
+ z-index: 9999;
95
+ transition: opacity 0.5s ease, visibility 0.5s ease;
96
+ }
97
+
98
+ .preloader.hidden {
99
+ opacity: 0;
100
+ visibility: hidden;
101
+ }
102
+
103
+ .dna-loader {
104
+ position: relative;
105
+ width: 100px;
106
+ height: 200px;
107
+ }
108
+
109
+ .dna-loader-strand {
110
+ position: absolute;
111
+ width: 2px;
112
+ height: 200px;
113
+ background-color: rgba(0, 80, 180, 0.3);
114
+ left: 30px;
115
+ transform: rotate(5deg);
116
+ }
117
+
118
+ .dna-loader-strand:nth-child(2) {
119
+ left: 70px;
120
+ transform: rotate(-5deg);
121
+ }
122
+
123
+ .dna-loader-rung {
124
+ position: absolute;
125
+ width: 40px;
126
+ height: 4px;
127
+ left: 30px;
128
+ background: linear-gradient(to right, #37c7ff, #ff8f30);
129
+ box-shadow: 0 0 10px rgba(55, 199, 255, 0.5);
130
+ transform-origin: center;
131
+ animation: dnaLoaderSpin 1.5s infinite ease-in-out;
132
+ }
133
+
134
+ .dna-loader-rung:nth-child(3) { top: 10%; animation-delay: -0.2s; }
135
+ .dna-loader-rung:nth-child(4) { top: 20%; animation-delay: -0.4s; }
136
+ .dna-loader-rung:nth-child(5) { top: 30%; animation-delay: -0.6s; }
137
+ .dna-loader-rung:nth-child(6) { top: 40%; animation-delay: -0.8s; }
138
+ .dna-loader-rung:nth-child(7) { top: 50%; animation-delay: -1.0s; }
139
+ .dna-loader-rung:nth-child(8) { top: 60%; animation-delay: -1.2s; }
140
+ .dna-loader-rung:nth-child(9) { top: 70%; animation-delay: -1.4s; }
141
+ .dna-loader-rung:nth-child(10) { top: 80%; animation-delay: -1.6s; }
142
+ .dna-loader-rung:nth-child(11) { top: 90%; animation-delay: -1.8s; }
143
+
144
+ @keyframes dnaLoaderSpin {
145
+ 0%, 100% {
146
+ transform: rotate(0deg) scaleX(1);
147
+ }
148
+ 50% {
149
+ transform: rotate(180deg) scaleX(0.6);
150
+ }
151
+ }
152
+
153
+ .loader-text {
154
+ position: absolute;
155
+ bottom: -40px;
156
+ font-size: 1.2rem;
157
+ color: var(--text-color);
158
+ font-weight: 300;
159
+ letter-spacing: 2px;
160
+ animation: loaderTextPulse 1.5s infinite;
161
+ }
162
+
163
+ @keyframes loaderTextPulse {
164
+ 0%, 100% {
165
+ opacity: 0.7;
166
+ }
167
+ 50% {
168
+ opacity: 1;
169
+ }
170
+ }
171
+
172
+ .preloader-particles {
173
+ position: absolute;
174
+ width: 100%;
175
+ height: 100%;
176
+ background-image:
177
+ radial-gradient(circle at 20% 30%, rgba(55, 199, 255, 0.4) 0%, transparent 10%),
178
+ radial-gradient(circle at 70% 60%, rgba(255, 143, 48, 0.4) 0%, transparent 10%),
179
+ radial-gradient(circle at 40% 80%, rgba(55, 199, 255, 0.4) 0%, transparent 10%),
180
+ radial-gradient(circle at 80% 40%, rgba(255, 143, 48, 0.4) 0%, transparent 10%);
181
+ opacity: 0.2;
182
+ filter: blur(2px);
183
+ animation: preloaderParticleFloat 5s ease-in-out infinite alternate;
184
+ }
185
+
186
+ @keyframes preloaderParticleFloat {
187
+ 0% {
188
+ background-position: 0 0, 0 0, 0 0, 0 0;
189
+ }
190
+ 100% {
191
+ background-position: 20px 20px, -20px 20px, 20px -20px, -20px -20px;
192
+ }
193
+ }
194
+
195
+ .container {
196
+ width: 100%;
197
+ max-width: 1200px;
198
+ margin: 0 auto;
199
+ padding: 0 20px;
200
+ }
201
+
202
+ h1, h2, h3, h4, h5, h6 {
203
+ margin-bottom: 20px;
204
+ line-height: 1.2;
205
+ }
206
+
207
+ p {
208
+ margin-bottom: 15px;
209
+ }
210
+
211
+ a {
212
+ text-decoration: none;
213
+ color: var(--primary-color);
214
+ transition: all 0.3s ease;
215
+ }
216
+
217
+ a:hover {
218
+ color: var(--secondary-color);
219
+ }
220
+
221
+ /* Header & Navigation */
222
+ header {
223
+ background-color: var(--dark-color);
224
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
225
+ position: fixed;
226
+ top: 0;
227
+ left: 0;
228
+ width: 100%;
229
+ z-index: 1000;
230
+ }
231
+
232
+ nav {
233
+ display: flex;
234
+ justify-content: space-between;
235
+ align-items: center;
236
+ padding: 20px;
237
+ max-width: 1200px;
238
+ margin: 0 auto;
239
+ }
240
+
241
+ .logo {
242
+ font-size: 1.8rem;
243
+ font-weight: 700;
244
+ color: var(--primary-color);
245
+ text-shadow: 0 0 8px rgba(126, 84, 255, 0.5);
246
+ }
247
+
248
+ .logo span {
249
+ color: var(--accent-color);
250
+ text-shadow: 0 0 8px rgba(255, 84, 177, 0.5);
251
+ }
252
+
253
+ nav ul {
254
+ display: flex;
255
+ list-style: none;
256
+ }
257
+
258
+ nav ul li {
259
+ margin-left: 30px;
260
+ }
261
+
262
+ nav ul li a {
263
+ color: var(--text-color);
264
+ font-weight: 500;
265
+ position: relative;
266
+ }
267
+
268
+ nav ul li a:after {
269
+ content: '';
270
+ position: absolute;
271
+ width: 0;
272
+ height: 2px;
273
+ background: var(--primary-color);
274
+ left: 0;
275
+ bottom: -5px;
276
+ transition: width 0.3s ease;
277
+ }
278
+
279
+ nav ul li a:hover:after {
280
+ width: 100%;
281
+ }
282
+
283
+ /* Form Section */
284
+ .form-section {
285
+ padding: 100px 0 60px;
286
+ background: linear-gradient(135deg, #050510 0%, #0c0c1a 100%);
287
+ position: relative;
288
+ overflow: hidden;
289
+ }
290
+
291
+ .form-section::before {
292
+ content: '';
293
+ position: absolute;
294
+ top: 0;
295
+ left: 0;
296
+ width: 100%;
297
+ height: 100%;
298
+ background: radial-gradient(circle at 30% 50%, rgba(0, 80, 180, 0.08) 0%, rgba(10, 10, 20, 0) 70%);
299
+ pointer-events: none;
300
+ }
301
+
302
+ .form-section .container {
303
+ display: flex;
304
+ flex-wrap: wrap;
305
+ gap: 40px;
306
+ position: relative;
307
+ z-index: 2;
308
+ }
309
+
310
+ .form-container {
311
+ flex: 2;
312
+ min-width: 300px;
313
+ background-color: var(--dark-color);
314
+ border-radius: 10px;
315
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
316
+ border: 1px solid rgba(126, 84, 255, 0.1);
317
+ padding: 40px;
318
+ animation: fadeIn 0.8s ease;
319
+ }
320
+
321
+ .side-info {
322
+ flex: 1;
323
+ min-width: 300px;
324
+ display: flex;
325
+ flex-direction: column;
326
+ gap: 30px;
327
+ }
328
+
329
+ .info-card {
330
+ background-color: var(--dark-color);
331
+ border-radius: 10px;
332
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
333
+ border: 1px solid rgba(126, 84, 255, 0.1);
334
+ padding: 30px;
335
+ animation: fadeIn 1s ease;
336
+ }
337
+
338
+ @keyframes fadeIn {
339
+ from {
340
+ opacity: 0;
341
+ transform: translateY(20px);
342
+ }
343
+ to {
344
+ opacity: 1;
345
+ transform: translateY(0);
346
+ }
347
+ }
348
+
349
+ .form-container h1 {
350
+ font-size: 2.2rem;
351
+ color: var(--primary-color);
352
+ margin-bottom: 20px;
353
+ text-align: center;
354
+ text-shadow: 0 0 10px rgba(126, 84, 255, 0.3);
355
+ }
356
+
357
+ .intro-text {
358
+ text-align: center;
359
+ color: #aaaaaa;
360
+ margin-bottom: 30px;
361
+ max-width: 700px;
362
+ margin-left: auto;
363
+ margin-right: auto;
364
+ }
365
+
366
+ .form-wrapper {
367
+ max-width: 800px;
368
+ margin: 0 auto;
369
+ }
370
+
371
+ /* Form Elements */
372
+ .form-group {
373
+ margin-bottom: 25px;
374
+ }
375
+
376
+ label {
377
+ display: block;
378
+ margin-bottom: 8px;
379
+ font-weight: 500;
380
+ color: var(--text-color);
381
+ }
382
+
383
+ input[type="text"],
384
+ input[type="email"],
385
+ input[type="number"],
386
+ input[type="file"],
387
+ select {
388
+ width: 100%;
389
+ padding: 12px 15px;
390
+ border: 1px solid var(--border-color);
391
+ border-radius: 5px;
392
+ font-size: 1rem;
393
+ transition: all 0.3s ease;
394
+ background-color: rgba(42, 42, 42, 0.7);
395
+ color: var(--text-color);
396
+ }
397
+
398
+ input[type="text"]:focus,
399
+ input[type="email"]:focus,
400
+ input[type="number"]:focus,
401
+ input[type="file"]:focus,
402
+ select:focus {
403
+ border-color: var(--primary-color);
404
+ box-shadow: 0 0 0 3px rgba(126, 84, 255, 0.2);
405
+ outline: none;
406
+ background-color: rgba(42, 42, 42, 0.9);
407
+ }
408
+
409
+ select {
410
+ appearance: none;
411
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23e0e0e0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
412
+ background-repeat: no-repeat;
413
+ background-position: right 10px center;
414
+ background-size: 16px;
415
+ }
416
+
417
+ small.warning {
418
+ display: block;
419
+ color: var(--error-color);
420
+ margin-top: 8px;
421
+ font-size: 0.9rem;
422
+ font-weight: 500;
423
+ background: rgba(244, 67, 54, 0.1);
424
+ padding: 5px 10px;
425
+ border-radius: 3px;
426
+ border-left: 3px solid var(--error-color);
427
+ }
428
+
429
+ .form-row {
430
+ display: flex;
431
+ gap: 20px;
432
+ flex-wrap: wrap;
433
+ }
434
+
435
+ .form-group.half {
436
+ flex: 1;
437
+ min-width: 200px;
438
+ }
439
+
440
+ /* Consent Group */
441
+ .consent-group {
442
+ display: flex;
443
+ align-items: flex-start;
444
+ gap: 10px;
445
+ }
446
+
447
+ .consent-group input[type="checkbox"] {
448
+ margin-top: 4px;
449
+ width: 18px;
450
+ height: 18px;
451
+ accent-color: var(--primary-color);
452
+ }
453
+
454
+ /* Submit Button */
455
+ .form-actions {
456
+ text-align: center;
457
+ margin-top: 40px;
458
+ }
459
+
460
+ .submit-button {
461
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
462
+ color: #ffffff;
463
+ border: none;
464
+ padding: 15px 40px;
465
+ font-size: 1.1rem;
466
+ font-weight: 500;
467
+ border-radius: 50px;
468
+ cursor: pointer;
469
+ transition: all 0.3s ease;
470
+ box-shadow: 0 0 20px rgba(126, 84, 255, 0.5);
471
+ position: relative;
472
+ overflow: hidden;
473
+ }
474
+
475
+ .submit-button::before {
476
+ content: '';
477
+ position: absolute;
478
+ top: -50%;
479
+ left: -50%;
480
+ width: 200%;
481
+ height: 200%;
482
+ background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
483
+ transform: rotate(45deg);
484
+ transition: all 0.5s ease;
485
+ pointer-events: none;
486
+ }
487
+
488
+ .submit-button:hover {
489
+ transform: translateY(-3px);
490
+ box-shadow: 0 0 30px rgba(126, 84, 255, 0.7);
491
+ }
492
+
493
+ .submit-button:hover::before {
494
+ left: 100%;
495
+ }
496
+
497
+ /* Upload Message */
498
+ .upload-message {
499
+ display: flex;
500
+ align-items: center;
501
+ justify-content: space-between;
502
+ margin-top: 10px;
503
+ padding: 10px 15px;
504
+ background: rgba(76, 175, 80, 0.1);
505
+ border: 1px solid var(--success-color);
506
+ border-radius: 5px;
507
+ color: var(--success-color);
508
+ font-size: 0.9rem;
509
+ font-weight: 500;
510
+ animation: slideIn 0.3s ease;
511
+ position: relative;
512
+ }
513
+
514
+ .upload-message.show {
515
+ display: flex;
516
+ }
517
+
518
+ .upload-message .message-text {
519
+ flex-grow: 1;
520
+ }
521
+
522
+ .upload-message .close-btn {
523
+ background: none;
524
+ border: none;
525
+ color: var(--success-color);
526
+ font-size: 1rem;
527
+ cursor: pointer;
528
+ padding: 0 5px;
529
+ transition: color 0.3s ease;
530
+ }
531
+
532
+ .upload-message .close-btn:hover {
533
+ color: #ffffff;
534
+ }
535
+
536
+ @keyframes slideIn {
537
+ from {
538
+ opacity: 0;
539
+ transform: translateY(-10px);
540
+ }
541
+ to {
542
+ opacity: 1;
543
+ transform: translateY(0);
544
+ }
545
+ }
546
+
547
+ /* Side Info Styles */
548
+ .info-card h2 {
549
+ color: var(--primary-color);
550
+ font-size: 1.5rem;
551
+ margin-bottom: 20px;
552
+ text-shadow: 0 0 10px rgba(126, 84, 255, 0.3);
553
+ }
554
+
555
+ .steps {
556
+ display: flex;
557
+ flex-direction: column;
558
+ gap: 20px;
559
+ }
560
+
561
+ .step {
562
+ display: flex;
563
+ align-items: flex-start;
564
+ gap: 15px;
565
+ }
566
+
567
+ .step-icon {
568
+ width: 35px;
569
+ height: 35px;
570
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
571
+ color: #ffffff;
572
+ border-radius: 50%;
573
+ display: flex;
574
+ align-items: center;
575
+ justify-content: center;
576
+ font-weight: 700;
577
+ flex-shrink: 0;
578
+ box-shadow: 0 0 15px rgba(126, 84, 255, 0.4);
579
+ }
580
+
581
+ .step-content h3 {
582
+ font-size: 1.1rem;
583
+ margin-bottom: 5px;
584
+ color: var(--primary-color);
585
+ text-shadow: 0 0 5px rgba(126, 84, 255, 0.3);
586
+ }
587
+
588
+ .step-content p {
589
+ color: #aaaaaa;
590
+ font-size: 0.95rem;
591
+ }
592
+
593
+ .contact-info {
594
+ margin-top: 20px;
595
+ }
596
+
597
+ .contact-item {
598
+ display: flex;
599
+ align-items: center;
600
+ gap: 10px;
601
+ margin-bottom: 15px;
602
+ color: #aaaaaa;
603
+ }
604
+
605
+ .contact-item svg {
606
+ color: var(--primary-color);
607
+ filter: drop-shadow(0 0 5px rgba(126, 84, 255, 0.5));
608
+ }
609
+
610
+ /* Footer */
611
+ footer {
612
+ background-color: var(--dark-color);
613
+ color: var(--text-color);
614
+ padding: 60px 0 20px;
615
+ border-top: 1px solid rgba(126, 84, 255, 0.2);
616
+ position: relative;
617
+ overflow: hidden;
618
+ }
619
+
620
+ footer::before {
621
+ content: '';
622
+ position: absolute;
623
+ top: 0;
624
+ left: 0;
625
+ width: 100%;
626
+ height: 100%;
627
+ background: radial-gradient(circle at 20% 80%, rgba(126, 84, 255, 0.05) 0%, rgba(0, 0, 0, 0) 70%);
628
+ pointer-events: none;
629
+ }
630
+
631
+ .footer-content {
632
+ display: flex;
633
+ flex-wrap: wrap;
634
+ justify-content: space-between;
635
+ margin-bottom: 40px;
636
+ position: relative;
637
+ z-index: 2;
638
+ }
639
+
640
+ .footer-logo {
641
+ font-size: 1.8rem;
642
+ font-weight: 700;
643
+ color: var(--text-color);
644
+ margin-bottom: 20px;
645
+ text-shadow: 0 0 8px rgba(126, 84, 255, 0.5);
646
+ }
647
+
648
+ .footer-logo span {
649
+ color: var(--accent-color);
650
+ text-shadow: 0 0 8px rgba(255, 84, 177, 0.5);
651
+ }
652
+
653
+ .footer-links, .footer-contact {
654
+ margin-bottom: 20px;
655
+ }
656
+
657
+ .footer-links h3, .footer-contact h3 {
658
+ font-size: 1.3rem;
659
+ margin-bottom: 15px;
660
+ color: var(--primary-color);
661
+ text-shadow: 0 0 5px rgba(126, 84, 255, 0.3);
662
+ }
663
+
664
+ .footer-links ul {
665
+ list-style: none;
666
+ }
667
+
668
+ .footer-links ul li {
669
+ margin-bottom: 10px;
670
+ }
671
+
672
+ .footer-links ul li a {
673
+ color: rgba(224, 224, 224, 0.7);
674
+ transition: all 0.3s ease;
675
+ position: relative;
676
+ }
677
+
678
+ .footer-links ul li a::before {
679
+ content: '';
680
+ position: absolute;
681
+ bottom: -2px;
682
+ left: 0;
683
+ width: 0;
684
+ height: 1px;
685
+ background-color: var(--primary-color);
686
+ transition: width 0.3s ease;
687
+ }
688
+
689
+ .footer-links ul li a:hover {
690
+ color: var(--primary-color);
691
+ }
692
+
693
+ .footer-links ul li a:hover::before {
694
+ width: 100%;
695
+ }
696
+
697
+ .footer-contact p {
698
+ color: rgba(224, 224, 224, 0.7);
699
+ margin-bottom: 10px;
700
+ }
701
+
702
+ .copyright {
703
+ text-align: center;
704
+ padding-top: 20px;
705
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
706
+ color: rgba(224, 224, 224, 0.4);
707
+ position: relative;
708
+ z-index: 2;
709
+ }
710
+
711
+ /* Responsive Styles */
712
+ @media (max-width: 992px) {
713
+ .form-section .container {
714
+ flex-direction: column;
715
+ }
716
+
717
+ .form-container, .side-info {
718
+ width: 100%;
719
+ }
720
+ }
721
+
722
+ @media (max-width: 768px) {
723
+ nav {
724
+ flex-direction: column;
725
+ }
726
+
727
+ nav ul {
728
+ margin-top: 20px;
729
+ }
730
+
731
+ nav ul li {
732
+ margin: 0 10px;
733
+ }
734
+
735
+ .form-section {
736
+ padding-top: 150px;
737
+ }
738
+ }
739
+
740
+ @media (max-width: 576px) {
741
+ .form-container {
742
+ padding: 25px;
743
+ }
744
+
745
+ .form-section {
746
+ padding: 120px 0 50px;
747
+ }
748
+
749
+ .form-container h1 {
750
+ font-size: 1.8rem;
751
+ }
752
+ }
753
+
754
+ /* File Upload Styles */
755
+ .file-upload-group {
756
+ position: relative;
757
+ }
758
+
759
+ .file-upload-wrapper {
760
+ position: relative;
761
+ display: flex;
762
+ align-items: center;
763
+ background-color: rgba(42, 42, 42, 0.7);
764
+ border: 1px solid var(--border-color);
765
+ border-radius: 5px;
766
+ padding: 12px 15px;
767
+ transition: all 0.3s ease;
768
+ }
769
+
770
+ .file-upload-wrapper:hover,
771
+ .file-upload-wrapper:focus-within {
772
+ border-color: var(--primary-color);
773
+ box-shadow: 0 0 0 3px rgba(126, 84, 255, 0.2);
774
+ background-color: rgba(42, 42, 42, 0.9);
775
+ }
776
+
777
+ .file-upload-wrapper input[type="file"] {
778
+ opacity: 0;
779
+ position: absolute;
780
+ width: 100%;
781
+ height: 100%;
782
+ top: 0;
783
+ left: 0;
784
+ cursor: pointer;
785
+ }
786
+
787
+ .file-upload-text {
788
+ flex-grow: 1;
789
+ color: var(--text-color);
790
+ font-size: 1rem;
791
+ font-weight: 400;
792
+ }
793
+
794
+ .file-upload-icon {
795
+ display: flex;
796
+ align-items: center;
797
+ justify-content: center;
798
+ width: 24px;
799
+ height: 24px;
800
+ color: var(--primary-color);
801
+ filter: drop-shadow(0 0 5px rgba(126, 84, 255, 0.5));
802
+ }
803
+
804
+ .file-upload-icon svg {
805
+ width: 20px;
806
+ height: 20px;
807
+ }
808
+
809
+ /* Result Section Styles */
810
+ .result-section {
811
+ background-color: rgba(42, 42, 42, 0.7);
812
+ border-radius: 10px;
813
+ padding: 20px;
814
+ border: 1px solid rgba(126, 84, 255, 0.1);
815
+ animation: slideIn 0.3s ease;
816
+ }
817
+
818
+ .result-section h2 {
819
+ color: var(--primary-color);
820
+ font-size: 1.5rem;
821
+ margin-bottom: 15px;
822
+ text-shadow: 0 0 10px rgba(126, 84, 255, 0.3);
823
+ }
824
+
825
+ .result-success, .result-warning, .result-error {
826
+ padding: 15px;
827
+ border-radius: 5px;
828
+ font-size: 1rem;
829
+ color: var(--text-color);
830
+ }
831
+
832
+ .result-success {
833
+ background: rgba(76, 175, 80, 0.1);
834
+ border: 1px solid var(--success-color);
835
+ }
836
+
837
+ .result-warning {
838
+ background: rgba(255, 193, 7, 0.1);
839
+ border: 1px solid #ffc107;
840
+ color: #ffc107;
841
+ }
842
+
843
+ .result-error {
844
+ background: rgba(244, 67, 54, 0.1);
845
+ border: 1px solid var(--error-color);
846
+ color: var(--error-color);
847
+ }
848
+
849
+ .result-success p, .result-warning p, .result-error p {
850
+ margin-bottom: 10px;
851
+ }
852
+
853
+ .result-success p strong, .result-warning p strong, .result-error p strong {
854
+ color: inherit;
855
+ }
static/logo.jpg ADDED

Git LFS Details

  • SHA256: 1747168eaf0be324b5cf8aea7c229805e0af3ac008e526cb8d01e9e5aa78ae48
  • Pointer size: 131 Bytes
  • Size of remote file: 127 kB
static/preloader.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Preloader Script
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ // Get preloader element
4
+ const preloader = document.querySelector('.preloader');
5
+
6
+ // Function to hide preloader
7
+ function hidePreloader() {
8
+ preloader.classList.add('hidden');
9
+
10
+ // Remove preloader from DOM after transition completes
11
+ setTimeout(() => {
12
+ preloader.style.display = 'none';
13
+ }, 500);
14
+ }
15
+
16
+ // Hide preloader after all content is loaded
17
+ window.addEventListener('load', function() {
18
+ // Add a small delay for visual effect
19
+ setTimeout(hidePreloader, 1500);
20
+ });
21
+
22
+ // Fallback - hide preloader after 5 seconds even if content isn't fully loaded
23
+ setTimeout(hidePreloader, 5000);
24
+ });
templates/form.html ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Skin-Lesion-Diagnosis</title>
7
+ <link rel="stylesheet" href="/static/form-styles.css">
8
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
9
+ <script src="/static/preloader.js"></script>
10
+ <script src="/static/cursor-effect.js"></script>
11
+ <script src="/static/form-handler.js" defer></script>
12
+ <link href="/static/logo.jpg" rel="shortcut icon">
13
+ </head>
14
+ <body>
15
+ <div class="preloader">
16
+ <div class="preloader-particles"></div>
17
+ <div class="dna-loader">
18
+ <div class="dna-loader-strand"></div>
19
+ <div class="dna-loader-strand"></div>
20
+ <div class="dna-loader-rung"></div>
21
+ <div class="dna-loader-rung"></div>
22
+ <div class="dna-loader-rung"></div>
23
+ <div class="dna-loader-rung"></div>
24
+ <div class="dna-loader-rung"></div>
25
+ <div class="dna-loader-rung"></div>
26
+ <div class="dna-loader-rung"></div>
27
+ <div class="dna-loader-rung"></div>
28
+ <div class="dna-loader-rung"></div>
29
+ <div class="loader-text">LOADING...</div>
30
+ </div>
31
+ </div>
32
+ <header>
33
+ <nav>
34
+ <div class="logo" style="color: beige;">SKIN <span style="color: #00ffff;">ALGNOSIS</span></div>
35
+ <ul>
36
+ <li><a href="/templates/form.html">Home</a></li>
37
+ <li><a href="/templates/about.html">About</a></li>
38
+ <li><a href="/templates/features.html">Features</a></li>
39
+ </ul>
40
+ </nav>
41
+ </header>
42
+
43
+ <section class="form-section">
44
+ <div class="container">
45
+ <div class="form-container">
46
+ <h1 style="color: white;">ENTER PATIENT DETAILS</h1>
47
+ <p class="intro-text">Fill out the form below for the diagnosis of your skin disease. Our AI will process your image and generate a predicted report for you.</p>
48
+
49
+ <div class="form-wrapper">
50
+ <form id="patient-form" action="/predict" method="POST" enctype="multipart/form-data">
51
+ <div class="form-group">
52
+ <label for="name">Full Name</label>
53
+ <input type="text" id="name" name="name" placeholder="Enter your name" required>
54
+ </div>
55
+
56
+ <div class="form-group">
57
+ <label for="email">Email Address</label>
58
+ <input type="email" id="email" name="email" placeholder="Enter your email address" required>
59
+ </div>
60
+
61
+ <div class="form-row">
62
+ <div class="form-group half">
63
+ <label for="gender">Gender</label>
64
+ <select id="gender" name="gender" required>
65
+ <option value="" disabled selected>Select gender</option>
66
+ <option value="male">Male</option>
67
+ <option value="female">Female</option>
68
+ <option value="other">Other</option>
69
+ </select>
70
+ </div>
71
+
72
+ <div class="form-group half">
73
+ <label for="age">Age</label>
74
+ <input type="number" id="age" name="age" placeholder="Enter your age" min="1" max="120" required>
75
+ </div>
76
+ </div>
77
+
78
+ <div class="form-group file-upload-group">
79
+ <label for="image">Upload Image</label>
80
+ <div class="file-upload-wrapper">
81
+ <input type="file" id="image" name="image" accept=".jpg,.jpeg,.png" required>
82
+ <span class="file-upload-text">Choose Image</span>
83
+ <span class="file-upload-icon">
84
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
85
+ </span>
86
+ </div>
87
+ <small class="warning">Warning: Please upload images in JPG, JPEG, or PNG format only. Maximum file size: 20MB.</small>
88
+ <div class="upload-message" id="upload-message" style="display: none;"></div>
89
+ </div>
90
+
91
+ <div class="form-group consent-group">
92
+ <input type="checkbox" id="consent" name="consent" required>
93
+ <label for="consent">I agree to the <a href="#">Terms of Service</a> and <a href="#">Privacy Policy</a></label>
94
+ </div>
95
+
96
+ <div class="form-actions">
97
+ <button type="submit" class="submit-button" id="submit-btn">Submit Details</button>
98
+ </div>
99
+ </form>
100
+
101
+ <!-- Result Display Section -->
102
+ <div class="result-section" id="result-section" style="display: none; margin-top: 30px;">
103
+ <h2>Diagnosis Result</h2>
104
+ <div id="result-content"></div>
105
+ </div>
106
+
107
+ <!-- Training History Plot -->
108
+ {% if history_plot %}
109
+ <div class="history-plot" style="margin-top: 30px; text-align: center;">
110
+ <h2>Model Training History</h2>
111
+ <img src="{{ history_plot }}" alt="Training History Plot" style="max-width: 100%; border-radius: 10px;">
112
+ </div>
113
+ {% endif %}
114
+ </div>
115
+ </div>
116
+
117
+ <div class="side-info">
118
+ <div class="info-card">
119
+ <h2>How It Works</h2>
120
+ <div class="steps">
121
+ <div class="step">
122
+ <div class="step-icon">1</div>
123
+ <div class="step-content">
124
+ <h3>Submit Your Data</h3>
125
+ <p>Fill out the form with your details and image.</p>
126
+ </div>
127
+ </div>
128
+
129
+ <div class="step">
130
+ <div class="step-icon">2</div>
131
+ <div class="step-content">
132
+ <h3>AI Processing</h3>
133
+ <p>Our AI model analyzes your image.</p>
134
+ </div>
135
+ </div>
136
+
137
+ <div class="step">
138
+ <div class="step-icon">3</div>
139
+ <div class="step-content">
140
+ <h3>Receive Your Report</h3>
141
+ <p>Get your diagnosis report on the website.</p>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ <div class="info-card">
148
+ <h2>Need Help?</h2>
149
+ <p>If you have any questions or need assistance, our support team is here to help.</p>
150
+ <div class="contact-info">
151
+ <div class="contact-item">
152
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
153
+ <span>info.skinalgnosiss@gmail.com</span>
154
+ </div>
155
+ <div class="contact-item">
156
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path></svg>
157
+ <span>7817833974</span>
158
+ <span>7983595318</span>
159
+ </div>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </section>
165
+
166
+ <footer>
167
+ <div class="container">
168
+ <div class="footer-content">
169
+ <div class="footer-logo">CODE<span>CATALYST</span></div>
170
+ <div class="footer-links">
171
+ <h3>Quick Links</h3>
172
+ <ul>
173
+ <li><a href="/templates/form.html">Home</a></li>
174
+ <li><a href="/templates/about.html">About</a></li>
175
+ <li><a href="/templates/features.html">Features</a></li>
176
+ </ul>
177
+ </div>
178
+ <div class="footer-contact">
179
+ <h3>Support</h3>
180
+ <p>Email: info.skinalgnosiss@gmail.com</p>
181
+ <p>Phone: 7817833974</p>
182
+ <p>7983595318</p>
183
+ </div>
184
+ </div>
185
+ <div class="copyright">
186
+ <p>© 2025 Skin Lesion Diagnosis. All rights reserved.</p>
187
+ </div>
188
+ </div>
189
+ </footer>
190
+ </body>
191
+ </html>
training_history.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3af786ad065dd1a86367d84228fe92649f4b7bf0bb215548f43b4cb5ee171ac4
3
+ size 976