diff --git a/hifi-src/src/i18n/de.js b/hifi-src/src/i18n/de.js index 1b7ee48..ca1660d 100644 --- a/hifi-src/src/i18n/de.js +++ b/hifi-src/src/i18n/de.js @@ -156,6 +156,8 @@ export default { 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.', }, leistungen: { metaTitle: 'Leistungen', diff --git a/hifi-src/src/i18n/en.js b/hifi-src/src/i18n/en.js index 9506557..08eaf48 100644 --- a/hifi-src/src/i18n/en.js +++ b/hifi-src/src/i18n/en.js @@ -156,6 +156,8 @@ export default { 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.', }, leistungen: { metaTitle: 'Services', diff --git a/hifi-src/src/pages/public/ModelPage.jsx b/hifi-src/src/pages/public/ModelPage.jsx index cac910a..862435d 100644 --- a/hifi-src/src/pages/public/ModelPage.jsx +++ b/hifi-src/src/pages/public/ModelPage.jsx @@ -7,31 +7,25 @@ import MaintenanceBypassBanner from '../../components/MaintenanceBypassBanner.js import DynamicIcon from '../../components/DynamicIcon.jsx'; import { useMaintenance } from '../../context/MaintenanceContext.jsx'; import { useLanguage } from '../../context/LanguageContext.jsx'; -import { useTheme } from '../../context/ThemeContext.jsx'; // Material-Stufen statt einfarbigem Verlauf: jede Preisstufe durchlaeuft eine eigene -// "Wertigkeit" wie bei Kreditkarten-/Loyalty-Stufen - Basis (weiss) -> Bronze -> Silber -// -> Gold -> Platin -> Onyx (fast schwarz, mit Gold-Glanz als Kroenung ganz oben). Jede -// Stufe hat sowohl eine Flaechenfarbe (bg) als auch eine dazu passende Akzentfarbe -// (Rahmen/Leucht-Schatten/Preis/Icon) - nur der "Kontakt anfragen"-Button bleibt ueberall +// "Wertigkeit" wie bei Kreditkarten-/Loyalty-Stufen - Graphit (Einstieg) -> Bronze -> +// Silber -> Gold -> Platin -> Onyx (fast schwarz, mit Gold-Glanz als Kroenung ganz oben). +// Die Paket-Sektion sitzt bewusst IMMER auf einer dunklen Buehne (unabhaengig vom +// Hell/Dunkel-Modus der Seite, wie ein Fahrzeugkonfigurator) - das ist der groesste Hebel +// fuer den edlen/luxurioesen Eindruck, den flache helle Kacheln nicht liefern konnten. +// Jede Stufe hat eine Flaechenfarbe (bg) und eine dazu passende Akzentfarbe (Rahmen/ +// Leucht-Schatten/Preis/Icon) - nur der "Kontakt anfragen"-Button bleibt ueberall // einheitlich markengruen, damit die Kernaktion auf jeder Karte wiedererkennbar bleibt. -// Zwischen zwei Nachbar-Materialien wird in HSL linear interpoliert, sodass JEDES Paket -// (nicht nur die Materialien selbst) eine eigene Abstufung bekommt. -const MATERIALS_LIGHT = [ - { bg: [0, 0, 100], accent: [88, 63, 40] }, // Basis - { bg: [24, 42, 55], accent: [24, 58, 42] }, // Bronze - { bg: [212, 10, 80], accent: [212, 15, 48] }, // Silber - { bg: [45, 60, 60], accent: [42, 75, 42] }, // Gold - { bg: [196, 16, 85], accent: [200, 22, 52] }, // Platin - { bg: [225, 20, 6], accent: [45, 70, 55] }, // Onyx -]; -const MATERIALS_DARK = [ - { bg: [0, 0, 9], accent: [88, 55, 48] }, - { bg: [22, 35, 20], accent: [25, 55, 58] }, - { bg: [212, 10, 20], accent: [212, 18, 68] }, - { bg: [45, 42, 22], accent: [42, 68, 58] }, - { bg: [196, 14, 24], accent: [200, 22, 72] }, - { bg: [225, 22, 4], accent: [45, 75, 68] }, +// Zwischen zwei Nachbar-Materialien wird in RGB linear interpoliert (siehe materialRgbAt), +// sodass JEDES Paket (nicht nur die Materialien selbst) eine eigene Abstufung bekommt. +const MATERIALS = [ + { bg: [0, 0, 14], accent: [88, 45, 55] }, // Graphit (Einstieg) + { bg: [24, 32, 22], accent: [24, 55, 58] }, // Bronze + { bg: [212, 10, 24], accent: [212, 18, 68] }, // Silber + { bg: [45, 38, 24], accent: [42, 68, 58] }, // Gold + { bg: [196, 12, 27], accent: [200, 20, 72] }, // Platin + { bg: [225, 22, 5], accent: [45, 78, 68] }, // Onyx ]; const hslToRgb = (h, s, l) => { @@ -73,7 +67,6 @@ export default function ModelPage() { const [error, setError] = useState(''); const maintenance = useMaintenance(); const { t, language } = useLanguage(); - const { theme } = useTheme(); const formatPrice = (value) => new Intl.NumberFormat(language === 'de' ? 'de-DE' : 'en-US', { style: 'currency', currency: 'EUR' }).format(value); @@ -105,12 +98,11 @@ export default function ModelPage() { const { model, packages } = data; - // Preis-Rang innerhalb dieses Modells bestimmt die optische Wucht der Kachel: - // die guenstigste Option bleibt schlicht/weiss, jede weitere Preisstufe durchlaeuft + // Preis-Rang innerhalb dieses Modells bestimmt die optische Wucht der Kachel: die + // guenstigste Option bleibt schlichtes Graphit, jede weitere Preisstufe durchlaeuft // 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 materials = theme === 'dark' ? MATERIALS_DARK : MATERIALS_LIGHT; 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]) @@ -122,8 +114,8 @@ export default function ModelPage() { 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'); + const bgRgb = materialRgbAt(MATERIALS, tierT, 'bg').map(Math.round); + const accentRgbFloat = materialRgbAt(MATERIALS, tierT, 'accent'); const accentRgb = accentRgbFloat.map(Math.round); const bgHsl = rgbToHsl(...bgRgb); const accentHsl = rgbToHsl(...accentRgbFloat); @@ -152,21 +144,26 @@ export default function ModelPage() { const highlight = hslToRgb(bgHsl[0], Math.max(bgHsl[1] - 8, 0), Math.min(bgL + 16, 99)); return { - isDarkCard, mutedColor, tierNumber, tierLabel, + accentColor: `rgb(${accentRgb.join(', ')})`, priceColor: `rgb(${priceRgb.join(', ')})`, iconChipStyle: { backgroundColor: `rgba(${accentRgb.join(', ')}, 0.16)`, color: `rgb(${priceRgb.join(', ')})`, boxShadow: `inset 0 0 0 1px rgba(${accentRgb.join(', ')}, 0.45)`, }, + // Duenne, gleichbleibende Haarlinie statt dickerem Rahmen - die zunehmende Wertigkeit + // zeigt sich in Farbe/Leucht-Schatten, nicht in der Strichstaerke. Wirkt ruhiger/edler. + glowLineStyle: { + background: `linear-gradient(90deg, transparent, rgba(${accentRgb.join(', ')}, 0.9), transparent)`, + boxShadow: `0 0 ${Math.round(6 + tierT * 14)}px rgba(${accentRgb.join(', ')}, ${(0.25 + tierT * 0.45).toFixed(2)})`, + }, style: { backgroundColor: `rgb(${bgRgb.join(', ')})`, backgroundImage: `radial-gradient(135% 160% at 88% -20%, rgb(${highlight.join(', ')}) 0%, rgb(${bgRgb.join(', ')}) 55%)`, borderColor: `rgb(${accentRgb.join(', ')})`, - borderWidth: tierT > 0.08 ? 2 : 1, boxShadow: tierT > 0.04 ? `0 ${Math.round(6 + tierT * 14)}px ${Math.round(20 + tierT * 45)}px -10px rgba(${accentRgb.join(', ')}, ${glowAlpha})` @@ -187,85 +184,105 @@ export default function ModelPage() { }; return ( -
- {t('modelPage.breadcrumbVehicles')} /{' '} - {model.brand_name} / {model.name} -
-+ {t('modelPage.breadcrumbVehicles')} /{' '} + {model.brand_name} / {model.name} +
+{t('modelPage.empty')}
- )} - -- {tierNumber} · {tierLabel} -
- - {pkg.icon_name && ( -- {pkg.tagline} -
- )} - -- {t('modelPage.totalPrice')} -
-- {formatPrice(pkg.total_price)} -
-{t('modelPage.empty')}
+ )}{t('modelPage.tiersSubheading')}
++ {pkg.tagline} +
+ )} + + + ++ {t('modelPage.totalPrice')} +
++ {formatPrice(pkg.total_price)} +
+