/* ==========================================================================
   Control KNX · plano de la villa «La casa que responde» (22/10/2026)
   Archivo propio (se carga después de estilos.css, solo en la portada): dibujo del plano (SVG),
   capas a demanda, estados de las escenas, lectura de la capa, dial del panel y diálogo.
   Unidades del SVG: 1 ud ≈ 5,5 cm (lienzo 720 × 470). Los muros son líneas simples.
   En reposo se ven la casa, la luz, las persianas, los dispositivos KNX y el bus; cada sistema es
   una capa a demanda (data-capas en .plano, lo pone interacciones.js). Sin JavaScript: reposo.
   ========================================================================== */

.plano {
  --c-arq: 200 218 228;
  --c-knx: 150 210 228;
  --c-ilum: 255 214 170;
  --c-clima: 92 225 240;
  --c-aero: 255 170 110;
  --c-vent: 74 222 128;
  --c-ext: 160 200 210;
  --c-suelo: 250 204 21;
  --c-pis: 125 211 252;
  --c-well: 255 170 110;
  --c-seg: 255 130 150;
  --c-ene: 110 231 160;
  --c-calor: 255 170 110;
  --c-frio: 166 201 74;
  --fl-hvac: var(--c-calor);
  --fondo-plano: #0c0c13;
}
.plano[data-modo="frio"] {
  --fl-hvac: var(--c-frio);
}

/* ---------- Estancias (relleno: escenas y clima lo tiñen) ---------- */
.pl-estancia {
  --pl-c: 42 139 175;
  fill: rgb(var(--pl-c));
  fill-opacity: 0.045;
  stroke: transparent;
  stroke-width: 1.4;
  transition: fill 0.8s ease, fill-opacity 0.8s ease, stroke 0.4s ease;
  transition-delay: calc(var(--i, 0) * 40ms);
}
.pl-estancia[data-z="pasillo"],
.pl-estancia[data-z="vestuario"],
.pl-estancia[data-z="tecnico"] {
  fill-opacity: 0.025;
}

