From 4ba6dea45a2e6decf8364d71a7ef8ca045c2e4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20Rosenl=C3=B6cher?= Date: Mon, 19 Oct 2026 17:31:48 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20T5=20Optik=20in=20ARCHITECTURE=20=C2=A7?= =?UTF-8?q?15,=20Annahmen=20119=E2=80=93124,=20TASKS,=20Spec-Abweichungen?= =?UTF-8?q?=20(T5b=20B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ARCHITECTURE: neuer Abschnitt 15 (Tokens und vier Farbsätze, Darstellung, Baukasten und Wächter, Bewegung und Budgets, Beute in Sonne, Wabe und Kartenmarker, Lebenslinie, Bienenjahr-Rad, Manifest/Icons/SW), §11.7 Königinnen mit automatischem Endstatus, Annahmen 119–124. - TASKS: Abschnitt T5 mit erledigten und offenen Punkten. - Spec: 18 markierte Abweichungen bei der Umsetzung mit Grund. Co-Authored-By: Claude Opus 5.5 (1M context) --- ARCHITECTURE.md | 79 ++++++++++++++++++- TASKS.md | 30 ++++++- .../specs/2026-10-13-t5-optik-design.md | 43 +++++++++- 3 files changed, 146 insertions(+), 6 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 6a26cc5..45b1cdf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -74,6 +74,8 @@ Zentrale Schnellaktion "+ Neu" (Abschnitt 22): permanent sichtbarer Floating-But **Navigation aus einer Quelle:** `app/(app)/nav-config.ts` ist die einzige Quelle für Seitenleiste, Bottom-Nav, „Mehr“-Sheet und „+ Neu“; der Berechtigungsfilter gilt überall gleich. Neue Seiten (auch „Verwaltung > System“) werden nur dort eingetragen. Auf allen `new`-Seiten führt ein Zurück-Link zur Liste, bei einem Entwurf mit Rückfrage. „Mehr“ und „+ Neu“ teilen sich die Sheet-Hülle `components/nav/nav-sheet.tsx`. +**Bereiche und Motiv (T5):** `src/lib/areas.ts` (`AREAS`) ist die einzige Quelle für Icon und Farbton je Bereich. Navigation, „+ Neu“, Seitenköpfe (`PageHeader`, `DetailHeader`, `EntityCard` mit Prop `area`) und Leerzustände lesen daraus; ein Icon steht immer für genau einen Bereich. Die Wabenzelle ist das einzige grafische Motiv: `AreaIcon`, `BrandMark`, Waben-Standkarte, Kartenmarker und die Kappe am Zeiger des Bienenjahr-Rads. Farbverläufe gibt es nicht mehr. Einzelheiten: Abschnitt 15. + **Offline-Seiten:** Ohne Netz zeigen `/offline` (Start: geladene Stände), `/offline/apiary?id=…` und `/offline/colony?id=…` die Daten aus den Offline-Paketen (IndexedDB). QR- und NFC-Deep-Links (`/apiaries/`, `/colonies/`) weicht der Service Worker ohne Netz auf diese Seiten aus; der Weg zurück („Mit Netz öffnen“) übernimmt `from` nur bei gleicher Herkunft (same-origin). Formulare lesen Völker und Königinnen aus dem Paket, wenn die API nicht antwortet (`withPackFallback`, `networkMode: "always"`). **Beute offline (Abschnitt 13.1):** Das Paket (`offline-pack`) trägt optional `hives` (Aufbau je Volk) und `hiveCatalog` (aktive Systeme plus die von den Aufbauten genutzten); ein älteres Paket ohne diese Felder sagt „unbekannt“, nicht „kein Aufbau“ (`hiveFromPacks`, `lib/hive/pack.ts`). Änderungen ohne Netz landen im IndexedDB-Speicher `pending-hive-changes` (DB-Version 5, `queueHiveChange`). `drainTimeline` (`lib/offline/sync.ts`) sendet diese Einträge und die Durchsichten, die eine `hiveChange` tragen, gemeinsam nach `queuedAt`, nacheinander. Netzfehler und 5xx stoppen den Lauf; jede andere Ablehnung (`HIVE_CONFLICT`, 403, 400) wird am Eintrag gespeichert, der Eintrag bleibt in der Warteschlange und hält die späteren Einträge desselben Volks zurück, andere Völker laufen weiter. **Überholschutz:** Solange für ein Volk eine Beute-Änderung wartet (auch eine abgelehnte), reiht `useHiveMutation` jede neue Kartenänderung hinter sie ein, statt online zu senden (`hasQueuedHiveChange`). Die Karte zeigt die wartenden Operationen auf dem geladenen Stand; eine abgelehnte Änderung zeigt `HivePendingNote` als Hinweis mit Weg in die Warteschlange (Annahme 102). @@ -241,6 +243,12 @@ Fachliche Annahmen, getroffen weil das Lastenheft an der Stelle keine eindeutige 116. **Der Text einer Sprachnotiz darf zu Anthropic (Drittland)**: Der Inhaber hat den API-Schlüssel angelegt (2026-10-16); das gilt als Freigabe von Spec-Frage 3. Modell `claude-sonnet-5-5` mit `effort: low` (Frage 1); die Cloud-Erkennung des Browsers bleibt ohne Einwilligung aus (Frage 2). Eine Region ist nicht gesetzt (`inference_geo` bleibt aus), es gilt die Voreinstellung des Anthropic-Arbeitsbereichs. Kosten bei falscher Annahme: Fehlt die Freigabe, bleibt der Worker ohne das Secret `apicore-anthropic`, die Notizen bleiben `TRANSCRIBED` und sind als Text nutzbar; Datenschutzerklärung und Verzeichnis müssen die Übermittlung vor dem Einschalten nennen. Abschnitt 14.7. 117. **Die Grenzen der Vorschläge folgen dem Formular, nicht der Spec**: Volksstärke bis 40 und fünf Futterstufen, wie `CreateInspectionDto` und das Formular; für `broodFrames` ohne Obergrenze im Formular gilt im Sprachweg 40. Kosten bei falscher Annahme: Die Auswertung schlägt Werte vor, die der Autor der Spec nicht erwartet hat, oder verwirft zulässige (sie landen als Zahl in `dropped`); die Grenzen stehen an einer Stelle (`core/inspection-field-specs.ts`). Abschnitt 14.4. 118. **Meldungen zu Sprachnotizen gehören in die Kategorie „Vorschläge“**: „Sprachnotiz ist fertig“ und „konnte nicht ausgewertet werden“ haben die Kategorie `SUGGESTION`, weil sie zu Vorschlägen führen. Kosten bei falscher Annahme: Wer die Kategorie „Vorschläge“ in Telegram oder der App abschaltet, bekommt auch diese Meldungen nicht mehr; die Notiz bleibt unter `/voice` sichtbar. Abschnitt 14.5. +119. **Die Darstellung gilt je Gerät, nicht je Konto**: Farbschema (hell, dunkel, wie Gerät), Kontrast (normal, Sonne) und Bewegungsstufe (Voll, Dezent, Aus) liegen in `localStorage` (`apicore.theme`, `apicore.contrast`, `apicore.motion`), nicht im Benutzerkonto. Ohne gespeicherte Wahl gilt hell, auch auf einem Gerät im Dunkelmodus; `prefers-contrast: more` schaltet Sonne vor, solange nichts gewählt ist. Ein Inline-Skript im `` (`AppearanceScript`) setzt die Werte vor dem ersten Bild. Es braucht `script-src 'unsafe-inline'`, das Next.js ohnehin verlangt (Abschnitt 9); wird die CSP verschärft, bekommt das Skript einen Hash (sein Inhalt ist fest, ohne Nutzerdaten). Ist der Speicher gesperrt, gilt die Wahl nur für den Besuch. Abmelden lässt die Werte stehen. Kosten bei falscher Annahme: Wer auf einem zweiten Gerät arbeitet, stellt die Darstellung dort neu ein. Ein Konto-Feld bräuchte Migration, Endpunkt und einen Abgleich nach dem Login, und das erste Bild käme trotzdem vor den Kontodaten. Abschnitt 15.2. +120. **Ein Baukasten, durch Tests erzwungen**: Auswahlgruppe (`ChoiceGroup`), Checkbox und Schalter (`Checkbox`, `CheckboxField`, `Switch`), Reiter (`Tabs`, `ActiveHistoryTabs`, `TabPicker`), Plus/Minus (`NumberStepper`), Bereichs-Icon (`AreaIcon`) und Markenzeichen (`BrandMark`) gibt es je genau einmal. `src/styles/baukasten-guard.spec.ts` durchsucht `src` und schlägt bei jedem Eigenbau fehl (Regeln in Abschnitt 15.3). Jede Ausnahme steht mit Grund in der Liste, und ein Test prüft, dass sie noch gebraucht wird. Ausnahmen heute: der eigene Fokus-Pfad der Wabenzelle (`honeycomb-cell.tsx`), der Fokus nach einem Skript-Sprung in `print-labels-card.tsx` und `social-media-tab.tsx`, die Rollen in `choice-group.tsx` und `aria-pressed` in `ui/button.tsx` und an den Kalendertagen. Kosten bei falscher Annahme: Ein Sonderfall, der wirklich anders aussehen muss, braucht einen Eintrag mit Grund im Wächter statt einer stillen Abweichung; das kostet einen Review-Schritt, verhindert aber Kopien. Abschnitt 15.3. +121. **Die offenen Fragen der T5-Spec gelten vorläufig mit Antwort A**: Hauptknopf in hellem Honig mit dunkler Tinte (Frage 1), Zellen der Waben-Standkarte nach Volkscode (Frage 2), hell als Standard auf einem neuen Gerät (Frage 3), ruhige Bewegung in der App mit der Beute als einziger Bühne (Frage 4). Der Inhaber hat sie noch nicht bestätigt. Kosten bei falscher Annahme: je Frage eine Token- oder Konstantenänderung (`--primary*` in `globals.css` zusammen mit `contrast.spec.ts`, `sortCells` in `lib/honeycomb/cell-state.ts`, `DEFAULT_THEME` in `lib/appearance/appearance-core.ts`, Bewegungs-Tokens in `lib/motion/tokens.ts`). Nur Frage 2 B (echte Reihenfolge am Stand) bräuchte ein neues Feld `Colony.standSlot` mit Migration (etwa 1 PT). Spec §16. +122. **Die Stärke-Kurve der Lebenslinie beginnt am 17.10.2026**: Seit T3 zählt `Inspection.colonyStrength` besetzte Waben, vorher war es eine Skala. Das Frontend mit dem Waben-Stepper ging am 16.10.2026 um 22:28 live (sha-cda3e02); der erste volle Tag ist der 17.10. (`STRENGTH_IN_FRAMES_SINCE` in `lib/colonies/lifeline.ts`). Ob ein Wert davor liegt, entscheidet `createdAt` der Durchsicht, ersatzweise ihr Datum. Ältere Werte stehen nur in der Tabelle mit der Einheit „Skala (alt)“. In Produktion gibt es zwischen 10.10. und 19.10. keine Durchsicht, also keinen Wert auf der Grenze. Kosten bei falscher Annahme: Die Kurve mischt Skala und Waben, oder echte Wabenwerte fehlen in der Kurve; die Konstante wird geändert, kein Datenumbau. Abschnitt 15.7. +123. **Die Bienenstände-Liste zeigt die Wabe nur für die ersten 20 Stände**: Jede Karte lädt ihre Übersicht einzeln (`GET /apiaries/:id/colony-overview`, derselbe Cache-Schlüssel und derselbe Paket-Rückfall wie die Stand-Seite). Ab dem 21. Stand zeigt die Karte nur Name und Ort (`MAX_COMB_CARDS`). Einen Sammel-Endpunkt gibt es nicht. Kosten bei falscher Annahme: Beim Öffnen der Liste gehen bis zu 20 Anfragen raus, und ein großer Betrieb sieht spätere Stände ohne Wabe. Abhilfe ist ein Sammel-Endpunkt für die Übersicht mehrerer Stände (Folgepunkt in TASKS.md). Abschnitt 15.6. +124. **Das Bienenjahr-Rad zeigt den Abschnitt der meisten Stände**: Bezugsstand ist der Abschnitt, in dem die meisten aktiven Stände sind; bei Gleichstand gilt der im Bienenjahr weiter fortgeschrittene. Abweichende Stände nennt eine Zeile unter dem Rad und die Tabelle. Liefert das Backend keinen `seasonCalendar` (älterer Stand), gelten `SEASON_DEFAULTS` (`lib/season/season-defaults.ts`); ein Test hält sie gleich mit `phenology.config.ts` und `season-autopilot.config.ts` des Backends. Kosten bei falscher Annahme: Wer Stände in sehr verschiedenen Lagen hat, sieht im Zentrum den Mehrheitsabschnitt; ein früherer Stand steht nur in der Zeile darunter. Der Fehler geht zur sicheren Seite: lieber eine Durchsicht zu früh als einen Schwarm verpasst. Abschnitt 15.8. ## 7. Kubernetes @@ -606,7 +614,7 @@ Als „letzte Behandlung“ zählt das späteste Ende (`endDate ?? date`); eine - Aktion: `CAPTURE` (Formular-Link; auch Ersatz, wenn `tasks:create` fehlt und die Absicht ein Formular hat), `ACCEPT` (nur mit `tasks:create`), `CLEANUP`, `OPEN_TASK` (`/tasks`). Ohne mögliche Aktion entfällt der Schritt. `withdrawalUntil` (nur mit `treatments:read`) und `inspectionDueOn` (nur mit `inspections:read`, `null` bei abgeschalteter Regel oder in der Winterruhe) sind Hinweise, keine Schritte. Das Erfassen einer Durchsicht erledigt Ketten-Vorschläge (`chains.queen-cells`) und führt Regel-Vorschläge nach (Annahme 12). - `GET /apiaries/:id/colony-overview` trägt je Volk zusätzlich `occupiedFrames`, `inspectionDueOn`, `segment` (oder `null`) und `nextStepTitle`; der Offline-Pack enthält `nextSteps` je Volk. -**Oberfläche.** Volksseite: „Als Nächstes“ (bis 3 Schritte mit Gründen, Fälligkeitschip, Aktion, Verwerfen; „Wartezeit bis TT.MM.“ als Hinweis; Fehlerzustand mit Wiederholen). Startseite „Heute“: der dringlichste Schritt, Karten „Durchsicht fällig – “ mit Saison-Chip und „Runde starten“, Chips „Vorschläge “ (nur mit `tasks:read`) und „: “; bei Fehler, offline oder ohne Recht greift die bisherige Aufgabenlogik. Stand-Seite: je Volk „Durchsicht fällig in 2 Tagen“ und „Als Nächstes: …“. Offline zeigt die Volksseite die Schritte aus dem Paket nur lesend (ohne Übernehmen, Verwerfen, Aufräumen). Aufräumen öffnet das Sheet aus Abschnitt 11.7; Übernehmen aus Schritten zeigt „Aufgabe angelegt“. +**Oberfläche.** Volksseite: „Als Nächstes“ (bis 3 Schritte mit Gründen, Fälligkeitschip, Aktion, Verwerfen; „Wartezeit bis TT.MM.“ als Hinweis; Fehlerzustand mit Wiederholen). Startseite „Heute“: der dringlichste Schritt, Karten „Durchsicht fällig – “ mit Saison-Chip und „Runde starten“, der Chip „Vorschläge “ (nur mit `tasks:read`); die Saison je Stand steht seit T5 im Bienenjahr-Rad (Abschnitt 15.8), die Chip-Zeile „: “ ist entfallen; bei Fehler, offline oder ohne Recht greift die bisherige Aufgabenlogik. Stand-Seite: je Volk „Durchsicht fällig in 2 Tagen“ und „Als Nächstes: …“. Offline zeigt die Volksseite die Schritte aus dem Paket nur lesend (ohne Übernehmen, Verwerfen, Aufräumen). Aufräumen öffnet das Sheet aus Abschnitt 11.7; Übernehmen aus Schritten zeigt „Aufgabe angelegt“. ### 11.6 Biotechnik-Ketten @@ -618,7 +626,7 @@ Drei Ketten schlagen die nächsten Prüfschritte vor (Handler `chains.*`, Tabell ### 11.7 Volk beendet und Aufräumen -Jeder Weg, auf dem ein Volk endet (Auflösen, Vereinigen, Bearbeiten auf `SOLD`/`DECEASED`, Auswinterungsverlust mit `markColonyDeceased`), beendet in derselben Transaktion die Königin-Zuordnung über `releaseQueenIn` (`src/colonies/colony-lifecycle.ts`): Die offene `QueenColonyHistory` bekommt `endDate`, `Colony.currentQueenId` wird `null`, der Status der Königin ändert sich nicht. Das Enddatum ist der Berliner Tag der Änderung (bei der Auswinterung das Datum der Prüfung). Beim Bearbeiten greift das nur beim Übergang von lebend nach beendet. Ein Wächter (`colony-end-releases-queen.spec.ts`, TypeScript-Compiler-API) fordert, dass jede Stelle, die einen beendeten Status schreibt, vorher `releaseQueenIn(` aufruft. `ColonyEnded` trägt `{ status, endedOn, releasedQueenId, via }` (`via`: `DISSOLVE`, `MERGE`, `EDIT`, `WINTER_CHECK`); ältere Ereignisse ohne diese Felder fallen auf den Berliner Tag des Ereignisses und `EDIT` zurück. +Jeder Weg, auf dem ein Volk endet (Auflösen, Vereinigen, Bearbeiten auf `SOLD`/`DECEASED`, Auswinterungsverlust mit `markColonyDeceased`), beendet in derselben Transaktion die Königin-Zuordnung über `releaseQueenIn` (`src/colonies/colony-lifecycle.ts`): Die offene `QueenColonyHistory` bekommt `endDate`, `Colony.currentQueenId` wird `null`. Danach setzt `QueensService.retireIn` in derselben Transaktion den Endstatus der Königin (Entscheidung des Inhabers 2026-10-19): Auflösen und Vereinigen setzen `REPLACED`, die Auswinterung mit Verlust setzt `DECEASED`, und eine Königin, die von einer neuen verdrängt wird, wird ebenfalls `REPLACED`. `retireIn` ändert nur einen verfügbaren Status (`ACTIVE`, `RESERVE`, `MATED`, `UNMATED`) und läuft über `updateStatusIn` mit Archivdatum (`deactivatedAt`) und Audit. Beim Bearbeiten auf `SOLD` oder `DECEASED` bleibt der Status der Königin; dort fragt das Aufräumen nach (unten). Ein Statuswechsel auf `LOST`, `DECEASED`, `REPLACED` oder `SOLD` setzt `deactivatedAt`, ein korrigierter Status nimmt es wieder weg (Migration `20261021100000_queen_deactivated_backfill` für den Bestand). Das Enddatum ist der Berliner Tag der Änderung (bei der Auswinterung das Datum der Prüfung). Beim Bearbeiten greift das nur beim Übergang von lebend nach beendet. Ein Wächter (`colony-end-releases-queen.spec.ts`, TypeScript-Compiler-API) fordert, dass jede Stelle, die einen beendeten Status schreibt, vorher `releaseQueenIn(` aufruft. `ColonyEnded` trägt `{ status, endedOn, releasedQueenId, via }` (`via`: `DISSOLVE`, `MERGE`, `EDIT`, `WINTER_CHECK`); ältere Ereignisse ohne diese Felder fallen auf den Berliner Tag des Ereignisses und `EDIT` zurück. Der Handler `suggestions.colony-ended` setzt offene Vorschläge des Volkes auf `OBSOLETE` und legt den Aufräum-Vorschlag an (Regel `COLONY_ENDED_CLEANUP`; Quelle `CHAIN`, Absicht `CLEANUP`, Art `DO`, Priorität `NORMAL`, `colonies:read`). Anker `ended--`, Beleg `{ colonyId, status, endedOn, via, openTaskIds, queenId }`; die Königin zählt nur, solange sie `ACTIVE` ist. Wird ein Volk reaktiviert (`reactivate`), werden seine offenen Aufräum-Vorschläge `OBSOLETE`; endet es am selben Tag erneut, entsteht ein neuer. @@ -1041,7 +1049,7 @@ Formfehler (Form der Anfrage, zukünftiger Tag, Tag vor `INIT`, mehr als 100 Ope - **Karte.** `components/hive/hive-card.tsx` auf der Volksseite (`mode` `view` oder `inspection`). Die Bühne (`HiveVisualSlot`) zeigt immer das SVG (`HiveSvg`, aus `lib/hive/layout.ts`), die 3D-Szene liegt darüber. Daneben stehen die Gliederung (`HiveOutline`, Baum aller Teile, per Tastatur bedienbar), Fakten und Fokusleiste. Teile und Rähmchen öffnen Sheets (`part-sheet`, `frame-sheet`); jede Änderung geht über `useHiveMutation` als `POST changes` mit eigenem `Idempotency-Key` je Versuch, einen Versand nach dem anderen, optimistisch auf dem Cache. Das Sheet zeigt bei `HIVE_CONFLICT` den aktuellen Stand aus der Antwort (`HiveConflictSheet`). Die Historie (`HiveHistory`) zeigt Änderungen und den Stand eines Tages (`GET at`). - **Editor** `/colonies/[id]/hive` („Umbauen“): Mehrere Schritte werden lokal als Entwurf gesammelt (`useHiveDraft`: Schritte, Rückgängig und Wiederholen, `snapshot` des Ausgangsstands, Annahme 104) und mit einer Änderung gespeichert. Bis dahin berührt der Entwurf den Server nicht; Verlassen der Seite mit offenen Schritten fragt nach. Der Editor lädt den Aufbau neu, sobald das Fenster den Fokus bekommt, und alle 30 s, solange ein Entwurf besteht (`REFRESH_INTERVAL_MS`). Umbauen geht nur mit Netz. - **Durchsicht.** `inspections/new` hat den Abschnitt „Beute“ (`hive-section.tsx`): die Karte im Modus `inspection` sammelt Operationen als Entwurf, sie gehen als `hiveChange` mit der Durchsicht in `POST /inspections` und in der Warteschlange mit dem Eintrag (Abschnitt 2). Die Sheets stoppen das `submit` an ihrem Formular (`stopPropagation`; sie liegen in einem Portal, doch React reicht Ereignisse im Komponentenbaum weiter), sonst würde „Speichern“ im Sheet die Durchsicht speichern; neue Formulare in Sheets müssen dasselbe tun. Zusätzlich stoppt der Abschnitt selbst `submit` an seinem Wrapper (`hive-section.tsx`). -- **3D-Szene.** `components/hive/hive-scene` (three, react-three-fiber, drei, postprocessing) wird nur über `next/dynamic` mit `ssr: false` geladen, sobald die Karte sichtbar wird (`IntersectionObserver`); andere Seiten laden nichts davon. Budget für den Chunk: höchstens 320 kB gzip (Spec 9.7); gemessen zuletzt etwa 296 kB. Die Szene misst die Bildrate (`quality.ts`): unter 45 fps fällt sie auf die Stufe `low` (kein Spiegelboden, kein MSAA, keine Tiefenunschärfe, keine Partikel), die Tiefenunschärfe läuft erst ab 55 fps. Bewegung folgt der Stufe `full`, `subtle` oder `off` (`useEffectiveMotionLevel`: Flüge, Überblendung 400, 150 oder 0 ms). **Rückfall auf das SVG** (`shouldUse3D`, `lib/hive/webgl.ts`): kein WebGL, erzwungene Farben, oder Bewegung `off` bei höchstens 2 Prozessorkernen; ein verlorener WebGL-Kontext baut die Szene einmal neu auf, der zweite lässt es beim SVG. Das SVG ist darunter immer gerendert und bleibt bedienbar, bis das erste 3D-Bild steht; im Druck entfällt die 3D-Ebene. +- **3D-Szene.** `components/hive/hive-scene` (three, react-three-fiber, drei, postprocessing) wird nur über `next/dynamic` mit `ssr: false` geladen, sobald die Karte sichtbar wird (`IntersectionObserver`); andere Seiten laden nichts davon. Budget für den Chunk: höchstens 320 kB gzip (Spec 9.7); gemessen zuletzt etwa 296 kB. Die Szene misst die Bildrate (`quality.ts`): unter 45 fps fällt sie auf die Stufe `low` (kein Spiegelboden, kein MSAA, keine Tiefenunschärfe, keine Partikel), die Tiefenunschärfe läuft erst ab 55 fps. Bewegung folgt der Stufe `full`, `subtle` oder `off` (`useEffectiveMotionLevel`: Flüge, Überblendung 400, 150 oder 0 ms). **Rückfall auf das SVG** (`shouldUse3D`, `lib/hive/webgl.ts`): kein WebGL, erzwungene Farben, oder Bewegung `off` bei höchstens 2 Prozessorkernen; ein verlorener WebGL-Kontext baut die Szene einmal neu auf, der zweite lässt es beim SVG. Das SVG ist darunter immer gerendert und bleibt bedienbar, bis das erste 3D-Bild steht; im Druck entfällt die 3D-Ebene. In der Sonne zeichnet die Szene Weiß auf Schwarz ohne Farbton (Abschnitt 15.5). ## 14. Sprache (T7 „Freihand“) @@ -1145,3 +1153,68 @@ Reihenfolge, jeder Schritt mit Freigabe: 4. **Anthropic.** Der Inhaber legt das Secret `apicore-anthropic` (Schlüssel `ANTHROPIC_API_KEY`) an und verschiebt den Schlüssel aus `apicore-backend-secret`, wo er über `envFrom` auch API- und Job-Pods erreichte; er setzt in der Anthropic-Konsole ein Ausgabenlimit (Vorschlag 10 $ im Monat). Mit 60 Aufrufen am Tag (`VOICE_DAILY_EXTRACTIONS`) bleibt der Verbrauch dahinter. 5. **Messung (Task 24)** auf dem Zielknoten: Speicherspitze des Whisper-Containers gegen 1280Mi (inklusive der Speicher-Volumes), Zeit und CPU-Drosselung, Trefferquote der Fachwörter (Ziel ≥ 90 %, sonst `medium` oder längere Fachwortliste), einmal `npm run eval:voice -- --live --yes`. 6. **Freigabe (Task 25).** `VOICE_RECORDING_ENABLED` auf `"true"` und Frontend ausrollen. Rückweg: Schalter auf `"false"` und die Komponente `voice` abschalten; offene Notizen laufen über die Fristen ab, es gehen nur flüchtige Daten verloren. + +## 15. Optik und Baukasten (T5) + +Die App soll draußen am Stand lesbar sein: in der Sonne, mit Handschuhen, mit einer Hand. Spec: `docs/superpowers/specs/2026-10-13-t5-optik-design.md`, Plan: `docs/superpowers/plans/2026-10-14-t5-optik.md`. T5a („Sonnentauglich und Baukasten“, Tasks A1–A12) ist seit 2026-10-19 produktiv. T5b („Handschrift“, Tasks B1–B8) bringt die Grafiken mit der Wabenzelle. Bis auf einen additiven Zusatz an `GET /autopilot/today` (Abschnitt 15.8) ist T5 reines Frontend. Annahmen 119–124. + +### 15.1 Tokens und vier Farbsätze + +- **Nur Tokens.** Farben stehen nur als CSS-Variablen in `src/app/globals.css`. Es gibt vier Sätze: hell (`:root`), dunkel (`.dark`), Sonne hell (`:root[data-contrast="sun"]`) und Sonne dunkel (`.dark[data-contrast="sun"]`). Sonne dunkel wiederholt jeden Wert von Sonne hell, damit die Kaskade nichts mischt. Der Druck nutzt immer den hellen Satz (`@media print`). +- **Tinte und Fläche.** Der Hauptknopf ist Honig (`--primary`) mit dunkler Tinte (`--primary-foreground`). Für Text, Links, Icons und den Fokusring auf hellen Flächen gibt es `--primary-ink`; `text-primary` ist verboten. Jede Zustandsfarbe hat drei Tokens: Fläche (`--condition-x`, `--warning`), Text auf der Fläche (`-foreground`) und Text oder Icon auf normalen Flächen und Tönungen (`-ink`). Dazu kommen Bereichstöne (`--area-*`), Zeichenfarben der Königin (`--queen-*`), Fokus (`--focus-width`, `--focus-offset`), Rahmen der Eingaben (`--control-border-width`) und die drei Randstufen der Wabe (`--comb-stroke-1` bis `-3`). +- **Sonne.** Reinweiße Fläche, fast schwarze Tinte, Rahmen in Tinte, keine Tönung, kein Schatten, keine Unschärfe. Der Fokusring ist 3 px breit, Eingaberahmen 2 px, Nebentext 16 px statt 14 px. +- **Wächter.** `src/styles/contrast.spec.ts` liest `globals.css` (über `src/styles/css-tokens.ts`) und misst jedes Paar der Spec-Tabelle 4.4 in allen vier Sätzen, dazu Tabs, Menüeinträge, Fokus um eine gewählte Honig-Option und die Beute-Bühne. Er prüft auch, dass jeder Wert im sRGB-Raum liegt und dass kein Satz ein Token auslässt. Tokenwerte ändern sich nur zusammen mit diesem Test. +- **Varianten.** `dark:` (als `:where(.dark, .dark *)`), `sun:`, `pointer-fine:`, `pointer-coarse:` und `motion-full:`. Klassen mit `dark:` gibt es im Code nicht; die Tokens tragen den Modus. +- **Schrift und Größen.** Fließtext, Beschriftungen, Auswahl-Knöpfe, Listentitel und Knopftext haben 16 px, Nebentext 14 px. `text-xs` ist 13 px und nur für Achsen, Legenden und Fußzeilen erlaubt. Ziele sind auf Touch-Geräten mindestens 44 px groß; das kompakte Maß (32/36 px) gibt es nur mit `pointer-fine:`, nie mit `sm:`. Icon-Knöpfe bekommen auf Touch eine Trefferfläche von 44 px über ein Pseudo-Element. +- **Fokus.** Eine Regel für alles: `:where(:focus-visible)` zeichnet einen durchgehenden Ring in `--ring` (= `--primary-ink`) mit 2 px Abstand (Sonne 3 px). Komponenten löschen ihn nicht. Firefox bekommt dieselbe Regel über `:-moz-focusring`. Menüeinträge zeigen den Fokus mit einem inneren Ring neben der Hervorhebung. + +### 15.2 Darstellung umschalten + +- **Ohne Flackern.** `components/appearance-script.tsx` rendert ein blockierendes Inline-Skript in den ``. Es liest die drei Werte (Annahme 119) und setzt vor dem ersten Bild `class="dark"`, `data-contrast` und `data-motion` auf `` sowie die `theme-color`-Metas (`#f9f4ea`, `#1a120d`, `#ffffff`, `#040302`). Das Skript entsteht aus denselben Konstanten wie `resolveAppearance()` in `lib/appearance/appearance-core.ts`; ein Test vergleicht beide über alle Kombinationen. `next-themes` ist entfernt. +- **Hook.** `useAppearance()` (`lib/appearance/appearance.ts`) liest und setzt die Werte. Er hört auf Systemeinstellungen (dunkel, `prefers-contrast: more`), andere Tabs und das eigene Ereignis `apicore:appearance`. `setMotion` löst weiter `MOTION_EVENT` aus, damit `useEffectiveMotionLevel()` sofort folgt. Der Toaster folgt dem Hook. +- **Bedienung.** Profil > Darstellung (`profile/appearance-section.tsx`): drei Auswahlgruppen als Karten mit dem Satz „Gilt nur für dieses Gerät.“. Im Benutzermenü schaltet „Sonnenmodus“ mit einem Tipp, danach ein Toast. Der Wechsel wirkt sofort und ohne Übergang. +- **Hinweis.** `components/appearance-notice.tsx` zeigt auf Start einmal je Gerät „Neu: Sonnenmodus für draußen – im Benutzermenü.“ mit „Ausprobieren“ und „Schließen“ (`apicore.notice.sun`). Beide Knöpfe setzen den Fokus auf die Überschrift der Seite. Ist die Sonne schon an, bleibt der Hinweis weg. + +### 15.3 Baukasten und Wächter + +- **Bausteine.** `ChoiceGroup` (`components/form/choice-group.tsx`, `appearance` `buttons` oder `cards`, gewählt mit Honig, 2 px Tinten-Rahmen und Haken; „Nicht geprüft“ gestrichelt), `Checkbox`, `Switch` und `CheckboxField` (`ui/checkbox.tsx`, `ui/switch.tsx`, `form/checkbox-field.tsx`; Formen `row`, `tile`, `task`; Enter schaltet nicht und sendet kein Formular ab, die Leertaste schaltet), `Tabs` mit der einen Variante `segment` (`ui/tabs.tsx`; Pfeiltasten bewegen den Fokus, Enter oder Leertaste wählt), `ActiveHistoryTabs` (wählt beim Fokus, Props `toolbar`, `toolbarAlign`, `heading`), `TabPicker` (unter 640 px eine Auswahlliste statt der Leiste), `NumberStepper` für ganzzahlige Zählwerte am Stand, `Button pressed` für Umschalter in Werkzeugleisten. `ToggleChip`, `SwitchRow` und `TabSwitcher` sind gelöscht. +- **Wächter.** `src/styles/baukasten-guard.spec.ts` (mit `guard-files.spec-helper.ts` und `touch-target-guard.spec.ts`) durchsucht `src/**/*.{ts,tsx}` und `globals.css`. Er schlägt fehl bei: eigenen Rollen oder Typen für Radio, Checkbox, Schalter und Reiter; `aria-pressed` außerhalb von `Button pressed` und Kalender; `ring-ring/50`, `outline-none` und eigenem Fokus-Rahmen; `text-primary` ohne `-ink` oder `-foreground`; `dark:`; Farbverläufen und `text-white`; Schrift unter 13 px; `sm:h-`, `sm:min-h-`, `sm:size-` und `md:text-sm`; Icon-Knöpfen nebeneinander ohne Abstand auf Touch; Listen von `CheckboxField` mit weniger als 4 px Abstand; und den Bewegungsregeln (15.4). Jede Regel hat einen Selbsttest mit Treffer und Nicht-Treffer. Ausnahmen: Annahme 120. +- **Bereiche.** `AREAS` (37 Bereiche) mit Icon, Ton und Bezeichnung; `AreaIcon` zeichnet das Icon in einer Wabenzelle im Bereichston, `BrandMark` das Markenzeichen. `lib/areas.spec.ts` prüft, dass jedes Icon genau einem Bereich gehört und jeder Eintrag in Navigation und „+ Neu“ sein Icon aus `AREAS` hat. Die Absichts-Icons der Vorschläge (`INTENT_ICONS` in `lib/suggestions/labels.ts`) sind eine eigene Tabelle, weil eine Absicht kein Bereich ist. + +### 15.4 Bewegung + +- **Stufen.** Es gilt die Bewegungsstufe je Gerät: „Voll“ (`full`), „Dezent“ (`subtle`, auch bei „Voll“ plus `prefers-reduced-motion: reduce`) und „Aus“ (`off`). Bei „Dezent“ bleiben nur Überblendungen, bei „Aus“ ist alles sofort. In JavaScript ist `useEffectiveMotionLevel()` (`lib/motion/motion-preference.ts`) die einzige Quelle, in CSS die Variante `motion-full:` für jede Verschiebung, Skalierung und Drehung. +- **Tokens.** `lib/motion/tokens.ts` und die Spiegel in `globals.css`: `--motion-fade` 150 ms, `--motion-enter` 200 ms, `--motion-settle` 350 ms, `--motion-moment` 420 ms, `--motion-shift` 8 px, `--ease-out-expo`, `--ease-in`, `--ease-spring` (aus der Feder der Beute, Steifigkeit 210, Dämpfung 21). `tokens.spec.ts` hält CSS, TypeScript und die Feder der Beute gleich. Kein DOM-Übergang ist länger als 420 ms; Ausnahmen sind die Beute-Bühne und das Wegfalten der Heute-Zeile (720 ms, gespeichert ist sofort). +- **Werkzeuge.** Seiten, Sheets, Dialoge, Menüs und Toasts laufen mit CSS (Base UI `data-starting-style`/`data-ending-style`). `motion` lädt nur für entfernte und umsortierte Listenzeilen: `components/app-motion.tsx` lädt im `(app)`-Layout das Modul `components/motion/motion-runtime.tsx` nach (`LazyMotion strict`, `domAnimation`, eigenes FLIP mit der Feder), `components/motion/presence-list.tsx` nutzt es (Heute-Liste, Warteschlange, Sprachnotizen). Bis das Modul da ist, ist die Liste ohne Bewegung. +- **Nie animiert.** Datenwerte (Zahlen, Ampeln, Balken, Kurven, Zellfüllungen, der Zeiger des Rads), Fehler und Warnungen, das Feld während der Eingabe, der Fokusring und der Wechsel der Darstellung. Bewegt werden nur `transform` und `opacity`, für Zustände auch Farben; `transition-all` ist verboten. +- **Wächter und Budgets.** Die Regelgruppe „Bewegung“ im Baukasten-Wächter prüft `motion-safe:` vor Bewegung, Puls und Drehen nur hinter `motion-full:`, `transition-all`, Breite und Höhe, Dauer über 420 ms, Hover mit Verschiebung, Übergänge nur aus den Tokens und die erlaubten Importe aus `motion/react`. Dateien mit Datenwerten (Wabe, Karte, Lebenslinie, Rad, Varroa-Diagramm) dürfen gar keinen Übergang haben. Der Image-Build prüft zwei Budgets: `npm run check:motion-budget` (alle `motion`-Chunks zusammen höchstens 30 kB gzip, zuletzt 28,1 kB, in keinem ersten Laden) und `npm run check:3d-budget` (höchstens 320 kB, zuletzt 301,3 kB). + +### 15.5 Beute-Bühne in der Sonne + +- Die Bühne ist eine eigene, immer dunkle Fläche mit eigenen Tokens (`--holo-*`); nur hier gibt es Glas, Leuchten und Ruhebewegung. `.holo-stage` setzt `--ring` auf `--holo-edge-strong`, weil der Ring der Seite auf der Bühne unter 3:1 läge; Karten auf der Bühne (`.stage-card`) bekommen den Ring der Seite zurück. +- `useHoloTheme()` (`hive-scene/theme.ts`) liest die Tokens neu, sobald sich Farbschema oder Kontrast ändern (Ereignis `apicore:appearance` und ein `MutationObserver` auf ``), nie je Bild. Bei gleichen Werten bleibt das Theme-Objekt, und kein Material wird neu gebaut. +- In der Sonne zeichnet die Szene Weiß auf Schwarz ohne Farbton: kein Bloom, keine Vignette, kein Dunst, keine Partikel, kein Spiegelboden, kein Raster. Kanten sind 2 px breit (3 px bei Hover, Auswahl und aktivem Ablageplatz). Rähmchen-Arten unterscheiden sich durch Schraffuren statt durch Farbe. Etiketten haben 16 px auf fester Fläche, Glas hat keine Unschärfe. + +### 15.6 Wabe: Standkarte und Kartenmarker + +- **Modell.** `lib/honeycomb/cell-state.ts` (`cellModelOf`, `rimStepOf`, `summarizeColonies`, `sortCells`) und `lib/honeycomb/layout.ts` (`combLayout`, `columnsFor`) sind reine Funktionen. Die Füllung kommt aus `colonyStatusLight` (T3/T4). Der Rand zeigt die Durchsicht in drei Stufen: frisch, bald fällig (heute bis in 3 Tagen) und fällig oder nie durchgesehen (gestrichelt, mit Uhr). Ohne `inspectionDueOn` (altes Paket) gelten 10 und 14 Tage. Ohne `inspections:read` hat die Zelle einen neutralen Rand ohne Aussage. Der Rand ist in der Tinte der Füllung gezeichnet (`--condition-*-foreground`). +- **Ansicht.** `components/honeycomb/apiary-comb.tsx` zeigt Zusammenfassung, Wabe, Legende, „Braucht Blick“ und die aufklappbare Liste „Alle n Völker als Liste“ (die T3-Zeilenliste als Tabellen-Rückfall). Sie dient der Stand-Seite, der Offline-Standseite (aus dem Paket) und, über `apiary-comb-card.tsx`, der Liste `/apiaries` (Annahme 123). Jede Zelle ist ein Link zum Volk mit vollem Namen („B3 – Achtung: …“). Nur die Sechseckfläche nimmt Zeiger an (`comb.css`), damit sich die Treffer verzahnter Reihen nicht überlappen. Den Fokus zeichnet ein eigener Sechseckpfad (einzige Ausnahme von `outline-none`). Die Spaltenzahl misst `useContainerWidth` vor dem ersten Bild. +- **Karte.** `lib/honeycomb/mini-comb-svg.ts` liefert je Stand einen SVG-String (48 × 52 px): Rosette aus bis zu 7 Zellen, das schlechteste Volk in der Mitte, Kreuz und Ausrufezeichen für Rot und Gelb, bei mehr als 7 Völkern ein Zählchip. `components/apiary-map.tsx` setzt ihn als `divIcon` mit `aria-label` („Stand Nordhang: 7 Völker, 1 kritisch, 1 Achtung“). Enter oder Tippen öffnet das Popup mit Zusammenfassung und „Stand öffnen“ (bekommt den Fokus). Die Seite `/apiaries/map` lädt die Übersichten mit `useQueries` je Stand mit Koordinaten (derselbe Schlüssel wie die Stand-Seite) und zeigt darunter die Liste „Stände auf der Karte“ sowie ohne Netz einen Hinweis; beide gehen auch, wenn Leaflet nicht lädt. `src/styles/leaflet-theme.css` bringt Popup, Zoomknöpfe und Schrift auf die Tokens und dunkelt die Kacheln im Dunkelmodus ab. Die PNG-Marker sind gelöscht. Zoom- und Marker-Animationen laufen nur bei „Voll“; das Verschieben per Pfeiltaste animiert Leaflet immer (Folgepunkt in TASKS.md). + +### 15.7 Lebenslinie + +- **Modell.** `buildLifeline(input, today, options)` (`lib/colonies/lifeline.ts`) ist eine reine Funktion. `useColonyLifeline(colonyId)` sammelt die Daten über dieselben Schlüssel wie die Karten der Volksseite, also ohne Doppel-Anfragen: Volk, Historie, Durchsichten, Varroa, Behandlungen, Fütterungen, Schwarmkontrollen, höchstens 5 Königinnen und `next-steps`. Fehlt ein Leserecht (403), fällt die Spur still weg. Offline zeigt die Karte einen Hinweis. +- **Ausschnitt.** 12 Monate zurück plus 21 Tage voraus; „Ganzes Leben zeigen“ zeigt die ganze Lebenszeit. Die Stärke-Kurve beginnt bei `STRENGTH_IN_FRAMES_SINCE` (Annahme 122). Eine Lücke über 45 Tage unterbricht die Linie. Milben zählen nur aus dem natürlichen Milbenfall; die Ampel kommt aus `rating.light` der Messung. +- **Ansicht.** `components/colonies/colony-lifeline.tsx` (Karte und Zustände), `lifeline-svg.tsx` (ein SVG in der gemessenen Breite, alle Beschriftungen als HTML), `lifeline-table.tsx` (Tabelle mit derselben Zeitspanne, immer im DOM, per `hidden` versteckt). Die Zukunftszone ist in Zeilen geteilt (22 px je Schritt, höchstens 3). Die Milben-Skala reicht bis zum Doppelten der höchsten Grenze. Ereignis-Icons, die näher als 20 px beieinander liegen, werden ein Icon mit Zähler. Die Karte steht auf der Volksseite in Zeile 2 über den Karten zu Durchsichten, Varroa usw.; höchstens 600 SVG-Knoten für drei Jahre, keine Animation. + +### 15.8 Bienenjahr-Rad + +- **Backend-Zusatz.** `GET /autopilot/today` trägt je Stand zusätzlich `segmentSince`, `reportedOn` und `note` (`NO_COORDINATES`, `WEATHER_GAPS`, `WEATHER_STALE`) und auf oberster Ebene `seasonCalendar` (`calendarFallback` mit 8 Phasen, `swarmSeason`, `winterVarroaWindow`) aus den Regeln `PHENOLOGY` und `SEASON_AUTOPILOT`, auch bei abgeschalteter Regel. Die Antwort ist nur erweitert; Recht weiter `colonies:read`, keine Migration. +- **Modell.** `buildYearWheel(input, today)` (`lib/season/year-wheel.ts`) liefert sechs Abschnitte als lückenlosen Ring ab dem aktuellen, die Arbeitsfenster (Tracht, Varroa Sommer, Einwinterung, Varroa Winter) dieses und des nächsten Bienenjahrs, Aufgabenpunkte je Woche (bis 51 Wochen voraus) und den Bezugsstand mit seinen Abweichungen (Annahme 124). Ohne `segmentSince` oder `seasonCalendar` greift der Kalender aus `SEASON_DEFAULTS`. +- **Ansicht.** `components/season/year-wheel.tsx` auf dem Dashboard als letzter Abschnitt „Bienenjahr“: SVG 320 × 320 (skaliert darunter mit), Monatsinitialen und Zentrum als HTML, Zentrum mit höchstens drei einzeiligen Zeilen über Container-Queries, darunter Legende, „Jetzt … · Danach …“ und eine Zeile für Wetter-Hinweis und abweichende Stände. „Als Tabelle“ öffnet drei Tabellen (Abschnitte, Arbeitsfenster, Aufgaben je Woche). Das Rad nutzt die Abfragen des Dashboards (`["tasks", "dashboard"]`, `autopilotTodayKey`), keine neue Anfrage. Die Kacheln „Überblick“ und die Abfrage `/dashboard/summary` sind aus dem Frontend entfernt (der Endpunkt bleibt), ebenso die Chip-Zeile „Saison je Stand“ in „Heute“. + +### 15.9 Manifest, Icons, Service Worker + +- Master ist `public/brand/apicore-mark.svg` (dieselben Pfade wie `BrandMark`). `npm run icons` (`scripts/generate-icons.mjs`, `sharp` als devDependency) erzeugt daraus `public/icons/*` (192/512 „any“ und „maskable“, 512 „monochrome“, drei Shortcut-Icons), `src/app/apple-icon.png`, `src/app/favicon.ico` (16/32/48) und `public/icon.svg`. Zwei Läufe ergeben dieselben Bytes. Die Dateien liegen im Repo. +- `src/app/manifest.ts`: Hintergrund- und Theme-Farbe = heller Seitenhintergrund (`#f9f4ea`), Shortcuts Durchsicht, Varroa und Heute. Der Proxy-Matcher nimmt `/icons/*` und `/apple-icon.png` von den Kopfzeilen aus. +- Der Service Worker heißt `apicore-shell-v5` und frischt die Icons bei jedem Aufwärmen auf. Android tauscht das Homescreen-Icon von selbst (das kann Stunden dauern); iOS erst, wenn die App entfernt und neu hinzugefügt wird. diff --git a/TASKS.md b/TASKS.md index 16b6b35..de80e9c 100644 --- a/TASKS.md +++ b/TASKS.md @@ -174,6 +174,7 @@ Reihenfolge = Bau-Reihenfolge. Gleiche Haken-Regel wie MVP: erst setzen, wenn Li - [x] Navigation, Dashboard, Login neu gestaltet (Hexagon-Markenzeichen, aktiver Link-Zustand, Waben-Signaturmuster) - [x] Verbleibende Bestandsseiten (Listen/Details aller MVP-Module) auf einheitlichen Karten-/Badge-/Icon-Stil durchgezogen - [ ] Alle neuen v1.x-Module folgen dem Redesign von Anfang an (kein Nacharbeiten nötig) +- [x] Weitergeführt in T5 „Optik mit eigener Handschrift“ (Abschnitt T5 unten): Tokens in vier Farbsätzen, Baukasten mit Wächter, Wabenzelle als einziges Motiv; die Verläufe dieses Abschnitts sind entfernt ### 22. QR-Code-System @@ -320,6 +321,33 @@ Plan: `docs/superpowers/plans/2026-10-13-t4-logik-ketten.md`, Annahmen 31–58 i - [ ] Folgepunkt: `packagingSize` im Backend als `@IsInt @Min(1) @Max(10000)` prüfen, nach Datenprüfung in Produktion (braucht Freigabe) - [ ] Ausrollen in den Cluster – gebaut und getestet, noch nicht ausgerollt +## T5. Optik mit eigener Handschrift + +Spec: `docs/superpowers/specs/2026-10-13-t5-optik-design.md`, Plan: `docs/superpowers/plans/2026-10-14-t5-optik.md`, Issue #26. Annahmen 119–124 in ARCHITECTURE.md, Beschreibung in Abschnitt 15. + +- [x] T5a (Tasks A1–A12): vier Farbsätze mit Kontrast-Wächter, Darstellung ohne Flackern (hell, dunkel, Sonne), `AREAS`/`AreaIcon`/`BrandMark`, Fokus und Größen, Auswahlgruppe, Checkbox, Tabs, Plus/Minus, Manifest und Icons, Bewegungs-Sprache mit Budget, Beute-Bühne in der Sonne, Baukasten-Wächter, Hinweis „Sonnenmodus“ +- [x] Browserprüfung T5a: Lighthouse Barrierefreiheit Login 100, Start 100, Durchsicht neu 98; axe-Kontrast 0 Befunde in 168 Läufen; Forced Colors in Ordnung (2026-10-19) +- [x] T5a produktiv (Frontend sha-d8efbc3, 2026-10-19) +- [x] T5b B1 Waben-Standkarte (Stand-Seite, Offline-Standseite, Bienenstände-Liste) +- [x] T5b B2 Mini-Waben-Marker auf der Karte mit Liste „Stände auf der Karte“ +- [x] T5b B3/B4 Lebenslinie auf der Volksseite (Modell, SVG, Tabelle) +- [x] T5b B5 Backend-Zusatz an `GET /autopilot/today` (`segmentSince`, `reportedOn`, `note`, `seasonCalendar`; additiv, keine Migration); produktiv mit Backend sha-1688e77 +- [x] T5b B6/B7 Bienenjahr-Rad auf dem Dashboard, ersetzt die Kacheln „Überblick“ und die Chip-Zeile „Saison je Stand“ +- [x] B8 Doku: ARCHITECTURE Abschnitt 2, 11.5, 11.7 und 15, Annahmen 119–124; Abweichungen in der Spec vermerkt +- [ ] B8 Sichtprüfung T5b: Screenshot-Matrix (Spec 13.4), Lighthouse Barrierefreiheit ≥ 95 in hell und Sonne, `impeccable detect --json` mit 0 Befunden +- [ ] Ausrollen T5b-Frontend nach der Sichtprüfung (Produktion bleibt bis dahin auf sha-d8efbc3) +- [ ] Inhaber: Draußen-Test in der ersten Woche (Spec 13.4 und 17.7 Aufgabe 6: Hauptknopf, kritisches und lange nicht durchgesehenes Volk auf 50 cm, Brutwaben 0 auf 6, Sonnenmodus, Abschnitt im Rad, Abhaken mit Handschuh). Ein Fehlschlag ändert Tokens oder Größen, zusammen mit `contrast.spec.ts` +- [ ] Inhaber: Spec §16 Fragen 1–4 bestätigen (umgesetzt mit A, Annahme 121) +- [ ] Changelog-Hinweis: iOS zeigt das neue Homescreen-Icon erst nach Entfernen und neu Hinzufügen der App +- [ ] `ApiCore-Frontend/DESIGN.md` per `/impeccable document` aus dem fertigen Stand erzeugen, damit neue Seiten den Baukasten finden +- [ ] Folgepunkt: Durchsicht neu hat eine versteckte `h3` (`sr-only`), die die Reihenfolge der Überschriften bricht (Lighthouse `heading-order`) +- [ ] Folgepunkt: Reihenfolge von Toast und Fokus nach „Ausprobieren“ im Sonnen-Hinweis ordnen +- [ ] Folgepunkt: Leaflet animiert das Verschieben per Pfeiltaste und zum Popup auch bei „Dezent“ und „Aus“ (Leaflet 1.9 hat dafür keine Option) +- [ ] Folgepunkt: Backend-e2e „an apiary-wide sync and single-colony syncs running together finish without a deadlock“ (`test/autopilot.e2e-spec.ts`) läuft unter CI-Last ins Zeitlimit +- [ ] Folgepunkt: `/apiaries` lädt die Übersicht mit einer Anfrage je Karte (höchstens 20, Annahme 123); ein Sammel-Endpunkt für mehrere Stände würde das ersetzen +- [ ] Folgepunkt (Inhaber): Ein von Hand gesetzter Endstatus eines Volks (`SOLD`, `DECEASED`) ändert die Königin nicht automatisch, das Aufräumen fragt nach; soll auch dieser Weg den Endstatus der Königin selbst setzen? (ARCHITECTURE 11.7) +- [ ] Nicht geprüft in der Sichtprüfung T5a: Glocke mit „9+“ bei 390 px, Herkunftsseite in allen Modi, privates Fenster (gesperrter Speicher) + ## T6. Compliance (Tierarzneimittel, Etikett, Behörden, Prüfbereitschaft) Plan: `docs/superpowers/plans/2026-10-14-t6-compliance.md`, Annahmen ab 59 in ARCHITECTURE.md (zuletzt 94), Beschreibung in Abschnitt 12. @@ -347,7 +375,7 @@ Spec: `docs/superpowers/specs/2026-10-15-beute-3d-design.md`. Teil 1 (Daten) und - [x] Task 22 Doku (ARCHITECTURE §13/13.1, §2 offline, Annahmen 101–105) und Vereinfachung des Frontend-Codes - [x] Größenbudget 3D-Chunk (320 kB gzip) im Frontend-Image-Build geprüft (299 kB am 2026-10-16) - [ ] Inhaber: Maße prüfen (Zarge außen, Styropor-Einlage, Verdunster) und Fotos der Beuten schicken (Spec §14) -- [ ] T5-Spec um eine Bewegungs-Sprache ergänzen, bevor T5 umgesetzt wird (Spec §12.4) +- [x] T5-Spec um eine Bewegungs-Sprache ergänzen, bevor T5 umgesetzt wird (Spec §12.4; T5-Spec §17, umgesetzt in T5a A11) - [ ] Teil 4 Sprach-Durchsicht: Anforderungen in `docs/superpowers/specs/2026-10-16-beute-teil4-sprach-durchsicht-anforderungen.md`; Brainstorming + Spec nach L5, baut auf T7 auf - [x] T7 L1 Diktat (Spracherkennung im Browser, Einwilligung, Diktat-Knopf, Seite „Sprache & Datenschutz“); Plan `docs/superpowers/plans/2026-10-16-t7-sprache.md` - [x] T7 L2 Backend (Tasks 4–12): Datenmodell, Schlüssel `voice`, Kern, API, Übernahme in die Durchsicht, Ereignisse, Wiederholregel, Fachwortliste diff --git a/docs/superpowers/specs/2026-10-13-t5-optik-design.md b/docs/superpowers/specs/2026-10-13-t5-optik-design.md index 6b0815f..053d2d0 100644 --- a/docs/superpowers/specs/2026-10-13-t5-optik-design.md +++ b/docs/superpowers/specs/2026-10-13-t5-optik-design.md @@ -1,6 +1,6 @@ # T5 Optik mit eigener Handschrift — Design -Stand: 2026-10-14, Nachtrag 2026-10-16 (§17 Bewegungs-Sprache, Frage 4 in §16; Anlass Beute-Spec §12.4, Issue #26) · Betrifft: ApiCore-Frontend (plus ein kleiner Zusatz am Backend-Endpunkt `GET /autopilot/today`) · Teilprojekt T5 aus `docs/superpowers/specs/2026-10-12-plattform-rueckgrat-und-fahrplan-design.md` §10 +Stand: 2026-10-14, Nachtrag 2026-10-16 (§17 Bewegungs-Sprache, Frage 4 in §16; Anlass Beute-Spec §12.4, Issue #26), Nachtrag 2026-10-19 (Abweichungen bei der Umsetzung, je an der Stelle als „Abweichung bei der Umsetzung 2026-10-19“ vermerkt) · Betrifft: ApiCore-Frontend (plus ein kleiner Zusatz am Backend-Endpunkt `GET /autopilot/today`) · Teilprojekt T5 aus `docs/superpowers/specs/2026-10-12-plattform-rueckgrat-und-fahrplan-design.md` §10 ## 1. Ziel @@ -116,7 +116,7 @@ Gemessen mit derselben OKLCH-Rechnung wie `src/lib/forms/rating-contrast.spec.ts | T3 Task 11 Varroa-Trend (SVG) | Muster für handgeschriebene Diagramme | Vorlage für Lebenslinie und Rad. | | T4 Task 15 `ColonyOverviewRow.inspectionDueOn`, `ColonyNextSteps`, `AutopilotToday` | Fälligkeit, nächste Schritte, Saison je Stand | Randstufen der Zelle, Zukunftszone der Lebenslinie, Rad. `AutopilotToday` bekommt zwei Felder (9.1). | | T4 Task 18 `SeasonChip`, `SEGMENT_LABELS`, Heute-Karten | Saison-Chips auf dem Dashboard | Die Saison je Stand zieht in die Rad-Karte. Die Chip-Zeile entfällt dort, um Doppelanzeige zu vermeiden. `SeasonChip` bleibt auf der Stand-Seite. | -| T4 Task 17 `INTENT_ICONS` | Icon je Absicht | Werte aus `AREAS` (Durchsicht, Varroa, Fütterung …). | +| T4 Task 17 `INTENT_ICONS` | Icon je Absicht | Werte aus `AREAS` (Durchsicht, Varroa, Fütterung …). **Abweichung bei der Umsetzung 2026-10-19:** `INTENT_ICONS` bleibt eine eigene Tabelle (`lib/suggestions/labels.ts`). Grund: Eine Absicht ist kein Bereich; viele Absichten (Gesundheit, Königin prüfen, Aufräumen) haben keinen Bereich in `AREAS`. | | T4 Task 19 Varroa-Bänder, `rating` je Messung | Ampel je Messung | Lebenslinie färbt mit `rating.light` und markiert `band.redAbove`. | | T4 Task 31 `PHASE_SOURCE_LABELS` | „gemeldet“, „geschätzt“, „Kalender“ | Rad zeigt die Quelle damit. | @@ -351,6 +351,7 @@ Alles wird je Gerät gespeichert, nicht am Konto. Draußen am Handy will man Son - Je Zeile: `code`, `status`, Bedingungen, `lastInspectionDate`, `openWarnings`, `openCritical`. Ab T4 zusätzlich `inspectionDueOn`, `segment`, `nextStepTitle`. - Zustand der Zelle = `colonyStatusLight(row, { today, inspectionsVisible })`, unverändert aus T3/T4. - Auf der Bienenstände-Liste lädt `useQueries` die Übersicht aller aktiven Stände, höchstens 20. Bei mehr Ständen zeigen die übrigen Karten nur die Zusammenfassung, ohne Wabe; das ist im Betrieb nicht zu erwarten. +- **Abweichung bei der Umsetzung 2026-10-19:** Jede Karte der Liste lädt ihre Übersicht selbst (`useColonyOverview`, derselbe Schlüssel und Paket-Rückfall), nicht ein `useQueries` über alle Stände. Karten ab dem 21. Stand laden nichts und zeigen nur Name und Ort, auch keine Zusammenfassung (`MAX_COMB_CARDS`). Grund: Die Zusammenfassung braucht dieselbe Übersicht wie die Wabe; ohne Anfrage gibt es keine Zahlen. Ein Sammel-Endpunkt ist Folgepunkt in TASKS.md (ARCHITECTURE Annahme 123). ### 6.2 Aufbau @@ -393,6 +394,7 @@ Bienenstände-Liste (`/apiaries`, Tab „Aktiv“): Jeder aktive Stand ist eine - Die Liste behält ihren Hauptknopf „+ Neuer Bienenstand“. Die Karten haben keine eigenen Knöpfe. Der Name führt zum Stand, die Zellen führen zu den Völkern. - Der Tab „Historie“ bleibt eine `EntityCard`-Liste ohne Wabe. - Die Legende steht einmal oben über allen Karten. +- **Abweichung bei der Umsetzung 2026-10-19:** Der `SeasonChip` der Karte zeigt nur den Abschnitt aus `segment` der Übersicht, ohne das Intervall („alle 7 Tage“). Grund: Die Übersicht trägt kein Intervall; es bräuchte eine zweite Anfrage je Karte. ### 6.3 Zelle @@ -418,6 +420,7 @@ Bienenstände-Liste (`/apiaries`, Tab „Aktiv“): Jeder aktive Stand ist eine - Ohne `inspections:read` (`inspectionsVisible = false`) gibt es einen 1-px-Rand in `--input` und keine Aussage zur Durchsicht. - Die 3-Tage-Grenze ist dieselbe wie „SOON“ in der T4-Rangfolge. - Die Funktion `rimStepOf(row, today, inspectionsVisible)` liegt in `src/lib/honeycomb/cell-state.ts`. + - **Abweichung bei der Umsetzung 2026-10-19:** Der Rand ist in der Tinte der Füllung gezeichnet (`--condition-*-foreground`), nicht in `--foreground`. Grund: Im Dunkelmodus ist `--foreground` hell und hat auf den hellen Füllungen unter 2:1; die Randstärke wäre nicht zu lesen. Das Paar Füllung und Tinte hält `contrast.spec.ts` in allen vier Sätzen bei mindestens 4,5:1. Die Breiten in der Sonne kommen aus `--comb-stroke-1` bis `-3`. - **Zustand „Winterruhe“:** Ab T4 heißt die Rand-Erklärung im Namen „Winterruhe – keine Durchsicht nötig“. ### 6.4 Bedienung @@ -472,6 +475,7 @@ Keine Animation. Ändert sich der Zustand nach einem Sync, wechselt die Füllung - Knopf „Stand öffnen“ (44 px, `size="touch"`, Umriss) - Keine Bedienung einzelner Zellen auf der Karte. Sie wären für Handschuhe zu klein. - Popup im Token-Stil: `.leaflet-popup-content-wrapper`, `.leaflet-popup-tip` mit `--popover`/`--popover-foreground` und Tinten-Rand. Den Schließknopf `.leaflet-popup-close-button` vergrößert ein Pseudo-Element auf 44 px. +- **Abweichung bei der Umsetzung 2026-10-19:** Zoom-, Ausblend- und Marker-Animationen sowie das Nachgleiten (`inertia`) laufen nur bei „Voll“ (§17.2). Das Verschieben per Pfeiltaste und das automatische Verschieben zum geöffneten Popup animiert Leaflet aber in jeder Stufe. Grund: Leaflet 1.9 hat dafür keine Option; ein Eingriff in Leaflet-Interna lohnt für diese kurze Bewegung nicht. Folgepunkt in TASKS.md. ### 7.3 Barrierefreiheit und Rückfall @@ -511,6 +515,12 @@ Ein SVG-String je Stand, gemerkt nach den Ampel-Zählern. Keine neue Abhängigke - Die Konstante `STRENGTH_IN_FRAMES_SINCE` (`lifeline.ts`) ist das Rollout-Datum von T3 Task 9. Es wird beim Bau aus dem Deploy-Protokoll eingetragen. - Ältere Werte erscheinen nicht in der Kurve, nur in der Tabelle mit der Einheit „Skala (alt)“. - So vermischt die Kurve keine Einheiten. + - **Abweichung bei der Umsetzung 2026-10-19:** `STRENGTH_IN_FRAMES_SINCE` ist `2026-10-17`, der erste volle Tag nach dem Rollout des Frontends mit dem Waben-Stepper (sha-cda3e02, 16.10.2026 abends), nicht der Tag des Rollouts selbst. Ob ein Wert davor liegt, entscheidet `createdAt` der Durchsicht, ersatzweise ihr Datum. Grund: Der Rollout war am 16.10. um 22:28; ein Wert dieses Tages stammt fast sicher noch aus der alten Skala. In Produktion liegt zwischen 10.10. und 19.10. keine Durchsicht, also kein Wert auf der Grenze (ARCHITECTURE Annahme 122). +- **Abweichung bei der Umsetzung 2026-10-19 (Tabelle oben):** + - Der Varroa-Key heißt im Bestand `["colonies", id, "varroa-measurements"]`; die Lebenslinie nutzt ihn, damit keine Doppel-Anfrage entsteht. + - Die Zeile „Ampel ohne T4“ entfällt: den Key `["varroa", "threshold"]` gibt es nicht mehr, jede Messung trägt seit T4 `rating`. `buildLifeline` nimmt für alte Daten optional ein Band, der Hook lädt keins. + - Ein Balken unter Behandlungswirkung bekommt keine Ampel, sondern das Wort „Behandlungseffekt“. Grund: Eine erfundene Ampel wäre falsch. + - Königin-Amtszeiten gibt es nur für die geladenen Königinnen (die letzten 5 nach Beginn). Eine unbekannte oder gelöschte Königin erscheint nur als Ereignis „Königinwechsel“. - **Rechte:** Fehlt ein Leserecht (403), fällt die Spur weg, ohne Fehlermeldung. Das gilt für `inspections:read` (Stärke), `varroa:read` (Milben), `treatments:read` (Behandlungen) und `queens:read` (Königin). Die Kopfzeile nennt nur vorhandene Spuren. ### 8.2 Aufbau @@ -554,10 +564,17 @@ Spalten (Handy / Desktop): - Ab T4 sind das `next-steps`, vorher offene Aufgaben. - Der Hinweis `withdrawalUntil` (T4) erscheint als Satz unter der Grafik: „Wartezeit bis 20.08.“ - **Wertbeschriftung:** Die Stärke-Kurve beschriftet nur den letzten Wert, den höchsten und den niedrigsten Wert im Ausschnitt sowie Punkte, die mindestens 24 px Abstand haben. So überlappen keine Zahlen. +- **Abweichung bei der Umsetzung 2026-10-19:** + - Die Zukunftszone ist in Zeilen geteilt (22 px je Schritt, höchstens 3), nicht im Zeitmaß. Grund: 21 Tage × 1,6 px sind 34 px, zu wenig für drei Zeilen mit Titel und Datum. + - Die Milben-Skala reicht bis zum Doppelten der höchsten Grenze im Ausschnitt (`threshold` oder `band.redAbove`), eine Skala für alle Balken. Grund: Ab T4 wechselt die Grenze mit der Jahreszeit; mit einer Skala je Balken wären die Balken nicht vergleichbar. + - Ereignis-Icons, die näher als 20 px beieinander liegen, werden ein Icon mit Zähler. Es zeigt die stärkste Art, sein Name nennt alle Ereignisse („05.09.: Behandlung; 01.09.: Fütterung und 1 weitere“). Grund: Icons verschiedener Tage lagen sonst übereinander. + - Alle Beschriftungen (Monate, „heute“, Werte, Titel der Zukunft) sind HTML in 13 px über dem SVG, kein SVG-Text. Grund: SVG-Text skaliert mit der Breite und fiele auf dem Handy unter 13 px. + - Ohne Spur „Milben“ entfällt die Milbenspalte; die Schraffur der Behandlungen liegt dann in der Stärke-Spalte. ### 8.3 Platzierung und Bedienung - **Volksseite:** eigene Karte „Lebenslinie“ direkt unter der T4-Karte „Als Nächstes“, auf dem Desktop in der rechten Spalte oben. Der Hauptknopf der Seite bleibt „Durchsicht erfassen“. + - **Abweichung bei der Umsetzung 2026-10-19:** Die Karte steht in Zeile 2 als erstes Element der breiten Fläche, über den Karten zu Durchsichten, Varroa usw. Reihenfolge im DOM: Stammdaten, Beute, Aktionen, Lebenslinie, Karten. Grund: Seit Beute L2 belegt die Beute die rechte Spalte oben, und Zeile 1 bleibt Stammdaten und Beute (Plan-Review 2026-10-16, Task B4). - **Bedienung:** nur zwei Knöpfe, „Ganzes Leben zeigen“ und „Als Tabelle“ (`aria-expanded`). Punkte und Balken sind nicht antippbar; sie wären zu klein für Handschuhe. Details stehen in der Tabelle und in den bestehenden Karten (Durchsichten, Varroa, Behandlungen). - **Leer:** Ohne Durchsicht und Messung steht dort „Noch keine Durchsicht. Die Lebenslinie wächst mit jeder Durchsicht.“ und der Knopf „Durchsicht erfassen“ als Umriss. Königin und Ereignisse erscheinen trotzdem, falls vorhanden. - **Laden:** ein Skelett-Block in voller Höhe des Ausschnitts, kein Spinner. @@ -616,6 +633,7 @@ Spalten (Handy / Desktop): - Das Rad zeigt den Abschnitt, in dem die meisten aktiven Stände sind. - Bei Gleichstand gilt der weiter fortgeschrittene Abschnitt. Lieber eine Durchsicht zu früh als einen Schwarm verpasst (T4 7.3). - Abweichende Stände nennt die dritte Zeile im Zentrum: „Südhang: noch Frühjahr“. Bei mehr als einem Stand steht dort „2 Stände weichen ab – siehe Tabelle“. + - **Abweichung bei der Umsetzung 2026-10-19:** Abweichende Stände und der Wetter-Hinweis (`note`) stehen in einer eigenen Zeile unter dem Rad und in der Tabelle, nicht im Zentrum. Grund: Der freie Kreis im Zentrum (r < 62) fasst bei 390 px und in der Sonne nur drei kurze, einzeilige Zeilen (siehe 9.2). - **Aufgaben:** offene Aufgaben mit Termin aus der bestehenden Dashboard-Query `["tasks", "dashboard"]`. - Je Kalenderwoche ein Punkt. - Überfällige Aufgaben bilden einen eigenen Punkt kurz vor dem Zeiger. @@ -685,6 +703,8 @@ Feste Größe 320 × 320 px. Unter 328 px Containerbreite skaliert das Rad mit, 1. Abschnitt (22 px, Space Grotesk halbfett) 2. „seit {Datum} · {Quelle}“ (14 px; Quelle aus `PHASE_SOURCE_LABELS`: „gemeldet am 18.04.“, „geschätzt“, „Kalender“; bei `note` zusätzlich „Wetterdaten lückenhaft“ bzw. „ohne Koordinaten“) 3. das laufende Arbeitsfenster mit Ende, oder die Abweichung eines Stands + + **Abweichung bei der Umsetzung 2026-10-19:** Das Zentrum hat höchstens drei einzeilige Zeilen: der Abschnitt (13 bis 22 px, wächst mit der Breite des Rads), „seit {Datum}“ und das laufende Fenster (breites Rad: Name, z. B. „Einwinterung“; schmales Rad: „bis 15.11.“). Die Quelle steht im Satz „Jetzt … (Kalender) · Danach …“, Wetter-Hinweis und Abweichungen in der Zeile unter dem Rad. Container-Queries wählen die Form, ohne Messen und ohne Layout-Sprung. Grund: Mit 22 px und umbrechenden 14-px-Zeilen lief der Text bei schmalen Breiten und in der Sonne aus dem Kreis. - **Zeiger:** Linie in `--foreground` mit der Wabenzelle als Kappe. Er steht auf heute und bewegt sich nie animiert. - **Darunter:** die Legende (eine Zeile, 13 px) und der Satz „Jetzt … · Danach …“. - **Karte:** Überschrift „Bienenjahr“ (h2). Rechts steht der Textlink „Zum Kalender“ (44 px Trefferfläche) als einzige Aktion; „Als Tabelle“ ist eine Aufklapp-Steuerung. @@ -711,6 +731,8 @@ Reihenfolge von oben nach unten: - Fenster | von | bis Dazu kommt die Zeile „Aufgaben je Woche“ für die nächsten 4 Wochen. + + **Abweichung bei der Umsetzung 2026-10-19:** „Aufgaben je Woche“ ist eine dritte Tabelle mit Beschriftung (4 Wochen ab dem Montag dieser Woche, dazu „überfällig“), keine lose Zeile. Grund: dieselbe Form wie die beiden anderen Tabellen, mit eigener Beschriftung für Screenreader. - Keine interaktiven Teile im SVG. Die Punkte wären zu klein (eine Woche entspricht etwa 15 px Bogen). ### 9.5 Leistung und Bewegung @@ -760,6 +782,9 @@ Der Wächter-Test `src/styles/baukasten-guard.spec.ts` prüft die Regeln aus 10. - `"tile"`: umrandete Kachel wie `ToggleChip`; gewählt mit Honig-Tönung und 2-px-Tinten-Rand; Sonne: `--card` mit Tinten-Rand - `"task"`: die Abhak-Zelle der Heute-Liste; die `honey-check`-Animation bleibt als einzige „Moment“-Bewegung, jetzt auf dem Base-UI-Primitiv statt eines eigenen `role="checkbox"` - `ToggleChip` wird nach der Umstellung gelöscht. +- **Abweichung bei der Umsetzung 2026-10-19:** + - Enter auf einer Checkbox schaltet nicht und sendet kein Formular ab; nur die Leertaste schaltet (Muster der WAI-ARIA APG). Das gilt auch für die Abhak-Zelle der Heute-Liste. Grund: Base UI schickte mit Enter das umgebende Formular ab (Ruling T5-3). + - `SwitchRow` geht ebenfalls in `CheckboxField` auf (Prop `role="switch"`, neu `ui/switch.tsx`). Es sind 35 Stellen in 27 Dateien, nicht 17. Grund: Seit der Spec kamen Stellen aus T6, T7 und Beute dazu. ### 10.4 Tabs @@ -770,6 +795,11 @@ Der Wächter-Test `src/styles/baukasten-guard.spec.ts` prüft die Regeln aus 10. - Base UI liefert `role="tab"`/`tabpanel`, `aria-controls` und Pfeiltasten. - Die Variante `line` entfällt; die Statistik nutzt `segment`. - `ActiveHistoryTabs` wird eine Vorlage auf `Tabs` (Props `value`, `onValueChange`, `activeCount`, `historyCount`, `children` je Panel). Die 9 Listen legen ihre Liste in das Panel. `TabSwitcher` wird gelöscht; die beiden Einstellungsseiten nutzen `Tabs` direkt. +- **Abweichung bei der Umsetzung 2026-10-19:** + - Pfeiltasten bewegen nur den Fokus; Enter oder Leertaste wählt den Reiter (manuelle Aktivierung). Nur `ActiveHistoryTabs` wählt schon beim Fokus. Grund: Statistik und Einstellungen laden je Reiter Daten, jeder Pfeil hätte eine Anfrage ausgelöst; `ActiveHistoryTabs` filtert nur schon geladene Daten. + - `ActiveHistoryTabs` hat zusätzlich die Props `toolbar`, `toolbarAlign` und `heading`. Grund: Fünf Listen haben Filter oder einen Anlegen-Knopf neben den Reitern; ohne die Props lägen sie unter den Panels. + - Unter 640 px zeigen `/settings/automation` und die Statistik eine Auswahlliste statt der Leiste (`TabPicker`). Grund: Sieben Reiter brachen bei 390 px in fünf Reihen um. + - Die Zahl im Reiter („Aktiv (3)“) fehlt, solange die Liste lädt oder fehlschlägt. Grund: „(0)“ wäre in diesem Zustand falsch. ### 10.5 Plus/Minus @@ -787,6 +817,7 @@ Der Wächter-Test `src/styles/baukasten-guard.spec.ts` prüft die Regeln aus 10. - MFA-Codes - Spalten und Zeilen im Etiketten-Designer - Werte in Aufgabenvorlagen und Regeln +- **Abweichung bei der Umsetzung 2026-10-19:** In `varroa/new` nutzt die Milbenzahl für ein Volk den `NumberStepper`. Die Sammelerfassung (`BulkMiteCounts`) bleibt ein Eingabefeld. Grund: Dort stehen viele Zeilen untereinander, und die Zahlen werden meist am Schreibtisch nach dem Auszählen getippt; ein Stepper je Zeile verdoppelte die Liste und bremste das Springen mit Tab (Ruling T5-2). Die Entnahme in `stock-card` bleibt bei Rohstoffen ein Eingabefeld (Gramm). ### 10.6 Fokus für eigene Bedienelemente @@ -915,6 +946,7 @@ export const AREAS: Readonly>; - Eine Seite, die einen anderen Bereich nur erwähnt (z. B. die Schleuderung in der Honigcharge), darf dessen Icon tragen. Ein Icon bedeutet immer seinen Bereich. - Die öffentliche Herkunftsseite behält ihre eigenen Icons. +- **Abweichung bei der Umsetzung 2026-10-19:** `AREAS` hat 37 Schlüssel statt 32. Neu sind `compliance` (`ShieldCheck`, betrieb), `medicines` (`Pill`, erfassung), `voice` (`Mic`, erfassung), `hive-systems` (`Box`, neutral) und `system` (`Activity`, neutral). Grund: T6, T7 und Beute brachten Einträge in Navigation und „+ Neu“, die ihr Icon aus `AREAS` lesen (Plan-Review 2026-10-16). ## 12. Manifest und Icons @@ -1147,6 +1179,7 @@ Der Fahrplan nannte etwa 5 PT. Mehr wird es durch vier Punkte, die §10 nicht ei - ARCHITECTURE §2: `AREAS` als einzige Quelle für Icon und Ton, Wabenzelle als einziges Motiv. - TASKS.md: Abschnitt T5 mit Haken; Abschnitt R „Redesign“ verweist auf T5. - Empfohlen: `ApiCore-Frontend/DESIGN.md` per `/impeccable document` aus dem fertigen Stand erzeugen, damit spätere Seiten den Baukasten finden. + - Stand 2026-10-19: Die nächste freie Nummer war 119. ARCHITECTURE hat die Annahmen 119 („Darstellung je Gerät“), 120 („Ein Baukasten“) und 121–124 (offene Fragen vorläufig A, Stichtag der Stärke-Kurve, 20 Karten in der Liste, Bezugsstand des Rads), den Absatz in §2 und den neuen Abschnitt 15. TASKS.md hat den Abschnitt T5, und Abschnitt R verweist darauf. `DESIGN.md` und der Changelog-Hinweis stehen als offene Punkte in TASKS.md. ## 16. Offene Fragen an den Inhaber @@ -1244,6 +1277,11 @@ Die Werte kommen aus dem, was die Beute schon ausliefert, und aus den bestehende | **Wabenmotiv** | Die Wabenzelle bewegt sich nur an zwei Stellen: beim Abhaken (`.honey-check`) und auf der Beute-Bühne. Waben-Standkarte, Mini-Waben, Rad, Lebenslinie, `BrandMark` und `AreaIcon` stehen still (6.8, 8.5, 9.5 bleiben). Ein Ladezeichen in Wabenform gibt es nicht. | – | – | | **Toast** | Sonner-Standard (gleitet), Dauer und Kurve auf die Tokens | ohne Gleiten, nur Deckkraft | CSS-Überschreibung an `[data-sonner-toaster]`, falls Sonner das nicht selbst tut | +**Abweichung bei der Umsetzung 2026-10-19:** +- **Listenzeile hinzu:** Eine neue Zeile erscheint sofort, ohne Eintritt. Grund: 17.5 „Daten stehen sofort da“ wiegt schwerer; eine einblendende Zeile wäre ein Datenwert in Bewegung. +- **Toast:** Bei „Voll“ behält Sonner sein eigenes Gleiten (400 ms, unter der Grenze von 420 ms) und wird nicht auf die Tokens umgestellt. Bei „Dezent“ blendet der Toast nur über. +- **Listenzeile weg:** Bei „Dezent“ blendet die Zeile im Fluss aus (`AnimatePresence` im Modus `sync`), die übrigen stehen danach sofort am Ziel. Eine Liste, die erscheint, bevor das `motion`-Modul geladen ist, bleibt bis zum nächsten Einhängen ohne Bewegung. + ### 17.5 Was nie animiert - **Datenwerte, die der Imker liest:** @@ -1286,6 +1324,7 @@ Die Werte kommen aus dem, was die Beute schon ausliefert, und aus den bestehende - Geladen wird nur im `(app)`-Layout, nicht auf Login und Herkunft. - Budget: höchstens 30 kB gzip, im Build gemessen wie das 3D-Budget. - Die three.js-Szene nutzt `motion` nicht (eigene Federn in `use-animated-node.ts`). + - **Abweichung bei der Umsetzung 2026-10-19:** Statt `domMax` lädt `motion` nur `domAnimation`; das Nachrücken der Zeilen ist ein eigenes FLIP (eine Messung zu Beginn, dann WAAPI-`transform` auf der `linear()`-Feder). Der ganze `motion`-Code liegt in einem nachgeladenen Modul (`components/motion/motion-runtime.tsx`), das in keinem ersten Laden steckt; `AppMotion` lädt es nur vor. Gemessen: 28,1 kB gzip für alle `motion`-Chunks zusammen (`npm run check:motion-budget`, im Image-Build). Grund: `domMax` allein hatte etwa 33 kB, und der erste Bau lag mit statischem Kern bei 47,5 kB, über dem Budget. ### 17.7 Abnahme