diff --git a/parfum-shop/public/blasse-seide-visual-phase-1.webp b/parfum-shop/public/blasse-seide-visual-phase-1.webp new file mode 100644 index 0000000..cb91e28 Binary files /dev/null and b/parfum-shop/public/blasse-seide-visual-phase-1.webp differ diff --git a/parfum-shop/public/blasse-seide-visual-phase-2.webp b/parfum-shop/public/blasse-seide-visual-phase-2.webp new file mode 100644 index 0000000..3131624 Binary files /dev/null and b/parfum-shop/public/blasse-seide-visual-phase-2.webp differ diff --git a/parfum-shop/public/blasse-seide-visual-phase-3.webp b/parfum-shop/public/blasse-seide-visual-phase-3.webp new file mode 100644 index 0000000..c1a096b Binary files /dev/null and b/parfum-shop/public/blasse-seide-visual-phase-3.webp differ diff --git a/parfum-shop/public/kalter-beton-visual-phase-1.webp b/parfum-shop/public/kalter-beton-visual-phase-1.webp new file mode 100644 index 0000000..5cb54e4 Binary files /dev/null and b/parfum-shop/public/kalter-beton-visual-phase-1.webp differ diff --git a/parfum-shop/public/kalter-beton-visual-phase-2.webp b/parfum-shop/public/kalter-beton-visual-phase-2.webp new file mode 100644 index 0000000..8aca504 Binary files /dev/null and b/parfum-shop/public/kalter-beton-visual-phase-2.webp differ diff --git a/parfum-shop/public/kalter-beton-visual-phase-3.webp b/parfum-shop/public/kalter-beton-visual-phase-3.webp new file mode 100644 index 0000000..53dc26b Binary files /dev/null and b/parfum-shop/public/kalter-beton-visual-phase-3.webp differ diff --git a/parfum-shop/public/nasser-marmor-visual-phase-1.webp b/parfum-shop/public/nasser-marmor-visual-phase-1.webp new file mode 100644 index 0000000..7b13858 Binary files /dev/null and b/parfum-shop/public/nasser-marmor-visual-phase-1.webp differ diff --git a/parfum-shop/public/nasser-marmor-visual-phase-2.webp b/parfum-shop/public/nasser-marmor-visual-phase-2.webp new file mode 100644 index 0000000..6b89215 Binary files /dev/null and b/parfum-shop/public/nasser-marmor-visual-phase-2.webp differ diff --git a/parfum-shop/public/nasser-marmor-visual-phase-3.webp b/parfum-shop/public/nasser-marmor-visual-phase-3.webp new file mode 100644 index 0000000..6401a09 Binary files /dev/null and b/parfum-shop/public/nasser-marmor-visual-phase-3.webp differ diff --git a/parfum-shop/public/schwarzes-benzin-visual-phase-1.webp b/parfum-shop/public/schwarzes-benzin-visual-phase-1.webp new file mode 100644 index 0000000..c14ed72 Binary files /dev/null and b/parfum-shop/public/schwarzes-benzin-visual-phase-1.webp differ diff --git a/parfum-shop/public/schwarzes-benzin-visual-phase-2.webp b/parfum-shop/public/schwarzes-benzin-visual-phase-2.webp new file mode 100644 index 0000000..2e1fe04 Binary files /dev/null and b/parfum-shop/public/schwarzes-benzin-visual-phase-2.webp differ diff --git a/parfum-shop/public/schwarzes-benzin-visual-phase-3.webp b/parfum-shop/public/schwarzes-benzin-visual-phase-3.webp new file mode 100644 index 0000000..542c447 Binary files /dev/null and b/parfum-shop/public/schwarzes-benzin-visual-phase-3.webp differ diff --git a/parfum-shop/public/verbranntes-chrom-visual-phase-1.webp b/parfum-shop/public/verbranntes-chrom-visual-phase-1.webp new file mode 100644 index 0000000..be8101b Binary files /dev/null and b/parfum-shop/public/verbranntes-chrom-visual-phase-1.webp differ diff --git a/parfum-shop/public/verbranntes-chrom-visual-phase-2.webp b/parfum-shop/public/verbranntes-chrom-visual-phase-2.webp new file mode 100644 index 0000000..d821a36 Binary files /dev/null and b/parfum-shop/public/verbranntes-chrom-visual-phase-2.webp differ diff --git a/parfum-shop/public/verbranntes-chrom-visual-phase-3.webp b/parfum-shop/public/verbranntes-chrom-visual-phase-3.webp new file mode 100644 index 0000000..9d6cc0b Binary files /dev/null and b/parfum-shop/public/verbranntes-chrom-visual-phase-3.webp differ diff --git a/parfum-shop/public/weisse-asche-visual-phase-1.webp b/parfum-shop/public/weisse-asche-visual-phase-1.webp new file mode 100644 index 0000000..4fffacc Binary files /dev/null and b/parfum-shop/public/weisse-asche-visual-phase-1.webp differ diff --git a/parfum-shop/public/weisse-asche-visual-phase-2.webp b/parfum-shop/public/weisse-asche-visual-phase-2.webp new file mode 100644 index 0000000..46d39c2 Binary files /dev/null and b/parfum-shop/public/weisse-asche-visual-phase-2.webp differ diff --git a/parfum-shop/public/weisse-asche-visual-phase-3.webp b/parfum-shop/public/weisse-asche-visual-phase-3.webp new file mode 100644 index 0000000..b028e68 Binary files /dev/null and b/parfum-shop/public/weisse-asche-visual-phase-3.webp differ diff --git a/parfum-shop/src/App.jsx b/parfum-shop/src/App.jsx index 575a3a0..efb750b 100644 --- a/parfum-shop/src/App.jsx +++ b/parfum-shop/src/App.jsx @@ -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; diff --git a/parfum-shop/src/components/ProductDetailPage.css b/parfum-shop/src/components/ProductDetailPage.css index c0bbeb8..89352da 100644 --- a/parfum-shop/src/components/ProductDetailPage.css +++ b/parfum-shop/src/components/ProductDetailPage.css @@ -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); } diff --git a/parfum-shop/src/components/ProductDetailPage.jsx b/parfum-shop/src/components/ProductDetailPage.jsx index 12bdd50..e5794cb 100644 --- a/parfum-shop/src/components/ProductDetailPage.jsx +++ b/parfum-shop/src/components/ProductDetailPage.jsx @@ -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 }) {
{phaseGroups.map((phase, index) => ( -
+
0{index + 1}

{phase.label}

diff --git a/parfum-shop/src/data/perfumes.js b/parfum-shop/src/data/perfumes.js index 68cb81e..4c93cd3 100644 --- a/parfum-shop/src/data/perfumes.js +++ b/parfum-shop/src/data/perfumes.js @@ -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: "2–3 Sprühstösse", longevity: "8–12 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: "2–3 Sprühstösse", longevity: "7–11 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: "3–4 Sprühstösse", longevity: "6–10 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: "2–3 Sprühstösse", longevity: "8–12 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: "1–2 Sprühstösse", longevity: "10–14 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: "1–2 Sprühstösse", longevity: "9–13 Stunden", diff --git a/parfum-shop/src/hooks/useButtonInteractions.js b/parfum-shop/src/hooks/useButtonInteractions.js new file mode 100644 index 0000000..08ab0a7 --- /dev/null +++ b/parfum-shop/src/hooks/useButtonInteractions.js @@ -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; diff --git a/parfum-shop/src/index.css b/parfum-shop/src/index.css index d4e9f7f..9a2e0f9 100644 --- a/parfum-shop/src/index.css +++ b/parfum-shop/src/index.css @@ -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"; diff --git a/parfum-shop/src/pages/AboutPage.css b/parfum-shop/src/pages/AboutPage.css index 7560526..49d8135 100644 --- a/parfum-shop/src/pages/AboutPage.css +++ b/parfum-shop/src/pages/AboutPage.css @@ -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; } } diff --git a/parfum-shop/src/pages/AboutPage.jsx b/parfum-shop/src/pages/AboutPage.jsx index e3ec356..c4fb5ac 100644 --- a/parfum-shop/src/pages/AboutPage.jsx +++ b/parfum-shop/src/pages/AboutPage.jsx @@ -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() {
-
- - ABOUT atmos - -

- WIR DEKODIEREN -
- ATMOSPHÄREN -
- UND WANDELN -
- SIE IN DÜFTE -

-

- 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. -

-
- -
- POSITIONIERUNG -

- Nischig. Hochwertig. Edel. Luxuriös. Reduziert in der Form, - konsequent in der Wirkung. -

- -
-
- HERKUNFT -

Made in Switzerland

-
-
- ANSATZ -

Atmosphäre vor Mainstream

-
-
- FOKUS -

Duft als Konzeptobjekt

-
-
-
+ + ABOUT atmos + +

+ WIR DEKODIEREN +
+ ATMOSPHÄREN. +

+

+ atmos entwickelt Nischendüfte, die eine Atmosphäre präzise + übersetzen — Material, Raum, Licht, Spannung. Reduziert in der + Form, konsequent in der Wirkung. +

-
-
- - UNSER VERSTÄNDNIS - -

NICHT DEKORATION. SONDERN HALTUNG.

-
- -
-

- 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. -

- -

- 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. -

-
-
- - - {PROOF_ITEMS.map((item) => ( - - {item.label} -

{item.text}

- - ))} -
- -
+

„Jeder Duft beginnt bei uns nicht mit einer Note, sondern mit einem Raumgefühl.“

- - {APPROACH_CARDS.map((card) => ( - - {card.label} -

{card.title}

-

{card.text}

- - ))} -
- -
-
- - PROZESS & KOMPETENZ +
+
+ + Unser Ansatz -

- WIE AUS EINER IDEE EINE BELASTBARE KOMPOSITION WIRD. -

-
+

DREI SCHRITTE. EINE HALTUNG.

+ -
-

- 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? -

- -

- 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. -

+
+ {PILLARS.map((pillar) => ( +
+ {pillar.label} +

{pillar.title}

+

{pillar.text}

+
+ ))}
- - {CREDENTIAL_CARDS.map((card) => ( - - {card.label} -

{card.title}

-

{card.text}

- - ))} -
- -
-
- - NACHWEISBARE DUFTLOGIK - -

- FÜR MENSCHEN, DIE HYPE ERKENNEN UND SUBSTANZ SUCHEN. -

-

- 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. -

-
- -
-
- DUFTSTRUKTUR -

Top, Heart und Base werden als Verlauf und nicht nur als Liste gedacht.

-
-
- MATERIALBEZUG -

Jeder Duft wird über Atmosphäre, Oberfläche und sensorische Spannung verankert.

-
-
- EDITIONEN -

Chargen und Editionen machen Entwicklung und Kontext nachvollziehbarer.

-
-
- TRAGBARKEIT -

Nische bedeutet bei uns Charakter – nicht Beliebigkeit oder reine Provokation.

-
-
-
- -
+
- - MADE IN SWITZERLAND + + Made in Switzerland -

PRÄZISION IN FORM, DUFT UND AUFTRITT.

+

PRÄZISION IN FORM UND DUFT.

- 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. -

-

- 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.

-
-
- QUALITÄTSVERSPRECHEN -

Kuratiert, präzise und mit Fokus auf charakterstarke Kompositionen.

-
-
- DESIGNSPRACHE -

Reduziert, architektonisch und materialorientiert.

-
-
- ZIEL -

Düfte schaffen, die nicht austauschbar sind, sondern erinnerbar.

-
-
+
    +
  • + Herkunft +

    Made in Switzerland

    +
  • +
  • + Ansatz +

    Atmosphäre vor Mainstream

    +
  • +
  • + Fokus +

    Duft als Konzeptobjekt

    +
  • +
-
- - QUALITÄTSVERSTÄNDNIS - -

- 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. -

-
- -
-
- +
+
+ atmos

- FÜR MENSCHEN, DIE NICHT NUR EINEN DUFT SUCHEN, SONDERN EINE HALTUNG. + FÜR MENSCHEN, DIE SUBSTANZ SUCHEN.

-

- Entdecke Düfte, die Atmosphäre nicht illustrieren, sondern in eine - tragbare Form übersetzen. -

-
- - Zur Startseite - - +
+ Düfte entdecken + + Discovery Set +
diff --git a/parfum-shop/src/pages/DatenschutzPage.css b/parfum-shop/src/pages/DatenschutzPage.css index 3076db0..3b77fe8 100644 --- a/parfum-shop/src/pages/DatenschutzPage.css +++ b/parfum-shop/src/pages/DatenschutzPage.css @@ -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. */ } diff --git a/parfum-shop/src/pages/DatenschutzPage.jsx b/parfum-shop/src/pages/DatenschutzPage.jsx index ee33285..8134cfd 100644 --- a/parfum-shop/src/pages/DatenschutzPage.jsx +++ b/parfum-shop/src/pages/DatenschutzPage.jsx @@ -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 ( -
- - + return ( +
+ + -
-
- RECHTLICHE ANGABEN -

DATENSCHUTZ

-

- 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. -

-
+
+
+ + Rechtliche Angaben + +

DATENSCHUTZ

+

+ 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. +

+
-
-
- VERANTWORTLICHE STELLE -

WER FÜR DIE DATENVERARBEITUNG VERANTWORTLICH IST

-
+ {SECTIONS.map((section) => ( +
+
+ + {section.label} + +

{section.title}

+
-
-

- Verantwortlich für die Datenverarbeitung im Zusammenhang mit dieser - Website ist: -

-

- atmos GmbH -
- Musterstrasse 12 -
- 7000 Chur -
- Schweiz -

-

- E-Mail: hello@atmos.ch -
- Telefon: +41 00 000 00 00 -

-
-
- -
-
- ALLGEMEINES -

WELCHE DATEN ERHOBEN WERDEN

-
- -
-

- 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. -

-

- 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. -

-
-
- -
-
- ZWECK -

WOFÜR DIE DATEN VERWENDET WERDEN

-
- -
-

Die Verarbeitung personenbezogener Daten erfolgt insbesondere:

-
    -
  • zur Bereitstellung und technischen Optimierung der Website,
  • -
  • zur Bearbeitung von Anfragen und Supportfällen,
  • -
  • zur Abwicklung von Bestellungen und Vertragsverhältnissen,
  • -
  • zur Kommunikation mit Kundinnen und Kunden,
  • -
  • zur Sicherheit und Missbrauchsprävention,
  • -
  • zur Erfüllung gesetzlicher Verpflichtungen.
  • -
-
-
- -
-
- COOKIES & TRACKING -

COOKIES UND ÄHNLICHE TECHNOLOGIEN

-
- -
-

- 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. -

-

- 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. -

-
-
- -
-
- WEITERGABE -

WEITERGABE AN DRITTE

-
- -
-

- 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. -

-

- 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. -

-
-
- -
-
- SPEICHERDAUER -

WIE LANGE DATEN GESPEICHERT WERDEN

-
- -
-

- 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. -

-
-
- -
-
- RECHTE -

RECHTE DER BETROFFENEN PERSONEN

-
- -
-

- 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. -

-

- Anfragen hierzu können an die oben genannte verantwortliche Stelle - gerichtet werden. -

-
-
- -
-
- SICHERHEIT -

TECHNISCHE UND ORGANISATORISCHE MASSNAHMEN

-
- -
-

- Wir treffen angemessene technische und organisatorische - Sicherheitsmassnahmen, um personenbezogene Daten vor unbefugtem - Zugriff, Verlust, Missbrauch oder Manipulation zu schützen. -

-

- Bitte beachte jedoch, dass die Datenübertragung im Internet trotz - aller Sorgfalt Sicherheitslücken aufweisen kann. -

-
-
- -
-
- AKTUALISIERUNG -

ÄNDERUNGEN DIESER DATENSCHUTZERKLÄRUNG

-
- -
-

- 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. -

-
-
-
-
- ); +
+ {section.body.map((paragraph) => ( +

{paragraph}

+ ))} + {section.list && ( +
    + {section.list.map((item) => ( +
  • {item}
  • + ))} +
+ )} +
+ + ))} + +
+ ); } export default DatenschutzPage; diff --git a/parfum-shop/src/pages/ImpressumPage.css b/parfum-shop/src/pages/ImpressumPage.css index 3fa3921..f62c84e 100644 --- a/parfum-shop/src/pages/ImpressumPage.css +++ b/parfum-shop/src/pages/ImpressumPage.css @@ -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; } } diff --git a/parfum-shop/src/pages/ImpressumPage.jsx b/parfum-shop/src/pages/ImpressumPage.jsx index 6bc4ba2..182dc92 100644 --- a/parfum-shop/src/pages/ImpressumPage.jsx +++ b/parfum-shop/src/pages/ImpressumPage.jsx @@ -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 +
+ Musterstrasse 12 +
+ 7000 Chur, Schweiz + + ), + }, + { + label: "Kontakt", + title: "Erreichbarkeit", + body: ( + <> + hello@atmos.ch +
+ +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 +
+ 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 ( -
+
-
- - RECHTLICHE ANGABEN +
+ + Rechtliche Angaben

IMPRESSUM

-

- Dieses Impressum enthält die rechtlichen Angaben zu atmos sowie - Informationen zur Verantwortlichkeit, Erreichbarkeit und zu den - veröffentlichten Inhalten dieser Website. +

+ Rechtliche Angaben zu atmos sowie Informationen zur Verantwortlichkeit, + Erreichbarkeit und zu den veröffentlichten Inhalten dieser Website.

-
-
- ANBIETER -

Unternehmen

-

- atmos GmbH -
- Musterstrasse 12 -
- 7000 Chur -
- Schweiz -

-
- -
- KONTAKT -

Erreichbarkeit

-

- E-Mail: hello@atmos.ch -
- Telefon: +41 00 000 00 00 -

-
- -
- VERTRETUNGSBERECHTIGT -

Geschäftsführung

-

- Amanda Nielsen, Ermin Zoronjic, Tobias Högger, Salih Hasicic -
- Geschäftsführer*innen -

-
- -
- HANDELSREGISTER -

Registereintrag

-

- Handelsregisteramt: Kanton Graubünden -
- Unternehmens-Identifikationsnummer: CHE-000.000.000 -

-
+
+ {FACTS.map((fact) => ( +
+ {fact.label} +

{fact.title}

+

{fact.body}

+
+ ))}
-
-
- - INHALTLICHE VERANTWORTUNG - -

VERANTWORTLICH FÜR DEN INHALT

-
- -
-

- Verantwortlich für diese Website und die publizierten Inhalte: -

-

- atmos GmbH -
- Musterstrasse 12 -
- 7000 Chur -
- Schweiz -

-
-
- -
-
- - HAFTUNG - -

HAFTUNGSAUSSCHLUSS

-
- -
-

- 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. -

-
-
- -
-
- - URHEBERRECHT - -

URHEBER- UND MARKENRECHTE

-
- -
-

- 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. -

-
-
- -
-
- - TRANSPARENZ - -

KLARE ANGABEN UND ERREICHBARKEIT

-
- -
-

- 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. -

-
-
+ {SECTIONS.map((section) => ( +
+
+ + {section.label} + +

{section.title}

+
+
+

{section.body}

+
+
+ ))}
); diff --git a/parfum-shop/src/pages/SmallBatchPage.css b/parfum-shop/src/pages/SmallBatchPage.css index 96babe8..9983e3d 100644 --- a/parfum-shop/src/pages/SmallBatchPage.css +++ b/parfum-shop/src/pages/SmallBatchPage.css @@ -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%; - } } diff --git a/parfum-shop/src/pages/SmallBatchPage.jsx b/parfum-shop/src/pages/SmallBatchPage.jsx index 2851865..3e00a6c 100644 --- a/parfum-shop/src/pages/SmallBatchPage.jsx +++ b/parfum-shop/src/pages/SmallBatchPage.jsx @@ -7,9 +7,9 @@ import "./SmallBatchPage.css"; function Requirement({ label, met, children }) { return ( -
- {label} - {children || (met ? "met" : "open")} +
+ {label} + {children || (met ? "Erfüllt" : "Offen")}
); } @@ -70,57 +70,89 @@ function SmallBatchPage() {
- - SMALL BATCH / ARCHIVE / PROTOTYPE + + Small Batch · Archive · Prototype -

EARLY ACCESS

-

- Limited releases are reserved for customers with enough purchase - history to understand the atmos material language. +

EARLY ACCESS.

+

+ Limitierte Releases sind Kund:innen vorbehalten, die genug + Berührung mit der atmos Materialwelt hatten — kuratiert, + chargenbasiert, in kleiner Auflage.

{!user ? (
- - LOGIN REQUIRED + + Login erforderlich -

Sign in to check access.

-

Small Batch access is calculated from your completed orders.

- +

Melde dich an, um deinen Zugang zu prüfen.

+

+ Small Batch Access wird aus deinen abgeschlossenen Bestellungen + berechnet. +

+
+ +
) : ( <>
- - ACCESS STATUS - -

{loyalty.unlocked ? "Unlocked" : "Locked"}

+
+
+ + Access Status + +

+ {loyalty.unlocked ? "Freigeschaltet" : "Noch gesperrt"} +

+
+ + {loyalty.unlocked ? "Unlocked" : "Locked"} + +
+
- = 3}> - {loyalty.purchases}/3 Purchases + = 3}> + {loyalty.purchases}/3 - 50000}> + 50000}> {formatChf(loyalty.spent_cents)} / CHF 500+
- {state.error &&

{state.error}

} - {state.loading &&

Loading access...

} + {state.error &&

{state.error}

} + {state.loading &&

Loading access…

} - {loyalty.unlocked && ( -
+ {loyalty.unlocked && state.releases.length > 0 && ( +
{state.releases.map((release) => (
- {release.type} + {release.type}

{release.name}

{release.note}

- +
))}
diff --git a/parfum-shop/src/pages/SupportPage.css b/parfum-shop/src/pages/SupportPage.css index 7649bda..6395a07 100644 --- a/parfum-shop/src/pages/SupportPage.css +++ b/parfum-shop/src/pages/SupportPage.css @@ -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%; - } } diff --git a/parfum-shop/src/pages/SupportPage.jsx b/parfum-shop/src/pages/SupportPage.jsx index c37e57a..30d66af 100644 --- a/parfum-shop/src/pages/SupportPage.jsx +++ b/parfum-shop/src/pages/SupportPage.jsx @@ -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 1–2 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 (
@@ -16,192 +53,79 @@ function SupportPage() {
- - SUPPORT + + Support

- WIR HELFEN -
KLAR, DIREKT
- UND OHNE -
- UMWEGE + UND PERSÖNLICH.

-

- 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. -

-
- -
- KONTAKT -

- Für Anliegen rund um Bestellung, Produkte, Versand und allgemeine - Fragen erreichst du uns direkt per E-Mail. -

- -
-
- E-MAIL -

support@atmos.ch

-
-
- ANTWORTZEIT -

In der Regel innerhalb von 1–2 Werktagen

-
-
- SERVICE -

Persönlich, präzise und sorgfältig

-
-
-
-
- -
-
- BESTELLUNG -

Fragen zu einer laufenden Bestellung

-

- Bei Fragen zu Bestellstatus, Anpassungen oder Unklarheiten zum Ablauf - hilft dir unser Support schnell und direkt weiter. -

-
- -
- VERSAND -

Lieferung und Zustellung

-

- Wir unterstützen bei Fragen zu Versanddauer, Zustellung, - Lieferbestätigung oder allfälligen Verzögerungen. -

-
- -
- PRODUKTE -

Beratung zu Duft, Sample oder Discovery Set

-

- Wenn du Unterstützung bei der Auswahl brauchst oder einen Duft besser - einordnen möchtest, helfen wir dir gerne weiter. -

-
-
- -
-
- - SO ERREICHST DU UNS - -

EIN GUTER SUPPORT BEGINNT MIT EINER KLAREN ANFRAGE.

-
- -
-

- 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. -

- -

- 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. -

-
-
- -
-
- AM BESTEN MITGEBEN -
    -
  • Bestellnummer, falls bereits bestellt wurde
  • -
  • Produkt- oder Duftname
  • -
  • Kurze Beschreibung des Anliegens
  • -
  • Bei Bedarf Fotos oder relevante Details
  • -
-
- -
- KONTAKT -

support@atmos.ch

-

- Unser Support beantwortet Anfragen in der Regel innerhalb von 1–2 +

+ Fragen zu Bestellung, Versand, Discovery Set oder einem Duft? + Unser Team antwortet sorgfältig — meist innerhalb von 1–2 Werktagen.

- - Support kontaktieren -
+ +
-
-
- - HÄUFIGE FRAGEN +
+ {TOPICS.map((topic) => ( +
+ {topic.label} +

{topic.title}

+

{topic.text}

+
+ ))} +
+ +
+
+ + Häufige Fragen

DIE WICHTIGSTEN THEMEN AUF EINEN BLICK.

-
+
-
-

Wie lange dauert der Versand?

-

- Bestellungen werden in der Regel innerhalb von 1–2 Werktagen - bearbeitet. Die Zustellung erfolgt üblicherweise wenige Werktage - danach. -

-
- -
-

Kann ich zuerst testen?

-

- 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. -

-
- -
-

Ich bin unsicher, welcher Duft zu mir passt.

-

- Schreib uns kurz, welche Duftcharaktere, Materialien oder Stimmungen - dich ansprechen. Wir helfen dir gerne bei der Einordnung. -

-
- -
-

Was tun bei einem Problem mit der Bestellung?

-

- 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. -

-
+ {FAQ.map((item) => ( +
+

{item.q}

+

{item.a}

+
+ ))}
-
-
- - atmos SUPPORT +
+
+ + atmos Support

- NICHT AUTOMATISIERT AUF DISTANZ. SONDERN PERSÖNLICH UND PRÄZISE. + PERSÖNLICH STATT AUTOMATISIERT.

-

- Wir möchten, dass sich auch der Service so anfühlt wie die Marke - selbst: klar, hochwertig und sorgfältig. -

-
- +
+ E-Mail senden - + Zur Startseite
diff --git a/parfum-shop/src/style/buttons.css b/parfum-shop/src/style/buttons.css new file mode 100644 index 0000000..18a42fb --- /dev/null +++ b/parfum-shop/src/style/buttons.css @@ -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: + * + * + * + * 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; + } +}