/* app.css — vistas propias del archivo. Se apoya en heredado.css para el aire
   general (glass, colores) y define aquí la rejilla y el visor de caras. */

:root {
  --cara: #d99b1c;              /* ámbar: legible sobre foto clara y oscura */
  --tinta: #23282f;
  --tenue: rgba(35, 40, 47, .62);
  --borde: rgba(0, 0, 0, .14);
  --panel: rgba(255, 248, 242, .72);
}

/* El armazón heredado tiene dos temas y arranca en claro, como el portal de
   origen. Estos colores eran fijos de tema oscuro y en claro no se leían: el
   nombre del portal y el correo del usuario quedaban casi invisibles. */
:root[data-theme="dark"] {
  --cara: #f5c451;
  --tinta: #e8ecf4;
  --tenue: rgba(232, 236, 244, .58);
  --borde: rgba(255, 255, 255, .12);
  --panel: rgba(16, 20, 28, .72);
}

/* Ahora sí se usa el armazón de origen tal cual: .main es la que desplaza,
   no el documento. Antes se anulaba porque la rejilla era la página entera. */
html, body { height: 100%; overflow: hidden; }

/* El vidrio esmerilado del portal de origen se apoya en una FOTOGRAFÍA de
   fondo. Sin ella el efecto no existe y todo se ve plano: ese era el motivo de
   que no se pareciera al original. Aquí el fondo sale del propio archivo, con
   un desenfoque de 64 px para que sea textura y no una foto reconocible.
   public/img/fondo.jpg lo genera scripts/fondo.py. */
:root { --fondo: url("../img/steve.jpg"); }

/* El velo de body::before sí se mantiene sobre el fondo, pero NUNCA sobre la
   rejilla: allí apagaba las fotos al ~30 % de luminosidad. */
/* El fondo es textura, no ilustración: un velo por encima lo calma para que
   el contenido gane el primer plano, como en el portal de origen. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: var(--body-overlay-lt, rgba(240,220,200,.10)) !important;
}
[data-theme="dark"] body::before {
  background: var(--body-overlay,
    linear-gradient(160deg, rgba(10,10,20,.6), rgba(20,10,10,.45) 50%, rgba(10,15,25,.65))) !important;
}
/* Solo el apilado: NO tocar `position`. Poner aquí position:relative anulaba
   el position:fixed de .sidebar, que dejaba de ser columna y empujaba el
   contenido cientos de píxeles hacia abajo. */
.sidebar { z-index: 2; background: var(--sidebar-bg-lt, rgba(255,245,235,.65)) !important; }
[data-theme="dark"] .sidebar { background: var(--sidebar-bg, rgba(255,255,255,.08)) !important; }
.main { z-index: 1; }
/* Tipografia.
   En el portal de origen practicamente todo es Josefin Sans: Montserrat
   aparece en seis reglas contadas frente a mas de mil. Aquello se sostiene
   porque cada componente suyo la declara; lo que escribimos nosotros heredaba
   la Montserrat del body y por eso desentonaba. Se declara aqui de una vez
   para que lo propio salga igual que lo heredado. */
body {
  margin: 0; color: var(--tinta);
  font-family: 'Josefin Sans', sans-serif;
}
.ayuda, .casilla, .anio, .cuenta, .tema-btn, .muestra-btn,
.datos, .prosa, .ficha .vital, .ficha h2,
select, option, input, textarea, button {
  font-family: 'Josefin Sans', sans-serif;
}

/* Las casillas del original son nativas con accent-color, puesto EN LINEA en
   cada input: por eso heredar_css.py nunca las trajo —no estan en ninguna
   hoja de estilo— y aqui salian con el azul del sistema. Se pone global para
   que valga tambien para los controles que se añadan despues. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--orange); }

/* Color de resalte del sitio: ámbar. Es el mismo tono de los recuadros de
   cara, así que el portal entero va con el color del archivo; el naranja
   heredado se queda como una opción más del selector.

   Esta es la BASE, no un adorno: manda antes de que arranque el JavaScript, y
   por tanto es la que se ve en la pantalla de acceso y en el primer pintado.
   Mientras aquí hubo otro color, se entraba en azul y todo viraba a naranja al
   cargar el portal. Quien elija otro tono en su perfil lo sobreescribe en
   caliente (pintarAcento en app.js), pero el punto de partida de todo el sitio
   es este.

   Los cuatro derivados salen del mismo cálculo que hace app.js, para que no
   haya ni un salto de color entre lo que pinta el CSS y lo que pinta el JS:
   -dark = 22 % hacia negro, -claro = 32 % hacia blanco, -rgb los componentes
   (heredado.css los mete dentro de rgba()) y el tono para fondo oscuro se
   aclara un 34 %, o se apagaría.

   Los cuatro derivados salen del mismo cálculo que app.js, así que cambiar de
   tono aquí y en ACENTOS basta para mover todo el sitio. */
:root {
  --orange: #c5a46a; --orange-dark: #9a8053; --orange-claro: #d8c19a;
  --orange-light: rgba(197,164,106,.16);
  --orange-rgb: 197,164,106;
}
:root[data-theme="dark"] {
  --orange: #d9c39d; --orange-dark: #a9987a; --orange-claro: #e5d6bc;
  --orange-light: rgba(217,195,157,.2);
  --orange-rgb: 217,195,157;
}

/* El cristal de las ventanas, por defecto: opacidad "satén" con el teñido
   violeta ya compuesto. Van los cuatro en :root porque heredado.css elige
   entre la pareja clara y la oscura con sus propios selectores de tema.

   Están calculados con la misma mezcla de alfas que hace aplicarCristal() en
   app.js. Escribirlos aquí es lo que hace que el teñido valga desde el primer
   pintado: si solo estuvieran en el JavaScript, se entraría con el cristal sin
   teñir y cambiaría al cargar el portal, igual que pasaba con el acento. */
:root {
  --card-bg:       rgba(191,165,237,.234);
  --sidebar-bg:    rgba(195,172,238,.252);
  --card-bg-lt:    rgba(233,218,238,.683);
  --sidebar-bg-lt: rgba(237,221,233,.824);
}

/* El armazón heredado maqueta con .sidebar fija y .main desplazada. Aquí la
   página desplaza dentro de .main, no el documento. */
#app { display: none; }
#app.visible { display: block; }
.page.active { display: block; }

/* El retrato ocupa el sitio del logotipo. Va centrado y con el nombre debajo,
   como la marca del portal de origen: primero el emblema, luego el rotulo. */
.sidebar-logo { text-align: center; }
.retrato-eduardo {
  display: block; width: 104px; height: 104px; margin: 2px auto 10px;
  border-radius: 50%;
  /* El dibujo es sepia claro y sobre el cristal claro casi desaparecia: un
     anillo tenue y una sombra suave lo despegan del fondo sin enmarcarlo. */
  box-shadow: 0 0 0 1px rgba(120,90,65,.22), 0 6px 18px rgba(60,40,24,.22);
}
[data-theme="dark"] .retrato-eduardo {
  box-shadow: 0 0 0 1px rgba(255,255,255,.18), 0 6px 20px rgba(0,0,0,.45);
}
.marca { font-size: 1.05rem; font-weight: 700; letter-spacing: -.01em; }
.marca-grande { font-size: 1.6rem; font-weight: 700; letter-spacing: -.02em; }
/* "Sistema Eduardo" va en el oro del portal, en la barra y en la entrada: es
   el nombre del archivo y el único sitio donde el acento manda sobre el texto.
   Sigue al color de resalte elegido, así que cambia con él.
   Y con la misma sombra que el retrato que tiene encima —la caída del retrato
   es 0 6px 18px en la barra y 0 8px 24px en la entrada—, traducida a texto:
   algo más corta y algo más marcada, porque un trazo de letra es fino y la
   sombra de una foto redonda se le queda en nada. */
/* Un resplandor y no una sombra: la sombra caía hacia abajo y dejaba el nombre
   igual de plano, y en el tema claro el oro sobre el lavanda casi no se leía.
   El halo es del propio oro, así que sigue al color de resalte. */
.marca, .marca-grande {
  color: var(--orange);
  text-shadow: 0 0 10px rgba(var(--orange-rgb), .55), 0 0 26px rgba(var(--orange-rgb), .35);
}
/* En claro, además, el oro oscuro: el mismo color de resalte pero en su
   versión honda, que es la que se lee sobre un fondo claro. */
[data-theme="light"] .marca, [data-theme="light"] .marca-grande {
  color: var(--orange-dark);
  text-shadow: 0 0 12px rgba(var(--orange-rgb), .5), 0 1px 1px rgba(60,40,24,.25);
}
.login-card .marca-grande {
  text-shadow: 0 0 14px rgba(var(--orange-rgb), .6), 0 0 34px rgba(var(--orange-rgb), .35);
}
/* En el panel, "Sistema Eduardo" se queda en el mismo gris que "Archivo
   familiar" justo debajo: los dos dicen quién es el archivo, y uno gritando
   en oro sobre el otro apagado no aportaba nada. El oro se queda para la
   entrada, la única pantalla donde el nombre está solo y puede destacar. */
.sidebar-logo .marca { color: rgba(255,255,255,.5); text-shadow: none; }
[data-theme="light"] .sidebar-logo .marca { color: rgba(60,40,24,.6); text-shadow: none; }
/* Y el filete dorado que separa el nombre de la bajada, que la entrada ya
   tenía y al panel no había llegado. Más corto de margen que en la tarjeta
   de acceso: el panel es bastante más estrecho. */
.sidebar-logo .login-divider { margin: 10px auto 8px; }
/* La bajada —"Archivo familiar"— se queda en el gris del menú: en tinta plena
   pesaba más que el retrato que tiene encima. */
.sidebar-tag, .login-card .login-subtitle { color: rgba(255,255,255,.5); }
[data-theme="light"] .sidebar-tag,
[data-theme="light"] .login-card .login-subtitle { color: rgba(60,40,24,.6); }
/* El pie de la barra, con la anatomía del original: ficha de quien ha entrado
   (retrato, nombre y nivel) que lleva al perfil, el interruptor de tema al
   lado, y debajo sugerencia, fallo y salir. Los colores de los dos primeros
   son los suyos: azul para la sugerencia, morado para el fallo. */
