Merge pull request 'feature/scss-cleanup' (#26) from feature/scss-cleanup into main
Reviewed-on: #26
This commit was merged in pull request #26.
This commit is contained in:
@@ -15,6 +15,10 @@ export class AssetsConstants {
|
|||||||
static readonly DIPLOMA = '/assets/projects/diploma/Dahm2010-Diplomarbeit.pdf';
|
static readonly DIPLOMA = '/assets/projects/diploma/Dahm2010-Diplomarbeit.pdf';
|
||||||
|
|
||||||
//project images
|
//project images
|
||||||
|
static readonly PLAYGROUND_IMAGES = [
|
||||||
|
'/assets/projects/playground/1.png'
|
||||||
|
];
|
||||||
|
|
||||||
static readonly EL_MUCHO_IMAGES = [
|
static readonly EL_MUCHO_IMAGES = [
|
||||||
'/assets/projects/el-mucho/1.jpg',
|
'/assets/projects/el-mucho/1.jpg',
|
||||||
'/assets/projects/el-mucho/2.jpg',
|
'/assets/projects/el-mucho/2.jpg',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export class UrlConstants {
|
export class UrlConstants {
|
||||||
static readonly LINKED_IN = 'https://www.linkedin.com/in/andreas-dahm-2395991ba';
|
static readonly LINKED_IN = 'https://www.linkedin.com/in/andreas-dahm-2395991ba';
|
||||||
static readonly GIT_HUB = 'https://github.com/LoboTheDark';
|
static readonly CODEBERG = 'https://codeberg.org/LoboTheDark';
|
||||||
static readonly DIJKSTRA_WIKI = 'https://de.wikipedia.org/wiki/Dijkstra-Algorithmus'
|
static readonly DIJKSTRA_WIKI = 'https://de.wikipedia.org/wiki/Dijkstra-Algorithmus'
|
||||||
static readonly ASTAR_WIKI = 'https://de.wikipedia.org/wiki/A*-Algorithmus'
|
static readonly ASTAR_WIKI = 'https://de.wikipedia.org/wiki/A*-Algorithmus'
|
||||||
static readonly BUBBLE_SORT_WIKI = 'https://de.wikipedia.org/wiki/Bubblesort'
|
static readonly BUBBLE_SORT_WIKI = 'https://de.wikipedia.org/wiki/Bubblesort'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<app-topbar />
|
<app-topbar />
|
||||||
|
|
||||||
<main class="container app-surface">
|
<main class="algo-container app-surface">
|
||||||
<router-outlet />
|
<router-outlet />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1 @@
|
|||||||
.container { max-width: 1100px; margin: 0 auto; padding: 1rem; }
|
|
||||||
.app-surface {
|
|
||||||
background: var(--app-bg);
|
|
||||||
color: var(--app-fg);
|
|
||||||
transition: background-color 220ms ease, color 220ms ease;
|
|
||||||
}
|
|
||||||
.foot {
|
|
||||||
border-top: 1px solid rgba(0,0,0,.08);
|
|
||||||
padding: 1rem; text-align: center; opacity: .8;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
<mat-toolbar class="topbar" color="primary" (keydown)="onKeydown($event)">
|
<mat-toolbar class="topbar" color="primary" (keydown)="onKeydown($event)">
|
||||||
<a class="brand" routerLink="/">
|
<a class="brand" routerLink="/">
|
||||||
<img class="logo-dot"
|
<img class="logo-dot" src="{{AssetsConstants.LOGO}}" alt="" aria-hidden="true" draggable="false"
|
||||||
src="{{AssetsConstants.LOGO}}"
|
oncontextmenu="return false;">
|
||||||
alt="" aria-hidden="true"
|
|
||||||
draggable="false"
|
|
||||||
oncontextmenu="return false;"
|
|
||||||
>
|
|
||||||
<span class="brand-text">{{ 'APP.TITLE' | translate }}</span>
|
<span class="brand-text">{{ 'APP.TITLE' | translate }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -17,11 +13,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Mobile nav menu button -->
|
<!-- Mobile nav menu button -->
|
||||||
<button
|
<button mat-icon-button class="nav-menu-btn" [matMenuTriggerFor]="navMenu" aria-label="Open navigation">
|
||||||
mat-icon-button
|
|
||||||
class="nav-menu-btn"
|
|
||||||
[matMenuTriggerFor]="navMenu"
|
|
||||||
aria-label="Open navigation">
|
|
||||||
<mat-icon>menu</mat-icon>
|
<mat-icon>menu</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -46,7 +38,8 @@
|
|||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
|
|
||||||
<!-- Settings: Sprache + Theme -->
|
<!-- Settings: Sprache + Theme -->
|
||||||
<button mat-icon-button [matMenuTriggerFor]="settingsMenu" aria-label="Open settings" matTooltip="{{ 'TOPBAR.SETTINGS' | translate }}">
|
<button mat-icon-button [matMenuTriggerFor]="settingsMenu" aria-label="Open settings"
|
||||||
|
matTooltip="{{ 'TOPBAR.SETTINGS' | translate }}">
|
||||||
<mat-icon>tune</mat-icon>
|
<mat-icon>tune</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +1,76 @@
|
|||||||
|
/* ---- Topbar Host & Base ---- */
|
||||||
|
:host {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
position: sticky; top: 0; z-index: 100;
|
/* Erzeugt den Milchglas-Effekt */
|
||||||
backdrop-filter: saturate(1.1) blur(8px);
|
backdrop-filter: saturate(1.1) blur(8px);
|
||||||
background:
|
-webkit-backdrop-filter: saturate(1.1) blur(8px);
|
||||||
color-mix(in oklab, var(--app-topbar-bg) 80%, transparent);
|
/* Safari Support */
|
||||||
|
|
||||||
|
/* Mischt die Variable mit Transparenz. !important überschreibt Material-Vorgaben */
|
||||||
|
background: color-mix(in oklab, var(--app-topbar-bg) 80%, transparent) !important;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, .08);
|
border-bottom: 1px solid rgba(0, 0, 0, .08);
|
||||||
|
|
||||||
.brand {
|
|
||||||
display:flex; align-items:center; gap:.6rem;
|
|
||||||
color: inherit; text-decoration: none;
|
|
||||||
.logo-dot {
|
|
||||||
width: 48px; height: 48px; border-radius: 50%;
|
|
||||||
}
|
|
||||||
.brand-text { font-weight: 600; letter-spacing:.2px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav { display:flex; gap:.25rem; margin-left:.5rem; }
|
|
||||||
|
|
||||||
.spacer { flex: 1; }
|
|
||||||
|
|
||||||
.flag-icon { width: 18px; height: 18px; border-radius: 2px; margin-right:.5rem; }
|
|
||||||
|
|
||||||
.menu-section { padding:.25rem .5rem .5rem; }
|
|
||||||
.menu-title { font-size:.75rem; opacity:.75; padding:.25rem .75rem .5rem; }
|
|
||||||
|
|
||||||
.kbd {
|
|
||||||
margin-left:auto; font-size:.7rem; opacity:.65; border:1px solid currentColor;
|
|
||||||
border-radius:4px; padding:0 .35rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .mat-mdc-menu-item .mdc-list-item__primary-text {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .5rem;
|
padding: clamp(0.5rem, 1vw, 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .mat-mdc-menu-item .kbd {
|
/* ---- Branding ---- */
|
||||||
margin-left: auto;
|
.brand {
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
display: flex;
|
||||||
font-size: 11px;
|
align-items: center;
|
||||||
line-height: 1.6;
|
gap: clamp(0.4rem, 1vw, 0.6rem);
|
||||||
padding: 0 .35rem;
|
color: inherit;
|
||||||
border: 0px solid currentColor;
|
text-decoration: none;
|
||||||
border-radius: 4px;
|
|
||||||
opacity: .65;
|
.logo-dot {
|
||||||
|
width: clamp(36px, 10vw, 48px);
|
||||||
|
height: clamp(36px, 10vw, 48px);
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .mat-mdc-menu-item .mat-icon {
|
.brand-text {
|
||||||
width: 20px; height: 20px; font-size: 20px;
|
font-weight: 600;
|
||||||
|
letter-spacing: .2px;
|
||||||
|
font-size: clamp(1rem, 3vw, 1.2rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .mat-mdc-menu-item .flag-icon {
|
/* ---- Navigation ---- */
|
||||||
width: 20px !important;
|
.nav {
|
||||||
height: 14px !important;
|
position: absolute;
|
||||||
object-fit: cover;
|
left: 50%;
|
||||||
border-radius: 2px;
|
transform: translateX(-50%);
|
||||||
margin-right: .5rem;
|
display: flex;
|
||||||
vertical-align: middle;
|
gap: clamp(0.25rem, 1vw, 0.5rem);
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .mat-mdc-menu-panel {
|
|
||||||
border-radius: 10px !important;
|
|
||||||
border: 1px solid rgba(0,0,0,.14);
|
|
||||||
}
|
|
||||||
.dark ::ng-deep .mat-mdc-menu-panel {
|
|
||||||
border-color: rgba(255,255,255,.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive: Collapse navigation to icon if width is smaller than 760px */
|
|
||||||
.nav-menu-btn {
|
.nav-menu-btn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Mobile Responsiveness ---- */
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.topbar .nav {
|
.nav {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-menu-btn {
|
.nav-menu-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
flex: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
<section class="about">
|
<section class="about">
|
||||||
<mat-card class="hero">
|
<mat-card class="hero">
|
||||||
|
<div class="hero-flex-container">
|
||||||
<div class="photo">
|
<div class="photo">
|
||||||
<img
|
<img [ngSrc]="AssetsConstants.ME" width="421" height="512" alt="{{ 'ABOUT.ALT.PROFILE' | translate }}"
|
||||||
[ngSrc]="AssetsConstants.ME"
|
draggable="false" oncontextmenu="return false;" priority />
|
||||||
width="421" height="512"
|
|
||||||
alt="{{ 'ABOUT.ALT.PROFILE' | translate }}"
|
|
||||||
draggable="false"
|
|
||||||
oncontextmenu="return false;"
|
|
||||||
priority />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="intro">
|
<div class="intro">
|
||||||
@@ -32,33 +28,43 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-icon svgIcon="github"></mat-icon>
|
<mat-icon>data_object</mat-icon>
|
||||||
<a href="{{UrlConstants.GIT_HUB}}" target="_blank" rel="noopener">GitHub</a>
|
<a href="{{UrlConstants.CODEBERG}}" target="_blank" rel="noopener">Codeberg</a>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<mat-icon svgIcon="linkedin"></mat-icon>
|
<mat-icon svgIcon="linkedin"></mat-icon>
|
||||||
<a href="{{UrlConstants.LINKED_IN}}" target="_blank" rel="noopener">LinkedIn</a>
|
<a href="{{UrlConstants.LINKED_IN}}" target="_blank" rel="noopener">LinkedIn</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-card class="skills">
|
<mat-card class="skills">
|
||||||
<h2>{{ 'ABOUT.SECTION.SKILLS' | translate }}</h2>
|
<h2>{{ 'ABOUT.SECTION.SKILLS' | translate }}</h2>
|
||||||
<div class="chip-groups">
|
<div class="chip-groups">
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ 'ABOUT.SECTION.PRIMARY' | translate }}</h3>
|
<h3>{{ 'ABOUT.SECTION.BACKEND_ARCH' | translate }}</h3>
|
||||||
<mat-chip-set aria-label="Primary skills">
|
<mat-chip-set aria-label="Backend and Architecture">
|
||||||
@for (s of primarySkills; track s) {
|
@for (s of skillsArchitecture; track s) {
|
||||||
<mat-chip>{{ s | translate }}</mat-chip>
|
<mat-chip>{{ s | translate }}</mat-chip>
|
||||||
}
|
}
|
||||||
</mat-chip-set>
|
</mat-chip-set>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ 'ABOUT.SECTION.TOOLSET' | translate }}</h3>
|
<h3>{{ 'ABOUT.SECTION.INFRA_CLOUD' | translate }}</h3>
|
||||||
<mat-chip-set aria-label="Toolset">
|
<mat-chip-set aria-label="Infrastructure and Cloud">
|
||||||
@for (t of toolset; track t) {
|
@for (s of skillsCore; track s) {
|
||||||
<mat-chip>{{ t | translate }}</mat-chip>
|
<mat-chip>{{ s | translate }}</mat-chip>
|
||||||
|
}
|
||||||
|
</mat-chip-set>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>{{ 'ABOUT.SECTION.SIM_ALGO' | translate }}</h3>
|
||||||
|
<mat-chip-set aria-label="Simulation and Algorithms">
|
||||||
|
@for (s of skillsEngineering; track s) {
|
||||||
|
<mat-chip>{{ s | translate }}</mat-chip>
|
||||||
}
|
}
|
||||||
</mat-chip-set>
|
</mat-chip-set>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,12 +78,7 @@
|
|||||||
<div class="xp-item">
|
<div class="xp-item">
|
||||||
<div class="xp-head-grid">
|
<div class="xp-head-grid">
|
||||||
<div class="logo-wrap">
|
<div class="logo-wrap">
|
||||||
<img
|
<img src="{{entry.logo}}" alt="" class="company-logo" aria-hidden="true" />
|
||||||
src="{{entry.logo}}"
|
|
||||||
alt=""
|
|
||||||
class="company-logo"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="head-row">
|
<div class="head-row">
|
||||||
<strong>{{ (entry.key + '.ROLE') | translate }}</strong>
|
<strong>{{ (entry.key + '.ROLE') | translate }}</strong>
|
||||||
@@ -119,19 +120,14 @@
|
|||||||
</div>
|
</div>
|
||||||
@if (entry.externalLink) {
|
@if (entry.externalLink) {
|
||||||
<div class="link-row">
|
<div class="link-row">
|
||||||
<a class="link-with-icon"
|
<a class="link-with-icon" href="{{entry.externalLink}}" target="_blank" rel="noopener noreferrer">
|
||||||
href="{{entry.externalLink}}"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer">
|
|
||||||
<mat-icon>open_in_new</mat-icon>
|
<mat-icon>open_in_new</mat-icon>
|
||||||
{{ (entry.key + '.LINK_EXTERNAL') | translate }}
|
{{ (entry.key + '.LINK_EXTERNAL') | translate }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="link-row">
|
<div class="link-row">
|
||||||
<a class="link-with-icon"
|
<a class="link-with-icon" [routerLink]="['/projects']" [queryParams]="{ project: entry.identifier }"
|
||||||
[routerLink]="['/projects']"
|
|
||||||
[queryParams]="{ project: entry.identifier }"
|
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer">
|
||||||
<mat-icon>link</mat-icon>
|
<mat-icon>link</mat-icon>
|
||||||
{{ (entry.key + '.LINK_INTERNAL') | translate }}
|
{{ (entry.key + '.LINK_INTERNAL') | translate }}
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
.about {
|
|
||||||
display: grid;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero block: Photo + Intro */
|
|
||||||
.hero {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 425px 1fr;
|
|
||||||
gap: 1.25rem;
|
|
||||||
border-radius: 16px;
|
|
||||||
background: var(--app-card-background);
|
|
||||||
|
|
||||||
.photo {
|
|
||||||
align-items:flex-start; justify-content:center;
|
|
||||||
img {
|
|
||||||
display:block;
|
|
||||||
width: 100%; height: auto;
|
|
||||||
max-width: 425px;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 6px 24px rgba(0,0,0,.25);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro {
|
|
||||||
display:flex; flex-direction:column; gap:.5rem;
|
|
||||||
h1 { margin-top: .25rem }
|
|
||||||
.lead { opacity:.9; margin: .25rem 0 0.5rem; }
|
|
||||||
|
|
||||||
.meta {
|
|
||||||
display:flex; flex-direction:column; gap:.25rem; margin-bottom: 0.5rem;
|
|
||||||
.row {
|
|
||||||
display:flex; align-items:center; gap:.4rem;
|
|
||||||
a { color: inherit; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display:flex; gap:.5rem; flex-wrap:wrap; margin-top:.5rem;
|
|
||||||
.mat-icon { margin-right:.25rem; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Skills block */
|
|
||||||
.skills {
|
|
||||||
padding: 5px;
|
|
||||||
h2 { margin-top: .25rem; margin-left: .25rem; }
|
|
||||||
.chip-groups {
|
|
||||||
margin-left: .25rem;
|
|
||||||
display:grid; gap:1rem;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
margin-bottom: .5rem;
|
|
||||||
h3 { margin: .2rem 0 .4rem; font-size: .95rem; opacity:.85; }
|
|
||||||
mat-chip-set {
|
|
||||||
display:flex; flex-wrap:wrap; gap:.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Experience block */
|
|
||||||
.experience {
|
|
||||||
padding: 5px;
|
|
||||||
h2 { margin-top: .25rem; margin-left: .25rem; }
|
|
||||||
.xp-item {
|
|
||||||
.xp-head {
|
|
||||||
display:flex; align-items:baseline; gap:.5rem;
|
|
||||||
.time { opacity:.75; font-size:.9rem; }
|
|
||||||
}
|
|
||||||
.xp-sub { opacity:.9; margin-bottom:.25rem; }
|
|
||||||
ul { margin: .25rem 0 .5rem 1.15rem; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Experience block */
|
|
||||||
.projects {
|
|
||||||
padding: 5px;
|
|
||||||
h2 { margin-top: .25rem;margin-left: .25rem; }
|
|
||||||
.xp-list {
|
|
||||||
margin-left: .25rem;
|
|
||||||
display: grid; gap: .75rem;
|
|
||||||
}
|
|
||||||
.xp-item {
|
|
||||||
.xp-head {
|
|
||||||
display:flex; align-items:baseline; gap:.5rem;
|
|
||||||
.time { opacity:.75; font-size:.9rem; }
|
|
||||||
}
|
|
||||||
.xp-sub { opacity:.9; margin-bottom:.25rem; }
|
|
||||||
ul { margin: .25rem 0 .5rem 1.15rem; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Experience block */
|
|
||||||
.education {
|
|
||||||
padding: 5px;
|
|
||||||
h2 { margin-top: .25rem;margin-left: .25rem; }
|
|
||||||
.xp-list {
|
|
||||||
margin-left: .25rem;
|
|
||||||
display: grid; gap: .75rem;
|
|
||||||
}
|
|
||||||
.xp-item {
|
|
||||||
.xp-head {
|
|
||||||
display:flex; align-items:baseline; gap:.5rem;
|
|
||||||
.time { opacity:.75; font-size:.9rem; }
|
|
||||||
}
|
|
||||||
.xp-sub { opacity:.9; margin-bottom:.25rem; }
|
|
||||||
ul { margin: .25rem 0 .5rem 1.15rem; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.hero { grid-template-columns: 1fr; }
|
|
||||||
.hero .photo { justify-content: flex-start; }
|
|
||||||
.skills .chip-groups { grid-template-columns: 1fr; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.xp-head-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: calc(48px + .75rem) 1fr; /* 1: Logo, 2: Text */
|
|
||||||
grid-template-rows: auto auto; /* 1: Role/Time, 2: Company */
|
|
||||||
column-gap: .75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-wrap {
|
|
||||||
grid-row: 1 / span 2;
|
|
||||||
grid-column: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.company-logo {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
object-fit: contain;
|
|
||||||
opacity: .9;
|
|
||||||
border-radius: 10%;
|
|
||||||
background-color: var(--app-logo-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.head-row {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 2;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: .5rem 1rem;
|
|
||||||
|
|
||||||
strong {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
.time {
|
|
||||||
opacity: .75; font-size: .9rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.company-row {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 2;
|
|
||||||
margin-top: .1rem;
|
|
||||||
opacity: .85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlights {
|
|
||||||
margin-top: .4rem;
|
|
||||||
margin-left: .75rem;
|
|
||||||
padding-left: 1.2rem;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: .2rem 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlights-noMargin {
|
|
||||||
margin-top: .4rem;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: .2rem 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -126,26 +126,30 @@ export class AboutComponent {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
primarySkills = [
|
skillsCore = [
|
||||||
'ABOUT.SKILLS.JAVA',
|
'ABOUT.SKILLS.JAVA',
|
||||||
'ABOUT.SKILLS.SPRING',
|
'ABOUT.SKILLS.SPRING',
|
||||||
'ABOUT.SKILLS.ANGULAR',
|
'ABOUT.SKILLS.ANGULAR',
|
||||||
'ABOUT.SKILLS.DOCKER',
|
'ABOUT.SKILLS.TYPESCRIPT',
|
||||||
'ABOUT.SKILLS.UNITY',
|
|
||||||
'ABOUT.SKILLS.PYTHON',
|
|
||||||
'ABOUT.SKILLS.CSHARP',
|
'ABOUT.SKILLS.CSHARP',
|
||||||
'ABOUT.SKILLS.TYPESCRIPT'
|
'ABOUT.SKILLS.PYTHON'
|
||||||
];
|
];
|
||||||
|
|
||||||
toolset = [
|
skillsArchitecture = [
|
||||||
'ABOUT.TOOLS.GIT',
|
'ABOUT.SKILLS.ARCH_MICROSERVICES',
|
||||||
'ABOUT.TOOLS.GITHUB',
|
'ABOUT.SKILLS.ARCH_CLOUD',
|
||||||
'ABOUT.TOOLS.GITLAB',
|
'ABOUT.TOOLS.DOCKER',
|
||||||
'ABOUT.TOOLS.JENKINS',
|
|
||||||
'ABOUT.TOOLS.K8S',
|
'ABOUT.TOOLS.K8S',
|
||||||
'ABOUT.TOOLS.POSTGRES',
|
'ABOUT.TOOLS.JENKINS',
|
||||||
'ABOUT.TOOLS.MONGO',
|
'ABOUT.TOOLS.POSTGRES'
|
||||||
'ABOUT.TOOLS.GRAFANA',
|
];
|
||||||
|
|
||||||
|
skillsEngineering = [
|
||||||
|
'ABOUT.SKILLS.ENG_ALGO',
|
||||||
|
'ABOUT.SKILLS.ENG_SIM',
|
||||||
|
'ABOUT.SKILLS.ENG_GPU',
|
||||||
|
'ABOUT.SKILLS.UNITY',
|
||||||
|
'ABOUT.SKILLS.ENG_PERF'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected readonly UrlConstants = UrlConstants;
|
protected readonly UrlConstants = UrlConstants;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="container">
|
<div class="algo-container">
|
||||||
<h1>{{ 'ALGORITHM.TITLE' |translate }}</h1>
|
<h1>{{ 'ALGORITHM.TITLE' |translate }}</h1>
|
||||||
<div class="category-cards">
|
<div class="category-cards">
|
||||||
@for (category of categories$ | async; track category.id) {
|
@for (category of categories$ | async; track category.id) {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
.container {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-cards {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 1rem;
|
|
||||||
margin-top: 2rem;
|
|
||||||
|
|
||||||
mat-card {
|
|
||||||
cursor: pointer;
|
|
||||||
min-width: 450px;
|
|
||||||
max-width: 450px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-card class="container">
|
<mat-card class="algo-container">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'GOL.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'GOL.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-card class="container">
|
<mat-card class="algo-container">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'FRACTAL.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'FRACTAL.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-card class="container">
|
<mat-card class="algo-container">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'FRACTAL3D.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'FRACTAL3D.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-card class="container">
|
<mat-card class="algo-container">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'LABYRINTH.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'LABYRINTH.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-card class="container">
|
<mat-card class="algo-container">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'PATHFINDING.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'PATHFINDING.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -1,33 +1,45 @@
|
|||||||
<mat-card class="container">
|
<mat-card class="algo-container">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'PENDULUM.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'PENDULUM.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<app-information [algorithmInformation]="algoInformation"/>
|
<app-information [algorithmInformation]="algoInformation"/>
|
||||||
<div class="controls-container">
|
<div class="controls-container">
|
||||||
<div class="slider-control-container">
|
<div class="sliders-grid">
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.TRAIL_DECAY_TIME' | translate }}</p>
|
<div class="slider-item">
|
||||||
|
<p>{{ 'PENDULUM.TRAIL_DECAY_TIME' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.trailDecay" [options]="trailDecayOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.trailDecay" [options]="trailDecayOptions" ></ngx-slider>
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.ATTRACTION' | translate }}</p>
|
</div>
|
||||||
|
<div class="slider-item">
|
||||||
|
<p>{{ 'PENDULUM.ATTRACTION' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.g" [options]="gravityOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.g" [options]="gravityOptions" ></ngx-slider>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-control-container">
|
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.L1_LENGTH' | translate }}</p>
|
<div class="slider-item">
|
||||||
|
<p>{{ 'PENDULUM.L1_LENGTH' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.l1" [options]="lengthOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.l1" [options]="lengthOptions" ></ngx-slider>
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.L2_LENGTH' | translate }}</p>
|
</div>
|
||||||
|
<div class="slider-item">
|
||||||
|
<p>{{ 'PENDULUM.L2_LENGTH' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.l2" [options]="lengthOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.l2" [options]="lengthOptions" ></ngx-slider>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-control-container">
|
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.M1_MASS' | translate }}</p>
|
<div class="slider-item">
|
||||||
|
<p>{{ 'PENDULUM.M1_MASS' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.m1" [options]="massOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.m1" [options]="massOptions" ></ngx-slider>
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.M2_MASS' | translate }}</p>
|
</div>
|
||||||
|
<div class="slider-item">
|
||||||
|
<p>{{ 'PENDULUM.M2_MASS' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.m2" [options]="massOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.m2" [options]="massOptions" ></ngx-slider>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-control-container">
|
|
||||||
<p style="white-space: nowrap">{{ 'PENDULUM.DAMPING' | translate }}</p>
|
<div class="slider-item full-width">
|
||||||
|
<p>{{ 'PENDULUM.DAMPING' | translate }}</p>
|
||||||
<ngx-slider [(value)]="simParams.damping" [options]="dampingOptions" ></ngx-slider>
|
<ngx-slider [(value)]="simParams.damping" [options]="dampingOptions" ></ngx-slider>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-control-container">
|
</div>
|
||||||
|
|
||||||
|
<div class="actions-container">
|
||||||
<button mat-raised-button color="primary" (click)="pushPendulum(true)">
|
<button mat-raised-button color="primary" (click)="pushPendulum(true)">
|
||||||
{{ 'PENDULUM.POKE_M1' | translate }}
|
{{ 'PENDULUM.POKE_M1' | translate }}
|
||||||
</button>
|
</button>
|
||||||
@@ -38,6 +50,7 @@
|
|||||||
{{ 'PENDULUM.RESET' | translate }}
|
{{ 'PENDULUM.RESET' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="legend" style="margin-top: 10px">
|
<div class="legend" style="margin-top: 10px">
|
||||||
<span><span class="legend-color L1"></span> L1</span>
|
<span><span class="legend-color L1"></span> L1</span>
|
||||||
<span><span class="legend-color L2"></span> L2</span>
|
<span><span class="legend-color L2"></span> L2</span>
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
.sliders-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
.slider-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
p {
|
||||||
|
width: 100px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx-slider {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.sliders-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<mat-card class="container sorting-card">
|
<mat-card class="algo-container sorting-card">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{ 'SORTING.TITLE' | translate }}</mat-card-title>
|
<mat-card-title>{{ 'SORTING.TITLE' | translate }}</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
.sorting-card {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1200px;
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.controls-panel {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
mat-form-field {
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.visualization-area {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
height: 300px; /* Max height for bars */
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
gap: 1px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
|
|
||||||
.bar {
|
|
||||||
flex-grow: 1;
|
|
||||||
background-color: #424242; /* Default unsorted color */
|
|
||||||
transition: height 0.05s ease-in-out, background-color 0.05s ease-in-out;
|
|
||||||
width: 10px; /* Default width, flex-grow will adjust */
|
|
||||||
min-width: 1px; /* Ensure bars are always visible */
|
|
||||||
|
|
||||||
&.unsorted {
|
|
||||||
background-color: #424242;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.comparing {
|
|
||||||
background-color: #ffeb3b; /* Yellow for comparing */
|
|
||||||
}
|
|
||||||
|
|
||||||
&.sorted {
|
|
||||||
background-color: #4caf50; /* Green for sorted */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-panel {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: #FFFFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
.imprint {
|
|
||||||
display: grid;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imprint-card {
|
|
||||||
padding: 1.25rem 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imprint-title {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imprint-section {
|
|
||||||
display: grid;
|
|
||||||
gap: 0.25rem;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.imprint-label {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
opacity: 0.7;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--mat-primary);
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,31 +1,52 @@
|
|||||||
<h2 mat-dialog-title>{{ project.title | translate }}</h2>
|
<h2 mat-dialog-title>{{ project.title | translate }}</h2>
|
||||||
<mat-dialog-content #dialogContent>
|
<mat-dialog-content #dialogContent>
|
||||||
<p>{{ project.introduction | translate }}</p>
|
<div class="project-dialog-layout">
|
||||||
|
<div class="project-info">
|
||||||
|
<p class="introduction">{{ project.introduction | translate }}</p>
|
||||||
|
|
||||||
|
<div class="features-list">
|
||||||
<ul>
|
<ul>
|
||||||
@for(bullet of project.bulletPoints; track bullet) {
|
@for(bullet of project.bulletPoints; track bullet) {
|
||||||
<li>{{ bullet | translate }}</li>
|
<li>{{ bullet | translate }}</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="insight-grid">
|
||||||
|
<div class="insight-card technical">
|
||||||
|
<div class="insight-header">
|
||||||
|
<mat-icon>settings_suggest</mat-icon>
|
||||||
|
<h3>{{ 'PROJECTS.SECTION.TECHNICAL' | translate }}</h3>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
@for(challenge of project.challenges; track challenge) {
|
||||||
|
<li>{{ challenge | translate }}</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="insight-card softskills">
|
||||||
|
<div class="insight-header">
|
||||||
|
<mat-icon>psychology</mat-icon>
|
||||||
|
<h3>{{ 'PROJECTS.SECTION.LEARNINGS' | translate }}</h3>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
@for(learning of project.learnings; track learning) {
|
||||||
|
<li>{{ learning | translate }}</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (project.images.length > 0)
|
@if (project.images.length > 0)
|
||||||
{
|
{
|
||||||
<swiper-container
|
<div class="media-section">
|
||||||
class="my-swiper"
|
<swiper-container class="my-swiper" [attr.slides-per-view]="1" [attr.space-between]="12" [attr.navigation]="true"
|
||||||
[attr.slides-per-view]="1.2"
|
[attr.pagination]="true" [attr.keyboard]="true" style="width: 100%;">
|
||||||
[attr.space-between]="12"
|
|
||||||
[attr.navigation]="true"
|
|
||||||
[attr.pagination]="true"
|
|
||||||
[attr.keyboard]="true"
|
|
||||||
style="width: 100%;"
|
|
||||||
>
|
|
||||||
@for (img of project.images; track img) {
|
@for (img of project.images; track img) {
|
||||||
<swiper-slide>
|
<swiper-slide>
|
||||||
<img
|
<img class="slide-img" [src]="img.url" [alt]="project.title | translate" />
|
||||||
class="slide-img"
|
|
||||||
[src]="img.url"
|
|
||||||
[alt]="project.title | translate"
|
|
||||||
/>
|
|
||||||
@if (img.source) {
|
@if (img.source) {
|
||||||
<div class="slide-source">
|
<div class="slide-source">
|
||||||
{{ img.source }}
|
{{ img.source }}
|
||||||
@@ -34,13 +55,17 @@
|
|||||||
</swiper-slide>
|
</swiper-slide>
|
||||||
}
|
}
|
||||||
</swiper-container>
|
</swiper-container>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="footer-details">
|
||||||
|
<div class="tech-stack">
|
||||||
<mat-chip-set aria-label="Technologies">
|
<mat-chip-set aria-label="Technologies">
|
||||||
@for(tech of project.technologies; track tech) {
|
@for(tech of project.technologies; track tech) {
|
||||||
<mat-chip>{{tech}}</mat-chip>
|
<mat-chip>{{tech}}</mat-chip>
|
||||||
}
|
}
|
||||||
</mat-chip-set>
|
</mat-chip-set>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="link-section">
|
<div class="link-section">
|
||||||
@for(link of project.links; track link)
|
@for(link of project.links; track link)
|
||||||
@@ -59,6 +84,8 @@
|
|||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
|
|||||||
@@ -1,81 +1,117 @@
|
|||||||
.my-swiper::part(button-prev),
|
.project-dialog-layout {
|
||||||
.my-swiper::part(button-next) {
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: rgba(0,0,0,.5);
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-swiper::part(button-prev):hover,
|
|
||||||
.my-swiper::part(button-next):hover {
|
|
||||||
background: rgba(0,0,0,.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-swiper {
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-swiper::part(pagination) {
|
|
||||||
bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
swiper-slide {
|
|
||||||
border-radius: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #222;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-img {
|
.introduction {
|
||||||
width: 100%;
|
font-size: 1.1rem;
|
||||||
height: auto;
|
line-height: 1.6;
|
||||||
max-height: 512px !important;
|
opacity: 0.9;
|
||||||
object-fit: contain;
|
margin-bottom: 1rem;
|
||||||
display: block;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-source {
|
.features-list {
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #aaa;
|
|
||||||
background: #2a2a2a;
|
|
||||||
padding: 0.5rem;
|
|
||||||
text-align: right;
|
|
||||||
border-top: 1px solid #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
ul {
|
||||||
|
padding-left: 1.2rem;
|
||||||
li {
|
li {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mat-chip-set {
|
.insight-grid {
|
||||||
margin-top: 1.5rem;
|
display: grid;
|
||||||
margin-bottom: 1.5rem;
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insight-card {
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.insight-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: var(--link-color);
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.2rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
opacity: 0.85;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .insight-card {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-section {
|
||||||
|
margin: 1rem 0;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .footer-details {
|
||||||
|
border-top-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-section {
|
.link-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-dialog-actions {
|
@media (max-width: 600px) {
|
||||||
justify-content: flex-end;
|
.insight-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
.project-grid {
|
|
||||||
display: grid;
|
|
||||||
gap: 1.5rem;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card {
|
|
||||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.featured {
|
|
||||||
grid-column: 1 / -1; // Span full width
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-card-header {
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-card-content {
|
|
||||||
flex-grow: 1; // Ensure content area expands
|
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-chip-set {
|
|
||||||
padding-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-card-actions {
|
|
||||||
margin-top: auto; // Push actions to the bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 200px; /* Or a height that fits your design */
|
|
||||||
background-color: #f0f0f0; /* A light background for the icon */
|
|
||||||
}
|
|
||||||
|
|
||||||
.fallback-icon {
|
|
||||||
font-size: 4rem;
|
|
||||||
width: 4rem;
|
|
||||||
height: 4rem;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure images don't exceed the card width and maintain aspect ratio
|
|
||||||
img[mat-card-image] {
|
|
||||||
width: 100%;
|
|
||||||
height: 250px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export interface Projects {
|
|||||||
url: string
|
url: string
|
||||||
}[],
|
}[],
|
||||||
bulletPoints: string[],
|
bulletPoints: string[],
|
||||||
|
challenges: string[],
|
||||||
|
learnings: string[],
|
||||||
isFeatured: boolean,
|
isFeatured: boolean,
|
||||||
technologies: string[]
|
technologies: string[]
|
||||||
}
|
}
|
||||||
@@ -57,7 +59,7 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
title: 'PROJECTS.PLAYGROUND.TITLE',
|
title: 'PROJECTS.PLAYGROUND.TITLE',
|
||||||
shortDescription: 'PROJECTS.PLAYGROUND.SHORT_DESCRIPTION',
|
shortDescription: 'PROJECTS.PLAYGROUND.SHORT_DESCRIPTION',
|
||||||
introduction: 'PROJECTS.PLAYGROUND.INTRODUCTION',
|
introduction: 'PROJECTS.PLAYGROUND.INTRODUCTION',
|
||||||
images: [],
|
images: AssetsConstants.PLAYGROUND_IMAGES.map(url => ({ url, source: '' })),
|
||||||
icon: 'web',
|
icon: 'web',
|
||||||
assets: '',
|
assets: '',
|
||||||
links: [{ name: 'PROJECTS.LINK_TO_PROJECT', url: 'https://andreas-dahm.eu' }],
|
links: [{ name: 'PROJECTS.LINK_TO_PROJECT', url: 'https://andreas-dahm.eu' }],
|
||||||
@@ -67,6 +69,14 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
'PROJECTS.PLAYGROUND.BULLET_3',
|
'PROJECTS.PLAYGROUND.BULLET_3',
|
||||||
'PROJECTS.PLAYGROUND.BULLET_4',
|
'PROJECTS.PLAYGROUND.BULLET_4',
|
||||||
],
|
],
|
||||||
|
challenges: [
|
||||||
|
'PROJECTS.PLAYGROUND.CHALLENGE_1',
|
||||||
|
'PROJECTS.PLAYGROUND.CHALLENGE_2',
|
||||||
|
],
|
||||||
|
learnings: [
|
||||||
|
'PROJECTS.PLAYGROUND.LEARNING_1',
|
||||||
|
'PROJECTS.PLAYGROUND.LEARNING_2',
|
||||||
|
],
|
||||||
isFeatured: false,
|
isFeatured: false,
|
||||||
technologies: ['Angular', 'TypeScript', 'SCSS', 'HTML', 'GitHub Actions', 'Docker']
|
technologies: ['Angular', 'TypeScript', 'SCSS', 'HTML', 'GitHub Actions', 'Docker']
|
||||||
},
|
},
|
||||||
@@ -85,6 +95,15 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
'PROJECTS.EL_MUCHO.BULLET_3',
|
'PROJECTS.EL_MUCHO.BULLET_3',
|
||||||
'PROJECTS.EL_MUCHO.BULLET_4',
|
'PROJECTS.EL_MUCHO.BULLET_4',
|
||||||
],
|
],
|
||||||
|
challenges: [
|
||||||
|
'PROJECTS.EL_MUCHO.CHALLENGE_1',
|
||||||
|
'PROJECTS.EL_MUCHO.CHALLENGE_2',
|
||||||
|
'PROJECTS.EL_MUCHO.CHALLENGE_3',
|
||||||
|
],
|
||||||
|
learnings: [
|
||||||
|
'PROJECTS.EL_MUCHO.LEARNING_1',
|
||||||
|
'PROJECTS.EL_MUCHO.LEARNING_2',
|
||||||
|
],
|
||||||
isFeatured: true,
|
isFeatured: true,
|
||||||
technologies: ['Unity', 'C#', 'Steamworks', 'Git']
|
technologies: ['Unity', 'C#', 'Steamworks', 'Git']
|
||||||
},
|
},
|
||||||
@@ -103,6 +122,14 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
'PROJECTS.GAME_JAMS.BULLET_3',
|
'PROJECTS.GAME_JAMS.BULLET_3',
|
||||||
'PROJECTS.GAME_JAMS.BULLET_4',
|
'PROJECTS.GAME_JAMS.BULLET_4',
|
||||||
],
|
],
|
||||||
|
challenges: [
|
||||||
|
'PROJECTS.GAME_JAMS.CHALLENGE_1',
|
||||||
|
'PROJECTS.GAME_JAMS.CHALLENGE_2',
|
||||||
|
],
|
||||||
|
learnings: [
|
||||||
|
'PROJECTS.GAME_JAMS.LEARNING_1',
|
||||||
|
'PROJECTS.GAME_JAMS.LEARNING_2',
|
||||||
|
],
|
||||||
isFeatured: false,
|
isFeatured: false,
|
||||||
technologies: ['Unity', 'C#', 'Git']
|
technologies: ['Unity', 'C#', 'Git']
|
||||||
},
|
},
|
||||||
@@ -121,6 +148,14 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
'PROJECTS.DIPLOMA.BULLET_3',
|
'PROJECTS.DIPLOMA.BULLET_3',
|
||||||
'PROJECTS.DIPLOMA.BULLET_4',
|
'PROJECTS.DIPLOMA.BULLET_4',
|
||||||
],
|
],
|
||||||
|
challenges: [
|
||||||
|
'PROJECTS.DIPLOMA.CHALLENGE_1',
|
||||||
|
'PROJECTS.DIPLOMA.CHALLENGE_2',
|
||||||
|
],
|
||||||
|
learnings: [
|
||||||
|
'PROJECTS.DIPLOMA.LEARNING_1',
|
||||||
|
'PROJECTS.DIPLOMA.LEARNING_2',
|
||||||
|
],
|
||||||
isFeatured: false,
|
isFeatured: false,
|
||||||
technologies: ['C++', 'OpenGL', 'Qt', '3D-Scanner']
|
technologies: ['C++', 'OpenGL', 'Qt', '3D-Scanner']
|
||||||
},
|
},
|
||||||
@@ -133,7 +168,7 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
{ url: AssetsConstants.TRIBBLE_IMAGES[0], source: 'https://upload.wikimedia.org/wikipedia/commons/0/03/Hostinger_Logo.png' },
|
{ url: AssetsConstants.TRIBBLE_IMAGES[0], source: 'https://upload.wikimedia.org/wikipedia/commons/0/03/Hostinger_Logo.png' },
|
||||||
{ url: AssetsConstants.TRIBBLE_IMAGES[1], source: 'https://dashboardicons.com/icons/docker-engine' },
|
{ url: AssetsConstants.TRIBBLE_IMAGES[1], source: 'https://dashboardicons.com/icons/docker-engine' },
|
||||||
{ url: AssetsConstants.TRIBBLE_IMAGES[2], source: 'https://dashboardicons.com/icons/gitea' },
|
{ url: AssetsConstants.TRIBBLE_IMAGES[2], source: 'https://dashboardicons.com/icons/gitea' },
|
||||||
{ url: AssetsConstants.TRIBBLE_IMAGES[3], source: 'https://commons.wikimedia.org/wiki/File:Traefik.logo.png'}
|
{ url: AssetsConstants.TRIBBLE_IMAGES[3], source: 'https://dashboardicons.com/icons/traefik' }
|
||||||
],
|
],
|
||||||
icon: 'dns',
|
icon: 'dns',
|
||||||
assets: '',
|
assets: '',
|
||||||
@@ -153,6 +188,14 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
'PROJECTS.TRIBBLE.BULLET_3',
|
'PROJECTS.TRIBBLE.BULLET_3',
|
||||||
'PROJECTS.TRIBBLE.BULLET_4',
|
'PROJECTS.TRIBBLE.BULLET_4',
|
||||||
],
|
],
|
||||||
|
challenges: [
|
||||||
|
'PROJECTS.TRIBBLE.CHALLENGE_1',
|
||||||
|
'PROJECTS.TRIBBLE.CHALLENGE_2',
|
||||||
|
],
|
||||||
|
learnings: [
|
||||||
|
'PROJECTS.TRIBBLE.LEARNING_1',
|
||||||
|
'PROJECTS.TRIBBLE.LEARNING_2',
|
||||||
|
],
|
||||||
isFeatured: false,
|
isFeatured: false,
|
||||||
technologies: ['Ubuntu Server', 'Docker', 'Traefik', 'Gitea', 'Jellyfin', 'AdGuard Home', 'Paperless-ngx', 'Tailscale']
|
technologies: ['Ubuntu Server', 'Docker', 'Traefik', 'Gitea', 'Jellyfin', 'AdGuard Home', 'Paperless-ngx', 'Tailscale']
|
||||||
}
|
}
|
||||||
@@ -172,8 +215,7 @@ export class ProjectsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private dialogOpenFunction() : void
|
private dialogOpenFunction(): void {
|
||||||
{
|
|
||||||
this.queryParamSub = this.route.queryParamMap.subscribe(params => {
|
this.queryParamSub = this.route.queryParamMap.subscribe(params => {
|
||||||
const projectIdentifier = params.get('project');
|
const projectIdentifier = params.get('project');
|
||||||
if (projectIdentifier) {
|
if (projectIdentifier) {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>particles-background works!</p>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-particles-background',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './particles-background.html',
|
||||||
|
styleUrl: './particles-background.scss',
|
||||||
|
})
|
||||||
|
export class ParticlesBackground {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
.canvas-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
max-width: 1000px;
|
|
||||||
max-height: 1000px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
|
|
||||||
min-width: 200px;
|
|
||||||
min-height: 200px;
|
|
||||||
max-width: 1000px;
|
|
||||||
max-height: 1000px;
|
|
||||||
|
|
||||||
touch-action: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 20px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"APP": {
|
"APP": {
|
||||||
"TITLE": "Playground",
|
"TITLE": "Playground",
|
||||||
"COPYRIGHT": "Bilder und Sourcecode sind urheberrechtlich geschützt, keine Nutzung ohne Zustimmung!"
|
"COPYRIGHT": "Bilder und Sourcecode sind urheberrechtlich geschützt, keine Nutzung ohne Zustimmung!"
|
||||||
@@ -25,21 +25,29 @@
|
|||||||
"CONTACT_ME": "Kontaktiere mich",
|
"CONTACT_ME": "Kontaktiere mich",
|
||||||
"SECTION": {
|
"SECTION": {
|
||||||
"SKILLS": "Fähigkeiten & Stack",
|
"SKILLS": "Fähigkeiten & Stack",
|
||||||
"PRIMARY": "Schwerpunkte",
|
"BACKEND_ARCH": "Backend & Architektur",
|
||||||
"TOOLSET": "Toolset",
|
"INFRA_CLOUD": "Infrastruktur & Cloud",
|
||||||
|
"SIM_ALGO": "Simulation & Algorithmen",
|
||||||
"EXPERIENCE": "Erfahrung",
|
"EXPERIENCE": "Erfahrung",
|
||||||
"PROJECTS": "Projekte",
|
"PROJECTS": "Projekte",
|
||||||
"EDUCATION": "Ausbildung"
|
"EDUCATION": "Ausbildung"
|
||||||
},
|
},
|
||||||
"SKILLS": {
|
"SKILLS": {
|
||||||
"JAVA": "Java 8/Java 21+",
|
"JAVA": "Java 8/21+",
|
||||||
"SPRING": "Spring Boot 2/3",
|
"SPRING": "Spring Boot 2/3",
|
||||||
"ANGULAR": "Angular 20+",
|
"ANGULAR": "Angular 19+",
|
||||||
"DOCKER": "Docker",
|
"DOCKER": "Docker",
|
||||||
"UNITY": "Unity",
|
"UNITY": "Unity",
|
||||||
"PYTHON": "Python",
|
"PYTHON": "Python",
|
||||||
"CSHARP": "C#",
|
"CSHARP": "C#",
|
||||||
"TYPESCRIPT": "TypeScript"
|
"TYPESCRIPT": "TypeScript",
|
||||||
|
"ARCH_MICROSERVICES": "Microservices",
|
||||||
|
"ARCH_CLOUD": "Cloud Architecture",
|
||||||
|
"ENG_ALGO": "Algorithm Design",
|
||||||
|
"ENG_SIM": "3D Simulation",
|
||||||
|
"ENG_GPU": "WebGPU / OpenGL / GLSL",
|
||||||
|
"ENG_PERF": "Performance Optimization",
|
||||||
|
"ENG_3D": "3D-Scanner Tech"
|
||||||
},
|
},
|
||||||
"TOOLS": {
|
"TOOLS": {
|
||||||
"GIT": "Git",
|
"GIT": "Git",
|
||||||
@@ -241,50 +249,75 @@
|
|||||||
"READ_MORE": "Mehr erfahren",
|
"READ_MORE": "Mehr erfahren",
|
||||||
"LINK_TO_PROJECT": "Zum Projekt",
|
"LINK_TO_PROJECT": "Zum Projekt",
|
||||||
"CLOSE": "Schließen",
|
"CLOSE": "Schließen",
|
||||||
|
"SECTION": {
|
||||||
|
"TECHNICAL": "Technische Herausforderungen",
|
||||||
|
"LEARNINGS": "Learnings & Soft Skills"
|
||||||
|
},
|
||||||
"PLAYGROUND": {
|
"PLAYGROUND": {
|
||||||
"TITLE": "Playground Website",
|
"TITLE": "Playground Portfolio",
|
||||||
"SHORT_DESCRIPTION": "Hier geht es um diese Webseite.",
|
"SHORT_DESCRIPTION": "Full-Stack Portfolio mit interaktiven Algorithmus-Visualisierungen.",
|
||||||
"INTRODUCTION": "Dieses Projekt ist hauptsächlich als eine Art 'Spielwiese' gestartet, daher der Name. Es ist geplant, die Seite mit der Zeit weiter auszubauen. Dabei werden hier neue Projekte auftauchen, oder ich werde die Seite an für sich weiter ausbauen, weil ich neue Sachen im Rahmen von Web Technologien ausprobieren möchte.",
|
"INTRODUCTION": "Diese Website dient als lebendiges Portfolio und Testumgebung für moderne Webtechnologien. Ziel ist es, komplexe Algorithmen und mathematische Konzepte (wie WebGPU-Simulationen oder Raymarching) anschaulich im Browser darzustellen.",
|
||||||
"BULLET_1": "Verwendung moderner Technologien und CI/CD-Pipelines (Angular 20+, Spring Boot 4, GitHub).",
|
"BULLET_1": "Entwicklung mit Angular 19+ und Material Design.",
|
||||||
"BULLET_2": "Präsentation persönlicher Projekte und kontinuierliche Verbesserung algorithmischer Fähigkeiten.",
|
"BULLET_2": "Implementierung performanter Visualisierungen (WebGPU, Shader, Canvas).",
|
||||||
"BULLET_3": "Vertiefung von JavaScript/TypeScript-, Angular- und Spring-Boot-Kenntnissen durch praktisches Arbeiten.",
|
"BULLET_3": "Automatisierte CI/CD-Pipelines und Containerisierung mit Docker.",
|
||||||
"BULLET_4": "Die Seite ist Open Source und auf GitHub verfügbar."
|
"BULLET_4": "Internationalisierung (i18n) für globale Reichweite.",
|
||||||
|
"CHALLENGE_1": "Optimierung der Render-Performance bei komplexen 3D-Fraktalen in Echtzeit.",
|
||||||
|
"CHALLENGE_2": "Architektur einer skalierbaren und wartbaren Frontend-Struktur für diverse Sub-Projekte.",
|
||||||
|
"LEARNING_1": "Effektives UI/UX-Design für komplexe datengesteuerte Visualisierungen.",
|
||||||
|
"LEARNING_2": "Modernstes State-Management und reaktive Programmierung in Angular."
|
||||||
},
|
},
|
||||||
"TRIBBLE": {
|
"TRIBBLE": {
|
||||||
"TITLE": "Trouble with Tribble",
|
"TITLE": "Self-Hosted Infrastructure",
|
||||||
"SHORT_DESCRIPTION": "Ein Projekt, das die Einrichtung und Wartung eines Homeservers beschreibt, auf dem verschiedene Docker-Container für Self-Hosting-Dienste laufen.",
|
"SHORT_DESCRIPTION": "Home-Infrastruktur mit Docker, Traefik und sicherer VPN-Anbindung.",
|
||||||
"INTRODUCTION": "Dieses Projekt dokumentiert die Einrichtung eines persönlichen Homeservers mit dem Spitznamen \"Tribble\". Es umfasst die Installation von Ubuntu Server und die Containerisierung von Diensten wie Gitea für die Versionskontrolle, Jellyfin für das Mediastreaming und AdGuard Home für das Blockieren von Werbung im Netzwerk. Der Server ist über Traefik als Reverse-Proxy und Tailscale für eine sichere Netzwerkverbindung mit dem Internet verbunden, was das Self-Hosting der CI/CD-Pipeline dieser Website ermöglicht.",
|
"INTRODUCTION": "Dokumentation und Aufbau einer privaten Cloud-Infrastruktur. Fokus liegt auf Datensouveränität, Automatisierung und Sicherheit.",
|
||||||
"BULLET_1": "Self-Hosting verschiedener Dienste mit Docker.",
|
"BULLET_1": "Zentrale Verwaltung via Docker-Compose und Portainer.",
|
||||||
"BULLET_2": "CI/CD-Pipeline für die persönliche Website mit Gitea.",
|
"BULLET_2": "Automatisches SSL-Management und Reverse-Proxy mit Traefik.",
|
||||||
"BULLET_3": "Sicherer Fernzugriff mit Tailscale und Traefik.",
|
"BULLET_3": "Private Versionskontrolle (Gitea) und Medien-Streaming (Jellyfin).",
|
||||||
"BULLET_4": "Netzwerkweites Blockieren von Werbung mit AdGuard Home."
|
"BULLET_4": "Netzwerkweite Ad-Blocking und DNS-Kontrolle via AdGuard Home.",
|
||||||
|
"CHALLENGE_1": "Konfiguration sicherer Netzwerkschichten und Firewall-Regeln für Remote-Zugriff.",
|
||||||
|
"CHALLENGE_2": "Automatisierung von Backups und Recovery-Strategien für containerisierte Daten.",
|
||||||
|
"LEARNING_1": "Tiefes Verständnis für moderne Netzwerkprotokolle und IT-Sicherheit.",
|
||||||
|
"LEARNING_2": "Effizientes Ressourcen-Management auf limitierten Server-Systemen."
|
||||||
},
|
},
|
||||||
"EL_MUCHO": {
|
"EL_MUCHO": {
|
||||||
"TITLE": "El Mucho",
|
"TITLE": "El Mucho (Steam Release)",
|
||||||
"SHORT_DESCRIPTION": "Hier geht es um mein ersten Spiel auf Steam.",
|
"SHORT_DESCRIPTION": "Rundenbasiertes Taktik-RPG, veröffentlicht auf Steam.",
|
||||||
"INTRODUCTION": "El Mucho ist ein rundenbasiertes taktisches RPG in einer fiktiven Welt namens Liberika. Es ist angelehnt an alte Klassiker wie Langrisser aka Warsong. In El Mucho geht es darum, die Welt gegen die Angriffe der fiesen Monster zu verteidigen.",
|
"INTRODUCTION": "Ein kommerzielles Spieleprojekt, das von der ersten Idee bis zum weltweiten Release auf Steam eigenverantwortlich umgesetzt wurde. Ein taktisches RPG, das klassische Gameplay-Elemente mit modernen Systemen verbindet.",
|
||||||
"BULLET_1": "Veröffentlichung eines Spiels auf Steam und Integration der Steam-API.",
|
"BULLET_1": "Komplette Engine-Entwicklung in Unity (C#).",
|
||||||
"BULLET_2": "Konzeption, Planung und vollständige Entwicklung eines eigenen Spiels.",
|
"BULLET_2": "Integration von Steamworks-Funktionen (Achievements, Cloud Saves).",
|
||||||
"BULLET_3": "Implementierung komplexer Algorithmen wie einer eigenen A*-Pfadfindungslogik und Spiel-KI.",
|
"BULLET_3": "Entwicklung einer eigenen taktischen KI und Pfadfindungs-Logik.",
|
||||||
"BULLET_4": "Das Spiel wurde mit Unity und C# entwickelt."
|
"BULLET_4": "Management des gesamten Asset-Pipelines und Sound-Designs.",
|
||||||
|
"CHALLENGE_1": "Implementierung eines robusten rundenbasierten Systems mit komplexen Abhängigkeiten.",
|
||||||
|
"CHALLENGE_2": "Performance-Optimierung für eine flüssige Darstellung auf verschiedenen Hardware-Profilen.",
|
||||||
|
"CHALLENGE_3": "Umgang mit den strengen Zertifizierungs-Anforderungen von Steam.",
|
||||||
|
"LEARNING_1": "Durchhaltevermögen und Fokus über einen mehrjährigen Entwicklungszyklus.",
|
||||||
|
"LEARNING_2": "Vermarktung und Community-Management für ein digitales Produkt."
|
||||||
},
|
},
|
||||||
"GAME_JAMS": {
|
"GAME_JAMS": {
|
||||||
"TITLE": "Game Jams",
|
"TITLE": "Rapid Prototyping & Game Jams",
|
||||||
"SHORT_DESCRIPTION": "Hier geht es meine Teilnahme an mehreren Game Jams.",
|
"SHORT_DESCRIPTION": "Sammlung innovativer Spielkonzepte, entstanden in unter 48 Stunden.",
|
||||||
"INTRODUCTION": "Da ich mich für die Entwicklung von Spielen interessiert, sind Game Jams für mich optimal, um fokussiert an neuen Ideen zu arbeiten und dabei Prototypen zu entwickeln, um zu sehen, ob Spielideen funktionieren oder nicht. In den letzten Jahren habe ich an einigen Game Jams teilgenommen und fasse das hier zusammen.",
|
"INTRODUCTION": "Teilnahme an internationalen Wettbewerben (z.B. Ludum Dare). Hier geht es darum, unter extremem Zeitdruck funktionale und spaßige Prototypen zu erschaffen.",
|
||||||
"BULLET_1": "Planung eines realistischen Projektumfangs mit einem Team, der innerhalb von 48 Stunden umsetzbar ist.",
|
"BULLET_1": "Fokus auf 'Core Game Loop' und schnelles Feedback.",
|
||||||
"BULLET_2": "Lernen, fokussiert und effizient unter strengen Zeitvorgaben zu arbeiten.",
|
"BULLET_2": "Kollaborative Entwicklung in kleinen, agilen Teams.",
|
||||||
"BULLET_3": "Die Freude zu erleben, in kurzer Zeit ein spielbares Projekt zu erstellen und andere damit spielen zu sehen.",
|
"BULLET_3": "Effektives Zeitmanagement und Scope-Kontrolle.",
|
||||||
"BULLET_4": "Alle Projekte sind auf Itch.io verfügbar und spielbar."
|
"BULLET_4": "Veröffentlichung und Iteration basierend auf Community-Votings.",
|
||||||
|
"CHALLENGE_1": "Reduzierung komplexer Ideen auf ein in 48h umsetzbares Minimum Viable Product (MVP).",
|
||||||
|
"CHALLENGE_2": "Schnelle Fehlerdiagnose und Bugfixing unter massivem Zeitdruck.",
|
||||||
|
"LEARNING_1": "Radikale Priorisierung von Features ('Kill your darlings').",
|
||||||
|
"LEARNING_2": "Effektive Kommunikation und Entscheidungsfindung im Team-Stress."
|
||||||
},
|
},
|
||||||
"DIPLOMA": {
|
"DIPLOMA": {
|
||||||
"TITLE": "Diplomarbeit",
|
"TITLE": "Wissenschaftliche Diplomarbeit",
|
||||||
"SHORT_DESCRIPTION": "Kollisionserkennung und Behandlung von komplexen Kleidungsstücken.",
|
"SHORT_DESCRIPTION": "Echtzeit-Kollisionserkennung für komplexe, flexible 3D-Objekte.",
|
||||||
"INTRODUCTION": "Die Diplomarbeit handelt von der Erkennung und der Behandlung von Kollisionen zwischen, sowie innerhalb, einzelnen Kleidungsstücken in Echtzeit. Das ist gerade aufgrund der Flexibilität von Stoffen und deren unterschiedlichen Eigenschaften besonders herausfordernd.",
|
"INTRODUCTION": "Forschungsarbeit im Bereich Computergraphik. Entwicklung eines Algorithmus zur physikalisch korrekten Simulation von Stoffen und Kleidung in Echtzeit.",
|
||||||
"BULLET_1": "Echtzeit behandlung von Kollisionserkennung und Behandlung.",
|
"BULLET_1": "Mathematische Modellierung von Mass-Spring-Systemen.",
|
||||||
"BULLET_2": "Verstehen und Einschätzen von wissenschaftlichen Arbeiten.",
|
"BULLET_2": "Low-Level Programmierung mit C++, OpenGL und Shader-Code.",
|
||||||
"BULLET_3": "Adaption und Weiterentwicklung von vorausgegangenen Forschungsarbeiten.",
|
"BULLET_3": "Optimierung durch räumliche Datenstrukturen (AABB Trees, Bounding Spheres).",
|
||||||
"BULLET_4": "Die Arbeit wurde mit C++ und OpenGL geschrieben und in die Vidya-Software integriert."
|
"BULLET_4": "Wissenschaftliche Evaluation der Simulations-Präzision.",
|
||||||
|
"CHALLENGE_1": "Behandlung von 'Self-Collisions' bei hochauflösenden Meshes ohne Performance-Einbruch.",
|
||||||
|
"CHALLENGE_2": "Mathematische Stabilisierung der Integrationsverfahren bei hohen Krafteinwirkungen.",
|
||||||
|
"LEARNING_1": "Transfer von theoretischen Forschungsarbeiten in produktiven, performanten Code.",
|
||||||
|
"LEARNING_2": "Präzises Arbeiten und Dokumentation nach wissenschaftlichen Standards."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"IMPRINT": {
|
"IMPRINT": {
|
||||||
@@ -386,7 +419,7 @@
|
|||||||
"EXPLANATION": {
|
"EXPLANATION": {
|
||||||
"TITLE": "Mathematische Kunst",
|
"TITLE": "Mathematische Kunst",
|
||||||
"MANDELBROT_EXPLANATION": "basiert auf der iterativen Formel 'z_{n+1} = z_n^2 + c'. Sie prüft für jeden Punkt in der komplexen Ebene, ob die Zahlenfolge stabil bleibt oder ins Unendliche entkommt. Vorteil: Gilt als 'Apfelmännchen' und Mutter der Fraktale. Sie bietet eine unendliche Vielfalt an selbstähnlichen Strukturen, in die man ewig hineinzoomen kann.",
|
"MANDELBROT_EXPLANATION": "basiert auf der iterativen Formel 'z_{n+1} = z_n^2 + c'. Sie prüft für jeden Punkt in der komplexen Ebene, ob die Zahlenfolge stabil bleibt oder ins Unendliche entkommt. Vorteil: Gilt als 'Apfelmännchen' und Mutter der Fraktale. Sie bietet eine unendliche Vielfalt an selbstähnlichen Strukturen, in die man ewig hineinzoomen kann.",
|
||||||
"JULIA_EXPLANATION": "nutzt dieselbe Formel wie Mandelbrot, fixiert jedoch den Parameter 'c' und variiert den Startwert. Je nach Wahl von 'c' entstehen filigrane, wolkenartige Gebilde oder zusammenhanglose 'Staubwolken'. Vorteil: Ermöglicht eine enorme ästhetische Varianz, da jede Koordinate der Mandelbrot-Menge ein völlig eigenes, einzigartiges Julia-Fraktal erzeugt.",
|
"JULIA_EXPLANATION": "nutzt dieselbe Formel wie Mandelbrot, fixiert jedoch den Parameter 'c' und variiert den Startwert. Je nach Wahl von 'c' entstehen filigrane, wolkenartige Gebilde oder zusammenhanglose 'Staubwolken'. Vorteil: Ermöglicht eine enorme ästiehetische Varianz, da jede Koordinate der Mandelbrot-Menge ein völlig eigenes, einzigartiges Julia-Fraktal erzeugt.",
|
||||||
"NEWTON_EXPLANATION": "entsteht durch die Visualisierung des Newton-Verfahrens zur Nullstellen-Suche einer komplexen Funktion. Jeder Pixel wird danach eingefärbt, zu welcher Nullstelle der Algorithmus konvergiert. Vorteil: Erzeugt faszinierende, sternförmige Symmetrien und komplexe Grenzen, an denen sich die Einzugsgebiete der Nullstellen auf chaotische Weise treffen.",
|
"NEWTON_EXPLANATION": "entsteht durch die Visualisierung des Newton-Verfahrens zur Nullstellen-Suche einer komplexen Funktion. Jeder Pixel wird danach eingefärbt, zu welcher Nullstelle der Algorithmus konvergiert. Vorteil: Erzeugt faszinierende, sternförmige Symmetrien und komplexe Grenzen, an denen sich die Einzugsgebiete der Nullstellen auf chaotische Weise treffen.",
|
||||||
"BURNING_SHIP_EXPLANATION": "ist eine Variation des Mandelbrots, bei der vor jedem Iterationsschritt der Absolutbetrag der Real- und Imaginärteile genommen wird: '(|Re(z)| + i|Im(z)|)^2 + c'. Vorteil: Erzeugt eine markante, asymmetrische Struktur, die einem brennenden Schiff mit Segeln ähnelt. Das Fraktal wirkt düsterer und 'mechanischer' als die klassischen Mengen.",
|
"BURNING_SHIP_EXPLANATION": "ist eine Variation des Mandelbrots, bei der vor jedem Iterationsschritt der Absolutbetrag der Real- und Imaginärteile genommen wird: '(|Re(z)| + i|Im(z)|)^2 + c'. Vorteil: Erzeugt eine markante, asymmetrische Struktur, die einem brennenden Schiff mit Segeln ähnelt. Das Fraktal wirkt düsterer und 'mechanischer' als die klassischen Mengen.",
|
||||||
"DISCLAIMER": "Alle diese Fraktale basieren auf dem Prinzip der Iteration und dem Chaos-Effekt. Das bedeutet für deine Visualisierung:",
|
"DISCLAIMER": "Alle diese Fraktale basieren auf dem Prinzip der Iteration und dem Chaos-Effekt. Das bedeutet für deine Visualisierung:",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"APP": {
|
"APP": {
|
||||||
"TITLE": "Playground",
|
"TITLE": "Playground",
|
||||||
"COPYRIGHT": "Images and code protected by copyright, no use without permission!"
|
"COPYRIGHT": "Images and code protected by copyright, no use without permission!"
|
||||||
@@ -25,21 +25,29 @@
|
|||||||
"CONTACT_ME": "Contact me",
|
"CONTACT_ME": "Contact me",
|
||||||
"SECTION": {
|
"SECTION": {
|
||||||
"SKILLS": "Skills & Stack",
|
"SKILLS": "Skills & Stack",
|
||||||
"PRIMARY": "Core",
|
"BACKEND_ARCH": "Backend & Architecture",
|
||||||
"TOOLSET": "Toolset",
|
"INFRA_CLOUD": "Infrastructure & Cloud",
|
||||||
|
"SIM_ALGO": "Simulation & Algorithms",
|
||||||
"EXPERIENCE": "Experience",
|
"EXPERIENCE": "Experience",
|
||||||
"PROJECTS": "Projects",
|
"PROJECTS": "Projects",
|
||||||
"EDUCATION": "Education"
|
"EDUCATION": "Education"
|
||||||
},
|
},
|
||||||
"SKILLS": {
|
"SKILLS": {
|
||||||
"JAVA": "Java 8/Java 21+",
|
"JAVA": "Java 8/21+",
|
||||||
"SPRING": "Spring Boot 2/3",
|
"SPRING": "Spring Boot 2/3",
|
||||||
"ANGULAR": "Angular 20+",
|
"ANGULAR": "Angular 19+",
|
||||||
"DOCKER": "Docker",
|
"DOCKER": "Docker",
|
||||||
"UNITY": "Unity",
|
"UNITY": "Unity",
|
||||||
"PYTHON": "Python",
|
"PYTHON": "Python",
|
||||||
"CSHARP": "C#",
|
"CSHARP": "C#",
|
||||||
"TYPESCRIPT": "TypeScript"
|
"TYPESCRIPT": "TypeScript",
|
||||||
|
"ARCH_MICROSERVICES": "Microservices",
|
||||||
|
"ARCH_CLOUD": "Cloud Architecture",
|
||||||
|
"ENG_ALGO": "Algorithm Design",
|
||||||
|
"ENG_SIM": "3D Simulation",
|
||||||
|
"ENG_GPU": "WebGPU / OpenGL / GLSL",
|
||||||
|
"ENG_PERF": "Performance Optimization",
|
||||||
|
"ENG_3D": "3D-Scanner Tech"
|
||||||
},
|
},
|
||||||
"TOOLS": {
|
"TOOLS": {
|
||||||
"GIT": "Git",
|
"GIT": "Git",
|
||||||
@@ -138,7 +146,7 @@
|
|||||||
"TIME": "Jul. 2002 – Jun. 2005",
|
"TIME": "Jul. 2002 – Jun. 2005",
|
||||||
"HIGHLIGHTS": {
|
"HIGHLIGHTS": {
|
||||||
"P1": "Development in PERL, PHP and ASP.",
|
"P1": "Development in PERL, PHP and ASP.",
|
||||||
"P2": "Porting, maintenance and reengineering of existing software.",
|
"P2": "Portierung, Wartung und Reengineering von bestender Software.",
|
||||||
"P3": "Regular performance of system tests and quality controls, as well as their documentation."
|
"P3": "Regular performance of system tests and quality controls, as well as their documentation."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +197,7 @@
|
|||||||
},
|
},
|
||||||
"TRIBBLE": {
|
"TRIBBLE": {
|
||||||
"TITLE": "Homeserver 'Tribble'",
|
"TITLE": "Homeserver 'Tribble'",
|
||||||
"DESCRIPTION": "This project is about setting up and maintaining my own homeserver. It runs several Docker containers like Gitea, Jellyfin, and more. It's a great learning experience in self-hosting and system administration.",
|
"DESCRIPTION": "This project is about setting up and maintaining my own homeserver. It runs several Docker containers like Gitea, Jellyfin and more. It's a great learning experience in self-hosting and system administration.",
|
||||||
"LINK_INTERNAL": "Project details",
|
"LINK_INTERNAL": "Project details",
|
||||||
"HIGHLIGHTS": {
|
"HIGHLIGHTS": {
|
||||||
"P1": "Self-hosting of various services using Docker.",
|
"P1": "Self-hosting of various services using Docker.",
|
||||||
@@ -241,50 +249,75 @@
|
|||||||
"READ_MORE": "Read More",
|
"READ_MORE": "Read More",
|
||||||
"LINK_TO_PROJECT": "To the project",
|
"LINK_TO_PROJECT": "To the project",
|
||||||
"CLOSE": "Close",
|
"CLOSE": "Close",
|
||||||
|
"SECTION": {
|
||||||
|
"TECHNICAL": "Technical Challenges",
|
||||||
|
"LEARNINGS": "Learnings & Soft Skills"
|
||||||
|
},
|
||||||
"PLAYGROUND": {
|
"PLAYGROUND": {
|
||||||
"TITLE": "Playground Website",
|
"TITLE": "Playground Portfolio",
|
||||||
"SHORT_DESCRIPTION": "This is about this website.",
|
"SHORT_DESCRIPTION": "Full-stack portfolio with interactive algorithm visualizations.",
|
||||||
"INTRODUCTION": "This project was mainly started as a kind of “playground”, hence the name. The plan is to expand the site over time. New projects will appear here, or I will continue to expand the site itself because I want to try out new things in the field of web technologies.",
|
"INTRODUCTION": "This website serves as a living portfolio and testing ground for modern web technologies. The goal is to clearly represent complex algorithms and mathematical concepts (such as WebGPU simulations or Raymarching) directly in the browser.",
|
||||||
"BULLET_1": "Using modern technologies and CI/CD pipelines (Angular 20+, Spring Boot 4, GitHub).",
|
"BULLET_1": "Development with Angular 19+ and Material Design.",
|
||||||
"BULLET_2": "Showcasing personal projects and improving algorithmic skills over time.",
|
"BULLET_2": "Implementation of performant visualizations (WebGPU, Shader, Canvas).",
|
||||||
"BULLET_3": "Deepening knowledge in JavaScript/TypeScript, Angular, Spring Boot and related technologies through hands-on practice.",
|
"BULLET_3": "Automated CI/CD pipelines and containerization with Docker.",
|
||||||
"BULLET_4": "The site is open source and available on GitHub."
|
"BULLET_4": "Internationalization (i18n) for global reach.",
|
||||||
|
"CHALLENGE_1": "Optimizing render performance for complex 3D fractals in real-time.",
|
||||||
|
"CHALLENGE_2": "Architecting a scalable and maintainable frontend structure for diverse sub-projects.",
|
||||||
|
"LEARNING_1": "Effective UI/UX design for complex data-driven visualizations.",
|
||||||
|
"LEARNING_2": "Advanced state management and reactive programming in Angular."
|
||||||
},
|
},
|
||||||
"TRIBBLE": {
|
"TRIBBLE": {
|
||||||
"TITLE": "Trouble with Tribble",
|
"TITLE": "Self-Hosted Infrastructure",
|
||||||
"SHORT_DESCRIPTION": "A project detailing the setup and maintenance of a home server running various Docker containers for self-hosting services.",
|
"SHORT_DESCRIPTION": "Home infrastructure with Docker, Traefik, and secure VPN connectivity.",
|
||||||
"INTRODUCTION": "This project documents the journey of setting up a personal home server, nicknamed \"Tribble\". It involves installing Ubuntu Server and containerizing services like Gitea for version control, Jellyfin for media streaming, and AdGuard Home for network-wide ad-blocking. The server is connected via Traefik as a reverse proxy and Tailscale for secure networking, enabling the self-hosted CI/CD pipeline for this website.",
|
"INTRODUCTION": "Documentation and construction of a private cloud infrastructure. Focus is on data sovereignty, automation, and security.",
|
||||||
"BULLET_1": "Self-hosting of various services using Docker.",
|
"BULLET_1": "Central management via Docker-Compose and Portainer.",
|
||||||
"BULLET_2": "CI/CD pipeline for the personal website using Gitea.",
|
"BULLET_2": "Automatic SSL management and reverse proxy with Traefik.",
|
||||||
"BULLET_3": "Secure remote access with Tailscale and Traefik.",
|
"BULLET_3": "Private version control (Gitea) and media streaming (Jellyfin).",
|
||||||
"BULLET_4": "Network-wide ad-blocking with AdGuard Home."
|
"BULLET_4": "Network-wide ad-blocking and DNS control via AdGuard Home.",
|
||||||
|
"CHALLENGE_1": "Configuring secure network layers and firewall rules for remote access.",
|
||||||
|
"CHALLENGE_2": "Automating backups and recovery strategies for containerized data.",
|
||||||
|
"LEARNING_1": "Deep understanding of modern network protocols and IT security.",
|
||||||
|
"LEARNING_2": "Efficient resource management on limited server systems."
|
||||||
},
|
},
|
||||||
"EL_MUCHO": {
|
"EL_MUCHO": {
|
||||||
"TITLE": "El Mucho",
|
"TITLE": "El Mucho (Steam Release)",
|
||||||
"SHORT_DESCRIPTION": "This is about my first game on steam.",
|
"SHORT_DESCRIPTION": "Turn-based tactical RPG, published on Steam.",
|
||||||
"INTRODUCTION": "El Mucho is a turn-based tactical RPG set in a fictional world called Liberika. It is inspired by old classics such as Langrisser, also known as Warsong. El Mucho is about defending the world against attacks from nasty monsters.",
|
"INTRODUCTION": "A commercial game project that was independently implemented from the initial idea to the worldwide release on Steam. A tactical RPG that combines classic gameplay elements with modern systems.",
|
||||||
"BULLET_1": "Publishing a game on Steam and integrating the Steam API.",
|
"BULLET_1": "Complete engine development in Unity (C#).",
|
||||||
"BULLET_2": "Designing, planning and developing a complete game from scratch.",
|
"BULLET_2": "Integration of Steamworks features (Achievements, Cloud Saves).",
|
||||||
"BULLET_3": "Implementing complex algorithms, including a custom A* pathfinding system and game AI logic.",
|
"BULLET_3": "Development of a custom tactical AI and pathfinding logic.",
|
||||||
"BULLET_4": "The game was developed with Unity and C#."
|
"BULLET_4": "Management of the entire asset pipeline and sound design.",
|
||||||
|
"CHALLENGE_1": "Implementing a robust turn-based system with complex dependencies.",
|
||||||
|
"CHALLENGE_2": "Performance optimization for a smooth experience across various hardware profiles.",
|
||||||
|
"CHALLENGE_3": "Handling Steam's strict certification requirements.",
|
||||||
|
"LEARNING_1": "Perseverance and focus over a multi-year development cycle.",
|
||||||
|
"LEARNING_2": "Marketing and community management for a digital product."
|
||||||
},
|
},
|
||||||
"GAME_JAMS": {
|
"GAME_JAMS": {
|
||||||
"TITLE": "Game Jams",
|
"TITLE": "Rapid Prototyping & Game Jams",
|
||||||
"SHORT_DESCRIPTION": "This is about my participation at several game jams.",
|
"SHORT_DESCRIPTION": "Collection of innovative game concepts, created in under 48 hours.",
|
||||||
"INTRODUCTION": "Since I am interested in game development, game jams are ideal for me to focus on new ideas and develop prototypes to see whether game ideas work or not. I have participated in several game jams over the past few years and summarise my experiences here.",
|
"INTRODUCTION": "Participation in international competitions (e.g., Ludum Dare). The focus is on creating functional and fun prototypes under extreme time pressure.",
|
||||||
"BULLET_1": "Planning a realistic project scope with a team that can be built within 48 hours.",
|
"BULLET_1": "Focus on 'Core Game Loop' and fast feedback.",
|
||||||
"BULLET_2": "Learning to stay focused and work effectively under strict time constraints.",
|
"BULLET_2": "Collaborative development in small, agile teams.",
|
||||||
"BULLET_3": "Experiencing the joy of creating a playable game in a short timeframe and seeing others enjoy it.",
|
"BULLET_3": "Effective time management and scope control.",
|
||||||
"BULLET_4": "All projects are available and playable on Itch.io."
|
"BULLET_4": "Publishing and iteration based on community voting.",
|
||||||
|
"CHALLENGE_1": "Reducing complex ideas to a Minimum Viable Product (MVP) achievable in 48h.",
|
||||||
|
"CHALLENGE_2": "Rapid bug diagnosis and fixing under massive time pressure.",
|
||||||
|
"LEARNING_1": "Radical prioritization of features ('Kill your darlings').",
|
||||||
|
"LEARNING_2": "Effective communication and decision-making under team stress."
|
||||||
},
|
},
|
||||||
"DIPLOMA": {
|
"DIPLOMA": {
|
||||||
"TITLE": "Diploma thesis",
|
"TITLE": "Scientific Diploma Thesis",
|
||||||
"SHORT_DESCRIPTION": "Collision detection and handling of complex garments.",
|
"SHORT_DESCRIPTION": "Real-time collision detection for complex, flexible 3D objects.",
|
||||||
"INTRODUCTION": "The thesis deals with the detection and handling of collisions between and within individual items of clothing in real time. This is particularly challenging due to the flexibility of fabrics and their varying properties.",
|
"INTRODUCTION": "Research work in the field of computer graphics. Development of an algorithm for physically correct simulation of fabrics and clothing in real-time.",
|
||||||
"BULLET_1": "Real-time handling of collision detection and response.",
|
"BULLET_1": "Mathematical modeling of mass-spring systems.",
|
||||||
"BULLET_2": "Understanding and evaluating scientific papers.",
|
"BULLET_2": "Low-level programming with C++, OpenGL, and shader code.",
|
||||||
"BULLET_3": "Adaptation and further development of previous research work.",
|
"BULLET_3": "Optimization through spatial data structures (AABB Trees, Bounding Spheres).",
|
||||||
"BULLET_4": "The thesis was written with C++ and OpenGL and integrated into the Vidya software."
|
"BULLET_4": "Scientific evaluation of simulation precision.",
|
||||||
|
"CHALLENGE_1": "Handling 'self-collisions' in high-resolution meshes without performance loss.",
|
||||||
|
"CHALLENGE_2": "Mathematical stabilization of integration methods under high force impacts.",
|
||||||
|
"LEARNING_1": "Transferring theoretical research into productive, high-performance code.",
|
||||||
|
"LEARNING_2": "Precise working and documentation according to scientific standards."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"IMPRINT": {
|
"IMPRINT": {
|
||||||
@@ -393,7 +426,7 @@
|
|||||||
"DISCLAIMER_2": "Escape-Time Algorithm: Colors usually represent how quickly a sequence exceeds a certain threshold—the faster it escapes, the 'hotter' or brighter the color.",
|
"DISCLAIMER_2": "Escape-Time Algorithm: Colors usually represent how quickly a sequence exceeds a certain threshold—the faster it escapes, the 'hotter' or brighter the color.",
|
||||||
"DISCLAIMER_3": "Complex Numbers: Calculations don't happen in a standard coordinate system, but in the complex plane using real and imaginary components.",
|
"DISCLAIMER_3": "Complex Numbers: Calculations don't happen in a standard coordinate system, but in the complex plane using real and imaginary components.",
|
||||||
"DISCLAIMER_4": "Computational Load: Since hundreds of calculations are performed for every single pixel, fractals are a classic benchmark for GPU and processor performance.",
|
"DISCLAIMER_4": "Computational Load: Since hundreds of calculations are performed for every single pixel, fractals are a classic benchmark for GPU and processor performance.",
|
||||||
"DISCLAIMER_BOTTOM": "Graphics cards calculate with 32-bit floating point numbers (float) by default. These only have about 7 digits of accuracy. At very high zoom levels (> 100,000), the difference between two pixels is so tiny that the graphics card can no longer display it. It calculates exactly the same value for 10 pixels next to each other -> you see blocks or stair steps."
|
"DISCLAIMER_BOTTOM": "Graphics cards calculate with 32-bit floating point numbers (float) by default. These only have about 7 digits of accuracy. At very high zoom levels (> 100.000), the difference between two pixels is so tiny that the graphics card can no longer display it. It calculates exactly the same value for 10 pixels next to each other -> you see blocks or stair steps."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"FRACTAL3D": {
|
"FRACTAL3D": {
|
||||||
@@ -422,7 +455,7 @@
|
|||||||
"L1_LENGTH": "Length L1",
|
"L1_LENGTH": "Length L1",
|
||||||
"L2_LENGTH": "Length L2",
|
"L2_LENGTH": "Length L2",
|
||||||
"M1_MASS": "Mass M1",
|
"M1_MASS": "Mass M1",
|
||||||
"M2_MASS": "Mass M2",
|
"M2_MASS": "Masse M2",
|
||||||
"POKE_M1": "Poke M1",
|
"POKE_M1": "Poke M1",
|
||||||
"POKE_M2": "Poke M2",
|
"POKE_M2": "Poke M2",
|
||||||
"RESET": "Reset",
|
"RESET": "Reset",
|
||||||
|
|||||||
BIN
src/assets/projects/playground/1.png
Normal file
BIN
src/assets/projects/playground/1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
622
src/styles.scss
622
src/styles.scss
@@ -1,14 +1,12 @@
|
|||||||
@use '@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
// ---- Themes ----
|
// ---- Themes ----
|
||||||
$light-theme: mat.define-theme((
|
$light-theme: mat.define-theme((color: (theme-type: light, primary: mat.$cyan-palette, tertiary: mat.$orange-palette ),
|
||||||
color: ( theme-type: light, primary: mat.$cyan-palette, tertiary: mat.$orange-palette ),
|
|
||||||
typography: (brand-family: 'Inter, Roboto, Arial, sans-serif', bold-weight: 600),
|
typography: (brand-family: 'Inter, Roboto, Arial, sans-serif', bold-weight: 600),
|
||||||
density: (scale: 0),
|
density: (scale: 0),
|
||||||
));
|
));
|
||||||
|
|
||||||
$dark-theme: mat.define-theme((
|
$dark-theme: mat.define-theme((color: (theme-type: dark, primary: mat.$cyan-palette, tertiary: mat.$orange-palette ),
|
||||||
color: ( theme-type: dark, primary: mat.$cyan-palette, tertiary: mat.$orange-palette ),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
// ---- Core + Components ----
|
// ---- Core + Components ----
|
||||||
@@ -23,11 +21,12 @@ $dark-theme: mat.define-theme((
|
|||||||
|
|
||||||
/* ---- Custom variables ---- */
|
/* ---- Custom variables ---- */
|
||||||
:root {
|
:root {
|
||||||
--app-topbar-bg: #{mat.get-theme-color($light-theme, surface)};
|
--app-maxWidth: 1200px;
|
||||||
--app-bg: #{mat.get-theme-color($light-theme, surface-container-low)};
|
--app-bg: #{mat.get-theme-color($light-theme, surface-container-low)};
|
||||||
--app-fg: #{mat.get-theme-color($light-theme, on-surface)};
|
--app-fg: #{mat.get-theme-color($light-theme, on-surface)};
|
||||||
--app-logo-bg: #313131;
|
--app-logo-bg: #313131;
|
||||||
--app-card-background: #fafafa;
|
--app-card-background: #fafafa;
|
||||||
|
--app-topbar-bg: var(--app-card-background);
|
||||||
|
|
||||||
--card-radius: 18px;
|
--card-radius: 18px;
|
||||||
--card-bg: var(--app-card-background);
|
--card-bg: var(--app-card-background);
|
||||||
@@ -37,12 +36,13 @@ $dark-theme: mat.define-theme((
|
|||||||
--link-color: #38a7ff;
|
--link-color: #38a7ff;
|
||||||
--link-color-hover: #66bfff;
|
--link-color-hover: #66bfff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--app-topbar-bg: #{mat.get-theme-color($dark-theme, surface-container-highest)};
|
|
||||||
--app-bg: #{mat.get-theme-color($dark-theme,surface-variant)};
|
--app-bg: #{mat.get-theme-color($dark-theme,surface-variant)};
|
||||||
--app-fg: #{mat.get-theme-color($dark-theme, on-surface)};
|
--app-fg: #{mat.get-theme-color($dark-theme, on-surface)};
|
||||||
--app-card-background: #313131;
|
--app-card-background: #313131;
|
||||||
--app-logo-bg: #313131;
|
--app-logo-bg: #313131;
|
||||||
|
--app-topbar-bg: var(--app-card-background);
|
||||||
|
|
||||||
--card-bg: var(--app-card-background);
|
--card-bg: var(--app-card-background);
|
||||||
|
|
||||||
@@ -51,7 +51,11 @@ $dark-theme: mat.define-theme((
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- global background and tests ---- */
|
/* ---- global background and tests ---- */
|
||||||
html, body { height: 100%; }
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Inter, Roboto, Arial, sans-serif;
|
font-family: Inter, Roboto, Arial, sans-serif;
|
||||||
@@ -62,10 +66,14 @@ body {
|
|||||||
|
|
||||||
.material-symbols-outlined {
|
.material-symbols-outlined {
|
||||||
font-variation-settings:
|
font-variation-settings:
|
||||||
"FILL" 0, /* 0 oder 1 */
|
"FILL" 0,
|
||||||
"wght" 400, /* 100..700 */
|
/* 0 oder 1 */
|
||||||
"GRAD" 0, /* -50..200 */
|
"wght" 400,
|
||||||
"opsz" 24; /* 20..48 */
|
/* 100..700 */
|
||||||
|
"GRAD" 0,
|
||||||
|
/* -50..200 */
|
||||||
|
"opsz" 24;
|
||||||
|
/* 20..48 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* smooth transition between theme change */
|
/* smooth transition between theme change */
|
||||||
@@ -111,9 +119,9 @@ a {
|
|||||||
box-shadow 200ms ease,
|
box-shadow 200ms ease,
|
||||||
transform 200ms ease;
|
transform 200ms ease;
|
||||||
|
|
||||||
&.container {
|
&.algo-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1200px;
|
max-width: 1920px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +156,8 @@ a {
|
|||||||
.mat-accordion .mat-expansion-panel {
|
.mat-accordion .mat-expansion-panel {
|
||||||
border-radius: var(--card-radius) !important;
|
border-radius: var(--card-radius) !important;
|
||||||
background: var(--card-bg) !important;
|
background: var(--card-bg) !important;
|
||||||
overflow: hidden; /* ok */
|
overflow: hidden;
|
||||||
|
/* ok */
|
||||||
border: none !important;
|
border: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,8 +229,11 @@ a {
|
|||||||
|
|
||||||
// algos
|
// algos
|
||||||
|
|
||||||
.container {
|
.algo-container {
|
||||||
padding: 2rem;
|
width: 100%;
|
||||||
|
max-width: var(--app-maxWidth);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: clamp(1rem, 4vw, 2rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.algo-info {
|
.algo-info {
|
||||||
@@ -276,6 +288,7 @@ a {
|
|||||||
canvas {
|
canvas {
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
display: block;
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,17 +307,49 @@ canvas {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
|
|
||||||
&.start { background-color: green; }
|
&.start {
|
||||||
&.end { background-color: red; }
|
background-color: green;
|
||||||
&.wall { background-color: black; }
|
}
|
||||||
&.visited { background-color: skyblue; }
|
|
||||||
&.path { background-color: gold; }
|
&.end {
|
||||||
&.empty { background-color: lightgray; }
|
background-color: red;
|
||||||
&.alive { background-color: black; }
|
}
|
||||||
&.L1 { background-color: yellow; }
|
|
||||||
&.L2 { background-color: magenta; }
|
&.wall {
|
||||||
&.M1 { background-color: red; }
|
background-color: black;
|
||||||
&.M2 { background-color: green; }
|
}
|
||||||
|
|
||||||
|
&.visited {
|
||||||
|
background-color: skyblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.path {
|
||||||
|
background-color: gold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.empty {
|
||||||
|
background-color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alive {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.L1 {
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.L2 {
|
||||||
|
background-color: magenta;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.M1 {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.M2 {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,39 +359,526 @@ canvas {
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider-control-container {
|
/* Sorting Visualization & Canvas */
|
||||||
display: flex;
|
.sorting-visualization-area,
|
||||||
align-items: center;
|
.visualization-area {
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sorting Visualization */
|
|
||||||
.sorting-visualization-area {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
height: 300px; /* Max height for bars */
|
height: clamp(200px, 40vh, 400px);
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid var(--app-fg);
|
||||||
margin-bottom: 20px;
|
margin-bottom: clamp(10px, 3vw, 20px);
|
||||||
gap: 1px;
|
gap: 1px;
|
||||||
background-color: #f0f0f0;
|
background-color: var(--card-bg);
|
||||||
|
|
||||||
.sorting-bar {
|
.sorting-bar,
|
||||||
|
.bar {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
background-color: #424242; /* Default unsorted color */
|
background-color: #424242;
|
||||||
transition: height 0.05s ease-in-out, background-color 0.05s ease-in-out;
|
transition: height 0.05s ease-in-out, background-color 0.05s ease-in-out;
|
||||||
width: 10px; /* Default width, flex-grow will adjust */
|
width: 10px;
|
||||||
min-width: 1px; /* Ensure bars are always visible */
|
min-width: 1px;
|
||||||
|
|
||||||
&.unsorted {
|
&.unsorted {
|
||||||
background-color: #424242;
|
background-color: #424242;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.comparing {
|
&.comparing {
|
||||||
background-color: #ffeb3b; /* Yellow for comparing */
|
background-color: #ffeb3b;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.sorted {
|
&.sorted {
|
||||||
background-color: #4caf50; /* Green for sorted */
|
background-color: #4caf50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Modern Layouts & Typography (Grid, Flex, Clamp) ---- */
|
||||||
|
|
||||||
|
.layout-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--app-maxWidth);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: clamp(1rem, 4vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
app-root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-surface {
|
||||||
|
flex-grow: 1;
|
||||||
|
background: var(--app-bg);
|
||||||
|
color: var(--app-fg);
|
||||||
|
transition: background-color 220ms ease, color 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.foot {
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, .08);
|
||||||
|
padding: clamp(1rem, 2vw, 1.5rem);
|
||||||
|
text-align: center;
|
||||||
|
opacity: .8;
|
||||||
|
background: var(--app-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Menu Overrides ---- */
|
||||||
|
.mat-mdc-menu-item .mdc-list-item__primary-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-menu-item .kbd {
|
||||||
|
margin-left: auto;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 0 .35rem;
|
||||||
|
opacity: .65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-menu-item .mat-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-menu-item .flag-icon {
|
||||||
|
width: 20px !important;
|
||||||
|
height: 14px !important;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: .5rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-menu-panel {
|
||||||
|
border-radius: 10px !important;
|
||||||
|
border: 1px solid rgba(0, 0, 0, .14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .mat-mdc-menu-panel {
|
||||||
|
border-color: rgba(255, 255, 255, .06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- About Page Sections ---- */
|
||||||
|
.about,
|
||||||
|
.imprint {
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(1rem, 3vw, 1.5rem);
|
||||||
|
max-width: var(--app-maxWidth);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
border-radius: var(--card-radius);
|
||||||
|
background: var(--card-bg);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-flex-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: clamp(1rem, 4vw, 2rem);
|
||||||
|
padding: clamp(1rem, 3vw, 1.5rem);
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .photo {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .photo img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-width: 425px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 6px 24px rgba(0, 0, 0, .25);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro {
|
||||||
|
flex: 1 1 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 850px) {
|
||||||
|
.hero-flex-container {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .photo {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: clamp(1.5rem, 5vw, 2.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro .lead {
|
||||||
|
opacity: .9;
|
||||||
|
margin: 0.5rem 0 1rem;
|
||||||
|
font-size: clamp(1rem, 2.5vw, 1.15rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro .meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .25rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro .meta .row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .intro .actions {
|
||||||
|
display: flex;
|
||||||
|
gap: .5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skills,
|
||||||
|
.experience,
|
||||||
|
.projects,
|
||||||
|
.education {
|
||||||
|
padding: clamp(5px, 2vw, 15px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skills h2,
|
||||||
|
.experience h2,
|
||||||
|
.projects h2,
|
||||||
|
.education h2 {
|
||||||
|
margin-top: .25rem;
|
||||||
|
margin-left: .25rem;
|
||||||
|
font-size: clamp(1.2rem, 4vw, 1.8rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skills .chip-groups {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
|
||||||
|
gap: clamp(0.5rem, 2vw, 1rem);
|
||||||
|
margin-left: .25rem;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skills .chip-groups h3 {
|
||||||
|
margin: .2rem 0 .4rem;
|
||||||
|
font-size: .95rem;
|
||||||
|
opacity: .85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-list {
|
||||||
|
margin-left: .25rem;
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(0.75rem, 2vw, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-item .xp-head {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-item .xp-head .time {
|
||||||
|
opacity: .75;
|
||||||
|
font-size: clamp(0.85rem, 2vw, 0.95rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-item .xp-sub {
|
||||||
|
opacity: .9;
|
||||||
|
margin-bottom: .25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-item ul {
|
||||||
|
margin: .25rem 0 .5rem 1.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xp-head-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: calc(clamp(32px, 8vw, 48px) + .75rem) 1fr;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
column-gap: clamp(0.5rem, 2vw, .75rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-wrap {
|
||||||
|
grid-row: 1 / span 2;
|
||||||
|
grid-column: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-logo {
|
||||||
|
width: clamp(32px, 8vw, 48px);
|
||||||
|
height: clamp(32px, 8vw, 48px);
|
||||||
|
object-fit: contain;
|
||||||
|
opacity: .9;
|
||||||
|
border-radius: 10%;
|
||||||
|
background-color: var(--app-logo-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-row {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: clamp(0.25rem, 1vw, 0.5rem) 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-row strong {
|
||||||
|
font-size: clamp(0.95rem, 2.5vw, 1.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-row .time {
|
||||||
|
opacity: .75;
|
||||||
|
font-size: clamp(0.85rem, 2vw, 0.95rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-row {
|
||||||
|
grid-row: 2;
|
||||||
|
grid-column: 2;
|
||||||
|
margin-top: .1rem;
|
||||||
|
opacity: .85;
|
||||||
|
font-size: clamp(0.85rem, 2vw, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlights {
|
||||||
|
margin-top: .4rem;
|
||||||
|
margin-left: clamp(0.25rem, 1vw, .75rem);
|
||||||
|
padding-left: clamp(0.8rem, 2vw, 1.2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlights li,
|
||||||
|
.highlights-noMargin li {
|
||||||
|
margin: .2rem 0;
|
||||||
|
font-size: clamp(0.9rem, 2vw, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Imprint ---- */
|
||||||
|
.imprint-card {
|
||||||
|
padding: clamp(1rem, 3vw, 1.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.imprint-title {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
font-size: clamp(1rem, 3vw, 1.2rem);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imprint-section {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imprint-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
opacity: 0.7;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Projects Page & Dialog ---- */
|
||||||
|
.project-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(1rem, 3vw, 1.5rem);
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(min(100%, 450px), 1fr));
|
||||||
|
max-width: var(--app-maxWidth);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card.featured {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card mat-card-header {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card mat-card-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card mat-chip-set {
|
||||||
|
padding-top: clamp(0.5rem, 2vw, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card mat-card-actions {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: clamp(150px, 20vw, 200px);
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fallback-icon {
|
||||||
|
font-size: clamp(3rem, 8vw, 4rem);
|
||||||
|
width: clamp(3rem, 8vw, 4rem);
|
||||||
|
height: clamp(3rem, 8vw, 4rem);
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
img[mat-card-image] {
|
||||||
|
width: 100%;
|
||||||
|
height: clamp(150px, 25vw, 250px);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-swiper {
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-swiper::part(button-prev),
|
||||||
|
.my-swiper::part(button-next) {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(0, 0, 0, .5);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-swiper::part(button-prev):hover,
|
||||||
|
.my-swiper::part(button-next):hover {
|
||||||
|
background: rgba(0, 0, 0, .75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-swiper::part(pagination) {
|
||||||
|
bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
swiper-slide {
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-height: clamp(300px, 60vh, 512px) !important;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-source {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #aaa;
|
||||||
|
background: #2a2a2a;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: right;
|
||||||
|
border-top: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-section {
|
||||||
|
display: flex;
|
||||||
|
gap: clamp(0.5rem, 2vw, 1rem);
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Shared Elements ---- */
|
||||||
|
.canvas-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-container canvas {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 1000px;
|
||||||
|
touch-action: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: clamp(10px, 2vw, 20px);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-cards {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: clamp(0.5rem, 2vw, 1rem);
|
||||||
|
margin-top: clamp(1rem, 3vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-cards mat-card {
|
||||||
|
cursor: pointer;
|
||||||
|
flex: 1 1 300px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-cards mat-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1920px;
|
||||||
|
padding: clamp(10px, 3vw, 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting-card .controls-panel {
|
||||||
|
display: flex;
|
||||||
|
gap: clamp(5px, 2vw, 10px);
|
||||||
|
margin-bottom: clamp(10px, 3vw, 20px);
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting-card .controls-panel mat-form-field {
|
||||||
|
width: clamp(150px, 20vw, 200px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting-card .info-panel {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user