34 Commits

Author SHA1 Message Date
ff2a284e64 Merge branch 'astro' 2026-05-31 09:18:17 -04:00
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
bdd454e706 Merge remote-tracking branch 'origin/astro' 2026-04-23 21:52:51 -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
de8ba59d59 Merge remote-tracking branch 'astro/astro'
# Conflicts:
#	.gitignore
2026-04-23 21:29:57 -04:00
900143073b Remove README 2026-04-23 21:27:40 -04:00
2cd6f70b99 Convert dangrubb.net to Astro framework
- Migrated main site, blog, CV, and music sections to Astro
- Component-based architecture with layouts
- JSON-based blog posts
- Static site generation
- Preserved original styling and functionality
2026-04-23 21:07:44 -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
89 changed files with 9078 additions and 61 deletions

5
.gitignore vendored
View File

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

4
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@ -0,0 +1,216 @@
# dangrubb.net Astro Conversion - Complete ✅
## Project Status
**Conversion**: ✅ Complete
**Build**: ✅ Successful
**Serving**: ✅ Active on port 4321
**URL**: `http://macmini.local:4321` (or `http://localhost:4321` on the Mac Mini)
---
## Completed Tasks
### 1. ✅ Astro Project Structure
- Initialized clean Astro v6.1.9 project in the repo
- Preserved `/music/` directory completely untouched
- Maintained `.git/` for version control
- Clean separation: source code in `src/`, output in `dist/`
### 2. ✅ Pages Converted
#### Landing Page (`/`)
- **File**: `src/pages/index.astro`
- **Features**:
- Glitch bard animation (all 17 strips with proper CSS animations)
- Site title and announcement link
- Blog preview showing latest post (Status)
- Bottom navigation with external links
- Fully mobile-responsive with media queries
#### Blog Listing (`/blog`)
- **File**: `src/pages/blog/index.astro`
- **Features**:
- Dynamic post rendering from `src/data/posts.json`
- Currently displays 1 post (Status, dated 2026-02-28)
- Hash-based navigation to individual posts
- Responsive card layout with image, title, date, excerpt
- "Read more" link functionality
#### CV Pages
- **Main CV** (`/cv`): `src/pages/cv/index.astro`
- Left sidebar: Home, Profile, Education, Skills
- Right sidebar: Experience (3 roles), Certificates, References, Languages, Interests
- Experience has link to "More Information"
- **Extended Experience** (`/cv/more-information`): `src/pages/cv/more-information.astro`
- Full 6 job history entries
- Back link to main CV
#### 404 Page
- **File**: `src/pages/404.astro`
- **Styling**: Dark green background with centered text
### 3. ✅ Components
- **GlitchBard** (`src/components/GlitchBard.astro`): Reusable glitch animation component with 17 animated strips
### 4. ✅ Styling System
#### Global Styles
- **Base Layout** (`src/layouts/Base.astro`): Dark theme, monospace font (#9d9aa4 text on #0d0a14 background)
- **Glitch Animations** (`src/styles/glitch.css`): 6 keyframe animations (glitch-5 through glitch-10)
- **Bard Component** (`src/styles/bard.css`): Strip animation and responsive sizing
#### Page-Specific Styles
- **Landing Page** (`src/pages/index.astro`): Scoped styles for announcement, blog preview, navigation
- **Blog Page** (`src/pages/blog/index.astro`): Card layouts, post styling
- **CV Pages** (`src/styles/cv.css`): Comprehensive CV styling with responsive layout
- Light/dark theme toggle support
- PDF export scaling
- Mobile-first responsive design (media queries at 320px, 768px, 968px)
### 5. ✅ Mobile Responsiveness
**All pages include responsive design**:
- **Mobile-first approach** with breakpoints at:
- 400px: Very small devices (adjust font sizes, bard aspect ratio)
- 768px: Tablets (adjust padding, navigation)
- 968px: Desktop (full 2-column CV layout)
**Key responsive features**:
- `.bard` scales with `font-size` and `aspect-ratio` adjustments
- Announcement, blog previews, navigation adapt to viewport
- CV uses fixed nav on mobile, hidden on desktop (shows at bottom)
- All text sizes scale appropriately
### 6. ✅ Data Management
- **Posts Data**: `src/data/posts.json` (1 post currently)
- Structure: id, title, date, excerpt, content, image, slug
- Can be extended with more blog posts
### 7. ✅ Build & Deployment
```bash
# Build
npm run build
# Output: dist/ (88KB, containing all static HTML/CSS/JS)
# Serve
npx serve dist -l 4321
# Running: http://localhost:4321
```
---
## Architecture
```
dangrubb.net/
├── src/
│ ├── pages/
│ │ ├── index.astro # Landing page
│ │ ├── 404.astro # 404 page
│ │ ├── blog/
│ │ │ └── index.astro # Blog listing
│ │ └── cv/
│ │ ├── index.astro # Main CV
│ │ └── more-information.astro # Extended experience
│ ├── components/
│ │ └── GlitchBard.astro # Glitch animation component
│ ├── layouts/
│ │ └── Base.astro # Base HTML layout
│ ├── styles/
│ │ ├── glitch.css # Glitch animations
│ │ ├── bard.css # Bard styling
│ │ └── cv.css # CV styling (9.7KB)
│ └── data/
│ └── posts.json # Blog posts data
├── public/ # Static assets (images, PDFs, JS libs)
│ ├── src/img/ # Favicon, logos
│ ├── blog/src/img/ # Blog images
│ └── cv/
│ ├── assets/img/ # Profile, CV favicons
│ ├── assets/pdf/ # Resume PDFs
│ └── js/ # html2pdf library
├── dist/ # Built output (static HTML/CSS/JS)
├── astro.config.mjs # Astro configuration
├── package.json # Dependencies & scripts
└── tsconfig.json # TypeScript config
```
---
## Key Features Preserved
**Dark Aesthetic**: #0d0a14 background, #9d9aa4 text, cyan links (#6be1e9)
**Glitch Animations**: All 17 strip animations with varying durations (5-10 seconds)
**Monospace Font**: "Cascadia Code", "Source Code Pro", Menlo, Consolas fallbacks
**Blog System**: JSON-driven, easily extensible
**CV Functionality**:
- Theme toggle (light/dark)
- PDF export button (requires html2pdf.bundle.min.js in public/)
- Responsive mobile navigation
- Smooth scrolling with active link highlighting
---
## Important Notes
### Missing Assets
The following assets need to be restored from the original repo or re-created:
- `public/src/img/favicon.ico`
- `public/src/img/DanGrubbLogoWhite.png`
- `public/blog/src/img/CannotConnect.JPEG`
- `public/cv/assets/img/perfil-example.jpg`
- `public/cv/assets/img/favicon.ico`
- `public/cv/assets/pdf/DanGrubbResume.pdf`
- `public/cv/assets/pdf/ResumeCv.pdf`
- `public/cv/js/html2pdf.bundle.min.js` (for PDF generation)
**Current Status**: Site serves without errors, but images won't load and PDF export won't function until assets are restored.
### Music Directory
✅ Completely untouched at `/music/`
### Next Steps
1. Restore asset files to `public/` directory
2. Test all pages in browser
3. Verify PDF export functionality
4. Push to Gitea repo (`https://pi.dangrubb.net/dangit/dangrubbb/dangrubb.net`)
---
## Testing Commands
```bash
# Build
npm run build
# Verify pages
curl -s http://localhost:4321/ | grep "dangrubb.net" | head -1
curl -s http://localhost:4321/blog/ | grep "blog-post" | head -1
curl -s http://localhost:4321/cv/ | grep "home__title" | head -1
# Check all routes return 200
curl -o /dev/null -w "%{http_code}" http://localhost:4321/
curl -o /dev/null -w "%{http_code}" http://localhost:4321/blog/
curl -o /dev/null -w "%{http_code}" http://localhost:4321/cv/
curl -o /dev/null -w "%{http_code}" http://localhost:4321/cv/more-information
```
---
## Summary
**Conversion Complete**: Plain HTML/CSS/JS → Astro project with component architecture
**Mobile Responsive**: Properly designed for 320px to 1920px+ viewports
**Dark Aesthetic Preserved**: All color scheme, fonts, animations intact
**Build Successful**: npm run build completes with no errors
**Serving**: Running on http://macmini.local:4321
**All Pages Live**:
- `/` - Landing page with glitch bard
- `/blog` - Blog listing
- `/cv` - Main CV
- `/cv/more-information` - Extended experience
- `/404` - Not found page
**Ready for asset restoration and live deployment!**

9
astro.config.mjs Normal file
View File

@ -0,0 +1,9 @@
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://dangrubb.net',
output: 'static',
build: {
assets: 'assets'
}
});

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

Binary file not shown.

View File