.user-card { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.user-av {
  width: 36px; height: 36px; border-radius: 10px; flex-shrink: 0; overflow: hidden;
  background: linear-gradient(135deg, var(--orange), var(--orange-claro));
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 13px; color: #fff;
  font-family: 'Josefin Sans', sans-serif;
}
.user-av img { width: 100%; height: 100%; object-fit: cover; }
/* Quién ha entrado y con qué nivel, en el mismo gris que los rótulos de grupo
   del menú ("PRINCIPAL", "ARCHIVO"): es información de servicio, no un título.
   Los valores son los de .nav-group-label, para que sea el mismo gris y no uno
   parecido. */
.user-name {
  font-family: 'Josefin Sans', sans-serif; font-size: 13px; font-weight: 600;
  letter-spacing: .5px; color: rgba(255,255,255,.25);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.user-role {
  font-family: 'Josefin Sans', sans-serif; font-size: 11px;
  letter-spacing: 1px; color: rgba(255,255,255,.25);
}
[data-theme="light"] .user-name,
[data-theme="light"] .user-role { color: rgba(60,40,24,.85); }
.btn-sugerencia {
  background: rgba(90,130,220,.15) !important; border-color: rgba(90,130,220,.35) !important;
  color: rgba(70,110,200,.95) !important;
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
[data-theme="dark"] .btn-sugerencia { color: rgba(110,150,235,.9) !important; }
.btn-sugerencia:hover {
  background: #28a745 !important; border-color: #28a745 !important; color: #fff !important;
}
.btn-fallo {
  background: rgba(142,68,173,.15) !important; border-color: rgba(142,68,173,.4) !important;
  color: rgba(130,60,170,.95) !important;
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
[data-theme="dark"] .btn-fallo { color: rgba(180,100,220,.95) !important; }
.btn-fallo:hover {
  background: #8e44ad !important; border-color: #8e44ad !important; color: #fff !important;
}
.nav-item .ni { font-size: .95rem; width: 1.1rem; text-align: center; }
/* La sección abierta, en oro macizo y letra blanca. El portal de origen la
   deja en una pastilla tenue con el texto gris; aquí se ve de un golpe cuál
   es, que en un archivo con siete secciones se agradece. */
.nav-item.active,
[data-theme="light"] .nav-item.active {
  background: var(--orange); border-color: var(--orange); color: #fff;
}
.nav-item.active .ni { color: #fff; }

.login-ok { color: #9fd8a0; font-size: .84rem; margin-top: 10px; text-align: center; }

/* El aviso para móvil: mismo fondo y misma tarjeta que el acceso, encima de
   todo (z-index por encima de #login-screen, que va a 9999). Oculto salvo que
   el propio dispositivo se anuncie táctil, por si el user-agent no lo dice
   claro pero el puntero sí. */
.mobile-msg { text-align: center; color: var(--tenue); font-size: .92rem; line-height: 1.65; }
.mobile-msg strong { color: var(--tinta); }
#mobile-block {
  display: none; position: fixed; inset: 0; z-index: 10000;
  align-items: center; justify-content: center;
  background: var(--fondo) center / cover no-repeat;
}
#mobile-block.show { display: flex; }
@media (hover: none) and (pointer: coarse) { #mobile-block { display: flex; } }

/* La tarjeta de acceso, con las proporciones del portal de origen: el emblema
   arriba, el rótulo, el filete y la bajada. La suya es más alta que ancha
   porque el logotipo ocupa su sitio; aquí lo ocupa el retrato. */
/* El mismo halo cálido que el nombre de debajo, en vez del anillo genérico de
   antes: sobre un fondo oscuro un aro blanco se queda plano y un poco frío;
   el oro del archivo une el retrato con el rótulo en una sola pieza. */
.retrato-login {
  display: block; width: 128px; height: 128px; margin: 0 auto 18px;
  border-radius: 50%; object-fit: cover;
  box-shadow: 0 0 0 1px rgba(var(--orange-rgb), .45), 0 0 22px 4px rgba(var(--orange-rgb), .3),
              0 0 50px 14px rgba(var(--orange-rgb), .14), 0 8px 24px rgba(60,40,24,.28);
}
[data-theme="dark"] .retrato-login {
  box-shadow: 0 0 0 1px rgba(var(--orange-rgb), .5), 0 0 26px 6px rgba(var(--orange-rgb), .38),
              0 0 60px 18px rgba(var(--orange-rgb), .18), 0 8px 26px rgba(0,0,0,.5);
}
.login-card .marca-grande {
  text-align: center; font-size: 1.5rem; letter-spacing: -.01em; line-height: 1.15;
}
/* Bajada y filete con el aire del original: 16 px alrededor del filete y 24
   antes de los campos, no un separador de 28 px puesto a ojo. */
.login-card .login-subtitle { margin-bottom: 24px; color: var(--tenue); }
/* El filete, del mismo oro que el nombre que tiene encima: en claro el hondo y
   en oscuro el vivo, igual que `.marca-grande`. Iban de distinto tono y se
   notaba, porque están pegados el uno al otro. */
.login-card .login-divider { background: var(--orange); }
[data-theme="light"] .login-card .login-divider { background: var(--orange-dark); }
@media (prefers-color-scheme: light) {
  .login-card .login-divider { background: var(--orange-dark); }
}
.login-card .form-group label { color: var(--tenue); }
/* El enlace de recuperar, separado del botón como en el original: pegado
   parecía parte de él. */
.login-card .btn-forgot { margin-top: 18px; }
.enlace-tenue {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--tenue); font-size: .82rem; text-decoration: underline;
}

.cabecera-pagina {
  display: flex; align-items: center; gap: 1rem; margin-bottom: 1.1rem; flex-wrap: wrap;
}
.cabecera-pagina h2 { margin: 0; font-size: 1.35rem; letter-spacing: -.015em; }

.doble { display: grid; grid-template-columns: 1.35fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 900px) { .doble { grid-template-columns: 1fr; } }

.ayuda { color: var(--tenue); font-size: .84rem; margin: 0 0 1rem; }
.campos { display: grid; gap: .2rem; }
.botonera { display: flex; gap: .6rem; flex-wrap: wrap; margin-top: .5rem; }
.casilla {
  display: flex; align-items: center; gap: .55rem; padding: .3rem 0;
  font-size: .87rem; cursor: pointer; color: var(--tinta);
}
.casilla input { accent-color: var(--orange); width: 1rem; height: 1rem; }

.cifras { display: flex; gap: .6rem; flex-wrap: wrap; }
.cifra {
  padding: .6rem .9rem; border-radius: .6rem; text-align: center;
  background: rgba(255,255,255,.06); border: 1px solid var(--borde);
}
.cifra b { display: block; font-size: 1.25rem; font-variant-numeric: tabular-nums; }
.cifra span { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tenue); }

.lista-personas { display: grid; gap: 2px; max-height: 26rem; overflow-y: auto; }
.fila-persona {
  display: flex; align-items: center; gap: .7rem; width: 100%; text-align: left;
  padding: .45rem .6rem; border: 0; border-radius: .45rem; cursor: pointer;
  background: none; color: var(--tinta); font: inherit;
}
.fila-persona:hover { background: rgba(245,196,81,.14); }
.fila-persona img, .fila-persona .hueco-res {
  width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex: none;
  background: rgba(255,255,255,.07); border: 1px solid var(--borde);
}
.fila-persona .n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fila-persona .a { color: var(--tenue); font-size: .78rem; font-variant-numeric: tabular-nums; }

.campos-fila { display: grid; grid-template-columns: 1.4fr 1.2fr 1fr; gap: 0 1rem; }
@media (max-width: 820px) { .campos-fila { grid-template-columns: 1fr; } }
.campos-fila input, .campos-fila select, #m-persona, #r-buscar, #perfil-buscar {
  width: 100%; padding: .5rem .7rem; border-radius: .5rem;
  background: rgba(255,255,255,.5); border: 1px solid rgba(255,255,255,.6);
  color: var(--tinta); font: inherit;
}
[data-theme="dark"] .campos-fila input, [data-theme="dark"] .campos-fila select,
[data-theme="dark"] #m-persona, [data-theme="dark"] #r-buscar, [data-theme="dark"] #perfil-buscar {
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14);
}

.tabla-miembros { display: grid; gap: .3rem; }
.fila-miembro {
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  padding: .6rem .7rem; border-radius: .5rem; border: 1px solid var(--borde);
  background: rgba(255,255,255,.25);
}
[data-theme="dark"] .fila-miembro { background: rgba(255,255,255,.05); }
.fila-miembro .correo { flex: 1; min-width: 12rem; font-size: .88rem; }
.fila-miembro .correo small { display: block; color: var(--tenue); font-size: .74rem; }
.fila-miembro select { padding: .3rem .5rem; border-radius: .4rem; font: inherit; font-size: .82rem;
  background: rgba(255,255,255,.5); border: 1px solid var(--borde); color: var(--tinta); }
[data-theme="dark"] .fila-miembro select { background: rgba(255,255,255,.08); }
.fila-miembro .yo { font-size: .72rem; color: var(--cara); }
.pastilla {
  font-size: .7rem; padding: .1rem .45rem; border-radius: 999px;
  border: 1px solid rgba(200,60,60,.45); background: rgba(200,60,60,.14); color: #a33;
}
[data-theme="dark"] .pastilla { color: #f5a0a0; }

/* Álbumes en tarjetas con su foto de portada. En filas de texto, cien álbumes
   eran un muro ilegible; con una imagen cada uno se reconocen de un vistazo,
   que es como se busca en un archivo de fotografías.

   La tarjeta entera es una ventana de cristal y no solo la imagen: el rótulo
   suelto sobre el fondo quedaba ilegible: la rejilla arranca sobre la parte
   negra del degradado y en tema claro el texto es oscuro. Sobre el cristal se
   lee con cualquier fondo, y además la tarjeta obedece a los controles de
   opacidad y teñido como el resto del portal. */
/* Sin align-items:start: así todas las tarjetas de una fila miden lo mismo.
   Y el rótulo lleva altura fija —dos líneas de nombre y una de datos— para
   que también coincidan entre filas, que es donde más se notaba el desnivel. */
.lista-albumes {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
  gap: 1rem;
}
.tarjeta-album {
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  border-radius: .8rem; border: 1px solid var(--borde); cursor: pointer;
  background: var(--card-bg-lt, rgba(255,248,242,.64));
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow: 0 4px 14px rgba(0,0,0,.16);
  color: var(--tinta); font: inherit; text-align: left;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
[data-theme="dark"] .tarjeta-album { background: var(--card-bg, rgba(255,255,255,.13)); }
.tarjeta-album:hover {
  transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0,0,0,.26);
  border-color: var(--orange);
}
.tarjeta-album .cubierta {
  display: block; aspect-ratio: 1; background: center/cover no-repeat;
  background-color: rgba(120,90,65,.12);
}
[data-theme="dark"] .tarjeta-album .cubierta { background-color: rgba(255,255,255,.08); }
.tarjeta-album .rotulo { display: block; min-width: 0; padding: .6rem .7rem .7rem; }
.tarjeta-album .nombre {
  font-family: 'Josefin Sans', sans-serif; font-size: .86rem;
  font-weight: 600; line-height: 1.25; color: var(--tinta);
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  min-height: 2.5em;             /* dos líneas siempre, aunque el nombre sea corto */
}
.tarjeta-album .cuantas {
  display: block; font-size: .72rem; color: var(--tenue); margin-top: .15rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────────────────── documentación */

/* Tarjetas más anchas que las de álbum y con la portada apaisada: lo que
   identifica una carta no es la imagen entera sino su cabecera —la fecha y el
   encabezamiento—, que está siempre arriba del papel. Por eso la portada se
   encuadra por arriba (background-position) en vez de centrada. */
.rejilla-docs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(14.5rem, 1fr));
  gap: 1rem; padding-bottom: 2rem;
}
.tarjeta-doc {
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  border-radius: .8rem; border: 1px solid var(--borde); cursor: pointer;
  background: var(--card-bg-lt, rgba(255,248,242,.64));
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow: 0 4px 14px rgba(0,0,0,.16);
  color: var(--tinta); font: inherit; text-align: left;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
[data-theme="dark"] .tarjeta-doc { background: var(--card-bg, rgba(255,255,255,.13)); }
.tarjeta-doc:hover {
  transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0,0,0,.26);
  border-color: var(--orange);
}
.tarjeta-doc .portada {
  position: relative; display: block; aspect-ratio: 3 / 2;
  background: top center/cover no-repeat rgba(120,90,65,.12);
}
[data-theme="dark"] .tarjeta-doc .portada { background-color: rgba(255,255,255,.08); }
/* Cuántos pliegos tiene, sobre la propia portada: en una carta de ocho
   páginas es el primer dato que se quiere saber antes de abrirla. */
.tarjeta-doc .hojas {
  position: absolute; right: .4rem; bottom: .4rem;
  padding: .1rem .35rem; border-radius: .25rem; font-size: .68rem;
  background: rgba(0,0,0,.62); color: var(--cara);
  font-variant-numeric: tabular-nums;
}
.tarjeta-doc .rotulo {
  display: flex; flex-direction: column; gap: .1rem;
  min-width: 0; padding: .6rem .7rem .7rem;
}
.tarjeta-doc .nombre {
  font-family: 'Josefin Sans', sans-serif; font-size: .9rem;
  font-weight: 600; line-height: 1.25; color: var(--tinta);
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.tarjeta-doc .corresponsales {
  font-size: .78rem; color: var(--tinta); opacity: .9;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tarjeta-doc .cuantas {
  display: block; font-size: .72rem; color: var(--tenue); margin-top: .15rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* La cita del texto encontrado. En cursiva y con comillas para que se lea como
   lo que es —un trozo de la carta— y no como un campo más de la ficha. */
.tarjeta-doc .cita {
  margin-top: .35rem; font-size: .74rem; line-height: 1.35;
  color: var(--tenue); font-style: italic;
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.tarjeta-doc .cita::before { content: "«"; }
.tarjeta-doc .cita::after  { content: "»"; }
.tarjeta-doc .sello {
  align-self: flex-start; margin-top: .35rem;
  padding: .1rem .4rem; border-radius: .3rem;
  font-size: .66rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--tenue); border: 1px dashed var(--borde);
}
.tarjeta-doc mark {
  background: rgba(var(--orange-rgb), .38); color: inherit;
  border-radius: .15rem; padding: 0 .1em;
}
/* Sin transcribir todavía: la tarjeta se ve, pero apagada. Así se distingue de
   un vistazo lo que ya se puede leer de lo que aún es solo un escaneo. */
.tarjeta-doc.sin-transcribir .portada { filter: grayscale(.55) contrast(.95); opacity: .82; }
.tarjeta-doc.sin-transcribir .nombre { font-weight: 500; color: var(--tenue); }

/* Los cuatro filtros y el botón, y la barra debajo, como en la página de
   fotos. El desplegable de allí no existe aquí: se filtra la propia rejilla. */
#page-documentos .filtros-fichas .buscador { margin-top: 12px; }
/* Cuatro desplegables y el botón: los cinco a lo ancho, como en Fichas. */

/* Qué es el papel y, si el escaneo llevaba varias piezas, cuál de ellas. */
.tarjeta-doc .insignia-tipo, .tarjeta-doc .insignia-pieza {
  position: absolute; top: .4rem; padding: .1rem .35rem;
  border-radius: .25rem; font-size: .62rem; letter-spacing: .04em;
  text-transform: uppercase;
  background: rgba(0,0,0,.6); color: var(--cara);
}
.tarjeta-doc .insignia-tipo { left: .4rem; }
.tarjeta-doc .insignia-pieza { right: .4rem; background: rgba(0,0,0,.45); color: #fff; }

/* ───────────────────────────────────────────────────── sección de personas */

/* Tarjetas más estrechas que las de documento y con la cara cuadrada arriba:
   aquí lo que identifica no es una fecha, es una cara. */
.rejilla-personas {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr));
  gap: 1rem; padding-bottom: 1rem;
}
.tarjeta-persona {
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  border-radius: .8rem; border: 1px solid var(--borde); cursor: pointer;
  background: var(--card-bg-lt, rgba(255,248,242,.64));
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow: 0 4px 14px rgba(0,0,0,.16);
  color: var(--tinta); font: inherit; text-align: left;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
[data-theme="dark"] .tarjeta-persona { background: var(--card-bg, rgba(255,255,255,.13)); }
.tarjeta-persona:hover {
  transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0,0,0,.26);
  border-color: var(--orange);
}
/* El retrato se encuadra por ARRIBA: en un retrato antiguo la cara está en el
   tercio superior, y centrar el recorte le corta la frente. */
.tarjeta-persona .cara-grande {
  display: grid; place-items: center; aspect-ratio: 1;
  background: top center/cover no-repeat rgba(120,90,65,.12);
  font-family: 'Josefin Sans', sans-serif; font-size: 2rem; font-weight: 300;
  color: var(--tenue);
}
[data-theme="dark"] .tarjeta-persona .cara-grande { background-color: rgba(255,255,255,.08); }
.tarjeta-persona .rotulo {
  display: flex; flex-direction: column; gap: .05rem;
  min-width: 0; padding: .55rem .65rem .65rem;
}
.tarjeta-persona .nombre {
  font-family: 'Josefin Sans', sans-serif; font-size: .84rem;
  font-weight: 600; line-height: 1.25; color: var(--tinta);
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  min-height: 2.1em;
}
.tarjeta-persona .cuantas {
  display: block; font-size: .71rem; color: var(--tenue);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.tarjeta-persona .cuantas.fotos { color: var(--orange-dark); }
[data-theme="dark"] .tarjeta-persona .cuantas.fotos { color: var(--orange); }
/* Mismo aspect-ratio que una tarjeta con foto (heredado, 1:1): con un 16:9
   la zona de texto no arrancaba a la misma altura que en el resto de la
   rejilla, y las filas quedaban descuadradas entre tarjetas con y sin cara. */
.tarjeta-persona.sin-retrato .cara-grande { font-size: 1.5rem; }

/* Sus fotos, a la izquierda de la ficha. Cuadradas y apretadas: es un
   contacto de laboratorio, no una galería. */
/* Columnas Y FILAS de medida fija, las dos de 8rem. Con columnas flexibles
   (1fr) el navegador calculaba el alto de fila por el contenido —que aquí es
   una imagen en posición absoluta, o sea nada—, daba filas de 7 px y las
   celdas, cuadradas por aspect-ratio, se montaban unas sobre otras. Con la
   fila declarada no hay nada que calcular y el cuadrado sale solo. */
/* La misma rejilla que la sección de Fotografías: columnas que se estiran hasta
   llenar el ancho, hueco de 3 px y celdas cuadradas. Antes eran columnas de 8rem
   fijas repartidas con `space-between`, y eso dejaba calles desiguales entre
   fotos según cuántas cupieran. Ya que la de Fotografías está resuelta, se usa
   esa y no dos maneras distintas de enseñar lo mismo. */
.lector-fotos {
  overflow: auto; padding: .8rem; min-width: 0;
  border-right: 1px solid var(--borde);
  background: rgba(0,0,0,.05);
  /* El panel que la contiene tiene backdrop-filter a pantalla entera: sin
     aislar esta zona, desplazarla obligaba al navegador a recalcular ESE
     desenfoque en cada fotograma del scroll, aunque el desenfoque en sí no
     dependiera de lo que hay aquí dentro. contain:paint le dice que lo de
     aquí no pinta nada fuera de su caja, y el scroll deja de arrastrar al
     resto del panel. */
  contain: paint;
  display: grid; gap: 3px;
  /* Columna y fila DECLARADAS, las dos. Es lo único que aguanta aquí.
     Con columnas elásticas la celda tiene que sacar su alto del ancho, y el
     ancho de la columna, y la fila del alto de la celda: el navegador se queda
     sin por dónde empezar y las apila —360 fotos en siete filas, unas encima de
     otras, y la columna sin nada que desplazar—. Da igual hacer el cuadrado con
     `aspect-ratio` o con relleno: el problema no es el cuadrado, es que nadie
     sabe cuánto mide la fila.
     Lo que sí se arregla de la versión anterior es el reparto: iba con
     `space-between`, que abría calles distintas según cuántas cupieran. */
  grid-template-columns: repeat(auto-fill, 8rem);
  grid-auto-rows: 8rem;
  align-content: start; justify-content: start;
}
[data-theme="dark"] .lector-fotos { background: rgba(0,0,0,.22); }
/* La celda llena su hueco de la rejilla, que ya es cuadrado porque la fila viene
   declarada. Nada de aspect-ratio ni de rellenos: aquí el cuadrado no es el
   problema —ver el comentario de .lector-fotos—. */
.foto-persona {
  position: relative; width: 100%; height: 100%; overflow: hidden; padding: 0;
  border: 0; border-radius: 2px; cursor: pointer;
  background: rgba(0,0,0,.06);
}
.foto-persona img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
  opacity: 0; transition: opacity .25s;
}
.foto-persona img[src] { opacity: 1; }
.foto-persona:hover img { transform: scale(1.03); transition: transform .3s, opacity .25s; }
.lector-fotos .sin-fotos, .lector-fotos .fin-fotos-persona { grid-column: 1 / -1; }
/* Con alto, no como una raya de cero. Es la marca que avisa de que hace falta
   otra tanda de fotos, y un elemento sin superficie no siempre cuenta como
   visible: el aviso no llegaba y la rejilla se quedaba en la primera tanda. */
.lector-fotos .fin-fotos-persona { height: 12px; }
.lector-fotos .sin-fotos { color: var(--tenue); padding: 1rem; }

/* La ficha prestada, dentro del panel derecho: aquí no lleva el marco ni el
   fondo que tiene en la sección de Fichas, que ya los pone el lector. */
#lp-datos .ficha { border: 0; background: none; box-shadow: none; padding: 0; margin: 0; }
/* El árbol, dentro de la columna. Antes se desplazaba en horizontal y dejaba
   su barra colgando debajo; ahora son los nodos los que ceden: cada generación
   es una fila flexible, así que una de cinco hermanos se estrecha hasta caber
   y una de dos se queda a su tamaño. Encogerlos es de layout, no un zoom, de
   modo que los hilos se trazan sobre las posiciones de verdad. */
#lp-datos .arbol { margin-top: 1.2rem; }
/* justify-items:stretch y no el center heredado: si cada generación se mide
   por su contenido nunca sobra ancho que ceder, y los nodos no se encogen
   jamás. Estirada la fila al ancho de la columna, los de dentro sí ceden; y
   `justify-content:center` de .gen mantiene centrada la que no lo necesita. */
#lp-datos .generaciones {
  min-width: 0; justify-items: stretch;
  /* minmax(0,1fr) y no la columna automática: una columna auto se mide por su
     contenido —los 590 px de la fila de cuatro— y entonces no hay ancho que
     ceder por mucho que los nodos sepan encogerse. */
  grid-template-columns: minmax(0, 1fr);
}
#lp-datos .gen .nodo { flex: 0 1 8.4rem; min-width: 5.2rem; }

@media (max-width: 900px) {
  .lector-fotos { border-right: 0; border-bottom: 1px solid var(--borde); }
}

/* ─────────────────────────────── lo que las cartas dicen de una persona */

.correspondencia { margin-top: 1.2rem; }
.correspondencia .ayuda { margin: .6rem 0 .4rem; }
.correspondencia .lector-chips { margin-bottom: .2rem; }
/* La nota del archivo no es un campo más: es prosa, y se lee como tal. El
   filete al margen dice de dónde sale —de haber leído los papeles— y la
   separa de lo que venía en la ficha de GDS. */
.nota-archivo {
  margin: .5rem 0 0; padding: .6rem .9rem;
  border-left: 2px solid var(--orange);
  background: rgba(var(--orange-rgb), .07);
  border-radius: 0 .4rem .4rem 0;
  font-size: .88rem; line-height: 1.6; color: var(--tinta);
}
.cartas-persona { display: flex; flex-direction: column; gap: .3rem; }
.carta-persona {
  display: flex; flex-direction: column; gap: .1rem; text-align: left;
  padding: .45rem .7rem; border-radius: .5rem;
  border: 1px solid var(--borde); background: transparent;
  color: var(--tinta); font: inherit; cursor: pointer;
}
.carta-persona:hover { border-color: var(--orange); background: rgba(var(--orange-rgb), .07); }
.carta-persona .cuando { font-size: .85rem; font-weight: 600; }
.carta-persona .papel { font-size: .75rem; color: var(--tenue); }
.carta-persona.ver-todas {
  margin-top: .3rem; text-align: center; font-size: .8rem; color: var(--tenue);
  border-style: dashed;
}

/* ───────────────────────────────────────────── lector de cartas */

/* Dos columnas que se desplazan por separado: el escaneo y lo que dice. Una
   carta manuscrita no se lee de corrido, se coteja, y para cotejar hay que
   poder mover el papel sin perder el renglón del texto (y al revés). */
/* El cristal (backdrop-filter) se había quitado pensando que en un panel a
   pantalla entera no había nada detrás que mereciera la pena transparentar
   —y de hecho no lo había: el intento de traerlo de vuelta le puso encima un
   degradado propio como "respaldo por si el desenfoque fallaba", pero al
   estar en el fondo de la MISMA capa, ese respaldo opaco tapaba el desenfoque
   siempre, funcionara o no. El resultado se veía plano pasara lo que pasara.
   La capa que sí tiene algo que transparentar es el fondo de pantalla
   elegido (--fondo, fijo detrás de toda la página): con solo el color
   translúcido de siempre (--panel) y el cristal encima, es ese fondo el que
   se ve desenfocado, igual que en las tarjetas de álbum y documentación. */
.lector {
  position: fixed; inset: 0; z-index: 120;
  display: none; flex-direction: column;
  background: var(--panel);
  backdrop-filter: blur(28px) saturate(150%);
  -webkit-backdrop-filter: blur(28px) saturate(150%);
}
.lector.abierto { display: flex; }
.lector-barra {
  display: flex; align-items: center; gap: 1rem;
  padding: .7rem 1.1rem; border-bottom: 1px solid var(--borde);
  background: var(--panel);
}
.lector-que { flex: 1; min-width: 0; }
.lector-titulo {
  margin: 0; font-family: 'Josefin Sans', sans-serif;
  font-size: 1.02rem; font-weight: 650; letter-spacing: -.01em; color: var(--tinta);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lector-sub {
  margin: .1rem 0 0; font-size: .78rem; color: var(--tenue);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lector-mandos { display: flex; gap: .35rem; }
.lector-mandos button {
  min-width: 2.1rem; height: 2.1rem; padding: 0 .55rem;
  border-radius: .5rem; border: 1px solid var(--borde);
  background: transparent; color: var(--tinta);
  font: inherit; font-size: 1rem; line-height: 1; cursor: pointer;
}
#lector-pdf { font-size: .72rem; letter-spacing: .06em; }
/* Los mismos iconos de trazo del visor de fotos, no caracteres sueltos: aquí
   conviven con botones de texto (Fotos, PDF), así que solo los que llevan
   SVG se centran como una rejilla —los de texto siguen con su padding. */
.lector-mandos button:has(svg) { display: grid; place-items: center; padding: 0; }
/* El mismo fallo de siempre: display:grid pisa el display:none de [hidden]. */
.lector-mandos button[hidden] { display: none; }
.lector-mandos button:hover:not(:disabled) { border-color: var(--orange); color: var(--orange); }
.lector-mandos button:disabled { opacity: .35; cursor: default; }
.lector-mandos button[aria-pressed="true"] { border-color: var(--orange); color: var(--orange); }

/* La fila, acotada. Sin declararla, la fila implícita mide `auto` y crece con su
   contenido: las dos columnas dejaban de desplazarse por separado —que es para
   lo que están hechas— y se estiraban hasta caber enteras, empujando el cuerpo
   del lector por debajo de la pantalla.
   No era solo cosa de aspecto. La rejilla de fotos de una ficha pide la
   siguiente tanda cuando el final se acerca al borde de SU zona; si esa zona no
   se desplaza nunca, el aviso salta una vez y no vuelve. Las fotos de Lydia
   —1.834— se quedaban en 240 y parecía que su vida se acababa en 1969. */
.lector-cuerpo {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
}
.lector-hojas {
  overflow: auto; padding: 1.2rem; min-width: 0;
  border-right: 1px solid var(--borde);
  background: rgba(0,0,0,.05);
  display: flex; flex-direction: column; align-items: center; gap: 1.2rem;
  contain: paint;    /* mismo motivo que .lector-fotos */
}
[data-theme="dark"] .lector-hojas { background: rgba(0,0,0,.22); }
.hoja { margin: 0; width: 100%; max-width: 46rem; }
/* height:auto con el width/height del catálogo puestos en el atributo: el
   navegador reserva el hueco por la proporción antes de bajar la imagen. */
.hoja img {
  display: block; width: 100%; height: auto;
  border-radius: .35rem; background: rgba(255,255,255,.55);
  box-shadow: 0 6px 20px rgba(0,0,0,.28);
}
.hoja figcaption {
  margin-top: .3rem; text-align: center;
  font-size: .7rem; color: var(--tenue); font-variant-numeric: tabular-nums;
}
/* Con la letra apretada, 46 rem no bastan: las hojas se sueltan del ancho de
   la columna y la columna se desplaza también en horizontal. */
.lector.hojas-grandes .lector-hojas { align-items: flex-start; }
.lector.hojas-grandes .hoja { max-width: none; width: 175%; }

.lector-texto {
  overflow: auto; padding: 1.3rem 1.6rem 4rem; min-width: 0;
  color: var(--tinta); font-size: .92rem; line-height: 1.62;
  contain: paint;    /* mismo motivo que .lector-fotos */
}
.lector-texto p { margin: 0 0 .9rem; }
.lector-rotulo {
  margin: 1.6rem 0 .6rem; font-family: 'Josefin Sans', sans-serif;
  font-size: .74rem; font-weight: 650; letter-spacing: .14em; text-transform: uppercase;
  color: var(--tenue);
}
.lector-texto h4.lector-rotulo { letter-spacing: .08em; text-transform: none; font-size: .84rem; }
.lector-texto > .lector-rotulo:first-child { margin-top: 0; }
/* Las citas son el texto ORIGINAL del papel (sobrescritos, sellos, lo que
   está escrito de otra mano): filete al margen y fondo tenue, para que no se
   confundan con lo que se explica sobre ellas. */
.lector-texto blockquote {
  margin: 0 0 1rem; padding: .6rem .9rem;
  border-left: 2px solid var(--orange);
  background: rgba(var(--orange-rgb), .09);
  border-radius: 0 .4rem .4rem 0;
}
.lector-texto blockquote p:last-child { margin-bottom: 0; }
.lector-texto ul { margin: 0 0 1rem; padding-left: 1.1rem; }
.lector-texto li { margin-bottom: .35rem; }
.lector-texto strong { font-weight: 650; }
.lector-texto em { color: var(--tenue); }

.lector-texto mark {
  background: rgba(var(--orange-rgb), .42); color: inherit;
  border-radius: .15rem; padding: 0 .1em;
}
/* La cabecera de correo: la cara de quien escribe y la de quien recibe. Es lo
   primero que se mira de una carta, y una cara se reconoce antes que un nombre
   de tres apellidos. */
.lector-corresponsales {
  display: flex; flex-direction: column; gap: .5rem;
  margin-bottom: 1rem; padding-bottom: .9rem;
  border-bottom: 1px solid var(--borde);
}
.corresponsal { display: flex; align-items: center; gap: .6rem; min-width: 0; }
/* "caras" a secas ya son los recuadros de cara del visor de fotos, que van
   en posición absoluta sobre la imagen: heredarlo dejaba el avatar fuera de
   flujo y el nombre se le montaba encima. */
.corresponsal .caras-corresponsal { display: flex; flex-shrink: 0; }
/* Cuando escriben dos, las caras se solapan como las fichas de un grupo: dice
   "estos dos juntos" y no ocupa el doble. */
.corresponsal .caras-corresponsal > * + * { margin-left: -.5rem; }
.cara-corresponsal {
  width: 2rem; height: 2rem; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center; padding: 0;
  border: 1px solid var(--borde); background: rgba(var(--orange-rgb), .18);
  color: var(--tinta); font: inherit; font-size: .8rem; font-weight: 600;
  cursor: pointer; flex-shrink: 0;
}
.cara-corresponsal img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cara-corresponsal.sin-ficha { cursor: default; background: transparent; color: var(--tenue); }
.cara-corresponsal:not(.sin-ficha):hover { border-color: var(--orange); }
.corresponsal .quien { min-width: 0; display: flex; flex-direction: column; }
.rotulo-corresponsal {
  font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--tenue);
}
.nombre-corresponsal { font-size: .86rem; color: var(--tinta); line-height: 1.3; }

.lector-ficha {
  display: grid; grid-template-columns: auto 1fr; gap: .25rem .8rem;
  margin: 0 0 1rem; font-size: .82rem;
}
.lector-ficha dt { color: var(--tenue); }
.lector-ficha dd { margin: 0; }
/* Las señas del sobrescrito, debajo de la ciudad y más calladas: son el dato
   fino, no el titular. */
.lector-ficha .senas { display: block; color: var(--tenue); font-size: .76rem; }
.lector-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.lector-chips .chip {
  padding: .18rem .5rem; border-radius: .4rem; font-size: .76rem;
  border: 1px solid var(--borde); color: var(--tenue);
  background: transparent; font-family: inherit;
}
.lector-chips .chip.con-ficha { color: var(--tinta); cursor: pointer; }
.lector-chips .chip.con-ficha:hover { border-color: var(--orange); color: var(--orange); }
.lector-pendiente {
  margin-top: 1.4rem; padding: .8rem .9rem; border-radius: .5rem;
  border: 1px dashed var(--borde); color: var(--tenue); font-size: .85rem;
}
/* La explicación del TIPO de papel, no de esta pieza: fondo tenue para que no
   se confunda con una transcripción real cuando algún día la haya. */
.lector-contexto {
  margin-top: 1rem; padding: .8rem .9rem; border-radius: .5rem;
  background: rgba(var(--orange-rgb), .07); border: 1px solid var(--borde);
  color: var(--tinta); font-size: .85rem; line-height: 1.58;
}
.lector-contexto p { margin: 0 0 .6rem; }
.lector-contexto p:last-child { margin-bottom: 0; }

/* En pantalla estrecha no caben dos columnas: primero las hojas, debajo el
   texto, y desplaza el conjunto. Partir la pantalla en dos mitades de 20 rem
   no serviría para cotejar nada. */
@media (max-width: 900px) {
  .lector-cuerpo { display: flex; flex-direction: column; overflow: auto; }
  /* La rejilla de fotos también: se quedaba con `overflow: auto` y sin alto que
     la acotara, así que fingía desplazarse y en realidad crecía. */
  .lector-hojas, .lector-texto, .lector-fotos { overflow: visible; flex: 0 0 auto; }
  /* En la ficha de una persona, apiladas al revés: primero quién es y luego sus
     fotos. Puestas arriba, para leer la ficha había que pasar por delante de
     todas —y de alguno hay mil ochocientas—. Y de regalo, la rejilla pasa a
     acabar donde acaba el panel, que es justo lo que necesita para saber cuándo
     pedir la siguiente tanda. */
  #lector-persona .lector-texto { order: 1; }
  #lector-persona .lector-fotos {
    order: 2; border-right: 0; border-top: 1px solid var(--borde);
  }
  .lector-hojas { border-right: 0; border-bottom: 1px solid var(--borde); }
  .lector.hojas-grandes .hoja { width: 100%; }
}

/* ───────────────────────────────────────────── buscador de la página fotos */

.barra {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 1rem;
  padding: .7rem 1.1rem;
  background: var(--panel);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid var(--borde);
}
.barra h1 { font-size: 1rem; margin: 0; font-weight: 650; letter-spacing: -.01em; }
.barra .cuenta { font-size: .8rem; color: var(--tenue); font-variant-numeric: tabular-nums; }
.barra .hueco { flex: 1; }
/* El buscador vivía en la barra superior, que ya no existe: ahora está en la
   cabecera de la página de fotografías y necesita heredar el mismo estilo. */
.barra input[type="search"], .cabecera-pagina input[type="search"] {
  width: min(24rem, 38vw); padding: .45rem .75rem;
  border-radius: .5rem; border: 1px solid var(--borde);
  background: rgba(255, 255, 255, .06); color: var(--tinta); font-size: .87rem;
}
.barra input[type="search"]::placeholder,
.cabecera-pagina input[type="search"]::placeholder { color: rgba(232, 236, 244, .4); }

.buscador { position: relative; }
.resultados {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 50;
  width: min(30rem, 90vw); max-height: 70vh; overflow-y: auto;
  border-radius: .6rem; padding: .35rem;
  background: var(--panel); border: 1px solid var(--borde);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, .5);
}
.resultados[hidden] { display: none; }
.resultados h4 {
  margin: .5rem .5rem .25rem; font-size: .66rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--tenue); font-weight: 600;
}
.res {
  display: flex; align-items: center; gap: .6rem; width: 100%;
  padding: .4rem .5rem; border: 0; border-radius: .4rem; cursor: pointer;
  background: none; color: var(--tinta); font: inherit; text-align: left;
}
.res:hover, .res.activo { background: rgba(245, 196, 81, .16); }
.res img, .res .hueco-res {
  width: 32px; height: 32px; border-radius: .25rem; object-fit: cover; flex: none;
  background: rgba(255, 255, 255, .07);
}
.res .txt { flex: 1; min-width: 0; }
.res .t1 { display: block; font-size: .85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.res .t2 { display: block; font-size: .72rem; color: var(--tenue); }
.res mark { background: none; color: var(--cara); font-weight: 600; }
.resultados .nada { padding: .8rem .6rem; color: var(--tenue); font-size: .85rem; }

.filtro {
  display: none; align-items: center; gap: .5rem;
  padding: .3rem .3rem .3rem .7rem; border-radius: 999px;
  background: rgba(245, 196, 81, .16); border: 1px solid rgba(245, 196, 81, .38);
  font-size: .83rem; white-space: nowrap;
}
.filtro.visible { display: inline-flex; }
.filtro button {
  border: 0; background: rgba(0, 0, 0, .3); color: var(--tinta);
  width: 1.3rem; height: 1.3rem; border-radius: 50%; cursor: pointer; line-height: 1;
}

/* ─────────────────────────────────────────── paneles del armazón heredado

   heredado.css NO da fondo a `.card`: el vidrio esmerilado lo aplica con
   reglas atadas a `#page-dashboard`. Como aquí las páginas se llaman de otra
   forma, las tarjetas salían transparentes sobre la foto de fondo y el panel
   de búsqueda parecía flotar en el aire. Se replica el mismo tratamiento. */

/* El fondo sale de una variable para que el control de "opacidad de ventanas"
   pueda moverlo, igual que en el portal de origen. */
/* Los paneles esmerilados del portal.
   .ficha y .arbol NO están en la lista, aunque lo estuvieron: viven dentro del
   acordeón de una persona y allí no son paneles, son su contenido. Mientras lo
   estuvieron traían fondo, desenfoque Y una sombra con línea blanca interior,
   y por eso dibujaban un bloque más claro dentro de la ficha. Taparlo
   propiedad a propiedad no valía: cada vez asomaba la siguiente. */
.card, .welcome-strip, .adash-card, .proj-stat {
  background: var(--card-bg-lt, rgba(255, 248, 242, .42));
  border: 1px solid rgba(255, 255, 255, .35);
  backdrop-filter: blur(40px) saturate(180%);
  -webkit-backdrop-filter: blur(40px) saturate(180%);
  box-shadow: 0 4px 24px rgba(0, 0, 0, .1), inset 0 1px 0 rgba(255, 255, 255, .45);
}
[data-theme="dark"] .card,
[data-theme="dark"] .welcome-strip,
[data-theme="dark"] .adash-card,
[data-theme="dark"] .proj-stat {
  background: var(--card-bg, rgba(255, 255, 255, .07));
  border-color: rgba(255, 255, 255, .14);
  box-shadow: 0 4px 24px rgba(0, 0, 0, .3), inset 0 1px 0 rgba(255, 255, 255, .08);
}

/* Los campos y botones también necesitan cuerpo sobre el vidrio. */
.cabecera-pagina input[type="search"], .campos input {
  background: rgba(255, 255, 255, .5); border: 1px solid rgba(255, 255, 255, .6);
  color: var(--tinta);
}
[data-theme="dark"] .cabecera-pagina input[type="search"],
[data-theme="dark"] .campos input {
  background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .14);
}

