Hub (Web): Auth-aware CTAs, Kontakt-Modal statt Mailto, Pruefung-Wording

- Angemeldete Nutzer sehen ueberall (Header, Hero, Kuendbar-Box, CTA,
  Footer) "Zum pressekonto" (rollengerecht via LoginRedirect::homeFor)
  statt Anmelden/Konto erstellen
- Keine E-Mail-Adresse mehr im Quelltext (Spam-Schutz): neues
  <x-web.hub.contact-modal> (Alpine, Honeypots, Erfolgs-/Fehlerzustand,
  auto-open nach Submit) am host-agnostischen kontakt.store-Endpunkt;
  Trigger in Enterprise-Streifen, FAQ-Support, DSGVO-Antwort, Footer
  und Utility-Bar
- "Redaktionelle Pruefung" -> "Pruefung" in allen Hub-Inhalten;
  "verifizierter Newsroom" -> "Ihr eigener Newsroom"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-03 09:52:29 +00:00
parent 88d6935014
commit 47d03ec777
6 changed files with 243 additions and 60 deletions

View file

@ -0,0 +1,99 @@
{{--
Kontakt-Modal der Hub-Landing: einfaches Formular statt sichtbarer
E-Mail-Adresse (Spam-Schutz). Öffnet auf das Window-Event
`open-contact-modal` (Trigger: `<button x-data @click="$dispatch('open-contact-modal')">`)
und automatisch nach Absenden (Erfolgs-Flash bzw. Validierungsfehler).
Versand über den host-agnostischen POST-Endpunkt des Kontaktformulars.
--}}
<div
x-data="{ open: @js(session()->has('contact-status') || $errors->any()) }"
@open-contact-modal.window="open = true"
@keydown.escape.window="open = false"
>
<div x-show="open" x-cloak class="fixed inset-0 z-50 flex items-center justify-center p-4">
<div class="absolute inset-0 bg-black/50" @click="open = false" aria-hidden="true"></div>
<div class="relative bg-white border border-bg-rule rounded-[4px] w-full max-w-[520px] max-h-[90vh] overflow-y-auto p-7"
role="dialog" aria-modal="true" aria-label="Kontaktanfrage">
<button type="button" @click="open = false" aria-label="Schließen"
class="absolute top-4 right-4 w-8 h-8 flex items-center justify-center text-ink-3 hover:text-ink transition-colors cursor-pointer">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
<path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" />
</svg>
</button>
@if (session('contact-status'))
<div class="section-eyebrow mb-4">Kontakt</div>
<h3 class="text-[20px] font-bold text-ink tracking-[-0.4px] m-0 mb-3">Nachricht gesendet</h3>
<p class="text-[13.5px] text-ink-2 leading-[1.6] m-0" role="status">
{{ session('contact-status') }}
</p>
<button type="button" @click="open = false"
class="mt-6 inline-flex items-center justify-center px-5 py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] transition-colors cursor-pointer">
Schließen
</button>
@else
<div class="section-eyebrow mb-4">Kontakt</div>
<h3 class="text-[20px] font-bold text-ink tracking-[-0.4px] m-0 mb-2">Schreiben Sie uns</h3>
<p class="text-[12.5px] text-ink-3 leading-[1.55] m-0 mb-6">
Ob Enterprise-Anfrage, Support oder Fragen zu Ihren Daten — wir melden uns werktags
schnellstmöglich zurück.
</p>
<form method="POST" action="{{ route('kontakt.store', ['edition' => 'de']) }}" class="space-y-4" novalidate>
@csrf
<div>
<label for="hub-contact-name" class="block text-[12px] font-semibold text-ink mb-1.5">Ihr Name</label>
<input id="hub-contact-name" type="text" name="name" value="{{ old('name') }}" required
autocomplete="name"
class="w-full border border-bg-rule px-3 py-2.5 text-[13px] bg-white text-ink focus:outline-none focus:border-hub" />
@error('name')
<p class="mt-1 text-[12px] text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label for="hub-contact-email" class="block text-[12px] font-semibold text-ink mb-1.5">E-Mail-Adresse</label>
<input id="hub-contact-email" type="email" name="email" value="{{ old('email') }}" required
autocomplete="email"
class="w-full border border-bg-rule px-3 py-2.5 text-[13px] bg-white text-ink focus:outline-none focus:border-hub" />
@error('email')
<p class="mt-1 text-[12px] text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label for="hub-contact-message" class="block text-[12px] font-semibold text-ink mb-1.5">Ihre Nachricht</label>
<textarea id="hub-contact-message" name="message" rows="5" required
class="w-full border border-bg-rule px-3 py-2.5 text-[13px] bg-white text-ink focus:outline-none focus:border-hub"
placeholder="Wie können wir Ihnen helfen?">{{ old('message') }}</textarea>
@error('message')
<p class="mt-1 text-[12px] text-red-600">{{ $message }}</p>
@enderror
</div>
{{-- Bot-Schutz: versteckte Honeypot-Felder (siehe ContactController). --}}
<div class="hidden" aria-hidden="true">
<label for="hub-contact-website">Website</label>
<input id="hub-contact-website" type="text" name="website" tabindex="-1" autocomplete="off" value="{{ old('website') }}" />
<label for="hub-contact-homepage">Homepage</label>
<input id="hub-contact-homepage" type="text" name="homepage" tabindex="-1" autocomplete="off" value="{{ old('homepage') }}" />
</div>
<div class="flex items-center justify-between gap-4 pt-1">
<p class="text-[11px] text-ink-3 leading-[1.5] m-0">
Ihre Angaben werden nur zur Bearbeitung der Anfrage verarbeitet
(<a href="{{ route('datenschutz', ['edition' => 'de']) }}" class="underline hover:text-ink">Datenschutz</a>).
</p>
<button type="submit"
class="inline-flex items-center justify-center px-5 py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] whitespace-nowrap transition-colors cursor-pointer">
Nachricht senden
</button>
</div>
</form>
@endif
</div>
</div>
</div>

