/* ==========================================================================
   Estrenova — sistema visual
   Paleta "Índigo y coral" y tipografía Schibsted Grotesk.

   Las vistas siguen la composición del prototipo v2 (barra lateral, columna de
   contenido y aside de 290 px), y el tono tipográfico es el de siempre: caja
   baja, sin versales espaciadas. El wordmark también va en caja baja, pero con
   familia propia (Bricolage Grotesque): es marca, no texto de interfaz.
   ========================================================================== */

/* Las dos familias, servidas desde aquí y no desde Google desde el 30-09-2026:
   son dos conexiones menos antes de pintar el texto y ninguna petición a un
   tercero que el visitante no ha aceptado. Solo el subconjunto latino, que
   cubre el castellano entero, el € y las comillas; los ficheros son las
   variables de Google Fonts tal cual (licencia OFL, public/fonts/LEEME.txt). */
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/schibsted-grotesk-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 600 700;
  font-stretch: 100%;
  font-display: swap;
  src: url("/fonts/bricolage-grotesque-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Paleta "Índigo y coral" */
  --paper: #F8F7FC;
  --surface: #E4E2F2;
  --card: #FFFFFF;
  --ink: #16142B;
  --accent: #3F3AA8;
  --on-accent: #F8F7FC;
  --muted: #55506B;
  /* 5,8 sobre papel y **4,9 sobre `--surface`**, que es la mitad de las
     secciones de la portada. Era #6F6987, que sobre papel daba 4,9 —el número
     que está en IDENTIDAD.md §4— pero sobre superficie se quedaba en 4,07 y
     ahí van los pies de los estilos y el `.disclaimer` de la etiqueta de IA.
     Al medir un gris apagado hay que medirlo contra los dos fondos. */
  --faint: #635D7A;
  --line: rgba(22, 20, 43, .16);
  --ph1: #E4E2F2;
  --ph2: #EFEEF8;

  /* La gama de «coste contra valor»: del precio (el acento) a la reforma (el
     más claro), y el valor reformado en tinta apagada porque no es coste. Viven
     aquí y no en app.js porque las leen las dos: la gráfica con
     getPropertyValue y la clave de debajo con background. */
  --coste-precio: #3F3AA8;
  --coste-gastos: #6F6ABF;
  --coste-reforma: #A9A5DC;
  --coste-valor: #55506B;

  /* El coral es el destello de la marca: pinta el símbolo, el punto del
     comparador y lo que va sobre tinta. Nunca texto sobre papel —ahí se queda
     en 2,4 de contraste— y nunca el error, que tiene color propio. */
  --spark: #FF7A59;

  /* Derivados */
  --danger: #B3382C; /* el rojo de .btn--danger; sin esta variable,
                        .badge--alerta quedaba en blanco sobre blanco */
  --line-soft: rgba(22, 20, 43, .09);
  --accent-soft: rgba(63, 58, 168, .10);
  --accent-line: rgba(63, 58, 168, .38);
  --accent-dark: #302B85;
  --shadow-s: 0 1px 2px rgba(22, 20, 43, .05);
  --shadow-m: 0 12px 28px rgba(22, 20, 43, .08);
  --shadow-l: 0 18px 48px rgba(22, 20, 43, .12);

  --font: "Schibsted Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Solo para el wordmark y el titular de portada. La interfaz no la usa. */
  --font-wm: "Bricolage Grotesque", "Schibsted Grotesk", system-ui, sans-serif;
  --r-s: 8px;
  --r-m: 11px;
  --r-l: 16px;

  --aside: 290px;
  --pad: 28px;
  /* Lo que mide la cabecera de la web pública: 16 de relleno arriba y abajo,
     los 36 del botón y **el filete de un píxel de abajo**, que es el que se
     olvida. Vive aquí porque el hero se descuenta de la pantalla y necesita
     este número; si cambia el relleno de `.site-header`, cambia también aquí. */
  --cabecera: 69px;
  /* Tope del lienzo del render (y del comparador, que va a juego). --marco es
     lo que ocupa en vertical todo lo que NO es el render dentro de la columna:
     cabecera del inmueble, pestañas, chips, título, modos y tira de versiones.
     De ahí sale el alto del render, para que la vista entre sin scroll. */
  --lienzo: 980px;
  --marco: 580px;

  /* Trama diagonal: hueco reservado para fotos reales */
  --stripes: repeating-linear-gradient(45deg, var(--ph1) 0 12px, var(--ph2) 12px 24px);
  --stripes-s: repeating-linear-gradient(45deg, var(--ph1) 0 9px, var(--ph2) 9px 18px);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 15px/1.6 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }
[hidden] { display: none !important; }

::selection { background: var(--accent); color: var(--on-accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* --------------------------------------------------------------- tipografía */

/* Rótulo de sección y rótulo de dato. En caja baja: en versales espaciadas la
   interfaz entera se leía como una ficha técnica. */
.eyebrow {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--accent);
}

.label {
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--faint);
}
.label--ink { color: var(--ink); font-weight: 600; }
.eyebrow--ico { display: inline-flex; align-items: center; gap: 7px; }

.h-display { font-size: clamp(34px, 5.2vw, 52px); font-weight: 600; letter-spacing: -1.6px; line-height: 1.08; text-wrap: balance; margin: 0; }
.h-1 { font-size: clamp(26px, 3.4vw, 34px); font-weight: 600; letter-spacing: -.9px; line-height: 1.15; margin: 0; }
.h-2 { font-size: 24px; font-weight: 600; letter-spacing: -.6px; line-height: 1.2; margin: 0; }
.h-3 { font-size: 18px; font-weight: 600; letter-spacing: -.3px; line-height: 1.3; margin: 0; }
em { color: var(--accent); font-style: normal; }

/* La fuente de marca, en la web pública y solo ahí.

   IDENTIDAD.md §1 la carga «para el wordmark y los titulares de portada», pero
   los titulares se quedaron en Schibsted: se descargaba una familia entera para
   una palabra de 16 px, y la portada se leía como una pantalla más de la
   aplicación. El corte va por tamaño: display y títulos de sección en la fuente
   de marca, de `.h-3` para abajo en la de interfaz, que es donde manda leer y
   no sonar.

   Va colgado de `.web` —la clase que pone el generador en el `body`— y no del
   elemento, porque `.h-1` y `.h-2` son también clases de la app y ahí la
   interfaz no la usa. */
.web .h-display, .web .h-1, .web .h-2 { font-family: var(--font-wm); }
/* Y equilibrado de líneas, que `.h-display` ya tenía y estos no: en columna
   estrecha «Qué es gratis y qué se paga» dejaba «paga» sola en el segundo
   renglón. */
.web .h-1, .web .h-2 { text-wrap: balance; }
/* Bricolage aprieta más que Schibsted al mismo tracking: con -1.6px las
   mayúsculas del titular se tocaban. */
.web .h-display { letter-spacing: -1.2px; }
.web .h-1 { letter-spacing: -.7px; }

.lede { font-size: 17px; line-height: 1.62; color: var(--muted); text-wrap: pretty; margin: 0; }
.body-s { font-size: 13.5px; line-height: 1.6; color: var(--muted); }
.micro { font-size: 12px; line-height: 1.5; color: var(--faint); }

.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.accent { color: var(--accent); }
.ico { display: block; flex: none; }

/* -------------------------------------------------------------------- logo */
/* El destello del estreno dentro del vano, más el wordmark. El símbolo es un
   SVG en línea y no un <span> con border-radius como el arco de antes: así el
   marco hereda el color por `currentColor` y el estado de hover se resuelve
   con una sola declaración en el enlace. */

.logo { display: inline-flex; align-items: center; gap: 8px; color: var(--accent); }
.logo__mark { width: 19px; height: 19px; flex: none; display: block; }
.logo__base { fill: var(--spark); }
.logo__word {
  font-family: var(--font-wm);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -.7px;
  color: var(--ink);
  white-space: nowrap;
}
.logo--lg .logo__mark { width: 27px; height: 27px; }
.logo--lg .logo__word { font-size: 23px; letter-spacing: -1px; }

/* El logo es un enlace, y tiene que parecerlo. El relleno le da un área de
   pulsación cómoda y el margen negativo la devuelve al hueco de siempre, así
   que crece hacia fuera sin descolocar nada. El aviso al pasar por encima va en
   el color y no en un fondo: el logo vive sobre la barra lateral y sobre la
   cabecera blanca, y un fondo solo se vería en una de las dos. */
.logo-link {
  display: inline-flex;
  align-items: center;
  padding: 8px 10px;
  margin: -8px -10px;
  border-radius: var(--r-s);
}
.logo-link:hover .logo__word { color: var(--accent); }
.logo-link:hover .logo { color: var(--accent-dark); }
.logo-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ------------------------------------------------------------------ botones */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: var(--r-s);
  padding: 12px 22px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  transition: background .15s, border-color .15s, color .15s, transform .08s;
  text-align: center;
}
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .45; pointer-events: none; }

.btn--accent { background: var(--accent); color: var(--on-accent); }
.btn--accent:hover { background: var(--accent-dark); }

.btn--ghost { background: var(--card); color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { border-color: var(--ink); }

/* Contorno en acento: la secundaria del mismo tema que la principal. */
.btn--line { background: transparent; color: var(--accent); border-color: var(--accent-line); }
.btn--line:hover { background: var(--accent-soft); border-color: var(--accent); }

.btn--quiet { background: transparent; color: var(--muted); }
.btn--quiet:hover { color: var(--ink); }

.btn--danger { background: var(--card); color: var(--danger); border-color: rgba(179, 56, 44, .34); }
.btn--danger:hover { background: rgba(179, 56, 44, .07); }

.btn--sm { padding: 9px 16px; font-size: 13px; }
.btn--xs { padding: 8px 13px; font-size: 12.5px; }
.btn--lg { padding: 14px 28px; font-size: 15px; }
.btn--block { width: 100%; }

.link {
  color: var(--accent);
  font-weight: 600;
  background: none;
  border: 0;
  padding: 0;
  font-size: inherit;
  text-align: left;
}
.link:hover { text-decoration: underline; }
/* Con icono al lado, el enlace se alinea en línea base y no lo subraya. */
.link:has(.ico) { display: inline-flex; align-items: center; gap: 5px; }
.link:has(.ico):hover { text-decoration: none; color: var(--accent-dark); }

/* ------------------------------------------------------- campos de formulario */

.field { display: flex; flex-direction: column; gap: 6px; }
.field > .label { color: var(--muted); }

.input, .textarea, .select {
  width: 100%;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  padding: 11px 13px;
  font-size: 14px;
  color: var(--ink);
  transition: border-color .15s, box-shadow .15s;
}
.input::placeholder, .textarea::placeholder { color: var(--faint); }
/* Fuera del rango que admite el servidor: se marca y no se guarda (wireInversion). */
.input[aria-invalid="true"] { border-color: var(--danger); }
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.textarea { min-height: 84px; resize: vertical; line-height: 1.55; }
.select { appearance: none; padding-right: 34px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 17px) 51%, calc(100% - 12px) 51%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.input--sm { padding: 8px 11px; font-size: 13px; }
select.input--sm { padding-right: 30px; }

.form-row { display: flex; gap: 14px; flex-wrap: wrap; }
.form-row > * { flex: 1; min-width: 150px; }

/* --------------------------------------------------------- chips y etiquetas */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
  transition: border-color .13s, color .13s, background .13s;
}
.chip:hover { border-color: var(--ink); color: var(--ink); }
/* Seleccionado en acento, no en tinta: el negro sobre blanco pesaba demasiado
   repetido por toda la interfaz. */
.chip[aria-pressed="true"], .chip[aria-current="true"], .chip--puesto {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent);
  font-weight: 600;
}
.chip--sm { padding: 6px 12px; font-size: 12px; }
.chip--add { border-style: dashed; }
.chip--wrap { white-space: normal; text-align: left; }
.chip--off { opacity: .6; }

/* Insignia de dato: DIY en acento, profesional en tinta. */
.badge {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 11px;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  white-space: nowrap;
}
.badge--diy { border-color: var(--accent-line); color: var(--accent); background: var(--accent-soft); }
.badge--soft { color: var(--muted); background: var(--surface); border-color: transparent; }
.badge--on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* Grupo segmentado */
.seg {
  display: inline-flex;
  background: var(--surface);
  border-radius: var(--r-s);
  padding: 3px;
  gap: 2px;
}
.seg > * {
  border: 0;
  background: none;
  border-radius: 6px;
  padding: 8px 15px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--faint);
  text-align: center;
  white-space: nowrap;
}
.seg > *:hover { color: var(--ink); }
.seg > [aria-pressed="true"], .seg > [aria-selected="true"] {
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow-s);
}
.seg--sm > * { padding: 6px 11px; font-size: 12.5px; }
.seg--block { display: flex; width: 100%; }
.seg--block > * { flex: 1; padding: 9px 0; }

/* Pestañas del resultado */
.tabs { display: flex; align-items: center; gap: 6px; }
.tabs > * {
  border: 0;
  background: none;
  border-radius: 999px;
  padding: 9px 16px;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--muted);
}
.tabs > *:hover { color: var(--ink); }
.tabs > [aria-selected="true"] { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

.only-sm { display: none; }

/* ----------------------------------------------------------- superficies */

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
}
.card--pad { padding: 22px 24px; }

.note {
  background: var(--surface);
  border-radius: var(--r-m);
  padding: 16px 18px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
}
.note b { color: var(--ink); }
/* Un aviso que además pide una acción: el texto y el botón en la misma línea.
   El botón en su propio párrafo debajo convertía una línea en un bloque. */
.note--accion {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap; margin-top: 24px;
}
.note--accion .btn { flex: none; }
/* El aviso que corrige a lo que tiene debajo: cifras viejas, una lectura que
   ya no cuadra. Lleva filo de color a la izquierda porque no es una nota más
   del montón, es la que dice que no te fíes de la siguiente. */
.note--aviso {
  border-left: 3px solid var(--danger);
  background: color-mix(in srgb, var(--danger) 6%, var(--surface));
}
.note--aviso > p + .kv { border-top: 1px solid var(--line); padding-top: 10px; }
.note--aviso .kv--rows > div { padding: 3px 0; }

.ph { background: var(--stripes); }
.ph--label { display: grid; place-items: center; text-align: center; font-size: 12px; line-height: 1.6; color: var(--faint); }

.stamp {
  position: absolute;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .8px;
  padding: 5px 10px;
  border-radius: 5px;
  background: rgba(22, 20, 43, .78);
  color: var(--paper);
  backdrop-filter: blur(3px);
}
.stamp--accent { background: var(--accent); color: var(--on-accent); }
.stamp--light { background: rgba(255, 255, 255, .92); color: var(--ink); }
.stamp--bl { left: 16px; bottom: 16px; }
.stamp--br { right: 16px; bottom: 16px; }
.stamp--tl { left: 14px; top: 14px; }
.stamp--tr { right: 14px; top: 14px; }

/* La etiqueta de IA (AI Act art. 50). Más discreta que los demás sellos —no
   compite con el contenido— pero nunca invisible: no puede depender de un
   hover ni desaparecer en pantallas pequeñas, porque entonces no cumple. */
.stamp--ia {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: rgba(255, 255, 255, .88);
  color: var(--muted);
  font-weight: 500;
  letter-spacing: .2px;
  backdrop-filter: blur(3px);
}
.stamp--ia .ico { opacity: .75; }

/* La marca de agua de los planes personales. Arriba a la derecha: la esquina
   que queda libre entre el número de versión (arriba-izquierda), la etiqueta
   de IA (abajo-izquierda) y las herramientas (abajo-derecha).

   Legible pero sin pelearse con la imagen: quien mira su propio piso no debe
   sentir que le han estropeado el render. Su trabajo es que no se pueda
   publicar en un portal como si fuera de la agencia, y para eso basta. */
.marca-agua {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  pointer-events: none;
  color: #fff;
  opacity: .82;
  text-shadow: 0 1px 4px rgba(22, 20, 43, .45);
}
.marca-agua .logo { gap: 5px; }
.marca-agua .logo__mark { width: 15px; height: 15px; }
.marca-agua .logo__word { font-size: 13px; letter-spacing: .2px; }
/* En papel el fondo es blanco: el blanco sobre blanco desaparece. */
@media print { .marca-agua { color: var(--muted); opacity: .7; text-shadow: none; } }

/* La misma etiqueta dentro del visor a pantalla completa, donde el sello de
   la esquina no existe porque la imagen se pinta sola sobre el fondo. */
.lightbox__ia {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: 10px;
  opacity: .8;
}

.rule { border: 0; border-top: 1px solid var(--line); margin: 0; }

/* Barra de reparto DIY / profesional. */
.split { display: flex; height: 8px; border-radius: 999px; background: var(--surface); overflow: hidden; }
.split__diy { background: var(--accent); border-radius: 999px; }
.split__pro { flex: 1; }
.split-legend {
  display: flex;
  justify-content: space-between;
  margin-top: 10px;
  font-size: 12px;
  color: var(--muted);
}

.meter { height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; }
.meter__fill { height: 100%; border-radius: 3px; background: var(--accent); transition: width .3s; }

/* Lista clave / valor de los asides. */
.kv { display: flex; flex-direction: column; }
.kv > div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
.kv b { font-weight: 500; color: var(--ink); text-align: right; }
.kv--rows > div { padding: 13px 0; border-bottom: 1px solid var(--line-soft); font-size: 14px; }
.kv--rows > div > span:first-child { color: var(--faint); }
/* Tres columnas de escenario (prudente, base, optimista) en la proyección. */
.kv--3 > div { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 10px; align-items: baseline; }
.kv--3 > div > span:not(:first-child) { text-align: right; }

/* ================================================================== LANDING */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 48px;
  background: rgba(248, 247, 252, .88);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line-soft);
}
/* La marca, un punto más grande que en la aplicación. En la barra de la web
   pública es lo único que dice de quién es la página, y a 16 px se perdía entre
   los cuatro enlaces. Va colgado de `.site-header` para no tocar el logo de la
   barra lateral, donde el tamaño de ahora es el correcto.

   No crece la cabecera: manda el alto del botón (36 px), y 22 sigue por debajo.
   Si esto sube más, hay que mover `--cabecera` y el hero con él. */
.site-header .logo__mark { width: 22px; height: 22px; }
.site-header .logo__word { font-size: 18.5px; letter-spacing: -.85px; }

.site-nav { display: flex; align-items: center; gap: 28px; font-size: 13.5px; color: var(--muted); }
.site-nav a:hover { color: var(--ink); }
.site-nav a[aria-current="page"] { color: var(--ink); font-weight: 600; }
.site-header__actions { display: flex; gap: 14px; align-items: center; }

/* La navegación de móvil.

   Existe porque no existía: por debajo de 860 px la barra se ocultaba entera y
   no la sustituía nada, así que en un teléfono —que es desde donde se mira un
   anuncio— Precios y Preguntas solo vivían en el pie, a cuatro mil píxeles de
   scroll.

   Es un `<details>` y no un menú con JavaScript a propósito: las páginas
   públicas no cargan guion ninguno, y el desplegable nativo ya trae el teclado,
   el rol y el estado sin que haya que escribirlos. Se cierra al volver a
   pulsar; no se cierra al tocar fuera, que es el precio de no traer un guion
   para cuatro enlaces. */
.nav-movil { display: none; }
.nav-movil > summary {
  list-style: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin: -2px;
  border-radius: var(--r-s);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
}
.nav-movil > summary::-webkit-details-marker { display: none; }
.nav-movil > summary::marker { content: ""; }
.nav-movil[open] > summary { background: var(--surface); border-color: var(--accent-line); }
/* Las dos rayas de en medio se cruzan al abrir: el mismo control dice qué hace
   ahora sin cambiar de icono ni de sitio. */