/* Las etiquetas de formulario del armazón son blancas: en tema claro
   desaparecían sobre el vidrio. */
.form-group label {
  color: var(--tenue) !important; font-family: 'Josefin Sans', sans-serif;
  font-size: 11px; letter-spacing: 2px; text-transform: uppercase;
}
.campos input::placeholder { color: rgba(35,40,47,.38); }
[data-theme="dark"] .campos input::placeholder { color: rgba(232,236,244,.35); }

/* Tipografía del portal de origen. Esta regla ponía Montserrat en el cuerpo,
   las filas de persona, las casillas y las ayudas, y al ir despues ganaba a
   la declaracion de mas arriba. Ahora va todo en Josefin Sans, como alli. */
.marca, .marca-grande, .cabecera-pagina h2, .ws-greeting, .ficha h2,
.cifra b, .nodo .n { font-family: 'Josefin Sans', sans-serif; }




/* La tarjeta del acceso, teñida como las ventanas del portal.

   No usa --card-bg: esa la mueve el control de opacidad, y aquí la tarjeta es
   más opaca a propósito —tiene que leerse sobre la fotografía y quien está
   entrando todavía no ha elegido nada—. Lo que se hace es componer el mismo
   violeta sobre SUS opacidades, para que el acceso y el portal se vean de la
   misma familia sin adelgazar el cristal justo donde hace falta que sea denso.

   OJO: son valores calculados a mano. Si cambia el teñido por defecto (COLORES
   en app.js), hay que recalcularlos con la misma mezcla de alfas. Que el
   oscuro coincida con el del satén es casualidad: la tarjeta ya venía al 0,13
   de origen, la misma opacidad que ese nivel. */
