From 78cfb14a77fb62b9795910f88cd671ea2e53c3dd Mon Sep 17 00:00:00 2001 From: Dorian Date: Sun, 10 May 2026 10:39:08 +0100 Subject: [PATCH] feat: budget total row + timeline polish + paint slider thumb + auto-advance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Budget panel - Removed the inner "Budget Allocation" subtitle and the inline total from bm-title — the outer panel header "Budget Plan" already names the section, the inner subtitle was redundant. - Added a Total Allocation row at the end of the breakdown using the same .bcat layout as the category lines so label + amount columns align. Top divider + heavier weight mark it as the summary line. DE: Gesamtbudget. The bar slot is collapsed to height:0 on the total row so only label + amount render. - Bumped .bm-title (when it existed) and .bcat-label / .bcat-cost so the budget figures read at a comfortable size. Timeline - Each .tl-item gets generous top + bottom padding (14px / 28px) so the steps read as distinct moments. Padding is consistent across every item so the connector positioning stays stable. - Connector line is now a light dotted left-border drawn on a zero-width pseudo, anchored top:46px / bottom:-14px so the dotted line crosses the gap between items and reaches each next dot. - .tl-when (step title) typeface switched from Space Mono to Barlow (matching the CTA buttons), weight 600, 0.16em tracking, dark green paint colour (#2a3010), vertically centred against the 32px dot via min-height + flex centring so the title sits at the dot's mid-line. Quiz - Slider thumb is now a CSS-painted dark green circle: linear gradient (#3a4220 → #2a3010 → #161a08) with an inset cream highlight and a soft drop shadow + halo ring, approximating the SVG paint gloss filter the CTA buttons use. Both -webkit and -moz pseudo elements styled. - Single-choice questions now auto-advance 220ms after a tap so the Continue button is no longer needed on those steps. renderQuestion drops the Continue button on single-choice, keeps Back when not on the first question. .quiz-footer:empty hides cleanly on the very first single-choice screen. Result panels - .result-panel-body top padding bumped from 0 to 20px so expanded content has clear breathing room below the summary header. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/App.vue | 34 +++++++++++++---- src/styles.css | 99 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/App.vue b/src/App.vue index 19ced96..b72ab17 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1096,12 +1096,15 @@ function renderQuestion() { html += `` const isLast = currentQ === QUESTIONS.length - 1 - const nav = `
- ${currentQ > 0 ? `` : ''} - -
` + // Single-choice steps auto-advance on tap, so the Continue button is + // redundant — render only Back (if available) on those screens. Multi + // and slider steps still need the explicit Continue. + const showContinue = q.type !== 'single' + const showBack = currentQ > 0 + const parts = [] + if (showBack) parts.push(``) + if (showContinue) parts.push(``) + const nav = parts.length ? `
${parts.join('')}
` : '' document.getElementById('question-container').innerHTML = html document.getElementById('quiz-footer').innerHTML = nav @@ -1117,8 +1120,16 @@ function selectOption(qid, val, el) { answers[qid] = val document.querySelectorAll('.q-opt').forEach(o => o.classList.remove('selected')) el.classList.add('selected') - document.getElementById('btn-next').disabled = false + const nextBtn = document.getElementById('btn-next') + if (nextBtn) nextBtn.disabled = false saveState() + // Single-choice → auto-advance. Short delay so the user sees the green + // selected state register before the screen flips. They can always go + // Back to revise — the answer is preserved in `answers[qid]` and + // re-applied as `selected` when renderQuestion runs again. + setTimeout(() => { + if (answers[qid] === val) nextQ() + }, 220) } function toggleMulti(qid, val, el) { @@ -1438,7 +1449,6 @@ function renderBudgetMeter() { { label: t('bcat_sanitation'), pct: 5, color: '#D4A820' }, ] let html = `
-
${t('budget_title')} — ${lang==='de'?'Gesamt':'Total'}: €${budget.toLocaleString()}
` cats.forEach(c => { html += `
@@ -1447,6 +1457,14 @@ function renderBudgetMeter() {
€${Math.round(budget*c.pct/100)}
` }) + // Total row at the end of the table — same .bcat layout so alignment + // matches the per-category lines. + const totalLabel = lang === 'de' ? 'Gesamtbudget' : 'Total Allocation' + html += `
+
${totalLabel}
+ +
€${budget.toLocaleString()}
+
` html += `
` document.getElementById('budget-meter-container').innerHTML = html } diff --git a/src/styles.css b/src/styles.css index 6931329..f0ed921 100644 --- a/src/styles.css +++ b/src/styles.css @@ -482,7 +482,33 @@ body.quiz-active .site-header { display: none; } .slider-labels { display: flex; justify-content: space-between; margin-bottom: 12px; } .slider-labels span { font-size: 12px; color: var(--text-dim); font-family: var(--font-mono); } input[type=range] { width: 100%; -webkit-appearance: none; appearance: none; height: 4px; background: var(--muted); border-radius: 99px; outline: none; cursor: pointer; } -input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; height: 22px; border-radius: 50%; background: var(--red); cursor: pointer; box-shadow: 0 2px 16px rgba(90,154,120,0.3); } +/* Slider thumb — dark green paint style. CSS-only approximation of the + SVG paintGlossBtn filter the CTA buttons use: a #2a3010 base with a + linear gradient simulating the embossed top-highlight / bottom-shadow, + plus an inset top stroke and a soft drop shadow. */ +input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + width: 22px; height: 22px; + border-radius: 50%; + background: linear-gradient(180deg, #3a4220 0%, #2a3010 55%, #161a08 100%); + border: 1px solid rgba(0,0,0,0.40); + cursor: pointer; + box-shadow: + inset 0 1px 0 rgba(255,236,200,0.22), + 0 2px 6px rgba(0,0,0,0.30), + 0 0 0 4px rgba(42,48,16,0.10); +} +input[type=range]::-moz-range-thumb { + width: 22px; height: 22px; + border-radius: 50%; + background: linear-gradient(180deg, #3a4220 0%, #2a3010 55%, #161a08 100%); + border: 1px solid rgba(0,0,0,0.40); + cursor: pointer; + box-shadow: + inset 0 1px 0 rgba(255,236,200,0.22), + 0 2px 6px rgba(0,0,0,0.30), + 0 0 0 4px rgba(42,48,16,0.10); +} .slider-val { text-align: center; font-family: var(--font-display); font-weight: 800; font-size: 32px; color: var(--white); margin-top: 16px; } .slider-val span { font-size: 18px; color: var(--text-dim); margin-left: 4px; } @@ -703,22 +729,60 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; itself is stripped to transparent in the panel-body override (line ~615), so only the inner element colours need adjusting here. */ .budget-meter { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px; margin-bottom: 20px; animation: fadeUp 0.4s 0.2s ease both; } -.bm-title { font-family: var(--font-display); font-weight: 700; font-size: 16px; color: var(--text); margin-bottom: 16px; } +/* Inner budget title matches the panel header typography (.rp-title: + var(--font-body), 700, 0.02em tracking) but a touch bigger so the + total reads as the primary stat inside the panel. */ +.bm-title { + font-family: var(--font-body); + font-weight: 700; + font-size: 19px; + letter-spacing: 0.02em; + line-height: 1.3; + color: var(--text); + margin-bottom: 16px; +} .budget-cats { display: flex; flex-direction: column; gap: 10px; } .bcat { display: flex; align-items: center; gap: 10px; } -.bcat-label { font-size: 13px; color: var(--text-dim); width: 100px; flex-shrink: 0; } +.bcat-label { font-size: 16px; color: var(--text-dim); width: 110px; flex-shrink: 0; } .bcat-bar-wrap { flex: 1; height: 6px; background: rgba(0,0,0,0.08); border-radius: 99px; overflow: hidden; } .bcat-bar { height: 100%; border-radius: 99px; transition: width 0.8s cubic-bezier(0.4,0,0.2,1); } -.bcat-cost { font-family: var(--font-mono); font-size: 12px; color: var(--text); width: 60px; text-align: right; flex-shrink: 0; } +.bcat-cost { font-family: var(--font-mono); font-size: 15px; color: var(--text); width: 72px; text-align: right; flex-shrink: 0; } +/* Total row — sums the breakdown above. Same .bcat layout so the label + and amount align with the category rows, plus a top divider and a + touch more weight to set it apart as the summary line. */ +.bcat-total { + margin-top: 6px; + padding-top: 10px; + border-top: 1px solid rgba(0,0,0,0.08); +} +.bcat-total .bcat-label { color: var(--text); font-weight: 600; } +.bcat-total .bcat-cost { color: var(--text); font-weight: 700; } +.bcat-total .bcat-bar-wrap { background: transparent; height: 0; } /* Timeline — same dark-on-light flip. Connector line uses a subtle dark border colour so it reads on the white-paint panel. */ .timeline { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px; margin-bottom: 20px; animation: fadeUp 0.4s 0.25s ease both; } .tl-title { font-family: var(--font-display); font-weight: 700; font-size: 16px; color: var(--text); margin-bottom: 16px; } .tl-items { display: flex; flex-direction: column; gap: 0; } -.tl-item { display: flex; gap: 16px; padding-bottom: 16px; position: relative; } -.tl-item:last-child { padding-bottom: 0; } -.tl-item:not(:last-child)::before { content: ''; position: absolute; left: 15px; top: 32px; bottom: 0; width: 2px; background: rgba(0,0,0,0.10); } +/* Each step gets generous breathing room top + bottom so the timeline + reads as a sequence of distinct moments rather than a tight list. + Padding is consistent across every item (no first/last overrides) so + the connector line positioning is stable from step to step. */ +.tl-item { display: flex; gap: 16px; padding: 14px 0 28px; position: relative; } +.tl-item:last-child { padding-bottom: 4px; } +/* Connector line — light dotted column drawn as a dotted left border on a + zero-width pseudo. Top starts 46px from item-top (just under a 32px dot + sat on 14px padding) and bottom extends -14px past the item so it + reaches the next item's dot at its 14px padding offset. */ +.tl-item:not(:last-child)::before { + content: ''; + position: absolute; + left: 15px; + top: 46px; + bottom: -14px; + width: 0; + border-left: 2px dotted rgba(0,0,0,0.18); +} /* Timeline dot — green-paint circle that matches the rec-icon chip but round. Same painted ::before with a slightly larger size so the icon inside reads at small viewport sizes. */ @@ -742,7 +806,22 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; filter: url(#paintGlossBtn); -webkit-filter: url(#paintGlossBtn); } -.tl-when { font-family: var(--font-mono); font-size:14px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 4px; } +/* Step title — vertically centred against the 32px dot using min-height + + flex centring so the label sits at the dot's mid-line rather than + its top edge. Dark-green-paint colour so the title matches the rest + of the brand-paint elements. */ +.tl-when { + font-family: var(--font-body); + font-weight: 600; + font-size: 14px; + letter-spacing: 0.16em; + text-transform: uppercase; + color: #2a3010; + margin-bottom: 6px; + min-height: 32px; + display: flex; + align-items: center; +} .tl-action { font-size:16px; color: var(--text); line-height: 1.5; } .tl-cost { font-family: var(--font-mono); font-size:15px; color: var(--green-bright); margin-top: 4px; } @@ -864,7 +943,9 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; } .result-panel[open] .rp-chevron { transform: rotate(180deg); } .result-panel-body { - padding: 0 18px 18px; + /* Generous top padding so the expanded content has clear breathing + room beneath the panel header (was 0 → 20px). */ + padding: 20px 18px 18px; } /* Strip box treatment from inner cards when wrapped in a panel — the panel is now the visible container. */