.nav-movil__ico line { transition: transform .18s ease, opacity .18s ease; transform-origin: center; }
.nav-movil[open] .nav-movil__ico line:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-movil[open] .nav-movil__ico line:nth-child(2) { opacity: 0; }
.nav-movil[open] .nav-movil__ico line:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
/* El panel cuelga de la cabecera, no del botón: a lo ancho de la ventana los
   cuatro rótulos caben en una línea cada uno y no hay que decidir un ancho.
   `top: 100%` sobre la cabecera y no una altura escrita, porque por debajo de
   520 px la barra envuelve a dos filas y crece. */
.nav-movil__panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-m);
  padding: 6px 0 10px;
}
.nav-movil__panel a {
  padding: 13px 20px;
  font-size: 15px;
  color: var(--muted);
  border-top: 1px solid var(--line-soft);
}
.nav-movil__panel a:first-child { border-top: 0; }
.nav-movil__panel a[aria-current="page"] { color: var(--accent); font-weight: 600; }

.section { padding: 60px 48px; }
.section--surface { background: var(--surface); }
.wrap { max-width: 1240px; margin: 0 auto; }

/* Héroe a dos columnas: el argumento a la izquierda, la prueba a la derecha. */
.hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-bottom: 1px solid var(--line);
}

/* El hero ocupa la pantalla, menos lo que mide la cabecera pegajosa: así el
   primer golpe de vista es la pregunta y su respuesta, y nada más.

   `dvh` y no `vh`: en un móvil `100vh` cuenta la ventana sin la barra del
   navegador, así que el bloque sobresale por abajo justo hasta que alguien
   hace scroll. Se deja el `vh` delante como respaldo para quien no entienda
   `dvh`.

   Solo por encima de 1080, que es donde el hero es de dos columnas. Apilado no
   tiene sentido: repartir una pantalla entre el texto y la tarjeta deja las dos
   mitades apretadas, y en un teléfono el texto ya llena la primera pantalla él
   solo. */
@media (min-width: 1081px) {
  .hero {
    min-height: calc(100vh - var(--cabecera));
    min-height: calc(100dvh - var(--cabecera));
  }
}
/* Centrado en vertical, no colgando de arriba: con el titular en dos líneas la
   columna de texto mide la mitad que la de la tarjeta, y alineadas por arriba
   el hero se quedaba cojo. */
.hero__text { padding: 64px 48px 60px; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; }
.hero__text .h-display { max-width: 560px; }
.hero__text .lede { margin-top: 20px; max-width: 460px; }
.hero__cta { display: flex; align-items: center; gap: 12px; margin-top: 30px; flex-wrap: wrap; }
.hero__media { border-left: 1px solid var(--line); position: relative; min-height: 460px; background: var(--stripes); }

/* ------------------------------------------------ el hero de la portada

   Lo que va en el hero es lo que vendes. Desde el 26-08-2026 va el informe y
   no el comparador de renders: la ventaja de este producto es el euro, no la
   imagen, y la maquetación tiene que decir lo mismo que el plan de negocio.
   El render baja a su sección, a media página. */

.hero--informe .hero__media {
  display: grid;
  place-items: center;
  padding: 44px 40px;
  /* Una fachada de calle detrás de la tarjeta, no una trama de rayas: la
     trama es relleno de maqueta y detrás de la pieza que más manda decía
     «esto es un mockup».

     El velo va en el color del papel y no en negro, que es lo que separa un
     fondo de una foto oscurecida: la tarjeta tiene que despegarse sin que la
     página cambie de temperatura. Y el `background-color` debajo sostiene el
     bloque mientras la imagen carga y si no llega. */
  background-color: var(--paper);
  background-image:
    linear-gradient(180deg, rgba(248, 247, 252, .52) 0%, rgba(248, 247, 252, .80) 100%),
    url("/samples/hero-fondo.webp");
  background-size: cover;
  background-position: center 30%;
}
.informe {
  width: min(420px, 100%);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-l);
  overflow: hidden;
}
.informe__cab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 22px 18px;
}
.informe__dir { font-weight: 600; font-size: 15px; }
/* La tira de cifras del informe vive sin sus filetes exteriores aquí dentro:
   la tarjeta ya tiene borde y dos líneas seguidas se leen como un error. */
.informe .kpis { border-top: 1px solid var(--line); border-bottom: 0; }
.informe .kpi { padding: 16px 22px 18px; }

.informe__filas { padding: 6px 22px 20px; border-top: 1px solid var(--line); }
.informe__filas > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 9px 0;
  font-size: 13.5px;
  color: var(--muted);
}
.informe__filas > div + div { border-top: 1px solid var(--line-soft); }
.informe__filas b { color: var(--ink); font-weight: 600; }
.informe__total { margin-top: 2px; }
.informe__total span { color: var(--ink); font-weight: 600; }
.informe__total b { color: var(--accent); font-size: 15px; }

/* ------------------------------------------------ secciones de la portada */

/* Rejilla de fichas de texto: «lo que el anuncio no te dice».
   `auto-fit` y no un número fijo, que es la lección de `.plans`: en cuanto
   cambia el número de entradas, una rejilla fija deja una huérfana de fila. */
/* 200 y no 240: con tres pruebas en el ancho medio (760 px) y 22 de hueco,
   240 se pasa por cuatro píxeles y la tercera ficha caía sola a otra fila.

   Y `auto-fit` a secas tampoco basta: con cuatro fichas necesita 866 px y en
   una ventana de 900 hay 794, así que en tableta caía a tres y dejaba una
   huérfana —el mismo fallo, 140 px más arriba. Por eso la banda intermedia va
   a dos columnas explícitas: cuatro, dos o una, nunca tres. */
.fichas { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 22px; }
@media (max-width: 1080px) { .fichas { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .fichas { grid-template-columns: 1fr; } }
.ficha {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  padding: 24px 24px 26px;
}
.ficha h3 { margin-bottom: 10px; }

.wrap--medio { max-width: 760px; }

/* FAQ y página citable: preguntas como encabezados de verdad. Un `h3` por
   pregunta y un párrafo que se entiende solo, porque es la unidad que un
   modelo extrae y cita — un párrafo que necesita el de al lado no sirve. */
.faq-grupo { margin-top: 40px; scroll-margin-top: calc(var(--cabecera) + 20px); }
.faq-grupo > .h-2 { padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.faq-item { margin-top: 24px; }
.faq-item .h-3 { margin-bottom: 8px; }
/* Sin tope de anchura propio: el `wrap--medio` de 760 ya es la medida de
   lectura de estas páginas, y un segundo tope dentro partía los párrafos de
   las respuestas a media columna mientras los del grupo iban a ancho completo. */

/* ------------------------------------- piezas de las páginas informativas

   /how-it-works, /how-we-calculate y las de segmento eran titulares con
   párrafos corridos. Estas piezas les dan cuerpo sin traer la maquinaria de
   la galería ni el bundle de la app: el recorrido numerado, la pregunta que
   se pliega, la captura enmarcada y la malla de datos. */

/* El recorrido: número a la izquierda, el paso al lado y su prueba —fichas,
   tarjeta o comparador— dentro de `paso__pieza`. La variante densa es para
   sub-pasos bajo un encabezado que ya ordena. */
.paso { display: grid; grid-template-columns: 54px 1fr; gap: 4px 20px; padding: 32px 0 36px; }
.paso + .paso { border-top: 1px solid var(--line); }
.paso__num { font-size: 27px; font-weight: 600; letter-spacing: -1px; color: var(--accent); line-height: 1.2; font-variant-numeric: tabular-nums; }
.paso__cuerpo > .h-2, .paso__cuerpo > .h-3 { margin-bottom: 8px; }
.paso__pieza { margin-top: 22px; }
.recorrido--denso .paso { padding: 20px 0 22px; }
.recorrido--denso .paso + .paso { border-top: 1px solid var(--line-soft); }
@media (max-width: 560px) {
  .paso { grid-template-columns: 1fr; }
  .paso__num { font-size: 21px; }
}

/* La barra de pegar el enlace, dibujada: es la instrucción hecha imagen, no
   un formulario. El botón sí es de verdad y lleva a la aplicación. */
.demo-enlace { display: flex; align-items: center; gap: 12px; max-width: 560px; padding: 9px 9px 9px 16px; background: var(--card); border: 1px solid var(--line); border-radius: 999px; box-shadow: var(--shadow-s); }
.demo-enlace__url { display: flex; flex: 1; min-width: 0; align-items: center; gap: 8px; color: var(--faint); font-size: 13px; }
.demo-enlace__url span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.demo-enlace__url svg { flex: none; opacity: .75; }
.demo-enlace .btn { flex: none; }

/* La pregunta que se pliega. El texto está entero en el HTML —plegar es
   maquetación, no ocultación, y un rastreador lo lee igual— y la primera de
   la lista nace abierta para que se vea que esto se abre. La lista es plana:
   los grupos con índice de píldoras duraron una tarde, liaban más que
   ordenaban. */
.pliegue { border-bottom: 1px solid var(--line-soft); }
.pliegue:first-child { border-top: 1px solid var(--line-soft); }
.pliegue summary { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 16px 0; cursor: pointer; list-style: none; }
.pliegue summary::-webkit-details-marker { display: none; }
.pliegue summary .h-3 { margin: 0; }
.pliegue__ind { flex: none; color: var(--faint); transition: transform .15s; }
.pliegue[open] .pliegue__ind { transform: rotate(45deg); color: var(--accent); }
.pliegue > .body-s { padding: 0 34px 18px 0; max-width: 68ch; }

/* Una captura enmarcada, sin la maquinaria de radios de la galería: la misma
   barra de navegador, la imagen y su pie. */
.lamina { margin-top: 32px; }
.lamina__marco { position: relative; display: block; border: 1px solid var(--line); border-radius: var(--r-l); overflow: hidden; background: var(--card); box-shadow: var(--shadow-m); }
.lamina__marco img { display: block; width: 100%; height: auto; }

/* La malla de datos: una celda por comunidad con su tipo. La tabla que nadie
   lee, en la malla que sí se mira. */
.malla-datos { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 10px; margin-top: 20px; }
.malla-datos > div { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 11px 14px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r-m); font-size: 12.5px; color: var(--muted); }
.malla-datos b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* La muestra del catálogo reutiliza la comparativa de planes, pero sus
   cabeceras de fila son nombres de partida y no caben sin partirse. */
.tabla-planes--partidas tbody th { white-space: normal; }
.tabla-planes--partidas thead th:first-child { text-align: left; }

/* Los tres «no» de la página citable, sin el filete que la lista trae de las
   tarjetas de plan. Doble clase porque `.lista-incluye` se define más abajo y
   a igual especificidad ganaría ella. Cruces apagadas, no rojas: es un
   deslinde, no un aviso. */
.lista-incluye.lista-no { margin-top: 0; padding-top: 0; border-top: 0; }
.lista-incluye.lista-no .ico { color: var(--faint); }

/* El pie pasó a llevar el mapa del sitio: es el enlazado interno que hace
   rastreables las páginas de segmento, que si no cuelgan de nada. */
/* Una tira de enlaces sobre papel. Se llamaba `.site-footer__nav` de cuando el
   pie era eso mismo; ahora el pie va por columnas y sobre tinta, y quien la usa
   es «Otras provincias». El nombre dice lo que es. */
.lista-enlaces { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.lista-enlaces a { color: var(--muted); }
.lista-enlaces a:hover { color: var(--ink); }

/* La tabla comparativa de precios. Cabeceras pegajosas no: son nueve filas y
   scroll horizontal en móvil, no una hoja de cálculo. */
.tabla-planes { width: 100%; border-collapse: collapse; font-size: 13.5px; min-width: 640px; }
.tabla-planes th, .tabla-planes td { padding: 11px 14px; text-align: center; }
.tabla-planes thead th { font-size: 13px; font-weight: 600; color: var(--ink); border-bottom: 1px solid var(--line); }
.tabla-planes tbody th {
  text-align: left;
  font-weight: 400;
  color: var(--muted);
  white-space: nowrap;
}
.tabla-planes tbody tr + tr th, .tabla-planes tbody tr + tr td { border-top: 1px solid var(--line-soft); }
.tabla-planes tbody td { color: var(--ink); }
.lede--sec { margin-top: 22px; }
.dos-columnas { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; margin-top: 22px; }
.dos-columnas .lista-incluye { margin-top: 14px; }

/* La línea de pago: el argumento en una columna y las dos listas al lado.

   Antes iba en el ancho medio de 760 px, entre secciones que van a 1240, y ese
   vaivén de medida no significaba nada: le quitaba presencia justo a la sección
   que explica el modelo de negocio. Ahora va al ancho completo, y en tres
   columnas y no en dos porque a 620 px por tarjeta la lista pasaba de los 90
   caracteres por línea. El ancho medio se queda solo donde hay prosa seguida,
   que es cuando querer 760 quiere decir algo. */
.pago { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 26px; align-items: start; }
.pago__tesis { padding-right: 12px; }
.pago .lista-incluye { margin-top: 14px; }
@media (max-width: 1080px) {
  .pago { grid-template-columns: 1fr 1fr; }
  .pago__tesis { grid-column: 1 / -1; padding-right: 0; }
}
@media (max-width: 620px) { .pago { grid-template-columns: 1fr; } }

/* 1.15 y no 1.35: con el comparador a 694 px la sección del render era el
   bloque más largo de la portada, y el render es lo accesorio. */
.render-demo { display: grid; grid-template-columns: 1.15fr 1fr; gap: 32px; align-items: center; }
.fuentes { display: grid; gap: 24px; margin-top: 8px; }
.fuentes h3 { margin-bottom: 8px; }

@media (max-width: 900px) {
  .render-demo { grid-template-columns: 1fr; gap: 22px; }
}

/* Cómo funciona: tres columnas separadas por filete, sin tarjetas. */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); }
.step { padding: 34px 40px 38px; }
.step + .step { border-left: 1px solid var(--line); }
.step__num { font-size: 30px; font-weight: 600; letter-spacing: -1px; color: var(--accent); }
.step h3 { margin: 12px 0 8px; }

/* ---------------------------------------------- la banda de cifras

   Va pegada al hero y antes de que empiece la prosa. Está porque la portada
   contaba la ventaja con adjetivos: fuera de la tarjeta del hero y de la lista
   de tarifas había dos cifras en toda la página, y la ventaja de este producto
   es el euro. Todo lo que se imprime aquí sale del catálogo y de la base, nunca
   de la plantilla.

   Es además el único fondo tinta de la portada, que es donde el coral tiene
   contraste (7,0) y por tanto el único sitio donde la paleta puede enseñar su
   segundo color sin saltarse su propia regla. */
.cifras {
  background: var(--ink);
  color: var(--on-accent);
  padding: 44px 48px 0;
  border-bottom: 4px solid var(--spark);
}
.cifras__cab { max-width: 1240px; margin: 0 auto 26px; }
.cifras__cab .eyebrow { color: var(--spark); }
.cifras__cab p { margin: 8px 0 0; font-size: 15px; line-height: 1.55; color: rgba(248, 247, 252, .72); max-width: 62ch; }
.cifras__lista {
  max-width: 1240px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 28px;
  padding-bottom: 40px;
}
.cifra b {
  display: block;
  font-family: var(--font-wm);
  font-size: clamp(30px, 3.6vw, 42px);
  font-weight: 700;
  letter-spacing: -1.4px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.cifra span { display: block; margin-top: 10px; font-size: 13px; line-height: 1.45; color: rgba(248, 247, 252, .72); }

/* ---------------------------------------------- la galería de capturas

   Una grande y las demás en miniatura, y se cambia pulsando. Con radios y
   `:checked`, sin una línea de JavaScript: las páginas públicas no cargan
   guiones, y el grupo de radios ya trae de fábrica lo que costaría escribir a
   mano —foco, teclado y estado— mejor de lo que lo haría uno.

   Apiladas a lo largo eran 2.250 px de portada, casi la mitad de lo que había.
   Y no se pueden encoger sin más: una interfaz reducida a 400 px no se lee, y
   una captura que no se lee no prueba nada. Así que en vez de cuatro pequeñas,
   una legible y tres esperando turno.

   Se sirven a 1600 y 1800 px de ancho para una caja de 1240: el texto de una
   interfaz necesita más resolución que una foto de un salón, y por eso estas
   no pasan por `optimizar-muestras.sh`, que baja a 1280 con calidad 80. */
/* 1040 y no los 1240 de la columna: a lo ancho entero la captura grande se iba
   a 1.024 px de alto ella sola. Alineada a la izquierda y no centrada, para que
   su filete cuadre con el del titular de la sección. */
.galeria { max-width: 1040px; margin-top: 24px; }

/* Fuera de la vista pero no fuera del alcance del teclado: `display:none` o
   `visibility:hidden` los sacarían del recorrido y la galería dejaría de
   poder recorrerse con las flechas. */
.galeria__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

.galeria__vista { display: none; margin: 0; }
.galeria__marco {
  display: block;
  position: relative;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  box-shadow: var(--shadow-m);
  overflow: hidden;
}

/* La barra del navegador. No es decoración: es lo que hace que la imagen se
   lea como «esto es la plataforma» en el primer vistazo, sin tener que
   escribirlo debajo. Va dentro del marco recortado, así que sus esquinas de
   arriba las redondea el propio `overflow`.

   Y va como va un navegador de verdad —luces, flechas y un campo ancho pegado
   a la izquierda—, no como un título de ventana centrado: la dirección
   centrada en mitad de la barra es justo lo que delata que es un adorno. */
.galeria__barra {
  display: flex;
  align-items: center;
  gap: 14px;
  height: 42px;
  padding: 0 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* Las tres luces con un solo elemento: la primera es el fondo y las otras dos
   son sombras desplazadas. Tres `<span>` para tres círculos era ruido en el
   HTML de todas las capturas. */
.galeria__luces {
  flex: none;
  width: 11px;
  height: 11px;
  margin-right: 36px;
  border-radius: 50%;
  background: #FF5F57;
  box-shadow: 18px 0 0 #FEBC2E, 36px 0 0 #28C840;
}
/* Atrás y adelante, apagadas: en una captura no llevan a ningún sitio y
   pintarlas en tinta invita a pulsarlas. */
.galeria__flechas {
  display: flex;
  flex: none;
  gap: 10px;
  color: var(--faint);
  opacity: .8;
}
/* El campo de la dirección: ancho, a ras de izquierda y ocupando todo lo que
   sobra, que es como se ve en cualquier navegador. */
.galeria__url {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 12px;
  border-radius: 8px;
  background: var(--card);
  border: 1px solid var(--line-soft);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1;
  color: var(--faint);
}
.galeria__url span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.galeria__url svg { flex: none; opacity: .75; }

/* La imagen va en su propia caja porque en móvil es esta la que arrastra en
   horizontal, y la barra de ventana tiene que quedarse quieta. */
.galeria__foto { display: block; }
.galeria__foto img { display: block; width: 100%; height: auto; }

/* El aviso de que la imagen se abre entera. Aparece al pasar por encima o al
   llegar con el tabulador; en un táctil no hay «encima», así que ahí se queda
   siempre puesto. */
.galeria__lupa {
  position: absolute;
  right: 12px;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 999px;
  background: rgba(22, 20, 43, .82);
  color: var(--on-accent);
  font-size: 12px;
  font-weight: 600;
  opacity: 0;
  transition: opacity .15s;
}
.galeria__marco:hover .galeria__lupa,
.galeria__marco:focus-visible .galeria__lupa { opacity: 1; }
@media (hover: none) { .galeria__lupa { opacity: 1; } }

.galeria__pie {
  margin: 13px 0 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--muted);
  max-width: 72ch;
}
.galeria__pie b { color: var(--ink); font-weight: 600; }

/* La tira de miniaturas. Cada una es la misma imagen recortada por CSS y no un
   fichero aparte: así el navegador ya la tiene descargada y decodificada
   cuando se pulsa, y el cambio es instantáneo en vez de un parpadeo. */
.galeria__tiras {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-top: 22px;
}
.galeria__tira {
  display: block;
  cursor: pointer;
  border-radius: var(--r-s);
}
.galeria__tira span {
  display: block;
  margin-top: 9px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--muted);
}
.galeria__tira img {
  display: block;
  width: 100%;
  height: 92px;
  object-fit: cover;
  object-position: top left;
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  background: var(--card);
  transition: border-color .15s, box-shadow .15s;
}
.galeria__tira:hover img { border-color: var(--accent-line); }

