Neues Hero-Bild: Amin bei der Arbeit

- Hero zeigt Amin mit Schere und Kamm am Kunden (Desktop 2000/1000 px,
  eigener Hochformat-Ausschnitt fürs Handy per <picture>)
- Headline und Button auf Desktop unten links, damit Gesichter, Schere
  und Kamm frei bleiben; auf dem Handy weiterhin zentriert
- Oben stärker abgedunkelt: Navigation (mix-blend-difference) auf dem
  hellen Studiohintergrund jetzt mit ca. 6:1 statt 2:1 Kontrast
- Menü: "Preise" zeigt ein Detail (Hände, Schere, Kamm), "Team" ein
  3 × 2 Raster aller Porträts
- Nicht mehr genutzte Fotos entfernt, Vorschaubild (og.jpg) und README
  aktualisiert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBHsCnWNXAB2Le3gAEsivF
This commit is contained in:
Claude committed 2026-09-29 13:49:45 +00:00
1 parent 54b5de6f5b
commit 790e18e8a6
12 files changed
+47 -31

No files matched your search

+24 -17
View File
@@ -13,36 +13,43 @@ export function Hero() {
return (
<section id="top" ref={ref} className="relative isolate h-svh min-h-[560px] overflow-hidden bg-ink text-paper">
<motion.div style={{ y }} className="absolute inset-0 -z-20">
<motion.img
src={image('photos/salon-wand')}
srcSet={`${image('photos/salon-wand-800')} 800w, ${image('photos/salon-wand')} 1317w`}
sizes="100vw"
alt={`Barber vor dem Logo an der Wand im Salon ${salon.name}`}
fetchPriority="high"
initial={{ scale: 1.15 }}
animate={{ scale: 1 }}
transition={{ duration: 2.2, ease: EASE_OUT_EXPO }}
className="size-full object-cover object-[50%_60%] md:object-[50%_100%]"
/>
{/* Handy: eigener Hochformat-Ausschnitt mit Amin und der Schere */}
<picture>
<source media="(max-width: 767px)" srcSet={image('photos/hero-mobile')} />
<motion.img
src={image('photos/hero')}
srcSet={`${image('photos/hero-1000')} 1000w, ${image('photos/hero')} 2000w`}
sizes="100vw"
alt={`${salon.name} schneidet einem Kunden mit Schere und Kamm die Haare`}
fetchPriority="high"
initial={{ scale: 1.15 }}
animate={{ scale: 1 }}
transition={{ duration: 2.2, ease: EASE_OUT_EXPO }}
className="size-full object-cover md:object-[50%_25%]"
/>
</picture>
</motion.div>
<div aria-hidden="true" className="absolute inset-0 -z-10 bg-ink/35" />
<div aria-hidden="true" className="absolute inset-x-0 top-0 -z-10 h-40 bg-linear-to-b from-ink/60 to-transparent" />
<div aria-hidden="true" className="absolute inset-x-0 bottom-0 -z-10 h-2/3 bg-linear-to-t from-ink/70 to-transparent" />
<div aria-hidden="true" className="absolute inset-0 -z-10 bg-ink/30" />
{/* oben dunkler, damit die Navigation auf dem hellen Studiohintergrund lesbar bleibt */}
<div aria-hidden="true" className="absolute inset-x-0 top-0 -z-10 h-44 bg-linear-to-b from-ink/80 to-transparent" />
<div aria-hidden="true" className="absolute inset-x-0 bottom-0 -z-10 h-3/5 bg-linear-to-t from-ink/75 to-transparent" />
<div aria-hidden="true" className="absolute inset-y-0 left-0 -z-10 hidden w-3/5 bg-linear-to-r from-ink/35 to-transparent md:block" />
<div className="flex h-full flex-col items-center justify-end px-4 pb-10 text-center md:pb-14">
{/* Text unten links: Gesichter, Schere und Kamm bleiben frei */}
<div className="flex h-full flex-col items-center justify-end px-4 pb-10 text-center md:items-start md:px-8 md:pb-12 md:text-left">
<RevealLines
as="h1"
onLoad
delay={0.3}
srLabel={`${salon.name} – Barber & Friseur in ${salon.city}. Dein Look, unser Handwerk.`}
className="display text-[clamp(1.55rem,6.4vw,5.75rem)]"
className="display text-[clamp(1.55rem,4.2vw,4.5rem)]"
lines={['Dein Look.', 'Unser Handwerk.']}
/>
<motion.div
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.9, ease: EASE_OUT_EXPO }}
className="mt-6 md:mt-8"
className="mt-6 md:mt-7"
>
<Button tone="light" />
</motion.div>
+13 -9
View File
@@ -1,15 +1,16 @@
import { AnimatePresence, motion } from 'motion/react'
import { useEffect, useRef, useState } from 'react'
import { BOOKING_URL, image, salon } from '../data/salon'
import { BOOKING_URL, image, salon, team } from '../data/salon'
import { cn } from '../lib/cn'
import { EASE_OUT_EXPO } from '../lib/motion'
import { scrollToHash, unlockScroll } from '../lib/scroll'
import { GridLines } from './GridLines'
// Ein Bild oder – beim Team – ein 3 × 2 Raster aus allen Porträts
const items = [
{ href: '#preise', label: 'Preise', photo: image('photos/emblem-quadrat') },
{ href: '#team', label: 'Team', photo: image('photos/mo-wand') },
{ href: '#kontakt', label: 'Kontakt', photo: image('photos/emblem-breit-900') },
{ href: '#preise', label: 'Preise', photos: [image('photos/hero-detail')] },
{ href: '#team', label: 'Team', photos: team.map((m) => m.thumb) },
{ href: '#kontakt', label: 'Kontakt', photos: [image('photos/emblem-breit-900')] },
]
/** Vollbild-Menü: große Links, daneben wechselt das Bild zum Link unter dem Mauszeiger. */
@@ -72,15 +73,18 @@ export function Menu({ open, onClose }: { open: boolean; onClose: () => void })
<div className="relative hidden md:block">
{items.map((item, i) => (
<img
<div
key={item.href}
src={item.photo}
alt=""
className={cn(
'absolute inset-0 size-full object-cover transition-all duration-700 ease-out-expo',
'absolute inset-0 grid gap-[2px] transition-all duration-700 ease-out-expo',
item.photos.length > 1 && 'grid-cols-3',
active === i ? 'scale-100 opacity-100' : 'scale-105 opacity-0',
)}
/>
>
{item.photos.map((src) => (
<img key={src} src={src} alt="" className="size-full min-h-0 object-cover" />
))}
</div>
))}
</div>
</div>