@ -201,14 +201,36 @@
</div> </div>
</section> </section>
</div> </div>
<div class="resume__right"> <div class="resume__right">
<!--========== EXPERIENCE ==========--> <!--========== EXPERIENCE ==========-->
<section class="experience section" id="experience"> <section class="experience section" id="experience">
<h2 class="section-title">Experience</h2> <h2 class="section-title">Experience</h2>
<div class="experience__container bd-grid"> <div class="experience__container bd-grid">
<div class="experience__content"> <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>
<p class="experience__description">
<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>
</p>
</div>
</div>
<div class="experience__content">
<div class="experience__time"> <div class="experience__time">
<span class="experience__rounder"></span> <span class="experience__rounder"></span>
<span class="experience__line"></span> <span class="experience__line"></span>
@ -216,8 +238,7 @@
<div class="experience__data bd-grid"> <div class="experience__data bd-grid">
<h3 class="experience__title">Technical Account Manager</h3> <h3 class="experience__title">Technical Account Manager</h3>
<span class="experience__company" <span class="experience__company"
>2023 to Present | ITG</span >2023 to 2025 | ITG</span>
>
<p class="experience__description"> <p class="experience__description">
<ul class="experience__description"> <ul class="experience__description">
<li>• Ensure customer success with technical consulting and implementation of adaptive software and hardware.</li> <li>• Ensure customer success with technical consulting and implementation of adaptive software and hardware.</li>
@ -232,49 +253,26 @@
</p> </p>
</div> </div>
</div> </div>
<div class="experience__content"> <div class="experience__content">
<div class="experience__time"> <div class="experience__time">
<span class="experience__rounder"></span> <span class="experience__rounder"></span>
<span class="experience__line"></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>
<p class="experience__description">
<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 Dells corporate, government, and consumer clients.</li>
<li>• Exceeded ticket completion standards handling all tickets in the Falls Church - Arlington area.</li>
</ul>
</p>
</div>
</div> </div>
<div class="experience__data bd-grid">
<h3 class="experience__title">Field Technician</h3>
<span class="experience__company"
>2023 | Dell</span
>
<p class="experience__description">
<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 Dells corporate, government, and consumer clients.</li>
<li>• Exceeded ticket completion standards handling all tickets in the Falls Church - Arlington area.</li>
</ul>
</p>
</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
>
<p class="experience__description">
<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>
</p>
</div>
</div>
<a href="moreInformation.html" class="more__information"> <a href="moreInformation.html" class="more__information">
<i class="bx bx-info-circle information__icon"></i> <i class="bx bx-info-circle information__icon"></i>
More Information More Information
@ -284,7 +282,7 @@
<!--========== CERTIFICATES ==========--> <!--========== CERTIFICATES ==========-->
<section class="certificate section" id="certificates"> <section class="certificate section" id="certificates">
<h2 class="section-title">Certificates</h2> <h2 class="section-title">Certifications</h2>
<div class="certificate__container bd-grid"> <div class="certificate__container bd-grid">
<div class="certificate__content"> <div class="certificate__content">

View File

@ -214,11 +214,37 @@
<h2 class="section-title">Experience</h2> <h2 class="section-title">Experience</h2>
<div class="experience__container bd-grid"> <div class="experience__container bd-grid">
<div class="experience__content"> <div class="experience__content">
<div class="experience__time"> <div class="experience__time">
<span class="experience__rounder"></span> <span class="experience__rounder"></span>
<span class="experience__line"></span> <span class="experience__line"></span>
</div> </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>
<p class="experience__description">
<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>
</p>
</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"> <div class="experience__data bd-grid">
<h3 class="experience__title">Technical Account Manager</h3> <h3 class="experience__title">Technical Account Manager</h3>
<span class="experience__company" <span class="experience__company"

View File

