28 Commits

Author SHA1 Message Date
de8ba59d59 Merge remote-tracking branch 'astro/astro'
# Conflicts:
#	.gitignore
2026-04-23 21:29:57 -04:00
bd00d84ba4 Seperate CSS for music 2026-02-28 22:32:58 +00:00
e17bfe884c Add thin yellow box outline around announcement
- Add border and padding to .announcement class
- Makes 'EXCLUSIVE: Leaked mixtape' text stand out
2026-02-28 22:10:02 +00:00
f824e30dc0 Merge pull request 'Blog JSON system' (#1) from OpenClaw into main
Reviewed-on: http://pi.dangrubb.net/dangit/dangrubbb/dangrubb.net/pulls/1
2026-02-28 21:57:54 +00:00
c1ab8e831b Minor Manual Fixes to Lobster's code 2026-02-28 16:52:01 -05:00
447265f7ed Fix: Center blog post content properly
- Use flexbox on blog-post container to center all children
- Set max-width on post container to constrain width
- Ensure content paragraph takes full width and centers text
2026-02-28 16:45:00 -05:00
e5b6d785ed Fix: Image path for homepage preview and center blog content
- Use absolute path for image (/blog/src/img/...) so homepage preview loads correctly
- Center blog post content with max-width for better readability
2026-02-28 16:42:56 -05:00
73f6c462f8 Make blog page dynamic using JSON data
- Blog posts now load from blog/posts.json
- Create blog/script.js to dynamically render posts
- Each post displays: title, date, content, image
- Support hash-based navigation to specific posts
- Updated blog/index.html to load posts dynamically
- Added styling for blog posts and containers
- First post is 'Status' matching the original content
2026-02-28 16:38:42 -05:00
a965274a31 Add dynamic blog preview to homepage
- Create blog/posts.json for managing blog post metadata
- Add blog preview box (max 1/3 screen width) to homepage
- Fetch latest post dynamically with image and excerpt
- Updates automatically when new posts added to JSON
- Styled to fit no-scroll homepage layout
- Link to full blog page for each post
2026-02-28 16:30:19 -05:00
7e8727d1b5 Fixing OpenClaw's CSS formatting 2026-02-28 15:22:06 +00:00
2293a3f566 Fixing OpenClaw's formatting 2026-02-28 15:21:43 +00:00
a38d756f4b Add music platform with DGDN album player
- Create /music landing page with artist grid
- Add /music/Darnea artist page with album listing
- Build /music/Darnea/DGDN album player with clickable tracklist
- Auto-play next track on completion
- Dangrubb-branded styling with cyan accent colors
- FLAC player pulls from /src/audio/DGDN
- Move landing page links to bottom with yellow announcement box
2026-02-28 15:20:27 +00:00
025fe8a80b Updated blog with image 2025-09-25 20:55:52 -04:00
7b70740e46 Updated blog 2025-09-21 09:01:55 -04:00
c5a6b90d7e Changed Blog Location 2025-09-21 08:32:48 -04:00
b93c70542e Updated CV for 9/2025 2025-09-21 08:07:58 -04:00
9b24e45c65 updated CV references 2025-06-13 03:45:08 -04:00
5f32d340fd updated scaling for mobile 2025-06-12 12:07:19 -04:00
f2db5f197a Initial CV PDF commit 2025-06-12 02:51:19 +01:00
ab16fd6b00 Initial CV img commit 2025-06-12 02:50:46 +01:00
8ecf3b38af CV js commit to js folder 2025-06-12 02:49:15 +01:00
0806ee7a55 revert c64341ada9
revert Initial CV js commit
2025-06-12 02:48:32 +01:00
c64341ada9 Initial CV js commit 2025-06-12 02:47:03 +01:00
3fa5a4ab4e Initial CV styles commit 2025-06-12 02:46:31 +01:00
a70f22dbfd Initial CV commit 2025-06-12 02:45:59 +01:00
5dbb99d505 Upload files to "/"
Created Custom 404 page
2025-06-12 02:29:23 +01:00
b01bc4a9a3 initial img commit 2025-06-12 02:26:18 +01:00
b85e5f281b Upload files to "/"
Initial Commit
2025-06-12 02:25:29 +01:00
72 changed files with 2231 additions and 322 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
deploy_dangrubb.net.sh
node_modules/
dist/
.astro/

254
blog/index.html Normal file
View File

@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>dangrubb.net</title>
<link rel="icon" href="/src/img/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/src/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div class="bard">
<div class="strip" style="
--glitch-x-1: -2em;
--glitch-hue-1: -16deg;
--glitch-x-2: 2em;
--glitch-hue-2: 13deg;
background-position: 0 -0em;
height: 5em;
animation-name: glitch-9;
animation-duration: 9000ms;
animation-delay: 2s;
"></div>
<div class="strip" style="
--glitch-x-1: -1em;
--glitch-hue-1: -42deg;
--glitch-x-2: -6em;
--glitch-hue-2: -23deg;
background-position: 0 -5em;
height: 3em;
animation-name: glitch-5;
animation-duration: 5000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -10em;
--glitch-hue-1: -42deg;
--glitch-x-2: -9em;
--glitch-hue-2: 45deg;
background-position: 0 -8em;
height: 3em;
animation-name: glitch-7;
animation-duration: 7000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: -6em;
--glitch-hue-1: -11deg;
--glitch-x-2: 2em;
--glitch-hue-2: 29deg;
background-position: 0 -11em;
height: 5em;
animation-name: glitch-8;
animation-duration: 8000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: 0em;
--glitch-hue-1: 42deg;
--glitch-x-2: 2em;
--glitch-hue-2: -28deg;
background-position: 0 -16em;
height: 5em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: -3em;
--glitch-hue-1: -45deg;
--glitch-x-2: -5em;
--glitch-hue-2: -15deg;
background-position: 0 -21em;
height: 2em;
animation-name: glitch-7;
animation-duration: 7000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: -5em;
--glitch-hue-1: 35deg;
--glitch-x-2: 9em;
--glitch-hue-2: 35deg;
background-position: 0 -23em;
height: 2em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: 10em;
--glitch-hue-1: 39deg;
--glitch-x-2: -8em;
--glitch-hue-2: 24deg;
background-position: 0 -25em;
height: 6em;
animation-name: glitch-7;
animation-duration: 7000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -6em;
--glitch-hue-1: -46deg;
--glitch-x-2: -3em;
--glitch-hue-2: 18deg;
background-position: 0 -31em;
height: 6em;
animation-name: glitch-10;
animation-duration: 10000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -8em;
--glitch-hue-1: -37deg;
--glitch-x-2: -6em;
--glitch-hue-2: 15deg;
background-position: 0 -37em;
height: 1em;
animation-name: glitch-9;
animation-duration: 9000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -1em;
--glitch-hue-1: 16deg;
--glitch-x-2: 2em;
--glitch-hue-2: 25deg;
background-position: 0 -38em;
height: 2em;
animation-name: glitch-5;
animation-duration: 5000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: 5em;
--glitch-hue-1: 32deg;
--glitch-x-2: 10em;
--glitch-hue-2: -3deg;
background-position: 0 -40em;
height: 2em;
animation-name: glitch-9;
animation-duration: 9000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: 10em;
--glitch-hue-1: -26deg;
--glitch-x-2: 6em;
--glitch-hue-2: -45deg;
background-position: 0 -42em;
height: 4em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -7em;
--glitch-hue-1: -45deg;
--glitch-x-2: -8em;
--glitch-hue-2: 45deg;
background-position: 0 -46em;
height: 3em;
animation-name: glitch-5;
animation-duration: 5000ms;
animation-delay: 2s;
"></div>
<div class="strip" style="
--glitch-x-1: 4em;
--glitch-hue-1: 40deg;
--glitch-x-2: -8em;
--glitch-hue-2: 29deg;
background-position: 0 -49em;
height: 3em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -4em;
--glitch-hue-1: 26deg;
--glitch-x-2: -6em;
--glitch-hue-2: -3deg;
background-position: 0 -52em;
height: 6em;
animation-name: glitch-10;
animation-duration: 10000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: 6em;
--glitch-hue-1: 43deg;
--glitch-x-2: -1em;
--glitch-hue-2: -1deg;
background-position: 0 -58em;
height: 4em;
animation-name: glitch-8;
animation-duration: 8000ms;
animation-delay: 0s;
"></div>
</div>
<p>
dangrubb.net/blog
</p>
<p>
<a href="../index.html"><-- back to home</a>
</p>
<div id="blogsContainer" class="blogs-container">
<p style="color: #9d9aa4;">Loading blog posts...</p>
</div>
<!-- partial -->
<script src="./script.js"></script>
</body>
</html>

11
blog/posts.json Normal file
View File

@ -0,0 +1,11 @@
[
{
"id": "01",
"title": "Status",
"date": "2026-02-28",
"excerpt": "dangrubb.net is fully functional and operating at 18% of potential. When Blog updates are posted, this score will increase to 19%.",
"content": "<p>dangrubb.net is fully functional and operating at 18% of potential. When Blog updates are posted, this score will increase to 19%.</p>",
"image": "/blog/src/img/CannotConnect.JPEG",
"slug": "status"
}
]

53
blog/script.js Normal file
View File

@ -0,0 +1,53 @@
"use strict";
// Load and display blog posts from JSON
async function loadBlogPosts() {
try {
const response = await fetch('./posts.json');
const posts = await response.json();
const blogsContainer = document.getElementById('blogsContainer');
if (posts.length === 0) {
blogsContainer.innerHTML = '<div class="no-posts"><p>No blog posts yet.</p></div>';
return;
}
// Create HTML for each post
const postsHTML = posts.map(post => `
<article class="blog-post" id="${post.slug}">
<h2 class="blog-post-title">${post.title}</h2>
<div class="blog-post-date">${new Date(post.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</div>
<div class="blog-post-content">
${post.content}
</div>
<div class="blog-post-image">
<img src="${post.image}" alt="${post.title}" />
</div>
</article>
`).join('');
blogsContainer.innerHTML = postsHTML;
} catch (error) {
console.error('Error loading blog posts:', error);
document.getElementById('blogsContainer').innerHTML = '<div class="error"><p>Error loading blog posts.</p></div>';
}
}
// Load blog posts when page is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadBlogPosts);
} else {
loadBlogPosts();
}
// Handle hash-based navigation to specific posts
window.addEventListener('hashchange', () => {
const postId = window.location.hash.slice(1);
if (postId) {
const element = document.getElementById(postId);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

290
blog/style.css Normal file

File diff suppressed because one or more lines are too long

39
custom_404.html Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Not Found</title>
<style>
body {
background-color: #123425;
color: white;
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
text-align: center;
}
h1 {
font-size: 3rem;
margin: 0.5em 0;
}
p {
font-size: 1.2rem;
margin: 0.5em 0;
}
.small-text {
font-size: 1rem;
margin-top: 0.5em;
}
</style>
</head>
<body>
<h1>Page Not Found</h1>
<p>This page does not exist yet.</p>
<p class="small-text">Stay tuned.</p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

BIN
cv/assets/img/dangrubb.net Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

BIN
cv/assets/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

BIN
cv/assets/pdf/ResumeCv.pdf Normal file

Binary file not shown.

3
cv/js/html2pdf.bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

272
index.html Normal file
View File

@ -0,0 +1,272 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>dangrubb.net</title>
<link rel="icon" href="/src/img/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/src/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div class="bard">
<div class="strip" style="
--glitch-x-1: -2em;
--glitch-hue-1: -16deg;
--glitch-x-2: 2em;
--glitch-hue-2: 13deg;
background-position: 0 -0em;
height: 5em;
animation-name: glitch-9;
animation-duration: 9000ms;
animation-delay: 2s;
"></div>
<div class="strip" style="
--glitch-x-1: -1em;
--glitch-hue-1: -42deg;
--glitch-x-2: -6em;
--glitch-hue-2: -23deg;
background-position: 0 -5em;
height: 3em;
animation-name: glitch-5;
animation-duration: 5000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -10em;
--glitch-hue-1: -42deg;
--glitch-x-2: -9em;
--glitch-hue-2: 45deg;
background-position: 0 -8em;
height: 3em;
animation-name: glitch-7;
animation-duration: 7000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: -6em;
--glitch-hue-1: -11deg;
--glitch-x-2: 2em;
--glitch-hue-2: 29deg;
background-position: 0 -11em;
height: 5em;
animation-name: glitch-8;
animation-duration: 8000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: 0em;
--glitch-hue-1: 42deg;
--glitch-x-2: 2em;
--glitch-hue-2: -28deg;
background-position: 0 -16em;
height: 5em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: -3em;
--glitch-hue-1: -45deg;
--glitch-x-2: -5em;
--glitch-hue-2: -15deg;
background-position: 0 -21em;
height: 2em;
animation-name: glitch-7;
animation-duration: 7000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: -5em;
--glitch-hue-1: 35deg;
--glitch-x-2: 9em;
--glitch-hue-2: 35deg;
background-position: 0 -23em;
height: 2em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: 10em;
--glitch-hue-1: 39deg;
--glitch-x-2: -8em;
--glitch-hue-2: 24deg;
background-position: 0 -25em;
height: 6em;
animation-name: glitch-7;
animation-duration: 7000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -6em;
--glitch-hue-1: -46deg;
--glitch-x-2: -3em;
--glitch-hue-2: 18deg;
background-position: 0 -31em;
height: 6em;
animation-name: glitch-10;
animation-duration: 10000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -8em;
--glitch-hue-1: -37deg;
--glitch-x-2: -6em;
--glitch-hue-2: 15deg;
background-position: 0 -37em;
height: 1em;
animation-name: glitch-9;
animation-duration: 9000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -1em;
--glitch-hue-1: 16deg;
--glitch-x-2: 2em;
--glitch-hue-2: 25deg;
background-position: 0 -38em;
height: 2em;
animation-name: glitch-5;
animation-duration: 5000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: 5em;
--glitch-hue-1: 32deg;
--glitch-x-2: 10em;
--glitch-hue-2: -3deg;
background-position: 0 -40em;
height: 2em;
animation-name: glitch-9;
animation-duration: 9000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: 10em;
--glitch-hue-1: -26deg;
--glitch-x-2: 6em;
--glitch-hue-2: -45deg;
background-position: 0 -42em;
height: 4em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -7em;
--glitch-hue-1: -45deg;
--glitch-x-2: -8em;
--glitch-hue-2: 45deg;
background-position: 0 -46em;
height: 3em;
animation-name: glitch-5;
animation-duration: 5000ms;
animation-delay: 2s;
"></div>
<div class="strip" style="
--glitch-x-1: 4em;
--glitch-hue-1: 40deg;
--glitch-x-2: -8em;
--glitch-hue-2: 29deg;
background-position: 0 -49em;
height: 3em;
animation-name: glitch-6;
animation-duration: 6000ms;
animation-delay: 1s;
"></div>
<div class="strip" style="
--glitch-x-1: -4em;
--glitch-hue-1: 26deg;
--glitch-x-2: -6em;
--glitch-hue-2: -3deg;
background-position: 0 -52em;
height: 6em;
animation-name: glitch-10;
animation-duration: 10000ms;
animation-delay: 0s;
"></div>
<div class="strip" style="
--glitch-x-1: 6em;
--glitch-hue-1: 43deg;
--glitch-x-2: -1em;
--glitch-hue-2: -1deg;
background-position: 0 -58em;
height: 4em;
animation-name: glitch-8;
animation-duration: 8000ms;
animation-delay: 0s;
"></div>
</div>
<p>
dangrubb.net
</p>
<p class="announcement">
<a href="https://dangrubb.net/music">EXCLUSIVE: Leaked mixtape from Darnea's vault</a>
</p>
<div class="blog-preview-container">
<div class="blog-preview" id="latestBlogPreview">
<p style="color: #9d9aa4;">Loading latest post...</p>
</div>
</div>
<div class="debug">
<div class="code"></div>
</div>
<!-- Navigation Links (now pinned to bottom via CSS) -->
<nav class="bottom-nav">
<p>
<a target="_parent" href="https://ai.dangrubb.net">ai</a>
<a target="_parent" href="https://dangrubb.net/blog">blog</a>
<a target="_parent" href="https://pi.dangrubb.net/jellyfin">media</a>
<a target="_parent" href="https://pi.dangrubb.net/nextcloud">storage</a>
<a target="_parent" href="https://dangrubb.net/cv">cv</a>
<a target="_parent" href="https://pi.dangrubb.net/dangit">git</a>
</p>
</nav>
<!-- partial -->
<script src="./script.js"></script>
</body>
</html>

View File

@ -1,14 +1,14 @@
// Album player for DGDN
const tracks = [
{ title: "Dan Beat (bonus)", filename: "Dan Beat (bonus).flac" },
{ title: "Neigh V3", filename: "Neigh V3.flac" },
{ title: "On Ice", filename: "On Ice.flac" },
{ title: "Otish", filename: "Otish.flac" },
{ title: "Scan V2", filename: "Scan V2.flac" },
{ title: "balance v2", filename: "balance v2.flac" },
{ title: "eh ee uhn V2", filename: "eh ee uhn V2.flac" },
{ title: "held v3", filename: "held v3.flac" },
{ title: "Dan Beat (bonus)", filename: "Dan Beat (bonus).flac" }
{ title: "held v3", filename: "held v3.flac" }
];
const audioPlayer = document.getElementById('audio-player');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

166
script.js Normal file
View File

@ -0,0 +1,166 @@
"use strict";
const random = (min, max) => {
return Math.round(Math.random() * (max - min)) + min;
};
const getKeyFrames = (name, glitchPercentageDuration, steps = 3, tick = 0.1) => {
const percentageStep = 100 / steps;
const keyframes = [];
// First keyframe
const baseKeys = [0];
for (let i = 1; i < steps; i++) {
const p = i * percentageStep;
baseKeys.push(p);
baseKeys.push(p + glitchPercentageDuration);
}
// Last keyframe
baseKeys.push(100);
keyframes.push({
keys: baseKeys,
css: {
transform: "none",
filter: "hue-rotate(0) drop-shadow(0 0 0 transparent)" // Hack to force animation in Safari
}
});
for (let i = 1; i < steps; i++) {
const p = i * percentageStep;
// Blue / red shadow
const color = Math.random() > 0.5 ? "rgb(255 0 0 / 0.1)" : "rgb(0 0 255 / 0.1)";
const shadowX = random(-4, 4);
const shadowY = random(-4, 4);
keyframes.push({
keys: [p + tick, p + glitchPercentageDuration - tick],
css: {
transform: `translateX(var(--glitch-x-${i}))`,
filter: `hue-rotate(var(--glitch-hue-${i})) drop-shadow(${shadowX}px ${shadowY}px 0 ${color})`
}
});
}
const css = keyframes
.map((keyframe) => {
const keys = keyframe.keys
.map((key) => `${key.toFixed(2)}%`)
.join(",\n ");
const content = Object.entries(keyframe.css)
.map(([key, value]) => ` ${key}: ${value};`)
.join("\n ");
return [keys, "{", content, "}"].join("\n ");
})
.join("\n\n ");
return `@keyframes ${name} {\n ${css}\n}`;
};
const getStripHTML = (top, stripHeight) => {
const duration = random(5, 10);
const name = `glitch-${duration}`;
return `<div
class="strip"
style="
--glitch-x-1: ${random(-10, 10)}em;
--glitch-hue-1: ${random(-50, 50)}deg;
--glitch-x-2: ${random(-10, 10)}em;
--glitch-hue-2: ${random(-50, 50)}deg;
background-position: 0 -${top}em;
height: ${stripHeight}em;
animation-name: ${name};
animation-duration: ${duration * 1000}ms;
animation-delay: ${random(0, 2)}s;
"
></div>`;
};
const getGlitchHTML = (height) => {
let i = 0;
const html = [];
while (1) {
const stripHeight = random(1, 6);
if (i + stripHeight < height) {
const strip = getStripHTML(i, stripHeight);
html.push(strip);
}
else {
// Last strip
const strip = getStripHTML(i, height - i);
html.push(strip);
break;
}
i = i + stripHeight;
}
return html;
};
/*
If you want to generate new CSS/HTML dinamically,
uncomment the code below.
*/
// const html = getGlitchHTML(62);
// // HTML
// const $glitch = document.querySelector(".bard") as HTMLElement;
// $glitch.innerHTML = html.join("\n");
// // CSS
// const css = [5,6,7,8,9,10].map((n) => {
// const glitchDurationMS = 500;
// const glitchPercentageDuration = (glitchDurationMS * 100) / (n * 1000);
// return getKeyFrames(`glitch-${n}`, glitchPercentageDuration);
// });
// // Add generated CSS to the page
// const $style = document.createElement("style");
// $style.innerHTML = css.join("\n");
// document.head.appendChild($style);
// // ----- Debug -------------- //
// // Not used for the animation //
// const $code = document.querySelector(".code");
// const escape = (html) => {
// return html
// .replace(/&/g, "&amp;")
// .replace(/</g, "&lt;")
// .replace(/>/g, "&gt;")
// .replace(/"/g, "&quot;")
// .replace(/'/g, "&#039;");
// };
// $code.innerHTML = `
// <div class="column">
// <div class="heading">HTML</div>
// <pre>${escape(html.join("\n\n"))}</pre>
// </div>
// <div class="column">
// <div class="heading">CSS</div>
// <pre>${css.join("\n\n")}</pre>
// </div>
// `;
// Load latest blog post preview
async function loadLatestBlogPost() {
try {
const response = await fetch('./blog/posts.json');
const posts = await response.json();
if (posts.length === 0) {
document.getElementById('latestBlogPreview').innerHTML = '<p style="color: #9d9aa4;">No blog posts yet.</p>';
return;
}
// Get the latest post (first in the array)
const latestPost = posts[0];
// Create the preview HTML
const previewHTML = `
<a href="./blog/#${latestPost.slug}" class="blog-preview-title">${latestPost.title}</a>
<div class="blog-preview-date">${new Date(latestPost.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</div>
<img src="${latestPost.image}" alt="${latestPost.title}" />
<p class="blog-preview-excerpt">${latestPost.excerpt}</p>
<a href="./blog/#${latestPost.slug}" class="blog-preview-link">Read more →</a>
`;
document.getElementById('latestBlogPreview').innerHTML = previewHTML;
} catch (error) {
console.error('Error loading blog posts:', error);
document.getElementById('latestBlogPreview').innerHTML = '<p style="color: #9d9aa4;">Error loading blog posts.</p>';
}
}
// Load blog preview when page is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadLatestBlogPost);
} else {
loadLatestBlogPost();
}

BIN
src/audio/._DGDN Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/audio/DGDN/._Otish.flac Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/audio/DGDN/On Ice.flac Normal file

Binary file not shown.

Binary file not shown.

BIN
src/audio/DGDN/Otish.flac Normal file

Binary file not shown.

Binary file not shown.

BIN
src/audio/DGDN/Scan V2.flac Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/audio/DGDN/held v3.flac Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
src/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,17 +0,0 @@
---
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<link rel="icon" href="/src/img/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />
</head>
<body>
<slot />
</body>
</html>

View File

@ -1,58 +0,0 @@
---
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<link rel="icon" href="/src/img/favicon.ico" type="image/x-icon" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Alfa+Slab+One&family=Barlow:wght@400;600;700&display=swap" rel="stylesheet">
</head>
<body>
<slot />
</body>
</html>
<style is:global>
* { box-sizing: border-box; margin: 0; padding: 0; }
/* Camel Crush Smooth Silver color palette */
:root {
--crush-teal: #008b8b;
--crush-teal-light: #20b2aa;
--crush-teal-dark: #006666;
--crush-silver: #c0c0c0;
--crush-white: #ffffff;
--crush-bg: #f8f9fa;
--crush-cream: #faf8f5;
}
body {
background: var(--crush-white);
font-family: 'Barlow', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
color: var(--crush-teal-dark);
min-height: 100vh;
margin: 0;
padding: 0;
overflow-x: hidden;
}
a {
color: var(--crush-teal);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--crush-teal-light);
}
a:visited {
color: var(--crush-teal);
}
</style>

View File

@ -1,19 +1,15 @@
---
import CrushBase from '../../layouts/CrushBase.astro';
import Base from '../../layouts/Base.astro';
import GlitchBard from '../../components/GlitchBard.astro';
import posts from '../../data/posts.json';
---
<CrushBase title="dangrubb.net/blog">
<Base title="dangrubb.net/blog">
<div class="page">
<header class="blog-header">
<div class="header-logo">
<img src="/src/img/DanGrubbLogoTeal.png" alt="dangrubb" class="logo-img">
</div>
<p class="page-title">
<span class="title-name">dangrubb</span><span class="title-dot">.</span><span class="title-tld">net</span><span class="title-slash">/</span><span class="title-page">blog</span>
</p>
<p class="back-link"><a href="/">← home</a></p>
</header>
<GlitchBard />
<p class="page-title">dangrubb.net/blog</p>
<p class="back-link"><a href="/">← back to home</a></p>
<div class="blogs-container">
{posts.map(post => (
@ -29,111 +25,66 @@ import posts from '../../data/posts.json';
</article>
))}
</div>
<footer class="blog-footer">
<a href="/">← home</a>
</footer>
</div>
</CrushBase>
</Base>
<style>
@import '../../styles/bard.css';
.page {
padding: 20px 20px 40px;
min-height: 100vh;
display: flex;
flex-direction: column;
background: var(--crush-white);
box-sizing: border-box;
}
.blog-header {
.page-title, .back-link {
margin: 20px auto 0;
max-width: 400px;
text-align: center;
padding: 30px 20px 20px;
background: linear-gradient(180deg, #e8f5f5 0%, transparent 100%);
border-bottom: 3px solid var(--crush-teal);
}
.header-logo {
display: flex;
justify-content: center;
margin-bottom: 12px;
}
.logo-img {
width: 60px;
height: 60px;
object-fit: contain;
}
.page-title {
font-family: 'Alfa Slab One', 'Georgia', serif;
font-size: clamp(1.8rem, 5vw, 3rem);
letter-spacing: -1px;
line-height: 1;
margin-bottom: 8px;
}
.title-name { color: var(--crush-teal); }
.title-dot { color: var(--crush-teal-light); }
.title-tld { color: var(--crush-teal-dark); }
.title-slash { color: var(--crush-teal); }
.title-page { color: var(--crush-teal-dark); font-size: 0.7em; }
.back-link {
line-height: 1.3;
font-size: 0.875rem;
color: var(--crush-teal);
margin-top: 4px;
}
.back-link a {
color: var(--crush-teal);
opacity: 0.7;
}
.back-link a:hover { opacity: 1; }
.blogs-container {
margin: 30px auto;
max-width: 700px;
margin: 40px auto 0;
max-width: 65em;
width: 100%;
padding: 0 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.blog-post {
margin-bottom: 40px;
padding: 24px;
border: 1px solid rgba(0, 139, 139, 0.12);
border-radius: 6px;
background: linear-gradient(135deg, #f0faf9, #f8f9fa);
margin: 40px auto;
padding: 20px;
border: 1px solid rgba(155, 169, 180, 0.2);
border-radius: 4px;
background: linear-gradient(135deg, rgba(13, 10, 20, 0.4), rgba(50, 30, 80, 0.2));
scroll-margin-top: 100px;
max-width: 700px;
width: 100%;
}
.blog-post-title {
color: var(--crush-teal);
font-weight: 700;
font-size: 1.6rem;
color: #ffff00;
font-weight: bold;
font-size: 1.8rem;
text-align: center;
margin-bottom: 6px;
}
.blog-post-date {
color: var(--crush-teal-light);
font-size: 0.85rem;
color: #aa95bd;
font-size: 0.9rem;
text-align: center;
margin-bottom: 20px;
opacity: 0.7;
}
.blog-post-content {
color: #9d9aa4;
font-size: 0.95rem;
line-height: 1.6;
color: #333;
text-align: center;
}
.blog-post-content :global(p) {
margin: 0 0 15px;
}
.blog-post-image {
display: flex;
justify-content: center;
@ -144,26 +95,13 @@ import posts from '../../data/posts.json';
height: auto;
max-height: 60vh;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 102, 102, 0.15);
}
.blog-footer {
text-align: center;
padding: 30px 20px 40px;
border-top: 3px solid var(--crush-teal);
background: linear-gradient(0deg, #e8f5f5 0%, transparent 100%);
margin-top: auto;
}
.blog-footer a {
color: var(--crush-teal);
font-weight: 600;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
@media (max-width: 768px) {
.blog-header { padding: 20px 15px 15px; }
.logo-img { width: 45px; height: 45px; }
.blogs-container { padding: 0 15px; }
.blog-post { padding: 16px; margin-bottom: 24px; }
.blog-post-title { font-size: 1.3rem; }
.page { padding: 20px 10px 40px; }
.page-title, .back-link { font-size: 0.8rem; max-width: 320px; }
.blog-post { padding: 15px; margin: 20px auto; }
.blog-post-title { font-size: 1.4rem; }
}
</style>

324
src/pages/cv/index.astro Normal file
View File

@ -0,0 +1,324 @@
---
import Base from '../../layouts/Base.astro';
---
<Base title="Dan Grubb CV">
<div class="cv-page">
<!-- HEADER -->
<header class="l-header" id="header">
<nav class="nav bd-container">
<a href="#" class="nav__logo">Dan</a>
<div class="nav__menu" id="nav-menu">
<ul class="nav__list">
<li class="nav__item"><a href="#home" class="nav__link active-link"><i class="bx bx-home nav__icon"></i>Home</a></li>
<li class="nav__item"><a href="#profile" class="nav__link"><i class="bx bx-user nav__icon"></i>Profile</a></li>
<li class="nav__item"><a href="#education" class="nav__link"><i class="bx bx-book nav__icon"></i>Education</a></li>
<li class="nav__item"><a href="#skills" class="nav__link"><i class="bx bx-receipt nav__icon"></i>Skills</a></li>
<li class="nav__item"><a href="#experiencie" class="nav__link"><i class="bx bx-briefcase nav__icon"></i>Experience</a></li>
<li class="nav__item"><a href="#certificates" class="nav__link"><i class="bx bx-briefcase nav__icon"></i>Certificates</a></li>
<li class="nav__item"><a href="#references" class="nav__link"><i class="bx bx-link-external nav__icon"></i>References</a></li>
</ul>
</div>
<div class="nav__toggle" id="nav-toggle"><i class="bx bx-grid-alt nav__icon"></i></div>
</nav>
</header>
<main class="l-main bd-container">
<div class="resume" id="area-cv">
<div class="resume__left">
<!-- HOME -->
<section class="home" id="home">
<div class="home__container section bd-grid">
<div class="home__data bd-grid">
<img src="/cv/assets/img/perfil-example.jpg" alt="perfil" class="home__img" />
<h1 class="home__title">Dan <b>Grubb</b></h1>
<h3 class="home__profession">IT Man</h3>
<div>
<a download="" href="/cv/assets/pdf/DanGrubbResume.pdf" class="home__button-movil">Download</a>
</div>
</div>
<div class="home__address bd-grid">
<span class="home__information"><i class="bx bx-map home__icon"></i>Falls Church, VA</span>
<span class="home__information"><i class="bx bx-envelope home__icon"></i> Dan@DanGrubb.Net</span>
<span class="home__information"><i class="bx bx-phone home__icon"></i> 703-649-1637</span>
<span class="home__information"><i class="bx bx-planet home__icon"></i> dangrubb.net</span>
</div>
</div>
<i class="bx bx-moon change-theme" title="Theme" id="theme-button"></i>
<i class="bx bx-download generate-pdf" title="Generate PDF" id="resume-button"></i>
</section>
<!-- PROFILE -->
<section class="profile section" id="profile">
<h2 class="section-title">Profile</h2>
<p class="profile__description">
Dynamic and detail-oriented IT professional with a proven track record in both independent and collaborative environments. Skilled in prioritizing tasks and adept at diagnosing and resolving complex technical issues to ensure optimal performance and customer satisfaction.
</p>
</section>
<!-- EDUCATION -->
<section class="education section" id="education">
<h2 class="section-title">Education</h2>
<div class="education__container">
<div class="education__content">
<div class="education__time">
<span class="education__rounder"></span>
<span class="education__line"></span>
</div>
<div class="education__data bd-grid">
<h3 class="education__title">Advanced Diploma</h3>
<span class="education__studies">Patriot High School</span>
<span class="education__year">2015 - 2019</span>
</div>
</div>
<div class="education__content">
<div class="education__time">
<span class="education__rounder"></span>
<span class="education__line"></span>
</div>
<div class="education__data bd-grid">
<h3 class="education__title">Some College</h3>
<span class="education__year">2020</span>
</div>
</div>
<div class="education__content">
<div class="education__time">
<span class="education__rounder"></span>
</div>
<div class="education__data bd-grid">
<h3 class="education__title">Self-Study Certifications</h3>
<span class="education__year">2020 - Present</span>
</div>
</div>
</div>
</section>
<!-- SKILLS -->
<section class="skills section" id="skills">
<h2 class="section-title">Skills</h2>
<div class="skills__content bd-grid">
<ul class="skills__data">
<li class="skills__name"><span class="skills__circle"></span>Windows</li>
<li class="skills__name"><span class="skills__circle"></span>Mac</li>
<li class="skills__name"><span class="skills__circle"></span>Linux</li>
<li class="skills__name"><span class="skills__circle"></span>Networking</li>
</ul>
<ul class="skills__data">
<li class="skills__name"><span class="skills__circle"></span>Security</li>
<li class="skills__name"><span class="skills__circle"></span>Hardware</li>
<li class="skills__name"><span class="skills__circle"></span>Software</li>
</ul>
</div>
</section>
</div>
<div class="resume__right">
<!-- EXPERIENCE -->
<section class="experience section" id="experience">
<h2 class="section-title">Experience</h2>
<div class="experience__container bd-grid">
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">IT Consultant and Support Technician</h3>
<span class="experience__company">2025 to Present | Blue Tech Innovation</span>
<ul class="experience__description">
<li>• Provide administration and internal support for company systems, including PCs, Macs, phones, printers, servers, and other related equipment.</li>
<li>• Offer end-user support for both office-based and remote employees.</li>
<li>• Ensure the health, stability, and best practices configuration of client infrastructures.</li>
<li>• Troubleshoot and resolve issues with the following technologies:</li>
<li>• Windows Server, VPN Clients, Group Policy, Folder Permissions</li>
<li>• Firewalls, Printer Servers, Office 365</li>
<li>• Mac systems, workstation and network connectivity issues</li>
<li>• Exchange Server, Microsoft Office Applications, and the latest Microsoft Office suite versions</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Technical Account Manager</h3>
<span class="experience__company">2023 to 2025 | ITG</span>
<ul class="experience__description">
<li>• Ensure customer success with technical consulting and implementation of adaptive software and hardware.</li>
<li>• Manage client relationships and troubleshoot technical issues.</li>
<li>• Deliver tailored solutions for business needs.</li>
<li>• Find creative solutions for government and private clients.</li>
<li>• Maintained 99% SLA compliance.</li>
<li>• Streamlined and documented standard operating procedures.</li>
<li>• Automated daily and repetitive tasks.</li>
<li>• Created instructional media for field technicians.</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line__last"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Field Technician</h3>
<span class="experience__company">2023 | Dell</span>
<ul class="experience__description">
<li>• Managed a workload of repairs in a ticketing system.</li>
<li>• Scheduled, routed, and completed all tickets in a timely manner.</li>
<li>• Found solutions to complex hardware/firmware issues and ensured systems were up and running efficiently.</li>
<li>• Maintained positive professional relations with Dell's corporate, government, and consumer clients.</li>
<li>• Exceeded ticket completion standards handling all tickets in the Falls Church - Arlington area.</li>
</ul>
</div>
</div>
<a href="/cv/more-information" class="more__information">
<i class="bx bx-info-circle information__icon"></i> More Information
</a>
</div>
</section>
<!-- CERTIFICATES -->
<section class="certificate section" id="certificates">
<h2 class="section-title">Certifications</h2>
<div class="certificate__container bd-grid">
<div class="certificate__content">
<h3 class="certificate__title">CompTIA A+, Network+ and Security+</h3>
<p class="certificate__description">https://www.credly.com/users/dangrubbb</p>
</div>
<div class="certificate__content">
<h3 class="certificate__title">Microsoft Office Specialist</h3>
<p class="certificate__description">https://www.credly.com/users/dangrubbb</p>
</div>
<div class="certificate__content">
<h3 class="certificate__title">Dell, Apple, Samsung Certified Repair Technician</h3>
</div>
</div>
</section>
<!-- REFERENCES -->
<section class="references section" id="references">
<h2 class="section-title">References</h2>
<div class="references__container bd-grid">
<div class="references__content bd-grid">
<span class="references__subtitle">IT Engineer, ITG</span>
<h3 class="references__title">Samuel Alpert</h3>
<ul class="references__contact"><li>Details available upon request</li></ul>
</div>
<div class="references__content bd-grid">
<span class="references__subtitle">Server Technician, AMD</span>
<h3 class="references__title">Brian Oberoi</h3>
<ul class="references__contact"><li>Details available upon request</li></ul>
</div>
</div>
</section>
<!-- LANGUAGES -->
<section class="languages section">
<h2 class="section-title">Languages</h2>
<div class="languages__container">
<ul class="languages__content bd-grid">
<li class="languages__name"><span class="languages__circle"></span> English</li>
<li class="languages__name"><span class="languages__circle"></span> Python</li>
<li class="languages__name"><span class="languages__circle"></span> HTML</li>
</ul>
</div>
</section>
<!-- INTERESTS -->
<section class="interests section">
<h2 class="section-title">Interests</h2>
<div class="interests__container bd-grid">
<div class="interests__content"><i class="bx bx-headphone interests__icon"></i><span class="interests__name">Music</span></div>
<div class="interests__content"><i class="bx bx-cog interests__icon"></i><span class="interests__name">Tech</span></div>
<div class="interests__content"><i class="bx bx-walk interests__icon"></i><span class="interests__name">Fitness</span></div>
<div class="interests__content"><i class="bx bx-bible interests__icon"></i><span class="interests__name">Christ</span></div>
</div>
</section>
</div>
</div>
</main>
<a href="#" class="scrolltop" id="scroll-top">
<i class="bx bx-up-arrow-alt scrolltop__icon"></i>
</a>
</div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />
<script is:inline src="/cv/js/html2pdf.bundle.min.js"></script>
<script>
// Menu toggle
const toggle = document.getElementById('nav-toggle');
const nav = document.getElementById('nav-menu');
if (toggle && nav) {
toggle.addEventListener('click', () => nav.classList.toggle('show-menu'));
}
document.querySelectorAll('.nav__link').forEach(l => {
l.addEventListener('click', () => nav?.classList.remove('show-menu'));
});
// Scroll active
const sections = document.querySelectorAll('section[id]');
window.addEventListener('scroll', () => {
const scrollY = window.pageYOffset;
sections.forEach(current => {
const h = current.offsetHeight;
const top = current.offsetTop - 50;
const id = current.getAttribute('id');
const link = document.querySelector(`.nav__menu a[href*="${id}"]`);
if (link) {
if (scrollY > top && scrollY <= top + h) link.classList.add('active-link');
else link.classList.remove('active-link');
}
});
// Scroll top
const scrollTopEl = document.getElementById('scroll-top');
if (scrollTopEl) {
if (scrollY >= 200) scrollTopEl.classList.add('show-scroll');
else scrollTopEl.classList.remove('show-scroll');
}
});
// Theme toggle
const themeButton = document.getElementById('theme-button');
if (themeButton) {
const darkTheme = 'dark-theme';
const iconTheme = 'bx-sun';
const selectedTheme = localStorage.getItem('selected-theme');
const selectedIcon = localStorage.getItem('selected-icon');
if (selectedTheme) {
document.body.classList[selectedTheme === 'dark' ? 'add' : 'remove'](darkTheme);
themeButton.classList[selectedIcon === 'bx-moon' ? 'add' : 'remove'](iconTheme);
}
themeButton.addEventListener('click', () => {
document.body.classList.toggle(darkTheme);
themeButton.classList.toggle(iconTheme);
localStorage.setItem('selected-theme', document.body.classList.contains(darkTheme) ? 'dark' : 'light');
localStorage.setItem('selected-icon', themeButton.classList.contains(iconTheme) ? 'bx-moon' : 'bx-sun');
});
}
// PDF generation
const resumeButton = document.getElementById('resume-button');
if (resumeButton) {
// @ts-ignore
resumeButton.addEventListener('click', () => {
document.body.classList.add('scale-cv');
const areaCv = document.getElementById('area-cv');
const expLink = document.querySelector('#experience > div > a');
const interests = document.querySelector('.interests.section');
if (expLink) (expLink as HTMLElement).style.display = 'none';
if (interests) (interests as HTMLElement).style.display = 'none';
// @ts-ignore
html2pdf(areaCv, {
margin: 0, filename: 'DanGrubbResume.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { format: 'a4', orientation: 'portrait' },
});
setTimeout(() => {
document.body.classList.remove('scale-cv');
if (expLink) (expLink as HTMLElement).style.display = '';
if (interests) (interests as HTMLElement).style.display = '';
}, 5000);
});
}
</script>
</Base>
<style is:global>
@import '../../styles/cv.css';
</style>

View File

@ -0,0 +1,275 @@
---
import Base from '../../layouts/Base.astro';
---
<Base title="Dan Grubb CV - More Information">
<div class="cv-page">
<header class="l-header" id="header">
<nav class="nav bd-container">
<a href="/cv/" class="nav__logo">Dan</a>
<div class="nav__menu" id="nav-menu">
<ul class="nav__list">
<li class="nav__item"><a href="/cv/#home" class="nav__link"><i class="bx bx-home nav__icon"></i>Home</a></li>
<li class="nav__item"><a href="/cv/#profile" class="nav__link"><i class="bx bx-user nav__icon"></i>Profile</a></li>
<li class="nav__item"><a href="/cv/#education" class="nav__link"><i class="bx bx-book nav__icon"></i>Education</a></li>
<li class="nav__item"><a href="/cv/#skills" class="nav__link"><i class="bx bx-receipt nav__icon"></i>Skills</a></li>
<li class="nav__item"><a href="/cv/#experience" class="nav__link active-link"><i class="bx bx-briefcase nav__icon"></i>Experience</a></li>
<li class="nav__item"><a href="/cv/#certificates" class="nav__link"><i class="bx bx-briefcase nav__icon"></i>Certificates</a></li>
<li class="nav__item"><a href="/cv/#references" class="nav__link"><i class="bx bx-link-external nav__icon"></i>References</a></li>
</ul>
</div>
<div class="nav__toggle" id="nav-toggle"><i class="bx bx-grid-alt nav__icon"></i></div>
</nav>
</header>
<main class="l-main bd-container">
<div class="resume" id="area-cv">
<div class="resume__left">
<section class="home" id="home">
<div class="home__container section bd-grid">
<div class="home__data bd-grid">
<img src="/cv/assets/img/perfil-example.jpg" alt="perfil" class="home__img" />
<h1 class="home__title">Dan <b>Grubb</b></h1>
<h3 class="home__profession">IT Man</h3>
<div>
<a download="" href="/cv/assets/pdf/ResumeCv.pdf" class="home__button-movil">Download</a>
</div>
</div>
<div class="home__address bd-grid">
<span class="home__information"><i class="bx bx-map home__icon"></i>Falls Church, VA</span>
<span class="home__information"><i class="bx bx-envelope home__icon"></i> Dan@DanGrubb.Net</span>
<span class="home__information"><i class="bx bx-phone home__icon"></i> 703-649-1637</span>
<span class="home__information"><i class="bx bx-planet home__icon"></i> dangrubb.net</span>
</div>
</div>
<i class="bx bx-moon change-theme" title="Theme" id="theme-button"></i>
<i class="bx bx-download generate-pdf" title="Generate PDF" id="resume-button"></i>
</section>
<section class="profile section" id="profile">
<h2 class="section-title">Profile</h2>
<p class="profile__description">
Dynamic and detail-oriented IT professional with a proven track record in both independent and collaborative environments. Skilled in prioritizing tasks and adept at diagnosing and resolving complex technical issues to ensure optimal performance and customer satisfaction.
</p>
</section>
<section class="education section" id="education">
<h2 class="section-title">Education</h2>
<div class="education__container">
<div class="education__content">
<div class="education__time"><span class="education__rounder"></span><span class="education__line"></span></div>
<div class="education__data bd-grid">
<h3 class="education__title">Advanced Diploma</h3>
<span class="education__studies">Patriot High School</span>
<span class="education__year">2015 - 2019</span>
</div>
</div>
<div class="education__content">
<div class="education__time"><span class="education__rounder"></span><span class="education__line"></span></div>
<div class="education__data bd-grid">
<h3 class="education__title">Some College</h3>
<span class="education__year">2020</span>
</div>
</div>
<div class="education__content">
<div class="education__time"><span class="education__rounder"></span></div>
<div class="education__data bd-grid">
<h3 class="education__title">Self-Study Certifications</h3>
<span class="education__year">2020 - Present</span>
</div>
</div>
</div>
</section>
<section class="skills section" id="skills">
<h2 class="section-title">Skills</h2>
<div class="skills__content bd-grid">
<ul class="skills__data">
<li class="skills__name"><span class="skills__circle"></span>Windows</li>
<li class="skills__name"><span class="skills__circle"></span>Mac</li>
<li class="skills__name"><span class="skills__circle"></span>Linux</li>
<li class="skills__name"><span class="skills__circle"></span>Networking</li>
</ul>
<ul class="skills__data">
<li class="skills__name"><span class="skills__circle"></span>Security</li>
<li class="skills__name"><span class="skills__circle"></span>Hardware</li>
<li class="skills__name"><span class="skills__circle"></span>Software</li>
</ul>
</div>
</section>
</div>
<div class="resume__right">
<section class="experience section" id="experience">
<h2 class="section-title">Experience</h2>
<div class="experience__container bd-grid">
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">IT Consultant and Support Technician</h3>
<span class="experience__company">2025 to Present | Blue Tech Innovation</span>
<ul class="experience__description">
<li>• Provide administration and internal support for company systems, including PCs, Macs, phones, printers, servers, and other related equipment.</li>
<li>• Offer end-user support for both office-based and remote employees.</li>
<li>• Ensure the health, stability, and best practices configuration of client infrastructures.</li>
<li>• Troubleshoot and resolve issues with the following technologies:</li>
<li>• Windows Server, VPN Clients, Group Policy, Folder Permissions</li>
<li>• Firewalls, Printer Servers, Office 365</li>
<li>• Mac systems, workstation and network connectivity issues</li>
<li>• Exchange Server, Microsoft Office Applications, and the latest Microsoft Office suite versions</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Technical Account Manager</h3>
<span class="experience__company">2023 to Present | ITG</span>
<ul class="experience__description">
<li>• Ensure customer success with technical consulting and implementation of adaptive software and hardware.</li>
<li>• Manage client relationships and troubleshoot technical issues.</li>
<li>• Deliver tailored solutions for business needs.</li>
<li>• Find creative solutions for government and private clients.</li>
<li>• Maintained 99% SLA compliance.</li>
<li>• Streamlined and documented standard operating procedures.</li>
<li>• Automated daily and repetitive tasks.</li>
<li>• Created instructional media for field technicians.</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Field Technician</h3>
<span class="experience__company">2023 | Dell</span>
<ul class="experience__description">
<li>• Managed a workload of repairs in a ticketing system.</li>
<li>• Scheduled, routed, and completed all tickets in a timely manner.</li>
<li>• Found solutions to complex hardware/firmware issues and ensured systems were up and running efficiently.</li>
<li>• Maintained positive professional relations with Dell's corporate, government, and consumer clients.</li>
<li>• Exceeded ticket completion standards handling all tickets in the Falls Church - Arlington area.</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Lead Repair Technician</h3>
<span class="experience__company">2021 to 2022 | T-Mobile</span>
<ul class="experience__description">
<li>• Diagnosed and repaired cellphones, tablets, laptops, and other mobile devices.</li>
<li>• Efficiently diagnosed issues with customers' devices and performed all necessary repairs.</li>
<li>• Handled inventory and parts returns while ensuring systems were up to date.</li>
<li>• Exceeded SLA expectations completing 90% of repairs in under 90 minutes.</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Lead Technician</h3>
<span class="experience__company">2021 | Red Door Pro Wash</span>
<ul class="experience__description">
<li>• Communicated with customers to perform soft wash services efficiently.</li>
<li>• Created job plans for the team prior to arrival.</li>
<li>• Maintained and drove the company vehicle.</li>
</ul>
</div>
</div>
<div class="experience__content">
<div class="experience__time"><span class="experience__rounder"></span><span class="experience__line"></span></div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Night Shift Manager</h3>
<span class="experience__company">2020 to 2021 | Taco Bell</span>
<ul class="experience__description">
<li>• Managed up to 10 staff during the night shift.</li>
<li>• Trained new employees and handled customer issues.</li>
<li>• Processed refunds, tracked inventory, and kept cash registers balanced.</li>
</ul>
</div>
</div>
<a href="/cv/" class="back__home">
<i class="bx bx-arrow-back back__icon"></i> Back to Home
</a>
</div>
</section>
</div>
</div>
</main>
<a href="#" class="scrolltop" id="scroll-top">
<i class="bx bx-up-arrow-alt scrolltop__icon"></i>
</a>
</div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />
<script is:inline src="/cv/js/html2pdf.bundle.min.js"></script>
<script>
// Menu toggle
const nav = document.getElementById('nav-menu');
if (toggle && nav) {
toggle.addEventListener('click', () => nav.classList.toggle('show-menu'));
}
document.querySelectorAll('.nav__link').forEach(l => {
l.addEventListener('click', () => nav?.classList.remove('show-menu'));
});
const sections = document.querySelectorAll('section[id]');
window.addEventListener('scroll', () => {
const scrollY = window.pageYOffset;
sections.forEach(current => {
const h = current.offsetHeight;
const top = current.offsetTop - 50;
const id = current.getAttribute('id');
const link = document.querySelector(`.nav__menu a[href*="${id}"]`);
if (link) {
if (scrollY > top && scrollY <= top + h) link.classList.add('active-link');
else link.classList.remove('active-link');
}
});
const scrollTopEl = document.getElementById('scroll-top');
if (scrollTopEl) {
if (scrollY >= 200) scrollTopEl.classList.add('show-scroll');
else scrollTopEl.classList.remove('show-scroll');
}
});
const themeButton = document.getElementById('theme-button');
if (themeButton) {
const darkTheme = 'dark-theme';
const iconTheme = 'bx-sun';
const selectedTheme = localStorage.getItem('selected-theme');
const selectedIcon = localStorage.getItem('selected-icon');
if (selectedTheme) {
document.body.classList[selectedTheme === 'dark' ? 'add' : 'remove'](darkTheme);
themeButton.classList[selectedIcon === 'bx-moon' ? 'add' : 'remove'](iconTheme);
}
themeButton.addEventListener('click', () => {
document.body.classList.toggle(darkTheme);
themeButton.classList.toggle(iconTheme);
localStorage.setItem('selected-theme', document.body.classList.contains(darkTheme) ? 'dark' : 'light');
localStorage.setItem('selected-icon', themeButton.classList.contains(iconTheme) ? 'bx-moon' : 'bx-sun');
});
}
const resumeButton = document.getElementById('resume-button');
if (resumeButton) {
resumeButton.addEventListener('click', () => {
document.body.classList.add('scale-cv');
const areaCv = document.getElementById('area-cv');
// @ts-ignore
html2pdf(areaCv, {
margin: 0, filename: 'DanGrubbResume.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { format: 'a4', orientation: 'portrait' },
});
setTimeout(() => document.body.classList.remove('scale-cv'), 5000);
});
}
</script>
</Base>
<style is:global>
@import '../../styles/cv.css';
</style>

View File

@ -1,182 +1,154 @@
---
import CrushBase from '../layouts/CrushBase.astro';
import Base from '../layouts/Base.astro';
import GlitchBard from '../components/GlitchBard.astro';
import posts from '../data/posts.json';
const latest = posts[0];
const formattedDate = new Date(latest.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
---
<CrushBase title="dangrubb.net">
<div class="crush-page">
<header class="top-banner">
<div class="brand">
<span class="brand-name">dangrubb</span><span class="brand-dot">.</span><span class="brand-tld">net</span>
</div>
<p class="tagline">OFFICIAL SITE</p>
</header>
<Base title="dangrubb.net">
<div class="page">
<GlitchBard />
<main class="logo-section">
<img src="/src/img/pack-splatter.png" alt="" class="splatter-img">
<div class="logo-center">
<img src="/src/img/DanGrubbLogoTeal.png" alt="DanGrubb" class="logo-img">
</div>
</main>
<p class="site-title">dangrubb.net</p>
<footer class="bottom-nav">
<a href="https://ai.dangrubb.net" class="nav-item">AI</a>
<span class="nav-dot">•</span>
<a href="/blog" class="nav-item">BLOG</a>
<span class="nav-dot">•</span>
<a href="/music" class="nav-item">MUSIC</a>
<span class="nav-dot">•</span>
<a href="https://pi.dangrubb.net/jellyfin" class="nav-item">MEDIA</a>
<span class="nav-dot">•</span>
<a href="https://pi.dangrubb.net/nextcloud" class="nav-item">STORAGE</a>
<span class="nav-dot">•</span>
<a href="/cv" class="nav-item">CV</a>
<span class="nav-dot">•</span>
<a href="https://pi.dangrubb.net/dangit" class="nav-item">GIT</a>
</footer>
<p class="announcement">
<a href="/music">EXCLUSIVE: Leaked mixtape from Darnea's vault</a>
</p>
<div class="blog-preview-container">
<div class="blog-preview">
<a href={`/blog/#${latest.slug}`} class="blog-preview-title">{latest.title}</a>
<div class="blog-preview-date">{formattedDate}</div>
<img src={latest.image} alt={latest.title} />
<p class="blog-preview-excerpt">{latest.excerpt}</p>
<a href={`/blog/#${latest.slug}`} class="blog-preview-link">Read more →</a>
</div>
</div>
<nav class="bottom-nav">
<a target="_parent" href="https://ai.dangrubb.net">ai</a>
<a target="_parent" href="/blog">blog</a>
<a target="_parent" href="https://pi.dangrubb.net/jellyfin">media</a>
<a target="_parent" href="https://pi.dangrubb.net/nextcloud">storage</a>
<a target="_parent" href="/cv">cv</a>
<a target="_parent" href="https://pi.dangrubb.net/dangit">git</a>
</nav>
</div>
</CrushBase>
</Base>
<style>
.crush-page {
@import '../styles/bard.css';
.page {
padding: 20px 20px 100px;
min-height: 100vh;
display: flex;
flex-direction: column;
height: 100vh;
height: 100dvh;
margin: 0;
background: var(--crush-white);
box-sizing: border-box;
}
.top-banner {
.site-title {
margin: 10px auto 0;
max-width: 400px;
text-align: center;
padding: 16px 20px 10px;
background: linear-gradient(180deg, #e8f5f5 0%, transparent 100%);
border-bottom: 3px solid var(--crush-teal);
flex-shrink: 0;
line-height: 1.3;
font-size: 0.875rem;
}
.brand {
font-family: 'Alfa Slab One', 'Georgia', serif;
font-size: clamp(3rem, 10vw, 5rem);
color: var(--crush-teal);
letter-spacing: -2px;
line-height: 1;
.announcement {
margin: 20px auto 0;
display: inline-block;
border: 1px solid #ffff00;
padding: 10px 15px;
border-radius: 3px;
font-size: 0.875rem;
text-align: center;
}
.brand-dot { color: var(--crush-teal-light); }
.brand-tld { color: var(--crush-teal-dark); }
.tagline {
font-size: 14px;
font-weight: 700;
letter-spacing: 4px;
color: var(--crush-teal);
margin-top: 8px;
text-transform: uppercase;
opacity: 0.8;
.announcement a {
color: #ffff00;
font-weight: bold;
}
.announcement a:hover { text-decoration: underline; }
.logo-section {
flex: 1;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
min-height: 0;
}
.splatter-img {
position: absolute;
inset: 0;
.blog-preview-container {
margin: 30px auto 0;
width: 100%;
height: 100%;
object-fit: contain;
pointer-events: none;
}
.logo-center {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
.logo-img {
width: 12%;
max-width: 160px;
aspect-ratio: 1;
object-fit: contain;
filter: drop-shadow(0 3px 8px rgba(0, 102, 102, 0.3));
.blog-preview {
border: 1px solid rgba(155, 169, 180, 0.3);
border-radius: 4px;
padding: 15px;
background: linear-gradient(135deg, rgba(13, 10, 20, 0.6), rgba(50, 30, 80, 0.3));
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
max-width: 350px;
width: 100%;
}
.blog-preview img {
width: 100%;
height: auto;
max-height: 150px;
object-fit: cover;
border-radius: 3px;
margin: 10px 0;
}
.blog-preview-title {
color: #ffff00;
font-weight: bold;
font-size: 0.95rem;
margin: 10px 0 5px;
display: block;
}
.blog-preview-title:hover { text-decoration: underline; }
.blog-preview-date {
color: #aa95bd;
font-size: 0.8rem;
margin-bottom: 8px;
}
.blog-preview-excerpt {
color: #9d9aa4;
font-size: 0.85rem;
line-height: 1.4;
margin: 8px 0 10px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.blog-preview-link {
display: inline-block;
color: #6be1e9;
font-size: 0.8rem;
}
.blog-preview-link:hover { text-decoration: underline; }
.bottom-nav {
margin-top: auto;
width: 100%;
text-align: center;
padding: 20px 10px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 8px;
padding: 20px 20px 28px;
background: linear-gradient(0deg, #e8f5f5 0%, transparent 100%);
border-top: 3px solid var(--crush-teal);
flex-shrink: 0;
gap: 8px 20px;
}
.bottom-nav a {
color: #6be1e9;
font-size: 0.875rem;
}
.nav-item {
font-size: 18px;
font-weight: 700;
color: var(--crush-teal);
letter-spacing: 2px;
padding: 8px 16px;
border-radius: 4px;
background: rgba(0, 139, 139, 0.05);
transition: all 0.3s ease;
@media (max-width: 768px) {
.page { padding: 20px 10px 80px; }
.site-title { font-size: 0.8rem; max-width: 320px; }
.blog-preview { max-width: 300px; }
}
.nav-item:hover {
background: rgba(0, 139, 139, 0.15);
color: var(--crush-teal-dark);
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0, 139, 139, 0.2);
}
.nav-dot {
font-size: 20px;
color: var(--crush-teal);
opacity: 0.5;
}
@media (max-width: 480px) {
.brand { font-size: 2.5rem; }
.top-banner {
flex: 1;
flex-shrink: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 12px 15px;
}
.logo-section {
aspect-ratio: 3 / 4;
flex: none;
}
.splatter-img {
object-fit: cover;
}
.logo-img { width: 36%; }
.nav-item { font-size: 16px; padding: 8px 14px; }
.bottom-nav {
flex: 1;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 3px;
padding: 4px 12px;
}
.nav-dot { display: none; }
@media (max-width: 400px) {
.site-title { font-size: 0.75rem; }
.blog-preview-container { margin-top: 20px; }
}
</style>

386
style.css Normal file

File diff suppressed because one or more lines are too long