Files
ladill-events/resources/views/public/qr/landing.blade.php
T
isaaccladandCursor ab192e443f
Deploy Ladill Events / deploy (push) Successful in 1m42s
Embed event checkouts and send seller email to gateways.
Ticket/contribution payments open in a desktop modal or mobile sheet, initialize with the organizer email, and break out of the iframe after callback.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 08:29:13 +00:00

1528 lines
110 KiB
PHP

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $qrCode->label }}</title>
@include('partials.favicon')
<meta name="description" content="{{ $qrCode->typeLabel() }} — {{ $qrCode->label }}">
@vite(['resources/css/app.css'])
</head>
<body class="min-h-screen bg-slate-50 text-slate-900 antialiased">
@php
$content = $qrCode->content();
$type = $qrCode->type;
@endphp
{{-- ===== PDF ===== --}}
@if($type === \App\Models\QrCode::TYPE_DOCUMENT)
<div class="flex min-h-screen flex-col items-center justify-center px-4 py-12">
<div class="w-full max-w-sm">
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-red-100">
<svg class="h-8 w-8 text-red-600" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"/>
</svg>
</div>
<a href="{{ $qrCode->publicPath('view') }}"
class="mt-6 flex w-full items-center justify-center gap-2 rounded-2xl bg-red-600 px-6 py-4 text-sm font-semibold text-white shadow-sm transition hover:bg-red-700">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
</svg>
View document
</a>
<a href="{{ $qrCode->publicPath('file') }}"
class="mt-3 flex w-full items-center justify-center gap-2 rounded-2xl border border-slate-200 bg-white px-6 py-4 text-sm font-semibold text-slate-700 shadow-sm transition hover:bg-slate-50">
<svg class="h-5 w-5 text-slate-500" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"/>
</svg>
Download PDF
</a>
</div>
</div>
{{-- ===== LIST OF LINKS ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_LINK_LIST)
<div class="mx-auto max-w-md px-4 py-12">
<h1 class="text-center text-2xl font-bold text-slate-900">{{ $qrCode->label }}</h1>
<div class="mt-8 space-y-3">
@foreach($content['links'] ?? [] as $link)
<a href="{{ $link['url'] }}" target="_blank" rel="noopener"
class="group flex items-center justify-between rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm transition hover:border-indigo-300 hover:shadow-md">
<span class="text-sm font-semibold text-slate-900 group-hover:text-indigo-700">{{ $link['title'] }}</span>
<svg class="h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-1 group-hover:text-indigo-500" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3"/>
</svg>
</a>
@endforeach
</div>
</div>
{{-- ===== VCARD ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_VCARD)
@php
$avatarUrl = !empty($content['avatar_path']) ? $qrCode->publicPath('avatar') : null;
$rawSocial = $content['social'] ?? [];
$social = [];
foreach ($rawSocial as $platform => $url) {
$social[$platform] = \App\Services\Qr\QrPayloadValidator::normalizeSocialUrl($platform, trim((string) $url));
}
$fullName = trim(($content['first_name'] ?? '') . ' ' . ($content['last_name'] ?? ''));
$initials = strtoupper(substr($content['first_name'] ?? ($content['last_name'] ?? '?'), 0, 1));
$socialIcons = [
'linkedin' => 'linkedin.svg',
'twitter' => 'twitter.svg',
'instagram' => 'instagram.svg',
'facebook' => 'facebook.svg',
'tiktok' => 'tik-tok.svg',
'youtube' => 'youtube.svg',
'whatsapp' => 'whatsapp.svg',
'snapchat' => 'snapchat.svg',
];
@endphp
<div class="min-h-screen bg-slate-100 pb-10">
{{-- Colored header strip --}}
<div class="h-36 bg-gradient-to-br from-indigo-500 to-violet-600"></div>
{{-- Floating card --}}
<div class="mx-auto -mt-20 max-w-sm px-4">
<div class="overflow-hidden rounded-3xl bg-white shadow-xl">
{{-- Avatar --}}
<div class="flex justify-center pt-4">
@if($avatarUrl)
<img src="{{ $avatarUrl }}" alt="{{ $fullName }}"
class="h-28 w-28 rounded-full object-cover ring-4 ring-white shadow-md">
@else
<div class="flex h-28 w-28 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 ring-4 ring-white shadow-md text-4xl font-bold text-white">
{{ $initials }}
</div>
@endif
</div>
{{-- Name & company --}}
<div class="px-6 pb-5 pt-4 text-center">
<h1 class="text-xl font-bold text-slate-900">{{ $fullName }}</h1>
@if(!empty($content['company']))
<p class="mt-0.5 text-sm text-slate-500">{{ $content['company'] }}</p>
@endif
</div>
{{-- Contact rows --}}
<div class="divide-y divide-slate-100 border-t border-slate-100">
@if(!empty($content['phone']))
<a href="tel:{{ $content['phone'] }}" class="flex items-center gap-3 px-6 py-3.5 text-sm text-slate-700 transition hover:bg-slate-50 hover:text-indigo-600">
<svg class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z"/></svg>
{{ $content['phone'] }}
</a>
@endif
@if(!empty($content['email']))
<a href="mailto:{{ $content['email'] }}" class="flex items-center gap-3 px-6 py-3.5 text-sm text-slate-700 transition hover:bg-slate-50 hover:text-indigo-600">
<svg class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"/></svg>
{{ $content['email'] }}
</a>
@endif
@if(!empty($content['website']))
<a href="{{ $content['website'] }}" target="_blank" rel="noopener" class="flex items-center gap-3 px-6 py-3.5 text-sm text-slate-700 transition hover:bg-slate-50 hover:text-indigo-600">
<svg class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253M3 12a8.96 8.96 0 0 0 .735 3.547"/></svg>
{{ $content['website'] }}
</a>
@endif
@if(!empty($content['address']))
<div class="flex items-start gap-3 px-6 py-3.5 text-sm text-slate-700">
<svg class="mt-0.5 h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"/></svg>
{{ $content['address'] }}
</div>
@endif
@if(!empty($content['note']))
<p class="px-6 py-3.5 text-sm italic text-slate-500">{{ $content['note'] }}</p>
@endif
</div>
{{-- Social links --}}
@if(!empty($social))
<div class="flex flex-wrap items-center justify-center gap-3 border-t border-slate-100 px-6 py-5">
@foreach($social as $platform => $url)
@if(isset($socialIcons[$platform]))
<a href="{{ $url }}" target="_blank" rel="noopener"
class="flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 transition hover:bg-indigo-100"
title="{{ ucfirst($platform) }}">
<img src="/images/qr-icons/{{ $socialIcons[$platform] }}" alt="{{ ucfirst($platform) }}" class="h-5 w-5">
</a>
@endif
@endforeach
</div>
@endif
{{-- Save contact button --}}
<div class="px-6 pb-6 pt-2">
<a href="{{ $qrCode->publicPath('vcard.vcf') }}"
class="btn-primary w-full">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"/></svg>
Save Contact
</a>
</div>
</div>
</div>
</div>
{{-- ===== BUSINESS ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_BUSINESS)
@php
$bizName = $content['name'] ?? $qrCode->label;
$bizTagline = $content['tagline'] ?? '';
$bizColor = $content['brand_color'] ?? '#1e3a5f';
$bizHasLogo = !empty($content['logo_path']);
$bizHasCover = !empty($content['cover_path']);
$bizLogoUrl = $bizHasLogo ? $qrCode->publicPath('business-logo') : null;
$bizCoverUrl = $bizHasCover ? $qrCode->publicPath('business-cover') : null;
$bizInitials = strtoupper(substr($bizName, 0, 1));
$bizRawSocial = $content['social'] ?? [];
$bizSocial = [];
foreach ($bizRawSocial as $platform => $url) {
$bizSocial[$platform] = \App\Services\Qr\QrPayloadValidator::normalizeSocialUrl($platform, trim((string) $url));
}
$socialIcons = [
'linkedin' => 'linkedin.svg',
'twitter' => 'twitter.svg',
'instagram' => 'instagram.svg',
'facebook' => 'facebook.svg',
'tiktok' => 'tik-tok.svg',
'youtube' => 'youtube.svg',
'whatsapp' => 'whatsapp.svg',
];
@endphp
<div class="min-h-screen bg-slate-100 pb-10">
{{-- Cover strip + logo anchor --}}
<div style="position:relative;">
{{-- Cover --}}
<div style="position:relative; overflow:hidden; height:200px;">
@if($bizHasCover)
<img src="{{ $bizCoverUrl }}" alt="{{ $bizName }}"
style="position:absolute; inset:0; width:100%; height:100%; object-fit:cover;">
<div style="position:absolute; inset:0; background:linear-gradient(to bottom,rgba(0,0,0,0.08) 0%,rgba(0,0,0,0.5) 100%);"></div>
@else
<div style="position:absolute; inset:0; background:linear-gradient(135deg,{{ $bizColor }} 0%,{{ $bizColor }}bb 100%);"></div>
<div style="position:absolute; inset:0; opacity:0.07; background-image:radial-gradient(circle, white 1px, transparent 1px); background-size:32px 32px;"></div>
@endif
</div>
{{-- Logo: floats at the cover/card seam --}}
<div style="position:absolute; bottom:-52px; left:50%; transform:translateX(-50%); z-index:10;">
@if($bizHasLogo)
<div style="width:104px; height:104px; border-radius:20px; background:#fff; box-shadow:0 8px 32px rgba(0,0,0,0.14); border:4px solid #fff; overflow:hidden; display:flex; align-items:center; justify-content:center;">
<img src="{{ $bizLogoUrl }}" alt="{{ $bizName }}"
class="public-brand-logo"
style="width:100%; height:100%; max-width:100%; object-fit:contain; padding:8px;">
</div>
@else
<div style="width:104px; height:104px; border-radius:20px; background:{{ $bizColor }}; box-shadow:0 8px 32px rgba(0,0,0,0.18); border:4px solid #fff; display:flex; align-items:center; justify-content:center; color:#fff; font-size:2.5rem; font-weight:900;">
{{ $bizInitials }}
</div>
@endif
</div>
</div>
{{-- Floating card --}}
<div class="mx-auto max-w-sm px-4" style="margin-top:64px;">
<div class="overflow-hidden rounded-3xl bg-white shadow-xl">
{{-- Name & tagline centred --}}
<div class="px-6 pb-5 pt-8 text-center">
<h1 class="text-xl font-bold text-slate-900">{{ $bizName }}</h1>
@if($bizTagline)
<p class="mt-1 text-sm text-slate-500">{{ $bizTagline }}</p>
@endif
</div>
{{-- Contact rows --}}
<div class="divide-y divide-slate-100 border-t border-slate-100">
@if(!empty($content['phone']))
<a href="tel:{{ $content['phone'] }}" class="flex items-center gap-3 px-6 py-3.5 text-sm text-slate-700 transition hover:bg-slate-50"
style="--hover-color: {{ $bizColor }}">
<svg class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z"/></svg>
{{ $content['phone'] }}
</a>
@endif
@if(!empty($content['email']))
<a href="mailto:{{ $content['email'] }}" class="flex items-center gap-3 px-6 py-3.5 text-sm text-slate-700 transition hover:bg-slate-50">
<svg class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"/></svg>
{{ $content['email'] }}
</a>
@endif
@if(!empty($content['website']))
<a href="{{ $content['website'] }}" target="_blank" rel="noopener" class="flex items-center gap-3 px-6 py-3.5 text-sm text-slate-700 transition hover:bg-slate-50">
<svg class="h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253M3 12a8.96 8.96 0 0 0 .735 3.547"/></svg>
{{ $content['website'] }}
</a>
@endif
@if(!empty($content['address']))
<div class="flex items-start gap-3 px-6 py-3.5 text-sm text-slate-700">
<svg class="mt-0.5 h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"/></svg>
{{ $content['address'] }}
</div>
@endif
@if(!empty($content['hours']))
<div class="flex items-start gap-3 px-6 py-3.5 text-sm text-slate-700">
<svg class="mt-0.5 h-4 w-4 shrink-0 text-slate-400" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>
<span class="whitespace-pre-line">{{ $content['hours'] }}</span>
</div>
@endif
</div>
{{-- Social links --}}
@if(!empty($bizSocial))
<div class="flex flex-wrap items-center justify-center gap-3 border-t border-slate-100 px-6 py-5">
@foreach($bizSocial as $platform => $url)
@if(isset($socialIcons[$platform]))
<a href="{{ $url }}" target="_blank" rel="noopener"
class="flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 transition hover:opacity-80"
style="background-color: {{ $bizColor }}1a;"
title="{{ ucfirst($platform) }}">
<img src="/images/qr-icons/{{ $socialIcons[$platform] }}" alt="{{ ucfirst($platform) }}" class="h-5 w-5">
</a>
@endif
@endforeach
</div>
@endif
{{-- CTA: website button if present --}}
@if(!empty($content['website']))
<div class="px-6 pb-6 {{ !empty($bizSocial) ? 'pt-0' : 'pt-2' }}">
<a href="{{ $content['website'] }}" target="_blank" rel="noopener"
class="flex w-full items-center justify-center gap-2 rounded-2xl py-3.5 text-sm font-semibold text-white shadow-sm transition"
style="background: {{ $bizColor }};">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253M3 12a8.96 8.96 0 0 0 .735 3.547"/></svg>
Visit Website
</a>
</div>
@endif
</div>
</div>
</div>
{{-- ===== CHURCH ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_CHURCH)
@php
$churchName = $content['name'] ?? $qrCode->label;
$churchColor = $content['brand_color'] ?? '#1a3a5c';
$churchHasLogo = !empty($content['logo_path']);
$churchHasCover = !empty($content['cover_path']);
$churchLogoUrl = $churchHasLogo ? $qrCode->publicPath('church-logo') : null;
$churchCoverUrl = $churchHasCover ? $qrCode->publicPath('church-cover') : null;
$churchInitials = strtoupper(substr($churchName, 0, 2));
$churchAcceptsPay = !empty($content['accepts_payment']);
$churchCurrency = $content['currency'] ?? 'GHS';
$churchTypes = $content['collection_types'] ?? ['offering', 'tithe', 'donation', 'harvest'];
// Normalise legacy lowercase slugs → display strings
$legacyLabels = ['offering'=>'Offering','tithe'=>'Tithe','donation'=>'Donation','harvest'=>'Harvest'];
$churchTypeLabels = array_combine(
$churchTypes,
array_map(fn($t) => $legacyLabels[strtolower(trim($t))] ?? trim($t), $churchTypes)
);
$churchOrderRoute = $qrCode->publicPath('order');
$churchCsrf = csrf_token();
@endphp
<div x-data="{
collectionType: @js($churchTypes[0] ?? 'offering'),
amount: '',
name: '',
email: '',
phone: '',
loading: false,
errorMsg: '',
showSheet: false,
checkoutUrl: '',
async give() {
if (!this.amount || parseFloat(this.amount) <= 0) { this.errorMsg = 'Enter a valid amount.'; return; }
if (!this.name.trim()) { this.errorMsg = 'Enter your name.'; return; }
if (!this.phone.trim()) { this.errorMsg = 'Enter your phone number.'; return; }
this.errorMsg = ''; this.loading = true;
const labels = @js($churchTypeLabels);
try {
const res = await fetch(@js($churchOrderRoute), {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': @js($churchCsrf) },
body: JSON.stringify({
customer_name: this.name,
customer_email: this.email,
customer_phone: this.phone,
items: [{ name: labels[this.collectionType] || this.collectionType, price: parseFloat(this.amount), qty: 1 }],
}),
});
const data = await res.json();
if (data.error) { this.errorMsg = data.error; this.loading = false; return; }
this.checkoutUrl = data.checkout_url;
this.showSheet = true;
this.loading = false;
} catch(e) {
this.errorMsg = 'Network error. Please try again.';
this.loading = false;
}
}
}" class="min-h-screen bg-slate-100 pb-10">
{{-- Cover strip + logo anchor --}}
<div style="position:relative;">
<div style="position:relative; overflow:hidden; height:200px;">
@if($churchHasCover)
<img src="{{ $churchCoverUrl }}" alt="{{ $churchName }}"
style="position:absolute; inset:0; width:100%; height:100%; object-fit:cover;">
<div style="position:absolute; inset:0; background:linear-gradient(to bottom,rgba(0,0,0,0.05) 0%,rgba(0,0,0,0.55) 100%);"></div>
@else
<div style="position:absolute; inset:0; background:linear-gradient(160deg,{{ $churchColor }} 0%,{{ $churchColor }}bb 100%);"></div>
<div style="position:absolute; inset:0; opacity:0.07; background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Cpath fill='white' d='M30 2v8m-4-4h8M8 58V26l22-16 22 16v32M22 58V44a8 8 0 0116 0v14M8 26H2M58 26h-6'/%3E%3C/svg%3E\"); background-size:60px 60px;"></div>
@endif
</div>
{{-- Logo: floats at cover/card seam --}}
<div style="position:absolute; bottom:-52px; left:50%; transform:translateX(-50%); z-index:10;">
@if($churchHasLogo)
<div style="width:104px; height:104px; border-radius:20px; background:#fff; box-shadow:0 8px 32px rgba(0,0,0,0.14); border:4px solid #fff; overflow:hidden; display:flex; align-items:center; justify-content:center;">
<img src="{{ $churchLogoUrl }}" alt="{{ $churchName }}"
class="public-brand-logo"
style="width:100%; height:100%; max-width:100%; object-fit:contain; padding:8px;">
</div>
@else
<div style="width:104px; height:104px; border-radius:20px; background:{{ $churchColor }}; box-shadow:0 8px 32px rgba(0,0,0,0.18); border:4px solid #fff; display:flex; align-items:center; justify-content:center; color:#fff; font-size:2rem; font-weight:900;">
{{ $churchInitials }}
</div>
@endif
</div>
</div>
{{-- Floating card --}}
<div class="mx-auto max-w-sm px-4 space-y-4" style="margin-top:64px;">
<div class="overflow-hidden rounded-3xl bg-white shadow-xl">
{{-- Name + denomination + description --}}
<div class="px-6 pb-4 pt-8 text-center">
<h1 class="text-xl font-bold text-slate-900">{{ $churchName }}</h1>
@if(!empty($content['denomination']))
<p class="mt-1 text-sm font-medium" style="color:{{ $churchColor }};">{{ $content['denomination'] }}</p>
@endif
@if(!empty($content['description']))
<p class="mt-2 text-xs text-slate-500 leading-relaxed">{{ $content['description'] }}</p>
@endif
</div>
{{-- Service times badge --}}
@if(!empty($content['service_times']))
<div class="px-6 pb-3 text-center">
<span style="display:inline-flex; align-items:center; gap:5px; background:#f8fafc; border:1px solid #e2e8f0; border-radius:999px; padding:5px 12px; font-size:0.75rem; color:#475569; font-weight:500;">
<svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>
{{ $content['service_times'] }}
</span>
</div>
@endif
{{-- Compact icon action row --}}
@php $hasContact = !empty($content['phone']) || !empty($content['email']) || !empty($content['address']) || !empty($content['website']); @endphp
@if($hasContact)
<div style="display:flex; justify-content:center; gap:12px; padding:12px 24px 18px; border-top:1px solid #f1f5f9;">
@if(!empty($content['phone']))
<a href="tel:{{ $content['phone'] }}" title="{{ $content['phone'] }}"
style="width:46px; height:46px; border-radius:50%; display:flex; align-items:center; justify-content:center; background:{{ $churchColor }}12; color:{{ $churchColor }}; text-decoration:none; transition:opacity 0.15s;" onmouseover="this.style.opacity='.75'" onmouseout="this.style.opacity='1'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z"/></svg>
</a>
@endif
@if(!empty($content['email']))
<a href="mailto:{{ $content['email'] }}" title="{{ $content['email'] }}"
style="width:46px; height:46px; border-radius:50%; display:flex; align-items:center; justify-content:center; background:{{ $churchColor }}12; color:{{ $churchColor }}; text-decoration:none; transition:opacity 0.15s;" onmouseover="this.style.opacity='.75'" onmouseout="this.style.opacity='1'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"/></svg>
</a>
@endif
@if(!empty($content['address']))
<a href="https://maps.google.com/?q={{ urlencode($content['address']) }}" target="_blank" rel="noopener" title="{{ $content['address'] }}"
style="width:46px; height:46px; border-radius:50%; display:flex; align-items:center; justify-content:center; background:{{ $churchColor }}12; color:{{ $churchColor }}; text-decoration:none; transition:opacity 0.15s;" onmouseover="this.style.opacity='.75'" onmouseout="this.style.opacity='1'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"/></svg>
</a>
@endif
@if(!empty($content['website']))
<a href="{{ $content['website'] }}" target="_blank" rel="noopener" title="Visit website"
style="width:46px; height:46px; border-radius:50%; display:flex; align-items:center; justify-content:center; background:{{ $churchColor }}12; color:{{ $churchColor }}; text-decoration:none; transition:opacity 0.15s;" onmouseover="this.style.opacity='.75'" onmouseout="this.style.opacity='1'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253M3 12a8.96 8.96 0 0 0 .735 3.547"/></svg>
</a>
@endif
</div>
@endif
</div>
@if($churchAcceptsPay && !empty($churchTypes))
{{-- Brand color CSS variable so focus rings match --}}
<style>
.church-input:focus { outline: none; border-color: {{ $churchColor }} !important; box-shadow: 0 0 0 3px {{ $churchColor }}22; }
.church-quick:hover { border-color: {{ $churchColor }}66; }
.church-amount-carousel { scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.church-amount-carousel::-webkit-scrollbar { display: none; }
</style>
{{-- Giving card --}}
<div class="overflow-hidden rounded-3xl bg-white shadow-xl">
{{-- Header --}}
<div class="px-6 py-6 text-center" style="background: linear-gradient(160deg, {{ $churchColor }}0d 0%, {{ $churchColor }}1f 100%); border-bottom: 1px solid {{ $churchColor }}22;">
<h2 class="text-lg font-bold text-slate-900">Give Online</h2>
<p class="mt-0.5 text-xs text-slate-500">Secured by Paystack. Powered by Ladill Pay</p>
</div>
<div class="px-5 pb-6 pt-5 space-y-5">
{{-- Collection type icon cards --}}
@php
// Keyed by lowercase label for case-insensitive lookup
$typeIconMap = [
'offering' => 'M21 11.25v8.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 1 0 9.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1 1 14.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z',
'tithe' => 'M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.75M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm3 0h.008v.008H18V10.5Zm-12 0h.008v.008H6V10.5Z',
'donation' => 'M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z',
'harvest' => 'M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z',
'school fees' => 'M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.63 48.63 0 0 1 12 20.904a48.63 48.63 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.57 50.57 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.697 50.697 0 0 1 12 13.489a50.702 50.702 0 0 1 3.741-1.342M6.75 14.25v4.5',
'pta levy' => 'M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z',
'exam fees' => 'M16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10',
'development fund' => 'M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008v-.008Z',
'sadaqah' => 'M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z',
'zakat' => 'M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.75M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm3 0h.008v.008H18V10.5Zm-12 0h.008v.008H6V10.5Z',
"waqf" => 'M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008v-.008Zm0 3h.008v.008h-.008v-.008Z',
"jumu'ah" => 'M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z',
'general donation' => 'M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z',
'project fund' => 'M20.25 14.15v4.25c0 1.094-.787 2.036-1.872 2.18-2.087.277-4.216.42-6.378.42s-4.291-.143-6.378-.42c-1.085-.144-1.872-1.086-1.872-2.18v-4.25m16.5 0a2.18 2.18 0 0 0 .75-1.661V8.706c0-1.081-.768-2.015-1.837-2.175a48.114 48.114 0 0 0-3.413-.387m4.5 8.006c-.194.165-.42.295-.673.38A23.978 23.978 0 0 1 12 15.75c-2.648 0-5.195-.429-7.577-1.22a2.016 2.016 0 0 1-.673-.38m0 0A2.18 2.18 0 0 1 3 12.489V8.706c0-1.081.768-2.015 1.837-2.175a48.111 48.111 0 0 1 3.413-.387m7.5 0V5.25A2.25 2.25 0 0 0 13.5 3h-3a2.25 2.25 0 0 0-2.25 2.25v.894m7.5 0a48.667 48.667 0 0 0-7.5 0M12 12.75h.008v.008H12v-.008Z',
'emergency relief' => 'M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z',
'sponsorship' => 'M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z',
'membership dues' => 'M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Zm6-10.125a1.875 1.875 0 1 1-3.75 0 1.875 1.875 0 0 1 3.75 0Zm1.294 6.336a6.721 6.721 0 0 1-3.17.789 6.721 6.721 0 0 1-3.168-.789 3.376 3.376 0 0 1 6.338 0Z',
'event fund' => 'M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5',
];
$defaultIcon = 'M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09Z';
// Resolve icon for each type by lowercase key
$typeIcons = [];
foreach ($churchTypeLabels as $key => $label) {
$typeIcons[$key] = $typeIconMap[strtolower($label)] ?? $defaultIcon;
}
@endphp
@if(count($churchTypeLabels) > 1)
<div style="border-radius:24px; background:linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); border:1px solid #eef2f7; padding:14px; box-shadow:0 14px 36px rgba(15,23,42,0.06);">
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px;">
<div>
<p class="text-[10px] font-bold uppercase tracking-widest" style="color:{{ $churchColor }}">I'm giving for</p>
<p style="margin-top:3px; font-size:0.75rem; color:#94a3b8;">Choose a giving category</p>
</div>
<span style="display:flex; height:34px; width:34px; flex-shrink:0; align-items:center; justify-content:center; border-radius:999px; background:{{ $churchColor }}12; color:{{ $churchColor }};">
<svg width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21s8.25-4.875 8.25-11.25A4.875 4.875 0 0 0 12 6.227 4.875 4.875 0 0 0 3.75 9.75C3.75 16.125 12 21 12 21Z"/>
</svg>
</span>
</div>
<div style="display:flex; flex-direction:column; gap:8px;">
@foreach($churchTypeLabels as $ct => $ctLabel)
@php $iconPath = $typeIcons[$ct] ?? $defaultIcon; @endphp
<button type="button"
@click="collectionType = @js($ct)"
:style="collectionType === @js($ct)
? 'display:flex;align-items:center;gap:12px;width:100%;border:1.5px solid {{ $churchColor }};border-radius:16px;padding:12px 14px;cursor:pointer;transition:all 0.2s ease;text-align:left;outline:none;overflow:hidden;background:linear-gradient(135deg, {{ $churchColor }}15 0%, #ffffff 70%);box-shadow:0 4px 14px {{ $churchColor }}20;'
: 'display:flex;align-items:center;gap:12px;width:100%;border:1.5px solid #edf0f7;border-radius:16px;padding:12px 14px;cursor:pointer;transition:all 0.2s ease;text-align:left;outline:none;overflow:hidden;background:#fafbfc;'">
{{-- Icon --}}
<span :style="collectionType === @js($ct)
? 'flex-shrink:0;display:flex;height:38px;width:38px;align-items:center;justify-content:center;border-radius:12px;background:{{ $churchColor }}18;transition:color 0.2s;color:{{ $churchColor }};'
: 'flex-shrink:0;display:flex;height:38px;width:38px;align-items:center;justify-content:center;border-radius:12px;background:#f1f5f9;transition:color 0.2s;color:#94a3b8;'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="{{ $iconPath }}"/>
</svg>
</span>
{{-- Label — single line, ellipsis on overflow --}}
<span :style="collectionType === @js($ct)
? 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.875rem;font-weight:700;transition:color 0.2s;color:{{ $churchColor }};'
: 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.875rem;font-weight:600;transition:color 0.2s;color:#475569;'">
{{ $ctLabel }}
</span>
{{-- Selected indicator --}}
<span x-show="collectionType === @js($ct)" x-cloak
style="flex-shrink:0;width:22px;height:22px;color:{{ $churchColor }};">
<svg viewBox="0 0 24 24" fill="currentColor" style="display:block;width:100%;height:100%;">
<path d="m12,0C5.383,0,0,5.383,0,12s5.383,12,12,12,12-5.383,12-12S18.617,0,12,0Zm-.091,15.419c-.387.387-.896.58-1.407.58s-1.025-.195-1.416-.585l-2.782-2.696,1.393-1.437,2.793,2.707,5.809-5.701,1.404,1.425-5.793,5.707Z"/>
</svg>
</span>
</button>
@endforeach
</div>
</div>
@endif
{{-- Quick amounts carousel — shows 2 full chips + peek at 3rd --}}
<div>
<p class="mb-2.5 text-[10px] font-bold uppercase tracking-widest" style="color:{{ $churchColor }}">Quick amount</p>
<div class="church-amount-carousel"
style="display:grid; grid-auto-flow:column; grid-auto-columns:calc((100% - 20px) / 2.5); gap:10px; overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:2px;">
@foreach([10, 20, 50, 100, 200] as $qa)
<button type="button"
@click="amount = '{{ $qa }}'"
:style="amount == '{{ $qa }}'
? 'scroll-snap-align:start;border-radius:16px;border:1.5px solid {{ $churchColor }};padding:14px 10px;font-size:0.95rem;font-weight:800;text-align:center;transition:all 0.15s;white-space:nowrap;background:{{ $churchColor }};color:#fff;box-shadow:0 4px 12px {{ $churchColor }}40;'
: 'scroll-snap-align:start;border-radius:16px;border:1.5px solid #e2e8f0;padding:14px 10px;font-size:0.95rem;font-weight:700;text-align:center;transition:all 0.15s;white-space:nowrap;background:#fff;color:#475569;'">
{{ $churchCurrency }} {{ $qa }}
</button>
@endforeach
</div>
</div>
{{-- Amount input — inline styles to prevent overflow on mobile --}}
<div>
<p class="mb-2.5 text-[10px] font-bold uppercase tracking-widest" style="color:{{ $churchColor }}">Or enter amount</p>
<div style="display:flex; align-items:center; border-radius:16px; border:1.5px solid #e2e8f0; background:#f8fafc; padding:12px 16px; box-sizing:border-box; width:100%;">
<span style="flex-shrink:0; font-size:0.875rem; font-weight:700; color:#94a3b8; margin-right:8px;">{{ $churchCurrency }}</span>
<input type="number" x-model="amount" inputmode="decimal" min="1" step="0.01" placeholder="0.00"
class="church-input"
style="flex:1; min-width:0; width:0; background:transparent; border:none; outline:none; font-size:1.75rem; font-weight:800; color:#0f172a; font-family:inherit;"
@focus="$el.parentElement.style.borderColor='{{ $churchColor }}'; $el.parentElement.style.boxShadow='0 0 0 3px {{ $churchColor }}22'"
@blur="$el.parentElement.style.borderColor='#e2e8f0'; $el.parentElement.style.boxShadow='none'">
</div>
</div>
{{-- Contact fields --}}
<div class="space-y-2.5">
<p class="text-[10px] font-bold uppercase tracking-widest" style="color:{{ $churchColor }}">Your details</p>
<input type="text" x-model="name" placeholder="Full name *" autocomplete="name"
class="church-input"
style="display:block; width:100%; box-sizing:border-box; border-radius:14px; border:1.5px solid #e2e8f0; padding:12px 16px; font-size:0.875rem; color:#0f172a; background:#fff;">
<input type="tel" x-model="phone" placeholder="Phone number *" autocomplete="tel"
class="church-input"
style="display:block; width:100%; box-sizing:border-box; border-radius:14px; border:1.5px solid #e2e8f0; padding:12px 16px; font-size:0.875rem; color:#0f172a; background:#fff;">
</div>
{{-- Error --}}
<div x-show="errorMsg" x-cloak
class="rounded-xl border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-600"
x-text="errorMsg"></div>
{{-- Give button --}}
<button type="button" @click="give()" :disabled="loading"
class="flex w-full items-center justify-center gap-2 rounded-2xl py-4 text-base font-bold text-white transition disabled:opacity-60 active:scale-[0.98]"
style="background:{{ $churchColor }}; box-shadow:0 8px 24px {{ $churchColor }}44;">
<svg x-show="loading" x-cloak class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
<span x-text="loading ? 'Processing…' : ('Give ' + @js($churchCurrency) + ' ' + (parseFloat(amount)||0).toFixed(2))">Give</span>
</button>
<p class="text-center text-[11px] text-slate-400">Your giving is safe &amp; secure</p>
</div>
</div>
@endif
@include('partials.paystack-sheet')
</div>
</div>
{{-- ===== EVENT ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_EVENT)
@php
$evColor = $content['brand_color'] ?? '#4f46e5';
$evHasLogo = !empty($content['logo_path']);
$evHasCover = !empty($content['cover_path']);
$evLogoUrl = $evHasLogo ? $qrCode->publicPath('event-logo') : null;
$evCoverUrl = $evHasCover ? $qrCode->publicPath('event-cover') : null;
$evName = $content['name'] ?? $qrCode->label;
$evInitials = strtoupper(mb_substr($evName, 0, 2));
$evTiers = $content['tiers'] ?? [];
$evCurrency = $content['currency'] ?? 'GHS';
$evFields = $content['badge_fields'] ?? [];
$evOpen = (bool) ($content['registration_open'] ?? true);
$evMode = in_array($content['mode'] ?? 'ticketing', ['contributions', 'free'], true) ? ($content['mode'] ?? 'ticketing') : 'ticketing';
$evCategories = $content['contribution_categories'] ?? ['Contribution'];
$evHeading = $evMode === 'contributions' ? 'Make a contribution' : 'Register';
$evRegRoute = $qrCode->publicPath('register');
$evCsrf = csrf_token();
$evMeetReturn = trim((string) ($meetReturn ?? ''));
@endphp
<div x-data="{
mode: @js($evMode),
tier: @js($evMode === 'contributions' ? ($evCategories[0] ?? '') : ($evTiers[0]['name'] ?? '')),
tierPrice: @js((float) ($evTiers[0]['price'] ?? 0)),
amount: '',
name: '', email: '', phone: '',
fields: {},
meetReturn: @js($evMeetReturn),
loading: false, errorMsg: '',
showSheet: false, checkoutUrl: '',
selectTier(n, p) { this.tier = n; this.tierPrice = p; },
selectCategory(n) { this.tier = n; },
async submit() {
if (!this.tier) { this.errorMsg = this.mode === 'contributions' ? 'Select a category.' : 'Select a ticket type.'; return; }
if (this.mode === 'contributions' && !(parseFloat(this.amount) > 0)) { this.errorMsg = 'Enter a contribution amount.'; return; }
if (!this.name.trim()) { this.errorMsg = 'Enter your full name.'; return; }
if (!this.email.trim() || !this.email.includes('@')) { this.errorMsg = 'Enter a valid email address.'; return; }
if (!this.phone.trim()) { this.errorMsg = 'Enter your phone number.'; return; }
this.errorMsg = ''; this.loading = true;
try {
const res = await fetch(@js($evRegRoute), {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': @js($evCsrf) },
body: JSON.stringify({ tier: this.tier, amount: this.mode === 'contributions' ? this.amount : null, attendee_name: this.name, attendee_email: this.email, attendee_phone: this.phone, badge_fields: this.fields, meet_return: this.meetReturn || null }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
this.errorMsg = data.error || data.message || 'Registration failed. Please try again.';
this.loading = false;
return;
}
if (data.error) { this.errorMsg = data.error; this.loading = false; return; }
if (data.paid) {
if (!data.checkout_url) { this.errorMsg = 'Could not start checkout. Please try again.'; this.loading = false; return; }
this.checkoutUrl = data.checkout_url; this.showSheet = true; this.loading = false;
return;
}
if (data.success_url) { window.location.href = data.success_url; return; }
this.errorMsg = 'Registration failed. Please try again.';
this.loading = false;
} catch(e) { this.errorMsg = 'Network error. Please try again.'; this.loading = false; }
}
}" class="min-h-screen bg-slate-50 pb-12">
{{-- Hero --}}
<div class="relative overflow-hidden" style="min-height:230px;">
@if($evHasCover)
<img src="{{ $evCoverUrl }}" alt="{{ $evName }}" class="absolute inset-0 h-full w-full object-cover">
<div class="absolute inset-0" style="background:linear-gradient(to bottom, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.72) 100%);"></div>
@else
<div class="absolute inset-0" style="background:linear-gradient(150deg, {{ $evColor }} 0%, {{ $evColor }}cc 100%);"></div>
@endif
<div class="relative flex min-h-[230px] flex-col items-center justify-end px-6 pb-7 text-center text-white">
@if($evHasLogo)
<img src="{{ $evLogoUrl }}" alt="{{ $evName }}" class="public-brand-logo mb-3 h-16 w-16 max-w-[50vw] rounded-2xl bg-white object-contain p-1 shadow-lg ring-2 ring-white/40">
@else
<div class="mb-3 flex h-16 w-16 items-center justify-center rounded-2xl text-xl font-black text-white shadow-lg ring-2 ring-white/40" style="background:rgba(255,255,255,0.18); backdrop-filter:blur(6px);">{{ $evInitials }}</div>
@endif
<h1 class="text-2xl font-black leading-tight drop-shadow">{{ $evName }}</h1>
@if(!empty($content['tagline']))<p class="mt-1 text-sm font-medium text-white/85">{{ $content['tagline'] }}</p>@endif
<div class="mt-3 flex flex-wrap items-center justify-center gap-2 text-xs">
@if(!empty($content['starts_at']))
<span class="inline-flex items-center gap-1.5 rounded-full bg-white/20 px-3 py-1 font-semibold backdrop-blur-sm">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"/></svg>
{{ \App\Support\Qr\QrDateFormatter::forDisplay($content['starts_at']) }}
</span>
@endif
@if(!empty($content['location']))
<span class="inline-flex items-center gap-1.5 rounded-full bg-white/20 px-3 py-1 font-semibold backdrop-blur-sm">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"/></svg>
{{ $content['location'] }}
</span>
@endif
</div>
</div>
</div>
<div class="mx-auto max-w-md px-4">
@if(!empty($content['description']))
<p class="mt-6 text-center text-sm leading-relaxed text-slate-600">{{ $content['description'] }}</p>
@endif
@php
$evFormat = $content['format'] ?? 'in_person';
$meetAccess = app(\App\Services\Meet\EventMeetAccessService::class);
$evVirtualSessions = collect($content['virtual_sessions'] ?? [])
->filter(fn ($s) => is_array($s) && ! empty($s['join_url']));
@endphp
@if(in_array($evFormat, ['virtual', 'hybrid'], true) && $evVirtualSessions->isNotEmpty())
<div class="mt-6 space-y-3">
<h2 class="text-center text-sm font-bold text-slate-900">Virtual sessions</h2>
@foreach($evVirtualSessions as $session)
@php $windowOpen = $meetAccess->isJoinWindowOpen($session); @endphp
@if($windowOpen)
<a href="{{ $session['join_url'] }}" target="_blank" rel="noopener"
class="block rounded-2xl border border-indigo-100 bg-white px-4 py-3 shadow-sm transition hover:border-indigo-300">
<p class="font-semibold text-slate-900">{{ $session['title'] ?? 'Join session' }}</p>
@if(!empty($session['scheduled_at']))
<p class="mt-1 text-xs text-slate-500">{{ \App\Support\Qr\QrDateFormatter::forDisplay($session['scheduled_at']) }}</p>
@endif
@if(($session['meet_status'] ?? '') === 'live')
<p class="mt-1 text-xs font-semibold text-emerald-600">Live now</p>
@endif
<p class="mt-2 text-xs font-semibold text-indigo-600">Join virtual session </p>
</a>
@else
<div class="block rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3">
<p class="font-semibold text-slate-700">{{ $session['title'] ?? 'Virtual session' }}</p>
@if(!empty($session['scheduled_at']))
<p class="mt-1 text-xs text-slate-500">{{ \App\Support\Qr\QrDateFormatter::forDisplay($session['scheduled_at']) }}</p>
@endif
<p class="mt-2 text-xs text-amber-700">Join opens 15 minutes before the session starts.</p>
</div>
@endif
@endforeach
</div>
@endif
@if(! $evOpen)
<div class="mt-6 rounded-2xl border border-amber-200 bg-amber-50 px-5 py-4 text-center text-sm font-medium text-amber-700">{{ $evMode === 'contributions' ? 'Contributions are currently closed.' : 'Registration is currently closed.' }}</div>
@else
<div class="mt-6 overflow-hidden rounded-3xl bg-white shadow-xl">
<div class="px-5 py-4 text-center" style="background:linear-gradient(160deg, {{ $evColor }}0d, {{ $evColor }}1f); border-bottom:1px solid {{ $evColor }}22;">
<h2 class="text-base font-bold text-slate-900">{{ $evHeading }}</h2>
</div>
<div class="px-5 pb-6 pt-5 space-y-5">
{{-- Ticket tiers --}}
@if($evMode === 'ticketing' && count($evTiers) > 0)
<div style="border-radius:24px; background:linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); border:1px solid #eef2f7; padding:14px; box-shadow:0 14px 36px rgba(15,23,42,0.06);">
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px;">
<div>
<p class="text-[10px] font-bold uppercase tracking-widest" style="color:{{ $evColor }}">Ticket</p>
<p style="margin-top:3px; font-size:0.75rem; color:#94a3b8;">Choose a ticket type</p>
</div>
<span style="display:flex; height:34px; width:34px; flex-shrink:0; align-items:center; justify-content:center; border-radius:999px; background:{{ $evColor }}12; color:{{ $evColor }};">
<svg width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z"/>
</svg>
</span>
</div>
<div style="display:flex; flex-direction:column; gap:8px;">
@foreach($evTiers as $t)
@php $tPrice = (float) ($t['price'] ?? 0); @endphp
<button type="button"
@click="selectTier(@js($t['name']), {{ $tPrice }})"
:style="tier === @js($t['name'])
? 'display:flex;align-items:center;gap:12px;width:100%;border:1.5px solid {{ $evColor }};border-radius:16px;padding:12px 14px;cursor:pointer;transition:all 0.2s ease;text-align:left;outline:none;overflow:hidden;background:linear-gradient(135deg, {{ $evColor }}15 0%, #ffffff 70%);box-shadow:0 4px 14px {{ $evColor }}20;'
: 'display:flex;align-items:center;gap:12px;width:100%;border:1.5px solid #edf0f7;border-radius:16px;padding:12px 14px;cursor:pointer;transition:all 0.2s ease;text-align:left;outline:none;overflow:hidden;background:#fafbfc;'">
{{-- Icon --}}
<span :style="tier === @js($t['name'])
? 'flex-shrink:0;display:flex;height:38px;width:38px;align-items:center;justify-content:center;border-radius:12px;background:{{ $evColor }}18;transition:color 0.2s;color:{{ $evColor }};'
: 'flex-shrink:0;display:flex;height:38px;width:38px;align-items:center;justify-content:center;border-radius:12px;background:#f1f5f9;transition:color 0.2s;color:#94a3b8;'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z"/>
</svg>
</span>
{{-- Name + price --}}
<span style="flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;">
<span :style="tier === @js($t['name'])
? 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.875rem;font-weight:700;transition:color 0.2s;color:{{ $evColor }};'
: 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.875rem;font-weight:600;transition:color 0.2s;color:#475569;'">
{{ $t['name'] }}
</span>
<span style="font-size:0.75rem;font-weight:700;color:#94a3b8;">{{ $tPrice > 0 ? $evCurrency . ' ' . number_format($tPrice, 2) : 'Free' }}</span>
</span>
{{-- Selected indicator --}}
<span x-show="tier === @js($t['name'])" x-cloak
style="flex-shrink:0;width:22px;height:22px;color:{{ $evColor }};">
<svg viewBox="0 0 24 24" fill="currentColor" style="display:block;width:100%;height:100%;">
<path d="m12,0C5.383,0,0,5.383,0,12s5.383,12,12,12,12-5.383,12-12S18.617,0,12,0Zm-.091,15.419c-.387.387-.896.58-1.407.58s-1.025-.195-1.416-.585l-2.782-2.696,1.393-1.437,2.793,2.707,5.809-5.701,1.404,1.425-5.793,5.707Z"/>
</svg>
</span>
</button>
@endforeach
</div>
</div>
@endif
{{-- Contribution categories + amount --}}
@if($evMode === 'contributions')
@if(count($evCategories) > 1)
<div style="border-radius:24px; background:linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); border:1px solid #eef2f7; padding:14px; box-shadow:0 14px 36px rgba(15,23,42,0.06);">
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px;">
<div>
<p class="text-[10px] font-bold uppercase tracking-widest" style="color:{{ $evColor }}">I'm giving for</p>
<p style="margin-top:3px; font-size:0.75rem; color:#94a3b8;">Choose a category</p>
</div>
<span style="display:flex; height:34px; width:34px; flex-shrink:0; align-items:center; justify-content:center; border-radius:999px; background:{{ $evColor }}12; color:{{ $evColor }};">
<svg width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 11.25v8.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 1 0 9.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1 1 14.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z"/>
</svg>
</span>
</div>
<div style="display:flex; flex-direction:column; gap:8px;">
@foreach($evCategories as $cat)
<button type="button"
@click="selectCategory(@js($cat))"
:style="tier === @js($cat)
? 'display:flex;align-items:center;gap:12px;width:100%;border:1.5px solid {{ $evColor }};border-radius:16px;padding:12px 14px;cursor:pointer;transition:all 0.2s ease;text-align:left;outline:none;overflow:hidden;background:linear-gradient(135deg, {{ $evColor }}15 0%, #ffffff 70%);box-shadow:0 4px 14px {{ $evColor }}20;'
: 'display:flex;align-items:center;gap:12px;width:100%;border:1.5px solid #edf0f7;border-radius:16px;padding:12px 14px;cursor:pointer;transition:all 0.2s ease;text-align:left;outline:none;overflow:hidden;background:#fafbfc;'">
<span :style="tier === @js($cat)
? 'flex-shrink:0;display:flex;height:38px;width:38px;align-items:center;justify-content:center;border-radius:12px;background:{{ $evColor }}18;color:{{ $evColor }};'
: 'flex-shrink:0;display:flex;height:38px;width:38px;align-items:center;justify-content:center;border-radius:12px;background:#f1f5f9;color:#94a3b8;'">
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 11.25v8.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5v-8.25M12 4.875A2.625 2.625 0 1 0 9.375 7.5H12m0-2.625V7.5m0-2.625A2.625 2.625 0 1 1 14.625 7.5H12m0 0V21m-8.625-9.75h18c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125h-18c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z"/>
</svg>
</span>
<span :style="tier === @js($cat)
? 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.875rem;font-weight:700;color:{{ $evColor }};'
: 'flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.875rem;font-weight:600;color:#475569;'">
{{ $cat }}
</span>
<span x-show="tier === @js($cat)" x-cloak style="flex-shrink:0;width:22px;height:22px;color:{{ $evColor }};">
<svg viewBox="0 0 24 24" fill="currentColor" style="display:block;width:100%;height:100%;">
<path d="m12,0C5.383,0,0,5.383,0,12s5.383,12,12,12,12-5.383,12-12S18.617,0,12,0Zm-.091,15.419c-.387.387-.896.58-1.407.58s-1.025-.195-1.416-.585l-2.782-2.696,1.393-1.437,2.793,2.707,5.809-5.701,1.404,1.425-5.793,5.707Z"/>
</svg>
</span>
</button>
@endforeach
</div>
</div>
@endif
{{-- Quick amounts --}}
<div>
<p class="mb-2.5 text-[10px] font-bold uppercase tracking-widest" style="color:{{ $evColor }}">Quick amount</p>
<div style="display:grid; grid-auto-flow:column; grid-auto-columns:calc((100% - 20px) / 2.5); gap:10px; overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:2px;">
@foreach([50, 100, 200, 500, 1000] as $qa)
<button type="button"
@click="amount = '{{ $qa }}'"
:style="amount == '{{ $qa }}'
? 'scroll-snap-align:start;border-radius:16px;border:1.5px solid {{ $evColor }};padding:14px 10px;font-size:0.95rem;font-weight:800;text-align:center;white-space:nowrap;background:{{ $evColor }};color:#fff;box-shadow:0 4px 12px {{ $evColor }}40;'
: 'scroll-snap-align:start;border-radius:16px;border:1.5px solid #e2e8f0;padding:14px 10px;font-size:0.95rem;font-weight:700;text-align:center;white-space:nowrap;background:#fff;color:#475569;'">
{{ $evCurrency }} {{ $qa }}
</button>
@endforeach
</div>
</div>
{{-- Custom amount --}}
<div>
<p class="mb-2.5 text-[10px] font-bold uppercase tracking-widest" style="color:{{ $evColor }}">Or enter amount</p>
<div style="display:flex; align-items:center; border-radius:16px; border:1.5px solid #e2e8f0; background:#f8fafc; padding:12px 16px; box-sizing:border-box; width:100%;">
<span style="flex-shrink:0; font-size:0.875rem; font-weight:700; color:#94a3b8; margin-right:8px;">{{ $evCurrency }}</span>
<input type="number" x-model="amount" inputmode="decimal" min="1" step="0.01" placeholder="0.00"
style="flex:1; min-width:0; width:0; background:transparent; border:none; outline:none; font-size:1.75rem; font-weight:800; color:#0f172a; font-family:inherit;"
@focus="$el.parentElement.style.borderColor='{{ $evColor }}'; $el.parentElement.style.boxShadow='0 0 0 3px {{ $evColor }}22'"
@blur="$el.parentElement.style.borderColor='#e2e8f0'; $el.parentElement.style.boxShadow='none'">
</div>
</div>
@endif
{{-- Attendee details --}}
<div class="space-y-2.5">
<p class="text-[10px] font-bold uppercase tracking-widest" style="color:{{ $evColor }}">Your details</p>
<input type="text" x-model="name" placeholder="Full name *" autocomplete="name"
class="w-full rounded-xl border border-slate-200 px-4 py-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-1">
<input type="email" x-model="email" placeholder="Email address *" autocomplete="email"
class="w-full rounded-xl border border-slate-200 px-4 py-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-1">
<input type="tel" x-model="phone" placeholder="Phone number *" autocomplete="tel"
class="w-full rounded-xl border border-slate-200 px-4 py-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-1">
@foreach($evFields as $field)
<input type="text" x-model="fields[@js($field)]" placeholder="{{ $field }}"
class="w-full rounded-xl border border-slate-200 px-4 py-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-1">
@endforeach
</div>
<div x-show="errorMsg" x-cloak class="rounded-xl border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-600" x-text="errorMsg"></div>
<button type="button" @click="submit()" :disabled="loading"
class="flex w-full items-center justify-center gap-2 rounded-2xl py-4 text-base font-bold text-white transition disabled:opacity-60 active:scale-[0.98]"
style="background:{{ $evColor }}; box-shadow:0 8px 24px {{ $evColor }}44;">
<svg x-show="loading" x-cloak class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg>
<span x-text="loading ? 'Processing…' : (mode === 'contributions' ? ('Contribute ' + @js($evCurrency) + ' ' + ((parseFloat(amount)||0).toFixed(2))) : (tierPrice > 0 ? ('Pay ' + @js($evCurrency) + ' ' + tierPrice.toFixed(2)) : 'Register free'))">{{ $evHeading }}</span>
</button>
<p class="text-center text-[11px] text-slate-400">{{ $evMode === 'contributions' ? 'Your contribution is safe & secure.' : 'A badge will be issued on registration.' }}</p>
</div>
</div>
@endif
</div>
@include('partials.paystack-sheet')
</div>
{{-- ===== ITINERARY ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_ITINERARY)
@php
$itinColor = $content['brand_color'] ?? '#b45309';
$itinHasCover = !empty($content['cover_path']);
$itinCoverUrl = $itinHasCover ? $qrCode->publicPath('itinerary-cover') : null;
$itinTitle = $content['title'] ?? $qrCode->label;
$itinDays = $content['days'] ?? [];
$multiDay = count($itinDays) > 1;
@endphp
<div class="min-h-screen bg-slate-50 pb-12">
{{-- Hero --}}
<div class="relative overflow-hidden" style="min-height:240px;">
@if($itinHasCover)
<img src="{{ $itinCoverUrl }}" alt="{{ $itinTitle }}" class="absolute inset-0 h-full w-full object-cover">
<div class="absolute inset-0" style="background:linear-gradient(to bottom, rgba(0,0,0,0.25) 0%, rgba(0,0,0,0.7) 100%);"></div>
@else
<div class="absolute inset-0" style="background:linear-gradient(150deg, {{ $itinColor }} 0%, {{ $itinColor }}cc 100%);"></div>
<div class="absolute inset-0 opacity-10" style="background-image:radial-gradient(circle at 20% 30%, white 1px, transparent 1px); background-size:34px 34px;"></div>
@endif
<div class="relative flex min-h-[240px] flex-col items-center justify-end px-6 pb-7 text-center text-white">
<h1 class="text-3xl font-black leading-tight drop-shadow">{{ $itinTitle }}</h1>
@if(!empty($content['subtitle']))
<p class="mt-1.5 text-sm font-medium text-white/85">{{ $content['subtitle'] }}</p>
@endif
<div class="mt-3 flex flex-wrap items-center justify-center gap-2 text-xs">
@if(!empty($content['event_date']))
<span class="inline-flex items-center gap-1.5 rounded-full bg-white/20 px-3 py-1 font-semibold backdrop-blur-sm">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"/></svg>
{{ \App\Support\Qr\QrDateFormatter::forDisplay($content['event_date']) }}
</span>
@endif
@if(!empty($content['location']))
<span class="inline-flex items-center gap-1.5 rounded-full bg-white/20 px-3 py-1 font-semibold backdrop-blur-sm">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"/></svg>
{{ $content['location'] }}
</span>
@endif
</div>
</div>
</div>
<div class="mx-auto max-w-xl px-4">
@if(!empty($content['description']))
<p class="mt-6 text-center text-sm leading-relaxed text-slate-600">{{ $content['description'] }}</p>
@endif
{{-- Programme --}}
<div class="mt-6 space-y-7" x-data="{ active: 0 }">
@if($multiDay)
<div class="flex gap-2 overflow-x-auto pb-1" style="scrollbar-width:none;">
@foreach($itinDays as $di => $day)
<button type="button" @click="active = {{ $di }}"
:style="active === {{ $di }} ? 'background:{{ $itinColor }};color:#fff;' : 'background:#fff;color:#475569;'"
class="shrink-0 rounded-full border border-slate-200 px-4 py-1.5 text-xs font-bold transition">
{{ $day['label'] ?: ('Day ' . ($di + 1)) }}
</button>
@endforeach
</div>
@endif
@foreach($itinDays as $di => $day)
<div {!! $multiDay ? 'x-show="active === ' . $di . '"' : '' !!}>
<div class="mb-3 flex items-baseline justify-between">
<h2 class="text-base font-bold text-slate-900">{{ $day['label'] ?: ('Day ' . ($di + 1)) }}</h2>
@if(!empty($day['date']))<span class="text-xs font-medium text-slate-400">{{ \App\Support\Qr\QrDateFormatter::forDisplay($day['date']) }}</span>@endif
</div>
<div class="relative space-y-0">
@foreach($day['items'] ?? [] as $ii => $item)
<div class="relative flex gap-3.5 pb-5 last:pb-0">
{{-- Timeline rail --}}
<div class="flex flex-col items-center">
<span class="mt-1 flex h-3 w-3 shrink-0 rounded-full ring-4 ring-white" style="background:{{ $itinColor }};"></span>
@if(! $loop->last)<span class="w-px flex-1" style="background:{{ $itinColor }}33;"></span>@endif
</div>
<div class="flex-1 -mt-0.5 pb-1">
@if(!empty($item['time']))
<p class="text-xs font-bold" style="color:{{ $itinColor }};">{{ $item['time'] }}</p>
@endif
<p class="text-sm font-semibold text-slate-900 leading-snug">{{ $item['title'] }}</p>
@if(!empty($item['host']) || !empty($item['location']))
<p class="mt-0.5 text-xs text-slate-500">
{{ $item['host'] ?? '' }}{{ !empty($item['host']) && !empty($item['location']) ? ' · ' : '' }}{{ $item['location'] ?? '' }}
</p>
@endif
@if(!empty($item['description']))
<p class="mt-1 text-xs leading-relaxed text-slate-500">{{ $item['description'] }}</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
{{-- ===== WIFI ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_WIFI)
<div class="flex min-h-screen flex-col items-center justify-center px-4 py-12">
<div class="w-full max-w-sm">
<div class="flex flex-col items-center text-center">
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-blue-100">
<svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.288 15.038a5.25 5.25 0 0 1 7.424 0M5.106 11.856c3.807-3.808 9.98-3.808 13.788 0M1.924 8.674c5.565-5.565 14.587-5.565 20.152 0M12.53 18.22l-.53.53-.53-.53a.75.75 0 0 1 1.06 0Z"/>
</svg>
</div>
<h1 class="mt-5 text-2xl font-bold text-slate-900">Join WiFi</h1>
<p class="mt-1 text-sm text-slate-500">{{ $qrCode->label }}</p>
</div>
<div class="mt-8 space-y-3">
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
<div class="px-5 py-4">
<p class="text-xs font-semibold uppercase tracking-widest text-slate-400">Network</p>
<p class="mt-1 text-lg font-bold text-slate-900">{{ $content['ssid'] ?? '' }}</p>
</div>
@if(($content['encryption'] ?? 'WPA') !== 'NOPASS' && !empty($content['password']))
<div class="border-t border-slate-100 px-5 py-4">
<p class="text-xs font-semibold uppercase tracking-widest text-slate-400">Password</p>
<div class="mt-1 flex items-center justify-between gap-3">
<p class="font-mono text-lg font-semibold text-slate-900" id="wifi-pw">{{ $content['password'] }}</p>
<button onclick="navigator.clipboard?.writeText('{{ addslashes($content['password']) }}')"
class="shrink-0 rounded-xl border border-slate-200 px-3 py-1.5 text-xs font-semibold text-slate-600 transition hover:bg-slate-50">Copy</button>
</div>
</div>
@endif
<div class="border-t border-slate-100 px-5 py-3">
<span class="inline-flex items-center gap-1.5 rounded-full bg-slate-100 px-2.5 py-1 text-xs font-medium text-slate-600">
{{ $content['encryption'] ?? 'WPA' }}
{{ !empty($content['hidden']) ? ' · Hidden' : '' }}
</span>
</div>
</div>
</div>
</div>
</div>
{{-- ===== IMAGES ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_IMAGE)
<div class="mx-auto max-w-lg px-4 py-10">
<h1 class="text-2xl font-bold text-slate-900">{{ $qrCode->label }}</h1>
<div class="mt-6 space-y-4">
@foreach($content['images'] ?? [] as $index => $image)
<img src="{{ $qrCode->publicPath('images/'.$index) }}"
alt="{{ $image['title'] ?? 'Image ' . ($index + 1) }}"
class="w-full rounded-2xl border border-slate-200 shadow-sm"
loading="lazy">
@endforeach
</div>
</div>
{{-- ===== APP ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_APP)
@php $appIconUrl = !empty($content['icon_path']) ? $qrCode->publicPath('app-icon') : null; @endphp
<div class="flex min-h-screen flex-col items-center justify-center px-4 py-12">
<div class="w-full max-w-sm text-center">
@if($appIconUrl)
<img src="{{ $appIconUrl }}" alt="{{ $content['name'] ?? $qrCode->label }}"
class="public-brand-logo mx-auto h-20 w-20 max-w-[50vw] rounded-[22px] object-cover shadow-lg">
@else
<div class="mx-auto flex h-20 w-20 items-center justify-center rounded-[22px] bg-gradient-to-br from-indigo-500 to-violet-600 shadow-lg">
<svg class="h-10 w-10 text-white" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 1.5H8.25A2.25 2.25 0 0 0 6 3.75v16.5a2.25 2.25 0 0 0 2.25 2.25h7.5A2.25 2.25 0 0 0 18 20.25V3.75a2.25 2.25 0 0 0-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3"/>
</svg>
</div>
@endif
<h1 class="mt-6 text-3xl font-bold text-slate-900">{{ $content['name'] ?? $qrCode->label }}</h1>
<div class="mt-8 space-y-3 text-left">
@if(!empty($content['ios_url']))
<a href="{{ $content['ios_url'] }}" target="_blank" rel="noopener"
class="flex items-center gap-4 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm transition hover:border-slate-300 hover:shadow-md">
<svg class="h-8 w-8 shrink-0 text-slate-900" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="M19.149,0.001H4.847C2.169,0.001,0,2.17,0,4.849v14.305c0,2.676,2.169,4.845,4.847,4.845h14.305c2.676,0,4.847-2.169,4.847-4.847V4.849C23.997,2.17,21.828,0.001,19.149,0.001z M6.635,19.001c-0.3,0.525-0.969,0.702-1.494,0.402c-0.525-0.3-0.702-0.969-0.402-1.494l0.78-1.35c0.882-0.273,1.598-0.063,2.166,0.621L6.635,19.001z M14.218,16.056H4.29c-0.606,0-1.092-0.486-1.092-1.092c0-0.606,0.486-1.092,1.092-1.092h2.783l3.564-6.176L9.524,5.764c-0.3-0.525-0.123-1.188,0.402-1.494c0.525-0.3,1.188-0.123,1.494,0.402l0.477,0.843l0.486-0.84c0.3-0.525,0.969-0.702,1.494-0.402c0.525,0.3,0.702,0.969,0.402,1.493l-4.682,8.105h3.387C14.08,13.872,14.695,15.162,14.218,16.056z M19.567,16.062h-1.578l1.065,1.847c0.3,0.525,0.123,1.188-0.402,1.494c-0.525,0.3-1.188,0.123-1.494-0.402c-1.793-3.111-3.141-5.438-4.034-6.989c-0.915-1.578-0.261-3.162,0.384-3.699c0.717,1.23,1.788,3.087,3.218,5.565h2.841c0.606,0,1.092,0.486,1.092,1.092C20.658,15.576,20.173,16.062,19.567,16.062z"/>
</g>
</svg>
<div>
<p class="text-xs text-slate-500">Download on the</p>
<p class="font-bold text-slate-900">App Store</p>
</div>
</a>
@endif
@if(!empty($content['android_url']))
<a href="{{ $content['android_url'] }}" target="_blank" rel="noopener"
class="flex items-center gap-4 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm transition hover:border-slate-300 hover:shadow-md">
<svg class="h-8 w-8 shrink-0 text-slate-900" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="m12,0C5.373,0,0,5.373,0,12s5.373,12,12,12,12-5.373,12-12S18.627,0,12,0Zm-2.877,5.22l7.3,4.084-2.047,2.047-6.315-6.315c.359-.085.742-.023,1.061.184Zm-2.123,12.403V6.376c0-.304.101-.592.278-.826l6.45,6.45-6.45,6.45c-.176-.234-.278-.522-.278-.826Zm2.123,1.156c-.227.147-.486.221-.747.221-.105,0-.211-.013-.314-.038l6.314-6.314,2.047,2.047-7.3,4.084Zm9.971-5.579l-1.84,1.029-2.23-2.23,2.23-2.23,1.839,1.029c.441.247.704.696.704,1.2s-.263.954-.704,1.201Z"/>
</svg>
<div>
<p class="text-xs text-slate-500">Get it on</p>
<p class="font-bold text-slate-900">Google Play</p>
</div>
</a>
@endif
@if(!empty($content['web_url']))
<a href="{{ $content['web_url'] }}" target="_blank" rel="noopener"
class="flex items-center gap-4 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm transition hover:border-slate-300 hover:shadow-md">
<svg class="h-8 w-8 shrink-0 text-slate-500" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253M3 12a8.96 8.96 0 0 0 .735 3.547"/></svg>
<div>
<p class="text-xs text-slate-500">Open</p>
<p class="font-bold text-slate-900">Website</p>
</div>
</a>
@endif
</div>
</div>
</div>
{{-- ===== COUPON ===== --}}
@elseif($type === \App\Models\QrCode::TYPE_COUPON)
<div class="flex min-h-screen flex-col items-center justify-center px-4 py-12">
<div class="w-full max-w-sm">
<div class="overflow-hidden rounded-3xl bg-white shadow-xl">
<div class="bg-gradient-to-br from-amber-400 to-orange-500 px-6 py-10 text-center text-white">
<svg class="mx-auto h-10 w-10" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 14.25l6-6m4.5-3.493V21.75l-3.75-1.5-3.75 1.5-3.75-1.5-3.75 1.5V4.757c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0c1.1.128 1.907 1.077 1.907 2.185ZM9.75 9h.008v.008H9.75V9Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm4.125 4.5h.008v.008h-.008V13.5Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"/>
</svg>
<h1 class="mt-4 text-xl font-bold">{{ $content['title'] ?? $qrCode->label }}</h1>
</div>
<div class="px-6 py-8 text-center">
<div class="rounded-2xl border-2 border-dashed border-orange-200 bg-orange-50 px-6 py-5">
<p class="text-xs font-semibold uppercase tracking-widest text-orange-500">Your code</p>
<p class="mt-2 text-3xl font-black tracking-[0.2em] text-orange-600">{{ $content['code'] ?? '' }}</p>
</div>
@if(!empty($content['description']))<p class="mt-5 text-sm text-slate-600">{{ $content['description'] }}</p>@endif
@if(!empty($content['expires_at']))<p class="mt-3 text-xs text-slate-400">Expires {{ $content['expires_at'] }}</p>@endif
@if(!empty($content['button_url']))
<a href="{{ $content['button_url'] }}" target="_blank" rel="noopener"
class="mt-6 flex items-center justify-center gap-2 rounded-2xl bg-orange-500 px-6 py-3.5 text-sm font-semibold text-white shadow-sm transition hover:bg-orange-600">
{{ $content['button_text'] ?? 'Shop now' }}
</a>
@endif
</div>
</div>
</div>
</div>
{{-- ===== MENU & SHOP (shared cart/order UI) ===== --}}
@elseif(in_array($type, [\App\Models\QrCode::TYPE_MENU, \App\Models\QrCode::TYPE_SHOP]))
@php
$isShop = $type === \App\Models\QrCode::TYPE_SHOP;
$currency = $content['currency'] ?? 'GHS';
$sections = $content['sections'] ?? [];
$title = $content['title'] ?? $qrCode->label;
$acceptsPayment = (bool) ($content['accepts_payment'] ?? false);
$orderRoute = $qrCode->publicPath('order');
$csrfToken = csrf_token();
$paystackPublicKey = $acceptsPayment ? app(\App\Services\Billing\PaystackService::class)->publicKey() : '';
$brandColor = $content['brand_color'] ?? ($isShop ? '#7c3aed' : '#f97316');
$hasLogo = !empty($content['logo_path']);
$hasCover = !empty($content['cover_path']);
$logoUrl = $hasLogo ? $qrCode->publicPath('menu-logo') : null;
$coverUrl = $hasCover ? $qrCode->publicPath('menu-cover') : null;
$shippingType = $content['shipping_type'] ?? 'none';
$shippingFee = (float) ($content['shipping_fee'] ?? 0);
$freeShippingAbove = (float) ($content['free_shipping_above'] ?? 0);
$itemCount = array_sum(array_map(fn($s) => count($s['items'] ?? []), $sections));
@endphp
<div x-data="{
cart: {},
name: '',
email: '',
phone: '',
showModal: false,
activeSection: 0,
loading: false,
errorMsg: '',
showSheet: false,
checkoutUrl: '',
shippingType: @js($shippingType),
shippingFee: @js($shippingFee),
freeAbove: @js($freeShippingAbove),
get cartItems() {
const items = [];
Object.entries(this.cart).forEach(([key, qty]) => {
if (qty > 0) {
const [si, ii] = key.split('-').map(Number);
const sections = @js($sections);
const item = sections[si]?.items[ii];
if (item) items.push({ key, si, ii, qty, name: item.name, price: item.price, priceNum: parseFloat(item.price) || 0 });
}
});
return items;
},
get cartCount() { return this.cartItems.reduce((s, i) => s + i.qty, 0) },
get cartTotal() { return this.cartItems.reduce((s, i) => s + i.qty * i.priceNum, 0) },
get shippingCost() {
if (this.shippingType !== 'flat' || this.shippingFee <= 0) return 0;
if (this.freeAbove > 0 && this.cartTotal >= this.freeAbove) return 0;
return this.shippingFee;
},
get orderTotal() { return this.cartTotal + this.shippingCost },
add(si, ii) { const k = si+'-'+ii; this.cart[k] = (this.cart[k] || 0) + 1 },
remove(si, ii) { const k = si+'-'+ii; if ((this.cart[k]||0) > 0) this.cart[k]-- },
qty(si, ii) { return this.cart[si+'-'+ii] || 0 },
async submitOrder() {
if (this.cartCount === 0) { this.errorMsg = 'Add at least one item.'; return }
if (!this.name.trim()) { this.errorMsg = 'Enter your name.'; return }
if (!this.phone.trim()) { this.errorMsg = 'Enter your phone number.'; return }
this.errorMsg = '';
this.loading = true;
const payload = {
customer_name: this.name,
customer_email: this.email,
customer_phone: this.phone,
shipping_fee: this.shippingCost,
items: this.cartItems.map(i => ({ name: i.name, price: i.priceNum, qty: i.qty })),
};
try {
const res = await fetch(@js($orderRoute), {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': @js($csrfToken) },
body: JSON.stringify(payload),
});
const data = await res.json();
if (data.error) { this.errorMsg = data.error; this.loading = false; return; }
this.checkoutUrl = data.checkout_url;
this.showSheet = true;
this.loading = false;
} catch(e) {
this.errorMsg = 'Network error. Please try again.';
this.loading = false;
}
}
}" class="min-h-screen bg-slate-50">
{{-- ===== HERO ===== --}}
<div class="relative overflow-hidden" style="min-height: 220px;">
@if($hasCover)
<img src="{{ $coverUrl }}" alt="{{ $title }}" class="absolute inset-0 h-full w-full object-cover">
@endif
{{-- gradient overlay always present --}}
<div class="absolute inset-0" style="background: linear-gradient(to bottom, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.62) 100%); {{ !$hasCover ? 'background: linear-gradient(135deg, ' . $brandColor . 'ee 0%, ' . $brandColor . '99 100%);' : '' }}"></div>
@if(!$hasCover)
<div class="absolute inset-0 opacity-10" style="background-image: radial-gradient(circle at 20% 50%, white 1px, transparent 1px), radial-gradient(circle at 80% 20%, white 1px, transparent 1px); background-size: 40px 40px;"></div>
@endif
{{-- Hero content --}}
<div class="relative flex flex-col justify-end px-5 pb-6 pt-12" style="min-height: 220px;">
<div class="flex items-end gap-4">
@if($hasLogo)
<img src="{{ $logoUrl }}" alt="{{ $title }}"
class="public-brand-logo h-16 w-16 max-w-[50vw] flex-shrink-0 rounded-2xl object-cover shadow-lg ring-2 ring-white/30">
@else
<div class="flex h-16 w-16 flex-shrink-0 items-center justify-center rounded-2xl shadow-lg ring-2 ring-white/30 text-2xl font-black text-white"
style="background: rgba(255,255,255,0.2); backdrop-filter: blur(8px);">
{{ strtoupper(substr($title, 0, 1)) }}
</div>
@endif
<div class="min-w-0 flex-1 pb-0.5">
<h1 class="text-2xl font-black text-white leading-tight drop-shadow">{{ $title }}</h1>
<div class="mt-1 flex flex-wrap items-center gap-2">
<span class="inline-flex items-center gap-1 rounded-full bg-white/20 px-2.5 py-0.5 text-[11px] font-semibold text-white backdrop-blur-sm">
{{ $isShop ? 'Shop' : 'Restaurant' }}
</span>
<span class="inline-flex items-center gap-1 rounded-full bg-white/20 px-2.5 py-0.5 text-[11px] font-semibold text-white backdrop-blur-sm">
{{ $itemCount }} {{ $isShop ? ($itemCount === 1 ? 'product' : 'products') : ($itemCount === 1 ? 'item' : 'items') }}
</span>
@if($acceptsPayment)
<span class="inline-flex items-center gap-1 rounded-full bg-green-500/80 px-2.5 py-0.5 text-[11px] font-semibold text-white backdrop-blur-sm">
<span class="h-1.5 w-1.5 rounded-full bg-white"></span> Order online
</span>
@endif
</div>
</div>
</div>
</div>
</div>
{{-- ===== SECTION TABS ===== --}}
@if(count($sections) > 1)
<div class="sticky top-0 z-20 bg-white shadow-sm">
<div class="flex gap-0 overflow-x-auto scrollbar-none px-4">
@foreach($sections as $si => $section)
<button @click="activeSection = {{ $si }}"
class="shrink-0 border-b-2 px-4 py-3.5 text-sm font-semibold whitespace-nowrap transition-colors"
:class="activeSection === {{ $si }} ? 'border-current text-current' : 'border-transparent text-slate-400 hover:text-slate-700'"
:style="activeSection === {{ $si }} ? 'color: {{ $brandColor }}; border-color: {{ $brandColor }}' : ''">
{{ $section['name'] }}
</button>
@endforeach
</div>
</div>
@endif
{{-- ===== ITEMS ===== --}}
<div class="mx-auto max-w-xl px-4 pb-36 pt-5 space-y-8">
@foreach($sections as $si => $section)
<div x-show="{{ count($sections) > 1 ? 'activeSection === ' . $si : 'true' }}">
@if(count($sections) === 1)
<h2 class="mb-4 text-xs font-bold uppercase tracking-widest text-slate-400">{{ $section['name'] }}</h2>
@endif
<div class="space-y-3">
@foreach($section['items'] ?? [] as $ii => $item)
@php $hasImage = !empty($item['image_path']); @endphp
<div class="flex gap-3 overflow-hidden rounded-2xl bg-white shadow-sm ring-1 ring-slate-100">
{{-- Item image --}}
@if($hasImage)
<img src="{{ $qrCode->publicPath('item-image/'.$si.'/'.$ii) }}"
alt="{{ $item['name'] }}"
class="h-[100px] w-[100px] flex-shrink-0 object-cover" loading="lazy">
@endif
{{-- Item details --}}
<div class="flex flex-1 min-w-0 flex-col justify-between py-3 pr-3 {{ $hasImage ? '' : 'pl-4' }}">
<div>
<p class="font-semibold text-slate-900 leading-snug">{{ $item['name'] }}</p>
@if(!empty($item['description']))
<p class="mt-0.5 text-xs text-slate-500 leading-relaxed line-clamp-2">{{ $item['description'] }}</p>
@endif
</div>
<div class="mt-2 flex items-center justify-between gap-2">
@if(!empty($item['price']))
<div class="flex items-baseline gap-0.5">
<span class="text-[10px] font-medium text-slate-400">{{ $currency }}</span>
<span class="text-base font-bold text-slate-900">{{ $item['price'] }}</span>
</div>
@else
<span class="text-xs font-medium text-slate-400"></span>
@endif
@if($acceptsPayment)
<div class="flex items-center gap-1.5">
<button @click="remove({{ $si }}, {{ $ii }})"
x-show="qty({{ $si }}, {{ $ii }}) > 0" x-cloak
class="flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 hover:border-slate-300 transition shadow-sm">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14"/></svg>
</button>
<span x-show="qty({{ $si }}, {{ $ii }}) > 0" x-cloak
x-text="qty({{ $si }}, {{ $ii }})"
class="min-w-[1.5rem] text-center text-sm font-bold text-slate-900"></span>
<button @click="add({{ $si }}, {{ $ii }})"
class="flex h-8 w-8 items-center justify-center rounded-xl text-white shadow-md transition active:scale-95"
style="background: {{ $brandColor }}">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
</button>
</div>
@endif
</div>
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
@if($acceptsPayment)
{{-- ===== STICKY CART BAR ===== --}}
<div x-show="cartCount > 0" x-cloak
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="translate-y-full opacity-0"
x-transition:enter-end="translate-y-0 opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="translate-y-0 opacity-100"
x-transition:leave-end="translate-y-full opacity-0"
class="fixed bottom-0 inset-x-0 z-30 px-4 pb-6 pt-2"
style="padding-bottom: max(1.5rem, env(safe-area-inset-bottom))">
<button @click="showModal = true"
class="flex w-full max-w-xl mx-auto items-center gap-3 rounded-2xl px-5 py-4 text-white shadow-2xl transition active:scale-[0.98]"
style="background: {{ $brandColor }}">
<span class="flex h-7 w-7 items-center justify-center rounded-xl bg-white/20 text-sm font-bold tabular-nums" x-text="cartCount"></span>
<span class="flex-1 text-left text-sm font-bold">View order</span>
<span class="text-sm font-semibold tabular-nums opacity-90" x-text="'{{ $currency }} ' + orderTotal.toFixed(2)"></span>
<svg class="h-4 w-4 opacity-80" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3"/>
</svg>
</button>
</div>
{{-- ===== PAYMENT MODAL (bottom sheet) ===== --}}
<div x-show="showModal" x-cloak
class="fixed inset-0 z-50 flex items-end justify-center"
style="background: rgba(0,0,0,0.5); backdrop-filter: blur(3px);"
x-transition:enter="transition-opacity ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition-opacity ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0">
<div @click.outside="if(!loading) showModal = false"
class="w-full max-w-lg overflow-hidden rounded-t-3xl bg-white shadow-2xl"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="translate-y-full"
x-transition:enter-end="translate-y-0"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="translate-y-0"
x-transition:leave-end="translate-y-full"
style="max-height: 90vh; overflow-y: auto; padding-bottom: env(safe-area-inset-bottom, 0px)">
{{-- Drag handle --}}
<div class="flex justify-center pt-3 pb-2">
<div class="h-1 w-10 rounded-full bg-slate-200"></div>
</div>
{{-- Header --}}
<div class="flex items-center justify-between border-b border-slate-100 px-5 pb-4 pt-2">
<h3 class="text-lg font-bold text-slate-900">Your order</h3>
<button @click="if(!loading) showModal = false"
class="flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 transition">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
</button>
</div>
<div class="px-5 pb-6 pt-4 space-y-5">
{{-- Cart items --}}
<div class="space-y-2">
<template x-for="item in cartItems" :key="item.key">
<div class="flex items-center gap-3">
<div class="flex items-center gap-1.5">
<button @click="remove(item.si, item.ii)"
class="flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200 text-slate-500 hover:border-slate-300 transition">
<svg class="h-3 w-3" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14"/></svg>
</button>
<span class="min-w-[1.5rem] text-center text-sm font-bold text-slate-900 tabular-nums" x-text="item.qty"></span>
<button @click="add(item.si, item.ii)"
class="flex h-7 w-7 items-center justify-center rounded-lg text-white transition"
style="background: {{ $brandColor }}">
<svg class="h-3 w-3" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
</button>
</div>
<span class="flex-1 text-sm text-slate-700 leading-snug" x-text="item.name"></span>
<span class="text-sm font-semibold text-slate-900 tabular-nums"
x-text="item.priceNum > 0 ? '{{ $currency }} ' + (item.qty * item.priceNum).toFixed(2) : 'Free'"></span>
</div>
</template>
</div>
{{-- Totals --}}
<div class="rounded-2xl bg-slate-50 px-4 py-3 space-y-2">
<div class="flex justify-between text-sm text-slate-600">
<span>Subtotal</span>
<span class="tabular-nums" x-text="'{{ $currency }} ' + cartTotal.toFixed(2)"></span>
</div>
<template x-if="shippingCost > 0">
<div class="flex justify-between text-sm text-slate-600">
<span>{{ $isShop ? 'Shipping' : 'Delivery' }}</span>
<span class="tabular-nums" x-text="'{{ $currency }} ' + shippingCost.toFixed(2)"></span>
</div>
</template>
<template x-if="shippingType === 'flat' && shippingFee > 0 && freeAbove > 0 && shippingCost === 0">
<div class="flex justify-between text-sm text-green-600">
<span>{{ $isShop ? 'Shipping' : 'Delivery' }}</span>
<span>Free</span>
</div>
</template>
<div class="flex justify-between border-t border-slate-200 pt-2 text-base font-bold text-slate-900">
<span>Total</span>
<span class="tabular-nums" x-text="'{{ $currency }} ' + orderTotal.toFixed(2)"></span>
</div>
</div>
{{-- Contact fields --}}
<div class="space-y-2.5">
<p class="text-xs font-semibold uppercase tracking-widest text-slate-400">Your details</p>
<input type="text" x-model="name" placeholder="Full name *"
class="w-full rounded-xl border border-slate-200 px-4 py-3 text-sm placeholder:text-slate-400 focus:border-indigo-400 focus:outline-none focus:ring-1 focus:ring-indigo-400/30">
<input type="tel" x-model="phone" placeholder="Phone number *"
class="w-full rounded-xl border border-slate-200 px-4 py-3 text-sm placeholder:text-slate-400 focus:border-indigo-400 focus:outline-none focus:ring-1 focus:ring-indigo-400/30">
</div>
<div x-show="errorMsg" x-cloak
class="rounded-xl border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-600"
x-text="errorMsg"></div>
<button @click="submitOrder" :disabled="loading"
class="flex w-full items-center justify-center gap-2 rounded-2xl py-4 text-sm font-bold text-white shadow-lg transition disabled:opacity-60 active:scale-[0.98]"
style="background: {{ $brandColor }}">
<svg x-show="loading" x-cloak class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
<span x-text="loading ? 'Opening payment…' : ('Pay {{ $currency }} ' + orderTotal.toFixed(2))"></span>
</button>
<p class="text-center text-[11px] text-slate-400">Secured by Paystack. Powered by Ladill Pay</p>
</div>
</div>
</div>
@endif
@if($acceptsPayment)
@include('partials.paystack-sheet')
@endif
</div>{{-- end x-data --}}
@endif
<script src="//cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
</body>
</html>