fix: Logo deutlich groesser, Video im eigenen Seitenverhaeltnis

- Logo ueber dem Headerbild bis 112 px hoch, nach dem Scrollen 64 px,
  im Fussbereich 80 px; Kopfzeile waechst mit
- Videos aus dem Medienverzeichnis direkt mit Steuerleiste, quer oder
  hoch, erstes Bild als Vorschau; ein einzelnes Video breit und mittig
- Abschnitt heisst "Stimmen": Models erzaehlen, wie sie ARTISTA erleben

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-02 06:30:08 +02:00
1 parent 84c8f2f32b
commit a37f12a60f
7 files changed
+46 -40

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@
@layer base {
html {
scroll-behavior: smooth;
scroll-padding-top: 5rem;
scroll-padding-top: 5.5rem;
-webkit-text-size-adjust: 100%;
scrollbar-color: var(--color-stone) var(--color-plaster);
accent-color: var(--color-rosso);
+1 -1
View File
@@ -53,7 +53,7 @@ export function Hero({ pricing, media, video }: { pricing: PublicPricing; media:
<div aria-hidden className="absolute inset-0 bg-[linear-gradient(180deg,rgb(35_33_31/0.6)_0%,rgb(35_33_31/0.45)_45%,rgb(35_33_31/0.75)_100%)]" />
</div>
<div className="container-edge flex flex-col items-center pb-20 pt-32 text-center">
<div className="container-edge flex flex-col items-center pb-20 pt-36 text-center md:pt-44">
<h1 id="hero-title" className="hero-fade max-w-4xl text-[clamp(2.75rem,8vw,6.25rem)] font-extrabold leading-[0.98] tracking-[-0.03em]">
<span className="sr-only">ARTISTA </span>
{hero.title}
+1 -1
View File
@@ -7,7 +7,7 @@ export function SiteFooter({ facts, showDrafts, logo }: { facts: PublicCampFacts
<footer id="site-footer" className="border-t border-line bg-plaster">
<div className="container-edge grid gap-10 py-14 md:grid-cols-12">
<div className="md:col-span-5">
<p className="flex h-10 items-center text-2xl">
<p className="flex h-20 items-center text-2xl">
<Logo src={logo} />
</p>
<p className="mt-1 text-stone">Modelcamp 2027</p>
+9 -2
View File
@@ -82,7 +82,9 @@ export function SiteHeader({ logoLight, logoDark }: { logoLight: string | null;
Zum Inhalt springen
</a>
<LazyMotion features={domAnimation}>
<div className="container-edge grid h-[4.5rem] grid-cols-[1fr_auto_1fr] items-center gap-4">
<div
className={`container-edge grid grid-cols-[1fr_auto_1fr] items-center gap-4 transition-[height] duration-300 ${solid ? "h-20" : "h-24 md:h-32"}`}
>
<div className="flex items-center">
<button
ref={menuButton}
@@ -99,7 +101,12 @@ export function SiteHeader({ logoLight, logoDark }: { logoLight: string | null;
</nav>
</div>
<a href="#top" aria-label="ARTISTA Modelcamp 2027, zum Seitenanfang" className="flex h-8 items-center text-xl md:h-10 md:text-2xl">
{/* Logo ueber dem Headerbild gross, nach dem Scrollen kompakter. */}
<a
href="#top"
aria-label="ARTISTA Modelcamp 2027, zum Seitenanfang"
className={`flex items-center text-xl transition-[height] duration-300 md:text-2xl ${solid ? "h-16" : "h-20 md:h-28"}`}
>
<Logo src={solid ? logoDark : logoLight} />
</a>
+25 -29
View File
@@ -1,47 +1,43 @@
"use client";
import { useState } from "react";
import type { Media } from "@/content/site";
import { MediaFrame } from "./MediaFrame";
type Slot = { name: string | null; caption: string | null; poster: Media | null; src: string | null };
/**
* Ein Reel im Hochformat (9:16) aus dem eigenen Medienverzeichnis. Das
* Video laedt erst nach Klick (preload="none") und startet nie von selbst
* mit Ton. Kein externer Anbieter, keine Datenuebertragung an Dritte.
* Ein Video aus dem eigenen Medienverzeichnis, im eigenen Seitenverhaeltnis
* (quer oder hoch). Es startet nie von selbst; geladen werden vorab nur die
* Metadaten und das erste Bild als Vorschau (#t=0.1), falls kein
* Vorschaubild hinterlegt ist. Kein externer Anbieter.
*/
export function VideoSlot({ slot, index, showDrafts }: { slot: Slot; index: number; showDrafts: boolean }) {
const [playing, setPlaying] = useState(false);
const label = slot.name ? `Reel von ${slot.name} abspielen` : `Reel ${index + 1} abspielen`;
const poster: Media = slot.poster ?? { kind: "placeholder", label: `Reel ${index + 1} folgt`, tone: "studio" };
const label = slot.name ? `Video von ${slot.name}` : `Video ${index + 1}`;
const poster = slot.poster?.kind === "image" ? slot.poster.src : undefined;
return (
<figure>
<div className="relative aspect-[9/16] overflow-hidden rounded-2xl bg-ink">
{playing && slot.src ? (
<video src={slot.src} controls autoPlay playsInline preload="none" className="h-full w-full object-cover">
{slot.src ? (
<video
src={poster ? slot.src : `${slot.src}#t=0.1`}
poster={poster}
controls
playsInline
preload="metadata"
aria-label={label}
className="mx-auto block max-h-[80vh] w-full rounded-2xl bg-ink object-contain"
>
<track kind="captions" />
</video>
) : (
<>
<MediaFrame media={poster} aspect="h-full w-full" sizes="(min-width: 640px) 33vw, 100vw" showLabel={showDrafts} />
{slot.src && (
<button
type="button"
onClick={() => setPlaying(true)}
aria-label={label}
className="absolute inset-0 m-auto flex h-20 w-20 items-center justify-center rounded-full bg-paper/90 text-ink transition-transform hover:scale-105"
>
<svg viewBox="0 0 24 24" className="ml-1 h-8 w-8" aria-hidden>
<path d="M7 4.5v15l12-7.5z" fill="currentColor" />
</svg>
</button>
)}
</>
)}
<div className="relative aspect-[9/16] overflow-hidden rounded-2xl">
<MediaFrame
media={slot.poster ?? { kind: "placeholder", label: `Video ${index + 1} folgt`, tone: "studio" }}
aspect="h-full w-full"
sizes="(min-width: 640px) 33vw, 100vw"
showLabel={showDrafts}
/>
</div>
{(slot.name || slot.caption || showDrafts) && (
)}
{(slot.name || slot.caption || (showDrafts && !slot.src)) && (
<figcaption className="mt-4">
{slot.name ? <span className="block font-semibold">{slot.name}</span> : showDrafts && <span className="draft-flag">Name und Freigabe folgen</span>}
{slot.caption && <span className="mt-1 block opacity-80">{slot.caption}</span>}
+7 -4
View File
@@ -95,14 +95,17 @@ export function AboutSection({ showDrafts }: { showDrafts: boolean }) {
}
/**
* Drei Reels im Hochformat. Leere Slots erscheinen nur in Entwicklung und
* Staging; oeffentlich nie ein erfundenes Testimonial als Ersatz.
* Bis zu drei Videos (Models ueber ARTISTA). Ein einzelnes Video steht
* breit in der Mitte, mehrere im Raster. Leere Slots erscheinen nur in
* Entwicklung und Staging; oeffentlich nie ein erfundenes Testimonial.
*/
export function ReelsSection({ showDrafts, reels }: { showDrafts: boolean; reels: Reel[] }) {
const { reels: content } = site;
const slots = content.slots.map((slot, index) => ({ ...slot, ...(reels[index] ?? { src: null, poster: null }) }));
const visible = showDrafts ? slots : slots.filter((s) => s.src);
const filled = slots.filter((s) => s.src);
const visible = showDrafts ? [...filled, ...slots.filter((s) => !s.src)] : filled;
if (visible.length === 0) return null;
const single = visible.length === 1;
return (
<section id="reels" aria-labelledby="reels-title" className="on-dark bg-ink py-24 text-paper md:py-32">
<div className="container-edge">
@@ -110,7 +113,7 @@ export function ReelsSection({ showDrafts, reels }: { showDrafts: boolean; reels
<h2 id="reels-title" className="section-title">{content.title}</h2>
<p className="max-w-md text-paper/75">{content.lead}</p>
</div>
<ul className="mx-auto mt-12 grid max-w-5xl gap-6 sm:grid-cols-3 md:gap-8">
<ul className={`mx-auto mt-12 grid gap-6 md:gap-8 ${single ? "max-w-4xl" : "max-w-5xl sm:grid-cols-3"}`}>
{visible.map((slot, index) => (
<li key={index}>
<VideoSlot slot={slot} index={index} showDrafts={showDrafts} />
+2 -2
View File
@@ -246,8 +246,8 @@ export const site = {
},
reels: {
title: "Reels",
lead: "Eindrücke und Stimmen in drei kurzen Clips.",
title: "Stimmen",
lead: "Models erzählen, wie sie ARTISTA erleben.",
// Genau drei Slots. Video und Vorschaubild kommen ueber das
// Medienverzeichnis (reels/reel-1.mp4 und reels/reel-1.jpg usw.).
// Name und Begleittext nur mit Freigabe der gezeigten Person.