Member-Bereich: Profilseite, Rollen-Selfservice, Web-Voting, Member-Gate

Der Discord-Login ist jetzt für alle Member nützlich:
- /profil: Rang + XP-Fortschritt, Discord-Rollen als Chips, Playtester-
  Badge, eigener Alpha-Key mit Kopier-Button
- Rollen-Selfservice: veröffentlichte Rollen-Menüs direkt im Browser
  togglen (gleiche Exklusiv-Logik wie die Discord-Buttons; nur Rollen
  aus aktiven Menüs erlaubt)
- Wunsch-Voting im Web: auf /roadmap Wünsche einreichen (postet wie
  /wunsch in den Voting-Kanal) und upvoten (wish_votes-Tabelle,
  ein Vote pro Member, unabhängig von Discord-👍)
- Member-Gate: Login nur für Mitglieder der konfigurierten Guild;
  Abgewiesene sehen einen Banner mit Discord-Invite-Link (neue
  Settings member_gate_enabled + discord_invite_url im System-Tab)
- Membership-Check mit 5-Minuten-Cache, Owner immer erlaubt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 21:52:59 +02:00
co-authored by Claude Fable 5
parent 3bfa559f2e
commit aa12dff7ab
11 changed files with 551 additions and 15 deletions
+52
View File
@@ -1113,3 +1113,55 @@ a.sha:hover { border-color: var(--neon); background: rgba(245, 197, 24, .07); }
.gamesel-item:hover, .gamesel-item.active { background: var(--bg3); color: var(--neon); }
.gamesel-id { color: var(--muted2); font-family: var(--mono); font-size: .75rem; }
.gamesel-item:hover .gamesel-id { color: var(--muted); }
/* ── Member-Bereich: Gate-Banner, Profil, Web-Voting ── */
.gate-banner {
display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
margin: 1.2rem auto 0; max-width: 900px; padding: .8rem 1.1rem;
background: rgba(255, 77, 0, 0.08); border: 1px solid rgba(255, 77, 0, 0.4);
border-radius: 10px; font-family: var(--body);
}
.gate-banner span { flex: 1; min-width: 240px; }
.profile-card {
display: flex; gap: 1.4rem; align-items: flex-start;
background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
padding: 1.4rem; margin-bottom: 2rem;
}
.profile-avatar { width: 84px; height: 84px; border-radius: 50%; border: 2px solid var(--neon); }
.profile-name { font-family: var(--display); font-size: 2rem; letter-spacing: .05em; margin: 0; }
.profile-meta { color: var(--muted); margin: .2rem 0 0; font-family: var(--body); }
.profile-badge {
margin-left: .8rem; padding: .1rem .55rem; border-radius: 999px;
border: 1px solid var(--success); color: var(--success); font-size: .85rem;
}
.profile-roles { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .8rem; }
.role-chip {
padding: .15rem .6rem; border-radius: 999px; font-size: .85rem;
border: 1px solid var(--border); color: var(--text); font-family: var(--body);
}
.xp-bar { height: 12px; border-radius: 999px; background: var(--bg3); border: 1px solid var(--border); overflow: hidden; }
.xp-fill { height: 100%; background: linear-gradient(90deg, var(--neon), var(--neon2)); }
.alpha-key {
font-family: var(--mono); background: var(--bg3); border: 1px solid var(--border);
border-radius: 8px; padding: .5rem .8rem; letter-spacing: .08em;
}
.rolemenu-box { margin-bottom: 1.4rem; }
.rolemenu-title { font-family: var(--body); font-weight: 600; font-size: 1.1rem; margin: 0 0 .2rem; }
.rolemenu-buttons { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .6rem; }
.rolemenu-btn.active { border-color: var(--neon); color: var(--neon); }
.wish-vote {
background: var(--bg3); border: 1px solid var(--border); border-radius: 999px;
color: var(--muted); font-family: var(--mono); padding: .25rem .7rem; cursor: pointer;
white-space: nowrap; transition: border-color .15s, color .15s;
}
.wish-vote:hover { border-color: var(--neon); color: var(--text); }
.wish-vote.voted { border-color: var(--neon); color: var(--neon); }
.wish-form { display: flex; gap: .6rem; margin-top: 1rem; }
.wish-form input { flex: 1; }
@media (max-width: 640px) {
.profile-card { flex-direction: column; align-items: center; text-align: center; }
.profile-roles { justify-content: center; }
.wish-form { flex-direction: column; }
}