/* Experiencia pública Urbani · Estimación de capacidad — dirección «Reposo y Luz»
   Implementa docs/product/PHASE2_REPOSO_Y_LUZ.md (aceptada 2026-08-23/24).
   Separada por completo del panel ejecutivo (app/cmf_app/static/index.html).

   Principio: calma en reposo, vida en la interacción. Todo movimiento responde a una acción
   del usuario o a un cambio de estado real; nada se anima por haber aparecido. */

/* ============================================================
   TIPOGRAFÍA — Montserrat self-hosted, mismo origen
   ------------------------------------------------------------
   SIL Open Font License 1.1 · Copyright 2024 The Montserrat.Git Project Authors
   https://github.com/JulietaUla/Montserrat  ·  licencia íntegra en fonts/OFL.txt
   Procedencia y checksum del binario: fonts/PROVENANCE.md

   Se sirve desde el propio origen bajo `font-src 'self'` (TP-CMF-F2-008C:R1). La experiencia
   pública NO hace ninguna petición de fuente a Google Fonts ni a ningún otro host, y no se
   autorizó `data:` ni ningún origen externo.

   El binario es una fuente variable. Se declaran TRES caras con un valor ÚNICO de `font-weight`
   cada una y el mismo `src`: un valor único fija el eje `wght` a esa instancia, de modo que el
   documento sólo conoce 400, 600 y 700 — el peso 650 que la dirección prohíbe no es
   instanciable. Al compartir URL, el navegador lo descarga una sola vez.
   ============================================================ */
@font-face{
  font-family:'Montserrat';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('/public/fonts/montserrat-latin.woff2') format('woff2');
}
@font-face{
  font-family:'Montserrat';
  font-style:normal;
  font-weight:600;
  font-display:swap;
  src:url('/public/fonts/montserrat-latin.woff2') format('woff2');
}
@font-face{
  font-family:'Montserrat';
  font-style:normal;
  font-weight:700;
  font-display:swap;
  src:url('/public/fonts/montserrat-latin.woff2') format('woff2');
}

/* Ángulo animable de la luz de borde. Registrado para poder interpolarse; donde
   `@property` no exista, la luz simplemente no aparece y la ficha conserva lift y sombra. */
@property --angle{
  syntax:'<angle>';
  initial-value:0deg;
  inherits:false;
}

/* ============================================================
   TOKENS
   ============================================================ */
:root{
  /* superficie — neutrales sesgados al verde, no al azul */
  --bg:#FAFBFA;
  --surface:#FFFFFF;
  --surface-2:#F4F6F4;

  /* tinta */
  --ink:#0F1211;
  --ink-2:#474D4A;
  /* #737A76 medía 4,40 / 4,24 / 4,05 sobre --surface / --bg / --surface-2: por debajo del
     mínimo AA de 4,5:1 para texto normal. Y este token gobierna justo el texto del que depende
     la confianza del producto — la nota de no almacenamiento del PDF, las advertencias de que
     esto no es una aprobación, la nota legal del pie y la comuna de cada ficha. Oscurecido lo
     mínimo para despejar 4,5:1 en las tres superficies (5,01 / 4,83 / 4,61) sin dejar de leerse
     como texto secundario. */
  --mute:#6A716C;
  --faint:#9AA19D;

  /* estructura */
  --line:#E3E7E4;
  --line-2:#EDF0EE;
  --line-strong:#CFD6D1;

  /* acento — jurisdicción escrita en §2.3 de la dirección.
     NUNCA sobre una cifra financiera: verde sobre una cifra se lee como «aprobado». */
  --brand-green:#70C11A;
  --brand-green-dark:#5EB50D;
  --brand-green-deep:#4C930A;
  --on-green:#0C1400;
  --green-wash:rgba(112,193,26,.10);
  --green-edge:rgba(112,193,26,.55);
  --green-halo:rgba(112,193,26,.09);

  /* estado */
  --warn:#8A5B00; --warn-bg:#FDF7E9; --warn-line:#F0E0B4;
  --stop:#8C2626; --stop-bg:#FDF2F2; --stop-line:#F2CFCF;

  /* control deshabilitado */
  --disabled-bg:#E4E8E5;
  --disabled-ink:#98A09B;

  /* elevación — tintada a la tinta, nunca negro puro */
  --e-1:0 1px 2px rgba(15,18,17,.04), 0 2px 8px rgba(15,18,17,.04);
  --e-2:0 2px 4px rgba(15,18,17,.05), 0 8px 24px rgba(15,18,17,.08);
  --e-3:0 12px 40px rgba(15,18,17,.12), 0 2px 6px rgba(15,18,17,.06);

  /* radios */
  --r-card:14px;
  --r-control:10px;
  --r-pill:999px;

  /* espaciado — base 4 */
  --s-1:4px;  --s-2:8px;   --s-3:12px;  --s-4:16px;  --s-5:20px;
  --s-6:24px; --s-8:32px;  --s-10:40px; --s-12:48px;
  --s-16:64px; --s-20:80px; --s-24:96px;

  /* movimiento — cuatro tiers, tres curvas */
  --t-micro:160ms;
  --t-comp:300ms;
  --t-scene:560ms;
  --e-out:cubic-bezier(.22,1,.36,1);
  --e-inout:cubic-bezier(.65,0,.35,1);
  --e-snap:cubic-bezier(.34,1.2,.64,1);

  --font-sans:'Montserrat', ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;

  /* escenario ilustrado — superficie de reposo para la ejecutiva */
  --stage:#EDF0EC;

  /* Neutros de la ARQUITECTURA de la escena de búsqueda. Los tonos del personaje —piel, pelo,
     vestuario, camisa— dejaron de vivir aquí: desde TP-CMF-F2-008D:R2 son parte de la
     ilustración final entregada, no de la hoja. */
  --adv-soft:#C6CFC8;
  --adv-soft-2:#DEE4DE;
}

/* ============================================================
   BASE
   ============================================================ */
*{box-sizing:border-box}
html,body{margin:0}
/* Columna de página: el cierre de marca se apoya en el fondo del viewport aunque la pantalla
   sea corta, en vez de dejar una banda muerta debajo. */
body{
  background:var(--bg);
  color:var(--ink);
  font:400 15px/1.55 var(--font-sans);
  -webkit-font-smoothing:antialiased;
  min-height:100vh;
  min-height:100dvh;
  display:flex;
  flex-direction:column;
}
main.shell{flex:1 0 auto}
.siteline{flex:none}
h1,h2,h3{font-family:var(--font-sans)}
button,input{font:inherit}
img{max-width:100%}

.visually-hidden{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0; border:0;
  clip:rect(0 0 0 0); clip-path:inset(50%); overflow:hidden; white-space:nowrap;
}

/* ============================================================
   FOCO — un solo lenguaje, dos jurisdicciones
   ------------------------------------------------------------
   Verde = foco de algo OPERABLE (botón, campo, enlace, control).
   Neutro = aterrizaje: un encabezado o un estado al que el programa llevó el foco para que el
   lector de pantalla lo lea. Son `tabindex="-1"`, no se pueden tabular y no hacen nada al
   pulsarlos; pintarlos de verde los haría pasar por controles.

   Lo que esto repara es concreto: el navegador dibujaba su propio anillo azul —`outline:auto`,
   `rgb(0,95,204)`— sobre esos encabezados en cuanto la persona avanzaba con el teclado. Azul no
   pertenece a este sistema. Se sustituye, no se elimina: el aterrizaje sigue siendo visible.
   ============================================================ */
:focus:not(:focus-visible){outline:none}
[tabindex="-1"]{outline:none}
[tabindex="-1"]:focus-visible{
  outline:2px solid var(--mute);
  outline-offset:5px;
  border-radius:4px;
}
/* Los aterrizajes cortos se ajustan al texto para que el anillo no dibuje una banda del ancho
   de la columna. Van solos en su línea: encogerlos no mueve nada. */