.login-card {
  background: rgba(191,165,237,.234) !important;
  border-color: rgba(214,198,244,.366) !important;
}
[data-theme="light"] .login-card,
:root:not([data-theme="dark"]) .login-card {
  background: rgba(233,216,232,.692) !important;
  border-color: rgba(228,217,247,.560) !important;
}

/* ─────────────────────────────────── bordes discontinuos
   NORMA DE LA CASA: un borde discontinuo NUNCA se hace con `border: dashed`.
   Se dibuja con un SVG en background-image.

   El motivo es de rasterizado: sobre una tarjeta con backdrop-filter y las
   esquinas redondeadas, Chrome pinta el `dashed` nativo con los trazos
   desiguales y las esquinas rotas —eso es el reborde feo—, y ademas parpadea
   al desplazar. El SVG lo dibuja el mismo motor que el resto del vector, sale
   parejo y se comporta igual en Chrome, Safari y Firefox.

   Ojo: dentro de una URL de datos NO se pueden usar variables CSS, asi que el
   color va escrito en el propio SVG y hace falta una version por tema. Por eso
   conviene tenerlo aqui una sola vez y no repetirlo por componente. Si hay que
   añadir otro elemento con borde discontinuo, se le pone la clase
   .borde-punteado en vez de escribir `dashed`. */
.proj-acc-item, .borde-punteado {
  border: none !important;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3e%3crect x='0.75' y='0.75' width='calc(100%25 - 1.5px)' height='calc(100%25 - 1.5px)' fill='none' rx='11.25' ry='11.25' stroke='%23ffffff' stroke-opacity='.18' stroke-width='1.5' stroke-dasharray='5 3.5' stroke-linecap='round'/%3e%3c/svg%3e") !important;
  background-size: 100% 100% !important;
  background-repeat: no-repeat !important;
  background-position: 0 0 !important;
}
[data-theme="light"] .proj-acc-item, [data-theme="light"] .borde-punteado {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3e%3crect x='0.75' y='0.75' width='calc(100%25 - 1.5px)' height='calc(100%25 - 1.5px)' fill='none' rx='11.25' ry='11.25' stroke='%23785a41' stroke-opacity='.45' stroke-width='1.5' stroke-dasharray='5 3.5' stroke-linecap='round'/%3e%3c/svg%3e") !important;
  background-color: rgba(255,248,242,.45) !important;
}

/* La de subir fondo tambien era `dashed`: mismo tratamiento, con su radio. */
.subir {
  border: none !important;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3e%3crect x='0.75' y='0.75' width='calc(100%25 - 1.5px)' height='calc(100%25 - 1.5px)' fill='none' rx='11.25' ry='11.25' stroke='%23ffffff' stroke-opacity='.3' stroke-width='1.5' stroke-dasharray='5 3.5' stroke-linecap='round'/%3e%3c/svg%3e");
  background-size: 100% 100%; background-repeat: no-repeat;
}
[data-theme="light"] .subir {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3e%3crect x='0.75' y='0.75' width='calc(100%25 - 1.5px)' height='calc(100%25 - 1.5px)' fill='none' rx='11.25' ry='11.25' stroke='%23785a41' stroke-opacity='.5' stroke-width='1.5' stroke-dasharray='5 3.5' stroke-linecap='round'/%3e%3c/svg%3e");
}

/* ──────────────────────────────────── buscador y lista de fichas */

/* Las cuatro cifras de cabecera. La caja es .proj-stat, que aquí entra en la
   lista de paneles esmerilados para que lleve EXACTAMENTE el mismo cristal que
   la tarjeta de abajo: la suya heredada es un gris que sobre el negro de la
   cabecera no era de la misma familia que el resto. Lo de dentro
   va en este orden —rótulo, número, nota y barra— porque es el del original,
   y su .proj-stat-lbl coloca el rótulo debajo. */
/* El original le pone !important al fondo claro de .proj-stat, así que no
   basta con meterla en la lista de paneles: hay que ganarle con la misma
   arma para que tome el cristal teñido de la tarjeta de abajo. */
[data-theme="light"] .proj-stat {
  background: var(--card-bg-lt, rgba(255,248,242,.42)) !important;
}
[data-theme="dark"] .proj-stat {
  background: var(--card-bg, rgba(255,255,255,.07)) !important;
}
.proj-stats { margin-bottom: 1rem; }
@media (max-width: 900px) { .proj-stats { grid-template-columns: repeat(2, 1fr); } }
.cifra-ficha .rot {
  font-family: 'Josefin Sans', sans-serif; font-size: 11px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--tenue);
}
.cifra-ficha .val {
  font-family: 'Josefin Sans', sans-serif; font-size: 28px; font-weight: 700;
  color: var(--tinta); line-height: 1.2; margin-top: 6px;
}
.cifra-ficha .nota {
  font-family: 'Josefin Sans', sans-serif; font-size: 11px;
  color: var(--tenue); margin-top: 2px;
}
/* "medida" y no "barra": .barra ya es la del buscador de fotos, con su
   relleno y su altura, y la proporción salía como un bloque gris. */
.cifra-ficha .medida {
  height: 3px; border-radius: 2px; margin-top: 12px; overflow: hidden;
  background: rgba(120,90,65,.16);
}
[data-theme="dark"] .cifra-ficha .medida { background: rgba(255,255,255,.12); }
.cifra-ficha .medida span { display: block; height: 100%; border-radius: 2px; }

/* Los filtros, la barra y la lista van DENTRO de la misma tarjeta, como la
   base de empresas del portal de origen: la lista es el contenido de ese
   panel, no una segunda cosa suelta debajo. */
.filtros-fichas { margin-bottom: 1rem; }
/* Los cinco a lo ancho y del mismo tamaño: en fila flexible se quedaban
   apelotonados a la izquierda con un hueco a la derecha. */
.filtros-fichas .proj-filters {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;
}
/* La rejilla de fotos tiene tres filtros y el botón: cuatro columnas. */
#page-fotos .filtros-fichas .proj-filters { grid-template-columns: repeat(3, 1fr); }
/* La tarjeta, por encima de la rejilla. El z-index de .resultados no bastaba:
   .adash-card lleva backdrop-filter, y eso crea un contexto de apilamiento
   propio del que el desplegable no puede salir por alto que suba dentro. Hay
   que levantar la tarjeta ENTERA por encima de la rejilla. */
#page-fotos .filtros-fichas { position: relative; z-index: 20; }
#page-fotos .rejilla { position: relative; z-index: 0; }
/* Y que el desplegable TAPE lo que hay debajo. Con el cristal de la tarjeta
   se transparentaban las insignias de las fotos y la lista no se leía; aquí
   toca opacidad casi total, que es lo que se espera de un desplegable. */
