feat: Team, Villa-Bilder und bestaetigte Camp-Eckdaten
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m49s

- Team-Abschnitt mit neun Personen, Eric Heitz als Head of Modelcamp,
  Portraets im Rundbogen mit Initialen-Platzhaltern bis Fotos vorliegen
- Unterkunftsbilder der Villa La Chiesina lokal in public/images/unterkunft
  (vom Auftraggeber freigegeben), Hero-Leitbild, Ausstattung, Booking-Link
- Migration: Termin 08.05.-15.05.2027, Ort Arcidosso (Toskana), eigenes
  Bett im geteilten Zimmer, 13 Plaetze; Admin-Werte bleiben erhalten
- Reveal: kein Hydration-Fehler mehr bei reduzierter Bewegung
- Doku zu offenen Punkten aktualisiert

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-01 07:39:27 +02:00
1 parent 28663cda7a
commit 804bb832ea
28 files changed
+220 -89

No files matched your search

+15 -7
View File
@@ -21,6 +21,9 @@ const TONES: Record<PlaceholderTone, Plate> = {
studio: { background: "#d2c9bd", glyph: "A", italic: true, style: { fontSize: "96cqh", right: "-16cqw", bottom: "-16cqh" } },
};
// Initialen auf Portraet-Platzhaltern: gross, angeschnitten am unteren Rand.
const INITIALS_STYLE: React.CSSProperties = { fontSize: "34cqh", left: "8cqw", bottom: "-4cqh", letterSpacing: "-0.03em" };
type Props = {
media: Media;
/** Tailwind-Klasse fuer das Seitenverhaeltnis, z. B. "aspect-[4/5]". */
@@ -29,17 +32,22 @@ type Props = {
showLabel: boolean;
priority?: boolean;
className?: string;
/** "arch": oben rund wie ein Rundbogenfenster (Team-Portraets). */
shape?: "rect" | "arch";
};
const ARCH = "rounded-t-[50vw]";
/**
* Bild oder erkennbarer Platzhalter. Platzhalter tragen in Entwicklung und
* Staging einen sichtbaren Hinweis, damit fehlende Freigaben nicht
* uebersehen werden.
*/
export function MediaFrame({ media, aspect, sizes, showLabel, priority, className = "" }: Props) {
export function MediaFrame({ media, aspect, sizes, showLabel, priority, className = "", shape = "rect" }: Props) {
const shapeClass = shape === "arch" ? ARCH : "";
if (media.kind === "image") {
return (
<div className={`relative overflow-hidden bg-sand ${aspect} ${className}`}>
<div className={`relative overflow-hidden bg-sand ${aspect} ${shapeClass} ${className}`}>
<Image src={media.src} alt={media.alt} fill sizes={sizes} priority={priority} className="object-cover" />
</div>
);
@@ -49,17 +57,17 @@ export function MediaFrame({ media, aspect, sizes, showLabel, priority, classNam
<div
role="img"
aria-label={`Platzhalter: ${media.label}`}
className={`placeholder-plate relative isolate overflow-hidden ${aspect} ${className}`}
className={`placeholder-plate relative isolate overflow-hidden ${aspect} ${shapeClass} ${className}`}
style={{ backgroundColor: tone.background, containerType: "size" }}
>
<span
aria-hidden
className={`display pointer-events-none absolute select-none leading-none text-ink/[0.07] ${tone.italic ? "italic" : ""}`}
style={tone.style}
className={`display pointer-events-none absolute select-none leading-none ${media.glyph ? "text-ink/[0.13]" : "text-ink/[0.07]"} ${tone.italic && !media.glyph ? "italic" : ""}`}
style={media.glyph ? INITIALS_STYLE : tone.style}
>
{tone.glyph}
{media.glyph ?? tone.glyph}
</span>
<span aria-hidden className="pointer-events-none absolute inset-3 border border-ink/10 md:inset-4" />
{shape === "rect" && <span aria-hidden className="pointer-events-none absolute inset-3 border border-ink/10 md:inset-4" />}
{showLabel && (
<span className="absolute bottom-6 left-6 bg-paper/90 px-2.5 py-1 text-xs font-semibold text-ink md:bottom-7 md:left-7">
Platzhalter: {media.label}