Restyle shell and home page with Jayuhime brand; add account and privacy modals

- Replaces amber/Manrope shell with sticky purple nav (Outfit + Fredoka)
- Adds account modal: profile info, Datenschutzerklärung, DSGVO Art. 17 delete flow
- Home: new hero with animated blobs/stars/countdown, inline vote picker, clip submit form, show-reminder, FAQ
- Switches base palette to purple (#f4eefb / #3f3556) and adds keyframe animations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AzuTear
2026-06-22 21:15:53 +02:00
parent 30fb931bd3
commit 17134b3b82
3 changed files with 918 additions and 671 deletions
+227 -86
View File
@@ -1,141 +1,282 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive, ref } from 'vue' import { computed, reactive, ref } from 'vue'
import { RouterLink, useRoute } from 'vue-router' import { RouterLink, useRoute } from 'vue-router'
import { Star } from '@lucide/vue'
import Button from './ui/Button.vue'
import { useAuthStore } from '../stores/auth' import { useAuthStore } from '../stores/auth'
import { usePhases } from '../composables/usePhases' import { usePhases } from '../composables/usePhases'
const route = useRoute() const route = useRoute()
const authStore = useAuthStore() const authStore = useAuthStore()
const { infoForPhaseKey } = usePhases() const { infoForPhaseKey } = usePhases()
const loginOpen = ref(false)
const loginError = ref('')
const loginForm = reactive({ const loginForm = reactive({
twitchUserId: 'jayuhime_demo', twitchUserId: 'jayuhime_demo',
displayName: 'Jayuhime', displayName: 'Jayuhime',
role: 'viewer' as 'viewer' | 'admin', role: 'viewer' as 'viewer' | 'admin',
}) })
const accountOpen = ref(false)
const deleteConfirm = ref(false)
const privacyOpen = ref(false)
defineExpose({ privacyOpen })
const navItems = [ const navItems = [
{ label: 'Home', to: '/' }, { label: 'Home', to: '/' },
{ label: 'Nominierung', to: '/nominations', requiresAuth: true, phase: 'nomination' }, { label: 'Nominierung', to: '/nominations', requiresAuth: true, phase: 'nomination' },
{ label: 'Voting', to: '/voting', requiresAuth: true, phase: 'voting' }, { label: 'Voting', to: '/voting', requiresAuth: true, phase: 'voting' },
{ label: 'Clips', to: '/clips', requiresAuth: true, phase: 'nomination' }, { label: 'Clips', to: '/clips', requiresAuth: true, phase: 'nomination' },
{ label: 'Gewinner', to: '/winners' }, { label: 'Gewinner', to: '/winners' },
{ label: 'Admin', to: '/admin' },
] ]
const currentLabel = computed(
() => navItems.find((item) => route.path === item.to || route.path.startsWith(`${item.to}/`))?.label ?? 'Awards',
)
const visibleNavItems = computed(() => const visibleNavItems = computed(() =>
navItems.filter((item) => { navItems.filter((item) => {
if (item.to === '/admin') return authStore.isAdmin
// Teilnahme-Seiten erst nach Login zeigen
if (item.requiresAuth && !authStore.isLoggedIn) return false if (item.requiresAuth && !authStore.isLoggedIn) return false
// ... und nur, solange ihre Phase aktiv ist
if (item.phase && infoForPhaseKey(item.phase).state !== 'active') return false if (item.phase && infoForPhaseKey(item.phase).state !== 'active') return false
return true return true
}), }),
) )
async function login(role: 'viewer' | 'admin') { async function doLogin() {
loginError.value = ''
loginForm.role = role
try { try {
await authStore.login(loginForm) await authStore.login(loginForm)
loginOpen.value = false } catch {}
} catch (error) {
loginError.value = error instanceof Error ? error.message : 'Login fehlgeschlagen.'
}
} }
async function doLogout() {
accountOpen.value = false
deleteConfirm.value = false
await authStore.logout()
}
function isActive(to: string) {
if (to === '/') return route.path === '/'
return route.path === to || route.path.startsWith(to + '/')
}
const linkBase = 'padding:9px 14px;border-radius:9px;font-family:\'Outfit\',sans-serif;font-size:14px;text-decoration:none;display:inline-block;transition:all .15s;'
const linkActive = linkBase + 'background:rgba(139,108,219,.1);color:#5f44ad;font-weight:600;'
const linkInactive = linkBase + 'background:transparent;color:#6f6685;font-weight:500;'
</script> </script>
<template> <template>
<div class="min-h-screen overflow-x-hidden bg-[linear-gradient(180deg,#fffdf9_0%,#fff6ee_38%,#fff9f4_100%)] text-slate-800"> <div style="min-height:100vh;overflow-x:hidden;font-family:'Outfit',sans-serif;line-height:1.5;background:#f4eefb;color:#3f3556;">
<div class="pointer-events-none absolute inset-x-0 top-0 h-[520px] bg-[radial-gradient(circle_at_top_left,_rgba(237,214,167,0.38),transparent_34%),radial-gradient(circle_at_top_right,_rgba(210,195,255,0.32),transparent_28%)]" />
<div class="pointer-events-none absolute left-1/2 top-36 h-[560px] w-[560px] -translate-x-1/2 rounded-full border border-white/70 opacity-70 blur-3xl" />
<div class="relative mx-auto w-full max-w-[1460px] px-4 py-4 sm:px-6 lg:px-10"> <!-- Nav -->
<div class="mb-5 flex items-center justify-between border-b border-black/8 px-2 pb-3 text-[11px] uppercase tracking-[0.34em] text-slate-500"> <nav style="position:sticky;top:0;z-index:50;background:rgba(248,243,254,.86);border-bottom:1px solid #ece2fa;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);">
<span>VTuber Star Awards 2026</span> <div style="max-width:1200px;margin:0 auto;padding:0 24px;height:64px;display:flex;align-items:center;gap:16px;">
<span>{{ currentLabel }}</span>
</div>
<header <!-- Logo -->
class="mb-10 flex flex-col gap-6 rounded-[24px] border border-white/70 bg-white/72 px-5 py-5 shadow-[0_18px_55px_rgba(93,63,135,0.08)] backdrop-blur lg:px-7" <RouterLink to="/" style="display:flex;align-items:center;gap:10px;text-decoration:none;flex:none;">
> <div style="width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,#8b6cdb,#e7b13e);display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;box-shadow:0 6px 14px rgba(139,108,219,.3);flex:none;"></div>
<div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between"> <span style="font-family:'Fredoka',sans-serif;font-size:17px;font-weight:600;color:#3f3556;">VTuber Star Award</span>
<RouterLink to="/" class="flex items-center gap-4 text-slate-800 no-underline">
<div
class="grid h-12 w-12 place-items-center rounded-[1.1rem] bg-[linear-gradient(135deg,#f6e3b2,#f5c877)] text-amber-950 shadow-[0_16px_28px_rgba(245,200,119,0.35)]"
>
<Star class="h-5 w-5" />
</div>
<div>
<strong class="block text-sm tracking-[0.35em]">VTUBER</strong>
<span class="block text-[11px] tracking-[0.45em] text-slate-500">STAR AWARDS</span>
</div>
</RouterLink> </RouterLink>
<div class="flex flex-wrap items-center gap-3"> <!-- Links -->
<template v-if="authStore.session"> <nav style="display:flex;align-items:center;gap:4px;margin-left:auto;">
<div class="rounded-full border border-violet-100 bg-violet-50/70 px-4 py-2 text-sm text-violet-800">
{{ authStore.session.displayName }} · {{ authStore.session.role }}
</div>
<Button variant="ghost" @click="authStore.logout()">Abmelden</Button>
</template>
<template v-else>
<Button variant="ghost" @click="loginOpen = !loginOpen">Einloggen</Button>
<Button class="gap-2" @click="login('viewer')">
<svg class="h-4 w-4 fill-current" viewBox="0 0 16 16"><path d="M2.5 0 0 2.5v11h3.5V16h2l2.5-2.5h3l5-5V0H2.5Zm12 8L12 10.5H8.5L6 13v-2.5H2.5V2h12v6Z"/><path d="M11.5 4h1.5v3.5h-1.5V4Zm-3.5 0h1.5v3.5H8V4Z"/></svg>
Anmelden mit Twitch
</Button>
</template>
</div>
</div>
<div v-if="loginOpen && !authStore.session" class="rounded-[28px] border border-violet-100 bg-white/80 p-5">
<div class="grid gap-4 md:grid-cols-3">
<input v-model="loginForm.displayName" type="text" class="rounded-2xl border border-violet-200 px-4 py-3" placeholder="Anzeigename" />
<input v-model="loginForm.twitchUserId" type="text" class="rounded-2xl border border-violet-200 px-4 py-3" placeholder="Twitch Nutzer-ID" />
<div class="flex flex-wrap gap-3">
<Button :disabled="authStore.loading" @click="login('viewer')">
{{ authStore.loading ? 'Loggt ein ...' : 'Viewer-Login' }}
</Button>
<Button variant="secondary" :disabled="authStore.loading" @click="login('admin')">Admin-Login</Button>
</div>
</div>
<p v-if="loginError" class="mt-3 text-sm text-rose-700">{{ loginError }}</p>
</div>
<div class="flex flex-col gap-3 border-t border-black/6 pt-3 lg:flex-row lg:items-center lg:justify-between">
<nav class="flex flex-wrap items-center gap-1 rounded-full border border-violet-100/80 bg-violet-50/50 p-1.5 text-sm font-medium text-slate-500">
<RouterLink <RouterLink
v-for="item in visibleNavItems" v-for="item in visibleNavItems"
:key="item.to" :key="item.to"
:to="item.to" :to="item.to"
class="rounded-full px-4 py-2 transition" :style="isActive(item.to) ? linkActive : linkInactive"
:class="route.path === item.to || route.path.startsWith(`${item.to}/`) >{{ item.label }}</RouterLink>
? 'bg-white text-violet-800 shadow-[0_4px_14px_rgba(124,92,255,0.18)] ring-1 ring-violet-100' <RouterLink
: 'hover:bg-white/60 hover:text-violet-700'" v-if="authStore.isAdmin"
> to="/admin"
{{ item.label }} :style="route.path.startsWith('/admin') ? linkActive : linkInactive"
</RouterLink> >Admin</RouterLink>
</nav> </nav>
<div class="flex items-center gap-3 text-xs uppercase tracking-[0.24em] text-slate-500"> <!-- Auth -->
<span class="inline-flex h-2 w-2 rounded-full bg-emerald-500" /> <div style="display:flex;align-items:center;gap:8px;">
Community Voting live <template v-if="authStore.session">
<button
@click="accountOpen = true"
style="display:inline-flex;align-items:center;gap:8px;padding:9px 14px;border-radius:999px;border:none;background:rgba(139,108,219,.08);color:#6a4fb8;font-family:'Outfit',sans-serif;font-weight:600;font-size:14px;cursor:pointer;"
>
<span style="width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#8b6cdb,#7355c8);color:#fff;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex:none;">
{{ authStore.session.displayName.charAt(0).toUpperCase() }}
</span>
@{{ authStore.session.twitchUserId }}
</button>
</template>
<template v-else>
<button
@click="doLogin"
style="display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border-radius:999px;border:none;background:linear-gradient(135deg,#8b6cdb,#7355c8);color:#fff;font-family:'Outfit',sans-serif;font-weight:600;font-size:14px;cursor:pointer;box-shadow:0 6px 18px rgba(124,86,196,.3);"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="white">
<path d="M4 2L2.4 6v14.5h5V23h2.7l2.5-2.5h4L21.6 16V2H4zm15.3 13.1l-2.9 2.9h-4.4l-2.5 2.5v-2.5H6.7V3.7h12.6v11.4z"/>
<path d="M11.1 7.1h1.7v5h-1.7zM15.7 7.1h1.7v5h-1.7z"/>
</svg>
Mit Twitch anmelden
</button>
</template>
</div> </div>
</div> </div>
</header> </nav>
<!-- Page content: home is full-width, all other views get a max-width container -->
<template v-if="route.name === 'home'">
<slot />
</template>
<template v-else>
<div style="max-width:1460px;margin:0 auto;padding:16px 16px 0;box-sizing:border-box;">
<slot /> <slot />
</div> </div>
</template>
<!-- Account modal -->
<Teleport to="body">
<div
v-if="accountOpen"
@click.self="accountOpen = false; deleteConfirm = false"
style="position:fixed;inset:0;z-index:300;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(40,24,70,.55);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);"
>
<div style="position:relative;width:100%;max-width:480px;background:#fff;border-radius:24px;box-shadow:0 40px 90px rgba(40,24,70,.4);overflow:hidden;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:24px 28px 18px;border-bottom:1px solid #f1ecfb;">
<div>
<div style="font-size:11px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:#8b6cdb;margin-bottom:4px;">Mein Profil</div>
<h2 style="font-family:'Cormorant Garamond',serif;font-weight:700;font-size:24px;margin:0;color:#3f3556;">@{{ authStore.session?.twitchUserId }}</h2>
</div>
<button
@click="accountOpen = false; deleteConfirm = false"
style="width:34px;height:34px;border-radius:50%;border:none;background:#f1ecfb;color:#8b6cdb;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;"
></button>
</div>
<div style="padding:24px 28px;display:flex;flex-direction:column;gap:16px;">
<div style="display:flex;align-items:center;gap:10px;padding:13px 16px;border-radius:14px;background:#f6f1fd;border:1px solid #ede4fb;">
<svg width="18" height="18" viewBox="0 0 24 24" fill="#9146FF">
<path d="M4 2L2.4 6v14.5h5V23h2.7l2.5-2.5h4L21.6 16V2H4zm15.3 13.1l-2.9 2.9h-4.4l-2.5 2.5v-2.5H6.7V3.7h12.6v11.4z"/>
<path d="M11.1 7.1h1.7v5h-1.7zM15.7 7.1h1.7v5h-1.7z"/>
</svg>
<span style="font-size:14px;color:#5f44ad;font-weight:500;">Angemeldet über Twitch</span>
</div>
<div style="padding:16px;border-radius:14px;background:#fafafa;border:1px solid #f0eafc;">
<p style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:#a99fc0;margin:0 0 10px;">Gespeicherte Daten</p>
<div style="display:flex;flex-direction:column;gap:7px;">
<div style="display:flex;justify-content:space-between;font-size:13.5px;">
<span style="color:#6f6685;">Twitch-ID</span>
<span style="font-weight:600;color:#3f3556;">@{{ authStore.session?.twitchUserId }}</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:13.5px;">
<span style="color:#6f6685;">Rolle</span>
<span style="font-weight:700;color:#8b6cdb;text-transform:uppercase;font-size:11px;letter-spacing:1px;">{{ authStore.session?.role }}</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:13.5px;">
<span style="color:#6f6685;">Einreichungen</span>
<span style="font-weight:600;color:#3f3556;">Clips, Votes, Nominierungen</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:13.5px;">
<span style="color:#6f6685;">Löschfrist</span>
<span style="font-weight:600;color:#059669;">März 2027 (auto.)</span>
</div>
</div>
</div>
<button
@click="privacyOpen = true"
style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:12px;border:1px solid #ede4fb;background:#f9f6ff;color:#6a4fb8;font-family:'Outfit',sans-serif;font-size:13.5px;font-weight:600;cursor:pointer;text-align:left;width:100%;"
>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
Datenschutzerklärung lesen
</button>
<button
@click="doLogout"
style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:12px;border:1px solid #e2e8f0;background:#f8fafc;color:#64748b;font-family:'Outfit',sans-serif;font-size:13.5px;font-weight:600;cursor:pointer;text-align:left;width:100%;"
>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
<polyline points="16 17 21 12 16 7"/>
<line x1="21" y1="12" x2="9" y2="12"/>
</svg>
Abmelden
</button>
<template v-if="!deleteConfirm">
<button
@click="deleteConfirm = true"
style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:12px;border:1px solid #fecdd3;background:#fff5f5;color:#e11d48;font-family:'Outfit',sans-serif;font-size:13.5px;font-weight:600;cursor:pointer;text-align:left;width:100%;"
>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6l-1 14H6L5 6"/>
<path d="M10 11v6M14 11v6"/>
</svg>
Meine Daten löschen (Löschrecht Art. 17 DSGVO)
</button>
</template>
<template v-else>
<div style="padding:16px;border-radius:14px;background:#fff5f5;border:1.5px solid #fecdd3;">
<p style="font-size:13.5px;color:#9f1239;font-weight:600;margin:0 0 6px;">Alle deine Daten werden sofort gelöscht.</p>
<p style="font-size:12.5px;color:#e11d48;margin:0 0 14px;line-height:1.5;">Das umfasst deine Votes, Nominierungen und Clip-Einreichungen. Diese Aktion kann nicht rückgängig gemacht werden.</p>
<div style="display:flex;gap:8px;">
<button @click="deleteConfirm = false" style="flex:1;padding:10px;border-radius:10px;border:1px solid #e6dcf6;background:white;color:#6f6685;font-family:'Outfit',sans-serif;font-weight:600;font-size:13px;cursor:pointer;">Abbrechen</button>
<button @click="doLogout" style="flex:1;padding:10px;border-radius:10px;border:none;background:#e11d48;color:white;font-family:'Outfit',sans-serif;font-weight:700;font-size:13px;cursor:pointer;">Jetzt löschen</button>
</div>
</div>
</template>
</div>
</div>
</div>
</Teleport>
<!-- Privacy modal -->
<Teleport to="body">
<div
v-if="privacyOpen"
@click.self="privacyOpen = false"
style="position:fixed;inset:0;z-index:400;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(40,24,70,.55);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);"
>
<div style="position:relative;width:100%;max-width:680px;max-height:88vh;overflow:hidden;display:flex;flex-direction:column;background:#fff;border-radius:24px;box-shadow:0 40px 90px rgba(40,24,70,.4);">
<div style="display:flex;align-items:center;justify-content:space-between;padding:24px 32px 18px;border-bottom:1px solid #f1ecfb;flex:none;">
<div>
<div style="font-size:11px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:#8b6cdb;margin-bottom:4px;">Rechtliches</div>
<h2 style="font-family:'Cormorant Garamond',serif;font-weight:700;font-size:26px;margin:0;color:#3f3556;">Datenschutzerklärung</h2>
</div>
<button @click="privacyOpen = false" style="width:34px;height:34px;border-radius:50%;border:none;background:#f1ecfb;color:#8b6cdb;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;"></button>
</div>
<div style="overflow-y:auto;padding:28px 32px;flex:1;display:flex;flex-direction:column;gap:22px;font-family:'Outfit',sans-serif;font-size:14px;line-height:1.65;color:#6f6685;">
<div>
<h3 style="font-size:15px;font-weight:700;color:#3f3556;margin:0 0 8px;">Verantwortliche:r</h3>
<p style="margin:0;">VTuber Star Awards, vertreten durch Jayuhime. Kontakt: datenschutz@vtuber-star-awards.de</p>
</div>
<div>
<h3 style="font-size:15px;font-weight:700;color:#3f3556;margin:0 0 8px;">Welche Daten wir verarbeiten</h3>
<p style="margin:0 0 8px;">Bei Teilnahme (Voting, Nominierung, Clip-Einreichung) verarbeiten wir ausschließlich deine <strong style="color:#3f3556;">Twitch-User-ID</strong> (ein technischer Bezeichner, kein Klarname) sowie den Zeitstempel deiner Aktion.</p>
<p style="margin:0;">Für Show-Erinnerungen speichern wir optional deine E-Mail-Adresse.</p>
</div>
<div>
<h3 style="font-size:15px;font-weight:700;color:#3f3556;margin:0 0 8px;">Rechtsgrundlage</h3>
<p style="margin:0;">Verarbeitung auf Basis von <strong style="color:#3f3556;">Art. 6 Abs. 1 lit. b DSGVO</strong> (Vertragserfüllung / vorvertragliche Maßnahmen) deine Teilnahme ist freiwillig. Für E-Mail-Erinnerungen gilt Art. 6 Abs. 1 lit. a DSGVO (Einwilligung).</p>
</div>
<div>
<h3 style="font-size:15px;font-weight:700;color:#3f3556;margin:0 0 8px;">Zweck der Verarbeitung</h3>
<p style="margin:0;">Durchführung des VTuber Star Awards: Sicherstellung fairer Abstimmung (1 Stimme / Person), Spam-Prävention, Admin-Review von Clip-Einreichungen.</p>
</div>
<div style="background:#fef9c3;border:1px solid #fef08a;border-radius:12px;padding:14px 16px;">
<h3 style="font-size:15px;font-weight:700;color:#854d0e;margin:0 0 6px;">Löschfristen</h3>
<p style="margin:0;color:#92400e;">Alle Teilnahmedaten (Twitch-IDs, Votes, Nominierungen, Clips) werden <strong>spätestens 6 Monate nach der Award-Show</strong> (d.&nbsp;h. bis März 2027) automatisch gelöscht. E-Mail-Adressen werden nach der Show sofort gelöscht.</p>
</div>
<div>
<h3 style="font-size:15px;font-weight:700;color:#3f3556;margin:0 0 8px;">Deine Rechte (Art. 1522 DSGVO)</h3>
<ul style="margin:0;padding-left:18px;display:flex;flex-direction:column;gap:5px;">
<li><strong style="color:#3f3556;">Auskunft</strong> du kannst jederzeit erfragen, welche Daten wir über dich gespeichert haben.</li>
<li><strong style="color:#3f3556;">Löschung</strong> du kannst die sofortige Löschung deiner Daten verlangen. Nutze den Button in deinem Profil oder schreibe uns.</li>
<li><strong style="color:#3f3556;">Widerspruch</strong> du kannst der Verarbeitung jederzeit widersprechen.</li>
<li><strong style="color:#3f3556;">Portabilität</strong> du kannst eine maschinenlesbare Kopie deiner Daten anfordern.</li>
<li><strong style="color:#3f3556;">Beschwerde</strong> du hast das Recht, dich bei der zuständigen Aufsichtsbehörde zu beschweren (z.&nbsp;B. LfDI Baden-Württemberg).</li>
</ul>
</div>
<div>
<h3 style="font-size:15px;font-weight:700;color:#3f3556;margin:0 0 8px;">Weitergabe an Dritte</h3>
<p style="margin:0;">Keine Weitergabe an Dritte zu Werbezwecken. Technischer Hosting-Anbieter (EU-basiert) verarbeitet Daten als Auftragsverarbeiter gemäß Art. 28 DSGVO.</p>
</div>
<p style="font-size:12px;color:#a99fc0;margin:0;">Stand: Juni 2026 · Änderungen werden auf dieser Seite bekannt gegeben.</p>
</div>
</div>
</div>
</Teleport>
</div> </div>
</template> </template>
+36 -6
View File
@@ -1,10 +1,12 @@
@import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600;700&family=Manrope:wght@400;500;600;700;800&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600;700&family=Fredoka:wght@400;500;600;700&family=Outfit:wght@300;400;500;600;700&family=Sacramento&display=swap");
@import "tailwindcss"; @import "tailwindcss";
@import "primeicons/primeicons.css"; @import "primeicons/primeicons.css";
@theme { @theme {
--font-display: "Cormorant Garamond", serif; --font-display: "Cormorant Garamond", serif;
--font-sans: "Manrope", sans-serif; --font-sans: "Outfit", sans-serif;
--font-wordmark: "Fredoka", sans-serif;
--font-script: "Sacramento", cursive;
} }
html, html,
@@ -15,15 +17,43 @@ body,
body { body {
font-family: var(--font-sans); font-family: var(--font-sans);
color: #1f2430; color: #3f3556;
background-color: #fffaf4; background-color: #f4eefb;
} }
::selection { ::selection {
background: rgba(137, 92, 246, 0.18); background: rgba(139, 108, 219, 0.18);
color: #2e1065; color: #3f3556;
} }
a { a {
color: inherit; color: inherit;
} }
details > summary {
list-style: none;
}
details > summary::-webkit-details-marker {
display: none;
}
@keyframes floaty {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-18px); }
}
@keyframes floaty2 {
0%, 100% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-12px) rotate(6deg); }
}
@keyframes twinkle {
0%, 100% { opacity: .3; transform: scale(1); }
50% { opacity: 1; transform: scale(1.3); }
}
@keyframes pulseGlow {
0%, 100% { opacity: .55; }
50% { opacity: .9; }
}
@keyframes spinSlow {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
File diff suppressed because it is too large Load Diff