#page-fotos .resultados {
  background: rgba(240,234,244,.985);
  backdrop-filter: blur(24px) saturate(160%);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  box-shadow: 0 14px 36px rgba(0,0,0,.3);
}
[data-theme="dark"] #page-fotos .resultados { background: rgba(26,26,36,.985); }
#page-fotos .filtros-fichas .buscador { position: relative; margin-top: 12px; }
#page-fotos .filtros-fichas .proj-search { width: 100%; box-sizing: border-box; }
@media (max-width: 1000px) { .filtros-fichas .proj-filters { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 680px)  { .filtros-fichas .proj-filters { grid-template-columns: repeat(2, 1fr); } }
.filtros-fichas .proj-filter-select { min-width: 0; width: 100%; }
/* Siempre visible, el último de la fila. Apagado mientras no haya nada que
   quitar: esconderlo movía los otros cuatro cada vez que se filtraba. */
.filtros-fichas .btn-reject {
  width: 100%; height: 36px; padding: 0 10px; border-radius: 10px;
  font-size: 11px; letter-spacing: 1px;
}
.filtros-fichas .btn-reject:disabled {
  opacity: .38; cursor: default;
  background: rgba(220,53,69,.06) !important; border-color: rgba(220,53,69,.18) !important;
}
.filtros-fichas .btn-reject:disabled:hover {
  background: rgba(220,53,69,.06) !important; border-color: rgba(220,53,69,.18) !important;
  color: #dc3545 !important;
}
/* En el original la barra comparte fila con el orden; aquí va sola y ancha. */
.filtros-fichas .proj-search { width: 100%; box-sizing: border-box; }

.lista-fichas {
  display: flex; flex-direction: column; gap: 8px; margin-top: 16px;
}

/* La fila plegada. El acordeón es .proj-acc-item, así que ya trae el borde
   discontinuo por SVG y el fondo del cristal. */
/* El acordeón no lleva el crema de .proj-acc-item: sobre una tarjeta teñida
   de violeta era una isla de otro color. Lleva EXACTAMENTE el mismo velo que
   los desplegables y la barra de búsqueda de arriba —rgba(255,255,255,.4) en
   claro, .05 en oscuro—, así que la fila y el panel abierto son del mismo tono
   que los controles con los que se usan.

   Y el mismo valor plegada que desplegada: un bloque más claro dentro del
   acordeón hacía que la ficha pareciera una tarjeta pegada encima en vez de
   el contenido de esa fila. */
.ficha-acc { overflow: hidden; }
.proj-acc-item.ficha-acc,
.proj-acc-item.ficha-acc.abierto { background-color: rgba(255,255,255,.4) !important; }
[data-theme="dark"] .proj-acc-item.ficha-acc,
[data-theme="dark"] .proj-acc-item.ficha-acc.abierto { background-color: rgba(255,255,255,.05) !important; }
.ficha-cab {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 12px 16px; border: 0; background: none; cursor: pointer;
  color: var(--tinta); font: inherit; text-align: left;
}
.ficha-cab .mini {
  width: 38px; height: 38px; border-radius: 9px; flex-shrink: 0; object-fit: cover;
  background: rgba(120,90,65,.14); border: 1px solid var(--borde);
}
[data-theme="dark"] .ficha-cab .mini { background: rgba(255,255,255,.09); }
.ficha-cab .quien { flex: 1; min-width: 0; }
.ficha-cab .nom {
  display: block; font-family: 'Josefin Sans', sans-serif;
  font-size: 13px; font-weight: 700; letter-spacing: .3px; color: var(--tinta);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ficha-cab .dat {
  display: block; font-family: 'Josefin Sans', sans-serif; font-size: 11px;
  color: var(--tenue); margin-top: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* El galón hereda .proj-acc-chev del original: opacidad .4 y giro de 0,3 s.
   Aquí solo se le dice cuándo girar, porque nuestra clase de abierto es otra. */
.ficha-cab .chevron { flex-shrink: 0; color: var(--tinta); }
.ficha-acc.abierto .ficha-cab .chevron { transform: rotate(180deg); }
/* El nombre de la fila abierta NO cambia de color: ya se sabe cuál está
   abierta por el galón y por la ficha desplegada debajo. */

/* Plegado de verdad: el contenido no se pinta hasta que se abre, pero si
   quedara en el DOM ocuparía sitio. */
.ficha-cuerpo-acc { display: none; padding: 0 16px 16px; }
.ficha-acc.abierto .ficha-cuerpo-acc { display: block; }

/* ─────────────────────────────────────────── árbol genealógico (sección) */

/* La sección se lee como las demás —cabecera, buscador y luego el contenido— y
   el lienzo se queda con el alto que sobra. El plano se mueve DENTRO de él: un
   árbol con dieciséis tatarabuelos no cabe en ningún ancho, así que en vez de
   encogerlo se navega por él.
   La cabecera flotaba sobre el lienzo y necesitaba un velo en degradado para
   que los nombres del árbol no se cruzaran con el título. Puesta en su sitio,
   el velo sobra: no hay nada que pase por detrás. */
#page-arbol.active { position: absolute; inset: 0; padding: 36px 40px 0; display: flex; flex-direction: column; }
/* La cabecera y el buscador van ENCIMA del lienzo, no antes que él: el árbol
   ocupa la pantalla entera y les pasa por debajo. Se cortaba en seco justo al
   llegar a la tarjeta, y un corte recto en mitad del dibujo se lee como que ahí
   se acaba el árbol. */
#page-arbol .page-head, #page-arbol .cabecera-pagina, #page-arbol .filtros-fichas {
  position: relative; z-index: 3; flex: 0 0 auto; pointer-events: none;
}
#page-arbol .filtros-fichas .buscador { pointer-events: auto; }
/* La tarjeta, más transparente que en las demás secciones: aquí lo que hay
   detrás no es un fondo, es el árbol, y se quiere intuir. */
#page-arbol .filtros-fichas {
  background: color-mix(in srgb, var(--panel) 62%, transparent);
  backdrop-filter: blur(16px) saturate(130%);
  -webkit-backdrop-filter: blur(16px) saturate(130%);
}
/* A sangre y por debajo de todo: el lienzo es la pantalla entera. Los márgenes
   negativos cancelan el acolchado de .main. */
.lienzo-arbol {
  position: absolute; inset: 0;
  overflow: hidden;
  cursor: grab; touch-action: none;
}
/* El velo: cuatro capas de desenfoque creciente, cada una asomando un poco más
   abajo que la anterior. Ninguna pinta color —solo emborronan lo que tienen
   detrás—, así que el árbol se difumina al subir hacia el título en vez de
   desaparecer bajo una banda. Hace falta apilarlas porque un `backdrop-filter`
   no admite un desenfoque que varíe con la altura. */
.velo-arbol {
  position: absolute; left: 0; right: 0; top: 0; height: 15rem;
  z-index: 2; pointer-events: none;
}
.velo-arbol i { position: absolute; left: 0; right: 0; top: 0; display: block; }
.velo-arbol i:nth-child(1) { bottom: 0;    backdrop-filter: blur(1px);  -webkit-backdrop-filter: blur(1px);  -webkit-mask-image: linear-gradient(to top, transparent, #000 35%); mask-image: linear-gradient(to top, transparent, #000 35%); }
.velo-arbol i:nth-child(2) { bottom: 25%;  backdrop-filter: blur(3px);  -webkit-backdrop-filter: blur(3px);  -webkit-mask-image: linear-gradient(to top, transparent, #000 45%); mask-image: linear-gradient(to top, transparent, #000 45%); }
.velo-arbol i:nth-child(3) { bottom: 50%;  backdrop-filter: blur(8px);  -webkit-backdrop-filter: blur(8px);  -webkit-mask-image: linear-gradient(to top, transparent, #000 55%); mask-image: linear-gradient(to top, transparent, #000 55%); }
.velo-arbol i:nth-child(4) { bottom: 72%;  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); -webkit-mask-image: linear-gradient(to top, transparent, #000 60%); mask-image: linear-gradient(to top, transparent, #000 60%); }
.lienzo-arbol.arrastrando { cursor: grabbing; }
/* El plano se arrastra, así que dentro no se selecciona ni se arrastra nada por
   su cuenta. Sin esto, empezar a mover el árbol desde encima de una caja pintaba
   de azul los nombres de medio archivo, y si el tirón empezaba sobre una cara,
   lo que se movía era la foto —el navegador la da por arrastrable— y el árbol se
   quedaba quieto. Los retratos llevan además draggable=false, que es lo único
   que para el arrastre nativo de imágenes en Firefox. */
.lienzo-arbol, .lienzo-arbol * {
  user-select: none; -webkit-user-select: none;
  -webkit-user-drag: none;
}
.lienzo-arbol img { pointer-events: none; }
.plano { position: absolute; left: 0; top: 0; transform-origin: 0 0; will-change: transform; }
.lienzo-arbol .hilos { position: absolute; left: 0; top: 0; pointer-events: none; }
/* Las filas ya no se centran: cada unidad lleva su margen calculado, que es lo
   que garantiza que los hijos caigan bajo su matrimonio. La fila empieza a la
   izquierda y de ahí para allá manda la maqueta. */
#arbol-gens { display: grid; gap: 2.6rem; justify-items: start; }
#arbol-gens .gen { justify-content: flex-start; gap: 0; align-items: stretch; }
/* La unidad: una persona o un matrimonio. El hueco de dentro es el sitio de la
   línea de boda; el de fuera lo pone la maqueta. */
#arbol-gens .unidad { display: flex; gap: 1.1rem; }

/* La persona del centro, marcada de verdad: el árbol es suyo. */
#arbol-gens .nodo.foco { border-color: var(--orange); background: rgba(var(--orange-rgb), .18); }
/* Los nodos, opacos: el plano se mueve por debajo y con cristal se veían los
   hilos y los nombres de detrás cruzando las cajas. */
#arbol-gens .nodo { background: var(--panel); }

/* El desplegable de candidatos cuelga del campo, que ahora es el buscador ancho
   de las demás secciones. Va por encima del lienzo y flotando: si empujara, el
   árbol daría un salto en cuanto se escribe una letra. */
#page-arbol .filtros-fichas { position: relative; z-index: 20; }
#arbol-candidatos {
  position: absolute; left: 0; right: 0; top: calc(100% + .4rem); z-index: 50;
  border-radius: .6rem; overflow: hidden; max-height: 22rem; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--borde);
  /* Más tapado que los demás desplegables y con sombra: éste cae sobre el
     árbol, no sobre una rejilla, y con el cristal de siempre se leían los
     nombres de las cajas de detrás cruzando los de la lista. */
  backdrop-filter: blur(24px) saturate(140%); -webkit-backdrop-filter: blur(24px) saturate(140%);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, .5);
}
#arbol-candidatos:empty { display: none; border: 0; }

/* Por debajo del velo: el lienzo empieza arriba del todo, y ahí los mandos se
   montaban encima del título y de la tarjeta de búsqueda. */
.arbol-mandos {
  position: absolute; top: 15.4rem; right: 1.1rem; z-index: 4;
  display: flex; flex-direction: column; gap: .35rem;
}
.arbol-mandos button {
  width: 2.3rem; height: 2.3rem; border-radius: .6rem;
  border: 1px solid var(--borde); background: var(--panel); color: var(--tinta);
  font: inherit; font-size: 1rem; line-height: 1; cursor: pointer;
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
}
.arbol-mandos button:hover { border-color: var(--orange); color: var(--orange); }

/* En la sección, los hilos van en oro. Sobre el fondo oscuro a pantalla
   completa el gris al 28 % —que es el correcto dentro de una ficha, sobre el
   cristal claro del panel— sencillamente no se ve. Aquí el árbol ES la
   pantalla, así que sus líneas se pintan con el color de resalte. */
/* Un color y uno solo. Se probó a dar tono distinto a dos familias vecinas para
   distinguirlas y no quedaba bien: el árbol pasaba a parecer un mapa de metro y
   los tonos no decían nada, porque no significan nada. Lo que hacía falta era
   poder seguir una línea, y eso lo da el rótulo al pasar por encima. */
#arbol-hilos line, #arbol-hilos path { stroke: var(--orange); opacity: .75; stroke-width: 2.2; }
/* La copia gruesa e invisible que se puede apuntar con el ratón: una línea de
   1,5 px no hay quien la señale. `stroke` y no `all`: solo responde el trazo,
   no el rectángulo que lo contiene, o taparía media pantalla. */
#arbol-hilos path.tocable {
  stroke: transparent; stroke-width: 16; fill: none;
  pointer-events: stroke; cursor: help;
}
#arbol-hilos { pointer-events: none; }
#arbol-hilos path.tocable { pointer-events: stroke; }
/* La rejilla de cajas se pinta ENCIMA de los hilos y tapa el hueco entre filas,
   que es justo por donde pasan las líneas: sin esto, el ratón nunca llegaba a
   una. Que responda solo lo que es una caja; el resto lo atraviesa. */
#arbol-gens, #arbol-gens .gen, #arbol-gens .unidad { pointer-events: none; }
#arbol-gens .nodo { pointer-events: auto; }

/* El rótulo que cuenta qué relación dibuja una línea: un mini árbol con esa
   familia sola, sin el ruido de las otras trescientas cajas.
   El cristal es el del selector de fecha del portal de la empresa, que es el
   patrón de ventana emergente que ya está validado ahí: mismo desenfoque, mismo
   radio, mismas sombras. */
.rotulo-hilo {
  position: absolute; z-index: 6; pointer-events: none;
  padding: 16px 18px 14px; border-radius: 16px;
  background: rgba(220, 215, 210, .25);
  border: 1px solid rgba(255, 255, 255, .65);
  backdrop-filter: blur(40px) saturate(180%);
  -webkit-backdrop-filter: blur(40px) saturate(180%);
  box-shadow: 0 16px 48px rgba(0, 0, 0, .22), 0 4px 16px rgba(0, 0, 0, .12),
              inset 0 1px 0 rgba(255, 255, 255, .7);
  color: var(--tinta);
}
:root[data-theme="dark"] .rotulo-hilo {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .18);
  box-shadow: 0 8px 32px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .12);
}
.rotulo-hilo[hidden] { display: none; }

/* El encabezado, con la letra del selector de fecha: versalitas espaciadas. */
.rotulo-hilo { max-width: 27rem; }
.rotulo-hilo .mini-titulo {
  font-family: 'Josefin Sans', sans-serif;
  font-size: 11px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
  text-align: center; margin-bottom: 5px; color: var(--tenue);
}
/* La frase va en caja baja: en versalitas espaciadas se lee «AOÛT 2026», que son
   dos palabras, pero no una explicación de dos líneas. */
.rotulo-hilo .mini-frase {
  text-align: center; margin-bottom: 14px;
  font-size: .8rem; line-height: 1.4; color: var(--tinta);
}

.mini-arbol { position: relative; display: flex; flex-direction: column; align-items: center; }
/* `stretch` y no `flex-start`: todas las cajas de una fila miden lo mismo aunque
   un nombre ocupe tres líneas y otro una. Es el mismo motivo que en el árbol
   grande —si una caja baja su borde, el hilo que sale de ella arranca más abajo
   que el de su pareja— y además, con una sola caja alta, las demás quedaban
   flotando encima del hueco. */
.mini-arbol .mini-fila { display: flex; align-items: stretch; justify-content: center; }
.mini-arbol .mini-fila.padres { gap: 26px; margin-bottom: 34px; }
.mini-arbol .mini-fila.hijos { gap: 14px; }
.mini-arbol .mini-unidad { display: flex; align-items: stretch; gap: 8px; }
.mini-arbol .mini-hilos { position: absolute; left: 0; top: 0; pointer-events: none; }
.mini-arbol .mini-hilos path { stroke: var(--orange); opacity: .75; stroke-width: 2; fill: none; }

.mini-caja {
  width: 5.6rem; padding: .4rem .35rem; border-radius: 9px; text-align: center;
  background: var(--panel); border: 1px solid var(--borde);
  font-size: .68rem; line-height: 1.2;
  /* Los años, siempre abajo del todo: con el nombre a una línea o a tres, la
     caja mide igual y el año queda a la misma altura en todas. */
  display: flex; flex-direction: column; justify-content: space-between;
}
.mini-caja .a { display: block; margin-top: .15rem; font-size: .6rem; color: var(--tenue); }
/* Quien no es de esta familia sino que está de paso —el marido de una hija, que
   sale en el árbol grande pegado a ella— va más apagado y sin línea a los
   padres: se dibuja para reconocer el sitio, no porque descienda de ellos. */
.mini-caja.pareja { opacity: .62; border-style: dashed; }

.arbol-ayuda {
  position: absolute; left: 0; right: 0; bottom: .8rem; z-index: 2;
  margin: 0; text-align: center; pointer-events: none;
  font-size: .76rem; color: var(--tenue);
}

/* ────────────────────────────────────────────────────── ficha de persona */

/* Dentro del acordeón la ficha va SIN panel propio: el acordeón ya es la
   ventana, y con los dos se veía una tarjeta dentro de otra. */
.ficha { display: block; margin: 0; padding: .4rem 0 0; background: none; border: 0; }
.ficha[hidden] { display: none; }

/* Arriba a la derecha y flotando: las rejillas de campos son contenedores de
   cuadrícula, así que no se meten por debajo del flotante —se estrechan
   mientras convive con él y recuperan el ancho entero al pasarlo—. Es justo
   lo que se busca: la foto ocupa el alto del nombre y los primeros datos, y
   de ahí para abajo la ficha usa la tarjeta completa. */
.retrato-caja {
  float: right; width: 210px; aspect-ratio: 3 / 4;
  margin: 0 0 1rem 1.6rem; position: relative;
}
.retrato-caja[hidden] { display: none; }
.retrato {
  width: 100%; height: 100%; object-fit: cover;
  border-radius: .6rem; margin: 0;
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--borde); cursor: pointer;
}
@media (max-width: 800px) { .retrato-caja { float: none; width: 150px; margin: 0 0 1rem; } }
.retrato:not([src]) { display: none; }
/* Mismo aire que la etiqueta del visor: una píldora oscura con el texto en
   ámbar, discreta en la esquina del retrato grande de la ficha. */
.etiqueta-ia {
  position: absolute; right: 8px; bottom: 8px;
  padding: .15rem .55rem; border-radius: 999px;
  background: rgba(0, 0, 0, .72); color: var(--cara);
  font-size: .68rem; font-weight: 700; letter-spacing: .04em;
  font-family: 'Josefin Sans', sans-serif; pointer-events: none;
}
.etiqueta-ia[hidden] { display: none; }

.ficha-cuerpo { min-width: 0; }
.ficha h2 {
  margin: 0 0 .2rem; font-family: 'Josefin Sans', sans-serif;
  font-size: 1.35rem; font-weight: 700; letter-spacing: .01em; line-height: 1.15;
}
.ficha .vital {
  margin: 0 0 1.4rem; color: var(--tenue); font-size: .82rem; letter-spacing: .01em;
}
.ficha .vital .apodo { color: var(--cara); }

/* Los datos van en campos con rótulo, como las fichas del portal de origen.
   La rejilla es la suya (.proj-acc-grid, cuatro columnas); aquí solo se le da
   el alto automático, porque los valores largos —una dirección entera— no
   caben en los 30 px fijos de un campo de formulario. */
/* Alineados por arriba, no estirados. La rejilla heredada estira las celdas y
   empuja el valor al fondo (justify-content:space-between), que con todos los
   campos de una línea queda bien; aquí un "Domicilio de sus abuelos paternos…"
   ocupa tres y dejaba a sus vecinos flotando abajo con un hueco encima. */
/* Todos los cuadros de una fila empiezan y acaban a la misma altura. Antes
   cada campo medía lo suyo: "SEGUNDO APELLIDO" ocupa dos líneas de rótulo y
   "SEXO" una, así que el cuadro de al lado arrancaba más arriba y la fila
   quedaba en escalera. El rótulo reserva SIEMPRE dos líneas y el cuadro se
   estira hasta el fondo de la celda; la rejilla ya iguala el alto de la fila. */
.datos { margin-bottom: 4px; align-items: stretch; }
.datos .proj-acc-f { justify-content: flex-start; height: 100%; }
.datos .proj-acc-lbl { min-height: 2.1em; }
.datos .proj-acc-inp {
  flex: 1;
  height: auto; min-height: 30px; padding: 6px 9px; line-height: 1.35;
  display: flex; align-items: center; white-space: normal;
  cursor: default; user-select: text;
}
/* Los cuadros de texto largo van solos en su fila y no necesitan estirarse. */
.datos .cuadro-texto { flex: 0 1 auto; }
@media (max-width: 1100px) { .datos { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px)  { .datos { grid-template-columns: 1fr; } }
/* Un campo que ocupa varias columnas no puede pasarse del ancho disponible. */
.datos .proj-acc-f { min-width: 0; }

/* El texto libre de un acta va debajo de sus cuadros, con el mismo aire que
   la prosa pero sin el plegado: es corto y forma parte del registro. */
#ficha-registros { margin-top: .2rem; }
/* Las notas y el texto libre de las actas, en el mismo cuadro que los demás
   campos: mismo tipo de letra, mismo relleno y mismo borde. Solo cambian el
   alto —crecen— y que respetan los saltos de línea del original. */
.datos .cuadro-texto {
  display: block; white-space: pre-wrap; line-height: 1.55;
  padding: 10px 12px; max-width: none;
}
textarea.proj-acc-inp.cuadro-texto { resize: vertical; font-family: inherit; }
#ficha-editor { margin-top: .2rem; }
#ficha-editor .editor-botones {
  grid-column: 1 / -1; flex-direction: row; gap: .6rem; margin-top: .2rem;
}
#ficha-editor .editor-botones button { flex: 0 0 auto; }
#ficha-registros .proj-acc-sec-title:first-child { margin-top: 18px; }

/* La familia usa la misma rejilla de campos que los datos: cada pariente en
   su cuadro, con el parentesco de rótulo. El cuadro es un botón porque lleva
   a su ficha, pero se ve igual que los demás. */
/* Con .datos delante: la regla de los campos ya declara display:flex y gana
   por especificidad a una sola clase. */
.datos .campo-persona {
  /* En bloque y no en fila: con los años como columna aparte, un nombre largo
     se partía en cuatro líneas para dejarles sitio. Así fluyen detrás. */
  display: block; text-align: left; cursor: pointer; line-height: 1.35;
  transition: border-color .15s ease, color .15s ease;
}
.campo-persona:hover { border-color: var(--orange); color: var(--orange); }
.campo-persona .anios { color: var(--tenue); font-size: .78rem; margin-left: .4rem; }
.campo-persona:hover .anios { color: inherit; opacity: .75; }

/* La prosa ya no es prosa suelta: son cuadros de campo. Sin ancho máximo
   —lo limitaba a 62 caracteres y el cuadro se quedaba a media tarjeta— y sin
   tipografía propia, que la ponía el selector .prosa p y le ganaba al campo
   por especificidad: por eso las notas seguían en Montserrat mientras el
   resto de la ficha ya iba en Josefin Sans. */
.prosa { font-size: .87rem; line-height: 1.6; }
.prosa h3 {
  margin: 1.3rem 0 .4rem; font-family: 'Josefin Sans', sans-serif;
  font-size: .62rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--tenue); font-weight: 700;
}
.prosa p { margin: 0 0 .5rem; white-space: pre-wrap; }
.prosa .sacramento {
  margin: 0 0 .45rem; padding-left: .7rem;
  border-left: 2px solid rgba(245, 196, 81, .35);
}
.prosa .sacramento b { font-weight: 600; }
/* Adjunto con vista previa. La miniatura se carga al pintar, así que si el
   fichero no está en el archivo se ve tachado antes de pulsarlo, en vez de
   descubrirlo con un clic que no hace nada. Al pulsar se abre el visor, que
   es una capa por encima: no se sale de la ficha. */
