index.php?action=anzeige: Uebersicht, Anlage und Speicher ohne Kopf und
Seitenleiste, schmale Leiste links, immer quer. Nach zwei Minuten ohne
Beruehrung zurueck zur Uebersicht, MQTT verbindet still neu (Punkt in der
Leiste), alle sechs Stunden neu laden. Standard "ruhig" fuer den Pi 3B+
(nur Fluesse, grobe Stufen, kein Mauszeiger); &animation=1 und &maus=1.
Dafuer:
- Seitentabelle kennt "eigen" (Vorlage ist eine ganze Seite)
- Energiefluss: Optionen hochkantUnter und klickFilter; Anlage: hochkantUnter
- Bewaesserungszustand aus solarMQTT.js nach bewaesserungAnzeige.js, von
Solar-Seite und Anzeige gemeinsam genutzt
- Speicher: "voll/leer" mit Tag ("morgen 21:52", "in 3 Tagen")
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3945 lines
147 KiB
CSS
3945 lines
147 KiB
CSS
/* ==========================================================================
|
|
Grundton der Oberflaeche
|
|
|
|
Die Seite laeuft auf AdminLTE, also auf Bootstrap. Beide beziehen ihre
|
|
Farben aus Variablen - deshalb wird hier nicht Regel fuer Regel
|
|
ueberschrieben, sondern die Palette ausgetauscht: ein paar Variablen am
|
|
Wurzelelement, und Karten, Tabellen, Abzeichen und Knoepfe folgen von
|
|
selbst. Was danach noch kommt, sind wenige gezielte Regeln fuer die Dinge,
|
|
die Bootstrap fest verdrahtet hat (Kartenrahmen, Kopfzeilen, Trennlinien).
|
|
|
|
Die Palette selbst ist die des Jahresvergleichs: ein leicht blaustichiges
|
|
Anthrazit als Grund, eine Spur hellere Flaeche fuer Karten, Haarlinien statt
|
|
Rahmen - und Bernstein als einzige Akzentfarbe, weil das auf dieser Seite
|
|
die Farbe der eigenen Anlage ist. Gruen, Rot und Gelb bleiben den
|
|
Zustaenden vorbehalten und sind kein Schmuck.
|
|
========================================================================== */
|
|
|
|
:root,
|
|
[data-bs-theme="dark"] {
|
|
--ton-grund: #191d20; /* Seitenhintergrund */
|
|
--ton-flaeche: #23282c; /* Karten */
|
|
--ton-flaeche-2: #2b3237; /* Kopfzeilen, Tabellenkopf */
|
|
--ton-linie: rgba(138, 148, 158, .20);
|
|
--ton-linie-satt: rgba(138, 148, 158, .38);
|
|
--ton-schrift: #e8ede9;
|
|
--ton-schrift-2: #9aa6ad;
|
|
--ton-schrift-3: #6f7a81;
|
|
--ton-sonne: #f0b429; /* die eigene Anlage */
|
|
--ton-sonne-rgb: 240, 180, 41;
|
|
--ton-gut: #4caf7d;
|
|
--ton-schlecht: #e06060;
|
|
|
|
/* Zwei Radien statt einem. Der grosse gilt fuer Flaechen (Karten, Modale,
|
|
Eingabefelder), der kleine fuer alles, was in einer Reihe steht -
|
|
Schalter, kleine Knoepfe, Abzeichen. Bootstrap kennt beide, benutzte
|
|
bisher aber nur den grossen; der kleine stand auf seiner Vorgabe von
|
|
.25rem und wirkte daneben eckig. */
|
|
--ton-radius: .875rem;
|
|
--ton-radius-klein: .5rem;
|
|
|
|
/* Die Anzeigeschrift. Poppins liegt seit jeher unter assets/fonts und trug
|
|
bisher nur die Etagenknoepfe im Aussenplan - eine ganze Schrift fuer vier
|
|
Buchstaben. Als Variable traegt sie jetzt alle Ueberschriften: waermer
|
|
und eine Spur weniger technisch als die Source Sans 3 des Fliesstextes,
|
|
ohne dass eine zweite Datei geladen wird. Zahlenkolonnen und Fliesstext
|
|
bleiben unberuehrt - Poppins hat keine Tabellenziffern. */
|
|
--font-anzeige: Poppins, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
|
|
/* ... und dieselben Werte dort, wo Bootstrap sie sucht. */
|
|
--bs-body-bg: var(--ton-grund);
|
|
--bs-body-bg-rgb: 25, 29, 32;
|
|
--bs-body-color: var(--ton-schrift);
|
|
--bs-body-color-rgb: 232, 237, 233;
|
|
--bs-emphasis-color: #fff;
|
|
--bs-secondary-color: var(--ton-schrift-2);
|
|
--bs-secondary-bg: var(--ton-flaeche-2);
|
|
--bs-tertiary-color: var(--ton-schrift-3);
|
|
--bs-tertiary-bg: var(--ton-flaeche-2);
|
|
--bs-border-color: var(--ton-linie-satt);
|
|
--bs-border-radius: var(--ton-radius);
|
|
--bs-border-radius-sm: var(--ton-radius-klein);
|
|
--bs-link-color: var(--ton-sonne);
|
|
--bs-link-hover-color: #ffc94d;
|
|
--bs-heading-color: var(--ton-schrift);
|
|
}
|
|
|
|
/* Ueberschriften in der Anzeigeschrift. Bewusst nur Ueberschriften: der
|
|
Fliesstext und alle Zahlenkolonnen bleiben bei der Schrift des Themes,
|
|
weil Poppins keine Tabellenziffern mitbringt und Messwerte sonst
|
|
springen wuerden. */
|
|
h1, h2, h3, h4, h5, h6,
|
|
.h1, .h2, .h3, .h4, .h5, .h6,
|
|
.card-title,
|
|
.modal-title {
|
|
font-family: var(--font-anzeige);
|
|
font-weight: 600;
|
|
letter-spacing: -.01em;
|
|
}
|
|
|
|
/* Der Rumpf traegt zwei Grundfarben: die Seite und die Leisten drumherum.
|
|
AdminLTE setzt sie ueber eigene Klassen, die hier auf denselben Ton
|
|
gebracht werden - sonst steht eine Karte auf drei verschiedenen Grautoenen. */
|
|
body.bg-body-tertiary,
|
|
.app-content,
|
|
.app-content-header { background: var(--ton-grund); }
|
|
|
|
.app-header.bg-body,
|
|
.app-sidebar.bg-body-secondary {
|
|
background: var(--ton-flaeche) !important;
|
|
border-color: var(--ton-linie) !important;
|
|
}
|
|
.app-header { box-shadow: 0 1px 0 var(--ton-linie); }
|
|
|
|
/* --- Karten --------------------------------------------------------------
|
|
Eine Karte ist eine Flaeche, kein Kasten: ein Ton heller als der Grund, eine
|
|
Haarlinie als Rand, kein Schlagschatten. Der Schatten trennte frueher, was
|
|
der Farbunterschied jetzt allein leistet. */
|
|
.card {
|
|
background: var(--ton-flaeche);
|
|
border: 1px solid var(--ton-linie);
|
|
border-radius: var(--ton-radius);
|
|
box-shadow: none;
|
|
}
|
|
.card-header {
|
|
background: transparent;
|
|
border-bottom: 1px solid var(--ton-linie);
|
|
padding-inline: 1rem;
|
|
}
|
|
.card-title {
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
letter-spacing: -.01em;
|
|
}
|
|
.card-body { padding: 1rem; }
|
|
.card-footer {
|
|
background: transparent;
|
|
border-top: 1px solid var(--ton-linie);
|
|
}
|
|
|
|
/* Werkzeugknoepfe in der Kopfzeile: ruhig, bis man sie braucht. */
|
|
.btn-tool {
|
|
color: var(--ton-schrift-3);
|
|
transition: color .18s, background .18s;
|
|
border-radius: .35rem;
|
|
}
|
|
.btn-tool:hover,
|
|
.btn-tool:focus-visible { color: var(--ton-schrift); background: rgba(138, 148, 158, .12); }
|
|
|
|
/* --- Menue ---------------------------------------------------------------
|
|
Der aktive Punkt traegt die Akzentfarbe, nicht das Blau des Themes -
|
|
sonst haette die Seite zwei Hauptfarben. */
|
|
.sidebar-menu .nav-link {
|
|
border-radius: .45rem;
|
|
color: var(--ton-schrift-2);
|
|
}
|
|
.sidebar-menu .nav-link:hover { background: rgba(138, 148, 158, .1); color: var(--ton-schrift); }
|
|
.sidebar-menu .nav-link.active,
|
|
.sidebar-menu .nav-link.active:hover {
|
|
background: rgba(var(--ton-sonne-rgb), .16);
|
|
color: var(--ton-sonne);
|
|
}
|
|
.sidebar-brand { border-bottom: 1px solid var(--ton-linie); }
|
|
.brand-text { font-weight: 400; letter-spacing: .01em; }
|
|
|
|
/* --- Schrift -------------------------------------------------------------
|
|
Zahlen stehen ueberall in Spalten untereinander - mit Ziffern gleicher
|
|
Breite bleiben sie ausgerichtet, auch wenn sich der Wert aendert. */
|
|
.card-body,
|
|
.table,
|
|
.info-box-number,
|
|
.progress-bar { font-variant-numeric: tabular-nums; }
|
|
|
|
.app-content-header h3 {
|
|
font-weight: 500;
|
|
letter-spacing: -.01em;
|
|
}
|
|
|
|
/* --- Tabellen ------------------------------------------------------------ */
|
|
.table {
|
|
--bs-table-bg: transparent;
|
|
--bs-table-color: var(--ton-schrift);
|
|
--bs-table-border-color: var(--ton-linie);
|
|
--bs-table-hover-bg: rgba(138, 148, 158, .06);
|
|
--bs-table-striped-bg: rgba(138, 148, 158, .04);
|
|
}
|
|
.table > thead {
|
|
color: var(--ton-schrift-2);
|
|
font-size: .82rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* --- Abzeichen -----------------------------------------------------------
|
|
Gedaempft statt grell: ein Abzeichen soll den Zustand sagen, nicht die
|
|
Kachel dominieren. */
|
|
.badge { font-weight: 500; letter-spacing: .01em; }
|
|
.badge.text-bg-secondary {
|
|
background: rgba(138, 148, 158, .18) !important;
|
|
color: var(--ton-schrift-2) !important;
|
|
}
|
|
.badge.text-bg-success {
|
|
background: rgba(76, 175, 125, .18) !important;
|
|
color: var(--ton-gut) !important;
|
|
}
|
|
.badge.text-bg-warning {
|
|
background: rgba(240, 180, 41, .18) !important;
|
|
color: var(--ton-sonne) !important;
|
|
}
|
|
.badge.text-bg-danger {
|
|
background: rgba(224, 96, 96, .18) !important;
|
|
color: var(--ton-schlecht) !important;
|
|
}
|
|
.badge.text-bg-info {
|
|
background: rgba(122, 167, 199, .18) !important;
|
|
color: #9cc4de !important;
|
|
}
|
|
|
|
/* --- Formulare ----------------------------------------------------------- */
|
|
.form-control,
|
|
.form-select {
|
|
background: rgba(0, 0, 0, .18);
|
|
border-color: var(--ton-linie-satt);
|
|
color: var(--ton-schrift);
|
|
}
|
|
.form-control:focus,
|
|
.form-select:focus {
|
|
background: rgba(0, 0, 0, .18);
|
|
border-color: var(--ton-sonne);
|
|
box-shadow: 0 0 0 .2rem rgba(var(--ton-sonne-rgb), .2);
|
|
color: var(--ton-schrift);
|
|
}
|
|
.form-check-input:checked { background-color: var(--ton-sonne); border-color: var(--ton-sonne); }
|
|
.form-check-input:focus { box-shadow: 0 0 0 .2rem rgba(var(--ton-sonne-rgb), .2); }
|
|
|
|
/* Sichtbarer Tastaturfokus, einheitlich - AdminLTE setzt ihn stellenweise ab. */
|
|
a:focus-visible,
|
|
button:focus-visible,
|
|
[tabindex]:focus-visible {
|
|
outline: 2px solid var(--ton-sonne);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
/* --- Trennlinien und Modals ---------------------------------------------- */
|
|
hr { border-color: var(--ton-linie); opacity: 1; }
|
|
.modal-content {
|
|
background: var(--ton-flaeche);
|
|
border: 1px solid var(--ton-linie-satt);
|
|
border-radius: var(--ton-radius);
|
|
}
|
|
.modal-header,
|
|
.modal-footer { border-color: var(--ton-linie); }
|
|
|
|
|
|
/* ==========================================================================
|
|
Jahresstatistik (js/solar/jahresstatistik.js)
|
|
|
|
Die Tabelle traegt die Seite "Vergangenheit" und steht in kleiner Form auch
|
|
auf der Solarseite. Sie ist zum Lesen einer Zeile gebaut: Kennzahlen links,
|
|
Jahre nebeneinander, die Veraenderung als Zahl - nicht als blosser Pfeil.
|
|
========================================================================== */
|
|
|
|
/* Der Baustein setzt seine Bloecke selbst auf Abstand - und zwar auf denselben
|
|
wie den zwischen den Leitzahlen und den Innenabstand der Karte. Alle Nahtstellen
|
|
sind dadurch gleich weit, gleich ob die Werkzeugleiste dabei ist oder nicht. */
|
|
.jahresstatistik {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--stat-abstand, 1.25rem);
|
|
}
|
|
|
|
/* Der Innenabstand der Karte gehoert zum selben Mass - sonst sitzt die erste
|
|
Kachel enger unter der Ueberschrift als die zweite unter der ersten.
|
|
|
|
Der Vorsatz .app-content ist noetig, nicht schmueckend: weiter unten in
|
|
dieser Datei setzt .app-content .card-body auf dem Handy einen kleineren
|
|
Innenabstand, und bei gleicher Spezifitaet gewaenne die spaetere Regel. */
|
|
.app-content .card-body.statistik { padding: var(--stat-abstand, 1.25rem); }
|
|
|
|
/* Auf dem Handy ruecken alle Nahtstellen zusammen - aber gemeinsam, sonst
|
|
waere die Gleichheit wieder dahin. */
|
|
@media (max-width: 767.98px) {
|
|
.jahresstatistik,
|
|
.app-content .card-body.statistik { --stat-abstand: 1rem; }
|
|
}
|
|
|
|
/* --- Leitzahlen ---------------------------------------------------------- */
|
|
.jahresstatistik .leitzahlen {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
|
gap: var(--stat-abstand, 1.25rem);
|
|
}
|
|
.jahresstatistik .leitzahl {
|
|
background: rgba(138, 148, 158, .07);
|
|
border: 1px solid var(--ton-linie);
|
|
border-radius: var(--ton-radius);
|
|
padding: .9rem 1.05rem 1rem;
|
|
}
|
|
.jahresstatistik .leitzahl .name { font-size: .78rem; color: var(--ton-schrift-2); }
|
|
.jahresstatistik .leitzahl .zahl {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: .35rem;
|
|
margin-top: .3rem;
|
|
font-size: 1.9rem;
|
|
font-weight: 500;
|
|
line-height: 1.1;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.jahresstatistik .leitzahl .einheit {
|
|
font-size: .95rem;
|
|
font-weight: 400;
|
|
color: var(--ton-schrift-2);
|
|
}
|
|
.jahresstatistik .leitzahl .fuss {
|
|
margin-top: .45rem;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
font-size: .76rem;
|
|
color: var(--ton-schrift-3);
|
|
}
|
|
.jahresstatistik .leitzahl.betont { border-color: rgba(var(--ton-sonne-rgb), .45); }
|
|
.jahresstatistik .leitzahl.betont .zahl { color: var(--ton-sonne); }
|
|
|
|
/* Die Veraenderung: Richtung und Groesse, nicht nur Richtung. */
|
|
.jahresstatistik .delta {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .25rem;
|
|
font-size: .78rem;
|
|
font-variant-numeric: tabular-nums;
|
|
white-space: nowrap;
|
|
}
|
|
.jahresstatistik .delta.gut { color: var(--ton-gut); }
|
|
.jahresstatistik .delta.schlecht { color: var(--ton-schlecht); }
|
|
.jahresstatistik .delta.gleich { color: var(--ton-schrift-3); }
|
|
|
|
/* --- Werkzeugleiste ------------------------------------------------------ */
|
|
.jahresstatistik .werkzeuge {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .75rem 1.25rem;
|
|
}
|
|
.jahresstatistik .umschalter {
|
|
display: inline-flex;
|
|
border: 1px solid var(--ton-linie-satt);
|
|
border-radius: .45rem;
|
|
overflow: hidden;
|
|
}
|
|
.jahresstatistik .umschalter button {
|
|
appearance: none;
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ton-schrift-2);
|
|
font: inherit;
|
|
font-size: .82rem;
|
|
padding: .4rem .9rem;
|
|
cursor: pointer;
|
|
transition: background .18s, color .18s;
|
|
}
|
|
.jahresstatistik .umschalter button + button { border-left: 1px solid var(--ton-linie-satt); }
|
|
.jahresstatistik .umschalter button:hover { background: rgba(138, 148, 158, .1); }
|
|
.jahresstatistik .umschalter button[aria-pressed="true"] {
|
|
background: rgba(var(--ton-sonne-rgb), .16);
|
|
color: var(--ton-sonne);
|
|
}
|
|
|
|
/* Das Jahrfenster: zwei Pfeile und dazwischen, was gerade zu sehen ist. */
|
|
.jahresstatistik .jahrwahl {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .35rem;
|
|
font-size: .82rem;
|
|
color: var(--ton-schrift-2);
|
|
}
|
|
.jahresstatistik .jahrwahl button {
|
|
appearance: none;
|
|
border: 1px solid var(--ton-linie-satt);
|
|
background: none;
|
|
color: var(--ton-schrift-2);
|
|
border-radius: .35rem;
|
|
width: 1.9rem;
|
|
height: 1.9rem;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
transition: background .18s, color .18s, opacity .18s;
|
|
}
|
|
.jahresstatistik .jahrwahl button:hover:not(:disabled) {
|
|
background: rgba(138, 148, 158, .12);
|
|
color: var(--ton-schrift);
|
|
}
|
|
.jahresstatistik .jahrwahl button:disabled { opacity: .3; cursor: default; }
|
|
.jahresstatistik .jahrwahl .fenster { font-variant-numeric: tabular-nums; min-width: 5.5rem; text-align: center; }
|
|
|
|
.jahresstatistik .kasten {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .45rem;
|
|
font-size: .82rem;
|
|
color: var(--ton-schrift-2);
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
.jahresstatistik .kasten input { accent-color: var(--ton-sonne); width: 1rem; height: 1rem; }
|
|
|
|
/* --- Die Tabelle --------------------------------------------------------- */
|
|
.jahresstatistik .tafel-scroll {
|
|
overflow-x: auto;
|
|
border: 1px solid var(--ton-linie);
|
|
border-radius: var(--ton-radius);
|
|
}
|
|
.jahresstatistik .tafel {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: .88rem;
|
|
margin: 0;
|
|
}
|
|
.jahresstatistik .tafel thead th {
|
|
background: var(--ton-flaeche-2);
|
|
text-align: right;
|
|
font-weight: 500;
|
|
font-size: .78rem;
|
|
padding: .55rem .9rem .5rem;
|
|
border-bottom: 1px solid var(--ton-linie-satt);
|
|
white-space: nowrap;
|
|
}
|
|
.jahresstatistik .tafel thead th:first-child { text-align: left; }
|
|
.jahresstatistik .tafel thead th.jetzt { color: var(--ton-sonne); }
|
|
.jahresstatistik .tafel thead .zeitraum {
|
|
display: block;
|
|
font-weight: 400;
|
|
font-size: .68rem;
|
|
color: var(--ton-schrift-3);
|
|
letter-spacing: .03em;
|
|
}
|
|
|
|
.jahresstatistik .tafel tbody td {
|
|
padding: .4rem .9rem;
|
|
border-bottom: 1px solid var(--ton-linie);
|
|
text-align: right;
|
|
font-variant-numeric: tabular-nums;
|
|
white-space: nowrap;
|
|
}
|
|
.jahresstatistik .tafel tbody td.name {
|
|
text-align: left;
|
|
width: 40%;
|
|
min-width: 14rem;
|
|
white-space: normal;
|
|
}
|
|
.jahresstatistik .tafel tbody tr:last-child td { border-bottom: 0; }
|
|
@media (hover: hover) {
|
|
.jahresstatistik .tafel tbody tr:hover td { background: rgba(138, 148, 158, .06); }
|
|
.jahresstatistik .tafel tbody tr.gruppe:hover td { background: none; }
|
|
}
|
|
|
|
/* Gruppenkopf: kein Kasten, nur eine Zaesur. */
|
|
.jahresstatistik .tafel tr.gruppe td {
|
|
padding: 1rem .9rem .25rem;
|
|
border-bottom: 1px solid var(--ton-linie-satt);
|
|
font-size: .7rem;
|
|
letter-spacing: .13em;
|
|
text-transform: uppercase;
|
|
color: var(--ton-schrift-3);
|
|
text-align: left;
|
|
}
|
|
|
|
/* Die Summenzeile traegt das Gewicht, die Teile darunter nicht. */
|
|
.jahresstatistik .tafel tr.summe td { font-size: .95rem; }
|
|
.jahresstatistik .tafel tr.summe td.name { font-weight: 500; }
|
|
.jahresstatistik .tafel tr.summe .wert-jetzt { color: var(--ton-sonne); }
|
|
|
|
/* Einrueckung: "davon" teilt die Summe auf, "darin" greift etwas heraus, das
|
|
schon in einer Zeile darueber steckt. */
|
|
.jahresstatistik .tafel td.name .stufe-1 {
|
|
padding-left: 1.4rem;
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
.jahresstatistik .tafel td.name .stufe-1::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: .45rem;
|
|
top: .75em;
|
|
width: .55rem;
|
|
border-top: 1px solid var(--ton-linie-satt);
|
|
}
|
|
.jahresstatistik .tafel tr.teilmenge td { color: var(--ton-schrift-2); font-size: .82rem; }
|
|
|
|
/* Zweite Stufe. Der Strich sitzt tiefer eingerueckt als der der ersten und
|
|
zeigt damit, an welcher Zeile die Teilmenge haengt. */
|
|
.jahresstatistik .tafel td.name .stufe-2 {
|
|
padding-left: 2.8rem;
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
.jahresstatistik .tafel td.name .stufe-2::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 1.85rem;
|
|
top: .75em;
|
|
width: .55rem;
|
|
border-top: 1px dashed var(--ton-linie-satt);
|
|
}
|
|
.jahresstatistik .tafel .darin {
|
|
font-size: .68rem;
|
|
letter-spacing: .06em;
|
|
text-transform: uppercase;
|
|
color: var(--ton-schrift-3);
|
|
margin-right: .4rem;
|
|
}
|
|
.jahresstatistik .tafel .einheit-klein {
|
|
font-size: .7rem;
|
|
color: var(--ton-schrift-3);
|
|
margin-left: .4rem;
|
|
}
|
|
|
|
.jahresstatistik .tafel .wert-alt { color: var(--ton-schrift-2); }
|
|
.jahresstatistik .tafel .leer { color: var(--ton-schrift-3); text-align: left; }
|
|
|
|
/* Die Gesamtspalte ist eine andere Art Zahl als ein Jahreswert - sie steht
|
|
deshalb abgesetzt und ruhiger. */
|
|
.jahresstatistik .tafel .gesamt {
|
|
border-left: 1px solid var(--ton-linie-satt);
|
|
color: var(--ton-schrift-2);
|
|
}
|
|
.jahresstatistik .tafel thead th.gesamt { color: var(--ton-schrift-2); }
|
|
|
|
/* Aufklappbare Teilzeilen */
|
|
.jahresstatistik .tafel button.klapp {
|
|
appearance: none;
|
|
background: none;
|
|
border: 0;
|
|
color: inherit;
|
|
font: inherit;
|
|
padding: 0;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .2rem .4rem;
|
|
text-align: left;
|
|
}
|
|
.jahresstatistik .tafel button.klapp .pfeil {
|
|
display: inline-block;
|
|
width: .55rem;
|
|
color: var(--ton-schrift-3);
|
|
transition: transform .18s;
|
|
}
|
|
.jahresstatistik .tafel button.klapp[aria-expanded="true"] .pfeil { transform: rotate(90deg); }
|
|
|
|
/* Was hinter einer zugeklappten Zeile steckt - sonst waere das Zugeklappte
|
|
nicht nur zusammengefasst, sondern verschwunden. */
|
|
.jahresstatistik .tafel .posten {
|
|
margin-left: .55rem;
|
|
padding: .05rem .45rem;
|
|
border: 1px solid var(--ton-linie-satt);
|
|
border-radius: 1rem;
|
|
font-size: .68rem;
|
|
font-weight: 400;
|
|
letter-spacing: .03em;
|
|
color: var(--ton-schrift-3);
|
|
white-space: nowrap;
|
|
}
|
|
@media (hover: hover) {
|
|
.jahresstatistik .tafel button.klapp:hover .posten {
|
|
border-color: var(--ton-sonne);
|
|
color: var(--ton-sonne);
|
|
}
|
|
}
|
|
.jahresstatistik .tafel tr.versteckt { display: none; }
|
|
|
|
/* --- Monatsverlauf ------------------------------------------------------- */
|
|
.jahresstatistik .tafel th.verlauf,
|
|
.jahresstatistik .tafel td.verlauf { width: 7.5rem; }
|
|
.jahresstatistik .spark {
|
|
display: block;
|
|
/* Waagerecht faehrt der Finger die Monate ab, senkrecht scrollt die Seite
|
|
weiter - ohne diese Angabe verschluckt der Browser das Wischen. */
|
|
touch-action: pan-y;
|
|
}
|
|
.jahresstatistik .spark rect.balken { fill: var(--ton-linie-satt); transition: fill .15s; }
|
|
.jahresstatistik .spark rect.balken.jetzt { fill: var(--ton-sonne); }
|
|
.jahresstatistik .spark rect.balken.aktiv { fill: var(--ton-sonne); }
|
|
.jahresstatistik .spark line.basis { stroke: var(--ton-linie); stroke-width: 1; }
|
|
|
|
/* Die ganze Zelle nimmt die Beruehrung an, nicht nur die Balken darin -
|
|
ein SVG-Wurzelelement laesst sich nicht mit Innenabstand vergroessern. */
|
|
.jahresstatistik .tafel td.verlauf { touch-action: pan-y; cursor: crosshair; }
|
|
|
|
.spark-tipp {
|
|
position: fixed;
|
|
z-index: 1080;
|
|
pointer-events: none;
|
|
background: #12161a;
|
|
border: 1px solid var(--ton-linie-satt);
|
|
border-radius: .35rem;
|
|
padding: .3rem .55rem;
|
|
font-size: .75rem;
|
|
font-variant-numeric: tabular-nums;
|
|
box-shadow: 0 .4rem 1.2rem rgba(0, 0, 0, .5);
|
|
opacity: 0;
|
|
transition: opacity .12s;
|
|
}
|
|
.spark-tipp.an { opacity: 1; }
|
|
|
|
/* --- Fussnoten ----------------------------------------------------------- */
|
|
.jahresstatistik .fussnoten {
|
|
display: grid;
|
|
gap: .5rem;
|
|
font-size: .78rem;
|
|
color: var(--ton-schrift-2);
|
|
max-width: 68ch;
|
|
}
|
|
.jahresstatistik .fussnoten p { margin: 0; }
|
|
.jahresstatistik .fussnoten b { color: var(--ton-schrift); font-weight: 500; }
|
|
|
|
@media (max-width: 575.98px) {
|
|
.jahresstatistik .tafel tbody td.name { min-width: 11rem; }
|
|
.jahresstatistik .werkzeuge { gap: .6rem .9rem; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.jahresstatistik .umschalter button,
|
|
.jahresstatistik .jahrwahl button,
|
|
.jahresstatistik .spark rect.balken,
|
|
.jahresstatistik .tafel button.klapp .pfeil,
|
|
.spark-tipp { transition: none; }
|
|
}
|
|
|
|
|
|
.winddir {
|
|
position:absolute;
|
|
left:60%;
|
|
width:25%;
|
|
height:40%;
|
|
padding: 0;
|
|
background-image: url('../assets/img/arrow.svg');
|
|
background-size:contain;
|
|
background-repeat: no-repeat;
|
|
background-position: center;
|
|
}
|
|
|
|
|
|
/* Schieberegler in den Modals (Autoladung, Heizstab, Thermostat).
|
|
Der gefuellte Anteil kommt aus --background-size, gesetzt von
|
|
bindModalSlider() in js/solar/common.js. */
|
|
input[type='range']::-webkit-slider-runnable-track {
|
|
background: linear-gradient(to right, #00788F, #00788F), #D7D7D7;
|
|
background-size: var(--background-size, 0%) 100%;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
/* Diagramme (Chart.js). Die Hoehe kommt aus diesem Container, nicht aus dem
|
|
canvas - die Charts laufen mit maintainAspectRatio: false. Auf dem Handy
|
|
ist der Container nur rund 300 px breit; mit 400 px Hoehe wird aus einer
|
|
Zeitreihe ein Hochformat, in dem die Zeitachse die kurze Seite ist. */
|
|
.chart-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 400px;
|
|
}
|
|
|
|
@media (max-width: 767.98px) {
|
|
.chart-container {
|
|
height: 250px;
|
|
}
|
|
}
|
|
|
|
/* Auf dem Handy gehen von 375 px rund 75 an Innenabstaende verloren, und
|
|
zwischen den Karten stehen 24 px. Beides wird hier enger gefasst - nur
|
|
im Inhaltsbereich, damit Seitenleiste, Kopfzeile und Modals unberuehrt
|
|
bleiben. Die Abstandsklassen von Bootstrap (mb-4) tragen !important,
|
|
deshalb steht es auch hier. */
|
|
@media (max-width: 767.98px) {
|
|
.app-content {
|
|
padding: 0 .25rem;
|
|
}
|
|
|
|
.app-content-header {
|
|
padding: .5rem .25rem;
|
|
}
|
|
|
|
.app-content .row {
|
|
--bs-gutter-x: .5rem;
|
|
}
|
|
|
|
.app-content .card {
|
|
margin-bottom: .75rem !important;
|
|
}
|
|
|
|
/* Nur der Standardwert wird kleiner; eigene Angaben im Markup (p-0 fuer
|
|
das Realtime-SVG) gelten weiter. */
|
|
.app-content .card-body {
|
|
padding: .5rem;
|
|
}
|
|
|
|
/* Die Jahrestabelle scrollt auf dem Handy seitwaerts; damit moeglichst
|
|
viel davon ohne Schieben zu sehen ist, ruecken die Spalten enger
|
|
zusammen. */
|
|
.jahresstatistik .tafel tbody td,
|
|
.jahresstatistik .tafel thead th {
|
|
padding-inline: .55rem;
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------
|
|
Drei Klassen, die der Automatik-Editor gebraucht hat - hier, nicht dort
|
|
|
|
Sie stehen absichtlich in der gemeinsamen Datei und nicht als Sonderfall
|
|
im Editor. Zwei davon sind allgemein genug, um anderswo zu taugen; die
|
|
dritte ist eine echte Ausnahme und als einzelne Klasse auch als solche
|
|
erkennbar.
|
|
-------------------------------------------------------------------------- */
|
|
|
|
/* Ein Auswahlfeld, das wie Text aussieht, bis man es anfaehrt.
|
|
|
|
Eine Bedingung hat sechs Bestandteile, aber nur zwei davon traegt man
|
|
wirklich ein: Vergleich und Wert. Standen alle sechs als gleich schwere
|
|
Kaesten nebeneinander, sagte nichts mehr, worauf es ankommt. Die
|
|
gepunktete Linie sagt "anklickbar", ohne den Platz und das Gewicht eines
|
|
Eingabefelds zu nehmen.
|
|
|
|
Bewusst allgemein: dieselbe Not haben das Raum-Modal und die
|
|
Kachel-Einstellungen, wo ebenfalls jede Kleinigkeit in einem Kasten
|
|
steht. Taugt fuer <select> wie fuer <button>. */
|
|
.feld-als-text {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
max-width: 100%;
|
|
padding: 0 0 1px;
|
|
border: 0;
|
|
border-bottom: 1px dashed var(--ton-linie-satt);
|
|
border-radius: 0;
|
|
background: none;
|
|
color: inherit;
|
|
font: inherit;
|
|
text-align: left;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.feld-als-text:hover,
|
|
.feld-als-text:focus-visible {
|
|
border-bottom-color: var(--bs-link-color);
|
|
color: var(--bs-emphasis-color);
|
|
outline: 0;
|
|
}
|
|
|
|
/* Ein Messwert, wie er in diesem Moment dasteht.
|
|
|
|
Steht neben einer Bedingung und beantwortet die Frage, die man wirklich
|
|
hat: "warum laeuft die nicht?". Das ist die Ausnahme unter den dreien -
|
|
Bedingungen gibt es sonst nirgends auf der Seite. */
|
|
.wert-jetzt {
|
|
font-size: .8125rem;
|
|
color: var(--ton-schrift-3);
|
|
font-variant-numeric: tabular-nums;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.wert-jetzt b { font-weight: 400; color: var(--ton-schrift-2); }
|
|
.wert-jetzt.erfuellt b { color: var(--ton-gut); }
|
|
|
|
/* Erfuellt oder noch nicht. Bewusst kein Rot: eine Bedingung, die gerade
|
|
nicht zutrifft, ist der Normalfall und kein Fehler. */
|
|
.punkt {
|
|
display: inline-block;
|
|
width: .7rem;
|
|
height: .7rem;
|
|
border: 1.5px solid var(--ton-schrift-3);
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.punkt.an {
|
|
border-color: var(--ton-gut);
|
|
background: var(--ton-gut);
|
|
}
|
|
|
|
/* Atmet leise, wo etwas mitlaeuft - ein Wort "live" daneben braucht es dann
|
|
nicht. */
|
|
.punkt.atmet { animation: punkt-atmen 2.4s ease-in-out infinite; }
|
|
|
|
@keyframes punkt-atmen {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: .35; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.punkt.atmet { animation: none; }
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------
|
|
Editor der Automatiken: die Bedingungszeile
|
|
|
|
Frueher eine input-group aus sechs gleich schweren Kaesten. Jetzt ein
|
|
Raster, in dem Geraet und Messwert uebereinander stehen (das Geraet
|
|
beantwortet "woher", nicht "was") und nur Vergleich und Wert noch wie
|
|
Eingabefelder aussehen.
|
|
|
|
Auf schmalen Schirmen bricht die Zeile in zwei: oben, worum es geht,
|
|
unten der Vergleich. Frueher schrumpften die Felder stattdessen mit, bis
|
|
von "Thermostat OG Bad" ein "Therm" uebrig war.
|
|
-------------------------------------------------------------------------- */
|
|
.auto-zeile {
|
|
display: grid;
|
|
grid-template-columns: 1.75rem minmax(0, 1fr) 5.25rem 8rem 8.5rem .75rem 1.75rem;
|
|
grid-template-areas: "sym was op wert jetzt punkt weg";
|
|
gap: .625rem;
|
|
align-items: center;
|
|
padding: .45rem .55rem;
|
|
border-radius: var(--ton-radius-klein);
|
|
}
|
|
|
|
.auto-zeile:nth-of-type(even) { background: rgba(255, 255, 255, .022); }
|
|
|
|
.auto-zeile > .auto-symbol { grid-area: sym; color: var(--ton-schrift-2); }
|
|
.auto-zeile > .auto-was { grid-area: was; min-width: 0; }
|
|
.auto-zeile > .auto-op { grid-area: op; }
|
|
.auto-zeile > .auto-wert { grid-area: wert; }
|
|
.auto-zeile > .wert-jetzt { grid-area: jetzt; }
|
|
.auto-zeile > .punkt { grid-area: punkt; }
|
|
.auto-zeile > .auto-weg { grid-area: weg; }
|
|
.auto-zeile > .auto-werte { grid-area: werte; }
|
|
|
|
/* Die Aktionszeile: dasselbe Raster, andere Spalten. Statt Vergleich, Wert
|
|
und laufendem Messwert steht dort ein Block mit den Parametern des
|
|
Kommandos - mal keiner ("Auf"), mal zwei ("Position+Neigung"). */
|
|
.auto-zeile.tat {
|
|
grid-template-columns: 1.75rem minmax(0, 1fr) auto 1.75rem;
|
|
grid-template-areas: "sym was werte weg";
|
|
}
|
|
|
|
/* Eine Zeile ohne Geraet: dann gibt es nur eines zu tun, und das darf die
|
|
ganze Breite haben. Vergleich, Wert und der laufende Messwert gibt es in
|
|
diesem Zustand noch gar nicht - ihre Spalten stuenden sonst nur leer
|
|
herum, waehrend die eine Schaltflaeche in eine schmale Spalte gequetscht
|
|
waere. */
|
|
.auto-zeile.leer { grid-template-areas: "sym was was was was punkt weg"; }
|
|
|
|
/* Geraet klein und matt ueber dem Messwert - und trotzdem anklickbar. */
|
|
.auto-was { display: flex; flex-direction: column; gap: .05rem; }
|
|
|
|
/* Nur in der Bedingungszeile: die Aktionszeile benutzt .auto-geraet
|
|
weiterhin als vollwertige Auswahlflaeche und darf davon nichts
|
|
abbekommen. */
|
|
/* Nicht in der leeren Zeile: dort traegt derselbe Knopf zusaetzlich
|
|
.feld-als-text und ist die einzige Handlung, die es gibt - klein und matt
|
|
waere er dort genau die Falle, die er vorher war. Die Klasse .auto-geraet
|
|
behaelt er trotzdem, weil daran haengt, WAS er ist: der Klick-Horcher
|
|
erkennt ihn daran. Verhalten und Aussehen sind absichtlich zwei
|
|
verschiedene Klassen. */
|
|
.auto-was > .auto-geraet:not(.feld-als-text) {
|
|
align-self: start;
|
|
max-width: 100%;
|
|
padding: 0;
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ton-schrift-3);
|
|
font-size: .72rem;
|
|
line-height: 1.3;
|
|
text-align: left;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.auto-was > .auto-geraet:not(.feld-als-text):hover,
|
|
.auto-was > .auto-geraet:not(.feld-als-text):focus-visible {
|
|
color: var(--bs-link-color);
|
|
outline: 0;
|
|
}
|
|
|
|
.auto-messwert { align-self: start; }
|
|
|
|
/* Der Papierkorb erscheint erst beim Ueberfahren. Acht rote Knoepfe im
|
|
Ruhezustand zogen alle Aufmerksamkeit auf die seltenste Handlung.
|
|
Wo es kein Ueberfahren gibt, steht er dauerhaft da. */
|
|
.auto-zeile > .auto-weg {
|
|
padding: 0;
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ton-schrift-3);
|
|
opacity: 0;
|
|
transition: opacity .15s ease, color .15s ease;
|
|
}
|
|
|
|
.auto-zeile:hover > .auto-weg,
|
|
.auto-zeile:focus-within > .auto-weg { opacity: 1; }
|
|
.auto-zeile > .auto-weg:hover { color: var(--ton-schlecht); }
|
|
|
|
@media (hover: none) {
|
|
.auto-zeile > .auto-weg { opacity: 1; }
|
|
}
|
|
|
|
/* Ein Segment je Bedingung, im Kopf des Bandes und ueber jeder Gruppe.
|
|
Eine Zahl allein ("2 von 4") liest man, die Reihe sieht man. */
|
|
.auto-segment {
|
|
width: .8rem;
|
|
height: .3rem;
|
|
border-radius: .15rem;
|
|
background: var(--ton-linie-satt);
|
|
}
|
|
|
|
.auto-segment.an { background: var(--ton-gut); }
|
|
|
|
/* Was der Browser nicht entscheiden kann, bleibt hohl - siehe ZEITARTEN in
|
|
autoActionFuncs.js. */
|
|
.auto-segment.offen {
|
|
background: none;
|
|
box-shadow: inset 0 0 0 1px var(--ton-linie-satt);
|
|
}
|
|
|
|
@media (max-width: 767.98px) {
|
|
.auto-zeile {
|
|
grid-template-columns: 1.75rem 5.25rem minmax(0, 1fr) .75rem 1.75rem;
|
|
/* Drei Zeilen statt zwei: der laufende Wert quetschte sich sonst mit
|
|
dem Wertfeld in dieselbe Reihe und blieb als "gerad…" stehen. Auf
|
|
einem Handy ist Hoehe billig und Breite teuer. */
|
|
grid-template-areas:
|
|
"sym was was punkt weg"
|
|
"sym op wert wert wert"
|
|
"sym jetzt jetzt jetzt jetzt";
|
|
row-gap: .35rem;
|
|
}
|
|
|
|
.auto-zeile.leer { grid-template-areas: "sym was was punkt weg"; }
|
|
|
|
.auto-zeile.tat {
|
|
grid-template-columns: 1.75rem minmax(0, 1fr) 1.75rem;
|
|
grid-template-areas:
|
|
"sym was weg"
|
|
"sym werte werte";
|
|
}
|
|
|
|
.auto-zeile.tat.leer { grid-template-areas: "sym was weg"; }
|
|
}
|
|
|
|
/* Raum-Modal. Ein Kommando mit mehreren Parametern ("Position+Neigung")
|
|
traegt Beschriftung, zwei Zahlenfelder und den Ausloeseknopf in einer
|
|
input-group - auf einem Handy schrumpfen die Felder dabei auf Nullbreite.
|
|
Eine Mindestbreite laesst die Zeile stattdessen umbrechen. */
|
|
.input-group.raum-cmd > .form-control,
|
|
.input-group.raum-cmd > .form-select {
|
|
min-width: 5rem;
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------
|
|
Raum-Modal: Bedienelemente
|
|
Erzeugt von restricted/roomControls.php, verdrahtet in js/solar/homeMQTT.js.
|
|
-------------------------------------------------------------------------- */
|
|
|
|
/* Die Geraetegruppen als Reiter. Auf einem Handy passen fuenf
|
|
Beschriftungen nicht nebeneinander - dann scrollt die Leiste seitwaerts,
|
|
statt die Namen umzubrechen. Die durchgehende Linie unter den Reitern
|
|
darf dabei nicht mitscrollen, sie gehoert an den unteren Rand. */
|
|
|
|
/* Waehrend ein Kommando unterwegs ist. Ein "Zu" an einer Jalousie dauert
|
|
ueber eine Minute; ohne sichtbare Sperre tippt man in der Zeit nach. */
|
|
.raum-geraet.raum-warte {
|
|
opacity: 0.55;
|
|
pointer-events: none;
|
|
}
|
|
.raum-geraet.raum-warte::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
right: 0.5rem;
|
|
width: 0.9rem;
|
|
height: 0.9rem;
|
|
border: 2px solid currentColor;
|
|
border-right-color: transparent;
|
|
border-radius: 50%;
|
|
animation: raum-dreht 0.8s linear infinite;
|
|
}
|
|
.raum-geraet {
|
|
position: relative;
|
|
}
|
|
@keyframes raum-dreht {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------
|
|
Regler im Raum-Modal
|
|
|
|
Eigenes Aussehen statt des Bootstrap-Standards: dessen weisse Bahn mit dem
|
|
blauen Griff sticht auf den dunklen Karten heraus, und in einem Raum mit
|
|
drei Jalousien sind das sechs helle Balken. Bahn und Griff tragen deshalb
|
|
dasselbe Blaugruen wie der Temperaturregler, und der ungefuellte Teil ist
|
|
ein Grau, das in beiden Themes traegt.
|
|
|
|
Bewusst nur innerhalb von #roomView: die Regler in den Modals fuer
|
|
Autoladung und Heizstab bleiben, wie sie sind.
|
|
-------------------------------------------------------------------------- */
|
|
#roomView input[type='range'] {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 100%;
|
|
height: 1.5rem;
|
|
padding: 0;
|
|
margin: 0;
|
|
background: transparent;
|
|
}
|
|
#roomView input[type='range']:focus {
|
|
outline: 0;
|
|
}
|
|
/* Getrennt notiert, nicht als Liste: einen Selektor, den er nicht kennt,
|
|
wirft der Browser samt der ganzen Regel weg - und dann bliebe die Bahn
|
|
ungestaltet. */
|
|
#roomView input[type='range']::-webkit-slider-runnable-track {
|
|
height: 0.375rem;
|
|
border: 0;
|
|
border-radius: 0.375rem;
|
|
background: linear-gradient(to right, #00788F, #00788F), rgba(128, 128, 128, 0.32);
|
|
background-size: var(--background-size, 0%) 100%;
|
|
background-repeat: no-repeat;
|
|
}
|
|
#roomView input[type='range']::-moz-range-track {
|
|
height: 0.375rem;
|
|
border: 0;
|
|
border-radius: 0.375rem;
|
|
background: linear-gradient(to right, #00788F, #00788F), rgba(128, 128, 128, 0.32);
|
|
background-size: var(--background-size, 0%) 100%;
|
|
background-repeat: no-repeat;
|
|
}
|
|
#roomView input[type='range']::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 1.05rem;
|
|
height: 1.05rem;
|
|
/* (Bahnhoehe - Griffhoehe) / 2, damit der Griff auf der Bahn sitzt. */
|
|
margin-top: -0.34rem;
|
|
border: 2px solid #fff;
|
|
border-radius: 50%;
|
|
background: #00788F;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
|
}
|
|
#roomView input[type='range']::-moz-range-thumb {
|
|
width: 1.05rem;
|
|
height: 1.05rem;
|
|
border: 2px solid #fff;
|
|
border-radius: 50%;
|
|
background: #00788F;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
|
}
|
|
|
|
/* Schieberegler stehen dicht untereinander (Helligkeit, Farbe). Ohne den
|
|
Abstand kleben Beschriftung und Bahn aneinander.
|
|
Die senkrechten Spalten stehen dagegen nebeneinander - dort wuerde der
|
|
Abstand die zweite Spalte um genau diese 0,35 rem tiefer setzen. */
|
|
.raum-regler + .raum-regler:not(.raum-spalte) {
|
|
margin-top: 0.35rem;
|
|
}
|
|
|
|
/* Messwerte als Gitter statt als Textwurst: ein Stromzaehler hat sechzehn
|
|
Werte, die in einer Zeile niemand findet. auto-fill statt fester Spalten,
|
|
damit auf dem Handy zwei und auf dem Schirm vier nebeneinander passen. */
|
|
.raum-werte {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
|
|
gap: 0.4rem;
|
|
}
|
|
.raum-wert {
|
|
background: rgba(128, 128, 128, 0.08);
|
|
border-radius: 0.35rem;
|
|
padding: 0.3rem 0.45rem;
|
|
overflow: hidden;
|
|
}
|
|
.raum-wert-zahl {
|
|
font-size: 0.95rem;
|
|
font-weight: 600;
|
|
line-height: 1.2;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.raum-wert-einheit {
|
|
font-size: 0.7rem;
|
|
font-weight: 400;
|
|
opacity: 0.6;
|
|
}
|
|
.raum-wert-name {
|
|
font-size: 0.68rem;
|
|
opacity: 0.6;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------
|
|
Raum-Modal: Jalousien
|
|
-------------------------------------------------------------------------- */
|
|
|
|
/* Fahrknoepfe, Hoehe und Neigung nebeneinander. Die Neigung nimmt den Rest -
|
|
sie ist die einzige der drei Spalten, die von Breite profitiert. */
|
|
.raum-jalousie {
|
|
display: flex;
|
|
align-items: stretch;
|
|
justify-content: space-between;
|
|
gap: 0.4rem;
|
|
}
|
|
/* Das Fensterbild steht zwischen Knoepfen und Reglern - schmal, mit Luft
|
|
zu beiden Seiten. Der uebrige Platz verteilt sich auf die Zwischenraeume
|
|
(space-between an .raum-jalousie), statt das Bild breitzuziehen. */
|
|
.raum-jalousie .raum-lamellen {
|
|
flex: 0 1 5rem;
|
|
min-width: 2.2rem;
|
|
margin: 0 0.4rem;
|
|
}
|
|
.raum-jalousie .dropdown {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
/* Auf, Stop, Zu untereinander - die Knoepfe zeigen in die Richtung, in die
|
|
sie fahren. Breit genug fuer einen Daumen: sie sind das, was man im
|
|
Vorbeigehen antippt, die Regler daneben braucht man seltener. */
|
|
.raum-fahrt {
|
|
flex: 0 0 3.1rem;
|
|
}
|
|
.raum-fahrt .btn {
|
|
padding: 0.3rem 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Eine Spalte mit senkrechtem Regler: Name, oberes Wort, Bahn, unteres
|
|
Wort, Zahl. Breit genug fuer "Position" in der kleinen Schrift. */
|
|
.raum-spalte {
|
|
flex: 0 0 3.4rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
.raum-name {
|
|
font-size: 0.62rem;
|
|
opacity: 0.8;
|
|
white-space: nowrap;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
/* Der Kasten, in dem ein gewoehnlicher Regler auf der Seite liegt.
|
|
Gedreht statt writing-mode: so gebaute Regler lassen sich am Handy kaum
|
|
anfassen - das Ziehen wird als Wischen gelesen und blaettert die Seite.
|
|
Gedreht bewegt sich der Regler in seinen eigenen Koordinaten waagerecht,
|
|
und touch-action haelt die Seite still, solange der Finger auf ihm liegt.
|
|
rotate(90deg) legt den kleinen Wert nach oben: "oben" ist 0 %. */
|
|
.raum-senkrecht {
|
|
position: relative;
|
|
width: 1.6rem;
|
|
height: 6.5rem;
|
|
margin: 0.1rem 0;
|
|
}
|
|
#roomView .raum-senkrecht input[type='range'] {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 6.5rem; /* = Hoehe des Kastens, die Laenge der Bahn */
|
|
height: 1.6rem; /* = Breite des Kastens, die Flaeche zum Treffen */
|
|
transform: translate(-50%, -50%) rotate(90deg);
|
|
touch-action: none;
|
|
}
|
|
|
|
/* Die Endstellungen in Worten statt in Prozent. */
|
|
.raum-ende {
|
|
font-size: 0.68rem;
|
|
opacity: 0.65;
|
|
line-height: 1.1;
|
|
}
|
|
|
|
/* Die Lamellen zur Neigung. Ihre Hoehe setzt lamellenZeichnen() in
|
|
js/solar/homeMQTT.js - schmal mit Luft dazwischen heisst offen,
|
|
aneinanderliegend heisst geschlossen. */
|
|
.raum-lamellen {
|
|
display: block;
|
|
align-self: center;
|
|
height: 6.9rem;
|
|
/* Der Rahmen macht aus dem Streifen ein Fenster - ohne ihn liest sich das
|
|
geschlossene Bild wie eine zweite Reglerbahn. */
|
|
background: rgba(0, 120, 143, 0.14);
|
|
border: 1px solid rgba(128, 128, 128, 0.35);
|
|
border-radius: 0.2rem;
|
|
}
|
|
.raum-lamelle {
|
|
fill: currentColor;
|
|
opacity: 0.6;
|
|
stroke: rgba(0, 0, 0, 0.35);
|
|
stroke-width: 0.6;
|
|
vector-effect: non-scaling-stroke;
|
|
}
|
|
.raum-endschiene {
|
|
fill: currentColor;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
/* --- Einstellungen: Preistabellen ------------------------------------ */
|
|
|
|
/* Der Spaltenkopf steht ueber der ersten Zeile und muss deren Einrueckung
|
|
treffen, sonst rutschen Beschriftung und Feld auseinander. Er bleibt
|
|
einzeilig, braucht also keinen senkrechten Gutter - und ohne den entfaellt
|
|
auch Bootstraps negative Marge von selbst. */
|
|
.preis-kopf {
|
|
--bs-gutter-y: 0;
|
|
padding: 0 0.25rem 0.35rem;
|
|
}
|
|
|
|
/* Eine Zeile ist selbst eine .row mit g-2. Bootstrap zieht so eine Reihe um
|
|
die Gutter-Hoehe nach oben und gibt jeder Spalte dieselbe Marge nach unten
|
|
zurueck - gedacht fuer Reihen, die nebeneinander im Raster stehen. Hier
|
|
stehen sie untereinander, und der Zug nach oben frisst den unteren
|
|
Innenabstand der vorigen Zeile auf: auf dem Handy, wo der Papierkorb in
|
|
einer eigenen Zeile landet, klebte er am Trennstrich.
|
|
|
|
Die senkrechte Gutter-Marge wird hier gebraucht - sie haelt die umbrochenen
|
|
Feldzeilen auseinander -, der Zug nach oben nicht. Die Regel braucht eine
|
|
Klasse mehr als Bootstraps .row: solar.css wird vor AdminLTE geladen und
|
|
verliert bei gleicher Spezifitaet. */
|
|
.preis-zeilen .preis-zeile {
|
|
margin-top: 0;
|
|
}
|
|
|
|
/* Oben um die Gutter-Hoehe weniger Innenabstand, damit ueber der ersten und
|
|
unter der letzten Feldzeile gleich viel Luft steht. */
|
|
.preis-zeile {
|
|
padding: 0.15rem 0.25rem 0.65rem;
|
|
border-top: 1px solid rgba(128, 128, 128, 0.25);
|
|
}
|
|
/* Die Einheit ist Beiwerk und darf der Zahl nicht den Platz nehmen: hinter
|
|
"kWh/100 km" bliebe in voller Schrift kaum ein Drittel der Spalte fuer das
|
|
Eingabefeld uebrig. */
|
|
.preis-zeile .input-group-text {
|
|
font-size: 0.75rem;
|
|
padding-left: 0.4rem;
|
|
padding-right: 0.4rem;
|
|
}
|
|
|
|
/* Der Zeitraum, der heute gilt - der einzige, den man beim Nachschlagen
|
|
wirklich sucht. */
|
|
.preis-heute {
|
|
background: rgba(0, 120, 143, 0.12);
|
|
border-radius: 0.4rem;
|
|
}
|
|
|
|
|
|
/* ==========================================================================
|
|
Skoda-Seite (restricted/skoda.php)
|
|
|
|
Farben kommen aus den Bootstrap-Variablen des Themes, damit die Seite dem
|
|
Hell-/Dunkelmodus folgt, ohne eine zweite Palette zu pflegen. Nur die
|
|
Bedeutungsfarben stehen fuer sich: bernstein fuer "offen", gelb fuer
|
|
eingeschaltetes Licht, gruen fuer Laden, rot fuer einen leeren Akku.
|
|
|
|
Sie haengen alle an .skoda-seite (dem <main> der Seite) und werden von dort
|
|
vererbt - Fahrzeugbild, Kennzahlen, Balken und Karte holen sie sich also
|
|
aus einer einzigen Quelle. Wo eine Farbe mit Deckkraft gebraucht wird,
|
|
steht sie zusaetzlich als RGB-Tripel bereit, denn rgba() kann eine
|
|
Hex-Variable nicht aufloesen.
|
|
========================================================================== */
|
|
|
|
.skoda-seite {
|
|
--skoda-offen: #e0904f; /* offene Tuer, Klappe, Fenster */
|
|
--skoda-licht: #f2d16b; /* eingeschaltetes Licht */
|
|
--skoda-laden: #4caf7d; /* Laden, gesunder Ladestand */
|
|
--skoda-laden-rgb: 76, 175, 125;
|
|
--skoda-niedrig: #e06060; /* Ladestand unter 15 Prozent */
|
|
--skoda-ruhe: #7aa7c7; /* Ladestand ohne Ladung */
|
|
--skoda-ruhe-rgb: 122, 167, 199;
|
|
--skoda-pv: #f0b429; /* Strom aus der eigenen Anlage */
|
|
--skoda-netz: #7a8791; /* Strom aus dem Netz */
|
|
--skoda-blech: 128, 138, 148; /* alles Unbeteiligte am Fahrzeug */
|
|
}
|
|
|
|
/* --- Reiter --------------------------------------------------------------
|
|
Eine Bauart fuer die ganze Seite: Einstellungen, Raum-Modal, Automatiken.
|
|
Gebaut werden sie von restricted/reiter.php, umgeschaltet von Bootstrap.
|
|
|
|
Statt der Ordnerlaschen von Bootstrap eine Unterstreichung: die Laschen
|
|
brauchen einen Rahmen um die Flaeche darunter, damit sie nicht in der Luft
|
|
haengen - und der Rahmen ist genau das, was diese Oberflaeche sonst
|
|
nirgends hat. Der aktive Reiter traegt die Akzentfarbe.
|
|
|
|
Jede Regel nennt .nav-tabs UND .reiter, obwohl .reiter allein die Leiste
|
|
schon trifft. Das ist noetig, nicht schmueckend: adminlte.min.css wird nach
|
|
dieser Datei geladen und bringt eigene Regeln fuer .nav-tabs .nav-link mit.
|
|
Bei gleicher Spezifitaet gewaenne die spaetere - der aktive Reiter waere
|
|
weiss statt bernstein. */
|
|
.nav-tabs.reiter {
|
|
border-bottom: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
gap: .1rem;
|
|
}
|
|
.nav-tabs.reiter .nav-link {
|
|
border: 0;
|
|
border-bottom: 2px solid transparent;
|
|
border-radius: .35rem .35rem 0 0;
|
|
padding: .45rem .9rem;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
background: transparent;
|
|
transition: color .18s, background .18s, border-color .18s;
|
|
}
|
|
@media (hover: hover) {
|
|
.nav-tabs.reiter .nav-link:hover {
|
|
background: rgba(138, 148, 158, .08);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
border-bottom-color: var(--ton-linie-satt, rgba(138, 148, 158, .38));
|
|
}
|
|
}
|
|
.nav-tabs.reiter .nav-link.active {
|
|
background: transparent;
|
|
color: var(--ton-sonne, #f0b429);
|
|
border-bottom-color: var(--ton-sonne, #f0b429);
|
|
}
|
|
.nav-tabs.reiter .nav-link:focus-visible {
|
|
outline: 2px solid var(--ton-sonne, #f0b429);
|
|
outline-offset: -2px;
|
|
}
|
|
.nav-tabs.reiter .nav-link i { margin-right: .5rem; }
|
|
|
|
/* Schmale Fassung fuer enge Stellen (Raum-Modal): kleinere Reiter, die
|
|
waagerecht scrollen statt umzubrechen - eine Zeile bleibt eine Zeile. */
|
|
.nav-tabs.reiter-schmal {
|
|
flex-wrap: nowrap;
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
scrollbar-width: none;
|
|
}
|
|
.nav-tabs.reiter-schmal::-webkit-scrollbar { display: none; }
|
|
.nav-tabs.reiter-schmal .nav-link {
|
|
padding: .3rem .7rem;
|
|
font-size: .8rem;
|
|
white-space: nowrap;
|
|
}
|
|
.nav-tabs.reiter-schmal .nav-link i { margin-right: .3rem; }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.nav-tabs.reiter .nav-link { transition: none; }
|
|
}
|
|
|
|
/* --- Aufbau der Fahrzeugkachel -------------------------------------------
|
|
Links das Bild, rechts die Zahlen dazu. Die Kachel bricht um, sobald die
|
|
Zahlenspalte unter 24rem faellt; das Bild behaelt dann seine Breite und
|
|
rueckt in die Mitte. Die drei Bloecke rechts verteilen sich ueber die Hoehe
|
|
des Bildes, damit die Kachel unten nicht ausfranst. */
|
|
.skoda-fahrzeug {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: clamp(1rem, 3vw, 2.5rem);
|
|
align-items: stretch;
|
|
}
|
|
.skoda-bild {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
/* Die Zahlenspalte laeuft nicht ueber die ganze Breite mit: ein Balken von
|
|
einem Meter Laenge teilt nicht besser als einer von dreissig Zentimetern.
|
|
Auf sehr breiten Schirmen steht die Kachel deshalb mittig statt gedehnt. */
|
|
/* Mittig statt oben buendig: ohne Ladung faellt der Ladestrombalken weg, und
|
|
die uebrigen Bloecke sollen sich dann gleichmaessig neben dem Fahrzeug
|
|
verteilen statt eine Luecke in der Mitte zu lassen. */
|
|
.skoda-daten {
|
|
flex: 1 1 24rem;
|
|
max-width: 48rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
/* Kleine Beschriftung ueber einem Block oder einer Zahl. Einmal beschrieben,
|
|
von Kennzahlen, Ladestrom und Zustandszeile gemeinsam benutzt. */
|
|
.skoda-marke,
|
|
.kennzahl .k,
|
|
.skoda-fahrleistung .posten .k {
|
|
font-size: .7rem;
|
|
letter-spacing: .09em;
|
|
text-transform: uppercase;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
}
|
|
|
|
/* --- Fahrzeug in Draufsicht ----------------------------------------------
|
|
Massstaeblich: 4648 x 1877 mm ergeben das Seitenverhaeltnis 2,48:1, der
|
|
Radstand von 2765 mm die Lage der Raeder. */
|
|
.skoda-auto {
|
|
width: 100%;
|
|
max-width: 297px; /* 294,4 Einheiten viewBox, eine Einheit ein Bildpunkt */
|
|
height: auto;
|
|
}
|
|
|
|
.skoda-auto .karosserie {
|
|
fill: rgba(var(--skoda-blech), 0.17);
|
|
stroke: rgba(var(--skoda-blech), 0.55);
|
|
stroke-width: 2;
|
|
}
|
|
|
|
/* --- Strasse ------------------------------------------------------------
|
|
Sichtbar nur waehrend der Fahrt, und sie traegt die eigentliche Bewegung.
|
|
|
|
Die Masse stehen im SVG: Leitlinie 6 m Strich auf 12 m Luecke, rechts
|
|
durchgezogen, das Ganze im Viertel des Fahrzeugmassstabs. Hier bleibt die
|
|
Bewegung: eine Umdrehung ist eine Strich-Lueckenlaenge (472,5 Einheiten),
|
|
in 1,08 s - das sind 437 Einheiten je Sekunde und in diesem Massstab gut
|
|
60 km/h. Der Takt der vorbeiziehenden Striche bleibt damit derselbe wie
|
|
zuvor, die Bewegung selbst ist ruhiger.
|
|
|
|
Nur die Leitlinie laeuft. Die durchgezogene Linie hat nichts, woran man
|
|
Bewegung saehe - genau wie in echt. */
|
|
.skoda-auto .strasse { display: none; }
|
|
.skoda-auto.faehrt .strasse { display: block; }
|
|
|
|
.skoda-auto .strasse .belag {
|
|
fill: rgba(8, 10, 12, 0.35);
|
|
}
|
|
|
|
.skoda-auto .strasse line {
|
|
stroke: rgba(232, 237, 233, 0.5);
|
|
stroke-width: 4.2; /* etwas breiter als massstaeblich (3,15), damit
|
|
sich die Markierung vom Fahrtwind abhebt */
|
|
}
|
|
|
|
.skoda-auto .strasse .leitlinie {
|
|
stroke-dasharray: 157.5 315; /* 6 m Strich, 12 m Luecke, geviertelt */
|
|
animation: skoda-leitlinie 1.08s linear infinite;
|
|
}
|
|
|
|
@keyframes skoda-leitlinie {
|
|
from { transform: translateY(0); }
|
|
to { transform: translateY(472.5px); }
|
|
}
|
|
|
|
/* --- Bananenschale ------------------------------------------------------
|
|
Der Ostergruss. Ausgeloest wird er selten und nur waehrend der Fahrt (siehe
|
|
bananeVielleicht() in js/solar/skodaMQTT.js); hier steht, wie er aussieht.
|
|
|
|
Zwei Bewegungen greifen ineinander: die Schale laeuft mit der Strasse mit,
|
|
und wenn sie unter der Vorderachse ist, kommt der Wagen ins Schleudern. Der
|
|
Vorlauf der zweiten Bewegung ist deshalb kein Gefuehl, sondern gerechnet:
|
|
die Schale startet 40 Einheiten ueber dem Bild, die Vorderachse liegt bei
|
|
97, macht 137 Einheiten - bei 437 Einheiten je Sekunde sind das 0,31 s.
|
|
|
|
Der Wagen dreht sich um seinen Schwerpunkt, nicht um die Bildmitte; ohne
|
|
transform-box: view-box bezoege sich transform-origin auf den Rahmen der
|
|
Gruppe und der Bogen saehe falsch aus. */
|
|
.skoda-auto .bananenschale { display: none; }
|
|
.skoda-auto.banane .bananenschale {
|
|
display: block;
|
|
animation: skoda-schale 1.5s linear;
|
|
}
|
|
.skoda-auto .bananenschale use {
|
|
fill: #e8c74a;
|
|
stroke: rgba(120, 92, 12, 0.75);
|
|
stroke-width: 0.8;
|
|
}
|
|
.skoda-auto .bananenschale .strunk { fill: #b8912c; }
|
|
|
|
.skoda-auto.banane #wagen {
|
|
transform-box: view-box;
|
|
transform-origin: 112px 250px;
|
|
animation: skoda-schleudern 1.2s 0.31s ease-in-out;
|
|
}
|
|
|
|
@keyframes skoda-schale {
|
|
from { transform: translateY(-40px); }
|
|
to { transform: translateY(560px); }
|
|
}
|
|
|
|
/* Erst ein Ausbrechen nach rechts, dann das Gegenlenken, dann austrudeln -
|
|
in dieser Reihenfolge faengt sich ein Fahrzeug wieder. */
|
|
@keyframes skoda-schleudern {
|
|
0% { transform: rotate(0deg) translateX(0); }
|
|
18% { transform: rotate(4.5deg) translateX(5px); }
|
|
45% { transform: rotate(-3.5deg) translateX(-4px); }
|
|
70% { transform: rotate(1.8deg) translateX(2px); }
|
|
88% { transform: rotate(-0.7deg) translateX(-1px); }
|
|
100% { transform: rotate(0deg) translateX(0); }
|
|
}
|
|
|
|
/* --- Fahrtwind ----------------------------------------------------------
|
|
Vier Streifen neben dem Wagen, die von vorn nach hinten laufen. Sichtbar
|
|
nur, solange zeichneFahrt() dem SVG die Klasse "faehrt" gibt.
|
|
|
|
Die Laenge der Streifen steckt im Markup (y1/y2), die Bewegung hier: jede
|
|
Spur laeuft in eigenem Takt und mit eigenem Vorlauf ueber das Bild, sonst
|
|
marschieren die vier im Gleichschritt und es sieht nach Zebrastreifen aus
|
|
statt nach Fahrt. Der Weg reicht von weit oberhalb bis weit unterhalb der
|
|
viewBox (-8 bis 508), damit kein Streifen im Bild entsteht oder endet. */
|
|
.skoda-auto .fahrtwind { display: none; }
|
|
.skoda-auto.faehrt .fahrtwind { display: block; }
|
|
/* Die Streifen ueber dem Wagen liegen ueber den beiden Knoepfen; sie duerfen
|
|
deren Beruehrungen nicht abfangen. */
|
|
.skoda-auto .fahrtwind-oben { pointer-events: none; }
|
|
|
|
.skoda-auto .fahrtwind line {
|
|
stroke: rgba(var(--skoda-blech), 0.75);
|
|
stroke-width: 2.5;
|
|
stroke-linecap: round;
|
|
animation: skoda-fahrtwind 1.1s linear infinite;
|
|
}
|
|
/* Vier Spuren, jede mit eigenem Takt und Vorlauf. Sie laufen so schnell wie
|
|
die Strasse darunter - ein Streifen, der die Fahrbahnmarkierung ueberholt,
|
|
faellt sofort auf. Mit der Strasse halbiert sich also auch ihr Tempo. */
|
|
.skoda-auto .fahrtwind .b1 { animation-duration: 1.56s; animation-delay: -0.60s; opacity: .5; }
|
|
.skoda-auto .fahrtwind .b6 { animation-duration: 1.72s; animation-delay: -1.10s; opacity: .5; }
|
|
|
|
/* Ueber dem Fahrzeug: heller, duenner, durchsichtiger. Der Wind zieht ueber
|
|
das Blech hinweg - er soll sich darauf legen, nicht hineinschneiden. Die
|
|
Streifen stehen dafuer in einer eigenen Gruppe am Ende des SVG (siehe
|
|
dort); ihre geringe Deckung haelt Ladestand und Umrisse lesbar. */
|
|
.skoda-auto .fahrtwind .ueber {
|
|
stroke: rgba(232, 237, 233, 0.85);
|
|
stroke-width: 2;
|
|
}
|
|
.skoda-auto .fahrtwind .b7 { animation-duration: 1.48s; animation-delay: -0.40s; opacity: .34; }
|
|
.skoda-auto .fahrtwind .b10 { animation-duration: 1.64s; animation-delay: -0.90s; opacity: .34; }
|
|
|
|
@keyframes skoda-fahrtwind {
|
|
from { transform: translateY(0); }
|
|
to { transform: translateY(680px); }
|
|
}
|
|
|
|
/* Wer Bewegung abbestellt hat, bekommt sie nicht - aber auch keine leere
|
|
Stelle: die Streifen stehen dann ruhig neben dem Wagen, und das Abzeichen
|
|
"unterwegs" im Kopf der Kachel sagt es ohnehin in Worten. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.skoda-auto .fahrtwind line {
|
|
animation: none;
|
|
transform: translateY(250px);
|
|
opacity: .45;
|
|
}
|
|
/* Auch die Leitlinie bleibt stehen - zu sehen ist dann eine Standaufnahme
|
|
statt einer Fahrt, und das Abzeichen "unterwegs" sagt den Rest. */
|
|
.skoda-auto .strasse .leitlinie {
|
|
animation: none;
|
|
}
|
|
/* Der Ostergruss faellt ganz aus: ein schleuderndes Fahrzeug ist genau die
|
|
Art Bewegung, die abbestellt wurde. Ausgeloest wird er hier gar nicht
|
|
erst - das prueft das Skript zusaetzlich. */
|
|
.skoda-auto.banane .bananenschale,
|
|
.skoda-auto.banane #wagen {
|
|
animation: none;
|
|
}
|
|
.skoda-auto.banane .bananenschale { display: none; }
|
|
}
|
|
|
|
/* Die Reifen liegen als unterste Lage unter der ganzen Karosserie (siehe die
|
|
Schichtung in restricted/skoda.php) und sind schwarz wie am Auto. Unter zwei
|
|
getoenten Flaechen bleibt davon nicht viel uebrig, vorn unter der Haube fast
|
|
nichts - das ist so gewollt. Von oben saehe man die Reifen ohnehin nicht;
|
|
sie stehen im Bild, damit die Achsen einen Ort haben, nicht damit man sie
|
|
liest. */
|
|
.skoda-auto .rad rect { fill: rgba(14, 16, 19, 0.95); }
|
|
.skoda-auto .spiegel rect { fill: rgba(var(--skoda-blech), 0.55); }
|
|
/* Die Kupplung ist schwarz und bleibt es: sie steht hinten heraus und liegt
|
|
dort auf dem Hintergrund, nicht unter dem Blech - sie braucht die
|
|
Aufhellung der Reifen also nicht. */
|
|
.skoda-auto .kupplung rect,
|
|
.skoda-auto .kupplung circle { fill: rgba(10, 12, 14, 0.95); }
|
|
/* Dachspoiler und Reling sind an diesem Fahrzeug schwarz, nicht lackiert -
|
|
und nur dadurch heben sie sich vom Dach ab, auf dem sie liegen. Vorher
|
|
hatten sie den Ton des Blechs und verschwanden darin. */
|
|
.skoda-auto .spoiler { fill: rgba(26, 29, 33, 0.85); }
|
|
|
|
/* Dachreling als Linie mit runden Enden - eine Strichstaerke beschreibt sie
|
|
vollstaendig, und die Enden runden sich von selbst. */
|
|
.skoda-auto .reling line {
|
|
stroke: rgba(26, 29, 33, 0.85);
|
|
stroke-width: 6;
|
|
stroke-linecap: round;
|
|
}
|
|
.skoda-auto .sicke {
|
|
fill: none;
|
|
stroke: rgba(var(--skoda-blech), 0.35);
|
|
stroke-width: 1.5;
|
|
}
|
|
|
|
/* Das Tech-Deck Face: beim Enyaq eine glaenzend schwarze Blende ueber die
|
|
volle Bugbreite. Von oben sieht man ihre Oberkante. */
|
|
/* Die Blende ist glaenzendes Schwarz, aber nicht das dunkelste im Bild: mit
|
|
voller Deckung sprang sie nach vorn und las sich wie ein Loch statt wie eine
|
|
Flaeche. Etwas durchscheinend und mit ruhiger Kante bleibt sie das, was sie
|
|
ist - eine Blende auf dem Blech. */
|
|
.skoda-auto .techdeck {
|
|
fill: rgba(20, 24, 30, 0.66);
|
|
stroke: rgba(var(--skoda-blech), 0.3);
|
|
stroke-width: 1;
|
|
}
|
|
|
|
.skoda-auto .scheibe,
|
|
.skoda-auto .dach {
|
|
fill: rgba(var(--skoda-blech), 0.28);
|
|
stroke: rgba(var(--skoda-blech), 0.45);
|
|
stroke-width: 1.5;
|
|
}
|
|
|
|
/* Grundzustand aller schaltbaren Teile: geschlossen und unauffaellig. */
|
|
.skoda-auto .teil > *,
|
|
.skoda-auto .teil > g > * {
|
|
fill: rgba(var(--skoda-blech), 0.34);
|
|
stroke: rgba(var(--skoda-blech), 0.6);
|
|
stroke-width: 1.5;
|
|
transition: fill .25s, stroke .25s, filter .25s;
|
|
}
|
|
|
|
.skoda-auto .teil-fenster > rect { fill: rgba(var(--skoda-blech), 0.5); }
|
|
|
|
/* Die Heckscheibe liegt in der Heckklappe - sie geht mit ihr auf, also ist
|
|
sie kein eigenes Teil. Aussehen soll sie trotzdem wie Glas und nicht wie
|
|
Blech; die Regel steht nach der allgemeinen und gewinnt damit. */
|
|
.skoda-auto .teil > .scheibe {
|
|
fill: rgba(var(--skoda-blech), 0.28);
|
|
stroke: rgba(var(--skoda-blech), 0.45);
|
|
}
|
|
/* Steht die Klappe offen, faerbt sich auch ihre Scheibe - nur durchsichtiger
|
|
als das Blech, sonst waere das Glas ploetzlich das Kraeftigste am Heck.
|
|
Der Rahmen kommt aus der allgemeinen Regel und ist damit derselbe. */
|
|
.skoda-auto .teil.offen > .scheibe { fill: rgba(224, 144, 79, 0.42); }
|
|
|
|
/* --- Antriebsbatterie ----------------------------------------------------
|
|
Die Platte im Fahrzeugboden ist die Ladestandsanzeige der Seite. Welche
|
|
Farbe sie traegt, steht in einer einzigen Variablen: die Zustandsklasse am
|
|
<svg> setzt --akku-farbe, und Huelle, Fuellung, Pegelkante, Stroemung und
|
|
Verlauf lesen sie von dort. Ein weiterer Zustand ist damit eine Zeile und
|
|
keine Suche durch die Datei. */
|
|
.skoda-auto { --akku-farbe: var(--skoda-ruhe); }
|
|
.skoda-auto.akku-laedt { --akku-farbe: var(--skoda-laden); }
|
|
.skoda-auto.akku-niedrig { --akku-farbe: var(--skoda-niedrig); }
|
|
.skoda-auto.akku-unbekannt { --akku-farbe: rgb(var(--skoda-blech)); }
|
|
|
|
/* Der Farbverlauf steht in <defs> und damit ausserhalb der Akkugruppe -
|
|
deshalb haengt --akku-farbe am <svg> und nicht an .akku. */
|
|
.skoda-auto #akkuVerlauf stop { stop-color: var(--akku-farbe); }
|
|
|
|
.skoda-auto .akku .huelle {
|
|
fill: rgba(8, 10, 12, .35);
|
|
stroke: var(--akku-farbe);
|
|
stroke-opacity: .55;
|
|
stroke-width: 1.5;
|
|
}
|
|
.skoda-auto .akku .fuellung {
|
|
fill: url(#akkuVerlauf);
|
|
stroke: none;
|
|
transition: y .8s ease, height .8s ease;
|
|
}
|
|
|
|
/* Die helle Kante auf der Fuellung. Sie macht den Stand auch dort ablesbar,
|
|
wo die getoente Flaeche darunter kaum auffaellt. */
|
|
.skoda-auto .akku .kante {
|
|
fill: var(--akku-farbe);
|
|
transition: y .8s ease;
|
|
}
|
|
.skoda-auto.akku-laedt .akku .kante {
|
|
filter: drop-shadow(0 0 5px var(--akku-farbe));
|
|
}
|
|
|
|
/* Modulfugen. Sie liegen auf 20, 40, 60 und 80 Prozent und sind damit
|
|
zugleich die Skala, an der sich der Stand ablesen laesst. */
|
|
.skoda-auto .akku .fugen line {
|
|
stroke: rgba(var(--skoda-blech), 0.4);
|
|
stroke-width: 1;
|
|
stroke-dasharray: 3 4;
|
|
}
|
|
|
|
/* Ladestrom: Streifen, die im gefuellten Teil nach vorn wandern. Der Schnitt
|
|
auf den Pegel kommt aus #akkuPegel, das zeichneAkku() mitfuehrt. */
|
|
.skoda-auto .akku .stroemung { opacity: 0; transition: opacity .4s; }
|
|
.skoda-auto .akku .stroemung rect { fill: var(--akku-farbe); }
|
|
.skoda-auto.akku-laedt .akku .stroemung { opacity: .4; }
|
|
.skoda-auto.akku-laedt .akku .stroemung .bahn {
|
|
animation: akkuStroemung 2.4s linear infinite;
|
|
}
|
|
@keyframes akkuStroemung {
|
|
from { transform: translateY(0); }
|
|
to { transform: translateY(-54px); } /* genau ein Streifenabstand */
|
|
}
|
|
|
|
/* Zielmarke. Sie wird als Ganzes verschoben, damit Linie und Beschriftung
|
|
zusammenbleiben und der Weg dorthin weich laeuft. */
|
|
.skoda-auto .akku .zielmarke { transition: transform .8s ease; }
|
|
.skoda-auto .akku .zielmarke line {
|
|
stroke: var(--bs-body-color, #e8ede9);
|
|
stroke-width: 1.5;
|
|
stroke-dasharray: 5 3;
|
|
opacity: .6;
|
|
}
|
|
.skoda-auto .akku .zielmarke text {
|
|
fill: var(--bs-body-color, #e8ede9);
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
text-anchor: end;
|
|
opacity: .8;
|
|
}
|
|
|
|
/* Zahlen in der Platte. Der dunkle Umriss liegt hinter der Fuellung der
|
|
Zeichen (paint-order), damit sie ueber geladenem wie leerem Teil der
|
|
Platte gleich gut lesbar bleiben. */
|
|
.skoda-auto .akku text {
|
|
paint-order: stroke;
|
|
stroke: rgba(8, 10, 12, .7);
|
|
stroke-linejoin: round;
|
|
stroke-width: 2.5;
|
|
}
|
|
.skoda-auto .akku .wert {
|
|
fill: var(--bs-body-color, #e8ede9);
|
|
font-size: 40px;
|
|
font-weight: 600;
|
|
text-anchor: middle;
|
|
font-variant-numeric: tabular-nums;
|
|
stroke-width: 4;
|
|
}
|
|
.skoda-auto .akku .wert .einheit {
|
|
font-size: 17px;
|
|
font-weight: 500;
|
|
opacity: .65;
|
|
}
|
|
.skoda-auto .akku .neben {
|
|
fill: var(--bs-body-color, #e8ede9);
|
|
fill-opacity: .8;
|
|
font-size: 14px;
|
|
text-anchor: middle;
|
|
}
|
|
|
|
/* Der Weg des Ladestroms vom Anschluss in die Platte. Die Striche laufen in
|
|
Fahrtrichtung des Stroms, also zum Akku hin. */
|
|
.skoda-auto .ladefluss {
|
|
fill: none;
|
|
stroke: var(--skoda-laden);
|
|
stroke-width: 2.5;
|
|
stroke-linecap: round;
|
|
stroke-dasharray: 3 7;
|
|
opacity: 0;
|
|
transition: opacity .3s;
|
|
}
|
|
.skoda-auto.akku-laedt .ladefluss {
|
|
opacity: .95;
|
|
animation: akkuFluss 1s linear infinite;
|
|
}
|
|
@keyframes akkuFluss {
|
|
from { stroke-dashoffset: 20; }
|
|
to { stroke-dashoffset: 0; }
|
|
}
|
|
|
|
/* Offen: bernstein und leicht leuchtend. Die Farbe allein traegt die Aussage
|
|
nicht - die Zeile unter dem Bild benennt jedes offene Teil zusaetzlich. */
|
|
.skoda-auto .teil.offen > *,
|
|
.skoda-auto .teil.offen > g > * {
|
|
fill: var(--skoda-offen);
|
|
stroke: var(--skoda-offen);
|
|
filter: drop-shadow(0 0 6px rgba(224, 144, 79, 0.65));
|
|
}
|
|
|
|
/* Licht: vorn die vier LED-Elemente je Seite, hinten das Leuchtenband. */
|
|
.skoda-auto .teil-licht.an > rect {
|
|
fill: var(--skoda-licht);
|
|
stroke: var(--skoda-licht);
|
|
filter: drop-shadow(0 0 8px rgba(242, 209, 107, 0.75));
|
|
}
|
|
.skoda-auto .teil-licht.an > rect.rueck {
|
|
fill: #e06060;
|
|
stroke: #e06060;
|
|
filter: drop-shadow(0 0 8px rgba(224, 96, 96, 0.7));
|
|
}
|
|
|
|
/* Ladeanschluss. Er ist gebaut wie der Klimaknopf; den Ladezustand traegt
|
|
nicht die Form, sondern die Farbe: blau wie jeder Knopf, gruen solange
|
|
geladen wird, dazu das Pulsen als Schein um die Scheibe. Die Scheibe selbst
|
|
bleibt deckend - gruen ausgefuellt verschluckte sie den Blitz. */
|
|
.skoda-auto .teil-stecker .glyphe { stroke-width: 2.2; }
|
|
.skoda-auto .teil-stecker.laedt .ring,
|
|
.skoda-auto .teil-stecker.laedt .flaeche { stroke: var(--skoda-laden); }
|
|
.skoda-auto .teil-stecker.laedt .glyphe { stroke: var(--skoda-laden); }
|
|
.skoda-auto .teil-stecker.laedt .flaeche {
|
|
animation: skodaPuls 1.8s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes skodaPuls {
|
|
0%, 100% { filter: drop-shadow(0 0 3px rgba(76, 175, 125, 0.5)); }
|
|
50% { filter: drop-shadow(0 0 12px rgba(76, 175, 125, 0.95)); }
|
|
}
|
|
|
|
/* Hervorheben, wenn der zugehoerige Eintrag darunter beruehrt wird - und
|
|
umgekehrt. Sichtbarer Fokus, damit das auch mit der Tastatur geht. */
|
|
.skoda-auto .teil.hervor > *,
|
|
.skoda-auto .teil.hervor > g > * {
|
|
stroke: var(--bs-primary, #0d6efd);
|
|
stroke-width: 2.5;
|
|
}
|
|
@media (hover: hover) {
|
|
.skoda-auto .teil:hover > *,
|
|
.skoda-auto .teil > g:hover > * {
|
|
stroke: var(--bs-primary, #0d6efd);
|
|
stroke-width: 2.5;
|
|
}
|
|
}
|
|
.skoda-auto .teil:focus,
|
|
.skoda-auto .teil > g:focus { outline: none; }
|
|
.skoda-auto .teil:focus-visible > *,
|
|
.skoda-auto .teil > g:focus-visible > * {
|
|
stroke: var(--bs-primary, #0d6efd);
|
|
stroke-width: 3;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.skoda-auto .teil > *,
|
|
.skoda-auto .teil > g > * { transition: none; }
|
|
.skoda-auto .teil-stecker.laedt .flaeche { animation: none; }
|
|
/* Der Ladestand bleibt sichtbar, nur nicht mehr in Bewegung. */
|
|
.skoda-auto .akku .fuellung,
|
|
.skoda-auto .akku .kante,
|
|
.skoda-auto .akku .zielmarke { transition: none; }
|
|
.skoda-auto.akku-laedt .akku .stroemung .bahn,
|
|
.skoda-auto.akku-laedt .ladefluss,
|
|
.skoda-auto.klima-laeuft .steuer .luefter .blaetter { animation: none; }
|
|
}
|
|
|
|
/* --- Zustandszeile unter dem Fahrzeug ------------------------------------ */
|
|
.skoda-teile {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .35rem;
|
|
justify-content: flex-start;
|
|
margin-top: .45rem;
|
|
}
|
|
.skoda-teile .chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .35rem;
|
|
font-size: .78rem;
|
|
padding: .2rem .55rem;
|
|
border-radius: 1rem;
|
|
border: 1px solid var(--bs-border-color, rgba(128, 128, 128, .35));
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
cursor: default;
|
|
white-space: nowrap;
|
|
}
|
|
/* Ein Punkt vor dem Wort traegt dieselbe Aussage wie die Farbe im Bild und
|
|
macht die Zeile auf einen Blick ueberfliegbar. */
|
|
.skoda-teile .chip::before {
|
|
content: "";
|
|
width: .45rem;
|
|
height: .45rem;
|
|
border-radius: 50%;
|
|
background: rgba(var(--skoda-blech), .75);
|
|
flex: 0 0 auto;
|
|
}
|
|
.skoda-teile .chip.offen { border-color: var(--skoda-offen); color: var(--skoda-offen); }
|
|
.skoda-teile .chip.offen::before { background: var(--skoda-offen); }
|
|
.skoda-teile .chip.an::before { background: var(--skoda-licht); }
|
|
.skoda-teile .chip.hervor {
|
|
border-color: var(--bs-primary, #0d6efd);
|
|
color: var(--bs-body-color, #e8ede9);
|
|
}
|
|
|
|
/* Auf Beruehrungsgeraeten sind die Ziele zu klein: eine Fingerkuppe deckt
|
|
rund 9 mm ab, die Chips waren keine 6 mm hoch. Gleiches gilt fuer die
|
|
Fahrtzeilen und den Umschalter. */
|
|
@media (pointer: coarse) {
|
|
.skoda-teile { gap: .5rem; }
|
|
.skoda-teile .chip {
|
|
font-size: .85rem;
|
|
padding: .45rem .8rem;
|
|
cursor: pointer;
|
|
}
|
|
.skoda-liste .zeile { padding: .75rem .9rem; }
|
|
.skoda-umschalter button { font-size: .85rem; padding: .5rem .9rem; }
|
|
}
|
|
.skoda-teile .chip.an { border-color: var(--skoda-licht); color: var(--skoda-licht); }
|
|
|
|
/* --- Kennzahlen neben dem Fahrzeug ---------------------------------------
|
|
Was sich nicht ins Bild zeichnen laesst, steht in gleich gebauten Kacheln:
|
|
Zeichen, Bezeichnung, Wert und bei Bedarf eine Nebenzeile. Eine Kachel
|
|
bekommt die Klasse .aktiv, solange ihre Zahl gerade etwas aussagt - die
|
|
Ladeleistung waehrend des Ladens, das Klima, solange es laeuft. Welche
|
|
Farbe die Hervorhebung traegt, steht in --kennzahl-rgb. */
|
|
.skoda-kennzahlen {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
|
|
gap: .75rem;
|
|
}
|
|
.kennzahl {
|
|
--kennzahl-rgb: var(--skoda-laden-rgb);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .75rem;
|
|
padding: .7rem .85rem;
|
|
border: 1px solid var(--bs-border-color, rgba(128, 128, 128, .25));
|
|
border-radius: .7rem;
|
|
background: rgba(var(--skoda-blech), .07);
|
|
transition: border-color .3s, background .3s;
|
|
}
|
|
.kennzahl .zeichen {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex: 0 0 auto;
|
|
width: 2.1rem;
|
|
height: 2.1rem;
|
|
border-radius: 50%;
|
|
background: rgba(var(--skoda-blech), .16);
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
transition: background .3s, color .3s;
|
|
}
|
|
.kennzahl .text { display: flex; flex-direction: column; min-width: 0; }
|
|
.kennzahl .v {
|
|
font-size: 1.2rem;
|
|
line-height: 1.3;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.kennzahl .neben {
|
|
font-size: .72rem;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
}
|
|
.kennzahl .neben:empty { display: none; }
|
|
|
|
.kennzahl.aktiv {
|
|
border-color: rgba(var(--kennzahl-rgb), .5);
|
|
background: rgba(var(--kennzahl-rgb), .1);
|
|
}
|
|
.kennzahl.aktiv .zeichen {
|
|
background: rgba(var(--kennzahl-rgb), .2);
|
|
color: rgb(var(--kennzahl-rgb));
|
|
}
|
|
#skodaKachelKlima { --kennzahl-rgb: var(--skoda-ruhe-rgb); }
|
|
|
|
/* --- Fahrleistung --------------------------------------------------------
|
|
Drei Zahlen nebeneinander, durch einen Strich getrennt. Kein Kasten und
|
|
keine Kachel: es ist eine Nebenauskunft, kein Messwert des Augenblicks.
|
|
Ein Wert, dessen Zeitraum weiter zurueckreicht als die Aufzeichnung, wird
|
|
gestrichelt unterstrichen und nennt im Titel, seit wann gemessen wird. */
|
|
.skoda-fahrleistung .reihe {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .35rem 1.4rem;
|
|
margin-top: .5rem;
|
|
}
|
|
.skoda-fahrleistung .posten {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 5.5rem;
|
|
}
|
|
.skoda-fahrleistung .posten + .posten {
|
|
border-left: 1px solid var(--bs-border-color, rgba(128, 128, 128, .25));
|
|
padding-left: 1.4rem;
|
|
}
|
|
/* Auf dem Handy zaehlt jeder Millimeter: die Trennstriche weichen, damit die
|
|
drei Zahlen in einer Zeile bleiben statt in zwei umzubrechen. */
|
|
@media (max-width: 30rem) {
|
|
.skoda-fahrleistung .reihe { gap: .3rem .9rem; }
|
|
.skoda-fahrleistung .posten { min-width: 0; }
|
|
.skoda-fahrleistung .posten + .posten { border-left: none; padding-left: 0; }
|
|
}
|
|
|
|
.skoda-fahrleistung .posten .v {
|
|
font-size: 1.15rem;
|
|
line-height: 1.3;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
/* --- Erklärungen ---------------------------------------------------------
|
|
Ein Element mit Erklärung traegt ein Info-Zeichen. Das ist der einzige
|
|
Hinweis darauf, dass es dort etwas zu lesen gibt - am Zeiger genuegte das
|
|
Ueberfahren, am Finger gibt es das nicht. Angetippt oder ueberfahren oeffnet
|
|
setzeHinweis() ein Popover; das Zeichen sagt vorher, dass sich das lohnt. */
|
|
/* Kein Textmarkieren beim Antippen: der Finger trifft Text, gemeint ist der
|
|
Auslöser - eine blau markierte Zahl waere die falsche Antwort darauf. */
|
|
.hat-hinweis {
|
|
cursor: help;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.skoda-fahrleistung .posten.hat-hinweis .v::after,
|
|
#skodaVerbrauchBasis.hat-hinweis::after {
|
|
content: "\f431"; /* bi-info-circle */
|
|
font-family: bootstrap-icons;
|
|
font-size: .78em;
|
|
margin-left: .3em;
|
|
opacity: .65;
|
|
vertical-align: .05em;
|
|
}
|
|
.hat-hinweis:focus { outline: none; }
|
|
.hat-hinweis:focus-visible {
|
|
outline: 2px solid var(--bs-primary, #4b8fd6);
|
|
outline-offset: 3px;
|
|
border-radius: .2rem;
|
|
}
|
|
|
|
/* Das Fenster selbst: dunkel wie die Kacheln, schmal genug zum Lesen. Auf dem
|
|
Handy darf es die halbe Breite einnehmen, mehr nicht. */
|
|
.skoda-erklaerung {
|
|
--bs-popover-max-width: 22rem;
|
|
--bs-popover-bg: #23282c;
|
|
--bs-popover-border-color: rgba(128, 138, 148, .35);
|
|
--bs-popover-body-color: var(--bs-body-color, #e8ede9);
|
|
--bs-popover-body-padding-x: .8rem;
|
|
--bs-popover-body-padding-y: .6rem;
|
|
--bs-popover-font-size: .82rem;
|
|
box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .45);
|
|
}
|
|
|
|
/* --- Herkunft des Ladestroms ---------------------------------------------
|
|
Der Balken teilt die Ladeleistung in den Anteil aus der eigenen Anlage und
|
|
den Rest aus dem Netz. Die Legende nennt beide Anteile in Zahlen - ein
|
|
Farbstueck allein sagt nicht, wie viel es ist. */
|
|
.skoda-quelle .kopf {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
gap: .75rem;
|
|
margin-bottom: .4rem;
|
|
}
|
|
.skoda-quelle .summe {
|
|
font-size: .85rem;
|
|
font-variant-numeric: tabular-nums;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
}
|
|
.skoda-balken {
|
|
display: flex;
|
|
height: .8rem;
|
|
border-radius: .4rem;
|
|
overflow: hidden;
|
|
background: rgba(var(--skoda-blech), 0.2);
|
|
}
|
|
.skoda-balken > div { transition: width .5s ease; }
|
|
.skoda-balken .teil-pv { background: var(--skoda-pv); }
|
|
.skoda-balken .teil-netz { background: var(--skoda-netz); }
|
|
|
|
.skoda-quelle .legende {
|
|
display: flex;
|
|
gap: 1.1rem;
|
|
margin-top: .45rem;
|
|
font-size: .78rem;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
}
|
|
.skoda-quelle .legende span { display: inline-flex; align-items: center; gap: .35rem; }
|
|
.skoda-quelle .legende b {
|
|
color: var(--bs-body-color, #e8ede9);
|
|
font-weight: 600;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.skoda-quelle .legende .pkt { width: .55rem; height: .55rem; border-radius: 50%; }
|
|
.skoda-quelle .legende .pkt.pv { background: var(--skoda-pv); }
|
|
.skoda-quelle .legende .pkt.netz { background: var(--skoda-netz); }
|
|
|
|
/* --- Listen (Ladungen, Fahrten) ------------------------------------------ */
|
|
/* Der Innenabstand oben trennt die erste Zeile von der Ueberschrift. Ohne
|
|
ihn klebt sie am Umschalter, was auf dem Handy besonders auffaellt. */
|
|
.skoda-liste {
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
padding-top: .5rem;
|
|
}
|
|
.skoda-liste .zeile {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
gap: .75rem;
|
|
padding: .5rem .9rem;
|
|
border-top: 1px solid var(--bs-border-color, rgba(128, 128, 128, .25));
|
|
}
|
|
.skoda-liste .zeile:first-child { border-top: none; }
|
|
@media (hover: hover) {
|
|
.skoda-liste .zeile:hover { background: rgba(128, 138, 148, 0.08); }
|
|
}
|
|
.skoda-liste .wann { font-size: .82rem; color: var(--bs-secondary-color, #9aa0a6); }
|
|
.skoda-liste .was { font-size: .95rem; font-variant-numeric: tabular-nums; text-align: right; }
|
|
.skoda-liste .neben { display: block; font-size: .75rem; color: var(--bs-secondary-color, #9aa0a6); }
|
|
.skoda-liste .leer { padding: 1.5rem .9rem; color: var(--bs-secondary-color, #9aa0a6); }
|
|
|
|
/* Umschalter kWh <-> Prozent in der Ueberschrift der Fahrtenliste. Welche
|
|
Einheit sichtbar ist, entscheidet die Klasse auf der Kachel - so bleibt die
|
|
Auswahl bestehen, wenn die Liste neu gebaut wird. */
|
|
.zeigt-kwh .v-prozent,
|
|
.zeigt-prozent .v-kwh,
|
|
.zeigt-prozent #skodaVerbrauchBasis { display: none; }
|
|
|
|
.skoda-umschalter {
|
|
display: inline-flex;
|
|
border: 1px solid var(--bs-border-color, rgba(128, 128, 128, .35));
|
|
border-radius: .3rem;
|
|
overflow: hidden;
|
|
}
|
|
.skoda-umschalter button {
|
|
background: none;
|
|
border: none;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
font-size: .72rem;
|
|
line-height: 1;
|
|
padding: .25rem .5rem;
|
|
cursor: pointer;
|
|
}
|
|
.skoda-umschalter button + button {
|
|
border-left: 1px solid var(--bs-border-color, rgba(128, 128, 128, .35));
|
|
}
|
|
.skoda-umschalter button:hover { background: rgba(128, 138, 148, .12); }
|
|
.skoda-umschalter button.aktiv {
|
|
background: rgba(128, 138, 148, .22);
|
|
color: var(--bs-body-color, #e8ede9);
|
|
}
|
|
.skoda-umschalter button:focus-visible {
|
|
outline: 2px solid var(--bs-primary, #0d6efd);
|
|
outline-offset: -2px;
|
|
}
|
|
|
|
/* Fahrtzeilen, die sich auf der Karte zeigen lassen. Der Zeiger und die
|
|
Markierung sind der einzige Hinweis darauf - ohne den waere die Kopplung
|
|
nicht zu erraten. */
|
|
.skoda-liste .zeile.anklickbar { cursor: pointer; }
|
|
.skoda-liste .zeile.anklickbar:focus-visible {
|
|
outline: 2px solid var(--bs-primary, #0d6efd);
|
|
outline-offset: -2px;
|
|
}
|
|
/* Dieselbe Farbe wie die gewaehlte Linie auf der Karte - die Zuordnung
|
|
zwischen Zeile und Linie soll ohne Nachdenken erkennbar sein. */
|
|
.skoda-liste .zeile.gewaehlt {
|
|
background: rgba(224, 144, 79, 0.14);
|
|
box-shadow: inset 3px 0 0 #e0904f;
|
|
}
|
|
|
|
/* --- Karte (Leaflet) -----------------------------------------------------
|
|
Die Kacheln kommen ueber ajax/tile.php aus dem eigenen Zwischenspeicher.
|
|
OpenStreetMap-Kacheln sind hell; im dunklen Dashboard wuerden sie wie ein
|
|
Scheinwerfer wirken. invert + hue-rotate dreht sie ins Dunkle, ohne einen
|
|
zweiten Anbieter samt eigener Bedingungen ins Spiel zu bringen. Der Filter
|
|
sitzt auf den Kacheln allein - Marker, Linien und Bedienelemente behalten
|
|
ihre Farben. */
|
|
.skoda-karte {
|
|
height: 420px;
|
|
width: 100%;
|
|
border-radius: .4rem;
|
|
background: rgba(128, 138, 148, 0.12);
|
|
z-index: 0; /* sonst legt sich die Karte ueber die Seitenleiste */
|
|
}
|
|
|
|
.skoda-karte .leaflet-tile-pane {
|
|
filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(0.9);
|
|
}
|
|
|
|
/* Bedienelemente und Sprechblasen an das dunkle Theme angleichen. */
|
|
.skoda-karte .leaflet-bar a,
|
|
.skoda-karte .leaflet-bar a:hover {
|
|
background: #23282c;
|
|
color: var(--bs-body-color, #e8ede9);
|
|
border-bottom-color: #33393e;
|
|
}
|
|
.skoda-karte .leaflet-bar a:hover { background: #2d3338; }
|
|
|
|
.skoda-karte .leaflet-popup-content-wrapper,
|
|
.skoda-karte .leaflet-popup-tip {
|
|
background: #23282c;
|
|
color: var(--bs-body-color, #e8ede9);
|
|
box-shadow: 0 3px 14px rgba(0, 0, 0, .5);
|
|
}
|
|
.skoda-karte .leaflet-popup-content { margin: .6rem .8rem; line-height: 1.5; }
|
|
.skoda-karte .leaflet-popup-close-button { color: var(--bs-secondary-color, #9aa0a6); }
|
|
|
|
/* Die Nennung von OpenStreetMap ist Pflicht und darf nicht versteckt werden -
|
|
nur ruhiger gesetzt, damit sie das Bild nicht dominiert.
|
|
|
|
Ohne Hintergrundkasten, dafuer mit Schattenwurf: ein durchsichtiger
|
|
Hintergrund allein liesse den Text ueber hellen Kartenstellen verschwinden,
|
|
und unlesbar waere so gut wie versteckt. Der Schatten haelt ihn auf jedem
|
|
Untergrund erkennbar, ohne einen Balken ins Bild zu setzen. */
|
|
/* Der doppelte Klassenname ist noetig, nicht Zierde: Leaflet setzt den
|
|
Hintergrund ueber .leaflet-container .leaflet-control-attribution, also mit
|
|
derselben Spezifitaet wie ein einfaches .skoda-karte davor - und leaflet.css
|
|
wird spaeter geladen, gewinnt bei Gleichstand also. Der Kartencontainer
|
|
traegt beide Klassen, damit sticht diese Regel. */
|
|
.skoda-karte.leaflet-container .leaflet-control-attribution {
|
|
background: transparent;
|
|
color: #c9d0d6;
|
|
font-size: .68rem;
|
|
padding: 0 .4rem;
|
|
text-shadow: 0 1px 2px rgba(0, 0, 0, .9), 0 0 4px rgba(0, 0, 0, .75);
|
|
}
|
|
.skoda-karte.leaflet-container .leaflet-control-attribution a {
|
|
color: #a9c8f0;
|
|
text-shadow: inherit;
|
|
}
|
|
|
|
.skoda-kartenlegende {
|
|
display: inline-flex;
|
|
gap: .9rem;
|
|
font-size: .75rem;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
}
|
|
.skoda-kartenlegende span { display: inline-flex; align-items: center; gap: .3rem; }
|
|
.skoda-kartenlegende .pkt { width: .55rem; height: .55rem; border-radius: 50%; }
|
|
.skoda-kartenlegende .pkt.jetzt { background: #4caf7d; }
|
|
.skoda-kartenlegende .pkt.halt { background: #8a949c; }
|
|
.skoda-kartenlegende .lin {
|
|
width: 1.1rem;
|
|
border-top: 2px dashed #8a949c;
|
|
}
|
|
|
|
/* --- Befund -------------------------------------------------------------- */
|
|
.skoda-befund .zahl {
|
|
font-size: 2.4rem;
|
|
font-weight: 600;
|
|
line-height: 1.1;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.skoda-befund .zahl .einheit {
|
|
font-size: .95rem;
|
|
font-weight: 400;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
margin-left: .35rem;
|
|
}
|
|
.skoda-befund dl { margin: 1rem 0 0; display: grid; gap: .5rem; }
|
|
.skoda-befund dl > div { display: flex; justify-content: space-between; gap: 1rem; }
|
|
.skoda-befund dt {
|
|
font-weight: 400;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
font-size: .85rem;
|
|
}
|
|
.skoda-befund dd { margin: 0; font-variant-numeric: tabular-nums; font-size: .9rem; }
|
|
|
|
/* --- Steuerung ----------------------------------------------------------
|
|
Die Befehle sitzen im Modal, das ueber die anklickbaren Stellen im
|
|
Fahrzeugbild aufgeht. */
|
|
.skoda-steuerung .knoepfe { display: flex; flex-wrap: wrap; gap: .5rem; }
|
|
.skoda-steuerung .knoepfe button { flex: 1 1 11rem; }
|
|
.skoda-steuerung .temperatur {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: .75rem;
|
|
font-size: .9rem;
|
|
}
|
|
.skoda-steuerung .temperatur span { display: flex; align-items: center; gap: .35rem; }
|
|
.skoda-steuerung .temperatur input { width: 5.5rem; }
|
|
|
|
@media (pointer: coarse) {
|
|
.skoda-steuerung .knoepfe button { padding: .7rem 1rem; }
|
|
}
|
|
|
|
.skoda-rueckmeldung {
|
|
font-size: .9rem;
|
|
padding: .5rem .75rem;
|
|
border-radius: .35rem;
|
|
display: none;
|
|
}
|
|
.skoda-rueckmeldung.warte,
|
|
.skoda-rueckmeldung.gut,
|
|
.skoda-rueckmeldung.schlecht { display: block; }
|
|
.skoda-rueckmeldung.warte { background: rgba(128, 138, 148, .16); color: var(--bs-secondary-color, #9aa0a6); }
|
|
.skoda-rueckmeldung.gut { background: rgba(76, 175, 125, .16); color: #6fd3a0; }
|
|
.skoda-rueckmeldung.schlecht { background: rgba(224, 96, 96, .16); color: #f08a8a; }
|
|
|
|
/* --- Anklickbare Stellen im Fahrzeugbild --------------------------------
|
|
Der Ring unterscheidet Bedienung von Anzeige: die uebrigen Teile zeigen
|
|
nur ihren Zustand, diese beiden aendern etwas. */
|
|
.skoda-auto .steuer { cursor: pointer; }
|
|
.skoda-auto .steuer .ring {
|
|
fill: none;
|
|
stroke: var(--bs-primary, #4b8fd6);
|
|
stroke-width: 1.5;
|
|
stroke-dasharray: 3 3;
|
|
opacity: .55;
|
|
transition: opacity .2s, stroke-width .2s;
|
|
}
|
|
/* Der Knopf sitzt mitten auf der Akkuplatte und muss sich gegen jeden
|
|
Fuellstand behaupten - gegen die dunkle leere Zelle ebenso wie gegen die
|
|
gefuellte. Eine durchscheinende Flaeche kann das nicht: sie nimmt die Farbe
|
|
an, die zufaellig darunter liegt. Deshalb eine eigene, deckende Scheibe. */
|
|
.skoda-auto .steuer .flaeche {
|
|
fill: rgba(10, 13, 17, .92);
|
|
stroke: var(--bs-primary, #4b8fd6);
|
|
stroke-width: 1.5;
|
|
filter: drop-shadow(0 0 3px rgba(0, 0, 0, .55));
|
|
}
|
|
.skoda-auto .steuer .glyphe {
|
|
fill: none;
|
|
stroke: #7db3f0; /* dieselbe Farbe wie die Luefterfluegel */
|
|
stroke-width: 1.8;
|
|
stroke-linecap: round;
|
|
stroke-linejoin: round;
|
|
}
|
|
|
|
/* Luefterrad des Klimaknopfs. Es dreht sich, solange die Klimatisierung
|
|
arbeitet - dasselbe Mittel wie beim Ladeanschluss: der Zustand steht als
|
|
Klasse am <svg>, die Form bleibt, wo sie ist. */
|
|
.skoda-auto .steuer .luefter use { fill: #7db3f0; }
|
|
.skoda-auto .steuer .luefter .nabe { fill: rgba(10, 13, 17, .92); }
|
|
.skoda-auto .steuer .luefter .blaetter { transform-origin: 0 0; }
|
|
.skoda-auto.klima-laeuft .steuer .luefter .blaetter {
|
|
animation: klimaDreht 3.2s linear infinite;
|
|
}
|
|
@keyframes klimaDreht {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
@media (hover: hover) {
|
|
.skoda-auto .steuer:hover .ring { opacity: 1; stroke-width: 2.5; }
|
|
}
|
|
.skoda-auto .steuer:focus { outline: none; }
|
|
.skoda-auto .steuer:focus-visible .ring { opacity: 1; stroke-width: 3; }
|
|
|
|
.skoda-hinweis {
|
|
margin: .6rem 0 0;
|
|
font-size: .75rem;
|
|
color: var(--bs-secondary-color, #9aa0a6);
|
|
text-align: center;
|
|
}
|
|
|
|
.skoda-alter { font-size: .85rem; }
|
|
|
|
/* === Einstellungen: Werte auf den Home-Kacheln ===========================
|
|
Eine Zeile je Raum, aufklappbar. Die Zeile zeigt im zugeklappten Zustand
|
|
schon, was auf der Kachel steht - sonst muesste man zwanzig Raeume
|
|
aufklappen, um einen zu finden. */
|
|
|
|
.kachel-etage { margin-bottom: 1.25rem; }
|
|
.kachel-etage:last-child { margin-bottom: 0; }
|
|
.kachel-etage-name {
|
|
font-size: .7rem;
|
|
letter-spacing: .13em;
|
|
text-transform: uppercase;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
margin-bottom: .35rem;
|
|
}
|
|
|
|
.kachel-zeile {
|
|
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
border-radius: .5rem;
|
|
margin-bottom: .4rem;
|
|
overflow: hidden;
|
|
}
|
|
.kachel-zeile.offen { border-color: var(--ton-linie-satt, rgba(138, 148, 158, .38)); }
|
|
|
|
.kachel-kopf {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .6rem;
|
|
width: 100%;
|
|
padding: .5rem .75rem;
|
|
background: none;
|
|
border: 0;
|
|
color: inherit;
|
|
font: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
@media (hover: hover) {
|
|
.kachel-kopf:hover { background: rgba(138, 148, 158, .08); }
|
|
}
|
|
.kachel-kopf .pfeil {
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
transition: transform .18s;
|
|
flex: 0 0 auto;
|
|
}
|
|
.kachel-zeile.offen .kachel-kopf .pfeil { transform: rotate(90deg); }
|
|
.kachel-kopf .name { min-width: 8rem; }
|
|
.kachel-kopf .werte {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .3rem;
|
|
flex: 1 1 auto;
|
|
}
|
|
.kachel-chip {
|
|
font-size: .72rem;
|
|
padding: .1rem .45rem;
|
|
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
border-radius: 1rem;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
white-space: nowrap;
|
|
}
|
|
.kachel-chip.leer { font-style: italic; }
|
|
.kachel-chip .bi { margin-right: .3rem; opacity: .8; }
|
|
|
|
.kachel-editor {
|
|
padding: .25rem .75rem .75rem;
|
|
border-top: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
}
|
|
|
|
/* Eine Wertzeile. Auf breiten Schirmen nebeneinander, auf dem Handy
|
|
untereinander - die Felder sind zu kurz, um sie zu quetschen. */
|
|
.kachel-wert {
|
|
display: grid;
|
|
grid-template-columns: 7.5rem minmax(10rem, 1fr) 7rem 5.5rem 9.5rem auto;
|
|
gap: .4rem;
|
|
align-items: center;
|
|
padding: .5rem 0;
|
|
border-bottom: 1px dashed var(--ton-linie, rgba(138, 148, 158, .2));
|
|
}
|
|
.kachel-wert:last-of-type { border-bottom: 0; }
|
|
/* Symbolname mit Vorschau davor: die Vorschau sagt, was der Name bedeutet -
|
|
"moisture" allein sagt es nicht. */
|
|
.kachel-wert .kw-symbol {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .35rem;
|
|
}
|
|
.kachel-wert .kw-symbol-schau {
|
|
flex: none;
|
|
width: 1.5rem;
|
|
text-align: center;
|
|
font-size: 1.05rem;
|
|
line-height: 1;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
}
|
|
.kachel-wert .kw-symbol-schau .unbekannt { color: var(--ton-schlecht, #e06060); }
|
|
.kachel-wert .kw-symbol input { min-width: 0; }
|
|
|
|
.kachel-wert .kw-schalter {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
white-space: nowrap;
|
|
}
|
|
.kachel-wert .kw-schalter .kw-format {
|
|
width: auto;
|
|
font-size: .78rem;
|
|
padding-top: .1rem;
|
|
padding-bottom: .1rem;
|
|
}
|
|
.kachel-wert .kw-schalter label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .25rem;
|
|
font-size: .78rem;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
cursor: pointer;
|
|
}
|
|
|
|
@media (max-width: 991.98px) {
|
|
.kachel-wert {
|
|
grid-template-columns: 1fr 1fr;
|
|
}
|
|
.kachel-wert .kw-topic { grid-column: 1 / -1; }
|
|
}
|
|
|
|
.kachel-fuss {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .75rem;
|
|
margin-top: .75rem;
|
|
}
|
|
|
|
/* === Geraetetafel (Einstellungen) ========================================
|
|
Sechzig Zeilen, in denen die meiste Arbeit an einer Handvoll haengt: den
|
|
Geraeten ohne Raum und denen, die seit Monaten schweigen. Beide sind
|
|
markiert, damit man sie nicht suchen muss - der Rest bleibt ruhig. */
|
|
.geraete-sieb {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .9rem;
|
|
margin-bottom: .75rem;
|
|
}
|
|
.geraete-sieb input[type="search"] { max-width: 22rem; }
|
|
.geraete-sieb .form-check { flex: none; }
|
|
|
|
/* Kein eigener Rollbalken: die Tafel ist so lang, wie sie ist, und die Seite
|
|
scrollt als Ganzes. Ein Kasten mit eigenem Balken in einer Karte, die selbst
|
|
in einer scrollenden Seite steht, ist ein Balken zu viel - man weiss beim
|
|
Wischen nie, welcher der beiden gerade dran ist. */
|
|
.geraete-tafel {
|
|
/* Bewusst ohne Rahmen und ohne overflow: beides machte aus der Tafel
|
|
wieder einen eigenen Scroll-Kasten, und der klebende Tabellenkopf haette
|
|
nichts mehr, woran er kleben kann. */
|
|
margin-bottom: 0;
|
|
}
|
|
/* Der Kopf bleibt trotzdem stehen: er klebt an der Kopfzeile der Seite, denn
|
|
gescrollt wird bei AdminLTE in .app-main, und dort ist oben die Navigation.
|
|
Ab Zeile zwanzig weiss man sonst nicht mehr, welche Spalte der Raum war. */
|
|
.geraete-tafel thead th {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1;
|
|
background: var(--ton-flaeche-2, #2b3237);
|
|
font-size: .72rem;
|
|
letter-spacing: .08em;
|
|
text-transform: uppercase;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
white-space: nowrap;
|
|
border-bottom: 1px solid var(--bs-border-color, #2c3034);
|
|
box-shadow: 0 1px 0 var(--ton-grund, #191d20); /* deckt die Zeile darunter */
|
|
}
|
|
.geraete-tafel td { vertical-align: middle; }
|
|
.geraete-tafel .name { line-height: 1.25; }
|
|
.geraete-tafel .name .neben {
|
|
display: block;
|
|
font-size: .72rem;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
.geraete-tafel .raum { min-width: 11rem; }
|
|
.geraete-tafel .raum select { min-width: 9rem; }
|
|
.geraete-tafel .alter { color: var(--ton-schrift-2, #9aa4ab); white-space: nowrap; }
|
|
.geraete-tafel .alter.lange { color: var(--ton-sonne, #f0b429); }
|
|
/* Ohne Raum: eine Kante links statt einer eingefaerbten Zeile - Letzteres
|
|
sieht bei zwanzig offenen Geraeten nach Fehler aus, ist aber nur Arbeit.
|
|
Als ::before und nicht als border, weil Bootstrap Tabellenzellen im
|
|
Hover mit einem inset-Schatten fuellt und den Rand damit ueberdeckt. */
|
|
.geraete-tafel tr.ohne-raum td:first-child { position: relative; }
|
|
.geraete-tafel tr.ohne-raum td:first-child::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0; top: 0; bottom: 0;
|
|
width: 3px;
|
|
background: var(--ton-sonne, #f0b429);
|
|
}
|
|
|
|
/* Der Papierkorb haelt sich zurueck, bis man ihn braucht. */
|
|
.geraete-tafel .geraet-weg {
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
border-color: transparent;
|
|
line-height: 1;
|
|
}
|
|
.geraete-tafel .geraet-weg:hover:not(:disabled),
|
|
.geraete-tafel .geraet-weg:focus-visible:not(:disabled) {
|
|
color: var(--ton-schlecht, #e06060);
|
|
border-color: var(--ton-schlecht, #e06060);
|
|
}
|
|
.geraete-tafel .geraet-weg:disabled { opacity: .25; }
|
|
|
|
.geraete-fuss {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: .75rem;
|
|
margin-top: .5rem;
|
|
}
|
|
|
|
@media (max-width: 575.98px) {
|
|
.geraete-sieb { flex-wrap: wrap; }
|
|
.geraete-sieb input[type="search"] { max-width: none; }
|
|
}
|
|
|
|
/* === Protokollseite ======================================================
|
|
Ein Log liest sich nur in einer Schrift mit fester Breite: die Zeitstempel
|
|
stehen dann untereinander, und das Auge findet die Stelle, an der etwas
|
|
anders wird. Die Zeilen umbrechen statt seitlich zu scrollen - ein
|
|
Traceback ist lang, und waagerecht zu blaettern verliert man sofort. */
|
|
.log-ausgabe {
|
|
max-height: calc(100vh - 22rem);
|
|
min-height: 20rem;
|
|
overflow-y: auto;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
font-size: .78rem;
|
|
line-height: 1.45;
|
|
background: var(--bs-body-bg, #1b1e21);
|
|
border: 1px solid var(--bs-border-color, #2c3034);
|
|
border-radius: .375rem;
|
|
}
|
|
|
|
.log-ausgabe .log-zeile {
|
|
display: flex;
|
|
gap: .6rem;
|
|
padding: .1rem .6rem;
|
|
border-left: 3px solid transparent;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
.log-ausgabe .log-zeile:hover { background: rgba(255, 255, 255, .04); }
|
|
|
|
.log-ausgabe .log-zeit { flex: 0 0 5rem; color: var(--bs-secondary-color, #8b9198); }
|
|
.log-ausgabe .log-level { flex: 0 0 4.5rem; font-weight: 600; }
|
|
.log-ausgabe .log-text { flex: 1 1 auto; }
|
|
|
|
/* Fortsetzungszeilen - die zweite bis letzte Zeile eines Tracebacks - tragen
|
|
kein eigenes Level. Sie bleiben in der Farbe ihrer Kopfzeile, ruecken aber
|
|
ein: so sieht man auf einen Blick, was zusammengehoert. */
|
|
.log-ausgabe .log-fortsetzung .log-text { padding-left: 1.2rem; opacity: .85; }
|
|
|
|
.log-ausgabe .log-debug { color: var(--bs-secondary-color, #8b9198); }
|
|
.log-ausgabe .log-info .log-level { color: #6ea8dc; }
|
|
.log-ausgabe .log-warning { border-left-color: #d9a441; background: rgba(217, 164, 65, .07); }
|
|
.log-ausgabe .log-warning .log-level { color: #d9a441; }
|
|
.log-ausgabe .log-error { border-left-color: #e06060; background: rgba(224, 96, 96, .09); }
|
|
.log-ausgabe .log-error .log-level { color: #f08a8a; }
|
|
|
|
/* Tagestrenner: das Datum steht einmal da, nicht in jeder Zeile. */
|
|
.log-ausgabe .log-tag {
|
|
position: sticky;
|
|
top: 0;
|
|
padding: .15rem .6rem;
|
|
font-size: .72rem;
|
|
letter-spacing: .04em;
|
|
color: var(--bs-secondary-color, #8b9198);
|
|
background: var(--bs-tertiary-bg, #23272b);
|
|
border-top: 1px solid var(--bs-border-color, #2c3034);
|
|
border-bottom: 1px solid var(--bs-border-color, #2c3034);
|
|
}
|
|
|
|
/* Die Dateiliste links: der aktive Eintrag wird von AdminLTE eingefaerbt,
|
|
die Zustandszeile darunter soll trotzdem lesbar bleiben. */
|
|
.log-liste .log-wahl.active .text-secondary,
|
|
.log-liste .log-wahl.active .text-warning { color: rgba(255, 255, 255, .75) !important; }
|
|
|
|
/* Etagenschalter im Grundriss der Home-Ansicht.
|
|
|
|
Vorher waren sie ein duenner Ring in einem Amber (#aa7713), das sonst
|
|
nirgends im Dashboard vorkommt - und alle vier sahen immer gleich aus,
|
|
die aktive Etage war an ihnen nicht zu erkennen. Jetzt tragen sie
|
|
dieselbe Scheibe wie die Kacheln im Grundriss, und die aktive Etage
|
|
bekommt die Akzentfarbe der Schieberegler. */
|
|
.floorBtn { cursor: pointer; }
|
|
|
|
.floorBtn circle {
|
|
fill: #0006;
|
|
stroke: #9aa0a6;
|
|
stroke-width: 4;
|
|
opacity: .55;
|
|
transition: stroke .25s ease, opacity .25s ease;
|
|
}
|
|
|
|
.floorBtn text {
|
|
font: 600 34px var(--font-anzeige);
|
|
fill: #9aa0a6;
|
|
opacity: .8;
|
|
transition: fill .25s ease, opacity .25s ease;
|
|
/* Der Kreis faengt den Klick, nicht die Beschriftung - sonst wandert der
|
|
Textcursor ueber die Schrift, sobald man sie streift. */
|
|
user-select: none;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.floorBtn:hover circle { opacity: .8; }
|
|
.floorBtn:hover text { opacity: 1; }
|
|
|
|
.floorBtn.active circle { stroke: #00788F; opacity: 1; }
|
|
.floorBtn.active text { fill: #e8ede9; opacity: 1; }
|
|
|
|
/* ===========================================================================
|
|
Einstellungen -> Grundriss (js/solar/grundriss.js)
|
|
|
|
Links der Plan, rechts die Formulare. Die Kachel im Plan sieht aus wie auf
|
|
der Startseite (restricted/home.php): dunkles Rechteck, weisse Schrift -
|
|
nur ohne Messwerte, dafuer mit Rand, wenn sie gewaehlt ist.
|
|
=========================================================================== */
|
|
.gr-etagen {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .4rem;
|
|
margin-bottom: .9rem;
|
|
}
|
|
/* Eine Klasse mehr als Bootstrap: solar.css laedt vor AdminLTE. */
|
|
.gr-etagen .btn.gr-etage {
|
|
display: inline-flex;
|
|
align-items: baseline;
|
|
gap: .4rem;
|
|
border: 1px solid var(--ton-linie-satt, rgba(138, 148, 158, .38));
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
}
|
|
.gr-etagen .btn.gr-etage.aktiv {
|
|
border-color: #00788F;
|
|
color: var(--ton-schrift, #e8ede9);
|
|
background: rgba(0, 120, 143, .18);
|
|
}
|
|
.gr-etage-code { font: 600 .9rem var(--font-anzeige); }
|
|
.gr-etage-name { font-size: .75rem; }
|
|
.gr-etage-std { font-size: .65rem; color: var(--ton-sonne, #f0b429); }
|
|
|
|
.gr-plan-huelle {
|
|
position: relative;
|
|
border-radius: var(--ton-radius-klein, .5rem);
|
|
overflow: hidden;
|
|
background: #0b0d10;
|
|
}
|
|
.gr-plan-huelle.platzieren .gr-plan { cursor: crosshair; }
|
|
.gr-plan-leer {
|
|
aspect-ratio: 4 / 3;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
font-size: .85rem;
|
|
}
|
|
.gr-plan {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
touch-action: none;
|
|
user-select: none;
|
|
}
|
|
.gr-ohne-bild {
|
|
fill: var(--ton-schrift-3, #6f7a81);
|
|
font: 12px sans-serif;
|
|
text-anchor: middle;
|
|
}
|
|
.gr-kachel { cursor: grab; }
|
|
.gr-kachel.zieht { cursor: grabbing; }
|
|
.gr-kachel rect {
|
|
fill: #0006;
|
|
stroke: #ffffff55;
|
|
stroke-width: .5;
|
|
}
|
|
.gr-kachel:hover rect { stroke: #ffffffaa; }
|
|
.gr-kachel.gewaehlt rect { stroke: var(--ton-sonne, #f0b429); stroke-width: 1; }
|
|
.gr-kachel text {
|
|
fill: #fff;
|
|
font: 4.8px sans-serif;
|
|
text-anchor: middle;
|
|
pointer-events: none;
|
|
}
|
|
.gr-geist rect {
|
|
fill: rgba(240, 180, 41, .28);
|
|
stroke: var(--ton-sonne, #f0b429);
|
|
stroke-width: .8;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.gr-block {
|
|
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
border-radius: var(--ton-radius-klein, .5rem);
|
|
padding: .6rem .75rem;
|
|
margin-bottom: .6rem;
|
|
}
|
|
.gr-block-titel {
|
|
font-size: .7rem;
|
|
letter-spacing: .13em;
|
|
text-transform: uppercase;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
margin-bottom: .35rem;
|
|
}
|
|
.gr-knoepfe {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .35rem;
|
|
margin-top: .6rem;
|
|
}
|
|
/* Papierkorb wie in der Geraetetafel: erst rot, wenn man ihn anfasst. */
|
|
.gr-knoepfe .btn.gr-etage-weg,
|
|
.gr-knoepfe .btn.gr-raum-weg {
|
|
margin-left: auto;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
.gr-knoepfe .btn.gr-etage-weg:hover,
|
|
.gr-knoepfe .btn.gr-raum-weg:hover { color: var(--ton-schlecht, #e06060); }
|
|
|
|
.gr-raeume { display: flex; flex-direction: column; gap: .15rem; }
|
|
.gr-raum {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .4rem;
|
|
padding: .1rem .3rem;
|
|
border-radius: .35rem;
|
|
}
|
|
.gr-raum.gewaehlt { background: rgba(240, 180, 41, .12); }
|
|
.gr-raum-name {
|
|
flex: 1 1 auto;
|
|
background: none;
|
|
border: 0;
|
|
padding: .2rem 0;
|
|
color: inherit;
|
|
font: inherit;
|
|
font-size: .88rem;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
.gr-raum-stand { color: var(--ton-schrift-3, #6f7a81); font-size: .8rem; }
|
|
.gr-raum .btn.gr-platzieren { padding: 0 .45rem; font-size: .75rem; }
|
|
|
|
/* ===========================================================================
|
|
Automatismen als Zeitleiste (js/solar/zeitleiste.js)
|
|
|
|
Eine Bahn je Geraeteart, die Spur ist der Tag von 0 bis 24 Uhr. Die Nacht
|
|
liegt als leichter Schatten dahinter, jetzt ist eine duenne Linie in der
|
|
Sonnenfarbe. Jede Bahn traegt ihre Akzentfarbe als --zl-farbe; Punkte,
|
|
Balken und Etiketten nehmen sie auf.
|
|
=========================================================================== */
|
|
.zl {
|
|
--zl-linie: var(--ton-linie, rgba(138, 148, 158, .2));
|
|
--zl-farbe: #9aa6ad;
|
|
--zl-farbe-rgb: 154, 166, 173;
|
|
}
|
|
.zl-bahn-licht { --zl-farbe: #f0b429; --zl-farbe-rgb: 240, 180, 41; }
|
|
.zl-bahn-beschattung { --zl-farbe: #5fb0dc; --zl-farbe-rgb: 95, 176, 220; }
|
|
.zl-bahn-heizung { --zl-farbe: #e8836a; --zl-farbe-rgb: 232, 131, 106; }
|
|
.zl-bahn-bewaesserung { --zl-farbe: #4cc18a; --zl-farbe-rgb: 76, 193, 138; }
|
|
.zl-bahn-schalter { --zl-farbe: #b59cf0; --zl-farbe-rgb: 181, 156, 240; }
|
|
|
|
/* Kopf */
|
|
.zl-kopf {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .6rem 1rem;
|
|
margin-bottom: .9rem;
|
|
}
|
|
.zl-tag .btn.zl-tag-name { min-width: 9.5rem; font-variant-numeric: tabular-nums; }
|
|
.zl-etagen { display: flex; flex-wrap: wrap; gap: .3rem; }
|
|
.zl-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .35rem;
|
|
padding: .2rem .65rem;
|
|
border: 1px solid var(--zl-linie);
|
|
border-radius: 999px;
|
|
background: transparent;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
font: 500 .8rem var(--font-anzeige, inherit);
|
|
cursor: pointer;
|
|
transition: background .15s, border-color .15s, color .15s;
|
|
}
|
|
.zl-chip:hover { border-color: var(--ton-linie-satt, rgba(138, 148, 158, .38)); color: var(--ton-schrift, #e8ede9); }
|
|
.zl-chip.an {
|
|
background: rgba(0, 120, 143, .22);
|
|
border-color: #00788F;
|
|
color: var(--ton-schrift, #e8ede9);
|
|
}
|
|
.zl-chip.leer { opacity: .55; }
|
|
.zl-chip-zahl {
|
|
font-size: .68rem;
|
|
padding: 0 .35rem;
|
|
border-radius: 999px;
|
|
background: rgba(138, 148, 158, .18);
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
}
|
|
.zl-zahl {
|
|
margin-left: auto;
|
|
font-size: .8rem;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
white-space: nowrap;
|
|
}
|
|
.zl-zahl-leise { color: var(--ton-schrift-3, #6f7a81); }
|
|
|
|
/* Raster */
|
|
.zl-rahmen {
|
|
overflow-x: auto;
|
|
border-radius: var(--ton-radius-klein, .5rem);
|
|
}
|
|
.zl-raster { position: relative; }
|
|
.zl-zeile {
|
|
display: grid;
|
|
grid-template-columns: var(--zl-kopf) minmax(0, 1fr);
|
|
}
|
|
.zl-bahn .zl-spur { border-top: 1px solid var(--zl-linie); }
|
|
.zl-bahn-kopf {
|
|
position: sticky;
|
|
left: 0;
|
|
z-index: 3;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: .45rem;
|
|
padding: .6rem .6rem 0 0;
|
|
background: var(--ton-flaeche, #23282c);
|
|
font: 500 .82rem var(--font-anzeige, inherit);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
}
|
|
.zl-bahn-kopf .bi { color: var(--zl-farbe); font-size: .95rem; line-height: 1.1; }
|
|
/* Schmal nur das Symbol; der Name steht im Hinweis beim Antippen der Eintraege. */
|
|
.zl-rahmen.schmal .zl-bahn-kopf { justify-content: center; padding-right: 0; }
|
|
.zl-rahmen.schmal .zl-bahn-kopf span { display: none; }
|
|
.zl-bahn-zahl {
|
|
margin-left: auto;
|
|
font-size: .68rem;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
.zl-spur {
|
|
position: relative;
|
|
min-height: 38px;
|
|
/* Alle drei Stunden eine Linie, jede volle Stunde ein Hauch. */
|
|
background-image:
|
|
linear-gradient(to right, rgba(138, 148, 158, .16) 1px, transparent 1px),
|
|
linear-gradient(to right, rgba(138, 148, 158, .06) 1px, transparent 1px);
|
|
background-size: 12.5% 100%, 4.1667% 100%;
|
|
}
|
|
.zl-nacht {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, .24);
|
|
pointer-events: none;
|
|
}
|
|
.zl-jetzt {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 0;
|
|
border-left: 1.5px solid var(--ton-sonne, #f0b429);
|
|
opacity: .8;
|
|
z-index: 2;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Achse */
|
|
.zl-achse .zl-bahn-kopf { padding-top: 0; }
|
|
.zl-achse .zl-spur { height: 26px; min-height: 0; background: none; }
|
|
.zl-stunde {
|
|
position: absolute;
|
|
bottom: 4px;
|
|
transform: translateX(-50%);
|
|
font-size: .7rem;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.zl-stunde.anfang { transform: none; }
|
|
.zl-stunde.ende { transform: translateX(-100%); }
|
|
.zl-sonne {
|
|
position: absolute;
|
|
top: -2px;
|
|
transform: translateX(-50%);
|
|
font-size: .78rem;
|
|
color: var(--ton-sonne, #f0b429);
|
|
opacity: .75;
|
|
cursor: help;
|
|
}
|
|
.zl-jetzt-marke {
|
|
position: absolute;
|
|
top: 1px;
|
|
transform: translateX(-50%);
|
|
padding: 0 .35rem;
|
|
border-radius: 999px;
|
|
background: var(--ton-sonne, #f0b429);
|
|
color: #1b1f22;
|
|
font: 600 .66rem var(--font-anzeige, inherit);
|
|
z-index: 2;
|
|
}
|
|
|
|
/* Eintraege */
|
|
.zl-ding {
|
|
position: absolute;
|
|
height: 24px;
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
z-index: 1;
|
|
}
|
|
.zl-ding:hover { z-index: 4; }
|
|
.zl-marke {
|
|
flex: none;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
border: 2px solid var(--zl-farbe);
|
|
background: var(--ton-flaeche, #23282c);
|
|
box-sizing: border-box;
|
|
}
|
|
.zl-punkt { transform: translateX(-6px); }
|
|
.zl-punkt.links { flex-direction: row-reverse; transform: translateX(calc(-100% + 6px)); }
|
|
.zl-etikett {
|
|
margin-left: 6px;
|
|
padding: .1rem .5rem;
|
|
border-radius: 6px;
|
|
border: 1px solid transparent;
|
|
background: rgba(var(--zl-farbe-rgb), .1);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
font-size: .75rem;
|
|
line-height: 1.35;
|
|
white-space: nowrap;
|
|
transition: background .15s, border-color .15s;
|
|
}
|
|
.zl-punkt.links .zl-etikett { margin-left: 0; margin-right: 6px; }
|
|
.zl-etikett b {
|
|
font-weight: 600;
|
|
margin-right: .4rem;
|
|
color: var(--zl-farbe);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.zl-etikett .bi-check2 { color: var(--ton-gut, #4caf7d); }
|
|
.zl-ding:hover .zl-etikett {
|
|
background: rgba(var(--zl-farbe-rgb), .2);
|
|
border-color: rgba(var(--zl-farbe-rgb), .45);
|
|
}
|
|
|
|
/* Zustaende */
|
|
.zl-ding.gelaufen .zl-marke { background: var(--zl-farbe); }
|
|
.zl-ding.vorbei { opacity: .5; }
|
|
.zl-ding.vorbei .zl-etikett b { color: var(--ton-schrift-2, #9aa6ad); }
|
|
.zl-ding.aktiv .zl-marke { box-shadow: 0 0 0 3px rgba(var(--zl-farbe-rgb), .25); }
|
|
/* Nicht dran: statt eines gestrichelten Kreises nur ein kleiner blasser Punkt
|
|
dicht am Etikett - das Kalender-Symbol darin sagt schon, was los ist. Die
|
|
Marke behaelt ihre 12px, damit Zeit, Stapeln und Kettenlinien stimmen. */
|
|
.zl-ding.nichtdran .zl-marke {
|
|
border: 0;
|
|
background: radial-gradient(circle, var(--ton-schrift-3, #6f7a81) 2.5px, transparent 3px);
|
|
}
|
|
.zl-punkt.nichtdran .zl-etikett { margin-left: 1px; }
|
|
.zl-punkt.nichtdran.links .zl-etikett { margin-left: 0; margin-right: 1px; }
|
|
.zl-ding.nichtdran .zl-etikett {
|
|
background: transparent;
|
|
border: 1px dashed var(--zl-linie);
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
.zl-ding.nichtdran .zl-etikett b { color: var(--ton-schrift-3, #6f7a81); }
|
|
.zl-nichtdran {
|
|
margin-right: .35rem;
|
|
font-size: .72rem;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
}
|
|
|
|
/* Zeitfenster: der Balken traegt Laeufe und die frueheste Marke. */
|
|
.zl-fenster { min-width: 10px; }
|
|
.zl-balken {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
top: 8px;
|
|
height: 8px;
|
|
border-radius: 4px;
|
|
background: rgba(var(--zl-farbe-rgb), .22);
|
|
border: 1px solid rgba(var(--zl-farbe-rgb), .4);
|
|
}
|
|
.zl-fenster .zl-etikett {
|
|
position: relative;
|
|
margin-left: 0;
|
|
background: var(--ton-flaeche, #23282c);
|
|
border-color: rgba(var(--zl-farbe-rgb), .3);
|
|
}
|
|
/* Reicht der Platz bis zum rechten Rand nicht, wird der Name gekuerzt. */
|
|
.zl-etikett.gekappt {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.zl-lauf {
|
|
margin-left: .25rem;
|
|
color: var(--ton-gut, #4caf7d);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.zl-fenster.nichtdran .zl-balken { background: transparent; border-style: dashed; border-color: var(--zl-linie); }
|
|
.zl-marke-innen {
|
|
position: absolute;
|
|
top: 50%;
|
|
width: 10px;
|
|
height: 10px;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
/* Ketten */
|
|
.zl-ketten {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
overflow: visible;
|
|
}
|
|
/* Die Linie liegt ueber dem Raster, aber unter den Etiketten. Ein dunkler
|
|
Grund darunter trennt sie von den Stundenlinien, sonst verschwindet sie
|
|
dort, wo sie eine kreuzt. */
|
|
.zl-ketten .zl-kette-grund {
|
|
fill: none;
|
|
stroke: var(--ton-flaeche, #23282c);
|
|
stroke-width: 4;
|
|
opacity: .7;
|
|
}
|
|
.zl-ketten .zl-kette {
|
|
fill: none;
|
|
stroke: rgba(214, 222, 227, .72);
|
|
stroke-width: 1.6;
|
|
stroke-dasharray: 4 3;
|
|
transition: stroke .15s;
|
|
}
|
|
.zl-ketten .zl-kette-ende { fill: rgba(214, 222, 227, .85); transition: fill .15s; }
|
|
.zl-ketten .zl-kette.hell { stroke: var(--ton-sonne, #f0b429); stroke-dasharray: none; }
|
|
.zl-ketten .zl-kette-ende.hell { fill: var(--ton-sonne, #f0b429); }
|
|
|
|
.zl-leer {
|
|
padding: 1.2rem 0 .6rem 132px;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
font-size: .85rem;
|
|
}
|
|
|
|
/* Baender */
|
|
.zl-baender {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .45rem;
|
|
margin-top: .8rem;
|
|
padding-top: .7rem;
|
|
border-top: 1px solid var(--zl-linie);
|
|
}
|
|
.zl-band {
|
|
display: grid;
|
|
grid-template-columns: 132px minmax(0, 1fr);
|
|
align-items: start;
|
|
}
|
|
.zl-band-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
|
|
@media (max-width: 575.98px) {
|
|
.zl-band { grid-template-columns: minmax(0, 1fr); gap: .3rem; }
|
|
.zl-zahl { margin-left: 0; }
|
|
}
|
|
.zl-band-titel {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .4rem;
|
|
padding-top: .2rem;
|
|
font: 500 .82rem var(--font-anzeige, inherit);
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
}
|
|
.zl-band-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .4rem;
|
|
padding: .2rem .6rem;
|
|
border-radius: 6px;
|
|
border: 1px solid var(--zl-linie);
|
|
background: rgba(138, 148, 158, .07);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
font-size: .75rem;
|
|
cursor: pointer;
|
|
}
|
|
.zl-band-chip:hover { border-color: var(--ton-linie-satt, rgba(138, 148, 158, .38)); }
|
|
.zl-band-pause .zl-band-chip { color: var(--ton-schrift-2, #9aa6ad); }
|
|
.zl-band-chip.nichtdran { border-style: dashed; }
|
|
.zl-band-etage {
|
|
font-size: .64rem;
|
|
padding: 0 .3rem;
|
|
border-radius: 4px;
|
|
background: rgba(138, 148, 158, .16);
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
|
|
/* Legende */
|
|
.zl-legende {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .4rem 1.1rem;
|
|
margin-top: .8rem;
|
|
font-size: .72rem;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
.zl-legende span { display: inline-flex; align-items: center; gap: .4rem; }
|
|
.zl-legende i { display: inline-block; }
|
|
.zl-l-punkt { width: 10px; height: 10px; border-radius: 50%; border: 2px solid #5fb0dc; }
|
|
.zl-l-punkt.gelaufen { background: #5fb0dc; }
|
|
.zl-l-punkt.nichtdran { border-style: dashed; border-color: var(--ton-schrift-3, #6f7a81); }
|
|
.zl-l-balken { width: 22px; height: 7px; border-radius: 4px; background: rgba(95, 176, 220, .22); border: 1px solid rgba(95, 176, 220, .4); }
|
|
.zl-l-kette { width: 22px; border-top: 1.2px dashed rgba(154, 166, 173, .7); }
|
|
|
|
/* Karte zu einem Eintrag: schwebt ueber der Zeitleiste, Akzent der Bahn. */
|
|
.zl { position: relative; }
|
|
.zl-karte {
|
|
position: absolute;
|
|
z-index: 20;
|
|
width: min(22rem, 100%);
|
|
padding: .7rem .85rem .75rem;
|
|
border-radius: var(--ton-radius-klein, .5rem);
|
|
border: 1px solid rgba(var(--zl-farbe-rgb), .45);
|
|
border-top: 3px solid var(--zl-farbe);
|
|
background: var(--ton-flaeche-2, #2b3237);
|
|
box-shadow: 0 10px 28px rgba(0, 0, 0, .45);
|
|
font-size: .8rem;
|
|
}
|
|
.zl-karte-kopf {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .45rem;
|
|
margin-bottom: .45rem;
|
|
}
|
|
.zl-karte-kopf .bi { color: var(--zl-farbe); }
|
|
.zl-karte-name {
|
|
flex: 1 1 auto;
|
|
font: 500 .92rem var(--font-anzeige, inherit);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
}
|
|
.zl-karte .btn-close.zl-karte-zu { font-size: .6rem; opacity: .6; }
|
|
.zl-karte-zeile {
|
|
display: flex;
|
|
gap: .5rem;
|
|
align-items: baseline;
|
|
padding: .12rem 0;
|
|
color: var(--ton-schrift, #e8ede9);
|
|
}
|
|
.zl-karte-zeile .bi { color: var(--ton-schrift-3, #6f7a81); flex: none; }
|
|
.zl-karte-stand.gut, .zl-karte-stand.gut .bi { color: var(--ton-gut, #4caf7d); }
|
|
.zl-karte-stand.leise { color: var(--ton-schrift-2, #9aa6ad); }
|
|
.zl-karte-wenn {
|
|
margin-top: .35rem;
|
|
padding-top: .35rem;
|
|
border-top: 1px solid var(--zl-linie);
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
font-size: .72rem;
|
|
line-height: 1.4;
|
|
max-height: 5.6em;
|
|
overflow: auto;
|
|
}
|
|
.zl-karte-knoepfe {
|
|
display: flex;
|
|
gap: .35rem;
|
|
margin-top: .65rem;
|
|
}
|
|
.zl-karte-knoepfe .btn.zl-karte-weg {
|
|
margin-left: auto;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
}
|
|
.zl-karte-knoepfe .btn.zl-karte-weg:hover { color: var(--ton-schlecht, #e06060); }
|
|
|
|
/* ===========================================================================
|
|
Einfuehrung (restricted/einfuehrung.php, js/solar/einfuehrung.js)
|
|
|
|
Eine Folie: links das Bild in einem ruhigen Rahmen (Handy-Aufnahmen
|
|
schmal wie ein Telefon, Desktop-Aufnahmen breit), rechts der Text mit den
|
|
nummerierten Erklaerungen. Auf schmalen Schirmen untereinander.
|
|
=========================================================================== */
|
|
.ef {
|
|
max-width: 1180px;
|
|
margin: 0 auto 2rem;
|
|
outline: none;
|
|
}
|
|
.ef-kopf {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .6rem 1rem;
|
|
margin-bottom: .6rem;
|
|
}
|
|
.ef-teile { display: flex; gap: .35rem; flex-wrap: wrap; }
|
|
.ef-teil {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
padding: .3rem .8rem .3rem .35rem;
|
|
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
border-radius: 999px;
|
|
background: transparent;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
font: 500 .85rem var(--font-anzeige, inherit);
|
|
cursor: pointer;
|
|
transition: background .15s, border-color .15s, color .15s;
|
|
}
|
|
.ef-teil-nr {
|
|
display: inline-grid;
|
|
place-items: center;
|
|
width: 1.5rem;
|
|
height: 1.5rem;
|
|
border-radius: 50%;
|
|
background: rgba(138, 148, 158, .18);
|
|
font-size: .75rem;
|
|
}
|
|
.ef-teil.aktiv {
|
|
border-color: var(--ton-sonne, #f0b429);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
background: rgba(var(--ton-sonne-rgb, 240, 180, 41), .1);
|
|
}
|
|
.ef-teil.aktiv .ef-teil-nr { background: var(--ton-sonne, #f0b429); color: #1b1f22; }
|
|
.ef-kopf .form-select.ef-kapitel { width: auto; min-width: 12rem; margin-left: auto; }
|
|
.ef-zaehler {
|
|
font-size: .8rem;
|
|
color: var(--ton-schrift-3, #6f7a81);
|
|
font-variant-numeric: tabular-nums;
|
|
min-width: 3.5rem;
|
|
text-align: right;
|
|
}
|
|
.ef-fortschritt {
|
|
height: 3px;
|
|
border-radius: 2px;
|
|
background: var(--ton-linie, rgba(138, 148, 158, .2));
|
|
margin-bottom: 1.2rem;
|
|
overflow: hidden;
|
|
}
|
|
.ef-fortschritt span {
|
|
display: block;
|
|
height: 100%;
|
|
width: 0;
|
|
background: var(--ton-sonne, #f0b429);
|
|
transition: width .35s ease;
|
|
}
|
|
|
|
/* Buehne und Folie */
|
|
.ef-buehne { position: relative; }
|
|
.ef-folie {
|
|
display: grid;
|
|
gap: 2rem;
|
|
align-items: center;
|
|
padding: 1.6rem;
|
|
border-radius: var(--ton-radius, .875rem);
|
|
background: var(--ton-flaeche, #23282c);
|
|
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
min-height: 34rem;
|
|
}
|
|
.ef-folie.mit-bild.form-handy { grid-template-columns: minmax(0, 330px) minmax(0, 1fr); }
|
|
.ef-folie.mit-bild.form-breit { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); }
|
|
.ef-folie.ohne-bild { grid-template-columns: minmax(0, 46rem); justify-content: center; }
|
|
.ef-folie.rein-vor { animation: ef-rein-vor .32s ease both; }
|
|
.ef-folie.rein-rueck { animation: ef-rein-rueck .32s ease both; }
|
|
@keyframes ef-rein-vor { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
|
|
@keyframes ef-rein-rueck { from { opacity: 0; transform: translateX(-18px); } to { opacity: 1; transform: none; } }
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.ef-folie.rein-vor, .ef-folie.rein-rueck { animation: none; }
|
|
}
|
|
|
|
.ef-bild { margin: 0; justify-self: center; width: 100%; }
|
|
.ef-rahmen {
|
|
position: relative;
|
|
border-radius: 14px;
|
|
overflow: visible;
|
|
box-shadow: 0 18px 40px rgba(0, 0, 0, .35);
|
|
}
|
|
.ef-rahmen img {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
border-radius: 14px;
|
|
border: 1px solid var(--ton-linie-satt, rgba(138, 148, 158, .38));
|
|
}
|
|
/* Handy-Aufnahmen in einem angedeuteten Telefon. */
|
|
.form-handy .ef-rahmen {
|
|
max-width: 330px;
|
|
margin: 0 auto;
|
|
padding: 10px;
|
|
border-radius: 30px;
|
|
background: #0d0f11;
|
|
border: 1px solid rgba(138, 148, 158, .3);
|
|
}
|
|
.form-handy .ef-rahmen img { border-radius: 22px; border: 0; }
|
|
|
|
/* Die nummerierten Punkte im Bild */
|
|
.ef-marke {
|
|
position: absolute;
|
|
transform: translate(-50%, -50%);
|
|
display: grid;
|
|
place-items: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 50%;
|
|
background: var(--ton-sonne, #f0b429);
|
|
color: #1b1f22;
|
|
font: 700 .8rem var(--font-anzeige, inherit);
|
|
box-shadow: 0 0 0 3px rgba(27, 31, 34, .85), 0 4px 10px rgba(0, 0, 0, .45);
|
|
cursor: pointer;
|
|
z-index: 2;
|
|
transition: transform .15s ease, box-shadow .15s ease;
|
|
}
|
|
.ef-marke::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: -6px;
|
|
border-radius: 50%;
|
|
border: 2px solid rgba(var(--ton-sonne-rgb, 240, 180, 41), .55);
|
|
animation: ef-puls 2.4s ease-out infinite;
|
|
}
|
|
.ef-marke.hell { transform: translate(-50%, -50%) scale(1.25); box-shadow: 0 0 0 3px rgba(27, 31, 34, .85), 0 0 0 8px rgba(var(--ton-sonne-rgb, 240, 180, 41), .35); }
|
|
@keyframes ef-puls { 0% { transform: scale(.8); opacity: .9; } 70%, 100% { transform: scale(1.5); opacity: 0; } }
|
|
@media (prefers-reduced-motion: reduce) { .ef-marke::after { animation: none; opacity: 0; } }
|
|
|
|
/* Text */
|
|
.ef-kapitelname {
|
|
font-size: .72rem;
|
|
letter-spacing: .12em;
|
|
text-transform: uppercase;
|
|
color: var(--ton-sonne, #f0b429);
|
|
margin-bottom: .4rem;
|
|
}
|
|
.ef-titel {
|
|
font: 600 1.7rem/1.2 var(--font-anzeige, inherit);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
margin: 0 0 .8rem;
|
|
}
|
|
.ef-einleitung {
|
|
font-size: 1.02rem;
|
|
line-height: 1.6;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
margin-bottom: 1.1rem;
|
|
}
|
|
.ef-einleitung b, .ef-marken b, .ef-punkte b, .ef-tipp b { color: var(--ton-schrift, #e8ede9); font-weight: 600; }
|
|
.ef-marken {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0 0 1rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .35rem;
|
|
}
|
|
.ef-marken li {
|
|
display: flex;
|
|
gap: .7rem;
|
|
align-items: flex-start;
|
|
padding: .45rem .6rem;
|
|
border-radius: 10px;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
line-height: 1.5;
|
|
cursor: pointer;
|
|
transition: background .15s;
|
|
}
|
|
.ef-marken li:hover, .ef-marken li.hell { background: rgba(var(--ton-sonne-rgb, 240, 180, 41), .09); }
|
|
.ef-marke-nr {
|
|
flex: none;
|
|
display: grid;
|
|
place-items: center;
|
|
width: 22px;
|
|
height: 22px;
|
|
margin-top: 1px;
|
|
border-radius: 50%;
|
|
background: var(--ton-sonne, #f0b429);
|
|
color: #1b1f22;
|
|
font: 700 .72rem var(--font-anzeige, inherit);
|
|
}
|
|
.ef-punkte {
|
|
padding-left: 1.1rem;
|
|
margin: 0 0 1rem;
|
|
color: var(--ton-schrift-2, #9aa6ad);
|
|
line-height: 1.6;
|
|
}
|
|
.ef-punkte li { margin-bottom: .45rem; }
|
|
.ef-punkte li::marker { color: var(--ton-sonne, #f0b429); }
|
|
.ef-tipp {
|
|
display: flex;
|
|
gap: .6rem;
|
|
align-items: flex-start;
|
|
margin: .4rem 0 0;
|
|
padding: .7rem .9rem;
|
|
border-radius: 10px;
|
|
background: rgba(0, 120, 143, .14);
|
|
border: 1px solid rgba(0, 120, 143, .35);
|
|
color: var(--ton-schrift, #e8ede9);
|
|
font-size: .92rem;
|
|
line-height: 1.5;
|
|
}
|
|
.ef-tipp .bi { color: #35b6cf; margin-top: .1rem; }
|
|
|
|
/* Fuss */
|
|
.ef-fuss {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-top: 1rem;
|
|
}
|
|
.ef-punkte-nav {
|
|
flex: 1 1 auto;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 5px;
|
|
}
|
|
.ef-punkt {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: rgba(138, 148, 158, .3);
|
|
cursor: pointer;
|
|
transition: background .15s, transform .15s;
|
|
}
|
|
.ef-punkt.neuer-teil { margin-left: 10px; }
|
|
.ef-punkt.aktiv { background: var(--ton-sonne, #f0b429); transform: scale(1.35); }
|
|
|
|
@media (max-width: 991.98px) {
|
|
.ef-folie.mit-bild.form-handy,
|
|
.ef-folie.mit-bild.form-breit { grid-template-columns: minmax(0, 1fr); gap: 1.4rem; }
|
|
.ef-folie { padding: 1rem; min-height: 0; }
|
|
.form-handy .ef-rahmen { max-width: 280px; }
|
|
.ef-titel { font-size: 1.4rem; }
|
|
.ef-kopf .form-select.ef-kapitel { margin-left: 0; flex: 1 1 12rem; }
|
|
.ef-punkte-nav { display: none; }
|
|
.ef-fuss { justify-content: space-between; position: sticky; bottom: 0; padding: .6rem 0; background: var(--ton-grund, #191d20); z-index: 5; }
|
|
}
|
|
|
|
/* Ansicht umschalten: eine Klasse mehr als Bootstrap (solar.css laedt vorher). */
|
|
.zl-ansicht .btn.active {
|
|
background: rgba(0, 120, 143, .25);
|
|
border-color: #00788F;
|
|
color: var(--ton-schrift, #e8ede9);
|
|
}
|
|
|
|
/* ==========================================================================
|
|
Energiefluss-Übersicht (js/solar/energiefluss.js, Katalog in
|
|
js/solar/energieflussKatalog.js).
|
|
|
|
Farben je Kreis kommen als --farbe aus dem Katalog, Breite und Tempo der
|
|
Flüsse als --breite/--dauer aus dem Renderer. Alles Übrige steht hier.
|
|
Transformationen liegen nie auf Elementen mit transform-Attribut (Kreis-
|
|
gruppe, Plakette) - CSS würde deren Lage überschreiben.
|
|
========================================================================== */
|
|
.enf-behaelter { position: relative; }
|
|
svg.enf {
|
|
display: block; width: 100%; height: auto; max-height: 80vh;
|
|
font-family: "Poppins", system-ui, sans-serif;
|
|
user-select: none; -webkit-tap-highlight-color: transparent;
|
|
}
|
|
svg.enf.enf-hoch { max-height: none; }
|
|
|
|
/* --- Kreis ------------------------------------------------------------- */
|
|
.enf .enf-hof { fill: #1c2024; stroke: rgba(255, 255, 255, .06); stroke-width: 1.5; transition: stroke .3s, stroke-width .3s; }
|
|
.enf .enf-spur { fill: none; stroke: #2b3139; stroke-width: 14; }
|
|
.enf .enf-bogen {
|
|
fill: none; stroke-width: 14; stroke-dasharray: 0 100;
|
|
transition: stroke-dasharray 1s cubic-bezier(.2, .7, .2, 1), stroke-dashoffset 1s cubic-bezier(.2, .7, .2, 1), stroke .4s;
|
|
}
|
|
.enf .enf-scheibe { fill: #20252b; }
|
|
.enf .enf-wert { fill: var(--ton-schrift, #e8ede9); font-size: 32px; font-weight: 600; text-anchor: middle; font-variant-numeric: tabular-nums; }
|
|
.enf .enf-neben { fill: var(--ton-schrift-2, #9aa6ad); font-size: 14.5px; text-anchor: middle; font-variant-numeric: tabular-nums; }
|
|
.enf .enf-name {
|
|
fill: var(--ton-schrift-2, #9aa6ad); font-size: 16px; font-weight: 600; text-anchor: middle; letter-spacing: .08em; text-transform: uppercase;
|
|
/* Kontur in Kartenfarbe: bleibt lesbar, wo ein Fluss unter dem Namen durchlaeuft. */
|
|
paint-order: stroke; stroke: var(--ton-flaeche, #23282c); stroke-width: 7px; stroke-linejoin: round;
|
|
}
|
|
|
|
.enf .enf-koerper { transform-box: view-box; transform-origin: 0 0; transition: transform .3s cubic-bezier(.2, .7, .2, 1), opacity .6s; }
|
|
.enf .enf-ruhe .enf-koerper { opacity: .6; }
|
|
.enf .enf-ruhe .enf-name { opacity: .7; }
|
|
.enf .enf-aus { opacity: .3; }
|
|
.enf .enf-aus .enf-spur { stroke-dasharray: 8 8; }
|
|
|
|
.enf .enf-klickbar { cursor: pointer; outline: none; }
|
|
.enf .enf-klickbar:hover .enf-koerper { transform: scale(1.045); }
|
|
.enf .enf-klickbar:hover .enf-hof,
|
|
.enf .enf-klickbar:focus-visible .enf-hof { stroke: var(--farbe); stroke-width: 3; }
|
|
.enf .enf-tipp circle { fill: #262c33; stroke: #3a424b; stroke-width: 1.5; }
|
|
.enf .enf-tipp path { fill: none; stroke: var(--farbe); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; transition: transform .25s; }
|
|
.enf .enf-klickbar:hover .enf-tipp path { transform: translateX(2px); }
|
|
|
|
/* --- Füllstand als Welle ------------------------------------------------ */
|
|
.enf .enf-fuellung {
|
|
--r: 67;
|
|
transform: translateY(calc(var(--r) * (1 - 2 * var(--stand)) * 1px));
|
|
transition: transform 1.4s cubic-bezier(.2, .7, .2, 1);
|
|
}
|
|
.enf .enf-welle { fill: var(--farbe); fill-opacity: .14; animation: enf-wogen 8s linear infinite; }
|
|
@keyframes enf-wogen { to { transform: translateX(calc(var(--r) * 2px)); } }
|
|
|
|
/* --- Symbole ------------------------------------------------------------ */
|
|
.enf .enf-symbol { color: var(--farbe); fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
|
|
.enf .enf-s-fein { stroke-width: 1.1; opacity: .75; }
|
|
.enf .enf-s-kern { fill: currentColor; fill-opacity: .2; transition: fill-opacity .6s; }
|
|
.enf .enf-an .enf-s-kern { fill-opacity: .55; }
|
|
.enf .enf-s-strahlen { transform-box: view-box; transform-origin: 0 0; opacity: .55; transition: opacity .6s; }
|
|
.enf .enf-an .enf-s-strahlen { opacity: 1; animation: enf-drehen 30s linear infinite; }
|
|
@keyframes enf-drehen { to { transform: rotate(360deg); } }
|
|
|
|
.enf .enf-s-stand {
|
|
fill: currentColor; stroke: none; opacity: .45;
|
|
transform-box: fill-box; transform-origin: 50% 100%;
|
|
transform: scaleY(var(--stand, 0)); transition: transform 1.2s cubic-bezier(.2, .7, .2, 1);
|
|
}
|
|
.enf .enf-s-blitz { fill: currentColor; stroke: none; opacity: 0; transition: opacity .4s; }
|
|
.enf .enf-sym-akku .enf-s-blitz { fill: #fff; }
|
|
.enf .enf-s-blitz.enf-s-an,
|
|
.enf .enf-laden .enf-s-blitz,
|
|
.enf .enf-an.enf-knoten .enf-sym-wallbox .enf-s-blitz { opacity: 1; }
|
|
.enf .enf-laden .enf-s-blitz,
|
|
.enf .enf-an .enf-sym-wallbox .enf-s-blitz { animation: enf-puls 1.8s ease-in-out infinite; }
|
|
@keyframes enf-puls { 50% { opacity: .35; } }
|
|
|
|
.enf .enf-an .enf-s-welle { animation: enf-puls 1.4s ease-in-out infinite; }
|
|
.enf .enf-s-ebene { fill: currentColor; stroke: none; opacity: .38; }
|
|
.enf .enf-an .enf-sym-wallbox .enf-s-kabel { stroke-dasharray: 2.4 2.4; animation: enf-kabel .9s linear infinite; }
|
|
@keyframes enf-kabel { to { stroke-dashoffset: -4.8; } }
|
|
.enf .enf-s-temps rect { stroke: none; fill: #2b3139; opacity: .9; transition: fill 1.5s; }
|
|
.enf .enf-s-spirale { stroke-width: 1.3; transition: stroke .4s; }
|
|
.enf .enf-an .enf-s-spirale { stroke: #ffb04a; animation: enf-gluehen 1.1s ease-in-out infinite alternate; }
|
|
@keyframes enf-gluehen { to { stroke: #ff5a2a; } }
|
|
|
|
/* --- Plaketten ---------------------------------------------------------- */
|
|
.enf .enf-plakette { animation: enf-einblenden .5s both; animation-delay: calc(var(--i) * 80ms); }
|
|
.enf .enf-plakette circle,
|
|
.enf .enf-plakette rect { fill: #1c2024; stroke: var(--pfarbe); stroke-width: 2; }
|
|
.enf .enf-plakette-glyph { font-family: bootstrap-icons; font-size: 16px; fill: var(--pfarbe); text-anchor: middle; dominant-baseline: central; }
|
|
.enf .enf-plakette-text { font-size: 14px; font-weight: 600; fill: var(--ton-schrift, #e8ede9); dominant-baseline: central; }
|
|
|
|
/* --- Bewässerung --------------------------------------------------------- */
|
|
.enf .enf-zone-glyph { font-family: bootstrap-icons; font-size: 17px; fill: currentColor; color: #6c757d; text-anchor: middle; dominant-baseline: central; transition: color .4s; }
|
|
.enf .enf-zone-name { font-size: 15px; fill: var(--ton-schrift-2, #9aa6ad); dominant-baseline: central; }
|
|
.enf .enf-zone-status { font-size: 15px; font-weight: 600; fill: var(--ton-schrift, #e8ede9); text-anchor: end; dominant-baseline: central; font-variant-numeric: tabular-nums; }
|
|
|
|
/* --- Flüsse -------------------------------------------------------------- */
|
|
.enf .enf-bett {
|
|
fill: none; stroke: #252b31; stroke-linecap: round;
|
|
stroke-width: calc(var(--breite, 0) * 1px + 5px);
|
|
stroke-dasharray: 1 1; stroke-dashoffset: 1;
|
|
transition: stroke-width .8s;
|
|
animation: enf-zeichnen .9s cubic-bezier(.5, 0, .2, 1) forwards; animation-delay: var(--verz);
|
|
}
|
|
@keyframes enf-zeichnen { to { stroke-dashoffset: 0; } }
|
|
.enf .enf-strom,
|
|
.enf .enf-strom-hof {
|
|
fill: none; stroke: var(--farbe); stroke-linecap: round;
|
|
stroke-dasharray: 0 20;
|
|
/* Stufen statt stetig: stroke-dashoffset laeuft nicht auf dem Compositor,
|
|
jede Stufe ist ein Neuzeichnen. 20 je Abschnitt sehen fluessig aus. */
|
|
animation: enf-fliessen var(--dauer, 1.5s) steps(20) infinite;
|
|
transition: stroke-width .8s, opacity .6s;
|
|
}
|
|
.enf .enf-strom { stroke-width: calc(var(--breite, 0) * 1px); }
|
|
.enf .enf-strom-hof { stroke-width: calc(var(--breite, 0) * 1.9px); opacity: .14; }
|
|
.enf .enf-rueck .enf-strom,
|
|
.enf .enf-rueck .enf-strom-hof { animation-direction: reverse; }
|
|
.enf .enf-still .enf-strom,
|
|
.enf .enf-still .enf-strom-hof { opacity: 0; animation: none; }
|
|
@keyframes enf-fliessen { to { stroke-dashoffset: -20; } }
|
|
|
|
/* --- Auftritt ------------------------------------------------------------ */
|
|
.enf .enf-koerper { animation: enf-erscheinen .7s cubic-bezier(.2, .9, .3, 1.25) backwards; animation-delay: var(--verz); }
|
|
.enf .enf-name { animation: enf-einblenden .7s backwards; animation-delay: calc(var(--verz) + 150ms); }
|
|
@keyframes enf-erscheinen { from { opacity: 0; transform: scale(.55); } }
|
|
@keyframes enf-einblenden { from { opacity: 0; } }
|
|
|
|
.enf.enf-pause,
|
|
.enf.enf-pause * { animation-play-state: paused !important; }
|
|
|
|
/* --- Bearbeiten (Einstellungen) ------------------------------------------ */
|
|
.enf.enf-bearbeiten .enf-koerper,
|
|
.enf.enf-bearbeiten .enf-name,
|
|
.enf.enf-bearbeiten .enf-bett { animation: none; stroke-dashoffset: 0; }
|
|
.enf.enf-bearbeiten .enf-ziehbar { cursor: grab; touch-action: none; }
|
|
.enf.enf-bearbeiten .enf-zieht { cursor: grabbing; }
|
|
.enf.enf-bearbeiten .enf-zieht .enf-hof { stroke: var(--farbe); stroke-width: 3; }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.enf, .enf * { animation: none !important; transition: none !important; }
|
|
.enf .enf-bett { stroke-dashoffset: 0; }
|
|
}
|
|
|
|
/* --- Einstellungen: Reiter "Übersicht" ------------------------------------ */
|
|
.enf-vorschau { background: var(--ton-grund, #191d20); border-radius: var(--ton-radius, .875rem); padding: .5rem; position: sticky; top: 4.5rem; }
|
|
.enf-e-zeile {
|
|
display: grid; grid-template-columns: auto minmax(0, 1fr) auto 1.2rem; gap: .35rem .7rem; align-items: center;
|
|
padding: .45rem 0; border-bottom: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
}
|
|
.enf-e-zeile.enf-e-aus .enf-e-name { opacity: .5; }
|
|
.enf-e-name { display: flex; align-items: center; gap: .55rem; margin: 0; }
|
|
.enf-e-punkt { flex: 0 0 auto; width: .7rem; height: .7rem; border-radius: 50%; background: var(--farbe); box-shadow: 0 0 0 3px color-mix(in srgb, var(--farbe) 22%, transparent); }
|
|
.enf-e-eltern { white-space: nowrap; }
|
|
.enf-e-flaechen { grid-column: 2 / -1; padding: .2rem 0 .3rem; }
|
|
.enf-e-flaeche { display: grid; grid-template-columns: 2.2rem minmax(0, 1fr) 4.2rem 4.2rem 1.2rem; gap: .35rem; align-items: center; margin-bottom: .3rem; }
|
|
.enf-e-flaeche .form-control.form-control-color { width: 2.2rem; height: 1.9rem; padding: .15rem; }
|
|
|
|
/* ==========================================================================
|
|
Anlage-Ansicht (js/solar/anlage.js, Katalog js/solar/anlageKatalog.js).
|
|
Dieselben Töne wie die Energiefluss-Übersicht.
|
|
========================================================================== */
|
|
.anl { display: grid; gap: .9rem; }
|
|
.anl .anl-flaechen { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .6rem; }
|
|
.anl .anl-flaeche { background: var(--ton-grund, #191d20); border-radius: .8rem; padding: .6rem .85rem .55rem; display: grid; gap: .35rem; }
|
|
.anl .anl-flaeche-kopf { display: flex; align-items: baseline; gap: .5rem; }
|
|
.anl .anl-flaeche-kopf i { width: .6rem; height: .6rem; border-radius: 50%; background: var(--farbe); align-self: center; box-shadow: 0 0 0 3px color-mix(in srgb, var(--farbe) 25%, transparent); }
|
|
.anl .anl-flaeche-kopf span { font-weight: 600; color: var(--ton-schrift, #e8ede9); }
|
|
.anl .anl-flaeche-kopf b { margin-left: auto; font-size: 1.2rem; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
.anl .anl-flaeche-balken { height: 5px; border-radius: 3px; background: #2b3139; overflow: hidden; }
|
|
.anl .anl-flaeche-balken span { display: block; height: 100%; background: var(--farbe); border-radius: 3px; transition: width 1s cubic-bezier(.2, .7, .2, 1); }
|
|
.anl .anl-flaeche small { color: var(--ton-schrift-3, #6f7a81); font-size: .76rem; }
|
|
@media (max-width: 575.98px) {
|
|
/* Auf dem Handy drei schmale Kacheln nebeneinander statt drei Zeilen. */
|
|
.anl .anl-flaechen { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .4rem; }
|
|
.anl .anl-flaeche { padding: .45rem .55rem; }
|
|
.anl .anl-flaeche-kopf { flex-wrap: wrap; gap: .1rem .4rem; }
|
|
.anl .anl-flaeche-kopf span { font-size: .8rem; }
|
|
.anl .anl-flaeche-kopf b { margin-left: 0; width: 100%; font-size: 1rem; }
|
|
.anl .anl-flaeche small { display: none; }
|
|
}
|
|
|
|
.anl .anl-plan-rahmen { background: var(--ton-grund, #191d20); border-radius: .9rem; padding: .4rem; }
|
|
svg.anl-plan { display: block; width: 100%; height: auto; max-height: 72vh; font-family: "Poppins", system-ui, sans-serif; user-select: none; -webkit-tap-highlight-color: transparent; }
|
|
svg.anl-plan.anl-hoch { max-height: none; }
|
|
|
|
.anl-plan .anl-dachflaeche { fill: var(--farbe); fill-opacity: .06; stroke: var(--farbe); stroke-opacity: .18; stroke-width: 22; stroke-linejoin: round; }
|
|
.anl-plan .anl-bereich { fill: var(--farbe); fill-opacity: .035; stroke: var(--farbe); stroke-opacity: .25; stroke-width: 2; stroke-dasharray: 7 7; }
|
|
|
|
.anl-plan .anl-teil { transform-box: fill-box; transform-origin: center; animation: anl-auf .55s cubic-bezier(.2, .9, .3, 1.2) backwards; animation-delay: calc(var(--d) * 1ms); }
|
|
@keyframes anl-auf { from { opacity: 0; transform: scale(.4); } }
|
|
.anl-plan .anl-koerper { fill: #2f353c; stroke: rgba(255, 255, 255, .1); stroke-width: 1; transition: fill 1s, stroke .2s, stroke-width .2s; }
|
|
.anl-plan .anl-platte .anl-koerper,
|
|
.anl-plan .anl-string .anl-koerper { fill: var(--fuellung, #3b3a32); }
|
|
.anl-plan .anl-platte.anl-offline .anl-koerper { fill: #262b31; stroke: #5c646d; stroke-dasharray: 4 3; }
|
|
.anl-plan .anl-zellen,
|
|
.anl-plan .anl-glanz { pointer-events: none; }
|
|
.anl-plan .anl-glanz { fill: #fff; opacity: 0; }
|
|
/* Lichtwelle: jede Platte hellt kurz auf, versetzt nach ihrer Lage. */
|
|
.anl-plan.anl-sonnig .anl-hell .anl-glanz {
|
|
animation: anl-glanz 7s ease-in-out infinite; animation-delay: calc(var(--glanz) * 7s); }
|
|
@keyframes anl-glanz { 0%, 100% { opacity: 0; } 7% { opacity: .3; } 15% { opacity: 0; } }
|
|
|
|
.anl-plan .anl-platte,
|
|
.anl-plan .anl-string { cursor: pointer; }
|
|
.anl-plan .anl-platte:hover .anl-koerper,
|
|
.anl-plan .anl-string:hover .anl-koerper { stroke: rgba(255, 255, 255, .7); stroke-width: 2; }
|
|
.anl-plan .anl-geschwister .anl-koerper { stroke: #e3e6ea; stroke-width: 2; stroke-dasharray: 6 4; }
|
|
.anl-plan .anl-gewaehlt .anl-koerper { stroke: #fff; stroke-width: 3.5; stroke-dasharray: none; }
|
|
|
|
.anl-plan .anl-w { font-size: 17px; font-weight: 600; fill: #c9d0d6; text-anchor: middle; dominant-baseline: central; pointer-events: none; font-variant-numeric: tabular-nums; }
|
|
.anl-plan .anl-hell .anl-w { fill: #15181c; }
|
|
.anl-plan .anl-offline .anl-w { fill: #8d96a0; }
|
|
.anl-plan.anl-klein .anl-w { display: none; }
|
|
|
|
.anl-plan .anl-warnung { display: none; pointer-events: none; }
|
|
.anl-plan .anl-schwach .anl-warnung { display: inline; }
|
|
.anl-plan .anl-warnung circle { fill: #e5624f; stroke: #1c2024; stroke-width: 2; }
|
|
.anl-plan .anl-warnung .anl-warnung-hof { stroke: none; opacity: .5; transform-box: fill-box; transform-origin: center; animation: anl-puls 1.8s ease-out infinite; }
|
|
@keyframes anl-puls { from { transform: scale(.6); opacity: .6; } to { transform: scale(1.5); opacity: 0; } }
|
|
.anl-plan .anl-warnung text { fill: #fff; font-size: 13px; font-weight: 700; text-anchor: middle; dominant-baseline: central; }
|
|
|
|
.anl-plan .anl-schild { pointer-events: none; }
|
|
.anl-plan .anl-schild rect { fill: rgba(21, 24, 28, .86); stroke: rgba(255, 255, 255, .08); }
|
|
.anl-plan .anl-schild-wert { font-size: 23px; font-weight: 600; fill: var(--ton-schrift, #e8ede9); text-anchor: middle; dominant-baseline: central; font-variant-numeric: tabular-nums; }
|
|
.anl-plan .anl-schild-name { font-size: 11px; font-weight: 600; fill: var(--ton-schrift-2, #9aa6ad); text-anchor: middle; dominant-baseline: central; text-transform: uppercase; letter-spacing: .08em; }
|
|
.anl-plan .anl-nord path { fill: #808090; }
|
|
.anl-plan .anl-nord text { fill: #808090; font-size: 15px; font-weight: 600; text-anchor: middle; }
|
|
.anl-plan.anl-pause,
|
|
.anl-plan.anl-pause * { animation-play-state: paused !important; }
|
|
|
|
.anl .anl-legende { display: flex; flex-wrap: wrap; gap: .3rem 1.1rem; font-size: .78rem; color: var(--ton-schrift-2, #9aa6ad); padding: 0 .3rem; }
|
|
.anl .anl-legende i { display: inline-block; width: .75rem; height: .75rem; border-radius: 3px; margin-right: .35rem; vertical-align: -.1rem; }
|
|
.anl .anl-l-verlauf { width: 2.6rem !important; background: linear-gradient(90deg, #3b3a32, #f7c000); }
|
|
.anl .anl-l-warnung { background: #e5624f; border-radius: 50% !important; color: #fff; font-style: normal; font-size: .6rem; font-weight: 700; text-align: center; line-height: .75rem; }
|
|
.anl .anl-l-offline { background: #262b31; box-shadow: inset 0 0 0 1px #5c646d; }
|
|
|
|
.anl .anl-unten { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: .9rem; align-items: start; }
|
|
@media (max-width: 860px) { .anl .anl-unten { grid-template-columns: minmax(0, 1fr); } }
|
|
.anl .anl-fach { background: var(--ton-grund, #191d20); border-radius: .9rem; padding: .9rem 1rem; display: grid; gap: .7rem; align-content: start; }
|
|
.anl .anl-ueberschrift { font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--ton-schrift-2, #9aa6ad); margin: 0; }
|
|
.anl .anl-kopf { display: flex; justify-content: space-between; align-items: center; gap: .6rem; }
|
|
.anl .anl-pille { font-size: .72rem; font-weight: 600; border-radius: 99px; padding: .1rem .55rem; white-space: nowrap; color: var(--ton-schrift-2, #9aa6ad); background: rgba(255, 255, 255, .06); }
|
|
.anl .anl-pille.anl-ok { color: #6cbe58; background: rgba(108, 190, 88, .13); }
|
|
.anl .anl-pille.anl-warn { color: #e8a33a; background: rgba(232, 163, 58, .14); }
|
|
.anl .anl-pille.anl-krit { color: #e5624f; background: rgba(229, 98, 79, .14); }
|
|
.anl .anl-gross { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem; }
|
|
.anl .anl-gross b { font-size: 2rem; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--ton-schrift, #e8ede9); }
|
|
.anl .anl-gross span { color: var(--ton-schrift-2, #9aa6ad); font-size: .88rem; }
|
|
.anl .anl-funken { width: 100%; height: auto; display: block; }
|
|
.anl .anl-gitter { stroke: #2a3037; }
|
|
.anl .anl-funken-flaeche { fill: rgba(247, 192, 0, .14); }
|
|
.anl .anl-funken-linie { fill: none; stroke: #f7c000; stroke-width: 2; stroke-linejoin: round; }
|
|
.anl .anl-funken-punkt { fill: #f7c000; }
|
|
.anl .anl-achse { fill: var(--ton-schrift-3, #6f7a81); font-size: 10px; }
|
|
.anl .anl-funken-leer { color: var(--ton-schrift-3, #6f7a81); font-size: .82rem; padding: .7rem 0; }
|
|
.anl .anl-paare { display: grid; grid-template-columns: auto 1fr; gap: .25rem .9rem; font-size: .88rem; margin: 0; }
|
|
.anl .anl-paare dt { color: var(--ton-schrift-2, #9aa6ad); font-weight: 400; }
|
|
.anl .anl-paare dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
|
|
.anl .anl-hinweis { font-size: .84rem; color: var(--ton-schrift-2, #9aa6ad); margin: 0; }
|
|
.anl button.anl-link { all: unset; cursor: pointer; color: #8fb8de; }
|
|
.anl button.anl-link:hover { text-decoration: underline; }
|
|
|
|
.anl .anl-wr,
|
|
.anl .anl-eingaenge,
|
|
.anl .anl-hinweise { list-style: none; margin: 0; padding: 0; display: grid; gap: .15rem; }
|
|
.anl .anl-wr button,
|
|
.anl .anl-eingaenge button,
|
|
.anl .anl-hinweise button { all: unset; box-sizing: border-box; cursor: pointer; width: 100%; border-radius: .5rem; padding: .42rem .55rem; transition: background .2s; }
|
|
.anl .anl-wr button:hover,
|
|
.anl .anl-eingaenge button:hover,
|
|
.anl .anl-hinweise button:hover { background: var(--ton-flaeche-2, #2b3237); }
|
|
.anl .anl-wr button:focus-visible,
|
|
.anl .anl-eingaenge button:focus-visible,
|
|
.anl .anl-hinweise button:focus-visible,
|
|
.anl button.anl-link:focus-visible { outline: 2px solid #8fb8de; outline-offset: 1px; }
|
|
.anl .anl-wr button { display: grid; grid-template-columns: minmax(0, 1fr) auto 6.5rem; gap: .7rem; align-items: center; font-size: .9rem; }
|
|
.anl .anl-wr button > .anl-pille { justify-self: end; }
|
|
.anl .anl-wr button[aria-pressed="true"] { background: var(--ton-flaeche-2, #2b3237); box-shadow: inset 3px 0 0 #f7c000; }
|
|
.anl .anl-wr-name small { display: block; color: var(--ton-schrift-3, #6f7a81); font-size: .74rem; }
|
|
.anl .anl-wr-wert { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
|
|
.anl .anl-wr-last { display: block; width: 4rem; height: 3px; margin: .25rem 0 0 auto; border-radius: 2px; background: #2b3139; overflow: hidden; }
|
|
.anl .anl-wr-last span { display: block; height: 100%; background: #f7c000; transition: width 1s; }
|
|
.anl .anl-eingaenge button { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr) 4.2rem; gap: .6rem; align-items: center; font-size: .86rem; }
|
|
.anl .anl-eingaenge b { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
.anl .anl-balken { height: 7px; border-radius: 4px; background: #2b3139; overflow: hidden; }
|
|
.anl .anl-balken span { display: block; height: 100%; border-radius: 4px; background: #f7c000; transition: width .8s cubic-bezier(.2, .7, .2, 1); }
|
|
.anl .anl-balken.anl-mittel span { background: #c98a22; }
|
|
.anl .anl-balken.anl-schwach span { background: #e5624f; }
|
|
.anl .anl-balken.anl-offline span,
|
|
.anl .anl-balken.anl-dunkel span { background: #5c646d; }
|
|
.anl .anl-hinweise button { display: flex; align-items: center; gap: .6rem; font-size: .88rem; }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.anl *, .anl-plan * { animation: none !important; transition: none !important; }
|
|
}
|
|
|
|
/* ==========================================================================
|
|
Speicher-Ansicht (js/solar/speicher.js). Töne wie Übersicht und Anlage.
|
|
========================================================================== */
|
|
.spk { display: grid; gap: .9rem; }
|
|
.spk .spk-meldung { border-radius: .7rem; padding: .55rem .9rem; font-size: .88rem; background: rgba(232, 163, 58, .12); color: #e8a33a; }
|
|
.spk .spk-meldung.spk-fehler { background: rgba(229, 98, 79, .14); color: #e5624f; }
|
|
.spk .spk-fach { background: var(--ton-grund, #191d20); border-radius: .9rem; padding: .9rem 1rem; display: grid; gap: .7rem; align-content: start; min-width: 0; }
|
|
.spk .spk-kopf { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: .4rem .8rem; }
|
|
.spk .spk-ueberschrift { font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--ton-schrift-2, #9aa6ad); margin: 0; }
|
|
.spk .spk-stand { font-size: .78rem; color: var(--ton-schrift-3, #6f7a81); }
|
|
.spk .spk-leer { color: var(--ton-schrift-3, #6f7a81); font-size: .85rem; margin: .5rem 0; }
|
|
|
|
/* Kennzahlen */
|
|
.spk .spk-kennzahlen { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .6rem; }
|
|
@media (max-width: 991.98px) { .spk .spk-kennzahlen { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
.spk .spk-notiz { font-size: .74rem; color: var(--ton-schrift-3, #6f7a81); margin-top: -.4rem; }
|
|
.spk .spk-einheit { font-weight: 600; }
|
|
.spk .spk-kachel { position: relative; background: var(--ton-grund, #191d20); border-radius: .8rem; padding: .65rem .85rem .7rem; display: grid; gap: .1rem; overflow: hidden; }
|
|
.spk .spk-kachel-titel { font-size: .74rem; color: var(--ton-schrift-2, #9aa6ad); display: flex; align-items: center; gap: .4rem; }
|
|
.spk .spk-kachel-titel i { font-family: bootstrap-icons; font-style: normal; font-size: .9rem; color: #6cbe58; }
|
|
.spk .spk-kachel b { font-size: 1.45rem; font-weight: 600; color: var(--ton-schrift, #e8ede9); font-variant-numeric: tabular-nums; line-height: 1.25; }
|
|
.spk .spk-kachel small { font-size: .74rem; color: var(--ton-schrift-3, #6f7a81); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.spk .spk-kachel.spk-gut b { color: #8fd37a; }
|
|
.spk .spk-kachel.spk-warm b { color: #e8a33a; }
|
|
.spk .spk-kachel.spk-warm .spk-kachel-titel i { color: #e8a33a; }
|
|
.spk .spk-kachel.spk-krit b,
|
|
.spk .spk-kachel.spk-krit .spk-kachel-titel i { color: #e5624f; }
|
|
.spk .spk-kachel-balken { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: #2b3139; }
|
|
.spk .spk-kachel-balken span { display: block; height: 100%; background: linear-gradient(90deg, #4f8fb8, #6cbe58); transition: width 1.2s cubic-bezier(.2, .7, .2, 1); }
|
|
|
|
/* Turm und Detail */
|
|
.spk .spk-mitte { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: .9rem; align-items: start; }
|
|
@media (max-width: 991.98px) { .spk .spk-mitte { grid-template-columns: minmax(0, 1fr); } }
|
|
.spk .spk-turm { position: relative; display: grid; gap: 4px; }
|
|
.spk .spk-bmu {
|
|
display: flex; align-items: center; gap: .6rem; padding: .5rem .8rem; font-size: .82rem;
|
|
background: linear-gradient(#2c3238, #24292f); border: 1px solid #333a42; border-radius: .7rem .7rem .25rem .25rem;
|
|
}
|
|
.spk .spk-bmu b { font-weight: 600; }
|
|
.spk .spk-bmu span:last-child { color: var(--ton-schrift-3, #6f7a81); font-size: .74rem; margin-left: auto; }
|
|
.spk .spk-led { width: .55rem; height: .55rem; border-radius: 50%; background: #444b53; }
|
|
.spk .spk-led.an { background: #6cbe58; box-shadow: 0 0 .5rem #6cbe58; animation: spk-led 2.4s ease-in-out infinite; }
|
|
@keyframes spk-led { 50% { opacity: .55; } }
|
|
.spk .spk-modul {
|
|
all: unset; box-sizing: border-box; cursor: pointer; position: relative; overflow: hidden;
|
|
display: grid; grid-template-columns: 2.2rem minmax(0, 1fr) 7.4rem; gap: .7rem; align-items: center;
|
|
padding: .45rem .7rem; border-radius: .25rem; border: 1px solid #2f363d;
|
|
background: linear-gradient(90deg, #1f2429, #262c32 50%, #1f2429);
|
|
transition: border-color .2s, box-shadow .2s;
|
|
animation: spk-modul-auf .5s cubic-bezier(.2, .9, .3, 1.1) backwards; animation-delay: calc(var(--i) * 45ms);
|
|
}
|
|
@keyframes spk-modul-auf { from { opacity: 0; transform: translateY(8px); } }
|
|
.spk .spk-modul:hover { border-color: #46505a; }
|
|
.spk .spk-modul:focus-visible { outline: 2px solid #8fb8de; outline-offset: 1px; }
|
|
.spk .spk-modul.gewaehlt { border-color: #6cbe58; box-shadow: inset 3px 0 0 #6cbe58; }
|
|
.spk .spk-nr { font-weight: 600; color: var(--ton-schrift-2, #9aa6ad); font-size: .9rem; }
|
|
.spk .spk-zellen { display: grid; grid-template-columns: repeat(16, minmax(0, 1fr)); gap: 2px; height: 2rem; align-items: end; }
|
|
.spk .spk-zellen i { display: block; height: var(--h); background: var(--f); border-radius: 2px 2px 1px 1px; transition: height 1s cubic-bezier(.2, .7, .2, 1), background 1s; }
|
|
.spk .spk-werte { text-align: right; line-height: 1.25; font-variant-numeric: tabular-nums; }
|
|
.spk .spk-werte b { display: block; font-size: .8rem; font-weight: 600; }
|
|
.spk .spk-werte small { font-size: .74rem; color: var(--ton-schrift-3, #6f7a81); }
|
|
.spk .spk-sockel { height: .6rem; margin-top: -2px; border-radius: 0 0 .6rem .6rem; background: #1a1e22; border: 1px solid #2f363d; border-top: 0; }
|
|
/* Laden: Licht wandert nach oben durch die Module, Entladen nach unten. */
|
|
.spk .spk-turm.spk-laden .spk-modul::after,
|
|
.spk .spk-turm.spk-entladen .spk-modul::after {
|
|
content: ""; position: absolute; inset: 0; pointer-events: none;
|
|
background: linear-gradient(90deg, transparent, rgba(108, 190, 88, .16), transparent);
|
|
transform: translateX(-100%); animation: spk-strom 2.6s ease-in-out infinite;
|
|
}
|
|
.spk .spk-turm.spk-laden .spk-modul::after { animation-delay: calc((8 - var(--i)) * 180ms); }
|
|
.spk .spk-turm.spk-entladen .spk-modul::after { background: linear-gradient(90deg, transparent, rgba(232, 163, 58, .14), transparent); animation-delay: calc(var(--i) * 180ms); }
|
|
@keyframes spk-strom { 0% { transform: translateX(-100%); } 60%, 100% { transform: translateX(100%); } }
|
|
.spk.spk-pause *,
|
|
.spk-pause .spk * { animation-play-state: paused !important; }
|
|
.spk .spk-legende { display: flex; flex-wrap: wrap; gap: .3rem 1rem; font-size: .76rem; color: var(--ton-schrift-2, #9aa6ad); }
|
|
.spk .spk-legende i { display: inline-block; width: .7rem; height: .7rem; border-radius: 2px; margin-right: .35rem; vertical-align: -.08rem; }
|
|
|
|
.spk .spk-pille { font-size: .72rem; font-weight: 600; border-radius: 99px; padding: .1rem .55rem; }
|
|
.spk .spk-pille.ok { color: #6cbe58; background: rgba(108, 190, 88, .13); }
|
|
.spk .spk-pille.warn { color: #e8a33a; background: rgba(232, 163, 58, .14); }
|
|
.spk .spk-gross { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem; }
|
|
.spk .spk-gross b { font-size: 2rem; font-weight: 600; line-height: 1.1; color: var(--ton-schrift, #e8ede9); font-variant-numeric: tabular-nums; }
|
|
.spk .spk-gross span { color: var(--ton-schrift-2, #9aa6ad); font-size: .88rem; }
|
|
.spk .spk-zellchart { width: 100%; height: auto; display: block; }
|
|
.spk .spk-zelle { transform-box: fill-box; transform-origin: 50% 100%; animation: spk-zelle .6s cubic-bezier(.2, .9, .3, 1.1) backwards; animation-delay: calc(var(--j) * 25ms); }
|
|
.spk .spk-zelle.markiert { stroke: #fff; stroke-width: 1.5; }
|
|
@keyframes spk-zelle { from { transform: scaleY(0); } }
|
|
.spk .spk-gitter { stroke: #2a3037; stroke-width: 1; }
|
|
.spk .spk-mittel { stroke: #8d96a0; stroke-dasharray: 3 3; }
|
|
.spk .spk-achse { fill: var(--ton-schrift-3, #6f7a81); font-size: 10px; font-family: "Poppins", system-ui, sans-serif; }
|
|
.spk .spk-fuehler { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 3px; }
|
|
.spk .spk-fuehler span { text-align: center; font-size: .74rem; font-weight: 600; padding: .2rem 0; border-radius: .3rem; color: #15181c; background: var(--f); font-variant-numeric: tabular-nums; }
|
|
.spk .spk-paare { display: grid; grid-template-columns: auto 1fr; gap: .25rem .9rem; font-size: .86rem; margin: 0; }
|
|
.spk .spk-paare dt { color: var(--ton-schrift-2, #9aa6ad); font-weight: 400; }
|
|
.spk .spk-paare dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
|
|
|
|
/* Verlauf */
|
|
.spk .spk-diagramme { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: .9rem; }
|
|
@media (max-width: 991.98px) { .spk .spk-diagramme { grid-template-columns: minmax(0, 1fr); } }
|
|
.spk .spk-zeitraum { display: inline-flex; gap: 2px; padding: 2px; border-radius: .5rem; background: #23282c; }
|
|
.spk .spk-zeitraum button { all: unset; cursor: pointer; font-size: .76rem; font-weight: 600; padding: .15rem .6rem; border-radius: .4rem; color: var(--ton-schrift-2, #9aa6ad); }
|
|
.spk .spk-zeitraum button.aktiv { background: #2f363d; color: var(--ton-schrift, #e8ede9); }
|
|
.spk .spk-zeitraum button:focus-visible { outline: 2px solid #8fb8de; }
|
|
.spk .spk-diagramm { position: relative; min-height: 190px; }
|
|
.spk .spk-verlauf { display: block; width: 100%; height: auto; touch-action: pan-y; }
|
|
.spk .spk-flaeche { opacity: .22; }
|
|
.spk .spk-linie { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
.spk .spk-verlauf.neu .spk-linie { stroke-dasharray: 1 1; stroke-dashoffset: 1; animation: spk-zeichnen 1.2s cubic-bezier(.5, 0, .2, 1) forwards; animation-delay: calc(var(--i) * 150ms); }
|
|
.spk .spk-verlauf.neu .spk-flaeche { animation: spk-einblenden 1.2s backwards; }
|
|
@keyframes spk-zeichnen { to { stroke-dashoffset: 0; } }
|
|
@keyframes spk-einblenden { from { opacity: 0; } }
|
|
.spk .spk-zeiger { stroke: rgba(255, 255, 255, .35); stroke-width: 1; pointer-events: none; }
|
|
.spk .spk-tipp {
|
|
position: absolute; top: .4rem; z-index: 2; pointer-events: none; min-width: 9rem;
|
|
background: rgba(21, 24, 28, .94); border: 1px solid #333a42; border-radius: .55rem; padding: .4rem .6rem;
|
|
font-size: .76rem; display: grid; gap: .1rem; box-shadow: 0 .4rem 1rem rgba(0, 0, 0, .35);
|
|
}
|
|
.spk .spk-tipp b { font-weight: 600; margin-bottom: .1rem; }
|
|
.spk .spk-tipp span { display: flex; align-items: center; gap: .4rem; color: var(--ton-schrift-2, #9aa6ad); }
|
|
.spk .spk-tipp em { font-style: normal; margin-left: auto; padding-left: .8rem; color: var(--ton-schrift, #e8ede9); font-variant-numeric: tabular-nums; }
|
|
.spk .spk-tipp i,
|
|
.spk .spk-reihen i { display: inline-block; width: .6rem; height: .6rem; border-radius: 2px; }
|
|
.spk .spk-reihen { display: flex; flex-wrap: wrap; gap: .2rem 1rem; font-size: .76rem; color: var(--ton-schrift-2, #9aa6ad); margin-top: .2rem; }
|
|
.spk .spk-reihen span { display: inline-flex; align-items: center; gap: .35rem; }
|
|
@media (max-width: 575.98px) {
|
|
.spk .spk-kennzahlen { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .45rem; }
|
|
.spk .spk-kachel b { font-size: 1.2rem; }
|
|
.spk .spk-modul { grid-template-columns: 1.8rem minmax(0, 1fr) 5.6rem; gap: .45rem; padding: .4rem .5rem; }
|
|
.spk .spk-werte b { font-size: .72rem; }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.spk *, .spk *::after { animation: none !important; transition: none !important; }
|
|
.spk .spk-verlauf.neu .spk-linie { stroke-dashoffset: 0; }
|
|
}
|
|
|
|
/* ==========================================================================
|
|
Vollbildanzeige (restricted/anzeige.php, js/solar/anzeige.js), z.B. am
|
|
Raspberry Pi mit 800 x 480: schmale Leiste links, Ansicht auf voller Höhe.
|
|
========================================================================== */
|
|
body.anzeige {
|
|
margin: 0; height: 100vh; overflow: hidden;
|
|
display: grid; grid-template-columns: 3.2rem minmax(0, 1fr);
|
|
background: var(--ton-grund, #191d20); color: var(--ton-schrift, #e8ede9);
|
|
font-family: "Poppins", system-ui, sans-serif; font-size: 14px;
|
|
-webkit-tap-highlight-color: transparent; user-select: none;
|
|
}
|
|
body.anzeige-ohne-maus,
|
|
body.anzeige-ohne-maus * { cursor: none !important; }
|
|
/* Die Seite lädt AdminLTE nicht - dessen Grundregeln fehlen hier. */
|
|
body.anzeige [hidden] { display: none !important; }
|
|
body.anzeige *, body.anzeige *::before, body.anzeige *::after { box-sizing: border-box; }
|
|
.anzeige .anzeige-leiste {
|
|
display: flex; flex-direction: column; align-items: center; gap: .4rem; padding: .5rem 0;
|
|
background: var(--ton-flaeche, #23282c); border-right: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
|
}
|
|
.anzeige .anzeige-leiste button {
|
|
all: unset; box-sizing: border-box; width: 2.6rem; height: 2.6rem; border-radius: .7rem;
|
|
display: grid; place-items: center; font-size: 1.3rem; color: var(--ton-schrift-2, #9aa6ad);
|
|
}
|
|
.anzeige .anzeige-leiste button[aria-pressed="true"] { background: var(--ton-flaeche-2, #2b3237); color: #f7c000; }
|
|
.anzeige .anzeige-fuss { margin-top: auto; display: grid; justify-items: center; gap: .3rem; }
|
|
.anzeige .anzeige-punkt { width: .6rem; height: .6rem; border-radius: 50%; background: #e5624f; }
|
|
.anzeige .anzeige-punkt.warn { background: #e8a33a; }
|
|
.anzeige .anzeige-punkt.gut { background: #6cbe58; }
|
|
.anzeige .anzeige-uhr { font-size: .72rem; font-weight: 600; color: var(--ton-schrift-2, #9aa6ad); font-variant-numeric: tabular-nums; }
|
|
.anzeige .anzeige-inhalt { min-width: 0; height: 100vh; }
|
|
.anzeige .anzeige-flaeche { box-sizing: border-box; height: 100vh; padding: .35rem; overflow-y: auto; scrollbar-width: none; }
|
|
.anzeige .anzeige-flaeche::-webkit-scrollbar { display: none; }
|
|
.anzeige .anzeige-flaeche[hidden] { display: none; }
|
|
.anzeige #anzeige-fluss { overflow: hidden; padding: .2rem; }
|
|
.anzeige #anzeige-fluss svg.enf { width: 100%; height: 100%; max-height: none; }
|
|
/* Anlage und Speicher: die Umbrüche der Solar-Seite richten sich nach der
|
|
Fensterbreite und würden am 800er-Display zu früh greifen. */
|
|
.anzeige .anl svg.anl-plan { max-height: calc(100vh - 7.5rem); }
|
|
.anzeige .spk .spk-kennzahlen { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .35rem; }
|
|
.anzeige .spk .spk-kachel { padding: .4rem .55rem .45rem; }
|
|
.anzeige .spk .spk-kachel b { font-size: 1.05rem; }
|
|
.anzeige .spk .spk-kachel small,
|
|
.anzeige .spk .spk-kachel-titel { font-size: .66rem; }
|
|
.anzeige .spk .spk-mitte,
|
|
.anzeige .spk .spk-diagramme { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: .5rem; }
|
|
.anzeige .spk .spk-fach { padding: .6rem .7rem; gap: .5rem; }
|
|
.anzeige .spk .spk-modul { grid-template-columns: 1.7rem minmax(0, 1fr) 5.4rem; gap: .4rem; padding: .3rem .45rem; }
|
|
.anzeige .spk .spk-zellen { height: 1.4rem; }
|
|
.anzeige .spk .spk-werte b { font-size: .68rem; }
|
|
.anzeige .spk .spk-werte small { font-size: .62rem; }
|
|
.anzeige .spk .spk-gross b { font-size: 1.4rem; }
|
|
|
|
/* Ruhig (Vorgabe am Pi): alles Zierwerk aus, nur die Flüsse laufen - in
|
|
groben Stufen, damit ein Raspberry Pi 3 nicht dauernd neu zeichnet. */
|
|
.anzeige-ruhig *,
|
|
.anzeige-ruhig *::after { animation: none !important; }
|
|
.anzeige-ruhig .enf .enf-bett,
|
|
.anzeige-ruhig .spk .spk-verlauf.neu .spk-linie { stroke-dashoffset: 0; }
|
|
.anzeige-ruhig .enf .enf-strom-hof { display: none; }
|
|
.anzeige-ruhig .enf .enf-strom { animation: enf-fliessen var(--dauer, 1.5s) steps(8) infinite !important; }
|
|
.anzeige-ruhig .enf .enf-rueck .enf-strom { animation-direction: reverse !important; }
|
|
.anzeige-ruhig .enf .enf-still .enf-strom,
|
|
.anzeige-ruhig .enf.enf-pause .enf-strom { animation: none !important; }
|
|
.anzeige-ruhig .enf .enf-strahlen,
|
|
.anzeige-ruhig .enf .enf-koerper { transition: none; }
|
|
|
|
/* --- Karte: Umschalter Übersicht / Anlage -------------------------------- */
|
|
.enf-umschalter { display: inline-flex; gap: 2px; padding: 3px; border-radius: var(--ton-radius-klein, .5rem); background: var(--ton-grund, #191d20); }
|
|
.enf-umschalter .btn.btn-sm { border: 0; color: var(--ton-schrift-2, #9aa6ad); font-weight: 600; padding: .2rem .8rem; }
|
|
.enf-umschalter .btn.btn-sm.active { background: var(--ton-flaeche-2, #2b3237); color: var(--ton-schrift, #e8ede9); }
|
|
.enf-ansicht[hidden] { display: none !important; }
|
|
.enf-ansicht.enf-rein { animation: enf-ansicht-rein .35s cubic-bezier(.2, .7, .2, 1); }
|
|
@keyframes enf-ansicht-rein { from { opacity: 0; transform: scale(.98); } }
|