Files
pizza-dummy/index.html
KNSONWS db6b0843d2 viel.
2026-03-27 16:56:19 +01:00

1767 lines
48 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Pizza</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Outfit:wght@300;500;600&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #1a1510;
--paper: #f4ede4;
--accent: #c45c26;
--muted: #8a7f72;
}
*, *::before, *::after {
box-sizing: border-box;
}
html, body {
margin: 0;
min-height: 100%;
background: var(--bg);
color: var(--paper);
font-family: "Outfit", system-ui, sans-serif;
}
body {
display: flex;
flex-direction: column;
}
body.preview-open {
overflow: hidden;
}
.cat-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 40;
flex-shrink: 0;
background: linear-gradient(180deg, rgba(26, 21, 16, 0.97) 0%, rgba(26, 21, 16, 0.88) 85%, transparent 100%);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(244, 237, 228, 0.1);
padding: 0.65rem clamp(0.5rem, 2vw, 1rem) 0.75rem;
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s ease;
will-change: transform;
}
.cat-nav.cat-nav--hidden {
transform: translateY(-100%);
opacity: 0;
pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
.cat-nav {
transition-duration: 0.01ms;
}
}
/** Shop-Bereich unter dem News-Block; Abstand oben, wenn Kategorien-Leiste eingeblendet ist */
.page-body {
flex: 1 1 auto;
min-height: 0;
width: 100%;
display: flex;
flex-direction: column;
transition: padding-top 0.35s ease;
}
body.cat-nav-visible .page-body {
padding-top: clamp(4.25rem, 11vw, 5.5rem);
}
.news-hero {
position: relative;
flex: 0 0 auto;
min-height: 100vh;
min-height: 100dvh;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 0;
padding: clamp(1.5rem, 4vw, 3rem);
padding-right: min(42vw, 28rem);
text-align: left;
background-color: var(--bg);
background-image:
linear-gradient(
to right,
var(--bg) 0%,
rgba(26, 21, 16, 0.97) 14%,
rgba(26, 21, 16, 0.88) 28%,
rgba(26, 21, 16, 0.5) 52%,
rgba(26, 21, 16, 0.12) 78%,
transparent 100%
),
url("BackGroud-news.png");
background-size: cover;
background-position: right center;
background-repeat: no-repeat;
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
}
.news-hero__inner {
position: relative;
z-index: 1;
max-width: min(36rem, 100%);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1.15rem;
}
.news-hero__badge {
display: inline-block;
font-size: clamp(0.7rem, 1.5vw, 0.82rem);
font-weight: 600;
letter-spacing: 0.2em;
text-transform: uppercase;
color: #e8c56a;
}
.news-hero__title {
font-family: "Bebas Neue", Impact, sans-serif;
font-size: clamp(2.75rem, 10vw, 4.5rem);
line-height: 0.95;
letter-spacing: 0.03em;
margin: 0;
max-width: 16ch;
color: var(--paper);
}
.news-hero__text {
margin: 0;
max-width: 28rem;
font-size: clamp(0.95rem, 2vw, 1.1rem);
font-weight: 300;
line-height: 1.55;
color: var(--muted);
}
.news-hero__text strong {
color: var(--paper);
font-weight: 500;
}
.news-hero__hint {
position: absolute;
bottom: clamp(1rem, 3vh, 2rem);
left: clamp(1.5rem, 4vw, 3rem);
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.35rem;
}
.news-hero__hint::after {
content: "";
width: 1px;
height: 2rem;
background: linear-gradient(180deg, rgba(244, 237, 228, 0.35), transparent);
animation: news-hint-bob 1.8s ease-in-out infinite;
}
@keyframes news-hint-bob {
0%, 100% { transform: translateY(0); opacity: 0.6; }
50% { transform: translateY(6px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.news-hero__hint::after {
animation: none;
}
}
@media (max-width: 640px) {
.news-hero {
padding-right: clamp(1rem, 4vw, 1.5rem);
background-image:
linear-gradient(
to right,
var(--bg) 0%,
rgba(26, 21, 16, 0.98) 32%,
rgba(26, 21, 16, 0.82) 58%,
rgba(26, 21, 16, 0.35) 82%,
rgba(26, 21, 16, 0.06) 96%,
transparent 100%
),
url("BackGroud-news.png");
background-position: 82% center;
}
}
.cat-nav__scroll {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 0.5rem;
overflow-x: auto;
overflow-y: hidden;
padding-bottom: 0.15rem;
scroll-snap-type: x proximity;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
scrollbar-color: rgba(196, 92, 38, 0.45) transparent;
}
.cat-nav__scroll::-webkit-scrollbar {
height: 4px;
}
.cat-nav__scroll::-webkit-scrollbar-thumb {
background: rgba(196, 92, 38, 0.45);
border-radius: 999px;
}
.cat-nav__btn {
flex: 0 0 auto;
scroll-snap-align: start;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
gap: 0.35rem;
min-width: 5rem;
max-width: 6.25rem;
padding: 0.4rem 0.45rem 0.5rem;
border: 1px solid rgba(244, 237, 228, 0.12);
border-radius: 10px;
background: rgba(35, 28, 21, 0.65);
color: var(--paper);
font-family: inherit;
cursor: pointer;
transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.cat-nav__btn:hover {
border-color: rgba(232, 197, 106, 0.45);
background: rgba(45, 36, 28, 0.75);
}
.cat-nav__btn.is-active {
border-color: rgba(196, 92, 38, 0.85);
background: rgba(196, 92, 38, 0.18);
box-shadow: 0 0 0 1px rgba(196, 92, 38, 0.25);
}
.cat-nav__btn.is-active .cat-nav__icon-wrap {
filter: brightness(1.12) saturate(1.08);
}
.cat-nav__icon-wrap {
width: 3.75rem;
height: 3.75rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: #e8c56a;
}
.cat-nav__icon-wrap svg,
.cat-nav__icon-wrap .cat-icon-img {
width: 3.25rem;
height: 3.25rem;
display: block;
}
.cat-nav__icon-wrap .cat-icon-img {
object-fit: contain;
}
.cat-nav__label {
font-size: 0.62rem;
font-weight: 600;
line-height: 1.15;
text-align: center;
color: var(--paper);
letter-spacing: 0.02em;
}
.cat-nav__hint {
font-size: 0.58rem;
font-weight: 300;
line-height: 1.2;
text-align: center;
color: var(--muted);
max-width: 5rem;
}
.filter-empty {
display: none;
flex: 1;
min-height: 40vh;
align-items: center;
justify-content: center;
padding: 2rem;
text-align: center;
color: var(--muted);
font-size: 0.95rem;
font-weight: 300;
}
.filter-empty.is-visible {
display: flex;
}
.page {
width: 100vw;
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
/** Drei Pizzen nebeneinander; sichtbare Karten teilen sich die Breite gleichmäßig */
.pizza-grid {
flex: 1 1 auto;
min-height: 0;
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: stretch;
gap: 1px;
background: rgba(244, 237, 228, 0.1);
}
.pizza-row {
flex: 1 1 0;
min-width: 0;
min-height: 0;
width: auto;
display: flex;
flex-direction: column;
align-items: stretch;
background: linear-gradient(180deg, #231c15 0%, rgba(26, 21, 16, 0.98) 45%, var(--bg) 100%);
}
.text-col,
.img-col {
min-width: 0;
min-height: 0;
display: flex;
align-items: center;
}
.text-col {
flex: 0 0 auto;
flex-direction: column;
justify-content: flex-start;
align-items: center;
text-align: center;
padding: clamp(0.65rem, 1.8vw, 1.1rem) clamp(0.5rem, 1.2vw, 0.85rem) 0.35rem;
background: transparent;
}
.img-col {
flex: 1 1 auto;
position: relative;
justify-content: center;
align-items: center;
padding: clamp(0.35rem, 1vw, 0.75rem) clamp(0.35rem, 1vw, 0.65rem) clamp(0.5rem, 1.2vw, 0.85rem);
background: radial-gradient(ellipse 75% 80% at 50% 45%, #2a2219 0%, transparent 72%);
}
.pizza-grid h1 {
font-size: clamp(1.45rem, 2.8vw, 2.35rem);
margin-bottom: 0.35rem;
}
.pizza-grid .subtitle {
margin-bottom: 0.5rem;
}
.pizza-grid .details {
max-width: 100%;
font-size: clamp(0.78rem, 1.25vw, 0.92rem);
line-height: 1.45;
}
.pizza-grid .price {
margin-top: 0.65rem;
font-size: clamp(0.95rem, 1.5vw, 1.1rem);
}
.pizza-grid .pizza-badges {
justify-content: center;
max-width: 100%;
margin: 0.2rem 0 0.45rem;
}
.pizza-grid .pizza-frame {
width: min(100%, 44vmin);
height: min(100%, 44vmin);
}
@media (max-width: 720px) {
.pizza-grid {
flex-direction: column;
flex-wrap: nowrap;
}
.pizza-row {
flex: 0 0 auto;
width: 100%;
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
}
.pizza-row:last-child {
border-bottom: none;
}
.pizza-grid .pizza-frame {
width: min(100%, min(68vmin, 82vw));
height: min(100%, min(68vmin, 82vw));
}
}
.img-col--clickable {
cursor: pointer;
}
.img-col--clickable:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 4px;
}
.pizza-frame {
position: relative;
width: min(100%, 55vh);
height: min(100%, 55vh);
max-width: 100%;
max-height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.pizza-frame img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
animation: pizza-spin 48s linear infinite;
will-change: transform;
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
pointer-events: none;
}
.pizza-frame:hover img {
animation-play-state: paused;
}
@keyframes pizza-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
h1 {
font-family: "Bebas Neue", Impact, sans-serif;
font-size: clamp(2.5rem, 8vw, 5rem);
line-height: 0.95;
letter-spacing: 0.02em;
margin: 0 0 0.5rem;
color: var(--paper);
}
.subtitle {
font-size: clamp(0.85rem, 1.8vw, 1rem);
font-weight: 500;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.18em;
margin-bottom: 1rem;
}
.details {
font-weight: 300;
font-size: clamp(0.9rem, 1.6vw, 1.05rem);
line-height: 1.55;
color: var(--muted);
max-width: 28em;
}
.details strong {
color: var(--paper);
font-weight: 500;
}
.price {
margin-top: 1.25rem;
font-size: clamp(1.1rem, 2vw, 1.35rem);
font-weight: 600;
color: var(--paper);
}
.pizza-badges {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin: 0.35rem 0 0.85rem;
max-width: 22rem;
}
.pizza-badge {
width: 3.1rem;
height: 3.1rem;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: transparent;
border: none;
box-shadow: none;
color: #e8c56a;
cursor: default;
transition: transform 0.15s ease, filter 0.15s ease;
}
.pizza-badge:hover {
transform: scale(1.06);
filter: brightness(1.08);
}
.pizza-badge svg,
.pizza-badge .cat-icon-img {
width: 2.75rem;
height: 2.75rem;
display: block;
}
.pizza-badge .cat-icon-img {
object-fit: contain;
}
.pizza-badge--preview {
width: 2.65rem;
height: 2.65rem;
}
.pizza-badge--preview svg,
.pizza-badge--preview .cat-icon-img {
width: 2.35rem;
height: 2.35rem;
}
.preview-badges {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin: 0 0 1rem;
}
/* ——— Preview ——— */
.preview {
position: fixed;
inset: 0;
z-index: 100;
display: none;
flex-direction: column;
background: var(--bg);
padding: clamp(0.75rem, 2vw, 1.5rem);
}
.preview.is-visible {
display: flex;
}
.preview__grid {
flex: 1;
min-height: 0;
display: grid;
grid-template-columns: minmax(9rem, 0.85fr) minmax(0, 3.2fr) minmax(9rem, 0.85fr);
grid-template-rows: 1fr auto;
grid-template-areas:
"left center right"
"left bottom bottom";
gap: clamp(0.5rem, 2vw, 1.25rem);
align-items: start;
}
@media (max-width: 900px) {
.preview__grid {
grid-template-columns: 1fr;
grid-template-rows: auto auto 1fr auto;
grid-template-areas:
"left"
"center"
"right"
"bottom";
}
.preview-left {
padding-top: 2.5rem;
}
}
.preview-left {
grid-area: left;
padding-right: 0.5rem;
}
.preview-center {
grid-area: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 0;
position: relative;
}
.preview-right {
grid-area: right;
justify-self: end;
text-align: right;
width: 100%;
max-width: 16rem;
}
.preview-bottom {
grid-area: bottom;
justify-self: end;
align-self: end;
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
justify-content: flex-end;
padding-top: 0.5rem;
}
.preview__back {
position: absolute;
top: clamp(0.5rem, 2vw, 1rem);
left: clamp(0.5rem, 2vw, 1rem);
z-index: 2;
background: rgba(26, 21, 16, 0.85);
border: 1px solid rgba(244, 237, 228, 0.2);
color: var(--paper);
font-family: inherit;
font-size: 0.9rem;
padding: 0.45rem 0.85rem;
border-radius: 6px;
cursor: pointer;
}
.preview__back:hover {
border-color: var(--accent);
color: var(--accent);
}
.preview-name {
font-family: "Bebas Neue", Impact, sans-serif;
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1;
margin: 0 0 1rem;
color: var(--paper);
}
.preview-label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--accent);
margin-bottom: 0.5rem;
}
.ingredient-list {
list-style: none;
margin: 0;
padding: 0;
}
.ingredient-list li {
font-size: clamp(0.9rem, 1.4vw, 1rem);
font-weight: 300;
color: var(--muted);
padding: 0.35rem 0;
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
}
.ingredient-list li:last-child {
border-bottom: none;
}
.macro-list {
list-style: none;
margin: 0;
padding: 0;
}
.macro-list li {
font-size: clamp(0.85rem, 1.3vw, 0.95rem);
padding: 0.4rem 0;
color: var(--muted);
border-bottom: 1px solid rgba(244, 237, 228, 0.08);
}
.macro-list li strong {
display: block;
color: var(--paper);
font-weight: 500;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 0.15rem;
}
.carousel-wrap {
position: relative;
width: 100%;
max-width: min(92vmin, 760px);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.carousel-stage {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.carousel-layer {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
will-change: transform;
}
.carousel-layer img {
width: 100%;
height: 100%;
object-fit: contain;
filter: drop-shadow(0 16px 40px rgba(0, 0, 0, 0.5));
}
.carousel-btn {
position: fixed;
top: 50%;
transform: translateY(-50%);
width: 48px;
height: 48px;
border-radius: 50%;
border: 1px solid rgba(244, 237, 228, 0.25);
background: rgba(26, 21, 16, 0.85);
color: var(--paper);
font-size: 1.35rem;
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 110;
}
.carousel-btn:hover {
border-color: var(--accent);
color: var(--accent);
}
.carousel-btn--prev {
left: env(safe-area-inset-left, 0);
right: auto;
}
.carousel-btn--next {
right: env(safe-area-inset-right, 0);
left: auto;
}
.preview-timer {
width: 100%;
max-width: min(92vmin, 760px);
margin-top: 0.75rem;
}
.preview-timer[hidden] {
display: none !important;
}
.preview-timer__label {
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 0.4rem;
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 0.5rem;
}
.preview-timer__sec {
font-variant-numeric: tabular-nums;
color: var(--paper);
font-weight: 500;
}
.preview-timer__track {
height: 8px;
border-radius: 999px;
background: rgba(244, 237, 228, 0.1);
overflow: hidden;
border: 1px solid rgba(244, 237, 228, 0.12);
}
.preview-timer__fill {
height: 100%;
width: 0%;
border-radius: inherit;
background: linear-gradient(90deg, #8b3d18, var(--accent));
box-shadow: 0 0 12px rgba(196, 92, 38, 0.35);
}
.cart-combo {
display: inline-flex;
align-items: stretch;
border: 1px solid rgba(244, 237, 228, 0.22);
border-radius: 10px;
overflow: hidden;
background: rgba(35, 28, 21, 0.85);
}
.cart-combo__qty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.25rem 0.35rem;
border-right: 1px solid rgba(244, 237, 228, 0.15);
min-width: 3.25rem;
}
.qty-arrow {
width: 100%;
border: none;
background: transparent;
color: var(--paper);
font-size: 0.7rem;
line-height: 1;
padding: 0.2rem 0.5rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.qty-arrow:hover:not(:disabled) {
color: var(--accent);
}
.qty-arrow:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.qty-value {
font-weight: 600;
font-size: 1.15rem;
line-height: 1.2;
padding: 0.15rem 0;
min-height: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.btn-cart {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.65rem 1.15rem;
border: none;
border-radius: 0;
background: var(--accent);
color: var(--bg);
font-family: inherit;
font-weight: 600;
font-size: 0.95rem;
cursor: pointer;
}
.btn-cart:hover {
filter: brightness(1.08);
}
.btn-cart svg {
width: 1.1em;
height: 1.1em;
}
.preview-hint {
font-size: 0.75rem;
color: var(--muted);
margin-top: 0.75rem;
text-align: center;
}
</style>
</head>
<body>
<nav
class="cat-nav cat-nav--hidden"
id="cat-nav"
aria-label="Kategorien wählen (mehrfach, Klick zum Abwählen)"
aria-hidden="true"
>
<div class="cat-nav__scroll" id="cat-nav-scroll" role="toolbar"></div>
</nav>
<main class="page" id="home">
<section id="news-hero" class="news-hero" aria-labelledby="news-hero-title">
<div class="news-hero__inner">
<p class="news-hero__badge">Aktion &amp; Neuigkeiten</p>
<h2 class="news-hero__title" id="news-hero-title">Wochenangebot: Diavolo scharf</h2>
<p class="news-hero__text">
<strong>Bis Sonntag 2 € sparen</strong> auf alle großen Pizzen — frisch aus dem Steinofen,
wie immer mit Teig aus 48&nbsp;Stunden Gare.
</p>
</div>
<p class="news-hero__hint">
<span>Runter scrollen</span>
</p>
</section>
<div class="page-body" id="page-body">
<div class="filter-empty" id="filter-empty" role="status" aria-live="polite">
Keine Pizza passt zu den gewählten Kategorien.
</div>
<div class="pizza-grid" id="pizza-grid">
<section class="pizza-row" data-pizza-index="0" aria-label="Pizza Margherita">
<div class="text-col">
<p class="subtitle">Klassiker</p>
<div class="pizza-badges" data-badges-for="0" aria-label="Kategorien"></div>
<h1>Margherita</h1>
<p class="details">
<strong>Tomate, Mozzarella, Basilikum.</strong> Unser Teig über Nacht, Ofen aus Stein.
Sanft, säuerlich, klar — wie Neapel, nur auf deinem Bildschirm.
</p>
<p class="price">ab 9,50 €</p>
</div>
<div
class="img-col img-col--clickable"
role="button"
tabindex="0"
data-pizza-index="0"
aria-label="Margherita Vorschau öffnen"
>
<div class="pizza-frame">
<img src="magarita.png" alt="" width="800" height="800" />
</div>
</div>
</section>
<section class="pizza-row" data-pizza-index="1" aria-label="Pizza Salami">
<div class="text-col">
<p class="subtitle">Beliebt</p>
<div class="pizza-badges" data-badges-for="1" aria-label="Kategorien"></div>
<h1>Salami</h1>
<p class="details">
<strong>Tomate, Mozzarella, luftgetrocknete Salami.</strong> Würzig, saftig, mit leichtem
Paprika-Touch. Für alle, die es etwas kräftiger mögen.
</p>
<p class="price">ab 11,00 €</p>
</div>
<div
class="img-col img-col--clickable"
role="button"
tabindex="0"
data-pizza-index="1"
aria-label="Salami Vorschau öffnen"
>
<div class="pizza-frame">
<img src="salami.png" alt="" width="800" height="800" />
</div>
</div>
</section>
<section class="pizza-row" data-pizza-index="2" aria-label="Pizza Diavolo">
<div class="text-col">
<p class="subtitle">Scharf</p>
<div class="pizza-badges" data-badges-for="2" aria-label="Kategorien"></div>
<h1>Diavolo</h1>
<p class="details">
<strong>Tomate, Mozzarella, scharfe Salami, Peperoncini.</strong> Feurig und würzig — für
alle, die es richtig scharf mögen.
</p>
<p class="price">ab 12,00 €</p>
</div>
<div
class="img-col img-col--clickable"
role="button"
tabindex="0"
data-pizza-index="2"
aria-label="Diavolo Vorschau öffnen"
>
<div class="pizza-frame">
<img src="Pizza Diavolo.png" alt="" width="800" height="800" />
</div>
</div>
</section>
</div>
</div>
</main>
<div
class="preview"
id="preview"
role="dialog"
aria-modal="true"
aria-labelledby="preview-title"
hidden
>
<button type="button" class="preview__back" id="preview-close">← Zurück</button>
<div class="preview__grid">
<div class="preview-left">
<p class="preview-label">Pizza</p>
<h2 class="preview-name" id="preview-title">Margherita</h2>
<div class="preview-badges" id="preview-badges" aria-label="Kategorien"></div>
<p class="preview-label">Zutaten</p>
<ul class="ingredient-list" id="preview-ingredients"></ul>
</div>
<div class="preview-center">
<div class="carousel-wrap">
<button type="button" class="carousel-btn carousel-btn--prev" id="carousel-prev" aria-label="Vorheriges Bild"></button>
<div class="carousel-stage" id="carousel-stage">
<div class="carousel-layer" id="carousel-layer-a">
<img src="" alt="" width="800" height="800" />
</div>
<div class="carousel-layer" id="carousel-layer-b">
<img src="" alt="" width="800" height="800" />
</div>
</div>
<button type="button" class="carousel-btn carousel-btn--next" id="carousel-next" aria-label="Nächstes Bild"></button>
</div>
<div
class="preview-timer"
id="preview-timer"
role="timer"
aria-label="Zeit bis zum nächsten Bildwechsel"
hidden
>
<div class="preview-timer__label">
<span>Nächster Wechsel</span>
<span class="preview-timer__sec" id="preview-timer-sec" aria-hidden="true">5 s</span>
</div>
<div class="preview-timer__track">
<div class="preview-timer__fill" id="preview-timer-fill"></div>
</div>
</div>
<p class="preview-hint">Pfeile: andere Pizza · Zurück: Übersicht</p>
</div>
<div class="preview-right">
<p class="preview-label">Nährwerte (pro Pizza)</p>
<ul class="macro-list" id="preview-macros"></ul>
</div>
<div class="preview-bottom">
<div class="cart-combo" role="group" aria-label="Menge und Warenkorb">
<div class="cart-combo__qty">
<button type="button" class="qty-arrow" id="qty-up" aria-label="Menge erhöhen"></button>
<span class="qty-value" id="qty-num">1</span>
<button type="button" class="qty-arrow" id="qty-down" aria-label="Menge verringern"></button>
</div>
<button type="button" class="btn-cart" id="btn-add-cart">
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49A1.003 1.003 0 0 0 20 4H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
In den Warenkorb
</button>
</div>
</div>
</div>
</div>
<script>
(function () {
const IMAGE_SWAP_MS = 5000;
const PREVIEW_SLIDE_MS = 650;
var CATEGORY_ORDER = [
"marine",
"fleisch",
"vegetarier",
"kaese",
"akut",
"einfach",
"kinder",
"ohne_zwiebeln",
"ohne_oliven",
"ohne_pilze",
"neuheit",
"goettlich",
];
var CATEGORY_DEFS = {
marine: {
label: "Marine",
hint: "Fisch & Meeresfrüchte",
icon: "icons/cat-marine.png",
},
fleisch: {
label: "Fleisch",
hint: "Mit Fleisch",
icon: "icons/cat-fleisch.png",
},
vegetarier: {
label: "Vegetarier",
hint: "Ohne Fleisch",
icon: "icons/cat-vegetarier.png",
},
kaese: {
label: "Käse",
hint: "Käse-lastig",
icon: "icons/cat-kaese.png",
},
akut: {
label: "Akut",
hint: "Scharf / würzig",
icon: "icons/cat-akut.png",
},
einfach: {
label: "Einfach",
hint: "Wenig Zutaten",
icon: "icons/cat-einfach.png",
},
kinder: {
label: "Für Kinder",
hint: "Mild & kindgerecht",
icon: "icons/cat-kinder.png",
},
ohne_zwiebeln: {
label: "Ohne Zwiebeln",
hint: "Zwiebel-frei",
icon: "icons/cat-ohne-zwiebeln.png",
},
ohne_oliven: {
label: "Ohne Oliven",
hint: "Oliven-frei",
icon: "icons/cat-ohne-oliven.png",
},
ohne_pilze: {
label: "Ohne Pilze",
hint: "Pilz-frei",
icon: "icons/cat-ohne-pilze.png",
},
neuheit: {
label: "Neuheit",
hint: "Neu im Sortiment",
icon: "icons/cat-neuheit.png",
},
goettlich: {
label: "Göttlich",
hint: "Besonders empfohlen",
icon: "icons/cat-goettlich.png",
},
};
function escapeAttr(s) {
return String(s)
.replace(/&/g, "&amp;")
.replace(/"/g, "&quot;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
function escapeHtml(s) {
return String(s)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
function categoryIconInnerHtml(def) {
if (!def) return "";
if (def.icon) {
return (
'<img src="' +
escapeAttr(def.icon) +
'" alt="" class="cat-icon-img" width="24" height="24" loading="lazy" decoding="async" />'
);
}
return def.svg || "";
}
/** Mehrfachauswahl: Klick toggelt; leer = alle Pizzas sichtbar */
var activeCategories = [];
function toggleCategory(id) {
var i = activeCategories.indexOf(id);
if (i >= 0) {
activeCategories.splice(i, 1);
} else {
activeCategories.push(id);
}
}
function pizzaMatchesFilter(p) {
if (!activeCategories.length) return true;
if (!p.categories || !p.categories.length) return false;
for (var a = 0; a < activeCategories.length; a++) {
if (p.categories.indexOf(activeCategories[a]) !== -1) return true;
}
return false;
}
function renderCategoryBadges(categoryIds, badgeClass) {
var cls = badgeClass || "pizza-badge";
if (!categoryIds || !categoryIds.length) return "";
return categoryIds
.map(function (id) {
var def = CATEGORY_DEFS[id];
if (!def) return "";
return (
'<span class="' +
cls +
'" title="' +
escapeAttr(def.label) +
'" role="img" aria-label="' +
escapeAttr(def.label) +
'">' +
categoryIconInnerHtml(def) +
"</span>"
);
})
.join("");
}
function mountOverviewBadges() {
document.querySelectorAll("[data-badges-for]").forEach(function (el) {
var idx = parseInt(el.getAttribute("data-badges-for"), 10);
var p = pizzas[idx];
if (!p || !p.categories) {
el.innerHTML = "";
return;
}
el.innerHTML = renderCategoryBadges(p.categories);
});
}
function getVisibleIndices() {
return pizzas
.map(function (p, i) {
return pizzaMatchesFilter(p) ? i : -1;
})
.filter(function (i) {
return i >= 0;
});
}
function applyFilter() {
var vis = getVisibleIndices();
var emptyEl = document.getElementById("filter-empty");
document.querySelectorAll(".pizza-row[data-pizza-index]").forEach(function (row) {
var idx = parseInt(row.getAttribute("data-pizza-index"), 10);
var show = vis.indexOf(idx) !== -1;
row.style.display = show ? "" : "none";
row.setAttribute("aria-hidden", show ? "false" : "true");
});
if (emptyEl) {
if (vis.length === 0) {
emptyEl.classList.add("is-visible");
emptyEl.removeAttribute("aria-hidden");
} else {
emptyEl.classList.remove("is-visible");
emptyEl.setAttribute("aria-hidden", "true");
}
}
document.querySelectorAll("#cat-nav-scroll .cat-nav__btn").forEach(function (btn) {
var cat = btn.getAttribute("data-cat");
if (!cat) return;
var match = activeCategories.indexOf(cat) !== -1;
btn.classList.toggle("is-active", match);
btn.setAttribute("aria-pressed", match ? "true" : "false");
});
}
function syncFilterAfterChange() {
applyFilter();
var vis = getVisibleIndices();
if (preview.classList.contains("is-visible")) {
if (vis.indexOf(currentIndex) === -1) {
if (vis.length) {
currentIndex = vis[0];
renderPizza();
} else {
closePreview();
}
}
}
}
function stepPreview(delta) {
var vis = getVisibleIndices();
if (!vis.length) return;
var pos = vis.indexOf(currentIndex);
if (pos === -1) {
currentIndex = vis[0];
} else {
var nextPos = (pos + delta + vis.length) % vis.length;
currentIndex = vis[nextPos];
}
renderPizza();
}
function mountCategoryNav() {
var scroll = document.getElementById("cat-nav-scroll");
if (!scroll) return;
var parts = [];
CATEGORY_ORDER.forEach(function (id) {
var d = CATEGORY_DEFS[id];
if (!d) return;
parts.push(
'<button type="button" class="cat-nav__btn" data-cat="' +
escapeAttr(id) +
'" aria-pressed="false" title="' +
escapeAttr(d.label + ": " + d.hint + " — erneut klicken zum Abwählen") +
'">' +
'<span class="cat-nav__icon-wrap">' +
categoryIconInnerHtml(d) +
"</span>" +
'<span class="cat-nav__label">' +
escapeHtml(d.label) +
"</span>" +
'<span class="cat-nav__hint">' +
escapeHtml(d.hint) +
"</span></button>"
);
});
scroll.innerHTML = parts.join("");
scroll.querySelectorAll(".cat-nav__btn").forEach(function (btn) {
btn.addEventListener("click", function () {
var cat = btn.getAttribute("data-cat");
if (!cat) return;
toggleCategory(cat);
syncFilterAfterChange();
});
});
}
const pizzas = [
{
name: "Margherita",
image: "magarita.png",
imageCut: "magaritta angeschnitten.png",
categories: ["vegetarier", "kaese", "einfach", "kinder", "goettlich"],
ingredients: [
"Tomatensauce San Marzano",
"Fior di Latte Mozzarella",
"Frisches Basilikum",
"Olivenöl extra vergine",
"Hefeteig 48h",
],
macros: [
{ label: "Energie", value: "724 kcal" },
{ label: "Fett", value: "26 g" },
{ label: "davon gesättigt", value: "11 g" },
{ label: "Kohlenhydrate", value: "84 g" },
{ label: "Eiweiß", value: "28 g" },
{ label: "Salz", value: "2,1 g" },
],
},
{
name: "Salami",
image: "salami.png",
imageCut: "salami angeschnitten.png",
categories: ["fleisch", "akut", "neuheit", "ohne_oliven"],
ingredients: [
"Tomatensauce",
"Mozzarella",
"Luftgetrocknete Salami",
"Oregano",
"Olivenöl",
"Hefeteig 48h",
],
macros: [
{ label: "Energie", value: "812 kcal" },
{ label: "Fett", value: "34 g" },
{ label: "davon gesättigt", value: "14 g" },
{ label: "Kohlenhydrate", value: "82 g" },
{ label: "Eiweiß", value: "32 g" },
{ label: "Salz", value: "2,8 g" },
],
},
{
name: "Diavolo",
image: "Pizza Diavolo.png",
imageCut: "Pizza Diavolo angeschnitten.png",
categories: ["fleisch", "akut", "ohne_pilze", "neuheit"],
ingredients: [
"Tomatensauce San Marzano",
"Mozzarella",
"Scharfe Salami",
"Peperoncini",
"Olivenöl extra vergine",
"Hefeteig 48h",
],
macros: [
{ label: "Energie", value: "856 kcal" },
{ label: "Fett", value: "36 g" },
{ label: "davon gesättigt", value: "15 g" },
{ label: "Kohlenhydrate", value: "83 g" },
{ label: "Eiweiß", value: "33 g" },
{ label: "Salz", value: "3,0 g" },
],
},
];
let currentIndex = 0;
let quantity = 1;
let previewImageTimer = null;
let previewSwapGen = 0;
let previewPhaseFull = true;
let previewNextFromBottom = true;
let previewTimerRaf = null;
function encodeAssetPath(path) {
return path.split("/").map(encodeURIComponent).join("/");
}
function clearPreviewImageTimer() {
if (previewImageTimer !== null) {
clearTimeout(previewImageTimer);
previewImageTimer = null;
}
stopTimerSlider();
}
function stopTimerSlider() {
if (previewTimerRaf !== null) {
cancelAnimationFrame(previewTimerRaf);
previewTimerRaf = null;
}
var fill = document.getElementById("preview-timer-fill");
if (fill) fill.style.width = "0%";
}
function startTimerSlider(expectedGen) {
stopTimerSlider();
var fill = document.getElementById("preview-timer-fill");
var secEl = document.getElementById("preview-timer-sec");
if (!fill) return;
var start = performance.now();
function tick(now) {
if (expectedGen !== previewSwapGen) return;
var elapsed = now - start;
var p = Math.min(100, (elapsed / IMAGE_SWAP_MS) * 100);
fill.style.width = p + "%";
if (secEl) {
var left = Math.max(0, IMAGE_SWAP_MS - elapsed);
var s = Math.ceil(left / 1000);
if (s <= 0) s = 0;
secEl.textContent = s + " s";
}
if (p >= 100) {
previewTimerRaf = null;
return;
}
previewTimerRaf = requestAnimationFrame(tick);
}
previewTimerRaf = requestAnimationFrame(tick);
}
const preview = document.getElementById("preview");
const previewTimerEl = document.getElementById("preview-timer");
const previewTitle = document.getElementById("preview-title");
const previewBadges = document.getElementById("preview-badges");
const previewIngredients = document.getElementById("preview-ingredients");
const previewMacros = document.getElementById("preview-macros");
const carouselLayerA = document.getElementById("carousel-layer-a");
const carouselLayerB = document.getElementById("carousel-layer-b");
const carouselImgA = carouselLayerA.querySelector("img");
const carouselImgB = carouselLayerB.querySelector("img");
const btnPrev = document.getElementById("carousel-prev");
const btnNext = document.getElementById("carousel-next");
const btnClose = document.getElementById("preview-close");
const qtyNum = document.getElementById("qty-num");
const qtyUp = document.getElementById("qty-up");
const qtyDown = document.getElementById("qty-down");
const btnAddCart = document.getElementById("btn-add-cart");
function resetPreviewLayers() {
[carouselLayerA, carouselLayerB].forEach(function (layer) {
layer.style.transition = "none";
layer.style.transform = "";
layer.style.zIndex = "";
});
}
function setupPreviewLoop() {
clearPreviewImageTimer();
previewSwapGen++;
const gen = previewSwapGen;
const p = pizzas[currentIndex];
previewPhaseFull = true;
previewNextFromBottom = true;
resetPreviewLayers();
carouselImgA.alt = "Pizza " + p.name;
carouselImgB.alt = "Pizza " + p.name;
if (!p.imageCut) {
if (previewTimerEl) previewTimerEl.hidden = true;
carouselImgA.src = encodeAssetPath(p.image);
carouselLayerA.style.transform = "translateY(0)";
carouselLayerA.style.zIndex = "2";
carouselImgB.removeAttribute("src");
carouselLayerB.style.transform = "translateY(110%)";
carouselLayerB.style.zIndex = "1";
return;
}
if (previewTimerEl) previewTimerEl.hidden = false;
carouselImgA.src = encodeAssetPath(p.image);
carouselImgB.src = encodeAssetPath(p.imageCut);
carouselLayerA.style.transform = "translateY(0)";
carouselLayerA.style.zIndex = "2";
carouselLayerB.style.transform = "translateY(110%)";
carouselLayerB.style.zIndex = "1";
previewImageTimer = setTimeout(function () {
runPreviewImageSwap(gen);
}, IMAGE_SWAP_MS);
startTimerSlider(gen);
}
function runPreviewImageSwap(expectedGen) {
if (expectedGen !== previewSwapGen) return;
const p = pizzas[currentIndex];
if (!p || !p.imageCut) return;
stopTimerSlider();
var outgoing =
carouselLayerA.style.zIndex === "2" ? carouselLayerA : carouselLayerB;
var incoming =
outgoing === carouselLayerA ? carouselLayerB : carouselLayerA;
var outgoingImg = outgoing.querySelector("img");
var incomingImg = incoming.querySelector("img");
var nextFull = !previewPhaseFull;
var nextPath = nextFull ? p.image : p.imageCut;
incomingImg.src = encodeAssetPath(nextPath);
incomingImg.alt = "Pizza " + p.name;
var fromBottom = previewNextFromBottom;
previewNextFromBottom = !previewNextFromBottom;
incoming.style.transition = "none";
outgoing.style.transition = "none";
incoming.style.transform = fromBottom
? "translateY(110%)"
: "translateY(-110%)";
outgoing.style.transform = "translateY(0)";
incoming.style.zIndex = "2";
outgoing.style.zIndex = "1";
outgoing.offsetHeight;
var ease = "cubic-bezier(0.4, 0, 0.2, 1)";
incoming.style.transition =
"transform " + PREVIEW_SLIDE_MS / 1000 + "s " + ease;
outgoing.style.transition =
"transform " + PREVIEW_SLIDE_MS / 1000 + "s " + ease;
function onEnd(ev) {
if (ev.propertyName !== "transform") return;
if (expectedGen !== previewSwapGen) return;
incoming.removeEventListener("transitionend", onEnd);
previewPhaseFull = nextFull;
outgoing.style.transition = "none";
outgoing.style.transform = "translateY(110%)";
outgoing.style.zIndex = "1";
incoming.style.zIndex = "2";
previewImageTimer = setTimeout(function () {
runPreviewImageSwap(expectedGen);
}, IMAGE_SWAP_MS);
startTimerSlider(expectedGen);
}
incoming.addEventListener("transitionend", onEnd);
requestAnimationFrame(function () {
requestAnimationFrame(function () {
if (expectedGen !== previewSwapGen) return;
incoming.style.transform = "translateY(0)";
outgoing.style.transform = "translateY(110%)";
});
});
}
function renderPizza() {
const p = pizzas[currentIndex];
previewTitle.textContent = p.name;
if (previewBadges) {
previewBadges.innerHTML = p.categories
? renderCategoryBadges(p.categories, "pizza-badge pizza-badge--preview")
: "";
}
previewIngredients.innerHTML = p.ingredients
.map(function (ing) {
return "<li>" + ing + "</li>";
})
.join("");
previewMacros.innerHTML = p.macros
.map(function (m) {
return "<li><strong>" + m.label + "</strong>" + m.value + "</li>";
})
.join("");
setupPreviewLoop();
}
function openPreview(index) {
currentIndex = Math.max(0, Math.min(pizzas.length - 1, index));
quantity = 1;
qtyNum.textContent = String(quantity);
updateQtyButtons();
renderPizza();
preview.hidden = false;
preview.classList.add("is-visible");
document.body.classList.add("preview-open");
btnClose.focus();
}
function closePreview() {
clearPreviewImageTimer();
if (previewTimerEl) previewTimerEl.hidden = true;
previewSwapGen++;
resetPreviewLayers();
preview.classList.remove("is-visible");
preview.hidden = true;
document.body.classList.remove("preview-open");
}
function updateQtyButtons() {
qtyDown.disabled = quantity <= 1;
qtyUp.disabled = quantity >= 99;
}
document.querySelectorAll(".img-col--clickable").forEach(function (el) {
el.addEventListener("click", function () {
const idx = parseInt(el.getAttribute("data-pizza-index"), 10);
openPreview(idx);
});
el.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
const idx = parseInt(el.getAttribute("data-pizza-index"), 10);
openPreview(idx);
}
});
});
btnPrev.addEventListener("click", function () {
stepPreview(-1);
});
btnNext.addEventListener("click", function () {
stepPreview(1);
});
btnClose.addEventListener("click", closePreview);
document.addEventListener("keydown", function (e) {
if (!preview.classList.contains("is-visible")) return;
if (e.key === "Escape") closePreview();
});
qtyUp.addEventListener("click", function () {
if (quantity < 99) {
quantity++;
qtyNum.textContent = String(quantity);
updateQtyButtons();
}
});
qtyDown.addEventListener("click", function () {
if (quantity > 1) {
quantity--;
qtyNum.textContent = String(quantity);
updateQtyButtons();
}
});
btnAddCart.addEventListener("click", function () {
const p = pizzas[currentIndex];
alert(
p.name +
" × " +
quantity +
" — (Demo: hier käme der echte Warenkorb.)"
);
});
(function () {
var newsHero = document.getElementById("news-hero");
var catNav = document.getElementById("cat-nav");
if (!newsHero || !catNav) return;
function applyNewsIntersection(entry) {
var inNews = entry.isIntersecting;
if (inNews) {
catNav.classList.add("cat-nav--hidden");
document.body.classList.remove("cat-nav-visible");
catNav.setAttribute("aria-hidden", "true");
} else {
catNav.classList.remove("cat-nav--hidden");
document.body.classList.add("cat-nav-visible");
catNav.setAttribute("aria-hidden", "false");
}
}
var io = new IntersectionObserver(
function (entries) {
entries.forEach(applyNewsIntersection);
},
{ root: null, threshold: 0, rootMargin: "0px" }
);
io.observe(newsHero);
})();
mountCategoryNav();
applyFilter();
mountOverviewBadges();
})();
</script>
</body>
</html>