Team-Avatare: echte Gesichts-Ausschnitte statt gequetschter Karten

Die runden Avatare zeigten bisher die grossen 1200x1200-Team-Karten mit
Rahmen, Goldschrift, Husky und Hase - bei 96 Pixeln nur Matsch.

- Neue Dateien avatar-*.jpg: sauber zugeschnittene Gesichts-Portraits
  (400x400) aus den Karten, jedes Gesicht mittig und erkennbar
- data-modis.js bekommt ein eigenes Feld "avatar"; die grosse Karte
  bleibt unter "photo" fuer grosse Darstellungen erhalten
- Avatar groesser (108px), mit Akzent-Ring, Glanz und Hover-Anhebung
- Ohne Foto jetzt nur noch EIN Buchstabe statt zwei (kein "FU" mehr)
- Profilkarten mit Lichtschein hinter dem Avatar und Hover-Effekt

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-07-31 03:19:26 +02:00
co-authored by Claude Opus 5
parent 7b4e1352ca
commit 3339deeef5
11 changed files with 70 additions and 22 deletions
+62 -18
View File
@@ -297,39 +297,83 @@ header.site-header {
/* ---------- Profil-Karten (Team/Creator) ---------- */
.profile-card {
position: relative;
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
text-align: center;
padding: 1.6rem 1.2rem;
padding: 2rem 1.2rem 1.7rem;
transition: border-color .3s ease, transform .3s ease, box-shadow .3s ease;
}
/* dezenter Lichtschein hinter dem Avatar */
.profile-card::before {
content: "";
position: absolute;
top: -30%;
left: 50%;
width: 200px;
height: 200px;
transform: translateX(-50%);
background: radial-gradient(circle, color-mix(in srgb, var(--accent) 20%, transparent), transparent 70%);
pointer-events: none;
}
.profile-card:hover {
border-color: color-mix(in srgb, var(--accent) 55%, transparent);
transform: translateY(-4px);
box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
}
.profile-card h3 { margin: 0 0 .25rem; font-size: 1.15rem; }
/* Avatar-Kreis mit Akzent-Ring.
WICHTIG: hier kommen fertig zugeschnittene Gesichts-Ausschnitte rein
(assets/img/avatar-*.jpg), NICHT die großen Team-Karten — die sind bei
dieser Größe nur Matsch. Siehe `avatar`-Feld in assets/js/data-modis.js. */
.avatar {
width: 96px;
height: 96px;
width: 108px;
height: 108px;
border-radius: 50%;
margin: 0 auto .9rem;
background: linear-gradient(135deg, var(--accent), var(--accent-2));
margin: 0 auto 1rem;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: linear-gradient(140deg, var(--accent), var(--accent-2));
box-shadow:
0 0 0 2px var(--card-bg),
0 0 0 4px color-mix(in srgb, var(--accent) 55%, transparent),
0 10px 26px rgba(0, 0, 0, .45);
transition: box-shadow .3s ease, transform .3s ease;
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Leichter Glanz oben, damit der Kreis nicht flach wirkt */
.avatar::after {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
background: linear-gradient(165deg, rgba(255, 255, 255, .18), transparent 55%);
pointer-events: none;
}
.avatar-initial {
font-family: var(--font-head);
font-weight: 800;
font-size: 1.6rem;
font-size: 2.4rem;
line-height: 1;
color: var(--accent-contrast);
overflow: hidden;
position: relative;
text-shadow: 0 2px 6px rgba(0, 0, 0, .25);
}
/* Team-Karten sind quadratische Motive mit viel Rahmen — der Avatar zoomt
deshalb auf den Gesichtsbereich (ca. 50% / 33% des Motivs), damit das
Gesicht im 96px-Kreis wirklich erkennbar bleibt. */
.avatar img {
position: absolute;
width: 190%;
height: 190%;
left: -45%;
top: -13%;
object-fit: cover;
.profile-card:hover .avatar {
transform: translateY(-2px);
box-shadow:
0 0 0 2px var(--card-bg),
0 0 0 4px var(--accent),
0 14px 32px rgba(0, 0, 0, .5);
}
.profile-card .role { color: var(--accent); font-weight: 600; font-size: .85rem; margin-bottom: .6rem; }
.profile-card .quote { font-style: italic; font-size: .9rem; }
Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+4
View File
@@ -23,6 +23,7 @@ const MODIS = [
role: "Rechte Hand von Dogfather · Musik-Produzentin (Van-Van, Spotify)",
rank: 1,
photo: "assets/img/card-vanvan.jpg",
avatar: "assets/img/avatar-vanvan.jpg",
quote: "", // TODO: Zitat von VanVan einholen
social: {},
page: "vanvan.html",
@@ -33,6 +34,7 @@ const MODIS = [
role: "Organisiert & plant gemeinsam mit VanVan",
rank: 2,
photo: "assets/img/card-diene.jpg",
avatar: "assets/img/avatar-diene.jpg",
quote: "",
social: {},
page: "",
@@ -53,6 +55,7 @@ const MODIS = [
role: "Modi & Cutterin",
rank: 4,
photo: "assets/img/card-miss.jpg",
avatar: "assets/img/avatar-miss.jpg",
quote: "",
social: {},
page: "",
@@ -63,6 +66,7 @@ const MODIS = [
role: "Kommentare auf den Clip-Accounts · alias FlauschiRex",
rank: 5,
photo: "assets/img/card-marina.jpg",
avatar: "assets/img/avatar-marina.jpg",
quote: "",
social: {},
page: "",
+1 -1
View File
@@ -69,7 +69,7 @@ document.addEventListener("DOMContentLoaded", () => {
const items = filter === "alle" ? list : list.filter((c) => c.category === filter);
grid.innerHTML = items.map((c) => `
<div class="profile-card">
<div class="avatar">${c.photo ? `<img src="${c.photo}" alt="${c.name}">` : c.name.slice(0,2).toUpperCase()}</div>
<div class="avatar">${(c.avatar || c.photo) ? `<img src="${c.avatar || c.photo}" alt="${c.name}" loading="lazy">` : `<span class="avatar-initial">${c.name.charAt(0).toUpperCase()}</span>`}</div>
<h3>${c.name}</h3>
<div class="role">${c.role}</div>
${c.quote ? `<p class="quote">„${c.quote}"</p>` : ""}
+1 -1
View File
@@ -321,7 +321,7 @@ document.addEventListener("DOMContentLoaded", () => {
.map(
(p) => `
<div class="profile-card">
<div class="avatar">${p.photo ? `<img src="${p.photo}" alt="${p.name}">` : p.name.slice(0, 2).toUpperCase()}</div>
<div class="avatar">${(p.avatar || p.photo) ? `<img src="${p.avatar || p.photo}" alt="${p.name}" loading="lazy">` : `<span class="avatar-initial">${p.name.charAt(0).toUpperCase()}</span>`}</div>
<h3>${p.name}</h3>
<div class="role">${p.role}</div>
</div>`
+1 -1
View File
@@ -69,7 +69,7 @@ document.addEventListener("DOMContentLoaded", () => {
const rest = window.MODIS.filter((m) => m.name !== "VanVan").sort((a,b) => a.rank - b.rank);
grid.innerHTML = rest.map((m) => `
<div class="profile-card">
<div class="avatar">${m.photo ? `<img src="${m.photo}" alt="${m.name}">` : m.name.slice(0,2).toUpperCase()}</div>
<div class="avatar">${(m.avatar || m.photo) ? `<img src="${m.avatar || m.photo}" alt="${m.name}" loading="lazy">` : `<span class="avatar-initial">${m.name.charAt(0).toUpperCase()}</span>`}</div>
<span class="badge${m.rank === 1 ? " rank-1" : ""}" style="margin-bottom:.5rem;">Stufe ${m.rank}</span>
<h3>${m.name}</h3>
<div class="role">${m.role}</div>
+1 -1
View File
@@ -54,7 +54,7 @@ document.addEventListener("DOMContentLoaded", () => {
if (!grid || !window.SCOUTS) return;
grid.innerHTML = window.SCOUTS.map((s) => `
<div class="profile-card">
<div class="avatar">${s.photo ? `<img src="${s.photo}" alt="${s.name}">` : s.name.slice(0,2).toUpperCase()}</div>
<div class="avatar">${(s.avatar || s.photo) ? `<img src="${s.avatar || s.photo}" alt="${s.name}" loading="lazy">` : `<span class="avatar-initial">${s.name.charAt(0).toUpperCase()}</span>`}</div>
<h3>${s.name}</h3>
<div class="role">${s.role}</div>
${s.quote ? `<p class="quote">„${s.quote}"</p>` : `<p class="small muted">Zitat folgt</p>`}