Compare commits

..

34 Commits

Author SHA1 Message Date
dereklseitz
cf68c88a34 feat: Enable public access to https://bloomvalleydemo.dlseitz.dev
- update cover photo for BVN demo card
2025-09-10 13:49:15 -05:00
dereklseitz
b2ceb8a33f feat: add box-shadow to all cta/submit/reset buttons 2025-09-07 18:03:56 -05:00
dereklseitz
9680a4936c feat: Add box-shadow to "View Source Code" button 2025-09-07 17:50:41 -05:00
dereklseitz
5056d5f5c7 fix: typo in /demos/ <h1> 2025-09-07 17:43:37 -05:00
dereklseitz
57e1bb8d9d feat(a11y): Improve accessibility of demo cards
- change outer <div> wrapper to <article> for improved semantic HTML
- add "aria-labelledby" attribute to explicitly link each demo card to its title
2025-09-07 17:39:42 -05:00
dereklseitz
3c22c88396 feat: Add demo card on demos page for first showcased demo
- add _data/demos.js data file
- add Nunjucks logic for dynamically injecting demo attributes into demo cards
- add style rules for demo cards
2025-09-07 17:24:16 -05:00
dereklseitz
264529c428 docs: Update Privacy Policy to include CSP info 2025-09-07 10:44:39 -05:00
dereklseitz
d1218a67b2 fix: Fix error preventing load of Privacy Policy from link in Submit Clause 2025-09-03 16:30:55 -05:00
dereklseitz
e0d613fc4e fix: Add 'src/_docs' to 11ty passthrough config 2025-09-03 16:27:42 -05:00
dereklseitz
680d96c601 feat: Add downloadable PDF for Privacy Policy 2025-09-03 16:20:39 -05:00
dereklseitz
e6d1ad1ba7 fix: update links to reflect new repo server location 2025-08-27 17:43:12 -05:00
dereklseitz
35ac27b24d fix: remove Privacy Policy checkbox
- Add statement of consent by affirmative action
2025-08-26 10:03:06 -05:00
dereklseitz
1a2acf5f5f fix: change privacy policy field validation strictly to browser validation 2025-08-26 08:53:16 -05:00
dereklseitz
111410cc7c . 2025-08-26 01:43:53 -05:00
dereklseitz
8ab7882501 , 2025-08-26 00:53:52 -05:00
dereklseitz
3bc28dc5e6 . 2025-08-26 00:45:59 -05:00
dereklseitz
599b53527e . 2025-08-26 00:12:16 -05:00
dereklseitz
828d7a34c4 . 2025-08-26 00:08:32 -05:00
dereklseitz
37d1b3af57 . 2025-08-26 00:04:26 -05:00
dereklseitz
2db0050f44 fix: correctly get checkbox data from contact form 2025-08-25 23:59:36 -05:00
dereklseitz
f2cdc9cdc2 fix: corrected privacyAccepted value assignment in contact-form.js 2025-08-25 23:43:06 -05:00
dereklseitz
5cf47eb202 feat: Add Privacy Policy acceptance to contact form and client-side validation to contact-form.js 2025-08-25 23:04:57 -05:00
dereklseitz
04da9dde0b feat: Add privacy policy page and external links 2025-08-25 20:24:14 -05:00
dereklseitz
516b893d6d chore: update README.md 2025-08-23 00:46:46 -05:00
dereklseitz
f18867acc4 chore: update README.md 2025-08-23 00:41:08 -05:00
dereklseitz
94db5fccfc chore: update README.md 2025-08-23 00:32:49 -05:00
dereklseitz
1a6bf78e49 add hcaptcha reset 2025-08-21 19:24:18 -05:00
dereklseitz
1479492753 add call for hCaptcha token 2025-08-21 19:22:10 -05:00
dereklseitz
e70d594e7f add call for hCaptcha token 2025-08-21 19:16:53 -05:00
dereklseitz
a475dde9be fix: correct app.use() 2025-08-21 18:20:16 -05:00
dereklseitz
4f3a256158 fix: correct app.use() 2025-08-21 18:16:09 -05:00
dereklseitz
f0fae1d559 fix: correct app.use() 2025-08-21 18:13:47 -05:00
dereklseitz
26b42cc4a0 fix api call 2025-08-21 08:39:32 -05:00
dereklseitz
5cdf98c389 fix: Correct hCaptcha sitekey 2025-08-21 08:24:07 -05:00
21 changed files with 2237 additions and 86 deletions

View File

