Files
pizza-dummy/indexStyleWhiteRed.html
2026-04-01 15:09:12 +02:00

1801 lines
49 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=Alfa+Slab+One&family=Bebas+Neue&family=Lora:ital,wght@0,400;0,600;0,700;1,400&display=swap" rel="stylesheet" />
<style>
:root {
--red: #d32f2f;
--white: #ffffff;
--paper: #fdf6f0;
--ink: #3a1015;
--checker-edge: clamp(10px, 1.6vw, 18px);
--checker-tile: 20px;
--news-cut-y: clamp(6rem, 20vw, 15rem);
--news-cut-x: clamp(7.8rem, 26vw, 19.5rem);
--frame-thick: 3px;
--frame-thin: 1px;
--frame-gap: 5px;
}
*, *::before, *::after { box-sizing: border-box; }
html, body {
margin: 0;
min-height: 100%;
color: var(--ink);
font-family: "Lora", "Georgia", serif;
}
body {
display: flex;
flex-direction: column;
background-color: var(--paper);
background-image: repeating-conic-gradient(
var(--white) 0% 25%,
var(--red) 25% 50%,
var(--white) 50% 75%,
var(--red) 75% 100%
);
background-size: var(--checker-tile) var(--checker-tile);
}
body.preview-open { overflow: hidden; }
.news-hero,
.pizza-row,
.preview {
isolation: isolate;
}
.pizza-row { position: relative; }
.news-hero::before,
.pizza-row::before,
.preview::before {
content: "";
position: absolute;
inset: var(--checker-edge);
z-index: 0;
background: var(--paper);
border: var(--frame-thick) solid var(--red);
outline: var(--frame-thin) solid var(--red);
outline-offset: var(--frame-gap);
pointer-events: none;
}
.news-hero::after {
content: "";
position: absolute;
inset: var(--checker-edge);
z-index: 1;
background-color: var(--paper);
background-image: url("bg-pic.png");
background-size: min(58vw, 720px) auto;
background-position: right center;
background-repeat: no-repeat;
pointer-events: none;
}
.news-hero::before,
.news-hero::after {
clip-path: polygon(
var(--news-cut-x) 0%,
100% 0%,
100% calc(100% - var(--news-cut-y)),
calc(100% - var(--news-cut-x)) 100%,
0% 100%,
0% var(--news-cut-y)
);
}
.news-hero > *,
.pizza-row > * {
position: relative;
z-index: 2;
}
/* ——— Category Nav ——— */
.cat-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 40;
flex-shrink: 0;
background: var(--paper);
border-bottom: 3px solid var(--red);
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; }
}
.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 ——— */
.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(2rem, 5vw, 4rem);
padding-right: min(42vw, 28rem);
text-align: left;
background: transparent;
}
.news-hero__inner {
position: relative;
z-index: 2;
max-width: min(36rem, 100%);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1.25rem;
}
.news-hero__badge {
display: inline-block;
font-family: "Bebas Neue", Impact, sans-serif;
font-size: clamp(0.85rem, 1.8vw, 1rem);
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--red);
border: 2px dashed var(--red);
padding: 0.3em 0.8em;
transform: rotate(-2deg);
}
.news-hero__title {
font-family: "Alfa Slab One", "Bebas Neue", Impact, sans-serif;
font-size: clamp(3rem, 11vw, 5.5rem);
line-height: 0.92;
letter-spacing: 0.02em;
margin: 0;
max-width: 16ch;
color: var(--ink);
text-transform: uppercase;
}
.news-hero__text {
margin: 0;
max-width: 28rem;
font-size: clamp(1rem, 2vw, 1.15rem);
font-weight: 400;
line-height: 1.6;
color: var(--ink);
}
.news-hero__text strong {
color: var(--red);
font-weight: 700;
}
.news-hero__hint {
position: absolute;
bottom: clamp(1.5rem, 3vh, 2.5rem);
left: clamp(2rem, 5vw, 4rem);
font-family: "Bebas Neue", Impact, sans-serif;
font-size: 0.85rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--red);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.35rem;
}
.news-hero__hint::after {
content: "";
width: 2px;
height: 2rem;
background: var(--red);
animation: news-hint-bob 1.8s ease-in-out infinite;
}
@keyframes news-hint-bob {
0%, 100% { transform: translateY(0); opacity: 0.5; }
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(1.25rem, 4vw, 2rem); }
.news-hero::after {
background-size: min(88vw, 480px) auto;
background-position: right bottom;
}
}
/* ——— Category Nav Buttons ——— */
.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: var(--red) transparent;
}
.cat-nav__scroll::-webkit-scrollbar { height: 4px; }
.cat-nav__scroll::-webkit-scrollbar-thumb {
background: var(--red);
border-radius: 0;
}
.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.3rem;
min-width: 5rem;
max-width: 6.25rem;
padding: 0.4rem 0.45rem 0.5rem;
border: 2px dashed var(--red);
border-radius: 0;
background: var(--white);
color: var(--ink);
font-family: inherit;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
.cat-nav__btn:hover {
background: var(--paper);
}
.cat-nav__btn.is-active {
background: var(--red);
color: var(--white);
border-style: solid;
}
.cat-nav__btn.is-active .cat-nav__label,
.cat-nav__btn.is-active .cat-nav__hint {
color: var(--white);
}
.cat-nav__btn.is-active .cat-nav__icon-wrap {
filter: brightness(0) invert(1);
}
.cat-nav__icon-wrap {
width: 3rem;
height: 3rem;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
}
.cat-nav__icon-wrap svg,
.cat-nav__icon-wrap .cat-icon-img {
width: 2.5rem;
height: 2.5rem;
display: block;
}
.cat-nav__icon-wrap .cat-icon-img { object-fit: contain; }
.cat-nav__label {
font-family: "Bebas Neue", Impact, sans-serif;
font-size: 0.7rem;
line-height: 1.15;
text-align: center;
color: var(--ink);
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cat-nav__hint {
font-size: 0.56rem;
font-weight: 400;
line-height: 1.2;
text-align: center;
color: var(--red);
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(--ink);
font-size: 1rem;
font-style: italic;
}
.filter-empty.is-visible { display: flex; }
.page {
width: 100vw;
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
/* ——— Pizza Grid ——— */
.pizza-grid {
flex: 1 1 auto;
min-height: 0;
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: stretch;
gap: calc(var(--checker-edge) * 2);
background: transparent;
}
.pizza-row {
flex: 1 1 0;
min-width: 0;
min-height: 0;
width: auto;
display: flex;
flex-direction: column;
align-items: stretch;
background: transparent;
}
.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(1rem, 2vw, 1.5rem) clamp(0.75rem, 1.5vw, 1.25rem) 0.5rem;
background: transparent;
}
.img-col {
flex: 1 1 auto;
position: relative;
justify-content: center;
align-items: center;
padding: clamp(0.5rem, 1.2vw, 1rem) clamp(0.5rem, 1.2vw, 0.85rem) clamp(0.75rem, 1.5vw, 1.25rem);
background: transparent;
}
.pizza-grid h1 {
font-size: clamp(1.6rem, 3vw, 2.5rem);
margin-bottom: 0.4rem;
}
.pizza-grid .subtitle { margin-bottom: 0.5rem; }
.pizza-grid .details {
max-width: 100%;
font-size: clamp(0.8rem, 1.3vw, 0.95rem);
line-height: 1.5;
}
.pizza-grid .price {
margin-top: 0.75rem;
font-size: clamp(1rem, 1.6vw, 1.15rem);
}
.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;
gap: calc(var(--checker-edge) * 2);
}
.pizza-row {
flex: 0 0 auto;
width: 100%;
}
.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: 3px dashed var(--red);
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::before {
content: "";
position: absolute;
inset: -4%;
border: 2px dashed var(--red);
border-radius: 50%;
opacity: 0.35;
pointer-events: none;
}
.pizza-frame img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
animation: pizza-spin 48s linear infinite;
will-change: transform;
pointer-events: none;
}
.pizza-frame:hover img { animation-play-state: paused; }
@keyframes pizza-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ——— Typography ——— */
h1 {
font-family: "Alfa Slab One", "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(--ink);
text-transform: uppercase;
}
.subtitle {
font-family: "Bebas Neue", Impact, sans-serif;
font-size: clamp(0.9rem, 1.8vw, 1.05rem);
color: var(--red);
text-transform: uppercase;
letter-spacing: 0.22em;
margin-bottom: 0.75rem;
border-bottom: 2px dashed var(--red);
padding-bottom: 0.3em;
display: inline-block;
}
.details {
font-weight: 400;
font-size: clamp(0.9rem, 1.6vw, 1.05rem);
line-height: 1.6;
color: var(--ink);
max-width: 28em;
}
.details strong {
color: var(--red);
font-weight: 700;
}
.price {
margin-top: 1.25rem;
font-family: "Bebas Neue", Impact, sans-serif;
font-size: clamp(1.2rem, 2.2vw, 1.5rem);
color: var(--red);
letter-spacing: 0.05em;
display: inline-block;
border: 2px dashed var(--red);
padding: 0.2em 0.6em;
transform: rotate(-2deg);
}
/* ——— Badges ——— */
.pizza-badges {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin: 0.35rem 0 0.85rem;
max-width: 22rem;
}
.pizza-badge {
width: 3rem;
height: 3rem;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: transparent;
border: 1px dashed var(--red);
color: var(--red);
cursor: default;
transition: transform 0.15s ease;
}
.pizza-badge:hover { transform: scale(1.08); }
.pizza-badge svg,
.pizza-badge .cat-icon-img {
width: 2.25rem;
height: 2.25rem;
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: 2rem;
height: 2rem;
}
.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: transparent;
padding: clamp(0.75rem, 2vw, 1.5rem);
}
.preview.is-visible { display: flex; }
.preview__grid {
position: relative;
z-index: 2;
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.75rem, 2vw, 1.5rem);
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: 3rem; }
}
.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.75rem;
}
.preview__back {
position: absolute;
top: clamp(0.75rem, 2vw, 1.25rem);
left: clamp(0.75rem, 2vw, 1.25rem);
z-index: 3;
background: var(--red);
border: 2px dashed var(--paper);
color: var(--white);
font-family: "Bebas Neue", Impact, sans-serif;
font-size: 1rem;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 0.4rem 0.9rem;
border-radius: 0;
cursor: pointer;
outline: 2px solid var(--red);
outline-offset: -1px;
}
.preview__back:hover {
background: var(--ink);
}
.preview-name {
font-family: "Alfa Slab One", "Bebas Neue", Impact, sans-serif;
font-size: clamp(2.2rem, 5.5vw, 3.8rem);
line-height: 1;
margin: 0 0 1rem;
color: var(--ink);
text-transform: uppercase;
}
.preview-label {
font-family: "Bebas Neue", Impact, sans-serif;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.15em;
color: var(--red);
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: 400;
color: var(--ink);
padding: 0.4rem 0;
border-bottom: 1px dashed var(--red);
}
.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.45rem 0;
color: var(--ink);
border-bottom: 1px dashed var(--red);
}
.macro-list li strong {
display: block;
font-family: "Bebas Neue", Impact, sans-serif;
color: var(--red);
font-weight: 400;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 0.1rem;
}
.carousel-wrap {
position: relative;
width: 100%;
max-width: min(92vmin, 760px);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
border: 2px dashed var(--red);
}
.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;
}
.carousel-btn {
position: fixed;
top: 50%;
transform: translateY(-50%);
width: 48px;
height: 48px;
border-radius: 0;
border: 2px dashed var(--red);
background: var(--white);
color: var(--ink);
font-family: "Bebas Neue", Impact, sans-serif;
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 110;
}
.carousel-btn:hover {
background: var(--red);
color: var(--white);
border-style: solid;
}
.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-family: "Bebas Neue", Impact, sans-serif;
font-size: 0.78rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--red);
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(--ink);
font-weight: 700;
}
.preview-timer__track {
height: 8px;
border-radius: 0;
background: var(--paper);
overflow: hidden;
border: 1px solid var(--red);
}
.preview-timer__fill {
height: 100%;
width: 0%;
background: var(--red);
}
.cart-combo {
display: inline-flex;
align-items: stretch;
border: 2px solid var(--red);
border-radius: 0;
overflow: hidden;
background: var(--white);
}
.cart-combo__qty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.25rem 0.4rem;
border-right: 2px solid var(--red);
min-width: 3.25rem;
}
.qty-arrow {
width: 100%;
border: none;
background: transparent;
color: var(--ink);
font-size: 0.7rem;
line-height: 1;
padding: 0.25rem 0.5rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.qty-arrow:hover:not(:disabled) { color: var(--red); }
.qty-arrow:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.qty-value {
font-family: "Bebas Neue", Impact, sans-serif;
font-weight: 400;
font-size: 1.3rem;
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.7rem 1.25rem;
border: none;
border-radius: 0;
background: var(--red);
color: var(--white);
font-family: "Bebas Neue", Impact, sans-serif;
font-size: 1.05rem;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
}
.btn-cart:hover { background: var(--ink); }
.btn-cart svg {
width: 1.1em;
height: 1.1em;
}
.preview-hint {
font-size: 0.78rem;
color: var(--red);
margin-top: 0.75rem;
text-align: center;
font-style: italic;
}
</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>