Lagen mit Steckbrief und eigener Karte
Lagen als Liste mit Steckbrief (Foto, Boden, Ausrichtung, Neigung, Höhe, Fläche, Sorten, Klassifikation, Weine mit Grundpreis) und als eigene SVG-Karte mit Höhenlinien und Bodenmustern; Liste und Karte markieren sich gegenseitig, jede Lage hat eine Adresse mit Rückweg zum Wein.
Weingut, Sektkellerei, Winzergenossenschaft: Lagen mit Boden, Ausrichtung, Neigung, Höhe, Rebsorten, Klassifikation und Weinen.
Die Lagen eines Weinguts, die Wiesen einer Brennerei oder die Felder eines Hofs als Liste und als eigene Zeichnung ohne Kartendienst. Die Liste ist der Inhalt: je Lage Name, Boden, Ausrichtung, Neigung, Fläche und ein Status. Wer eine Lage wählt, öffnet ihren Steckbrief mit Foto, allen Daten (Boden, Ausrichtung, Neigung in Prozent, Höhe in Metern, Fläche, Rebsorten oder Obstsorten, Klassifikation) und zwei bis vier Weinen mit Jahrgang, Füllmenge, Preis und Grundpreis. Die Karte daneben ist eine zweite Ansicht: Höhenlinien, Fluss, Ort und die Lagen als Flächen mit Namen, Böden in Farbe und Muster mit Legende. Liste und Karte markieren sich gegenseitig. Jede Lage hat eine eigene Adresse (#lage-…), damit die Weinseite auf sie verlinkt und von dort ein Rückweg zum Wein führt. Lagen ohne lieferbaren Wein, gepachtete und neu bepflanzte Flächen sagen das im Text.
Bei Weingütern, Obsthöfen und Brennereien ist der Ort das Argument. Eine Liste mit echten Daten beantwortet, was Kenner wissen wollen (Boden, Ausrichtung, Neigung), und die Zeichnung zeigt, wie die Lagen zueinander liegen, ohne Cookies und ohne fremde Karte. Weil jede Lage ihre Weine nennt und jeder Wein seine Lage, wird aus der Herkunftsgeschichte ein Weg in den Shop. Ehrliche Zustände (ausgetrunken, neu gepflanzt) machen die Lage glaubwürdig.
Wann einsetzen
Weingüter mit Lagen- oder Ortsweinen, Sektkellereien mit eigenen Lagen, Brennereien und Obsthöfe mit Streuobstwiesen, Hofläden mit Feldern, Weiden und Ställen, Gärtnereien mit Flächen. Ab etwa drei Flächen; bei einer einzigen Lage reicht ein Steckbrief ohne Karte.
Wann nicht
Für den Weg zum Hof oder zur Vinothek ist karte-zwei-klick da (Adresse, Anfahrt, Karte nach Einwilligung). Keine Kartendienst-Kacheln als Untergrund der Lagen (Einwilligung, Gestaltung bricht). Keine Daten schätzen: Neigung, Höhe und Fläche aus dem Weinbergskataster oder einer Vermessung übernehmen, Klassifikationen nur, wenn sie stimmen (VDP-Stufen nur für Mitglieder).
Varianten
- Weinberg: Lagen mit Boden, Ausrichtung, Neigung, Höhe, Fläche, Rebsorten, Klassifikation und Weinen
- Obstwiese: Wiesen einer Brennerei mit Sorten, Zahl der Bäume, Baumalter, Erntejahr und Bränden
- Hof: Felder, Weiden, Garten und Ställe eines Hofladens mit dem, was dort wächst, und dem Angebot im Laden
Umsetzung
Daten je Fläche aus einer Quelle: id, Name, Art, Boden, Ausrichtung, Neigung (von bis in Prozent), Höhe (von bis in Metern), Fläche in Hektar, Sorten, Klassifikation, Status (lieferbar, ausgetrunken, gepachtet seit, neu bepflanzt), Foto mit Alt-Text, Weine (Jahrgang, Name, Füllmenge, Preis, Link) und die Fläche als Polygon. Die Liste ist ein <ol>; jede Lage ist ein Link auf #lage-<id>, der ihren Steckbrief öffnet (aria-expanded, Fokus bleibt auf dem Link). Der Steckbrief klappt über grid-template-rows 0fr → 1fr auf (öffnen 300 ms, schließen 200 ms) und ist zu inert. Die Karte ist ein inline-SVG: Höhenlinien aus einem Geländemodell (alle 10 m, jede 50. kräftiger und beschriftet), Fluss, Ort, Wald, Lagen als Pfade mit Muster-Füllung (<pattern>) je Boden und Namen als <text>; jede Fläche ist ein <a href="#lage-<id>"> mit Namen. Hover und Fokus in Liste oder Karte markieren beide. Die Adresse mit #lage-<id> öffnet die Lage beim Laden; kommt der Besuch von einer Weinseite (?von=<wein>), steht oben im Steckbrief ein Rückweg zum Wein. Grundpreis bei jedem Preis (Preis ÷ Füllmenge). Druckansicht: alle Steckbriefe offen, Karte klein, helle Fläche, Muster statt Farbe; beforeprint/afterprint setzen sie auch beim echten Druck.
Robust umsetzen
Telefon. Die Karte steht klein über der Liste (volle Breite, ohne Sticky), die Liste bleibt der Inhalt. Steckbriefe öffnen sich in der Liste, Foto oben, Daten zweispaltig, Weine als Zeilen mit Preis und Grundpreis. Trefferflächen der Lagen-Links mindestens 44 px hoch.
Ohne Bewegung. Steckbriefe springen ohne Höhenübergang auf, Fotos stehen sofort ohne Maske und Skalierung, Markierungen ohne Übergang.
Barrierefrei. Die Liste ist der Inhalt und vollständig ohne Karte bedienbar; die Karte ist eine zweite Ansicht mit eigener Beschreibung. Lagen sind Links mit aria-expanded und aria-controls; geschlossene Steckbriefe sind inert. Flächen in der Karte sind Links mit Namen (aria-label mit Boden und Ausrichtung) und sichtbarem Fokus. Böden sind in der Karte durch Muster und Beschriftung unterscheidbar, nicht nur durch Farbe; die Legende nennt Farbe und Muster. Zahlen in Tabellenziffern, Fotos mit beschreibendem Alt-Text. Status (ausgetrunken, gepachtet, neu bepflanzt) als Text.
Code
<!DOCTYPE html>
<!--
Lagen mit Steckbrief und eigener Karte
Fundus · Sektionen & Funktionen · https://cecodesign.de/lab/fundus/sektionen/lagen-steckbrief-karte/
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>Lagen mit Steckbrief und eigener Karte · 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: '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-style: normal; font-display: swap; }
@font-face { font-family: 'EB Garamond'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/eb-garamond:vf@latest/latin-wght-italic.woff2') format('woff2-variations'); font-weight: 400 800; font-style: italic; font-display: swap; }
@font-face { font-family: 'DM Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/dm-mono@latest/latin-400-normal.woff2') format('woff2'); font-weight: 400; 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; }
:root {
/* Rezept weingut-hofladen-herkunft: Schiefer für das Lagen-Kapitel */
--kalk: #f1ece2; --text: #22211f; --loess: #e6dccb; --schiefer: #2f3236; --schiefer-tief: #272a2d;
--sand: #8a3a2b; --sand-tief: #74301f; --leise: #5a564f; --linie: #d8cfc0; --linie-stark: #b3a893;
--hell-leise: rgba(241, 236, 226, .76); --hell-linie: rgba(241, 236, 226, .2);
--auf: cubic-bezier(0.16, 1, 0.3, 1); --zu: cubic-bezier(0.7, 0, 0.84, 0); --ui: cubic-bezier(0.4, 0, 0.2, 1);
--lux: cubic-bezier(0.37, 0, 0.63, 1); --lux-aus: cubic-bezier(0.61, 1, 0.88, 1);
--serif: 'EB Garamond', Garamond, 'Times New Roman', serif;
--mono: 'DM Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
/* Böden: Farbe + Muster */
--b-muschelkalk: #d3cab7; --b-keuper: #b88a72; --b-loess: #d5b06f;
--b-hof: #c9c4ba; --b-garten: #a7b37f; --b-weide: #b6c08f; --b-acker: #b08d66;
}
* { box-sizing: border-box; margin: 0; }
html { background: var(--kalk); color: var(--text); font: 400 1.25rem/1.55 var(--serif); -webkit-text-size-adjust: 100%; }
:root:has(:focus-visible) { scroll-behavior: auto; }
:focus-visible { outline: 2px solid var(--text); outline-offset: 3px; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
.sr { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.nw { white-space: nowrap; }
.label { font: 500 .75rem/1.3 var(--mono); letter-spacing: .1em; text-transform: uppercase; }
/* Shop-Kopf (Umgebung) */
.shopkopf { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px clamp(16px, 4vw, 48px); border-bottom: 1px solid var(--linie); }
.marke { display: grid; line-height: 1; text-decoration: none; }
.marke b { font: 500 1.5rem/1 var(--serif); letter-spacing: .02em; }
.marke span { font: 400 .6875rem/1.4 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--leise); margin-top: 3px; }
.shopnav { display: flex; gap: 22px; font: 500 .75rem/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; }
.shopnav a { text-decoration: none; padding: 14px 0; }
.shopnav a[aria-current] { text-decoration: underline; text-underline-offset: 6px; }
/* Kapitel auf Schiefer */
.kapitel { background: var(--schiefer); color: var(--kalk); padding: clamp(28px, 5vw, 64px) clamp(16px, 4vw, 48px) clamp(48px, 7vw, 96px); }
.kapitel :focus-visible { outline-color: var(--kalk); }
.innen { max-width: 1280px; margin: 0 auto; }
.kapitel-kopf { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px clamp(24px, 5vw, 64px); align-items: end; margin-bottom: clamp(24px, 4vw, 44px); }
.kapitel-kopf .label { color: var(--hell-leise); grid-column: 1 / -1; }
.kapitel-kopf h2 { font: 400 clamp(2.25rem, 1.5rem + 2.6vw, 3.75rem)/1.02 var(--serif); letter-spacing: -0.015em; text-wrap: balance; }
.kapitel-kopf h2 em { font-style: italic; }
.kapitel-kopf p { color: var(--hell-leise); max-width: 52ch; text-wrap: pretty; }
.raster { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 56px); align-items: start; }
/* Karte */
.karte { position: sticky; top: 16px; margin: 0; display: grid; gap: 14px; container: karte / inline-size; }
.karte-svg { display: block; width: 100%; height: auto; background: var(--schiefer-tief); }
.k-wald path { fill: url(#p-wald); stroke: rgba(241, 236, 226, .16); stroke-width: .8; }
.k-hl path { fill: none; stroke: rgba(241, 236, 226, .17); stroke-width: .8; }
.k-hl-stark path { stroke: rgba(241, 236, 226, .36); stroke-width: 1.1; }
.k-hl path, .k-wald path, .k-fluss, .k-strasse, .k-bach, .k-dorf, .flaeche-grund, .flaeche-rand, .k-mass path, .k-tag rect { vector-effect: non-scaling-stroke; }
.karte-svg { --s: 1.2; }
.k-zahl { font: 400 calc(var(--s) * 10px) var(--mono); fill: rgba(241, 236, 226, .72); paint-order: stroke; stroke: var(--schiefer-tief); stroke-width: 3px; }
.k-fluss { fill: #3a454c; stroke: rgba(241, 236, 226, .3); stroke-width: 1; }
.k-mitte { fill: none; stroke: none; }
.k-fluss-name { font: italic 400 calc(var(--s) * 14px) var(--serif); fill: rgba(241, 236, 226, .78); letter-spacing: 2px; }
.k-strasse { fill: none; stroke: rgba(241, 236, 226, .34); stroke-width: 1.6; }
.k-bach { fill: none; stroke: #6f8790; stroke-width: 1.4; stroke-dasharray: 5 3; }
.k-dorf { fill: rgba(241, 236, 226, .1); stroke: rgba(241, 236, 226, .42); stroke-width: 1; }
.k-ort { font: 500 calc(var(--s) * 9.5px) var(--mono); letter-spacing: 3px; text-transform: uppercase; fill: var(--kalk); }
.k-klein { font: italic 400 calc(var(--s) * 11px) var(--serif); fill: rgba(241, 236, 226, .72); }
.k-nord path { fill: var(--kalk); } .k-nord text { font: 500 9px var(--mono); fill: var(--kalk); } .k-mass text { font: 500 calc(var(--s) * 9px) var(--mono); fill: var(--kalk); }
.k-mass path { fill: none; stroke: var(--kalk); stroke-width: 1.2; }
.k-flaeche { cursor: pointer; outline: none; }
.k-flaeche path { stroke: var(--schiefer-tief); stroke-width: 1.2; transition: fill-opacity 150ms var(--ui), stroke-width 150ms var(--ui), stroke 150ms var(--ui); }
.k-flaeche .flaeche-grund { fill-opacity: .88; }
.k-flaeche .flaeche-muster { pointer-events: none; }
.karte.hat-wahl .k-flaeche:not(.aktiv) .flaeche-grund { fill-opacity: .5; }
.k-flaeche.markiert .flaeche-grund, .k-flaeche:hover .flaeche-grund { fill-opacity: 1; }
.k-flaeche.markiert .flaeche-rand, .k-flaeche:hover .flaeche-rand { stroke: var(--kalk); stroke-width: 2; }
.k-flaeche.aktiv .flaeche-grund { fill-opacity: 1; }
.k-flaeche.aktiv .flaeche-rand { stroke: var(--kalk); stroke-width: 3.2; }
.k-flaeche:focus-visible .flaeche-rand { stroke: var(--kalk); stroke-width: 3.2; stroke-dasharray: 7 4; }
.flaeche-rand { fill: none; }
.k-tag rect { fill: var(--schiefer); stroke: rgba(241, 236, 226, .5); stroke-width: 1; transition: fill 150ms var(--ui); }
.k-tag text { font-family: var(--serif); font-style: italic; fill: var(--kalk); transition: fill 150ms var(--ui); }
.k-flaeche.aktiv .k-tag rect, .k-flaeche.markiert .k-tag rect, .k-flaeche:hover .k-tag rect, .k-flaeche:focus-visible .k-tag rect { fill: var(--kalk); stroke: var(--kalk); }
.k-flaeche.aktiv .k-tag text, .k-flaeche.markiert .k-tag text, .k-flaeche:hover .k-tag text, .k-flaeche:focus-visible .k-tag text { fill: var(--text); }
.karte figcaption { font: 400 .75rem/1.45 var(--mono); color: var(--hell-leise); }
.legende { display: flex; flex-wrap: wrap; gap: 8px 18px; padding: 0; list-style: none; font: 400 .8125rem/1.3 var(--mono); }
.legende li { display: inline-flex; align-items: center; gap: 8px; }
.legende svg { width: 30px; height: 18px; flex: none; }
.legende rect { stroke: var(--schiefer-tief); stroke-width: 1; }
/* Liste */
.liste-rahmen { container: liste / inline-size; min-width: 0; }
.liste { list-style: none; padding: 0; border-top: 1px solid var(--hell-linie); counter-reset: lage; }
.lage { border-bottom: 1px solid var(--hell-linie); counter-increment: lage; }
.lage-link { position: relative; display: grid; grid-template-columns: 2.2rem minmax(0, 1fr) auto; gap: 2px 12px; align-items: baseline; min-height: 64px; padding: 16px 0; text-decoration: none; }
.lage-link::before { content: counter(lage, decimal-leading-zero); font: 500 .75rem/1 var(--mono); color: var(--hell-leise); grid-row: 1 / span 2; padding-top: .3em; }
.lage-link .name { font: italic 400 1.875rem/1.1 var(--serif); }
.lage-link .name .ort { display: block; font: 500 .6875rem/1.4 var(--mono); font-style: normal; letter-spacing: .1em; text-transform: uppercase; color: var(--hell-leise); margin-bottom: 2px; }
.lage-link .meta { grid-column: 2; font: 400 .8125rem/1.45 var(--mono); color: var(--hell-leise); font-variant-numeric: tabular-nums; }
.lage-link .status { grid-column: 3; grid-row: 1; align-self: start; font: 500 .6875rem/1.2 var(--mono); letter-spacing: .06em; text-transform: uppercase; padding: 5px 7px; border: 1px solid rgba(241, 236, 226, .55); white-space: nowrap; }
.lage-link .status.ruht { border-style: dashed; }
.lage-link .zeichen { grid-column: 3; grid-row: 2; justify-self: end; width: 18px; height: 18px; position: relative; }
.lage-link .zeichen::before, .lage-link .zeichen::after { content: ''; position: absolute; left: 2px; right: 2px; top: 8px; height: 1.5px; background: currentColor; transition: transform 200ms var(--zu); }
.lage-link .zeichen::after { transform: rotate(90deg); }
.lage[data-offen] .lage-link .zeichen::after { transform: rotate(0deg); transition: transform 300ms var(--auf); }
.lage-link:hover .kn, .lage-link.markiert .kn { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 6px; }
.lage[data-offen] .lage-link .kn { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 6px; }
.panel { display: grid; grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows 200ms var(--zu), visibility 0s linear 200ms; }
.lage[data-offen] .panel { grid-template-rows: 1fr; visibility: visible; transition: grid-template-rows 300ms var(--auf), visibility 0s; }
.panel > div { overflow: hidden; min-height: 0; }
.blatt { background: var(--loess); color: var(--text); margin: 0 0 18px; padding: clamp(16px, 2.4vw, 24px); display: grid; gap: 16px; }
.blatt :focus-visible { outline-color: var(--text); }
.rueckweg { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; font: 500 .75rem/1.2 var(--mono); letter-spacing: .08em; text-transform: uppercase; text-decoration: none; border-bottom: 1px solid var(--text); justify-self: start; }
.rueckweg:hover { border-bottom-width: 2px; }
.foto { position: relative; margin: 0; aspect-ratio: 3 / 2; overflow: hidden; background: #cfc5b2; }
.foto img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; clip-path: inset(100% 0 0 0); transform: scale(1.06); }
.foto.gezeigt img { clip-path: inset(0 0 0 0); transform: none; transition: clip-path 1600ms var(--lux), transform 2200ms var(--lux-aus); }
.foto figcaption { position: absolute; left: 0; bottom: 0; padding: 5px 9px; background: rgba(34, 33, 31, .8); color: var(--kalk); font: 400 .6875rem/1.3 var(--mono); }
.daten { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr)); gap: 0 18px; }
.daten div { display: grid; align-content: start; gap: 2px; padding: 9px 0 10px; border-top: 1px solid var(--linie-stark); }
.daten div.breit { grid-column: 1 / -1; }
.daten dt { font: 500 .6875rem/1.4 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--leise); }
.daten dd { font: 400 .9375rem/1.4 var(--mono); font-variant-numeric: tabular-nums; }
.blatt .pruefen { justify-self: start; display: inline-flex; align-items: baseline; gap: 6px; font: 500 .6875rem/1.35 var(--mono); letter-spacing: .06em; color: var(--sand-tief); border: 1px dashed var(--sand); padding: 4px 7px; }
.blatt .pruefen::before { content: '§'; }
.blatt .text { font-size: 1.125rem; line-height: 1.55; max-width: 60ch; font-variant-numeric: oldstyle-nums; }
.hinweis { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: start; padding: 14px; border: 1.5px solid var(--text); background: var(--kalk); }
.hinweis .marke-h { font: 500 .6875rem/1.2 var(--mono); letter-spacing: .06em; text-transform: uppercase; padding: 6px 7px; border: 1px solid var(--text); white-space: nowrap; }
.hinweis p { font: 400 1rem/1.5 var(--serif); }
.weine h3 { font: 500 .75rem/1.3 var(--mono); letter-spacing: .1em; text-transform: uppercase; margin-bottom: 4px; }
.weine ul { list-style: none; padding: 0; }
.weine li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 14px; padding: 10px 0; border-top: 1px solid var(--linie-stark); }
.weine li a { font: 400 1.125rem/1.25 var(--serif); text-decoration: none; }
.weine li a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
.weine li .unter { grid-column: 1; font: 400 .75rem/1.4 var(--mono); color: var(--leise); font-variant-numeric: tabular-nums; }
.weine li .pr { grid-column: 2; grid-row: 1 / span 2; text-align: right; font: 500 .9375rem/1.3 var(--mono); font-variant-numeric: tabular-nums; }
.weine li .pr span { display: block; font: 400 .75rem/1.4 var(--mono); color: var(--leise); }
.weine li.aus .pr { color: var(--leise); font-weight: 400; font-size: .75rem; }
.weine .fuss { margin-top: 8px; font: 400 .75rem/1.5 var(--mono); color: var(--leise); }
.ecken { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 48px; padding: 0 20px; font: 500 .8125rem/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; text-decoration: none; color: var(--text); justify-self: start; }
.ecken::before, .ecken::after { content: ''; position: absolute; width: 12px; height: 12px; border: 1.5px solid currentColor; transition: width 310ms var(--zu), height 310ms var(--zu); }
.ecken::before { left: 0; top: 0; border-right: 0; border-bottom: 0; }
.ecken::after { right: 0; bottom: 0; border-left: 0; border-top: 0; }
.ecken:hover::before, .ecken:hover::after, .ecken:focus-visible::before, .ecken:focus-visible::after { width: calc(100% + 1px); height: calc(100% + 1px); transition: width 480ms var(--auf), height 480ms var(--auf); }
.ecken:focus-visible { outline: 1px solid var(--sand); outline-offset: 5px; }
.kapitel-fuss { display: flex; flex-wrap: wrap; gap: 12px 24px; align-items: center; margin-top: 26px; font: 400 .8125rem/1.5 var(--mono); color: var(--hell-leise); }
.kapitel-fuss .ecken { color: var(--kalk); }
/* Pult */
.pult { position: fixed; right: 14px; bottom: 14px; z-index: 40; display: grid; gap: 8px; max-width: min(460px, calc(100vw - 28px)); padding: 12px 14px; background: rgba(241, 236, 226, .97); color: var(--text); border: 1px solid var(--text); font: 500 .6875rem/1.45 var(--mono); letter-spacing: .06em; text-transform: uppercase; }
.pult .zeile-p { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.pult button { min-height: 30px; padding: 0 10px; border: 1px solid rgba(34, 33, 31, .55); font: inherit; letter-spacing: inherit; text-transform: inherit; }
.pult button[aria-pressed="true"] { background: var(--text); color: var(--kalk); }
.pult .werte { text-transform: none; letter-spacing: .02em; color: var(--leise); }
.pult .werte b { color: var(--text); font-weight: 500; }
/* Druckansicht (Schalter im Pult und echter Druck über beforeprint) */
body.druck .shopkopf, body.druck .pult, body.druck .kapitel-fuss .ecken, body.druck .lage-link .zeichen { display: none !important; }
body.druck .kapitel { background: #fff; color: #000; padding: 24px 0; }
body.druck .kapitel-kopf .label, body.druck .kapitel-kopf p, body.druck .lage-link .meta, body.druck .lage-link .name .ort, body.druck .lage-link::before, body.druck .karte figcaption, body.druck .kapitel-fuss { color: #333; }
body.druck .raster { grid-template-columns: minmax(0, 1fr); }
body.druck .karte { position: static; max-width: 560px; }
body.druck .karte-svg { background: #fff; }
body.druck .k-hl path { stroke: #999; } body.druck .k-hl-stark path { stroke: #555; }
body.druck .k-fluss { fill: #ddd; stroke: #888; } body.druck .k-strasse { stroke: #888; } body.druck .k-dorf { fill: #eee; stroke: #888; }
body.druck .k-ort, body.druck .k-fluss-name, body.druck .k-klein, body.druck .k-zahl, body.druck .k-nord text, body.druck .k-mass text { fill: #000; stroke: none; }
body.druck .k-nord path { fill: #000; } body.druck .k-mass path { stroke: #000; }
body.druck .k-wald path { stroke: #aaa; }
body.druck .k-flaeche .flaeche-grund { fill: #fff !important; fill-opacity: 1 !important; }
body.druck .k-flaeche .flaeche-rand { stroke: #000; stroke-width: 1.2; }
body.druck .k-tag rect { fill: #fff; stroke: #000; } body.druck .k-tag text { fill: #000; }
body.druck .liste, body.druck .lage { border-color: #999; }
body.druck .lage-link .status { border-color: #000; }
body.druck .panel { grid-template-rows: 1fr !important; visibility: visible !important; transition: none !important; }
body.druck .blatt { background: #fff; border: 1px solid #000; break-inside: avoid; }
body.druck .foto { aspect-ratio: 3 / 1; }
body.druck .foto img { clip-path: none; transform: none; }
body.druck .karte .legende { color: #000; }
@media print {
.pult, .shopkopf { display: none !important; }
.kapitel { background: #fff !important; color: #000 !important; }
@page { margin: 14mm; }
}
[hidden] { display: none !important; }
@media (max-width: 860px) {
.raster { grid-template-columns: minmax(0, 1fr); }
.karte { position: static; }
.kapitel-kopf { grid-template-columns: minmax(0, 1fr); }
.shopnav { display: none; }
}
@container liste (max-width: 500px) {
.lage-link { grid-template-columns: 2rem minmax(0, 1fr) 18px; }
.lage-link .status { grid-column: 2; grid-row: auto; justify-self: start; margin-top: 6px; }
.lage-link .zeichen { grid-column: 3; grid-row: 1; align-self: center; }
}
@container liste (max-width: 380px) {
.lage-link .name { font-size: 1.5rem; }
.weine li { grid-template-columns: minmax(0, 1fr); }
.weine li .pr { grid-column: 1; grid-row: auto; text-align: left; }
}
@media (hover: none) {
.ecken::before, .ecken::after { width: 12px; height: 12px; }
}
.vorschau body { overflow: hidden; }
.vorschau .kapitel-kopf p { display: none; }
.vorschau .kapitel { padding-top: 22px; padding-bottom: 70vh; }
.vorschau .kapitel-kopf { margin-bottom: 18px; }
:root.reduziert *, :root.reduziert *::before, :root.reduziert *::after { transition-duration: .01ms !important; transition-delay: 0s !important; animation-duration: .01ms !important; }
:root.reduziert .foto img { clip-path: none; transform: none; }
@media (forced-colors: active) {
button, [role=button], [role=tab], [role=switch], input, select, textarea { border: 1px solid ButtonText !important; }
.k-flaeche.aktiv .flaeche-rand, .k-flaeche:focus-visible .flaeche-rand { stroke: Highlight; }
}
</style>
</head>
<body>
<header class="shopkopf">
<a class="marke" href="#"><b>Hörlein</b><span>Weingut · Wellbach am Main</span></a>
<nav class="shopnav" aria-label="Hauptmenü"><a href="#">Weine</a><a href="#" aria-current="page">Lagen</a><a href="#">Weinproben</a><a href="#">Hofladen</a></nav>
</header>
<main>
<section class="kapitel" aria-labelledby="kapitel-h">
<div class="innen">
<div class="kapitel-kopf">
<p class="label" id="kapitel-label">Herkunft · Lagen</p>
<h2 id="kapitel-h">Fünf Lagen über dem Main</h2>
<p id="kapitel-text">Muschelkalk, Keuper und Löss auf einem Kilometer Hang. Jede Lage mit Boden, Ausrichtung, Neigung und den Weinen, die aus ihr kommen.</p>
</div>
<div class="raster">
<figure class="karte" id="karte" aria-labelledby="karte-text">
<svg class="karte-svg" id="karte-svg" viewBox="0 0 800 600" aria-labelledby="karte-titel">
<title id="karte-titel">Übersichtskarte Wellbach am Main</title>
<defs>
<pattern id="p-wald" width="10" height="10" patternUnits="userSpaceOnUse"><circle cx="3" cy="3" r="1.2" fill="rgba(241,236,226,.15)"/><circle cx="8" cy="8" r=".9" fill="rgba(241,236,226,.1)"/></pattern>
<pattern id="p-muschelkalk" width="12" height="8" patternUnits="userSpaceOnUse"><path d="M1 2h5M7 6h4" stroke="#2f3236" stroke-opacity=".55" stroke-width="1.2"/></pattern>
<pattern id="p-keuper" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><path d="M0 0v7" stroke="#2f3236" stroke-opacity=".5" stroke-width="1.4"/></pattern>
<pattern id="p-loess" width="7" height="7" patternUnits="userSpaceOnUse"><circle cx="3.5" cy="3.5" r="1.1" fill="#2f3236" fill-opacity=".55"/></pattern>
<pattern id="p-hof" width="8" height="8" patternUnits="userSpaceOnUse"><path d="M0 8L8 0" stroke="#2f3236" stroke-opacity=".5" stroke-width="1"/><path d="M0 0L8 8" stroke="#2f3236" stroke-opacity=".5" stroke-width="1"/></pattern>
<pattern id="p-garten" width="10" height="10" patternUnits="userSpaceOnUse"><rect x="1" y="1" width="3.5" height="3.5" fill="#2f3236" fill-opacity=".45"/><rect x="6" y="6" width="3.5" height="3.5" fill="#2f3236" fill-opacity=".45"/></pattern>
<pattern id="p-weide" width="10" height="8" patternUnits="userSpaceOnUse"><path d="M2 6l1.5-3 1.5 3M7 3l1.5-3 1.5 3" fill="none" stroke="#2f3236" stroke-opacity=".55" stroke-width="1"/></pattern>
<pattern id="p-acker" width="6" height="6" patternUnits="userSpaceOnUse"><path d="M0 1h6" stroke="#2f3236" stroke-opacity=".5" stroke-width="1.6"/></pattern>
</defs>
<g class="k-wald" aria-hidden="true"><path d="M299 230L126 230L120 227L150 214L121 186L115 186L15 176L12 200L1 198L0 1L441 0L444 15L458 44L470 78L470 153L482 154L482 168L476 189L465 198L449 206L441 208L429 214L423 214L416 176L291 182L299 230Z"/><path d="M619 176L608 174L602 168L596 154L676 154L657 162L648 170L619 176Z"/><path d="M735 160L714 160L702 156L690 156L684 154L756 154L735 160Z"/></g><g class="k-hl" aria-hidden="true"><path d="M799 350L789 349L754 352L722 361L698 363L680 370L668 379L648 383L629 395L604 395L582 405L557 407L534 419L502 425L491 432L475 438L468 438L458 434L414 437L396 435L377 436L361 433L339 435L326 433L310 434L289 431L280 433L266 440L224 448L189 448L174 445L127 444L108 441L62 444L46 440L26 437L0 437"/><path d="M523 599L530 586L538 584L549 584L552 583L553 580L549 569L550 564L553 561L562 560L569 564L577 560L581 559L587 560L599 567L601 573L598 579L602 590L616 594L627 593L634 596L640 596L644 599"/><path d="M729 599L732 594L746 585L752 574L760 570L765 564L776 561L788 566L799 564"/><path d="M683 599L694 591L698 586L699 581L699 573L702 569L706 566L713 565L717 566L723 570L727 581L727 586L720 595L719 599"/><path d="M353 599L361 586L368 582L374 581L378 581L384 586L392 588L395 592L396 599"/><path d="M651 599L658 597L662 599"/><path d="M799 304L787 305L741 320L724 324L675 330L647 330L622 344L611 347L590 344L573 345L543 344L529 341L523 337L518 331L510 308L506 301L503 300L499 302L487 313L473 331L448 358L434 368L412 372L396 379L391 378L384 370L375 367L360 367L339 370L321 375L267 398L240 407L228 409L211 407L171 398L152 392L139 390L129 388L79 386L62 390L47 386L28 384L0 383"/><path d="M799 287L783 287L753 298L740 299L718 305L700 305L682 309L664 309L648 311L634 315L611 326L589 326L564 325L548 320L537 308L533 294L528 251L526 246L523 244L519 247L487 282L478 297L465 309L457 319L447 326L436 331L426 342L417 343L392 337L352 344L339 350L329 352L308 364L292 368L242 388L224 391L195 389L179 385L150 371L128 366L97 364L62 369L38 365L15 365L0 362"/><path d="M516 42L517 39L516 36L510 31L509 34L511 39L514 42L516 42"/><path d="M799 267L773 271L751 280L738 284L719 285L705 288L696 288L686 290L672 289L631 301L619 302L609 305L567 304L558 301L553 295L550 288L545 269L544 256L543 238L545 183L543 161L545 135L544 113L535 81L528 70L524 67L523 74L530 104L535 149L531 178L524 199L502 244L487 265L475 278L468 288L447 305L432 312L412 319L400 321L384 320L369 321L328 332L320 335L301 346L282 352L260 363L225 374L205 375L189 372L170 365L153 353L147 351L106 348L90 350L72 351L44 345L20 344L0 340"/><path d="M483 0L489 25L508 76L518 107L522 136L521 170L518 186L514 198L492 240L480 257L460 281L451 288L443 292L424 297L413 298L403 302L387 304L376 304L335 312L305 322L270 338L238 354L214 359L199 359L181 354L170 348L160 340L153 337L117 331L98 332L77 332L69 330L47 320L0 318"/><path d="M799 240L763 260L751 262L725 271L682 270L661 275L623 280L609 284L580 283L568 278L564 273L558 260L554 242L554 226L557 195L557 142L554 108L534 39L523 17L518 0"/><path d="M465 0L468 11L485 56L502 106L505 122L507 157L507 173L505 182L499 197L473 238L444 258L428 265L387 266L331 280L292 286L264 299L246 314L221 324L213 325L203 325L158 313L150 309L138 300L124 297L114 291L98 284L58 278L31 281L0 276"/><path d="M799 202L788 203L767 209L754 217L746 224L733 228L706 230L683 234L668 234L650 236L639 242L623 246L604 246L592 244L585 240L579 232L575 215L571 154L571 133L566 100L548 35L540 16L536 0"/><path d="M457 0L465 27L479 59L494 104L497 120L500 159L497 181L486 205L465 229L450 240L432 248L422 250L413 250L398 246L388 246L367 252L349 260L336 264L295 268L274 273L253 284L233 297L215 303L198 302L183 298L166 296L152 287L139 283L113 269L93 263L73 252L66 252L33 258L20 258L0 255"/><path d="M799 183L785 184L767 188L741 201L718 208L668 208L643 214L620 224L609 228L603 228L597 225L591 219L586 207L579 162L577 132L572 92L544 0"/><path d="M448 0L457 28L472 63L481 89L489 124L490 165L487 184L482 194L467 211L448 224L422 229L377 229L361 232L328 245L312 249L299 247L283 248L256 258L244 266L228 274L209 275L188 272L174 266L151 263L121 250L106 239L95 234L73 233L62 229L48 226L37 228L24 227L0 221"/><path d="M799 160L789 160L774 164L760 171L730 182L712 184L697 177L678 177L670 179L659 187L623 197L613 198L604 194L596 185L591 172L586 144L580 86L574 68L565 43L553 0"/><path d="M437 0L440 15L455 46L469 89L475 120L478 149L476 168L469 185L464 189L451 194L439 200L422 205L367 206L351 208L343 210L331 218L305 219L264 232L246 235L236 239L224 238L213 241L202 239L188 243L178 240L159 229L126 220L114 216L105 211L76 201L56 202L39 196L16 192L7 186L0 184"/><path d="M799 136L744 146L732 150L715 150L706 146L696 145L686 142L676 143L649 153L634 163L618 166L609 164L601 156L597 148L594 126L591 79L576 36L565 0"/></g><g class="k-hl k-hl-stark" aria-hidden="true"><path d="M799 326L771 330L701 345L660 351L648 358L627 366L607 367L592 366L563 366L554 367L542 372L521 375L510 373L499 368L491 368L484 371L469 385L446 398L424 407L411 409L385 406L370 400L360 394L346 392L311 398L289 408L267 414L254 420L239 425L217 425L199 423L179 416L160 417L142 416L118 410L92 415L56 416L39 412L13 412L0 407"/><path d="M473 0L509 107L513 132L514 159L514 173L512 184L507 197L491 226L480 243L469 255L453 269L443 275L436 279L416 283L368 289L301 302L283 310L234 340L220 343L208 342L181 336L124 312L86 308L75 303L60 300L30 299L16 295L0 295"/><path d="M799 221L778 228L766 239L761 242L745 246L731 246L708 253L668 254L641 263L624 263L609 264L591 264L575 256L568 248L565 234L566 194L564 140L560 103L542 37L531 15L527 0"/><path d="M407 0L409 7L420 18L422 28L429 35L439 56L443 69L443 75L439 85L438 93L445 113L444 120L445 130L441 150L431 160L416 162L395 160L384 167L371 167L363 165L355 158L346 153L336 152L332 153L327 159L319 162L301 161L293 159L287 162L280 163L263 178L245 185L233 184L225 187L189 187L172 184L161 184L151 181L142 183L127 183L110 170L95 163L74 165L55 163L50 161L37 150L19 145L12 142L0 142"/><path d="M799 93L791 94L785 98L774 97L761 107L749 111L736 108L717 107L703 105L681 111L663 112L639 122L624 119L620 116L619 112L616 98L613 87L610 66L604 49L604 43L608 33L608 28L593 11L588 0"/><path d="M71 26L72 24L70 23L69 25L71 26"/><path d="M122 158L128 152L128 149L125 147L120 148L116 153L117 158L122 158"/></g><g aria-hidden="true"><text class="k-zahl" x="45" y="413" dy="-4">200</text><text class="k-zahl" x="44" y="299" dy="-4">250</text><text class="k-zahl" x="45" y="156" dy="-4">300</text></g><path class="k-fluss" d="M0 473L8 474L16 475L24 476L32 477L40 478L48 479L56 480L64 480L72 481L80 482L88 482L96 483L104 484L112 484L120 485L128 485L136 486L144 486L152 486L160 487L168 487L176 487L184 487L192 487L200 487L208 487L216 487L224 487L232 487L240 487L248 487L256 486L264 486L272 485L280 485L288 484L296 484L304 483L312 482L320 482L328 481L336 480L344 479L352 478L360 477L368 476L376 475L384 474L392 473L400 472L408 471L416 469L424 468L432 467L440 465L448 464L456 463L464 461L472 460L480 458L488 457L496 455L504 454L512 452L520 451L528 449L536 448L544 446L552 445L560 443L568 441L576 440L584 438L592 437L600 435L608 434L616 432L624 431L632 429L640 428L648 426L656 425L664 423L672 422L680 421L688 419L696 418L704 417L712 415L720 414L728 413L736 412L744 411L752 410L760 409L768 408L776 407L784 406L792 405L800 404L800 464L792 465L784 466L776 467L768 468L760 469L752 470L744 471L736 472L728 473L720 474L712 475L704 477L696 478L688 479L680 481L672 482L664 483L656 485L648 486L640 488L632 489L624 491L616 492L608 494L600 495L592 497L584 498L576 500L568 501L560 503L552 505L544 506L536 508L528 509L520 511L512 512L504 514L496 515L488 517L480 518L472 520L464 521L456 523L448 524L440 525L432 527L424 528L416 529L408 531L400 532L392 533L384 534L376 535L368 536L360 537L352 538L344 539L336 540L328 541L320 542L312 542L304 543L296 544L288 544L280 545L272 545L264 546L256 546L248 547L240 547L232 547L224 547L216 547L208 547L200 547L192 547L184 547L176 547L168 547L160 547L152 546L144 546L136 546L128 545L120 545L112 544L104 544L96 543L88 542L80 542L72 541L64 540L56 540L48 539L40 538L32 537L24 536L16 535L8 534L0 533Z" aria-hidden="true"/><path id="k-mitte" class="k-mitte" d="M0 503L8 504L16 505L24 506L32 507L40 508L48 509L56 510L64 510L72 511L80 512L88 512L96 513L104 514L112 514L120 515L128 515L136 516L144 516L152 516L160 517L168 517L176 517L184 517L192 517L200 517L208 517L216 517L224 517L232 517L240 517L248 517L256 516L264 516L272 515L280 515L288 514L296 514L304 513L312 512L320 512L328 511L336 510L344 509L352 508L360 507L368 506L376 505L384 504L392 503L400 502L408 501L416 499L424 498L432 497L440 495L448 494L456 493L464 491L472 490L480 488L488 487L496 485L504 484L512 482L520 481L528 479L536 478L544 476L552 475L560 473L568 471L576 470L584 468L592 467L600 465L608 464L616 462L624 461L632 459L640 458L648 456L656 455L664 453L672 452L680 451L688 449L696 448L704 447L712 445L720 444L728 443L736 442L744 441L752 440L760 439L768 438L776 437L784 436L792 435L800 434" aria-hidden="true"/><path class="k-strasse" d="M0 461L8 462L16 463L24 464L32 465L40 466L48 467L56 468L64 468L72 469L80 470L88 470L96 471L104 472L112 472L120 473L128 473L136 474L144 474L152 474L160 475L168 475L176 475L184 475L192 475L200 475L208 475L216 475L224 475L232 475L240 475L248 475L256 474L264 474L272 473L280 473L288 472L296 472L304 471L312 470L320 470L328 469L336 468L344 467L352 466L360 465L368 464L376 463L384 462L392 461L400 460L408 459L416 457L424 456L432 455L440 453L448 452L456 451L464 449L472 448L480 446L488 445L496 443L504 442L512 440L520 439L528 437L536 436L544 434L552 433L560 431L568 429L576 428L584 426L592 425L600 423L608 422L616 420L624 419L632 417L640 416L648 414L656 413L664 411L672 410L680 409L688 407L696 406L704 405L712 403L720 402L728 401L736 400L744 399L752 398L760 397L768 396L776 395L784 394L792 393L800 392" aria-hidden="true"/><path class="k-bach" d="M519 60L520 70L522 80L524 90L525 100L526 110L526 120L527 130L527 140L527 150L527 160L526 170L525 180L524 190L522 200L521 210L519 220L517 230L515 240L513 250L511 260L508 270L506 280L503 290L501 300L498 310L496 320L494 330L492 340L490 350L488 360L487 370L486 380L485 390L484 400L483 410L483 420L483 430L483 440L484 450L485 460" aria-hidden="true"/><path class="k-dorf" d="M330 392L470 384L478 432L452 446L330 446L318 420Z" aria-hidden="true"/><text class="k-ort" x="412" y="420" text-anchor="middle" aria-hidden="true">Wellbach</text><text class="k-fluss-name" aria-hidden="true" dy="5"><textPath href="#k-mitte" startOffset="30%">M a i n</textPath></text><text class="k-fluss-name" aria-hidden="true" dy="5"><textPath href="#k-mitte" startOffset="78%">M a i n</textPath></text><text class="k-klein" x="486" y="352" transform="rotate(-80 486 352)" aria-hidden="true">Klingenbach</text><g class="k-nord" id="k-nord" transform="translate(758 46)" aria-hidden="true"><path d="M0 -20 L7 8 L0 3 L-7 8Z"/><text y="22" text-anchor="middle">N</text></g><g class="k-mass" id="k-mass" transform="translate(24 572)" aria-hidden="true"><path d="M0 0H100M0 -5V5M50 -3V3M100 -5V5"/><text x="0" y="-9">0</text><text x="100" y="-9" text-anchor="middle">200 m</text></g>
<g id="flaechen" tabindex="-1"></g>
</svg>
<ul class="legende" id="legende" aria-label="Legende"></ul>
<figcaption id="karte-text">Eigene Zeichnung, kein Kartendienst. Höhenlinien alle 10 m, beschriftet alle 50 m. Die Liste enthält alle Angaben als Text.</figcaption>
</figure>
<div class="liste-rahmen"><ol class="liste" id="liste"></ol></div>
</div>
<div class="kapitel-fuss"><span id="fuss-text">Alle Daten aus dem Weinbergskataster des Guts.</span><button type="button" class="ecken" id="drucken">Lagen drucken</button></div>
</div>
</section>
</main>
<div class="pult" role="group" aria-label="Demo-Steuerung">
<div class="zeile-p" role="group" aria-label="Variante"><span>Variante</span>
<button type="button" data-v="weinberg">Weinberg</button><button type="button" data-v="obstwiese">Obstwiese</button><button type="button" data-v="hof">Hof</button></div>
<div class="zeile-p"><button type="button" id="von-wein">Von der Weinseite</button><button type="button" id="druckansicht" aria-pressed="false">Druckansicht</button><button type="button" id="uebersicht">Übersicht</button></div>
<div class="werte" id="werte">Steckbrief <b>300/200 ms</b> · Foto-Maske <b>1600 ms</b>, Skalierung <b>1.06 → 1 in 2200 ms</b> · Markierung <b>150 ms</b></div>
</div>
<script>
(() => {
const html = document.documentElement;
const reduziert = () => html.classList.contains('reduziert');
const q = new URLSearchParams(location.search);
const VARIANTEN = ['weinberg', 'obstwiese', 'hof'];
const GEO = {"sonnwendberg": {"poly": "M58 232L150 262L196 292L186 352L150 352L96 330L52 312L40 270Z", "mitte": [116, 300], "hoehe": [235, 280], "neigung": [18, 34], "ausrichtung": "Süd-Südwest", "flaeche": 4.8}, "grauer-buehl": {"poly": "M206 300L262 296L300 312L296 374L246 390L206 372Z", "mitte": [253, 341], "hoehe": [215, 270], "neigung": [23, 32], "ausrichtung": "Süd-Südost", "flaeche": 3.0}, "lettenacker": {"poly": "M392 236L452 222L470 262L468 316L420 332L386 292Z", "mitte": [431, 277], "hoehe": [220, 280], "neigung": [24, 41], "ausrichtung": "Süd-Südost", "flaeche": 2.8}, "kreuzleite": {"poly": "M548 214L596 206L612 262L600 318L560 326L546 268Z", "mitte": [577, 266], "hoehe": [220, 275], "neigung": [21, 43], "ausrichtung": "Südwest", "flaeche": 2.5}, "muehlwinkel": {"poly": "M630 316L722 308L752 330L738 364L650 368L624 344Z", "mitte": [686, 338], "hoehe": [185, 220], "neigung": [19, 31], "ausrichtung": "Süd-Südost", "flaeche": 2.4}, "zwetschgengrund": {"poly": "M506 96L546 94L548 150L542 206L508 210L504 150Z", "mitte": [526, 151], "hoehe": [225, 265], "neigung": [8, 70], "ausrichtung": "Ost", "flaeche": 1.8}, "birnbaumleite": {"poly": "M640 206L740 192L790 200L790 262L700 268L636 250Z", "mitte": [716, 230], "hoehe": [235, 275], "neigung": [18, 28], "ausrichtung": "Süd-Südost", "flaeche": 3.9}, "kirschrain": {"poly": "M14 176L120 186L150 214L110 230L30 224L10 206Z", "mitte": [72, 206], "hoehe": [280, 300], "neigung": [12, 19], "ausrichtung": "Süd-Südwest", "flaeche": 2.2}, "hohe-wiese": {"poly": "M290 182L416 176L424 226L300 236Z", "mitte": [358, 205], "hoehe": [280, 300], "neigung": [8, 22], "ausrichtung": "Süd", "flaeche": 2.6}, "hofstelle": {"poly": "M478 388L528 384L536 424L484 430Z", "mitte": [506, 406], "hoehe": [185, 200], "neigung": [5, 13], "ausrichtung": "Süd-Südost", "flaeche": 0.9}, "gemuesegarten": {"poly": "M536 392L588 388L594 422L540 426Z", "mitte": [564, 407], "hoehe": [185, 195], "neigung": [10, 13], "ausrichtung": "Süd-Südost", "flaeche": 0.7}, "huehnerweide": {"poly": "M598 378L672 372L678 402L602 412Z", "mitte": [638, 391], "hoehe": [180, 200], "neigung": [8, 18], "ausrichtung": "Süd-Südost", "flaeche": 1.0}, "kartoffelacker": {"poly": "M472 522L636 512L644 556L480 568Z", "mitte": [558, 540], "hoehe": [180, 190], "neigung": [2, 11], "ausrichtung": "Nord-Nordwest", "flaeche": 3.0}};
const euro = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' });
const zahl = (n, d) => new Intl.NumberFormat('de-DE', { minimumFractionDigits: d, maximumFractionDigits: d }).format(n);
const menge = (m, e) => e === 'l' ? zahl(m, m < 1 && (m * 100) % 10 ? 2 : (m % 1 ? 2 : 1)).replace(/(,\d)0$/, '$1') + ' l' : e === 'kg' ? zahl(m, m % 1 ? 1 : 0) + ' kg' : m + ' Stück';
const grund = (p, m, e) => e === 'l' ? `${euro.format(p / m)} / 1 l` : e === 'kg' ? `${euro.format(p / m)} / 1 kg` : `${euro.format(p / m)} je Stück`;
const BOEDEN = {
muschelkalk: ['Muschelkalk', 'var(--b-muschelkalk)', 'p-muschelkalk', 'kurze Striche'],
keuper: ['Lettenkeuper', 'var(--b-keuper)', 'p-keuper', 'Schraffur'],
loess: ['Löss-Lehm', 'var(--b-loess)', 'p-loess', 'Punkte'],
hof: ['Hofstelle', 'var(--b-hof)', 'p-hof', 'Kreuzschraffur'],
garten: ['Garten', 'var(--b-garten)', 'p-garten', 'Beete'],
weide: ['Weide', 'var(--b-weide)', 'p-weide', 'Grasbüschel'],
acker: ['Acker', 'var(--b-acker)', 'p-acker', 'Furchen']
};
const hm = (id) => `${GEO[id].hoehe[0]} bis ${GEO[id].hoehe[1]} m`;
const ng = (id) => `${GEO[id].neigung[0]} bis ${GEO[id].neigung[1]} %`;
const fl = (id) => `${zahl(GEO[id].flaeche, 1)} ha`;
const V = {
weinberg: {
ausschnitt: '0 140 800 430',
label: 'Herkunft · Lagen', titel: 'Fünf Lagen <em>über dem Main</em>', text: 'Muschelkalk, Keuper und Löss auf einem Kilometer Hang. Jede Lage mit Boden, Ausrichtung, Neigung und den Weinen, die aus ihr kommen.',
fuss: 'Höhe, Neigung und Fläche aus dem Weinbergskataster des Guts.', ding: 'Lage', weinWort: 'Weine aus dieser Lage',
orte: [
{ id: 'sonnwendberg', name: 'Wellbacher Sonnwendberg', kurz: 'Sonnwendberg', boden: 'muschelkalk', sorten: 'Silvaner, Riesling', klasse: 'Lagenwein (Einzellage)', seit: 'seit 1931 im Besitz',
text: 'Der steilste Hang des Guts, Wellenkalk mit wenig Feinerde. Die Zeilen laufen in der Falllinie, gearbeitet wird von Hand, gelesen in mehreren Durchgängen.',
alt: 'Südhang über dem Main im Herbst: Rebzeilen in Gelb und Rostrot laufen in der Falllinie, dazwischen Wege, oben der Waldsaum der Hochfläche',
weine: [{ j: '2024', name: 'Silvaner trocken', m: .75, e: 'l', preis: 14.5 }, { j: '2024', name: 'Silvaner trocken, Magnum', m: 1.5, e: 'l', preis: 32 }, { j: '2024', name: 'Riesling trocken', m: .75, e: 'l', preis: 16 }, { j: '2023', name: 'Silvaner trocken', m: .75, e: 'l', preis: 15.5, wenig: true }] },
{ id: 'grauer-buehl', name: 'Wellbacher Grauer Bühl', kurz: 'Grauer Bühl', boden: 'muschelkalk', sorten: 'Riesling, Weißburgunder', klasse: 'Lagenwein (Einzellage)', seit: 'seit 1964 im Besitz',
text: 'Ein Sporn über dem Dorf, grau vom verwitterten Kalk. Kühler Wind aus dem Tal hält die Säure; aus seinem Riesling entsteht der Winzersekt.',
alt: 'Sporn mit Rebparzellen in Gelb und Grün, Feldwege dazwischen, am Fuß die ersten Dächer von Wellbach',
weine: [{ j: '2022', name: 'Riesling Sekt brut', m: .75, e: 'l', preis: 19 }, { j: '2024', name: 'Riesling trocken', m: .75, e: 'l', preis: 15 }, { j: '2024', name: 'Weißburgunder trocken', m: .75, e: 'l', preis: 13.5 }] },
{ id: 'lettenacker', name: 'Wellbacher Lettenacker', kurz: 'Lettenacker', boden: 'keuper', sorten: 'Müller-Thurgau, Bacchus', klasse: 'Ortswein (Wellbacher)', seit: 'seit 1988 im Besitz', status: 'aus',
text: 'Schwerer, roter Lettenkeuper an der Westflanke der Klinge. Er hält das Wasser auch im trockenen Sommer, die Weine sind saftig und früh zugänglich.',
hinweis: ['ausgetrunken', 'Zurzeit ausgetrunken. Die Weine des Jahrgangs 2025 sind verkauft; der Jahrgang 2026 liegt im Keller und kommt im März 2027.'],
alt: 'Blick über die Dächer und den Kirchturm von Wellbach auf den Hang mit Rebzeilen, rechts das Seitental',
weine: [{ j: '2025', name: 'Müller-Thurgau trocken', m: .75, e: 'l', preis: 9.5, aus: true }, { j: '2025', name: 'Bacchus feinherb', m: .75, e: 'l', preis: 10.5, aus: true }] },
{ id: 'kreuzleite', name: 'Wellbacher Kreuzleite', kurz: 'Kreuzleite', boden: 'muschelkalk', sorten: 'Riesling', klasse: 'Lagenwein ab Jahrgang 2025', seit: 'gepachtet seit 2025', status: 'pacht',
text: 'Die Westseite der Klinge, Nachmittagssonne bis zum Abend. Alte Riesling-Reben aus den 1980er Jahren, bis 2024 von einem Wellbacher Nebenerwerbswinzer bewirtschaftet.',
hinweis: ['gepachtet', 'Seit Januar 2025 gepachtet. Die erste Lese reift im Holzfass; der erste Riesling aus der Kreuzleite kommt im Frühjahr 2027.'],
alt: 'Westseite des Seitentals mit Rebzeilen, vorn Dächer und eine Scheune am Ortsrand',
weine: [] },
{ id: 'muehlwinkel', name: 'Wellbacher Mühlwinkel', kurz: 'Mühlwinkel', boden: 'loess', sorten: 'Silvaner (Jungfeld)', klasse: 'Gutswein', seit: 'neu bepflanzt 2024', status: 'neu',
text: 'Löss-Lehm über Muschelkalk am Fuß des Hangs, nahe der alten Mühle. Im Frühjahr 2024 neu bepflanzt mit Silvaner aus eigener Auslese der alten Stöcke vom Sonnwendberg.',
hinweis: ['neu bepflanzt', 'Die jungen Reben tragen noch nicht. Erster Ertrag frühestens 2027; bis dahin gibt es aus dem Mühlwinkel keinen Wein.'],
alt: 'Unterer Hang über dem Main mit jungen Reben, davor eine grüne Weide mit Mobilstall und der Fluss',
weine: [] }
]
},
obstwiese: {
ausschnitt: '0 50 800 420',
label: 'Herkunft · Streuobst', titel: 'Vier Wiesen <em>für die Brennerei</em>', text: 'Hochstämme auf Löss, Kalk und Keuper. Jede Wiese mit Sorten, Bäumen, Baumalter, Erntejahr und den Bränden, die aus ihr kommen.',
fuss: 'Bäume gezählt im Winter 2025/26. Brände nur an Personen ab 18 Jahren.', ding: 'Wiese', weinWort: 'Brände aus dieser Wiese',
orte: [
{ id: 'zwetschgengrund', name: 'Zwetschgengrund', kurz: 'Zwetschgengrund', boden: 'loess', sorten: 'Hauszwetschge, Bühler Frühzwetschge', baeume: '62', alter: '40 bis 70 Jahre', ernte: 'September 2025',
text: 'Alte Hochstämme im Talgrund der Klinge. Gemäht wird zweimal im Jahr, das Obst wird aufgelesen, nicht geschüttelt.',
alt: 'Seitental mit einzelnen Zwetschgenbäumen am Osthang, links Rebzeilen am Gegenhang',
weine: [{ j: 'Ernte 2024', name: 'Zwetschgenwasser, 42 % vol', m: .5, e: 'l', preis: 32 }, { j: 'Ernte 2024', name: 'Zwetschgenwasser, 42 % vol', m: .35, e: 'l', preis: 24 }] },
{ id: 'birnbaumleite', name: 'Birnbaumleite', kurz: 'Birnbaumleite', boden: 'muschelkalk', sorten: 'Williams Christ, Gute Luise, Schweizer Wasserbirne', baeume: '48', alter: '25 bis 35 Jahre', ernte: 'August 2025', status: 'aus',
text: 'Oberer Hang unter dem Wald, kalkiger Boden, viel Wind. Die Williams-Birnen reifen hier spät und bleiben fest.',
hinweis: ['ausgetrunken', 'Der Williams-Birnenbrand aus der Ernte 2024 ist ausgetrunken. Der Brand der Ernte 2025 ruht im Steingut und kommt im Herbst 2027.'],
alt: 'Obstbäume auf einer Wiese am oberen Hang, darunter Rebparzellen im Herbstlaub',
weine: [{ j: 'Ernte 2024', name: 'Williams-Birnenbrand, 40 % vol', m: .5, e: 'l', preis: 38, aus: true }] },
{ id: 'kirschrain', name: 'Kirschrain', kurz: 'Kirschrain', boden: 'keuper', sorten: 'Schwarze Knorpelkirsche, Dollenseppler', baeume: '36', alter: '50 bis 90 Jahre', ernte: 'Juli 2025',
text: 'An der Hangkante im Westen, schwerer Keuper. Die ältesten Bäume des Hofs; gepflückt wird mit Leiter, ein Baum am Tag.',
alt: 'Hochstamm-Kirschbäume auf einer Wiese an der Hangkante, vorn Reben im Gelb des Herbstes',
weine: [{ j: 'Ernte 2024', name: 'Kirschwasser, 42 % vol', m: .35, e: 'l', preis: 29 }, { j: 'Ernte 2024', name: 'Kirschwasser, 42 % vol', m: .5, e: 'l', preis: 39 }] },
{ id: 'hohe-wiese', name: 'Hohe Wiese', kurz: 'Hohe Wiese', boden: 'loess', sorten: 'Bohnapfel, Brettacher, Oberösterreichische Weinbirne', baeume: '40 Jungbäume', alter: 'gepflanzt 2023', ernte: 'noch keine', status: 'neu',
text: 'Früher Acker, seit 2023 Streuobstwiese. Alte Sorten aus der Gegend, veredelt auf Sämlingsunterlage für große Kronen.',
hinweis: ['neu gepflanzt', 'Die Bäume tragen noch nicht. Erster Brand aus der Hohen Wiese frühestens aus der Ernte 2030.'],
alt: 'Wiese an der Hangkante mit frisch gepflanzten Jungbäumen, dahinter Waldsaum',
weine: [] }
]
},
hof: {
ausschnitt: '400 330 320 260',
label: 'Hof · Felder und Ställe', titel: 'Was <em>vor der Tür</em> wächst', text: 'Vom Acker in den Hofladen: Felder, Garten, Weide und Stall rund um die Hofstelle, mit dem, was gerade wächst, und dem, was es im Laden gibt.',
fuss: 'Hofladen: Freitag 14 bis 18 Uhr, Samstag 9 bis 14 Uhr.', ding: 'Fläche', weinWort: 'Im Hofladen',
orte: [
{ id: 'hofstelle', name: 'Hofstelle mit Hofladen', kurz: 'Hofstelle', boden: 'hof', waechst: 'Hofladen, Vinothek, Brennerei; Stall mit sechs Rhönschafen für die Steillagen', saison: 'ganzjährig',
text: 'Wohnhaus, Scheune und Stall am Ortsrand. Die Rhönschafe weiden im Winter zwischen den Rebzeilen und halten das Gras kurz.',
alt: 'Hofstelle am Ortsrand mit Wohnhaus, Scheune und Stall, davor der Main',
weine: [{ j: 'Hofladen', name: 'Traubensaft vom Silvaner', m: 1, e: 'l', preis: 4.2 }, { j: 'Hofladen', name: 'Lammsalami', m: .15, e: 'kg', preis: 6.9 }] },
{ id: 'gemuesegarten', name: 'Gemüsegarten', kurz: 'Garten', boden: 'garten', waechst: 'Tomaten, Bohnen, Mangold, Hokkaido-Kürbis', saison: 'Mai bis Oktober; jetzt Kürbis und Mangold',
text: 'Beete in Streifen neben der Scheune, gegossen aus dem Brunnen. Was nicht verkauft wird, geht in die Hofküche.',
alt: 'Gemüsebeete in Streifen neben der Hofstelle, im Vordergrund der Main',
weine: [{ j: 'jetzt', name: 'Hokkaido-Kürbis', m: 1, e: 'Stück', preis: 3.5 }, { j: 'jetzt', name: 'Mangold, Bund', m: .5, e: 'kg', preis: 2.4 }] },
{ id: 'huehnerweide', name: 'Hühnerweide', kurz: 'Hühnerweide', boden: 'weide', waechst: '220 Legehennen und drei Hähne im Mobilstall', saison: 'ganzjährig; im November Mauser',
text: 'Der Mobilstall zieht alle zwei Wochen ein Stück weiter, damit die Grasnarbe sich erholt. Im November legen die Hennen weniger.',
hinweis: ['Mauser', 'Im November gibt es nur freitags Eier, und nur so viele, wie gelegt werden.'],
alt: 'Grüne Weide mit weißem Mobilstall zwischen Fluss und Rebhang',
weine: [{ j: 'freitags', name: 'Eier, 10 Stück', m: 10, e: 'Stück', preis: 4.2 }] },
{ id: 'kartoffelacker', name: 'Kartoffelacker am Ried', kurz: 'Kartoffelacker', boden: 'acker', waechst: 'Linda, Annabelle; nach der Ernte Zwischenfrucht', saison: 'Ernte August bis September', seit: 'gepachtet seit 2026', status: 'pacht',
text: 'Leichter Lössboden am Fuß des Hangs, seit diesem Jahr dazugepachtet. Nach der Ernte steht Phacelia als Zwischenfrucht.',
alt: 'Abgeernteter, brauner Acker am Fuß des Rebhangs, davor Ufer und Main',
weine: [{ j: 'Ernte 2026', name: 'Kartoffeln Linda, festkochend', m: 2.5, e: 'kg', preis: 4.5 }, { j: 'Ernte 2026', name: 'Kartoffeln Linda, Sack', m: 10, e: 'kg', preis: 14 }] }
]
}
};
const zustand = { v: VARIANTEN.includes(q.get('v')) ? q.get('v') : 'weinberg', offen: null, von: q.get('von') || '' };
const liste = document.getElementById('liste'), flaechenG = document.getElementById('flaechen'), karte = document.getElementById('karte'), svg = document.getElementById('karte-svg');
function statusText(o) {
if (o.status === 'aus') return ['ausgetrunken', 'ruht'];
if (o.status === 'pacht') return [o.seit, ''];
if (o.status === 'neu') return [o.seit || 'neu gepflanzt', 'ruht'];
const n = o.weine.filter(w => !w.aus).length;
return [zustand.v === 'hof' ? `${n} im Laden` : `${n} ${zustand.v === 'obstwiese' ? (n === 1 ? 'Brand' : 'Brände') : (n === 1 ? 'Wein' : 'Weine')}`, ''];
}
function datenZeilen(o) {
const g = GEO[o.id], B = BOEDEN[o.boden][0];
if (zustand.v === 'weinberg') return [['Boden', B], ['Ausrichtung', g.ausrichtung], ['Neigung', ng(o.id)], ['Höhe', hm(o.id)], ['Fläche', fl(o.id)], ['Rebsorten', o.sorten], ['Klassifikation', o.klasse, 'breit'], ['Besitz', o.seit, 'breit']];
if (zustand.v === 'obstwiese') return [['Boden', B], ['Ausrichtung', g.ausrichtung], ['Neigung', ng(o.id)], ['Höhe', hm(o.id)], ['Fläche', fl(o.id)], ['Bäume', o.baeume], ['Baumalter', o.alter], ['Letzte Ernte', o.ernte], ['Sorten', o.sorten, 'breit']];
return [['Nutzung', B], ['Boden', 'Löss-Lehm'], ['Fläche', fl(o.id)], ['Höhe', hm(o.id)], ['Was hier ist', o.waechst, 'breit'], ['Saison', o.saison, 'breit']].concat(o.seit ? [['Status', o.seit, 'breit']] : []);
}
function metaText(o) {
const g = GEO[o.id];
const t = x => `<span class="nw">${x}</span>`;
if (zustand.v === 'hof') return `${t(BOEDEN[o.boden][0])} · ${t(fl(o.id))}`;
return `${t(BOEDEN[o.boden][0])} · ${t(g.ausrichtung)} · ${t(ng(o.id))} · ${t(fl(o.id))}`;
}
function blatt(o) {
const vv = V[zustand.v];
const weine = o.weine.length ? `<div class="weine"><h3>${vv.weinWort}</h3><ul>${o.weine.map(w => `<li class="${w.aus ? 'aus' : ''}">
<a href="#wein">${w.name}</a><span class="unter">${w.j} · <span class="nw">${menge(w.m, w.e)}</span>${w.wenig ? ' · nur noch wenige' : ''}</span>
<span class="pr">${w.aus ? 'ausgetrunken' : `${euro.format(w.preis)}<span>${w.e === 'Stück' && w.m === 1 ? 'je Stück' : grund(w.preis, w.m, w.e)}</span>`}</span></li>`).join('')}</ul>
<p class="fuss">${zustand.v === 'hof' ? 'Preise inkl. MwSt.; Grundpreis je kg oder l.' : 'Preise inkl. 19 % MwSt., zzgl. Versand; Grundpreis je Liter.'}${zustand.v === 'obstwiese' ? ' Abgabe ab 18 Jahren.' : zustand.v === 'weinberg' ? ' Abgabe ab 16 Jahren.' : ''}</p></div>` : '';
const rueck = zustand.von && o.id === 'sonnwendberg' ? `<a class="rueckweg" href="#wein" id="rueckweg">← Zurück zum Wein: 2024 Silvaner trocken</a>` : '';
return `<div class="blatt">${rueck}
<figure class="foto"><img src="bilder/lagen-steckbrief-karte-${o.id}.webp" width="900" height="600" alt="${o.alt}" loading="lazy"><figcaption>Eigene Ansicht · Wellbach am Main</figcaption></figure>
<dl class="daten">${datenZeilen(o).map(([a, b, k]) => `<div${k ? ' class="breit"' : ''}><dt>${a}</dt><dd>${b}</dd></div>`).join('')}</dl>
<p class="pruefen">Platzhalter · ${zustand.v === 'weinberg' ? 'Lagendaten und Klassifikation' : zustand.v === 'obstwiese' ? 'Flächen- und Sortenangaben' : 'Flächenangaben'} vom Kunden prüfen lassen</p>
<p class="text">${o.text}</p>
${o.hinweis ? `<div class="hinweis" role="note"><span class="marke-h">${o.hinweis[0]}</span><p>${o.hinweis[1]}</p></div>` : ''}
${weine}
<a class="ecken" href="#${zustand.v === 'hof' ? 'hofladen' : 'weine'}">${zustand.v === 'hof' ? 'Zum Hofladen' : zustand.v === 'obstwiese' ? 'Alle Brände der Wiese' : 'Alle Weine der Lage'}</a>
</div>`;
}
function tagMasse() {
const w = svg.getBoundingClientRect().width || 800, vbox = svg.viewBox.baseVal, vb = vbox.width || 800, sk = vb / w;
svg.style.setProperty('--s', sk.toFixed(3));
document.getElementById('k-nord').setAttribute('transform', `translate(${vbox.x + vbox.width - 30 * sk} ${vbox.y + 34 * sk}) scale(${sk.toFixed(3)})`);
document.getElementById('k-mass').setAttribute('transform', `translate(${vbox.x + 18 * sk} ${vbox.y + vbox.height - 16 * sk})`);
const mt = document.getElementById('k-mass'); mt.querySelectorAll('text').forEach(t => t.setAttribute('y', (-9 * sk).toFixed(1)));
mt.querySelector('path').setAttribute('d', `M0 0H100M0 ${-5 * sk}V${5 * sk}M50 ${-3 * sk}V${3 * sk}M100 ${-5 * sk}V${5 * sk}`);
const k = Math.min(2.6, Math.max(.3, vb / w * .9));
flaechenG.querySelectorAll('.k-tag').forEach(t => {
const tx = t.querySelector('text'), fs = 14 * k;
tx.setAttribute('font-size', fs.toFixed(1));
const b = tx.getComputedTextLength ? tx.getComputedTextLength() : fs * 6;
const r = t.querySelector('rect');
r.setAttribute('x', (-b / 2 - 7 * k).toFixed(1)); r.setAttribute('width', (b + 14 * k).toFixed(1));
r.setAttribute('y', (-12 * k).toFixed(1)); r.setAttribute('height', (24 * k).toFixed(1));
tx.setAttribute('dy', (5 * k).toFixed(1));
});
}
function zeichne() {
const vv = V[zustand.v];
document.getElementById('kapitel-label').textContent = vv.label;
document.getElementById('kapitel-h').innerHTML = vv.titel;
document.getElementById('kapitel-text').textContent = vv.text;
document.getElementById('fuss-text').textContent = vv.fuss;
document.getElementById('drucken').textContent = `${vv.ding === 'Lage' ? 'Lagen' : vv.ding === 'Wiese' ? 'Wiesen' : 'Flächen'} drucken`;
liste.setAttribute('aria-label', vv.ding === 'Lage' ? 'Lagen' : vv.ding === 'Wiese' ? 'Wiesen' : 'Flächen');
liste.innerHTML = vv.orte.map(o => {
const [st, art] = statusText(o);
return `<li class="lage" id="lage-${o.id}" data-id="${o.id}">
<a class="lage-link" href="#lage-${o.id}" aria-expanded="false" aria-controls="panel-${o.id}"><span class="name">${o.name.startsWith('Wellbacher ') ? `<span class="ort">Wellbacher </span><span class="kn">${o.name.slice(11)}</span>` : `<span class="kn">${o.name}</span>`}</span><span class="meta">${metaText(o)}</span><span class="status ${art}">${st}</span><span class="zeichen" aria-hidden="true"></span></a>
<div class="panel" id="panel-${o.id}" inert><div>${blatt(o)}</div></div></li>`;
}).join('');
flaechenG.innerHTML = vv.orte.map(o => {
const g = GEO[o.id], b = BOEDEN[o.boden];
return `<a class="k-flaeche" href="#lage-${o.id}" data-id="${o.id}" aria-label="${vv.ding} ${o.name}, ${b[0]}${zustand.v !== 'hof' ? ', ' + g.ausrichtung : ''}">
<path class="flaeche-grund" d="${g.poly}" style="fill:${b[1]}"/><path class="flaeche-muster" d="${g.poly}" fill="url(#${b[2]})"/><path class="flaeche-rand" d="${g.poly}"/>
<g class="k-tag" transform="translate(${g.mitte[0]} ${g.mitte[1]})"><rect rx="0"/><text text-anchor="middle">${o.kurz}</text></g></a>`;
}).join('');
svg.setAttribute('viewBox', vv.ausschnitt);
const vx = Number(vv.ausschnitt.split(' ')[0]);
svg.querySelector('.k-ort').setAttribute('x', Math.max(412, vx + 50));
const boeden = [...new Set(vv.orte.map(o => o.boden))];
document.getElementById('legende').innerHTML = boeden.map(b => `<li><svg viewBox="0 0 30 18" aria-hidden="true"><rect width="30" height="18" style="fill:${BOEDEN[b][1]}"/><rect width="30" height="18" fill="url(#${BOEDEN[b][2]})"/></svg>${BOEDEN[b][0]}<span class="sr"> (${BOEDEN[b][3]})</span></li>`).join('');
document.querySelectorAll('.pult [data-v]').forEach(b => b.setAttribute('aria-pressed', String(b.dataset.v === zustand.v)));
tagMasse();
zustand.offen = null; karte.classList.remove('hat-wahl');
}
function oeffne(id, opt = {}) {
const neu = zustand.offen === id && !opt.immer ? null : id;
liste.querySelectorAll('.lage').forEach(li => {
const an = li.dataset.id === neu, link = li.querySelector('.lage-link'), panel = li.querySelector('.panel');
if (an) { li.setAttribute('data-offen', ''); panel.inert = false; } else { li.removeAttribute('data-offen'); panel.inert = true; }
link.setAttribute('aria-expanded', String(an));
if (an) {
const f = li.querySelector('.foto');
if (f && !f.classList.contains('gezeigt')) { const img = f.querySelector('img'); const los = () => requestAnimationFrame(() => requestAnimationFrame(() => f.classList.add('gezeigt'))); if (img.complete) los(); else { img.loading = 'eager'; img.addEventListener('load', los, { once: true }); img.addEventListener('error', los, { once: true }); } }
}
});
flaechenG.querySelectorAll('.k-flaeche').forEach(a => a.classList.toggle('aktiv', a.dataset.id === neu));
karte.classList.toggle('hat-wahl', !!neu);
zustand.offen = neu;
const p = new URLSearchParams(location.search);
history.replaceState(null, '', '?' + p.toString().replace(/=(?=&|$)/g, '') + (neu ? '#lage-' + neu : ''));
if (neu && opt.zeigen) {
const li = document.getElementById('lage-' + neu);
setTimeout(() => li.scrollIntoView({ behavior: reduziert() || opt.sofort ? 'auto' : 'smooth', block: 'start' }), opt.sofort || reduziert() ? 0 : 320);
}
}
function markiere(id) {
flaechenG.querySelectorAll('.k-flaeche').forEach(a => a.classList.toggle('markiert', a.dataset.id === id));
liste.querySelectorAll('.lage-link').forEach(a => a.classList.toggle('markiert', a.closest('.lage').dataset.id === id));
}
liste.addEventListener('click', e => {
const a = e.target.closest('.lage-link'); if (a) { e.preventDefault(); oeffne(a.closest('.lage').dataset.id); return; }
const r = e.target.closest('#rueckweg'); if (r) { e.preventDefault(); if (document.referrer && history.length > 1) history.back(); return; }
const w = e.target.closest('a[href^="#"]'); if (w) e.preventDefault();
});
flaechenG.addEventListener('click', e => { const a = e.target.closest('.k-flaeche'); if (a) { e.preventDefault(); oeffne(a.dataset.id, { immer: true, zeigen: true }); } });
[liste, flaechenG].forEach(el => {
el.addEventListener('pointerover', e => { const t = e.target.closest('[data-id], .lage-link'); const id = t && (t.dataset.id || t.closest('.lage')?.dataset.id); markiere(id || null); });
el.addEventListener('pointerleave', () => markiere(null));
el.addEventListener('focusin', e => { const t = e.target.closest('.k-flaeche, .lage-link'); if (t) markiere(t.dataset.id || t.closest('.lage').dataset.id); });
el.addEventListener('focusout', () => markiere(null));
});
addEventListener('resize', tagMasse);
function setzeVariante(v, schreiben = true) {
zustand.v = v;
if (schreiben) { const p = new URLSearchParams(location.search); p.set('v', v); p.delete('von'); zustand.von = ''; history.replaceState(null, '', '?' + p.toString().replace(/=(?=&|$)/g, '')); }
zeichne();
}
function druck(an) {
document.body.classList.toggle('druck', an);
document.getElementById('druckansicht').setAttribute('aria-pressed', String(an));
liste.querySelectorAll('.panel').forEach(p => p.inert = an ? false : !p.closest('.lage').hasAttribute('data-offen'));
liste.querySelectorAll('.foto').forEach(f => an && f.classList.add('gezeigt'));
}
function vonWein() {
if (zustand.v !== 'weinberg') setzeVariante('weinberg');
zustand.von = 'silvaner-2024';
const p = new URLSearchParams(location.search); p.set('von', zustand.von); history.replaceState(null, '', '?' + p.toString());
zeichne(); oeffne('sonnwendberg', { immer: true, zeigen: true });
}
document.querySelector('.pult').addEventListener('click', e => {
const b = e.target.closest('[data-v]'); if (b) { pause(); setzeVariante(b.dataset.v); return; }
if (e.target.closest('#von-wein')) { pause(); vonWein(); }
if (e.target.closest('#druckansicht')) { pause(); druck(!document.body.classList.contains('druck')); }
if (e.target.closest('#uebersicht')) { pause(); oeffne(null); scrollTo(0, 0); }
});
document.getElementById('drucken').addEventListener('click', () => window.print());
addEventListener('beforeprint', () => druck(true));
addEventListener('afterprint', () => druck(false));
/* Vorführung */
let schritte = [], laeuft = false;
const warte = (ms, f) => schritte.push(setTimeout(f, ms));
function neustart() { schritte.forEach(clearTimeout); schritte = []; druck(false); zeichne(); scrollTo(0, 0); }
function vorfuehren() {
pause(); laeuft = true; html.classList.add('vorfuehrung');
neustart();
const ids = V[zustand.v].orte.map(o => o.id);
warte(700, () => markiere(ids[0]));
warte(1500, () => { markiere(null); oeffne(ids[0], { immer: true }); });
warte(4300, () => markiere(ids[2]));
warte(5000, () => { markiere(null); oeffne(ids[2], { immer: true, zeigen: true }); });
warte(7800, () => oeffne(ids[3], { immer: true, zeigen: true }));
warte(10400, () => oeffne(zustand.v === 'weinberg' ? ids[4] : ids[1], { immer: true, zeigen: true }));
if (zustand.v === 'weinberg') { warte(13000, () => { scrollTo(0, 0); vonWein(); }); warte(16200, () => { if (laeuft) vorfuehren(); }); }
else warte(13400, () => { if (laeuft) vorfuehren(); });
}
function pause() { laeuft = false; schritte.forEach(clearTimeout); schritte = []; html.classList.remove('vorfuehrung'); }
function weiter() { vorfuehren(); }
zeichne();
const ziel = location.hash.match(/^#lage-([\w-]+)$/);
if (ziel && V[zustand.v].orte.some(o => o.id === ziel[1])) oeffne(ziel[1], { immer: true, zeigen: true, sofort: true });
if (q.has('druck')) druck(true);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(tagMasse);
})();
</script>
</body>
</html>