/* Los cuatro radios van juntos al principio de `.galeria`, antes del lienzo y
   de la tira: es la única colocación desde la que un radio alcanza a las dos
   con el combinador de hermano. De ahí que cada regla lleve sus cuatro
   selectores en vez de uno con `.galeria__radio`. */
#cap-report:checked ~ .galeria__lienzo .galeria__vista--report,
#cap-price:checked ~ .galeria__lienzo .galeria__vista--price,
#cap-works:checked ~ .galeria__lienzo .galeria__vista--works,
#cap-library:checked ~ .galeria__lienzo .galeria__vista--library { display: block; }

/* La seleccionada: filete en acento y el rótulo en tinta. El color no va solo
   —el borde engorda y el peso del texto cambia—, que es lo que hace que se vea
   cuál está puesta sin depender de distinguir dos azules. */
#cap-report:checked ~ .galeria__tiras .galeria__tira--report img,
#cap-price:checked ~ .galeria__tiras .galeria__tira--price img,
#cap-works:checked ~ .galeria__tiras .galeria__tira--works img,
#cap-library:checked ~ .galeria__tiras .galeria__tira--library img {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}
#cap-report:checked ~ .galeria__tiras .galeria__tira--report span,
#cap-price:checked ~ .galeria__tiras .galeria__tira--price span,
#cap-works:checked ~ .galeria__tiras .galeria__tira--works span,
#cap-library:checked ~ .galeria__tiras .galeria__tira--library span { color: var(--ink); }

#cap-report:focus-visible ~ .galeria__tiras .galeria__tira--report img,
#cap-price:focus-visible ~ .galeria__tiras .galeria__tira--price img,
#cap-works:focus-visible ~ .galeria__tiras .galeria__tira--works img,
#cap-library:focus-visible ~ .galeria__tiras .galeria__tira--library img {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------------------------------------------- el caso trabajado

   Las dos superficies enfrentadas y lo que le hace al euro por metro. Vivía
   metido dentro de una ficha de 13,5 px, que es donde no lo lee nadie. */
.caso { display: grid; grid-template-columns: 1.1fr .9fr; gap: 40px; align-items: center; }
.caso__cifras { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.caso__cara { padding: 22px 24px 24px; border: 1px solid var(--line); background: var(--card); }
.caso__cara:first-child { border-radius: var(--r-l) 0 0 var(--r-l); border-right: 0; }
.caso__cara:last-child { border-radius: 0 var(--r-l) var(--r-l) 0; border-color: var(--accent-line); box-shadow: inset 0 0 0 1px var(--accent-line); }
/* Sin `tabular-nums`, que es lo que hace `.num`: aquí no hay columna que
   alinear y la coma tabular de Schibsted ocupa un dígito entero, así que
   «108,8» se leía «108 , 8». Las cifras que se comparan a ojo van con la
   familia de marca y con espaciado normal; el tabular es para las que se
   apilan, como la tira del informe. */
.caso__cara b {
  display: block;
  margin-top: 10px;
  font-family: var(--font-wm);
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -1.1px;
  line-height: 1;
  white-space: nowrap;
}
.caso__cara:last-child b { color: var(--accent); }
.caso__cara span { display: block; margin-top: 8px; font-size: 12.5px; color: var(--faint); }
/* El remate: lo que las dos cifras de arriba le hacen al precio por metro. */
.caso__saldo { margin-top: 18px; padding-left: 16px; border-left: 3px solid var(--spark); }
.caso__saldo b { font-size: 19px; font-weight: 600; letter-spacing: -.4px; }

/* ---------------------------------------------- las objeciones

   Pregunta como encabezado de verdad y respuesta que se entiende sola: es la
   misma unidad que la FAQ, y por la misma razón —un párrafo que necesita el de
   al lado no se puede citar. Sin tarjeta: la portada ya tiene demasiadas. */
.objeciones { display: grid; gap: 0; }
.objecion { padding: 22px 0; border-top: 1px solid var(--line); }
.objecion:last-child { border-bottom: 1px solid var(--line); }
.objecion h3 { margin-bottom: 8px; }
.objecion .body-s { max-width: 68ch; }

/* ---------------------------------------------- para quién

   Eran cuatro tarjetas idénticas a las de «lo que el anuncio no te dice» que
   repetían cuatro precios ya publicados en /pricing. Ahora es una fila de
   enlaces a las páginas de segmento, que es lo único que aportaban. */
.segmentos { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 0 28px; }
.segmento { padding: 18px 0 20px; border-top: 2px solid var(--line); }
.segmento:hover { border-top-color: var(--accent); }
.segmento b { display: block; font-size: 15.5px; font-weight: 600; letter-spacing: -.2px; }
.segmento span { display: block; margin-top: 6px; font-size: 13px; line-height: 1.5; color: var(--muted); }
.segmento:hover b { color: var(--accent); }

.style-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: 14px; }
.style-strip__thumb { height: 108px; border-radius: 62px 62px 6px 6px; background: var(--stripes-s); overflow: hidden; }
.style-strip__thumb img { width: 100%; height: 100%; object-fit: cover; }
.style-strip__name { font-size: 12.5px; font-weight: 600; margin-top: 10px; }

/* ---------------------------------------------------------------- el pie

   Sobre tinta y con el filete de coral por arriba, el mismo que la banda de
   cifras lleva por abajo: la página abre y cierra con la misma franja.

   Los grises salen de opacidades sobre `--ink` y están medidos, no elegidos a
   ojo: el rótulo de columna es de 11 px en versales, que es donde un gris
   flojo deja de leerse. */
.site-footer {
  background: var(--ink);
  color: rgba(248, 247, 252, .82);
  padding: 56px 48px 28px;
  border-top: 4px solid var(--spark);
}
.site-footer__cuerpo {
  max-width: 1240px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 48px;
}

/* El logo en negativo. Sobre tinta el índigo de la marca no se ve, así que la
   pieza va en papel y el filete de la base se queda en coral, que ahí sí tiene
   contraste. Y el aviso al pasar por encima pasa a coral por lo mismo: el
   índigo del resto del sitio aquí sería pulsar y que no ocurra nada. */
.site-footer .logo,
.site-footer .logo__word { color: var(--on-accent); }
.site-footer .logo__mark { width: 24px; height: 24px; }
.site-footer .logo__word { font-size: 20px; letter-spacing: -.9px; }
.site-footer .logo-link:hover .logo,
.site-footer .logo-link:hover .logo__word { color: var(--spark); }
.site-footer .logo-link:focus-visible { outline-color: var(--on-accent); }

.site-footer__marca p {
  margin: 18px 0 0;
  max-width: 40ch;
  font-size: 13px;
  line-height: 1.65;
  color: rgba(248, 247, 252, .62);
}

.site-footer__col { display: flex; flex-direction: column; align-items: flex-start; }
.site-footer__col h2 {
  margin: 0 0 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: rgba(248, 247, 252, .62);
}
.site-footer__col a {
  padding: 5px 0;
  font-size: 13.5px;
  line-height: 1.45;
  color: rgba(248, 247, 252, .82);
}
.site-footer__col a:hover { color: var(--spark); }
.site-footer__col a:focus-visible { outline: 2px solid var(--on-accent); outline-offset: 3px; border-radius: 3px; }

.site-footer__legal {
  max-width: 1240px;
  margin: 46px auto 0;
  padding-top: 22px;
  border-top: 1px solid rgba(248, 247, 252, .14);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  justify-content: space-between;
  font-size: 12px;
  line-height: 1.55;
  color: rgba(248, 247, 252, .62);
}

/* Precios */
/* Cuatro planes desde la v3 del catálogo, no tres. Con `repeat(3, 1fr)` la
   cuarta tarjeta caía sola a una segunda fila y parecía un añadido de última
   hora, justo el que más cobra. `auto-fit` reparte lo que haya: si mañana son
   tres otra vez, o cinco, sigue cuadrando sin tocar esto. */
.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
  gap: 20px;
  max-width: 1180px;
  margin: 0 auto;
  /* Sin `align-items: start` cada tarjeta se estira a la altura de la fila.
     Con él, cada una medía lo que midiera su lista y la parrilla quedaba
     dentada: al comparar planes eso se lee como que a uno le falta algo. */
}
.plan { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-l); padding: 28px 26px; display: flex; flex-direction: column; }
.plan--featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* El que ya se tiene: borde de tinta, sin el realce del destacado. */
.plan--actual { border-color: var(--ink); box-shadow: none; }
.plan .btn[disabled] { opacity: .55; cursor: default; }
.plan__price { display: flex; align-items: baseline; gap: 6px; margin: 12px 0 0; }
.plan__price b { font-size: 34px; font-weight: 600; letter-spacing: -1.2px; }
.plan__price span { font-size: 14px; color: var(--faint); }
/* La lista de «qué incluye». Vive en las tarjetas de plan y también en la
   puerta del análisis financiero y en las compras sueltas, así que la clase
   propia: confinada a `.plan`, en cualquier otro sitio salían el punto de
   viñeta y el check en renglones distintos. */
.plan ul, .lista-incluye {
  list-style: none;
  margin: 20px 0 0;
  padding: 14px 0 0;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
}
.plan li, .lista-incluye li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
}
.plan li .ico, .lista-incluye li .ico { color: var(--accent); margin-top: 2px; flex: none; }
/* `auto` y no una medida: empuja el botón al fondo de la tarjeta, así que
   todos los de la fila quedan a la misma altura aunque una lista tenga dos
   puntos más que otra. Es lo que hace que la comparación se lea de un vistazo. */
.plan .btn { margin-top: auto; }
.plan ul { margin-bottom: 22px; }

/* Acceso */
/* Sin la cabecera de la web encima: entrar es pantalla de la app y la
   marca ya va dentro de la tarjeta. Detrás, la misma fachada que el hero de
   la portada y con el mismo velo en color papel: entrar es cruzar la puerta
   de esa calle, y el velo mantiene la temperatura de la página. El
   `background-color` sostiene la pantalla mientras la foto carga. */
.auth {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 56px 24px;
  background-color: var(--paper);
  background-image:
    linear-gradient(180deg, rgba(248, 247, 252, .52) 0%, rgba(248, 247, 252, .80) 100%),
    url("/samples/hero-fondo.webp");
  background-size: cover;
  background-position: center 30%;
}
.auth__card {
  width: min(420px, 100%);
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  padding: 32px 34px;
  /* La sombra grande y no la media: sobre foto, la tarjeta necesita
     despegarse igual que el informe del hero. */
  box-shadow: var(--shadow-l);
}

/* ================================================================== APP */

/* La app ocupa la ventana y no la desborda: cada columna scrollea por dentro.
   Antes el aside medía 100vh y colgaba por debajo de la barra de estancia, así
   que la página era siempre 100vh + la barra y había scroll aunque no sobrara
   nada. En cuanto el aside deja de ser columna (1080px) esto se deshace y
   vuelve el scroll normal del documento. */
.app { display: flex; height: 100vh; overflow: hidden; }

/* Barra lateral: logo, acción principal, navegación y el consumo del plan. */
.sidebar {
  width: 226px;
  flex: none;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  padding: 22px 16px;
  gap: 18px;
  position: sticky;
  top: 0;
  height: 100vh;
}
.sidebar__nav { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.sidebar__nav a {
  padding: 9px 14px;
  border-radius: 999px;
  color: var(--muted);
  transition: background .13s, color .13s;
}
.sidebar__nav a:hover { background: rgba(255, 255, 255, .6); color: var(--ink); }
.sidebar__nav a[aria-current="page"] { background: var(--paper); color: var(--accent); font-weight: 600; }
.sidebar__sep { margin: 8px 14px 2px; font-size: 12px; color: var(--faint); }

.usage { margin-top: auto; background: var(--paper); border-radius: var(--r-l); padding: 16px; }
.usage .meter { margin: 10px 0 8px; }

/* El perfil, al pie: avatar con la inicial, el nombre y a dónde lleva. */
.sidebar__perfil {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border-radius: 999px;
  color: var(--muted);
  transition: background .13s, color .13s;
}
.sidebar__perfil:hover { background: rgba(255, 255, 255, .6); color: var(--ink); }
.sidebar__perfil[aria-current="page"] { background: var(--paper); color: var(--accent); }
.sidebar__avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  background: var(--ink);
  color: var(--surface);
  font-size: 13px;
  font-weight: 600;
}
.sidebar__perfil[aria-current="page"] .sidebar__avatar { background: var(--accent); }
.sidebar__quien { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.sidebar__quien b { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar__quien span { font-size: 11.5px; color: var(--faint); }

.topbar { display: none; }
.tabbar { display: none; }

/* La barra lateral se pliega a iconos (16-09-2026); el estado se guarda. */
.sidebar__cab { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.sidebar__plegar { border: 0; background: transparent; color: var(--faint); cursor: pointer; padding: 4px; border-radius: 6px; display: grid; place-items: center; }
.sidebar__plegar:hover { background: rgba(255,255,255,.6); color: var(--ink); }
.sidebar__nav a { display: flex; align-items: center; gap: 9px; }
.sidebar__nav a .ico { color: inherit; flex: none; }
.sidebar__nuevo { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.app--plegada .sidebar { width: 66px; padding: 22px 10px; }
.app--plegada .sidebar__cab { justify-content: center; }
.app--plegada .logo__word, .app--plegada .txt, .app--plegada .usage, .app--plegada .sidebar__quien { display: none; }
.app--plegada .sidebar__nav a { justify-content: center; padding: 9px; }
.app--plegada .sidebar__nuevo { padding: 9px; width: 42px; margin: 0 auto; }
.app--plegada .sidebar__perfil { justify-content: center; padding: 6px; }

/* Nova, el asesor, como panel lateral a la derecha del contenido. */
.nova { width: 380px; flex: none; min-width: 0; min-height: 0; border-left: 1px solid var(--line); background: var(--card); display: flex; flex-direction: column; position: relative; }
/* El tirador del panel, sobre su filete izquierdo: mismo dibujo que el de la columna de renders. */
.nova__grip { position: absolute; left: -3px; top: 0; bottom: 0; width: 7px; padding: 0; border: 0; background: none; cursor: col-resize; z-index: 3; touch-action: none; }
.nova__grip::before { content: ""; position: absolute; left: 3px; top: 0; bottom: 0; width: 1px; background: transparent; transition: background .12s; }
.nova__grip:hover::before, .nova__grip:focus-visible::before { background: var(--accent); }
.nova__grip:focus-visible { outline: 0; }
@media (max-width: 1080px) { .nova__grip { display: none; } .nova { width: auto !important; } }
.nova__cab { display: flex; align-items: center; gap: 10px; padding: 14px 14px 12px; border-bottom: 1px solid var(--line-soft); }
.nova__cab b { font-size: 14px; }
.nova__avatar { width: 24px; height: 24px; border-radius: 50%; flex: none; background: linear-gradient(135deg, var(--accent), var(--spark)); display: inline-block; }
.nova__avatar--mini { width: 16px; height: 16px; vertical-align: -3px; margin-right: 2px; }
.nova-boton[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.nova__cerrar { border: 0; background: transparent; font-size: 20px; color: var(--faint); cursor: pointer; padding: 0 4px; line-height: 1; }
.nova__lista { padding: 10px 14px 0; }
.nova__cuerpo { flex: 1; min-height: 0; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }
.nova__msg { max-width: 92%; padding: 10px 13px; border-radius: var(--r-m); font-size: 13.5px; line-height: 1.5; }
.nova__msg--yo { align-self: flex-end; background: var(--accent); color: var(--on-accent); border-bottom-right-radius: 4px; }
.nova__msg--ia { align-self: flex-start; background: var(--surface); border-bottom-left-radius: 4px; }
.nova__sugeridos { border: 1px solid var(--accent-line); border-radius: var(--r-s); padding: 10px 12px; margin-top: 10px; background: var(--card); }
.nova__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.nova__pie { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--line-soft); }
.nova__pie .input { flex: 1; min-width: 0; }
.nova__nota { padding: 0 14px 10px; color: var(--faint); }
.nova-lista { display: grid; gap: 8px; margin-top: 18px; max-width: 760px; }
.nova-lista__fila { display: flex; align-items: flex-start; gap: 12px; text-align: left; font: inherit; color: inherit; background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--r-m); padding: 12px 14px; cursor: pointer; }
.nova-lista__fila:hover { border-color: var(--ink); box-shadow: var(--shadow-m); }
@media (max-width: 1080px) {
  .nova { position: fixed; inset: 0; width: auto; z-index: 40; border-left: 0; }
}

/* La barra de estancia cruza por encima de las dos columnas, así que el área
   de trabajo es una columna con la barra arriba y main + aside debajo. */
.content { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.workspace { flex: 1; min-width: 0; min-height: 0; display: flex; align-items: stretch; }
.main { flex: 1; min-width: 0; min-height: 0; overflow-y: auto; padding: 24px var(--pad) 32px; }
.main--flush { padding: 0; display: flex; flex-direction: column; }

/* Aside de 290 px: coste, reparto y las acciones de cierre. Ya no necesita ser
   pegajoso ni medir 100vh: el shell le da la altura y scrollea por dentro. */
.aside {
  width: var(--aside);
  flex: none;
  min-width: 0;
  border-left: 1px solid var(--line);
  padding: 30px 24px 40px;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  position: relative;
}
.aside__foot { margin-top: auto; padding-top: 24px; display: flex; flex-direction: column; gap: 10px; }

/* Tirador de ancho, sobre el filete izquierdo. El ancho se guarda en el
   navegador: quien trabaja con la tira de versiones quiere el lienzo grande y
   quien escribe prompts largos quiere lo contrario. */
.aside__grip {
  position: absolute;
  left: -3px;
  top: 0;
  bottom: 0;
  width: 7px;
  padding: 0;
  border: 0;
  background: none;
  cursor: col-resize;
  z-index: 3;
  touch-action: none;
}
.aside__grip::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--accent);
  opacity: 0;
  transition: opacity .13s;
}
.aside__grip:hover::before,
.aside__grip:focus-visible::before,
.aside--sizing .aside__grip::before { opacity: 1; }
.aside__grip:focus-visible { outline: 0; }
/* Mientras se arrastra, el puntero manda en toda la ventana y nada selecciona. */
body.is-sizing { cursor: col-resize; user-select: none; }

/* Cabecera del aside: qué hace esta columna y, a la derecha, sobre qué. */
.aside__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.aside__head-t { font-size: 13px; font-weight: 600; }

/* De qué versión sale la siguiente. Hasta ahora esto no se decía en ninguna
   parte: abrías la v2, pedías un cambio y salía una v5 sin relación visible. */
.base {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11px;
  font-weight: 600;
}

/* Migas: con reforma y estancias hace falta decir siempre dónde está uno. */
.crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 12.5px;
  color: var(--faint);
  margin-bottom: 14px;
}
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--ink); text-decoration: underline; }
.crumbs span:last-child { color: var(--ink); font-weight: 500; }
.crumbs__sep { color: var(--line); }

