diff --git a/JonathanMyPortfolio/JonathanPetersCV.pdf b/JonathanMyPortfolio/JonathanPetersCV.pdf new file mode 100644 index 0000000..34f9c46 Binary files /dev/null and b/JonathanMyPortfolio/JonathanPetersCV.pdf differ diff --git a/JonathanMyPortfolio/app.py b/JonathanMyPortfolio/app.py new file mode 100644 index 0000000..00858f6 --- /dev/null +++ b/JonathanMyPortfolio/app.py @@ -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) diff --git a/JonathanMyPortfolio/calculator.png b/JonathanMyPortfolio/calculator.png new file mode 100644 index 0000000..ee37a12 Binary files /dev/null and b/JonathanMyPortfolio/calculator.png differ diff --git a/JonathanMyPortfolio/cryptotracker.png b/JonathanMyPortfolio/cryptotracker.png new file mode 100644 index 0000000..909509f Binary files /dev/null and b/JonathanMyPortfolio/cryptotracker.png differ diff --git a/JonathanMyPortfolio/css/normalize.css b/JonathanMyPortfolio/css/normalize.css new file mode 100644 index 0000000..b0c1902 --- /dev/null +++ b/JonathanMyPortfolio/css/normalize.css @@ -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; +} \ No newline at end of file diff --git a/JonathanMyPortfolio/css/style.css b/JonathanMyPortfolio/css/style.css new file mode 100644 index 0000000..bd09341 --- /dev/null +++ b/JonathanMyPortfolio/css/style.css @@ -0,0 +1,1423 @@ +/* Base Styles and Variables */ +:root { + --primary-color: #6c5ce7; + --primary-color-rgb: 108, 92, 231; + --primary-light: #a29bfe; + --primary-light-rgb: 162, 155, 254; + --secondary-color: #00cec9; + --secondary-color-rgb: 0, 206, 201; + --bg-dark: #121212; + --bg-dark-rgb: 18, 18, 18; + --bg-light: #1e1e1e; + --bg-light-rgb: 30, 30, 30; + --bg-lighter: #2a2a2a; + --bg-lighter-rgb: 42, 42, 42; + --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); + position: relative; + overflow: hidden; + z-index: 1; +} + +.primary-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + rgba(255, 255, 255, 0.1), + rgba(255, 255, 255, 0.2), + rgba(255, 255, 255, 0.1) + ); + transition: left 0.8s ease; + z-index: -1; +} + +.primary-btn:hover::before { + left: 100%; +} + +.secondary-btn { + background: transparent; + border: 2px solid var(--primary-color); + color: var(--text-color); + position: relative; + overflow: hidden; + z-index: 1; +} + +.secondary-btn::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0%; + height: 0%; + background-color: var(--primary-color); + border-radius: 50%; + transform: translate(-50%, -50%); + transition: width 0.4s ease-out, height 0.4s ease-out; + z-index: -1; + opacity: 0; +} + +.primary-btn:hover { + box-shadow: 0 6px 20px rgba(108, 92, 231, 0.5); + transform: translateY(-3px) scale(1.02); + color: white; +} + +.secondary-btn:hover { + color: white; + transform: translateY(-3px) scale(1.02); + box-shadow: 0 6px 20px rgba(108, 92, 231, 0.3); + border-color: transparent; +} + +.secondary-btn:hover::before { + width: 300%; + height: 300%; + opacity: 1; +} + +.primary-btn:active, +.secondary-btn:active { + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(108, 92, 231, 0.3); +} + +.download-cv-btn { + background: linear-gradient(45deg, var(--secondary-color), #38b2ac); + color: white; + box-shadow: 0 4px 15px rgba(0, 206, 201, 0.3); + margin-top: 1rem; + position: relative; + overflow: hidden; +} + +.download-cv-btn::after { + content: "\f019"; /* FontAwesome download icon */ + font-family: "Font Awesome 5 Free"; + font-weight: 900; + position: absolute; + font-size: 1.2rem; + top: 50%; + right: 20px; + transform: translate(20px, -50%); + opacity: 0; + transition: all 0.3s ease; +} + +.download-cv-btn:hover { + box-shadow: 0 6px 20px rgba(0, 206, 201, 0.5); + transform: translateY(-3px); + color: white; + padding-right: 45px; +} + +.download-cv-btn:hover::after { + transform: translate(0, -50%); + opacity: 1; +} + +.download-cv-btn:active { + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(0, 206, 201, 0.3); +} + +/* Success Notification */ +.notification { + position: fixed; + bottom: 20px; + right: 20px; + background-color: var(--primary-color); + color: white; + padding: 1rem 2rem; + border-radius: 5px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + z-index: 1000; + transform: translateY(150%); + transition: transform 0.4s ease; +} + +.notification.show { + transform: translateY(0); +} + +.notification i { + margin-right: 10px; +} + +.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); + overflow: hidden; +} + +/* Particles container */ +#particles-js { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + z-index: 10; +} + +.logo { + position: relative; + cursor: pointer; + padding: 5px 10px; + border-radius: 5px; + transition: all var(--transition-speed) cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.logo::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, + rgba(var(--primary-color-rgb), 0.05), + rgba(var(--secondary-color-rgb), 0.05) + ); + border-radius: 5px; + opacity: 0; + transition: opacity var(--transition-speed) ease; + z-index: -1; +} + +.logo:hover { + transform: scale(1.03); +} + +.logo:hover::before { + opacity: 1; +} + +.logo h1 { + font-size: 1.8rem; + margin-bottom: 5px; + font-weight: 700; + background: linear-gradient( + 90deg, + var(--text-color) 0%, + var(--text-color) 50%, + var(--primary-light) 50%, + var(--primary-light) 100% + ); + background-size: 200% 100%; + background-position: 100% 0; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + transition: background-position var(--transition-speed) ease; +} + +.logo:hover h1 { + background-position: 0 0; +} + +.subtitle { + font-size: 1rem; + color: var(--primary-light); + margin: 0; + transition: transform var(--transition-speed) cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.logo:hover .subtitle { + transform: translateY(2px); +} + +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: 8px 12px; + margin-bottom: 5px; + border-radius: 5px; + transition: all var(--transition-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275); + background-color: transparent; + overflow: hidden; +} + +nav ul li a::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(var(--primary-color-rgb), 0.1), + transparent + ); + transition: left 0.5s ease; + z-index: -1; +} + +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) cubic-bezier(0.68, -0.55, 0.27, 1.55); +} + +nav ul li a:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + background-color: rgba(var(--primary-color-rgb), 0.05); + color: var(--primary-light); +} + +nav ul li a:hover::before { + left: 100%; +} + +nav ul li a:hover::after { + width: 100%; +} + +nav ul li a:active { + transform: translateY(0); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +/* 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; + transition: all 0.3s ease; + position: relative; + z-index: 1; +} + +.flip-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 10px; + background: var(--primary-color); + opacity: 0; + z-index: -1; + transition: opacity 0.3s ease; + box-shadow: 0 0 15px rgba(108, 92, 231, 0.5); +} + +.flip-card:hover { + transform: translateY(-10px); +} + +.flip-card:hover::before { + opacity: 0.1; +} + +.flip-card-inner { + position: relative; + width: 100%; + height: 100%; + transition: transform 0.8s, box-shadow 0.3s ease; + transform-style: preserve-3d; + box-shadow: var(--card-shadow); + border-radius: 10px; +} + +.flip-card:hover .flip-card-inner { + transform: rotateY(180deg); + box-shadow: var(--glow-effect); +} + +.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; + transition: all 0.5s ease; +} + +.flip-card-front { + background-color: var(--bg-lighter); + border: 1px solid var(--border-color); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1); +} + +.flip-card:hover .flip-card-front { + background-color: rgba(var(--bg-lighter-rgb), 0.95); +} + +.flip-card-front i { + font-size: 4rem; + margin-bottom: 1rem; + color: var(--primary-color); + transition: transform 0.5s ease, color 0.3s ease; +} + +.flip-card:hover .flip-card-front i { + transform: scale(1.1); + color: var(--primary-light); +} + +.flip-card-front h3 { + transition: transform 0.3s ease, color 0.3s ease; + position: relative; +} + +.flip-card:hover .flip-card-front h3 { + color: var(--primary-light); +} + +.flip-card-front h3::after { + content: ''; + position: absolute; + bottom: -8px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 2px; + background: var(--primary-light); + transition: width 0.3s ease; +} + +.flip-card:hover .flip-card-front h3::after { + width: 50%; +} + +.flip-card-front .project-image { + width: 80%; + height: auto; + max-height: 120px; + margin-bottom: 1rem; + border-radius: 5px; + object-fit: contain; + transition: transform 0.5s ease, filter 0.3s ease; +} + +.flip-card:hover .flip-card-front .project-image { + transform: scale(1.05); + filter: brightness(1.1); +} + +.flip-card-back { + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + color: white; + transform: rotateY(180deg); + box-shadow: inset 0 0 20px rgba(255, 255, 255, 0.2); + overflow: hidden; + position: relative; +} + +.flip-card-back::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + transparent, + rgba(255, 255, 255, 0.1), + transparent + ); + transform: rotate(45deg); + transition: transform 1s ease; + opacity: 0; +} + +.flip-card:hover .flip-card-back::before { + transform: rotate(45deg) translate(0, 100%); + opacity: 1; +} + +.flip-card-back h3 { + margin-bottom: 1rem; + position: relative; + display: inline-block; +} + +.flip-card-back p { + margin-bottom: 1.5rem; + font-size: 0.9rem; + max-width: 90%; + transition: transform 0.3s ease; +} + +.flip-card:hover .flip-card-back p { + transform: scale(1.02); +} + +.view-project-btn { + background-color: rgba(255, 255, 255, 0.2); + color: white; + padding: 8px 20px; + border-radius: 20px; + transition: all var(--transition-speed) ease; + position: relative; + overflow: hidden; + z-index: 1; +} + +.view-project-btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 0; + height: 100%; + background-color: white; + transition: width 0.3s ease; + z-index: -1; + border-radius: 20px; +} + +.view-project-btn:hover::before { + width: 100%; +} + +.view-project-btn:hover { + color: var(--primary-color); + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); +} + +/* 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); + position: relative; + overflow: hidden; + z-index: 1; +} + +.tool-item::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, + rgba(var(--primary-color-rgb), 0.1), + rgba(var(--secondary-color-rgb), 0.1) + ); + opacity: 0; + z-index: -1; + transition: opacity 0.3s ease; +} + +.tool-item::after { + content: ''; + position: absolute; + bottom: -5px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 3px; + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +.tool-item i { + font-size: 3rem; + margin-bottom: 1rem; + color: var(--primary-color); + transition: transform 0.3s ease, color 0.3s ease; +} + +.tool-item span { + transition: color 0.3s ease; +} + +.tool-item:hover { + transform: translateY(-10px); + box-shadow: var(--glow-effect); + border-color: rgba(var(--primary-color-rgb), 0.3); +} + +.tool-item:hover::before { + opacity: 1; +} + +.tool-item:hover::after { + width: 80%; +} + +.tool-item:hover i { + transform: scale(1.1); + color: var(--primary-light); +} + +.tool-item:hover span { + color: var(--primary-light); +} + +/* 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; +} + +.about-text a { + color: var(--primary-light); + font-weight: 500; + transition: all var(--transition-speed) ease; + position: relative; + padding-bottom: 2px; +} + +.about-text a::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 1px; + background: var(--primary-light); + transition: width var(--transition-speed) ease; +} + +.about-text a:hover { + color: var(--primary-color); +} + +.about-text a:hover::after { + width: 100%; +} + +/* 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); + transition: all var(--transition-speed) ease; + position: relative; + z-index: 1; + overflow: hidden; +} + +.contact-form::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 5px; + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + transform: scaleX(0); + transform-origin: left; + transition: transform 0.5s ease; + z-index: 2; +} + +.contact-form:focus-within { + box-shadow: 0 10px 25px rgba(var(--primary-color-rgb), 0.15); + transform: translateY(-5px); +} + +.contact-form:focus-within::before { + transform: scaleX(1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; +} + +.form-group { + position: relative; +} + +.form-group label { + position: relative; + padding-left: 5px; + transition: all var(--transition-speed) ease; +} + +.form-group label::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 0; + background: var(--primary-color); + transition: height 0.3s ease; +} + +.form-group:focus-within label { + color: var(--primary-light); + padding-left: 10px; +} + +.form-group:focus-within label::before { + height: 80%; +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: 12px 15px; + background-color: var(--bg-dark); + border: 1px solid var(--border-color); + border-radius: 5px; + color: var(--text-color); + transition: all var(--transition-speed) cubic-bezier(0.165, 0.84, 0.44, 1); + position: relative; + z-index: 1; +} + +.form-group::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + width: 0; + height: 2px; + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease, left 0.3s ease; + z-index: 2; +} + +.form-group:focus-within::after { + width: 100%; + left: 0; +} + +.form-group input:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 5px 15px rgba(var(--primary-color-rgb), 0.2); + transform: translateY(-2px); + padding-left: 20px; +} + +.form-group input:focus::placeholder, +.form-group textarea:focus::placeholder { + opacity: 0.7; + transform: translateX(5px); +} + +.form-group input::placeholder, +.form-group textarea::placeholder { + color: rgba(var(--text-muted), 0.7); + transition: all var(--transition-speed) ease; +} + +.submit-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + overflow: hidden; + position: relative; + transition: all var(--transition-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +.submit-btn span { + transition: transform var(--transition-speed) ease; +} + +.submit-btn i { + font-size: 0; + transform: translateX(20px); + opacity: 0; + transition: all var(--transition-speed) cubic-bezier(0.68, -0.55, 0.27, 1.55); +} + +.submit-btn:hover span { + transform: translateX(-5px); +} + +.submit-btn:hover i { + font-size: 1rem; + transform: translateX(0); + opacity: 1; +} + +.submit-btn:active i { + transform: translateX(5px); +} + +.submit-btn:disabled { + cursor: not-allowed; + opacity: 0.8; + transform: none; +} + +.email-status { + margin-top: 1rem; + padding: 10px; + border-radius: 5px; + text-align: center; + font-weight: 500; + display: none; + animation: fadeIn 0.5s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.email-status.success { + display: block; + background-color: rgba(39, 174, 96, 0.2); + color: #27ae60; + border: 1px solid #27ae60; +} + +.email-status.error { + display: block; + background-color: rgba(231, 76, 60, 0.2); + color: #e74c3c; + border: 1px solid #e74c3c; +} + +.contact-info { + display: flex; + flex-direction: column; + justify-content: center; +} + +.contact-item { + display: flex; + align-items: center; + margin-bottom: 2rem; +} + +.contact-item { + position: relative; + transition: all var(--transition-speed) ease; +} + +.contact-item:hover { + transform: translateX(5px); +} + +.contact-item i { + font-size: 2rem; + margin-right: 1rem; + color: var(--primary-color); + position: relative; + z-index: 1; + transition: all var(--transition-speed) ease; +} + +.contact-item:hover i { + color: var(--primary-light); + animation: pulse-icon 1s infinite; +} + +.contact-item::before { + content: ''; + position: absolute; + top: 50%; + left: 1rem; + transform: translate(-50%, -50%); + width: 3rem; + height: 3rem; + background-color: rgba(var(--primary-color-rgb), 0.1); + border-radius: 50%; + opacity: 0; + transition: opacity 0.3s ease; + z-index: 0; +} + +.contact-item:hover::before { + opacity: 1; +} + +@keyframes pulse-icon { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + } +} + +/* Footer Section */ +footer { + padding: 3rem 0; + background-color: var(--bg-dark); + border-top: 1px solid var(--border-color); + text-align: center; + position: relative; + overflow: hidden; +} + +#footer-particles-js { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; +} + +footer .container { + position: relative; + z-index: 1; +} + +.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) cubic-bezier(0.68, -0.55, 0.27, 1.55); + box-shadow: var(--card-shadow); + position: relative; + overflow: hidden; +} + +.social-links a::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + z-index: 0; + opacity: 0; + transform: scale(0); + transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease; +} + +.social-links a i { + font-size: 1.5rem; + color: var(--text-color); + transition: all var(--transition-speed) ease; + position: relative; + z-index: 1; +} + +.social-links a:hover { + transform: translateY(-5px) rotate(5deg); + box-shadow: var(--glow-effect); +} + +.social-links a:active { + transform: translateY(-2px) rotate(0deg); +} + +.social-links a:hover::before { + opacity: 1; + transform: scale(1); +} + +.social-links a:hover i { + color: white; + transform: scale(1.2); + animation: social-pulse 1.5s infinite alternate; +} + +@keyframes social-pulse { + 0% { + transform: scale(1); + } + 100% { + transform: scale(1.2); + } +} + +.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); + } +} + +/* Navigation animations */ +nav ul li a.fadeIn { + animation: fadeIn 0.5s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +nav ul li a.bounce { + animation: bounce 0.6s ease; +} + +@keyframes bounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateY(0); + } + 40% { + transform: translateY(-10px); + } + 60% { + transform: translateY(-5px); + } +} + +/* Header state animations */ +header.scrolling { + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); + transition: box-shadow 0.3s ease; +} + +header.scrolling .logo { + transform: scale(0.98); +} + +/* Typing animation for project descriptions */ +.typing-text-container { + text-align: center; + min-height: 3rem; + margin-bottom: 1rem; + display: flex; + justify-content: center; +} + +.typing-text { + display: inline-block; + position: relative; + overflow: hidden; + border-right: 2px solid var(--primary-color); + white-space: nowrap; + margin: 0 auto; + animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite; +} + +@keyframes typing { + from { width: 0 } + to { width: 100% } +} + +@keyframes blink-caret { + from, to { border-color: transparent } + 50% { border-color: var(--primary-color) } +} + +/* 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); + } +} \ No newline at end of file diff --git a/JonathanMyPortfolio/digitaldividerecords.png b/JonathanMyPortfolio/digitaldividerecords.png new file mode 100644 index 0000000..9a9e707 Binary files /dev/null and b/JonathanMyPortfolio/digitaldividerecords.png differ diff --git a/JonathanMyPortfolio/dragme.png b/JonathanMyPortfolio/dragme.png new file mode 100644 index 0000000..f2e77fe Binary files /dev/null and b/JonathanMyPortfolio/dragme.png differ diff --git a/JonathanMyPortfolio/entrepreneurship.png b/JonathanMyPortfolio/entrepreneurship.png new file mode 100644 index 0000000..79bb8aa Binary files /dev/null and b/JonathanMyPortfolio/entrepreneurship.png differ diff --git a/JonathanMyPortfolio/files/jonathan_peters_cv.pdf b/JonathanMyPortfolio/files/jonathan_peters_cv.pdf new file mode 100644 index 0000000..badbc55 --- /dev/null +++ b/JonathanMyPortfolio/files/jonathan_peters_cv.pdf @@ -0,0 +1,38 @@ +%PDF-1.7 +1 0 obj +<> +endobj +2 0 obj +<> +endobj +3 0 obj +<>/Contents 4 0 R/Parent 2 0 R>> +endobj +4 0 obj +<> +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 +<> +startxref +380 +%%EOF \ No newline at end of file diff --git a/JonathanMyPortfolio/generated-icon.png b/JonathanMyPortfolio/generated-icon.png new file mode 100644 index 0000000..d796176 Binary files /dev/null and b/JonathanMyPortfolio/generated-icon.png differ diff --git a/JonathanMyPortfolio/githubcalculator.png b/JonathanMyPortfolio/githubcalculator.png new file mode 100644 index 0000000..9c267b2 Binary files /dev/null and b/JonathanMyPortfolio/githubcalculator.png differ diff --git a/JonathanMyPortfolio/githubprofilesearch.png b/JonathanMyPortfolio/githubprofilesearch.png new file mode 100644 index 0000000..0e3a9a9 Binary files /dev/null and b/JonathanMyPortfolio/githubprofilesearch.png differ diff --git a/JonathanMyPortfolio/gkrooninvestmentcalculator.png b/JonathanMyPortfolio/gkrooninvestmentcalculator.png new file mode 100644 index 0000000..00a1d35 Binary files /dev/null and b/JonathanMyPortfolio/gkrooninvestmentcalculator.png differ diff --git a/JonathanMyPortfolio/gkroonpasswordgenerator.png b/JonathanMyPortfolio/gkroonpasswordgenerator.png new file mode 100644 index 0000000..342df1a Binary files /dev/null and b/JonathanMyPortfolio/gkroonpasswordgenerator.png differ diff --git a/JonathanMyPortfolio/gkroonwebsite.png b/JonathanMyPortfolio/gkroonwebsite.png new file mode 100644 index 0000000..772adf1 Binary files /dev/null and b/JonathanMyPortfolio/gkroonwebsite.png differ diff --git a/JonathanMyPortfolio/images/projects/project1.svg b/JonathanMyPortfolio/images/projects/project1.svg new file mode 100644 index 0000000..51caad3 --- /dev/null +++ b/JonathanMyPortfolio/images/projects/project1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + E-Commerce + Platform + \ No newline at end of file diff --git a/JonathanMyPortfolio/images/projects/project2.svg b/JonathanMyPortfolio/images/projects/project2.svg new file mode 100644 index 0000000..cd961a7 --- /dev/null +++ b/JonathanMyPortfolio/images/projects/project2.svg @@ -0,0 +1,9 @@ + + + + + + + + Weather Dashboard + \ No newline at end of file diff --git a/JonathanMyPortfolio/index.html b/JonathanMyPortfolio/index.html new file mode 100644 index 0000000..b1274e8 --- /dev/null +++ b/JonathanMyPortfolio/index.html @@ -0,0 +1,183 @@ + + + + + + Jonathan Peters | Front-End Developer + + + + + + + +
+ +
+
+
+
+ + +
+
+
+ + +
+
+
+

Creating Beautiful & Functional Web Experiences

+

I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.

+ +
+
+
+ + +
+
+
+

My Projects

+

Explore some of my recent work

+
+
+ +
+
+
+ + +
+
+
+

My Skills

+

Technical proficiency and expertise

+
+
+ +
+
+
+ + +
+
+
+

Languages & Tools

+

Technologies I work with

+
+
+ +
+
+
+ + +
+
+
+

About Me

+

Get to know me better

+
+
+
+

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.

+

I have 3+ years experience & I have completed the Responsive Web Design & the Data Visualizations course on freeCodeCamp, as well as Front End Development courses on Bitdegree.

+

I am currently pursuing courses in Backend Development: Node JS, Express, Python & React.

+

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.

+

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.

+
+
+
+
+ + +
+
+
+

Get In Touch

+

Let's work together on your next project

+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + jonathanpeters051@gmail.com +
+
+ + +27661199255 +
+
+ + George, Western Cape. South Africa +
+
+
+
+
+ + + +
+ + +
+ + CV downloaded successfully! +
+ + + + + + + + \ No newline at end of file diff --git a/JonathanMyPortfolio/js/script.js b/JonathanMyPortfolio/js/script.js new file mode 100644 index 0000000..e460c0f --- /dev/null +++ b/JonathanMyPortfolio/js/script.js @@ -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 ? + `${project.title}` : + ``; + + projectsHTML += ` +
+
+
+ ${projectImage} +

${project.title}

+
+
+

${project.title}

+

+ ${project.description} +

+ View Project +
+
+
+ `; + }); + + projectsContainer.innerHTML = projectsHTML; + } + + function renderSkills() { + const skillsContainer = document.getElementById('skills-container'); + if (!skillsContainer) return; + + let skillsHTML = ''; + + skills.forEach(skill => { + skillsHTML += ` +
+
+
+ + ${skill.name} +
+
${skill.percentage}%
+
+
+
+
+
+ `; + }); + + skillsContainer.innerHTML = skillsHTML; + } + + function renderTools() { + const toolsContainer = document.getElementById('tools-container'); + if (!toolsContainer) return; + + let toolsHTML = ''; + + tools.forEach(tool => { + toolsHTML += ` +
+ + ${tool.name} +
+ `; + }); + + toolsContainer.innerHTML = toolsHTML; + } + + function renderSocialLinks() { + const socialLinksContainer = document.getElementById('social-links-container'); + if (!socialLinksContainer) return; + + let socialLinksHTML = ''; + + socialLinks.forEach(social => { + socialLinksHTML += ` + + + + `; + }); + + 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(); +}); \ No newline at end of file diff --git a/JonathanMyPortfolio/main.py b/JonathanMyPortfolio/main.py new file mode 100644 index 0000000..4a8e2a5 --- /dev/null +++ b/JonathanMyPortfolio/main.py @@ -0,0 +1,13 @@ +from flask import Flask, send_from_directory + +app = Flask(__name__, static_folder='.') + +@app.route('/', defaults={'path': ''}) +@app.route('/') +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) diff --git a/JonathanMyPortfolio/meme.png b/JonathanMyPortfolio/meme.png new file mode 100644 index 0000000..cb241fc Binary files /dev/null and b/JonathanMyPortfolio/meme.png differ diff --git a/JonathanMyPortfolio/memegenerator.png b/JonathanMyPortfolio/memegenerator.png new file mode 100644 index 0000000..754a511 Binary files /dev/null and b/JonathanMyPortfolio/memegenerator.png differ diff --git a/JonathanMyPortfolio/musicplayer.png b/JonathanMyPortfolio/musicplayer.png new file mode 100644 index 0000000..21e8648 Binary files /dev/null and b/JonathanMyPortfolio/musicplayer.png differ diff --git a/JonathanMyPortfolio/pyproject.toml b/JonathanMyPortfolio/pyproject.toml new file mode 100644 index 0000000..2c2459a --- /dev/null +++ b/JonathanMyPortfolio/pyproject.toml @@ -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", +] diff --git a/JonathanMyPortfolio/rectangularrun.png b/JonathanMyPortfolio/rectangularrun.png new file mode 100644 index 0000000..a0f16b5 Binary files /dev/null and b/JonathanMyPortfolio/rectangularrun.png differ diff --git a/JonathanMyPortfolio/replit.nix b/JonathanMyPortfolio/replit.nix new file mode 100644 index 0000000..7500af1 --- /dev/null +++ b/JonathanMyPortfolio/replit.nix @@ -0,0 +1,6 @@ +{pkgs}: { + deps = [ + pkgs.postgresql + pkgs.openssl + ]; +} diff --git a/JonathanMyPortfolio/replitcapabilitiesguide.png b/JonathanMyPortfolio/replitcapabilitiesguide.png new file mode 100644 index 0000000..f9db547 Binary files /dev/null and b/JonathanMyPortfolio/replitcapabilitiesguide.png differ diff --git a/JonathanMyPortfolio/sampleartistepk.png b/JonathanMyPortfolio/sampleartistepk.png new file mode 100644 index 0000000..155cb11 Binary files /dev/null and b/JonathanMyPortfolio/sampleartistepk.png differ diff --git a/JonathanMyPortfolio/serve.sh b/JonathanMyPortfolio/serve.sh new file mode 100644 index 0000000..3a0304d --- /dev/null +++ b/JonathanMyPortfolio/serve.sh @@ -0,0 +1,2 @@ +#!/bin/bash +python -m http.server 5000 \ No newline at end of file diff --git a/JonathanMyPortfolio/serve_static.py b/JonathanMyPortfolio/serve_static.py new file mode 100644 index 0000000..57c334a --- /dev/null +++ b/JonathanMyPortfolio/serve_static.py @@ -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() \ No newline at end of file diff --git a/JonathanMyPortfolio/static/css/normalize.css b/JonathanMyPortfolio/static/css/normalize.css new file mode 100644 index 0000000..192eb9c --- /dev/null +++ b/JonathanMyPortfolio/static/css/normalize.css @@ -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; +} diff --git a/JonathanMyPortfolio/static/css/style.css b/JonathanMyPortfolio/static/css/style.css new file mode 100644 index 0000000..40c3eba --- /dev/null +++ b/JonathanMyPortfolio/static/css/style.css @@ -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); + } +} diff --git a/JonathanMyPortfolio/static/js/script.js b/JonathanMyPortfolio/static/js/script.js new file mode 100644 index 0000000..f49d9c1 --- /dev/null +++ b/JonathanMyPortfolio/static/js/script.js @@ -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(); +}); diff --git a/JonathanMyPortfolio/templates/index.html b/JonathanMyPortfolio/templates/index.html new file mode 100644 index 0000000..97e1771 --- /dev/null +++ b/JonathanMyPortfolio/templates/index.html @@ -0,0 +1,199 @@ + + + + + + Jonathan Peters | Front-End Developer + + + + + + +
+ +
+
+
+ + +
+
+
+ + +
+
+
+