.eyebrow[tabindex="-1"],.search-status[tabindex="-1"]{display:inline-block}

/* ============================================================
   MARCA — la superficie pública es de cara a Urbani
   ------------------------------------------------------------
   Composición tipográfica en Montserrat (la tipografía de marca, ya self-hosted) más una barra
   verde. Aceptado como lockup definitivo del MVP (TP-CMF-F2-008F:R1): NO es el logotipo
   corporativo maestro oficial, y esta decisión no crea uno ni lo imita.
   ============================================================ */
.brand,.siteline-brand{
  display:inline-flex;align-items:center;gap:var(--s-2);
}
.brand-mark{
  width:3px;height:16px;border-radius:2px;background:var(--brand-green);flex:none;
}
.brand-word{
  font-weight:700;font-size:17px;letter-spacing:.02em;color:var(--ink);line-height:1;
}

.topbar{background:var(--surface);border-bottom:1px solid var(--line);padding:var(--s-4) 0}
.topbar .in{
  max-width:640px;margin:0 auto;padding:0 var(--s-5);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
}
.brand-context{
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:600;color:var(--mute);
  text-align:right;
}

/* Cierre de página: la marca otra vez, y el encuadre de claims en su sitio natural. */
.siteline{border-top:1px solid var(--line);background:var(--surface);padding:var(--s-8) 0}
.siteline .in{max-width:640px;margin:0 auto;padding:0 var(--s-5)}
.siteline-brand{margin-bottom:var(--s-3)}
.siteline-brand .brand-word{font-size:15px}
.siteline-note{
  margin:0;font-size:12px;line-height:1.55;color:var(--mute);max-width:60ch;
}

/* ---------- shell ----------
   `width:100%` NO es decorativo. `body` es un contenedor flex en columna y `.shell` es uno de
   sus ítems; unos márgenes automáticos en el eje transversal desactivan el `stretch`, de modo
   que sin un ancho definido la columna se encogía a `fit-content`. El resultado medido: 640 px
   en entrada, resultado, contacto y cierre, pero 216 px en análisis y 358 px en búsqueda — la
   columna cambiaba de ancho al cambiar de pantalla, y el escenario del análisis quedaba
   encajonado en un tercio de su medida. Con un ancho definido, `max-width` vuelve a mandar y
   las seis pantallas comparten una sola columna. */
.shell{max-width:640px;width:100%;margin:0 auto;padding:var(--s-8) var(--s-5) var(--s-16)}
@media (min-width:768px){ .shell{padding-top:var(--s-10)} }
@media (min-width:1280px){ .shell{padding-top:var(--s-16);padding-bottom:var(--s-20)} }

/* ---------- escenas ---------- */
.screen[hidden]{display:none}
.screen{opacity:1;transform:none}
.screen.is-entering{opacity:0;transform:translateY(12px)}
.screen.is-entered{
  opacity:1;transform:none;
  transition:opacity var(--t-scene) var(--e-inout), transform var(--t-scene) var(--e-inout);
}

/* ============================================================
   TIPOGRAFÍA — escala. Sin medios píxeles, sin peso 650.
   ============================================================ */
.eyebrow{
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--mute);
  font-weight:700;margin:0 0 var(--s-3);
}
h1.title{
  font-size:26px;line-height:1.18;letter-spacing:-.022em;margin:0 0 var(--s-3);
  font-weight:700;text-wrap:balance;
}
@media (min-width:768px){ h1.title{font-size:32px} }
/* Título de continuidad: la pantalla ya tiene contexto, así que el título no compite con la
   cifra ni con la fotografía que vienen debajo. Antes vivía como `style` en el marcado. */
h1.title--sm{font-size:22px;letter-spacing:-.018em}
@media (min-width:768px){ h1.title--sm{font-size:24px} }
.lede{color:var(--ink-2);margin:0 0 var(--s-8);max-width:52ch;font-size:15px}

/* ---------- entrada ----------
   La ejecutiva acompaña al encabezado y nunca le quita medida: dentro de la columna de 640 px
   una segunda columna dejaría el titular en cuatro renglones. Se apila —alineada a la
   izquierda, no centrada como un mascota— y sólo pasa a columna lateral cuando el margen del
   viewport la puede alojar entera, que es lo que pide advisor-character-spec.md §4. */
.entry-hero{position:relative;margin-bottom:var(--s-8)}
.entry-hero .advisor--welcome{margin-left:0;margin-right:auto}
.entry-hero-copy .lede{margin-bottom:0}
@media (min-width:1240px){
  .entry-hero .advisor--welcome{
    position:absolute;right:calc(100% + var(--s-10));bottom:0;
    width:200px;margin:0;
  }
}

/* ============================================================
   CARGA DE DOCUMENTO
   ============================================================ */
.upload-zone{
  display:block;position:relative;text-align:center;cursor:pointer;
  border:1.5px dashed var(--line-strong);border-radius:var(--r-card);
  padding:var(--s-10) var(--s-5);
  background:var(--surface-2);
  transition:border-color var(--t-comp) var(--e-out),
             background-color var(--t-comp) var(--e-out),
             transform var(--t-comp) var(--e-out),
             box-shadow var(--t-comp) var(--e-out);
}
.upload-zone:hover{border-color:var(--ink-2)}
.upload-zone:focus-within{outline:2px solid var(--brand-green);outline-offset:3px}
/* Arrastre en curso: el estado se vuelve explícito, no sólo un cambio de borde. */
.upload-zone.is-dragover{
  border-color:var(--brand-green);
  border-style:solid;
  background:var(--green-wash);
  transform:translateY(-2px);
  box-shadow:var(--e-2);
}
.upload-zone svg{
  width:32px;height:32px;color:var(--ink-2);margin-bottom:var(--s-3);
  transition:color var(--t-comp) var(--e-out), transform var(--t-comp) var(--e-out);
}
.upload-zone.is-dragover svg{color:var(--brand-green-deep);transform:translateY(-3px)}
.upload-zone .u-label{font-weight:600;font-size:15px;color:var(--ink)}
.upload-zone .u-hint{font-size:13px;color:var(--mute);margin-top:var(--s-1)}
.upload-zone input[type=file]{
  position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
}

.privacy-note{
  display:flex;gap:var(--s-2);align-items:flex-start;
  margin:var(--s-4) 0 var(--s-6);font-size:13px;color:var(--mute);line-height:1.5;
}
.privacy-note svg{width:15px;height:15px;flex:none;margin-top:2px;color:var(--mute)}

