Responsive Sektionskanten mit shape()
Vier Sektionskanten mit fester Höhe in rem: Bogen, Welle, Schräge und Riss wachsen mit dem ziehbaren Container in die Breite, nie in die Höhe.
Weingut, Hofladen, Boutique-Hotel: eine Hang- oder Uferlinie aus der Landschaft als wiederkehrende Kante zwischen Hero und Kapitel und vor dem Footer.
Die Unterkante einer Sektion ist kein Strich, sondern eine Form: ein flacher Bogen, eine Welle, eine Schräge oder ein gerissener Rand. Die Höhe der Kante bleibt fest, etwa 2rem, ihre Breite folgt stufenlos dem Container. So sieht der Bogen am Telefon und am breiten Bildschirm gleich aus, statt sich wie ein gestrecktes SVG zu verzerren.
Eine einzige wiederkehrende Kante aus der Marke (Wasserlinie, Hang, Holzkante, Papierriss) gliedert die Seite ohne Linien und ohne Kicker. Die Starter Haubentaucher und Sievers nutzen genau diese Idee. Als Muster wird sie wiederverwendbar und bleibt an jeder Breite sauber.
Wann einsetzen
Wenn die Marke eine Form hergibt: Weinberg-Hang, Seeufer, Werkstatt-Schräge, Papierkante. Zwischen Hero und erstem Kapitel oder vor dem Footer.
Wann nicht
Nicht als Deko ohne Bezug zur Marke. Nicht zwischen jeder Sektion (höchstens zwei Kanten pro Seite). Keine Wellen bei Kanzlei, Praxis oder Bestattung. Nicht animiert als Dauerbewegung. Generik-Gefahr: die Welle aus Landingpage-Baukästen der 2010er.
Varianten
- bogen: flacher Bogen mit fester Höhe
- welle: zwei bis drei Wellen über die Breite
- schraege: gerade Schräge (geht auch mit polygon)
- riss: unregelmäßige Kante aus vielen Punkten, wie gerissenes Papier
Umsetzung
- Token
--kante-hoehe(z. B. 2rem) und--kante-formim Rezept festlegen. - Sektion mit
clip-path: shape(from 0 0, line to 100% 0, line to 100%calc(100% - var(--kante-hoehe)), curve to 0calc(100% - var(--kante-hoehe))with 50% 100%, close) für den Bogen. Welle und Riss mit mehreren Segmenten. - padding-block-end mindestens so groß wie
--kante-hoehe, damit kein Inhalt beschnitten wird. Die nächste Sektion mitmargin-block-start:calc(var(--kante-hoehe) * -1)darunterschieben. @supports(clip-path: shape(from 0 0, line to 0 0)) als Schutz. Sonst Schräge perpolygon(), Welle per mask mit radial-gradient, im Zweifel gerade Kante.- Kantenhöhe in rem, damit sie bei Zoom mitwächst.
- Nicht animieren. Falls doch, nur bei
prefers-reduced-motion: no-preference und einmalig.
Robust umsetzen
Telefon. Gleiche Kantenhöhe, die Form passt sich der Breite an. Der Riss hat weniger Punkte.
Ohne Bewegung. Statisch, nichts zu tun.
Barrierefrei. Reine Deko, kein Inhalt in der Kante. Innenabstand mindestens so groß wie die Kantenhöhe, damit Text bei 200 % Zoom und erhöhten Textabständen nicht abgeschnitten wird. Kontrast des Textes zur Fläche wie sonst. Im Kontrastmodus (forced-colors) bleibt die Kante unsichtbar, die Sektion trennt dann ein Rahmen.
Code
<!DOCTYPE html>
<!--
Responsive Sektionskanten mit shape()
Fundus · Hero & Section-Layouts · https://cecodesign.de/lab/fundus/hero-layouts/sektionskante-shape/
Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Responsive Sektionskanten mit shape() · Fundus-Demo</title>
<script>document.documentElement.classList.add('js');if(matchMedia('(prefers-reduced-motion: reduce)').matches)document.documentElement.classList.add('reduziert')</script>
<style>
@font-face { font-family: 'Geist Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/geist-mono:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-display: swap; }
/* Welt bogen: Rezept weingut-hofladen-herkunft */
@font-face { font-family: 'EB Garamond'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/eb-garamond:vf@latest/latin-wght-normal.woff') format('woff'); font-weight: 400 800; font-display: swap; }
@font-face { font-family: 'EB Garamond'; font-style: italic; src: url('https://cdn.jsdelivr.net/fontsource/fonts/eb-garamond:vf@latest/latin-wght-italic.woff2') format('woff2-variations'); font-weight: 400 800; font-display: swap; }
@font-face { font-family: 'DM Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/dm-mono@latest/latin-500-normal.woff2') format('woff2'); font-weight: 500; font-display: swap; }
/* Welt welle: Rezept gastro-warm-appetitlich */
@font-face { font-family: 'Fraunces'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/fraunces:vf@latest/latin-full-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-display: swap; }
@font-face { font-family: 'Fraunces'; font-style: italic; src: url('https://cdn.jsdelivr.net/fontsource/fonts/fraunces@latest/latin-300-italic.woff2') format('woff2'); font-weight: 300; font-display: swap; }
@font-face { font-family: 'Commissioner'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/commissioner:vf@latest/latin-full-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-display: swap; }
/* Welt schraege: Rezept handwerk-werkstatt-klartext */
@font-face { font-family: 'Special Gothic'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/special-gothic:vf@latest/latin-standard-normal.woff2') format('woff2-variations'); font-weight: 400 700; font-stretch: 75% 125%; font-display: swap; }
@font-face { font-family: 'Atkinson Hyperlegible Next'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/atkinson-hyperlegible-next:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 200 800; font-display: swap; }
/* Welt riss: Rezept kultur-programm-haltung */
@font-face { font-family: 'Schibsted Grotesk'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/schibsted-grotesk:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 400 900; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/ibm-plex-mono@latest/latin-500-normal.woff2') format('woff2'); font-weight: 500; font-display: swap; }
:root {
--bank: #161615; --hell: #efebe3; --leise: rgba(239, 235, 227, .68); --linie: rgba(239, 235, 227, .16);
--akzent: #d4643a;
--ui: 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--bank); color: var(--hell); font-family: 'Geist Mono', ui-monospace, monospace; -webkit-font-smoothing: antialiased; }
body { min-height: 100svh; padding: clamp(14px, 3vw, 40px); }
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--akzent); outline-offset: 3px; }
.mono { font: 500 11px/1.35 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; }
.nur-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* ——— Werkbank: Lineal, Rahmen, Griff ——— */
.kopf { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 8px 24px; margin-bottom: 10px; color: var(--leise); }
.kopf b { color: var(--hell); font-weight: 500; font-variant-numeric: tabular-nums; }
.kopf .k b { color: var(--akzent); }
.buehne { position: relative; }
.halter { position: relative; width: max-content; transform-origin: 0 0; }
.rahmen { container: rahmen / inline-size; position: relative; width: 1240px; min-width: 320px; overflow: clip; border-radius: 3px; box-shadow: 0 30px 80px -30px rgba(0, 0, 0, .8); }
.griff {
position: absolute; top: 220px; right: -16px; z-index: 3; width: 10px; height: 64px; border-radius: 6px;
background: var(--hell); cursor: ew-resize; touch-action: none; box-shadow: 0 0 0 4px var(--bank);
transition: scale var(--ui), background-color var(--ui);
}
.griff::before { content: ""; position: absolute; inset: 22px 3px; border-block: 1px solid rgba(20, 20, 19, .5); }
.griff::after { content: ""; position: absolute; inset: -12px -14px; }
.griff:hover, .griff.zieht, .vorfuehrung .griff { background: var(--akzent); scale: 1.15; }
/* ——— Pult ——— */
.pult { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 22px; padding-bottom: 18px; color: var(--leise); }
.wahl { display: flex; flex-wrap: wrap; gap: 3px; padding: 3px; border: 1px solid var(--linie); border-radius: 999px; }
.pult button { font: inherit; letter-spacing: inherit; text-transform: inherit; color: var(--leise); background: none; border: 0; border-radius: 999px; padding: 0 12px; min-height: 32px; cursor: pointer; transition: color var(--ui), background-color var(--ui); }
.pult button:hover { color: var(--hell); }
.pult button[aria-pressed="true"] { background: var(--hell); color: var(--bank); }
.schalter { border: 1px solid var(--linie) !important; }
.regler { display: flex; align-items: center; gap: 10px; }
.regler input { width: clamp(110px, 16vw, 200px); accent-color: var(--akzent); }
.regler output { min-width: 4.6em; color: var(--hell); font-variant-numeric: tabular-nums; }
.stand { color: var(--hell); }
.stand i { font-style: normal; color: #8fd0a4; }
.stand i.nein { color: #f0a383; }
/* ——— Werte unter der Bühne ——— */
.werte { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 6px 32px; margin-top: 28px; padding-top: 18px; border-top: 1px solid var(--linie); color: var(--leise); font: 400 11.5px/1.6 'Geist Mono', ui-monospace, monospace; }
.werte div { display: flex; gap: 12px; min-width: 0; }
.werte dt { color: var(--hell); flex: none; min-width: 8.5em; }
.werte dd { overflow-wrap: anywhere; }
/* =====================================================================
DAS MUSTER: Kante mit fester Höhe in rem, Breite folgt dem Container
===================================================================== */
.welt > * { position: relative; }
.welt > :nth-child(1) { z-index: 4; } .welt > :nth-child(2) { z-index: 3; } .welt > :nth-child(3) { z-index: 2; } .welt > :nth-child(4) { z-index: 1; }
/* --k sitzt an der Welt, damit auch die folgende Sektion (margin, padding) die Kantenhöhe kennt */
.welt { --k: 2.5rem; }
.kante { overflow: clip; padding-block-end: calc(var(--k) + var(--luft, 2rem)); }
.kante + .unter { margin-block-start: calc(var(--k) * -1); padding-block-start: calc(var(--k) + var(--luft-oben, 3rem)); }
/* Rückfall für alle Browser (läuft immer, shape() überschreibt unten) */
.k-bogen { border-end-start-radius: 50% var(--k); border-end-end-radius: 50% var(--k); }
.k-welle {
/* Bogenreihe per mask: feste Wellenlänge 7rem statt fester Wellenzahl */
-webkit-mask: radial-gradient(50% 100% at 50% 0, #000 calc(100% - .5px), #0000 100%) bottom left / 7rem var(--k) repeat-x, linear-gradient(#000 0 0) top / 100% calc(100% - var(--k)) no-repeat;
mask: radial-gradient(50% 100% at 50% 0, #000 calc(100% - .5px), #0000 100%) bottom left / 7rem var(--k) repeat-x, linear-gradient(#000 0 0) top / 100% calc(100% - var(--k)) no-repeat;
}
.k-schraege { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--k)), 0 100%); }
/* Moderne Kante: shape() mischt Prozent und rem */
@supports (clip-path: shape(from 0 0, line to 0 0)) {
:root:not(.rueckfall) .k-bogen {
border-radius: 0;
/* Kontrollpunkt bei 100% + Kantenhöhe: die Kurvenmitte erreicht genau die Unterkante. 32 % statt 50 %: ein Hang, kein Halbmond */
clip-path: shape(from 0 0, line to 100% 0, line to 100% calc(100% - var(--k)), curve to 0 calc(100% - var(--k)) with 32% calc(100% + var(--k)), close);
}
:root:not(.rueckfall) .k-welle {
-webkit-mask: none; mask: none;
/* fünf Halbwellen: die Zahl bleibt, die Wellenlänge wächst mit der Breite, die Höhe bleibt --k */
clip-path: shape(from 0 0, line to 100% 0, line to 100% calc(100% - var(--k)), curve to 80% 100% with 92.4% calc(100% - var(--k)) / 87.6% 100%, curve to 60% calc(100% - var(--k)) with 72.4% 100% / 67.6% calc(100% - var(--k)), curve to 40% 100% with 52.4% calc(100% - var(--k)) / 47.6% 100%, curve to 20% calc(100% - var(--k)) with 32.4% 100% / 27.6% calc(100% - var(--k)), curve to 0% 100% with 12.4% calc(100% - var(--k)) / 7.6% 100%, close);
}
:root:not(.rueckfall) .k-schraege { clip-path: shape(from 0 0, line to 100% 0, line to 100% calc(100% - var(--k)), line to 0 100%, close); }
}
/* Riss: zwei Lagen. Außen der weiße Papierkern, innen die bedruckte Fläche, beide mit eigener Kante.
Der Riss braucht keine Kurven: polygon() mit calc() läuft überall, shape() setzt nur feine Fasern dazu. */
.riss { background: #fbfaf7; padding-block-end: .5rem; }
.riss > .riss-innen { padding-block-end: calc(var(--k) + var(--luft, 2.5rem)); }
.riss + .unter { margin-block-start: calc((var(--k2) + .5rem) * -1); padding-block-start: calc(var(--k2) + .5rem + var(--luft-oben, 3rem)); }
/* Folgt ein Riss auf einen Riss, gehört der Abstand in die innere Lage, sonst zeigt sich oben der Papierkern */
.riss + .unter.riss { padding-block-start: 0; }
.riss + .unter.riss > .riss-innen { padding-block-start: calc(var(--k2) + .5rem + var(--luft-oben, 3rem)); }
.riss { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--k2) * 0.51), 95.25% calc(100% - var(--k2) * 0.02), 92.21% calc(100% - var(--k2) * 0.45), 88.51% calc(100% - var(--k2) * 0.08), 84.61% calc(100% - var(--k2) * 0.67), 82.21% calc(100% - var(--k2) * 0.85), 77.36% calc(100% - var(--k2) * 0.13), 74.14% calc(100% - var(--k2) * 0.04), 71.72% calc(100% - var(--k2) * 0.52), 68.24% calc(100% - var(--k2) * 0.13), 65.45% calc(100% - var(--k2) * 0.51), 61.92% calc(100% - var(--k2) * 0.20), 56% calc(100% - var(--k2) * 0.79), 54.06% calc(100% - var(--k2) * 0.60), 50.77% calc(100% - var(--k2) * 0.65), 45.41% calc(100% - var(--k2) * 0.58), 42.37% calc(100% - var(--k2) * 0.25), 38.27% calc(100% - var(--k2) * 0.14), 36.45% 100%, 32.47% 100%, 28.6% calc(100% - var(--k2) * 0.61), 24.21% calc(100% - var(--k2) * 0.46), 21.65% calc(100% - var(--k2) * 0.29), 17.88% calc(100% - var(--k2) * 0.43), 14.2% calc(100% - var(--k2) * 0.31), 11.77% calc(100% - var(--k2) * 0.41), 7.29% calc(100% - var(--k2) * 0.13), 3.45% calc(100% - var(--k2) * 0.48), 0% calc(100% - var(--k2) * 0.46)); }
.riss-innen { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--k) * 0.71), 97.34% calc(100% - var(--k) * 0.28), 91.68% calc(100% - var(--k) * 0.01), 88.79% calc(100% - var(--k) * 0.12), 85.94% calc(100% - var(--k) * 0.06), 82.79% calc(100% - var(--k) * 0.27), 77.48% calc(100% - var(--k) * 0.08), 75.43% calc(100% - var(--k) * 0.35), 72.03% calc(100% - var(--k) * 0.35), 67.92% calc(100% - var(--k) * 0.55), 65.21% calc(100% - var(--k) * 0.52), 61.05% calc(100% - var(--k) * 0.42), 56.27% calc(100% - var(--k) * 0.59), 53.92% calc(100% - var(--k) * 0.32), 49.94% calc(100% - var(--k) * 0.48), 47.27% calc(100% - var(--k) * 0.03), 42.78% calc(100% - var(--k) * 0.67), 40.52% calc(100% - var(--k) * 0.73), 35.05% calc(100% - var(--k) * 0.77), 31.54% calc(100% - var(--k) * 0.10), 29.42% calc(100% - var(--k) * 0.05), 23.78% calc(100% - var(--k) * 0.47), 20.34% calc(100% - var(--k) * 0.17), 18.17% calc(100% - var(--k) * 0.51), 14.55% calc(100% - var(--k) * 0.07), 10.39% calc(100% - var(--k) * 0.24), 7.25% calc(100% - var(--k) * 0.10), 2.92% calc(100% - var(--k) * 0.24), 0% calc(100% - var(--k) * 0.45)); }
@supports (clip-path: shape(from 0 0, line to 0 0)) {
:root:not(.rueckfall) .riss-innen { clip-path: shape(from 0 0, line to 100% 0, line to 100% calc(100% - var(--k) * 0.71), curve to 97.34% calc(100% - var(--k) * 0.28) with 98.67% calc(100% - var(--k) * 0.18), line to 91.68% calc(100% - var(--k) * 0.01), curve to 88.79% calc(100% - var(--k) * 0.12) with 90.24% calc(100% - var(--k) * 0.51), curve to 85.94% calc(100% - var(--k) * 0.06) with 87.37% calc(100% - var(--k) * 0.10), line to 82.79% calc(100% - var(--k) * 0.27), curve to 77.48% calc(100% - var(--k) * 0.08) with 80.13% calc(100% - var(--k) * 0.42), curve to 75.43% calc(100% - var(--k) * 0.35) with 76.46% calc(100% - var(--k) * 0.13), curve to 72.03% calc(100% - var(--k) * 0.35) with 73.73% calc(100% - var(--k) * 0.75), curve to 67.92% calc(100% - var(--k) * 0.55) with 69.97% calc(100% - var(--k) * 0.24), line to 65.21% calc(100% - var(--k) * 0.52), line to 61.05% calc(100% - var(--k) * 0.42), line to 56.27% calc(100% - var(--k) * 0.59), curve to 53.92% calc(100% - var(--k) * 0.32) with 55.09% calc(100% - var(--k) * 0.88), curve to 49.94% calc(100% - var(--k) * 0.48) with 51.93% calc(100% - var(--k) * 0.78), curve to 47.27% calc(100% - var(--k) * 0.03) with 48.61% calc(100% - var(--k) * 0.17), curve to 42.78% calc(100% - var(--k) * 0.67) with 45.03% calc(100% - var(--k) * 0.31), line to 40.52% calc(100% - var(--k) * 0.73), curve to 35.05% calc(100% - var(--k) * 0.77) with 37.79% calc(100% - var(--k) * 0.54), line to 31.54% calc(100% - var(--k) * 0.10), curve to 29.42% calc(100% - var(--k) * 0.05) with 30.48% calc(100% - var(--k) * 0.52), curve to 23.78% calc(100% - var(--k) * 0.47) with 26.6% calc(100% - var(--k) * 0.10), curve to 20.34% calc(100% - var(--k) * 0.17) with 22.06% calc(100% - var(--k) * 0.63), curve to 18.17% calc(100% - var(--k) * 0.51) with 19.26% calc(100% - var(--k) * 0.32), line to 14.55% calc(100% - var(--k) * 0.07), line to 10.39% calc(100% - var(--k) * 0.24), curve to 7.25% calc(100% - var(--k) * 0.10) with 8.82% calc(100% - var(--k) * 0.73), line to 2.92% calc(100% - var(--k) * 0.24), curve to 0% calc(100% - var(--k) * 0.45) with 1.46% calc(100% - var(--k) * 0.54), close); }
}
/* Schmaler Container: Riss mit 12 statt 28 Punkten, sonst wird er zur Säge */
@container rahmen (width < 40rem) {
.riss { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--k2) * 0.80), 89.3% calc(100% - var(--k2) * 0.40), 85.04% calc(100% - var(--k2) * 0.84), 75.76% calc(100% - var(--k2) * 0.72), 66.74% calc(100% - var(--k2) * 0.33), 56.49% calc(100% - var(--k2) * 0.29), 50.51% calc(100% - var(--k2) * 0.03), 41.71% calc(100% - var(--k2) * 0.02), 33.13% calc(100% - var(--k2) * 0.81), 27.47% calc(100% - var(--k2) * 0.26), 17.02% calc(100% - var(--k2) * 0.16), 8.06% calc(100% - var(--k2) * 0.77), 0% calc(100% - var(--k2) * 0.59)); }
.riss-innen, :root:not(.rueckfall) .riss-innen { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--k) * 0.35), 94.56% calc(100% - var(--k) * 0.21), 81.78% calc(100% - var(--k) * 0.80), 73.6% calc(100% - var(--k) * 0.28), 68.64% calc(100% - var(--k) * 0.22), 55.49% calc(100% - var(--k) * 0.41), 47.47% calc(100% - var(--k) * 0.11), 42.4% calc(100% - var(--k) * 0.31), 33.94% calc(100% - var(--k) * 0.72), 24.24% calc(100% - var(--k) * 0.31), 16.92% calc(100% - var(--k) * 0.45), 6.8% calc(100% - var(--k) * 0.14), 0% calc(100% - var(--k) * 0.45)); }
}
/* Kontrastmodus: Flächen fallen weg, also trennt eine Linie statt der Kante */
@media (forced-colors: active) {
.kante, .riss, .riss-innen { clip-path: none !important; -webkit-mask: none !important; mask: none !important; border-radius: 0 !important; border-block-end: 2px solid CanvasText; }
}
/* =====================================================================
Welt bogen · Weingut Steinrausch, Durbach (weingut-hofladen-herkunft)
===================================================================== */
.w-bogen { --kalk: #F1ECE2; --text: #22211F; --loess: #E6DCCB; --schiefer: #2F3236; --signal: #8A3A2B; --grau: #5A564F; --linie-w: #D8CFC0;
background: var(--kalk); color: var(--text); font: 400 1.25rem/1.55 'EB Garamond', Georgia, serif; }
.w-bogen .etikett { font: 500 .75rem/1.4 'DM Mono', ui-monospace, monospace; letter-spacing: .1em; text-transform: uppercase; }
.wb-held { --luft: 3.5rem; position: relative; min-height: 560px; display: flex; flex-direction: column; color: var(--schiefer); }
.wb-held > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 70%; z-index: -2; }
/* Schleier aus Kalk hinter dem Text: hält 4,5 : 1 auch über den mittleren Hügeln */
.wb-held::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(90deg, rgba(241, 236, 226, .62) 0, rgba(241, 236, 226, .35) 38%, rgba(241, 236, 226, 0) 62%); pointer-events: none; }
.wb-nav { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 22px clamp(18px, 4cqi, 56px); }
.wb-marke { font-style: italic; font-size: 1.6rem; letter-spacing: -.01em; }
.wb-links { display: flex; gap: 2em; }
.wb-text { padding: clamp(28px, 6cqi, 72px) clamp(18px, 4cqi, 56px) 0; max-width: 40rem; }
.wb-text h1 { font-weight: 400; font-size: clamp(2.75rem, 1.6rem + 5cqi, 6rem); line-height: 1; letter-spacing: -.015em; margin: 14px 0 18px; text-wrap: balance; }
.wb-text h1 em { font-style: italic; }
.wb-text p:not(.etikett) { max-width: 30ch; color: #3a3836; }
.wb-knopf { display: inline-flex; align-items: center; gap: .8em; margin-top: 24px; min-height: 44px; padding: 0 18px; background: var(--signal); color: #fff; text-decoration: none; font: 500 .875rem/1 'DM Mono', ui-monospace, monospace; letter-spacing: .08em; text-transform: uppercase; }
.wb-lagen { --luft-oben: 3.5rem; padding-inline: clamp(18px, 4cqi, 56px); padding-block-end: 64px; }
.wb-lagen h2, .wb-laden h2 { font-weight: 400; font-size: clamp(2rem, 1.5rem + 2cqi, 3.25rem); line-height: 1.05; margin: 10px 0 26px; }
.wb-lagen h2 em, .wb-laden h2 em { font-style: italic; }
.wb-zeilen { list-style: none; border-top: 1px solid var(--linie-w); }
.wb-zeilen li { display: grid; grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr)); gap: 8px 16px; align-items: baseline; padding: 14px 0; border-bottom: 1px solid var(--linie-w); }
.wb-zeilen b { font-weight: 400; font-style: italic; font-size: 1.5rem; }
.wb-zeilen span { color: var(--grau); }
.wb-laden { --luft: 3rem; background: var(--loess); padding-inline: clamp(18px, 4cqi, 56px); padding-block-start: 56px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px 48px; }
.wb-zeiten { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 22px; align-self: end; }
.wb-zeiten dt { color: var(--grau); }
.wb-status { display: inline-flex; align-items: center; gap: .6em; }
.wb-status::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: #3d6b3f; }
.wb-fuss { --luft-oben: 3rem; background: var(--schiefer); color: #E9E4DA; padding-inline: clamp(18px, 4cqi, 56px); padding-block-end: 34px; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px 32px; }
.wb-fuss .etikett { color: #cfc8bb; }
@container rahmen (width < 44rem) {
.wb-links span:not(:last-child) { display: none; }
.wb-zeilen li { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.wb-zeilen b { grid-column: 1 / -1; }
.wb-laden { grid-template-columns: minmax(0, 1fr); }
.wb-held { min-height: 520px; }
}
/* =====================================================================
Welt welle · Lotsenstube, Glückstadt (gastro-warm-appetitlich)
===================================================================== */
.w-welle { --k: 2rem; --grund: #F0ECE0; --text: #353839; --salbei: #7F937A; --dunkel: #293E3A; --senf: #EAC16E; --leise-w: #5f605e; --linie-w: #D2CFC5;
background: var(--grund); color: var(--text); font: 400 1.0625rem/1.55 'Commissioner', system-ui, sans-serif; }
.w-welle .rubrik { font: 600 .75rem/1.3 'Commissioner', system-ui, sans-serif; font-variation-settings: "FLAR" 100; letter-spacing: .15em; text-transform: uppercase; }
.ww-held { --luft: 3.5rem; background: var(--dunkel); color: #F0ECE0; padding-inline: clamp(18px, 4cqi, 48px); }
.ww-nav { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 22px 0 0; }
.ww-marke { font: 500 1.5rem/1 'Fraunces', Georgia, serif; font-variation-settings: "SOFT" 100; letter-spacing: -.01em; }
.ww-status { display: inline-flex; align-items: center; gap: .6em; color: #F0ECE0; }
.ww-status::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--senf); }
.ww-raster { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 28px clamp(24px, 4cqi, 56px); align-items: center; padding-top: clamp(32px, 6cqi, 72px); }
.ww-raster h1 { font: 500 clamp(2.5rem, 1.6rem + 4cqi, 5rem)/1.02 'Fraunces', Georgia, serif; font-variation-settings: "SOFT" 80; letter-spacing: -.01em; margin: 14px 0 18px; text-wrap: balance; }
.ww-raster h1 em { font-style: italic; font-weight: 300; }
.ww-raster p { max-width: 36ch; color: #dcd8cc; }
.ww-knoepfe { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; }
.ww-knopf { display: inline-flex; align-items: center; min-height: 44px; padding: 0 22px; border-radius: 999px; background: var(--grund); color: var(--dunkel); text-decoration: none; font-weight: 600; }
.ww-knopf.leer { background: none; color: var(--grund); box-shadow: inset 0 0 0 1px rgba(240, 236, 224, .5); }
.ww-raster img { width: 100%; aspect-ratio: 1 / 1; max-height: 440px; object-fit: cover; object-position: 55% 50%; border-radius: 12px; }
.ww-karte { --luft: 3rem; --luft-oben: 3.5rem; background: var(--grund); padding-inline: clamp(18px, 4cqi, 48px); }
.ww-karte h2 { font: 500 clamp(2rem, 1.4rem + 2cqi, 3rem)/1.05 'Fraunces', Georgia, serif; font-variation-settings: "SOFT" 80; margin: 10px 0 26px; }
.ww-spalten { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 48px; }
.ww-spalten li { list-style: none; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 16px; padding: 14px 0; border-top: 1px solid var(--linie-w); }
.ww-spalten b { font-weight: 500; font-size: 1.125rem; }
.ww-spalten small { grid-column: 1; font-size: .9375rem; color: var(--leise-w); }
.ww-spalten data { grid-row: 1; grid-column: 2; font-variant-numeric: tabular-nums; }
.ww-fuss { --luft-oben: 3rem; background: var(--salbei); color: #1d2a27; padding-inline: clamp(18px, 4cqi, 48px); padding-block-end: 34px; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px 32px; }
.ww-fuss { background: #C9D2C3; }
@container rahmen (width < 44rem) {
.ww-raster { grid-template-columns: minmax(0, 1fr); }
.ww-raster img { aspect-ratio: 3 / 2; }
.ww-spalten { grid-template-columns: minmax(0, 1fr); }
.ww-nav .ww-status { display: none; }
}
/* =====================================================================
Welt schraege · Badwerkstatt Kalkmann, Isny (handwerk-werkstatt-klartext)
===================================================================== */
.w-schraege { --k: 3rem; --grund: #EFEDEA; --zement: #D3CDC2; --holz: #C3B4A5; --text: #1A1A1A; --signal: #FB6100; --leise-w: #5d5c5b; --linie-w: #bdb6ab;
background: var(--grund); color: var(--text); font: 400 1.0625rem/1.55 'Atkinson Hyperlegible Next', system-ui, sans-serif; }
.w-schraege .gewerk { font: 700 .8125rem/1.3 'Special Gothic', system-ui, sans-serif; font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase; }
.ws-held { --luft: 3.5rem; background: var(--grund); padding-inline: clamp(18px, 4cqi, 48px); }
.ws-nav { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 20px 0; border-bottom: 1px solid var(--linie-w); }
.ws-marke { font: 700 1.25rem/1 'Special Gothic', system-ui, sans-serif; letter-spacing: -.01em; }
.ws-tel { font-weight: 700; text-decoration: none; }
.ws-raster { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 28px clamp(24px, 4cqi, 48px); align-items: end; padding-top: clamp(28px, 5cqi, 56px); }
.ws-raster h1 { font: 700 clamp(2.25rem, 1.5rem + 3cqi, 3.5rem)/1.05 'Special Gothic', system-ui, sans-serif; letter-spacing: -.01em; margin: 12px 0 16px; text-wrap: balance; }
.ws-raster p { max-width: 34ch; }
.ws-knopf { display: inline-flex; align-items: center; gap: .6em; margin-top: 22px; min-height: 44px; padding: 0 20px; border-radius: 4px; background: var(--signal); color: #1A1A1A; font-weight: 700; text-decoration: none; }
.ws-raster img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 8px; }
.ws-ablauf { --luft: 3rem; --luft-oben: 3.5rem; background: var(--zement); padding-inline: clamp(18px, 4cqi, 48px); }
.ws-ablauf h2 { font: 700 clamp(1.625rem, 1.2rem + 1.6cqi, 2.25rem)/1.1 'Special Gothic', system-ui, sans-serif; margin: 8px 0 26px; }
.ws-schritte { list-style: none; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; counter-reset: s; }
.ws-schritte li { counter-increment: s; border-top: 2px solid var(--text); padding-top: 12px; }
.ws-schritte li::before { content: "0" counter(s); display: block; font: 700 2rem/1 'Special Gothic', system-ui, sans-serif; font-stretch: 75%; margin-bottom: 10px; }
.ws-schritte b { display: block; font-weight: 700; }
.ws-schritte span { font-size: .9375rem; color: #3b3a38; }
.ws-fuss { --luft-oben: 3rem; background: #1A1A1A; color: #EFEDEA; padding-inline: clamp(18px, 4cqi, 48px); padding-block-end: 34px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 14px 32px; }
.ws-fuss .ws-knopf { margin: 0; }
@container rahmen (width < 44rem) {
.ws-raster { grid-template-columns: minmax(0, 1fr); }
.ws-schritte { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ws-nav .gewerk { display: none; }
}
/* =====================================================================
Welt riss · Plakatsaal Gera (kultur-programm-haltung)
===================================================================== */
.w-riss { --k: 1.5rem; --k2: 2.1rem; --grund: #efede9; --text: #0b0b0b; --musik: #c4421a; --theater: #3d5f9a; --kunst: #2f7d4a; --leise-w: #5e5d5c;
background: var(--grund); color: var(--text); font: 400 1.125rem/1.6 'Atkinson Hyperlegible Next', system-ui, sans-serif; }
.w-riss .daten { font: 500 .8125rem/1.4 'IBM Plex Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.wr-held .riss-innen { background: var(--grund); padding-inline: clamp(16px, 3cqi, 40px); }
.wr-nav { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--text); }
.wr-marke { font: 900 1.25rem/1 'Schibsted Grotesk', system-ui, sans-serif; letter-spacing: -.02em; text-transform: uppercase; }
.wr-links { display: flex; gap: 1.6em; }
.wr-raster { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 24px clamp(20px, 3cqi, 40px); padding-top: 28px; align-items: end; }
.wr-raster h1 { font: 900 clamp(2.5rem, .6rem + 4.6cqi, 5.5rem)/1 'Schibsted Grotesk', system-ui, sans-serif; letter-spacing: -.02em; margin: 10px 0 16px; }
.wr-raster p { max-width: 34ch; }
.wr-raster img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.wr-ticket { display: inline-flex; align-items: center; min-height: 44px; padding: 0 18px; margin-top: 20px; background: var(--text); color: var(--grund); text-decoration: none; font: 700 1rem/1 'Schibsted Grotesk', system-ui, sans-serif; }
.wr-programm { --luft-oben: 3rem; color: #efede9; }
.wr-programm > .riss-innen { background: #0b0b0b; padding-inline: clamp(16px, 3cqi, 40px); }
.wr-programm h2 { font: 800 clamp(1.375rem, 1rem + 2.2cqi, 2rem)/1.1 'Schibsted Grotesk', system-ui, sans-serif; margin-bottom: 18px; }
.wr-zeilen { list-style: none; }
.wr-zeilen li { display: grid; grid-template-columns: 9.5em minmax(0, 1fr) auto; gap: 4px 18px; align-items: baseline; padding: 12px 0; border-top: 1px solid rgba(239, 237, 233, .28); }
.wr-zeilen b { font: 700 1.125rem/1.3 'Schibsted Grotesk', system-ui, sans-serif; }
.wr-zeilen .sparte { display: inline-block; width: 10px; height: 10px; margin-right: 8px; vertical-align: 0; }
.wr-fuss { --luft-oben: 2.5rem; background: var(--theater); color: #fff; padding-inline: clamp(16px, 3cqi, 40px); padding-block-end: 30px; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px 32px; }
@container rahmen (width < 44rem) {
.wr-raster { grid-template-columns: minmax(0, 1fr); }
.wr-links span:not(:last-child) { display: none; }
.wr-zeilen li { grid-template-columns: minmax(0, 1fr) auto; }
.wr-zeilen .daten:first-child { grid-column: 1 / -1; }
}
/* Vorschau: nur die Bühne */
.vorschau body { overflow: hidden; padding: 22px 26px; }
.vorschau .werte, .vorschau .pult, .vorschau .kopf .hilfe { display: none; }
:root.reduziert * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
@media (hover: none) {
.griff { width: 14px; right: -18px; }
.griff:hover { background: var(--hell); scale: 1; }
}
@media (max-width: 640px) {
.pult { gap: 10px; }
.wahl { width: 100%; }
.wahl button { flex: 1; padding: 0 6px; letter-spacing: .06em; }
.regler { width: 100%; }
.regler input { flex: 1; width: auto; }
.werte dt { min-width: 7em; }
}
@media (forced-colors: active) {
button, [role=button], [role=tab], [role=switch], input, select, textarea { border: 1px solid ButtonText !important; }
}
</style>
</head>
<body>
<div class="pult mono">
<div class="wahl" role="group" aria-label="Variante">
<button type="button" data-v-wahl="bogen" aria-pressed="true">Bogen</button>
<button type="button" data-v-wahl="welle" aria-pressed="false">Welle</button>
<button type="button" data-v-wahl="schraege" aria-pressed="false">Schräge</button>
<button type="button" data-v-wahl="riss" aria-pressed="false">Riss</button>
</div>
<label class="regler">Breite <input type="range" min="320" max="1240" step="1" value="1240" data-regler aria-label="Breite des Containers in Pixel"><output data-ausgabe>1240 px</output></label>
<button type="button" class="schalter" data-rueckfall aria-pressed="false">Rückfall zeigen</button>
<span class="stand" data-stand></span>
</div>
<div class="kopf mono">
<span>Container <b data-breite>1240</b> px</span>
<span class="k">Kante <b data-kante>40</b> px fest (<span data-kante-rem>2.5rem</span>)</span>
<span class="hilfe">Griff rechts ziehen oder Regler im Pult</span>
</div>
<div class="buehne" data-buehne>
<div class="halter" data-halter>
<div class="rahmen" data-rahmen>
<!-- ========== bogen: Weingut Steinrausch ========== -->
<div class="welt w-bogen" data-welt="bogen">
<section class="kante k-bogen wb-held" aria-labelledby="wb-h1">
<img src="bilder/huegel-nebel.jpg" alt="" width="1200" height="800">
<nav class="wb-nav" aria-label="Beispielnavigation Weingut">
<span class="wb-marke">Steinrausch</span>
<span class="wb-links etikett"><span>Weine</span><span>Lagen</span><span>Hofladen</span><span>Besuch</span></span>
</nav>
<div class="wb-text">
<p class="etikett">Durbach · Ortenau · Granit seit 1911</p>
<h1 id="wb-h1">Herkunft ist ein <em>Hang</em>.</h1>
<p>Drei Lagen über dem Durbachtal, jede mit eigenem Boden. Wir füllen sie getrennt, damit man den Unterschied schmeckt.</p>
<a class="wb-knopf" href="#wb-lagen">Weine nach Lage</a>
</div>
</section>
<section class="unter wb-lagen" id="wb-lagen" aria-labelledby="wb-h2">
<p class="etikett">Lagen</p>
<h2 id="wb-h2">Drei Lagen, <em>ein</em> Boden.</h2>
<ul class="wb-zeilen">
<li><b>Kreuzleite</b><span class="etikett">Granitgrus</span><span class="etikett">Süd · 38 %</span><span class="etikett">290 m</span></li>
<li><b>Sonnwendberg</b><span class="etikett">Granit, Löss</span><span class="etikett">Südwest · 24 %</span><span class="etikett">250 m</span></li>
<li><b>Grauer Bühl</b><span class="etikett">Gneis</span><span class="etikett">West · 18 %</span><span class="etikett">220 m</span></li>
</ul>
</section>
<section class="kante k-bogen wb-laden" aria-labelledby="wb-h3">
<div>
<p class="etikett">Hofladen</p>
<h2 id="wb-h3">Probieren, <em>wo</em> er wächst.</h2>
<p class="wb-status etikett">Heute geöffnet bis 18 Uhr</p>
</div>
<dl class="wb-zeiten etikett">
<dt>Mi bis Fr</dt><dd>14 bis 18 Uhr</dd>
<dt>Samstag</dt><dd>9 bis 14 Uhr</dd>
<dt>Weinprobe</dt><dd>Sa 15 Uhr · 29 € p. P.</dd>
</dl>
</section>
<footer class="unter wb-fuss etikett">
<span>Weingut Steinrausch · Talstraße 12 · 77770 Durbach</span>
<span>Abgabe von Wein ab 18 Jahren · Impressum · Datenschutz</span>
</footer>
</div>
<!-- ========== welle: Lotsenstube Glückstadt ========== -->
<div class="welt w-welle" data-welt="welle" hidden>
<section class="kante k-welle ww-held" aria-labelledby="ww-h1">
<nav class="ww-nav" aria-label="Beispielnavigation Restaurant">
<span class="ww-marke">Lotsenstube</span>
<span class="ww-status rubrik">Heute offen · Küche bis 21 Uhr</span>
</nav>
<div class="ww-raster">
<div>
<p class="rubrik">Am Binnenhafen 3 · Glückstadt</p>
<h1 id="ww-h1">Matjes, Elbe <em>und</em> eine halbe Zitrone.</h1>
<p>Fisch vom Kutter nebenan, Kartoffeln aus der Marsch, Brot aus dem eigenen Ofen. Mittags kurz, abends lang.</p>
<div class="ww-knoepfe"><a class="ww-knopf" href="#ww-karte">Tisch reservieren</a><a class="ww-knopf leer" href="#ww-karte">Zur Karte</a></div>
</div>
<img src="bilder/zitronen-tisch.jpg" alt="Zitronen und eine halbierte Zitrone auf einem hellen Steingutteller auf dunklem Schiefer" width="1200" height="800">
</div>
</section>
<section class="unter kante k-welle ww-karte" id="ww-karte" aria-labelledby="ww-h2">
<p class="rubrik">Mittagstisch · 11.30 bis 14.30 Uhr</p>
<h2 id="ww-h2">Heute auf der Tafel</h2>
<ul class="ww-spalten">
<li><b>Glückstädter Matjes</b><data value="16.5">16,50 €</data><small>Hausfrauenart, Apfel, Zwiebel, Pellkartoffeln</small></li>
<li><b>Scholle Finkenwerder Art</b><data value="24">24,00 €</data><small>Speck, Krabben, Gurkensalat</small></li>
<li><b>Elbfischsuppe</b><data value="12.5">12,50 €</data><small>Zander, Fenchel, Safran, Brot vom Ofen</small></li>
<li><b>Rote Grütze</b><data value="7">7,00 €</data><small>Vanillesoße, ein Hauch Zitrone</small></li>
</ul>
</section>
<footer class="unter ww-fuss rubrik">
<span>Di bis So 11.30 bis 22 Uhr · Mo Ruhetag</span>
<span>04124 · Reservierung · Impressum · Datenschutz</span>
</footer>
</div>
<!-- ========== schraege: Badwerkstatt Kalkmann ========== -->
<div class="welt w-schraege" data-welt="schraege" hidden>
<section class="kante k-schraege ws-held" aria-labelledby="ws-h1">
<nav class="ws-nav" aria-label="Beispielnavigation Handwerk">
<span class="ws-marke">Kalkmann Bad</span>
<span class="gewerk">Bad · Fliese · Sanitär</span>
<a class="ws-tel" href="#ws-fuss">07562 41 08</a>
</nav>
<div class="ws-raster">
<div>
<p class="gewerk">Badwerkstatt in Isny im Allgäu</p>
<h1 id="ws-h1">Ein neues Bad. Drei Wochen. Ein Festpreis.</h1>
<p>Wir messen auf, nennen einen festen Preis und bauen mit einem Team vom Abbruch bis zur Silikonfuge.</p>
<a class="ws-knopf" href="#ws-fuss">Bad anfragen</a>
</div>
<img src="bilder/handwerk-bad.webp" alt="Neues Bad mit bodengleicher Dusche, Wandnische und Holzhocker, Waschtisch mit Holzunterschrank vor salbeigrüner Wand" width="1500" height="1000">
</div>
</section>
<section class="unter kante k-schraege ws-ablauf" aria-labelledby="ws-h2">
<p class="gewerk">So läuft es</p>
<h2 id="ws-h2">Vier Schritte, ein Ansprechpartner</h2>
<ol class="ws-schritte">
<li><b>Aufmaß vor Ort</b><span>45 Minuten, kostenlos im Umkreis von 25 km</span></li>
<li><b>Festpreis</b><span>schriftlich innerhalb von 5 Werktagen</span></li>
<li><b>Bau</b><span>15 Werktage, staubarm, mit Notdusche</span></li>
<li><b>Abnahme</b><span>gemeinsam, mit Pflegeplan für Fugen</span></li>
</ol>
</section>
<footer class="unter ws-fuss" id="ws-fuss">
<span class="gewerk">Kalkmann Bad GmbH · Lindauer Straße 21 · 88316 Isny</span>
<a class="ws-knopf" href="#ws-held">Rückruf anfragen</a>
</footer>
</div>
<!-- ========== riss: Plakatsaal Gera ========== -->
<div class="welt w-riss" data-welt="riss" hidden>
<section class="riss wr-held" aria-labelledby="wr-h1">
<div class="riss-innen">
<nav class="wr-nav" aria-label="Beispielnavigation Ausstellungshaus">
<span class="wr-marke">Plakatsaal Gera</span>
<span class="wr-links daten"><span>Programm</span><span>Sammlung</span><span>Besuch</span></span>
</nav>
<div class="wr-raster">
<div>
<p class="daten">Ausstellung · 14.11.2026 bis 28.02.2027</p>
<h1 id="wr-h1">Abgerissen.</h1>
<p>Hundert Jahre Plakate von der Litfaßsäule: geklebt, überklebt, abgezogen. Wir zeigen auch die Schichten darunter.</p>
<a class="wr-ticket" href="#wr-programm">Zeitfenster buchen</a>
</div>
<img src="bilder/typo-plakat.jpg" alt="Ausstellungsplakat: riesiges Wort LICHT in schmaler Grotesk vor orangefarbener Scheibe auf Limettengrün, kursive Zeile „und was es übrig lässt“" width="1200" height="800">
</div>
</div>
</section>
<section class="unter riss wr-programm" id="wr-programm" aria-labelledby="wr-h2">
<div class="riss-innen">
<h2 id="wr-h2">Diese Woche</h2>
<ul class="wr-zeilen">
<li><span class="daten">Do 19.11. · 18:00</span><b><span class="sparte" style="background: var(--musik)" aria-hidden="true"></span>Kleister und Klang: Eröffnung</b><span class="daten">Halle 1</span></li>
<li><span class="daten">Sa 21.11. · 11:00</span><b><span class="sparte" style="background: #6f9bdc" aria-hidden="true"></span>Schichten lesen: Führung</b><span class="daten">Halle 2</span></li>
<li><span class="daten">So 22.11. · 15:00</span><b><span class="sparte" style="background: #58b679" aria-hidden="true"></span>Siebdruck für alle ab 10</b><span class="daten">Werkstatt</span></li>
</ul>
</div>
</section>
<footer class="unter wr-fuss daten">
<span>Plakatsaal Gera · Puschkinplatz 4 · Di bis So 11 bis 19 Uhr</span>
<span>Eintritt 8 € · ermäßigt 5 € · Impressum · Datenschutz</span>
</footer>
</div>
</div>
<div class="griff" data-griff role="presentation" title="Ziehen, um die Breite zu ändern"></div>
</div>
</div>
<dl class="werte" data-werte></dl>
<script>
(() => {
const html = document.documentElement;
const reduziert = () => html.classList.contains('reduziert');
const $ = s => document.querySelector(s);
const buehne = $('[data-buehne]'), halter = $('[data-halter]'), rahmen = $('[data-rahmen]'), griff = $('[data-griff]');
const regler = $('[data-regler]'), ausgabe = $('[data-ausgabe]'), breiteText = $('[data-breite]');
const kanteText = $('[data-kante]'), kanteRem = $('[data-kante-rem]'), werteListe = $('[data-werte]'), stand = $('[data-stand]');
const rueckKnopf = $('[data-rueckfall]');
const MIN = 320, MAX = 1240;
const kannShape = CSS.supports('clip-path', 'shape(from 0 0, line to 0 0)');
const IDS = ['bogen', 'welle', 'schraege', 'riss'];
const KANTE = { bogen: '2.5rem', welle: '2rem', schraege: '3rem', riss: '1.5rem' };
const WERTE = {
bogen: [
['Welt', 'Weingut Steinrausch, Durbach (weingut-hofladen-herkunft)'],
['Kante', '--k: 2.5rem; zwei Kanten: Hero → Lagen, Hofladen → Footer'],
['shape()', 'line to 100% calc(100% - var(--k)), curve to 0 calc(100% - var(--k)) with 32% calc(100% + var(--k))'],
['Kontrollpunkt', '100% + --k: die Kurvenmitte liegt genau auf der Unterkante; 32 % macht einen Hang statt eines Halbmonds'],
['Rückfall', 'border-end-start/end-radius: 50% var(--k) (Halbellipse, symmetrisch)'],
['Innenabstand', 'padding-block-end: calc(var(--k) + 3.5rem); nächste Sektion margin-block-start: calc(var(--k) * -1)']
],
welle: [
['Welt', 'Lotsenstube, Glückstadt (gastro-warm-appetitlich)'],
['Kante', '--k: 2rem; zwei Kanten: Hero → Karte, Karte → Footer'],
['shape()', 'fünf kubische Halbwellen (curve to … with … / …), Stützpunkte 38 % der Halbwelle waagerecht: sinusähnlich'],
['Verhalten', 'Wellenzahl fest (2,5), Wellenlänge wächst mit der Breite, Höhe bleibt 2rem'],
['Rückfall', 'mask: radial-gradient(50% 100% at 50% 0, …) / 7rem var(--k) repeat-x + linear-gradient für die Fläche; feste Wellenlänge 7rem'],
['Innenabstand', 'calc(var(--k) + 3.5rem) bzw. + 3rem']
],
schraege: [
['Welt', 'Badwerkstatt Kalkmann, Isny (handwerk-werkstatt-klartext)'],
['Kante', '--k: 3rem; zwei Kanten in gleicher Richtung, links tief, rechts hoch'],
['shape()', 'line to 100% calc(100% - var(--k)), line to 0 100%'],
['Rückfall', 'polygon(0 0, 100% 0, 100% calc(100% - var(--k)), 0 100%): gleiches Bild in jedem Browser, shape() ist hier Kür'],
['Innenabstand', 'calc(var(--k) + 3.5rem) bzw. + 3rem']
],
riss: [
['Welt', 'Plakatsaal Gera (kultur-programm-haltung)'],
['Kante', 'zwei Lagen: Papierkern #fbfaf7 mit --k2: 2.1rem, bedruckte Fläche mit --k: 1.5rem, 0.5rem Versatz'],
['Punkte', '28 Punkte, unter 40rem Containerbreite 12 Punkte (@container rahmen (width < 40rem))'],
['shape()', 'gleiche Punkte wie polygon(), dazu feine Fasern als quadratische Kurven (curve to … with …) auf der oberen Lage'],
['Rückfall', 'polygon() mit calc(100% - var(--k) * n): gleiche Kante ohne Fasern'],
['Innenabstand', 'calc(var(--k) + 2.5rem) bzw. Versatz der nächsten Sektion calc((var(--k2) + .5rem) * -1)']
]
};
let v = new URLSearchParams(location.search).get('v');
if (!IDS.includes(v)) v = IDS[0];
let breite = MAX, massstab = 1;
function platz() { return buehne.clientWidth - 20; }
function grenze() { const p = platz(); return p >= 700 ? Math.max(MIN, Math.min(MAX, p)) : MAX; }
function setzeBreite(w) {
breite = Math.round(Math.max(MIN, Math.min(grenze(), w)));
rahmen.style.width = breite + 'px';
massstab = Math.min(1, platz() / breite);
halter.style.transform = massstab < 1 ? `scale(${massstab})` : '';
halter.style.height = massstab < 1 ? rahmen.offsetHeight * massstab + 'px' : '';
breiteText.textContent = breite;
regler.max = grenze(); regler.value = breite;
ausgabe.textContent = breite + ' px';
}
function zeigeStand() {
const rueck = html.classList.contains('rueckfall');
const art = { bogen: 'Halbellipse per border-radius', welle: 'mask mit radial-gradient', schraege: 'polygon()', riss: 'polygon()' }[v];
stand.innerHTML = kannShape
? (rueck ? `Rückfall: <i class="nein">${art}</i>` : 'shape(): <i>läuft hier</i>')
: `shape(): <i class="nein">fehlt</i> · Rückfall ${art}`;
}
function setze(neu, url) {
v = neu;
html.dataset.v = neu;
document.querySelectorAll('[data-welt]').forEach(w => { w.hidden = w.dataset.welt !== neu; });
document.querySelectorAll('[data-v-wahl]').forEach(b => b.setAttribute('aria-pressed', String(b.dataset.vWahl === neu)));
if (url) {
const q = new URLSearchParams(location.search); q.set('v', neu);
history.replaceState(null, '', location.pathname + '?' + q.toString().replace(/=(?=&|$)/g, '') + location.hash);
}
kanteRem.textContent = KANTE[neu];
kanteText.textContent = Math.round(parseFloat(KANTE[neu]) * parseFloat(getComputedStyle(html).fontSize));
werteListe.innerHTML = WERTE[neu].map(([a, b]) => `<div><dt>${a}</dt><dd>${b}</dd></div>`).join('');
zeigeStand();
setzeBreite(breite);
}
document.querySelectorAll('[data-v-wahl]').forEach(b => b.addEventListener('click', () => { stopp(); setze(b.dataset.vWahl, true); }));
rueckKnopf.addEventListener('click', () => {
const an = !html.classList.contains('rueckfall');
html.classList.toggle('rueckfall', an);
rueckKnopf.setAttribute('aria-pressed', String(an));
zeigeStand();
});
if (!kannShape) { rueckKnopf.disabled = true; rueckKnopf.textContent = 'Rückfall aktiv'; }
regler.addEventListener('input', () => { stopp(); setzeBreite(+regler.value); });
let zug = null;
griff.addEventListener('pointerdown', e => { stopp(); zug = { x: e.clientX, w: breite }; griff.setPointerCapture(e.pointerId); griff.classList.add('zieht'); });
griff.addEventListener('pointermove', e => { if (zug) setzeBreite(zug.w + (e.clientX - zug.x) / massstab); });
const los = () => { zug = null; griff.classList.remove('zieht'); };
griff.addEventListener('pointerup', los); griff.addEventListener('pointercancel', los);
new ResizeObserver(() => setzeBreite(breite)).observe(buehne);
// Links in der Beispielseite führen nirgendwohin
rahmen.addEventListener('click', e => { if (e.target.closest('a')) e.preventDefault(); });
// Vorführung: der Container atmet zwischen voller Breite und Telefonbreite, die Kante bleibt gleich hoch
let uhr = null, tick = null, start = 0, versatz = 0;
const RUNDE = 8000, UNTEN = 360;
function stopp() { cancelAnimationFrame(uhr); clearTimeout(tick); uhr = tick = null; html.classList.remove('vorfuehrung'); }
function neustart() { stopp(); versatz = 0; setze(v, false); setzeBreite(grenze()); window.scrollTo(0, 0); }
function vorfuehren() {
stopp();
html.classList.add('vorfuehrung');
if (reduziert()) {
const stufen = [grenze(), 900, 600, 380];
let i = 0;
const weiterSchalten = () => { setzeBreite(stufen[i++ % stufen.length]); tick = setTimeout(weiterSchalten, 2000); };
weiterSchalten();
return;
}
start = performance.now() - versatz;
const schritt = t => {
versatz = (t - start) % RUNDE;
const p = versatz / RUNDE, oben = grenze();
setzeBreite(UNTEN + (oben - UNTEN) * (0.5 + 0.5 * Math.cos(p * 2 * Math.PI)));
uhr = requestAnimationFrame(schritt);
};
uhr = requestAnimationFrame(schritt);
}
function pause() { cancelAnimationFrame(uhr); clearTimeout(tick); uhr = tick = null; }
function weiter() { vorfuehren(); }
setze(v, false);
// Telefon: 1 : 1 in Gerätebreite starten
if (platz() < 700) setzeBreite(Math.max(MIN, platz()));
})();
</script>
</body>
</html>