.view-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}
.view-head__title { min-width: 0; }
.view-head__title .label { display: block; }
.view-head__title .h-1 + .label,
.view-head__title .h-2 + .label { margin-top: 6px; }
.view-head__title .label + .h-1,
.view-head__title .label + .h-2 { margin-top: 4px; }
.actions-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* El nombre del inmueble, con su lápiz al lado. El nombre viene del anuncio y
   es largo —«Piso en venta en Calle de … — idealista»—, así que el que se
   recorta con puntos suspensivos es el texto y no la fila: el lápiz no encoge
   ni se va de la pantalla por mucho que crezca el título. */
.titulo { display: flex; align-items: center; gap: 6px; min-width: 0; margin-top: 2px; }
.titulo__texto { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.titulo__accion {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: var(--r-s);
  background: none;
  color: var(--faint);
  cursor: pointer;
  transition: color .15s, background .15s;
}
.titulo__accion:hover { color: var(--ink); background: var(--surface); }
.titulo__accion--ok:hover { color: var(--accent); }
.titulo__accion[disabled] { opacity: .4; pointer-events: none; }

/* Editando: el campo ocupa el sitio del título y con su misma letra. El alto
   es el mismo que el de la fila en reposo —el del lápiz, que es lo más alto de
   las dos—, así que abrir el campo no empuja hacia abajo las cifras. */
.titulo__campo {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -.6px;
  line-height: 1.2;
  height: 30px;
  padding: 0 8px;
}

/* Tira de KPI: filete arriba y abajo, columnas separadas por filete. */
.kpis {
  display: grid;
  /* Se reparten el ancho que haya en vez de exigir cuatro columnas: con cuatro
     fijas, una cifra larga como «243.760 – 256.500 €» se salía del contenedor
     en cuanto la tira caía en una columna estrecha. */
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
/* El filete que separa una cifra de la siguiente lo lleva CADA celda a su
   izquierda, y la de la primera columna se sale del contenedor y se recorta.
   Con `.kpi + .kpi` la celda que abría una fila nueva —cuando la tira no cabe
   entera— se quedaba con una raya suelta a su izquierda, sin nada al otro
   lado. */
.kpis { overflow: hidden; }
.kpi { padding: 18px 22px 20px; min-width: 0; border-left: 1px solid var(--line); margin-left: -1px; }
.kpi__value { overflow-wrap: anywhere; }
.kpi__value { font-size: 21px; font-weight: 600; letter-spacing: -.5px; margin-top: 8px; }
.kpi__sub { font-size: 12px; color: var(--faint); margin-top: 6px; }
.kpi--accent .kpi__value { color: var(--accent); }
/* La celda protagonista de un bloque: el €/m² tiene que ganar a su vecina de
   al lado, no empatar con ella. */
/* La cifra protagonista se marca con el COLOR, no con el cuerpo: una tira
   donde cada número mide una cosa distinta —32, 21 y 16 en la misma fila— se
   lee como una escalera y no como una fila de cifras comparables. Todas van
   al mismo tamaño desde el 16-09-2026 y la jerarquía la pone el acento. */
.kpi--hero .kpi__value { color: var(--accent); }
.kpi--hero .label { color: var(--accent); }

/* ------------------------------------------------------- el precio por metro

   El bloque del €/m² y su recta. La recta contesta «¿está caro?» de un vistazo:
   cada banda del mercado es una caja del cuartil 25 al 75 con su mediana
   marcada, y la aguja vertical es este piso. Todo en porcentajes sobre el mismo
   eje, así que las pistas de las dos filas quedan alineadas solas. */

.sec--m2 .kpis { margin-top: 0; }
.m2__cab { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }

.veredicto { font-size: 12.5px; font-weight: 500; padding: 3px 10px; border-radius: 999px; }
.veredicto--ok { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); }
.veredicto--mal { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
.veredicto--neutro { color: var(--muted); background: var(--surface); }
/* El veredicto de una lectura caducada. Ni verde ni rojo: en gris y tachado,
   porque no es un veredicto vigente, es lo que se dijo entonces. Pintarlo con
   su color lo dejaba discutiendo de tú a tú con las cuentas de arriba. */
.veredicto--viejo {
  color: var(--faint); background: var(--surface); border: 1px dashed var(--line);
}
/* Y el texto que sostiene, apagado: se puede leer, pero no manda. */
.lectura--vieja { color: var(--muted) !important; }

.recta { position: relative; margin-top: 22px; }
.recta__fila + .recta__fila { margin-top: 16px; }
.recta__cab { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 5px; }
.recta__cab b { color: var(--ink); font-weight: 600; }
/* Pista lisa: la trama diagonal es el hueco reservado a una foto, y aquí
   competía con la caja rayada de la muestra corta. */
.recta__pista { position: relative; height: 22px; border-radius: 4px; background: var(--surface); }

.recta__caja { position: absolute; top: 0; bottom: 0; border-radius: 4px; min-width: 3px; }
.recta__caja--aref { background: #A9A5DC; }
.recta__caja--be { background: #4F49B4; }
/* Muestra por debajo del mínimo: rayada, para que se vea que la cifra está ahí
   de referencia pero no entra en el cálculo. La trama va del color contrario
   al de su caja: en negro sobre el azul oscuro no se veía, y dos bandas igual
   de flojas se leían como una dudosa y otra fiable. */
.recta__caja--corta { background-image: repeating-linear-gradient(45deg, rgba(22, 20, 43, .22) 0 3px, transparent 3px 7px); }
.recta__caja--be.recta__caja--corta {
  background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, .42) 0 3px, transparent 3px 7px);
}
.recta__mediana { position: absolute; top: -4px; bottom: -4px; width: 2px; background: var(--ink); opacity: .55; }
/* La referencia que manda no tiene caja —no es un rango, es un número—, así que
   su marca tiene que sostener la fila ella sola: entera y más alta. */
.recta__mediana--ref { top: -6px; bottom: -6px; width: 3px; opacity: 1; }

/* La aguja se dibuja dentro de cada pista y sobresale por arriba y por abajo,
   así que las dos quedan en la misma vertical y el ojo las une. Cruzando el
   bloque entero desde una altura fija caía sobre el rótulo de la fila en
   cuanto este ocupaba dos líneas, que en móvil es siempre. */
/* La aguja va en coral y no en índigo: contra las marcas de mediana, que son
   de tinta, dos rayas del mismo tono no se distinguían y la más importante
   —dónde cae ESTE piso— se perdía entre ellas. Es el uso que la paleta le
   reserva al coral: marca sobre tinta, nunca texto sobre papel. */
.recta__aguja { position: absolute; top: -7px; bottom: -7px; width: 2px; background: var(--spark); z-index: 2; }
/* Sin punta de flecha: la etiqueta va justo encima de la aguja y del mismo
   color, así que la unión ya se ve. Una punta a esa altura cae sobre el rótulo
   de la fila —«Se vende a reformar (n=4…)»— y se pierde entre las letras. */
.recta__cima { position: relative; height: 20px; }
/* Cuando la etiqueta no cabe centrada sobre la aguja se pega al borde, y esta
   hairline las une: sin ella quedaban a un palmo la una de la otra. */
.recta__guia { position: absolute; bottom: 6px; height: 1px; background: var(--spark); opacity: .55; }
.recta__etiqueta {
  position: absolute; bottom: 0; font-size: 12.5px; color: var(--muted);
  white-space: nowrap;
}
.recta__etiqueta--centro { transform: translateX(-50%); }
/* El coral no puede ir en el texto —2,4 de contraste sobre papel—, así que la
   etiqueta lo lleva en un punto: basta para amarrarla a su aguja. */
.recta__punto {
  display: inline-block; width: 7px; height: 7px; border-radius: 999px;
  background: var(--spark); margin-right: 5px;
}
.recta__etiqueta b { color: var(--accent); font-weight: 600; }
.recta__pie { display: flex; justify-content: space-between; margin-top: 6px; color: var(--faint); }

/* --------------------------------------------------- los globos de la recta

   El texto va en `data-tip` y el lado al que se abre en una clase que decide
   el generador (`tip--izq` / `tip--centro` / `tip--dcha`), con la posición que
   ya tiene calculada. Así el globo es CSS puro: `render()` rehace este HTML en
   cada pintada y unos listeners habría que volver a colgarlos cada vez.

   `position: relative` va SOLO donde hace falta: la caja, la mediana y la
   aguja ya están posicionadas en absoluto y una regla general se la comería
   por especificidad. */
.recta__cab [data-tip], .recta__pie [data-tip] { position: relative; }
.recta [data-tip], .riel [data-tip], .riel__clave [data-tip] { cursor: help; }
.riel__clave [data-tip] { position: relative; }
.recta [data-tip]::after,
.riel [data-tip]::after,
.riel__clave [data-tip]::after {
  content: attr(data-tip);
  position: absolute; bottom: calc(100% + 9px); left: 50%;
  transform: translateX(-50%);
  width: max-content; max-width: min(280px, 62vw);
  padding: 9px 11px; border-radius: var(--r-s);
  background: var(--ink); color: var(--paper);
  font-size: 12px; font-weight: 400; line-height: 1.45; text-align: left;
  white-space: normal; text-wrap: pretty;
  box-shadow: var(--shadow-m);
  opacity: 0; visibility: hidden; transition: opacity .12s ease;
  pointer-events: none; z-index: 6;
}
.recta [data-tip]:hover::after, .recta [data-tip]:focus-visible::after,
.riel [data-tip]:hover::after, .riel [data-tip]:focus-visible::after,
.riel__clave [data-tip]:hover::after, .riel__clave [data-tip]:focus-visible::after {
  opacity: 1; visibility: visible;
}
.recta .tip--izq::after { left: 0; transform: none; }
.recta .tip--dcha::after { left: auto; right: 0; transform: none; }
/* En el riel el globo se abre por DEBAJO: encima del riel está la cifra grande
   del precio y el globo la tapaba justo cuando se quiere comparar las dos. */
.riel [data-tip]::after, .riel__clave [data-tip]::after { bottom: auto; top: calc(100% + 9px); }

/* La aguja y las medianas son rayas de dos píxeles: sin ensanchar la zona
   sensible no hay forma de apuntarlas con el ratón. El área va en el ::before
   y no en la caja del elemento para no mover la raya ni un píxel. */
.recta__mediana::before, .recta__aguja::before { content: ""; position: absolute; inset: -5px -7px; }

/* ------------------------------------------------------------- la decisión

   La cabecera de la pestaña de inversión: primero el mando (para qué lo
   quieres), luego la nota que ese mando decide. En el orden contrario —que es
   como estuvo hasta el 25-08-2026— la nota se leía como un veredicto absoluto
   sobre el piso, cuando es un veredicto sobre el piso PARA UN USO. */

.decision { display: flex; flex-direction: column; gap: 18px; }
.seg--usos { margin-top: 8px; flex-wrap: wrap; }

.veredicto-caja {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  padding: 20px 22px;
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-s);
}
/* La nota, en cuadro y no en píldora: es la respuesta de la pantalla y una
   píldora de doce píxeles no lo parecía. El número manda y el «/ 100» lo sitúa. */
.veredicto-caja__nota {
  display: flex; align-items: baseline; gap: 7px;
  padding: 10px 16px; border-radius: var(--r-m);
  background: var(--accent-soft); color: var(--accent);
}
.veredicto-caja__nota b { font-size: 34px; font-weight: 600; letter-spacing: -1.5px; line-height: 1; }
.veredicto-caja__nota span { font-size: 17px; font-weight: 500; }
.nota--baja { background: color-mix(in srgb, var(--danger) 10%, transparent); color: var(--danger); }
.nota--media { background: var(--surface); color: var(--muted); }
/* Una nota a la que le falta un tercio del peso o más se pinta en hueco: sale,
   porque es lo mejor que se sabe, pero no puede llevar la misma cara de
   respuesta rotunda que una completa. */
.nota--parcial {
  background: transparent; border: 1px dashed currentColor; opacity: .75;
}

/* El desglose de la nota: cuatro barras con lo que pesa cada una a la derecha.
   Sin la columna del peso, un 83 y un 1 parecían igual de importantes. */
.partes { margin-top: 12px; max-width: 560px; }
.parte {
  display: grid; grid-template-columns: minmax(140px, 1fr) 1.4fr 34px 62px;
  gap: 12px; align-items: center; padding: 7px 0;
}
.parte--off { opacity: .45; }
.parte__n { text-align: right; font-weight: 600; color: var(--ink); }
.parte__peso { text-align: right; }
.parte__aviso {
  margin-top: 8px; padding: 8px 12px; border-radius: var(--r-s);
  color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent);
}

/* ----------------------------------------------------- el riel de precio

   El mando del trato. Hasta el 25-08-2026 había dos exploradores de precio que
   no se hablaban: una tabla fija de −15/−10/−5/0/+5 % que no se podía mover y
   un slider suelto debajo. Ahora hay un mando solo con la escalera pintada
   encima: la pista se colorea por tramos —verde mientras queda margen, ámbar
   en la franja justa, rojo pasado el punto de equilibrio— y los precios que
   deciden son marcas y botones. Arrastrar es explorar; pulsar es ir. */

/* El riel del precio se lleva el doble de ancho que el de la obra: es el mando
   de la pantalla, y en mitad de columna sus tres franjas de color no se
   distinguían. Por debajo de 760 px caen uno bajo otro. */
.mandos { display: grid; grid-template-columns: 2fr 1fr; gap: 34px; margin-top: 4px; }
.mandos > .mando:only-child { grid-column: 1 / -1; }
/* Se apilan en el mismo punto en que la barra lateral todavía come columna:
   el riel del precio necesita unos 400 px para que sus tres franjas y sus
   cuatro atajos se lean, y por debajo de eso prefiere el ancho entero a
   compartirlo con el de la obra. */
@media (max-width: 980px) { .mandos { grid-template-columns: 1fr; } }
.mando { min-width: 0; }
.mando__cab { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.mando__cifra { font-size: 19px; font-weight: 600; letter-spacing: -.4px; }

.riel { position: relative; margin-top: 10px; height: 22px; }
.riel__pista {
  position: absolute; left: 0; right: 0; top: 5px; height: 12px;
  border-radius: 6px; background: var(--surface); overflow: hidden;
}
.riel__zona { position: absolute; top: 0; bottom: 0; }
/* Tramos translúcidos y no planos: debajo pasa la pista, y así el tramo sin
   escalera (gris) y el tramo pintado se leen como el mismo objeto. */
.riel__zona--ok { background: color-mix(in srgb, var(--ok) 44%, transparent); }
.riel__zona--justo { background: color-mix(in srgb, var(--spark) 62%, transparent); }
.riel__zona--mal { background: color-mix(in srgb, var(--danger) 38%, transparent); }
/* La marca de un precio que decide: una raya, no un punto. Un punto sobre una
   pista de doce píxeles se confunde con el pulgar del mando. */
.riel__hito {
  position: absolute; top: 5px; height: 12px; width: 2px;
  margin-left: -1px; background: var(--ink); opacity: .55; border-radius: 1px;
}
.riel__hito::before { content: ""; position: absolute; inset: -6px -7px; }

/* El mando va encima de la pista con su propia pista transparente: así el
   color lo pinta el div de abajo y el navegador solo dibuja el pulgar. */
.riel__mando {
  position: absolute; left: 0; top: 0; width: 100%; height: 22px; margin: 0;
  -webkit-appearance: none; appearance: none; background: transparent;
}
.riel__mando::-webkit-slider-runnable-track { height: 22px; background: transparent; border: 0; }
.riel__mando::-moz-range-track { height: 22px; background: transparent; border: 0; }
.riel__mando::-webkit-slider-thumb {
  -webkit-appearance: none; width: 20px; height: 20px; margin-top: 1px;
  border-radius: 50%; background: var(--card); border: 3px solid var(--accent);
  box-shadow: var(--shadow-s); cursor: grab;
}
.riel__mando::-moz-range-thumb {
  width: 20px; height: 20px; box-sizing: border-box;
  border-radius: 50%; background: var(--card); border: 3px solid var(--accent);
  box-shadow: var(--shadow-s); cursor: grab;
}
.riel__mando:active::-webkit-slider-thumb { cursor: grabbing; }
.riel__mando:active::-moz-range-thumb { cursor: grabbing; }

/* La clave: los precios de la escalera como botones. Debajo del riel y no
   encima de él —encima se solapaban en cuanto dos precios caían cerca— y con
   la cifra dentro, que es lo que se venía a leer. */
.riel__clave { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }
.riel__salto {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 5px 11px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--card);
  font-size: 12px; color: var(--muted); white-space: nowrap;
}
.riel__salto b { font-weight: 600; color: var(--ink); }
.riel__salto:hover { border-color: var(--ink); color: var(--ink); }
/* Son botones que llevan el mando a su precio, así que el cursor es el de
   pulsar. Va después de la regla del globo (`[data-tip] { cursor: help }`),
   que se los comía por ir después en el archivo: todos llevan `data-tip`. */
.riel__salto, .riel__clave .riel__salto[data-tip] { cursor: pointer; }
/* Cada atajo lleva el color de su tramo en el borde izquierdo: el botón y la
   franja del riel a la que salta son la misma cosa. */
/* El color de cada atajo lo dice su tono, no su id: desde el 15-09-2026 las
   marcas cambian con el uso (buena compra, rinde lo que pides, te lo darían…). */
.riel__salto--ok { box-shadow: inset 3px 0 0 var(--ok); }
.riel__salto--justo { box-shadow: inset 3px 0 0 var(--spark); }
.riel__salto--mal { box-shadow: inset 3px 0 0 var(--danger); }
.riel__salto--piden { box-shadow: inset 3px 0 0 var(--accent); }

/* La salida viva del trato: las cifras que cambian al mover los mandos. Ya no
   es una fila de tres cosas fijas — el juego de KPI depende del uso, porque
   para vender manda el margen y para alquilar el flujo del mes. */
.kpis--vivo { margin-top: 26px; }
.kpi--ok .kpi__value { color: var(--ok); }
.kpi--mal .kpi__value { color: var(--danger); }
.kpi--hero.kpi--ok .label, .kpi--hero.kpi--ok .kpi__value { color: var(--ok); }
.kpi--hero.kpi--mal .label, .kpi--hero.kpi--mal .kpi__value { color: var(--danger); }

/* La gráfica no gana nada por estirarse: a lo ancho de la columna entera las
   barras quedan finas y separadas por medio metro de blanco. */
.trato__grafica { max-width: 820px; margin-top: 24px; }

/* La clave del coste. Dentro de la barra solo caben los tramos gordos —los
   1.800 € de notaría son un pelo de once píxeles—, así que las partidas se
   listan aquí con su color y su importe: la gráfica cuenta la proporción y la
   clave cuenta las cifras, y ninguna de las dos depende de la otra. */