.file-chip{
  display:flex;align-items:center;gap:var(--s-3);
  border:1px solid var(--line);border-radius:var(--r-control);
  padding:var(--s-3);margin-bottom:var(--s-6);background:var(--surface);
}
.file-chip[hidden]{display:none}
.file-chip:not([hidden]){animation:chip-in var(--t-comp) var(--e-out) both}
@keyframes chip-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.file-chip .fc-name{
  flex:1;min-width:0;font-size:14px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.file-chip .fc-meta{font-size:12px;color:var(--mute);font-variant-numeric:tabular-nums}
.file-chip button{
  background:none;border:0;color:var(--mute);font-size:13px;cursor:pointer;
  padding:var(--s-2);border-radius:6px;text-decoration:underline;
  transition:color var(--t-micro) var(--e-out);
}
.file-chip button:hover{color:var(--ink)}
.file-chip button:focus-visible{outline:2px solid var(--brand-green);outline-offset:2px}

/* ============================================================
   CAMPOS
   ============================================================ */
.field-group[hidden]{display:none}
.field-group{margin-bottom:var(--s-6)}
.field{margin-bottom:0}
label.field-label{
  display:block;font-size:13px;font-weight:600;color:var(--ink);margin-bottom:var(--s-2);
}
.field-hint{font-size:12px;color:var(--mute);margin-top:var(--s-2);line-height:1.45}
input[type=text],input[type=tel],input[type=number]{
  width:100%;min-height:48px;padding:var(--s-3) var(--s-4);
  border:1px solid var(--line-strong);border-radius:var(--r-control);
  background:var(--surface);color:var(--ink);
  transition:border-color var(--t-micro) var(--e-out), box-shadow var(--t-micro) var(--e-out);
}
input:focus-visible,input:focus{
  outline:none;border-color:var(--brand-green);
  box-shadow:0 0 0 3px var(--green-wash);
}
input[aria-invalid=true]{border-color:var(--stop-line);background:var(--stop-bg)}
input[aria-invalid=true]:focus{box-shadow:0 0 0 3px rgba(140,38,38,.10)}

/* Confirmación de campo válido. Único uso decorativo del verde en el tramo financiero,
   y no toca ninguna cifra.

   Va anclada a la LÍNEA DE LA ETIQUETA, no debajo del campo. Debajo empujaría todo lo que
   sigue —incluido el botón de envío— en el instante en que el campo pierde el foco: la
   persona va a pulsar el botón, el acuse aparece, el botón se mueve y el toque falla. Aquí
   ocupa un hueco que ya existía y el reflujo es exactamente cero. */
.field-group{position:relative}
.field-ok{
  position:absolute;top:0;right:0;margin:0;
  display:flex;align-items:center;gap:5px;
  font-size:12px;font-weight:600;color:var(--brand-green-deep);
  pointer-events:none;
}
.field-ok[hidden]{display:none}
.field-ok svg{width:13px;height:13px;flex:none}
.field-ok svg path{
  stroke-dasharray:16;stroke-dashoffset:0;
  animation:tick var(--t-micro) var(--e-out) both;
}
@keyframes tick{from{stroke-dashoffset:16}to{stroke-dashoffset:0}}

.field-error{
  display:flex;gap:6px;align-items:flex-start;font-size:13px;color:var(--stop);
  margin-top:var(--s-2);line-height:1.4;
}
.field-error[hidden]{display:none}
.field-error svg{width:14px;height:14px;flex:none;margin-top:1px}
.figure-cash{font-variant-numeric:tabular-nums}

/* ============================================================
   BOTONES — táctil mínimo 48px, primario y fantasma
   ============================================================ */
.btn{
  width:100%;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:48px;padding:var(--s-3) var(--s-5);
  border-radius:var(--r-control);border:1px solid transparent;
  font-weight:600;font-size:15px;cursor:pointer;font-family:var(--font-sans);
  transition:background-color var(--t-micro) var(--e-out),
             border-color var(--t-micro) var(--e-out),
             box-shadow var(--t-micro) var(--e-out),
             color var(--t-micro) var(--e-out),
             transform var(--t-micro) var(--e-out);
}
.btn:focus-visible{outline:2px solid var(--brand-green);outline-offset:2px}
.btn .arw{display:inline-block;transition:transform var(--t-micro) var(--e-out)}
.btn:hover .arw,.btn:focus-visible .arw{transform:translateX(3px)}

.btn-primary{background:var(--brand-green);color:var(--on-green)}
.btn-primary:hover:not(:disabled){background:var(--brand-green-dark);box-shadow:0 4px 14px var(--green-halo)}
.btn-primary:active:not(:disabled){transform:translateY(1px);background:var(--brand-green-deep)}
.btn-primary:disabled{background:var(--disabled-bg);color:var(--disabled-ink);cursor:not-allowed}
.btn-primary.is-busy{cursor:progress}

.btn-ghost{
  background:transparent;color:var(--ink);border-color:var(--line-strong);
  width:auto;min-height:48px;padding:var(--s-3) var(--s-5);
}
.btn-ghost:hover{border-color:var(--ink-2);background:var(--surface-2)}

/* ============================================================
   CAMINO CON URBANI — storytelling secundario (TP-CMF-F2-008H)
   ------------------------------------------------------------
   Banda compacta después de la acción primaria (subir/calcular) y antes del cierre de página.
   Cuatro pasos informativos, nunca accionables: son <li> de una lista ordenada, sin `tabindex`
   y sin semántica de botón o enlace. Todo el significado —índice, título, línea de apoyo— es
   texto visible sin interacción; el trazo y el realce son decoración que se añade encima.

   El trazo reutiliza el lenguaje de dibujo técnico ya aceptado en la escena de búsqueda (línea
   de datum + degradado verde→azul), aplicado aquí a la progresión de los cuatro pasos en vez
   de a la escena ilustrada. El azul vive ACOTADO a este componente, igual que en la escena de
   búsqueda: nunca se declara en `:root`.
   ============================================================ */
.journey{
  --journey-blue:#3333B9;
  margin-top:var(--s-10);padding-top:var(--s-8);border-top:1px solid var(--line);
}
@media (min-width:768px){ .journey{margin-top:var(--s-12);padding-top:var(--s-10)} }

.journey-heading{
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--mute);
  font-weight:700;margin:0 0 var(--s-8);
}

.journey-track{position:relative}

/* Línea de datum: neutra en reposo, siempre dibujada por completo — no depende de JS para
   verse. Sólo el RELLENO que se superpone (`.journey-rail-fill`) es la parte interactiva. */
.journey-rail{
  position:absolute;background:var(--line);border-radius:var(--r-pill);overflow:hidden;
  pointer-events:none;
  left:22px;width:2px;top:22px;bottom:22px;
}
.journey-rail-fill{
  position:absolute;inset:0;
  background:linear-gradient(180deg, var(--brand-green-dark), var(--journey-blue));
  transform-origin:top left;
  transform:scaleY(var(--journey-progress, 0));
  transition:transform var(--t-micro) var(--e-out);
}

.journey-stages{
  list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;gap:var(--s-8);
}
.journey-stage{
  position:relative;display:flex;gap:var(--s-4);align-items:flex-start;
}
.journey-node{
  position:relative;z-index:1;flex:none;
  width:44px;height:44px;border-radius:var(--r-pill);
  border:1px solid var(--line-strong);background:var(--surface);
  display:flex;align-items:center;justify-content:center;
  transition:border-color var(--t-micro) var(--e-out),
             box-shadow var(--t-micro) var(--e-out),
             transform var(--t-micro) var(--e-out);
}
.journey-icon{width:20px;height:20px;color:var(--ink-2);transition:color var(--t-micro) var(--e-out)}
.journey-content{display:flex;flex-direction:column;padding-top:3px;min-width:0}
.journey-index{
  font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--mute);
  font-variant-numeric:tabular-nums;transition:color var(--t-micro) var(--e-out);
}
.journey-title{
  font-size:15px;font-weight:700;letter-spacing:-.01em;color:var(--ink);
  margin:var(--s-1) 0 var(--s-1);
}
.journey-copy{font-size:13px;color:var(--ink-2);margin:0;line-height:1.5;max-width:46ch}

/* ---------- >= 900px: cuatro pasos en fila, un solo trazo horizontal ----------
   El trazo pasa a ser una franja por ENCIMA de la fila, no una línea que intente atravesar
   el centro de cada nodo: con `gap` entre columnas, el centro real de un nodo alineado a la
   izquierda no coincide con una fracción simple del ancho total, y perseguirlo con calc()
   produce una costura fina y frágil. Como franja independiente arriba de las cuatro columnas,
   el trazo sigue siendo continuo y su iluminación sigue siendo proporcional (25/50/75/100%),
   sin depender de esa coincidencia. */
