:-)
· 1 year ago
901e05401af457813da136e6e9c5127d671d8575
Parent:
df75662a8
1 file changed +477 −394
- lifestyle-calculator.html +477 −394
Diff
--- a/lifestyle-calculator.html +++ b/lifestyle-calculator.html @@ -63,68 +63,74 @@ <style> :root { --primary-color: #0d6efd; - /* Bootstrap Blue */ + --primary-color-rgb: 13, 110, 253; --secondary-color: #6c757d; - /* Bootstrap Gray */ --success-color: #198754; - /* Bootstrap Green */ --warning-color: #ffc107; - /* Bootstrap Yellow */ --danger-color: #dc3545; - /* Bootstrap Red */ --info-color: #0dcaf0; - /* Bootstrap Cyan */ - --dark-color: #212529; - /* Bootstrap Dark */ - --light-color: #f8f9fa; - /* Bootstrap Light */ + --dark-color: #212529; /* Main text color */ + --light-color: #f8f9fa; /* Light element backgrounds */ + + --body-bg-color: #e9ecef; + --main-container-bg: rgba(255, 255, 255, 0.98); + --main-container-shadow: 0 15px 40px rgba(0, 0, 0, 0.12); --card-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); --border-radius: 0.75rem; - /* 12px */ - --primary-color-rgb: 13, 110, 253; - /* For box-shadow transparency */ + + --page-title-font-family: 'Roboto Slab', serif; + --page-title-color: var(--primary-color); + --page-title-letter-spacing: normal; + --page-title-text-transform: none; + --page-title-font-weight: 700; + + --category-title-font-family: 'Roboto Slab', serif; + --category-title-letter-spacing: normal; + --category-title-text-transform: none; + --category-title-font-weight: 700; + + --category-icon-opacity: 0.8; + --item-title-font-weight: 600; + --item-description-color: #495057; + --category-item-border-color: #dee2e6; + + --slider-thumb-bg: var(--primary-color); + --slider-thumb-border: 5px solid white; + --slider-thumb-shadow-opacity: 0.5; + + --calculator-note-bg: #fff3cd; + --calculator-note-border: #ffeeba; + --calculator-note-text: var(--dark-color); + + --income-bracket-desc-color: #495057; + --chart-legend-text-color: #495057; + /* Refined Badge Colors */ - --badge-budgetSmart-bg: #e0f2f1; - /* Teal light */ - --badge-budgetSmart-text: #00796b; - /* Teal dark */ - --badge-milestone-bg: #e3f2fd; - /* Blue light */ - --badge-milestone-text: #1565c0; - /* Blue dark */ - --badge-essential-bg: #e8eaed; - /* Muted Grey-Blue light */ - --badge-essential-text: #5f6368; - /* Muted Grey-Blue dark */ - --badge-premium-bg: #fff8e1; - /* Yellow light */ - --badge-premium-text: #f57f17; - /* Yellow/Orange dark */ - --badge-ultraPremium-bg: #f3e5f5; - /* Purple light */ - --badge-ultraPremium-text: #6a1b9a; - /* Purple dark */ - --badge-aspirational-bg: #e8f5e9; - /* Green light */ - --badge-aspirational-text: #2e7d32; - /* Green dark */ - --badge-convenience-bg: #f1f8e9; - /* Light Green light */ - --badge-convenience-text: #558b2f; - /* Light Green dark */ - --badge-statusSymbol-bg: #311b92; - /* Deep rich purple */ - --badge-statusSymbol-text: #ffca28; - /* Amber/Gold */ + --badge-budgetSmart-bg: #e0f2f1; /* Teal light */ + --badge-budgetSmart-text: #00796b; /* Teal dark */ + --badge-milestone-bg: #e3f2fd; /* Blue light */ + --badge-milestone-text: #1565c0; /* Blue dark */ + --badge-essential-bg: #e8eaed; /* Muted Grey-Blue light */ + --badge-essential-text: #5f6368; /* Muted Grey-Blue dark */ + --badge-premium-bg: #fff8e1; /* Yellow light */ + --badge-premium-text: #f57f17; /* Yellow/Orange dark */ + --badge-ultraPremium-bg: #f3e5f5; /* Purple light */ + --badge-ultraPremium-text: #6a1b9a; /* Purple dark */ + --badge-aspirational-bg: #e8f5e9; /* Green light */ + --badge-aspirational-text: #2e7d32; /* Green dark */ + --badge-convenience-bg: #f1f8e9; /* Light Green light */ + --badge-convenience-text: #558b2f; /* Light Green dark */ + --badge-statusSymbol-bg: #311b92; /* Deep rich purple */ + --badge-statusSymbol-text: #ffca28; /* Amber/Gold */ } body { font-family: 'Inter', sans-serif; padding: 2rem 0; color: var(--dark-color); - background-color: #e9ecef; - /* Fallback */ + background-color: var(--body-bg-color); + transition: background-color 1.2s ease-in-out, color 1.2s ease-in-out; } .background-container { @@ -143,69 +149,74 @@ left: 0; width: 100%; height: 100%; - transition: opacity 1.2s ease-in-out; + transition: opacity 1.2s ease-in-out, background 1.2s ease-in-out; opacity: 0; z-index: -1; } - .theme-basic { - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); - } - - .theme-comfortable { - background: linear-gradient(135deg, #e0fbfc 0%, #c2f0fc 100%); - } + .theme-basic { background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); } + .theme-comfortable { background: linear-gradient(135deg, #e0fbfc 0%, #c2f0fc 100%); } + .theme-affluent { background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); } + .theme-luxury { background: linear-gradient(135deg, #fce4ec 0%, #f8bbd0 100%); } + .theme-ultra { background: linear-gradient(135deg, #ede7f6 0%, #d1c4e9 100%); } + .theme-elite { background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); } + + /* HNW and UHNW specific backgrounds */ + .theme-hnw-bg { background: linear-gradient(135deg, #FDF5E6 0%, #DAA520 100%); } + .theme-uhnw-bg { background: linear-gradient(135deg, #1a1a1a 0%, #003366 70%, #00A9E0 100%); } - .theme-affluent { - background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); - } - - .theme-luxury { - background: linear-gradient(135deg, #fce4ec 0%, #f8bbd0 100%); - } - - .theme-ultra { - background: linear-gradient(135deg, #ede7f6 0%, #d1c4e9 100%); - } - - .theme-elite { - background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); - } .main-container { - background: rgba(255, 255, 255, 0.98); + background: var(--main-container-bg); backdrop-filter: blur(5px); border-radius: var(--border-radius); - box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12); + box-shadow: var(--main-container-shadow); padding: 2.5rem; margin-bottom: 2rem; + transition: background-color 1.2s ease-in-out, box-shadow 1.2s ease-in-out; } .page-title { - font-family: 'Roboto Slab', serif; + font-family: var(--page-title-font-family); font-size: 2.6rem; - font-weight: 700; - color: var(--primary-color); + font-weight: var(--page-title-font-weight); + color: var(--page-title-color); margin-bottom: 0.25rem; + letter-spacing: var(--page-title-letter-spacing); + text-transform: var(--page-title-text-transform); + transition: color 1.2s ease-in-out, letter-spacing 0.5s ease, text-transform 0.5s ease; + } + + body.uhnw-theme-active .page-title { + letter-spacing: 1.5px; + text-transform: uppercase; + font-weight: 600; /* Slightly thinner for uppercase */ + text-shadow: 0 0 8px rgba(var(--primary-color-rgb), 0.7); } + body.hnw-theme-active .page-title { + text-shadow: 0 0 5px rgba(var(--primary-color-rgb), 0.5); + } + .page-subtitle { font-size: 1.0rem; color: var(--secondary-color); margin-bottom: 0.5rem; + transition: color 1.2s ease-in-out; } .calculator-note { font-size: 0.9rem; - color: var(--dark-color); + color: var(--calculator-note-text); font-style: normal; font-weight: 500; - background-color: #fff3cd; - border: 1px solid #ffeeba; + background-color: var(--calculator-note-bg); + border: 1px solid var(--calculator-note-border); padding: 0.5rem 0.75rem; border-radius: 0.375rem; margin-bottom: 1.5rem; text-align: center; + transition: background-color 1.2s ease-in-out, color 1.2s ease-in-out, border-color 1.2s ease-in-out; } .controls-container { @@ -213,13 +224,16 @@ border-radius: var(--border-radius); padding: 1.5rem; margin-bottom: 2rem; - border: 1px solid #ced4da; + border: 1px solid var(--category-item-border-color); /* Re-use for consistency */ + transition: background-color 1.2s ease-in-out, border-color 1.2s ease-in-out; } .form-label { font-weight: 500; margin-bottom: 0.3rem; font-size: 0.9rem; + color: var(--dark-color); /* Ensures it inherits themed text color */ + transition: color 1.2s ease-in-out; } .form-select-sm { @@ -235,7 +249,15 @@ color: var(--primary-color); text-align: center; margin-bottom: 0.1rem; + transition: color 1.2s ease-in-out; } + body.uhnw-theme-active .income-display { + text-shadow: 0 0 10px rgba(var(--primary-color-rgb), 0.8); + } + body.hnw-theme-active .income-display { + text-shadow: 0 0 8px rgba(var(--primary-color-rgb), 0.6); + } + .effective-income-display { font-size: 0.95rem; @@ -244,6 +266,7 @@ margin-bottom: 0.25rem; font-style: italic; min-height: 20px; + transition: color 1.2s ease-in-out; } .income-bracket-label { @@ -252,16 +275,18 @@ text-align: center; font-weight: 600; margin-bottom: 0.25rem; + transition: color 1.2s ease-in-out; } .income-bracket-description { font-size: 0.9rem; - color: #495057; + color: var(--income-bracket-desc-color); text-align: center; font-weight: 400; margin-bottom: 1.5rem; min-height: 38px; line-height: 1.4; + transition: color 1.2s ease-in-out; } .slider { @@ -281,89 +306,93 @@ width: 30px; height: 30px; border-radius: 50%; - background: var(--primary-color); + background: var(--slider-thumb-bg); cursor: pointer; - border: 5px solid white; - box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), 0.5); + border: var(--slider-thumb-border); + box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), var(--slider-thumb-shadow-opacity)); + transition: background-color 0.3s ease-in-out, border-color 0.3s ease-in-out; } .slider::-moz-range-thumb { width: 30px; height: 30px; border-radius: 50%; - background: var(--primary-color); + background: var(--slider-thumb-bg); cursor: pointer; - border: 5px solid white; - box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), 0.5); + border: var(--slider-thumb-border); + box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), var(--slider-thumb-shadow-opacity)); + transition: background-color 0.3s ease-in-out, border-color 0.3s ease-in-out; } .slider-labels { display: flex; justify-content: space-between; font-size: 0.85rem; - color: #6c757d; + color: var(--secondary-color); /* Changed from #6c757d */ padding: 0 5px; - } - - .visual-income-bar-container { - display: flex; - width: 100%; - height: 14px; - background-color: #e9ecef; - border-radius: 7px; - overflow: hidden; - margin: 1.25rem auto 1.5rem; - box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.08); - } - - .income-bar-segment { - height: 100%; - background: var(--primary-color); - transition: width 0.5s ease-in-out; + transition: color 1.2s ease-in-out; } .lifestyle-category { - background: #ffffff; + background: #ffffff; /* Default, might be overridden by --light-color in some themes */ border-radius: var(--border-radius); padding: 1.75rem; margin-bottom: 1.75rem; border-left: 6px solid var(--primary-color); - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); + box-shadow: var(--card-shadow); transition: all 0.3s ease; } + body.uhnw-theme-active .lifestyle-category { + background: var(--light-color); /* Will be darkish gray */ + } + .lifestyle-category:hover { transform: translateY(-4px) scale(1.015); box-shadow: 0 12px 35px rgba(0, 0, 0, 0.1); } + body.hnw-theme-active .lifestyle-category:hover, body.uhnw-theme-active .lifestyle-category:hover { + box-shadow: 0 12px 35px rgba(var(--primary-color-rgb), 0.3); + } + .category-title { - font-family: 'Roboto Slab', serif; + font-family: var(--category-title-font-family); font-size: 1.5rem; - font-weight: 700; + font-weight: var(--category-title-font-weight); color: var(--dark-color); margin-bottom: 1.25rem; display: flex; align-items: center; gap: 0.85rem; + letter-spacing: var(--category-title-letter-spacing); + text-transform: var(--category-title-text-transform); + transition: color 1.2s ease-in-out, letter-spacing 0.5s ease, text-transform 0.5s ease; + } + body.uhnw-theme-active .category-title { + text-transform: uppercase; + letter-spacing: 1px; + font-weight: 600; } + .category-icon { font-size: 2rem; color: var(--primary-color); - opacity: 0.8; + opacity: var(--category-icon-opacity); + transition: color 1.2s ease-in-out, opacity 1.2s ease-in-out; } .category-item-icon { font-size: 1.6rem; - color: var(--primary-color); - /* Will be overridden by category specific color */ - opacity: 0.75; + color: var(--primary-color); /* Will be overridden by category specific color */ + opacity: 0.75; /* Keep this less than main category icon for hierarchy */ margin-right: 1rem; vertical-align: middle; flex-shrink: 0; width: 32px; text-align: center; + transition: color 1.2s ease-in-out; } .category-item { @@ -371,246 +400,92 @@ border-radius: 10px; padding: 1.25rem; margin-bottom: 1rem; - border: 1px solid #dee2e6; + border: 1px solid var(--category-item-border-color); display: flex; align-items: flex-start; - transition: background-color 0.2s ease; + transition: background-color 0.2s ease, border-color 1.2s ease-in-out; } .category-item:hover { - background-color: #e9ecef; + background-color: #e9ecef; /* This needs to be themed too */ } + body.hnw-theme-active .category-item:hover { background-color: #FAF0E6; } /* Lighter linen */ + body.uhnw-theme-active .category-item:hover { background-color: #404040; } /* Slightly lighter dark gray */ - .category-item .item-content-wrapper { - flex-grow: 1; - } .item-title { - font-weight: 600; + font-weight: var(--item-title-font-weight); font-size: 1.0rem; color: var(--dark-color); margin-bottom: 0.3rem; display: flex; align-items: center; + transition: color 1.2s ease-in-out, font-weight 0.5s ease; } .item-description { - color: #495057; + color: var(--item-description-color); font-size: 0.85rem; line-height: 1.5; margin-bottom: 0.3rem; + transition: color 1.2s ease-in-out; } .item-financial-note { - color: #555; + color: var(--secondary-color); /* Changed from #555 */ font-size: 0.8rem; font-style: italic; margin-top: 0.4rem; opacity: 0.9; + transition: color 1.2s ease-in-out; + } + + /* Specific category border colors - these should remain distinct */ + .dining { border-left-color: #fd7e14 !important; } + .dining .category-icon, .dining .category-item-icon { color: #fd7e14 !important; } + .travel { border-left-color: #0dcaf0 !important; } + .travel .category-icon, .travel .category-item-icon { color: #0dcaf0 !important; } + .housing { border-left-color: #198754 !important; } + .housing .category-icon, .housing .category-item-icon { color: #198754 !important; } + .transportation { border-left-color: #6f42c1 !important; } + .transportation .category-icon, .transportation .category-item-icon { color: #6f42c1 !important; } + .entertainment { border-left-color: #d63384 !important; } + .entertainment .category-icon, .entertainment .category-item-icon { color: #d63384 !important; } + .education { border-left-color: #ffc107 !important; } + .education .category-icon, .education .category-item-icon { color: #ffc107 !important; } + .health-wellness { border-left-color: #e83e8c !important; } + .health-wellness .category-icon, .health-wellness .category-item-icon { color: #e83e8c !important; } + .services { border-left-color: #6c757d !important; } /* This is default secondary, might clash if secondary changes */ + .services .category-icon, .services .category-item-icon { color: #6c757d !important; } + .financial-planning { border-left-color: #20c997 !important; } + .financial-planning .category-icon, .financial-planning .category-item-icon { color: #20c997 !important; } + + /* Ensure category specific borders and icons are NOT overridden by primary color changes in HNW/UHNW themes */ + body.hnw-theme-active .lifestyle-category, body.uhnw-theme-active .lifestyle-category { + /* border-left-color will be handled by the specific classes above with !important */ + } + body.hnw-theme-active .category-icon, body.hnw-theme-active .category-item-icon, + body.uhnw-theme-active .category-icon, body.uhnw-theme-active .category-item-icon { + /* color will be handled by the specific classes above with !important */ + } + /* This ensures the general --primary-color doesn't override the specific category icon colors if they are not specific enough */ + .lifestyle-category .category-icon, .lifestyle-category .category-item-icon { + /* Fallback to primary if not specifically themed by category color */ + color: var(--primary-color); } - .item-budget-percent { - color: var(--success-color); - font-size: 0.8rem; - font-weight: 500; - margin-top: 0.4rem; - display: block; - } - - .badges-container { - margin-top: 0.75rem; - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - } - - .badge-custom { - padding: 0.35rem 0.85rem; - border-radius: 50px; - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - border-width: 1px; - border-style: solid; - display: inline-flex; - align-items: center; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); - transition: all 0.2s ease-in-out; - } - - .badge-custom:hover { - transform: translateY(-1px); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.12); - } - - .badge-budgetSmart { - background-color: var(--badge-budgetSmart-bg); - color: var(--badge-budgetSmart-text); - border-color: var(--badge-budgetSmart-text); - } - - .badge-milestone { - background-color: var(--badge-milestone-bg); - color: var(--badge-milestone-text); - border-color: var(--badge-milestone-text); - } - - .badge-essential { - background-color: var(--badge-essential-bg); - color: var(--badge-essential-text); - border-color: var(--badge-essential-text); - } - - .badge-premium { - background-color: var(--badge-premium-bg); - color: var(--badge-premium-text); - border-color: var(--badge-premium-text); - } - - .badge-ultraPremium { - background-color: var(--badge-ultraPremium-bg); - color: var(--badge-ultraPremium-text); - border-color: var(--badge-ultraPremium-text); - } - - .badge-aspirational { - background-color: var(--badge-aspirational-bg); - color: var(--badge-aspirational-text); - border-color: var(--badge-aspirational-text); - } - - .badge-convenience { - background-color: var(--badge-convenience-bg); - color: var(--badge-convenience-text); - border-color: var(--badge-convenience-text); - } - - .badge-statusSymbol { - background-color: var(--badge-statusSymbol-bg); - color: var(--badge-statusSymbol-text); - border-color: var(--badge-statusSymbol-text); - } - - .dining { - border-left-color: #fd7e14; - } - - .dining .category-icon { - color: #fd7e14; - } - - .dining .category-item-icon { - color: #fd7e14 !important; - } - - .travel { - border-left-color: #0dcaf0; - } - - .travel .category-icon { - color: #0dcaf0; - } - - .travel .category-item-icon { - color: #0dcaf0 !important; - } - - .housing { - border-left-color: #198754; - } - - .housing .category-icon { - color: #198754; - } - - .housing .category-item-icon { - color: #198754 !important; - } - - .transportation { - border-left-color: #6f42c1; - } - - .transportation .category-icon { - color: #6f42c1; - } - - .transportation .category-item-icon { - color: #6f42c1 !important; - } - - .entertainment { - border-left-color: #d63384; - } - - .entertainment .category-icon { - color: #d63384; - } - - .entertainment .category-item-icon { - color: #d63384 !important; - } - - .education { - border-left-color: #ffc107; - } - - .education .category-icon { - color: #ffc107; - } - - .education .category-item-icon { - color: #ffc107 !important; - } - - .health-wellness { - border-left-color: #e83e8c; - } - - .health-wellness .category-icon { - color: #e83e8c; - } - - .health-wellness .category-item-icon { - color: #e83e8c !important; - } - - .services { - border-left-color: #6c757d; - } - - .services .category-icon { - color: #6c757d; - } - - .services .category-item-icon { - color: #6c757d !important; - } - - .financial-planning { - border-left-color: #20c997; - } - - .financial-planning .category-icon { - color: #20c997; - } - - .financial-planning .category-item-icon { - color: #20c997 !important; - } #milestone-popup { position: fixed; bottom: -100px; left: 50%; transform: translateX(-50%); - background-color: var(--dark-color); - color: white; + background-color: var(--dark-color); /* Themed */ + color: var(--light-color); /* Themed for contrast */ padding: 1rem 2rem; border-radius: 50px; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.25); - transition: bottom 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + transition: bottom 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275), background-color 1.2s ease, color 1.2s ease; z-index: 1050; font-size: 1.1rem; font-weight: 600; @@ -619,14 +494,8 @@ gap: 0.75rem; } - #milestone-popup.show { - bottom: 25px; - } - - .highlight-update { - animation: highlight-animation 1.2s ease-out; - } - + #milestone-popup.show { bottom: 25px; } + .highlight-update { animation: highlight-animation 1.2s ease-out; } @keyframes highlight-animation { 0% { box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); @@ -812,6 +681,44 @@ let lastSliderValue = -1; let lastShownMilestone = -1; + const HNW_THRESHOLD_INDEX = 9; // "Ultra High Net Worth" Corresponds to incomeBrackets[9] + const UHNW_THRESHOLD_INDEX = 12; // "Ultra Wealthy" Corresponds to incomeBrackets[12] + + // Store default style values + const defaultStyles = { + primaryColor: '#0d6efd', + primaryColorRGB: '13, 110, 253', + secondaryColor: '#6c757d', + darkColor: '#212529', + lightColor: '#f8f9fa', + bodyBgColor: '#e9ecef', + mainContainerBg: 'rgba(255, 255, 255, 0.98)', + mainContainerShadow: '0 15px 40px rgba(0, 0, 0, 0.12)', + cardShadow: '0 10px 25px rgba(0, 0, 0, 0.1)', + pageTitleFontFamily: "'Roboto Slab', serif", + pageTitleColor: '#0d6efd', // Tied to primary + pageTitleLetterSpacing: 'normal', + pageTitleTextTransform: 'none', + pageTitleFontWeight: '700', + categoryTitleFontFamily: "'Roboto Slab', serif", + categoryTitleLetterSpacing: 'normal', + categoryTitleTextTransform: 'none', + categoryTitleFontWeight: '700', + categoryIconOpacity: '0.8', + itemTitleFontWeight: '600', + itemDescriptionColor: '#495057', + categoryItemBorderColor: '#dee2e6', + sliderThumbBg: '#0d6efd', // Tied to primary + sliderThumbBorder: '5px solid white', + sliderThumbShadowOpacity: '0.5', + calculatorNoteBg: '#fff3cd', + calculatorNoteBorder: '#ffeeba', + calculatorNoteText: '#212529', // Tied to darkColor + incomeBracketDescColor: '#495057', + chartLegendTextColor: '#495057' + }; + + const slider = document.getElementById('incomeSlider'); const incomeDisplayEl = document.getElementById('incomeDisplay'); const effectiveIncomeDisplayEl = document.getElementById('effectiveIncomeDisplay'); @@ -819,8 +726,8 @@ const incomeBracketDescEl = document.getElementById('incomeBracketDescription'); const colaSelectorEl = document.getElementById('colaSelector'); const familySizeSelectorEl = document.getElementById('familySizeSelector'); + const rootStyle = document.documentElement.style; - // Background Layers const bgLayer1 = document.getElementById('background-layer-1'); const bgLayer2 = document.getElementById('background-layer-2'); let activeBgLayer = 1; @@ -831,14 +738,179 @@ return `${symbol}${income.toLocaleString()}`; } - function updateDynamicBackground(bracketIndex) { + function getRGBFromHex(hex) { + if (!hex || typeof hex !== 'string') return defaultStyles.primaryColorRGB; // Fallback + let r = 0, g = 0, b = 0; + const hexColor = hex.startsWith('#') ? hex : `#${hex}`; // Ensure '#' prefix + + if (hexColor.length === 4) { // #RGB + r = parseInt(hexColor[1] + hexColor[1], 16); + g = parseInt(hexColor[2] + hexColor[2], 16); + b = parseInt(hexColor[3] + hexColor[3], 16); + } else if (hexColor.length === 7) { // #RRGGBB + r = parseInt(hexColor.substring(1, 3), 16); + g = parseInt(hexColor.substring(3, 5), 16); + b = parseInt(hexColor.substring(5, 7), 16); + } else { + return defaultStyles.primaryColorRGB; // Fallback for invalid hex + } + if (isNaN(r) || isNaN(g) || isNaN(b)) return defaultStyles.primaryColorRGB; // Fallback for parsing errors + return `${r}, ${g}, ${b}`; + } + + function updatePrimaryRGBVar(hexColor) { + const rgbString = getRGBFromHex(hexColor); + document.documentElement.style.setProperty('--primary-color-rgb', rgbString); + } + + + + function updateThemeStyling(bracketIndex) { + + const body = document.body; + + body.classList.remove('hnw-theme-active', 'uhnw-theme-active'); + + if (bracketIndex >= UHNW_THRESHOLD_INDEX) { // UHNW Theme + body.classList.add('uhnw-theme-active'); + rootStyle.setProperty('--primary-color', '#00A9E0'); // Vibrant Blue + updatePrimaryRGBVar('#00A9E0'); + rootStyle.setProperty('--secondary-color', '#A9A9A9'); // Light Gray + rootStyle.setProperty('--dark-color', '#F5F5F5'); // Main text (White Smoke) + rootStyle.setProperty('--light-color', '#282828'); // Element BGs (Dark Gray) + rootStyle.setProperty('--body-bg-color', '#121212'); // Very Dark Gray/Black + rootStyle.setProperty('--main-container-bg', 'rgba(20, 20, 20, 0.97)'); // Slightly transparent dark + rootStyle.setProperty('--main-container-shadow', '0 15px 50px rgba(0, 169, 224, 0.2)'); + rootStyle.setProperty('--card-shadow', '0 10px 30px rgba(0, 169, 224, 0.15)'); + + rootStyle.setProperty('--page-title-color', 'var(--primary-color)'); + // Typography changes for UHNW are in CSS via body.uhnw-theme-active .page-title + + rootStyle.setProperty('--category-icon-opacity', '1'); + rootStyle.setProperty('--item-title-font-weight', '700'); + rootStyle.setProperty('--item-description-color', '#D3D3D3'); // LightGray for descriptions + rootStyle.setProperty('--category-item-border-color', '#444444'); + + rootStyle.setProperty('--slider-thumb-bg', 'var(--primary-color)'); + rootStyle.setProperty('--slider-thumb-border', '3px solid #555555'); // Darker border for thumb + rootStyle.setProperty('--slider-thumb-shadow-opacity', '0.7'); + + rootStyle.setProperty('--calculator-note-bg', '#303030'); + rootStyle.setProperty('--calculator-note-border', '#505050'); + rootStyle.setProperty('--calculator-note-text', '#E0E0E0'); + rootStyle.setProperty('--income-bracket-desc-color', '#B0B0B0'); + rootStyle.setProperty('--chart-legend-text-color', '#D3D3D3'); + + + // UHNW Background Override + if (activeBgLayer === 1) { + bgLayer2.className = 'background-layer theme-uhnw-bg'; + bgLayer1.style.opacity = 0; bgLayer2.style.opacity = 1; activeBgLayer = 2; + } else { + bgLayer1.className = 'background-layer theme-uhnw-bg'; + bgLayer1.style.opacity = 1; bgLayer2.style.opacity = 0; activeBgLayer = 1; + } + + } else if (bracketIndex >= HNW_THRESHOLD_INDEX) { // HNW Theme + body.classList.add('hnw-theme-active'); + rootStyle.setProperty('--primary-color', '#B8860B'); // DarkGoldenRod + updatePrimaryRGBVar('#B8860B'); + rootStyle.setProperty('--secondary-color', '#4A3B15'); // Darker Brown/Gold + rootStyle.setProperty('--dark-color', '#3B2E0E'); // Main text (Very Dark Brown) + rootStyle.setProperty('--light-color', '#FFF8DC'); // Element BGs (Cornsilk) + rootStyle.setProperty('--body-bg-color', '#FDF5E6'); // Linen + rootStyle.setProperty('--main-container-bg', 'rgba(253, 245, 230, 0.98)'); // Slightly transparent Linen + rootStyle.setProperty('--main-container-shadow', '0 15px 40px rgba(184, 134, 11, 0.2)'); + rootStyle.setProperty('--card-shadow', '0 10px 25px rgba(184, 134, 11, 0.15)'); + + rootStyle.setProperty('--page-title-color', 'var(--primary-color)'); + // Typography changes for HNW are in CSS via body.hnw-theme-active .page-title + + rootStyle.setProperty('--category-icon-opacity', '0.9'); + rootStyle.setProperty('--item-title-font-weight', '600'); // Default is fine + rootStyle.setProperty('--item-description-color', '#554216'); // Darker Brown for descriptions + rootStyle.setProperty('--category-item-border-color', '#E0D6B3'); // Pale Gold/Beige + + rootStyle.setProperty('--slider-thumb-bg', 'var(--primary-color)'); + rootStyle.setProperty('--slider-thumb-border', '4px solid #FFF8DC'); // Cornsilk border + rootStyle.setProperty('--slider-thumb-shadow-opacity', '0.6'); + + rootStyle.setProperty('--calculator-note-bg', '#FAF0E6'); // Lighter Linen + rootStyle.setProperty('--calculator-note-border', '#E8DDCB'); // Darker Linen border + rootStyle.setProperty('--calculator-note-text', 'var(--dark-color)'); + rootStyle.setProperty('--income-bracket-desc-color', '#6F5E33'); // Medium Brown + rootStyle.setProperty('--chart-legend-text-color', '#554216'); // Darker Brown for legend + + + // HNW Background Override + if (activeBgLayer === 1) { + bgLayer2.className = 'background-layer theme-hnw-bg'; + bgLayer1.style.opacity = 0; bgLayer2.style.opacity = 1; activeBgLayer = 2; + } else { + bgLayer1.className = 'background-layer theme-hnw-bg'; + bgLayer1.style.opacity = 1; bgLayer2.style.opacity = 0; activeBgLayer = 1; + } + + + } else { // Default Theme + Object.keys(defaultStyles).forEach(key => { + const cssVarName = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`; + if (key === 'primaryColorRGB') { + updatePrimaryRGBVar(defaultStyles.primaryColor); // Ensure RGB is set from the hex + } else if (key === 'pageTitleColor' || key === 'sliderThumbBg') { + rootStyle.setProperty(cssVarName, `var(--primary-color)`); + } else if (key === 'calculatorNoteText') { + rootStyle.setProperty(cssVarName, `var(--dark-color)`); + } + else { + rootStyle.setProperty(cssVarName, defaultStyles[key]); + } + }); + // Ensure primary color itself and its RGB are reset based on defaultStyles.primaryColor + rootStyle.setProperty('--primary-color', defaultStyles.primaryColor); + updatePrimaryRGBVar(defaultStyles.primaryColor); // Explicitly reset RGB from default hex + + // Let original dynamic background logic take over + updateDynamicBackground(bracketIndex, true); + } + // Apply body background color explicitly after all var changes + body.style.backgroundColor = rootStyle.getPropertyValue('--body-bg-color').trim(); + } + + + function updateDynamicBackground(bracketIndex, forceDefaultLogic = false) { + // If HNW/UHNW themes are active, their specific backgrounds are set in updateThemeStyling. + // This function should only run for brackets *below* HNW/UHNW or if forced. + if (!forceDefaultLogic && (document.body.classList.contains('hnw-theme-active') || document.body.classList.contains('uhnw-theme-active'))) { + return; + } + let themeClass = ''; + // These thresholds define the non-HNW/UHNW background themes if (bracketIndex <= 1) themeClass = 'theme-basic'; else if (bracketIndex <= 3) themeClass = 'theme-comfortable'; - else if (bracketIndex <= 6) themeClass = 'theme-affluent'; - else if (bracketIndex <= 9) themeClass = 'theme-luxury'; - else if (bracketIndex <= 12) themeClass = 'theme-ultra'; - else themeClass = 'theme-elite'; + else if (bracketIndex <= 5) themeClass = 'theme-affluent'; // Affluent up to index 5 ($150k) + else if (bracketIndex <= HNW_THRESHOLD_INDEX -1 ) themeClass = 'theme-luxury'; // Luxury up to index 8 ($500k), before HNW at 9 + // No 'ultra' or 'elite' needed here as they would fall into HNW/UHNW index ranges + // Or, if HNW_THRESHOLD is, say, 6, then luxury would not be hit. + // This logic needs to be robust for HNW_THRESHOLD_INDEX. + // Example: + // 0,1: basic + // 2,3: comfortable + // 4,5: affluent (Affluent is bracket index 5, $150k) + // 6,7,8: luxury (High Income $200k, Wealthy $300k, Very Wealthy $500k are indices 6,7,8) + // HNW starts at index 9 ($750k UHNW by original label) + // UHNW starts at index 12 ($5M Ultra Wealthy by original label) + + // Let's refine the non-HNW/UHNW theme classes based on original progression idea + if (bracketIndex <= 1) themeClass = 'theme-basic'; // Working Poor, Low Income + else if (bracketIndex <= 3) themeClass = 'theme-comfortable'; // Lower Mid, Middle Class + else if (bracketIndex <= 5) themeClass = 'theme-affluent'; // Upper Mid, Affluent + else if (bracketIndex < HNW_THRESHOLD_INDEX) themeClass = 'theme-luxury'; // High Income, Wealthy, Very Wealthy + // If HNW/UHNW logic is bypassed (forceDefaultLogic), this still needs to pick *something* + else if (bracketIndex < UHNW_THRESHOLD_INDEX) themeClass = 'theme-ultra'; // (Original) UHNW, Millionaire, Multi-Millionaire + else themeClass = 'theme-elite'; // (Original) Ultra Wealthy, Ultra Elite, Billionaire Class + if (activeBgLayer === 1) { bgLayer2.className = `background-layer ${themeClass}`; @@ -860,17 +932,23 @@ const labels = Object.keys(bracketData.budgetAllocation); const data = Object.values(bracketData.budgetAllocation); - // A more sophisticated, financial-themed color palette - const colorPalette = [ - '#2d4059', // Deep Blue - '#ea5455', // Muted Red - '#f07b3f', // Orange - '#ffd460', // Yellow - '#7e9a9a', // Teal-Gray - '#4a6c6f', // Darker Teal - '#99a8b2', // Light Slate - '#596e79' // Medium Slate - ]; + const isUHNW = document.body.classList.contains('uhnw-theme-active'); + const isHNW = document.body.classList.contains('hnw-theme-active'); + + const legendTextColor = getComputedStyle(document.documentElement).getPropertyValue('--chart-legend-text-color').trim() || defaultStyles.chartLegendTextColor; + const chartBorderColor = isUHNW ? '#121212' : (isHNW ? '#FDF5E6' : '#ffffff'); + + + const defaultColorPalette = ['#2d4059', '#ea5455', '#f07b3f', '#ffd460', '#7e9a9a', '#4a6c6f', '#99a8b2', '#596e79']; + const hnwColorPalette = ['#A0522D', '#CD853F', '#D2B48C', '#B8860B', '#F4A460', '#8B4513', '#BC8F8F', '#DEB887']; // Sienna, Peru, Tan, DarkGoldenRod, SandyBrown, SaddleBrown, RosyBrown, BurlyWood + const uhnwColorPalette = ['#00BFFF', '#1E90FF', '#87CEFA', '#ADD8E6', '#B0E0E6', '#5F9EA0', '#4682B4', '#AFEEEE']; // DeepSkyBlue, DodgerBlue, LightSkyBlue, LightBlue, PowderBlue, CadetBlue, SteelBlue, PaleTurquoise + + + let currentPalette; + if (isUHNW) currentPalette = uhnwColorPalette; + else if (isHNW) currentPalette = hnwColorPalette; + else currentPalette = defaultColorPalette; + const chartConfig = { type: 'doughnut', @@ -879,10 +957,10 @@ datasets: [{ label: 'Budget Allocation', data: data, - backgroundColor: colorPalette, - borderColor: '#ffffff', + backgroundColor: currentPalette, + borderColor: chartBorderColor, borderWidth: 3, - hoverBorderColor: '#f8f9fa', + hoverBorderColor: isUHNW ? '#333333' : (isHNW ? '#FAF0E6' : '#f8f9fa'), hoverBorderWidth: 1, hoverOffset: 8 }] @@ -890,67 +968,48 @@ options: { responsive: true, maintainAspectRatio: false, - cutout: '70%', // Makes the doughnut a bit thinner and more modern - animation: { - animateScale: true, - animateRotate: true - }, + cutout: '70%', + animation: { animateScale: true, animateRotate: true }, plugins: { legend: { display: true, - position: 'right', // Display labels in a legend + position: 'right', align: 'center', labels: { boxWidth: 12, padding: 15, - font: { - size: 11, - family: "'Inter', sans-serif" - }, - color: '#495057' + font: { size: 11, family: "'Inter', sans-serif" }, + color: legendTextColor } }, tooltip: { enabled: true, backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleFont: { - size: 14, - weight: 'bold' - }, - bodyFont: { - size: 12 - }, + titleFont: { size: 14, weight: 'bold' }, + bodyFont: { size: 12 }, padding: 12, cornerRadius: 8, callbacks: { label: function (context) { let label = context.label || ''; - if (label) { - label += ': '; - } - if (context.parsed !== null) { - label += context.parsed + '%'; - } + if (label) label += ': '; + if (context.parsed !== null) label += context.parsed + '%'; return label; } } } }, - layout: { - padding: { - left: 0, - right: 10 // Add padding to ensure legend labels are not cut off - } - } + layout: { padding: { left: 0, right: 10 } } } }; if (budgetChart) { budgetChart.data.labels = labels; budgetChart.data.datasets[0].data = data; - // Re-apply config on update to ensure settings persist - budgetChart.options = chartConfig.options; - budgetChart.update(); // Use default animation on update + budgetChart.data.datasets[0].backgroundColor = currentPalette; + budgetChart.data.datasets[0].borderColor = chartBorderColor; + budgetChart.options.plugins.legend.labels.color = legendTextColor; + budgetChart.update(); } else { const ctx = document.getElementById('budgetChart').getContext('2d'); budgetChart = new Chart(ctx, chartConfig); @@ -978,6 +1037,8 @@ const baseBracket = incomeBrackets[selectedBracketIndex]; if (!baseBracket) return; + updateThemeStyling(selectedBracketIndex); + const selectedColaFactor = parseFloat(colaSelectorEl.options[colaSelectorEl.selectedIndex].dataset.factor); currentCurrencySymbol = colaSelectorEl.options[colaSelectorEl.selectedIndex].dataset.currency; const selectedFamilySize = familySizeSelectorEl.value; @@ -989,7 +1050,13 @@ incomeBracketLabelEl.textContent = baseBracket.label; incomeBracketDescEl.textContent = baseBracket.description; - updateDynamicBackground(selectedBracketIndex); + // Background logic is now complex: + // 1. updateThemeStyling sets specific HNW/UHNW backgrounds. + // 2. If not HNW/UHNW, updateThemeStyling calls updateDynamicBackground with force=true + // to use the original progressive backgrounds. + // So, no explicit call to updateDynamicBackground needed here unless it's for non-HNW/UHNW only. + // updateThemeStyling handles the conditional logic for background setting. + updateBudgetChart(selectedBracketIndex); showMilestone(selectedBracketIndex); @@ -1003,7 +1070,7 @@ return; } - const oldHTML = contentDiv.innerHTML; // Store current HTML + const oldHTML = contentDiv.innerHTML; let itemsToDisplay = []; for (let i = categoryData.length - 1; i >= 0; i--) { @@ -1017,7 +1084,7 @@ } const itemsHTML = itemsToDisplay.map((item, idx) => { - const badgesHTML = item.badges ? `<div class="badges-container">${item.badges.map(badge => `<span class="badge-custom badge-${badge.toLowerCase()}">${badge.replace(/([A-Z])/g, ' $1').trim()}</span>`).join('')}</div>` : ''; + const badgesHTML = item.badges ? `<div class="badges-container">${item.badges.map(badge => `<span class="badge-custom badge-${badge.toLowerCase().replace(/\s+/g, '')}">${badge.replace(/([A-Z])/g, ' $1').trim()}</span>`).join('')}</div>` : ''; const itemIconHTML = item.itemIcon ? `<i class="bi ${item.itemIcon} category-item-icon"></i>` : '<i class="bi bi-record-circle-fill category-item-icon"></i>'; const financialNoteHTML = item.financialNote ? `<div class="item-financial-note">${item.financialNote}</div>` : ''; return ` @@ -1029,18 +1096,16 @@ ${financialNoteHTML} ${badgesHTML} </div> - </div> - `; + </div>`; }).join(''); contentDiv.innerHTML = itemsHTML; - // *** NEW: Check for changes and apply highlight *** if (contentDiv.innerHTML !== oldHTML) { const categoryCard = contentDiv.closest('.lifestyle-category'); if (categoryCard) { - categoryCard.classList.remove('highlight-update'); // Remove old class first - void categoryCard.offsetWidth; // Trigger reflow to restart animation + categoryCard.classList.remove('highlight-update'); + void categoryCard.offsetWidth; categoryCard.classList.add('highlight-update'); } } @@ -1049,6 +1114,33 @@ } function initializeApp() { + // Capture the *actual* initial primary color from CSS in case it was changed from Bootstrap default + const actualInitialPrimaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); + defaultStyles.primaryColor = actualInitialPrimaryColor; + defaultStyles.primaryColorRGB = getRGBFromHex(actualInitialPrimaryColor); + defaultStyles.pageTitleColor = actualInitialPrimaryColor; // Tie to actual initial + defaultStyles.sliderThumbBg = actualInitialPrimaryColor; // Tie to actual initial + + // Set all default styles to root, ensuring --primary-color-rgb is derived correctly + Object.keys(defaultStyles).forEach(key => { + const cssVarName = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`; + if (key === 'primaryColorRGB') { + // This is handled by updatePrimaryRGBVar called below + } else if (key === 'pageTitleColor' || key === 'sliderThumbBg') { + rootStyle.setProperty(cssVarName, `var(--primary-color)`); + } else if (key === 'calculatorNoteText') { + rootStyle.setProperty(cssVarName, `var(--dark-color)`); + } + else { + document.documentElement.style.setProperty(cssVarName, defaultStyles[key]); + } + }); + // Explicitly set primary color and its RGB derivative + document.documentElement.style.setProperty('--primary-color', defaultStyles.primaryColor); + updatePrimaryRGBVar(defaultStyles.primaryColor); + document.body.style.backgroundColor = defaultStyles.bodyBgColor; + + costOfLivingFactors.forEach(factor => { const option = document.createElement('option'); option.value = factor.city; @@ -1057,17 +1149,9 @@ option.dataset.currency = factor.currencySymbol; colaSelectorEl.appendChild(option); }); - - const primaryColorHex = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); - if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(primaryColorHex)) { - let r = 0, g = 0, b = 0; - if (primaryColorHex.length === 4) { r = parseInt(primaryColorHex[1] + primaryColorHex[1], 16); g = parseInt(primaryColorHex[2] + primaryColorHex[2], 16); b = parseInt(primaryColorHex[3] + primaryColorHex[3], 16); } - else if (primaryColorHex.length === 7) { r = parseInt(primaryColorHex.substring(1, 3), 16); g = parseInt(primaryColorHex.substring(3, 5), 16); b = parseInt(primaryColorHex.substring(5, 7), 16); } - document.documentElement.style.setProperty('--primary-color-rgb', `${r}, ${g}, ${b}`); - } - + lastSliderValue = parseInt(slider.value); - updateLifestyle(); + updateLifestyle(); slider.addEventListener('input', updateLifestyle); colaSelectorEl.addEventListener('change', updateLifestyle); @@ -1105,5 +1189,4 @@ <script crossorigin="anonymous" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> </body> - </html> \ No newline at end of file