feat: billableaua.de — add billable hours calculator
Interactive calculator: enter annual hours target + vacation days, shows actual billable hours per working day after subtracting weekends, vacation, and public holidays. Makes the absurdity of 2400h targets visible.
This commit is contained in:
@@ -157,6 +157,91 @@
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* Rechner */
|
||||
.rechner {
|
||||
padding: 120px 0;
|
||||
}
|
||||
|
||||
.rechner h2 {
|
||||
font-size: clamp(2.5rem, 7vw, 4rem);
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.02em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.rechner h2 .dot { color: var(--red); }
|
||||
|
||||
.rechner-grid {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.rechner-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rechner-input label {
|
||||
display: block;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-dim);
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.rechner-input input {
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: 1px solid var(--text-muted);
|
||||
color: var(--text);
|
||||
font-family: inherit;
|
||||
font-size: 2.4rem;
|
||||
font-weight: 700;
|
||||
padding: 12px 16px;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
transition: border-color 0.3s;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.rechner-input input::-webkit-inner-spin-button,
|
||||
.rechner-input input::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.rechner-input input:focus {
|
||||
border-color: var(--red);
|
||||
}
|
||||
|
||||
.rechner-result {
|
||||
font-size: clamp(1.3rem, 3vw, 1.8rem);
|
||||
font-weight: 300;
|
||||
color: var(--text-dim);
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.rechner-result .num {
|
||||
color: var(--red);
|
||||
font-weight: 700;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.rechner-result .verdict {
|
||||
display: block;
|
||||
margin-top: 24px;
|
||||
font-size: clamp(1.5rem, 4vw, 2.2rem);
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.rechner { padding: 80px 0; }
|
||||
.rechner-grid { flex-direction: column; gap: 24px; }
|
||||
}
|
||||
|
||||
/* Silence */
|
||||
.silence {
|
||||
padding: 160px 0;
|
||||
@@ -227,6 +312,25 @@
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<section class="rechner">
|
||||
<div class="wrap">
|
||||
<h2 class="reveal">Rechne selbst<span class="dot">.</span></h2>
|
||||
<div class="rechner-grid reveal">
|
||||
<div class="rechner-input">
|
||||
<label>Billable-Stunden / Jahr</label>
|
||||
<input type="number" id="hours" value="2400" min="0" max="5000">
|
||||
</div>
|
||||
<div class="rechner-input">
|
||||
<label>Urlaubstage</label>
|
||||
<input type="number" id="vacation" value="25" min="0" max="60">
|
||||
</div>
|
||||
</div>
|
||||
<div class="rechner-result reveal" id="result"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<section class="kritik">
|
||||
<div class="wrap">
|
||||
|
||||
@@ -277,6 +381,37 @@
|
||||
}, { threshold: 0.15 });
|
||||
|
||||
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
|
||||
|
||||
// Rechner
|
||||
function calculate() {
|
||||
const hours = parseInt(document.getElementById('hours').value) || 0;
|
||||
const vacation = parseInt(document.getElementById('vacation').value) || 0;
|
||||
const weekends = 104;
|
||||
const feiertage = 10;
|
||||
const workingDays = 365 - weekends - vacation - feiertage;
|
||||
const perDay = workingDays > 0 ? (hours / workingDays) : 0;
|
||||
const perDayStr = perDay.toFixed(1).replace('.', ',');
|
||||
|
||||
let verdict = '';
|
||||
if (perDay > 10) {
|
||||
verdict = '<span class="verdict">Das ist kein Job. <span class="dim">Das ist ein Lifestyle.</span></span>';
|
||||
} else if (perDay > 8) {
|
||||
verdict = '<span class="verdict">Jeden Tag über <span class="num">8</span> Stunden billable. <span class="dim">Ohne Mittagspause, ohne Admin, ohne Atmen.</span></span>';
|
||||
} else if (perDay > 6) {
|
||||
verdict = '<span class="verdict">Klingt machbar? <span class="dim">Vergiss nicht: billable ≠ Arbeitszeit.</span></span>';
|
||||
}
|
||||
|
||||
document.getElementById('result').innerHTML =
|
||||
'<span class="num">' + workingDays + '</span> Arbeitstage ' +
|
||||
'<span class="dim">(365 − ' + weekends + ' Wochenenden − ' + vacation + ' Urlaub − ' + feiertage + ' Feiertage)</span><br>' +
|
||||
'<span class="num">' + hours.toLocaleString('de-DE') + '</span> Stunden ÷ <span class="num">' + workingDays + '</span> Tage = ' +
|
||||
'<span class="num">' + perDayStr + '</span> Stunden pro Tag.' +
|
||||
verdict;
|
||||
}
|
||||
|
||||
document.getElementById('hours').addEventListener('input', calculate);
|
||||
document.getElementById('vacation').addEventListener('input', calculate);
|
||||
calculate();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user