@media (min-width:900px){
  .journey-track{display:flex;flex-direction:column}
  .journey-rail{
    position:relative;left:auto;right:auto;top:auto;bottom:auto;
    width:100%;height:2px;margin-bottom:var(--s-8);
  }
  .journey-rail-fill{
    background:linear-gradient(90deg, var(--brand-green-dark), var(--journey-blue));
    transform-origin:left center;transform:scaleX(var(--journey-progress, 0));
  }
  .journey-stages{flex-direction:row;gap:var(--s-6);align-items:flex-start}
  .journey-stage{flex:1;flex-direction:column;gap:var(--s-3);align-items:stretch;min-width:0}
  .journey-content{padding-top:0}
}

/* ---------- Hover de escritorio: sólo con puntero fino ---------- */
@media (hover:hover) and (pointer:fine){
  .journey-stage:hover .journey-node,
  .journey-stage:focus-within .journey-node{
    border-color:var(--brand-green-dark);
    box-shadow:var(--e-1);
    transform:translateY(-5px) scale(1.02);
  }
  .journey-stage:hover .journey-icon,
  .journey-stage:focus-within .journey-icon{color:var(--brand-green-deep)}
  .journey-stage:hover .journey-index,
  .journey-stage:focus-within .journey-index{color:var(--ink)}
  .journey-stage:hover .journey-title,
  .journey-stage:focus-within .journey-title{transform:translateY(-2px)}
  .journey-title{transition:transform var(--t-micro) var(--e-out)}
}

/* ---------- Estado "actual" en scroll móvil (<900px), sin equivalente en hover de escritorio */
@media (max-width:899px){
  .journey-stage.is-current .journey-node{
    border-color:var(--brand-green-dark);box-shadow:var(--e-1);
  }
  .journey-stage.is-current .journey-icon{color:var(--brand-green-deep)}
  .journey-stage.is-current .journey-index{color:var(--ink)}
}

/* ---------- Entrada única al aparecer en el viewport ---------- */
@media (prefers-reduced-motion:no-preference){
  @keyframes journey-rail-in-y{from{transform:scaleY(0)}to{transform:scaleY(1)}}
  @keyframes journey-rail-in-x{from{transform:scaleX(0)}to{transform:scaleX(1)}}
  @keyframes journey-stage-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

  .journey.journey-inview .journey-rail{
    animation:journey-rail-in-y 640ms var(--e-inout) both;
  }
  .journey.journey-inview .journey-stage{
    animation:journey-stage-in var(--t-scene) var(--e-out) both;
    animation-delay:calc(var(--stage-i, 0) * 90ms + 120ms);
  }
  @media (min-width:900px){
    .journey.journey-inview .journey-rail{
      animation-name:journey-rail-in-x;
    }
  }
}

/* ============================================================
   ANÁLISIS
   ============================================================ */
/* El análisis y la búsqueda son las dos pantallas de espera del recorrido, y se componen igual:
   el contenido se centra en el alto disponible en vez de colgar del borde superior. Antes el
   bloque arrancaba con 64 px fijos de aire arriba y dejaba ~240 px muertos abajo; ahora el vacío
   queda repartido y la pantalla se lee deliberada. `min-height` acotado con `min()` para que en
   un viewport bajo no empuje el alto de la página.

   `flex` y no `grid`: con `align-items:center` cada hijo se ajusta a su contenido, y eso es lo
   que conserva el anillo de foco del aterrizaje pegado al texto del encabezado en vez de
   dibujar una banda del ancho de la columna. */
.analysis-wrap{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  /* La MISMA constante que `.search-wrap`: las dos pantallas de espera comparten
     composición, no sólo se parecen. */
  min-height:min(58vh,520px);
  padding:var(--s-8) 0 var(--s-10);text-align:center;
}
/* ---------- Espera de análisis: INDETERMINADA ----------
   Esta pantalla no mide avance y por lo tanto no lo dibuja. Antes había una barra que recorría
   0→100 con un temporizador fijo: se llenaba a ~1,8s y se quedaba llena mientras la petición
   real seguía en vuelo, afirmando un final que nadie había alcanzado. Eso es la medida
   inventada que la escena de búsqueda ya tenía prohibida; aquí queda prohibida igual.

   Comparte VOCABULARIO de movimiento con `.search-line` —el mismo `@keyframes sweep`, que es
   el gesto "esto sigue ocurriendo" del sistema— pero conserva selectores propios y tiempo
   propio: análisis es el estado más callado de los dos y barre más lento. Búsqueda no depende
   de estas reglas ni estas reglas de las suyas.

   240 y no 320: con la columna en su medida real, un tope de 320 dejaba la pista MAS ancha que
   la propia ilustracion (192), y el elemento de mayor peso horizontal de la pantalla pasaba a
   ser el indicador. En una pantalla que no mide avance real, eso es justo lo que no debe
   dominar. Queda por encima de la ejecutiva y por debajo del bloque de texto. */
.analysis-wait{
  position:relative;height:3px;border-radius:var(--r-pill);background:var(--line);
  overflow:hidden;margin:var(--s-5) 0 0;width:100%;max-width:240px;
}
/* `transform`, nunca `width`: `width` provoca layout en cada cuadro. El tramo es corto y no
   tiene origen ni destino — entra por un borde y sale por el otro, indefinidamente. */
.analysis-wait span{
  position:absolute;inset-block:0;left:0;width:34%;border-radius:var(--r-pill);
  background:linear-gradient(90deg,transparent,var(--brand-green),transparent);
  animation:sweep 1900ms var(--e-inout) infinite;
}
.analysis-step{font-size:14px;color:var(--ink-2);font-weight:600;margin:0}

/* ============================================================
   BÚSQUEDA DE INVENTARIO
   ------------------------------------------------------------
   Estado diseñado, no un spinner y no un hueco. La pantalla se compone en dos piezas —el
   escenario ilustrado y la columna de texto— y se centra en el alto disponible, de modo que el
   vacío queda repartido y no volcado debajo.

   Honestidad: la lista nombra el trabajo real, está completa desde el primer cuadro, no lleva
   marcas de «completado» y no hay porcentaje. Sólo viaja el énfasis, que es decoración.
   ============================================================ */
.search-wrap{
  display:grid;gap:var(--s-8);justify-items:center;text-align:center;
  padding:var(--s-10) 0 var(--s-12);
  min-height:min(58vh,520px);align-content:center;
}
/* Escenario: la ilustración deja de flotar y pasa a tener un lugar. */
.search-stage{
  width:min(300px,100%);
  background:var(--stage);
  border-radius:var(--r-card);
  padding:var(--s-5) var(--s-5) 0;
  display:flex;justify-content:center;
  overflow:hidden;
}
.search-copy{display:grid;justify-items:center;gap:var(--s-4);width:min(420px,100%)}
.search-copy .eyebrow{margin:0}
.search-line{
  position:relative;width:min(320px,100%);height:3px;border-radius:var(--r-pill);
  background:var(--line);overflow:hidden;
}
.search-line span{
  position:absolute;inset-block:0;left:0;width:34%;border-radius:var(--r-pill);
  background:linear-gradient(90deg,transparent,var(--brand-green),transparent);
  animation:sweep 1500ms var(--e-inout) infinite;
}
@keyframes sweep{
  from{transform:translateX(-110%)}
  to{transform:translateX(330%)}
}
.search-status{
  font-size:17px;font-weight:700;letter-spacing:-.015em;color:var(--ink);margin:0;
  max-width:22ch;text-wrap:balance;
}

/* Los tres pasos, siempre los tres. El punto marca cuál se está describiendo ahora mismo; no
   marca cuál terminó, porque el servidor no informa eso y aquí no se inventa. */
.search-steps{
  list-style:none;margin:0;padding:0;display:grid;gap:var(--s-3);
  text-align:left;width:100%;max-width:340px;
}
.search-steps li{
  display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s-3);
  font-size:13px;line-height:1.4;color:var(--faint);
  transition:color var(--t-comp) var(--e-out);
}
.search-steps .ss-dot{
  width:7px;height:7px;border-radius:var(--r-pill);
  background:var(--line-strong);flex:none;
  transition:background-color var(--t-comp) var(--e-out),
             transform var(--t-comp) var(--e-out);
}
.search-steps li.is-current{color:var(--ink);font-weight:600}
.search-steps li.is-current .ss-dot{background:var(--brand-green);transform:scale(1.3)}
.search-sub{font-size:12px;color:var(--mute);margin:0;max-width:40ch;line-height:1.5}