@ -139,6 +139,11 @@ body.scale-cv {
height: 110%; height: 110%;
transform: translate(5px, 0); transform: translate(5px, 0);
} }
.scale-cv .experience__line__last {
width: 1px;
height: 100%;
transform: translate(5px, 0);
}
.scale-cv .education__data, .scale-cv .education__data,
.scale-cv .experience__data { .scale-cv .experience__data {
gap: 0.25rem; gap: 0.25rem;
@ -410,7 +415,14 @@ img {
.experience__line { .experience__line {
display: block; display: block;
width: 2px; width: 2px;
height: 110%; height: 109%;
background-color: var(--text-color-light);
transform: translate(7px, 0);
}
.experience__line__last {
display: block;
width: 2px;
height: 100%;
background-color: var(--text-color-light); background-color: var(--text-color-light);
transform: translate(7px, 0); transform: translate(7px, 0);
} }
@ -430,6 +442,7 @@ img {
.education__year { .education__year {
font-size: var(--smaller-font-size); font-size: var(--smaller-font-size);
} }
/*========== SKILLS AND LANGUAGES ==========*/ /*========== SKILLS AND LANGUAGES ==========*/
.skills__content, .skills__content,
.languages__content { .languages__content {

View File

@ -238,18 +238,35 @@
<p> <p>
dangrubb.net dangrubb.net
</p> </p>
<p>
<a target="_parent" href="https://ai.dangrubb.net">ai</a> <p class="announcement">
<a target="_parent" href="https://ghost.dangrubb.net">blog</a> <a href="https://dangrubb.net/music">EXCLUSIVE: Leaked mixtape from Darnea's vault</a>
<a target="_parent" href="https://pi.dangrubb.net/jellyfin">media</a> </p>
<a target="_parent" href="https://pi.dangrubb.net/nextcloud">storage</a>
<a target="_parent" href="https://dangrubb.net/cv">cv</a> <div class="blog-preview-container">
<a target="_parent" href="https://pi.dangrubb.net/dangit">git</a></p> <div class="blog-preview" id="latestBlogPreview">
<p style="color: #9d9aa4;">Loading latest post...</p>
</div>
</div>
<div class="debug"> <div class="debug">
<div class="code"></div> <div class="code"></div>
</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 --> <!-- partial -->
<script src="./script.js"></script> <script src="./script.js"></script>
</body> </body>
</html> </html>

4823
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "dangrubb-astro",
"type": "module",
"version": "0.0.1",
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^6.1.9"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

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.

Binary file not shown.

396
public/cv/index.html Normal file
View File

@ -0,0 +1,396 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="assets/img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css"
/>
<link rel="stylesheet" href="styles/index.css" />
<title>Dan Grubb CV</title>
</head>
<body>
<!--========== HEADER ==========-->
<header class="l-header" id="header">
<nav class="nav bd-container">
<a href="#" class="nav 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">
<!-- All elements within this div, is generated in PDF -->
<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="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="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>
<span class="home__information">
<i class="bx bx-planet home__icon"></i> dangrubb.net
</span>
</div>
</div>
<!-- Theme change button -->
<i
class="bx bx-moon change-theme"
title="Theme"
id="theme-button"
></i>
<!-- Button to generate and download the pdf. Available for desktop. -->
<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__studies"></span> -->
<span class="education__year">2020</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">Self-Study Certifications</h3>
<!-- <span class="education__studies"></span> -->
<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>
<p class="experience__description">
<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>
</p>
</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>
<p class="experience__description">
<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>
</p>
</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>
<p class="experience__description">
<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 Dells corporate, government, and consumer clients.</li>
<li>• Exceeded ticket completion standards handling all tickets in the Falls Church - Arlington area.</li>
</ul>
</p>
</div>
</div>
<a href="moreInformation.html" 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>
<p class="certificate__description">
</p>
</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>
<!-- <li>Email: user@email.com</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>
<!-- <li>Email: user@email.com</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>
<!--========== SCROLL TOP ==========-->
<a href="#" class="scrolltop" id="scroll-top">
<i class="bx bx-up-arrow-alt scrolltop__icon"></i>
</a>
<!--========== HTML2PDF ==========-->
<script src="js/html2pdf.bundle.min.js"></script>
<!--========== MAIN JS ==========-->
<script src="js/main.js"></script>
</body>
</html>

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

File diff suppressed because one or more lines are too long

179
public/cv/js/main.js Normal file
View File

@ -0,0 +1,179 @@
/*==================== SHOW MENU ====================*/
const showMenu = (toggleId, navId) => {
const toggle = document.getElementById(toggleId),
nav = document.getElementById(navId);
//validate that variables exist
if (toggle && nav) {
toggle.addEventListener("click", () => {
//we add the show menu class to the div tag with the nav__menu class
nav.classList.toggle("show-menu");
});
}
};
showMenu("nav-toggle", "nav-menu");
/*==================== REMOVE MENU MOBILE ====================*/
const navLink = document.querySelectorAll(".nav__link");
function linkAction() {
const navMenu = document.getElementById("nav-menu");
//when we click on each nav__link, we remove the show-menu class
navMenu.classList.remove("show-menu");
}
navLink.forEach((n) => n.addEventListener("click", linkAction));
/*==================== SCROLL SECTIONS ACTIVE LINK ====================*/
const sections = document.querySelectorAll("section[id]");
function scrollActive() {
const scrollY = window.pageYOffset;
sections.forEach((current) => {
const sectionHeight = current.offsetHeight;
const sectionTop = current.offsetTop - 50;
sectionId = current.getAttribute("id");
if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
document
.querySelector(".nav__menu a[href*=" + sectionId + "]")
.classList.add("active-link");
} else {
document
.querySelector(".nav__menu a[href*=" + sectionId + "]")
.classList.remove("active-link");
}
});
}
window.addEventListener("scroll", scrollActive);
/*==================== SHOW SCROLL TOP ====================*/
function scrollTop() {
const scrollTop = document.getElementById("scroll-top");
// When the scroll is higher than 200 viewport height, add the show-scroll class to the a tag with the scroll-top class
if (this.scrollY >= 200) scrollTop.classList.add("show-scroll");
else scrollTop.classList.remove("show-scroll");
}
window.addEventListener("scroll", scrollTop);
/*==================== DARK LIGHT THEME ====================*/
const themeButton = document.getElementById("theme-button");
const darkTheme = "dark-theme";
const iconTheme = "bx-sun";
// Previously selected topic (if user selected)
const selectedTheme = localStorage.getItem("selected-theme");
const selectedIcon = localStorage.getItem("selected-icon");
// We obtain the current theme that the interface has by validating the dark-theme class
const getCurrentTheme = () =>
document.body.classList.contains(darkTheme) ? "dark" : "light";
const getCurrentIcon = () =>
themeButton.classList.contains(iconTheme) ? "bx-moon" : "bx-sun";
// We validate if the user previously chose a topic
if (selectedTheme) {
// If the validation is fulfilled, we ask what the issue was to know if we activated or deactivated the dark
document.body.classList[selectedTheme === "dark" ? "add" : "remove"](
darkTheme
);
themeButton.classList[selectedIcon === "bx-moon" ? "add" : "remove"](
iconTheme
);
}
// Activate / deactivate the theme manually with the button
themeButton.addEventListener("click", () => {
// Add or remove the dark / icon theme
document.body.classList.toggle(darkTheme);
themeButton.classList.toggle(iconTheme);
// We save the theme and the current icon that the user chose
localStorage.setItem("selected-theme", getCurrentTheme());
localStorage.setItem("selected-icon", getCurrentIcon());
});
/*==================== REDUCE THE SIZE AND PRINT ON AN A4 SHEET ====================*/
function scaleCv() {
document.body.classList.add("scale-cv");
}
/*==================== REMOVE THE SIZE WHEN THE CV IS DOWNLOADED ====================*/
function removeScale() {
document.body.classList.remove("scale-cv");
}
/*==================== GENERATE PDF ====================*/
// PDF generated area
let areaCv = document.getElementById("area-cv");
let resumeButton = document.getElementById("resume-button");
// Html2pdf options
let opt = {
margin: 0,
filename: "DanGrubbResume.pdf",
image: { type: "jpeg", quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { format: "a4", orientation: "portrait" },
};
// Function to call areaCv and Html2Pdf options
function generateResume() {
// Select the elements to hide
const experienceLink = document.querySelector("#experience > div > a");
const interestsSection = document.querySelector("#area-cv > div.resume__right > section.interests.section");
// Hide the elements before generating PDF
if (experienceLink) {
experienceLink.style.display = 'none'; // Hide the link
}
if (interestsSection) {
interestsSection.style.display = 'none'; // Hide the interests section
}
// Generate the PDF
html2pdf(areaCv, opt);
// Show the elements again after a timeout
setTimeout(() => {
if (experienceLink) {
experienceLink.style.display = 'block'; // Show the link again
}
if (interestsSection) {
interestsSection.style.display = 'block'; // Show the interests section again
}
}, 2000); // Adjust the timeout if necessary
}
// When the button is clicked, it executes the three functions
resumeButton.addEventListener("click", () => {
// 1. The class .scale-cv is added to the body, where it reduces the size of the elements
scaleCv();
// 2. The PDF is generated
generateResume();
// 3. The .scale-cv class is removed from the body after 5 seconds to return to normal size.
setTimeout(removeScale, 5000);
});
/*========== CONFIRMATION MESSAGE ==========*/
const countdownElement = document.getElementById("countdown");
// Date to countdown (July 1, 2023)
const countdownDate = new Date("July 1, 2023 00:00:00").getTime();
// Update the countdown every second
const countdownTimer = setInterval(() => {
const now = new Date().getTime();
const distance = countdownDate - now;
// Calculate days, hours, minutes, and seconds remaining
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
// Update the countdown element
countdownElement.innerHTML = `The remaining time is ${days} days, ${hours} hours, ${minutes} minutes, and ${seconds} seconds.`;
// If the target date is reached, stop the countdown
if (distance < 0) {
clearInterval(countdownTimer);
countdownElement.innerHTML = "The day has arrived!";
}
}, 1000);

View File

@ -0,0 +1,366 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="assets/img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css"
/>
<link rel="stylesheet" href="styles/index.css" />
<title>Dan Grubb CV</title>
</head>
<body>
<!--========== HEADER ==========-->
<header class="l-header" id="header">
<nav class="nav bd-container">
<a href="#" class="nav 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">
<!-- All elements within this div, is generated in PDF -->
<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="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="assets/pdf/ResumeCv.pdf"
class="home__button-movil"
>Download</a
>
</div>
<!--
<div>
<a href="contact.html" class="home__button-contact"
>Contact Me</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>
<!-- Theme change button -->
<i
class="bx bx-moon change-theme"
title="Theme"
id="theme-button"
></i>
<!-- Button to generate and download the pdf. Available for desktop. -->
<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__studies"></span> -->
<span class="education__year">2020</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">Self-Study Certifications</h3>
<!-- <span class="education__studies"></span> -->
<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>
<p class="experience__description">
<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>
</p>
</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
>
<p class="experience__description">
<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>
</p>
</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
>
<p class="experience__description">
<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 Dells corporate, government, and consumer clients.</li>
<li>• Exceeded ticket completion standards handling all tickets in the Falls Church - Arlington area.</li>
</ul>
</p>
</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
>
<p class="experience__description">
<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>
</p>
</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
>
<p class="experience__description">
<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> </p>
</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
>
<p class="experience__description">
<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> </p>
</div>
</div>
<a href="index.html" class="back__home">
<i class="bx bx-arrow-back back__icon"></i>
Back to Home
</a>
</div>
</section>
</div>
</div>
</main>
<!--========== SCROLL TOP ==========-->
<a href="#" class="scrolltop" id="scroll-top">
<i class="bx bx-up-arrow-alt scrolltop__icon"></i>
</a>
<!--========== HTML2PDF ==========-->
<script src="js/html2pdf.bundle.min.js"></script>
<!--========== MAIN JS ==========-->
<script src="js/main.js"></script>
</body>
</html>

692
public/cv/styles/index.css Normal file
View File

@ -0,0 +1,692 @@
/*===== GOOGLE FONTS =====*/
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
/*===== VARIABLES CSS =====*/
:root {
--header-height: 3rem;
/*========== Colors ==========*/
--title-color: #0b0a0a;
--text-color: #403a3a;
--text-color-light: #707070;
--container-color: #fafafa;
--container-color-alt: #f0efef;
--body-color: #fcfcfc;
/*========== Font and typography ==========*/
--body-font: "Poppins", sans-serif;
--h1-font-size: 1.5rem;
--h2-font-size: 1.25rem;
--h3-font-size: 1rem;
--normal-font-size: 0.938rem;
--small-font-size: 0.875rem;
--smaller-font-size: 0.813rem;
/*========== Font weight ==========*/
--font-medium: 500;
--font-semi-bold: 600;
/*========== Margenes ==========*/
--mb-1: 0.5rem;
--mb-2: 1rem;
--mb-3: 1.5rem;
/*========== z index ==========*/
--z-tooltip: 10;
--z-fixed: 100;
}
/*========== BASE ==========*/
/*========== Variables Dark theme ==========*/
body.dark-theme {
--title-color: #f2f2f2;
--text-color: #bfbfbf;
--container-color: #212121;
--container-color-alt: #181616;
--body-color: #2b2b2b;
}
/*========== Button Dark/Light ==========*/
.change-theme {
position: absolute;
right: 0;
top: 2.2rem;
display: flex;
color: var(--text-color);
font-size: 1.2rem;
cursor: pointer;
}
.change-theme:hover {
color: var(--title-color);
}
/*========== Font size variables to scale cv ==========*/
body.scale-cv {
--h1-font-size: 0.938rem;
--h2-font-size: 0.938rem;
--h3-font-size: 0.875rem;
--normal-font-size: 0.813rem;
--small-font-size: 0.75rem;
--smaller-font-size: 0.688rem;
}
/*========== Generate PDF button ==========*/
.generate-pdf {
display: none;
position: absolute;
top: 2.2rem;
left: 0;
font-size: 1.2rem;
color: var(--text-color);
cursor: pointer;
}
.generate-pdf:hover {
color: var(--title-color);
}
/*========== Classes modified to reduce size and print on A4 sheet ==========*/
.scale-cv .change-theme,
.scale-cv .generate-pdf {
display: none;
}
.scale-cv .bd-container {
max-width: 595px;
}
.scale-cv .section {
padding: 1.5rem 0 0.8rem;
}
.scale-cv .section-title {
margin-bottom: 0.75rem;
}
.scale-cv .resume__left,
.scale-cv .resume__right {
padding: 0 1rem;
}
.scale-cv .home-img {
width: 90px;
height: 90px;
}
.scale-cv .home__container {
gap: 1.5rem;
}
.scale-cv .home__data {
gap: 0.25rem;
}
.scale-cv .home__address,
.scale-cv .social__container {
gap: 0.75rem;
}
.scale-cv .home__icon,
.scale-cv .social__icon,
.scale-cv .interests__icon {
font-size: 1rem;
}
.scale-cv .education__container,
.scale-cv .experience__company,
.scale-cv .certificate__container {
gap: 1rem;
}
.scale-cv .education__time,
.scale-cv .experience__time {
padding-right: 0.5rem;
}
.scale-cv .education__rounder,
.scale-cv .experience__rounder {
width: 11px;
height: 11px;
margin-top: 0.22rem;
}
.scale-cv .education__line,
.scale-cv .experience__line {
width: 1px;
height: 110%;
transform: translate(5px, 0);
}
.scale-cv .experience__line__last {
width: 1px;
height: 100%;
transform: translate(5px, 0);
}
.scale-cv .education__data,
.scale-cv .experience__data {
gap: 0.25rem;
}
.scale-cv .skills__name {
margin-bottom: var(--mb-1);
}
.scale-cv .interests__container {
column-gap: 2.5rem;
}
body {
margin: 0 0 var(--header-height);
padding: 0;
font-family: var(--body-font);
font-size: var(--normal-font-size);
background-color: var(--body-color);
color: var(--text-color);
}
h1,
h2,
h3,
ul,
p {
margin: 0;
}
h1,
h2,
h3 {
color: var(--title-color);
font-weight: var(--font-medium);
}
ul {
padding: 0;
list-style: none;
}
a {
text-decoration: none;
}
img {
max-width: 100%;
height: auto;
}
/*========== CLASS CSS ==========*/
.section {
padding: 1.5rem 0;
}
.section-title {
font-size: var(--h2-font-size);
color: var(--title-color);
font-weight: var(--font-semi-bold);
text-transform: uppercase;
letter-spacing: 0.35rem;
text-align: center;
margin-bottom: var(--mb-3);
}
/*========== LAYOUT ==========*/
.bd-container {
max-width: 968px;
width: calc(100% - 3rem);
margin-left: var(--mb-3);
margin-right: var(--mb-3);
}
.bd-grid {
display: grid;
gap: 1.5rem;
}
.l-header {
width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: var(--z-fixed);
background-color: var(--body-color);
box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.1);
transition: 0.3s;
}
/*========== NAV ==========*/
.nav {
height: var(--header-height);
display: flex;
justify-content: space-between;
align-items: center;
}
@media screen and (max-width: 968px) {
.nav__menu {
position: fixed;
bottom: -100%;
left: 0;
width: 100%;
padding: 2rem 0rem;
background-color: var(--body-color);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
border-radius: 1rem 1rem 0 0;
z-index: var(--z-fixed);
transition: 0.3s;
}
}
.nav__logo,
.nav__toggle {
color: var(--title-color);
font-weight: var(--font-medium);
}
.nav__toggle {
font-size: 1.2rem;
cursor: pointer;
}
.nav__item {
text-align: center;
}
.nav__list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
.nav__link {
display: flex;
flex-direction: column;
font-size: var(--smaller-font-size);
color: var(--text-color-light);
font-weight: var(--font-medium);
}
.nav__link:hover {
color: var(--title-color);
}
.nav__icon {
font-size: 1.2rem;
margin-bottom: var(--mb-1);
}
/* Show menu */
.show-menu {
bottom: var(--header-height);
}
/* Active menu link */
.active-link {
color: var(--title-color);
}
/*========== HOME ==========*/
.home {
position: relative;
}
.home__container {
gap: 3rem;
}
.home__data {
gap: 0.5rem;
text-align: center;
}
.home__img {
width: 120px;
height: 120px;
border-radius: 50%;
justify-self: center;
margin-bottom: var(--mb-1);
}
.home__title {
font-size: var(--h1-font-size);
}
.home__profession {
font-size: var(--normal-font-size);
margin-bottom: var(--mb-1);
}
.home__address {
gap: 1rem;
}
.home__information {
display: flex;
align-items: center;
font-size: var(--small-font-size);
}
.home__icon {
font-size: 1.2rem;
margin-right: 0.25rem;
}
.home__button-movil {
display: inline-block;
border: 2px solid var(--text-color);
color: var(--title-color);
padding: 1rem 2rem;
border-radius: 0.25rem;
transition: 0.3s;
font-weight: var(--font-medium);
margin-top: var(--mb-3);
}
.home__button-movil:hover {
background-color: var(--text-color);
color: var(--container-color);
}
.home__button-contact {
display: inline-block;
border: 2px solid var(--text-color);
color: var(--title-color);
padding: 1rem 1.6rem;
border-radius: 0.25rem;
transition: 0.3s;
font-weight: var(--font-medium);
margin-top: var(--mb-3);
}
.home__button-contact:hover {
background-color: var(--text-color);
color: var(--container-color);
}
.information__icon,
.back__icon {
font-size: 1.5rem;
color: var(--text-color);
}
.information__icon:hover,
.back__icon:hover {
color: var(--title-color);
}
.more__information,
.back__home {
font-size: var(--h3-font-size);
color: var(--text-color);
font-weight: var(--font-medium);
display: flex;
gap: 0.5rem;
}
.back__home {
margin-top: 1.5rem;
}
.more__information:hover,
.back__home:hover {
color: var(--title-color);
}
/*========== SOCIAL ==========*/
.social__container {
grid-template-columns: max-content;
gap: 1rem;
}
.social__link {
display: inline-flex;
align-items: center;
font-size: var(--small-font-size);
color: var(--text-color);
}
.social__link:hover {
color: var(--title-color);
}
.social__icon {
font-size: 1.2rem;
margin-right: 0.25rem;
}
/*========== PROFILE ==========*/
.profile__description {
text-align: center;
}
/*========== EDUCATION AND EXPERIENCE ==========*/
.education__content,
.experience__content {
display: flex;
}
.education__time,
.experience__time {
padding-right: 1rem;
}
.education__rounder,
.experience__rounder {
position: relative;
display: block;
width: 16px;
height: 16px;
background-color: var(--text-color-light);
border-radius: 50%;
margin-top: 0.25rem;
}
.education__line,
.experience__line {
display: block;
width: 2px;
height: 109%;
background-color: var(--text-color-light);
transform: translate(7px, 0);
}
.experience__line__last {
display: block;
width: 2px;
height: 100%;
background-color: var(--text-color-light);
transform: translate(7px, 0);
}
.education__data,
.experience__data {
gap: 0.5rem;
}
.education__title,
.experience__title {
font-size: var(--h3-font-size);
}
.education__studies,
.experience__company {
font-size: var(--small-font-size);
color: var(--title-color);
}
.education__year {
font-size: var(--smaller-font-size);
}
/*========== SKILLS AND LANGUAGES ==========*/
.skills__content,
.languages__content {
grid-template-columns: repeat(2, 1fr);
gap: 0;
}
.skills__name,
.languages__name {
display: flex;
align-items: center;
margin-bottom: var(--mb-3);
}
.skills__circle,
.languages__circle {
display: inline-block;
width: 5px;
height: 5px;
background-color: var(--text-color);
border-radius: 50%;
margin-right: 0.75rem;
}
/*========== CERTIFICATES ==========*/
.certificate__title {
font-size: var(--h3-font-size);
margin-bottom: var(--mb-1);
}
/*========== REFERENCES ==========*/
.references__content {
gap: 0.25rem;
}
.references__subtitle {
color: var(--text-color-light);
}
.references__subtitle,
.references__contact {
font-size: var(--smaller-font-size);
}
.references__title {
font-size: var(--h3-font-size);
}
/*========== INTERESTS ==========*/
.interests__container {
display: grid; /* Ensure the grid display is set */
grid-template-columns: repeat(4, 1fr); /* Keep 4 columns with equal widths */
gap: 1.5rem; /* Adjust gap if needed; this is value you can tweak */
margin-top: var(--mb-2);
}
.interests__content {
display: flex;
flex-direction: column;
align-items: center;
/* You can add a fixed width here if necessary to avoid wrapping, but typically it's better to avoid fixed widths */
}
.interests__icon {
font-size: 1.5rem;
color: var(--text-color-light);
margin-bottom: 0.25rem;
}
/*========== CONTACT ==========*/
.contact__container {
display: flex;
justify-content: center;
flex-direction: column;
}
.contact__info {
display: flex;
flex-direction: row;
gap: 3rem;
align-items: center;
font-size: var(--h3-font-size);
color: var(--text-color);
font-weight: var(--font-medium);
display: flex;
gap: 2rem;
}
.contact__icon {
font-size: 1.5rem;
color: var(--text-color);
}
.contact__phone,
.contact__email {
font-size: var(--h3-font-size);
color: var(--text-color);
font-weight: var(--font-medium);
gap: 0.5rem;
}
.contact__form {
width: 100%;
max-width: 500px;
padding: 3rem;
background-color: #f1f1f1;
border-radius: 10px;
}
.contact__content {
display: flex;
flex-direction: row;
}
.contact__inputs {
display: grid;
gap: 1rem;
}
.contact__input,
.contact__textarea {
width: 100%;
padding: 0.75rem;
border: none;
background-color: #e5e5e5;
border-radius: 5px;
}
.contact__textarea {
resize: none;
height: 150px;
margin-top: 1rem;
}
.contact__button {
display: block;
width: 100%;
padding: 0.75rem;
margin-top: 1.5rem;
border: none;
background-color: #222;
color: #fff;
font-size: 1.2rem;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.contact__button:hover {
background-color: #555;
}
/* Scroll top */
.scrolltop {
position: fixed;
right: 1rem;
bottom: -20%;
display: flex;
justify-content: center;
align-items: center;
padding: 0.3rem;
background-color: var(--container-color-alt);
border-radius: 0.4rem;
z-index: var(--z-tooltip);
transition: 0.4s;
visibility: hidden;
}
.scrolltop__icon {
font-size: 1.2rem;
color: var(--text-color);
}
.show-scroll {
visibility: visible;
bottom: 5rem;
}
/*========== CONFIRMATION MESSAGE ==========*/
.confirmation {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.confirmation h2 {
font-size: var(--h1-font-size);
color: var(--title-color);
margin-bottom: var(--mb-2);
}
.confirmation p {
font-size: 1.5rem;
margin-bottom: 2rem;
}
#countdown {
font-size: 2rem;
margin-bottom: 2rem;
}
/*========== MEDIA QUERIES ==========*/
/* For small devices, menu two columns */
@media screen and (max-width: 320px) {
.nav__list {
grid-template-columns: repeat(2, 1fr);
gap: 1rem 0.5rem;
}
}
/* Classes modified for large screen size */
@media screen and (min-width: 968px) {
body {
margin: 3rem 0;
}
.bd-container {
margin-left: auto;
margin-right: auto;
}
.home__button-contact {
margin-top: 0;
}
.l-header,
.scrolltop {
display: none;
}
.resume {
display: grid;
grid-template-columns: 0.5fr 1fr;
background-color: var(--container-color);
box-shadow: 0 0 8px rgba(13, 12, 12, 15);
}
.resume__left {
background-color: var(--container-color-alt);
}
.resume__left,
.resume__right {
padding: 0 1.5rem;
}
.generate-pdf {
display: inline-block;
}
.section-title,
.profile__description {
text-align: initial;
}
.home__container {
gap: 1.5rem;
}
.home__button-movil {
display: none;
}
.references__container {
grid-template-columns: repeat(2, 1fr);
}
.languages__content {
grid-template-columns: repeat(3, max-content);
column-gap: 3.5rem;
}
.interests__container {
grid-template-columns: repeat(4, max-content);
column-gap: 3.5rem;
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

9
public/favicon.svg Normal file
View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DGDN - Darnea | dangrubb.net</title>
<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="../../music.css">
<link rel="stylesheet" href="./player.css">
</head>
<body>
<div class="music-container">
<div class="music-header">
<h1><a href="/music">Music</a> / <a href="/music/Darnea">Darnea</a> / DGDN</h1>
</div>
<div class="album-player">
<div class="album-header">
<div class="album-artwork">🔥</div>
<div class="album-info">
<h2>DGDN</h2>
<p class="artist-name">Darnea</p>
<p class="album-desc">Leaked from the vault</p>
</div>
</div>
<div class="player-section">
<div id="now-playing" class="now-playing">
<p>Select a track to play</p>
</div>
<audio id="audio-player" controls></audio>
</div>
<div class="tracklist">
<h3>Tracklist</h3>
<ol id="tracks-list"></ol>
</div>
</div>
<div class="music-footer">
<p><a href="/music/Darnea">← back to artist</a></p>
</div>
</div>
<script src="./player.js"></script>
</body>
</html>

View File

@ -0,0 +1,139 @@
/* Album Player */
.album-player {
margin: 40px 0;
}
.album-header {
display: flex;
gap: 30px;
margin-bottom: 40px;
padding: 30px;
background: rgba(107, 225, 233, 0.05);
border-radius: 8px;
align-items: center;
}
.album-artwork {
font-size: 5rem;
min-width: 150px;
text-align: center;
}
.album-info h2 {
margin: 0 0 5px 0;
font-size: 2rem;
color: #9d9aa4;
}
.album-info .artist-name {
margin: 0 0 5px 0;
color: #6be1e9;
font-size: 1.1rem;
}
.album-info .album-desc {
margin: 0;
color: #6be1e9;
font-size: 0.9rem;
font-style: italic;
}
/* Now Playing */
.now-playing {
text-align: center;
padding: 20px;
background: rgba(107, 225, 233, 0.1);
border: 1px solid rgba(107, 225, 233, 0.3);
border-radius: 6px;
margin-bottom: 20px;
min-height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
.now-playing p {
margin: 0;
color: #9d9aa4;
}
.now-playing.playing p {
color: #6be1e9;
font-weight: bold;
}
/* Audio Player */
#audio-player {
width: 100%;
margin-bottom: 30px;
accent-color: #6be1e9;
}
/* Tracklist */
.tracklist {
margin-top: 40px;
}
.tracklist h3 {
margin: 0 0 20px 0;
font-size: 1.3rem;
color: #9d9aa4;
}
.tracklist ol {
list-style-position: inside;
padding: 0;
margin: 0;
}
.tracklist li {
padding: 12px;
margin-bottom: 8px;
background: rgba(157, 154, 164, 0.05);
border: 1px solid rgba(107, 225, 233, 0.2);
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
color: #9d9aa4;
}
.tracklist li:hover {
background: rgba(107, 225, 233, 0.1);
border-color: rgba(107, 225, 233, 0.5);
}
.tracklist li.active {
background: rgba(107, 225, 233, 0.2);
border-color: #6be1e9;
color: #6be1e9;
font-weight: bold;
}
.track-duration {
float: right;
color: #6be1e9;
font-size: 0.9rem;
}
/* Responsive */
@media (max-width: 768px) {
.album-header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.album-artwork {
font-size: 3rem;
}
.album-info h2 {
font-size: 1.5rem;
}
.tracklist li {
padding: 10px;
font-size: 0.9rem;
}
}

View File

@ -0,0 +1,71 @@
// 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" }
];
const audioPlayer = document.getElementById('audio-player');
const tracksList = document.getElementById('tracks-list');
const nowPlaying = document.getElementById('now-playing');
let currentTrackIndex = 0;
// Build tracklist
function initTracklist() {
tracks.forEach((track, index) => {
const li = document.createElement('li');
li.textContent = track.title;
li.dataset.index = index;
li.addEventListener('click', () => playTrack(index));
tracksList.appendChild(li);
});
}
// Play track
function playTrack(index) {
currentTrackIndex = index;
const track = tracks[index];
// Update audio source
audioPlayer.src = `/src/audio/DGDN/${track.filename}`;
audioPlayer.play();
// Update UI
updateNowPlaying(track.title);
updateActiveTrack();
}
// Update now playing display
function updateNowPlaying(title) {
nowPlaying.classList.add('playing');
nowPlaying.innerHTML = `<p>▶ Now playing: ${title}</p>`;
}
// Update active track in list
function updateActiveTrack() {
document.querySelectorAll('.tracklist li').forEach((li, i) => {
li.classList.toggle('active', i === currentTrackIndex);
});
}
// Auto-play next track when current ends
audioPlayer.addEventListener('ended', () => {
if (currentTrackIndex < tracks.length - 1) {
playTrack(currentTrackIndex + 1);
}
});
// Update active track when manually seeking/playing
audioPlayer.addEventListener('play', () => {
updateActiveTrack();
});
// Initialize
initTracklist();

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Darnea - dangrubb.net</title>
<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="../music.css">
</head>
<body>
<div class="music-container">
<div class="music-header">
<h1><a href="/music">Music</a> / Darnea</h1>
</div>
<div class="artist-detail">
<div class="artist-banner">🎤</div>
<h2>Darnea</h2>
</div>
<div class="albums-grid">
<div class="album-card">
<div class="album-cover">🔥</div>
<h3><a href="/music/Darnea/DGDN">DGDN</a></h3>
<p class="album-meta">8 tracks</p>
</div>
</div>
<div class="music-footer">
<p><a href="/music">← back to music</a></p>
</div>
</div>
</body>
</html>

29
public/music/index.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>dangrubb.net Music</title>
<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="./music.css">
</head>
<body>
<div class="music-container">
<div class="music-header">
<h1><a href="/">dangrubb</a>.net/music</h1>
</div>
<div class="artists-grid">
<div class="artist-card">
<div class="artist-cover">🎤</div>
<h2><a href="/music/Darnea">Darnea</a></h2>
<p class="artist-meta">1 album</p>
</div>
</div>
<div class="music-footer">
<p><a href="/">← back home</a></p>
</div>
</div>
</body>
</html>

212
public/music/music.css Normal file
View File

@ -0,0 +1,212 @@
/* --- BASE STYLES (Merged from style.css) --- */
body {
background: #0d0a14;
padding: 40px 20px;
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
color: #9d9aa4;
/* Fixed the no-scroll issue here: */
min-height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow-y: auto;
}
a {
color: #6be1e9;
text-decoration: none;
}
a:focus,
a:hover,
a:visited {
opacity: 0.8;
}
/* --- MUSIC PLATFORM STYLING --- */
.music-container {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
flex: 1; /* Allows container to grow and push footer down */
}
.music-header {
text-align: center;
margin-bottom: 50px;
border-bottom: 2px solid rgba(107, 225, 233, 0.3);
padding-bottom: 20px;
}
.music-header h1 {
margin: 0;
font-size: 2rem;
color: #9d9aa4;
font-weight: bold;
}
.music-header h1 a {
color: #6be1e9;
}
/* Artists/Albums Grid */
.artists-grid,
.albums-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 30px;
margin: 40px 0;
}
.artist-card,
.album-card {
text-align: center;
padding: 20px;
background: rgba(157, 154, 164, 0.05);
border: 1px solid rgba(107, 225, 233, 0.2);
border-radius: 8px;
transition: all 0.3s ease;
}
.artist-card:hover,
.album-card:hover {
background: rgba(107, 225, 233, 0.1);
border-color: rgba(107, 225, 233, 0.5);
transform: translateY(-4px);
}
.artist-cover,
.album-cover {
font-size: 4rem;
margin-bottom: 15px;
}
.artist-card h2,
.album-card h3 {
margin: 10px 0;
font-size: 1.3rem;
color: #9d9aa4;
}
.artist-meta,
.album-meta {
color: #6be1e9;
font-size: 0.9rem;
margin: 0;
}
/* Artist Detail */
.artist-detail {
text-align: center;
margin: 40px 0;
padding: 40px 20px;
background: rgba(107, 225, 233, 0.05);
border-radius: 8px;
}
.artist-banner {
font-size: 5rem;
margin-bottom: 20px;
}
.artist-detail h2 {
margin: 0;
font-size: 2.5rem;
color: #9d9aa4;
}
/* Footer / Bottom Nav */
.music-footer {
text-align: center;
margin-top: 60px;
padding: 20px 0;
border-top: 2px solid rgba(107, 225, 233, 0.3);
}
.bottom-nav {
margin-top: auto;
width: 100%;
text-align: center;
padding: 20px 0;
}
.bottom-nav a {
margin: 0 10px;
display: inline-block;
}
/* --- BARD / IMAGE STYLES --- */
.bard {
margin: 0 auto 30px;
width: 100%;
max-width: 65em;
height: auto;
aspect-ratio: 1 / 0.95;
font-size: 2px;
flex-shrink: 0;
position: relative;
}
.bard img {
width: 100%;
height: auto;
position: absolute;
left: 0;
top: 0;
}
/* --- ANNOUNCEMENT & BLOG PREVIEW --- */
.announcement {
margin: 20px auto;
display: block;
border: 1px solid #ffff00;
padding: 10px 15px;
border-radius: 3px;
text-align: center;
max-width: 400px;
}
.announcement a {
color: #ffff00;
font-weight: bold;
}
.blog-preview-container {
margin: 30px auto;
max-width: 600px;
width: 100%;
display: flex;
justify-content: center;
}
.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));
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
/* --- RESPONSIVE MEDIA QUERIES --- */
@media (max-width: 768px) {
.music-container {
padding: 20px 10px;
}
.music-header h1 {
font-size: 1.5rem;
}
.artists-grid,
.albums-grid {
grid-template-columns: 1fr;
}
.artist-banner {
font-size: 3rem;
}
.artist-detail h2 {
font-size: 1.8rem;
}
.bard {
font-size: 3px;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
public/src/img/bard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/src/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

View File

@ -126,4 +126,41 @@ uncomment the code below.
// <div class="heading">CSS</div> // <div class="heading">CSS</div>
// <pre>${css.join("\n\n")}</pre> // <pre>${css.join("\n\n")}</pre>
// </div> // </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.

View File

@ -0,0 +1,100 @@
---
const strips = [
{ top: 0, h: 5, x1: -2, hue1: -16, x2: 2, hue2: 13, anim: "glitch-9", dur: 9000, delay: 2 },
{ top: 5, h: 3, x1: -1, hue1: -42, x2: -6, hue2: -23, anim: "glitch-5", dur: 5000, delay: 1 },
{ top: 8, h: 3, x1: -10, hue1: -42, x2: -9, hue2: 45, anim: "glitch-7", dur: 7000, delay: 0 },
{ top: 11, h: 5, x1: -6, hue1: -11, x2: 2, hue2: 29, anim: "glitch-8", dur: 8000, delay: 1 },
{ top: 16, h: 5, x1: 0, hue1: 42, x2: 2, hue2: -28, anim: "glitch-6", dur: 6000, delay: 0 },
{ top: 21, h: 2, x1: -3, hue1: -45, x2: -5, hue2: -15, anim: "glitch-7", dur: 7000, delay: 0 },
{ top: 23, h: 2, x1: -5, hue1: 35, x2: 9, hue2: 35, anim: "glitch-6", dur: 6000, delay: 0 },
{ top: 25, h: 6, x1: 10, hue1: 39, x2: -8, hue2: 24, anim: "glitch-7", dur: 7000, delay: 1 },
{ top: 31, h: 6, x1: -6, hue1: -46, x2: -3, hue2: 18, anim: "glitch-10", dur: 10000, delay: 1 },
{ top: 37, h: 1, x1: -8, hue1: -37, x2: -6, hue2: 15, anim: "glitch-9", dur: 9000, delay: 1 },
{ top: 38, h: 2, x1: -1, hue1: 16, x2: 2, hue2: 25, anim: "glitch-5", dur: 5000, delay: 1 },
{ top: 40, h: 2, x1: 5, hue1: 32, x2: 10, hue2: -3, anim: "glitch-9", dur: 9000, delay: 1 },
{ top: 42, h: 4, x1: 10, hue1: -26, x2: 6, hue2: -45, anim: "glitch-6", dur: 6000, delay: 1 },
{ top: 46, h: 3, x1: -7, hue1: -45, x2: -8, hue2: 45, anim: "glitch-5", dur: 5000, delay: 2 },
{ top: 49, h: 3, x1: 4, hue1: 40, x2: -8, hue2: 29, anim: "glitch-6", dur: 6000, delay: 1 },
{ top: 52, h: 6, x1: -4, hue1: 26, x2: -6, hue2: -3, anim: "glitch-10", dur: 10000, delay: 0 },
{ top: 58, h: 4, x1: 6, hue1: 43, x2: -1, hue2: -1, anim: "glitch-8", dur: 8000, delay: 0 },
];
---
<div class="bard">
{strips.map(s => (
<div class="strip" style={`
--glitch-x-1: ${s.x1}em;
--glitch-hue-1: ${s.hue1}deg;
--glitch-x-2: ${s.x2}em;
--glitch-hue-2: ${s.hue2}deg;
background-position: 0 -${s.top}em;
height: ${s.h}em;
animation-name: ${s.anim};
animation-duration: ${s.dur}ms;
animation-delay: ${s.delay}s;
`}></div>
))}
</div>
<style>
.bard {
margin: 0 auto 30px;
width: 100%;
max-width: 65em;
height: auto;
aspect-ratio: 1 / 0.95;
font-size: 2px;
flex-shrink: 0;
}
.strip {
overflow: hidden;
position: relative;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-timing-function: linear;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
background-size: 100% auto;
background-image: url('/src/img/bard.png');
}
@keyframes glitch-5 {
0.00%, 33.33%, 43.33%, 66.67%, 76.67%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 43.23% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(0px -4px 0 rgba(0, 0, 255, 0.1)); }
66.77%, 76.57% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(-1px 0px 0 rgba(255, 0, 0, 0.1)); }
}
@keyframes glitch-6 {
0.00%, 33.33%, 41.67%, 66.67%, 75.00%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 41.57% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(-2px 3px 0 rgba(255, 0, 0, 0.1)); }
66.77%, 74.90% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(-3px -2px 0 rgba(0, 0, 255, 0.1)); }
}
@keyframes glitch-7 {
0.00%, 33.33%, 40.48%, 66.67%, 73.81%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 40.38% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(0px -3px 0 rgba(0, 0, 255, 0.1)); }
66.77%, 73.71% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(4px 1px 0 rgba(255, 0, 0, 0.1)); }
}
@keyframes glitch-8 {
0.00%, 33.33%, 39.58%, 66.67%, 72.92%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 39.48% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(-1px -1px 0 rgba(0, 0, 255, 0.1)); }
66.77%, 72.82% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(3px -1px 0 rgba(0, 0, 255, 0.1)); }
}
@keyframes glitch-9 {
0.00%, 33.33%, 38.89%, 66.67%, 72.22%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 38.79% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(1px -3px 0 rgba(255, 0, 0, 0.1)); }
66.77%, 72.12% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(1px 1px 0 rgba(255, 0, 0, 0.1)); }
}
@keyframes glitch-10 {
0.00%, 33.33%, 38.33%, 66.67%, 71.67%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 38.23% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(3px -1px 0 rgba(255, 0, 0, 0.1)); }
66.77%, 71.57% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(-3px 2px 0 rgba(255, 0, 0, 0.1)); }
}
@media (max-width: 768px) {
.bard { font-size: 3px; }
}
@media (max-width: 400px) {
.bard { aspect-ratio: 1 / 1; }
}
</style>

