Add files via upload

This commit is contained in:
Jonathan Peters 2025-03-31 22:26:11 +02:00 committed by GitHub
parent 8b63297591
commit 893df2a899
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
72 changed files with 9196 additions and 0 deletions

Binary file not shown.

148
JonathanMyPortfolio/app.py Normal file
View File

@ -0,0 +1,148 @@
import os
import logging
from flask import Flask, render_template
# Configure logging
logging.basicConfig(level=logging.DEBUG)
# Create the app
app = Flask(__name__)
app.secret_key = os.environ.get("SESSION_SECRET")
@app.route('/')
def index():
# Project data
projects = [
{
"title": "E-Commerce Platform",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "A full-featured online shopping platform built with React and Node.js, featuring cart functionality, payment processing, and user accounts.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Weather Dashboard",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Interactive weather application displaying forecast data with dynamic visualizations using D3.js and the OpenWeather API.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Portfolio Generator",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Node.js application that generates personalized portfolio websites based on user input and customizable templates.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Task Management System",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Kanban-style task management application with drag-and-drop functionality, built with React and a Express.js backend.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Recipe Finder",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Web application that helps users discover recipes based on available ingredients, dietary restrictions, and meal preferences.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Fitness Tracker",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Application to track workout routines, exercise progress, and fitness goals with data visualization using D3.js.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Social Media Dashboard",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Consolidated dashboard for managing multiple social media accounts with analytics and scheduled posting features.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Budget Planner",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Financial planning tool with expense tracking, budget creation, and visual reports using Chart.js.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Real Estate Listings",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Property listing application with search filters, interactive maps, and property comparison features.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Learning Management System",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Educational platform for course creation, student enrollment, and progress tracking with interactive lessons.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Travel Planner",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Trip planning application with itinerary creation, destination information, and budget management features.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Blog Platform",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Content management system for bloggers with markdown support, category management, and SEO optimization tools.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Music Discovery App",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Application to discover new music based on preferences, listening history, and social recommendations.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Event Booking System",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Platform for creating, finding, and registering for events with calendar integration and payment processing.",
"link": "https://github.com/jonathanpeters"
}
]
# Skills data with percentages
skills = [
{"name": "HTML", "percentage": 95, "icon": "fab fa-html5"},
{"name": "CSS", "percentage": 90, "icon": "fab fa-css3-alt"},
{"name": "JavaScript", "percentage": 85, "icon": "fab fa-js"},
{"name": "React", "percentage": 80, "icon": "fab fa-react"},
{"name": "Node.js", "percentage": 75, "icon": "fab fa-node-js"},
{"name": "Express.js", "percentage": 70, "icon": "fab fa-node-js"},
{"name": "D3.js", "percentage": 65, "icon": "fas fa-chart-bar"},
{"name": "Bootstrap", "percentage": 85, "icon": "fab fa-bootstrap"},
{"name": "Tailwind CSS", "percentage": 80, "icon": "fab fa-css3"},
{"name": "Python", "percentage": 70, "icon": "fab fa-python"}
]
# Tools data
tools = [
{"name": "HTML", "icon": "fab fa-html5"},
{"name": "CSS", "icon": "fab fa-css3-alt"},
{"name": "JavaScript", "icon": "fab fa-js"},
{"name": "React", "icon": "fab fa-react"},
{"name": "Node.js", "icon": "fab fa-node-js"},
{"name": "Express.js", "icon": "fab fa-node-js"},
{"name": "D3.js", "icon": "fas fa-chart-bar"},
{"name": "Bootstrap", "icon": "fab fa-bootstrap"},
{"name": "Tailwind CSS", "icon": "fab fa-css3"},
{"name": "Python", "icon": "fab fa-python"},
{"name": "GitHub", "icon": "fab fa-github"},
{"name": "Replit", "icon": "fas fa-code"},
{"name": "VS Code", "icon": "fas fa-code"}
]
# Social media links
social_links = [
{"platform": "WhatsApp", "icon": "fab fa-whatsapp", "link": "https://wa.me/1234567890"},
{"platform": "Facebook", "icon": "fab fa-facebook-f", "link": "https://facebook.com/jonathanpeters"},
{"platform": "Twitter", "icon": "fab fa-twitter", "link": "https://twitter.com/jonathanpeters"},
{"platform": "LinkedIn", "icon": "fab fa-linkedin-in", "link": "https://linkedin.com/in/jonathanpeters"},
{"platform": "Instagram", "icon": "fab fa-instagram", "link": "https://instagram.com/jonathanpeters"},
{"platform": "GitHub", "icon": "fab fa-github", "link": "https://github.com/jonathanpeters"},
{"platform": "Replit", "icon": "fas fa-code", "link": "https://replit.com/@jonathanpeters"}
]
return render_template('index.html',
projects=projects,
skills=skills,
tools=tools,
social_links=social_links)

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

349
JonathanMyPortfolio/css/normalize.css vendored Normal file
View File

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

View File

@ -0,0 +1,38 @@
%PDF-1.7
1 0 obj
<</Type/Catalog/Pages 2 0 R>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids[3 0 R]>>
endobj
3 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Resources<<>>/Contents 4 0 R/Parent 2 0 R>>
endobj
4 0 obj
<</Length 119>>
stream
1 0 0 1 50 750 cm
BT
/F1 24 Tf
(Jonathan Peters - CV) Tj
ET
1 0 0 1 50 700 cm
BT
/F1 12 Tf
(Front-End Developer) Tj
ET
endstream
endobj
xref
0 5
0000000000 65535 f
0000000010 00000 n
0000000056 00000 n
0000000111 00000 n
0000000210 00000 n
trailer
<</Size 5/Root 1 0 R>>
startxref
380
%%EOF

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="400" height="300" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="300" fill="#2a2a2a"/>
<g fill="#6c5ce7">
<path d="M170 60h60v30h-60z"/>
<path d="M170 100h60v30h-60z"/>
<path d="M170 140h60v30h-60z"/>
<path d="M170 180h60v30h-60z"/>
<path d="M170 220h60v30h-60z"/>
</g>
<text x="200" y="130" font-family="Arial" font-size="20" text-anchor="middle" fill="white">E-Commerce</text>
<text x="200" y="160" font-family="Arial" font-size="20" text-anchor="middle" fill="white">Platform</text>
</svg>

After

Width:  |  Height:  |  Size: 626 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="400" height="300" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="300" fill="#2a2a2a"/>
<circle cx="200" cy="120" r="70" fill="#00cec9"/>
<path d="M150 180 L200 220 L250 180" fill="none" stroke="#6c5ce7" stroke-width="6"/>
<path d="M170 140 L230 140" fill="none" stroke="#6c5ce7" stroke-width="6"/>
<path d="M170 150 L230 150" fill="none" stroke="#6c5ce7" stroke-width="6"/>
<text x="200" y="240" font-family="Arial" font-size="20" text-anchor="middle" fill="white">Weather Dashboard</text>
</svg>

After

Width:  |  Height:  |  Size: 612 B

View File

@ -0,0 +1,183 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jonathan Peters | Front-End Developer</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/devicon.min.css">
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="page-container">
<!-- Header Section -->
<header>
<div id="particles-js"></div>
<div class="container">
<div class="header-content">
<div class="logo">
<h1>Jonathan Peters</h1>
<p class="subtitle">Front-End Developer</p>
</div>
<nav>
<ul>
<li><a href="#projects">Projects</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#tools">Tools</a></li>
<li><a href="#about">About Me</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</div>
</header>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h2>Creating <span class="highlight">Beautiful</span> &amp; <span class="highlight">Functional</span> Web Experiences</h2>
<p>I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.</p>
<div class="hero-buttons">
<a href="#projects" class="btn primary-btn">View My Work</a>
<a href="#contact" class="btn secondary-btn">Get In Touch</a>
<a href="JonathanPetersCV.pdf" download class="btn download-cv-btn">Download CV</a>
</div>
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="projects-section">
<div class="container">
<div class="section-header">
<h2>My Projects</h2>
<p>Explore some of my recent work</p>
</div>
<div class="projects-grid" id="projects-container">
<!-- Projects will be loaded via JavaScript -->
</div>
</div>
</section>
<!-- Skills Section -->
<section id="skills" class="skills-section">
<div class="container">
<div class="section-header">
<h2>My Skills</h2>
<p>Technical proficiency and expertise</p>
</div>
<div class="skills-content" id="skills-container">
<!-- Skills will be loaded via JavaScript -->
</div>
</div>
</section>
<!-- Tools Section -->
<section id="tools" class="tools-section">
<div class="container">
<div class="section-header">
<h2>Languages &amp; Tools</h2>
<p>Technologies I work with</p>
</div>
<div class="tools-grid" id="tools-container">
<!-- Tools will be loaded via JavaScript -->
</div>
</div>
</section>
<!-- About Section -->
<section id="about" class="about-section">
<div class="container">
<div class="section-header">
<h2>About Me</h2>
<p>Get to know me better</p>
</div>
<div class="about-content">
<div class="about-text">
<p>Hi, I'm Jonathan Peters, a front-end developer passionate about creating beautiful and functional web experiences. With expertise in HTML, CSS, JavaScript, and various frameworks, I strive to build responsive and user-friendly websites and applications.</p>
<p>I have 3+ years experience & I have completed the <a href="https://www.freecodecamp.org/learn/responsive-web-design/" target="_blank">Responsive Web Design</a> & the <a href="https://www.freecodecamp.org/learn/data-visualization/" target="_blank">Data Visualizations</a> course on freeCodeCamp, as well as <a href="https://www.bitdegree.org/courses/user/djjonnas/certificates" target="_blank">Front End Development</a> courses on Bitdegree.</p>
<p>I am currently pursuing courses in Backend Development: Node JS, Express, Python & React.</p>
<p>My journey in web development started with a curiosity for how things work on the internet, which quickly turned into a passion for creating my own digital experiences. I continuously learn and adapt to stay updated with the latest technologies and best practices in the industry.</p>
<p>When I'm not coding, you might find me exploring new design trends, contributing to open-source projects, or experimenting with new technologies to enhance my skill set.</p>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section">
<div class="container">
<div class="section-header">
<h2>Get In Touch</h2>
<p>Let's work together on your next project</p>
</div>
<div class="contact-content">
<div class="contact-form">
<form id="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="user_name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="user_email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit" class="btn primary-btn submit-btn" id="submit-btn">
<span>Send Message</span>
<i class="fas fa-paper-plane"></i>
</button>
<div id="email-status" class="email-status"></div>
</form>
</div>
<div class="contact-info">
<div class="contact-item">
<i class="fas fa-envelope"></i>
<span>jonathanpeters051@gmail.com</span>
</div>
<div class="contact-item">
<i class="fas fa-phone"></i>
<span>+27661199255</span>
</div>
<div class="contact-item">
<i class="fas fa-map-marker-alt"></i>
<span>George, Western Cape. South Africa</span>
</div>
</div>
</div>
</div>
</section>
<!-- Footer Section -->
<footer>
<div id="footer-particles-js"></div>
<div class="container">
<div class="social-links" id="social-links-container">
<!-- Social links will be loaded via JavaScript -->
</div>
<div class="footer-text">
<p>&copy; 2025 Jonathan Peters. All rights reserved.</p>
</div>
</div>
</footer>
</div>
<!-- Notification for CV download -->
<div class="notification" id="cv-notification">
<i class="fas fa-check-circle"></i>
<span>CV downloaded successfully!</span>
</div>
<!-- EmailJS script -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@emailjs/browser@3/dist/email.min.js"></script>
<!-- Particles.js script -->
<script src="https://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
<script src="js/script.js"></script>
</body>
</html>

View File