/* ============================================================
   EJECUTIVA — ilustración final, decorativa. Ninguna información depende de ella.
   ------------------------------------------------------------
   Los cuatro assets comparten lienzo (640×656) y recorte, de modo que fijar el ANCHO fija
   también la escala y el eje de la figura en los cuatro estados.
   ============================================================ */
.advisor{display:block;margin-inline:auto;height:auto;max-width:100%}
.advisor[hidden]{display:none}
.advisor--welcome{width:164px;margin-bottom:var(--s-4)}
/* El recorte de la ilustración de análisis es más estrecho que el de bienvenida —la figura ocupa
   el 73 % del lienzo frente al 92 %—, así que a igual ancho de caja se leía un tercio más
   pequeña. La caja sube para compensar ese recorte, no para agrandar al personaje: sigue por
   debajo de la caja de bienvenida en ancho y en alto, y la figura sigue siendo visiblemente
   menor que en la entrada. */
.advisor--analysis{width:158px;margin-bottom:var(--s-6)}
/* Resultado: pequeño a propósito. Es el estado que CEDE el protagonismo. */
.advisor--result{width:96px;margin:0 0 var(--s-4)}
@media (min-width:768px){
  .advisor--welcome{width:208px}
  .advisor--analysis{width:192px}
}
/* Con la ejecutiva ya fuera de la columna, el margen del viewport la puede alojar más grande.
   La regla iba sin el ancestro y perdía por especificidad contra la del bloque de ≥1240, así que
   nunca llegó a aplicarse; queda escrita como lo que siempre quiso decir. */
@media (min-width:1280px){ .entry-hero .advisor--welcome{width:224px} }
/* Bajo 390px sólo sobrevive la bienvenida: en el resto, el espacio es del contenido. */
@media (max-width:389px){
  .advisor--analysis,.advisor--result{display:none}
  /* Sin ilustración, el escenario quedaría como una caja vacía con fondo. */
  .search-stage{display:none}
}

/* ============================================================
   ESCENA DE BÚSQUEDA — tres capas superpuestas que comparten caja
   ------------------------------------------------------------
   1. `.scene-trace` — firma de marca (traza verde→azul, retícula de cruces)
   2. `.scene-arch`  — arquitectura y linework
   3. `.scene-figure`— la ilustración final: la capa HUMANA, delante

   Las dos capas SVG comparten `viewBox` y ancho, así que encajan al píxel. El ancho lo posee
   el contenedor: si lo declarara cada capa, podrían desalinearse. La escena NO se aplana en
   una imagen — la arquitectura sigue siendo geometría animada detrás del arte.
   ============================================================ */
.search-scene{
  position:relative;width:195px;aspect-ratio:260/200;
  /* Azul oficial del degradado web de Urbani. Vive AQUÍ y no en `:root` a propósito: es una
     firma de movimiento acotada a esta escena, no un token de interfaz. Declararlo global
     abriría la puerta a que tiña la aplicación, que sigue siendo blanco, neutros cálidos,
     tinta y verde Urbani. */
  --urbani-blue:#3333B9;
}
.scene-trace,.scene-arch{position:absolute;inset:0;width:100%;height:auto;display:block}
/* La ilustración se apoya en el suelo de la escena y se centra SIN `transform`, para dejar el
   `transform` libre para su animación de entrada. */
.scene-figure{position:absolute;inset:0;display:flex;align-items:flex-end;justify-content:center}
.advisor--search{width:auto;height:96%;margin:0}
@media (min-width:768px){ .search-scene{width:221px} }

/* ============================================================
   `UrbaniSearchSequence` — la escena de búsqueda, en CSS/SVG.
   ------------------------------------------------------------
   Tier Narrativa (`--t-narr`, 1800ms) es el único que puede pasar de 560ms, y esta escena es
   su ÚNICO uso autorizado. El arco completo se asienta a ~2,1s, dentro de la ventana de
   1,8–2,2s que fijó remotion-search-sequence-spec.md §2.

   Todo se anima con `transform` y `opacity`. Nada mide progreso: la escena describe el trabajo
   que ya está ocurriendo, y cuando termina NO reinicia — pasa a una respiración ambiental
   mínima sobre la arquitectura. La ejecutiva queda quieta, como exige su especificación.
   ============================================================ */
.scene-arch .sm-vol{transform-box:fill-box}

/* 1. La ilustración final entra con la escena. Es la capa HUMANA: ya sostiene la tablet, así
      que el documento dibujado que animaba R1 desaparece — había uno solo y ahora vive en el
      arte. En reposo la ejecutiva queda QUIETA (advisor-character-spec.md §5): entra y se
      detiene; no respira y no tiene bucle propio. */
.advisor--search{animation:sm-figure 560ms var(--e-out) both}
@keyframes sm-figure{
  from{opacity:0;transform:translateY(10px)}
  to{opacity:1;transform:none}
}

/* 2. La arquitectura emerge escalonada, ya con la ejecutiva en cuadro. */
.scene-arch .sm-vol{
  transform-origin:50% 100%;
  animation:sm-vol 700ms var(--e-out) calc(1150ms + var(--sm-i) * 110ms) both;
}
@keyframes sm-vol{
  from{opacity:0;transform:translateY(12px) scaleY(.84)}
  to{opacity:1;transform:none}
}
.scene-arch .sm-win{animation:sm-win 400ms var(--e-out) 1700ms both}
@keyframes sm-win{from{opacity:0}to{opacity:1}}

/* 4b. FIRMA DE MARCA — la traza verde→azul, entre el dato y la arquitectura.
       ------------------------------------------------------------
       Es el puente del relato: se dibuja JUSTO cuando el dato termina de organizarse y JUSTO
       antes de que emerjan los volúmenes, de modo que el color explica el paso de una cosa a la
       otra en vez de decorarlo.

       Por qué no es neón: mide 1,6 px de alto, no emite —sin `filter`, sin `box-shadow`, sin
       halo—, se dibuja UNA vez y se queda quieta. El degradado se lee como tinta de dibujo
       técnico, que es el registro de la marca, no como un LED. */
.scene-trace .sm-datum,
.scene-trace .sm-riser,
.scene-trace .sm-plus path{transform-box:fill-box}

.scene-trace .sm-datum{
  transform-origin:0% 50%;
  animation:sm-datum 620ms var(--e-out) 1020ms both;
}
@keyframes sm-datum{
  from{opacity:0;transform:scaleX(0)}
  to{opacity:1;transform:none}
}
.scene-trace .sm-riser{
  transform-origin:50% 100%;
  animation:sm-riser 420ms var(--e-out) 1480ms both;
}
@keyframes sm-riser{
  from{opacity:0;transform:scaleY(0)}
  to{opacity:1;transform:none}
}

/* 4c. Retícula de cruces: resuelve al final, escalonada y muy tenue. Es identidad, no ruido —
       cinco marcas en todo el cuadro, en el aire libre, sin tocar nunca a la figura. */
.scene-trace .sm-plus path{
  transform-origin:50% 50%;
  animation:sm-plus 320ms var(--e-out) calc(1640ms + var(--sm-i) * 55ms) both;
}
@keyframes sm-plus{
  from{opacity:0;transform:scale(.4)}
  to{opacity:1;transform:none}
}

/* 5. Espera larga: NO se reinicia la narrativa. La escena queda asentada y sólo respira la
      arquitectura, muy poco y muy lento. Sin espectáculo, sin neón, sin loader. */
