﻿/* Sistema de diseño. Todo lo visual sale de estas variables: cambiar aquí cambia
   la app entera y evita los valores sueltos repartidos por la hoja. */
:root {
  /* Superficies: escalonadas de fondo a primer plano, para que los paneles
     apilados se distingan sin necesidad de bordes gruesos. */
  --bg: #0d1119; --panel: #151b27; --panel-2: #1c2433; --panel-3: #232c3e;
  --line: #2a3346; --line-soft: #212a3a;
  --text: #eef2f8; --text-2: #c3ccdd; --muted: #8b97ae;
  --accent: #4c8dff; --accent-soft: rgba(76,141,255,.14); --accent-line: rgba(76,141,255,.45);
  /* LA TINTA DEL ACENTO, y el acento del hover. Hacían falta porque el texto de un
     botón de color de marca estaba escrito `#fff` a mano en ocho sitios, y blanco NO
     se lee sobre cualquier marca: sobre un amarillo da 1,4:1. Aquí se mide y se elige
     la tinta que más contrasta —el mínimo legible es 4,5:1— y `--accent-deep` se
     aleja de esa tinta, nunca se acerca, para que el hover SUBA el contraste.
     `empresa.js` recalcula los dos al aplicar el color de la cadena.
     Blanco sobre este azul da 3,20:1 y no llega, así que la tinta es oscura (5,65:1). */
  --accent-ink: #12161f; --accent-deep: #659dff;
  --accent-2: #2fbf8c; --danger: #ff5f6d; --warn: #ffb547;
  /* La tinta del rojo, por lo mismo: blanco sobre este rojo da 2,95:1 y no se lee;
     esta tinta oscura da 6,13:1. El rojo no se deriva de la marca —es un código de
     significado— pero el fallo de escribir `#fff` al lado es el mismo. */
  --danger-ink: #12161f;
  /* Radios y sombras coherentes: un solo lenguaje de formas. */
  --r-sm: 6px; --r: 9px; --r-lg: 14px; --radius: 9px;
  --sh-1: 0 1px 2px rgba(0,0,0,.28);
  --sh-2: 0 4px 14px rgba(0,0,0,.32);
  --sh-3: 0 14px 40px rgba(0,0,0,.45);
  --ease: cubic-bezier(.2,.7,.3,1);
  --canvas-bg: #0b0f18;
  --scroll: #33405e; --scroll-hover: #46567c;
}

/* TEMA CLARO. No es un adorno: un plano se enseña y se imprime en blanco, y hay
   quien trabaja a pleno sol en obra. Se activa con `data-theme="claro"` en <html>
   (lo pone `main.js`), y el LIENZO tiene su propia paleta en `editor/theme.js`
   porque el canvas no entiende de variables CSS. */
:root[data-theme="claro"] {
  --bg: #eef1f7; --panel: #ffffff; --panel-2: #f4f6fb; --panel-3: #e8edf6;
  --line: #d8dfec; --line-soft: #e7ecf5;
  --text: #131a26; --text-2: #3c4658; --muted: #6a7588;
  --accent: #1f6fe5; --accent-soft: rgba(31,111,229,.10); --accent-line: rgba(31,111,229,.42);
  --accent-ink: #ffffff; --accent-deep: #1d63c9; /* blanco 4,70:1 sobre este azul: vale */
  --accent-2: #0d9465; --danger: #d93a45; --warn: #b57500;
  --danger-ink: #ffffff; /* 4,52:1 sobre este rojo, contra 4,00:1 de la tinta oscura */
  --sh-1: 0 1px 2px rgba(20,30,50,.07);
  --sh-2: 0 4px 14px rgba(20,30,50,.10);
  --sh-3: 0 16px 44px rgba(20,30,50,.18);
  --canvas-bg: #f5f7fb;
  --scroll: #c3ccdc; --scroll-hover: #a8b3c8;
}
/* La transición hace que el cambio no dé un latigazo. Solo en superficies. */
body, #topbar, .card, .toolbar, .palette, .right, .tabbar, .modal, .ctxmenu {
  transition: background-color .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font: 14.5px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
  /* Cifras de ancho fijo: las medidas y los precios no bailan al cambiar. */
  font-variant-numeric: tabular-nums;
}
a { color: var(--accent); text-decoration: none; }
.muted { color: var(--muted); }
.spacer { flex: 1; }

/* FOCO VISIBLE en todo lo interactivo. Faltaba: con el teclado no se sabía dónde
   estabas, y es lo primero que se nota en una auditoría de accesibilidad. */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
::selection { background: var(--accent-soft); }

/* `hidden` GANA SIEMPRE. Ésta es la CUARTA vez que el mismo fallo cuesta un rato: el
   `display: none` que el navegador le da a `[hidden]` viene de su hoja de estilos, así
   que CUALQUIER regla nuestra con `display` (y media hoja lo lleva: flex, grid,
   inline-grid…) lo pisa, y `el.hidden = true` deja el elemento en pantalla. Cuando
   apareció se parcheó suelto —`.impersonando[hidden]`, `.enl-tabla[hidden]`—, y así se
   siguió cazando de uno en uno: la barra de arriba seguía puesta en la pantalla de
   entrar, la chapa del aviso de capas ocultas salía siempre vacía, y el aviso con acción
   no se iba ni con el aspa, ni con Esc, ni a los 9,6 s. Con `!important` se acaba la
   familia entera, presente y futura: si algo lleva `hidden`, no se ve. */
[hidden] { display: none !important; }

/* Scrollbars personalizadas */
* { scrollbar-width: thin; scrollbar-color: var(--scroll) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--scroll); border-radius: 8px; border: 2px solid var(--panel); background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: var(--scroll-hover); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* Topbar */
#topbar {
  display: flex; align-items: center; gap: 14px; padding: 9px 18px;
  background: var(--panel); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20; box-shadow: var(--sh-1);
}
.brand { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.brand svg { color: var(--accent); }
.nav-tit { display: inline-flex; align-items: center; gap: 5px; }
#crumbs { display: flex; align-items: center; gap: 8px; color: var(--muted); }
#crumbs .sep { opacity: .5; }
#whoami { font-size: 13px; }

/* Layout */
#app { min-height: calc(100% - 52px); }
/* 1100 px se quedaba corto en cuanto la tarjeta llevó dibujo: en una pantalla normal
   sobraba medio ancho de pantalla vacío a los lados y las tarjetas salían apretadas. */
.page { max-width: 1240px; margin: 0 auto; padding: 24px 18px 60px; }
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.page-head h2 { margin: 0; }
.page-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.center { min-height: 100%; display: grid; place-items: center; padding: 24px; }
.loading { padding: 40px; text-align: center; color: var(--muted); }

/* ===========================================================================
   PANTALLA DE ENTRADA. Era una cajita de 380 px flotando arriba del todo, con un
   emoji por logotipo. Es lo primero que ve alguien que abre esto, y decía «proyecto
   de fin de semana» antes de que nadie tocara nada.

   Partida en dos: a la izquierda lo que hace la herramienta sobre una retícula de
   plano (hecha con gradientes: ni una imagen que descargar), a la derecha el
   formulario. En móvil el lado izquierdo se pliega y queda solo la entrada, que es
   lo único imprescindible.
   =========================================================================== */
/* `100vh` y no `100%`: en la pantalla de entrar la barra de arriba está oculta, así
   que la página ES la ventana. Con `100%` la mitad de abajo quedaba en blanco, porque
   un porcentaje contra un padre sin altura fija no resuelve. */
.auth { min-height: 100vh; display: grid; grid-template-columns: 1.15fr .85fr; }
.auth-lado {
  position: relative; overflow: hidden; padding: 54px clamp(28px, 5vw, 72px);
  display: flex; flex-direction: column; justify-content: center; gap: 18px;
  background:
    radial-gradient(120% 90% at 12% 0%, var(--accent-soft), transparent 60%),
    linear-gradient(160deg, var(--panel-2), var(--bg) 70%);
  border-right: 1px solid var(--line);
}
/* La retícula del plano, de fondo y muy tenue: es el producto, no un adorno. */
.auth-plano {
  position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image:
    linear-gradient(var(--line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 46px 46px, 46px 46px;
  -webkit-mask-image: radial-gradient(80% 70% at 70% 90%, #000, transparent 75%);
          mask-image: radial-gradient(80% 70% at 70% 90%, #000, transparent 75%);
  z-index: 0;
}
.auth-lado > *:not(.auth-plano) { position: relative; z-index: 1; }
.auth-marca { display: inline-flex; align-items: center; gap: 10px; font-size: 19px; font-weight: 700; letter-spacing: -.02em; }
.auth-logo {
  display: grid; place-items: center; width: 38px; height: 38px; border-radius: 11px;
  background: var(--accent); color: var(--accent-ink); box-shadow: var(--sh-2);
}
.auth-lado h1 {
  margin: 6px 0 0; font-size: clamp(28px, 3.4vw, 42px); line-height: 1.1;
  letter-spacing: -.03em; max-width: 12em;
}
.auth-claim { margin: 0; max-width: 34em; color: var(--text-2); font-size: 15.5px; }
.auth-puntos { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 12px; max-width: 30em; }
.auth-puntos li { display: flex; align-items: flex-start; gap: 11px; }
.auth-puntos svg { color: var(--accent); flex: none; margin-top: 2px; }
.auth-puntos strong { display: block; font-size: 14.5px; }
.auth-puntos span span { color: var(--muted); font-size: 13px; }

.auth-caja { display: grid; align-content: center; justify-items: stretch; padding: 40px clamp(20px, 4vw, 56px); }
.auth-form { width: min(380px, 100%); margin: 0 auto; }
.auth-form h2 { margin: 0 0 6px; font-size: 22px; letter-spacing: -.02em; }
.btn.big { width: 100%; margin-top: 18px; padding: 11px 16px; font-size: 15px; }
.auth-demos { width: min(380px, 100%); margin: 22px auto 0; display: grid; gap: 8px; }
/* Separador con texto: dos líneas y la frase en medio. */
.auth-sep { display: flex; align-items: center; gap: 10px; margin: 0 0 4px; color: var(--muted); font-size: 12px; }
.auth-sep::before, .auth-sep::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.auth-demo {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 10px 13px; cursor: pointer; color: var(--text);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  font: inherit; transition: border-color .14s var(--ease), background .14s var(--ease), transform .14s var(--ease);
}
.auth-demo:hover { border-color: var(--accent-line); background: var(--panel-2); transform: translateY(-1px); }
.auth-demo svg { color: var(--accent); flex: none; }
.auth-demo strong { display: block; font-size: 14px; }
.auth-demo .small { display: block; line-height: 1.35; }
/* El hueco de las cuentas de prueba: existe siempre aunque no se rellene, para que el
   pie de contacto no baile cuando responde /api/health. */
.auth-hueco { display: contents; }
/* «¿Todavía no tienes cuenta?». Es la única salida de quien llega y no puede entrar:
   discreta, pero visible sin buscarla. */
.auth-pie {
  width: min(380px, 100%); margin: 22px auto 0; padding-top: 16px;
  border-top: 1px solid var(--line); color: var(--muted); font-size: 13px; text-align: center;
}
/* Botón que parece un enlace: acciones secundarias que no compiten con el primario. */
.btn.link {
  padding: 0; background: none; border: none; box-shadow: none;
  color: var(--accent); font: inherit; font-weight: 600; cursor: pointer;
}
.btn.link:hover { text-decoration: underline; background: none; }
/* Doble clase a propósito: `.btn.link` pinta de azul y hay que ganarle, porque este
   botón es la salida secundaria y no puede competir con «Enviar». */
.btn.link.auth-volver { display: block; width: 100%; margin-top: 14px; font-weight: 500; color: var(--muted); }
.btn.link.auth-volver:hover { color: var(--accent); }
/* «¿Has olvidado la contraseña?»: debajo del botón de entrar, discreta pero a la vista.
   Quien llega aquí sin acordarse ya está de mal humor; no es momento de esconderla. */
.btn.link.auth-olvido { display: block; width: 100%; margin-top: 12px; font-weight: 500; font-size: 13px; color: var(--muted); }
.btn.link.auth-olvido:hover { color: var(--accent); }
/* El mismo cuadro del logotipo, en tono de aviso: el enlace caducado no es un error de
   la persona, así que se cuenta con calma y no en rojo de alarma. */
.auth-logo-aviso { background: var(--warn); color: #1a202c; }
/* Acuse de recibo: SUSTITUYE al formulario. Un aviso que se va solo deja a quien acaba
   de escribir sin saber si se mandó, y lo manda otra vez. */
.auth-gracias { width: min(380px, 100%); margin: 0 auto; display: grid; justify-items: center; gap: 10px; text-align: center; }
.auth-gracias h2 { margin: 6px 0 0; font-size: 22px; letter-spacing: -.02em; }
.auth-gracias p { margin: 0; max-width: 26em; }
.auth-gracias .btn { margin-top: 8px; }

@media (max-width: 880px) {
  .auth { grid-template-columns: 1fr; }
  .auth-lado { display: none; }
}

/* ===========================================================================
   CABECERA DE PÁGINA CON CIFRAS. Una lista de proyectos que solo dice el nombre
   obliga a entrar en cada uno para saber cuál es cuál. Las cifras van arriba,
   grandes, y salen del MISMO modelo que el plano (m² del recinto, no un campo que
   alguien teclea y se queda viejo).
   =========================================================================== */
.hero { margin-bottom: 22px; }
.hero-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.hero h2 { margin: 0; font-size: 26px; letter-spacing: -.025em; }
.hero .sub { margin: 4px 0 0; color: var(--muted); }
.stats { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
.stat {
  flex: 1 1 130px; min-width: 120px; padding: 11px 14px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
}
.stat b { display: block; font-size: 21px; line-height: 1.15; letter-spacing: -.02em; }
.stat span { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }
.stat.acc b { color: var(--accent); }
.stat.ok b { color: var(--accent-2); }

/* Cards / grid */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 14px; }
.card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 16px 18px; display: block; color: var(--text);
  transition: border-color .16s var(--ease), transform .16s var(--ease), box-shadow .16s var(--ease);
}
a.card:hover {
  border-color: var(--accent-line); transform: translateY(-2px); box-shadow: var(--sh-2);
}
.card h3 { margin: 0 0 6px; }
.badge {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 10px; padding: 3px 9px; font-size: 12px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 999px; color: var(--muted);
}
.card .open { display: block; margin-top: 12px; color: var(--accent); font-size: 13px; }

/* Tarjeta de ESTANCIA con vista previa del plano. La miniatura es un SVG hecho
   con la geometría real, así que siempre está al día y pesa nada. */
.card.room.with-prev, .card.room { padding: 0; overflow: hidden; }
.card.room .prev {
  display: block; background: var(--canvas-bg); border-bottom: 1px solid var(--line);
  color: var(--text-2); padding: 6px;
}
.card.room .plan-prev { display: block; width: 100%; height: 132px; }
.card.room .prev-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; height: 132px; color: var(--muted); font-size: 12px;
}
.card.room .room-meta { padding: 12px 14px 14px; }
.card.room .room-meta h3 { margin: 0 0 8px; font-size: 15.5px; }
.card.room .room-meta .badge { margin: 0 5px 0 0; }
.card.room .card-acts { top: 8px; right: 8px; }
.card.room:hover .plan-prev { color: var(--accent); }
.grid { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }

/* TARJETA DE PROYECTO con la planta de su estancia mayor. Mismo argumento que en las
   estancias: el dibujo se reconoce antes que cualquier texto, y sale de la geometría
   real, así que no hay nada que mantener al día. */
.card.project { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.card.project .prev {
  display: block; background: var(--canvas-bg); border-bottom: 1px solid var(--line);
  color: var(--text-2); padding: 6px; transition: color .16s var(--ease);
}
.card.project .plan-prev { display: block; width: 100%; height: 112px; }
.card.project:hover .plan-prev { color: var(--accent); }
.card.project .prev-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; height: 112px; color: var(--muted); font-size: 12px;
}
.card.project .proj-meta { padding: 13px 15px 15px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.card.project .proj-meta h3 { margin: 0; font-size: 15.5px; }
.card.project .proj-meta .muted { margin: 0; font-size: 13px; }
.card.project .proj-cifras {
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line-soft);
  display: flex; align-items: baseline; gap: 4px; flex-wrap: wrap; font-size: 12.5px; color: var(--muted);
}
.card.project .proj-cifras b { color: var(--text); font-weight: 600; }
.card.project .proj-cifras .sep { opacity: .45; }

/* Mandos de la tarjeta (renombrar / borrar). Aparecen al pasar el ratón para no
   competir con el contenido, pero SIEMPRE visibles con el foco por teclado. */
.card { position: relative; }
.card-acts {
  position: absolute; top: 10px; right: 10px; display: flex; gap: 4px;
  opacity: 0; transition: opacity .14s var(--ease);
}
.card:hover .card-acts, .card:focus-within .card-acts { opacity: 1; }
.icon-btn {
  width: 28px; height: 28px; display: grid; place-items: center; padding: 0;
  font-size: 13px; line-height: 1; cursor: pointer; color: var(--text-2);
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  transition: border-color .14s var(--ease), background .14s var(--ease);
}
.icon-btn:hover { color: var(--text); border-color: var(--accent-line); background: var(--panel-3); }
.icon-btn.danger:hover { border-color: var(--danger); background: rgba(255,95,109,.14); }

/* Form / login */
.login { width: min(380px, 92vw); }
.login h1 { margin: 0 0 4px; }
label { display: block; margin: 12px 0 4px; font-size: 13px; color: var(--muted); }
input, select, textarea {
  width: 100%; padding: 9px 11px; background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r); font: inherit;
  transition: border-color .14s var(--ease), background .14s var(--ease);
}
input:hover, select:hover { border-color: #3a4459; }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder { color: #6b7789; }
label { font-weight: 500; letter-spacing: .01em; }
.hint { font-size: 12px; margin-top: 14px; }

/* Botones. Tres niveles: primario (acción principal), normal y fantasma. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 8px 13px; border-radius: var(--r); border: 1px solid var(--line);
  background: var(--panel-2); color: var(--text-2); cursor: pointer; font: inherit;
  line-height: 1.2; white-space: nowrap;
  transition: background .14s var(--ease), border-color .14s var(--ease),
              color .14s var(--ease), transform .06s var(--ease);
}
.btn:hover { background: var(--panel-3); border-color: #3a4459; color: var(--text); }
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600;
  box-shadow: var(--sh-1);
}
/* El hover del botón principal MUEVE EL COLOR DE MARCA, no lo cambia por otro.
   Estuvo con un azul fijo (#5f9aff), así que un botón rosa de Douglas se volvía azul
   al pasar el ratón: un fallo que solo aparece con el color de marca puesto, y por eso
   llevaba ahí sin que se viera. Y aclararlo tampoco servía: con el rosa de Douglas,
   `+18 % de blanco` bajaba el blanco de 4,53:1 a 3,95:1 — el hover EMPEORABA la
   lectura. Por eso el destino es `--accent-deep`, que siempre se aleja de la tinta
   (con Douglas, 4,53 → 5,69:1). */
.btn.primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover { background: var(--panel-2); border-color: var(--line); }
.btn.small { padding: 6px 10px; font-size: 12.5px; border-radius: var(--r-sm); }
/* EL TEXTO DE UN BOTÓN ACTIVO. Estuvo en `#cfe0ff` —un azul pálido fijo, pensado para
   un fondo azul oscuro— y con el color de marca puesto quedaba texto azul clarito
   sobre un fondo rosa claro: ILEGIBLE. Es el fallo que se reportó.

   Lo primero que puse fue `var(--accent)`, y medirlo lo tumbó: el acento sobre el
   acento al 14 % da entre 2,68 y 3,97:1 según tema y marca — mejor que antes y aun
   así por debajo del mínimo. `--text` da 11 a 14:1 en los cuatro casos. Que el botón
   está activo ya lo dicen el fondo teñido y el borde de marca; el texto solo tiene
   que leerse. */
.btn.active { background: var(--accent-soft); border-color: var(--accent-line); color: var(--text); }
.btn.danger:hover { background: rgba(255,95,109,.14); border-color: var(--danger); color: #ffb3ba; }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn:disabled:hover { background: var(--panel-2); border-color: var(--line); transform: none; }
/* ICONOS. Son SVG de trazo que heredan el color del texto: un solo lenguaje
   gráfico en toda la app, en vez de emojis que cada sistema pinta a su manera. */
.ic { flex: 0 0 auto; display: block; }
.btn .ic { margin-right: -1px; }
.icon-btn .ic { display: block; }
/* Botón SOLO icono: cuadrado, sin el hueco del texto. */
.btn.icon-only { padding: 7px; }

/* Toast */
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  padding: 11px 18px 11px 16px; border-radius: var(--r); background: var(--panel-2);
  border: 1px solid var(--line); border-left-width: 3px;
  z-index: 100; box-shadow: var(--sh-3); font-size: 13.5px;
  animation: toast-in .2s var(--ease);
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 10px); } }
.toast.con-accion { display: flex; align-items: center; gap: 14px; padding-right: 6px; max-width: min(560px, 92vw); }
/* El aspa va SEPARADA de la acción y más apagada: son cosas opuestas y pulsar una
   creyendo pulsar la otra, con 9,6 s de reloj corriendo, es de las que fastidian. */
.toast-cerrar {
  flex: none; display: grid; place-items: center; width: 26px; height: 26px; margin-left: -6px;
  padding: 0; cursor: pointer; color: var(--muted); background: none; border: none;
  border-radius: var(--r-sm);
}
.toast-cerrar:hover { color: var(--text); background: var(--panel-3); }
.toast-act {
  flex: none; padding: 5px 11px; margin: 0; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent);
  background: none; border: 1px solid var(--accent-line); border-radius: var(--r-sm);
}
.toast-act:hover { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); }
.toast.ok { border-left-color: var(--accent-2); }
.toast.error { border-left-color: var(--danger); }
.toast.info { border-left-color: var(--accent); }