@ -0,0 +1,680 @@
document.addEventListener('DOMContentLoaded', function() {
// Project data
const projects = [
{
"title": "Crypto Tracker App",
"image": "cryptotracker.png",
"projectImg": "cryptotracker.png",
"description": "An app that allows yu to buy & sell crypto",
"link": "https://qms85.github.io/CryptoTrackerPro/"
},
{
"title": "Digital Divide Records",
"image": "javascript",
"projectImg": "digitaldividerecords.png",
"description": "A South African House Music Record Label",
"link": "https://digitaldividerecords-pty-ltd.github.io/DigitalDivideRecords/"
},
{
"title": "Drag Me",
"image": "nodejs",
"projectImg": "dragme.png",
"description": "A div element, that you can drag around the screen",
"link": "https://qms85.github.io/DragMeAroundTheScreen/"
},
{
"title": "Entrepreneurship",
"image": "react",
"projectImg": "entrepreneurship.png",
"description": "A guide to Entrepreneurship, using Replit .",
"link": "https://entrepreneurship-withreplit.streamlit.app/"
},
{
"title": "GitHub Profile Search",
"image": "javascript",
"projectImg": "githubprofilesearch.png",
"description": "A Web app that searches for Github profiles,enter username & enter",
"link": "https://qms85.github.io/Github-User-Search/"
},
{
"title": "GKroon Investment Calculator",
"image": "nodejs",
"projectImg": "gkrooninvestmentcalculator.png",
"description": "An investment calculator, in $USD",
"link": "https://gkroon-web.github.io/Investment-Calculator/"
},
{
"title": "GKroon Password Generator",
"image": "react",
"projectImg": "gkroonpasswordgenerator.png",
"description": "This app generates passwords up to 24 characters",
"link": "https://qms85.github.io/Password-Generator/"
},
{
"title": "GKroon",
"image": "javascript",
"projectImg": "gkroonwebsite.png",
"description": "A business website for GKroon",
"link": "https://g-kroon.github.io/GKroon/"
},
{
"title": "Music Player",
"image": "nodejs",
"projectImg": "musicplayer.png",
"description": "A mp3 music player. Press Play & enjoy the groove",
"link": "https://qms85.github.io/MusicPlayer/"
},
{
"title": "Meme Generator",
"image": "react",
"projectImg": "memegenerator.png",
"description": "Upload your images, add text & download your new meme",
"link": "https://qms85.github.io/MemeGenerator/"
},
{
"title": "Replit Capabilities Guide",
"image": "javascript",
"projectImg": "replitcapabilitiesguide.png",
"description": "A comprehensive guide to Repit.",
"link": "https://qms85.github.io/ReplitCapabilitiesGuide/"
},
{
"title": "Rectangular Run",
"image": "nodejs",
"projectImg": "rectangularrun.png",
"description": "A simple javascript game, inspired by the classic Dino Run game. Only this version uses rectangles",
"link": "https://gkroon-web.github.io/RectangleRun/"
},
{
"title": "Sample Artist EPK",
"image": "react",
"projectImg": "sampleartistepk.png",
"description": "A sample artist electronic press kit (EPK)",
"link": "https://digitaldividerecords-pty-ltd.github.io/SampleArtistEPK/"
},
{
"title": "Calculator",
"image": "react",
"projectImg": "calculator.png",
"description": "A simple calculator",
"link": "https://qms85.github.io/GitHubCalculator/"
}
];
// Skills data with percentages
const skills = [
{"name": "HTML", "percentage": 95, "icon": "devicon-html5-plain colored"},
{"name": "CSS", "percentage": 90, "icon": "devicon-css3-plain colored"},
{"name": "JavaScript", "percentage": 85, "icon": "devicon-javascript-plain colored"},
{"name": "React", "percentage": 80, "icon": "devicon-react-original colored"},
{"name": "Node.js", "percentage": 45, "icon": "devicon-nodejs-plain colored"},
{"name": "Express.js", "percentage": 45, "icon": "devicon-express-original colored"},
{"name": "D3.js", "percentage": 70, "icon": "devicon-d3js-plain colored"},
{"name": "Bootstrap", "percentage": 70, "icon": "devicon-bootstrap-plain colored"},
{"name": "Tailwind CSS", "percentage": 70, "icon": "devicon-tailwindcss-plain colored"},
{"name": "Python", "percentage": 45, "icon": "devicon-python-plain colored"}
];
// Tools data
const tools = [
{"name": "HTML", "icon": "devicon-html5-plain colored"},
{"name": "CSS", "icon": "devicon-css3-plain colored"},
{"name": "JavaScript", "icon": "devicon-javascript-plain colored"},
{"name": "React", "icon": "devicon-react-original colored"},
{"name": "Node.js", "icon": "devicon-nodejs-plain colored"},
{"name": "Express.js", "icon": "devicon-express-original colored"},
{"name": "D3.js", "icon": "devicon-d3js-plain colored"},
{"name": "Bootstrap", "icon": "devicon-bootstrap-plain colored"},
{"name": "Tailwind CSS", "icon": "devicon-tailwindcss-plain colored"},
{"name": "Python", "icon": "devicon-python-plain colored"},
{"name": "GitHub", "icon": "devicon-github-original colored"},
{"name": "Replit", "icon": "devicon-replit-original colored"},
{"name": "VS Code", "icon": "devicon-vscode-plain colored"}
];
// Social media links
const socialLinks = [
{"platform": "WhatsApp", "icon": "fab fa-whatsapp", "link": "https://wa.me/+2766119925"},
{"platform": "Facebook", "icon": "fab fa-facebook-f", "link": "https://facebook.com/2jonathanpeters"},
{"platform": "Twitter", "icon": "fab fa-twitter", "link": "https://twitter.com/DJJonnas85"},
{"platform": "LinkedIn", "icon": "fab fa-linkedin-in", "link": "https://linkedin.com/in/2jonathanpeters"},
{"platform": "Instagram", "icon": "fab fa-instagram", "link": "https://instagram.com/jonathanpeters"},
{"platform": "GitHub", "icon": "fab fa-github", "link": "https://github.com/QMS85"},
{"platform": "Replit", "icon": "devicon-replit-plain", "link": "https://replit.com/DJJonnas85"}
];
// Rendering functions
function renderProjects() {
const projectsContainer = document.getElementById('projects-container');
if (!projectsContainer) return;
let projectsHTML = '';
projects.forEach(project => {
let iconClass = '';
// Map image names to Devicon classes
switch(project.image) {
case 'react':
iconClass = 'devicon-react-original colored';
break;
case 'javascript':
iconClass = 'devicon-javascript-plain colored';
break;
case 'nodejs':
iconClass = 'devicon-nodejs-plain colored';
break;
default:
iconClass = 'devicon-devicon-plain colored';
}
const projectImage = project.projectImg ?
`<img src="${project.projectImg}" alt="${project.title}" class="project-image">` :
`<i class="${iconClass}"></i>`;
projectsHTML += `
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
${projectImage}
<h3>${project.title}</h3>
</div>
<div class="flip-card-back">
<h3>${project.title}</h3>
<p class="typing-text-container">
<span class="typing-text">${project.description}</span>
</p>
<a href="${project.link}" class="btn view-project-btn" target="_blank">View Project</a>
</div>
</div>
</div>
`;
});
projectsContainer.innerHTML = projectsHTML;
}
function renderSkills() {
const skillsContainer = document.getElementById('skills-container');
if (!skillsContainer) return;
let skillsHTML = '';
skills.forEach(skill => {
skillsHTML += `
<div class="skill-item">
<div class="skill-info">
<div class="skill-name">
<i class="${skill.icon}"></i>
<span>${skill.name}</span>
</div>
<div class="skill-percentage">${skill.percentage}%</div>
</div>
<div class="skill-bar">
<div class="skill-progress" style="width: ${skill.percentage}%"></div>
</div>
</div>
`;
});
skillsContainer.innerHTML = skillsHTML;
}
function renderTools() {
const toolsContainer = document.getElementById('tools-container');
if (!toolsContainer) return;
let toolsHTML = '';
tools.forEach(tool => {
toolsHTML += `
<div class="tool-item">
<i class="${tool.icon}"></i>
<span>${tool.name}</span>
</div>
`;
});
toolsContainer.innerHTML = toolsHTML;
}
function renderSocialLinks() {
const socialLinksContainer = document.getElementById('social-links-container');
if (!socialLinksContainer) return;
let socialLinksHTML = '';
socialLinks.forEach(social => {
socialLinksHTML += `
<a href="${social.link}" target="_blank" title="${social.platform}">
<i class="${social.icon}"></i>
</a>
`;
});
socialLinksContainer.innerHTML = socialLinksHTML;
}
// Initialize animations for elements when they come into view
const animateOnScroll = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
const elementPosition = element.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (elementPosition < windowHeight - 100) {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}
});
};
// Set initial styles for animation
const initAnimations = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
element.style.opacity = '0';
element.style.transform = 'translateY(20px)';
element.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
});
};
// Animate progress bars when skills section is in view
const animateSkillBars = () => {
const skillsSection = document.getElementById('skills');
if (!skillsSection) return;
const skillBars = document.querySelectorAll('.skill-progress');
const sectionPosition = skillsSection.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (sectionPosition < windowHeight - 100) {
skillBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0';
setTimeout(() => {
bar.style.width = width;
}, 100);
});
}
};
// Smooth scrolling for navigation links
const smoothScroll = () => {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 80,
behavior: 'smooth'
});
}
});
});
};
// EmailJS initialization and form submission handling
const handleFormSubmission = () => {
const contactForm = document.getElementById('contact-form');
const emailStatus = document.getElementById('email-status');
if (!contactForm) return;
// Initialize EmailJS with your user ID
// Replace 'YOUR_EMAILJS_USER_ID' with your actual EmailJS user ID/public key
emailjs.init('k8FMxcW3OlbaRfu2u');
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// Disable submit button and show loading state
const submitBtn = document.getElementById('submit-btn');
const originalBtnText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
// Send email using EmailJS
// Replace 'YOUR_EMAILJS_SERVICE_ID' and 'YOUR_EMAILJS_TEMPLATE_ID' with your actual values
emailjs.sendForm('service_y8eilob', 'template_0fbrelq', this)
.then(function(response) {
// Show success message
emailStatus.textContent = 'Thank you! Your message has been sent successfully.';
emailStatus.className = 'email-status success';
// Reset form
contactForm.reset();
// Hide success message after 5 seconds
setTimeout(() => {
emailStatus.style.display = 'none';
}, 5000);
console.log('SUCCESS!', response.status, response.text);
}, function(error) {
// Show error message
emailStatus.textContent = 'Oops! Something went wrong. Please try again later.';
emailStatus.className = 'email-status error';
console.log('FAILED...', error);
})
.finally(() => {
// Re-enable submit button
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
});
});
};
// Add active state to navigation links based on scroll position with micro-interactions
const setActiveNavLink = () => {
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('nav ul li a');
const header = document.querySelector('header');
// Store previous section for transition effects
let previousSection = '';
let scrollingTimeout;
window.addEventListener('scroll', () => {
let current = '';
const scrollTop = window.pageYOffset;
// Clear the timeout on scroll
clearTimeout(scrollingTimeout);
// Add scrolling class to header for potential scroll-based effects
header.classList.add('scrolling');
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (scrollTop >= (sectionTop - 200)) {
current = section.getAttribute('id');
}
});
// Only update if we have a change in section
if (current !== previousSection) {
// Remove active class from all links first
navLinks.forEach(link => {
link.classList.remove('active');
// Remove any transition classes
link.classList.remove('fadeIn', 'bounce');
});
// Find the active link
const activeLink = Array.from(navLinks).find(link =>
link.getAttribute('href') === `#${current}`
);
// If we have an active link, add classes with animation
if (activeLink) {
activeLink.classList.add('active');
// Add a quick animation effect
activeLink.classList.add('bounce');
setTimeout(() => {
activeLink.classList.remove('bounce');
}, 500);
}
// Update previous section
previousSection = current;
}
// Remove scrolling class after scrolling stops
scrollingTimeout = setTimeout(() => {
header.classList.remove('scrolling');
}, 150);
});
// Add click event for smooth scrolling and animation
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
// Remove active class and animations from all links
navLinks.forEach(l => {
l.classList.remove('active', 'fadeIn', 'bounce');
});
// Add active class and animation to clicked link
this.classList.add('active', 'fadeIn');
// Find the target section
const targetId = this.getAttribute('href').substring(1);
const targetSection = document.getElementById(targetId);
if (targetSection) {
e.preventDefault();
// Smooth scroll to section
window.scrollTo({
top: targetSection.offsetTop - 100,
behavior: 'smooth'
});
// Update URL without page reload
history.pushState(null, null, `#${targetId}`);
// Update previousSection
previousSection = targetId;
}
});
});
};
// Enhance flip cards for touch devices
const enhanceFlipCards = () => {
const flipCards = document.querySelectorAll('.flip-card');
flipCards.forEach(card => {
// For touch devices
card.addEventListener('touchstart', function() {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(180deg)';
});
// Return to front after delay
card.addEventListener('mouseleave', function() {
setTimeout(() => {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(0deg)';
}, 1000);
});
});
};
// Add CSS class to style active navigation link
const styleActiveNavLinks = () => {
const style = document.createElement('style');
style.textContent = `
nav ul li a.active {
color: var(--primary-light);
}
nav ul li a.active::after {
width: 100%;
}
`;
document.head.appendChild(style);
};
// Handle CV download notification
const handleCVDownload = () => {
const downloadBtn = document.querySelector('.download-cv-btn');
const notification = document.getElementById('cv-notification');
if (!downloadBtn || !notification) return;
downloadBtn.addEventListener('click', () => {
// Show the notification
notification.classList.add('show');
// Hide the notification after 3 seconds
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
});
};
// Initialize particles animation
const initParticles = () => {
// Check if particles.js is loaded
if (typeof particlesJS !== 'undefined') {
// Particle configuration for both header and footer
const particleConfig = {
"particles": {
"number": {
"value": 50,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": ["#FF4500", "#32CD32", "#FF0000"] // Orange, Lime-Green, Red
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
}
},
"opacity": {
"value": 0.5,
"random": true,
"anim": {
"enable": true,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 3,
"random": true,
"anim": {
"enable": true,
"speed": 2,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": true,
"distance": 150,
"color": "#32CD32",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 2,
"direction": "none",
"random": true,
"straight": false,
"out_mode": "bounce",
"bounce": false,
"attract": {
"enable": true,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "repulse"
},
"onclick": {
"enable": false,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 100,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
};
// Initialize particles for header
particlesJS('particles-js', particleConfig);
// Initialize particles for footer
particlesJS('footer-particles-js', particleConfig);
} else {
console.error('particles.js not loaded');
}
};
// Initialize all functionality
const init = () => {
// Render content from data
renderProjects();
renderSkills();
renderTools();
renderSocialLinks();
// Initialize animations and interactions
initAnimations();
animateOnScroll(); // Initial check on page load
smoothScroll();
handleFormSubmission();
setActiveNavLink();
styleActiveNavLinks();
enhanceFlipCards();
handleCVDownload();
initParticles(); // Initialize particles
// Listen for scroll events
window.addEventListener('scroll', () => {
animateOnScroll();
animateSkillBars();
});
// Handle window resize events
window.addEventListener('resize', () => {
animateOnScroll();
});
};
// Initialize everything
init();
});

View File

@ -0,0 +1,13 @@
from flask import Flask, send_from_directory
app = Flask(__name__, static_folder='.')
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve_static(path):
if path == '':
return send_from_directory('.', 'index.html')
return send_from_directory('.', path)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -0,0 +1,12 @@
[project]
name = "repl-nix-workspace"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.11"
dependencies = [
"email-validator>=2.2.0",
"flask>=3.1.0",
"flask-sqlalchemy>=3.1.1",
"gunicorn>=23.0.0",
"psycopg2-binary>=2.9.10",
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,6 @@
{pkgs}: {
deps = [
pkgs.postgresql
pkgs.openssl
];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,2 @@
#!/bin/bash
python -m http.server 5000

View File

@ -0,0 +1,9 @@
import http.server
import socketserver
PORT = 5000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving at http://0.0.0.0:{PORT}")
httpd.serve_forever()

View File

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View File

@ -0,0 +1,644 @@
/* Base Styles and Variables */
:root {
--primary-color: #6c5ce7;
--primary-light: #a29bfe;
--secondary-color: #00cec9;
--bg-dark: #121212;
--bg-light: #1e1e1e;
--bg-lighter: #2a2a2a;
--text-color: #f5f5f5;
--text-muted: #a0a0a0;
--border-color: #333;
--card-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
--glow-effect: 0 0 15px rgba(108, 92, 231, 0.5);
--transition-speed: 0.3s;
}
/* Global Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: 'Roboto', sans-serif;
background-color: var(--bg-dark);
color: var(--text-color);
line-height: 1.6;
}
.page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Raleway', sans-serif;
font-weight: 700;
margin-bottom: 1rem;
}
p {
margin-bottom: 1rem;
}
a {
color: var(--text-color);
text-decoration: none;
transition: all var(--transition-speed) ease;
}
a:hover {
color: var(--primary-light);
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
position: relative;
display: inline-block;
padding-bottom: 10px;
}
.section-header h2::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 3px;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
}
.section-header p {
font-size: 1.1rem;
color: var(--text-muted);
}
.btn {
display: inline-block;
padding: 12px 25px;
border-radius: 30px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
transition: all var(--transition-speed) ease;
cursor: pointer;
border: none;
outline: none;
}
.primary-btn {
background: linear-gradient(45deg, var(--primary-color), var(--primary-light));
color: white;
box-shadow: 0 4px 15px rgba(108, 92, 231, 0.3);
}
.secondary-btn {
background: transparent;
border: 2px solid var(--primary-color);
color: var(--text-color);
}
.primary-btn:hover {
box-shadow: 0 6px 20px rgba(108, 92, 231, 0.5);
transform: translateY(-3px);
color: white;
}
.secondary-btn:hover {
background-color: var(--primary-color);
color: white;
transform: translateY(-3px);
}
.highlight {
color: var(--primary-color);
}
/* Header Styles */
header {
background-color: var(--bg-dark);
padding: 1.5rem 0;
position: sticky;
top: 0;
z-index: 100;
border-bottom: 1px solid var(--border-color);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo h1 {
font-size: 1.8rem;
margin-bottom: 5px;
font-weight: 700;
}
.subtitle {
font-size: 1rem;
color: var(--primary-light);
margin: 0;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-left: 2rem;
}
nav ul li a {
font-size: 1rem;
font-weight: 500;
position: relative;
padding-bottom: 5px;
}
nav ul li a::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
transition: width var(--transition-speed) ease;
}
nav ul li a:hover::after {
width: 100%;
}
/* Hero Section */
.hero {
height: 90vh;
display: flex;
align-items: center;
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle at top right, rgba(108, 92, 231, 0.1), transparent 60%);
z-index: -1;
}
.hero-content {
max-width: 800px;
margin: 0 auto;
text-align: center;
}
.hero-content h2 {
font-size: 3rem;
margin-bottom: 1.5rem;
line-height: 1.2;
}
.hero-content p {
font-size: 1.2rem;
margin-bottom: 2rem;
color: var(--text-muted);
}
.hero-buttons {
display: flex;
justify-content: center;
gap: 1rem;
}
/* Projects Section */
.projects-section {
padding: 6rem 0;
background-color: var(--bg-light);
}
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
}
.flip-card {
perspective: 1000px;
height: 250px;
border-radius: 10px;
cursor: pointer;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.8s;
transform-style: preserve-3d;
box-shadow: var(--card-shadow);
border-radius: 10px;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
border-radius: 10px;
padding: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.flip-card-front {
background-color: var(--bg-lighter);
border: 1px solid var(--border-color);
}
.flip-card-front i {
font-size: 4rem;
margin-bottom: 1rem;
color: var(--primary-color);
}
.flip-card-back {
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
color: white;
transform: rotateY(180deg);
}
.flip-card-back h3 {
margin-bottom: 1rem;
}
.flip-card-back p {
margin-bottom: 1.5rem;
font-size: 0.9rem;
}
.view-project-btn {
background-color: rgba(255, 255, 255, 0.2);
color: white;
padding: 8px 15px;
border-radius: 20px;
transition: all var(--transition-speed) ease;
}
.view-project-btn:hover {
background-color: white;
color: var(--primary-color);
}
/* Skills Section */
.skills-section {
padding: 6rem 0;
background-color: var(--bg-dark);
}
.skills-content {
max-width: 800px;
margin: 0 auto;
}
.skill-item {
margin-bottom: 2rem;
}
.skill-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.skill-name {
display: flex;
align-items: center;
}
.skill-name i {
font-size: 1.5rem;
margin-right: 10px;
color: var(--primary-color);
}
.skill-percentage {
font-weight: bold;
color: var(--primary-light);
}
.skill-bar {
height: 10px;
background-color: var(--bg-lighter);
border-radius: 5px;
overflow: hidden;
}
.skill-progress {
height: 100%;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
border-radius: 5px;
transition: width 1s ease-in-out;
}
/* Tools Section */
.tools-section {
padding: 6rem 0;
background-color: var(--bg-light);
}
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 2rem;
}
.tool-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: var(--bg-lighter);
border-radius: 10px;
padding: 2rem 1rem;
box-shadow: var(--card-shadow);
transition: all var(--transition-speed) ease;
border: 1px solid var(--border-color);
}
.tool-item i {
font-size: 3rem;
margin-bottom: 1rem;
color: var(--primary-color);
}
.tool-item:hover {
transform: translateY(-10px);
box-shadow: var(--glow-effect);
}
/* About Section */
.about-section {
padding: 6rem 0;
background-color: var(--bg-dark);
}
.about-content {
display: flex;
justify-content: center;
gap: 4rem;
}
.about-text {
max-width: 700px;
}
.about-text p {
font-size: 1.1rem;
margin-bottom: 1.5rem;
line-height: 1.8;
}
/* Contact Section */
.contact-section {
padding: 6rem 0;
background-color: var(--bg-light);
}
.contact-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
}
.contact-form {
background-color: var(--bg-lighter);
padding: 2rem;
border-radius: 10px;
box-shadow: var(--card-shadow);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px;
background-color: var(--bg-dark);
border: 1px solid var(--border-color);
border-radius: 5px;
color: var(--text-color);
transition: all var(--transition-speed) ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.2);
}
.contact-info {
display: flex;
flex-direction: column;
justify-content: center;
}
.contact-item {
display: flex;
align-items: center;
margin-bottom: 2rem;
}
.contact-item i {
font-size: 2rem;
margin-right: 1rem;
color: var(--primary-color);
}
/* Footer Section */
footer {
padding: 3rem 0;
background-color: var(--bg-dark);
border-top: 1px solid var(--border-color);
text-align: center;
}
.social-links {
display: flex;
justify-content: center;
gap: 1.5rem;
margin-bottom: 2rem;
}
.social-links a {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
background-color: var(--bg-lighter);
border-radius: 50%;
transition: all var(--transition-speed) ease;
box-shadow: var(--card-shadow);
}
.social-links a i {
font-size: 1.5rem;
color: var(--text-color);
transition: all var(--transition-speed) ease;
}
.social-links a:hover {
transform: translateY(-5px);
background-color: var(--primary-color);
box-shadow: var(--glow-effect);
}
.social-links a:hover i {
color: white;
}
.footer-text {
color: var(--text-muted);
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(108, 92, 231, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(108, 92, 231, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(108, 92, 231, 0);
}
}
/* Responsive Styles */
@media screen and (max-width: 992px) {
.container {
padding: 0 40px;
}
.hero-content h2 {
font-size: 2.5rem;
}
.contact-content {
grid-template-columns: 1fr;
gap: 3rem;
}
}
@media screen and (max-width: 768px) {
.header-content {
flex-direction: column;
}
nav ul {
margin-top: 1rem;
}
nav ul li {
margin: 0 1rem;
}
.hero {
height: auto;
padding: 6rem 0;
}
.hero-content h2 {
font-size: 2rem;
}
.hero-buttons {
flex-direction: column;
gap: 1rem;
}
.about-content {
flex-direction: column;
gap: 2rem;
}
}
@media screen and (max-width: 576px) {
.container {
padding: 0 20px;
}
.section-header h2 {
font-size: 2rem;
}
nav ul {
flex-wrap: wrap;
justify-content: center;
}
nav ul li {
margin: 0.5rem;
}
.tools-grid {
grid-template-columns: repeat(2, 1fr);
}
}

View File

@ -0,0 +1,175 @@
document.addEventListener('DOMContentLoaded', function() {
// Initialize animations for elements when they come into view
const animateOnScroll = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
const elementPosition = element.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (elementPosition < windowHeight - 100) {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}
});
};
// Set initial styles for animation
const initAnimations = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
element.style.opacity = '0';
element.style.transform = 'translateY(20px)';
element.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
});
};
// Animate progress bars when skills section is in view
const animateSkillBars = () => {
const skillsSection = document.getElementById('skills');
if (!skillsSection) return;
const skillBars = document.querySelectorAll('.skill-progress');
const sectionPosition = skillsSection.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (sectionPosition < windowHeight - 100) {
skillBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0';
setTimeout(() => {
bar.style.width = width;
}, 100);
});
}
};
// Smooth scrolling for navigation links
const smoothScroll = () => {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 80,
behavior: 'smooth'
});
}
});
});
};
// Form submission handling
const handleFormSubmission = () => {
const contactForm = document.querySelector('.contact-form form');
if (!contactForm) return;
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// Get form data
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const message = document.getElementById('message').value;
// In a real-world scenario, you would send this data to a server
console.log('Form submission:', { name, email, message });
// Show success message
alert('Thank you for your message! I will get back to you soon.');
// Reset form
contactForm.reset();
});
};
// Initialize all functionality
initAnimations();
animateOnScroll(); // Initial check on page load
smoothScroll();
handleFormSubmission();
// Listen for scroll events
window.addEventListener('scroll', () => {
animateOnScroll();
animateSkillBars();
});
// Handle window resize events
window.addEventListener('resize', () => {
animateOnScroll();
});
// Add active state to navigation links based on scroll position
const setActiveNavLink = () => {
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('nav ul li a');
window.addEventListener('scroll', () => {
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (pageYOffset >= (sectionTop - 200)) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${current}`) {
link.classList.add('active');
}
});
});
};
setActiveNavLink();
// Enhance flip cards for touch devices
const enhanceFlipCards = () => {
const flipCards = document.querySelectorAll('.flip-card');
flipCards.forEach(card => {
// For touch devices
card.addEventListener('touchstart', function() {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(180deg)';
});
// Return to front after delay
card.addEventListener('mouseleave', function() {
setTimeout(() => {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(0deg)';
}, 1000);
});
});
};
enhanceFlipCards();
// Add CSS class to style active navigation link
const styleActiveNavLinks = () => {
const style = document.createElement('style');
style.textContent = `
nav ul li a.active {
color: var(--primary-light);
}
nav ul li a.active::after {
width: 100%;
}
`;
document.head.appendChild(style);
};
styleActiveNavLinks();
});

View File

@ -0,0 +1,199 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jonathan Peters | Front-End Developer</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="page-container">
<!-- Header Section -->
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<h1>Jonathan Peters</h1>
<p class="subtitle">Front-End Developer</p>
</div>
<nav>
<ul>
<li><a href="#projects">Projects</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#tools">Tools</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</div>
</header>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h2>Creating <span class="highlight">Beautiful</span> &amp; <span class="highlight">Functional</span> Web Experiences</h2>
<p>I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.</p>
<div class="hero-buttons">
<a href="#projects" class="btn primary-btn">View My Work</a>
<a href="#contact" class="btn secondary-btn">Get In Touch</a>
</div>
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="projects-section">
<div class="container">
<div class="section-header">
<h2>My Projects</h2>
<p>Explore some of my recent work</p>
</div>
<div class="projects-grid">
{% for project in projects %}
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<i class="{{ project.image.split('/')[-1].split('.')[0] }}"></i>
<h3>{{ project.title }}</h3>
</div>
<div class="flip-card-back">
<h3>{{ project.title }}</h3>
<p>{{ project.description }}</p>
<a href="{{ project.link }}" class="btn view-project-btn" target="_blank">View Project</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- Skills Section -->
<section id="skills" class="skills-section">
<div class="container">
<div class="section-header">
<h2>My Skills</h2>
<p>Technical proficiency and expertise</p>
</div>
<div class="skills-content">
{% for skill in skills %}
<div class="skill-item">
<div class="skill-info">
<div class="skill-name">
<i class="{{ skill.icon }}"></i>
<span>{{ skill.name }}</span>
</div>
<div class="skill-percentage">{{ skill.percentage }}%</div>
</div>
<div class="skill-bar">
<div class="skill-progress" style="width: {{ skill.percentage }}%"></div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- Tools Section -->
<section id="tools" class="tools-section">
<div class="container">
<div class="section-header">
<h2>Languages &amp; Tools</h2>
<p>Technologies I work with</p>
</div>
<div class="tools-grid">
{% for tool in tools %}
<div class="tool-item">
<i class="{{ tool.icon }}"></i>
<span>{{ tool.name }}</span>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- About Section -->
<section id="about" class="about-section">
<div class="container">
<div class="section-header">
<h2>About Me</h2>
<p>Get to know me better</p>
</div>
<div class="about-content">
<div class="about-text">
<p>Hi, I'm Jonathan Peters, a front-end developer passionate about creating beautiful and functional web experiences. With expertise in HTML, CSS, JavaScript, and various frameworks, I strive to build responsive and user-friendly websites and applications.</p>
<p>My journey in web development started with a curiosity for how things work on the internet, which quickly turned into a passion for creating my own digital experiences. I continuously learn and adapt to stay updated with the latest technologies and best practices in the industry.</p>
<p>I have 3+ years experience & i have completed the <a href="#">Responsive Web Design</a> & the <a href="#">Data Visualizations</a> course on freeCodeCamp</p>
<p>When I'm not coding, you might find me exploring new design trends, contributing to open-source projects, or experimenting with new technologies to enhance my skill set.</p>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section">
<div class="container">
<div class="section-header">
<h2>Get In Touch</h2>
<p>Let's work together on your next project</p>
</div>
<div class="contact-content">
<div class="contact-form">
<form>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit" class="btn primary-btn">Send Message</button>
</form>
</div>
<div class="contact-info">
<div class="contact-item">
<i class="fas fa-envelope"></i>
<span>jonathanpeters051@gmail.com</span>
</div>
<div class="contact-item">
<i class="fas fa-phone"></i>
<span>+27661199255</span>
</div>
<div class="contact-item">
<i class="fas fa-map-marker-alt"></i>
<span>George, Western Cape. South Africa</span>
</div>
</div>
</div>
</div>
</section>
<!-- Footer Section -->
<footer>
<div class="container">
<div class="social-links">
{% for social in social_links %}
<a href="{{ social.link }}" target="_blank" title="{{ social.platform }}">
<i class="{{ social.icon }}"></i>
</a>
{% endfor %}
</div>
<div class="footer-text">
<p>&copy; 2025 Jonathan Peters. All rights reserved.</p>
</div>
</div>
</footer>
</div>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>

346
JonathanMyPortfolio/uv.lock generated Normal file
View File

@ -0,0 +1,346 @@
version = 1
requires-python = ">=3.11"
[[package]]
name = "blinker"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "dnspython"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
]
[[package]]
name = "email-validator"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "dnspython" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 },
]
[[package]]
name = "flask"
version = "3.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blinker" },
{ name = "click" },
{ name = "itsdangerous" },
{ name = "jinja2" },
{ name = "werkzeug" },
]
sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979 },
]
[[package]]
name = "flask-sqlalchemy"
version = "3.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "flask" },
{ name = "sqlalchemy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/91/53/b0a9fcc1b1297f51e68b69ed3b7c3c40d8c45be1391d77ae198712914392/flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312", size = 81899 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/6a/89963a5c6ecf166e8be29e0d1bf6806051ee8fe6c82e232842e3aeac9204/flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", size = 25125 },
]
[[package]]
name = "greenlet"
version = "3.1.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 },
{ url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 },
{ url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 },
{ url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 },
{ url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 },
{ url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 },
{ url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 },
{ url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 },
{ url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 },
{ url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 },
{ url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 },
{ url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 },
{ url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 },
{ url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 },
{ url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 },
{ url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 },
{ url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 },
{ url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 },
{ url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 },
{ url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 },
{ url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 },
{ url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 },
{ url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 },
{ url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 },
{ url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 },
{ url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 },
{ url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 },
{ url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 },
{ url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 },
{ url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 },
{ url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 },
{ url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 },
{ url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 },
{ url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
]
[[package]]
name = "gunicorn"
version = "23.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "itsdangerous"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
]
[[package]]
name = "markupsafe"
version = "3.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 },
{ url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 },
{ url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 },
{ url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 },
{ url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 },
{ url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 },
{ url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 },
{ url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 },
{ url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 },
{ url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 },
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "psycopg2-binary"
version = "2.9.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/8f/9feb01291d0d7a0a4c6a6bab24094135c2b59c6a81943752f632c75896d6/psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", size = 3043397 },
{ url = "https://files.pythonhosted.org/packages/15/30/346e4683532011561cd9c8dfeac6a8153dd96452fee0b12666058ab7893c/psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", size = 3274806 },
{ url = "https://files.pythonhosted.org/packages/66/6e/4efebe76f76aee7ec99166b6c023ff8abdc4e183f7b70913d7c047701b79/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", size = 2851370 },
{ url = "https://files.pythonhosted.org/packages/7f/fd/ff83313f86b50f7ca089b161b8e0a22bb3c319974096093cd50680433fdb/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", size = 3080780 },
{ url = "https://files.pythonhosted.org/packages/e6/c4/bfadd202dcda8333a7ccafdc51c541dbdfce7c2c7cda89fa2374455d795f/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", size = 3264583 },
{ url = "https://files.pythonhosted.org/packages/5d/f1/09f45ac25e704ac954862581f9f9ae21303cc5ded3d0b775532b407f0e90/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", size = 3019831 },
{ url = "https://files.pythonhosted.org/packages/9e/2e/9beaea078095cc558f215e38f647c7114987d9febfc25cb2beed7c3582a5/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", size = 2871822 },
{ url = "https://files.pythonhosted.org/packages/01/9e/ef93c5d93f3dc9fc92786ffab39e323b9aed066ba59fdc34cf85e2722271/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", size = 2820975 },
{ url = "https://files.pythonhosted.org/packages/a5/f0/049e9631e3268fe4c5a387f6fc27e267ebe199acf1bc1bc9cbde4bd6916c/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", size = 2919320 },
{ url = "https://files.pythonhosted.org/packages/dc/9a/bcb8773b88e45fb5a5ea8339e2104d82c863a3b8558fbb2aadfe66df86b3/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", size = 2957617 },
{ url = "https://files.pythonhosted.org/packages/e2/6b/144336a9bf08a67d217b3af3246abb1d027095dab726f0687f01f43e8c03/psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", size = 1024618 },
{ url = "https://files.pythonhosted.org/packages/61/69/3b3d7bd583c6d3cbe5100802efa5beacaacc86e37b653fc708bf3d6853b8/psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", size = 1163816 },
{ url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 },
{ url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 },
{ url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 },
{ url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 },
{ url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 },
{ url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 },
{ url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 },
{ url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 },
{ url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 },
{ url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 },
{ url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 },
{ url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 },
{ url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699 },
{ url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245 },
{ url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631 },
{ url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140 },
{ url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762 },
{ url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967 },
{ url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326 },
{ url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
{ url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
{ url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
]
[[package]]
name = "repl-nix-workspace"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "email-validator" },
{ name = "flask" },
{ name = "flask-sqlalchemy" },
{ name = "gunicorn" },
{ name = "psycopg2-binary" },
]
[package.metadata]
requires-dist = [
{ name = "email-validator", specifier = ">=2.2.0" },
{ name = "flask", specifier = ">=3.1.0" },
{ name = "flask-sqlalchemy", specifier = ">=3.1.1" },
{ name = "gunicorn", specifier = ">=23.0.0" },
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.40"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/c3/3f2bfa5e4dcd9938405fe2fab5b6ab94a9248a4f9536ea2fd497da20525f/sqlalchemy-2.0.40.tar.gz", hash = "sha256:d827099289c64589418ebbcaead0145cd19f4e3e8a93919a0100247af245fa00", size = 9664299 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/7e/55044a9ec48c3249bb38d5faae93f09579c35e862bb318ebd1ed7a1994a5/sqlalchemy-2.0.40-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6bacab7514de6146a1976bc56e1545bee247242fab030b89e5f70336fc0003e", size = 2114025 },
{ url = "https://files.pythonhosted.org/packages/77/0f/dcf7bba95f847aec72f638750747b12d37914f71c8cc7c133cf326ab945c/sqlalchemy-2.0.40-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5654d1ac34e922b6c5711631f2da497d3a7bffd6f9f87ac23b35feea56098011", size = 2104419 },
{ url = "https://files.pythonhosted.org/packages/75/70/c86a5c20715e4fe903dde4c2fd44fc7e7a0d5fb52c1b954d98526f65a3ea/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35904d63412db21088739510216e9349e335f142ce4a04b69e2528020ee19ed4", size = 3222720 },
{ url = "https://files.pythonhosted.org/packages/12/cf/b891a8c1d0c27ce9163361664c2128c7a57de3f35000ea5202eb3a2917b7/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7a80ed86d6aaacb8160a1caef6680d4ddd03c944d985aecee940d168c411d1", size = 3222682 },
{ url = "https://files.pythonhosted.org/packages/15/3f/7709d8c8266953d945435a96b7f425ae4172a336963756b58e996fbef7f3/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:519624685a51525ddaa7d8ba8265a1540442a2ec71476f0e75241eb8263d6f51", size = 3159542 },
{ url = "https://files.pythonhosted.org/packages/85/7e/717eaabaf0f80a0132dc2032ea8f745b7a0914451c984821a7c8737fb75a/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2ee5f9999a5b0e9689bed96e60ee53c3384f1a05c2dd8068cc2e8361b0df5b7a", size = 3179864 },
{ url = "https://files.pythonhosted.org/packages/e4/cc/03eb5dfcdb575cbecd2bd82487b9848f250a4b6ecfb4707e834b4ce4ec07/sqlalchemy-2.0.40-cp311-cp311-win32.whl", hash = "sha256:c0cae71e20e3c02c52f6b9e9722bca70e4a90a466d59477822739dc31ac18b4b", size = 2084675 },
{ url = "https://files.pythonhosted.org/packages/9a/48/440946bf9dc4dc231f4f31ef0d316f7135bf41d4b86aaba0c0655150d370/sqlalchemy-2.0.40-cp311-cp311-win_amd64.whl", hash = "sha256:574aea2c54d8f1dd1699449f332c7d9b71c339e04ae50163a3eb5ce4c4325ee4", size = 2110099 },
{ url = "https://files.pythonhosted.org/packages/92/06/552c1f92e880b57d8b92ce6619bd569b25cead492389b1d84904b55989d8/sqlalchemy-2.0.40-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9d3b31d0a1c44b74d3ae27a3de422dfccd2b8f0b75e51ecb2faa2bf65ab1ba0d", size = 2112620 },
{ url = "https://files.pythonhosted.org/packages/01/72/a5bc6e76c34cebc071f758161dbe1453de8815ae6e662393910d3be6d70d/sqlalchemy-2.0.40-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37f7a0f506cf78c80450ed1e816978643d3969f99c4ac6b01104a6fe95c5490a", size = 2103004 },
{ url = "https://files.pythonhosted.org/packages/bf/fd/0e96c8e6767618ed1a06e4d7a167fe13734c2f8113c4cb704443e6783038/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bb933a650323e476a2e4fbef8997a10d0003d4da996aad3fd7873e962fdde4d", size = 3252440 },
{ url = "https://files.pythonhosted.org/packages/cd/6a/eb82e45b15a64266a2917a6833b51a334ea3c1991728fd905bfccbf5cf63/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959738971b4745eea16f818a2cd086fb35081383b078272c35ece2b07012716", size = 3263277 },
{ url = "https://files.pythonhosted.org/packages/45/97/ebe41ab4530f50af99e3995ebd4e0204bf1b0dc0930f32250dde19c389fe/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:110179728e442dae85dd39591beb74072ae4ad55a44eda2acc6ec98ead80d5f2", size = 3198591 },
{ url = "https://files.pythonhosted.org/packages/e6/1c/a569c1b2b2f5ac20ba6846a1321a2bf52e9a4061001f282bf1c5528dcd69/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8040680eaacdce4d635f12c55c714f3d4c7f57da2bc47a01229d115bd319191", size = 3225199 },
{ url = "https://files.pythonhosted.org/packages/8f/91/87cc71a6b10065ca0209d19a4bb575378abda6085e72fa0b61ffb2201b84/sqlalchemy-2.0.40-cp312-cp312-win32.whl", hash = "sha256:650490653b110905c10adac69408380688cefc1f536a137d0d69aca1069dc1d1", size = 2082959 },
{ url = "https://files.pythonhosted.org/packages/2a/9f/14c511cda174aa1ad9b0e42b64ff5a71db35d08b0d80dc044dae958921e5/sqlalchemy-2.0.40-cp312-cp312-win_amd64.whl", hash = "sha256:2be94d75ee06548d2fc591a3513422b873490efb124048f50556369a834853b0", size = 2108526 },
{ url = "https://files.pythonhosted.org/packages/8c/18/4e3a86cc0232377bc48c373a9ba6a1b3fb79ba32dbb4eda0b357f5a2c59d/sqlalchemy-2.0.40-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:915866fd50dd868fdcc18d61d8258db1bf9ed7fbd6dfec960ba43365952f3b01", size = 2107887 },
{ url = "https://files.pythonhosted.org/packages/cb/60/9fa692b1d2ffc4cbd5f47753731fd332afed30137115d862d6e9a1e962c7/sqlalchemy-2.0.40-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a4c5a2905a9ccdc67a8963e24abd2f7afcd4348829412483695c59e0af9a705", size = 2098367 },
{ url = "https://files.pythonhosted.org/packages/4c/9f/84b78357ca641714a439eb3fbbddb17297dacfa05d951dbf24f28d7b5c08/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55028d7a3ebdf7ace492fab9895cbc5270153f75442a0472d8516e03159ab364", size = 3184806 },
{ url = "https://files.pythonhosted.org/packages/4b/7d/e06164161b6bfce04c01bfa01518a20cccbd4100d5c951e5a7422189191a/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cfedff6878b0e0d1d0a50666a817ecd85051d12d56b43d9d425455e608b5ba0", size = 3198131 },
{ url = "https://files.pythonhosted.org/packages/6d/51/354af20da42d7ec7b5c9de99edafbb7663a1d75686d1999ceb2c15811302/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb19e30fdae77d357ce92192a3504579abe48a66877f476880238a962e5b96db", size = 3131364 },
{ url = "https://files.pythonhosted.org/packages/7a/2f/48a41ff4e6e10549d83fcc551ab85c268bde7c03cf77afb36303c6594d11/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:16d325ea898f74b26ffcd1cf8c593b0beed8714f0317df2bed0d8d1de05a8f26", size = 3159482 },
{ url = "https://files.pythonhosted.org/packages/33/ac/e5e0a807163652a35be878c0ad5cfd8b1d29605edcadfb5df3c512cdf9f3/sqlalchemy-2.0.40-cp313-cp313-win32.whl", hash = "sha256:a669cbe5be3c63f75bcbee0b266779706f1a54bcb1000f302685b87d1b8c1500", size = 2080704 },
{ url = "https://files.pythonhosted.org/packages/1c/cb/f38c61f7f2fd4d10494c1c135ff6a6ddb63508d0b47bccccd93670637309/sqlalchemy-2.0.40-cp313-cp313-win_amd64.whl", hash = "sha256:641ee2e0834812d657862f3a7de95e0048bdcb6c55496f39c6fa3d435f6ac6ad", size = 2104564 },
{ url = "https://files.pythonhosted.org/packages/d1/7c/5fc8e802e7506fe8b55a03a2e1dab156eae205c91bee46305755e086d2e2/sqlalchemy-2.0.40-py3-none-any.whl", hash = "sha256:32587e2e1e359276957e6fe5dad089758bc042a971a8a09ae8ecf7a8fe23d07a", size = 1903894 },
]
[[package]]
name = "typing-extensions"
version = "4.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 },
]
[[package]]
name = "werkzeug"
version = "3.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 },
]

BIN
JonathanPetersCV.pdf Normal file

Binary file not shown.

148
app.py Normal file
View File

@ -0,0 +1,148 @@
import os
import logging
from flask import Flask, render_template
# Configure logging
logging.basicConfig(level=logging.DEBUG)
# Create the app
app = Flask(__name__)
app.secret_key = os.environ.get("SESSION_SECRET")
@app.route('/')
def index():
# Project data
projects = [
{
"title": "E-Commerce Platform",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "A full-featured online shopping platform built with React and Node.js, featuring cart functionality, payment processing, and user accounts.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Weather Dashboard",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Interactive weather application displaying forecast data with dynamic visualizations using D3.js and the OpenWeather API.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Portfolio Generator",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Node.js application that generates personalized portfolio websites based on user input and customizable templates.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Task Management System",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Kanban-style task management application with drag-and-drop functionality, built with React and a Express.js backend.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Recipe Finder",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Web application that helps users discover recipes based on available ingredients, dietary restrictions, and meal preferences.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Fitness Tracker",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Application to track workout routines, exercise progress, and fitness goals with data visualization using D3.js.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Social Media Dashboard",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Consolidated dashboard for managing multiple social media accounts with analytics and scheduled posting features.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Budget Planner",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Financial planning tool with expense tracking, budget creation, and visual reports using Chart.js.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Real Estate Listings",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Property listing application with search filters, interactive maps, and property comparison features.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Learning Management System",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Educational platform for course creation, student enrollment, and progress tracking with interactive lessons.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Travel Planner",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Trip planning application with itinerary creation, destination information, and budget management features.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Blog Platform",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
"description": "Content management system for bloggers with markdown support, category management, and SEO optimization tools.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Music Discovery App",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
"description": "Application to discover new music based on preferences, listening history, and social recommendations.",
"link": "https://github.com/jonathanpeters"
},
{
"title": "Event Booking System",
"image": "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
"description": "Platform for creating, finding, and registering for events with calendar integration and payment processing.",
"link": "https://github.com/jonathanpeters"
}
]
# Skills data with percentages
skills = [
{"name": "HTML", "percentage": 95, "icon": "fab fa-html5"},
{"name": "CSS", "percentage": 90, "icon": "fab fa-css3-alt"},
{"name": "JavaScript", "percentage": 85, "icon": "fab fa-js"},
{"name": "React", "percentage": 80, "icon": "fab fa-react"},
{"name": "Node.js", "percentage": 75, "icon": "fab fa-node-js"},
{"name": "Express.js", "percentage": 70, "icon": "fab fa-node-js"},
{"name": "D3.js", "percentage": 65, "icon": "fas fa-chart-bar"},
{"name": "Bootstrap", "percentage": 85, "icon": "fab fa-bootstrap"},
{"name": "Tailwind CSS", "percentage": 80, "icon": "fab fa-css3"},
{"name": "Python", "percentage": 70, "icon": "fab fa-python"}
]
# Tools data
tools = [
{"name": "HTML", "icon": "fab fa-html5"},
{"name": "CSS", "icon": "fab fa-css3-alt"},
{"name": "JavaScript", "icon": "fab fa-js"},
{"name": "React", "icon": "fab fa-react"},
{"name": "Node.js", "icon": "fab fa-node-js"},
{"name": "Express.js", "icon": "fab fa-node-js"},
{"name": "D3.js", "icon": "fas fa-chart-bar"},
{"name": "Bootstrap", "icon": "fab fa-bootstrap"},
{"name": "Tailwind CSS", "icon": "fab fa-css3"},
{"name": "Python", "icon": "fab fa-python"},
{"name": "GitHub", "icon": "fab fa-github"},
{"name": "Replit", "icon": "fas fa-code"},
{"name": "VS Code", "icon": "fas fa-code"}
]
# Social media links
social_links = [
{"platform": "WhatsApp", "icon": "fab fa-whatsapp", "link": "https://wa.me/1234567890"},
{"platform": "Facebook", "icon": "fab fa-facebook-f", "link": "https://facebook.com/jonathanpeters"},
{"platform": "Twitter", "icon": "fab fa-twitter", "link": "https://twitter.com/jonathanpeters"},
{"platform": "LinkedIn", "icon": "fab fa-linkedin-in", "link": "https://linkedin.com/in/jonathanpeters"},
{"platform": "Instagram", "icon": "fab fa-instagram", "link": "https://instagram.com/jonathanpeters"},
{"platform": "GitHub", "icon": "fab fa-github", "link": "https://github.com/jonathanpeters"},
{"platform": "Replit", "icon": "fas fa-code", "link": "https://replit.com/@jonathanpeters"}
]
return render_template('index.html',
projects=projects,
skills=skills,
tools=tools,
social_links=social_links)

BIN
calculator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
cryptotracker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

349
css/normalize.css vendored Normal file
View File

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

1423
css/style.css Normal file

File diff suppressed because it is too large Load Diff

BIN
digitaldividerecords.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

BIN
dragme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
entrepreneurship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

View File

@ -0,0 +1,38 @@
%PDF-1.7
1 0 obj
<</Type/Catalog/Pages 2 0 R>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids[3 0 R]>>
endobj
3 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Resources<<>>/Contents 4 0 R/Parent 2 0 R>>
endobj
4 0 obj
<</Length 119>>
stream
1 0 0 1 50 750 cm
BT
/F1 24 Tf
(Jonathan Peters - CV) Tj
ET
1 0 0 1 50 700 cm
BT
/F1 12 Tf
(Front-End Developer) Tj
ET
endstream
endobj
xref
0 5
0000000000 65535 f
0000000010 00000 n
0000000056 00000 n
0000000111 00000 n
0000000210 00000 n
trailer
<</Size 5/Root 1 0 R>>
startxref
380
%%EOF

BIN
generated-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

BIN
githubcalculator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
githubprofilesearch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
gkroonpasswordgenerator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
gkroonwebsite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="400" height="300" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="300" fill="#2a2a2a"/>
<g fill="#6c5ce7">
<path d="M170 60h60v30h-60z"/>
<path d="M170 100h60v30h-60z"/>
<path d="M170 140h60v30h-60z"/>
<path d="M170 180h60v30h-60z"/>
<path d="M170 220h60v30h-60z"/>
</g>
<text x="200" y="130" font-family="Arial" font-size="20" text-anchor="middle" fill="white">E-Commerce</text>
<text x="200" y="160" font-family="Arial" font-size="20" text-anchor="middle" fill="white">Platform</text>
</svg>

After

Width:  |  Height:  |  Size: 626 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="400" height="300" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="300" fill="#2a2a2a"/>
<circle cx="200" cy="120" r="70" fill="#00cec9"/>
<path d="M150 180 L200 220 L250 180" fill="none" stroke="#6c5ce7" stroke-width="6"/>
<path d="M170 140 L230 140" fill="none" stroke="#6c5ce7" stroke-width="6"/>
<path d="M170 150 L230 150" fill="none" stroke="#6c5ce7" stroke-width="6"/>
<text x="200" y="240" font-family="Arial" font-size="20" text-anchor="middle" fill="white">Weather Dashboard</text>
</svg>

After

Width:  |  Height:  |  Size: 612 B

183
index.html Normal file
View File

@ -0,0 +1,183 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jonathan Peters | Front-End Developer</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/devicon.min.css">
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="page-container">
<!-- Header Section -->
<header>
<div id="particles-js"></div>
<div class="container">
<div class="header-content">
<div class="logo">
<h1>Jonathan Peters</h1>
<p class="subtitle">Front-End Developer</p>
</div>
<nav>
<ul>
<li><a href="#projects">Projects</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#tools">Tools</a></li>
<li><a href="#about">About Me</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</div>
</header>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h2>Creating <span class="highlight">Beautiful</span> &amp; <span class="highlight">Functional</span> Web Experiences</h2>
<p>I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.</p>
<div class="hero-buttons">
<a href="#projects" class="btn primary-btn">View My Work</a>
<a href="#contact" class="btn secondary-btn">Get In Touch</a>
<a href="JonathanPetersCV.pdf" download class="btn download-cv-btn">Download CV</a>
</div>
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="projects-section">
<div class="container">
<div class="section-header">
<h2>My Projects</h2>
<p>Explore some of my recent work</p>
</div>
<div class="projects-grid" id="projects-container">
<!-- Projects will be loaded via JavaScript -->
</div>
</div>
</section>
<!-- Skills Section -->
<section id="skills" class="skills-section">
<div class="container">
<div class="section-header">
<h2>My Skills</h2>
<p>Technical proficiency and expertise</p>
</div>
<div class="skills-content" id="skills-container">
<!-- Skills will be loaded via JavaScript -->
</div>
</div>
</section>
<!-- Tools Section -->
<section id="tools" class="tools-section">
<div class="container">
<div class="section-header">
<h2>Languages &amp; Tools</h2>
<p>Technologies I work with</p>
</div>
<div class="tools-grid" id="tools-container">
<!-- Tools will be loaded via JavaScript -->
</div>
</div>
</section>
<!-- About Section -->
<section id="about" class="about-section">
<div class="container">
<div class="section-header">
<h2>About Me</h2>
<p>Get to know me better</p>
</div>
<div class="about-content">
<div class="about-text">
<p>Hi, I'm Jonathan Peters, a front-end developer passionate about creating beautiful and functional web experiences. With expertise in HTML, CSS, JavaScript, and various frameworks, I strive to build responsive and user-friendly websites and applications.</p>
<p>I have 3+ years experience & I have completed the <a href="https://www.freecodecamp.org/learn/responsive-web-design/" target="_blank">Responsive Web Design</a> & the <a href="https://www.freecodecamp.org/learn/data-visualization/" target="_blank">Data Visualizations</a> course on freeCodeCamp, as well as <a href="https://www.bitdegree.org/courses/user/djjonnas/certificates" target="_blank">Front End Development</a> courses on Bitdegree.</p>
<p>I am currently pursuing courses in Backend Development: Node JS, Express, Python & React.</p>
<p>My journey in web development started with a curiosity for how things work on the internet, which quickly turned into a passion for creating my own digital experiences. I continuously learn and adapt to stay updated with the latest technologies and best practices in the industry.</p>
<p>When I'm not coding, you might find me exploring new design trends, contributing to open-source projects, or experimenting with new technologies to enhance my skill set.</p>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section">
<div class="container">
<div class="section-header">
<h2>Get In Touch</h2>
<p>Let's work together on your next project</p>
</div>
<div class="contact-content">
<div class="contact-form">
<form id="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="user_name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="user_email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit" class="btn primary-btn submit-btn" id="submit-btn">
<span>Send Message</span>
<i class="fas fa-paper-plane"></i>
</button>
<div id="email-status" class="email-status"></div>
</form>
</div>
<div class="contact-info">
<div class="contact-item">
<i class="fas fa-envelope"></i>
<span>jonathanpeters051@gmail.com</span>
</div>
<div class="contact-item">
<i class="fas fa-phone"></i>
<span>+27661199255</span>
</div>
<div class="contact-item">
<i class="fas fa-map-marker-alt"></i>
<span>George, Western Cape. South Africa</span>
</div>
</div>
</div>
</div>
</section>
<!-- Footer Section -->
<footer>
<div id="footer-particles-js"></div>
<div class="container">
<div class="social-links" id="social-links-container">
<!-- Social links will be loaded via JavaScript -->
</div>
<div class="footer-text">
<p>&copy; 2025 Jonathan Peters. All rights reserved.</p>
</div>
</div>
</footer>
</div>
<!-- Notification for CV download -->
<div class="notification" id="cv-notification">
<i class="fas fa-check-circle"></i>
<span>CV downloaded successfully!</span>
</div>
<!-- EmailJS script -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@emailjs/browser@3/dist/email.min.js"></script>
<!-- Particles.js script -->
<script src="https://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"></script>
<script src="js/script.js"></script>
</body>
</html>

680
js/script.js Normal file
View File

@ -0,0 +1,680 @@
document.addEventListener('DOMContentLoaded', function() {
// Project data
const projects = [
{
"title": "Crypto Tracker App",
"image": "cryptotracker.png",
"projectImg": "cryptotracker.png",
"description": "An app that allows yu to buy & sell crypto",
"link": "https://qms85.github.io/CryptoTrackerPro/"
},
{
"title": "Digital Divide Records",
"image": "javascript",
"projectImg": "digitaldividerecords.png",
"description": "A South African House Music Record Label",
"link": "https://digitaldividerecords-pty-ltd.github.io/DigitalDivideRecords/"
},
{
"title": "Drag Me",
"image": "nodejs",
"projectImg": "dragme.png",
"description": "A div element, that you can drag around the screen",
"link": "https://qms85.github.io/DragMeAroundTheScreen/"
},
{
"title": "Entrepreneurship",
"image": "react",
"projectImg": "entrepreneurship.png",
"description": "A guide to Entrepreneurship, using Replit .",
"link": "https://entrepreneurship-withreplit.streamlit.app/"
},
{
"title": "GitHub Profile Search",
"image": "javascript",
"projectImg": "githubprofilesearch.png",
"description": "A Web app that searches for Github profiles,enter username & enter",
"link": "https://qms85.github.io/Github-User-Search/"
},
{
"title": "GKroon Investment Calculator",
"image": "nodejs",
"projectImg": "gkrooninvestmentcalculator.png",
"description": "An investment calculator, in $USD",
"link": "https://gkroon-web.github.io/Investment-Calculator/"
},
{
"title": "GKroon Password Generator",
"image": "react",
"projectImg": "gkroonpasswordgenerator.png",
"description": "This app generates passwords up to 24 characters",
"link": "https://qms85.github.io/Password-Generator/"
},
{
"title": "GKroon",
"image": "javascript",
"projectImg": "gkroonwebsite.png",
"description": "A business website for GKroon",
"link": "https://g-kroon.github.io/GKroon/"
},
{
"title": "Music Player",
"image": "nodejs",
"projectImg": "musicplayer.png",
"description": "A mp3 music player. Press Play & enjoy the groove",
"link": "https://qms85.github.io/MusicPlayer/"
},
{
"title": "Meme Generator",
"image": "react",
"projectImg": "memegenerator.png",
"description": "Upload your images, add text & download your new meme",
"link": "https://qms85.github.io/MemeGenerator/"
},
{
"title": "Replit Capabilities Guide",
"image": "javascript",
"projectImg": "replitcapabilitiesguide.png",
"description": "A comprehensive guide to Repit.",
"link": "https://qms85.github.io/ReplitCapabilitiesGuide/"
},
{
"title": "Rectangular Run",
"image": "nodejs",
"projectImg": "rectangularrun.png",
"description": "A simple javascript game, inspired by the classic Dino Run game. Only this version uses rectangles",
"link": "https://gkroon-web.github.io/RectangleRun/"
},
{
"title": "Sample Artist EPK",
"image": "react",
"projectImg": "sampleartistepk.png",
"description": "A sample artist electronic press kit (EPK)",
"link": "https://digitaldividerecords-pty-ltd.github.io/SampleArtistEPK/"
},
{
"title": "Calculator",
"image": "react",
"projectImg": "calculator.png",
"description": "A simple calculator",
"link": "https://qms85.github.io/GitHubCalculator/"
}
];
// Skills data with percentages
const skills = [
{"name": "HTML", "percentage": 95, "icon": "devicon-html5-plain colored"},
{"name": "CSS", "percentage": 90, "icon": "devicon-css3-plain colored"},
{"name": "JavaScript", "percentage": 85, "icon": "devicon-javascript-plain colored"},
{"name": "React", "percentage": 80, "icon": "devicon-react-original colored"},
{"name": "Node.js", "percentage": 45, "icon": "devicon-nodejs-plain colored"},
{"name": "Express.js", "percentage": 45, "icon": "devicon-express-original colored"},
{"name": "D3.js", "percentage": 70, "icon": "devicon-d3js-plain colored"},
{"name": "Bootstrap", "percentage": 70, "icon": "devicon-bootstrap-plain colored"},
{"name": "Tailwind CSS", "percentage": 70, "icon": "devicon-tailwindcss-plain colored"},
{"name": "Python", "percentage": 45, "icon": "devicon-python-plain colored"}
];
// Tools data
const tools = [
{"name": "HTML", "icon": "devicon-html5-plain colored"},
{"name": "CSS", "icon": "devicon-css3-plain colored"},
{"name": "JavaScript", "icon": "devicon-javascript-plain colored"},
{"name": "React", "icon": "devicon-react-original colored"},
{"name": "Node.js", "icon": "devicon-nodejs-plain colored"},
{"name": "Express.js", "icon": "devicon-express-original colored"},
{"name": "D3.js", "icon": "devicon-d3js-plain colored"},
{"name": "Bootstrap", "icon": "devicon-bootstrap-plain colored"},
{"name": "Tailwind CSS", "icon": "devicon-tailwindcss-plain colored"},
{"name": "Python", "icon": "devicon-python-plain colored"},
{"name": "GitHub", "icon": "devicon-github-original colored"},
{"name": "Replit", "icon": "devicon-replit-original colored"},
{"name": "VS Code", "icon": "devicon-vscode-plain colored"}
];
// Social media links
const socialLinks = [
{"platform": "WhatsApp", "icon": "fab fa-whatsapp", "link": "https://wa.me/+2766119925"},
{"platform": "Facebook", "icon": "fab fa-facebook-f", "link": "https://facebook.com/2jonathanpeters"},
{"platform": "Twitter", "icon": "fab fa-twitter", "link": "https://twitter.com/DJJonnas85"},
{"platform": "LinkedIn", "icon": "fab fa-linkedin-in", "link": "https://linkedin.com/in/2jonathanpeters"},
{"platform": "Instagram", "icon": "fab fa-instagram", "link": "https://instagram.com/jonathanpeters"},
{"platform": "GitHub", "icon": "fab fa-github", "link": "https://github.com/QMS85"},
{"platform": "Replit", "icon": "devicon-replit-plain", "link": "https://replit.com/DJJonnas85"}
];
// Rendering functions
function renderProjects() {
const projectsContainer = document.getElementById('projects-container');
if (!projectsContainer) return;
let projectsHTML = '';
projects.forEach(project => {
let iconClass = '';
// Map image names to Devicon classes
switch(project.image) {
case 'react':
iconClass = 'devicon-react-original colored';
break;
case 'javascript':
iconClass = 'devicon-javascript-plain colored';
break;
case 'nodejs':
iconClass = 'devicon-nodejs-plain colored';
break;
default:
iconClass = 'devicon-devicon-plain colored';
}
const projectImage = project.projectImg ?
`<img src="${project.projectImg}" alt="${project.title}" class="project-image">` :
`<i class="${iconClass}"></i>`;
projectsHTML += `
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
${projectImage}
<h3>${project.title}</h3>
</div>
<div class="flip-card-back">
<h3>${project.title}</h3>
<p class="typing-text-container">
<span class="typing-text">${project.description}</span>
</p>
<a href="${project.link}" class="btn view-project-btn" target="_blank">View Project</a>
</div>
</div>
</div>
`;
});
projectsContainer.innerHTML = projectsHTML;
}
function renderSkills() {
const skillsContainer = document.getElementById('skills-container');
if (!skillsContainer) return;
let skillsHTML = '';
skills.forEach(skill => {
skillsHTML += `
<div class="skill-item">
<div class="skill-info">
<div class="skill-name">
<i class="${skill.icon}"></i>
<span>${skill.name}</span>
</div>
<div class="skill-percentage">${skill.percentage}%</div>
</div>
<div class="skill-bar">
<div class="skill-progress" style="width: ${skill.percentage}%"></div>
</div>
</div>
`;
});
skillsContainer.innerHTML = skillsHTML;
}
function renderTools() {
const toolsContainer = document.getElementById('tools-container');
if (!toolsContainer) return;
let toolsHTML = '';
tools.forEach(tool => {
toolsHTML += `
<div class="tool-item">
<i class="${tool.icon}"></i>
<span>${tool.name}</span>
</div>
`;
});
toolsContainer.innerHTML = toolsHTML;
}
function renderSocialLinks() {
const socialLinksContainer = document.getElementById('social-links-container');
if (!socialLinksContainer) return;
let socialLinksHTML = '';
socialLinks.forEach(social => {
socialLinksHTML += `
<a href="${social.link}" target="_blank" title="${social.platform}">
<i class="${social.icon}"></i>
</a>
`;
});
socialLinksContainer.innerHTML = socialLinksHTML;
}
// Initialize animations for elements when they come into view
const animateOnScroll = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
const elementPosition = element.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (elementPosition < windowHeight - 100) {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}
});
};
// Set initial styles for animation
const initAnimations = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
element.style.opacity = '0';
element.style.transform = 'translateY(20px)';
element.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
});
};
// Animate progress bars when skills section is in view
const animateSkillBars = () => {
const skillsSection = document.getElementById('skills');
if (!skillsSection) return;
const skillBars = document.querySelectorAll('.skill-progress');
const sectionPosition = skillsSection.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (sectionPosition < windowHeight - 100) {
skillBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0';
setTimeout(() => {
bar.style.width = width;
}, 100);
});
}
};
// Smooth scrolling for navigation links
const smoothScroll = () => {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 80,
behavior: 'smooth'
});
}
});
});
};
// EmailJS initialization and form submission handling
const handleFormSubmission = () => {
const contactForm = document.getElementById('contact-form');
const emailStatus = document.getElementById('email-status');
if (!contactForm) return;
// Initialize EmailJS with your user ID
// Replace 'YOUR_EMAILJS_USER_ID' with your actual EmailJS user ID/public key
emailjs.init('k8FMxcW3OlbaRfu2u');
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// Disable submit button and show loading state
const submitBtn = document.getElementById('submit-btn');
const originalBtnText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
// Send email using EmailJS
// Replace 'YOUR_EMAILJS_SERVICE_ID' and 'YOUR_EMAILJS_TEMPLATE_ID' with your actual values
emailjs.sendForm('service_y8eilob', 'template_0fbrelq', this)
.then(function(response) {
// Show success message
emailStatus.textContent = 'Thank you! Your message has been sent successfully.';
emailStatus.className = 'email-status success';
// Reset form
contactForm.reset();
// Hide success message after 5 seconds
setTimeout(() => {
emailStatus.style.display = 'none';
}, 5000);
console.log('SUCCESS!', response.status, response.text);
}, function(error) {
// Show error message
emailStatus.textContent = 'Oops! Something went wrong. Please try again later.';
emailStatus.className = 'email-status error';
console.log('FAILED...', error);
})
.finally(() => {
// Re-enable submit button
submitBtn.disabled = false;
submitBtn.textContent = originalBtnText;
});
});
};
// Add active state to navigation links based on scroll position with micro-interactions
const setActiveNavLink = () => {
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('nav ul li a');
const header = document.querySelector('header');
// Store previous section for transition effects
let previousSection = '';
let scrollingTimeout;
window.addEventListener('scroll', () => {
let current = '';
const scrollTop = window.pageYOffset;
// Clear the timeout on scroll
clearTimeout(scrollingTimeout);
// Add scrolling class to header for potential scroll-based effects
header.classList.add('scrolling');
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (scrollTop >= (sectionTop - 200)) {
current = section.getAttribute('id');
}
});
// Only update if we have a change in section
if (current !== previousSection) {
// Remove active class from all links first
navLinks.forEach(link => {
link.classList.remove('active');
// Remove any transition classes
link.classList.remove('fadeIn', 'bounce');
});
// Find the active link
const activeLink = Array.from(navLinks).find(link =>
link.getAttribute('href') === `#${current}`
);
// If we have an active link, add classes with animation
if (activeLink) {
activeLink.classList.add('active');
// Add a quick animation effect
activeLink.classList.add('bounce');
setTimeout(() => {
activeLink.classList.remove('bounce');
}, 500);
}
// Update previous section
previousSection = current;
}
// Remove scrolling class after scrolling stops
scrollingTimeout = setTimeout(() => {
header.classList.remove('scrolling');
}, 150);
});
// Add click event for smooth scrolling and animation
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
// Remove active class and animations from all links
navLinks.forEach(l => {
l.classList.remove('active', 'fadeIn', 'bounce');
});
// Add active class and animation to clicked link
this.classList.add('active', 'fadeIn');
// Find the target section
const targetId = this.getAttribute('href').substring(1);
const targetSection = document.getElementById(targetId);
if (targetSection) {
e.preventDefault();
// Smooth scroll to section
window.scrollTo({
top: targetSection.offsetTop - 100,
behavior: 'smooth'
});
// Update URL without page reload
history.pushState(null, null, `#${targetId}`);
// Update previousSection
previousSection = targetId;
}
});
});
};
// Enhance flip cards for touch devices
const enhanceFlipCards = () => {
const flipCards = document.querySelectorAll('.flip-card');
flipCards.forEach(card => {
// For touch devices
card.addEventListener('touchstart', function() {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(180deg)';
});
// Return to front after delay
card.addEventListener('mouseleave', function() {
setTimeout(() => {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(0deg)';
}, 1000);
});
});
};
// Add CSS class to style active navigation link
const styleActiveNavLinks = () => {
const style = document.createElement('style');
style.textContent = `
nav ul li a.active {
color: var(--primary-light);
}
nav ul li a.active::after {
width: 100%;
}
`;
document.head.appendChild(style);
};
// Handle CV download notification
const handleCVDownload = () => {
const downloadBtn = document.querySelector('.download-cv-btn');
const notification = document.getElementById('cv-notification');
if (!downloadBtn || !notification) return;
downloadBtn.addEventListener('click', () => {
// Show the notification
notification.classList.add('show');
// Hide the notification after 3 seconds
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
});
};
// Initialize particles animation
const initParticles = () => {
// Check if particles.js is loaded
if (typeof particlesJS !== 'undefined') {
// Particle configuration for both header and footer
const particleConfig = {
"particles": {
"number": {
"value": 50,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": ["#FF4500", "#32CD32", "#FF0000"] // Orange, Lime-Green, Red
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
}
},
"opacity": {
"value": 0.5,
"random": true,
"anim": {
"enable": true,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 3,
"random": true,
"anim": {
"enable": true,
"speed": 2,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": true,
"distance": 150,
"color": "#32CD32",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 2,
"direction": "none",
"random": true,
"straight": false,
"out_mode": "bounce",
"bounce": false,
"attract": {
"enable": true,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "repulse"
},
"onclick": {
"enable": false,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 100,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
};
// Initialize particles for header
particlesJS('particles-js', particleConfig);
// Initialize particles for footer
particlesJS('footer-particles-js', particleConfig);
} else {
console.error('particles.js not loaded');
}
};
// Initialize all functionality
const init = () => {
// Render content from data
renderProjects();
renderSkills();
renderTools();
renderSocialLinks();
// Initialize animations and interactions
initAnimations();
animateOnScroll(); // Initial check on page load
smoothScroll();
handleFormSubmission();
setActiveNavLink();
styleActiveNavLinks();
enhanceFlipCards();
handleCVDownload();
initParticles(); // Initialize particles
// Listen for scroll events
window.addEventListener('scroll', () => {
animateOnScroll();
animateSkillBars();
});
// Handle window resize events
window.addEventListener('resize', () => {
animateOnScroll();
});
};
// Initialize everything
init();
});

13
main.py Normal file
View File

@ -0,0 +1,13 @@
from flask import Flask, send_from_directory
app = Flask(__name__, static_folder='.')
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve_static(path):
if path == '':
return send_from_directory('.', 'index.html')
return send_from_directory('.', path)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)

BIN
meme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

BIN
memegenerator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

BIN
musicplayer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

12
pyproject.toml Normal file
View File

@ -0,0 +1,12 @@
[project]
name = "repl-nix-workspace"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.11"
dependencies = [
"email-validator>=2.2.0",
"flask>=3.1.0",
"flask-sqlalchemy>=3.1.1",
"gunicorn>=23.0.0",
"psycopg2-binary>=2.9.10",
]

BIN
rectangularrun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

6
replit.nix Normal file
View File

@ -0,0 +1,6 @@
{pkgs}: {
deps = [
pkgs.postgresql
pkgs.openssl
];
}

BIN
replitcapabilitiesguide.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
sampleartistepk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

2
serve.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/bash
python -m http.server 5000

9
serve_static.py Normal file
View File

@ -0,0 +1,9 @@
import http.server
import socketserver
PORT = 5000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving at http://0.0.0.0:{PORT}")
httpd.serve_forever()

349
static/css/normalize.css vendored Normal file
View File

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

644
static/css/style.css Normal file
View File

@ -0,0 +1,644 @@
/* Base Styles and Variables */
:root {
--primary-color: #6c5ce7;
--primary-light: #a29bfe;
--secondary-color: #00cec9;
--bg-dark: #121212;
--bg-light: #1e1e1e;
--bg-lighter: #2a2a2a;
--text-color: #f5f5f5;
--text-muted: #a0a0a0;
--border-color: #333;
--card-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
--glow-effect: 0 0 15px rgba(108, 92, 231, 0.5);
--transition-speed: 0.3s;
}
/* Global Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: 'Roboto', sans-serif;
background-color: var(--bg-dark);
color: var(--text-color);
line-height: 1.6;
}
.page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Raleway', sans-serif;
font-weight: 700;
margin-bottom: 1rem;
}
p {
margin-bottom: 1rem;
}
a {
color: var(--text-color);
text-decoration: none;
transition: all var(--transition-speed) ease;
}
a:hover {
color: var(--primary-light);
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
position: relative;
display: inline-block;
padding-bottom: 10px;
}
.section-header h2::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 3px;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
}
.section-header p {
font-size: 1.1rem;
color: var(--text-muted);
}
.btn {
display: inline-block;
padding: 12px 25px;
border-radius: 30px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
transition: all var(--transition-speed) ease;
cursor: pointer;
border: none;
outline: none;
}
.primary-btn {
background: linear-gradient(45deg, var(--primary-color), var(--primary-light));
color: white;
box-shadow: 0 4px 15px rgba(108, 92, 231, 0.3);
}
.secondary-btn {
background: transparent;
border: 2px solid var(--primary-color);
color: var(--text-color);
}
.primary-btn:hover {
box-shadow: 0 6px 20px rgba(108, 92, 231, 0.5);
transform: translateY(-3px);
color: white;
}
.secondary-btn:hover {
background-color: var(--primary-color);
color: white;
transform: translateY(-3px);
}
.highlight {
color: var(--primary-color);
}
/* Header Styles */
header {
background-color: var(--bg-dark);
padding: 1.5rem 0;
position: sticky;
top: 0;
z-index: 100;
border-bottom: 1px solid var(--border-color);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo h1 {
font-size: 1.8rem;
margin-bottom: 5px;
font-weight: 700;
}
.subtitle {
font-size: 1rem;
color: var(--primary-light);
margin: 0;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-left: 2rem;
}
nav ul li a {
font-size: 1rem;
font-weight: 500;
position: relative;
padding-bottom: 5px;
}
nav ul li a::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
transition: width var(--transition-speed) ease;
}
nav ul li a:hover::after {
width: 100%;
}
/* Hero Section */
.hero {
height: 90vh;
display: flex;
align-items: center;
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle at top right, rgba(108, 92, 231, 0.1), transparent 60%);
z-index: -1;
}
.hero-content {
max-width: 800px;
margin: 0 auto;
text-align: center;
}
.hero-content h2 {
font-size: 3rem;
margin-bottom: 1.5rem;
line-height: 1.2;
}
.hero-content p {
font-size: 1.2rem;
margin-bottom: 2rem;
color: var(--text-muted);
}
.hero-buttons {
display: flex;
justify-content: center;
gap: 1rem;
}
/* Projects Section */
.projects-section {
padding: 6rem 0;
background-color: var(--bg-light);
}
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
}
.flip-card {
perspective: 1000px;
height: 250px;
border-radius: 10px;
cursor: pointer;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.8s;
transform-style: preserve-3d;
box-shadow: var(--card-shadow);
border-radius: 10px;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
border-radius: 10px;
padding: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.flip-card-front {
background-color: var(--bg-lighter);
border: 1px solid var(--border-color);
}
.flip-card-front i {
font-size: 4rem;
margin-bottom: 1rem;
color: var(--primary-color);
}
.flip-card-back {
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
color: white;
transform: rotateY(180deg);
}
.flip-card-back h3 {
margin-bottom: 1rem;
}
.flip-card-back p {
margin-bottom: 1.5rem;
font-size: 0.9rem;
}
.view-project-btn {
background-color: rgba(255, 255, 255, 0.2);
color: white;
padding: 8px 15px;
border-radius: 20px;
transition: all var(--transition-speed) ease;
}
.view-project-btn:hover {
background-color: white;
color: var(--primary-color);
}
/* Skills Section */
.skills-section {
padding: 6rem 0;
background-color: var(--bg-dark);
}
.skills-content {
max-width: 800px;
margin: 0 auto;
}
.skill-item {
margin-bottom: 2rem;
}
.skill-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.skill-name {
display: flex;
align-items: center;
}
.skill-name i {
font-size: 1.5rem;
margin-right: 10px;
color: var(--primary-color);
}
.skill-percentage {
font-weight: bold;
color: var(--primary-light);
}
.skill-bar {
height: 10px;
background-color: var(--bg-lighter);
border-radius: 5px;
overflow: hidden;
}
.skill-progress {
height: 100%;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
border-radius: 5px;
transition: width 1s ease-in-out;
}
/* Tools Section */
.tools-section {
padding: 6rem 0;
background-color: var(--bg-light);
}
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 2rem;
}
.tool-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: var(--bg-lighter);
border-radius: 10px;
padding: 2rem 1rem;
box-shadow: var(--card-shadow);
transition: all var(--transition-speed) ease;
border: 1px solid var(--border-color);
}
.tool-item i {
font-size: 3rem;
margin-bottom: 1rem;
color: var(--primary-color);
}
.tool-item:hover {
transform: translateY(-10px);
box-shadow: var(--glow-effect);
}
/* About Section */
.about-section {
padding: 6rem 0;
background-color: var(--bg-dark);
}
.about-content {
display: flex;
justify-content: center;
gap: 4rem;
}
.about-text {
max-width: 700px;
}
.about-text p {
font-size: 1.1rem;
margin-bottom: 1.5rem;
line-height: 1.8;
}
/* Contact Section */
.contact-section {
padding: 6rem 0;
background-color: var(--bg-light);
}
.contact-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
}
.contact-form {
background-color: var(--bg-lighter);
padding: 2rem;
border-radius: 10px;
box-shadow: var(--card-shadow);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px;
background-color: var(--bg-dark);
border: 1px solid var(--border-color);
border-radius: 5px;
color: var(--text-color);
transition: all var(--transition-speed) ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.2);
}
.contact-info {
display: flex;
flex-direction: column;
justify-content: center;
}
.contact-item {
display: flex;
align-items: center;
margin-bottom: 2rem;
}
.contact-item i {
font-size: 2rem;
margin-right: 1rem;
color: var(--primary-color);
}
/* Footer Section */
footer {
padding: 3rem 0;
background-color: var(--bg-dark);
border-top: 1px solid var(--border-color);
text-align: center;
}
.social-links {
display: flex;
justify-content: center;
gap: 1.5rem;
margin-bottom: 2rem;
}
.social-links a {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
background-color: var(--bg-lighter);
border-radius: 50%;
transition: all var(--transition-speed) ease;
box-shadow: var(--card-shadow);
}
.social-links a i {
font-size: 1.5rem;
color: var(--text-color);
transition: all var(--transition-speed) ease;
}
.social-links a:hover {
transform: translateY(-5px);
background-color: var(--primary-color);
box-shadow: var(--glow-effect);
}
.social-links a:hover i {
color: white;
}
.footer-text {
color: var(--text-muted);
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(108, 92, 231, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(108, 92, 231, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(108, 92, 231, 0);
}
}
/* Responsive Styles */
@media screen and (max-width: 992px) {
.container {
padding: 0 40px;
}
.hero-content h2 {
font-size: 2.5rem;
}
.contact-content {
grid-template-columns: 1fr;
gap: 3rem;
}
}
@media screen and (max-width: 768px) {
.header-content {
flex-direction: column;
}
nav ul {
margin-top: 1rem;
}
nav ul li {
margin: 0 1rem;
}
.hero {
height: auto;
padding: 6rem 0;
}
.hero-content h2 {
font-size: 2rem;
}
.hero-buttons {
flex-direction: column;
gap: 1rem;
}
.about-content {
flex-direction: column;
gap: 2rem;
}
}
@media screen and (max-width: 576px) {
.container {
padding: 0 20px;
}
.section-header h2 {
font-size: 2rem;
}
nav ul {
flex-wrap: wrap;
justify-content: center;
}
nav ul li {
margin: 0.5rem;
}
.tools-grid {
grid-template-columns: repeat(2, 1fr);
}
}

175
static/js/script.js Normal file
View File

@ -0,0 +1,175 @@
document.addEventListener('DOMContentLoaded', function() {
// Initialize animations for elements when they come into view
const animateOnScroll = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
const elementPosition = element.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (elementPosition < windowHeight - 100) {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}
});
};
// Set initial styles for animation
const initAnimations = () => {
const elements = document.querySelectorAll('.section-header, .projects-grid, .skills-content, .tools-grid, .about-content, .contact-content');
elements.forEach(element => {
element.style.opacity = '0';
element.style.transform = 'translateY(20px)';
element.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
});
};
// Animate progress bars when skills section is in view
const animateSkillBars = () => {
const skillsSection = document.getElementById('skills');
if (!skillsSection) return;
const skillBars = document.querySelectorAll('.skill-progress');
const sectionPosition = skillsSection.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (sectionPosition < windowHeight - 100) {
skillBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0';
setTimeout(() => {
bar.style.width = width;
}, 100);
});
}
};
// Smooth scrolling for navigation links
const smoothScroll = () => {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 80,
behavior: 'smooth'
});
}
});
});
};
// Form submission handling
const handleFormSubmission = () => {
const contactForm = document.querySelector('.contact-form form');
if (!contactForm) return;
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// Get form data
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const message = document.getElementById('message').value;
// In a real-world scenario, you would send this data to a server
console.log('Form submission:', { name, email, message });
// Show success message
alert('Thank you for your message! I will get back to you soon.');
// Reset form
contactForm.reset();
});
};
// Initialize all functionality
initAnimations();
animateOnScroll(); // Initial check on page load
smoothScroll();
handleFormSubmission();
// Listen for scroll events
window.addEventListener('scroll', () => {
animateOnScroll();
animateSkillBars();
});
// Handle window resize events
window.addEventListener('resize', () => {
animateOnScroll();
});
// Add active state to navigation links based on scroll position
const setActiveNavLink = () => {
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('nav ul li a');
window.addEventListener('scroll', () => {
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (pageYOffset >= (sectionTop - 200)) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${current}`) {
link.classList.add('active');
}
});
});
};
setActiveNavLink();
// Enhance flip cards for touch devices
const enhanceFlipCards = () => {
const flipCards = document.querySelectorAll('.flip-card');
flipCards.forEach(card => {
// For touch devices
card.addEventListener('touchstart', function() {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(180deg)';
});
// Return to front after delay
card.addEventListener('mouseleave', function() {
setTimeout(() => {
this.querySelector('.flip-card-inner').style.transform = 'rotateY(0deg)';
}, 1000);
});
});
};
enhanceFlipCards();
// Add CSS class to style active navigation link
const styleActiveNavLinks = () => {
const style = document.createElement('style');
style.textContent = `
nav ul li a.active {
color: var(--primary-light);
}
nav ul li a.active::after {
width: 100%;
}
`;
document.head.appendChild(style);
};
styleActiveNavLinks();
});

199
templates/index.html Normal file
View File

@ -0,0 +1,199 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jonathan Peters | Front-End Developer</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="page-container">
<!-- Header Section -->
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<h1>Jonathan Peters</h1>
<p class="subtitle">Front-End Developer</p>
</div>
<nav>
<ul>
<li><a href="#projects">Projects</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#tools">Tools</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</div>
</header>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h2>Creating <span class="highlight">Beautiful</span> &amp; <span class="highlight">Functional</span> Web Experiences</h2>
<p>I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.</p>
<div class="hero-buttons">
<a href="#projects" class="btn primary-btn">View My Work</a>
<a href="#contact" class="btn secondary-btn">Get In Touch</a>
</div>
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="projects-section">
<div class="container">
<div class="section-header">
<h2>My Projects</h2>
<p>Explore some of my recent work</p>
</div>
<div class="projects-grid">
{% for project in projects %}
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<i class="{{ project.image.split('/')[-1].split('.')[0] }}"></i>
<h3>{{ project.title }}</h3>
</div>
<div class="flip-card-back">
<h3>{{ project.title }}</h3>
<p>{{ project.description }}</p>
<a href="{{ project.link }}" class="btn view-project-btn" target="_blank">View Project</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- Skills Section -->
<section id="skills" class="skills-section">
<div class="container">
<div class="section-header">
<h2>My Skills</h2>
<p>Technical proficiency and expertise</p>
</div>
<div class="skills-content">
{% for skill in skills %}
<div class="skill-item">
<div class="skill-info">
<div class="skill-name">
<i class="{{ skill.icon }}"></i>
<span>{{ skill.name }}</span>
</div>
<div class="skill-percentage">{{ skill.percentage }}%</div>
</div>
<div class="skill-bar">
<div class="skill-progress" style="width: {{ skill.percentage }}%"></div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- Tools Section -->
<section id="tools" class="tools-section">
<div class="container">
<div class="section-header">
<h2>Languages &amp; Tools</h2>
<p>Technologies I work with</p>
</div>
<div class="tools-grid">
{% for tool in tools %}
<div class="tool-item">
<i class="{{ tool.icon }}"></i>
<span>{{ tool.name }}</span>
</div>
{% endfor %}
</div>
</div>
</section>
<!-- About Section -->
<section id="about" class="about-section">
<div class="container">
<div class="section-header">
<h2>About Me</h2>
<p>Get to know me better</p>
</div>
<div class="about-content">
<div class="about-text">
<p>Hi, I'm Jonathan Peters, a front-end developer passionate about creating beautiful and functional web experiences. With expertise in HTML, CSS, JavaScript, and various frameworks, I strive to build responsive and user-friendly websites and applications.</p>
<p>My journey in web development started with a curiosity for how things work on the internet, which quickly turned into a passion for creating my own digital experiences. I continuously learn and adapt to stay updated with the latest technologies and best practices in the industry.</p>
<p>I have 3+ years experience & i have completed the <a href="#">Responsive Web Design</a> & the <a href="#">Data Visualizations</a> course on freeCodeCamp</p>
<p>When I'm not coding, you might find me exploring new design trends, contributing to open-source projects, or experimenting with new technologies to enhance my skill set.</p>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section">
<div class="container">
<div class="section-header">
<h2>Get In Touch</h2>
<p>Let's work together on your next project</p>
</div>
<div class="contact-content">
<div class="contact-form">
<form>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit" class="btn primary-btn">Send Message</button>
</form>
</div>
<div class="contact-info">
<div class="contact-item">
<i class="fas fa-envelope"></i>
<span>jonathanpeters051@gmail.com</span>
</div>
<div class="contact-item">
<i class="fas fa-phone"></i>
<span>+27661199255</span>
</div>
<div class="contact-item">
<i class="fas fa-map-marker-alt"></i>
<span>George, Western Cape. South Africa</span>
</div>
</div>
</div>
</div>
</section>
<!-- Footer Section -->
<footer>
<div class="container">
<div class="social-links">
{% for social in social_links %}
<a href="{{ social.link }}" target="_blank" title="{{ social.platform }}">
<i class="{{ social.icon }}"></i>
</a>
{% endfor %}
</div>
<div class="footer-text">
<p>&copy; 2025 Jonathan Peters. All rights reserved.</p>
</div>
</div>
</footer>
</div>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>

346
uv.lock generated Normal file
View File

@ -0,0 +1,346 @@
version = 1
requires-python = ">=3.11"
[[package]]
name = "blinker"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "dnspython"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
]
[[package]]
name = "email-validator"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "dnspython" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 },
]
[[package]]
name = "flask"
version = "3.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blinker" },
{ name = "click" },
{ name = "itsdangerous" },
{ name = "jinja2" },
{ name = "werkzeug" },
]
sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979 },
]
[[package]]
name = "flask-sqlalchemy"
version = "3.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "flask" },
{ name = "sqlalchemy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/91/53/b0a9fcc1b1297f51e68b69ed3b7c3c40d8c45be1391d77ae198712914392/flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312", size = 81899 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/6a/89963a5c6ecf166e8be29e0d1bf6806051ee8fe6c82e232842e3aeac9204/flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", size = 25125 },
]
[[package]]
name = "greenlet"
version = "3.1.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 },
{ url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 },
{ url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 },
{ url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 },
{ url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 },
{ url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 },
{ url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 },
{ url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 },
{ url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 },
{ url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 },
{ url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 },
{ url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 },
{ url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 },
{ url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 },
{ url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 },
{ url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 },
{ url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 },
{ url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 },
{ url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 },
{ url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 },
{ url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 },
{ url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 },
{ url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 },
{ url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 },
{ url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 },
{ url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 },
{ url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 },
{ url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 },
{ url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 },
{ url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 },
{ url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 },
{ url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 },
{ url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 },
{ url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
]
[[package]]
name = "gunicorn"
version = "23.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "itsdangerous"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
]
[[package]]
name = "markupsafe"
version = "3.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 },
{ url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 },
{ url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 },
{ url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 },
{ url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 },
{ url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 },
{ url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 },
{ url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 },
{ url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 },
{ url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 },
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "psycopg2-binary"
version = "2.9.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/8f/9feb01291d0d7a0a4c6a6bab24094135c2b59c6a81943752f632c75896d6/psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", size = 3043397 },
{ url = "https://files.pythonhosted.org/packages/15/30/346e4683532011561cd9c8dfeac6a8153dd96452fee0b12666058ab7893c/psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", size = 3274806 },
{ url = "https://files.pythonhosted.org/packages/66/6e/4efebe76f76aee7ec99166b6c023ff8abdc4e183f7b70913d7c047701b79/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", size = 2851370 },
{ url = "https://files.pythonhosted.org/packages/7f/fd/ff83313f86b50f7ca089b161b8e0a22bb3c319974096093cd50680433fdb/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", size = 3080780 },
{ url = "https://files.pythonhosted.org/packages/e6/c4/bfadd202dcda8333a7ccafdc51c541dbdfce7c2c7cda89fa2374455d795f/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", size = 3264583 },
{ url = "https://files.pythonhosted.org/packages/5d/f1/09f45ac25e704ac954862581f9f9ae21303cc5ded3d0b775532b407f0e90/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", size = 3019831 },
{ url = "https://files.pythonhosted.org/packages/9e/2e/9beaea078095cc558f215e38f647c7114987d9febfc25cb2beed7c3582a5/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", size = 2871822 },
{ url = "https://files.pythonhosted.org/packages/01/9e/ef93c5d93f3dc9fc92786ffab39e323b9aed066ba59fdc34cf85e2722271/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", size = 2820975 },
{ url = "https://files.pythonhosted.org/packages/a5/f0/049e9631e3268fe4c5a387f6fc27e267ebe199acf1bc1bc9cbde4bd6916c/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", size = 2919320 },
{ url = "https://files.pythonhosted.org/packages/dc/9a/bcb8773b88e45fb5a5ea8339e2104d82c863a3b8558fbb2aadfe66df86b3/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", size = 2957617 },
{ url = "https://files.pythonhosted.org/packages/e2/6b/144336a9bf08a67d217b3af3246abb1d027095dab726f0687f01f43e8c03/psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", size = 1024618 },
{ url = "https://files.pythonhosted.org/packages/61/69/3b3d7bd583c6d3cbe5100802efa5beacaacc86e37b653fc708bf3d6853b8/psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", size = 1163816 },
{ url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 },
{ url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 },
{ url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 },
{ url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 },
{ url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 },
{ url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 },
{ url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 },
{ url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 },
{ url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 },
{ url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 },
{ url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 },
{ url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 },
{ url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699 },
{ url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245 },
{ url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631 },
{ url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140 },
{ url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762 },
{ url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967 },
{ url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326 },
{ url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
{ url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
{ url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
]
[[package]]
name = "repl-nix-workspace"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "email-validator" },
{ name = "flask" },
{ name = "flask-sqlalchemy" },
{ name = "gunicorn" },
{ name = "psycopg2-binary" },
]
[package.metadata]
requires-dist = [
{ name = "email-validator", specifier = ">=2.2.0" },
{ name = "flask", specifier = ">=3.1.0" },
{ name = "flask-sqlalchemy", specifier = ">=3.1.1" },
{ name = "gunicorn", specifier = ">=23.0.0" },
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.40"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/c3/3f2bfa5e4dcd9938405fe2fab5b6ab94a9248a4f9536ea2fd497da20525f/sqlalchemy-2.0.40.tar.gz", hash = "sha256:d827099289c64589418ebbcaead0145cd19f4e3e8a93919a0100247af245fa00", size = 9664299 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/7e/55044a9ec48c3249bb38d5faae93f09579c35e862bb318ebd1ed7a1994a5/sqlalchemy-2.0.40-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6bacab7514de6146a1976bc56e1545bee247242fab030b89e5f70336fc0003e", size = 2114025 },
{ url = "https://files.pythonhosted.org/packages/77/0f/dcf7bba95f847aec72f638750747b12d37914f71c8cc7c133cf326ab945c/sqlalchemy-2.0.40-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5654d1ac34e922b6c5711631f2da497d3a7bffd6f9f87ac23b35feea56098011", size = 2104419 },
{ url = "https://files.pythonhosted.org/packages/75/70/c86a5c20715e4fe903dde4c2fd44fc7e7a0d5fb52c1b954d98526f65a3ea/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35904d63412db21088739510216e9349e335f142ce4a04b69e2528020ee19ed4", size = 3222720 },
{ url = "https://files.pythonhosted.org/packages/12/cf/b891a8c1d0c27ce9163361664c2128c7a57de3f35000ea5202eb3a2917b7/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7a80ed86d6aaacb8160a1caef6680d4ddd03c944d985aecee940d168c411d1", size = 3222682 },
{ url = "https://files.pythonhosted.org/packages/15/3f/7709d8c8266953d945435a96b7f425ae4172a336963756b58e996fbef7f3/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:519624685a51525ddaa7d8ba8265a1540442a2ec71476f0e75241eb8263d6f51", size = 3159542 },
{ url = "https://files.pythonhosted.org/packages/85/7e/717eaabaf0f80a0132dc2032ea8f745b7a0914451c984821a7c8737fb75a/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2ee5f9999a5b0e9689bed96e60ee53c3384f1a05c2dd8068cc2e8361b0df5b7a", size = 3179864 },
{ url = "https://files.pythonhosted.org/packages/e4/cc/03eb5dfcdb575cbecd2bd82487b9848f250a4b6ecfb4707e834b4ce4ec07/sqlalchemy-2.0.40-cp311-cp311-win32.whl", hash = "sha256:c0cae71e20e3c02c52f6b9e9722bca70e4a90a466d59477822739dc31ac18b4b", size = 2084675 },
{ url = "https://files.pythonhosted.org/packages/9a/48/440946bf9dc4dc231f4f31ef0d316f7135bf41d4b86aaba0c0655150d370/sqlalchemy-2.0.40-cp311-cp311-win_amd64.whl", hash = "sha256:574aea2c54d8f1dd1699449f332c7d9b71c339e04ae50163a3eb5ce4c4325ee4", size = 2110099 },
{ url = "https://files.pythonhosted.org/packages/92/06/552c1f92e880b57d8b92ce6619bd569b25cead492389b1d84904b55989d8/sqlalchemy-2.0.40-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9d3b31d0a1c44b74d3ae27a3de422dfccd2b8f0b75e51ecb2faa2bf65ab1ba0d", size = 2112620 },
{ url = "https://files.pythonhosted.org/packages/01/72/a5bc6e76c34cebc071f758161dbe1453de8815ae6e662393910d3be6d70d/sqlalchemy-2.0.40-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37f7a0f506cf78c80450ed1e816978643d3969f99c4ac6b01104a6fe95c5490a", size = 2103004 },
{ url = "https://files.pythonhosted.org/packages/bf/fd/0e96c8e6767618ed1a06e4d7a167fe13734c2f8113c4cb704443e6783038/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bb933a650323e476a2e4fbef8997a10d0003d4da996aad3fd7873e962fdde4d", size = 3252440 },
{ url = "https://files.pythonhosted.org/packages/cd/6a/eb82e45b15a64266a2917a6833b51a334ea3c1991728fd905bfccbf5cf63/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959738971b4745eea16f818a2cd086fb35081383b078272c35ece2b07012716", size = 3263277 },
{ url = "https://files.pythonhosted.org/packages/45/97/ebe41ab4530f50af99e3995ebd4e0204bf1b0dc0930f32250dde19c389fe/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:110179728e442dae85dd39591beb74072ae4ad55a44eda2acc6ec98ead80d5f2", size = 3198591 },
{ url = "https://files.pythonhosted.org/packages/e6/1c/a569c1b2b2f5ac20ba6846a1321a2bf52e9a4061001f282bf1c5528dcd69/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8040680eaacdce4d635f12c55c714f3d4c7f57da2bc47a01229d115bd319191", size = 3225199 },
{ url = "https://files.pythonhosted.org/packages/8f/91/87cc71a6b10065ca0209d19a4bb575378abda6085e72fa0b61ffb2201b84/sqlalchemy-2.0.40-cp312-cp312-win32.whl", hash = "sha256:650490653b110905c10adac69408380688cefc1f536a137d0d69aca1069dc1d1", size = 2082959 },
{ url = "https://files.pythonhosted.org/packages/2a/9f/14c511cda174aa1ad9b0e42b64ff5a71db35d08b0d80dc044dae958921e5/sqlalchemy-2.0.40-cp312-cp312-win_amd64.whl", hash = "sha256:2be94d75ee06548d2fc591a3513422b873490efb124048f50556369a834853b0", size = 2108526 },
{ url = "https://files.pythonhosted.org/packages/8c/18/4e3a86cc0232377bc48c373a9ba6a1b3fb79ba32dbb4eda0b357f5a2c59d/sqlalchemy-2.0.40-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:915866fd50dd868fdcc18d61d8258db1bf9ed7fbd6dfec960ba43365952f3b01", size = 2107887 },
{ url = "https://files.pythonhosted.org/packages/cb/60/9fa692b1d2ffc4cbd5f47753731fd332afed30137115d862d6e9a1e962c7/sqlalchemy-2.0.40-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a4c5a2905a9ccdc67a8963e24abd2f7afcd4348829412483695c59e0af9a705", size = 2098367 },
{ url = "https://files.pythonhosted.org/packages/4c/9f/84b78357ca641714a439eb3fbbddb17297dacfa05d951dbf24f28d7b5c08/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55028d7a3ebdf7ace492fab9895cbc5270153f75442a0472d8516e03159ab364", size = 3184806 },
{ url = "https://files.pythonhosted.org/packages/4b/7d/e06164161b6bfce04c01bfa01518a20cccbd4100d5c951e5a7422189191a/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cfedff6878b0e0d1d0a50666a817ecd85051d12d56b43d9d425455e608b5ba0", size = 3198131 },
{ url = "https://files.pythonhosted.org/packages/6d/51/354af20da42d7ec7b5c9de99edafbb7663a1d75686d1999ceb2c15811302/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb19e30fdae77d357ce92192a3504579abe48a66877f476880238a962e5b96db", size = 3131364 },
{ url = "https://files.pythonhosted.org/packages/7a/2f/48a41ff4e6e10549d83fcc551ab85c268bde7c03cf77afb36303c6594d11/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:16d325ea898f74b26ffcd1cf8c593b0beed8714f0317df2bed0d8d1de05a8f26", size = 3159482 },
{ url = "https://files.pythonhosted.org/packages/33/ac/e5e0a807163652a35be878c0ad5cfd8b1d29605edcadfb5df3c512cdf9f3/sqlalchemy-2.0.40-cp313-cp313-win32.whl", hash = "sha256:a669cbe5be3c63f75bcbee0b266779706f1a54bcb1000f302685b87d1b8c1500", size = 2080704 },
{ url = "https://files.pythonhosted.org/packages/1c/cb/f38c61f7f2fd4d10494c1c135ff6a6ddb63508d0b47bccccd93670637309/sqlalchemy-2.0.40-cp313-cp313-win_amd64.whl", hash = "sha256:641ee2e0834812d657862f3a7de95e0048bdcb6c55496f39c6fa3d435f6ac6ad", size = 2104564 },
{ url = "https://files.pythonhosted.org/packages/d1/7c/5fc8e802e7506fe8b55a03a2e1dab156eae205c91bee46305755e086d2e2/sqlalchemy-2.0.40-py3-none-any.whl", hash = "sha256:32587e2e1e359276957e6fe5dad089758bc042a971a8a09ae8ecf7a8fe23d07a", size = 1903894 },
]
[[package]]
name = "typing-extensions"
version = "4.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 },
]
[[package]]
name = "werkzeug"
version = "3.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 },
]