View file

@ -8,7 +8,6 @@
$brandTagline = $brand['tagline_short'] ?? 'Publisher · Hub';
$brandTaglineLong = $brand['tagline_long'] ?? 'Der gemeinsame Publisher-Bereich für presseecho und businessportal24.';
$footerLegal = str_replace(':year', (string) now()->year, $brand['footer_legal'] ?? '© ' . now()->year . ' pressekonto');
$footerContactEmail = $brand['contact_email'] ?? 'info@pressekonto.com';
@endphp
<footer class="bg-hub-grad-2 text-ink-on-dark">
@ -39,10 +38,14 @@
<div>
<div class="eyebrow on-dark mb-3.5">Konto</div>
<ul class="space-y-2 text-[13px] text-white/75 list-none p-0 m-0">
<li><a href="{{ route('login') }}" class="hover:text-white transition-colors">Anmelden</a></li>
<li><a href="{{ route('register') }}" class="hover:text-white transition-colors">Konto erstellen</a></li>
@auth
<li><a href="{{ \App\Support\LoginRedirect::homeFor(auth()->user()) }}" class="hover:text-white transition-colors">Zum pressekonto</a></li>
@else
<li><a href="{{ route('login') }}" class="hover:text-white transition-colors">Anmelden</a></li>
<li><a href="{{ route('register') }}" class="hover:text-white transition-colors">Konto erstellen</a></li>
@endauth
<li><a href="#tarife" class="hover:text-white transition-colors">Tarife</a></li>
<li><a href="mailto:{{ $footerContactEmail }}" class="hover:text-white transition-colors">Kontakt aufnehmen</a></li>
<li><button type="button" x-data @click="$dispatch('open-contact-modal')" class="hover:text-white transition-colors cursor-pointer">Kontakt aufnehmen</button></li>
</ul>
</div>

View file

