Raum-Modal im neuen Stil: Reiter-Kacheln, Beschattung mit my, Heizring, Licht

- Beschattung: Auf/Stop/my/Zu, Hoehe und Lamellen in 25-%-Stufen, Ziehen im
  Fensterbild, neue Behang-Grafik ueber die ganze Kartenhoehe, Alle im Raum
- my-Position: aktuellen Stand per setMemorized1Position/-Orientation in den
  Motor schreiben (room.php?action=my), gespeicherte Werte werden angezeigt
- Heizung: Ring mit Ist-Temperatur und ziehbarem Soll-Punkt, -/+ und Schnellwahl
- Licht: Birne als Schalter, Helligkeit, Farbfelder, Effekt/Preset eingeklappt
- Logik aus homeMQTT.js nach js/solar/raumfenster.js; bindModalSlider entfaellt

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 22:21:19 +02:00
co-authored by Claude Opus 5
parent fed0d35e03
commit d5d2d8f336
8 changed files with 1201 additions and 1016 deletions
+213 -238
View File
@@ -922,262 +922,237 @@ input[type='range']::-webkit-slider-runnable-track {
.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.
Raum-Modal (Klick auf einen Raum im Grundriss)
Markup: ajax/room.php + restricted/roomControls.php,
Verhalten: js/solar/raumfenster.js. Gleiche Bauart wie Lade-, Heiz- und
Bewaesserungsfenster (.lade): Akzentfarbe je Bereich, Stufen statt
Schieber, jede Wahl gilt sofort.
Alle Regeln tragen .rf davor: solar.css laedt vor AdminLTE, und die
Knoepfe hier setzen die Bootstrap-Gestaltung mit all: unset zurueck.
-------------------------------------------------------------------------- */
#modalEV .modal-dialog:has(.rf) { max-width: 42rem; }
#modalEV .modal-content:has(.rf) .modal-footer { display: none; }
#modalEV .modal-title .rf-etage { font-size: .7rem; font-weight: 600; padding: .05rem .5rem; border-radius: 99px; background: rgba(255, 255, 255, .07); color: var(--ton-schrift-2, #9aa6ad); vertical-align: middle; font-family: var(--bs-body-font-family); }
/* 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. */
.rf { display: grid; gap: .75rem; padding: .9rem 1rem .8rem; --a: #60afbf; }
.rf h3 { margin: 0; font-size: .68rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-leer { margin: 0; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-meldung { font-size: .76rem; color: var(--ton-schrift-3, #6f7a81); }
.rf .rf-meldung.gut { color: #6cbe58; }
.rf .rf-meldung.fehler { color: #e5624f; }
.rf .bi { line-height: 1; }
/* 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); }
/* Waehrend ein Kommando unterwegs ist - Lampen, Schalter, Heizung. Jalousien
zeigen stattdessen "faehrt…" (.rf-bewegt). */
.rf .raum-geraet, .rf .rf-heiz { position: relative; }
.rf .raum-warte { opacity: .6; pointer-events: none; }
.rf .raum-warte::after {
content: ""; position: absolute; top: .55rem; right: .55rem; width: .85rem; height: .85rem;
border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
animation: raum-dreht .8s linear infinite;
}
@keyframes raum-dreht { to { transform: rotate(360deg); } }
/* --------------------------------------------------------------------------
Regler im Raum-Modal
/* Reiter als Kacheln mit Zustand */
.rf .rf-reiter { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: .4rem; overflow-x: auto; scrollbar-width: none; }
.rf .rf-reiter::-webkit-scrollbar { display: none; }
.rf .rf-reiter button {
all: unset; box-sizing: border-box; cursor: pointer; min-width: 6.5rem;
display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: .5rem; align-items: center;
padding: .45rem .6rem; border-radius: .75rem; background: var(--ton-flaeche-2, #2b3237); border: 1.5px solid transparent;
transition: background .2s, border-color .2s;
}
.rf .rf-reiter button:hover { background: #313940; }
.rf .rf-reiter button:focus-visible { outline: 2px solid var(--c); outline-offset: 1px; }
.rf .rf-reiter .rf-sym { grid-row: span 2; width: 1.9rem; height: 1.9rem; border-radius: .55rem; display: grid; place-items: center; background: rgba(255, 255, 255, .06); color: var(--c); font-size: 1rem; }
.rf .rf-reiter b { font-weight: 500; font-size: .84rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rf .rf-reiter .rf-st { font-size: .72rem; color: var(--ton-schrift-2, #9aa6ad); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.rf .rf-reiter .rf-st:empty::before { content: "\00a0"; }
.rf .rf-reiter button[aria-selected="true"] { border-color: var(--c); background: color-mix(in srgb, var(--c) 11%, var(--ton-flaeche-2, #2b3237)); }
.rf .rf-reiter button[aria-selected="true"] .rf-sym { background: color-mix(in srgb, var(--c) 24%, transparent); }
.rf .rf-flaeche { display: grid; gap: .6rem; animation: rf-auf .25s cubic-bezier(.2, .7, .2, 1); }
.rf .rf-flaeche[hidden] { display: none; }
@keyframes rf-auf { from { opacity: 0; transform: translateY(4px); } }
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.
/* Bausteine */
.rf .rf-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.rf .rf-chip { all: unset; box-sizing: border-box; cursor: pointer; padding: .32rem .7rem; border-radius: 99px; background: #363d43; font-size: .8rem; font-weight: 500; display: inline-flex; align-items: center; gap: .35rem; font-variant-numeric: tabular-nums; }
.rf .rf-chip:hover { background: #3f474e; }
.rf .rf-chip span { opacity: .7; }
.rf .rf-chip[aria-pressed="true"] { background: var(--a); color: #0c1a1d; font-weight: 600; }
.rf .rf-chip:focus-visible { outline: 2px solid var(--a); outline-offset: 1px; }
.rf .rf-pille { font-size: .7rem; font-weight: 600; border-radius: 99px; padding: .05rem .5rem; background: rgba(255, 255, 255, .06); color: var(--ton-schrift-2, #9aa6ad); white-space: nowrap; display: inline-flex; align-items: center; gap: .3rem; }
.rf .rf-pille:empty { display: none; }
.rf .rf-pille.faehrt { color: #60afbf; background: rgba(96, 175, 191, .15); }
.rf .rf-pille.faehrt::before { content: ""; width: .4rem; height: .4rem; border-radius: 50%; background: currentColor; animation: rf-puls 1.2s infinite; }
@keyframes rf-puls { 50% { opacity: .3; } }
.rf .rf-link { all: unset; cursor: pointer; color: #60afbf; font-weight: 500; margin-left: auto; }
.rf .rf-link:hover { text-decoration: underline; }
.rf .rf-link:focus-visible { outline: 2px solid #60afbf; outline-offset: 2px; border-radius: 3px; }
.rf .rf-kopf-zeile { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.rf .rf-kopf-zeile > b { font-weight: 500; font-size: .92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rf .rf-kopf-zeile .rf-pille { margin-left: auto; }
.rf .rf-mehr { flex: none; }
.rf .rf-kopf-zeile b + .rf-mehr, .rf .rf-kopf-zeile .rf-pille:empty + .rf-mehr, .rf .rf-lampe-name + .rf-mehr { margin-left: auto; }
.rf .rf-mehr-knopf { all: unset; cursor: pointer; width: 1.75rem; height: 1.75rem; display: grid; place-items: center; border-radius: .5rem; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-mehr-knopf:hover { background: #363d43; }
.rf .rf-karte { display: grid; gap: .5rem; padding: .7rem .75rem; border-radius: .75rem; background: var(--ton-flaeche-2, #2b3237); }
.rf .rf-karte .btn { --bs-btn-padding-y: .2rem; }
.rf details.rf-effekte summary { cursor: pointer; font-size: .8rem; color: var(--ton-schrift-2, #9aa6ad); list-style: none; display: flex; align-items: center; gap: .35rem; }
.rf details.rf-effekte summary::-webkit-details-marker { display: none; }
.rf details.rf-effekte summary::after { content: ""; font-size: 1rem; transition: transform .2s; }
.rf details.rf-effekte[open] summary::after { transform: rotate(90deg); }
.rf details.rf-effekte[open] { display: grid; gap: .5rem; }
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);
}
/* Regler wie im Ladefenster */
.rf input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 1.9rem; background: transparent; margin: 0; }
.rf input[type="range"]::-webkit-slider-runnable-track { height: .5rem; border-radius: .25rem; background: linear-gradient(90deg, var(--a) 0 var(--wert, 0%), #394046 var(--wert, 0%)); }
.rf input[type="range"]::-moz-range-track { height: .5rem; border-radius: .25rem; background: linear-gradient(90deg, var(--a) 0 var(--wert, 0%), #394046 var(--wert, 0%)); }
.rf input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 1.5rem; height: 1.5rem; margin-top: -.5rem; border-radius: 50%; background: #fff; border: 4px solid var(--a); box-shadow: 0 .15rem .5rem rgba(0, 0, 0, .4); }
.rf input[type="range"]::-moz-range-thumb { width: 1rem; height: 1rem; border-radius: 50%; background: #fff; border: 4px solid var(--a); }
.rf input[type="range"]:focus-visible { outline: 2px solid var(--a); outline-offset: 4px; }
/* 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;
}
/* --- Beschattung --- */
.rf .rf-alle { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; padding: .6rem .7rem; border-radius: .75rem; background: var(--ton-flaeche-2, #2b3237); }
.rf .rf-alle h3 { margin-right: auto; }
.rf .rf-alle .bi { font-size: .8rem; }
/* 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;
}
.rf .rf-behang { display: grid; grid-template-columns: 4.75rem minmax(0, 1fr); gap: .85rem; padding: .7rem; border-radius: .75rem; background: var(--ton-flaeche-2, #2b3237); border: 1.5px solid transparent; transition: border-color .3s; }
.rf .rf-behang.rf-bewegt { border-color: rgba(96, 175, 191, .6); }
.rf .rf-behang-mitte { display: grid; gap: .5rem; min-width: 0; align-content: start; }
/* Das Fenster: dunkles Glas mit Tageslicht-Schimmer, helle Lamellen, die
Unterleiste im Akzent. Ueber die ganze Hoehe der Karte. */
.rf .rf-fenster { position: relative; min-height: 6rem; padding: .25rem; border-radius: .75rem; background: #1c2226; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07); touch-action: none; }
.rf .rf-fenster[data-rf-zieh] { cursor: ns-resize; }
.rf .rf-glas { position: relative; height: 100%; border-radius: .5rem; overflow: hidden;
background: radial-gradient(120% 55% at 80% 0%, rgba(126, 214, 230, .5), transparent 60%), linear-gradient(170deg, #1f4c5a 0%, #173640 55%, #122a31 100%); }
.rf .rf-glas::after { content: ""; position: absolute; inset: 0; background: linear-gradient(115deg, transparent 38%, rgba(255, 255, 255, .09) 44%, transparent 52%); pointer-events: none; }
.rf .rf-behang-teil { position: absolute; left: 0; right: 0; top: 0; height: 0; overflow: hidden; transition: height .35s cubic-bezier(.2, .7, .2, 1); }
.rf .rf-lamellen { position: absolute; left: 0; right: 0; bottom: 3px; display: grid; gap: 1.5px; }
.rf .rf-lamellen i { display: block; height: 5px; border-radius: 3px; background: linear-gradient(180deg, #e9f1f3, #b9c9cf); transform: scaleY(var(--kipp, 1)); box-shadow: 0 1px 0 rgba(0, 0, 0, .25); transition: transform .35s; }
.rf .rf-panzer { position: absolute; inset: 0 0 3px; background: repeating-linear-gradient(0deg, #d5e0e4 0 5px, #a9bac0 5px 6.5px); }
.rf .rf-schiene { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; border-radius: 2px; background: #60afbf; box-shadow: 0 0 8px rgba(96, 175, 191, .7); }
/* --------------------------------------------------------------------------
Raum-Modal: Jalousien
-------------------------------------------------------------------------- */
.rf .rf-fahrt { display: grid; grid-template-columns: 1fr auto auto 1fr; gap: .35rem; }
.rf .rf-fahrt button { all: unset; box-sizing: border-box; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: .35rem; height: 2.4rem; padding: 0 .6rem; border-radius: .6rem; background: #363d43; font-weight: 600; font-size: .86rem; }
.rf .rf-fahrt button:hover { background: #3f474e; }
.rf .rf-fahrt button:active { transform: scale(.97); }
.rf .rf-fahrt button:focus-visible { outline: 2px solid #60afbf; outline-offset: 1px; }
.rf .rf-fahrt .rf-stop { width: 2.75rem; }
.rf .rf-fahrt .rf-my .bi { color: #f7c000; font-size: .8rem; }
.rf .rf-fahrt .rf-my.aktiv { background: color-mix(in srgb, #f7c000 16%, #363d43); box-shadow: inset 0 0 0 1.5px #f7c000; }
/* 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;
}
.rf .rf-reihe { display: grid; grid-template-columns: 4.3rem minmax(0, 1fr); align-items: center; gap: .5rem; }
.rf .rf-reihe-titel { font-size: .76rem; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-segment { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 3px; padding: 3px; border-radius: .6rem; background: #22292d; }
.rf .rf-segment button { all: unset; box-sizing: border-box; cursor: pointer; text-align: center; padding: .3rem .1rem; border-radius: .45rem; font-size: .76rem; font-weight: 500; color: var(--ton-schrift-2, #9aa6ad); font-variant-numeric: tabular-nums; white-space: nowrap; }
.rf .rf-segment button:hover { color: var(--ton-schrift, #e8ede9); }
.rf .rf-segment button[aria-checked="true"] { background: #363d43; color: var(--ton-schrift, #e8ede9); box-shadow: inset 0 0 0 1.5px var(--a); }
.rf .rf-segment button:focus-visible { outline: 2px solid var(--a); }
/* 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;
}
.rf .rf-my-zeile { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem; font-size: .76rem; color: var(--ton-schrift-3, #6f7a81); min-height: 1.5rem; }
.rf .rf-my-zeile > .bi { color: #f7c000; font-size: .72rem; }
.rf .rf-my-zeile.frage { padding: .35rem .5rem; border-radius: .55rem; background: rgba(247, 192, 0, .08); color: var(--ton-schrift, #e8ede9); }
.rf .rf-my-knoepfe { display: inline-flex; gap: .3rem; margin-left: auto; }
.rf .rf-my-zeile .rf-chip { padding: .2rem .6rem; font-size: .76rem; }
.rf .rf-my-zeile .rf-chip.ja { background: #f7c000; color: #1a1406; font-weight: 600; }
/* 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;
}
/* --- Heizung --- */
.rf .rf-heiz { display: grid; grid-template-columns: 10.5rem minmax(0, 1fr); gap: 1rem; align-items: center; --a: #e5624f; }
.rf .rf-ring { position: relative; width: 10.5rem; aspect-ratio: 1; max-width: 100%; }
.rf .rf-ring svg { width: 100%; height: 100%; display: block; touch-action: none; }
.rf .rf-ring-spur { fill: none; stroke: #2b3237; stroke-width: 12; stroke-linecap: round; }
.rf .rf-ring-ist { fill: none; stroke: url(#rfHeizVerlauf); stroke-width: 12; stroke-linecap: round; transition: stroke-dasharray .6s cubic-bezier(.2, .7, .2, 1); }
.rf .rf-ring-griff { fill: none; stroke: transparent; stroke-width: 36; cursor: pointer; }
.rf .rf-ring-soll { fill: #fff; stroke: #e5624f; stroke-width: 4; cursor: grab; transform-box: view-box; transform-origin: 85px 85px; transition: transform .35s cubic-bezier(.2, .7, .2, 1); }
.rf .rf-heiz.zieht .rf-ring-soll { cursor: grabbing; transition: none; }
.rf .rf-ring-soll:focus-visible { outline: none; stroke: #fff; fill: #e5624f; }
.rf .rf-ring-mitte { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; pointer-events: none; }
.rf .rf-ring-mitte small { font-size: .7rem; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-ring-mitte b { font-size: 1.9rem; font-weight: 600; line-height: 1.05; font-variant-numeric: tabular-nums; }
.rf .rf-ring-mitte b small { font-size: .95rem; font-weight: 500; color: var(--ton-schrift, #e8ede9); }
.rf .rf-ring-mitte span { font-size: .74rem; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-heiz-rechts { display: grid; gap: .6rem; min-width: 0; }
.rf .rf-stepper { display: grid; grid-template-columns: 3rem minmax(0, 1fr) 3rem; align-items: center; gap: .5rem; background: var(--ton-flaeche-2, #2b3237); border-radius: .85rem; padding: .35rem; }
.rf .rf-stepper button { all: unset; cursor: pointer; height: 3rem; border-radius: .6rem; display: grid; place-items: center; background: #363d43; font-size: 1.4rem; font-weight: 500; }
.rf .rf-stepper button:hover { background: #3f474e; }
.rf .rf-stepper button:active { transform: scale(.95); }
.rf .rf-stepper button:focus-visible { outline: 2px solid #e5624f; }
.rf .rf-stepper output { text-align: center; font-size: 1.55rem; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.15; }
.rf .rf-stepper output small { display: block; font-size: .7rem; font-weight: 400; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-hinweis { padding: .45rem .65rem; border-radius: .6rem; background: rgba(255, 255, 255, .05); color: var(--ton-schrift-2, #9aa6ad); font-size: .8rem; display: flex; gap: .45rem; align-items: flex-start; }
.rf .rf-hinweis[hidden] { display: none; }
.rf .rf-hinweis.an { background: rgba(229, 98, 79, .12); color: #f3b3a9; }
.rf .rf-hinweis .bi { margin-top: .15rem; }
.rf .rf-pillen { display: flex; flex-wrap: wrap; gap: .35rem; }
.rf .rf-pillen .rf-pille { font-size: .74rem; padding: .15rem .6rem; }
.rf .rf-pillen .rf-pille[hidden] { display: none; }
/* 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;
}
/* --- Licht --- */
.rf .rf-lampe { display: grid; gap: .55rem; padding: .7rem; border-radius: .75rem; background: var(--ton-flaeche-2, #2b3237); --a: #f7c000; }
.rf .rf-lampe-kopf { display: flex; align-items: center; gap: .7rem; }
.rf .rf-birne { all: unset; cursor: pointer; flex: none; width: 3rem; height: 3rem; border-radius: .9rem; display: grid; place-items: center; background: #22292d; color: var(--ton-schrift-3, #6f7a81); font-size: 1.35rem; transition: background .3s, box-shadow .3s, color .3s; }
.rf .rf-birne[aria-pressed="true"] { color: #1a1406; background: var(--farbe, #ffd27a); box-shadow: 0 0 0 4px color-mix(in srgb, var(--farbe, #ffd27a) 22%, transparent), 0 .35rem 1.3rem color-mix(in srgb, var(--farbe, #ffd27a) 45%, transparent); }
.rf .rf-birne:focus-visible { outline: 2px solid #f7c000; outline-offset: 3px; }
.rf .rf-lampe-name { display: grid; min-width: 0; }
.rf .rf-lampe-name b { font-weight: 500; font-size: .92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rf .rf-lampe-name span { font-size: .76rem; color: var(--ton-schrift-2, #9aa6ad); font-variant-numeric: tabular-nums; }
.rf .rf-lampe-regler { display: grid; gap: .5rem; transition: opacity .3s; }
.rf .rf-lampe.aus .rf-lampe-regler { opacity: .5; }
.rf .rf-hell::-webkit-slider-runnable-track { background: linear-gradient(90deg, #4a3f22 0, var(--farbe, #ffd27a) var(--wert, 0%), #394046 var(--wert, 0%)) !important; }
.rf .rf-hell::-moz-range-track { background: linear-gradient(90deg, #4a3f22 0, var(--farbe, #ffd27a) var(--wert, 0%), #394046 var(--wert, 0%)) !important; }
.rf .rf-farben { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; }
.rf .rf-farbe { all: unset; box-sizing: border-box; cursor: pointer; width: 1.65rem; height: 1.65rem; border-radius: 50%; background: var(--f); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25); transition: transform .15s; position: relative; }
.rf .rf-farbe:hover { transform: scale(1.1); }
.rf .rf-farbe[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--ton-flaeche-2, #2b3237), 0 0 0 4px var(--f); }
.rf .rf-farbe:focus-visible, .rf .rf-farbe-eigen:focus-within { outline: 2px solid #f7c000; outline-offset: 3px; }
.rf .rf-farbe-eigen { background: conic-gradient(#f55, #fd5, #5f7, #5df, #75f, #f5d, #f55); }
.rf .rf-farbe-eigen input { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
.rf .rf-auswahl { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .5rem; align-items: center; font-size: .78rem; color: var(--ton-schrift-2, #9aa6ad); margin: 0; }
/* Die Endstellungen in Worten statt in Prozent. */
.raum-ende {
font-size: 0.68rem;
opacity: 0.65;
line-height: 1.1;
}
/* --- Schalter --- */
.rf .rf-schalter-liste { display: grid; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); gap: .5rem; }
.rf .rf-schalter { all: unset; box-sizing: border-box; cursor: pointer; display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: .6rem; align-items: center; padding: .6rem; border-radius: .75rem; background: var(--ton-flaeche-2, #2b3237); border: 1.5px solid transparent; position: relative; }
.rf .rf-schalter .rf-sym { grid-row: span 2; width: 2.1rem; height: 2.1rem; border-radius: .6rem; display: grid; place-items: center; background: #22292d; color: var(--ton-schrift-3, #6f7a81); }
.rf .rf-schalter b { font-weight: 500; font-size: .84rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rf .rf-schalter .rf-st { font-size: .72rem; color: var(--ton-schrift-2, #9aa6ad); }
.rf .rf-schalter[aria-pressed="true"] { border-color: #6cbe58; }
.rf .rf-schalter[aria-pressed="true"] .rf-sym { background: rgba(108, 190, 88, .18); color: #6cbe58; }
.rf .rf-schalter:focus-visible { outline: 2px solid #6cbe58; }
/* 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;
/* --- Messwerte und Sonstiges --- */
.rf .raum-werte { display: grid; grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr)); gap: .4rem; }
.rf .raum-wert { background: rgba(255, 255, 255, .04); border-radius: .5rem; padding: .3rem .5rem; overflow: hidden; }
.rf .raum-wert-zahl { font-size: .95rem; font-weight: 600; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rf .raum-wert-einheit { font-size: .7rem; font-weight: 400; opacity: .6; }
.rf .raum-wert-name { font-size: .68rem; opacity: .6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Ein Kommando mit mehreren Parametern traegt Beschriftung, Felder und Knopf
in einer input-group - ohne Mindestbreite schrumpfen die Felder am Handy
auf nichts. */
.rf .input-group.raum-cmd > .form-control,
.rf .input-group.raum-cmd > .form-select { min-width: 5rem; }
/* --- Handy --- */
@media (max-width: 575.98px) {
.rf { padding: .75rem .75rem .7rem; }
.rf .rf-reiter button { grid-template-columns: 1fr; justify-items: center; text-align: center; padding: .4rem .25rem; row-gap: .1rem; min-width: 5.5rem; }
.rf .rf-reiter .rf-sym { grid-row: auto; width: 1.6rem; height: 1.6rem; }
.rf .rf-reiter b { font-size: .76rem; }
.rf .rf-reiter .rf-st { font-size: .68rem; }
.rf .rf-behang { grid-template-columns: 3.6rem minmax(0, 1fr); gap: .6rem; padding: .6rem; }
/* Ueber die ganze Kartenhoehe waere das Fenster hier ein Strich - also ein
festes Seitenverhaeltnis, oben ausgerichtet. */
.rf .rf-fenster { align-self: start; height: auto; aspect-ratio: 9 / 16; min-height: 0; }
.rf .rf-behang .rf-kopf-zeile { flex-wrap: wrap; row-gap: .15rem; }
.rf .rf-behang .rf-kopf-zeile > b { flex: 1 1 calc(100% - 2.5rem); }
.rf .rf-behang .rf-kopf-zeile .rf-pille { order: 3; margin-left: 0; }
.rf .rf-fahrt button { padding: 0 .4rem; }
.rf .rf-reihe { grid-template-columns: 1fr; gap: .2rem; }
.rf .rf-heiz { grid-template-columns: 1fr; justify-items: center; }
.rf .rf-heiz-rechts { width: 100%; }
}
/* --- Einstellungen: Preistabellen ------------------------------------ */