.scene-arch .sm-arch{animation:sm-breathe 5200ms var(--e-inout) 2300ms infinite}
@keyframes sm-breathe{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(-1.6px)}
}

/* ============================================================
   RESULTADO FINANCIERO
   ------------------------------------------------------------
   Las cifras son SIEMPRE tinta. Nunca verde. La luz suave del panel es el único verde aquí, al
   9% y en el fondo.

   Jerarquía: la capacidad es la cifra protagonista y su panel manda; el techo de precio es su
   consecuencia bajo un escenario y se compone un escalón por debajo —fondo apagado, cifra más
   chica, misma medida—. La etiqueta y el selector comparten una sola línea en escritorio, de
   modo que la cifra empieza más arriba y el bloque deja de leerse como un formulario.

   Nada de esto toca un cálculo: el bloque llega resuelto por el motor y aquí sólo se presenta.
   ============================================================ */
.result-block{
  position:relative;overflow:hidden;
  border:1px solid var(--line);border-radius:var(--r-card);
  padding:var(--s-6) var(--s-5);
  margin-bottom:var(--s-4);background:var(--surface);
}
@media (min-width:768px){ .result-block{padding:var(--s-8) var(--s-6)} }
.result-block::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(90% 70% at 6% 0%, var(--green-halo), transparent 60%);
}
.result-block > *{position:relative}
.result-block h2{
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;
  color:var(--mute);font-weight:700;margin:0;max-width:34ch;
}
/* El escenario es la consecuencia, no un segundo protagonista. */
.result-block--scenario{background:var(--surface-2);border-color:var(--line)}
.result-block--scenario::before{display:none}

.rb-head{
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-4);
  margin-bottom:var(--s-5);
}
@media (min-width:600px){
  .rb-head{flex-direction:row;align-items:center;justify-content:space-between;gap:var(--s-5)}
}
.rb-lead{font-size:13px;color:var(--ink-2);margin:0 0 var(--s-4);line-height:1.5;max-width:46ch}
/* Una línea fina separa la cifra de sus advertencias: la cifra es el dato, lo de abajo es su
   encuadre, y no deben leerse como el mismo párrafo. */
.rb-foot{
  margin-top:var(--s-5);padding-top:var(--s-4);border-top:1px solid var(--line-2);
}
.rb-foot .figure-caption:first-child{margin-top:0}

/* segmented — el indicador se desliza. Único overshoot del sistema. */
.segmented{
  position:relative;display:inline-grid;grid-auto-flow:column;
  border:1px solid var(--line);border-radius:var(--r-pill);
  padding:3px;background:var(--surface);flex:none;
}
.segmented::before{
  content:"";position:absolute;top:3px;bottom:3px;left:3px;
  width:calc(50% - 3px);border-radius:var(--r-pill);
  background:var(--brand-green);
  transform:translateX(calc(var(--seg-i, 0) * 100%));
  transition:transform var(--t-micro) var(--e-snap);
}
.segmented button{
  position:relative;z-index:1;
  border:0;background:transparent;padding:var(--s-3) var(--s-5);
  /* 48px como toda acción táctil primaria (§25 del packet): el pill queda en 54 con su
     relleno de 3px, que sigue leyéndose como control y no como bloque. */
  min-height:48px;border-radius:var(--r-pill);
  font-size:13px;font-weight:600;color:var(--ink-2);cursor:pointer;
  font-family:var(--font-sans);white-space:nowrap;
  transition:color var(--t-micro) var(--e-out);
}
.segmented button[aria-pressed=true]{color:var(--on-green)}
.segmented button:focus-visible{outline:2px solid var(--brand-green);outline-offset:3px}

.figure-uf{
  font-size:44px;font-weight:700;letter-spacing:-.03em;line-height:1;
  font-variant-numeric:tabular-nums;color:var(--ink);margin:0;
}
@media (min-width:768px){ .figure-uf{font-size:60px} }
.figure-clp{
  font-size:15px;color:var(--ink-2);margin-top:var(--s-3);
  font-variant-numeric:tabular-nums;
}
.figure-caption{font-size:13px;color:var(--mute);margin:var(--s-2) 0 0;line-height:1.5}

/* El techo de precio es la cifra secundaria del tramo: grande, pero no la protagonista. */
.figure-uf--scenario{font-size:34px}
@media (min-width:768px){ .figure-uf--scenario{font-size:44px} }

details.assumptions{
  margin-top:var(--s-5);font-size:13px;color:var(--ink-2);
  border-top:1px solid var(--line);padding-top:var(--s-4);
}
details.assumptions summary{
  cursor:pointer;font-weight:600;color:var(--ink);font-size:13px;
  /* Era 24px: el unico control operable de toda la interfaz por debajo del minimo tactil, en
     una pantalla que en movil se usa con el pulgar. El resto del sistema ya esta en 48; este
     no puede subir tanto sin separarse de su propio bloque, asi que toma el minimo de 44. */
  min-height:44px;display:flex;align-items:center;gap:var(--s-2);
  list-style:none;
}
details.assumptions summary::-webkit-details-marker{display:none}
/* `display:flex` en `summary` borra el triángulo del navegador: se repone como chevrón propio,
   que además gira al abrir. */
details.assumptions summary::before{
  content:"";width:6px;height:6px;flex:none;
  border-right:1.5px solid var(--mute);border-bottom:1.5px solid var(--mute);
  transform:rotate(-45deg);transform-origin:60% 60%;
  transition:transform var(--t-micro) var(--e-out);
}
details.assumptions[open] summary::before{transform:rotate(45deg)}
details.assumptions summary:focus-visible{outline:2px solid var(--brand-green);outline-offset:2px}
details.assumptions ul{margin:var(--s-3) 0 0;padding-left:18px;line-height:1.6}
details.assumptions li+li{margin-top:var(--s-2)}

.cta-row{display:flex;flex-direction:column;gap:var(--s-3);margin-top:var(--s-8)}

/* Lo que todavía no existe se declara como agenda, no como tres controles rotos. Tres botones
   deshabilitados del alto de un CTA competían en peso con el único CTA real de la pantalla. */
.soon{
  margin-top:var(--s-6);padding-top:var(--s-5);border-top:1px solid var(--line);
}
.soon-title{
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;
  color:var(--mute);margin:0 0 var(--s-3);
}
.soon-list{
  list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:var(--s-2);
}
.soon-list button{
  font-size:12px;color:var(--faint);font-weight:600;font-family:var(--font-sans);
  border:1px solid var(--line);border-radius:var(--r-pill);
  padding:var(--s-2) var(--s-4);background:var(--surface);
  cursor:not-allowed;
}

/* ---------- lead ---------- */
.lead-summary{
  border:1px solid var(--line);border-radius:var(--r-card);
  padding:var(--s-4);margin-bottom:var(--s-6);
  background:var(--surface-2);font-size:13px;color:var(--ink-2);
}
.lead-summary strong{color:var(--ink);font-variant-numeric:tabular-nums}

/* ---------- estado final ---------- */
.postlead-wrap{text-align:left;padding:var(--s-2) 0}
.postlead-icon{width:36px;height:36px;color:var(--brand-green-deep);margin-bottom:var(--s-4)}

/* ============================================================
   FICHAS DE PROYECTO — el momento de mayor energía visual.
   La fotografía manda; la ficha es su marco.
   ============================================================ */
.recos{margin:var(--s-2) 0 var(--s-8)}
.recos[hidden]{display:none}
.recos-head{margin-bottom:var(--s-5)}
.recos-title{
  font-size:15px;letter-spacing:-.01em;font-weight:700;margin:0 0 var(--s-2);color:var(--ink);
}
/* 64ch y no 56: Montserrat es más ancha que el stack de sistema con el que se compuso
   esta nota, y a 56ch el último renglón quedaba con una sola palabra huérfana en
   escritorio. Ajuste de métrica, no de diseño. */