11
src/data/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"
}
]

37
src/layouts/Base.astro Normal file
View File

@ -0,0 +1,37 @@
---
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" />
</head>
<body>
<slot />
</body>
</html>
<style is:global>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #0d0a14;
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
color: #9d9aa4;
min-height: 100vh;
margin: 0;
padding: 0;
overflow-x: hidden;
}
a {
color: #6be1e9;
text-decoration: none;
}
a:hover, a:visited { opacity: 0.8; }
a:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
</style>

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>

28
src/pages/404.astro Normal file
View File

@ -0,0 +1,28 @@
---
import Base from '../layouts/Base.astro';
---
<Base title="Page Not Found">
<div class="not-found">
<h1>Page Not Found</h1>
<p>This page does not exist yet.</p>
<p class="small-text">Stay tuned.</p>
</div>
</Base>
<style>
.not-found {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
text-align: center;
background: #123425;
color: white;
font-family: Arial, sans-serif;
}
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>

169
src/pages/blog/index.astro Normal file
View File

@ -0,0 +1,169 @@
---
import CrushBase from '../../layouts/CrushBase.astro';
import posts from '../../data/posts.json';
---
<CrushBase 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>
<div class="blogs-container">
{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" set:html={post.content} />
<div class="blog-post-image">
<img src={post.image} alt={post.title} />
</div>
</article>
))}
</div>
<footer class="blog-footer">
<a href="/">← home</a>
</footer>
</div>
</CrushBase>
<style>
.page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: var(--crush-white);
}
.blog-header {
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 {
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;
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);
max-width: 700px;
width: 100%;
}
.blog-post-title {
color: var(--crush-teal);
font-weight: 700;
font-size: 1.6rem;
text-align: center;
margin-bottom: 6px;
}
.blog-post-date {
color: var(--crush-teal-light);
font-size: 0.85rem;
text-align: center;
margin-bottom: 20px;
opacity: 0.7;
}
.blog-post-content {
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;
margin-top: 20px;
}
.blog-post-image img {
max-width: 100%;
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;
}
@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; }
}
</style>