/* ---------- Responsive (tablet / móvil) ---------- */
@media (max-width: 1024px) {
  .palette { width: 150px; }
  .right { width: 216px; }
}
@media (max-width: 760px) {
  body { overflow-x: hidden; }
  #whoami { display: none; }
  /* La barra de arriba tenía siete cosas y en 412 px se salía por la derecha.
     Se queda con lo que se usa: marca, migas y salir. */
  #topbar { gap: 8px; padding: 8px 10px; }
  #topbar .brand span { display: none; }
  #crumbs { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #crumbs a:not(:last-child), #crumbs .sep:first-of-type { display: none; }
  .empresa-chip { display: none; }
  .toolbar { gap: 4px; padding: 6px 8px; }
  .toolbar .btn.small { padding: 6px 8px; }
  .palette { width: 118px; }
  .palette h5 { font-size: 10px; }
  .pal-grid { grid-template-columns: 1fr; }
  .right { width: 186px; }
  .v3d-bar { flex-wrap: wrap; gap: 8px; }
  .v3d-ctl span { display: none; }
}

/* ---------- Editor ---------- */
.editor { display: flex; flex-direction: column; height: calc(100vh - 53px); }

/* ===========================================================================
   BARRA DE MODOS: TIENDA · PLANO · DATOS
   ===========================================================================
   Son tres respuestas de igual rango a tres preguntas distintas —cómo se ve, dónde
   va cada cosa, si funciona— y por eso se pintan como un selector de tres y no como
   un botón principal con dos hermanos pequeños. La jerarquía la marca CUÁL ESTÁ
   PUESTO, y el que está puesto al abrir es «Tienda».

   Va ARRIBA de la barra de herramientas y no dentro: las herramientas son del modo
   Plano, así que no pueden estar al mismo nivel que lo que elige el modo. Es la
   diferencia entre «qué estoy mirando» y «con qué estoy trabajando», y mezclarlas es
   lo que hacía que el editor pareciera un CAD con cosas colgando. */
/* LA FILA DE ARRIBA: mandos del modo a la izquierda, pestañas a la derecha.
   Eran dos filas —una con tres pestañas y metro y medio de blanco, otra apretada— y ahora
   es una. Envuelve en pantalla estrecha en vez de desbordar. */
.modebar {
  display: flex; align-items: center; gap: 4px;
  padding: 8px 12px 0;
  background: var(--panel);
  flex-wrap: wrap; row-gap: 4px;
}
.modo-tabs { display: flex; align-items: center; gap: 4px; margin-left: auto; }
/* Con la barra de la 3D dentro, la fila deja de tener el hueco de arriba: los mandos son
   botones y no pestañas, y necesitan el mismo aire por los dos lados. */
.modebar.con-barra { padding: 0 12px 0 0; align-items: stretch; }
.modebar.con-barra .v3d-bar {
  flex: 1 1 auto; min-width: 0; border-bottom: 0; background: transparent; padding: 8px 12px;
}
.modebar.con-barra .modo-tabs { align-self: flex-end; }
.modo-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 16px 10px; border: 0; border-bottom: 2.5px solid transparent;
  background: transparent; color: var(--muted);
  font: 600 13.5px/1 inherit; font-family: inherit; letter-spacing: -.005em;
  cursor: pointer; border-radius: var(--r-sm) var(--r-sm) 0 0;
  transition: color .15s var(--ease), background-color .15s var(--ease), border-color .15s var(--ease);
}
.modo-btn:hover { color: var(--text-2); background: var(--panel-2); }
.modo-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
.modo-btn svg { flex: none; }

/* El reparto de la pantalla en cada modo. Nadie se desmonta: se esconde. Los paneles
   guardan su estado —la pieza seleccionada, la pestaña abierta, el scroll— y volver a
   Plano tiene que devolver el editor donde estaba, no uno nuevo. */
.tienda-host { flex: 1; min-width: 0; position: relative; }

.modo-tienda .toolbar,
.modo-tienda .palette,
.modo-tienda .canvas,
.modo-tienda .right,
.modo-tienda .hintbar,
.modo-tienda .movil-bar { display: none !important; }

.modo-plano .tienda-host { display: none; }

/* DATOS: el panel derecho pasa a ocupar la pantalla entera. Un planograma es una
   TABLA y una tabla en una columna de 320 px se lee con lupa — que es como se leía
   hasta ahora, apretada contra el borde del plano. */
.modo-datos .toolbar,
.modo-datos .palette,
.modo-datos .canvas,
.modo-datos .tienda-host,
.modo-datos .hintbar,
.modo-datos .movil-bar { display: none !important; }
.modo-datos .right {
  width: auto; flex: 1; max-width: none; border-left: 0;
}
.modo-datos .right .tabpane { max-width: 1100px; margin: 0 auto; }
/* La barra de pestañas se centra con el contenido en vez de estirarse de borde a
   borde: repartidas por 1.500 px, tres pestañas quedan tan separadas que dejan de
   leerse como un grupo. */
.modo-datos .tabbar { max-width: 1100px; margin: 0 auto; width: 100%; }
/* PROPIEDADES no es de este modo. Es el panel de la pieza que tienes seleccionada en
   el PLANO, y aquí no hay plano ni selección: en Datos solo enseñaría los acabados
   del suelo, que es justo la clase de puerta que hace dudar de en qué modo estás. */
.modo-datos .tabbar .tab[data-tab="props"] { display: none; }

/* La 3D empotrada en el modo Tienda. Es LA MISMA vista y la misma barra que la capa
   a pantalla completa —el paseo, la chuleta de teclas y el PNG estaban resueltos
   allí—: lo único que cambia es que deja de flotar sobre todo y se queda en su hueco. */
.v3d-overlay.dentro { position: absolute; inset: 0; z-index: 1; }
.toolbar {
  display: flex; align-items: center; gap: 5px; padding: 7px 10px; flex-wrap: wrap;
  background: var(--panel); border-bottom: 1px solid var(--line); box-shadow: var(--sh-1);
}
.toolbar .sep-v { width: 1px; height: 20px; background: var(--line); margin: 0 5px; }
/* Las herramientas forman un bloque: se lee como un selector, no como botones sueltos. */
.toolbar .tool {
  padding: 7px; background: transparent; border-color: transparent;
}
.toolbar .tool:hover { background: var(--panel-2); border-color: var(--line); }
.toolbar .tool.active {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.toolbar select.small { width: auto; padding: 5px 8px; font-size: 12.5px; }
.chk {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--muted);
  padding: 5px 8px; border-radius: var(--r-sm); cursor: pointer; user-select: none;
}
.chk:hover { background: var(--panel-2); color: var(--text-2); }
.chk input { width: auto; accent-color: var(--accent); cursor: pointer; }

.editor-body { flex: 1; display: flex; min-height: 0; }
.palette { width: 190px; overflow-y: auto; padding: 12px 10px; border-right: 1px solid var(--line); background: var(--panel); }
.palette h5 { margin: 14px 0 7px; color: var(--muted); font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em; }
.palette h5:first-child { margin-top: 0; }
.pal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
/* BUSCADOR de la paleta. Con 54 tipos en 8 familias encontrar «probador» era
   recorrer la lista con la vista; con el catálogo de una cadena serán varios cientos
   y eso deja de ser una opción. Pegajoso arriba: al bajar por las familias el campo
   se queda a mano en vez de perderse. */
.pal-buscar {
  position: sticky; top: -12px; z-index: 2;
  width: 100%; margin: -2px 0 12px; padding: 7px 10px;
  font-size: 12.5px;
}

/* Encabezado de familia CON SU PUNTO DE COLOR. Es la leyenda del plano, puesta donde
   se eligen las piezas y no en una tarjeta aparte que hay que ir a buscar: así el
   código de color se aprende usándolo, que es la única forma en que se aprende. */
.pal-fam { display: flex; align-items: center; gap: 7px; }
.pal-punto {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  box-shadow: 0 0 0 2.5px color-mix(in srgb, currentColor 12%, transparent);
}

.pal-item {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 9px 4px 7px;
  background: var(--panel-2); border: 1px solid var(--line-soft); border-radius: var(--r);
  /* La FRANJA DE FAMILIA. Un filete de 3 px del color de la familia en el canto
     izquierdo: identifica la ficha sin teñirla entera, que con ocho familias en
     pantalla a la vez sería una bandera y no una paleta. El color llega en la
     variable `--fam`, que pone `_buildPalette`. */
  border-left: 3px solid var(--fam, var(--line-soft));
  color: var(--text-2); cursor: pointer; font-size: 10.5px; text-align: center;
  transition: background .14s var(--ease), border-color .14s var(--ease), transform .06s var(--ease);
}
/* Al pasar por encima manda el color de la FAMILIA, no el azul de acento de la app:
   es el mismo tono con el que la pieza se va a dibujar en el plano, y ver los dos
   juntos es lo que une la ficha con su rectángulo. */
.pal-item:hover {
  border-color: var(--fam, var(--accent-line));
  background: color-mix(in srgb, var(--fam, var(--accent)) 10%, var(--panel-2));
  color: var(--text);
}
.pal-item:active { transform: translateY(1px); }
/* ELEGIDA. Aquí sí manda el acento de la app y no el color de familia: «esto es lo
   que voy a colocar» es un estado de la HERRAMIENTA, y los estados de herramienta se
   marcan igual en toda la aplicación. El filete de familia sigue puesto al lado, así
   que no se pierde de qué familia era. */
.pal-item.active {
  border-color: var(--accent); background: var(--accent-soft); color: var(--text);
  box-shadow: 0 0 0 3px var(--accent-soft);
  border-left-color: var(--fam, var(--accent));
}
/* EN SERIE: la pieza se queda cargada hasta Esc. Tiene que distinguirse de la
   marcada normal —que coloca UNA— porque el error que esto arregla era justamente no
   saber en qué modo estabas. Va un borde discontinuo, que se lee como «esto sigue
   abierto», y el «∞» dice cuántas sin ocupar sitio. */
.pal-item.serie { border-style: dashed; border-color: var(--accent); position: relative; }
.pal-item.serie::after {
  content: '∞'; position: absolute; top: -1px; right: 3px;
  font-size: 12px; line-height: 1; color: var(--accent); font-weight: 700;
}
.pal-ic { display: grid; place-items: center; height: 32px; font-size: 20px; color: var(--text-2); opacity: .92; }
.pal-ic svg { display: block; max-width: 100%; }
.pal-item:hover .pal-ic, .pal-item.active .pal-ic { color: inherit; opacity: 1; }
/* La silueta va del color de su familia. Es el mismo dibujo que antes —el símbolo de
   planta real, con la proporción real de la pieza— pero deja de ser un trazo gris
   entre 53 trazos grises iguales. */
.pal-ic { color: var(--fam, var(--text-2)); }
.pal-lbl { line-height: 1.1; }
/* La MEDIDA en la ficha. Estaba solo en el `title`, o sea escondida tras un segundo
   de ratón quieto — y elegir entre «Góndola» y «Góndola baja» es elegir una medida.
   Va en cifras de ancho fijo para que la columna no baile. */
.pal-med {
  font-size: 9.5px; color: var(--muted); letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
}
.pal-item:hover .pal-med, .pal-item.active .pal-med { color: var(--text-2); }

.canvas { flex: 1; min-width: 0; position: relative; background: var(--canvas-bg); overflow: hidden; }
.canvas .konvajs-content { position: absolute; inset: 0; }

/* Campo para escribir una medida SOBRE el plano (clic en el número). */
.dim-input {
  position: absolute; z-index: 6; width: 76px; padding: 3px 6px; margin: 0;
  transform: translate(-50%, -50%); text-align: center;
  font: 600 13px/1.2 inherit; color: var(--text);
  /* EL FONDO IBA A FUEGO (#0d1526) y el texto seguía al tema: en modo claro salían
     números oscuros sobre un fondo oscuro y no se leía lo que estabas escribiendo —
     justo en el campo donde se teclea una medida—. Va con los tokens, como el resto
     de lo que flota sobre el lienzo; la sombra también, que la de antes estaba
     calculada para fondo oscuro y en claro era una mancha. */
  background: var(--panel); border: 1px solid var(--accent); border-radius: 6px;
  box-shadow: var(--sh-2);
}
.dim-input:focus { outline: none; border-color: var(--accent); }

