Tune it live. Every preset is pure CSS variables.
"Default" follows the body font.
Copy your complete theme and paste it as your resources/css/app.css — the Tailwind import, every token and the @theme mapping are all included, so it works as-is.
A vibrant retro-editorial festival poster — spinning vinyl, line-up with bios, a now-playing player, schedule, tickets and a photo gallery.
@php
/* ─────────────────────────────────────────────────────
| WAVELENGTH 2026 — Music Festival template
| Retro-editorial poster aesthetic: deep indigo base,
| magenta→orange→yellow gradients, cyan accent.
───────────────────────────────────────────────────── */
$artists = [
['name' => 'SOLARA', 'genre' => 'Electronic', 'time' => '22:00', 'day' => 1, 'seed' => 'solara1', 'bio' => 'Solara blends hyper-textured bass with ethereal synths, forging a sound that feels both ancient and alien.'],
['name' => 'KRYPT', 'genre' => 'Techno', 'time' => '00:30', 'day' => 1, 'seed' => 'krypt22', 'bio' => "Berlin-trained selector and producer, Krypt's sets unfold like brutalist architecture — relentless and hypnotic."],
['name' => 'NOVA VEGA', 'genre' => 'Indie Pop', 'time' => '20:00', 'day' => 2, 'seed' => 'novavega3', 'bio' => 'Nova Vega writes love songs for collapsing stars. Four acclaimed albums and a sold-out world tour in 2025.'],
['name' => 'THRENODY', 'genre' => 'Post-Rock', 'time' => '21:30', 'day' => 2, 'seed' => 'threnody4', 'bio' => 'Cinematic post-rock from Dublin. Guitars, cello, and found sounds woven into forty-minute soundscapes.'],
['name' => 'PULSE UNIT', 'genre' => 'Synth-Pop', 'time' => '19:00', 'day' => 1, 'seed' => 'pulseunit5', 'bio' => 'Pulse Unit resurrects the best of 80s synth-pop and electrifies it with modern production muscle.'],
['name' => 'MARIGOLD', 'genre' => 'R&B / Soul', 'time' => '21:00', 'day' => 3, 'seed' => 'marigold6', 'bio' => "Marigold's voice is raw honey. Her debut album topped charts in 27 countries; the follow-up drops June 2026."],
['name' => 'CIRCUIT BOY', 'genre' => 'Hyperpop', 'time' => '23:00', 'day' => 3, 'seed' => 'circuitboy7', 'bio' => 'Chaotic, glitchy, irresistible — Circuit Boy is the act everyone talks about the morning after.'],
['name' => 'SERAPHINE', 'genre' => 'Dream Pop', 'time' => '18:30', 'day' => 2, 'seed' => 'seraphine8', 'bio' => "Seraphine's hazy, reverb-soaked dream pop is the perfect opener for a festival sunset."],
];
$schedule = [
1 => [
['time' => '16:00', 'act' => 'DOORS OPEN', 'stage' => '—', 'genre' => ''],
['time' => '17:00', 'act' => 'DJ WARMUP', 'stage' => 'Stage B', 'genre' => 'House'],
['time' => '19:00', 'act' => 'PULSE UNIT', 'stage' => 'Main Stage', 'genre' => 'Synth-Pop'],
['time' => '21:00', 'act' => 'SOLARA', 'stage' => 'Main Stage', 'genre' => 'Electronic'],
['time' => '23:00', 'act' => 'KRYPT', 'stage' => 'Stage B', 'genre' => 'Techno'],
],
2 => [
['time' => '16:00', 'act' => 'DOORS OPEN', 'stage' => '—', 'genre' => ''],
['time' => '17:30', 'act' => 'OPEN DECKS', 'stage' => 'Stage B', 'genre' => 'Various'],
['time' => '18:30', 'act' => 'SERAPHINE', 'stage' => 'Main Stage', 'genre' => 'Dream Pop'],
['time' => '20:00', 'act' => 'NOVA VEGA', 'stage' => 'Main Stage', 'genre' => 'Indie Pop'],
['time' => '21:30', 'act' => 'THRENODY', 'stage' => 'Stage B', 'genre' => 'Post-Rock'],
],
3 => [
['time' => '16:00', 'act' => 'DOORS OPEN', 'stage' => '—', 'genre' => ''],
['time' => '18:00', 'act' => 'RISING ACTS', 'stage' => 'Stage B', 'genre' => 'Various'],
['time' => '21:00', 'act' => 'MARIGOLD', 'stage' => 'Main Stage', 'genre' => 'R&B / Soul'],
['time' => '23:00', 'act' => 'CIRCUIT BOY', 'stage' => 'Stage B', 'genre' => 'Hyperpop'],
['time' => '00:30', 'act' => 'CLOSING SET', 'stage' => 'Main Stage', 'genre' => 'Electronic'],
],
];
$tickets = [
['tier' => 'General Admission', 'price' => '€89', 'badge' => 'Available', 'badge_tone' => 'success', 'rating' => 4, 'perks' => ['3-day festival access', 'Camping included', 'Late-night zones', 'Food-market access'], 'highlight' => false],
['tier' => 'VIP', 'price' => '€219', 'badge' => 'Popular', 'badge_tone' => null, 'rating' => 5, 'perks' => ['GA + VIP fast-track entry', 'Private viewing platform', 'Artist meet & greet ballot', 'Lounge bar & concierge'], 'highlight' => true],
['tier' => 'Backstage Pass', 'price' => '€499', 'badge' => 'Limited', 'badge_tone' => 'warning', 'rating' => 5, 'perks' => ['Everything in VIP', 'Backstage access all 3 days', 'Exclusive merchandise box', 'Post-show dinner with artists'], 'highlight' => false],
];
$faqs = [
['q' => 'Where is Wavelength 2026 held?', 'a' => 'The festival takes place at Parc des Ondes, Brussels — accessible by metro, train and dedicated shuttle buses from Brussels Central.'],
['q' => 'Is camping included in the ticket?', 'a' => 'Yes — all General Admission and VIP tickets include camping. Upgrade to a glamping pod for an extra fee at checkout.'],
['q' => 'Can I bring my own food and drinks?', 'a' => 'Sealed water bottles are welcome. Outside alcohol is not permitted, but our food-market and zero-waste bars cover every taste.'],
['q' => 'Are tickets transferable?', 'a' => 'GA and VIP tickets can be transferred once via your account dashboard up to 7 days before the event. Backstage Passes are non-transferable.'],
['q' => 'What is the minimum age?', 'a' => 'All ages welcome until 22:00. Late-night zones (Stage B after midnight) are 18+ with valid ID.'],
];
$gallery = [
['src' => 'https://picsum.photos/seed/wl-g1/900/600', 'thumb' => 'https://picsum.photos/seed/wl-g1/400/400', 'alt' => 'Crowd at night stage'],
['src' => 'https://picsum.photos/seed/wl-g2/900/600', 'thumb' => 'https://picsum.photos/seed/wl-g2/400/400', 'alt' => 'DJ performing under lasers'],
['src' => 'https://picsum.photos/seed/wl-g3/900/600', 'thumb' => 'https://picsum.photos/seed/wl-g3/400/400', 'alt' => 'Festival crowd from above'],
['src' => 'https://picsum.photos/seed/wl-g4/900/600', 'thumb' => 'https://picsum.photos/seed/wl-g4/400/400', 'alt' => 'Colourful light show on main stage'],
['src' => 'https://picsum.photos/seed/wl-g5/900/600', 'thumb' => 'https://picsum.photos/seed/wl-g5/400/400', 'alt' => 'Artist backstage moment'],
['src' => 'https://picsum.photos/seed/wl-g6/900/600', 'thumb' => 'https://picsum.photos/seed/wl-g6/400/400', 'alt' => 'Sunset over the festival grounds'],
];
$listeners = ['wl-l1', 'wl-l2', 'wl-l3', 'wl-l4', 'wl-l5'];
$ticker = ['WAVELENGTH 2026', 'BRUSSELS · JULY 18–20', 'SOLARA', 'KRYPT', 'NOVA VEGA', 'THRENODY', 'PULSE UNIT', 'MARIGOLD', 'CIRCUIT BOY', 'SERAPHINE', '60+ ARTISTS', '3 STAGES', '3 DAYS'];
@endphp
<x-layouts.app title="Wavelength — Music Festival 2026">
{{-- ═══════════════════════════════════════════════════════════
SONNER TOASTER (needed for "Added to queue" toast)
═══════════════════════════════════════════════════════════ --}}
<x-ui.sonner position="bottom-right" />
{{-- ═══════════════════════════════════════════════════════════
SCOPED STYLES
═══════════════════════════════════════════════════════════ --}}
<style>
@import url('https://fonts.bunny.net/css?family=space-grotesk:400,500,700,900|outfit:300,400,500,600&display=swap');
/* Reset site theme influence inside the template */
#tpl-vinyl,
#tpl-vinyl * {
--tw-ring-color: #ff2d95;
}
#tpl-vinyl {
--vl-base: #1b0a3b;
--vl-base2: #120726;
--vl-magenta: #ff2d95;
--vl-orange: #ff8a00;
--vl-yellow: #ffd23f;
--vl-cyan: #00e5ff;
--vl-cream: #fff3e6;
--vl-white: #ffffff;
--vl-purple: #7b2fff;
--vl-muted: rgba(255,243,230,0.55);
font-family: 'Outfit', sans-serif;
background: var(--vl-base);
color: var(--vl-cream);
}
/* Display headings use Space Grotesk */
#tpl-vinyl .vl-display {
font-family: 'Space Grotesk', sans-serif;
font-weight: 900;
}
/* Gradient text helper */
#tpl-vinyl .vl-grad-text {
background: linear-gradient(90deg, var(--vl-magenta) 0%, var(--vl-orange) 50%, var(--vl-yellow) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Diagonal gradient band */
#tpl-vinyl .vl-diagonal-band {
background: linear-gradient(135deg, var(--vl-magenta) 0%, var(--vl-orange) 55%, var(--vl-yellow) 100%);
}
/* Vinyl record */
#tpl-vinyl .vl-record {
width: 340px;
height: 340px;
border-radius: 50%;
background: radial-gradient(circle at 50%,
#1a1a1a 0 9%,
#2d2d2d 9% 11%,
#111 11% 18%,
#2a2a2a 18% 20%,
#111 20% 28%,
#2a2a2a 28% 30%,
#111 30% 39%,
#2a2a2a 39% 41%,
#111 41% 50%,
#1f1f1f 50% 100%
);
box-shadow:
0 0 0 3px #333,
0 0 40px rgba(255,45,149,0.45),
0 0 80px rgba(0,229,255,0.15);
animation: vl-spin 14s linear infinite;
flex-shrink: 0;
}
#tpl-vinyl .vl-record::after {
content: '';
position: absolute;
inset: 41%;
border-radius: 50%;
background: radial-gradient(circle, var(--vl-magenta) 0%, var(--vl-orange) 60%, var(--vl-yellow) 100%);
box-shadow: 0 0 12px rgba(255,45,149,0.8);
}
#tpl-vinyl .vl-record-wrap {
position: relative;
}
@keyframes vl-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Grain overlay */
#tpl-vinyl .vl-grain::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E");
background-size: 200px 200px;
pointer-events: none;
z-index: 1;
opacity: 0.35;
mix-blend-mode: overlay;
}
/* Glow hero title */
#tpl-vinyl .vl-hero-title {
text-shadow:
0 0 40px rgba(255,45,149,0.7),
0 0 80px rgba(255,138,0,0.4),
0 2px 0 rgba(0,0,0,0.5);
}
/* Marquee ticker */
#tpl-vinyl .vl-ticker {
background: linear-gradient(90deg, var(--vl-magenta) 0%, var(--vl-orange) 50%, var(--vl-yellow) 100%);
color: var(--vl-base);
}
/* Section divider */
#tpl-vinyl .vl-divider {
height: 4px;
background: linear-gradient(90deg, var(--vl-magenta), var(--vl-orange), var(--vl-yellow), var(--vl-cyan));
border: none;
}
/* Player widget */
#tpl-vinyl .vl-player {
background: linear-gradient(135deg, rgba(27,10,59,0.9) 0%, rgba(123,47,255,0.25) 100%);
border: 1px solid rgba(255,45,149,0.3);
backdrop-filter: blur(12px);
}
/* Cards with glow on hover */
#tpl-vinyl .vl-card {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,243,230,0.12);
transition: border-color .25s, box-shadow .25s, transform .2s;
}
#tpl-vinyl .vl-card:hover {
border-color: rgba(255,45,149,0.55);
box-shadow: 0 0 24px rgba(255,45,149,0.2);
transform: translateY(-2px);
}
/* Ticket highlight */
#tpl-vinyl .vl-ticket-highlight {
border: 2px solid var(--vl-magenta) !important;
box-shadow: 0 0 40px rgba(255,45,149,0.35);
background: rgba(255,45,149,0.08) !important;
}
/* Button overrides */
#tpl-vinyl .vl-btn-primary {
background: linear-gradient(90deg, var(--vl-magenta) 0%, var(--vl-orange) 100%);
color: var(--vl-base);
font-weight: 700;
font-family: 'Space Grotesk', sans-serif;
letter-spacing: 0.02em;
border: none;
transition: filter .2s, transform .15s;
}
#tpl-vinyl .vl-btn-primary:hover {
filter: brightness(1.12);
transform: translateY(-1px);
}
#tpl-vinyl .vl-btn-outline {
background: transparent;
border: 2px solid var(--vl-cyan);
color: var(--vl-cyan);
font-weight: 600;
font-family: 'Space Grotesk', sans-serif;
transition: background .2s, color .2s, transform .15s;
}
#tpl-vinyl .vl-btn-outline:hover {
background: var(--vl-cyan);
color: var(--vl-base);
transform: translateY(-1px);
}
/* Accordion overrides */
#tpl-vinyl [data-slot="accordion-item"] {
border-color: rgba(255,243,230,0.15);
}
#tpl-vinyl [data-slot="accordion-trigger"] {
color: var(--vl-cream);
}
#tpl-vinyl [data-slot="accordion-content"] {
color: var(--vl-muted);
}
/* Table */
#tpl-vinyl [data-slot="table"] th {
color: var(--vl-muted);
font-family: 'Space Grotesk', sans-serif;
font-size: 0.7rem;
letter-spacing: 0.1em;
text-transform: uppercase;
}
#tpl-vinyl [data-slot="table"] td {
color: var(--vl-cream);
}
#tpl-vinyl [data-slot="table-row"] {
border-color: rgba(255,243,230,0.08);
}
#tpl-vinyl [data-slot="table-row"]:hover {
background: rgba(255,45,149,0.06);
}
/* Tabs override */
#tpl-vinyl [data-slot="tabs-list"] {
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,243,230,0.12);
}
#tpl-vinyl [data-slot="tabs-trigger"] {
color: var(--vl-muted);
}
#tpl-vinyl [data-slot="tabs-trigger"][data-state="active"] {
background: linear-gradient(90deg, var(--vl-magenta), var(--vl-orange)) !important;
color: var(--vl-base) !important;
font-weight: 700;
border: none !important;
box-shadow: none !important;
}
/* Slider fill */
#tpl-vinyl [data-slot="slider-range"] {
background: linear-gradient(90deg, var(--vl-magenta), var(--vl-orange));
}
#tpl-vinyl [data-slot="slider-thumb"] {
border-color: var(--vl-magenta);
background: var(--vl-cream);
}
/* Progress fill */
#tpl-vinyl [data-slot="progress"] {
background: rgba(255,243,230,0.15);
}
#tpl-vinyl [data-slot="progress-indicator"] {
background: linear-gradient(90deg, var(--vl-magenta), var(--vl-cyan));
}
/* Input group */
#tpl-vinyl [data-slot="input-group"] {
background: rgba(255,255,255,0.06);
border-color: rgba(255,243,230,0.2);
}
#tpl-vinyl [data-slot="input-group-control"] {
color: var(--vl-cream);
}
#tpl-vinyl [data-slot="input-group-control"]::placeholder {
color: var(--vl-muted);
}
/* Separator */
#tpl-vinyl [data-slot="separator"] {
background: rgba(255,243,230,0.12);
}
/* Hover-card */
#tpl-vinyl [data-slot="hover-card-content"] {
background: #1b0a3b;
border: 1px solid rgba(255,45,149,0.35);
color: var(--vl-cream);
width: 220px;
}
/* Badge: override for custom look */
#tpl-vinyl .vl-badge-grad {
background: linear-gradient(90deg, var(--vl-magenta) 0%, var(--vl-orange) 100%);
color: var(--vl-base);
border: none;
font-family: 'Space Grotesk', sans-serif;
font-weight: 700;
letter-spacing: 0.04em;
}
/* Nav bar */
#tpl-vinyl .vl-nav {
background: rgba(27,10,59,0.85);
border-bottom: 1px solid rgba(255,45,149,0.25);
backdrop-filter: blur(16px);
}
/* Footer */
#tpl-vinyl .vl-footer {
background: var(--vl-base2);
border-top: 1px solid rgba(255,45,149,0.25);
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
#tpl-vinyl .vl-record { animation: none; }
}
/* Rating gold */
#tpl-vinyl [data-slot="rating"] { color: var(--vl-yellow); }
</style>
<div id="tpl-vinyl">
{{-- ───────────────────────────────────────────────────────
§ 0 · TOP MARQUEE TICKER (dates + cities)
─────────────────────────────────────────────────────────── --}}
<div class="vl-ticker overflow-hidden py-2.5" aria-label="Wavelength festival dates ticker">
<x-ui.marquee :duration="'28s'" :pause-on-hover="false" gap="0.5rem">
@foreach ($ticker as $item)
<span class="vl-display text-xs sm:text-sm font-bold tracking-widest uppercase px-4 whitespace-nowrap">{{ $item }}</span>
<span class="text-sm opacity-60 px-1" aria-hidden="true">✱</span>
@endforeach
</x-ui.marquee>
</div>
{{-- ───────────────────────────────────────────────────────
§ 1 · NAVIGATION
─────────────────────────────────────────────────────────── --}}
<header class="vl-nav sticky top-0 z-40">
<div class="mx-auto flex h-16 max-w-7xl items-center gap-4 px-6">
<a href="#" class="vl-display flex items-center gap-2 text-xl font-black tracking-tight text-white">
<span class="inline-flex size-8 items-center justify-center rounded-full" style="background: linear-gradient(135deg, #ff2d95, #ff8a00);" aria-hidden="true">
<x-lucide-music class="size-4 text-white" />
</span>
WAVELENGTH
</a>
<nav class="ml-6 hidden items-center gap-5 text-sm md:flex" aria-label="Primary navigation">
@foreach (['Line-Up' => '#lineup', 'Schedule' => '#schedule', 'Tickets' => '#tickets', 'Gallery' => '#gallery', 'FAQ' => '#faq'] as $label => $href)
<a href="{{ $href }}" class="font-medium tracking-wide transition-colors hover:text-white" style="color: var(--vl-muted)">{{ $label }}</a>
@endforeach
</nav>
<div class="ml-auto flex items-center gap-2">
<a href="#tickets" class="vl-btn-primary inline-flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm" role="button">
<x-lucide-ticket class="size-4" />
Get Tickets
</a>
</div>
</div>
</header>
{{-- ───────────────────────────────────────────────────────
§ 2 · HERO
─────────────────────────────────────────────────────────── --}}
<section class="vl-grain relative overflow-hidden" style="background: radial-gradient(ellipse at 30% 0%, rgba(255,45,149,0.22) 0%, transparent 55%), radial-gradient(ellipse at 80% 30%, rgba(0,229,255,0.12) 0%, transparent 50%), var(--vl-base);">
<div class="mx-auto grid max-w-7xl items-center gap-12 px-6 py-20 lg:grid-cols-2 lg:py-28">
{{-- Left: text --}}
<div class="relative z-10">
<div class="mb-5 flex flex-wrap items-center gap-2">
<x-ui.badge class="vl-badge-grad px-3 py-1 text-xs">3 DAYS</x-ui.badge>
<x-ui.badge class="vl-badge-grad px-3 py-1 text-xs">60+ ARTISTS</x-ui.badge>
<x-ui.badge class="vl-badge-grad px-3 py-1 text-xs">3 STAGES</x-ui.badge>
</div>
<h1 class="vl-display vl-hero-title text-6xl font-black leading-none tracking-tight text-white sm:text-7xl lg:text-8xl">
WAVE<br>
<span class="vl-grad-text">LENGTH</span>
</h1>
<p class="vl-display mt-3 text-2xl font-bold tracking-widest" style="color: var(--vl-cyan);">2026</p>
<div class="mt-5 flex flex-wrap items-center gap-3 text-sm font-medium" style="color: var(--vl-muted);">
<span class="flex items-center gap-1.5"><x-lucide-calendar class="size-4" style="color: var(--vl-yellow);" /> July 18 – 20, 2026</span>
<span aria-hidden="true">·</span>
<span class="flex items-center gap-1.5"><x-lucide-map-pin class="size-4" style="color: var(--vl-yellow);" /> Parc des Ondes, Brussels</span>
</div>
<p class="mt-5 max-w-md leading-relaxed" style="color: var(--vl-muted); font-size: 1.05rem;">
Three days of electronic music, indie anthems, post-rock landscapes and everything in between — under open skies on the outskirts of Brussels.
</p>
<div class="mt-8 flex flex-wrap gap-3">
<a href="#tickets" class="vl-btn-primary inline-flex items-center gap-2 rounded-xl px-7 py-3 text-base font-bold" role="button">
<x-lucide-ticket class="size-5" />
Buy Tickets
</a>
<a href="#lineup" class="vl-btn-outline inline-flex items-center gap-2 rounded-xl px-7 py-3 text-base" role="button">
<x-lucide-users class="size-5" />
See Line-Up
</a>
</div>
<div class="mt-8 flex items-center gap-3">
<div class="flex -space-x-2" aria-label="Festival attendees">
@foreach ($listeners as $seed)
<x-ui.avatar class="ring-2 size-9" style="--tw-ring-color: var(--vl-base);">
<x-ui.avatar-image src="https://picsum.photos/seed/{{ $seed }}/80/80" alt="Attendee" />
<x-ui.avatar-fallback style="background: var(--vl-purple); color: var(--vl-cream);">W</x-ui.avatar-fallback>
</x-ui.avatar>
@endforeach
</div>
<p class="text-sm" style="color: var(--vl-muted);">
<span class="font-bold text-white">12,400+</span> tickets already sold
</p>
</div>
</div>
{{-- Right: spinning vinyl --}}
<div class="flex items-center justify-center lg:justify-end">
<div class="vl-record-wrap relative flex items-center justify-center">
<div class="vl-record" role="img" aria-label="Vinyl record graphic"></div>
{{-- Glowing ring behind the record --}}
<div class="pointer-events-none absolute inset-0 rounded-full" style="box-shadow: 0 0 100px rgba(255,45,149,0.3), 0 0 200px rgba(0,229,255,0.1);"></div>
</div>
</div>
</div>
{{-- Diagonal colour stripe at bottom --}}
<div class="vl-diagonal-band h-2 w-full" aria-hidden="true"></div>
</section>
{{-- ───────────────────────────────────────────────────────
§ 3 · LINE-UP GRID (hover-cards for bios)
─────────────────────────────────────────────────────────── --}}
<section id="lineup" class="scroll-mt-20 py-20 lg:py-28" style="background: linear-gradient(180deg, var(--vl-base) 0%, rgba(18,7,38,1) 100%);">
<div class="mx-auto max-w-7xl px-6">
<div class="mb-12 text-center">
<x-ui.badge class="vl-badge-grad mb-4 px-3 py-1">LINE-UP</x-ui.badge>
<h2 class="vl-display text-4xl font-black text-white sm:text-5xl">The Artists</h2>
<p class="mt-3 text-sm sm:text-base" style="color: var(--vl-muted);">Hover an artist card to learn more. Three days, one lineup to remember.</p>
</div>
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
@foreach ($artists as $artist)
<x-ui.hover-card>
<x-ui.hover-card-trigger>
<div class="vl-card group cursor-pointer overflow-hidden rounded-2xl">
<div class="relative aspect-square overflow-hidden">
<img
src="https://picsum.photos/seed/{{ $artist['seed'] }}/400/400"
alt="{{ $artist['name'] }}"
loading="lazy"
class="size-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
{{-- Gradient overlay --}}
<div class="absolute inset-0" style="background: linear-gradient(to top, rgba(27,10,59,0.9) 0%, transparent 60%);" aria-hidden="true"></div>
{{-- Day badge --}}
<span class="absolute right-2 top-2">
<x-ui.badge class="vl-badge-grad text-xs px-2 py-0.5">Day {{ $artist['day'] }}</x-ui.badge>
</span>
{{-- Artist info at bottom --}}
<div class="absolute bottom-0 left-0 right-0 p-4">
<p class="vl-display text-lg font-black text-white leading-none">{{ $artist['name'] }}</p>
<p class="mt-1 text-xs font-medium tracking-wider uppercase" style="color: var(--vl-cyan);">{{ $artist['genre'] }}</p>
</div>
</div>
<div class="p-3 flex items-center justify-between">
<span class="text-xs font-semibold" style="color: var(--vl-muted);">
<x-lucide-clock class="inline size-3.5 mr-1 align-middle" aria-hidden="true" />{{ $artist['time'] }}
</span>
<x-ui.tooltip>
<x-ui.tooltip-trigger>
<x-lucide-info class="size-4" style="color: var(--vl-muted);" aria-label="Artist info" />
</x-ui.tooltip-trigger>
<x-ui.tooltip-content>{{ $artist['genre'] }} · Day {{ $artist['day'] }} @ {{ $artist['time'] }}</x-ui.tooltip-content>
</x-ui.tooltip>
</div>
</div>
</x-ui.hover-card-trigger>
<x-ui.hover-card-content side="top" align="center">
<p class="vl-display text-sm font-bold text-white">{{ $artist['name'] }}</p>
<p class="text-xs mb-2" style="color: var(--vl-cyan);">{{ $artist['genre'] }} · Day {{ $artist['day'] }} · {{ $artist['time'] }}</p>
<p class="text-xs leading-relaxed" style="color: var(--vl-muted);">{{ $artist['bio'] }}</p>
</x-ui.hover-card-content>
</x-ui.hover-card>
@endforeach
</div>
</div>
</section>
<hr class="vl-divider" aria-hidden="true" />
{{-- ───────────────────────────────────────────────────────
§ 4 · NOW PLAYING PLAYER WIDGET
─────────────────────────────────────────────────────────── --}}
<section class="py-16" style="background: var(--vl-base2);">
<div class="mx-auto max-w-4xl px-6">
<div class="mb-8 text-center">
<x-ui.badge class="vl-badge-grad mb-3 px-3 py-1">NOW PLAYING</x-ui.badge>
<h2 class="vl-display text-3xl font-black text-white sm:text-4xl">Festival Vibes</h2>
</div>
<div class="vl-player rounded-3xl p-6 sm:p-8"
x-data="{
playing: false,
progress: 34,
volume: 72,
trackIndex: 0,
tracks: [
{ title: 'Ultraviolet', artist: 'SOLARA', duration: '6:42' },
{ title: 'Concrete Jungle', artist: 'KRYPT', duration: '8:15' },
{ title: 'Open Skies', artist: 'NOVA VEGA', duration: '4:28' },
],
get track() { return this.tracks[this.trackIndex]; },
next() { this.trackIndex = (this.trackIndex + 1) % this.tracks.length; this.progress = 0; },
prev() { this.trackIndex = (this.trackIndex - 1 + this.tracks.length) % this.tracks.length; this.progress = 0; },
}">
<div class="grid gap-8 sm:grid-cols-[auto_1fr]">
{{-- Album art --}}
<div class="relative mx-auto sm:mx-0">
<div class="overflow-hidden rounded-2xl shadow-2xl" style="width: 140px; height: 140px; box-shadow: 0 0 40px rgba(255,45,149,0.4);">
<img src="https://picsum.photos/seed/wl-now/280/280" alt="Album art" class="size-full object-cover" />
</div>
{{-- Animated ring on playing --}}
<div x-show="playing" class="pointer-events-none absolute inset-0 rounded-2xl" style="box-shadow: 0 0 0 3px rgba(255,45,149,0.6), 0 0 30px rgba(255,45,149,0.3);" aria-hidden="true"></div>
</div>
{{-- Controls --}}
<div class="flex min-w-0 flex-col justify-between gap-4">
<div>
<p class="vl-display text-xl font-black text-white" x-text="track.title"></p>
<p class="text-sm font-medium mt-0.5" style="color: var(--vl-cyan);" x-text="track.artist"></p>
</div>
{{-- Seek slider --}}
<div>
<x-ui.slider :value="34" :min="0" :max="100" ariaLabel="Track position" x-model="progress" />
<div class="mt-1.5 flex justify-between text-xs tabular-nums" style="color: var(--vl-muted);">
<span x-text="Math.floor(progress * 0.0642) + ':' + String(Math.floor((progress * 0.0642 % 1) * 60)).padStart(2,'0')">2:18</span>
<span x-text="track.duration">6:42</span>
</div>
</div>
{{-- Playback buttons --}}
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-1">
<x-ui.button variant="ghost" size="icon-sm" @click="prev()" aria-label="Previous track" class="text-white/60 hover:text-white">
<x-lucide-skip-back class="size-5" />
</x-ui.button>
<button
type="button"
@click="playing = !playing"
:aria-label="playing ? 'Pause' : 'Play'"
class="vl-btn-primary inline-flex size-11 items-center justify-center rounded-full text-base"
>
<x-lucide-play class="size-5" x-show="!playing" aria-hidden="true" />
<x-lucide-pause class="size-5" x-show="playing" x-cloak aria-hidden="true" />
</button>
<x-ui.button variant="ghost" size="icon-sm" @click="next()" aria-label="Next track" class="text-white/60 hover:text-white">
<x-lucide-skip-forward class="size-5" />
</x-ui.button>
</div>
{{-- Volume --}}
<div class="hidden items-center gap-2 sm:flex">
<x-lucide-volume-2 class="size-4 shrink-0" style="color: var(--vl-muted);" aria-hidden="true" />
<div class="w-24">
<x-ui.slider :value="72" :min="0" :max="100" ariaLabel="Volume" x-model="volume" />
</div>
</div>
{{-- Add to queue toast trigger --}}
<x-ui.button
variant="ghost"
size="sm"
@click="window.toast({ type: 'success', title: 'Added to queue', description: track.title + ' by ' + track.artist })"
class="text-xs"
style="color: var(--vl-muted);"
aria-label="Add current track to queue"
>
<x-lucide-list-plus class="size-4" />
Queue
</x-ui.button>
</div>
{{-- Progress bar (overall festival stream progress) --}}
<div>
<p class="mb-1.5 text-xs font-medium" style="color: var(--vl-muted);">Stream popularity</p>
<x-ui.progress :value="78" ariaLabel="Track stream popularity 78%" />
</div>
</div>
</div>
{{-- Listeners --}}
<x-ui.separator class="my-5" />
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<div class="flex -space-x-1.5">
@foreach ($listeners as $seed)
<x-ui.avatar class="ring-1 size-6" style="--tw-ring-color: var(--vl-base2);">
<x-ui.avatar-image src="https://picsum.photos/seed/{{ $seed }}/48/48" alt="Listener" />
<x-ui.avatar-fallback style="background: var(--vl-purple); color: var(--vl-cream); font-size: 0.55rem;">W</x-ui.avatar-fallback>
</x-ui.avatar>
@endforeach
</div>
<p class="text-xs" style="color: var(--vl-muted);"><span class="font-semibold text-white">3,842</span> listening now</p>
</div>
<x-ui.badge variant="outline" class="border-current text-xs" style="color: var(--vl-cyan); border-color: var(--vl-cyan);">
<x-lucide-radio class="size-3 animate-pulse" aria-hidden="true" /> LIVE
</x-ui.badge>
</div>
</div>
</div>
</section>
<hr class="vl-divider" aria-hidden="true" />
{{-- ───────────────────────────────────────────────────────
§ 5 · SCHEDULE TABS → TABLE
─────────────────────────────────────────────────────────── --}}
<section id="schedule" class="scroll-mt-20 py-20 lg:py-28" style="background: var(--vl-base);">
<div class="mx-auto max-w-4xl px-6">
<div class="mb-10 text-center">
<x-ui.badge class="vl-badge-grad mb-4 px-3 py-1">SCHEDULE</x-ui.badge>
<h2 class="vl-display text-4xl font-black text-white sm:text-5xl">Set Times</h2>
<p class="mt-2 text-sm" style="color: var(--vl-muted);">All times CEST. Subject to change — check the app day-of.</p>
</div>
<x-ui.tabs value="day-1">
<x-ui.tabs-list class="mx-auto w-full max-w-xs grid grid-cols-3">
<x-ui.tabs-trigger value="day-1">Day 1</x-ui.tabs-trigger>
<x-ui.tabs-trigger value="day-2">Day 2</x-ui.tabs-trigger>
<x-ui.tabs-trigger value="day-3">Day 3</x-ui.tabs-trigger>
</x-ui.tabs-list>
@foreach ([1, 2, 3] as $day)
<x-ui.tabs-content value="day-{{ $day }}" class="mt-6">
<x-ui.table>
<x-ui.table-header>
<x-ui.table-row>
<x-ui.table-head>Time</x-ui.table-head>
<x-ui.table-head>Artist / Act</x-ui.table-head>
<x-ui.table-head>Stage</x-ui.table-head>
<x-ui.table-head>Genre</x-ui.table-head>
</x-ui.table-row>
</x-ui.table-header>
<x-ui.table-body>
@foreach ($schedule[$day] as $set)
<x-ui.table-row>
<x-ui.table-cell class="tabular-nums font-mono text-sm font-semibold" style="color: var(--vl-cyan);">{{ $set['time'] }}</x-ui.table-cell>
<x-ui.table-cell class="vl-display font-bold text-white">{{ $set['act'] }}</x-ui.table-cell>
<x-ui.table-cell style="color: var(--vl-muted); font-size: 0.85rem;">{{ $set['stage'] }}</x-ui.table-cell>
<x-ui.table-cell>
@if ($set['genre'])
<x-ui.badge variant="outline" class="text-xs border-white/20" style="color: var(--vl-muted);">{{ $set['genre'] }}</x-ui.badge>
@endif
</x-ui.table-cell>
</x-ui.table-row>
@endforeach
</x-ui.table-body>
</x-ui.table>
</x-ui.tabs-content>
@endforeach
</x-ui.tabs>
</div>
</section>
{{-- ───────────────────────────────────────────────────────
§ 6 · PHOTO GALLERY
─────────────────────────────────────────────────────────── --}}
<section id="gallery" class="scroll-mt-20 py-20 lg:py-28" style="background: var(--vl-base2);">
<div class="mx-auto max-w-6xl px-6">
<div class="mb-10 text-center">
<x-ui.badge class="vl-badge-grad mb-4 px-3 py-1">GALLERY</x-ui.badge>
<h2 class="vl-display text-4xl font-black text-white sm:text-5xl">Past Editions</h2>
<p class="mt-2 text-sm" style="color: var(--vl-muted);">Click any photo to open the full-screen lightbox.</p>
</div>
<x-ui.gallery :images="$gallery" :columns="3" rounded="rounded-xl" />
</div>
</section>
<hr class="vl-divider" aria-hidden="true" />
{{-- ───────────────────────────────────────────────────────
§ 7 · TICKETS
─────────────────────────────────────────────────────────── --}}
<section id="tickets" class="scroll-mt-20 py-20 lg:py-28" style="background: var(--vl-base);">
<div class="mx-auto max-w-6xl px-6">
<div class="mb-12 text-center">
<x-ui.badge class="vl-badge-grad mb-4 px-3 py-1">TICKETS</x-ui.badge>
<h2 class="vl-display text-4xl font-black text-white sm:text-5xl">Choose Your Experience</h2>
<p class="mt-3 text-sm sm:text-base" style="color: var(--vl-muted);">All tiers include 3-day festival access + camping. Prices include all fees.</p>
</div>
<div class="grid items-stretch gap-6 lg:grid-cols-3">
@foreach ($tickets as $ticket)
<div class="vl-card @if($ticket['highlight']) vl-ticket-highlight @endif relative flex flex-col rounded-2xl p-7">
@if ($ticket['highlight'])
<div class="absolute -top-3.5 left-1/2 -translate-x-1/2">
<x-ui.badge class="vl-badge-grad px-4 py-1 text-xs font-bold uppercase tracking-wider">Most Popular</x-ui.badge>
</div>
@endif
<div class="flex items-start justify-between gap-2">
<h3 class="vl-display text-xl font-bold text-white">{{ $ticket['tier'] }}</h3>
@if ($ticket['badge_tone'])
<x-ui.badge :tone="$ticket['badge_tone']" variant="soft" class="text-xs shrink-0">{{ $ticket['badge'] }}</x-ui.badge>
@else
<x-ui.badge class="vl-badge-grad text-xs shrink-0">{{ $ticket['badge'] }}</x-ui.badge>
@endif
</div>
<div class="mt-4 flex items-baseline gap-1">
<span class="vl-display text-5xl font-black" style="color: var(--vl-yellow);">{{ $ticket['price'] }}</span>
<span class="text-sm" style="color: var(--vl-muted);">/person</span>
</div>
{{-- Star rating --}}
<div class="mt-3">
<x-ui.rating :value="$ticket['rating']" :max="5" :readonly="true" color="text-yellow-400" size="sm" />
</div>
<x-ui.separator class="my-5" />
<ul class="mb-6 flex-1 space-y-2.5 text-sm" style="color: var(--vl-muted);">
@foreach ($ticket['perks'] as $perk)
<li class="flex items-center gap-2.5">
<x-lucide-check class="size-4 shrink-0" style="color: var(--vl-cyan);" aria-hidden="true" />
{{ $perk }}
</li>
@endforeach
</ul>
<a
href="#"
class="@if($ticket['highlight']) vl-btn-primary @else vl-btn-outline @endif inline-flex w-full items-center justify-center gap-2 rounded-xl py-3 text-sm font-bold"
role="button"
>
<x-lucide-ticket class="size-4" />
Buy {{ $ticket['tier'] }}
</a>
</div>
@endforeach
</div>
<p class="mt-8 text-center text-xs" style="color: var(--vl-muted);">
Secure checkout · Instant e-ticket delivery · 100% money-back guarantee if the event is cancelled
</p>
</div>
</section>
<hr class="vl-divider" aria-hidden="true" />
{{-- ───────────────────────────────────────────────────────
§ 8 · FAQ ACCORDION
─────────────────────────────────────────────────────────── --}}
<section id="faq" class="scroll-mt-20 py-20 lg:py-28" style="background: var(--vl-base2);">
<div class="mx-auto max-w-2xl px-6">
<div class="mb-10 text-center">
<x-ui.badge class="vl-badge-grad mb-4 px-3 py-1">FAQ</x-ui.badge>
<h2 class="vl-display text-4xl font-black text-white sm:text-5xl">Need to Know</h2>
</div>
<x-ui.accordion type="single" collapsible class="w-full">
@foreach ($faqs as $i => $faq)
<x-ui.accordion-item value="faq-{{ $i }}">
<x-ui.accordion-trigger>{{ $faq['q'] }}</x-ui.accordion-trigger>
<x-ui.accordion-content>{{ $faq['a'] }}</x-ui.accordion-content>
</x-ui.accordion-item>
@endforeach
</x-ui.accordion>
</div>
</section>
{{-- ───────────────────────────────────────────────────────
§ 9 · NEWSLETTER SIGNUP
─────────────────────────────────────────────────────────── --}}
<section class="vl-grain relative overflow-hidden py-20 lg:py-28" style="background: linear-gradient(135deg, rgba(255,45,149,0.18) 0%, rgba(27,10,59,1) 40%, rgba(0,229,255,0.1) 100%);">
<div class="relative z-10 mx-auto max-w-2xl px-6 text-center">
<x-ui.badge class="vl-badge-grad mb-4 px-3 py-1">STAY CONNECTED</x-ui.badge>
<h2 class="vl-display text-4xl font-black text-white sm:text-5xl">
Never Miss a Drop
</h2>
<p class="mt-3 text-sm sm:text-base" style="color: var(--vl-muted);">
Line-up updates, pre-sale codes, and last-minute ticket alerts — direct to your inbox.
</p>
<form
class="mt-8 flex w-full max-w-md mx-auto flex-col gap-2 sm:flex-row"
@submit.prevent="window.toast({ type: 'success', title: 'You\'re on the list!', description: 'We\'ll be in touch before tickets go on sale.' })"
>
<x-ui.input-group class="flex-1" style="background: rgba(255,255,255,0.07); border-color: rgba(255,243,230,0.2);">
<x-ui.input-group-input
type="email"
placeholder="[email protected]"
required
aria-label="Email address"
autocomplete="email"
/>
</x-ui.input-group>
<button type="submit" class="vl-btn-primary inline-flex items-center justify-center gap-2 rounded-xl px-6 py-2.5 text-sm font-bold whitespace-nowrap">
<x-lucide-bell class="size-4" />
Notify Me
</button>
</form>
<p class="mt-3 text-xs" style="color: var(--vl-muted);">No spam. Unsubscribe any time.</p>
</div>
</section>
{{-- ───────────────────────────────────────────────────────
§ 10 · FOOTER WITH BOTTOM MARQUEE
─────────────────────────────────────────────────────────── --}}
<footer class="vl-footer pt-14">
<div class="mx-auto max-w-6xl px-6 pb-10">
<div class="grid gap-10 sm:grid-cols-2 lg:grid-cols-4">
<div class="lg:col-span-2">
<a href="#" class="vl-display flex items-center gap-2 text-2xl font-black text-white">
<span class="inline-flex size-9 items-center justify-center rounded-full" style="background: linear-gradient(135deg, #ff2d95, #ff8a00);" aria-hidden="true">
<x-lucide-music class="size-4 text-white" />
</span>
WAVELENGTH
</a>
<p class="mt-3 max-w-xs text-sm leading-relaxed" style="color: var(--vl-muted);">
Three days. Three stages. One festival that redefines what music outdoors can be.
</p>
<div class="mt-5 flex items-center gap-2">
@foreach (['instagram', 'twitter', 'youtube', 'music'] as $social)
<a href="#" aria-label="{{ ucfirst($social) }}" class="inline-flex size-9 items-center justify-center rounded-full border transition-colors hover:border-white/40" style="border-color: rgba(255,243,230,0.15); color: var(--vl-muted);">
<x-dynamic-component :component="'lucide-'.$social" class="size-4" />
</a>
@endforeach
</div>
</div>
@foreach (['Festival' => ['Line-Up', 'Schedule', 'Stages', 'Artists', 'Gallery'], 'Info' => ['Tickets', 'Getting There', 'Camping', 'Accessibility', 'FAQ']] as $heading => $links)
<div>
<h3 class="vl-display text-xs font-bold uppercase tracking-widest" style="color: var(--vl-cyan);">{{ $heading }}</h3>
<ul class="mt-4 space-y-2 text-sm">
@foreach ($links as $link)
<li><a href="#" class="transition-colors hover:text-white" style="color: var(--vl-muted);">{{ $link }}</a></li>
@endforeach
</ul>
</div>
@endforeach
</div>
<x-ui.separator class="my-8" />
<div class="text-xs flex flex-col sm:flex-row items-center justify-between gap-3" style="color: var(--vl-muted);">
<span>© {{ date('Y') }} Wavelength Festival. All rights reserved.</span>
<div class="flex items-center gap-4">
<a href="#" class="hover:text-white transition-colors">Privacy</a>
<a href="#" class="hover:text-white transition-colors">Terms</a>
<a href="#" class="hover:text-white transition-colors">Cookies</a>
</div>
</div>
</div>
{{-- Bottom marquee --}}
<div class="vl-ticker overflow-hidden py-3" aria-hidden="true">
<x-ui.marquee :duration="'40s'" direction="right" :pause-on-hover="false" gap="0.75rem">
@foreach ($ticker as $item)
<span class="vl-display text-xs font-bold tracking-widest uppercase px-4 whitespace-nowrap">{{ $item }}</span>
<span class="opacity-50 px-1">●</span>
@endforeach
</x-ui.marquee>
</div>
</footer>
</div>
</x-layouts.app>