182
src/pages/index.astro Normal file
View File

@ -0,0 +1,182 @@
---
import CrushBase from '../layouts/CrushBase.astro';
---
<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>
<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>
<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>
.crush-page {
display: flex;
flex-direction: column;
height: 100vh;
height: 100dvh;
margin: 0;
background: var(--crush-white);
}
.top-banner {
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;
}
.brand {
font-family: 'Alfa Slab One', 'Georgia', serif;
font-size: clamp(3rem, 10vw, 5rem);
color: var(--crush-teal);
letter-spacing: -2px;
line-height: 1;
}
.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;
}
.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;
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));
}
.bottom-nav {
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;
}
.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;
}
.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; }
}
</style>

30
src/styles/bard.css Normal file
View File

@ -0,0 +1,30 @@
/* ===== Glitch Bard Component ===== */
.bard {
margin: 0 auto 30px;
width: 100%;
max-width: 65em;
height: auto;
aspect-ratio: 1 / 0.95;
font-size: 2px;
flex-shrink: 0;
}
.strip {
overflow: hidden;
position: relative;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-timing-function: linear;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
background-size: 100% auto;
background-image: url('/src/img/bard.png');
}
@media (max-width: 768px) {
.bard { font-size: 3px; }
}
@media (max-width: 400px) {
.bard { aspect-ratio: 1 / 1; }
}

