add global text reveal hook and scroll animations

This commit is contained in:
Ermin Zoronjic 2026-04-23 22:56:47 +02:00
parent 9de9ce7f43
commit d0c7916386
13 changed files with 431 additions and 262 deletions

View File

@ -1,3 +1,4 @@
import { useRef } from "react";
import { Routes, Route, useLocation } from "react-router";
import LandingPage from "./pages/LandingPage";
import ProductDetailPage from "./components/ProductDetailPage";
@ -12,26 +13,33 @@ import SupportChatbot from "./components/SupportChatbot";
import ScrollToTop from "./components/ScrollToTop";
import ShopDrawer from "./components/ShopDrawer";
import CartToast from "./components/CartToast";
import useScrollTextReveal from "./hooks/useScrollTextReveal";
import "./style/textReveal.css";
function App() {
const location = useLocation();
const routeContentRef = useRef(null);
const shouldFlushFooter =
location.pathname === "/" || location.pathname.startsWith("/duft/");
useScrollTextReveal(routeContentRef, [location.pathname]);
return (
<>
<ScrollToTop />
<Routes>
<Route path="/" element={<LandingPage />} />
<Route path="/duft/:perfumeSlug" element={<ProductDetailPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/impressum" element={<ImpressumPage />} />
<Route path="/datenschutz" element={<DatenschutzPage />} />
<Route path="/support" element={<SupportPage />} />
<Route path="/discovery-set" element={<DiscoverySetPage />} />
<Route path="/small-batch" element={<SmallBatchPage />} />
</Routes>
<div ref={routeContentRef}>
<Routes>
<Route path="/" element={<LandingPage />} />
<Route path="/duft/:perfumeSlug" element={<ProductDetailPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/impressum" element={<ImpressumPage />} />
<Route path="/datenschutz" element={<DatenschutzPage />} />
<Route path="/support" element={<SupportPage />} />
<Route path="/discovery-set" element={<DiscoverySetPage />} />
<Route path="/small-batch" element={<SmallBatchPage />} />
</Routes>
</div>
<ShopDrawer />
<CartToast />

View File

@ -247,11 +247,13 @@ function ProductDetailContent({ perfumeSlug }) {
</div>
<div className="detail-info">
<div className="detail-heading">
<div className="detail-heading" data-reveal-group>
<div className="detail-heading-copy">
<span className="detail-kicker">Edition 04</span>
<h1>{perfume.name}</h1>
<p>{perfume.shortText}</p>
<span className="detail-kicker" data-reveal="fade">
Edition 04
</span>
<h1 data-reveal="fade">{perfume.name}</h1>
<p data-reveal="fade">{perfume.shortText}</p>
</div>
</div>
@ -282,8 +284,8 @@ function ProductDetailContent({ perfumeSlug }) {
</div>
</div>
<div className="discovery-note">
<div className="discovery-note-text">
<div className="discovery-note" data-reveal-group data-reveal-start="top 88%">
<div className="discovery-note-text" data-reveal="fade">
<strong>Discovery Set wird einmalig angerechnet</strong>
<p>
Nur das erste Discovery Set erzeugt CHF 48 Guthaben. Es wird
@ -297,7 +299,7 @@ function ProductDetailContent({ perfumeSlug }) {
)}
</div>
<Link to="/discovery-set" className="discovery-note-btn">
<Link to="/discovery-set" className="discovery-note-btn" data-reveal="fade">
Zum Set
</Link>
</div>
@ -468,15 +470,15 @@ function ProductDetailContent({ perfumeSlug }) {
</div>
</section>
<section className="detail-bottom-cta">
<h2>Lieber erst testen?</h2>
<p>
<section className="detail-bottom-cta" data-reveal-group>
<h2 data-reveal="fade">Lieber erst testen?</h2>
<p data-reveal="fade">
Bestelle ein 2ml Sample für CHF 12 oder das komplette Discovery Set
mit allen 6 Düften für CHF 48. Beide werden beim späteren Full-Size-Kauf
vollständig angerechnet.
</p>
<div className="detail-bottom-actions">
<div className="detail-bottom-actions" data-reveal="fade">
<button
type="button"
onClick={() =>

View File

@ -6,12 +6,18 @@ function SharedNavbar({ variant = "light", active = "" }) {
const { cart, openCart, openProfile, user } = useShop();
const cartLabel =
cart.total_quantity > 0 ? `Cart ${cart.total_quantity}` : "Cart";
const logoSrc =
variant === "hero" ? "/atmos-logo-light.svg" : "/atmos-logo-dark.svg";
return (
<nav className={`navbar navbar--${variant}`} aria-label="Hauptnavigation">
<div className="nav-pill">
<Link to="/" className={`nav-link ${active === "atmos" ? "active" : ""}`}>
atmos
<Link
to="/"
className={`nav-link nav-link--brand ${active === "atmos" ? "active" : ""}`}
aria-label="Atmos Startseite"
>
<img src={logoSrc} alt="" className="nav-brand-logo" />
</Link>
<Link
to="/discovery-set"

View File

@ -4,6 +4,7 @@ import SharedNavbar from "../SharedNavbar";
function HeroSection({
heroImageWrapRef,
heroImageRef,
setHeadlinePrimaryRef,
setHeadlineSecondaryRef,
setDescriptionRef,
@ -18,30 +19,25 @@ function HeroSection({
src="/atmos-hero-image.png"
alt="Atmos Hero"
className="hero-media__image"
ref={heroImageRef}
loading="eager"
fetchPriority="high"
/>
</div>
<Link to="/" className="hero-brand" aria-label="Atmos Startseite">
<img
src="/atmos-logo-light.svg"
alt="atmos"
className="hero-brand__logo"
loading="eager"
fetchPriority="high"
/>
</Link>
<SharedNavbar variant="hero" active="atmos" />
<div className="hero-content">
<h1 className="hero-title">
<span className="hero-title-line" ref={setHeadlinePrimaryRef}>
{"D\u00DCFTE ALS"}
<span className="hero-title-line">
<span className="reveal-line" ref={setHeadlinePrimaryRef}>
{"D\u00DCFTE ALS"}
</span>
</span>
<span className="hero-title-line" ref={setHeadlineSecondaryRef}>
AUSDRUCK
<span className="hero-title-line">
<span className="reveal-line" ref={setHeadlineSecondaryRef}>
AUSDRUCK
</span>
</span>
</h1>

View File

@ -1,3 +1,4 @@
import { Link } from "react-router";
import SharedNavbar from "../components/SharedNavbar";
import "./AboutPage.css";
@ -7,10 +8,12 @@ function AboutPage() {
<SharedNavbar variant="light" />
<main className="about-shell">
<section className="about-hero">
<section className="about-hero" data-reveal-group>
<div className="about-hero-copy">
<span className="about-kicker">ABOUT atmos</span>
<h1>
<span className="about-kicker" data-reveal="fade">
ABOUT atmos
</span>
<h1 data-reveal="lines">
WIR DEKODIEREN
<br />
ATMOSPHÄREN
@ -19,7 +22,7 @@ function AboutPage() {
<br />
SIE IN DÜFTE
</h1>
<p className="about-intro">
<p className="about-intro" data-reveal="fade">
atmos entwickelt Nischendüfte, die nicht bloss gut riechen sollen,
sondern eine Atmosphäre präzise übersetzen: Material, Raum, Kälte,
Licht, Oberfläche, Spannung. Jeder Duft ist eine verdichtete
@ -27,7 +30,7 @@ function AboutPage() {
</p>
</div>
<div className="about-hero-panel">
<div className="about-hero-panel" data-reveal="fade">
<span className="about-panel-label">POSITIONIERUNG</span>
<p>
Nischig. Hochwertig. Edel. Luxuriös. Reduziert in der Form,
@ -51,13 +54,15 @@ function AboutPage() {
</div>
</section>
<section className="about-section about-section--split">
<section className="about-section about-section--split" data-reveal-group>
<div className="about-section-heading">
<span className="about-label">UNSER VERSTÄNDNIS</span>
<h2>NICHT DEKORATION. SONDERN HALTUNG.</h2>
<span className="about-label" data-reveal="fade">
UNSER VERSTÄNDNIS
</span>
<h2 data-reveal="lines">NICHT DEKORATION. SONDERN HALTUNG.</h2>
</div>
<div className="about-section-copy">
<div className="about-section-copy" data-reveal="fade">
<p>
atmos versteht Parfum nicht als beiläufiges Accessoire, sondern als
Form von Ausdruck. Unsere Düfte entstehen nicht aus Trends, sondern
@ -75,34 +80,34 @@ function AboutPage() {
</div>
</section>
<section className="about-proof-strip">
<div className="about-proof-item">
<section className="about-proof-strip" data-reveal-group data-reveal-start="top 90%">
<div className="about-proof-item" data-reveal="fade">
<span className="about-label">ATELIER</span>
<p>Entwicklung aus einem kuratierten Duft- und Materialkontext</p>
</div>
<div className="about-proof-item">
<div className="about-proof-item" data-reveal="fade">
<span className="about-label">KLEINSERIEN</span>
<p>Chargenbasiert gedacht statt massenmarktfähig optimiert</p>
</div>
<div className="about-proof-item">
<div className="about-proof-item" data-reveal="fade">
<span className="about-label">KOMPOSITION</span>
<p>Materiallogik vor Trendformel und Lautstärke</p>
</div>
<div className="about-proof-item">
<div className="about-proof-item" data-reveal="fade">
<span className="about-label">HERKUNFT</span>
<p>Creative Direction und Qualitätsanspruch aus der Schweiz</p>
</div>
</section>
<section className="about-quote-block">
<p>
<section className="about-quote-block" data-reveal-group>
<p data-reveal="fade">
Jeder Duft beginnt bei uns nicht mit einer Note, sondern mit einem
Raumgefühl.
</p>
</section>
<section className="about-grid-section">
<div className="about-card">
<section className="about-grid-section" data-reveal-group data-reveal-start="top 84%">
<div className="about-card" data-reveal="fade">
<span className="about-label">01 / DEKODIEREN</span>
<h3>Atmosphäre lesen</h3>
<p>
@ -112,7 +117,7 @@ function AboutPage() {
</p>
</div>
<div className="about-card">
<div className="about-card" data-reveal="fade">
<span className="about-label">02 / VERDICHTEN</span>
<h3>In Duft übersetzen</h3>
<p>
@ -122,7 +127,7 @@ function AboutPage() {
</p>
</div>
<div className="about-card">
<div className="about-card" data-reveal="fade">
<span className="about-label">03 / REDUZIEREN</span>
<h3>Wirkung schärfen</h3>
<p>
@ -133,13 +138,20 @@ function AboutPage() {
</div>
</section>
<section className="about-section about-section--split about-process-section">
<section
className="about-section about-section--split about-process-section"
data-reveal-group
>
<div className="about-section-heading">
<span className="about-label">PROZESS & KOMPETENZ</span>
<h2>WIE AUS EINER IDEE EINE BELASTBARE KOMPOSITION WIRD.</h2>
<span className="about-label" data-reveal="fade">
PROZESS & KOMPETENZ
</span>
<h2 data-reveal="lines">
WIE AUS EINER IDEE EINE BELASTBARE KOMPOSITION WIRD.
</h2>
</div>
<div className="about-section-copy">
<div className="about-section-copy" data-reveal="fade">
<p>
atmos arbeitet nicht mit einem losgelösten Storytelling, das im
Nachhinein auf einen Duft gelegt wird. Jede Komposition beginnt mit
@ -156,8 +168,12 @@ function AboutPage() {
</div>
</section>
<section className="about-credentials-grid">
<article className="about-credential-card">
<section
className="about-credentials-grid"
data-reveal-group
data-reveal-start="top 84%"
>
<article className="about-credential-card" data-reveal="fade">
<span className="about-label">ATELIER / ENTWICKLUNG</span>
<h3>Komposition aus einem klaren Duftverständnis</h3>
<p>
@ -167,7 +183,7 @@ function AboutPage() {
</p>
</article>
<article className="about-credential-card">
<article className="about-credential-card" data-reveal="fade">
<span className="about-label">MATERIALLOGIK</span>
<h3>Noten folgen einer Idee, nicht bloss einer Wirkung</h3>
<p>
@ -177,7 +193,7 @@ function AboutPage() {
</p>
</article>
<article className="about-credential-card">
<article className="about-credential-card" data-reveal="fade">
<span className="about-label">CHARGENPRINZIP</span>
<h3>Kleinserie statt gesichtsloser Massenästhetik</h3>
<p>
@ -187,7 +203,7 @@ function AboutPage() {
</p>
</article>
<article className="about-credential-card">
<article className="about-credential-card" data-reveal="fade">
<span className="about-label">QUALITÄTSPRÜFUNG</span>
<h3>Komposition, Haltbarkeit und Verlauf werden bewusst geprüft</h3>
<p>
@ -198,11 +214,15 @@ function AboutPage() {
</article>
</section>
<section className="about-method-section">
<section className="about-method-section" data-reveal-group>
<div className="about-method-copy">
<span className="about-label">NACHWEISBARE DUFTLOGIK</span>
<h2>FÜR MENSCHEN, DIE HYPE ERKENNEN UND SUBSTANZ SUCHEN.</h2>
<p>
<span className="about-label" data-reveal="fade">
NACHWEISBARE DUFTLOGIK
</span>
<h2 data-reveal="lines">
FÜR MENSCHEN, DIE HYPE ERKENNEN UND SUBSTANZ SUCHEN.
</h2>
<p data-reveal="fade">
Wer in der Szene unterwegs ist, erkennt schnell, wenn ein Duft vor
allem über Bildsprache verkauft wird. Genau deshalb machen wir unsere
Denkweise sichtbar: über Struktur, Materialbezug, Edition, Trageverlauf
@ -210,7 +230,7 @@ function AboutPage() {
</p>
</div>
<div className="about-method-points">
<div className="about-method-points" data-reveal="fade">
<div>
<span>DUFTSTRUKTUR</span>
<p>Top, Heart und Base werden als Verlauf und nicht nur als Liste gedacht.</p>
@ -230,23 +250,25 @@ function AboutPage() {
</div>
</section>
<section className="about-section about-origin-section">
<section className="about-section about-origin-section" data-reveal-group>
<div className="about-origin-copy">
<span className="about-label">MADE IN SWITZERLAND</span>
<h2>PRÄZISION IN FORM, DUFT UND AUFTRITT.</h2>
<p>
<span className="about-label" data-reveal="fade">
MADE IN SWITZERLAND
</span>
<h2 data-reveal="lines">PRÄZISION IN FORM, DUFT UND AUFTRITT.</h2>
<p data-reveal="fade">
atmos ist in der Schweiz verankert. Dieser Ursprung zeigt sich nicht
nur in der Herstellung, sondern auch in unserer Haltung zur Qualität:
kontrolliert, bewusst, präzise und kompromisslos in der Ausarbeitung.
</p>
<p>
<p data-reveal="fade">
Unsere Düfte bewegen sich zwischen Luxus und Konzept. Sie sollen
hochwertig wirken, ohne laut zu werden. Edel, ohne klassisch zu sein.
Und luxuriös, ohne sich über Überfluss zu definieren.
</p>
</div>
<div className="about-origin-box">
<div className="about-origin-box" data-reveal="fade">
<div>
<span>QUALITÄTSVERSPRECHEN</span>
<p>Kuratiert, präzise und mit Fokus auf charakterstarke Kompositionen.</p>
@ -262,29 +284,35 @@ function AboutPage() {
</div>
</section>
<section className="about-trust-note">
<span className="about-label">QUALITÄTSVERSTÄNDNIS</span>
<p>
Bei atmos steht nicht die schnelle Aufmerksamkeit im Vordergrund, sondern die
Qualität der Komposition. Unsere Düfte entstehen aus klaren Materialideen,
kontrollierter Entwicklung und einem kuratierten Anspruch an Verlauf,
Balance, Haltbarkeit und Charakter. Jede Edition soll nicht nur auffallen,
sondern bestehen durch Präzision, Eigenständigkeit und eine spürbare
kompositorische Disziplin.
</p>
</section>
<section className="about-trust-note" data-reveal-group>
<span className="about-label" data-reveal="fade">
QUALITÄTSVERSTÄNDNIS
</span>
<p data-reveal="fade">
Bei atmos steht nicht die schnelle Aufmerksamkeit im Vordergrund, sondern die
Qualität der Komposition. Unsere Düfte entstehen aus klaren Materialideen,
kontrollierter Entwicklung und einem kuratierten Anspruch an Verlauf,
Balance, Haltbarkeit und Charakter. Jede Edition soll nicht nur auffallen,
sondern bestehen durch Präzision, Eigenständigkeit und eine spürbare
kompositorische Disziplin.
</p>
</section>
<section className="about-bottom-cta">
<section className="about-bottom-cta" data-reveal-group>
<div className="about-bottom-copy">
<span className="about-label">atmos</span>
<h2>FÜR MENSCHEN, DIE NICHT NUR EINEN DUFT SUCHEN, SONDERN EINE HALTUNG.</h2>
<p>
<span className="about-label" data-reveal="fade">
atmos
</span>
<h2 data-reveal="lines">
FÜR MENSCHEN, DIE NICHT NUR EINEN DUFT SUCHEN, SONDERN EINE HALTUNG.
</h2>
<p data-reveal="fade">
Entdecke Düfte, die Atmosphäre nicht illustrieren, sondern in eine
tragbare Form übersetzen.
</p>
</div>
<div className="about-bottom-actions">
<div className="about-bottom-actions" data-reveal="fade">
<Link to="/" className="about-btn about-btn--primary">
Zur Startseite
</Link>

View File

@ -7,10 +7,10 @@ function DatenschutzPage() {
<SharedNavbar variant="light" />
<main className="datenschutz-shell">
<section className="datenschutz-hero">
<span className="datenschutz-kicker">RECHTLICHE ANGABEN</span>
<h1>DATENSCHUTZ</h1>
<p className="datenschutz-intro">
<section className="datenschutz-hero" data-reveal-group>
<span className="datenschutz-kicker" data-reveal="fade">RECHTLICHE ANGABEN</span>
<h1 data-reveal="lines">DATENSCHUTZ</h1>
<p className="datenschutz-intro" data-reveal="fade">
Der Schutz persönlicher Daten hat für atmos einen hohen Stellenwert.
Nachfolgend informieren wir darüber, welche personenbezogenen Daten bei
der Nutzung dieser Website erhoben, verarbeitet und gespeichert werden,
@ -19,13 +19,13 @@ function DatenschutzPage() {
</p>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">VERANTWORTLICHE STELLE</span>
<h2>WER FÜR DIE DATENVERARBEITUNG VERANTWORTLICH IST</h2>
<span className="datenschutz-label" data-reveal="fade">VERANTWORTLICHE STELLE</span>
<h2 data-reveal="lines">WER FÜR DIE DATENVERARBEITUNG VERANTWORTLICH IST</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Verantwortlich für die Datenverarbeitung im Zusammenhang mit dieser
Website ist:
@ -47,13 +47,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">ALLGEMEINES</span>
<h2>WELCHE DATEN ERHOBEN WERDEN</h2>
<span className="datenschutz-label" data-reveal="fade">ALLGEMEINES</span>
<h2 data-reveal="lines">WELCHE DATEN ERHOBEN WERDEN</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Beim Besuch dieser Website können automatisch technische Daten
erfasst werden. Dazu gehören insbesondere IP-Adresse, Datum und
@ -70,13 +70,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">ZWECK</span>
<h2>WOFÜR DIE DATEN VERWENDET WERDEN</h2>
<span className="datenschutz-label" data-reveal="fade">ZWECK</span>
<h2 data-reveal="lines">WOFÜR DIE DATEN VERWENDET WERDEN</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>Die Verarbeitung personenbezogener Daten erfolgt insbesondere:</p>
<ul className="datenschutz-list">
<li>zur Bereitstellung und technischen Optimierung der Website,</li>
@ -89,13 +89,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">COOKIES & TRACKING</span>
<h2>COOKIES UND ÄHNLICHE TECHNOLOGIEN</h2>
<span className="datenschutz-label" data-reveal="fade">COOKIES & TRACKING</span>
<h2 data-reveal="lines">COOKIES UND ÄHNLICHE TECHNOLOGIEN</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Diese Website kann Cookies oder ähnliche Technologien verwenden, um
Funktionen bereitzustellen, die Nutzung zu analysieren und das
@ -112,13 +112,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">WEITERGABE</span>
<h2>WEITERGABE AN DRITTE</h2>
<span className="datenschutz-label" data-reveal="fade">WEITERGABE</span>
<h2 data-reveal="lines">WEITERGABE AN DRITTE</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Eine Weitergabe personenbezogener Daten an Dritte erfolgt nur, soweit
dies zur Vertragserfüllung notwendig ist, eine gesetzliche
@ -134,13 +134,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">SPEICHERDAUER</span>
<h2>WIE LANGE DATEN GESPEICHERT WERDEN</h2>
<span className="datenschutz-label" data-reveal="fade">SPEICHERDAUER</span>
<h2 data-reveal="lines">WIE LANGE DATEN GESPEICHERT WERDEN</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Personenbezogene Daten werden nur so lange gespeichert, wie dies für
die jeweiligen Zwecke erforderlich ist oder gesetzliche
@ -151,13 +151,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">RECHTE</span>
<h2>RECHTE DER BETROFFENEN PERSONEN</h2>
<span className="datenschutz-label" data-reveal="fade">RECHTE</span>
<h2 data-reveal="lines">RECHTE DER BETROFFENEN PERSONEN</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Betroffene Personen haben im Rahmen des anwendbaren Datenschutzrechts
insbesondere das Recht auf Auskunft, Berichtigung, Löschung,
@ -171,13 +171,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">SICHERHEIT</span>
<h2>TECHNISCHE UND ORGANISATORISCHE MASSNAHMEN</h2>
<span className="datenschutz-label" data-reveal="fade">SICHERHEIT</span>
<h2 data-reveal="lines">TECHNISCHE UND ORGANISATORISCHE MASSNAHMEN</h2>
</div>
<div className="datenschutz-section-copy">
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Wir treffen angemessene technische und organisatorische
Sicherheitsmassnahmen, um personenbezogene Daten vor unbefugtem
@ -190,13 +190,13 @@ function DatenschutzPage() {
</div>
</section>
<section className="datenschutz-section">
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label">AKTUALISIERUNG</span>
<h2>ÄNDERUNGEN DIESER DATENSCHUTZERKLÄRUNG</h2>
<span className="datenschutz-label" data-reveal="fade">AKTUALISIERUNG</span>
<h2 data-reveal="lines">ÄNDERUNGEN DIESER DATENSCHUTZERKLÄRUNG</h2>
</div>
<div className="datenschutz-note-box">
<div className="datenschutz-note-box" data-reveal="fade">
<p>
atmos behält sich vor, diese Datenschutzerklärung bei Bedarf
anzupassen, insbesondere wenn sich rechtliche Vorgaben, technische

View File

@ -31,10 +31,12 @@ function DiscoverySetPage() {
</button>
</div>
<section className="discovery-hero">
<section className="discovery-hero" data-reveal-group>
<div className="discovery-hero-copy">
<span className="discovery-kicker">DISCOVERY SET</span>
<h1>
<span className="discovery-kicker" data-reveal="fade">
DISCOVERY SET
</span>
<h1 data-reveal="lines">
DER SICHERSTE EINSTIEG
<br />
IN DIE WELT DER NISCHEN-
@ -42,12 +44,12 @@ function DiscoverySetPage() {
DÜFTE
</h1>
<p className="discovery-intro">
<p className="discovery-intro" data-reveal="fade">
6 Düfte × 2ml. Jeden Duft eine Woche tragen. Verstehen, was
wirklich funktioniert. Ohne Risiko.
</p>
<div className="discovery-benefits">
<div className="discovery-benefits" data-reveal="fade">
<div className="discovery-benefit">
<span className="discovery-benefit-icon"></span>
<div>
@ -93,7 +95,7 @@ function DiscoverySetPage() {
</div>
</div>
<div className="discovery-hero-actions">
<div className="discovery-hero-actions" data-reveal="fade">
<button type="button" className="discovery-primary-btn" onClick={buyDiscoverySet}>
DISCOVERY SET BESTELLEN CHF 48.
</button>
@ -110,10 +112,12 @@ function DiscoverySetPage() {
</div>
</section>
<section className="discovery-included">
<section className="discovery-included" data-reveal-group>
<div className="discovery-section-heading">
<span className="discovery-label">IM SET ENTHALTEN</span>
<h2>ALLE 6 SIGNATURE-DÜFTE ZUM TESTEN.</h2>
<span className="discovery-label" data-reveal="fade">
IM SET ENTHALTEN
</span>
<h2 data-reveal="lines">ALLE 6 SIGNATURE-DÜFTE ZUM TESTEN.</h2>
</div>
<div className="discovery-products-grid">
@ -139,11 +143,11 @@ function DiscoverySetPage() {
</section>
<section className="discovery-steps-section">
<div className="discovery-steps-shell">
<h2>So funktioniert&apos;s</h2>
<div className="discovery-steps-shell" data-reveal-group>
<h2 data-reveal="lines">So funktioniert&apos;s</h2>
<div className="discovery-steps-grid">
<article className="discovery-step-card">
<article className="discovery-step-card" data-reveal="fade">
<div className="discovery-step-number">1</div>
<h3>Bestellen</h3>
<p>
@ -152,7 +156,7 @@ function DiscoverySetPage() {
</p>
</article>
<article className="discovery-step-card">
<article className="discovery-step-card" data-reveal="fade">
<div className="discovery-step-number">2</div>
<h3>Testen</h3>
<p>
@ -161,7 +165,7 @@ function DiscoverySetPage() {
</p>
</article>
<article className="discovery-step-card">
<article className="discovery-step-card" data-reveal="fade">
<div className="discovery-step-number">3</div>
<h3>Entscheiden</h3>
<p>
@ -173,11 +177,13 @@ function DiscoverySetPage() {
</div>
</section>
<section className="discovery-comparison-section">
<section className="discovery-comparison-section" data-reveal-group>
<div className="discovery-section-heading discovery-section-heading--center">
<span className="discovery-label">WARUM DISCOVERY SET</span>
<h2>DER KLÜGERE EINSTIEG IN NISCHENDÜFTE.</h2>
<p>
<span className="discovery-label" data-reveal="fade">
WARUM DISCOVERY SET
</span>
<h2 data-reveal="lines">DER KLÜGERE EINSTIEG IN NISCHENDÜFTE.</h2>
<p data-reveal="fade">
Nischen-Parfums sind keine Impulskäufe. Sie brauchen Zeit, um zu
verstehen, wie sie auf deiner Haut funktionieren, wie sie sich im
Alltag entwickeln und ob sie wirklich zu dir passen.
@ -185,7 +191,7 @@ function DiscoverySetPage() {
</div>
<div className="discovery-comparison-grid">
<div className="discovery-comparison-card">
<div className="discovery-comparison-card" data-reveal="fade">
<div className="discovery-comparison-head">
<span className="discovery-comparison-icon">×</span>
<h3>Traditioneller Weg</h3>
@ -197,7 +203,10 @@ function DiscoverySetPage() {
</p>
</div>
<div className="discovery-comparison-card discovery-comparison-card--highlight">
<div
className="discovery-comparison-card discovery-comparison-card--highlight"
data-reveal="fade"
>
<div className="discovery-comparison-head">
<span className="discovery-comparison-icon"></span>
<h3>Discovery Set Weg</h3>
@ -210,7 +219,7 @@ function DiscoverySetPage() {
</div>
</div>
<div className="discovery-bottom-cta">
<div className="discovery-bottom-cta" data-reveal="fade">
<button type="button" className="discovery-primary-btn" onClick={buyDiscoverySet}>
DISCOVERY SET BESTELLEN CHF 48.
</button>

View File

@ -7,18 +7,20 @@ function ImpressumPage() {
<SharedNavbar variant="light" />
<main className="impressum-shell">
<section className="impressum-hero">
<span className="impressum-kicker">RECHTLICHE ANGABEN</span>
<h1>IMPRESSUM</h1>
<p className="impressum-intro">
<section className="impressum-hero" data-reveal-group>
<span className="impressum-kicker" data-reveal="fade">
RECHTLICHE ANGABEN
</span>
<h1 data-reveal="lines">IMPRESSUM</h1>
<p className="impressum-intro" data-reveal="fade">
Dieses Impressum enthält die rechtlichen Angaben zu atmos sowie
Informationen zur Verantwortlichkeit, Erreichbarkeit und zu den
veröffentlichten Inhalten dieser Website.
</p>
</section>
<section className="impressum-grid">
<article className="impressum-card">
<section className="impressum-grid" data-reveal-group data-reveal-start="top 90%">
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">ANBIETER</span>
<h2>Unternehmen</h2>
<p>
@ -32,7 +34,7 @@ function ImpressumPage() {
</p>
</article>
<article className="impressum-card">
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">KONTAKT</span>
<h2>Erreichbarkeit</h2>
<p>
@ -42,7 +44,7 @@ function ImpressumPage() {
</p>
</article>
<article className="impressum-card">
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">VERTRETUNGSBERECHTIGT</span>
<h2>Geschäftsführung</h2>
<p>
@ -52,7 +54,7 @@ function ImpressumPage() {
</p>
</article>
<article className="impressum-card">
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">HANDELSREGISTER</span>
<h2>Registereintrag</h2>
<p>
@ -63,13 +65,15 @@ function ImpressumPage() {
</article>
</section>
<section className="impressum-section">
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label">INHALTLICHE VERANTWORTUNG</span>
<h2>VERANTWORTLICH FÜR DEN INHALT</h2>
<span className="impressum-label" data-reveal="fade">
INHALTLICHE VERANTWORTUNG
</span>
<h2 data-reveal="lines">VERANTWORTLICH FÜR DEN INHALT</h2>
</div>
<div className="impressum-section-copy">
<div className="impressum-section-copy" data-reveal="fade">
<p>
Verantwortlich für diese Website und die publizierten Inhalte:
</p>
@ -85,13 +89,15 @@ function ImpressumPage() {
</div>
</section>
<section className="impressum-section">
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label">HAFTUNG</span>
<h2>HAFTUNGSAUSSCHLUSS</h2>
<span className="impressum-label" data-reveal="fade">
HAFTUNG
</span>
<h2 data-reveal="lines">HAFTUNGSAUSSCHLUSS</h2>
</div>
<div className="impressum-section-copy">
<div className="impressum-section-copy" data-reveal="fade">
<p>
Trotz sorgfältiger inhaltlicher Kontrolle übernimmt atmos keine
Gewähr für die Aktualität, Richtigkeit, Vollständigkeit oder
@ -105,13 +111,15 @@ function ImpressumPage() {
</div>
</section>
<section className="impressum-section">
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label">URHEBERRECHT</span>
<h2>URHEBER- UND MARKENRECHTE</h2>
<span className="impressum-label" data-reveal="fade">
URHEBERRECHT
</span>
<h2 data-reveal="lines">URHEBER- UND MARKENRECHTE</h2>
</div>
<div className="impressum-section-copy">
<div className="impressum-section-copy" data-reveal="fade">
<p>
Sämtliche Inhalte dieser Website, einschliesslich Texte, Bilder,
Gestaltungselemente, Logos und Marken, sind urheberrechtlich
@ -123,13 +131,15 @@ function ImpressumPage() {
</div>
</section>
<section className="impressum-section">
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label">TRANSPARENZ</span>
<h2>KLARE ANGABEN UND ERREICHBARKEIT</h2>
<span className="impressum-label" data-reveal="fade">
TRANSPARENZ
</span>
<h2 data-reveal="lines">KLARE ANGABEN UND ERREICHBARKEIT</h2>
</div>
<div className="impressum-note-box">
<div className="impressum-note-box" data-reveal="fade">
<p>
atmos legt Wert auf eine klare, transparente und nachvollziehbare
Kommunikation. Dieses Impressum dient der eindeutigen

View File

@ -43,6 +43,7 @@
display: block;
object-fit: cover;
object-position: center;
will-change: transform;
}
.hero .navbar--hero {
@ -54,21 +55,6 @@
padding-top: 0;
}
.hero-brand {
position: absolute;
top: 22px;
left: clamp(1rem, 1.45vw, 20px);
z-index: 14;
display: inline-flex;
align-items: center;
}
.hero-brand__logo {
display: block;
width: clamp(74px, 8vw, 112px);
height: auto;
}
.hero-content {
position: relative;
z-index: 6;
@ -92,7 +78,13 @@
.hero-title-line {
display: block;
will-change: transform, opacity;
overflow: hidden;
padding-bottom: 0.08em;
margin-bottom: -0.08em;
}
.hero-title-line .reveal-line {
will-change: transform;
}
.hero-title-line + .hero-title-line {
@ -446,6 +438,7 @@
height: 100%;
object-fit: cover;
display: block;
will-change: transform;
}
/* RESPONSIVE */
@ -475,10 +468,6 @@
}
@media (max-width: 640px) {
.hero-brand {
top: 14px;
}
.hero .navbar--hero {
top: 14px;
}

View File

@ -7,6 +7,7 @@ import {
} from "react";
import { Link } from "react-router";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import HeroSection from "../components/landing/HeroSection";
import perfumes from "../data/perfumes";
import "../pages/LandingPage.css";
@ -14,11 +15,15 @@ import "../style/navbar.css";
const INTRO_SESSION_KEY = "atmos-landing-intro-played";
gsap.registerPlugin(ScrollTrigger);
function LandingPage() {
const pageRef = useRef(null);
const overlayRef = useRef(null);
const overlayTextRef = useRef(null);
const heroImageWrapRef = useRef(null);
const heroImageRef = useRef(null);
const discoveryImageRef = useRef(null);
const headlineLineRefs = useRef([]);
const heroMetaRefs = useRef([]);
const cardRefs = useRef([]);
@ -62,9 +67,8 @@ function LandingPage() {
const heroImageWrap = heroImageWrapRef.current;
const headlineLines = headlineLineRefs.current.filter(Boolean);
const heroMeta = heroMetaRefs.current.filter(Boolean);
const revealTargets = [...headlineLines, ...heroMeta];
if (!overlay || !overlayText || !heroImageWrap || revealTargets.length === 0) {
if (!overlay || !overlayText || !heroImageWrap || headlineLines.length === 0) {
return undefined;
}
@ -74,14 +78,22 @@ function LandingPage() {
transformOrigin: "center center",
});
gsap.set(revealTargets, {
y: 56,
gsap.set(headlineLines, {
yPercent: 115,
rotate: 2.2,
transformOrigin: "0% 100%",
force3D: true,
});
gsap.set(heroMeta, {
y: 36,
autoAlpha: 0,
});
if (!shouldPlayIntro) {
gsap.set(heroImageWrap, { scale: 1 });
gsap.set(revealTargets, { y: 0, autoAlpha: 1 });
gsap.set(headlineLines, { yPercent: 0, rotate: 0 });
gsap.set(heroMeta, { y: 0, autoAlpha: 1 });
gsap.set(overlay, {
yPercent: -100,
autoAlpha: 0,
@ -130,11 +142,11 @@ function LandingPage() {
.to(
headlineLines,
{
y: 0,
autoAlpha: 1,
duration: 1.04,
stagger: 0.16,
ease: "power3.out",
yPercent: 0,
rotate: 0,
duration: 1.18,
stagger: 0.1,
ease: "power4.out",
},
"<0.27"
)
@ -143,11 +155,11 @@ function LandingPage() {
{
y: 0,
autoAlpha: 1,
duration: 0.98,
stagger: 0.14,
ease: "power3.out",
duration: 1.02,
stagger: 0.12,
ease: "power4.out",
},
"<0.2"
"<0.16"
)
.set(overlay, { display: "none" });
}, pageRef);
@ -157,6 +169,69 @@ function LandingPage() {
};
}, [shouldPlayIntro]);
useLayoutEffect(() => {
const heroImageWrap = heroImageWrapRef.current;
const heroImage = heroImageRef.current;
const discoveryImage = discoveryImageRef.current;
if (!heroImageWrap || !heroImage || !discoveryImage || typeof window === "undefined") {
return undefined;
}
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return undefined;
}
const heroSection = heroImageWrap.closest(".hero");
const discoveryBanner = discoveryImage.closest(".discovery-banner");
if (!heroSection || !discoveryBanner) {
return undefined;
}
const ctx = gsap.context(() => {
gsap.set(heroImage, {
scale: 1.14,
yPercent: -4,
transformOrigin: "center center",
force3D: true,
});
gsap.to(heroImage, {
yPercent: 8,
ease: "none",
scrollTrigger: {
trigger: heroSection,
start: "top top",
end: "bottom top",
scrub: 1.15,
},
});
gsap.set(discoveryImage, {
scale: 1.16,
yPercent: -8,
transformOrigin: "center center",
force3D: true,
});
gsap.to(discoveryImage, {
yPercent: 10,
ease: "none",
scrollTrigger: {
trigger: discoveryBanner,
start: "top bottom",
end: "bottom top",
scrub: 1.2,
},
});
}, pageRef);
return () => {
ctx.revert();
};
}, []);
useEffect(() => {
const cards = cardRefs.current.filter(Boolean);
const cardStates = cards
@ -302,6 +377,7 @@ function LandingPage() {
<div className="page" ref={pageRef}>
<HeroSection
heroImageWrapRef={heroImageWrapRef}
heroImageRef={heroImageRef}
setHeadlinePrimaryRef={setHeadlinePrimaryRef}
setHeadlineSecondaryRef={setHeadlineSecondaryRef}
setDescriptionRef={setDescriptionRef}
@ -311,9 +387,9 @@ function LandingPage() {
/>
<main>
<section className="section" id="dufte">
<section className="section" id="dufte" data-reveal-group>
<div className="section-heading">
<h2>
<h2 data-reveal="lines">
{"W\u00C4HLE EINE"}
<br />
{"ATMOSPH\u00C4RE"}
@ -368,27 +444,37 @@ function LandingPage() {
</div>
</section>
<section className="discovery-section" id="testen">
<section
className="discovery-section"
id="testen"
data-reveal-group
data-reveal-start="top 82%"
>
<div className="discovery-copy">
<h2>
<h2 data-reveal="lines">
DER SICHERE EINSTIEG
<br />
DISCOVERY SET
</h2>
<p>
<p data-reveal="fade">
{"Alle 6 D\u00FCfte als 2ml Samples."}
<br />
Jeden Duft eine Woche tragen.
<br />
Verstehen, was funktioniert.
</p>
<Link to="/discovery-set" className="discovery-btn">
<Link to="/discovery-set" className="discovery-btn" data-reveal="fade">
Discovery Set bestellen
</Link>
</div>
<div className="discovery-banner">
<img src="/atmos-discovery-set-thumbnail.png" alt="Discovery Set" loading="lazy" />
<img
src="/atmos-discovery-set-thumbnail.png"
alt="Discovery Set"
loading="lazy"
ref={discoveryImageRef}
/>
</div>
</section>
</main>

View File

@ -63,30 +63,36 @@ function SmallBatchPage() {
<SharedNavbar variant="light" />
<main className="small-shell">
<section className="small-hero">
<span className="small-kicker">SMALL BATCH / ARCHIVE / PROTOTYPE</span>
<h1>EARLY ACCESS</h1>
<p>
<section className="small-hero" data-reveal-group>
<span className="small-kicker" data-reveal="fade">
SMALL BATCH / ARCHIVE / PROTOTYPE
</span>
<h1 data-reveal="fade">EARLY ACCESS</h1>
<p data-reveal="fade">
Limited releases are reserved for customers with enough purchase
history to understand the atmos material language.
</p>
</section>
{!user ? (
<section className="small-panel">
<span className="small-kicker">LOGIN REQUIRED</span>
<h2>Sign in to check access.</h2>
<p>Small Batch access is calculated from your completed orders.</p>
<section className="small-panel" data-reveal-group>
<span className="small-kicker" data-reveal="fade">
LOGIN REQUIRED
</span>
<h2 data-reveal="fade">Sign in to check access.</h2>
<p data-reveal="fade">Small Batch access is calculated from your completed orders.</p>
<button type="button" onClick={openProfile}>
Login / Register
</button>
</section>
) : (
<>
<section className="small-panel">
<span className="small-kicker">ACCESS STATUS</span>
<h2>{loyalty.unlocked ? "Unlocked" : "Locked"}</h2>
<div className="small-requirements">
<section className="small-panel" data-reveal-group>
<span className="small-kicker" data-reveal="fade">
ACCESS STATUS
</span>
<h2 data-reveal="fade">{loyalty.unlocked ? "Unlocked" : "Locked"}</h2>
<div className="small-requirements" data-reveal="fade">
<Requirement label="Discovery Set" met={loyalty.hasDiscoverySet} />
<Requirement label="Full Size" met={loyalty.hasFullSize} />
<Requirement label="Purchases" met={loyalty.purchases >= 3}>
@ -102,9 +108,9 @@ function SmallBatchPage() {
{state.loading && <p className="small-error">Loading access...</p>}
{loyalty.unlocked && (
<section className="release-grid">
<section className="release-grid" data-reveal-group data-reveal-start="top 88%">
{state.releases.map((release) => (
<article className="release-card" key={release.name}>
<article className="release-card" key={release.name} data-reveal="fade">
<span>{release.type}</span>
<h3>{release.name}</h3>
<p>{release.note}</p>

View File

@ -1,3 +1,4 @@
import { Link } from "react-router";
import SharedNavbar from "../components/SharedNavbar";
import "./SupportPage.css";
@ -7,10 +8,12 @@ function SupportPage() {
<SharedNavbar variant="light" />
<main className="support-shell">
<section className="support-hero">
<section className="support-hero" data-reveal-group>
<div className="support-hero-copy">
<span className="support-kicker">SUPPORT</span>
<h1>
<span className="support-kicker" data-reveal="fade">
SUPPORT
</span>
<h1 data-reveal="lines">
WIR HELFEN
<br />
KLAR, DIREKT
@ -19,7 +22,7 @@ function SupportPage() {
<br />
UMWEGE
</h1>
<p className="support-intro">
<p className="support-intro" data-reveal="fade">
Wenn du Fragen zu Bestellung, Versand, Discovery Set, Produkten oder
deiner Auswahl hast, ist das Support-Team von atmos für dich da.
Präzise, persönlich und mit dem Anspruch, jedes Anliegen sorgfältig
@ -27,7 +30,7 @@ function SupportPage() {
</p>
</div>
<div className="support-hero-panel">
<div className="support-hero-panel" data-reveal="fade">
<span className="support-panel-label">KONTAKT</span>
<p>
Für Anliegen rund um Bestellung, Produkte, Versand und allgemeine
@ -51,8 +54,8 @@ function SupportPage() {
</div>
</section>
<section className="support-quick-grid">
<article className="support-quick-card">
<section className="support-quick-grid" data-reveal-group data-reveal-start="top 90%">
<article className="support-quick-card" data-reveal="fade">
<span className="support-label">BESTELLUNG</span>
<h3>Fragen zu einer laufenden Bestellung</h3>
<p>
@ -61,7 +64,7 @@ function SupportPage() {
</p>
</article>
<article className="support-quick-card">
<article className="support-quick-card" data-reveal="fade">
<span className="support-label">VERSAND</span>
<h3>Lieferung und Zustellung</h3>
<p>
@ -70,7 +73,7 @@ function SupportPage() {
</p>
</article>
<article className="support-quick-card">
<article className="support-quick-card" data-reveal="fade">
<span className="support-label">PRODUKTE</span>
<h3>Beratung zu Duft, Sample oder Discovery Set</h3>
<p>
@ -80,13 +83,15 @@ function SupportPage() {
</article>
</section>
<section className="support-section support-section--split">
<section className="support-section support-section--split" data-reveal-group>
<div className="support-section-heading">
<span className="support-label">SO ERREICHST DU UNS</span>
<h2>EIN GUTER SUPPORT BEGINNT MIT EINER KLAREN ANFRAGE.</h2>
<span className="support-label" data-reveal="fade">
SO ERREICHST DU UNS
</span>
<h2 data-reveal="lines">EIN GUTER SUPPORT BEGINNT MIT EINER KLAREN ANFRAGE.</h2>
</div>
<div className="support-section-copy">
<div className="support-section-copy" data-reveal="fade">
<p>
Damit wir dein Anliegen möglichst schnell bearbeiten können, hilft es,
wenn du uns in deiner Nachricht die wichtigsten Informationen direkt
@ -102,8 +107,8 @@ function SupportPage() {
</div>
</section>
<section className="support-info-grid">
<div className="support-info-box">
<section className="support-info-grid" data-reveal-group>
<div className="support-info-box" data-reveal="fade">
<span className="support-label">AM BESTEN MITGEBEN</span>
<ul className="support-list">
<li>Bestellnummer, falls bereits bestellt wurde</li>
@ -113,7 +118,7 @@ function SupportPage() {
</ul>
</div>
<div className="support-info-box support-info-box--dark">
<div className="support-info-box support-info-box--dark" data-reveal="fade">
<span className="support-label">KONTAKT</span>
<h3>support@atmos.ch</h3>
<p>
@ -126,14 +131,16 @@ function SupportPage() {
</div>
</section>
<section className="support-faq-section">
<section className="support-faq-section" data-reveal-group>
<div className="support-section-heading">
<span className="support-label">HÄUFIGE FRAGEN</span>
<h2>DIE WICHTIGSTEN THEMEN AUF EINEN BLICK.</h2>
<span className="support-label" data-reveal="fade">
HÄUFIGE FRAGEN
</span>
<h2 data-reveal="lines">DIE WICHTIGSTEN THEMEN AUF EINEN BLICK.</h2>
</div>
<div className="support-faq-grid">
<article className="support-faq-card">
<article className="support-faq-card" data-reveal="fade">
<h3>Wie lange dauert der Versand?</h3>
<p>
Bestellungen werden in der Regel innerhalb von 12 Werktagen
@ -142,7 +149,7 @@ function SupportPage() {
</p>
</article>
<article className="support-faq-card">
<article className="support-faq-card" data-reveal="fade">
<h3>Kann ich zuerst testen?</h3>
<p>
Ja. Dafür ist das Discovery Set oder ein einzelnes Sample gedacht.
@ -151,7 +158,7 @@ function SupportPage() {
</p>
</article>
<article className="support-faq-card">
<article className="support-faq-card" data-reveal="fade">
<h3>Ich bin unsicher, welcher Duft zu mir passt.</h3>
<p>
Schreib uns kurz, welche Duftcharaktere, Materialien oder Stimmungen
@ -159,7 +166,7 @@ function SupportPage() {
</p>
</article>
<article className="support-faq-card">
<article className="support-faq-card" data-reveal="fade">
<h3>Was tun bei einem Problem mit der Bestellung?</h3>
<p>
Kontaktiere uns direkt mit deiner Bestellnummer und einer kurzen
@ -170,17 +177,21 @@ function SupportPage() {
</div>
</section>
<section className="support-bottom-cta">
<section className="support-bottom-cta" data-reveal-group>
<div className="support-bottom-copy">
<span className="support-label">atmos SUPPORT</span>
<h2>NICHT AUTOMATISIERT AUF DISTANZ. SONDERN PERSÖNLICH UND PRÄZISE.</h2>
<p>
<span className="support-label" data-reveal="fade">
atmos SUPPORT
</span>
<h2 data-reveal="lines">
NICHT AUTOMATISIERT AUF DISTANZ. SONDERN PERSÖNLICH UND PRÄZISE.
</h2>
<p data-reveal="fade">
Wir möchten, dass sich auch der Service so anfühlt wie die Marke
selbst: klar, hochwertig und sorgfältig.
</p>
</div>
<div className="support-bottom-actions">
<div className="support-bottom-actions" data-reveal="fade">
<a href="mailto:support@atmos.ch" className="support-btn support-btn--primary">
E-Mail senden
</a>

View File

@ -27,6 +27,16 @@
transition: 0.2s ease;
}
.nav-link--brand {
padding: 8px 12px;
}
.nav-brand-logo {
display: block;
width: clamp(56px, 5.4vw, 78px);
height: auto;
}
.nav-button {
border: none;
font-family: inherit;
@ -90,4 +100,12 @@
padding: 8px 10px;
font-size: 12px;
}
.nav-link--brand {
padding: 8px 10px;
}
.nav-brand-logo {
width: 54px;
}
}