14 Commits

Author SHA1 Message Date
7796792517 checkpoint: CV restored to original standalone files (bypass Astro) 2026-05-31 09:13:52 -04:00
b3eabbd98f checkpoint: blog rethemed to crush (teal logo, white bg, teal accents) 2026-05-31 09:01:51 -04:00
b38cd8ba52 checkpoint: music link, mobile nav 2-row layout, logo scaling fixes 2026-05-31 08:56:19 -04:00
c264a2e979 checkpoint: splatter layout with centered logo, one-viewport fit, mobile nav rows 2026-05-31 08:33:11 -04:00
c4823cf2be feat: better splatter - no camel branding 2026-05-30 14:35:44 -04:00
55032bf6b3 feat: use isolated pack splatter with logo overlay 2026-05-30 14:31:27 -04:00
0ce09fc35e feat: extract splatter from actual Camel Crush pack 2026-05-30 14:28:02 -04:00
498dc4f7a3 feat: use generated splatter image instead of hand-coded SVG 2026-05-30 14:24:48 -04:00
8f5b831cc2 feat: bigger dramatic splatters with SVG turbulence filter 2026-05-30 14:14:15 -04:00
1f7df496fe feat: grungy splatter ring (SVG-based irregular spray marks) 2026-05-30 14:09:32 -04:00
7b0a9ea9ce feat: use actual logo in circle, remove menthol text 2026-05-30 14:04:22 -04:00
20fe2a0962 feat: camel crush themed landing page (crush branch)
- New CrushBase.astro layout with teal/silver color palette
- Redesigned index.astro with Camel Crush pack-inspired branding:
  - Alfa Slab One font header (dangrubb.net)
  - Center circular DG monogram with teal gradient (like Joe Camel)
  - Animated splatter background effect
  - 'Hint of Menthol • More Menthol' tagline
  - Bottom nav links styled like pack text (ai•blog•media•storage•cv•git)
2026-05-30 14:00:02 -04:00
9c18b60e04 Fix CV font: use dedicated CVBase layout instead of main site layout
The main site Base layout sets a dark monospace font that overrides
the CV's Poppins font. CV pages now use CVBase which has no global
font overrides.
2026-04-23 21:52:45 -04:00
44b3b2a30d Move music to public/ for Astro static serving 2026-04-23 21:49:08 -04:00
72 changed files with 320 additions and 2229 deletions

1
.gitignore vendored
View File

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

View File

@ -1,254 +0,0 @@
<!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>

View File

@ -1,11 +0,0 @@
[
{
"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"
}
]

View File

@ -1,53 +0,0 @@
"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.

Before

Width:  |  Height:  |  Size: 5.0 MiB

File diff suppressed because one or more lines are too long

View File

@ -1,39 +0,0 @@
<!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.

Before

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,272 +0,0 @@
<!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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

166
script.js
View File

@ -1,166 +0,0 @@
"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();
}

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.

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.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

17
src/layouts/CVBase.astro Normal file
View File

@ -0,0 +1,17 @@
---
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

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

View File

@ -1,324 +0,0 @@
---
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