214
src/styles/cv.css Normal file
View File

@ -0,0 +1,214 @@
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
:root {
--header-height: 3rem;
--title-color: #0b0a0a;
--text-color: #403a3a;
--text-color-light: #707070;
--container-color: #fafafa;
--container-color-alt: #f0efef;
--body-color: #fcfcfc;
--body-font: "Poppins", sans-serif;
--h1-font-size: 1.5rem;
--h2-font-size: 1.25rem;
--h3-font-size: 1rem;
--normal-font-size: 0.938rem;
--small-font-size: 0.875rem;
--smaller-font-size: 0.813rem;
--font-medium: 500;
--font-semi-bold: 600;
--mb-1: 0.5rem;
--mb-2: 1rem;
--mb-3: 1.5rem;
--z-tooltip: 10;
--z-fixed: 100;
}
body.dark-theme {
--title-color: #f2f2f2;
--text-color: #bfbfbf;
--container-color: #212121;
--container-color-alt: #181616;
--body-color: #2b2b2b;
}
.change-theme {
position: absolute; right: 0; top: 2.2rem;
display: flex; color: var(--text-color); font-size: 1.2rem; cursor: pointer;
}
.change-theme:hover { color: var(--title-color); }
body.scale-cv {
--h1-font-size: 0.938rem; --h2-font-size: 0.938rem; --h3-font-size: 0.875rem;
--normal-font-size: 0.813rem; --small-font-size: 0.75rem; --smaller-font-size: 0.688rem;
}
.generate-pdf {
display: none; position: absolute; top: 2.2rem; left: 0;
font-size: 1.2rem; color: var(--text-color); cursor: pointer;
}
.generate-pdf:hover { color: var(--title-color); }
.scale-cv .change-theme, .scale-cv .generate-pdf { display: none; }
.scale-cv .bd-container { max-width: 595px; }
.scale-cv .section { padding: 1.5rem 0 0.8rem; }
.scale-cv .section-title { margin-bottom: 0.75rem; }
.scale-cv .resume__left, .scale-cv .resume__right { padding: 0 1rem; }
.scale-cv .home-img { width: 90px; height: 90px; }
.scale-cv .home__container { gap: 1.5rem; }
.scale-cv .home__data { gap: 0.25rem; }
.scale-cv .home__address, .scale-cv .social__container { gap: 0.75rem; }
.scale-cv .home__icon, .scale-cv .social__icon, .scale-cv .interests__icon { font-size: 1rem; }
.scale-cv .education__container, .scale-cv .experience__company, .scale-cv .certificate__container { gap: 1rem; }
.scale-cv .education__time, .scale-cv .experience__time { padding-right: 0.5rem; }
.scale-cv .education__rounder, .scale-cv .experience__rounder { width: 11px; height: 11px; margin-top: 0.22rem; }
.scale-cv .education__line, .scale-cv .experience__line { width: 1px; height: 110%; transform: translate(5px, 0); }
.scale-cv .experience__line__last { width: 1px; height: 100%; transform: translate(5px, 0); }
.scale-cv .education__data, .scale-cv .experience__data { gap: 0.25rem; }
.scale-cv .skills__name { margin-bottom: var(--mb-1); }
.scale-cv .interests__container { column-gap: 2.5rem; }
.cv-page body {
margin: 0 0 var(--header-height); padding: 0;
font-family: var(--body-font); font-size: var(--normal-font-size);
background-color: var(--body-color); color: var(--text-color);
}
.section { padding: 1.5rem 0; }
.section-title {
font-size: var(--h2-font-size); color: var(--title-color);
font-weight: var(--font-semi-bold); text-transform: uppercase;
letter-spacing: 0.35rem; text-align: center; margin-bottom: var(--mb-3);
}
.bd-container { max-width: 968px; width: calc(100% - 3rem); margin-left: var(--mb-3); margin-right: var(--mb-3); }
.bd-grid { display: grid; gap: 1.5rem; }
.l-header {
width: 100%; position: fixed; bottom: 0; left: 0;
z-index: var(--z-fixed); background-color: var(--body-color);
box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.1); transition: 0.3s;
}
.nav { height: var(--header-height); display: flex; justify-content: space-between; align-items: center; }
@media screen and (max-width: 968px) {
.nav__menu {
position: fixed; bottom: -100%; left: 0; width: 100%;
padding: 2rem 0; background-color: var(--body-color);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); border-radius: 1rem 1rem 0 0;
z-index: var(--z-fixed); transition: 0.3s;
}
}
.nav__logo, .nav__toggle { color: var(--title-color); font-weight: var(--font-medium); }
.nav__toggle { font-size: 1.2rem; cursor: pointer; }
.nav__item { text-align: center; }
.nav__list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; }
.nav__link { display: flex; flex-direction: column; font-size: var(--smaller-font-size); color: var(--text-color-light); font-weight: var(--font-medium); }
.nav__link:hover { color: var(--title-color); }
.nav__icon { font-size: 1.2rem; margin-bottom: var(--mb-1); }
.show-menu { bottom: var(--header-height); }
.active-link { color: var(--title-color); }
.home { position: relative; }
.home__container { gap: 3rem; }
.home__data { gap: 0.5rem; text-align: center; }
.home__img { width: 120px; height: 120px; border-radius: 50%; justify-self: center; margin-bottom: var(--mb-1); }
.home__title { font-size: var(--h1-font-size); }
.home__profession { font-size: var(--normal-font-size); margin-bottom: var(--mb-1); }
.home__address { gap: 1rem; }
.home__information { display: flex; align-items: center; font-size: var(--small-font-size); }
.home__icon { font-size: 1.2rem; margin-right: 0.25rem; }
.home__button-movil {
display: inline-block; border: 2px solid var(--text-color); color: var(--title-color);
padding: 1rem 2rem; border-radius: 0.25rem; transition: 0.3s;
font-weight: var(--font-medium); margin-top: var(--mb-3);
}
.home__button-movil:hover { background-color: var(--text-color); color: var(--container-color); }
.home__button-contact {
display: inline-block; border: 2px solid var(--text-color); color: var(--title-color);
padding: 1rem 1.6rem; border-radius: 0.25rem; transition: 0.3s;
font-weight: var(--font-medium); margin-top: var(--mb-3);
}
.home__button-contact:hover { background-color: var(--text-color); color: var(--container-color); }
.information__icon, .back__icon { font-size: 1.5rem; color: var(--text-color); }
.information__icon:hover, .back__icon:hover { color: var(--title-color); }
.more__information, .back__home {
font-size: var(--h3-font-size); color: var(--text-color);
font-weight: var(--font-medium); display: flex; gap: 0.5rem;
}
.back__home { margin-top: 1.5rem; }
.more__information:hover, .back__home:hover { color: var(--title-color); }
.profile__description { text-align: center; }
.education__content, .experience__content { display: flex; }
.education__time, .experience__time { padding-right: 1rem; }
.education__rounder, .experience__rounder {
position: relative; display: block; width: 16px; height: 16px;
background-color: var(--text-color-light); border-radius: 50%; margin-top: 0.25rem;
}
.education__line, .experience__line {
display: block; width: 2px; height: 109%;
background-color: var(--text-color-light); transform: translate(7px, 0);
}
.experience__line__last {
display: block; width: 2px; height: 100%;
background-color: var(--text-color-light); transform: translate(7px, 0);
}
.education__data, .experience__data { gap: 0.5rem; }
.education__title, .experience__title { font-size: var(--h3-font-size); }
.education__studies, .experience__company { font-size: var(--small-font-size); color: var(--title-color); }
.education__year { font-size: var(--smaller-font-size); }
.experience__description { list-style: none; padding: 0; margin: 0; }
.experience__description li { font-size: var(--normal-font-size); color: var(--text-color); }
.skills__content, .languages__content { grid-template-columns: repeat(2, 1fr); gap: 0; }
.skills__name, .languages__name { display: flex; align-items: center; margin-bottom: var(--mb-3); }
.skills__circle, .languages__circle {
display: inline-block; width: 5px; height: 5px;
background-color: var(--text-color); border-radius: 50%; margin-right: 0.75rem;
}
.certificate__title { font-size: var(--h3-font-size); margin-bottom: var(--mb-1); }
.references__content { gap: 0.25rem; }
.references__subtitle { color: var(--text-color-light); }
.references__subtitle, .references__contact { font-size: var(--smaller-font-size); }
.references__title { font-size: var(--h3-font-size); }
.interests__container { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; margin-top: var(--mb-2); }
.interests__content { display: flex; flex-direction: column; align-items: center; }
.interests__icon { font-size: 1.5rem; color: var(--text-color-light); margin-bottom: 0.25rem; }
.scrolltop {
position: fixed; right: 1rem; bottom: -20%;
display: flex; justify-content: center; align-items: center;
padding: 0.3rem; background-color: var(--container-color-alt);
border-radius: 0.4rem; z-index: var(--z-tooltip); transition: 0.4s;
visibility: hidden;
}
.scrolltop__icon { font-size: 1.2rem; color: var(--text-color); }
.show-scroll { visibility: visible; bottom: 5rem; }
@media screen and (max-width: 320px) {
.nav__list { grid-template-columns: repeat(2, 1fr); gap: 1rem 0.5rem; }
}
@media screen and (min-width: 968px) {
.cv-page body { margin: 3rem 0; }
.bd-container { margin-left: auto; margin-right: auto; }
.home__button-contact { margin-top: 0; }
.l-header, .scrolltop { display: none; }
.resume {
display: grid; grid-template-columns: 0.5fr 1fr;
background-color: var(--container-color); box-shadow: 0 0 8px rgba(13, 12, 12, 15);
}
.resume__left { background-color: var(--container-color-alt); }
.resume__left, .resume__right { padding: 0 1.5rem; }
.generate-pdf { display: inline-block; }
.section-title, .profile__description { text-align: initial; }
.home__container { gap: 1.5rem; }
.home__button-movil { display: none; }
.references__container { grid-template-columns: repeat(2, 1fr); }
.languages__content { grid-template-columns: repeat(3, max-content); column-gap: 3.5rem; }
.interests__container { grid-template-columns: repeat(4, max-content); column-gap: 3.5rem; }
}