Creating Beautiful & Functional Web Experiences

+

I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.

+ +
+
+
+ + +
+
+
+

My Projects

+

Explore some of my recent work

+
+
+ {% for project in projects %} +
+
+
+ +

{{ project.title }}

+
+
+

{{ project.title }}

+

{{ project.description }}

+ View Project +
+
+
+ {% endfor %} +
+
+
+ + +
+
+
+

My Skills

+

Technical proficiency and expertise

+
+
+ {% for skill in skills %} +
+
+
+ + {{ skill.name }} +
+
{{ skill.percentage }}%
+
+
+
+
+
+ {% endfor %} +
+
+
+ + +
+
+
+

Languages & Tools

+

Technologies I work with

+
+
+ {% for tool in tools %} +
+ + {{ tool.name }} +
+ {% endfor %} +
+
+
+ + +
+
+
+

About Me

+

Get to know me better

+
+
+
+

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.

+

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.

+

I have 3+ years experience & i have completed the Responsive Web Design & the Data Visualizations course on freeCodeCamp

+

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.

+
+
+
+
+ + +
+
+
+

Get In Touch

+

Let's work together on your next project

+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+ + jonathanpeters051@gmail.com +
+
+ + +27661199255 +
+
+ + George, Western Cape. South Africa +
+
+
+
+
+ + +
+
+ + +
+
+
+ + + + diff --git a/JonathanMyPortfolio/uv.lock b/JonathanMyPortfolio/uv.lock new file mode 100644 index 0000000..1da9df0 --- /dev/null +++ b/JonathanMyPortfolio/uv.lock @@ -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 }, +] diff --git a/JonathanPetersCV.pdf b/JonathanPetersCV.pdf new file mode 100644 index 0000000..34f9c46 Binary files /dev/null and b/JonathanPetersCV.pdf differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..00858f6 --- /dev/null +++ b/app.py @@ -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) diff --git a/calculator.png b/calculator.png new file mode 100644 index 0000000..ee37a12 Binary files /dev/null and b/calculator.png differ diff --git a/cryptotracker.png b/cryptotracker.png new file mode 100644 index 0000000..909509f Binary files /dev/null and b/cryptotracker.png differ diff --git a/css/normalize.css b/css/normalize.css new file mode 100644 index 0000000..b0c1902 --- /dev/null +++ b/css/normalize.css @@ -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; +} \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..bd09341 --- /dev/null +++ b/css/style.css @@ -0,0 +1,1423 @@ +/* Base Styles and Variables */ +:root { + --primary-color: #6c5ce7; + --primary-color-rgb: 108, 92, 231; + --primary-light: #a29bfe; + --primary-light-rgb: 162, 155, 254; + --secondary-color: #00cec9; + --secondary-color-rgb: 0, 206, 201; + --bg-dark: #121212; + --bg-dark-rgb: 18, 18, 18; + --bg-light: #1e1e1e; + --bg-light-rgb: 30, 30, 30; + --bg-lighter: #2a2a2a; + --bg-lighter-rgb: 42, 42, 42; + --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); + position: relative; + overflow: hidden; + z-index: 1; +} + +.primary-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + rgba(255, 255, 255, 0.1), + rgba(255, 255, 255, 0.2), + rgba(255, 255, 255, 0.1) + ); + transition: left 0.8s ease; + z-index: -1; +} + +.primary-btn:hover::before { + left: 100%; +} + +.secondary-btn { + background: transparent; + border: 2px solid var(--primary-color); + color: var(--text-color); + position: relative; + overflow: hidden; + z-index: 1; +} + +.secondary-btn::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0%; + height: 0%; + background-color: var(--primary-color); + border-radius: 50%; + transform: translate(-50%, -50%); + transition: width 0.4s ease-out, height 0.4s ease-out; + z-index: -1; + opacity: 0; +} + +.primary-btn:hover { + box-shadow: 0 6px 20px rgba(108, 92, 231, 0.5); + transform: translateY(-3px) scale(1.02); + color: white; +} + +.secondary-btn:hover { + color: white; + transform: translateY(-3px) scale(1.02); + box-shadow: 0 6px 20px rgba(108, 92, 231, 0.3); + border-color: transparent; +} + +.secondary-btn:hover::before { + width: 300%; + height: 300%; + opacity: 1; +} + +.primary-btn:active, +.secondary-btn:active { + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(108, 92, 231, 0.3); +} + +.download-cv-btn { + background: linear-gradient(45deg, var(--secondary-color), #38b2ac); + color: white; + box-shadow: 0 4px 15px rgba(0, 206, 201, 0.3); + margin-top: 1rem; + position: relative; + overflow: hidden; +} + +.download-cv-btn::after { + content: "\f019"; /* FontAwesome download icon */ + font-family: "Font Awesome 5 Free"; + font-weight: 900; + position: absolute; + font-size: 1.2rem; + top: 50%; + right: 20px; + transform: translate(20px, -50%); + opacity: 0; + transition: all 0.3s ease; +} + +.download-cv-btn:hover { + box-shadow: 0 6px 20px rgba(0, 206, 201, 0.5); + transform: translateY(-3px); + color: white; + padding-right: 45px; +} + +.download-cv-btn:hover::after { + transform: translate(0, -50%); + opacity: 1; +} + +.download-cv-btn:active { + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(0, 206, 201, 0.3); +} + +/* Success Notification */ +.notification { + position: fixed; + bottom: 20px; + right: 20px; + background-color: var(--primary-color); + color: white; + padding: 1rem 2rem; + border-radius: 5px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + z-index: 1000; + transform: translateY(150%); + transition: transform 0.4s ease; +} + +.notification.show { + transform: translateY(0); +} + +.notification i { + margin-right: 10px; +} + +.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); + overflow: hidden; +} + +/* Particles container */ +#particles-js { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + z-index: 10; +} + +.logo { + position: relative; + cursor: pointer; + padding: 5px 10px; + border-radius: 5px; + transition: all var(--transition-speed) cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.logo::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, + rgba(var(--primary-color-rgb), 0.05), + rgba(var(--secondary-color-rgb), 0.05) + ); + border-radius: 5px; + opacity: 0; + transition: opacity var(--transition-speed) ease; + z-index: -1; +} + +.logo:hover { + transform: scale(1.03); +} + +.logo:hover::before { + opacity: 1; +} + +.logo h1 { + font-size: 1.8rem; + margin-bottom: 5px; + font-weight: 700; + background: linear-gradient( + 90deg, + var(--text-color) 0%, + var(--text-color) 50%, + var(--primary-light) 50%, + var(--primary-light) 100% + ); + background-size: 200% 100%; + background-position: 100% 0; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + transition: background-position var(--transition-speed) ease; +} + +.logo:hover h1 { + background-position: 0 0; +} + +.subtitle { + font-size: 1rem; + color: var(--primary-light); + margin: 0; + transition: transform var(--transition-speed) cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.logo:hover .subtitle { + transform: translateY(2px); +} + +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: 8px 12px; + margin-bottom: 5px; + border-radius: 5px; + transition: all var(--transition-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275); + background-color: transparent; + overflow: hidden; +} + +nav ul li a::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(var(--primary-color-rgb), 0.1), + transparent + ); + transition: left 0.5s ease; + z-index: -1; +} + +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) cubic-bezier(0.68, -0.55, 0.27, 1.55); +} + +nav ul li a:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + background-color: rgba(var(--primary-color-rgb), 0.05); + color: var(--primary-light); +} + +nav ul li a:hover::before { + left: 100%; +} + +nav ul li a:hover::after { + width: 100%; +} + +nav ul li a:active { + transform: translateY(0); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +/* 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; + transition: all 0.3s ease; + position: relative; + z-index: 1; +} + +.flip-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 10px; + background: var(--primary-color); + opacity: 0; + z-index: -1; + transition: opacity 0.3s ease; + box-shadow: 0 0 15px rgba(108, 92, 231, 0.5); +} + +.flip-card:hover { + transform: translateY(-10px); +} + +.flip-card:hover::before { + opacity: 0.1; +} + +.flip-card-inner { + position: relative; + width: 100%; + height: 100%; + transition: transform 0.8s, box-shadow 0.3s ease; + transform-style: preserve-3d; + box-shadow: var(--card-shadow); + border-radius: 10px; +} + +.flip-card:hover .flip-card-inner { + transform: rotateY(180deg); + box-shadow: var(--glow-effect); +} + +.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; + transition: all 0.5s ease; +} + +.flip-card-front { + background-color: var(--bg-lighter); + border: 1px solid var(--border-color); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1); +} + +.flip-card:hover .flip-card-front { + background-color: rgba(var(--bg-lighter-rgb), 0.95); +} + +.flip-card-front i { + font-size: 4rem; + margin-bottom: 1rem; + color: var(--primary-color); + transition: transform 0.5s ease, color 0.3s ease; +} + +.flip-card:hover .flip-card-front i { + transform: scale(1.1); + color: var(--primary-light); +} + +.flip-card-front h3 { + transition: transform 0.3s ease, color 0.3s ease; + position: relative; +} + +.flip-card:hover .flip-card-front h3 { + color: var(--primary-light); +} + +.flip-card-front h3::after { + content: ''; + position: absolute; + bottom: -8px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 2px; + background: var(--primary-light); + transition: width 0.3s ease; +} + +.flip-card:hover .flip-card-front h3::after { + width: 50%; +} + +.flip-card-front .project-image { + width: 80%; + height: auto; + max-height: 120px; + margin-bottom: 1rem; + border-radius: 5px; + object-fit: contain; + transition: transform 0.5s ease, filter 0.3s ease; +} + +.flip-card:hover .flip-card-front .project-image { + transform: scale(1.05); + filter: brightness(1.1); +} + +.flip-card-back { + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + color: white; + transform: rotateY(180deg); + box-shadow: inset 0 0 20px rgba(255, 255, 255, 0.2); + overflow: hidden; + position: relative; +} + +.flip-card-back::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + transparent, + rgba(255, 255, 255, 0.1), + transparent + ); + transform: rotate(45deg); + transition: transform 1s ease; + opacity: 0; +} + +.flip-card:hover .flip-card-back::before { + transform: rotate(45deg) translate(0, 100%); + opacity: 1; +} + +.flip-card-back h3 { + margin-bottom: 1rem; + position: relative; + display: inline-block; +} + +.flip-card-back p { + margin-bottom: 1.5rem; + font-size: 0.9rem; + max-width: 90%; + transition: transform 0.3s ease; +} + +.flip-card:hover .flip-card-back p { + transform: scale(1.02); +} + +.view-project-btn { + background-color: rgba(255, 255, 255, 0.2); + color: white; + padding: 8px 20px; + border-radius: 20px; + transition: all var(--transition-speed) ease; + position: relative; + overflow: hidden; + z-index: 1; +} + +.view-project-btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 0; + height: 100%; + background-color: white; + transition: width 0.3s ease; + z-index: -1; + border-radius: 20px; +} + +.view-project-btn:hover::before { + width: 100%; +} + +.view-project-btn:hover { + color: var(--primary-color); + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); +} + +/* 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); + position: relative; + overflow: hidden; + z-index: 1; +} + +.tool-item::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, + rgba(var(--primary-color-rgb), 0.1), + rgba(var(--secondary-color-rgb), 0.1) + ); + opacity: 0; + z-index: -1; + transition: opacity 0.3s ease; +} + +.tool-item::after { + content: ''; + position: absolute; + bottom: -5px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 3px; + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +.tool-item i { + font-size: 3rem; + margin-bottom: 1rem; + color: var(--primary-color); + transition: transform 0.3s ease, color 0.3s ease; +} + +.tool-item span { + transition: color 0.3s ease; +} + +.tool-item:hover { + transform: translateY(-10px); + box-shadow: var(--glow-effect); + border-color: rgba(var(--primary-color-rgb), 0.3); +} + +.tool-item:hover::before { + opacity: 1; +} + +.tool-item:hover::after { + width: 80%; +} + +.tool-item:hover i { + transform: scale(1.1); + color: var(--primary-light); +} + +.tool-item:hover span { + color: var(--primary-light); +} + +/* 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; +} + +.about-text a { + color: var(--primary-light); + font-weight: 500; + transition: all var(--transition-speed) ease; + position: relative; + padding-bottom: 2px; +} + +.about-text a::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 1px; + background: var(--primary-light); + transition: width var(--transition-speed) ease; +} + +.about-text a:hover { + color: var(--primary-color); +} + +.about-text a:hover::after { + width: 100%; +} + +/* 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); + transition: all var(--transition-speed) ease; + position: relative; + z-index: 1; + overflow: hidden; +} + +.contact-form::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 5px; + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + transform: scaleX(0); + transform-origin: left; + transition: transform 0.5s ease; + z-index: 2; +} + +.contact-form:focus-within { + box-shadow: 0 10px 25px rgba(var(--primary-color-rgb), 0.15); + transform: translateY(-5px); +} + +.contact-form:focus-within::before { + transform: scaleX(1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; +} + +.form-group { + position: relative; +} + +.form-group label { + position: relative; + padding-left: 5px; + transition: all var(--transition-speed) ease; +} + +.form-group label::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 0; + background: var(--primary-color); + transition: height 0.3s ease; +} + +.form-group:focus-within label { + color: var(--primary-light); + padding-left: 10px; +} + +.form-group:focus-within label::before { + height: 80%; +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: 12px 15px; + background-color: var(--bg-dark); + border: 1px solid var(--border-color); + border-radius: 5px; + color: var(--text-color); + transition: all var(--transition-speed) cubic-bezier(0.165, 0.84, 0.44, 1); + position: relative; + z-index: 1; +} + +.form-group::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + width: 0; + height: 2px; + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease, left 0.3s ease; + z-index: 2; +} + +.form-group:focus-within::after { + width: 100%; + left: 0; +} + +.form-group input:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 5px 15px rgba(var(--primary-color-rgb), 0.2); + transform: translateY(-2px); + padding-left: 20px; +} + +.form-group input:focus::placeholder, +.form-group textarea:focus::placeholder { + opacity: 0.7; + transform: translateX(5px); +} + +.form-group input::placeholder, +.form-group textarea::placeholder { + color: rgba(var(--text-muted), 0.7); + transition: all var(--transition-speed) ease; +} + +.submit-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + overflow: hidden; + position: relative; + transition: all var(--transition-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +.submit-btn span { + transition: transform var(--transition-speed) ease; +} + +.submit-btn i { + font-size: 0; + transform: translateX(20px); + opacity: 0; + transition: all var(--transition-speed) cubic-bezier(0.68, -0.55, 0.27, 1.55); +} + +.submit-btn:hover span { + transform: translateX(-5px); +} + +.submit-btn:hover i { + font-size: 1rem; + transform: translateX(0); + opacity: 1; +} + +.submit-btn:active i { + transform: translateX(5px); +} + +.submit-btn:disabled { + cursor: not-allowed; + opacity: 0.8; + transform: none; +} + +.email-status { + margin-top: 1rem; + padding: 10px; + border-radius: 5px; + text-align: center; + font-weight: 500; + display: none; + animation: fadeIn 0.5s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.email-status.success { + display: block; + background-color: rgba(39, 174, 96, 0.2); + color: #27ae60; + border: 1px solid #27ae60; +} + +.email-status.error { + display: block; + background-color: rgba(231, 76, 60, 0.2); + color: #e74c3c; + border: 1px solid #e74c3c; +} + +.contact-info { + display: flex; + flex-direction: column; + justify-content: center; +} + +.contact-item { + display: flex; + align-items: center; + margin-bottom: 2rem; +} + +.contact-item { + position: relative; + transition: all var(--transition-speed) ease; +} + +.contact-item:hover { + transform: translateX(5px); +} + +.contact-item i { + font-size: 2rem; + margin-right: 1rem; + color: var(--primary-color); + position: relative; + z-index: 1; + transition: all var(--transition-speed) ease; +} + +.contact-item:hover i { + color: var(--primary-light); + animation: pulse-icon 1s infinite; +} + +.contact-item::before { + content: ''; + position: absolute; + top: 50%; + left: 1rem; + transform: translate(-50%, -50%); + width: 3rem; + height: 3rem; + background-color: rgba(var(--primary-color-rgb), 0.1); + border-radius: 50%; + opacity: 0; + transition: opacity 0.3s ease; + z-index: 0; +} + +.contact-item:hover::before { + opacity: 1; +} + +@keyframes pulse-icon { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } + 100% { + transform: scale(1); + } +} + +/* Footer Section */ +footer { + padding: 3rem 0; + background-color: var(--bg-dark); + border-top: 1px solid var(--border-color); + text-align: center; + position: relative; + overflow: hidden; +} + +#footer-particles-js { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; +} + +footer .container { + position: relative; + z-index: 1; +} + +.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) cubic-bezier(0.68, -0.55, 0.27, 1.55); + box-shadow: var(--card-shadow); + position: relative; + overflow: hidden; +} + +.social-links a::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + z-index: 0; + opacity: 0; + transform: scale(0); + transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease; +} + +.social-links a i { + font-size: 1.5rem; + color: var(--text-color); + transition: all var(--transition-speed) ease; + position: relative; + z-index: 1; +} + +.social-links a:hover { + transform: translateY(-5px) rotate(5deg); + box-shadow: var(--glow-effect); +} + +.social-links a:active { + transform: translateY(-2px) rotate(0deg); +} + +.social-links a:hover::before { + opacity: 1; + transform: scale(1); +} + +.social-links a:hover i { + color: white; + transform: scale(1.2); + animation: social-pulse 1.5s infinite alternate; +} + +@keyframes social-pulse { + 0% { + transform: scale(1); + } + 100% { + transform: scale(1.2); + } +} + +.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); + } +} + +/* Navigation animations */ +nav ul li a.fadeIn { + animation: fadeIn 0.5s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +nav ul li a.bounce { + animation: bounce 0.6s ease; +} + +@keyframes bounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateY(0); + } + 40% { + transform: translateY(-10px); + } + 60% { + transform: translateY(-5px); + } +} + +/* Header state animations */ +header.scrolling { + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); + transition: box-shadow 0.3s ease; +} + +header.scrolling .logo { + transform: scale(0.98); +} + +/* Typing animation for project descriptions */ +.typing-text-container { + text-align: center; + min-height: 3rem; + margin-bottom: 1rem; + display: flex; + justify-content: center; +} + +.typing-text { + display: inline-block; + position: relative; + overflow: hidden; + border-right: 2px solid var(--primary-color); + white-space: nowrap; + margin: 0 auto; + animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite; +} + +@keyframes typing { + from { width: 0 } + to { width: 100% } +} + +@keyframes blink-caret { + from, to { border-color: transparent } + 50% { border-color: var(--primary-color) } +} + +/* 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); + } +} \ No newline at end of file diff --git a/digitaldividerecords.png b/digitaldividerecords.png new file mode 100644 index 0000000..9a9e707 Binary files /dev/null and b/digitaldividerecords.png differ diff --git a/dragme.png b/dragme.png new file mode 100644 index 0000000..f2e77fe Binary files /dev/null and b/dragme.png differ diff --git a/entrepreneurship.png b/entrepreneurship.png new file mode 100644 index 0000000..79bb8aa Binary files /dev/null and b/entrepreneurship.png differ diff --git a/files/jonathan_peters_cv.pdf b/files/jonathan_peters_cv.pdf new file mode 100644 index 0000000..badbc55 --- /dev/null +++ b/files/jonathan_peters_cv.pdf @@ -0,0 +1,38 @@ +%PDF-1.7 +1 0 obj +<> +endobj +2 0 obj +<> +endobj +3 0 obj +<>/Contents 4 0 R/Parent 2 0 R>> +endobj +4 0 obj +<> +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 +<> +startxref +380 +%%EOF \ No newline at end of file diff --git a/generated-icon.png b/generated-icon.png new file mode 100644 index 0000000..d796176 Binary files /dev/null and b/generated-icon.png differ diff --git a/githubcalculator.png b/githubcalculator.png new file mode 100644 index 0000000..9c267b2 Binary files /dev/null and b/githubcalculator.png differ diff --git a/githubprofilesearch.png b/githubprofilesearch.png new file mode 100644 index 0000000..0e3a9a9 Binary files /dev/null and b/githubprofilesearch.png differ diff --git a/gkrooninvestmentcalculator.png b/gkrooninvestmentcalculator.png new file mode 100644 index 0000000..00a1d35 Binary files /dev/null and b/gkrooninvestmentcalculator.png differ diff --git a/gkroonpasswordgenerator.png b/gkroonpasswordgenerator.png new file mode 100644 index 0000000..342df1a Binary files /dev/null and b/gkroonpasswordgenerator.png differ diff --git a/gkroonwebsite.png b/gkroonwebsite.png new file mode 100644 index 0000000..772adf1 Binary files /dev/null and b/gkroonwebsite.png differ diff --git a/images/projects/project1.svg b/images/projects/project1.svg new file mode 100644 index 0000000..51caad3 --- /dev/null +++ b/images/projects/project1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + E-Commerce + Platform + \ No newline at end of file diff --git a/images/projects/project2.svg b/images/projects/project2.svg new file mode 100644 index 0000000..cd961a7 --- /dev/null +++ b/images/projects/project2.svg @@ -0,0 +1,9 @@ + + + + + + + + Weather Dashboard + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..b1274e8 --- /dev/null +++ b/index.html @@ -0,0 +1,183 @@ + + + + + + Jonathan Peters | Front-End Developer + + + + + + + +
+ +
+
+
+
+ + +
+
+
+ + +
+
+
+

Creating Beautiful & Functional Web Experiences

+

I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.

+ +
+
+
+ + +
+
+
+

My Projects

+

Explore some of my recent work

+
+
+ +
+
+
+ + +
+
+
+

My Skills

+

Technical proficiency and expertise

+
+
+ +
+
+
+ + +
+
+
+

Languages & Tools

+

Technologies I work with

+
+
+ +
+
+
+ + +
+
+
+

About Me

+

Get to know me better

+
+
+
+

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.

+

I have 3+ years experience & I have completed the Responsive Web Design & the Data Visualizations course on freeCodeCamp, as well as Front End Development courses on Bitdegree.

+

I am currently pursuing courses in Backend Development: Node JS, Express, Python & React.

+

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.

+

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.

+
+
+
+
+ + +
+
+
+

Get In Touch

+

Let's work together on your next project

+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + jonathanpeters051@gmail.com +
+
+ + +27661199255 +
+
+ + George, Western Cape. South Africa +
+
+
+
+
+ + +
+ +
+ + +
+
+
+ + +
+ + CV downloaded successfully! +
+ + + + + + + + \ No newline at end of file diff --git a/js/script.js b/js/script.js new file mode 100644 index 0000000..e460c0f --- /dev/null +++ b/js/script.js @@ -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 ? + `${project.title}` : + ``; + + projectsHTML += ` +
+
+
+ ${projectImage} +

${project.title}

+
+
+

${project.title}

+

+ ${project.description} +

+ View Project +
+
+
+ `; + }); + + projectsContainer.innerHTML = projectsHTML; + } + + function renderSkills() { + const skillsContainer = document.getElementById('skills-container'); + if (!skillsContainer) return; + + let skillsHTML = ''; + + skills.forEach(skill => { + skillsHTML += ` +
+
+
+ + ${skill.name} +
+
${skill.percentage}%
+
+
+
+
+
+ `; + }); + + skillsContainer.innerHTML = skillsHTML; + } + + function renderTools() { + const toolsContainer = document.getElementById('tools-container'); + if (!toolsContainer) return; + + let toolsHTML = ''; + + tools.forEach(tool => { + toolsHTML += ` +
+ + ${tool.name} +
+ `; + }); + + toolsContainer.innerHTML = toolsHTML; + } + + function renderSocialLinks() { + const socialLinksContainer = document.getElementById('social-links-container'); + if (!socialLinksContainer) return; + + let socialLinksHTML = ''; + + socialLinks.forEach(social => { + socialLinksHTML += ` + + + + `; + }); + + 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(); +}); \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..4a8e2a5 --- /dev/null +++ b/main.py @@ -0,0 +1,13 @@ +from flask import Flask, send_from_directory + +app = Flask(__name__, static_folder='.') + +@app.route('/', defaults={'path': ''}) +@app.route('/') +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) diff --git a/meme.png b/meme.png new file mode 100644 index 0000000..cb241fc Binary files /dev/null and b/meme.png differ diff --git a/memegenerator.png b/memegenerator.png new file mode 100644 index 0000000..754a511 Binary files /dev/null and b/memegenerator.png differ diff --git a/musicplayer.png b/musicplayer.png new file mode 100644 index 0000000..21e8648 Binary files /dev/null and b/musicplayer.png differ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2c2459a --- /dev/null +++ b/pyproject.toml @@ -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", +] diff --git a/rectangularrun.png b/rectangularrun.png new file mode 100644 index 0000000..a0f16b5 Binary files /dev/null and b/rectangularrun.png differ diff --git a/replit.nix b/replit.nix new file mode 100644 index 0000000..7500af1 --- /dev/null +++ b/replit.nix @@ -0,0 +1,6 @@ +{pkgs}: { + deps = [ + pkgs.postgresql + pkgs.openssl + ]; +} diff --git a/replitcapabilitiesguide.png b/replitcapabilitiesguide.png new file mode 100644 index 0000000..f9db547 Binary files /dev/null and b/replitcapabilitiesguide.png differ diff --git a/sampleartistepk.png b/sampleartistepk.png new file mode 100644 index 0000000..155cb11 Binary files /dev/null and b/sampleartistepk.png differ diff --git a/serve.sh b/serve.sh new file mode 100644 index 0000000..3a0304d --- /dev/null +++ b/serve.sh @@ -0,0 +1,2 @@ +#!/bin/bash +python -m http.server 5000 \ No newline at end of file diff --git a/serve_static.py b/serve_static.py new file mode 100644 index 0000000..57c334a --- /dev/null +++ b/serve_static.py @@ -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() \ No newline at end of file diff --git a/static/css/normalize.css b/static/css/normalize.css new file mode 100644 index 0000000..192eb9c --- /dev/null +++ b/static/css/normalize.css @@ -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; +} diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..40c3eba --- /dev/null +++ b/static/css/style.css @@ -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); + } +} diff --git a/static/js/script.js b/static/js/script.js new file mode 100644 index 0000000..f49d9c1 --- /dev/null +++ b/static/js/script.js @@ -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(); +}); diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..97e1771 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,199 @@ + + + + + + Jonathan Peters | Front-End Developer + + + + + + +
+ +
+
+
+ + +
+
+
+ + +
+
+
+