@ -1,275 +0,0 @@
---
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,154 +1,182 @@
--- ---
import Base from '../layouts/Base.astro'; import CrushBase from '../layouts/CrushBase.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' });
--- ---
<Base title="dangrubb.net"> <CrushBase title="dangrubb.net">
<div class="page"> <div class="crush-page">
<GlitchBard /> <header class="top-banner">
<div class="brand">
<p class="site-title">dangrubb.net</p> <span class="brand-name">dangrubb</span><span class="brand-dot">.</span><span class="brand-tld">net</span>
<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> </div>
<p class="tagline">OFFICIAL SITE</p>
</header>
<nav class="bottom-nav"> <main class="logo-section">
<a target="_parent" href="https://ai.dangrubb.net">ai</a> <img src="/src/img/pack-splatter.png" alt="" class="splatter-img">
<a target="_parent" href="/blog">blog</a> <div class="logo-center">
<a target="_parent" href="https://pi.dangrubb.net/jellyfin">media</a> <img src="/src/img/DanGrubbLogoTeal.png" alt="DanGrubb" class="logo-img">
<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> </div>
</Base> </main>
<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>
</div>
</CrushBase>
<style> <style>
@import '../styles/bard.css'; .crush-page {
.page {
padding: 20px 20px 100px;
min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; height: 100vh;
height: 100dvh;
margin: 0;
background: var(--crush-white);
} }
.site-title { .top-banner {
margin: 10px auto 0;
max-width: 400px;
text-align: center; text-align: center;
line-height: 1.3; padding: 16px 20px 10px;
font-size: 0.875rem; background: linear-gradient(180deg, #e8f5f5 0%, transparent 100%);
border-bottom: 3px solid var(--crush-teal);
flex-shrink: 0;
} }
.announcement { .brand {
margin: 20px auto 0; font-family: 'Alfa Slab One', 'Georgia', serif;
display: inline-block; font-size: clamp(3rem, 10vw, 5rem);
border: 1px solid #ffff00; color: var(--crush-teal);
padding: 10px 15px; letter-spacing: -2px;
border-radius: 3px; line-height: 1;
font-size: 0.875rem;
text-align: center;
} }
.announcement a {
color: #ffff00;
font-weight: bold;
}
.announcement a:hover { text-decoration: underline; }
.blog-preview-container { .brand-dot { color: var(--crush-teal-light); }
margin: 30px auto 0; .brand-tld { color: var(--crush-teal-dark); }
width: 100%;
.tagline {
font-size: 14px;
font-weight: 700;
letter-spacing: 4px;
color: var(--crush-teal);
margin-top: 8px;
text-transform: uppercase;
opacity: 0.8;
}
.logo-section {
flex: 1;
position: relative;
display: flex; display: flex;
align-items: center;
justify-content: center; justify-content: center;
overflow: hidden;
min-height: 0;
} }
.blog-preview { .splatter-img {
border: 1px solid rgba(155, 169, 180, 0.3); position: absolute;
border-radius: 4px; inset: 0;
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%; width: 100%;
height: 100%;
object-fit: contain;
pointer-events: none;
} }
.blog-preview img {
width: 100%; .logo-center {
height: auto; position: absolute;
max-height: 150px; inset: 0;
object-fit: cover; display: flex;
border-radius: 3px; align-items: center;
margin: 10px 0; justify-content: center;
z-index: 2;
} }
.blog-preview-title {
color: #ffff00; .logo-img {
font-weight: bold; width: 12%;
font-size: 0.95rem; max-width: 160px;
margin: 10px 0 5px; aspect-ratio: 1;
display: block; object-fit: contain;
filter: drop-shadow(0 3px 8px rgba(0, 102, 102, 0.3));
} }
.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 { .bottom-nav {
margin-top: auto;
width: 100%;
text-align: center;
padding: 20px 10px;
display: flex; display: flex;
align-items: center;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px 20px; gap: 8px;
} padding: 20px 20px 28px;
.bottom-nav a { background: linear-gradient(0deg, #e8f5f5 0%, transparent 100%);
color: #6be1e9; border-top: 3px solid var(--crush-teal);
font-size: 0.875rem; flex-shrink: 0;
} }
@media (max-width: 768px) { .nav-item {
.page { padding: 20px 10px 80px; } font-size: 18px;
.site-title { font-size: 0.8rem; max-width: 320px; } font-weight: 700;
.blog-preview { max-width: 300px; } 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: 400px) {
.site-title { font-size: 0.75rem; } .nav-item:hover {
.blog-preview-container { margin-top: 20px; } 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; }
} }
</style> </style>

386
style.css

File diff suppressed because one or more lines are too long