31
src/styles/glitch.css Normal file
View File

@ -0,0 +1,31 @@
/* ===== Glitch Bard Animations ===== */
@keyframes glitch-5 {
0.00%, 33.33%, 43.33%, 66.67%, 76.67%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 43.23% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(0px -4px 0 rgba(0, 0, 255, 0.1)); }
66.77%, 76.57% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(-1px 0px 0 rgba(255, 0, 0, 0.1)); }
}
@keyframes glitch-6 {
0.00%, 33.33%, 41.67%, 66.67%, 75.00%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 41.57% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(-2px 3px 0 rgba(255, 0, 0, 0.1)); }
66.77%, 74.90% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(-3px -2px 0 rgba(0, 0, 255, 0.1)); }
}
@keyframes glitch-7 {
0.00%, 33.33%, 40.48%, 66.67%, 73.81%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 40.38% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(0px -3px 0 rgba(0, 0, 255, 0.1)); }
66.77%, 73.71% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(4px 1px 0 rgba(255, 0, 0, 0.1)); }
}
@keyframes glitch-8 {
0.00%, 33.33%, 39.58%, 66.67%, 72.92%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 39.48% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(-1px -1px 0 rgba(0, 0, 255, 0.1)); }
66.77%, 72.82% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(3px -1px 0 rgba(0, 0, 255, 0.1)); }
}
@keyframes glitch-9 {
0.00%, 33.33%, 38.89%, 66.67%, 72.22%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 38.79% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(1px -3px 0 rgba(255, 0, 0, 0.1)); }
66.77%, 72.12% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(1px 1px 0 rgba(255, 0, 0, 0.1)); }
}
@keyframes glitch-10 {
0.00%, 33.33%, 38.33%, 66.67%, 71.67%, 100.00% { transform: none; filter: hue-rotate(0) drop-shadow(0 0 0 transparent); }
33.43%, 38.23% { transform: translateX(var(--glitch-x-1)); filter: hue-rotate(var(--glitch-hue-1)) drop-shadow(3px -1px 0 rgba(255, 0, 0, 0.1)); }
66.77%, 71.57% { transform: translateX(var(--glitch-x-2)); filter: hue-rotate(var(--glitch-hue-2)) drop-shadow(-3px 2px 0 rgba(255, 0, 0, 0.1)); }
}

