diff --git a/hifi-src/src/assets/textures/metal-deep-blue.webp b/hifi-src/src/assets/textures/metal-deep-blue.webp new file mode 100644 index 0000000..e6dc617 Binary files /dev/null and b/hifi-src/src/assets/textures/metal-deep-blue.webp differ diff --git a/hifi-src/src/assets/textures/metal-graphite.webp b/hifi-src/src/assets/textures/metal-graphite.webp new file mode 100644 index 0000000..6ca4c6b Binary files /dev/null and b/hifi-src/src/assets/textures/metal-graphite.webp differ diff --git a/hifi-src/src/assets/textures/metal-warm-bronze.webp b/hifi-src/src/assets/textures/metal-warm-bronze.webp new file mode 100644 index 0000000..74227d6 Binary files /dev/null and b/hifi-src/src/assets/textures/metal-warm-bronze.webp differ diff --git a/hifi-src/src/i18n/de.js b/hifi-src/src/i18n/de.js index ca1660d..fed1070 100644 --- a/hifi-src/src/i18n/de.js +++ b/hifi-src/src/i18n/de.js @@ -155,7 +155,6 @@ export default { productLoading: 'Produkt wird geladen…', requestContact: 'Kontakt anfragen', featuredBadge: 'Empfohlen', - tierLabels: ['Einstieg', 'Upgrade', 'Performance', 'Premium', 'High-End', 'Signature', 'Referenz'], tiersHeading: (count) => `${count} Stufen. Ein Ziel: perfekter Klang.`, tiersSubheading: 'Von der soliden Basis bis zum kompromisslosen Meisterwerk – finde dein Paket.', }, diff --git a/hifi-src/src/i18n/en.js b/hifi-src/src/i18n/en.js index 08eaf48..be4fcea 100644 --- a/hifi-src/src/i18n/en.js +++ b/hifi-src/src/i18n/en.js @@ -155,7 +155,6 @@ export default { productLoading: 'Loading product…', requestContact: 'Request contact', featuredBadge: 'Recommended', - tierLabels: ['Entry', 'Upgrade', 'Performance', 'Premium', 'High-End', 'Signature', 'Reference'], tiersHeading: (count) => `${count} tiers. One goal: perfect sound.`, tiersSubheading: 'From a solid foundation to an uncompromising masterpiece – find your package.', }, diff --git a/hifi-src/src/pages/public/ModelPage.jsx b/hifi-src/src/pages/public/ModelPage.jsx index 51011d7..e0e6baa 100644 --- a/hifi-src/src/pages/public/ModelPage.jsx +++ b/hifi-src/src/pages/public/ModelPage.jsx @@ -8,6 +8,19 @@ 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 textureGraphite from '../../assets/textures/metal-graphite.webp'; +import textureDeepBlue from '../../assets/textures/metal-deep-blue.webp'; +import textureWarmBronze from '../../assets/textures/metal-warm-bronze.webp'; + +// Eine generierte, gebuerstete Metall-Oberflaechenstruktur je Theme (statt Fotos pro +// Paket, die es fuer echte Kundenpakete nicht gibt) - wird per "overlay"-Blendmodus mit +// dem bestehenden Farbverlauf kombiniert, sodass die Preis-Staffelung weiterhin die +// Farbe/Helligkeit steuert, die Textur nur die Material-Oberflaeche liefert. +const PACKAGE_TEXTURES = { + graphite: textureGraphite, + 'deep-blue': textureDeepBlue, + 'warm-bronze': textureWarmBronze, +}; // Material-Stufen statt einfarbigem Verlauf: jede Preisstufe durchlaeuft eine eigene // "Wertigkeit" wie bei Kreditkarten-/Loyalty-Stufen. Die Paket-Sektion sitzt bewusst IMMER @@ -89,6 +102,7 @@ export default function ModelPage() { const { t, language } = useLanguage(); const { package_card_theme: packageCardTheme } = useSiteSettings(); const MATERIALS = PACKAGE_THEMES[packageCardTheme] || PACKAGE_THEMES.graphite; + const cardTexture = PACKAGE_TEXTURES[packageCardTheme] || PACKAGE_TEXTURES.graphite; const formatPrice = (value) => new Intl.NumberFormat(language === 'de' ? 'de-DE' : 'en-US', { style: 'currency', currency: 'EUR' }).format(value); @@ -125,7 +139,6 @@ export default function ModelPage() { // eine eigene Materialstufe (Bronze/Silber/Gold/Platin) bis zur Onyx-Kroenung ganz // oben. Das braucht keine zusaetzliche Admin-Einstellung und passt sich automatisch // an jede Paketanzahl an. - const tierLabels = t('modelPage.tierLabels'); const rankById = new Map( [...packages].sort((a, b) => a.total_price - b.total_price).map((p, i) => [p.id, i]) ); @@ -133,8 +146,6 @@ export default function ModelPage() { const n = packages.length; const rank = rankById.get(pkg.id) ?? 0; const tierT = n <= 1 ? 0 : rank / (n - 1); - const tierNumber = String(rank + 1).padStart(2, '0'); - const tierLabel = tierLabels[Math.min(Math.floor(tierT * tierLabels.length), tierLabels.length - 1)]; const bgRgb = materialRgbAt(MATERIALS, tierT, 'bg').map(Math.round); const accentRgbFloat = materialRgbAt(MATERIALS, tierT, 'accent'); @@ -171,8 +182,6 @@ export default function ModelPage() { return { mutedColor, - tierNumber, - tierLabel, accentColor: `rgb(${accentRgb.join(', ')})`, priceColor: `rgb(${priceRgb.join(', ')})`, roadOpacity, @@ -190,7 +199,12 @@ export default function ModelPage() { }, style: { backgroundColor: `rgb(${bgRgb.join(', ')})`, - backgroundImage: `radial-gradient(135% 160% at 88% -20%, rgb(${highlight.join(', ')}) 0%, rgb(${bgRgb.join(', ')}) 55%)`, + // Generierte Metall-Oberflaechenstruktur (oberste Ebene, per Blendmodus mit der + // Farbe darunter verschmolzen) + der bestehende Eck-Lichtschein-Verlauf darunter - + // die Preis-Farbstufe bleibt fuehrend, die Textur liefert nur die Materialoberflaeche. + backgroundImage: `url(${cardTexture}), radial-gradient(135% 160% at 88% -20%, rgb(${highlight.join(', ')}) 0%, rgb(${bgRgb.join(', ')}) 55%)`, + backgroundSize: '480px 480px, cover', + backgroundBlendMode: 'overlay, normal', borderColor: `rgb(${accentRgb.join(', ')})`, boxShadow: tierT > 0.04 @@ -241,18 +255,8 @@ export default function ModelPage() {