Files
Calendarr/frontend/index.html
Scarriffle c632dabc31 feat(web): branded green PWA icons + maskable, installable app polish
Replace the stale blue calendar icon with the real green Calendarr app
icon (matching iOS/Android) for the installable web app:
- icon-192/512.png regenerated from the branded 1024 app icon
- add dedicated maskable icons (192/512) with brand-green safe-zone
  padding so Android/Chrome render a proper adaptive icon (no white box)
- manifest: maskable purpose entries, id, description, lang/dir,
  categories; theme_color -> brand green (#16713d)
- index.html: theme-color -> green, add modern mobile-web-app-capable
- favicon.svg + icon.svg recolored to brand green
Bump APP_VERSION v89 -> v90.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 18:31:58 +02:00

1105 lines
60 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<!-- Title wird beim Laden aus version.js dynamisch gesetzt -->
<title>Calendarr</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#16713d" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Calendarr" />
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.css" />
<link rel="stylesheet" href="/static/css/app.css" />
</head>
<body>
<!-- ─── SETUP SCREEN ─────────────────────────────────────── -->
<div id="screen-setup" class="auth-screen hidden">
<div class="auth-card">
<div class="auth-logo">
<svg class="logo-icon" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<h1>Calendarr</h1>
</div>
<h2>Ersteinrichtung</h2>
<p class="auth-sub">Erstelle den Administrator-Account</p>
<form id="setup-form">
<div class="form-group">
<label>Benutzername</label>
<input type="text" id="setup-username" required autocomplete="username" />
</div>
<div class="form-group">
<label>E-Mail (optional)</label>
<input type="email" id="setup-email" autocomplete="email" />
</div>
<div class="form-group">
<label>Passwort</label>
<input type="password" id="setup-password" required autocomplete="new-password" />
</div>
<div class="form-group">
<label>Passwort wiederholen</label>
<input type="password" id="setup-password2" required autocomplete="new-password" />
</div>
<div id="setup-error" class="form-error hidden"></div>
<button type="submit" class="btn btn-primary btn-full">Admin-Account erstellen</button>
</form>
</div>
</div>
<!-- ─── LOGIN SCREEN ─────────────────────────────────────── -->
<div id="screen-login" class="auth-screen hidden">
<div class="auth-card">
<div class="auth-logo">
<svg class="logo-icon" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<div>
<h1>Calendarr</h1>
<p class="auth-tagline">das minimalistische Kalender Frontend</p>
</div>
</div>
<h2>Anmelden</h2>
<form id="login-form">
<div class="form-group">
<label>Benutzername</label>
<input type="text" id="login-username" required autocomplete="username" />
</div>
<div class="form-group">
<label>Passwort</label>
<input type="password" id="login-password" required autocomplete="current-password" />
</div>
<div class="form-group hidden" id="login-totp-row">
<label>2FA-Code</label>
<input type="text" id="login-totp" placeholder="6-stelliger Code" maxlength="6" inputmode="numeric" autocomplete="one-time-code" />
</div>
<label class="toggle-label" style="margin-bottom:14px">
<input type="checkbox" id="login-remember" /> Angemeldet bleiben
</label>
<div id="login-error" class="form-error hidden"></div>
<button type="submit" class="btn btn-primary btn-full">Anmelden</button>
</form>
</div>
<button class="impressum-link" onclick="openImpressum()">©&nbsp;2026&nbsp;Scarriffleservices</button>
</div>
<!-- ─── MAIN APP ──────────────────────────────────────────── -->
<div id="app" class="hidden">
<!-- TOP BAR -->
<header class="topbar">
<div class="topbar-left">
<button class="icon-btn" id="sidebar-toggle" title="Seitenleiste">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
</button>
<div class="topbar-logo">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<span class="logo-text">Calendarr</span>
</div>
</div>
<div class="topbar-center">
<button class="btn btn-ghost" id="btn-today" data-i18n="btn_today">Heute</button>
<button class="icon-btn" id="btn-prev">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>
</button>
<button class="icon-btn" id="btn-next">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</button>
<h2 id="view-title" class="view-title"></h2>
</div>
<div class="topbar-right">
<div class="view-switcher">
<button class="view-btn" data-view="quarter" data-i18n="view_quarter">Quartal</button>
<button class="view-btn" data-view="month" data-i18n="view_month">Monat</button>
<button class="view-btn" data-view="week" data-i18n="view_week">Woche</button>
<button class="view-btn" data-view="day" data-i18n="view_day">Tag</button>
<button class="view-btn" data-view="agenda" data-i18n="view_agenda">Termine</button>
</div>
<div class="view-mobile-wrapper">
<button class="icon-btn" id="btn-view-mobile" title="Ansicht wechseln" aria-label="Ansicht wechseln">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg>
</button>
<div id="view-mobile-dropdown" class="user-dropdown hidden" style="right:0;top:50px;min-width:180px">
<button class="dropdown-item" data-mobile-view="quarter" data-i18n="view_quarter">Quartal</button>
<button class="dropdown-item" data-mobile-view="month" data-i18n="view_month">Monat</button>
<button class="dropdown-item" data-mobile-view="week" data-i18n="view_week">Woche</button>
<button class="dropdown-item" data-mobile-view="day" data-i18n="view_day">Tag</button>
<button class="dropdown-item" data-mobile-view="agenda" data-i18n="view_agenda">Termine</button>
<button class="dropdown-item" id="btn-today-mobile" style="border-top:1px solid var(--border)" data-i18n="btn_today">Heute</button>
</div>
</div>
<button class="icon-btn" id="btn-settings" data-i18n-title="settings_title" title="Einstellungen">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>
</button>
<div class="user-menu-wrapper">
<div class="user-avatar" id="user-avatar" title="Benutzer"></div>
<div class="user-dropdown hidden" id="user-dropdown">
<div class="dropdown-user" id="dropdown-username"></div>
<button class="dropdown-item" id="btn-profile">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>
<span data-i18n="btn_profile">Profil</span>
</button>
<button class="dropdown-item dropdown-item-mobile-only" id="btn-settings-from-user">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>
<span data-i18n="settings_title">Einstellungen</span>
</button>
<button class="dropdown-item" id="btn-logout">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v24c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
<span data-i18n="btn_logout">Abmelden</span>
</button>
</div>
</div>
</div>
</header>
<!-- CONTENT -->
<div class="content-wrapper">
<!-- SIDEBAR -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-inner">
<div class="create-split add-cal-dropdown-wrap">
<button class="btn btn-fab create-main" id="btn-create-event">
<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span data-i18n="btn_create">Erstellen</span>
</button>
<button class="btn btn-fab create-caret" id="btn-create-menu" data-i18n-title="birthday_new" title="Neuer Geburtstag" aria-haspopup="menu" aria-expanded="false">
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M7 10l5 5 5-5z"/></svg>
</button>
<div class="add-cal-dropdown create-menu hidden" id="create-menu" role="menu">
<button data-action="event" data-i18n="create_event_option">Neuer Termin</button>
<button data-action="birthday" data-i18n="birthday_new">Neuer Geburtstag</button>
</div>
</div>
<!-- Mini Calendar -->
<div class="mini-cal" id="mini-cal">
<div class="mini-cal-header">
<button class="icon-btn mini-btn" id="mini-prev">&#8249;</button>
<span id="mini-title" class="mini-title"></span>
<button class="icon-btn mini-btn" id="mini-next">&#8250;</button>
</div>
<div class="mini-cal-grid">
<div class="mini-dow">Mo</div><div class="mini-dow">Di</div>
<div class="mini-dow">Mi</div><div class="mini-dow">Do</div>
<div class="mini-dow">Fr</div><div class="mini-dow">Sa</div>
<div class="mini-dow">So</div>
</div>
<div class="mini-cal-days" id="mini-days"></div>
</div>
<!-- Group members (shown only in the group overlay; hide/show people) -->
<div class="cal-list hidden" id="group-members">
<div class="cal-list-header">
<span data-i18n="group_members">Mitglieder</span>
</div>
<div id="group-members-items"></div>
</div>
<!-- Calendar List -->
<div class="cal-list" id="cal-list">
<div class="cal-list-header">
<span data-i18n="my_calendars">Meine Kalender</span>
<div class="add-cal-dropdown-wrap">
<button class="icon-btn mini-btn" id="btn-add-cal" title="Kalender hinzufügen">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
</button>
<div class="add-cal-dropdown hidden" id="add-cal-dropdown">
<button data-action="local">Lokaler Kalender</button>
<button data-action="birthday" data-i18n="birthday_calendar_type">Geburtstagskalender</button>
<button data-action="caldav">CalDAV-Konto</button>
<button data-action="ical">iCal-URL abonnieren</button>
<button data-action="google">Google Kalender</button>
<button data-action="homeassistant">Home Assistant</button>
</div>
</div>
</div>
<div id="cal-list-items"></div>
</div>
<!-- Groups -->
<div class="cal-list" id="group-list">
<div class="cal-list-header">
<span data-i18n="groups_title">Gruppen</span>
<button class="icon-btn mini-btn" id="btn-add-group" data-i18n-title="group_create" title="Gruppe erstellen">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
</button>
</div>
<div id="group-list-items"></div>
</div>
</div>
<button class="sidebar-copyright" id="sidebar-copyright" onclick="openImpressum()">©&nbsp;2026&nbsp;Scarriffleservices</button>
</aside>
<div id="sidebar-backdrop" class="sidebar-backdrop"></div>
<!-- MAIN VIEW -->
<main class="main-view" id="main-view">
<div id="group-view-banner" class="group-view-banner hidden">
<span id="group-view-label"></span>
<div style="flex:1"></div>
<button class="btn btn-primary btn-sm" id="group-view-new-event" data-i18n="group_new_event">+ Gruppentermin</button>
<button class="btn btn-ghost btn-sm" id="group-view-exit" data-i18n="group_exit">Gruppenansicht verlassen</button>
</div>
<div id="view-container"></div>
</main>
</div><!-- content-wrapper -->
</div><!-- app -->
<!-- ─── MODALS ────────────────────────────────────────────── -->
<!-- Event Modal -->
<div id="modal-event" class="modal-overlay hidden">
<div class="modal-card" style="max-width:520px">
<div class="modal-header">
<h3 id="modal-event-title-label">Termin erstellen</h3>
<button class="icon-btn modal-close" data-modal="modal-event">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<input type="text" id="ev-title" placeholder="Titel hinzufügen" class="input-title" />
</div>
<div class="form-row">
<label class="toggle-label">
<input type="checkbox" id="ev-allday" /> Ganztägig
</label>
</div>
<div class="form-row" id="ev-time-row">
<div class="form-group half">
<label>Start</label>
<input type="hidden" id="ev-start" />
<div class="dt-display" id="ev-start-display" tabindex="0" role="button">
<span class="dt-display-text"></span>
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
</div>
</div>
<div class="form-group half">
<label>Ende</label>
<input type="hidden" id="ev-end" />
<div class="dt-display" id="ev-end-display" tabindex="0" role="button">
<span class="dt-display-text"></span>
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
</div>
</div>
</div>
<div class="form-row" id="ev-date-row" style="display:none">
<div class="form-group half">
<label>Start</label>
<input type="hidden" id="ev-start-date" />
<div class="dt-display" id="ev-start-date-display" tabindex="0" role="button">
<span class="dt-display-text"></span>
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
</div>
</div>
<div class="form-group half">
<label>Ende</label>
<input type="hidden" id="ev-end-date" />
<div class="dt-display" id="ev-end-date-display" tabindex="0" role="button">
<span class="dt-display-text"></span>
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
</div>
</div>
</div>
<div class="form-group">
<label id="ev-rec-label">Wiederholung</label>
<select id="ev-recurrence">
<option value="">Keine</option>
<option value="FREQ=DAILY">Täglich</option>
<option value="FREQ=WEEKLY">Wöchentlich</option>
<option value="FREQ=MONTHLY">Monatlich</option>
<option value="FREQ=YEARLY">Jährlich</option>
<option value="custom">Benutzerdefiniert…</option>
</select>
</div>
<div id="ev-recurrence-custom" class="form-group hidden">
<div class="form-row" style="gap:8px;align-items:center">
<label style="white-space:nowrap" id="ev-rec-every-label">Alle</label>
<input type="number" id="ev-rec-interval" value="1" min="1" max="99" style="width:60px" />
<select id="ev-rec-freq">
<option value="DAILY">Tage</option>
<option value="WEEKLY">Wochen</option>
<option value="MONTHLY">Monate</option>
</select>
</div>
<div id="ev-rec-weekdays" class="rec-weekdays hidden">
<button type="button" class="rec-day-btn" data-day="MO">Mo</button>
<button type="button" class="rec-day-btn" data-day="TU">Di</button>
<button type="button" class="rec-day-btn" data-day="WE">Mi</button>
<button type="button" class="rec-day-btn" data-day="TH">Do</button>
<button type="button" class="rec-day-btn" data-day="FR">Fr</button>
<button type="button" class="rec-day-btn" data-day="SA">Sa</button>
<button type="button" class="rec-day-btn" data-day="SU">So</button>
</div>
<div class="form-row" style="gap:8px;align-items:center;margin-top:8px">
<label id="ev-rec-ends-label">Endet</label>
<select id="ev-rec-end-type">
<option value="never">Nie</option>
<option value="count">Nach Anzahl</option>
<option value="until">Am Datum</option>
</select>
</div>
<div id="ev-rec-end-count" class="hidden" style="margin-top:4px">
<input type="number" id="ev-rec-count" value="10" min="1" max="999" style="width:80px" />
<span id="ev-rec-occ-label"> Termine</span>
</div>
<div id="ev-rec-end-until" class="hidden" style="margin-top:4px">
<input type="hidden" id="ev-rec-until" />
<div class="dt-display" id="ev-rec-until-display" tabindex="0" role="button">
<span class="dt-display-text"></span>
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
</div>
</div>
</div>
<div class="form-group">
<label>Kalender</label>
<!-- Hidden native select stays the value holder + change-event source;
the custom dropdown below renders colour dots and group icons. -->
<select id="ev-calendar" class="cal-native-hidden"></select>
<div class="cal-select" id="ev-cal-select">
<button type="button" class="cal-select-trigger" id="ev-cal-trigger" aria-haspopup="listbox" aria-expanded="false">
<span class="cal-select-current" id="ev-cal-current"></span>
<svg class="cal-select-chevron" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M7 10l5 5 5-5z"/></svg>
</button>
<div class="cal-select-menu hidden" id="ev-cal-menu" role="listbox"></div>
</div>
</div>
<div class="form-row" id="ev-private-row" style="display:none">
<label class="toggle-label">
<input type="checkbox" id="ev-private" /> <span data-i18n="event_private">Privat</span>
</label>
</div>
<div class="form-group">
<label>Ort</label>
<input type="text" id="ev-location" placeholder="Ort hinzufügen" />
</div>
<div class="form-group">
<label>Beschreibung</label>
<textarea id="ev-description" placeholder="Beschreibung hinzufügen" rows="3"></textarea>
</div>
<div class="form-group">
<label>Farbe</label>
<div class="ev-color-row">
<input type="text" id="ev-color-hex" class="ev-color-hex" maxlength="7" placeholder="#4285f4" spellcheck="false" />
<div class="ev-color-preview" id="ev-color-preview" title="Farbe wählen"></div>
</div>
</div>
<div class="form-group" id="ev-reminders-group" style="display:none">
<label data-i18n="reminders">Benachrichtigungen</label>
<div id="ev-reminders-hint" class="form-hint" style="display:none" data-i18n="reminders_calendar_off">Für diesen Kalender sind Benachrichtigungen deaktiviert Erinnerungen werden nicht ausgeführt.</div>
<div id="ev-reminders-list" class="ev-reminders-list"></div>
<button type="button" class="btn btn-ghost btn-sm" id="ev-reminder-add" data-i18n="reminder_add">Benachrichtigung hinzufügen</button>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger hidden" id="ev-delete">Löschen</button>
<div style="flex:1"></div>
<button class="btn btn-ghost" data-modal="modal-event">Abbrechen</button>
<button class="btn btn-primary" id="ev-save">Speichern</button>
</div>
</div>
</div>
<!-- Create Group Modal -->
<div id="modal-group" class="modal-overlay hidden">
<div class="modal-card" style="max-width:480px">
<div class="modal-header">
<h3 id="group-modal-title" data-i18n="group_create">Gruppe erstellen</h3>
<button class="icon-btn modal-close" data-modal="modal-group">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label data-i18n="group_name">Name</label>
<input type="text" id="group-name" data-i18n-placeholder="group_name_ph" placeholder="Gruppenname" />
</div>
<div class="form-group">
<label data-i18n="group_icon">Icon</label>
<div id="group-icon-picker" class="group-icon-picker"></div>
</div>
<div class="form-group">
<label data-i18n="group_members">Mitglieder</label>
<div id="group-member-picker" class="share-user-picker"></div>
</div>
<div class="form-group" id="group-colors-group" style="display:none">
<label data-i18n="group_member_colors">Farben der Mitglieder</label>
<div id="group-member-colors"></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger hidden" id="group-delete" data-i18n="group_delete">Gruppe löschen</button>
<div style="flex:1"></div>
<button class="btn btn-ghost" data-modal="modal-group" data-i18n="cancel">Abbrechen</button>
<button class="btn btn-primary" id="group-save" data-i18n="save">Speichern</button>
</div>
</div>
</div>
<!-- Share Calendar Modal -->
<div id="modal-share" class="modal-overlay hidden">
<div class="modal-card" style="max-width:480px">
<div class="modal-header">
<h3 data-i18n="share_title">Kalender teilen</h3>
<button class="icon-btn modal-close" data-modal="modal-share">&times;</button>
</div>
<div class="modal-body">
<h4 class="panel-title" data-i18n="share_current">Aktuelle Freigaben</h4>
<div id="share-current-list" class="accounts-list"></div>
<h4 class="panel-title" style="margin-top:20px" data-i18n="share_add">Benutzer hinzufügen</h4>
<div class="form-row" style="gap:8px;align-items:center">
<input type="text" id="share-user-search" data-i18n-placeholder="share_search" placeholder="Benutzer suchen…" style="flex:1" />
<select id="share-permission">
<option value="read" data-i18n="perm_read">Nur lesen</option>
<option value="read_write" data-i18n="perm_read_write">Lesen &amp; schreiben</option>
</select>
</div>
<div id="share-user-picker" class="share-user-picker"></div>
</div>
<div class="modal-footer">
<div style="flex:1"></div>
<button class="btn btn-primary" data-modal="modal-share" data-i18n="done">Fertig</button>
</div>
</div>
</div>
<!-- Delete Confirm Modal -->
<div id="modal-delete-confirm" class="modal-overlay hidden">
<div class="modal-card" style="max-width:400px">
<div class="modal-header">
<h3 id="delete-confirm-title">Termin löschen</h3>
<button class="icon-btn modal-close" data-modal="modal-delete-confirm">&times;</button>
</div>
<div class="modal-body">
<p id="delete-confirm-text"></p>
<div id="delete-series-options" class="hidden" style="margin-top:12px">
<label class="toggle-label" style="display:block;margin-bottom:8px">
<input type="radio" name="delete-scope" value="single" checked /> Nur diesen Termin
</label>
<label class="toggle-label" style="display:block">
<input type="radio" name="delete-scope" value="all" /> Alle Serienelemente
</label>
</div>
</div>
<div class="modal-footer">
<div style="flex:1"></div>
<button class="btn btn-ghost" data-modal="modal-delete-confirm">Abbrechen</button>
<button class="btn btn-danger" id="delete-confirm-ok">Löschen</button>
</div>
</div>
</div>
<!-- Generic Confirm Dialog (styled replacement for window.confirm) -->
<div id="modal-confirm" class="modal-overlay hidden">
<div class="modal-card" style="max-width:400px">
<div class="modal-header">
<h3 id="confirm-title">Bestätigen</h3>
<button class="icon-btn modal-close" data-modal="modal-confirm">&times;</button>
</div>
<div class="modal-body">
<p id="confirm-text"></p>
</div>
<div class="modal-footer">
<div style="flex:1"></div>
<button class="btn btn-ghost" id="confirm-cancel" data-modal="modal-confirm" data-i18n="cancel">Abbrechen</button>
<button class="btn btn-danger" id="confirm-ok" data-i18n="delete">Löschen</button>
</div>
</div>
</div>
<!-- Event Detail Popup -->
<div id="popup-event" class="event-popup hidden">
<div class="popup-header">
<div class="popup-color-dot" id="popup-color-dot"></div>
<h4 id="popup-title"></h4>
<div class="popup-toolbar">
<button class="popup-icon-btn" id="popup-edit" title="Bearbeiten" aria-label="Bearbeiten">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
</button>
<button class="popup-icon-btn" id="popup-copy" title="Kopieren" aria-label="Kopieren">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</button>
<button class="popup-icon-btn popup-icon-btn-danger" id="popup-delete" title="Löschen" aria-label="Löschen">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
</button>
<button class="popup-icon-btn popup-icon-btn-close" id="popup-close" title="Schließen" aria-label="Schließen">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</div>
</div>
<div class="popup-body">
<div class="popup-row" id="popup-row-time">
<svg class="popup-row-icon" viewBox="0 0 24 24"><path d="M12 2a10 10 0 100 20 10 10 0 000-20zm0 18a8 8 0 110-16 8 8 0 010 16zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/></svg>
<span id="popup-time"></span>
</div>
<div class="popup-row" id="popup-row-location" style="display:none">
<svg class="popup-row-icon" viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1112 6.5a2.5 2.5 0 010 5z"/></svg>
<span id="popup-location"></span>
</div>
<div class="popup-row popup-row-desc" id="popup-row-description" style="display:none">
<svg class="popup-row-icon" viewBox="0 0 24 24"><path d="M3 5h18v2H3V5zm0 6h18v2H3v-2zm0 6h12v2H3v-2z"/></svg>
<span id="popup-description"></span>
</div>
<div class="popup-row" id="popup-row-calendar">
<svg class="popup-row-icon" viewBox="0 0 24 24"><path d="M19 4h-1V2h-2v2H8V2H6v2H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zm0 16H5V10h14v10zm0-12H5V6h14v2z"/></svg>
<span id="popup-calendar"></span>
</div>
<div class="popup-row" id="popup-row-creator" style="display:none">
<svg class="popup-row-icon" viewBox="0 0 24 24"><path d="M12 12a5 5 0 100-10 5 5 0 000 10zm0 2c-5.33 0-8 2.67-8 6v2h16v-2c0-3.33-2.67-6-8-6z"/></svg>
<span id="popup-creator"></span>
</div>
</div>
<div id="popup-copy-menu" class="popup-copy-menu hidden"></div>
</div>
<!-- Add CalDAV Account Modal -->
<div id="modal-account" class="modal-overlay hidden">
<div class="modal-card" style="max-width:480px">
<div class="modal-header">
<h3>CalDAV-Konto hinzufügen</h3>
<button class="icon-btn modal-close" data-modal="modal-account">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Anzeigename</label>
<input type="text" id="acc-name" placeholder="z.B. Mein Nextcloud" />
</div>
<div class="form-group">
<label>CalDAV-URL</label>
<input type="url" id="acc-url" placeholder="https://cloud.example.com/remote.php/dav" />
</div>
<div class="form-group">
<label>Benutzername</label>
<input type="text" id="acc-username" autocomplete="username" />
</div>
<div class="form-group">
<label>Passwort</label>
<input type="password" id="acc-password" autocomplete="current-password" />
</div>
<div class="form-group">
<label>Farbe</label>
<div class="ev-color-row">
<input type="text" id="acc-color-hex" class="ev-color-hex" maxlength="7" value="#4285f4" spellcheck="false" />
<div class="ev-color-preview" id="acc-color-preview" style="background:#4285f4" title="Farbe wählen"></div>
</div>
</div>
<div id="acc-error" class="form-error hidden"></div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-account">Abbrechen</button>
<button class="btn btn-primary" id="acc-save">Verbinden</button>
</div>
</div>
</div>
<!-- Local Calendar Modal -->
<div id="modal-local-cal" class="modal-overlay hidden">
<div class="modal-card" style="max-width:400px">
<div class="modal-header">
<h3>Lokalen Kalender erstellen</h3>
<button class="icon-btn modal-close" data-modal="modal-local-cal">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Name</label>
<input type="text" id="local-cal-name" placeholder="z.B. Persönlich" />
</div>
<div class="form-group">
<label>Farbe</label>
<div class="ev-color-row">
<input type="text" id="local-cal-color-hex" class="ev-color-hex" maxlength="7" value="#34a853" spellcheck="false" />
<div class="ev-color-preview" id="local-cal-color-preview" style="background:#34a853" title="Farbe wählen"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-local-cal">Abbrechen</button>
<button class="btn btn-primary" id="local-cal-save">Erstellen</button>
</div>
</div>
</div>
<!-- Birthday modal -->
<div id="modal-birthday" class="modal-overlay hidden">
<div class="modal-card" style="max-width:400px">
<div class="modal-header">
<h3 data-i18n="birthday_modal_title">Neuen Geburtstag hinzufügen</h3>
<button class="icon-btn modal-close" data-modal="modal-birthday">&times;</button>
</div>
<div class="modal-body">
<div id="birthday-no-cal" class="hidden">
<div class="form-hint" data-i18n="birthday_activate_hint">Aktiviere den Geburtstagskalender, um Geburtstage anzulegen. Er erscheint als eigener Kalender in der Seitenleiste.</div>
<button class="btn btn-primary btn-sm" id="birthday-activate" data-i18n="birthday_activate">Geburtstagskalender aktivieren</button>
</div>
<div id="birthday-form">
<div class="form-group">
<label data-i18n="birthday_person">Name</label>
<input type="text" id="birthday-name" data-i18n-ph="birthday_person_ph" placeholder="Name der Person" />
</div>
<div class="form-group">
<label data-i18n="birthday_date">Geburtstag</label>
<div class="birthday-date-row">
<select id="birthday-day"></select>
<select id="birthday-month"></select>
<input type="number" id="birthday-year" min="1900" max="2100" data-i18n-ph="birthday_year_ph" placeholder="Jahr" />
</div>
<label class="checkbox-row" style="margin-top:10px;display:flex;align-items:center;gap:8px;text-transform:none;letter-spacing:normal;font-weight:400;cursor:pointer">
<input type="checkbox" id="birthday-year-unknown" style="width:16px;height:16px;flex:none;margin:0" />
<span data-i18n="birthday_year_unknown">Jahr unbekannt</span>
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-birthday" data-i18n="cancel">Abbrechen</button>
<button class="btn btn-primary" id="birthday-save" data-i18n="save">Speichern</button>
</div>
</div>
</div>
<!-- iCal Subscription Modal -->
<div id="modal-ical-sub" class="modal-overlay hidden">
<div class="modal-card" style="max-width:480px">
<div class="modal-header">
<h3>iCal-URL abonnieren</h3>
<button class="icon-btn modal-close" data-modal="modal-ical-sub">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Name</label>
<input type="text" id="ical-sub-name" placeholder="z.B. Feiertage" />
</div>
<div class="form-group">
<label>iCal-URL</label>
<input type="url" id="ical-sub-url" placeholder="https://example.com/calendar.ics" />
</div>
<div class="form-group">
<label>Farbe</label>
<div class="ev-color-row">
<input type="text" id="ical-sub-color-hex" class="ev-color-hex" maxlength="7" value="#46bdc6" spellcheck="false" />
<div class="ev-color-preview" id="ical-sub-color-preview" style="background:#46bdc6" title="Farbe wählen"></div>
</div>
</div>
<div class="form-group">
<label>Aktualisierung</label>
<select id="ical-sub-refresh">
<option value="15">Alle 15 Minuten</option>
<option value="30">Alle 30 Minuten</option>
<option value="60" selected>Stündlich</option>
<option value="360">Alle 6 Stunden</option>
<option value="1440">Täglich</option>
</select>
</div>
<div id="ical-sub-error" class="form-error hidden"></div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-ical-sub">Abbrechen</button>
<button class="btn btn-primary" id="ical-sub-save">Abonnieren</button>
</div>
</div>
</div>
<!-- Home Assistant Account Modal -->
<div id="modal-ha-account" class="modal-overlay hidden">
<div class="modal-card" style="max-width:480px">
<div class="modal-header">
<h3>Home Assistant verbinden</h3>
<button class="icon-btn modal-close" data-modal="modal-ha-account">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Anzeigename</label>
<input type="text" id="ha-account-name" placeholder="z.B. Mein Home Assistant" />
</div>
<div class="form-group">
<label>Home Assistant URL</label>
<input type="url" id="ha-account-url" placeholder="http://homeassistant.local:8123" />
</div>
<div style="margin-bottom:16px">
<div style="font-size:12px;font-weight:500;text-transform:uppercase;letter-spacing:.5px;color:var(--text-2);margin-bottom:8px">Anmeldemethode</div>
<div style="display:flex;gap:20px">
<label style="display:flex;align-items:center;gap:6px;cursor:pointer;color:var(--text-1);font-size:14px">
<input type="radio" name="ha-auth-method" value="oauth" id="ha-auth-oauth" checked style="width:auto;accent-color:var(--primary)" /> Mit Home Assistant anmelden
</label>
<label style="display:flex;align-items:center;gap:6px;cursor:pointer;color:var(--text-1);font-size:14px">
<input type="radio" name="ha-auth-method" value="token" id="ha-auth-token" style="width:auto;accent-color:var(--primary)" /> Long-Lived Token
</label>
</div>
</div>
<div id="ha-oauth-info" style="margin-bottom:16px;padding:10px 12px;background:var(--bg-app);border-radius:var(--radius-sm);font-size:13px;color:var(--text-2);line-height:1.5">
Du wirst zur Login-Seite deiner Home Assistant Instanz weitergeleitet und nach erfolgreichem Login wieder zurück zu Calendarr.
</div>
<div class="form-group hidden" id="ha-token-group">
<label>Long-Lived Access Token</label>
<input type="password" id="ha-account-token" placeholder="Token aus Profil → Sicherheit" autocomplete="off" />
</div>
<div id="ha-account-error" class="form-error hidden"></div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-ha-account">Abbrechen</button>
<button class="btn btn-primary" id="ha-account-save">Mit Home Assistant anmelden</button>
</div>
</div>
</div>
<!-- Settings Page -->
<div id="modal-settings" class="modal-overlay hidden">
<div class="settings-page-card">
<div class="settings-page-header">
<button class="icon-btn modal-close" data-modal="modal-settings" style="margin-right:8px">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
</button>
<button class="icon-btn settings-nav-toggle" id="settings-nav-toggle" title="Menü" aria-label="Menü">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
</button>
<h3 data-i18n="settings_title">Einstellungen</h3>
<button class="btn btn-primary" id="settings-save" style="margin-left:auto" data-i18n="save">Speichern</button>
</div>
<div class="settings-page-body">
<div class="settings-nav-backdrop" id="settings-nav-backdrop"></div>
<nav class="settings-nav">
<button class="settings-nav-btn active" data-panel="profile" data-i18n="settings_nav_profile">Profil</button>
<button class="settings-nav-btn" data-panel="general" data-i18n="settings_nav_appearance">Darstellung</button>
<button class="settings-nav-btn" data-panel="accounts" data-i18n="settings_nav_calendars">Kalender</button>
<button class="settings-nav-btn hidden" data-panel="users" id="settings-nav-users" data-i18n="settings_nav_admin">Admin</button>
</nav>
<div class="settings-panels">
<!-- Profil: Name, Privatsphäre, geteilter Kalender -->
<div class="settings-panel active" id="settings-panel-profile">
<h4 class="panel-title" data-i18n="profile_account">Konto</h4>
<div class="form-group">
<label data-i18n="profile_display_name">Anzeigename</label>
<input type="text" id="cfg-display-name" data-i18n-placeholder="profile_display_name_ph" placeholder="Anzeigename" />
</div>
<div class="form-group">
<label data-i18n="profile_login_name">Login-Name</label>
<input type="text" id="cfg-login-name" spellcheck="false" autocapitalize="none" />
<p class="panel-desc" data-i18n="profile_login_name_desc">Klein geschrieben, fürs Anmelden. Groß-/Kleinschreibung egal.</p>
</div>
<div class="form-group">
<label>E-Mail</label>
<input type="email" id="cfg-email" placeholder="Keine E-Mail hinterlegt" />
</div>
<h4 class="panel-title" style="margin-top:24px" data-i18n="app_pw_title">App-Passwörter (CalDAV)</h4>
<p class="panel-desc" data-i18n="app_pw_desc">Eigene Passwörter für CalDAV-Clients. Bei aktivem 2FA nötig, da Apps keinen 2FA-Code eingeben können. Jederzeit widerrufbar.</p>
<div class="form-group app-pw-create">
<input type="text" id="app-pw-label" data-i18n-placeholder="app_pw_label_ph" placeholder="Name (z.B. iPhone)" maxlength="100" />
<button class="btn btn-primary btn-sm" id="app-pw-create-btn" data-i18n="app_pw_create">Erstellen</button>
</div>
<div id="app-pw-new" class="app-pw-new hidden">
<label data-i18n="app_pw_new_label">Neues App-Passwort (nur jetzt sichtbar):</label>
<div class="totp-secret-row">
<code id="app-pw-new-value"></code>
<button class="btn btn-ghost btn-sm" id="app-pw-copy" title="Kopieren">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</button>
</div>
</div>
<div id="app-pw-list"></div>
<h4 class="panel-title" style="margin-top:24px" data-i18n="settings_privacy">Privatsphäre</h4>
<p class="panel-desc" data-i18n="settings_private_visibility_desc">Wie private Termine für andere Gruppenmitglieder erscheinen</p>
<div class="form-group">
<label data-i18n="settings_private_visibility">Private Termine für Gruppenmitglieder</label>
<select id="cfg-private-visibility">
<option value="busy" data-i18n="private_visibility_busy">Als „Beschäftigt“ anzeigen</option>
<option value="hidden" data-i18n="private_visibility_hidden">Ausblenden</option>
</select>
</div>
<div class="form-group">
<label class="checkbox-row">
<input type="checkbox" id="cfg-directory-hidden" />
<span data-i18n="settings_directory_hidden">Profil verbergen (nicht in Teilen-/Gruppen-Auswahl anzeigen)</span>
</label>
<p class="panel-desc" data-i18n="settings_directory_hidden_desc">Andere Nutzer können dich dann nicht auswählen, um Kalender zu teilen oder dich zu Gruppen hinzuzufügen. In der Admin-Benutzerverwaltung bleibst du sichtbar.</p>
</div>
<h4 class="panel-title" style="margin-top:24px" data-i18n="settings_calendars">Geteilter Kalender</h4>
<p class="panel-desc" data-i18n="settings_group_visible_desc">Wähle, welcher deiner Kalender für deine Gruppenmitglieder sichtbar ist</p>
<div class="form-group">
<label data-i18n="settings_group_visible">Für Gruppen sichtbarer Kalender</label>
<div id="cfg-group-visible-list" class="cal-radio-list"></div>
</div>
</div>
<!-- Darstellung: einheitliche Sync-Tabelle (Sync-Icon | Name | Wert) -->
<div class="settings-panel" id="settings-panel-general">
<div class="sync-global-row">
<div class="sync-global-text">
<span class="sync-global-title" data-i18n="settings_sync_all">Alle synchronisieren</span>
<span class="panel-desc" data-i18n="settings_sync_all_desc">Diese Einstellungen zwischen deinen Geräten teilen</span>
</div>
<button type="button" class="sync-toggle sync-toggle-lg" id="cfg-sync-all" role="switch" aria-checked="false"></button>
</div>
<div class="settings-table" id="settings-appearance-table"></div>
<div class="theme-io-row">
<button type="button" class="btn btn-secondary btn-sm" id="cfg-theme-export" data-i18n="theme_export">Theme exportieren</button>
<button type="button" class="btn btn-secondary btn-sm" id="cfg-theme-import" data-i18n="theme_import">Theme importieren</button>
<a class="theme-io-help" id="cfg-theme-help" href="https://git.scarriffle.com/Scarriffle/Calendarr/src/branch/beta/THEME.md" target="_blank" rel="noopener noreferrer" data-i18n-title="theme_docs_hint" title="Erklärung der Theme-Parameter">?</a>
<input type="file" id="cfg-theme-file" accept=".theme.json,.json,.theme,application/json" hidden />
</div>
</div>
<!-- Kalender: einheitliche Tabelle aller Kalender aus allen Quellen -->
<div class="settings-panel" id="settings-panel-accounts">
<h4 class="panel-title" data-i18n="settings_nav_calendars">Kalender</h4>
<div class="accounts-add-row">
<button class="btn btn-secondary btn-sm" id="settings-btn-add-local">+ Lokal</button>
<button class="btn btn-secondary btn-sm" id="settings-btn-add-birthday" data-i18n="birthday_add_btn">+ Geburtstage</button>
<button class="btn btn-secondary btn-sm" id="settings-btn-add-caldav">+ CalDAV</button>
<button class="btn btn-secondary btn-sm" id="settings-btn-add-ical">+ iCal</button>
<button class="btn btn-secondary btn-sm" id="settings-btn-add-ha">+ Home Assistant</button>
<button class="btn btn-secondary btn-sm" id="settings-btn-add-google">+ Google</button>
</div>
<div id="cal-settings-table" style="margin-top:16px;overflow-x:auto"></div>
<h4 class="panel-title" style="margin-top:24px" data-i18n="birthday_settings_title">Geburtstage</h4>
<div id="birthday-settings"></div>
</div>
<!-- Admin: Benutzerverwaltung + Standard-Theme + Branding -->
<div class="settings-panel" id="settings-panel-users">
<h4 class="panel-title"><span data-i18n="settings_nav_users">Benutzerverwaltung</span> <span class="badge-admin">Admin</span></h4>
<div id="users-list"></div>
<button class="btn btn-secondary" id="btn-add-user" style="margin-top:12px" data-i18n="users_add">Benutzer hinzufügen</button>
<div id="add-user-form" class="hidden" style="margin-top:12px">
<div class="form-group">
<label data-i18n="username">Benutzername</label>
<input type="text" id="new-username" />
</div>
<div class="form-group">
<label>Passwort</label>
<input type="password" id="new-password" />
</div>
<label class="toggle-label" style="margin-bottom:8px">
<input type="checkbox" id="new-is-admin" /> Administrator
</label>
<button class="btn btn-primary" id="new-user-save">Erstellen</button>
</div>
<!-- Instanz-Standardtheme -->
<h4 class="panel-title" style="margin-top:28px" data-i18n="admin_default_theme">Standard-Theme</h4>
<p class="panel-desc" data-i18n="admin_default_theme_desc">Gilt für alle Nutzer, die keine eigene Farbe gesetzt haben. „Zurück" bei einer Farbe springt auf diesen Standard.</p>
<div id="admin-theme-grid" class="admin-theme-grid"></div>
<div class="admin-theme-actions">
<button class="btn btn-primary btn-sm" id="admin-theme-save" data-i18n="admin_theme_save">Standard speichern</button>
<button class="btn btn-secondary btn-sm" id="admin-theme-import" data-i18n="admin_theme_import">Theme importieren</button>
<button class="btn btn-ghost btn-sm" id="admin-theme-discard" data-i18n="admin_theme_discard">Verwerfen</button>
<input type="file" id="admin-theme-file" accept=".theme.json,.json,.theme,application/json" hidden />
</div>
<!-- Branding: Logo & Favicon -->
<h4 class="panel-title" style="margin-top:28px" data-i18n="admin_branding">Branding</h4>
<p class="panel-desc" data-i18n="admin_branding_desc">Eigenes Logo (oben links) und Favicon (Tab-Symbol) für die ganze Instanz. PNG/JPEG/WebP, max. 5 MB.</p>
<div class="admin-brand-row">
<span class="admin-brand-label" data-i18n="admin_logo">Logo</span>
<div class="admin-brand-preview admin-brand-preview-logo" id="admin-logo-preview"></div>
<div class="admin-brand-controls">
<div class="admin-brand-btns">
<button class="btn btn-secondary btn-sm" id="admin-logo-upload" data-i18n="admin_upload">Hochladen</button>
<button class="btn btn-ghost btn-sm" id="admin-logo-remove" data-i18n="admin_remove">Entfernen</button>
</div>
<span class="admin-brand-dims" data-i18n="admin_logo_dims">PNG/JPEG/WebP · Anzeige max. 150 × 40 px</span>
</div>
<input type="file" id="admin-logo-file" accept="image/png,image/jpeg,image/webp" hidden />
</div>
<div class="admin-brand-row">
<span class="admin-brand-label" data-i18n="admin_favicon">Favicon</span>
<div class="admin-brand-preview" id="admin-favicon-preview"></div>
<div class="admin-brand-controls">
<div class="admin-brand-btns">
<button class="btn btn-secondary btn-sm" id="admin-favicon-upload" data-i18n="admin_upload">Hochladen</button>
<button class="btn btn-ghost btn-sm" id="admin-favicon-remove" data-i18n="admin_remove">Entfernen</button>
</div>
<span class="admin-brand-dims" data-i18n="admin_favicon_dims">PNG/JPEG/WebP · 128 × 128 px (quadratisch)</span>
</div>
<input type="file" id="admin-favicon-file" accept="image/png,image/jpeg,image/webp" hidden />
</div>
</div>
</div><!-- settings-panels -->
</div><!-- settings-page-body -->
</div><!-- settings-page-card -->
</div>
<!-- Profile Modal -->
<div id="modal-profile" class="modal-overlay hidden">
<div class="modal-card" style="max-width:540px">
<div class="modal-header">
<h3>Profil</h3>
<button class="icon-btn modal-close" data-modal="modal-profile">&times;</button>
</div>
<div class="modal-body">
<!-- Avatar Section -->
<div class="profile-avatar-section">
<div class="profile-avatar" id="profile-avatar">
<span id="profile-avatar-letter"></span>
<img id="profile-avatar-img" class="hidden" />
</div>
<div class="profile-avatar-actions">
<button class="btn btn-secondary btn-sm" id="profile-avatar-upload">Bild hochladen</button>
<button class="btn btn-ghost btn-sm hidden" id="profile-avatar-remove">Entfernen</button>
<input type="file" id="profile-avatar-input" accept="image/jpeg,image/png,image/webp" class="hidden" />
<div class="profile-username" id="profile-display-name"></div>
</div>
</div>
<!-- Account Info -->
<div class="settings-section">
<h4 data-i18n="profile_account">Konto</h4>
<div class="form-group">
<label data-i18n="profile_display_name">Anzeigename</label>
<input type="text" id="profile-display-name-input" data-i18n-placeholder="profile_display_name_ph" placeholder="Anzeigename" />
</div>
<div class="form-group">
<label data-i18n="profile_login_name">Login-Name</label>
<input type="text" id="profile-username" spellcheck="false" autocapitalize="none" />
<p class="panel-desc" data-i18n="profile_login_name_desc">Klein geschrieben, fürs Anmelden. Groß-/Kleinschreibung egal.</p>
</div>
<div class="form-group">
<label>E-Mail</label>
<input type="email" id="profile-email" placeholder="Keine E-Mail hinterlegt" />
</div>
<button class="btn btn-primary btn-sm" id="profile-save-info" data-i18n="save">Speichern</button>
</div>
<!-- Password -->
<div class="settings-section">
<h4>Passwort ändern</h4>
<div class="form-group">
<label>Aktuelles Passwort</label>
<input type="password" id="profile-pw-current" autocomplete="current-password" />
</div>
<div class="form-group">
<label>Neues Passwort</label>
<input type="password" id="profile-pw-new" autocomplete="new-password" />
</div>
<div class="form-group">
<label>Neues Passwort wiederholen</label>
<input type="password" id="profile-pw-confirm" autocomplete="new-password" />
</div>
<button class="btn btn-primary btn-sm" id="profile-pw-save">Passwort ändern</button>
</div>
<!-- 2FA -->
<div class="settings-section">
<h4>Zwei-Faktor-Authentifizierung</h4>
<div id="2fa-status">
<div id="2fa-disabled-section">
<p class="text-muted">2FA ist deaktiviert. Schütze dein Konto mit einem Authenticator.</p>
<button class="btn btn-primary btn-sm" id="2fa-setup-btn">2FA einrichten</button>
</div>
<div id="2fa-setup-section" class="hidden">
<p class="text-muted">Scanne den QR-Code mit deiner Authenticator-App (z.B. Bitwarden, Google Authenticator).</p>
<div class="totp-qr-wrapper">
<img id="2fa-qr-img" />
</div>
<div class="form-group">
<label>Oder gib diesen Schlüssel manuell ein</label>
<div class="totp-secret-row">
<code id="2fa-secret-code"></code>
<button class="btn btn-ghost btn-sm" id="2fa-copy-secret" title="Kopieren">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M16 1H4c-1.1 0-2 .9-2 2v24h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v24c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v24z"/></svg>
</button>
</div>
</div>
<div class="form-group">
<label>Bestätigungscode eingeben</label>
<input type="text" id="2fa-verify-code" placeholder="6-stelliger Code" maxlength="6" inputmode="numeric" autocomplete="one-time-code" />
</div>
<button class="btn btn-primary btn-sm" id="2fa-enable-btn">Aktivieren</button>
<button class="btn btn-ghost btn-sm" id="2fa-cancel-btn">Abbrechen</button>
</div>
<div id="2fa-enabled-section" class="hidden">
<p class="text-success">2FA ist aktiviert.</p>
<div class="form-group">
<label>Passwort zum Deaktivieren</label>
<input type="password" id="2fa-disable-pw" autocomplete="current-password" />
</div>
<button class="btn btn-danger btn-sm" id="2fa-disable-btn">2FA deaktivieren</button>
</div>
</div>
</div>
<!-- Calendars -->
<div class="settings-section">
<h4>Meine Kalender</h4>
<div id="profile-calendars"></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-profile">Schließen</button>
</div>
</div>
</div>
<!-- Avatar Crop Modal -->
<div id="modal-crop" class="modal-overlay hidden" style="z-index:1000">
<div class="modal-card" style="max-width:480px">
<div class="modal-header">
<h3>Bildausschnitt wählen</h3>
<button class="icon-btn modal-close" data-modal="modal-crop">&times;</button>
</div>
<div class="modal-body" style="padding:0">
<div class="crop-container">
<img id="crop-image" />
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" data-modal="modal-crop">Abbrechen</button>
<button class="btn btn-primary" id="crop-save">Zuschneiden & Hochladen</button>
</div>
</div>
</div>
<!-- Toast -->
<!-- Floating action button (mobile only — create event) -->
<button id="btn-create-fab" class="create-fab" title="Termin erstellen" aria-label="Termin erstellen">
<svg viewBox="0 0 24 24" fill="currentColor" width="28" height="28"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
</button>
<div id="toast" class="toast hidden"></div>
<!-- Impressum Modal -->
<div id="modal-impressum" class="modal-overlay hidden" style="z-index:1100" onclick="if(event.target===this)closeImpressum()">
<div class="modal-card" style="max-width:460px">
<div class="modal-header">
<h3>Impressum</h3>
<button class="icon-btn" style="font-size:20px;line-height:1" onclick="closeImpressum()">&times;</button>
</div>
<div class="modal-body" style="line-height:1.7">
<p><strong>Scarriffleservices</strong><br>
Software &amp; Webentwicklung</p>
<p>Diese Software wurde von Scarriffleservices mit grösster Sorgfalt entwickelt und bereitgestellt. Alle Rechte vorbehalten &copy; 2026 Scarriffleservices.</p>
<p><strong>Datenspeicherung</strong><br>
Alle Anwendungsdaten werden auf dem Server gespeichert und verarbeitet, auf dem diese Calendarr-Instanz betrieben wird. Der Speicherort hängt damit vom Betreiber des jeweiligen Servers ab. Bei Nutzung der Google&nbsp;Kalender-Anbindung werden Daten über die Google&nbsp;API ausgetauscht; für diese Daten gelten die Datenschutzbestimmungen von Google. Bei Nutzung der Home&nbsp;Assistant-Anbindung werden Daten mit der jeweiligen Home&nbsp;Assistant-Instanz ausgetauscht. Home&nbsp;Assistant ist ein Projekt der Open&nbsp;Home&nbsp;Foundation.</p>
<p><strong>Haftungsausschluss</strong><br>
Trotz sorgfältiger Erstellung wird keine Haftung für die Richtigkeit, Vollständigkeit oder Aktualität der bereitgestellten Inhalte übernommen. Die Nutzung erfolgt auf eigene Verantwortung.</p>
<p><strong>Kontakt</strong><br>
<a href="mailto:scarriffleservices@gmail.com">scarriffleservices@gmail.com</a></p>
</div>
<div class="modal-footer" style="justify-content:space-between;align-items:center">
<span id="impressum-version" style="font-size:12px;color:var(--text-3)">Calendarr</span>
<button class="btn btn-ghost" onclick="closeImpressum()">Schliessen</button>
</div>
</div>
</div>
<script>
function openImpressum() {
document.getElementById('modal-impressum').classList.remove('hidden');
}
function closeImpressum() {
document.getElementById('modal-impressum').classList.add('hidden');
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.js"></script>
<script type="module" src="/static/js/app.js"></script>
</body>
</html>