Files
Schmacko-napf/assets/site.js
T
NotPika69andClaude Opus 5.5 fef4ac8eae Prüfdurchgang: Buchungslinks, alte Adressen, Tablet/Handy-Layout
- Buchungslinks direkt auf Portal-IDs; Ernährungsberatung auf id=107,
  Kaufberatung entfernt (im Portal gibt es keine Seite dafür)
- .htaccess: alte WordPress-Adressen per 301 auf die neuen Seiten
- Tablet bis 980 px: Logo-Siegel über den Kacheln statt darauf
- Handy: lange Wörter werden nicht mehr abgeschnitten, Pfeile
  überdecken keinen Text mehr, „Buchen“ neben dem Menü-Knopf
- Dreier-Karten: Überschriften auf einer Linie (Subgrid)
- Menü schließt mit Escape und Tipp daneben; sichtbarer Fokusrahmen
- Startseite Handy/Tablet: Kopfleiste, sobald das Siegel weg ist
- 404: Pfade ab der Wurzel statt <base>, Sprunglink bleibt auf der Seite

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:32:54 +02:00

83 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(() => {
const body = document.body;
// Mobiles Menü – schließt auch mit Escape oder Tipp daneben
const burger = document.querySelector('.burger');
if (burger) {
const setMenu = open => {
body.classList.toggle('menu-open', open);
burger.setAttribute('aria-expanded', open);
burger.setAttribute('aria-label', open ? 'Menü schließen' : 'Menü öffnen');
};
burger.addEventListener('click', () => setMenu(!body.classList.contains('menu-open')));
document.querySelectorAll('.nav a').forEach(a => a.addEventListener('click', () => setMenu(false)));
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && body.classList.contains('menu-open')) { setMenu(false); burger.focus(); }
});
document.addEventListener('click', e => {
if (body.classList.contains('menu-open') && !e.target.closest('.nav, .burger')) setMenu(false);
});
}
// Startseite: Header einblenden, sobald der Hero verlassen wird. Auf Handy/Tablet steht das
// Siegel über den Kacheln – dort kommt der Header schon, sobald das Siegel aus dem Bild ist.
const hero = document.querySelector('.hero');
const seal = document.querySelector('.seal');
if (body.classList.contains('is-home') && hero) {
const past = { hero: false, seal: false };
const update = () => body.classList.toggle('scrolled', past.hero || past.seal);
new IntersectionObserver(([e]) => { past.hero = !e.isIntersecting; update(); }, {
rootMargin: '-120px 0px 0px 0px'
}).observe(hero);
if (seal) new IntersectionObserver(([e]) => {
past.seal = !e.isIntersecting && e.boundingClientRect.top < 0 && getComputedStyle(seal).position === 'static';
update();
}).observe(seal);
}
// Ring um das Logo-Siegel: Kreis + die vier Kachel-Lücken, Übergänge mit Radius f abgerundet
const joint = document.querySelector('.seal-joint');
if (seal && joint) {
const drawJoint = () => {
const gap = parseFloat(getComputedStyle(seal.parentElement).columnGap) || 14;
const h = gap / 2; // halbe Lückenbreite
const R = seal.offsetWidth / 2 + gap; // Außenradius des Rings
const f = 18; // Radius der Rundung
const yd = Math.sqrt((R + f) ** 2 - (h + f) ** 2);
const k = R / (R + f);
const L = Math.ceil(R + f + 2); // Arme reichen bis zum Rand der Fläche
const rot = ([x, y], n) => { for (let i = 0; i < n; i++) [x, y] = [-y, x]; return [x + L, y + L]; };
const pt = (p, n) => rot(p, n).map(v => v.toFixed(2)).join(' ');
let d = `M${pt([-(h + f) * k, -yd * k], 0)}`;
for (let n = 0; n < 4; n++) {
d += ` A${f} ${f} 0 0 0 ${pt([-h, -yd], n)}`
+ ` L${pt([-h, -L], n)} L${pt([h, -L], n)} L${pt([h, -yd], n)}`
+ ` A${f} ${f} 0 0 0 ${pt([(h + f) * k, -yd * k], n)}`
+ ` A${R} ${R} 0 0 1 ${pt([-(h + f) * k, -yd * k], n + 1)}`;
}
joint.setAttribute('width', 2 * L);
joint.setAttribute('height', 2 * L);
joint.setAttribute('viewBox', `0 0 ${2 * L} ${2 * L}`);
joint.firstElementChild.setAttribute('d', d + ' Z');
};
drawJoint();
addEventListener('resize', drawJoint);
}
// Elemente beim Scrollen einblenden
const items = document.querySelectorAll('.reveal');
if ('IntersectionObserver' in window) {
const io = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add('in');
io.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
items.forEach(el => io.observe(el));
} else {
items.forEach(el => el.classList.add('in'));
}
})();