@@ -1,8 +1,8 @@
module.exports = function (eleventyConfig) {
// ✅ Pass through static assets to the root of output
eleventyConfig.addPassthroughCopy({ "src/images": "images" });
eleventyConfig.addPassthroughCopy({ "src/styles": "styles" });
eleventyConfig.addPassthroughCopy({ "src/scripts": "scripts" });
eleventyConfig.addPassthroughCopy({ "src/_docs": "_docs" })
return {
dir: {

5
.gitignore vendored
View File

@@ -12,4 +12,7 @@ node_modules/
# NPM debug logs
npm-debug.log*
yarn-debug.log*
yarn-debug.log*
# Privacy Policy Word Doc
_privacy-policy.docx

View File

@@ -1,4 +1,8 @@
# [dlseitz.dev](https://dlseitz.dev/): A Frontend Demonstration
<a id="top">&#8203;</a>
To learn more about the backend of this two-part project, check out [**dlseitz.dev - A Backend Demonstration**](https://gitea.dlseitz.dev/dereklseitz/dlseitz.dev-backend/src/branch/refactor-modular-app/README.md)
---
### Table of Contents
@@ -41,7 +45,7 @@ To solve this, I designed and built a full-stack website from the ground up, lev
This project is a multi-phased initiative designed to serve as both a public portfolio and a long-term business asset. The website is architected like a house, with the main landing page acting as the front door—designed to be simple and approachable. The ["About"](https://dlseitz.dev/about/) page, which includes a contact form for client engagement, functions as the foyer, inviting further conversation and providing a more detailed introduction of myself. Individual projects and demos are planned as separate "rooms" (subdomains), each with its own style and purpose, demonstrating a breadth of skills. This vision is a strategic roadmap to not only showcase a robust technical foundation but also to build a professional brand based on the values of **accessibility**, **equity**, and **transparency**, and a pipeline for future client work.
[Back to Top](#dlseitz.dev-a-frontend-demonstration)
[Back to Top](#top)
---
## Design & Accessibility
@@ -65,7 +69,7 @@ The user interface is designed to be clean, professional, and intuitive. Key UI/
### Overall User Flow
The overall design of the site creates a clear and intuitive user flow, guiding potential clients through a strategic journey. The [`Home`](https://dlseitz.dev/) page acts as a point of discovery, introducing the business benefits and services in a scannable format. It then directs the user to the [`About`](https://dlseitz.dev/about/) page, which provides more detailed information and the [contact form](https://dlseitz.dev/about/#contact) to initiate a conversation. This intentional flow from general introduction to specific engagement demonstrates a thoughtful approach to user experience and client conversion.
[Back to Top](#dlseitz.dev-a-frontend-demonstration)
[Back to Top](#top)
---
## Website Structure & Functionality
@@ -84,7 +88,7 @@ The website is a static, multi-page application with several key features that e
* **Interactive Accordions:** Custom JavaScript is used to create interactive accordion components that allow for a clean, organized presentation of content. These are used to display professional values on the ["About"](https://dlseitz.dev/about/) page and to list services on the ["Home"](https://dlseitz.dev/) page.
* **Contact Form:** A detailed and functional contact form is embedded on the ["About"](https://dlseitz.dev/about/#contact) page to facilitate direct client inquiries. The form includes input fields for a user's contact information and a detailed message field, providing a clear path for client engagement.
[Back to Top](#dlseitz.dev-a-frontend-demonstration)
[Back to Top](#top)
---
## Key Technology Decisions
@@ -106,7 +110,7 @@ To ensure a reliable and predictable user experience, the website utilizes a con
### Modular CSS Architecture
The site's styles are organized into reusable CSS files, such as `base.css` and `modules.css`, to promote a highly modular architecture. This approach ensures that the visual design is not only consistent but also highly maintainable. By separating global styles from component-specific ones, you can easily update the site's design or add new sections in the future without creating code redundancy or breaking the existing layout.
[Back to Top](#dlseitz.dev-a-frontend-demonstration)
[Back to Top](#top)
---
## Issues & Lessons Learned
@@ -117,5 +121,5 @@ One of the primary challenges was bridging the gap between my formal training in
### Reflections
This project has been a valuable exercise that has given me a deeper understanding of the interplay between design principles and development realities. By approaching the project as an **MVP** (Minimum Viable Product), I was able to deliver a professional-grade product in a compressed timeline. This reinforced a key lesson: the most effective solutions are not always the most complex, but are instead those that are grounded in thoughtful planning, resourcefulness, and pragmatism. The project is a testament to the idea that a growth mindset and a commitment to continuous learning can overcome gaps in formal training. It also provided me an opportunity to practice my belief that transparency—as demonstrated in this documentation—is not a sign of weakness, but an act of integrity that invites constructive criticism and fosters continuous improvement.
[Back to Top](#dlseitz.dev-a-frontend-demonstration)
[Back to Top](#top)
---

1681
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,27 @@
{
"name": "dereklseitz.github.io",
"name": "dlseitz.dev-frontend",
"version": "1.0.0",
"description": "Portfolio website",
"description": "Frontend code for Derek L. Seitzs personal portfolio and web development services.",
"main": "script.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npx @11ty/eleventy",
"dev": "npm run clean && npm run start",
"start": "npx @11ty/eleventy --serve",
"clean": "rimraf _site",
"dev": "npm run clean && npm run start"
"build": "npx @11ty/eleventy",
"clean": "rimraf _site"
},
"keywords": ["portfolio", "11ty", "eleventy", "web development", "static site", "frontend", "Derek Seitz"],
"author": "Derek L. Seitz",
"license": "MIT",
"homepage": "https://dlseitz.dev",
"repository": {
"type": "git",
"url": "git+https://github.com/dereklseitz/dereklseitz.github.io.git"
"url": "https://gitea.dlseitz.dev/dereklseitz/dlseitz.dev-frontend.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/dereklseitz/dereklseitz.github.io/issues"
"url": "https://gitea.dlseitz.dev/dereklseitz/dlseitz.dev-frontend/issues"
},
"homepage": "https://github.com/dereklseitz/dereklseitz.github.io#readme",
"devDependencies": {
"@11ty/eleventy": "^3.0.0",
"rimraf": "^6.0.1"
}
}

55
src/_data/demos.js Normal file
View File

@@ -0,0 +1,55 @@
/**
* @fileoverview This file holds the data for demo cards served on https://dlseitz.dev/demos/
*/
const DEMOS = [
    {
        "title": "Bloom Valley Nursery",
"id": "bvn",
        "imageFile": "bloom-valley-nursery-demo.jpg",
        "altText": "A screenshot of the Bloom Valley Nursery Demo landing page",
        "description": "This demo is a modern e-commerce website (front end) designed to help a fictional family-owned business expand its reach to a broader online audience. It focuses on clean design, data-driven interactivity, and a personal, community-focused touch.",
        "demoUrl": "https://bloomvalleydemo.dlseitz.dev",
        "repoUrl": "https://gitea.dlseitz.dev/dereklseitz/BloomValleyNursery",
        "techstack": [
"11ty",
"HTML",
"CSS",
"JavaScript",
"Nunjucks",
"Node.js",
"Font Awesome",
"Zoho Calendar API"
],
"featuresShort": [
"Data-driven content",
"Interactive carousels",
"Shopping cart functionality",
"Customer feedback & newsletter subscription",
"Third-party API integration",
"Web performance optimizations",
"Accessibility features",
"Modular design"
],
"featuresLong": [
"Data-driven content for products, promotions, testimonials, and events.",
"Interactive carousels on the homepage and gallery to showcase featured items, promotions, and testimonials.",
"Shopping cart functionality with temporary session storage.",
"Customer feedback form with client-side validation and local storage.",
"Newsletter subscription with client-side validation.",
"Dynamic integration with the Zoho Calendar API to display community events.",
"Responsive design that adapts to various screen sizes.",
"Accessibility features, including ARIA labels and semantic HTML.",
"Web performance optimizations, such as lazy loading for images.",
"Modular code and file structure for improved scalability and maintenance."
],
        "category": "e-commerce",
        "problemStatement": "To help a small, family-owned and operated business with deep roots in community involvement and public service expand their reach to customers who may prefer shopping online.",
        "solutionSummary": "This front end site is built to emulate a seamless online shopping experience that captures the fictional brand's community-focused story. The design prioritizes accessibility, visual harmony, and usability, while the data-driven functionality allows for a scalable and easily updatable web application.",
"isLive": true
    }
]
module.exports = {
    DEMOS
};

Binary file not shown.

View File

@@ -12,6 +12,7 @@
<link rel="stylesheet" href="/styles/base.css">
<link rel="stylesheet" href="/styles/header-footer.css">
<link rel="stylesheet" href="{{ stylesheet }}">
{% if hCAPTCHA %}
<script src="{{ hCAPTCHA }}" async defer></script>
@@ -19,8 +20,9 @@
</head>
<body>
<!-- <a href="https://www.svgbackgrounds.com/set/free-svg-backgrounds-and-patterns/">Free SVG Backgrounds and Patterns by SVGBackgrounds.com</a> -->
{% include "header.html" %}
{% include "header.njk" %}
<main>
@@ -28,9 +30,7 @@
</main>
{% include "footer.html" %}
<!-- <a href="https://www.svgbackgrounds.com/set/free-svg-backgrounds-and-patterns/">Free SVG Backgrounds and Patterns by SVGBackgrounds.com</a> -->
{% include "footer.njk" %}
{% if pageScripts %}
{% for script in pageScripts %}

View File

@@ -1,20 +1,25 @@
<footer>
<ul class="social-links">
<li class="linkedin"><a href="https://linkedin.com/in/dereklseitz" target="_blank">Connect on LinkedIn</a></li>
<li class="gitea"><a href="https://git.dsnet.pro/dereklseitz" target="_blank">My Project Repos</a></li>
</ul>
<nav aria-label="Footer navigation">
<ul class="footer-links">
<li><a href="#top">Back to Top</a></li>
<li><a href="/">Home</a></li>
<li><a href="/about/">About</a></li>
<li><a href="/demos/">Demos</a></li>
<li><a href="/about/#contact">Contact</a></li>
</ul>
</nav>
<p class="copyright">&copy; 2025, Derek L. Seitz, All rights reserved.</p>
<footer>
<ul class="social-links">
<li class="linkedin"><a href="https://linkedin.com/in/dereklseitz" target="_blank" rel="noopener noreferrer"
>Connect on LinkedIn</a></li>
<li class="gitea"><a href="https://gitea.dlseitz.dev/dereklseitz" target="_blank" rel="noopener noreferrer"
>My Project Repos</a></li>
<li class="campfire"><a href="https://campfire.dlseitz.dev" target=_blank" rel="noopener noreferrer">Campfire Logs</a></li>
</ul>
<nav aria-label="Footer navigation">
<ul class="footer-links">
<li><a href="#top">Back to Top</a></li>
<li><a href="/">Home</a></li>
<li><a href="/about/">About</a></li>
<li><a href="/demos/">Demos</a></li>
<li><a href="/about/#contact">Contact</a></li>
<li><a href="/privacy-policy/" target="_blank" rel="noopener noreferrer"
>Privacy Policy</a></li>
</ul>
</nav>
<p class="copyright">&copy; 2025, Derek L. Seitz, All rights reserved.</p>
</footer>

View File

@@ -1,14 +1,17 @@
<header class="main-header">
<nav class="main-nav">
<ul class="nav-list">
<li class="nav-item nav-logo"><a href="/">Derek L. Seitz</a></li>
<li class="nav-item nav-about"><a href="/about/">About</a></li>
<li class="nav-item nav-demos"><a href="/demos/">Demos</a></li>
<li class="nav-item nav-contact"><a href="/about/#contact">Contact</a></li>
</ul>
</nav>
<header class="main-header top">
<nav class="main-nav">
<ul class="nav-list">
<li class="nav-item nav-logo"><a href="/">Derek L. Seitz</a></li>
<li class="nav-item nav-about"><a href="/about/">About</a></li>
<li class="nav-item nav-demos"><a href="/demos/">Demos</a></li>
<li class="nav-item nav-contact"><a href="/about/#contact">Contact</a></li>
<li class="nav-item nav-blog"><a href="https://campfire.dlseitz.dev" target="_blank" rel="noopener noreferrer"
>Blog</a></li>
<li class="nav-item nav-repos"><a href="https://gitea.dlseitz.dev" target="_blank" rel="noopener noreferrer"
>My Repos</a></li>
</ul>
</nav>
</header>

View File

@@ -108,16 +108,24 @@ pageScripts:
<fieldset class="message-field">
<legend>Message</legend>
<label for="message">Questions or Feedback</label>
<textarea id="message" name="message" rows="15" placeholder="What questions or feedback do you have for me?" required aria-describedby="message-error"></textarea>
<textarea id="message" name="message" rows="10" placeholder="What questions or feedback do you have for me?" required aria-describedby="message-error"></textarea>
<span class="error-message" id="message-error" aria-live="polite"></span>
</fieldset>
<div class="checks">
<div class="h-captcha" data-sitekey="b63e5b64-c6f2-4154-b5a6-77169a924022"></div>
<div id="privacy" class="consent-statement">
<p>By clicking "Send Message" below, you <strong>accept and agree to</strong> this site's <a href="../privacy-policy/" target="_blank" rel="noopener noreferrer">Privacy Policy</a>.</p>
</div>
</div>
<div class="submit-reset">
<button type="submit">Send Message</button>
<button type="reset">Reset Form</button>
</div>
<div class="h-captcha" data-sitekey="b63e5b64-c6f2-4154-b5a6-77169a924022E"></div>
<div class="honeypot-field" aria-hidden="true">
<label for="url">Website URL</label>

View File

@@ -6,4 +6,23 @@ stylesheet: /styles/demos.css
isLandingPage: false
---
<h1>Demos Coming Soon!</h1>
<h1>Solutions I've Built</h1>
{% for demo in demos.DEMOS %}
<article id="demo-{{ demo.id }}" class="{{ demo.id }}-demo-card" aria-labelledby="demo-title-{{ demo.id }}">
<div class="{{ demo.id }}-demo module">
<h2 id="demo-title-{{ demo.id }}" class="demo-title">{{ demo.title }}</h2>
<img class="demo-image" src="/images/_demo-thumbs/{{ demo.imageFile }}" alt="{{ demo.altText }}">
<p class="demo-description">{{ demo.description}}</p>
<h3>Business Problem</h3>
<p class="demo-problem">{{ demo.problemStatement }}</p>
<h3>Solution</h3>
<p class="demo-solution">{{ demo.solutionSummary }}</p>
{% if demo.isLive === false %}
<p class="coming-soon">Check back soon for full access to this demo!</p>
{% else %}
<a href="{{ demo.demoUrl }}" class="demo-link" target="_blank" rel="noopener noreferrer">Access the live demo here!</a>
{% endif %}
<a href="{{ demo.repoUrl }}" class="demo-repo" target="_blank" rel="noopener noreferrer">View Demo's Source Code</a>
</div>
</article>
{% endfor %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

View File

@@ -2,7 +2,7 @@
layout: base.njk
metaDesc: "Elevate your business with a custom, high-quality website. Derek L. Seitz provides professional web design and development to help you grow your online presence."
title: "Derek L. Seitz - Portfolio"
stylesheet: styles/index.css
stylesheet: /styles/index.css
isLandingPage: true
pageScripts:
- "/scripts/benefits.js"
@@ -10,7 +10,7 @@ pageScripts:
---
<!-- ↓ Start Intro Module ↓ -->
<div id="top"></div>
<div class="intro module">
<h1>Grow Your Online Presence with Me, <span class="accent-name">Derek L. Seitz</span></h1>
<p>A great website is often your most powerful sales tool. It's the first impression you make and the cornerstone of your online presence.</p>
@@ -61,12 +61,12 @@ pageScripts:
<div class="transition">
<p>Tackling these areas yourself can demand a huge amount of your time. This is where I come in.</p>
<p>Trusting me to focus on getting your website noticed, you can instead focus on what makes your business thrive: the people and communities you serve.</p>
<p>Trusting me to focus on getting your website noticed, you can instead focus on what makes your business thrive: <br><span class="thrive">the people and communities you serve</span>.</p>
</div>
</div>
<!-- ↓ Start Services Module ↓ -->
<divid="services-section" class="services-section module">
<div id="services-section" class="services-section module">
<div class="accordion-container">
<h2>How I Can Help</h2>
<p>Click each button below to see ways I can help!</p>

124
src/privacy-policy.njk Normal file
View File

@@ -0,0 +1,124 @@
---
layout: base.njk
metaDesc: "Your privacy is my priority. This policy explains how I securely handle your contact information, what I use it for, and your rights to access or delete your data."
title: "Derek L. Seitz - Privacy Policy"
stylesheet: "/styles/privacy.css"
isLandingPage: false
---
<div class="module">
<h1>Privacy Policy - dlseitz.dev</h1>
<strong>Last updated: September 7, 2025</strong>
<p>This <strong>Privacy Policy</strong> describes my policies and procedures on the collection and use of your information when you use my Service. I use your personal data to communicate with you regarding your inquiries.</p>
<hr>
<div id="toc" class="toc">
<h2>Table of Contents</h2>
<ul>
<li><a href="#definitions">Interpretation and Definitions</a></li>
<li><a href="#data-collection">What Information I Collect</a></li>
<li><a href="#data-use">How I Use Your Personal Data</a></li>
<li><a href="#no-tracking">No Cookies or Tracking</a></li>
<li><a href="#data-retention">How Long I Keep Your Information</a></li>
<li><a href="#data-protection">How I Protect Your Information</a></li>
<li><a href="#privacy-rights">Your Privacy Rights</a></li>
<li><a href="#policy-changes">Changes to This Privacy Policy</a></li>
<li><a href="#contact">How to Contact Me</a></li>
</ul>
</div>
<hr>
<section id="definitions">
<h2>Interpretation and Definitions</h2>
<p>For the purposes of this Privacy Policy:</p>
<p><strong>"I"</strong>, <strong>"me"</strong>, or <strong>"my"</strong> refers to <strong>Derek L. Seitz</strong>.<br>
<p><strong>"Country"</strong> refers to: <strong>Arkansas, United States</strong>.<br>
<p><strong>"Personal Data"</strong> is any information that relates to an identified or identifiable individual.<br>
<p><strong>"Service(s)"</strong> refers to the <strong>"Website"</strong> and all related services I offer, including client consultation, project discovery, and web development services.<br>
<p><strong>"Website"</strong> refers to <strong>Derek L. Seitz</strong>, accessible from <a href="https://dlseitz.dev">https://dlseitz.dev</a>.<br>
<p><strong>"You"</strong> or <strong>"Your"</strong> means the individual accessing or using the <strong>"Service"</strong>.<br>
</section>
<a class="back-to-top" href="./#top" aria-label="Back to top of page">Back to Top</a>
<hr>
<section id="data-collection">
<h2>What Information I Collect</h2>
<p>I collect information that you voluntarily provide to me when you express an interest in obtaining information about my Services or otherwise contact me. The personal information I collect includes:</p>
<ul class="data-collected">
<li>First name and last name</li>
<li>Phone number</li>
<li>Email address</li>
<li>Preferred contact method</li>
<li>Organization</li>
<li>Your message</li>
</ul>
<p>I may also collect certain technical information automatically, such as the date and time when a form is submitted, for the purpose of diagnostics and usage analysis.</p>
<p class="highlight-statement"><strong>I do not collect any information from third parties.<br>
I do not collect or process sensitive personal information.</strong></p>
</section>
<a class="back-to-top" href="./#top" aria-label="Back to top of page">Back to Top</a>
<hr>
<section id="data-use">
<h2>How I Use Your Personal Data</h2>
<p>I process your personal information for a variety of reasons, including:</p>
<ul>
<li>To respond to inquiries and provide support</li>
<li>To deliver and facilitate the services you request</li>
<li>To maintain records of communication for continuity</li>
<li>To prevent spam or abuse</li>
<li>To comply with legal obligations, if necessary</li>
</ul>
<p><strong>I do not sell, rent, or share your personal information with any third parties.</strong> I may disclose information only when required by law (such as a subpoena or legal process).</p>
</section>
<section id="no-tracking">
<h2>No Cookies or Tracking</h2>
<p>My website does not use cookies, tracking scripts, or analytics services. Your visit to my site is not monitored or tracked.</p>
</section>
<section id="data-retention">
<h2>How Long I Keep Your Information</h2>
<p>I will only keep your personal information for as long as is necessary for the purposes described in this Privacy Policy. No purpose in this notice will require me to keep your personal information for longer than 1 year, unless required for legal purposes.</p>
<p>When I no longer have a legitimate reason to process your data, I will delete it or securely store it until it can be deleted.</p>
</section>
<section id="data-protection">
<h2>How I Protect Your Information</h2>
<p>I have implemented reasonable technical and organizational security measures to protect your personal data. A <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy" target="_blank" rel="noopener noreferrer">Content Security Policy (CSP)</a> is used to ensure your submitted data is only ever sent to my server, protecting it from being redirected to any other location. Your information is transmitted from the contact form using <a href="https://developers.google.com/search/docs/crawling-indexing/https" target="_blank" rel="noopener noreferrer">HTTPS encryption</a>, which is enforced by default on this .dev domain.</p>
<p>Submitted data undergoes multiple security checks to prevent spam and abuse, including the use of <a href="https://www.hcaptcha.com/" target="_blank" rel="noopener noreferrer">hCaptcha</a> and rate limiting to verify the user is a human and minimize the risk of unauthorized access to stored data. The data is also validated and sanitized to ensure its integrity before it is stored in my database.</p>
<p>However, security is an ongoing practice, and no method of electronic transmission or storage is ever absolutely secure. While I strive to protect your data, I cannot guarantee its absolute security.</p>
</section>
<a class="back-to-top" href="./#top" aria-label="Back to top of page">Back to Top</a>
<hr>
<section id="privacy-rights">
<h2>Your Privacy Rights</h2>
<p>You have the right to:</p>
<ul>
<li>Request access to the personal data I hold about you</li>
<li>Request corrections to inaccurate information</li>
<li>Request deletion of your personal data</li>
</ul>
<p>If I decline your request for legal or other reasons, you may contact me to appeal the decision. I will respond with the reason for my decision.</p>
</section>
<section id="policy-changes">
<h2>Changes to This Privacy Policy</h2>
<p>I may update this Privacy Policy from time to time. When I do, I will update the "Last updated" date at the top of this page. If I make material changes, I will post a notice on the Website. I encourage you to review this policy periodically to stay informed.</p>
</section>
<section id="contact">
<h2>How to Contact Me</h2>
<p>If you have any questions about this Privacy Policy or wish to exercise your privacy rights, you can contact me:</p>
<ul>
<li>By email: <a href="mailto:contact@dlseitz.dev">contact@dlseitz.dev</a></li>
<li>Or via the contact form on my website: <a href="https://dlseitz.dev/about/#contact" target="_blank" rel="noopener noreferrer">https://dlseitz.dev/about/#contact</a></li>
</ul>
<p>I aim to respond to all privacy-related requests within 5 business days.</p>
</section>
<a class="back-to-top" href="./#top" aria-label="Back to top of page">Back to Top</a>
</div>
<a class="download" href="/_docs/_privacy-policy.pdf" download>Download Privacy Policy</a>

View File

@@ -52,11 +52,9 @@ form.addEventListener("submit", function(event) {
const honeypotField = document.getElementById("url").value.trim();
if (honeypotField.length > 0) {
console.warn("Honeypot field was filled. Blocking submission.");
// Fail silently to avoid alerting the bot.
return;
}
// Get values, including the hCaptcha response token
const firstName = document.getElementById("first-name").value.trim();
const lastName = document.getElementById("last-name").value.trim();
const organization = document.getElementById("organization").value.trim();
@@ -64,12 +62,9 @@ form.addEventListener("submit", function(event) {
const phone = document.getElementById("phone").value.trim();
const contactMethod = document.querySelector('input[name="contact-method"]:checked')?.value;
const message = document.getElementById("message").value.trim();
// Get the hCaptcha token from the global hcaptcha object
const hCaptchaResponse = hcaptcha.getResponse();
let hasErrors = false;
// Validation logic...
if (!firstName) {
showError("first-name", "Please enter your first name.");
hasErrors = true;
@@ -109,13 +104,13 @@ form.addEventListener("submit", function(event) {
hasErrors = true;
}
const hCaptchaResponse = hcaptcha.getResponse();
// Check for hCaptcha token
if (!hCaptchaResponse) {
showMessage("Please complete the CAPTCHA.", true);
hasErrors = true;
}
if (!hasErrors) {
// Package the form data, including the hCaptcha token
const formData = {
@@ -127,11 +122,11 @@ form.addEventListener("submit", function(event) {
contactMethod: contactMethod,
message: message,
url: honeypotField,
hCaptchaResponse: hCaptchaResponse // <-- THIS IS THE NEW PART
hCaptchaResponse: hCaptchaResponse,
};
// Send the data to the backend using fetch()
fetch('/submit-form', {
fetch('/api/submit-form', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -148,6 +143,7 @@ form.addEventListener("submit", function(event) {
console.log('Success:', data);
showMessage('Form submitted successfully!', false);
form.reset();
hcaptcha.reset();
})
.catch((error) => {
console.error('Error:', error);

View File

@@ -205,6 +205,19 @@ textarea:focus {
font-weight: normal;
}
.checks {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.privacy-checkbox > div {
display: flex;
align-items: center; /* Vertically aligns items in the center */
gap: 10px; /* Adds space between the checkbox and the text */
}
/* Button */
.submit-reset {
display: flex;
@@ -221,11 +234,13 @@ button[type="submit"], button[type="reset"] {
border-radius: 50px;
font-size: 1rem;
cursor: pointer;
box-shadow: 0 4px 8px rgba(202, 110, 11, 0.4);
transition: background-color 0.3s ease;
}
button[type="submit"]:hover, button[type="reset"]:hover {
background-color: #2e97be;
box-shadow: 0 4px 8px rgba(46, 151, 190, 0.4);
}
.honeypot-field {

View File

@@ -16,4 +16,97 @@ h1, h2 {
text-align: center;
margin-top: 200px;
font-size: 2.5rem;
margin-top: 50px;
}
.demo-card {
box-shadow: var(--shadow-small);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.demo-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-medium);
}
.demo-title {
margin-top: 0;
font-size: 2rem;
color: var(--primary-color);
margin-bottom: var(--margin-small);
}
.demo-image {
width: 70%;
height: auto;
border-radius: 8px;
box-shadow: var(--shadow-small);
margin-bottom: var(--margin-medium);
}
.demo-description,
.demo-problem,
.demo-solution,
.coming-soon {
line-height: 1.6;
margin-bottom: var(--margin-small);
font-size: 1.25rem;
text-align: center;
}
.demo-problem,
.demo-solution {
font-weight: 500;
}
h3 {
font-weight: bold;
color: var(--primary-color);
font-size: 2rem;
}
.coming-soon {
font-weight: bold;
color: var(--secondary-color);
text-align: center;
font-style: italic;
margin-top: 10px;
}
.demo-link,
.demo-repo {
display: inline-block;
padding: 8px 25px;
margin: 20px 10px;
background-color: var(--secondary-color);
color: var(--light-bg-color);
text-decoration: none;
font-weight: 600;
border-radius: 50px;
box-shadow: 0 4px 8px rgba(202, 110, 11, 0.4);
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.demo-link:hover,
.demo-repo:hover {
background-color: var(--primary-color);
box-shadow: 0 4px 8px rgba(46, 151, 190, 0.4);
}
/* Responsive tweaks */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
.demo-card {
padding: var(--padding-medium);
}
.demo-link,
.demo-repo {
display: block;
margin: 10px auto;
width: fit-content;
}
}

View File

@@ -29,7 +29,7 @@
text-decoration: none;
font-weight: 500;
transition: all 0.3s ease;
padding: 8px 15px;
padding: 4px 15px;
border-radius: 5px;
position: relative;
overflow: hidden;
@@ -56,9 +56,9 @@
}
.nav-logo a:hover {
color: var(--secondary-color); /* This will now be applied */
background-color: transparent; /* Override the background change */
box-shadow: none; /* Remove the shadow */
color: var(--secondary-color);
background-color: transparent;
box-shadow: none;
}
/*? ↓  Start Footer ↓ */

View File

@@ -35,6 +35,7 @@ body {
.intro p {
font-size: 1.1em;
font-weight: 600;
color: #495057;
}
/*? ↓  Start Benefits Section  ↓ */
@@ -135,13 +136,17 @@ body {
}
.transition p:first-child {
font-size: 1.3em;
font-size: 1.1em;
}
.transition p:last-child {
font-size: 1.4rem;
font-size: 1.1rem;
}
.thrive {
color: #ca6e0b;
font-size: 1.2em;
}
/*? ↓  Start Services Container  ↓ */
.services-section.module {
@@ -164,7 +169,7 @@ body {
.accordion-container p {
font-weight: 600;
color: #333333;
color: #495057;
}
.accordion {
@@ -209,7 +214,7 @@ body {
box-shadow: none;
margin-bottom: 15px;
font-size: 1.3em;
color: #333333;
color: #495057;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
transition: background-color 0.3s ease;
@@ -226,7 +231,7 @@ body {
transition: max-height 0.3s ease;
background-color: #ffffff;
padding: 0 15px;
color: #333333;
color: #495057;
}
.accordion-content ul {
@@ -237,7 +242,7 @@ body {
.accordion-content li {
padding: 10px;
color: #333333;
color: #495057;
}
.hover-list {
@@ -280,18 +285,19 @@ body {
text-decoration: none;
font-weight: 600;
border-radius: 50px;
box-shadow: 0 4px 8px rgba(202, 110, 11, 0.4);
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.demos-link:hover,
.cta-button:hover {
background-color: #2e97be;
box-shadow: 0 4px 8px rgba(46, 151, 190, 0.2);
box-shadow: 0 4px 8px rgba(46, 151, 190, 0.4);
}
.demos-link:active, .cta-button:active {
background-color: #ca6e0b;
box-shadow: 0 4px 8px rgba(46, 151, 190, 0.2);
box-shadow: 0 4px 8px rgba(202, 110, 11, 0.4);
}
.project-steps {

145
src/styles/privacy.css Normal file
View File

@@ -0,0 +1,145 @@
/* privacy.css */
/* Sets the background color for this page */
body {
background: linear-gradient(to bottom, #9FB8CC 0%, #DAE4F0 30%, #ffffff 50%, #DAE4F0 70%, #9FB8CC 100%);
font-family: var(--font-family-body);
}
/* Styles the main container that holds the content */
.module {
margin: var(--margin-medium) auto;
padding: var(--padding-large);
max-width: 900px;
box-shadow: var(--shadow-large);
background-color: var(--light-bg-color);
border-radius: 15px;
border: 1px solid #7B8899;
}
/* Styles the primary heading */
h1 {
color: var(--text-color);
text-align: center;
font-size: 2.5em;
margin-bottom: var(--margin-small);
}
/* Styles the secondary headings within sections */
h2 {
color: var(--primary-color);
font-size: 1.8em;
margin-top: var(--margin-medium);
margin-bottom: var(--margin-small);
text-align: center;
}
/* General paragraph and list item styles */
.module p,
.module li {
color: var(--text-color);
line-height: 1.6;
margin-bottom: var(--margin-small);
text-align: left;
margin-left: 30px;
margin-right: 30px;
}
/* Emphasizes strong text and headers for readability */
strong {
color: var(--primary-color);
}
/* Styles all links on the page */
a {
color: var(--secondary-color);
text-decoration: none;
transition: color 0.3s ease;
}
/* Defines the hover state for all links */
a:hover {
color: var(--primary-color);
text-decoration: underline;
}
/* Defines the style for the horizontal rule element */
hr {
border: none;
height: 1px;
background-color: var(--medium-gray-color);
margin: var(--margin-large) 0;
}
/* Styles the Table of Contents container */
.toc {
border-left: 3px solid var(--secondary-color);
padding-left: 15px;
margin-left: auto;
margin-right: auto;
text-align: center;
width: fit-content;
}
.toc ul {
list-style: none;
padding: 0;
margin: 0;
text-align: left;
}
.toc li {
padding: 2px 0;
padding-left: 10px;
margin: 0;
}
/* Correctly indents all other lists */
#data-collection ul, #privacy-rights ul, #contact ul, #data-use ul {
list-style: disc; /* Re-enable bullet points for all lists inside the module */
padding-left: 20px; /* Indent the entire list */
margin-left: 10px; /* Adds additional margin */
}
.highlight-statement {
color: var(--primary-color);
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
/* Styles the "Back to Top" link */
.back-to-top {
display: block;
text-align: center;
margin: var(--margin-large) 0;
font-size: 0.9em;
font-weight: bold;
color: var(--dark-gray-color);
text-decoration: underline;
transition: color 0.3s ease;
}
/* Styles the hover state for the "Back to Top" link */
.back-to-top:hover {
color: var(--primary-color);
text-decoration: none;
}
.download {
display: inline-block;
background: var(--secondary-color);
border: solid 1px var(--medium-gray-color);
border-radius: 10px;
padding: 5px 20px;
box-shadow: var(--shadow-medium);
align-items: center;
text-align: center;
color: var(--light-gray-color);
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.download:hover {
background-color: var(--primary-color);
box-shadow: var(--shadow-large);
color: var(--light-gray-color);
}