Design-System Sprint A: utilities.css + 948 Inline-Styles → Utility-Klassen, SW by-v1102

PHASE 1 — Sofort-Cleanup ohne Risiko:
- Neue Datei utilities.css mit ~25 Klassen für häufige Kombinationen:
  * text-xs-muted, text-xs-secondary, text-sm-muted, text-sm-secondary
  * flex-gap-2/3, flex-col-gap-2/3/4, flex-center-gap-1/2/3
  * flex-between, flex-1-min, mb-1/3, mt-1/3
  * icon-xs/sm/md/lg, label-block, caption
- index.html bindet utilities.css ein
- mb-3/mt-3 ergänzt (waren in design-system.css unvollständig)

PHASE 2 — .by-tab Modifier für Vereinheitlichung:
- .by-tabs.grid (mit --tab-cols Variable für Admin/Health/etc.)
- .by-tabs.sticky (Desktop vertikale Tabs für Admin)
- .by-tabs.wrap (Zuchthunde, flex-wrap statt scroll)
- .by-tabs.separated (Sitting, mit eigenem Hintergrund + Border)

PHASE 3 — Inline-Style → Klassen-Migration (Python-Script):
- 948 Inline-Styles entfernt (5101 → 4153, -18%)
- 962 Migrationen über 47 Page-Dateien
- Top-Treffer: admin.js (180), health.js (67), dog-profile.js (67),
  litters.js (62), settings.js (61), zuchthunde.js (51)
- Patterns: text-muted, text-secondary, text-danger, text-xs-muted,
  text-sm-muted, grid-2 (Duplikat-Bug behoben!), flex-col-gap-3,
  p-3/4, mb-2/3/4, hidden, w-full, flex-1, ...
- Bewahrt bestehende class-Attribute (mergt korrekt)

Alle 19 Tests grün. Kein visueller Diff erwartet (gleiche Property-Werte).
This commit is contained in:
rene 2026-05-27 07:11:27 +02:00
parent 279f76714e
commit 459cd425f2
54 changed files with 1809 additions and 956 deletions

View file