.clave-barras { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 10px; }
.clave-barras__i { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--faint); }
.clave-barras__i i { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.clave-barras__i b { font-weight: 600; color: var(--ink); }
.clave-barras__i--precio i { background: var(--coste-precio); }
.clave-barras__i--gastos i { background: var(--coste-gastos); }
.clave-barras__i--reforma i { background: var(--coste-reforma); }
.clave-barras__i--costes i { background: var(--surface); border: 1px solid var(--line); }
/* El total cierra la fila y va con el peso de un total, no de una partida. */

/* Un tramo de datos con su rótulo. Diecisiete filas seguidas se leen como una
   lista de la compra; en tres bloques de cinco o seis, cada uno se ve entero
   de un vistazo y se sabe qué se está mirando. */
.grupo { margin-top: 26px; }
.grupo > .label { margin-bottom: 8px; display: block; }
.grupo__cab { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }

@media (max-width: 700px) {
  .recta__cab { flex-direction: column; gap: 1px; }
}

/* Tarjetas de estancia: miniatura a la izquierda, datos a la derecha. */
.rooms { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
.room-card {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  overflow: hidden;
  background: var(--card);
  text-align: left;
  color: inherit;
  transition: border-color .15s, box-shadow .15s;
}
.room-card:hover { border-color: var(--ink); box-shadow: var(--shadow-m); }
.room-card--off { opacity: .72; }
.room-card__thumb {
  width: 132px;
  flex: none;
  position: relative;
  background: var(--stripes-s);
  display: grid;
  place-items: center;
  text-align: center;
  font-size: 12px;
  line-height: 1.5;
  color: var(--faint);
}
.room-card__thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.room-card__body { flex: 1; min-width: 0; padding: 15px 18px 17px; }
.room-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.room-card__name { font-size: 15.5px; font-weight: 600; }
.room-card__cost { font-size: 14px; font-weight: 600; margin-top: 11px; }

.card-add {
  border: 1px dashed var(--line);
  border-radius: var(--r-m);
  display: grid;
  place-items: center;
  align-content: center;
  gap: 6px;
  min-height: 150px;
  padding: 20px;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
}
.card-add:hover { border-color: var(--accent); color: var(--accent); }
.card-add b { display: inline-flex; align-items: center; gap: 7px; font-weight: 600; }
.card-add span { font-weight: 400; color: var(--faint); font-size: 12px; }

/* Coste por estancia, con barra proporcional. */
.cost-list > div { padding: 11px 0; border-bottom: 1px solid var(--line-soft); }
.cost-list__row { display: flex; justify-content: space-between; gap: 10px; font-size: 13.5px; }
.cost-list__row span:last-child { color: var(--muted); }
.cost-list__bar { height: 5px; border-radius: 999px; background: var(--surface); margin-top: 9px; overflow: hidden; }
.cost-list__bar > div { height: 100%; border-radius: 999px; background: var(--accent); }
.cost-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.cost-total b { font-size: 15px; font-weight: 600; color: var(--accent); }

/* ------------------------------------------------------------ tabla del plan */

.plan-table { width: 100%; }
.plan-row {
  display: grid;
  grid-template-columns: 1fr 130px 160px 96px;
  align-items: center;
  gap: 12px;
  padding: 15px 0;
  border-bottom: 1px solid var(--line-soft);
}
.plan-row--head {
  padding: 10px 0;
  border-top: 1px solid var(--line);
  border-bottom-color: var(--line);
  font-size: 12px;
  color: var(--faint);
}
.plan-row--off { opacity: .5; }
.plan-row__name { min-width: 0; padding-right: 16px; }
.plan-row__name b { font-size: 14.5px; font-weight: 600; }
.plan-row__desc { font-size: 12.5px; color: var(--muted); margin-top: 3px; text-wrap: pretty; }
.plan-row__cost { text-align: right; font-size: 14px; font-weight: 600; }
.plan-row__cost span { display: block; font-size: 11.5px; font-weight: 400; color: var(--faint); margin-top: 3px; }
.plan-row__cta { text-align: right; font-size: 12px; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.plan-row--total {
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  font-weight: 600;
}
.plan-row--total .plan-row__cost { font-size: 17px; color: var(--accent); }

/* Cabecera de grupo dentro del plan completo. */
.group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 26px 0 10px;
  border-bottom: 1px solid var(--line);
}
.group-head__name { font-size: 18px; font-weight: 600; letter-spacing: -.3px; }
.group-head__name:hover { color: var(--accent); }
.group-head__sub { font-size: 12px; color: var(--faint); }
.group-head b { font-size: 14px; font-weight: 600; }

.motivo {
  margin: 7px 0 0;
  padding-left: 11px;
  border-left: 2px solid var(--accent-line);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  font-style: italic;
}

/* Procedencia del desglose: catálogo o análisis de las fotos. */
.analisis {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  padding: 18px 22px;
  margin-bottom: 18px;
}
.analisis--hecho { border-color: var(--accent-line); }
.analisis--cargando { display: block; font-size: 14px; line-height: 1.6; color: var(--muted); }
.analisis p { margin: 0; }
.analisis p + p { margin-top: 6px; }

.avisos { margin-top: 18px; background: var(--accent-soft); border-radius: var(--r-m); padding: 18px 22px; }
.avisos ul { margin: 10px 0 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 8px; }
.avisos li { font-size: 13.5px; line-height: 1.6; color: var(--ink); }
.avisos li::marker { color: var(--accent); }

/* Un solo desplegable para toda la aplicación: la flecha gira al abrir y el
   triángulo del navegador se esconde. `.descartadas` lo estrenó con las fotos
   que no son estancia; `.desplegable` es el mismo trato para cualquier bloque
   que se abra bajo demanda. */
.descartadas, .desplegable { margin-top: 16px; }
.descartadas summary, .desplegable summary {
  cursor: pointer;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted);
  padding: 10px 2px;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 7px;
}
.descartadas summary::-webkit-details-marker,
.desplegable summary::-webkit-details-marker { display: none; }
.descartadas summary .ico, .desplegable summary .ico { color: var(--accent); transition: transform .15s; }
.descartadas[open] summary .ico, .desplegable[open] summary .ico { transform: rotate(90deg); }
.descartadas summary:hover, .desplegable summary:hover { color: var(--ink); }

/* Las fotos que no son una estancia. Se enseñan pequeñas y con su motivo: si
   desaparecieran sin más, parecería que se han perdido por el camino. */
/* La galería de renders del inmueble: solo imágenes, clic para el visor. */
.render-gal {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 10px;
}
.render-gal button {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  overflow: hidden;
  background: var(--card);
  cursor: zoom-in;
  text-align: left;
  transition: border-color .13s;
}
.render-gal button:hover { border-color: var(--ink); }
.render-gal img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: var(--stripes-s);
}
.render-gal span { display: block; padding: 6px 9px 7px; }

.descartadas__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 14px;
  padding: 6px 2px 2px;
}
.descartadas__grid figure { margin: 0; }
.descartadas__grid img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-s);
  border: 1px solid var(--line);
  filter: grayscale(1);
  opacity: .72;
}
.descartadas__grid figcaption {
  margin-top: 7px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--faint);
}
.descartadas__grid figcaption b { color: var(--muted); text-transform: capitalize; }

.disclaimer { font-size: 11.5px; line-height: 1.6; color: var(--faint); max-width: 620px; }

/* ------------------------------------------------------- importar un anuncio */

.importar {
  border: 1px solid var(--accent-line);
  background: var(--accent-soft);
  border-radius: var(--r-m);
  padding: 20px 22px;
  margin-top: 22px;
}
.importar__row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.importar__row .btn { flex-shrink: 0; }
.importar--curso { display: flex; gap: 15px; align-items: center; }
.importar--pegar textarea { margin-top: 12px; width: 100%; font-family: ui-monospace, monospace; font-size: 12px; }
.importar__err { margin: 10px 0 0; font-size: 13px; color: var(--danger); }

/* El separador entre pegar la URL y el formulario de siempre. Que se vea que
   son dos caminos hacia lo mismo y no un paso obligatorio antes del otro. */
.importar__o {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 22px 0 4px;
  color: var(--faint);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.importar__o::before,
.importar__o::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.badge--alerta { background: var(--danger); border-color: var(--danger); color: #fff; }

.tira-fotos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 7px;
  margin-top: 12px;
}
.tira-fotos button {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  overflow: hidden;
  background: none;
  cursor: zoom-in;
  aspect-ratio: 1;
}
.tira-fotos img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tira-fotos button:hover { border-color: var(--accent); }

@media (max-width: 640px) {
  .importar__row { align-items: stretch; }
  .importar__row .btn { width: 100%; }
}

/* ------------------------------------------------- nueva estancia (formulario) */

.step-label { margin: 32px 0 12px; }
.step-label:first-of-type { margin-top: 26px; }

.dropzone {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  transition: opacity .15s;
}
.dropzone.is-over { opacity: .6; }
.dropzone__hint {
  height: 104px;
  border: 1.5px dashed var(--line);
  border-radius: var(--r-m);
  background: var(--card);
  display: grid;
  place-items: center;
  text-align: center;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--faint);
}
.dropzone__hint:hover { border-color: var(--accent); color: var(--accent); }
.dropzone__hint span { display: block; padding: 0 12px; }
/* En `inline-flex` el rótulo no cortaba línea y salía «Añadir fotoArrastra o
   pega con Ctrl+V» de corrido. Al ser flex ocupa su propia línea. */
.dropzone__hint b {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 5px;
}
.dropzone__hint:hover b { color: var(--accent); }

.ref {
  position: relative;
  height: 104px;
  border-radius: 7px;
  overflow: hidden;
  background: var(--stripes-s);
}
.ref img { width: 100%; height: 100%; object-fit: cover; }
.ref__name {
  position: absolute; left: 6px; bottom: 6px;
  font-size: 9.5px; padding: 2px 6px; border-radius: 4px;
  background: rgba(22, 20, 43, .72); color: var(--paper);
  max-width: calc(100% - 12px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ref__del {
  position: absolute; top: 5px; right: 5px;
  width: 20px; height: 20px; border: 0; border-radius: 50%;
  background: rgba(22, 20, 43, .8); color: var(--paper);
  display: grid; place-items: center; padding: 0;
}
.ref__del:hover { background: var(--accent); }
.ref--iter { outline: 2px solid var(--accent); outline-offset: -2px; }
.ref__badge {
  position: absolute; top: 5px; left: 5px;
  font-size: 10px; font-weight: 600;
  padding: 2px 7px; border-radius: 4px; background: var(--accent); color: var(--on-accent);
}

.style-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.style-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  overflow: hidden;
  text-align: left;
  padding: 0;
  transition: border-color .13s, box-shadow .13s;
}
.style-card:hover { border-color: var(--ink); }
.style-card[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.style-card__thumb { display: block; height: 88px; background: var(--stripes-s); overflow: hidden; }
.style-card__thumb img { width: 100%; height: 100%; object-fit: cover; }
.style-card__body { display: block; padding: 12px 14px 14px; }
.style-card__name { display: block; font-size: 14px; font-weight: 600; }
.style-card__tag { display: block; font-size: 12.5px; color: var(--muted); margin-top: 4px; line-height: 1.5; text-wrap: pretty; }
.style-card__check {
  position: absolute; z-index: 1; top: 8px; right: 8px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  display: none; place-items: center;
}
.style-card[aria-pressed="true"] .style-card__check { display: grid; }
.style-card--custom {
  border-style: dashed;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 130px;
  color: var(--muted);
}

/* ------------------------------------------------------------ generando */

.gen {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 28px;
  text-align: center;
}
.gen__title { font-size: clamp(26px, 3.4vw, 36px); font-weight: 600; letter-spacing: -1px; line-height: 1.14; max-width: 620px; text-wrap: balance; margin: 0; }
.gen__bar { width: min(520px, 100%); margin-top: 34px; height: 8px; border-radius: 999px; background: var(--surface); overflow: hidden; }
.gen__bar > div { height: 100%; border-radius: 999px; background: var(--accent); transition: width .2s linear; }
.gen__meta {
  width: min(520px, 100%);
  display: flex;
  justify-content: space-between;
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--muted);
}
.gen__steps { width: min(520px, 100%); margin-top: 32px; border-top: 1px solid var(--line-soft); text-align: left; }
.gen__step { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.gen__dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--line); }
.gen__step--on .gen__dot { background: var(--accent); }
.gen__step span:nth-child(2) { font-size: 13.5px; color: var(--faint); }
.gen__step--on span:nth-child(2) { color: var(--ink); }
.gen__meta-step { margin-left: auto; font-size: 12px; color: var(--faint); }

/* ------------------------------------------------------- estancia (resultado) */

/* Cabecera del par render / plan: las migas y, debajo, a qué estancia miras. */
.room-bar { border-bottom: 1px solid var(--line); padding: 16px var(--pad) 14px; }
.room-bar .crumbs { margin: 0; }
.room-bar__chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }

/* Comparador antes / después (arrastrable). Toma la proporción del render, así
   que ninguna de las dos fotos se recorta por arriba y por abajo. */
.compare {
  position: relative;
  overflow: hidden;
  background: var(--stripes);
}
/* El cursor de arrastrar y el bloqueo del gesto solo cuando hay algo que
   arrastrar. En la portada no lo había —la web pública no carga `app.js`— y el
   puntero prometía un movimiento que no ocurría; la clase la pone quien ata el
   arrastre, así que el aspecto no puede volver a adelantarse al comportamiento. */
.compare--viva {
  touch-action: none;
  user-select: none;
  cursor: ew-resize;
}
.compare__base { position: absolute; inset: 0; }
.compare__layer { position: absolute; inset: 0; clip-path: inset(0 0 0 var(--split, 50%)); }
.compare__layer img, .compare__base img { width: 100%; height: 100%; object-fit: cover; }
.compare__handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--split, 50%);
  width: 2px;
  background: var(--card);
  transform: translateX(-1px);
  z-index: 3;
}
.compare__grip {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--card);
  color: var(--ink);
  display: grid;
  place-items: center;
  box-shadow: 0 2px 12px rgba(22, 20, 43, .28);
}
.compare .stamp { z-index: 2; }

/* En el resultado manda la proporción real del render: la imagen va en flujo y
   es la que da tamaño a la caja, así no se recorta ni por arriba ni por abajo.
   El "antes" se superpone recortado por la izquierda de la línea. */
.compare--room {
  width: fit-content;
  max-width: min(88%, var(--lienzo));
  margin-inline: auto;
  border-radius: var(--r-m);
  border: 1px solid var(--line);
}
/* Igual que el lienzo, más el pie «arrastra la línea para comparar». */
.compare__sizer { display: block; max-width: 100%; max-height: clamp(300px, calc(100vh - var(--marco) - 30px), 730px); width: auto; height: auto; }
.compare__over {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--split, 50%)) 0 0);
}
.compare__over img { width: 100%; height: 100%; object-fit: cover; }
.compare--hero { position: absolute; inset: 0; }
/* El comparador de la sección del render, que ya no está dentro del hero: sin
   caja propia hereda el `position:absolute; inset:0` de `--hero` y se escapa a
   ocupar la ventana entera. */
.compare--demo {
  aspect-ratio: 3 / 2;
  border-radius: var(--r-l);
  border: 1px solid var(--line);
}

/* Los tres modos, centrados encima de la imagen. */
.view-toolbar { display: flex; justify-content: center; margin-bottom: 12px; }

.canvas {
  position: relative;
  border-radius: var(--r-m);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--stripes);
  min-height: 240px;
  display: grid;
  place-items: center;
  width: fit-content;
  max-width: min(88%, var(--lienzo));
  margin-inline: auto;
}
/* El render manda en la vista, pero no hasta el borde: en un monitor ancho se
   estiraba a más de 1300 px y aplastaba todo lo demás. El tope lo baja un punto
   en cualquier pantalla (88 % de la columna) sin pasar de --lienzo, y va en la
   caja para que la trama y el filete sigan pegados a la imagen.
   El alto es el hueco que queda bajo el resto de la vista (--marco): así la
   estancia entra de una vez y cambiar de Antes a Después no obliga a scrollear.
   El suelo de 340 px es para pantallas bajas, donde encoger más el render para
   ganar dos dedos de scroll no compensa. */
.canvas img { max-width: 100%; max-height: clamp(300px, calc(100vh - var(--marco)), 760px); width: auto; height: auto; }
/* Cuando la versión trae varias imágenes, la fila de variantes se cuela entre
   el lienzo y la tira: el render le cede ese alto en vez de empujarla fuera. */
.canvas:has(+ .variants) img { max-height: clamp(300px, calc(100vh - var(--marco) - 66px), 694px); }
.canvas__tools { position: absolute; right: 12px; bottom: 12px; display: flex; gap: 8px; }
.canvas__tool {
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 8px;
  background: rgba(255, 255, 255, .92);
  color: var(--ink);
  display: grid;
  place-items: center;
  backdrop-filter: blur(4px);
  box-shadow: 0 1px 4px rgba(22, 20, 43, .18);
  transition: background .13s, color .13s;
}
.canvas__tool:hover { background: var(--accent); color: var(--on-accent); }

/* Las variantes son las imágenes de una misma versión: van pegadas al lienzo,
   dentro de la versión abierta, y por eso no llevan rótulo de versión. */
