mirror of
https://github.com/asimonson1125/asimonson1125.github.io.git
synced 2026-09-22 11:35:33 -05:00
Compare commits
4 Commits
7a585d79ed
...
8432cc0bf6
| Author | SHA1 | Date | |
|---|---|---|---|
| 8432cc0bf6 | |||
| 21ebd13a07 | |||
| 78aa49c0f3 | |||
| 9fc5bfe9c7 |
@@ -62,7 +62,6 @@ def load_json(path):
|
||||
projects = load_json("./static/json/projects.json")
|
||||
books = load_json("./static/json/books.json")
|
||||
skills = load_json("./static/json/skills.json")
|
||||
timeline = load_json("./static/json/timeline.json")
|
||||
pages = load_json("./static/json/pages.json")
|
||||
|
||||
pages['projects']['skillList'] = skills
|
||||
|
||||
@@ -16,7 +16,7 @@ SERVICES = [
|
||||
# {'id': 'EternalRelays', 'name': 'eternalrelays.com', 'url': 'https://eternalrelays.com', 'timeout': 10},
|
||||
{'id': 'files', 'name': 'files.asimonson.com', 'url': 'https://files.asimonson.com', 'timeout': 10},
|
||||
{'id': 'git', 'name': 'git.asimonson.com', 'url': 'https://git.asimonson.com', 'timeout': 10},
|
||||
{'id': 'cascadalyst', 'name': 'cascadalyst.com', 'url': 'https://cascadalyst.com', 'timeout': 10},
|
||||
# {'id': 'cascadalyst', 'name': 'cascadalyst.com', 'url': 'https://cascadalyst.com', 'timeout': 10},
|
||||
]
|
||||
|
||||
CHECK_INTERVAL = 60 # seconds between checks
|
||||
|
||||
@@ -193,6 +193,16 @@ tr {
|
||||
position: unset !important;
|
||||
}
|
||||
|
||||
.growBottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.growBottom :last-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
display: inline-block;
|
||||
transition: .4s;
|
||||
@@ -551,17 +561,15 @@ tr {
|
||||
}
|
||||
|
||||
#skillTree {
|
||||
border-left: var(--accent) solid 3px;
|
||||
border-radius: 0.5em;
|
||||
background: rgba(var(--bg-card-rgb), 0.85);
|
||||
background: rgba(var(--bg-card-rgb), 0.6);
|
||||
font-size: x-large;
|
||||
margin-bottom: 10px;
|
||||
padding: 1em;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
padding: 1em 1.25em;
|
||||
}
|
||||
|
||||
.skill {
|
||||
padding: .2em;
|
||||
padding: 0.2em 0;
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
@@ -594,21 +602,26 @@ tr {
|
||||
|
||||
.skill > .skillname {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease, padding-left 0.2s ease;
|
||||
transition: color 0.2s ease;
|
||||
padding: 0.2em 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.skill > .skillname:hover {
|
||||
padding-left: 0.3em;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.skill > .skillname::after {
|
||||
content: " >";
|
||||
content: " \2192";
|
||||
font-size: 0.85em;
|
||||
margin-left: 0.2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.skill.activeSkill > .skillname::after {
|
||||
content: " <";
|
||||
content: " \2190";
|
||||
}
|
||||
|
||||
.skill[data-length="0"].activeSkill > .skillname, .skill[data-length="0"] > .skillname:hover, .skill.activeSkill > .skillname:hover {
|
||||
@@ -628,8 +641,8 @@ tr {
|
||||
/* Project List Grid */
|
||||
.projectList {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(340px, 480px));
|
||||
gap: 1.5em;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 1.25em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -637,61 +650,26 @@ tr {
|
||||
.project {
|
||||
position: relative;
|
||||
background: rgba(var(--bg-card-rgb), 0.92);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.15);
|
||||
border-left: 3px solid rgba(var(--accent-rgb), 0.75);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: 0.75em;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
/* Corner bracket: top-right */
|
||||
.project::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-top: 2px solid rgba(var(--accent-rgb), 0.5);
|
||||
border-right: 2px solid rgba(var(--accent-rgb), 0.5);
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
/* Corner bracket: bottom-left */
|
||||
.project::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 2px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-bottom: 2px solid rgba(var(--accent-rgb), 0.5);
|
||||
border-left: 2px solid rgba(var(--accent-rgb), 0.5);
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
transition: border-color 0.3s ease;
|
||||
transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.project:hover {
|
||||
border-color: rgba(var(--accent-rgb), 0.45);
|
||||
border-left-color: rgb(var(--accent-rgb));
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(var(--accent-rgb), 0.08);
|
||||
}
|
||||
|
||||
.project:hover::before,
|
||||
.project:hover::after {
|
||||
border-color: rgba(var(--accent-rgb), 0.9);
|
||||
border-color: rgba(var(--accent-rgb), 0.35);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Project Image */
|
||||
.projImageWrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
height: 180px;
|
||||
overflow: hidden;
|
||||
background: #0c0c0c;
|
||||
flex-shrink: 0;
|
||||
@@ -702,140 +680,89 @@ tr {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center top;
|
||||
filter: saturate(0.6) brightness(0.7);
|
||||
transition: filter 0.5s ease, transform 0.5s ease;
|
||||
filter: brightness(0.88);
|
||||
transition: filter 0.3s ease;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.project:hover .projImageWrap img {
|
||||
filter: saturate(0.85) brightness(0.85);
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
/* Scanlines overlay */
|
||||
.projImageWrap::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 3px,
|
||||
rgba(0, 0, 0, 0.1) 3px,
|
||||
rgba(0, 0, 0, 0.1) 4px
|
||||
);
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Gradient fade into card body */
|
||||
.projImageWrap::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 55%;
|
||||
background: linear-gradient(to bottom, transparent, rgba(var(--bg-card-rgb), 0.98));
|
||||
z-index: 3;
|
||||
filter: brightness(1);
|
||||
}
|
||||
|
||||
/* Placeholder for projects without an image */
|
||||
.projImagePlaceholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
repeating-linear-gradient(
|
||||
45deg,
|
||||
rgba(var(--accent-rgb), 0.04) 0px,
|
||||
rgba(var(--accent-rgb), 0.04) 1px,
|
||||
transparent 1px,
|
||||
transparent 8px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgba(var(--accent-rgb), 0.04) 0px,
|
||||
rgba(var(--accent-rgb), 0.04) 1px,
|
||||
transparent 1px,
|
||||
transparent 8px
|
||||
);
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.06) 25%, transparent 25%) -4px 0,
|
||||
linear-gradient(225deg, rgba(var(--accent-rgb), 0.06) 25%, transparent 25%) -4px 0,
|
||||
linear-gradient(315deg, rgba(var(--accent-rgb), 0.06) 25%, transparent 25%) 4px 0,
|
||||
linear-gradient(45deg, rgba(var(--accent-rgb), 0.06) 25%, transparent 25%) 4px 0;
|
||||
background-size: 8px 8px;
|
||||
background-color: rgba(30, 30, 30, 0.6);
|
||||
}
|
||||
|
||||
/* Status Badge */
|
||||
.proj-status-badge {
|
||||
position: absolute;
|
||||
top: 0.75em;
|
||||
right: 0.75em;
|
||||
z-index: 4;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45em;
|
||||
padding: 0.25em 0.65em 0.25em 0.5em;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.62rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid currentColor;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.proj-status-badge.complete { color: var(--status-online); }
|
||||
.proj-status-badge.WIP { color: var(--status-degraded); }
|
||||
.proj-status-badge.incomplete { color: var(--status-offline); }
|
||||
|
||||
.status-indicator {
|
||||
/* Status indicator (inline, in card body) */
|
||||
.proj-status {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
color: inherit;
|
||||
background: currentColor;
|
||||
box-shadow: 0 0 6px currentColor;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
padding: 0.15em 0.5em;
|
||||
border-radius: 0.2em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.proj-status-badge.complete .status-indicator {
|
||||
animation: statusPulse 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.proj-status-badge.WIP .status-indicator {
|
||||
animation: statusPulse 0.9s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.proj-status-badge.incomplete .status-indicator {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
@keyframes statusPulse {
|
||||
0%, 100% { opacity: 1; box-shadow: 0 0 6px currentColor; }
|
||||
50% { opacity: 0.2; box-shadow: none; }
|
||||
}
|
||||
.proj-status.complete { color: var(--status-online); background: rgba(76, 175, 80, 0.1); }
|
||||
.proj-status.WIP { color: var(--status-degraded); background: rgba(255, 193, 7, 0.1); }
|
||||
.proj-status.incomplete { color: var(--status-offline); background: rgba(244, 67, 54, 0.1); }
|
||||
|
||||
/* Project Content */
|
||||
.projContent {
|
||||
padding: 1.1em 1.4em 1.4em 1.2em;
|
||||
padding: 1.2em 1.4em 1.4em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
gap: 0.6em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.projTitleRow {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 0.75em;
|
||||
}
|
||||
|
||||
.project h3 {
|
||||
margin: 0;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-heading);
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1.3;
|
||||
display: block;
|
||||
border-bottom: none;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
/* Project category tags */
|
||||
.projTags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.proj-tag {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: capitalize;
|
||||
padding: 0.1em 0.45em;
|
||||
border-radius: 0.2em;
|
||||
color: var(--text-secondary);
|
||||
background: rgba(var(--accent-rgb), 0.06);
|
||||
}
|
||||
|
||||
.projDesc {
|
||||
font-size: 0.83rem;
|
||||
line-height: 1.65;
|
||||
color: rgba(236, 235, 235, 0.65);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
@@ -852,47 +779,29 @@ tr {
|
||||
.proj-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45em;
|
||||
padding: 0.3em 0.8em 0.3em 0.6em;
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.35);
|
||||
background: rgba(var(--accent-rgb), 0.07);
|
||||
color: rgba(236, 235, 235, 0.75);
|
||||
gap: 0.4em;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.proj-link::before {
|
||||
content: '>';
|
||||
color: rgba(var(--accent-rgb), 0.7);
|
||||
font-weight: 700;
|
||||
font-size: 0.8rem;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.proj-link:hover {
|
||||
background: rgba(var(--accent-rgb), 0.18);
|
||||
border-color: rgba(var(--accent-rgb), 0.7);
|
||||
color: #ecebeb;
|
||||
}
|
||||
|
||||
.proj-link:hover::before {
|
||||
color: rgb(var(--accent-rgb));
|
||||
color: var(--text-heading);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.proj-link img.projectLink {
|
||||
height: 0.9em;
|
||||
height: 1em;
|
||||
width: auto;
|
||||
filter: invert(1) opacity(0.75);
|
||||
filter: invert(1) opacity(0.65);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
transition: filter 0.2s ease;
|
||||
transition: filter 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.proj-link:hover img.projectLink {
|
||||
filter: invert(1);
|
||||
filter: invert(1) opacity(0.9);
|
||||
}
|
||||
|
||||
.grow {
|
||||
@@ -1004,8 +913,8 @@ tr {
|
||||
|
||||
.homeSubContent {
|
||||
background-color: rgba(var(--bg-card-rgb), 0.85);
|
||||
padding: 2em 8em;
|
||||
border-top: solid 4px rgba(var(--accent-rgb), 0.5);
|
||||
padding: 2.5em 6em;
|
||||
border-top: solid 2px rgba(var(--accent-rgb), 0.25);
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -1327,7 +1236,7 @@ tr {
|
||||
@media screen and (max-width: 1400px) {
|
||||
|
||||
.projectList {
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 420px));
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
}
|
||||
|
||||
#homepage {
|
||||
@@ -1459,9 +1368,6 @@ tr {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.projDesc {
|
||||
margin: 0;
|
||||
}
|
||||
.col {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
@@ -1550,6 +1456,145 @@ tr {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Timeline */
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
background: linear-gradient(to bottom, rgba(var(--accent-rgb), 0.05), rgba(var(--accent-rgb), 0.7) 10%, rgba(var(--accent-rgb), 0.7) 90%, rgba(var(--accent-rgb), 0.05));
|
||||
}
|
||||
|
||||
.timeitem {
|
||||
position: relative;
|
||||
margin-bottom: 1.5em;
|
||||
transition: .4s;
|
||||
overflow-y: clip;
|
||||
height: auto;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.timeitem::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -2.15em;
|
||||
top: 0.5em;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-base);
|
||||
border: 2px solid rgba(var(--accent-rgb), 0.6);
|
||||
z-index: 1;
|
||||
transition: background 0.25s ease, border-color 0.25s ease;
|
||||
}
|
||||
|
||||
.timeitem:hover::before {
|
||||
background: rgba(var(--accent-rgb), 0.4);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.timeitem-inner {
|
||||
position: relative;
|
||||
padding: 1em 1.25em;
|
||||
}
|
||||
|
||||
.timeitem-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.timeitem-header strong {
|
||||
color: var(--text-heading);
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.timeitem-header .datetext {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.timeitem-content {
|
||||
color: var(--text-body);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
.timeitem-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4em;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 0.15em 0.6em;
|
||||
border-radius: 0.25em;
|
||||
font-size: 0.7rem;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.35);
|
||||
color: var(--text-secondary);
|
||||
background: rgba(var(--accent-rgb), 0.07);
|
||||
}
|
||||
|
||||
.tag.pinned {
|
||||
border-color: rgba(var(--accent-rgb), 0.7);
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.tag.experience { color: #6cb4ee; }
|
||||
.tag.education { color: #8bc34a; }
|
||||
.tag.technical { color: #ce93d8; }
|
||||
|
||||
/* Timeline filter buttons */
|
||||
.timeline-filters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 0.35em 0.9em;
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: 0.25em;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-btn:hover, .filter-btn.active {
|
||||
border-color: rgba(var(--accent-rgb), 0.7);
|
||||
background: rgba(var(--accent-rgb), 0.12);
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.timeitem.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Summary Card */
|
||||
.summary-card {
|
||||
padding: 2em;
|
||||
@@ -1973,3 +2018,28 @@ tr {
|
||||
from { background-position: 200% 0; }
|
||||
to { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* Skip-to-content link (accessible navigation) */
|
||||
.skip-link {
|
||||
position: fixed;
|
||||
top: -100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 10000;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
border: 2px solid currentColor;
|
||||
border-radius: 0 0 4px 4px;
|
||||
white-space: nowrap;
|
||||
transition: top 0.15s ease;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
outline: 2px solid #4E9A26;
|
||||
outline-offset: 0;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@ function toggleMenu(collapse) {
|
||||
if (window.innerWidth < 1400) {
|
||||
const menu = document.querySelector(".navControl");
|
||||
const bar = document.querySelector(".header");
|
||||
const btn = document.getElementById("menu");
|
||||
const isCollapsed = !menu.style.maxHeight || menu.style.maxHeight === "0px";
|
||||
|
||||
if (isCollapsed && !collapse) {
|
||||
menu.style.maxHeight = `${menu.scrollHeight + 10}px`;
|
||||
bar.style.borderBottomWidth = "0px";
|
||||
if (btn) btn.setAttribute("aria-expanded", "true");
|
||||
} else {
|
||||
menu.style.maxHeight = "0px";
|
||||
bar.style.borderBottomWidth = "3px";
|
||||
if (btn) btn.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,10 +102,14 @@ function activeSkill(obj) {
|
||||
let skill = obj.closest(".skill");
|
||||
if (skill.classList.contains("activeSkill")) {
|
||||
skill.classList.remove("activeSkill");
|
||||
const btn = obj.closest('.skillname') || obj.querySelector?.('.skillname') || obj;
|
||||
if (btn && btn.setAttribute) btn.setAttribute("aria-expanded", "false");
|
||||
return;
|
||||
}
|
||||
while (skill) {
|
||||
skill.classList.add("activeSkill");
|
||||
const nameEl = skill.querySelector?.(':scope > .skillname') || skill.querySelector('.skillname');
|
||||
if (nameEl) nameEl.setAttribute("aria-expanded", "true");
|
||||
skill = skill.parentElement.closest(".skill");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"home": {
|
||||
"template": "home.html",
|
||||
"title": "Andrew Simonson - Portfolio Home",
|
||||
"description": "Andrew Simonson's Digital Portfolio home",
|
||||
"title": "Andrew Simonson - Data Scientist & Researcher",
|
||||
"description": "Andrew Simonson - Data Scientist at Ecolab, graduate student at RIT. Projects, research papers, and more.",
|
||||
"canonical": "/"
|
||||
},
|
||||
"status": {
|
||||
@@ -13,8 +13,8 @@
|
||||
},
|
||||
"projects": {
|
||||
"template": "projects.html",
|
||||
"title": "Andrew Simonson - Projects",
|
||||
"description": "Recent projects by Andrew Simonson on his lovely portfolio website :)",
|
||||
"title": "Projects & Research - Andrew Simonson",
|
||||
"description": "Data science projects, geospatial analysis, research papers, and software experiments by Andrew Simonson.",
|
||||
"canonical": "/projects"
|
||||
},
|
||||
"books": {
|
||||
@@ -31,8 +31,8 @@
|
||||
},
|
||||
"certificates": {
|
||||
"template": "certs.html",
|
||||
"title": "Certificates and Awards",
|
||||
"description": "Certificates and Awards Listing",
|
||||
"title": "Certifications & Awards - Andrew Simonson",
|
||||
"description": "Data science, DevOps, and entrepreneurship certifications earned by Andrew Simonson.",
|
||||
"canonical": "/certs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,53 @@
|
||||
{
|
||||
"Co-op @ Dow Chemical": {
|
||||
"Data Scientist @ Ecolab": {
|
||||
"classes": "pinned experience technical",
|
||||
"date": "01/2024 - Present",
|
||||
"content": "Primary model developer of RushReady from inception through multimillion-dollar product commercialization. Engineered near-real-time data platform with Delta Live Tables and automated MLOps lifecycle. Built explainable anomaly detection with XGBoost/SHAP attribution, increasing speed of service by 20%. Represented Ecolab in Microsoft's 100-member AI accelerator program."
|
||||
},
|
||||
"Data Scientist / Data Engineer": {
|
||||
"classes": "pinned experience technical",
|
||||
"date": "01/2023 - 05/2023",
|
||||
"content": "Spring 2023 Semester Co-op under Dow Chemical's Global Reactive Chemicals team in Analytical Sciences. Responsibilities included management of chemical compatability data and tool creation for parsing, generating, and submitting reports."
|
||||
"content": "Co-op under Dow Chemical's Global Reactive Chemicals team in Analytical Sciences. Independently built reactive chemistry analysis Flask app encoding adiabatic correction and exotherm-detection logic. Replaced 4 legacy VBA tools with an object-model abstraction architecture. Product became the interdepartmental data standard, saving >1,020 hours (~0.65 FTE) annually."
|
||||
},
|
||||
"Rochester Institute of Technology — M.S.": {
|
||||
"classes": "pinned education technical",
|
||||
"date": "01/2023 - 12/2026",
|
||||
"content": "Data Science M.S. with dual cluster in Data Science and AI. Capstone Fall 2026. Certified by Databricks in ML Model Deployment, Retrieval Agents, and MLOps. Certified by Microsoft in Data Analysis Career Essentials and Docker Foundations."
|
||||
},
|
||||
"Rochester Institute of Technology — B.S.": {
|
||||
"classes": "pinned education technical",
|
||||
"date": "08/2021 - 12/2024",
|
||||
"content": "Computer Science B.S. (GPA 3.63, Dean's List) with a minor in International Relations. Active member of Computer Science House — built and maintained web services for 80+ members, presented seminars on web scraping, analytics, and GIS."
|
||||
},
|
||||
"UCSanDiegoX Data Science MicroMasters": {
|
||||
"classes": "education technical",
|
||||
"date": "2024",
|
||||
"content": "Completed MicroMasters program in Data Science through UC San Diego on edX."
|
||||
},
|
||||
"Started Portfolio": {
|
||||
"classes": "",
|
||||
"date": "08/26/2021",
|
||||
"content": "I started building this website on this day. I wish I could say I was farther along than I am."
|
||||
},
|
||||
"Rochester Institute of Technology": {
|
||||
"classes": "pinned education technical",
|
||||
"date": "08/2021 - 12/2024",
|
||||
"content": "Studying in Rochester Institute of Technology's Computer Science BS program with a minor in International Relations."
|
||||
"Human vs AI Code Smell Study": {
|
||||
"classes": "technical",
|
||||
"date": "2024 - 2025",
|
||||
"content": "Analyzed 85K+ code definitions comparing AI vs human code maintainability via static analysis and non-parametric statistics. Found AI code carries 3-4x more structural debt invisible to reviewers."
|
||||
},
|
||||
"Generalized Trust Review": {
|
||||
"classes": "technical",
|
||||
"date": "2023 - 2024",
|
||||
"content": "Cross-national correlation analysis of media and science trust across 128 countries with subgroup OLS regression. Found trust transfer attenuates 5.7x in low press-freedom regimes."
|
||||
},
|
||||
"Antietam Watershed Monitoring": {
|
||||
"classes": "technical",
|
||||
"date": "2023",
|
||||
"content": "ArcGIS choropleth dashboard monitoring stream temps and biotic indices across Upper Potomac sub-watersheds. Flags exceedances of healthy stream conditions for state environmental review."
|
||||
},
|
||||
"Hackathon Awards": {
|
||||
"classes": "technical",
|
||||
"date": "2023 - 2024",
|
||||
"content": "CSHacks - Best Use of AI (Paychex). Tiger Games - Social Impact Winner. HACK.COMS '24 - Accessibility Winner."
|
||||
},
|
||||
"Pretzel & Pizza Creations": {
|
||||
"classes": "experience",
|
||||
@@ -27,11 +62,11 @@
|
||||
"Boonsboro High School": {
|
||||
"classes": "education",
|
||||
"date": "09/2016 - 06/2021",
|
||||
"content": "Graduated high school with highest honors.\nMember of National Honor Society, Academic Team County Champions. Participated in Physics Olympics, Robotics Club, and scored at state championships in Cross Country and Track and Field (4x800, 800)."
|
||||
"content": "Graduated high school with highest honors. Member of National Honor Society, Academic Team County Champions. Participated in Physics Olympics, Robotics Club, and scored at state championships in Cross Country and Track and Field (4x800, 800)."
|
||||
},
|
||||
"Vex Robotics Team Lead/Club Preisdent": {
|
||||
"Vex Robotics Team Lead/Club President": {
|
||||
"classes": "technical",
|
||||
"date": "10/2015 - 04/2021",
|
||||
"content": "Led 5 teams through middle and high school to VEX Robotics Competitions, elevating Boonsboro from county group-stage elimination to its first state championship participation. Reorganized club and set up its first interface with the community + sponsors"
|
||||
"content": "Led 5 teams through middle and high school to VEX Robotics Competitions, elevating Boonsboro from county group-stage elimination to its first state championship participation. Reorganized club and set up its first interface with the community + sponsors."
|
||||
}
|
||||
}
|
||||
|
||||
73
src/templates/about.html
Normal file
73
src/templates/about.html
Normal file
@@ -0,0 +1,73 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="relative growBottom">
|
||||
<div id="nametag" class="flex" data-aos="fade-up">
|
||||
<div>
|
||||
<h1 class="textGrad">About</h1>
|
||||
<h2>My story, in order</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="homeSubContent">
|
||||
<div class="foregroundContent">
|
||||
<div class="concentratedHead">
|
||||
<h3>Career & Education Timeline</h3>
|
||||
</div>
|
||||
|
||||
<div class="timeline-filters" role="toolbar" aria-label="Filter timeline entries">
|
||||
<button class="filter-btn active" data-filter="all" aria-pressed="true">All</button>
|
||||
<button class="filter-btn" data-filter="experience" aria-pressed="false">Experience</button>
|
||||
<button class="filter-btn" data-filter="education" aria-pressed="false">Education</button>
|
||||
<button class="filter-btn" data-filter="technical" aria-pressed="false">Technical</button>
|
||||
</div>
|
||||
|
||||
<div class="timeline" id="timeline">
|
||||
{% for name, item in var['timeline'].items() %}
|
||||
<div class="timeitem" data-categories="{{ item.classes }}">
|
||||
<div class="timeitem-inner boxed">
|
||||
<div class="timeitem-header">
|
||||
<span class="datetext">{{ item.date }}</span>
|
||||
<strong>{{ name }}</strong>
|
||||
</div>
|
||||
<div class="timeitem-content">
|
||||
{{ item.content | safe }}
|
||||
</div>
|
||||
{% if item.classes %}
|
||||
<div class="timeitem-tags">
|
||||
{% for cls in item.classes.split() %}
|
||||
<span class="tag tag-{{ cls }}">{{ cls | replace('_', ' ') | title }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const buttons = document.querySelectorAll('.filter-btn');
|
||||
const items = document.querySelectorAll('.timeitem');
|
||||
|
||||
buttons.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var filter = this.getAttribute('data-filter');
|
||||
buttons.forEach(function(b) { b.classList.remove('active'); b.setAttribute('aria-pressed', 'false'); });
|
||||
this.classList.add('active');
|
||||
this.setAttribute('aria-pressed', 'true');
|
||||
|
||||
items.forEach(function(item) {
|
||||
var cats = item.getAttribute('data-categories') || '';
|
||||
if (filter === 'all' || cats.indexOf(filter) !== -1) {
|
||||
item.classList.remove('hidden');
|
||||
} else {
|
||||
item.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -3,14 +3,15 @@
|
||||
<div class="foregroundContent">
|
||||
<h2>"You can't judge a book by its cover but you can judge a person by their bookshelf" - Me, today.</h2>
|
||||
<h3>My Favorites</h3>
|
||||
<div class='flex wrap boxed'>
|
||||
<div class='flex wrap boxed' role="list" aria-label="Favorite books">
|
||||
{% for i in var.books.selection %}
|
||||
<div>
|
||||
<a href="#'{{i}}'">
|
||||
<div role="listitem">
|
||||
<a href="#'{{i}}'" aria-label="View review of {{ i }}">
|
||||
<img
|
||||
class="bookcover"
|
||||
alt="{{i}} cover"
|
||||
alt="{{ i }} cover"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename))}}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
@@ -18,16 +19,17 @@
|
||||
</div>
|
||||
<h3>All Reviews</h3>
|
||||
<p>Hover to reveal review<br />Read to reveal KNOWLEDGE</p>
|
||||
<div class="booklist flex wrap">
|
||||
<div class="booklist flex wrap" role="list" aria-label="Book reviews">
|
||||
{% for i in var.books.books %}
|
||||
<div id="'{{i}}'" class="bookReview">
|
||||
<div id="'{{i}}'" class="bookReview" role="listitem" aria-label="Review of {{ i }}">
|
||||
<div class='fullHeight flex'>
|
||||
<div>
|
||||
<div class='heightBox'>
|
||||
<img
|
||||
class="bookcover"
|
||||
alt="{{i}} cover"
|
||||
alt="{{ i }} cover"
|
||||
src="{{ url_for('static', filename=('photos/books/' + var.books.books[i].filename))}}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,7 +38,7 @@
|
||||
<h4 class='nomargin'>{{ i }}</h4>
|
||||
<p class='nomargin'>{{ var.books.books[i].review }}</p>
|
||||
</div>
|
||||
<a href="{{ var.books.books[i].link }}"><p>Book Source</p></a>
|
||||
<a href="{{ var.books.books[i].link }}" rel="noopener noreferrer" aria-label="Buy or learn more about {{ i }}">Book Source</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
||||
<meta name="description" content="{{ var['description'] }}" />
|
||||
<meta property="og:title" content="Andrew Simonson" />
|
||||
<meta name="og:description" content="{{ var['description'] }}" />
|
||||
<meta property="og:description" content="{{ var['description'] }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta
|
||||
property="og:image"
|
||||
@@ -32,6 +32,29 @@
|
||||
<meta name="twitter:image:alt" content="some example picture idk" />
|
||||
<meta name="twitter:site" content="@asimonson1125" />
|
||||
|
||||
<!-- Structured data for SEO -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Person",
|
||||
"name": "Andrew Simonson",
|
||||
"url": "https://asimonson.com",
|
||||
"sameAs": [
|
||||
"https://github.com/asimonson1125",
|
||||
"https://www.linkedin.com/in/simonsonandrew"
|
||||
],
|
||||
"jobTitle": "Data Scientist",
|
||||
"worksFor": {
|
||||
"@type": "Organization",
|
||||
"name": "Ecolab"
|
||||
},
|
||||
"alumniOf": {
|
||||
"@type": "CollegeOrUniversity",
|
||||
"name": "Rochester Institute of Technology"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics (deferred for performance) -->
|
||||
<script
|
||||
async
|
||||
@@ -59,12 +82,13 @@
|
||||
</head>
|
||||
{% block header %}
|
||||
<body onpopstate="backButton()">
|
||||
<a class="skip-link" href="#root">Skip to content</a>
|
||||
<div id="loading-bar"></div>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<main id='map'></main>
|
||||
<main id='map' aria-label="Main content"></main>
|
||||
<div id="contentStuffer">
|
||||
<div class="header">
|
||||
<div id="name-container" onclick="goto('home')">
|
||||
<header class="header">
|
||||
<div id="name-container" onclick="goto('home')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('home')" role="button" tabindex="0" aria-label="Go to home">
|
||||
<div class="line name">
|
||||
<span class="textGrad">{# Andrew Simonoson #}</span>
|
||||
</div>
|
||||
@@ -74,27 +98,32 @@
|
||||
src="{{ url_for('static', filename='icons/menu.svg')}}"
|
||||
alt="menu"
|
||||
id="menu"
|
||||
role="button"
|
||||
aria-label="Toggle navigation menu"
|
||||
aria-expanded="false"
|
||||
aria-controls="navBar"
|
||||
onClick="toggleMenu()"
|
||||
onKeyDown="if(event.key==='Enter'||event.key===' ')toggleMenu()"
|
||||
/>
|
||||
<div class="navControl">
|
||||
<div class="navBar">
|
||||
<div onClick="goto('home')" class="navElement">
|
||||
<nav id="navBar" class="navBar" role="menubar" aria-label="Main navigation">
|
||||
<div onClick="goto('home')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('home')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>Home</p>
|
||||
</div>
|
||||
<div onclick="goto('status')" class="navElement">
|
||||
<p>Status</p>
|
||||
</div>
|
||||
<div onclick="goto('projects')" class="navElement">
|
||||
<div onClick="goto('projects')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('projects')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>Work</p>
|
||||
</div>
|
||||
<div onclick="goto('status')" onKeyDown="if(event.key==='Enter'||event.key===' ')goto('status')" role="menuitem" tabindex="0" class="navElement">
|
||||
<p>Status</p>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{% endblock %} {% block content%}
|
||||
<div id="root">{% include var['template'] %}</div>
|
||||
{% endblock %} {% block footer %}
|
||||
<div id="footerSpacer"></div>
|
||||
<div class="footer">{% include 'partials/socials.html' %}</div>
|
||||
<footer class="footer" role="contentinfo">{% include 'partials/socials.html' %}</footer>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
<div class="foreground homeground"></div>
|
||||
<div class="relative">
|
||||
<div class="relative growBottom">
|
||||
<div id="nametag" class="flex" data-aos="fade-up">{{ nameplate() }}</div>
|
||||
<!--<INSERT SMALL BANNER HERE FOR PROJECT IMAGECARD CAROUSEL>-->
|
||||
<div id="desktopSpacer"></div>
|
||||
|
||||
@@ -7,12 +7,19 @@
|
||||
{% else %}
|
||||
<div class="projImagePlaceholder"></div>
|
||||
{% endif %}
|
||||
<div class="proj-status-badge {{ status }}">
|
||||
<span class="status-indicator"></span>{{ status }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="projContent">
|
||||
<div class="projTitleRow">
|
||||
<h3>{{ title }}</h3>
|
||||
<span class="proj-status {{ status }}">{{ status }}</span>
|
||||
</div>
|
||||
{% if classes %}
|
||||
<div class="projTags">
|
||||
{% for cls in classes.split() %}
|
||||
{% if cls != 'pinned' %}<span class="proj-tag">{{ cls }}</span>{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="projDesc">{{ content }}</p>
|
||||
{% if links %}
|
||||
<div class="projLinks">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro expandSkill(dict, name, classes="") %}
|
||||
<div class='skill {{ classes }}' data-length='{{ dict[name]|length }}'>
|
||||
<div onclick='activeSkill(this)' class='skillname'>{{ name }}</div>
|
||||
<div onclick='activeSkill(this)' onKeyDown="if(event.key==='Enter'||event.key===' ')activeSkill(this)" role='button' tabindex='0' aria-expanded='false' class='skillname'>{{ name }}</div>
|
||||
{% if dict[name]|length > 0 %}
|
||||
<div class='skill-children'>
|
||||
<div class='skill-children-inner'>
|
||||
@@ -14,7 +14,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro skills(skills) %}
|
||||
<div id="skillTree">
|
||||
<div id="skillTree" role='tree' aria-label='Skills'>
|
||||
{% for skill in skills %}
|
||||
{% set classes = "" %}
|
||||
{% if skill == skills|first %}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<div class='socials'>
|
||||
<a href='https://github.com/asimonson1125'><img alt='Github' src="{{ url_for('static', filename='icons/github.svg') }}" /></a>
|
||||
<a href='https://www.linkedin.com/in/simonsonandrew/'><img alt='LinkedIn' src="{{ url_for('static', filename='icons/linkedin.svg') }}" /></a>
|
||||
<a href='mailto:asimonson1125@gmail.com'><img alt='E-mail' src="{{ url_for('static', filename='icons/email.svg') }}" /></a>
|
||||
<div id='vertLine'></div>
|
||||
<div class='socials' role='contentinfo' aria-label='Social links'>
|
||||
<a href='https://github.com/asimonson1125' aria-label='GitHub profile' rel='noopener noreferrer'>
|
||||
<img alt='' src="{{ url_for('static', filename='icons/github.svg') }}" />
|
||||
</a>
|
||||
<a href='https://www.linkedin.com/in/simonsonandrew/' aria-label='LinkedIn profile' rel='noopener noreferrer'>
|
||||
<img alt='' src="{{ url_for('static', filename='icons/linkedin.svg') }}" />
|
||||
</a>
|
||||
<a href='mailto:asimonson1125@gmail.com' aria-label='Send an email'>
|
||||
<img alt='' src="{{ url_for('static', filename='icons/email.svg') }}" />
|
||||
</a>
|
||||
<div id='vertLine' aria-hidden='true'></div>
|
||||
</div>
|
||||
@@ -1,10 +1,17 @@
|
||||
{% block content %}
|
||||
<div class="foreground"></div>
|
||||
<div class="foregroundContent">
|
||||
<div class="flex equalitems vertOnMobile">
|
||||
<div class="relative growBottom">
|
||||
<div id="nametag" class="flex" data-aos="fade-up">
|
||||
<div>
|
||||
<div>
|
||||
<h2 class="concentratedHead">About Me<p><sup>Data Scientist, Amateur SysAdmin, Polymath</sup></p></h2>
|
||||
<h1 class="textGrad">Work</h1>
|
||||
<h2>Projects & experiments</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="homeSubContent">
|
||||
<div class="foregroundContent">
|
||||
|
||||
<!-- About / Intro -->
|
||||
<div style="max-width: 52em; margin-bottom: 3em;">
|
||||
<p>
|
||||
I'm Andrew Simonson<!--, CEO of the anti-thermodynamics syndicate.-->,
|
||||
a <strong>Data Scientist at Ecolab</strong> and a graduate Data
|
||||
@@ -13,49 +20,30 @@
|
||||
recently completed the <b>Computer Science BS</b> program
|
||||
(international relations minor) with a focus on probability
|
||||
theory.
|
||||
<!-- <br />
|
||||
<br />
|
||||
I started in ~2017, reverse engineering probablistic logic
|
||||
models in games and developing interfaces to recreate my
|
||||
findings for friends. Now I develop tracable AI built on
|
||||
deductive reasoning, maintaning scientific methodology in an
|
||||
industry obsessed with implicit rules and exclusive empiricism.
|
||||
As the analysis grew more sophisticated, so too did the tech
|
||||
stack - to the point that I now manage most services, like this
|
||||
website, end to end, container image to insight visual.
|
||||
|
||||
With substantial development and systems operation expertise, I
|
||||
am capable of working with small teams that require many critical
|
||||
functions, from devops to analytical platforms, to artificial
|
||||
intelligence implementations, to all be handled by the same person.
|
||||
Looking for an analyst who can create and operate the full digital
|
||||
stack on your crack team of scientists? You've come to the right place.
|
||||
-->
|
||||
<br />
|
||||
<br />
|
||||
<br/>
|
||||
<br/>
|
||||
I get bored and throw random stuff on this website.<br/>
|
||||
This is what unprofessional development looks like.
|
||||
</p>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<h4 class='concentratedHead'>
|
||||
<p style="color: var(--text-secondary); font-size: 0.9rem;">
|
||||
I also have a
|
||||
<a href="certs">certifications page</a>
|
||||
and a
|
||||
<a href="Resume_Simonson_Andrew.pdf" target="_blank">resume</a>
|
||||
for unexplained reasons.
|
||||
</h4>
|
||||
</p>
|
||||
</div>
|
||||
<div id="skills">
|
||||
<h2 id="skillstag">Technologies</h2>
|
||||
|
||||
<!-- Skills -->
|
||||
<div id="skills" style="margin-bottom: 3.5em;">
|
||||
<h2>Technologies</h2>
|
||||
{% from 'partials/skills.html' import skills %} {{
|
||||
skills(var['skillList']) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<h2 class="concentratedHead">Projects</h2>
|
||||
<!-- Projects -->
|
||||
<h2>Projects</h2>
|
||||
<div class="projectList">
|
||||
{% from 'partials/project.html' import project %} {% for i in
|
||||
var["projects"] %} {{ project(i, var["projects"][i]["classes"],
|
||||
@@ -63,6 +51,9 @@
|
||||
var["projects"][i]["content"], var["projects"][i]["links"]) }} {% endfor
|
||||
%}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<div class="foregroundContent">
|
||||
<h2 class='concentratedHead'>Service Status Monitor</h2>
|
||||
|
||||
<div class="info-box">
|
||||
<div class="info-box" role="region" aria-label="Status overview">
|
||||
<div class='flex spaceBetween'>
|
||||
<div>
|
||||
<span id="lastUpdate">Last checked: Loading...</span>
|
||||
<span id="lastUpdate" aria-live="polite">Last checked: Loading...</span>
|
||||
<br />
|
||||
<span id="nextUpdate">Next check: --</span>
|
||||
<span id="nextUpdate" aria-live="polite">Next check: --</span>
|
||||
</div>
|
||||
<button id="refreshBtn" onclick="refreshStatus()">Refresh Now</button>
|
||||
<button id="refreshBtn" onclick="refreshStatus()" aria-label="Refresh service status now">Refresh Now</button>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Overall Status Bar -->
|
||||
<div class="info-card summary-card" id="overallStatus">
|
||||
<div class="info-card summary-card" id="overallStatus" role="status" aria-live="polite">
|
||||
<div class="summary-content">
|
||||
<div class="summary-indicator">
|
||||
<span class="summary-icon loading">◐</span>
|
||||
<span class="summary-icon loading" aria-hidden="true">◐</span>
|
||||
<div>
|
||||
<h3 class="summary-title">Checking Systems...</h3>
|
||||
<h5 id="summary-subtitle">Loading service status</h5>
|
||||
@@ -43,45 +43,45 @@
|
||||
</div>
|
||||
<div class="flex spaceBetween">
|
||||
<div class="metric-box">
|
||||
<span class="metric-number" id="onlineCount">--</span>
|
||||
<span class="metric-number" id="onlineCount" aria-label="Online services">--</span>
|
||||
<span class="metric-label">Online</span>
|
||||
</div>
|
||||
<div class="metric-box">
|
||||
<span class="metric-number" id="totalCount">--</span>
|
||||
<span class="metric-number" id="totalCount" aria-label="Total services">--</span>
|
||||
<span class="metric-label">Total</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-grid">
|
||||
<div class="card-grid" role="list" aria-label="Service status cards">
|
||||
{% for service in var.services %}
|
||||
<a href="{{ service.url }}">
|
||||
<div class="info-card" id="status-{{ service.id }}">
|
||||
<a href="{{ service.url }}" class="info-card-link" id="status-{{ service.id }}" role="listitem">
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<h3>{{ service.name }}</h3>
|
||||
<div class="state-indicator">
|
||||
<span class="state-dot loading"></span>
|
||||
<span class="state-dot loading" aria-label="Status: Loading..."></span>
|
||||
<span class="state-text">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Response Time:</span>
|
||||
<span class="metric-value" id="time-{{ service.id }}">--</span>
|
||||
<span class="metric-value" id="time-{{ service.id }}" aria-live="polite">--</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Status Code:</span>
|
||||
<span class="metric-value" id="code-{{ service.id }}">--</span>
|
||||
<span class="metric-value" id="code-{{ service.id }}" aria-live="polite">--</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">Total Checks:</span>
|
||||
<span class="metric-value" id="checks-{{ service.id }}">--</span>
|
||||
<span class="metric-value" id="checks-{{ service.id }}" aria-live="polite">--</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="detail-label">Uptime:</div>
|
||||
<div class="detail-values" id="uptime-{{ service.id }}">Loading...</div>
|
||||
<div class="detail-values" id="uptime-{{ service.id }}" aria-live="polite">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user