.adjunto {
  display: inline-flex; align-items: center; gap: .5rem; vertical-align: middle;
  margin: .15rem .3rem .15rem 0; padding: .2rem .55rem;
  border: 1px solid var(--borde); border-radius: .5rem; cursor: pointer;
  background: rgba(245,196,81,.16); color: var(--tinta);
  font-family: 'Josefin Sans', sans-serif; font-size: .8rem; text-align: left;
  max-width: 100%;
}
/* Todos juntos y debajo del texto, en su propia fila. */
.adjuntos {
  display: flex; flex-wrap: wrap; gap: .4rem;
  margin-top: .75rem; padding-top: .1rem;
}
.adjunto.con-vista { padding: .25rem .6rem .25rem .25rem; }
.adjunto img {
  width: 34px; height: 34px; object-fit: cover; border-radius: .35rem;
  flex-shrink: 0; background: rgba(255,255,255,.2);
}
.adjunto .nombre { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adjunto:hover { background: rgba(245,196,81,.3); }
.adjunto.ausente { opacity: .5; cursor: not-allowed; text-decoration: line-through; }

.btn-tema {
  width: 100%; padding: 8px; margin-bottom: 8px; cursor: pointer;
  background: rgba(255,255,255,.08); border: 1px solid var(--borde);
  border-radius: 8px; color: inherit; font-size: .78rem;
}
.btn-tema:hover { background: rgba(255,255,255,.16); }

.prosa .aviso-datos { color: var(--tenue); font-style: italic; font-size: .8rem; }

/* Las notas de algunas fichas ocupan pantallas enteras y empujaban la galería
   fuera de la vista. Se pliegan y se despliegan a voluntad. */
.prosa.plegada { max-height: 11rem; overflow: hidden; position: relative; }
/* El degradado del pliegue debe fundir con el panel, no con un color fijo. */
.prosa.plegada::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 4rem;
  background: linear-gradient(transparent, var(--panel) 92%); pointer-events: none;
}
.desplegar {
  background: none; border: 0; padding: .35rem 0 0; cursor: pointer;
  color: var(--cara); font-size: .82rem; text-decoration: underline;
}

@media (max-width: 640px) {
  .ficha { padding: 1rem; gap: 1rem; }
  .retrato { width: 96px; }
  .datos { grid-template-columns: 1fr; gap: .1rem .5rem; }
  .datos dt { margin-top: .4rem; }
}

/* El título de página hereda un blanco pensado para el fondo oscuro del portal
   de origen; sobre el fondo claro de aquí no se leía. */
/* El contador va junto al título y sobre la parte oscura del fondo: en gris
   no se leía. Toma el mismo color que el título en cada tema, no un blanco
   fijo, o en oscuro quedaba un punto más brillante que el propio título. */
.cuenta {
  color: #fff; font-family: 'Josefin Sans', sans-serif;
  font-size: .82rem; letter-spacing: .04em;
}
[data-theme="dark"] .cuenta { color: var(--tinta); }
.page-title { color: var(--tinta) !important; }
.page-sub { color: var(--tenue) !important; }

/* ────────────────────────────────────────────────── piezas del perfil */

.avatar-inicial, .avatar-foto {
  width: 84px; height: 84px; border-radius: 16px; display: flex;
  align-items: center; justify-content: center; object-fit: cover;
  font-family: 'Josefin Sans', sans-serif; font-size: 30px; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--orange), #f0d089);
  border: 2px solid var(--orange);
}

/* El rojo de quitar viene heredado, pero sin estado de foco: se le añade el
   mismo relleno al pasar por encima que tienen los demás botones de borrar. */
.btn-reject:hover { background: #dc3545 !important; border-color: #dc3545 !important; color: #fff !important; }

.tema-btn {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px; flex: 1;
  border-radius: 8px; cursor: pointer; font: inherit; font-size: .88rem;
  border: 1.5px solid var(--borde); background: rgba(255,255,255,.04); color: var(--tinta);
}
.tema-btn.activo { border-color: var(--orange); background: var(--orange-light); color: var(--orange); }

.fondos {
  display: grid; grid-template-columns: repeat(auto-fill, 72px);
  gap: 8px; margin-top: 8px; justify-content: center;
}
/* La marca de elegido va DENTRO de la muestra, como en el original. */
.fondo-swatch { position: relative; }
.fondo-swatch .tick {
  position: absolute; top: 0; left: 0; width: 72px; height: 72px; border-radius: 12px;
  display: none; align-items: center; justify-content: center;
  background: rgba(0,0,0,.3); color: #fff; font-size: 19px; font-weight: 700;
  pointer-events: none;
}
.fondo-swatch.activo .tick { display: flex; }
.fondo-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 6px; width: 72px;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.fondo-swatch .muestra {
  width: 72px; height: 72px; border-radius: 12px; display: block;
  background-size: cover; background-position: center;
  outline: 2px solid transparent; outline-offset: -2px; transition: outline-color .2s;
}
.fondo-swatch.activo .muestra { outline-color: var(--orange); }
.fondo-swatch .etq {
  font-family: 'Josefin Sans', sans-serif; font-size: 8px; letter-spacing: 1px;
  text-transform: uppercase; color: var(--tenue);
}
.fondo-swatch.activo .etq { color: var(--orange); font-weight: 700; }

/* Cada ajuste en su marco, con icono, texto y el interruptor a la derecha:
   la misma anatomía que la lista de widgets del portal de origen. */
/* Las filas iban pegadas: el original las separa 8 px con el gap de
   .proj-acc-wrap, y aquí son hermanas sueltas. */
.proj-acc-item.ajuste { margin-bottom: 8px; }
.fila-ajuste {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 15px 18px;
}
.fila-ajuste .que { display: flex; align-items: center; gap: 12px; }
.fila-ajuste .ic { font-size: 18px; width: 26px; text-align: center; flex-shrink: 0; color: var(--tenue); }
.fila-ajuste .tit {
  font-family: 'Josefin Sans', sans-serif; font-size: 12px; font-weight: 700;
  letter-spacing: .3px; color: var(--tinta);
}
.fila-ajuste .sub {
  font-family: 'Josefin Sans', sans-serif; font-size: 11px;
  color: var(--tenue); margin-top: 5px;
}

/* El ojo de las contraseñas, dentro del propio campo. */
.con-ojo { position: relative; }
.con-ojo .proj-acc-inp { width: 100%; box-sizing: border-box; padding-right: 34px; }
.ojo {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; border: 0; background: none; cursor: pointer;
  color: var(--tenue); line-height: 1; opacity: .6;
  display: flex; align-items: center; justify-content: center; padding: 0;
}
.ojo:hover, .ojo.viendo { opacity: 1; color: var(--orange); }

/* El botón de cambiarla hereda la forma de .btn-logout pero no su rojo: aquí
   no se borra nada, se guarda algo. */
.btn-clave {
  background: rgba(var(--orange-rgb), .15) !important;
  border-color: rgba(var(--orange-rgb), .35) !important;
  color: var(--orange) !important;
}
.btn-clave:hover {
  background: var(--orange) !important; border-color: var(--orange) !important; color: #fff !important;
}
.btn-clave:disabled { opacity: .5; cursor: default; }

/* El enlace con la propia ficha: retrato, nombre con los datos debajo y el
   botón de quitar al otro extremo. */
.enlace-mio { display: flex; align-items: center; gap: 12px; }
.enlace-mio .mini {
  width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0; object-fit: cover;
  background: rgba(120,90,65,.14); border: 1px solid var(--borde);
}
[data-theme="dark"] .enlace-mio .mini { background: rgba(255,255,255,.09); }
.enlace-mio .quien {
  flex: 1; min-width: 0; text-align: left; background: none; border: 0;
  padding: 0; cursor: pointer; color: var(--tinta);
}
.enlace-mio .nom {
  display: block; font-family: 'Josefin Sans', sans-serif; font-size: 13px;
  font-weight: 700; letter-spacing: .3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.enlace-mio .dat {
  display: block; font-family: 'Josefin Sans', sans-serif; font-size: 11px;
  color: var(--tenue); margin-top: 4px;
}
.enlace-mio .quien:hover .nom { color: var(--orange); }
.enlace-mio .btn-reject { flex-shrink: 0; }

/* Muestras de opacidad, tinte y tamaño: mismo patrón que las de fondo. */
/* Los reguladores van separados por una línea, como en el original: son un
   grupo aparte del fondo y del tema, no una lista seguida. */
.apartado { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--borde); }
.muestras {
  display: grid; grid-template-columns: repeat(auto-fill, 72px);
  gap: 8px; margin: 10px 0 16px; justify-content: center;
}
.apartado .muestras:last-child { margin-bottom: 0; }
.muestra-btn {
  display: flex; flex-direction: column; align-items: center; gap: 6px; width: 72px;
  background: none; border: 0; padding: 3px; cursor: pointer; border-radius: 12px;
  outline: 2px solid transparent; outline-offset: 3px;
}
.muestra-btn.activo { outline-color: var(--orange); }
/* La muestra enseña el efecto sobre el fondo de verdad: una capa translúcida
   sobre la misma imagen que se ve detrás del portal. Sin esto, las opacidades
   y los tintes se parecían todos entre sí. */
/* SIN borde. Las capas se posicionan con inset:0, que es la caja de relleno:
   por dentro del borde. El degradado de debajo asomaba por ese anillo de 1 px
   —violeta arriba a la izquierda, naranja abajo a la derecha— y parecía un
   bisel alrededor de cada muestra. Las del portal de origen no llevan ninguno. */
.muestra-btn .caja {
  position: relative; width: 66px; height: 66px; border-radius: 10px;
  display: grid; place-items: center; overflow: hidden;
  background: var(--fondo); background-size: cover; background-position: center;
}
.muestra-btn .capa { position: absolute; inset: 0; }
/* La de tamaño no previsualiza el fondo: ahí lo que se compara es la letra, y
   como no hay degradado debajo sí puede llevar su contorno, como en el original. */
/* Gris tenue, como en el original: sobre el crema del panel la muestra
   desaparecía y las cinco parecían recortadas en blanco. */
.muestra-btn .caja.lisa {
  background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.12);
}
[data-theme="light"] .muestra-btn .caja.lisa {
  background: rgba(120,90,65,.08); border: 1px solid rgba(120,90,65,.14);
}
.muestra-btn .caja span:not(.capa) { position: relative; }
.muestra-btn .etq {
  font-family: 'Josefin Sans', sans-serif; font-size: 8px; letter-spacing: 1px;
  text-transform: uppercase; color: var(--tenue);
}
.muestra-btn.activo .etq { color: var(--orange); font-weight: 700; }
.muestra-btn .caja span { font-family: 'Josefin Sans', sans-serif; font-weight: 700; color: var(--tinta); }