.recos-note{font-size:13px;color:var(--mute);margin:0;line-height:1.5;max-width:64ch}

.recos-grid{display:grid;gap:var(--s-5);grid-template-columns:minmax(0,1fr)}
.recos-grid>*{min-width:0}
.recos[data-count="1"] .recos-grid{grid-template-columns:minmax(0,420px)}
@media (min-width:768px){
  .recos[data-count="2"] .recos-grid,.recos[data-count="3"] .recos-grid{
    grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  }
}
/* En escritorio la BANDA FOTOGRÁFICA se ensancha más allá de la columna; el texto no.
   ------------------------------------------------------------
   Antes se ensanchaba el bloque completo —título, nota, fichas y vigencia—, y eso dejaba la
   pantalla final con dos bordes izquierdos de texto: el titular del estado quedaba en la columna
   de 640 y el título del bloque de proyectos, 220 px más a la izquierda. Ensanchando sólo la
   rejilla, todo el texto de la pantalla comparte un único borde y la toma de ancho completo es
   de la fotografía, que es de lo que se trataba. Acotado con `min()`: nunca desborda en
   horizontal. */
@media (min-width:1280px){
  .recos[data-count="2"] .recos-grid,.recos[data-count="3"] .recos-grid{
    position:relative;left:50%;transform:translateX(-50%);
    width:min(1040px,calc(100vw - 64px));
  }
  .recos[data-count="2"] .recos-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .recos[data-count="3"] .recos-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (min-width:1440px){
  .recos[data-count="2"] .recos-grid,.recos[data-count="3"] .recos-grid{
    width:min(1120px,calc(100vw - 64px));
  }
}

.pcard{
  position:relative;
  display:flex;flex-direction:column;min-width:0;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-card);
  overflow:hidden;
  box-shadow:var(--e-1);
  transition:transform var(--t-comp) var(--e-out),
             box-shadow var(--t-comp) var(--e-out),
             border-color var(--t-comp) var(--e-out);
}

/* Luz de borde: arco de ~26% del perímetro, opacidad máxima .55, UNA pasada y se asienta.
   Un loop convierte cualquier luz en un LED. Compuesta por GPU: no toca layout. */
.pcard::after{
  content:"";position:absolute;inset:0;border-radius:var(--r-card);
  padding:1px;pointer-events:none;opacity:0;
  background:conic-gradient(from var(--angle),
      transparent 0 58%, var(--green-edge) 72%, transparent 84% 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite:exclude;
  transition:opacity var(--t-comp) var(--e-out);
}

.pcard-media{
  position:relative;aspect-ratio:7/5;overflow:hidden;background:var(--surface-2);
  display:flex;align-items:center;justify-content:center;
}
.pcard-img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--t-comp) var(--e-out);
}

/* Hover sólo donde hay puntero fino. En táctil esto no existe. */
@media (hover:hover) and (pointer:fine){
  .pcard:hover,.pcard:focus-within{
    transform:translateY(-6px) scale(1.012);
    box-shadow:var(--e-2), 0 10px 30px var(--green-halo);
    border-color:transparent;
  }
  .pcard:hover::after,.pcard:focus-within::after{
    opacity:1;
    animation:edge-travel 900ms var(--e-out) forwards;
  }
  .pcard:hover .pcard-img,.pcard:focus-within .pcard-img{transform:scale(1.035)}
  .pcard:hover .pcard-brochure .arw,
  .pcard:focus-within .pcard-brochure .arw{transform:translateX(3px)}
}
@keyframes edge-travel{ from{--angle:0deg} to{--angle:360deg} }

/* Táctil: compresión al presionar, con retorno rápido. */
@media (hover:none){
  .pcard:active{transform:scale(.985)}
}

.pcard-body{padding:var(--s-5);display:flex;flex-direction:column;flex:1}
/* La comuna va ANTES del nombre: quien compara tres proyectos filtra por dónde antes que por
   cuál. */