/* ---------- Arquitectura: muros de línea simple, carpintería y exterior ---------- */
.pl-muro {
  stroke: rgb(var(--c-arq) / 0.9);
  stroke-width: 1.4;
  stroke-linecap: square;
}
.pl-tabique {
  stroke: rgb(var(--c-arq) / 0.7);
  stroke-width: 0.8;
  stroke-linecap: square;
}
.pl-hoja {
  stroke: rgb(var(--c-arq) / 0.75);
  stroke-width: 0.8;
}
.pl-arco {
  stroke: rgb(var(--c-arq) / 0.4);
  stroke-width: 0.5;
  stroke-dasharray: 1.6 1.6;
}
.pl-ventana {
  stroke: rgb(170 210 255 / 0.85);
  stroke-width: 0.5;
  stroke-linecap: butt;
}
.pl-terraza {
  stroke: rgb(var(--c-arq) / 0.3);
  stroke-width: 0.6;
  stroke-dasharray: 3 3;
  fill: rgb(42 139 175);
  fill-opacity: 0.025;
  transition: fill-opacity 0.8s ease;
}
.pl-pergola {
  stroke: rgb(var(--c-arq) / 0.22);
  stroke-width: 0.5;
}
.pl-porche,
.pl-caseta,
.pl-bancada {
  stroke: rgb(var(--c-arq) / 0.35);
  stroke-width: 0.6;
  stroke-dasharray: 3 2;
}
.pl-coronacion {
  stroke: rgb(var(--c-arq) / 0.55);
  stroke-width: 0.7;
}
.pl-agua {
  fill: rgb(var(--c-pis));
  fill-opacity: 0.1;
  stroke: rgb(var(--c-pis) / 0.55);
  stroke-width: 0.5;
  transition: fill-opacity 0.8s ease, fill 0.8s ease;
}
/* Canal desbordante con rejilla, junto al borde sur del vaso (Enrique, 1/11/2026) */
.pl-canal {
  fill: rgb(var(--c-pis) / 0.06);
  stroke: rgb(var(--c-pis) / 0.45);
  stroke-width: 0.4;
}
.pl-agua--spa {
  stroke: rgb(var(--c-arq) / 0.6);
  stroke-width: 0.7;
}
.pl-escalon,
.pl-ducha,
.pl-banco,
.pl-estufa {
  stroke: rgb(var(--c-arq) / 0.5);
  stroke-width: 0.5;
}
.pl-estufa {
  stroke: rgb(var(--c-calor) / 0.7);
}
.pl-calor {
  stroke: rgb(var(--c-calor) / 0.85);
  stroke-width: 0.7;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.plano[data-sauna="1"] .pl-calor {
  opacity: 1;
}

/* Persianas: línea gruesa por fuera del vidrio (pathLength 1: --p es lo bajada) y su símbolo cuadrado */
.pl-persiana {
  stroke: #2A8BAF;
  stroke-width: 2.2;
  stroke-linecap: butt;
  stroke-dasharray: var(--p, 0) 1;
  transition: stroke-dasharray 0.7s ease;
}
.pl-persiana__motor {
  fill: #2A8BAF;
  stroke: none;
}
.pl-toldo {
  transform: scaleY(max(0.08, var(--t, 0)));
  transform-box: fill-box;
  transform-origin: 50% 0;
  transition: transform var(--dur-bloque) var(--ea-sale);
}
.pl-toldo rect {
  stroke: rgb(42 139 175 / 0.7);
  stroke-width: 0.6;
  fill: rgb(42 139 175 / 0.12);
}
.pl-toldo path {
  stroke: rgb(42 139 175 / 0.35);
  stroke-width: 0.4;
}

/* ---------- Rótulos: solo el nombre de la estancia ---------- */
.plano__svg .pl-rotulo {
  font-family: Poppins, system-ui, sans-serif;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  fill: rgb(215 215 238 / 0.8);
  stroke: var(--fondo-plano);
  stroke-width: 3px;
  stroke-linejoin: round;
  paint-order: stroke;
  transition: opacity 0.5s ease;
}
.plano__svg .pl-rotulo--peq {
  font-size: 7.5px;
}

/* ---------- Luz (siempre: es el estado de la casa) ---------- */
.pl-luz {
  stroke: rgb(var(--c-ilum) / 0.85);
  stroke-width: 0.5;
  transition: opacity 0.5s ease;
}
.pl-luz__cuerpo {
  fill: rgb(var(--c-ilum) / calc(0.25 + var(--dim, 1) * 0.6));
  transition: fill 0.5s ease;
}
.pl-luz__halo {
  fill: rgb(var(--c-ilum));
  stroke: none;
  opacity: calc(var(--dim, 1) * 0.28);
  transition: opacity 0.5s ease;
}
.pl-luz[data-luz="0"] .pl-luz__cuerpo {
  fill: var(--fondo-plano);
}
.pl-luz[data-luz="0"] .pl-luz__halo {
  opacity: 0;
}
.pl-luz[data-luz="0"] {
  opacity: 0.5;
}
/* Luz o persiana bajo el ratón (se encienden o se mueven al tocarlas: interacciones.js) */
.pl-luz.pl-tocable .pl-luz__cuerpo {
  stroke: #fff;
  stroke-width: 0.8;
}
.pl-luz.pl-tocable .pl-luz__halo {
  opacity: 0.55;
}
.pl-persiana.pl-tocable {
  stroke: #96D2E4;
}
.plano--tocable .plano__lienzo,
.plano--tocable .zona {
  cursor: pointer;
}

/* ---------- Bus KNX: una sola línea fina con el degradado del logo, del armario a cada dispositivo; y el telegrama ---------- */
.pl-bus,
.pl-deriv {
  stroke: url(#knx-bus-grad);
  stroke-width: 0.7;
  stroke-linecap: butt;
  stroke-linejoin: miter;
  transition: opacity 0.4s ease;
}
.pl-telegrama {
  stroke: #fff;
  stroke-width: 1.2;
  opacity: 0;
  pointer-events: none;
}
.plano--vivo .pl-telegrama {
  opacity: 0.85;
}

/* ---------- Equipos y dispositivos: contorno simple + etiqueta corta (color de su capa) ---------- */
.pl-sim {
  --pl-k: var(--c-knx);
  transition: opacity 0.5s ease, visibility 0s;
}
.pl-en-clima { --pl-k: var(--c-clima); }
.pl-en-aerotermia { --pl-k: var(--c-aero); }
.pl-en-ventilacion { --pl-k: var(--c-vent); }
.pl-en-suelo { --pl-k: var(--c-suelo); }
.pl-en-piscina { --pl-k: var(--c-pis); }
.pl-en-wellness { --pl-k: var(--c-well); }
.pl-en-energia { --pl-k: var(--c-ene); }
.pl-en-knx { --pl-k: var(--c-knx); }
.pl-sim .pl-caja,
rect.pl-sim,
path.pl-sim {
  stroke: rgb(var(--pl-k) / 0.85);
  stroke-width: 0.6;
  fill: #0d0d15;
}
path.pl-sim {
  fill: rgb(var(--pl-k) / 0.5);
}
.pl-caja--oculta {
  stroke-dasharray: 2.5 1.5;
  fill: rgb(13 13 21 / 0.6);
}
.pl-sim .pl-lama {
  stroke: rgb(var(--pl-k) / 0.55);
  stroke-width: 0.4;
}
.pl-sim .pl-relleno {
  fill: rgb(var(--pl-k) / 0.8);
  stroke: none;
}
.pl-sim .pl-punto {
  stroke: rgb(var(--pl-k) / 0.9);
  stroke-width: 1.3;
  stroke-linecap: round;
}
.plano__svg .pl-etq {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 4.6px;
  font-weight: 600;
  letter-spacing: 0.02em;
  fill: rgb(var(--pl-k) / 0.95);
}
.plano__svg .pl-etq--peq {
  font-size: 3.4px;
}
.plano__svg .pl-etq--sonda {
  font-size: 3.6px;
}
.plano__svg .pl-etq--fuera {
  font-size: 4px;
}
.plano__svg .pl-etq--dep {
  font-size: 4.4px;
}

/* ---------- Aviso del spa (escena Spa): «HR alta → en rango» ---------- */
.pl-hr {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}
.plano[data-escena="spa"] .pl-hr {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.4s ease, visibility 0s;
}
.pl-hr rect {
  fill: rgb(12 12 19 / 0.88);
  stroke: rgb(var(--c-well) / 0.6);
  stroke-width: 0.5;
  transition: stroke 0.6s ease;
}
.plano__svg .pl-hr text {
  font-family: Poppins, system-ui, sans-serif;
  font-size: 5px;
  font-weight: 600;
}
.pl-hr__alta {
  fill: rgb(var(--c-well));
}
.pl-hr__rango {
  fill: rgb(var(--c-ene));
  opacity: 0.28;
  transition: opacity 0.6s ease;
}
.plano[data-hr="rango"] .pl-hr rect {
  stroke: rgb(var(--c-ene) / 0.6);
}
.plano[data-hr="rango"] .pl-hr__rango {
  opacity: 1;
}

/* ---------- Redes de cada capa: tubos (ida continua, retorno discontinuo) y conductos (doble línea) ---------- */
.pl-red {
  --pl-k: var(--c-clima);
  fill: none;
  stroke: rgb(var(--pl-k) / 0.8);
  stroke-width: 0.7;
}
.pl-red.pl-en-aerotermia { --pl-k: var(--c-aero); }
.pl-red.pl-en-ventilacion { --pl-k: var(--c-vent); }
.pl-red.pl-en-suelo { --pl-k: var(--c-suelo); stroke-width: 0.55; stroke-opacity: 0.7; }
.pl-red.pl-en-piscina { --pl-k: var(--c-pis); }
.pl-red.pl-en-wellness { --pl-k: var(--c-well); }
.pl-red.pl-en-energia { --pl-k: var(--c-ene); stroke-width: 0.45; }
.pl-tubo.pl-r--ret {
  stroke-dasharray: 3 1.6;
  stroke-opacity: 0.6;
}
.pl-en-clima.pl-tubo { --pl-k: var(--fl-hvac); }
/* Conducto: línea ancha del color de la capa y un alma oscura encima (se lee como doble línea) */
.pl-conducto {
  stroke-width: 3;
  stroke-linecap: butt;
  stroke-linejoin: miter;
  stroke-opacity: 0.75;
}
.pl-conducto.pl-r--ext {
  --pl-k: var(--c-ext);
  stroke-dasharray: 4 2.5;
}
.pl-en-wellness.pl-conducto { --pl-k: var(--c-vent); }
.pl-en-wellness.pl-conducto.pl-r--ext { --pl-k: var(--c-ext); }
.pl-alma {
  fill: none;
  stroke: var(--fondo-plano);
  stroke-width: 1.6;
  stroke-linecap: butt;
  stroke-linejoin: miter;
}
/* Flujo: guiones que avanzan en el sentido del fluido (los mueve animaciones.js) */
.pl-flujo {
  --pl-k: var(--fl-hvac);
  fill: none;
  stroke: rgb(var(--pl-k));
  stroke-width: 0.9;
  stroke-dasharray: 2 22;
  stroke-linecap: round;
}
.pl-flujo[data-capa="ventilacion"] { --pl-k: var(--c-vent); stroke-dasharray: 3 9; }
.pl-flujo[data-capa="suelo"] { --pl-k: var(--c-suelo); }
.pl-flujo[data-capa="piscina"] { --pl-k: var(--c-pis); }
.pl-flujo[data-capa="wellness"] { --pl-k: var(--c-well); }
.pl-perimetro {
  stroke: rgb(var(--c-seg) / 0.55);
  stroke-width: 0.6;
  stroke-dasharray: 4 6;
}

/* ---------- Capas a demanda: ocultas en reposo; se encienden con data-capas (sistema, escena o diálogo) ---------- */
.pl-ademanda {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s ease, visibility 0s linear 0.45s;
}
.plano[data-capas~="clima"] .pl-en-clima,
.plano[data-capas~="aerotermia"] .pl-en-aerotermia,
.plano[data-capas~="ventilacion"] .pl-en-ventilacion,
.plano[data-capas~="suelo"] .pl-en-suelo,
.plano[data-capas~="piscina"] .pl-en-piscina,
.plano[data-capas~="wellness"] .pl-en-wellness,
.plano[data-capas~="seguridad"] .pl-en-seguridad,
.plano[data-capas~="energia"] .pl-en-energia {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.45s ease, visibility 0s;
}
/* Con una capa a demanda encendida, el bus y sus derivaciones bajan (siguen llegando a cada dispositivo,
   salvo que «KNX» esté encendida: en el diálogo) y la luz y las persianas también, para que se lea la red */
.plano:not([data-capas=""]):not([data-capas~="knx"]) :is(.pl-bus, .pl-deriv) {
  opacity: 0.45;
}
.plano--foco:not([data-sistema="luz"]):not(.plano--pantalla) .pl-capa--luz,
.plano[data-capas~="suelo"]:not(.plano--pantalla) .pl-capa--luz {
  opacity: 0.22;
}
.plano--foco:not([data-sistema="persianas"]):not(.plano--pantalla) :is(.pl-capa--persianas, .pl-persiana) {
  opacity: 0.45;
}
.pl-capa--luz,
.pl-capa--persianas,
.pl-persiana {
  transition: opacity 0.45s ease, stroke-dasharray 0.7s ease;
}
/* Sistema señalado sin red propia (luz, persianas): el resto se atenúa */
.plano[data-sistema="luz"] :is(.pl-capa--persianas, .pl-persiana, .pl-equipos) {
  opacity: 0.3;
}
.plano[data-sistema="persianas"] :is(.pl-capa--luz, .pl-equipos) {
  opacity: 0.3;
}
.pl-equipos {
  transition: opacity 0.45s ease;
}

/* ---------- Estancia abierta: su recinto se marca y el resto se atenúa ---------- */
.plano--zona .pl-estancia {
  fill-opacity: 0.02;
}
.plano--zona .pl-estancia.pl-zona-on {
  stroke: rgb(150 210 228 / 0.85);
  stroke-width: 0.8;
  fill-opacity: 0.12;
}
.pl-estancia.pl-marca {
  fill-opacity: 0.11;
}

/* ---------- Clima por estancia (consigna de la ficha o del dial) ---------- */
.plano .pl-estancia[data-clima="calor"] {
  --pl-c: var(--c-calor);
  fill-opacity: 0.17;
}
.plano .pl-estancia[data-clima="frio"] {
  --pl-c: var(--c-frio);
  fill-opacity: 0.17;
}

/* ---------- Escenas (el estado de luces y persianas lo pone interacciones.js) ---------- */
.plano[data-escena="llegar"] .pl-estancia:is([data-z^="su"], [data-z="salon"], [data-z="cocina"], [data-z="entrada"]) {
  --pl-c: var(--c-calor);
  fill-opacity: 0.12;
}
.plano[data-escena="noche"] .pl-estancia[data-p="dorm"] {
  --pl-c: var(--c-frio);
  fill-opacity: 0.12;
}
.plano[data-escena="salir"] .pl-estancia {
  fill-opacity: 0.015;
}
.plano[data-escena="salir"] .pl-agua {
  fill-opacity: 0.05;
}
.plano[data-escena="piscina"] .pl-agua:not(.pl-agua--spa),
.plano[data-escena="hotel"] .pl-agua:not(.pl-agua--spa) {
  fill-opacity: 0.32;
}
.plano[data-escena="piscina"] .pl-terraza,
.plano[data-escena="hotel"] .pl-terraza {
  fill-opacity: 0.1;
}
.plano[data-spa="1"] .pl-agua--spa,
.plano[data-escena="spa"] /* Canal desbordante con rejilla, junto al borde sur del vaso (Enrique, 1/11/2026) */
.pl-canal {
  fill: rgb(var(--c-pis) / 0.06);
  stroke: rgb(var(--c-pis) / 0.45);
  stroke-width: 0.4;
}
.pl-agua--spa {
  fill: rgb(var(--c-calor));
  fill-opacity: 0.22;
}
.plano[data-escena="spa"] .pl-estancia:is([data-z="spa"], [data-z="sauna"]) {
  --pl-c: var(--c-calor);
  fill-opacity: 0.12;
}
.plano[data-escena="verano"] .pl-estancia:is([data-z^="su"], [data-z="salon"], [data-z="cocina"], [data-z="entrada"], [data-z="spa"]) {
  --pl-c: var(--c-frio);
  fill-opacity: 0.13;
}
.plano[data-escena="hotel"] .pl-estancia[data-z="salon"] {
  fill-opacity: 0.18;
}

/* ==========================================================================
   Alrededor del plano: mesa, «Abrir plano», lectura de la capa, chips de sistemas (móvil)
   ========================================================================== */
/* Sin HUD: el plano ocupa la mesa entera; «Abrir plano» arriba a la izquierda, en el margen */
.plano__mesa {
  inset: 46px 1.5% 1.5%;
}
.plano__abrir {
  left: 12px;
  transform: none;
}
/* Lectura: nombre de lo que se ve y su leyenda en palabras (solo las filas de las capas encendidas).
   Abajo a la izquierda, sobre la terraza (ninguna red pasa por allí); con la piscina, arriba a la derecha */
.plano__lectura {
  position: absolute;
  z-index: 3;
  left: 12px;
  bottom: 12px;
  max-width: min(30%, 210px);
  padding: 6px 12px 7px;
  border-radius: 14px;
  background: rgb(16 16 26 / 0.82);
  border: 1px solid rgb(255 255 255 / 0.1);
  font-size: 0.68rem;
  line-height: 1.35;
  color: var(--gris);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s ease, visibility 0s linear 0.35s;
}
.plano__lectura.visible {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.35s ease, visibility 0s;
}
@media (min-width: 900px) {
  .plano[data-capas~="piscina"] .plano__lectura {
    left: auto;
    right: 12px;
    bottom: auto;
    top: 52px;
  }
  /* Ventilación: con la toma de aire exterior y la expulsión, la leyenda tiene dos filas más; más ancha
     para que las largas quepan en una línea y no suba hasta el rótulo del salón (Enrique, 1/11/2026) */
  .plano[data-capas~="ventilacion"] .plano__lectura {
    max-width: min(42%, 280px);
  }
  /* Recorrido con el scroll (varias capas a la vez): solo el nombre, en la franja libre de arriba a la
     derecha (a la altura de «Abrir plano»), sin tapar el dibujo (antes tapaba el rótulo SPA) */
  .plano.plano--recorrido .plano__lectura {
    left: auto;
    right: 12px;
    bottom: auto;
    top: 12px;
    max-width: none;
  }
}
.plano__lectura-titulo {
  margin: 0 0 3px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--blanco);
}
.leyenda {
  margin: 0;
  padding: 0;
  list-style: none;
}
.leyenda--plano {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.leyenda__fila {
  display: none;
  align-items: center;
  gap: 6px;
}
.leyenda__fila.visible {
  display: flex;
}
/* Muestras de la leyenda (22 × 14): mismos colores y trazos que el plano */
.muestra {
  flex: none;
  width: 22px;
  height: 14px;
  overflow: visible;
}
.leyenda__fila { --pl-k: var(--c-knx, 150 210 228); }
.leyenda__fila[data-capa="clima"] { --pl-k: 92 225 240; }
.leyenda__fila[data-capa="aerotermia"],
.leyenda__fila[data-capa="wellness"] { --pl-k: 255 170 110; }
.leyenda__fila[data-capa="ventilacion"] { --pl-k: 74 222 128; }
.leyenda__fila[data-capa="suelo"] { --pl-k: 250 204 21; }
.leyenda__fila[data-capa="piscina"] { --pl-k: 125 211 252; }
.leyenda__fila[data-capa="persianas"] { --pl-k: 42 139 175; }
.leyenda__fila[data-capa="luz"] { --pl-k: 255 214 170; }
.leyenda__fila[data-capa="seguridad"] { --pl-k: 255 130 150; }
.leyenda__fila[data-capa="energia"] { --pl-k: 110 231 160; }
.muestra :is(.mu-caja, .mu-tubo, .mu-conducto, .mu-bus, .mu-perimetro, .mu-persiana) {
  fill: none;
  stroke: rgb(var(--pl-k) / 0.9);
  stroke-width: 1.2;
}
.muestra .mu-oculta { stroke-dasharray: 2.5 1.5; }
.muestra .mu-conducto { stroke-width: 4; stroke-opacity: 0.75; }
.muestra .mu-conducto.pl-r--ext { stroke: rgb(160 200 210 / 0.8); stroke-dasharray: 4 2.5; }
.muestra .mu-bus { stroke: #2A8BAF; }
.muestra .mu-perimetro { stroke-dasharray: 4 3; }
.muestra .mu-persiana { stroke-width: 3; }
.muestra .mu-luz { fill: rgb(255 214 170 / 0.85); stroke: none; }

/* Chips de sistemas (móvil): fila encima del plano; en escritorio no se enseñan (está la lista) */
.plano__capas {
  display: none;
}

/* ---------- Dial de temperatura del panel (input range real: ratón, dedo y teclado) ---------- */
.panel-knx__clima {
  grid-template-columns: auto 1fr auto;
}
.panel-knx__nota {
  grid-column: 1 / -1;
  font-size: 0.66rem;
  color: var(--gris-apagado);
}
.dial {
  --t: 0.5;
  grid-column: 1 / -1;
  width: 100%;
  height: 28px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  touch-action: pan-y;
}
.dial::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background:
    linear-gradient(90deg, #A6C94A, var(--azul), #ffaa6e) 0 0 / calc(var(--t) * 100%) 100% no-repeat,
    rgb(255 255 255 / 0.12);
}
.dial::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: rgb(255 255 255 / 0.12);
}
.dial::-moz-range-progress {
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, #A6C94A, var(--azul), #ffaa6e);
}
.dial::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  margin-top: -7px;
  border: 0;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 4px rgb(42 139 175 / 0.35), 0 2px 8px rgb(0 0 0 / 0.5);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.dial::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 4px rgb(42 139 175 / 0.35), 0 2px 8px rgb(0 0 0 / 0.5);
}
.dial:hover::-webkit-slider-thumb,
.dial:active::-webkit-slider-thumb {
  box-shadow: 0 0 0 6px rgb(42 139 175 / 0.45), 0 2px 8px rgb(0 0 0 / 0.5);
}
.dial:focus-visible {
  outline: 2px solid var(--azul);
  outline-offset: 3px;
  border-radius: 8px;
}

/* ==========================================================================
   Diálogo «Abrir plano»: fondo opaco detrás del dibujo, carril de capas y leyenda en palabras
   ========================================================================== */
.plano-dialogo {
  background: rgb(12 12 20 / 0.96);
}
.plano-dialogo__cuerpo {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
}
.plano-dialogo__carril {
  flex: none;
  width: 228px;
  padding: 6px 14px 16px 18px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.plano-dialogo__sub {
  margin: 10px 0 8px;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gris-apagado);
}
.plano-dialogo__capas {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.capa-boton {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding: 4px 10px;
  border: 1px solid rgb(255 255 255 / 0.1);
  border-radius: 10px;
  background: rgb(255 255 255 / 0.03);
  color: var(--gris);
  font: inherit;
  font-size: 0.78rem;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}
.capa-boton:hover {
  border-color: rgb(42 139 175 / 0.5);
}
.capa-boton__marca {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  border: 1.5px solid rgb(255 255 255 / 0.35);
  transition: background-color 0.25s ease, border-color 0.25s ease;
}
.capa-boton[aria-pressed="true"] {
  color: var(--blanco);
  border-color: rgb(42 139 175 / 0.45);
  background: rgb(42 139 175 / 0.12);
}
.capa-boton[aria-pressed="true"] .capa-boton__marca {
  border-color: transparent;
  background: var(--degradado);
}
.leyenda--dialogo {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 0.74rem;
  color: var(--gris);
}
/* Escritorio: el carril es una columna; las capas arriba y la leyenda debajo, con su propio scroll
   (barra visible y un degradado abajo mientras quede leyenda por ver: lo marca interacciones.js) */
@media (min-width: 900px) {
  .plano-dialogo__carril {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding-bottom: 10px;
  }
  .capa-boton {
    min-height: 30px;
    padding: 3px 10px;
  }
  .plano-dialogo__capas {
    gap: 3px;
  }
  .leyenda--dialogo {
    flex: 1 1 auto;
    min-height: 60px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0 6px 6px 0;
    scrollbar-width: thin;
    scrollbar-color: rgb(42 139 175 / 0.55) rgb(255 255 255 / 0.05);
  }
  .leyenda--dialogo::-webkit-scrollbar {
    width: 6px;
  }
  .leyenda--dialogo::-webkit-scrollbar-thumb {
    border-radius: 3px;
    background: rgb(42 139 175 / 0.55);
  }
  .leyenda--dialogo.leyenda--mas {
    -webkit-mask-image: linear-gradient(#000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(#000 calc(100% - 28px), transparent);
  }
}

/* ==========================================================================
   Móvil (menos de 900 px): chips de sistemas encima del plano, rótulos más grandes, diálogo apilado
   ========================================================================== */
@media (max-width: 899px) {
  .plano__mesa {
    inset: auto;
    padding: 4px 0;
  }
  /* Vista del móvil: la casa entera, con la terraza, la piscina y la bomba de calor (VISTA_MOVIL) */
  .js .plano__lienzo {
    --vx: 52;
    --vy: 46;
    --vw: 570;
    --vh: 366;
  }
  .plano__capas {
    order: -1;
    display: block;
    overflow-x: auto;
    padding: 50px 10px 4px;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  .plano__capas-pista {
    position: relative;
    display: flex;
    gap: 6px;
    width: max-content;
  }
  /* Recorrido con el scroll: mientras hay un sistema «actual», la fila no se desliza a mano; la pista se
     mueve con transform (lo compone el navegador, sin tocar ningún scroll) hasta centrarlo. Al soltarla
     (todos juntos, sin recorrido o si el visitante elige algo), lo desplazado pasa a ser el scroll normal
     de la fila (interacciones.js) */
  .plano__capas--guiada {
    overflow-x: hidden;
    scroll-snap-type: none;
  }
  .plano__capas--guiada .plano__capas-pista {
    transition: transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
    will-change: transform;
  }
  .plano__capas::-webkit-scrollbar {
    display: none;
  }
  .chip--capa {
    display: inline-flex;
    align-items: center;
    gap: 6px;
  }
  .chip__icono {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  /* Encendido: el pulsado y, en el recorrido con el scroll, el que se acaba de encender llevan el
     degradado lleno; los ya encendidos, solo un tinte azul; los que faltan, atenuados (1/11: antes todos
     los encendidos iban iguales y no se veía cuál era el actual) */
  .chip--capa {
    transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease, opacity 0.3s ease;
  }
  .chip--capa.en-secuencia {
    color: var(--blanco);
    border-color: rgb(42 139 175 / 0.6);
    background: rgb(42 139 175 / 0.18);
  }
  .chip--capa[aria-pressed="true"],
  .chip--capa.en-secuencia--actual {
    color: var(--negro);
    border-color: transparent;
    background: var(--degradado);
  }
  .plano--recorrido .chip--capa:not(.en-secuencia) {
    opacity: 0.45;
  }
  /* La lista «Sistemas conectados al bus KNX» no se enseña: los sistemas están en la fila de encima del
     plano y en el diálogo (decisión de Enrique, 1/11). En escritorio sigue junto al plano */
  .control__escenario > .control__sistemas {
    display: none;
  }
  /* La lectura queda como una píldora arriba a la derecha (solo el nombre de lo que se ve) */
  .plano__lectura {
    top: 10px;
    bottom: auto;
    left: auto;
    right: 10px;
    max-width: calc(100% - 170px);
    padding: 6px 10px;
    border-radius: 999px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .plano__lectura-titulo {
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .leyenda--plano {
    display: none;
  }
  .plano:not(.plano--pantalla) .pl-rotulo {
    font-size: 13px;
  }
  .plano:not(.plano--pantalla) .pl-rotulo--peq {
    font-size: 12.6px;
  }
  /* En la página el plano del móvil enseña lo esencial: el bus llega a pulsadores y termostatos, pero sin
     sondas ni etiquetas de equipo (se leerían a menos de 7 px). En el diálogo, con zoom, se ve todo */
  .plano:not(.plano--pantalla) :is(.pl-deriv--sonda, .pl-etq, .pl-sim[data-d$="-co2"], .pl-sim[data-d$="-hr"]) {
    display: none;
  }
  /* Diálogo: capas en una fila arriba (se desliza), sin leyenda; el plano debajo */
  .plano-dialogo__cuerpo {
    flex-direction: column;
  }
  .plano-dialogo__carril {
    width: auto;
    padding: 0 10px 6px;
    overflow: visible;
  }
  .plano-dialogo__carril .plano-dialogo__sub,
  .leyenda--dialogo {
    display: none;
  }
  .plano-dialogo__capas {
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .capa-boton {
    flex: none;
    min-height: 44px;
    white-space: nowrap;
  }
}

/* «Reducir movimiento»: los cambios de capa y de estado son instantáneos */
@media (prefers-reduced-motion: reduce) {
  .pl-ademanda,
  .plano[data-capas] .pl-ademanda,
  .pl-estancia,
  .pl-persiana,
  .pl-toldo,
  .pl-luz,
  .pl-luz__cuerpo,
  .pl-luz__halo,
  .pl-capa--luz,
  .pl-capa--persianas,
  .pl-equipos,
  .plano__lectura {
    transition: none;
  }
}

/* Proporción del lienzo: la de la casa entera (viewBox 46 44 580 372); interacciones.js la cambia al acercar */
.plano__lienzo {
  --vx: 46;
  --vy: 44;
  --vw: 580;
  --vh: 372;
}
/* Bomba de calor: como mucho un círculo inscrito muy fino (sin aspas) */
.pl-sim[data-d="bc"] .pl-lama {
  stroke-opacity: 0.3;
}

/* Diálogo: luz, persianas y dispositivos KNX también se apagan desde el carril (quedan tenues) */
.plano--pantalla:not([data-capas~="luz"]) .pl-capa--luz {
  opacity: 0.2;
}
.plano--pantalla:not([data-capas~="persianas"]) :is(.pl-persiana, .pl-persiana__motor, .pl-capa--persianas) {
  opacity: 0.25;
}
.plano--pantalla:not([data-capas~="knx"]) .pl-sim.pl-en-knx {
  opacity: 0.25;
}

/* Escritorio: el marco se ajusta a la casa (sin el hueco del antiguo HUD) */
@media (min-width: 900px) {
  .control__plano {
    aspect-ratio: 720 / 470;
  }
}
/* En el diálogo las capas se eligen en su carril: fuera los chips de sistemas y la lectura */
.plano--pantalla :is(.plano__capas, .plano__lectura) {
  display: none;
}

/* Diálogo: el plano se ve siempre terminado, aunque se abra antes de que acabe la secuencia de entrada
   (que va ligada al scroll): bus dibujado, derivaciones, dispositivos y luces a su tamaño */
.plano--pantalla .pl-bus {
  stroke-dasharray: none !important;
  stroke-dashoffset: 0 !important;
}
.plano--pantalla :is(.pl-deriv, .pl-capa--arq) {
  clip-path: none !important;
}
.plano--pantalla :is(.pl-sim.pl-en-knx, .pl-luz__cuerpo, .pl-luz__halo) {
  transform: none !important;
}

/* ==========================================================================
   Ficha siempre entera (móvil, 22/10): la cabecera (título y X) no se va con el scroll; solo se
   desplaza el cuerpo. En la página del móvil la ficha es una hoja fija a la pantalla (abajo en
   vertical, a la derecha en horizontal): se ve entera aunque se baje la página
   ========================================================================== */
.ficha:not([hidden]) {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ficha__cabeza {
  flex: none;
}
.ficha__cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  margin-right: -8px;
  padding-right: 8px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgb(42 139 175 / 0.55) transparent;
}
@media (max-width: 899px) {
  .plano:not(.plano--pantalla) .ficha {
    position: fixed;
    z-index: 40;
    left: max(8px, env(safe-area-inset-left));
    right: max(8px, env(safe-area-inset-right));
    top: auto;
    bottom: calc(8px + env(safe-area-inset-bottom));
    width: auto;
    max-height: 56vh;
    max-height: 56dvh;
    margin: 0;
    background: rgb(14 14 22 / 0.94);
  }
  .ficha__cabeza {
    margin-bottom: 4px;
  }
}
/* Móvil en horizontal: la hoja va a la derecha, del borde de la cabecera de la web hasta abajo */
@media (max-width: 899px) and (orientation: landscape) {
  .plano:not(.plano--pantalla) .ficha {
    left: auto;
    top: 80px;
    width: min(340px, 48vw);
    max-height: none;
  }
}
/* Página del móvil en horizontal: el plano cabe en el alto de la pantalla (no más ancho de lo que deja
   ver entero) y, con la ficha abierta a la derecha, se arrima a la izquierda para no quedar debajo */
@media (max-width: 899px) and (orientation: landscape) {
  .plano:not(.plano--pantalla) .plano__lienzo {
    width: min(100%, calc((100svh - 140px) * var(--vw) / var(--vh)));
    margin-inline: auto;
  }
  .plano--zona:not(.plano--pantalla) .plano__lienzo {
    margin-left: 8px;
  }
}

/* ==========================================================================
   Diálogo en el móvil (22/10; sin aviso de girar desde el 1/11): en horizontal, capas en un
   carril a la izquierda, el plano a todo el alto y la ficha a la derecha
   ========================================================================== */
@media (max-width: 899px) and (orientation: landscape) {
  .plano-dialogo {
    padding: 0 env(safe-area-inset-right) 0 env(safe-area-inset-left);
  }
  /* Cabecera en una sola fila (título, zoom y X): en horizontal el alto es lo que más falta. La explicación
     no se ve (el carril y el propio plano ya dicen qué hacer), pero los lectores de pantalla la siguen
     leyendo: el diálogo la nombra con aria-describedby */
  .plano-dialogo__barra {
    flex-wrap: nowrap;
    padding: 4px 8px 4px 14px;
  }
  .plano-dialogo__textos {
    flex: 1 1 auto;
  }
  .plano-dialogo__titulo {
    font-size: 0.92rem;
  }
  .plano-dialogo__ayuda {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .plano-dialogo__cuerpo {
    flex-direction: row;
  }
  .plano-dialogo__carril {
    width: 168px;
    padding: 8px 6px calc(10px + env(safe-area-inset-bottom)) 10px;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
  }
  .plano-dialogo__capas {
    flex-direction: column;
    overflow: visible;
    gap: 4px;
  }
  .capa-boton {
    min-height: 40px;
    padding: 4px 10px;
    white-space: normal;
    line-height: 1.15;
  }
  /* Las estancias se tocan en el plano: la fila de chips no roba alto */
  .plano-dialogo .plano__chips {
    display: none;
  }
  .plano-dialogo .plano--zona .plano__mesa {
    place-items: center;
  }
  /* Zoom en la cabecera, junto a la X: así no tapa el plano ni cae en la zona de la muesca */
  .plano-dialogo__zoom {
    position: static;
    flex: none;
  }
  .plano-dialogo:has(.plano--zona) .plano-dialogo__zoom {
    display: flex; /* en la cabecera no estorba a la ficha: se queda (y la cabecera no cambia de alto) */
  }
  .plano-dialogo .ficha {
    left: auto;
    right: 8px;
    top: 8px;
    bottom: calc(8px + env(safe-area-inset-bottom));
    width: min(320px, 50%);
    max-height: none;
  }
}

/* ==========================================================================
   Remate del móvil (23/10): zonas seguras del iPhone, marco del diálogo y ficha sin «trocitos»
   ========================================================================== */
@media (max-width: 899px) {
  /* El diálogo ocupa toda la pantalla: fuera el marco blanco de 3 px que el navegador pone a <dialog> */
  .plano-dialogo {
    border: 0;
  }
  /* Ficha: la raya que separa el título del contenido va con el título (fija). Antes era el borde del
     primer bloque y se iba con el scroll, así que bajo el título asomaba el trozo de un botón o de un
     texto cortado. El contenido se funde arriba y abajo al desplazarse, en vez de cortarse en seco */
  .ficha__cabeza {
    margin-bottom: 0;
    padding-bottom: 8px;
    border-bottom: 1px solid rgb(255 255 255 / 0.08);
  }
  .ficha__cuerpo > .ficha__bloque:first-child {
    border-top: 0;
  }
  .ficha:not([hidden]) {
    padding-bottom: 0;
  }
  .ficha__cuerpo {
    padding-bottom: 14px;
    -webkit-mask-image: linear-gradient(transparent, #000 9px, #000 calc(100% - 16px), transparent);
    mask-image: linear-gradient(transparent, #000 9px, #000 calc(100% - 16px), transparent);
  }
}
/* Diálogo en vertical: lejos de la barra de estado (muesca, isla) y de la barra de inicio del iPhone */
@media (max-width: 899px) and (orientation: portrait) {
  .plano-dialogo {
    padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
  }
  /* La ficha queda encima de la fila de estancias (58 px): se puede cambiar de estancia sin cerrarla */
  .plano-dialogo .ficha {
    bottom: 66px;
  }
}
/* Página del móvil en horizontal: la hoja empieza bajo la cabecera de la web, también con muesca arriba */
@media (max-width: 899px) and (orientation: landscape) {
  .plano:not(.plano--pantalla) .ficha {
    top: calc(80px + env(safe-area-inset-top, 0px));
  }
}

/* ==========================================================================
   Sistemas en el diálogo (móvil, 1/11): en lugar de las capas, una fila de sistemas con su icono (uno
   a la vez), como la de encima del plano. En escritorio no se enseña: allí siguen el carril de capas y
   la lista junto al plano
   ========================================================================== */
.plano-dialogo__sistemas {
  display: none;
}
@media (max-width: 899px) {
  .plano-dialogo__capas {
    display: none;
  }
  .plano-dialogo__sistemas {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding: 2px 0 4px;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    /* Se funde por los lados: se nota que la fila sigue */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 10px, #000 calc(100% - 22px), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 10px, #000 calc(100% - 22px), transparent);
  }
  .plano-dialogo__sistemas::-webkit-scrollbar {
    display: none;
  }
  .plano-dialogo__sistemas .chip--capa {
    transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
  }
  .plano-dialogo__sistemas .chip--capa:first-child {
    margin-left: 4px;
  }
}
/* Horizontal: la fila pasa a ser la columna del carril (a la izquierda del plano), como antes las capas */
@media (max-width: 899px) and (orientation: landscape) {
  .plano-dialogo__sistemas {
    flex-direction: column;
    gap: 4px;
    overflow: visible;
    padding: 0;
    -webkit-mask-image: none;
    mask-image: none;
  }
  .plano-dialogo__sistemas .chip--capa {
    min-height: 40px;
    padding: 4px 10px;
    border-radius: 12px;
    line-height: 1.15;
    text-align: left;
  }
  .plano-dialogo__sistemas .chip--capa:first-child {
    margin-left: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .chip--capa,
  .plano-dialogo__sistemas .chip--capa {
    transition: none;
  }
}

/* Ayuda del diálogo: en móvil se elige un sistema cada vez (1/11) */
.plano-dialogo__ayuda .solo-movil { display: none; }
@media (max-width: 899.98px) {
  .plano-dialogo__ayuda .solo-escritorio { display: none; }
  .plano-dialogo__ayuda .solo-movil { display: inline; }
}
