diff --git a/parfum-shop/public/atmos-discovery-set-thumbnail.png b/parfum-shop/public/atmos-discovery-set-thumbnail.png
new file mode 100755
index 0000000..59bb364
Binary files /dev/null and b/parfum-shop/public/atmos-discovery-set-thumbnail.png differ
diff --git a/parfum-shop/public/blasse-seide-product-sample-image.png b/parfum-shop/public/blasse-seide-product-sample-image.png
new file mode 100755
index 0000000..f85610f
Binary files /dev/null and b/parfum-shop/public/blasse-seide-product-sample-image.png differ
diff --git a/parfum-shop/public/kalter-beton-product-sample-image.png b/parfum-shop/public/kalter-beton-product-sample-image.png
new file mode 100755
index 0000000..b2ed715
Binary files /dev/null and b/parfum-shop/public/kalter-beton-product-sample-image.png differ
diff --git a/parfum-shop/public/nasser-marmor-product-sample-image.png b/parfum-shop/public/nasser-marmor-product-sample-image.png
new file mode 100755
index 0000000..dd59ab7
Binary files /dev/null and b/parfum-shop/public/nasser-marmor-product-sample-image.png differ
diff --git a/parfum-shop/public/schwarzes-benzin-product-sample-image.png b/parfum-shop/public/schwarzes-benzin-product-sample-image.png
new file mode 100755
index 0000000..cb0f6c6
Binary files /dev/null and b/parfum-shop/public/schwarzes-benzin-product-sample-image.png differ
diff --git a/parfum-shop/public/verbrannteschrom-product-sample-image.png b/parfum-shop/public/verbrannteschrom-product-sample-image.png
new file mode 100755
index 0000000..aeeedc0
Binary files /dev/null and b/parfum-shop/public/verbrannteschrom-product-sample-image.png differ
diff --git a/parfum-shop/public/weisse-asche-product-sample-image.png b/parfum-shop/public/weisse-asche-product-sample-image.png
new file mode 100755
index 0000000..4210ffc
Binary files /dev/null and b/parfum-shop/public/weisse-asche-product-sample-image.png differ
diff --git a/parfum-shop/src/hooks/useScrollTextReveal.js b/parfum-shop/src/hooks/useScrollTextReveal.js
new file mode 100644
index 0000000..3f6c9a8
--- /dev/null
+++ b/parfum-shop/src/hooks/useScrollTextReveal.js
@@ -0,0 +1,168 @@
+import { useLayoutEffect } from "react";
+import { gsap } from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+
+let pluginsRegistered = false;
+
+const registerGsap = () => {
+ if (!pluginsRegistered) {
+ gsap.registerPlugin(ScrollTrigger);
+ pluginsRegistered = true;
+ }
+};
+
+const createRevealLines = (element) => {
+ if (!element) {
+ return [];
+ }
+
+ if (element.dataset.revealPrepared === "true") {
+ return Array.from(element.querySelectorAll(".reveal-line"));
+ }
+
+ const originalHtml = element.innerHTML;
+ const segments = originalHtml
+ .split(/
/i)
+ .map((segment) => segment.trim())
+ .filter(Boolean);
+
+ if (segments.length === 0) {
+ return [];
+ }
+
+ element.dataset.revealPrepared = "true";
+ element.dataset.revealOriginalHtml = originalHtml;
+ element.innerHTML = segments
+ .map(
+ (segment) =>
+ `${segment}`
+ )
+ .join("");
+
+ return Array.from(element.querySelectorAll(".reveal-line"));
+};
+
+const restoreRevealLines = (element) => {
+ if (!element || element.dataset.revealPrepared !== "true") {
+ return;
+ }
+
+ const originalHtml = element.dataset.revealOriginalHtml;
+
+ if (originalHtml) {
+ element.innerHTML = originalHtml;
+ }
+
+ delete element.dataset.revealPrepared;
+ delete element.dataset.revealOriginalHtml;
+};
+
+function useScrollTextReveal(scopeRef, deps = []) {
+ useLayoutEffect(() => {
+ const scope = scopeRef.current;
+
+ if (!scope || typeof window === "undefined") {
+ return undefined;
+ }
+
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
+ return undefined;
+ }
+
+ registerGsap();
+
+ const preparedElements = [];
+ const ctx = gsap.context(() => {
+ const groups = gsap.utils.toArray("[data-reveal-group]");
+
+ groups.forEach((group) => {
+ const items = Array.from(group.querySelectorAll("[data-reveal]")).filter(
+ (item) => item.closest("[data-reveal-group]") === group
+ );
+
+ if (items.length === 0) {
+ return;
+ }
+
+ items.forEach((item) => {
+ if (item.dataset.reveal === "lines") {
+ gsap.set(item, { autoAlpha: 0 });
+ return;
+ }
+
+ gsap.set(item, {
+ y: 36,
+ autoAlpha: 0,
+ force3D: true,
+ });
+ });
+
+ ScrollTrigger.create({
+ trigger: group,
+ start: group.dataset.revealStart || "top 86%",
+ once: true,
+ onEnter: () => {
+ const timeline = gsap.timeline();
+
+ items.forEach((item, index) => {
+ const position = index === 0 ? 0 : "<0.16";
+
+ if (item.dataset.reveal === "lines") {
+ const lines = createRevealLines(item);
+
+ if (lines.length === 0) {
+ return;
+ }
+
+ preparedElements.push(item);
+ gsap.set(item, { autoAlpha: 1 });
+ gsap.set(lines, {
+ yPercent: 115,
+ rotate: 2.2,
+ transformOrigin: "0% 100%",
+ force3D: true,
+ });
+
+ timeline.to(
+ lines,
+ {
+ yPercent: 0,
+ rotate: 0,
+ duration: 1.18,
+ stagger: 0.1,
+ ease: "power4.out",
+ clearProps: "transform",
+ },
+ position
+ );
+
+ return;
+ }
+
+ timeline.to(
+ item,
+ {
+ y: 0,
+ autoAlpha: 1,
+ duration: 1.02,
+ ease: "power4.out",
+ clearProps: "transform,opacity,visibility",
+ },
+ position
+ );
+ });
+ },
+ });
+ });
+
+ ScrollTrigger.refresh();
+ }, scope);
+
+ return () => {
+ ctx.revert();
+ preparedElements.forEach((element) => restoreRevealLines(element));
+ };
+ }, [scopeRef, ...deps]);
+}
+
+export default useScrollTextReveal;
diff --git a/parfum-shop/src/style/textReveal.css b/parfum-shop/src/style/textReveal.css
new file mode 100644
index 0000000..a288d61
--- /dev/null
+++ b/parfum-shop/src/style/textReveal.css
@@ -0,0 +1,16 @@
+.reveal-line-mask {
+ display: block;
+ overflow: hidden;
+ padding-bottom: 0.08em;
+ margin-bottom: -0.08em;
+}
+
+.reveal-line {
+ display: block;
+ will-change: transform;
+ backface-visibility: hidden;
+}
+
+[data-reveal="fade"] {
+ will-change: transform, opacity;
+}