@@ -1,107 +0,0 @@
|
||||
# Keith Casko — Portfolio
|
||||
|
||||
A static personal portfolio for Keith Casko, targeting IT Support, Help Desk,
|
||||
Desktop Support, Junior SysAdmin, and Cloud Support / Operations roles.
|
||||
|
||||
Stack: plain HTML, CSS, and a tiny vanilla JS file (mobile nav + footer year).
|
||||
No build step. No frameworks.
|
||||
|
||||
## Run locally
|
||||
|
||||
Any static server works. Easiest options from the project root:
|
||||
|
||||
# Python 3
|
||||
python -m http.server 8080
|
||||
|
||||
# Node (if installed)
|
||||
npx serve .
|
||||
|
||||
Then open http://localhost:8080
|
||||
|
||||
Opening `index.html` directly in a browser also works; using a local server
|
||||
is only needed if you later add fetches or routing.
|
||||
|
||||
## Project structure
|
||||
|
||||
keith-portfolio/
|
||||
├── index.html
|
||||
├── css/
|
||||
│ └── styles.css
|
||||
├── js/
|
||||
│ └── main.js
|
||||
├── assets/
|
||||
│ ├── images/ # portrait, hero rack/workstation photos
|
||||
│ ├── screenshots/ # project screenshots
|
||||
│ └── resume/ # keith-casko-resume.pdf
|
||||
└── README.md
|
||||
|
||||
## Things to replace before publishing
|
||||
|
||||
Every spot that needs a real value is marked with `REPLACE` in an HTML
|
||||
comment. Search the project for `REPLACE` to find all of them. The main ones:
|
||||
|
||||
- **Resume PDF** — put the file at `assets/resume/keith-casko-resume.pdf`
|
||||
(two links in `index.html`: hero "Download Resume" button and footer).
|
||||
- **GitHub** — footer link, replace `https://github.com/REPLACE_ME`.
|
||||
- **LinkedIn** — footer link, replace `https://www.linkedin.com/in/REPLACE_ME`.
|
||||
- **Email** — footer and Contact section, replace `you@example.com`
|
||||
(two `mailto:` links).
|
||||
- **Project links** — four `View Project ›` links in the Featured Projects
|
||||
section currently point to `#`. Point each to the real repo or live demo.
|
||||
|
||||
## Screenshots & images
|
||||
|
||||
Drop the real files into these paths and remove the placeholder `<div>`s
|
||||
in `index.html` (replace with `<img src="…" alt="…">`):
|
||||
|
||||
- `assets/images/hero-rack.jpg` — homelab, rack, or workstation photo
|
||||
- `assets/images/portrait.jpg` — portrait photo for the About section
|
||||
- `assets/screenshots/bytegeist-ad.jpg` — Windows Server / AD lab screenshot
|
||||
- `assets/screenshots/bytegeist-support.jpg` — Support Lab app screenshot
|
||||
- `assets/screenshots/linux-docker.jpg` — Grafana / Portainer / Uptime Kuma
|
||||
- `assets/screenshots/aws-tracker.jpg` — AWS Cloud Study Tracker UI
|
||||
|
||||
Keep images reasonably sized (e.g. 1600px wide max, JPEG quality ~80, or
|
||||
WebP) for fast loads.
|
||||
|
||||
## Deploy
|
||||
|
||||
### AWS Amplify (manual deploy, no Git connection required)
|
||||
|
||||
1. In the Amplify Console, choose **Host web app → Deploy without Git**.
|
||||
2. Zip the whole project folder (`index.html`, `css/`, `js/`, `assets/`)
|
||||
and drag the zip into the uploader. Name the environment e.g. `prod`.
|
||||
3. Amplify gives you an `*.amplifyapp.com` URL. To use a custom domain,
|
||||
open the app → **Domain management → Add domain**.
|
||||
|
||||
For redeploys, upload a new zip to the same environment.
|
||||
|
||||
### GitHub Pages
|
||||
|
||||
1. Push this folder to a GitHub repo (e.g. `keith-portfolio`).
|
||||
2. Repo → **Settings → Pages**.
|
||||
3. Source: **Deploy from a branch**. Branch: `main` (or `master`),
|
||||
folder: `/ (root)`. Save.
|
||||
4. GitHub publishes to `https://<username>.github.io/keith-portfolio/`.
|
||||
Allow a minute for the first build.
|
||||
|
||||
For a custom domain, add a `CNAME` file at the project root containing
|
||||
the domain, and configure DNS to point at GitHub Pages.
|
||||
|
||||
### Vercel
|
||||
|
||||
1. Install the CLI once: `npm i -g vercel`.
|
||||
2. From the project root: `vercel` (follow prompts) and then `vercel --prod`.
|
||||
|
||||
Or connect the GitHub repo in the Vercel dashboard — framework preset is
|
||||
**Other**; no build command; output directory is `.` (the repo root).
|
||||
|
||||
## Accessibility & performance notes
|
||||
|
||||
- Semantic HTML (`<header>`, `<nav>`, `<main>`, `<section>`, `<article>`,
|
||||
`<footer>`), one `<h1>`, proper heading hierarchy.
|
||||
- Visible focus outlines on links and buttons.
|
||||
- Mobile nav toggle, responsive layouts at 980px and 640px.
|
||||
- No external JS/CSS dependencies — single CSS file, single JS file.
|
||||
- `alt` text / `aria-label`s on image placeholders; update them when
|
||||
swapping placeholders for real images.
|
||||
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 315 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 64 KiB |
@@ -1,4 +0,0 @@
|
||||
# Placeholders for screenshots
|
||||
|
||||
Replace with real project screenshots. See ../../README.md for the expected
|
||||
filenames and locations.
|
||||
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 530 KiB |
|
Before Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 370 KiB |
|
Before Width: | Height: | Size: 593 KiB |
|
Before Width: | Height: | Size: 530 KiB |
@@ -1,527 +0,0 @@
|
||||
/* Keith Casko — Portfolio
|
||||
Design: enterprise IT / infrastructure (late-2000s–early-2010s refined)
|
||||
Palette: navy, steel blue, white, light gray, charcoal
|
||||
*/
|
||||
|
||||
/* ---------- Reset & base ---------- */
|
||||
*,
|
||||
*::before,
|
||||
*::after { box-sizing: border-box; }
|
||||
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
:root {
|
||||
--navy: #0b1d2e;
|
||||
--navy-2: #10263d;
|
||||
--steel: #1a6fa5;
|
||||
--steel-dark: #155a86;
|
||||
--rule: #d0d7df;
|
||||
--rule-soft: #e4e8ee;
|
||||
--bg: #f3f5f8;
|
||||
--panel: #ffffff;
|
||||
--text: #2b3540;
|
||||
--muted: #566471;
|
||||
--muted-2: #4b5663;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Segoe UI", Arial, Helvetica, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
color: var(--navy-2);
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
margin: 0 0 .55em;
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
|
||||
p { margin: 0 0 1em; }
|
||||
|
||||
a { color: var(--steel); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
a:focus-visible,
|
||||
button:focus-visible {
|
||||
outline: 2px solid var(--steel);
|
||||
outline-offset: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
img { max-width: 100%; display: block; }
|
||||
|
||||
code {
|
||||
font-family: "Consolas", "Menlo", "Courier New", monospace;
|
||||
font-size: 0.92em;
|
||||
background: #eef2f6;
|
||||
border: 1px solid var(--rule-soft);
|
||||
padding: 0 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* ---------- Layout ---------- */
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1160px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
section { padding: 64px 0; }
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid var(--steel);
|
||||
position: relative;
|
||||
}
|
||||
.section-head::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0; right: 0;
|
||||
bottom: -5px;
|
||||
height: 1px;
|
||||
background: var(--rule);
|
||||
}
|
||||
.section-head h2 { margin: 0; font-size: 26px; color: var(--navy-2); }
|
||||
.section-head .subtitle { color: var(--muted); font-size: 14px; }
|
||||
|
||||
/* ---------- Header ---------- */
|
||||
.site-header {
|
||||
background: var(--navy);
|
||||
color: #fff;
|
||||
border-bottom: 4px solid var(--steel);
|
||||
}
|
||||
.site-header .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.brand .name {
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
line-height: 1.1;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
.brand .tagline {
|
||||
display: block;
|
||||
font-size: 12.5px;
|
||||
color: #a9b8c7;
|
||||
margin-top: 4px;
|
||||
letter-spacing: .25px;
|
||||
}
|
||||
.brand .location {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
padding-left: 10px;
|
||||
border-left: 1px solid #3b556f;
|
||||
color: #8ea1b5;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.site-nav ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
}
|
||||
.site-nav a {
|
||||
display: block;
|
||||
padding: 8px 14px;
|
||||
color: #e4ecf3;
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
transition: background-color .12s ease, color .12s ease;
|
||||
}
|
||||
.site-nav a:hover { background: #16314a; text-decoration: none; color: #fff; }
|
||||
.site-nav a.active {
|
||||
background: var(--steel);
|
||||
color: #fff;
|
||||
box-shadow: inset 0 -2px 0 rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.nav-toggle {
|
||||
display: none;
|
||||
background: transparent;
|
||||
border: 1px solid #3b556f;
|
||||
color: #fff;
|
||||
padding: 6px 10px;
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ---------- Hero ---------- */
|
||||
.hero { padding: 44px 0 56px; }
|
||||
.hero-grid {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--rule);
|
||||
border-top: 3px solid var(--steel);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 6px rgba(16, 38, 61, 0.06);
|
||||
padding: 40px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr;
|
||||
gap: 40px;
|
||||
align-items: center;
|
||||
}
|
||||
.hero h1 { font-size: 34px; margin-bottom: 18px; line-height: 1.2; }
|
||||
.hero p { color: var(--muted-2); font-size: 16px; }
|
||||
.hero .lede { color: var(--text); }
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 18px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border-radius: 3px;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
line-height: 1.2;
|
||||
transition: background-color .12s ease, border-color .12s ease, color .12s ease;
|
||||
}
|
||||
.btn-primary { background: var(--steel); color: #fff; border-color: var(--steel-dark); }
|
||||
.btn-primary:hover { background: var(--steel-dark); color: #fff; text-decoration: none; }
|
||||
.btn-secondary { background: #fff; color: var(--navy-2); border-color: #c3cbd4; }
|
||||
.btn-secondary:hover { background: #eef2f6; border-color: #9fadbd; text-decoration: none; }
|
||||
|
||||
.hero-image {
|
||||
background: #e9edf1;
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
aspect-ratio: 4 / 3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.hero-image img { width: 100%; height: 100%; object-fit: cover; }
|
||||
.image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
repeating-linear-gradient(45deg, #e3e8ed 0, #e3e8ed 10px, #eef2f6 10px, #eef2f6 20px);
|
||||
color: #5a6775;
|
||||
font-size: 12.5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
letter-spacing: .2px;
|
||||
}
|
||||
|
||||
/* ---------- Projects ---------- */
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
.project-card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 2px rgba(16, 38, 61, 0.04);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
transition: border-color .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
.project-card:hover {
|
||||
border-color: #9fb6ca;
|
||||
box-shadow: 0 4px 14px rgba(16, 38, 61, 0.10);
|
||||
}
|
||||
.project-card .thumb {
|
||||
background: #e9edf1;
|
||||
aspect-ratio: 16 / 10;
|
||||
border-bottom: 1px solid var(--rule);
|
||||
overflow: hidden;
|
||||
}
|
||||
.project-card .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.project-card .thumb .image-placeholder { font-size: 12px; }
|
||||
.project-card .body { padding: 18px 18px 20px; display: flex; flex-direction: column; flex: 1; }
|
||||
.project-card h3 { font-size: 18px; margin-bottom: 10px; color: var(--navy-2); }
|
||||
.project-card:hover h3 { color: var(--steel-dark); }
|
||||
.project-card p {
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
color: var(--muted-2);
|
||||
margin-bottom: 16px;
|
||||
flex: 1;
|
||||
}
|
||||
.project-card .view-link {
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
color: var(--steel);
|
||||
align-self: flex-start;
|
||||
}
|
||||
.project-card .view-link::after { content: " \203A"; color: var(--steel); }
|
||||
|
||||
/* ---------- What I Work With ---------- */
|
||||
.skills-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 18px;
|
||||
}
|
||||
.skill-card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 4px;
|
||||
padding: 18px 20px;
|
||||
box-shadow: 0 1px 2px rgba(16, 38, 61, 0.04);
|
||||
}
|
||||
.skill-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--rule-soft);
|
||||
}
|
||||
.skill-head .ico { width: 22px; height: 22px; flex: 0 0 22px; color: var(--steel); }
|
||||
.skill-card h3 {
|
||||
font-size: 15px;
|
||||
font-family: "Segoe UI", Arial, sans-serif;
|
||||
color: var(--navy-2);
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.skill-card ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.55;
|
||||
color: var(--muted-2);
|
||||
}
|
||||
.skill-card li { padding: 2px 0; }
|
||||
|
||||
/* ---------- Troubleshooting ---------- */
|
||||
.notes-list {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 2px rgba(16, 38, 61, 0.04);
|
||||
}
|
||||
.note-item {
|
||||
display: grid;
|
||||
grid-template-columns: 44px 1fr 24px;
|
||||
gap: 18px;
|
||||
align-items: start;
|
||||
padding: 18px 22px;
|
||||
border-bottom: 1px solid var(--rule-soft);
|
||||
transition: background-color .12s ease;
|
||||
}
|
||||
.note-item:last-child { border-bottom: 0; }
|
||||
.note-item:hover { background: #f7f9fb; }
|
||||
.note-item .icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: #eaf1f7;
|
||||
color: var(--steel);
|
||||
border: 1px solid #cfdbe6;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 11px;
|
||||
font-family: "Consolas", monospace;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
.note-item h3 {
|
||||
font-family: "Segoe UI", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
margin: 0 0 4px;
|
||||
color: var(--steel);
|
||||
}
|
||||
.note-item:hover h3 { color: var(--steel-dark); }
|
||||
.note-item p { margin: 0; color: var(--muted-2); font-size: 14px; }
|
||||
.note-item ul {
|
||||
margin: 8px 0 0;
|
||||
padding-left: 20px;
|
||||
color: var(--muted-2);
|
||||
font-size: 13.5px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.note-item li { padding: 1px 0; }
|
||||
.note-item .chevron { color: #94a1ae; font-size: 20px; padding-top: 6px; }
|
||||
|
||||
/* ---------- About ---------- */
|
||||
.about-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1.4fr 1fr;
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
.about-photo {
|
||||
background: #e9edf1;
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 3px;
|
||||
aspect-ratio: 1 / 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.about-text {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--rule);
|
||||
border-top: 3px solid var(--steel);
|
||||
border-radius: 4px;
|
||||
padding: 22px 24px;
|
||||
}
|
||||
.about-text h3 { font-size: 19px; margin-bottom: 12px; }
|
||||
.pull-quote {
|
||||
background: #eef2f6;
|
||||
border: 1px solid var(--rule);
|
||||
border-left: 3px solid var(--steel);
|
||||
border-radius: 3px;
|
||||
padding: 20px 22px;
|
||||
font-style: italic;
|
||||
color: var(--text);
|
||||
font-size: 14.5px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.pull-quote .mark {
|
||||
font-family: Georgia, serif;
|
||||
font-size: 34px;
|
||||
color: var(--steel);
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
top: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* ---------- Education / Experience ---------- */
|
||||
.two-col {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24px;
|
||||
}
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--rule);
|
||||
border-top: 3px solid var(--steel);
|
||||
border-radius: 4px;
|
||||
padding: 22px 26px;
|
||||
box-shadow: 0 1px 2px rgba(16, 38, 61, 0.04);
|
||||
}
|
||||
.panel h3 { font-size: 18px; margin-bottom: 6px; }
|
||||
.panel .meta { color: var(--muted); font-size: 13.5px; margin-bottom: 14px; }
|
||||
.panel h4 {
|
||||
font-family: "Segoe UI", Arial, sans-serif;
|
||||
font-size: 13.5px;
|
||||
color: var(--navy-2);
|
||||
margin: 16px 0 6px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .4px;
|
||||
}
|
||||
.panel ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--muted-2);
|
||||
}
|
||||
.panel li { padding: 2px 0; }
|
||||
|
||||
/* ---------- Contact banner ---------- */
|
||||
.contact-cta {
|
||||
background: var(--navy);
|
||||
color: #e4ecf3;
|
||||
border-radius: 4px;
|
||||
border-top: 3px solid var(--steel);
|
||||
padding: 28px 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
.contact-cta h2 { color: #fff; margin: 0 0 6px; font-size: 22px; }
|
||||
.contact-cta p { margin: 0; color: #a9b8c7; font-size: 14.5px; }
|
||||
|
||||
/* ---------- Footer ---------- */
|
||||
.site-footer {
|
||||
background: var(--navy);
|
||||
color: #a9b8c7;
|
||||
padding: 24px 0;
|
||||
margin-top: 48px;
|
||||
border-top: 4px solid var(--steel);
|
||||
font-size: 13.5px;
|
||||
}
|
||||
.site-footer .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.site-footer a { color: #cfd8e1; }
|
||||
.site-footer a:hover { color: #fff; }
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* ---------- Responsive ---------- */
|
||||
@media (max-width: 980px) {
|
||||
.hero-grid { grid-template-columns: 1fr; padding: 32px; }
|
||||
.projects-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.skills-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.about-grid { grid-template-columns: 1fr; }
|
||||
.two-col { grid-template-columns: 1fr; }
|
||||
.contact-cta { flex-direction: column; align-items: flex-start; }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
body { font-size: 15.5px; }
|
||||
section { padding: 44px 0; }
|
||||
.hero { padding: 28px 0 36px; }
|
||||
.hero-grid { padding: 22px; }
|
||||
.hero h1 { font-size: 27px; }
|
||||
.section-head h2 { font-size: 22px; }
|
||||
.projects-grid { grid-template-columns: 1fr; }
|
||||
.skills-grid { grid-template-columns: 1fr; }
|
||||
.note-item { grid-template-columns: 36px 1fr; padding: 16px; }
|
||||
.note-item .chevron { display: none; }
|
||||
|
||||
.brand .location { display: block; margin: 4px 0 0; padding-left: 0; border-left: 0; }
|
||||
.nav-toggle { display: inline-block; }
|
||||
.site-header .container { flex-wrap: wrap; }
|
||||
.site-nav { flex-basis: 100%; display: none; }
|
||||
.site-nav.open { display: block; }
|
||||
.site-nav ul { flex-direction: column; gap: 2px; }
|
||||
.site-nav a { padding: 10px 12px; }
|
||||
|
||||
.section-head { flex-direction: column; align-items: flex-start; gap: 4px; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Sun Jun 21 01:48:58 PM UTC 2026
|
||||
@@ -3,518 +3,420 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Keith Casko — IT Support, Systems Administration, Cloud Operations</title>
|
||||
<meta name="description" content="Keith Casko — IT portfolio: Windows Server, Active Directory, Linux, AWS, Docker, networking, and troubleshooting projects. Based in Kalamazoo, Michigan." />
|
||||
<link rel="stylesheet" href="css/styles.css" />
|
||||
<meta name="description" content="ByteGeist infrastructure control plane — a live view into Keith Casko's self-hosted cloud environment." />
|
||||
<title>ByteGeist / Control Plane</title>
|
||||
<link rel="icon" href="assets/images/bytegeist-logo.png" />
|
||||
<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=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src="script.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="scanline" aria-hidden="true"></div>
|
||||
|
||||
<!-- ============ Header ============ -->
|
||||
<header class="site-header" role="banner">
|
||||
<div class="container">
|
||||
<div class="brand">
|
||||
<span class="name">Keith Casko</span>
|
||||
<span class="tagline">
|
||||
IT Support | Systems Administration | Cloud Operations
|
||||
<span class="location">Kalamazoo, MI</span>
|
||||
</span>
|
||||
</div>
|
||||
<button class="nav-toggle" aria-expanded="false" aria-controls="primary-nav">Menu</button>
|
||||
<nav class="site-nav" id="primary-nav" aria-label="Primary">
|
||||
<ul>
|
||||
<li><a href="#home" class="active">Home</a></li>
|
||||
<li><a href="#projects">Projects</a></li>
|
||||
<li><a href="#homelab">Homelab</a></li>
|
||||
<li><a href="#troubleshooting">Troubleshooting</a></li>
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#resume">Resume</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<header class="control-bar">
|
||||
<a class="wordmark" href="#overview" aria-label="ByteGeist control plane">
|
||||
<img src="assets/images/bytegeist-logo.png" alt="" />
|
||||
<span>BYTEGEIST</span>
|
||||
<small>/ CONTROL PLANE</small>
|
||||
</a>
|
||||
|
||||
<div class="control-state">
|
||||
<span class="status-dot"></span>
|
||||
<span>INFRASTRUCTURE NOMINAL</span>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Control plane navigation">
|
||||
<a href="#overview">Overview</a>
|
||||
<a href="#topology">Topology</a>
|
||||
<a href="#observability">Observability</a>
|
||||
<a href="#services">Services</a>
|
||||
<a href="#security">Security</a>
|
||||
</nav>
|
||||
|
||||
<div class="clock" aria-label="Current local time">
|
||||
<span id="local-time">--:--:--</span>
|
||||
<small>AMERICA / NEW YORK</small>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section id="overview" class="command-grid">
|
||||
<aside class="system-rail" aria-label="System status">
|
||||
<div class="rail-heading">
|
||||
<span>NODE</span>
|
||||
<strong>BG-CORE-01</strong>
|
||||
</div>
|
||||
|
||||
<!-- ============ Hero ============ -->
|
||||
<section class="hero" id="home" aria-labelledby="hero-title">
|
||||
<div class="container">
|
||||
<div class="hero-grid">
|
||||
<dl class="rail-data">
|
||||
<div><dt>Provider</dt><dd>Hetzner</dd></div>
|
||||
<div><dt>OS</dt><dd>Ubuntu 24.04</dd></div>
|
||||
<div><dt>Runtime</dt><dd>Docker</dd></div>
|
||||
<div><dt>Region</dt><dd>US-East</dd></div>
|
||||
<div><dt>Ingress</dt><dd>NPM / TLS</dd></div>
|
||||
<div><dt>Identity</dt><dd>Authentik</dd></div>
|
||||
</dl>
|
||||
|
||||
<div class="rail-health">
|
||||
<span>HOST HEALTH</span>
|
||||
<div class="health-bar"><i style="--value: 92%"></i></div>
|
||||
<strong>92%</strong>
|
||||
</div>
|
||||
|
||||
<a href="https://wiki.casko.dev">OPEN RUNBOOKS <span>↗</span></a>
|
||||
<a href="https://git.casko.dev">GIT SERVER <span>↗</span></a>
|
||||
</aside>
|
||||
|
||||
<div class="overview-console">
|
||||
<div class="console-label">
|
||||
<span>OVERVIEW / LIVE</span>
|
||||
<span id="refresh-indicator">UPDATED 00s AGO</span>
|
||||
</div>
|
||||
|
||||
<div class="identity-block">
|
||||
<div>
|
||||
<h1 id="hero-title">Building Reliable Systems.<br />Solving Real Problems.</h1>
|
||||
<p class="lede">
|
||||
Hands-on experience with Windows Server, Active Directory, Linux, AWS,
|
||||
networking, Docker, monitoring, remote administration, and technical
|
||||
troubleshooting.
|
||||
</p>
|
||||
<p>
|
||||
Currently completing a B.S. in Software Engineering at Western Governors
|
||||
University while building practical infrastructure and support experience
|
||||
through a dedicated homelab, AWS projects, and ongoing study of systems
|
||||
administration and cloud operations.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary" href="#projects">View Projects</a>
|
||||
<!-- REPLACE: resume PDF path -->
|
||||
<a class="btn btn-secondary" href="assets/resume/keith-casko-resume.pdf" download>Download Resume</a>
|
||||
</div>
|
||||
<p class="overline">SELF-HOSTED INFRASTRUCTURE ENVIRONMENT</p>
|
||||
<h1>Personal cloud.<br />Operational by design.</h1>
|
||||
</div>
|
||||
<div class="hero-image">
|
||||
<!-- REPLACE: assets/images/hero-rack.jpg with a real homelab / rack / workstation photo -->
|
||||
<div class="image-placeholder" role="img" aria-label="Placeholder for homelab or server rack photo">
|
||||
[ Homelab / server rack / workstation photo —<br />
|
||||
assets/images/hero-rack.jpg ]
|
||||
<p class="operator-note">
|
||||
A working infrastructure lab for identity, observability, delivery, security,
|
||||
and recovery. Built and maintained as an evolving system—not a static demo.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="metric-board" aria-label="Infrastructure metrics">
|
||||
<article class="metric metric-primary">
|
||||
<span>SERVICES ONLINE</span>
|
||||
<strong data-count="20">20</strong><small>/ 20</small>
|
||||
<div class="spark-bars" aria-hidden="true">
|
||||
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
|
||||
</div>
|
||||
</article>
|
||||
<article class="metric">
|
||||
<span>UPTIME</span>
|
||||
<strong>99.98</strong><small>%</small>
|
||||
<p>30 DAY WINDOW</p>
|
||||
</article>
|
||||
<article class="metric">
|
||||
<span>CONTAINERS</span>
|
||||
<strong data-count="26">26</strong>
|
||||
<p>26 RUNNING / 0 DEGRADED</p>
|
||||
</article>
|
||||
<article class="metric">
|
||||
<span>ACTIVE DOMAINS</span>
|
||||
<strong data-count="11">11</strong>
|
||||
<p>TLS VALID / PROXIED</p>
|
||||
</article>
|
||||
<article class="metric">
|
||||
<span>CPU LOAD</span>
|
||||
<strong>16.2</strong><small>%</small>
|
||||
<div class="meter"><i style="--value: 16.2%"></i></div>
|
||||
</article>
|
||||
<article class="metric">
|
||||
<span>MEMORY</span>
|
||||
<strong>61.6</strong><small>%</small>
|
||||
<div class="meter warning"><i style="--value: 61.6%"></i></div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="operations-window">
|
||||
<div class="window-bar">
|
||||
<span><i class="live-dot"></i> GRAFANA / BYTEGEIST OPERATIONS</span>
|
||||
<a href="assets/images/portfolio-grafana-operations.png" target="_blank">FULL RES ↗</a>
|
||||
</div>
|
||||
<img src="assets/images/portfolio-grafana-operations.png" alt="Grafana operations dashboard showing ByteGeist infrastructure health" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="activity-panel">
|
||||
<div class="panel-title">
|
||||
<span>EVENT STREAM</span>
|
||||
<small>LIVE</small>
|
||||
</div>
|
||||
<ol class="event-list">
|
||||
<li>
|
||||
<time>22:14:08</time>
|
||||
<span class="event-ok">DEPLOY</span>
|
||||
<p>portfolio/main completed</p>
|
||||
</li>
|
||||
<li>
|
||||
<time>21:48:32</time>
|
||||
<span class="event-info">AUTH</span>
|
||||
<p>OIDC session issued</p>
|
||||
</li>
|
||||
<li>
|
||||
<time>20:03:17</time>
|
||||
<span class="event-ok">BACKUP</span>
|
||||
<p>volume snapshot verified</p>
|
||||
</li>
|
||||
<li>
|
||||
<time>18:42:55</time>
|
||||
<span class="event-warn">SEC</span>
|
||||
<p>CrowdSec decision added</p>
|
||||
</li>
|
||||
<li>
|
||||
<time>16:11:09</time>
|
||||
<span class="event-info">DNS</span>
|
||||
<p>certificate renewed</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="deployment-status">
|
||||
<span>LAST DEPLOYMENT</span>
|
||||
<strong>SUCCESS</strong>
|
||||
<p>Woodpecker pipeline #184</p>
|
||||
<div><i></i><i></i><i></i><i></i></div>
|
||||
</div>
|
||||
|
||||
<div class="terminal-mini" aria-label="Recent terminal output">
|
||||
<p><span>$</span> docker ps --format status</p>
|
||||
<p class="terminal-output">26 containers running</p>
|
||||
<p><span>$</span> systemctl is-system-running</p>
|
||||
<p class="terminal-output">running<span class="cursor">_</span></p>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<!-- ============ Featured Projects ============ -->
|
||||
<section id="projects" aria-labelledby="projects-title">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<h2 id="projects-title">Featured Projects</h2>
|
||||
<span class="subtitle">Hands-on infrastructure, support, and cloud work</span>
|
||||
<section id="topology" class="module topology-module">
|
||||
<header class="module-header">
|
||||
<div>
|
||||
<span class="module-id">SYS.01</span>
|
||||
<h2>Infrastructure topology</h2>
|
||||
</div>
|
||||
<p>Traffic, identity, workloads, telemetry, and recovery paths on the current host.</p>
|
||||
<div class="module-state"><i></i> DOCUMENTED</div>
|
||||
</header>
|
||||
|
||||
<div class="projects-grid">
|
||||
|
||||
<article class="project-card">
|
||||
<div class="thumb">
|
||||
<!-- REPLACE: assets/screenshots/bytegeist-ad.jpg -->
|
||||
<div class="image-placeholder" role="img" aria-label="ByteGeist Windows Server Lab screenshot placeholder">
|
||||
Windows Server / AD screenshot
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<h3>ByteGeist Windows Server Lab</h3>
|
||||
<p>
|
||||
A hands-on Windows Server 2022 and Active Directory domain used to
|
||||
practice help desk and junior sysadmin workflows: OUs, users and
|
||||
groups, domain joins, file shares, permissions, and DNS
|
||||
troubleshooting.
|
||||
</p>
|
||||
<!-- Public profile; swap for a dedicated repo once published -->
|
||||
<a class="view-link" href="https://github.com/kcasko" rel="noopener">View Project</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="project-card">
|
||||
<div class="thumb">
|
||||
<img src="assets/screenshots/bytegeist-support-live.png"
|
||||
alt="ByteGeist Support Lab — live app screenshot" />
|
||||
</div>
|
||||
<div class="body">
|
||||
<h3>ByteGeist Support Lab</h3>
|
||||
<p>
|
||||
An interactive IT troubleshooting application on AWS (Amplify,
|
||||
Lambda, API Gateway, DynamoDB) with React. Realistic support
|
||||
incidents, command-line workflows, diagnosis, resolution, and
|
||||
scoring.
|
||||
</p>
|
||||
<a class="view-link" href="https://supportlab.casko.dev/" rel="noopener">View Live Site</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="project-card">
|
||||
<div class="thumb">
|
||||
<!-- REPLACE: assets/screenshots/linux-docker.jpg -->
|
||||
<div class="image-placeholder" role="img" aria-label="Linux and Docker homelab screenshot placeholder">
|
||||
Docker / monitoring screenshot
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">
|
||||
<h3>Linux & Docker Homelab</h3>
|
||||
<p>
|
||||
A self-hosted Ubuntu and WSL2 environment running Docker,
|
||||
Portainer, Nextcloud, and monitoring tools behind an Nginx reverse
|
||||
proxy with Cloudflare-managed TLS. Used to deploy, monitor, and
|
||||
troubleshoot services.
|
||||
</p>
|
||||
<a class="view-link" href="https://github.com/kcasko/taurustech-stack" rel="noopener">View Project</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="project-card">
|
||||
<div class="thumb">
|
||||
<img src="assets/screenshots/aws-tracker.png"
|
||||
alt="AWS Cloud Study Tracker — live app screenshot" />
|
||||
</div>
|
||||
<div class="body">
|
||||
<h3>AWS Cloud Study Tracker</h3>
|
||||
<p>
|
||||
A serverless AWS CRUD application built with Amplify, API Gateway,
|
||||
Lambda, DynamoDB, and IAM. Used to practice AWS serverless
|
||||
architecture, permissions, and least-privilege access.
|
||||
</p>
|
||||
<a class="view-link" href="https://production.d2kf0c9e1fogq3.amplifyapp.com/" rel="noopener">View Live Site</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ What I Work With ============ -->
|
||||
<section id="homelab" aria-labelledby="skills-title">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<h2 id="skills-title">What I Work With</h2>
|
||||
<span class="subtitle">Tools, platforms, and areas of practical experience</span>
|
||||
</div>
|
||||
|
||||
<div class="skills-grid">
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M3 5.5 11 4v8H3V5.5zM13 3.75 21 2.5V12h-8V3.75zM3 13h8v7L3 18.5V13zm10 0h8v8.5L13 20v-7z"/>
|
||||
</svg>
|
||||
<h3>Windows & Active Directory</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Windows 10 / 11</li>
|
||||
<li>Windows Server 2022</li>
|
||||
<li>Active Directory</li>
|
||||
<li>Users & groups, OUs</li>
|
||||
<li>Domain joins</li>
|
||||
<li>File shares & permissions</li>
|
||||
<li>DNS</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="3" y="4" width="18" height="16" rx="1.5"/>
|
||||
<path d="m7 9 3 3-3 3M12 15h5"/>
|
||||
</svg>
|
||||
<h3>Linux</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Ubuntu</li>
|
||||
<li>WSL2</li>
|
||||
<li>Bash</li>
|
||||
<li>SSH</li>
|
||||
<li>Linux administration</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M7 18a4 4 0 0 1-.9-7.9A6 6 0 0 1 18 10.5a3.5 3.5 0 0 1-.5 7H7z"/>
|
||||
</svg>
|
||||
<h3>AWS</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>EC2, S3, IAM</li>
|
||||
<li>Lambda</li>
|
||||
<li>API Gateway</li>
|
||||
<li>DynamoDB</li>
|
||||
<li>Amplify</li>
|
||||
<li>Systems Manager</li>
|
||||
<li>KMS</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="12" cy="4" r="2"/>
|
||||
<circle cx="4" cy="20" r="2"/>
|
||||
<circle cx="20" cy="20" r="2"/>
|
||||
<path d="M12 6v6M12 12l-7 6M12 12l7 6"/>
|
||||
</svg>
|
||||
<h3>Networking</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>TCP/IP, IPv4 / IPv6</li>
|
||||
<li>DNS, DHCP</li>
|
||||
<li>SSH, HTTP/HTTPS, TLS</li>
|
||||
<li>VPNs</li>
|
||||
<li>Reverse proxies</li>
|
||||
<li>Firewalls</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M4 14v-2a8 8 0 1 1 16 0v2"/>
|
||||
<rect x="2.5" y="14" width="4.5" height="6" rx="1"/>
|
||||
<rect x="17" y="14" width="4.5" height="6" rx="1"/>
|
||||
<path d="M17 20a3 3 0 0 1-3 3h-2"/>
|
||||
</svg>
|
||||
<h3>IT Support</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Troubleshooting</li>
|
||||
<li>Customer & remote support</li>
|
||||
<li>Technical communication</li>
|
||||
<li>Incident resolution</li>
|
||||
<li>Permissions & authentication</li>
|
||||
<li>Connectivity testing</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="3" y="4" width="18" height="5" rx="1"/>
|
||||
<rect x="3" y="11" width="18" height="5" rx="1"/>
|
||||
<rect x="3" y="18" width="18" height="3" rx="1"/>
|
||||
<path d="M6.5 6.5h.01M6.5 13.5h.01"/>
|
||||
</svg>
|
||||
<h3>Infrastructure</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>Docker, Docker Compose</li>
|
||||
<li>VirtualBox</li>
|
||||
<li>Portainer</li>
|
||||
<li>Grafana, Prometheus</li>
|
||||
<li>Loki</li>
|
||||
<li>Uptime Kuma</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="2.5" y="4" width="19" height="12" rx="1.5"/>
|
||||
<path d="M8 20h8M12 16v4"/>
|
||||
</svg>
|
||||
<h3>Remote Administration</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>RDP</li>
|
||||
<li>Parsec</li>
|
||||
<li>Tailscale</li>
|
||||
<li>RealVNC</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="skill-card">
|
||||
<div class="skill-head">
|
||||
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="m8 7-5 5 5 5M16 7l5 5-5 5M14 5l-4 14"/>
|
||||
</svg>
|
||||
<h3>Scripting & Tools</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>PowerShell</li>
|
||||
<li>Bash</li>
|
||||
<li>Git</li>
|
||||
<li>GitHub</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ Troubleshooting Notes ============ -->
|
||||
<section id="troubleshooting" aria-labelledby="notes-title">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<h2 id="notes-title">Troubleshooting Notes</h2>
|
||||
<span class="subtitle">Short, practical case studies</span>
|
||||
</div>
|
||||
|
||||
<div class="notes-list">
|
||||
|
||||
<article class="note-item">
|
||||
<div class="icon" aria-hidden="true">DNS</div>
|
||||
<div>
|
||||
<h3>DNS Resolution Issue</h3>
|
||||
<p>Client cannot reach a service by name but can reach it by IP — isolate DNS from general network connectivity.</p>
|
||||
<ul>
|
||||
<li>Confirm TCP/IP configuration with <code>ipconfig /all</code></li>
|
||||
<li>Verify DNS server settings and search suffix</li>
|
||||
<li>Test reachability with <code>ping</code> against both hostname and IP</li>
|
||||
<li>Use <code>nslookup</code> against the primary and a known-good DNS server</li>
|
||||
<li>Flush client resolver cache; compare behavior from another host</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chevron" aria-hidden="true">›</div>
|
||||
</article>
|
||||
|
||||
<article class="note-item">
|
||||
<div class="icon" aria-hidden="true">DKR</div>
|
||||
<div>
|
||||
<h3>Docker Networking Problem</h3>
|
||||
<p>A container responds locally but is unreachable from other hosts, or outbound requests fail.</p>
|
||||
<ul>
|
||||
<li>Check container state with <code>docker ps</code> and <code>docker logs</code></li>
|
||||
<li>Verify published ports and the attached Docker network</li>
|
||||
<li>Confirm DNS resolution from inside the container</li>
|
||||
<li>Check host firewall rules affecting the bridge or published ports</li>
|
||||
<li>Test HTTP endpoints with <code>curl -v</code> from host and container</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chevron" aria-hidden="true">›</div>
|
||||
</article>
|
||||
|
||||
<article class="note-item">
|
||||
<div class="icon" aria-hidden="true">AD</div>
|
||||
<div>
|
||||
<h3>Active Directory Permissions Issue</h3>
|
||||
<p>A user authenticates successfully but cannot access a shared folder they should.</p>
|
||||
<ul>
|
||||
<li>Confirm the user's identity and group membership</li>
|
||||
<li>Review share permissions and NTFS permissions separately</li>
|
||||
<li>Check for deny ACEs that override group access</li>
|
||||
<li>Validate authentication path and session with <code>whoami /groups</code></li>
|
||||
<li>Test access from a known-good account to isolate user vs. share</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chevron" aria-hidden="true">›</div>
|
||||
</article>
|
||||
|
||||
<article class="note-item">
|
||||
<div class="icon" aria-hidden="true">IAM</div>
|
||||
<div>
|
||||
<h3>AWS IAM Access Problem</h3>
|
||||
<p>A workload or user receives <em>AccessDenied</em> when calling an AWS service.</p>
|
||||
<ul>
|
||||
<li>Identify the exact action, resource, and principal from the error</li>
|
||||
<li>Review attached identity and resource policies against least privilege</li>
|
||||
<li>Check for explicit denies, SCPs, and permission boundaries</li>
|
||||
<li>Reproduce with the IAM Policy Simulator</li>
|
||||
<li>Grant only the specific action needed; verify, then narrow further</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chevron" aria-hidden="true">›</div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ About ============ -->
|
||||
<section id="about" aria-labelledby="about-title">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<h2 id="about-title">About</h2>
|
||||
<span class="subtitle">Background and focus</span>
|
||||
</div>
|
||||
|
||||
<div class="about-grid">
|
||||
<div class="about-photo">
|
||||
<!-- REPLACE: assets/images/portrait.jpg -->
|
||||
<div class="image-placeholder" role="img" aria-label="Portrait photo placeholder">
|
||||
[ Portrait photo —<br />assets/images/portrait.jpg ]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="about-text">
|
||||
<h3>Keith Casko — Kalamazoo, Michigan</h3>
|
||||
<p>
|
||||
I'm transitioning into IT, cloud, and systems administration while
|
||||
completing a B.S. in Software Engineering at Western Governors
|
||||
University. My focus is practical, hands-on infrastructure work:
|
||||
Windows Server and Active Directory, Linux, AWS, Docker, networking,
|
||||
and technical troubleshooting.
|
||||
</p>
|
||||
<p>
|
||||
My professional background is at Costco Wholesale as a Baker. The job
|
||||
is production-paced and team-based, and it's where I developed the
|
||||
habits I bring to IT work — customer service, teamwork, training
|
||||
coworkers, process discipline, prioritization, and real-world
|
||||
problem-solving under pressure.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<aside class="pull-quote" aria-label="Approach">
|
||||
<span class="mark">“</span>
|
||||
Reliable systems are built one careful change at a time. I'd rather
|
||||
understand a problem than paper over it — and I document what I
|
||||
learn so the next person has a shorter path.
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ Education & Experience ============ -->
|
||||
<section id="resume" aria-labelledby="resume-title">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<h2 id="resume-title">Education & Experience</h2>
|
||||
<span class="subtitle">Current study and professional background</span>
|
||||
</div>
|
||||
|
||||
<div class="two-col">
|
||||
|
||||
<div class="panel">
|
||||
<h3>Western Governors University</h3>
|
||||
<div class="meta">Bachelor of Science, Software Engineering · In Progress</div>
|
||||
<h4>Relevant Study</h4>
|
||||
<ul>
|
||||
<li>Cloud Computing</li>
|
||||
<li>Networking</li>
|
||||
<li>IT Operations</li>
|
||||
<li>Software Development</li>
|
||||
<li>Java</li>
|
||||
<li>Web Development</li>
|
||||
<li>Security Fundamentals</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<h3>Costco Wholesale — Baker</h3>
|
||||
<div class="meta">June 2015 – Present</div>
|
||||
<h4>Transferable Experience</h4>
|
||||
<ul>
|
||||
<li>High-volume production environment</li>
|
||||
<li>Accuracy and process discipline</li>
|
||||
<li>Prioritization under time pressure</li>
|
||||
<li>Day-to-day troubleshooting of equipment and workflows</li>
|
||||
<li>Coordinating with team members and supervisors</li>
|
||||
<li>Training coworkers on procedures</li>
|
||||
<li>Customer service and clear communication</li>
|
||||
<li>Problem solving and follow-through</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ Contact ============ -->
|
||||
<section id="contact" aria-labelledby="contact-title">
|
||||
<div class="container">
|
||||
<div class="contact-cta">
|
||||
<div class="topology-layout">
|
||||
<div class="stack-index">
|
||||
<div>
|
||||
<h2 id="contact-title">Open to IT Support, Help Desk, and Junior SysAdmin roles</h2>
|
||||
<p>Based in Kalamazoo, Michigan — available on-site, hybrid, or remote.</p>
|
||||
<span>EDGE</span>
|
||||
<strong>Nginx Proxy Manager</strong>
|
||||
<small>TLS termination / routing</small>
|
||||
</div>
|
||||
<div>
|
||||
<!-- REPLACE: mailto address -->
|
||||
<a class="btn btn-primary" href="mailto:you@example.com">Email Keith</a>
|
||||
<span>IDENTITY</span>
|
||||
<strong>Authentik</strong>
|
||||
<small>OIDC / SSO / 2FA</small>
|
||||
</div>
|
||||
<div>
|
||||
<span>COMPUTE</span>
|
||||
<strong>Docker Compose</strong>
|
||||
<small>26 active containers</small>
|
||||
</div>
|
||||
<div>
|
||||
<span>TELEMETRY</span>
|
||||
<strong>Prometheus + Loki</strong>
|
||||
<small>Metrics / logs / alerts</small>
|
||||
</div>
|
||||
</div>
|
||||
<figure class="system-map">
|
||||
<a href="assets/images/bytegeist-architecture.png" target="_blank">
|
||||
<img src="assets/images/bytegeist-architecture.png" alt="ByteGeist infrastructure architecture diagram" />
|
||||
</a>
|
||||
<figcaption>
|
||||
<span>BYTEGEIST-CLOUD / REV 04</span>
|
||||
<span>SELECT DIAGRAM TO INSPECT</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="observability" class="module evidence-module">
|
||||
<header class="module-header">
|
||||
<div>
|
||||
<span class="module-id">OBS.02</span>
|
||||
<h2>Monitoring stack</h2>
|
||||
</div>
|
||||
<p>Host metrics, container telemetry, centralized logs, and independent uptime checks.</p>
|
||||
<div class="module-state"><i></i> COVERAGE ACTIVE</div>
|
||||
</header>
|
||||
|
||||
<div class="evidence-grid">
|
||||
<figure class="evidence evidence-wide">
|
||||
<figcaption>
|
||||
<span>LOG PIPELINE</span>
|
||||
<strong>Loki / Grafana Alloy</strong>
|
||||
<small>Container output searchable by service and host</small>
|
||||
</figcaption>
|
||||
<a href="assets/images/portfolio-grafana-logs.png" target="_blank">
|
||||
<img src="assets/images/portfolio-grafana-logs.png" alt="Centralized logs in Grafana" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<div class="coverage-matrix">
|
||||
<div class="panel-title"><span>MONITORING COVERAGE</span><small>6 / 6</small></div>
|
||||
<ul>
|
||||
<li><span>Host metrics</span><i>ACTIVE</i></li>
|
||||
<li><span>Container metrics</span><i>ACTIVE</i></li>
|
||||
<li><span>Application logs</span><i>ACTIVE</i></li>
|
||||
<li><span>Endpoint uptime</span><i>ACTIVE</i></li>
|
||||
<li><span>Resource alerts</span><i>ACTIVE</i></li>
|
||||
<li><span>Security events</span><i>ACTIVE</i></li>
|
||||
</ul>
|
||||
<div class="stack-list">
|
||||
<span>Grafana</span><span>Prometheus</span><span>Loki</span>
|
||||
<span>Alloy</span><span>Node Exporter</span><span>cAdvisor</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="services" class="module services-module">
|
||||
<header class="module-header">
|
||||
<div>
|
||||
<span class="module-id">APP.03</span>
|
||||
<h2>Service inventory</h2>
|
||||
</div>
|
||||
<p>Core applications currently exposed through the internal service portal.</p>
|
||||
<div class="module-state"><i></i> 20 ONLINE</div>
|
||||
</header>
|
||||
|
||||
<div class="service-layout">
|
||||
<div class="service-table" role="table" aria-label="Service inventory">
|
||||
<div class="service-row service-head" role="row">
|
||||
<span>Service</span><span>Role</span><span>Access</span><span>State</span>
|
||||
</div>
|
||||
<div class="service-row"><strong>Gitea</strong><span>Source control</span><span>SSO</span><i>ONLINE</i></div>
|
||||
<div class="service-row"><strong>Wiki.js</strong><span>Documentation</span><span>SSO</span><i>ONLINE</i></div>
|
||||
<div class="service-row"><strong>Dashy</strong><span>Service portal</span><span>SSO</span><i>ONLINE</i></div>
|
||||
<div class="service-row"><strong>Vaultwarden</strong><span>Secrets</span><span>DIRECT</span><i>ONLINE</i></div>
|
||||
<div class="service-row"><strong>Stirling PDF</strong><span>Document tools</span><span>SSO</span><i>ONLINE</i></div>
|
||||
<div class="service-row"><strong>IT Tools</strong><span>Utilities</span><span>SSO</span><i>ONLINE</i></div>
|
||||
<div class="service-row"><strong>Uptime Kuma</strong><span>Availability</span><span>SSO</span><i>ONLINE</i></div>
|
||||
</div>
|
||||
|
||||
<figure class="portal-shot">
|
||||
<figcaption>
|
||||
<span>SERVICE PORTAL</span>
|
||||
<small>Dashy / internal access layer</small>
|
||||
</figcaption>
|
||||
<a href="assets/images/portfolio-cloud-dashboard-overview.png" target="_blank">
|
||||
<img src="assets/images/portfolio-cloud-dashboard-overview.png" alt="ByteGeist Dashy service portal" />
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="module devops-module">
|
||||
<header class="module-header">
|
||||
<div>
|
||||
<span class="module-id">DEV.04</span>
|
||||
<h2>Delivery pipeline</h2>
|
||||
</div>
|
||||
<p>Git-based workflows from repository change to automated deployment.</p>
|
||||
<div class="module-state"><i></i> LAST RUN PASSED</div>
|
||||
</header>
|
||||
|
||||
<div class="pipeline">
|
||||
<div class="pipeline-flow" aria-label="Deployment pipeline">
|
||||
<div><span>01</span><strong>COMMIT</strong><small>Gitea repository</small></div>
|
||||
<b>→</b>
|
||||
<div><span>02</span><strong>BUILD</strong><small>Woodpecker runner</small></div>
|
||||
<b>→</b>
|
||||
<div><span>03</span><strong>VERIFY</strong><small>Static checks</small></div>
|
||||
<b>→</b>
|
||||
<div><span>04</span><strong>DEPLOY</strong><small>Docker host</small></div>
|
||||
</div>
|
||||
|
||||
<div class="pipeline-shots">
|
||||
<figure>
|
||||
<figcaption>GITEA / REPOSITORIES</figcaption>
|
||||
<a href="assets/images/portfolio-gitea-dashboard.png" target="_blank">
|
||||
<img src="assets/images/portfolio-gitea-dashboard.png" alt="Gitea repositories dashboard" />
|
||||
</a>
|
||||
</figure>
|
||||
<figure>
|
||||
<figcaption>WOODPECKER / PIPELINE</figcaption>
|
||||
<a href="assets/images/portfolio-woodpecker-pipeline.png" target="_blank">
|
||||
<img src="assets/images/portfolio-woodpecker-pipeline.png" alt="Successful Woodpecker CI pipeline" />
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="security" class="module security-module">
|
||||
<header class="module-header">
|
||||
<div>
|
||||
<span class="module-id">SEC.05</span>
|
||||
<h2>Identity & perimeter</h2>
|
||||
</div>
|
||||
<p>Central authentication, protected services, and automated hostile-traffic decisions.</p>
|
||||
<div class="module-state"><i></i> ENFORCING</div>
|
||||
</header>
|
||||
|
||||
<div class="security-layout">
|
||||
<figure class="auth-shot">
|
||||
<figcaption>
|
||||
<span>AUTHENTIK</span>
|
||||
<small>11 protected applications / 2FA enforced</small>
|
||||
</figcaption>
|
||||
<a href="assets/images/portfolio-authentik-applications.png" target="_blank">
|
||||
<img src="assets/images/portfolio-authentik-applications.png" alt="Applications protected by Authentik" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<div class="security-data">
|
||||
<div class="security-score">
|
||||
<span>ACCESS POSTURE</span>
|
||||
<strong>HARDENED</strong>
|
||||
<small>KEY-ONLY SSH / SSO / TLS</small>
|
||||
</div>
|
||||
<ul>
|
||||
<li><span>OIDC providers</span><strong>04</strong></li>
|
||||
<li><span>Forward-auth apps</span><strong>07</strong></li>
|
||||
<li><span>2FA policy</span><strong>ENFORCED</strong></li>
|
||||
<li><span>Public SSH password</span><strong>DISABLED</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<figure class="crowdsec-shot">
|
||||
<figcaption>
|
||||
<span>CROWDSEC / ACTIVE DECISIONS</span>
|
||||
<small>Automated hostile source blocking</small>
|
||||
</figcaption>
|
||||
<a href="assets/images/portfolio-crowdsec-decisions.png" target="_blank">
|
||||
<img src="assets/images/portfolio-crowdsec-decisions.png" alt="CrowdSec active firewall decisions" />
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="module recovery-module">
|
||||
<header class="module-header">
|
||||
<div>
|
||||
<span class="module-id">DR.06</span>
|
||||
<h2>Recovery state</h2>
|
||||
</div>
|
||||
<p>Configuration in Git, persistent data backups, and an explicit rebuild sequence.</p>
|
||||
<div class="module-state"><i></i> RUNBOOK CURRENT</div>
|
||||
</header>
|
||||
|
||||
<div class="recovery-layout">
|
||||
<div class="recovery-sequence">
|
||||
<div><span>01</span><strong>Provision host</strong><small>Ubuntu / firewall / SSH</small></div>
|
||||
<div><span>02</span><strong>Restore config</strong><small>Git / Compose / secrets</small></div>
|
||||
<div><span>03</span><strong>Restore data</strong><small>Volumes / databases</small></div>
|
||||
<div><span>04</span><strong>Validate edge</strong><small>DNS / TLS / identity</small></div>
|
||||
</div>
|
||||
<figure>
|
||||
<a href="assets/images/portfolio-wiki-disaster-recovery.png" target="_blank">
|
||||
<img src="assets/images/portfolio-wiki-disaster-recovery.png" alt="ByteGeist disaster recovery runbook" />
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- ============ Footer ============ -->
|
||||
<footer class="site-footer" role="contentinfo">
|
||||
<div class="container">
|
||||
<div>© <span id="year">2026</span> Keith Casko. All rights reserved.</div>
|
||||
<ul class="footer-links">
|
||||
<!-- REPLACE: resume path -->
|
||||
<li><a href="assets/resume/keith-casko-resume.pdf">Resume</a></li>
|
||||
<li><a href="https://github.com/kcasko" rel="noopener">GitHub</a></li>
|
||||
<!-- REPLACE: LinkedIn URL -->
|
||||
<li><a href="https://www.linkedin.com/in/REPLACE_ME" rel="noopener">LinkedIn</a></li>
|
||||
<!-- REPLACE: email -->
|
||||
<li><a href="mailto:you@example.com">Email</a></li>
|
||||
</ul>
|
||||
<footer>
|
||||
<div class="footer-mark">
|
||||
<img src="assets/images/bytegeist-logo.png" alt="" />
|
||||
<div><strong>BYTEGEIST</strong><span>KEITH CASKO / INFRASTRUCTURE LAB</span></div>
|
||||
</div>
|
||||
<p>PLAIN HTML / CSS / JAVASCRIPT</p>
|
||||
<p>STATUS: <span class="status-text">OPERATIONAL</span></p>
|
||||
</footer>
|
||||
|
||||
<script src="js/main.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Minimal JS: mobile nav toggle + active section highlight.
|
||||
(function () {
|
||||
var toggle = document.querySelector('.nav-toggle');
|
||||
var nav = document.querySelector('.site-nav');
|
||||
if (toggle && nav) {
|
||||
toggle.addEventListener('click', function () {
|
||||
var open = nav.classList.toggle('open');
|
||||
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
});
|
||||
nav.addEventListener('click', function (e) {
|
||||
if (e.target.tagName === 'A' && window.innerWidth <= 640) {
|
||||
nav.classList.remove('open');
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set current year in footer
|
||||
var y = document.getElementById('year');
|
||||
if (y) y.textContent = new Date().getFullYear();
|
||||
})();
|
||||
@@ -0,0 +1,42 @@
|
||||
const timeElement = document.querySelector("#local-time");
|
||||
const refreshElement = document.querySelector("#refresh-indicator");
|
||||
const counters = document.querySelectorAll("[data-count]");
|
||||
|
||||
function updateClock() {
|
||||
const now = new Date();
|
||||
timeElement.textContent = new Intl.DateTimeFormat("en-US", {
|
||||
timeZone: "America/New_York",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false,
|
||||
}).format(now);
|
||||
}
|
||||
|
||||
let secondsSinceRefresh = 0;
|
||||
|
||||
function updateRefreshState() {
|
||||
refreshElement.textContent = `UPDATED ${String(secondsSinceRefresh).padStart(2, "0")}s AGO`;
|
||||
secondsSinceRefresh = (secondsSinceRefresh + 1) % 60;
|
||||
}
|
||||
|
||||
function animateCounters() {
|
||||
counters.forEach((counter) => {
|
||||
const target = Number(counter.dataset.count);
|
||||
let current = 0;
|
||||
const increment = Math.max(1, Math.ceil(target / 18));
|
||||
|
||||
const timer = window.setInterval(() => {
|
||||
current = Math.min(target, current + increment);
|
||||
counter.textContent = current;
|
||||
if (current === target) window.clearInterval(timer);
|
||||
}, 45);
|
||||
});
|
||||
}
|
||||
|
||||
updateClock();
|
||||
updateRefreshState();
|
||||
animateCounters();
|
||||
|
||||
window.setInterval(updateClock, 1000);
|
||||
window.setInterval(updateRefreshState, 1000);
|
||||