{"id":3088,"date":"2025-12-04T08:09:58","date_gmt":"2025-12-04T08:09:58","guid":{"rendered":"https:\/\/kovara-xwebun.org\/?page_id=3088"},"modified":"2026-06-01T12:13:30","modified_gmt":"2026-06-01T12:13:30","slug":"home","status":"publish","type":"page","link":"https:\/\/kovara-xwebun1.org\/","title":{"rendered":"HOME &#8211; Slide\/Snap"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"3088\" class=\"elementor elementor-3088\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b5ca0e4 e-con-full mainco e-flex e-con e-parent\" data-id=\"b5ca0e4\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-c0fb387 e-con-full vert e-flex e-con e-child\" data-id=\"c0fb387\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3bac5a7 elementor-fixed elementor-widget elementor-widget-heading\" data-id=\"3bac5a7\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;fixed&quot;}\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">JETZT ERH\u00c4LTLICH...<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c3c8413 elementor-widget elementor-widget-html\" data-id=\"c3c8413\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\n\/* ===== Magnetic Vertical Snap (Loop OFF + Dot Nav ON) \u2013 mobile-safe sizing ===== *\/\n\nhtml, body {\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  \/* JS is the only animator (avoid double-easing conflicts) *\/\n  scroll-behavior: auto !important;\n  \/* Prevent rubber-band overscroll leaking to the page *\/\n  overscroll-behavior-y: contain;\n}\n\n\/* Elementor wrapper: NO inner scroller \u2014 let the window scroll *\/\n.elementor-element.mainco {\n  display: block;\n  overflow: visible !important;\n}\n\n\/* Each slide *\/\n.elementor-element.mainco .elementor-element.vert {\n  \/* Fill the visible viewport even when the address bar shows\/hides *\/\n  height: 100dvh;          \/* dynamic viewport height (modern iOS\/Android) *\/\n  min-height: 100svh;      \/* never shrink below the \u201csmall\u201d viewport *\/\n  width: 100%;\n\n  \/* Compositor hints (smoother paints during snaps) *\/\n  contain: paint;\n  backface-visibility: hidden;\n  will-change: transform;\n\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  box-sizing: border-box;\n}\n\n\/* Fallback for older engines without dvh *\/\n@supports not (height: 100dvh) {\n  :root { --vh: 1vh; }     \/* JS will update this *\/\n  .elementor-element.mainco .elementor-element.vert {\n    height: calc(var(--vh) * 100);\n  }\n}\n\n\/* Dot navigation *\/\n.dot-nav {\n  position: fixed;\n  top: 50%;\n  right: 30px;\n  transform: translateY(-50%);\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  z-index: 9999;\n  pointer-events: auto;\n}\n.dot-nav .dot {\n  width: 12px; height: 12px; border-radius: 50%;\n  background: #aaa; cursor: pointer;\n  transition: background .25s, transform .25s;\n}\n.dot-nav .dot.active {\n  background: #333;\n  transform: scale(1.2);\n}\n\n\/* If you have a fixed header, you can set scroll padding like this: *\/\n\/* html, body { scroll-padding-top: 64px; } *\/\n\n<\/style>\n<script>\ndocument.addEventListener('DOMContentLoaded', () => {\n  \/* --- 0) Dynamic VH fallback: keep slides full-height when the address bar moves --- *\/\n  function setVH() {\n    const vh = (window.visualViewport ? window.visualViewport.height : window.innerHeight) * 0.01;\n    document.documentElement.style.setProperty('--vh', `${vh}px`);\n  }\n  setVH();\n  window.addEventListener('resize', setVH);\n  window.addEventListener('orientationchange', setVH);\n  if (window.visualViewport) {\n    window.visualViewport.addEventListener('resize', setVH);\n    window.visualViewport.addEventListener('scroll', setVH);\n  }\n\n  \/* --- 1) Grab sections --- *\/\n  const sections = Array.from(document.querySelectorAll('.elementor-element.mainco .elementor-element.vert'));\n  if (!sections.length) return;\n\n  \/* --- 2) Build dot nav (DOTS = ON) --- *\/\n  const dotNav = document.createElement('div');\n  dotNav.className = 'dot-nav';\n  sections.forEach((_, i) => {\n    const dot = document.createElement('div');\n    dot.className = 'dot' + (i === 0 ? ' active' : '');\n    dot.addEventListener('click', () => goTo(i));\n    dotNav.appendChild(dot);\n  });\n  document.body.appendChild(dotNav);\n  const dots = Array.from(dotNav.querySelectorAll('.dot'));\n\n  \/* --- 3) iOS detection + reduced motion --- *\/\n  const isIOS = (() => {\n    const ua = navigator.userAgent || navigator.vendor || window.opera;\n    const touchMac = \/Macintosh\/.test(ua) && 'ontouchend' in document;\n    return \/iPad|iPhone|iPod\/.test(ua) || touchMac;\n  })();\n  const prefersReduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n  \/* --- 4) Measure targets --- *\/\n  let tops = [];\n  function computeTops() {\n    tops = sections.map(s => Math.round(s.getBoundingClientRect().top + window.scrollY));\n  }\n  computeTops();\n\n  function nearestIndex(y = window.scrollY) {\n    let idx = 0, best = Infinity;\n    for (let i = 0; i < tops.length; i++) {\n      const d = Math.abs(y - tops[i]);\n      if (d < best) { best = d; idx = i; }\n    }\n    return idx;\n  }\n\n  function setActive(i) {\n    dots.forEach((d, k) => d.classList.toggle('active', k === i));\n  }\n\n  \/* --- 5) Smooth scroll (iOS polyfill), LOOP = OFF --- *\/\n  let isAnimating = false;\n\n  function rafScrollTo(targetY, duration = 520) {\n    const startY = window.scrollY;\n    const dist = targetY - startY;\n    const startT = performance.now();\n    const ease = (t) => t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t + 2, 3) \/ 2; \/\/ easeInOutCubic\n    function step(now) {\n      const p = Math.min(1, (now - startT) \/ duration);\n      const y = startY + dist * ease(p);\n      window.scrollTo(0, y);\n      if (p < 1 && isAnimating) requestAnimationFrame(step);\n      else isAnimating = false;\n    }\n    requestAnimationFrame(step);\n  }\n\n  function clamp(i) {\n    return Math.max(0, Math.min(sections.length - 1, i));\n  }\n\n  function goTo(index) {\n    index = clamp(index); \/\/ LOOP OFF (no wrap-around)\n\n    isAnimating = true;\n    const y = tops[index];\n    setActive(index);\n\n    if (prefersReduced) {\n      window.scrollTo(0, y);\n      isAnimating = false;\n      return;\n    }\n\n    if (isIOS) {\n      rafScrollTo(y, 520);\n    } else {\n      window.scrollTo({ top: y, behavior: 'smooth' });\n      if ('onscrollend' in window) {\n        const onEnd = () => { window.removeEventListener('scrollend', onEnd); isAnimating = false; };\n        window.addEventListener('scrollend', onEnd, { once: true });\n      } else {\n        setTimeout(() => { isAnimating = false; }, 520);\n      }\n    }\n  }\n\n  \/* --- 6) Wheel (desktop): one notch = one panel --- *\/\n  let lastWheel = 0;\n  const WHEEL_DELAY = 340;\n\n  function normalizeDelta(e) {\n    let d = e.deltaY;\n    if (e.deltaMode === 1) d *= 16;\n    else if (e.deltaMode === 2) d *= window.innerHeight;\n    return d;\n  }\n\n  window.addEventListener('wheel', (e) => {\n    if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) return; \/\/ ignore horizontal wheels\n    const now = Date.now();\n    if (isAnimating || (now - lastWheel) < WHEEL_DELAY) { e.preventDefault(); return; }\n\n    const delta = normalizeDelta(e);\n    if (Math.abs(delta) < 10) return;\n\n    e.preventDefault();\n    const cur = nearestIndex();\n    const next = clamp(cur + (delta > 0 ? 1 : -1));\n    if (next !== cur) {\n      goTo(next);\n      lastWheel = now;\n    }\n  }, { passive: false });\n\n  \/* --- 7) Touch swipe (mobile): distance OR velocity triggers --- *\/\n  let startY = 0, startT = 0, committed = false;\n  const SWIPE_THRESHOLD = 36;      \/\/ px\n  const VELOCITY_THRESHOLD = 0.35; \/\/ px\/ms\n\n  window.addEventListener('touchstart', (e) => {\n    if (!e.touches || !e.touches.length) return;\n    if (isAnimating) { e.preventDefault(); return; } \/\/ lock during snap\n    committed = false;\n    startY = e.touches[0].clientY;\n    startT = performance.now();\n  }, { passive: false });\n\n  window.addEventListener('touchmove', (e) => {\n    if (!e.touches || !e.touches.length || isAnimating) return;\n    const dy = startY - e.touches[0].clientY;\n    const dt = Math.max(1, performance.now() - startT);\n    const velocity = Math.abs(dy) \/ dt;\n\n    if (!committed && (Math.abs(dy) > SWIPE_THRESHOLD || velocity > VELOCITY_THRESHOLD)) {\n      e.preventDefault(); \/\/ take over native scroll\n      committed = true;\n      const cur = nearestIndex();\n      const next = clamp(cur + (dy > 0 ? 1 : -1)); \/\/ LOOP OFF\n      if (next !== cur) goTo(next);\n    }\n  }, { passive: false });\n\n  window.addEventListener('touchend', () => {\n    if (!committed && !isAnimating) {\n      goTo(nearestIndex());\n    }\n  }, { passive: true });\n\n  \/* --- 8) Keyboard navigation (Arrow\/PageUp\/PageDown\/Space\/Home\/End) --- *\/\n  const KEY_MAP = { ArrowDown:1, ArrowUp:-1, PageDown:1, PageUp:-1, ' ':1 };\n  const isTypingContext = (t) => {\n    const tag = (t.tagName || '').toLowerCase();\n    return tag === 'input' || tag === 'textarea' || t.isContentEditable;\n  };\n\n  document.addEventListener('keydown', (e) => {\n    if (isTypingContext(e.target)) return;  \/\/ don't hijack input fields\n    if (isAnimating) { e.preventDefault(); return; }\n\n    if (e.key === 'Home') { e.preventDefault(); goTo(0); return; }\n    if (e.key === 'End')  { e.preventDefault(); goTo(sections.length - 1); return; }\n\n    if (e.key in KEY_MAP) {\n      e.preventDefault();\n      const dir = KEY_MAP[e.key];\n      const cur = nearestIndex();\n      const next = clamp(cur + dir); \/\/ LOOP OFF\n      if (next !== cur) goTo(next);\n    }\n  });\n\n  \/* --- 9) Keep targets fresh on layout\/UI changes --- *\/\n  let recomputeTimer;\n  const refresh = () => {\n    clearTimeout(recomputeTimer);\n    recomputeTimer = setTimeout(() => {\n      computeTops();\n      setActive(nearestIndex());\n    }, 120);\n  };\n  window.addEventListener('resize', refresh);\n  window.addEventListener('orientationchange', refresh);\n  if (window.visualViewport) {\n    window.visualViewport.addEventListener('resize', refresh);\n    window.visualViewport.addEventListener('scroll', refresh);\n  }\n  setTimeout(refresh, 650); \/\/ after Elementor\/fonts settle\n\n  \/* --- 10) Dot sync during free\/inertia scroll --- *\/\n  let rafId = null;\n  window.addEventListener('scroll', () => {\n    if (rafId) return;\n    rafId = requestAnimationFrame(() => {\n      setActive(nearestIndex());\n      rafId = null;\n    });\n  }, { passive: true });\n});\n<\/script>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-acad40e e-con-full vert e-flex e-con e-child\" data-id=\"acad40e\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f6adec8 elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"f6adec8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<iframe data-testid=\"embed-iframe\" style=\"border-radius:12px\" src=\"https:\/\/open.spotify.com\/embed\/episode\/1W5Nx9FOp5vHMwPYjO8VcS?utm_source=generator\" width=\"100%\" height=\"352\" frameBorder=\"0\" allowfullscreen=\"\" allow=\"autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture\" loading=\"lazy\"><\/iframe>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ffdcda3 elementor-fixed elementor-widget elementor-widget-heading\" data-id=\"ffdcda3\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;fixed&quot;}\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">AUF SPOTIFY H\u00d6REN<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-1449ed5 e-con-full vert e-flex e-con e-child\" data-id=\"1449ed5\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>JETZT ERH\u00c4LTLICH&#8230; AUF SPOTIFY H\u00d6REN<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-3088","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/pages\/3088","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/comments?post=3088"}],"version-history":[{"count":34,"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/pages\/3088\/revisions"}],"predecessor-version":[{"id":3282,"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/pages\/3088\/revisions\/3282"}],"wp:attachment":[{"href":"https:\/\/kovara-xwebun1.org\/index.php\/wp-json\/wp\/v2\/media?parent=3088"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}