Creating Beautiful & Functional Web Experiences

+

I'm a passionate front-end developer specializing in creating responsive, user-friendly websites and applications.

+ +
+
+
+ + +
+
+
+

My Projects

+

Explore some of my recent work

+
+
+ {% for project in projects %} +
+
+
+ +

{{ project.title }}

+
+
+

{{ project.title }}

+

{{ project.description }}

+ View Project +
+
+
+ {% endfor %} +
+
+
+ + +
+
+
+

My Skills

+

Technical proficiency and expertise

+
+
+ {% for skill in skills %} +
+
+
+ + {{ skill.name }} +
+
{{ skill.percentage }}%
+
+
+
+
+
+ {% endfor %} +
+
+
+ + +
+
+
+

Languages & Tools

+

Technologies I work with

+
+
+ {% for tool in tools %} +
+ + {{ tool.name }} +
+ {% endfor %} +
+
+
+ + +
+
+
+

About Me

+

Get to know me better

+
+
+
+

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.

+

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.

+

I have 3+ years experience & i have completed the Responsive Web Design & the Data Visualizations course on freeCodeCamp

+

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.

+
+
+
+
+ + +
+
+
+

Get In Touch

+

Let's work together on your next project

+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+ + jonathanpeters051@gmail.com +
+
+ + +27661199255 +
+
+ + George, Western Cape. South Africa +
+
+
+
+
+ + +
+
+ + +
+
+
+ + + + diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..1da9df0 --- /dev/null +++ b/uv.lock @@ -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 }, +]