import { useEffect, useRef, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { api } from '../../api/client.js'; import usePageMeta from '../../hooks/usePageMeta.js'; import MaintenanceNotice from '../../components/MaintenanceNotice.jsx'; import MaintenanceBypassBanner from '../../components/MaintenanceBypassBanner.jsx'; import DynamicIcon from '../../components/DynamicIcon.jsx'; import { useMaintenance } from '../../context/MaintenanceContext.jsx'; import { useLanguage } from '../../context/LanguageContext.jsx'; import { useSiteSettings } from '../../context/SiteSettingsContext.jsx'; import roadGlowTexture from '../../assets/textures/road-glow.webp'; // Die 8 fertigen "Straßen"-Glow-Bilder aus dem Kunden-Referenzdesign (Kacheln.html) - // je Preisstufe ein eigenes Bild (Grau -> Petrol -> Gruen -> Gold). Wurden 1:1 aus der // Referenzdatei extrahiert und nur PNG->WebP optimiert (Motiv unveraendert). import roadBase from '../../assets/roads/base.webp'; import roadClear from '../../assets/roads/clear.webp'; import roadDrive from '../../assets/roads/drive.webp'; import roadPrime from '../../assets/roads/prime.webp'; import roadEliteR from '../../assets/roads/elite_r.webp'; import roadApex from '../../assets/roads/apex.webp'; import roadStatement from '../../assets/roads/statement.webp'; import roadLimitless from '../../assets/roads/limitless.webp'; const hslToRgb = (h, s, l) => { s /= 100; l /= 100; const k = (n) => (n + h / 30) % 12; const a = s * Math.min(l, 1 - l); const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)]; }; const hslCss = (h, s, l) => `rgb(${hslToRgb(h, s, l).join(', ')})`; // Baut aus [bgH,bgS,bgL, acH,acS,acL]-Zeilen eine Stufenliste ohne eigenes Straßenbild // (fuer die beiden nicht im Referenzdesign enthaltenen Themes) - Rahmenfarbe = Flaeche // etwas heller, wie im Referenzdesign. const gen = (rows) => rows.map(([bh, bs, bl, ah, as, al]) => ({ bgTop: hslCss(bh, bs, bl), border: hslCss(bh, bs, bl + 8), accent: hslCss(ah, as, al), img: null, })); // "graphite" = exakt die 8 Referenz-Kacheln (Farben + Straßenbild) aus Kacheln.html: // Base / Clear / Drive / Prime / Elite / Apex / The Statement / Limitless. Jede Stufe // hat eine Flaechenfarbe oben im Verlauf (bgTop), eine Rahmenfarbe (border) und eine // Akzentfarbe (Name + Trennlinie). Welche Stufe ein Paket bekommt, ergibt sich aus seinem // Preis-Rang (siehe tierOf). Die beiden anderen Themes sind eigene Farbfamilien im selben // Aufbau, nutzen aber (mangels eigener Referenzbilder) das neutrale, umfaerbbare Glow-Motiv. const PACKAGE_THEMES = { graphite: [ { accent: '#b6b6b6', border: '#2a2a2a', bgTop: '#1c1c1c', img: roadBase }, { accent: '#d3d3d3', border: '#2e2e2e', bgTop: '#212121', img: roadClear }, { accent: '#8fb8bd', border: '#26383b', bgTop: '#16282b', img: roadDrive }, { accent: '#6fbf73', border: '#1f3a24', bgTop: '#122916', img: roadPrime }, { accent: '#63d34f', border: '#204a1c', bgTop: '#123312', img: roadEliteR }, { accent: '#8bea3c', border: '#2b5518', bgTop: '#173a0f', img: roadApex }, { accent: '#d9a83c', border: '#4a3a16', bgTop: '#33270d', img: roadStatement }, { accent: '#f2c14e', border: '#5a4718', bgTop: '#3d2f0c', img: roadLimitless }, ], 'deep-blue': gen([ [215, 18, 11, 210, 15, 60], [212, 25, 11, 208, 32, 60], [210, 38, 12, 206, 46, 60], [212, 46, 12, 205, 56, 61], [214, 52, 12, 205, 63, 62], [216, 58, 12, 205, 68, 63], [220, 62, 12, 212, 73, 65], [226, 66, 12, 216, 78, 68], ]), 'warm-bronze': gen([ [30, 15, 11, 34, 22, 60], [34, 32, 11, 34, 46, 60], [32, 46, 12, 30, 58, 60], [26, 52, 12, 24, 65, 60], [20, 55, 12, 18, 68, 60], [14, 55, 12, 12, 68, 61], [8, 52, 12, 6, 64, 62], [2, 50, 12, 358, 60, 63], ]), }; // Referenz-Werte fuer die Preis-Sektion (unabhaengig von der Preisstufe, wie im Design): const PRICE_COLOR = '#f2f2f2'; const LABEL_COLOR = '#8a8a8a'; const BULLET_COLOR = '#c4c4c4'; // Mehr sichtbare Stichpunkte wuerden das Straßenbild verdraengen - gerade bei den teuren // Paketen (die laut Kunde die laengsten Listen haben) soll die Kachel ruhig bleiben. Der // Rest steckt hinter einem "+X weitere Leistungen"-Toggle, dessen Zaehler selbst als // Wertigkeitssignal wirkt. const VISIBLE_BULLETS = 5; function PackageCard({ pkg, tier, tierT, layout, bullets, formatPrice, contactUrl, t }) { const [expanded, setExpanded] = useState(false); const hiddenCount = bullets.length - VISIBLE_BULLETS; const shownBullets = expanded ? bullets : bullets.slice(0, VISIBLE_BULLETS); // Fuer die Themes ohne eigenes Straßenbild: neutrales Glow-Motiv, per Maske // in die Akzentfarbe eingefaerbt (zwei Ebenen: weicher Halo + scharfe Kontur). const glowMask = tier.img ? null : { backgroundColor: tier.accent, WebkitMaskImage: `url(${roadGlowTexture})`, maskImage: `url(${roadGlowTexture})`, maskMode: 'luminance', WebkitMaskSize: 'cover', maskSize: 'cover', WebkitMaskPosition: 'center', maskPosition: 'center', WebkitMaskRepeat: 'no-repeat', maskRepeat: 'no-repeat', }; return (
{/* Vollflaechiges Straßen-Glow-Bild der Preisstufe (Referenzdesign). */} {tier.img ? ( ) : ( <>