181
style.css
View File

@ -1,18 +1,23 @@
body { body {
background: #0d0a14; background: #0d0a14;
padding: 40px 20px; padding: 20px 20px 100px 20px;
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace; font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
color: #9d9aa4; color: #9d9aa4;
position: relative;
height: 100vh;
overflow-y: auto;
box-sizing: border-box;
} }
.bard { .bard {
margin: 0 auto 40px; margin: 0 auto 30px;
/* Remove fixed width in pixels */ /* Remove fixed width in pixels */
width: 100%; width: 100%;
max-width: 65em; /* Keep the same max size for desktop */ max-width: 65em; /* Keep the same max size for desktop */
height: auto; height: auto;
aspect-ratio: 1 / 0.95; /* Maintains the proportion of the artwork */ aspect-ratio: 1 / 0.95; /* Maintains the proportion of the artwork */
font-size: 2px; font-size: 2px;
flex-shrink: 0;
} }
/* Adjust media queries for better scaling */ /* Adjust media queries for better scaling */
@ -148,7 +153,7 @@ body {
} }
} }
p { p {
margin: 20px auto 0; margin: 10px auto 0;
position: relative; position: relative;
max-width: 400px; max-width: 400px;
text-align: center; text-align: center;
@ -156,6 +161,56 @@ p {
font-size: 0.875rem; font-size: 0.875rem;
} }
.announcement {
margin: 10px auto 0;
position: relative;
max-width: 400px;
text-align: center;
line-height: 1.3;
font-size: 0.875rem;
}
.announcement a {
color: #ffeb3b;
font-weight: bold;
text-decoration: none;
}
.announcement a:hover,
.announcement a:focus {
text-decoration: underline;
opacity: 0.85;
}
.debug {
margin: 40px auto 0;
position: relative;
max-width: 800px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 100%;
}
.links-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
text-align: center;
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
background: linear-gradient(to top, rgba(13, 10, 20, 0.95), rgba(13, 10, 20, 0.8));
backdrop-filter: blur(4px);
}
.links-bottom a {
color: #6be1e9;
}
a { a {
color: #6be1e9; color: #6be1e9;
} }
@ -208,4 +263,124 @@ a:focus-visible {
.column + .column { .column + .column {
color: #aa95bd; color: #aa95bd;
}
/* Make body use flexbox to push nav to bottom */
body {
background: #0d0a14;
padding: 40px 20px;
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
color: #9d9aa4;
min-height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden; /* Prevents scrollbar */
}
/* Color the announcement link yellow */
.announcement {
margin: 20px auto 0;
display: inline-block;
border: 1px solid #ffff00;
padding: 10px 15px;
border-radius: 3px;
}
.announcement a {
color: #ffff00;
font-weight: bold;
text-decoration: none;
}
.announcement a:hover {
text-decoration: underline;
}
/* Blog Preview Box */
.blog-preview-container {
margin: 30px auto 0;
max-width: 33%;
width: 100%;
display: flex;
justify-content: center;
}
.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);
}
.blog-preview img {
width: 100%;
height: auto;
max-height: 150px;
object-fit: cover;
border-radius: 3px;
margin-bottom: 10px;
}
.blog-preview-title {
color: #ffff00;
font-weight: bold;
font-size: 0.95rem;
margin: 10px 0 5px;
text-decoration: none;
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;
text-decoration: none;
}
.blog-preview-link:hover {
text-decoration: underline;
}
/* Push the navigation to the bottom using flexbox spacer */
.bottom-nav {
margin-top: auto;
width: 100%;
text-align: center;
display: flex;
justify-content: center;
}
.bottom-nav p {
margin: 0;
text-align: center;
}
.bottom-nav a {
margin: 0 10px;
display: inline-block;
} }

5
tsconfig.json Normal file
View File

@ -0,0 +1,5 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}