d889353cbe
TERMIN. Der Werkstatttermin wanderte bisher ins Fahrzeugprofil, stand als
Merkmal an der Karte und liess sich "verwerfen" - eine zweite Verwaltung neben
dem Kalender. Auf Ansage entfernt: er dient jetzt allein dem Kalendereintrag
"was, wann, bei welcher Werkstatt". Dabei aufgefallen: die App schrieb gar kein
LOCATION in die .ics, obwohl das Panel es seit jeher setzt.
Ohne Speicherung startete das Feld leer ("tt.mm.jjjj", so gemeldet). Es
schlaegt jetzt die Faelligkeit der gewaehlten Art vor, sonst heute - dafuer
liefert service.ts serviceKandidaten(). "Andere" ergaenzt die Auswahl.
BREITEN. Am laufenden Panel gemessen: die vier festen 132px-Datumsfelder
brauchen ab 140px, damit die letzte Ziffer nicht unter dem Kalendersymbol
liegt (jetzt 150px); die Art-Auswahl braucht 178px fuer
"Hauptuntersuchung" (jetzt 190px). In der App war nichts davon kaputt - eine
dort vorsorglich eingebaute Breite ist wieder entfernt.
LISTEN. Fahrten- und Tankliste verloren 58px an tote Breite: 22px Einrueckung
der Monatsebene plus 40px linkes Polster der Zeile. Auf dem Telefon brach
"Eichstaett -> Adelschlag" deshalb um. Apples Richtlinien gruppieren ueber
Abschnittskoepfe statt Einrueckung; die Zeilen stehen jetzt alle auf derselben
Kante (34 statt 96px), der Monat traegt Versalien und Sperrung und liest sich
als Kopf. Beschraenkt auf .liste/.dm-liste - anderswo ist das Polster gewollt.
BILDER. Die Draufsicht ist aus der Galerie genommen (nurStatus), gehoert aber
weiter in die Auswahl der Einstellungen: nur dort laesst sie sich hochladen.
Die Galerie schneidet Fotos nicht mehr an - die Frontansicht verlor unten die
Stossstange.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1546 lines
71 KiB
CSS
1546 lines
71 KiB
CSS
/* audi-dashboard.css — Stil des Panels, aufgebaut auf den Tokens und
|
||
Komponenten des Design-Systems AudiDashUI (_ds_bundle.css). Klassennamen
|
||
wie im Prototyp/Panel (unpräfixiert), Werte 1:1 aus dem Design-System. */
|
||
|
||
:host {
|
||
--red: #F50537;
|
||
--r-tile: 20px;
|
||
--r-pill: 999px;
|
||
--r-func: 8px;
|
||
--rahmen-hg: #111;
|
||
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 22px;
|
||
--canvas: #161b23;
|
||
--tile: #1f2733;
|
||
--tile-2: #2a3341;
|
||
--line: rgba(255,255,255,.10);
|
||
--line-strong: rgba(255,255,255,.20);
|
||
--fg: #FFFFFF;
|
||
--fg2: #9aa1ad;
|
||
/* A2: von #657081 (3,0:1 auf --tile) auf 4,6:1 angehoben. Unterhalb von
|
||
12 px wird diese Farbe nicht mehr verwendet. */
|
||
--fg3: #8a94a3;
|
||
--ok: #15da15;
|
||
--warn: #ffaa00;
|
||
--bad: #fd2c4e;
|
||
--shade: rgba(255,255,255,.05);
|
||
--font-stack: "Audi Type", Helvetica, Arial, sans-serif;
|
||
/* Bugfix: in der echten HA-Einbettung liefert partial-panel-resolver
|
||
(display:inline, height:auto) keine definierte Höhe - eine
|
||
height:100%-Kette von hier nach oben rechnet also gegen eine
|
||
Auto-Box und kollabiert auf die Inhaltshöhe (nur Topbar+Tabbar
|
||
sichtbar, main#view ohne Fläche zum Scrollen). Deshalb wie im
|
||
ursprünglichen Prototyp direkt an der Viewport-Höhe verankert,
|
||
unabhängig von der Elternkette - und zentriert wie eine
|
||
Telefon-Karte, statt randlos zu füllen. */
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100vh;
|
||
height: 100dvh;
|
||
overflow: hidden;
|
||
}
|
||
:host([data-theme=tag]) {
|
||
--canvas: #FFFFFF;
|
||
--tile: #f2f2f2;
|
||
--tile-2: #e5e5e5;
|
||
--line: rgba(0,0,0,.10);
|
||
--line-strong: rgba(0,0,0,.22);
|
||
--fg: #000000;
|
||
--fg2: #4c4c4c;
|
||
--fg3: #666666;
|
||
--ok: #0DA20D;
|
||
--warn: #ffaa00;
|
||
--bad: #eb0d3f;
|
||
--shade: rgba(0,0,0,.04);
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
|
||
/* Sichtbarer Tastatur-Fokus. Bis hierher gab es .feld input:focus als einzige
|
||
Fokus-Regel im ganzen Blatt - alle Knöpfe, Kacheln und Tabs waren zwar
|
||
fokussierbar, aber ohne jede sichtbare Markierung. Auf dem Telefon fällt das
|
||
nicht auf, per Bluetooth-Tastatur (iPadOS/Desktop über Tailscale) oder mit
|
||
iOS-Vollzugriff per Tastatur ist die App dadurch praktisch unbedienbar.
|
||
:focus-visible statt :focus, damit Maus- und Tipp-Bedienung unverändert
|
||
ohne Rahmen bleibt - der Browser entscheidet, was "echte" Tastaturnavigation
|
||
ist. */
|
||
:focus-visible {
|
||
outline: 2px solid var(--red);
|
||
outline-offset: 2px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* Zwischen-Wrapper, den _aufbauen() in den Shadow Root haengt - bekommt hier
|
||
die einzige "echte" (viewport-verankerte) Höhe der ganzen Kette; .phone
|
||
darunter kann sich dann gefahrlos mit height:100% darauf beziehen. */
|
||
:host > div {
|
||
width: 100%;
|
||
max-width: 412px;
|
||
height: min(880px, calc(100vh - 24px));
|
||
height: min(880px, calc(100dvh - 24px));
|
||
min-height: 0;
|
||
/* Verhindert, dass eine Zieh-Geste, die main#view unten nach oben hin
|
||
verlaesst, an die dahinterliegende HA-Seite weitergereicht wird - sonst
|
||
kann der Browser selbst ein natives Overscroll/Pull-to-Refresh starten
|
||
und der eigene Ziehen-zum-Aktualisieren-Indikator bekommt die Geste nie
|
||
zu Ende. */
|
||
overscroll-behavior: none;
|
||
}
|
||
|
||
/* ------------------------------------------------------------- Gerüst */
|
||
.phone {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
min-height: 0;
|
||
/* B4: Bezugsrahmen für Action Sheet und Popups. */
|
||
position: relative;
|
||
background: var(--canvas);
|
||
color: var(--fg);
|
||
font-family: var(--font-stack);
|
||
font-variant-numeric: tabular-nums;
|
||
overflow: hidden;
|
||
/* B2: kein graues Safari-Rechteck beim Tippen - Rückmeldung kommt aus den
|
||
:active-Regeln unten. */
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.topbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
/* B1: oben Platz für Statusbalken/Dynamic Island. */
|
||
padding: max(22px, calc(env(safe-area-inset-top) + 10px)) 20px 14px;
|
||
flex: 0 0 auto;
|
||
background: var(--canvas);
|
||
}
|
||
.back {
|
||
/* B6: 44 px Trefferfläche. */
|
||
width: 44px; height: 44px;
|
||
margin-left: -5px;
|
||
flex: 0 0 auto;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
border: none; border-radius: 50%;
|
||
background: none;
|
||
color: var(--fg3);
|
||
cursor: pointer;
|
||
padding: 0;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
.back.on { opacity: 1; pointer-events: auto; }
|
||
.back:hover { background: var(--tile-2); color: var(--fg); }
|
||
.back:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
|
||
.head { flex: 1; min-width: 0; }
|
||
.eyebrow {
|
||
font-size: 11px;
|
||
letter-spacing: .18em;
|
||
text-transform: uppercase;
|
||
color: var(--fg3);
|
||
min-height: 12px;
|
||
}
|
||
.title {
|
||
font-size: 23px;
|
||
letter-spacing: -.015em;
|
||
line-height: 1.15;
|
||
color: var(--fg);
|
||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||
}
|
||
.sync {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-top: 4px;
|
||
font-size: 11px;
|
||
color: var(--fg3);
|
||
white-space: nowrap;
|
||
}
|
||
.themebtn, .rings {
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
border: none; background: none; color: var(--fg3);
|
||
cursor: pointer; padding: 0; flex: 0 0 auto;
|
||
}
|
||
.themebtn { width: 44px; height: 44px; border-radius: 50%; }
|
||
/* A6: Die Topbar-Taste ist entfallen - die Wahl liegt in den Einstellungen und
|
||
folgt sonst der Systemeinstellung. display:inline-flex oben würde das
|
||
hidden-Attribut sonst überstimmen. */
|
||
.themebtn[hidden] { display: none; }
|
||
.themebtn:hover { background: var(--tile-2); color: var(--fg); }
|
||
.themebtn:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
|
||
.themebtn svg { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; }
|
||
/* Wie .themebtn (44px Trefferflaeche), aber ohne die Kontur-Stroke-Regel -
|
||
fuer CI-Symbole (ciSVG()), deren Pfade ihr eigenes fill="currentColor"
|
||
mitbringen (z. B. list-s im Kopf der Batteriespannungs-Kachel). */
|
||
.iconbtn {
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
width: 44px; height: 44px; border-radius: 50%;
|
||
border: none; background: none; color: var(--fg3);
|
||
cursor: pointer; padding: 0; flex: 0 0 auto;
|
||
}
|
||
.iconbtn:hover { background: var(--tile-2); color: var(--fg); }
|
||
.iconbtn:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
|
||
/* "Neue Räder anlegen" auf der Reifen-Seite: bewusst dauerhaft mit
|
||
sichtbarem Hintergrund statt nur bei Hover/Tap wie .iconbtn sonst - sitzt
|
||
direkt neben dem gefüllten "Montiert"-Knopf und wirkt ohne eigene Fläche
|
||
zu unscheinbar, um dort als eigenständiger Knopf erkannt zu werden. */
|
||
.archivbtn { background: var(--tile-2); color: var(--fg2); }
|
||
.archivbtn:hover, .archivbtn:active { color: var(--fg); }
|
||
/* D4 war urspruenglich: die Ringe sind Marke, kein Knopf. Seit die
|
||
iOS-Auflage in Telefonbreite das Zahnrad ausblendet, sind sie dort aber der
|
||
einzige Zugang zu den Einstellungen - und als reines div mit
|
||
pointer-events:none war dieser Zugang fuer Tastatur und Sprachausgabe gar
|
||
nicht erreichbar. Sie sind deshalb jetzt ein echter Knopf, in jeder Breite
|
||
bedienbar. Auf grossen Bildschirmen gibt es damit zwei Wege in die
|
||
Einstellungen (Ringe und Zahnrad) - unschaedlich, und der Zugang haengt
|
||
nicht mehr davon ab, ob audi-dashboard-ios.css geladen wurde. */
|
||
/* Design-Audit 2026-08-13: in Telefonbreite ist dies der einzige Zugang zu
|
||
den Einstellungen (siehe Kommentar oben), war mit 42px aber deutlich
|
||
kleiner als jeder andere Symbol-Knopf in der Kopfzeile (.themebtn/
|
||
.profilbtn/.back sind alle 44px) und lag damit unter der ueblichen
|
||
44px-Mindestgroesse fuer Tippflaechen. */
|
||
.rings { width: 58px; height: 32px; color: var(--fg); }
|
||
.rings svg { width: 58px; height: auto; }
|
||
|
||
/* Der Markenklon in der Seitenleiste wird vom Skript unbedingt in die
|
||
Menueleiste gehaengt; sichtbar macht ihn erst die iOS-Auflage ab 860px.
|
||
Das Verstecken gehoert deshalb hierher: laedt die Auflage nicht (404,
|
||
Cache), waere er sonst ein sechstes Element im fuenfspaltigen Raster und
|
||
die Menueleiste zerfiele. */
|
||
.navmarke { display: none; }
|
||
.profilbtn {
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
width: 44px; height: 44px; border-radius: 50%;
|
||
border: none; background: var(--tile); color: var(--fg2);
|
||
cursor: pointer; padding: 0; flex: 0 0 auto;
|
||
}
|
||
/* Flaeche statt Kontur, seit das Zahnrad settings-s aus dem CI-Satz ist
|
||
(02.09.2026): der Pfad bringt sein eigenes fill="currentColor" mit, und ein
|
||
geerbter stroke wuerde zusaetzlich um jede Flaeche gezeichnet - dieselbe
|
||
Falle wie bei .voll/RahmenFlaeche in der App. */
|
||
.profilbtn svg { width: 19px; height: 19px; fill: currentColor; stroke: none; }
|
||
.profilbtn:hover { color: var(--fg); }
|
||
.profilbtn:active { background: var(--tile-2); color: var(--fg); transform: scale(.94); }
|
||
|
||
.ptr {
|
||
height: 0;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
color: var(--fg3);
|
||
font-size: 12.5px;
|
||
transition: height .2s;
|
||
flex: 0 0 auto;
|
||
}
|
||
.ptr.spin svg { animation: drehen 1s linear infinite; }
|
||
@keyframes drehen { to { transform: rotate(360deg); } }
|
||
|
||
main#view {
|
||
/* Der seitliche Rand steht als Variable, weil randlose Elemente ihn per
|
||
negativem Rand ausgleichen muessen (siehe .standort-vollbild). Wer das
|
||
Padding aendert, ohne die Variable zu benutzen, verschiebt die randlosen
|
||
Elemente unbemerkt. */
|
||
--seitenrand: 20px;
|
||
flex: 1 1 0;
|
||
min-height: 0;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
padding: 10px var(--seitenrand) 34px;
|
||
scroll-behavior: smooth;
|
||
-webkit-overflow-scrolling: touch;
|
||
scrollbar-width: none;
|
||
/* Ohne das hier greift auf echten Touch-Geraeten oft das native
|
||
Overscroll/Pull-to-Refresh des Browsers, sobald am oberen Rand weiter
|
||
nach unten gezogen wird - das nimmt der eigenen "Ziehen zum
|
||
Aktualisieren"-Geste (siehe ptrY0-Logik in ereignisseVerdrahten()) die
|
||
Kontrolle weg, obwohl deren JS/CSS für sich genommen korrekt arbeitet. */
|
||
overscroll-behavior-y: contain;
|
||
}
|
||
main#view::-webkit-scrollbar { width: 0; height: 0; }
|
||
|
||
#overlay:empty { display: none; }
|
||
|
||
.tabbar {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
border-top: 1px solid var(--line);
|
||
flex: 0 0 auto;
|
||
background: var(--canvas);
|
||
/* B1: Luft über dem Home-Indikator. */
|
||
padding-bottom: max(6px, env(safe-area-inset-bottom));
|
||
}
|
||
/* B5: kein roter Oberkantenstrich (Desktop-Muster), sondern eine gefüllte
|
||
Pille um das aktive Symbol - wie in der myAudi-App. */
|
||
.tab {
|
||
display: flex; flex-direction: column; align-items: center; gap: 7px;
|
||
padding: 12px 0 10px;
|
||
background: none; border: none;
|
||
color: var(--fg3);
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
transition: color .15s;
|
||
}
|
||
.tab.on { color: var(--fg); }
|
||
/* Die Pille ist ein eigenes Feld um das Symbol - so bleibt ihre Groesse
|
||
unabhaengig vom Symbol und der Wechsel laeuft sauber ueber background. */
|
||
.tabpille {
|
||
display: flex; align-items: center; justify-content: center;
|
||
width: 60px; height: 34px; border-radius: var(--r-pill);
|
||
transition: background .18s;
|
||
}
|
||
.tab.on .tabpille { background: var(--tile-2); }
|
||
.tab svg {
|
||
fill: none; stroke: currentColor; stroke-width: 1.6;
|
||
stroke-linecap: round; stroke-linejoin: round;
|
||
transition: transform .18s;
|
||
}
|
||
.tab svg.voll { fill: currentColor; stroke: none; }
|
||
.tab:active svg { transform: scale(.9); }
|
||
.tab span { font-size: 11px; letter-spacing: .01em; }
|
||
/* :not(.tabpille) - die Icon-Huelle ist selbst ein <span>, ein blosses
|
||
".tab span" hier hiess frueher auch der Pille (samt Icon) display:none.
|
||
Ueberschrieben von derselben Regel in audi-dashboard-ios.css (dieselbe
|
||
Spezifitaet, spaeter geladen), aber auch hier falsch und daher fixiert. */
|
||
.tabbar.ohne .tab span:not(.tabpille) { display: none; }
|
||
.tabbar.ohne .tab { padding: 14px 0 12px; }
|
||
.tabbar.ohne .tab svg { width: 26px; height: 26px; }
|
||
|
||
/* -------------------------------------------------------------- Kacheln */
|
||
.tile {
|
||
background: var(--tile);
|
||
border-radius: var(--r-tile);
|
||
padding: var(--sp-5);
|
||
margin-bottom: var(--sp-4);
|
||
position: relative;
|
||
}
|
||
.tile.flat { background: none; padding: 0; }
|
||
button.tile, .tilebtn {
|
||
display: block;
|
||
width: 100%;
|
||
border: none;
|
||
font-family: inherit;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
color: inherit;
|
||
}
|
||
button.tile:hover { background: var(--tile-2); }
|
||
button.tile:active, .tilebtn:active { background: var(--tile-2); transform: scale(.99); }
|
||
button.tile, .tilebtn { transition: background .15s, transform .1s; }
|
||
.go {
|
||
position: absolute;
|
||
top: var(--sp-5); right: var(--sp-5);
|
||
width: 6px; height: 10px;
|
||
color: var(--fg3);
|
||
fill: none; stroke: currentColor; stroke-width: 1.4;
|
||
}
|
||
.zahnrad {
|
||
position: absolute;
|
||
top: 8px; right: 8px;
|
||
width: 44px; height: 44px;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
border: none; border-radius: 50%;
|
||
background: none; color: var(--fg3);
|
||
cursor: pointer; padding: 0;
|
||
}
|
||
.zahnrad:hover { background: var(--tile-2); color: var(--fg); }
|
||
.zahnrad:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
|
||
.zahnrad svg { width: 17px; height: 17px; }
|
||
/* Titelzeile nie unter dem Zahnrad/Chevron hindurchlaufen lassen - und die
|
||
Zeile hoch genug für die 44px-Trefferfläche des Zahnrads machen. Ohne die
|
||
Mindesthöhe ragte es in den Kachelinhalt und lag auf dem ersten Knopf
|
||
(Versicherung, "Inland · <Nummer>"; gemeldet 02.09.2026). Gleiche Regel in
|
||
der App (.dm-kachel--zahnrad). */
|
||
.tile:has(> .zahnrad) > .label:first-child,
|
||
.tile:has(> .go) > .label:first-child { padding-right: 48px; }
|
||
.tile:has(> .zahnrad) > .label:first-child { min-height: 34px; }
|
||
|
||
/* C1: Abschnittstitel gemischt geschrieben, 14 px, ohne Sperrung - wie in der
|
||
aktuellen myAudi-App. Versalien trägt nur noch .eyebrow über dem Seitentitel. */
|
||
.label {
|
||
font-size: 14px;
|
||
letter-spacing: normal;
|
||
color: var(--fg2);
|
||
display: block;
|
||
line-height: 1.3;
|
||
}
|
||
/* HIG "Loading": statt reinem Text ein sichtbar laufender Indikator, solange
|
||
DATEN_GELADEN false ist - hilft auch, "lädt noch" optisch von "hängt fest"
|
||
zu unterscheiden (dieser Bildschirm hatte schon ein echtes Hänger-Problem). */
|
||
.lade-spinner {
|
||
width: 16px; height: 16px; flex: none;
|
||
border: 2px solid var(--line-strong);
|
||
border-top-color: var(--fg2);
|
||
border-radius: 50%;
|
||
animation: lade-spin .8s linear infinite;
|
||
}
|
||
@keyframes lade-spin { to { transform: rotate(360deg); } }
|
||
/* Echte Überschrift innerhalb einer Kachel (z.B. "Sommerräder"/"Winterräder")
|
||
statt des sonst üblichen kleinen, gedämpften .label-Abschnittstitels -
|
||
selbe Typografie-Stufe wie .title (Seitentitel), nur etwas kleiner, weil
|
||
sie innerhalb einer Kachel steht statt in der Kopfzeile. */
|
||
.tile-headline {
|
||
font-size: 20px;
|
||
letter-spacing: -.01em;
|
||
line-height: 1.2;
|
||
color: var(--fg);
|
||
display: block;
|
||
}
|
||
/* Icon + Text statt reinem Fließtext-Label (z.B. Batterie-Symbol vor
|
||
"Ruhespannung") - dieselbe Zeilen-Geometrie wie .sync in der Kopfzeile. */
|
||
.bv-skala-kopf { display: flex; align-items: center; gap: 6px; }
|
||
|
||
/* A4: Messwerte erben nie mehr das uppercase der Label-Klasse. */
|
||
.wert {
|
||
font-size: 14px;
|
||
letter-spacing: normal;
|
||
text-transform: none;
|
||
color: var(--fg2);
|
||
}
|
||
.fig {
|
||
font-family: "Audi Type Wide", var(--font-stack);
|
||
font-weight: 300;
|
||
letter-spacing: -.02em;
|
||
line-height: .95;
|
||
color: var(--fg);
|
||
}
|
||
.fig .unit {
|
||
/* A3: relativ zur Zahl, aber nie unter der Lesbarkeitsgrenze - bei einer
|
||
29-px-Zahl ergäbe .32em nur 9,3 px. */
|
||
font-size: max(11px, .32em);
|
||
font-weight: 400;
|
||
letter-spacing: normal;
|
||
color: var(--fg2);
|
||
margin-left: .32em;
|
||
}
|
||
.werte { font-size: 14px; color: var(--fg); display: block; }
|
||
|
||
.rows { margin: 0; }
|
||
.row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
gap: var(--sp-4);
|
||
padding: 15px 0;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
.row dt { font-size: 13px; color: var(--fg2); line-height: 1.4; }
|
||
.row dd { margin: 0; font-size: 14px; color: var(--fg); text-align: right; }
|
||
.row dd small { display: block; font-size: 12.5px; color: var(--fg3); margin-top: 3px; }
|
||
.rows-fest dd { min-width: 120px; }
|
||
/* .row-fussnote ist mit dem Umbau der Service-Seite auf .termingruppe
|
||
entfallen (2026-08-18) - dort gibt es keine dt/dd-Spalten mehr, die um
|
||
Platz konkurrieren. Die beiden Regeln dafuer standen seither ohne
|
||
passendes Markup da. */
|
||
/* Sammelzeilen der Sicherheit-Ansicht (vSicherheit()): Kreis-Häkchen + Text
|
||
+ optionaler Chevron statt dt/dd. Erbt Abstand/Trennlinie von .row, ersetzt
|
||
aber dessen space-between-Layout und setzt die Button-Vorgaben zurück, wenn
|
||
die Zeile klickbar ist (Türen und Klappen). */
|
||
.sicherheitszeile {
|
||
justify-content: flex-start;
|
||
width: 100%;
|
||
background: none;
|
||
border: none;
|
||
border-bottom: 1px solid var(--line);
|
||
font: inherit;
|
||
color: inherit;
|
||
text-align: left;
|
||
cursor: default;
|
||
}
|
||
button.sicherheitszeile { cursor: pointer; }
|
||
button.sicherheitszeile:active { background: var(--tile-2); }
|
||
.sicherheitsliste .sicherheitszeile:last-child { border-bottom: 0; }
|
||
.sicherheitszeile-text { flex: 1; font-size: 14px; color: var(--fg); }
|
||
.sicherheitszeile .chev { margin-left: auto; }
|
||
/* Wert als Kommentar statt als Angabe - fuer Zeilen, deren rechte Seite nur
|
||
beschreibt, was hinter der Kachel liegt (Mein Audi, "Details"). Groesse
|
||
und Farbe wie jede andere Nebenzeile. */
|
||
.row dd.row-hinweis { font-size: 12.5px; color: var(--fg3); }
|
||
|
||
|
||
.leaf {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
gap: var(--sp-4);
|
||
width: 100%;
|
||
background: none; border: none;
|
||
font-family: inherit; text-align: left;
|
||
padding: 14px 0 14px 40px;
|
||
min-height: 56px;
|
||
border-bottom: 1px solid var(--line);
|
||
color: inherit;
|
||
}
|
||
/* Das Label nimmt den freien Platz und kuerzt bei Bedarf mit Auslassung.
|
||
|
||
Ohne flex:1 blieb in der Zeile freier Raum, und weil sie DREI Kinder hat
|
||
(Label, Werte, Pfeil), verteilte space-between ihn ZWISCHEN alle drei - die
|
||
Wertespalte stand damit in der Mitte, ihre Lage abhaengig von der Laenge des
|
||
Ortsnamens. Gemessen: rechte Kante bei 549px in der einen Zeile, 520px in
|
||
der naechsten. Mit flex:1 bleibt kein freier Raum uebrig, Werte und Pfeil
|
||
sitzen buendig rechts.
|
||
|
||
min-width:0 ist noetig, damit ein Flex-Kind ueberhaupt unter seine
|
||
Inhaltsbreite schrumpfen darf - ohne das greift text-overflow nie. */
|
||
.leaf .k { font-size: 14px; color: var(--fg); display: flex; flex-direction: column; gap: 3px; line-height: 1.35; flex: 1 1 auto; min-width: 0; }
|
||
.leaf .k > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.leaf .k small { font-size: 12.5px; color: var(--fg3); }
|
||
/* Feste Mindestbreite, damit Strecke und Verbrauch ueber alle Zeilen hinweg
|
||
in einer Spalte stehen. Ohne sie waechst der Kasten mit dem Inhalt: gemessen
|
||
86px bei "11,4 l/100 km" gegen 37px bei einer Zeile ohne Werte, und die
|
||
Zahlen standen entsprechend versetzt. 92px traegt die laengste Form
|
||
("11,4 l/100 km") ohne Umbruch. */
|
||
.leaf .v { font-size: 13px; color: var(--fg2); text-align: right; display: flex; flex-direction: column; gap: 3px; flex: 0 0 92px; }
|
||
.leaf .v small { font-size: 12.5px; color: var(--fg3); }
|
||
/* .teaser (die "Zuletzt"-Kachel auf der Übersicht, teaser()): genau zwei
|
||
Zeilen mit sehr unterschiedlich breiten Werten ("offen" vs. "51,59 l") -
|
||
.v ist sonst flex:0 0 auto und damit genau so breit wie sein eigener
|
||
Inhalt, wodurch die linke Kante des Wertes je Zeile unterschiedlich weit
|
||
rechts beginnt (rechte Kante bleibt an der Chevron-Spalte gleich, das
|
||
täuscht Ausrichtung nur vor). min-width sorgt für eine gemeinsame
|
||
Spaltenbreite, damit beide Werte auf derselben linken Kante beginnen.
|
||
Das allein reicht aber nicht: .leaf selbst verteilt seine drei Kinder
|
||
(.k/.v/Chevron) per justify-content:space-between, und .k hat kein
|
||
flex-grow - unterschiedlich lange Beschriftungen ("→" vs. "Hermann Mogler
|
||
Mineralölg. GmbH") verschieben dadurch die GESAMTE restliche Zeile
|
||
(.v + Chevron) um die Differenz, obwohl .v selbst jetzt gleich breit ist.
|
||
.k bekommt deshalb flex:1, damit es den verbleibenden Platz füllt und .v
|
||
dadurch wirklich immer am selben Abstand vom rechten Rand sitzt. */
|
||
.teaser .leaf .k { flex: 1 1 auto; min-width: 0; }
|
||
.teaser .leaf .v { min-width: 72px; }
|
||
/* cursor/:active nur für echte .leaf-Buttons (data-go, navigieren bei Klick)
|
||
- die Batterie-Messwertliste (vBatterieliste()) ist bewusst ein reines
|
||
<div class="leaf">, keine Zeilennavigation, nur Wischen zum Löschen; sie
|
||
soll deshalb weder den Hand-Cursor noch die Tipp-Rückmeldung zeigen, die
|
||
sonst "hier passiert beim Klick etwas" versprechen. */
|
||
button.leaf { cursor: pointer; transition: background .15s; }
|
||
button.leaf:active { background: var(--tile-2); }
|
||
.chev { width: 6px; height: 10px; color: var(--fg3); flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.4; }
|
||
|
||
/* ------------------------------------------------------------- Status */
|
||
.status {
|
||
display: flex; align-items: center; gap: var(--sp-3);
|
||
width: 100%;
|
||
background: none; border: none; padding: 16px 0 0;
|
||
min-height: 44px;
|
||
text-align: left; font-family: inherit; cursor: pointer;
|
||
color: inherit;
|
||
}
|
||
.dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; background: var(--ok); display: inline-block; }
|
||
.dot.warn { background: var(--warn); }
|
||
.dot.bad { background: var(--bad); }
|
||
/* Kreis-Häkchen statt einfarbigem Punkt - für die Sicherheit-Ansicht
|
||
(vSicherheit()/vTuerenKlappen()), nach Nutzer-Vorlage: erfüllt/offen/
|
||
unbekannt sofort am Symbol erkennbar, nicht nur an der Farbe. */
|
||
.statuskreis { width: 22px; height: 22px; border-radius: 50%; flex: 0 0 auto; background: var(--ok); display: inline-flex; align-items: center; justify-content: center; }
|
||
.statuskreis.warn { background: var(--warn); }
|
||
.statuskreis.bad { background: var(--bad); }
|
||
.status .t { font-size: 14px; color: var(--fg); }
|
||
.status { transition: opacity .15s; }
|
||
.status:active { opacity: .55; }
|
||
|
||
/* -------------------------------------------------------------- Übersicht */
|
||
.tankzeile { display: flex; align-items: flex-end; justify-content: space-between; gap: 14px; margin-bottom: 14px; }
|
||
.pct { font-size: 15px; color: var(--fg2); padding-bottom: 6px; }
|
||
.balken { height: 4px; border-radius: var(--r-pill); background: var(--line); overflow: hidden; }
|
||
.balken i { display: block; height: 100%; background: var(--fg); border-radius: var(--r-pill); }
|
||
/* Der Außenabstand sitzt am Raster, nicht an den Kacheln - sonst wird er im
|
||
2×2-Raster (vHome) zum zusätzlichen internen Zeilenabstand und der Abstand
|
||
innerhalb der Gruppe wäre größer als der zur nächsten. */
|
||
/* Übersicht: "Nächster Service" als tippbare Kennzahl, darunter der
|
||
Kilometerstand als Wertezeile (siehe Kommentar in vHome()). Der Knopf sitzt
|
||
INNERHALB einer normalen .tile - nicht die Kachel selbst ist der Knopf,
|
||
sondern nur ihr oberer Teil, damit die Kilometerstand-Zeile erkennbar reine
|
||
Anzeige bleibt. Der Chevron steht deshalb auf right:0 relativ zum Knopf und
|
||
landet damit genau auf demselben Innenabstand wie ein normales .go in einer
|
||
gepolsterten Kachel. */
|
||
.serviceblock {
|
||
position: relative;
|
||
display: block; width: 100%;
|
||
border: 0; background: none; padding: 0;
|
||
font-family: inherit; text-align: left; color: inherit; cursor: pointer;
|
||
}
|
||
.serviceblock > .go { top: 3px; right: 0; }
|
||
/* Rückmeldung beim Tippen: ein Hintergrundwechsel wie bei .tilebtn ginge hier
|
||
nicht, der Knopf liegt ja schon auf der Kachelfläche. */
|
||
.serviceblock:active { opacity: .55; }
|
||
/* Beschriftung nie unter dem Chevron hindurchlaufen lassen */
|
||
.serviceblock > .label:first-child { padding-right: 24px; }
|
||
/* Serviceart-Icon links der Kennzahl statt des Wortes "bis zum/zur ...". */
|
||
.fig-zeile { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
|
||
.fig-zeile svg { color: var(--fg2); flex: 0 0 auto; }
|
||
.servicezeile {
|
||
display: flex; align-items: baseline; justify-content: space-between;
|
||
gap: var(--sp-4);
|
||
border-top: 1px solid var(--line);
|
||
margin-top: 14px; padding-top: 13px;
|
||
}
|
||
|
||
/* Anstehende Termine (vService): drei sichtbar getrennte Bloecke statt einer
|
||
dt/dd-Liste - Apple HIG ("essential information... don't obscure it by
|
||
crowding it with nonessential details") plus Audi-CI (Flaeche mit Haarlinien
|
||
statt Karten, eigene Groessenklasse fuer Zahlen). Trennung ueber Abstand und
|
||
eine Haarlinie zwischen den Bloecken, nicht ueber verschachtelte Kacheln.
|
||
Jeder Block: Kopfzeile (Icon + Art-Name, optional ein Chip fuer einen
|
||
vereinbarten Werkstatttermin), optionale Herstellervorgabe-Zeile, der Wert,
|
||
optional die eigene Prognose als Fusszeile. */
|
||
.termingruppe { padding: 18px 0; border-top: 1px solid var(--line); }
|
||
.termingruppe:first-of-type { padding-top: 2px; border-top: 0; }
|
||
.tg-kopf { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
||
.tg-name { display: flex; align-items: center; gap: 9px; min-width: 0; }
|
||
.tg-name svg { color: var(--fg2); flex: 0 0 auto; }
|
||
.tg-name span { font-size: 16px; color: var(--fg); font-weight: 400; }
|
||
/* Chip statt rotem Fliesstext fuer einen vereinbarten Werkstatttermin - Rot
|
||
bleibt Akzent (Text/Rahmen), nie Flaeche, wie im Rest der App. */
|
||
.tg-chip {
|
||
font-size: 10.5px; color: var(--red);
|
||
border: 1px solid var(--red); border-radius: var(--r-pill);
|
||
padding: 3px 8px; white-space: nowrap; flex: 0 0 auto;
|
||
}
|
||
.tg-vorgabe { font-size: 12.5px; color: var(--fg2); margin-top: 8px; }
|
||
.tg-wert {
|
||
font-family: "Audi Type Wide", var(--font-stack); font-weight: 300;
|
||
font-size: 22px; color: var(--fg); letter-spacing: -.005em; margin-top: 4px;
|
||
}
|
||
.tg-wert.leer { font-family: var(--font-stack); font-weight: 400; font-size: 15px; color: var(--fg3); margin-top: 8px; }
|
||
.tg-prognose { font-size: 12.5px; color: var(--fg2); margin-top: 6px; }
|
||
|
||
.badge { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||
.badge img { height: 18px; width: auto; display: none; }
|
||
:host([data-theme=nacht]) .badge img.negative { display: block; }
|
||
:host([data-theme=tag]) .badge img.positive { display: block; }
|
||
.badge .zusatz { font-size: 13px; color: var(--fg2); }
|
||
|
||
.szene { position: relative; padding: 8px 0 18px; }
|
||
/* C5: randloses Fahrzeugbild - hebt die 20 px Seitenrand von main#view auf und
|
||
blendet nach unten in die Fläche aus. */
|
||
.szene.bleed { margin: -10px -20px 0; padding: 0 0 10px; }
|
||
.szene.bleed .carbox { padding: 0 8px; }
|
||
/* Der Ausblend-Verlauf und der Bodenschatten sind am 2026-08-31 entfallen.
|
||
Beide lagen ueber dem unteren Teil des Fotos, und der Eigentuemer hat
|
||
zweimal darum gebeten, das Fahrzeug unverdeckt zu sehen. Zurueckholen
|
||
waere ein linear-gradient auf .szene.bleed::before bzw. ein
|
||
radial-gradient auf .szene.strasse::after - beides steht in der
|
||
Versionsgeschichte. */
|
||
.carbox { position: relative; }
|
||
.carfix { width: 100%; aspect-ratio: 16 / 9; }
|
||
/* 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
|
||
(796x220 Kasten, 796x448 skaliertes Bild). Das Dach lag darin.
|
||
|
||
Statt das Seitenverhaeltnis 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. */
|
||
.bildbox.carfix.klein { aspect-ratio: auto; height: auto; }
|
||
img.carfix.klein { aspect-ratio: auto; height: auto; }
|
||
/* Reifenfoto ist oft eine breite Aufnahme; statt beide Ränder symmetrisch
|
||
wegzuschneiden (object-fit:cover-Standard), bleibt bewusst der rechte Rand
|
||
des Originalfotos erhalten - dort sitzt das Rad meist im Bild. */
|
||
.radbild { object-position: right center; }
|
||
/* .bildbox.radbild statt .radbild allein: .bildbox unten setzt width/aspect-
|
||
ratio auf derselben Spezifitaet (eine Klasse) und gewinnt als spaeter
|
||
notierte Regel sonst automatisch - die Box blieb dadurch bislang die volle
|
||
Kachelbreite im 4:3-Format statt eines echten 96x96-Quadrats. Rechtsbündig
|
||
sitzt es jetzt ueber .radkopf-row (display:flex; justify-content:space-
|
||
between), kein margin-left:auto mehr noetig. */
|
||
.bildbox.radbild { width: 96px; height: 96px; aspect-ratio: 1 / 1; }
|
||
|
||
.bildbox { position: relative; width: 100%; aspect-ratio: 4 / 3; border-radius: 12px; overflow: hidden; }
|
||
.bildbox img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
||
.bildbox .platzhalter { display: none; position: absolute; inset: 0; }
|
||
.bildbox.bild-fehlt img { display: none; }
|
||
.bildbox.bild-fehlt .platzhalter { display: flex; }
|
||
/* D1: Solange kein Foto vorliegt, bleibt die Bildfläche flach - vorher standen
|
||
260 px gestrichelte Leerfläche vor der Reichweite, der wichtigsten Zahl der
|
||
App. Mit Foto gilt wieder das volle Seitenverhältnis. */
|
||
.bildbox.bild-fehlt.carfix { aspect-ratio: auto; height: 132px; }
|
||
/* 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. */
|
||
.bildbox.bild-fehlt.carfix.klein { aspect-ratio: 16 / 8; height: auto; }
|
||
/* Die Draufsicht auf der Fahrzeugseite ist eine Beigabe, keine Szene: sie
|
||
steht ueber den Sicherheitszeilen und soll die Zeilen nicht nach unten
|
||
draengen. Statt randlos ueber die volle Breite (rund 374px auf einem
|
||
Telefon, mit 16:8 also 187px hoch) steht sie eingerueckt und mittig -
|
||
60% Breite, die Hoehe geht im selben Verhaeltnis mit auf rund 112px
|
||
zurueck. Auf Ansage des Eigentuemers am 02.09.2026 ("Draufsicht-Foto
|
||
Bereich ist zu gross"): schmaler, nicht flacher - flacher haette bei
|
||
einem Foto, das nicht sehr breit ist, oben und unten geschnitten. */
|
||
.szene.bleed.schmal { margin: 0; padding: 22px 0 26px; }
|
||
.szene.bleed.schmal .carbox { width: 60%; margin: 0 auto; padding: 0; }
|
||
|
||
.szene.bleed:has(.bild-fehlt)::before { display: none; }
|
||
.szene.bleed:has(.bild-fehlt) { margin-left: 0; margin-right: 0; }
|
||
.szene.bleed:has(.bild-fehlt) .carbox { padding: 0; }
|
||
|
||
/* A5/C5: kein gestrichelter Rahmen und kein Dateiname mehr - Motivname plus
|
||
Aufforderung, die Fläche ist tippbar. */
|
||
.platzhalter {
|
||
width: 100%; height: 100%;
|
||
flex-direction: column; align-items: center; justify-content: center; gap: 7px;
|
||
background: var(--shade);
|
||
border: 1px solid var(--line);
|
||
border-radius: inherit;
|
||
color: var(--fg3);
|
||
text-align: center;
|
||
padding: var(--sp-3);
|
||
}
|
||
.platzhalter-icon { width: 26px; height: 26px; color: var(--fg3); }
|
||
.platzhalter-label { font-size: 13px; letter-spacing: normal; color: var(--fg2); }
|
||
.platzhalter-aktion { font-size: 12.5px; color: var(--fg3); }
|
||
|
||
.dots { display: flex; gap: 6px; justify-content: center; margin-top: 12px; }
|
||
.dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--line-strong); }
|
||
.dots i.on { background: var(--red); }
|
||
|
||
.marke { display: inline-flex; align-items: center; gap: 6px; }
|
||
.marke-logo { display: inline-flex; align-items: center; }
|
||
/* 19,2px = 16px + 20%. Der Eigentuemer wollte das Markenlogo in JEDER
|
||
Darstellung um ein Fuenftel groesser; es steht ueberall neben Text der
|
||
Zeilenhoehe 16px, also hier eine Zahl statt mehrerer Sondergroessen. */
|
||
.marke-logo img { height: 19.2px; width: auto; }
|
||
.marke-logo .ph { display: none; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--fg3); }
|
||
.marke-logo.logo-fehlt img { display: none; }
|
||
.marke-logo.logo-fehlt .ph { display: inline; }
|
||
|
||
/* -------------------------------------------------------------- Formulare */
|
||
.feld {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
gap: var(--sp-4);
|
||
padding: 14px 0;
|
||
min-height: 56px;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
/* :not(.switch): der Schalter (Tanken/Einstellungen etc.) ist selbst ein
|
||
<label class="switch">, kein Textlabel - ohne den Ausschluss erbte er
|
||
dieselbe flex:1 1 auto-Regel und zog sich über die ganze Zeilenbreite. */
|
||
.feld label:not(.switch) { font-size: 14px; color: var(--fg2); flex: 1 1 auto; min-width: 0; line-height: 1.35; }
|
||
/* Eingaben nie breiter als die Kachel: die im Markup gesetzten festen
|
||
Breiten (width:132px) bleiben Obergrenze, schrumpfen aber mit. */
|
||
.feld input, .feld select, .notiz {
|
||
background: var(--tile-2);
|
||
border: 1px solid var(--line);
|
||
color: var(--fg);
|
||
font-family: inherit;
|
||
/* Muss >= 16px bleiben: Safari/WKWebView (und damit auch die HA-Companion-App
|
||
auf dem iPhone) zoomt die Seite beim Fokussieren automatisch hinein, wenn
|
||
das Feld kleiner als 16px gesetzt ist - der Nutzer muss dann nach jedem
|
||
Feld von Hand wieder herauszoomen. Vorher standen hier 15px. */
|
||
font-size: 16px;
|
||
padding: 11px 13px;
|
||
border-radius: 12px;
|
||
text-align: right;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
flex: 0 1 auto;
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
}
|
||
.feld input:focus, .feld select:focus, .notiz:focus { outline: none; border-color: var(--red); }
|
||
/* z. B. Fahrgestellnummer bei aktivem "automatisch"-Schalter: der Wert kommt
|
||
aus den zugeordneten Sensoren, Handeingabe ist erst nach dem Ausschalten
|
||
wieder möglich - das Feld soll das auf den ersten Blick zeigen. */
|
||
.feld input:disabled { opacity: .5; cursor: not-allowed; }
|
||
/* Label und Eingabe untereinander statt nebeneinander, die Eingabe über die
|
||
volle Zeilenbreite - für Felder, deren Wert typischerweise länger ist als
|
||
ein Kurztext (Kommentar bei Sommer-/Winterrädern, live wie archiviert).
|
||
Sonst gilt .feld input oben: die Eingabe wächst nie über ihre intrinsische
|
||
Breite, nur das Label tut das (flex:1 1 auto) - für einen Reifenkommentar
|
||
("Profiltiefe 5,1 mm bei der letzten Montage") reichte das nicht. */
|
||
.feld--breit { display: block; }
|
||
.feld--breit label { display: block; margin-bottom: 8px; }
|
||
.feld--breit input { width: 100%; text-align: left; }
|
||
/* Das AUFGEKLAPPTE <select>-Popup ist Browser-/OS-Chrome, kein Teil des
|
||
Seiten-DOMs - color-scheme (siehe :host weiter oben) beeinflusst es nur
|
||
teilweise, der Rest bleibt hell. Einzige zuverlaessige Stellschraube:
|
||
Chrome (u.a.) rendert die Zeilen im Popup tatsaechlich mit dem hier
|
||
gesetzten background-color/color statt Systemfarben - deckende Werte,
|
||
var(--tile-2) im Nacht-Theme ist transparent und wuerde durchscheinen. */
|
||
.feld select option { background-color: var(--canvas); color: var(--fg); }
|
||
/* Pull-down-Button-Optik (Apple HIG): einzelner Abwaerts-Chevron statt des
|
||
durch appearance:none oben entfernten nativen Browser-Pfeils. */
|
||
.feld select {
|
||
padding-right: 30px;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%238a94a3' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||
background-repeat: no-repeat;
|
||
background-position: right 11px center;
|
||
background-size: 10px 10px;
|
||
}
|
||
:host([data-theme=tag]) .feld select {
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5l3.5 3.5 3.5-3.5' fill='none' stroke='%23666666' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||
}
|
||
.feld > span { flex: 0 1 auto; min-width: 0; }
|
||
.mitEinheit { display: inline-flex; align-items: center; gap: var(--sp-2); max-width: 100%; }
|
||
.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; }
|
||
.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; }
|
||
.switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; min-width: 0; }
|
||
.switch .track {
|
||
position: absolute; inset: 0;
|
||
background: var(--tile-2);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-pill);
|
||
transition: background .18s;
|
||
pointer-events: none;
|
||
}
|
||
.switch .track::before {
|
||
content: ""; position: absolute; left: 2px; top: 1px;
|
||
width: 23px; height: 23px; background: #fff; border-radius: 50%;
|
||
transition: transform .18s;
|
||
}
|
||
/* A7: Rot bleibt Akzent und Destruktiv. Der aktive Schalter trägt --fg mit
|
||
dunklem Knopf. */
|
||
.switch input:checked + .track { background: var(--fg); border-color: var(--fg); }
|
||
.switch input:checked + .track::before { transform: translateX(19px); background: var(--canvas); }
|
||
|
||
/* C3: gemischt geschrieben, 15 px, 48 px hoch. .primaer ist gefüllt,
|
||
.aktion allein bleibt die Umriss-Sekundäraktion. */
|
||
.aktion {
|
||
display: block; width: 100%;
|
||
font-family: inherit; font-size: 15px;
|
||
letter-spacing: normal;
|
||
color: var(--fg);
|
||
background: none;
|
||
border: 1px solid var(--line-strong);
|
||
border-radius: var(--r-pill);
|
||
padding: 14px 18px;
|
||
min-height: 48px;
|
||
margin-top: var(--sp-4);
|
||
cursor: pointer;
|
||
transition: border-color .15s, background .15s, transform .1s, opacity .15s;
|
||
}
|
||
.aktion + .aktion, .aktion + label.aktion { margin-top: 10px; }
|
||
.aktion:hover { border-color: var(--line-strong); background: var(--shade); }
|
||
.aktion:active { transform: scale(.985); background: var(--shade); }
|
||
.aktion.primaer { background: var(--fg); border-color: var(--fg); color: var(--canvas); }
|
||
.aktion.primaer:hover { opacity: .9; background: var(--fg); }
|
||
.aktion.primaer:active { opacity: .8; background: var(--fg); }
|
||
.aktion.loeschen { color: var(--bad); border-color: var(--bad); background: none; }
|
||
.aktion.loeschen:hover { background: var(--bad); color: #fff; }
|
||
.aktion.loeschen:active { background: var(--bad); color: #fff; }
|
||
|
||
/* C4: eine Bahn mit wanderndem Knopf statt zwei getrennter Umrisspillen. */
|
||
.seg {
|
||
display: flex; gap: 0;
|
||
background: var(--tile-2);
|
||
border-radius: var(--r-pill);
|
||
padding: 3px;
|
||
}
|
||
.seg button {
|
||
flex: 1;
|
||
border: none;
|
||
background: none;
|
||
color: var(--fg2);
|
||
font-family: inherit;
|
||
font-size: 14px;
|
||
letter-spacing: normal;
|
||
padding: 11px 8px;
|
||
min-height: 44px;
|
||
border-radius: var(--r-pill);
|
||
cursor: pointer;
|
||
transition: background .18s, color .18s;
|
||
}
|
||
.seg button.on { background: var(--fg); color: var(--canvas); }
|
||
.seg button:active { opacity: .8; }
|
||
|
||
.pill {
|
||
display: inline-flex; align-items: center;
|
||
font-size: 12.5px; letter-spacing: normal;
|
||
color: var(--fg2);
|
||
background: none;
|
||
border: 1px solid var(--line-strong);
|
||
border-radius: var(--r-pill);
|
||
padding: 6px 12px;
|
||
}
|
||
|
||
/* ------------------------------------------------------------ Akkordeon */
|
||
.acc-h {
|
||
display: flex; align-items: center; gap: var(--sp-3);
|
||
width: 100%;
|
||
background: none; border: none;
|
||
font-family: inherit; text-align: left;
|
||
padding: 15px 0;
|
||
min-height: 56px;
|
||
cursor: pointer;
|
||
border-bottom: 1px solid var(--line);
|
||
color: inherit;
|
||
}
|
||
.acc-h .k { flex: 1; font-size: 15px; color: var(--fg); }
|
||
.acc-h .v { font-size: 12.5px; color: var(--fg2); text-align: right; white-space: nowrap; flex: 0 0 auto; }
|
||
/* Ohne eigene Regel erbt <small> den UA-Wert "smaller" (0,833x) - im
|
||
Akkordeonkopf sind das 10,4px und damit unter der 11px-Untergrenze, die
|
||
dieses Projekt aus den Apple-Richtlinien uebernommen hat. */
|
||
.acc-h .v small { font-size: 11px; }
|
||
.acc-h { transition: background .15s; }
|
||
.acc-h:active { background: var(--tile-2); }
|
||
.mark {
|
||
/* Design-Audit 2026-08-13: die Border-Ecken-Technik (zwei Kanten +
|
||
45deg-Drehung) braucht ein Quadrat, um einen sauberen Chevron zu
|
||
ergeben - bei 6x10px (nicht quadratisch, wie fuer die SVG-Chevrons
|
||
.chev/.go gedacht) sind die beiden Kantenlaengen ungleich lang und das
|
||
Ergebnis sah eher wie ein Haekchen als ein Pfeil aus. */
|
||
width: 8px; height: 8px;
|
||
border-right: 1.4px solid var(--fg3);
|
||
border-bottom: 1.4px solid var(--fg3);
|
||
transform: rotate(-45deg);
|
||
transition: transform .15s, border-color .15s;
|
||
flex: 0 0 auto;
|
||
}
|
||
/* A8: ein Chevron, eine Farbe - nur die Drehung zeigt den Zustand. */
|
||
.acc-h[aria-expanded=true] .mark { transform: rotate(45deg); }
|
||
/* Kachelkoepfe der Statistik verhalten sich wie Aufklapp-Zeilen. */
|
||
.stat-kopfzeile { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
|
||
.stat-kopfzeile .label { flex: 1; }
|
||
.tile-h[aria-expanded=true] .mark { transform: rotate(45deg); }
|
||
.acc-b { display: none; }
|
||
.acc-b.on { display: block; }
|
||
.lvl2 { margin-left: 22px; }
|
||
.lvl2 .acc-h .k { font-size: 13px; }
|
||
|
||
/* Fahrten- und Tankliste: Abschnittsköpfe statt Einrückung.
|
||
|
||
Gemessen am 02.09.2026 im laufenden Panel: von der Innenkante der
|
||
Kachel bis zum Ortsnamen lagen 58px tote Breite - 22px Einrückung der
|
||
Monatsebene plus 40px linkes Polster der Zeile. Auf einem Telefon blieb
|
||
für "Eichstätt → Adelschlag" so wenig übrig, dass der Name umbrach
|
||
(gemeldet vom Eigentümer mit Bildschirmfoto).
|
||
|
||
Apples Richtlinien lösen Gruppierung nicht über Einrückung, sondern über
|
||
Abschnittsköpfe, die über die volle Breite laufen; die Zeilen darunter
|
||
stehen alle auf derselben Kante. Genau das ist hier umgesetzt - die
|
||
Monatszeile bleibt kleiner und grau und liest sich dadurch als Kopf,
|
||
ohne Platz zu kosten. Beschränkt auf .liste, weil das linke Polster von
|
||
.leaf anderswo (Batteriewerte, Servicebuch) gewollt ist. */
|
||
.liste .lvl2 { margin-left: 0; }
|
||
/* Der Monat ist ein Abschnittskopf, kein kleingeratener Eintrag.
|
||
|
||
Vorher unterschied er sich von der Fahrtzeile nur durch Groesse (13 gegen
|
||
16px) und Farbe - beim Lesen wirkte die Liste dadurch zweistufig statt
|
||
dreistufig, und genau danach hat der Eigentuemer gefragt (02.09.2026).
|
||
Versalien plus Sperrung sind die Form, die iOS fuer Abschnittskoepfe
|
||
benutzt und die in dieser Oberflaeche schon ueber dem Seitentitel steht
|
||
(.eyebrow).
|
||
|
||
11px und nicht 10: das ist die Untergrenze, die dieses Projekt aus den
|
||
Apple-Richtlinien uebernommen hat. In beiden Oberflaechen dieselben
|
||
Werte, damit der Kopf nicht je Geraet anders aussieht. */
|
||
.liste .lvl2 .acc-h .k {
|
||
font-size: 11px;
|
||
letter-spacing: .18em;
|
||
text-transform: uppercase;
|
||
color: var(--fg3);
|
||
}
|
||
.liste .leaf { padding-left: 0; }
|
||
|
||
.tile-h {
|
||
display: block; width: 100%;
|
||
background: none; border: none;
|
||
font-family: inherit; text-align: left;
|
||
padding: 0; cursor: pointer; color: inherit;
|
||
}
|
||
.tile-b { display: none; }
|
||
.tile-b.on { display: block; }
|
||
|
||
/* -------------------------------------------------------------- Statistik */
|
||
/* Fuenf Spalten (Jahr/Monat/Woche/Tag/Absolut) passen auf einem Telefon nicht
|
||
nebeneinander: bei 307px Innenbreite blieben je 51px, und "209.177" braucht
|
||
bei 16px Audi Type Wide rund 60. auto-fit bricht deshalb um - auf dem Telefon
|
||
drei plus zwei, auf einem breiten Bildschirm alle fuenf in einer Reihe. */
|
||
/* 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. */
|
||
.quad { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-3); margin-top: var(--sp-3); }
|
||
.quad .n { display: block; font-family: "Audi Type Wide", var(--font-stack); font-size: 16px; font-weight: 300; color: var(--fg); line-height: 1.2; }
|
||
.quad .l { display: block; margin-top: 4px; font-size: 11px; letter-spacing: normal; color: var(--fg3); line-height: 1.3; }
|
||
.bar { display: flex; height: 4px; border-radius: var(--r-pill); overflow: hidden; gap: 2px; }
|
||
.bar i { display: block; border-radius: var(--r-pill); }
|
||
.legend { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-2); font-size: 11px; color: var(--fg2); }
|
||
.legend span { display: inline-flex; align-items: center; gap: 6px; }
|
||
.legend i { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
||
|
||
/* -------------------------------------------------------------- Wischen */
|
||
.swipe-wrap { position: relative; overflow: hidden; }
|
||
/* Der rote Loeschknopf liegt dauerhaft hinter der Zeile. Zeilenhoehen sind
|
||
gebrochen (z. B. 66,28 px), dadurch fiel die Kante von .swipe-content nicht
|
||
aufs Geraetepixelraster und der rote Knopf blitzte als duenne Linie an den
|
||
Zeilenraendern durch - sichtbar als roter Teilrahmen um jede Listenzeile.
|
||
Er wird deshalb erst sichtbar, sobald wirklich gewischt wird ("wischt"
|
||
waehrend der Geste, "swiped" fuer die offen stehende Zeile). */
|
||
.swipe-delete { position: absolute; inset: 0 0 0 auto; width: 84px; display: flex; visibility: hidden; }
|
||
.swipe-wrap.wischt .swipe-delete,
|
||
.swipe-wrap.swiped .swipe-delete { visibility: visible; }
|
||
.swipe-loeschen {
|
||
width: 100%;
|
||
background: var(--bad); color: #fff; border: none;
|
||
font-family: inherit; font-size: 13px;
|
||
letter-spacing: normal;
|
||
cursor: pointer;
|
||
}
|
||
.swipe-loeschen:active { filter: brightness(.88); }
|
||
.swipe-content { position: relative; background: var(--tile); touch-action: pan-y; transition: transform .18s; }
|
||
.swipe-wrap.swiped .swipe-content { transform: translateX(-84px); }
|
||
|
||
/* -------------------------------------------------------------- Karte */
|
||
/* position:relative, damit der Zentrieren-Knopf im Einzelbeleg (absolut
|
||
positioniert, siehe .kartensteuerung-klein) sich an dieser Kachel ausrichtet
|
||
und nicht am naechsten Vorfahren weiter oben. */
|
||
.mapbox { position: relative; border-radius: var(--r-tile); overflow: hidden; height: 210px; background: var(--tile); }
|
||
.mapbox #map { width: 100%; height: 100%; }
|
||
|
||
/* --------------------------------------------------------- Standort
|
||
Vorschau-Kachel (Übersicht) + Vollbildansicht + Fahrzeug-Menü. Höhen sind
|
||
bewusst fest statt prozentual/vererbt - eine relative Höhe wird von
|
||
panel_custom nicht zuverlässig durchgereicht (siehe audi-dashboard-
|
||
ios.css-Kommentar zur selben Falle). */
|
||
.standort-kachel { padding: 0; overflow: hidden; }
|
||
.standort-kachelbtn {
|
||
display: block; width: 100%; position: relative;
|
||
border: none; padding: 0; margin: 0; background: none; cursor: pointer;
|
||
}
|
||
.standort-vorschau { width: 100%; height: 190px; background: var(--tile); }
|
||
.standort-leer {
|
||
position: absolute; inset: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 13px; color: var(--fg3); text-align: center; padding: 0 30px;
|
||
background: var(--tile);
|
||
}
|
||
|
||
.fahrzeug-pin-wrap { background: none; border: none; }
|
||
/* poi-car/poi aus dem CI-Satz sind reine Konturformen (Ring aus zwei
|
||
gegenlaeufigen Randlinien + duenne Detaillinien) - eine Pixelprobe ergab
|
||
nur 13-40% gefuellte Flaeche in der eigenen Bounding-Box und war auf
|
||
Kartenkacheln praktisch unsichtbar (Nutzer-Feedback "Pin ist transparent").
|
||
fahrzeugMarkerSVG()/tankstellenMarkerSVG() rendern deshalb zusaetzlich eine
|
||
Vollton-Silhouette (CI.*Sil, exakt der aeussere Ballon-Umriss desselben
|
||
CI-Pfads, keine Neuzeichnung) als unterste Ebene; die Iconfarbe hier ist
|
||
deshalb weiss statt dunkel, damit Umriss und Detaillinien auf der jetzt
|
||
massiven Flaeche lesbar bleiben. Feste Farben statt Theme-Variablen, damit
|
||
die Nadeln in Tag- und Nachtansicht gleich aussehen. */
|
||
.fahrzeug-pin {
|
||
display: flex; align-items: center; justify-content: center;
|
||
color: #fff;
|
||
filter: drop-shadow(0 2px 4px rgba(0,0,0,.4));
|
||
}
|
||
.user-pin-wrap { background: none; border: none; }
|
||
.user-pin {
|
||
position: relative; width: 16px; height: 16px; border-radius: 50%;
|
||
background: #0A84FF; border: 3px solid #fff;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,.3);
|
||
}
|
||
.user-pin::after {
|
||
content: ""; position: absolute; inset: -14px; border-radius: 50%;
|
||
background: rgba(10,132,255,.18);
|
||
animation: user-pulse 2.4s ease-out infinite;
|
||
}
|
||
@keyframes user-pulse { 0% { transform: scale(.4); opacity: .9; } 100% { transform: scale(1); opacity: 0; } }
|
||
|
||
.standort-vollbild { position: relative; height: min(640px, calc(100dvh - 190px)); border-radius: var(--r-tile); overflow: hidden; margin: 0 calc(-1 * var(--seitenrand)); }
|
||
.standort-karte { width: 100%; height: 100%; }
|
||
|
||
.kartensteuerung {
|
||
position: absolute; z-index: 400; top: 16px; right: 16px;
|
||
display: flex; flex-direction: column; gap: 12px;
|
||
}
|
||
.kartensteuerung button {
|
||
width: 48px; height: 48px; border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: rgba(16, 20, 26, .84); color: #fff;
|
||
border: none; box-shadow: 0 4px 14px rgba(0,0,0,.32);
|
||
-webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
|
||
cursor: pointer; padding: 0;
|
||
}
|
||
/* Tag-Theme: helle Kapsel mit dunklem Symbol - die CI-Symbole selbst tragen
|
||
fill="currentColor" und folgen der Textfarbe, es gibt keine zweite Datei. */
|
||
:host([data-theme="tag"]) .kartensteuerung button {
|
||
background: rgba(255,255,255,.92); color: #101418;
|
||
box-shadow: 0 4px 14px rgba(0,0,0,.18);
|
||
}
|
||
.kartensteuerung button:active { transform: scale(.94); }
|
||
:host([data-theme="tag"]) .kartensteuerung button:active { background: #fff; }
|
||
/* Variante in der Belegkarte: die Karte ist dort nur 210px hoch, ein 48er-
|
||
Knopf mit 16px Abstand wuerde ein Viertel davon verdecken. */
|
||
.kartensteuerung-klein { top: 10px; right: 10px; }
|
||
.kartensteuerung-klein button { width: 36px; height: 36px; }
|
||
.kartensteuerung-klein button:disabled { opacity: .45; cursor: default; }
|
||
.kartensteuerung-klein button:disabled:active { transform: none; }
|
||
|
||
/* Sichtbarer Anteil im geschlossenen Zustand (STANDORT_PEEK_PX im Frontend) -
|
||
beide Werte müssen zusammenpassen, siehe Kommentar dort. Der Rest des
|
||
Blatts wird nach unten geschoben und mit einem Zug nach oben geöffnet. */
|
||
.standortmenu {
|
||
position: absolute; z-index: 410; left: 0; right: 0; bottom: 0;
|
||
--standort-peek: 96px;
|
||
/* Deckend, nicht durchscheinend - unter dem Blatt liegt die Karte. */
|
||
background: var(--tile-deckend, var(--canvas));
|
||
border-radius: var(--r-tile) var(--r-tile) 0 0;
|
||
box-shadow: 0 -6px 24px rgba(0,0,0,.35);
|
||
/* Kein env(safe-area-inset-bottom) mehr: das Blatt liegt nicht am unteren
|
||
Bildschirmrand, sondern ueber der Tableiste - und die haelt den Bereich
|
||
des Home-Indikators bereits frei. Der Einzug wurde dadurch doppelt
|
||
gezaehlt, das Blatt war auf dem Geraet rund 16px hoeher als noetig und
|
||
schob sich entsprechend weiter hoch. Am Rechner faellt das nicht auf,
|
||
dort ist der Einzug 0. */
|
||
padding: 0 20px 34px;
|
||
transform: translateY(calc(100% - var(--standort-peek)));
|
||
transition: transform .28s cubic-bezier(.22,.61,.36,1);
|
||
}
|
||
/* Aufgezogen bleibt unter den Knoepfen ein fester Abstand stehen, statt dass
|
||
sie buendig mit der Menueleiste abschliessen.
|
||
|
||
Vorher stand hier min-height:50% - Apples "medium detent". Gemessen ergab
|
||
das 89px (Panel) bzw. 76px (App) Luft unter dem letzten Knopf, und das war
|
||
sichtbar zu viel: das Blatt zog ueber die Haelfte des Bildschirms auf,
|
||
obwohl sein Inhalt dort laengst zu Ende war. Eine Raste ist nur dann
|
||
richtig, wenn zwischen ihr und der Inhaltshoehe kein grosses Loch klafft.
|
||
|
||
Jetzt bestimmt der Inhalt die Hoehe, und die 34px Fussabstand halten die
|
||
Knoepfe von der Leiste frei - hergeleitet aus der Beanstandung, nicht
|
||
geraten: 18px waren zu wenig, 76-89px zu viel. */
|
||
.standortmenu.offen { transform: translateY(0); }
|
||
.standortmenu-griffzone { padding: 10px 0 14px; touch-action: none; cursor: grab; position: relative; }
|
||
/* Der Griff ist jetzt ein Knopf (Tastaturzugang zum Menue). Die Knopf-
|
||
Voreinstellungen des Browsers werden deshalb zurueckgesetzt; die
|
||
Trefferflaeche waechst per Pseudo-Element auf 44px, ohne den sichtbaren
|
||
Strich zu vergroessern. */
|
||
.standortmenu-griff {
|
||
position: relative; display: block; width: 36px; height: 4px;
|
||
border: 0; padding: 0; border-radius: 2px;
|
||
background: var(--line-strong); margin: 0 auto; cursor: pointer;
|
||
}
|
||
.standortmenu-griff::after {
|
||
content: ""; position: absolute; left: 50%; top: 50%;
|
||
transform: translate(-50%, -50%); width: 88px; height: 44px;
|
||
}
|
||
/* --ios-fill statt --tile: das Blatt selbst ist deckend, im Tagmodus also
|
||
weiss - und --tile ist dort ebenfalls #FFFFFF. Der Knopf war damit weiss
|
||
auf weiss und als Knopf nicht zu erkennen. Nachts faellt es nicht auf,
|
||
weil --tile dort ein Schleier ueber dunklem Grund ist. --ios-fill ist in
|
||
diesem Projekt ohnehin das Zeichen fuer "das hier laesst sich bedienen"
|
||
und traegt in beiden Themes. */
|
||
.standortmenu-schliessen {
|
||
position: absolute; top: 14px; right: 0; width: 32px; height: 32px; border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: var(--ios-fill); border: none; color: var(--fg2); cursor: pointer; padding: 0;
|
||
}
|
||
/* Sichtbar nur im aufgezogenen Blatt (Begruendung am Knopf im Markup). */
|
||
.standortmenu-schliessen { display: none; }
|
||
.standortmenu.offen .standortmenu-schliessen { display: flex; }
|
||
.standortmenu-kopf { margin-top: 8px; padding-right: 44px; }
|
||
.standortmenu-name { font-size: 17px; color: var(--fg); }
|
||
.standortmenu-distanz { font-size: 12.5px; color: var(--fg3); margin-top: 4px; }
|
||
/* Abstand als leere Zeile zwischen Kopf und Details */
|
||
.standortmenu-adresse { font-size: 13px; color: var(--fg2); margin-top: 20px; }
|
||
.standortmenu-status { display: flex; align-items: center; gap: 9px; margin-top: 8px; font-size: 13px; color: var(--fg2); }
|
||
.standortmenu-status svg { color: var(--fg3); flex: 0 0 auto; }
|
||
.standortmenu-werte { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-size: 13px; color: var(--fg2); }
|
||
.standortmenu-werte svg { color: var(--fg3); flex: 0 0 auto; }
|
||
.standortmenu-aktionen { display: flex; gap: 10px; margin-top: 18px; padding-bottom: 4px; }
|
||
/* Beide Pillen folgen der Knopfsprache des restlichen Panels (.aktion /
|
||
.aktion.primaer weiter oben): Umriss fuer die Nebenaktion, gefuellt fuer
|
||
die Hauptaktion.
|
||
|
||
Vorher stand hier background: var(--tile) fuer "Teilen" und
|
||
background: var(--line-strong) fuer "Route". Beides ging im Tagmodus
|
||
schief: das Blatt darunter ist var(--tile-deckend), und die iOS-Auflage
|
||
(audi-dashboard-ios.css) setzt im Tagmodus --tile UND --tile-deckend
|
||
beide auf #FFFFFF - "Teilen" war damit weiss auf weiss, Kontrast 1,0:1,
|
||
also schlicht unsichtbar. --line-strong als Flaeche war ausserdem eine
|
||
Linienfarbe in der Rolle einer Fuellung.
|
||
|
||
Der Umriss haengt jetzt an denselben Tokens wie jeder andere Knopf und
|
||
traegt deshalb in beiden Themes und auch unter der iOS-Auflage, ohne dass
|
||
hier eine eigene Farbe gepflegt werden muss. */
|
||
.standort-pille {
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
||
height: 40px; padding: 0 20px; border-radius: var(--r-pill);
|
||
border: 1px solid var(--line-strong); background: none; color: var(--fg);
|
||
font-family: inherit; font-size: 14px; text-decoration: none; cursor: pointer;
|
||
}
|
||
.standort-pille.primaer {
|
||
background: var(--fg); border-color: var(--fg); color: var(--canvas);
|
||
}
|
||
.standort-pille:active { transform: scale(.97); }
|
||
.standort-pille[disabled] { opacity: .4; pointer-events: none; }
|
||
|
||
/* ------------------------------------------------------- Bilderverwaltung */
|
||
/* ---------------------------------------------------------- Blätter
|
||
Apples Vorgabe kennt für etwas Modales auf dem Telefon kein frei
|
||
schwebendes Fenster: es kommt von der unteren Kante. Das Aktionsblatt
|
||
(eine kurze Liste von Wegen) und das Formularblatt (eine Eingabe) sind
|
||
die beiden Formen; die Karte in der Bildschirmmitte war keine von
|
||
beiden und stand ausserdem ohne abgedunkelten Grund über der voll
|
||
sichtbaren Seite.
|
||
|
||
Der Schleier gehört dazu und ist nicht Geschmack: modal heisst, dass
|
||
der Inhalt dahinter nicht bedienbar ist - das soll man sehen. */
|
||
.bildmenu-catcher {
|
||
position: absolute; inset: 0; z-index: 1000;
|
||
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); }
|
||
|
||
/* Formularblatt (siehe Kasten oben): unten angeschlagen, nur oben rund,
|
||
blickdicht. Stand vorher als Karte 60px unter dem oberen Rand - ausser
|
||
Daumenreichweite und ohne sichtbaren Weg hinaus. */
|
||
.sdpopup {
|
||
position: absolute; z-index: 1001;
|
||
left: 0; right: 0; bottom: 0; top: auto;
|
||
background: var(--tile-deckend, var(--canvas));
|
||
border-radius: 16px 16px 0 0;
|
||
padding: var(--sp-4) 20px max(20px, env(safe-area-inset-bottom));
|
||
animation: blatt-hoch .28s cubic-bezier(.22,.61,.36,1);
|
||
}
|
||
@keyframes blatt-hoch { from { transform: translateY(100%) } to { transform: none } }
|
||
|
||
/* Beleg-Fenster: PDF hineinziehen (Rechner) oder einfügen (Telefon). */
|
||
.beleg-catcher {
|
||
position: absolute; inset: 0; z-index: 1000;
|
||
background: rgba(0,0,0,.45);
|
||
animation: sheet-fade .18s ease-out;
|
||
}
|
||
/* Formularblatt (siehe Kasten oben). Traegt drei Fenster: Beleg
|
||
hochladen, Daten importieren, Datensatz sichern/laden. */
|
||
.beleg-popup {
|
||
position: absolute; z-index: 1001;
|
||
left: 0; right: 0; bottom: 0;
|
||
top: auto; transform: none;
|
||
max-height: 88%;
|
||
overflow-y: auto;
|
||
/* --tile-2 ist im Nacht-Theme transluzent; ein Blatt ueber der Seite
|
||
braucht einen blickdichten Grund. */
|
||
background: var(--tile-deckend, var(--canvas));
|
||
border-radius: 16px 16px 0 0;
|
||
padding: var(--sp-4) 20px max(20px, env(safe-area-inset-bottom));
|
||
animation: blatt-hoch .28s cubic-bezier(.22,.61,.36,1);
|
||
}
|
||
.beleg-drop {
|
||
margin-top: var(--sp-3);
|
||
border: 1px dashed var(--line-strong);
|
||
border-radius: 14px;
|
||
padding: 28px var(--sp-4);
|
||
text-align: center;
|
||
display: flex; flex-direction: column; gap: 6px;
|
||
}
|
||
.beleg-drop strong { font-size: 15px; color: var(--fg); }
|
||
.beleg-drop span { font-size: 12.5px; color: var(--fg2); }
|
||
.beleg-drop.darueber { border-color: var(--fg); border-style: solid; background: var(--shade); }
|
||
|
||
/* Kopfzeile der Reifen-Kachel: Überschrift+km links, Radfoto rechts, beide
|
||
oben bündig - ersetzt die frühere Überlagerung (Montiert absolut über dem
|
||
Foto), seit Überschrift und km ihren eigenen Platz über Montiert bekommen
|
||
haben. */
|
||
.radkopf-row { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-4); }
|
||
.radkopf { display: flex; flex-direction: column; }
|
||
|
||
/* Editier-Symbol links neben der km-Anzeige (Audi-CI edit-s) - oeffnet das
|
||
Korrekturfeld fuer den manuell gepflegten Kilometerstand des Reifensatzes.
|
||
44x44pt Tippflaeche per unsichtbarem ::before wie bei .montiert/.zahnrad,
|
||
optisch bleibt nur das kleine Symbol sichtbar. */
|
||
.km-edit {
|
||
position: relative;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
width: 22px; height: 22px;
|
||
border: none; background: none; color: var(--fg3);
|
||
padding: 0; cursor: pointer; flex: 0 0 auto;
|
||
}
|
||
.km-edit::before { content: ""; position: absolute; inset: -11px; }
|
||
.km-edit:hover { color: var(--fg); }
|
||
.km-edit:active { color: var(--fg); transform: scale(.92); }
|
||
|
||
/* Zeile aus einer bearbeitbaren Liste entfernen (z. B. Selbstbeteiligung im
|
||
Vertrag) - 44x44pt Tippflaeche per unsichtbarem ::before wie bei .km-edit,
|
||
optisch bleibt nur das kleine × sichtbar. */
|
||
.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;
|
||
}
|
||
.zeile-loeschen::before { content: ""; position: absolute; inset: -11px; }
|
||
.zeile-loeschen:hover { color: var(--bad); }
|
||
.zeile-loeschen:active { color: var(--bad); transform: scale(.92); }
|
||
|
||
/* Kompakte Pille statt voller Breite (Apple-HIG-Pull-down-Maß): normale
|
||
Flusslage unter der Kachel-Überschrift/km, nicht mehr über dem Foto
|
||
überlagert (das saß nur dort, solange Überschrift+km diesen Platz noch
|
||
nicht beanspruchten). */
|
||
.montiert {
|
||
position: relative;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
font-family: inherit; font-size: 12.5px; letter-spacing: normal;
|
||
color: var(--fg2);
|
||
background: none;
|
||
border: 1px solid var(--line-strong);
|
||
border-radius: var(--r-pill);
|
||
padding: 7px 13px;
|
||
cursor: pointer;
|
||
transition: background .15s, color .15s, border-color .15s, transform .1s;
|
||
}
|
||
/* Optisch bleibt die Pille klein, die Tippflaeche wird per unsichtbarem
|
||
Pseudo-Element auf Apple-HIG-Mindestmass 44x44pt gebracht (Klick auf die
|
||
Flaeche des ::before zaehlt als Klick auf den echten Button). */
|
||
.montiert::before { content: ""; position: absolute; top: -8px; bottom: -8px; left: 0; right: 0; }
|
||
.montiert:hover { background: var(--shade); }
|
||
.montiert:active { transform: scale(.96); background: var(--shade); }
|
||
.montiert.on { background: var(--fg); border-color: var(--fg); color: var(--canvas); }
|
||
|
||
/* ------------------------------------------------- B4: Action Sheet
|
||
Ersetzt confirm()/alert(). Von unten eingeblendet, destruktive Zeile rot,
|
||
Abbrechen abgesetzt - iOS-Muster. */
|
||
.sheet-catcher {
|
||
position: absolute; inset: 0; z-index: 1200;
|
||
background: rgba(0,0,0,.45);
|
||
animation: sheet-fade .18s ease-out;
|
||
}
|
||
.sheet {
|
||
position: absolute; z-index: 1201;
|
||
left: 10px; right: 10px;
|
||
bottom: max(10px, env(safe-area-inset-bottom));
|
||
display: flex; flex-direction: column; gap: 8px;
|
||
animation: sheet-rein .24s cubic-bezier(.22,.61,.36,1);
|
||
}
|
||
@keyframes sheet-fade { from { opacity: 0 } to { opacity: 1 } }
|
||
@keyframes sheet-rein { from { transform: translateY(18px); opacity: 0 } to { transform: none; opacity: 1 } }
|
||
.sheet-gruppe {
|
||
/* --tile-2 is translucent in the iOS overlay's night theme (rgba white,
|
||
meant for tile-on-tile layering) - a confirm sheet sits directly over
|
||
whatever page content triggered it, so a see-through card made titles
|
||
like "Doppelt zugeordnet" hard to read against the busy background
|
||
bleeding through. Same fix already applied to .setup-popup/.standortmenu
|
||
for the same reason - opaque surface token, with the --canvas fallback
|
||
this file already uses elsewhere for when the (purely additive, in
|
||
principle removable) overlay isn't loaded. */
|
||
background: var(--tile-deckend, var(--canvas));
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
display: flex; flex-direction: column;
|
||
}
|
||
.sheet-kopf {
|
||
padding: 15px 18px 14px;
|
||
border-bottom: 1px solid var(--line);
|
||
display: flex; flex-direction: column; gap: 4px;
|
||
}
|
||
.sheet-kopf strong { font-size: 15px; font-weight: 400; color: var(--fg); }
|
||
.sheet-kopf span { font-size: 13px; color: var(--fg2); line-height: 1.45; }
|
||
.sheet-gruppe button {
|
||
background: none; border: none;
|
||
font-family: inherit; font-size: 16px;
|
||
color: var(--fg);
|
||
padding: 16px 18px; min-height: 52px;
|
||
text-align: left; cursor: pointer;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
.sheet-gruppe button:last-child { border-bottom: none; }
|
||
.sheet-gruppe button:active { background: var(--shade); }
|
||
.sheet-gruppe button.loeschen { color: var(--bad); }
|
||
.sheet-abbrechen {
|
||
background: var(--tile-deckend, var(--canvas)); border: none; border-radius: 16px;
|
||
font-family: inherit; font-size: 16px; color: var(--fg);
|
||
padding: 16px; min-height: 52px; cursor: pointer;
|
||
}
|
||
.sheet-abbrechen:active { background: var(--shade); }
|
||
|
||
/* ------------------------------------------------------- Setup-Popup
|
||
Sensor-zu-Entität-Zuordnung (Einstellungen -> Fahrzeug einrichten ->
|
||
Setup). Gleiche Catcher/Karten-Bauweise wie .bildmenu/.sdpopup oben,
|
||
aber großformatig und scrollbar statt Bottom-Sheet-Größe; nutzt die
|
||
bereits vorhandenen sheet-fade/sheet-rein-Keyframes. z-index bewusst
|
||
unter .sheet (1200er), damit ein Fehlerhinweis (hinweis()) bei
|
||
fehlgeschlagenem Speichern über dem noch offenen Setup-Popup liegt. */
|
||
.setup-catcher {
|
||
position: absolute; inset: 0; z-index: 1000;
|
||
background: rgba(0,0,0,.45);
|
||
animation: sheet-fade .18s ease-out;
|
||
}
|
||
.setup-popup {
|
||
position: absolute; z-index: 1001;
|
||
left: 10px; right: 10px;
|
||
top: max(10px, env(safe-area-inset-top));
|
||
bottom: max(10px, env(safe-area-inset-bottom));
|
||
/* --tile-2 ist inzwischen transluzent (rgba(255,255,255,.1) im Nacht-
|
||
Theme) - fuer ein frei ueber dem Inhalt schwebendes Popup braucht es
|
||
einen blickdichten Hintergrund, gleiches Muster wie .standortmenu
|
||
oben. */
|
||
background: var(--tile-deckend, var(--canvas));
|
||
border-radius: var(--r-tile);
|
||
display: flex; flex-direction: column;
|
||
overflow: hidden;
|
||
animation: sheet-rein .24s cubic-bezier(.22,.61,.36,1);
|
||
}
|
||
.setup-kopf {
|
||
flex: 0 0 auto;
|
||
padding: 18px 20px 14px;
|
||
border-bottom: 1px solid var(--line);
|
||
display: flex; flex-direction: column; gap: 4px;
|
||
}
|
||
.setup-kopf strong { font-size: 17px; font-weight: 400; color: var(--fg); }
|
||
.setup-kopf span { font-size: 13px; color: var(--fg2); line-height: 1.45; }
|
||
.setup-kopf-schalter { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
|
||
.setup-kopf-schalter span { font-size: 13px; color: var(--fg); }
|
||
.setup-koerper { flex: 1 1 0; min-height: 0; overflow-y: auto; padding: 4px 20px 20px; }
|
||
.setup-gruppe { margin-top: 18px; }
|
||
.setup-gruppe:first-child { margin-top: 6px; }
|
||
.setup-gruppe-titel { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); margin-bottom: 6px; }
|
||
.setup-feldzeile { padding: 12px 0; border-bottom: 1px solid var(--line); }
|
||
.setup-feldzeile:last-child { border-bottom: none; }
|
||
/* Nutzerwunsch: die Kennung des zugeordneten Sensors (entitaetIdKurz() in
|
||
audi-dashboard-app.js) steht NEBEN der Feld-Überschrift, nicht mehr wie
|
||
die frühere statische Beschreibung darunter - deshalb Zeile statt Spalte.
|
||
Die erwartete Einheit (feldEinheitAnzeige()) hängt direkt am Label selbst
|
||
("Tankfüllstand [%]"), losgelöst davon, ob/welcher Sensor zugeordnet ist.
|
||
2026-08-28, Nutzerkorrektur: EINE Zeile "(z. B. sensor_id)" statt zwei -
|
||
der Klammertext "(z. B. "/")" in derselben Größe/Farbe wie das Label links
|
||
(.setup-feld-label), die Sensor-ID selbst darin in derselben kleinen/
|
||
grauen Schrift wie die Einheit daneben (.setup-feld-einheit,
|
||
"[on/off]"/"[km]") - deshalb teilt sich .setup-feld-hinweis die Klasse
|
||
.setup-feld-einheit für den inneren Span statt einer eigenen Regel. */
|
||
.setup-feld-kopf { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
|
||
.setup-feld-label { font-size: 14px; color: var(--fg); flex: 0 1 auto; }
|
||
.setup-feld-einheit { color: var(--fg3); font-size: 12.5px; }
|
||
.setup-feld-hinweis {
|
||
font-size: 14px; color: var(--fg); line-height: 1.4; text-align: right;
|
||
max-width: 55%;
|
||
}
|
||
.setup-unterfelder { display: flex; flex-direction: column; gap: 12px; }
|
||
.setup-unterfeld { display: flex; flex-direction: column; gap: 4px; }
|
||
/* Positionsname links, Erwartungswert rechts - derselbe Bau wie
|
||
.setup-feld-kopf eine Ebene darueber, damit beide Zeilen dieselbe Kante
|
||
haben. Der Hinweis erbt hier die kleine graue Schrift des Labels; die
|
||
14px/--fg-Form von .setup-feld-hinweis passt nur in die Kopfzeile. */
|
||
.setup-unterfeld-label {
|
||
font-size: 12.5px; color: var(--fg3);
|
||
display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
|
||
}
|
||
.setup-unterfeld-label .setup-feld-hinweis {
|
||
font-size: inherit; color: inherit; max-width: none; flex: 0 0 auto;
|
||
}
|
||
|
||
.entitaet-combo { position: relative; }
|
||
.entitaet-combo-icon {
|
||
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
|
||
color: var(--fg3); display: flex; pointer-events: none;
|
||
}
|
||
.entitaet-combo-input {
|
||
width: 100%; box-sizing: border-box;
|
||
background: var(--tile-deckend, var(--canvas)); border: 1px solid var(--line); color: var(--fg);
|
||
font-family: inherit; font-size: 16px; /* siehe .feld input weiter oben: >=16px gegen Safari-Autozoom */
|
||
padding: 11px 34px 11px 36px;
|
||
border-radius: 12px;
|
||
text-align: left;
|
||
-webkit-appearance: none; appearance: none;
|
||
}
|
||
.entitaet-combo-input:focus { outline: none; border-color: var(--red); }
|
||
/* Die Zuordnung passt zum Beispielnamen der Rolle (siehe
|
||
entitaetPasstZumBeispiel). Bewusst zurueckhaltend: eine Bestaetigung im
|
||
Vorbeigehen, kein Ausrufezeichen. Deshalb die Farbe ueber color-mix aus dem
|
||
vorhandenen Erfolgs-Ton und dem Feldgrund statt eines eigenen Gruens - so
|
||
bleibt sie in beiden Themen ruhig und passt zum Rest der Oberflaeche.
|
||
Der Rahmen traegt etwas mehr Farbe als die Flaeche, sonst verschwindet die
|
||
Markierung auf dunklem Grund. */
|
||
.entitaet-combo-input.passend {
|
||
background: color-mix(in srgb, var(--ok, #2e7d32) 9%, var(--tile-deckend, var(--canvas)));
|
||
border-color: color-mix(in srgb, var(--ok, #2e7d32) 38%, var(--line));
|
||
}
|
||
.entitaet-combo-input.passend:focus { border-color: var(--red); }
|
||
.entitaet-liste {
|
||
position: absolute; z-index: 5; left: 0; right: 0; top: calc(100% + 4px);
|
||
max-height: 240px; overflow-y: auto;
|
||
background: var(--tile-deckend, var(--canvas)); border: 1px solid var(--line-strong);
|
||
border-radius: 12px;
|
||
box-shadow: 0 12px 28px rgba(0,0,0,.35);
|
||
}
|
||
.entitaet-eintrag {
|
||
display: flex; flex-direction: column; gap: 1px;
|
||
width: 100%; text-align: left;
|
||
background: none; border: none; border-bottom: 1px solid var(--line);
|
||
color: var(--fg); font-family: inherit;
|
||
padding: 9px 12px; cursor: pointer;
|
||
}
|
||
.entitaet-eintrag:last-child { border-bottom: none; }
|
||
.entitaet-eintrag:active, .entitaet-eintrag.aktiv { background: var(--shade); }
|
||
.entitaet-eintrag-name { font-size: 14px; color: var(--fg); }
|
||
.entitaet-eintrag-id { font-size: 12.5px; color: var(--fg3); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||
.entitaet-eintrag-wert { font-size: 12.5px; color: var(--fg2); margin-top: 1px; }
|
||
.entitaet-eintrag-wert.unbekannt { color: var(--warn); }
|
||
.entitaet-leer { padding: 14px 12px; font-size: 13px; color: var(--fg2); }
|
||
.setup-feld-warnung { font-size: 12.5px; color: var(--warn); line-height: 1.4; margin-top: 6px; }
|
||
|
||
.setup-fuss {
|
||
flex: 0 0 auto;
|
||
display: flex; gap: 10px;
|
||
padding: 14px 20px max(14px, env(safe-area-inset-bottom));
|
||
border-top: 1px solid var(--line);
|
||
}
|
||
.setup-fuss .aktion { flex: 1; margin-top: 0; }
|
||
|
||
/* ------------------------------------------------- B3: Push/Pop
|
||
Seitenwechsel wie auf iOS: neue Ebene kommt von rechts, zurück geht sie
|
||
dorthin. */
|
||
.push-rein { animation: push-rein .26s cubic-bezier(.32,.72,0,1); }
|
||
.push-raus { animation: push-raus .26s cubic-bezier(.32,.72,0,1); }
|
||
@keyframes push-rein { from { transform: translateX(26px); opacity: 0 } to { transform: none; opacity: 1 } }
|
||
@keyframes push-raus { from { transform: translateX(-18px); opacity: 0 } to { transform: none; opacity: 1 } }
|
||
|
||
/* D5: Leerzustand nennt die Ursache und die nächste Handlung. */
|
||
.leer { display: flex; flex-direction: column; gap: 8px; padding: 4px 0 2px; }
|
||
.leer strong { font-size: 16px; font-weight: 400; color: var(--fg); }
|
||
.leer p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--fg2); text-wrap: pretty; }
|
||
|
||
/* B7 */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: .01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: .01ms !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
}
|