@ -42,18 +42,29 @@
</nav>
<div class="flex items-center gap-2">
<a href="{{ route('login') }}"
class="inline-flex items-center gap-2 px-4 py-2.5 text-[13px] font-semibold text-ink rounded-[4px] hover:bg-bg transition-colors">
Anmelden
</a>
<a href="{{ route('register') }}"
class="inline-flex items-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] transition-colors">
Konto erstellen
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M3 9L9 3M9 3H4M9 3v5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</a>
@auth
<a href="{{ \App\Support\LoginRedirect::homeFor(auth()->user()) }}"
class="inline-flex items-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] transition-colors">
Zum pressekonto
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M3 9L9 3M9 3H4M9 3v5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</a>
@else
<a href="{{ route('login') }}"
class="inline-flex items-center gap-2 px-4 py-2.5 text-[13px] font-semibold text-ink rounded-[4px] hover:bg-bg transition-colors">
Anmelden
</a>
<a href="{{ route('register') }}"
class="inline-flex items-center gap-2 px-[18px] py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] transition-colors">
Konto erstellen
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M3 9L9 3M9 3H4M9 3v5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</a>
@endauth
</div>
</div>
</header>

View file

@ -46,7 +46,7 @@
<span class="flex-1"></span>
<span class="flex items-center gap-4 py-2.5 whitespace-nowrap text-white/65">
<a href="mailto:{{ config('domains.domains.pressekonto.brand.contact_email') ?? 'info@pressekonto.com' }}" class="hover:text-white">Kontakt</a>
<button type="button" x-data @click="$dispatch('open-contact-modal')" class="hover:text-white cursor-pointer">Kontakt</button>
</span>
</div>

View file