.subir {
  /* El borde lo pone la regla SVG de más arriba; aquí solo el hueco. */
  background: none; color: var(--tenue);
  border-radius: 12px; width: 72px; height: 72px; cursor: pointer; font-size: 22px;
}
.subir:hover { color: var(--orange); }
.avatar-envoltorio { position: relative; cursor: pointer; }
.avatar-camara {
  position: absolute; bottom: -5px; right: -5px; width: 22px; height: 22px;
  border-radius: 50%; background: var(--orange); color: #fff; border: 0;
  display: grid; place-items: center; font-size: 11px; cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,.35);
}

/* ──────────────────────────────────────────────────────── árbol familiar */

/* Dentro del acordeón, el árbol tampoco lleva panel propio: con el crema de
   --panel era otro bloque de distinto color sobre la ficha. Solo una línea
   arriba que lo separa de las notas. */
/* El desplazamiento es SOLO horizontal. Poniendo `overflow-x: auto` a secas, el
   otro eje deja de ser `visible` y pasa a `auto` él solo —lo dice la norma: los
   dos ejes no pueden ir uno recortado y otro no—, así que la ficha salía con una
   barra vertical propia y se podía mover arriba y abajo aunque dentro solo
   hubiera un matrimonio sin padres ni hijos. Lo que sobraba por abajo era el
   SVG de los hilos, que se dimensiona aparte y no cuenta para el alto de la
   caja. Los dos ejes, escritos. */
.arbol {
  position: relative; margin: .6rem 0 0; padding: .8rem 0 .4rem;
  overflow: auto hidden; background: none; border: 0;
}
.arbol[hidden] { display: none; }

/* Los hilos se dibujan tras medir los nodos ya colocados: dejar que el
   navegador maquete y luego trazar evita tener que calcular posiciones a mano. */
/* Ídem en el árbol pequeño de la ficha: sin width:100%, que es lo mismo que
   estirarlo, porque ahí también hay desplazamiento horizontal. */
.hilos { position: absolute; left: 0; top: 0; pointer-events: none; }
.hilos line, .hilos path { stroke: currentColor; opacity: .28; stroke-width: 1.5; fill: none; }
/* La línea de matrimonio, igual que las demás. Iba en ámbar y más gruesa, de
   cuando era la única horizontal del dibujo; ahora que hay barras de hermanos
   por todas partes, destacarla solo hacía que un trazo del árbol pareciera de
   otro sistema. Un árbol se lee mejor con una sola línea. */

.generaciones {
  position: relative; display: grid; gap: 2.2rem; justify-items: center;
  min-width: max-content;
}
/* stretch y no flex-start: los nodos de una misma fila miden lo mismo aunque
   un nombre ocupe dos líneas. Si no, la caja más alta baja su borde inferior y
   el hilo que sale de ella arranca más abajo que el de su pareja: eso era la
   asimetría de las bajadas. */
.gen { display: flex; gap: 1.1rem; align-items: stretch; justify-content: center; }
.gen.pareja { gap: 2.4rem; }

.nodo {
  display: flex; flex-direction: column; align-items: center; gap: .35rem;
  width: 8.4rem; padding: .5rem .4rem; border-radius: .55rem; cursor: pointer;
  background: rgba(255, 255, 255, .05); border: 1px solid var(--borde);
  color: var(--tinta); text-align: center; font: inherit;
}
.nodo:hover { background: rgba(245, 196, 81, .14); border-color: rgba(245, 196, 81, .45); }
.nodo.foco { background: rgba(245, 196, 81, .18); border-color: var(--cara); }
.nodo .cara-mini {
  width: 46px; height: 46px; border-radius: 50%; object-fit: cover;
  background: rgba(255, 255, 255, .07); border: 1px solid var(--borde);
}
.nodo .sinfoto {
  width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, .06); border: 1px solid var(--borde);
  color: var(--tenue); font-size: .95rem;
}
.nodo .n { font-size: .78rem; line-height: 1.25; }
.nodo .a { font-size: .7rem; color: var(--tenue); font-variant-numeric: tabular-nums; }
.nodo.hueco {
  cursor: default; opacity: .32; border-style: dashed;
  background: none; visibility: hidden;
}
.arbol .vacio { color: var(--tenue); font-size: .85rem; text-align: center; }

/* ─────────────────────────────────────────────────────────────── rejilla */

.rejilla {
  display: grid; gap: 3px; padding: 0;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.celda {
  position: relative; aspect-ratio: 1; overflow: hidden;
  background: rgba(0, 0, 0, .06); cursor: pointer;
  border-radius: 2px;
}
.celda img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  opacity: 0; transition: opacity .25s;
}
.celda img.cargada { opacity: 1; }
.celda:hover img { transform: scale(1.03); transition: transform .3s, opacity .25s; }
.celda .marca, .foto-persona .marca {
  position: absolute; right: 4px; bottom: 4px;
  padding: 1px 5px; border-radius: 3px; font-size: .68rem;
  background: rgba(0, 0, 0, .62); color: var(--cara);
  font-variant-numeric: tabular-nums;
}
.celda .anio, .foto-persona .anio {
  position: absolute; left: 4px; bottom: 4px;
  padding: 1px 5px; border-radius: 3px; font-size: .68rem;
  background: rgba(0, 0, 0, .62); color: var(--cara);
  font-variant-numeric: tabular-nums;
}
/* Los PDF se rasterizan para poder verlos, pero son documentos: se marcan para
   que no se confundan con fotos y para explicar que no llevan recuadros. */
.celda.documento::before {
  content: "DOC"; position: absolute; left: 4px; top: 4px; z-index: 1;
  padding: 1px 4px; border-radius: 3px; font-size: .6rem; letter-spacing: .04em;
  background: rgba(0, 0, 0, .62); color: rgba(255, 255, 255, .72);
}
.celda.documento img { object-fit: contain; background: #fff; }

.alerta {
  position: fixed; left: 50%; bottom: 1.2rem; transform: translateX(-50%); z-index: 200;
  display: flex; align-items: center; gap: .8rem; max-width: min(38rem, 92vw);
  padding: .7rem 1rem; border-radius: .6rem; font-size: .87rem; line-height: 1.45;
  background: rgba(120, 30, 30, .92); border: 1px solid rgba(255, 140, 140, .4);
  color: #ffe6e6; box-shadow: 0 .6rem 1.6rem rgba(0, 0, 0, .5);
}
.alerta button {
  border: 0; background: rgba(0, 0, 0, .3); color: inherit; cursor: pointer;
  width: 1.5rem; height: 1.5rem; border-radius: 50%; flex: none; line-height: 1;
}

.fin { padding: 2.5rem; text-align: center; color: var(--tenue); font-size: .85rem; }

/* ───────────────────────────────────────────────────────────────── visor */

/* Por encima de TODO, también de la ficha a pantalla completa y del lector de
   cartas (z-index 120): desde una ficha se abre una foto, y con 100 la imagen
   se cargaba por detrás y se adivinaba a través del cristal de los paneles. */
.visor {
  position: fixed; inset: 0; z-index: 300; display: none;
  background: rgba(6, 8, 12, .93);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
/* minmax(0,1fr) y no 1fr: por defecto una fila de grid no baja de su contenido,
   así que la foto desbordaba y la barra la tapaba. La barra va primero
   —arriba—, la foto se lleva el resto. */
.visor.abierto { display: grid; grid-template-rows: auto minmax(0, 1fr); }

.lienzo {
  position: relative; display: flex; align-items: center; justify-content: center;
  overflow: hidden; padding: 1rem; min-height: 0;
}

/* El marco debe medir EXACTAMENTE lo que mide la imagen pintada: es lo que
   permite colocar las caras en porcentaje.
   No sirve `max-height:100%` sobre la imagen: el porcentaje se resuelve contra
   la altura del marco, que es automática, así que no limita nada y la foto se
   pintaba a tamaño natural desbordando por abajo. Con aspect-ratio la cadena es
   determinista: el marco toma la altura del lienzo, deduce el ancho, y
   max-width lo reencuadra cuando la foto es apaisada.
   --ar lo fija app.js con las dimensiones reales de cada foto. */
.marco {
  position: relative; line-height: 0;
  height: 100%; aspect-ratio: var(--ar, 4 / 3); max-width: 100%;
}
.marco img { width: 100%; height: 100%; display: block; border-radius: 3px; }
/* La misma idea que el botón de la barra de arriba, pero encima de la propia
   imagen: pulsarla hace lo mismo (alternar original/mejorada), solo que sin
   tener que levantar la vista hasta los mandos. */
.etiqueta-mejorada {
  position: absolute; right: 10px; bottom: 10px; z-index: 1;
  display: flex; align-items: center; gap: .3rem; cursor: pointer;
  padding: .3rem .6rem; border-radius: 999px; border: 0;
  background: rgba(0, 0, 0, .72); color: var(--cara); font-size: .78rem;
  font-family: 'Josefin Sans', sans-serif;
}
.etiqueta-mejorada:hover { background: rgba(0, 0, 0, .88); }
.etiqueta-mejorada[hidden] { display: none; }

.cara {
  position: absolute; border: 2px solid var(--cara); border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), inset 0 0 0 1px rgba(0, 0, 0, .35);
  cursor: pointer; transition: background .15s;
}
.cara:hover { background: rgba(245, 196, 81, .18); }
.cara .nombre {
  position: absolute; left: -2px; top: 100%; margin-top: 3px;
  padding: 2px 6px; border-radius: 3px; white-space: nowrap;
  background: rgba(0, 0, 0, .82); color: var(--cara);
  font-size: .74rem; line-height: 1.35; pointer-events: none;
}
/* Las de abajo del todo sacan la etiqueta hacia arriba para no salirse. */
.cara.baja .nombre { top: auto; bottom: 100%; margin: 0 0 3px; }
.visor.sin-caras .cara { display: none; }

.visor-barra {
  padding: .9rem 1.2rem 1.1rem;
  background: var(--panel); border-bottom: 1px solid var(--borde);
  display: flex; gap: 1.4rem; align-items: center; flex-wrap: wrap;
}
.visor-barra .texto { flex: 1; min-width: 14rem; }
.visor-barra .titulo { font-size: 1rem; margin: 0 0 .2rem; font-weight: 600; }
.visor-barra .meta { font-size: .8rem; color: var(--tenue); margin: 0; }
.visor-barra .nota { font-size: .8rem; color: var(--tenue); margin: .5rem 0 0; max-width: 60ch; }
.visor-barra .gente { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; max-width: 46rem; }
/* Una cápsula por persona, no una por botón: el icono de retrato (solo para
   un administrador) va cosido al nombre en vez de ser su propia píldora
   suelta al lado, que se leería como una segunda persona. */
.visor-barra .gente .persona {
  display: inline-flex; align-items: stretch; border-radius: 999px;
  background: rgba(0, 0, 0, .55); border: 1px solid rgba(245, 196, 81, .3);
}
.visor-barra .gente .nombre-persona {
  padding: .25rem .7rem; border-radius: 999px; cursor: pointer;
  background: none; border: 0; color: var(--cara); font-size: .78rem;
}
.visor-barra .gente .persona:has(.retrato-persona) .nombre-persona { padding-right: .35rem; }
.visor-barra .gente .persona:hover .nombre-persona { background: rgba(0, 0, 0, .3); border-radius: 999px 0 0 999px; }
.visor-barra .gente .retrato-persona {
  display: grid; place-items: center; padding: 0 .55rem 0 .1rem; cursor: pointer;
  background: none; border: 0; color: var(--cara); opacity: .55;
}
.visor-barra .gente .retrato-persona:hover:not(:disabled) { opacity: 1; color: var(--orange); }
.visor-barra .gente .retrato-persona.hecho { opacity: 1; color: var(--orange); cursor: default; }
.visor-barra .gente .retrato-persona:disabled { cursor: default; }

.mandos { display: flex; gap: .4rem; align-items: center; }
/* Iconos de trazo, como los del pie de la barra lateral —sugerencia, fallo,
   tema—, no caracteres de texto: un "×" o un "‹" sueltos dependen de la
   fuente y salen finos y pequeños pase lo que pase con el tamaño de letra. */
.mandos button {
  width: 2.1rem; height: 2.1rem; border-radius: .5rem; cursor: pointer;
  background: rgba(255, 255, 255, .07); border: 1px solid var(--borde);
  color: var(--tinta); display: grid; place-items: center; padding: 0;
}
/* El propio display:grid de arriba, puesto para centrar el SVG, pisaba el
   display:none que trae el atributo hidden —una clase gana a la hoja de
   estilos por defecto del navegador—: un botón "oculto" seguía pintándose
   como una rejilla vacía. */
.mandos button[hidden] { display: none; }
.mandos button:hover { background: rgba(255, 255, 255, .15); }
.mandos button[aria-pressed="true"] { background: rgba(245, 196, 81, .22); border-color: rgba(245, 196, 81, .45); color: var(--orange-dark); }
[data-theme="dark"] .mandos button[aria-pressed="true"] { color: var(--orange); }

.aviso {
  position: fixed; inset: 0; display: grid; place-items: center; z-index: 200;
  background: rgba(6, 8, 12, .9); padding: 2rem; text-align: center;
}
.aviso div { max-width: 34rem; }
.aviso h2 { margin: 0 0 .6rem; font-size: 1.2rem; }
.aviso p { color: var(--tenue); font-size: .9rem; line-height: 1.6; margin: 0 0 .4rem; }
.aviso code { background: rgba(255,255,255,.08); padding: .1rem .35rem; border-radius: .25rem; }

@media (max-width: 640px) {
  .rejilla { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
  .barra input[type="search"] { width: 100%; }
  .visor-barra { flex-direction: column; gap: .8rem; }
}

/* ══════════════════════════════════════════════════ la portada (Inicio)

   La pantalla de entrada: el cielo, la caja de búsqueda y las constelaciones
   de los que ya no están. Es LA ÚNICA parte del portal que no sigue el tema
   claro/oscuro: un cielo estrellado no tiene versión clara. Sus colores van
   escritos aquí y no en variables, a propósito.

   Va `fixed` sobre el mismo hueco que `.main` —que también es fixed— en vez de
   dentro del flujo de la página: así el cielo llega a los cuatro bordes y no lo
   recorta el padding de 36/40 px, y además no participa del scroll. */

/* `.main` lleva `transform: translateZ(0)`, así que un `fixed` de dentro NO se
   mide contra la ventana sino contra ella: `inset:0` es exactamente su hueco,
   el que deja la barra lateral. Poner `left:256px` lo desplazaba otros 256.
   Y se le quita el padding a `.main` mientras la portada está puesta, que si
   no el cielo se queda a 36 px de los bordes. */
.main:has(#page-inicio.active) { padding: 0; }
.portada { position: fixed; inset: 0; overflow: hidden; animation: none; }
.portada-cielo { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.portada-escena {
  position: absolute; inset: 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
}
.portada-caja { width: min(680px, 86%); position: relative; }

/* La cápsula. El vidrio de Apple, hasta donde llega el navegador: fondo
   desenfocado, filo de arriba iluminado por dentro y un brillo que recorre el
   borde al escribir. Lo que no se puede imitar es la refracción de los cantos. */
.portada-capsula {
  position: relative; display: flex; align-items: center; gap: 14px;
  height: 74px; padding: 0 26px; border-radius: 37px;
  background: linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,0) 42%),
              rgba(22,22,26,.55);
  backdrop-filter: blur(30px) saturate(180%);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.22), inset 0 -1px 0 rgba(255,255,255,.05),
              0 18px 50px rgba(0,0,0,.55), 0 0 0 .5px rgba(0,0,0,.35);
  transition: box-shadow .35s;
}
.portada-capsula.viva {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), 0 18px 60px rgba(0,0,0,.6),
              0 0 34px rgba(255,214,170,.13);
}
@property --portada-giro { syntax: '<angle>'; initial-value: 0deg; inherits: false }
.portada-capsula::after {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px;
  background: conic-gradient(from var(--portada-giro, 0deg),
    rgba(255,255,255,0) 0deg, rgba(255,222,180,.75) 40deg,
    rgba(180,205,255,.55) 90deg, rgba(255,255,255,0) 150deg, rgba(255,255,255,0) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: 0; transition: opacity .4s; pointer-events: none;
}
.portada-capsula.viva::after { opacity: 1; animation: portada-giro 5.5s linear infinite; }
@keyframes portada-giro { to { --portada-giro: 360deg } }

.portada-capsula input {
  flex: 1; min-width: 0; background: none; border: 0; outline: 0; box-shadow: none;
  color: #e9e6df; font: 400 25px/1 'Josefin Sans', sans-serif; letter-spacing: .01em;
  caret-color: #ffd9a8; height: auto; padding: 0;
}
.portada-capsula input::placeholder { color: rgba(233,230,223,.42); }
.portada-capsula input::-webkit-search-cancel-button { display: none; }

/* El desplegable de resultados. `position: absolute` y no un simple
   margin-top: `.portada-caja` está centrada verticalmente en toda la
   pantalla (`.portada-escena` es flex), así que si el panel contara para
   su altura, cada resultado nuevo desplazaba el centro hacia arriba y la
   caja de escribir se iba subiendo sola según llegaban coincidencias. Fuera
   del flujo, el panel cuelga de la cápsula sin mover nada más. */
.portada-panel {
  position: absolute; top: calc(100% + 12px); left: 0; right: 0;
  border-radius: 26px; overflow: hidden auto;
  background: linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,0) 30%),
              rgba(22,22,26,.55);
  backdrop-filter: blur(30px) saturate(180%);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18), 0 24px 60px rgba(0,0,0,.55);
  max-height: min(52vh, 460px);
  opacity: 0; transform: translateY(-8px) scale(.985); pointer-events: none;
  transition: opacity .28s, transform .28s;
}
.portada-panel.abierto { opacity: 1; transform: none; pointer-events: auto; }
.portada-fila {
  display: flex; align-items: center; gap: 16px; padding: 13px 22px; cursor: pointer;
  font-family: 'Josefin Sans', sans-serif;
}
.portada-fila:hover, .portada-fila.sel { background: rgba(255,255,255,.07); }
.portada-fila .portada-cara {
  position: relative; overflow: hidden;
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  background: #1a1c22 center / cover no-repeat; box-shadow: 0 0 0 1px rgba(255,255,255,.16);
}
/* El vídeo de "pensando", quieto encima del retrato fijo hasta que
   pintarPortada lo arranca: mismo encuadre que la imagen de detrás, así que
   el cambio de una a otro no se nota más que en que empieza a moverse. */
