Files
iptvnator/apps/website/src/components/feature/FeatureHero.astro
T
4grayandClaude Fable 5.1 1cf17f7301 style(website): solid hairlines, Bricolage Grotesque display face, drop TV effects (#1539)
* style(website): solid hairlines, Bricolage Grotesque display face, drop TV effects

First of three landing redesign PRs. This one only touches tokens and
surface treatment so every page (home, blog, /features, /compare,
/download) benefits without any structural change:

- Replace every dashed border with a solid hairline; `.card-dotted`
  becomes `.card-panel` (bg step + hairline, lighter hairline on hover)
  and the section divider is solid `surface-800`.
- Swap the display face from Playfair Display to Bricolage Grotesque
  (DM Sans body and IBM Plex Mono unchanged). The italic accent word
  that every heading repeated is gone; only page-level h1s keep the
  accent, and as colour rather than italic.
- Remove the CRT/TV effects: the `.btn-tv` block and its nine keyframes,
  the scanline overlay on the hero screenshot, the CRT hover overlay and
  teal glow shadows on feature cards, the pulsing mascot, and the unused
  scan/tv-* Tailwind animations.
- The hero download button and the header Download link are now solid
  accent buttons; the self-host link is a plain text link.
- Blog tag chips use neutral surface borders so teal stays reserved for
  actions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* style(website): finish the dashed sweep in prose and package rows

The blanket `border-dashed ` removal ate the utility but not the
variant prefix in the blog prose classes, leaving
`prose-pre:prose-pre:...` and `prose-hr:prose-hr:...`; and the
Linux package rows still used `divide-dashed`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 12:11:56 +02:00

102 lines
4.8 KiB
Plaintext

---
import type { FeatureEntry } from '../../lib/features';
import { FEATURES_HUB_HREF } from '../../lib/features';
interface Screenshot {
src: string;
alt: string;
width: number;
height: number;
}
interface Props {
feature: FeatureEntry;
titleLead: string;
titleAccent: string;
lede: string;
facts: string[];
screenshot: Screenshot;
}
const { feature, titleLead, titleAccent, lede, facts, screenshot } = Astro.props;
---
<section class="relative overflow-hidden pt-36 pb-20 sm:pt-40">
<div class="pointer-events-none absolute top-0 left-1/2 h-[520px] w-[900px] -translate-x-1/2 -translate-y-1/3 rounded-full bg-accent-500/[0.06] blur-[120px]"></div>
<div class="relative mx-auto max-w-6xl px-6">
<nav aria-label="Breadcrumb" class="font-mono text-[11px] uppercase tracking-[0.15em] text-surface-500">
<ol class="flex flex-wrap items-center gap-2">
<li><a href="/iptvnator/" class="transition-colors hover:text-accent-400">IPTVnator</a></li>
<li aria-hidden="true">/</li>
<li><a href={FEATURES_HUB_HREF} class="transition-colors hover:text-accent-400">Features</a></li>
<li aria-hidden="true">/</li>
<li class="text-surface-300" aria-current="page">{feature.label}</li>
</ol>
</nav>
<div class="mt-10 grid items-center gap-14 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)]">
<div>
<div class="inline-flex items-center gap-3">
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-accent-500/[0.08] ring-1 ring-accent-500/20">
<svg class="h-5 w-5 text-accent-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.6" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d={feature.icon} />
</svg>
</span>
<span class="font-mono text-xs uppercase tracking-[0.2em] text-accent-500">{feature.eyebrow}</span>
</div>
<h1 class="mt-6 font-display text-4xl font-bold tracking-tight text-surface-50 sm:text-5xl lg:text-6xl">
{titleLead}{' '}
<span class="text-accent-400">{titleAccent}</span>
</h1>
<p class="mt-6 max-w-xl text-lg leading-relaxed text-surface-400">{lede}</p>
<div class="mt-10 flex flex-wrap gap-4">
<a
href="/iptvnator/download/"
class="inline-flex items-center gap-3 rounded-xl bg-accent-500 px-6 py-4 text-base font-semibold text-surface-950 shadow-[0_0_30px_rgba(32,168,168,0.25)] transition-all duration-300 hover:-translate-y-0.5 hover:bg-accent-400 hover:shadow-[0_0_44px_rgba(32,168,168,0.4)]"
>
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
Download IPTVnator
</a>
<a
href={feature.guide.href}
class="inline-flex items-center gap-3 rounded-xl border border-surface-600/60 bg-surface-900/50 px-6 py-4 text-base font-semibold text-surface-100 transition-all duration-300 hover:border-accent-500/40 hover:bg-accent-500/[0.05]"
>
<svg class="h-5 w-5 text-accent-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.7" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.253v13m0-13C10.832 5.483 9.246 5 7.5 5 5.205 5 3.182 5.836 2 7.117v11.41C3.182 17.247 5.205 16.41 7.5 16.41c1.746 0 3.332.484 4.5 1.254m0-11.41C13.168 5.483 14.754 5 16.5 5c2.295 0 4.318.836 5.5 2.117v11.41C20.818 17.247 18.795 16.41 16.5 16.41c-1.746 0-3.332.484-4.5 1.254" />
</svg>
{feature.guide.label}
</a>
</div>
<ul class="mt-8 flex flex-wrap gap-2" aria-label="Key facts">
{facts.map((fact) => (
<li class="rounded-full border border-surface-700/70 px-3 py-1 font-mono text-[10px] uppercase tracking-[0.12em] text-surface-400">{fact}</li>
))}
</ul>
</div>
<figure class="relative">
<div class="pointer-events-none absolute -inset-6 rounded-3xl bg-accent-500/[0.05] blur-2xl"></div>
<div class="relative overflow-hidden rounded-xl border border-surface-600/60 bg-surface-900/60 p-2 shadow-[0_30px_80px_rgba(0,0,0,0.45)]">
<img
src={screenshot.src}
alt={screenshot.alt}
width={screenshot.width}
height={screenshot.height}
class="h-auto w-full rounded-lg"
loading="eager"
decoding="async"
/>
</div>
<figcaption class="mt-3 text-center font-mono text-[10px] uppercase tracking-[0.15em] text-surface-600">{screenshot.alt}</figcaption>
</figure>
</div>
</div>
</section>