add site refactors and media for scent notes

This commit is contained in:
Ermin Zoronjic 2026-05-05 20:03:18 +02:00
parent f3782a0dbf
commit 8960ffd844
35 changed files with 1396 additions and 1528 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

View File

@ -16,6 +16,7 @@ import CartToast from "./components/CartToast";
import { ProductTransitionProvider } from "./components/ProductTransition";
import useLenisSmoothScroll from "./hooks/useLenisSmoothScroll";
import useScrollTextReveal from "./hooks/useScrollTextReveal";
import useButtonInteractions from "./hooks/useButtonInteractions";
import { ThemeProvider } from "./theme/ThemeContext";
import "./style/textReveal.css";
@ -35,6 +36,7 @@ function App() {
useLenisSmoothScroll(location.pathname);
useScrollTextReveal(routeContentRef, location.pathname);
useButtonInteractions();
useEffect(() => {
if (typeof document === "undefined") return;

View File

@ -626,23 +626,54 @@
}
.structure-phase-card {
position: relative;
isolation: isolate;
overflow: hidden;
min-height: 280px;
padding: clamp(1rem, 2vw, 1.5rem);
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--gap-md);
border-color: rgba(255, 255, 255, 0.18);
background: #262626;
color: #fff;
}
.structure-phase-card::before,
.structure-phase-card::after {
content: "";
position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
}
.structure-phase-card::before {
background: var(--phase-visual-image, none) center / cover no-repeat;
transform: scale(1.02);
}
.structure-phase-card::after {
background:
linear-gradient(180deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.72)),
linear-gradient(90deg, rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.12));
}
.structure-phase-card > * {
position: relative;
z-index: 1;
}
.structure-index {
color: var(--theme-accent);
color: rgba(255, 255, 255, 0.7);
font-size: var(--text-sm);
letter-spacing: 0.2em;
}
.structure-phase-card h3 {
margin: 0;
color: var(--theme-text);
color: #fff;
font-size: var(--text-lg);
font-weight: 400;
letter-spacing: 0.02em;
@ -657,8 +688,9 @@
.structure-phase-card div span {
padding: 0.55rem 0.75rem;
border: 1px solid var(--theme-border);
color: var(--theme-text-muted);
border: 1px solid rgba(255, 255, 255, 0.22);
background: rgba(0, 0, 0, 0.26);
color: rgba(255, 255, 255, 0.86);
font-size: var(--text-sm);
}

View File

@ -279,10 +279,23 @@ function ProductStorySection({ perfume }) {
}
function ProductStructureSection({ perfume }) {
const phaseVisualImages = perfume.phaseVisualImages || [];
const phaseGroups = [
{ label: "Top Notes / 0-1 h", notes: perfume.phases.top },
{ label: "Heart Notes / 1-4 h", notes: perfume.phases.heart },
{ label: "Base Notes / 4 h+", notes: perfume.phases.base },
{
label: "Top Notes / 0-1 h",
notes: perfume.phases.top,
visualImage: phaseVisualImages[0],
},
{
label: "Heart Notes / 1-4 h",
notes: perfume.phases.heart,
visualImage: phaseVisualImages[1],
},
{
label: "Base Notes / 4 h+",
notes: perfume.phases.base,
visualImage: phaseVisualImages[2],
},
];
return (
@ -294,7 +307,16 @@ function ProductStructureSection({ perfume }) {
<div className="structure-timeline">
{phaseGroups.map((phase, index) => (
<article className="structure-phase-card" key={phase.label} data-reveal="fade">
<article
className="structure-phase-card"
key={phase.label}
data-reveal="fade"
style={
phase.visualImage
? { "--phase-visual-image": `url(${phase.visualImage})` }
: undefined
}
>
<span className="structure-index">0{index + 1}</span>
<h3>{phase.label}</h3>
<div>

View File

@ -18,6 +18,11 @@ const perfumes = [
heart: ["Stein Akkord", "Salz", "Staubnote"],
base: ["Zedernholz", "Pfeffer", "Weihrauch"],
},
phaseVisualImages: [
"/kalter-beton-visual-phase-1.webp",
"/kalter-beton-visual-phase-2.webp",
"/kalter-beton-visual-phase-3.webp",
],
mood: "Unbewohnte Räume. Erste Kälte. Architektonische Stille.",
dosage: "23 Sprühstösse",
longevity: "812 Stunden",
@ -98,6 +103,11 @@ const perfumes = [
heart: ["Marmorakkord", "Iris", "Nebelnote"],
base: ["Moschus", "Vetiver", "Ambra"],
},
phaseVisualImages: [
"/nasser-marmor-visual-phase-1.webp",
"/nasser-marmor-visual-phase-2.webp",
"/nasser-marmor-visual-phase-3.webp",
],
mood: "Feuchte Flächen. Kühle Eleganz. Polierte Ruhe.",
dosage: "23 Sprühstösse",
longevity: "711 Stunden",
@ -178,6 +188,11 @@ const perfumes = [
heart: ["Iris", "Seidenakkord", "Puder"],
base: ["Kaschmirholz", "Weisser Moschus", "Ambrette"],
},
phaseVisualImages: [
"/blasse-seide-visual-phase-1.webp",
"/blasse-seide-visual-phase-2.webp",
"/blasse-seide-visual-phase-3.webp",
],
mood: "Helles Gewebe. Stille Hautnähe. Gedämpfte Wärme.",
dosage: "34 Sprühstösse",
longevity: "610 Stunden",
@ -258,6 +273,11 @@ const perfumes = [
heart: ["Ascheakkord", "Papyrus", "Kreide"],
base: ["Weisses Holz", "Rauch", "Harz"],
},
phaseVisualImages: [
"/weisse-asche-visual-phase-1.webp",
"/weisse-asche-visual-phase-2.webp",
"/weisse-asche-visual-phase-3.webp",
],
mood: "Stille Rückstände. Helle Spuren. Erhobene Leere.",
dosage: "23 Sprühstösse",
longevity: "812 Stunden",
@ -338,6 +358,11 @@ const perfumes = [
heart: ["Metallakkord", "Rauch", "Harz"],
base: ["Oud", "Labdanum", "Dunkles Holz"],
},
phaseVisualImages: [
"/verbranntes-chrom-visual-phase-1.webp",
"/verbranntes-chrom-visual-phase-2.webp",
"/verbranntes-chrom-visual-phase-3.webp",
],
mood: "Hitze auf Metall. Dunkler Glanz. Kontrollierte Zerstörung.",
dosage: "12 Sprühstösse",
longevity: "1014 Stunden",
@ -418,6 +443,11 @@ const perfumes = [
heart: ["Leder", "Birke", "Harz"],
base: ["Patchouli", "Vetiver", "Dunkles Holz"],
},
phaseVisualImages: [
"/schwarzes-benzin-visual-phase-1.webp",
"/schwarzes-benzin-visual-phase-2.webp",
"/schwarzes-benzin-visual-phase-3.webp",
],
mood: "Verbotene Oberfläche. Glanz im Schatten. Asphalt nach Regen.",
dosage: "12 Sprühstösse",
longevity: "913 Stunden",

View File

@ -0,0 +1,99 @@
import { useEffect } from "react";
import { gsap } from "gsap";
/**
* Global, subtle GSAP micro-interactions for every `.atmos-btn`.
*
* Uses event delegation on the document so newly mounted buttons (drawer,
* route changes, conditional CTAs) work without re-binding.
*
* Effect = premium-but-quiet:
* - pointerenter: smooth lift + faint glow (overrides CSS transition)
* - pointerleave: return to rest
* - pointerdown: precise press
* - pointerup: release
*
* Skipped when prefers-reduced-motion is set, or when the button is disabled.
*/
function useButtonInteractions() {
useEffect(() => {
if (typeof document === "undefined") return undefined;
const reduceMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (reduceMotion) return undefined;
const isInteractiveButton = (target) => {
if (!target || typeof target.closest !== "function") return null;
const button = target.closest(".atmos-btn");
if (!button) return null;
if (button.disabled || button.getAttribute("aria-disabled") === "true") {
return null;
}
return button;
};
const onPointerEnter = (event) => {
const button = isInteractiveButton(event.target);
if (!button) return;
gsap.to(button, {
y: -2,
duration: 0.32,
ease: "power2.out",
overwrite: "auto",
});
};
const onPointerLeave = (event) => {
const button = isInteractiveButton(event.target);
if (!button) return;
gsap.to(button, {
y: 0,
scale: 1,
duration: 0.42,
ease: "power2.out",
overwrite: "auto",
});
};
const onPointerDown = (event) => {
const button = isInteractiveButton(event.target);
if (!button) return;
gsap.to(button, {
scale: 0.97,
duration: 0.12,
ease: "power2.out",
overwrite: "auto",
});
};
const onPointerUp = (event) => {
const button = isInteractiveButton(event.target);
if (!button) return;
gsap.to(button, {
scale: 1,
duration: 0.28,
ease: "power3.out",
overwrite: "auto",
});
};
const root = document;
root.addEventListener("pointerenter", onPointerEnter, true);
root.addEventListener("pointerleave", onPointerLeave, true);
root.addEventListener("pointerdown", onPointerDown, true);
root.addEventListener("pointerup", onPointerUp, true);
root.addEventListener("pointercancel", onPointerLeave, true);
return () => {
root.removeEventListener("pointerenter", onPointerEnter, true);
root.removeEventListener("pointerleave", onPointerLeave, true);
root.removeEventListener("pointerdown", onPointerDown, true);
root.removeEventListener("pointerup", onPointerUp, true);
root.removeEventListener("pointercancel", onPointerLeave, true);
};
}, []);
}
export default useButtonInteractions;

View File

@ -9,6 +9,7 @@
@import "./style/tokens.css";
@import "./style/grid.css";
@import "./style/breakpoints.css";
@import "./style/buttons.css";
@font-face {
font-family: "Questrial";

View File

@ -1,43 +1,42 @@
/**
* AboutPage aligned to the visual language of LandingPage / ProductDetail:
* - shared 12-column container width via .shell
* - small uppercase "eyebrow" label + large light-weight headline
* - section-y rhythm tokens
* - hard-edge surface cards with subtle gradient overlay
* - accent-filled CTA banner reused from product page
*/
.about-page {
min-height: 100vh;
padding: 0 0 var(--section-y-sm);
color: var(--theme-text);
background:
radial-gradient(circle at 86% 8%, rgba(var(--theme-accent-rgb) / 0.13), transparent 28rem),
linear-gradient(180deg, var(--theme-bg), color-mix(in srgb, var(--theme-bg) 88%, #000 12%));
background: var(--theme-bg);
}
.about-kicker,
.about-label,
.about-panel-label,
.about-origin-box span,
.about-panel-meta span,
.about-method-points span {
/* Eyebrow — single source of truth for kicker/label styling on this page. */
.about-eyebrow {
display: block;
margin-bottom: var(--gap-2xs);
color: var(--theme-text-muted);
font-size: var(--text-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
}
/* ----- Hero -------------------------------------------------------------- */
.about-hero {
display: grid;
grid-template-columns: minmax(0, 1.45fr) minmax(18rem, 0.72fr);
gap: var(--gap-lg);
align-items: end;
padding: clamp(2rem, 5vw, 5rem) 0 var(--section-y-sm);
border-bottom: 1px solid var(--theme-border);
gap: clamp(0.75rem, 1.6vw, 1.25rem);
padding: clamp(5rem, 9vw, 8rem) 0 var(--section-y-sm);
}
.about-hero-copy {
min-width: 0;
}
.about-hero-copy h1 {
max-width: 11.4ch;
margin: clamp(0.85rem, 2vw, 1.2rem) 0 clamp(1rem, 2vw, 1.35rem);
.about-hero h1 {
max-width: 14ch;
margin: 0;
color: var(--theme-text);
font-size: clamp(3rem, 7.4vw, 8.8rem);
font-size: clamp(2.8rem, 8vw, 8rem);
line-height: 0.9;
font-weight: 300;
letter-spacing: 0;
@ -45,66 +44,45 @@
text-wrap: balance;
}
.about-intro {
.about-lead {
max-width: var(--text-measure);
margin: 0;
margin: clamp(1rem, 2vw, 1.4rem) 0 0;
color: var(--theme-text-muted);
font-size: var(--text-lg);
line-height: 1.65;
}
.about-hero-panel {
padding: clamp(1.25rem, 3vw, 2rem);
border: 1px solid rgba(var(--theme-accent-rgb) / 0.2);
background:
linear-gradient(135deg, rgba(var(--theme-accent-rgb) / 0.1), transparent 62%),
var(--theme-surface-soft);
}
.about-hero-panel p {
margin: 0.75rem 0 0;
color: var(--theme-text);
font-size: var(--text-base);
line-height: 1.62;
}
.about-panel-meta {
display: grid;
gap: var(--gap-sm);
margin-top: clamp(1.2rem, 2.6vw, 2rem);
padding-top: var(--gap-sm);
border-top: 1px solid rgba(var(--theme-accent-rgb) / 0.2);
/* ----- Quote ------------------------------------------------------------- */
.about-quote {
margin-top: var(--section-y-sm);
padding-top: clamp(1.6rem, 3.5vw, 2.6rem);
border-top: 1px solid var(--theme-border);
}
.about-panel-meta p,
.about-origin-box p,
.about-method-points p {
margin: 0.45rem 0 0;
.about-quote p {
max-width: 64rem;
margin: 0;
color: var(--theme-text);
font-size: var(--text-sm);
line-height: 1.55;
font-size: clamp(1.6rem, 4vw, 3.6rem);
line-height: 1.1;
font-weight: 300;
letter-spacing: 0;
text-wrap: balance;
}
.about-section {
padding-top: var(--section-y-sm);
}
/* ----- Section heading (shared) ----------------------------------------- */
.about-section--split,
.about-origin-section,
.about-method-section {
.about-section-head {
display: grid;
grid-template-columns: minmax(16rem, 0.72fr) minmax(0, 1.28fr);
gap: var(--gap-lg);
align-items: start;
gap: var(--gap-2xs);
margin-bottom: clamp(1.6rem, 4vw, 3rem);
}
.about-section-heading h2,
.about-origin-copy h2,
.about-bottom-copy h2,
.about-method-copy h2 {
margin: 0.75rem 0 0;
.about-section-head h2 {
margin: 0;
color: var(--theme-text);
font-size: clamp(2.15rem, 5.2vw, 6rem);
font-size: clamp(2.2rem, 5.4vw, 6rem);
line-height: 0.94;
font-weight: 300;
letter-spacing: 0;
@ -112,149 +90,110 @@
text-wrap: balance;
}
.about-section-copy,
.about-method-points {
display: flex;
flex-direction: column;
gap: var(--gap-sm);
}
/* ----- Pillars ----------------------------------------------------------- */
.about-section-copy p,
.about-origin-copy p,
.about-bottom-copy p,
.about-method-copy p,
.about-credential-card p,
.about-card p,
.about-proof-item p,
.about-trust-note p {
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.7;
}
.about-section-copy p + p,
.about-origin-copy p + p {
margin-top: var(--gap-sm);
}
/* These three sections use the global Grid12 system (see Grid.jsx).
We keep only the section-level spacing here. */
.about-proof-strip,
.about-grid-section,
.about-credentials-grid {
.about-pillars {
margin-top: var(--section-y-sm);
}
.about-card,
.about-proof-item,
.about-credential-card,
.about-origin-box,
.about-method-section,
.about-trust-note {
.about-pillars-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--gap-sm);
}
.about-pillar {
display: flex;
flex-direction: column;
gap: var(--gap-xs);
min-height: 14rem;
padding: clamp(1.1rem, 2.4vw, 1.8rem);
border: 1px solid var(--theme-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
var(--theme-surface-soft);
}
.about-card,
.about-proof-item,
.about-credential-card {
min-height: 100%;
padding: clamp(1.1rem, 2.4vw, 1.8rem);
transition:
transform var(--duration-med) var(--ease-out),
border-color var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out);
}
.about-card:hover,
.about-proof-item:hover,
.about-credential-card:hover {
.about-pillar:hover {
transform: translateY(-4px);
border-color: rgba(var(--theme-accent-rgb) / 0.42);
box-shadow: var(--theme-shadow-soft);
}
.about-card h3,
.about-credential-card h3 {
margin: 0.9rem 0 0.75rem;
.about-pillar h3 {
margin: 0;
color: var(--theme-text);
font-size: var(--text-xl);
line-height: 1.08;
font-weight: 400;
line-height: 1.1;
letter-spacing: 0;
}
.about-proof-item {
min-height: 9rem;
}
.about-proof-item p {
margin-top: 0.7rem;
}
.about-quote-block {
margin-top: var(--section-y-sm);
padding: clamp(1.4rem, 4vw, 3rem);
overflow: hidden;
border-left: 3px solid var(--theme-accent);
background:
radial-gradient(circle at 100% 0%, rgba(var(--theme-accent-rgb) / 0.18), transparent 18rem),
#171717;
}
.about-quote-block p {
max-width: 58rem;
.about-pillar p {
margin: 0;
color: #fff;
font-size: clamp(1.7rem, 4vw, 4.2rem);
line-height: 1.08;
font-weight: 300;
letter-spacing: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.6;
}
.about-process-section,
.about-origin-section {
/* ----- Origin ------------------------------------------------------------ */
.about-origin {
display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
gap: var(--gap-lg);
align-items: start;
margin-top: var(--section-y-sm);
padding-top: var(--section-y-sm);
border-top: 1px solid var(--theme-border);
}
.about-method-section {
margin-top: var(--section-y-sm);
padding: clamp(1.25rem, 3vw, 2.4rem);
.about-origin-copy h2 {
margin: 0;
color: var(--theme-text);
font-size: clamp(2.2rem, 5.2vw, 5.4rem);
line-height: 0.94;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.about-method-points > div,
.about-origin-box > div {
padding-top: 1rem;
.about-origin-copy p {
max-width: var(--text-measure);
margin: clamp(1rem, 2vw, 1.4rem) 0 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.65;
}
.about-origin-meta {
display: grid;
gap: var(--gap-sm);
margin: 0;
padding: 0;
list-style: none;
}
.about-origin-meta li {
padding-top: var(--gap-xs);
border-top: 1px solid var(--theme-border);
}
.about-origin-box {
display: grid;
gap: var(--gap-sm);
padding: clamp(1.1rem, 2.4vw, 1.8rem);
}
.about-trust-note {
margin-top: var(--section-y-sm);
padding: clamp(1.1rem, 2.4vw, 1.8rem);
border-color: rgba(var(--theme-accent-rgb) / 0.24);
background:
linear-gradient(135deg, rgba(var(--theme-accent-rgb) / 0.11), transparent 60%),
var(--theme-surface-soft);
}
.about-trust-note p {
max-width: 72rem;
margin-top: 0.75rem;
.about-origin-meta p {
margin: 0;
color: var(--theme-text);
font-size: var(--text-base);
line-height: 1.5;
}
.about-bottom-cta {
/* ----- Bottom CTA (shares language with .detail-bottom-cta) ------------- */
.about-cta {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--gap-lg);
@ -263,88 +202,49 @@
padding: clamp(1.5rem, 4vw, 3.5rem);
overflow: hidden;
background:
radial-gradient(circle at 92% 0%, rgba(255, 255, 255, 0.22), transparent 20rem),
var(--theme-accent-fill);
radial-gradient(circle at 86% 12%, rgba(255, 255, 255, 0.24), transparent 18rem),
linear-gradient(135deg, var(--theme-accent-fill), var(--theme-accent-fill-strong));
}
.about-bottom-copy .about-label,
.about-bottom-copy h2,
.about-bottom-copy p {
.about-cta .about-eyebrow,
.about-cta h2 {
color: var(--theme-accent-contrast);
}
.about-bottom-copy p {
max-width: 48rem;
margin-top: 1rem;
.about-cta-copy h2 {
max-width: 22ch;
margin: 0;
font-size: clamp(2rem, 5vw, 5rem);
line-height: 0.96;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.about-bottom-actions {
display: flex;
flex-wrap: wrap;
gap: var(--gap-xs);
justify-content: flex-end;
}
.about-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 48px;
padding: 0 1.1rem;
border: 1px solid transparent;
border-radius: var(--radius-lg);
color: inherit;
font-size: var(--text-sm);
text-decoration: none;
transition:
transform var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out),
background-color var(--duration-med) var(--ease-out);
}
.about-btn:hover,
.about-btn:focus-visible {
transform: translateY(-2px);
box-shadow: var(--theme-shadow-soft);
}
.about-btn--primary {
background: #fff;
color: var(--theme-accent-fill-strong);
}
.about-btn--secondary {
border-color: rgba(255, 255, 255, 0.22);
background: rgba(255, 255, 255, 0.14);
color: #fff;
backdrop-filter: blur(8px);
}
/* ----- Responsive -------------------------------------------------------- */
@media (max-width: 1180px) {
.about-hero,
.about-section--split,
.about-origin-section,
.about-method-section,
.about-bottom-cta {
.about-pillars-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.about-origin,
.about-cta {
grid-template-columns: 1fr;
}
}
@media (max-width: 760px) {
.about-hero {
padding-top: clamp(1.4rem, 5vw, 2rem);
padding-top: clamp(5.5rem, 18vw, 7rem);
}
.about-hero-copy h1 {
.about-hero h1 {
font-size: clamp(2.55rem, 13vw, 4.4rem);
}
.about-bottom-actions {
display: grid;
justify-content: stretch;
}
.about-btn {
width: 100%;
.about-pillars-grid {
grid-template-columns: 1fr;
}
}

View File

@ -1,54 +1,23 @@
import { Link } from "react-router";
import SharedNavbar from "../components/SharedNavbar";
import Grid, { Col } from "../components/layout/Grid";
import PageMeta from "../components/seo/PageMeta";
import "./AboutPage.css";
const PROOF_ITEMS = [
{ label: "ATELIER", text: "Entwicklung aus einem kuratierten Duft- und Materialkontext" },
{ label: "KLEINSERIEN", text: "Chargenbasiert gedacht statt massenmarktfähig optimiert" },
{ label: "KOMPOSITION", text: "Materiallogik vor Trendformel und Lautstärke" },
{ label: "HERKUNFT", text: "Creative Direction und Qualitätsanspruch aus der Schweiz" },
];
const APPROACH_CARDS = [
const PILLARS = [
{
label: "01 / DEKODIEREN",
label: "01 / Material",
title: "Atmosphäre lesen",
text: "Wir beobachten Licht, Materialität, Temperatur, Distanz und Spannung. Nicht als Moodboard allein, sondern als System von Eindrücken, das eine bestimmte Wirkung erzeugt.",
text: "Licht, Temperatur, Oberfläche. Wir beobachten zuerst — dann komponieren wir.",
},
{
label: "02 / VERDICHTEN",
label: "02 / Komposition",
title: "In Duft übersetzen",
text: "Diese Eindrücke werden in eine olfaktorische Sprache übertragen: mineralisch, staubig, metallisch, glatt, rau, still oder warm. So entsteht keine Kopie eines Objekts, sondern seine Atmosphäre.",
text: "Eindrücke werden zu olfaktorischer Sprache: mineralisch, staubig, glatt, still.",
},
{
label: "03 / REDUZIEREN",
label: "03 / Reduktion",
title: "Wirkung schärfen",
text: "Wir lassen weg, was beliebig ist. Übrig bleibt eine klare Signatur: charaktervoll, hochwertig und bewusst nischig. Ein Duft, der Haltung zeigt, statt nur zu gefallen.",
},
];
const CREDENTIAL_CARDS = [
{
label: "ATELIER / ENTWICKLUNG",
title: "Komposition aus einem klaren Duftverständnis",
text: "Jede Arbeit basiert auf einem kuratierten Konzept, einer definierten Materialwelt und mehreren internen Entwicklungsstufen statt auf kurzfristigen Trendbriefings.",
},
{
label: "MATERIALLOGIK",
title: "Noten folgen einer Idee, nicht bloss einer Wirkung",
text: "Rohstoffe und Akkorde werden danach gewählt, welche Oberfläche, Temperatur oder Spannung sie transportieren nicht nur danach, ob sie möglichst sofort gefallen.",
},
{
label: "CHARGENPRINZIP",
title: "Kleinserie statt gesichtsloser Massenästhetik",
text: "atmos denkt in kontrollierten Batches und klaren Editionen. Das stärkt Nachvollziehbarkeit, Qualitätsfokus und die Eigenständigkeit jeder Komposition.",
},
{
label: "QUALITÄTSPRÜFUNG",
title: "Komposition, Haltbarkeit und Verlauf werden bewusst geprüft",
text: "Bewertet werden nicht nur Auftakt und Präsenz, sondern auch Übergänge, Balance, Textur, Wiedererkennbarkeit und das Verhalten auf Haut über mehrere Stunden.",
text: "Wir lassen weg, was beliebig ist. Übrig bleibt eine klare, eigene Signatur.",
},
];
@ -57,294 +26,100 @@ function AboutPage() {
<div className="about-page">
<PageMeta
title="About"
description="atmos dekodiert Atmosphären und übersetzt Materialität, Raum und Charakter in konzeptionelle Düfte. Nischig. Hochwertig. Made in Switzerland."
description="atmos dekodiert Atmosphären und übersetzt Materialität, Raum und Charakter in konzeptionelle Düfte. Made in Switzerland."
path="/about"
/>
<SharedNavbar variant="hero" />
<main id="main-content" className="shell">
<section className="about-hero" data-reveal-group>
<div className="about-hero-copy">
<span className="about-kicker" data-reveal="fade">
ABOUT atmos
</span>
<h1 data-reveal="lines">
WIR DEKODIEREN
<br />
ATMOSPHÄREN
<br />
UND WANDELN
<br />
SIE IN DÜFTE
</h1>
<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
Komposition aus Eindruck, Haltung und Charakter.
</p>
</div>
<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,
konsequent in der Wirkung.
</p>
<div className="about-panel-meta">
<div>
<span>HERKUNFT</span>
<p>Made in Switzerland</p>
</div>
<div>
<span>ANSATZ</span>
<p>Atmosphäre vor Mainstream</p>
</div>
<div>
<span>FOKUS</span>
<p>Duft als Konzeptobjekt</p>
</div>
</div>
</div>
<span className="about-eyebrow" data-reveal="fade">
ABOUT atmos
</span>
<h1 data-reveal="lines">
WIR DEKODIEREN
<br />
ATMOSPHÄREN.
</h1>
<p className="about-lead" data-reveal="fade">
atmos entwickelt Nischendüfte, die eine Atmosphäre präzise
übersetzen Material, Raum, Licht, Spannung. Reduziert in der
Form, konsequent in der Wirkung.
</p>
</section>
<section className="about-section about-section--split" data-reveal-group>
<div className="about-section-heading">
<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" 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
aus Stimmungen, Kontrasten und räumlichen Eindrücken. Uns interessiert
nicht das Laute, Überladene oder Gefällige, sondern das Präzise, das
Eigenständige und das, was nachwirkt.
</p>
<p>
Deshalb arbeiten wir mit Atmosphären wie kaltem Beton, nassem Marmor,
heller Asche oder textilem Puder. Wir übersetzen Oberflächen,
Materialien und emotionale Spannung in olfaktorische Kompositionen,
die sich bewusst von klassischer Parfümerie abheben.
</p>
</div>
</section>
<Grid
as="section"
gap="sm"
className="about-proof-strip"
data-reveal-group
data-reveal-start="top 90%"
>
{PROOF_ITEMS.map((item) => (
<Col
key={item.label}
span={12}
md={6}
lg={3}
className="about-proof-item"
data-reveal="fade"
>
<span className="about-label">{item.label}</span>
<p>{item.text}</p>
</Col>
))}
</Grid>
<section className="about-quote-block" data-reveal-group>
<section className="about-quote" data-reveal-group>
<p data-reveal="fade">
Jeder Duft beginnt bei uns nicht mit einer Note, sondern mit einem
Raumgefühl.
</p>
</section>
<Grid
as="section"
gap="sm"
className="about-grid-section"
data-reveal-group
data-reveal-start="top 84%"
>
{APPROACH_CARDS.map((card) => (
<Col
key={card.label}
span={12}
md={6}
lg={4}
className="about-card"
data-reveal="fade"
>
<span className="about-label">{card.label}</span>
<h3>{card.title}</h3>
<p>{card.text}</p>
</Col>
))}
</Grid>
<section
className="about-section about-section--split about-process-section"
data-reveal-group
>
<div className="about-section-heading">
<span className="about-label" data-reveal="fade">
PROZESS & KOMPETENZ
<section className="about-pillars" data-reveal-group data-reveal-start="top 86%">
<header className="about-section-head">
<span className="about-eyebrow" data-reveal="fade">
Unser Ansatz
</span>
<h2 data-reveal="lines">
WIE AUS EINER IDEE EINE BELASTBARE KOMPOSITION WIRD.
</h2>
</div>
<h2 data-reveal="lines">DREI SCHRITTE. EINE HALTUNG.</h2>
</header>
<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
einer klaren Material- und Spannungslogik: Welche Oberfläche soll
spürbar werden? Welche Temperatur? Welche Dichte? Welche Distanz?
</p>
<p>
Erst danach wird in Akkorden, Übergängen und Gewichtungen gedacht.
So entstehen Düfte, die nicht bloss inspirierte Namen tragen,
sondern in sich konsistent wirken vom ersten Eindruck bis in den
Drydown.
</p>
<div className="about-pillars-grid">
{PILLARS.map((pillar) => (
<article className="about-pillar" key={pillar.label} data-reveal="fade">
<span className="about-eyebrow">{pillar.label}</span>
<h3>{pillar.title}</h3>
<p>{pillar.text}</p>
</article>
))}
</div>
</section>
<Grid
as="section"
gap="sm"
className="about-credentials-grid"
data-reveal-group
data-reveal-start="top 84%"
>
{CREDENTIAL_CARDS.map((card) => (
<Col
key={card.label}
as="article"
span={12}
md={6}
className="about-credential-card"
data-reveal="fade"
>
<span className="about-label">{card.label}</span>
<h3>{card.title}</h3>
<p>{card.text}</p>
</Col>
))}
</Grid>
<section className="about-method-section" data-reveal-group>
<div className="about-method-copy">
<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
und eine klare innere Logik der Komposition.
</p>
</div>
<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>
</div>
<div>
<span>MATERIALBEZUG</span>
<p>Jeder Duft wird über Atmosphäre, Oberfläche und sensorische Spannung verankert.</p>
</div>
<div>
<span>EDITIONEN</span>
<p>Chargen und Editionen machen Entwicklung und Kontext nachvollziehbarer.</p>
</div>
<div>
<span>TRAGBARKEIT</span>
<p>Nische bedeutet bei uns Charakter nicht Beliebigkeit oder reine Provokation.</p>
</div>
</div>
</section>
<section className="about-section about-origin-section" data-reveal-group>
<section className="about-origin" data-reveal-group>
<div className="about-origin-copy">
<span className="about-label" data-reveal="fade">
MADE IN SWITZERLAND
<span className="about-eyebrow" data-reveal="fade">
Made in Switzerland
</span>
<h2 data-reveal="lines">PRÄZISION IN FORM, DUFT UND AUFTRITT.</h2>
<h2 data-reveal="lines">PRÄZISION IN FORM UND DUFT.</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 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.
Verankert in der Schweiz. Kontrollierte Chargen, klare Editionen,
eine Designsprache zwischen Luxus und Konzept. Hochwertig, ohne
laut zu werden.
</p>
</div>
<div className="about-origin-box" data-reveal="fade">
<div>
<span>QUALITÄTSVERSPRECHEN</span>
<p>Kuratiert, präzise und mit Fokus auf charakterstarke Kompositionen.</p>
</div>
<div>
<span>DESIGNSPRACHE</span>
<p>Reduziert, architektonisch und materialorientiert.</p>
</div>
<div>
<span>ZIEL</span>
<p>Düfte schaffen, die nicht austauschbar sind, sondern erinnerbar.</p>
</div>
</div>
<ul className="about-origin-meta" data-reveal="fade">
<li>
<span className="about-eyebrow">Herkunft</span>
<p>Made in Switzerland</p>
</li>
<li>
<span className="about-eyebrow">Ansatz</span>
<p>Atmosphäre vor Mainstream</p>
</li>
<li>
<span className="about-eyebrow">Fokus</span>
<p>Duft als Konzeptobjekt</p>
</li>
</ul>
</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" data-reveal-group>
<div className="about-bottom-copy">
<span className="about-label" data-reveal="fade">
<section className="about-cta" data-reveal-group data-on-accent>
<div className="about-cta-copy">
<span className="about-eyebrow" data-reveal="fade">
atmos
</span>
<h2 data-reveal="lines">
FÜR MENSCHEN, DIE NICHT NUR EINEN DUFT SUCHEN, SONDERN EINE HALTUNG.
FÜR MENSCHEN, DIE SUBSTANZ SUCHEN.
</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" data-reveal="fade">
<Link to="/" className="about-btn about-btn--primary">
Zur Startseite
</Link>
<Link to="/#dufte" className="about-btn about-btn--secondary">
<div className="atmos-btn-row atmos-btn-row--responsive" data-reveal="fade">
<Link to="/#dufte" className="atmos-btn atmos-btn--primary">
Düfte entdecken
</Link>
<Link to="/discovery-set" className="atmos-btn atmos-btn--secondary">
Discovery Set
</Link>
</div>
</section>
</main>

View File

@ -1,108 +1,11 @@
/**
* Datenschutz-specific overrides.
*
* The full visual system lives in ImpressumPage.css under the shared
* `.legal-*` namespace; both pages are rendered with `class="legal-page"`
* so they stay perfectly consistent. Page-specific tweaks belong here.
*/
.datenschutz-page {
min-height: 100vh;
padding: 0 0 var(--section-y-sm);
color: var(--theme-text);
background:
radial-gradient(circle at 86% 8%, rgba(var(--theme-accent-rgb) / 0.12), transparent 28rem),
linear-gradient(180deg, var(--theme-bg), color-mix(in srgb, var(--theme-bg) 88%, #000 12%));
}
.datenschutz-kicker,
.datenschutz-label {
display: block;
color: var(--theme-text-muted);
font-size: var(--text-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
}
.datenschutz-hero {
padding: clamp(2rem, 5vw, 5rem) 0 var(--section-y-sm);
border-bottom: 1px solid var(--theme-border);
}
.datenschutz-hero h1 {
margin: clamp(0.85rem, 2vw, 1.2rem) 0 clamp(1rem, 2vw, 1.35rem);
color: var(--theme-text);
font-size: clamp(3rem, 8vw, 7.2rem);
line-height: 0.9;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
}
.datenschutz-intro {
max-width: var(--text-measure);
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-lg);
line-height: 1.7;
}
.datenschutz-section {
display: grid;
grid-template-columns: minmax(14rem, 0.72fr) minmax(0, 1.28fr);
gap: var(--gap-lg);
align-items: start;
margin-top: var(--section-y-sm);
padding-top: var(--section-y-sm);
border-top: 1px solid var(--theme-border);
}
.datenschutz-section-heading h2 {
margin: 0.75rem 0 0;
color: var(--theme-text);
font-size: clamp(2rem, 4.2vw, 4.5rem);
line-height: 0.96;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.datenschutz-section-copy p {
margin: 0 0 1rem;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.75;
}
.datenschutz-list {
display: grid;
gap: 0.75rem;
margin: 0;
padding-left: 1.1rem;
}
.datenschutz-list li {
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.7;
}
.datenschutz-note-box {
padding: clamp(1.1rem, 2.4vw, 1.8rem);
border: 1px solid rgba(var(--theme-accent-rgb) / 0.24);
background:
linear-gradient(135deg, rgba(var(--theme-accent-rgb) / 0.11), transparent 60%),
var(--theme-surface-soft);
}
.datenschutz-note-box p {
margin: 0;
color: var(--theme-text);
font-size: var(--text-base);
line-height: 1.65;
}
@media (max-width: 900px) {
.datenschutz-section {
grid-template-columns: 1fr;
}
}
@media (max-width: 700px) {
.datenschutz-hero {
padding-top: clamp(1.4rem, 5vw, 2rem);
}
/* No bespoke overrides today — kept for future page-only needs. */
}

View File

@ -1,220 +1,132 @@
import SharedNavbar from "../components/SharedNavbar";
import PageMeta from "../components/seo/PageMeta";
// Datenschutz reuses the shared legal-page system defined in ImpressumPage.css.
import "./ImpressumPage.css";
import "./DatenschutzPage.css";
const SECTIONS = [
{
label: "Verantwortliche Stelle",
title: "Wer für die Datenverarbeitung verantwortlich ist",
body: [
"Verantwortlich für die Datenverarbeitung im Zusammenhang mit dieser Website ist die atmos GmbH, Musterstrasse 12, 7000 Chur, Schweiz.",
"E-Mail: hello@atmos.ch · Telefon: +41 00 000 00 00",
],
},
{
label: "Allgemeines",
title: "Welche Daten erhoben werden",
body: [
"Beim Besuch dieser Website können automatisch technische Daten erfasst werden — etwa IP-Adresse, Datum und Uhrzeit des Zugriffs, Browsertyp, Betriebssystem und Referrer-URL.",
"Personenbezogene Daten werden darüber hinaus nur erhoben, wenn du sie freiwillig mitteilst — etwa bei einer Bestellung, einer Supportanfrage oder einer Newsletter-Anmeldung.",
],
},
{
label: "Zweck",
title: "Wofür die Daten verwendet werden",
body: ["Die Verarbeitung personenbezogener Daten erfolgt insbesondere:"],
list: [
"zur Bereitstellung und technischen Optimierung der Website,",
"zur Bearbeitung von Anfragen und Supportfällen,",
"zur Abwicklung von Bestellungen und Vertragsverhältnissen,",
"zur Sicherheit und Missbrauchsprävention,",
"zur Erfüllung gesetzlicher Verpflichtungen.",
],
},
{
label: "Cookies & Tracking",
title: "Cookies und ähnliche Technologien",
body: [
"Diese Website kann Cookies oder ähnliche Technologien verwenden, um Funktionen bereitzustellen und das Nutzererlebnis zu verbessern.",
"Soweit nicht technisch notwendige Cookies oder Tracking-Tools eingesetzt werden, erfolgt dies nur im Rahmen der jeweils geltenden rechtlichen Vorgaben.",
],
},
{
label: "Weitergabe",
title: "Weitergabe an Dritte",
body: [
"Eine Weitergabe personenbezogener Daten an Dritte erfolgt nur, soweit dies zur Vertragserfüllung notwendig ist, eine gesetzliche Verpflichtung besteht oder eine entsprechende Einwilligung erteilt wurde.",
],
},
{
label: "Speicherdauer",
title: "Wie lange Daten gespeichert werden",
body: [
"Personenbezogene Daten werden nur so lange gespeichert, wie dies für die jeweiligen Zwecke erforderlich ist oder gesetzliche Aufbewahrungspflichten bestehen.",
],
},
{
label: "Rechte",
title: "Rechte der betroffenen Personen",
body: [
"Betroffene Personen haben im Rahmen des anwendbaren Datenschutzrechts insbesondere das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung sowie gegebenenfalls auf Widerspruch.",
],
},
{
label: "Sicherheit",
title: "Technische und organisatorische Massnahmen",
body: [
"Wir treffen angemessene technische und organisatorische Sicherheitsmassnahmen, um personenbezogene Daten vor unbefugtem Zugriff, Verlust oder Manipulation zu schützen.",
],
},
{
label: "Aktualisierung",
title: "Änderungen dieser Datenschutzerklärung",
body: [
"atmos behält sich vor, diese Datenschutzerklärung bei Bedarf anzupassen. Es gilt jeweils die auf dieser Website veröffentlichte aktuelle Fassung.",
],
},
];
function DatenschutzPage() {
return (
<div className="datenschutz-page">
<PageMeta
title="Datenschutz"
description="Datenschutzerklärung von atmos: Erhebung, Speicherung und Verarbeitung personenbezogener Daten."
path="/datenschutz"
/>
<SharedNavbar variant="hero" />
return (
<div className="datenschutz-page legal-page">
<PageMeta
title="Datenschutz"
description="Datenschutzerklärung von atmos: Erhebung, Speicherung und Verarbeitung personenbezogener Daten."
path="/datenschutz"
/>
<SharedNavbar variant="hero" />
<main id="main-content" className="shell">
<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,
zu welchen Zwecken dies erfolgt und welche Rechte betroffene Personen im
Zusammenhang mit ihren Daten haben.
</p>
</section>
<main id="main-content" className="shell">
<section className="legal-hero" data-reveal-group>
<span className="legal-eyebrow" data-reveal="fade">
Rechtliche Angaben
</span>
<h1 data-reveal="lines">DATENSCHUTZ</h1>
<p className="legal-lead" data-reveal="fade">
Der Schutz persönlicher Daten hat für atmos einen hohen Stellenwert.
Nachfolgend erfährst du, welche Daten wir bei der Nutzung dieser
Website verarbeiten und welche Rechte du dabei hast.
</p>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label" data-reveal="fade">VERANTWORTLICHE STELLE</span>
<h2 data-reveal="lines">WER FÜR DIE DATENVERARBEITUNG VERANTWORTLICH IST</h2>
</div>
{SECTIONS.map((section) => (
<section className="legal-section" key={section.label} data-reveal-group>
<header className="legal-section-head">
<span className="legal-eyebrow" data-reveal="fade">
{section.label}
</span>
<h2 data-reveal="lines">{section.title}</h2>
</header>
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Verantwortlich für die Datenverarbeitung im Zusammenhang mit dieser
Website ist:
</p>
<p>
atmos GmbH
<br />
Musterstrasse 12
<br />
7000 Chur
<br />
Schweiz
</p>
<p>
E-Mail: hello@atmos.ch
<br />
Telefon: +41 00 000 00 00
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label" data-reveal="fade">ALLGEMEINES</span>
<h2 data-reveal="lines">WELCHE DATEN ERHOBEN WERDEN</h2>
</div>
<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
Uhrzeit des Zugriffs, Browsertyp, Betriebssystem, Referrer-URL sowie
weitere Informationen, die zur Stabilität und Sicherheit der Website
erforderlich sind.
</p>
<p>
Personenbezogene Daten werden darüber hinaus nur dann erhoben, wenn
du uns diese freiwillig mitteilst, beispielsweise über ein
Kontaktformular, bei einer Bestellung, bei einer Supportanfrage oder
durch die Anmeldung zu einem Newsletter.
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<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" data-reveal="fade">
<p>Die Verarbeitung personenbezogener Daten erfolgt insbesondere:</p>
<ul className="datenschutz-list">
<li>zur Bereitstellung und technischen Optimierung der Website,</li>
<li>zur Bearbeitung von Anfragen und Supportfällen,</li>
<li>zur Abwicklung von Bestellungen und Vertragsverhältnissen,</li>
<li>zur Kommunikation mit Kundinnen und Kunden,</li>
<li>zur Sicherheit und Missbrauchsprävention,</li>
<li>zur Erfüllung gesetzlicher Verpflichtungen.</li>
</ul>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<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" data-reveal="fade">
<p>
Diese Website kann Cookies oder ähnliche Technologien verwenden, um
Funktionen bereitzustellen, die Nutzung zu analysieren und das
Nutzererlebnis zu verbessern. Cookies sind kleine Textdateien, die
auf deinem Endgerät gespeichert werden.
</p>
<p>
Soweit technisch nicht notwendige Cookies oder Tracking-Tools
eingesetzt werden, erfolgt dies nur im Rahmen der jeweils geltenden
rechtlichen Vorgaben. Falls du ein Cookie-Banner oder
Einwilligungs-Tool einsetzt, sollten die hier gemachten Angaben exakt
auf dieses Setup abgestimmt sein.
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label" data-reveal="fade">WEITERGABE</span>
<h2 data-reveal="lines">WEITERGABE AN DRITTE</h2>
</div>
<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
Verpflichtung besteht, ein berechtigtes Interesse vorliegt oder eine
entsprechende Einwilligung erteilt wurde.
</p>
<p>
Eine Datenweitergabe kann insbesondere an technische Dienstleister,
Hosting-Anbieter, Zahlungsdienstleister, Versandpartner oder
Analyseanbieter erfolgen, sofern dies für den Betrieb der Website
oder die angebotenen Leistungen erforderlich ist.
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<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" data-reveal="fade">
<p>
Personenbezogene Daten werden nur so lange gespeichert, wie dies für
die jeweiligen Zwecke erforderlich ist oder gesetzliche
Aufbewahrungspflichten bestehen. Anschliessend werden die Daten
gelöscht oder anonymisiert, soweit keine weitere rechtliche Grundlage
für die Verarbeitung besteht.
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label" data-reveal="fade">RECHTE</span>
<h2 data-reveal="lines">RECHTE DER BETROFFENEN PERSONEN</h2>
</div>
<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,
Einschränkung der Verarbeitung sowie gegebenenfalls auf Widerspruch
gegen bestimmte Datenverarbeitungen.
</p>
<p>
Anfragen hierzu können an die oben genannte verantwortliche Stelle
gerichtet werden.
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label" data-reveal="fade">SICHERHEIT</span>
<h2 data-reveal="lines">TECHNISCHE UND ORGANISATORISCHE MASSNAHMEN</h2>
</div>
<div className="datenschutz-section-copy" data-reveal="fade">
<p>
Wir treffen angemessene technische und organisatorische
Sicherheitsmassnahmen, um personenbezogene Daten vor unbefugtem
Zugriff, Verlust, Missbrauch oder Manipulation zu schützen.
</p>
<p>
Bitte beachte jedoch, dass die Datenübertragung im Internet trotz
aller Sorgfalt Sicherheitslücken aufweisen kann.
</p>
</div>
</section>
<section className="datenschutz-section" data-reveal-group>
<div className="datenschutz-section-heading">
<span className="datenschutz-label" data-reveal="fade">AKTUALISIERUNG</span>
<h2 data-reveal="lines">ÄNDERUNGEN DIESER DATENSCHUTZERKLÄRUNG</h2>
</div>
<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
Prozesse oder die angebotenen digitalen Leistungen weiterentwickeln.
Es gilt jeweils die auf dieser Website veröffentlichte aktuelle
Fassung.
</p>
</div>
</section>
</main>
</div>
);
<div className="legal-section-body" data-reveal="fade">
{section.body.map((paragraph) => (
<p key={paragraph}>{paragraph}</p>
))}
{section.list && (
<ul className="legal-list">
{section.list.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
)}
</div>
</section>
))}
</main>
</div>
);
}
export default DatenschutzPage;

View File

@ -1,91 +1,105 @@
.impressum-page {
/**
* Shared legal-page styling used by both Impressum and Datenschutz.
* Aligned with the global design system: .shell container, eyebrow + light
* weight headlines, hard-edge surface cards, accent stripes via tokens.
*
* Class prefix `.legal-*` is shared so both pages stay perfectly consistent.
*/
.legal-page {
min-height: 100vh;
padding: 0 0 var(--section-y-sm);
color: var(--theme-text);
background:
radial-gradient(circle at 86% 8%, rgba(var(--theme-accent-rgb) / 0.12), transparent 28rem),
linear-gradient(180deg, var(--theme-bg), color-mix(in srgb, var(--theme-bg) 88%, #000 12%));
background: var(--theme-bg);
}
.impressum-kicker,
.impressum-label {
.legal-eyebrow {
display: block;
margin-bottom: var(--gap-2xs);
color: var(--theme-text-muted);
font-size: var(--text-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
}
.impressum-hero {
padding: clamp(2rem, 5vw, 5rem) 0 var(--section-y-sm);
border-bottom: 1px solid var(--theme-border);
/* ----- Hero -------------------------------------------------------------- */
.legal-hero {
display: grid;
gap: clamp(0.75rem, 1.6vw, 1.25rem);
padding: clamp(5rem, 9vw, 8rem) 0 var(--section-y-sm);
}
.impressum-hero h1 {
margin: clamp(0.85rem, 2vw, 1.2rem) 0 clamp(1rem, 2vw, 1.35rem);
.legal-hero h1 {
margin: 0;
color: var(--theme-text);
font-size: clamp(3rem, 8vw, 7.2rem);
font-size: clamp(3rem, 8vw, 7.6rem);
line-height: 0.9;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.impressum-intro {
.legal-lead {
max-width: var(--text-measure);
margin: 0;
margin: clamp(1rem, 2vw, 1.4rem) 0 0;
color: var(--theme-text-muted);
font-size: var(--text-lg);
line-height: 1.65;
line-height: 1.62;
}
.impressum-grid {
/* ----- Fact grid (Impressum address blocks) ---------------------------- */
.legal-fact-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--gap-sm);
margin-top: var(--section-y-sm);
}
.impressum-card,
.impressum-note-box {
.legal-fact {
display: flex;
flex-direction: column;
gap: var(--gap-xs);
min-height: 12rem;
padding: clamp(1.1rem, 2.4vw, 1.6rem);
border: 1px solid var(--theme-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
var(--theme-surface-soft);
}
.impressum-card {
min-height: 210px;
padding: clamp(1.1rem, 2.4vw, 1.8rem);
transition:
transform var(--duration-med) var(--ease-out),
border-color var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out);
}
.impressum-card:hover {
.legal-fact:hover {
transform: translateY(-4px);
border-color: rgba(var(--theme-accent-rgb) / 0.42);
box-shadow: var(--theme-shadow-soft);
}
.impressum-card h2 {
margin: 0.9rem 0 0.75rem;
.legal-fact h2 {
margin: 0;
color: var(--theme-text);
font-size: var(--text-xl);
line-height: 1.08;
font-size: var(--text-lg);
font-weight: 400;
line-height: 1.15;
letter-spacing: 0;
text-transform: none;
}
.impressum-card p {
.legal-fact p {
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.7;
line-height: 1.6;
}
.impressum-section {
/* ----- Long-form sections ----------------------------------------------- */
.legal-section {
display: grid;
grid-template-columns: minmax(14rem, 0.72fr) minmax(0, 1.28fr);
gap: var(--gap-lg);
@ -95,10 +109,10 @@
border-top: 1px solid var(--theme-border);
}
.impressum-section-heading h2 {
margin: 0.75rem 0 0;
.legal-section-head h2 {
margin: 0;
color: var(--theme-text);
font-size: clamp(2rem, 4.2vw, 4.5rem);
font-size: clamp(1.85rem, 4vw, 3.6rem);
line-height: 0.96;
font-weight: 300;
letter-spacing: 0;
@ -106,37 +120,44 @@
text-wrap: balance;
}
.impressum-section-copy p {
margin: 0 0 1rem;
.legal-section-body {
display: flex;
flex-direction: column;
gap: var(--gap-sm);
max-width: var(--text-measure);
}
.legal-section-body p {
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.75;
line-height: 1.7;
}
.impressum-note-box {
padding: clamp(1.1rem, 2.4vw, 1.8rem);
border-color: rgba(var(--theme-accent-rgb) / 0.24);
background:
linear-gradient(135deg, rgba(var(--theme-accent-rgb) / 0.11), transparent 60%),
var(--theme-surface-soft);
}
.impressum-note-box p {
.legal-list {
display: grid;
gap: 0.6rem;
margin: 0;
color: var(--theme-text);
padding-left: 1.1rem;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.65;
line-height: 1.6;
}
@media (max-width: 900px) {
.impressum-grid,
.impressum-section {
/* ----- Responsive -------------------------------------------------------- */
@media (max-width: 1024px) {
.legal-section {
grid-template-columns: 1fr;
}
}
@media (max-width: 700px) {
.impressum-hero {
padding-top: clamp(1.4rem, 5vw, 2rem);
@media (max-width: 760px) {
.legal-hero {
padding-top: clamp(5.5rem, 18vw, 7rem);
}
.legal-fact-grid {
grid-template-columns: 1fr;
}
}

View File

@ -2,9 +2,70 @@ import SharedNavbar from "../components/SharedNavbar";
import PageMeta from "../components/seo/PageMeta";
import "./ImpressumPage.css";
const FACTS = [
{
label: "Anbieter",
title: "Unternehmen",
body: (
<>
atmos GmbH
<br />
Musterstrasse 12
<br />
7000 Chur, Schweiz
</>
),
},
{
label: "Kontakt",
title: "Erreichbarkeit",
body: (
<>
hello@atmos.ch
<br />
+41 00 000 00 00
</>
),
},
{
label: "Vertretungsberechtigt",
title: "Geschäftsführung",
body: <>Amanda Nielsen, Ermin Zoronjic, Tobias Högger, Salih Hasicic</>,
},
{
label: "Handelsregister",
title: "Registereintrag",
body: (
<>
Handelsregisteramt: Kanton Graubünden
<br />
UID: CHE-000.000.000
</>
),
},
];
const SECTIONS = [
{
label: "Inhaltliche Verantwortung",
title: "Verantwortlich für den Inhalt",
body: "Verantwortlich für diese Website und die publizierten Inhalte ist die atmos GmbH, Musterstrasse 12, 7000 Chur, Schweiz.",
},
{
label: "Haftung",
title: "Haftungsausschluss",
body: "Trotz sorgfältiger Kontrolle übernimmt atmos keine Gewähr für Aktualität, Richtigkeit oder Vollständigkeit der bereitgestellten Informationen. Haftungsansprüche werden im gesetzlich zulässigen Rahmen ausgeschlossen.",
},
{
label: "Urheberrecht",
title: "Urheber- und Markenrechte",
body: "Sämtliche Inhalte dieser Website sind urheberrechtlich geschützt. Jede Vervielfältigung oder Bearbeitung bedarf der vorherigen schriftlichen Zustimmung.",
},
];
function ImpressumPage() {
return (
<div className="impressum-page">
<div className="impressum-page legal-page">
<PageMeta
title="Impressum"
description="Rechtliche Angaben, Verantwortlichkeit und Erreichbarkeit von atmos."
@ -13,148 +74,40 @@ function ImpressumPage() {
<SharedNavbar variant="hero" />
<main id="main-content" className="shell">
<section className="impressum-hero" data-reveal-group>
<span className="impressum-kicker" data-reveal="fade">
RECHTLICHE ANGABEN
<section className="legal-hero" data-reveal-group>
<span className="legal-eyebrow" 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 className="legal-lead" data-reveal="fade">
Rechtliche Angaben zu atmos sowie Informationen zur Verantwortlichkeit,
Erreichbarkeit und zu den veröffentlichten Inhalten dieser Website.
</p>
</section>
<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>
atmos GmbH
<br />
Musterstrasse 12
<br />
7000 Chur
<br />
Schweiz
</p>
</article>
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">KONTAKT</span>
<h2>Erreichbarkeit</h2>
<p>
E-Mail: hello@atmos.ch
<br />
Telefon: +41 00 000 00 00
</p>
</article>
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">VERTRETUNGSBERECHTIGT</span>
<h2>Geschäftsführung</h2>
<p>
Amanda Nielsen, Ermin Zoronjic, Tobias Högger, Salih Hasicic
<br />
Geschäftsführer*innen
</p>
</article>
<article className="impressum-card" data-reveal="fade">
<span className="impressum-label">HANDELSREGISTER</span>
<h2>Registereintrag</h2>
<p>
Handelsregisteramt: Kanton Graubünden
<br />
Unternehmens-Identifikationsnummer: CHE-000.000.000
</p>
</article>
<section className="legal-fact-grid" data-reveal-group data-reveal-start="top 88%">
{FACTS.map((fact) => (
<article className="legal-fact" key={fact.label} data-reveal="fade">
<span className="legal-eyebrow">{fact.label}</span>
<h2>{fact.title}</h2>
<p>{fact.body}</p>
</article>
))}
</section>
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<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" data-reveal="fade">
<p>
Verantwortlich für diese Website und die publizierten Inhalte:
</p>
<p>
atmos GmbH
<br />
Musterstrasse 12
<br />
7000 Chur
<br />
Schweiz
</p>
</div>
</section>
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label" data-reveal="fade">
HAFTUNG
</span>
<h2 data-reveal="lines">HAFTUNGSAUSSCHLUSS</h2>
</div>
<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
Qualität der bereitgestellten Informationen. Haftungsansprüche gegen
atmos wegen Schäden materieller oder immaterieller Art, welche aus
dem Zugriff oder der Nutzung beziehungsweise Nichtnutzung der
veröffentlichten Informationen, durch Missbrauch der Verbindung oder
durch technische Störungen entstanden sind, werden im gesetzlich
zulässigen Rahmen ausgeschlossen.
</p>
</div>
</section>
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label" data-reveal="fade">
URHEBERRECHT
</span>
<h2 data-reveal="lines">URHEBER- UND MARKENRECHTE</h2>
</div>
<div className="impressum-section-copy" data-reveal="fade">
<p>
Sämtliche Inhalte dieser Website, einschliesslich Texte, Bilder,
Gestaltungselemente, Logos und Marken, sind urheberrechtlich
geschützt und Eigentum von atmos oder der jeweils genannten
Rechteinhaber, sofern nicht anders angegeben. Jede Vervielfältigung,
Bearbeitung, Verbreitung oder sonstige Verwendung ausserhalb der
gesetzlichen Schranken bedarf der vorherigen schriftlichen Zustimmung.
</p>
</div>
</section>
<section className="impressum-section" data-reveal-group>
<div className="impressum-section-heading">
<span className="impressum-label" data-reveal="fade">
TRANSPARENZ
</span>
<h2 data-reveal="lines">KLARE ANGABEN UND ERREICHBARKEIT</h2>
</div>
<div className="impressum-note-box" data-reveal="fade">
<p>
atmos legt Wert auf eine klare, transparente und nachvollziehbare
Kommunikation. Dieses Impressum dient der eindeutigen
Anbieterkennzeichnung und stellt die wesentlichen Informationen zur
verantwortlichen Stelle, Kontaktaufnahme und rechtlichen Zuordnung
dieser Website bereit.
</p>
</div>
</section>
{SECTIONS.map((section) => (
<section className="legal-section" key={section.label} data-reveal-group>
<header className="legal-section-head">
<span className="legal-eyebrow" data-reveal="fade">
{section.label}
</span>
<h2 data-reveal="lines">{section.title}</h2>
</header>
<div className="legal-section-body" data-reveal="fade">
<p>{section.body}</p>
</div>
</section>
))}
</main>
</div>
);

View File

@ -1,124 +1,153 @@
/**
* SmallBatchPage (Early Access) same visual rhythm as About / Support.
* All buttons go through the global .atmos-btn system; only structural,
* data-display styling lives here.
*/
.small-page {
min-height: 100vh;
padding: 0 0 var(--section-y-sm);
color: var(--theme-text);
background:
radial-gradient(circle at 84% 8%, rgba(var(--theme-accent-rgb) / 0.13), transparent 28rem),
linear-gradient(180deg, var(--theme-bg), color-mix(in srgb, var(--theme-bg) 88%, #000 12%));
background: var(--theme-bg);
}
.small-hero {
max-width: 64rem;
padding: clamp(2rem, 5vw, 5rem) 0 var(--section-y-sm);
border-bottom: 1px solid var(--theme-border);
}
.small-kicker,
.small-requirement span,
.release-card span {
.small-eyebrow {
display: block;
margin-bottom: 0.75rem;
margin-bottom: var(--gap-2xs);
color: var(--theme-text-muted);
font-size: var(--text-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
}
.small-hero h1,
.small-panel h2 {
margin: 0 0 clamp(0.85rem, 2vw, 1.2rem);
color: var(--theme-text);
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
/* ----- Hero -------------------------------------------------------------- */
.small-hero {
display: grid;
gap: clamp(0.75rem, 1.6vw, 1.25rem);
max-width: 64rem;
padding: clamp(5rem, 9vw, 8rem) 0 var(--section-y-sm);
}
.small-hero h1 {
margin: 0;
color: var(--theme-text);
font-size: clamp(3rem, 8.6vw, 9rem);
line-height: 0.88;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.small-panel h2 {
font-size: clamp(2.2rem, 5vw, 5.4rem);
line-height: 0.94;
}
.small-hero p,
.small-panel p,
.release-card p {
.small-lead {
max-width: var(--text-measure);
margin: 0;
margin: clamp(1rem, 2vw, 1.4rem) 0 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.65;
font-size: var(--text-lg);
line-height: 1.62;
}
.small-panel,
.release-card,
.small-error {
/* ----- Panels ------------------------------------------------------------ */
.small-panel {
display: flex;
flex-direction: column;
gap: var(--gap-sm);
margin-top: var(--section-y-sm);
padding: clamp(1.25rem, 3vw, 2.2rem);
border: 1px solid var(--theme-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
var(--theme-surface-soft);
}
.small-panel {
max-width: 68rem;
margin-top: var(--section-y-sm);
padding: clamp(1.25rem, 3vw, 2.2rem);
.small-panel h2 {
margin: 0;
color: var(--theme-text);
font-size: clamp(1.85rem, 4vw, 3.4rem);
line-height: 0.96;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
}
.small-panel button,
.release-card button {
.small-panel p {
max-width: var(--text-measure);
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.6;
}
.small-panel-head {
display: flex;
flex-wrap: wrap;
align-items: end;
justify-content: space-between;
gap: var(--gap-sm);
}
.small-panel-head > div {
min-width: 0;
}
.small-status-pill {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 48px;
margin-top: 1.2rem;
padding: 0 1.1rem;
border: 1px solid #111;
border-radius: var(--radius-lg);
background: #111;
color: #fff;
cursor: pointer;
font-size: var(--text-sm);
min-height: 32px;
padding: 0 0.85rem;
border: 1px solid var(--theme-border);
background: transparent;
color: var(--theme-text-muted);
font-size: var(--text-xs);
letter-spacing: 0.18em;
text-transform: uppercase;
letter-spacing: 0.1em;
transition:
transform var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out),
background-color var(--duration-med) var(--ease-out);
}
.small-panel button:hover,
.release-card button:hover,
.small-panel button:focus-visible,
.release-card button:focus-visible {
transform: translateY(-2px);
box-shadow: var(--theme-shadow-soft);
.small-status-pill.is-unlocked {
border-color: var(--theme-accent);
color: var(--theme-accent);
}
/* ----- Requirements grid ------------------------------------------------ */
.small-requirements {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--gap-sm);
margin-top: clamp(1.4rem, 3vw, 2.4rem);
gap: var(--gap-xs);
margin-top: var(--gap-sm);
}
.small-requirement {
min-height: 7rem;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--gap-xs);
padding: clamp(1rem, 2vw, 1.4rem);
min-height: 7rem;
padding: clamp(0.95rem, 2vw, 1.25rem);
border: 1px solid var(--theme-border);
background: var(--theme-paper);
isolation: isolate;
}
.small-requirement span {
margin-bottom: 0;
.small-requirement::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--theme-accent);
transform: scaleX(0);
transform-origin: left center;
transition: transform var(--duration-med) var(--ease-out);
}
.small-requirement.is-met::before {
transform: scaleX(1);
}
.small-requirement strong {
@ -128,18 +157,26 @@
line-height: 1.3;
}
.small-requirement strong.met {
.small-requirement.is-met strong {
color: var(--theme-accent);
}
.small-error {
max-width: 68rem;
/* ----- Status / error message ------------------------------------------ */
.small-message {
max-width: 64rem;
margin: var(--gap-sm) 0 0;
padding: clamp(1rem, 2vw, 1.4rem);
border-color: rgba(var(--theme-accent-rgb) / 0.45);
border: 1px solid rgba(var(--theme-accent-rgb) / 0.45);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
var(--theme-surface-soft);
color: var(--theme-text);
font-size: var(--text-sm);
}
/* ----- Releases --------------------------------------------------------- */
.release-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
@ -148,12 +185,15 @@
}
.release-card {
min-height: 280px;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--gap-sm);
padding: clamp(1.1rem, 2.4vw, 1.8rem);
gap: var(--gap-xs);
min-height: 18rem;
padding: clamp(1.1rem, 2.4vw, 1.6rem);
border: 1px solid var(--theme-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
var(--theme-surface-soft);
transition:
transform var(--duration-med) var(--ease-out),
border-color var(--duration-med) var(--ease-out),
@ -166,21 +206,30 @@
box-shadow: var(--theme-shadow-soft);
}
.release-card span {
margin-bottom: 0;
}
.release-card h3 {
margin: 0;
color: var(--theme-text);
font-size: var(--text-xl);
line-height: 1.08;
font-size: var(--text-lg);
font-weight: 400;
line-height: 1.15;
letter-spacing: 0;
}
.release-card p {
flex: 1;
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.6;
}
/* ----- Responsive -------------------------------------------------------- */
@media (max-width: 1180px) {
.small-requirements,
.small-requirements {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.release-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@ -188,7 +237,7 @@
@media (max-width: 760px) {
.small-hero {
padding-top: clamp(1.4rem, 5vw, 2rem);
padding-top: clamp(5.5rem, 18vw, 7rem);
}
.small-hero h1 {
@ -199,9 +248,4 @@
.release-grid {
grid-template-columns: 1fr;
}
.small-panel button,
.release-card button {
width: 100%;
}
}

View File

@ -7,9 +7,9 @@ import "./SmallBatchPage.css";
function Requirement({ label, met, children }) {
return (
<div className="small-requirement">
<span>{label}</span>
<strong className={met ? "met" : ""}>{children || (met ? "met" : "open")}</strong>
<div className={`small-requirement ${met ? "is-met" : ""}`}>
<span className="small-eyebrow">{label}</span>
<strong>{children || (met ? "Erfüllt" : "Offen")}</strong>
</div>
);
}
@ -70,57 +70,89 @@ function SmallBatchPage() {
<main id="main-content" className="shell">
<section className="small-hero" data-reveal-group>
<span className="small-kicker" data-reveal="fade">
SMALL BATCH / ARCHIVE / PROTOTYPE
<span className="small-eyebrow" 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.
<h1 data-reveal="lines">EARLY ACCESS.</h1>
<p className="small-lead" data-reveal="fade">
Limitierte Releases sind Kund:innen vorbehalten, die genug
Berührung mit der atmos Materialwelt hatten kuratiert,
chargenbasiert, in kleiner Auflage.
</p>
</section>
{!user ? (
<section className="small-panel" data-reveal-group>
<span className="small-kicker" data-reveal="fade">
LOGIN REQUIRED
<span className="small-eyebrow" data-reveal="fade">
Login erforderlich
</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>
<h2 data-reveal="fade">Melde dich an, um deinen Zugang zu prüfen.</h2>
<p data-reveal="fade">
Small Batch Access wird aus deinen abgeschlossenen Bestellungen
berechnet.
</p>
<div className="atmos-btn-row" data-reveal="fade">
<button
type="button"
className="atmos-btn atmos-btn--primary"
onClick={openProfile}
>
Anmelden / Registrieren
</button>
</div>
</section>
) : (
<>
<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-panel-head">
<div>
<span className="small-eyebrow" data-reveal="fade">
Access Status
</span>
<h2 data-reveal="fade">
{loyalty.unlocked ? "Freigeschaltet" : "Noch gesperrt"}
</h2>
</div>
<span
className={`small-status-pill ${loyalty.unlocked ? "is-unlocked" : ""}`}
data-reveal="fade"
>
{loyalty.unlocked ? "Unlocked" : "Locked"}
</span>
</div>
<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}>
{loyalty.purchases}/3 Purchases
<Requirement label="Bestellungen" met={loyalty.purchases >= 3}>
{loyalty.purchases}/3
</Requirement>
<Requirement label="Spend" met={loyalty.spent_cents > 50000}>
<Requirement label="Umsatz" met={loyalty.spent_cents > 50000}>
{formatChf(loyalty.spent_cents)} / CHF 500+
</Requirement>
</div>
</section>
{state.error && <p className="small-error">{state.error}</p>}
{state.loading && <p className="small-error">Loading access...</p>}
{state.error && <p className="small-message">{state.error}</p>}
{state.loading && <p className="small-message">Loading access</p>}
{loyalty.unlocked && (
<section className="release-grid" data-reveal-group data-reveal-start="top 88%">
{loyalty.unlocked && state.releases.length > 0 && (
<section
className="release-grid"
data-reveal-group
data-reveal-start="top 88%"
>
{state.releases.map((release) => (
<article className="release-card" key={release.name} data-reveal="fade">
<span>{release.type}</span>
<span className="small-eyebrow">{release.type}</span>
<h3>{release.name}</h3>
<p>{release.note}</p>
<button type="button">Request Allocation</button>
<button
type="button"
className="atmos-btn atmos-btn--outline atmos-btn--block"
>
Allokation anfragen
</button>
</article>
))}
</section>

View File

@ -1,30 +1,35 @@
/**
* SupportPage same visual rhythm as AboutPage:
* - .shell container, eyebrow + light-weight headline
* - hard-edge surface cards
* - accent CTA banner reused at the bottom
* - all CTAs use the global .atmos-btn system
*/
.support-page {
min-height: 100vh;
padding: 0 0 var(--section-y-sm);
color: var(--theme-text);
background:
radial-gradient(circle at 86% 8%, rgba(var(--theme-accent-rgb) / 0.13), transparent 28rem),
linear-gradient(180deg, var(--theme-bg), color-mix(in srgb, var(--theme-bg) 88%, #000 12%));
background: var(--theme-bg);
}
.support-kicker,
.support-label,
.support-panel-label,
.support-panel-meta span {
.support-eyebrow {
display: block;
margin-bottom: var(--gap-2xs);
color: var(--theme-text-muted);
font-size: var(--text-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
}
/* ----- Hero -------------------------------------------------------------- */
.support-hero {
display: grid;
grid-template-columns: minmax(0, 1.45fr) minmax(18rem, 0.72fr);
grid-template-columns: minmax(0, 1.4fr) minmax(18rem, 0.72fr);
gap: var(--gap-lg);
align-items: end;
padding: clamp(2rem, 5vw, 5rem) 0 var(--section-y-sm);
border-bottom: 1px solid var(--theme-border);
padding: clamp(5rem, 9vw, 8rem) 0 var(--section-y-sm);
}
.support-hero-copy {
@ -32,10 +37,10 @@
}
.support-hero-copy h1 {
max-width: 10.8ch;
margin: clamp(0.85rem, 2vw, 1.2rem) 0 clamp(1rem, 2vw, 1.35rem);
max-width: 12ch;
margin: 0;
color: var(--theme-text);
font-size: clamp(3rem, 7.4vw, 8.8rem);
font-size: clamp(2.8rem, 8vw, 8rem);
line-height: 0.9;
font-weight: 300;
letter-spacing: 0;
@ -43,108 +48,68 @@
text-wrap: balance;
}
.support-intro {
.support-lead {
max-width: var(--text-measure);
margin: 0;
margin: clamp(1rem, 2vw, 1.4rem) 0 0;
color: var(--theme-text-muted);
font-size: var(--text-lg);
line-height: 1.65;
}
.support-hero-panel {
padding: clamp(1.25rem, 3vw, 2rem);
border: 1px solid rgba(var(--theme-accent-rgb) / 0.2);
background:
linear-gradient(135deg, rgba(var(--theme-accent-rgb) / 0.1), transparent 62%),
var(--theme-surface-soft);
}
.support-hero-panel p {
margin: 0.75rem 0 0;
color: var(--theme-text);
font-size: var(--text-base);
line-height: 1.62;
}
.support-panel-meta {
display: grid;
gap: var(--gap-sm);
margin-top: clamp(1.2rem, 2.6vw, 2rem);
padding-top: var(--gap-sm);
border-top: 1px solid rgba(var(--theme-accent-rgb) / 0.2);
/* ----- Contact card ------------------------------------------------------ */
.support-contact-card {
display: flex;
flex-direction: column;
gap: var(--gap-xs);
padding: clamp(1.25rem, 3vw, 1.8rem);
border: 1px solid var(--theme-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
var(--theme-surface-soft);
}
.support-panel-meta p {
margin: 0.45rem 0 0;
.support-mail-link {
margin: 0;
color: var(--theme-text);
font-size: clamp(1.25rem, 2.4vw, 1.75rem);
font-weight: 400;
letter-spacing: 0;
text-decoration: none;
word-break: break-word;
}
.support-mail-link:hover {
color: var(--theme-accent);
}
.support-contact-card p {
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-sm);
line-height: 1.55;
}
.support-quick-grid,
.support-info-grid,
.support-faq-grid {
display: grid;
gap: var(--gap-sm);
.support-contact-card .atmos-btn {
margin-top: var(--gap-xs);
}
.support-quick-grid {
/* ----- Topic cards ------------------------------------------------------- */
.support-topics {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--gap-sm);
margin-top: var(--section-y-sm);
}
.support-section,
.support-faq-section {
padding-top: var(--section-y-sm);
}
.support-section--split,
.support-info-grid {
display: grid;
grid-template-columns: minmax(16rem, 0.72fr) minmax(0, 1.28fr);
gap: var(--gap-lg);
align-items: start;
}
.support-info-grid {
margin-top: var(--section-y-sm);
}
.support-section-heading h2,
.support-bottom-copy h2 {
margin: 0.75rem 0 0;
color: var(--theme-text);
font-size: clamp(2.15rem, 5.2vw, 6rem);
line-height: 0.94;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.support-section-copy {
.support-topic,
.support-faq-item {
display: flex;
flex-direction: column;
gap: var(--gap-sm);
}
.support-section-copy p,
.support-bottom-copy p,
.support-quick-card p,
.support-faq-card p,
.support-info-box p,
.support-list li {
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.7;
}
.support-quick-card,
.support-faq-card,
.support-info-box {
min-height: 100%;
padding: clamp(1.1rem, 2.4vw, 1.8rem);
gap: var(--gap-xs);
min-height: 12rem;
padding: clamp(1.1rem, 2.4vw, 1.6rem);
border: 1px solid var(--theme-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.008)),
@ -155,82 +120,65 @@
box-shadow var(--duration-med) var(--ease-out);
}
.support-quick-card:hover,
.support-faq-card:hover,
.support-info-box:hover {
.support-topic:hover,
.support-faq-item:hover {
transform: translateY(-4px);
border-color: rgba(var(--theme-accent-rgb) / 0.42);
box-shadow: var(--theme-shadow-soft);
}
.support-quick-card h3,
.support-faq-card h3,
.support-info-box h3 {
margin: 0.9rem 0 0.75rem;
.support-topic h3,
.support-faq-item h3 {
margin: 0;
color: var(--theme-text);
font-size: var(--text-xl);
line-height: 1.08;
font-size: var(--text-lg);
font-weight: 400;
line-height: 1.15;
letter-spacing: 0;
}
.support-info-box--dark {
border-color: rgba(255, 255, 255, 0.16);
background:
radial-gradient(circle at 100% 0%, rgba(var(--theme-accent-rgb) / 0.18), transparent 18rem),
#171717;
.support-topic p,
.support-faq-item p {
margin: 0;
color: var(--theme-text-muted);
font-size: var(--text-base);
line-height: 1.6;
}
.support-info-box--dark .support-label,
.support-info-box--dark p {
color: rgba(255, 255, 255, 0.78);
}
/* ----- FAQ --------------------------------------------------------------- */
.support-info-box--dark h3 {
color: #fff;
}
.support-list {
display: grid;
gap: 0.75rem;
margin: 1rem 0 0;
padding-left: 1.1rem;
}
.support-mail-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 48px;
margin-top: 1.15rem;
padding: 0 1.1rem;
border-radius: var(--radius-lg);
background: var(--theme-accent-fill);
color: var(--theme-accent-contrast);
font-size: var(--text-sm);
text-decoration: none;
transition:
transform var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out);
}
.support-mail-btn:hover,
.support-mail-btn:focus-visible {
transform: translateY(-2px);
box-shadow: var(--theme-shadow-soft);
}
.support-faq-section {
.support-faq {
margin-top: var(--section-y-sm);
padding-top: var(--section-y-sm);
border-top: 1px solid var(--theme-border);
}
.support-faq-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
margin-top: clamp(1.6rem, 4vw, 3rem);
.support-section-head {
display: grid;
gap: var(--gap-2xs);
margin-bottom: clamp(1.6rem, 4vw, 3rem);
}
.support-bottom-cta {
.support-section-head h2 {
margin: 0;
color: var(--theme-text);
font-size: clamp(2.2rem, 5.4vw, 6rem);
line-height: 0.94;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.support-faq-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--gap-sm);
}
/* ----- CTA --------------------------------------------------------------- */
.support-cta {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--gap-lg);
@ -239,72 +187,35 @@
padding: clamp(1.5rem, 4vw, 3.5rem);
overflow: hidden;
background:
radial-gradient(circle at 92% 0%, rgba(255, 255, 255, 0.22), transparent 20rem),
var(--theme-accent-fill);
radial-gradient(circle at 86% 12%, rgba(255, 255, 255, 0.24), transparent 18rem),
linear-gradient(135deg, var(--theme-accent-fill), var(--theme-accent-fill-strong));
}
.support-bottom-copy .support-label,
.support-bottom-copy h2,
.support-bottom-copy p {
.support-cta .support-eyebrow,
.support-cta h2 {
color: var(--theme-accent-contrast);
}
.support-bottom-copy p {
max-width: 48rem;
margin-top: 1rem;
.support-cta-copy h2 {
max-width: 22ch;
margin: 0;
font-size: clamp(2rem, 5vw, 5rem);
line-height: 0.96;
font-weight: 300;
letter-spacing: 0;
text-transform: uppercase;
text-wrap: balance;
}
.support-bottom-actions {
display: flex;
flex-wrap: wrap;
gap: var(--gap-xs);
justify-content: flex-end;
}
.support-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 48px;
padding: 0 1.1rem;
border: 1px solid transparent;
border-radius: var(--radius-lg);
color: inherit;
font-size: var(--text-sm);
text-decoration: none;
transition:
transform var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out),
background-color var(--duration-med) var(--ease-out);
}
.support-btn:hover,
.support-btn:focus-visible {
transform: translateY(-2px);
box-shadow: var(--theme-shadow-soft);
}
.support-btn--primary {
background: #fff;
color: var(--theme-accent-fill-strong);
}
.support-btn--secondary {
border-color: rgba(255, 255, 255, 0.22);
background: rgba(255, 255, 255, 0.14);
color: #fff;
backdrop-filter: blur(8px);
}
/* ----- Responsive -------------------------------------------------------- */
@media (max-width: 1180px) {
.support-hero,
.support-section--split,
.support-info-grid,
.support-bottom-cta {
.support-cta {
grid-template-columns: 1fr;
}
.support-quick-grid,
.support-topics,
.support-faq-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@ -312,24 +223,15 @@
@media (max-width: 760px) {
.support-hero {
padding-top: clamp(1.4rem, 5vw, 2rem);
padding-top: clamp(5.5rem, 18vw, 7rem);
}
.support-hero-copy h1 {
font-size: clamp(2.55rem, 13vw, 4.4rem);
}
.support-quick-grid,
.support-topics,
.support-faq-grid {
grid-template-columns: 1fr;
}
.support-bottom-actions {
display: grid;
justify-content: stretch;
}
.support-btn {
width: 100%;
}
}

View File

@ -3,6 +3,43 @@ import SharedNavbar from "../components/SharedNavbar";
import PageMeta from "../components/seo/PageMeta";
import "./SupportPage.css";
const TOPICS = [
{
label: "Bestellung",
title: "Status, Anpassung, Ablauf",
text: "Fragen zu einer laufenden Bestellung beantworten wir schnell und direkt.",
},
{
label: "Versand",
title: "Lieferung & Zustellung",
text: "Versanddauer, Zustellung, Bestätigung oder mögliche Verzögerungen.",
},
{
label: "Beratung",
title: "Duft, Sample, Discovery",
text: "Unterstützung bei der Auswahl oder beim Einordnen eines Duftes.",
},
];
const FAQ = [
{
q: "Wie lange dauert der Versand?",
a: "Bestellungen werden in der Regel innerhalb von 12 Werktagen bearbeitet und zugestellt.",
},
{
q: "Kann ich zuerst testen?",
a: "Ja — über das Discovery Set oder ein einzelnes Sample. So erlebst du den Duft auf der Haut.",
},
{
q: "Welcher Duft passt zu mir?",
a: "Schreib uns kurz, welche Materialien oder Stimmungen dich ansprechen. Wir helfen bei der Einordnung.",
},
{
q: "Problem mit der Bestellung?",
a: "Schick uns die Bestellnummer und eine kurze Beschreibung. Wir prüfen den Fall und melden uns zeitnah.",
},
];
function SupportPage() {
return (
<div className="support-page">
@ -16,192 +53,79 @@ function SupportPage() {
<main id="main-content" className="shell">
<section className="support-hero" data-reveal-group>
<div className="support-hero-copy">
<span className="support-kicker" data-reveal="fade">
SUPPORT
<span className="support-eyebrow" data-reveal="fade">
Support
</span>
<h1 data-reveal="lines">
WIR HELFEN
<br />
KLAR, DIREKT
<br />
UND OHNE
<br />
UMWEGE
UND PERSÖNLICH.
</h1>
<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
zu bearbeiten.
</p>
</div>
<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
Fragen erreichst du uns direkt per E-Mail.
</p>
<div className="support-panel-meta">
<div>
<span>E-MAIL</span>
<p>support@atmos.ch</p>
</div>
<div>
<span>ANTWORTZEIT</span>
<p>In der Regel innerhalb von 12 Werktagen</p>
</div>
<div>
<span>SERVICE</span>
<p>Persönlich, präzise und sorgfältig</p>
</div>
</div>
</div>
</section>
<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>
Bei Fragen zu Bestellstatus, Anpassungen oder Unklarheiten zum Ablauf
hilft dir unser Support schnell und direkt weiter.
</p>
</article>
<article className="support-quick-card" data-reveal="fade">
<span className="support-label">VERSAND</span>
<h3>Lieferung und Zustellung</h3>
<p>
Wir unterstützen bei Fragen zu Versanddauer, Zustellung,
Lieferbestätigung oder allfälligen Verzögerungen.
</p>
</article>
<article className="support-quick-card" data-reveal="fade">
<span className="support-label">PRODUKTE</span>
<h3>Beratung zu Duft, Sample oder Discovery Set</h3>
<p>
Wenn du Unterstützung bei der Auswahl brauchst oder einen Duft besser
einordnen möchtest, helfen wir dir gerne weiter.
</p>
</article>
</section>
<section className="support-section support-section--split" data-reveal-group>
<div className="support-section-heading">
<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" 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
mitgibst. Dazu gehören je nach Fall deine Bestellnummer, der betroffene
Duft oder Artikel sowie eine kurze Beschreibung deines Anliegens.
</p>
<p>
Bei Fragen zur Auswahl genügt oft schon ein Hinweis darauf, welche
Düfte dich bisher angesprochen haben oder welche Richtung du suchst.
So können wir gezielter und hilfreicher antworten.
</p>
</div>
</section>
<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>
<li>Produkt- oder Duftname</li>
<li>Kurze Beschreibung des Anliegens</li>
<li>Bei Bedarf Fotos oder relevante Details</li>
</ul>
</div>
<div className="support-info-box support-info-box--dark" data-reveal="fade">
<span className="support-label">KONTAKT</span>
<h3>support@atmos.ch</h3>
<p>
Unser Support beantwortet Anfragen in der Regel innerhalb von 12
<p className="support-lead" data-reveal="fade">
Fragen zu Bestellung, Versand, Discovery Set oder einem Duft?
Unser Team antwortet sorgfältig meist innerhalb von 12
Werktagen.
</p>
<a href="mailto:support@atmos.ch" className="support-mail-btn">
Support kontaktieren
</a>
</div>
<aside className="support-contact-card" data-reveal="fade">
<span className="support-eyebrow">Kontakt</span>
<a className="support-mail-link" href="mailto:support@atmos.ch">
support@atmos.ch
</a>
<p>Antwortzeit 12 Werktage · persönlich, präzise.</p>
<a
href="mailto:support@atmos.ch"
className="atmos-btn atmos-btn--primary atmos-btn--block"
>
E-Mail schreiben
</a>
</aside>
</section>
<section className="support-faq-section" data-reveal-group>
<div className="support-section-heading">
<span className="support-label" data-reveal="fade">
HÄUFIGE FRAGEN
<section className="support-topics" data-reveal-group data-reveal-start="top 88%">
{TOPICS.map((topic) => (
<article className="support-topic" key={topic.label} data-reveal="fade">
<span className="support-eyebrow">{topic.label}</span>
<h3>{topic.title}</h3>
<p>{topic.text}</p>
</article>
))}
</section>
<section className="support-faq" data-reveal-group>
<header className="support-section-head">
<span className="support-eyebrow" data-reveal="fade">
Häufige Fragen
</span>
<h2 data-reveal="lines">DIE WICHTIGSTEN THEMEN AUF EINEN BLICK.</h2>
</div>
</header>
<div className="support-faq-grid">
<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
bearbeitet. Die Zustellung erfolgt üblicherweise wenige Werktage
danach.
</p>
</article>
<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.
So kannst du einen Duft erst auf der Haut erleben, bevor du dich
für eine Full Size entscheidest.
</p>
</article>
<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
dich ansprechen. Wir helfen dir gerne bei der Einordnung.
</p>
</article>
<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
Beschreibung des Anliegens. Wir prüfen den Fall sorgfältig und
melden uns so schnell wie möglich zurück.
</p>
</article>
{FAQ.map((item) => (
<article className="support-faq-item" key={item.q} data-reveal="fade">
<h3>{item.q}</h3>
<p>{item.a}</p>
</article>
))}
</div>
</section>
<section className="support-bottom-cta" data-reveal-group>
<div className="support-bottom-copy">
<span className="support-label" data-reveal="fade">
atmos SUPPORT
<section className="support-cta" data-reveal-group data-on-accent>
<div className="support-cta-copy">
<span className="support-eyebrow" data-reveal="fade">
atmos Support
</span>
<h2 data-reveal="lines">
NICHT AUTOMATISIERT AUF DISTANZ. SONDERN PERSÖNLICH UND PRÄZISE.
PERSÖNLICH STATT AUTOMATISIERT.
</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" data-reveal="fade">
<a href="mailto:support@atmos.ch" className="support-btn support-btn--primary">
<div className="atmos-btn-row atmos-btn-row--responsive" data-reveal="fade">
<a href="mailto:support@atmos.ch" className="atmos-btn atmos-btn--primary">
E-Mail senden
</a>
<Link to="/" className="support-btn support-btn--secondary">
<Link to="/" className="atmos-btn atmos-btn--secondary">
Zur Startseite
</Link>
</div>

View File

@ -0,0 +1,316 @@
/**
* Global Button System (atmos-btn)
* ----------------------------------------------------------------------------
* Single source of truth for every button & button-styled link in the app.
* Mirrors the visual language used by Landing Hero and Product Detail
* (hard edges, surface lift, accent stripe on hover).
*
* Usage:
* <button class="atmos-btn atmos-btn--primary"></button>
* <a class="atmos-btn atmos-btn--secondary"></a>
*
* Variants:
* --primary accent fill, contrast text (key CTA, works on any bg)
* --invert paper fill, accent-strong (CTA inside accent banners)
* --secondary surface w/ blur, themed text (secondary CTA on dark/photo)
* --outline transparent + strong border (utility CTA, theme-aware)
* --ghost transparent, subtle hover (tertiary, link-style)
* --solid neutral text-color fill (form submit / drawer primary)
* --danger destructive, theme-aware (remove / cancel actions)
*
* Sizes:
* default 48px min-height
* --sm 40px min-height
* --lg 56px min-height
*
* Modifiers:
* --block width: 100%
* --icon compact square (no text)
*
* GSAP-driven micro-interactions are bound globally by
* useButtonInteractions() (see src/hooks/useButtonInteractions.js).
* The CSS here keeps a graceful no-JS fallback (transitions only).
*/
.atmos-btn {
--btn-bg: transparent;
--btn-bg-hover: transparent;
--btn-fg: var(--theme-text);
--btn-fg-hover: var(--theme-text);
--btn-border: transparent;
--btn-border-hover: transparent;
--btn-stripe: var(--theme-accent);
--btn-shadow-hover: var(--theme-shadow-soft);
--btn-min-height: 48px;
--btn-px: clamp(1rem, 2vw, 1.35rem);
--btn-font: var(--text-sm);
position: relative;
isolation: isolate;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.55rem;
min-height: var(--btn-min-height);
padding: 0 var(--btn-px);
border: 1px solid var(--btn-border);
border-radius: var(--radius-lg);
background: var(--btn-bg);
color: var(--btn-fg);
font: inherit;
font-size: var(--btn-font);
line-height: 1;
letter-spacing: 0.04em;
text-decoration: none;
text-align: center;
white-space: nowrap;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
transition:
transform var(--duration-med) var(--ease-out),
background-color var(--duration-med) var(--ease-out),
border-color var(--duration-med) var(--ease-out),
color var(--duration-med) var(--ease-out),
box-shadow var(--duration-med) var(--ease-out),
opacity var(--duration-med) var(--ease-out);
}
/* Top accent stripe — same idiom as .size-card on the product page. */
.atmos-btn::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--btn-stripe);
transform: scaleX(0);
transform-origin: left center;
transition: transform var(--duration-med) var(--ease-out);
pointer-events: none;
z-index: 1;
}
.atmos-btn > * {
position: relative;
z-index: 2;
}
.atmos-btn:hover,
.atmos-btn:focus-visible {
background: var(--btn-bg-hover);
border-color: var(--btn-border-hover);
color: var(--btn-fg-hover);
box-shadow: var(--btn-shadow-hover);
transform: translateY(-2px);
}
.atmos-btn:hover::before,
.atmos-btn:focus-visible::before {
transform: scaleX(1);
}
.atmos-btn:active {
transform: translateY(0) scale(0.985);
transition-duration: var(--duration-fast);
}
.atmos-btn:focus-visible {
outline: 2px solid var(--theme-focus-ring);
outline-offset: 3px;
}
.atmos-btn:disabled,
.atmos-btn[aria-disabled="true"] {
opacity: 0.45;
cursor: not-allowed;
pointer-events: none;
transform: none;
box-shadow: none;
}
.atmos-btn:disabled::before,
.atmos-btn[aria-disabled="true"]::before {
transform: scaleX(0);
}
/* ----- Variants ---------------------------------------------------------- */
.atmos-btn--primary {
--btn-bg: var(--theme-accent-fill);
--btn-bg-hover: var(--theme-accent-fill-strong);
--btn-fg: var(--theme-accent-contrast);
--btn-fg-hover: var(--theme-accent-contrast);
--btn-border: var(--theme-accent-fill);
--btn-border-hover: var(--theme-accent-fill-strong);
--btn-stripe: rgba(255, 255, 255, 0.85);
}
.atmos-btn--invert {
--btn-bg: #fff;
--btn-bg-hover: #fff;
--btn-fg: var(--theme-accent-fill-strong);
--btn-fg-hover: var(--theme-accent-fill-strong);
--btn-border: #fff;
--btn-border-hover: #fff;
--btn-stripe: var(--theme-accent-fill-strong);
--btn-shadow-hover: 0 16px 36px rgba(0, 0, 0, 0.22);
}
.atmos-btn--secondary {
--btn-bg: color-mix(in srgb, var(--theme-surface) 72%, transparent);
--btn-bg-hover: color-mix(in srgb, var(--theme-surface) 86%, transparent);
--btn-fg: var(--theme-text);
--btn-fg-hover: var(--theme-text);
--btn-border: var(--theme-border-strong);
--btn-border-hover: rgba(var(--theme-accent-rgb) / 0.55);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.atmos-btn--outline {
--btn-bg: transparent;
--btn-bg-hover: rgba(var(--theme-accent-rgb) / 0.06);
--btn-fg: var(--theme-text);
--btn-fg-hover: var(--theme-text);
--btn-border: var(--theme-border);
--btn-border-hover: var(--theme-accent);
}
.atmos-btn--ghost {
--btn-bg: transparent;
--btn-bg-hover: rgba(var(--theme-accent-rgb) / 0.08);
--btn-fg: var(--theme-text);
--btn-fg-hover: var(--theme-text);
--btn-border: transparent;
--btn-border-hover: transparent;
--btn-shadow-hover: none;
}
.atmos-btn--solid {
--btn-bg: var(--theme-text);
--btn-bg-hover: var(--theme-text);
--btn-fg: var(--theme-bg);
--btn-fg-hover: var(--theme-bg);
--btn-border: var(--theme-text);
--btn-border-hover: var(--theme-text);
--btn-stripe: var(--theme-accent);
}
.atmos-btn--danger {
--btn-bg: transparent;
--btn-bg-hover: rgba(var(--theme-accent-rgb) / 0.1);
--btn-fg: var(--theme-text);
--btn-fg-hover: var(--theme-text);
--btn-border: var(--theme-border);
--btn-border-hover: var(--theme-accent);
--btn-stripe: var(--theme-accent);
--btn-shadow-hover: none;
}
/* On any accent-filled banner, primary/secondary need different defaults so
they remain readable. Apply by adding `data-on-accent` to a wrapper. */
[data-on-accent] .atmos-btn--primary,
.atmos-btn--primary[data-on-accent] {
--btn-bg: #fff;
--btn-bg-hover: #fff;
--btn-fg: var(--theme-accent-fill-strong);
--btn-fg-hover: var(--theme-accent-fill-strong);
--btn-border: #fff;
--btn-border-hover: #fff;
--btn-stripe: var(--theme-accent-fill-strong);
}
[data-on-accent] .atmos-btn--secondary,
.atmos-btn--secondary[data-on-accent] {
--btn-bg: rgba(255, 255, 255, 0.14);
--btn-bg-hover: rgba(255, 255, 255, 0.22);
--btn-fg: #fff;
--btn-fg-hover: #fff;
--btn-border: rgba(255, 255, 255, 0.32);
--btn-border-hover: #fff;
--btn-stripe: #fff;
}
[data-on-accent] .atmos-btn--ghost,
.atmos-btn--ghost[data-on-accent] {
--btn-fg: rgba(255, 255, 255, 0.86);
--btn-fg-hover: #fff;
--btn-bg-hover: rgba(255, 255, 255, 0.12);
}
/* ----- Sizes ------------------------------------------------------------- */
.atmos-btn--sm {
--btn-min-height: 40px;
--btn-px: clamp(0.7rem, 1.4vw, 0.95rem);
--btn-font: var(--text-xs);
}
.atmos-btn--lg {
--btn-min-height: 56px;
--btn-px: clamp(1.2rem, 2.6vw, 1.7rem);
--btn-font: var(--text-base);
}
/* ----- Modifiers --------------------------------------------------------- */
.atmos-btn--block {
width: 100%;
}
.atmos-btn--icon {
--btn-px: 0;
width: var(--btn-min-height);
min-width: var(--btn-min-height);
padding: 0;
gap: 0;
}
.atmos-btn--uppercase {
text-transform: uppercase;
letter-spacing: 0.12em;
}
/* Group helper — consistent spacing wherever buttons cluster. */
.atmos-btn-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--gap-xs);
}
.atmos-btn-row--end {
justify-content: flex-end;
}
.atmos-btn-row--stretch {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
gap: var(--gap-xs);
}
@media (max-width: 600px) {
.atmos-btn-row--responsive {
display: grid;
grid-template-columns: 1fr;
}
.atmos-btn-row--responsive > .atmos-btn {
width: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.atmos-btn,
.atmos-btn::before {
transition: none;
}
.atmos-btn:hover,
.atmos-btn:focus-visible {
transform: none;
}
}