.portada-pensando {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .2s;
}
.portada-cara.pensando .portada-pensando { opacity: 1; }
.portada-fila .portada-ico {
  width: 38px; height: 38px; border-radius: 11px; flex: none; display: grid;
  place-items: center; background: rgba(255,255,255,.07); font-size: 17px; color: #e9e6df;
}
.portada-fila .portada-txt { min-width: 0; flex: 1; }
.portada-fila .portada-tit { font-size: 16px; color: #e9e6df; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.portada-fila .portada-sub { font-size: 13.5px; color: rgba(233,230,223,.55); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* La pregunta y el "consultadas X piezas" iban pegados, sin el aire que
   separa un título de su bajada en cualquier otra fila. */
.portada-fila.preguntar .portada-sub { margin-top: 4px; }
/* Solo esta cara es animada —las demás son iconos o fotos quietas—, y a
   38 px la pose de pensar apenas se distinguía. Más grande que el resto de
   la fila a propósito: es la única que hace algo. */
.portada-fila.preguntar .portada-cara { width: 60px; height: 60px; }
.portada-fila.preguntar { padding-top: 15px; padding-bottom: 15px; }
.portada-fila .portada-atajo { font-size: 13px; color: rgba(233,230,223,.35); flex: none; }
.portada-rotulo {
  padding: 12px 22px 5px; font: 600 11.5px/1 'Josefin Sans', sans-serif;
  letter-spacing: .13em; text-transform: uppercase; color: rgba(233,230,223,.36);
}
/* La respuesta de Eduardo: a diferencia de una fila de resultado, envuelve —
   son frases, no un título de una línea— y se deja leer sin prisa. */
.portada-respuesta {
  padding: 18px 22px 20px; font: 400 14.5px/1.6 'Josefin Sans', sans-serif;
  color: rgba(233,230,223,.85); white-space: pre-wrap;
}
.portada-respuesta.cargando { color: rgba(233,230,223,.45); font-style: italic; }
.portada-destinos { display: flex; flex-wrap: wrap; gap: 10px; padding: 6px 22px 18px; }
.portada-destino {
  padding: 9px 16px; border-radius: 999px; font: 400 14.5px/1 'Josefin Sans', sans-serif;
  color: #e9e6df; background: rgba(255,255,255,.07);
  border: 1px solid rgba(255,255,255,.10); cursor: pointer;
}
.portada-destino:hover { background: rgba(255,255,255,.13); }
.portada-destino b { font-weight: 600; }
.portada-destino span { color: rgba(233,230,223,.55); }

/* Las constelaciones. Van DEBAJO de la caja (z-index 2 contra 3): la búsqueda
   manda, y una estrella no puede taparla. */
.portada-constelaciones { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
/* La coreografía en tres tiempos —estrellas, luego el trazo, luego los
   familiares encima— vive en tres capas con su PROPIA opacidad, cada una
   con su retraso puesto en JS (dibujarConstelacion): sin retraso propio,
   `.cons` no necesita opacidad de por sí. Al deshacerse la figura, todos los
   retrasos se quitan antes (en rodarZona), para que baje entera de una vez y
   no la misma coreografía puesta del revés. */
.cons { position: absolute; inset: 0; }
.cons svg {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: visible;
  opacity: 0; transition: opacity 1.4s ease;
}
.cons.puesta svg { opacity: 1; }
/* La estrella antes de que llegue el familiar: un punto de luz en el sitio
   exacto donde luego se centra su cara —incluida la separación de un
   matrimonio—, para que uno aparezca justo encima del otro. */
.punto {
  position: absolute; width: 8px; height: 8px; transform: translate(-50%, -50%);
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, #fff 0%, #ffe3b8 45%, rgba(255,200,140,0) 75%);
  box-shadow: 0 0 10px 3px rgba(255,214,160,.5), 0 0 26px 9px rgba(255,196,120,.22);
  opacity: 0; transition: opacity 2s ease;
}
.cons.puesta .punto { opacity: 1; }
/* La cara, pequeña: una estrella con foto tiene que leerse como estrella, no
   como un retrato con una raya al lado. `--cara` manda sobre el tamaño y sobre
   dónde se ancla el bloque.

   EL ANCLAJE IMPORTA: la estrella —el vértice de la línea— es un punto, y el
   bloque lleva cara, nombre y año. Centrando el bloque entero en ese punto, la
   cara queda por encima del vértice y la línea muere en el aire. Se baja media
   cara, y entonces el vértice cae justo en el centro del retrato. */
.astro {
  --cara: 29px; --brecha: 7px;
  position: absolute; transform: translate(-50%, calc(var(--cara) / -2));
  text-align: center; width: 90px; height: var(--cara);
  font-family: 'Josefin Sans', sans-serif;
  opacity: 0; transition: opacity 1.4s ease;
}
.cons.puesta .astro { opacity: 1; }
/* Casados, uno al lado del otro: el nombre se estrecha para que no se pisen. */
.astro.pareja { width: 76px; }
.astro .astro-foto {
  width: var(--cara); height: var(--cara); border-radius: 50%; margin: 0 auto;
  background-color: #12141a; background-repeat: no-repeat;
  box-shadow: 0 0 0 1px rgba(255,240,210,.55), 0 0 10px 2px rgba(255,214,160,.45),
              0 0 24px 7px rgba(255,196,120,.26), 0 0 50px 16px rgba(255,182,96,.10);
}
/* Quien no tiene cara identificada en ninguna foto: estrella y nombre. */
.astro .astro-lucero {
  width: var(--cara); height: var(--cara); margin: 0 auto;
  display: grid; place-items: center;
}
.astro .astro-lucero::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, #ffe3b8 42%, rgba(255,200,140,0) 72%);
  box-shadow: 0 0 14px 4px rgba(255,214,160,.55), 0 0 34px 12px rgba(255,196,120,.26),
              0 0 64px 22px rgba(255,182,96,.10);
}
/* Nombre y año, SIEMPRE fuera del flujo: `.astro` mide exactamente la cara
   (`height: var(--cara)` arriba, para que estos porcentajes tengan una
   altura de referencia fija) y su punto de anclaje no se mueve pase lo que
   pase con el nombre. Por defecto la etiqueta cuelga debajo; cuando
   dibujarConstelacion() decide que este astro va a coincidir con un vecino,
   pone la clase `.arriba` y se voltea encima, sin tocar dónde está la cara. */
.astro-etiqueta { position: absolute; left: 0; width: 100%; top: calc(100% + var(--brecha)); }
/* Arriba o abajo, el orden de lectura no cambia: nombre y luego el año
   debajo, como en cualquier astro. Solo se voltea DÓNDE cuelga el bloque
   entero (encima en vez de debajo de la cara), no el orden de dentro. */
.astro.arriba .astro-etiqueta { top: auto; bottom: calc(100% + var(--brecha)); }
.astro .astro-nom {
  font-size: 11px; letter-spacing: .02em; line-height: 1.22;
  color: rgba(255,246,232,.88); text-shadow: 0 1px 10px rgba(0,0,0,.95);
}
.astro .astro-anno { font-size: 10px; color: rgba(255,228,190,.55); }

/* En una ventana estrecha las zonas son pequeñas y los nombres se pisan: la
   misma constelación, a la escala que quepa. */
.cons.chica .astro { --cara: 24px; --brecha: 5px; width: 78px; }
.cons.chica .astro.pareja { width: 66px; }
.cons.chica .astro-nom { font-size: 9.5px; }
.cons.chica .astro-anno { font-size: 8.5px; }

@media (max-width: 900px) {
  .portada-capsula { height: 62px; padding: 0 20px; }
  .portada-capsula input { font-size: 20px; }
}

/* ─────────────────────────────────────────────── el botón de entrar

   El armazón heredado deja el botón translúcido en tema claro —un relleno al
   28 % con el texto en oro oscuro—, y el portal arranca en claro: la puerta de
   entrada quedaba desvaída. Va en oro macizo con el texto en blanco, que es lo
   que se espera de un botón que es LA acción de esa pantalla. El !important no
   es capricho: las reglas de heredado.css llevan el suyo. */
/* Oro plano, sin degradado, y con un 10 % de transparencia. La transparencia
   va en el COLOR del fondo, no en `opacity` del botón: `opacity` se llevaría
   también el texto por delante y el blanco dejaría de ser blanco. */
.btn-login,
[data-theme="light"] .btn-login {
  background: #9a8053e6 !important;               /* --orange-dark al 90 % */
  color: #fff !important;
  border: 1px solid var(--orange-dark) !important;
  box-shadow: 0 4px 18px rgba(var(--orange-rgb), .32) !important;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}
.btn-login:hover,
[data-theme="light"] .btn-login:hover {
  background: #9a8053 !important;                 /* al pasar por encima, macizo */
  box-shadow: 0 6px 24px rgba(var(--orange-rgb), .45) !important;
}
@media (prefers-color-scheme: light) {
  .btn-login {
    background: #9a8053e6 !important;
    color: #fff !important;
    border: 1px solid var(--orange-dark) !important;
  }
}
