
  @import url('https://fonts.googleapis.com/css2?family=Geist:wght@500;600;700&family=Playfair+Display:ital,wght@1,500&family=Inter:wght@300;400;500;600;700&display=swap');

  * { box-sizing: border-box; margin: 0; padding: 0; }
  a { text-decoration: none; color: inherit; }

  :root {
    --black: #1a1a1a;
    --white: #ffffff;
    --cream: #ece9e4;
    --cream-dark: #e0dcd6;
    --text: #1a1a1a;
    --muted: #888;
    --border: #e0dcd6;
    --accent: #1e40af;
    --accent-hover: #1e3a8a;
    /* El fondo del chat. Antes era blanco, igual que la caja de escribir, asi
       que la caja no se veia: era un borde flotando sobre su mismo color.
       Con el fondo un punto por debajo, lo blanco pasa a estar ENCIMA de
       algo. Vale para la caja, para los mensajes y para los botones, que
       tampoco se distinguian. */
    --fondo-chat: #fafafa;
    /* Los dos tonos flojos del acento. La seccion de Proyectos tenia su propio
       color -- un naranja var(--accent) en carpetas, bordes al enfocar, el proyecto
       activo, la casilla y hasta el poligono del mapa -- asi que la app tenia
       DOS acentos: azul para enviar, naranja para proyectos. Unificados en uno.
       El naranja que queda (#c2410c) no es acento: significa error o borrar, y
       tiene que seguir sin parecerse al color de la accion normal. */
    --accent-suave: #eef2fc;
    --accent-borde: #c5d2ee;
    /* Mismo matiz que el acento (226 grados), dos escalones mas claro. No es
       un color nuevo: es el MISMO azul un rango por debajo, y existe para que
       las carpetas de cada proyecto no pesen igual que la del menu que las
       agrupa. Si hiciera falta en mas sitios, es siempre lo mismo -- lo de
       dentro, mas claro que lo que lo contiene. */
    --accent-claro: #758cd7;
    /* El verde del cumple. La paleta tenia dos significados --azul para la
       accion y #c2410c para el error-- y el tick de "esto cumple" iba en
       azul, o sea en el mismo color que enviar o seleccionar. Con el verde,
       las tres cosas se distinguen sin leer. */
    --verde: #157347;
    --verde-suave: #e9f4ee;
    /* La linea de un pixel que parte un bloque en celdas. Es gris frio
       a proposito: --border (#e0dcd6) viene del fondo crema y sobre
       blanco tira a amarillo. */
    --rejilla: #e4e4e7;
  }

  html { scroll-behavior: smooth; }

  /* Los controles de formulario no heredan la tipografia: el navegador les
     pone la suya. Hoy no se ve porque todos nuestros botones llevan solo un
     icono SVG dentro, pero el dia que uno lleve texto saldria en Arial. */
  button, input, select, textarea { font-family: inherit; }

  body {
    font-family: 'Inter', sans-serif;
    color: var(--text);
    font-size: 15px;
    line-height: 1.6;
    background-color: #ffffff;
    /* Recorta el 100vw de la linea de la barra. Va aqui y no en #landingView
       --donde estuvo y rompia la linea a partir de 1320-- porque el body es
       lo unico que mide siempre lo que mide la pantalla.
       'clip' y no 'hidden', por lo mismo que en .hero-band: hidden crearia
       un contenedor de scroll y rompe el position:sticky de la barra. */
    overflow-x: clip;
  }

  /* Grid overlay on every section */
  #landingView nav, section, footer {
    background-image:
      linear-gradient(rgba(224,220,214,0.3) 1px, transparent 1px),
      linear-gradient(90deg, rgba(224,220,214,0.3) 1px, transparent 1px);
    background-size: 60px 60px;
  }



  /* ── NAV ── */






  .btn-ghost {
    padding: 8px 18px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: transparent;
    font-family: inherit;
    font-size: 13.5px;
    color: var(--text);
    cursor: pointer;
    text-decoration: none;
    transition: all 0.12s;
  }

  .btn-ghost:hover { background: var(--cream); }

  .btn-primary {
    padding: 8px 18px;
    border: none;
    border-radius: 7px;
    background: var(--black);
    font-family: inherit;
    font-size: 13.5px;
    color: #fff;
    cursor: pointer;
    text-decoration: none;
    transition: all 0.12s;
  }

  .btn-primary:hover { background: #333; }

  .btn-accent {
    padding: 8px 18px;
    border: none;
    border-radius: 7px;
    background: var(--accent);
    font-family: inherit;
    font-size: 13.5px;
    color: #fff;
    cursor: pointer;
    text-decoration: none;
    transition: all 0.12s;
  }

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

  /* El destello de los botones de accion principal (el de la barra y el de la
     portada), a la manera del de maite.ai: el color no es del boton sino de
     dos capas detras del texto.
       ::before  un degradado de tres azules, repetido y a 800 % de tamano, que
                 viaja por el boton en 1,2 s y se queda quieto otros 1,9 s: un
                 destello que lo cruza cada 3,1 s sin parar.
       ::after   el degradado fijo, a opacidad 0; con el raton encima sube a 1 en
                 0,6 s y el destello queda por debajo.
     Las capas van a z-index -1 dentro del propio boton (z-index: 0 le crea un
     contexto de apilamiento) y el overflow las recorta al radio. El azul de la
     marca es el del medio; el oscuro y el claro son los extremos del barrido.
     Se añade como clase aparte (.cta-destello) a .btn-accent o .btn-hero: el
     tamano y la letra los sigue poniendo cada uno. Va DESPUES de .btn-accent
     para ganarle el fondo, y el :hover repite el transparent porque el de
     .btn-accent:hover es mas especifico. */
  .cta-destello {
    --cta-medio: #1e40af;
    --cta-oscuro: #132a7c;
    --cta-claro: #5b7bea;
    position: relative;
    z-index: 0;
    overflow: hidden;
    background: transparent;
    transition: none;
  }
  .cta-destello:hover { background: transparent; }
  .cta-destello::before,
  .cta-destello::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    z-index: -1;
    background-size: 800% 800%;
  }
  .cta-destello::before {
    background-image: linear-gradient(135deg,
      var(--cta-medio), var(--cta-oscuro), var(--cta-claro), var(--cta-medio),
      var(--cta-medio), var(--cta-oscuro), var(--cta-claro), var(--cta-medio));
    animation: cta-destello 3.1s linear infinite;
  }
  .cta-destello::after {
    background-image: linear-gradient(107deg,
      var(--cta-oscuro) 0%, var(--cta-medio) 60%, var(--cta-claro) 120%);
    background-size: 100% 100%;
    opacity: 0;
    transition: opacity 0.6s;
  }
  .cta-destello:hover::after { opacity: 1; }
  @keyframes cta-destello {
    0%   { background-position: 100% 100%; }
    40%  { background-position: left 37.5% top 37.5%; }
    100% { background-position: left 37.5% top 37.5%; }
  }
  /* Sin animaciones: el boton se queda en el azul de la marca, quieto. */
  @media (prefers-reduced-motion: reduce) {
    .cta-destello::before { animation: none; background-position: left 37.5% top 37.5%; }
  }

  /* ── HERO ── */
  /* La retícula cubre toda la banda del hero, así que el fondo y el degradado viven en
     la banda que los envuelve. z-index explícito para crear contexto de
     apilamiento: sin él, los hijos con z-index negativo se pintarían por
     detrás del fondo opaco y no se verían. */
  .hero-band {
    position: relative;
    z-index: 0;
    background-color: #ffffff;
    /* El panel de color de la derecha NO llega de lado a lado, a proposito:
       las bandas crema, la foto y el pie si, pero el azul de detras de la
       ventana conserva sus limites laterales y acaba en su columna, con las
       esquinas redondas. Antes se estiraba 100vw y se recortaba aqui; ya no,
       y el clip se queda de seguro para que nada de la portada ensanche la
       pagina por la derecha.
       'clip' y no 'hidden' a proposito: hidden crearia un contenedor de
       scroll y rompe el position:sticky de la barra. */
    overflow-x: clip;
  }

  .hero {
    /* 100 arriba. Estuvo en 104, bajo a 64 para que el panel llegase a tocar
       la barra, subio a 88, despues a 132 al quitar la linea bajo la barra
       (sin la raya, el titular pegaba a la barra y hacia falta aire), y baja
       a 100 cuando la barra gano 16 px por encima: entre las dos ya habia
       mas hueco del necesario. El panel mantiene su hueco con la barra -- lo
       que se resta aqui se resta tambien en su top negativo.
       Y 152 abajo, para que el panel azul --que se estira 121px por debajo
       de su columna-- acabe con aire antes de que empiece la seccion crema,
       en vez de tocarla. */
    padding: 100px 0 152px;
    position: relative;
    background-color: transparent;
  }

  /* La retícula global tiene contraste ~4/255: no se ve. En el hero se sube
     a ~20 (como la de Ichi) y se desvanece con el degradado de abajo, así
     asoma por las esquinas y desaparece detrás del titular. */
  /* La retícula del hero la pintan líneas sueltas (.hg-line), no el fondo de
     la sección: hace falta que sean elementos para poder iluminarlas una a
     una en ola. */
  .hero { background-image: none; background-color: transparent; }

  .hero-grid {
    position: absolute;
    inset: 0;
    z-index: -2;
    display: block;
    width: 100%;
    height: 100%;
  }

  /* Sin reticula en la PORTADA: va sobre blanco a secas. Por id y no por
     clase -- la misma clase la lleva el lienzo de "Para quien", que si tiene
     que verse. El canvas se queda en el marcado y se apaga aqui, que es una
     linea en vez de tocar el JS que lo dibuja. */
  #heroGrid { display: none; }

  /* «Para quien» va igual que la portada: sobre blanco a secas, sin la
     reticula. El lienzo se queda en el marcado y se apaga aqui. */
  #whoGrid { display: none; }

  .hero-inner {
    /* Sin max-width propio: así el contenido arranca en el margen de la
       página, alineado con el logo y con la primera línea de la retícula. */
    display: grid;
    /* No mitad y mitad: la columna del panel es mas estrecha que la del texto,
       asi el panel queda vertical en vez de cuadrado. */
    grid-template-columns: 1.15fr 0.85fr;
    gap: 80px;
    align-items: center;
    position: relative;
    z-index: 1;
  }

  .hero-left h1 {
    font-family: 'Geist', sans-serif;
    /* Medido en chatbase.co: 64 px en escritorio, 48 en movil, y en las dos
       anchuras interlineado 1.1 y tracking -0.04em. Estaba en 56 px con
       -1.5px, que a ese cuerpo son -0.027em: casi la mitad de apretado. */
    /* Como doctrine.it: titular y subtitulo de cuerpos cercanos (50 y 30) y
       pegados, de modo que se leen como un solo bloque de texto y no como un
       titular grande con letra pequeña debajo. La tipografia es la de siempre
       (Geist 500 con el apretado de -0.04em). Estaba en 64 px. */
    font-size: 50px;
    font-weight: 500;
    line-height: 1.15;
    /* En em y no en px, para que al cambiar el cuerpo en movil el apretado se
       escale solo -- que es lo que hacen ellos. */
    letter-spacing: -0.04em;
    color: var(--black);
    margin-bottom: 10px;
    /* Reparte las dos lineas: sin esto «exacto» se quedaba solo en la segunda. */
    text-wrap: balance;
  }


  /* El sector, en cursiva y rotando. Va en su propia linea: asi la frase de
     arriba no se recoloca al cambiar de palabra, que es lo que pasaria si
     "obra civil" e "instalaciones" tuvieran que caber en el mismo renglon.
     Cursiva de Geist de verdad (ital,wght@1,500 en el import): sin ella el
     navegador inclinaria la redonda y las letras saldrian deformadas. */
  /* La ultima palabra, en cursiva y de otra familia. La referencia usa Proza
     Display, que es de pago. El contraste no lo da la inclinacion: lo da que
     sea otra especie de letra dentro de la frase. */
  .hero-acento {
    font-family: 'Playfair Display', Georgia, serif;
    font-style: italic;
    font-weight: 500;
    color: var(--accent);
    /* El titular lleva -0.04em de tracking, puesto para una grotesca. En una
       serif con remates ese mismo apretado junta las letras hasta pegarlas, y
       ahora que ha subido de -0.027 se notaria mas. */
    letter-spacing: 0;
  }

  .hero-left p {
    /* El subtitulo, grande y en la misma tipografia de la pagina (Inter): es lo
       que explica el titular y se lee como la segunda mitad de la frase. */
    font-size: 30px;
    font-weight: 400;
    color: var(--black);
    line-height: 1.25;
    letter-spacing: -0.02em;
    margin-bottom: 32px;
    max-width: 1180px;
  }
  /* nowrap: «Tú tienes el control.» no se parte entre dos lineas. */
  .hero-left p strong { color: var(--black); font-weight: 600; white-space: nowrap; }

  .hero-cta {
    display: flex;
    gap: 10px;
    align-items: center;
  }

  .btn-hero {
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    color: #fff;
    cursor: pointer;
    text-decoration: none;
  }

  /* App preview */
  .hero-right {
    position: relative;
  }

  /* La ventana de la app deja de flotar sobre blanco y se apoya en un panel
     de color que se sale por la derecha hasta el borde del marco. La trama
     diagonal va a un 10% de blanco. Estuvo al 5 y ahi no llegaba a leerse
     como trama: se intuia y poco mas. */
  .hero-right::before {
    content: '';
    position: absolute;
    /* El panel NO toca la barra: empieza 44 px por debajo de ella, el mismo
       aire que le queda por abajo antes de la seccion crema (152 - 108). Sin
       la raya bajo la barra, el azul pegado al blanco se leia como un corte
       y no como un panel. Es -(padding del hero - 44), o sea que va atado a
       el: con 100 arriba, 100 - 44 = 56. Si ese padding cambia, este cambia
       con el. Al hacer scroll el panel sigue metiendose por debajo de la
       barra, que es sticky y blanca. Abajo el panel acaba antes que la
       seccion crema, por eso no es simetrico: -108 contra -56. */
    top: -56px;
    bottom: -108px;
    left: -28px;
    right: 0;
    z-index: -1;
    /* Redondo por las cuatro esquinas. Antes el panel se estiraba 100vw por
       la derecha y se recortaba recto en el borde de la banda, asi que solo
       podia ir cuadrado: era una banda que salia por un lado. Ahora acaba en
       el margen de su columna -- que es donde lo recortaba el borde de la
       banda, o sea que no se mueve -- y es un panel con cuatro esquinas. 24
       porque la ventana de dentro lleva 14 y el panel la rodea: el exterior
       tiene que ser mayor que el interior para que las curvas sean paralelas. */
    border-radius: 24px;
    /* El color lo pone el ACTO: cada grupo trae el suyo y el JS lo
       escribe en --panel. El acento queda de reserva por si no corre. */
    background-color: var(--panel, var(--accent));
    background-image:
      repeating-linear-gradient(45deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 68px),
      repeating-linear-gradient(-45deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 68px);
    /* Viaja con el cruce lateral en vez de saltar: los mismos 1.150 ms
       que tarda el cambio de acto. */
    transition: background-color 1.15s cubic-bezier(0.4, 0, 0.2, 1);
  }

  /* La ventana de la app, a la manera de sauna.ai: clara, muy redondeada y
     dentro de un aro blanco translucido, como un cristal esmerilado puesto
     sobre el panel de color. Sin barra de titulo ni fila de pestanas: la
     ventana empieza directamente en la escena. El aro es un box-shadow con extension y no un
     marco de verdad: sigue el radio del borde y no mueve nada de sitio. */
  .app-preview {
    position: relative;
    background: #f8f7f5;
    border: 1px solid rgba(255,255,255,0.8);
    border-radius: 20px;
    overflow: hidden;
    /* La ventana se mete por los dos lados: 72 px de azul a la izquierda y
       otros 72 a la derecha, contando lo que el panel sobresale por su lado.
       Sin esto la ventana acababa justo donde acaba el panel y las dos cosas
       se cortaban a la vez, como si faltara media pantalla. */
    margin-left: 44px;
    margin-right: 72px;
    /* Aire por encima, para el selector de los tres videos: el aro de 8 px de
       la ventana casi tocaba a la pastilla (10 px de hueco menos 8 de aro).
       Con 14 mas quedan unos 16 px visibles. */
    margin-top: 14px;
    /* Aro de 8 px y debajo la sombra de siempre, mas marcada que antes: sobre
       el panel de color la de 0.12 no se veia y la ventana parecia pegada al
       fondo. */
    box-shadow: 0 0 0 8px rgba(255,255,255,0.20), 0 30px 80px rgba(6,14,50,0.38);
  }


  /* El selector de los tres videos, centrado sobre la ventana: una pastilla
     translucida con los tres filtros y el activo en blanco, como el de
     sauna.ai. Va dentro de la franja que el panel de color deja libre encima
     de la ventana (56 px: el panel sobresale lo mismo por arriba que lo que
     mide el top negativo del ::before), asi que no empuja nada. La ventana va
     metida 44 px por la izquierda y 72 por la derecha dentro del panel, y el
     selector lleva los mismos margenes para quedar justo encima de ella. */
  .pv-selector {
    position: absolute;
    left: 44px;
    right: 72px;
    top: -46px;
    z-index: 3;
    display: flex;
    justify-content: center;
    pointer-events: none;
  }
  /* El grupo: la pastilla translucida que envuelve a los tres filtros. */
  .pv-grupo {
    display: inline-flex;
    gap: 2px;
    padding: 4px;
    border-radius: 999px;
    background: rgba(10, 22, 80, 0.30);
    border: 1px solid rgba(255,255,255,0.22);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    pointer-events: auto;
  }
  .pv-filtro {
    appearance: none;
    border: 0;
    background: transparent;
    font: 500 11.5px/1 'Geist', sans-serif;
    letter-spacing: -0.1px;
    color: rgba(255,255,255,0.82);
    padding: 8px 14px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
  }
  .pv-filtro:hover { color: #fff; }
  .pv-filtro.activa {
    background: #fff;
    color: var(--black);
    box-shadow: 0 2px 8px rgba(8, 20, 80, 0.25);
  }
  .pv-filtro:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

  /* ── Portada apilada: titular arriba, panel con la ventana debajo ──
     PRUEBA. En una columna el panel ya no sale por un lado: se queda dentro
     de los margenes de la pagina (los mismos que las bandas crema) y la
     ventana se centra en el, mas ancha que en dos columnas porque ahora
     dispone de todo el ancho. En movil no cambia nada: las reglas de
     max-width 820 de mas abajo ya lo ponian en una columna. */
  /* 112 arriba (antes 64): mas aire entre la barra y el titular. */
  .hero { padding: 112px 0 120px; }
  .hero-inner { grid-template-columns: 1fr; gap: 56px; align-items: start; }
  /* Texto a la izquierda, como doctrine.fr: titular, parrafo y boton parten del
     margen izquierdo, el mismo que el logo y la ventana de abajo. */
  .hero-left { text-align: left; }
  /* 620 y no 460: con 460 el parrafo se iba a cuatro lineas, la ultima con una
     sola palabra («control.»). A 620 caben tres, y el balanceo las reparte
     parejas en vez de dejar la tercera corta. */
  .hero-left p { margin-left: 0; margin-right: 0; max-width: 1180px; text-wrap: pretty; }
  .hero-cta { justify-content: flex-start; }
  .hero-right { padding: 56px 0; }
  .hero-right::before { top: 0; bottom: 0; left: 0; right: 0; }
  .app-preview { max-width: 1000px; margin-left: auto; margin-right: auto; }
  /* El selector de los tres videos: aqui el panel deja 56 px libres por encima
     de la ventana (el padding de .hero-right), y el selector se centra en todo
     el ancho, que es el de la ventana. */
  .pv-selector { left: 0; right: 0; top: 10px; }
  /* En escritorio los tres ejemplos viven en la barra lateral de la ventana
     (.pv-lado), asi que la pastilla de encima sobra: vuelve en movil. */
  .pv-selector { display: none; }
  .hero-right { padding: 36px 0 44px; }

  /* La ventana como la app: barra de titulo arriba, barra lateral a la
     izquierda y el chat a su derecha. Rejilla y no flex: la barra de titulo
     ocupa las dos columnas y el raton (position:absolute) no entra en ella. */
  .app-preview {
    display: grid;
    grid-template-columns: 208px minmax(0, 1fr);
    grid-template-rows: auto 1fr;
  }
  .pv-barra-titulo {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    height: 34px;
    padding: 0 14px;
    background: #fff;
    border-bottom: 1px solid #ebe9e4;
    position: relative;
  }
  .pv-puntos { display: inline-flex; gap: 6px; }
  .pv-puntos i { width: 9px; height: 9px; border-radius: 50%; background: #dcdad5; }
  .pv-titulo-ventana {
    position: absolute; left: 0; right: 0; text-align: center;
    font: 500 11px/1 'Geist', sans-serif; color: #9a978f; letter-spacing: -0.1px;
  }
  .pv-lado {
    grid-row: 2;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 10px;
    background: #fff;
    border-right: 1px solid #ebe9e4;
    min-width: 0;
  }
  .pv-lado-seccion {
    font: 500 10.5px/1 'Geist', sans-serif;
    color: #a3a099;
    padding: 10px 8px 6px;
  }
  .pv-lado-seccion:first-child { padding-top: 2px; }
  .pv-lado-proyecto, .pv-lado-nuevo {
    display: flex; align-items: center; gap: 8px;
    font: 500 12px/1.3 'Geist', sans-serif;
    color: var(--black);
    padding: 7px 8px;
    border-radius: 8px;
  }
  .pv-lado-proyecto svg { width: 14px; height: 14px; flex: none; color: #6b6963; }
  .pv-lado-nuevo {
    margin: 4px 0 2px;
    border: 1px solid #e4e2dc;
    color: #4b4944;
  }
  .pv-lado-nuevo span { font-size: 14px; line-height: 1; color: #8a877f; }
  .pv-lado-item {
    appearance: none;
    border: 0;
    background: transparent;
    text-align: left;
    font: 400 12px/1.35 'Geist', sans-serif;
    color: #55534d;
    padding: 8px;
    border-radius: 8px;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease;
    /* Una linea con puntos suspensivos, como la lista de chats de la app. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .pv-lado-item:hover { background: #f4f3ef; color: var(--black); }
  .pv-lado-item.activa { background: #eeedf7; color: var(--black); font-weight: 500; }
  .pv-lado-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
  /* Con 820 de ancho, el cuerpo de 11,5 px de la ventana estrecha se queda
     pequeno: aqui todo sube un punto y el dialogo gana ancho y alto para
     que el mapa del catastro se vea grande. */
  .pv-conversacion .preview-text,
  .pv-conversacion .pv-burbuja,
  .preview-question { font-size: 13px; }
  .preview-etiqueta { font-size: 10px; }
  .preview-chip { font-size: 11px; }
  .pv-hora, .pv-autor span, .pv-fuente { font-size: 10px; }
  .pv-ubi { font-size: 11.5px; }
  .pv-caja { max-width: 520px; }
  .pv-sub, .pv-pestana, .pv-estado { font-size: 11.5px; }
  .pv-input, .pv-btn { font-size: 12px; }
  .pv-mapa.visible { max-height: 260px; }

  .preview-body {
    background: #f8f7f5;
    /* Fijo, no minimo: la respuesta se monta por bloques y si el alto siguiera
       al contenido la ventana creceria a tirones, y con ella el panel de color
       que la sostiene. Lo que no cabe se sube solo, como en un chat de verdad.
       520 y no 480: la revision del plano (ficha y plano al lado) mide unos
       360, y el hueco de la conversacion, descontada la caja de escribir,
       son 344. */
    height: 520px;
    position: relative;
    overflow: hidden;
  }

  /* ── La conversacion ── */
  /* Es lo unico que se desplaza. La caja de escribir y el dialogo flotan
     encima, fuera de ella: por eso abajo se deja sitio (118) para que lo
     ultimo que sale no quede tapado. */
  .pv-conversacion {
    position: absolute;
    inset: 0;
    padding: 18px 18px 118px;
    overflow-y: auto;
    /* Sin barra: el scroll lo hace el JS (abajo()) a medida que sale la
       respuesta, y una barra gris dentro de la ventana la hacia parecer una
       maqueta con un widget del sistema pegado. */
    scrollbar-width: none;
    /* Lo que sube y se va por arriba se desvanece en vez de cortarse en seco
       contra el borde de la ventana. Cabe en el padding de 18 px: en reposo
       no toca el contenido. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 18px);
    mask-image: linear-gradient(to bottom, transparent 0, #000 18px);
  }
  .pv-conversacion::-webkit-scrollbar { display: none; }

  /* El turno: las dos rondas (pregunta sin ubicacion, y pregunta con ella).
     Al pasar al acto siguiente sube y se va, y el nuevo llega desde abajo.
     Antes se vaciaba y se volvia a escribir en el sitio, que se leia como un
     borrado, no como una conversacion que avanza. */
  .pv-turno {
    display: flex;
    flex-direction: column;
    gap: 16px;
    transition: opacity 0.62s cubic-bezier(0.4, 0, 0.2, 1),
                transform 0.62s cubic-bezier(0.4, 0, 0.2, 1);
  }
  .pv-turno.sale { opacity: 0; transform: translateY(-22px); }
  .pv-turno.llega { opacity: 0; transform: translateY(22px); transition: none; }

  .pv-ronda { display: flex; flex-direction: column; gap: 12px; }

  /* Lo que se ha preguntado, ya enviado: una burbuja gris a la derecha con su
     hora debajo, que es como lo pone sauna.ai. */
  .pv-usuario {
    align-self: flex-end;
    max-width: 88%;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.3s ease, transform 0.3s ease;
  }
  .pv-usuario.entra { opacity: 1; transform: none; }
  .pv-burbuja {
    /* Como la burbuja del chat de verdad (.msg-user): azul flojo y la esquina
       de abajo, hacia su lado, mas cerrada. */
    background: var(--accent-suave);
    color: var(--text);
    border-radius: 16px 16px 4px 16px;
    padding: 9px 13px;
    font-size: 11.5px;
    line-height: 1.5;
  }
  /* El plano adjunto, dentro de la burbuja de quien pregunta: el nombre del
     fichero y lo que se ha leido de el. */
  .pv-adjunto {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 7px;
    padding: 5px 9px;
    border-radius: 9px;
    background: rgba(255,255,255,0.75);
    font-size: 10.5px;
    line-height: 1.3;
    color: #555;
  }
  .pv-adjunto svg { width: 13px; height: 13px; flex: none; color: var(--accent); }
  .pv-adjunto b { font-weight: 600; color: var(--black); }

  /* ── La respuesta ── */
  .preview-answer {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    /* Aparece cuando hay algo que ensenar y se va al cambiar de acto. */
    opacity: 0;
    transition: opacity 0.3s ease;
  }
  .preview-answer.entra { opacity: 1; }

  .preview-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #1a1a1a;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 700;
    color: #fff;
    flex-shrink: 0;
    font-family: 'Geist', sans-serif;
  }

  .preview-text {
    font-size: 11.5px;
    color: #444;
    line-height: 1.6;
    flex: 1;
    min-width: 0;
  }
  .preview-text strong { font-weight: 600; color: var(--text); }

  /* Quien contesta, con su rotulo: nombre y etiqueta en una linea sobre el
     texto, como el "Sauna · Your AI teammate" de la referencia. */
  .pv-autor { display: flex; align-items: baseline; gap: 7px; margin-bottom: 7px; }
  .pv-autor b { font: 600 11.5px/1 'Geist', sans-serif; letter-spacing: -0.1px; color: var(--text); }
  .pv-autor span { font-size: 9.5px; color: #a29d95; }

  /* Los parrafos de la pregunta del chat: texto corrido, sin tarjeta. */
  .pv-linea + .pv-linea { margin-top: 8px; }

  /* "Pensando", mientras se busca en el corpus: un giratorio con lo que se
     esta haciendo y una etiqueta de estado, como la fila "Querying PostHog
     · Working" de sauna.ai. */
  .preview-pensando {
    display: none;
    gap: 7px;
    align-items: center;
    height: 22px;
  }
  .preview-pensando.entra { display: inline-flex; }
  /* La chispa y la palabra que brilla son las del chat real (.ai-chispa y
     .ai-thinking, mas arriba): aqui solo se les baja el cuerpo al de la ventana. */
  .preview-pensando .ai-thinking { font-size: 11.5px; }
  .pv-spin {
    width: 11px;
    height: 11px;
    flex: none;
    border-radius: 50%;
    border: 1.5px solid #d6d1c9;
    border-top-color: var(--accent);
    animation: pvGira 0.8s linear infinite;
  }
  @keyframes pvGira { to { transform: rotate(360deg); } }
  .pv-estado-etiqueta {
    padding: 2px 7px;
    border-radius: 6px;
    background: var(--accent-suave);
    color: var(--accent);
    font-size: 9px;
    font-weight: 600;
  }

  /* La respuesta larga se monta por bloques. Cada uno es una tarjeta blanca,
     como la del grafico de sauna.ai: la respuesta deja de ser un texto corrido
     y se lee por piezas. */
  .preview-bloque {
    background: var(--white);
    border: 1px solid #ebe9e5;
    border-radius: 12px;
    padding: 9px 12px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.03);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.32s ease, transform 0.32s ease;
  }
  .preview-bloque.entra { opacity: 1; transform: none; }
  .preview-bloque + .preview-bloque { margin-top: 8px; }

  .preview-etiqueta {
    font-family: 'Geist', sans-serif;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #a8a29a;
    margin-bottom: 3px;
  }

  /* El bloque que cruza las normas es la respuesta de verdad: va sobre el
     azul flojo de la marca para que se vea que es la conclusion. */
  .preview-bloque.destacado {
    background: var(--accent-suave);
    border-color: var(--accent-borde);
  }
  .preview-bloque.destacado .preview-etiqueta { color: var(--accent); }
  .preview-linea + .preview-linea { margin-top: 3px; }

  /* La respuesta, como en el chat real: parrafos corridos (.ai-text), con el
     numero de la cita pegado a la cifra y las chapas de las normas debajo.
     Sin tarjetas ni etiquetas: eso no lo hace el chat. */
  .pv-parrafos + .pv-parrafos, .pv-linea + .pv-parrafos, .pv-parrafos + .pv-linea { margin-top: 8px; }
  .pv-parrafos .cita-n { display: none; cursor: default; }
  .pv-parrafos .pv-p.lista .cita-n { display: inline-flex; }
  .preview-text .sources:empty { display: none; }
  .preview-text .sources { gap: 6px; margin-top: 10px; padding-top: 9px; }
  .preview-text .chip { font-size: 10px; color: #5f5b55; padding: 4px 9px 4px 7px; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
  .pv-chip { opacity: 0; transform: translateY(4px); transition: opacity 0.28s ease, transform 0.28s ease; }
  .pv-chip.entra { opacity: 1; transform: none; }
  .pv-chip svg { width: 9px; height: 9px; opacity: 0.5; }

  .preview-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
  }

  .preview-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 10px 5px 8px;
    background: var(--white);
    border: 1px solid #e4e1dc;
    border-radius: 999px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    font-size: 10px;
    color: #5f5b55;
    /* La cita no se parte: o cabe entera o baja a la linea siguiente. Partida
       en dos deja de leerse como una referencia. */
    white-space: nowrap;
    /* Entran una detras de otra cuando la respuesta ya esta escrita: la cita
       es lo ultimo que aparece porque es lo que hay que mirar. */
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.28s ease, transform 0.28s ease;
  }
  .preview-chip.entra { opacity: 1; transform: none; }
  /* El icono de documento, para que la cita se lea como una referencia y no
     como una etiqueta cualquiera. Es una mascara y no una imagen para que
     tome el color del acento. */
  .preview-chip::before {
    content: '';
    width: 11px;
    height: 11px;
    flex: none;
    background: var(--accent);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a1.5 1.5 0 0 0-1.5 1.5v15A1.5 1.5 0 0 0 7 21h10a1.5 1.5 0 0 0 1.5-1.5V7.5z'/%3E%3Cpath d='M14 3v4.5h4.5'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a1.5 1.5 0 0 0-1.5 1.5v15A1.5 1.5 0 0 0 7 21h10a1.5 1.5 0 0 0 1.5-1.5V7.5z'/%3E%3Cpath d='M14 3v4.5h4.5'/%3E%3C/svg%3E") center / contain no-repeat;
  }

  /* La atribucion del dato de la parcela. La pone el backend por codigo cuando
     usa el planeamiento de Valencia (la licencia CC BY 4.0 la exige), asi que
     la respuesta real la lleva al final y la demo tambien. */
  .pv-fuente {
    display: none;
    margin-top: 8px;
    font-size: 9px;
    line-height: 1.45;
    font-style: italic;
    color: #a29d95;
    opacity: 0;
    transition: opacity 0.3s ease;
  }
  .pv-fuente.hay { display: block; }
  .pv-fuente.entra { opacity: 1; }

  /* ── La revision del plano (tercer video) ── */
  /* La misma ficha que la caja «Revisión de planos» de la seccion de producto
     --los estilos .rev-* son los de alli--, y al lado el propio plano con las
     zonas que hay que corregir. Esta dentro de la respuesta, no en un bloque
     suelto, asi que va en el flujo de la conversacion y sube con ella.
     Lleva su propio contenedor: con ancho de sobra (ventana apilada) van la
     lista a la izquierda y el plano a la derecha; en estrecho, uno debajo de
     otro. La consulta es del contenedor y no de la pantalla porque la ventana
     mide distinto segun la maqueta de la portada. */
  .pv-rev { display: none; margin-top: 2px; container-type: inline-size; }
  .pv-rev.hay { display: block; }
  .pv-rev-malla { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-items: start; }
  @container (min-width: 540px) {
    .pv-rev-malla { grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); gap: 14px; }
  }

  .pv-plano {
    margin: 0;
    background: var(--white);
    border: 1px solid var(--rejilla);
    border-radius: 10px;
    padding: 10px 12px 8px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.02), 0 8px 18px rgba(0,0,0,0.03);
    opacity: 0;
    transform: translateY(5px);
    transition: opacity 0.4s ease, transform 0.4s ease;
  }
  .pv-plano.entra { opacity: 1; transform: none; }
  .pv-plano-cab {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
    font-size: 10px;
    line-height: 1.2;
    color: #a3a3a3;
  }
  .pv-plano-cab b {
    font: 600 9px/1 'Geist', sans-serif;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #a8a29a;
  }
  .pv-plano-leyenda { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
  .pv-plano-leyenda i { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }
  .pv-plano-leyenda i.mal { background: #c2410c; }
  .pv-plano-leyenda i.bien { background: var(--verde); margin-left: 6px; }
  .pv-plano svg { display: block; width: 100%; height: auto; }

  /* El dibujo: muros oscuros, trazo fino para puertas y escalera, y los dos
     colores de la ficha de al lado (naranja = no cumple, verde = cumple). */
  .pv-plano .p-muro { fill: #2e2d36; }
  .pv-plano .p-fino { fill: none; stroke: #8f8b96; stroke-width: 0.9; stroke-linejoin: round; }
  .pv-plano .p-vent { stroke: #6f6b78; stroke-width: 0.8; }
  .pv-plano .p-rotulo { fill: #b3aeb8; font: 500 8.5px 'Geist', sans-serif; letter-spacing: 0.08em; }
  .pv-plano text { font-family: 'Geist', sans-serif; }
  .pv-plano .p-mal { fill: #c2410c; }
  .pv-plano .p-bien { fill: var(--verde); }
  .pv-plano .p-cota { font-size: 11.5px; font-weight: 600; }
  .pv-plano .p-cota-fina { font-size: 9.5px; font-weight: 500; }
  .pv-plano .p-tinte-mal { fill: rgba(194, 65, 12, 0.11); }
  .pv-plano .p-linea-mal { fill: none; stroke: #c2410c; stroke-width: 1.3; }
  /* El circulo que cumple lleva el tinte y el trazo en el mismo elemento. */
  .pv-plano .p-zona-bien { fill: rgba(21, 115, 71, 0.10); stroke: var(--verde); stroke-width: 1.3; }
  .pv-plano .p-punteado { stroke-dasharray: 4 3; }

  /* Cada zona marcada entra con su fila: la fila dice que falla y el plano
     enseña donde. Primero se funde la zona y despues salta la marca. */
  .pv-plano .pl-m { opacity: 0; transition: opacity 0.45s ease; }
  .pv-plano .pl-m.entra { opacity: 1; }
  .pv-plano .pl-pop {
    transform-box: fill-box;
    transform-origin: center;
    transform: scale(0.4);
    transition: transform 0.4s cubic-bezier(0.2, 1.5, 0.4, 1);
  }
  .pv-plano .pl-m.entra .pl-pop { transform: none; }

  @media (prefers-reduced-motion: reduce) {
    .pv-plano, .pv-plano .pl-m, .pv-plano .pl-pop { transition: none; }
  }

  /* ── Lo de abajo: la caja de escribir y el boton de ubicacion ── */
  /* Fijo abajo y fuera de la conversacion, como el del chat real. Lo que se
     teclea va aqui; al enviar, pasa a la burbuja y la caja queda vacia con su
     sugerencia. */
  .pv-pie {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 18px 12px 12px;
    /* Con fondo propio: sin el, la conversacion que sube por debajo se veia
       entre la caja y el boton, y debajo del boton. El degradado de arriba
       hace que lo que llega no se corte contra la caja. */
    background: linear-gradient(to bottom, rgba(248,247,245,0), #f8f7f5 18px);
  }

  /* ── El estado vacio: la caja de escribir en el centro ── */
  /* Como en el chat real: sin conversacion, la caja esta en medio de la
     ventana con su saludo encima; al enviar la primera pregunta baja al fondo
     y empieza la conversacion. La clase .vacio la quita el JS al enviar. Se
     ancla por la base (bottom) y no por el centro para que, si lo que se
     teclea ocupa dos lineas, la caja crezca hacia arriba y no se desplace. */
  .pv-pie {
    transition: bottom 0.65s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.4s ease;
  }
  .preview-body.vacio .pv-pie {
    bottom: calc(50% - 84px);
    background: none;
  }
  .pv-saludo {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(50% + 40px);
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    padding: 0 24px;
    text-align: center;
    opacity: 0;
    transform: translateY(8px);
    pointer-events: none;
    transition: opacity 0.4s ease, transform 0.5s ease;
  }
  .pv-saludo b {
    font: 500 22px/1.2 'Geist', sans-serif;
    letter-spacing: -0.5px;
    color: var(--black);
  }
  .pv-saludo span { font-size: 11.5px; line-height: 1.5; color: #8a857d; }
  .preview-body.vacio .pv-saludo { opacity: 1; transform: none; }
  /* La pista: en escritorio apunta a la barra de la izquierda, en movil a los
     filtros de arriba. Se va con el primer clic (la clase .jugado del marco). */
  .pv-pista { display: block; margin-top: 10px; font-size: 11px; font-weight: 500; color: var(--accent); }
  .pv-pista-movil { display: none; }
  .app-preview.jugado .pv-pista { display: none; }
  /* El primer chat de la lista late hasta que alguien lo pulsa: lo unico que
     hay que hacer en la ventana es pulsar ahi. */
  .pv-lado-item.invita { animation: pvInvita 1.8s ease-in-out infinite; background: #f4f6ff; }
  @keyframes pvInvita {
    0%, 100% { box-shadow: 0 0 0 0 rgba(30, 64, 175, 0.0); }
    50% { box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.22); }
  }
  @media (prefers-reduced-motion: reduce) { .pv-lado-item.invita { animation: none; } }
  @media (prefers-reduced-motion: reduce) {
    .pv-pie, .pv-saludo { transition: none; }
  }

  .preview-question {
    background: var(--white);
    border: 1px solid #e4e1dc;
    border-radius: 16px;
    padding: 9px 9px 9px 15px;
    font-size: 11.5px;
    color: var(--text);
    display: flex;
    align-items: flex-end;
    gap: 10px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.06);
  }
  .preview-question-texto { flex: 1; min-width: 0; line-height: 1.5; padding-bottom: 4px; }
  .pv-placeholder { display: none; color: #aaa59d; }
  #previewPregunta:empty ~ .pv-placeholder { display: inline; }

  .preview-enviar {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    /* Cuadrado de esquinas blandas, no circulo: es el boton del chat real. */
    border-radius: 9px;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0.28;
    transition: opacity 0.25s ease, transform 0.1s ease;
  }
  .preview-enviar svg { width: 14px; height: 14px; display: block; }
  .preview-enviar.listo { opacity: 1; }
  .preview-enviar.pulsa { transform: scale(0.86); }
  /* Al entrar: la pregunta ya escrita y el boton de enviar resaltado, latiendo,
     hasta que alguien lo pulsa. */
  .preview-enviar.invita {
    opacity: 1;
    cursor: pointer;
    animation: pvEnviarInvita 1.6s ease-in-out infinite;
  }
  .preview-enviar.invita:hover { transform: scale(1.08); }
  @keyframes pvEnviarInvita {
    0%, 100% { box-shadow: 0 0 0 0 rgba(30, 64, 175, 0.0); }
    50% { box-shadow: 0 0 0 5px rgba(30, 64, 175, 0.25); }
  }
  @media (prefers-reduced-motion: reduce) { .preview-enviar.invita { animation: none; box-shadow: 0 0 0 4px rgba(30, 64, 175, 0.25); } }

  /* «Añadir ubicacion»: el mismo boton que hay debajo del chat de verdad
     (borde fino, texto gris) y, con la ubicacion puesta, en el azul de marca
     como el .con-ubicacion del chat. */
  .pv-herramientas { display: flex; gap: 6px; }
  .pv-ubi {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    padding: 4px 9px;
    border: 1px solid #e0dcd6;
    border-radius: 5px;
    background: transparent;
    font-size: 10.5px;
    color: #999;
    white-space: nowrap;
    transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, transform 0.1s ease;
  }
  .pv-ubi svg { width: 11px; height: 11px; flex: none; }
  .pv-ubi span { overflow: hidden; text-overflow: ellipsis; }
  .pv-ubi.pulsa { background: #f5f3f0; color: #1a1a1a; transform: scale(0.95); }
  .pv-ubi.con { color: #1e40af; border-color: #c9d3f2; background: #f2f5fd; }

  /* El puntero es un dibujo, no el raton de verdad: se coloca con transform
     desde el JS y viaja hasta el boton antes de que ocurra lo que pulsa. */
  .preview-raton {
    position: absolute;
    left: 0;
    top: 0;
    /* 38: la ventana de la portada no esta a escala 1:1 -- es una maqueta
       pequena dentro de un panel, y a tamano real el puntero se perdia. */
    width: 38px;
    height: 38px;
    z-index: 6;
    opacity: 0;
    pointer-events: none;
    filter: drop-shadow(0 2px 5px rgba(0,0,0,0.28));
    transition: transform 950ms cubic-bezier(0.33, 0, 0.15, 1), opacity 260ms ease;
  }
  .preview-raton.visible { opacity: 1; }

  .preview-caret {
    display: inline-block;
    color: var(--accent);
    animation: parpadeoCursor 1s step-end infinite;
  }
  .preview-caret.oculto { visibility: hidden; }
  @keyframes parpadeoCursor { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

  /* ── El dialogo «Ubicacion de este chat» ── */
  /* Es el mismo cuadro del chat real (titulo, pestanas Referencia catastral /
     Direccion, campo, Buscar y despues Usar esta ubicacion), a escala de
     maqueta. Lo unico que el cuadro real no lleva es el mapa: aqui sale al
     buscar, porque es lo que hace ver que la parcela es la que es. */
  .pv-fondo {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 12px;
    background: rgba(26,26,26,0.42);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.35s ease, visibility 0s linear 0.35s;
  }
  .pv-fondo.show { opacity: 1; visibility: visible; transition: opacity 0.35s ease; }

  .pv-caja {
    width: 100%;
    max-width: 420px;
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 16px 16px 14px;
    box-shadow: 0 20px 60px rgba(26,26,26,0.25);
    transform: translateY(10px);
    transition: transform 0.35s ease;
  }
  .pv-fondo.show .pv-caja { transform: none; }

  .pv-caja h4 {
    font: 500 14px/1.2 'Geist', sans-serif;
    letter-spacing: -0.3px;
    color: var(--black);
    margin: 0 0 4px;
  }

  /* Titulo y explicacion se pliegan cuando sale el mapa: sin eso el cuadro
     no cabe en el alto de la ventana. */
  .pv-intro {
    max-height: 90px;
    overflow: hidden;
    transition: max-height 0.5s ease, opacity 0.4s ease, margin 0.5s ease;
  }
  .pv-caja.con-mapa .pv-intro { max-height: 0; opacity: 0; margin-bottom: -4px; }
  .pv-sub { font-size: 10.5px; line-height: 1.5; color: #8a857e; margin: 0 0 4px; }
  .pv-alcance { font-size: 10px; color: var(--accent); margin: 0 0 11px; }

  .pv-pestanas { display: flex; gap: 5px; margin-bottom: 9px; }
  .pv-pestana {
    flex: 1;
    text-align: center;
    padding: 6px 8px;
    border-radius: 7px;
    border: 1px solid var(--border);
    background: var(--white);
    font-size: 10.5px;
    color: #6f6a62;
  }
  .pv-pestana.on { background: #e6ebfa; border-color: var(--accent); color: var(--accent); font-weight: 500; }

  .pv-input {
    display: flex;
    align-items: center;
    min-height: 31px;
    padding: 7px 10px;
    border: 1px solid var(--accent);
    border-radius: 7px;
    background: var(--white);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    letter-spacing: -0.2px;
    color: var(--black);
    white-space: nowrap;
    overflow: hidden;
  }
  #pvRC:empty ~ .pv-placeholder { display: inline; }

  .pv-estado { font-size: 10.5px; min-height: 15px; margin-top: 8px; color: #8a857e; }

  /* El mapa del catastro. El contenedor es un marco de proporcion fija y la
     imagen va dentro, ampliada y centrada en la parcela: asi la parcela se ve
     grande y el contorno (un svg en porcentaje sobre la MISMA caja que la
     imagen) cae donde tiene que caer. La imagen es un SVG propio, construido
     con los servicios INSPIRE del catastro (construir_mapa_catastro.py):
     vectorial, asi que las lineas salen nitidas a cualquier tamano y en
     cualquier pantalla. El PNG del WMS, reducido a la ventana, las dejaba
     desvaidas. */
  .pv-mapa {
    aspect-ratio: 2.2 / 1;
    max-height: 0;
    margin-top: 0;
    border-radius: 9px;
    border: 1px solid transparent;
    overflow: hidden;
    position: relative;
    background: var(--white);
    opacity: 0;
    transition: max-height 0.55s ease, margin-top 0.55s ease, opacity 0.45s ease, border-color 0.45s ease;
  }
  .pv-mapa.visible { max-height: 220px; margin-top: 10px; opacity: 1; border-color: #e6e2dc; }
  .pv-mapa-in {
    position: absolute;
    left: -25%;
    top: 50%;
    width: 150%;
    aspect-ratio: 3 / 2;
    transform: translateY(-50%);
  }
  .pv-mapa-in img { width: 100%; height: 100%; display: block; }
  /* La atribucion que pide el catastro. La imagen del WMS la llevaba impresa
     en una esquina; en el SVG propio va aqui, porque la esquina del encuadre
     queda fuera de lo que se ve. */
  .pv-mapa-fuente {
    position: absolute;
    right: 6px;
    bottom: 4px;
    font-size: 8px;
    line-height: 1;
    color: #8a857d;
    background: rgba(255,255,255,0.78);
    padding: 2px 4px;
    border-radius: 3px;
  }
  .pv-mapa-in svg { position: absolute; inset: 0; width: 100%; height: 100%; }

  /* El contorno se dibuja: el trazo va oculto detras de si mismo y se
     descubre. 900 es de sobra para el perimetro de este camino. */
  #pvParcela {
    fill: rgba(30, 64, 175, 0);
    stroke: var(--accent);
    stroke-width: 2.5;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
    stroke-dasharray: 900;
    stroke-dashoffset: 900;
    /* 1,7s, buscado a base de mirarlo: 1,1 se pasaba sin darte tiempo; 3,6 y
       2,8 se hacian largos. El relleno arranca a 1,45, cuando al trazo le
       queda poco para cerrarse: entrando antes se veia el color desbordar por
       donde la linea todavia no habia llegado. */
    transition: stroke-dashoffset 1.7s cubic-bezier(0.32, 0, 0.25, 1),
                fill 0.45s ease 1.45s;
  }
  .pv-mapa.encontrada #pvParcela {
    stroke-dashoffset: 0;
    fill: rgba(30, 64, 175, 0.30);
  }

  .pv-ok {
    display: none;
    margin-top: 10px;
    padding: 9px 11px;
    border-radius: 8px;
    background: #f5f3f0;
    border: 1px solid var(--border);
  }
  .pv-ok.show { display: block; }
  .pv-ok b { display: block; font-size: 11.5px; color: var(--black); margin-bottom: 1px; }
  .pv-ok span { font-size: 10px; color: #8a857e; }

  /* El panel de «Subir plano»: el de la propia app, no el del ordenador. Una
     zona de soltar con el plano ya puesto en la lista y un boton para
     subirlo, a la manera del «Importa i documenti» de doctrine.it. */
  .pv-sel {
    width: 100%;
    max-width: 380px;
    background: #fff;
    border: 1px solid #e6e3dd;
    border-radius: 14px;
    padding: 14px;
    box-shadow: 0 20px 60px rgba(26,26,26,0.25);
    transform: translateY(10px);
    transition: transform 0.35s ease;
  }
  .pv-fondo.show .pv-sel { transform: none; }
  .pv-imp-cab { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
  .pv-imp-cab b { font: 600 12px/1.2 'Geist', sans-serif; color: var(--black); }
  .pv-imp-cab span { font-size: 10px; color: #a29d95; }
  .pv-imp-zona {
    border: 1.5px dashed #d9d5ce;
    border-radius: 12px;
    background: #fafaf9;
    padding: 16px 14px 14px;
    text-align: center;
  }
  .pv-imp-icono { width: 26px; height: 26px; margin: 0 auto; color: #b5b0a7; }
  .pv-imp-icono svg { width: 100%; height: 100%; display: block; }
  .pv-imp-titulo { margin-top: 8px; font-size: 12px; font-weight: 500; color: #3a3834; }
  .pv-imp-sub { margin: 3px 0 12px; font-size: 10px; color: #a29d95; }
  .pv-imp-fila {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    background: #fff;
    border: 1px solid #ebe9e5;
    border-radius: 9px;
    font-size: 11px;
    color: #333;
    text-align: left;
  }
  .pv-imp-fila svg { width: 13px; height: 13px; flex: none; color: #9a958c; }
  .pv-imp-fila .pv-imp-nombre { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pv-imp-fila .pv-imp-peso { color: #b0aba2; font-size: 10px; }
  .pv-imp-boton {
    margin-top: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 8px 16px;
    border-radius: 9px;
    background: var(--black);
    color: #fff;
    font: 500 11.5px/1 'Geist', sans-serif;
    box-shadow: 0 0 0 3px #fff, 0 0 0 4.5px #d9d5ce;
    transition: transform 0.1s ease, filter 0.1s ease;
  }
  .pv-imp-boton svg { width: 13px; height: 13px; }
  .pv-imp-boton.pulsa { transform: scale(0.94); filter: brightness(0.9); }

  .pv-pie-caja { display: flex; gap: 7px; margin-top: 12px; }
  .pv-btn {
    flex: 1;
    text-align: center;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: 11.5px;
    font-weight: 500;
    transition: transform 0.1s ease, filter 0.1s ease;
  }
  .pv-btn.sec { background: var(--white); border: 1px solid var(--border); color: #6f6a62; }
  .pv-btn.pri { background: var(--black); color: #fff; }
  .pv-btn.pulsa { transform: scale(0.94); filter: brightness(0.9); }

  /* Sin animaciones: se deja el final de la primera consulta entero y quieto. */
  @media (prefers-reduced-motion: reduce) {
    .preview-raton { display: none; }
    .preview-caret, .pv-spin { animation: none; }
    .pv-turno, .pv-usuario, .preview-answer, .preview-bloque, .preview-chip,
    .pv-fuente, .pv-fondo, .pv-caja, .pv-mapa, #pvParcela { transition: none; }
    .preview-chip { opacity: 1; transform: none; }
  }

  /* ── FEATURES ── */
  .features-wrap {
    background-color: #ece9e4;
    position: relative;
    z-index: 0;
  }

  /* El fondo de las dos bandas crema (y la retícula) llega de lado a lado de
     la pantalla: vive en un ::before de 100vw centrado detras del contenido,
     que no se mueve de sus margenes. Antes era un cuadrado de 1256 con blanco
     a los lados. La retícula pasa de la seccion interior a la banda para no
     pintarse dos veces dentro y ninguna fuera. */
  .features-wrap::before, .pricing-wrap::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 100vw;
    z-index: -1;
    background-color: #ece9e4;
    background-image:
      linear-gradient(rgba(224,220,214,0.3) 1px, transparent 1px),
      linear-gradient(90deg, rgba(224,220,214,0.3) 1px, transparent 1px);
    background-size: 60px 60px;
    background-position: center top;
  }
  .features-wrap .features, .pricing-wrap .pricing { background-image: none; }

  .features {
    padding: 100px 48px;
    max-width: 1100px;
    margin: 0 auto;
    position: relative;
  }

  .features-wrap .features { padding: 100px 48px; }


  .section-label {
    font-size: 11px;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: #bbb;
    font-weight: 500;
    margin-bottom: 12px;
  }

  .section-title {
    font-family: 'Geist', sans-serif;
    font-size: 40px;
    font-weight: 500;
    letter-spacing: -1px;
    color: var(--black);
    margin-bottom: 16px;
    max-width: 600px;
    line-height: 1.15;
  }

  /* La segunda mitad del titular, en gris: es la frase que antes iba debajo
     como subtitulo, ahora dentro de la misma oracion. Y con las dos mitades
     seguidas hace falta mas ancho: a 600 se parte en cinco lineas. */
  .section-title .gris { color: #918b83; }
  /* La misma letra del titular, inclinada. Geist no tiene cursiva propia en Google
     Fonts: el navegador la inclina (oblicua), que en una grotesca se lee bien. */
  .section-title .cursiva { font-style: italic; }
  .section-title.ancho { max-width: 860px; margin-bottom: 56px; }
  /* Los titulares de "Producto", "Para quien" y "Precios" ocupan el ancho
     entero de la seccion, de lado a lado, en vez de cortarse a 860 y quedarse
     en tres lineas con el borde derecho suelto. En los tres la segunda mitad
     va en gris dentro del mismo titular. */
  #features .section-title.ancho,
  #who .section-title.ancho,
  #pricing .section-title.ancho { max-width: none; }

  .section-sub {
    font-size: 15px;
    color: #888;
    max-width: 500px;
    margin-bottom: 64px;
    line-height: 1.7;
  }

  /* ── El selector de producto, como el de sauna.ai ── */
  /* Una lista de cuatro puntos y, al lado, un panel grande. El punto elegido
     despliega su texto y se oscurece; el panel cambia de demo y de color (cada
     color es una capa aparte que entra por opacidad: un degradado no se anima,
     asi que lo que se cruza son cuatro fondos). */
  .sel {
    display: grid;
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    gap: 64px;
    align-items: center;
  }

  .sel-lista { display: flex; flex-direction: column; gap: 4px; }

  .sel-item {
    appearance: none;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    width: 100%;
    padding: 14px 0;
    border: 0;
    background: none;
    font: inherit;
    text-align: left;
    color: inherit;
    cursor: pointer;
  }
  .sel-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 8px; }

  /* El marcador de cada punto: un cuadrado de plano con marcas de esquina (las
     de un encuadre de laminas) y un pictograma propio -- el articulo (§), una
     planta con su puerta, una parcela acotada y un triangulo de revision --,
     en vez de un icono de aplicacion. El elegido se rellena en negro. */
  .sel-cuadro {
    position: relative;
    flex: none;
    width: 30px;
    height: 30px;
    margin: 3px 0 0 5px;
    border: 1.5px solid #a39e96;
    background: var(--crema-banda, #ece9e4);
    color: #a39e96;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
  }
  .sel-cuadro svg { width: 17px; height: 17px; }
  .sel-cuadro::before,
  .sel-cuadro::after {
    content: '';
    position: absolute;
    width: 4px;
    height: 4px;
    border: 1.5px solid currentColor;
  }
  .sel-cuadro::before { top: -4px; left: -4px; border-right: 0; border-bottom: 0; }
  .sel-cuadro::after { bottom: -4px; right: -4px; border-left: 0; border-top: 0; }
  .sel-item:hover .sel-cuadro { border-color: #6f6a62; color: #6f6a62; }
  .sel-item.activa .sel-cuadro { background: var(--black); border-color: var(--black); color: #fff; }
  /* Las marcas de esquina toman el color del texto (currentColor), y en el
     elegido el texto es blanco: las marcas desaparecian sobre el crema y el
     cuadro negro parecia mas pequeno que los otros tres, que si las llevan. */
  .sel-item.activa .sel-cuadro::before,
  .sel-item.activa .sel-cuadro::after { border-color: var(--black); }

  .sel-cuerpo { flex: 1; min-width: 0; }

  .sel-titulo {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    font-family: 'Geist', sans-serif;
    font-size: clamp(19px, 2.1vw, 26px);
    font-weight: 500;
    line-height: 1.25;
    letter-spacing: -0.5px;
    color: #aaa59d;
    transition: color 0.3s ease;
  }
  .sel-item:hover .sel-titulo { color: #6f6a62; }
  .sel-item.activa .sel-titulo { color: var(--black); }

  /* "En desarrollo", a la vista junto al titulo y no escondido en un hover: es
     lo que la tarjeta de antes ya decia al pasar por encima. */
  .sel-etiqueta {
    padding: 3px 9px;
    border-radius: 999px;
    border: 1px solid #d9d4ca;
    background: rgba(255,255,255,0.6);
    font: 500 11px/1.2 'Geist', sans-serif;
    letter-spacing: 0;
    color: #8a857d;
  }

  /* El texto se despliega con la tecnica de las filas del municipal: una fila
     de grid que pasa de 0fr a 1fr, sin medir nada. */
  .sel-texto {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.4s ease, opacity 0.3s ease;
  }
  .sel-texto > span {
    display: block;
    overflow: hidden;
    padding-top: 0;
    font-size: 16px;
    line-height: 1.6;
    color: #7d786f;
  }
  .sel-item.activa .sel-texto { grid-template-rows: 1fr; opacity: 1; }
  .sel-item.activa .sel-texto > span { padding-top: 10px; }

  /* El panel */
  .sel-panel {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    border-radius: 28px;
    min-height: 520px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 44px 32px;
  }
  .sel-fondo {
    position: absolute;
    inset: 0;
    z-index: -2;
    opacity: 0;
    transition: opacity 0.7s ease;
  }
  .sel-fondo.activa { opacity: 1; }
  .sel-fondo--0 { background: linear-gradient(135deg, #2e4eb8 0%, #4a6bdf 52%, #9bb1f4 100%); }
  .sel-fondo--1 { background: linear-gradient(135deg, #3c3a96 0%, #5b57c9 55%, #aaa7ee 100%); }
  .sel-fondo--2 { background: linear-gradient(135deg, #1b5a6a 0%, #2f8aa1 55%, #9fd3df 100%); }
  .sel-fondo--3 { background: linear-gradient(135deg, #2f6b4f 0%, #3f9a73 55%, #a9dcc2 100%); }
  /* La luz: dos manchas que se mueven muy despacio detras de la ventana. */
  .sel-panel::before {
    content: '';
    position: absolute;
    inset: -30%;
    z-index: -1;
    background:
      radial-gradient(40% 34% at 78% 14%, rgba(255,255,255,0.50), rgba(255,255,255,0) 72%),
      radial-gradient(44% 38% at 6% 92%, rgba(9,24,96,0.45), rgba(9,24,96,0) 72%);
    animation: mv-deriva 15s ease-in-out infinite alternate;
  }

  .sel-escena { display: none; width: 100%; max-width: 420px; }
  .sel-escena.activa { display: block; animation: sel-entra 0.5s ease; }
  @keyframes sel-entra {
    from { opacity: 0; transform: translateY(10px) scale(0.985); }
    to   { opacity: 1; transform: none; }
  }
  /* Las demos ya no van dentro de una tarjeta: flotan sobre el color. */
  .sel-escena .rev, .sel-escena .mun, .sel-escena .act { margin-top: 0; }
  .sel-escena .rev-hilo { background: rgba(255,255,255,0.6); }
  .sel-escena .mun, .sel-escena .act, .sel-escena .rev-ficha {
    box-shadow: 0 22px 60px rgba(8, 20, 80, 0.32);
    border-color: transparent;
  }

  @media (prefers-reduced-motion: reduce) {
    .sel-texto, .sel-cuadro, .sel-titulo, .sel-fondo { transition: none; }
    .sel-panel::before { animation: none; }
    .sel-escena.activa { animation: none; }
  }

  /* ── El mini video de "Búsqueda normativa instantánea" ── */
  /* Es el gesto de maite.ai y de sauna.ai: no una captura, sino una pieza corta
     en bucle -- una ventana de producto pequena flotando sobre un fondo de
     color, con el cursor, lo que se teclea y lo que contesta. Aqui el fondo es
     el azul de la portada con luz suave que se desplaza despacio, y la ventana
     es el chat: se pregunta, el puntero pulsa enviar y sale la respuesta con su
     articulo. Las tres consultas de BUSQUEDAS se van turnando. */
  /* Sin fondo propio: el color y la luz los pone el panel del selector
     (.sel-panel); aqui solo queda el marco donde corre la demo, y el
     puntero se coloca respecto a el. */
  .mv {
    position: relative;
  }
  @keyframes mv-deriva {
    from { transform: translate(-5%, -3%) scale(1); }
    to   { transform: translate(5%, 4%) scale(1.1); }
  }

  .mv-ui {
    position: relative;
    width: 100%;
    max-width: 390px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 16px;
    background: #fff;
    border-radius: 14px;
    box-shadow: 0 22px 60px rgba(8, 20, 80, 0.38);
  }

  /* Lo preguntado y lo contestado. Se queda al alto de la respuesta mas larga,
     MEDIDO (fijarAlto) y no escrito a ojo: las tres no miden lo mismo y esa
     diferencia seria un salto en cada vuelta. */
  .mv-mensajes { display: flex; flex-direction: column; gap: 12px; }
  .mv-usuario {
    align-self: flex-end;
    max-width: 90%;
    background: #ebe9e5;
    color: var(--black);
    border-radius: 14px;
    padding: 8px 12px;
    font-size: 12.5px;
    line-height: 1.5;
    opacity: 0;
    transform: translateY(5px);
    transition: opacity 0.3s ease, transform 0.3s ease;
  }
  .mv-usuario.entra { opacity: 1; transform: none; }

  .mv-resp {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    opacity: 0;
    transition: opacity 0.3s ease;
  }
  .mv-resp.entra { opacity: 1; }
  .mv-avatar {
    flex: none;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: #1a1a1a;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font: 700 9.5px/1 'Geist', sans-serif;
  }
  .mv-cuerpo { flex: 1; min-width: 0; font-size: 12.5px; line-height: 1.6; color: #555; }
  .mv-cuerpo strong { font-weight: 600; color: var(--black); }
  .mv-citas { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
  .mv-cita {
    padding: 3px 9px;
    border-radius: 999px;
    background: var(--accent-suave);
    border: 1px solid var(--accent-borde);
    font-size: 10.5px;
    color: var(--accent);
    white-space: nowrap;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.28s ease, transform 0.28s ease;
  }
  .mv-cita.entra { opacity: 1; transform: none; }
  .mv-pensando { display: none; gap: 4px; align-items: center; height: 20px; }
  .mv-pensando.entra { display: inline-flex; }
  .mv-pensando i {
    width: 5px; height: 5px; border-radius: 50%; background: #b3aea6;
    animation: mv-punto 1.1s ease-in-out infinite;
  }
  .mv-pensando i:nth-child(2) { animation-delay: 0.15s; }
  .mv-pensando i:nth-child(3) { animation-delay: 0.3s; }
  @keyframes mv-punto {
    0%, 60%, 100% { opacity: 0.28; transform: translateY(0); }
    30%           { opacity: 1;    transform: translateY(-3px); }
  }

  /* La caja de escribir, abajo. */
  .mv-entrada {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    padding: 8px 8px 8px 12px;
    border: 1px solid #e4e1dc;
    border-radius: 12px;
    background: #fff;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--black);
    min-height: 44px;
  }
  .mv-texto { flex: 1; min-width: 0; padding-bottom: 3px; }
  .mv-caret { color: var(--accent); animation: parpadeoCursor 1s step-end infinite; }
  .mv-caret.oculto { visibility: hidden; }
  .mv-sugerencia { display: none; color: #aaa59d; }
  #mvPregunta:empty ~ .mv-sugerencia { display: inline; }
  .mv-enviar {
    flex: none;
    width: 26px;
    height: 26px;
    border-radius: 8px;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0.3;
    transition: opacity 0.25s ease, transform 0.1s ease;
  }
  .mv-enviar svg { width: 13px; height: 13px; display: block; }
  .mv-enviar.listo { opacity: 1; }
  .mv-enviar.pulsa { transform: scale(0.86); }

  /* El puntero: un dibujo, no el raton de verdad. */
  .mv-raton {
    position: absolute;
    left: 0;
    top: 0;
    width: 30px;
    height: 30px;
    z-index: 3;
    opacity: 0;
    pointer-events: none;
    filter: drop-shadow(0 2px 5px rgba(0,0,0,0.3));
    transition: transform 900ms cubic-bezier(0.33, 0, 0.15, 1), opacity 240ms ease;
  }
  .mv-raton.visible { opacity: 1; }

  @media (prefers-reduced-motion: reduce) {
    .mv::before, .mv-caret, .mv-pensando i { animation: none; }
    .mv-usuario, .mv-resp, .mv-cita, .mv-enviar, .mv-raton { transition: none; }
    .mv-raton { display: none; }
  }

  /* ── La demo de "Revisión de planos" ── */
  /* Mismo esqueleto que la de al lado -- algo arriba, un hilo, una ficha --
     para que las dos celdas se lean como la misma familia. Cambia el tiempo:
     alli los temas se turnan, aqui hay una secuencia con principio y final
     (entra el fichero, se lee, salen las comprobaciones) que se repite. */
  .rev { margin-top: 26px; }

  .rev-fichero {
    position: relative;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 13px 11px;
    /* El mismo azul oscuro que las pastillas de la caja de al lado. Es el
       fichero que aporta el usuario, la unica pieza de la escena que pone el;
       lo que sale debajo es respuesta y se queda en blanco. */
    border: 1px solid var(--accent);
    border-radius: 10px;
    background: var(--accent);
    overflow: hidden;
  }

  .rev-doc {
    display: flex;
    color: #fff;
    flex: none;
  }
  .rev-doc svg { width: 16px; height: 16px; }

  .rev-nombre {
    font: 500 12.5px/1 'Geist', sans-serif;
    letter-spacing: -0.2px;
    color: #fff;
    /* Que se corte el nombre antes que empujar el peso fuera de la caja */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .rev-peso {
    margin-left: auto;
    flex: none;
    font-size: 11px;
    color: rgba(255, 255, 255, 0.62);
  }

  /* La barra vive pegada al borde de abajo y no como una fila aparte: asi la
     caja no cambia de alto cuando aparece. */
  .rev-barra {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    width: 100%;
    /* Sobre el azul, el carril y su relleno van en blanco: cualquier azul
       mas oscuro se perderia contra el fondo. */
    background: rgba(255, 255, 255, 0.22);
  }
  .rev-barra i {
    display: block;
    height: 100%;
    width: 0;
    /* Blanco y no verde: esta barra va DENTRO del recuadro azul del fichero,
       y ahi el verde no contrasta -- probado. La de la caja de al lado si
       puede ir verde porque corre sobre blanco. */
    background: #fff;
    transition: width 1.1s cubic-bezier(0.35, 0, 0.2, 1);
  }

  .rev-hilo {
    width: 1px;
    height: 18px;
    margin: 0 auto;
    background: var(--rejilla);
  }

  .rev-ficha {
    border: 1px solid var(--rejilla);
    border-radius: 10px;
    background: var(--white);
    padding: 12px 14px 13px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.02), 0 8px 18px rgba(0,0,0,0.03);
  }

  /* Lo que analizar_plano() saca del DXF: cuantos elementos y en que capas.
     Va antes que nada porque es el orden real -- ese resumen es lo que se
     antepone a la pregunta antes de que el agente busque. */
  .rev-leido {
    font-size: 11px;
    color: #a3a3a3;
    padding-bottom: 9px;
    border-bottom: 1px solid var(--rejilla);
    margin-bottom: 4px;
  }

  .rev-fila {
    display: flex;
    gap: 9px;
    padding: 8px 0 7px;
  }
  .rev-fila + .rev-fila { border-top: 1px solid #f2f2f4; }

  .rev-marca {
    flex: none;
    width: 15px;
    height: 15px;
    margin-top: 1px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .rev-marca svg { width: 9px; height: 9px; stroke-width: 2.6; }
  /* Rellenas, no en pastel con el icono de color. Un veredicto se ve o no se
     ve: a ese tamano --15px-- un tick verde sobre verde palido se lee como un
     gris cualquiera. Solidas, el ojo separa cumple de no cumple sin leer. */
  .rev-fila.falla .rev-marca { background: #c2410c; color: #fff; }
  .rev-fila.pasa  .rev-marca { background: var(--verde); color: #fff; }

  .rev-que {
    font-size: 12.5px;
    line-height: 1.35;
    color: var(--black);
    font-weight: 500;
  }
  .rev-cota {
    font-size: 11.5px;
    line-height: 1.45;
    color: #8d8d8d;
    margin-top: 2px;
  }
  /* La cifra del plano en naranja solo cuando incumple: es lo unico que hay
     que mirar de la fila. El naranja de esta web significa error, no acento. */
  .rev-fila.falla .rev-cota b { color: #c2410c; font-weight: 600; }
  .rev-fila.pasa  .rev-cota b { color: var(--black); font-weight: 600; }
  .rev-art {
    display: inline-block;
    margin-top: 5px;
    font-size: 10.5px;
    color: var(--accent);
    background: var(--accent-suave);
    border: 1px solid var(--accent-borde);
    border-radius: 5px;
    padding: 2px 6px;
  }

  /* Todo entra desde abajo, una cosa detras de otra */
  .rev-fichero, .rev-leido, .rev-fila {
    opacity: 0;
    transform: translateY(5px);
    transition: opacity 0.35s ease, transform 0.35s ease;
  }
  .rev-fichero.entra, .rev-leido.entra, .rev-fila.entra {
    opacity: 1;
    transform: none;
  }

  @media (prefers-reduced-motion: reduce) {
    .rev-fichero, .rev-leido, .rev-fila, .rev-barra i { transition: none; }
  }

  /* ── La demo de "Normativa municipal" ── */
  /* Aqui no hay secuencia ni carrusel: hay un indice. Tres municipios en una
     pila y el de turno abierto -- que es la forma de la caja, un sitio donde
     estan todos y entras en uno. */
  .mun {
    margin-top: 26px;
    border: 1px solid var(--rejilla);
    border-radius: 10px;
    background: var(--white);
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0,0,0,0.02), 0 8px 18px rgba(0,0,0,0.03);
  }

  .mun-fila + .mun-fila { border-top: 1px solid var(--rejilla); }

  .mun-cab {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 11px 14px;
  }

  .mun-punto {
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--rejilla);
    transition: background-color 0.4s ease, box-shadow 0.4s ease;
  }
  .mun-fila.abierta .mun-punto {
    background: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-suave);
  }

  .mun-nombre {
    font: 500 12.5px/1 'Geist', sans-serif;
    letter-spacing: -0.2px;
    color: #9a9a9a;
    transition: color 0.4s ease;
  }
  .mun-fila.abierta .mun-nombre { color: var(--black); }

  .mun-docs {
    margin-left: auto;
    font-size: 10.5px;
    color: #b4b4b4;
  }

  /* El acordeon con grid y no con max-height: 0fr a 1fr anima el alto REAL,
     asi que no hay que adivinar un maximo. Si un navegador no lo soporta se
     abre de golpe, que es un final aceptable. */
  .mun-cuerpo {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.45s cubic-bezier(0.4, 0, 0.2, 1);
  }
  .mun-fila.abierta .mun-cuerpo { grid-template-rows: 1fr; }
  .mun-cuerpo > div { overflow: hidden; }

  .mun-dentro { padding: 0 14px 13px; }

  .mun-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-bottom: 10px;
  }
  .mun-chip {
    font-size: 10px;
    letter-spacing: 0.2px;
    color: #8d8d8d;
    background: #f7f7f8;
    border: 1px solid var(--rejilla);
    border-radius: 4px;
    padding: 2px 6px;
  }

  .mun-que {
    display: block;
    font-size: 11.5px;
    color: #a3a3a3;
    margin-bottom: 3px;
  }
  .mun-cifra {
    display: block;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.3px;
    color: var(--black);
  }
  .mun-cifra i { font-style: normal; font-weight: 400; font-size: 11.5px; color: #8d8d8d; }
  .mun-art {
    display: inline-block;
    margin-top: 7px;
    font-size: 10.5px;
    color: var(--accent);
    background: var(--accent-suave);
    border: 1px solid var(--accent-borde);
    border-radius: 5px;
    padding: 2px 6px;
  }

  @media (prefers-reduced-motion: reduce) {
    .mun-cuerpo, .mun-punto, .mun-nombre { transition: none; }
  }

  /* ── La demo de "Siempre actualizado" ── */
  /* Lo que se enseña es la comprobación que existe de verdad:
     manifiesto_fuentes.py --comprobar vuelve a calcular el sha256 de los 62
     documentos y lo contrasta con el que se guardó al indexarlos. Un fichero
     cambiado significa que el corpus en git salió de OTRO texto. */
  .act {
    margin-top: 26px;
    border: 1px solid var(--rejilla);
    border-radius: 10px;
    background: var(--white);
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0,0,0,0.02), 0 8px 18px rgba(0,0,0,0.03);
  }

  .act-cab {
    position: relative;
    padding: 11px 14px 12px;
    border-bottom: 1px solid var(--rejilla);
  }
  .act-titulo {
    font: 500 12px/1 'Geist', sans-serif;
    letter-spacing: -0.15px;
    color: #8d8d8d;
  }
  /* Pegada al borde de abajo de la cabecera, como en la caja de planos: asi
     no añade una fila ni cambia el alto al aparecer. */
  .act-barra {
    position: absolute;
    left: 0;
    bottom: -1px;
    height: 2px;
    width: 100%;
    background: transparent;
  }
  .act-barra i {
    display: block;
    height: 100%;
    width: 0;
    /* Verde como la de planos: las dos barras son una comprobacion
       avanzando, y acaban en ticks verdes. */
    background: var(--verde);
    transition: width 1.9s linear;
  }

  .act-fila {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 8px 14px;
  }
  .act-fila + .act-fila { border-top: 1px solid #f2f2f4; }

  .act-marca {
    flex: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #f2f2f4;
    color: transparent;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.3s ease, color 0.3s ease;
  }
  .act-marca svg { width: 8px; height: 8px; stroke-width: 3; }
  /* Relleno y con el tick en blanco, igual que las marcas de la caja de
     planos. Eran dos estilos de tick conviviendo en la misma fila. */
  .act-fila.hecha .act-marca { background: var(--verde); color: #fff; }

  .act-nombre {
    font-size: 12px;
    color: #a9a9a9;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color 0.3s ease;
  }
  .act-fila.hecha .act-nombre { color: var(--black); }

  /* La huella en monoespaciada: es lo que la hace creible como huella y no
     como adorno. Son los doce primeros caracteres del sha256 de verdad. */
  .act-huella {
    margin-left: auto;
    flex: none;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    letter-spacing: -0.2px;
    color: #c4c4c4;
    transition: color 0.3s ease;
  }
  .act-fila.hecha .act-huella { color: #9a9a9a; }

  .act-pie {
    padding: 10px 14px 11px;
    border-top: 1px solid var(--rejilla);
    background: #fcfcfd;
    font-size: 11.5px;
    color: #8d8d8d;
    opacity: 0;
    transition: opacity 0.35s ease;
  }
  .act-pie.entra { opacity: 1; }
  .act-pie b { color: var(--black); font-weight: 600; }

  @media (prefers-reduced-motion: reduce) {
    .act-barra i, .act-marca, .act-nombre, .act-huella, .act-pie { transition: none; }
  }

  /* ── WHO ── */
  /* Transparente y sin la retícula CSS de 60px, igual que .hero:
     en esta banda la única trama es el lienzo de 120px. */
  .who {
    padding: 100px 48px;
    background-color: transparent;
    background-image: none;
    position: relative;
  }

  /* Calcado de .hero-band: el fondo y el desvanecido viven en la banda,
     no en la sección, para que el lienzo quede por detrás del contenido. */
  .who-band {
    position: relative;
    z-index: 0;
    background-color: #ffffff;
  }

  /* La banda es más alta que la portada, así que el desvanecido inferior
     empieza mucho más abajo: si no, la retícula moría a media sección. */
  .who-band::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
      radial-gradient(ellipse at center, transparent 0%, transparent 74%, #ffffff 100%),
      linear-gradient(to bottom, #ffffff 0%, transparent 20%, transparent 72%, #ffffff 97%),
      linear-gradient(to left, #ffffff 0%, transparent 15%, transparent 85%, #ffffff 100%);
  }

  .who-inner {
    max-width: 1100px;
    margin: 0 auto;
  }

  /* Tres celdas planas, como las de la referencia (Strucl): una sola caja con
     linea fina y marcas de esquina, una linea fina entre celda y celda, la
     primera con un tinte azul flojo, y en cada una la etiqueta del perfil, un
     titular, la linea que lo explica, el enlace y debajo una imagen grande de
     esquinas redondeadas. La imagen es una foto (--foto, que se pone en el
     style de cada celda) y, sin foto, el panel de color con la trama de la
     portada. Encima, apoyado abajo, el ejemplo de verdad en una ventana. */
  .who-inner { max-width: 1240px; }

  /* Las tres celdas son cajas sueltas, como las tarjetas de «Precios»: mismo
     borde (var(--border)), mismas esquinas (12 px) y 16 px de hueco entre una
     y otra. Sin la caja grande ni las marcas de esquina de antes. */
  .who-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    margin-top: 48px;
  }

  .who-tarjeta {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    min-width: 0;
    padding: 32px 24px 24px;
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: 12px;
    transition: border-color 0.15s;
  }
  .who-tarjeta:hover { border-color: var(--accent-borde); }

  .who-tarjeta h3 {
    margin-top: 0;
    font: 500 28px/1.2 'Geist', sans-serif;
    letter-spacing: -0.8px;
    color: var(--black);
  }

  .who-tarjeta > p {
    margin-top: 12px;
    font-size: 15px;
    color: #7d7871;
    line-height: 1.6;
  }

  .who-enlace {
    margin: 16px 0 28px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    font-weight: 500;
    color: var(--black);
    cursor: pointer;
  }
  .who-enlace svg { width: 12px; height: 12px; transition: transform 0.15s ease; }
  .who-enlace:hover svg { transform: translateX(3px); }

  /* La imagen: grande, de esquinas redondeadas, pegada abajo para que las tres
     queden a la misma altura aunque el texto de arriba cambie de lineas. */
  .who-lienzo {
    /* Pegado abajo: con auto, los tres paneles quedan a la misma altura aunque
       el texto de arriba ocupe dos lineas en una columna y tres en otra. */
    margin-top: auto;
    position: relative;
    width: 100%;
    min-height: 440px;
    display: flex;
    align-items: flex-end;
    padding: 16px;
    border-radius: 10px;
    overflow: hidden;
    background-color: var(--panel, var(--accent));
    background-image:
      repeating-linear-gradient(45deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 68px),
      repeating-linear-gradient(-45deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 68px);
  }
  /* La foto (--foto, en el style de cada celda) ocupa la parte de arriba del
     panel, en apaisado para que quepan las cuatro personas, y la ventana del
     ejemplo se apoya debajo, solapandola un poco. */
  .who-lienzo::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 270px;
    background: var(--foto, none) var(--pos, center 15%) / cover no-repeat;
  }
  .who-ventana { position: relative; z-index: 1; }

  /* El ejemplo, en una ventana a escala (la misma de la demo de arriba). */
  .who-ventana {
    width: 100%;
    background: #f8f7f5;
    border-radius: 12px;
    overflow: hidden;
    text-align: left;
    box-shadow: 0 10px 30px rgba(6,14,50,0.28);
  }
  .who-barra {
    display: flex;
    align-items: center;
    gap: 5px;
    height: 24px;
    padding: 0 11px;
    background: #fff;
    border-bottom: 1px solid #ebe9e4;
  }
  .who-barra i { width: 7px; height: 7px; border-radius: 50%; background: #dcdad5; }

  .who-ejemplo {
    display: flex;
    flex-direction: column;
    gap: 9px;
    padding: 12px;
  }

  .who-consulta {
    align-self: flex-end;
    max-width: 92%;
    background: var(--accent-suave);
    border-radius: 12px 12px 3px 12px;
    padding: 7px 10px;
    font-size: 11px;
    color: var(--text);
    line-height: 1.45;
  }

  .who-respuesta { display: block; }
  .who-avatar { display: none; }

  .who-texto-ia { font-size: 11px; color: #444; line-height: 1.55; }
  .who-texto-ia strong { font-weight: 600; color: var(--black); }

  .who-citas { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; padding-top: 8px; border-top: 1px solid #e8e4de; }
  .who-cita {
    padding: 2px 8px;
    background: #fff;
    border: 1px solid #e4e1dc;
    border-radius: 20px;
    font-size: 9.5px;
    color: #5f5b55;
    white-space: nowrap;
  }

  /* ── PRICING ── */
  .pricing-wrap {
    background-color: #ece9e4;
    position: relative;
    z-index: 0;
  }

  .pricing {
    padding: 100px 48px;
    max-width: 1100px;
    margin: 0 auto;
    position: relative;
  }

  /* Cuatro columnas, no dos. La estructura es la de la referencia: la MISMA
     lista de prestaciones en las cuatro tarjetas, con las que no entran en
     gris. Asi la comparacion se hace de un vistazo por la linea, en vez de
     contando items de una columna a otra. */
  .pricing-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-top: 56px;
    text-align: left;
  }

  .pricing-card {
    display: flex;
    flex-direction: column;
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 24px 22px;
    /* Sin esto, la tarjeta no baja del ancho de su linea mas larga */
    min-width: 0;
    transition: border-color 0.15s, box-shadow 0.15s;
  }
  .pricing-card:hover {
    border-color: var(--accent-borde);
    box-shadow: 0 6px 20px rgba(0,0,0,0.05);
  }

  /* La destacada ya no es negra entera: solo se rodea. Con cuatro tarjetas,
     una en negro se comia a las otras tres. */
  .pricing-card.featured {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
  }

  .plan-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    align-self: flex-start;
    padding: 5px 10px;
    border: 1px solid var(--border);
    border-radius: 7px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text);
  }
  .plan-chip svg { width: 13px; height: 13px; color: #9a9a9a; }
  .pricing-card.featured .plan-chip {
    border-color: var(--accent-borde);
    background: var(--accent-suave);
    color: var(--accent);
  }
  .pricing-card.featured .plan-chip svg { color: var(--accent); }

  .plan-para {
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.3px;
    color: var(--black);
    margin-top: 16px;
  }
  .plan-desde {
    font-size: 12px;
    color: #a3a3a3;
    margin-top: 6px;
  }

  .price {
    font-family: 'Geist', sans-serif;
    font-size: 38px;
    font-weight: 500;
    letter-spacing: -1.6px;
    color: var(--black);
    margin-top: 4px;
    white-space: nowrap;
  }
  .price small {
    font-size: 15px;
    font-weight: 400;
    letter-spacing: -0.3px;
    color: #8d8d8d;
  }
  /* "A medida" no cabe a 38px en una columna de cuatro */
  .price.texto { font-size: 30px; letter-spacing: -1px; }

  .price-sub {
    font-size: 11.5px;
    color: #b4b4b4;
    margin-top: 6px;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--rejilla);
  }

  .pricing-features {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 11px;
    margin-top: 18px;
    /* Empuja el boton al fondo: con listas del mismo largo en las cuatro, los
       cuatro botones quedan a la misma altura. */
    margin-bottom: 22px;
  }

  .pricing-features li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--text);
  }

  /* La marca es un cuadro relleno, como en la referencia: se distingue de un
     vistazo la que entra de la que no, sin tener que leer el gris. */
  .pricing-features li::before {
    content: '';
    flex: none;
    width: 15px;
    height: 15px;
    margin-top: 1px;
    border-radius: 4px;
    background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6'/%3E%3C/svg%3E") center / 9px 9px no-repeat;
  }

  .pricing-features li.no { color: #bdbdbd; }
  .pricing-features li.no::before {
    background-color: #f0f0f2;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23d4d4d8' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6'/%3E%3C/svg%3E");
  }

  .pricing-btn {
    width: 100%;
    margin-top: auto;
    padding: 11px;
    border-radius: 8px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--black);
    background: var(--black);
    color: #fff;
    transition: background-color 0.12s, border-color 0.12s;
  }
  .pricing-btn:hover { background: #333; border-color: #333; }

  .pricing-card.featured .pricing-btn {
    background: var(--accent);
    border-color: var(--accent);
  }
  .pricing-card.featured .pricing-btn:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
  }

  /* Entre el movil y el escritorio, cuatro columnas dejan cada tarjeta en
     240px y "Planeamiento de 38 municipios, 11 con ordenanzas propias" cae en
     cuatro lineas. De 1.040 para abajo, dos y dos. */
  @media (max-width: 1040px) {
    .pricing-grid { grid-template-columns: repeat(2, 1fr); gap: 18px; }
  }

  /* ── CTA ── */
  .cta-section {
    /* Margen vertical: deja ver el blanco de la página por arriba y por abajo,
       para que la foto no toque ni la sección anterior ni el footer. A 10 px la
       franja se leia como una junta mal cerrada; a 44 se lee como aire. */
    margin: 44px 0;
    padding: 100px 48px;
    background: none;
    text-align: center;
    position: relative;
    z-index: 0;
  }

  /* La foto llega de lado a lado (100vw centrado detras del texto), igual que
     las bandas crema; el texto se queda en sus margenes. */
  .cta-section::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 100vw;
    z-index: -1;
    background: linear-gradient(rgba(20,16,12,0.35), rgba(20,16,12,0.45)), url('/assets/abstract-city-building-shadows.jpg') center/cover no-repeat;
  }

  .cta-section h2 {
    font-family: 'Geist', sans-serif;
    font-size: 44px;
    font-weight: 500;
    letter-spacing: -1px;
    color: #fff;
    margin-bottom: 16px;
    line-height: 1.15;
  }

  .cta-section p {
    font-size: 16px;
    color: #d8d4ce;
    margin-bottom: 36px;
  }

  .cta-btns {
    display: flex;
    gap: 10px;
    justify-content: center;
  }

  .btn-cta-white {
    padding: 12px 28px;
    border: none;
    border-radius: 8px;
    background: var(--accent);
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    color: #fff;
    cursor: pointer;
    text-decoration: none;
    transition: all 0.12s;
  }

  .btn-cta-white:hover { background: var(--accent-hover); }

  .btn-cta-outline {
    padding: 12px 28px;
    border: 1px solid #333;
    border-radius: 8px;
    background: transparent;
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    color: #888;
    cursor: pointer;
    text-decoration: none;
    transition: all 0.12s;
  }

  .btn-cta-outline:hover { border-color: #555; color: #ccc; }

  /* ── FOOTER ── */
  footer {
    background: #1a1a1a;
    position: relative;
    overflow: hidden;
    /* De lado a lado: la sombra no la recorta el overflow del propio footer, y el
       clip-path deja que se vea hacia los lados pero no hacia arriba ni abajo. */
    box-shadow: 0 0 0 100vmax #1a1a1a;
    clip-path: inset(0 -100vmax);
  }

  .footer-inner {
    max-width: 1240px;
    margin: 0 auto;
    padding: 0 60px;
    position: relative;
    z-index: 2;
  }

  /* -- Arriba: «Reservar una llamada» a la izquierda y las columnas del menu a
        la derecha, como la referencia (Wordsmith). -- */
  .footer-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 64px;
    padding: 64px 0 48px;
    border-bottom: 1px solid #2a2a2a;
  }

  .footer-llamada h4 {
    font-family: 'Geist', sans-serif;
    font-size: 34px;
    font-weight: 500;
    line-height: 1.15;
    letter-spacing: -0.04em;
    color: #fff;
    max-width: 440px;
    margin-bottom: 28px;
  }

  .footer-llamada-form {
    display: flex;
    gap: 8px;
    padding: 6px;
    max-width: 520px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid #333;
  }

  .footer-llamada-form input {
    flex: 1;
    min-width: 0;
    padding: 11px 12px;
    border: 0;
    background: transparent;
    color: #fff;
    font-family: inherit;
    font-size: 14px;
    outline: none;
  }

  .footer-llamada-form input::placeholder { color: #8a8a8a; }

  .footer-llamada-form button {
    padding: 11px 18px;
    border: none;
    border-radius: 7px;
    background: #fff;
    color: #1a1a1a;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.12s;
  }

  .footer-llamada-form button:hover { background: #ddd; }

  /* -- Columns block -- */
  /* Las columnas del menu, a la derecha. */
  .footer-cols {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 32px 28px;
  }

  .footer-col h5 {
    font-family: 'Geist', sans-serif;
    font-size: 14px;
    font-weight: 600;
    color: #fff;
    margin-bottom: 18px;
  }

  .footer-col a {
    display: block;
    font-size: 13px;
    color: #777;
    margin-bottom: 13px;
    line-height: 1.4;
    transition: color 0.12s;
  }

  .footer-col a:hover { color: #ccc; }

  /* Las paginas de normativa aun no estan publicadas. Estos <a> se quedan sin
     href a proposito: asi no navegan, no entran en el tabulador y un lector de
     pantalla no los canta como enlaces. Solo hay que apagarles el cursor y el
     hover, que prometerian un clic que no pasa nada. */
  .footer-col a:not([href]),
  .footer-links a:not([href]) { cursor: default; }
  .footer-col a:not([href]):hover { color: #777; }
  .footer-links a:not([href]):hover { color: #666; }

  .footer-bottom-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 24px 0 0;
  }

  .footer-links {
    display: flex;
    gap: 24px;
  }

  .footer-links a {
    font-size: 12.5px;
    color: #555;
    transition: color 0.12s;
  }

  .footer-links a:hover { color: #888; }

  .footer-copy {
    font-size: 12px;
    color: #444;
  }

  .footer-logo-big {
    position: relative;
    z-index: 2;
    width: 100%;
    padding-top: 20%;
    overflow: hidden;
  }

  .footer-logo-big svg {
    width: 100%;
    height: auto;
    display: block;
  }

  @media (max-width: 900px) {
    .footer-top { grid-template-columns: 1fr; gap: 40px; }
    .footer-cols { grid-template-columns: repeat(2, 1fr); }
  }

  /* ── CORNER MARKERS ── */
  /* Body has side padding so content doesn't touch edges */
  #landingView {
    max-width: 1320px;
    margin: 0 auto;
    padding: 0 32px;
  }

  .block-wrap {
    position: relative;
  }

  .block-wrap::before,
  .block-wrap::after {
    content: none;
    position: absolute;
    width: 20px;
    height: 20px;
    border-color: #999691;
    border-style: solid;
    pointer-events: none;
    z-index: 100;
  }

  .block-wrap::before { top: 0; left: 0; border-width: 2.5px 0 0 2.5px; }
  .block-wrap::after  { top: 0; right: 0; border-width: 2.5px 2.5px 0 0; }

  .corners-bottom {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 100;
  }

  .corners-bottom::before,
  .corners-bottom::after {
    content: none;
    position: absolute;
    width: 20px;
    height: 20px;
    border-color: #999691;
    border-style: solid;
    pointer-events: none;
  }

  .corners-bottom::before { bottom: 0; left: 0; border-width: 0 0 2.5px 2.5px; }
  .corners-bottom::after  { bottom: 0; right: 0; border-width: 0 2.5px 2.5px 0; }

  .block-wrap.dark::before,
  .block-wrap.dark::after,
  .corners-bottom.dark::before,
  .corners-bottom.dark::after {
    border-color: rgba(255,255,255,0.3);
  }

  .cross-overlay { display: none; }

  #landingView nav {
    /* Fijada arriba. Estuvo un tiempo en relative --se iba con la pagina--
       pero una barra que se va obliga a subir del todo para cambiar de
       seccion, y la pagina es larga. */
    position: sticky;
    top: 0;
    z-index: 100;
    background: #ffffff;
    /* 76 y no 60: 16 px mas de aire POR ENCIMA del logo y los enlaces, que
       pegaban al borde de la ventana. Va en padding-top y no en subir el
       alto del contenido: los elementos siguen centrados en sus 60 px de
       siempre y todo el hueco nuevo queda arriba, no abajo. En movil sigue
       en 60 (mas abajo, en su bloque): el panel del menu abierto se coloca
       con 61 px de padding para quedar justo debajo. */
    height: 76px;
    padding: 16px 0 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  /* Sin linea debajo de la barra: era una raya de 100vw que separaba la barra del
     resto, y se quita. En su lugar la barra es BLANCA de lado a lado: vive dentro
     de #landingView (1320 como mucho, con su padding), asi que sin esto las bandas
     de color, que ahora llegan a los bordes de la pantalla, pasarian por los lados
     de la barra al hacer scroll. 100vw centrado en la barra, detras de su contenido;
     el body recorta el sobrante (y la barra de scroll clasica) con overflow-x: clip. */
  #landingView nav::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 100vw;
    background: #ffffff;
    z-index: -1;
  }

  .nav-logo { display: flex; align-items: center; }
  .nav-links { display: flex; align-items: center; gap: 32px; position: absolute; left: 50%; transform: translateX(-50%); }
  .nav-links a { font-size: 13.5px; font-weight: 500; color: #000; }
  .nav-links a:hover { color: #000; }

  .nav-actions { display: flex; align-items: center; gap: 10px; }

  /* Sin sesion se ve "Entrar / Pruébalo gratis"; con sesion, la cuenta. El
     estado por defecto del marcado es SIN sesion: si el JS no llega a correr,
     lo que queda es la puerta de entrada, que es lo util. */
  .nav-actions [data-con-sesion] { display: none; }
  body.con-sesion .nav-actions [data-sin-sesion] { display: none; }
  body.con-sesion .nav-actions [data-con-sesion] { display: inline-flex; }
  .nav-panel-content { display: contents; }
  .nav-hamburger { display: none; }
  #navPanelBackdrop { display: none; }


  /* ══════════════════════════════════════════════
     APP VIEW (chat) — scoped under #appView
     ══════════════════════════════════════════════ */

  * { box-sizing: border-box; margin: 0; padding: 0; }
  #appView {
    font-family: 'Inter', sans-serif;
    background: var(--fondo-chat);
    height: 100vh;
    display: flex;
    overflow: hidden;
    font-size: 13.5px;
    color: #1a1a1a;
  }

  /* ── La espera («Pensando…»): la usan la ventana de la portada y el chat ── */
  /* El brillo que recorre la palabra mientras se espera. Antes eran tres
     bolas rebotando; esto dice lo mismo sin moverse de sitio, y a los ocho
     segundos que puede tardar una respuesta cansa mucho menos la vista.
     Va en el fondo recortado al texto: el degradado se mueve y la palabra
     hace de mascara. */
  /* La chispa y la palabra van en la misma fila. La fila existe para poder
     sacar la chispa FUERA de .ai-thinking: ahi dentro heredaria el
     color:transparent del recorte al texto y no se veria. */
  .ai-espera {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 8px;
  }

  /* La marca que acompaña a la palabra. Nace como un punto, crece hasta ser
     bola, se convierte en cuadrilatero y vuelve, y se encoge otra vez hasta
     desaparecer -- en bucle. Es un cuadrado de 11px al que se le anima el
     border-radius -- de 50% (circulo) a 2px (cuadrado)-- y no un SVG: la
     forma ES la caja, asi que el navegador interpola entre las dos sin que
     haya que dibujar ningun camino.
     El giro va con la forma y no aparte. Un circulo girando no se ve girar,
     y un cuadrado a 90 grados es identico a uno a 0; a 45 es un rombo, que
     es lo unico que distingue de verdad las dos formas. Por eso el punto
     medio del ciclo lleva las dos cosas a la vez: cuadrado Y rombo. */
  .ai-chispa {
    width: 11px;
    height: 11px;
    flex: none;
    background: var(--accent);
    border-radius: 50%;
    /* Una sola animacion para todo el recorrido, no dos encadenadas: nacer,
       morfar y encogerse son el mismo bucle, asi que tienen que vivir en el
       mismo sitio. Con dos, la segunda tendria que arrancar justo donde acaba
       la primera y el empalme se rompe en cuanto alguien cambia una duracion. */
    animation: ciclo 4.5s infinite;
  }

  /* El recorrido entero: punto diminuto -> bola -> cuadrado -> bola -> punto,
     y vuelta a empezar.
     Cada tramo lleva SU propio easing, declarado en el fotograma donde
     empieza. Hacen falta distintos: crecer se pasa de largo y vuelve (el 1.4),
     el morfo va suave por los dos lados, y encogerse acelera al final -- que
     es lo que hace que no parezca que se apaga, sino que se va.
     Y el giro es de media vuelta: al acabar en 90 grados, el cuadrado del
     ciclo siguiente sale del mismo sitio. La vuelta a 0 del bucle cae con la
     bola ya invisible, asi que no se ve saltar. */
  @keyframes ciclo {
    0% {
      transform: scale(0.1) rotate(0deg);
      border-radius: 50%; opacity: 0;
      animation-timing-function: cubic-bezier(0.34, 1.4, 0.64, 1);
    }
    22% {
      transform: scale(1) rotate(0deg);
      border-radius: 50%; opacity: 1;
      animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
    }
    55% {
      transform: scale(0.92) rotate(45deg);
      border-radius: 2px; opacity: 1;
      animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
    }
    80% {
      transform: scale(1) rotate(90deg);
      border-radius: 50%; opacity: 1;
      animation-timing-function: cubic-bezier(0.55, 0, 0.9, 0.6);
    }
    100% {
      transform: scale(0.1) rotate(90deg);
      border-radius: 50%; opacity: 0;
    }
  }

  .ai-thinking {
    display: inline-block;
    font-size: 13px;
    background: linear-gradient(90deg, #c4c4c4 0%, #c4c4c4 35%,
                                       #4a4a4a 50%,
                                       #c4c4c4 65%, #c4c4c4 100%);
    background-size: 220% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: brillo 2.4s linear infinite;
    /* Para el relevo de palabra: la que se va cae y se apaga, la que entra
       sube. Ver .releva. */
    transition: opacity 0.18s ease, transform 0.18s ease;
  }

  /* Los 180 ms del relevo son una treceava parte de lo que la palabra pasa
     quieta: se ve el cambio y no parece un parpadeo. */
  .ai-thinking.releva {
    opacity: 0;
    transform: translateY(3px);
  }

  /* De derecha a izquierda del degradado, o sea que el brillo viaja hacia la
     derecha -- el sentido en que se lee. */
  @keyframes brillo {
    from { background-position: 110% 0; }
    to   { background-position: -10% 0; }
  }

  /* Quien tenga el movimiento reducido ve la palabra quieta y en gris. */
  @media (prefers-reduced-motion: reduce) {
    .ai-thinking {
      animation: none;
      background: none;
      color: #999;
      transition: none;
    }
    .ai-thinking.releva { opacity: 1; transform: none; }
    .ai-chispa { animation: none; }   /* se queda redonda, y sin nacer */
  }

  /* En la portada estas etiquetas siempre se han visto ocultas: la regla del sidebar (.section-label, abajo en
     app.css) pisaba a la de la portada. Se deja como estaba. */
  #landingView .section-label { display: none; }

  /* ── Las normas bajo la respuesta y los numeros de cita: los usan la ventana de la portada y el chat ── */
  /* AI action buttons */
  .sources {
    display: flex;
    gap: 5px;
    flex-wrap: wrap;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid #e8e4de;
  }

  /* La chapa que se puede abrir se distingue de la que no: las del historial
     no traen texto y se quedan como estaban. */
  .chip-abre {
    font-family: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    transition: all 0.12s;
  }
  .chip-abre:hover { border-color: var(--accent); color: var(--accent); }
  .chip-abre svg { width: 10px; height: 10px; opacity: 0.5; }
  .chip-abre:hover svg { opacity: 1; }

  /* El numero de una cita dentro del texto de la respuesta: pequeño, volado, y abre el articulo. */
  .cita-n {
    font: 600 10px/1 'Geist', sans-serif;
    min-width: 16px; height: 16px; padding: 0 4px;
    margin: 0 1px 0 2px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--cream);
    color: var(--accent);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    position: relative; top: -0.4em;
    transition: all 0.12s;
  }
  .cita-n:hover, .cita-n:focus-visible { background: var(--accent); border-color: var(--accent); color: #fff; outline: none; }
  .chip-n { font-weight: 600; color: var(--accent); }

  .chip {
    padding: 3px 9px;
    background: #fff;
    border: 1px solid #e0dcd6;
    border-radius: 20px;
    font-size: 11px;
    color: #999;
    font-weight: 500;
  }
