diff --git a/frontend/css/app.css b/frontend/css/app.css
index d2846f8..cdda4d2 100644
--- a/frontend/css/app.css
+++ b/frontend/css/app.css
@@ -165,15 +165,28 @@ a { color: var(--primary); text-decoration: none; }
}
.btn-fab:active { transform: translateY(0) scale(.985); }
-/* Split create button: main "Erstellen" action + a caret that opens a small
- menu (new event / new birthday). */
-.create-split { display: flex; align-items: stretch; gap: 4px; margin: 16px 12px 8px; }
-.create-split .btn-fab { margin: 0; }
-.create-split .create-main { flex: 1; justify-content: center; }
-.create-split .create-caret { flex: 0 0 auto; padding: 12px 12px; }
+/* Split create button: one seamless pill — main "Erstellen" action on the left,
+ a caret section on the right that opens a small menu (new event / new birthday). */
+.create-split {
+ display: flex; align-items: stretch; margin: 16px 12px 8px;
+ border-radius: 999px;
+ box-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 35%, transparent);
+}
+.create-split .btn-fab { margin: 0; box-shadow: none; border-radius: 0; }
+.create-split .btn-fab:hover { transform: none; box-shadow: none; filter: brightness(1.08); }
+.create-split .create-main { flex: 1; justify-content: center; border-radius: 999px 0 0 999px; }
+.create-split .create-caret {
+ flex: 0 0 auto; padding: 12px 14px; border-radius: 0 999px 999px 0;
+ border-left: 1px solid color-mix(in srgb, #fff 25%, transparent);
+}
.create-split .create-caret svg { width: 18px; height: 18px; }
.create-menu { left: 0; right: auto; min-width: 200px; }
+/* Birthday modal: day / month / year row */
+.birthday-date-row { display: flex; gap: 8px; }
+.birthday-date-row select { flex: 1; }
+.birthday-date-row #birthday-year { width: 90px; flex: 0 0 auto; }
+
/* Circular icon buttons (topbar nav, modal close, etc.) */
.icon-btn {
display: inline-flex; align-items: center; justify-content: center;
@@ -1487,6 +1500,10 @@ a { color: var(--primary); text-decoration: none; }
.ct-dav-hint { font-size: 11px; color: var(--text-3); margin-top: 6px; max-width: 640px; }
.app-pw-create { display: flex; gap: 8px; align-items: center; }
.app-pw-create input { flex: 1; }
+
+/* Consistent placement for a section's action button: right-aligned, matching
+ the sticky header save and the app-password create row. */
+.settings-actions { display: flex; justify-content: flex-end; margin-top: 10px; }
.app-pw-new { margin: 8px 0; }
.app-pw-item { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-top: 1px solid var(--border); }
.app-pw-name { font-weight: 500; }
diff --git a/frontend/index.html b/frontend/index.html
index 8628794..10c388d 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -590,17 +590,6 @@
diff --git a/frontend/js/calendar.js b/frontend/js/calendar.js
index 11b0220..e6d43a4 100644
--- a/frontend/js/calendar.js
+++ b/frontend/js/calendar.js
@@ -2538,22 +2538,12 @@ function openLocalCalModal() {
document.getElementById('local-cal-name').value = '';
document.getElementById('local-cal-color-hex').value = '#34a853';
document.getElementById('local-cal-color-preview').style.background = '#34a853';
- const bday = document.getElementById('local-cal-birthday');
- bday.checked = false;
- document.getElementById('local-cal-notify').value = '-1';
- document.getElementById('local-cal-notify-group').classList.add('hidden');
openModal('modal-local-cal');
}
function bindLocalCalModal() {
const preview = document.getElementById('local-cal-color-preview');
const hex = document.getElementById('local-cal-color-hex');
- const bday = document.getElementById('local-cal-birthday');
- const notifyGroup = document.getElementById('local-cal-notify-group');
- fillBirthdayNotifySelect(document.getElementById('local-cal-notify'));
- bday.addEventListener('change', () => {
- notifyGroup.classList.toggle('hidden', !bday.checked);
- });
preview.addEventListener('click', async () => {
const picked = await openColorPicker(preview, hex.value || '#34a853');
if (picked) { hex.value = picked.toUpperCase(); preview.style.background = picked; }
@@ -2568,15 +2558,8 @@ function bindLocalCalModal() {
const name = document.getElementById('local-cal-name').value.trim();
if (!name) { showToast(t('error_enter_name'), true); return; }
const color = hex.value;
- const isBirthday = bday.checked;
- const notify = parseInt(document.getElementById('local-cal-notify').value, 10);
- const body = { name, color };
- if (isBirthday) {
- body.is_birthday = true;
- if (notify >= 0) body.birthday_notify_days_before = notify;
- }
try {
- const cal = await api.post('/local/calendars', body);
+ const cal = await api.post('/local/calendars', { name, color });
state.localCalendars.push(cal);
renderCalendarList();
closeModal('modal-local-cal');
@@ -2585,53 +2568,90 @@ function bindLocalCalModal() {
};
}
+// ── Birthdays (single dedicated calendar per user) ─────────
+const BIRTHDAY_DEFAULT_COLOR = '#e0407f';
+
+/** The user's single birthday calendar (owned, is_birthday), or null. */
+function birthdayCalendar() {
+ return (state.localCalendars || []).find(c => c.is_birthday && c.owned !== false) || null;
+}
+
+/** Create the single "Geburtstage" birthday calendar if none exists; returns it. */
+async function ensureBirthdayCalendar() {
+ const existing = birthdayCalendar();
+ if (existing) return existing;
+ const cal = await api.post('/local/calendars', {
+ name: t('birthday_calendar_name'), color: BIRTHDAY_DEFAULT_COLOR, is_birthday: true,
+ });
+ state.localCalendars.push(cal);
+ renderCalendarList();
+ return cal;
+}
+
// ── Birthday Modal (manual entry) ─────────────────────────
-function birthdayCalendars() {
- return (state.localCalendars || []).filter(
- c => c.is_birthday && (c.owned !== false || c.permission === 'read_write')
- );
+function fillBirthdayDateSelects() {
+ const daySel = document.getElementById('birthday-day');
+ const monthSel = document.getElementById('birthday-month');
+ if (daySel.options.length === 0) {
+ daySel.innerHTML = Array.from({ length: 31 }, (_, i) =>
+ `