lazyflat/web/templates/base.html
Moritz 376551213a map view (Leaflet + OSM), iOS switches, Alarm → Benachrichtigungen
* flats: new lat/lng columns (migration v3); alert geocodes every new flat
  through googlemaps and ships coords in the payload
* web: CSP extended for unpkg (leaflet.css) + tile.openstreetmap.org
* Wohnungen tab: Liste/Karte view toggle (segmented, CSS-only via :has(),
  selection persisted in localStorage). Karte shows passende flats as Pins
  on an OSM tile map; Popup per Pin mit Adresse, Zimmer/m²/€ und Link
* Top-strip toggles are now proper iOS-style toggle switches (single
  rounded knob sliding in a pill, red when on), no descriptive subtitle
* Alarm-Karte verlinkt jetzt auf /einstellungen/benachrichtigungen
  (Filter-Karte bleibt /einstellungen/filter)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:02:40 +02:00

115 lines
6.8 KiB
HTML

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>{% block title %}lazyflat{% endblock %}</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script src="/static/app.js" defer></script>
<script src="/static/map.js" defer></script>
<style>
:root {
--bg-from: #e4f0fb; --bg-to: #f7fbfe;
--surface: #ffffff; --border: #d8e6f3;
--text: #10253f; --muted: #667d98;
--primary: #2f8ae0; --primary-hover: #1f74c8;
--danger: #e05a6a; --danger-hover: #c44a59;
--ghost: #eaf2fb; --ghost-hover: #d5e5f4;
--accent: #fbd76b;
}
html { color-scheme: light; }
body {
background: linear-gradient(180deg, var(--bg-from) 0%, var(--bg-to) 100%);
background-attachment: fixed;
color: var(--text);
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, sans-serif;
}
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
box-shadow: 0 1px 2px rgba(16, 37, 63, 0.04); }
.border-soft { border-color: var(--border) !important; }
.divide-soft > :not([hidden]) ~ :not([hidden]) { border-color: var(--border) !important; }
.btn { border-radius: 9px; padding: 0.45rem 0.95rem; font-weight: 500;
transition: background 0.15s, box-shadow 0.15s, transform 0.05s; display: inline-block; }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--primary); color: white; box-shadow: 0 1px 2px rgba(47,138,224,.25); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-danger { background: var(--danger); color: white; }
.btn-danger:hover { background: var(--danger-hover); }
.btn-ghost { background: var(--ghost); color: var(--text); border: 1px solid var(--border); }
.btn-ghost:hover { background: var(--ghost-hover); }
.input { background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
padding: 0.55rem 0.8rem; width: 100%; color: var(--text);
transition: border-color .15s, box-shadow .15s; }
.input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(47,138,224,.18); }
.chip { padding: .2rem .7rem; border-radius: 999px; font-size: .75rem; font-weight: 500; display: inline-block; }
.chip-ok { background: #e4f6ec; color: #1f8a4a; border: 1px solid #b7e4c7; }
.chip-warn { background: #fff4dd; color: #a36a1f; border: 1px solid #f5d48b; }
.chip-bad { background: #fde6e9; color: #b8404e; border: 1px solid #f5b5bf; }
.chip-info { background: #e3effc; color: #1f5f99; border: 1px solid #b6d4f0; }
/* iOS-style toggle switch */
.switch { position: relative; display: inline-block; width: 46px; height: 26px;
flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.switch-visual { position: absolute; cursor: pointer; inset: 0;
background: #cfd9e6; border-radius: 999px;
transition: background .2s; }
.switch-visual::before { content: ""; position: absolute; width: 20px; height: 20px;
left: 3px; top: 3px; background: #fff; border-radius: 50%;
box-shadow: 0 1px 3px rgba(16,37,63,0.25);
transition: transform .2s; }
.switch input:checked + .switch-visual { background: var(--primary); }
.switch input:checked + .switch-visual::before { transform: translateX(20px); }
.switch.warn input:checked + .switch-visual { background: var(--danger); }
.switch input:focus-visible + .switch-visual { box-shadow: 0 0 0 3px rgba(47,138,224,.25); }
/* View toggle (Liste / Karte) — segmented pill, CSS-only via :has() */
.view-toggle { display: inline-flex; border: 1px solid var(--border);
border-radius: 999px; overflow: hidden; background: var(--surface);
font-size: 0.85rem; font-weight: 500; }
.view-toggle label { padding: 0.35rem 0.95rem; cursor: pointer; user-select: none;
color: var(--muted); transition: background .15s, color .15s; }
.view-toggle input { position: absolute; opacity: 0; pointer-events: none;
width: 0; height: 0; }
.view-toggle label:hover { color: var(--text); background: var(--ghost); }
.view-toggle label:has(input:checked) { background: var(--primary); color: #fff; }
.view-map { display: none; }
body:has(#v_map:checked) .view-list { display: none; }
body:has(#v_map:checked) .view-map { display: block; }
#flats-map { height: 520px; border-radius: 10px; }
.brand-dot {
width: 2rem; height: 2rem; border-radius: 10px;
background: linear-gradient(135deg, #66b7f2 0%, #2f8ae0 60%, #fbd76b 100%);
box-shadow: 0 1px 4px rgba(47, 138, 224, 0.35);
}
a { color: var(--primary); }
a:hover { text-decoration: underline; }
/* tab nav */
.tab { padding: 0.7rem 0.2rem; color: var(--muted); border-bottom: 2px solid transparent;
margin-right: 1.5rem; font-weight: 500; }
.tab.active { color: var(--text); border-color: var(--primary); }
.tab:hover { color: var(--text); text-decoration: none; }
/* auto-apply hot button */
.btn-hot { background: linear-gradient(135deg, #ff7a85 0%, #e14a56 100%); color: white;
box-shadow: 0 2px 6px rgba(225, 74, 86, 0.35); font-weight: 600; }
.btn-hot:hover { filter: brightness(1.05); }
.btn-hot.off { background: linear-gradient(135deg, #cfd9e6 0%, #99abc2 100%);
box-shadow: 0 1px 2px rgba(16, 37, 63, 0.15); }
/* forensic JSON tree */
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 12px; }
details > summary { cursor: pointer; user-select: none; }
details > summary::marker { color: var(--muted); }
</style>
</head>
<body class="min-h-screen">
{% block body %}{% endblock %}
</body>
</html>