@ -4,7 +4,6 @@
$brand = config('domains.domains.pressekonto.brand', []);
$from = request()->query('from');
$todayDate = now()->locale('de')->isoFormat('dddd, D. MMMM YYYY');
$hubContactEmail = $brand['contact_email'] ?? 'info@pressekonto.com';
$fmtEuro = static fn (int $cents): string => number_format($cents / 100, $cents % 100 === 0 ? 0 : 2, ',', '.');
$fmtCount = static fn (int $value): string => number_format($value, 0, ',', '.');
$minPlanMonthlyCents = $plans->min('monthly_price_cents');
@ -46,18 +45,29 @@
</p>
<div class="mt-8 flex flex-wrap items-center gap-3">
<a href="{{ route('register') }}"
class="inline-flex items-center gap-2.5 px-7 py-3.5 text-[15px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[6px] transition-colors cursor-pointer">
Konto erstellen
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M3 13L13 3M13 3H6M13 3v7" stroke="currentColor" stroke-width="1.6"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
<a href="{{ route('login') }}"
class="inline-flex items-center gap-2 px-6 py-3.5 text-[15px] font-semibold text-hub border border-hub/25 hover:border-hub/60 rounded-[6px] transition-colors cursor-pointer">
Anmelden
</a>
@auth
<a href="{{ \App\Support\LoginRedirect::homeFor(auth()->user()) }}"
class="inline-flex items-center gap-2.5 px-7 py-3.5 text-[15px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[6px] transition-colors cursor-pointer">
Zum pressekonto
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M3 13L13 3M13 3H6M13 3v7" stroke="currentColor" stroke-width="1.6"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
@else
<a href="{{ route('register') }}"
class="inline-flex items-center gap-2.5 px-7 py-3.5 text-[15px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[6px] transition-colors cursor-pointer">
Konto erstellen
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M3 13L13 3M13 3H6M13 3v7" stroke="currentColor" stroke-width="1.6"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
<a href="{{ route('login') }}"
class="inline-flex items-center gap-2 px-6 py-3.5 text-[15px] font-semibold text-hub border border-hub/25 hover:border-hub/60 rounded-[6px] transition-colors cursor-pointer">
Anmelden
</a>
@endauth
</div>
<div class="mt-9 flex flex-wrap items-center gap-x-7 gap-y-3 text-[12.5px] text-ink-3">
@ -68,7 +78,7 @@
<path d="M5.5 8l2 2 3.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
Redaktionelle Prüfung
Prüfung inklusive
</span>
<span class="flex items-center gap-2">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" class="text-hub">
@ -342,7 +352,7 @@
</div>
<div class="text-[16.5px] font-semibold text-ink tracking-[-0.2px] mb-2">Prüfen lassen</div>
<p class="text-[12.5px] text-ink-2 leading-[1.55] m-0">
Redaktionelle Prüfung auf Quellenqualität, Relevanz und formale Korrektheit — werktags in der
Prüfung auf Quellenqualität, Relevanz und formale Korrektheit — werktags in der
Regel am selben Tag. Feedback direkt an der Mitteilung.
</p>
</li>
@ -363,7 +373,7 @@
<hr class="rule-strong" />
<div class="mt-5 px-5 py-3 bg-bg-card-warm border-l-2 border-accent text-[12.5px] text-ink-2 leading-[1.55]">
<strong class="text-ink font-semibold">Redaktionelle Prüfung</strong> ist Teil unseres
<strong class="text-ink font-semibold">Die Prüfung</strong> ist Teil unseres
Qualitätsversprechens — keine PR-Wand, kein automatisierter Durchlauf. Das gilt für alle Tarife, auch den
Starter.
</div>
@ -381,7 +391,7 @@
Ein Konto, beide Portale. Keine versteckten Aufschläge für Reichweite.
</h2>
<p class="text-[14.5px] text-ink-2 leading-[1.55] m-0 mt-4 max-w-[640px]">
Alle Tarife enthalten redaktionelle Prüfung, einen verifizierten Newsroom und die
Alle Tarife enthalten die Prüfung, Ihren eigenen Newsroom und die
Veröffentlichung auf <strong class="text-ink font-semibold"><x-web.brand-mark
brand="presseecho" :serif="false" /></strong> und <strong
class="text-ink font-semibold"><x-web.brand-mark brand="businessportal24"
@ -472,18 +482,18 @@
<div class="eyebrow on-dark mb-2">Enterprise</div>
<div class="text-[13.5px] text-ink-on-dark-2 leading-[1.6] max-w-[720px]">
Mehr als 60 Mitteilungen pro Monat, mehrere Marken oder Sonderkonditionen?
Wir erstellen Ihnen ein Angebot auf Anfrage — redaktionelle Prüfung und die
Wir erstellen Ihnen ein Angebot auf Anfrage — Prüfung und die
Veröffentlichung auf beiden Portalen inklusive.
</div>
</div>
<a href="mailto:{{ $hubContactEmail }}"
<button type="button" x-data @click="$dispatch('open-contact-modal')"
class="inline-flex items-center justify-center gap-2 px-5 py-3 text-[13.5px] font-semibold text-hub bg-white hover:bg-bg rounded-[4px] whitespace-nowrap transition-colors cursor-pointer">
Anfrage senden
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M3 9L9 3M9 3H4M9 3v5" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
</button>
</article>
<div
@ -501,10 +511,17 @@
die Abrechnung läuft sicher über Stripe. Auch ohne Abo möglich: eine
Einzel-Pressemitteilung für {{ $fmtEuro($singlePmPriceCents) }}&nbsp;€ netto.
</div>
<a href="{{ route('register') }}"
class="text-[12.5px] font-semibold text-hub whitespace-nowrap hover:underline cursor-pointer">
Jetzt Konto erstellen →
</a>
@auth
<a href="{{ \App\Support\LoginRedirect::homeFor(auth()->user()) }}"
class="text-[12.5px] font-semibold text-hub whitespace-nowrap hover:underline cursor-pointer">
Zum pressekonto →
</a>
@else
<a href="{{ route('register') }}"
class="text-[12.5px] font-semibold text-hub whitespace-nowrap hover:underline cursor-pointer">
Jetzt Konto erstellen →
</a>
@endauth
</div>
</div>
</section>
@ -700,8 +717,9 @@
</h2>
<p class="text-[13px] text-ink-3 leading-[1.55] mt-4 m-0">
Sie finden hier nicht, was Sie suchen?
<a class="text-hub font-semibold underline underline-offset-[3px] decoration-hub/30 hover:decoration-hub"
href="mailto:{{ $hubContactEmail }}">Schreiben Sie unserem Support</a> — wir antworten werktags.
<button type="button" x-data @click="$dispatch('open-contact-modal')"
class="text-hub font-semibold underline underline-offset-[3px] decoration-hub/30 hover:decoration-hub cursor-pointer">Schreiben
Sie unserem Support</button> — wir antworten werktags.
</p>
</div>
@ -722,14 +740,14 @@
Ein Konto selbst kostet nichts. Sie zahlen für die Pressemitteilungen, die Sie veröffentlichen —
entweder als Einzel-Pressemitteilung ohne Abo ({{ $fmtEuro($singlePmPriceCents) }}&nbsp;€ netto)
oder über einen Monats- bzw. Jahres-Tarif ab {{ $fmtEuro((int) $minPlanMonthlyCents) }}&nbsp;€ / Monat.
Die redaktionelle Prüfung und die Veröffentlichung auf beiden Portalen sind immer enthalten.
Die Prüfung und die Veröffentlichung auf beiden Portalen sind immer enthalten.
</div>
</details>
<details class="border-b border-bg-rule group">
<summary class="flex items-center justify-between gap-4 py-5">
<span class="text-[15.5px] font-semibold text-ink tracking-[-0.2px]">Wie läuft die
redaktionelle Prüfung?</span>
Prüfung?</span>
<svg class="faq-chev text-ink-3 flex-shrink-0" width="14" height="14"
viewBox="0 0 16 16" fill="none">
<path d="M3 6l5 5 5-5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"
@ -813,9 +831,9 @@
finden Sie in unserer <a
class="text-hub underline underline-offset-[3px] decoration-hub/30 hover:decoration-hub"
href="{{ route('datenschutz', ['edition' => 'de']) }}">Datenschutzerklärung</a>. Für
Anfragen zu Ihren Daten erreichen Sie uns unter <a
class="text-hub underline underline-offset-[3px] decoration-hub/30 hover:decoration-hub"
href="mailto:{{ $hubContactEmail }}">{{ $hubContactEmail }}</a>.
Anfragen zu Ihren Daten nutzen Sie unser <button type="button" x-data
@click="$dispatch('open-contact-modal')"
class="text-hub underline underline-offset-[3px] decoration-hub/30 hover:decoration-hub cursor-pointer">Kontaktformular</button>.
</div>
</details>
@ -885,22 +903,35 @@
</p>
</div>
<div class="relative flex flex-col gap-2.5">
<a href="{{ route('register') }}"
class="inline-flex items-center justify-center gap-2.5 px-7 py-3.5 text-[15px] font-semibold text-hub bg-white hover:bg-bg rounded-[6px] whitespace-nowrap transition-colors cursor-pointer">
Konto erstellen
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M3 13L13 3M13 3H6M13 3v7" stroke="currentColor" stroke-width="1.6"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
<a href="{{ route('login') }}"
class="inline-flex items-center justify-center gap-2 px-7 py-3 text-[13px] font-medium text-white/85 hover:text-white whitespace-nowrap transition-colors cursor-pointer">
Bereits Konto? Anmelden →
</a>
@auth
<a href="{{ \App\Support\LoginRedirect::homeFor(auth()->user()) }}"
class="inline-flex items-center justify-center gap-2.5 px-7 py-3.5 text-[15px] font-semibold text-hub bg-white hover:bg-bg rounded-[6px] whitespace-nowrap transition-colors cursor-pointer">
Zum pressekonto
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M3 13L13 3M13 3H6M13 3v7" stroke="currentColor" stroke-width="1.6"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
@else
<a href="{{ route('register') }}"
class="inline-flex items-center justify-center gap-2.5 px-7 py-3.5 text-[15px] font-semibold text-hub bg-white hover:bg-bg rounded-[6px] whitespace-nowrap transition-colors cursor-pointer">
Konto erstellen
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
<path d="M3 13L13 3M13 3H6M13 3v7" stroke="currentColor" stroke-width="1.6"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</a>
<a href="{{ route('login') }}"
class="inline-flex items-center justify-center gap-2 px-7 py-3 text-[13px] font-medium text-white/85 hover:text-white whitespace-nowrap transition-colors cursor-pointer">
Bereits Konto? Anmelden →
</a>
@endauth
</div>
</div>
</section>
</main>
<x-web.hub.contact-modal />
<x-web.hub.site-footer />
@endsection