Files
KiraTV---Aplicacion-para-Wi…/apps/website/src/components/FeatureGrid.astro
T
2026-05-03 23:36:48 +02:00

224 lines
12 KiB
Plaintext

---
const heroFeatures = [
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />',
title: 'M3U / M3U8 Playlists',
description: 'Import playlists from files or URLs with full M3U and M3U8 support.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />',
title: 'Xtream Codes API',
description: 'Connect to Xtream Codes providers with live TV, VOD, and series.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />',
title: 'Electronic Program Guide',
description: 'XMLTV-based EPG with current and upcoming program information.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /><path stroke-linecap="round" stroke-linejoin="round" d="M12 18v3" />',
title: 'Cross-Platform',
description: 'Runs on macOS, Windows, Linux as a desktop app or PWA in any browser.',
},
];
const carouselFeatures = [
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.858 15.355-5.858 21.213 0" />',
title: 'Stalker Portals',
description: 'Browse and stream content from Stalker/Ministra portal providers.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />',
title: 'Favorites & History',
description: 'Mark channels as favorites and keep track of recently watched.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />',
title: 'Search & Filter',
description: 'Quickly find channels across playlists with full-text search.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />',
title: 'Multi-Language',
description: 'Available in 16 languages with community-contributed translations.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />',
title: 'External Players',
description: 'Open streams in MPV, VLC, or other external players from the app.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />',
title: 'Light & Dark Themes',
description: 'Choose between light and dark themes for comfortable viewing.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />',
title: 'Remote Control',
description: 'Control the player from your phone with the built-in remote.',
},
{
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />',
title: 'Privacy First',
description: 'No tracking, no analytics. Your data stays on your device.',
},
];
---
<section id="features" class="relative py-28">
<hr class="section-divider mb-28" />
<div class="relative z-10 mx-auto max-w-6xl px-6">
<!-- Editorial header: left-aligned -->
<div class="max-w-xl">
<span class="font-mono text-xs uppercase tracking-[0.2em] text-accent-500">
Features
</span>
<h2 class="mt-4 font-display text-3xl font-bold tracking-tight text-surface-50 sm:text-4xl lg:text-5xl">
Everything <em class="font-medium text-accent-400" style="font-style: italic;">you need</em>
</h2>
<p class="mt-4 text-lg text-surface-400">
A full-featured IPTV player packed with the tools for the best viewing experience.
</p>
</div>
<!-- Hero feature cards: 2x2 grid -->
<div class="stagger-children mt-16 grid grid-cols-1 gap-5 sm:grid-cols-2" data-stagger>
{
heroFeatures.map((feature, i) => (
<div class="feature-card group relative overflow-hidden rounded-lg border border-dashed border-surface-600/60 bg-gradient-to-br from-surface-900/80 via-surface-950/60 to-surface-900/40 p-7 transition-all duration-500 hover:border-accent-500/40 hover:shadow-[0_0_30px_rgba(32,168,168,0.08)]">
<!-- Background watermark icon — large, rotated, partially clipped -->
<div class={`feature-watermark pointer-events-none absolute -bottom-6 -right-4 opacity-[0.04] transition-all duration-700 ease-out group-hover:opacity-[0.1] ${i % 2 === 0 ? 'watermark-left' : 'watermark-right'}`}>
<svg class="h-36 w-36 text-accent-400 sm:h-44 sm:w-44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="0.7">
<Fragment set:html={feature.icon} />
</svg>
</div>
<!-- Watermark gradient mask — fades icon into card bg from top-left -->
<div class="pointer-events-none absolute inset-0 bg-gradient-to-br from-surface-900/95 via-surface-950/40 to-transparent" />
<!-- CRT scanline overlay on hover -->
<div class="pointer-events-none absolute inset-0 z-10 opacity-0 transition-opacity duration-500 group-hover:opacity-100" style="background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(32,168,168,0.015) 2px, rgba(32,168,168,0.015) 4px);" />
<!-- Card content -->
<div class="relative z-[1]">
<!-- Mono label + accent line -->
<div class="mb-5 flex items-center gap-3">
<span class="font-mono text-[10px] font-medium uppercase tracking-[0.2em] text-accent-500/70 transition-colors duration-300 group-hover:text-accent-400">0{i + 1}</span>
<span class="h-px flex-1 bg-gradient-to-r from-accent-500/20 to-transparent transition-colors duration-300 group-hover:from-accent-500/40" />
</div>
<h3 class="font-display text-xl font-bold tracking-tight text-surface-100 transition-colors duration-300 group-hover:text-surface-50 sm:text-2xl">
{feature.title}
</h3>
<p class="mt-2.5 max-w-[85%] text-sm leading-relaxed text-surface-500 transition-colors duration-300 group-hover:text-surface-400">
{feature.description}
</p>
</div>
</div>
))
}
</div>
<!-- Sub-header between hero and carousel -->
<div class="mt-16">
<span class="font-mono text-xs uppercase tracking-[0.15em] text-surface-500">
And more
</span>
</div>
</div>
<!-- Carousel: full-width infinite marquee -->
<div class="relative mt-6 overflow-hidden opacity-0" data-carousel>
<!-- Gradient masks: left and right fade -->
<div class="pointer-events-none absolute inset-y-0 left-0 z-10 w-24 bg-gradient-to-r from-surface-950 to-transparent sm:w-32" />
<div class="pointer-events-none absolute inset-y-0 right-0 z-10 w-24 bg-gradient-to-l from-surface-950 to-transparent sm:w-32" />
<!-- Marquee track -->
<div class="carousel-track flex w-max gap-4 py-4" aria-label="Additional features">
<!-- First set of cards -->
{
carouselFeatures.map((feature, i) => (
<div class="group relative w-64 shrink-0 overflow-hidden rounded-lg border border-dashed border-surface-600/60 bg-gradient-to-b from-surface-900/60 to-surface-950/40 p-5 transition-all duration-500 hover:border-accent-500/40 hover:shadow-[0_0_24px_rgba(32,168,168,0.06)]">
<div class="relative mb-3.5 inline-block">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-accent-500/[0.08] ring-1 ring-accent-500/20 transition-all duration-500 group-hover:bg-accent-500/[0.14] group-hover:ring-accent-500/35 group-hover:shadow-[0_0_14px_rgba(32,168,168,0.12)]">
<svg class="h-5 w-5 text-accent-400 transition-all duration-500 group-hover:text-accent-300 group-hover:drop-shadow-[0_0_4px_rgba(32,168,168,0.4)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<Fragment set:html={feature.icon} />
</svg>
</div>
<div class="absolute -right-0.5 -top-0.5 h-1 w-1 rounded-full bg-accent-500/50 transition-colors duration-300 group-hover:bg-accent-400" />
</div>
<h3 class="font-display text-sm font-semibold tracking-tight text-surface-100 transition-colors duration-300 group-hover:text-surface-50">{feature.title}</h3>
<p class="mt-1.5 text-xs leading-relaxed text-surface-500 transition-colors duration-300 group-hover:text-surface-400">
{feature.description}
</p>
</div>
))
}
<!-- Duplicate set for seamless loop -->
{
carouselFeatures.map((feature, i) => (
<div class="group relative w-64 shrink-0 overflow-hidden rounded-lg border border-dashed border-surface-600/60 bg-gradient-to-b from-surface-900/60 to-surface-950/40 p-5 transition-all duration-500 hover:border-accent-500/40 hover:shadow-[0_0_24px_rgba(32,168,168,0.06)]" aria-hidden="true">
<div class="relative mb-3.5 inline-block">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-accent-500/[0.08] ring-1 ring-accent-500/20 transition-all duration-500 group-hover:bg-accent-500/[0.14] group-hover:ring-accent-500/35 group-hover:shadow-[0_0_14px_rgba(32,168,168,0.12)]">
<svg class="h-5 w-5 text-accent-400 transition-all duration-500 group-hover:text-accent-300 group-hover:drop-shadow-[0_0_4px_rgba(32,168,168,0.4)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<Fragment set:html={feature.icon} />
</svg>
</div>
<div class="absolute -right-0.5 -top-0.5 h-1 w-1 rounded-full bg-accent-500/50 transition-colors duration-300 group-hover:bg-accent-400" />
</div>
<h3 class="font-display text-sm font-semibold tracking-tight text-surface-100 transition-colors duration-300 group-hover:text-surface-50">{feature.title}</h3>
<p class="mt-1.5 text-xs leading-relaxed text-surface-500 transition-colors duration-300 group-hover:text-surface-400">
{feature.description}
</p>
</div>
))
}
</div>
</div>
</section>
<script>
// Stagger animation for hero grid on scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('stagger-visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('[data-stagger]').forEach((el) => observer.observe(el));
// Carousel fade-in on scroll
const carouselEl = document.querySelector('[data-carousel]');
if (carouselEl) {
const co = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.remove('opacity-0');
e.target.classList.add('animate-fade-in');
co.unobserve(e.target);
}
});
}, { threshold: 0.05 });
co.observe(carouselEl);
}
</script>
<style>
.watermark-left {
transform: rotate(-12deg) scale(1);
}
.watermark-right {
transform: rotate(12deg) scale(1);
}
.feature-card:hover .watermark-left {
transform: rotate(-10deg) scale(1.12);
}
.feature-card:hover .watermark-right {
transform: rotate(10deg) scale(1.12);
}
</style>