/* Navegador de la vista: minimapa + cruceta (sustituye a la mano y a «Ajustar»). */
.nav-widget {
  /* Arriba a la derecha: la esquina de abajo es del CAJETÍN del plano, que es
     contenido y no un mando. */
  position: absolute; right: 12px; top: 12px; z-index: 5; width: 164px;
  padding: 6px; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 6px 20px rgba(0,0,0,.45);
  user-select: none;
}
.nav-head {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
  padding: 0 2px 5px;
}
.nav-tog {
  width: 20px; height: 18px; padding: 0; line-height: 1; font-size: 11px; cursor: pointer;
  background: transparent; border: 1px solid var(--line); border-radius: 5px; color: var(--muted);
}
.nav-tog:hover { color: var(--text); border-color: var(--accent); }
.nav-widget.closed { width: auto; }
.nav-widget.closed .nav-head { padding-bottom: 0; }
.nav-widget.closed .nav-body { display: none; }
.nav-body { display: flex; flex-direction: column; gap: 6px; }
.nav-map {
  display: block; width: 152px; height: 106px; cursor: crosshair;
  border: 1px solid var(--line); border-radius: 6px; background: var(--canvas-bg);
}
.nav-ctl { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.nav-pad { display: grid; grid-template-columns: repeat(3, 26px); grid-auto-rows: 22px; gap: 2px; }
.nav-zoom { display: grid; grid-template-columns: 26px 26px; grid-auto-rows: 22px; gap: 2px; }
.nav-zoom .nav-btn:last-child { grid-column: span 2; }
.nav-gap { display: block; }
.nav-btn {
  display: grid; place-items: center; padding: 0; font-size: 12px; line-height: 1; cursor: pointer;
  color: var(--text); background: var(--panel-2); border: 1px solid var(--line); border-radius: 5px;
}
.nav-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
.nav-btn:active { transform: translateY(1px); }
.nav-fit { color: var(--accent); }

@media (max-width: 900px) {
  /* En pantallas pequeñas el lienzo es lo escaso: solo la cruceta. */
  .nav-widget { width: auto; right: 8px; top: 8px; }
  .nav-map { display: none; }
}

/* El panel NO scrollea entero: la pestañera se queda fija y es CADA pestaña la
   que scrollea. Así la cabecera de acabados puede fijarse de verdad (con el panel
   entero como scroller, el `sticky` no llegaba a engancharse). */
.right {
  width: 264px; padding: 0; border-left: 1px solid var(--line); background: var(--panel);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
.tabbar { display: flex; flex: 0 0 auto; background: var(--panel); border-bottom: 1px solid var(--line); }
.tabbar .tab {
  flex: 1 1 0; min-width: 0; padding: 10px 2px; font-size: 11px; font-weight: 500;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  background: transparent; border: none; border-bottom: 2px solid transparent;
  color: var(--muted); cursor: pointer; white-space: nowrap; overflow: hidden;
  transition: color .14s var(--ease), border-color .14s var(--ease);
}
.tabbar .tab span { overflow: hidden; text-overflow: ellipsis; }
.tabbar .tab:hover { color: var(--text-2); background: var(--panel-2); }
.tabbar .tab.active { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }
.tabpane { padding: 12px; flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; }

/* Selector de materiales/acabados */
.mat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 6px 0 12px; }
.mat-item {
  display: flex; flex-direction: column; align-items: stretch; gap: 3px; padding: 5px;
  background: var(--panel-2); border: 2px solid var(--line); border-radius: 8px; color: var(--text);
  cursor: pointer; font-size: 11px; text-align: center;
}
.mat-item:hover { border-color: var(--accent); }
.mat-item.active { border-color: var(--accent-2); box-shadow: 0 0 0 1px var(--accent-2); }
.mat-item:disabled { cursor: default; opacity: .9; }
.mat-sw { width: 100%; height: 42px; object-fit: cover; border-radius: 5px; display: block; }
.mat-none { height: 42px; display: grid; place-items: center; font-size: 20px; color: var(--muted); border: 1px dashed var(--line); border-radius: 5px; }
.mat-name { font-weight: 600; line-height: 1.1; }
.mat-price { color: var(--muted); font-size: 10px; }

/* Cabecera FIJA del panel de acabados: superficie + ficha + filtros.
   El 34px es el alto de la pestañera, que también es sticky. */
.fin-head {
  /* El scroller es la propia pestaña, así que se fija arriba del todo. */
  position: sticky; top: -12px; z-index: 1;
  /* Se sale del padding del panel para que al scrollar nada asome por los lados. */
  margin: -12px -12px 0; padding: 12px 12px 9px;
  background: var(--panel); border-bottom: 1px solid var(--line);
}
.fin-seg { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.fin-tab {
  display: grid; grid-template-columns: 34px 1fr; grid-template-rows: auto auto;
  gap: 0 8px; align-items: center; padding: 6px 8px; cursor: pointer; text-align: left;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px; color: var(--text);
}
.fin-tab:hover { border-color: var(--accent); }
.fin-tab.active { border-color: var(--accent); background: var(--accent-soft); }
.fin-tab-sw { grid-row: span 2; width: 34px; height: 34px; object-fit: cover; border-radius: 5px; display: block; }
.fin-tab-none, .fin-tab-varios {
  display: grid; place-items: center; font-size: 15px; color: var(--muted);
  border: 1px dashed var(--line); border-radius: 5px;
}
.fin-tab-lbl { font-size: 12.5px; font-weight: 600; line-height: 1.15; }
.fin-tab-mat {
  font-size: 10.5px; color: var(--muted); line-height: 1.15;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Catálogo de proveedor: filtros + ficha del acabado puesto */
.mat-filters { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 8px 0 0; }
.mat-filters select, .mat-filters input { width: 100%; min-width: 0; padding: 5px 7px; font-size: 12px; }
.mat-filters .mat-serie, .mat-filters input { grid-column: span 2; }
/* Aviso de paredes con acabado propio (vista de estancia) */
.wall-scope {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  margin: 6px 0 0; padding: 5px 7px; border-radius: 7px;
  background: var(--accent-soft); font-size: 11.5px;
}
.fin-intro { margin: 0 0 8px; font-size: 11.5px; line-height: 1.3; }
/* Ficha en UNA fila: miniatura · nombre+datos · €/m². Va en la cabecera fija. */
.mat-card {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; gap: 8px; align-items: center;
  padding: 6px 7px; margin: 6px 0 0;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
}
.mat-card-img { width: 40px; height: 34px; object-fit: cover; border-radius: 4px; display: block; }
.mat-card-txt { min-width: 0; display: flex; flex-direction: column; }
.mat-card-name {
  font-size: 12.5px; font-weight: 600; line-height: 1.2; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
a.mat-card-name:hover { color: var(--accent); }
.mat-card-ext { color: var(--accent); }
.mat-card-txt span { font-size: 10.5px; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mat-card-price { display: flex; align-items: center; gap: 4px; margin: 0; }
.mat-card-price span { font-size: 10px; color: var(--muted); }
.mat-card-price input { width: 54px; padding: 3px 5px; font-size: 12px; text-align: right; }
.mat-card-empty { margin: 6px 0 0; font-size: 11.5px; }
/* Selector de acabado dentro de Propiedades (pared seleccionada) */
.props-fin-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.props-fin-head .btn, .wall-scope .btn { text-transform: none; letter-spacing: 0; white-space: nowrap; padding: 4px 9px; }
.props .mat-filters { margin-top: 6px; }
.props .mat-grid { margin-bottom: 4px; }
.mat-more { grid-column: span 2; margin: 4px 0 0; text-align: center; }
.finishes-panel .mat-grid { margin-top: 10px; }
/* Miniaturas de azulejo real: algo más altas, que se aprecie el despiece */
.mat-item .mat-sw { height: 50px; }
.right h4 { margin: 14px 0 8px; font-size: 13px; }
.right h4:first-child { margin-top: 0; }
.layer-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 14px; }
.layer-row span { flex: 1; }
.layer-row input { width: auto; }
.field { display: block; margin: 8px 0; }
.field span { display: block; font-size: 12px; color: var(--muted); margin-bottom: 3px; }
.props .small { font-size: 12px; }
/* Selector segmentado (tipo de abertura, lado de bisagra…) */
.seg-field span { margin-bottom: 4px; }
.seg { display: flex; gap: 0; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.seg-btn {
  flex: 1 1 0; min-width: 0; padding: 6px 4px; font: inherit; font-size: 11.5px;
  background: var(--panel-2); color: var(--muted); border: none; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-right: 1px solid var(--line); transition: background .12s, color .12s;
}
.seg-btn:last-child { border-right: none; }
.seg-btn:hover { color: var(--text); }
.seg-btn.active { background: var(--accent); color: var(--accent-ink); font-weight: 600; }

.rot-row { display: flex; align-items: flex-end; gap: 6px; }
.rot-row .field { flex: 1; margin-bottom: 0; }
.rot-row .btn { white-space: nowrap; padding: 8px 10px; }

.hintbar { display: flex; align-items: center; gap: 12px;
  display: flex; align-items: center; padding: 6px 14px; border-top: 1px solid var(--line);
  font-size: 12px; background: var(--panel); min-height: 28px; color: var(--muted);
}

/* Panel de presupuesto (dentro del editor) */
.budget-panel { font-size: 13px; }

/* LAS CUATRO CIFRAS DE CABECERA: implantación, campaña, €/m² y €/m lineal. Rejilla de
   dos por dos y no una fila de cuatro porque el panel mide 264 px: en fila, cada cifra
   se quedaría en 60 px y «12.480,00 €» no cabe. En dos columnas cada una tiene ancho
   para su importe entero, que es justo lo que no se puede recortar.
   El número grande y la etiqueta pequeña debajo: se leen los cuatro de un vistazo y se
   vuelve al que interesa. */
.bud-kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 6px; }
.bud-kpi { background: var(--panel-2, var(--panel)); border: 1px solid var(--line);
  border-radius: 8px; padding: 8px 9px; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.bud-kpi-n { font-size: 15px; font-weight: 650; line-height: 1.15; overflow-wrap: anywhere; }
.bud-kpi-n.muted { color: var(--muted); font-weight: 500; }
.bud-kpi-l { font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; line-height: 1.25; }
.bud-sub { display: flex; align-items: center; justify-content: space-between; margin: 14px 0 5px; font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); }
.bud-row { display: flex; align-items: center; gap: 6px; padding: 3px 0; }
/* EL NOMBRE DE LA PARTIDA BAJA DE LÍNEA EN VEZ DE CORTARSE. Con el campo de precio
   (62 px) y el importe (78 px), al nombre le quedaban 87 px de los 264 del panel: se
   cortaban TODAS las líneas, hasta «Pintura de techo» por 7 px, y el `title` llevaba
   los m² —no el nombre—, así que lo cortado no había forma de leerlo. Los importes no
   se tocan: en un presupuesto, la cifra recortada sería mucho peor que el rótulo.
   Las que caben siguen en una línea; solo crecen las que lo necesitan.
   Se mantiene `align-items: center` de la fila: alinear arriba descolocaba las filas
   con campo —el importe del IVA se iba por encima de su propia casilla— porque el
   campo es más alto que el texto. */
.bud-row span.bud-name { flex: 1; min-width: 0; overflow-wrap: anywhere; line-height: 1.3; }
.bud-row input.bud-price, .bud-row .bud-sum { flex: 0 0 auto; }
.bud-row input { min-width: 0; }
.bud-row input.bud-name { flex: 1; padding: 4px 6px; }
/* El ciclo del extra va en su PROPIA línea, debajo de la fila: en la misma no cabe
   —nombre, cantidad, precio y papelera ya llenan los 264 px— y partirlo así deja
   leer «Campaña» entero en vez de un desplegable de 40 px con puntos suspensivos. */
.bud-row.extra { flex-wrap: wrap; }
.bud-row.extra .bud-ciclo { flex: 1 0 100%; margin: 2px 0 6px; padding: 3px 6px;
  font-size: 11px; color: var(--muted); background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px; }
/* Revestimiento partido por material: la cabecera lleva los metros y el subtotal, y
   debajo va una fila por material con SU €/m². Se sangran para que se lea que
   suman a la de arriba y no son cuatro partidas sueltas. */
.bud-row-cab { padding-bottom: 0; }
.bud-row-cab .bud-name { font-weight: 600; }
.bud-row-mat { padding-left: 12px; border-left: 2px solid var(--line); margin-left: 2px; }
.bud-row-mat .bud-name { color: var(--text-2); }

/* Chapa de «hay alguien más aquí». Discreta a propósito: que dos personas tengan la
   misma zona abierta es lo normal —el medidor en la tienda, quien presupuesta en la
   oficina—, así que informa sin dar la impresión de que hay que hacer algo. */
.badge.presencia { display: inline-flex; align-items: center; gap: 5px; }
.badge.presencia svg { opacity: .8; }
.budget-panel, .finishes-panel { overflow-x: hidden; }
.bud-price, .bud-qty, .bud-iva { width: 62px; padding: 4px 6px; text-align: right; }
.bud-qty { width: 48px; }
.bud-iva { width: 52px; }
/* Va dentro de un `label.chk`, y `.chk input { width:auto }` le ganaba por
   especificidad: el campo del IVA se estiraba y se comía el importe. */
.chk input.bud-iva { width: 52px; }
.bud-sum { width: 78px; text-align: right; white-space: nowrap; color: var(--text); }
.bud-totals { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 8px; }
.bud-totals .total { font-size: 16px; font-weight: 700; color: var(--accent-2); }
.bud-totals .btn { width: 100%; margin-top: 10px; }

/* Modales (plantillas, ayuda) */
.modal-back {
  position: fixed; inset: 0; z-index: 250; background: rgba(6,9,16,.66);
  backdrop-filter: blur(3px); display: grid; place-items: center; padding: 20px;
  animation: fade-in .14s var(--ease);
}
@keyframes fade-in { from { opacity: 0; } }
.modal {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 22px; width: min(560px, 96vw); max-height: 90vh; overflow: auto;
  box-shadow: var(--sh-3); animation: modal-in .18s var(--ease);
}
@keyframes modal-in { from { opacity: 0; transform: translateY(8px) scale(.985); } }
.modal h3 { margin: 0 0 14px; font-size: 17px; letter-spacing: -.01em; }
.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(164px, 1fr)); gap: 10px; margin-bottom: 14px; }
.tpl-card {
  display: flex; flex-direction: column; align-items: stretch; gap: 3px; padding: 8px 8px 10px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px;
  color: var(--text); cursor: pointer; text-align: left;
}
.tpl-card:hover { border-color: var(--accent); }
/* La MISMA miniatura que las estancias guardadas: se elige por la forma de la planta,
   no por un emoji que en cada sistema se dibuja distinto y no dice nada del reparto. */
.tpl-prev {
  display: block; margin-bottom: 5px; border-radius: 7px; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line);
}
.tpl-prev .plan-prev { display: block; width: 100%; height: auto; }
.tpl-card .tpl-name { font-size: 13px; font-weight: 600; }
.tpl-name { font-weight: 600; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }

/* Diálogos de formulario (crear/renombrar). Sustituyen a `prompt()`, que bloquea
   la pestaña y no se puede estilar ni automatizar. */
.modal-form { width: min(420px, 96vw); }
.modal-form .fields { display: flex; flex-direction: column; gap: 10px; }
.modal-form .fld { display: block; margin: 0; }
.modal-form .fld > span {
  display: block; margin-bottom: 4px; font-size: 12.5px; color: var(--text-2);
}
.modal-form input.err, .modal-form select.err { border-color: var(--danger); }
.btn.danger {
  background: rgba(255,95,109,.16); border-color: rgba(255,95,109,.5); color: #ffb3ba; font-weight: 600;
}
.mount-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 4px 0 8px; padding: 6px 8px; background: var(--panel-2); border: 1px dashed var(--line); border-radius: 8px; }
.mount-row span { flex: 1 1 auto; }
.ai-file { display: block; width: 100%; margin: 4px 0; }
.ai-preview { display: block; max-width: 100%; max-height: 300px; margin: 10px auto; border: 1px solid var(--line); border-radius: 10px; object-fit: contain; }
.ai-howto { margin: 10px 0; padding: 8px 12px; background: var(--panel-2); border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: 8px; }
.ai-howto ol { margin: 6px 0 0; padding-left: 18px; color: var(--muted, #8fa3c9); }
.ai-howto li { margin: 2px 0; }
.ai-status { margin-top: 10px; }
.ai-status.error { color: var(--danger, #ff6b6b); }
.bg-controls { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.bg-controls h5 { margin: 0 0 6px; }
.bg-controls .field { margin: 4px 0 8px; }
.bg-controls .btn { width: 100%; justify-content: center; }
.help-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 14px; }
.help-row { display: flex; justify-content: space-between; gap: 16px; padding: 6px 0; border-bottom: 1px solid var(--line); }
.help-row kbd { font-family: inherit; background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px; padding: 2px 8px; font-size: 12px; color: var(--muted); }

/* Menú contextual (clic derecho) */
.ctxmenu {
  position: fixed; z-index: 300; min-width: 190px; padding: 5px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); display: flex; flex-direction: column;
  animation: menu-in .12s var(--ease);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px); } }
.ctx-item {
  display: flex; align-items: center; gap: 9px;
  text-align: left; padding: 8px 12px; background: transparent; border: none;
  color: var(--text-2); cursor: pointer; border-radius: var(--r-sm); font: inherit; font-size: 13px;
  transition: background .1s var(--ease), color .1s var(--ease);
}
.ctx-item .ic { color: var(--muted); flex: 0 0 auto; }
.ctx-item:hover .ic { color: var(--accent-ink); }
.ctx-item:hover { background: var(--accent); color: var(--accent-ink); }
.ctx-sep { height: 1px; margin: 5px 8px; background: var(--line); }
.toolbar .more { padding: 7px; }
/* Conmutador de instalaciones: apagado se ve claramente apagado. */
.toolbar .inst.off { color: var(--muted); opacity: .75; }
.toolbar .inst.off .ic { opacity: .6; }
.toolbar .inst:not(.off) { border-color: var(--accent-line); background: var(--accent-soft); color: var(--text); }
:root[data-theme="claro"] .toolbar .inst:not(.off) { color: var(--text); }

/* Vista 3D */
.v3d-overlay { position: fixed; inset: 0; z-index: 200; background: #0e1422; display: flex; flex-direction: column; }
.v3d-bar {
  display: flex; align-items: center; gap: 8px; padding: 7px 14px;
  background: var(--panel); border-bottom: 1px solid var(--line);
  /* Envuelve en vez de desbordar: con la ventana estrecha, una barra que se sale deja
     mandos inalcanzables y no hay forma de saber que están. */
  flex-wrap: wrap; row-gap: 6px;
}
/* Los mandos de vista, en un bloque propio y con menos aire entre ellos: son cuatro
   preguntas de la misma familia (cómo se dibuja) y leídos como cuatro botones sueltos
   parecen cuatro acciones. */
/* Y TODO A LA MISMA ALTURA. Los botones miden lo que mide su texto más el relleno y los
   selectores lo que mide el suyo, así que en la misma fila quedaban desalineados un par de
   píxeles — poco para nombrarlo y bastante para que la barra se vea sucia. Se les da a los
   dos la misma altura de línea y se centran. */
.v3d-bar > * { align-self: center; }
.v3d-bar .btn.small, .v3d-bar .v3d-ctl select { height: 30px; box-sizing: border-box; }
.v3d-bar .btn.small { display: inline-flex; align-items: center; }
.v3d-ctl { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--muted); line-height: 30px; }
.v3d-ctl + .v3d-ctl { margin-left: -2px; }
.v3d-ctl select { width: auto; padding: 4px 7px; font-size: 12px; }
/* Dentro de la barra, el botón de recorridos deja de flotar: es un botón más de la fila. */
.v3d-bar .rec-abrir { position: static; box-shadow: none; }
.v3d-canvas { flex: 1; min-height: 0; position: relative; }

/* CHULETA DEL PASEO. Flota SOBRE el lienzo (no ocupa sitio en la barra) y va abajo del
   todo, donde no tapa el escaparate ni la línea de visión, que es lo que se está
   mirando. Los tonos son oscuros y translúcidos a propósito: tiene que leerse encima de
   una tienda blanca y encima de una negra. */
.v3d-pista {
  position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: center;
  max-width: min(92vw, 720px);
  padding: 9px 15px; border-radius: 999px;
  background: rgba(14, 20, 34, .82); color: #e8edf6;
  border: 1px solid rgba(255, 255, 255, .13);
  font-size: 12.5px; backdrop-filter: blur(7px);
  pointer-events: none;                 /* nunca se interpone entre el clic y el lienzo */
  box-shadow: 0 6px 22px rgba(0, 0, 0, .35);
}
.v3d-pista kbd {
  display: inline-block; min-width: 20px; text-align: center;
  padding: 2px 6px; border-radius: 5px;
  background: rgba(255, 255, 255, .12); border: 1px solid rgba(255, 255, 255, .2);
  border-bottom-width: 2px;
  font: 600 11.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
}
.v3d-pista strong { color: #ffd479; font-weight: 650; }
.v3d-canvas canvas { display: block; }
.v3d-error { padding: 30px; color: var(--muted); text-align: center; }

/* Impresión del presupuesto */
#print-root { display: none; }
@media print {
  body > *:not(#print-root) { display: none !important; }
  #print-root { display: block !important; color: #000; padding: 20px; font: 13px system-ui, sans-serif; }
  #print-root h1 { margin: 0 0 4px; }
  #print-root table { width: 100%; border-collapse: collapse; margin-top: 12px; }
  #print-root th, #print-root td { border: 1px solid #bbb; padding: 6px 8px; text-align: left; }
  #print-root th { background: #eee; }
  #print-root .ptot { margin-top: 16px; text-align: right; line-height: 1.7; }
  #print-root .pgrand { font-size: 20px; font-weight: 700; border-top: 2px solid #000; display: inline-block; padding-top: 6px; margin-top: 6px; }

  /* Dossier del cliente: plano + 3D + acabados + presupuesto en un documento.
     Pensado para «Guardar como PDF» desde el propio diálogo de impresión. */
  #print-root.dossier { padding: 0 8mm; font-size: 12px; }
  .dossier .dos-head {
    display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
    padding-bottom: 10px; border-bottom: 2px solid #111; margin-bottom: 14px;
  }
  .dossier .dos-head h1 { font-size: 24px; letter-spacing: -.02em; }
  .dossier .dos-marca { display: flex; align-items: center; gap: 14px; }
  .dossier .dos-logo { max-width: 130px; max-height: 54px; object-fit: contain; }
  .dossier .dos-emp { text-align: right; font-size: 11px; line-height: 1.5; color: #444; }
  .dossier .dos-emp strong { display: block; font-size: 13px; color: #111; }
  .dossier .dos-emp span { display: block; }
  .dossier .dos-sub { margin: 4px 0 0; color: #444; }
  .dossier .dos-date { color: #444; white-space: nowrap; }
  .dossier .dos-kpis { display: flex; gap: 10px; margin-bottom: 16px; }
  .dossier .dos-kpis > div {
    flex: 1; border: 1px solid #ccc; border-radius: 6px; padding: 8px 10px; text-align: center;
  }
  .dossier .dos-kpis b { display: block; font-size: 17px; }
  .dossier .dos-kpis span { font-size: 10.5px; color: #555; text-transform: uppercase; letter-spacing: .04em; }
  .dossier h2 {
    font-size: 14px; text-transform: uppercase; letter-spacing: .06em; color: #333;
    margin: 0 0 8px; padding-bottom: 4px; border-bottom: 1px solid #ddd;
  }
  .dossier section { margin-bottom: 18px; }
  /* Cada bloque grande, en su hoja: si no, el plano parte por la mitad. */
  .dossier .dos-break { break-before: page; page-break-before: always; }
  /* Fondo BLANCO: el PNG del plano sale con fondo transparente (Konva no pinta el
     del contenedor) y el dossier se genera siempre con la paleta clara. */
  .dossier .dos-img {
    display: block; width: 100%; max-height: 165mm; object-fit: contain;
    border: 1px solid #ddd; border-radius: 6px; background: #fff;
  }
  .dossier table { font-size: 11.5px; margin-top: 0; }
  .dossier td.n, .dossier th.n { text-align: right; }
  .dossier td.na { color: #666; font-style: italic; }
  .dossier tbody tr:nth-child(even) { background: #f6f6f6; }
  .dossier .dos-foot { margin-top: 10px; padding-top: 8px; border-top: 1px solid #ddd; font-size: 10.5px; color: #555; }
  /* Plano de instalaciones: notas de método y comprobaciones del gremio. */
  .dossier .dos-nota { margin: 6px 0 0; font-size: 10.5px; color: #555; line-height: 1.45; }
  .dossier .dos-avisos { margin: 0; padding-left: 18px; font-size: 11.5px; line-height: 1.5; }
  .dossier .dos-avisos li { margin-bottom: 4px; }
  /* Punto de color del circuito, el mismo que su línea en el plano. */
  .dossier .dos-chip {
    display: inline-block; width: 9px; height: 9px; border-radius: 50%;
    margin-right: 6px; vertical-align: middle;
  }
}

/* Los dos botones de impresión del panel de presupuesto, uno al lado del otro. */
.bud-print { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.bud-print .btn { flex: 1 1 auto; }

/* ---------- Presupuesto ---------- */
.room-actions { display: flex; gap: 8px; margin-top: 12px; }
.budget-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin: 6px 0 20px; }
.metric { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 12px; }
.metric .mv { display: block; font-size: 20px; font-weight: 700; }
.metric .mk { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }

.budget-table { width: 100%; border-collapse: collapse; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.budget-table th { text-align: left; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; padding: 10px; border-bottom: 1px solid var(--line); background: var(--panel-2); }
.budget-table td { padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.budget-table td.num { text-align: right; white-space: nowrap; }
.budget-table input, .budget-table select { padding: 6px 8px; background: var(--panel-2); }
.budget-table input[type=number] { text-align: right; }
.budget-table td:nth-child(1) input { width: 130px; }
.budget-table td:nth-child(2) input { width: 100%; min-width: 180px; }
.budget-table td:nth-child(4) input { width: 90px; }
.budget-table td:nth-child(5) input { width: 100px; }

.totals { margin: 16px 0 0; margin-left: auto; width: min(340px, 100%); }
.totals .trow { display: flex; justify-content: space-between; padding: 8px 10px; }
.totals .trow.total { font-size: 18px; font-weight: 700; border-top: 2px solid var(--line); margin-top: 4px; color: var(--accent-2); }

/* Impresión: solo el presupuesto, en claro */
@media print {
  #topbar, .no-print, .budget-table th:last-child, .budget-table td:last-child { display: none !important; }
  body { background: #fff; color: #000; }
  .page.budget { max-width: none; padding: 0; }
  .metric, .budget-table, .budget-table th, .totals .trow.total { background: #fff !important; color: #000 !important; border-color: #ccc !important; }
  .budget-table input, .budget-table select { border: none !important; background: #fff !important; color: #000 !important; -webkit-appearance: none; appearance: none; }
  .metric .mk, .muted { color: #444 !important; }
}


/* ---------- Paleta de comandos (Ctrl+K) ----------
   Un solo sitio para buscar y ejecutar cualquier cosa. En una app con 37 tipos de
   pieza y mil acabados, escribir gana siempre a buscar con el ratón. */
/* Disparador de la paleta en la barra superior: parece un buscador porque es lo
   que la gente busca con la vista, y enseña el atajo. */
.cmdk-trigger {
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 10px;
  min-width: 210px; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 999px; color: var(--muted); cursor: pointer; font: inherit; font-size: 12.5px;
  transition: border-color .14s var(--ease), color .14s var(--ease);
}
.cmdk-trigger:hover { border-color: var(--accent-line); color: var(--text-2); }
.cmdk-trigger span { flex: 1; text-align: left; }
.cmdk-trigger kbd {
  padding: 1px 6px; border: 1px solid var(--line); border-radius: 5px;
  background: var(--panel); font: inherit; font-size: 11px; color: var(--muted);
}
@media (max-width: 900px) { .cmdk-trigger { min-width: 0; } .cmdk-trigger span, .cmdk-trigger kbd { display: none; } }

.cmdk-back {
  position: fixed; inset: 0; z-index: 400; background: rgba(6,9,16,.55);
  backdrop-filter: blur(4px); display: flex; justify-content: center;
  align-items: flex-start; padding: 12vh 16px 16px;
  animation: fade-in .12s var(--ease);
}
:root[data-theme="claro"] .cmdk-back { background: rgba(30,40,60,.34); }
.cmdk {
  width: min(620px, 100%); max-height: 66vh; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); overflow: hidden; animation: modal-in .16s var(--ease);
}
.cmdk-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
.cmdk-head .ic { color: var(--muted); }
.cmdk-input {
  flex: 1; padding: 0; border: none; background: transparent; color: var(--text);
  font-size: 16px; outline: none;
}
.cmdk-input::placeholder { color: var(--muted); }
.cmdk-list { flex: 1; overflow-y: auto; padding: 6px; }
.cmdk-group {
  padding: 10px 10px 4px; font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.cmdk-item {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--r-sm);
  color: var(--text-2); cursor: pointer; font-size: 13.5px;
}
.cmdk-item .ic { color: var(--muted); }
.cmdk-item.sel { background: var(--accent); color: var(--accent-ink); }
.cmdk-item.sel .ic, .cmdk-item.sel .cmdk-hint { color: rgba(255,255,255,.82); }
.cmdk-lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdk-hint { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.cmdk-empty { padding: 26px; text-align: center; color: var(--muted); }
.cmdk-foot {
  display: flex; gap: 16px; padding: 8px 14px; border-top: 1px solid var(--line);
  background: var(--panel-2); font-size: 11.5px; color: var(--muted);
}
.cmdk-foot kbd {
  padding: 1px 5px; margin-right: 4px; border: 1px solid var(--line); border-radius: 4px;
  background: var(--panel); font: inherit; font-size: 11px;
}


/* ---------- Revisión del plano ----------
   El contador vive en la barra y el detalle en un panel flotante: un aviso que
   hay que ir a buscar a una pestaña no lo lee nadie. */
.toolbar .check .check-n {
  min-width: 17px; padding: 0 5px; border-radius: 999px; font-size: 11px; font-weight: 700;
  background: var(--panel-3); color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center;   /* el «sin avisos» es un SVG */
}
.toolbar .check .check-n svg { display: block; }
.toolbar .check.has-error { color: #ffc3c8; border-color: rgba(255,95,109,.5); background: rgba(255,95,109,.14); }
.toolbar .check.has-error .check-n { background: var(--danger); color: var(--danger-ink); }
.toolbar .check.has-warn { color: #ffd9a0; border-color: rgba(255,181,71,.45); background: rgba(255,181,71,.12); }
.toolbar .check.has-warn .check-n { background: var(--warn); color: #241800; }
.toolbar .check.all-ok .check-n { background: rgba(47,191,140,.2); color: var(--accent-2); }

.revpanel {
  position: fixed; z-index: 260; width: min(390px, 94vw); max-height: 70vh;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); animation: menu-in .14s var(--ease);
}
.rev-head { display: flex; align-items: center; gap: 8px; padding: 11px 12px; border-bottom: 1px solid var(--line); flex: none; }
/* TODO lo que no es la cabecera rueda junto. Antes scrolleaba solo la lista de avisos y
   lo de debajo —botón de corregir, pie y repaso del jefe de obra— se salía del panel y
   quedaba cortado, sin manera de llegar a ello. `min-height: 0` es imprescindible: sin
   él un hijo flexible no baja de su contenido y no aparece la barra. */
.rev-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.rev-head .ic { color: var(--accent); }
.rev-sum { margin: 10px 12px 4px; }
/* --- ALZADOS Y DESPIECE --------------------------------------------------
   La pared vista de frente. La pieza CORTADA se distingue de la entera con un relleno
   más apagado: de un vistazo se ve dónde queda el corte, que es lo único que se mira
   en un despiece. */
.modal-alz { width: min(680px, 96vw); max-height: 92vh; display: flex; flex-direction: column; }
.alz-cuerpo { flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 6px -4px 0; padding: 0 4px; }
.alz-bloque { margin-bottom: 18px; }
.alz-cab { display: flex; align-items: baseline; gap: 10px; justify-content: space-between; margin-bottom: 4px; }
/* Tamaño REAL del SVG (no 100%): así todos los alzados de una estancia salen a la misma
   escala y se pueden comparar de un vistazo, que es para lo que se miran juntos. */
.alz { display: block; max-width: 100%; background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-sm); }
.alz-pieza { fill: var(--accent-soft, rgba(90,150,255,.14)); stroke: var(--line); stroke-width: .6; }
/* La CORTADA se distingue por relleno, no solo por el borde: en un despiece lo único
   que se busca es dónde queda el corte. */
.alz-pieza.cortada { fill: var(--warn-soft, rgba(230,170,60,.22)); stroke: var(--warn); stroke-width: .5; }
.alz-hueco { fill: var(--panel); stroke: var(--muted); stroke-width: 1.2; }
.alz-hueco-t { fill: var(--muted); font-size: 9px; }
.alz-muro { fill: none; stroke: var(--text-2); stroke-width: 1.6; }
.alz-punto { fill: var(--warn); stroke: var(--panel); stroke-width: 1; }
.alz-punto.toma_agua, .alz-punto.desague, .alz-punto.llave_paso, .alz-punto.toma_lavadora { fill: var(--accent-2); }
.alz-punto-t { fill: var(--muted); font-size: 8px; }
/* El mecanismo que cae mal en el alicatado: se ve en el dibujo Y se explica debajo.
   Solo en el dibujo no se entiende qué le pasa; solo en el texto no se sabe cuál es. */
.alz-punto.mal { fill: var(--danger); stroke: var(--panel); stroke-width: 1.4; }
.alz-avisos { margin-top: 5px; display: flex; flex-direction: column; gap: 4px; }
.alz-aviso {
  display: flex; gap: 7px; align-items: flex-start; padding: 6px 9px; font-size: 12px;
  line-height: 1.45; color: var(--text-2);
  background: var(--warn-soft, rgba(230,170,60,.12));
  border: 1px solid var(--warn); border-radius: var(--r-sm);
}
.alz-aviso .ic { flex: none; color: var(--warn); margin-top: 2px; }
.alz-cota { stroke: var(--muted); stroke-width: .8; }
.alz-cota-t { fill: var(--muted); font-size: 9px; }
.alz-datos { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 5px; font-size: 12px; }
.alz-dato { white-space: nowrap; }

/* MATERIAL A PEDIR dentro del presupuesto: se distingue de las partidas porque NO suma
   al total — es lo que pagas, no lo que cobras. Borde discontinuo y fondo apagado. */
.bud-pedido:not(:empty) {
  margin: 8px 0; padding: 2px 0 6px;
  border-top: 1px dashed var(--line); border-bottom: 1px dashed var(--line);
}
.bud-row.bud-ped .bud-sum { color: var(--text-2); font-weight: 500; }
.bud-ped-nota { margin: -2px 0 6px; padding-left: 2px; line-height: 1.35; }

.alz-compra { margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--line); }
.alz-compra h4 { margin: 0 0 3px; font-size: 14px; }
.alz-mat { padding: 9px 11px; margin-top: 8px; background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-sm); }
/* La cadena m² pared → con cortes → con merma, en una línea: enseñar SOLO el número
   final no convence a nadie de que hay que pedir más de lo que mide la pared. */
.alz-linea { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin: 4px 0 6px; font-size: 12px; color: var(--muted); }
.alz-fl { opacity: .55; }
.alz-cajas { display: flex; align-items: baseline; gap: 6px; }
.alz-cajas strong { font-size: 15px; color: var(--accent); }

/* ALTURA LIBRE de la estancia, encima de los acabados: de ella salen los m² de pared,
   así que se enseña el resultado al lado del campo — si no, parece un dato de ficha. */
.alt-fila {
  margin: 0 0 10px; padding: 8px 10px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-sm);
}
.alt-linea { display: flex; align-items: center; gap: 8px; }
.alt-lbl { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--text); }
.alt-lbl .ic { color: var(--accent); }
.alt-in {
  width: 68px; padding: 4px 7px; font: inherit; font-size: 13px; text-align: right;
  color: var(--text); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-sm);
}
.alt-in:focus { outline: none; border-color: var(--accent); }
.alt-in:disabled { opacity: .6; }
/* En su propia línea y con permiso para partirse: es el dato que explica PARA QUÉ
   sirve el campo, y era justo el que se cortaba. */
.alt-m2 { margin-top: 5px; line-height: 1.35; }

.rev-ia { width: calc(100% - 16px); margin: 2px 8px 6px; justify-content: center; }
.rev-foot { margin: 4px 12px 10px; opacity: .8; }
/* El botón de repaso dentro de la burbuja del asistente: separado del texto, no pegado. */
.asis-fix { margin-top: 8px; }

/* EL REPASO DEL JEFE DE OBRA: la otra mitad de «revisar». Arriba lo que está mal
   dibujado; aquí lo que NO ESTÁ. Va separado por una línea y con el fondo un punto
   más apagado, porque no es una comprobación instantánea: se pide, tarda y cuesta. */
.rev-repaso { margin: 0 8px 10px; padding: 10px; background: var(--panel-2); border: 1px solid var(--line-soft); border-radius: var(--r-sm); }
.rev-rep-cab { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; color: var(--text); }
.rev-rep-cab .ic { color: var(--accent); }
.rev-repaso > p { margin: 0 0 8px; font-size: 12px; line-height: 1.45; }
.rev-rep-btn { width: 100%; justify-content: center; }
.rev-rep-esp { animation: emp-lat 1.2s ease-in-out infinite; }
.rev-rep-err { color: var(--danger); }
.rev-rep-ok { color: var(--ok, var(--accent-2)); }
.rev-rep-item {
  padding: 8px 9px; margin-bottom: 5px; border-radius: var(--r-sm);
  background: var(--panel); border: 1px solid var(--line-soft); border-left: 3px solid var(--muted);
}
/* El color va por CERTEZA, no por importe: lo que decide si esto te para antes de
   enviar el presupuesto es si va a pasar, no cuántos euros son. */
.rev-rep-item.seguro { border-left-color: var(--danger); }
.rev-rep-item.probable { border-left-color: var(--warn); }
.rev-rep-item.preguntar { border-left-color: var(--accent); }
.rev-rep-tit { display: flex; align-items: baseline; gap: 8px; justify-content: space-between; }
.rev-rep-tit strong { font-size: 12.5px; color: var(--text); }
.rev-rep-cert { flex: none; font-size: 10.5px; color: var(--muted); white-space: nowrap; }
.rev-rep-item p { margin: 3px 0 6px; font-size: 12px; line-height: 1.45; color: var(--muted); }
/* El nombre de la partida viene de la IA: largo imprevisible. Se parte por palabras y,
   si aun así se pasa de tres líneas, se corta con puntos suspensivos — el título
   completo sigue en el `title` del botón y en el propio presupuesto. */
.rev-rep-part {
  margin: 0 0 6px !important; font-size: 11.5px; line-height: 1.4; color: var(--text-2);
  overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.rev-rep-item .btn { width: 100%; justify-content: center; }
/* YA EN EL PRESUPUESTO: se queda a la vista pero apagada y al final de la lista. No se
   borra del repaso a propósito — saber lo que YA has cubierto es la mitad de un
   repaso—, pero tiene que distinguirse de un vistazo de lo que falta por hacer. */
.rev-rep-item.hecha { opacity: .62; border-left-color: var(--ok, var(--accent-2)); }
.rev-rep-cert.ok { display: inline-flex; align-items: center; gap: 4px; color: var(--ok, var(--accent-2)); }
.rev-rep-quitar {
  display: flex; align-items: center; gap: 6px; width: 100%; justify-content: center;
  padding: 5px 8px; font: inherit; font-size: 11.5px; color: var(--muted);
  background: none; border: 1px dashed var(--line); border-radius: var(--r-sm); cursor: pointer;
}
.rev-rep-quitar:hover { color: var(--text-2); border-color: var(--accent-line, var(--accent)); }
/* Ya no lleva scroll propio: rueda con `.rev-scroll`. Dos barras anidadas en un panel
   de 390 px es la forma más rápida de que no se encuentre ninguna de las dos. */
.rev-list { padding: 4px 8px; }
.rev-item {
  display: flex; gap: 9px; align-items: flex-start; width: 100%; padding: 9px 10px; margin-bottom: 4px;
  background: var(--panel-2); border: 1px solid var(--line-soft); border-left: 3px solid var(--muted);
  border-radius: var(--r-sm); color: var(--text-2);
  transition: border-color .14s var(--ease), background .14s var(--ease);
}
.rev-item:hover { background: var(--panel-3); }
/* El aviso es una COLUMNA: el texto arriba y, si la propuesta sabe arreglarlo, su
   botón debajo. El texto sigue siendo un botón (lleva al plano), así que el arreglo
   no puede ir dentro de él —un botón dentro de otro no es HTML válido y el clic se
   iría al de fuera—. */
.rev-body { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; }
.rev-txt {
  width: 100%; min-width: 0; text-align: left; padding: 0; background: none; border: none;
  color: inherit; cursor: pointer; font: inherit;
}
/* El arreglo se ve SIEMPRE, no al pasar por encima como silenciar: es la acción
   principal del aviso, y una acción que hay que descubrir moviendo el ratón no la
   encuentra quien no sabe que existe. */
.rev-fix { margin-top: 8px; }
.rev-fix-btn { border-color: var(--accent-line); color: var(--text); }
.rev-fix-btn .ic { color: var(--accent); }
.rev-fix-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
/* El botón de silenciar aparece al pasar por encima: es una salida, no una acción
   de todos los días. */
.rev-mute { opacity: 0; flex: 0 0 auto; }
.rev-item:hover .rev-mute, .rev-mute:focus-visible { opacity: 1; }
.rev-mudos {
  display: flex; align-items: center; gap: 7px; width: calc(100% - 16px); margin: 2px 8px 0;
  padding: 8px 10px; background: transparent; border: 1px dashed var(--line);
  border-radius: var(--r-sm); color: var(--muted); cursor: pointer; font: inherit; font-size: 12px;
}
.rev-mudos:hover { color: var(--text-2); border-color: var(--accent-line); }
.rev-ok .btn { margin-top: 8px; }
.rev-item strong { display: block; font-size: 13px; color: var(--text); }
.rev-item p { margin: 2px 0 0; font-size: 12px; line-height: 1.45; color: var(--muted); }
.rev-item.error { border-left-color: var(--danger); }
.rev-item.error .ic { color: var(--danger); }
.rev-item.aviso { border-left-color: var(--warn); }
.rev-item.aviso .ic { color: var(--warn); }
.rev-ok { display: flex; align-items: center; gap: 12px; padding: 18px 14px 20px; }
.rev-ok .ic { color: var(--accent-2); flex: 0 0 auto; }
.rev-ok p { margin: 3px 0 0; }


/* ---------- Modo presentación ----------
   Lo que ve el CLIENTE. Nada de mandos de edición: cifras grandes, el plano, la 3D
   en vivo, el antes/después y el total. */
.present {
  position: fixed; inset: 0; z-index: 300; display: flex; flex-direction: column;
  background: var(--bg); animation: fade-in .16s var(--ease);
}
.pres-bar {
  display: flex; align-items: center; gap: 10px; padding: 10px 16px;
  background: var(--panel); border-bottom: 1px solid var(--line);
}
.pres-bar .ic { color: var(--accent); }
.pres-tit { font-size: 12.5px; color: var(--muted); }
.pres-dots { display: flex; gap: 6px; margin: 0 6px; }
.pres-dot {
  width: 9px; height: 9px; padding: 0; border-radius: 50%; cursor: pointer;
  background: var(--panel-3); border: 1px solid var(--line);
}
.pres-dot.on { background: var(--accent); border-color: var(--accent); }
.pres-body { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; padding: 26px; }

.pres-cover { display: flex; flex-direction: column; gap: 34px; width: min(980px, 100%); }
.pres-eyebrow { margin: 0 0 6px; color: var(--accent); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; }
.pres-cover h1 { margin: 0; font-size: clamp(34px, 6vw, 62px); line-height: 1.05; letter-spacing: -.03em; }
.pres-sub { margin: 10px 0 0; color: var(--muted); font-size: 16px; }
.pres-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
/* La cifra de portada, con el FILETE DE MARCA en el canto izquierdo. Es exactamente
   el mismo recurso que lleva el KPI del dossier en PDF (`DocPdf::kpis`), y a
   propósito: la presentación y el PDF son las dos cosas que ve el cliente, y si la
   pantalla y el papel no se parecen, parecen de dos sitios distintos. */
.pres-kpi {
  padding: 18px 20px; background: var(--panel); border: 1px solid var(--line);
  border-left: 3px solid var(--accent); border-radius: var(--r-lg);
}
.pres-kpi strong { display: block; font-size: 25px; letter-spacing: -.02em; }
.pres-kpi span { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }

.pres-figura { display: flex; flex-direction: column; gap: 12px; align-items: center; width: 100%; height: 100%; }
.pres-img {
  flex: 1; min-height: 0; max-width: 100%; object-fit: contain;
  background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg);
}
.pres-3d {
  flex: 1; width: min(1200px, 100%); min-height: 0; position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--canvas-bg);
}
.pres-3d canvas { display: block; width: 100%; height: 100%; }
.pres-pie { margin: 0; color: var(--muted); font-size: 12.5px; }

.pres-panel { width: min(940px, 100%); }
.pres-panel h2 { margin: 0 0 18px; font-size: 26px; letter-spacing: -.02em; }
.pres-mats { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.pres-mat {
  display: flex; gap: 14px; align-items: center; padding: 12px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
}
.pres-mat-img {
  width: 74px; height: 74px; flex: 0 0 auto; border-radius: var(--r); background-size: cover;
  background-position: center; border: 1px solid var(--line); display: grid; place-items: center; color: var(--muted);
}
.pres-mat-sup { margin: 0 0 2px; font-size: 11.5px; color: var(--accent); text-transform: uppercase; letter-spacing: .06em; }
.pres-mat strong { font-size: 15px; }
.pres-mat p:last-child { margin: 3px 0 0; }

.pres-lineas { display: flex; flex-direction: column; gap: 2px; margin-bottom: 22px; }
.pres-linea {
  display: flex; justify-content: space-between; padding: 13px 4px;
  border-bottom: 1px solid var(--line-soft); font-size: 16px;
}
.pres-total {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 20px;
  padding-top: 16px; border-top: 2px solid var(--text);
}
.pres-total-n { margin: 2px 0 0; font-size: clamp(30px, 5vw, 46px); font-weight: 700; letter-spacing: -.03em; }

@media (max-width: 700px) {
  .pres-body { padding: 14px; }
  .pres-bar .pres-tit { display: none; }
}


/* Ajustes de la empresa */
.modal-empresa { width: min(560px, 96vw); }
/* ---------- Recorridos simulados ---------- */
/* LA ADVERTENCIA. Va con el color de AVISO y no con el de acento, y se queda puesta:
   unos muñecos andando por una tienda se leen como una previsión de ventas, y no lo
   son. Quien enseña la pantalla en una reunión no es quien la encendió, así que decirlo
   una vez al pulsar no sirve — tiene que estar ahí cada vez que se mira. */
.rec-aviso {
  display: flex; gap: 9px; align-items: flex-start;
  margin: 4px 0 14px; padding: 10px 12px;
  border: 1px solid var(--warn); border-left-width: 3px;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--warn) 9%, transparent);
  font-size: 12px; line-height: 1.5; color: var(--text-2);
}
.rec-aviso svg { flex: none; margin-top: 1px; color: var(--warn); }
.rec-aviso b { color: var(--text); }
/* «tapa»: la chapa del mueble que pasa de la altura de ojo. Es la explicación más
   frecuente de un mueble por el que no pasa nadie, así que va pegada a su nombre. */
.rec-tapa {
  margin-left: 6px; padding: 1px 5px; border-radius: 4px;
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  color: var(--danger); font-size: 9.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
}

/* COLOR DE MARCA: la muestra y el hexadecimal, juntos. Los dos hacen falta y no es
   redundancia — el selector es para quien elige a ojo, el campo de texto para quien
   llega con el código del manual de marca, que es lo que ocurre siempre con una
   cadena grande. Se escriben el uno al otro, así que no hay un «cuál manda». */
.brand-ct { margin: 6px 0 0; font-size: 12px; line-height: 1.4; color: var(--muted); }
.brand-ct.flojo { color: var(--warn); }
.brand-fld { display: flex; gap: 8px; align-items: center; }
.brand-col {
  width: 42px; height: 34px; flex: none; padding: 2px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--panel-2); cursor: pointer;
}
/* Sin esto Chrome deja un marco gris alrededor de la muestra y parece deshabilitada. */
.brand-col::-webkit-color-swatch { border: 0; border-radius: 4px; }
.brand-col::-webkit-color-swatch-wrapper { padding: 0; }
.brand-txt {
  width: 110px; flex: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  text-transform: uppercase;
}

.emp-logo { display: flex; gap: 14px; align-items: center; margin: 12px 0 4px; }
.emp-logo > div { display: flex; flex-direction: column; gap: 6px; }
.logo-prev {
  width: 96px; height: 64px; flex: 0 0 auto; display: grid; place-items: center;
  border: 1px dashed var(--line); border-radius: var(--r); color: var(--muted);
  background-size: contain; background-position: center; background-repeat: no-repeat;
  background-color: var(--panel-2);
}
.modal-form textarea {
  width: 100%; resize: vertical; padding: 9px 11px; font: inherit; font-size: 13.5px;
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line); border-radius: var(--r);
}
.pres-logo { display: block; max-width: 220px; max-height: 70px; object-fit: contain; margin-bottom: 6px; }
.pres-marca { margin: 0 0 4px; font-weight: 700; letter-spacing: .02em; }

/* ---------- Asistente del plano (chat) ----------
   Cajón a la derecha, sobre el lienzo: se habla con él mientras se ve el plano
   cambiar detrás. */
.asis {
  /* `bottom` cuenta con la BARRA DE ESTADO (`.hintbar`, 28 px + borde): a 14 px la
     pastilla plegada se sentaba encima de «Área … · Esc 1:25». Lo que va fijo al
     viewport tiene que descontar lo que ya vive pegado al borde. */
  position: fixed; right: 14px; bottom: 42px; z-index: 240; width: min(390px, calc(100vw - 28px));
  max-height: min(620px, calc(100vh - 118px)); display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); overflow: hidden; animation: modal-in .18s var(--ease);
}
.asis-cab { display: flex; align-items: center; gap: 8px; padding: 11px 12px; border-bottom: 1px solid var(--line); }
.asis-cab .ic { color: var(--accent); }
.asis-chapa { display: none; }

/* PLEGADO: la pastilla en reposo. Ocupa lo que un botón y deja el plano libre, pero el
   hilo entero sigue montado debajo — replegar no es cerrar, y la IA conserva el
   contexto de lo que lleváis hablado. Es además el ÚNICO acceso: por eso está siempre
   a la vista, que un botón de texto perdido entre otros seis se ve menos. */
.asis.plegado {
  width: auto; max-height: none; border-radius: 999px;
  box-shadow: var(--sh-2); cursor: pointer;
  transition: border-color .14s var(--ease), background .14s var(--ease);
}
.asis.plegado:hover { border-color: var(--accent); background: var(--panel-2); }
.asis.plegado .asis-hilo, .asis.plegado .asis-pie, .asis.plegado .asis-plegar,
.asis.plegado .spacer, .asis.plegado .asis-tit { display: none; }
.asis.plegado .asis-cab { border-bottom: 0; padding: 9px 15px 9px 13px; }
.asis.plegado .asis-chapa { display: inline; font-size: 13px; font-weight: 600; color: var(--text); }
/* El punto que dice que hay conversación esperando: sin él, replegar y olvidarse de
   que la tienes ahí es lo más fácil del mundo. */
.asis.plegado.con-hilo .asis-chapa::after {
  content: ''; display: inline-block; width: 6px; height: 6px; margin-left: 7px;
  border-radius: 50%; background: var(--accent); vertical-align: middle;
}
.asis-hilo { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.asis-msg { max-width: 92%; padding: 9px 12px; border-radius: 12px; font-size: 13.5px; line-height: 1.5; }
.asis-msg p { margin: 0; }
.asis-msg.yo { align-self: flex-end; background: var(--accent); color: var(--accent-ink); border-bottom-right-radius: 4px; }
.asis-msg.ia { align-self: flex-start; background: var(--panel-2); border: 1px solid var(--line); border-bottom-left-radius: 4px; }
.asis-msg.error { align-self: flex-start; background: rgba(255,95,109,.14); border: 1px solid rgba(255,95,109,.45); color: #ffb3ba; }
.asis-msg.pensando { opacity: .7; }
.asis-extra { margin-top: 6px !important; font-size: 12px; color: var(--muted); }
.asis-msg.yo .asis-extra { color: rgba(255,255,255,.8); }
.asis-pie { display: flex; gap: 8px; align-items: flex-end; padding: 10px 12px; border-top: 1px solid var(--line); background: var(--panel-2); }
.asis-campo {
  flex: 1; resize: none; padding: 8px 10px; font: inherit; font-size: 13.5px; line-height: 1.4;
  background: var(--panel); color: var(--text); border: 1px solid var(--line); border-radius: var(--r);
}
.asis-campo:focus { outline: none; border-color: var(--accent); }
.asis-pie .btn { height: 38px; }
@media (max-width: 700px) {
  .asis { left: 8px; right: 8px; bottom: 36px; width: auto; }
  /* Plegado en móvil NO se estira de lado a lado: es una pastilla, no una barra. */
  .asis.plegado { left: auto; right: 8px; width: auto; }
}

/* ---------- Multiempresa: chapa de empresa y aviso de soporte ---------- */
.empresa-chip {
  padding: 3px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel-2); color: var(--muted); font-size: 12px; white-space: nowrap;
}
.empresa-chip.soporte { border-color: var(--accent); color: var(--accent); }
/* El aviso de suplantación va FIJO y en un color que no se confunde con nada:
   estar dentro de los datos de un cliente no puede pasar desapercibido. */
/* El parche de `[hidden]` que había aquí ya no hace falta: lo cubre la regla global de
   arriba (buscar «`hidden` GANA SIEMPRE»), que resolvió la familia entera. */
.impersonando {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 7px 14px; font-size: 13px;
  background: #7a4a12; color: #ffe9c7; border-bottom: 1px solid #a9691c;
}
:root[data-theme="claro"] .impersonando { background: #fde8c8; color: #7a4a12; border-bottom-color: #e6c48b; }
.impersonando .btn { background: rgba(255,255,255,.14); border-color: currentColor; color: inherit; }

/* ---------- Tablas de gestión (usuarios, empresas, tarifa) ---------- */
.tabla-cont { overflow-x: auto; margin-bottom: 18px; }
.tabla { width: 100%; border-collapse: collapse; font-size: 14px; }
.tabla th, .tabla td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--line-soft); }
.tabla th { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 600; }
.tabla tbody tr:hover { background: var(--panel-2); }
.tabla tr.inactivo td { opacity: .55; }
/* Los mandos, en LÍNEA: `.icon-btn` es `display:grid` y en una celda se apilaban
   uno debajo de otro, dejando filas de tres pisos. */
.tabla td.acciones { text-align: right; white-space: nowrap; }
.tabla td.acciones { display: flex; gap: 4px; justify-content: flex-end; align-items: center; }
.tabla td.acciones .icon-btn { display: inline-grid; }
.tabla td .badge { margin-top: 0; }
.tabla td strong + .badge, .tabla td .badge + .badge { margin-left: 6px; }
.tabla.compacta th, .tabla.compacta td { padding: 5px 10px; font-size: 12.5px; }
.badge.warn { border-color: #b4741d; color: #e0a44a; }
.badge.ok { border-color: var(--accent); color: var(--accent); }
.sec-title { margin: 22px 0 10px; font-size: 15px; display: flex; align-items: center; gap: 8px; }
/* Un aviso que se queda en la pantalla, no un toast: lo de «el correo no sale» hay que
   poder leerlo con calma y seguir ahí cuando vuelvas. */
.aviso-error {
  margin: 0 0 12px; padding: 10px 13px; border-radius: var(--r); font-size: 13px;
  color: var(--danger); background: rgba(255,95,109,.10); border: 1px solid var(--danger);
}
/* El texto que escribe el interesado puede ser largo: que no estire la tabla entera. */
.celda-larga { max-width: 34em; }

.kpis { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; }
.kpi {
  flex: 1 1 130px; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 12px 14px;
}
.kpi b { display: block; font-size: 22px; line-height: 1.1; }
.kpi span { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }

/* ---------- Tarifa de la empresa ---------- */
.tarifa-grid { display: grid; grid-template-columns: 1fr auto; gap: 8px 14px; align-items: center; }
.tarifa-grid input { width: 110px; text-align: right; }
.tarifa-sec { margin: 16px 0 8px; font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
@media (max-width: 700px) {
  .tabla th:nth-child(2), .tabla td:nth-child(2) { display: none; }
}

/* ---------- Mi catálogo (materiales de la empresa) ----------
   OJO con los nombres: `.mat-*` ya es del selector de acabados del editor, y
   reutilizarlo aquí convertía la ficha en una rejilla de dos columnas con la
   muestra de color hecha una tira. Prefijo propio, `cat-`. */
.cat-grid { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.cat-card { display: block; padding: 0; overflow: hidden; }
.cat-muestra { height: 96px; background-size: cover; background-position: center; border-bottom: 1px solid var(--line); }
.cat-info { padding: 12px 14px 14px; }
.cat-info h3 { margin: 0 0 2px; font-size: 15px; }
.cat-info .badge { margin: 8px 6px 0 0; }
/* Estado vacío con salida: una pantalla en blanco no dice qué hacer. */
.vacio {
  text-align: center; padding: 46px 20px; border: 1px dashed var(--line);
  border-radius: var(--r-lg); background: var(--panel);
}
.vacio h3 { margin: 0 0 6px; }
.vacio p { max-width: 460px; margin: 0 auto 16px; }
.tarifa-els { max-height: 260px; overflow-y: auto; padding-right: 4px; }
/* Un precio PROPIO se distingue del orientativo del catálogo de un vistazo. */
.tarifa-els input.propio { border-color: var(--accent); }
#whoami.clickable { cursor: pointer; }
#whoami.clickable:hover { color: var(--text); text-decoration: underline; }

/* ---------- Plano de distribución de la vivienda ---------- */
.page-ancha { max-width: 1400px; }
.dist-lienzo {
  height: 560px; background: var(--canvas-bg); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden; margin-bottom: 10px;
}

/* ---------- Vista de obra (a pie de tajo) ----------
   Dedos con guantes y pantalla al sol: casillas grandes, contraste alto y nada
   que se pueda tocar sin querer. */
.obra-progreso {
  position: relative; height: 30px; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 999px; overflow: hidden; margin-bottom: 18px;
}
.obra-barra { height: 100%; width: 0; background: var(--accent); transition: width .25s var(--ease); }
.obra-progreso.completo .obra-barra { background: var(--accent-2); }
.obra-pct {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 13px; font-weight: 600; color: var(--text);
}
.obra-cols { display: grid; grid-template-columns: 1fr 400px; gap: 22px; align-items: start; }
.obra-mini { position: sticky; top: 70px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 12px; }
.obra-mini .plan-prev { width: 100%; height: 260px; color: var(--text-2); }
.obra-tarea {
  display: flex; align-items: center; gap: 14px; padding: 13px 14px; margin: 0 0 8px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  cursor: pointer; user-select: none;
}
.obra-tarea:hover { border-color: var(--accent-line); }
.obra-tarea input { width: 26px; height: 26px; flex: none; accent-color: var(--accent-2); cursor: pointer; }
.obra-txt { flex: 1; }
.obra-tarea.hecha { background: var(--panel-2); }
.obra-tarea.hecha .obra-txt { text-decoration: line-through; opacity: .6; }
.obra-firma { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
@media (max-width: 900px) {
  .obra-cols { grid-template-columns: 1fr; }
  .obra-mini { position: static; order: -1; }
}

/* ---------- Editor en MÓVIL: los laterales pasan a ser cajones ----------
   Con paleta y panel fijos, en 400 px el plano se quedaba en una tira de 120 px.
   Aquí se abren por encima del lienzo, con velo, y se cierran al tocar fuera o al
   elegir una pieza. La barra inferior es lo único que ocupa sitio siempre. */
.movil-bar { display: none; }
.cajon-velo { display: none; }
@media (max-width: 760px) {
  /* `overflow: hidden` no es cosmético: el cajón derecho, aparcado en
     `translateX(102%)`, asomaba fuera y dejaba la página con scroll horizontal. */
  .editor-body { position: relative; overflow: hidden; }
  /* El lienzo pasa a ocupar TODO: los laterales ya no reservan sitio. */
  .editor-body > .canvas { flex: 1 1 100%; width: 100%; }
  .palette, .right {
    position: absolute; top: 0; bottom: 0; z-index: 12;
    width: min(78vw, 300px); max-width: none;
    transform: translateX(-102%); transition: transform .2s var(--ease);
    box-shadow: var(--sh-3);
  }
  .palette { left: 0; }
  .right { right: 0; transform: translateX(102%); }
  .palette.abierto, .right.abierto { transform: translateX(0); }
  .palette .pal-grid { grid-template-columns: 1fr 1fr; }
  .cajon-velo {
    display: block; position: absolute; inset: 0; z-index: 11;
    background: rgba(0,0,0,.45); opacity: 0; pointer-events: none;
    transition: opacity .2s var(--ease);
  }
  .cajon-velo.visible { opacity: 1; pointer-events: auto; }
  /* Barra inferior: los dos accesos, con área de toque de verdad. */
  .movil-bar {
    display: flex; gap: 8px; padding: 8px 10px;
    background: var(--panel); border-top: 1px solid var(--line);
  }
  .movil-bar .btn { flex: 1; justify-content: center; height: 42px; }
  /* La ayuda de abajo se come una línea entera de una pantalla pequeña. */
  .hintbar { display: none; }
  /* La barra de herramientas ocupaba TRES filas de las nueve que caben. Fuera lo
     informativo (escala, área) y las etiquetas largas: los iconos se entienden. */
  #ed-zoom, .toolbar .rejilla-lbl, .toolbar .area-lbl { display: none; }
  .toolbar { gap: 4px; padding: 5px 7px; }
  .toolbar .btn.small { padding: 5px 7px; }
}

/* ---------- Desplegable «Ver» (estado de la vista) ----------
   Cuelga de su botón en la barra. Nada flota sobre el plano: aparece donde lo
   pides y se va al soltar. */
.ver-btn.activo { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }
.ver-btn.con-ocultas { border-color: var(--warn); }
.ver-punto {
  display: inline-grid; place-items: center; min-width: 16px; height: 16px; padding: 0 4px;
  margin-left: 2px; border-radius: 999px; background: var(--warn); color: #201400;
  font-size: 10.5px; font-weight: 700; line-height: 1;
}
.pop {
  position: fixed; z-index: 300; width: 268px; padding: 12px 14px 14px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); animation: menu-in .12s var(--ease);
}
.pop-tit {
  margin: 0 0 6px; font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.pop-sep { height: 1px; background: var(--line); margin: 14px -14px 12px; }
/* Cada fila: etiqueta a la izquierda, mando a la derecha, alineados en columna.
   Es lo que hace que una lista de ocho controles se lea como una lista y no como
   un montón de casillas sueltas. */
.pop-fila {
  display: flex; align-items: center; gap: 10px; min-height: 30px;
  margin: 0; padding: 0; cursor: pointer; user-select: none;
}
.pop-lbl { flex: 1; font-size: 13.5px; color: var(--text); }
.pop-fila select { width: 96px; padding: 4px 7px; }
/* Marca de «capa activa»: solo se ve al pasar el ratón o cuando está puesta. */
.pop-activa {
  display: grid; place-items: center; width: 22px; height: 22px; padding: 0;
  background: none; border: 0; border-radius: var(--r-sm); color: var(--muted);
  opacity: 0; cursor: pointer; transition: opacity .12s var(--ease);
}
.pop-fila:hover .pop-activa { opacity: .6; }
.pop-activa.on, .pop-activa:hover { opacity: 1; color: var(--accent); }

/* Interruptor. Un `checkbox` de sistema en una lista de ocho se ve a rayas; esto
   da una columna limpia y un objetivo de toque decente. */
/* OJO con la especificidad: las filas de capa conservan `.layer-row` (los tests
   las buscan así) y `.layer-row span { flex: 1 }` estiraba el interruptor hasta
   los 98 px. Por eso el selector va prefijado con `.pop`. */
.pop .sw, .sw { position: relative; display: inline-block; width: 34px; height: 20px; flex: 0 0 34px; }
.sw-in { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.sw-track {
  position: absolute; inset: 0; border-radius: 999px; background: var(--panel-3);
  border: 1px solid var(--line); transition: background .15s var(--ease), border-color .15s var(--ease);
}
.sw-track::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--muted); transition: transform .15s var(--ease), background .15s var(--ease);
}
.sw-in:checked + .sw-track { background: var(--accent-soft); border-color: var(--accent-line); }
.sw-in:checked + .sw-track::after { transform: translateX(14px); background: var(--accent); }
.sw-in:focus-visible + .sw-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.pop .ver-atajo { width: 100%; justify-content: center; margin-top: 10px; }
.pop .bg-controls { border: 0; padding: 0; margin: 10px 0 0; }
.pop .bg-controls p { margin: 0 0 8px; }
.pop .bg-controls .btn { width: 100%; justify-content: center; }


/* Cuerpo del desplegable «Ver». Antes de esto fue una tarjeta fija sobre el
   lienzo: se descartó porque tapaba el plano y competía con el navegador. */
.ver-body { display: flex; flex-direction: column; }

/* Diálogo de plantillas: con veintitantas, por grupos y con buscador. */
.modal-tpl { width: min(720px, 94vw); }
.modal-tpl input[type="search"] { margin-bottom: 4px; }
.tpl-cuerpo { max-height: 54vh; overflow-y: auto; margin: 6px -4px 4px; padding: 0 4px; }
.tpl-grupo {
  margin: 14px 0 8px; font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.tpl-cuerpo .tpl-grupo:first-child { margin-top: 4px; }
.empresa-chip.clickable { cursor: pointer; }
.empresa-chip.clickable:hover { border-color: var(--accent-line); color: var(--text); }

/* Miga de pan renombrable: el lápiz solo aparece al pasar por encima. */
.crumb-edit {
  display: inline-flex; align-items: center; gap: 5px; padding: 2px 6px;
  background: none; border: 1px solid transparent; border-radius: var(--r-sm);
  color: inherit; font: inherit; cursor: pointer;
}
.crumb-edit svg { opacity: 0; transition: opacity .12s var(--ease); }
.crumb-edit:hover { border-color: var(--line); background: var(--panel-2); }
.crumb-edit:hover svg { opacity: .7; }

/* Menú de empresa: mismo desplegable que «Ver», con dos líneas por opción. */
.pop-emp { width: 280px; padding: 6px; }
.pop-item {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 9px 10px;
  background: none; border: 0; border-radius: var(--r); color: var(--text);
  font: inherit; text-align: left; cursor: pointer;
}
.pop-item:hover { background: var(--panel-3); }
.pop-item svg { margin-top: 2px; color: var(--muted); flex: none; }
.pop-item strong { display: block; font-weight: 600; font-size: 13.5px; }
.pop-item .small { display: block; line-height: 1.35; }
.emp-btn .chev, .exp-btn .chev { opacity: .6; font-size: 10px; margin-left: 2px; }

/* EXPORTAR: todo lo que sale del editor, en dos grupos (cliente / obra). Sustituye
   al menú ⋯ y al icono de descarga que hacía una sola de las seis cosas. */
.exp-btn.activo { color: var(--text); border-color: var(--accent-line); background: var(--panel-3); }
.pop-exp { width: 300px; padding: 8px; }
.pop-exp .pop-tit { margin: 8px 8px 4px; }
.pop-exp .pop-tit:first-child { margin-top: 2px; }

/* --- Pantalla de arranque del lienzo vacío -------------------------------
   Un plano en blanco no es un estado: es una pregunta («¿y ahora qué?»), y es la
   pantalla que más gente ve por primera vez. Manda la comodidad del que no sabe de
   planos: dos clics con dibujos delante, la caja de texto A LA VISTA (no detrás de
   un botón) y lo demás en el pie. El envoltorio NO captura el ratón —se puede
   seguir moviendo la vista por detrás—; solo la tarjeta. Se va sola al haber paredes. */
.empezar {
  position: absolute; inset: 0; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  padding: 16px; pointer-events: none;
}
.emp-card {
  pointer-events: auto; position: relative;
  width: min(680px, 100%); max-height: 100%; overflow: auto;
  padding: 22px 22px 16px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: 0 18px 48px rgba(0,0,0,.34);
  animation: emp-in .18s var(--ease) both;
}
@keyframes emp-in { from { opacity: 0; transform: translateY(6px) scale(.99); } }
.emp-card h3 { margin: 0 0 3px; font-size: 17px; }
.emp-card > p { margin: 0 0 12px; max-width: 52ch; }
.emp-x { position: absolute; top: 10px; right: 10px; }

/* Título de paso, con su número en una chapa: en una pantalla con tres caminos, sin
   numerar no se sabe cuál es «el» camino. */
.emp-tit {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 9px; font-size: 13px; font-weight: 600; color: var(--text);
}
.emp-num {
  flex: none; display: grid; place-items: center; width: 19px; height: 19px;
  font-size: 11.5px; font-weight: 700; border-radius: 999px;
  color: var(--accent); background: var(--accent-soft, rgba(90,150,255,.14));
}

/* PASO 1: qué estancia. Botones anchos con el símbolo en planta — se reconoce antes
   que el texto, y es el mismo dibujo que verá luego en el plano. */
.emp-tipos { display: flex; flex-wrap: wrap; gap: 8px; }
.emp-tipo {
  display: flex; align-items: center; gap: 7px; flex: 1 1 108px;
  padding: 10px 12px; margin: 0; cursor: pointer; font: inherit; font-size: 13px;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--r);
  transition: border-color .16s var(--ease), background .16s var(--ease), transform .16s var(--ease);
}
.emp-tipo:hover { border-color: var(--accent); background: var(--panel-3); transform: translateY(-1px); }
.emp-tipo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.emp-tipo.on { border-color: var(--accent); background: var(--panel-3); color: var(--text); font-weight: 600; }
.emp-tipo.on svg { color: var(--accent); }

/* PASO 2: cuál se parece. La planta dibujada manda; el nombre y las medidas la
   acompañan. Es la misma miniatura que la galería de plantillas y que las estancias
   guardadas: si mejora una, mejoran las tres. */
.emp-paso2:not(:empty) { margin-top: 13px; }
.emp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.emp-tpl {
  display: flex; flex-direction: column; gap: 2px; align-items: stretch; text-align: left;
  padding: 7px; margin: 0; cursor: pointer; font: inherit;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--r);
  transition: border-color .16s var(--ease), background .16s var(--ease), transform .16s var(--ease);
}
.emp-tpl:hover { border-color: var(--accent); background: var(--panel-3); transform: translateY(-1px); }
.emp-tpl:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.emp-prev {
  display: block; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--panel); overflow: hidden; margin-bottom: 3px;
}
.emp-prev svg, .emp-prev img { display: block; width: 100%; height: auto; }
.emp-tpl-n { font-size: 12.5px; font-weight: 600; line-height: 1.25; }

.emp-medidas {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin-top: 10px; padding: 9px 11px;
  background: var(--panel-2); border: 1px dashed var(--line); border-radius: var(--r);
}
.emp-med {
  width: 74px; padding: 5px 7px; font: inherit; font-size: 13px;
  color: var(--text); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-sm);
}

/* Separador con texto: marca que lo de abajo es OTRO camino, no el paso siguiente. */
.emp-o {
  display: flex; align-items: center; gap: 10px; margin: 14px 0 9px;
  font-size: 12px; color: var(--muted);
}
.emp-o::before, .emp-o::after { content: ''; flex: 1; height: 1px; background: var(--line); }

.emp-decir { display: flex; gap: 8px; align-items: stretch; }
.emp-campo {
  flex: 1; min-width: 0; resize: vertical; padding: 9px 11px; font: inherit; font-size: 13.5px;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r);
}
.emp-campo:focus { outline: none; border-color: var(--accent); }
.emp-decir-bts { display: flex; flex-direction: column; gap: 6px; justify-content: center; }
.emp-decir-bts .btn { white-space: nowrap; }
/* Grabando: el punto que late. Un botón que solo cambia de texto no se ve mientras
   hablas mirando a otro lado. */
.emp-mic.grabando { border-color: var(--danger, #e5484d); color: var(--danger, #e5484d); }
.emp-mic.grabando svg { animation: emp-lat 1.1s ease-in-out infinite; }
@keyframes emp-lat { 50% { opacity: .3; } }

.emp-ejs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.emp-ej {
  padding: 4px 10px; margin: 0; cursor: pointer; font: inherit; font-size: 12px;
  color: var(--muted); background: none;
  border: 1px solid var(--line); border-radius: 999px;
}
.emp-ej:hover { color: var(--text); border-color: var(--accent-line, var(--accent)); background: var(--panel-2); }

.emp-pie {
  display: flex; flex-wrap: wrap; gap: 4px; justify-content: center;
  margin: 13px -6px 0; padding-top: 10px; border-top: 1px solid var(--line);
}
.emp-otra {
  display: flex; align-items: center; gap: 6px; padding: 6px 10px;
  font: inherit; font-size: 12.5px; color: var(--muted);
  background: none; border: 0; border-radius: var(--r-sm); cursor: pointer;
}
.emp-otra:hover { color: var(--text); background: var(--panel-2); }

@media (max-width: 720px) {
  .emp-card { padding: 18px 16px 12px; }
  .emp-tipo { flex: 1 1 46%; }
  .emp-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }
  .emp-decir { flex-direction: column; }
  .emp-decir-bts { flex-direction: row; }
  .emp-decir-bts .btn { flex: 1; justify-content: center; }
}

/* --- Diálogo del PDF -----------------------------------------------------
   Un documento es PARA ALGUIEN: primero se elige el destinatario y solo después,
   si hace falta, se toca la lista de secciones. */
.modal-pdf { width: min(560px, 94vw); }
.pdf-presets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 12px 0 4px; }
.pdf-preset {
  display: block; text-align: left; padding: 10px; margin: 0; cursor: pointer;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--r);
  transition: border-color .16s var(--ease), background .16s var(--ease);
}
.pdf-preset:hover { border-color: var(--accent-line); background: var(--panel-3); }
.pdf-preset.sel { border-color: var(--accent); background: var(--accent-soft); }
.pdf-preset strong { display: block; font-size: 13px; margin-bottom: 2px; }
.pdf-preset .small { display: block; line-height: 1.3; }
.pdf-libre {
  grid-column: 1 / -1; justify-self: start; padding: 2px 8px;
  font-size: 11px; font-weight: 600; color: var(--accent);
  background: var(--accent-soft); border-radius: 999px;
}
.pdf-sep { height: 1px; background: var(--line); margin: 14px 0 10px; }
.pdf-tit { margin: 0 0 6px; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.pdf-secs { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 14px; max-height: 40vh; overflow-y: auto; }
.pdf-sec { display: flex; gap: 8px; align-items: flex-start; padding: 5px 2px; cursor: pointer; }
/* La regla global pone width:100% a TODO input, y una casilla con ancho de campo de
   texto empuja la etiqueta y parte cada palabra. Aqui se le da su tamano. */
.pdf-sec input { flex: none; width: 15px; height: 15px; min-width: 15px; margin: 2px 0 0; padding: 0; }
.pdf-sec > span { min-width: 0; }
.pdf-sec strong { display: block; font-size: 12.5px; font-weight: 500; }
.pdf-sec .small { display: block; line-height: 1.3; }
.pdf-aviso { margin: 10px 0 0; }
.pdf-estado { margin: 4px 0 0; min-height: 1.2em; color: var(--accent); }
.pdf-estado.error { color: var(--danger); }

@media (max-width: 620px) {
  .pdf-presets, .pdf-secs { grid-template-columns: 1fr; }
}

/* --- Ayuda de la barra superior -----------------------------------------
   Los atajos se leen de arriba abajo buscando uno concreto, así que la ventana
   scrollea sola y los títulos de sección se quedan pegados: con 27 filas, sin eso
   pierdes de vista a qué ámbito pertenece la que estás mirando. */
.pop-ayuda { width: 272px; padding: 8px; }
.modal-ayuda { width: min(600px, 94vw); }
.ayuda-cuerpo { max-height: 62vh; overflow-y: auto; margin: 12px 0 4px; padding-right: 4px; }
.ayuda-grupo {
  position: sticky; top: 0; z-index: 1; margin: 0 0 6px; padding: 6px 0 4px;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); background: var(--panel);
}
.ayuda-cuerpo .help-list:not(:last-child) { margin-bottom: 4px; }

/* La plantilla que está puesta ahora mismo, marcada en el selector: probar plantillas
   es comparar, y sin saber de cuál vienes no se puede comparar. */
.tpl-card.actual { border-color: var(--accent); background: var(--accent-soft); }
.tpl-actual {
  align-self: flex-start; margin-bottom: 4px; padding: 1px 7px;
  font-size: 10.5px; font-weight: 600; color: var(--accent);
  background: var(--panel); border-radius: 999px;
}
.pdf-opcion { grid-column: 1 / -1; padding: 2px 2px 0; }

/* Aviso de conflicto: dos personas en la misma estancia. Es un alto en el camino, no
   un toast — hay que leerlo y decidir, así que no se cierra tocando fuera. */
.modal-conf { width: min(500px, 94vw); }
.modal-conf h3 { display: flex; align-items: center; gap: 8px; color: var(--warn); }
.modal-conf p { margin: 10px 0 0; }
.conf-acciones { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; margin-top: 18px; }
.conf-acciones .btn.danger { color: #ffc3c8; border-color: rgba(255,95,109,.5); background: rgba(255,95,109,.14); }
.conf-acciones .btn.danger:hover { background: rgba(255,95,109,.22); }

/* --- Documentos compartidos --------------------------------------------- */
.enl-tabla { display: flex; flex-direction: column; gap: 2px; margin-top: 12px; }
.enl-fila {
  display: grid; grid-template-columns: 1fr 110px 110px 90px 120px; gap: 12px;
  align-items: center; padding: 10px 12px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  font-size: 13px;
}
.enl-fila strong { display: block; }
.enl-fila .small { display: block; }
.enl-cab {
  background: none; border: 0; padding: 0 12px 2px;
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
}
.enl-num { text-align: right; }
.enl-acts { display: flex; gap: 4px; justify-content: flex-end; }
/* Un enlace retirado se sigue viendo, en gris: saber que YA no vale es tan útil como
   saber que vale. */
.enl-fila.muerto { opacity: .55; }
.enl-fila.muerto strong { text-decoration: line-through; }

@media (max-width: 760px) {
  .enl-fila { grid-template-columns: 1fr auto; gap: 6px; }
  .enl-cab { display: none; }
  .enl-acts { grid-column: 2; grid-row: 1 / span 3; }
}
.enl-plegar { margin-top: 12px; }
/* El parche de `[hidden]` de aquí también se retiró: lo cubre la regla global. */


/* ---------- Planograma ----------
   El panel donde se reparte el surtido. Dos mitades: las cifras arriba (el porqué) y
   el mueble en alzado abajo (el dónde). */
.plano-panel { padding: 12px; overflow: auto; }

/* ═══ LAS CIFRAS SE REPARTEN, NO SE DESBORDAN ═══════════════════════════════

   Estas filas de cifras nacieron en el panel ancho de la vista de datos, donde caben
   cuatro seguidas. En el panel de recorridos el hueco son 362 px y `.stat` pide 120 de
   mínimo: cuatro cifras son 504, y sin `wrap` eso no se reparte en dos líneas — se sale
   por la derecha y el panel entero se pone a hacer SCROLL LATERAL. Medido: una cifra
   asomando 119 px.

   Con `wrap` y sin mínimo, cabe lo que cabe y el resto baja de línea. Es lo que tiene
   que hacer un panel de ancho fijo: repartir. */
/* DOS Y DOS, no tres y una. Con `flex` las cuatro cifras se repartían por ancho y en la
   columna de recorridos entraban tres arriba y una sola abajo, ocupando toda la fila: la
   cuarta parecía otra cosa —un total, un resumen— cuando es una más de las cuatro. Una
   rejilla de dos columnas las hace leerse como lo que son, cuatro cifras del mismo rango. */
.pg-cifras { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px; }
.pg-cifras .stat { min-width: 0; padding: 8px 10px; }
/* Y la cifra en sí no puede empujar: los números largos van con puntos suspensivos
   antes que abrir el panel. */
.pg-cifras .stat b,
.pg-cifras .stat span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* LA TABLA DEL REPARTO. Dos columnas de porcentaje que dicen cosas distintas: lo que
   una categoría OCUPA y lo que de verdad EXPONE. La flecha marca la diferencia, que es
   justo el hallazgo — nadie mira una tabla de porcentajes iguales. */
.pg-tabla { display: flex; flex-direction: column; gap: 1px; }
.pg-fila {
  display: grid; grid-template-columns: 1fr 62px 78px; align-items: center; gap: 6px;
  padding: 5px 6px; border-radius: 6px; font-size: 12.5px;
}
.pg-fila:nth-child(odd of .pg-fila:not(.pg-cab)) { background: var(--panel-2, rgba(127, 140, 170, .06)); }
.pg-fila span:not(.pg-cat) { text-align: right; font-variant-numeric: tabular-nums; }
.pg-cab { font-size: 11px; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); }
.pg-cat { display: flex; align-items: center; gap: 6px; min-width: 0; }
.pg-cat span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-punto { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.pg-sube { color: #35c08a; font-weight: 600; }
.pg-baja { color: #e2564d; font-weight: 600; }
.pg-aviso { color: #e2564d; margin: 8px 0 0; }

/* Reparto por altura: una barra por nivel, del sombrero al suelo. */
.pg-niveles { display: flex; flex-direction: column; gap: 4px; }
.pg-nivel { display: grid; grid-template-columns: 68px 1fr 40px; align-items: center; gap: 8px; font-size: 12px; }
.pg-nivel-p { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }
.pg-barra { height: 8px; border-radius: 4px; background: rgba(127, 140, 170, .18); overflow: hidden; }
/* `--accent`, no `--acento`. La variable con tilde NO EXISTE en la hoja —la de CSS
   se llama `--accent`; `--acento` es el nombre en el objeto del LIENZO, que es otra
   cosa—
   así que esto caía siempre al azul de reserva y la barra del planograma se quedaba
   azul aunque la cadena fuera rosa. Un `var()` con reserva no falla: se equivoca en
   silencio, que es peor. */
.pg-barra i { display: block; height: 100%; background: var(--accent); border-radius: 4px; }

/* EL ALZADO. Las bandas de nivel al fondo son la mitad del valor del dibujo: sin ellas
   es un mueble, con ellas se ve qué has puesto a la altura que vende. */
.pg-alzado { margin: 10px 0; border-radius: 8px; overflow: hidden; background: var(--panel-2, rgba(127, 140, 170, .06)); }
.pg-svg { display: block; }
.pg-banda-ojos     { fill: rgba(53, 192, 138, .16); }
.pg-banda-manos    { fill: rgba(79, 140, 255, .10); }
.pg-banda-sombrero { fill: rgba(255, 181, 71, .10); }
.pg-banda-suelo    { fill: rgba(127, 140, 170, .10); }
.pg-carcasa { fill: none; stroke: var(--line); stroke-width: 1; }
.pg-balda { stroke: var(--muted); stroke-width: 1.2; opacity: .75; }
.pg-prod { stroke: rgba(0, 0, 0, .18); stroke-width: .5; }
.pg-desborda { stroke: #e2564d; stroke-width: 4; }

.pg-caras { display: flex; gap: 6px; margin-bottom: 6px; }
/* Botones y desplegable de una fila de acciones: si no caben, bajan de línea. Un botón
   con una etiqueta larga no puede poner a hacer scroll al panel entero. */
.pg-acciones { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 10px; }
.pg-acciones select { flex: 1 1 120px; min-width: 0; }
.pg-acciones .btn { flex: 0 1 auto; min-width: 0; }

.pg-baldas { display: flex; flex-direction: column; gap: 6px; }
.pg-balda-fila { padding: 7px 8px; border: 1px solid var(--line); border-radius: 8px; }
.pg-balda-fila.mal { border-color: #e2564d; }
.pg-balda-cab { display: flex; align-items: center; gap: 7px; font-size: 12px; margin-bottom: 4px; }
.pg-etq {
  font-size: 10.5px; padding: 1px 6px; border-radius: 999px;
  background: rgba(127, 140, 170, .16); color: var(--muted);
}
.pg-item { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 2px 0; }
.pg-nom { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-fac { min-width: 22px; text-align: center; font-variant-numeric: tabular-nums; font-weight: 600; }

/* Leyenda del mapa de calor: la barra ES la escala, así que va con el mismo degradado
   que usa `colorTemp`. Dos escalas distintas para lo mismo se separan siempre. */
.heat-leyenda { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.heat-barra {
  flex: 1; height: 8px; border-radius: 4px;
  background: linear-gradient(90deg, rgb(60,110,190), rgb(157,155,130), rgb(255,200,70), rgb(255,135,50), rgb(255,70,30));
}
.pg-temp { display: inline-block; width: 26px; height: 9px; border-radius: 3px; }

/* ---------- Comparar variantes ----------
   La tabla se lee de izquierda a derecha: qué se mide y cuánto da cada montaje. La
   columna que gana se marca; el empate NO se marca, porque marcar los dos es no decir
   nada y ocupa el mismo sitio. */
.cmp-overlay {
  position: fixed; inset: 0; z-index: 210; display: flex; align-items: center; justify-content: center;
  background: rgba(8, 12, 22, .55); backdrop-filter: blur(3px); padding: 20px;
}
.cmp-caja {
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, .35);
  width: min(760px, 96vw); max-height: 88vh; display: flex; flex-direction: column;
}
.cmp-cab { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
.cmp-cab span:first-child { display: flex; align-items: center; gap: 8px; }
.cmp-cuerpo { padding: 14px 16px 18px; overflow: auto; }
.cmp-tabla { display: flex; flex-direction: column; gap: 1px; }
.cmp-fila {
  display: grid; grid-template-columns: 1.4fr repeat(var(--cols), 1fr);
  align-items: center; gap: 8px; padding: 7px 8px; border-radius: 7px; font-size: 13px;
}
.cmp-fila:nth-child(even) { background: var(--panel-2, rgba(127, 140, 170, .06)); }
.cmp-fila > span:not(.cmp-nom) { text-align: right; font-variant-numeric: tabular-nums; }
.cmp-cab-fila { font-size: 12.5px; border-bottom: 1px solid var(--line); border-radius: 0; padding-bottom: 10px; }
.cmp-cab-fila span { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.cmp-cab-fila .actual b { color: var(--accent); }
.cmp-esta, .cmp-ir { font-size: 10.5px; color: var(--muted); font-weight: 400; }
.cmp-nom { display: flex; flex-direction: column; gap: 1px; text-align: left; }
.cmp-dir { font-size: 10.5px; color: var(--muted); font-style: normal; }
.cmp-fila .gana { font-weight: 700; color: #35c08a; }

/* Mandos de la multitud en la barra de la 3D. El deslizador va estrecho a propósito:
   compite por sitio con los cinco mandos de la vista, y el número al lado hace que no
   haga falta ser preciso con el ratón para saber cuánta gente hay. */
/* El mando «Gente» se quitó: cuánta gente hay dentro es una CONSECUENCIA de la
   afluencia (ley de Little), no una preferencia. Sus reglas se van con él. */

/* Sugerencias del diagnóstico de recorridos. Misma forma que un aviso de la revisión:
   quien usa esto ya sabe leer ese patrón, y reinventarlo aquí obligaría a aprenderlo
   dos veces. El color del filete izquierdo dice la gravedad. */
.diag {
  margin: 0 0 10px; padding: 10px 12px;
  border: 1px solid var(--line); border-left: 3px solid var(--muted);
  border-radius: var(--r); background: var(--panel-2);
}
.diag[data-nivel="error"] { border-left-color: var(--danger); }
.diag[data-nivel="aviso"] { border-left-color: var(--warn); }
.diag[data-nivel="ok"]    { border-left-color: var(--accent-2); }
.diag-cab { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text); }
.diag[data-nivel="error"] .diag-cab svg { color: var(--danger); }
.diag[data-nivel="aviso"] .diag-cab svg { color: var(--warn); }
.diag[data-nivel="ok"] .diag-cab svg { color: var(--accent-2); }
/* Lo VISTO y lo SUGERIDO se distinguen tipográficamente a propósito: uno es un hecho
   medido y el otro una recomendación, y mezclarlos hace que la recomendación parezca
   un dato. */
.diag-visto { margin: 6px 0 0; font-size: 12px; color: var(--text-2); line-height: 1.5; }
.diag-sug { margin: 5px 0 0; font-size: 12px; color: var(--muted); font-style: italic; line-height: 1.5; }
.diag .btn { margin-top: 9px; }

/* ===========================================================================
   RECORRIDOS SOBRE LA TIENDA
   ===========================================================================
   El análisis vive DELANTE de la tienda y no en otra pestaña. Estuvo en el panel de
   Datos, con el argumento de que su salida son los números y los muñecos la
   ilustración: cierto, y mala decisión. Se lee «por la torre pasa el 100 %» y la torre
   está en otra pantalla; se aplica un arreglo y no se ve el efecto. Es el mismo error
   que este proyecto vino a corregir — lo bueno detrás de un botón.

   El botón, discreto y arriba a la izquierda: no compite con los mandos de la 3D y no
   tapa nada. Cerrado no se ve la capa; la vista Tienda es para ver la tienda. */
/* LOS BOTONES DE LAS CAPAS, en fila y en una caja que los ordena. Era uno solo y
   llevaba sus coordenadas escritas encima; con el segundo —el diario— dos botones en
   `top:12px; left:12px` se pintan uno sobre otro. El sitio lo sabe el contenedor, no
   cada botón: así el tercero no obliga a tocar nada. */
.capa-btns { position: absolute; top: 12px; left: 12px; z-index: 3; display: flex; gap: 8px; }
.rec-abrir {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--text-2);
  font: 600 12.5px/1 inherit; font-family: inherit; cursor: pointer;
  box-shadow: var(--sh-1);
  transition: background .14s var(--ease), color .14s var(--ease), border-color .14s var(--ease);
}
.rec-abrir:hover { background: var(--panel-2); color: var(--text); border-color: var(--accent-line); }
.rec-abrir.active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* La capa: columna a la izquierda, sobre el render. A la IZQUIERDA a propósito — los
   mandos de la 3D están a la derecha, y un panel que los tapara obligaría a cerrarlo
   para cambiar el aforo, que es justo lo que se hace mientras se lee esto.

   Translúcida y con desenfoque: se sigue viendo que hay tienda detrás, lo cual importa
   porque el panel habla de ella. Opaca se leería como otra pantalla, que es lo que
   estamos quitando. */
.rec-capa {
  position: absolute; top: 12px; left: 12px; bottom: 12px; z-index: 4;
  width: min(400px, 46%);
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--sh-3);
  overflow: hidden;
}
.rec-cab {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 10px 10px 13px; border-bottom: 1px solid var(--line);
  font-size: 13px; color: var(--text); flex: none;
}
.rec-cab svg { color: var(--accent); }
/* El contenido scrollea SOLO él: la cabecera se queda, que es lo que permite cerrar sin
   subir hasta arriba. */
/* Cada pestaña es ahora un `.recorridos-panel` DENTRO de `.rec-cuerpo`, que es quien
   scrollea y quien pone el margen. Aquí ya no va ni `overflow` ni `padding`: con los dos
   puestos salían dos barras de scroll anidadas y el margen doble. */
.recorridos-panel { min-height: 0; }
/* ═══ LA TABLA DE TRÁFICO, QUE ADEMÁS SE EDITA ══════════════════════════════

   Cada fila lleva el nombre del mueble arriba y un desplegable de categoría debajo, en
   DOS LÍNEAS y no en una. En 400 px de ancho, un nombre («Torre expositora izquierda»)
   más un desplegable más dos porcentajes en la misma línea deja el desplegable en
   cuarenta píxeles, o sea inservible — y este desplegable es el mando principal del
   panel, porque es con el que se prueban las ideas. */
.rec-fila { align-items: start; padding: 7px 6px; }
.rec-fila .pg-cat { display: block; }
.rec-nom { display: flex; align-items: center; gap: 6px; min-width: 0; }
.rec-nom > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rec-cat {
  width: 100%; margin-top: 5px; max-width: 100%;
  font-size: 11.5px; padding: 3px 5px;
}
.rec-cat:disabled { opacity: .5; }
/* El atajo al plano: discreto hasta que se pasa por la fila. Es una acción de segunda
   —la primera es cambiar el producto— y compite por el mismo sitio. */
.rec-ir {
  flex: none; margin-left: auto; padding: 2px; line-height: 0;
  background: none; border: 0; border-radius: 5px; color: var(--muted); cursor: pointer;
  opacity: 0; transition: opacity .12s var(--ease), color .12s var(--ease);
}
.rec-fila:hover .rec-ir, .rec-ir:focus-visible { opacity: 1; }
.rec-ir:hover { color: var(--accent); background: var(--accent-soft); }

/* EL SALTO respecto a la tirada anterior. Verde y rojo son los de significado de la
   hoja, no los de marca: «más gente pasa» no es una opinión de marca. */
.rec-dif {
  display: inline-block; margin-left: 5px; font-size: 11px; font-weight: 600;
  font-style: normal; padding: 1px 4px; border-radius: 4px;
}
.rec-dif[data-signo="mas"] { color: var(--accent-2); background: color-mix(in srgb, var(--accent-2) 14%, transparent); }
.rec-dif[data-signo="menos"] { color: var(--danger); background: color-mix(in srgb, var(--danger) 14%, transparent); }

/* ═══ LA VENTA IMPORTADA ═══════════════════════════════════════════════════ */
.rec-ventas { margin: 4px 0 8px; }
.rec-ventas .pg-cifras { margin-bottom: 8px; }
.rec-peso { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.rec-peso .pg-barra { width: 40px; flex: none; }
/* «ninguno»: una categoría que vende y no está en ningún mueble. Va en el color de
   peligro porque es un hallazgo, no un dato más de la tabla. */
.rec-cero { color: var(--danger); font-size: 11.5px; }

.rec-orig { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }
.rec-orig-f {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 6px; border-radius: 6px; background: var(--panel-2); font-size: 12px;
}
.rec-orig-n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rec-orig-d { flex: none; color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }
/* La marca de «esto lo inventó la app». Va en el color de aviso y no en el de marca: es
   una advertencia sobre la procedencia del dato, y tiene que leerse como tal. */
.rec-sim {
  flex: none; font-size: 10.5px; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 5px; border-radius: 4px;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent);
}

/* ═══ EL MANDO DE AFLUENCIA ════════════════════════════════════════════════

   Un recuadro y no tres controles sueltos en fila: los tres dicen lo mismo —cuánta
   gente y durante cuánto— y separados se leían como tres ajustes sin relación. */
/* La caja no se estira con su contenido: lo que no quepa, que se reparta dentro. */
.af-caja { min-width: 0; }
.af-caja {
  margin: 2px 0 12px; padding: 9px 10px 8px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--panel-2);
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.af-cab { display: flex; align-items: baseline; gap: 7px; margin-bottom: 6px; }
.af-cab > span:first-child { font-size: 11px; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); }
.af-n { font-size: 15px; font-variant-numeric: tabular-nums; }
.af-nota { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.af-rango { width: 100%; accent-color: var(--accent); cursor: pointer; }
.af-marcas { display: flex; gap: 4px; margin: 2px 0 8px; }
.af-marca {
  flex: 1; padding: 3px 2px; font: inherit; font-size: 10.5px;
  background: none; border: 1px solid transparent; border-radius: 5px;
  color: var(--muted); cursor: pointer; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.af-marca:hover { color: var(--text); border-color: var(--accent-line); background: var(--accent-soft); }
.af-int { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
/* LA FILA DE REPRODUCCIÓN: encender la gente y a qué velocidad corre el reloj. Separada
   por un filete de la fila de simular porque son dos momentos distintos —primero se
   calcula, luego se mira— y sin la línea se leían como cinco mandos en fila. */
/* Los mandos de abajo (ver la gente · tiempo · qué piensan) suman 371 px en un hueco
   de 340: sin `wrap` empujaban la caja de afluencia fuera del panel. Bajan de línea. */
.af-play {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; row-gap: 7px;
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line);
}
.af-play .af-ver { flex: 1 1 auto; min-width: 0; }
.af-play select { width: auto; max-width: 100%; }
/* LA SALA LLENA. Se enciende sola por debajo de 2 m² por persona, que es donde la gente
   empieza a estorbarse: subir la afluencia hasta que esto se enciende ES la pregunta
   «¿cuánta gente aguanta esta tienda?», y la respuesta tiene que salir en el mando, no
   en un párrafo tres pantallas más abajo. */
.af-caja.af-lleno { border-color: var(--warn); background: color-mix(in srgb, var(--warn) 9%, var(--panel-2)); }
.af-caja.af-lleno .af-nota::after { content: ' · la sala se queda pequeña'; color: var(--warn); }

/* ═══ QUIÉN ENTRA: la mezcla de clientela ══════════════════════════════════

   Plegado por defecto: es un ajuste que se toca una vez por cadena y luego se olvida,
   y cinco deslizadores siempre abiertos empujarían hacia abajo lo que se mira cada día
   (las cifras y el diagnóstico). El resumen va en la propia cabecera para no tener que
   abrirlo solo para saber qué hay puesto. */
.cl-caja {
  margin: 0 0 12px; padding: 0 10px; border: 1px solid var(--line);
  border-radius: var(--r); background: var(--panel-2);
}
.cl-caja > summary {
  display: flex; align-items: baseline; gap: 8px; padding: 8px 0; cursor: pointer;
  font-size: 11px; text-transform: uppercase; letter-spacing: .03em; color: var(--muted);
  list-style: none;
}
.cl-caja > summary::-webkit-details-marker { display: none; }
.cl-caja > summary::before {
  content: '▸'; font-size: 10px; transition: transform .15s var(--ease); color: var(--muted);
}
.cl-caja[open] > summary::before { transform: rotate(90deg); }
.cl-res {
  text-transform: none; letter-spacing: 0; font-size: 11px; color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.cl-fila { display: grid; grid-template-columns: 92px 1fr 36px; align-items: center; gap: 8px; margin-bottom: 5px; }
.cl-nom { font-size: 11.5px; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cl-rango { width: 100%; accent-color: var(--accent); cursor: pointer; }
.cl-val { font-size: 11.5px; text-align: right; font-variant-numeric: tabular-nums; }
.cl-ojo { margin: 8px 0; line-height: 1.4; }
.cl-caja .btn { margin-bottom: 10px; }

/* ═══ EL MARCADOR EN VIVO ══════════════════════════════════════════════════ */
.v3d-hud {
  flex: none; pointer-events: auto;
  padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  backdrop-filter: blur(8px); box-shadow: var(--sh-2);
  font-size: 11.5px; pointer-events: auto;
}
.hud-n { display: flex; align-items: baseline; gap: 6px; }
.hud-n b { font-size: 20px; line-height: 1; font-variant-numeric: tabular-nums; color: var(--accent); }
.hud-n span { color: var(--muted); font-size: 11px; }
.hud-p { display: flex; gap: 8px; margin: 5px 0 6px; color: var(--text-2); }
.hud-and::before, .hud-par::before {
  content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 4px;
}
.hud-and::before { background: var(--accent-2); }
.hud-par::before { background: var(--warn); }
.hud-top {
  display: block; width: 100%; text-align: left; padding: 4px 6px; font: inherit; font-size: 11px;
  border: 1px solid var(--accent-line); border-radius: 6px;
  background: var(--accent-soft); color: var(--text); cursor: pointer;
}
.hud-top:hover { border-color: var(--accent); }
.hud-vacio { color: var(--muted); font-size: 11px; }
/* QUIÉN hay dentro, en fichas diminutas: comprobar de un vistazo que la mezcla que se
   ha declarado se está viendo de verdad en la sala. */
.hud-mix { display: flex; flex-wrap: wrap; gap: 3px 5px; margin-bottom: 6px; }
.hud-mix-i {
  font-size: 10px; padding: 1px 5px; border-radius: 4px;
  background: var(--panel-3); color: var(--text-2); white-space: nowrap;
}
/* CÓMO LO ESTÁN PASANDO. El color es el del significado, no el de la marca: esto es un
   semáforo de experiencia y tiene que leerse como tal aunque la cadena sea roja. */
.hud-exp { display: flex; align-items: baseline; gap: 5px; margin-bottom: 3px; }
.hud-exp b { font-size: 14px; font-variant-numeric: tabular-nums; }
.hud-exp span { font-size: 10.5px; color: var(--muted); }
.hud-exp[data-nivel="mal"] b { color: var(--danger); }
.hud-exp[data-nivel="medio"] b { color: var(--warn); }
.hud-exp[data-nivel="bien"] b { color: var(--accent-2); }
.hud-motivo { font-size: 10.5px; color: var(--text-2); margin-bottom: 6px; line-height: 1.3; }
/* El botón de bocadillos, en la fila de reproducción. */
.af-play .af-bur { flex: none; }

/* ═══ NOMBRES PULSABLES: de un aviso al mueble ═════════════════════════════

   Un aviso que dice «"Estantería mural", "Estantería mural" y 3 más» en una tienda que
   tiene cinco iguales no identifica nada. Se pintan como texto normal y no como botones
   —son nombres, no acciones— y se descubren al pasar por encima. */
.rec-ir-nom, .diag-chip {
  font: inherit; background: none; border: 0; padding: 0; cursor: pointer;
  color: inherit; text-align: left;
  border-bottom: 1px dashed var(--line);
}
.rec-ir-nom {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.rec-ir-nom:hover, .diag-chip:hover { color: var(--accent); border-bottom-color: var(--accent); }
.diag-chips { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 7px; }
.diag-chip { font-size: 11.5px; color: var(--text-2); }

/* ═══ LA FICHA DEL MUEBLE, sobre la 3D ═════════════════════════════════════

   Abajo a la DERECHA, que es el único cuadrante libre: arriba está la barra de mandos
   de la 3D y a la izquierda la capa de recorridos. Una ficha que tapara cualquiera de
   las dos obligaría a cerrarla para seguir trabajando, y se trabaja con las tres a la
   vez — se pulsa un mueble, se le cambia el producto y se mira la tabla.

   Estrecha (270 px) a propósito: habla de UNA pieza. Si necesitara más ancho, sería
   que está contando lo que ya cuenta el panel de al lado. */
/* ARRIBA A LA DERECHA, EN LA COLUMNA, y no flotando por su cuenta.
   Estuvo abajo a la derecha, donde el ASISTENTE (fijo en `right:14px; bottom:42px`, con
   z-index 240) la tapaba entera. Se movió abajo a la izquierda y el problema pasó a ser
   otro: **se comía el suelo de la tienda**, que es por donde anda la gente y lo único
   que se ha venido a mirar. Arriba solo hay techo. */
.v3d-ficha {
  width: 100%;
  padding: 11px 12px 12px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--panel) 93%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--sh-3);
  pointer-events: auto;
  /* SE DESBORDABA. Un mueble con nombre largo, siete categorías en el desplegable y las
     tres cifras no cabían en el alto que le tocaba, y el contenido se salía de la
     tarjeta. Ahora lo que sobra se desplaza dentro; el techo lo pone la columna. */
  min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
/* LA COLUMNA. Arranca del borde de arriba del lienzo y llega como mucho al de abajo:
   el marcador mide lo que mide y la ficha se queda con lo que sobra, con su propio
   desplazamiento si no le llega.

   `pointer-events: none` en la columna y `auto` en los paneles: sin eso, la banda
   invisible de 270 px que queda debajo se tragaría los arrastres de la cámara, y girar
   la tienda por ese lado dejaría de funcionar sin ninguna pista de por qué. */
.v3d-lado {
  position: absolute; top: 12px; right: 12px; z-index: 5;
  width: 270px; max-width: calc(100% - 24px);
  max-height: calc(100% - 24px);
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
  pointer-events: none;
}
@media (max-width: 720px) {
  /* Con la capa de recorridos abierta en estrecho no queda sitio para las dos: la capa
     ocupa la pantalla y la columna se esconde en vez de solaparse. */
  .v3d-overlay.con-capa .v3d-lado { display: none; }
}
.fi-cab { display: flex; align-items: center; gap: 7px; font-size: 13px; margin-bottom: 2px; }
.fi-cab svg { color: var(--accent); flex: none; }
.fi-cab b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fi-med { margin: 0 0 10px; font-size: 11.5px; color: var(--muted); line-height: 1.35; }
/* LAS TRES CIFRAS, EN REJILLA Y NO EN FILA FLEXIBLE.
   Con `flex` cada una medía 120 px por su contenido —«de la puerta» es largo— y tres de
   120 no caben en una tarjeta de 270: la fila se salía por el lado derecho. Era el
   desbordamiento que se reportó. Con `1fr` cada columna vale lo mismo pase lo que pase y
   el texto largo parte de línea, que es lo que tiene que hacer un texto largo. */
.v3d-ficha .pg-cifras { display: grid; grid-template-columns: repeat(3, 1fr); margin-bottom: 10px; gap: 6px; }
.v3d-ficha .stat { padding: 6px 7px; min-width: 0; }
.v3d-ficha .stat b { font-size: 15px; }
.v3d-ficha .stat span { font-size: 10px; line-height: 1.25; }
/* Y la tarjeta no deja salir nada por el lado: lo que no cabe a lo ancho se parte, y lo
   que no cabe a lo alto se desplaza. */
.v3d-ficha { overflow-x: hidden; }
.fi-cat { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-bottom: 9px; }
.fi-cat > span { color: var(--muted); flex: none; }
.fi-cat select { flex: 1; min-width: 0; }
.v3d-ficha .pg-acciones { display: flex; gap: 6px; }
.v3d-ficha .pg-acciones .btn { flex: 1; }

/* LA COMPARACIÓN DE LAS DOS COLOCACIONES, dentro de la tarjeta de diagnóstico. */
.diag-comp:not(:empty) { margin-top: 9px; }
.comp-col { margin-bottom: 6px; }
.comp-col .pg-fila { grid-template-columns: 1fr 62px 62px; }
/* El que gana en cada línea, y solo ese. Nada de pintar una columna entera: cada
   opción gana en cosas distintas, y colorear la columna sería elegir por el usuario. */
.comp-gana { color: var(--accent-2); font-weight: 700; }

/* En pantalla estrecha la capa ocupa todo: 400 px sobre un móvil no dejan tienda ni
   panel, y en ese caso es mejor una cosa entera que dos a medias. */
@media (max-width: 720px) {
  .rec-capa { width: auto; right: 12px; }
}

/* ── ¿QUÉ HA CAMBIADO? El antes y el después de un cambio en la tienda ───────
   Tres decisiones de forma, y las tres son de significado:

   · el ANTES va apagado y el DESPUÉS en el color del texto. Una comparación en la que
     las dos columnas pesan igual obliga a leer la cabecera para saber cuál es cuál;
   · la DIFERENCIA es lo único con color fuerte, porque es lo que se venía a ver;
   · y el verde/rojo NO sale del signo del número sino de si eso es mejor o peor —«+3
     muebles que no ve nadie» es rojo y «+3 m andados» no es ninguna de las dos cosas—.
     Lo decide el modelo (`impacto.js`), no la hoja de estilos. */
.imp-caja { margin: 14px 0 4px; }
.imp-grupo { margin: 10px 0 12px; }
.imp-tit { font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 4px; }
.imp-tabla { width: 100%; border-collapse: collapse; font-size: 12px; }
.imp-tabla th { text-align: left; font-weight: 400; color: var(--text); padding: 3px 6px 3px 0; }
.imp-tabla td { padding: 3px 0; white-space: nowrap; }
.imp-tabla tr + tr th, .imp-tabla tr + tr td { border-top: 1px solid var(--line); }
.imp-a { color: var(--muted); text-align: right; padding-right: 4px !important; }
.imp-f { color: var(--muted); opacity: .55; padding: 0 4px !important; }
.imp-b { font-weight: 600; text-align: right; padding-right: 8px !important; }
.imp-d { font-size: 11px; font-weight: 600; padding: 1px 6px; border-radius: 999px; white-space: nowrap; }
.imp-d.mejor { color: #17663f; background: #e3f4ea; }
.imp-d.peor { color: #8d2036; background: #fbe6ea; }
.imp-d.neutro { color: var(--muted); background: var(--panel-2); }
.imp-d.igual, .imp-d.nada { color: var(--muted); background: transparent; font-weight: 400; }
.imp-motivos, .imp-cats { list-style: none; margin: 6px 0 0; padding: 0; font-size: 12px; }
.imp-motivos li, .imp-cats li { display: flex; align-items: baseline; gap: 8px; padding: 2px 0; }
.imp-motivos li + li, .imp-cats li + li { border-top: 1px solid var(--line); }
.imp-m { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* LA VENTA, aparte y enmarcada: es la única cifra de la app que proyecta un euro, y
   mezclarla con las demás la haría parecer del mismo material. Va con su supuesto
   debajo, y el marco es lo que separa «esto lo hemos medido» de «esto lo hemos supuesto». */
.imp-venta { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--panel-2); }
.imp-euros { font-size: 18px; font-weight: 700; letter-spacing: -.01em; }
.imp-euros.mejor { color: #17663f; }
.imp-euros.peor { color: #8d2036; }
.imp-euros.neutro { color: var(--text); }
@media (max-width: 720px) {
  .imp-tabla { font-size: 11.5px; }
}

/* ── Probar un cambio sin hacerlo ───────────────────────────────────────────
   Una línea por prueba: nombre, lo que se mueve y el botón. Los deltas van en la misma
   fila y no debajo, porque lo que se compara son las pruebas entre sí: puestos en
   columna hay que ir subiendo y bajando la vista para saber cuál conviene. */
.imp-pruebas { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 8px; }
.imp-prueba { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2); }
.imp-prueba .imp-m { flex: 1 1 140px; }
.imp-deltas { display: flex; gap: 4px; flex-wrap: wrap; }
.imp-deltas .imp-d { font-size: 10.5px; font-weight: 500; }
.imp-prueba .btn { flex: 0 0 auto; }

/* El botón de marcar parados va con los de reproducción; sin esto se sale de la fila
   cuando el panel está estrecho, que es el mismo desborde que ya costó una ronda. */
.af-play .btn { flex: 0 1 auto; min-width: 0; }

/* EL CONTADOR DEL INSTRUMENTO DE ESTADO. Encima de la tienda, abajo a la izquierda: es un
   número que se mira MIENTRAS se mira la sala, no un informe que se lee después. Verde
   mientras todo cuadra y rojo en cuanto no, que es la única lectura que hace falta. */
.v3d-cuenta {
  /* ABAJO A LA DERECHA, y no a la izquierda: ahí lo tapa el panel de recorridos —que es
     justo el panel desde el que se enciende esto—, y un contador escondido no cuenta
     nada. Se sube 72 px para no pisar el botón del asistente. */
  position: absolute; right: 16px; bottom: 72px; z-index: 6;
  padding: 8px 12px; border-radius: 10px; pointer-events: none;
  background: rgba(23, 102, 63, .92); color: #fff;
  font-size: 12px; line-height: 1.45; letter-spacing: .01em;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .25);
}
.v3d-cuenta.mal { background: rgba(168, 28, 42, .94); }
.v3d-cuenta b { font-weight: 700; }
.v3d-cuenta-b { opacity: .78; }

.v3d-cuenta hr { border: 0; border-top: 1px solid rgba(255, 255, 255, .28); margin: 6px 0; }
.v3d-cuenta { max-width: 340px; pointer-events: none; }

/* Con una ficha dentro, el recuadro se puede seleccionar: es la salida de emergencia para
   cuando el navegador no deja escribir en el portapapeles. Sin ficha sigue siendo un
   cartel y no debe robar clics a la tienda. */
.v3d-cuenta.ficha { pointer-events: auto; user-select: text; cursor: text; }

/* EL INTERRUPTOR DE DEPURACIÓN. Muy tenue: es una herramienta, no una función de la
   tienda, y quien no la busca no tiene por qué verla. Se enciende en el color de acento
   para que no haya duda de que está puesto. */
.v3d-debug {
  position: absolute; right: 14px; bottom: 14px; z-index: 7;
  width: 30px; height: 30px; padding: 0; border-radius: 50%;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--muted); opacity: .45; cursor: pointer;
  font-size: 15px; line-height: 1; display: flex; align-items: center; justify-content: center;
  transition: opacity .15s ease, color .15s ease;
}
.v3d-debug:hover { opacity: 1; }
.v3d-debug.activo { opacity: 1; color: #fff; background: var(--acc); border-color: var(--acc); }
/* Con el modo puesto, el contador se aparta del botón. */
.v3d-cuenta { bottom: 56px; }

/* ===========================================================================
   ANTES Y DESPUÉS · la ventana de comparar
   ---------------------------------------------------------------------------
   Las mismas cifras del panel, pero donde caben. Dos columnas, tipografía un punto
   mayor y el fondo translúcido: se lee la comparación CON LA TIENDA DETRÁS, que es de
   lo que hablan los números.
   =========================================================================== */
.cmp-fondo { position: fixed; inset: 0; z-index: 240; background: rgba(12, 16, 24, .34); }
.cmp-win {
  position: fixed; z-index: 241; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(1040px, 94vw); max-height: 86vh; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, .32); overflow: hidden;
}
.cmp-cab {
  display: flex; align-items: center; gap: 9px; padding: 12px 14px;
  border-bottom: 1px solid var(--line); background: var(--panel-2);
}
.cmp-cuerpo { padding: 16px 18px 20px; overflow: auto; }
/* DOS COLUMNAS, y que ningún bloque se parta por la mitad: una tabla cortada entre dos
   columnas es peor que una tabla estrecha. */
@media (min-width: 900px) {
  .cmp-cuerpo { columns: 2; column-gap: 26px; }
  .cmp-cuerpo > * { break-inside: avoid; margin-bottom: 14px; }
}
.cmp-cuerpo .imp-tabla { font-size: 13px; }
.cmp-cuerpo .bud-sub { font-size: 15px; }

/* ---------------------------------------------------------------------------
   INTERRUPTORES DE VISTA (huecos, lámparas): su estado normal es ENCENDIDO, así que
   pintarlos del color de acento llena la barra de botones rosas que no piden nada. El
   acento se reserva para lo que es una acción —«Pasear»— y para lo que está fuera de lo
   normal. Encendido = fondo suave; apagado = igual que cualquier otro botón.
   --------------------------------------------------------------------------- */
.v3d-bar .btn.small.active:not(.primary) {
  background: var(--panel-2); border-color: var(--line); color: var(--text);
}
.v3d-bar .btn.small.active:not(.primary):hover { border-color: var(--accent-line); }
/* Y EL INTERRUPTOR DE DEPURACIÓN, LIBRE DEL ASISTENTE. La pastilla del asistente va fija
   al viewport en `bottom: 42px` y mide unos 40 px de alto, así que ocupa la franja 42-82:
   a 62 px el botón caía justo debajo de ella y quedaba medio tapado. Se sube por encima de
   esa franja, y el contador por encima del botón. */
.v3d-debug { bottom: 96px; }
.v3d-cuenta { bottom: 138px; }

/* ---------------------------------------------------------------------------
   EL MENÚ DE VISTA. Cuatro preguntas de la misma familia —acabado, cámara, paredes,
   techo— más el interruptor de los aparatos del techo. Sueltas ocupaban 430 px de fila
   y no eran cuatro acciones: son ajustes de cómo se dibuja, que se tocan de vez en
   cuando y no mientras se mira.
   --------------------------------------------------------------------------- */
.v3d-popcaja { position: relative; display: inline-flex; }
.v3d-pop {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 24;
  display: grid; gap: 8px; min-width: 244px; padding: 12px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--sh-3);
}
.pop-fila { display: grid; grid-template-columns: 76px 1fr; align-items: center; gap: 10px; font-size: 12.5px; color: var(--muted); }
.pop-fila select { width: 100%; }
.pop-sep { height: 1px; background: var(--line); margin: 2px 0; }
.v3d-pop .btn { justify-content: center; }

/* El detalle de una prueba: qué cambio exacto se ha medido. Va debajo del titular y en
   gris, porque el titular dice el problema y esto dice la propuesta. */
.imp-detalle { display: block; margin-top: 2px; font-size: 11.5px; color: var(--muted); }

/* ══════════════════════════════════════════════════════════════════════════════
   EL DIARIO DE LA TIENDA. Una lista de fechas con lo que hizo la venta a cada lado.

   La forma es deliberadamente la de un extracto bancario y no la de una tarjeta por
   cambio: lo que se viene a hacer aquí es recorrer una columna de porcentajes de arriba
   abajo buscando cuál fue el bueno, y una rejilla de tarjetas obliga a leer en zigzag.
   ══════════════════════════════════════════════════════════════════════════════ */
.dia-lista { display: flex; flex-direction: column; gap: 2px; }
.dia-f {
  position: relative;
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 8px; border-radius: 8px;
}
.dia-f:hover { background: var(--panel-2); }
.dia-f + .dia-f { border-top: 1px solid var(--line); }
/* La fecha, en bloque y con el año debajo: es la clave de todo lo demás, así que se
   lee sin buscarla. Tabular para que los días queden en columna. */
.dia-fecha {
  flex: none; width: 42px; text-align: center; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.dia-fecha b { display: block; font-size: 13px; color: var(--text); }
.dia-fecha span { display: block; font-size: 10.5px; color: var(--muted); }
.dia-cuerpo { flex: 1; min-width: 0; }
.dia-tit { font-size: 13px; color: var(--text); }
.dia-nota, .dia-det { margin: 2px 0 0; line-height: 1.35; }
.dia-det { font-variant-numeric: tabular-nums; color: var(--text-2); }
.dia-sep { margin: 0 5px; color: var(--muted); }
.dia-aviso {
  display: flex; align-items: flex-start; gap: 5px; margin: 4px 0 0;
  color: var(--warn); line-height: 1.3;
}
.dia-aviso svg { flex: none; margin-top: 1px; }
.dia-corte { margin: 3px 0 0; }
/* LA CIFRA. El porcentaje grande y los euros/día debajo: el primero compara un cambio
   con otro, el segundo lo compara con el alquiler. */
.dia-cifra {
  flex: none; width: 72px; text-align: right; line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.dia-cifra b { display: block; font-size: 15px; }
.dia-cifra span { display: block; font-size: 10.5px; color: var(--muted); }
.dia-sube b { color: var(--accent-2); }
.dia-baja b { color: var(--danger); }
.dia-igual b { color: var(--text-2); }
/* «Sin datos» NO se pinta en rojo: no es un resultado malo, es una pregunta que
   todavía no se puede contestar. En el color de apagado, como corresponde. */
.dia-mudo span { color: var(--muted); font-size: 12px; font-style: italic; }
/* LOS BOTONES NO ROBAN ANCHO. La capa mide 400 px y entre la fecha (42), la cifra (72)
   y tres botones al titulo le quedaban 150: «gondola de desayunos a la entrada» salia en
   cuatro lineas. Flotan sobre la fila al pasar por encima, que es cuando existen. */
.dia-acts {
  position: absolute; right: 6px; bottom: 5px; z-index: 1;
  display: flex; gap: 2px; opacity: 0; transition: opacity .12s var(--ease);
  background: var(--panel-2); border-radius: 7px; padding: 1px;
}
.dia-f:hover .dia-acts, .dia-f:focus-within .dia-acts { opacity: 1; }
.dia-pie { margin-top: 10px; line-height: 1.4; }

/* UNA CIFRA QUE APRIETA. No es un aviso —los avisos tienen su sitio y su forma— es un
   número que se ha ido de madre, así que se marca el número y no se le pone icono ni
   caja de alarma. El borde a la izquierda y no el fondo entero: en una rejilla de seis
   celdas, dos fondos de color se leen como dos botones. */
.pg-cifras .stat-ojo { border-left: 3px solid var(--warn); }
.pg-cifras .stat-ojo b { color: var(--warn); }
.pg-cifras .stat-mal { border-left: 3px solid var(--danger); }
.pg-cifras .stat-mal b { color: var(--danger); }

/* «Ver N avisos más». Tenue y a la izquierda: es una salida, no una acción. Si pesara
   como un botón normal competiría con los arreglos de los avisos de arriba, que son lo
   que sí hay que pulsar. */
.diag-vermas { margin-top: 2px; color: var(--muted); font-weight: 500; }
.diag-vermas:hover { color: var(--text); }
.diag-mas { display: flex; flex-direction: column; gap: 8px; }
.diag-mas:not(:empty) { margin-top: 8px; }

/* ══════════════════════════════════════════════════════════════════════════════
   LAS PESTAÑAS DEL PANEL DE ANÁLISIS. Tres verbos: leer, trabajar y comprobar.

   Fijas arriba y el cuerpo debajo con su propio scroll, que es lo que arregla el
   problema de verdad: antes la columna entera medía 3.372 px y para volver a los mandos
   había que subirlo todo. Con la tira fija, cambiar de pestaña es un clic y no un viaje.
   ══════════════════════════════════════════════════════════════════════════════ */
.rec-tabs {
  display: flex; gap: 2px; flex: none;
  padding: 6px 8px 0; border-bottom: 1px solid var(--line);
}
.rec-tab {
  flex: 1; padding: 7px 6px 8px; border: 0; border-bottom: 2px solid transparent;
  background: transparent; color: var(--muted); cursor: pointer;
  font: 600 12px/1 inherit; font-family: inherit;
  border-radius: 7px 7px 0 0; white-space: nowrap;
  transition: color .12s var(--ease), border-color .12s var(--ease);
}
.rec-tab:hover { color: var(--text-2); background: var(--panel-2); }
.rec-tab.active { color: var(--text); border-bottom-color: var(--accent); }
/* El cuerpo es quien scrollea, no la capa: si scrollea la capa, la tira de pestañas se
   va hacia arriba y deja de servir justo cuando más falta hace. */
.rec-cuerpo { flex: 1; min-height: 0; overflow: auto; padding: 10px 13px 14px; }

/* EL ESTADO DE LA MEDICIÓN, en el centro de la barra de la 3D. Es lo que llenó el hueco
   que había entre los mandos de la izquierda y «PNG»: a qué afluencia estás mirando la
   tienda, y el botón de volver a tirar. */
.v3d-medir { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.v3d-ph-btn {
  display: inline-flex; align-items: baseline; gap: 6px; min-width: 0;
  padding: 5px 9px; border: 1px solid transparent; border-radius: 999px;
  background: transparent; cursor: pointer; font-family: inherit;
  transition: background .12s var(--ease), border-color .12s var(--ease);
}
.v3d-ph-btn:hover { background: var(--panel-2); border-color: var(--line); }
.v3d-ph { font: 700 13px/1 inherit; color: var(--text); font-variant-numeric: tabular-nums; }
.v3d-ph-nota { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Sala llena: el mismo aviso que enciende el deslizador, para enterarse sin abrir nada. */
.v3d-medir-lleno .v3d-ph { color: var(--danger); }
/* En pantalla estrecha la barra envuelve y el espejo se lleva una fila entera para él
   solo. Se queda sin el nombre del tramo, que es lo prescindible. */
@media (max-width: 760px) { .v3d-ph-nota { display: none; } }

/* Los mandos de la gente simulada, dentro del menú «Vista». Vacío no ocupa: sin tirada
   no hay nada que reproducir y el menú no debe enseñar un separador suelto. */
.v3d-play:empty { display: none; }
.v3d-play { display: flex; flex-direction: column; gap: 4px; }
.v3d-play .btn { justify-content: flex-start; width: 100%; }

/* EL TRANSPORTE de la gente simulada, en la barra de la 3D. Aparece por escalones —ver
   `_montarTransporte`— así que vacío no debe dejar hueco ni separador. */
.v3d-transporte { display: inline-flex; align-items: center; gap: 5px; }
.v3d-transporte:empty { display: none; }
/* LA MISMA ALTURA Y SIN MARGEN, como los botones de la barra.
   `.v3d-vel` es un `<label>`, así que heredaba de la regla genérica de formularios un
   `margin: 12px 0 4px` pensado para campos apilados en un panel. En una fila de botones
   eso hace dos cosas: lo baja 4 px respecto a sus vecinos y estira la fila entera de 30 a
   46 px. Se veía torcido sin que se viera por qué. */
.v3d-bar .v3d-vel { height: 30px; box-sizing: border-box; margin: 0; }
.v3d-vel {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 0 6px 0 9px; border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); line-height: 1;
}
/* NO «transparent». Lo puse así para que el selector se fundiera con la píldora y en
   tema oscuro el desplegable salía BLANCO SOBRE BLANCO: la lista de opciones la pinta el
   sistema, y con el fondo transparente se queda con el blanco de la ventana mientras la
   tinta sigue siendo la clara del tema. Reportado tal cual: «el desplegable de velocidad
   x8 no se ve en darkmode». Con el token del panel se funde igual y las opciones heredan
   un fondo de verdad. */
/* Y el `select` de dentro, a la altura de la píldora y sin su propio alto: con los 30 px
   heredados de la barra sobresalía por abajo del borde redondeado. */
.v3d-vel select { border: 0; background: var(--panel-2); padding: 0 2px; width: auto; height: auto; line-height: 1; }

/* La nota del intervalo: dice que no es una espera. Pegada a su mando y sin margen
   arriba, o se lee como un párrafo suelto en vez de como el pie de ese desplegable. */
.af-nota-t { margin: 2px 0 0; line-height: 1.35; }

/* ══════════════════════════════════════════════════════════════════════════════
   LA GUÍA DE CINCO PASOS. Señala los mandos de verdad, así que lo importante aquí es
   que el recuadro caiga EXACTAMENTE sobre el mando y que el resto se apague sin
   taparlo: un velo opaco por encima haría que se señalara algo que no se ve.
   ══════════════════════════════════════════════════════════════════════════════ */
.guia-velo {
  position: fixed; inset: 0; z-index: 400;
  background: color-mix(in srgb, #0a0f1a 52%, transparent);
  /* No come clics: la guía habla de mandos reales y el primer impulso es tocarlos. */
  pointer-events: none;
}
/* El hueco se hace con una sombra enorme hacia fuera, que es lo que recorta el velo sin
   tener que dibujar cuatro rectángulos alrededor. */
.guia-foco {
  position: fixed; z-index: 401; border-radius: 10px; pointer-events: none;
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 9999px color-mix(in srgb, #0a0f1a 52%, transparent);
  transition: top .18s var(--ease), left .18s var(--ease), width .18s var(--ease), height .18s var(--ease);
}
.guia-flecha {
  position: fixed; z-index: 402; width: 12px; height: 12px; pointer-events: none;
  background: var(--panel); border: 1px solid var(--line);
  transform: rotate(45deg);
}
.guia-flecha.arriba { border-right: 0; border-bottom: 0; }
.guia-flecha:not(.arriba) { border-left: 0; border-top: 0; }
.guia-tarjeta {
  position: fixed; z-index: 403;
  padding: 12px 14px 11px; border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--panel); box-shadow: var(--sh-3);
  transition: top .18s var(--ease), left .18s var(--ease);
}
.guia-cab { display: flex; align-items: center; gap: 7px; margin-bottom: 6px; }
.guia-cab svg { flex: none; color: var(--accent); }
.guia-tit { font-size: 14px; color: var(--text); }
.guia-cuenta { font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.guia-txt { margin: 0 0 11px; font-size: 13px; line-height: 1.45; color: var(--text-2); }
.guia-pies { display: flex; align-items: center; gap: 8px; }


/* LAS OPCIONES DE TODOS LOS DESPLEGABLES, con fondo y tinta declarados.
   No había ninguna regla para `option` y funcionaba de casualidad: los `select` llevan
   `background: var(--panel-2)` y la lista lo heredaba. En cuanto un select puso el fondo
   a `transparent` —la píldora de la velocidad— la lista se quedó con el blanco del
   sistema y la tinta clara del tema: blanco sobre blanco. Declarado aquí, no puede
   volver a depender de lo que ponga cada select. */
option { background: var(--panel-2); color: var(--text); }

/* La acción de un paso de la guía. Vacía no ocupa: solo un paso la tiene. */
.guia-accion:empty { display: none; }
.guia-accion { margin: 0 0 11px; }

/* EL CAJÓN DE ACABADOS en el panel de la zona. Plegado por defecto: en una tienda el
   suelo se decide una vez, y tenía la primera pantalla del modo Plano entera para él. */
.zona-acabados { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.zona-acabados > summary {
  display: flex; align-items: center; gap: 7px; cursor: pointer; list-style: none;
  padding: 6px 2px; border-radius: 7px; color: var(--text-2); font-size: 13px;
}
.zona-acabados > summary::-webkit-details-marker { display: none; }
.zona-acabados > summary:hover { background: var(--panel-2); color: var(--text); }
.zona-acabados > summary svg { flex: none; }
.zona-acabados[open] > summary { color: var(--text); }
.zona-pista { margin-top: 12px; }

/* EL VEREDICTO DE UNA PRUEBA. Una palabra antes de los deltas: si la suma de lo que se
   mueve cae del lado bueno o del malo. Sin esto, tres cifras en rojo y un botón azul
   «Aplicar» debajo se leen como una recomendación. */
.imp-veredicto {
  display: inline-block; margin-right: 6px; padding: 1px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
}
.v-gana { color: var(--accent-2); background: color-mix(in srgb, var(--accent-2) 14%, transparent); }
.v-pierde { color: var(--danger); background: color-mix(in srgb, var(--danger) 14%, transparent); }
.v-igual { color: var(--muted); background: var(--panel-2); }
/* La que empeora se apaga entera: sigue estando —dice que esa idea ya está descartada—
   pero no compite con las que suman. */
.imp-pierde { opacity: .72; }
.imp-pierde:hover { opacity: 1; }

/* LA LEYENDA DE LA TABLA DE MUEBLES. El punto de color y la etiqueta TAPA llevaban
   meses sin explicarse en ninguna parte: quien no ha escrito el código no puede saber
   que naranja es «pasa poca gente». Una línea debajo de la tabla, no un tooltip. */
.rec-intro { margin: 0 0 8px; line-height: 1.4; }
.rec-leyenda {
  display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 8px;
  line-height: 1.5;
}
.rec-leyenda > span { display: inline-flex; align-items: center; gap: 5px; }
.rec-pt { width: 8px; height: 8px; border-radius: 50%; flex: none; display: inline-block; }
.rec-pt-mal { background: var(--danger); }
.rec-pt-flojo { background: var(--warn); }
.rec-pt-bien { background: var(--accent-2); }
.rec-tapa-ej {
  font-size: 9.5px; letter-spacing: .04em; padding: 1px 4px; border-radius: 4px;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent);
}

/* Las cuatro secciones de «Cómo va». Vacías no dejan hueco: el estado sin simulación
   solo llena una. */
.rec-sec:empty { display: none; }

/* «Mide [30 min] de tienda» en una línea: partido en dos se leía como dos etiquetas. */
.af-int { flex-wrap: nowrap; white-space: nowrap; }
.af-int select { flex: none; }

/* La conclusión del reparto: pegada a su tabla, no como párrafo suelto. */
.pg-conclusion { margin: 6px 0 0; line-height: 1.4; }

/* ══════════════════════════════════════════════════════════════════════════════
   EL PANEL, EN UNA COLUMNA CON LO SECUNDARIO PLEGADO.

   Sustituye a las tres pestañas. Plegado NO es escondido: los títulos se leen de arriba
   abajo como un índice —con cuántas cosas hay dentro— que es justo lo que no daban las
   pestañas: dos tercios del panel invisibles y sin una pista de qué contenían.
   ══════════════════════════════════════════════════════════════════════════════ */
.rec-plegable { border-top: 1px solid var(--line); }
.rec-plegable > summary {
  display: flex; align-items: center; gap: 7px; cursor: pointer; list-style: none;
  padding: 10px 2px; color: var(--text-2); font-size: 12.5px; font-weight: 600;
}
.rec-plegable > summary::-webkit-details-marker { display: none; }
.rec-plegable > summary:hover { color: var(--text); }
.rec-plegable > summary svg { flex: none; transition: transform .15s var(--ease); color: var(--muted); }
.rec-plegable[open] > summary svg { transform: rotate(90deg); }
.rec-plegable[open] > summary { color: var(--text); }
.rec-pleg-tit { flex: 1; min-width: 0; }
/* La cuenta: «Mueble a mueble (14)» dice si merece la pena abrirlo; sin ella hay que
   abrirlo para saberlo. */
.rec-pleg-n {
  flex: none; padding: 1px 7px; border-radius: 999px;
  background: var(--panel-2); color: var(--muted); font-size: 11px; font-weight: 600;
}
.rec-plegado-cuerpo { padding: 0 0 12px; }

/* EL ARREGLO DENTRO DEL AVISO. Un desplegable pegado al hallazgo que lo justifica: es la
   única acción de la app y hasta ahora vivía en otra pestaña. */
.diag-fix {
  display: flex; align-items: center; gap: 7px; margin-top: 8px;
  font-size: 12px; color: var(--text-2);
}
.diag-fix > span { flex: none; }
.diag-fix select { flex: 1; min-width: 0; }

/* LA TESIS, arriba del panel. No es un lema: lleva dentro la afluencia de esta tirada,
   así que describe lo que se está mirando. */
.rec-tesis {
  margin: 0 0 12px; padding: 10px 12px; border-radius: var(--r);
  background: var(--panel-2); border-left: 3px solid var(--accent);
  font-size: 12.5px; line-height: 1.45; color: var(--text-2);
}
.rec-tesis b { color: var(--text); }

/* «Lleva» en el panel de propiedades del plano: la primera fila y la más ancha, porque
   para una tienda qué expone un mueble pesa más que cuánto mide. */
.pr-cat select { width: 100%; }
.pr-nocat { margin: 4px 0 10px; line-height: 1.4; }
