feat(ui): dashboard ribilanciata + trial banner d'impatto + masthead minimale

Tre fix di vestizione dopo il primo "sguardo dal vivo" della dashboard loggata:

1. **Masthead minimale**: rimosse "ANNO I · N. 01" e data corrente che davano un sapore "magazine vintage" stridente con la nuova typography Geist. Ora solo logo a sx + (opzionale) etichetta sezione a dx, divider sottile.

2. **TrialBanner d'impatto**: era un avviso sussurrato (bordo dashed oro su crema). Adesso card scura full-width con mesh-blob oro animato, numero giorni in mega 4xl/5xl con accento oro, CTA bottone solido oro→pomodoro al hover. Diventa il primo focus visivo della dashboard, coerente col fatto che è la conversion principale.

3. **Dashboard bilanciata**: hero "Piano della settimana" allargata da `md:col-span-6` a `md:col-span-7` + rotazione ridotta da `-rotate-1` a `-rotate-[0.5deg]` (meno dramma). La nav resta su 5 col da col-start-8, ma con un `pl-2` chiude lo spazio fra hero e prima voce, eliminando il "buco" centrale di aria. Rimossi anche chapter marker gigante "I" e vertical text "Continua la lettura" che rinforzavano il sapore magazine.

Lint/typecheck/build verdi in locale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucianoandClaude Opus 4.7 committed 2026-05-07 15:00:40 +02:00
1 parent 38f28e015b
commit 0722abc070
2 files changed
+57 -64

No files matched your search