@ -92,7 +92,7 @@ window.Page_health = (() => {
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#wave-sine"></use></svg>
<span class="health-transponder-label">Transponder:</span>
<span class="health-transponder-nr" id="health-transponder-nr">
${dog?.chip_nr ? `<strong>${_esc(dog.chip_nr)}</strong>` : '<em style="color:var(--c-text-muted)">nicht eingetragen</em>'}
${dog?.chip_nr ? `<strong>${_esc(dog.chip_nr)}</strong>` : '<em class="text-muted">nicht eingetragen</em>'}
</span>
<button class="btn btn-link btn-sm health-transponder-edit" id="health-transponder-edit"
style="padding:0;font-size:var(--text-xs);color:var(--c-text-muted)">
@ -194,12 +194,12 @@ window.Page_health = (() => {
background:var(--c-surface);border-radius:var(--radius-md);
border-left:3px solid ${ampel.color === 'red' ? '#ef4444' : ampel.color === 'yellow' ? '#f59e0b' : '#22c55e'}">
<span style="font-size:1.2rem">${ICONS[e._typ] || '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#clipboard-text"></use></svg>'}</span>
<div style="flex:1;min-width:0">
<div class="flex-1-min">
<div style="font-size:var(--text-sm);font-weight:var(--weight-medium);
white-space:nowrap;overflow:hidden;text-overflow:ellipsis">
${_esc(e.bezeichnung)}
</div>
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">
<div class="text-xs-muted">
${ageLabel} · ${dateStr}
</div>
</div>
@ -482,11 +482,11 @@ window.Page_health = (() => {
<div class="health-card" data-id="${e.id}" data-action="open-entry"
style="padding:var(--space-3) var(--space-4)">
<div style="display:flex;justify-content:space-between;align-items:center;width:100%">
<span style="font-size:var(--text-sm);color:var(--c-text-secondary)">
<span class="text-sm-secondary">
${UI.time.format(e.datum + 'T00:00:00')}
</span>
<span style="font-weight:var(--weight-bold);font-size:var(--text-lg)">
${e.wert} <span style="font-size:var(--text-sm);color:var(--c-text-secondary)">${e.einheit || 'kg'}</span>
${e.wert} <span class="text-sm-secondary">${e.einheit || 'kg'}</span>
</span>
</div>
${e.notiz ? `<div class="health-card-note" style="padding-top:var(--space-1)">${_esc(e.notiz)}</div>` : ''}
@ -512,7 +512,7 @@ window.Page_health = (() => {
</div>
${chart}
</div>` : ''}
<div class="health-list" style="margin-top:var(--space-2)">${items}</div>
<div class="health-list mt-2">${items}</div>
<div style="text-align:center;padding:var(--space-4)">${addBtn}</div>
`;
}
@ -693,7 +693,7 @@ window.Page_health = (() => {
<span style="font-size:1.5rem">${UI.icon('gender-female')}</span>
<div>
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold)">Nächste Läufigkeit erwartet</div>
<div style="font-size:var(--text-sm);color:var(--c-text-secondary)">${label}
<div class="text-sm-secondary">${label}
${avgInterval ? ` · Ø ${avgInterval} Tage Abstand` : ''}
</div>
</div>
@ -737,7 +737,7 @@ window.Page_health = (() => {
return `
${banner}
<div class="health-list" style="margin-top:var(--space-4)">${items}</div>
<div class="health-list mt-4">${items}</div>
<div style="text-align:center;padding:var(--space-4)">${addBtn}</div>`;
}
@ -869,7 +869,7 @@ window.Page_health = (() => {
</a>`
).join('')}
</div>`
: `<span style="font-size:var(--text-xs);color:var(--c-text-muted)">Noch keine Datei hochgeladen</span>`}
: `<span class="text-xs-muted">Noch keine Datei hochgeladen</span>`}
</div>
</div>
`;
@ -990,7 +990,7 @@ window.Page_health = (() => {
: (entry.datei_url ? [{ id: null, url: entry.datei_url, media_type: entry.datei_typ || 'image' }] : []);
const mediaHtml = mediaItems.length
? `<div class="health-media-gallery" style="margin-top:var(--space-4)">
? `<div class="health-media-gallery mt-4">
${mediaItems.map(m => m.media_type === 'pdf'
? `<a href="${_esc(m.url)}" target="_blank" rel="noopener"
class="btn btn-secondary btn-sm health-media-gallery-pdf">
@ -1042,8 +1042,8 @@ window.Page_health = (() => {
if (praxis) {
const adresse = [praxis.strasse, [praxis.plz, praxis.ort].filter(Boolean).join(' ')].filter(Boolean).join(', ');
const tel = praxis.telefon ? ` · <a href="tel:${_esc(praxis.telefon)}">${_esc(praxis.telefon)}</a>` : '';
const oh = praxis.opening_hours ? `<br><small style="color:var(--c-text-secondary)">🕐 ${_esc(_fmtOeffnungszeiten(praxis.opening_hours))}</small>` : '';
rows.push(['Praxis', `<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#first-aid"></use></svg> ${_esc(praxis.name)}${adresse ? `<br><small style="color:var(--c-text-secondary)">${_esc(adresse)}${tel}</small>` : tel}${oh}`]);
const oh = praxis.opening_hours ? `<br><small class="text-secondary">🕐 ${_esc(_fmtOeffnungszeiten(praxis.opening_hours))}</small>` : '';
rows.push(['Praxis', `<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#first-aid"></use></svg> ${_esc(praxis.name)}${adresse ? `<br><small class="text-secondary">${_esc(adresse)}${tel}</small>` : tel}${oh}`]);
}
} else if (e.tierarzt_name) {
rows.push(['Tierarzt', _esc(e.tierarzt_name)]);
@ -1143,8 +1143,8 @@ window.Page_health = (() => {
const footer = `
<div style="display:flex;flex-direction:column;gap:var(--space-2);width:100%">
<button type="submit" form="health-form" class="btn btn-primary" style="width:100%">${isEdit ? 'Speichern' : 'Erstellen'}</button>
<div style="display:flex;gap:var(--space-2)">
<button type="submit" form="health-form" class="btn btn-primary w-full">${isEdit ? 'Speichern' : 'Erstellen'}</button>
<div class="flex-gap-2">
${isEdit ? `<button type="button" class="btn btn-danger" id="health-form-delete">Löschen</button>` : ''}
<button type="button" class="btn btn-secondary flex-1" id="health-form-cancel">Abbrechen</button>
</div>
@ -1384,7 +1384,7 @@ window.Page_health = (() => {
<button type="button" class="btn btn-ghost btn-sm" style="padding:0;font-size:inherit"
data-action="goto-praxen">Praxis im Tab Praxen anlegen</button>
</div>
<label class="form-label" style="margin-top:var(--space-2)">Tierarzt / Praxis (Freitext)</label>
<label class="form-label mt-2">Tierarzt / Praxis (Freitext)</label>
<input class="form-control" name="tierarzt_name"
value="${_esc(entry?.tierarzt_name || '')}" placeholder="Dr. Muster">
</div>`;
@ -1423,7 +1423,7 @@ window.Page_health = (() => {
<input class="form-control" type="text" name="haeufigkeit"
value="${_esc(entry?.haeufigkeit || '')}" placeholder="z.B. täglich, 2x wöchentlich">
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="form-group">
<label class="form-label">Gabe bis (optional)</label>
<input class="form-control" type="date" name="bis_datum" value="${entry?.bis_datum || ''}">
@ -1486,7 +1486,7 @@ window.Page_health = (() => {
</div>` : '';
return `
${lastInfo}
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="form-group">
<label class="form-label">Dauer (Tage)</label>
<input class="form-control" type="number" min="1" max="60" name="wert"
@ -1512,7 +1512,7 @@ window.Page_health = (() => {
text-transform:uppercase;letter-spacing:0.05em;margin-bottom:var(--space-3)">
Zucht (optional)
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="form-group">
<label class="form-label">Deckdatum</label>
<input class="form-control" type="date" name="deckdatum"
@ -1623,7 +1623,7 @@ window.Page_health = (() => {
const ratingHtml = hasRating
? `<div style="display:flex;align-items:center;gap:var(--space-1);margin-top:var(--space-1);font-size:var(--text-sm)">
${stars}
<span style="color:var(--c-text-secondary)">${p.avg_rating.toFixed(1)} (${p.anz_bewertungen} Bew.)</span>
<span class="text-secondary">${p.avg_rating.toFixed(1)} (${p.anz_bewertungen} Bew.)</span>
</div>`
: `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:var(--space-1)">Noch keine Bewertungen</div>`;
return `
@ -1688,7 +1688,7 @@ window.Page_health = (() => {
const favCard = _favoritVet ? `
<div style="margin-bottom:var(--space-4)">
<div class="mb-4">
<div style="font-size:var(--text-xs);font-weight:600;color:var(--c-primary);
text-transform:uppercase;letter-spacing:.05em;margin-bottom:var(--space-2)">
${UI.icon('heart')} Mein Tierarzt
@ -1794,7 +1794,7 @@ window.Page_health = (() => {
<div style="padding:var(--space-3) 0;border-bottom:1px solid var(--c-border)">
<div style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-1)">
${_renderStarsReadonly(k.gesamt)}
<span style="font-size:var(--text-xs);color:var(--c-text-muted)">
<span class="text-xs-muted">
${k.created_at ? k.created_at.slice(0, 10) : ''}
</span>
</div>
@ -1806,7 +1806,7 @@ window.Page_health = (() => {
</div>` : ''}
<p style="margin:0;font-size:var(--text-sm)">${_esc(k.text || '')}</p>
</div>`).join('')
: `<p style="color:var(--c-text-muted);font-size:var(--text-sm)">Noch keine Kommentare.</p>`;
: `<p class="text-sm-muted">Noch keine Kommentare.</p>`;
const bewBody = anz_bewertungen === 0
? `<p style="color:var(--c-text-muted);text-align:center;padding:var(--space-4) 0">
@ -1814,12 +1814,12 @@ window.Page_health = (() => {
</p>`
: `
<div style="display:flex;align-items:center;gap:var(--space-4);margin-bottom:var(--space-4)">
<div style="text-align:center">
<div class="text-center">
<div style="font-size:3rem;font-weight:700;line-height:1">${avg_rating.toFixed(1)}</div>
<div>${_renderStarsReadonly(avg_rating)}</div>
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">${anz_bewertungen} Bewertung${anz_bewertungen !== 1 ? 'en' : ''}</div>
<div class="text-xs-muted">${anz_bewertungen} Bewertung${anz_bewertungen !== 1 ? 'en' : ''}</div>
</div>
<div style="flex:1">${balken}</div>
<div class="flex-1">${balken}</div>
</div>
<div>${kommentarHtml}</div>`;
@ -1845,22 +1845,22 @@ window.Page_health = (() => {
${_renderStarsInput('gesamt', cur.gesamt || 0)}
<input type="hidden" name="gesamt" id="bew-gesamt" value="${cur.gesamt || 0}">
</div>
<div class="form-group" style="margin-top:var(--space-3)">
<div class="form-group mt-3">
<label class="form-label">Wartezeit</label>
${_renderStarsInput('wartezeit', cur.wartezeit || 0)}
<input type="hidden" name="wartezeit" id="bew-wartezeit" value="${cur.wartezeit || 0}">
</div>
<div class="form-group" style="margin-top:var(--space-3)">
<div class="form-group mt-3">
<label class="form-label">Freundlichkeit</label>
${_renderStarsInput('freundlichkeit', cur.freundlichkeit || 0)}
<input type="hidden" name="freundlichkeit" id="bew-freundlichkeit" value="${cur.freundlichkeit || 0}">
</div>
<div class="form-group" style="margin-top:var(--space-3)">
<div class="form-group mt-3">
<label class="form-label">Kompetenz</label>
${_renderStarsInput('kompetenz', cur.kompetenz || 0)}
<input type="hidden" name="kompetenz" id="bew-kompetenz" value="${cur.kompetenz || 0}">
</div>
<div class="form-group" style="margin-top:var(--space-3)">
<div class="form-group mt-3">
<label class="form-label">Kommentar <span style="font-weight:400;color:var(--c-text-muted)">(optional, anonym)</span></label>
<textarea class="form-control" name="text" maxlength="500" rows="3"
placeholder="Deine Erfahrungen mit dieser Praxis…">${_esc(cur.text || '')}</textarea>
@ -1967,7 +1967,7 @@ window.Page_health = (() => {
value="${_esc(praxis?.ort || '')}" placeholder="Musterstadt">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="form-group">
<label class="form-label">Telefon</label>
<input class="form-control" type="tel" name="telefon"
@ -2037,11 +2037,11 @@ window.Page_health = (() => {
const hits = await API.tieraerzte.osmNearby(pos.lat, pos.lon);
if (!hits.length) {
resultsEl.style.display = 'block';
resultsEl.innerHTML = '<p style="font-size:var(--text-sm);color:var(--c-text-secondary)">Keine Praxen in der Nähe im OSM-Cache gefunden.</p>';
resultsEl.innerHTML = '<p class="text-sm-secondary">Keine Praxen in der Nähe im OSM-Cache gefunden.</p>';
} else {
resultsEl.style.display = 'block';
resultsEl.innerHTML = hits.map(h => `
<div class="health-card" style="margin-bottom:var(--space-2)">
<div class="health-card mb-2">
<div style="cursor:pointer;flex:1"
data-osm-id="${_esc(h.osm_id)}"
data-name="${_esc(h.name)}"
@ -2049,8 +2049,8 @@ window.Page_health = (() => {
data-phone="${_esc(h.phone || '')}"
data-action="pick-osm">
<div style="font-weight:600">${_esc(h.name)}</div>
${h.opening_hours_fmt ? `<div style="font-size:var(--text-sm);color:var(--c-text-secondary)">${_esc(h.opening_hours_fmt)}</div>` : '<div style="font-size:var(--text-sm);color:var(--c-text-muted)">Öffnungszeiten unbekannt</div>'}
<div style="font-size:var(--text-xs);color:var(--c-text-secondary)">${h.distanz_km} km entfernt</div>
${h.opening_hours_fmt ? `<div class="text-sm-secondary">${_esc(h.opening_hours_fmt)}</div>` : '<div class="text-sm-muted">Öffnungszeiten unbekannt</div>'}
<div class="text-xs-secondary">${h.distanz_km} km entfernt</div>
</div>
<button class="btn btn-secondary btn-sm" style="flex-shrink:0;align-self:flex-start"
data-action="korrigieren"
@ -2130,8 +2130,8 @@ window.Page_health = (() => {
// ----------------------------------------------------------
function _renderSymptomCheck(content) {
content.innerHTML = `
<div style="padding:var(--space-4)">
<div class="card" style="padding:var(--space-4)">
<div class="p-4">
<div class="card p-4">
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0 0 var(--space-4)">
Beschreibe die Symptome deines Hundes. Die KI gibt eine erste Einschätzung kein Ersatz für den Tierarzt.
</p>
@ -2140,7 +2140,7 @@ window.Page_health = (() => {
<textarea id="symptom-input" class="form-control" rows="4"
placeholder="z.B. frisst nicht, trinkt viel, schläft mehr als sonst..."></textarea>
</div>
<button id="symptom-submit-btn" class="btn btn-primary" style="width:100%">
<button id="symptom-submit-btn" class="btn btn-primary w-full">
Symptome analysieren <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#magnifying-glass"></use></svg>
</button>
<div id="symptom-result" style="display:none;margin-top:var(--space-5)"></div>
@ -2261,7 +2261,7 @@ window.Page_health = (() => {
const nrEl = _container.querySelector('#health-transponder-nr');
if (nrEl) nrEl.innerHTML = nr
? `<strong>${_esc(nr)}</strong>`
: '<em style="color:var(--c-text-muted)">nicht eingetragen</em>';
: '<em class="text-muted">nicht eingetragen</em>';
} catch (e) {
UI.setLoading(btn, false);
UI.toast('Fehler beim Speichern', 'error');
@ -2299,7 +2299,7 @@ window.Page_health = (() => {
">
<div style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-1)">
<svg class="ph-icon" aria-hidden="true" style="flex-shrink:0"><use href="/icons/phosphor.svg#star"></use></svg>
<strong style="font-size:var(--text-sm)">KI-Gesundheitsbericht</strong>
<strong class="text-sm">KI-Gesundheitsbericht</strong>
${datum ? `<span style="font-size:var(--text-xs);color:var(--c-text-muted);margin-left:auto">${datum}</span>` : ''}
</div>
<div style="font-size:var(--text-sm);color:var(--c-text-muted);line-height:1.5">${preview}</div>
@ -2318,7 +2318,7 @@ window.Page_health = (() => {
<button onclick="window._kiPrev()" style="padding:6px 16px;border-radius:999px;
border:1.5px solid var(--c-border);background:var(--c-surface);cursor:pointer;
font-size:var(--text-sm);${idx >= berichte.length-1 ? 'opacity:.3;pointer-events:none' : ''}"> Älter</button>
<span style="font-size:var(--text-xs);color:var(--c-text-muted)">${idx+1} / ${berichte.length}</span>
<span class="text-xs-muted">${idx+1} / ${berichte.length}</span>
<button onclick="window._kiNext()" style="padding:6px 16px;border-radius:999px;
border:1.5px solid var(--c-border);background:var(--c-surface);cursor:pointer;
font-size:var(--text-sm);${idx <= 0 ? 'opacity:.3;pointer-events:none' : ''}">Neuer </button>
@ -2357,27 +2357,27 @@ window.Page_health = (() => {
});
el.innerHTML = `
<div style="margin-bottom:var(--space-3)">
<div class="mb-3">
<div style="font-size:var(--text-xs);font-weight:600;color:var(--c-text-muted);
text-transform:uppercase;letter-spacing:.05em;margin-bottom:var(--space-2)">
Terminvorschläge
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-2)">
<div class="flex-col-gap-2">
${vorschlaege.map(v => {
const badge = v.ueberfaellig
? `<span style="font-size:var(--text-xs);color:var(--c-danger);font-weight:600">Überfällig seit ${_fmtDatum(v.naechstes)}</span>`
: `<span style="font-size:var(--text-xs);color:var(--c-warning);font-weight:600">Fällig am ${_fmtDatum(v.naechstes)}</span>`;
return `
<div class="health-card" style="flex-direction:row;align-items:center;gap:var(--space-3)">
<div style="flex:1;min-width:0">
<div class="flex-1-min">
<div style="font-weight:600;font-size:var(--text-sm)">${_esc(v.bezeichnung)}</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary)">${_esc(v.label)}${v.praxis_name ? ' · ' + _esc(v.praxis_name) : ''}</div>
<div class="text-xs-secondary">${_esc(v.label)}${v.praxis_name ? ' · ' + _esc(v.praxis_name) : ''}</div>
${badge}
</div>
<div style="text-align:right;flex-shrink:0">
<div style="font-size:var(--text-xs);color:var(--c-text-muted)">Vorschlag</div>
<div class="text-xs-muted">Vorschlag</div>
<div style="font-size:var(--text-sm);font-weight:600">${_fmtDatum(v.datum_vorschlag)}</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary)">${v.uhrzeit_vorschlag} Uhr</div>
<div class="text-xs-secondary">${v.uhrzeit_vorschlag} Uhr</div>
<button class="btn btn-primary btn-sm" style="margin-top:var(--space-1)"
data-action="termin-anlegen"
data-v='${_esc(JSON.stringify(v))}'>
@ -2419,7 +2419,7 @@ window.Page_health = (() => {
<label class="form-label">Bezeichnung</label>
<input class="form-control" type="text" name="titel" value="${_esc(titel)}" required>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="form-group">
<label class="form-label">Datum</label>
<input class="form-control" type="date" name="datum" value="${_esc(v.datum_vorschlag)}" required>
@ -2488,12 +2488,12 @@ window.Page_health = (() => {
<div style="font-size:1.6rem;flex-shrink:0">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#first-aid"></use></svg>
</div>
<div class="health-card-body" style="flex:1;min-width:0">
<div class="health-card-body flex-1-min">
${vet ? `
<div class="health-card-title">${_esc(vet.name)}</div>
${adresse ? `<div class="health-card-meta">${_esc(adresse)}</div>` : ''}
${vet.telefon ? `
<div style="margin-top:var(--space-2)">
<div class="mt-2">
<a href="tel:${_esc(vet.telefon)}" class="btn btn-secondary btn-sm"
onclick="event.stopPropagation()">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> ${_esc(vet.telefon)}
@ -2507,10 +2507,10 @@ window.Page_health = (() => {
</a>
</div>` : ''}
` : `
<div style="color:var(--c-text-muted);font-size:var(--text-sm)">
<div class="text-sm-muted">
Noch kein Tierarzt als Favorit gespeichert.
</div>
<button class="btn btn-secondary btn-sm" style="margin-top:var(--space-2)"
<button class="btn btn-secondary btn-sm mt-2"
id="health-suche-tierarzt-btn">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-pin"></use></svg> Tierarzt suchen
</button>
@ -2583,7 +2583,7 @@ window.Page_health = (() => {
<div style="font-size:1.4rem;flex-shrink:0;color:var(--c-primary)">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#${_esc(icon)}"></use></svg>
</div>
<div class="health-card-body" style="flex:1;min-width:0">
<div class="health-card-body flex-1-min">
<div class="health-card-title">${_esc(doc.titel)}</div>
<div class="health-card-meta">
${_esc(label)}${datum ? ' · ' + datum : ''}
@ -2597,7 +2597,7 @@ window.Page_health = (() => {
? '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#image"></use></svg> Bild öffnen'
: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF öffnen'}
</a>
<button class="btn btn-ghost btn-xs" style="color:var(--c-danger)"
<button class="btn btn-ghost btn-xs text-danger"
data-action="delete-hdoc" data-doc-id="${doc.id}"
onclick="event.stopPropagation()">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trash"></use></svg>
@ -2815,7 +2815,7 @@ window.Page_health = (() => {
<div class="form-group">
<label class="form-label">Aktuelle Angabe</label>
<input class="form-control" type="text" value="${_esc(currentOh)}" disabled
style="color:var(--c-text-muted)">
class="text-muted">
</div>
<div class="form-group">
<label class="form-label">Korrekte Öffnungszeiten *</label>
@ -2958,7 +2958,7 @@ window.Page_health = (() => {
<textarea id="ki-tierarzt-symptom" class="form-control" rows="4"
placeholder="${_esc(placeholder)}"></textarea>
</div>
<div id="ki-tierarzt-result" style="display:none"></div>
<div id="ki-tierarzt-result" class="hidden"></div>
<div style="margin-top:var(--space-3);padding:var(--space-3);
background:#fff3cd;border-radius:var(--radius-md);
font-size:var(--text-xs);color:#856404;
@ -3089,7 +3089,7 @@ window.Page_health = (() => {
<svg class="ph-icon" style="width:16px;height:16px;color:${color};flex-shrink:0" aria-hidden="true">
<use href="/icons/phosphor.svg#bell-ringing"></use>
</svg>
<div style="flex:1;min-width:0">
<div class="flex-1-min">
<span style="font-weight:600;font-size:var(--text-sm);color:var(--c-text)">${_esc(r.bezeichnung)}</span>
<span style="font-size:var(--text-xs);color:var(--c-text-secondary);margin-left:var(--space-1)">${TYPE_LABEL[r.typ] || r.typ}</span>
</div>
@ -3133,15 +3133,15 @@ window.Page_health = (() => {
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-2);margin-top:var(--space-3);font-size:var(--text-sm)">
<div><span style="color:var(--c-text-secondary)">Jahresbeitrag</span><br><strong>${_fmtEur(p.jahresbeitrag)}</strong></div>
<div><span style="color:var(--c-text-secondary)">Läuft ab</span><br><strong>${_fmtDate(p.ablaufdatum)}</strong></div>
${p.kontakt ? `<div style="grid-column:1/-1"><span style="color:var(--c-text-secondary)">Kontakt</span><br>${_esc(p.kontakt)}</div>` : ''}
${p.notizen ? `<div style="grid-column:1/-1"><span style="color:var(--c-text-secondary)">Notizen</span><br>${_esc(p.notizen)}</div>` : ''}
<div><span class="text-secondary">Jahresbeitrag</span><br><strong>${_fmtEur(p.jahresbeitrag)}</strong></div>
<div><span class="text-secondary">Läuft ab</span><br><strong>${_fmtDate(p.ablaufdatum)}</strong></div>
${p.kontakt ? `<div style="grid-column:1/-1"><span class="text-secondary">Kontakt</span><br>${_esc(p.kontakt)}</div>` : ''}
${p.notizen ? `<div style="grid-column:1/-1"><span class="text-secondary">Notizen</span><br>${_esc(p.notizen)}</div>` : ''}
</div>
</div>`).join('') : `
<div style="text-align:center;padding:var(--space-6);color:var(--c-text-muted)">
<svg class="ph-icon" style="width:2.5rem;height:2.5rem;margin-bottom:var(--space-3);display:block;margin-inline:auto" aria-hidden="true"><use href="/icons/phosphor.svg#shield-check"></use></svg>
<div style="font-size:var(--text-sm)">Noch keine Versicherung eingetragen.</div>
<div class="text-sm">Noch keine Versicherung eingetragen.</div>
</div>`;
content.innerHTML = `<div style="padding:var(--space-4) 0">
@ -3175,7 +3175,7 @@ window.Page_health = (() => {
<div class="by-form-group"><label class="by-label">Police-Nr.</label>
<input type="text" name="police_nr" class="form-control by-input" value="${_esc(existing?.police_nr||'')}" placeholder="optional">
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="by-form-group"><label class="by-label">Jahresbeitrag ()</label>
<input type="number" name="jahresbeitrag" class="form-control by-input" value="${existing?.jahresbeitrag||''}" min="0" step="0.01" placeholder="z. B. 149.00">
</div>
@ -3267,7 +3267,7 @@ window.Page_health = (() => {
return `
<div class="card" style="padding:var(--space-3);margin-bottom:var(--space-2);display:flex;align-items:flex-start;gap:var(--space-3)">
<div style="width:3px;border-radius:2px;background:${color};align-self:stretch;flex-shrink:0"></div>
<div style="flex:1;min-width:0">
<div class="flex-1-min">
<div style="display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap">
<span style="font-weight:700;font-size:var(--text-sm);color:${color}">${_esc(katLabel)}</span>
${trigLabel ? `<span style="font-size:var(--text-xs);background:var(--c-surface-2);padding:1px 6px;border-radius:100px;color:var(--c-text-secondary)">${_esc(trigLabel)}</span>` : ''}
@ -3283,7 +3283,7 @@ window.Page_health = (() => {
}).join('') : `
<div style="text-align:center;padding:var(--space-6);color:var(--c-text-muted)">
<svg class="ph-icon" style="width:2.5rem;height:2.5rem;margin-bottom:var(--space-3);display:block;margin-inline:auto" aria-hidden="true"><use href="/icons/phosphor.svg#brain"></use></svg>
<div style="font-size:var(--text-sm)">Noch keine Einträge. Protokolliere auffälliges Verhalten um Muster zu erkennen.</div>
<div class="text-sm">Noch keine Einträge. Protokolliere auffälliges Verhalten um Muster zu erkennen.</div>
</div>`;
content.innerHTML = `<div style="padding:var(--space-4) 0">
@ -3309,7 +3309,7 @@ window.Page_health = (() => {
const today = new Date().toISOString().slice(0, 10);
const nowTime = (() => { const d=new Date(); return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; })();
const body = `<form id="${id}">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
<div class="grid-2">
<div class="by-form-group"><label class="by-label">Datum</label>
<input type="date" name="datum" class="form-control by-input" value="${today}" required>
</div>
@ -3324,7 +3324,7 @@ window.Page_health = (() => {
</select>
</div>
<div class="by-form-group"><label class="by-label">Intensität (1 = gering, 5 = stark)</label>
<div style="display:flex;gap:var(--space-2)">
<div class="flex-gap-2">
${[1,2,3,4,5].map(n => `<button type="button" class="beh-int-btn" data-val="${n}"
style="flex:1;padding:10px;border-radius:8px;border:1.5px solid var(--c-border);
background:${n<=3?'var(--c-primary)':'var(--c-bg-card)'};