# AGENTS.md — Project state, review findings, open items, and working rules **Last updated: 2026-08-30** (owner findings batch - vehicle pin, tile chevrons, select arrows, SmartDeal dialog, address cache and pull-to-refresh; manifest `2026.8.30.19`, see sections AO, AP and AQ. Before that: design-audit follow-through - the five priority items applied to both codebases, plus the location preview's wrong pin; manifest `2026.8.30.8`, see section AN. Before that: third parity round — the panel had never actually rendered Audi Type (@font-face inside a shadow root is ignored), "Daten bearbeiten" now works in both, and the last nine screens are aligned; manifest `2026.8.30.5`, see section AM. Before that: second parity round — the shared chrome turned out to be the real problem, plus seven screens rebuilt and two panel-side defects fixed; manifest `2026.8.30.3`, see section AL. Before that: a full 11-chapter panel↔companion-app visual/functional parity audit, one continuous session — see "Chapter 1" through "Chapter 11 (Abschluss)" further down for the complete record; companion-app manifest bumped to `2026.8.30.2`, OTA bundle rebuilt and redeployed; 2026-08-29: iOS signing prepared end to end — device build verified, signature blocked on a device registration at Apple, see section AI; 2026-08-23: merged the `umsetzung-datametric360` branch — companion app phases 1–10 done, see `UMSETZUNGSPLAN.md`; 2026-08-13: cleaned up remaining EU Data Act residue, fixed oversized toggle switches, and fixed a desktop-layout audit (settings button / rings logo / popups overflowing past the capped content column) — see `DESIGN_AUDIT_2026-08-13.md`; 2026-08-16: fixed five user-reported bugs — removed Türschloss/Haubenschloss checks, fixed a translucent confirm- sheet, added a flespi-style split lat/lon location source, hardened the "Lädt …" bootstrap race, and added a generic (station-independent) fuel-receipt parser fallback; later the same day: removed the now-unused combined `STANDORT_TRACKER` field entirely (flespi never provides it), closed a real silent-failure gap in the receipt-upload error path, and replaced the vehicle's map marker with a two-tone pin+car icon — see section C; a third round the same day found and fixed the long-standing "fragmented Leaflet rendering" defect at its root — the Leaflet stylesheet was loaded into `document.head` and therefore never reached the panel's shadow root — plus light-only map tiles, the CI `poi-car` marker, the red frame on list rows, the select-field grey box, and two more generic-receipt-parser gaps; a sixth round the same day fixed the map pins' real visibility problem (the CI `poi-car`/`poi` icons are thin outline-only paths, only 13-40% filled — solved with a solid silhouette layer extracted from each icon's own outer contour, not a redraw), applied a batch of ~20 user-reported UI polish items against Apple's Human Interface Guidelines, some via a guided Q&A, and lowered the battery-voltage statistic's cutoff from 13.2V to 12.8V; a seventh round the same day fixed a real CSS specificity bug that kept the wheel photo a wide 4:3 box instead of the intended 96x96 square, moved the "Montiert" pill to match, added `color-scheme` so native `` values ("10.000 km"/"1 Jahr") looked like inert text — iOS overlay strips border/background from `.feld select` and colors it the same grey as any other secondary text; added `.feld select{color:var(--ios-tint)}` so picker fields read as editable the way iOS conventionally signals it (accent color), free-text `.feld input`s deliberately left alone. (4) the expand/collapse `.mark` chevron on group headers (Fahrten "2026"/"August") looked like a checkmark — it uses the classic "2 borders + rotate(45deg)" CSS chevron technique, which requires a square box; `.mark` was 6×10px (non-square, sized for the unrelated SVG-path `.chev`/`.go` icons), made it 8×8px. Two more reported findings ("grey box around all menu icons", "red partial frame on Fahrten/Tanken rows") were investigated (computed-style diffing across all 5 tabs in both themes, a temporary 2.5x `transform:scale()` zoom on the live swipe-row DOM) but could not be reproduced — documented in the audit file as open, pending a screenshot from the real device. - [x] Fix five user-reported bugs (2026-08-16): (1) **Türschloss-/Haubenschloss-Erkennung entfernt** — user asked for these two checks to be permanently removed (not just left blank/unmapped like the rest of the retired VAG- integration fields). Removed `TUERSCHLOSS_SENSOREN`/`HAUBENSCHLOSS_SENSOR` from `entitaeten.py`'s `FELDER` catalog (no longer offered in the Setup menu) and from `einstellungen.py` (attributes deleted, not just blanked), and removed their entries from `_sicherheitscheck()` in `frontend_veroeffentlichung.py` (no longer listed under "Geprüfte Punkte"). `HECKKLAPPENSCHLOSS_SENSOR` (tailgate lock) was deliberately left untouched — user named only the two door/hood locks. Also removed the now-orphaned `input_boolean.test_entriegelt` helper and the four `test_audi_lock_*` template sensors from `testumgebung/konfiguration.yaml` (door-open/window fixtures for `TUER_SENSOREN`/ `FENSTER_SENSOREN` are untouched), and updated `testumgebung/README.md` accordingly. (2) **"Doppelt zugeordnet" (and every other confirm-sheet) was see-through** — `.sheet-gruppe`/ `.sheet-abbrechen` in `audi-dashboard.css` used `--tile-2`, which the iOS overlay's night theme redefines as `rgba(255,255,255,.10)` (translucent, meant for tile-on-tile layering) — the same root cause already fixed for `.setup-popup`/`.standortmenu` on 2026-08-13, just not carried over to the generic confirm-sheet used by `bestaetigen()`/`hinweis()`. Switched both to `var(--tile-deckend, var(--canvas))`, matching the existing fallback pattern already used elsewhere in this file (base CSS must not go blank if the purely-additive iOS overlay is ever reverted, since `--tile-deckend` is an overlay-only token). (3) **FMM003 coordinates arrive as two separate sensors, not one device_tracker** — user reported "coordinates are split into latitude and longitude". Root cause: `_standort()` in `frontend_veroeffentlichung.py` only ever read `latitude`/`longitude` as *attributes of a single `STANDORT_TRACKER` device_tracker entity* (HA's usual convention) — but some integrations (confirmed live in `audi_ha_test`'s own simulated FMM003 device, which exposes separate "Latitude coordinate value"/"Longitude coordinate value" sensors) publish them as two independent `sensor` entities instead, which `_standort()` had no way to consume. Added `STANDORT_LAT_SENSOR`/`STANDORT_LON_SENSOR` (new optional fields in `einstellungen.py` + `entitaeten.py`'s Setup-menu catalog, group "standort") as a second path: `_standort()` now tries `STANDORT_TRACKER` first (unchanged behavior when it works), and falls back to reading the two plain sensors' states directly when the tracker is unset or has no coordinates. (4) **App can show only "Lädt …" until a tab is clicked** — known, already-mitigated race (see the `nachladeAnstossen` header comment, `audi-dashboard-app.js`): on a slow/cold backend start, `render()` no-ops until `DATEN_GELADEN` flips true, and the previously observed manual fix was a HA panel remount. Root cause of *why* the click helps was not fully reproducible in `audi_ha_test` (this is a genuine uncertainty, flagged rather than guessed around) — but as a safe, low-risk hardening, `go()` (the tab/menu click handler) now calls `datenLaden(false)` proactively if `DATEN_GELADEN` is still false, so any click that happens to occur after data has actually become available is guaranteed to pick it up immediately instead of depending on timing. (5) **Fuel-receipt parser only understood Shell's exact layout** — user asked for a generic algorithm for other/unknown fuel stations: find the address, find the total ("Gesamt"/ "Absolut"), find the liters ("Menge"/"Amount"), divide to get the paid price/liter, compare against a printed "Preis/Liter" — a mismatch means a discount, found on the receipt as a minus-marked amount. Implemented exactly this as `_parsen_generisch()` in `homeassistant/data/shell_beleg_parser.py`, wired as a fallback in `main()` (Shell-specific `_parsen()` tried first, unchanged — verified the existing 10-receipt regression suite still passes byte-for-byte; `_parsen_generisch()` verified against a synthetic non-Shell receipt with a known discount, all fields and the discount math correct). No separate "SmartDeal" flag was needed — the frontend already treats any populated `discount` as SmartDeal-eligible (gated only by the user's own "SmartDeal aktiv" switch, not by station name), so a generically parsed Shell receipt behaves identically to one parsed via the strict path. Receipt-key generation uses `hashlib.sha1` (not the built-in `hash()`, which is randomly salted per process and would have broken the §7.7 duplicate-detection dedup across the parser's per-receipt subprocess invocations). All five deployed and verified live in `audi_ha_test` (version `1786752000`): Sicherheit- Liste and Setup-Katalog confirmed to no longer offer Türschloss/Haubenschloss; the new Breitengrad/Längengrad Setup fields confirmed present and correctly auto-suggesting the container's real split lat/lon sensors; "Doppelt zugeordnet" confirmed rendering on an opaque card by deliberately triggering it (this container's limited entity set causes a real duplicate auto-suggestion); pyscript reload clean, no new console errors beyond the pre-existing placeholder-image 404s. Synced to `installationspaket/`. - [x] Follow-up round the same day (2026-08-16), three more items: (1) **Removed `STANDORT_TRACKER` entirely** — user confirmed the combined device_tracker field is no longer needed (flespi only ever provides the split lat/lon sensors added earlier that day). Deleted the field from `einstellungen.py` and `entitaeten.py`'s Setup catalog, and simplified `_standort()` in `frontend_veroeffentlichung.py` down to the single split-sensor path (no more two-branch fallback). `overrides_schreiben()`'s existing "only known keys survive" behavior (see its docstring) meant no explicit migration was needed for stale `STANDORT_TRACKER` entries already sitting in `data/entitaeten.json` — confirmed live: this container's own leftover override file still had `STANDORT_TRACKER` from earlier testing (plus stale `TUERSCHLOSS_SENSOREN`/`HAUBENSCHLOSS_SENSOR` from before finding (1) of the previous entry), all silently dropped on the next Setup save, which is exactly why the GPS tile was still showing "Kein GPS-Signal" after the user's own remap attempt earlier — that attempt happened to save while the old combined field was still in the catalog, so nothing ever actually persisted `STANDORT_LAT_SENSOR`/`STANDORT_LON_SENSOR`. Redid the Setup save live (mapped both to `sensor.testcar_b9_fmm003_testintegratoin_lat/longitude_coordinate_value`) and confirmed the Übersicht/fullscreen Standort map switched from the "no signal" placeholder to a real rendered map at the correct coordinates (South Tyrol terrain, "Burgleralm" label visible). (2) **Fuel-receipt "silently does nothing", root cause found** — reproduced the exact real upload flow (button click → real `` → synthetic `File`/`DataTransfer` dispatch → `FileReader` → `hass.callService`) via browser instrumentation: the happy path works completely (backend parses, publishes `pyscript.audi_dashboard_beleg_ergebnis`, frontend fills the form) — `belegverarbeitung.py`'s existing `try/except` around `_parser_aufrufen()` already publishes a visible `fehler` for parser failures specifically. The actual gap: `base64.b64decode()` and `_pdf_speichern()` in `audi_dashboard_beleg_hochladen()` were *outside* that try block — any failure there (corrupted upload, disk full, ...) raised an unhandled exception with zero UI feedback, since `serviceRufen()`'s generic `.catch()` only does `console.error()`. Widened the try block to cover all three steps. Also stopped using `serviceRufen()` for this one call site specifically (`audi-dashboard-app.js`, the `eingabe.onchange` handler) — it now calls `HASS.callService()` directly with its own `.catch()` that sets `belegFehler` and re-renders, so a rejected service call (network, timeout — anything that bypasses the backend's own state-publish) is visible too, not just logged. Verified against the real 10-receipt Shell suite (still passing) both before and after this change. (3) **Vehicle map marker replaced with a two-tone pin+car icon** — added `CI.pinCar` (a balloon/pin path + white badge circle + a simplified car-front glyph built from rects, not circles, after an isolated-DOM-injection test round showed a circles-for-headlights version read as a face/animal rather than a car) to `audi-dashboard-app.js`, replacing the single-color `poiCar` teardrop previously used by `fahrzeugMarkerSVG()`. Updated both marker call sites' `iconAnchor` to the new pin's actual tip position and simplified `.fahrzeug-pin` in `audi-dashboard.css` (no more `color:var(--red)`/white-halo-filter hack, since the new icon carries its own fixed colors). Verified the icon markup renders correctly via an isolated DOM-injection test (bypassing Leaflet); could **not** get a final on-map screenshot in `audi_ha_test` — the Standort map tiles themselves only ever partially load in this container (reproduced on a fully fresh reload, unrelated to this change), consistent with a previously documented network limitation of this specific sandbox (see the Standort-Kachel entry above: reverse-geocoding via Nominatim had the same kind of container-specific network issue). Structurally verified only; needs a look on a real device/network to confirm final visual placement. All three deployed to `audi_ha_test` (version `1786758000`) and synced to `installationspaket/`. - [x] Third round the same day (2026-08-16) — **the "fragmented Leaflet rendering" defect is solved at its root**, plus four smaller items: (1) **Leaflet CSS never reached the shadow root.** `leafletLaden()` appended `leaflet.min.css` to `document.head`, but the panel renders inside `this.shadowRoot` — and document stylesheets do not cross a shadow boundary. Inside the panel, `.leaflet-tile {position:absolute}` and friends therefore never applied: the tiles laid themselves out as ordinary in-flow `` elements (two per row, ~1040 px of stacked height inside a 190 px box), so only a thin strip of map was ever visible, and the marker pane ended up ~1009 px below the visible area — which is exactly why the new vehicle pin appeared "missing". This also explains the earlier misdiagnosis as a container/network limitation: every tile request succeeded (`complete:true`, `naturalWidth:512`), the geometry was the problem. Fixed by injecting the stylesheet into `ROOT` and awaiting it before the map is built; the script tag stays in `document.head` (it must, `window.L` is global). The CSS injection is deliberately re-checked on every `leafletLaden()` call rather than guarded by the `window.L` check, because a HA panel remount produces a fresh shadow root while `window.L` is already set. Verified live: both the Übersicht preview map and the fullscreen Standort map now fill their containers and show the pin at the vehicle position. (2) **Maps are now always light.** `TILES[theme]` switched to CARTO `dark_all` in Nacht mode; replaced by a single themeless `TILE_URL` (`light_all`), matching how Google Maps & co. keep the standard road map light regardless of app theme. (3) **Vehicle marker now uses the real CI `poi-car` icons** (`poi-car-l.svg` / `poi-car-s.svg` from the delivered icon set) instead of the hand-built two-tone pin from the previous round: `CI.poiCarL` (48-grid) at ≥34 px, the existing `CI.poiCar` (24-grid) below, anchors recomputed per variant. Since these are single-color outline forms in `currentColor`, `.fahrzeug-pin` regained a stacked white drop-shadow halo so the outline stays readable on map tiles. (4) **Red partial frame on Fahrten/Tanken list rows** (open since the 2026-08-13 design audit, finding 3) — reproduced and confirmed by recoloring the delete button live. Row heights are fractional (66.28 px), so `.swipe-content`'s edges miss the device-pixel grid and the red `.swipe-delete` behind it bled through as a hairline around every row. Fixed by only painting the delete button while a swipe is actually happening: `.swipe-delete{visibility:hidden}` plus a `wischt` class set on the first movement of the gesture (and the existing `swiped` class for the open row). Verified the full gesture still works — button appears at the first pixel of drag and the row settles open at −84 px. (5) **Select fields now show the grey background box** (design audit finding 1) — replaced `.feld select{color:var(--ios-tint)}` with `background:var(--ios-fill);padding:8px 10px`, matching `.mitEinheit input` ("Pause bis [15] Minuten"), as the user asked. Verified on Modell / Ölwechsel-Intervall / Bildposition; free text fields stay plain, so the box now genuinely marks "there is a choice here". (6) **Generic receipt parser: total and station name** — a real Austrian non-Shell receipt returned the right litres but 6,00 € and no station. Two causes, both fixed: the free-running total pattern matched the *column heading* `SUMME-EUR` and read the next line's article number as the amount, and bon printers letter-space headings (`G E S A M T BETRAG EUR: 92,60`), which the keyword never matched. Total detection is now line-based — keyword checked against the whitespace-stripped line, amount must be a real money value (`_GELD`, decimals required), tax lines (`MWST`/`UST`/`VAT`/`STEUER`/`NETTO`) excluded, largest remaining candidate wins. The address heuristic no longer demands three fixed lines with a 5-digit postcode (AT uses 4 digits, and this receipt carries `SHELL TANKSTELLE, 6450 SÖLDEN` on one line): it finds the first postcode line in the header and takes the name from the line above. `Preis/L` without a spelled-out "Liter" now matches too. Result on that receipt: 42,13 l · 92,60 € · 2,198 €/l · no discount · `AUTO B. FRISCHMANN GMBH`. The 10-receipt Shell regression suite still passes. All deployed to `audi_ha_test` (version `1786765000`) and synced to `installationspaket/`. - [x] Fourth round the same day (2026-08-16): (1) **The "grey box around the icons" report is resolved — it was never a defect.** After five fruitless reproduction attempts across two sessions, the user clarified what they actually meant: the grey box seen on "10.000 km", "1 Jahr", "15" is the *desired* pattern, and **every value the user can change should carry it** to signal editability. `.feld input`/`.feld select` in `audi-dashboard-ios.css` now all get `background:var(--ios-fill);padding:8px 10px`; inputs with their own visual language (`checkbox`/`radio`/`range`/`file`) are excluded and keep the transparent base rule, so the three switches stay untouched. The separate `.mitEinheit input` rule became redundant and was removed (its width/alignment still come from `audi-dashboard.css`). Verified live: FIN, Kennzeichen, Erstzulassung, Ausführung, Modell, both Ölwechsel selects, Ansicht, Pause bis and Autom. Backup all boxed; the three checkboxes not. **Do not re-open this as a bug.** (2) **Station display name is now "Marke, Straße, Ort"** (e.g. `Shell, Pascalstr. 8, Ingolstadt`) instead of the bare operator name (`A. Zrenner GmbH`) — the brand and place say more when scrolling the Tankvorgänge list. New `_marke()`/`_ist_strasse()`/`_tankstelle()` helpers in `shell_beleg_parser.py`, shared by both parser paths so Shell and non-Shell receipts format identically. The brand is matched only against the first 8 lines (the receipt header) — searching the whole text would let "Total" hit a totals line. Missing parts are dropped rather than left as empty comma slots (the Austrian receipt prints no street: `Shell, SÖLDEN`), and without a recognizable brand the operator name takes the brand's place. `station_address` keeps the full street + postcode + city as before. The regression suite's `test_bekannte_stationen` expectations were updated to the new format (intended change, not a break); all 8 tests / 164 subtests pass. (3) **`installationspaket/` is versioned from now on** (user request) — removed from `homeassistant/.gitignore`. It carries no real vehicle data, only `data/fahrzeugprofil.example.json` with empty FIN/Kennzeichen placeholders; verified before un-ignoring. **Keep it in sync whenever `pyscript/` or `www/` changes** — that was already the working rule, but it is now visible in the repo when it is forgotten. Deployed to `audi_ha_test` (version `1786766000`). - [x] Fifth round the same day (2026-08-16) — one-click installer and the Einzelbeleg map: (1) **`installationspaket/Installieren.cmd` + `install.ps1`** automate steps 1–3 of `ANLEITUNG.md`. The `.cmd` wrapper exists because double-clicking a `.ps1` opens it in an editor and the execution policy blocks it; the bypass is per-call, the machine policy is not touched. Safe to re-run: never overwrites `fahrzeugprofil.json`, `fahrten.jsonl`, `tankvorgaenge.jsonl`, `entitaeten.json`, `ha_token.txt`; backs `configuration.yaml` up to `.bak` and replaces its own marker-delimited block instead of appending twice; refuses to touch the file at all when `pyscript:`/`panel_custom:` are already there from another source (duplicate top-level keys are invalid YAML — HA would not start); aborts before writing if the target has no `configuration.yaml`. `-Pruefen` is a dry run. **`install.ps1` is stored UTF-8 *with* BOM on purpose** — PowerShell 5.1 reads scripts as ANSI otherwise and mangles every umlaut; the files it *writes* stay BOM-less (YAML). Verified against a fake config tree: fresh install, re-run idempotency, data preservation, foreign-key refusal, wrong-directory abort, and the merged YAML parses. (2) **Tankstellenposition im Einzelbeleg.** The red `circleMarker` is now the CI `poi` pin (`CI.poiL`/`CI.poiS` from `poi-l.svg`/`poi-s.svg`, via `tankstellenMarkerSVG()`) — deliberately the plain pin, not `poi-car`, so vehicle and station stay distinguishable. **Found while testing: nothing in the backend ever writes `station_lat`/`station_lon`**, so this marker had never actually been reachable. Fixed by geocoding the receipt address in the frontend (`adresseAufloesen()`, Nominatim `/search` — the forward counterpart to the existing `standortAdresseAufloesen()`), with results cached in `localStorage` (`audi_dashboard_geocache`) including negative results, since a station address does not move and Nominatim's terms ask for caching. Confirmed live: `Shell, SÖLDEN` → 46.9756/11.0111. (3) **Zentrieren-Knopf** in the Einzelbeleg map card (`mapBoxMitZentrieren()`, `CI.gps` — byte-identical to the delivered `gps-s.svg`), disabled until the coordinates arrive. `.mapbox` gained `position:relative` so the control anchors to the card. Only there, per explicit request — the Standort fullscreen already has its own centre controls. Deployed to `audi_ha_test` (version `1786769000`). Note for testing map behaviour in this app: `initMap()` runs on **every** `render()`, and the app re-renders on each backend push (~30 s), rebuilding the map and re-centring it. Panning the map from the console and checking later therefore proves nothing — spy on `L.Map.prototype.flyTo` instead. - [x] Sixth round the same day (2026-08-16) — a large batch of user-reported UI polish items, plus the map pins' real visibility root cause: (1) **Map pins were genuinely near-invisible, not just a styling nit.** The user reported the vehicle's map pin looked "transparent"; reproduced empirically (not by eye) by rasterizing the live marker's SVG to a canvas and sampling alpha — only ~13% of the icon's own bounding box had any ink, and the exact center pixel was fully transparent. Root cause: `CI.poiCarL`/`poiCar` (and `poiL`/`poiS`, used for the Einzelbeleg station pin) are pure **contour** forms — the balloon outline is drawn as two nested, oppositely-wound paths that cancel to a 1px ring under the default nonzero fill rule, plus thin interior detail lines; there is no filled area at all. A first fix (solid navy circle behind just the icon's round "head", via `::before`) was explicitly rejected by the user ("I want the whole icon solid, not just the car" / "make the needle opaque") — correct, since it only covered ~36% of the icon and left the pointed tip as a bare outline. Real fix: each icon's `d` attribute is multiple sub-paths (split at top-level M/m); the **first** sub-path alone is already the complete, correct outer balloon silhouette (verified by rasterizing it in isolation — solid center pixel, 36-41% fill ratio, matching a normal teardrop-in-square ratio). Extracted that first sub-path verbatim (no redrawing) into four new `CI.poiCarLSil`/`poiCarSil`/`poiLSil`/`poiSSil` constants, fixed-color-filled (not `currentColor`), and `fahrzeugMarkerSVG()`/`tankstellenMarkerSVG()` now render it as the bottom layer behind the original detailed icon; `.fahrzeug-pin`'s color switched from dark navy to white so the original outline/detail lines read against the new solid fill, exactly like the white-glyph-on-solid-teardrop convention of Google/Apple Maps pins. Applies uniformly to both marker types now (station pin included, per explicit user request), so the earlier per-variant `.auto` CSS modifier and its circle-position math were removed again as dead weight. (2) **Battery voltage statistic cutoff lowered 13.2V → 12.8V** (`AGM_RUHE_MAX_V` in `audi-dashboard-app.js`) — user's point: anything above ~12.8V (full-charge resting voltage per the existing in-code research citation) is already alternator output, not the battery's own state, so admitting up to 13.2V let charging-tail values distort the charge-state stats. (3) **Battery chart Y-axis no longer "stretches."** `bvZeichnen()` recomputed `yMin`/`yMax` from whatever points were currently zoomed into (min 0.4V span + 15% pad) on every redraw, so the axis rescaled on every pan/zoom gesture. Now computed once from the full dataset in `vBatterieverlauf()` (`bvYDomain`) and reused unchanged across zoom/pan. (4) Removed two explanatory paragraphs from the Batteriespannung detail view ("Näherung auf Basis…" and "Ziehen zum Verschieben…") per explicit request. (5) **Tankfüllung now shows "X % / X l"** (`vAudi()`) instead of a literal-litre primary value with a redundant "100 % von 58 l" sub-line. (6) Removed the "· noch etwa X l im Tank" suffix from the Übersicht's "Reichweite" label (`vHome()`); the now-unused `liter` local was removed with it. (7) **Active/primary buttons are grey again, not red** — `.aktion.primaer` in `audi-dashboard-ios.css` filled with `--ios-tint` (red) since the 2026-08-13 iOS-overlay decision; explicit user request to drop that and match the base stylesheet's neutral `var(--fg)`/`var(--canvas)` fill. Destructive actions (`.aktion.loeschen`) intentionally stay red — Apple's own HIG reserves red specifically for destructive actions, so this one use is correct per the guideline, not just left over. (8) **`` popups now get a `color-scheme` hint.** The closed ``'s *opened* option list is rendered by the browser/OS as a separate surface outside the page's render tree entirely — confirmed firsthand when a screenshot tool call actually hung for 30s trying to capture it, and even a real click-to-open attempt afterward produced no visible change in any screenshot. `color-scheme` only gets partial credit there; the reliable lever is that Chrome/Firefox/Edge *do* honor explicit `background-color`/`color` set directly on `