Update lifestyle-calculator.html
· 1 year ago
1c128428a495de1bb771476be257527df13acd9c
Parent:
4dd2ba0e1
1 file changed +924 −463
- lifestyle-calculator.html +924 −463
Diff
--- a/lifestyle-calculator.html +++ b/lifestyle-calculator.html @@ -1,28 +1,37 @@ <!DOCTYPE html> <html lang="en"> + <head> - <meta charset="utf-8"/> - <meta content="width=device-width, initial-scale=1.0" name="viewport"/> + <meta charset="utf-8" /> + <meta content="width=device-width, initial-scale=1.0" name="viewport" /> <title>Lifestyle Calculator: What Your Family's Income Really Buys</title> - <meta content="Explore how different family income levels translate to real lifestyle choices. From $20K to $25M+, see what your household can afford: dining, travel, housing, entertainment, education, financial planning, and luxury services with this interactive tool. Includes a basic Cost of Living Adjustment." name="description"/> - <meta content="lifestyle calculator, income lifestyle, family budget, what can I afford, salary lifestyle, income comparison, lifestyle expenses, luxury calculator, income brackets, wealth lifestyle, finance, cost of living, financial planning, investments, family finance" name="keywords"/> - <link href="https://cheatsheets.davidveksler.com/lifestyle-calculator.html" rel="canonical"/> + <meta + content="Explore how different family income levels translate to real lifestyle choices. From $20K to $25M+, see what your household can afford: dining, travel, housing, entertainment, education, financial planning, and luxury services with this interactive tool. Includes a basic Cost of Living Adjustment." + name="description" /> + <meta + content="lifestyle calculator, income lifestyle, family budget, what can I afford, salary lifestyle, income comparison, lifestyle expenses, luxury calculator, income brackets, wealth lifestyle, finance, cost of living, financial planning, investments, family finance" + name="keywords" /> + <link href="https://cheatsheets.davidveksler.com/lifestyle-calculator.html" rel="canonical" /> <!-- Open Graph / Facebook --> - <meta content="website" property="og:type"/> - <meta content="https://cheatsheets.davidveksler.com/lifestyle-calculator.html" property="og:url"/> - <meta content="Lifestyle Calculator: What Your Family's Income Really Buys" property="og:title"/> - <meta content="Explore how different family income levels translate to real lifestyle choices, with a basic Cost of Living Adjustment. See what your household can afford from $20K to $25M+." property="og:description"/> - <meta content="images/lifestyle-calculator.png" property="og:image"/> - <meta content="Interactive lifestyle calculator showing income to lifestyle progression " property="og:image:alt"/> - <meta content="1200" property="og:image:width"/> - <meta content="630" property="og:image:height"/> + <meta content="website" property="og:type" /> + <meta content="https://cheatsheets.davidveksler.com/lifestyle-calculator.html" property="og:url" /> + <meta content="Lifestyle Calculator: What Your Family's Income Really Buys" property="og:title" /> + <meta + content="Explore how different family income levels translate to real lifestyle choices, with a basic Cost of Living Adjustment. See what your household can afford from $20K to $25M+." + property="og:description" /> + <meta content="images/lifestyle-calculator.png" property="og:image" /> + <meta content="Interactive lifestyle calculator showing income to lifestyle progression " property="og:image:alt" /> + <meta content="1200" property="og:image:width" /> + <meta content="630" property="og:image:height" /> <!-- Twitter Card --> - <meta content="summary_large_image" name="twitter:card"/> - <meta content="https://cheatsheets.davidveksler.com/lifestyle-calculator.html" name="twitter:url"/> - <meta content="Lifestyle Calculator: What Your Family's Income Really Buys" name="twitter:title"/> - <meta content="Explore how different family income levels translate to real lifestyle choices, with a basic Cost of Living Adjustment. See what your household can afford from $20K to $25M+." name="twitter:description"/> - <meta content="images/lifestyle-calculator.png" name="twitter:image"/> - + <meta content="summary_large_image" name="twitter:card" /> + <meta content="https://cheatsheets.davidveksler.com/lifestyle-calculator.html" name="twitter:url" /> + <meta content="Lifestyle Calculator: What Your Family's Income Really Buys" name="twitter:title" /> + <meta + content="Explore how different family income levels translate to real lifestyle choices, with a basic Cost of Living Adjustment. See what your household can afford from $20K to $25M+." + name="twitter:description" /> + <meta content="images/lifestyle-calculator.png" name="twitter:image" /> + <!-- SEO: JSON-LD Structured Data --> <script type="application/ld+json"> { @@ -42,72 +51,127 @@ } </script> - <link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"/> - <link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet"/> - <link href="https://fonts.googleapis.com" rel="preconnect"/> - <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/> - <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Slab:wght@700&display=swap" rel="stylesheet"/> + <link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" + rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" /> + <link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet" /> + <link href="https://fonts.googleapis.com" rel="preconnect" /> + <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" /> + <link + href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Slab:wght@700&display=swap" + rel="stylesheet" /> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <style> :root { - --primary-color: #0d6efd; /* Bootstrap Blue */ - --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 */ + --primary-color: #0d6efd; + /* Bootstrap Blue */ + --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 */ --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 */ - + --border-radius: 0.75rem; + /* 12px */ + --primary-color-rgb: 13, 110, 253; + /* For box-shadow transparency */ + /* 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: #e9ecef; + /* Fallback */ } .background-container { position: fixed; - top: 0; left: 0; width: 100%; height: 100%; + top: 0; + left: 0; + width: 100%; + height: 100%; z-index: -2; } - #background-layer-1, #background-layer-2 { + + #background-layer-1, + #background-layer-2 { position: absolute; - top: 0; left: 0; width: 100%; height: 100%; + top: 0; + left: 0; + width: 100%; + height: 100%; transition: opacity 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-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%); } - + .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%); + } + .main-container { background: rgba(255, 255, 255, 0.98); backdrop-filter: blur(5px); @@ -116,6 +180,7 @@ padding: 2.5rem; margin-bottom: 2rem; } + .page-title { font-family: 'Roboto Slab', serif; font-size: 2.6rem; @@ -123,11 +188,13 @@ color: var(--primary-color); margin-bottom: 0.25rem; } + .page-subtitle { font-size: 1.0rem; color: var(--secondary-color); margin-bottom: 0.5rem; } + .calculator-note { font-size: 0.9rem; color: var(--dark-color); @@ -140,7 +207,7 @@ margin-bottom: 1.5rem; text-align: center; } - + .controls-container { background: var(--light-color); border-radius: var(--border-radius); @@ -148,10 +215,19 @@ margin-bottom: 2rem; border: 1px solid #ced4da; } - - .form-label { font-weight: 500; margin-bottom: 0.3rem; font-size: 0.9rem;} - .form-select-sm { font-size: 0.875rem; padding-top: 0.4rem; padding-bottom: 0.4rem;} - + + .form-label { + font-weight: 500; + margin-bottom: 0.3rem; + font-size: 0.9rem; + } + + .form-select-sm { + font-size: 0.875rem; + padding-top: 0.4rem; + padding-bottom: 0.4rem; + } + .income-display { font-family: 'Roboto Slab', serif; font-size: 2.3rem; @@ -160,6 +236,7 @@ text-align: center; margin-bottom: 0.1rem; } + .effective-income-display { font-size: 0.95rem; color: var(--secondary-color); @@ -168,7 +245,7 @@ font-style: italic; min-height: 20px; } - + .income-bracket-label { font-size: 1.3rem; color: var(--dark-color); @@ -176,6 +253,7 @@ font-weight: 600; margin-bottom: 0.25rem; } + .income-bracket-description { font-size: 0.9rem; color: #495057; @@ -185,60 +263,342 @@ min-height: 38px; line-height: 1.4; } - - .slider { width: 100%; height: 12px; border-radius: 6px; background: #dee2e6; outline: none; -webkit-appearance: none; appearance: none; margin-bottom: 0.5rem; } - .slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 30px; height: 30px; border-radius: 50%; background: var(--primary-color); cursor: pointer; border: 5px solid white; box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), 0.5); } - .slider::-moz-range-thumb { width: 30px; height: 30px; border-radius: 50%; background: var(--primary-color); cursor: pointer; border: 5px solid white; box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), 0.5); } - .slider-labels { display: flex; justify-content: space-between; font-size: 0.85rem; color: #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; } - - .lifestyle-category { background: #ffffff; 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); transition: all 0.3s ease; } - .lifestyle-category:hover { transform: translateY(-4px) scale(1.015); box-shadow: 0 12px 35px rgba(0, 0, 0, 0.1); } - .category-title { font-family: 'Roboto Slab', serif; font-size: 1.5rem; font-weight: 700; color: var(--dark-color); margin-bottom: 1.25rem; display: flex; align-items: center; gap: 0.85rem; } - .category-icon { font-size: 2rem; color: var(--primary-color); opacity: 0.8; } - + + .slider { + width: 100%; + height: 12px; + border-radius: 6px; + background: #dee2e6; + outline: none; + -webkit-appearance: none; + appearance: none; + margin-bottom: 0.5rem; + } + + .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 30px; + height: 30px; + border-radius: 50%; + background: var(--primary-color); + cursor: pointer; + border: 5px solid white; + box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), 0.5); + } + + .slider::-moz-range-thumb { + width: 30px; + height: 30px; + border-radius: 50%; + background: var(--primary-color); + cursor: pointer; + border: 5px solid white; + box-shadow: 0 3px 8px rgba(var(--primary-color-rgb), 0.5); + } + + .slider-labels { + display: flex; + justify-content: space-between; + font-size: 0.85rem; + color: #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; + } + + .lifestyle-category { + background: #ffffff; + 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); + transition: all 0.3s ease; + } + + .lifestyle-category:hover { + transform: translateY(-4px) scale(1.015); + box-shadow: 0 12px 35px rgba(0, 0, 0, 0.1); + } + + .category-title { + font-family: 'Roboto Slab', serif; + font-size: 1.5rem; + font-weight: 700; + color: var(--dark-color); + margin-bottom: 1.25rem; + display: flex; + align-items: center; + gap: 0.85rem; + } + + .category-icon { + font-size: 2rem; + color: var(--primary-color); + opacity: 0.8; + } + .category-item-icon { - font-size: 1.6rem; - color: var(--primary-color); /* Will be overridden by category specific color */ + font-size: 1.6rem; + color: var(--primary-color); + /* Will be overridden by category specific color */ opacity: 0.75; margin-right: 1rem; vertical-align: middle; flex-shrink: 0; - width: 32px; + width: 32px; text-align: center; } - .category-item { background: var(--light-color); border-radius: 10px; padding: 1.25rem; margin-bottom: 1rem; border: 1px solid #dee2e6; display: flex; align-items: flex-start; transition: background-color 0.2s ease; } - .category-item:hover { background-color: #e9ecef; } - .category-item .item-content-wrapper { flex-grow: 1; } - - .item-title { font-weight: 600; font-size: 1.0rem; color: var(--dark-color); margin-bottom: 0.3rem; display: flex; align-items: center; } - .item-description { color: #495057; font-size: 0.85rem; line-height: 1.5; margin-bottom: 0.3rem; } - .item-financial-note { color: #555; font-size: 0.8rem; font-style: italic; margin-top: 0.4rem; opacity: 0.9; } - .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; } + + .category-item { + background: var(--light-color); + border-radius: 10px; + padding: 1.25rem; + margin-bottom: 1rem; + border: 1px solid #dee2e6; + display: flex; + align-items: flex-start; + transition: background-color 0.2s ease; + } + + .category-item:hover { + background-color: #e9ecef; + } + + .category-item .item-content-wrapper { + flex-grow: 1; + } + + .item-title { + font-weight: 600; + font-size: 1.0rem; + color: var(--dark-color); + margin-bottom: 0.3rem; + display: flex; + align-items: center; + } + + .item-description { + color: #495057; + font-size: 0.85rem; + line-height: 1.5; + margin-bottom: 0.3rem; + } + + .item-financial-note { + color: #555; + font-size: 0.8rem; + font-style: italic; + margin-top: 0.4rem; + opacity: 0.9; + } + + .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; @@ -249,7 +609,7 @@ color: white; padding: 1rem 2rem; border-radius: 50px; - box-shadow: 0 5px 20px rgba(0,0,0,0.25); + 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); z-index: 1050; font-size: 1.1rem; @@ -258,321 +618,409 @@ align-items: center; gap: 0.75rem; } + #milestone-popup.show { bottom: 25px; } - - @keyframes itemEntryAnimation { from { opacity: 0; transform: translateX(-20px) scale(0.98); } to { opacity: 1; transform: translateX(0) scale(1); } } - .animate-item-entry { animation: itemEntryAnimation 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; } - .fade-in { animation: fadeIn 0.6s ease-in-out; } - @keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } } + + .highlight-update { + animation: highlight-animation 1.2s ease-out; + } + + @keyframes highlight-animation { + 0% { + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); + transform: scale(1); + } + + 30% { + box-shadow: 0 10px 40px rgba(255, 193, 7, 0.7); + /* Bright yellow glow */ + transform: scale(1.02); + } + + 100% { + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); + transform: scale(1); + } + } + + @keyframes itemEntryAnimation { + from { + opacity: 0; + transform: translateX(-20px) scale(0.98); + } + + to { + opacity: 1; + transform: translateX(0) scale(1); + } + } + + .animate-item-entry { + animation: itemEntryAnimation 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; + } + + .fade-in { + animation: fadeIn 0.6s ease-in-out; + } + + @keyframes fadeIn { + from { + opacity: 0; + transform: translateY(15px); + } + + to { + opacity: 1; + transform: translateY(0); + } + } </style> - <meta content="images/lifestyle-calculator.png" property="og:image"/><meta content="images/lifestyle-calculator.png" name="twitter:image"/> + <meta content="images/lifestyle-calculator.png" property="og:image" /> + <meta content="images/lifestyle-calculator.png" name="twitter:image" /> </head> + <body> -<div class="background-container"> - <div id="background-layer-1"></div> - <div id="background-layer-2"></div> -</div> - -<main class="container"> - <div class="main-container"> - <div class="text-center"> - <h1 class="page-title">💰 Lifestyle Calculator</h1> - <p class="page-subtitle">Slide to see what your income *really* affords for your family!</p> - <p class="calculator-note"><i class="bi bi-info-circle-fill"></i> This calculator estimates lifestyle based on **annual family budget**, not net worth. Cost of Living (COLA) adjustment provides regional context.</p> - </div> + <div class="background-container"> + <div id="background-layer-1"></div> + <div id="background-layer-2"></div> + </div> - <div class="controls-container"> - <div class="row align-items-center"> - <div class="col-lg-8"> - <div class="row"> - <div class="col-md-6 mb-3 mb-md-0"> - <label class="form-label" for="colaSelector">Cost of Living Adjustment (COLA):</label> - <select class="form-select form-select-sm" id="colaSelector"></select> + <main class="container"> + <div class="main-container"> + <div class="text-center"> + <h1 class="page-title">💰 Lifestyle Calculator</h1> + <p class="page-subtitle">Slide to see what your income *really* affords for your family!</p> + <p class="calculator-note"><i class="bi bi-info-circle-fill"></i> This calculator estimates lifestyle + based on **annual family budget**, not net worth. Cost of Living (COLA) adjustment provides regional + context.</p> + </div> + + <div class="controls-container"> + <div class="row align-items-center"> + <div class="col-lg-8"> + <div class="row"> + <div class="col-md-6 mb-3 mb-md-0"> + <label class="form-label" for="colaSelector">Cost of Living Adjustment (COLA):</label> + <select class="form-select form-select-sm" id="colaSelector"></select> + </div> + <div class="col-md-6"> + <label class="form-label" for="familySizeSelector">Household / Life Stage:</label> + <select class="form-select form-select-sm" id="familySizeSelector"> + <option value="single">Single</option> + <option value="couple">Couple / DINK</option> + <option value="family" selected>Family with Kids</option> + </select> + </div> </div> - <div class="col-md-6"> - <label class="form-label" for="familySizeSelector">Household / Life Stage:</label> - <select class="form-select form-select-sm" id="familySizeSelector"> - <option value="single">Single</option> - <option value="couple">Couple / DINK</option> - <option value="family" selected>Family with Kids</option> - </select> + <div class="income-display mt-3" id="incomeDisplay">$50,000</div> + <div class="effective-income-display" id="effectiveIncomeDisplay"></div> + <div class="income-bracket-label" id="incomeBracketLabel">Lower Middle Class</div> + <div class="income-bracket-description" id="incomeBracketDescription">Making ends meet, with a + little extra for fun.</div> + <input class="slider" id="incomeSlider" max="14" min="0" type="range" value="2" /> + <div class="slider-labels"> + <span>$20K</span> + <span>$1M</span> + <span>$25M</span> </div> </div> - <div class="income-display mt-3" id="incomeDisplay">$50,000</div> - <div class="effective-income-display" id="effectiveIncomeDisplay"></div> - <div class="income-bracket-label" id="incomeBracketLabel">Lower Middle Class</div> - <div class="income-bracket-description" id="incomeBracketDescription">Making ends meet, with a little extra for fun.</div> - <input class="slider" id="incomeSlider" max="14" min="0" type="range" value="2"/> - <div class="slider-labels"> - <span>$20K</span> - <span>$1M</span> - <span>$25M</span> + <div class="col-lg-4 d-flex justify-content-center align-items-center mt-3 mt-lg-0"> + <canvas id="budgetChart" style="max-height: 250px; max-width: 250px;"></canvas> </div> </div> - <div class="col-lg-4 d-flex justify-content-center align-items-center mt-3 mt-lg-0"> - <canvas id="budgetChart" style="max-height: 250px; max-width: 250px;"></canvas> + </div> + + <div class="row"> + <div class="col-lg-6"> + <div class="lifestyle-category dining fade-in"> + <div class="category-title"><i class="bi bi-egg-fried category-icon"></i> Dining & Food</div> + <div id="dining-content"></div> + </div> + <div class="lifestyle-category travel fade-in"> + <div class="category-title"><i class="bi bi-airplane category-icon"></i> Travel & Vacations + </div> + <div id="travel-content"></div> + </div> + <div class="lifestyle-category housing fade-in"> + <div class="category-title"><i class="bi bi-house-door category-icon"></i> Housing & Living + </div> + <div id="housing-content"></div> + </div> + <div class="lifestyle-category transportation fade-in"> + <div class="category-title"><i class="bi bi-car-front-fill category-icon"></i> Transportation + </div> + <div id="transportation-content"></div> + </div> + </div> + <div class="col-lg-6"> + <div class="lifestyle-category entertainment fade-in"> + <div class="category-title"><i class="bi bi-dice-5-fill category-icon"></i> Entertainment & + Hobbies</div> + <div id="entertainment-content"></div> + </div> + <div class="lifestyle-category education fade-in"> + <div class="category-title"><i class="bi bi-book-half category-icon"></i> Education & Growth + </div> + <div id="education-content"></div> + </div> + <div class="lifestyle-category health-wellness fade-in"> + <div class="category-title"><i class="bi bi-heart-pulse-fill category-icon"></i> Health & + Wellness</div> + <div id="health-wellness-content"></div> + </div> + <div class="lifestyle-category services fade-in"> + <div class="category-title"><i class="bi bi-person-check-fill category-icon"></i> Personal + Services</div> + <div id="services-content"></div> + </div> + <div class="lifestyle-category financial-planning fade-in"> + <div class="category-title"><i class="bi bi-piggy-bank category-icon"></i> Financial Planning & + Investments</div> + <div id="financial-services-content"></div> + </div> </div> </div> </div> + </main> - <div class="row"> - <div class="col-lg-6"> - <div class="lifestyle-category dining fade-in"><div class="category-title"><i class="bi bi-egg-fried category-icon"></i> Dining & Food</div><div id="dining-content"></div></div> - <div class="lifestyle-category travel fade-in"><div class="category-title"><i class="bi bi-airplane category-icon"></i> Travel & Vacations</div><div id="travel-content"></div></div> - <div class="lifestyle-category housing fade-in"><div class="category-title"><i class="bi bi-house-door category-icon"></i> Housing & Living</div><div id="housing-content"></div></div> - <div class="lifestyle-category transportation fade-in"><div class="category-title"><i class="bi bi-car-front-fill category-icon"></i> Transportation</div><div id="transportation-content"></div></div> - </div> - <div class="col-lg-6"> - <div class="lifestyle-category entertainment fade-in"><div class="category-title"><i class="bi bi-dice-5-fill category-icon"></i> Entertainment & Hobbies</div><div id="entertainment-content"></div></div> - <div class="lifestyle-category education fade-in"><div class="category-title"><i class="bi bi-book-half category-icon"></i> Education & Growth</div><div id="education-content"></div></div> - <div class="lifestyle-category health-wellness fade-in"><div class="category-title"><i class="bi bi-heart-pulse-fill category-icon"></i> Health & Wellness</div><div id="health-wellness-content"></div></div> - <div class="lifestyle-category services fade-in"><div class="category-title"><i class="bi bi-person-check-fill category-icon"></i> Personal Services</div><div id="services-content"></div></div> - <div class="lifestyle-category financial-planning fade-in"><div class="category-title"><i class="bi bi-piggy-bank category-icon"></i> Financial Planning & Investments</div><div id="financial-services-content"></div></div> - </div> + <footer class="container text-center py-3"> + <div> + <a class="mx-2 link-secondary" href="https://www.linkedin.com/in/davidveksler/" target="_blank" + title="David Veksler on LinkedIn"> + <i class="bi bi-linkedin"></i> LinkedIn + </a> + <a class="mx-2 link-secondary" href="https://cheatsheets.davidveksler.com/" title="Browse All Cheatsheets"> + <i class="bi bi-collection"></i> All Cheatsheets + </a> </div> + </footer> + + <div id="milestone-popup"> + <span id="milestone-emoji"></span> + <span>Milestone Unlocked: <strong id="milestone-text"></strong></span> </div> -</main> - -<footer class="container text-center py-3"> - <div> - <a class="mx-2 link-secondary" href="https://www.linkedin.com/in/davidveksler/" target="_blank" title="David Veksler on LinkedIn"> - <i class="bi bi-linkedin"></i> LinkedIn - </a> - <a class="mx-2 link-secondary" href="https://cheatsheets.davidveksler.com/" title="Browse All Cheatsheets"> - <i class="bi bi-collection"></i> All Cheatsheets - </a> - </div> -</footer> - -<div id="milestone-popup"> - <span id="milestone-emoji"></span> - <span>Milestone Unlocked: <strong id="milestone-text"></strong></span> -</div> - -<!-- Main Application Script --> -<script> -let incomeBrackets = []; -let lifestyleData = {}; -let costOfLivingFactors = []; -let currentCurrencySymbol = "$"; -let budgetChart; -let lastSliderValue = -1; -let lastShownMilestone = -1; - -const slider = document.getElementById('incomeSlider'); -const incomeDisplayEl = document.getElementById('incomeDisplay'); -const effectiveIncomeDisplayEl = document.getElementById('effectiveIncomeDisplay'); -const incomeBracketLabelEl = document.getElementById('incomeBracketLabel'); -const incomeBracketDescEl = document.getElementById('incomeBracketDescription'); -const colaSelectorEl = document.getElementById('colaSelector'); -const familySizeSelectorEl = document.getElementById('familySizeSelector'); - -// Background Layers -const bgLayer1 = document.getElementById('background-layer-1'); -const bgLayer2 = document.getElementById('background-layer-2'); -let activeBgLayer = 1; - -function formatIncome(income, symbol = "$") { - if (income >= 1000000) return `${symbol}${(income / 1000000).toFixed(income % 1000000 === 0 ? 0 : (income < 10000000 ? 1 : 0))}M`; - if (income >= 1000) return `${symbol}${(income / 1000).toFixed(0)}K`; - return `${symbol}${income.toLocaleString()}`; -} - -function updateDynamicBackground(bracketIndex) { - let themeClass = ''; - 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'; - - if (activeBgLayer === 1) { - bgLayer2.className = `background-layer ${themeClass}`; - bgLayer1.style.opacity = 0; - bgLayer2.style.opacity = 1; - activeBgLayer = 2; - } else { - bgLayer1.className = `background-layer ${themeClass}`; - bgLayer1.style.opacity = 1; - bgLayer2.style.opacity = 0; - activeBgLayer = 1; - } -} - -function updateBudgetChart(bracketIndex) { - const bracketData = incomeBrackets[bracketIndex]; - if (!bracketData || !bracketData.budgetAllocation) return; - - 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 chartConfig = { - type: 'doughnut', - data: { - labels: labels, - datasets: [{ - label: 'Budget Allocation', - data: data, - backgroundColor: colorPalette, - borderColor: '#ffffff', - borderWidth: 3, - hoverBorderColor: '#f8f9fa', - hoverBorderWidth: 1, - hoverOffset: 8 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - cutout: '70%', // Makes the doughnut a bit thinner and more modern - animation: { - animateScale: true, - animateRotate: true - }, - plugins: { - legend: { - display: true, - position: 'right', // Display labels in a legend - align: 'center', - labels: { - boxWidth: 12, - padding: 15, - font: { - size: 11, - family: "'Inter', sans-serif" - }, - color: '#495057' - } + + <!-- Main Application Script --> + <script> + let incomeBrackets = []; + let lifestyleData = {}; + let costOfLivingFactors = []; + let currentCurrencySymbol = "$"; + let budgetChart; + let lastSliderValue = -1; + let lastShownMilestone = -1; + + const slider = document.getElementById('incomeSlider'); + const incomeDisplayEl = document.getElementById('incomeDisplay'); + const effectiveIncomeDisplayEl = document.getElementById('effectiveIncomeDisplay'); + const incomeBracketLabelEl = document.getElementById('incomeBracketLabel'); + const incomeBracketDescEl = document.getElementById('incomeBracketDescription'); + const colaSelectorEl = document.getElementById('colaSelector'); + const familySizeSelectorEl = document.getElementById('familySizeSelector'); + + // Background Layers + const bgLayer1 = document.getElementById('background-layer-1'); + const bgLayer2 = document.getElementById('background-layer-2'); + let activeBgLayer = 1; + + function formatIncome(income, symbol = "$") { + if (income >= 1000000) return `${symbol}${(income / 1000000).toFixed(income % 1000000 === 0 ? 0 : (income < 10000000 ? 1 : 0))}M`; + if (income >= 1000) return `${symbol}${(income / 1000).toFixed(0)}K`; + return `${symbol}${income.toLocaleString()}`; + } + + function updateDynamicBackground(bracketIndex) { + let themeClass = ''; + 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'; + + if (activeBgLayer === 1) { + bgLayer2.className = `background-layer ${themeClass}`; + bgLayer1.style.opacity = 0; + bgLayer2.style.opacity = 1; + activeBgLayer = 2; + } else { + bgLayer1.className = `background-layer ${themeClass}`; + bgLayer1.style.opacity = 1; + bgLayer2.style.opacity = 0; + activeBgLayer = 1; + } + } + + function updateBudgetChart(bracketIndex) { + const bracketData = incomeBrackets[bracketIndex]; + if (!bracketData || !bracketData.budgetAllocation) return; + + 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 chartConfig = { + type: 'doughnut', + data: { + labels: labels, + datasets: [{ + label: 'Budget Allocation', + data: data, + backgroundColor: colorPalette, + borderColor: '#ffffff', + borderWidth: 3, + hoverBorderColor: '#f8f9fa', + hoverBorderWidth: 1, + hoverOffset: 8 + }] }, - tooltip: { - enabled: true, - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleFont: { - size: 14, - weight: 'bold' + options: { + responsive: true, + maintainAspectRatio: false, + cutout: '70%', // Makes the doughnut a bit thinner and more modern + animation: { + animateScale: true, + animateRotate: true }, - bodyFont: { - size: 12 - }, - padding: 12, - cornerRadius: 8, - callbacks: { - label: function(context) { - let label = context.label || ''; - if (label) { - label += ': '; + plugins: { + legend: { + display: true, + position: 'right', // Display labels in a legend + align: 'center', + labels: { + boxWidth: 12, + padding: 15, + font: { + size: 11, + family: "'Inter', sans-serif" + }, + color: '#495057' } - if (context.parsed !== null) { - label += context.parsed + '%'; + }, + tooltip: { + enabled: true, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + 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 + '%'; + } + return label; + } } - return label; + } + }, + layout: { + padding: { + left: 0, + right: 10 // Add padding to ensure legend labels are not cut off } } } - }, - layout: { - padding: { - left: 0, - right: 10 // Add padding to ensure legend labels are not cut off - } + }; + + 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 + } else { + const ctx = document.getElementById('budgetChart').getContext('2d'); + budgetChart = new Chart(ctx, chartConfig); } } - }; - - 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 - } else { - const ctx = document.getElementById('budgetChart').getContext('2d'); - budgetChart = new Chart(ctx, chartConfig); - } -} - -function showMilestone(bracketIndex) { - const bracket = incomeBrackets[bracketIndex]; - if (bracket.milestone && bracketIndex > lastSliderValue && bracketIndex > lastShownMilestone) { - document.getElementById('milestone-emoji').textContent = bracket.milestone.emoji; - document.getElementById('milestone-text').textContent = bracket.milestone.title; - const popup = document.getElementById('milestone-popup'); - popup.classList.add('show'); - lastShownMilestone = bracketIndex; - setTimeout(() => { - popup.classList.remove('show'); - }, 3500); - } -} - -function updateLifestyle() { - if (!incomeBrackets || incomeBrackets.length === 0 || !costOfLivingFactors || costOfLivingFactors.length === 0) return; - - const selectedBracketIndex = parseInt(slider.value); - const baseBracket = incomeBrackets[selectedBracketIndex]; - if (!baseBracket) return; - - const selectedColaFactor = parseFloat(colaSelectorEl.options[colaSelectorEl.selectedIndex].dataset.factor); - currentCurrencySymbol = colaSelectorEl.options[colaSelectorEl.selectedIndex].dataset.currency; - const selectedFamilySize = familySizeSelectorEl.value; - - const effectiveIncome = baseBracket.income / selectedColaFactor; - - incomeDisplayEl.textContent = formatIncome(baseBracket.income, currentCurrencySymbol); - if (selectedColaFactor !== 1.0) { - effectiveIncomeDisplayEl.textContent = `(Effectively ~${formatIncome(effectiveIncome, currentCurrencySymbol)} in selected city)`; - } else { - effectiveIncomeDisplayEl.textContent = ""; - } - incomeBracketLabelEl.textContent = baseBracket.label; - incomeBracketDescEl.textContent = baseBracket.description; - - updateDynamicBackground(selectedBracketIndex); - updateBudgetChart(selectedBracketIndex); - showMilestone(selectedBracketIndex); - - Object.keys(lifestyleData).forEach(category => { - const categoryData = lifestyleData[category]; - const contentDivId = `${category.replace(/([A-Z])/g, '-$1').toLowerCase()}-content`; - const contentDiv = document.getElementById(contentDivId); - - if (!contentDiv) { - console.warn(`Content div not found for category ID: ${contentDivId}`); - return; - } - - let itemsToDisplay = []; - for (let i = categoryData.length - 1; i >= 0; i--) { - if (selectedBracketIndex >= categoryData[i].min) { - const filteredItems = categoryData[i].items.filter(item => { - if (!item.modifiers || !item.modifiers.familySize) return true; // Show if no modifier - return item.modifiers.familySize.includes(selectedFamilySize); - }); - itemsToDisplay = filteredItems; - break; + + function showMilestone(bracketIndex) { + const bracket = incomeBrackets[bracketIndex]; + if (bracket.milestone && bracketIndex > lastSliderValue && bracketIndex > lastShownMilestone) { + document.getElementById('milestone-emoji').textContent = bracket.milestone.emoji; + document.getElementById('milestone-text').textContent = bracket.milestone.title; + const popup = document.getElementById('milestone-popup'); + popup.classList.add('show'); + lastShownMilestone = bracketIndex; + setTimeout(() => { + popup.classList.remove('show'); + }, 3500); } } - - const itemsHTML = itemsToDisplay.map((item, idx) => { - let 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>` : ''; - let itemIconHTML = item.itemIcon ? `<i class="bi ${item.itemIcon} category-item-icon"></i>` : '<i class="bi bi-record-circle-fill category-item-icon"></i>'; - let financialNoteHTML = item.financialNote ? `<div class="item-financial-note">${item.financialNote}</div>` : ''; - return ` + + function updateLifestyle() { + if (!incomeBrackets || incomeBrackets.length === 0 || !costOfLivingFactors || costOfLivingFactors.length === 0) return; + + const selectedBracketIndex = parseInt(slider.value); + const baseBracket = incomeBrackets[selectedBracketIndex]; + if (!baseBracket) return; + + const selectedColaFactor = parseFloat(colaSelectorEl.options[colaSelectorEl.selectedIndex].dataset.factor); + currentCurrencySymbol = colaSelectorEl.options[colaSelectorEl.selectedIndex].dataset.currency; + const selectedFamilySize = familySizeSelectorEl.value; + + const effectiveIncome = baseBracket.income / selectedColaFactor; + + incomeDisplayEl.textContent = formatIncome(baseBracket.income, currentCurrencySymbol); + effectiveIncomeDisplayEl.textContent = selectedColaFactor !== 1.0 ? `(Effectively ~${formatIncome(effectiveIncome, currentCurrencySymbol)} in selected city)` : ""; + incomeBracketLabelEl.textContent = baseBracket.label; + incomeBracketDescEl.textContent = baseBracket.description; + + updateDynamicBackground(selectedBracketIndex); + updateBudgetChart(selectedBracketIndex); + showMilestone(selectedBracketIndex); + + Object.keys(lifestyleData).forEach(category => { + const categoryData = lifestyleData[category]; + const contentDivId = `${category.replace(/([A-Z])/g, '-$1').toLowerCase()}-content`; + const contentDiv = document.getElementById(contentDivId); + + if (!contentDiv) { + console.warn(`Content div not found for category ID: ${contentDivId}`); + return; + } + + const oldHTML = contentDiv.innerHTML; // Store current HTML + + let itemsToDisplay = []; + for (let i = categoryData.length - 1; i >= 0; i--) { + if (selectedBracketIndex >= categoryData[i].min) { + itemsToDisplay = categoryData[i].items.filter(item => { + if (!item.modifiers || !item.modifiers.familySize) return true; + return item.modifiers.familySize.includes(selectedFamilySize); + }); + break; + } + } + + 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 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 ` <div class="category-item animate-item-entry" style="animation-delay: ${idx * 50}ms;"> ${itemIconHTML} <div class="item-content-wrapper"> @@ -583,66 +1031,79 @@ function updateLifestyle() { </div> </div> `; - }).join(''); - contentDiv.innerHTML = itemsHTML; - }); - lastSliderValue = selectedBracketIndex; -} - -function initializeApp() { - costOfLivingFactors.forEach(factor => { - const option = document.createElement('option'); - option.value = factor.city; - option.textContent = factor.city; - option.dataset.factor = factor.factor; - 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(); - - slider.addEventListener('input', updateLifestyle); - colaSelectorEl.addEventListener('change', updateLifestyle); - familySizeSelectorEl.addEventListener('change', updateLifestyle); - - document.querySelectorAll('.lifestyle-category.fade-in').forEach((category, index) => { - category.style.opacity = '0'; - category.style.transform = 'translateY(20px)'; - setTimeout(() => { - category.style.transition = 'opacity 0.5s ease-out, transform 0.5s ease-out'; - category.style.opacity = '1'; - category.style.transform = 'translateY(0)'; - }, index * 100 + 50); - }); -} - -document.addEventListener('DOMContentLoaded', () => { - fetch('https://cheatsheets.davidveksler.com/lifestyle-calculator-data.json') - .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) - .then(data => { - incomeBrackets = data.incomeBrackets; - lifestyleData = data.lifestyleData; - costOfLivingFactors = data.costOfLivingFactors; - slider.max = incomeBrackets.length - 1; - initializeApp(); - }) - .catch(error => { - console.error("Could not fetch or parse lifestyle data:", error); - const mainContainer = document.querySelector('.main-container'); - if(mainContainer) { mainContainer.innerHTML = `<div class="alert alert-danger" role="alert"><strong>Error:</strong> Could not load lifestyle data. Please ensure 'lifestyle-calculator-data.json' is accessible.</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.add('highlight-update'); + } + } + }); + lastSliderValue = selectedBracketIndex; + } + + function initializeApp() { + costOfLivingFactors.forEach(factor => { + const option = document.createElement('option'); + option.value = factor.city; + option.textContent = factor.city; + option.dataset.factor = factor.factor; + 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(); + + slider.addEventListener('input', updateLifestyle); + colaSelectorEl.addEventListener('change', updateLifestyle); + familySizeSelectorEl.addEventListener('change', updateLifestyle); + + document.querySelectorAll('.lifestyle-category.fade-in').forEach((category, index) => { + category.style.opacity = '0'; + category.style.transform = 'translateY(20px)'; + setTimeout(() => { + category.style.transition = 'opacity 0.5s ease-out, transform 0.5s ease-out'; + category.style.opacity = '1'; + category.style.transform = 'translateY(0)'; + }, index * 100 + 50); + }); + } + + document.addEventListener('DOMContentLoaded', () => { + fetch('https://cheatsheets.davidveksler.com/lifestyle-calculator-data.json') + .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) + .then(data => { + incomeBrackets = data.incomeBrackets; + lifestyleData = data.lifestyleData; + costOfLivingFactors = data.costOfLivingFactors; + slider.max = incomeBrackets.length - 1; + initializeApp(); + }) + .catch(error => { + console.error("Could not fetch or parse lifestyle data:", error); + const mainContainer = document.querySelector('.main-container'); + if (mainContainer) { mainContainer.innerHTML = `<div class="alert alert-danger" role="alert"><strong>Error:</strong> Could not load lifestyle data. Please ensure 'lifestyle-calculator-data.json' is accessible.</div>`; } + }); }); -}); -</script> -<script crossorigin="anonymous" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> + </script> + <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