.variants { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; justify-content: center; align-items: center; }
.variants__lbl { font-size: 11.5px; color: var(--faint); }
.variants button { width: 74px; height: 52px; padding: 0; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; background: var(--stripes-s); }
.variants button[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.variants img { width: 100%; height: 100%; object-fit: cover; }

/* Tira de versiones. El historial vive con la imagen y no en el aside: son
   fotos, se eligen mirándolas. El aside se queda solo para pedir el cambio. */
.strip {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-top: 14px;
  padding-top: 13px;
  border-top: 1px solid var(--line-soft);
  overflow-x: auto;
  scrollbar-width: thin;
}
.strip__lbl { font-size: 11.5px; color: var(--faint); align-self: center; flex: none; padding-right: 4px; }

.shot {
  flex: none;
  width: 96px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--card);
  text-align: left;
  position: relative;
  transition: border-color .13s, box-shadow .13s;
}
.shot:hover { border-color: var(--ink); }
.shot img { width: 100%; height: 54px; object-fit: cover; display: block; background: var(--stripes-s); }
.shot__cap { display: block; padding: 5px 7px 6px; font-size: 10.5px; line-height: 1.32; color: var(--faint); }
.shot__cap b { display: block; font-size: 11px; font-weight: 600; color: var(--ink); }
/* La nota se recorta a dos líneas; la entera está en el `title` del botón. */
.shot__cap span {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.shot[aria-current="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.shot[aria-current="true"] .shot__cap b { color: var(--accent); }
.shot__n {
  position: absolute;
  top: 5px;
  right: 5px;
  background: rgba(22, 20, 43, .72);
  color: #fff;
  font-size: 9.5px;
  font-weight: 600;
  padding: 1px 5px;
  border-radius: 4px;
}
.shot--add {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 5px;
  border-style: dashed;
  color: var(--faint);
  font-size: 11px;
  line-height: 1.3;
  text-align: center;
  padding: 10px 8px;
}
.shot--add:hover { border-color: var(--accent); color: var(--accent); }

.enviado {
  margin: 0;
  padding: 10px 12px;
  background: var(--surface);
  border-radius: var(--r-s);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
}
.enviado .label { display: block; margin-bottom: 3px; }

/* ------------------------------------------------------------------ galería */

.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; }
.gcard {
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  overflow: hidden;
  background: var(--card);
  text-align: left;
  padding: 0;
  display: block;
  color: inherit;
  transition: border-color .15s, box-shadow .15s;
}
.gcard:hover { border-color: var(--ink); box-shadow: var(--shadow-m); }
.gcard--quieta:hover { border-color: var(--line); box-shadow: none; }
.gcard__img { display: block; position: relative; height: 168px; overflow: hidden; background: var(--stripes-s); }
/* La foto va encima de la lámina dibujada, que queda de fondo: si el archivo
   no carga, el delegado de `error` esconde la imagen y el alzado sigue ahí. */
.gcard__img img { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
.gcard__lamina { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Sello de esquina al estilo de un portal: dice si lo que se ve es el render
   generado o las fotos del anuncio, para no confundir uno con otro. */
.gcard__tag {
  position: absolute;
  z-index: 2;
  left: 10px;
  bottom: 10px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(22, 20, 43, .66);
  color: var(--on-accent);
  font-size: 11px;
  line-height: 1.55;
}
/* La nota, sobre la foto y en la esquina de arriba (16-09-2026): en el pie
   compartía renglón con el tipo de reforma, se rozaban y no llegaban a
   alinearse nunca. Arriba a la derecha no estorba al sello del render, que va
   abajo a la izquierda, ni a los puntos del carrusel. Fondos sólidos porque
   debajo hay una foto: la insignia de interfaz es demasiado clara ahí. */
.gcard__nota {
  position: absolute;
  z-index: 2;
  top: 10px;
  right: 10px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  color: #fff;
  background: rgba(22, 20, 43, .72);
  box-shadow: 0 1px 6px rgba(22, 20, 43, .25);
}
.gcard__nota small { font-size: 10.5px; font-weight: 500; opacity: .75; }
.gcard__nota--alta { background: var(--accent); color: var(--on-accent); }
.gcard__nota--baja { background: var(--danger); }
.gcard__body { display: block; padding: 14px 16px 16px; color: inherit; text-decoration: none; }
.gcard__head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
/* El nombre a dos líneas como mucho, y la fecha a una: un título de portal
   llega a ochenta letras y se comía la tarjeta entera. */
.gcard__title { font-size: 14.5px; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.3; }
.gcard__cuando { white-space: nowrap; flex: none; }
.gcard__cifra { display: block; margin-top: 8px; font-size: 15px; color: var(--ink); }
.gcard__cifra .num:not(b) { color: var(--muted); font-size: 13px; }
.gcard__meta { display: block; font-size: 12px; color: var(--faint); margin-top: 3px; }
.gcard__anuncio { display: inline-flex; align-items: center; gap: 5px; color: var(--accent); white-space: nowrap; }
.gcard__anuncio:hover { text-decoration: underline; }
/* El carrusel: hasta cinco fotos con ajuste, flechas que salen al pasar el
   puntero y puntos abajo. */
.gcard__slides { position: absolute; inset: 0; z-index: 1; display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
.gcard__slides::-webkit-scrollbar { display: none; }
.gcard__slides img { flex: none; width: 100%; height: 100%; object-fit: cover; scroll-snap-align: start; position: static; }
.gcard__flecha { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; width: 28px; height: 28px; border-radius: 50%; border: 0; background: rgba(255,255,255,.88); color: var(--ink); display: grid; place-items: center; cursor: pointer; opacity: 0; transition: opacity .15s; }
.gcard__flecha--izq { left: 8px; } .gcard__flecha--dcha { right: 8px; }
.gcard__img--carrusel:hover .gcard__flecha, .gcard__flecha:focus-visible { opacity: 1; }
.gcard__puntos { position: absolute; left: 0; right: 0; bottom: 8px; z-index: 2; display: flex; justify-content: center; gap: 5px; pointer-events: none; }
.gcard__puntos i { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.55); }
.gcard__puntos i.on { background: #fff; }
.gcard__foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 12px; font-size: 13px; color: var(--muted); }
.gcard--add {
  border-style: dashed;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  padding: 30px 24px;
  min-height: 200px;
  color: var(--muted);
}
.gcard--add .micro { max-width: 210px; }

/* Barra de la galería: buscar, abrir los filtros y ordenar. La búsqueda se come
   el ancho que sobre y los otros dos mandos se quedan con el suyo, para que la
   barra sea una sola línea mientras quepa. Y `stretch` en vez de `center`: un
   botón es más bajo que un campo de texto por su propio padding, y en una fila
   de tres mandos se le nota; así los tres miden lo que mida la fila, sin ir
   cuadrando paddings a mano. */
.gbar { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.gbar__q { flex: 1 1 220px; min-width: 0; }
.gbar__abrir, .gbar__orden { flex: 0 0 auto; }
.gbar__orden { width: auto; }
/* El galón gira al desplegarse: es la única pieza que dice si el panel está
   abierto cuando el panel queda fuera de la pantalla. */
.gbar__abrir .ico { transition: transform .15s; }
.gbar__abrir[aria-expanded="true"] .ico { transform: rotate(90deg); }

/* El panel de filtros. Cada grupo con su rótulo encima y sus mandos debajo, y
   todos en fila hasta que dejan de caber: los grupos no miden lo mismo —dos
   casillas de precio y tres pastillas de estado— y una rejilla de columnas
   iguales dejaba huecos donde no hacían falta. */
.panel-filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 16px 26px;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  background: var(--card);
  padding: 16px 18px;
  margin-bottom: 18px;
}
.filtro .label { display: block; }
.filtro__campos { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.filtro__num { width: 104px; }
.filtro__sel { width: auto; }
.panel-filtros__pie { margin-left: auto; align-self: center; }

/* Lo que hay puesto, con el panel plegado: cada filtro en su pastilla y con su
   aspa, porque quitar uno tiene que poder hacerse sin volver a abrir nada. */
.gbar-puestos { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: -8px 0 18px; }
.gbar-puestos .ico { opacity: .75; }

/* Miniatura del inmueble en la cabecera del informe y portada del resumen:
   el mismo apilado de lámina + foto que la ficha de la galería. */
.thumb-inmueble, .portada-inmueble {
  display: block;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--stripes-s);
  padding: 0;
}
.thumb-inmueble { flex: none; width: 64px; height: 64px; border-radius: var(--r-s); }
.portada-inmueble { width: 100%; height: 190px; border-radius: var(--r-m); }
.thumb-inmueble img, .portada-inmueble img { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
.thumb-inmueble__lamina, .portada-inmueble__lamina { position: absolute; inset: 0; width: 100%; height: 100%; }

/* --------------------------------------------------------------- tutoriales */

/* -------------------------------------------------------------- estados */

.empty {
  border: 1.5px dashed var(--line);
  border-radius: var(--r-m);
  padding: 56px 24px;
  text-align: center;
  color: var(--faint);
  font-size: 14px;
  line-height: 1.7;
}
.status { font-size: 13px; min-height: 20px; color: var(--muted); }
.status--err { color: var(--danger); white-space: pre-wrap; word-break: break-word; }
.status--ok { color: var(--accent); }
.status--ask { color: var(--ink); background: var(--accent-soft); border-radius: var(--r-s); padding: 10px 12px; line-height: 1.5; }

.spinner {
  display: inline-block;
  width: 13px;
  height: 13px;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
  vertical-align: -2px;
  margin-right: 7px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* -------------------------------------------------------------- overlays */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(22, 20, 43, .93);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 28px;
}
.lightbox img { max-width: min(95vw, 1500px); max-height: 78vh; object-fit: contain; border-radius: 10px; box-shadow: var(--shadow-l); }
.lightbox__bar { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.lightbox__name { color: #A9A5C4; font-size: 12px; }
.lightbox__close { position: absolute; top: 16px; right: 20px; background: none; border: 0; color: #A9A5C4; padding: 6px; display: grid; place-items: center; }
.lightbox__close:hover { color: var(--paper); }

.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  z-index: 70;
  background: var(--ink);
  color: var(--paper);
  padding: 12px 20px;
  border-radius: 999px;
  font-size: 13.5px;
  box-shadow: var(--shadow-l);
  animation: rise .22s ease-out;
}
@keyframes rise { from { opacity: 0; transform: translate(-50%, 10px); } }

/* ========================================================== responsive */

@media (max-width: 1240px) {
  :root { --aside: 264px; --pad: 24px; }
  .style-strip { grid-template-columns: repeat(4, 1fr); }
}

@media (max-width: 1080px) {
  /* El aside deja de ser columna y pasa a ir debajo del contenido. Con las dos
     apiladas ya no tiene sentido que cada una scrollee por su cuenta: vuelve el
     scroll normal del documento. */
  .app { height: auto; min-height: 100vh; overflow: visible; }
  .main { overflow-y: visible; }
  .workspace { flex-direction: column; }
  .aside {
    width: auto;
    border-left: 0;
    border-top: 1px solid var(--line);
    position: static;
    overflow-y: visible;
    padding: 24px var(--pad) 40px;
  }
  .aside__grip { display: none; }
  .aside__foot { margin-top: 22px; }
  .rooms { grid-template-columns: 1fr; }
  .plans { grid-template-columns: 1fr; max-width: 460px; }
  .hero { grid-template-columns: 1fr; }
  .hero__media { border-left: 0; border-top: 1px solid var(--line); min-height: 380px; }
  .cifras__lista { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 28px; }
  .caso { grid-template-columns: 1fr; gap: 26px; }
  .kpis { grid-template-columns: repeat(2, 1fr); }
  .kpi:nth-child(n+3) { border-top: 1px solid var(--line); }
  .style-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 860px) {
  .site-header { padding: 14px 20px; }
  /* La barra larga se va y en su sitio queda el desplegable, que hasta este
     cambio no existía y dejaba la web pública sin navegación en el teléfono. */
  .site-nav { display: none; }
  .nav-movil { display: block; }
  .section { padding: 44px 20px; }
  .cifras { padding: 34px 20px 0; }
  .cifras__lista { padding-bottom: 30px; }

  /* A 390 px una captura de interfaz entera es una mancha. Se le deja su ancho
     mínimo y la caja arrastra en horizontal, que es la única forma de que en un
     teléfono se pueda leer de verdad en vez de hacer bulto. */
  .galeria__foto { overflow-x: auto; }
  /* Ancho fijo y no `min-width`: en un elemento reemplazado `width: auto`
     resuelve al ancho natural del fichero —1.800 px— y `min-width` no lo
     limita, así que la captura se pintaba a tamaño completo. */
  .galeria__foto img { width: 760px; max-width: none; }
  /* Y la lupa se va: la imagen ya se puede recorrer arrastrando, y el aviso
     tapaba media captura en una pantalla estrecha. */
  .galeria__lupa { display: none; }
  /* Cuatro miniaturas en 350 px son cromos de 80: dos filas de dos. */
  .galeria__tiras { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .galeria__tira img { height: 76px; }
  .hero__text { padding: 44px 20px 36px; }
  .steps { grid-template-columns: 1fr; }
  .step { padding: 26px 20px 28px; }
  .step + .step { border-left: 0; border-top: 1px solid var(--line); }
  /* Tres columnas en 350 px no son columnas. La marca ocupa la fila entera y
     las dos listas se reparten la de abajo, que a este ancho siguen entrando
     enteras sin partir ningún enlace. */
  .site-footer { padding: 40px 20px 24px; }
  .site-footer__cuerpo { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 24px; }
  .site-footer__marca { grid-column: 1 / -1; }
  .site-footer__legal { margin-top: 34px; }

  /* La navegación pasa a barra superior + pestañas inferiores. */
  .sidebar { display: none; }
  .app { flex-direction: column; }
  .main { padding: 16px 20px 90px; }
  .aside { padding: 22px 20px 90px; }

  /* Aquí no sobra ancho: el lienzo recupera toda la columna. Y el marco es
     otro, así que el alto vuelve a ser una fracción de la pantalla. */
  .canvas, .compare--room { max-width: 100%; }
  .canvas img, .compare__sizer { max-height: 64vh; }

  .topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 20px;
    background: var(--card);
    border-bottom: 1px solid var(--line);
    position: sticky;
    top: 0;
    z-index: 20;
  }
  .topbar__title { font-size: 13.5px; font-weight: 600; text-align: center; line-height: 1.3; }
  .topbar button { border: 0; background: none; color: var(--muted); padding: 4px 8px; display: grid; place-items: center; }

  .tabbar {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 25;
    background: var(--card);
    border-top: 1px solid var(--line);
    padding: 8px 6px calc(8px + env(safe-area-inset-bottom));
  }
  .tabbar a {
    flex: 1;
    text-align: center;
    font-size: 10.5px;
    font-weight: 600;
    color: var(--faint);
    padding: 4px 2px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
  }
  .tabbar a[aria-current="page"] { color: var(--accent); }
  .tabbar a[aria-current="page"] .ico { stroke-width: 2.4; }

  .hide-sm { display: none; }
  .only-sm { display: inline; }
  .room-bar { padding: 14px 20px 12px; }

  .dropzone { grid-template-columns: repeat(3, 1fr); }
  .style-grid { grid-template-columns: 1fr; }

  /* Las filas del plan se apilan: nombre y coste arriba, quién debajo. */
  .plan-row { grid-template-columns: 1fr auto; gap: 8px; padding: 14px 0; }
  .plan-row--head { display: none; }
  .plan-row__name { grid-column: 1; padding-right: 10px; }
  .plan-row__cost { grid-column: 2; }
  .plan-row__who { grid-column: 1; }
  .plan-row__cta { grid-column: 2; flex-direction: row; gap: 12px; align-items: center; }
  .plan-row--total { grid-template-columns: 1fr auto; }


  .compare--room { max-height: none; }
  .lightbox img { max-height: 62vh; }
}

@media (max-width: 520px) {
  /* Logo y dos botones no caben en una línea de 375 px: que envuelva antes que
     salirse. */
  .site-header { padding: 12px 16px; gap: 10px; flex-wrap: wrap; }
  .site-header__actions { gap: 8px; }
  .site-header__actions .btn { padding: 9px 13px; font-size: 12.5px; }
  .dropzone { grid-template-columns: repeat(2, 1fr); }
  .style-strip { grid-template-columns: repeat(2, 1fr); }
  .cifras__lista { grid-template-columns: 1fr; gap: 22px; }

  /* La tarjeta del informe en un teléfono. La tira de KPI hereda de `.kpis` un
     «dos columnas pase lo que pase» que aquí no vale: a 165 px el rótulo del
     €/m² protagonista partía en dos líneas y las dos cifras quedaban a alturas
     distintas, que es lo que hacía que la pieza más importante de la portada
     pareciese rota. Y las filas de abajo bajan un punto para que «Desembolso
     total» y su cifra vuelvan a caber en el mismo renglón. */
  .informe .kpis { grid-template-columns: 1fr; }
  .informe .kpi + .kpi { border-top: 1px solid var(--line); }
  .informe__filas > div { font-size: 12.5px; gap: 10px; }
  .informe__total b { font-size: 14px; }
  /* Y 40 px de margen a cada lado de la tarjeta son de maqueta ancha: en un
     teléfono se los come la fila del desembolso, que es la cifra que da nombre
     al producto y partía en dos líneas por dos píxeles. */
  .hero--informe .hero__media { padding: 28px 20px; }
  /* Apiladas, las dos caras del caso vuelven a ser dos cajas enteras: los
     bordes cosidos de la fila solo tienen sentido en horizontal. */
  .caso__cifras { grid-template-columns: 1fr; }
  .caso__cara:first-child { border-radius: var(--r-l) var(--r-l) 0 0; border-right: 1px solid var(--line); border-bottom: 0; }
  .caso__cara:last-child { border-radius: 0 0 var(--r-l) var(--r-l); }
  .gallery { grid-template-columns: 1fr; }
  /* En 375 px la barra de la galería es de tres pisos, y el mínimo de nota
     envuelve dentro de su píldora como ya hacen las bandas de uso previsto. */
  .gbar__q, .gbar__orden, .gbar__abrir { flex: 1 1 100%; }
  .panel-filtros { gap: 14px 18px; padding: 14px 15px; }
  .filtro, .panel-filtros__pie { flex: 1 1 100%; margin-left: 0; }
  .filtro__num { flex: 1 1 0; width: auto; min-width: 0; }
  .filtro .seg { flex-wrap: wrap; }
  .kpis { grid-template-columns: 1fr 1fr; }
  .h-display { letter-spacing: -1px; }
  .view-head { gap: 14px; }
  /* En un teléfono la cabecera va justa —portada, nombre y nota en la misma
     línea—, así que el lápiz se aprieta: cada píxel que ocupa se lo quita al
     nombre, que es lo que hay que leer. */
  .titulo { gap: 2px; }
  .titulo__accion { width: 26px; height: 26px; }
  .compare .stamp { font-size: 9.5px; padding: 4px 8px; letter-spacing: .4px; }
  .compare .stamp--bl { left: 10px; bottom: 10px; }
  .compare .stamp--br { right: 10px; bottom: 10px; }
}

/* ------------------------------------------------------------------ imprimir */

/* En pantalla no existe; en papel es lo que hace que el documento se sostenga
   solo cuando alguien lo lleva al banco o al reformista. */
.solo-papel { display: none; }

/* Y al revés: lo que solo tiene sentido con un ratón delante. En papel, «pasa
   el puntero» no es una instrucción, es una errata. */
@media print { .solo-pantalla { display: none !important; } }

@media print {
  .sidebar, .topbar, .tabbar, .site-header, .site-footer, .aside__foot, .btn, .seg,
  .aside__grip, .titulo__accion { display: none !important; }
  /* El mando del riel no se puede arrastrar en papel; sus marcas y sus atajos
     sí valen impresos, que son los precios que deciden. */
  .riel__mando { display: none !important; }
  .riel__salto { border-color: var(--line) !important; }
  /* El shell de altura fija recortaría el papel a una página. */
  .app { height: auto; overflow: visible; }
  .main { overflow: visible; }
  .aside { width: auto; border: 0; padding: 0; overflow: visible; }
  .workspace { flex-direction: column; }
  body { background: #fff; }

  @page { margin: 16mm 14mm; }

  .solo-papel { display: block; }

  .print-head {
    display: flex !important;
    justify-content: space-between;
    gap: 16px;
    padding-bottom: 8px;
    margin-bottom: 18px;
    border-bottom: 1px solid var(--line);
    font-size: 11px;
    color: var(--muted);
  }
  .print-head b { color: var(--ink); font-size: 13px; }

  /* Las migas son navegación: en papel sobran, y el título ya va en la
     cabecera de impresión. */
  .crumbs, .tabs, .desplegable summary, .modal, .toast { display: none !important; }
  /* Un desplegable cerrado en pantalla no puede esconder partidas en el
     documento: quien lo imprime quiere el presupuesto entero. */
  .desplegable[open], .desplegable { display: block; }
  .desplegable > *:not(summary) { display: revert !important; }

  /* La puerta de pago no pinta nada en un documento ya pagado. */
  .sec:has([data-act="desbloquear"]) { display: none !important; }

  /* Los fondos de acento son el dato, no decoración: sin esto el navegador
     los tira y la nota, el veredicto y el riel salen en blanco. */
  .card, .sec, .meter, .meter__fill, .veredicto, .nota, .riel, .chip, .badge {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Nada se parte por la mitad: ni una partida, ni una tarjeta, ni una fila. */
  .card, .sec, li, tr, .kv > div, .partida { break-inside: avoid; page-break-inside: avoid; }
  h1, h2, h3, .h-1, .h-2, .eyebrow { break-after: avoid; page-break-after: avoid; }
  img { break-inside: avoid; max-width: 100% !important; }

  /* Los enlaces impresos que no dicen a dónde van son ruido; los del propio
     documento, peor. Solo se revela el destino de los externos. */
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 10px; color: var(--muted); }

  /* AI Act art. 50: lo generado por un modelo tiene que ir dicho, y un
     documento impreso es justo el sitio donde el render se separa de la app y
     deja de tener contexto. */
  .print-pie {
    display: block;
    margin-top: 24px;
    padding-top: 10px;
    border-top: 1px solid var(--line);
    font-size: 10px;
    color: var(--muted);
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  html { scroll-behavior: auto; }
}


/* ------------------------------------------------------------------ packs */
/* Solo aparecen dentro del aviso de créditos agotados. Tres botones anchos y
   del mismo tamaño: no hay «el recomendado», porque cuál conviene depende de
   cuánto le quede por hacer y eso no lo sabemos nosotros. */

.packs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
  margin-top: 10px;
}
.pack {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  background: var(--card);
  cursor: pointer;
  text-align: left;
  transition: border-color .15s, background .15s;
}
.pack:hover { border-color: var(--accent); background: var(--paper); }
.pack__n { font-size: 13px; color: var(--muted); }
.pack__eur { font-size: 19px; font-weight: 600; color: var(--ink); }

/* ----------------------------------------------------------------- modal */
/* Los tutoriales pasaron a ser ruta propia, pero la lista de compra y el aviso
   de cuota son contextuales: sacan de donde estás y devuelven al mismo sitio. */

.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(22, 20, 43, .55);
  display: grid;
  place-items: center;
  padding: 24px;
}
.modal__panel {
  background: var(--paper);
  border-radius: var(--r-l);
  width: min(680px, 100%);
  max-height: 86vh;
  overflow: auto;
  box-shadow: var(--shadow-l);
}
.modal__head {
  position: sticky;
  top: 0;
  background: var(--paper);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 24px 28px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.modal__body { padding: 20px 28px 28px; }
.modal__close { border: 0; background: none; font-size: 24px; line-height: 1; color: var(--faint); padding: 0; }
.modal__close:hover { color: var(--ink); }

/* Diálogo de confirmación. Estrecho y sin cabecera pegajosa ni aspa: aquí no
   hay nada que recorrer, solo una pregunta y dos salidas, y un aspa daría una
   tercera forma de decir que no. */
.modal__panel--dialogo { width: min(440px, 100%); }

/* El formulario de pago. Viene de Stripe dentro de un marco, con su propio
   relleno y decidiendo su alto solo, así que aquí se le quita casi todo el
   nuestro de los lados para no sumar dos marcos. El alto mínimo es para que
   mientras carga no aparezca una tira de nada: el marco empieza en cero y
   crece cuando Stripe le dice cuánto mide. */
.modal__panel--pago .modal__body { padding: 10px 16px 20px; }
#marco-pago { min-height: 320px; }
.dialogo__detalle { margin-top: 14px; }
.dialogo__detalle > :first-child { margin-top: 0; }

/* Qué entra en la tanda o qué se va a borrar, dicho pieza a pieza: es la
   información que el cuadro del navegador no tenía dónde poner. */
.dialogo__lista {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 288px;
  overflow-y: auto;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-s);
  background: var(--card);
}
.dialogo__lista li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 13px;
  font-size: 13.5px;
}
.dialogo__lista li + li { border-top: 1px solid var(--line-soft); }

.dialogo__coste { margin: 14px 0 0; font-size: 13.5px; color: var(--muted); }
.dialogo__coste b { color: var(--ink); font-weight: 600; }

.dialogo__aviso {
  margin: 14px 0 0;
  padding: 11px 13px;
  border-radius: var(--r-s);
  background: color-mix(in srgb, var(--danger) 9%, transparent);
  color: var(--danger);
  font-size: 13px;
  line-height: 1.5;
}

/* Confirmar a la derecha, como en el resto de la aplicación; cancelar es un
   fantasma para que no compitan. */
.dialogo__pie { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; flex-wrap: wrap; }

/* ------------------------------------------------------------- asistente

   Las cuatro pantallas de antes de una tanda de renders. Una pregunta por
   pantalla: el panel es algo más ancho que el del diálogo para que la rejilla
   de estilos quepa en dos filas, y no más, para que siga leyéndose como una
   pregunta y no como un formulario. */
.modal__panel--asis { width: min(560px, 100%); }

.asis__pasos { display: flex; gap: 8px; flex-wrap: wrap; }
.asis__paso {
  font-size: 11.5px;
  color: var(--faint);
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--surface);
}
.asis__paso--ahora { background: var(--accent); color: var(--on-accent); font-weight: 600; }
.asis__paso--hecho { color: var(--accent); background: var(--accent-soft); }

.asis__cuerpo { margin-top: 18px; }

/* Los estilos se eligen mirando la muestra, así que la foto manda y el nombre
   va debajo en pequeño. */
.asis__estilos { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.asis__estilo {
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  background: var(--card);
  padding: 0;
  overflow: hidden;
  text-align: left;
  transition: border-color .15s, box-shadow .15s;
}
.asis__estilo img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.asis__estilo span { display: block; padding: 7px 9px; font-size: 12px; }
.asis__estilo:hover { border-color: var(--ink); }
.asis__estilo[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.asis__estilo[aria-pressed="true"] span { color: var(--accent); font-weight: 600; }

/* Nivel de obra y casillas de conservación: la misma fila pulsable, porque son
   la misma clase de decisión y no hay motivo para que se vean distintas. */
.asis__opciones { display: flex; flex-direction: column; gap: 8px; }
.asis__opcion {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3px;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  background: var(--card);
  transition: border-color .15s, background .15s;
}
.asis__opcion b { font-size: 14px; font-weight: 600; }
.asis__opcion:hover { border-color: var(--ink); }
.asis__opcion[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.asis__opcion[aria-pressed="true"] b { color: var(--accent); }

.asis__opcion--check { grid-template-columns: 22px 1fr; align-items: center; column-gap: 10px; }
.asis__opcion--check .micro { grid-column: 2; }

/* El tic vive en una caja fija: sin ella la fila daba un salto al marcarse. */
.asis__tic {
  width: 20px;
  height: 20px;
  border: 1px solid var(--line);
  border-radius: 5px;
  display: grid;
  place-items: center;
  color: var(--on-accent);
  background: var(--card);
}
.asis__opcion[aria-pressed="true"] .asis__tic,
.asis__quitar[aria-pressed="true"] .asis__tic { background: var(--accent); border-color: var(--accent); }

/* El repaso reutiliza .dialogo__lista; cada fila es pulsable para dejar la
   estancia fuera de la tanda. */
.asis__quitar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  font-size: 13.5px;
}
.asis__quitar .asis__tic { width: 18px; height: 18px; flex: none; }
.asis__fuera { color: var(--faint); }
.asis__fuera .asis__quitar { text-decoration: line-through; text-decoration-color: var(--line); }

@media (max-width: 620px) {
  .asis__estilos { grid-template-columns: repeat(3, 1fr); }
  .asis__pasos { gap: 5px; }
  .asis__paso { padding: 3px 8px; font-size: 11px; }
}

@media (max-width: 620px) {
  .modal { padding: 0; place-items: end stretch; }
  .modal__panel { border-radius: var(--r-l) var(--r-l) 0 0; max-height: 92vh; }
  .modal__head { padding: 18px 20px 12px; }
  .modal__body { padding: 16px 20px 24px; }
}

/* ------------------------------------------------------- lista de compra */

.compra__totales {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  overflow: hidden;
  margin: 20px 0 24px;
}
.compra__nivel {
  background: var(--card);
  padding: 11px 13px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.compra__nivel b { font-size: 14px; }
.compra__nivel .label { text-transform: capitalize; }

.compra { display: flex; flex-direction: column; gap: 14px; }
.compra__pieza {
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  padding: 15px 17px;
}
.compra__cab { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.compra__cab b { font-size: 15px; }
.compra__niveles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 12px; }
.compra__niveles .compra__nivel { background: var(--surface); border-radius: 6px; padding: 8px 10px; }
.compra__pieza code { font-size: .88em; background: var(--surface); border-radius: 4px; padding: 1px 5px; }

@media (max-width: 620px) {
  .compra__totales, .compra__niveles { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------ pantalla del inmueble
   El ritmo común de las pestañas: secciones a 32, título→contenido a 12, y un
   solo grid de dos columnas para todas (la ancha pesa un 30 % más). Las cifras
   dentro van con su ritmo corto de 8/16. */
/* Cada sección del informe se separa con una raya y aire (16-09-2026): con
   seis bloques seguidos, el ojo necesita saber dónde empieza cada uno. */
.sec { margin-top: 36px; padding-top: 22px; border-top: 1px solid var(--line); }
.sec > .eyebrow { margin-bottom: 12px; }
.sec > .m2__cab .eyebrow, .sec > .eyebrow { font-size: 14px; letter-spacing: .04em; color: var(--ink); }
.select--sm { padding: 5px 28px 5px 9px; font-size: 12.5px; min-height: 0; }
.kpis--plano { border-top: 0; }
.avisos-card { border-left: 3px solid var(--spark); padding-left: 16px; }
.cols { display: flex; gap: 28px; flex-wrap: wrap; align-items: flex-start; }
.cols > * { flex: 1; min-width: 260px; }
.cols > .col--wide { flex: 1.3; min-width: 300px; }
/* Tablas anchas: scroll propio, nunca de la página. */
.tabla-x { overflow-x: auto; }
.tabla-x > * { min-width: 430px; }

/* ---------------------------------------------- gráficas de la inversión
   (fase 6 del plan de mejora). Añadido al final a propósito: más arriba hay
   trabajo sin commitear y aquí no se reordena nada. */

/* El verde de «esto sale a cuenta». La paleta no tenía verde y el puente y
   el meter del umbral lo necesitan como pareja del --danger: tierra apagado,
   como el resto de «Ciruela y arena». */
:root { --ok: #3E7A52; }

/* Mapa de calor de la sensibilidad 3×3: el fondo de la celda dice cómo queda
   el precio máximo frente al pedido. color-mix sobre las variables del tema,
   para que aguante un cambio de paleta sin retocar esto. */
.heat { display: flex; flex-direction: column; gap: 2px; padding: 5px 8px; border-radius: 6px; }
.heat--ok { background: color-mix(in srgb, var(--ok) 14%, transparent); }
.heat--justo { background: color-mix(in srgb, var(--muted) 10%, transparent); }
.heat--mal { background: color-mix(in srgb, var(--danger) 13%, transparent); }
/* El escenario del plan (obra y valor base) va señalado con borde; en sombra
   interior para no mover el layout de la rejilla. */
.heat--centro { box-shadow: inset 0 0 0 1.5px var(--accent-line); }
.heat__pct { color: var(--muted); }

/* El meter del diferencial contra el umbral: el fill es el diferencial de la
   zona y la marca vertical, el umbral (obra más gastos por m²). Necesita
   overflow visible porque la marca sobresale del carril a propósito. */
.meter--umbral { position: relative; overflow: visible; height: 10px; border-radius: 5px; }
.meter--umbral .meter__fill { border-radius: 5px; }
.meter__marca {
  position: absolute;
  top: -4px;
  bottom: -4px;
  width: 2px;
  border-radius: 1px;
  background: var(--ink);
}

/* ==========================================================================
   Mapa de precios por zona

   SVG plano, sin teselas ni librería de mapas: las manchas son los polígonos
   que genera scripts/geo-zonas.mjs y el color lo pone public/zonemap.js. Lo
   que hay aquí es el marco, el realce al pasar por encima y la leyenda de la
   escala; los rellenos van en el atributo `fill` de cada path, que es donde
   los pone la escala de cuantiles.
   ========================================================================== */

.zm-marco {
  margin-top: 20px;
  padding: 14px;
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-s);
}

/* El alto sale de la forma de la zona, así que un municipio alargado —o
   España con Canarias sin insertar— se comía dos pantallas y dejaba la leyenda
   fuera de la vista. Con el tope, el `preserveAspectRatio` de «meet» encoge el
   dibujo y lo centra: se ve el mapa entero y su escala de una vez. */
.zm { display: block; width: 100%; height: auto; max-height: 66vh; }

/* La línea entre zonas se dibuja con el trazo de cada mancha: así no hace
   falta un mapa de fronteras aparte y siempre casan. */
.zm__zona {
  stroke: var(--card);
  stroke-width: .7;
  stroke-linejoin: round;
  cursor: pointer;
  transition: opacity .12s ease;
}

.zm__zona:hover,
.zm__zona:focus-visible { opacity: .82; outline: none; }

/* El realce va con un trazo de tinta por encima, no con un color de relleno
   distinto: cambiar el relleno al pasar el ratón haría leer mal la escala. */
.zm__zona:hover,
.zm__zona:focus-visible,
.zm__zona.is-activa {
  stroke: var(--ink);
  stroke-width: 1.6;
  paint-order: stroke;
}

.zm__zona--vacia { cursor: default; }

/* El término municipal, por debajo de las manchas: solo se ve donde el
   reparto no llega, que no debería ser en ningún sitio. */
.zm__contorno { fill: none; stroke: var(--line); stroke-width: 1; }

.zm-leyenda {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 0 2px;
  margin-top: 12px;
}

.zm-leyenda__paso { display: flex; flex-direction: column; gap: 4px; min-width: 54px; }
.zm-leyenda__color { display: block; height: 10px; border-radius: 2px; }
/* La misma trama que pinta el mapa, no la de los huecos de foto: si la
   leyenda enseña otra, deja de explicar lo que se ve. */
.zm-leyenda__color--vacio {
  background: repeating-linear-gradient(45deg, #F4F3FA 0 2px, #E4E2F2 2px 4px);
}
.zm-leyenda__cifra { font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; }
.zm-leyenda__unidad { align-self: flex-end; margin-left: 10px; font-size: 12px; color: var(--muted); }

/* Las ciudades con mapa propio: tarjetas de entrada, no una lista de enlaces
   sueltos — es la acción principal de la pantalla de España. */
.zm-ciudades { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }

.zm-ciudad {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 16px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-m);
  background: var(--card);
  text-decoration: none;
  color: inherit;
  transition: border-color .12s ease, box-shadow .12s ease;
}

.zm-ciudad:hover { border-color: var(--accent-line); box-shadow: var(--shadow-s); }
.zm-ciudad__nombre { font-weight: 600; }

.zm-tabla { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.zm-tabla th {
  text-align: left;
  font-weight: 600;
  font-size: 12px;
  color: var(--faint);
  padding: 0 0 6px;
  border-bottom: 1px solid var(--line-soft);
}
.zm-tabla th.num, .zm-tabla td.num { text-align: right; font-variant-numeric: tabular-nums; }
.zm-tabla td { padding: 5px 0; border-bottom: 1px solid var(--line-soft); }
.zm-tabla tr:last-child td { border-bottom: 0; }

/* El recuadro de Canarias: una guía discreta que dice «esto está movido», no
   un marco que compita con las manchas. */
.zm__inserto { fill: none; stroke: var(--line); stroke-width: 1; stroke-dasharray: 3 3; }

/* Las columnas de cifras necesitan aire: sin él «5460 €/m²» y «6280 €/m²»
   se leen como un solo número larguísimo. */
.zm-tabla td.num, .zm-tabla th.num { padding-left: 14px; white-space: nowrap; }
.zm-tabla td:first-child { padding-right: 8px; }

/* El rótulo que sigue al puntero sobre el mapa. Va en el documento y no dentro
   del SVG para que pueda salirse del dibujo sin recortarse, y para poder
   peinarlo con la tipografía de la casa — el <title> nativo no admite ni una
   cosa ni la otra, y además tarda un segundo largo en aparecer. */
.zm-marco { position: relative; }

.zm-rotulo {
  position: absolute;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 11px;
  border-radius: var(--r-s);
  background: var(--ink);
  color: var(--paper);
  font-size: 12.5px;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: var(--shadow-m);
}

.zm-rotulo b { font-weight: 600; }
.zm-rotulo span { color: rgba(248, 247, 252, .72); font-variant-numeric: tabular-nums; }

/* Los nombres sobre las manchas: por encima del color y por debajo del
   rótulo, y sin capturar el puntero — si lo hicieran, el ratón «entraría» en
   el texto y el rótulo parpadearía. */
.zm__nombre {
  font-size: 11px;
  font-weight: 500;
  fill: var(--ink);
  paint-order: stroke;
  stroke: var(--card);
  stroke-width: 2.5px;
  stroke-linejoin: round;
  pointer-events: none;
}

/* --------------------------------------------------------------- usuarios

   La tabla de administración. Alineada a la izquierda —son datos, no una
   comparativa como `.tabla-planes`— y con el envoltorio que le deja hacer
   scroll horizontal por su cuenta: sin él, con nueve columnas la página entera
   se movía de lado en el portátil. */
.tabla-envoltura { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-m); background: var(--card); }
.tabla-admin { width: 100%; border-collapse: collapse; font-size: 13.5px; min-width: 720px; }
.tabla-admin th, .tabla-admin td { padding: 12px 14px; text-align: left; vertical-align: middle; }
.tabla-admin thead th {
  font-size: 12px;
  font-weight: 600;
  color: var(--faint);
  background: var(--surface);
  white-space: nowrap;
}
.tabla-admin tbody tr + tr td { border-top: 1px solid var(--line-soft); }
.tabla-admin .badge { margin-left: 6px; vertical-align: middle; }
.admin__regalo { display: flex; gap: 8px; align-items: center; }
.admin__regalo .input { width: 84px; }

/* El precio de antes, tachado al lado del de la oferta. Pequeño y apagado: la
   cifra que manda es la que se paga hoy, y el tachón solo tiene que dar la
   referencia. */
.plan__antes { font-size: 15px; color: var(--faint); text-decoration-thickness: 1px; }
.plan__oferta { font-size: 12.5px; color: var(--accent); margin-top: 6px; font-weight: 500; }

/* ------------------------------------------------ el veredicto compartido

   La página pública de un inmueble (COMPARTIR.md §3). Es una página de la web
   y no una pantalla de la app: hereda `.web`, la cabecera y el pie, y no carga
   ningún guion del bundle.

   Va a lo ancho y a dos columnas (16-09-2026). En una sola columna de 760 px
   la página salía larguísima y el veredicto —la nota y el desembolso, que es a
   lo que se entra— caía por debajo de la primera pantalla, empujado por una
   imagen a tamaño completo. */
.veredicto__fuente { margin-top: 12px; }
/* Dos tercios para las imágenes y un tercio para el veredicto: la galería es
   lo que se mira y la nota con las cifras es lo que se lee, y ninguna de las
   dos debe esperar a que la otra termine. */
.veredicto__cabeza {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 30px;
  align-items: start;
  margin-top: 30px;
}
.veredicto__ficha { display: flex; flex-direction: column; gap: 16px; }
.veredicto__detalle { margin-top: 44px; }
.veredicto__resumen { margin-top: 20px; max-width: 70ch; }

/* El rótulo de estado dentro del reparto por estancia. La lista pinta de gris
   su último hijo, y ese hijo era el rótulo: «Anticuado» salía en gris sobre
   coral y «A reformar» en gris sobre rojo, ilegibles los dos. */
.veredicto .cost-list__row .badge--alerta { color: #fff; }
.veredicto .cost-list__row .badge--on { color: var(--on-accent); }
.veredicto .cost-list__row .badge--diy { color: var(--accent); }
.veredicto .cost-list__row .badge--soft { color: var(--muted); }

/* La nota, que es el veredicto: primera de la fila y del tamaño de un titular. */
.veredicto__nota {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 20px 24px;
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  background: var(--card);
}
.veredicto__nota-num {
  font-family: var(--font-wm);
  font-size: 46px;
  font-weight: 700;
  letter-spacing: -1.5px;
  line-height: 1;
}
.veredicto__nota-num small { font-size: 14px; font-weight: 500; letter-spacing: 0; color: var(--faint); }
.veredicto__nota .label { display: block; margin-bottom: 4px; }
.veredicto__nota--alta .veredicto__nota-num { color: #2E7D5B; }
.veredicto__nota--media .veredicto__nota-num { color: var(--accent); }
.veredicto__nota--baja .veredicto__nota-num { color: var(--danger); }

.veredicto__cifra {
  font-family: var(--font-wm);
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -1.2px;
  line-height: 1.05;
  margin: 8px 0 14px;
  color: var(--accent);
}
.veredicto__cta { margin-top: 40px; }
.veredicto__pie { margin-top: 24px; }

/* La galería de la página: una imagen a la vez, con flechas. Las miniaturas
   se quitaron el 16-09-2026: en dos tercios de ancho eran tres recuadros de
   130 px que no se miraban, y el carrusel enseña cada estancia a un tamaño en
   el que se ve algo. */
.veredicto__hero { position: relative; }
.veredicto__hero-img {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  aspect-ratio: 3 / 2;
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  overflow: hidden;
  background: var(--surface);
  user-select: none;
  cursor: pointer;
}
.veredicto__hero-img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.veredicto__hero-img:hover { border-color: var(--ink); box-shadow: var(--shadow-m); }
.veredicto__hero .stamp--br { font-size: 11px; }

/* El escudo: una capa transparente sobre la imagen. Con ella, el clic derecho
   no ofrece «Guardar imagen» y la foto no se puede arrastrar fuera. Estorba,
   no impide: la URL del render está en el HTML. Grabar la marca dentro del PNG
   tiraría las credenciales C2PA, y eso no se toca (CREDITOS.md, fase 5). */
.veredicto__hero-img::after,
.vg__marco::after,
.vg__compare::after { content: ""; position: absolute; inset: 0; z-index: 3; }

.veredicto__flecha, .vg__flecha {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 999px;
  background: rgba(22, 20, 43, .55);
  color: var(--paper);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.veredicto__flecha:hover, .vg__flecha:hover { background: rgba(22, 20, 43, .8); }
.veredicto__flecha--izq { left: 14px; }
.veredicto__flecha--der { right: 14px; }

/* La marca de agua, en el centro de la imagen y no en una esquina: una esquina
   se recorta, el centro no. */
.marca-agua--centro {
  top: 50%;
  left: 50%;
  right: auto;
  transform: translate(-50%, -50%);
  opacity: .62;
}
.marca-agua--centro .logo__mark { width: 26px; height: 26px; }
.marca-agua--centro .logo__word { font-size: 23px; letter-spacing: .4px; }

/* El visor a pantalla completa, sobre el `.lightbox` de la aplicación. */
.vg__marco { position: relative; display: inline-block; line-height: 0; }
.vg__compare {
  width: min(95vw, 1100px);
  aspect-ratio: 3 / 2;
  max-height: 78vh;
  border-radius: 10px;
  box-shadow: var(--shadow-l);
}
.vg__flecha--izq { left: 18px; }
.vg__flecha--der { right: 18px; }
.vg__pie { text-align: center; max-width: 70ch; }
.vg__nombre {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 0;
  color: var(--paper);
  font-size: 14px;
}
.vg__nombre .num { color: #A9A5C4; font-size: 12px; }
/* Los rótulos de estado están pensados para fondo claro: sobre el del visor,
   «Anticuado» salía morado sobre morado. Aquí el texto va siempre claro y el
   color queda en el velo y el borde, que es lo que distingue un estado de
   otro. */
.vg .badge { color: var(--paper); border-color: rgba(248, 247, 252, .3); background: rgba(248, 247, 252, .14); }
.vg .badge--diy { border-color: rgba(169, 165, 232, .7); background: rgba(99, 93, 200, .38); }
.vg .badge--on { border-color: rgba(143, 214, 180, .6); background: rgba(46, 125, 91, .42); }
.vg .badge--alerta { border-color: rgba(255, 158, 146, .65); background: rgba(179, 56, 44, .5); }
.vg__motivo { margin: 7px 0 0; color: #A9A5C4; font-size: 12.5px; line-height: 1.5; }

@media (max-width: 900px) {
  .veredicto__cabeza { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  .veredicto__cifra { font-size: 28px; }
  .veredicto__nota-num { font-size: 38px; }
  .vg__flecha--izq { left: 6px; }
  .vg__flecha--der { right: 6px; }
}

/* El panel de compartir dentro de la app. */
.compartir__url { font-family: var(--font-mono, ui-monospace, monospace); font-size: 12.5px; }
.compartir__insignia { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.compartir__insignia summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.compartir__insignia summary:hover { color: var(--ink); }

/* ------------------------------------------------------ la ficha embebible

   Lo que la agencia pega en su web dentro de un iframe (COMPARTIR.md §15).
   Vive sobre `body.embed`, sin cabecera ni pie del sitio: dentro de un marco
   esos dos sobran y cada píxel de alto cuenta. Alturas fijas y declaradas,
   460 px la ficha y 96 px la tira, que son las que dice el código para pegar. */
body.embed { background: transparent; margin: 0; }
.embed { border-radius: 12px; background: var(--card); overflow: hidden; height: 100vh; display: flex; flex-direction: column; }
.embed__img { position: relative; flex: 1; min-height: 0; user-select: none; }
.embed__img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.embed__img::after { content: ""; position: absolute; inset: 0; z-index: 3; }
.embed__cuerpo { padding: 13px 16px 4px; }
.embed__fila { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 13px; padding: 7px 0; border-bottom: 1px solid var(--line-soft); }
.embed__fila:last-child { border-bottom: 0; }
.embed__pie { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 11px 16px; border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); }
.embed__pie-nota b { font-size: 18px; font-weight: 600; margin-right: 3px; }
.embed__salir { color: var(--accent); font-weight: 600; white-space: nowrap; }
.embed__salir:hover { text-decoration: underline; }

.embed--barra { flex-direction: row; align-items: center; gap: 15px; padding: 0 18px; border: 1px solid var(--line); }
.embed__nota { font-size: 27px; font-weight: 700; letter-spacing: -1px; line-height: 1; flex: none; }
.embed__medio { flex: 1; min-width: 0; }
.embed__titular { font-size: 13px; font-weight: 600; }

/* La nota, con los mismos tres colores que la página compartida. */
.embed .veredicto__nota--alta { color: #2E7D5B; }
.embed .veredicto__nota--media { color: var(--accent); }
.embed .veredicto__nota--baja { color: var(--danger); }
.embed .stamp--ia { font-size: 10px; padding: 3px 7px; left: 8px; bottom: 8px; }
.embed .marca-agua--centro .logo__mark { width: 18px; height: 18px; }
.embed .marca-agua--centro .logo__word { font-size: 16px; }

/* -------------------------------------------------------- la pestaña Publicar

   Un taller y no un formulario: se elige a dónde va la pieza y se ve cómo
   queda allí. El lienzo es el de verdad, el mismo que se descarga, así que lo
   que se ve es exactamente lo que se sube. */
.pub-destinos { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 10px; }
.pub-destinos button {
  display: flex; flex-direction: column; gap: 5px; align-items: flex-start;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-m);
  background: var(--card); cursor: pointer; font: inherit; color: inherit; text-align: left;
}
.pub-destinos button:hover { border-color: var(--ink); }
.pub-destinos button[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: var(--accent-soft); }
.pub-destinos b { font-size: 13.5px; }
.pub-destinos span { font-size: 11.5px; color: var(--faint); line-height: 1.4; }

.pub-taller { display: grid; grid-template-columns: 1.4fr 1fr; gap: 28px; align-items: start; }
.pub-lienzo { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-l); padding: 24px 18px; }
@media (max-width: 900px) { .pub-taller { grid-template-columns: 1fr; } }

/* El marco de la previsualización: un teléfono con la proporción que tiene un
   teléfono. El marco ancho de la primera versión mentía, porque todo parecía
   caber y luego en el móvil no cabía. 393 × 852 son los puntos de una pantalla
   de iPhone actual, y la isla se dibuja porque es lo que come la franja de
   arriba, que es justo la que hay que respetar al componer las láminas. */
.pub-iphone {
  position: relative;
  width: 290px;
  aspect-ratio: 393 / 852;
  margin: 0 auto;
  padding: 9px;
  border-radius: 46px;
  background: var(--ink);
  box-shadow: var(--shadow-l), inset 0 0 0 1.5px rgba(255, 255, 255, .14);
}
.pub-iphone__isla {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  width: 84px;
  height: 25px;
  border-radius: 999px;
  background: #000;
  z-index: 5;
}
.pub-iphone__pantalla {
  height: 100%;
  border-radius: 38px;
  overflow: hidden;
  background: var(--card);
  display: flex;
  flex-direction: column;
}
.pub-iphone--historia .pub-iphone__pantalla { background: #000; }

/* La barra de estado, que es lo que deja sitio a la isla. */
.pub-estado {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 22px 7px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  flex: none;
}
.pub-estado__der { display: flex; align-items: center; gap: 5px; }
.pub-estado__der svg { height: 10px; width: auto; fill: currentColor; }

.pub-post__cab { display: flex; align-items: center; gap: 9px; padding: 8px 12px; flex: none; }
.pub-post__av { width: 30px; height: 30px; border-radius: 999px; background: var(--accent); color: var(--on-accent); display: grid; place-items: center; flex: none; }
.pub-post__av--sm { width: 24px; height: 24px; }
.logo--av { gap: 0; }
.logo--av .logo__mark { width: 17px; height: 17px; }
.logo--av .logo__word { display: none; }
.pub-post__av--sm .logo__mark { width: 13px; height: 13px; }
.pub-post__quien { font-size: 12.5px; font-weight: 600; line-height: 1.2; }
.pub-post__donde { font-size: 11px; color: var(--muted); }
.pub-post__lienzo { position: relative; flex: none; }
.pub-post__acc { display: flex; gap: 14px; padding: 8px 12px 2px; color: var(--ink); flex: none; }
.pub-post__acc svg { width: 21px; height: 21px; }
.pub-post__pie { padding: 4px 12px 10px; font-size: 11.5px; line-height: 1.45; color: var(--muted); overflow: hidden; }
.pub-post__pie b { color: var(--ink); font-weight: 600; }
.pub-puntos { display: flex; gap: 5px; justify-content: center; padding: 8px 0 2px; flex: none; }
.pub-puntos button { width: 6px; height: 6px; padding: 0; border: 0; border-radius: 999px; background: var(--line); cursor: pointer; }
.pub-puntos button.on { background: var(--accent); }

/* La historia ocupa la pantalla entera, y ahí se ve lo que ya sabíamos: una
   lámina 9:16 en un teléfono más alargado deja franja arriba y abajo, que es
   justo donde la red social pone su interfaz. */
.pub-iphone--historia .pub-post__lienzo--historia { flex: 1; min-height: 0; display: grid; place-items: center; overflow: hidden; }
.pub-historia__prog { display: flex; gap: 3px; padding: 46px 10px 0; flex: none; }
.pub-historia__prog i { flex: 1; height: 2px; border-radius: 2px; background: rgba(255, 255, 255, .35); }
.pub-historia__prog i.on { background: #fff; }
.pub-historia__cab { display: flex; align-items: center; gap: 8px; padding: 10px 12px; color: #fff; font-size: 12px; flex: none; }
.pub-historia__hora { color: rgba(255, 255, 255, .6); }
.pub-historia__responder { padding: 0 14px 16px; flex: none; }
.pub-historia__responder span {
  display: block;
  border: 1px solid rgba(255, 255, 255, .45);
  border-radius: 999px;
  padding: 9px 16px;
  font-size: 12px;
  color: rgba(255, 255, 255, .7);
}
.pub-canvas { display: block; width: 100%; }
/* El lienzo de la historia se escala como una imagen, manteniendo la
   proporción y cabiendo de alto: con `width:100%` a secas empujaba la caja de
   responder fuera de la pantalla. */
/* En el feed la lámina ocupa el ancho; en historia se encoge hasta caber de
   alto, que es lo que hace ver las franjas que deja un 9:16 en un teléfono
   más alargado. */
.pub-canvas--carrusel { height: auto; }
.pub-canvas--historia { width: auto; height: auto; max-width: 100%; max-height: 100%; }
.pub-check { display: flex; align-items: center; gap: 9px; font-size: 13px; cursor: pointer; }
.pub-check input { accent-color: var(--accent); width: 15px; height: 15px; flex: none; }

/* La ficha del portal, para ver el texto puesto antes de pegarlo. */
.pub-portal { border: 1px solid var(--line); border-radius: var(--r-m); overflow: hidden; background: var(--card); margin-top: 12px; }
.pub-portal__barra { background: var(--surface); padding: 8px 14px; font-size: 11.5px; color: var(--faint); border-bottom: 1px solid var(--line); }
.pub-portal__cuerpo { display: grid; grid-template-columns: 1fr 1.2fr; }
.pub-portal__foto { position: relative; min-height: 180px; background: var(--surface); display: grid; place-items: center; }
.pub-portal__foto img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pub-portal__datos { padding: 16px 18px 20px; }
.pub-portal__precio { font-size: 23px; font-weight: 700; letter-spacing: -.6px; }
.pub-portal__desc { margin-top: 14px; font-size: 12.5px; line-height: 1.6; color: var(--muted); }
.pub-inserto { background: var(--accent-soft); border-left: 3px solid var(--accent); padding: 10px 12px; margin: 10px 0; border-radius: 0 6px 6px 0; color: var(--ink); }
@media (max-width: 700px) { .pub-portal__cuerpo { grid-template-columns: 1fr; } }

/* ------------------------------------------------- el ranking (COMPARTIR.md §17)

   Una lista numerada con la nota a la derecha, como la ficha de la página
   compartida pero en una línea. Las filas tapadas no llevan texto real:
   son huecos pintados, y el candado dice por qué. */
.ranking { list-style: none; margin: 26px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.ranking__fila {
  position: relative; display: flex; align-items: center; gap: 16px;
  padding: 14px 18px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r-l);
}
.ranking__pos { flex: 0 0 30px; font-size: 20px; font-weight: 600; letter-spacing: -.6px; color: var(--faint); text-align: center; }
.ranking__foto { position: relative; flex: 0 0 96px; height: 72px; border-radius: 10px; overflow: hidden; background: var(--ph1); }
.ranking__foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ranking__foto .stamp { transform: scale(.8); transform-origin: bottom left; }
.ranking__cuerpo { flex: 1 1 auto; min-width: 0; }
.ranking__titulo { margin: 0; font-size: 15.5px; font-weight: 600; letter-spacing: -.2px; }
.ranking__titulo .micro { font-weight: 400; margin-left: 6px; }
.ranking__cifras { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 8px; font-size: 14px; }
.ranking__cifras span { display: inline-flex; flex-direction: column; gap: 2px; }
.ranking__cifras small { font-size: 11.5px; color: var(--faint); font-weight: 500; }
.ranking__cifras em { font-style: normal; font-size: 12px; color: var(--muted); margin-left: 4px; }
.ranking__nota { flex: 0 0 auto; display: inline-flex; align-items: baseline; gap: 3px; }
.ranking__nota b { font-size: 26px; font-weight: 700; letter-spacing: -1px; }
.ranking__nota small { font-size: 12px; color: var(--faint); }
.ranking__nota.veredicto__nota--alta b { color: #2E7D5B; }
.ranking__nota.veredicto__nota--media b { color: var(--accent); }
.ranking__nota.veredicto__nota--baja b { color: var(--danger); }
.ranking__fila--tapada { border-style: dashed; }
.ranking__hueco { display: inline-block; height: 12px; border-radius: 6px; background: var(--surface); vertical-align: middle; }
.ranking__hueco--titulo { width: 46%; height: 15px; }
.ranking__hueco--linea { width: 62%; }
.ranking__hueco--cifra { width: 84px; height: 14px; margin-top: 3px; }
.ranking__candado { position: absolute; top: 10px; right: 12px; color: var(--faint); }
.ranking__desbloqueo { margin-top: 14px; border-color: var(--accent); }
.ranking__desbloqueo-fila { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
.ranking__aviso { margin: 0 0 12px; font-size: 13.5px; color: var(--muted); }
.ranking__aviso--ok { color: #2E7D5B; }
.ranking__cta { margin-top: 36px; }
.ranking__ciudades { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; margin-top: 22px; }
.ranking__ciudad {
  display: flex; flex-direction: column; gap: 6px; padding: 18px 20px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-l); text-decoration: none; color: inherit;
}
.ranking__ciudad:hover { border-color: var(--accent); }
.ranking__ciudad-nombre { margin: 0; }
.ranking__ciudad-primero { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-size: 13.5px; color: var(--muted); }
.ranking__ciudad-primero .ranking__nota b { font-size: 22px; }
.ranking__zonas th:first-child, .ranking__zonas td:first-child { text-align: left; }
@media (max-width: 640px) {
  .ranking__fila { flex-wrap: wrap; }
  .ranking__foto { flex-basis: 100%; height: 150px; order: -1; }
  .ranking__nota { margin-left: auto; }
}

/* ------------------------------------------ el informe de captación (CAPTACION.md)

   Un documento de papel dentro de la app: blanco, con la marca de la agencia
   arriba y el descargo abajo. `--marca` es el color de la agencia, y solo
   colorea el filete y las cifras: el resto es tinta. */
.captacion {
  --marca: var(--accent);
  background: #fff; border: 1px solid var(--line); border-radius: var(--r-l);
  padding: 34px 38px 28px; max-width: 820px;
}
.captacion__cab { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; padding-bottom: 14px; border-bottom: 3px solid var(--marca); }
.captacion__marca { display: flex; align-items: center; gap: 14px; font-size: 17px; font-weight: 600; letter-spacing: -.3px; }
.captacion__logo { max-height: 44px; max-width: 160px; object-fit: contain; display: block; }
.captacion__contacto { text-align: right; line-height: 1.6; }
.captacion__cifras { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px; margin-top: 24px; }
.captacion__cifra { display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--r-m); min-width: 0; }
/* Un rango («40.730 a 64.950 €») es el doble de largo que una cifra: cuerpo
   más contenido y permiso para partir por la «a», nunca por el número. */
.captacion__cifra b { font-size: 21px; font-weight: 600; letter-spacing: -.5px; color: var(--marca); line-height: 1.25; overflow-wrap: anywhere; }
.captacion__cifra--rango b { font-size: 16.5px; letter-spacing: -.3px; }
.captacion__figura { position: relative; margin: 26px 0 0; }
.captacion__figura img { width: 100%; max-height: 380px; object-fit: cover; border-radius: var(--r-m); display: block; }
.captacion__figura .stamp { bottom: 34px; }
.captacion__figura figcaption { margin-top: 8px; }
.captacion__pie { margin-top: 30px; padding-top: 14px; border-top: 1px solid var(--line); }
.captacion__descargo { margin: 10px 0 0; padding-left: 18px; line-height: 1.55; }
.captacion__descargo li + li { margin-top: 4px; }
@media print {
  .captacion { border: 0; padding: 0; max-width: none; }
  .captacion__cifra, .captacion__cab { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .captacion__figura { break-inside: avoid; }
}

/* ==========================================================================
   El aviso de cookies de la web pública (lib/web.mjs). Solo aparece en
   estrenova.com y solo si hay ID de Analytics. Flota abajo en vez de empujar
   la página: así no mueve nada al aparecer ni al irse.
   ========================================================================== */
.aviso-cookies {
  position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 50;
  max-width: 600px; margin: 0 auto;
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px 18px;
  padding: 14px 16px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-l);
  box-shadow: var(--shadow-l);
}
.aviso-cookies[hidden] { display: none; }
.aviso-cookies p { flex: 1 1 280px; margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--muted); }
.aviso-cookies__botones { display: flex; gap: 8px; margin-left: auto; }

/* La lista de espera: la página que ocupa /app mientras la aplicación no está
   abierta (scripts/desplegar-web.mjs). */
.espera { display: grid; gap: 16px; margin-top: 28px; }
.espera .btn { justify-self: start; }
.espera__trampa { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
