little optimisation

This commit is contained in:
marcinator_2 committed 2026-10-16 01:52:41 +02:00
1 parent 11e0875a20
commit 4c1728441f
7 files changed
+144 -39

No files matched your search

+3 -1
View File
@@ -107,7 +107,9 @@ Aktueller Stand und offene Pi-Prüfungen stehen am Anfang von `PLAN.md`.
- WebUI nutzt breite Desktopansicht: Bild links, Bedienung rechts; schmal - WebUI nutzt breite Desktopansicht: Bild links, Bedienung rechts; schmal
untereinander. Kamera-Endpunkte/manuelle Einrichtung und Detailparameter untereinander. Kamera-Endpunkte/manuelle Einrichtung und Detailparameter
aufklappbar, Status/Start/Stopp unmittelbar sichtbar. Keine Änderung an aufklappbar, Status/Start/Stopp unmittelbar sichtbar. Keine Änderung an
Servo-/Speicherverträgen durch Layoutänderungen. Servo-/Speicherverträgen durch Layoutänderungen. Schwarz/blaues CSS-Design
mit dezentem statischem Glow; keine Daueranimationen oder externen Assets.
Stopp rot, Warnungen amberfarben, sichtbarer Tastaturfokus beibehalten.
- Manueller Kamera-Einrichtmodus: Richtungstasten halten statt automatischer - Manueller Kamera-Einrichtmodus: Richtungstasten halten statt automatischer
Anschlagfahrt; beide Achsen vor Anschlag stoppen. 40 µs/s, 0,35-s-Bewegungslease, Anschlagfahrt; beide Achsen vor Anschlag stoppen. 40 µs/s, 0,35-s-Bewegungslease,
Loslassen hält Position, Stopp beendet PWM. Kein Farbzielsuchen; Grenzen aus Loslassen hält Position, Stopp beendet PWM. Kein Farbzielsuchen; Grenzen aus
+14
View File
@@ -681,3 +681,17 @@ Bildsequenzen eines Blender-Schnittprojekts bei Weitergabe mitnehmen.
wiederholtes Suchen, Wiederaufnahme, Farbe nach Neukonstruktion sowie wiederholtes Suchen, Wiederaufnahme, Farbe nach Neukonstruktion sowie
bestehende Stopp-/Watchdog-Regeln. JS-Syntax und git diff --check bestanden. bestehende Stopp-/Watchdog-Regeln. JS-Syntax und git diff --check bestanden.
- Nur lokale Umsetzung; Upload und echter Wiedererkennungstest noch offen. - Nur lokale Umsetzung; Upload und echter Wiedererkennungstest noch offen.
## Schwarz/blaues Design · 16. Oktober 2026
- Nutzerwunsch lokal umgesetzt: dunkler Hintergrund, blaue Flächen/Akzente,
statischer Glow an Navigation, Rahmen, Status und Hauptaktion. Rote Stopps,
amberfarbene Hinweise und deutlicher Tastaturfokus bleiben unterscheidbar.
- Nur CSS/Designfarben; keine Änderungen an API, Polling, Bild-/Servosteuerung.
Keine externen Assets, Daueranimation oder zusätzliche Pi-Bildberechnung.
Hover-Übergänge unterstützen reduzierte Bewegung.
- Browserdemo visuell geprüft: Kamera-Desktop, beide Seiten bei 390×844 ohne
horizontalen Überlauf, Details bedienbar. Screenshot der lokalen Demo:
/tmp/halloween-schwarz-blau.png. Kein echter Hardware-/Pi-Leistungstest.
- Bestehende Seitentests/JS-Prüfungen, Syntax und diff --check geprüft.
Upload zum Pi bleibt offen; Dokumentation und Anweisungen aktualisiert.
+1
View File
@@ -20,6 +20,7 @@ an der echten Halterung stehen noch aus.
Die WebUI hat zwei Seiten: **Grab** (`/grab`, auch Startseite `/`) und Die WebUI hat zwei Seiten: **Grab** (`/grab`, auch Startseite `/`) und
**Kamera / Finger** (`/kamera`), mit eigener Bedienung und gemeinsamem Livebild. **Kamera / Finger** (`/kamera`), mit eigener Bedienung und gemeinsamem Livebild.
Die Oberfläche nutzt Schwarz/Blau mit dezenten statischen Leuchteffekten.
Auf breiten Bildschirmen stehen Bild und Steuerung nebeneinander. Detailwerte Auf breiten Bildschirmen stehen Bild und Steuerung nebeneinander. Detailwerte
sind aufklappbar; Endpunkte und Mittelstellung findest du unter sind aufklappbar; Endpunkte und Mittelstellung findest du unter
**Endpunkte & manuelle Einrichtung**. Auf kleinen Bildschirmen wird einspaltig angezeigt. **Endpunkte & manuelle Einrichtung**. Auf kleinen Bildschirmen wird einspaltig angezeigt.
+4 -1
View File
@@ -436,7 +436,10 @@ seine Status-API 15,0 Bilder/s bei inaktiver Nachführung.
### Übersichtliche Bedienung ### Übersichtliche Bedienung
Die Seiten nutzen bis zu 1800 Pixel Breite. Ab 1000 Pixel Bildschirmbreite Die Oberfläche nutzt Schwarz/Blau und dezente statische CSS-Leuchteffekte.
Diese werden vom Browser gezeichnet; keine zusätzlichen Kameraberechnungen,
Animationsschleifen oder externen Schrift-/Grafikabrufe. Stopp bleibt rot und
Warnhinweise bleiben farblich erkennbar. Die Seiten nutzen bis zu 1800 Pixel Breite. Ab 1000 Pixel Bildschirmbreite
stehen Kamerabild und Bedienung nebeneinander; darunter untereinander. stehen Kamerabild und Bedienung nebeneinander; darunter untereinander.
Auf der Kameraseite sind Status, Vorschau, echte Nachführung und Kamerastopp Auf der Kameraseite sind Status, Vorschau, echte Nachführung und Kamerastopp
rechts direkt sichtbar. **Farbe, Drehrichtung, Tempo und Suche einstellen** rechts direkt sichtbar. **Farbe, Drehrichtung, Tempo und Suche einstellen**
+64 -24
View File
@@ -5,37 +5,37 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kamera / Finger · Halloween</title> <title>Kamera / Finger · Halloween</title>
<style> <style>
:root { color-scheme: dark; font-family: system-ui, sans-serif; background: #111718; color: #eef0e9; } :root { color-scheme: dark; font-family: system-ui, sans-serif; background: #070a10; color: #eef4ff; }
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { margin: 0; } body { margin: 0; }
header { padding: 1.1rem max(1rem, calc((100vw - 1120px)/2)); background: #182122; border-bottom: 1px solid #3a4644; } header { padding: 1.1rem max(1rem, calc((100vw - 1120px)/2)); background: #0b111c; border-bottom: 1px solid #22334d; }
h1 { margin: 0; font-size: 1.5rem; } h1 { margin: 0; font-size: 1.5rem; }
header p { margin: .35rem 0 0; color: #c1cbc2; } header p { margin: .35rem 0 0; color: #a7b7d0; }
main { max-width: 1120px; margin: auto; padding: 1rem; display: grid; gap: 1rem; grid-template-columns: minmax(0, 2fr) minmax(290px, 1fr); } main { max-width: 1120px; margin: auto; padding: 1rem; display: grid; gap: 1rem; grid-template-columns: minmax(0, 2fr) minmax(290px, 1fr); }
section { background: #1a2424; border: 1px solid #394846; border-radius: 12px; padding: 1rem; } section { background: #0d1522; border: 1px solid #243550; border-radius: 12px; padding: 1rem; }
h2 { margin: 0 0 .7rem; font-size: 1.1rem; } h2 { margin: 0 0 .7rem; font-size: 1.1rem; }
.camera { width: 100%; aspect-ratio: 4/3; object-fit: contain; background: #050808; border-radius: 8px; } .camera { width: 100%; aspect-ratio: 4/3; object-fit: contain; background: #03060b; border-radius: 8px; }
.camera-wrap { position: relative; aspect-ratio: 4/3; } .camera-wrap { position: relative; aspect-ratio: 4/3; }
.camera-wrap .camera { position: absolute; inset: 0; height: 100%; } .camera-wrap .camera { position: absolute; inset: 0; height: 100%; }
#detection-overlay { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; cursor: crosshair; } #detection-overlay { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; cursor: crosshair; }
.row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; } .row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
button, select, input, a.download { border: 1px solid #5c6c67; border-radius: 7px; background: #293938; color: #f4f5ed; font: inherit; padding: .65rem .8rem; } button, select, input, a.download { border: 1px solid #344b6c; border-radius: 7px; background: #152238; color: #f1f6ff; font: inherit; padding: .65rem .8rem; }
button, select, a.download { cursor: pointer; } button, select, a.download { cursor: pointer; }
button:hover, a.download:hover { background: #36504a; } button:hover, a.download:hover { background: #1d3659; }
button:focus-visible, select:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid #ffb44f; outline-offset: 2px; } button:focus-visible, select:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid #ffb44f; outline-offset: 2px; }
button.danger { background: #782c26; border-color: #c76652; font-weight: 700; } button.danger { background: #782c26; border-color: #c76652; font-weight: 700; }
button.danger:hover { background: #9c352c; } button.danger:hover { background: #9c352c; }
button:disabled { opacity: .5; cursor: not-allowed; } button:disabled { opacity: .5; cursor: not-allowed; }
a.download { display: inline-block; text-decoration: none; } a.download { display: inline-block; text-decoration: none; }
nav a[aria-current="page"] { background:#46695b; border:2px solid #c4e0cb; font-weight:700; } nav a[aria-current="page"] { background:#123c69; border:2px solid #5aafff; font-weight:700; }
.endpoint-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.8rem; } .endpoint-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.8rem; }
@media (max-width:520px) { .endpoint-grid { grid-template-columns:1fr; } } @media (max-width:520px) { .endpoint-grid { grid-template-columns:1fr; } }
.value { font-weight: 650; color: #ffca78; } .value { font-weight: 650; color: #8bd6ff; }
.notice { border-left: 4px solid #ffb44f; padding: .55rem .8rem; background: #302b21; margin: .8rem 0; } .notice { border-left: 4px solid #ffb44f; padding: .55rem .8rem; background: #211c14; margin: .8rem 0; }
.muted { color: #bfcbc6; } .muted { color: #acbdd4; }
#message { min-height: 1.5rem; color: #ffca78; } #message { min-height: 1.5rem; color: #8bd6ff; }
dl { display: grid; grid-template-columns: auto 1fr; gap: .4rem .8rem; margin: .5rem 0 1rem; } dl { display: grid; grid-template-columns: auto 1fr; gap: .4rem .8rem; margin: .5rem 0 1rem; }
dt { color: #adbfba; } dd { margin: 0; } dt { color: #a7b7d0; } dd { margin: 0; }
.events { grid-column: 1 / -1; } .events { grid-column: 1 / -1; }
#events-list { margin: .5rem 0 0; padding-left: 1.4rem; } #events-list { margin: .5rem 0 0; padding-left: 1.4rem; }
#events-list li { margin: .35rem 0; } #events-list li { margin: .35rem 0; }
@@ -58,20 +58,20 @@
summary { cursor: pointer; font-weight: 650; padding: .65rem 0; } summary { cursor: pointer; font-weight: 650; padding: .65rem 0; }
summary:focus-visible { outline: 3px solid #ffb44f; outline-offset: 3px; } summary:focus-visible { outline: 3px solid #ffb44f; outline-offset: 3px; }
.follow-picture { width: min(100%, calc((100vh - 380px)*1.3333)); min-width: 220px; margin: auto; } .follow-picture { width: min(100%, calc((100vh - 380px)*1.3333)); min-width: 220px; margin: auto; }
.follow-picture img, .follow-canvas { display: block; width: 100%; height: auto; aspect-ratio: 4/3; border-radius: 8px; background: #050808; } .follow-picture img, .follow-canvas { display: block; width: 100%; height: auto; aspect-ratio: 4/3; border-radius: 8px; background: #03060b; }
.follow-canvas { cursor: crosshair; } .follow-canvas { cursor: crosshair; }
#follow-status { padding: .75rem; background: #111b1a; border: 1px solid #759c8f; border-radius: 8px; color: #ffca78; font-weight: 600; } #follow-status { padding: .75rem; background: #0a1b30; border: 1px solid #35577e; border-radius: 8px; color: #8bd6ff; font-weight: 600; }
.start-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; } .start-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
#follow-stop { grid-column: 1 / -1; } #follow-stop { grid-column: 1 / -1; }
#follow-hardware { background: #365b49; border-color: #91bda3; } #follow-hardware { background: #165ca1; border-color: #69b6ff; }
.settings-card { margin-top: 1rem; border-top: 1px solid #5c6c67; } .settings-card { margin-top: 1rem; border-top: 1px solid #344b6c; }
.settings-card > label { display: inline-flex; align-items: center; gap: .4rem; margin: .6rem .5rem .6rem 0; } .settings-card > label { display: inline-flex; align-items: center; gap: .4rem; margin: .6rem .5rem .6rem 0; }
.settings-card .hint, #follow-settings-status { color: #bfcbc6; } .settings-card .hint, #follow-settings-status { color: #acbdd4; }
.help { margin-top: .6rem; } .help { margin-top: .6rem; }
.help summary { color: #c5d3cc; } .help summary { color: #b7c9e3; }
.calibration-panel { grid-column: 1 / -1; } .calibration-panel { grid-column: 1 / -1; }
.calibration-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 1.2rem; margin-top: .8rem; } .calibration-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 1.2rem; margin-top: .8rem; }
.calibration-card { min-width: 0; padding: 1rem; border: 1px solid #759c8f; border-radius: 8px; } .calibration-card { min-width: 0; padding: 1rem; border: 1px solid #35577e; border-radius: 8px; }
.calibration-card label { display: inline-flex; align-items: center; gap: .5rem; flex-wrap: wrap; } .calibration-card label { display: inline-flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
button { min-height: 44px; } button { min-height: 44px; }
@media (max-width: 999px) { @media (max-width: 999px) {
@@ -90,6 +90,46 @@
.calibration-panel summary span { display: block; } .calibration-panel summary span { display: block; }
button, select { max-width: 100%; overflow-wrap: anywhere; } button, select { max-width: 100%; overflow-wrap: anywhere; }
} }
/* Static blue highlights: no animation loop, external fonts or graphics. */
:root { --accent: #62b4ff; --line: #294466; --muted: #acbdd4; }
body { background: radial-gradient(ellipse at 15% 0%, #101f39 0, transparent 48%), #070a10; }
header { background: #090f19; border-bottom: 1px solid #28466a; box-shadow: 0 5px 26px #0006; }
h1 { letter-spacing: -.035em; font-weight: 750; display: flex; align-items: center; gap: .65rem; }
h1::before { content: ''; display: inline-block; flex: 0 0 12px; width: 12px; height: 12px; border: 2px solid #89d7ff; border-radius: 4px; background: #1475c5; box-shadow: 0 0 16px #238bff70; }
header p { font-size: .92rem; }
section { background: linear-gradient(145deg, #101c2d, #0b121e 65%); border: 1px solid #29405f; border-radius: 16px; box-shadow: 0 10px 28px #0004, inset 0 1px 0 #a7cfff0a; }
h2 { display: flex; align-items: center; gap: .55rem; letter-spacing: -.015em; font-weight: 700; }
h2::before { content: ''; width: 3px; height: 1em; border-radius: 2px; background: var(--accent); box-shadow: 0 0 10px #339eff60; }
nav { gap: .6rem !important; }
nav a.download { border-radius: 10px; background: #101b2b; border-color: #293e5a; }
nav a[aria-current="page"] { background: linear-gradient(135deg,#174b7c,#122d50); border: 1px solid #69b6ff; box-shadow: 0 0 18px #1987ff28, inset 0 1px 0 #a0d7ff22; }
button, a.download, select, input { border-radius: 9px; }
button, a.download { transition: border-color .15s ease, background-color .15s ease; }
button:not(:disabled):hover, a.download:hover { border-color: #72bdff; background: #1b385a; box-shadow: 0 0 14px #278fff24; }
button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible, summary:focus-visible { outline: 3px solid #84d4ff; outline-offset: 3px; }
input[type="number"], input[type="text"], select { background: #080f1b; border-color: #3b5375; color: #f1f6ff; }
input[type="checkbox"] { accent-color: #459fff; width: 1.05em; height: 1.05em; flex-shrink: 0; }
button:disabled { opacity: .55; box-shadow: none; }
#follow-hardware, #save-mode, #save-detection { background: linear-gradient(135deg,#1768b5,#164880); border-color: #69b6ff; box-shadow: 0 0 20px #1684ff24; font-weight: 650; }
#follow-hardware:not(:disabled):hover, #save-mode:hover, #save-detection:hover { background: #1c6db5; box-shadow: 0 0 22px #1684ff45; }
button.danger { background: #381c27; border-color: #bf5d72; color: #ffe8ee; box-shadow: none; }
button.danger:not(:disabled):hover { background: #5b2433; border-color: #ff8da2; box-shadow: 0 0 14px #e94a6820; }
#follow-status { background: linear-gradient(110deg,#102b49,#0b1a2e); border-color: #3677ac; color: #c5e9ff; box-shadow: 0 0 18px #1684ff12; line-height: 1.5; }
#follow-camera-status, #camera-status { color: #a9c7e9; font-size: .92rem; font-variant-numeric: tabular-nums; }
.follow-picture, .camera-wrap { border-radius: 12px; box-shadow: 0 0 0 1px #34547c, 0 0 24px #1782ff12; overflow: hidden; }
.camera, .follow-canvas { border-radius: 12px; }
.value { color: #91d4ff; font-variant-numeric: tabular-nums; }
summary { color: #d8e9ff; }
summary::marker { color: #65b7ff; }
details[open] > summary { color: #8fd0ff; }
.settings-card { border-color: #294466; }
.calibration-card, .endpoint-grid > div { background: #0a1423; border-color: #2d4c70; }
.endpoint-grid > div { box-shadow: inset 0 1px 0 #9dd1ff0a; }
.notice { background: #211c14; color: #f1dbb5; border-left-color: #ddb361; border-radius: 0 8px 8px 0; }
dl { background: #0a1423; border: 1px solid #263c59; border-radius: 10px; padding: .9rem; }
a { color: #9bd6ff; }
@media (prefers-reduced-motion: reduce) { button, a.download { transition: none; } }
</style> </style>
</head> </head>
<body> <body>
@@ -191,25 +231,25 @@
</div> </div>
<p>Taste gedrückt halten: langsame Bewegung. Loslassen: Zielposition bleibt stehen. Bei fehlendem Tastensignal endet die Bewegung spätestens nach 0,35 s, sofern Pi und Treiber reagieren.</p> <p>Taste gedrückt halten: langsame Bewegung. Loslassen: Zielposition bleibt stehen. Bei fehlendem Tastensignal endet die Bewegung spätestens nach 0,35 s, sofern Pi und Treiber reagieren.</p>
<div class="endpoint-grid"> <div class="endpoint-grid">
<div style="border:1px solid #759c8f;border-radius:8px;padding:.8rem"> <div style="border:1px solid #35577e;border-radius:8px;padding:.8rem">
<h3 style="margin:0 0 .5rem">Links</h3> <h3 style="margin:0 0 .5rem">Links</h3>
<p id="setup-value-left" class="value">Gespeichert: lädt …</p> <p id="setup-value-left" class="value">Gespeichert: lädt …</p>
<button id="setup-save-left">Diese Position als Links speichern</button> <button id="setup-save-left">Diese Position als Links speichern</button>
<p id="setup-hint-left" class="muted"></p> <p id="setup-hint-left" class="muted"></p>
</div> </div>
<div style="border:1px solid #759c8f;border-radius:8px;padding:.8rem"> <div style="border:1px solid #35577e;border-radius:8px;padding:.8rem">
<h3 style="margin:0 0 .5rem">Rechts</h3> <h3 style="margin:0 0 .5rem">Rechts</h3>
<p id="setup-value-right" class="value">Gespeichert: lädt …</p> <p id="setup-value-right" class="value">Gespeichert: lädt …</p>
<button id="setup-save-right">Diese Position als Rechts speichern</button> <button id="setup-save-right">Diese Position als Rechts speichern</button>
<p id="setup-hint-right" class="muted"></p> <p id="setup-hint-right" class="muted"></p>
</div> </div>
<div style="border:1px solid #759c8f;border-radius:8px;padding:.8rem"> <div style="border:1px solid #35577e;border-radius:8px;padding:.8rem">
<h3 style="margin:0 0 .5rem">Oben</h3> <h3 style="margin:0 0 .5rem">Oben</h3>
<p id="setup-value-up" class="value">Gespeichert: lädt …</p> <p id="setup-value-up" class="value">Gespeichert: lädt …</p>
<button id="setup-save-up">Diese Position als Oben speichern</button> <button id="setup-save-up">Diese Position als Oben speichern</button>
<p id="setup-hint-up" class="muted"></p> <p id="setup-hint-up" class="muted"></p>
</div> </div>
<div style="border:1px solid #759c8f;border-radius:8px;padding:.8rem"> <div style="border:1px solid #35577e;border-radius:8px;padding:.8rem">
<h3 style="margin:0 0 .5rem">Unten</h3> <h3 style="margin:0 0 .5rem">Unten</h3>
<p id="setup-value-down" class="value">Gespeichert: lädt …</p> <p id="setup-value-down" class="value">Gespeichert: lädt …</p>
<button id="setup-save-down">Diese Position als Unten speichern</button> <button id="setup-save-down">Diese Position als Unten speichern</button>
+53 -13
View File
@@ -5,35 +5,35 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Halloween-Grab · Steuerung</title> <title>Halloween-Grab · Steuerung</title>
<style> <style>
:root { color-scheme: dark; font-family: system-ui, sans-serif; background: #111718; color: #eef0e9; } :root { color-scheme: dark; font-family: system-ui, sans-serif; background: #070a10; color: #eef4ff; }
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { margin: 0; } body { margin: 0; }
header { padding: 1.1rem max(1rem, calc((100vw - 1120px)/2)); background: #182122; border-bottom: 1px solid #3a4644; } header { padding: 1.1rem max(1rem, calc((100vw - 1120px)/2)); background: #0b111c; border-bottom: 1px solid #22334d; }
h1 { margin: 0; font-size: 1.5rem; } h1 { margin: 0; font-size: 1.5rem; }
header p { margin: .35rem 0 0; color: #c1cbc2; } header p { margin: .35rem 0 0; color: #a7b7d0; }
main { max-width: 1120px; margin: auto; padding: 1rem; display: grid; gap: 1rem; grid-template-columns: minmax(0, 2fr) minmax(290px, 1fr); } main { max-width: 1120px; margin: auto; padding: 1rem; display: grid; gap: 1rem; grid-template-columns: minmax(0, 2fr) minmax(290px, 1fr); }
section { background: #1a2424; border: 1px solid #394846; border-radius: 12px; padding: 1rem; } section { background: #0d1522; border: 1px solid #243550; border-radius: 12px; padding: 1rem; }
h2 { margin: 0 0 .7rem; font-size: 1.1rem; } h2 { margin: 0 0 .7rem; font-size: 1.1rem; }
.camera { width: 100%; aspect-ratio: 4/3; object-fit: contain; background: #050808; border-radius: 8px; } .camera { width: 100%; aspect-ratio: 4/3; object-fit: contain; background: #03060b; border-radius: 8px; }
.camera-wrap { position: relative; aspect-ratio: 4/3; } .camera-wrap { position: relative; aspect-ratio: 4/3; }
.camera-wrap .camera { position: absolute; inset: 0; height: 100%; } .camera-wrap .camera { position: absolute; inset: 0; height: 100%; }
#detection-overlay { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; cursor: crosshair; } #detection-overlay { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; cursor: crosshair; }
.row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; } .row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
button, select, input, a.download { border: 1px solid #5c6c67; border-radius: 7px; background: #293938; color: #f4f5ed; font: inherit; padding: .65rem .8rem; } button, select, input, a.download { border: 1px solid #344b6c; border-radius: 7px; background: #152238; color: #f1f6ff; font: inherit; padding: .65rem .8rem; }
button, select, a.download { cursor: pointer; } button, select, a.download { cursor: pointer; }
button:hover, a.download:hover { background: #36504a; } button:hover, a.download:hover { background: #1d3659; }
button:focus-visible, select:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid #ffb44f; outline-offset: 2px; } button:focus-visible, select:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid #ffb44f; outline-offset: 2px; }
button.danger { background: #782c26; border-color: #c76652; font-weight: 700; } button.danger { background: #782c26; border-color: #c76652; font-weight: 700; }
button.danger:hover { background: #9c352c; } button.danger:hover { background: #9c352c; }
button:disabled { opacity: .5; cursor: not-allowed; } button:disabled { opacity: .5; cursor: not-allowed; }
a.download { display: inline-block; text-decoration: none; } a.download { display: inline-block; text-decoration: none; }
nav a[aria-current="page"] { background:#46695b; border:2px solid #c4e0cb; font-weight:700; } nav a[aria-current="page"] { background:#123c69; border:2px solid #5aafff; font-weight:700; }
.value { font-weight: 650; color: #ffca78; } .value { font-weight: 650; color: #8bd6ff; }
.notice { border-left: 4px solid #ffb44f; padding: .55rem .8rem; background: #302b21; margin: .8rem 0; } .notice { border-left: 4px solid #ffb44f; padding: .55rem .8rem; background: #211c14; margin: .8rem 0; }
.muted { color: #bfcbc6; } .muted { color: #acbdd4; }
#message { min-height: 1.5rem; color: #ffca78; } #message { min-height: 1.5rem; color: #8bd6ff; }
dl { display: grid; grid-template-columns: auto 1fr; gap: .4rem .8rem; margin: .5rem 0 1rem; } dl { display: grid; grid-template-columns: auto 1fr; gap: .4rem .8rem; margin: .5rem 0 1rem; }
dt { color: #adbfba; } dd { margin: 0; } dt { color: #a7b7d0; } dd { margin: 0; }
.events { grid-column: 1 / -1; } .events { grid-column: 1 / -1; }
#events-list { margin: .5rem 0 0; padding-left: 1.4rem; } #events-list { margin: .5rem 0 0; padding-left: 1.4rem; }
#events-list li { margin: .35rem 0; } #events-list li { margin: .35rem 0; }
@@ -67,6 +67,46 @@
section { padding: .8rem; } section { padding: .8rem; }
button, select, input { max-width: 100%; } button, select, input { max-width: 100%; }
} }
/* Static blue highlights: no animation loop, external fonts or graphics. */
:root { --accent: #62b4ff; --line: #294466; --muted: #acbdd4; }
body { background: radial-gradient(ellipse at 15% 0%, #101f39 0, transparent 48%), #070a10; }
header { background: #090f19; border-bottom: 1px solid #28466a; box-shadow: 0 5px 26px #0006; }
h1 { letter-spacing: -.035em; font-weight: 750; display: flex; align-items: center; gap: .65rem; }
h1::before { content: ''; display: inline-block; flex: 0 0 12px; width: 12px; height: 12px; border: 2px solid #89d7ff; border-radius: 4px; background: #1475c5; box-shadow: 0 0 16px #238bff70; }
header p { font-size: .92rem; }
section { background: linear-gradient(145deg, #101c2d, #0b121e 65%); border: 1px solid #29405f; border-radius: 16px; box-shadow: 0 10px 28px #0004, inset 0 1px 0 #a7cfff0a; }
h2 { display: flex; align-items: center; gap: .55rem; letter-spacing: -.015em; font-weight: 700; }
h2::before { content: ''; width: 3px; height: 1em; border-radius: 2px; background: var(--accent); box-shadow: 0 0 10px #339eff60; }
nav { gap: .6rem !important; }
nav a.download { border-radius: 10px; background: #101b2b; border-color: #293e5a; }
nav a[aria-current="page"] { background: linear-gradient(135deg,#174b7c,#122d50); border: 1px solid #69b6ff; box-shadow: 0 0 18px #1987ff28, inset 0 1px 0 #a0d7ff22; }
button, a.download, select, input { border-radius: 9px; }
button, a.download { transition: border-color .15s ease, background-color .15s ease; }
button:not(:disabled):hover, a.download:hover { border-color: #72bdff; background: #1b385a; box-shadow: 0 0 14px #278fff24; }
button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible, summary:focus-visible { outline: 3px solid #84d4ff; outline-offset: 3px; }
input[type="number"], input[type="text"], select { background: #080f1b; border-color: #3b5375; color: #f1f6ff; }
input[type="checkbox"] { accent-color: #459fff; width: 1.05em; height: 1.05em; flex-shrink: 0; }
button:disabled { opacity: .55; box-shadow: none; }
#follow-hardware, #save-mode, #save-detection { background: linear-gradient(135deg,#1768b5,#164880); border-color: #69b6ff; box-shadow: 0 0 20px #1684ff24; font-weight: 650; }
#follow-hardware:not(:disabled):hover, #save-mode:hover, #save-detection:hover { background: #1c6db5; box-shadow: 0 0 22px #1684ff45; }
button.danger { background: #381c27; border-color: #bf5d72; color: #ffe8ee; box-shadow: none; }
button.danger:not(:disabled):hover { background: #5b2433; border-color: #ff8da2; box-shadow: 0 0 14px #e94a6820; }
#follow-status { background: linear-gradient(110deg,#102b49,#0b1a2e); border-color: #3677ac; color: #c5e9ff; box-shadow: 0 0 18px #1684ff12; line-height: 1.5; }
#follow-camera-status, #camera-status { color: #a9c7e9; font-size: .92rem; font-variant-numeric: tabular-nums; }
.follow-picture, .camera-wrap { border-radius: 12px; box-shadow: 0 0 0 1px #34547c, 0 0 24px #1782ff12; overflow: hidden; }
.camera, .follow-canvas { border-radius: 12px; }
.value { color: #91d4ff; font-variant-numeric: tabular-nums; }
summary { color: #d8e9ff; }
summary::marker { color: #65b7ff; }
details[open] > summary { color: #8fd0ff; }
.settings-card { border-color: #294466; }
.calibration-card, .endpoint-grid > div { background: #0a1423; border-color: #2d4c70; }
.endpoint-grid > div { box-shadow: inset 0 1px 0 #9dd1ff0a; }
.notice { background: #211c14; color: #f1dbb5; border-left-color: #ddb361; border-radius: 0 8px 8px 0; }
dl { background: #0a1423; border: 1px solid #263c59; border-radius: 10px; padding: .9rem; }
a { color: #9bd6ff; }
@media (prefers-reduced-motion: reduce) { button, a.download { transition: none; } }
</style> </style>
</head> </head>
<body> <body>
+5
View File
@@ -219,3 +219,8 @@ unter `tests/` und die Bedienhinweise in `SOFTWARE.md` prüfen.
vorhandene PWM hält Position. Zwei aufeinanderfolgende Funde setzen Tracking vorhandene PWM hält Position. Zwei aufeinanderfolgende Funde setzen Tracking
fort. Keine automatischen weiteren Suchrunden ohne erneuten Fund. fort. Keine automatischen weiteren Suchrunden ohne erneuten Fund.
Stopp/Fehler/Neustart beenden weiterhin die Freigabe und lassen die Farbe stehen. Stopp/Fehler/Neustart beenden weiterhin die Freigabe und lassen die Farbe stehen.
- Gestaltung beider Seiten schwarz/blau, Glow über statische CSS-Schatten.
Keine Animationsschleifen, externen Schriftarten, Bilder oder UI-Bibliotheken.
Kurze Hover-Übergänge respektieren prefers-reduced-motion. Stopp rot und
Hinweise amberfarben lassen; Canvas-Erkennungsfarben/Koordinaten unverändert.