+42 -41
View File
@@ -837,23 +837,6 @@ function SignedInDashboard({
className="mesh-blob mesh-blob--oro animate-float-z bottom-12 right-1/4 h-72 w-72 opacity-30" className="mesh-blob mesh-blob--oro animate-float-z bottom-12 right-1/4 h-72 w-72 opacity-30"
/> />
{/* Chapter marker gigante "I" come art di sfondo */}
<span
aria-hidden
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
>
I
</span>
{/* Eyebrow verticale a destra */}
<div
aria-hidden
className="pointer-events-none absolute right-0 top-32 hidden md:block"
style={{ writingMode: 'vertical-rl' }}
>
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">{t('lettura')}</p>
</div>
{/* Greeting bleed-left, sborda fuori dal container */} {/* Greeting bleed-left, sborda fuori dal container */}
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-12 font-medium [animation-delay:120ms]"> <h1 className="font-display text-mega bleed-left animate-fade-up relative mt-12 font-medium [animation-delay:120ms]">
<span className="text-ink block">Buongiorno,</span> <span className="text-ink block">Buongiorno,</span>
@@ -869,15 +852,10 @@ function SignedInDashboard({
<TrialBanner billing={billing} /> <TrialBanner billing={billing} />
{/* Nav asimmetrica: card grande "/plan" a sinistra, le altre 4 in colonna a destra */} {/* Nav asimmetrica bilanciata: card grande "/plan" 7 col a sx, lista 5 col a dx, gap chiuso */}
<div className="animate-fade-up relative mt-20 grid grid-cols-12 gap-6 [animation-delay:360ms]"> <div className="animate-fade-up relative mt-20 grid grid-cols-12 gap-6 [animation-delay:360ms]">
<DashboardHero <DashboardHero href="/plan" eyebrow="Cucina" title={t('viewPlan')} accent="oliva" />
href="/plan" <ul className="col-span-12 space-y-px md:col-span-5 md:col-start-8 md:pl-2">
eyebrow="Capitolo I · Cucina"
title={t('viewPlan')}
accent="oliva"
/>
<ul className="col-span-12 space-y-px md:col-span-5 md:col-start-8">
<NavItem href="/shopping" label={t('viewShopping')} eyebrow="Spesa" chapter="II" /> <NavItem href="/shopping" label={t('viewShopping')} eyebrow="Spesa" chapter="II" />
<NavItem href="/fasting" label={t('viewFasting')} eyebrow="Digiuno" chapter="III" /> <NavItem href="/fasting" label={t('viewFasting')} eyebrow="Digiuno" chapter="III" />
<NavItem href="/tracking" label={t('viewTracking')} eyebrow="Tracking" chapter="IV" /> <NavItem href="/tracking" label={t('viewTracking')} eyebrow="Tracking" chapter="IV" />
@@ -916,7 +894,7 @@ function DashboardHero({
return ( return (
<Link <Link
href={href} href={href}
className="hover-lift border-ink/15 group col-span-12 -rotate-1 overflow-hidden border md:col-span-6" className="hover-lift border-ink/15 group col-span-12 -rotate-[0.5deg] overflow-hidden border md:col-span-7"
style={{ background: tintGrad }} style={{ background: tintGrad }}
> >
<div <div
@@ -993,42 +971,65 @@ function TrialBanner({ billing }: { billing: BillingStatus | null }) {
if (derived.kind === 'trial' && derived.trialDaysRemaining != null) { if (derived.kind === 'trial' && derived.trialDaysRemaining != null) {
const endsAt = derived.accessEndsAt ? ITALIAN_DATE.format(new Date(derived.accessEndsAt)) : '—'; const endsAt = derived.accessEndsAt ? ITALIAN_DATE.format(new Date(derived.accessEndsAt)) : '—';
return ( return (
<aside className="border-oro/35 bg-oro/5 animate-fade-up relative mt-12 border-2 border-dashed p-6 [animation-delay:300ms] md:ml-[16.66%]"> <aside className="bg-ink animate-fade-up relative mt-12 overflow-hidden [animation-delay:300ms]">
<div className="flex flex-col gap-3 md:flex-row md:items-baseline md:justify-between"> <div
<div> aria-hidden
<p className="editorial-eyebrow">{t('trialBannerTitle')}</p> className="mesh-blob mesh-blob--oro animate-float-x -right-20 -top-20 h-72 w-72 opacity-60"
<p className="font-display text-ink mt-2 text-lg leading-snug"> />
{t('trialBannerBody', { days: derived.trialDaysRemaining, endsAt })} <div className="relative grid items-center gap-8 p-8 md:grid-cols-12 md:gap-12 md:p-12">
<div className="md:col-span-7">
<p className="editorial-eyebrow text-carta-light/60">{t('trialBannerTitle')}</p>
<p className="font-display text-carta mt-3 text-4xl font-medium leading-[1.05] tracking-tight md:text-5xl">
<span className="text-oro tabular-nums">{derived.trialDaysRemaining}</span>{' '}
{derived.trialDaysRemaining === 1 ? 'giorno' : 'giorni'}
<span className="text-pomodoro">.</span>
</p>
<p className="font-display text-carta/70 mt-3 max-w-md text-base italic leading-snug">
Tutto sbloccato fino al {endsAt}. Poi serve un abbonamento per generare nuovi piani.
</p> </p>
</div> </div>
<div className="md:col-span-5 md:text-right">
<Link <Link
href="/billing" href="/billing"
className="text-oro hover:text-pomodoro shrink-0 font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px]" className="bg-oro text-ink hover:bg-pomodoro hover:text-carta inline-flex items-center gap-3 px-7 py-4 font-mono text-[11px] font-medium uppercase tracking-[0.18em] transition-colors"
> >
{t('trialBannerCta')} → {t('trialBannerCta')}
<span aria-hidden>→</span>
</Link> </Link>
</div> </div>
</div>
</aside> </aside>
); );
} }
if (derived.kind === 'trial_expired' || derived.kind === 'past_due') { if (derived.kind === 'trial_expired' || derived.kind === 'past_due') {
return ( return (
<aside className="border-pomodoro/40 bg-pomodoro/5 animate-fade-up relative mt-12 border-2 border-dashed p-6 [animation-delay:300ms] md:ml-[16.66%]"> <aside className="bg-ink animate-fade-up relative mt-12 overflow-hidden [animation-delay:300ms]">
<div className="flex flex-col gap-3 md:flex-row md:items-baseline md:justify-between"> <div
<div> aria-hidden
<p className="editorial-eyebrow">{t('trialEndedBannerTitle')}</p> className="mesh-blob mesh-blob--pomodoro animate-float-x -right-20 -top-20 h-72 w-72 opacity-65"
<p className="font-display text-ink mt-2 text-lg leading-snug"> />
<div className="relative grid items-center gap-8 p-8 md:grid-cols-12 md:gap-12 md:p-12">
<div className="md:col-span-7">
<p className="editorial-eyebrow text-carta-light/60">{t('trialEndedBannerTitle')}</p>
<p className="font-display text-carta mt-3 text-4xl font-medium leading-[1.05] tracking-tight md:text-5xl">
{t('trialEndedBannerTitle')}
<span className="text-pomodoro">.</span>
</p>
<p className="font-display text-carta/70 mt-3 max-w-md text-base italic leading-snug">
{t('trialEndedBannerBody')} {t('trialEndedBannerBody')}
</p> </p>
</div> </div>
<div className="md:col-span-5 md:text-right">
<Link <Link
href="/billing" href="/billing"
className="text-pomodoro hover:text-ink shrink-0 font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px]" className="bg-pomodoro text-carta hover:bg-oro hover:text-ink inline-flex items-center gap-3 px-7 py-4 font-mono text-[11px] font-medium uppercase tracking-[0.18em] transition-colors"
> >
{t('trialEndedBannerCta')} → {t('trialEndedBannerCta')}
<span aria-hidden>→</span>
</Link> </Link>
</div> </div>
</div>
</aside> </aside>
); );
} }
+15 -23
View File
@@ -3,40 +3,32 @@ import Link from 'next/link';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
interface MastheadProps { interface MastheadProps {
/**
* Etichetta opzionale a destra (es. "N. 05 — Cucina"). Se omessa, l'header
* resta minimale con solo il logo a sinistra. Tono più \"app\" che \"magazine\".
*/
issueLabel?: string; issueLabel?: string;
className?: string; className?: string;
} }
const FORMATTER = new Intl.DateTimeFormat('it-IT', {
weekday: 'long',
day: 'numeric',
month: 'long',
year: 'numeric',
});
/** /**
* Top-of-page editorial header. Three columns on desktop: * Top-of-page header minimale: logo a sinistra, sezione corrente a destra (se
* ┌──────────────────┬─────────────────────┬──────────────────┐ * fornita). Una riga sottile sotto fa da divider editoriale.
* │ KP · monogram │ ANNO I · N. 01 │ Lunedì 29 aprile │
* └──────────────────┴─────────────────────┴──────────────────┘
* On mobile collapses to a row with monogram + date only.
*/ */
export function Masthead({ issueLabel = 'ANNO I · N. 01', className }: MastheadProps) { export function Masthead({ issueLabel, className }: MastheadProps) {
const today = FORMATTER.format(new Date());
return ( return (
<header className={cn('relative pb-5 pt-6', className)}> <header className={cn('relative pb-5 pt-6', className)}>
<div className="rule-thick mb-4" /> <div className="flex items-center justify-between gap-4">
<div className="flex items-baseline justify-between gap-4"> <Link
<Link href="/" className="monogram tracking-tight" aria-label="KetoPath"> href="/"
className="monogram tracking-tight transition-opacity hover:opacity-70"
aria-label="KetoPath"
>
KetoPath<span className="text-pomodoro">.</span> KetoPath<span className="text-pomodoro">.</span>
</Link> </Link>
<p className="editorial-eyebrow hidden sm:block">{issueLabel}</p> {issueLabel ? <p className="editorial-eyebrow hidden sm:block">{issueLabel}</p> : null}
<p className="text-ink-soft font-mono text-[11px] tracking-wide first-letter:uppercase">
{today}
</p>
</div> </div>
<div className="rule mt-4" /> <div className="rule mt-5" />
</header> </header>
); );
} }