main
Obai Albek 2025-09-18 18:17:19 -04:00
parent 2b0113b25b
commit 753702aa69
8 changed files with 596 additions and 369 deletions

Binary file not shown.

View File

@ -1,331 +1,186 @@
/* =========================================
Obai Albeek Portfolio Styles (Dark)
Palette: Onyx + Jade | v2
Obai Albek Portfolio Styles (Dark)
Palette: Onyx + Jade | v3
========================================= */
/* ---------- CSS Variables ---------- */
:root{
--bg: #0E1116; /* Onyx (Seitenhintergrund) */
--surface: #101721; /* Dunkle Card/Fläche */
--surface-2: #0B111A; /* noch dunklere Fläche */
--text: #E6E9EF; /* Primärtext hell */
--muted: #A8B0BF; /* Sekundärtext */
--line: rgba(255,255,255,.08);
--accent: #12B3A4; /* Jade/Türkis */
--accent-ink: #061014; /* Text auf Akzent (dunkel, angenehmer als weiß) */
--radius: 22px;
--shadow: 0 16px 40px rgba(0,0,0,.55);
--shadow-sm: 0 8px 24px rgba(0,0,0,.45);
--container: 1100px;
--space-1: .25rem;
--space-2: .5rem;
--space-3: .75rem;
--space-4: 1rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-7: 2rem;
--space-8: 2.5rem;
--space-9: 3rem;
--bg:#0E1116; --surface:#101721; --surface-2:#0B111A;
--text:#E6E9EF; --muted:#A8B0BF; --line:rgba(255,255,255,.08);
--accent:#12B3A4; --accent-ink:#061014;
--radius:22px;
--shadow:0 16px 40px rgba(0,0,0,.55);
--shadow-sm:0 8px 24px rgba(0,0,0,.45);
--container:1100px;
--space-1:.25rem; --space-2:.5rem; --space-3:.75rem; --space-4:1rem;
--space-5:1.25rem; --space-6:1.5rem; --space-7:2rem; --space-8:2.5rem; --space-9:3rem;
}
/* ---------- Reset & Base ---------- */
*,
*::before,
*::after{ box-sizing: border-box; }
html, body{ height: 100%; scroll-behavior: smooth; }
*,*::before,*::after{box-sizing:border-box}
html,body{height:100%;scroll-behavior:smooth}
body{
margin: 0;
font-family: "Roboto", system-ui, -apple-system, Segoe UI, Arial, sans-serif;
color: var(--text);
margin:0;font-family:"Roboto",system-ui,-apple-system,Segoe UI,Arial,sans-serif;
color:var(--text);
background:
radial-gradient(900px 600px at 20% -10%, #121925 0%, transparent 55%),
radial-gradient(1100px 700px at 110% -20%, #0F1721 0%, transparent 60%),
var(--bg);
line-height: 1.6;
line-height:1.6;
}
:focus-visible{outline:3px solid color-mix(in oklab,var(--accent),white 15%);outline-offset:2px}
.container{max-width:var(--container);margin-inline:auto;padding-inline:var(--space-7)}
:focus-visible{
outline: 3px solid color-mix(in oklab, var(--accent), white 15%);
outline-offset: 2px;
}
.container{
max-width: var(--container);
margin-inline: auto;
padding-inline: var(--space-7);
}
/* ---------- Sticky Topbar (Nav rechts, Name links) ---------- */
/* ---------- Topbar ---------- */
.topbar{
position: sticky;
top: 0;
z-index: 50;
display: flex; /* NEU: Flex-Layout */
align-items: center;
justify-content: space-between;/* sorgt dafür, dass Nav ganz rechts sitzt */
padding: var(--space-4) var(--space-7);
backdrop-filter: saturate(1.05) blur(8px);
background: color-mix(in oklab, var(--bg), black 20% / 65%);
border-bottom: 1px solid var(--line);
}
/* Brand / Name links */
.topbar .brand{
display: flex;
align-items: center;
gap: .6rem;
text-decoration: none;
position:sticky;top:0;z-index:50;display:flex;align-items:center;justify-content:space-between;
padding:var(--space-4) var(--space-7);
backdrop-filter:saturate(1.05) blur(8px);
background:color-mix(in oklab,var(--bg),black 20% / 65%);
border-bottom:1px solid var(--line);
}
.topbar .brand{display:flex;align-items:center;gap:.6rem;text-decoration:none}
.brand-mark{
display: inline-grid;
place-items: center;
width: 36px; height: 36px;
border-radius: 12px;
background: var(--accent);
color: var(--accent-ink);
font-weight: 800;
letter-spacing: .5px;
box-shadow: 0 8px 20px color-mix(in oklab, var(--accent), black 65%);
}
.brand-text{
color: var(--text);
font-weight: 800;
font-size: 1.08rem; /* Name deutlicher sichtbar */
letter-spacing: .2px;
}
/* Nav ganz rechts */
.nav{
margin-left: auto; /* bleibt als Absicherung */
display: flex;
gap: .6rem;
align-items: center;
display:inline-grid;place-items:center;width:36px;height:36px;border-radius:12px;
background:var(--accent);color:var(--accent-ink);font-weight:800;letter-spacing:.5px;
box-shadow:0 8px 20px color-mix(in oklab,var(--accent),black 65%);
}
.brand-text{color:var(--text);font-weight:800;font-size:1.08rem;letter-spacing:.2px}
.nav{margin-left:auto;display:flex;gap:.6rem;align-items:center}
.nav-link{
--pad-x: 1rem;
display: inline-block;
padding: .55rem var(--pad-x);
text-decoration: none;
color: var(--text);
background: rgba(255,255,255,.03);
border: 1px solid var(--line);
border-radius: 999px;
transition: transform .06s ease, box-shadow .2s ease, background .2s ease, border-color .2s ease;
box-shadow: var(--shadow-sm);
}
.nav-link:hover{
transform: translateY(-1px);
background: rgba(255,255,255,.06);
border-color: rgba(255,255,255,.12);
}
.nav-link.cta{
background: var(--accent);
color: var(--accent-ink);
border-color: transparent;
box-shadow: 0 10px 26px color-mix(in oklab, var(--accent), black 58%);
--pad-x:1rem;display:inline-block;padding:.55rem var(--pad-x);text-decoration:none;color:var(--text);
background:rgba(255,255,255,.03);border:1px solid var(--line);border-radius:999px;
transition:transform .06s ease,box-shadow .2s ease,background .2s ease,border-color .2s ease;
box-shadow:var(--shadow-sm);
}
.nav-link:hover{transform:translateY(-1px);background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.12)}
.nav-link.cta{background:var(--accent);color:var(--accent-ink);border-color:transparent;box-shadow:0 10px 26px color-mix(in oklab,var(--accent),black 58%)}
/* ---------- Hero ---------- */
.hero{ padding: clamp(3rem, 5vw, 5rem) var(--space-7); }
.hero{ padding: clamp(2rem, 3.6vw, 3rem) var(--space-7); }
.hero-card{
position: relative;
display: grid;
grid-template-columns: 1.1fr 1.4fr;
gap: var(--space-8);
align-items: center;
max-width: var(--container);
margin: 0 auto;
background: linear-gradient(180deg, var(--surface) 0%, color-mix(in oklab, var(--surface), black 8%) 100%);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: clamp(1.25rem, 3vw, 2rem);
box-shadow: var(--shadow);
position:relative;display:grid;grid-template-columns:1.05fr 1.6fr;gap:var(--space-8);align-items:center;
max-width:var(--container);margin:0 auto;background:linear-gradient(180deg,var(--surface) 0%,color-mix(in oklab,var(--surface),black 8%) 100%);
border:1px solid var(--line);border-radius:var(--radius);padding:clamp(1.2rem,2.4vw,1.9rem);box-shadow:var(--shadow);
}
.hero-media{ position: relative; min-height: 240px; }
.hero-media{position:relative;min-height:clamp(220px, 24vw, 300px)}
.hero-photo{
position: absolute;
inset: auto 0 0 50%;
transform: translate(-50%, 18%) scale(1.02);
width: min(320px, 78%);
aspect-ratio: 1/1;
object-fit: cover;
border-radius: 50%;
border: 10px solid var(--surface);
/* dezentes Teal-Glow */
box-shadow:
0 0 0 10px color-mix(in oklab, var(--accent), transparent 85%),
0 26px 60px color-mix(in oklab, var(--accent), black 70%);
background: #111;
position:absolute;inset:auto 0 0 50%;transform:translate(-50%,6%) scale(1);
width:clamp(185px, 24vw, 300px);aspect-ratio:1/1;object-fit:cover;border-radius:50%;
border:12px solid var(--surface);
box-shadow:0 0 0 12px color-mix(in oklab,var(--accent),transparent 85%),0 26px 60px color-mix(in oklab,var(--accent),black 70%);
background:#111;
}
/* Textblock */
.kicker{ margin: 0 0 .25rem 0; color: var(--muted); letter-spacing: .4px; }
.title{
margin: 0 0 .35rem 0;
font-size: clamp(2rem, 5vw, 3rem);
line-height: 1.15;
color: var(--text);
}
.subtitle{ margin: 0 0 var(--space-7) 0; color: var(--muted); }
/* ---------- Buttons ---------- */
.kicker{margin:0 0 .25rem;color:var(--muted);letter-spacing:.4px}
.title{margin:0 0 .35rem;font-size:clamp(2rem,5vw,3.1rem);line-height:1.15;color:var(--text)}
.subtitle{margin:0 0 var(--space-7);color:var(--muted)}
.actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-bottom:var(--space-6)}
.btn{
--px: 1.1rem;
display: inline-block;
padding: .75rem var(--px);
border-radius: 999px;
text-decoration: none;
font-weight: 800;
line-height: 1;
border: 1px solid transparent;
transition: transform .06s ease, box-shadow .2s ease, background .2s ease, border-color .2s ease;
cursor: pointer;
}
.btn:hover{ transform: translateY(-1px); }
.btn.primary{
background: var(--accent);
color: var(--accent-ink);
box-shadow: 0 10px 26px color-mix(in oklab, var(--accent), black 58%);
}
.btn.ghost{
background: rgba(255,255,255,.04);
color: var(--text);
border-color: var(--line);
}
.btn.small{
padding: .55rem .9rem;
font-weight: 700;
--px:1.1rem;display:inline-block;padding:.75rem var(--px);border-radius:999px;text-decoration:none;font-weight:800;line-height:1;
border:1px solid transparent;transition:transform .06s ease,box-shadow .2s ease,background .2s ease,border-color .2s ease;cursor:pointer;
}
.btn:hover{transform:translateY(-1px)}
.btn.primary{background:var(--accent);color:var(--accent-ink);box-shadow:0 10px 26px color-mix(in oklab,var(--accent),black 58%)}
.btn.ghost{background:rgba(255,255,255,.04);color:var(--text);border-color:var(--line)}
.btn.small{padding:.55rem .9rem;font-weight:700}
/* ---------- Social ---------- */
.social{ display:flex; gap:.5rem; margin:0; padding:0; list-style:none; }
.iconlink{
display: inline-grid;
place-items: center;
width: 40px; height: 40px;
border-radius: 12px;
border: 1px solid var(--line);
background: rgba(255,255,255,.03);
color: var(--text);
text-decoration: none;
box-shadow: var(--shadow-sm);
}
.iconlink:hover{
transform: translateY(-1px);
background: rgba(255,255,255,.06);
.social{display:flex;gap:.5rem;margin:0;padding:0;list-style:none}
.hero .social{margin-top:var(--space-7)}
a.iconlink{
display:inline-grid;place-items:center;width:44px;height:44px;border-radius:12px;border:1px solid var(--line);
background:rgba(255,255,255,.03);color:var(--text);text-decoration:none;box-shadow:var(--shadow-sm);
transition:background .2s ease,border-color .2s ease,transform .06s ease;
}
a.iconlink:hover{transform:translateY(-1px);background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.14)}
a.iconlink svg{display:block}
/* ---------- Sections ---------- */
.section{ padding: clamp(2.5rem, 5vw, 4rem) 0; scroll-margin-top: 90px; }
.section h2{ font-size: clamp(1.6rem, 3.4vw, 2rem); margin: 0 0 var(--space-5) 0; }
.section{padding:clamp(2.5rem,5vw,4rem) 0;scroll-margin-top:90px}
.section h2{font-size:clamp(1.6rem,3.4vw,2rem);margin:0 0 var(--space-5)}
.about .highlights{display:grid;grid-template-columns:1fr;gap:.5rem;padding:0;margin:var(--space-6) 0 0;list-style:"✓ "}
.about .highlights li{padding-left:.4rem;color:var(--text)}
/* About */
.about .highlights{
display: grid;
grid-template-columns: 1fr;
gap: .5rem;
padding: 0; margin: var(--space-6) 0 0 0;
list-style: "✓ ";
}
.about .highlights li{ padding-left: .4rem; color: var(--text); }
/* ---------- Skills ---------- */
.skills .skill-grid{display:grid;gap:var(--space-6);grid-template-columns:repeat(4,1fr)}
.skill{background:var(--surface-2);border:1px solid var(--line);border-radius:calc(var(--radius) - 6px);padding:var(--space-7);box-shadow:var(--shadow-sm)}
.skill h3{margin:0 0 .35rem}
.skill p{color:var(--muted);margin:0}
/* Skills */
.skills .skill-grid{
display: grid;
gap: var(--space-6);
grid-template-columns: repeat(4, 1fr);
}
.skill{
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: calc(var(--radius) - 6px);
padding: var(--space-7);
box-shadow: var(--shadow-sm);
}
.skill h3{ margin: 0 0 .35rem 0; }
.skill p{ color: var(--muted); margin: 0; }
/* Projects */
.project-list{
display: grid;
gap: var(--space-6);
grid-template-columns: repeat(3, 1fr);
}
/* ---------- Projekte ---------- */
.project-list{display:grid;gap:var(--space-6);grid-template-columns:repeat(3,1fr)}
.project-card{
background: var(--surface);
border: 1px solid var(--line);
border-radius: calc(var(--radius) - 6px);
padding: var(--space-7);
display: grid;
gap: .9rem;
box-shadow: var(--shadow-sm);
background:var(--surface);border:1px solid var(--line);border-radius:calc(var(--radius) - 6px);
padding:var(--space-7);display:grid;gap:.9rem;box-shadow:var(--shadow-sm);
}
.project-card .meta{ color: var(--muted); margin: .25rem 0 0 0; }
.project-actions{ display: flex; gap: .5rem; }
.project-card .meta{color:var(--muted);margin:.25rem 0 0}
.project-actions{display:flex;gap:.5rem}
/* Kontakt */
/* Filter/Search/Tags */
.project-controls{display:flex;gap:1rem;align-items:center;flex-wrap:wrap;margin-bottom:var(--space-6)}
.project-search{
flex:1 1 280px;min-width:260px;border:1px solid var(--line);border-radius:999px;padding:.8rem 1rem;background:#0A0F16;color:var(--text)
}
.filter-chips{display:flex;gap:.5rem;flex-wrap:wrap}
.chip{
padding:.55rem .9rem;border-radius:999px;background:rgba(255,255,255,.04);color:var(--text);border:1px solid var(--line);cursor:pointer
}
.chip.is-active{background:var(--accent);color:var(--accent-ink);border-color:transparent}
.tags{display:flex;gap:.4rem;flex-wrap:wrap;list-style:none;padding:0;margin:.25rem 0 0}
.tags li{
display:inline-flex;align-items:center;justify-content:center;
height:30px;padding:0 .65rem;width:auto;border-radius:999px;
font-size:.85rem;line-height:1;background:rgba(255,255,255,.05);border:1px solid var(--line);color:var(--muted)
}
.project-card[hidden]{display:none !important}
/* ---------- Kontakt ---------- */
.contact .contact-form{
margin-top: var(--space-6);
display: grid;
gap: var(--space-5);
grid-template-columns: 1fr 1fr;
margin-top:var(--space-6);
display:grid;
gap:var(--space-5);
grid-template-columns:1fr 1fr;
align-items:start;
}
.field{ display: grid; gap: .35rem; }
.field:nth-child(3){ grid-column: 1 / -1; }
label{ font-weight: 700; color: var(--text); }
input, textarea{
width: 100%;
border: 1px solid var(--line);
border-radius: 14px;
padding: .85rem 1rem;
font: inherit;
background: #0A0F16;
color: var(--text);
transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
.field{display:grid;gap:.35rem}
label{font-weight:700;color:var(--text)}
/* Nachricht über zwei Spalten robust gegen Honeypot */
.contact .contact-form > .field:nth-of-type(3){ grid-column:1 / -1; }
input,textarea{
width:100%;border:1px solid var(--line);border-radius:14px;padding:.85rem 1rem;font:inherit;background:#0A0F16;color:var(--text);
transition:border-color .2s,box-shadow .2s,background .2s;
}
input::placeholder, textarea::placeholder{ color: #8A93A5; }
input:focus, textarea:focus{
border-color: color-mix(in oklab, var(--accent), white 15%);
box-shadow: 0 0 0 6px color-mix(in oklab, var(--accent), black 75%);
input::placeholder,textarea::placeholder{color:#8A93A5}
input:focus,textarea:focus{
border-color:color-mix(in oklab,var(--accent),white 15%);
box-shadow:0 0 0 3px color-mix(in oklab,var(--accent),black 78%);
}
/* Footer */
.footer{
border-top: 1px solid var(--line);
background: #0A0F16;
padding: var(--space-6) 0;
text-align: center;
color: var(--muted);
/* Senden-Button */
.contact .contact-form .btn{
grid-column:1 / -1;
justify-self:start; /* center => mittig, start => links */
width:auto;
display:inline-flex; align-items:center;
padding:.8rem 1.2rem;
}
/* ---------- Footer ---------- */
.footer{border-top:1px solid var(--line);background:#0A0F16;padding:var(--space-6) 0;text-align:center;color:var(--muted)}
.count{font-size:.9rem;color:var(--muted);margin-left:.5rem}
/* ---------- Responsive ---------- */
@media (max-width: 1000px){
.hero-card{
grid-template-columns: 1fr;
padding-top: clamp(2rem, 6vw, 3rem);
}
.hero-photo{
position: static;
display: block;
margin: -3.5rem auto 0 auto;
transform: none;
width: 180px;
}
.skills .skill-grid{ grid-template-columns: repeat(2, 1fr); }
.project-list{ grid-template-columns: repeat(2, 1fr); }
@media (max-width:1000px){
.hero-card{grid-template-columns:1fr;padding-top:clamp(2rem,6vw,3rem)}
.hero-photo{position:static;display:block;margin:-3.0rem auto 0;transform:none;width:200px}
.skills .skill-grid{grid-template-columns:repeat(2,1fr)}
.project-list{grid-template-columns:repeat(2,1fr)}
}
@media (max-width: 640px){
.nav{ display: none; } /* optional: auf sehr klein ausblenden */
.container{ padding-inline: var(--space-5); }
.project-list, .skills .skill-grid{ grid-template-columns: 1fr; }
.contact .contact-form{ grid-template-columns: 1fr; }
@media (max-width:640px){
.nav{display:none}
.container{padding-inline:var(--space-5)}
.project-list,.skills .skill-grid{grid-template-columns:1fr}
.contact .contact-form{grid-template-columns:1fr}
.contact .contact-form > .field:nth-of-type(3){ grid-column:auto; }
.contact .contact-form .btn{ justify-self:stretch;width:100%; }
}

View File

@ -1,27 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Obai Albek | Portfolio</title>
<meta name="description" content="Portfolio von Obai Albeek - Informatik Student, Java Entwickler, Webentwicklung (HTML, CSS, JavaScript, PHP).">
<meta name="keywords" content="Obai Albeek, Portfolio, Informatik, Java, Webentwicklung, HTML, CSS, JavaScript, PHP, Projekte">
<meta name="author" content="Obai Albeek">
<link rel="icon" type="image/png" href="favicon.png">
<meta property="og:title" content="Obai Albeek | Portfolio">
<meta property="og:description" content="Schau dir meine Projekte in Java, Webentwicklung und Informatik an.">
<meta property="og:image" content="preview.png">
<meta property="og:url" content="https://deine-portfolio-url.com">
<meta property="og:type" content="website">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../css/style.css">
</head>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Obai Albek | Portfolio</title>
<body>
<meta name="description" content="Portfolio von Obai Albek Informatik-Student, Java-Entwickler und Webentwicklung (HTML, CSS, JavaScript, PHP)." />
<meta name="keywords" content="Obai Albek, Portfolio, Informatik, Java, Webentwicklung, HTML, CSS, JavaScript, PHP, Projekte" />
<meta name="author" content="Obai Albek" />
<meta property="og:title" content="Obai Albek | Portfolio" />
<meta property="og:description" content="Schau dir meine Projekte in Java, Webentwicklung und Informatik an." />
<meta property="og:image" content="assets/preview.png" />
<meta property="og:url" content="https://deine-portfolio-url.com" />
<meta property="og:type" content="website" />
<link rel="icon" type="image/png" href="assets/favicon.png" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="../css/style.css" />
<!-- Projekt-Filter (deine bestehende Datei) -->
<script src="../js/projektFilter.js" defer></script>
</head>
<body>
<!-- Sticky Topbar -->
<header class="topbar" role="banner">
<a class="brand" href="#home" aria-label="Start">
<span class="brand-mark">OA</span><span class="brand-text">Obai Albeek</span>
<span class="brand-mark">OA</span>
<span class="brand-text">Obai Albek</span>
</a>
<nav class="nav" aria-label="Hauptnavigation">
@ -32,49 +38,56 @@
</nav>
</header>
<!-- Hero Section (Card + Overlap Image) -->
<!-- Hero -->
<main id="home" class="hero" role="main">
<div class="hero-card" aria-labelledby="intro-title">
<div class="hero-media">
<!-- Bild-Link absichtlich leer lassen -->
<img class="hero-photo" src="../assets/myPhoto.jpeg" alt="Porträt von Obai Albeek">
<!-- eigenes Bild -->
<img class="hero-photo" src="../assets/myPhoto.jpeg" alt="Porträt von Obai Albek" />
</div>
<div class="hero-copy">
<p class="kicker">Hallo, ich bin</p>
<h1 id="intro-title" class="title">Obai Albek</h1>
<p class="subtitle">
Informatik-Student & Java-Entwickler · Web (HTML, CSS, JavaScript, PHP, JQuery, React)
Informatik-Student & Java-Entwickler · Web (HTML, CSS, JavaScript, PHP, jQuery, React)
</p>
<div class="actions">
<a class="btn primary" href="#projects">Mein Lebenslauf</a>
<a class="btn ghost" href="#contact">Mit mir zusammenarbeiten</a>
<a class="btn primary" href="../assets/Lebenslauf_Obai_Albek.pdf" target="_blank" rel="noopener" download>
Mein Lebenslauf
</a>
</div>
<!-- Social -->
<ul class="social" aria-label="Social Links">
<li><a class="iconlink" href="#" aria-label="E-Mail">@
</a></li>
<li><a class="iconlink" href="#" aria-label="GitHub">
<span aria-hidden="true">GH</span>
</a></li>
<li><a class="iconlink" href="#" aria-label="LinkedIn">
<span aria-hidden="true">in</span>
</a></li>
<li>
<a class="iconlink" href="https://github.com/ObaiAlbek" target="_blank" rel="noopener" aria-label="GitHub">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 .5a12 12 0 0 0-3.79 23.4c.6.11.82-.26.82-.58 0-.29-.01-1.06-.02-2.08-3.34.73-4.04-1.61-4.04-1.61-.55-1.38-1.34-1.75-1.34-1.75-1.09-.75.08-.74.08-.74 1.2.08 1.83 1.24 1.83 1.24 1.07 1.84 2.8 1.31 3.48 1 .11-.78.42-1.31.76-1.61-2.67-.3-5.48-1.34-5.48-5.98 0-1.32.47-2.39 1.24-3.24-.12-.3-.54-1.52.12-3.16 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.64.24 2.86.12 3.16.77.85 1.23 1.92 1.23 3.24 0 4.65-2.81 5.67-5.49 5.97.43.37.81 1.1.81 2.22 0 1.6-.02 2.88-.02 3.27 0 .32.21.7.83.58A12 12 0 0 0 12 .5Z"/>
</svg>
</a>
</li>
<li>
<a class="iconlink" href="https://www.linkedin.com/in/obai-albek-85365a357" target="_blank" rel="noopener" aria-label="LinkedIn">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M20.45 20.45h-3.55v-5.6c0-1.34-.02-3.06-1.87-3.06-1.87 0-2.16 1.46-2.16 2.96v5.7H9.32V9h3.41v1.56h.05c.47-.89 1.63-1.83 3.35-1.83 3.58 0 4.24 2.36 4.24 5.43v6.29ZM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12ZM3.57 20.45h3.55V9H3.57v11.45Z"/>
</svg>
</a>
</li>
</ul>
</div>
</div>
</main>
<!-- About Section -->
<!-- About -->
<section id="about" class="section about">
<div class="container">
<h2>Über mich</h2>
<p>
Ich studiere Informatik an der TH&nbsp;Mannheim und baue saubere,
wartbare Software. In Java fühle ich mich wie zu Hause
(OOP, Collections, Streams). Im Web arbeite ich gern full-stack
mit HTML/CSS/JS und PHP (+SQL).
Ich studiere Informatik an der TH Mannheim und baue saubere, wartbare Software.
In Java fühle ich mich zu Hause (OOP, Collections, Streams). Im Web arbeite ich
full-stack mit HTML/CSS/JS und PHP (+SQL).
</p>
<ul class="highlights">
<li>Fokus: Java-Backend & Web-Apps</li>
@ -84,14 +97,14 @@
</div>
</section>
<!-- Skills Section -->
<!-- Skills -->
<section id="skills" class="section skills" aria-labelledby="skills-title">
<div class="container">
<h2 id="skills-title">Skills</h2>
<div class="skill-grid">
<article class="skill">
<h3>Java</h3>
<p>OOP, Streams, Collections, JDBC/JPA, Testing</p>
<h3>Programmiersprachen</h3>
<p>Java, C/C++, Go, Haskell, Python</p>
</article>
<article class="skill">
<h3>Web</h3>
@ -109,58 +122,166 @@
</div>
</section>
<!-- Projects Section -->
<!-- Projekte -->
<section id="projects" class="section projects" aria-labelledby="projects-title">
<div class="container">
<h2 id="projects-title">Ausgewählte Projekte</h2>
<h2 id="projects-title">Projekte <span id="projectCount" class="count"></span></h2>
<div class="project-list">
<article class="project-card">
<div class="project-meta">
<h3>Bibliotheksverwaltung (Java)</h3>
<p class="meta">OOP · JDBC/JPA · MVC</p>
</div>
<p>Ausleihe/Retouren, Gebührenlogik, Admin-Zahlungen, Katalog.</p>
<div class="project-actions">
<a class="btn small" href="#" target="_blank" rel="noopener">Code</a>
<a class="btn small ghost" href="#" target="_blank" rel="noopener">Demo</a>
</div>
</article>
<article class="project-card">
<div class="project-meta">
<h3>Parking-Garage System (Java)</h3>
<p class="meta">CLI · Gebühren · Nummernschilder</p>
</div>
<p>Simulation von Ein/Ausfahrt, Zeitstempel, E-Auto-Rabatt.</p>
<div class="project-actions">
<a class="btn small" href="#" target="_blank" rel="noopener">Code</a>
<a class="btn small ghost" href="#" target="_blank" rel="noopener">Demo</a>
</div>
</article>
<article class="project-card">
<div class="project-meta">
<h3>Learning-Website (Full-Stack)</h3>
<p class="meta">HTML/CSS/JS · PHP · MySQL</p>
</div>
<p>Login, Kurse, Quiz, responsives Design, barrierearm.</p>
<div class="project-actions">
<a class="btn small" href="#" target="_blank" rel="noopener">Code</a>
<a class="btn small ghost" href="#" target="_blank" rel="noopener">Live</a>
</div>
</article>
<!-- Filter & Suche -->
<div class="project-controls" role="region" aria-label="Projektfilter">
<input id="projectSearch" class="project-search" type="search"
placeholder="Suche nach Titel/Technologie …" aria-label="Projekte durchsuchen" />
<div class="filter-chips" role="group" aria-label="Technologie-Filter">
<button class="chip is-active" data-filter="all">Alle</button>
<button class="chip" data-filter="Java">Java</button>
<button class="chip" data-filter="Web">Web</button>
<button class="chip" data-filter="Go">Go</button>
<button class="chip" data-filter="UI/UX">UI/UX</button>
</div>
</div>
<div class="project-list">
<!-- Local Chat -->
<article class="project-card" data-tags="Java,OOP,Maven,JUnit,GUI,Facade,Networking,Sockets,Domain">
<div class="project-meta">
<h3>Local Chat (Java, Maven, JUnit)</h3>
<p class="meta">GUI · Domain/Fassade · ChatServer (Sockets)</p>
</div>
<p>Lokaler Chat ohne DB: Benutzer/IDs, ChatRoom, Server/Client-Sockets, Unit-Tests, saubere Schichten (Domain, GUI, Fassade).</p>
<ul class="tags"><li>Java</li><li>Maven</li><li>JUnit</li><li>GUI</li><li>Fassade</li><li>Sockets</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/MyLocalchat" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- Bibliotheksverwaltung -->
<article class="project-card" data-tags="Java,OOP,JDBC,JPA,MVC,CLI,GUI">
<div class="project-meta">
<h3>Bibliotheksverwaltung (Java)</h3>
<p class="meta">OOP · JDBC/JPA · MVC</p>
</div>
<p>Ausleihe/Retouren, Gebühren, Admin-Zahlungen, Katalog.</p>
<ul class="tags"><li>Java</li><li>MVC</li><li>JPA</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/Bibliotheksverwaltungssystem" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- Parkhaus -->
<article class="project-card" data-tags="Java,MVC,Simulation">
<div class="project-meta">
<h3>Parkhaussimulator (Java)</h3>
<p class="meta">MVC · Gebühren · Nummernschilder</p>
</div>
<p>Ein/Ausfahrt, Zeitstempel, E-Auto-Rabatt, Abrechnung.</p>
<ul class="tags"><li>Java</li><li>Simulation</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/Parkhaus" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- E-Mail Client -->
<article class="project-card" data-tags="Java,GUI,MVC">
<div class="project-meta">
<h3>E-Mail Client (Java, GUI, MVC)</h3>
<p class="meta">GUI · MVC</p>
</div>
<p>GUI-basierte Mail-App mit sauberer Schichtentrennung.</p>
<ul class="tags"><li>Java</li><li>GUI</li><li>MVC</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/SimpleMailSystem" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- Flughafensimulation -->
<article class="project-card" data-tags="Go,Concurrency,Channels,Goroutines">
<div class="project-meta">
<h3>Flughafensimulation (Go)</h3>
<p class="meta">Goroutines · Channels</p>
</div>
<p>Modellierung von Abfertigung/Security/Gates mit Concurrency-Mustern.</p>
<ul class="tags"><li>Go</li><li>Concurrency</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/Flughafensimulation" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- Lernplattform -->
<article class="project-card" data-tags="Web,HTML,CSS,JavaScript,PHP,SQL,Auth">
<div class="project-meta">
<h3>Lernplattform (Full-Stack)</h3>
<p class="meta">HTML/CSS/JS · PHP · MySQL</p>
</div>
<p>Login, Kurse, Quiz, responsives Design.</p>
<ul class="tags"><li>Web</li><li>PHP</li><li>MySQL</li></ul>
<div class="project-actions">
<a class="btn small" href="#" target="_blank" rel="noopener">Code</a>
<a class="btn small ghost" href="https://github.com/ObaiAlbek/HTML-CSS_Lernwebseite" target="_blank" rel="noopener">Live</a>
</div>
</article>
<!-- UI/UX -->
<article class="project-card" data-tags="UI/UX,Figma,Design">
<div class="project-meta">
<h3>Urlaub-Transport-App (UI/UX)</h3>
<p class="meta">Figma · User Flows</p>
</div>
<p>App-Design für Transportbuchungen, Fokus auf Usability.</p>
<ul class="tags"><li>UI/UX</li><li>Figma</li></ul>
<div class="project-actions">
<a class="btn small ghost" href="https://github.com/ObaiAlbek/Urlaub-Transport-App" target="_blank" rel="noopener">Mockups</a>
</div>
</article>
<!-- Spiele -->
<article class="project-card" data-tags="Java,GUI,Games">
<div class="project-meta">
<h3>Spiele (Java GUI)</h3>
<p class="meta">Tic-Tac-Toe · Pong · Hitori</p>
</div>
<p>Kleine Spiele mit Java-GUI und sauberem OOP-Design.</p>
<ul class="tags"><li>Java</li><li>GUI</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/MyHitori-Spiel" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- React Auth -->
<article class="project-card" data-tags="Web,React,JavaScript,Auth">
<div class="project-meta">
<h3>React Auth Form</h3>
<p class="meta">React · useState · Conditional Rendering</p>
</div>
<p>Login/Signup-Formular mit Validation und CSS.</p>
<ul class="tags"><li>Web</li><li>React</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/reactAuthForm" target="_blank" rel="noopener">Code</a>
</div>
</article>
<!-- Parallel -->
<article class="project-card" data-tags="Java,Threads,Locks,Concurrency">
<div class="project-meta">
<h3>Parallelprogrammierung (Java)</h3>
<p class="meta">Dining Philosophers · ThreadsafeSimplifiedList&lt;T&gt;</p>
</div>
<p>Concurrency mit Threads/Locks und Hand-over-Hand-Locking.</p>
<ul class="tags"><li>Java</li><li>Concurrency</li></ul>
<div class="project-actions">
<a class="btn small" href="https://github.com/ObaiAlbek/concurrency-dining-philosophers" target="_blank" rel="noopener">Code</a>
</div>
</article>
</div>
</div>
</section>
<!-- Contact Section -->
<!-- Kontakt -->
<section id="contact" class="section contact" aria-labelledby="contact-title">
<div class="container">
<h2 id="contact-title">Kontakt</h2>
<p>Schreib mir deine Nachricht</p>
<form class="contact-form" action="#" method="post">
<form id="contactForm" class="contact-form" action="php/send.php" method="post" novalidate>
<div class="field">
<label for="name">Name</label>
<input id="name" name="name" type="text" autocomplete="name" required>
@ -173,20 +294,28 @@
<label for="msg">Nachricht</label>
<textarea id="msg" name="message" rows="5" required></textarea>
</div>
<button class="btn primary" type="submit">Nachricht senden</button>
<div id="formAlert" aria-live="polite" style="margin:.25rem 0 0;color:var(--muted)"></div>
<button class="btn primary" type="submit" id="sendBtn">Nachricht senden</button>
<!-- Honeypot (unsichtbar, am Ende → stört Layout nicht) -->
<input type="text" name="website" id="website" class="hp" tabindex="-1" autocomplete="off" aria-hidden="true"
style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden">
</form>
</div>
</section>
<footer class="footer" role="contentinfo">
<div class="container">
<p>© <span id="year"></span> Obai Albeek · Alle Rechte vorbehalten.</p>
<p>© <span id="year"></span> Obai Albek · Alle Rechte vorbehalten.</p>
</div>
<script>
// kleines JS: Jahr automatisch
document.getElementById('year').textContent = new Date().getFullYear();
</script>
<!-- Kontakt-Handler (Validation + Fetch) -->
<script src="../js/contact.js" defer></script>
</footer>
</body>
</html>
</html>

26
html/thanks.html 100644
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Danke!</title>
<link rel="stylesheet" href="../css/style.css" />
<style>
.center-wrap{min-height:60vh;display:grid;place-items:center;padding:2rem}
.thanks{
max-width:680px;margin:auto;background:var(--surface);border:1px solid var(--line);
border-radius:var(--radius);padding:2rem;box-shadow:var(--shadow);text-align:center;
}
.thanks h1{margin-top:0}
</style>
</head>
<body>
<div class="center-wrap">
<div class="thanks">
<h1>Danke für dein Feedback! 🙌</h1>
<p>Deine Nachricht wurde erfolgreich gesendet.</p>
<a href="myPortfolio.html" class="btn primary" style="display:inline-block;margin-top:1rem">Zur Startseite</a>
</div>
</div>
</body>
</html>

67
js/contact.js 100644
View File

@ -0,0 +1,67 @@
/* js/contact.js */
(() => {
const $ = s => document.querySelector(s);
const form = $('#contactForm');
const nameI = $('#name');
const mailI = $('#email');
const msgI = $('#msg');
const hp = $('#website'); // Honeypot
const alert = $('#formAlert');
const btn = $('#sendBtn');
if (!form) return;
const emailRx = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
const setAlert = (msg, good=false) => {
if (!alert) return;
alert.textContent = msg || '';
alert.style.color = good ? 'var(--accent)' : '#ff9aa2';
};
function validate() {
const errs = [];
if (!nameI.value.trim() || nameI.value.trim().length < 2) errs.push('Bitte gib einen gültigen Namen ein.');
if (!emailRx.test(mailI.value.trim())) errs.push('Bitte gib eine gültige E-Mail an.');
if (!msgI.value.trim() || msgI.value.trim().length < 10) errs.push('Nachricht bitte mit mindestens 10 Zeichen.');
if (hp.value) errs.push('Spam erkannt.');
return errs;
}
form.addEventListener('submit', async (e) => {
e.preventDefault();
setAlert('');
const errs = validate();
if (errs.length) {
setAlert(errs[0]);
return;
}
btn.disabled = true;
const orig = btn.textContent;
btn.textContent = 'Wird gesendet …';
try {
const fd = new FormData(form);
const res = await fetch(form.getAttribute('action') || 'php/send.php', {
method: 'POST',
body: fd,
headers: {'Accept': 'application/json'}
});
const data = await res.json().catch(() => ({}));
if (!res.ok || !data.ok) {
throw new Error(data.error || 'Senden fehlgeschlagen.');
}
// Erfolg → Danke-Seite
window.location.href = 'thanks.html';
} catch (err) {
setAlert(err.message || 'Es ist ein Fehler aufgetreten.');
btn.disabled = false;
btn.textContent = orig;
}
});
})();

View File

@ -0,0 +1,56 @@
/* projects.js Suche + Filter für Projekte */
(() => {
const $ = s => document.querySelector(s);
const $$ = s => Array.from(document.querySelectorAll(s));
const search = $('#projectSearch');
const chips = $$('.chip');
const cards = $$('.project-card');
let active = localStorage.getItem('proj.filter') || 'all';
const norm = s => (s || '').toLowerCase();
function apply() {
const term = norm(search?.value);
cards.forEach(card => {
const tags = norm(card.dataset.tags);
const title = norm(card.querySelector('h3')?.textContent);
const text = norm(card.textContent);
const inFilter = active === 'all' || tags.includes(norm(active));
const inSearch = !term || tags.includes(term) || title.includes(term) || text.includes(term);
card.hidden = !(inFilter && inSearch);
});
chips.forEach(c => c.classList.toggle('is-active', (c.dataset.filter || 'all') === active));
const counter = $('#projectCount');
if (counter) counter.textContent = cards.filter(c => !c.hidden).length;
}
function setFilter(f) {
active = f || 'all';
localStorage.setItem('proj.filter', active);
apply();
}
chips.forEach(ch => ch.addEventListener('click', () => setFilter(ch.dataset.filter)));
if (search) {
let t;
search.addEventListener('input', () => {
clearTimeout(t);
t = setTimeout(apply, 120);
});
search.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { search.value = ''; apply(); }
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setFilter(active));
} else {
setFilter(active);
}
})();

8
php/.env 100644
View File

@ -0,0 +1,8 @@
SMTP_HOST=smtp.deine-domain.de
SMTP_PORT=587
SMTP_USER=dein-smtp-user
SMTP_PASS=dein-smtp-passwort
MAIL_FROM=no-reply@deine-domain.de
MAIL_FROM_NAME="Portfolio Kontakt"
MAIL_TO=deinpostfach@deine-domain.de

86
php/send.php 100644
View File

@ -0,0 +1,86 @@
<?php
// php/send.php
declare(strict_types=1);
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require __DIR__ . '/../vendor/autoload.php';
header('Content-Type: application/json; charset=UTF-8');
try {
// ENV laden (SMTP-Daten außerhalb des Codes)
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->safeLoad();
// Nur POST zulassen
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['ok' => false, 'error' => 'Method not allowed']);
exit;
}
// Honeypot (Bot check)
if (!empty($_POST['website'])) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Spam detected']);
exit;
}
// Daten einsammeln & validieren
$name = trim((string)($_POST['name'] ?? ''));
$email = trim((string)($_POST['email'] ?? ''));
$message = trim((string)($_POST['message'] ?? ''));
if (mb_strlen($name) < 2) throw new Exception('Bitte einen gültigen Namen eingeben.');
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) throw new Exception('Bitte eine gültige E-Mail angeben.');
if (mb_strlen($message) < 10) throw new Exception('Nachricht ist zu kurz.');
// Mailer konfigurieren
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = $_ENV['SMTP_HOST'] ?? 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = $_ENV['SMTP_USER'] ?? 'user@example.com';
$mail->Password = $_ENV['SMTP_PASS'] ?? 'secret';
$mail->Port = (int)($_ENV['SMTP_PORT'] ?? 587);
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
// Absender & Empfänger
// Wichtig: setFrom sollte eine Domain nutzen, die zu deinem SMTP passt.
$fromEmail = $_ENV['MAIL_FROM'] ?? 'no-reply@deine-domain.de';
$fromName = $_ENV['MAIL_FROM_NAME'] ?? 'Portfolio Kontakt';
$toEmail = $_ENV['MAIL_TO'] ?? 'ich@deine-domain.de';
$mail->setFrom($fromEmail, $fromName);
$mail->addAddress($toEmail, 'Obai Albek');
$mail->addReplyTo($email, $name); // Antworten gehen an den Absender
// Inhalt
$mail->isHTML(true);
$mail->Subject = 'Neue Nachricht über das Kontaktformular';
$body = sprintf('<h2>Neue Nachricht</h2>
<p><strong>Name:</strong> %s</p>
<p><strong>E-Mail:</strong> %s</p>
<p><strong>Nachricht:</strong><br>%s</p>
<hr>
<small>IP: %s &middot; %s</small>',
htmlspecialchars($name, ENT_QUOTES, 'UTF-8'),
htmlspecialchars($email, ENT_QUOTES, 'UTF-8'),
nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8')),
$_SERVER['REMOTE_ADDR'] ?? 'n/a',
date('Y-m-d H:i:s')
);
$mail->Body = $body;
$mail->AltBody = "Neue Nachricht\n\nName: $name\nE-Mail: $email\n\n$message";
// Senden
$mail->send();
echo json_encode(['ok' => true]);
} catch (Exception $e) {
http_response_code(422);
echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
}