.pcard-commune{
  font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mute);margin:0 0 var(--s-1);
}
.pcard-name{
  font-size:20px;font-weight:700;letter-spacing:-.015em;line-height:1.22;
  margin:0 0 var(--s-3);color:var(--ink);overflow-wrap:anywhere;
}
.pcard-price{display:flex;align-items:baseline;gap:var(--s-2);margin:0;flex-wrap:wrap}
.pcard-from{
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;color:var(--mute);
}
.pcard-amount{
  font-size:24px;font-weight:700;letter-spacing:-.02em;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.pcard-context{font-size:13px;color:var(--ink-2);margin:var(--s-2) 0 0;line-height:1.45}
.pcard-brochure{
  display:inline-flex;align-items:center;gap:var(--s-2);
  align-self:flex-start;min-height:48px;margin-top:auto;padding:var(--s-3) var(--s-4);
  border:1px solid var(--line-strong);border-radius:var(--r-control);
  background:var(--surface);color:var(--ink);
  font-size:13px;font-weight:600;text-decoration:none;
  transition:border-color var(--t-micro) var(--e-out),
             background-color var(--t-micro) var(--e-out);
}
.pcard-brochure .arw{display:inline-block;transition:transform var(--t-micro) var(--e-out)}
.pcard-brochure:hover{border-color:var(--ink-2);background:var(--surface-2)}
.pcard-brochure:focus-visible{outline:2px solid var(--brand-green);outline-offset:2px}
/* El margen superior lo pone el botón cuando existe; si no hay brochure, lo pone el contexto. */
.pcard-brochure{margin-top:var(--s-5)}
.pcard-body > .pcard-context:last-child{margin-bottom:0}

/* Pie de ficha alineado.
   ------------------------------------------------------------
   La regla de arriba pisaba el `margin-top:auto` declarado en el bloque del botón, así que el
   brochure quedaba pegado al último párrafo en vez de al pie de la ficha. Como las fichas de una
   misma fila tienen la MISMA altura pero distinto número de líneas —«1 unidad» no imprime la
   línea de «la más cercana»—, los tres botones caían a alturas distintas y la ficha más corta
   quedaba con un hueco muerto debajo.

   Se ancla el botón al pie y la separación mínima la aporta el párrafo anterior, de modo que en
   la ficha más alta —donde no sobra espacio— el botón nunca toca el texto. El `:has()` está para
   que, donde no exista, se conserve exactamente el comportamiento anterior en vez de degradar a
   un botón pegado al párrafo. */
.pcard-body:has(> .pcard-brochure) .pcard-context:last-of-type{margin-bottom:var(--s-5)}
.pcard-body:has(> .pcard-brochure) .pcard-brochure{margin-top:auto}

/* Tratamiento de marca sin fotografía oficial. Estado de primera clase: conserva lift, luz y
   foco. Nunca una imagen rota, nunca la foto de otro proyecto, nunca banco de imágenes.

   Antes era un rectángulo gris con dos letras flotando: se leía como el hueco de algo que
   faltaba. Ahora es una placa compuesta —motivo arquitectónico plano al fondo, monograma en su
   soporte y una línea que declara de qué es la placa—, de modo que la ficha se sostiene sola.
   No se dibuja ningún edificio concreto y no se insinúa una fotografía: el motivo es geometría,
   deliberadamente abstracta. */
.pcard-media--fallback{
  background:var(--stage);
  border-bottom:2px solid var(--brand-green);
}
.pcard-plate{
  position:relative;width:100%;height:100%;
  display:grid;align-content:center;justify-items:center;gap:var(--s-3);
  padding:var(--s-5);
}
.pcard-plate-art{
  position:absolute;inset:0;width:100%;height:100%;
  color:var(--line-strong);pointer-events:none;
}
.pcard-monogram{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;
  width:56px;height:56px;border-radius:var(--r-pill);
  border:1px solid var(--line-strong);background:var(--surface);
  font-size:18px;font-weight:700;letter-spacing:.06em;color:var(--ink-2);
  user-select:none;
}
.pcard-plate-label{
  position:relative;margin:0;
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-weight:700;color:var(--mute);
}

/* Llegada escalonada: 80ms, tope en 3. Sólo bajo `no-preference` (ver bloque reduce). */
.recos-freshness{
  display:flex;gap:var(--s-2);align-items:flex-start;margin:var(--s-4) 0 0;
  font-size:12px;color:var(--mute);line-height:1.5;
}
.recos-freshness svg{width:14px;height:14px;flex:none;margin-top:2px}

/* ============================================================
   DESENLACES SIN FICHAS
   Deben leerse DISTINTO entre sí. Ninguno usa el rojo de rechazo:
   no son un rechazo de nada.
   ============================================================ */
.reco-state{
  border:1px solid var(--line);border-radius:var(--r-card);
  padding:var(--s-6) var(--s-5);margin:var(--s-2) 0 var(--s-6);background:var(--surface);
}
.reco-state[hidden]{display:none}
.reco-state h2{
  font-size:18px;font-weight:700;letter-spacing:-.015em;line-height:1.3;
  margin:0 0 var(--s-2);color:var(--ink);
}
.reco-state p{font-size:14px;color:var(--ink-2);margin:0;line-height:1.55;max-width:56ch}
.reco-state-icon{width:28px;height:28px;margin-bottom:var(--s-3)}
.reco-state--empty{background:var(--surface-2)}
.reco-state--empty .reco-state-icon{color:var(--ink-2)}
.reco-state--pending{background:var(--warn-bg);border-color:var(--warn-line)}
.reco-state--pending h2{color:var(--warn)}
.reco-state--pending .reco-state-icon{color:var(--warn)}

.postlead-contact{
  border:1px solid var(--line);border-radius:var(--r-card);
  padding:var(--s-4);margin:0 0 var(--s-6);background:var(--surface);
}
.postlead-contact .pc-title{font-size:14px;font-weight:700;margin:0 0 var(--s-1);color:var(--ink)}
.postlead-contact .pc-text{font-size:13px;color:var(--ink-2);margin:0;line-height:1.55}

/* Análisis completo sin capacidad orientativa. Deliberadamente neutral: ni el verde de
   afirmación ni el rojo de rechazo. No es un error. */
.no-capacity-wrap{text-align:left;padding:var(--s-2) 0}
.no-capacity-icon{width:36px;height:36px;color:var(--ink-2);margin-bottom:var(--s-4)}
#no-capacity-back{margin-top:var(--s-2)}

/* ============================================================
   ALERTAS
   ============================================================ */
.form-alert{
  display:flex;gap:var(--s-2);align-items:flex-start;font-size:13px;color:var(--stop);
  background:var(--stop-bg);border:1px solid var(--stop-line);border-radius:var(--r-control);
  padding:var(--s-3) var(--s-4);margin:0 0 var(--s-5);line-height:1.45;
}
.form-alert[hidden]{display:none}
.form-alert svg{width:16px;height:16px;flex:none;margin-top:1px}
.form-alert--warn{color:var(--warn);background:var(--warn-bg);border-color:var(--warn-line)}

/* ============================================================
   MOVIMIENTO
   La entrada escalonada se declara SÓLO bajo `no-preference`: con movimiento reducido no
   existe ni la animación ni su retardo, y las fichas están visibles desde el primer pintado.
   ============================================================ */
@media (prefers-reduced-motion: no-preference){
  @keyframes pcard-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
  /* `--t-scene`, la misma duración con la que entra la pantalla: la llegada de las fichas y el
     cambio de escena son un solo gesto, no dos que casi coinciden. */
  .pcard{
    animation:pcard-in var(--t-scene) var(--e-out) both;
    animation-delay:calc(var(--pcard-index, 0) * 80ms);
  }
}

/* ============================================================
   MOVIMIENTO REDUCIDO — «quitar traslado, mantener estado»
   No se aplana todo a .001ms: eso elimina el feedback de hover y foco y hace que la
   interfaz se sienta rota. Se quita el VIAJE; se conserva el ESTADO y la información.
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  /* fuera: traslado, escala, zoom, barrido, viaje de la luz */
  .screen.is-entering{opacity:1;transform:none}
  .screen.is-entered{transition:none}
  .file-chip:not([hidden]){animation:none}
  .field-ok svg path{animation:none}
  /* El barrido se congela. Y al congelarse NO puede quedar a media pista: una barra verde
     detenida en un tramo se lee como «62 % hecho», que es exactamente el porcentaje inventado
     que esta pantalla tiene prohibido mostrar. Queda como una regla neutra completa —presencia,
     no medida— y la información la llevan los tres pasos, que siguen enteros en pantalla. */
  .search-line{background:none}
  .search-line span{
    animation:none;width:100%;transform:none;background:var(--line-strong);
  }
  /* El punto del paso vigente cambia de color, no de tamaño. */
  .search-steps li.is-current .ss-dot{transform:none}
  /* `UrbaniSearchSequence`: se retira la narrativa entera —el equivalente a no reproducir el
     video— y la escena aparece YA ASENTADA, que es su póster estático. No queda ni un cuadro
     de traslado, ni la respiración ambiental, y la ejecutiva aparece ya en su sitio. */
  .scene-arch .sm-arch,.scene-arch .sm-vol,.scene-arch .sm-win,.advisor--search,
  .scene-trace .sm-datum,.scene-trace .sm-riser,.scene-trace .sm-plus path{
    animation:none;opacity:1;transform:none;
  }
  .pcard{animation:none}
  .pcard:hover,.pcard:focus-within{transform:none}
  .pcard:hover .pcard-img,.pcard:focus-within .pcard-img{transform:none}
  .pcard:hover::after,.pcard:focus-within::after{animation:none;--angle:135deg;opacity:1}
  .upload-zone.is-dragover{transform:none}
  .upload-zone.is-dragover svg{transform:none}
  .btn:hover .arw,.btn:focus-visible .arw,
  .pcard:hover .pcard-brochure .arw,.pcard:focus-within .pcard-brochure .arw{transform:none}
  .btn-primary:active:not(:disabled){transform:none}

  /* Camino con Urbani: sin dibujo de entrada (ya vive sólo bajo `no-preference`, arriba) y sin
     traslado/escala en el hover ni en el estado "actual" del scroll móvil. El realce de borde,
     sombra y color se queda — es información de estado, no viaje. El relleno del trazo deja de
     animarse: salta a su proporción en vez de recorrerla. */
  .journey-stage:hover .journey-node,.journey-stage:focus-within .journey-node,
  .journey-stage.is-current .journey-node{transform:none}
  .journey-stage:hover .journey-title,.journey-stage:focus-within .journey-title{transform:none}
  .journey-rail-fill{transition:none}

  /* se queda: color, borde, elevación y estado seleccionado, a 120ms */
  .pcard,.upload-zone,.btn,.pcard-brochure,.journey-node,
  input[type=text],input[type=tel],input[type=number]{
    transition-property:background-color,border-color,box-shadow,color;
    transition-duration:120ms;
  }
  /* el segmented conserva su estado seleccionado; pierde el deslizamiento */
  .segmented::before{transition:none}
  .segmented button{transition:none}
  /* Espera de análisis: estado INDETERMINADO ESTÁTICO. Se detiene el barrido, y al detenerse no
     puede quedar ni lleno ni anclado al borde izquierdo: un tramo llena la pista se lee como
     "listo", y un tramo pegado a la izquierda se lee como "20 % hecho". Las dos son medidas que
     esta pantalla no tiene. Queda un tramo corto y CENTRADO, en gris neutro y sin verde de
     marca: presencia, no medida. La información la lleva el texto, que sigue entero. */
  .analysis-wait span{
    animation:none;transform:none;left:33%;width:34%;background:var(--line-strong);
  }
}
