From e9c16325cc02ce51246aaae8fba6b366a1ffe3c9 Mon Sep 17 00:00:00 2001 From: Tobi G Date: Sat, 5 Sep 2026 02:24:08 +0200 Subject: [PATCH] Musterseite verworfen, Zahnrad-Regression behoben, Fotomenue als Aktionsblatt Musterseite entfernt (companion-app/muster, vite.muster.config.ts, /config/www/dmmuster). Ansage des Eigentuemers: sie fuehrt nur zu neuen Fehlerquellen. Ausloeser war ein Befund, den ich als App-Fehler gemeldet hatte - Ringe und Typenschild schwarz auf schwarz - und der nur dort bestand. Behoben: * Das Zahnrad im Autohaus-Kasten hatte seine Ecke verlassen (58/288 statt 8/8). Ursache war mein HIG-Durchgang von gestern: .zahnrad kam in die Gruppe fuer die 44x44-Trefferflaeche und bekam damit position:relative, was das absolute aus dem Basisblatt ueberschrieb. Die Auflage war ueberfluessig - .zahnrad ist dort schon 44x44. * Das Fotomenue hing mit position:absolute an der Kachel statt am Bildschirm und lag bei der hohen Raeder-Kachel ausserhalb des Sichtfelds; einen Abbrechen-Knopf hatte es nie. Das Panel benutzt jetzt dasselbe Aktionsblatt wie die App. .bildmenu ist samt Zustand, Markup, totem Handler und CSS entfernt. * Der Blattkopf las sich als dritte Auswahl (linksbuendig, 15px, --fg). Jetzt zentriert, 13px, gedaempft - Apples Muster, in beiden Codebasen. * "Anstehende Termine" stand 5px ueber "Oelwechsel" und wirkte als dessen Beischrift. Entfernt. * Wartungsplan-Formular: Art nach oben, Datum 124px statt Browser-Vorgabe, Werkstatt ueber die volle Zeile (218 -> 306px), Kosten 116px. Zwei Erklaertexte entfernt. * Thema folgt jetzt einer Systemumstellung im laufenden Betrieb. Vorher las es prefers-color-scheme genau einmal beim Laden. Drei Tests, die ohne die Behebung nachweislich fallen. Geprueft: 295 App-Tests, Panel node --check, 0 Tracebacks, alle Aenderungen live an der Testinstanz gemessen. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 147 + companion-app/.gitignore | 2 - companion-app/muster/index.html | 12 - companion-app/muster/muster.tsx | 79 - companion-app/src/screens/Service.tsx | 40 +- companion-app/src/stile/screens.css | 4500 +++++++++-------- companion-app/src/theme.test.ts | 57 + companion-app/src/theme.ts | 35 + companion-app/vite.muster.config.ts | 17 - .../audi_dashboard/frontend/app/bundle.json | 2 +- .../audi_dashboard/frontend/app/bundle.zip | Bin 324399 -> 324448 bytes .../frontend/audi-dashboard-app.js | 147 +- .../frontend/audi-dashboard-ios.css | 23 +- .../frontend/audi-dashboard.css | 59 +- .../audi_dashboard/manifest.json | 2 +- design-system/src/components/Sheet/Sheet.css | 19 +- 16 files changed, 2674 insertions(+), 2467 deletions(-) delete mode 100644 companion-app/muster/index.html delete mode 100644 companion-app/muster/muster.tsx delete mode 100644 companion-app/vite.muster.config.ts diff --git a/AGENTS.md b/AGENTS.md index 78d8fbc..0d73752 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9952,6 +9952,17 @@ strukturell, nicht optisch belegt. ## CG. Eine Musterseite loest den wiederkehrenden Pruefblocker (2026.9.4.12) +> **Zurueckgenommen am 05.09.2026.** Die Musterseite ist entfernt - +> `companion-app/muster/`, `vite.muster.config.ts`, `dist-muster/` und +> `/config/www/dmmuster` im Testcontainer. Ansage des Eigentuemers: *"der +> fehler ist nur in deiner musterseite. Verwerfe diese - die fuehrt nur zu +> neuen Fehlerquellen"*. Ausloeser war ein Befund, den ich als App-Fehler +> gemeldet habe (Vier Ringe und Typenschild schwarz auf schwarz im +> Nachtmodus) und der nur auf der Musterseite bestand - genau die Klasse +> Fehlalarm, die eine zweite Bauweise derselben Oberflaeche erzeugt. Fuer +> Live-Pruefungen gilt jetzt allein der Weg ueber den Zugangstoken +> (Abschnitt CP). Der folgende Text bleibt als Aufzeichnung stehen. + **Das Problem, das seit Wochen wiederkehrt:** die App zeigt ohne gespeicherten Zugang gar nichts, jede OPTISCHE Pruefung haengt damit an einem Zugangstoken - und fast jeder Fehler, den dieses Projekt findet, ist nur am gerenderten Bild @@ -11451,3 +11462,139 @@ sie war es nicht: Beides hat dazu gefuehrt, dass ich zwischenzeitlich alte Dateien gelesen und fuer neue gehalten habe. Gegenprobe, die das aufdeckt: `find -name manifest.json | wc -l` muss `1` ergeben. + +## CS. Fuenf Meldungen aus einer Sitzung - und ein Fehlalarm, den ich selbst gebaut hatte (2026.9.5.10) + +### Der Fehlalarm zuerst: die Musterseite ist weg + +Gemeldet als App-Fehler: "RS4 Logo ist im Nachtmodus schwarz und nicht mehr +lesbar, Audi Ringe rechts oben sind schwarz auf schwarz". Ich habe daraufhin +eine Ursache benannt, die keine war - `fill: rgb(0,0,0)` aus meiner Messung ist +die geerbte CSS-Eigenschaft des ``-Elements und sagt ueber das gezeichnete +SVG nichts. Die Gegenprobe im Dunkelmodus zeigte weisse Ringe. + +Der Befund stimmte, aber nur auf der **Musterseite**. Ansage des Eigentuemers: +*"der fehler ist nur in deiner musterseite. Verwerfe diese - die fuehrt nur zu +neuen Fehlerquellen"*. Sie ist entfernt (Abschnitt CG traegt jetzt den +Ruecknahme-Vermerk). Eine zweite Bauweise derselben Oberflaeche erzeugt +Fehlalarme - genau das ist hier passiert. + +**Ein echter Mangel blieb aus der Suche uebrig**: `theme.ts` und das Panel +lasen `prefers-color-scheme` genau einmal beim Laden und horchten danach nie +wieder. Stellt das Geraet im Betrieb um - automatische Umschaltung bei +Sonnenuntergang -, blieb die Oberflaeche bis zum naechsten Neuladen stehen. +Beide horchen jetzt; eine selbst getroffene Wahl bleibt unangetastet. Drei +Tests, die ohne die Behebung nachweislich fallen (gegengeprueft). + +### Das Zahnrad im Autohaus-Kasten - meiner + +Gemeldet: *"Das Zahnrad vom Autohaus hat seine Position (rechts oben in der +Kachel) verlassen. Warum"*. Gemessen: 58px von oben, 288px von rechts statt +8/8. + +Ursache war mein eigener HIG-Durchgang vom 04.09.2026 (Commit b0ec8b4). Ich +hatte `.zahnrad` in die Gruppe fuer die 44x44-Trefferflaeche aufgenommen: + + #back, .ringsbtn, .icon-btn, .zahnrad { position: relative; } + +`audi-dashboard-ios.css` laedt nach `audi-dashboard.css`, gleiche Spezifitaet - +das `position: absolute`, das das Zahnrad in die Kachelecke heftet, war damit +ueberschrieben. Die Auflage war ausserdem ueberfluessig: `.zahnrad` ist im +Basisblatt schon 44x44. Ich hatte die Gruppe zusammengestellt, ohne zu pruefen, +welche Knoepfe ihre Flaeche laengst haben. `.zahnrad` ist wieder heraus, die +drei anderen sind nicht absolut positioniert und bleiben. + +### Das Fotomenue: an der Kachel statt am Bildschirm, ohne Ausweg + +Gemeldet: *"Der Popup zum ersetzen erscheint ausserhalb vom Sichtfeld... es +fehlt ein abbrechen button wenn man zufaellig auf das rad klickt."* + +`.bildmenu` war `position: absolute` und hing damit an der **Kachel**, nicht am +Bildschirm. Bei der hohen Raeder-Kachel lag es unterhalb des Sichtfelds. Einen +Abbrechen-Knopf hatte es nie. + +Die App hatte den Fall schon geloest - `` mit abgesetztem +Abbrechen. Das Panel zieht jetzt nach: `aktionsblatt(titel, aktionen)` neben +`bestaetigen()`/`hinweis()`, gerendert am Wurzelelement. `.bildmenu` samt +`bildMenuOffen`, den beiden Markup-Bloecken, dem toten +`data-bildloeschen`-Handler und allen CSS-Regeln in beiden Blaettern ist +entfernt. `.bildmenu-catcher` bleibt - den benutzt das SmartDeal-Blatt. + +Gemessen am laufenden Panel: Blatt bei 553-760px in einem 794px-Fenster, drei +Knoepfe "Foto ersetzen / Foto loeschen / Abbrechen". + +### Der Blattkopf las sich als dritte Auswahl + +Gemeldet: *"Die Ueberschrift des Menues Sommerrad ersetzen/loeschen lautet +'Sommerrad'... Auf mich wirkt es wie eine dritte Auswahl"*. Zu Recht: er stand +linksbuendig, 15px, in `--fg` - fast genau wie die Knopfzeilen darunter. Apples +Aktionsblatt setzt den Titel **zentriert, kleiner (13px) und gedaempft** +(`--fg2`). Beide Codebasen nachgezogen. + +### "Anstehende Termine" gehoerte scheinbar zum Oelwechsel + +Gemeldet: *"Aktuell wirkt es als wuerde es zu Oelwechsel gehoeren"*. Gemessen: +13px, Gewicht 400, `--fg3`, keine Grossschrift - und nur **5px** ueber +"Oelwechsel" (16px, `--fg`). Der Kopf ist entfernt: die drei Zeilen benennen +sich selbst, die Seite heisst bereits "Service". + +**Offen und nicht angefasst:** dieselbe schwache Behandlung hat die +Kachelueberschrift ueberall im Panel (`.label`, iOS-Auflage: 13px, `--fg3`, +keine Grossschrift). Die App setzt an derselben Stelle 10px in Grossschrift mit +0.2em Sperrung - eine erkennbare Augenbraue. Das ist eine Abweichung ueber +Dutzende Kacheln und gehoert nicht in einen Nebensatz. + +### Wartungsplan-Formular + +Vorgabe: Art nach oben, Feldbreiten nach Inhalt, zwei Erklaertexte weg. +Gemessen vorher/nachher: + +| Feld | vorher | jetzt | +|---|---|---| +| Art | 3. Zeile | 1. Zeile | +| Datum | ~170-190px (Browser-Vorgabe) | 124px Panel / 150px App | +| Werkstatt | 218px | 306px, eigene Zeile | +| Kosten | 96px | 116px | + +Die Browser-Vorgabe ist inhaltsblind: **jedes** Textfeld ohne `size` bekommt +dieselbe Breite. Deshalb schwamm das Datum und wurde der Werkstattname +abgeschnitten. + +Der Werkstattname passt in keine Spalte neben einem Label: ein echter Name +("Audi AG Kundendienst Center Ingolstadt") braucht 322px, auf 375px Geraetebreite +bleiben neben dem Label 218. Er steht deshalb in einer `.feld--breit`-Zeile. +**Auch das reicht fuer diesen Extremfall nicht ganz** - 306 gegen 322px, das +Feld scrollt dann. Weiter kaeme man nur mit kleinerer Schrift, und unter 16px +zoomt iOS beim Antippen die ganze Seite. + +Die App hat ein natives `type="date"` mit Kalendersymbol, das Panel ein +Textfeld - daher 150 gegen 124px. Das ist ein Bauunterschied, kein Versehen. + +### Raederzaehler: er rechnet, ihm fehlt nur Futter + +Frage: *"Funktioniert der Raederzaehler ueberhaupt? Ich sehe hier wenig +Bewegung bei den Fahrten."* Gemessen an der Testinstanz: + +| | Anzahl | +|---|---| +| Fahrten gesamt | 19 | +| mit `reifensatz` | 4 | +| davon mit gemessener Strecke | 2 | +| noch nicht gutgeschrieben | 0 | + +Die vier gestempelten Fahrten summieren 9,9 km, und **genau 9,9 km** stehen auf +den Sommerraedern. Der Zaehler arbeitet also fehlerfrei. Wenig Bewegung hat +drei Gruende, alle beabsichtigt oder bekannt: + +* Importierte Fahrten (6) werden **bewusst nicht** gestempelt - ihre Kilometer + stecken schon im gespeicherten Radstand, ein erneuter Import wuerde sie sonst + ein zweites Mal aufschlagen (Kommentar in `historienimport.py`). +* Sieben `ha`-Fahrten stammen aus der Zeit vor dem Stempel und tragen ihn nicht + nach. +* Zwei der vier gestempelten Fahrten haben noch keine gemessene Strecke; sie + werden gutgeschrieben, sobald das Screening sie fuellt. + +Der eigentliche Grund auf der **realen** Instanz ist ein anderer und heute +zuerst gefunden worden: dort wurden ueberhaupt keine Fahrten erkannt, weil die +Erkennung still das Trip-Signal des Dongles bevorzugte, das nie ankam. Ohne +Fahrten kein Radstand. diff --git a/companion-app/.gitignore b/companion-app/.gitignore index 1a27a41..f879a22 100644 --- a/companion-app/.gitignore +++ b/companion-app/.gitignore @@ -1,7 +1,5 @@ node_modules/ dist/ -# Bau der Musterseite (vite.muster.config.ts) - reines Ergebnis, nie in der .ipa. -dist-muster/ *.local .DS_Store diff --git a/companion-app/muster/index.html b/companion-app/muster/index.html deleted file mode 100644 index 70933f4..0000000 --- a/companion-app/muster/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - DataMetric360 – Musterseite - - -
- - - diff --git a/companion-app/muster/muster.tsx b/companion-app/muster/muster.tsx deleted file mode 100644 index 408ce18..0000000 --- a/companion-app/muster/muster.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Musterseite: die Oberflaeche der App mit Beispieldaten, ohne Server. - * - * WOFUER - * ------ - * Die App braucht sonst einen gespeicherten Zugang, um ueberhaupt etwas - * anzuzeigen - und damit war jede OPTISCHE Pruefung an ein Zugangstoken - * gebunden. Genau daran ist in diesem Projekt wiederholt die Verifikation - * haengengeblieben, obwohl fast jeder gefundene Fehler (CSS-Spezifitaet, - * Flex-Basis in Spalten, unsichtbare Rahmen) nur am gerenderten Bild - * sichtbar war und von tsc und den Tests nicht gefunden werden konnte. - * - * Diese Seite mountet dieselben Bildschirme mit demselben Stylesheet, aber - * mit `beispielApi()` aus den Tests. Kein Netz, kein Token, keine echten - * Daten - und trotzdem echtes CSS. - * - * NICHT TEIL DER APP - * ------------------ - * Gebaut wird sie ueber `vite.muster.config.ts` nach `dist-muster/`. Der - * normale `npm run build` fasst sie nicht an, sie liegt also auch nicht in - * der .ipa. - * - * Aufruf: `?seite=einst` (Vorgabe), jeder Name aus navigation.ts TITEL. - * Dazu `?seite=einrichtung` fuer den Willkommensbildschirm - der ist keine - * Route (er steht VOR der Anmeldung) und braucht deshalb einen eigenen Zweig. - */ - -import { StrictMode, useState } from "react" -import { createRoot } from "react-dom/client" - -import "@audi-dash/ui/styles.css" -import "../src/stile/grundlage.css" -import "../src/stile/audi-schrift.css" -import "../src/stile/rahmen.css" -import "../src/stile/screens.css" - -import { DatenAnbieter } from "../src/daten/DatenKontext" -import { Shell } from "../src/Shell" -import type { SeitenName } from "../src/navigation" -import { TITEL } from "../src/navigation" -import { Bestaetigungsblatt } from "../src/screens/bestaetigung" -import { Einrichtung } from "../src/screens/Einrichtung" -import { SeiteFuer } from "../src/screens/register" -import { beispielApi } from "../src/tests/beispieldaten" - -function Muster() { - const gewaehlt = new URLSearchParams(location.search).get("seite") || "einst" - const start = gewaehlt as SeitenName - const [route, setzeRoute] = useState<{ name: SeitenName; id?: string }>({ name: start }) - const geheZu = (name: SeitenName, id?: string) => - setzeRoute(id === undefined ? { name } : { name, id }) - // Der Willkommensbildschirm haengt an keinem Zugang und an keiner Route. - if (gewaehlt === "einrichtung") return {}} /> - return ( - - - - {}} - beiAbmeldung={() => {}} - /> - - - ) -} - -// Zum schnellen Springen zwischen den Seiten, ohne die Adresse zu tippen. -;(window as unknown as { SEITEN: string[] }).SEITEN = Object.keys(TITEL) - -const wurzel = document.getElementById("wurzel") -if (!wurzel) throw new Error("Wurzelelement #wurzel fehlt") -createRoot(wurzel).render( - - - , -) diff --git a/companion-app/src/screens/Service.tsx b/companion-app/src/screens/Service.tsx index aa0cdc8..7888700 100644 --- a/companion-app/src/screens/Service.tsx +++ b/companion-app/src/screens/Service.tsx @@ -130,7 +130,6 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => return ( <> - Anstehende Termine
@@ -504,17 +503,6 @@ export function Servicebuch({ Eintrag - - setze("datum", w)} /> - - - setze("km", Number(e.target.value))} - /> - setze("km", Number(e.target.value))} + /> + + {/* Label oben, Feld über die ganze Zeile - wie der Reifen-Kommentar + und .feld--breit im Panel. Ein echter Werkstattname braucht 322px; + neben einem Label bleiben auf 375px nur 218 (gemessen 05.09.2026). */} +
+ + setze("werkstatt", e.target.value)} /> - +
setze("kosten", e.target.value === "" ? null : Number(e.target.value))} /> - - Kosten fließen in die Jahresübersicht ein -
@@ -561,9 +562,6 @@ export function Servicebuch({ value={entwurf.arbeiten ?? ""} onChange={(e) => setze("arbeiten", e.target.value)} /> - - Beim Verkauf ist diese Liste mehr wert als die Rechnung — sie zeigt, dass gepflegt wurde - void speichern()} disabled={laeuft}> diff --git a/companion-app/src/stile/screens.css b/companion-app/src/stile/screens.css index a0ca4ee..17f1200 100644 --- a/companion-app/src/stile/screens.css +++ b/companion-app/src/stile/screens.css @@ -1,2244 +1,2256 @@ -/* - * Screen-übergreifende Bausteine: Eingabefelder, Hinweisleiste, - * Ersteinrichtung, Leerzustände, Ladeanzeige. - */ - -/* -------------------------------------------------------- Eingabefelder */ - -.dm-eingabe, -.dm-auswahl { - font: inherit; - font-weight: 300; - font-size: 16px; - color: var(--fg); - background: none; - border: 0; - text-align: right; - outline: none; - min-width: 120px; - max-width: 60%; - padding: 0; -} - -.dm-eingabe::placeholder { - color: var(--fg3); -} - -/* Die 16px oben sind nicht Geschmack, sondern Pflicht: unterhalb davon zoomt - iOS beim Antippen eines Eingabefelds die ganze Seite heran, danach lässt - sich die Ansicht verschieben - in einer nativen Hülle wirkt das wie ein - Fehler (Nutzerbefund 2026-08-29, Willkommensbildschirm). - Sie kamen bisher nie an: @audi-dash/ui setzt ".ads-feld input" auf 13.5px, - und dieser Selektor ist spezifischer (Klasse + Typ) als das bloße - ".dm-eingabe" - er gewinnt also, egal in welcher Reihenfolge geladen wird. - Deshalb hier dieselbe Angabe noch einmal, spezifisch genug um zu greifen. - @audi-dash/ui bleibt unangetastet, wie schon beim type=date-Fall unten. */ -.ads-feld input.dm-eingabe, -.ads-feld select.dm-auswahl { - font-size: 16px; -} - -/* type="date": ohne appearance-Reset behält Safari/WKWebView (die iOS-Hülle - dieser App) sein eigenes, stark abgerundetes natives Eingabe-Chrome statt - der Box-Optik aus @audi-dash/ui's Feld - beim Nutzer sichtbar als - "gestreckter Kreis statt Box" (Reifen.tsx, Wechseltermin). @audi-dash/ui - selbst bleibt unangetastet (kein appearance-Reset dort, andere Felder - zeigen den Fehler nicht) - der Reset sitzt hier, spezifisch für type=date, - höhere Selektor-Spezifität als das geteilte ".ads-feld input" schlägt - dessen feste min-width:132px, die hier breiter ist als "27.08.2026" - braucht. */ -input[type="date"].dm-eingabe { - -webkit-appearance: none; - appearance: none; - /* Kein width:fit-content mehr. Das stammte aus der Zeit, als .ads-feld input - eine feste min-width von 132px hatte - die ist beim Paritaetsabgleich auf - min-width:0 umgestellt worden, seither zwang fit-content das Feld in eine - Breite, die je Datum und Stelle anders ausfiel. Jetzt bemisst es sich wie - jedes andere Eingabefeld (flex:0 1 auto aus @audi-dash/ui), genau wie - .feld input im Panel. */ - text-align: right; -} - -/* Eine Zeile aus einer bearbeitbaren Liste (z. B. Selbstbeteiligung im - Vertrag) - zwei Eingaben plus Löschen-Knopf in einer Reihe, deckungsgleich - mit .feld/.zeile-loeschen im Panel (audi-dashboard.css). */ -.dm-selbst-zeile { - display: inline-flex; - align-items: center; - gap: var(--sp-2); -} - -.dm-zeile-loeschen { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - width: 22px; - height: 22px; - border: none; - background: none; - color: var(--fg3); - font-size: 18px; - line-height: 1; - padding: 0; - cursor: pointer; - flex: 0 0 auto; -} - -.dm-zeile-loeschen::before { - content: ""; - position: absolute; - inset: -11px; -} - -.dm-zeile-loeschen:active { - color: var(--bad); -} - -.dm-auswahl { - text-align: right; - appearance: none; - cursor: pointer; -} - -/* Deckend, NICHT --tile: die aufgeklappte Liste eines malt der + Browser selbst, ausserhalb der Seite. Ein durchscheinender Wert laesst + dort den hellen Untergrund des Systems durch - im Panel steht aus genau + diesem Grund --canvas (.feld select option). */ +.dm-auswahl option { + color: var(--fg); + background: var(--canvas); +} + +/* `.notiz` im Panel: --ios-fill statt --tile-2, 12px Radius, 14px Abstand + nach oben, Zeilenhöhe 1,55 und normaler Schnitt. */ +.dm-notiz { + font: inherit; + font-size: 16px; + color: var(--fg); + background: var(--ios-fill); + border: 0; + border-radius: 12px; + padding: 10px 12px; + margin-top: 14px; + width: 100%; + min-height: 88px; + line-height: 1.55; + resize: vertical; + outline: none; +} + +/* --------------------------------------------------------- Hinweisleiste */ + +.dm-hinweis { + display: flex; + align-items: center; + gap: var(--sp-2); + padding: 9px var(--sp-5); + padding-top: calc(env(safe-area-inset-top, 0px) + 9px); + font-size: 12.5px; + font-weight: 300; + color: var(--fg); + background: var(--tile-2); + border-bottom: 1px solid var(--line); +} + +.dm-hinweis--offline { + color: var(--warn); +} + +.dm-hinweis svg { + flex: none; +} + +/* -------------------------------------------------------- Ersteinrichtung */ + +.dm-einrichtung { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + /* Seitlich 20 statt 32: auf 375 px blieben fuer die Beschriftung nur + 87 px, und "Server-Adresse" brach dadurch am Bindestrich um - + ausgerechnet auf dem allerersten Bildschirm (gemessen 04.09.2026). + Oben und unten bleibt es bei 32. */ + padding: 32px 20px; + overflow-y: auto; +} + +.dm-einrichtung__karte { + width: 100%; + max-width: 420px; +} + +.dm-einrichtung__titel { + font-size: 28px; + font-weight: 300; + letter-spacing: -0.02em; + margin: 10px 0 12px; +} + +.dm-einrichtung__text { + font-size: 14px; + font-weight: 300; + line-height: 1.55; + color: var(--fg2); + margin: 0 0 22px; + text-wrap: pretty; +} + +.dm-einrichtung__fehler { + font-size: 13px; + font-weight: 300; + line-height: 1.5; + color: var(--bad); + margin: 14px 0 0; +} + +.dm-einrichtung__knoepfe { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: var(--sp-4); +} + +.dm-einrichtung__hinweis { + font-size: 12.5px; + font-weight: 300; + line-height: 1.5; + color: var(--fg3); + margin: 18px 0 0; +} + +/* ------------------------------------------------- Leer- und Ladezustände */ + +/* `.leer` im Panel: linksbündig und knapp gesetzt (gap 8px, padding 4px 0 2px), + Titel 16px/400 in --fg, Text 13,5px in --fg2. Hier war der Leerzustand + zentriert, mit 44px Polster und dünnerem Schnitt - deutlich mehr Fläche als + im Panel. */ +.dm-leer { + display: flex; + flex-direction: column; + gap: var(--sp-2); + padding: 4px 0 2px; + color: var(--fg2); +} + +.dm-leer__titel { + font-size: 16px; + font-weight: 400; + color: var(--fg); +} + +.dm-leer__text { + font-size: 13px; + line-height: 1.55; + margin: 0; + text-wrap: pretty; +} + +.dm-laden { + display: flex; + align-items: center; + justify-content: center; + gap: var(--sp-2); + padding: 44px 24px; + color: var(--fg2); + font-size: 13px; + font-weight: 300; +} + +/* ------------------------------------------------------------ Übersicht */ + +/* Deckungsgleich mit .szene.bleed im Panel: randlos, keine Kartenfläche - + Fahrzeugbild und Name/Kennzeichen sitzen direkt auf dem Seitenhintergrund, + nicht in einer grauen Karte. War hier bis 2026-08-30 fälschlich als + Kachel mit Hintergrund gebaut (Parität-Audit). */ +/* Randloses Fahrzeugbild wie im Panel (`.szene.bleed{margin:-10px -20px 0; + padding:0 0 10px}` plus `.szene.bleed .carbox{padding:0 8px}` und der + Verlauf nach unten in die Fläche). Hier war das Bild bisher auf die + Inhaltsbreite eingerückt und in eine abgerundete Kachel geschnitten - + im Panel läuft es über die volle Bildschirmbreite und blendet nach unten + aus. */ +.dm-szene { + position: relative; + margin: -10px calc(-1 * var(--seitenrand)) 0; + padding: 0 8px 10px; +} + +/* Die drei oberen Kacheln der Übersicht tragen im Panel eigene Abstände + (20/22/22 statt der 14 aller anderen). */ +.dm-kopfblock { + margin-bottom: 20px; +} + +.dm-tankkachel, +.dm-servicekachel { + margin-bottom: 22px; +} + +/* Der Ausblend-Verlauf ist am 2026-08-31 entfallen: er lag ueber dem + unteren Teil des Fotos, und der Eigentuemer hat zweimal darum gebeten, + das Fahrzeug unverdeckt zu sehen. Ebenso der Bodenschatten, der hier + frueher als .dm-szene--strasse::after stand - samt dem Schalter dafuer. */ + + +/* Das Fahrzeugfoto wird nicht mehr beschnitten. + + Gemessen am echten Foto (2026-08-31): es ist 2000x1125 gross, also 16:9. + Der Kasten stand auf 16:8 - bei object-fit:cover wurde das Bild auf die + Kastenbreite skaliert und oben wie unten je ein Viertel abgeschnitten. + Das Dach lag darin. + + Statt das Verhaeltnis auf 16:9 festzuschreiben - was beim naechsten Foto in + einem anderen Format wieder schneiden wuerde - bestimmt jetzt das Bild + selbst die Hoehe. Der Platzhalter behaelt sein festes Verhaeltnis, er hat + ja keines von sich aus. */ +.dm-szene .ads-bildbox { + aspect-ratio: auto; +} + +.dm-szene .ads-bildbox img { + height: auto; +} + +/* Ohne Foto bleibt die Fläche flach und läuft nicht randlos: sonst stünden + 172px leerer Platzhalter vor der Reichweite, der wichtigsten Zahl der + Seite (`.bildbox.bild-fehlt.carfix.klein{height:120px}` und + `.szene.bleed:has(.bild-fehlt)` im Panel). */ +/* Der Platzhalter belegt denselben Kasten wie ein echtes Foto (16:8). Er war + zwischenzeitlich auf 120 und dann 72px gestaucht, um die Reichweite + hoeher zu ruecken - auf Ansage des Besitzers steht er wieder in voller + Groesse, weil ein Streifen nicht erkennen laesst, was dort hingehoert. */ +.dm-szene .ads-bildbox:has(.ads-platzhalter) { + aspect-ratio: 16 / 8; + height: auto; +} + +.dm-galerie-wechsel .ads-bildbox:has(.ads-platzhalter) { + aspect-ratio: auto; + height: 132px; +} + +.dm-szene:has(.ads-platzhalter) { + margin-left: 0; + margin-right: 0; + padding-left: 0; + padding-right: 0; +} + +.dm-szene:has(.ads-platzhalter)::before { + display: none; +} + +/* Die Vorschau in den Einstellungen zeigt das ganze Foto. + + Vorher galt der Regelkasten 4:3 mit object-fit:cover - ein Foto anderen + Formats wurde oben und unten beschnitten, bei der Frontansicht besonders + deutlich (gemeldet 02.09.2026). Wer prüfen will, ob das richtige Bild + hochgeladen ist, muss es ganz sehen. Dieselbe Entscheidung wie beim + Fahrzeugfoto der Übersicht (.carfix.klein im Panel): das Bild bestimmt die + Höhe, nicht der Kasten. Nur der Platzhalter behält ein festes Verhältnis, + er hat ja keines von sich aus. */ +.dm-einstellbild .ads-bildbox { + aspect-ratio: auto; + height: auto; +} + +.dm-einstellbild .ads-bildbox img { + height: auto; +} + +.dm-einstellbild .ads-bildbox:has(.ads-platzhalter) { + aspect-ratio: 4 / 3; +} + +/* Die Draufsicht auf der Fahrzeugseite ist eine Beigabe, keine Szene: sie + steht über den Sicherheitszeilen und soll die Zeilen nicht nach unten + drängen. Statt randlos über die volle Breite (rund 374px auf einem Telefon, + mit 16:8 also 187px hoch) steht sie eingerückt und mittig — 60% Breite, die + Höhe geht im selben Verhältnis mit auf rund 112px zurück. Auf Ansage des + Eigentümers am 02.09.2026 („Draufsicht-Foto Bereich ist zu groß"): + schmaler, nicht flacher — flacher hätte bei einem Foto, das nicht sehr + breit ist, oben und unten geschnitten. Gleichlautend .szene.bleed.schmal + im Panel. Der :has()-Zweig oben setzt die Ränder bereits auf 0, wenn kein + Foto da ist; hier gilt es unabhängig davon. */ +.dm-szene--schmal { + /* Der Abstand zum Fahrzeug kommt jetzt aus dem Layout, nicht aus leerem + Bildrand: das Foto ist seit dem 02.09.2026 freigestellt und eng + beschnitten. Oben und unten Luft wie im Vorbild des Eigentümers. */ + margin: 0; + padding: 22px 0 26px; +} + +.dm-szene--schmal .ads-bildbox { + width: 60%; + margin: 0 auto; +} + +/* Zum Löschen nach links wischen (WischLoeschen in bausteine.tsx). + + Der Knopf liegt fest hinter dem Inhalt, verschoben wird nur der Inhalt + darüber — deshalb braucht er einen DECKENDEN Grund, sonst schiene das Rot + hindurch. --tile ist der Grund der Kachel, in der die Zeile sitzt. + + touch-action: pan-y sagt dem Browser, dass die waagerechte Achse uns + gehört: senkrecht scrollt er weiter selbst, waagerecht wartet er auf uns, + statt die Geste nach ein paar Pixeln zu übernehmen. */ +.dm-wisch { + position: relative; + overflow: hidden; +} + +/* Unsichtbar, solange nicht gewischt wird - siehe WischLoeschen und + .ads-swipe-delete im Design-System. */ +.dm-wisch__loeschen { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 88px; + visibility: hidden; + border: 0; + padding: 0; + background: var(--bad-flaeche, var(--bad)); + color: #fff; + font: inherit; + font-size: 15px; + cursor: pointer; +} + +.dm-wisch--zeigt .dm-wisch__loeschen { + visibility: visible; +} + +/* Voll durchgewischt: die Flaeche wird kraeftiger, damit vor dem Loslassen + ablesbar ist, dass jetzt geloescht wird und nicht nur aufgeklappt. + Wortgleich mit .ads-swipe-delete--armed im Design-System. */ +.dm-wisch__loeschen--scharf { + filter: brightness(1.12); +} + +.dm-wisch__inhalt { + position: relative; + /* Deckend, NICHT --tile: die Zeile liegt auf einer Kachel, und die ist + im Nachtmodus selbst nur ein durchscheinender Schleier - zweimal + gemalt wird der Streifen heller als die Kachel um ihn herum, und der + rote Knopf darunter scheint durch. Dieselbe Regel wie bei + .ads-swipe-content und .swipe-content im Panel (Abschnitt AP). */ + background: var(--tile-deckend, var(--canvas)); + touch-action: pan-y; +} + +.dm-szene .ads-bild, +.dm-szene img { + width: 100%; + display: block; +} + +/* Kein linkes/rechtes Innenpolster: das Panel stellt Kennzeichen/Marke in + eine randlose "tile flat" (padding:0) - links/rechts eingerückt zu haben + ließ diesen Text nicht mit "Reichweite" & Co. auf einer Kante stehen + (Nutzerbericht "nicht linksbündig zum Rest der Seite", live gemessen: + 38px hier gegen 22px bei "Reichweite" - im Panel beide bei 16px). Nur + oben/unten bleibt Abstand (zum Bild bzw. zur nächsten Kachel). */ +/* Ohne eigenes Polster: im Panel sitzen Typenschild, Kennzeichen und + Statuszeile direkt in einer randlosen `.tile.flat` (padding 0), das + Kennzeichen mit 10px Abstand zum Schild. Die 16/22px hier machten den Block + 36px höher als im Panel (dort 89px, hier gemessen 125px). */ +.dm-szene__text > .ads-eyebrow { + margin-top: 10px; +} + +/* Übersicht: "Nächster Service" als tippbare Kennzahl innerhalb der Kachel, + darunter der Kilometerstand als ruhige Wertezeile. Nur der obere Teil ist + ein Knopf, damit "differentiate controls from content" (Apple HIG) gilt - + deckungsgleich mit .serviceblock/.servicezeile im Panel + (audi-dashboard.css). */ +.dm-serviceblock { + position: relative; + display: block; + width: 100%; + border: 0; + background: none; + padding: 0; + margin: 0; + font: inherit; + text-align: left; + color: inherit; +} +/* Nur wenn es wirklich ein Knopf ist (ein Termin vorhanden, siehe + Uebersicht.tsx) - der "kein Eintrag"-Zustand ist ein reines
, ohne + Zeigehand/Press-Feedback, deckungsgleich mit der nicht-klickbaren + .tile-Variante im Panel. */ +button.dm-serviceblock { + cursor: pointer; +} +button.dm-serviceblock:active { + opacity: 0.55; +} +.dm-serviceblock__chevron { + position: absolute; + top: 3px; + right: 0; + width: 6px; + height: 10px; + color: var(--fg3); + fill: none; + stroke: currentColor; + stroke-width: 1.4; +} +/* Serviceart-Icon links der Kennzahl statt des Wortes "bis zum/zur ..." - + deckungsgleich mit .fig-zeile im Panel (audi-dashboard.css). */ +/* margin-top deckungsgleich mit .fig-zeile im Panel - fehlte hier, "Nächster + Service:" und die Kennzahl standen ohne Abstand aufeinander (Parität- + Audit 2026-08-30). Der zweite Selektor deckt den Fall ohne Serviceart- + Icon ab (Hauptuntersuchung), wo das Panel dasselbe margin-top direkt auf + .fig setzt statt auf eine Wrapper-Zeile. */ +.dm-servicefig { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; +} +.dm-serviceblock > .ads-fig { + margin-top: 8px; +} +.dm-servicefig svg { + color: var(--fg2); + flex: none; +} +/* `.zahnrad` im Panel: rechts oben in der Kachel, 44px Trefferfläche, + 17px Symbol. */ +.dm-kachel--zahnrad { + position: relative; +} + +.dm-kachel--zahnrad > .ads-eyebrow:first-child { + padding-right: 44px; +} + +/* Das Zahnrad ist 44px hoch (Trefferfläche), die Überschrift darunter nur + 12px. Ohne reservierte Zeile ragte es also 20px in den Kachelinhalt und lag + sichtbar auf dem ersten Knopf - auf der Versicherungsseite auf "Inland · + ", von dort auch gemeldet (02.09.2026). Es fing dessen obere Hälfte + ab: der Knopf ließ sich am oberen Rand nicht wählen. + + Statt das Zahnrad zu verkleinern (44px sind die Mindestgröße einer + Trefferfläche) bekommt die Kopfzeile die Höhe, die es braucht. Gleiche + Regel im Panel (.tile:has(> .zahnrad)). */ +.dm-kachel--zahnrad > .ads-eyebrow:first-child, +.dm-kachel--zahnrad > .ads-title:first-child { + min-height: 34px; + padding-right: 48px; +} + +.dm-zahnrad { + position: absolute; + top: 8px; + right: 8px; + width: 44px; + height: 44px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 0; + border-radius: 50%; + background: none; + color: var(--fg3); + cursor: pointer; + padding: 0; +} + +.dm-zahnrad:hover, +.dm-zahnrad:active { + background: var(--tile-2); + color: var(--fg); +} + +.dm-zahnrad:active { + transform: scale(.92); +} + +/* Rufnummern der Notruf-Kachel: sehen aus wie Aktionsknöpfe, sind aber Links + (`` im Panel). */ +.dm-waehlknopf { + display: block; + text-decoration: none; + text-align: center; +} + +/* `.tile` der Kfz-Steuer steht im Panel mit 26px Abstand nach der + Vertragskachel. */ +.dm-steuerkachel { + margin-top: 26px; +} + +.dm-diagramm__fuss { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 10px; +} + +/* Der Zoom-Knopf ist im Panel eine schmale Variante von `.aktion` + (`padding:8px 16px;font-size:11px;width:auto;margin-top:0`). */ +.dm-zoomzurueck { + width: auto; + min-height: 0; + margin-top: 0; + padding: 8px 16px; + font-size: 11px; +} + +.dm-diagramm__tooltip { + margin-top: var(--sp-3); + font-size: 13px; + color: var(--fg2); +} + +/* Ruhespannungs-Skala auf der Batterieseite (Panel: .bv-skala-kopf plus der + Farbverlauf rot → gelb → grün und die drei Eckwerte darunter). */ +.dm-skalakopf { + display: flex; + align-items: center; + gap: 6px; + margin-top: 16px; + font-size: 13px; + letter-spacing: .01em; + color: var(--fg3); +} + +.dm-skalabalken { + margin-top: 8px; + height: 8px; + border-radius: var(--r-pill); + background: linear-gradient(to right, var(--bad), var(--warn), var(--ok)); +} + +.dm-skalamarken { + display: flex; + justify-content: space-between; + margin-top: 6px; +} + +.dm-skalamarken small { + font-size: 12.5px; + color: var(--fg3); +} + +.dm-bewertung { + display: inline-flex; + align-items: center; + gap: 8px; + justify-content: flex-end; +} + +.dm-bewertungstext { + display: block; + margin-top: 10px; + color: var(--fg2); + font-size: 13px; + line-height: 1.6; +} + +/* Antipp-Menü am Fahrzeugfoto (`.bildmenu` im Panel, iOS-Auflage: kein + Rahmen, durchscheinend mit Weichzeichner). */ +.dm-bildklick { + position: relative; +} + +.dm-bildklick__flaeche { + display: block; + width: 100%; + padding: 0; + border: 0; + background: none; + cursor: pointer; +}/* Name des Autohauses: 15px in --fg, wie im Panel. */ +.dm-autohaus__name { + font-size: 15px; + margin-top: var(--sp-3); +} + +/* Fehlender Wert im technischen Datenblatt: „noch offen" in --fg3, wie im + Panel (`
`). */ +.dm-offen { + color: var(--fg3); +} + +/* Zweitzeile unter der Bezeichnung eines Beitragspostens (SF + Beitragssatz). */ +.dm-beitragsposten__sf { + color: var(--fg3); + font-size: 11px; +} + +/* ------------------------------------------------------------ Statistik */ + +/* `.tile-h` im Panel: der ganze Kachelkopf ist der Schalter zum Aufklappen, + ohne eigenes Aussehen. */ +.dm-statkopf { + display: block; + width: 100%; + padding: 0; + border: 0; + background: none; + font-family: inherit; + text-align: left; + color: inherit; + cursor: pointer; +} + +.dm-statkopf__kopfzeile { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sp-3); +} + +.dm-statkopf__kopfzeile .ads-eyebrow { + flex: 1; +} + +.dm-statkopf[aria-expanded="true"] .ads-mark { + transform: rotate(45deg); +} + +.dm-statkopf__zeile { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 14px; + margin-top: var(--sp-3); +} + +.dm-statkopf__neben { + padding-bottom: 5px; +} + +/* `.quad` im Panel: vier Werte nebeneinander, Zahl im Wide-Schnitt. */ +/* Fuenf Spalten (Jahr/Monat/Woche/Tag/Absolut) passen auf einem Telefon nicht + nebeneinander: bei rund 300px Innenbreite blieben je 51px, und "209.177" + braucht bei 16px Audi Type Wide etwa 60. auto-fit bricht deshalb um - auf dem + Telefon drei plus zwei, auf einem breiten Bildschirm alle fuenf in einer + Reihe. Deckungsgleich mit .quad im Panel. */ +/* Vier feste Spalten - Jahr, Monat, Woche, Tag muessen in EINE Zeile passen. + + Vorher stand hier repeat(auto-fit, minmax(84px, 1fr)). Auf dem Telefon ist + die Kachel innen 307px breit, damit passten bei 12px Abstand nur drei + Spalten (3 x 84 + 2 x 12 = 276; vier waeren 372) - die vierte rutschte in + eine zweite Zeile. + + Mit vier festen Spalten bleiben 67,75px je Spalte. Der laengste realistisch + vorkommende Wert ist ein sechsstelliger Kilometerstand wie "209.177": am + laufenden Panel gemessen 67px in Audi Type Wide 300 bei 16px. Es passt + also, aber ohne Reserve - eine fuenfte Spalte gaebe es nur mit kleinerer + Schrift, und genau deshalb steht der Gesamtwert jetzt in der Kopfzeile der + Kachel statt in einer eigenen Spalte. + + Bewusst kein white-space:nowrap: waere ein Wert doch einmal breiter, + umbricht er innerhalb seiner Spalte, statt aus der Kachel zu laufen. */ +.dm-quad { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--sp-3); + margin-top: var(--sp-3); +} + +.dm-quad__n { + display: block; + font-family: var(--font-wide); + font-size: 16px; + font-weight: 300; + color: var(--fg); + line-height: 1.2; +} + +.dm-quad__l { + display: block; + margin-top: 4px; + font-size: 11px; + letter-spacing: normal; + color: var(--fg3); + line-height: 1.3; +} + +.dm-warnton { + color: var(--warn); +} + +/* `.wert` im Panel: der normale Fließtext einer Kachel. */ +.dm-wert { + font-size: 14px; + letter-spacing: normal; + color: var(--fg2); + margin-top: var(--sp-2); +} + +.dm-servicezeile { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--sp-4); + margin-top: 14px; + padding-top: 13px; + border-top: 1px solid var(--line); +} + +/* Service-Seite, "Anstehende Termine": drei sichtbar getrennte Bloecke statt + einer Wertzeilen-Liste - deckungsgleich mit .termingruppe im Panel + (audi-dashboard.css, vService()). Trennung ueber Abstand und eine Haarlinie + zwischen den Bloecken, nicht ueber verschachtelte Kacheln (Audi-CI). */ +.dm-termingruppe { + padding: 18px 0; + border-top: 1px solid var(--line); +} +.dm-termingruppe:first-of-type { + padding-top: 2px; + border-top: 0; +} +.dm-tgkopf { + display: flex; + align-items: center; + gap: 9px; + min-width: 0; +} +.dm-tgkopf svg { + color: var(--fg2); + flex: none; +} +.dm-tgkopf span { + font-size: 16px; + color: var(--fg); + font-weight: 400; +} +.dm-tgvorgabe { + font-size: 12.5px; + color: var(--fg2); + margin-top: 8px; +} +.dm-tgwert { + /* Fehlte hier ganz - fiel auf die schmale Grundschrift zurück statt auf + Audi Type Wide wie .tg-wert im Panel (Parität-Audit 2026-08-29). */ + font-family: var(--font-wide); + font-weight: 300; + font-size: 22px; + color: var(--fg); + letter-spacing: -0.005em; + margin-top: 4px; +} +.dm-tgwert--leer { + font-weight: 400; + font-size: 15px; + color: var(--fg3); + margin-top: 8px; +} +.dm-tgprognose { + font-size: 12.5px; + color: var(--fg2); + margin-top: 6px; +} + +/* Reichweiten-Kachel auf der Übersicht - deckungsgleich mit .tankzeile/.pct + im Panel (audi-dashboard.css). */ +.dm-tankzeile { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 14px; + margin-bottom: 14px; +} +.dm-tankzeile__pct { + font-size: 15px; + color: var(--fg2); + padding-bottom: 6px; +} + +.dm-navikachel__wert { + margin-top: var(--sp-1); + font-size: 15px; + font-weight: 300; + color: var(--fg); +}.dm-navikachel__wert small, +.dm-wertzeile small { + display: block; + margin-top: 2px; + font-size: 12.5px; + color: var(--fg3); +}.dm-textknopf { + border: 0; + background: transparent; + color: var(--fg2); + font-size: 13px; + font-weight: 300; + padding: 10px 14px; + border-radius: var(--r-pill); + cursor: pointer; +} + +.dm-textknopf:hover:not(:disabled) { + background: var(--shade); + color: var(--fg); +} + +.dm-textknopf:disabled { + opacity: 0.5; + cursor: default; +} + +/* --------------------------------------------------------- Werteliste */ + +.dm-werteliste { + margin: var(--sp-3) 0 0; +} + +/* Live am Panel gemessen (.row/dt/dd, audi-dashboard.css): beide Spalten + 16px/400, dt (Feldname) in var(--fg) - NICHT grau, das ist der Wert (dd, + var(--fg2)) - genau umgekehrt zu dem, was hier vorher stand (13px/grau + Label, 14px/hell Wert). Betrifft jede Wertzeile app-weit (Parität-Audit + 2026-08-30). */ +.dm-wertzeile { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--sp-4); + padding: 11px 0; + border-bottom: 1px solid var(--line); +} + +.dm-wertzeile:last-child { + border-bottom: 0; +} + +.dm-wertzeile dt { + font-size: 16px; + font-weight: 400; + color: var(--fg); +} + +.dm-wertzeile dd { + margin: 0; + font-size: 16px; + font-weight: 400; + color: var(--fg2); + text-align: right; +} + +/* Tankstelle im Einzelbeleg (Vorgabe des Eigentuemers, 04.09.2026). + + Vorher eine gewoehnliche Wertzeile: Name rechts in der Wertespalte, die + ganze Anschrift als Kleintext darunter. Die Spalte ist rund 120px breit - + "Hermann Mogler Mineraloelg. GmbH, Heidenheimerstr.6, 89551 Koenigsbronn" + stand dort vier- bis fuenfzeilig. Hier bekommt sie die volle Kachelbreite + unter der Beschriftung, zwei Zeilen (Tankstelle und Strasse / Ort), und + fuehrt beim Tippen in den Kartendienst - deshalb der Chevron rechts. + + Deckungsgleich mit .tankstellenzeile im Panel. */ +.dm-tankstellenzeile { + display: flex; + flex-direction: column; + gap: 4px; + padding: 11px 0; + border-bottom: 0; +} +.dm-tankstellenzeile dt { + font-size: 13px; + color: var(--fg2); +} +.dm-tankstellenzeile dd { + margin: 0; +} +.dm-tankstellenzeile__inhalt { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sp-3); + color: inherit; + text-decoration: none; +} +a.dm-tankstellenzeile__inhalt:active { + opacity: 0.6; +} +.dm-tankstellenzeile__ort { + display: block; + font-size: 16px; + color: var(--fg); + line-height: 1.35; + min-width: 0; +} +.dm-tankstellenzeile__ort small { + display: block; + margin-top: 2px; + font-size: 13px; + color: var(--fg3); +} + +.dm-abschnitt { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--sp-3); + padding: var(--sp-3) 2px 0; +} + +/* Wechselbild "Mein Audi" - deckungsgleich mit carImg("galerie")/.dots im + Panel: ein antippbares Bild, das zum nächsten Galerieplatz weiterschaltet, + plus Punktanzeige darunter. */ +.dm-galerie-wechsel { + display: block; + width: 100%; + border: 0; + background: none; + padding: 0; + cursor: pointer; +} +/* Das Bild bestimmt die Höhe, wie im Panel ("carfix klein"). + + Bis zum 02.09.2026 stand hier ein fester 16:9-Kasten mit object-fit:cover. + Jedes Foto, dessen Verhältnis davon abweicht, verlor dabei oben und unten + etwas - bei der Frontansicht die Stoßstange, so gemeldet. Der Preis ist, + dass der Block beim Durchblättern seine Höhe ändert; das ist dieselbe + Abwägung, die die Übersicht-Szene am 31.08. entschieden hat. Der + Platzhalter behält sein festes Maß (Regel weiter oben), er hat ja kein + eigenes Verhältnis. */ +.dm-galerie-wechsel .ads-bildbox { + aspect-ratio: auto; +} + +.dm-galerie-wechsel .ads-bildbox img { + height: auto; +} +.dm-galerie-punkte { + display: flex; + gap: 6px; + justify-content: center; + margin-top: 12px; +} +.dm-galerie-punkte span { + width: 5px; + height: 5px; + border-radius: 50%; + background: var(--line-strong); +} +.dm-galerie-punkte span.an { + background: var(--red); +} + +/* Klickbare Wertzeile (Batteriespannung → Verlauf) - deckungsgleich mit + `.row[data-go]` im Panel: derselbe Zeilenaufbau wie .dm-wertzeile, nur als + ` : ``}`; @@ -3647,19 +3654,13 @@ function vEinst() { ${(() => { const info = bildInfo(startIndex()); return `
-
+
${/* "klein" = das Bild bestimmt die Hoehe, statt im 16:9-Kasten beschnitten zu werden (siehe .carfix.klein). Wer hier prueft, ob das richtige Foto hochgeladen ist, muss es ganz sehen. */"" }${bildMitPlatzhalter(info.src, info.datei, info.label, "carfix klein", null, true)}
- ${bildMenuOffen === info.datei ? ` -
-
- - -
` : ""}
`; })()}
@@ -3879,7 +3880,26 @@ let debugOffen = false; "Speichern" uebernimmt ihn, weil derselbe Wert den Schlaf-Timeout des Dongles stellt. Siehe die Kachel in vEinst(). */ let PAUSENZEIT_ENTWURF = null; -let bildMenuOffen = null; +/* Das Blatt zu einem vorhandenen Foto: ersetzen oder loeschen. Loest + bildMenuOffen ab - der Zustand wird nicht mehr gebraucht, weil das Blatt + selbst der Zustand ist (siehe aktionsblatt()). */ +function bildAktionsblatt(dateiname, name) { + aktionsblatt(name, [ + { text: "Foto ersetzen", tun: () => { + const input = ROOT.querySelector(`[data-bildupload="${dateiname}"]`); + if (input) input.click(); + } }, + { text: "Foto löschen", destruktiv: true, tun: () => { + bestaetigen(LOESCH_TEXT.bild, LOESCH_HINWEIS.bild, "Foto löschen", () => { + (async () => { + try { await HASS.callService(DOMAIN, "bild_loeschen", { dateiname }); } + catch (err) { hinweis("Löschen fehlgeschlagen", err.message); return; } + bildVersion = Date.now(); render(); + })(); + }); + } }, + ]); +} let smartdealPopupOffen = false; // "sommer"|"winter", welcher Reifensatz gerade sein km-Korrektur-Feld offen // hat (Editier-Symbol neben der km-Anzeige) - null wenn keins offen ist. @@ -4172,7 +4192,7 @@ function vReifen() { ${t.km != null ? de(t.km) : "–"}km
-
+
${bildMitPlatzhalter(bildAdresse(radDatei), radDatei, radLabel, "radbild")}
@@ -4189,12 +4209,6 @@ function vReifen() {
` : ""} - ${bildMenuOffen === radDatei ? ` -
-
- - -
` : ""}
@@ -5178,6 +5192,34 @@ function themeGewaehlt() { if (gespeichert === "nacht" || gespeichert === "tag") return gespeichert; return window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches ? "tag" : "nacht"; } +/* Systemumstellung im laufenden Betrieb nachziehen - Gegenstueck zum + gleichnamigen Horcher in companion-app/src/theme.ts. + + themeGewaehlt() liest die Systemeinstellung genau einmal, beim Einhaengen + des Elements. Stellte das Geraet danach um - etwa durch die automatische + Umschaltung bei Sonnenuntergang -, blieb das Panel auf dem alten Stand + stehen, bis jemand neu lud. Das Stylesheet haengt an data-theme, nicht an + der Medienabfrage, es blieb also stimmig - aber eben stimmig falsch. + + Nur solange niemand selbst gewaehlt hat: eine gespeicherte Wahl bleibt. */ +function themaSystemNachziehen() { + if (!window.matchMedia) return; + const dunkel = window.matchMedia("(prefers-color-scheme: dark)"); + const nachziehen = () => { + let gespeichert = null; + try { gespeichert = localStorage.getItem(THEME_KEY); } catch (e) { /* Privatmodus */ } + if (gespeichert === "nacht" || gespeichert === "tag") return; + if (!HOST) return; + const neu = dunkel.matches ? "nacht" : "tag"; + if (HOST.dataset.theme === neu) return; + HOST.dataset.theme = neu; + themeIcon(); + tiles(); + }; + if (dunkel.addEventListener) dunkel.addEventListener("change", nachziehen); + else if (dunkel.addListener) dunkel.addListener(nachziehen); +} + function themeSetzen(modus) { HOST.dataset.theme = modus; try { localStorage.setItem(THEME_KEY, modus); } catch (e) { /* Privatmodus */ } @@ -5197,6 +5239,22 @@ function hinweis(titel, text) { sheet = { titel, text, knopf: null, aktion: null }; render(); } + +/* Mehrere Wahlmoeglichkeiten in einem Blatt, plus abgesetztes Abbrechen - + Apples Aktionsblatt. Gegenstueck zu in der App + (design-system), das dort schon seit dem 31.08.2026 das schwebende + Bildmenue abgeloest hat. + + Warum das Panel nachzieht: das alte .bildmenu war mit position:absolute an + die KACHEL geheftet, nicht an den Bildschirm. Bei einer hohen Kachel - den + Raedern - lag es dadurch unterhalb des Sichtfelds, und einen Weg hinaus gab + es auch nicht: wer das Rad nur versehentlich antippte, fand kein Abbrechen. + Beides am 05.09.2026 vom Eigentuemer gemeldet. Das Blatt haengt am Wurzel- + Element und ist deshalb immer zu sehen. */ +function aktionsblatt(titel, aktionen) { + sheet = { titel, text: null, knopf: null, aktion: null, aktionen }; + render(); +} /* ------------------------------------------------------- Beleg-Upload Ein Weg für alle drei Quellen (Ziehen, Einfügen, Dateiauswahl). Kein serviceRufen() hier: dessen catch loggt nur in die Konsole - siehe den @@ -5501,6 +5559,15 @@ function vBelegPopup() { function sheetMarkup() { if (!sheet) return ""; const kopf = `
${esc(sheet.titel)}${sheet.text ? `${esc(sheet.text)}` : ""}
`; + if (sheet.aktionen) { + const knoepfe = sheet.aktionen.map((a, i) => + ``).join(""); + return `
+ `; + } const aktion = sheet.knopf ? `` : ``; @@ -6281,6 +6348,13 @@ function ereignisseVerdrahten() { ROOT.addEventListener("click", (e) => { // B4: Das Action Sheet liegt über allem und fängt daher zuerst ab. if (e.target.closest("[data-sheet-abbrechen]")) { sheetSchliessen(); return; } + const blattAktion = e.target.closest("[data-sheet-aktion]"); + if (blattAktion) { + const gewaehlt = sheet && sheet.aktionen && sheet.aktionen[Number(blattAktion.dataset.sheetAktion)]; + sheet = null; render(); + if (gewaehlt && gewaehlt.tun) gewaehlt.tun(); + return; + } if (e.target.closest("[data-sheet-ok]")) { const aktion = sheet && sheet.aktion; sheet = null; render(); @@ -6370,18 +6444,6 @@ function ereignisseVerdrahten() { bestaetigen(LOESCH_TEXT.sbuch, LOESCH_HINWEIS.sbuch, "Löschen", () => { eintragLoeschen("sbuch", id); go("service"); }); return; } - const bildDel = e.target.closest("[data-bildloeschen]"); - if (bildDel) { - const dateiname = bildDel.dataset.bildloeschen; - bestaetigen(LOESCH_TEXT.bild, LOESCH_HINWEIS.bild, "Foto löschen", () => { - (async () => { - try { await HASS.callService(DOMAIN, "bild_loeschen", { dateiname }); } - catch (err) { hinweis("Löschen fehlgeschlagen", err.message); return; } - bildMenuOffen = null; bildVersion = Date.now(); render(); - })(); - }); - return; - } // Fahrzeugbild (Radbild wie Fahrzeugbilder-Slots): vorhandenes Foto -> Popup // mit Ersetzen/Löschen, fehlendes Foto (Platzhalter, bild-fehlt) -> Dateiauswahl // direkt öffnen. @@ -6394,18 +6456,10 @@ function ereignisseVerdrahten() { const input = container.querySelector(`[data-bildupload="${dateiname}"]`); if (input) input.click(); } else { - bildMenuOffen = dateiname; render(); + bildAktionsblatt(dateiname, bildKlick.dataset.bildname || "Foto"); } return; } - if (e.target.closest("[data-bildmenu-schliessen]")) { bildMenuOffen = null; render(); return; } - const bildErsetzen = e.target.closest("[data-bildersetzen]"); - if (bildErsetzen) { - const container = bildErsetzen.closest(".tile"); - const input = container.querySelector(`[data-bildupload="${bildErsetzen.dataset.bildersetzen}"]`); - if (input) input.click(); - return; - } if (e.target.closest("[data-sdabbrechen]")) { smartdealPopupOffen = false; render(); return; } if (e.target.closest("[data-sdbestaetigen]")) { const datum = $("#sdPopupBis").value; @@ -6887,7 +6941,7 @@ function ereignisseVerdrahten() { const daten_base64 = await bildAlsBase64(klein); await HASS.callService(DOMAIN, "bild_hochladen", { dateiname, daten_base64 }); } catch (err) { hinweis("Hochladen fehlgeschlagen", err.message); } - bildMenuOffen = null; bildVersion = Date.now(); render(); + bildVersion = Date.now(); render(); })(); return; } @@ -7312,6 +7366,7 @@ class AudiDashboardPanel extends HTMLElement { /* A6: Standard folgt der Systemeinstellung, eine manuelle Wahl überlebt den Reload. Vorher stand die App nach jedem Neuladen wieder auf Nacht. */ HOST.dataset.theme = HOST.dataset.theme || themeGewaehlt(); + themaSystemNachziehen(); schriftenLaden(); diff --git a/custom_components/audi_dashboard/frontend/audi-dashboard-ios.css b/custom_components/audi_dashboard/frontend/audi-dashboard-ios.css index 7a22aaf..541ced6 100644 --- a/custom_components/audi_dashboard/frontend/audi-dashboard-ios.css +++ b/custom_components/audi_dashboard/frontend/audi-dashboard-ios.css @@ -236,15 +236,10 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo folgen der neuen Lage: das Aktionsblatt steht frei ueber der unteren Kante und ist rundum rund, das Formularblatt sitzt auf der Kante auf und ist nur oben rund. */ -.bildmenu{border:0;border-radius:16px;background:rgba(40,44,52,.92); - -webkit-backdrop-filter:saturate(180%) blur(22px);backdrop-filter:saturate(180%) blur(22px); - box-shadow:0 12px 40px rgba(0,0,0,.5)} .sdpopup{border:0;border-radius:16px 16px 0 0;background:rgba(40,44,52,.92); -webkit-backdrop-filter:saturate(180%) blur(22px);backdrop-filter:saturate(180%) blur(22px); box-shadow:0 -8px 40px rgba(0,0,0,.5)} -:host([data-theme="tag"]) .bildmenu,:host([data-theme="tag"]) .sdpopup{background:rgba(250,250,252,.92)} -.bildmenu button{padding:16px 18px;font-size:16px;min-height:52px;border-bottom:1px solid var(--ios-sep)} -.bildmenu button:last-child{border-bottom:none} +:host([data-theme="tag"]) .sdpopup{background:rgba(250,250,252,.92)} /* ---------------- Tab-Leiste ---------------- */ .tabbar{border-top:1px solid var(--ios-sep);padding-bottom:26px; @@ -363,7 +358,7 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo ruhigen Karte. max-width + margin:auto zentriert sie innerhalb des bestehenden left/right-Abstands, ohne die transform-basierte Einblend-Animation dieser Elemente zu beruehren. */ - .sdpopup,.sheet,.beleg-popup,.bildmenu{max-width:560px;margin-left:auto;margin-right:auto} + .sdpopup,.sheet,.beleg-popup{max-width:560px;margin-left:auto;margin-right:auto} /* .setup-popup breiter als die übrigen Popups: die Sensorkennung neben der Feld-Überschrift (entitaetIdKurz()) kann ein echter Rohsensorname von Herstellerseite sein, teils 40-60 Zeichen lang @@ -401,11 +396,21 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo Wortgleich mit rahmen.css/screens.css der App (04.09.2026 gemessen). Das Overlay laesst das Bild unveraendert und vergroessert nur, was der Finger trifft; Mindesthoehen dort, wo das Element ohnehin Flaeche ist. */ -#back, .ringsbtn, .icon-btn, .zahnrad { +/* .zahnrad gehoert NICHT in diese Gruppe. Es ist in audi-dashboard.css + bereits 44x44 gross und mit position:absolute in die obere rechte + Kachelecke gesetzt. Dieses Blatt laedt danach, gleiche Spezifitaet - das + position:relative hier hat das absolute also ueberschrieben, und das + Zahnrad rutschte aus der Ecke in den Textfluss (im Autohaus-Kasten 58px + von oben, 288px von rechts; gemeldet vom Eigentuemer am 05.09.2026). + + Die Auflage brauchte es ohnehin nie: die 44x44 stehen schon im Basisblatt. + Hinzugefuegt hatte ich sie beim HIG-Durchgang am 04.09.2026, ohne zu + pruefen, welche Knoepfe ihre Flaeche laengst haben. */ +#back, .ringsbtn, .icon-btn { position: relative; } -#back::after, .ringsbtn::after, .icon-btn::after, .zahnrad::after { +#back::after, .ringsbtn::after, .icon-btn::after { content: ""; position: absolute; left: 50%; diff --git a/custom_components/audi_dashboard/frontend/audi-dashboard.css b/custom_components/audi_dashboard/frontend/audi-dashboard.css index 6f4daa3..ff4d960 100644 --- a/custom_components/audi_dashboard/frontend/audi-dashboard.css +++ b/custom_components/audi_dashboard/frontend/audi-dashboard.css @@ -808,6 +808,18 @@ img.carfix.klein { aspect-ratio: auto; height: auto; } .mitEinheit input { min-width: 0; width: 96px; text-align: right; } .mitEinheit span:last-child { font-size: 13px; color: var(--fg2); } .druckfeld { width: 58px !important; min-width: 0 !important; } +/* Feldbreiten nach dem, was drinsteht, statt nach dem Browser-Standardwert. + + Ohne diese Klassen bekommt jedes Textfeld dieselbe intrinsische Breite + (~170-190px, Safaris Vorgabe ohne size-Attribut) - unabhaengig vom Inhalt. + Im Wartungsplan hiess das: "01.03.2025" schwamm in einem viel zu breiten + Feld, waehrend ein Werkstattname im selben Mass abgeschnitten wurde + (gemeldet am 05.09.2026). Die Breiten sind an ihrem laengsten erwarteten + Inhalt gemessen ("01.03.2025" bzw. "1234,56"). Der Werkstattname passt in + keine Spalte neben einem Label und steht deshalb in einer .feld--breit-Zeile + ueber die ganze Breite. */ +.feld input.w-datum { width: 124px; flex: 0 0 auto; } +.mitEinheit input.w-betrag { width: 116px; } .notiz { width: 100%; text-align: left; margin-top: 14px; line-height: 1.55; resize: vertical; } .switch { position: relative; display: inline-block; flex-shrink: 0; width: 46px; height: 27px; } @@ -1319,34 +1331,9 @@ img.carfix.klein { aspect-ratio: auto; height: auto; } background: rgba(0,0,0,.45); animation: sheet-fade .18s ease-out; } -.bildmenu { - position: absolute; z-index: 1001; - left: 10px; right: 10px; - bottom: max(10px, env(safe-area-inset-bottom)); - top: auto; - transform: none; - background: var(--tile-deckend, var(--canvas)); - border-radius: 16px; - overflow: hidden; - padding: 0; - display: flex; flex-direction: column; - animation: sheet-rein .24s cubic-bezier(.22,.61,.36,1); -} -/* Die beiden Lage-Varianten sind gegenstandslos geworden - ein Blatt - kommt immer von unten. Die Klassen bleiben im Markup stehen, damit die - Aufrufstellen unberührt bleiben; hier tragen sie nichts mehr bei. */ -.bildmenu.rad, .bildmenu.ansicht { left: 10px; right: 10px; top: auto; transform: none; } -.bildmenu button { - display: block; width: 100%; text-align: left; - background: none; border: none; - color: var(--fg); font-family: inherit; font-size: 16px; - padding: 16px 18px; min-height: 52px; border-radius: 0; - border-bottom: 1px solid var(--line); - cursor: pointer; -} -.bildmenu button:last-child { border-bottom: none; } -.bildmenu button:active { background: var(--shade); } -.bildmenu button.loeschen { color: var(--bad); } +/* .bildmenu ist am 05.09.2026 entfallen: das Fotomenue laeuft jetzt ueber + das Aktionsblatt (aktionsblatt() in audi-dashboard-app.js), wie in der + App. .bildmenu-catcher bleibt - den benutzt das SmartDeal-Blatt. */ /* Formularblatt (siehe Kasten oben): unten angeschlagen, nur oben rund, blickdicht. Stand vorher als Karte 60px unter dem oberen Rand - ausser @@ -1486,12 +1473,22 @@ img.carfix.klein { aspect-ratio: auto; height: auto; } overflow: hidden; display: flex; flex-direction: column; } +/* Der Kopf eines Aktionsblatts ist eine Aufschrift, keine Wahl. + + Er stand linksbuendig, 15px, in --fg - also fast genau wie die Knopfzeilen + darunter, nur eine Haarlinie davon getrennt. Beim Radfoto ("Sommerrad") + las der Eigentuemer ihn deshalb als dritte Auswahl (gemeldet 05.09.2026). + + Apples Aktionsblatt setzt den Titel zentriert, kleiner und in der + gedaempften Farbe - dieselben drei Unterschiede, die ihn hier wieder als + Überschrift lesbar machen. */ .sheet-kopf { - padding: 15px 18px 14px; + padding: 14px 18px 12px; border-bottom: 1px solid var(--line); - display: flex; flex-direction: column; gap: 4px; + display: flex; flex-direction: column; align-items: center; + text-align: center; gap: 4px; } -.sheet-kopf strong { font-size: 15px; font-weight: 400; color: var(--fg); } +.sheet-kopf strong { font-size: 13px; font-weight: 600; color: var(--fg2); } .sheet-kopf span { font-size: 13px; color: var(--fg2); line-height: 1.45; } .sheet-gruppe button { background: none; border: none; diff --git a/custom_components/audi_dashboard/manifest.json b/custom_components/audi_dashboard/manifest.json index 98cce5d..2b905bd 100644 --- a/custom_components/audi_dashboard/manifest.json +++ b/custom_components/audi_dashboard/manifest.json @@ -1,7 +1,7 @@ { "domain": "audi_dashboard", "name": "Audi Dashboard", - "version": "2026.9.5.3", + "version": "2026.9.5.10", "documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md", "issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues", "codeowners": [ diff --git a/design-system/src/components/Sheet/Sheet.css b/design-system/src/components/Sheet/Sheet.css index ffa3229..3573e48 100644 --- a/design-system/src/components/Sheet/Sheet.css +++ b/design-system/src/components/Sheet/Sheet.css @@ -139,18 +139,29 @@ flex-direction: column; } +/* Der Kopf eines Aktionsblatts ist eine Aufschrift, keine Wahl. + + Er stand linksbündig, 15px, in --fg - also fast genau wie die Knopfzeilen + darunter, nur eine Haarlinie davon getrennt. Beim Radfoto ("Sommerrad") + las der Eigentümer ihn deshalb als dritte Auswahl (gemeldet 05.09.2026). + + Apples Aktionsblatt setzt den Titel zentriert, kleiner und in der + gedämpften Farbe - dieselben drei Unterschiede, die ihn hier wieder als + Überschrift lesbar machen. */ .ads-aktionsblatt__kopf { - padding: 15px 18px 14px; + padding: 14px 18px 12px; border-bottom: 1px solid var(--line); display: flex; flex-direction: column; + align-items: center; + text-align: center; gap: 4px; } .ads-aktionsblatt__kopf strong { - font-size: 15px; - font-weight: 400; - color: var(--fg); + font-size: 13px; + font-weight: 600; + color: var(--fg2); } .ads-aktionsblatt__kopf span {