const DS = window.IseeITDesignSystem_37fb1f;
// Sanftes Scrollen zu einem Element — dieselbe Kurve wie die Anker-Navigation der App,
// nutzbar auch auf Unterseiten (Fachgebiete, Unternehmen).
window.sanftZu = function (el, versatz) {
if (typeof el === 'string') el = document.getElementById(el);
if (!el) return;
var ziel = Math.max(0, el.getBoundingClientRect().top + window.pageYOffset - (versatz == null ? 100 : versatz));
var start = window.pageYOffset, weg = ziel - start;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches || Math.abs(weg) < 40) { window.scrollTo(0, ziel); return; }
var dauer = Math.min(1900, 620 + Math.abs(weg) * 0.34), t0 = performance.now();
var schritt = function (t) {
var p = Math.min(1, (t - t0) / dauer);
var e = p < .5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2;
window.scrollTo(0, start + weg * e);
if (p < 1) requestAnimationFrame(schritt);
};
requestAnimationFrame(schritt);
};
function useBlobSrc(url, fortschritt) {
const [src, setSrc] = React.useState(null);
React.useEffect(() => {
if (!url) { setSrc(null); return; }
let alive = true, obj;
fetch(url).then(async r => {
if (!r.ok) throw new Error('http ' + r.status);
const laenge = fortschritt ? +(r.headers.get('Content-Length') || 0) : 0;
if (!fortschritt || !r.body || !laenge) return r.blob();
const leser = r.body.getReader(); const teile = []; let da = 0;
for (;;) { const s = await leser.read(); if (s.done) break; teile.push(s.value); da += s.value.length; if (alive) fortschritt(Math.min(1, da / laenge)); }
return new Blob(teile, { type: r.headers.get('Content-Type') || 'video/mp4' });
}).then(b => { if (!alive) return; obj = URL.createObjectURL(b); setSrc(obj); }).catch(() => { if (alive && fortschritt) setSrc('fehlgeschlagen'); });
return () => { alive = false; if (obj) URL.revokeObjectURL(obj); };
}, [url]);
return src;
}
// Sprachumschalter: ein Regler — eine helle Platte gleitet in einem gemeinsamen
// Rahmen zur gewählten Sprache. Die nicht gewählten Tasten bleiben vollständig
// durchsichtig (das Hero-Video scheint durch); die gewählte trägt ihre eigene Farbe.
const SPRACHFARBE = { de: '#A63A1E', en: '#221E19', es: '#A3521A', el: '#0E6E74' }; // konstant: liegt stets auf der cremefarbenen Platte
const SPRACHEN_KURZ = [['de', 'DE'], ['en', 'EN'], ['el', 'EL'], ['es', 'ES']];
const SPRACHEN_LANG = [['de', 'Deutsch'], ['en', 'English'], ['el', 'Ελληνικά'], ['es', 'Español']];
function SprachPlatte({ eintraege, lang, waehle, label, gross, fuellen, fett }) {
const box = React.useRef(null);
const [p, setP] = React.useState({ x: 0, b: 0, sitzt: false });
const [an, setAn] = React.useState(false);
const messen = React.useCallback(() => {
const w = box.current; if (!w) return;
const el = w.querySelector('[data-aktiv="ja"]'); if (!el) return;
const x = el.offsetLeft, b = el.offsetWidth;
setP(v => (v.sitzt && Math.abs(v.x - x) < .5 && Math.abs(v.b - b) < .5) ? v : { x, b, sitzt: true });
}, []);
React.useLayoutEffect(() => { messen(); }, [messen, lang, eintraege, gross, fuellen]);
React.useEffect(() => {
const f = () => messen();
window.addEventListener('resize', f, { passive: true });
if (document.fonts && document.fonts.ready) document.fonts.ready.then(f).catch(() => {});
const id = requestAnimationFrame(() => setAn(true));
return () => { window.removeEventListener('resize', f); cancelAnimationFrame(id); };
}, [messen]);
const still = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const taste = (aktiv, code) => ({
position: 'relative', zIndex: 1, appearance: 'none', WebkitAppearance: 'none', background: 'none', border: 0, borderRadius: 0, margin: 0, cursor: 'pointer', boxSizing: 'border-box',
padding: gross ? '10px 15px' : fuellen ? '13px 8px' : '7px 4px',
minWidth: gross || fuellen ? 'auto' : '38px',
minHeight: fuellen ? '44px' : 'auto',
flex: fuellen ? '1 1 0' : '0 0 auto',
textAlign: 'center',
font: gross ? (fett ? "700 13.5px/1 var(--font-grotesk)" : "500 13.5px/1 var(--font-grotesk)") : (fett ? "700 11.5px/1 var(--font-mono)" : "500 11.5px/1 var(--font-mono)"),
letterSpacing: gross ? '-.005em' : '.06em',
color: aktiv ? SPRACHFARBE[code] : (fett ? '#FBF8F3' : 'rgba(251,248,243,.82)'),
textShadow: aktiv ? 'none' : (fett ? '0 1px 12px rgba(34,30,25,.66), 0 1px 3px rgba(34,30,25,.35)' : '0 1px 12px rgba(34,30,25,.55)'),
/* Der Text färbt sich einen Wimpernschlag nach dem Eintreffen der Platte. */
transition: 'color .42s ease ' + (aktiv ? '.14s' : '0s')
});
return
{/* Die Platte ragt 1px über den Rahmen hinaus: an der gewählten Sprache verschwindet die Umrandung unter ihr, statt als dunkler Saum durchzuscheinen. */}
{eintraege.map(([code, txt]) => )}
;
}
// Hell/Dunkel-Schalter: Sonne/Mond im Header, Wahl bleibt gespeichert (Startwert: Systemeinstellung).
function ThemaKnopf() {
const { t } = React.useContext(window.SpracheCtx);
const [dunkel, setDunkel] = React.useState(() => document.documentElement.getAttribute('data-thema') === 'dunkel');
const [hov, setHov] = React.useState(false);
// Einmaliges Aufleuchten: erst wenn der Hero betrachtet wurde und der Besucher ein Stück gescrollt hat — derselbe Lichtstrahl wie auf der Ladeblende.
const [blitz, setBlitz] = React.useState(false);
React.useEffect(() => {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
let t1, t2, aus = false;
const beiScroll = () => {
if (aus || window.pageYOffset < window.innerHeight * 1.05) return;
aus = true; window.removeEventListener('scroll', beiScroll);
t1 = setTimeout(() => { setBlitz(true); t2 = setTimeout(() => setBlitz(false), 1600); }, 350);
};
window.addEventListener('scroll', beiScroll, { passive: true });
return () => { window.removeEventListener('scroll', beiScroll); clearTimeout(t1); clearTimeout(t2); };
}, []);
const wechsle = () => setDunkel(d => {
const n = !d;
if (n) document.documentElement.setAttribute('data-thema', 'dunkel'); else document.documentElement.removeAttribute('data-thema');
try { localStorage.setItem('iseeit-thema', n ? 'dunkel' : 'hell'); } catch (e) {}
return n;
});
const label = dunkel ? (t.nav.themaHell || 'Helles Design') : (t.nav.themaDunkel || 'Dunkles Design');
return ;
}
function SiteHeader({ onNav, onGoto }) {
const { t, lang, waehle } = React.useContext(window.SpracheCtx);
const schmal = window.useSchmal();
const [offen, setOffen] = React.useState(false);
const [prog, setProg] = React.useState(0);
const [kopfH, setKopfH] = React.useState(58);
const kopfRef = React.useRef(null);
const reiheRef = React.useRef(null), navRef = React.useRef(null), logoRef = React.useRef(null), grpRef = React.useRef(null);
/* Griechisch braucht in der Navigation deutlich mehr Breite als Englisch. Statt die
Zeile schrumpfen und überlappen zu lassen, misst die Kopfzeile den Bedarf: passt die
Navigation nicht neben Logo, Sprachen und Kontakt, wandert sie ins Menü. */
const [engpass, setEngpass] = React.useState(false);
React.useEffect(() => {
if (schmal) { setEngpass(false); return; }
const messe = () => {
const reihe = reiheRef.current, nav = navRef.current, logo = logoRef.current, grp = grpRef.current;
if (!reihe || !nav || !logo || !grp) return;
const cs = getComputedStyle(reihe);
const platz = reihe.clientWidth - parseFloat(cs.paddingLeft) - parseFloat(cs.paddingRight);
const luecke = parseFloat(cs.columnGap) || 12;
const brauche = logo.offsetWidth + nav.scrollWidth + grp.offsetWidth + 2 * luecke;
setEngpass(e => (e ? brauche + 12 > platz : brauche > platz));
};
messe();
window.addEventListener('resize', messe, { passive: true });
const id = requestAnimationFrame(messe);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(messe).catch(() => {});
return () => { window.removeEventListener('resize', messe); cancelAnimationFrame(id); };
}, [schmal, lang]);
React.useEffect(() => {
const f = () => { const m = document.documentElement.scrollHeight - window.innerHeight; setProg(m > 0 ? Math.min(1, window.pageYOffset / m) : 0); };
window.addEventListener('scroll', f, { passive: true }); f();
return () => window.removeEventListener('scroll', f);
}, []);
React.useEffect(() => { if (!schmal && !engpass) setOffen(false); }, [schmal, engpass]);
React.useEffect(() => {
document.body.style.overflow = offen ? 'hidden' : '';
return () => { document.body.style.overflow = ''; };
}, [offen]);
React.useEffect(() => {
if (!offen) return;
const f = (e) => { if (e.key === 'Escape') setOffen(false); };
window.addEventListener('keydown', f);
return () => window.removeEventListener('keydown', f);
}, [offen]);
React.useLayoutEffect(() => { if (offen && kopfRef.current) setKopfH(kopfRef.current.offsetHeight); }, [offen, schmal, engpass]);
const [kHov, setKHov] = React.useState(false);
const zu = () => setOffen(false);
const geh = (id) => (e) => { e.preventDefault(); zu(); onNav(id); };
const eintraege = [['wahl', t.nav.wahl], ['prozess', t.nav.prozess], ['ankommen', t.nav.ankommen], ['ueber-uns', t.nav.wir], ['faq', t.nav.fragen]];
const L = (id, txt) => e.target.style.color = 'var(--gold-hell)'} onMouseLeave={e => e.target.style.color = 'var(--sektion-text)'}>{txt};
const mLnk = { color: 'var(--sektion-text)', textDecoration: 'none', font: "500 19px/1.25 var(--font-grotesk)", padding: '16px 0', borderBottom: '1px solid rgba(251,248,243,.12)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '14px' };
return